/*
* Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2014 Haiku, Inc. All rights reserved.
*
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* John Scipione, jscipione@gmail.com
*/
#include "AttributeListView.h"
#include <stdio.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <Locale.h>
#include <ObjectList.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Attribute ListView"
const struct type_map kTypeMap[] = {
{ B_TRANSLATE("String"), B_STRING_TYPE },
{ B_TRANSLATE("Boolean"), B_BOOL_TYPE },
{ B_TRANSLATE("Integer 8 bit"), B_INT8_TYPE },
{ B_TRANSLATE("Integer 16 bit"), B_INT16_TYPE },
{ B_TRANSLATE("Integer 32 bit"), B_INT32_TYPE },
{ B_TRANSLATE("Integer 64 bit"), B_INT64_TYPE },
{ B_TRANSLATE("Float"), B_FLOAT_TYPE },
{ B_TRANSLATE("Double"), B_DOUBLE_TYPE },
{ B_TRANSLATE("Time"), B_TIME_TYPE },
{ NULL, 0 }
};
// TODO: in the future, have a (private) Tracker API that exports these
// as well as a nice GUI for them.
const struct display_as_map kDisplayAsMap[] = {
{ B_TRANSLATE("Default"), NULL,
{}
},
{ B_TRANSLATE("Checkbox"), B_TRANSLATE("checkbox"),
{ B_BOOL_TYPE, B_INT8_TYPE, B_INT16_TYPE, B_INT32_TYPE }
},
{ B_TRANSLATE("Duration"), B_TRANSLATE("duration"),
{ B_TIME_TYPE, B_INT8_TYPE, B_INT16_TYPE, B_INT32_TYPE, B_INT64_TYPE }
},
{ B_TRANSLATE("Rating"), B_TRANSLATE("rating"),
{ B_INT8_TYPE, B_INT16_TYPE, B_INT32_TYPE }
},
{ NULL, NULL,
{}
}
};
static void
add_display_as(BString& string, const char* displayAs)
{
if (displayAs == NULL || !displayAs[0])
return;
BString base(displayAs);
int32 end = base.FindFirst(':');
if (end > 0)
base.Truncate(end);
for (int32 i = 0; kDisplayAsMap[i].name != NULL; i++) {
if (base.ICompare(kDisplayAsMap[i].identifier) == 0) {
string += ", ";
string += base;
return;
}
}
}
static void
name_for_type(BString& string, type_code type, const char* displayAs)
{
for (int32 i = 0; kTypeMap[i].name != NULL; i++) {
if (kTypeMap[i].type == type) {
string = kTypeMap[i].name;
add_display_as(string, displayAs);
return;
}
}
char buffer[32];
buffer[0] = '\'';
buffer[1] = 0xff & (type >> 24);
buffer[2] = 0xff & (type >> 16);
buffer[3] = 0xff & (type >> 8);
buffer[4] = 0xff & (type);
buffer[5] = '\'';
buffer[6] = 0;
for (int16 i = 0; i < 4; i++) {
if (buffer[i] < ' ')
buffer[i] = '.';
}
snprintf(buffer + 6, sizeof(buffer), " (0x%" B_PRIx32 ")", type);
string = buffer;
}
AttributeItem*
create_attribute_item(BMessage& attributes, int32 index)
{
const char* publicName;
if (attributes.FindString("attr:public_name", index, &publicName) != B_OK)
return NULL;
const char* name;
if (attributes.FindString("attr:name", index, &name) != B_OK)
name = "-";
type_code type;
if (attributes.FindInt32("attr:type", index, (int32 *)&type) != B_OK)
type = B_STRING_TYPE;
const char* displayAs;
if (attributes.FindString("attr:display_as", index, &displayAs) != B_OK)
displayAs = NULL;
bool editable;
if (attributes.FindBool("attr:editable", index, &editable) != B_OK)
editable = false;
bool visible;
if (attributes.FindBool("attr:viewable", index, &visible) != B_OK)
visible = false;
int32 alignment;
if (attributes.FindInt32("attr:alignment", index, &alignment) != B_OK)
alignment = B_ALIGN_LEFT;
int32 width;
if (attributes.FindInt32("attr:width", index, &width) != B_OK)
width = 50;
return new AttributeItem(name, publicName, type, displayAs, alignment,
width, visible, editable);
}
// #pragma mark - AttributeItem
AttributeItem::AttributeItem(const char* name, const char* publicName,
type_code type, const char* displayAs, int32 alignment,
int32 width, bool visible, bool editable)
:
BStringItem(publicName),
fName(name),
fType(type),
fDisplayAs(displayAs),
fAlignment(alignment),
fWidth(width),
fVisible(visible),
fEditable(editable)
{
}
AttributeItem::AttributeItem()
:
BStringItem(""),
fType(B_STRING_TYPE),
fAlignment(B_ALIGN_LEFT),
fWidth(60),
fVisible(true),
fEditable(false)
{
}
AttributeItem::AttributeItem(const AttributeItem& other)
:
BStringItem(other.PublicName())
{
*this = other;
}
AttributeItem::~AttributeItem()
{
}
void
AttributeItem::DrawItem(BView* owner, BRect frame, bool drawEverything)
{
BStringItem::DrawItem(owner, frame, drawEverything);
BString type;
name_for_type(type, fType, fDisplayAs.String());
const char* typeString = type.String();
if (typeString == NULL)
return;
rgb_color highColor = owner->HighColor();
rgb_color lowColor = owner->LowColor();
// set the low color
if (IsSelected())
owner->SetLowColor(ui_color(B_LIST_SELECTED_BACKGROUND_COLOR));
else
owner->SetLowColor(ui_color(B_LIST_BACKGROUND_COLOR));
// set the high color
if (!IsEnabled()) {
rgb_color textColor = ui_color(B_LIST_ITEM_TEXT_COLOR);
if (textColor.red + textColor.green + textColor.blue > 128 * 3)
owner->SetHighColor(tint_color(textColor, B_DARKEN_2_TINT));
else
owner->SetHighColor(tint_color(textColor, B_LIGHTEN_2_TINT));
} else {
if (IsSelected())
owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR));
else
owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
}
// move the pen into position
owner->MovePenTo(frame.left + frame.Width() / 2.0f
+ be_control_look->DefaultLabelSpacing(),
owner->PenLocation().y);
// draw the type string
owner->DrawString(typeString);
// set the high color and low color back to the original
owner->SetHighColor(highColor);
owner->SetLowColor(lowColor);
}
AttributeItem&
AttributeItem::operator=(const AttributeItem& other)
{
SetText(other.PublicName());
fName = other.Name();
fType = other.Type();
fDisplayAs = other.DisplayAs();
fAlignment = other.Alignment();
fWidth = other.Width();
fVisible = other.Visible();
fEditable = other.Editable();
return *this;
}
bool
AttributeItem::operator==(const AttributeItem& other) const
{
return !strcmp(Name(), other.Name())
&& !strcmp(PublicName(), other.PublicName())
&& !strcmp(DisplayAs(), other.DisplayAs())
&& Type() == other.Type()
&& Alignment() == other.Alignment()
&& Width() == other.Width()
&& Visible() == other.Visible()
&& Editable() == other.Editable();
}
bool
AttributeItem::operator!=(const AttributeItem& other) const
{
return !(*this == other);
}
// #pragma mark - AttributeListView
AttributeListView::AttributeListView(const char* name)
:
BListView(name, B_SINGLE_SELECTION_LIST,
B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS)
{
}
AttributeListView::~AttributeListView()
{
_DeleteItems();
}
void
AttributeListView::_DeleteItems()
{
for (int32 i = CountItems() - 1; i >= 0; i--)
delete ItemAt(i);
MakeEmpty();
}
void
AttributeListView::SetTo(BMimeType* type)
{
AttributeItem selectedItem;
if (CurrentSelection(0) >= 0)
selectedItem = *(AttributeItem*)ItemAt(CurrentSelection(0));
// Remove the current items but remember them for now. Also remember
// the currently selected item.
BObjectList<AttributeItem> previousItems(CountItems(), true);
while (AttributeItem* item = (AttributeItem*)RemoveItem((int32)0))
previousItems.AddItem(item);
// fill it again
if (type == NULL)
return;
BMessage attributes;
if (type->GetAttrInfo(&attributes) != B_OK)
return;
AttributeItem* item;
int32 i = 0;
while ((item = create_attribute_item(attributes, i++)) != NULL)
AddItem(item);
// Maybe all the items are the same, except for one item. That
// attribute probably just got added. We should select it so the user
// can better follow what's going on. The problem we are solving by
// doing it this way is that updates to the MIME database are very
// asynchronous. Most likely we have created the new attribute ourselves,
// but the notification comes so late, we can't know for sure.
if (CountItems() == previousItems.CountItems() + 1) {
// First try to make sure that every previous item is there again.
bool allPreviousItemsFound = true;
for (i = previousItems.CountItems() - 1; i >= 0; i--) {
bool previousItemFound = false;
for (int32 j = CountItems() - 1; j >= 0; j--) {
item = (AttributeItem*)ItemAt(j);
if (*item == *previousItems.ItemAt(i)) {
previousItemFound = true;
break;
}
}
if (!previousItemFound) {
allPreviousItemsFound = false;
break;
}
}
if (allPreviousItemsFound) {
for (i = CountItems() - 1; i >= 0; i--) {
item = (AttributeItem*)ItemAt(i);
bool foundNewItem = false;
for (int32 j = previousItems.CountItems() - 1; j >= 0; j--) {
if (*item != *previousItems.ItemAt(j)) {
foundNewItem = true;
break;
}
}
if (foundNewItem) {
Select(i);
ScrollToSelection();
break;
}
}
}
} else {
// Try to re-selected a previously selected item, if it's the exact
// same attribute. This helps not loosing the selection, since changes
// to the model are followed by completely rebuilding the list all the
// time.
for (i = CountItems() - 1; i >= 0; i--) {
item = (AttributeItem*)ItemAt(i);
if (*item == selectedItem) {
Select(i);
ScrollToSelection();
break;
}
}
}
}
void
AttributeListView::Draw(BRect updateRect)
{
BListView::Draw(updateRect);
SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
B_DARKEN_2_TINT));
float middle = Bounds().Width() / 2.0f;
StrokeLine(BPoint(middle, 0.0f), BPoint(middle, Bounds().bottom));
}
↑ V512 A call of the 'snprintf' function will lead to overflow of the buffer 'buffer + 6'.