1 /* 2 * Copyright 2001-2010, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Pfeiffer 7 */ 8 9 10 #include "PrinterListView.h" 11 12 #include <Application.h> 13 #include <Bitmap.h> 14 #include <Catalog.h> 15 #include <Directory.h> 16 #include <IconUtils.h> 17 #include <Locale.h> 18 #include <Mime.h> 19 #include <NodeInfo.h> 20 #include <Resources.h> 21 #include <String.h> 22 23 #include "pr_server.h" 24 #include "Messages.h" 25 #include "Globals.h" 26 #include "PrintersWindow.h" 27 #include "SpoolFolder.h" 28 29 30 #undef B_TRANSLATION_CONTEXT 31 #define B_TRANSLATION_CONTEXT "PrinterListView" 32 33 34 // #pragma mark -- PrinterListView 35 36 37 PrinterListView::PrinterListView(BRect frame) 38 : Inherited(frame, "printers_list", B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL, 39 B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE), 40 fFolder(NULL), 41 fActivePrinter(NULL) 42 { 43 fLayoutData.fLeftColumnMaximumWidth = 100; 44 fLayoutData.fRightColumnMaximumWidth = 100; 45 } 46 47 48 PrinterListView::~PrinterListView() 49 { 50 while (!IsEmpty()) 51 delete RemoveItem((int32)0); 52 } 53 54 55 void 56 PrinterListView::BuildPrinterList() 57 { 58 // clear list 59 while (!IsEmpty()) 60 delete RemoveItem((int32)0); 61 62 // Find directory containing printer definition nodes 63 BPath path; 64 if (find_directory(B_USER_PRINTERS_DIRECTORY, &path) != B_OK) 65 return; 66 67 BDirectory dir(path.Path()); 68 if (dir.InitCheck() != B_OK) 69 return; 70 71 BEntry entry; 72 while(dir.GetNextEntry(&entry) == B_OK) { 73 BDirectory printer(&entry); 74 _AddPrinter(printer, false); 75 } 76 77 _LayoutPrinterItems(); 78 } 79 80 81 void 82 PrinterListView::AttachedToWindow() 83 { 84 Inherited::AttachedToWindow(); 85 86 SetSelectionMessage(new BMessage(kMsgPrinterSelected)); 87 SetInvocationMessage(new BMessage(kMsgMakeDefaultPrinter)); 88 SetTarget(Window()); 89 90 BPath path; 91 if (find_directory(B_USER_PRINTERS_DIRECTORY, &path) != B_OK) 92 return; 93 94 BDirectory dir(path.Path()); 95 if (dir.InitCheck() != B_OK) { 96 // directory has to exist in order to start watching it 97 if (create_directory(path.Path(), 0777) != B_OK) 98 return; 99 dir.SetTo(path.Path()); 100 } 101 102 fFolder = new FolderWatcher(Window(), dir, true); 103 fFolder->SetListener(this); 104 105 BuildPrinterList(); 106 107 // Select active printer 108 BString activePrinterName(ActivePrinterName()); 109 for (int32 i = 0; i < CountItems(); i ++) { 110 PrinterItem* item = dynamic_cast<PrinterItem*>(ItemAt(i)); 111 if (item != NULL && item->Name() == activePrinterName) { 112 Select(i); 113 fActivePrinter = item; 114 break; 115 } 116 } 117 } 118 119 120 bool 121 PrinterListView::QuitRequested() 122 { 123 delete fFolder; 124 return true; 125 } 126 127 128 void 129 PrinterListView::UpdateItem(PrinterItem* item) 130 { 131 item->UpdatePendingJobs(); 132 InvalidateItem(IndexOf(item)); 133 } 134 135 136 PrinterItem* 137 PrinterListView::ActivePrinter() const 138 { 139 return fActivePrinter; 140 } 141 142 143 void 144 PrinterListView::SetActivePrinter(PrinterItem* item) 145 { 146 fActivePrinter = item; 147 } 148 149 150 PrinterItem* 151 PrinterListView::SelectedItem() const 152 { 153 return dynamic_cast<PrinterItem*>(ItemAt(CurrentSelection())); 154 } 155 156 157 // FolderListener interface 158 159 void 160 PrinterListView::EntryCreated(node_ref* node, entry_ref* entry) 161 { 162 BDirectory printer(node); 163 _AddPrinter(printer, true); 164 } 165 166 167 void 168 PrinterListView::EntryRemoved(node_ref* node) 169 { 170 PrinterItem* item = _FindItem(node); 171 if (item) { 172 if (item == fActivePrinter) 173 fActivePrinter = NULL; 174 175 RemoveItem(item); 176 delete item; 177 } 178 } 179 180 181 void 182 PrinterListView::AttributeChanged(node_ref* node) 183 { 184 BDirectory printer(node); 185 _AddPrinter(printer, true); 186 } 187 188 189 // private methods 190 191 void 192 PrinterListView::_AddPrinter(BDirectory& printer, bool calculateLayout) 193 { 194 BString state; 195 node_ref node; 196 // If the entry is a directory 197 if (printer.InitCheck() == B_OK 198 && printer.GetNodeRef(&node) == B_OK 199 && _FindItem(&node) == NULL 200 && printer.ReadAttrString(PSRV_PRINTER_ATTR_STATE, &state) == B_OK 201 && state == "free") { 202 // Check it's Mime type for a spool director 203 BNodeInfo info(&printer); 204 char buffer[256]; 205 206 if (info.GetType(buffer) == B_OK 207 && strcmp(buffer, PSRV_PRINTER_FILETYPE) == 0) { 208 // Yes, it is a printer definition node 209 AddItem(new PrinterItem(static_cast<PrintersWindow*>(Window()), 210 printer, fLayoutData)); 211 if (calculateLayout) 212 _LayoutPrinterItems(); 213 } 214 } 215 } 216 217 218 void 219 PrinterListView::_LayoutPrinterItems() 220 { 221 float& leftColumnMaximumWidth = fLayoutData.fLeftColumnMaximumWidth; 222 float& rightColumnMaximumWidth = fLayoutData.fRightColumnMaximumWidth; 223 224 for (int32 i = 0; i < CountItems(); i ++) { 225 PrinterItem* item = static_cast<PrinterItem*>(ItemAt(i)); 226 227 float leftColumnWidth = 0; 228 float rightColumnWidth = 0; 229 item->GetColumnWidth(this, leftColumnWidth, rightColumnWidth); 230 231 leftColumnMaximumWidth = MAX(leftColumnMaximumWidth, 232 leftColumnWidth); 233 rightColumnMaximumWidth = MAX(rightColumnMaximumWidth, 234 rightColumnWidth); 235 } 236 237 Invalidate(); 238 } 239 240 241 PrinterItem* 242 PrinterListView::_FindItem(node_ref* node) const 243 { 244 for (int32 i = CountItems() - 1; i >= 0; i--) { 245 PrinterItem* item = dynamic_cast<PrinterItem*>(ItemAt(i)); 246 node_ref ref; 247 if (item && item->Node()->GetNodeRef(&ref) == B_OK && ref == *node) 248 return item; 249 } 250 return NULL; 251 } 252 253 254 255 // #pragma mark -- PrinterItem 256 257 258 BBitmap* PrinterItem::sIcon = NULL; 259 BBitmap* PrinterItem::sSelectedIcon = NULL; 260 261 262 PrinterItem::PrinterItem(PrintersWindow* window, const BDirectory& node, 263 PrinterListLayoutData& layoutData) 264 : BListItem(0, false), 265 fFolder(NULL), 266 fNode(node), 267 fLayoutData(layoutData) 268 { 269 BRect rect(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1); 270 if (sIcon == NULL) { 271 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 272 sIcon = new BBitmap(rect, B_RGBA32); 273 #else 274 sIcon = new BBitmap(rect, B_CMAP8); 275 #endif 276 BMimeType type(PSRV_PRINTER_FILETYPE); 277 type.GetIcon(sIcon, B_LARGE_ICON); 278 } 279 280 if (sIcon && sIcon->IsValid() && sSelectedIcon == NULL) { 281 const float checkMarkIconSize = 20.0; 282 BBitmap *checkMark = _LoadVectorIcon("check_mark_icon", 283 checkMarkIconSize); 284 if (checkMark && checkMark->IsValid()) { 285 sSelectedIcon = new BBitmap(rect, B_RGBA32, true); 286 if (sSelectedIcon && sSelectedIcon->IsValid()) { 287 // draw check mark at bottom left over printer icon 288 BView *view = new BView(rect, "offscreen", B_FOLLOW_ALL, 289 B_WILL_DRAW); 290 float y = rect.Height() - checkMark->Bounds().Height(); 291 sSelectedIcon->Lock(); 292 sSelectedIcon->AddChild(view); 293 view->DrawBitmap(sIcon); 294 view->SetDrawingMode(B_OP_ALPHA); 295 view->DrawBitmap(checkMark, BPoint(0, y)); 296 view->Sync(); 297 view->RemoveSelf(); 298 sSelectedIcon->Unlock(); 299 delete view; 300 } 301 } 302 delete checkMark; 303 } 304 305 // Get Name of printer 306 _GetStringProperty(PSRV_PRINTER_ATTR_PRT_NAME, fName); 307 _GetStringProperty(PSRV_PRINTER_ATTR_COMMENTS, fComments); 308 _GetStringProperty(PSRV_PRINTER_ATTR_TRANSPORT, fTransport); 309 _GetStringProperty(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, fTransportAddress); 310 _GetStringProperty(PSRV_PRINTER_ATTR_DRV_NAME, fDriverName); 311 312 BPath path; 313 if (find_directory(B_USER_PRINTERS_DIRECTORY, &path) != B_OK) 314 return; 315 316 // Setup spool folder 317 path.Append(fName.String()); 318 BDirectory dir(path.Path()); 319 if (dir.InitCheck() == B_OK) { 320 fFolder = new SpoolFolder(window, this, dir); 321 UpdatePendingJobs(); 322 } 323 } 324 325 326 PrinterItem::~PrinterItem() 327 { 328 delete fFolder; 329 } 330 331 332 void 333 PrinterItem::GetColumnWidth(BView* view, float& leftColumn, float& rightColumn) 334 { 335 BFont font; 336 view->GetFont(&font); 337 338 leftColumn = font.StringWidth(fName.String()); 339 leftColumn = MAX(leftColumn, font.StringWidth(fDriverName.String())); 340 341 rightColumn = font.StringWidth(fPendingJobs.String()); 342 rightColumn = MAX(rightColumn, font.StringWidth(fTransport.String())); 343 rightColumn = MAX(rightColumn, font.StringWidth(fComments.String())); 344 } 345 346 347 void 348 PrinterItem::Update(BView *owner, const BFont *font) 349 { 350 BListItem::Update(owner,font); 351 352 font_height height; 353 font->GetHeight(&height); 354 355 SetHeight((height.ascent + height.descent + height.leading) * 3.0 + 8.0); 356 } 357 358 359 bool PrinterItem::Remove(BListView* view) 360 { 361 BMessenger msgr; 362 if (GetPrinterServerMessenger(msgr) == B_OK) { 363 BMessage script(B_DELETE_PROPERTY); 364 script.AddSpecifier("Printer", view->IndexOf(this)); 365 366 BMessage reply; 367 if (msgr.SendMessage(&script,&reply) == B_OK) 368 return true; 369 } 370 return false; 371 } 372 373 374 void 375 PrinterItem::DrawItem(BView *owner, BRect /*bounds*/, bool complete) 376 { 377 BListView* list = dynamic_cast<BListView*>(owner); 378 if (list == NULL) 379 return; 380 381 BFont font; 382 owner->GetFont(&font); 383 384 font_height height; 385 font.GetHeight(&height); 386 387 float fntheight = height.ascent + height.descent + height.leading; 388 389 BRect bounds = list->ItemFrame(list->IndexOf(this)); 390 391 rgb_color color = owner->ViewColor(); 392 rgb_color oldLowColor = owner->LowColor(); 393 rgb_color oldHighColor = owner->HighColor(); 394 395 if (IsSelected()) 396 color = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR); 397 398 owner->SetLowColor(color); 399 owner->SetHighColor(color); 400 401 owner->FillRect(bounds); 402 403 owner->SetLowColor(oldLowColor); 404 owner->SetHighColor(oldHighColor); 405 406 float iconColumnWidth = B_LARGE_ICON + 8.0; 407 float x = iconColumnWidth; 408 BPoint iconPt(bounds.LeftTop() + BPoint(2.0, 2.0)); 409 BPoint namePt(iconPt + BPoint(x, fntheight)); 410 BPoint driverPt(iconPt + BPoint(x, fntheight * 2.0)); 411 BPoint defaultPt(iconPt + BPoint(x, fntheight * 3.0)); 412 BPoint transportPt(iconPt + BPoint(x, fntheight * 3.0)); 413 414 float totalWidth = bounds.Width() - iconColumnWidth; 415 float maximumWidth = fLayoutData.fLeftColumnMaximumWidth + 416 fLayoutData.fRightColumnMaximumWidth; 417 float width; 418 if (totalWidth < maximumWidth) { 419 width = fLayoutData.fRightColumnMaximumWidth * totalWidth / 420 maximumWidth; 421 } else { 422 width = fLayoutData.fRightColumnMaximumWidth; 423 } 424 425 BPoint pendingPt(bounds.right - width - 8.0, namePt.y); 426 BPoint commentPt(bounds.right - width - 8.0, driverPt.y); 427 428 429 drawing_mode mode = owner->DrawingMode(); 430 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 431 owner->SetDrawingMode(B_OP_ALPHA); 432 #else 433 owner->SetDrawingMode(B_OP_OVER); 434 #endif 435 if (IsActivePrinter()) { 436 if (sSelectedIcon && sSelectedIcon->IsValid()) 437 owner->DrawBitmap(sSelectedIcon, iconPt); 438 else 439 owner->DrawString(B_TRANSLATE("Default Printer"), defaultPt); 440 } else { 441 if (sIcon && sIcon->IsValid()) 442 owner->DrawBitmap(sIcon, iconPt); 443 } 444 445 owner->SetDrawingMode(B_OP_OVER); 446 447 // left of item 448 BString s = fName; 449 owner->SetFont(be_bold_font); 450 owner->TruncateString(&s, B_TRUNCATE_MIDDLE, pendingPt.x - namePt.x); 451 owner->DrawString(s.String(), s.Length(), namePt); 452 owner->SetFont(&font); 453 454 s = B_TRANSLATE("Driver: %driver%"); 455 s.ReplaceFirst("%driver%", fDriverName); 456 owner->TruncateString(&s, B_TRUNCATE_END, commentPt.x - driverPt.x); 457 owner->DrawString(s.String(), s.Length(), driverPt); 458 459 460 if (fTransport.Length() > 0) { 461 s = B_TRANSLATE("Transport: %transport% %transport_address%"); 462 s.ReplaceFirst("%transport%", fTransport); 463 s.ReplaceFirst("%transport_address%", fTransportAddress); 464 owner->TruncateString(&s, B_TRUNCATE_BEGINNING, totalWidth); 465 owner->DrawString(s.String(), s.Length(), transportPt); 466 } 467 468 // right of item 469 s = fPendingJobs; 470 owner->TruncateString(&s, B_TRUNCATE_END, bounds.Width() - pendingPt.x); 471 owner->DrawString(s.String(), s.Length(), pendingPt); 472 473 s = fComments; 474 owner->TruncateString(&s, B_TRUNCATE_MIDDLE, bounds.Width() - commentPt.x); 475 owner->DrawString(s.String(), s.Length(), commentPt); 476 477 owner->SetDrawingMode(mode); 478 } 479 480 481 bool 482 PrinterItem::IsActivePrinter() const 483 { 484 return fName == ActivePrinterName(); 485 } 486 487 488 bool 489 PrinterItem::HasPendingJobs() const 490 { 491 return fFolder && fFolder->CountJobs() > 0; 492 } 493 494 495 SpoolFolder* 496 PrinterItem::Folder() const 497 { 498 return fFolder; 499 } 500 501 502 BDirectory* 503 PrinterItem::Node() 504 { 505 return &fNode; 506 } 507 508 509 void 510 PrinterItem::UpdatePendingJobs() 511 { 512 if (fFolder) { 513 uint32 pendingJobs = fFolder->CountJobs(); 514 if (pendingJobs == 1) { 515 fPendingJobs = B_TRANSLATE("1 pending job"); 516 return; 517 } else if (pendingJobs > 1) { 518 fPendingJobs = ""; 519 fPendingJobs << pendingJobs << B_TRANSLATE(" pending jobs"); 520 return; 521 } 522 } 523 fPendingJobs = B_TRANSLATE("No pending jobs"); 524 } 525 526 527 void 528 PrinterItem::_GetStringProperty(const char* propName, BString& outString) 529 { 530 fNode.ReadAttrString(propName, &outString); 531 } 532 533 534 BBitmap* 535 PrinterItem::_LoadVectorIcon(const char* resourceName, float iconSize) 536 { 537 size_t dataSize; 538 BResources* resources = BApplication::AppResources(); 539 const void* data = resources->LoadResource(B_VECTOR_ICON_TYPE, 540 resourceName, &dataSize); 541 542 if (data != NULL){ 543 BBitmap *iconBitmap = new BBitmap(BRect(0, 0, iconSize - 1, 544 iconSize - 1), 0, B_RGBA32); 545 if (BIconUtils::GetVectorIcon( 546 reinterpret_cast<const uint8*>(data), 547 dataSize, iconBitmap) == B_OK) 548 return iconBitmap; 549 else 550 delete iconBitmap; 551 }; 552 return NULL; 553 } 554