summaryrefslogtreecommitdiff
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/net/opietooth/applet/bluezapplet.cpp12
-rw-r--r--noncore/net/opietooth/applet/bluezapplet.h2
-rw-r--r--noncore/net/opietooth/lib/device.cc108
-rw-r--r--noncore/net/opietooth/lib/lib.pro7
-rw-r--r--noncore/net/opietooth/lib/obexpush.cpp108
-rw-r--r--noncore/net/opietooth/lib/obexpush.h77
-rw-r--r--noncore/net/opietooth/manager/obexdialog.cpp68
-rw-r--r--noncore/net/opietooth/manager/obexdialog.h15
-rw-r--r--noncore/net/opietooth/manager/obexftpdialog.cpp2
-rw-r--r--noncore/net/opietooth/manager/obexpopup.cpp8
10 files changed, 289 insertions, 118 deletions
diff --git a/noncore/net/opietooth/applet/bluezapplet.cpp b/noncore/net/opietooth/applet/bluezapplet.cpp
index 90b0c6d..6351d4e 100644
--- a/noncore/net/opietooth/applet/bluezapplet.cpp
+++ b/noncore/net/opietooth/applet/bluezapplet.cpp
@@ -95,102 +95,106 @@ namespace OpieTooth {
95 } 95 }
96 } 96 }
97 97
98 int BluezApplet::position() 98 int BluezApplet::position()
99 { 99 {
100 return 6; 100 return 6;
101 } 101 }
102 102
103 103
104 bool BluezApplet::checkBluezStatus() { 104 bool BluezApplet::checkBluezStatus() {
105 if (btDevice) { 105 if (btDevice) {
106 if (btDevice->isLoaded() ) { 106 if (btDevice->isLoaded() ) {
107 odebug << "btDevice isLoaded" << oendl; 107 odebug << "btDevice isLoaded" << oendl;
108 return true; 108 return true;
109 } else { 109 } else {
110 odebug << "btDevice is NOT loaded" << oendl; 110 odebug << "btDevice is NOT loaded" << oendl;
111 return false; 111 return false;
112 } 112 }
113 } else { 113 } else {
114 odebug << "btDevice is ZERO" << oendl; 114 odebug << "btDevice is ZERO" << oendl;
115 return false; 115 return false;
116 } 116 }
117 } 117 }
118 118
119 int BluezApplet::setBluezStatus(int c) { 119 int BluezApplet::setBluezStatus(int c, bool sync) {
120 120
121 if ( c == 1 ) { 121 if ( c == 1 ) {
122 switch ( ODevice::inst()->model() ) { 122 switch ( ODevice::inst()->model() ) {
123 case Model_iPAQ_H39xx: 123 case Model_iPAQ_H39xx:
124 btDevice = new Device( "/dev/tts/1", "bcsp", "921600" ); 124 btDevice = new Device( "/dev/tts/1", "bcsp", "921600" );
125 break; 125 break;
126 126
127 case Model_iPAQ_H5xxx: 127 case Model_iPAQ_H5xxx:
128 btDevice = new Device( "/dev/tts/1", "any", "921600" ); 128 btDevice = new Device( "/dev/tts/1", "any", "921600" );
129 break; 129 break;
130 130
131#ifndef OPIE120 131#ifndef OPIE120
132 case Model_MyPal_716: 132 case Model_MyPal_716:
133 btDevice = new Device( "/dev/ttyS1", "bcsp", "921600" ); 133 btDevice = new Device( "/dev/ttyS1", "bcsp", "921600" );
134 break; 134 break;
135#endif 135#endif
136 136
137 default: 137 default:
138 btDevice = new Device( "/dev/ttySB0", "bcsp", "230400" ); 138 btDevice = new Device( "/dev/ttySB0", "bcsp", "230400" );
139 break; 139 break;
140 } 140 }
141 QCopEnvelope e("QPE/System", "execute(QString)"); 141 if (sync) {
142 e << QString("/etc/init.d/bluetooth start"); 142 ::system("/etc/init.d/bluetooth start >/dev/null 2>/dev/null");
143 } else {
144 QCopEnvelope e("QPE/System", "execute(QString)");
145 e << QString("/etc/init.d/bluetooth start");
146 }
143 } else { 147 } else {
144 ::system("/etc/init.d/bluetooth stop >/dev/null 2>/dev/null"); 148 ::system("/etc/init.d/bluetooth stop >/dev/null 2>/dev/null");
145 if ( btManager ) { 149 if ( btManager ) {
146 delete btManager; 150 delete btManager;
147 btManager = 0; 151 btManager = 0;
148 } 152 }
149 if ( btDevice ) { 153 if ( btDevice ) {
150 delete btDevice; 154 delete btDevice;
151 btDevice = 0; 155 btDevice = 0;
152 } 156 }
153 } 157 }
154 return 0; 158 return 0;
155 } 159 }
156 160
157 int BluezApplet::checkBluezDiscoveryStatus() { 161 int BluezApplet::checkBluezDiscoveryStatus() {
158 return isScanning; 162 return isScanning;
159 } 163 }
160 164
161 int BluezApplet::setBluezDiscoveryStatus(int d) { 165 int BluezApplet::setBluezDiscoveryStatus(int d) {
162 return bluezDiscoveryActive = d; 166 return bluezDiscoveryActive = d;
163 } 167 }
164 168
165 // FIXME mbhaynie 169 // FIXME mbhaynie
166 // receiver for QCopChannel("QPE/Bluetooth") messages. 170 // receiver for QCopChannel("QPE/Bluetooth") messages.
167 void BluezApplet::slotMessage( const QCString& str, const QByteArray& ) 171 void BluezApplet::slotMessage( const QCString& str, const QByteArray& )
168 { 172 {
169 if ( str == "enableBluetooth()") { 173 if ( str == "enableBluetooth()") {
170 m_wasOn = checkBluezStatus(); 174 m_wasOn = checkBluezStatus();
171 if (!m_wasOn) { 175 if (!m_wasOn) {
172 setBluezStatus(1); 176 setBluezStatus(1, true);
173 sleep(2); 177 sleep(2);
174 } 178 }
175 } 179 }
176 else if ( str == "disableBluetooth()") { 180 else if ( str == "disableBluetooth()") {
177 /* 181 /*
178 * We can down BT only if it was started by qcop. We don't want 182 * We can down BT only if it was started by qcop. We don't want
179 * to down BT started from menu an break our networking connection 183 * to down BT started from menu an break our networking connection
180 */ 184 */
181 if (checkBluezStatus() && !m_wasOn) 185 if (checkBluezStatus() && !m_wasOn)
182 setBluezStatus(0); 186 setBluezStatus(0);
183 doListDevice = false; 187 doListDevice = false;
184 } 188 }
185 else if ( str == "listDevices()") { 189 else if ( str == "listDevices()") {
186 if (checkBluezStatus()) { 190 if (checkBluezStatus()) {
187 doListDevice = false; 191 doListDevice = false;
188 timerEvent(0); 192 timerEvent(0);
189 if (!btManager) { 193 if (!btManager) {
190 btManager = new Manager("hci0"); 194 btManager = new Manager("hci0");
191 connect( btManager, 195 connect( btManager,
192 SIGNAL( foundDevices(const QString&, RemoteDevice::ValueList) ), 196 SIGNAL( foundDevices(const QString&, RemoteDevice::ValueList) ),
193 this, SLOT( fillList(const QString&, RemoteDevice::ValueList) ) ) ; 197 this, SLOT( fillList(const QString&, RemoteDevice::ValueList) ) ) ;
194 } 198 }
195 btManager->searchDevices(); 199 btManager->searchDevices();
196 isScanning = true; 200 isScanning = true;
diff --git a/noncore/net/opietooth/applet/bluezapplet.h b/noncore/net/opietooth/applet/bluezapplet.h
index 90bee3b..1937934 100644
--- a/noncore/net/opietooth/applet/bluezapplet.h
+++ b/noncore/net/opietooth/applet/bluezapplet.h
@@ -34,48 +34,48 @@
34#include <qtimer.h> 34#include <qtimer.h>
35#include <manager.h> 35#include <manager.h>
36#include <opie2/oprocess.h> 36#include <opie2/oprocess.h>
37 37
38namespace OpieTooth { 38namespace OpieTooth {
39 class Device; 39 class Device;
40 40
41 class BluezApplet : public QWidget { 41 class BluezApplet : public QWidget {
42 Q_OBJECT 42 Q_OBJECT
43 public: 43 public:
44 BluezApplet( QWidget *parent = 0, const char *name=0 ); 44 BluezApplet( QWidget *parent = 0, const char *name=0 );
45 ~BluezApplet(); 45 ~BluezApplet();
46 static int position(); 46 static int position();
47 protected: 47 protected:
48 void timerEvent(QTimerEvent *te ); 48 void timerEvent(QTimerEvent *te );
49 49
50public slots: 50public slots:
51 void fillList( const QString& device, RemoteDevice::ValueList list ); 51 void fillList( const QString& device, RemoteDevice::ValueList list );
52 52
53 private: 53 private:
54 void mousePressEvent( QMouseEvent * ); 54 void mousePressEvent( QMouseEvent * );
55 void paintEvent( QPaintEvent* ); 55 void paintEvent( QPaintEvent* );
56 void launchManager(); 56 void launchManager();
57 bool checkBluezStatus(); 57 bool checkBluezStatus();
58 int setBluezStatus(int); 58 int setBluezStatus(int, bool sync = false);
59 int checkBluezDiscoveryStatus(); 59 int checkBluezDiscoveryStatus();
60 int setBluezDiscoveryStatus(int); 60 int setBluezDiscoveryStatus(int);
61 61
62 private: 62 private:
63 Device* btDevice; 63 Device* btDevice;
64 Manager *btManager; 64 Manager *btManager;
65 QPixmap bluezOnPixmap; 65 QPixmap bluezOnPixmap;
66 QPixmap bluezOffPixmap; 66 QPixmap bluezOffPixmap;
67 QPixmap bluezDiscoveryOnPixmap; 67 QPixmap bluezDiscoveryOnPixmap;
68 bool bluezactive; 68 bool bluezactive;
69 bool bluezDiscoveryActive; 69 bool bluezDiscoveryActive;
70 bool doListDevice; //If I have to list devices after bringing BT up? 70 bool doListDevice; //If I have to list devices after bringing BT up?
71 bool isScanning; //If I'm scanning devices 71 bool isScanning; //If I'm scanning devices
72 bool m_wasOn; //If BT was started by menu? 72 bool m_wasOn; //If BT was started by menu?
73 73
74private slots: 74private slots:
75 void slotMessage( const QCString& , const QByteArray& ); 75 void slotMessage( const QCString& , const QByteArray& );
76 }; 76 };
77}; 77};
78 78
79 79
80#endif 80#endif
81 81
diff --git a/noncore/net/opietooth/lib/device.cc b/noncore/net/opietooth/lib/device.cc
index 40acbd2..2f04d46 100644
--- a/noncore/net/opietooth/lib/device.cc
+++ b/noncore/net/opietooth/lib/device.cc
@@ -1,186 +1,182 @@
1 1
2#include "device.h" 2#include "device.h"
3 3
4/* OPIE */ 4/* OPIE */
5#include <opie2/oprocess.h> 5#include <opie2/oprocess.h>
6#include <opie2/odebug.h> 6#include <opie2/odebug.h>
7#include <opie2/odevice.h> 7#include <opie2/odevice.h>
8 8
9using namespace Opie::Core; 9using namespace Opie::Core;
10 10
11/* STD */ 11/* STD */
12#include <signal.h> 12#include <signal.h>
13 13
14 14
15using namespace OpieTooth; 15using namespace OpieTooth;
16 16
17using Opie::Core::OProcess; 17using Opie::Core::OProcess;
18namespace { 18namespace {
19 int parsePid( const QCString& par ) 19 int parsePid( const QCString& par )
20 { 20 {
21 int id=0; 21 int id = 0;
22 QString string( par ); 22 QString string( par );
23 QStringList list = QStringList::split( '\n', string ); 23 QStringList list = QStringList::split( '\n', string );
24 24
25 for( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) 25 for( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
26 { 26 owarn << "parsePID: " << (*it).latin1() << oendl;
27 owarn << "parsePID: " << (*it).latin1() << oendl;
28 27
29 // FIXME mbhaynie: Surely there is a better way to skip 28 // FIXME mbhaynie: Surely there is a better way to skip
30 // verbosity (E.g. the TI device configuration 29 // verbosity (E.g. the TI device configuration
31 // script). Apparently the PID is always on a line by 30 // script). Apparently the PID is always on a line by
32 // itself, or is at least the first word of a line. Does 31 // itself, or is at least the first word of a line. Does
33 // QString have somethine like startsWithRegex("[0-9]+")? 32 // QString have somethine like startsWithRegex("[0-9]+")?
34 if( (*it).startsWith("#") ) continue; 33 if( (*it).startsWith("#") ) continue;
35 if( (*it).startsWith("TI") ) continue; 34 if( (*it).startsWith("TI") ) continue;
36 if( (*it).startsWith("Loading") ) continue; 35 if( (*it).startsWith("Loading") ) continue;
37 if( (*it).startsWith("BTS") ) continue; 36 if( (*it).startsWith("BTS") ) continue;
38 if( !(*it).startsWith("CSR") ) 37 if( !(*it).startsWith("CSR") ) {
39 { 38 id = (*it).toInt();
40 id = (*it).toInt(); 39 break;
41 break; 40 }
42 }
43 }
44 return id;
45 } 41 }
42 return id;
43 }
46} 44}
47 45
48Device::Device(const QString &device, const QString &mode, const QString &speed ) 46Device::Device(const QString &device, const QString &mode, const QString &speed )
49 : QObject(0, "device") { 47 : QObject(0, "device") {
50 48
51 owarn << "OpieTooth::Device create" << oendl; 49 owarn << "OpieTooth::Device create" << oendl;
52 m_hci = 0; 50 m_hci = 0;
53 m_process = 0; 51 m_process = 0;
54 m_attached = false; 52 m_attached = false;
55 m_device = device; 53 m_device = device;
56 m_mode = mode; 54 m_mode = mode;
57 m_speed = speed; 55 m_speed = speed;
58 attach(); 56 attach();
59} 57}
60Device::~Device(){ 58Device::~Device(){
61 detach(); 59 detach();
62} 60}
63 61
64// FIXME mbhaynie -- If BT is active, and opie is restarted, this 62// FIXME mbhaynie -- If BT is active, and opie is restarted, this
65// applet thinks bt is down, and will fail to start it again. Not 63// applet thinks bt is down, and will fail to start it again. Not
66// sure why. 64// sure why.
67void Device::attach(){ 65void Device::attach(){
68 owarn << "attaching " << m_device.latin1() << " " << m_mode.latin1() << " " << m_speed.latin1() << oendl; 66 owarn << "attaching " << m_device.latin1() << " " << m_mode.latin1() << " " << m_speed.latin1() << oendl;
69 if(m_process == 0 ){ 67 if(m_process == 0 ){
70 m_output.resize(0); 68 m_output.resize(0);
71 owarn << "new process to create" << oendl; 69 owarn << "new process to create" << oendl;
72 m_process = new OProcess(); 70 m_process = new OProcess();
73 *m_process << "hciattach"; 71 *m_process << "hciattach";
74 *m_process << "-p"; 72 *m_process << "-p";
75 73
76 // FIXME -- this is a hack for an odd hciattach interface. 74 // FIXME -- this is a hack for an odd hciattach interface.
77 if ( ODevice::inst()->modelString() == "HX4700" ) 75 if ( ODevice::inst()->modelString() == "HX4700" )
78 { 76 *m_process << "-S" << "/etc/bluetooth/TIInit_3.2.26.bts" << "/dev/ttyS1" << "texas";
79 *m_process << "-S" << "/etc/bluetooth/TIInit_3.2.26.bts" << "/dev/ttyS1" << "texas";
80 }
81 else 77 else
82 { 78 *m_process << m_device << m_mode << m_speed;
83 *m_process << m_device << m_mode << m_speed;
84 }
85 connect(m_process, SIGNAL( processExited(Opie::Core::OProcess*) ), 79 connect(m_process, SIGNAL( processExited(Opie::Core::OProcess*) ),
86 this, SLOT( slotExited(Opie::Core::OProcess* ) ) ); 80 this, SLOT( slotExited(Opie::Core::OProcess* ) ) );
87 connect(m_process, SIGNAL( receivedStdout(Opie::Core::OProcess*, char*, int) ), 81 connect(m_process, SIGNAL( receivedStdout(Opie::Core::OProcess*, char*, int) ),
88 this, SLOT(slotStdOut(Opie::Core::OProcess*,char*,int ) ) ); 82 this, SLOT(slotStdOut(Opie::Core::OProcess*,char*,int ) ) );
89 connect(m_process, SIGNAL(receivedStderr(Opie::Core::OProcess*, char*, int ) ), 83 connect(m_process, SIGNAL(receivedStderr(Opie::Core::OProcess*, char*, int ) ),
90 this, SLOT(slotStdErr(Opie::Core::OProcess*,char*,int) ) ); 84 this, SLOT(slotStdErr(Opie::Core::OProcess*,char*,int) ) );
91 if(!m_process->start(OProcess::NotifyOnExit, OProcess::AllOutput ) ){ 85 if(!m_process->start(OProcess::NotifyOnExit, OProcess::AllOutput ) ){
92 owarn << "Could not start" << oendl; 86 owarn << "Could not start" << oendl;
93 delete m_process; 87 delete m_process;
94 m_process = 0; 88 m_process = 0;
95 } 89 }
96 }; 90 }
97} 91}
98void Device::detach(){ 92void Device::detach(){
99 delete m_hci; 93 delete m_hci;
94 m_hci = 0;
100 delete m_process; 95 delete m_process;
96 m_process = 0;
101 // kill the pid we got 97 // kill the pid we got
102 if(m_attached ){ 98 if(m_attached ){
103 //kill the pid 99 //kill the pid
104 owarn << "killing" << oendl; 100 owarn << "killing" << oendl;
105 kill(pid, 9); 101 ::kill(pid, 9);
106 } 102 }
107 owarn << "detached" << oendl; 103 owarn << "detached" << oendl;
108} 104}
109bool Device::isLoaded()const{ 105bool Device::isLoaded()const{
110 return m_attached; 106 return m_attached;
111} 107}
112QString Device::devName()const { 108QString Device::devName()const {
113 return QString::fromLatin1("hci0"); 109 return QString::fromLatin1("hci0");
114}; 110};
115void Device::slotExited( OProcess* proc) 111void Device::slotExited( OProcess* proc)
116{ 112{
117 owarn << "prcess exited" << oendl; 113 owarn << "prcess exited" << oendl;
118 if(proc== m_process ){ 114 if(proc== m_process ){
119 owarn << "proc == m_process" << oendl; 115 owarn << "proc == m_process" << oendl;
120 if( m_process->normalExit() ){ // normal exit 116 if( m_process->normalExit() ){ // normal exit
121 owarn << "normalExit" << oendl; 117 owarn << "normalExit" << oendl;
122 int ret = m_process->exitStatus(); 118 int ret = m_process->exitStatus();
123 if( ret == 0 ){ // attached 119 if( ret == 0 ){ // attached
124 owarn << "attached" << oendl; 120 owarn << "attached" << oendl;
125 owarn << "Output: " << m_output.data() << oendl; 121 owarn << "Output: " << m_output.data() << oendl;
126 pid = parsePid( m_output ); 122 pid = parsePid( m_output );
127 owarn << "Pid = " << pid << oendl; 123 owarn << "Pid = " << pid << oendl;
128 // now hciconfig hci0 up ( determine hciX FIXME) 124 // now hciconfig hci0 up ( determine hciX FIXME)
129 // and call hciconfig hci0 up 125 // and call hciconfig hci0 up
130 // FIXME hardcoded to hci0 now :( 126 // FIXME hardcoded to hci0 now :(
131 m_hci = new OProcess( ); 127 m_hci = new OProcess( );
132 *m_hci << "hciconfig"; 128 *m_hci << "hciconfig";
133 *m_hci << "hci0 up"; 129 *m_hci << "hci0 up";
134 connect(m_hci, SIGNAL( processExited(Opie::Core::OProcess*) ), 130 connect(m_hci, SIGNAL( processExited(Opie::Core::OProcess*) ),
135 this, SLOT( slotExited(Opie::Core::OProcess* ) ) ); 131 this, SLOT( slotExited(Opie::Core::OProcess* ) ) );
136 if(!m_hci->start() ){ 132 if(!m_hci->start() ){
137 owarn << "could not start" << oendl; 133 owarn << "could not start" << oendl;
138 m_attached = false; 134 m_attached = false;
139 emit device("hci0", false ); 135 emit device("hci0", false );
140 } 136 }
141 }else{ 137 }else{
142 owarn << "crass" << oendl; 138 owarn << "crass" << oendl;
143 m_attached = false; 139 m_attached = false;
144 emit device("hci0", false ); 140 emit device("hci0", false );
145 141
146 } 142 }
147 } 143 }
148 delete m_process; 144 delete m_process;
149 m_process = 0; 145 m_process = 0;
150 }else if(proc== m_hci ){ 146 }else if(proc== m_hci ){
151 owarn << "M HCI exited" << oendl; 147 owarn << "M HCI exited" << oendl;
152 if( m_hci->normalExit() ){ 148 if( m_hci->normalExit() ){
153 owarn << "normal exit" << oendl; 149 owarn << "normal exit" << oendl;
154 int ret = m_hci->exitStatus(); 150 int ret = m_hci->exitStatus();
155 if( ret == 0 ){ 151 if( ret == 0 ){
156 owarn << "attached really really attached" << oendl; 152 owarn << "attached really really attached" << oendl;
157 m_attached = true; 153 m_attached = true;
158 emit device("hci0", true ); 154 emit device("hci0", true );
159 }else{ 155 }else{
160 owarn << "failed" << oendl; 156 owarn << "failed" << oendl;
161 emit device("hci0", false ); 157 emit device("hci0", false );
162 m_attached = false; 158 m_attached = false;
163 } 159 }
164 }// normal exit 160 }// normal exit
165 delete m_hci; 161 delete m_hci;
166 m_hci = 0; 162 m_hci = 0;
167 } 163 }
168} 164}
169void Device::slotStdOut(OProcess* proc, char* chars, int len) 165void Device::slotStdOut(OProcess* proc, char* chars, int len)
170{ 166{
171 owarn << "std out" << oendl; 167 owarn << "std out" << oendl;
172 if( len <1 ){ 168 if( len <1 ){
173 owarn << "len < 1 " << oendl; 169 owarn << "len < 1 " << oendl;
174 return; 170 return;
175 } 171 }
176 if(proc == m_process ){ 172 if(proc == m_process ){
177 QCString string( chars, len+1 ); // \0 == +1 173 QCString string( chars, len+1 ); // \0 == +1
178 owarn << "output: " << string.data() << oendl; 174 owarn << "output: " << string.data() << oendl;
179 m_output.append( string.data() ); 175 m_output.append( string.data() );
180 } 176 }
181} 177}
182void Device::slotStdErr(OProcess* proc, char* chars, int len) 178void Device::slotStdErr(OProcess* proc, char* chars, int len)
183{ 179{
184 owarn << "std err" << oendl; 180 owarn << "std err" << oendl;
185 slotStdOut( proc, chars, len ); 181 slotStdOut( proc, chars, len );
186} 182}
diff --git a/noncore/net/opietooth/lib/lib.pro b/noncore/net/opietooth/lib/lib.pro
index 781bf15..e11af9a 100644
--- a/noncore/net/opietooth/lib/lib.pro
+++ b/noncore/net/opietooth/lib/lib.pro
@@ -1,12 +1,15 @@
1TEMPLATE = lib 1TEMPLATE = lib
2CONFIG += qte warn_on 2CONFIG += qte warn_on
3 HEADERS = connection.h parser.h device.h manager.h remotedevice.h services.h \ 3 HEADERS = connection.h parser.h device.h manager.h remotedevice.h services.h \
4 startpanconnection.h startdunconnection.h bt-serial.h forwarder.h 4 startpanconnection.h startdunconnection.h obexpush.h \
5 bt-serial.h forwarder.h
5 SOURCES = connection.cpp parser.cc device.cc manager.cc remotedevice.cc services.cc \ 6 SOURCES = connection.cpp parser.cc device.cc manager.cc remotedevice.cc services.cc \
6 startpanconnection.cpp startdunconnection.cpp bt-serial.c forwarder.cc 7 startpanconnection.cpp startdunconnection.cpp obexpush.cpp \
8 bt-serial.c forwarder.cc
9
7 TARGET = opietooth1 10 TARGET = opietooth1
8INCLUDEPATH += $(OPIEDIR)/include . 11INCLUDEPATH += $(OPIEDIR)/include .
9 DESTDIR = $(OPIEDIR)/lib 12 DESTDIR = $(OPIEDIR)/lib
10LIBS += -lopiecore2 13LIBS += -lopiecore2
11 14
12include( $(OPIEDIR)/include.pro ) 15include( $(OPIEDIR)/include.pro )
diff --git a/noncore/net/opietooth/lib/obexpush.cpp b/noncore/net/opietooth/lib/obexpush.cpp
new file mode 100644
index 0000000..e0a4bee
--- a/dev/null
+++ b/noncore/net/opietooth/lib/obexpush.cpp
@@ -0,0 +1,108 @@
1/* $Id$ */
2/* OBEX push functions implementation */
3/***************************************************************************
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 ***************************************************************************/
11#include "obexpush.h"
12
13#include <qfileinfo.h>
14#include <qfile.h>
15
16#include <opie2/odebug.h>
17#include <opie2/odebug.h>
18
19using namespace Opie::Core;
20using namespace OpieTooth;
21
22ObexPush::ObexPush()
23{
24 pushProc = new OProcess();
25 connect(pushProc, SIGNAL(processExited(Opie::Core::OProcess*)),
26 this, SLOT(slotPushExited(Opie::Core::OProcess*)));
27 connect(pushProc, SIGNAL(receivedStdout(Opie::Core::OProcess*, char*, int)),
28 this, SLOT(slotPushOut(Opie::Core::OProcess*, char*, int)));
29 connect(pushProc, SIGNAL(receivedStderr(Opie::Core::OProcess*, char*, int)),
30 this, SLOT(slotPushErr(Opie::Core::OProcess*, char*, int)));
31
32}
33
34ObexPush::~ObexPush()
35{
36 if (pushProc->isRunning())
37 pushProc->kill();
38 delete pushProc;
39 pushProc = NULL;
40}
41
42/**
43 * Function that sends a file
44 * @param mac destination device MAC address
45 * @param port service port number
46 * @param src source file name
47 * @param dst destination file name
48 * @return 0 on success, -1 on error, 1 if sending process is running
49 */
50int ObexPush::send(QString& mac, int port, QString& src, QString& dst)
51{
52 QString dev = mac;
53 QString execName = "ussp-push";
54 if (pushProc->isRunning())
55 return 1;
56 pushProc->clearArguments();
57 dev += "@";
58 dev += QString::number(port);
59 if (!dst.isEmpty())
60 *pushProc << execName << "--timeo 30" << dev
61 << QFile::encodeName(src) << QFile::encodeName(dst);
62 else
63 *pushProc << execName << "--timeo 30" << dev
64 << QFile::encodeName(src)
65 << QFile::encodeName(QFileInfo(src).fileName());
66 odebug << execName << " " << dev << " " << src << " "
67 << ((!dst.isEmpty())? dst: QFileInfo(src).fileName()) << oendl;
68 pushProc->setUseShell(true);
69 if (!pushProc->start(OProcess::NotifyOnExit, OProcess::All))
70 return -1;
71 else
72 return 0;
73}
74
75void ObexPush::slotPushExited(Opie::Core::OProcess* proc)
76{
77 if (pushProc != proc)
78 return;
79 odebug << "Process exited" << oendl;
80 if (pushProc->normalExit())
81 emit sendComplete(pushProc->exitStatus());
82 else
83 emit sendError(-1);
84}
85
86/**
87 * Function makes a notification from slotPushOut and slotPushErr
88 */
89void ObexPush::notifyInfo(Opie::Core::OProcess* proc, char* buf, int len)
90{
91 if (proc != pushProc)
92 return;
93 QCString str(buf, len);
94 odebug << str << oendl;
95 emit status(str);
96}
97
98void ObexPush::slotPushOut(Opie::Core::OProcess* proc, char* buf, int len)
99{
100 notifyInfo(proc, buf, len);
101}
102
103void ObexPush::slotPushErr(Opie::Core::OProcess* proc, char* buf, int len)
104{
105 notifyInfo(proc, buf, len);
106}
107
108//eof
diff --git a/noncore/net/opietooth/lib/obexpush.h b/noncore/net/opietooth/lib/obexpush.h
new file mode 100644
index 0000000..8147643
--- a/dev/null
+++ b/noncore/net/opietooth/lib/obexpush.h
@@ -0,0 +1,77 @@
1/* $Id$ */
2/* OBEX push functions declarations */
3/***************************************************************************
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 ***************************************************************************/
11#ifndef _OBEXPUSH_H_
12#define _OBEXPUSH_H_
13#include <qobject.h>
14#include <qstring.h>
15#include "services.h"
16#include <opie2/oprocess.h>
17namespace OpieTooth {
18 class ObexPush : public QObject {
19 Q_OBJECT
20 public:
21 /**
22 * Constructor which creates an object
23 */
24 ObexPush();
25
26 /**
27 * Public destructor
28 */
29 ~ObexPush();
30
31 /**
32 * Function that sends a file
33 * @param mac destination device MAC address
34 * @param port service port number
35 * @param src source file name
36 * @param dst destination file name
37 * @return 0 on success, -1 on error, 1 if sending process is running
38 */
39 int send(QString& mac, int port, QString& src, QString& dst);
40
41 /**
42 * @return true if it's sending and false otherwise
43 */
44 bool isSending() { return pushProc->isRunning(); }
45 signals:
46 /**
47 * Informs that the sending process has completed
48 * @param status the finish status
49 */
50 void sendComplete(int);
51
52 /**
53 * Informs that the sending process has finished with error
54 * @param status the finish status
55 */
56 void sendError(int);
57
58 /**
59 * Informs that we have a string status update
60 * @param str status string
61 */
62 void status(QCString&);
63 protected slots:
64 void slotPushOut(Opie::Core::OProcess*, char*, int);
65 void slotPushErr(Opie::Core::OProcess*, char*, int);
66 void slotPushExited(Opie::Core::OProcess* proc);
67 protected:
68 /**
69 * Function makes a notification from slotPushOut and slotPushErr
70 */
71 void notifyInfo(Opie::Core::OProcess*, char*, int);
72 protected:
73 Opie::Core::OProcess* pushProc; //The push process
74 };
75};
76#endif
77//eof
diff --git a/noncore/net/opietooth/manager/obexdialog.cpp b/noncore/net/opietooth/manager/obexdialog.cpp
index 8d7b593..4b795b7 100644
--- a/noncore/net/opietooth/manager/obexdialog.cpp
+++ b/noncore/net/opietooth/manager/obexdialog.cpp
@@ -1,140 +1,122 @@
1 1
2#include "obexdialog.h" 2#include "obexdialog.h"
3#include <errno.h> 3#include <errno.h>
4#include <qpushbutton.h> 4#include <qpushbutton.h>
5#include <qmultilineedit.h> 5#include <qmultilineedit.h>
6#include <qlineedit.h> 6#include <qlineedit.h>
7#include <qlayout.h> 7#include <qlayout.h>
8#include <qlabel.h> 8#include <qlabel.h>
9#include <qfileinfo.h> 9#include <qfileinfo.h>
10 10
11#include <qpe/resource.h> 11#include <qpe/resource.h>
12 12
13#include <opie2/oprocess.h> 13#include <opie2/oprocess.h>
14#include <opie2/ofiledialog.h> 14#include <opie2/ofiledialog.h>
15#include <opie2/odebug.h> 15#include <opie2/odebug.h>
16using namespace Opie::Core; 16using namespace Opie::Core;
17 17
18using namespace OpieTooth; 18using namespace OpieTooth;
19 19
20using namespace Opie::Core; 20using namespace Opie::Core;
21using namespace Opie::Ui; 21using namespace Opie::Ui;
22using namespace Opie::Core; 22using namespace Opie::Core;
23ObexDialog::ObexDialog(const QString& device, QWidget* parent, const char* name, bool modal, WFlags fl) 23ObexDialog::ObexDialog(const QString& device, int port,
24 QWidget* parent, const char* name, bool modal, WFlags fl)
24 : QDialog( parent, name, modal, fl ) { 25 : QDialog( parent, name, modal, fl ) {
25 26
26 if ( !name ) 27 if ( !name )
27 setName( "ObexDialog" ); 28 setName( "ObexDialog" );
28 setCaption( tr( "beam files " ) ) ; 29 setCaption( tr( "beam files " ) ) ;
29 30
30 m_device = device; 31 m_device = device;
32 m_port = port;
31 33
32 layout = new QVBoxLayout( this ); 34 layout = new QVBoxLayout( this );
33 obexSend = new OProcess(); 35 obexSend = new ObexPush();
34 36
35 info = new QLabel( this ); 37 info = new QLabel( this );
36 info->setText( tr("Which file should be beamed?") ); 38 info->setText( tr("Which file should be beamed?") );
37 39
38 statLine = new QLabel(this); 40 statLine = new QLabel(this);
39 statLine->setText( tr("Ready") ); 41 statLine->setText( tr("Ready") );
40 42
41 status = new QMultiLineEdit(this); 43 status = new QMultiLineEdit(this);
42 status->setReadOnly(true); 44 status->setReadOnly(true);
43 45
44 cmdLine = new QLineEdit( this ); 46 cmdLine = new QLineEdit( this );
45 47
46 QPushButton *browserButton; 48 QPushButton *browserButton;
47 browserButton = new QPushButton( Resource::loadIconSet("fileopen"),"",this,"BrowseButton"); 49 browserButton = new QPushButton( Resource::loadIconSet("fileopen"),"",this,"BrowseButton");
48 connect( browserButton, SIGNAL(released() ), this , SLOT(browse() ) ); 50 connect( browserButton, SIGNAL(released() ), this , SLOT(browse() ) );
49 51
50 chNameLine = new QLineEdit( this ); 52 chNameLine = new QLineEdit( this );
51 53
52 sendButton = new QPushButton( this ); 54 sendButton = new QPushButton( this );
53 sendButton->setText( tr( "Send" ) ); 55 sendButton->setText( tr( "Send" ) );
54 56
55 layout->addWidget(info); 57 layout->addWidget(info);
56 layout->addWidget(status); 58 layout->addWidget(status);
57 layout->addWidget(cmdLine); 59 layout->addWidget(cmdLine);
58 layout->addWidget(browserButton); 60 layout->addWidget(browserButton);
59 layout->addWidget(chNameLine); 61 layout->addWidget(chNameLine);
60 layout->addWidget(sendButton); 62 layout->addWidget(sendButton);
61 layout->addWidget(statLine); 63 layout->addWidget(statLine);
62 64
63 connect( sendButton, SIGNAL( clicked() ), this, SLOT( sendData() ) ); 65 connect( sendButton, SIGNAL( clicked() ), this, SLOT( sendData() ) );
64 66
65 connect(obexSend, SIGNAL(processExited(Opie::Core::OProcess*)), 67 connect(obexSend, SIGNAL(sendComplete(int)),
66 this, SLOT(slotProcessExited(Opie::Core::OProcess*))); 68 this, SLOT(slotPushComplete(int)));
67 connect(obexSend, SIGNAL(receivedStdout(Opie::Core::OProcess*, char*, int)), 69 connect(obexSend, SIGNAL(sendError(int)),
68 this, SLOT(slotPushOut(Opie::Core::OProcess*, char*, int))); 70 this, SLOT(slotPushError(int)));
69 connect(obexSend, SIGNAL(receivedStderr(Opie::Core::OProcess*, char*, int)), 71 connect(obexSend, SIGNAL(status(QCString&)),
70 this, SLOT(slotPushErr(Opie::Core::OProcess*, char*, int))); 72 this, SLOT(slotPushStatus(QCString&)));
71 73
72} 74}
73 75
74ObexDialog::~ObexDialog() { 76ObexDialog::~ObexDialog() {
75 if (obexSend->isRunning())
76 obexSend->kill();
77 delete obexSend; 77 delete obexSend;
78 obexSend = NULL; 78 obexSend = NULL;
79} 79}
80 80
81void ObexDialog::browse() { 81void ObexDialog::browse() {
82 82
83 MimeTypes types; 83 MimeTypes types;
84 QStringList all; 84 QStringList all;
85 all << "*/*"; 85 all << "*/*";
86 types.insert("All Files", all ); 86 types.insert("All Files", all );
87 87
88 QString str = OFileDialog::getOpenFileName( 1,"/","", types, 0 ); 88 QString str = OFileDialog::getOpenFileName( 1,"/","", types, 0 );
89 cmdLine->setText( str ); 89 cmdLine->setText( str );
90 statLine->setText( tr("Ready") ); 90 statLine->setText( tr("Ready") );
91} 91}
92 92
93void ObexDialog::sendData() { 93void ObexDialog::sendData() {
94 int result; //function call result
94 QString fileURL = cmdLine->text(); 95 QString fileURL = cmdLine->text();
95 QString file = QFileInfo( fileURL ).fileName();
96 QString modifiedName = chNameLine->text(); 96 QString modifiedName = chNameLine->text();
97 QString execName = "ussp-push"; 97 result = obexSend->send(m_device, m_port, fileURL, modifiedName);
98 98 if (result > 0)
99 if (obexSend->isRunning())
100 return; 99 return;
101 obexSend->clearArguments(); 100 else if (result < 0)
102 // vom popupmenu beziehen
103 if ( !modifiedName.isEmpty() ) {
104 *obexSend << execName << "--timeo 30" << m_device << fileURL << modifiedName;
105 } else {
106 *obexSend << execName << "--timeo 30" << m_device << fileURL << file;
107 }
108 obexSend->setUseShell(true);
109 if (!obexSend->start(OProcess::NotifyOnExit, OProcess::All) ) {
110 statLine->setText( tr("Error: couln't start process") ); 101 statLine->setText( tr("Error: couln't start process") );
111 }
112 else 102 else
113 statLine->setText( tr("Sending") ); 103 statLine->setText( tr("Sending") );
114} 104}
115 105
116void ObexDialog::slotPushOut(OProcess*, char* buf, int len) { 106void ObexDialog::slotPushStatus(QCString& str) {
117 QCString str(buf, len);
118 status->append(str); 107 status->append(str);
119} 108}
120 109
121void ObexDialog::slotPushErr(OProcess*, char* buf, int len) { 110void ObexDialog::slotPushComplete(int result) {
122 QCString str(buf, len); 111 status->append( tr("Finished with result ") );
123 status->append(str); 112 status->append( QString::number(result) );
113 status->append( tr("\n") );
114 odebug << result << oendl;
115 statLine->setText( tr("Finished: ") + tr(strerror(result)) );
124} 116}
125 117
126void ObexDialog::slotProcessExited(OProcess*) { 118void ObexDialog::slotPushError(int) {
127 if (obexSend == NULL) 119 status->append( tr("Exited abnormally\n") );
128 return; 120 statLine->setText( tr("Exited abnormally") );
129 if (obexSend->normalExit()) {
130 status->append( tr("Finished with result ") );
131 status->append( QString::number(obexSend->exitStatus()) );
132 status->append( tr("\n") );
133 odebug << obexSend->exitStatus() << oendl;
134 statLine->setText( tr("Finished: ") + tr(strerror(obexSend->exitStatus())) );
135 }
136 else {
137 status->append( tr("Exited abnormally\n") );
138 statLine->setText( tr("Exited abnormally") );
139 }
140} 121}
122//eof
diff --git a/noncore/net/opietooth/manager/obexdialog.h b/noncore/net/opietooth/manager/obexdialog.h
index 44a26f3..063b7f1 100644
--- a/noncore/net/opietooth/manager/obexdialog.h
+++ b/noncore/net/opietooth/manager/obexdialog.h
@@ -1,48 +1,51 @@
1#ifndef OBEXDIALOG_H 1#ifndef OBEXDIALOG_H
2#define OBEXDIALOG_H 2#define OBEXDIALOG_H
3 3
4 4
5#include <qdialog.h> 5#include <qdialog.h>
6#include <opie2/oprocess.h>
7#include <qlabel.h> 6#include <qlabel.h>
8#include <qmultilineedit.h> 7#include <qmultilineedit.h>
8#include "obexpush.h"
9 9
10class QVBoxLayout; 10class QVBoxLayout;
11class QPushButton; 11class QPushButton;
12class QMultiLineEdit; 12class QMultiLineEdit;
13class QLineEdit; 13class QLineEdit;
14 14
15namespace Opie {namespace Core {class OProcess;}} 15namespace Opie {namespace Core {class OProcess;}}
16namespace OpieTooth { 16namespace OpieTooth {
17 17
18 class ObexDialog : public QDialog { 18 class ObexDialog : public QDialog {
19 19
20 Q_OBJECT 20 Q_OBJECT
21 21
22 public: 22 public:
23 ObexDialog( const QString& device = 0, QWidget* parent = 0, const char* name = 0, bool modal = TRUE, WFlags fl = 0); 23 ObexDialog( const QString& device = 0, int port = 0,
24 QWidget* parent = 0, const char* name = 0,
25 bool modal = TRUE, WFlags fl = 0);
24 ~ObexDialog(); 26 ~ObexDialog();
25 27
26 28
27private slots: 29private slots:
28 void browse(); 30 void browse();
29 void sendData(); 31 void sendData();
30 void slotPushOut(Opie::Core::OProcess*, char*, int); 32 void slotPushStatus(QCString&);
31 void slotPushErr(Opie::Core::OProcess*, char*, int); 33 void slotPushComplete(int);
32 void slotProcessExited(Opie::Core::OProcess* proc); 34 void slotPushError(int);
33 35
34 protected: 36 protected:
35 QVBoxLayout* layout; 37 QVBoxLayout* layout;
36 QLineEdit* cmdLine; 38 QLineEdit* cmdLine;
37 QLineEdit* chNameLine; 39 QLineEdit* chNameLine;
38 QPushButton* sendButton; 40 QPushButton* sendButton;
39 QLabel* info; 41 QLabel* info;
40 QMultiLineEdit* status; 42 QMultiLineEdit* status;
41 QLabel* statLine; 43 QLabel* statLine;
42 private: 44 private:
43 // Device that is used 45 // Device that is used
44 QString m_device; 46 QString m_device;
45 Opie::Core::OProcess *obexSend; 47 int m_port; //Port used
48 ObexPush* obexSend;
46 }; 49 };
47} 50}
48#endif 51#endif
diff --git a/noncore/net/opietooth/manager/obexftpdialog.cpp b/noncore/net/opietooth/manager/obexftpdialog.cpp
index efb3ff2..2a578ac 100644
--- a/noncore/net/opietooth/manager/obexftpdialog.cpp
+++ b/noncore/net/opietooth/manager/obexftpdialog.cpp
@@ -222,49 +222,49 @@ void ObexFtpDialog::status(const char* msg)
222 */ 222 */
223void ObexFtpDialog::slotCd(QListViewItem* item) 223void ObexFtpDialog::slotCd(QListViewItem* item)
224{ 224{
225 FileListItem* file = (FileListItem*)item; 225 FileListItem* file = (FileListItem*)item;
226 int idx; 226 int idx;
227 if (file == NULL) 227 if (file == NULL)
228 return; 228 return;
229 odebug << "Item " << file->text(0) << " clicked" << oendl; 229 odebug << "Item " << file->text(0) << " clicked" << oendl;
230 if (file->gettype() == IS_DIR) { 230 if (file->gettype() == IS_DIR) {
231 if (file->text(0) == "../") { 231 if (file->text(0) == "../") {
232 if (curdir.right(1) == "/") 232 if (curdir.right(1) == "/")
233 curdir.remove(curdir.length() - 1, 1); 233 curdir.remove(curdir.length() - 1, 1);
234 idx = curdir.findRev('/'); 234 idx = curdir.findRev('/');
235 if (idx >= 0) 235 if (idx >= 0)
236 curdir.remove(idx, curdir.length() - idx); 236 curdir.remove(idx, curdir.length() - idx);
237 else 237 else
238 curdir = ""; 238 curdir = "";
239 } 239 }
240 else { 240 else {
241 if (curdir != "" && curdir.right(1) != "/") 241 if (curdir != "" && curdir.right(1) != "/")
242 curdir += "/"; 242 curdir += "/";
243 curdir += file->text(0); 243 curdir += file->text(0);
244 } 244 }
245 odebug << "Browse " << curdir << oendl; 245 odebug << "Browse " << curdir << oendl;
246 if (obexftp_setpath(client, curdir, 0) < 0) 246 if (obexftp_setpath(client, QFile::encodeName(curdir), 0) < 0)
247 log(tr("CD failed: ") + tr(strerror(errno))); 247 log(tr("CD failed: ") + tr(strerror(errno)));
248 doBrowse(); 248 doBrowse();
249 } 249 }
250} 250}
251 251
252/* 252/*
253 * Copy file from a remote device to the local device 253 * Copy file from a remote device to the local device
254 */ 254 */
255void ObexFtpDialog::getFile() 255void ObexFtpDialog::getFile()
256{ 256{
257 FileListItem* file = (FileListItem*)fileList->selectedItem(); 257 FileListItem* file = (FileListItem*)fileList->selectedItem();
258 int result; 258 int result;
259 if (file == NULL) 259 if (file == NULL)
260 return; 260 return;
261 file2get = "/"; 261 file2get = "/";
262 local = localCurdir; 262 local = localCurdir;
263 if (local == "") { 263 if (local == "") {
264 errBox("Select a destination first"); 264 errBox("Select a destination first");
265 return; 265 return;
266 } 266 }
267 if (local.right(1) != "/") 267 if (local.right(1) != "/")
268 local += "/"; 268 local += "/";
269 if (file->gettype() == IS_FILE) { 269 if (file->gettype() == IS_FILE) {
270 if (client == NULL) { 270 if (client == NULL) {
diff --git a/noncore/net/opietooth/manager/obexpopup.cpp b/noncore/net/opietooth/manager/obexpopup.cpp
index d1d1b4a..759a452 100644
--- a/noncore/net/opietooth/manager/obexpopup.cpp
+++ b/noncore/net/opietooth/manager/obexpopup.cpp
@@ -1,49 +1,47 @@
1 1
2#include "obexdialog.h" 2#include "obexdialog.h"
3#include "obexpopup.h" 3#include "obexpopup.h"
4 4
5/* OPIE */ 5/* OPIE */
6#include <qpe/qpeapplication.h> 6#include <qpe/qpeapplication.h>
7#include <opie2/odebug.h> 7#include <opie2/odebug.h>
8using namespace Opie::Core; 8using namespace Opie::Core;
9 9
10/* QT */ 10/* QT */
11#include <qtimer.h> 11#include <qtimer.h>
12 12
13using namespace OpieTooth; 13using namespace OpieTooth;
14 14
15/* 15/*
16 * c'tor init the QAction 16 * c'tor init the QAction
17 */ 17 */
18ObexPopup::ObexPopup(const OpieTooth::Services& service, OpieTooth::BTDeviceItem* item) 18ObexPopup::ObexPopup(const OpieTooth::Services& service, OpieTooth::BTDeviceItem* item)
19 : QPopupMenu(), m_service(service) 19 : QPopupMenu(), m_service(service)
20{ 20{
21 owarn << "ObexPopup c'tor" << oendl; 21 odebug << "ObexPopup c'tor" << oendl;
22 22
23 m_item = item; 23 m_item = item;
24 /* connect action */ 24 /* connect action */
25 m_push = new QAction( ); // so it's get deleted 25 m_push = new QAction( ); // so it's get deleted
26 m_push->setText("Push file"); 26 m_push->setText("Push file");
27 m_push->addTo( this ); 27 m_push->addTo( this );
28 connect(m_push, SIGNAL(activated()), SLOT(slotPush())); 28 connect(m_push, SIGNAL(activated()), SLOT(slotPush()));
29} 29}
30 30
31 31
32ObexPopup::~ObexPopup() 32ObexPopup::~ObexPopup()
33{ 33{
34 delete m_push; 34 delete m_push;
35} 35}
36 36
37 37
38void ObexPopup::slotPush() 38void ObexPopup::slotPush()
39{ 39{
40 QString device = m_item->mac(); 40 QString device = m_item->mac();
41 int port = m_service.protocolDescriptorList().last().port(); 41 int port = m_service.protocolDescriptorList().last().port();
42 device += "@"; 42 odebug << "push something to " << device << " " << port << oendl;
43 device += QString::number(port); 43 ObexDialog obexDialog(device, port);
44 owarn << "push something to " << device << oendl;
45 ObexDialog obexDialog(device);
46 QPEApplication::execDialog( &obexDialog ); 44 QPEApplication::execDialog( &obexDialog );
47} 45}
48 46
49 47