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 27const 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. 43const 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 62static void 63add_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 83static void 84name_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 112AttributeItem* 113create_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 154AttributeItem::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 170AttributeItem::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 182AttributeItem::AttributeItem(const AttributeItem& other) 183 : 184 BStringItem(other.PublicName()) 185{ 186 *this = other; 187} 188 189 190AttributeItem::~AttributeItem() 191{ 192} 193 194 195void 196AttributeItem::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 243AttributeItem& 244AttributeItem::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 259bool 260AttributeItem::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 273bool 274AttributeItem::operator!=(const AttributeItem& other) const 275{ 276 return !(*this == other); 277} 278 279 280// #pragma mark - AttributeListView 281 282 283AttributeListView::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 291AttributeListView::~AttributeListView() 292{ 293 _DeleteItems(); 294} 295 296 297void 298AttributeListView::_DeleteItems() 299{ 300 for (int32 i = CountItems() - 1; i >= 0; i--) 301 delete ItemAt(i); 302 303 MakeEmpty(); 304} 305 306 307void 308AttributeListView::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 391void 392AttributeListView::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