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