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