/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }