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