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