/*
* Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "AttributeWindow.h"
#include "FileTypes.h"
#include "FileTypesWindow.h"
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <ControlLook.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <Mime.h>
#include <PopUpMenu.h>
#include <SpaceLayoutItem.h>
#include <String.h>
#include <TextControl.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Attribute Window"
const uint32 kMsgAttributeUpdated = 'atup';
const uint32 kMsgTypeChosen = 'typc';
const uint32 kMsgDisplayAsChosen = 'dach';
const uint32 kMsgVisibilityChanged = 'vsch';
const uint32 kMsgAlignmentChosen = 'alnc';
const uint32 kMsgAccept = 'acpt';
static bool
compare_display_as(const char* a, const char* b)
{
bool emptyA = a == NULL || !a[0];
bool emptyB = b == NULL || !b[0];
if (emptyA && emptyB)
return true;
if (emptyA || emptyB)
return false;
const char* end = strchr(a, ':');
int32 lengthA = end ? end - a : strlen(a);
end = strchr(b, ':');
int32 lengthB = end ? end - b : strlen(b);
if (lengthA != lengthB)
return false;
return !strncasecmp(a, b, lengthA);
}
static const char*
display_as_parameter(const char* special)
{
const char* parameter = strchr(special, ':');
if (parameter != NULL)
return parameter + 1;
return NULL;
}
// #pragma mark -
AttributeWindow::AttributeWindow(FileTypesWindow* target, BMimeType& mimeType,
AttributeItem* attributeItem)
:
BWindow(BRect(100, 100, 350, 200), B_TRANSLATE("Attribute"),
B_MODAL_WINDOW_LOOK, B_MODAL_SUBSET_WINDOW_FEEL,
B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS),
fTarget(target),
fMimeType(mimeType.Type())
{
float padding = be_control_look->DefaultItemSpacing();
if (attributeItem != NULL)
fAttribute = *attributeItem;
fPublicNameControl = new BTextControl(B_TRANSLATE("Attribute name:"),
fAttribute.PublicName(), NULL);
fPublicNameControl->SetModificationMessage(
new BMessage(kMsgAttributeUpdated));
fPublicNameControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
fAttributeControl = new BTextControl(B_TRANSLATE("Internal name:"),
fAttribute.Name(), NULL);
fAttributeControl->SetModificationMessage(
new BMessage(kMsgAttributeUpdated));
fAttributeControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
// filter out invalid characters that can't be part of an attribute
BTextView* textView = fAttributeControl->TextView();
const char* disallowedCharacters = "/";
for (int32 i = 0; disallowedCharacters[i]; i++) {
textView->DisallowChar(disallowedCharacters[i]);
}
fTypeMenu = new BPopUpMenu("type");
BMenuItem* item = NULL;
for (int32 i = 0; kTypeMap[i].name != NULL; i++) {
BMessage* message = new BMessage(kMsgTypeChosen);
message->AddInt32("type", kTypeMap[i].type);
item = new BMenuItem(kTypeMap[i].name, message);
fTypeMenu->AddItem(item);
if (kTypeMap[i].type == fAttribute.Type())
item->SetMarked(true);
}
BMenuField* typeMenuField = new BMenuField("types" , B_TRANSLATE("Type:"),
fTypeMenu);
typeMenuField->SetAlignment(B_ALIGN_RIGHT);
// we must set the color manually when adding a menuField directly
// into a window.
typeMenuField->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
typeMenuField->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
fVisibleCheckBox = new BCheckBox("visible", B_TRANSLATE("Visible"),
new BMessage(kMsgVisibilityChanged));
fVisibleCheckBox->SetValue(fAttribute.Visible());
BMenu* menu = new BPopUpMenu("display as");
for (int32 i = 0; kDisplayAsMap[i].name != NULL; i++) {
BMessage* message = new BMessage(kMsgDisplayAsChosen);
if (kDisplayAsMap[i].identifier != NULL) {
message->AddString("identifier", kDisplayAsMap[i].identifier);
for (int32 j = 0; kDisplayAsMap[i].supported[j]; j++) {
message->AddInt32("supports", kDisplayAsMap[i].supported[j]);
}
}
item = new BMenuItem(kDisplayAsMap[i].name, message);
menu->AddItem(item);
if (compare_display_as(kDisplayAsMap[i].identifier,
fAttribute.DisplayAs()))
item->SetMarked(true);
}
fDisplayAsMenuField = new BMenuField("display as",
B_TRANSLATE_COMMENT("Display as:",
"Tracker offers different display modes for attributes."), menu);
fDisplayAsMenuField->SetAlignment(B_ALIGN_RIGHT);
fEditableCheckBox = new BCheckBox("editable",
B_TRANSLATE_COMMENT("Editable",
"If Tracker allows to edit this attribute."),
new BMessage(kMsgAttributeUpdated));
fEditableCheckBox->SetValue(fAttribute.Editable());
fSpecialControl = new BTextControl(B_TRANSLATE("Special:"),
display_as_parameter(fAttribute.DisplayAs()), NULL);
fSpecialControl->SetModificationMessage(
new BMessage(kMsgAttributeUpdated));
fSpecialControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
fSpecialControl->SetEnabled(false);
char text[64];
snprintf(text, sizeof(text), "%" B_PRId32, fAttribute.Width());
fWidthControl = new BTextControl(B_TRANSLATE_COMMENT("Width:",
"Default column width in Tracker for this attribute."),
text, NULL);
fWidthControl->SetModificationMessage(
new BMessage(kMsgAttributeUpdated));
fWidthControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
// filter out invalid characters that can't be part of a width
textView = fWidthControl->TextView();
for (int32 i = 0; i < 256; i++) {
if (!isdigit(i))
textView->DisallowChar(i);
}
textView->SetMaxBytes(4);
const struct alignment_map {
int32 alignment;
const char* name;
} kAlignmentMap[] = {
{B_ALIGN_LEFT, B_TRANSLATE_COMMENT("Left",
"Attribute column alignment in Tracker")},
{B_ALIGN_RIGHT, B_TRANSLATE_COMMENT("Right",
"Attribute column alignment in Tracker")},
{B_ALIGN_CENTER, B_TRANSLATE_COMMENT("Center",
"Attribute column alignment in Tracker")},
{0, NULL}
};
menu = new BPopUpMenu("alignment");
for (int32 i = 0; kAlignmentMap[i].name != NULL; i++) {
BMessage* message = new BMessage(kMsgAlignmentChosen);
message->AddInt32("alignment", kAlignmentMap[i].alignment);
item = new BMenuItem(kAlignmentMap[i].name, message);
menu->AddItem(item);
if (kAlignmentMap[i].alignment == fAttribute.Alignment())
item->SetMarked(true);
}
fAlignmentMenuField = new BMenuField("alignment",
B_TRANSLATE("Alignment:"), menu);
fAlignmentMenuField->SetAlignment(B_ALIGN_RIGHT);
fAcceptButton = new BButton("add",
item ? B_TRANSLATE("Done") : B_TRANSLATE("Add"),
new BMessage(kMsgAccept));
fAcceptButton->SetEnabled(false);
BButton* cancelButton = new BButton("cancel", B_TRANSLATE("Cancel"),
new BMessage(B_QUIT_REQUESTED));
BBox* visibleBox;
BLayoutBuilder::Group<>(this, B_VERTICAL, padding)
.SetInsets(padding, padding, padding, padding)
.AddGrid(padding, padding / 2)
.Add(fPublicNameControl->CreateLabelLayoutItem(), 0, 0)
.Add(fPublicNameControl->CreateTextViewLayoutItem(), 1, 0)
.Add(fAttributeControl->CreateLabelLayoutItem(), 0, 1)
.Add(fAttributeControl->CreateTextViewLayoutItem(), 1, 1)
.Add(typeMenuField->CreateLabelLayoutItem(), 0, 2)
.Add(typeMenuField->CreateMenuBarLayoutItem(), 1, 2)
.End()
.Add(visibleBox = new BBox(B_FANCY_BORDER,
BLayoutBuilder::Grid<>(padding, padding / 2)
.Add(fDisplayAsMenuField->CreateLabelLayoutItem(), 0, 0)
.Add(fDisplayAsMenuField->CreateMenuBarLayoutItem(), 1, 0)
.Add(fEditableCheckBox, 2, 0)
.Add(fSpecialControl->CreateLabelLayoutItem(), 0, 1)
.Add(fSpecialControl->CreateTextViewLayoutItem(), 1, 1, 2)
.Add(fWidthControl->CreateLabelLayoutItem(), 0, 2)
.Add(fWidthControl->CreateTextViewLayoutItem(), 1, 2, 2)
.Add(fAlignmentMenuField->CreateLabelLayoutItem(), 0, 3)
.Add(fAlignmentMenuField->CreateMenuBarLayoutItem(), 1, 3, 2)
.SetInsets(padding, padding, padding, padding)
.View())
)
.AddGroup(B_HORIZONTAL, padding)
.AddGlue()
.Add(cancelButton)
.Add(fAcceptButton);
visibleBox->SetLabel(fVisibleCheckBox);
fAcceptButton->MakeDefault(true);
fPublicNameControl->MakeFocus(true);
target->PlaceSubWindow(this);
AddToSubset(target);
_CheckDisplayAs();
_CheckAcceptable();
}
AttributeWindow::~AttributeWindow()
{
}
type_code
AttributeWindow::_CurrentType() const
{
type_code type = B_STRING_TYPE;
BMenuItem* item = fTypeMenu->FindMarked();
if (item != NULL && item->Message() != NULL) {
int32 value;
if (item->Message()->FindInt32("type", &value) == B_OK)
type = value;
}
return type;
}
BMenuItem*
AttributeWindow::_DefaultDisplayAs() const
{
return fDisplayAsMenuField->Menu()->ItemAt(0);
}
void
AttributeWindow::_CheckDisplayAs()
{
// check display as suported types
type_code currentType = _CurrentType();
BMenu* menu = fDisplayAsMenuField->Menu();
for (int32 i = menu->CountItems(); i-- > 0;) {
BMenuItem* item = menu->ItemAt(i);
bool supported = item == _DefaultDisplayAs();
// the default type is always supported
type_code type;
for (int32 j = 0; item->Message()->FindInt32("supports",
j, (int32*)&type) == B_OK; j++) {
if (type == currentType) {
supported = true;
break;
}
}
item->SetEnabled(supported);
if (item->IsMarked() && !supported)
menu->ItemAt(0)->SetMarked(true);
}
fSpecialControl->SetEnabled(!_DefaultDisplayAs()->IsMarked());
}
void
AttributeWindow::_CheckAcceptable()
{
bool enabled = fAttributeControl->Text() != NULL
&& fAttributeControl->Text()[0] != '\0'
&& fPublicNameControl->Text() != NULL
&& fPublicNameControl->Text()[0] != '\0';
if (enabled) {
// check for equality
AttributeItem* item = _NewItemFromCurrent();
enabled = fAttribute != *item;
delete item;
}
// Update button
if (fAcceptButton->IsEnabled() != enabled)
fAcceptButton->SetEnabled(enabled);
}
AttributeItem*
AttributeWindow::_NewItemFromCurrent()
{
const char* newAttribute = fAttributeControl->Text();
type_code type = _CurrentType();
int32 alignment = B_ALIGN_LEFT;
BMenuItem* item = fAlignmentMenuField->Menu()->FindMarked();
if (item != NULL && item->Message() != NULL) {
int32 value;
if (item->Message()->FindInt32("alignment", &value) == B_OK)
alignment = value;
}
int32 width = atoi(fWidthControl->Text());
if (width < 0)
width = 0;
BString displayAs;
item = fDisplayAsMenuField->Menu()->FindMarked();
if (item != NULL) {
const char* identifier;
if (item->Message()->FindString("identifier", &identifier) == B_OK) {
displayAs = identifier;
if (fSpecialControl->Text() && fSpecialControl->Text()[0]) {
displayAs += ":";
displayAs += fSpecialControl->Text();
}
}
}
return new AttributeItem(newAttribute,
fPublicNameControl->Text(), type, displayAs.String(), alignment,
width, fVisibleCheckBox->Value() == B_CONTROL_ON,
fEditableCheckBox->Value() == B_CONTROL_ON);
}
void
AttributeWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case kMsgAttributeUpdated:
case kMsgAlignmentChosen:
case kMsgTypeChosen:
_CheckDisplayAs();
_CheckAcceptable();
break;
case kMsgDisplayAsChosen:
fSpecialControl->SetEnabled(!_DefaultDisplayAs()->IsMarked());
_CheckAcceptable();
break;
case kMsgVisibilityChanged:
{
bool enabled = fVisibleCheckBox->Value() != B_CONTROL_OFF;
fDisplayAsMenuField->SetEnabled(enabled);
fWidthControl->SetEnabled(enabled);
fAlignmentMenuField->SetEnabled(enabled);
fEditableCheckBox->SetEnabled(enabled);
_CheckDisplayAs();
_CheckAcceptable();
break;
}
case kMsgAccept:
{
BMessage attributes;
status_t status = fMimeType.GetAttrInfo(&attributes);
if (status == B_OK) {
// replace the entry, and remove any equivalent entries
BList list;
const char* newAttribute = fAttributeControl->Text();
const char* attribute;
for (int32 i = 0; attributes.FindString("attr:name", i,
&attribute) == B_OK; i++) {
if (!strcmp(fAttribute.Name(), attribute)
|| !strcmp(newAttribute, attribute)) {
// remove this item
continue;
}
AttributeItem* item = create_attribute_item(attributes, i);
if (item != NULL)
list.AddItem(item);
}
list.AddItem(_NewItemFromCurrent());
// Copy them to a new message (their memory is still part of the
// original BMessage)
BMessage newAttributes;
for (int32 i = 0; i < list.CountItems(); i++) {
AttributeItem* item = (AttributeItem*)list.ItemAt(i);
newAttributes.AddString("attr:name", item->Name());
newAttributes.AddString("attr:public_name", item->PublicName());
newAttributes.AddInt32("attr:type", (int32)item->Type());
newAttributes.AddString("attr:display_as", item->DisplayAs());
newAttributes.AddInt32("attr:alignment", item->Alignment());
newAttributes.AddInt32("attr:width", item->Width());
newAttributes.AddBool("attr:viewable", item->Visible());
newAttributes.AddBool("attr:editable", item->Editable());
delete item;
}
status = fMimeType.SetAttrInfo(&newAttributes);
}
if (status != B_OK) {
error_alert(B_TRANSLATE("Could not change attributes"),
status);
}
PostMessage(B_QUIT_REQUESTED);
break;
}
default:
BWindow::MessageReceived(message);
break;
}
}
bool
AttributeWindow::QuitRequested()
{
return true;
}
↑ V773 Visibility scope of the 'typeMenuField' pointer was exited without releasing the memory. A memory leak is possible.