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