1/*
2 * Copyright 2002-2011, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Michael Pfeiffer
7 */
8
9
10#include "ConfigWindow.h"
11
12#include <limits.h>
13#include <math.h>
14#include <stdlib.h>
15#include <string.h>
16
17#include <Application.h>
18#include <Autolock.h>
19#include <Catalog.h>
20#include <Debug.h>
21#include <GroupLayout.h>
22#include <GroupLayoutBuilder.h>
23#include <IconUtils.h>
24#include <Layout.h>
25#include <Locale.h>
26#include <Resources.h>
27#include <Window.h>
28
29#include "pr_server.h"
30#include "Printer.h"
31#include "PrintServerApp.h"
32#include "PrintUtils.h"
33
34
35#undef B_TRANSLATION_CONTEXT
36#define B_TRANSLATION_CONTEXT "ConfigWindow"
37
38
39static const float a0_width = 2380.0;
40static const float a0_height = 3368.0;
41static const float a1_width = 1684.0;
42static const float a1_height = 2380.0;
43static const float a2_width = 1190.0;
44static const float a2_height = 1684.0;
45static const float a3_width = 842.0;
46static const float a3_height = 1190.0;
47static const float a4_width = 595.0;
48static const float a4_height = 842.0;
49static const float a5_width = 421.0;
50static const float a5_height = 595.0;
51static const float a6_width = 297.0;
52static const float a6_height = 421.0;
53static const float b5_width = 501.0;
54static const float b5_height = 709.0;
55static const float letter_width = 612.0;
56static const float letter_height = 792.0;
57static const float legal_width  = 612.0;
58static const float legal_height  = 1008.0;
59static const float ledger_width = 1224.0;
60static const float ledger_height = 792.0;
61static const float tabloid_width = 792.0;
62static const float tabloid_height = 1224.0;
63static const float jis_b5_width = 516.0;
64static const float jis_b5_height = 729.0;
65
66
67static struct PageFormat
68{
69	const char  *label;
70	float width;
71	float height;
72} pageFormat[] =
73{
74	{B_TRANSLATE_MARK_COMMENT("Letter", "ANSI A (letter), a North American "
75		"paper size"), letter_width, letter_height },
76	{B_TRANSLATE_MARK_COMMENT("Legal", "A North American paper size (216 x 356"
77		" mm, or 8.5 x 14 in)"), legal_width,  legal_height },
78	{B_TRANSLATE_MARK_COMMENT("Ledger", "ANSI B (ledger), a North American "
79		"paper size"), ledger_width, ledger_height },
80	{B_TRANSLATE_MARK_COMMENT("Tabloid", "ANSI B (tabloid), a North American "
81		"paper size"), tabloid_width, tabloid_height },
82	{B_TRANSLATE_MARK_COMMENT("A0", "ISO 216 paper size"),
83		a0_width, a0_height },
84	{B_TRANSLATE_MARK_COMMENT("A1", "ISO 216 paper size"),
85		a1_width, a1_height },
86	{B_TRANSLATE_MARK_COMMENT("A2", "ISO 216 paper size"),
87		a2_width, a2_height },
88	{B_TRANSLATE_MARK_COMMENT("A3", "ISO 216 paper size"),
89		a3_width, a3_height },
90	{B_TRANSLATE_MARK_COMMENT("A4", "ISO 216 paper size"),
91		a4_width, a4_height },
92	{B_TRANSLATE_MARK_COMMENT("A5", "ISO 216 paper size"),
93		a5_width, a5_height },
94	{B_TRANSLATE_MARK_COMMENT("A6", "ISO 216 paper size"),
95		a6_width, a6_height },
96	{B_TRANSLATE_MARK_COMMENT("B5", "ISO 216 paper size"),
97		b5_width, b5_height },
98	{B_TRANSLATE_MARK_COMMENT("B5 (JIS)", "JIS P0138 B5, a Japanese "
99		"paper size"), jis_b5_width, jis_b5_height },
100};
101
102
103static void
104GetPageFormat(float w, float h, BString& label)
105{
106	w = floor(w + 0.5); h = floor(h + 0.5);
107	for (uint i = 0; i < sizeof(pageFormat) / sizeof(struct PageFormat); i ++) {
108		struct PageFormat& pf = pageFormat[i];
109		if ((pf.width == w && pf.height == h) || (pf.width == h
110			&& pf.height == w)) {
111			label = B_TRANSLATE_NOCOLLECT(pf.label);
112			return;
113		}
114	}
115
116	float unit = 72.0; // currently inches only
117	label << (w / unit) << "x" << (h / unit) << " in.";
118}
119
120
121static BGroupLayoutBuilder
122LeftAlign(BView* view)
123{
124	return BGroupLayoutBuilder(B_HORIZONTAL)
125		.Add(view)
126		.AddGlue()
127		.SetInsets(0, 0, 0, 0);
128}
129
130
131ConfigWindow::ConfigWindow(config_setup_kind kind, Printer* defaultPrinter,
132	BMessage* settings, AutoReply* sender)
133	:
134	BWindow(ConfigWindow::GetWindowFrame(),
135		B_TRANSLATE("Page setup"),
136		B_TITLED_WINDOW,
137		B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS
138		| B_CLOSE_ON_ESCAPE),
139	fKind(kind),
140	fDefaultPrinter(defaultPrinter),
141	fSettings(settings),
142	fSender(sender),
143	fCurrentPrinter(NULL),
144	fPageFormatText(NULL),
145	fJobSetupText(NULL)
146{
147	MimeTypeForSender(settings, fSenderMimeType);
148	PrinterForMimeType();
149
150	if (kind == kJobSetup)
151		SetTitle(B_TRANSLATE("Print setup"));
152
153	BView* panel = new BBox(Bounds(), "temporary", B_FOLLOW_ALL, B_WILL_DRAW);
154	AddChild(panel);
155
156	// print selection pop up menu
157	BPopUpMenu* menu = new BPopUpMenu(B_TRANSLATE("Select a printer"));
158	SetupPrintersMenu(menu);
159
160	fPrinters = new BMenuField(B_TRANSLATE("Printer:"), menu);
161
162	// page format button
163	fPageSetup = AddPictureButton(panel, "Paper setup",
164		"PAGE_SETUP", MSG_PAGE_SETUP);
165
166	// add description to button
167	BStringView *pageFormatTitle = new BStringView("paperSetupTitle",
168		B_TRANSLATE("Paper setup:"));
169	fPageFormatText = new BStringView("pageSetupText", "");
170
171	// page selection button
172	fJobSetup = NULL;
173	BStringView* jobSetupTitle = NULL;
174	if (kind == kJobSetup) {
175		fJobSetup = AddPictureButton(panel, "Job setup",
176			"JOB_SETUP", MSG_JOB_SETUP);
177		// add description to button
178		jobSetupTitle = new BStringView("jobSetupTitle",
179			B_TRANSLATE("Print job setup:"));
180		fJobSetupText = new BStringView("jobSetupText", "");
181	}
182
183	// separator line
184	BBox* separator = new BBox("separator");
185	separator->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1));
186
187	// Cancel & OK button
188	BButton* cancel = new BButton("Cancel", B_TRANSLATE("Cancel"),
189		new BMessage(B_QUIT_REQUESTED));
190	fOk = new BButton("OK", B_TRANSLATE("OK"), new BMessage(MSG_OK));
191
192	RemoveChild(panel);
193
194	SetLayout(new BGroupLayout(B_VERTICAL));
195	BGroupLayoutBuilder builder(B_VERTICAL);
196
197	builder
198		.Add(fPrinters)
199		.Add(BGroupLayoutBuilder(B_HORIZONTAL, 10)
200				.Add(fPageSetup)
201				.Add(BGroupLayoutBuilder(B_VERTICAL, 0)
202						.Add(LeftAlign(pageFormatTitle))
203						.Add(LeftAlign(fPageFormatText))
204						.SetInsets(0, 0, 0, 0)
205				)
206				.AddGlue()
207		);
208
209	if (fJobSetup != NULL) {
210		builder
211			.Add(BGroupLayoutBuilder(B_HORIZONTAL, 10)
212					.Add(fJobSetup)
213					.Add(BGroupLayoutBuilder(B_VERTICAL, 0)
214							.Add(LeftAlign(jobSetupTitle))
215							.Add(LeftAlign(fJobSetupText))
216							.SetInsets(0, 0, 0, 0)
217					)
218					.AddGlue()
219			);
220	}
221
222	builder
223		.AddGlue()
224		.Add(separator)
225		.Add(BGroupLayoutBuilder(B_HORIZONTAL)
226			.AddGlue()
227			.Add(cancel)
228			.Add(fOk)
229		)
230		.SetInsets(5, 5, 5, 5);
231
232	AddChild(builder);
233
234	AddShortcut('a', 0, new BMessage(B_ABOUT_REQUESTED));
235
236	SetDefaultButton(fOk);
237
238	fPrinters->MakeFocus(true);
239
240	UpdateSettings(true);
241}
242
243
244ConfigWindow::~ConfigWindow()
245{
246	if (fCurrentPrinter)
247		fCurrentPrinter->Release();
248	release_sem(fFinished);
249}
250
251
252void
253ConfigWindow::Go()
254{
255	sem_id sid = create_sem(0, "finished");
256	if (sid >= 0) {
257		fFinished = sid;
258		Show();
259		acquire_sem(sid);
260		delete_sem(sid);
261	} else {
262		Quit();
263	}
264}
265
266
267void
268ConfigWindow::MessageReceived(BMessage* m)
269{
270	switch (m->what) {
271		case MSG_PAGE_SETUP:
272			Setup(kPageSetup);
273			break;
274		case MSG_JOB_SETUP:
275			Setup(kJobSetup);
276			break;
277		case MSG_PRINTER_SELECTED:
278		{
279			BString printer;
280			if (m->FindString("name", &printer) == B_OK) {
281				UpdateAppSettings(fSenderMimeType.String(), printer.String());
282				PrinterForMimeType();
283				UpdateSettings(true);
284			}
285			break;
286		}
287		case MSG_OK:
288			UpdateSettings(false);
289			if (fKind == kPageSetup)
290				fSender->SetReply(&fPageSettings);
291			else
292				fSender->SetReply(&fJobSettings);
293			Quit();
294			break;
295		case B_ABOUT_REQUESTED: AboutRequested();
296			break;
297		default:
298			BWindow::MessageReceived(m);
299	}
300}
301
302
303void
304ConfigWindow::AboutRequested()
305{
306	BString text = B_TRANSLATE("Printer server");
307	text <<	"\n"
308		"�� 2001-2010 Haiku, Inc.\n"
309		"\n"
310		"\tIthamar R. Adema\n"
311		"\tMichael Pfeiffer\n";
312
313	BAlert *about = new BAlert("About printer server", text.String(),
314		B_TRANSLATE("OK"));
315	about->SetFlags(about->Flags() | B_CLOSE_ON_ESCAPE);
316	about->Go();
317}
318
319
320void
321ConfigWindow::FrameMoved(BPoint p)
322{
323	BRect frame = GetWindowFrame();
324	frame.OffsetTo(p);
325	SetWindowFrame(frame);
326}
327
328
329BRect
330ConfigWindow::GetWindowFrame()
331{
332	BRect frame(0, 0, 10, 10);
333	BAutolock lock(gLock);
334	if (lock.IsLocked())
335		frame.OffsetBy(Settings::GetSettings()->ConfigWindowFrame().LeftTop());
336
337	frame.OffsetBy(30, 30);
338	return frame;
339}
340
341
342void
343ConfigWindow::SetWindowFrame(BRect r)
344{
345	BAutolock lock(gLock);
346	if (lock.IsLocked())
347		Settings::GetSettings()->SetConfigWindowFrame(r);
348}
349
350
351BButton*
352ConfigWindow::AddPictureButton(BView* panel, const char* name,
353	const char* picture, uint32 what)
354{
355	BResources *res = BApplication::AppResources();
356	if (res == NULL)
357		return NULL;
358
359	size_t length;
360	const void *bits = res->LoadResource('VICN', picture, &length);
361	BButton* button = NULL;
362
363	BBitmap* onBM = new BBitmap(BRect(0, 0, 24, 24), B_RGBA32);
364
365	if (onBM != NULL) {
366		if (BIconUtils::GetVectorIcon((uint8*)bits, length, onBM) != B_OK) {
367			delete onBM;
368			return NULL;
369		}
370
371		button = new BButton(name, new BMessage(what));
372		button->SetIcon(onBM, B_TRIM_ICON_BITMAP_KEEP_ASPECT);
373		button->SetViewColor(B_TRANSPARENT_COLOR);
374		button->SetLabel(NULL);
375	}
376
377	delete onBM;
378
379	return button;
380}
381
382
383void
384ConfigWindow::PrinterForMimeType()
385{
386	BAutolock lock(gLock);
387	if (fCurrentPrinter) {
388		fCurrentPrinter->Release();
389		fCurrentPrinter = NULL;
390	}
391
392	if (lock.IsLocked()) {
393		Settings* s = Settings::GetSettings();
394		AppSettings* app = s->FindAppSettings(fSenderMimeType.String());
395		if (app)
396			fPrinterName = app->GetPrinter();
397		else
398			fPrinterName = fDefaultPrinter ? fDefaultPrinter->Name() : "";
399		fCurrentPrinter = Printer::Find(fPrinterName);
400		if (fCurrentPrinter)
401			fCurrentPrinter->Acquire();
402	}
403}
404
405
406void
407ConfigWindow::SetupPrintersMenu(BMenu* menu)
408{
409	// clear menu
410	while (menu->CountItems() != 0)
411		delete menu->RemoveItem((int32)0);
412
413	// fill menu with printer names
414	BAutolock lock(gLock);
415	if (lock.IsLocked()) {
416		BString n;
417		BMessage* m;
418		BMenuItem* item;
419		for (int i = 0; i < Printer::CountPrinters(); i ++) {
420			Printer::At(i)->GetName(n);
421			m = new BMessage(MSG_PRINTER_SELECTED);
422			m->AddString("name", n.String());
423			menu->AddItem(item = new BMenuItem(n.String(), m));
424			if (n == fPrinterName)
425				item->SetMarked(true);
426		}
427	}
428}
429
430
431void
432ConfigWindow::UpdateAppSettings(const char* mime, const char* printer)
433{
434	BAutolock lock(gLock);
435	if (lock.IsLocked()) {
436		Settings* s = Settings::GetSettings();
437		AppSettings* app = s->FindAppSettings(mime);
438		if (app)
439			app->SetPrinter(printer);
440		else
441			s->AddAppSettings(new AppSettings(mime, printer));
442	}
443}
444
445
446void
447ConfigWindow::UpdateSettings(bool read)
448{
449	BAutolock lock(gLock);
450	if (lock.IsLocked()) {
451		Settings* s = Settings::GetSettings();
452		PrinterSettings* p = s->FindPrinterSettings(fPrinterName.String());
453		if (p == NULL) {
454			p = new PrinterSettings(fPrinterName.String());
455			s->AddPrinterSettings(p);
456		}
457		ASSERT(p != NULL);
458		if (read) {
459			fPageSettings = *p->GetPageSettings();
460			fJobSettings = *p->GetJobSettings();
461		} else {
462			p->SetPageSettings(&fPageSettings);
463			p->SetJobSettings(&fJobSettings);
464		}
465	}
466	UpdateUI();
467}
468
469
470void
471ConfigWindow::UpdateUI()
472{
473	if (fCurrentPrinter == NULL) {
474		fPageSetup->SetEnabled(false);
475		if (fJobSetup) {
476			fJobSetup->SetEnabled(false);
477			fJobSetupText->SetText(B_TRANSLATE("Undefined"));
478		}
479		fOk->SetEnabled(false);
480		fPageFormatText->SetText(B_TRANSLATE("Undefined"));
481	} else {
482		fPageSetup->SetEnabled(true);
483
484		if (fJobSetup)
485			fJobSetup->SetEnabled(fKind == kJobSetup
486				&& !fPageSettings.IsEmpty());
487
488		fOk->SetEnabled((fKind == kJobSetup && !fJobSettings.IsEmpty())
489			|| (fKind == kPageSetup && !fPageSettings.IsEmpty()));
490
491		// display information about page format
492		BRect paperRect;
493		BString pageFormat;
494		if (fPageSettings.FindRect(PSRV_FIELD_PAPER_RECT, &paperRect) == B_OK) {
495			GetPageFormat(paperRect.Width(), paperRect.Height(), pageFormat);
496
497			int32 orientation = 0;
498			fPageSettings.FindInt32(PSRV_FIELD_ORIENTATION, &orientation);
499			if (orientation == 0)
500				pageFormat << ", " << B_TRANSLATE("Portrait");
501			else
502				pageFormat << ", " << B_TRANSLATE("Landscape");
503		} else
504			pageFormat << B_TRANSLATE("Undefined");
505
506		fPageFormatText->SetText(pageFormat.String());
507
508		// display information about job
509		if (fKind == kJobSetup) {
510			BString job;
511			int32 first, last, copies;
512			if (fJobSettings.FindInt32(PSRV_FIELD_FIRST_PAGE, &first) == B_OK
513				&& fJobSettings.FindInt32(PSRV_FIELD_LAST_PAGE, &last) ==
514				B_OK) {
515
516				bool printRange = first >= 1 && first <= last && last != INT_MAX;
517				char number[12];
518				if (fJobSettings.FindInt32(PSRV_FIELD_COPIES, &copies)
519					== B_OK && copies > 1) {
520					if (printRange) {
521						job = B_TRANSLATE("Page %1 to %2, %3 copies");
522						snprintf(number, sizeof(number), "%d", (int)first);
523						job.ReplaceFirst("%1", number);
524						snprintf(number, sizeof(number), "%d", (int)last);
525						job.ReplaceFirst("%2", number);
526						snprintf(number, sizeof(number), "%d", (int)copies);
527						job.ReplaceFirst("%3", number);
528					} else {
529						job = B_TRANSLATE("All pages, %1 copies");
530						snprintf(number, sizeof(number), "%d", (int)copies);
531						job.ReplaceFirst("%1", number);
532					}
533				} else {
534					if (printRange) {
535						job = B_TRANSLATE("Page %1 to %2");
536						snprintf(number, sizeof(number), "%d", (int)first);
537						job.ReplaceFirst("%1", number);
538						snprintf(number, sizeof(number), "%d", (int)last);
539						job.ReplaceFirst("%2", number);
540					} else
541						job = B_TRANSLATE("All pages");
542				}
543			} else
544				job << B_TRANSLATE("Undefined");
545
546			fJobSetupText->SetText(job.String());
547		}
548	}
549}
550
551
552void
553ConfigWindow::Setup(config_setup_kind kind)
554{
555	if (fCurrentPrinter) {
556		Hide();
557		if (kind == kPageSetup) {
558			BMessage settings = fPageSettings;
559			if (fCurrentPrinter->ConfigurePage(settings) == B_OK) {
560				fPageSettings = settings;
561				if (!fJobSettings.IsEmpty())
562					AddFields(&fJobSettings, &fPageSettings);
563			}
564		} else {
565			BMessage settings;
566			if (fJobSettings.IsEmpty()) settings = fPageSettings;
567			else settings = fJobSettings;
568
569			if (fCurrentPrinter->ConfigureJob(settings) == B_OK)
570				fJobSettings = settings;
571		}
572		UpdateUI();
573		Show();
574	}
575}
576