xref: /haiku/src/servers/print/ConfigWindow.cpp (revision 4bd0c1066b227cec4b79883bdef697c7a27f2e90)
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 
39 static const float a0_width = 2380.0;
40 static const float a0_height = 3368.0;
41 static const float a1_width = 1684.0;
42 static const float a1_height = 2380.0;
43 static const float a2_width = 1190.0;
44 static const float a2_height = 1684.0;
45 static const float a3_width = 842.0;
46 static const float a3_height = 1190.0;
47 static const float a4_width = 595.0;
48 static const float a4_height = 842.0;
49 static const float a5_width = 421.0;
50 static const float a5_height = 595.0;
51 static const float a6_width = 297.0;
52 static const float a6_height = 421.0;
53 static const float b5_width = 501.0;
54 static const float b5_height = 709.0;
55 static const float letter_width = 612.0;
56 static const float letter_height = 792.0;
57 static const float legal_width  = 612.0;
58 static const float legal_height  = 1008.0;
59 static const float ledger_width = 1224.0;
60 static const float ledger_height = 792.0;
61 static const float tabloid_width = 792.0;
62 static const float tabloid_height = 1224.0;
63 static const float jis_b5_width = 516.0;
64 static const float jis_b5_height = 729.0;
65 
66 
67 static 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 
103 static void
104 GetPageFormat(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 
121 static BGroupLayoutBuilder
122 LeftAlign(BView* view)
123 {
124 	return BGroupLayoutBuilder(B_HORIZONTAL)
125 		.Add(view)
126 		.AddGlue()
127 		.SetInsets(0, 0, 0, 0);
128 }
129 
130 
131 ConfigWindow::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 
244 ConfigWindow::~ConfigWindow()
245 {
246 	if (fCurrentPrinter)
247 		fCurrentPrinter->Release();
248 	release_sem(fFinished);
249 }
250 
251 
252 void
253 ConfigWindow::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 
267 void
268 ConfigWindow::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 
303 void
304 ConfigWindow::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 
320 void
321 ConfigWindow::FrameMoved(BPoint p)
322 {
323 	BRect frame = GetWindowFrame();
324 	frame.OffsetTo(p);
325 	SetWindowFrame(frame);
326 }
327 
328 
329 BRect
330 ConfigWindow::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 
342 void
343 ConfigWindow::SetWindowFrame(BRect r)
344 {
345 	BAutolock lock(gLock);
346 	if (lock.IsLocked())
347 		Settings::GetSettings()->SetConfigWindowFrame(r);
348 }
349 
350 
351 BButton*
352 ConfigWindow::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 
383 void
384 ConfigWindow::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 
406 void
407 ConfigWindow::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 
431 void
432 ConfigWindow::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 
446 void
447 ConfigWindow::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 
470 void
471 ConfigWindow::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 
552 void
553 ConfigWindow::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