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