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