/*
 * Copyright 2002-2009, Haiku.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Michael Pfeiffer
 *		Philippe Houdoin
 */
 
 
#include "AddPrinterDialog.h"
 
#include <stdio.h>
 
#include <Button.h>
#include <Catalog.h>
#include <FindDirectory.h>
#include <GroupLayoutBuilder.h>
#include <Layout.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <MimeType.h>
#include <NodeInfo.h>
#include <Path.h>
 
#include "pr_server.h"
#include "Globals.h"
#include "Messages.h"
#include "PrinterListView.h"
#include "TransportMenu.h"
 
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "AddPrinterDialog"
 
 
AddPrinterDialog::AddPrinterDialog(BWindow *parent)
	:
	Inherited(BRect(78, 71, 400, 300), B_TRANSLATE("Add printer"),
		B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
		B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS
		| B_CLOSE_ON_ESCAPE),
	fPrintersPrefletMessenger(parent)
{
	_BuildGUI(0);
 
	Show();
}
 
 
bool
AddPrinterDialog::QuitRequested()
{
	fPrintersPrefletMessenger.SendMessage(kMsgAddPrinterClosed);
	return Inherited::QuitRequested();
}
 
 
void
AddPrinterDialog::MessageReceived(BMessage* msg)
{
	switch(msg->what) {
		case B_OK:
			_AddPrinter(msg);
			PostMessage(B_QUIT_REQUESTED);
			break;
 
		case B_CANCEL:
			PostMessage(B_QUIT_REQUESTED);
			break;
 
		case kNameChangedMsg:
			fNameText = fName->Text();
			_Update();
			break;
 
		case kPrinterSelectedMsg:
			_StorePrinter(msg);
			break;
 
		case kTransportSelectedMsg:
			_HandleChangedTransport(msg);
			break;
 
		default:
			Inherited::MessageReceived(msg);
			break;
	}
}
 
 
void
AddPrinterDialog::_AddPrinter(BMessage *msg)
{
	BMessage m(PSRV_MAKE_PRINTER);
	BMessenger msgr;
	if (GetPrinterServerMessenger(msgr) != B_OK)
		return;
 
	BString transport;
	BString transportPath;
	if (fPrinterText != "Preview") {
		// Preview printer does not use transport add-on
		transport = fTransportText;
		transportPath = fTransportPathText;
	}
 
	m.AddString("driver", fPrinterText.String());
	m.AddString("transport", transport.String());
	m.AddString("transport path", transportPath.String());
	m.AddString("printer name", fNameText.String());
	m.AddString("connection", "Local");
	msgr.SendMessage(&m);
		// request print_server to create printer
}
 
 
void
AddPrinterDialog::_StorePrinter(BMessage *msg)
{
	BString name;
	if (msg->FindString("name", &name) != B_OK)
		name = "";
 
	fPrinterText = name;
	_Update();
}
 
 
void
AddPrinterDialog::_HandleChangedTransport(BMessage *msg)
{
	BString name;
	if (msg->FindString("name", &name) != B_OK) {
		name = "";
	}
	fTransportText = name;
 
	BString path;
	if (msg->FindString("path", &path) == B_OK) {
		// transport path selected
		fTransportPathText = path;
 
		// mark sub menu
		void* pointer;
		if (msg->FindPointer("source", &pointer) == B_OK) {
			BMenuItem* item = (BMenuItem*)pointer;
 
			// Update printer name with Transport Path if not filled in
			if (strlen(fName->Text()) == 0)
				fName->SetText(item->Label());
 
			BMenu* menu = item->Menu();
			int32 index = fTransport->IndexOf(menu);
			item = fTransport->ItemAt(index);
			if (item != NULL)
				item->SetMarked(true);
		}
	} else {
		// transport selected
		fTransportPathText = "";
 
		// remove mark from item in sub menu of transport sub menu
		for (int32 i = fTransport->CountItems() - 1; i >= 0; i --) {
			BMenu* menu = fTransport->SubmenuAt(i);
			if (menu != NULL) {
				BMenuItem* item = menu->FindMarked();
				if (item != NULL)
					item->SetMarked(false);
			}
		}
	}
	_Update();
}
 
 
void
AddPrinterDialog::_BuildGUI(int stage)
{
	// add a "printer name" input field
	fName = new BTextControl("printer_name", B_TRANSLATE("Printer name:"),
		B_EMPTY_STRING, NULL);
	fName->SetFont(be_bold_font);
	fName->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
	fName->SetModificationMessage(new BMessage(kNameChangedMsg));
 
	// add a "driver" popup menu field
	fPrinter = new BPopUpMenu(B_TRANSLATE("<pick one>"));
	BMenuField *printerMenuField = new BMenuField("drivers_list",
		B_TRANSLATE("Printer type:"), fPrinter);
	printerMenuField->SetAlignment(B_ALIGN_RIGHT);
	_FillMenu(fPrinter, "Print", kPrinterSelectedMsg);
 
	// add a "connected to" (aka transports list) menu field
	fTransport = new BPopUpMenu(B_TRANSLATE("<pick one>"));
	BMenuField *transportMenuField = new BMenuField("transports_list",
		B_TRANSLATE("Connected to:"), fTransport);
	transportMenuField->SetAlignment(B_ALIGN_RIGHT);
	_FillTransportMenu(fTransport);
 
	// add a "OK" button
	fOk = new BButton(NULL, B_TRANSLATE("Add"), new BMessage((uint32)B_OK),
		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
 
	// add a "Cancel button
	BButton *cancel = new BButton(NULL, B_TRANSLATE("Cancel"),
		new BMessage(B_CANCEL));
 
	BLayoutBuilder::Grid<>(this, B_USE_ITEM_SPACING, B_USE_ITEM_SPACING)
		.Add(fName->CreateLabelLayoutItem(), 0, 0)
		.Add(fName->CreateTextViewLayoutItem(), 1, 0)
		.Add(printerMenuField->CreateLabelLayoutItem(), 0, 1)
		.Add(printerMenuField->CreateMenuBarLayoutItem(), 1, 1)
		.Add(transportMenuField->CreateLabelLayoutItem(), 0, 2)
		.Add(transportMenuField->CreateMenuBarLayoutItem(), 1, 2)
		.Add(BGroupLayoutBuilder(B_HORIZONTAL)
			.AddGlue()
			.Add(cancel)
			.Add(fOk)
			, 0, 3, 2)
		.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
			B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING);
 
	AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
 
	SetDefaultButton(fOk);
	fOk->MakeDefault(true);
 
	fName->MakeFocus(true);
 
	_Update();
// Stage == 0
// init_icon 64x114  Add a Local or Network Printer
//                   ------------------------------
//                   I would like to add a...
//                              Local Printer
//                              Network Printer
// ------------------------------------------------
//                                Cancel   Continue
 
// Add local printer:
 
// Stage == 1
// local_icon        Add a Local Printer
//                   ------------------------------
//                   Printer Name: ________________
//                   Printer Type: pick one
//                   Connected to: pick one
// ------------------------------------------------
//                                Cancel        Add
 
// This seems to be hard coded into the preferences dialog:
// Don't show Network transport add-on in Printer Type menu.
// If Printer Type == Preview disable Connect to popup menu.
// If Printer Type == Serial Port add a submenu to menu item
//    with names in /dev/ports (if empty remove item from menu)
// If Printer Type == Parallel Port add a submenu to menu item
//    with names in /dev/parallel (if empty remove item from menu)
 
// Printer Driver Setup
 
// Dialog Info
// Would you like to make X the default printer?
//                                        No Yes
 
// Add network printer:
 
// Dialog Info
// Apple Talk networking isn't currenty enabled. If you
// wish to install a network printer you should enable
// AppleTalk in the Network preferences.
//                               Cancel   Open Network
 
// Stage == 2
 
// network_icon      Add a Network Printer
//                   ------------------------------
//                   Printer Name: ________________
//                   Printer Type: pick one
//              AppleTalk Printer: pick one
// ------------------------------------------------
//                                Cancel        Add
}
 
 
static directory_which gAddonDirs[] = {
	B_USER_NONPACKAGED_ADDONS_DIRECTORY,
	B_USER_ADDONS_DIRECTORY,
	B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
	B_SYSTEM_ADDONS_DIRECTORY,
};
 
 
void
AddPrinterDialog::_FillMenu(BMenu* menu, const char* path, uint32 what)
{
	for (uint32 i = 0; i < sizeof(gAddonDirs) / sizeof(directory_which); i++) {
		BPath addonPath;
		if (find_directory(gAddonDirs[i], &addonPath) != B_OK)
			continue;
 
		if (addonPath.Append(path) != B_OK)
			continue;
 
		BDirectory dir(addonPath.Path());
		if (dir.InitCheck() != B_OK)
			continue;
 
		BEntry entry;
		while (dir.GetNextEntry(&entry, true) == B_OK) {
			if (!entry.IsFile())
				continue;
 
			BNode node(&entry);
			if (node.InitCheck() != B_OK)
				continue;
 
			BNodeInfo info(&node);
			if (info.InitCheck() != B_OK)
				continue;
 
			char type[B_MIME_TYPE_LENGTH + 1];
			info.GetType(type);
			BMimeType entryType(type);
			// filter non executable entries (like "transport" subfolder...)
			if (entryType == B_APP_MIME_TYPE) {
				BPath transportPath;
				if (entry.GetPath(&transportPath) != B_OK)
					continue;
 
				BMessage* msg = new BMessage(what);
				msg->AddString("name", transportPath.Leaf());
				menu->AddItem(new BMenuItem(transportPath.Leaf(), msg));
			}
		}
	}
}
 
 
void
AddPrinterDialog::_FillTransportMenu(BMenu* menu)
{
	BMessenger msgr;
	if (GetPrinterServerMessenger(msgr) != B_OK)
		return;
 
	for (long idx = 0; ; idx++) {
		BMessage reply, msg(B_GET_PROPERTY);
		msg.AddSpecifier("Transport", idx);
		if (msgr.SendMessage(&msg, &reply) != B_OK)
			break;
 
		BMessenger transport;
		if (reply.FindMessenger("result", &transport) != B_OK)
			break;
 
		// Got messenger to transport now
		msg.MakeEmpty();
		msg.what = B_GET_PROPERTY;
		msg.AddSpecifier("Name");
		if (transport.SendMessage(&msg, &reply) != B_OK)
			continue;
 
		BString transportName;
		if (reply.FindString("result", &transportName) != B_OK)
			continue;
 
		// Now get ports...
		BString portId, portName;
		int32 error;
		msg.MakeEmpty();
		msg.what = B_GET_PROPERTY;
		msg.AddSpecifier("Ports");
		if (transport.SendMessage(&msg, &reply) != B_OK
				|| reply.FindInt32("error", &error) != B_OK
				|| error != B_OK
				|| (transportName == "IPP"
						&& reply.FindString("port_id", &portId) != B_OK)) {
			// Transport does not provide list of ports
			BMessage* menuMsg = new BMessage(kTransportSelectedMsg);
			menuMsg->AddString("name", transportName);
			menu->AddItem(new BMenuItem(transportName.String(), menuMsg));
			continue;
		}
 
		// Create submenu
		BMenu* transportMenu = new TransportMenu(transportName.String(),
			kTransportSelectedMsg, transport, transportName);
		menu->AddItem(transportMenu);
		transportMenu->SetRadioMode(true);
		menu->ItemAt(menu->IndexOf(transportMenu))->
			SetMessage(new BMessage(kTransportSelectedMsg));
	}
}
 
 
void
AddPrinterDialog::_Update()
{
	fOk->SetEnabled(fNameText != "" && fPrinterText != ""
		&& (fTransportText != "" || fPrinterText == "Preview"));
 
	fTransport->SetEnabled(fPrinterText != "Preview");
}

V773 Visibility scope of the 'transportMenuField' pointer was exited without releasing the memory. A memory leak is possible.

V773 Visibility scope of the 'printerMenuField' pointer was exited without releasing the memory. A memory leak is possible.