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_TRANSLATION_CONTEXT 28#define B_TRANSLATION_CONTEXT "PrinterListView" 29 30 31// #pragma mark -- PrinterListView 32 33 34PrinterListView::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 45PrinterListView::~PrinterListView() 46{ 47 while (!IsEmpty()) 48 delete RemoveItem((int32)0); 49} 50 51 52void 53PrinterListView::BuildPrinterList() 54{ 55 // clear list 56 while (!IsEmpty()) 57 delete RemoveItem((int32)0); 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 78void 79PrinterListView::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 117bool 118PrinterListView::QuitRequested() 119{ 120 delete fFolder; 121 return true; 122} 123 124 125void 126PrinterListView::UpdateItem(PrinterItem* item) 127{ 128 item->UpdatePendingJobs(); 129 InvalidateItem(IndexOf(item)); 130} 131 132 133PrinterItem* 134PrinterListView::ActivePrinter() const 135{ 136 return fActivePrinter; 137} 138 139 140void 141PrinterListView::SetActivePrinter(PrinterItem* item) 142{ 143 fActivePrinter = item; 144} 145 146 147PrinterItem* 148PrinterListView::SelectedItem() const 149{ 150 return dynamic_cast<PrinterItem*>(ItemAt(CurrentSelection())); 151} 152 153 154// FolderListener interface 155 156void 157PrinterListView::EntryCreated(node_ref* node, entry_ref* entry) 158{ 159 BDirectory printer(node); 160 _AddPrinter(printer, true); 161} 162 163 164void 165PrinterListView::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 178void 179PrinterListView::AttributeChanged(node_ref* node) 180{ 181 BDirectory printer(node); 182 _AddPrinter(printer, true); 183} 184 185 186// private methods 187 188void 189PrinterListView::_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 215void 216PrinterListView::_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 238PrinterItem* 239PrinterListView::_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 255BBitmap* PrinterItem::sIcon = NULL; 256BBitmap* PrinterItem::sSelectedIcon = NULL; 257 258 259PrinterItem::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 321PrinterItem::~PrinterItem() 322{ 323 delete fFolder; 324} 325 326 327void 328PrinterItem::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 342void 343PrinterItem::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 354bool 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 369void 370PrinterItem::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 480bool 481PrinterItem::IsActivePrinter() const 482{ 483 return fName == ActivePrinterName(); 484} 485 486 487bool 488PrinterItem::HasPendingJobs() const 489{ 490 return fFolder && fFolder->CountJobs() > 0; 491} 492 493 494SpoolFolder* 495PrinterItem::Folder() const 496{ 497 return fFolder; 498} 499 500 501BDirectory* 502PrinterItem::Node() 503{ 504 return &fNode; 505} 506 507 508void 509PrinterItem::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 525void 526PrinterItem::_GetStringProperty(const char* propName, BString& outString) 527{ 528 fNode.ReadAttrString(propName, &outString); 529} 530 531