2 * Copyright 2012-2017, Adrien Destugues, pulkomandy@pulkomandy.tk
3 * Distributed under the terms of the MIT licence.
7 #include "SerialWindow.h"
11 #include <FilePanel.h>
12 #include <GroupLayout.h>
16 #include <ScrollView.h>
17 #include <SerialPort.h>
18 #include <StatusBar.h>
20 #include "SerialApp.h"
24 const int SerialWindow::kBaudrates
[] = { 50, 75, 110, 134, 150, 200, 300, 600,
25 1200, 1800, 2400, 4800, 9600, 19200, 31250, 38400, 57600, 115200, 230400
29 // The values for these constants are not in the expected order, so we have to
30 // rely on this lookup table if we want to keep the menu items sorted.
31 const int SerialWindow::kBaudrateConstants
[] = { B_50_BPS
, B_75_BPS
, B_110_BPS
,
32 B_134_BPS
, B_150_BPS
, B_200_BPS
, B_300_BPS
, B_600_BPS
, B_1200_BPS
,
33 B_1800_BPS
, B_2400_BPS
, B_4800_BPS
, B_9600_BPS
, B_19200_BPS
, B_31250_BPS
,
34 B_38400_BPS
, B_57600_BPS
, B_115200_BPS
, B_230400_BPS
38 const char* SerialWindow::kWindowTitle
= "SerialConnect";
41 SerialWindow::SerialWindow()
42 : BWindow(BRect(100, 100, 400, 400), SerialWindow::kWindowTitle
,
43 B_DOCUMENT_WINDOW
, B_QUIT_ON_WINDOW_CLOSE
| B_AUTO_UPDATE_SIZE_LIMITS
)
45 , fSendFilePanel(NULL
)
47 BMenuBar
* menuBar
= new BMenuBar(Bounds(), "menuBar");
48 menuBar
->ResizeToPreferred();
51 r
.top
= menuBar
->Bounds().bottom
+ 1;
52 r
.right
-= B_V_SCROLL_BAR_WIDTH
;
53 fTermView
= new TermView(r
);
54 fTermView
->ResizeToPreferred();
56 r
= fTermView
->Frame();
58 r
.right
= r
.left
+ B_V_SCROLL_BAR_WIDTH
;
60 r
.bottom
-= B_H_SCROLL_BAR_HEIGHT
- 1;
61 BScrollBar
* scrollBar
= new BScrollBar(r
, "scrollbar", NULL
, 0, 0,
64 scrollBar
->SetTarget(fTermView
);
66 ResizeTo(r
.right
- 1, r
.bottom
+ B_H_SCROLL_BAR_HEIGHT
- 1);
68 r
= fTermView
->Frame();
69 r
.top
= r
.bottom
- 37;
71 fStatusBar
= new BStatusBar(r
, "file transfer progress", NULL
, NULL
);
72 fStatusBar
->SetResizingMode(B_FOLLOW_BOTTOM
| B_FOLLOW_LEFT_RIGHT
);
73 fStatusBar
->SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
81 fConnectionMenu
= new BMenu("Connection");
82 fFileMenu
= new BMenu("File");
83 BMenu
* settingsMenu
= new BMenu("Settings");
85 fConnectionMenu
->SetRadioMode(true);
87 menuBar
->AddItem(fConnectionMenu
);
88 menuBar
->AddItem(fFileMenu
);
89 menuBar
->AddItem(settingsMenu
);
91 // TODO edit menu - what's in it ? Usual copy/paste would be nice but we
92 // need a way to select text in the window.
93 //BMenu* editMenu = new BMenu("Edit");
94 //menuBar->AddItem(editMenu);
96 BMenuItem
* logFile
= new BMenuItem("Log to file" B_UTF8_ELLIPSIS
,
97 new BMessage(kMsgLogfile
));
98 fFileMenu
->AddItem(logFile
);
100 // The "send" items are disabled initially. They are enabled only once we
101 // are connected to a serial port.
102 BMessage
* sendMsg
= new BMessage(kMsgSendFile
);
103 sendMsg
->AddString("protocol", "xmodem");
104 BMenuItem
* xmodemSend
= new BMenuItem("XModem send" B_UTF8_ELLIPSIS
,
106 fFileMenu
->AddItem(xmodemSend
);
107 xmodemSend
->SetEnabled(false);
109 BMenuItem
* rawSend
= new BMenuItem("Raw send" B_UTF8_ELLIPSIS
,
110 new BMessage(kMsgSendFile
));
111 fFileMenu
->AddItem(rawSend
);
112 rawSend
->SetEnabled(false);
115 // TODO implement this
116 BMenuItem
* xmodemReceive
= new BMenuItem(
117 "X/Y/Zmodem receive" B_UTF8_ELLIPSIS
, NULL
);
118 fFileMenu
->AddItem(xmodemReceive
);
119 xmodemReceive
->SetEnabled(false);
122 // Configuring all this by menus may be a bit unhandy. Make a setting
124 fBaudrateMenu
= new BMenu("Baud rate");
125 fBaudrateMenu
->SetRadioMode(true);
126 settingsMenu
->AddItem(fBaudrateMenu
);
128 fParityMenu
= new BMenu("Parity");
129 fParityMenu
->SetRadioMode(true);
130 settingsMenu
->AddItem(fParityMenu
);
132 fStopbitsMenu
= new BMenu("Stop bits");
133 fStopbitsMenu
->SetRadioMode(true);
134 settingsMenu
->AddItem(fStopbitsMenu
);
136 fFlowcontrolMenu
= new BMenu("Flow control");
137 fFlowcontrolMenu
->SetRadioMode(true);
138 settingsMenu
->AddItem(fFlowcontrolMenu
);
140 fDatabitsMenu
= new BMenu("Data bits");
141 fDatabitsMenu
->SetRadioMode(true);
142 settingsMenu
->AddItem(fDatabitsMenu
);
144 fLineTerminatorMenu
= new BMenu("Line terminator");
145 fLineTerminatorMenu
->SetRadioMode(true);
146 settingsMenu
->AddItem(fLineTerminatorMenu
);
148 BMessage
* message
= new BMessage(kMsgSettings
);
149 message
->AddInt32("parity", B_NO_PARITY
);
150 BMenuItem
* parityNone
= new BMenuItem("None", message
);
152 message
= new BMessage(kMsgSettings
);
153 message
->AddInt32("parity", B_ODD_PARITY
);
154 BMenuItem
* parityOdd
= new BMenuItem("Odd", message
);
156 message
= new BMessage(kMsgSettings
);
157 message
->AddInt32("parity", B_EVEN_PARITY
);
158 BMenuItem
* parityEven
= new BMenuItem("Even", message
);
160 fParityMenu
->AddItem(parityNone
);
161 fParityMenu
->AddItem(parityOdd
);
162 fParityMenu
->AddItem(parityEven
);
163 fParityMenu
->SetTargetForItems(be_app
);
165 message
= new BMessage(kMsgSettings
);
166 message
->AddInt32("databits", B_DATA_BITS_7
);
167 BMenuItem
* data7
= new BMenuItem("7", message
);
169 message
= new BMessage(kMsgSettings
);
170 message
->AddInt32("databits", B_DATA_BITS_8
);
171 BMenuItem
* data8
= new BMenuItem("8", message
);
173 fDatabitsMenu
->AddItem(data7
);
174 fDatabitsMenu
->AddItem(data8
);
175 fDatabitsMenu
->SetTargetForItems(be_app
);
177 message
= new BMessage(kMsgSettings
);
178 message
->AddInt32("stopbits", B_STOP_BITS_1
);
179 BMenuItem
* stop1
= new BMenuItem("1", message
);
181 message
= new BMessage(kMsgSettings
);
182 message
->AddInt32("stopbits", B_STOP_BITS_2
);
183 BMenuItem
* stop2
= new BMenuItem("2", message
);
185 fStopbitsMenu
->AddItem(stop1
);
186 fStopbitsMenu
->AddItem(stop2
);
187 fStopbitsMenu
->SetTargetForItems(be_app
);
189 // Loop backwards to add fastest rates at top of menu
190 for (int i
= sizeof(kBaudrates
) / sizeof(kBaudrates
[0]); --i
>= 0;)
192 message
= new BMessage(kMsgSettings
);
193 message
->AddInt32("baudrate", kBaudrateConstants
[i
]);
196 sprintf(buffer
, "%d", kBaudrates
[i
]);
197 BMenuItem
* item
= new BMenuItem(buffer
, message
);
199 fBaudrateMenu
->AddItem(item
);
202 message
= new BMessage(kMsgCustomBaudrate
);
203 BMenuItem
* custom
= new BMenuItem("custom" B_UTF8_ELLIPSIS
, message
);
204 fBaudrateMenu
->AddItem(custom
);
206 fBaudrateMenu
->SetTargetForItems(be_app
);
208 message
= new BMessage(kMsgSettings
);
209 message
->AddInt32("flowcontrol", B_HARDWARE_CONTROL
);
210 BMenuItem
* hardware
= new BMenuItem("Hardware", message
);
212 message
= new BMessage(kMsgSettings
);
213 message
->AddInt32("flowcontrol", B_SOFTWARE_CONTROL
);
214 BMenuItem
* software
= new BMenuItem("Software", message
);
216 message
= new BMessage(kMsgSettings
);
217 message
->AddInt32("flowcontrol", B_HARDWARE_CONTROL
| B_SOFTWARE_CONTROL
);
218 BMenuItem
* both
= new BMenuItem("Both", message
);
220 message
= new BMessage(kMsgSettings
);
221 message
->AddInt32("flowcontrol", 0);
222 BMenuItem
* noFlow
= new BMenuItem("None", message
);
224 fFlowcontrolMenu
->AddItem(hardware
);
225 fFlowcontrolMenu
->AddItem(software
);
226 fFlowcontrolMenu
->AddItem(both
);
227 fFlowcontrolMenu
->AddItem(noFlow
);
228 fFlowcontrolMenu
->SetTargetForItems(be_app
);
230 message
= new BMessage(kMsgSettings
);
231 message
->AddString("terminator", "\n");
232 BMenuItem
* lf
= new BMenuItem("LF (\\n)", message
);
234 message
= new BMessage(kMsgSettings
);
235 message
->AddString("terminator", "\r");
236 BMenuItem
* cr
= new BMenuItem("CR (\\r)", message
);
238 message
= new BMessage(kMsgSettings
);
239 message
->AddString("terminator", "\r\n");
240 BMenuItem
* crlf
= new BMenuItem("CR/LF (\\r\\n)", message
);
242 fLineTerminatorMenu
->AddItem(lf
);
243 fLineTerminatorMenu
->AddItem(cr
);
244 fLineTerminatorMenu
->AddItem(crlf
);
250 SerialWindow::~SerialWindow()
252 delete fLogFilePanel
;
253 delete fSendFilePanel
;
257 void SerialWindow::MenusBeginning()
259 // remove all items from the menu
260 fConnectionMenu
->RemoveItems(0, fConnectionMenu
->CountItems(), true);
262 // fill it with the (updated) serial port list
263 BSerialPort serialPort
;
264 int deviceCount
= serialPort
.CountDevices();
265 bool connected
= false;
267 for (int i
= 0; i
< deviceCount
; i
++)
270 serialPort
.GetDeviceName(i
, buffer
, 256);
272 BMessage
* message
= new BMessage(kMsgOpenPort
);
273 message
->AddString("port name", buffer
);
274 BMenuItem
* portItem
= new BMenuItem(buffer
, message
);
275 portItem
->SetTarget(be_app
);
277 const BString
& connectedPort
= ((SerialApp
*)be_app
)->GetPort();
279 if (connectedPort
== buffer
) {
281 portItem
->SetMarked(true);
284 fConnectionMenu
->AddItem(portItem
);
287 if (deviceCount
> 0) {
288 fConnectionMenu
->AddSeparatorItem();
290 BMenuItem
* disconnect
= new BMenuItem("Disconnect",
291 new BMessage(kMsgOpenPort
), 'Z', B_OPTION_KEY
);
293 disconnect
->SetEnabled(false);
294 disconnect
->SetTarget(be_app
);
295 fConnectionMenu
->AddItem(disconnect
);
297 BMenuItem
* noDevices
= new BMenuItem("<no serial port available>", NULL
);
298 noDevices
->SetEnabled(false);
299 fConnectionMenu
->AddItem(noDevices
);
304 void SerialWindow::MessageReceived(BMessage
* message
)
306 switch (message
->what
)
311 bool open
= (message
->FindString("port name", &path
) == B_OK
);
312 int i
= 1; // Skip "log to file", which woeks even when offline.
314 while((item
= fFileMenu
->ItemAt(i
++)))
316 item
->SetEnabled(open
);
324 if (message
->FindData("data", B_RAW_TYPE
, (const void**)&bytes
,
326 fTermView
->PushBytes(bytes
, length
);
331 // Let's lazy init the file panel
332 if (fLogFilePanel
== NULL
) {
333 fLogFilePanel
= new BFilePanel(B_SAVE_PANEL
,
334 &be_app_messenger
, NULL
, B_FILE_NODE
, false);
335 fLogFilePanel
->SetMessage(message
);
337 fLogFilePanel
->Show();
342 // Let's lazy init the file panel
343 if (fSendFilePanel
== NULL
) {
344 fSendFilePanel
= new BFilePanel(B_OPEN_PANEL
,
345 &be_app_messenger
, NULL
, B_FILE_NODE
, false);
347 fSendFilePanel
->SetMessage(message
);
348 fSendFilePanel
->Show();
360 if (message
->FindInt32("databits", (int32
*)&dataBits
) == B_OK
) {
361 for (int i
= 0; i
< fDatabitsMenu
->CountItems(); i
++) {
362 BMenuItem
* item
= fDatabitsMenu
->ItemAt(i
);
364 item
->Message()->FindInt32("databits", &code
);
366 if (code
== dataBits
)
367 item
->SetMarked(true);
371 if (message
->FindInt32("stopbits", (int32
*)&stopBits
) == B_OK
) {
372 for (int i
= 0; i
< fStopbitsMenu
->CountItems(); i
++) {
373 BMenuItem
* item
= fStopbitsMenu
->ItemAt(i
);
375 item
->Message()->FindInt32("stopbits", &code
);
377 if (code
== stopBits
)
378 item
->SetMarked(true);
382 if (message
->FindInt32("parity", (int32
*)&parity
) == B_OK
)
384 for (int i
= 0; i
< fParityMenu
->CountItems(); i
++) {
385 BMenuItem
* item
= fParityMenu
->ItemAt(i
);
387 item
->Message()->FindInt32("parity", &code
);
390 item
->SetMarked(true);
394 if (message
->FindInt32("flowcontrol", (int32
*)&flowcontrol
)
396 for (int i
= 0; i
< fFlowcontrolMenu
->CountItems(); i
++) {
397 BMenuItem
* item
= fFlowcontrolMenu
->ItemAt(i
);
399 item
->Message()->FindInt32("flowcontrol", &code
);
401 if (code
== (int32
)flowcontrol
)
402 item
->SetMarked(true);
406 if (message
->FindInt32("baudrate", &baudrate
) == B_OK
) {
408 BMenuItem
* item
= NULL
;
409 for (i
= 0; i
< fBaudrateMenu
->CountItems(); i
++) {
410 item
= fBaudrateMenu
->ItemAt(i
);
412 item
->Message()->FindInt32("baudrate", &code
);
414 if (baudrate
== code
) {
415 item
->SetMarked(true);
420 if (i
== fBaudrateMenu
->CountItems() && item
!= NULL
) {
421 // Rate was not found, mark it as "custom".
422 // Since that is the last item in the menu, we still point
424 item
->SetMarked(true);
425 item
->Message()->SetInt32("baudrate", baudrate
);
429 if (message
->FindString("terminator", &terminator
) == B_OK
) {
430 fTermView
->SetLineTerminator(terminator
);
431 for (int i
= 0; i
< fLineTerminatorMenu
->CountItems(); i
++) {
432 BMenuItem
* item
= fLineTerminatorMenu
->ItemAt(i
);
434 item
->Message()->FindString("terminator", &code
);
436 if (terminator
== code
)
437 item
->SetMarked(true);
445 // File transfer progress
446 int32 pos
= message
->FindInt32("pos");
447 int32 size
= message
->FindInt32("size");
448 BString label
= message
->FindString("info");
451 if (!fStatusBar
->IsHidden()) {
453 fTermView
->ResizeBy(0, fStatusBar
->Bounds().Height() - 1);
457 text
.SetToFormat("%" B_PRId32
"/%" B_PRId32
, pos
, size
);
458 fStatusBar
->SetMaxValue(size
);
459 fStatusBar
->SetTo(pos
, label
, text
);
460 if (fStatusBar
->IsHidden()) {
462 fTermView
->ResizeBy(0, -(fStatusBar
->Bounds().Height() - 1));
469 BWindow::MessageReceived(message
);