xref: /haiku/src/preferences/printers/PrinterListView.cpp (revision 4d83a710f5cd775ec282f8dd28c22a460cdc05b0)
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