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