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