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