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