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