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