xref: /haiku/src/preferences/filetypes/AttributeListView.cpp (revision 19d1cffe33373572da33f477596411ce4e0cb965)
1 /*
2  * Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2014 Haiku, Inc. All rights reserved.
4  *
5  * Distributed under the terms of the MIT License.
6  *
7  * Authors:
8  *		Axel Dörfler, axeld@pinc-software.de
9  *		John Scipione, jscipione@gmail.com
10  */
11 
12 
13 #include "AttributeListView.h"
14 
15 #include <stdio.h>
16 
17 #include <Catalog.h>
18 #include <ControlLook.h>
19 #include <Locale.h>
20 #include <ObjectList.h>
21 
22 
23 #undef B_TRANSLATION_CONTEXT
24 #define B_TRANSLATION_CONTEXT "Attribute ListView"
25 
26 
27 const struct type_map kTypeMap[] = {
28 	{ B_TRANSLATE("String"),         B_STRING_TYPE },
29 	{ B_TRANSLATE("Boolean"),        B_BOOL_TYPE   },
30 	{ B_TRANSLATE("Integer 8 bit"),  B_INT8_TYPE   },
31 	{ B_TRANSLATE("Integer 16 bit"), B_INT16_TYPE  },
32 	{ B_TRANSLATE("Integer 32 bit"), B_INT32_TYPE  },
33 	{ B_TRANSLATE("Integer 64 bit"), B_INT64_TYPE  },
34 	{ B_TRANSLATE("Float"),          B_FLOAT_TYPE  },
35 	{ B_TRANSLATE("Double"),         B_DOUBLE_TYPE },
36 	{ B_TRANSLATE("Time"),           B_TIME_TYPE   },
37 	{ NULL,                          0             }
38 };
39 
40 
41 // TODO: in the future, have a (private) Tracker API that exports these
42 //	as well as a nice GUI for them.
43 const struct display_as_map kDisplayAsMap[] = {
44 	{ B_TRANSLATE("Default"),	NULL,
45 		{}
46 	},
47 	{ B_TRANSLATE("Checkbox"),	B_TRANSLATE("checkbox"),
48 		{ B_BOOL_TYPE, B_INT8_TYPE, B_INT16_TYPE, B_INT32_TYPE }
49 	},
50 	{ B_TRANSLATE("Duration"),	B_TRANSLATE("duration"),
51 		{ B_TIME_TYPE, B_INT8_TYPE, B_INT16_TYPE, B_INT32_TYPE, B_INT64_TYPE }
52 	},
53 	{ B_TRANSLATE("Rating"),	B_TRANSLATE("rating"),
54 		{ B_INT8_TYPE, B_INT16_TYPE, B_INT32_TYPE }
55 	},
56 	{ NULL,						NULL,
57 		{}
58 	}
59 };
60 
61 
62 static void
add_display_as(BString & string,const char * displayAs)63 add_display_as(BString& string, const char* displayAs)
64 {
65 	if (displayAs == NULL || !displayAs[0])
66 		return;
67 
68 	BString base(displayAs);
69 	int32 end = base.FindFirst(':');
70 	if (end > 0)
71 		base.Truncate(end);
72 
73 	for (int32 i = 0; kDisplayAsMap[i].name != NULL; i++) {
74 		if (base.ICompare(kDisplayAsMap[i].identifier) == 0) {
75 			string += ", ";
76 			string += base;
77 			return;
78 		}
79 	}
80 }
81 
82 
83 static void
name_for_type(BString & string,type_code type,const char * displayAs)84 name_for_type(BString& string, type_code type, const char* displayAs)
85 {
86 	for (int32 i = 0; kTypeMap[i].name != NULL; i++) {
87 		if (kTypeMap[i].type == type) {
88 			string = kTypeMap[i].name;
89 			add_display_as(string, displayAs);
90 			return;
91 		}
92 	}
93 
94 	char buffer[32];
95 	buffer[0] = '\'';
96 	buffer[1] = 0xff & (type >> 24);
97 	buffer[2] = 0xff & (type >> 16);
98 	buffer[3] = 0xff & (type >> 8);
99 	buffer[4] = 0xff & (type);
100 	buffer[5] = '\'';
101 	buffer[6] = 0;
102 	for (int16 i = 0; i < 4; i++) {
103 		if (buffer[i] < ' ')
104 			buffer[i] = '.';
105 	}
106 
107 	snprintf(buffer + 6, sizeof(buffer) - 6, " (0x%" B_PRIx32 ")", type);
108 	string = buffer;
109 }
110 
111 
112 AttributeItem*
create_attribute_item(BMessage & attributes,int32 index)113 create_attribute_item(BMessage& attributes, int32 index)
114 {
115 	const char* publicName;
116 	if (attributes.FindString("attr:public_name", index, &publicName) != B_OK)
117 		return NULL;
118 
119 	const char* name;
120 	if (attributes.FindString("attr:name", index, &name) != B_OK)
121 		name = "-";
122 
123 	type_code type;
124 	if (attributes.FindInt32("attr:type", index, (int32 *)&type) != B_OK)
125 		type = B_STRING_TYPE;
126 
127 	const char* displayAs;
128 	if (attributes.FindString("attr:display_as", index, &displayAs) != B_OK)
129 		displayAs = NULL;
130 
131 	bool editable;
132 	if (attributes.FindBool("attr:editable", index, &editable) != B_OK)
133 		editable = false;
134 	bool visible;
135 	if (attributes.FindBool("attr:viewable", index, &visible) != B_OK)
136 		visible = false;
137 
138 	int32 alignment;
139 	if (attributes.FindInt32("attr:alignment", index, &alignment) != B_OK)
140 		alignment = B_ALIGN_LEFT;
141 
142 	int32 width;
143 	if (attributes.FindInt32("attr:width", index, &width) != B_OK)
144 		width = 50;
145 
146 	return new AttributeItem(name, publicName, type, displayAs, alignment,
147 		width, visible, editable);
148 }
149 
150 
151 //	#pragma mark - AttributeItem
152 
153 
AttributeItem(const char * name,const char * publicName,type_code type,const char * displayAs,int32 alignment,int32 width,bool visible,bool editable)154 AttributeItem::AttributeItem(const char* name, const char* publicName,
155 	type_code type, const char* displayAs, int32 alignment,
156 	int32 width, bool visible, bool editable)
157 	:
158 	BStringItem(publicName),
159 	fName(name),
160 	fType(type),
161 	fDisplayAs(displayAs),
162 	fAlignment(alignment),
163 	fWidth(width),
164 	fVisible(visible),
165 	fEditable(editable)
166 {
167 }
168 
169 
AttributeItem()170 AttributeItem::AttributeItem()
171 	:
172 	BStringItem(""),
173 	fType(B_STRING_TYPE),
174 	fAlignment(B_ALIGN_LEFT),
175 	fWidth(60),
176 	fVisible(true),
177 	fEditable(false)
178 {
179 }
180 
181 
AttributeItem(const AttributeItem & other)182 AttributeItem::AttributeItem(const AttributeItem& other)
183 	:
184 	BStringItem(other.PublicName())
185 {
186 	*this = other;
187 }
188 
189 
~AttributeItem()190 AttributeItem::~AttributeItem()
191 {
192 }
193 
194 
195 void
DrawItem(BView * owner,BRect frame,bool drawEverything)196 AttributeItem::DrawItem(BView* owner, BRect frame, bool drawEverything)
197 {
198 	BStringItem::DrawItem(owner, frame, drawEverything);
199 
200 	BString type;
201 	name_for_type(type, fType, fDisplayAs.String());
202 	const char* typeString = type.String();
203 	if (typeString == NULL)
204 		return;
205 
206 	rgb_color highColor = owner->HighColor();
207 	rgb_color lowColor = owner->LowColor();
208 
209 	// set the low color
210 	if (IsSelected())
211 		owner->SetLowColor(ui_color(B_LIST_SELECTED_BACKGROUND_COLOR));
212 	else
213 		owner->SetLowColor(ui_color(B_LIST_BACKGROUND_COLOR));
214 
215 	// set the high color
216 	if (!IsEnabled()) {
217 		rgb_color textColor = ui_color(B_LIST_ITEM_TEXT_COLOR);
218 		if (textColor.red + textColor.green + textColor.blue > 128 * 3)
219 			owner->SetHighColor(tint_color(textColor, B_DARKEN_2_TINT));
220 		else
221 			owner->SetHighColor(tint_color(textColor, B_LIGHTEN_2_TINT));
222 	} else {
223 		if (IsSelected())
224 			owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR));
225 		else
226 			owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
227 	}
228 
229 	// move the pen into position
230 	owner->MovePenTo(frame.left + frame.Width() / 2.0f
231 			+ be_control_look->DefaultLabelSpacing(),
232 		owner->PenLocation().y);
233 
234 	// draw the type string
235 	owner->DrawString(typeString);
236 
237 	// set the high color and low color back to the original
238 	owner->SetHighColor(highColor);
239 	owner->SetLowColor(lowColor);
240 }
241 
242 
243 AttributeItem&
operator =(const AttributeItem & other)244 AttributeItem::operator=(const AttributeItem& other)
245 {
246 	SetText(other.PublicName());
247 	fName = other.Name();
248 	fType = other.Type();
249 	fDisplayAs = other.DisplayAs();
250 	fAlignment = other.Alignment();
251 	fWidth = other.Width();
252 	fVisible = other.Visible();
253 	fEditable = other.Editable();
254 
255 	return *this;
256 }
257 
258 
259 bool
operator ==(const AttributeItem & other) const260 AttributeItem::operator==(const AttributeItem& other) const
261 {
262 	return !strcmp(Name(), other.Name())
263 		&& !strcmp(PublicName(), other.PublicName())
264 		&& !strcmp(DisplayAs(), other.DisplayAs())
265 		&& Type() == other.Type()
266 		&& Alignment() == other.Alignment()
267 		&& Width() == other.Width()
268 		&& Visible() == other.Visible()
269 		&& Editable() == other.Editable();
270 }
271 
272 
273 bool
operator !=(const AttributeItem & other) const274 AttributeItem::operator!=(const AttributeItem& other) const
275 {
276 	return !(*this == other);
277 }
278 
279 
280 //	#pragma mark - AttributeListView
281 
282 
AttributeListView(const char * name)283 AttributeListView::AttributeListView(const char* name)
284 	:
285 	BListView(name, B_SINGLE_SELECTION_LIST,
286 		B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS)
287 {
288 }
289 
290 
~AttributeListView()291 AttributeListView::~AttributeListView()
292 {
293 	_DeleteItems();
294 }
295 
296 
297 void
_DeleteItems()298 AttributeListView::_DeleteItems()
299 {
300 	for (int32 i = CountItems() - 1; i >= 0; i--)
301 		delete ItemAt(i);
302 
303 	MakeEmpty();
304 }
305 
306 
307 void
SetTo(BMimeType * type)308 AttributeListView::SetTo(BMimeType* type)
309 {
310 	AttributeItem selectedItem;
311 	if (CurrentSelection(0) >= 0)
312 		selectedItem = *(AttributeItem*)ItemAt(CurrentSelection(0));
313 
314 	// Remove the current items but remember them for now. Also remember
315 	// the currently selected item.
316 	BObjectList<AttributeItem> previousItems(CountItems(), true);
317 	while (AttributeItem* item = (AttributeItem*)RemoveItem((int32)0))
318 		previousItems.AddItem(item);
319 
320 	// fill it again
321 
322 	if (type == NULL)
323 		return;
324 
325 	BMessage attributes;
326 	if (type->GetAttrInfo(&attributes) != B_OK)
327 		return;
328 
329 	AttributeItem* item;
330 	int32 i = 0;
331 	while ((item = create_attribute_item(attributes, i++)) != NULL)
332 		AddItem(item);
333 
334 	// Maybe all the items are the same, except for one item. That
335 	// attribute probably just got added. We should select it so the user
336 	// can better follow what's going on. The problem we are solving by
337 	// doing it this way is that updates to the MIME database are very
338 	// asynchronous. Most likely we have created the new attribute ourselves,
339 	// but the notification comes so late, we can't know for sure.
340 	if (CountItems() == previousItems.CountItems() + 1) {
341 		// First try to make sure that every previous item is there again.
342 		bool allPreviousItemsFound = true;
343 		for (i = previousItems.CountItems() - 1; i >= 0; i--) {
344 			bool previousItemFound = false;
345 			for (int32 j = CountItems() - 1; j >= 0; j--) {
346 				item = (AttributeItem*)ItemAt(j);
347 				if (*item == *previousItems.ItemAt(i)) {
348 					previousItemFound = true;
349 					break;
350 				}
351 			}
352 			if (!previousItemFound) {
353 				allPreviousItemsFound = false;
354 				break;
355 			}
356 		}
357 		if (allPreviousItemsFound) {
358 			for (i = CountItems() - 1; i >= 0; i--) {
359 				item = (AttributeItem*)ItemAt(i);
360 				bool foundNewItem = false;
361 				for (int32 j = previousItems.CountItems() - 1; j >= 0; j--) {
362 					if (*item != *previousItems.ItemAt(j)) {
363 						foundNewItem = true;
364 						break;
365 					}
366 				}
367 				if (foundNewItem) {
368 					Select(i);
369 					ScrollToSelection();
370 					break;
371 				}
372 			}
373 		}
374 	} else {
375 		// Try to re-selected a previously selected item, if it's the exact
376 		// same attribute. This helps not loosing the selection, since changes
377 		// to the model are followed by completely rebuilding the list all the
378 		// time.
379 		for (i = CountItems() - 1; i >= 0; i--) {
380 			item = (AttributeItem*)ItemAt(i);
381 			if (*item == selectedItem) {
382 				Select(i);
383 				ScrollToSelection();
384 				break;
385 			}
386 		}
387 	}
388 }
389 
390 
391 void
Draw(BRect updateRect)392 AttributeListView::Draw(BRect updateRect)
393 {
394 	BListView::Draw(updateRect);
395 
396 	SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
397 		B_DARKEN_2_TINT));
398 
399 	float middle = Bounds().Width() / 2.0f;
400 	StrokeLine(BPoint(middle, 0.0f), BPoint(middle, Bounds().bottom));
401 }
402