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 { 54 NupCap(const string &label, bool isDefault, int nup) 55 : 56 EnumCap(label, isDefault), 57 fNup(nup) 58 {} 59 60 int32 ID() const { return fNup; } 61 62 int fNup; 63 }; 64 65 66 struct DitherCap : public EnumCap { 67 DitherCap(const string &label, bool isDefault, 68 Halftone::DitherType ditherType) 69 : 70 EnumCap(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 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 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* 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 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 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 453 JobSetupView::IsHalftoneConfigurationNeeded() 454 { 455 return fPrinterCap->Supports(PrinterCap::kHalftone); 456 } 457 458 459 void 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 889 JobSetupView::Gamma() 890 { 891 const float value = (float)fGamma->Value(); 892 return pow(2.0, value / 100.0); 893 } 894 895 896 float 897 JobSetupView::InkDensity() 898 { 899 const float value = (float)(127 - fInkDensity->Value()); 900 return value; 901 } 902 903 904 JobData::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 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 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 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