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