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