xref: /haiku/src/servers/print/ConfigWindow.cpp (revision 3dfd9cb95ce45f59160d50975210bc55e3fc0709)
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_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 		| 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->Go();
313 }
314 
315 
316 void
317 ConfigWindow::FrameMoved(BPoint p)
318 {
319 	BRect frame = GetWindowFrame();
320 	frame.OffsetTo(p);
321 	SetWindowFrame(frame);
322 }
323 
324 
325 BRect
326 ConfigWindow::GetWindowFrame()
327 {
328 	BRect frame(0, 0, 10, 10);
329 	BAutolock lock(gLock);
330 	if (lock.IsLocked())
331 		frame.OffsetBy(Settings::GetSettings()->ConfigWindowFrame().LeftTop());
332 
333 	frame.OffsetBy(30, 30);
334 	return frame;
335 }
336 
337 
338 void
339 ConfigWindow::SetWindowFrame(BRect r)
340 {
341 	BAutolock lock(gLock);
342 	if (lock.IsLocked())
343 		Settings::GetSettings()->SetConfigWindowFrame(r);
344 }
345 
346 
347 BPictureButton*
348 ConfigWindow::AddPictureButton(BView* panel, BRect frame,
349 	const char* name, const char* on, const char* off, uint32 what)
350 {
351 	BBitmap* onBM = LoadBitmap(on);
352 	BBitmap* offBM = LoadBitmap(off);
353 
354 	BPicture* onPict = BitmapToPicture(panel, onBM);
355 	BPicture* offPict = BitmapToPicture(panel, offBM);
356 
357 	BPictureButton* button = NULL;
358 
359 	if (onPict != NULL && offPict != NULL) {
360 		button = new BPictureButton(frame, name, onPict, offPict,
361 			new BMessage(what));
362 		button->SetViewColor(B_TRANSPARENT_COLOR);
363 		panel->AddChild(button);
364 		onBM->Lock();
365 		int32 width = (int32)onBM->Bounds().Width();
366 		int32 height = (int32)onBM->Bounds().Height();
367 		button->ResizeTo(width, height);
368 		button->SetExplicitMaxSize(BSize(width, height));
369 		onBM->Unlock();
370 		panel->RemoveChild(button);
371 
372 		BPicture* disabled = BitmapToGrayedPicture(panel, offBM);
373 		button->SetDisabledOn(disabled);
374 		delete disabled;
375 
376 		disabled = BitmapToGrayedPicture(panel, onBM);
377 		button->SetDisabledOff(disabled);
378 		delete disabled;
379 	}
380 
381 	delete onPict; delete offPict;
382 	delete onBM; delete offBM;
383 
384 	return button;
385 }
386 
387 
388 void
389 ConfigWindow::PrinterForMimeType()
390 {
391 	BAutolock lock(gLock);
392 	if (fCurrentPrinter) {
393 		fCurrentPrinter->Release();
394 		fCurrentPrinter = NULL;
395 	}
396 
397 	if (lock.IsLocked()) {
398 		Settings* s = Settings::GetSettings();
399 		AppSettings* app = s->FindAppSettings(fSenderMimeType.String());
400 		if (app)
401 			fPrinterName = app->GetPrinter();
402 		else
403 			fPrinterName = fDefaultPrinter ? fDefaultPrinter->Name() : "";
404 		fCurrentPrinter = Printer::Find(fPrinterName);
405 		if (fCurrentPrinter)
406 			fCurrentPrinter->Acquire();
407 	}
408 }
409 
410 
411 void
412 ConfigWindow::SetupPrintersMenu(BMenu* menu)
413 {
414 	// clear menu
415 	while (menu->CountItems() != 0)
416 		delete menu->RemoveItem(0L);
417 
418 	// fill menu with printer names
419 	BAutolock lock(gLock);
420 	if (lock.IsLocked()) {
421 		BString n;
422 		BMessage* m;
423 		BMenuItem* item;
424 		for (int i = 0; i < Printer::CountPrinters(); i ++) {
425 			Printer::At(i)->GetName(n);
426 			m = new BMessage(MSG_PRINTER_SELECTED);
427 			m->AddString("name", n.String());
428 			menu->AddItem(item = new BMenuItem(n.String(), m));
429 			if (n == fPrinterName)
430 				item->SetMarked(true);
431 		}
432 	}
433 }
434 
435 
436 void
437 ConfigWindow::UpdateAppSettings(const char* mime, const char* printer)
438 {
439 	BAutolock lock(gLock);
440 	if (lock.IsLocked()) {
441 		Settings* s = Settings::GetSettings();
442 		AppSettings* app = s->FindAppSettings(mime);
443 		if (app)
444 			app->SetPrinter(printer);
445 		else
446 			s->AddAppSettings(new AppSettings(mime, printer));
447 	}
448 }
449 
450 
451 void
452 ConfigWindow::UpdateSettings(bool read)
453 {
454 	BAutolock lock(gLock);
455 	if (lock.IsLocked()) {
456 		Settings* s = Settings::GetSettings();
457 		PrinterSettings* p = s->FindPrinterSettings(fPrinterName.String());
458 		if (p == NULL) {
459 			p = new PrinterSettings(fPrinterName.String());
460 			s->AddPrinterSettings(p);
461 		}
462 		ASSERT(p != NULL);
463 		if (read) {
464 			fPageSettings = *p->GetPageSettings();
465 			fJobSettings = *p->GetJobSettings();
466 		} else {
467 			p->SetPageSettings(&fPageSettings);
468 			p->SetJobSettings(&fJobSettings);
469 		}
470 	}
471 	UpdateUI();
472 }
473 
474 
475 void
476 ConfigWindow::UpdateUI()
477 {
478 	if (fCurrentPrinter == NULL) {
479 		fPageSetup->SetEnabled(false);
480 		if (fJobSetup) {
481 			fJobSetup->SetEnabled(false);
482 			fJobSetupText->SetText(B_TRANSLATE("Undefined"));
483 		}
484 		fOk->SetEnabled(false);
485 		fPageFormatText->SetText(B_TRANSLATE("Undefined"));
486 	} else {
487 		fPageSetup->SetEnabled(true);
488 
489 		if (fJobSetup)
490 			fJobSetup->SetEnabled(fKind == kJobSetup
491 				&& !fPageSettings.IsEmpty());
492 
493 		fOk->SetEnabled((fKind == kJobSetup && !fJobSettings.IsEmpty())
494 			|| (fKind == kPageSetup && !fPageSettings.IsEmpty()));
495 
496 		// display information about page format
497 		BRect paperRect;
498 		BString pageFormat;
499 		if (fPageSettings.FindRect(PSRV_FIELD_PAPER_RECT, &paperRect) == B_OK) {
500 			GetPageFormat(paperRect.Width(), paperRect.Height(), pageFormat);
501 
502 			int32 orientation = 0;
503 			fPageSettings.FindInt32(PSRV_FIELD_ORIENTATION, &orientation);
504 			if (orientation == 0)
505 				pageFormat << ", " << B_TRANSLATE("Portrait");
506 			else
507 				pageFormat << ", " << B_TRANSLATE("Landscape");
508 		} else
509 			pageFormat << B_TRANSLATE("Undefined");
510 
511 		fPageFormatText->SetText(pageFormat.String());
512 
513 		// display information about job
514 		if (fKind == kJobSetup) {
515 			BString job;
516 			int32 first, last, copies;
517 			if (fJobSettings.FindInt32(PSRV_FIELD_FIRST_PAGE, &first) == B_OK
518 				&& fJobSettings.FindInt32(PSRV_FIELD_LAST_PAGE, &last) ==
519 				B_OK) {
520 
521 				bool printRange = first >= 1 && first <= last && last != INT_MAX;
522 				char number[12];
523 				if (fJobSettings.FindInt32(PSRV_FIELD_COPIES, &copies)
524 					== B_OK && copies > 1) {
525 					if (printRange) {
526 						job = B_TRANSLATE("Page %1 to %2, %3 copies");
527 						snprintf(number, sizeof(number), "%d", (int)first);
528 						job.ReplaceFirst("%1", number);
529 						snprintf(number, sizeof(number), "%d", (int)last);
530 						job.ReplaceFirst("%2", number);
531 						snprintf(number, sizeof(number), "%d", (int)copies);
532 						job.ReplaceFirst("%3", number);
533 					} else {
534 						job = B_TRANSLATE("All pages, %1 copies");
535 						snprintf(number, sizeof(number), "%d", (int)copies);
536 						job.ReplaceFirst("%1", number);
537 					}
538 				} else {
539 					if (printRange) {
540 						job = B_TRANSLATE("Page %1 to %2");
541 						snprintf(number, sizeof(number), "%d", (int)first);
542 						job.ReplaceFirst("%1", number);
543 						snprintf(number, sizeof(number), "%d", (int)last);
544 						job.ReplaceFirst("%2", number);
545 					} else
546 						job = B_TRANSLATE("All pages");
547 				}
548 			} else
549 				job << B_TRANSLATE("Undefined");
550 
551 			fJobSetupText->SetText(job.String());
552 		}
553 	}
554 }
555 
556 
557 void
558 ConfigWindow::Setup(config_setup_kind kind)
559 {
560 	if (fCurrentPrinter) {
561 		Hide();
562 		if (kind == kPageSetup) {
563 			BMessage settings = fPageSettings;
564 			if (fCurrentPrinter->ConfigurePage(settings) == B_OK) {
565 				fPageSettings = settings;
566 				if (!fJobSettings.IsEmpty())
567 					AddFields(&fJobSettings, &fPageSettings);
568 			}
569 		} else {
570 			BMessage settings;
571 			if (fJobSettings.IsEmpty()) settings = fPageSettings;
572 			else settings = fJobSettings;
573 
574 			if (fCurrentPrinter->ConfigureJob(settings) == B_OK)
575 				fJobSettings = settings;
576 		}
577 		UpdateUI();
578 		Show();
579 	}
580 }
581