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