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