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