1/*******************************************************************************
2/
3/	File:			ColumnTypes.h
4/
5/   Description:    Experimental classes that implement particular column/field
6/					data types for use in BColumnListView.
7/
8/	Copyright 2000+, Be Incorporated, All Rights Reserved
9/
10*******************************************************************************/
11
12#include "ColumnTypes.h"
13
14#include <View.h>
15
16#include <parsedate.h>
17#include <stdio.h>
18
19
20#define kTEXT_MARGIN	8
21
22
23BTitledColumn::BTitledColumn(const char* title, float width, float minWidth,
24		float maxWidth, alignment align)
25	: BColumn(width, minWidth, maxWidth, align),
26	fTitle(title)
27{
28	font_height	fh;
29
30	be_plain_font->GetHeight(&fh);
31	fFontHeight = fh.descent + fh.leading;
32}
33
34
35void
36BTitledColumn::DrawTitle(BRect rect, BView* parent)
37{
38	float width = rect.Width() - (2 * kTEXT_MARGIN);
39	BString out_string(fTitle);
40
41	parent->TruncateString(&out_string, B_TRUNCATE_END, width + 2);
42	DrawString(out_string.String(), parent, rect);
43}
44
45
46void
47BTitledColumn::GetColumnName(BString* into) const
48{
49	*into = fTitle;
50}
51
52
53void
54BTitledColumn::DrawString(const char* string, BView* parent, BRect rect)
55{
56	float width = rect.Width() - (2 * kTEXT_MARGIN);
57	float y;
58	BFont font;
59	font_height	finfo;
60
61	parent->GetFont(&font);
62	font.GetHeight(&finfo);
63	y = rect.top + ((rect.Height() - (finfo.ascent + finfo.descent + finfo.leading)) / 2)
64		+ (finfo.ascent + finfo.descent) - 2;
65
66	switch (Alignment()) {
67		default:
68		case B_ALIGN_LEFT:
69			parent->MovePenTo(rect.left + kTEXT_MARGIN, y);
70			break;
71
72		case B_ALIGN_CENTER:
73			parent->MovePenTo(rect.left + kTEXT_MARGIN + ((width - font.StringWidth(string)) / 2), y);
74			break;
75
76		case B_ALIGN_RIGHT:
77			parent->MovePenTo(rect.right - kTEXT_MARGIN - font.StringWidth(string), y);
78			break;
79	}
80	parent->DrawString(string);
81}
82
83
84void
85BTitledColumn::SetTitle(const char* title)
86{
87	fTitle.SetTo(title);
88}
89
90
91void
92BTitledColumn::Title(BString* forTitle) const
93{
94	if (forTitle)
95		forTitle->SetTo(fTitle.String());
96}
97
98
99float
100BTitledColumn::FontHeight() const
101{
102	return fFontHeight;
103}
104
105
106float
107BTitledColumn::GetPreferredWidth(BField *_field, BView* parent) const
108{
109	return parent->StringWidth(fTitle.String()) + 2 * kTEXT_MARGIN;
110}
111
112
113// #pragma mark -
114
115
116BStringField::BStringField(const char* string)
117	:
118	fWidth(0),
119	fString(string),
120	fClippedString(string)
121{
122}
123
124
125void
126BStringField::SetString(const char* val)
127{
128	fString = val;
129	fClippedString = "";
130	fWidth = 0;
131}
132
133
134const char*
135BStringField::String() const
136{
137	return fString.String();
138}
139
140
141void
142BStringField::SetWidth(float width)
143{
144	fWidth = width;
145}
146
147
148float
149BStringField::Width()
150{
151	return fWidth;
152}
153
154
155void
156BStringField::SetClippedString(const char* val)
157{
158	fClippedString = val;
159}
160
161
162const char*
163BStringField::ClippedString()
164{
165	return fClippedString.String();
166}
167
168
169// #pragma mark -
170
171
172BStringColumn::BStringColumn(const char* title, float width, float minWidth,
173		float maxWidth, uint32 truncate, alignment align)
174	: BTitledColumn(title, width, minWidth, maxWidth, align),
175	fTruncate(truncate)
176{
177}
178
179
180void
181BStringColumn::DrawField(BField* _field, BRect rect, BView* parent)
182{
183	float width = rect.Width() - (2 * kTEXT_MARGIN);
184	BStringField* field = static_cast<BStringField*>(_field);
185	bool clipNeeded = width < field->Width();
186
187	if (clipNeeded) {
188		BString out_string(field->String());
189
190		parent->TruncateString(&out_string, fTruncate, width + 2);
191		field->SetClippedString(out_string.String());
192		field->SetWidth(width);
193	}
194
195	DrawString(clipNeeded ? field->ClippedString() : field->String(), parent, rect);
196}
197
198
199float
200BStringColumn::GetPreferredWidth(BField *_field, BView* parent) const
201{
202	BStringField* field = static_cast<BStringField*>(_field);
203	return parent->StringWidth(field->String()) + 2 * kTEXT_MARGIN;
204}
205
206
207int
208BStringColumn::CompareFields(BField* field1, BField* field2)
209{
210	return ICompare(((BStringField*)field1)->String(),
211		(((BStringField*)field2)->String()));
212}
213
214
215bool
216BStringColumn::AcceptsField(const BField *field) const
217{
218	return static_cast<bool>(dynamic_cast<const BStringField*>(field));
219}
220
221
222// #pragma mark -
223
224
225BDateField::BDateField(time_t *t)
226	:
227	fTime(*localtime(t)),
228	fUnixTime(*t),
229	fSeconds(0),
230	fClippedString(""),
231	fWidth(0)
232{
233	fSeconds = mktime(&fTime);
234}
235
236
237void
238BDateField::SetWidth(float width)
239{
240	fWidth = width;
241}
242
243
244float
245BDateField::Width()
246{
247	return fWidth;
248}
249
250
251void
252BDateField::SetClippedString(const char* val)
253{
254	fClippedString = val;
255}
256
257
258const char*
259BDateField::ClippedString()
260{
261	return fClippedString.String();
262}
263
264
265time_t
266BDateField::Seconds()
267{
268	return fSeconds;
269}
270
271
272time_t
273BDateField::UnixTime()
274{
275	return fUnixTime;
276}
277
278
279// #pragma mark -
280
281
282BDateColumn::BDateColumn(const char* title, float width, float minWidth,
283		float maxWidth, alignment align)
284	: BTitledColumn(title, width, minWidth, maxWidth, align),
285	fTitle(title)
286{
287}
288
289
290const char *kTIME_FORMATS[] = {
291	"%A, %B %d %Y, %I:%M:%S %p",	// Monday, July 09 1997, 05:08:15 PM
292	"%a, %b %d %Y, %I:%M:%S %p",	// Mon, Jul 09 1997, 05:08:15 PM
293	"%a, %b %d %Y, %I:%M %p",		// Mon, Jul 09 1997, 05:08 PM
294	"%b %d %Y, %I:%M %p",			// Jul 09 1997, 05:08 PM
295	"%m/%d/%y, %I:%M %p",			// 07/09/97, 05:08 PM
296	"%m/%d/%y",						// 07/09/97
297	NULL
298};
299
300
301void
302BDateColumn::DrawField(BField* _field, BRect rect, BView* parent)
303{
304	float width = rect.Width() - (2 * kTEXT_MARGIN);
305	BDateField*	field = (BDateField*)_field;
306
307	if (field->Width() != rect.Width()) {
308		char dateString[256];
309		time_t curtime = field->UnixTime();
310		tm time_data;
311		BFont font;
312
313		parent->GetFont(&font);
314		localtime_r(&curtime, &time_data);
315
316		for (int32 index = 0; ; index++) {
317			if (!kTIME_FORMATS[index])
318				break;
319			strftime(dateString, 256, kTIME_FORMATS[index], &time_data);
320			if (font.StringWidth(dateString) <= width)
321				break;
322		}
323
324		if (font.StringWidth(dateString) > width) {
325			BString out_string(dateString);
326
327			parent->TruncateString(&out_string, B_TRUNCATE_MIDDLE, width + 2);
328			strcpy(dateString, out_string.String());
329		}
330		field->SetClippedString(dateString);
331		field->SetWidth(width);
332	}
333
334	DrawString(field->ClippedString(), parent, rect);
335}
336
337
338int
339BDateColumn::CompareFields(BField* field1, BField* field2)
340{
341	return((BDateField*)field1)->Seconds() - ((BDateField*)field2)->Seconds();
342}
343
344
345// #pragma mark -
346
347
348BSizeField::BSizeField(off_t size)
349	:
350	fSize(size)
351{
352}
353
354
355void
356BSizeField::SetSize(off_t size)
357{
358	fSize = size;
359}
360
361
362off_t
363BSizeField::Size()
364{
365	return fSize;
366}
367
368
369// #pragma mark -
370
371
372BSizeColumn::BSizeColumn(const char* title, float width, float minWidth,
373		float maxWidth, alignment align)
374	: BTitledColumn(title, width, minWidth, maxWidth, align)
375{
376}
377
378
379const int64 kKB_SIZE = 1024;
380const int64 kMB_SIZE = 1048576;
381const int64 kGB_SIZE = 1073741824;
382const int64 kTB_SIZE = kGB_SIZE * kKB_SIZE;
383
384const char *kSIZE_FORMATS[] = {
385	"%.2f %s",
386	"%.1f %s",
387	"%.f %s",
388	"%.f%s",
389	0
390};
391
392void
393BSizeColumn::DrawField(BField* _field, BRect rect, BView* parent)
394{
395	char str[256];
396	float width = rect.Width() - (2 * kTEXT_MARGIN);
397	BFont font;
398	BString string;
399	off_t size = ((BSizeField*)_field)->Size();
400
401	parent->GetFont(&font);
402	if (size < kKB_SIZE) {
403		sprintf(str, "%" B_PRId64 " bytes", size);
404		if (font.StringWidth(str) > width)
405			sprintf(str, "%" B_PRId64 " B", size);
406	} else {
407		const char*	suffix;
408		float float_value;
409		if (size >= kTB_SIZE) {
410			suffix = "TB";
411			float_value = (float)size / kTB_SIZE;
412		} else if (size >= kGB_SIZE) {
413			suffix = "GB";
414			float_value = (float)size / kGB_SIZE;
415		} else if (size >= kMB_SIZE) {
416			suffix = "MB";
417			float_value = (float)size / kMB_SIZE;
418		} else {
419			suffix = "KB";
420			float_value = (float)size / kKB_SIZE;
421		}
422
423		for (int32 index = 0; ; index++) {
424			if (!kSIZE_FORMATS[index])
425				break;
426
427			sprintf(str, kSIZE_FORMATS[index], float_value, suffix);
428			// strip off an insignificant zero so we don't get readings
429			// such as 1.00
430			char *period = 0;
431			char *tmp (NULL);
432			for (tmp = str; *tmp; tmp++) {
433				if (*tmp == '.')
434					period = tmp;
435			}
436			if (period && period[1] && period[2] == '0') {
437				// move the rest of the string over the insignificant zero
438				for (tmp = &period[2]; *tmp; tmp++)
439					*tmp = tmp[1];
440			}
441			if (font.StringWidth(str) <= width)
442				break;
443		}
444	}
445
446	string = str;
447	parent->TruncateString(&string, B_TRUNCATE_MIDDLE, width + 2);
448	DrawString(string.String(), parent, rect);
449}
450
451
452int
453BSizeColumn::CompareFields(BField* field1, BField* field2)
454{
455	return ((BSizeField*)field1)->Size() - ((BSizeField*)field2)->Size();
456}
457
458
459// #pragma mark -
460
461
462BIntegerField::BIntegerField(int32 number)
463	:
464	fInteger(number)
465{
466}
467
468
469void
470BIntegerField::SetValue(int32 value)
471{
472	fInteger = value;
473}
474
475
476int32
477BIntegerField::Value()
478{
479	return fInteger;
480}
481
482
483// #pragma mark -
484
485
486BIntegerColumn::BIntegerColumn(const char* title, float width, float minWidth,
487		float maxWidth, alignment align)
488	: BTitledColumn(title, width, minWidth, maxWidth, align)
489{
490}
491
492
493void
494BIntegerColumn::DrawField(BField *field, BRect rect, BView* parent)
495{
496	char formatted[256];
497	float width = rect.Width() - (2 * kTEXT_MARGIN);
498	BString	string;
499
500	sprintf(formatted, "%d", (int)((BIntegerField*)field)->Value());
501
502	string = formatted;
503	parent->TruncateString(&string, B_TRUNCATE_MIDDLE, width + 2);
504	DrawString(string.String(), parent, rect);
505}
506
507
508int
509BIntegerColumn::CompareFields(BField *field1, BField *field2)
510{
511	return (((BIntegerField*)field1)->Value() - ((BIntegerField*)field2)->Value());
512}
513
514
515// #pragma mark -
516
517
518GraphColumn::GraphColumn(const char* name, float width, float minWidth,
519		float maxWidth, alignment align)
520	: BIntegerColumn(name, width, minWidth, maxWidth, align)
521{
522}
523
524
525void
526GraphColumn::DrawField(BField* field, BRect rect, BView* parent)
527{
528	int	number = ((BIntegerField*)field)->Value();
529
530	if (number > 100)
531		number = 100;
532	else if (number < 0)
533		number = 0;
534
535	BRect graphRect(rect);
536	graphRect.InsetBy(5, 3);
537	parent->StrokeRect(graphRect);
538	if (number > 0) {
539		graphRect.InsetBy(1, 1);
540		float val = graphRect.Width() * (float) number / 100;
541		graphRect.right = graphRect.left + val;
542		parent->SetHighColor(0, 0, 190);
543		parent->FillRect(graphRect);
544	}
545
546	parent->SetDrawingMode(B_OP_INVERT);
547	parent->SetHighColor(128, 128, 128);
548	char numstr[256];
549	sprintf(numstr, "%d%%", number);
550
551	float width = be_plain_font->StringWidth(numstr);
552	parent->MovePenTo(rect.left + rect.Width() / 2 - width / 2, rect.bottom - FontHeight());
553	parent->DrawString(numstr);
554}
555
556
557// #pragma mark -
558
559
560BBitmapField::BBitmapField(BBitmap *bitmap)
561	:
562	fBitmap(bitmap)
563{
564}
565
566
567const BBitmap*
568BBitmapField::Bitmap()
569{
570	return fBitmap;
571}
572
573
574void
575BBitmapField::SetBitmap(BBitmap* bitmap)
576{
577	fBitmap = bitmap;
578}
579
580
581// #pragma mark -
582
583
584BBitmapColumn::BBitmapColumn(const char* title, float width, float minWidth,
585		float maxWidth, alignment align)
586	: BTitledColumn(title, width, minWidth, maxWidth, align)
587{
588}
589
590
591void
592BBitmapColumn::DrawField(BField* field, BRect rect, BView* parent)
593{
594	BBitmapField *bitmapField = static_cast<BBitmapField *>(field);
595	const BBitmap *bitmap = bitmapField->Bitmap();
596
597	if (bitmap != NULL) {
598		float x = 0.0;
599		BRect r = bitmap->Bounds();
600		float y = rect.top + ((rect.Height() - r.Height()) / 2);
601
602		switch (Alignment()) {
603			default:
604			case B_ALIGN_LEFT:
605				x = rect.left + kTEXT_MARGIN;
606				break;
607
608			case B_ALIGN_CENTER:
609				x = rect.left + ((rect.Width() - r.Width()) / 2);
610				break;
611
612			case B_ALIGN_RIGHT:
613				x = rect.right - kTEXT_MARGIN - r.Width();
614				break;
615		}
616		// setup drawing mode according to bitmap color space,
617		// restore previous mode after drawing
618		drawing_mode oldMode = parent->DrawingMode();
619		if (bitmap->ColorSpace() == B_RGBA32
620			|| bitmap->ColorSpace() == B_RGBA32_BIG) {
621			parent->SetDrawingMode(B_OP_ALPHA);
622			parent->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
623		} else {
624			parent->SetDrawingMode(B_OP_OVER);
625		}
626
627		parent->DrawBitmap(bitmap, BPoint(x, y));
628
629		parent->SetDrawingMode(oldMode);
630	}
631}
632
633
634int
635BBitmapColumn::CompareFields(BField* /*field1*/, BField* /*field2*/)
636{
637	// Comparing bitmaps doesn't really make sense...
638	return 0;
639}
640
641
642bool
643BBitmapColumn::AcceptsField(const BField *field) const
644{
645	return static_cast<bool>(dynamic_cast<const BBitmapField*>(field));
646}
647
648
649