1/*
2 * Copyright 2012, Adrien Destugues, pulkomandy@gmail.com
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 <SerialPort.h>
17
18#include "SerialApp.h"
19#include "TermView.h"
20
21
22SerialWindow::SerialWindow()
23	: BWindow(BRect(100, 100, 400, 400), SerialWindow::kWindowTitle,
24		B_DOCUMENT_WINDOW, B_QUIT_ON_WINDOW_CLOSE | B_AUTO_UPDATE_SIZE_LIMITS)
25	, fLogFilePanel(NULL)
26{
27	SetLayout(new BGroupLayout(B_VERTICAL, 0.0f));
28
29	BMenuBar* menuBar = new BMenuBar("menuBar");
30	fTermView = new TermView();
31
32	AddChild(menuBar);
33	AddChild(fTermView);
34
35	fConnectionMenu = new BMenu("Connection");
36	BMenu* fileMenu = new BMenu("File");
37	BMenu* settingsMenu = new BMenu("Settings");
38
39	fConnectionMenu->SetRadioMode(true);
40
41	menuBar->AddItem(fConnectionMenu);
42	menuBar->AddItem(fileMenu);
43	menuBar->AddItem(settingsMenu);
44
45	// TODO edit menu - what's in it ?
46	//BMenu* editMenu = new BMenu("Edit");
47	//menuBar->AddItem(editMenu);
48
49	BMenuItem* logFile = new BMenuItem("Log to file" B_UTF8_ELLIPSIS,
50		new BMessage(kMsgLogfile));
51	fileMenu->AddItem(logFile);
52	BMenuItem* xmodemSend = new BMenuItem("X/Y/ZModem send" B_UTF8_ELLIPSIS,
53		NULL);
54	fileMenu->AddItem(xmodemSend);
55	BMenuItem* xmodemReceive = new BMenuItem(
56		"X/Y/Zmodem receive" B_UTF8_ELLIPSIS, NULL);
57	fileMenu->AddItem(xmodemReceive);
58
59	// Configuring all this by menus may be a bit unhandy. Make a setting
60	// window instead ?
61	BMenu* baudRate = new BMenu("Baud rate");
62	baudRate->SetRadioMode(true);
63	settingsMenu->AddItem(baudRate);
64
65	BMenu* parity = new BMenu("Parity");
66	parity->SetRadioMode(true);
67	settingsMenu->AddItem(parity);
68
69	BMenu* stopBits = new BMenu("Stop bits");
70	stopBits->SetRadioMode(true);
71	settingsMenu->AddItem(stopBits);
72
73	BMenu* flowControl = new BMenu("Flow control");
74	flowControl->SetRadioMode(true);
75	settingsMenu->AddItem(flowControl);
76
77	BMenu* dataBits = new BMenu("Data bits");
78	dataBits->SetRadioMode(true);
79	settingsMenu->AddItem(dataBits);
80
81
82	BMessage* message = new BMessage(kMsgSettings);
83	message->AddInt32("parity", B_NO_PARITY);
84	BMenuItem* parityNone = new BMenuItem("None", message);
85
86	message = new BMessage(kMsgSettings);
87	message->AddInt32("parity", B_ODD_PARITY);
88	BMenuItem* parityOdd = new BMenuItem("Odd", message);
89
90	message = new BMessage(kMsgSettings);
91	message->AddInt32("parity", B_EVEN_PARITY);
92	BMenuItem* parityEven = new BMenuItem("Even", message);
93	parityNone->SetMarked(true);
94
95	parity->AddItem(parityNone);
96	parity->AddItem(parityOdd);
97	parity->AddItem(parityEven);
98	parity->SetTargetForItems(be_app);
99
100	message = new BMessage(kMsgSettings);
101	message->AddInt32("databits", B_DATA_BITS_7);
102	BMenuItem* data7 = new BMenuItem("7", message);
103
104	message = new BMessage(kMsgSettings);
105	message->AddInt32("databits", B_DATA_BITS_8);
106	BMenuItem* data8 = new BMenuItem("8", message);
107	data8->SetMarked(true);
108
109	dataBits->AddItem(data7);
110	dataBits->AddItem(data8);
111	dataBits->SetTargetForItems(be_app);
112
113	message = new BMessage(kMsgSettings);
114	message->AddInt32("stopbits", B_STOP_BITS_1);
115	BMenuItem* stop1 = new BMenuItem("1", NULL);
116
117	message = new BMessage(kMsgSettings);
118	message->AddInt32("stopbits", B_STOP_BITS_2);
119	BMenuItem* stop2 = new BMenuItem("2", NULL);
120	stop1->SetMarked(true);
121
122	stopBits->AddItem(stop1);
123	stopBits->AddItem(stop2);
124	stopBits->SetTargetForItems(be_app);
125
126	static const int baudrates[] = { 50, 75, 110, 134, 150, 200, 300, 600,
127		1200, 1800, 2400, 4800, 9600, 19200, 31250, 38400, 57600, 115200,
128		230400
129	};
130
131	// Loop backwards to add fastest rates at top of menu
132	for (int i = sizeof(baudrates) / sizeof(char*); --i >= 0;)
133	{
134		message = new BMessage(kMsgSettings);
135		message->AddInt32("baudrate", baudrates[i]);
136
137		char buffer[7];
138		sprintf(buffer,"%d", baudrates[i]);
139		BMenuItem* item = new BMenuItem(buffer, message);
140
141		if (baudrates[i] == 19200)
142			item->SetMarked(true);
143
144		baudRate->AddItem(item);
145	}
146
147	baudRate->SetTargetForItems(be_app);
148
149	message = new BMessage(kMsgSettings);
150	message->AddInt32("flowcontrol", B_HARDWARE_CONTROL);
151	BMenuItem* hardware = new BMenuItem("Hardware", message);
152	hardware->SetMarked(true);
153
154	message = new BMessage(kMsgSettings);
155	message->AddInt32("flowcontrol", B_SOFTWARE_CONTROL);
156	BMenuItem* software = new BMenuItem("Software", message);
157
158	message = new BMessage(kMsgSettings);
159	message->AddInt32("flowcontrol", B_HARDWARE_CONTROL | B_SOFTWARE_CONTROL);
160	BMenuItem* both = new BMenuItem("Both", message);
161
162	message = new BMessage(kMsgSettings);
163	message->AddInt32("flowcontrol", 0);
164	BMenuItem* noFlow = new BMenuItem("None", message);
165
166	flowControl->AddItem(hardware);
167	flowControl->AddItem(software);
168	flowControl->AddItem(both);
169	flowControl->AddItem(noFlow);
170	flowControl->SetTargetForItems(be_app);
171
172	CenterOnScreen();
173}
174
175
176
177SerialWindow::~SerialWindow()
178{
179	delete fLogFilePanel;
180}
181
182
183void SerialWindow::MenusBeginning()
184{
185	// remove all items from the menu
186	while(fConnectionMenu->RemoveItem(0L));
187
188	// fill it with the (updated) serial port list
189	BSerialPort serialPort;
190	int deviceCount = serialPort.CountDevices();
191
192	for(int i = 0; i < deviceCount; i++)
193	{
194		char buffer[256];
195		serialPort.GetDeviceName(i, buffer, 256);
196
197		BMessage* message = new BMessage(kMsgOpenPort);
198		message->AddString("port name", buffer);
199		BMenuItem* portItem = new BMenuItem(buffer, message);
200		portItem->SetTarget(be_app);
201
202		fConnectionMenu->AddItem(portItem);
203	}
204
205	if (deviceCount > 0) {
206		fConnectionMenu->AddSeparatorItem();
207
208		BMenuItem* disconnect = new BMenuItem("Disconnect",
209			new BMessage(kMsgOpenPort), 'Z', B_OPTION_KEY);
210		fConnectionMenu->AddItem(disconnect);
211	} else {
212		BMenuItem* noDevices = new BMenuItem("<no serial port available>", NULL);
213		noDevices->SetEnabled(false);
214		fConnectionMenu->AddItem(noDevices);
215	}
216}
217
218void SerialWindow::MessageReceived(BMessage* message)
219{
220	switch(message->what)
221	{
222		case kMsgOpenPort:
223		{
224			BMenuItem* disconnectMenu;
225			if(message->FindPointer("source", (void**)&disconnectMenu) == B_OK)
226				disconnectMenu->SetMarked(false);
227			be_app->PostMessage(new BMessage(*message));
228			break;
229		}
230		case kMsgDataRead:
231		{
232			const char* bytes;
233			ssize_t length;
234			message->FindData("data", B_RAW_TYPE, (const void**)&bytes, &length);
235			fTermView->PushBytes(bytes, length);
236			break;
237		}
238		case kMsgLogfile:
239		{
240			// Let's lazy init the file panel
241			if(fLogFilePanel == NULL) {
242				fLogFilePanel = new BFilePanel(B_SAVE_PANEL, &be_app_messenger,
243					NULL, B_FILE_NODE, false);
244				fLogFilePanel->SetMessage(message);
245			}
246			fLogFilePanel->Show();
247			break;
248		}
249		default:
250			BWindow::MessageReceived(message);
251	}
252}
253
254
255const char* SerialWindow::kWindowTitle = "SerialConnect";
256