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