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 "JobListView.h"
11
12#include <stdio.h>
13
14#include <Catalog.h>
15#include <Locale.h>
16#include <MimeType.h>
17#include <Roster.h>
18#include <StringFormat.h>
19#include <Window.h>
20
21#include "pr_server.h"
22#include "Globals.h"
23#include "Jobs.h"
24#include "Messages.h"
25#include "SpoolFolder.h"
26
27
28#undef B_TRANSLATION_CONTEXT
29#define B_TRANSLATION_CONTEXT "JobListView"
30
31
32// #pragma mark -- JobListView
33
34
35JobListView::JobListView(BRect frame)
36	:
37	Inherited(frame, "jobs_list", B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL,
38		B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE)
39{
40}
41
42
43JobListView::~JobListView()
44{
45	while (!IsEmpty())
46		delete RemoveItem((int32)0);
47}
48
49
50void
51JobListView::AttachedToWindow()
52{
53	Inherited::AttachedToWindow();
54
55	SetSelectionMessage(new BMessage(kMsgJobSelected));
56	SetTarget(Window());
57}
58
59
60void
61JobListView::SetSpoolFolder(SpoolFolder* folder)
62{
63	// clear list
64	while (!IsEmpty())
65		delete RemoveItem((int32)0);
66
67	if (folder == NULL)
68		return;
69
70	// Find directory containing printer definition nodes
71	for (int32 i = 0; i < folder->CountJobs(); i++)
72		AddJob(folder->JobAt(i));
73}
74
75
76JobItem*
77JobListView::FindJob(Job* job) const
78{
79	const int32 n = CountItems();
80	for (int32 i = 0; i < n; i++) {
81		JobItem* item = dynamic_cast<JobItem*>(ItemAt(i));
82		if (item && item->GetJob() == job)
83			return item;
84	}
85	return NULL;
86}
87
88
89JobItem*
90JobListView::SelectedItem() const
91{
92	return dynamic_cast<JobItem*>(ItemAt(CurrentSelection()));
93}
94
95
96void
97JobListView::AddJob(Job* job)
98{
99	AddItem(new JobItem(job));
100	Invalidate();
101}
102
103
104void
105JobListView::RemoveJob(Job* job)
106{
107	JobItem* item = FindJob(job);
108	if (item) {
109		RemoveItem(item);
110		delete item;
111		Invalidate();
112	}
113}
114
115
116void
117JobListView::UpdateJob(Job* job)
118{
119	JobItem* item = FindJob(job);
120	if (item) {
121		item->Update();
122		InvalidateItem(IndexOf(item));
123	}
124}
125
126
127void
128JobListView::RestartJob()
129{
130	JobItem* item = SelectedItem();
131	if (item && item->GetJob()->Status() == kFailed) {
132		// setting the state changes the file attribute and
133		// we will receive a notification from SpoolFolder
134		item->GetJob()->SetStatus(kWaiting);
135	}
136}
137
138
139void
140JobListView::CancelJob()
141{
142	JobItem* item = SelectedItem();
143	if (item && item->GetJob()->Status() != kProcessing) {
144		item->GetJob()->SetStatus(kFailed);
145		item->GetJob()->Remove();
146	}
147}
148
149
150// #pragma mark -- JobItem
151
152
153JobItem::JobItem(Job* job)
154	:
155	BListItem(0, false),
156	fJob(job),
157	fIcon(NULL)
158{
159	fJob->Acquire();
160	Update();
161}
162
163
164JobItem::~JobItem()
165{
166	fJob->Release();
167	delete fIcon;
168}
169
170
171void
172JobItem::Update()
173{
174	BNode node(&fJob->EntryRef());
175	if (node.InitCheck() != B_OK)
176		return;
177
178	node.ReadAttrString(PSRV_SPOOL_ATTR_DESCRIPTION, &fName);
179
180	BString mimeType;
181	node.ReadAttrString(PSRV_SPOOL_ATTR_MIMETYPE, &mimeType);
182
183	entry_ref ref;
184	if (fIcon == NULL && be_roster->FindApp(mimeType.String(), &ref) == B_OK) {
185#ifdef HAIKU_TARGET_PLATFORM_HAIKU
186		font_height fontHeight;
187		be_plain_font->GetHeight(&fontHeight);
188		float height = (fontHeight.ascent + fontHeight.descent
189			+ fontHeight.leading) * 2.0;
190		BRect rect(0, 0, height, height);
191		fIcon = new BBitmap(rect, B_RGBA32);
192#else
193		BRect rect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1);
194		fIcon = new BBitmap(rect, B_CMAP8);
195#endif
196		BMimeType type(mimeType.String());
197		if (type.GetIcon(fIcon, B_MINI_ICON) != B_OK) {
198			delete fIcon;
199			fIcon = NULL;
200		}
201	}
202
203	fPages = "";
204	int32 pages;
205	static BStringFormat format(B_TRANSLATE("{0, plural, "
206		"=-1{??? pages}"
207		"=1{# page}"
208		"other{# pages}}"));
209
210	if (node.ReadAttr(PSRV_SPOOL_ATTR_PAGECOUNT,
211		B_INT32_TYPE, 0, &pages, sizeof(pages)) == sizeof(pages)) {
212		format.Format(fPages, pages);
213	} else {
214		// unknown page count, probably the printer is paginating without
215		// software help.
216		format.Format(fPages, -1);
217	}
218
219	fSize = "";
220	off_t size;
221	if (node.GetSize(&size) == B_OK) {
222		char buffer[80];
223		snprintf(buffer, sizeof(buffer), B_TRANSLATE("%.2f KB"),
224			size / 1024.0);
225		fSize = buffer;
226	}
227
228	fStatus = "";
229	switch (fJob->Status()) {
230		case kWaiting:
231			fStatus = B_TRANSLATE("Waiting");
232			break;
233
234		case kProcessing:
235			fStatus = B_TRANSLATE("Processing");
236			break;
237
238		case kFailed:
239			fStatus = B_TRANSLATE("Failed");
240			break;
241
242		case kCompleted:
243			fStatus = B_TRANSLATE("Completed");
244			break;
245
246		default:
247			fStatus = B_TRANSLATE("Unknown status");
248	}
249}
250
251
252void
253JobItem::Update(BView *owner, const BFont *font)
254{
255	BListItem::Update(owner, font);
256
257	font_height height;
258	font->GetHeight(&height);
259
260	SetHeight((height.ascent + height.descent + height.leading) * 2.0 + 8.0);
261}
262
263
264void
265JobItem::DrawItem(BView *owner, BRect, bool complete)
266{
267	BListView* list = dynamic_cast<BListView*>(owner);
268	if (list) {
269		BFont font;
270		owner->GetFont(&font);
271
272		font_height height;
273		font.GetHeight(&height);
274		float fntheight = height.ascent + height.descent + height.leading;
275
276		BRect bounds = list->ItemFrame(list->IndexOf(this));
277
278		rgb_color color = owner->ViewColor();
279		rgb_color oldLowColor = owner->LowColor();
280		rgb_color oldHighColor = owner->HighColor();
281
282		if (IsSelected())
283			color = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR);
284
285		owner->SetHighColor(color);
286		owner->SetLowColor(color);
287
288		owner->FillRect(bounds);
289
290		owner->SetLowColor(oldLowColor);
291		owner->SetHighColor(oldHighColor);
292
293		BPoint iconPt(bounds.LeftTop() + BPoint(2.0, 2.0));
294		float iconHeight = B_MINI_ICON;
295#ifdef HAIKU_TARGET_PLATFORM_HAIKU
296		if (fIcon)
297			iconHeight = fIcon->Bounds().Height();
298#endif
299		BPoint leftTop(bounds.LeftTop() + BPoint(12.0 + iconHeight, 2.0));
300		BPoint namePt(leftTop + BPoint(0.0, fntheight));
301		BPoint statusPt(leftTop + BPoint(0.0, fntheight * 2.0));
302
303		float x = owner->StringWidth(fPages.String()) + 32.0;
304		BPoint pagePt(bounds.RightTop() + BPoint(-x, fntheight));
305		BPoint sizePt(bounds.RightTop() + BPoint(-x, fntheight * 2.0));
306
307		drawing_mode mode = owner->DrawingMode();
308#ifdef HAIKU_TARGET_PLATFORM_HAIKU
309	owner->SetDrawingMode(B_OP_ALPHA);
310#else
311	owner->SetDrawingMode(B_OP_OVER);
312#endif
313
314		if (fIcon)
315			owner->DrawBitmap(fIcon, iconPt);
316
317		// left of item
318		BString name = fName;
319		owner->TruncateString(&name, B_TRUNCATE_MIDDLE, pagePt.x - namePt.x);
320		owner->DrawString(name.String(), name.Length(), namePt);
321		BString status = fStatus;
322		owner->TruncateString(&status, B_TRUNCATE_MIDDLE, sizePt.x - statusPt.x);
323		owner->DrawString(status.String(), status.Length(), statusPt);
324
325		// right of item
326		owner->DrawString(fPages.String(), fPages.Length(), pagePt);
327		owner->DrawString(fSize.String(), fSize.Length(), sizePt);
328
329		owner->SetDrawingMode(mode);
330	}
331}
332