xref: /haiku/src/libs/print/libprint/JobSetupDlg.cpp (revision de18d919e7c940b402a6e213f7deb21e2f187e22)
1 /*
2  * JobSetupDlg.cpp
3  * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
4  */
5 
6 #include <cstdio>
7 #include <cstring>
8 #include <cstdlib>
9 #include <string>
10 #include <fcntl.h>
11 #include <unistd.h>
12 #include <sys/stat.h>
13 #include <math.h>
14 
15 #include <Alert.h>
16 #include <Bitmap.h>
17 #include <Box.h>
18 #include <Button.h>
19 #include <CheckBox.h>
20 #include <Debug.h>
21 #include <GridView.h>
22 #include <GroupLayout.h>
23 #include <GroupLayoutBuilder.h>
24 #include <Looper.h>
25 #include <MessageFilter.h>
26 #include <MenuField.h>
27 #include <MenuItem.h>
28 #include <Message.h>
29 #include <Point.h>
30 #include <PopUpMenu.h>
31 #include <PrintJob.h>
32 #include <RadioButton.h>
33 #include <Rect.h>
34 #include <Slider.h>
35 #include <String.h>
36 #include <TextControl.h>
37 #include <TextView.h>
38 #include <View.h>
39 
40 #include "HalftoneView.h"
41 #include "JobSetupDlg.h"
42 #include "JobData.h"
43 #include "JSDSlider.h"
44 #include "PagesView.h"
45 #include "PrinterData.h"
46 #include "PrinterCap.h"
47 #include "DbgMsg.h"
48 
49 
50 using namespace std;
51 
52 
53 struct NupCap : public BaseCap {
54 	NupCap(const string &label, bool isDefault, int nup)
55 		:
56 		BaseCap(label, isDefault),
57 		fNup(nup)
58 	{}
59 
60 	int32	ID() const { return fNup; }
61 
62 	int	fNup;
63 };
64 
65 
66 struct DitherCap : public BaseCap {
67 	DitherCap(const string &label, bool isDefault,
68 		Halftone::DitherType ditherType)
69 		:
70 		BaseCap(label, isDefault),
71 		fDitherType(ditherType)
72 	{}
73 
74 	int32	ID() const { return fDitherType; }
75 
76 	Halftone::DitherType fDitherType;
77 };
78 
79 
80 static const NupCap gNup1("1", true,  1);
81 static const NupCap gNup2("2",   false, 2);
82 static const NupCap gNup4("4",   false, 4);
83 static const NupCap gNup8("8",   false, 8);
84 static const NupCap gNup9("9",   false, 9);
85 static const NupCap gNup16("16", false, 16);
86 static const NupCap gNup25("25", false, 25);
87 static const NupCap gNup32("32", false, 32);
88 static const NupCap gNup36("36", false, 36);
89 
90 
91 static const DitherCap gDitherType1("Crosshatch", false, Halftone::kType1);
92 static const DitherCap gDitherType2("Grid", false, Halftone::kType2);
93 static const DitherCap gDitherType3("Stipple", false, Halftone::kType3);
94 static const DitherCap gDitherFloydSteinberg("Floyd-Steinberg", false,
95 	Halftone::kTypeFloydSteinberg);
96 
97 
98 const BaseCap *gNups[] = {
99 	&gNup1,
100 	&gNup2,
101 	&gNup4,
102 	&gNup8,
103 	&gNup9,
104 	&gNup16,
105 	&gNup25,
106 	&gNup32,
107 	&gNup36
108 };
109 
110 
111 const BaseCap *gDitherTypes[] = {
112 	&gDitherType1,
113 	&gDitherType2,
114 	&gDitherType3,
115 	&gDitherFloydSteinberg
116 };
117 
118 
119 enum {
120 	kMsgRangeAll = 'JSdl',
121 	kMsgRangeSelection,
122 	kMsgPreview,
123 	kMsgCancel,
124 	kMsgOK,
125 	kMsgQuality,
126 	kMsgCollateChanged,
127 	kMsgReverseChanged,
128 	kMsgDuplexChanged,
129 	kMsgNone = 0
130 };
131 
132 
133 JobSetupView::JobSetupView(JobData* jobData, PrinterData* printerData,
134 	const PrinterCap *printerCap)
135 	:
136 	BView("jobSetup", B_WILL_DRAW),
137 	fCopies(NULL),
138 	fFromPage(NULL),
139 	fToPage(NULL),
140 	fJobData(jobData),
141 	fPrinterData(printerData),
142 	fPrinterCap(printerCap),
143 	fColorType(NULL),
144 	fDitherType(NULL),
145 	fGamma(NULL),
146 	fInkDensity(NULL),
147 	fHalftone(NULL),
148 	fAll(NULL),
149 	fCollate(NULL),
150 	fReverse(NULL),
151 	fPages(NULL),
152 	fPaperFeed(NULL),
153 	fDuplex(NULL),
154 	fNup(NULL),
155 	fAllPages(NULL),
156 	fOddNumberedPages(NULL),
157 	fEvenNumberedPages(NULL)
158 {
159 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
160 }
161 
162 
163 BRadioButton*
164 JobSetupView::CreatePageSelectionItem(const char* name, const char* label,
165 	JobData::PageSelection pageSelection)
166 {
167 	BRadioButton* button = new BRadioButton(name, label, NULL);
168 	if (fJobData->getPageSelection() == pageSelection) {
169 		button->SetValue(B_CONTROL_ON);
170 	}
171 	return button;
172 }
173 
174 
175 void
176 JobSetupView::AllowOnlyDigits(BTextView* textView, int maxDigits)
177 {
178 	int num;
179 	for (num = 0; num <= 255; num++) {
180 		textView->DisallowChar(num);
181 	}
182 	for (num = 0; num <= 9; num++) {
183 		textView->AllowChar('0' + num);
184 	}
185 	textView->SetMaxBytes(maxDigits);
186 }
187 
188 
189 void
190 JobSetupView::AttachedToWindow()
191 {
192 	// quality
193 	BBox* qualityBox = new BBox("quality");
194 	qualityBox->SetLabel("Quality");
195 
196 	// color
197 	fColorType = new BPopUpMenu("color");
198 	fColorType->SetRadioMode(true);
199 	FillCapabilityMenu(fColorType, kMsgQuality, PrinterCap::kColor,
200 		fJobData->getColor());
201 	BMenuField* colorMenuField = new BMenuField("color", "Color:", fColorType);
202 	fColorType->SetTargetForItems(this);
203 
204 	if (IsHalftoneConfigurationNeeded())
205 		CreateHalftoneConfigurationUI();
206 
207 	// page range
208 
209 	BBox* pageRangeBox = new BBox("pageRange");
210 	pageRangeBox->SetLabel("Page Range");
211 
212 	fAll = new BRadioButton("all", "Print all Pages", new BMessage(kMsgRangeAll));
213 
214 	BRadioButton *range = new BRadioButton("selection", "Print selected Pages:",
215 		new BMessage(kMsgRangeSelection));
216 
217 	fFromPage = new BTextControl("from", "From:", "", NULL);
218 	fFromPage->SetAlignment(B_ALIGN_LEFT, B_ALIGN_RIGHT);
219 	AllowOnlyDigits(fFromPage->TextView(), 6);
220 
221 	fToPage = new BTextControl("to", "To:", "", NULL);
222 	fToPage->SetAlignment(B_ALIGN_LEFT, B_ALIGN_RIGHT);
223 	AllowOnlyDigits(fToPage->TextView(), 6);
224 
225 	int first_page = fJobData->getFirstPage();
226 	int last_page  = fJobData->getLastPage();
227 
228 	if (first_page <= 1 && last_page <= 0) {
229 		fAll->SetValue(B_CONTROL_ON);
230 	} else {
231 		range->SetValue(B_CONTROL_ON);
232 		if (first_page < 1)
233 			first_page = 1;
234 		if (first_page > last_page)
235 			last_page = -1;
236 
237 		BString oss1;
238 		oss1 << first_page;
239 		fFromPage->SetText(oss1.String());
240 
241 		BString oss2;
242 		oss2 << last_page;
243 		fToPage->SetText(oss2.String());
244 	}
245 
246 	fAll->SetTarget(this);
247 	range->SetTarget(this);
248 
249 	// paper source
250 	fPaperFeed = new BPopUpMenu("");
251 	fPaperFeed->SetRadioMode(true);
252 	FillCapabilityMenu(fPaperFeed, kMsgNone, PrinterCap::kPaperSource,
253 		fJobData->getPaperSource());
254 	BMenuField* paperSourceMenufield = new BMenuField("paperSource",
255 		"Paper Source:", fPaperFeed);
256 
257 	// Pages per sheet
258 	fNup = new BPopUpMenu("");
259 	fNup->SetRadioMode(true);
260 	FillCapabilityMenu(fNup, kMsgNone, gNups, sizeof(gNups) / sizeof(gNups[0]),
261 		(int)fJobData->getNup());
262 	BMenuField* pagesPerSheet = new BMenuField("pagesPerSheet",
263 		"Pages Per Sheet:", fNup);
264 
265 	// duplex
266 	if (fPrinterCap->isSupport(PrinterCap::kPrintStyle)) {
267 		fDuplex = new BCheckBox("duplex", "Duplex",
268 			new BMessage(kMsgDuplexChanged));
269 		if (fJobData->getPrintStyle() != JobData::kSimplex) {
270 			fDuplex->SetValue(B_CONTROL_ON);
271 		}
272 		fDuplex->SetTarget(this);
273 	} else {
274 		fDuplex = NULL;
275 	}
276 
277 	// copies
278 	fCopies = new BTextControl("copies", "Number of Copies:", "", NULL);
279 	AllowOnlyDigits(fCopies->TextView(), 3);
280 
281 	BString copies;
282 	copies << fJobData->getCopies();
283 	fCopies->SetText(copies.String());
284 
285 	// collate
286 	fCollate = new BCheckBox("collate", "Collate",
287 		new BMessage(kMsgCollateChanged));
288 	if (fJobData->getCollate()) {
289 		fCollate->SetValue(B_CONTROL_ON);
290 	}
291 	fCollate->SetTarget(this);
292 
293 	// reverse
294 	fReverse = new BCheckBox("reverse", "Reverse Order",
295 		new BMessage(kMsgReverseChanged));
296 	if (fJobData->getReverse()) {
297 		fReverse->SetValue(B_CONTROL_ON);
298 	}
299 	fReverse->SetTarget(this);
300 
301 	// pages view
302 	// TODO make layout API compatible
303 	fPages = new PagesView(BRect(0, 0, 150, 40), "pages", B_FOLLOW_ALL,
304 		B_WILL_DRAW);
305 	fPages->setCollate(fJobData->getCollate());
306 	fPages->setReverse(fJobData->getReverse());
307 	fPages->SetExplicitMinSize(BSize(150, 40));
308 	fPages->SetExplicitMaxSize(BSize(150, 40));
309 
310 	// page selection
311 	BBox* pageSelectionBox = new BBox("pageSelection");
312 	pageSelectionBox->SetLabel("Page Selection");
313 
314 	fAllPages = CreatePageSelectionItem("allPages", "All Pages",
315 		JobData::kAllPages);
316 	fOddNumberedPages = CreatePageSelectionItem("oddPages",
317 		"Odd-Numbered Pages", JobData::kOddNumberedPages);
318 	fEvenNumberedPages = CreatePageSelectionItem("evenPages",
319 		"Even-Numbered Pages", JobData::kEvenNumberedPages);
320 
321 	// separator line
322 	BBox *separator = new BBox("separator");
323 	separator->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1));
324 
325 	// buttons
326 	BButton* preview = new BButton("preview", "Preview" B_UTF8_ELLIPSIS,
327 		new BMessage(kMsgPreview));
328 	BButton* cancel = new BButton("cancel", "Cancel",
329 		new BMessage(kMsgCancel));
330 	BButton* ok = new BButton("ok", "OK", new BMessage(kMsgOK));
331 	ok->MakeDefault(true);
332 
333 	if (IsHalftoneConfigurationNeeded()) {
334 		BGroupView* halftoneGroup = new BGroupView(B_VERTICAL, 0);
335 		BGroupLayout* halftoneLayout = halftoneGroup->GroupLayout();
336 		halftoneLayout->AddView(fHalftone);
337 		fHalftoneBox->AddChild(halftoneGroup);
338 	}
339 
340 	BGridView* qualityGrid = new BGridView();
341 	BGridLayout* qualityGridLayout = qualityGrid->GridLayout();
342 	qualityGridLayout->AddItem(colorMenuField->CreateLabelLayoutItem(), 0, 0);
343 	qualityGridLayout->AddItem(colorMenuField->CreateMenuBarLayoutItem(), 1, 0);
344 	if (IsHalftoneConfigurationNeeded()) {
345 		qualityGridLayout->AddItem(fDitherMenuField->CreateLabelLayoutItem(),
346 			0, 1);
347 		qualityGridLayout->AddItem(fDitherMenuField->CreateMenuBarLayoutItem(),
348 			1, 1);
349 		qualityGridLayout->AddView(fGamma, 0, 2, 2);
350 		qualityGridLayout->AddView(fInkDensity, 0, 3, 2);
351 		qualityGridLayout->AddView(fHalftoneBox, 0, 4, 2);
352 	} else {
353 		AddDriverSpecificSettings(qualityGridLayout, 1);
354 	}
355 	qualityGridLayout->SetSpacing(0, 0);
356 	qualityGridLayout->SetInsets(5, 5, 5, 5);
357 	qualityBox->AddChild(qualityGrid);
358 
359 	BGridView* pageRangeGrid = new BGridView();
360 	BGridLayout* pageRangeLayout = pageRangeGrid->GridLayout();
361 	pageRangeLayout->AddItem(fFromPage->CreateLabelLayoutItem(), 0, 0);
362 	pageRangeLayout->AddItem(fFromPage->CreateTextViewLayoutItem(), 1, 0);
363 	pageRangeLayout->AddItem(fToPage->CreateLabelLayoutItem(), 0, 1);
364 	pageRangeLayout->AddItem(fToPage->CreateTextViewLayoutItem(), 1, 1);
365 	pageRangeLayout->SetInsets(0, 0, 0, 0);
366 	pageRangeLayout->SetSpacing(0, 0);
367 
368 	BGroupView* pageRangeGroup = new BGroupView(B_VERTICAL, 0);
369 	BGroupLayout* pageRangeGroupLayout = pageRangeGroup->GroupLayout();
370 	pageRangeGroupLayout->AddView(fAll);
371 	pageRangeGroupLayout->AddView(range);
372 	pageRangeGroupLayout->AddView(pageRangeGrid);
373 	pageRangeGroupLayout->SetInsets(5, 5, 5, 5);
374 	pageRangeBox->AddChild(pageRangeGroup);
375 
376 	BGridView* settings = new BGridView();
377 	BGridLayout* settingsLayout = settings->GridLayout();
378 	settingsLayout->AddItem(paperSourceMenufield->CreateLabelLayoutItem(), 0,
379 		0);
380 	settingsLayout->AddItem(paperSourceMenufield->CreateMenuBarLayoutItem(), 1,
381 		0);
382 	settingsLayout->AddItem(pagesPerSheet->CreateLabelLayoutItem(), 0, 1);
383 	settingsLayout->AddItem(pagesPerSheet->CreateMenuBarLayoutItem(), 1, 1);
384 	int row = 2;
385 	if (fDuplex != NULL) {
386 		settingsLayout->AddView(fDuplex, 0, row, 2);
387 		row ++;
388 	}
389 	settingsLayout->AddItem(fCopies->CreateLabelLayoutItem(), 0, row);
390 	settingsLayout->AddItem(fCopies->CreateTextViewLayoutItem(), 1, row);
391 	settingsLayout->SetSpacing(0, 0);
392 
393 
394 	BGroupView* pageSelectionGroup = new BGroupView(B_VERTICAL, 0);
395 	BGroupLayout* groupLayout = pageSelectionGroup->GroupLayout();
396 	groupLayout->AddView(fAllPages);
397 	groupLayout->AddView(fOddNumberedPages);
398 	groupLayout->AddView(fEvenNumberedPages);
399 	groupLayout->SetInsets(5, 5, 5, 5);
400 	pageSelectionBox->AddChild(pageSelectionGroup);
401 
402 	SetLayout(new BGroupLayout(B_VERTICAL));
403 	AddChild(BGroupLayoutBuilder(B_VERTICAL, 0)
404 		.AddGroup(B_HORIZONTAL, 10, 1.0f)
405 			.AddGroup(B_VERTICAL, 10, 1.0f)
406 				.Add(qualityBox)
407 				.Add(pageRangeBox)
408 				.AddGlue()
409 			.End()
410 			.AddGroup(B_VERTICAL, 0, 1.0f)
411 				.Add(settings)
412 				.AddStrut(5)
413 				.Add(fCollate)
414 				.Add(fReverse)
415 				.Add(fPages)
416 				.AddStrut(5)
417 				.Add(pageSelectionBox)
418 				.AddGlue()
419 			.End()
420 		.End()
421 		.AddGlue()
422 		.Add(separator)
423 		.AddGroup(B_HORIZONTAL, 10, 1.0f)
424 			.AddGlue()
425 			.Add(cancel)
426 			.Add(preview)
427 			.Add(ok)
428 		.End()
429 		.SetInsets(0, 0, 0, 0)
430 	);
431 
432 	UpdateHalftonePreview();
433 
434 	UpdateButtonEnabledState();
435 }
436 
437 
438 bool
439 JobSetupView::IsHalftoneConfigurationNeeded()
440 {
441 	return fPrinterCap->isSupport(PrinterCap::kHalftone);
442 }
443 
444 
445 void
446 JobSetupView::CreateHalftoneConfigurationUI()
447 {
448 	// dither type
449 	fDitherType = new BPopUpMenu("");
450 	fDitherType->SetRadioMode(true);
451 	FillCapabilityMenu(fDitherType, kMsgQuality, gDitherTypes,
452 		sizeof(gDitherTypes) / sizeof(gDitherTypes[0]),
453 		(int)fJobData->getDitherType());
454 	fDitherMenuField = new BMenuField("dithering", "Dot Pattern:",
455 		fDitherType);
456 	fDitherType->SetTargetForItems(this);
457 
458 	// halftone preview view
459 	fHalftoneBox = new BBox("halftoneBox");
460 	fHalftoneBox->SetBorder(B_PLAIN_BORDER);
461 
462 	// TODO make layout compatible
463 	BSize size(240, 14 * 4);
464 	BRect rect(0, 0, size.width, size.height);
465 	fHalftone = new HalftoneView(rect, "halftone",
466 		B_FOLLOW_ALL, B_WILL_DRAW);
467 	fHalftone->SetExplicitMinSize(size);
468 	fHalftone->SetExplicitMaxSize(size);
469 
470 	// gamma
471 	fGamma = new JSDSlider("gamma", "Gamma", new BMessage(kMsgQuality),
472 		-300, 300);
473 
474 	fGamma->SetLimitLabels("Lighter", "Darker");
475 	fGamma->SetValue((int32)(100 * log(fJobData->getGamma()) / log(2.0)));
476 	fGamma->SetHashMarks(B_HASH_MARKS_BOTH);
477 	fGamma->SetHashMarkCount(7);
478 	fGamma->SetModificationMessage(new BMessage(kMsgQuality));
479 	fGamma->SetTarget(this);
480 
481 	// ink density
482 	fInkDensity = new JSDSlider("inkDensity", "Ink Usage",
483 		new BMessage(kMsgQuality), 0, 127);
484 
485 	fInkDensity->SetLimitLabels("Min", "Max");
486 	fInkDensity->SetValue((int32)fJobData->getInkDensity());
487 	fInkDensity->SetHashMarks(B_HASH_MARKS_BOTH);
488 	fInkDensity->SetHashMarkCount(10);
489 	fInkDensity->SetModificationMessage(new BMessage(kMsgQuality));
490 	fInkDensity->SetTarget(this);
491 }
492 
493 
494 void
495 JobSetupView::AddDriverSpecificSettings(BGridLayout* gridLayout, int row)
496 {
497 	if (!fPrinterCap->isSupport(PrinterCap::kDriverSpecificCapabilities))
498 		return;
499 
500 	int count = fPrinterCap->countCap(PrinterCap::kDriverSpecificCapabilities);
501 	const BaseCap** capabilities = fPrinterCap->enumCap(
502 		PrinterCap::kDriverSpecificCapabilities);
503 
504 	for (int i = 0; i < count; i ++) {
505 		const DriverSpecificCap* capability = static_cast<const DriverSpecificCap*>(
506 			capabilities[i]);
507 
508 		const char* label = capability->fLabel.c_str();
509 		BPopUpMenu* popUpMenu = new BPopUpMenu(label);
510 		popUpMenu->SetRadioMode(true);
511 
512 		PrinterCap::CapID category = static_cast<PrinterCap::CapID>(
513 			capability->ID());
514 
515 		const BaseCap** categoryCapabilities = fPrinterCap->enumCap(category);
516 
517 		int categoryCount = fPrinterCap->countCap(category);
518 
519 		string value = GetDriverSpecificValue(category, capability->Key());
520 		PrinterCap::KeyPredicate predicate(value.c_str());
521 
522 		FillCapabilityMenu(popUpMenu, kMsgNone, categoryCapabilities,
523 			categoryCount, predicate);
524 
525 		BString menuLabel = label;
526 		menuLabel << ":";
527 		BMenuField* menuField = new BMenuField(label, menuLabel.String(),
528 			popUpMenu);
529 		popUpMenu->SetTargetForItems(this);
530 
531 		gridLayout->AddItem(menuField->CreateLabelLayoutItem(),
532 			0, row);
533 		gridLayout->AddItem(menuField->CreateMenuBarLayoutItem(),
534 			1, row);
535 		row ++;
536 
537 		fDriverSpecificLists[category] = popUpMenu;
538 	}
539 }
540 
541 
542 string
543 JobSetupView::GetDriverSpecificValue(PrinterCap::CapID category,
544 	const char* key)
545 {
546 	if (fJobData->HasDriverSpecificSetting(key))
547 		return fJobData->DriverSpecificSetting(key);
548 
549 	const BaseCap* defaultCapability = fPrinterCap->getDefaultCap(category);
550 	return defaultCapability->fKey;
551 }
552 
553 
554 template<typename Predicate>
555 void
556 JobSetupView::FillCapabilityMenu(BPopUpMenu* menu, uint32 message,
557 	const BaseCap** capabilities, int count, Predicate& predicate)
558 {
559 	bool marked = false;
560 
561 	BMenuItem* firstItem = NULL;
562 	BMenuItem* defaultItem = NULL;
563 	BMenuItem* item = NULL;
564 	while (count--) {
565 		const BaseCap* capability = *capabilities;
566 		if (message != kMsgNone)
567 			item = new BMenuItem(capability->fLabel.c_str(),
568 				new BMessage(message));
569 		else
570 			item = new BMenuItem(capability->fLabel.c_str(), NULL);
571 
572 		menu->AddItem(item);
573 
574 		if (firstItem == NULL)
575 			firstItem = item;
576 
577 		if (capability->fIsDefault)
578 			defaultItem = item;
579 
580 
581 		if (predicate(capability)) {
582 			item->SetMarked(true);
583 			marked = true;
584 		}
585 
586 		capabilities++;
587 	}
588 
589 	if (marked)
590 		return;
591 
592 	if (defaultItem != NULL)
593 		defaultItem->SetMarked(true);
594 	else if (firstItem != NULL)
595 		firstItem->SetMarked(true);
596 }
597 
598 
599 void
600 JobSetupView::FillCapabilityMenu(BPopUpMenu* menu, uint32 message,
601 	PrinterCap::CapID category, int id)
602 {
603 	PrinterCap::IDPredicate predicate(id);
604 	int count = fPrinterCap->countCap(category);
605 	const BaseCap **capabilities = fPrinterCap->enumCap(category);
606 	FillCapabilityMenu(menu, message, capabilities, count, predicate);
607 }
608 
609 
610 void
611 JobSetupView::FillCapabilityMenu(BPopUpMenu* menu, uint32 message,
612 	const BaseCap** capabilities, int count, int id)
613 {
614 	PrinterCap::IDPredicate predicate(id);
615 	FillCapabilityMenu(menu, message, capabilities, count, predicate);
616 }
617 
618 
619 int
620 JobSetupView::GetID(const BaseCap** capabilities, int count, const char* label,
621 	int defaultValue)
622 {
623 	while (count--) {
624 		const BaseCap* capability = *capabilities;
625 		if (capability->fLabel == label)
626 			return capability->ID();
627 	}
628 	return defaultValue;
629 }
630 
631 
632 void
633 JobSetupView::UpdateButtonEnabledState()
634 {
635 	bool pageRangeEnabled = fAll->Value() != B_CONTROL_ON;
636 	fFromPage->SetEnabled(pageRangeEnabled);
637 	fToPage->SetEnabled(pageRangeEnabled);
638 
639 	bool pageSelectionEnabled = fDuplex == NULL ||
640 		fDuplex->Value() != B_CONTROL_ON;
641 	fAllPages->SetEnabled(pageSelectionEnabled);
642 	fOddNumberedPages->SetEnabled(pageSelectionEnabled);
643 	fEvenNumberedPages->SetEnabled(pageSelectionEnabled);
644 }
645 
646 
647 void
648 JobSetupView::MessageReceived(BMessage* message)
649 {
650 	switch (message->what) {
651 	case kMsgRangeAll:
652 	case kMsgRangeSelection:
653 	case kMsgDuplexChanged:
654 		UpdateButtonEnabledState();
655 		break;
656 
657 	case kMsgQuality:
658 		UpdateHalftonePreview();
659 		break;
660 
661 	case kMsgCollateChanged:
662 		fPages->setCollate(fCollate->Value() == B_CONTROL_ON);
663 		break;
664 
665 	case kMsgReverseChanged:
666 		fPages->setReverse(fReverse->Value() == B_CONTROL_ON);
667 		break;
668 	}
669 }
670 
671 
672 void
673 JobSetupView::UpdateHalftonePreview()
674 {
675 	if (!IsHalftoneConfigurationNeeded())
676 		return;
677 
678 	fHalftone->preview(Gamma(), InkDensity(), DitherType(),
679 		Color() != JobData::kMonochrome);
680 }
681 
682 
683 JobData::Color
684 JobSetupView::Color()
685 {
686 	const char *label = fColorType->FindMarked()->Label();
687 	const BaseCap* capability = fPrinterCap->findCap(PrinterCap::kColor, label);
688 	if (capability == NULL)
689 		return JobData::kMonochrome;
690 
691 	const ColorCap* colorCap = static_cast<const ColorCap*>(capability);
692 	return colorCap->fColor;
693 }
694 
695 
696 Halftone::DitherType
697 JobSetupView::DitherType()
698 {
699 	const char *label = fDitherType->FindMarked()->Label();
700 	int id = GetID(gDitherTypes, sizeof(gDitherTypes) / sizeof(gDitherTypes[0]),
701 		label, Halftone::kTypeFloydSteinberg);
702 	return static_cast<Halftone::DitherType>(id);
703 }
704 
705 float
706 JobSetupView::Gamma()
707 {
708 	const float value = (float)fGamma->Value();
709 	return pow(2.0, value / 100.0);
710 }
711 
712 
713 float
714 JobSetupView::InkDensity()
715 {
716 	const float value = (float)(127 - fInkDensity->Value());
717 	return value;
718 }
719 
720 
721 JobData::PaperSource
722 JobSetupView::PaperSource()
723 {
724 	const char *label = fPaperFeed->FindMarked()->Label();
725 	const BaseCap* capability = fPrinterCap->findCap(PrinterCap::kPaperSource,
726 		label);
727 
728 	if (capability == NULL)
729 		capability = fPrinterCap->getDefaultCap(PrinterCap::kPaperSource);
730 	return static_cast<const PaperSourceCap*>(capability)->fPaperSource;
731 
732 }
733 
734 bool
735 JobSetupView::UpdateJobData(bool showPreview)
736 {
737 	fJobData->setShowPreview(showPreview);
738 	fJobData->setColor(Color());
739 	if (IsHalftoneConfigurationNeeded()) {
740 		fJobData->setGamma(Gamma());
741 		fJobData->setInkDensity(InkDensity());
742 		fJobData->setDitherType(DitherType());
743 	}
744 
745 	int first_page;
746 	int last_page;
747 
748 	if (B_CONTROL_ON == fAll->Value()) {
749 		first_page = 1;
750 		last_page  = -1;
751 	} else {
752 		first_page = atoi(fFromPage->Text());
753 		last_page  = atoi(fToPage->Text());
754 	}
755 
756 	fJobData->setFirstPage(first_page);
757 	fJobData->setLastPage(last_page);
758 
759 	fJobData->setPaperSource(PaperSource());
760 
761 	fJobData->setNup(GetID(gNups, sizeof(gNups) / sizeof(gNups[0]),
762 		fNup->FindMarked()->Label(), 1));
763 
764 	if (fPrinterCap->isSupport(PrinterCap::kPrintStyle)) {
765 		fJobData->setPrintStyle((B_CONTROL_ON == fDuplex->Value())
766 			? JobData::kDuplex : JobData::kSimplex);
767 	}
768 
769 	fJobData->setCopies(atoi(fCopies->Text()));
770 
771 	fJobData->setCollate(B_CONTROL_ON == fCollate->Value());
772 	fJobData->setReverse(B_CONTROL_ON == fReverse->Value());
773 
774 	JobData::PageSelection pageSelection = JobData::kAllPages;
775 	if (fOddNumberedPages->Value() == B_CONTROL_ON)
776 		pageSelection = JobData::kOddNumberedPages;
777 	if (fEvenNumberedPages->Value() == B_CONTROL_ON)
778 		pageSelection = JobData::kEvenNumberedPages;
779 	fJobData->setPageSelection(pageSelection);
780 
781 	std::map<PrinterCap::CapID, BPopUpMenu*>::iterator it =
782 		fDriverSpecificLists.begin();
783 	for(; it != fDriverSpecificLists.end(); it++) {
784 		PrinterCap::CapID category = it->first;
785 		BPopUpMenu* popUpMenu = it->second;
786 		const char* key = fPrinterCap->findCap(
787 			PrinterCap::kDriverSpecificCapabilities, (int)category)->Key();
788 		const char* label = popUpMenu->FindMarked()->Label();
789 		const char* value = fPrinterCap->findCap(category, label)->Key();
790 		fJobData->SetDriverSpecificSetting(key, value);
791 	}
792 
793 	fJobData->save();
794 	return true;
795 }
796 
797 
798 JobSetupDlg::JobSetupDlg(JobData* jobData, PrinterData* printerData,
799 	const PrinterCap* printerCap)
800 	:
801 	DialogWindow(BRect(100, 100, 200, 200), "PrintJob Setup",
802 		B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
803 		B_NOT_RESIZABLE | B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE
804 			| B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
805 {
806 	SetResult(B_ERROR);
807 	AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
808 
809 	fJobSetup = new JobSetupView(jobData, printerData, printerCap);
810 	SetLayout(new BGroupLayout(B_VERTICAL));
811 	AddChild(BGroupLayoutBuilder(B_VERTICAL, 0)
812 		.Add(fJobSetup)
813 		.SetInsets(10, 10, 10, 10)
814 	);
815 }
816 
817 
818 void
819 JobSetupDlg::MessageReceived(BMessage* message)
820 {
821 	switch (message->what) {
822 	case kMsgOK:
823 	case kMsgPreview:
824 		fJobSetup->UpdateJobData(message->what == kMsgPreview);
825 		SetResult(B_NO_ERROR);
826 		PostMessage(B_QUIT_REQUESTED);
827 		break;
828 
829 	case kMsgCancel:
830 		PostMessage(B_QUIT_REQUESTED);
831 		break;
832 
833 	default:
834 		DialogWindow::MessageReceived(message);
835 		break;
836 	}
837 }
838