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