1 /* 2 * Copyright 2002-2011, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Pfeiffer 7 */ 8 9 10 #include "ConfigWindow.h" 11 12 #include <limits.h> 13 #include <math.h> 14 #include <stdlib.h> 15 #include <string.h> 16 17 #include <Application.h> 18 #include <Autolock.h> 19 #include <Catalog.h> 20 #include <Debug.h> 21 #include <GroupLayout.h> 22 #include <GroupLayoutBuilder.h> 23 #include <Layout.h> 24 #include <Locale.h> 25 #include <Window.h> 26 27 #include "pr_server.h" 28 #include "Printer.h" 29 #include "PrintServerApp.h" 30 #include "PrintUtils.h" 31 32 33 #undef B_TRANSLATION_CONTEXT 34 #define B_TRANSLATION_CONTEXT "ConfigWindow" 35 36 37 static const float a0_width = 2380.0; 38 static const float a0_height = 3368.0; 39 static const float a1_width = 1684.0; 40 static const float a1_height = 2380.0; 41 static const float a2_width = 1190.0; 42 static const float a2_height = 1684.0; 43 static const float a3_width = 842.0; 44 static const float a3_height = 1190.0; 45 static const float a4_width = 595.0; 46 static const float a4_height = 842.0; 47 static const float a5_width = 421.0; 48 static const float a5_height = 595.0; 49 static const float a6_width = 297.0; 50 static const float a6_height = 421.0; 51 static const float b5_width = 501.0; 52 static const float b5_height = 709.0; 53 static const float letter_width = 612.0; 54 static const float letter_height = 792.0; 55 static const float legal_width = 612.0; 56 static const float legal_height = 1008.0; 57 static const float ledger_width = 1224.0; 58 static const float ledger_height = 792.0; 59 static const float tabloid_width = 792.0; 60 static const float tabloid_height = 1224.0; 61 static const float jis_b5_width = 516.0; 62 static const float jis_b5_height = 729.0; 63 64 65 static struct PageFormat 66 { 67 const char *label; 68 float width; 69 float height; 70 } pageFormat[] = 71 { 72 {B_TRANSLATE_MARK_COMMENT("Letter", "ANSI A (letter), a North American " 73 "paper size"), letter_width, letter_height }, 74 {B_TRANSLATE_MARK_COMMENT("Legal", "A North American paper size (216 x 356" 75 " mm, or 8.5 x 14 in)"), legal_width, legal_height }, 76 {B_TRANSLATE_MARK_COMMENT("Ledger", "ANSI B (ledger), a North American " 77 "paper size"), ledger_width, ledger_height }, 78 {B_TRANSLATE_MARK_COMMENT("Tabloid", "ANSI B (tabloid), a North American " 79 "paper size"), tabloid_width, tabloid_height }, 80 {B_TRANSLATE_MARK_COMMENT("A0", "ISO 216 paper size"), 81 a0_width, a0_height }, 82 {B_TRANSLATE_MARK_COMMENT("A1", "ISO 216 paper size"), 83 a1_width, a1_height }, 84 {B_TRANSLATE_MARK_COMMENT("A2", "ISO 216 paper size"), 85 a2_width, a2_height }, 86 {B_TRANSLATE_MARK_COMMENT("A3", "ISO 216 paper size"), 87 a3_width, a3_height }, 88 {B_TRANSLATE_MARK_COMMENT("A4", "ISO 216 paper size"), 89 a4_width, a4_height }, 90 {B_TRANSLATE_MARK_COMMENT("A5", "ISO 216 paper size"), 91 a5_width, a5_height }, 92 {B_TRANSLATE_MARK_COMMENT("A6", "ISO 216 paper size"), 93 a6_width, a6_height }, 94 {B_TRANSLATE_MARK_COMMENT("B5", "ISO 216 paper size"), 95 b5_width, b5_height }, 96 {B_TRANSLATE_MARK_COMMENT("B5 (JIS)", "JIS P0138 B5, a Japanese " 97 "paper size"), jis_b5_width, jis_b5_height }, 98 }; 99 100 101 static void 102 GetPageFormat(float w, float h, BString& label) 103 { 104 w = floor(w + 0.5); h = floor(h + 0.5); 105 for (uint i = 0; i < sizeof(pageFormat) / sizeof(struct PageFormat); i ++) { 106 struct PageFormat& pf = pageFormat[i]; 107 if ((pf.width == w && pf.height == h) || (pf.width == h 108 && pf.height == w)) { 109 label = B_TRANSLATE_NOCOLLECT(pf.label); 110 return; 111 } 112 } 113 114 float unit = 72.0; // currently inches only 115 label << (w / unit) << "x" << (h / unit) << " in."; 116 } 117 118 119 static BGroupLayoutBuilder 120 LeftAlign(BView* view) 121 { 122 return BGroupLayoutBuilder(B_HORIZONTAL) 123 .Add(view) 124 .AddGlue() 125 .SetInsets(0, 0, 0, 0); 126 } 127 128 129 ConfigWindow::ConfigWindow(config_setup_kind kind, Printer* defaultPrinter, 130 BMessage* settings, AutoReply* sender) 131 : 132 BWindow(ConfigWindow::GetWindowFrame(), 133 B_TRANSLATE("Page setup"), 134 B_TITLED_WINDOW, 135 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS 136 | B_CLOSE_ON_ESCAPE), 137 fKind(kind), 138 fDefaultPrinter(defaultPrinter), 139 fSettings(settings), 140 fSender(sender), 141 fCurrentPrinter(NULL), 142 fPageFormatText(NULL), 143 fJobSetupText(NULL) 144 { 145 MimeTypeForSender(settings, fSenderMimeType); 146 PrinterForMimeType(); 147 148 if (kind == kJobSetup) 149 SetTitle(B_TRANSLATE("Print setup")); 150 151 BView* panel = new BBox(Bounds(), "temporary", B_FOLLOW_ALL, B_WILL_DRAW); 152 AddChild(panel); 153 154 BRect dummyRect(0, 0, 1, 1); 155 156 // print selection pop up menu 157 BPopUpMenu* menu = new BPopUpMenu(B_TRANSLATE("Select a printer")); 158 SetupPrintersMenu(menu); 159 160 fPrinters = new BMenuField(B_TRANSLATE("Printer:"), menu); 161 162 // page format button 163 fPageSetup = AddPictureButton(panel, dummyRect, "Paper setup", 164 "PAGE_SETUP_ON", "PAGE_SETUP_OFF", MSG_PAGE_SETUP); 165 166 // add description to button 167 BStringView *pageFormatTitle = new BStringView("paperSetupTitle", 168 B_TRANSLATE("Paper setup:")); 169 fPageFormatText = new BStringView("paperSetupText", ""); 170 171 // page selection button 172 fJobSetup = NULL; 173 BStringView* jobSetupTitle = NULL; 174 if (kind == kJobSetup) { 175 fJobSetup = AddPictureButton(panel, dummyRect, "Page setup", 176 "JOB_SETUP_ON", "JOB_SETUP_OFF", MSG_JOB_SETUP); 177 // add description to button 178 jobSetupTitle = new BStringView("jobSetupTitle", 179 B_TRANSLATE("Page setup:")); 180 fJobSetupText = new BStringView("jobSetupText", ""); 181 } 182 183 // separator line 184 BBox* separator = new BBox("separator"); 185 separator->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1)); 186 187 // Cancel & OK button 188 BButton* cancel = new BButton(dummyRect, "Cancel", B_TRANSLATE("Cancel"), 189 new BMessage(B_QUIT_REQUESTED)); 190 fOk = new BButton(dummyRect, "OK", B_TRANSLATE("OK"), 191 new BMessage(MSG_OK)); 192 193 RemoveChild(panel); 194 195 SetLayout(new BGroupLayout(B_VERTICAL)); 196 BGroupLayoutBuilder builder(B_VERTICAL); 197 198 builder 199 .Add(fPrinters) 200 .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10) 201 .Add(fPageSetup) 202 .Add(BGroupLayoutBuilder(B_VERTICAL, 0) 203 .Add(LeftAlign(pageFormatTitle)) 204 .Add(LeftAlign(fPageFormatText)) 205 .SetInsets(0, 0, 0, 0) 206 ) 207 .AddGlue() 208 ); 209 210 if (fJobSetup != NULL) { 211 builder 212 .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10) 213 .Add(fJobSetup) 214 .Add(BGroupLayoutBuilder(B_VERTICAL, 0) 215 .Add(LeftAlign(jobSetupTitle)) 216 .Add(LeftAlign(fJobSetupText)) 217 .SetInsets(0, 0, 0, 0) 218 ) 219 .AddGlue() 220 ); 221 } 222 223 builder 224 .AddGlue() 225 .Add(separator) 226 .Add(BGroupLayoutBuilder(B_HORIZONTAL) 227 .AddGlue() 228 .Add(cancel) 229 .Add(fOk) 230 ) 231 .SetInsets(5, 5, 5, 5); 232 233 AddChild(builder); 234 235 AddShortcut('a', 0, new BMessage(B_ABOUT_REQUESTED)); 236 237 SetDefaultButton(fOk); 238 239 fPrinters->MakeFocus(true); 240 241 UpdateSettings(true); 242 } 243 244 245 ConfigWindow::~ConfigWindow() 246 { 247 if (fCurrentPrinter) 248 fCurrentPrinter->Release(); 249 release_sem(fFinished); 250 } 251 252 253 void 254 ConfigWindow::Go() 255 { 256 sem_id sid = create_sem(0, "finished"); 257 if (sid >= 0) { 258 fFinished = sid; 259 Show(); 260 acquire_sem(sid); 261 delete_sem(sid); 262 } else { 263 Quit(); 264 } 265 } 266 267 268 void 269 ConfigWindow::MessageReceived(BMessage* m) 270 { 271 switch (m->what) { 272 case MSG_PAGE_SETUP: 273 Setup(kPageSetup); 274 break; 275 case MSG_JOB_SETUP: 276 Setup(kJobSetup); 277 break; 278 case MSG_PRINTER_SELECTED: 279 { 280 BString printer; 281 if (m->FindString("name", &printer) == B_OK) { 282 UpdateAppSettings(fSenderMimeType.String(), printer.String()); 283 PrinterForMimeType(); 284 UpdateSettings(true); 285 } 286 break; 287 } 288 case MSG_OK: 289 UpdateSettings(false); 290 if (fKind == kPageSetup) 291 fSender->SetReply(&fPageSettings); 292 else 293 fSender->SetReply(&fJobSettings); 294 Quit(); 295 break; 296 case B_ABOUT_REQUESTED: AboutRequested(); 297 break; 298 default: 299 BWindow::MessageReceived(m); 300 } 301 } 302 303 304 void 305 ConfigWindow::AboutRequested() 306 { 307 BString text = B_TRANSLATE("Printer server"); 308 text << "\n" 309 "© 2001-2010 Haiku, Inc.\n" 310 "\n" 311 "\tIthamar R. Adema\n" 312 "\tMichael Pfeiffer\n"; 313 314 BAlert *about = new BAlert("About printer server", text.String(), 315 B_TRANSLATE("OK")); 316 about->SetFlags(about->Flags() | B_CLOSE_ON_ESCAPE); 317 about->Go(); 318 } 319 320 321 void 322 ConfigWindow::FrameMoved(BPoint p) 323 { 324 BRect frame = GetWindowFrame(); 325 frame.OffsetTo(p); 326 SetWindowFrame(frame); 327 } 328 329 330 BRect 331 ConfigWindow::GetWindowFrame() 332 { 333 BRect frame(0, 0, 10, 10); 334 BAutolock lock(gLock); 335 if (lock.IsLocked()) 336 frame.OffsetBy(Settings::GetSettings()->ConfigWindowFrame().LeftTop()); 337 338 frame.OffsetBy(30, 30); 339 return frame; 340 } 341 342 343 void 344 ConfigWindow::SetWindowFrame(BRect r) 345 { 346 BAutolock lock(gLock); 347 if (lock.IsLocked()) 348 Settings::GetSettings()->SetConfigWindowFrame(r); 349 } 350 351 352 BPictureButton* 353 ConfigWindow::AddPictureButton(BView* panel, BRect frame, 354 const char* name, const char* on, const char* off, uint32 what) 355 { 356 BBitmap* onBM = LoadBitmap(on); 357 BBitmap* offBM = LoadBitmap(off); 358 359 BPicture* onPict = BitmapToPicture(panel, onBM); 360 BPicture* offPict = BitmapToPicture(panel, offBM); 361 362 BPictureButton* button = NULL; 363 364 if (onPict != NULL && offPict != NULL) { 365 button = new BPictureButton(frame, name, onPict, offPict, 366 new BMessage(what)); 367 button->SetViewColor(B_TRANSPARENT_COLOR); 368 panel->AddChild(button); 369 onBM->Lock(); 370 int32 width = (int32)onBM->Bounds().Width(); 371 int32 height = (int32)onBM->Bounds().Height(); 372 button->ResizeTo(width, height); 373 button->SetExplicitMaxSize(BSize(width, height)); 374 onBM->Unlock(); 375 panel->RemoveChild(button); 376 377 BPicture* disabled = BitmapToGrayedPicture(panel, offBM); 378 button->SetDisabledOn(disabled); 379 delete disabled; 380 381 disabled = BitmapToGrayedPicture(panel, onBM); 382 button->SetDisabledOff(disabled); 383 delete disabled; 384 } 385 386 delete onPict; delete offPict; 387 delete onBM; delete offBM; 388 389 return button; 390 } 391 392 393 void 394 ConfigWindow::PrinterForMimeType() 395 { 396 BAutolock lock(gLock); 397 if (fCurrentPrinter) { 398 fCurrentPrinter->Release(); 399 fCurrentPrinter = NULL; 400 } 401 402 if (lock.IsLocked()) { 403 Settings* s = Settings::GetSettings(); 404 AppSettings* app = s->FindAppSettings(fSenderMimeType.String()); 405 if (app) 406 fPrinterName = app->GetPrinter(); 407 else 408 fPrinterName = fDefaultPrinter ? fDefaultPrinter->Name() : ""; 409 fCurrentPrinter = Printer::Find(fPrinterName); 410 if (fCurrentPrinter) 411 fCurrentPrinter->Acquire(); 412 } 413 } 414 415 416 void 417 ConfigWindow::SetupPrintersMenu(BMenu* menu) 418 { 419 // clear menu 420 while (menu->CountItems() != 0) 421 delete menu->RemoveItem((int32)0); 422 423 // fill menu with printer names 424 BAutolock lock(gLock); 425 if (lock.IsLocked()) { 426 BString n; 427 BMessage* m; 428 BMenuItem* item; 429 for (int i = 0; i < Printer::CountPrinters(); i ++) { 430 Printer::At(i)->GetName(n); 431 m = new BMessage(MSG_PRINTER_SELECTED); 432 m->AddString("name", n.String()); 433 menu->AddItem(item = new BMenuItem(n.String(), m)); 434 if (n == fPrinterName) 435 item->SetMarked(true); 436 } 437 } 438 } 439 440 441 void 442 ConfigWindow::UpdateAppSettings(const char* mime, const char* printer) 443 { 444 BAutolock lock(gLock); 445 if (lock.IsLocked()) { 446 Settings* s = Settings::GetSettings(); 447 AppSettings* app = s->FindAppSettings(mime); 448 if (app) 449 app->SetPrinter(printer); 450 else 451 s->AddAppSettings(new AppSettings(mime, printer)); 452 } 453 } 454 455 456 void 457 ConfigWindow::UpdateSettings(bool read) 458 { 459 BAutolock lock(gLock); 460 if (lock.IsLocked()) { 461 Settings* s = Settings::GetSettings(); 462 PrinterSettings* p = s->FindPrinterSettings(fPrinterName.String()); 463 if (p == NULL) { 464 p = new PrinterSettings(fPrinterName.String()); 465 s->AddPrinterSettings(p); 466 } 467 ASSERT(p != NULL); 468 if (read) { 469 fPageSettings = *p->GetPageSettings(); 470 fJobSettings = *p->GetJobSettings(); 471 } else { 472 p->SetPageSettings(&fPageSettings); 473 p->SetJobSettings(&fJobSettings); 474 } 475 } 476 UpdateUI(); 477 } 478 479 480 void 481 ConfigWindow::UpdateUI() 482 { 483 if (fCurrentPrinter == NULL) { 484 fPageSetup->SetEnabled(false); 485 if (fJobSetup) { 486 fJobSetup->SetEnabled(false); 487 fJobSetupText->SetText(B_TRANSLATE("Undefined")); 488 } 489 fOk->SetEnabled(false); 490 fPageFormatText->SetText(B_TRANSLATE("Undefined")); 491 } else { 492 fPageSetup->SetEnabled(true); 493 494 if (fJobSetup) 495 fJobSetup->SetEnabled(fKind == kJobSetup 496 && !fPageSettings.IsEmpty()); 497 498 fOk->SetEnabled((fKind == kJobSetup && !fJobSettings.IsEmpty()) 499 || (fKind == kPageSetup && !fPageSettings.IsEmpty())); 500 501 // display information about page format 502 BRect paperRect; 503 BString pageFormat; 504 if (fPageSettings.FindRect(PSRV_FIELD_PAPER_RECT, &paperRect) == B_OK) { 505 GetPageFormat(paperRect.Width(), paperRect.Height(), pageFormat); 506 507 int32 orientation = 0; 508 fPageSettings.FindInt32(PSRV_FIELD_ORIENTATION, &orientation); 509 if (orientation == 0) 510 pageFormat << ", " << B_TRANSLATE("Portrait"); 511 else 512 pageFormat << ", " << B_TRANSLATE("Landscape"); 513 } else 514 pageFormat << B_TRANSLATE("Undefined"); 515 516 fPageFormatText->SetText(pageFormat.String()); 517 518 // display information about job 519 if (fKind == kJobSetup) { 520 BString job; 521 int32 first, last, copies; 522 if (fJobSettings.FindInt32(PSRV_FIELD_FIRST_PAGE, &first) == B_OK 523 && fJobSettings.FindInt32(PSRV_FIELD_LAST_PAGE, &last) == 524 B_OK) { 525 526 bool printRange = first >= 1 && first <= last && last != INT_MAX; 527 char number[12]; 528 if (fJobSettings.FindInt32(PSRV_FIELD_COPIES, &copies) 529 == B_OK && copies > 1) { 530 if (printRange) { 531 job = B_TRANSLATE("Page %1 to %2, %3 copies"); 532 snprintf(number, sizeof(number), "%d", (int)first); 533 job.ReplaceFirst("%1", number); 534 snprintf(number, sizeof(number), "%d", (int)last); 535 job.ReplaceFirst("%2", number); 536 snprintf(number, sizeof(number), "%d", (int)copies); 537 job.ReplaceFirst("%3", number); 538 } else { 539 job = B_TRANSLATE("All pages, %1 copies"); 540 snprintf(number, sizeof(number), "%d", (int)copies); 541 job.ReplaceFirst("%1", number); 542 } 543 } else { 544 if (printRange) { 545 job = B_TRANSLATE("Page %1 to %2"); 546 snprintf(number, sizeof(number), "%d", (int)first); 547 job.ReplaceFirst("%1", number); 548 snprintf(number, sizeof(number), "%d", (int)last); 549 job.ReplaceFirst("%2", number); 550 } else 551 job = B_TRANSLATE("All pages"); 552 } 553 } else 554 job << B_TRANSLATE("Undefined"); 555 556 fJobSetupText->SetText(job.String()); 557 } 558 } 559 } 560 561 562 void 563 ConfigWindow::Setup(config_setup_kind kind) 564 { 565 if (fCurrentPrinter) { 566 Hide(); 567 if (kind == kPageSetup) { 568 BMessage settings = fPageSettings; 569 if (fCurrentPrinter->ConfigurePage(settings) == B_OK) { 570 fPageSettings = settings; 571 if (!fJobSettings.IsEmpty()) 572 AddFields(&fJobSettings, &fPageSettings); 573 } 574 } else { 575 BMessage settings; 576 if (fJobSettings.IsEmpty()) settings = fPageSettings; 577 else settings = fJobSettings; 578 579 if (fCurrentPrinter->ConfigureJob(settings) == B_OK) 580 fJobSettings = settings; 581 } 582 UpdateUI(); 583 Show(); 584 } 585 } 586