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