xref: /haiku/src/servers/print/ConfigWindow.cpp (revision e7d5c75dce28921de0dc981ed840205a67a0c0e5)
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 }
122 
123 
124 ConfigWindow::ConfigWindow(config_setup_kind kind, Printer* defaultPrinter,
125 	BMessage* settings, AutoReply* sender)
126 	:
127 	BWindow(ConfigWindow::GetWindowFrame(), B_TRANSLATE("Page setup"),
128 		B_TITLED_WINDOW,
129 		B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS),
130 	fKind(kind),
131 	fDefaultPrinter(defaultPrinter),
132 	fSettings(settings),
133 	fSender(sender),
134 	fCurrentPrinter(NULL),
135 	fPageFormatText(NULL),
136 	fJobSetupText(NULL)
137 {
138 	MimeTypeForSender(settings, fSenderMimeType);
139 	PrinterForMimeType();
140 
141 	if (kind == kJobSetup)
142 		SetTitle(B_TRANSLATE("Print setup"));
143 
144 	BView* panel = new BBox(Bounds(), "temporary", B_FOLLOW_ALL, B_WILL_DRAW);
145 	AddChild(panel);
146 
147 	BRect dummyRect(0, 0, 1, 1);
148 
149 	// print selection pop up menu
150 	BPopUpMenu* menu = new BPopUpMenu(B_TRANSLATE("Select a printer"));
151 	SetupPrintersMenu(menu);
152 
153 	fPrinters = new BMenuField(B_TRANSLATE("Printer:"), menu, NULL);
154 
155 	// page format button
156 	fPageSetup = AddPictureButton(panel, dummyRect, "Paper setup",
157 		"PAGE_SETUP_ON", "PAGE_SETUP_OFF", MSG_PAGE_SETUP);
158 
159 	// add description to button
160 	BStringView *pageFormatTitle = new BStringView("paperSetupTitle",
161 		B_TRANSLATE("Paper setup:"));
162 	fPageFormatText = new BStringView("paperSetupText", "");
163 
164 	// page selection button
165 	fJobSetup = NULL;
166 	BStringView* jobSetupTitle = NULL;
167 	if (kind == kJobSetup) {
168 		fJobSetup = AddPictureButton(panel, dummyRect, "Page setup",
169 			"JOB_SETUP_ON", "JOB_SETUP_OFF", MSG_JOB_SETUP);
170 		// add description to button
171 		jobSetupTitle = new BStringView("jobSetupTitle",
172 			B_TRANSLATE("Page setup:"));
173 		fJobSetupText = new BStringView("jobSetupText", "");
174 	}
175 
176 	// separator line
177 	BBox* separator = new BBox(dummyRect, "line",
178 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
179 	separator->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1));
180 
181 	// Cancel & OK button
182 	BButton* cancel = new BButton(dummyRect, "Cancel", B_TRANSLATE("Cancel"),
183 		new BMessage(B_QUIT_REQUESTED));
184 	fOk = new BButton(dummyRect, "OK", B_TRANSLATE("OK"),
185 		new BMessage(MSG_OK));
186 
187 	RemoveChild(panel);
188 
189 	SetLayout(new BGroupLayout(B_VERTICAL));
190 	BGroupLayoutBuilder builder(B_VERTICAL);
191 
192 	builder
193 		.Add(fPrinters)
194 		.AddStrut(5)
195 		.Add(BGroupLayoutBuilder(B_HORIZONTAL)
196 				.Add(fPageSetup)
197 				.AddStrut(5)
198 				.Add(BGroupLayoutBuilder(B_VERTICAL)
199 						.Add(LeftAlign(pageFormatTitle))
200 						.Add(LeftAlign(fPageFormatText))
201 				)
202 				.AddGlue()
203 		);
204 
205 	if (fJobSetup != NULL) {
206 		builder
207 			.AddStrut(5)
208 			.Add(BGroupLayoutBuilder(B_HORIZONTAL)
209 					.Add(fJobSetup)
210 					.AddStrut(5)
211 					.Add(BGroupLayoutBuilder(B_VERTICAL)
212 							.Add(LeftAlign(jobSetupTitle))
213 							.Add(LeftAlign(fJobSetupText))
214 					)
215 					.AddGlue()
216 			);
217 	}
218 
219 	builder
220 		.AddStrut(5)
221 		.Add(separator)
222 		.AddStrut(5)
223 		.Add(BGroupLayoutBuilder(B_HORIZONTAL)
224 				.AddGlue()
225 				.Add(cancel)
226 				.AddStrut(5)
227 				.Add(fOk)
228 				.AddGlue()
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->Go();
316 }
317 
318 
319 void
320 ConfigWindow::FrameMoved(BPoint p)
321 {
322 	BRect frame = GetWindowFrame();
323 	frame.OffsetTo(p);
324 	SetWindowFrame(frame);
325 }
326 
327 
328 BRect
329 ConfigWindow::GetWindowFrame()
330 {
331 	BAutolock lock(gLock);
332 	if (lock.IsLocked())
333 		return Settings::GetSettings()->ConfigWindowFrame();
334 
335 	return BRect(30, 30, 300, 300);
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 						sprintf(number, "%d", (int)first);
529 						job.ReplaceFirst("%1", number);
530 						sprintf(number, "%d", (int)last);
531 						job.ReplaceFirst("%2", number);
532 						sprintf(number, "%d", (int)copies);
533 						job.ReplaceFirst("%3", number);
534 					} else {
535 						job = B_TRANSLATE("All pages, %1 copies");
536 						sprintf(number, "%d", (int)copies);
537 						job.ReplaceFirst("%1", number);
538 					}
539 				} else {
540 					if (printRange) {
541 						job = B_TRANSLATE("Page %1 to %2");
542 						sprintf(number, "%d", (int)first);
543 						job.ReplaceFirst("%1", number);
544 						sprintf(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