fat: Greatly simplify and clean up dosfs_get_file_map().
[haiku.git] / src / preferences / printers / PrinterListView.cpp
blobd353287664aac5aaa7285532435e7322221c11cc
1 /*
2 * Copyright 2001-2010, Haiku.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Michael Pfeiffer
7 */
10 #include "PrinterListView.h"
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>
23 #include "pr_server.h"
24 #include "Messages.h"
25 #include "Globals.h"
26 #include "PrintersWindow.h"
27 #include "SpoolFolder.h"
30 #undef B_TRANSLATION_CONTEXT
31 #define B_TRANSLATION_CONTEXT "PrinterListView"
34 // #pragma mark -- PrinterListView
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)
43 fLayoutData.fLeftColumnMaximumWidth = 100;
44 fLayoutData.fRightColumnMaximumWidth = 100;
48 PrinterListView::~PrinterListView()
50 while (!IsEmpty())
51 delete RemoveItem((int32)0);
55 void
56 PrinterListView::BuildPrinterList()
58 // clear list
59 while (!IsEmpty())
60 delete RemoveItem((int32)0);
62 // Find directory containing printer definition nodes
63 BPath path;
64 if (find_directory(B_USER_PRINTERS_DIRECTORY, &path) != B_OK)
65 return;
67 BDirectory dir(path.Path());
68 if (dir.InitCheck() != B_OK)
69 return;
71 BEntry entry;
72 while(dir.GetNextEntry(&entry) == B_OK) {
73 BDirectory printer(&entry);
74 _AddPrinter(printer, false);
77 _LayoutPrinterItems();
81 void
82 PrinterListView::AttachedToWindow()
84 Inherited::AttachedToWindow();
86 SetSelectionMessage(new BMessage(kMsgPrinterSelected));
87 SetInvocationMessage(new BMessage(kMsgMakeDefaultPrinter));
88 SetTarget(Window());
90 BPath path;
91 if (find_directory(B_USER_PRINTERS_DIRECTORY, &path) != B_OK)
92 return;
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());
102 fFolder = new FolderWatcher(Window(), dir, true);
103 fFolder->SetListener(this);
105 BuildPrinterList();
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;
120 bool
121 PrinterListView::QuitRequested()
123 delete fFolder;
124 return true;
128 void
129 PrinterListView::UpdateItem(PrinterItem* item)
131 item->UpdatePendingJobs();
132 InvalidateItem(IndexOf(item));
136 PrinterItem*
137 PrinterListView::ActivePrinter() const
139 return fActivePrinter;
143 void
144 PrinterListView::SetActivePrinter(PrinterItem* item)
146 fActivePrinter = item;
150 PrinterItem*
151 PrinterListView::SelectedItem() const
153 return dynamic_cast<PrinterItem*>(ItemAt(CurrentSelection()));
157 // FolderListener interface
159 void
160 PrinterListView::EntryCreated(node_ref* node, entry_ref* entry)
162 BDirectory printer(node);
163 _AddPrinter(printer, true);
167 void
168 PrinterListView::EntryRemoved(node_ref* node)
170 PrinterItem* item = _FindItem(node);
171 if (item) {
172 if (item == fActivePrinter)
173 fActivePrinter = NULL;
175 RemoveItem(item);
176 delete item;
181 void
182 PrinterListView::AttributeChanged(node_ref* node)
184 BDirectory printer(node);
185 _AddPrinter(printer, true);
189 // private methods
191 void
192 PrinterListView::_AddPrinter(BDirectory& printer, bool calculateLayout)
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];
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();
218 void
219 PrinterListView::_LayoutPrinterItems()
221 float& leftColumnMaximumWidth = fLayoutData.fLeftColumnMaximumWidth;
222 float& rightColumnMaximumWidth = fLayoutData.fRightColumnMaximumWidth;
224 for (int32 i = 0; i < CountItems(); i ++) {
225 PrinterItem* item = static_cast<PrinterItem*>(ItemAt(i));
227 float leftColumnWidth = 0;
228 float rightColumnWidth = 0;
229 item->GetColumnWidth(this, leftColumnWidth, rightColumnWidth);
231 leftColumnMaximumWidth = MAX(leftColumnMaximumWidth,
232 leftColumnWidth);
233 rightColumnMaximumWidth = MAX(rightColumnMaximumWidth,
234 rightColumnWidth);
237 Invalidate();
241 PrinterItem*
242 PrinterListView::_FindItem(node_ref* node) const
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;
250 return NULL;
255 // #pragma mark -- PrinterItem
258 BBitmap* PrinterItem::sIcon = NULL;
259 BBitmap* PrinterItem::sSelectedIcon = NULL;
262 PrinterItem::PrinterItem(PrintersWindow* window, const BDirectory& node,
263 PrinterListLayoutData& layoutData)
264 : BListItem(0, false),
265 fFolder(NULL),
266 fNode(node),
267 fLayoutData(layoutData)
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);
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;
302 delete checkMark;
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);
312 BPath path;
313 if (find_directory(B_USER_PRINTERS_DIRECTORY, &path) != B_OK)
314 return;
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();
326 PrinterItem::~PrinterItem()
328 delete fFolder;
332 void
333 PrinterItem::GetColumnWidth(BView* view, float& leftColumn, float& rightColumn)
335 BFont font;
336 view->GetFont(&font);
338 leftColumn = font.StringWidth(fName.String());
339 leftColumn = MAX(leftColumn, font.StringWidth(fDriverName.String()));
341 rightColumn = font.StringWidth(fPendingJobs.String());
342 rightColumn = MAX(rightColumn, font.StringWidth(fTransport.String()));
343 rightColumn = MAX(rightColumn, font.StringWidth(fComments.String()));
347 void
348 PrinterItem::Update(BView *owner, const BFont *font)
350 BListItem::Update(owner,font);
352 font_height height;
353 font->GetHeight(&height);
355 SetHeight((height.ascent + height.descent + height.leading) * 3.0 + 8.0);
359 bool PrinterItem::Remove(BListView* view)
361 BMessenger msgr;
362 if (GetPrinterServerMessenger(msgr) == B_OK) {
363 BMessage script(B_DELETE_PROPERTY);
364 script.AddSpecifier("Printer", view->IndexOf(this));
366 BMessage reply;
367 if (msgr.SendMessage(&script,&reply) == B_OK)
368 return true;
370 return false;
374 void
375 PrinterItem::DrawItem(BView *owner, BRect /*bounds*/, bool complete)
377 BListView* list = dynamic_cast<BListView*>(owner);
378 if (list == NULL)
379 return;
381 BFont font;
382 owner->GetFont(&font);
384 font_height height;
385 font.GetHeight(&height);
387 float fntheight = height.ascent + height.descent + height.leading;
389 BRect bounds = list->ItemFrame(list->IndexOf(this));
391 rgb_color color = owner->ViewColor();
392 rgb_color oldLowColor = owner->LowColor();
393 rgb_color oldHighColor = owner->HighColor();
395 if (IsSelected())
396 color = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR);
398 owner->SetLowColor(color);
399 owner->SetHighColor(color);
401 owner->FillRect(bounds);
403 owner->SetLowColor(oldLowColor);
404 owner->SetHighColor(oldHighColor);
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));
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;
425 BPoint pendingPt(bounds.right - width - 8.0, namePt.y);
426 BPoint commentPt(bounds.right - width - 8.0, driverPt.y);
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);
445 owner->SetDrawingMode(B_OP_OVER);
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);
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);
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);
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);
473 s = fComments;
474 owner->TruncateString(&s, B_TRUNCATE_MIDDLE, bounds.Width() - commentPt.x);
475 owner->DrawString(s.String(), s.Length(), commentPt);
477 owner->SetDrawingMode(mode);
481 bool
482 PrinterItem::IsActivePrinter() const
484 return fName == ActivePrinterName();
488 bool
489 PrinterItem::HasPendingJobs() const
491 return fFolder && fFolder->CountJobs() > 0;
495 SpoolFolder*
496 PrinterItem::Folder() const
498 return fFolder;
502 BDirectory*
503 PrinterItem::Node()
505 return &fNode;
509 void
510 PrinterItem::UpdatePendingJobs()
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;
523 fPendingJobs = B_TRANSLATE("No pending jobs.");
527 void
528 PrinterItem::_GetStringProperty(const char* propName, BString& outString)
530 fNode.ReadAttrString(propName, &outString);
534 BBitmap*
535 PrinterItem::_LoadVectorIcon(const char* resourceName, float iconSize)
537 size_t dataSize;
538 BResources* resources = BApplication::AppResources();
539 const void* data = resources->LoadResource(B_VECTOR_ICON_TYPE,
540 resourceName, &dataSize);
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;
552 return NULL;