1/*
2 * Copyright 2001-2011, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Michael Pfeiffer
7 *		Philippe Houdoin
8 */
9
10
11#include "PrintersWindow.h"
12
13#include <stdio.h>
14
15#include <Application.h>
16#include <Button.h>
17#include <Catalog.h>
18#include <FindDirectory.h>
19#include <GroupLayout.h>
20#include <Layout.h>
21#include <ListView.h>
22#include <Locale.h>
23#include <PrintJob.h>
24#include <ScrollView.h>
25
26#include "pr_server.h"
27#include "AddPrinterDialog.h"
28#include "Globals.h"
29#include "JobListView.h"
30#include "Messages.h"
31#include "PrinterListView.h"
32#include "TestPageView.h"
33#include "SpoolFolder.h"
34
35
36#undef B_TRANSLATION_CONTEXT
37#define B_TRANSLATION_CONTEXT "PrintersWindow"
38
39
40class TestPageWindow : public BWindow {
41public:
42						TestPageWindow(BPrintJob* job, PrinterItem* printer);
43	virtual				~TestPageWindow();
44
45			void 		MessageReceived(BMessage* message);
46private:
47		BPrintJob*		fJob;
48		TestPageView*	fTestPage;
49};
50
51
52TestPageWindow::TestPageWindow(BPrintJob* job, PrinterItem* printer)
53	: BWindow(job->PaperRect().OffsetByCopy(-20000, -20000), B_TRANSLATE("Test Page"),
54		B_TITLED_WINDOW, 0), fJob(job)
55{
56	fTestPage = new TestPageView(job->PrintableRect(), printer);
57
58	// SetLayout(new BGroupLayout(B_VERTICAL));
59	AddChild(fTestPage);
60}
61
62
63TestPageWindow::~TestPageWindow()
64{
65	delete fJob;
66}
67
68
69void
70TestPageWindow::MessageReceived(BMessage* message)
71{
72	if (message->what != kMsgPrintTestPage) {
73		BWindow::MessageReceived(message);
74		return;
75	}
76
77	fJob->BeginJob();
78
79	fJob->DrawView(fTestPage, fTestPage->Bounds(), B_ORIGIN);
80	fJob->SpoolPage();
81
82	if (!fJob->CanContinue())
83		return;
84
85	fJob->CommitJob();
86
87	Quit();
88}
89
90
91// #pragma mark PrintersWindow main class
92
93
94PrintersWindow::PrintersWindow(BRect frame)
95	:
96	BWindow(BRect(78, 71, 761, 509), B_TRANSLATE_SYSTEM_NAME("Printers"),
97		B_TITLED_WINDOW, 0),
98	fSelectedPrinter(NULL),
99	fAddingPrinter(false)
100{
101	_BuildGUI();
102}
103
104
105bool
106PrintersWindow::QuitRequested()
107{
108	bool result = Inherited::QuitRequested();
109	if (result)
110		be_app->PostMessage(B_QUIT_REQUESTED);
111
112	return result;
113}
114
115
116void
117PrintersWindow::MessageReceived(BMessage* msg)
118{
119	switch(msg->what) {
120		case kMsgPrinterSelected:
121		{
122			fSelectedPrinter = fPrinterListView->SelectedItem();
123			if (fSelectedPrinter) {
124				fJobsBox->SetLabel((BString(B_TRANSLATE("Print jobs for "))
125					<< fSelectedPrinter->Name()).String());
126				fMakeDefault->SetEnabled(true);
127				fRemove->SetEnabled(true);
128				fJobListView->SetSpoolFolder(fSelectedPrinter->Folder());
129			} else {
130				fJobsBox->SetLabel(
131					B_TRANSLATE("Print jobs: No printer selected"));
132				fMakeDefault->SetEnabled(false);
133				fRemove->SetEnabled(false);
134				fSelectedPrinter = NULL;
135				fJobListView->SetSpoolFolder(NULL);
136			}
137			_UpdateJobButtons();
138			_UpdatePrinterButtons();
139			break;
140		}
141
142		case kMsgAddPrinter:
143			if (!fAddingPrinter) {
144				fAddingPrinter = true;
145				new AddPrinterDialog(this);
146			}
147			break;
148
149		case kMsgAddPrinterClosed:
150			fAddingPrinter = false;
151			break;
152
153		case kMsgRemovePrinter:
154		{
155			fSelectedPrinter = fPrinterListView->SelectedItem();
156			if (fSelectedPrinter)
157				fSelectedPrinter->Remove(fPrinterListView);
158			break;
159		}
160
161		case kMsgMakeDefaultPrinter:
162		{
163			PrinterItem* printer = fPrinterListView->SelectedItem();
164			if (printer && printer == fPrinterListView->ActivePrinter())
165				break;
166			BMessenger msgr;
167			if (printer && GetPrinterServerMessenger(msgr) == B_OK) {
168				BMessage setActivePrinter(B_SET_PROPERTY);
169				setActivePrinter.AddSpecifier("ActivePrinter");
170				setActivePrinter.AddString("data", printer->Name());
171				msgr.SendMessage(&setActivePrinter);
172				_UpdatePrinterButtons();
173			}
174			break;
175		}
176
177		case kMsgPrintTestPage:
178		{
179			fSelectedPrinter = fPrinterListView->SelectedItem();
180			if (fSelectedPrinter)
181				PrintTestPage(fSelectedPrinter);
182			break;
183		}
184
185		case kMsgCancelJob:
186			fJobListView->CancelJob();
187			break;
188
189		case kMsgRestartJob:
190			fJobListView->RestartJob();
191			break;
192
193		case kMsgJobSelected:
194			_UpdateJobButtons();
195			break;
196
197		case B_PRINTER_CHANGED:
198		{
199			// active printer could have been changed, even outside of prefs
200			BString activePrinterName(ActivePrinterName());
201			PrinterItem* item = fPrinterListView->ActivePrinter();
202			if (item && item->Name() != activePrinterName)
203				fPrinterListView->UpdateItem(item);
204
205			for (int32 i = 0; i < fPrinterListView->CountItems(); ++i) {
206				item = dynamic_cast<PrinterItem*>(fPrinterListView->ItemAt(i));
207				if (item && item->Name() == activePrinterName) {
208					fPrinterListView->UpdateItem(item);
209					fPrinterListView->SetActivePrinter(item);
210					break;
211				}
212			}
213		}	break;
214
215		default:
216			Inherited::MessageReceived(msg);
217	}
218}
219
220
221void
222PrintersWindow::PrintTestPage(PrinterItem* printer)
223{
224	BPrintJob* job = new BPrintJob(B_TRANSLATE("Test Page"));
225	job->ConfigPage();
226
227	// job->ConfigJob();
228
229	BMessage* settings = job->Settings();
230	if (settings == NULL) {
231		delete job;
232		return;
233	}
234
235	// enforce job config properties
236	settings->AddInt32("copies", 1);
237	settings->AddInt32("first_page", 1);
238	settings->AddInt32("last_page", -1);
239
240	BWindow* win = new TestPageWindow(job, printer);
241	win->Show();
242	win->PostMessage(kMsgPrintTestPage);
243}
244
245
246void
247PrintersWindow::AddJob(SpoolFolder* folder, Job* job)
248{
249	if (_IsSelected(folder->Item()))
250		fJobListView->AddJob(job);
251	fPrinterListView->UpdateItem(folder->Item());
252	_UpdatePrinterButtons();
253}
254
255
256void
257PrintersWindow::RemoveJob(SpoolFolder* folder, Job* job)
258{
259	if (_IsSelected(folder->Item()))
260		fJobListView->RemoveJob(job);
261	fPrinterListView->UpdateItem(folder->Item());
262	_UpdatePrinterButtons();
263}
264
265
266void
267PrintersWindow::UpdateJob(SpoolFolder* folder, Job* job)
268{
269	if (_IsSelected(folder->Item())) {
270		fJobListView->UpdateJob(job);
271		_UpdateJobButtons();
272	}
273	fPrinterListView->UpdateItem(folder->Item());
274	_UpdatePrinterButtons();
275}
276
277
278// #pragma mark -
279
280
281void
282PrintersWindow::_BuildGUI()
283{
284	const float boxInset = 10.0;
285	BRect r(Bounds());
286
287// ------------------------ First of all, create a nice grey backdrop
288	BBox * backdrop = new BBox(Bounds(), "backdrop", B_FOLLOW_ALL_SIDES,
289						B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP,
290						B_PLAIN_BORDER);
291	AddChild(backdrop);
292
293// ------------------------ Next, build the printers overview box
294	BBox* printersBox = new BBox(BRect(boxInset, boxInset,
295		r.Width() - boxInset, (r.Height()/2) - (boxInset/2)),
296		"printersBox", B_FOLLOW_ALL);
297	printersBox->SetFont(be_bold_font);
298	printersBox->SetLabel(B_TRANSLATE("Printers"));
299	backdrop->AddChild(printersBox);
300
301		// Width of largest button
302	float maxWidth = 0;
303
304		// Add Button
305	BButton* addButton = new BButton(BRect(5,5,5,5), "add",
306		B_TRANSLATE("Add …"), new BMessage(kMsgAddPrinter), B_FOLLOW_RIGHT);
307	printersBox->AddChild(addButton);
308	addButton->ResizeToPreferred();
309
310	maxWidth = addButton->Bounds().Width();
311
312		// Remove button
313	fRemove = new BButton(BRect(5,30,5,30), "remove",
314		B_TRANSLATE("Remove"), new BMessage(kMsgRemovePrinter),
315		B_FOLLOW_RIGHT);
316	printersBox->AddChild(fRemove);
317	fRemove->ResizeToPreferred();
318
319	if (fRemove->Bounds().Width() > maxWidth)
320		maxWidth = fRemove->Bounds().Width();
321
322		// Make Default button
323	fMakeDefault = new BButton(BRect(5,60,5,60), "default",
324		B_TRANSLATE("Make default"), new BMessage(kMsgMakeDefaultPrinter),
325		B_FOLLOW_RIGHT);
326	printersBox->AddChild(fMakeDefault);
327	fMakeDefault->ResizeToPreferred();
328
329	if (fMakeDefault->Bounds().Width() > maxWidth)
330		maxWidth = fMakeDefault->Bounds().Width();
331
332		// Print Test Page button
333	fPrintTestPage = new BButton(BRect(5,60,5,60), "print_test_page",
334		B_TRANSLATE("Print test page"), new BMessage(kMsgPrintTestPage),
335		B_FOLLOW_RIGHT);
336	printersBox->AddChild(fPrintTestPage);
337	fPrintTestPage->ResizeToPreferred();
338
339	if (fPrintTestPage->Bounds().Width() > maxWidth)
340		maxWidth = fPrintTestPage->Bounds().Width();
341
342
343		// Resize all buttons to maximum width and align them to the right
344	float xPos = printersBox->Bounds().Width() - boxInset - maxWidth;
345	addButton->MoveTo(xPos, boxInset + 8);
346	addButton->ResizeTo(maxWidth, addButton->Bounds().Height());
347
348	fRemove->MoveTo(xPos,
349		boxInset + addButton->Bounds().Height() + boxInset + 8);
350	fRemove->ResizeTo(maxWidth, fRemove->Bounds().Height());
351
352	fMakeDefault->MoveTo(xPos, boxInset + addButton->Bounds().Height() +
353		boxInset + fRemove->Bounds().Height() + boxInset + 8);
354	fMakeDefault->ResizeTo(maxWidth, fMakeDefault->Bounds().Height());
355
356	fPrintTestPage->MoveTo(xPos, boxInset + addButton->Bounds().Height() +
357		boxInset + fRemove->Bounds().Height() +
358		boxInset + fMakeDefault->Bounds().Height() + boxInset + 8);
359	fPrintTestPage->ResizeTo(maxWidth, fPrintTestPage->Bounds().Height());
360
361
362		// Disable all selection-based buttons
363	fRemove->SetEnabled(false);
364	fMakeDefault->SetEnabled(false);
365	fPrintTestPage->SetEnabled(false);
366
367		// Create listview with scroller
368	BRect listBounds(boxInset, boxInset + 8,
369		fMakeDefault->Frame().left - boxInset - B_V_SCROLL_BAR_WIDTH,
370		printersBox->Bounds().Height()- boxInset - 3);
371	fPrinterListView = new PrinterListView(listBounds);
372	BScrollView* pscroller = new BScrollView("printer_scroller", fPrinterListView,
373		B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS, false, true, B_FANCY_BORDER);
374	printersBox->AddChild(pscroller);
375
376// ------------------------ Lastly, build the jobs overview box
377	fJobsBox = new BBox(BRect(boxInset, r.Height() / 2 + boxInset / 2,
378		Bounds().Width() - 10, Bounds().Height() - boxInset), "jobsBox",
379		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
380	fJobsBox->SetFont(be_bold_font);
381	fJobsBox->SetLabel(B_TRANSLATE("Print jobs: No printer selected"));
382	backdrop->AddChild(fJobsBox);
383
384		// Cancel Job Button
385	BButton* cancelButton = new BButton(BRect(5, 5, 5, 5), "cancel",
386		B_TRANSLATE("Cancel job"), new BMessage(kMsgCancelJob),
387		B_FOLLOW_RIGHT+B_FOLLOW_TOP);
388	fJobsBox->AddChild(cancelButton);
389	cancelButton->ResizeToPreferred();
390	fCancel = cancelButton;
391
392	maxWidth = cancelButton->Bounds().Width();
393
394		// Restart Job button
395	BButton* restartButton = new BButton(BRect(5, 30, 5, 30), "restart",
396		B_TRANSLATE("Restart job"), new BMessage(kMsgRestartJob),
397		B_FOLLOW_RIGHT | B_FOLLOW_TOP);
398	fJobsBox->AddChild(restartButton);
399	restartButton->ResizeToPreferred();
400	fRestart = restartButton;
401
402	if (restartButton->Bounds().Width() > maxWidth)
403		maxWidth = restartButton->Bounds().Width();
404
405		// Resize all buttons to maximum width and align them to the right
406	xPos = fJobsBox->Bounds().Width() - boxInset - maxWidth;
407	cancelButton->MoveTo(xPos, boxInset + 8);
408	cancelButton->ResizeTo(maxWidth, cancelButton->Bounds().Height());
409
410	restartButton->MoveTo(xPos, boxInset + cancelButton->Bounds().Height()
411		+ boxInset + 8);
412	restartButton->ResizeTo(maxWidth, restartButton->Bounds().Height());
413
414		// Disable all selection-based buttons
415	cancelButton->SetEnabled(false);
416	restartButton->SetEnabled(false);
417
418		// Create listview with scroller
419	listBounds = BRect(boxInset, boxInset + 8,
420		cancelButton->Frame().left - boxInset - B_V_SCROLL_BAR_WIDTH,
421		fJobsBox->Bounds().Height() - boxInset - 3);
422	fJobListView = new JobListView(listBounds);
423	BScrollView* jscroller = new BScrollView("jobs_scroller", fJobListView,
424		B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS, false, true, B_FANCY_BORDER);
425	fJobsBox->AddChild(jscroller);
426
427		// Determine min width
428	float width = (jscroller->Bounds().Width() < pscroller->Bounds().Width())
429		? jscroller->Bounds().Width() : pscroller->Bounds().Width();
430
431		// Resize boxes to the same size
432	jscroller->ResizeTo(width, jscroller->Bounds().Height());
433	pscroller->ResizeTo(width, pscroller->Bounds().Height());
434}
435
436
437bool
438PrintersWindow::_IsSelected(PrinterItem* printer)
439{
440	return fSelectedPrinter && fSelectedPrinter == printer;
441}
442
443
444void
445PrintersWindow::_UpdatePrinterButtons()
446{
447	PrinterItem* item = fPrinterListView->SelectedItem();
448	fRemove->SetEnabled(item && !item->HasPendingJobs());
449	fMakeDefault->SetEnabled(item && !item->IsActivePrinter());
450	fPrintTestPage->SetEnabled(item);
451}
452
453
454void
455PrintersWindow::_UpdateJobButtons()
456{
457	JobItem* item = fJobListView->SelectedItem();
458	if (item != NULL) {
459		Job* job = item->GetJob();
460		fCancel->SetEnabled(job->Status() != kProcessing);
461		fRestart->SetEnabled(job->Status() == kFailed);
462	} else {
463		fCancel->SetEnabled(false);
464		fRestart->SetEnabled(false);
465	}
466}
467
468
469