1/*
2 * Copyright 2002-2009, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Michael Pfeiffer
7 *		Philippe Houdoin
8 */
9
10
11#include "AddPrinterDialog.h"
12
13#include <stdio.h>
14
15#include <Button.h>
16#include <Catalog.h>
17#include <FindDirectory.h>
18#include <GroupLayoutBuilder.h>
19#include <Layout.h>
20#include <LayoutBuilder.h>
21#include <Locale.h>
22#include <MenuField.h>
23#include <MenuItem.h>
24#include <MimeType.h>
25#include <NodeInfo.h>
26#include <Path.h>
27
28#include "pr_server.h"
29#include "Globals.h"
30#include "Messages.h"
31#include "PrinterListView.h"
32#include "TransportMenu.h"
33
34
35#undef B_TRANSLATION_CONTEXT
36#define B_TRANSLATION_CONTEXT "AddPrinterDialog"
37
38
39AddPrinterDialog::AddPrinterDialog(BWindow *parent)
40	:
41	Inherited(BRect(78, 71, 400, 300), B_TRANSLATE("Add printer"),
42		B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
43		B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS
44		| B_CLOSE_ON_ESCAPE),
45	fPrintersPrefletMessenger(parent)
46{
47	_BuildGUI(0);
48
49	Show();
50}
51
52
53bool
54AddPrinterDialog::QuitRequested()
55{
56	fPrintersPrefletMessenger.SendMessage(kMsgAddPrinterClosed);
57	return Inherited::QuitRequested();
58}
59
60
61void
62AddPrinterDialog::MessageReceived(BMessage* msg)
63{
64	switch(msg->what) {
65		case B_OK:
66			_AddPrinter(msg);
67			PostMessage(B_QUIT_REQUESTED);
68			break;
69
70		case B_CANCEL:
71			PostMessage(B_QUIT_REQUESTED);
72			break;
73
74		case kNameChangedMsg:
75			fNameText = fName->Text();
76			_Update();
77			break;
78
79		case kPrinterSelectedMsg:
80			_StorePrinter(msg);
81			break;
82
83		case kTransportSelectedMsg:
84			_HandleChangedTransport(msg);
85			break;
86
87		default:
88			Inherited::MessageReceived(msg);
89			break;
90	}
91}
92
93
94void
95AddPrinterDialog::_AddPrinter(BMessage *msg)
96{
97	BMessage m(PSRV_MAKE_PRINTER);
98	BMessenger msgr;
99	if (GetPrinterServerMessenger(msgr) != B_OK)
100		return;
101
102	BString transport;
103	BString transportPath;
104	if (fPrinterText != "Preview") {
105		// Preview printer does not use transport add-on
106		transport = fTransportText;
107		transportPath = fTransportPathText;
108	}
109
110	m.AddString("driver", fPrinterText.String());
111	m.AddString("transport", transport.String());
112	m.AddString("transport path", transportPath.String());
113	m.AddString("printer name", fNameText.String());
114	m.AddString("connection", "Local");
115	msgr.SendMessage(&m);
116		// request print_server to create printer
117}
118
119
120void
121AddPrinterDialog::_StorePrinter(BMessage *msg)
122{
123	BString name;
124	if (msg->FindString("name", &name) != B_OK)
125		name = "";
126
127	fPrinterText = name;
128	_Update();
129}
130
131
132void
133AddPrinterDialog::_HandleChangedTransport(BMessage *msg)
134{
135	BString name;
136	if (msg->FindString("name", &name) != B_OK) {
137		name = "";
138	}
139	fTransportText = name;
140
141	BString path;
142	if (msg->FindString("path", &path) == B_OK) {
143		// transport path selected
144		fTransportPathText = path;
145
146		// mark sub menu
147		void* pointer;
148		if (msg->FindPointer("source", &pointer) == B_OK) {
149			BMenuItem* item = (BMenuItem*)pointer;
150
151			// Update printer name with Transport Path if not filled in
152			if (strlen(fName->Text()) == 0)
153				fName->SetText(item->Label());
154
155			BMenu* menu = item->Menu();
156			int32 index = fTransport->IndexOf(menu);
157			item = fTransport->ItemAt(index);
158			if (item != NULL)
159				item->SetMarked(true);
160		}
161	} else {
162		// transport selected
163		fTransportPathText = "";
164
165		// remove mark from item in sub menu of transport sub menu
166		for (int32 i = fTransport->CountItems() - 1; i >= 0; i --) {
167			BMenu* menu = fTransport->SubmenuAt(i);
168			if (menu != NULL) {
169				BMenuItem* item = menu->FindMarked();
170				if (item != NULL)
171					item->SetMarked(false);
172			}
173		}
174	}
175	_Update();
176}
177
178
179void
180AddPrinterDialog::_BuildGUI(int stage)
181{
182	// add a "printer name" input field
183	fName = new BTextControl("printer_name", B_TRANSLATE("Printer name:"),
184		B_EMPTY_STRING, NULL);
185	fName->SetFont(be_bold_font);
186	fName->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
187	fName->SetModificationMessage(new BMessage(kNameChangedMsg));
188
189	// add a "driver" popup menu field
190	fPrinter = new BPopUpMenu(B_TRANSLATE("<pick one>"));
191	BMenuField *printerMenuField = new BMenuField("drivers_list",
192		B_TRANSLATE("Printer type:"), fPrinter);
193	printerMenuField->SetAlignment(B_ALIGN_RIGHT);
194	_FillMenu(fPrinter, "Print", kPrinterSelectedMsg);
195
196	// add a "connected to" (aka transports list) menu field
197	fTransport = new BPopUpMenu(B_TRANSLATE("<pick one>"));
198	BMenuField *transportMenuField = new BMenuField("transports_list",
199		B_TRANSLATE("Connected to:"), fTransport);
200	transportMenuField->SetAlignment(B_ALIGN_RIGHT);
201	_FillTransportMenu(fTransport);
202
203	// add a "OK" button
204	fOk = new BButton(NULL, B_TRANSLATE("Add"), new BMessage((uint32)B_OK),
205		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
206
207	// add a "Cancel button
208	BButton *cancel = new BButton(NULL, B_TRANSLATE("Cancel"),
209		new BMessage(B_CANCEL));
210
211	BLayoutBuilder::Grid<>(this, B_USE_ITEM_SPACING, B_USE_ITEM_SPACING)
212		.Add(fName->CreateLabelLayoutItem(), 0, 0)
213		.Add(fName->CreateTextViewLayoutItem(), 1, 0)
214		.Add(printerMenuField->CreateLabelLayoutItem(), 0, 1)
215		.Add(printerMenuField->CreateMenuBarLayoutItem(), 1, 1)
216		.Add(transportMenuField->CreateLabelLayoutItem(), 0, 2)
217		.Add(transportMenuField->CreateMenuBarLayoutItem(), 1, 2)
218		.Add(BGroupLayoutBuilder(B_HORIZONTAL)
219			.AddGlue()
220			.Add(cancel)
221			.Add(fOk)
222			, 0, 3, 2)
223		.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
224			B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING);
225
226	AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
227
228	SetDefaultButton(fOk);
229	fOk->MakeDefault(true);
230
231	fName->MakeFocus(true);
232
233	_Update();
234// Stage == 0
235// init_icon 64x114  Add a Local or Network Printer
236//                   ------------------------------
237//                   I would like to add a...
238//                              Local Printer
239//                              Network Printer
240// ------------------------------------------------
241//                                Cancel   Continue
242
243// Add local printer:
244
245// Stage == 1
246// local_icon        Add a Local Printer
247//                   ------------------------------
248//                   Printer Name: ________________
249//                   Printer Type: pick one
250//                   Connected to: pick one
251// ------------------------------------------------
252//                                Cancel        Add
253
254// This seems to be hard coded into the preferences dialog:
255// Don't show Network transport add-on in Printer Type menu.
256// If Printer Type == Preview disable Connect to popup menu.
257// If Printer Type == Serial Port add a submenu to menu item
258//    with names in /dev/ports (if empty remove item from menu)
259// If Printer Type == Parallel Port add a submenu to menu item
260//    with names in /dev/parallel (if empty remove item from menu)
261
262// Printer Driver Setup
263
264// Dialog Info
265// Would you like to make X the default printer?
266//                                        No Yes
267
268// Add network printer:
269
270// Dialog Info
271// Apple Talk networking isn't currenty enabled. If you
272// wish to install a network printer you should enable
273// AppleTalk in the Network preferences.
274//                               Cancel   Open Network
275
276// Stage == 2
277
278// network_icon      Add a Network Printer
279//                   ------------------------------
280//                   Printer Name: ________________
281//                   Printer Type: pick one
282//              AppleTalk Printer: pick one
283// ------------------------------------------------
284//                                Cancel        Add
285}
286
287
288static directory_which gAddonDirs[] = {
289	B_USER_NONPACKAGED_ADDONS_DIRECTORY,
290	B_USER_ADDONS_DIRECTORY,
291	B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
292	B_SYSTEM_ADDONS_DIRECTORY,
293};
294
295
296void
297AddPrinterDialog::_FillMenu(BMenu* menu, const char* path, uint32 what)
298{
299	for (uint32 i = 0; i < sizeof(gAddonDirs) / sizeof(directory_which); i++) {
300		BPath addonPath;
301		if (find_directory(gAddonDirs[i], &addonPath) != B_OK)
302			continue;
303
304		if (addonPath.Append(path) != B_OK)
305			continue;
306
307		BDirectory dir(addonPath.Path());
308		if (dir.InitCheck() != B_OK)
309			continue;
310
311		BEntry entry;
312		while (dir.GetNextEntry(&entry, true) == B_OK) {
313			if (!entry.IsFile())
314				continue;
315
316			BNode node(&entry);
317			if (node.InitCheck() != B_OK)
318				continue;
319
320			BNodeInfo info(&node);
321			if (info.InitCheck() != B_OK)
322				continue;
323
324			char type[B_MIME_TYPE_LENGTH + 1];
325			info.GetType(type);
326			BMimeType entryType(type);
327			// filter non executable entries (like "transport" subfolder...)
328			if (entryType == B_APP_MIME_TYPE) {
329				BPath transportPath;
330				if (entry.GetPath(&transportPath) != B_OK)
331					continue;
332
333				BMessage* msg = new BMessage(what);
334				msg->AddString("name", transportPath.Leaf());
335				menu->AddItem(new BMenuItem(transportPath.Leaf(), msg));
336			}
337		}
338	}
339}
340
341
342void
343AddPrinterDialog::_FillTransportMenu(BMenu* menu)
344{
345	BMessenger msgr;
346	if (GetPrinterServerMessenger(msgr) != B_OK)
347		return;
348
349	for (long idx = 0; ; idx++) {
350		BMessage reply, msg(B_GET_PROPERTY);
351		msg.AddSpecifier("Transport", idx);
352		if (msgr.SendMessage(&msg, &reply) != B_OK)
353			break;
354
355		BMessenger transport;
356		if (reply.FindMessenger("result", &transport) != B_OK)
357			break;
358
359		// Got messenger to transport now
360		msg.MakeEmpty();
361		msg.what = B_GET_PROPERTY;
362		msg.AddSpecifier("Name");
363		if (transport.SendMessage(&msg, &reply) != B_OK)
364			continue;
365
366		BString transportName;
367		if (reply.FindString("result", &transportName) != B_OK)
368			continue;
369
370		// Now get ports...
371		BString portId, portName;
372		int32 error;
373		msg.MakeEmpty();
374		msg.what = B_GET_PROPERTY;
375		msg.AddSpecifier("Ports");
376		if (transport.SendMessage(&msg, &reply) != B_OK
377				|| reply.FindInt32("error", &error) != B_OK
378				|| error != B_OK
379				|| (transportName == "IPP"
380						&& reply.FindString("port_id", &portId) != B_OK)) {
381			// Transport does not provide list of ports
382			BMessage* menuMsg = new BMessage(kTransportSelectedMsg);
383			menuMsg->AddString("name", transportName);
384			menu->AddItem(new BMenuItem(transportName.String(), menuMsg));
385			continue;
386		}
387
388		// Create submenu
389		BMenu* transportMenu = new TransportMenu(transportName.String(),
390			kTransportSelectedMsg, transport, transportName);
391		menu->AddItem(transportMenu);
392		transportMenu->SetRadioMode(true);
393		menu->ItemAt(menu->IndexOf(transportMenu))->
394			SetMessage(new BMessage(kTransportSelectedMsg));
395	}
396}
397
398
399void
400AddPrinterDialog::_Update()
401{
402	fOk->SetEnabled(fNameText != "" && fPrinterText != ""
403		&& (fTransportText != "" || fPrinterText == "Preview"));
404
405	fTransport->SetEnabled(fPrinterText != "Preview");
406}
407