xref: /haiku/src/apps/serialconnect/SerialWindow.cpp (revision f0650dc98fed895fc134a359aab99c27de6a0c6a)
1 /*
2  * Copyright 2012-2015, Adrien Destugues, pulkomandy@pulkomandy.tk
3  * Distributed under the terms of the MIT licence.
4  */
5 
6 
7 #include "SerialWindow.h"
8 
9 #include <stdio.h>
10 
11 #include <FilePanel.h>
12 #include <GroupLayout.h>
13 #include <Menu.h>
14 #include <MenuBar.h>
15 #include <MenuItem.h>
16 #include <ScrollView.h>
17 #include <SerialPort.h>
18 
19 #include "SerialApp.h"
20 #include "TermView.h"
21 
22 
23 const int SerialWindow::kBaudrates[] = { 50, 75, 110, 134, 150, 200, 300, 600,
24 	1200, 1800, 2400, 4800, 9600, 19200, 31250, 38400, 57600, 115200, 230400
25 };
26 
27 
28 // The values for these constants are not in the expected order, so we have to
29 // rely on this lookup table if we want to keep the menu items sorted.
30 const int SerialWindow::kBaudrateConstants[] = { B_50_BPS, B_75_BPS, B_110_BPS,
31 	B_134_BPS, B_150_BPS, B_200_BPS, B_300_BPS, B_600_BPS, B_1200_BPS,
32 	B_1800_BPS, B_2400_BPS, B_4800_BPS, B_9600_BPS, B_19200_BPS, B_31250_BPS,
33 	B_38400_BPS, B_57600_BPS, B_115200_BPS, B_230400_BPS
34 };
35 
36 
37 const char* SerialWindow::kWindowTitle = "SerialConnect";
38 
39 
40 SerialWindow::SerialWindow()
41 	: BWindow(BRect(100, 100, 400, 400), SerialWindow::kWindowTitle,
42 		B_DOCUMENT_WINDOW, B_QUIT_ON_WINDOW_CLOSE | B_AUTO_UPDATE_SIZE_LIMITS)
43 	, fLogFilePanel(NULL)
44 {
45 	BMenuBar* menuBar = new BMenuBar(Bounds(), "menuBar");
46 	menuBar->ResizeToPreferred();
47 
48 	BRect r = Bounds();
49 	r.top = menuBar->Bounds().bottom + 1;
50 	r.right -= B_V_SCROLL_BAR_WIDTH;
51 	fTermView = new TermView(r);
52 	fTermView->ResizeToPreferred();
53 
54 	r = fTermView->Frame();
55 	r.left = r.right + 1;
56 	r.right = r.left + B_V_SCROLL_BAR_WIDTH;
57 	r.top -= 1;
58 	r.bottom -= B_H_SCROLL_BAR_HEIGHT - 1;
59 	BScrollBar* scrollBar = new BScrollBar(r, "scrollbar", NULL, 0, 0,
60 		B_VERTICAL);
61 
62 	scrollBar->SetTarget(fTermView);
63 
64 	ResizeTo(r.right - 1, r.bottom + B_H_SCROLL_BAR_HEIGHT - 1);
65 
66 	AddChild(menuBar);
67 	AddChild(fTermView);
68 	AddChild(scrollBar);
69 
70 	fConnectionMenu = new BMenu("Connection");
71 	BMenu* fileMenu = new BMenu("File");
72 	BMenu* settingsMenu = new BMenu("Settings");
73 
74 	fConnectionMenu->SetRadioMode(true);
75 
76 	menuBar->AddItem(fConnectionMenu);
77 	menuBar->AddItem(fileMenu);
78 	menuBar->AddItem(settingsMenu);
79 
80 	// TODO edit menu - what's in it ?
81 	//BMenu* editMenu = new BMenu("Edit");
82 	//menuBar->AddItem(editMenu);
83 
84 	BMenuItem* logFile = new BMenuItem("Log to file" B_UTF8_ELLIPSIS,
85 		new BMessage(kMsgLogfile));
86 	fileMenu->AddItem(logFile);
87 #if 0
88 	// TODO implement these
89 	BMenuItem* xmodemSend = new BMenuItem("X/Y/ZModem send" B_UTF8_ELLIPSIS,
90 		NULL);
91 	fileMenu->AddItem(xmodemSend);
92 	BMenuItem* xmodemReceive = new BMenuItem(
93 		"X/Y/Zmodem receive" B_UTF8_ELLIPSIS, NULL);
94 	fileMenu->AddItem(xmodemReceive);
95 #endif
96 
97 	// Configuring all this by menus may be a bit unhandy. Make a setting
98 	// window instead ?
99 	fBaudrateMenu = new BMenu("Baud rate");
100 	fBaudrateMenu->SetRadioMode(true);
101 	settingsMenu->AddItem(fBaudrateMenu);
102 
103 	fParityMenu = new BMenu("Parity");
104 	fParityMenu->SetRadioMode(true);
105 	settingsMenu->AddItem(fParityMenu);
106 
107 	fStopbitsMenu = new BMenu("Stop bits");
108 	fStopbitsMenu->SetRadioMode(true);
109 	settingsMenu->AddItem(fStopbitsMenu);
110 
111 	fFlowcontrolMenu = new BMenu("Flow control");
112 	fFlowcontrolMenu->SetRadioMode(true);
113 	settingsMenu->AddItem(fFlowcontrolMenu);
114 
115 	fDatabitsMenu = new BMenu("Data bits");
116 	fDatabitsMenu->SetRadioMode(true);
117 	settingsMenu->AddItem(fDatabitsMenu);
118 
119 	fLineTerminatorMenu = new BMenu("Line terminator");
120 	fLineTerminatorMenu->SetRadioMode(true);
121 	settingsMenu->AddItem(fLineTerminatorMenu);
122 
123 	BMessage* message = new BMessage(kMsgSettings);
124 	message->AddInt32("parity", B_NO_PARITY);
125 	BMenuItem* parityNone = new BMenuItem("None", message);
126 
127 	message = new BMessage(kMsgSettings);
128 	message->AddInt32("parity", B_ODD_PARITY);
129 	BMenuItem* parityOdd = new BMenuItem("Odd", message);
130 
131 	message = new BMessage(kMsgSettings);
132 	message->AddInt32("parity", B_EVEN_PARITY);
133 	BMenuItem* parityEven = new BMenuItem("Even", message);
134 
135 	fParityMenu->AddItem(parityNone);
136 	fParityMenu->AddItem(parityOdd);
137 	fParityMenu->AddItem(parityEven);
138 	fParityMenu->SetTargetForItems(be_app);
139 
140 	message = new BMessage(kMsgSettings);
141 	message->AddInt32("databits", B_DATA_BITS_7);
142 	BMenuItem* data7 = new BMenuItem("7", message);
143 
144 	message = new BMessage(kMsgSettings);
145 	message->AddInt32("databits", B_DATA_BITS_8);
146 	BMenuItem* data8 = new BMenuItem("8", message);
147 
148 	fDatabitsMenu->AddItem(data7);
149 	fDatabitsMenu->AddItem(data8);
150 	fDatabitsMenu->SetTargetForItems(be_app);
151 
152 	message = new BMessage(kMsgSettings);
153 	message->AddInt32("stopbits", B_STOP_BITS_1);
154 	BMenuItem* stop1 = new BMenuItem("1", message);
155 
156 	message = new BMessage(kMsgSettings);
157 	message->AddInt32("stopbits", B_STOP_BITS_2);
158 	BMenuItem* stop2 = new BMenuItem("2", message);
159 
160 	fStopbitsMenu->AddItem(stop1);
161 	fStopbitsMenu->AddItem(stop2);
162 	fStopbitsMenu->SetTargetForItems(be_app);
163 
164 	// Loop backwards to add fastest rates at top of menu
165 	for (int i = sizeof(kBaudrates) / sizeof(kBaudrates[0]); --i >= 0;)
166 	{
167 		message = new BMessage(kMsgSettings);
168 		message->AddInt32("baudrate", kBaudrateConstants[i]);
169 
170 		char buffer[7];
171 		sprintf(buffer, "%d", kBaudrates[i]);
172 		BMenuItem* item = new BMenuItem(buffer, message);
173 
174 		fBaudrateMenu->AddItem(item);
175 	}
176 
177 	fBaudrateMenu->SetTargetForItems(be_app);
178 
179 	message = new BMessage(kMsgSettings);
180 	message->AddInt32("flowcontrol", B_HARDWARE_CONTROL);
181 	BMenuItem* hardware = new BMenuItem("Hardware", message);
182 
183 	message = new BMessage(kMsgSettings);
184 	message->AddInt32("flowcontrol", B_SOFTWARE_CONTROL);
185 	BMenuItem* software = new BMenuItem("Software", message);
186 
187 	message = new BMessage(kMsgSettings);
188 	message->AddInt32("flowcontrol", B_HARDWARE_CONTROL | B_SOFTWARE_CONTROL);
189 	BMenuItem* both = new BMenuItem("Both", message);
190 
191 	message = new BMessage(kMsgSettings);
192 	message->AddInt32("flowcontrol", 0);
193 	BMenuItem* noFlow = new BMenuItem("None", message);
194 
195 	fFlowcontrolMenu->AddItem(hardware);
196 	fFlowcontrolMenu->AddItem(software);
197 	fFlowcontrolMenu->AddItem(both);
198 	fFlowcontrolMenu->AddItem(noFlow);
199 	fFlowcontrolMenu->SetTargetForItems(be_app);
200 
201 	message = new BMessage(kMsgSettings);
202 	message->AddString("terminator", "\n");
203 	BMenuItem* lf = new BMenuItem("LF (\\n)", message);
204 
205 	message = new BMessage(kMsgSettings);
206 	message->AddString("terminator", "\r");
207 	BMenuItem* cr = new BMenuItem("CR (\\r)", message);
208 
209 	message = new BMessage(kMsgSettings);
210 	message->AddString("terminator", "\r\n");
211 	BMenuItem* crlf = new BMenuItem("CR/LF (\\r\\n)", message);
212 
213 	fLineTerminatorMenu->AddItem(lf);
214 	fLineTerminatorMenu->AddItem(cr);
215 	fLineTerminatorMenu->AddItem(crlf);
216 
217 	CenterOnScreen();
218 }
219 
220 
221 SerialWindow::~SerialWindow()
222 {
223 	delete fLogFilePanel;
224 }
225 
226 
227 void SerialWindow::MenusBeginning()
228 {
229 	// remove all items from the menu
230 	fConnectionMenu->RemoveItems(0, fConnectionMenu->CountItems(), true);
231 
232 	// fill it with the (updated) serial port list
233 	BSerialPort serialPort;
234 	int deviceCount = serialPort.CountDevices();
235 	bool connected = false;
236 
237 	for (int i = 0; i < deviceCount; i++)
238 	{
239 		char buffer[256];
240 		serialPort.GetDeviceName(i, buffer, 256);
241 
242 		BMessage* message = new BMessage(kMsgOpenPort);
243 		message->AddString("port name", buffer);
244 		BMenuItem* portItem = new BMenuItem(buffer, message);
245 		portItem->SetTarget(be_app);
246 
247 		const BString& connectedPort = ((SerialApp*)be_app)->GetPort();
248 
249 		if (connectedPort == buffer) {
250 			connected = true;
251 			portItem->SetMarked(true);
252 		}
253 
254 		fConnectionMenu->AddItem(portItem);
255 	}
256 
257 	if (deviceCount > 0) {
258 		fConnectionMenu->AddSeparatorItem();
259 
260 		BMenuItem* disconnect = new BMenuItem("Disconnect",
261 			new BMessage(kMsgOpenPort), 'Z', B_OPTION_KEY);
262 		if (!connected)
263 			disconnect->SetEnabled(false);
264 		disconnect->SetTarget(be_app);
265 		fConnectionMenu->AddItem(disconnect);
266 	} else {
267 		BMenuItem* noDevices = new BMenuItem("<no serial port available>", NULL);
268 		noDevices->SetEnabled(false);
269 		fConnectionMenu->AddItem(noDevices);
270 	}
271 }
272 
273 
274 void SerialWindow::MessageReceived(BMessage* message)
275 {
276 	switch (message->what)
277 	{
278 		case kMsgDataRead:
279 		{
280 			const char* bytes;
281 			ssize_t length;
282 			if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
283 					&length) == B_OK)
284 				fTermView->PushBytes(bytes, length);
285 			return;
286 		}
287 		case kMsgLogfile:
288 		{
289 			// Let's lazy init the file panel
290 			if (fLogFilePanel == NULL) {
291 				fLogFilePanel = new BFilePanel(B_SAVE_PANEL, &be_app_messenger,
292 					NULL, B_FILE_NODE, false);
293 				fLogFilePanel->SetMessage(message);
294 			}
295 			fLogFilePanel->Show();
296 			return;
297 		}
298 		case kMsgSettings:
299 		{
300 			int32 baudrate;
301 			stop_bits stopBits;
302 			data_bits dataBits;
303 			parity_mode parity;
304 			uint32 flowcontrol;
305 			BString terminator;
306 
307 			if (message->FindInt32("databits", (int32*)&dataBits) == B_OK) {
308 				for (int i = 0; i < fDatabitsMenu->CountItems(); i++) {
309 					BMenuItem* item = fDatabitsMenu->ItemAt(i);
310 					int32 code;
311 					item->Message()->FindInt32("databits", &code);
312 
313 					if (code == dataBits)
314 						item->SetMarked(true);
315 				}
316 			}
317 
318 			if (message->FindInt32("stopbits", (int32*)&stopBits) == B_OK) {
319 				for (int i = 0; i < fStopbitsMenu->CountItems(); i++) {
320 					BMenuItem* item = fStopbitsMenu->ItemAt(i);
321 					int32 code;
322 					item->Message()->FindInt32("stopbits", &code);
323 
324 					if (code == stopBits)
325 						item->SetMarked(true);
326 				}
327 			}
328 
329 			if (message->FindInt32("parity", (int32*)&parity) == B_OK)
330 			{
331 				for (int i = 0; i < fParityMenu->CountItems(); i++) {
332 					BMenuItem* item = fParityMenu->ItemAt(i);
333 					int32 code;
334 					item->Message()->FindInt32("parity", &code);
335 
336 					if (code == parity)
337 						item->SetMarked(true);
338 				}
339 			}
340 
341 			if (message->FindInt32("flowcontrol", (int32*)&flowcontrol)
342 					== B_OK) {
343 				for (int i = 0; i < fFlowcontrolMenu->CountItems(); i++) {
344 					BMenuItem* item = fFlowcontrolMenu->ItemAt(i);
345 					int32 code;
346 					item->Message()->FindInt32("flowcontrol", &code);
347 
348 					if (code == (int32)flowcontrol)
349 						item->SetMarked(true);
350 				}
351 			}
352 
353 			if (message->FindInt32("baudrate", &baudrate) == B_OK) {
354 				for (int i = 0; i < fBaudrateMenu->CountItems(); i++) {
355 					BMenuItem* item = fBaudrateMenu->ItemAt(i);
356 					int32 code;
357 					item->Message()->FindInt32("baudrate", &code);
358 
359 					if (baudrate == code)
360 						item->SetMarked(true);
361 				}
362 			}
363 
364 			if (message->FindString("terminator", &terminator) == B_OK) {
365 				fTermView->SetLineTerminator(terminator);
366 				for (int i = 0; i < fLineTerminatorMenu->CountItems(); i++) {
367 					BMenuItem* item = fLineTerminatorMenu->ItemAt(i);
368 					BString code;
369 					item->Message()->FindString("terminator", &code);
370 
371 					if (terminator == code)
372 						item->SetMarked(true);
373 				}
374 			}
375 
376 			return;
377 		}
378 	}
379 
380 	BWindow::MessageReceived(message);
381 }
382