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 EnumCap {
NupCapNupCap54 NupCap(const string &label, bool isDefault, int nup)
55 :
56 EnumCap(label, isDefault),
57 fNup(nup)
58 {}
59
IDNupCap60 int32 ID() const { return fNup; }
61
62 int fNup;
63 };
64
65
66 struct DitherCap : public EnumCap {
DitherCapDitherCap67 DitherCap(const string &label, bool isDefault,
68 Halftone::DitherType ditherType)
69 :
70 EnumCap(label, isDefault),
71 fDitherType(ditherType)
72 {}
73
IDDitherCap74 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 static const char* kCategoryID = "id";
120
121
122 enum {
123 kMsgRangeAll = 'JSdl',
124 kMsgRangeSelection,
125 kMsgCancel,
126 kMsgOK,
127 kMsgQuality,
128 kMsgCollateChanged,
129 kMsgReverseChanged,
130 kMsgDuplexChanged,
131 kMsgIntSliderChanged,
132 kMsgDoubleSliderChanged,
133 kMsgNone = 0
134 };
135
136
JobSetupView(JobData * jobData,PrinterData * printerData,const PrinterCap * printerCap)137 JobSetupView::JobSetupView(JobData* jobData, PrinterData* printerData,
138 const PrinterCap *printerCap)
139 :
140 BView("jobSetup", B_WILL_DRAW),
141 fCopies(NULL),
142 fFromPage(NULL),
143 fToPage(NULL),
144 fJobData(jobData),
145 fPrinterData(printerData),
146 fPrinterCap(printerCap),
147 fColorType(NULL),
148 fDitherType(NULL),
149 fGamma(NULL),
150 fInkDensity(NULL),
151 fHalftone(NULL),
152 fAll(NULL),
153 fCollate(NULL),
154 fReverse(NULL),
155 fPages(NULL),
156 fPaperFeed(NULL),
157 fDuplex(NULL),
158 fNup(NULL),
159 fAllPages(NULL),
160 fOddNumberedPages(NULL),
161 fEvenNumberedPages(NULL)
162 {
163 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
164 }
165
166
167 BRadioButton*
CreatePageSelectionItem(const char * name,const char * label,JobData::PageSelection pageSelection)168 JobSetupView::CreatePageSelectionItem(const char* name, const char* label,
169 JobData::PageSelection pageSelection)
170 {
171 BRadioButton* button = new BRadioButton(name, label, NULL);
172 if (fJobData->GetPageSelection() == pageSelection) {
173 button->SetValue(B_CONTROL_ON);
174 }
175 return button;
176 }
177
178
179 void
AllowOnlyDigits(BTextView * textView,int maxDigits)180 JobSetupView::AllowOnlyDigits(BTextView* textView, int maxDigits)
181 {
182 int num;
183 for (num = 0; num <= 255; num++) {
184 textView->DisallowChar(num);
185 }
186 for (num = 0; num <= 9; num++) {
187 textView->AllowChar('0' + num);
188 }
189 textView->SetMaxBytes(maxDigits);
190 }
191
192
193 void
AttachedToWindow()194 JobSetupView::AttachedToWindow()
195 {
196 // quality
197 BBox* qualityBox = new BBox("quality");
198 qualityBox->SetLabel("Quality");
199
200 // color
201 fColorType = new BPopUpMenu("color");
202 fColorType->SetRadioMode(true);
203 FillCapabilityMenu(fColorType, kMsgQuality, PrinterCap::kColor,
204 fJobData->GetColor());
205 BMenuField* colorMenuField = new BMenuField("color", "Color:", fColorType);
206 fColorType->SetTargetForItems(this);
207
208 if (IsHalftoneConfigurationNeeded())
209 CreateHalftoneConfigurationUI();
210
211 // page range
212
213 BBox* pageRangeBox = new BBox("pageRange");
214 pageRangeBox->SetLabel("Page range");
215
216 fAll = new BRadioButton("all", "Print all Pages", new BMessage(kMsgRangeAll));
217
218 BRadioButton *range = new BRadioButton("selection", "Print selected Pages:",
219 new BMessage(kMsgRangeSelection));
220
221 fFromPage = new BTextControl("from", "From:", "", NULL);
222 fFromPage->SetAlignment(B_ALIGN_LEFT, B_ALIGN_RIGHT);
223 AllowOnlyDigits(fFromPage->TextView(), 6);
224
225 fToPage = new BTextControl("to", "To:", "", NULL);
226 fToPage->SetAlignment(B_ALIGN_LEFT, B_ALIGN_RIGHT);
227 AllowOnlyDigits(fToPage->TextView(), 6);
228
229 int first_page = fJobData->GetFirstPage();
230 int last_page = fJobData->GetLastPage();
231
232 if (first_page <= 1 && last_page <= 0) {
233 fAll->SetValue(B_CONTROL_ON);
234 } else {
235 range->SetValue(B_CONTROL_ON);
236 if (first_page < 1)
237 first_page = 1;
238 if (first_page > last_page)
239 last_page = -1;
240
241 BString oss1;
242 oss1 << first_page;
243 fFromPage->SetText(oss1.String());
244
245 BString oss2;
246 oss2 << last_page;
247 fToPage->SetText(oss2.String());
248 }
249
250 fAll->SetTarget(this);
251 range->SetTarget(this);
252
253 // paper source
254 fPaperFeed = new BPopUpMenu("");
255 fPaperFeed->SetRadioMode(true);
256 FillCapabilityMenu(fPaperFeed, kMsgNone, PrinterCap::kPaperSource,
257 fJobData->GetPaperSource());
258 BMenuField* paperSourceMenufield = new BMenuField("paperSource",
259 "Paper source:", fPaperFeed);
260
261 // Pages per sheet
262 fNup = new BPopUpMenu("");
263 fNup->SetRadioMode(true);
264 FillCapabilityMenu(fNup, kMsgNone, gNups,
265 sizeof(gNups) / sizeof(gNups[0]), (int)fJobData->GetNup());
266 BMenuField* pagesPerSheet = new BMenuField("pagesPerSheet",
267 "Pages per sheet:", fNup);
268
269 // duplex
270 if (fPrinterCap->Supports(PrinterCap::kPrintStyle)) {
271 fDuplex = new BCheckBox("duplex", "Duplex",
272 new BMessage(kMsgDuplexChanged));
273 if (fJobData->GetPrintStyle() != JobData::kSimplex) {
274 fDuplex->SetValue(B_CONTROL_ON);
275 }
276 fDuplex->SetTarget(this);
277 } else {
278 fDuplex = NULL;
279 }
280
281 // copies
282 fCopies = new BTextControl("copies", "Number of copies:", "", NULL);
283 AllowOnlyDigits(fCopies->TextView(), 3);
284
285 BString copies;
286 copies << fJobData->GetCopies();
287 fCopies->SetText(copies.String());
288
289 // collate
290 fCollate = new BCheckBox("collate", "Collate",
291 new BMessage(kMsgCollateChanged));
292 if (fJobData->GetCollate()) {
293 fCollate->SetValue(B_CONTROL_ON);
294 }
295 fCollate->SetTarget(this);
296
297 // reverse
298 fReverse = new BCheckBox("reverse", "Reverse order",
299 new BMessage(kMsgReverseChanged));
300 if (fJobData->GetReverse()) {
301 fReverse->SetValue(B_CONTROL_ON);
302 }
303 fReverse->SetTarget(this);
304
305 // pages view
306 // TODO make layout API compatible
307 fPages = new PagesView(BRect(0, 0, 150, 40), "pages", B_FOLLOW_ALL,
308 B_WILL_DRAW);
309 fPages->SetCollate(fJobData->GetCollate());
310 fPages->SetReverse(fJobData->GetReverse());
311 fPages->SetExplicitMinSize(BSize(150, 40));
312 fPages->SetExplicitMaxSize(BSize(150, 40));
313
314 // page selection
315 BBox* pageSelectionBox = new BBox("pageSelection");
316 pageSelectionBox->SetLabel("Page selection");
317
318 fAllPages = CreatePageSelectionItem("allPages", "All pages",
319 JobData::kAllPages);
320 fOddNumberedPages = CreatePageSelectionItem("oddPages",
321 "Odd-numbered pages", JobData::kOddNumberedPages);
322 fEvenNumberedPages = CreatePageSelectionItem("evenPages",
323 "Even-numbered pages", JobData::kEvenNumberedPages);
324
325 fPreview = new BCheckBox("preview", "Show preview before printing", NULL);
326 if (fJobData->GetShowPreview())
327 fPreview->SetValue(B_CONTROL_ON);
328
329 // separator line
330 BBox *separator = new BBox("separator");
331 separator->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1));
332
333 // buttons
334 BButton* cancel = new BButton("cancel", "Cancel",
335 new BMessage(kMsgCancel));
336 BButton* ok = new BButton("ok", "OK", new BMessage(kMsgOK));
337 ok->MakeDefault(true);
338
339 if (IsHalftoneConfigurationNeeded()) {
340 BGroupView* halftoneGroup = new BGroupView(B_VERTICAL, 0);
341 BGroupLayout* halftoneLayout = halftoneGroup->GroupLayout();
342 halftoneLayout->AddView(fHalftone);
343 fHalftoneBox->AddChild(halftoneGroup);
344 }
345
346 BGridView* qualityGrid = new BGridView();
347 BGridLayout* qualityGridLayout = qualityGrid->GridLayout();
348 qualityGridLayout->AddItem(colorMenuField->CreateLabelLayoutItem(), 0, 0);
349 qualityGridLayout->AddItem(colorMenuField->CreateMenuBarLayoutItem(), 1, 0);
350 if (IsHalftoneConfigurationNeeded()) {
351 qualityGridLayout->AddItem(fDitherMenuField->CreateLabelLayoutItem(),
352 0, 1);
353 qualityGridLayout->AddItem(fDitherMenuField->CreateMenuBarLayoutItem(),
354 1, 1);
355 qualityGridLayout->AddView(fGamma, 0, 2, 2);
356 qualityGridLayout->AddView(fInkDensity, 0, 3, 2);
357 qualityGridLayout->AddView(fHalftoneBox, 0, 4, 2);
358 } else {
359 AddDriverSpecificSettings(qualityGridLayout, 1);
360 }
361 qualityGridLayout->SetSpacing(0, 0);
362 qualityGridLayout->SetInsets(5, 5, 5, 5);
363 qualityBox->AddChild(qualityGrid);
364 // TODO put qualityGrid in a scroll view
365 // the layout of the box surrounding the scroll view using the following
366 // code is not correct; the box still has the size of the qualityGird;
367 // and the scroll view is vertically centered inside the box!
368 //BScrollView* qualityScroller = new BScrollView("qualityScroller",
369 // qualityGrid, 0, false, true);
370 //qualityScroller->SetExplicitMaxSize(BSize(500, 500));
371 //qualityBox->AddChild(qualityScroller);
372
373 BGridView* pageRangeGrid = new BGridView();
374 BGridLayout* pageRangeLayout = pageRangeGrid->GridLayout();
375 pageRangeLayout->AddItem(fFromPage->CreateLabelLayoutItem(), 0, 0);
376 pageRangeLayout->AddItem(fFromPage->CreateTextViewLayoutItem(), 1, 0);
377 pageRangeLayout->AddItem(fToPage->CreateLabelLayoutItem(), 0, 1);
378 pageRangeLayout->AddItem(fToPage->CreateTextViewLayoutItem(), 1, 1);
379 pageRangeLayout->SetInsets(0, 0, 0, 0);
380 pageRangeLayout->SetSpacing(0, 0);
381
382 BGroupView* pageRangeGroup = new BGroupView(B_VERTICAL, 0);
383 BGroupLayout* pageRangeGroupLayout = pageRangeGroup->GroupLayout();
384 pageRangeGroupLayout->AddView(fAll);
385 pageRangeGroupLayout->AddView(range);
386 pageRangeGroupLayout->AddView(pageRangeGrid);
387 pageRangeGroupLayout->SetInsets(5, 5, 5, 5);
388 pageRangeBox->AddChild(pageRangeGroup);
389
390 BGridView* settings = new BGridView();
391 BGridLayout* settingsLayout = settings->GridLayout();
392 settingsLayout->AddItem(paperSourceMenufield->CreateLabelLayoutItem(), 0,
393 0);
394 settingsLayout->AddItem(paperSourceMenufield->CreateMenuBarLayoutItem(), 1,
395 0);
396 settingsLayout->AddItem(pagesPerSheet->CreateLabelLayoutItem(), 0, 1);
397 settingsLayout->AddItem(pagesPerSheet->CreateMenuBarLayoutItem(), 1, 1);
398 int row = 2;
399 if (fDuplex != NULL) {
400 settingsLayout->AddView(fDuplex, 0, row, 2);
401 row ++;
402 }
403 settingsLayout->AddItem(fCopies->CreateLabelLayoutItem(), 0, row);
404 settingsLayout->AddItem(fCopies->CreateTextViewLayoutItem(), 1, row);
405 settingsLayout->SetSpacing(0, 0);
406
407
408 BGroupView* pageSelectionGroup = new BGroupView(B_VERTICAL, 0);
409 BGroupLayout* groupLayout = pageSelectionGroup->GroupLayout();
410 groupLayout->AddView(fAllPages);
411 groupLayout->AddView(fOddNumberedPages);
412 groupLayout->AddView(fEvenNumberedPages);
413 groupLayout->SetInsets(5, 5, 5, 5);
414 pageSelectionBox->AddChild(pageSelectionGroup);
415
416 SetLayout(new BGroupLayout(B_VERTICAL));
417 AddChild(BGroupLayoutBuilder(B_VERTICAL, 0)
418 .AddGroup(B_HORIZONTAL, 10, 1.0f)
419 .AddGroup(B_VERTICAL, 10, 1.0f)
420 .Add(qualityBox)
421 .Add(pageRangeBox)
422 .AddGlue()
423 .End()
424 .AddGroup(B_VERTICAL, 0, 1.0f)
425 .Add(settings)
426 .AddStrut(5)
427 .Add(fCollate)
428 .Add(fReverse)
429 .Add(fPages)
430 .AddStrut(5)
431 .Add(pageSelectionBox)
432 .AddGlue()
433 .End()
434 .End()
435 .Add(fPreview)
436 .AddGlue()
437 .Add(separator)
438 .AddGroup(B_HORIZONTAL, 10, 1.0f)
439 .AddGlue()
440 .Add(cancel)
441 .Add(ok)
442 .End()
443 .SetInsets(0, 0, 0, 0)
444 );
445
446 UpdateHalftonePreview();
447
448 UpdateButtonEnabledState();
449 }
450
451
452 bool
IsHalftoneConfigurationNeeded()453 JobSetupView::IsHalftoneConfigurationNeeded()
454 {
455 return fPrinterCap->Supports(PrinterCap::kHalftone);
456 }
457
458
459 void
CreateHalftoneConfigurationUI()460 JobSetupView::CreateHalftoneConfigurationUI()
461 {
462 // dither type
463 fDitherType = new BPopUpMenu("");
464 fDitherType->SetRadioMode(true);
465 FillCapabilityMenu(fDitherType, kMsgQuality, gDitherTypes,
466 sizeof(gDitherTypes) / sizeof(gDitherTypes[0]),
467 (int)fJobData->GetDitherType());
468 fDitherMenuField = new BMenuField("dithering", "Dot Pattern:",
469 fDitherType);
470 fDitherType->SetTargetForItems(this);
471
472 // halftone preview view
473 fHalftoneBox = new BBox("halftoneBox");
474 fHalftoneBox->SetBorder(B_PLAIN_BORDER);
475
476 // TODO make layout compatible
477 BSize size(240, 14 * 4);
478 BRect rect(0, 0, size.width, size.height);
479 fHalftone = new HalftoneView(rect, "halftone",
480 B_FOLLOW_ALL, B_WILL_DRAW);
481 fHalftone->SetExplicitMinSize(size);
482 fHalftone->SetExplicitMaxSize(size);
483
484 // gamma
485 fGamma = new JSDSlider("gamma", "Gamma", new BMessage(kMsgQuality),
486 -300, 300);
487
488 fGamma->SetLimitLabels("Lighter", "Darker");
489 fGamma->SetValue((int32)(100 * log(fJobData->GetGamma()) / log(2.0)));
490 fGamma->SetHashMarks(B_HASH_MARKS_BOTH);
491 fGamma->SetHashMarkCount(7);
492 fGamma->SetModificationMessage(new BMessage(kMsgQuality));
493 fGamma->SetTarget(this);
494
495 // ink density
496 fInkDensity = new JSDSlider("inkDensity", "Ink usage",
497 new BMessage(kMsgQuality), 0, 127);
498
499 fInkDensity->SetLimitLabels("Min", "Max");
500 fInkDensity->SetValue((int32)fJobData->GetInkDensity());
501 fInkDensity->SetHashMarks(B_HASH_MARKS_BOTH);
502 fInkDensity->SetHashMarkCount(10);
503 fInkDensity->SetModificationMessage(new BMessage(kMsgQuality));
504 fInkDensity->SetTarget(this);
505 }
506
507
508 void
AddDriverSpecificSettings(BGridLayout * gridLayout,int row)509 JobSetupView::AddDriverSpecificSettings(BGridLayout* gridLayout, int row)
510 {
511 if (!fPrinterCap->Supports(PrinterCap::kDriverSpecificCapabilities))
512 return;
513
514 int count = fPrinterCap->CountCap(PrinterCap::kDriverSpecificCapabilities);
515 const BaseCap** capabilities = fPrinterCap->GetCaps(
516 PrinterCap::kDriverSpecificCapabilities);
517
518 for (int i = 0; i < count; i ++) {
519 const DriverSpecificCap* capability =
520 static_cast<const DriverSpecificCap*>(capabilities[i]);
521
522 switch (capability->fType) {
523 case DriverSpecificCap::kList:
524 AddPopUpMenu(capability, gridLayout, row);
525 break;
526 case DriverSpecificCap::kBoolean:
527 AddCheckBox(capability, gridLayout, row);
528 break;
529 case DriverSpecificCap::kIntRange:
530 case DriverSpecificCap::kIntDimension:
531 AddIntSlider(capability, gridLayout, row);
532 break;
533 case DriverSpecificCap::kDoubleRange:
534 AddDoubleSlider(capability, gridLayout, row);
535 break;
536
537 }
538 }
539 }
540
541
542 void
AddPopUpMenu(const DriverSpecificCap * capability,BGridLayout * gridLayout,int & row)543 JobSetupView::AddPopUpMenu(const DriverSpecificCap* capability,
544 BGridLayout* gridLayout, int& row)
545 {
546 const char* label = capability->fLabel.c_str();
547 BPopUpMenu* popUpMenu = new BPopUpMenu(label);
548 popUpMenu->SetRadioMode(true);
549
550 PrinterCap::CapID category = static_cast<PrinterCap::CapID>(
551 capability->ID());
552
553 const BaseCap** categoryCapabilities = fPrinterCap->GetCaps(category);
554
555 int categoryCount = fPrinterCap->CountCap(category);
556
557 string value = GetDriverSpecificValue(category, capability->Key());
558 PrinterCap::KeyPredicate predicate(value.c_str());
559
560 FillCapabilityMenu(popUpMenu, kMsgNone, categoryCapabilities,
561 categoryCount, predicate);
562
563 BString menuLabel = label;
564 menuLabel << ":";
565 BMenuField* menuField = new BMenuField(label, menuLabel.String(),
566 popUpMenu);
567 popUpMenu->SetTargetForItems(this);
568
569 gridLayout->AddItem(menuField->CreateLabelLayoutItem(),
570 0, row);
571 gridLayout->AddItem(menuField->CreateMenuBarLayoutItem(),
572 1, row);
573 row ++;
574
575 fDriverSpecificPopUpMenus[category] = popUpMenu;
576 }
577
578
579 void
AddCheckBox(const DriverSpecificCap * capability,BGridLayout * gridLayout,int & row)580 JobSetupView::AddCheckBox(const DriverSpecificCap* capability,
581 BGridLayout* gridLayout, int& row)
582 {
583 PrinterCap::CapID category = static_cast<PrinterCap::CapID>(
584 capability->ID());
585 const BooleanCap* booleanCap = fPrinterCap->FindBooleanCap(category);
586 if (booleanCap == NULL) {
587 fprintf(stderr, "Internal error: BooleanCap for '%s' not found!\n",
588 capability->Label());
589 return;
590 }
591
592 const char* key = capability->Key();
593 BString name;
594 name << "pds_" << key;
595 BCheckBox* checkBox = new BCheckBox(name.String(), capability->Label(),
596 NULL);
597
598 bool value = booleanCap->DefaultValue();
599 if (fJobData->Settings().HasBoolean(key))
600 value = fJobData->Settings().GetBoolean(key);
601 if (value)
602 checkBox->SetValue(B_CONTROL_ON);
603
604 gridLayout->AddView(checkBox, 0, row, 2);
605 row ++;
606
607 fDriverSpecificCheckBoxes[capability->Key()] = checkBox;
608 }
609
610
611 void
AddIntSlider(const DriverSpecificCap * capability,BGridLayout * gridLayout,int & row)612 JobSetupView::AddIntSlider(const DriverSpecificCap* capability,
613 BGridLayout* gridLayout, int& row)
614 {
615 PrinterCap::CapID category = static_cast<PrinterCap::CapID>(
616 capability->ID());
617 const IntRangeCap* range = fPrinterCap->FindIntRangeCap(category);
618 if (range == NULL) {
619 fprintf(stderr, "Internal error: IntRangeCap for '%s' not found!\n",
620 capability->Label());
621 return;
622 }
623
624 const char* label = capability->Label();
625 const char* key = capability->Key();
626 BString name;
627 name << "pds_" << key;
628 BMessage* message = new BMessage(kMsgIntSliderChanged);
629 message->AddInt32(kCategoryID, category);
630 BSlider* slider = new BSlider(name.String(), label,
631 message, 0, 1000, B_HORIZONTAL);
632 slider->SetModificationMessage(new BMessage(*message));
633 slider->SetTarget(this);
634
635 int32 value = range->DefaultValue();
636 if (fJobData->Settings().HasInt(key))
637 value = fJobData->Settings().GetInt(key);
638 float position = (value - range->Lower()) /
639 (range->Upper() - range->Lower());
640 slider->SetPosition(position);
641
642 gridLayout->AddView(slider, 0, row, 2);
643 row ++;
644
645 IntRange intRange(label, key, range, slider);
646 fDriverSpecificIntSliders[category] = intRange;
647 intRange.UpdateLabel();
648 }
649
650
651 void
AddDoubleSlider(const DriverSpecificCap * capability,BGridLayout * gridLayout,int & row)652 JobSetupView::AddDoubleSlider(const DriverSpecificCap* capability,
653 BGridLayout* gridLayout, int& row)
654 {
655 PrinterCap::CapID category = static_cast<PrinterCap::CapID>(
656 capability->ID());
657 const DoubleRangeCap* range = fPrinterCap->FindDoubleRangeCap(category);
658 if (range == NULL) {
659 fprintf(stderr, "Internal error: DoubleRangeCap for '%s' not found!\n",
660 capability->Label());
661 return;
662 }
663
664 const char* label = capability->Label();
665 const char* key = capability->Key();
666 BString name;
667 name << "pds_" << key;
668 BMessage* message = new BMessage(kMsgDoubleSliderChanged);
669 message->AddInt32(kCategoryID, category);
670 BSlider* slider = new BSlider(name.String(), label,
671 message, 0, 1000, B_HORIZONTAL);
672 slider->SetModificationMessage(new BMessage(*message));
673 slider->SetTarget(this);
674
675 double value = range->DefaultValue();
676 if (fJobData->Settings().HasDouble(key))
677 value = fJobData->Settings().GetDouble(key);
678 float position = static_cast<float>((value - range->Lower()) /
679 (range->Upper() - range->Lower()));
680 slider->SetPosition(position);
681
682 gridLayout->AddView(slider, 0, row, 2);
683 row ++;
684
685 DoubleRange doubleRange(label, key, range, slider);
686 fDriverSpecificDoubleSliders[category] = doubleRange;
687 doubleRange.UpdateLabel();
688 }
689
690
691 string
GetDriverSpecificValue(PrinterCap::CapID category,const char * key)692 JobSetupView::GetDriverSpecificValue(PrinterCap::CapID category,
693 const char* key)
694 {
695 if (fJobData->Settings().HasString(key))
696 return fJobData->Settings().GetString(key);
697
698 const EnumCap* defaultCapability = fPrinterCap->GetDefaultCap(category);
699 return defaultCapability->fKey;
700 }
701
702
703 template<typename Predicate>
704 void
FillCapabilityMenu(BPopUpMenu * menu,uint32 message,const BaseCap ** capabilities,int count,Predicate & predicate)705 JobSetupView::FillCapabilityMenu(BPopUpMenu* menu, uint32 message,
706 const BaseCap** capabilities, int count, Predicate& predicate)
707 {
708 bool marked = false;
709
710 BMenuItem* firstItem = NULL;
711 BMenuItem* defaultItem = NULL;
712 BMenuItem* item = NULL;
713 while (count--) {
714 const EnumCap* capability = dynamic_cast<const EnumCap*>(*capabilities);
715 if (message != kMsgNone)
716 item = new BMenuItem(capability->fLabel.c_str(),
717 new BMessage(message));
718 else
719 item = new BMenuItem(capability->fLabel.c_str(), NULL);
720
721 menu->AddItem(item);
722
723 if (firstItem == NULL)
724 firstItem = item;
725
726 if (capability->fIsDefault)
727 defaultItem = item;
728
729
730 if (predicate(capability)) {
731 item->SetMarked(true);
732 marked = true;
733 }
734
735 capabilities++;
736 }
737
738 if (marked)
739 return;
740
741 if (defaultItem != NULL)
742 defaultItem->SetMarked(true);
743 else if (firstItem != NULL)
744 firstItem->SetMarked(true);
745 }
746
747
748 void
FillCapabilityMenu(BPopUpMenu * menu,uint32 message,PrinterCap::CapID category,int id)749 JobSetupView::FillCapabilityMenu(BPopUpMenu* menu, uint32 message,
750 PrinterCap::CapID category, int id)
751 {
752 PrinterCap::IDPredicate predicate(id);
753 int count = fPrinterCap->CountCap(category);
754 const BaseCap **capabilities = fPrinterCap->GetCaps(category);
755 FillCapabilityMenu(menu, message, capabilities, count, predicate);
756 }
757
758
759 void
FillCapabilityMenu(BPopUpMenu * menu,uint32 message,const BaseCap ** capabilities,int count,int id)760 JobSetupView::FillCapabilityMenu(BPopUpMenu* menu, uint32 message,
761 const BaseCap** capabilities, int count, int id)
762 {
763 PrinterCap::IDPredicate predicate(id);
764 FillCapabilityMenu(menu, message, capabilities, count, predicate);
765 }
766
767
768 int
GetID(const BaseCap ** capabilities,int count,const char * label,int defaultValue)769 JobSetupView::GetID(const BaseCap** capabilities, int count, const char* label,
770 int defaultValue)
771 {
772 while (count--) {
773 const EnumCap* capability =
774 dynamic_cast<const EnumCap*>(*capabilities);
775 if (capability == NULL)
776 break;
777
778 if (capability->fLabel == label)
779 return capability->ID();
780 }
781 return defaultValue;
782 }
783
784
785 void
UpdateButtonEnabledState()786 JobSetupView::UpdateButtonEnabledState()
787 {
788 bool pageRangeEnabled = fAll->Value() != B_CONTROL_ON;
789 fFromPage->SetEnabled(pageRangeEnabled);
790 fToPage->SetEnabled(pageRangeEnabled);
791
792 bool pageSelectionEnabled = fDuplex == NULL ||
793 fDuplex->Value() != B_CONTROL_ON;
794 fAllPages->SetEnabled(pageSelectionEnabled);
795 fOddNumberedPages->SetEnabled(pageSelectionEnabled);
796 fEvenNumberedPages->SetEnabled(pageSelectionEnabled);
797 }
798
799
800 void
MessageReceived(BMessage * message)801 JobSetupView::MessageReceived(BMessage* message)
802 {
803 switch (message->what) {
804 case kMsgRangeAll:
805 case kMsgRangeSelection:
806 case kMsgDuplexChanged:
807 UpdateButtonEnabledState();
808 break;
809
810 case kMsgQuality:
811 UpdateHalftonePreview();
812 break;
813
814 case kMsgCollateChanged:
815 fPages->SetCollate(fCollate->Value() == B_CONTROL_ON);
816 break;
817
818 case kMsgReverseChanged:
819 fPages->SetReverse(fReverse->Value() == B_CONTROL_ON);
820 break;
821
822 case kMsgIntSliderChanged:
823 UpdateIntSlider(message);
824 break;
825
826 case kMsgDoubleSliderChanged:
827 UpdateDoubleSlider(message);
828 break;
829 }
830 }
831
832
833 void
UpdateHalftonePreview()834 JobSetupView::UpdateHalftonePreview()
835 {
836 if (!IsHalftoneConfigurationNeeded())
837 return;
838
839 fHalftone->Preview(Gamma(), InkDensity(), DitherType(),
840 Color() != JobData::kMonochrome);
841 }
842
843
844 void
UpdateIntSlider(BMessage * message)845 JobSetupView::UpdateIntSlider(BMessage* message)
846 {
847 int32 id;
848 if (message->FindInt32(kCategoryID, &id) != B_OK)
849 return;
850 PrinterCap::CapID capID = static_cast<PrinterCap::CapID>(id);
851 fDriverSpecificIntSliders[capID].UpdateLabel();
852 }
853
854
855 void
UpdateDoubleSlider(BMessage * message)856 JobSetupView::UpdateDoubleSlider(BMessage* message)
857 {
858 int32 id;
859 if (message->FindInt32(kCategoryID, &id) != B_OK)
860 return;
861 PrinterCap::CapID capID = static_cast<PrinterCap::CapID>(id);
862 fDriverSpecificDoubleSliders[capID].UpdateLabel();
863 }
864
865
866 JobData::Color
Color()867 JobSetupView::Color()
868 {
869 const char *label = fColorType->FindMarked()->Label();
870 const BaseCap* capability = fPrinterCap->FindCap(PrinterCap::kColor, label);
871 if (capability == NULL)
872 return JobData::kMonochrome;
873
874 const ColorCap* colorCap = static_cast<const ColorCap*>(capability);
875 return colorCap->fColor;
876 }
877
878
879 Halftone::DitherType
DitherType()880 JobSetupView::DitherType()
881 {
882 const char *label = fDitherType->FindMarked()->Label();
883 int id = GetID(gDitherTypes, sizeof(gDitherTypes) / sizeof(gDitherTypes[0]),
884 label, Halftone::kTypeFloydSteinberg);
885 return static_cast<Halftone::DitherType>(id);
886 }
887
888 float
Gamma()889 JobSetupView::Gamma()
890 {
891 const float value = (float)fGamma->Value();
892 return pow(2.0, value / 100.0);
893 }
894
895
896 float
InkDensity()897 JobSetupView::InkDensity()
898 {
899 const float value = (float)(127 - fInkDensity->Value());
900 return value;
901 }
902
903
904 JobData::PaperSource
PaperSource()905 JobSetupView::PaperSource()
906 {
907 const char *label = fPaperFeed->FindMarked()->Label();
908 const BaseCap* capability = fPrinterCap->FindCap(PrinterCap::kPaperSource,
909 label);
910
911 if (capability == NULL)
912 capability = fPrinterCap->GetDefaultCap(PrinterCap::kPaperSource);
913 return static_cast<const PaperSourceCap*>(capability)->fPaperSource;
914
915 }
916
917 bool
UpdateJobData()918 JobSetupView::UpdateJobData()
919 {
920 fJobData->SetShowPreview(fPreview->Value() == B_CONTROL_ON);
921 fJobData->SetColor(Color());
922 if (IsHalftoneConfigurationNeeded()) {
923 fJobData->SetGamma(Gamma());
924 fJobData->SetInkDensity(InkDensity());
925 fJobData->SetDitherType(DitherType());
926 }
927
928 int first_page;
929 int last_page;
930
931 if (B_CONTROL_ON == fAll->Value()) {
932 first_page = 1;
933 last_page = -1;
934 } else {
935 first_page = atoi(fFromPage->Text());
936 last_page = atoi(fToPage->Text());
937 }
938
939 fJobData->SetFirstPage(first_page);
940 fJobData->SetLastPage(last_page);
941
942 fJobData->SetPaperSource(PaperSource());
943
944 fJobData->SetNup(GetID(gNups, sizeof(gNups) / sizeof(gNups[0]),
945 fNup->FindMarked()->Label(), 1));
946
947 if (fPrinterCap->Supports(PrinterCap::kPrintStyle)) {
948 fJobData->SetPrintStyle((B_CONTROL_ON == fDuplex->Value())
949 ? JobData::kDuplex : JobData::kSimplex);
950 }
951
952 fJobData->SetCopies(atoi(fCopies->Text()));
953
954 fJobData->SetCollate(B_CONTROL_ON == fCollate->Value());
955 fJobData->SetReverse(B_CONTROL_ON == fReverse->Value());
956
957 JobData::PageSelection pageSelection = JobData::kAllPages;
958 if (fOddNumberedPages->Value() == B_CONTROL_ON)
959 pageSelection = JobData::kOddNumberedPages;
960 if (fEvenNumberedPages->Value() == B_CONTROL_ON)
961 pageSelection = JobData::kEvenNumberedPages;
962 fJobData->SetPageSelection(pageSelection);
963
964 {
965 std::map<PrinterCap::CapID, BPopUpMenu*>::iterator it =
966 fDriverSpecificPopUpMenus.begin();
967 for(; it != fDriverSpecificPopUpMenus.end(); it++) {
968 PrinterCap::CapID category = it->first;
969 BPopUpMenu* popUpMenu = it->second;
970 const char* key = fPrinterCap->FindCap(
971 PrinterCap::kDriverSpecificCapabilities, (int)category)->Key();
972 const char* label = popUpMenu->FindMarked()->Label();
973 const char* value = static_cast<const EnumCap*>(fPrinterCap->
974 FindCap(category, label))->Key();
975 fJobData->Settings().SetString(key, value);
976 }
977 }
978
979 {
980 std::map<string, BCheckBox*>::iterator it =
981 fDriverSpecificCheckBoxes.begin();
982 for(; it != fDriverSpecificCheckBoxes.end(); it++) {
983 const char* key = it->first.c_str();
984 BCheckBox* checkBox = it->second;
985 bool value = checkBox->Value() == B_CONTROL_ON;
986 fJobData->Settings().SetBoolean(key, value);
987 }
988 }
989
990 {
991 std::map<PrinterCap::CapID, IntRange>::iterator it =
992 fDriverSpecificIntSliders.begin();
993 for(; it != fDriverSpecificIntSliders.end(); it++) {
994 IntRange& range = it->second;
995 fJobData->Settings().SetInt(range.Key(), range.Value());
996 }
997 }
998
999 {
1000 std::map<PrinterCap::CapID, DoubleRange>::iterator it =
1001 fDriverSpecificDoubleSliders.begin();
1002 for(; it != fDriverSpecificDoubleSliders.end(); it++) {
1003 DoubleRange& range = it->second;
1004 fJobData->Settings().SetDouble(range.Key(), range.Value());
1005 }
1006 }
1007
1008 fJobData->Save();
1009 return true;
1010 }
1011
1012
JobSetupDlg(JobData * jobData,PrinterData * printerData,const PrinterCap * printerCap)1013 JobSetupDlg::JobSetupDlg(JobData* jobData, PrinterData* printerData,
1014 const PrinterCap* printerCap)
1015 :
1016 DialogWindow(BRect(100, 100, 200, 200), "Print job setup",
1017 B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
1018 B_NOT_RESIZABLE | B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE
1019 | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS
1020 | B_CLOSE_ON_ESCAPE)
1021 {
1022 SetResult(B_ERROR);
1023 AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
1024
1025 fJobSetup = new JobSetupView(jobData, printerData, printerCap);
1026 SetLayout(new BGroupLayout(B_VERTICAL));
1027 AddChild(BGroupLayoutBuilder(B_VERTICAL, 0)
1028 .Add(fJobSetup)
1029 .SetInsets(10, 10, 10, 10)
1030 );
1031 }
1032
1033
1034 void
MessageReceived(BMessage * message)1035 JobSetupDlg::MessageReceived(BMessage* message)
1036 {
1037 switch (message->what) {
1038 case kMsgOK:
1039 fJobSetup->UpdateJobData();
1040 SetResult(B_NO_ERROR);
1041 PostMessage(B_QUIT_REQUESTED);
1042 break;
1043
1044 case kMsgCancel:
1045 PostMessage(B_QUIT_REQUESTED);
1046 break;
1047
1048 default:
1049 DialogWindow::MessageReceived(message);
1050 break;
1051 }
1052 }
1053