2 * Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
7 #include "AttributeWindow.h"
9 #include "FileTypesWindow.h"
15 #include <ControlLook.h>
16 #include <LayoutBuilder.h>
18 #include <MenuField.h>
21 #include <PopUpMenu.h>
22 #include <SpaceLayoutItem.h>
24 #include <TextControl.h>
32 #undef B_TRANSLATION_CONTEXT
33 #define B_TRANSLATION_CONTEXT "Attribute Window"
36 const uint32 kMsgAttributeUpdated
= 'atup';
37 const uint32 kMsgTypeChosen
= 'typc';
38 const uint32 kMsgDisplayAsChosen
= 'dach';
39 const uint32 kMsgVisibilityChanged
= 'vsch';
40 const uint32 kMsgAlignmentChosen
= 'alnc';
41 const uint32 kMsgAccept
= 'acpt';
45 compare_display_as(const char* a
, const char* b
)
47 bool emptyA
= a
== NULL
|| !a
[0];
48 bool emptyB
= b
== NULL
|| !b
[0];
55 const char* end
= strchr(a
, ':');
56 int32 lengthA
= end
? end
- a
: strlen(a
);
58 int32 lengthB
= end
? end
- b
: strlen(b
);
60 if (lengthA
!= lengthB
)
63 return !strncasecmp(a
, b
, lengthA
);
68 display_as_parameter(const char* special
)
70 const char* parameter
= strchr(special
, ':');
71 if (parameter
!= NULL
)
81 AttributeWindow::AttributeWindow(FileTypesWindow
* target
, BMimeType
& mimeType
,
82 AttributeItem
* attributeItem
)
84 BWindow(BRect(100, 100, 350, 200), B_TRANSLATE("Attribute"),
85 B_MODAL_WINDOW_LOOK
, B_MODAL_SUBSET_WINDOW_FEEL
,
86 B_NOT_ZOOMABLE
| B_AUTO_UPDATE_SIZE_LIMITS
| B_ASYNCHRONOUS_CONTROLS
),
88 fMimeType(mimeType
.Type())
90 float padding
= be_control_look
->DefaultItemSpacing();
92 if (attributeItem
!= NULL
)
93 fAttribute
= *attributeItem
;
95 fPublicNameControl
= new BTextControl(B_TRANSLATE("Attribute name:"),
96 fAttribute
.PublicName(), NULL
);
97 fPublicNameControl
->SetModificationMessage(
98 new BMessage(kMsgAttributeUpdated
));
99 fPublicNameControl
->SetAlignment(B_ALIGN_RIGHT
, B_ALIGN_LEFT
);
101 fAttributeControl
= new BTextControl(B_TRANSLATE("Internal name:"),
102 fAttribute
.Name(), NULL
);
103 fAttributeControl
->SetModificationMessage(
104 new BMessage(kMsgAttributeUpdated
));
105 fAttributeControl
->SetAlignment(B_ALIGN_RIGHT
, B_ALIGN_LEFT
);
107 // filter out invalid characters that can't be part of an attribute
108 BTextView
* textView
= fAttributeControl
->TextView();
109 const char* disallowedCharacters
= "/";
110 for (int32 i
= 0; disallowedCharacters
[i
]; i
++) {
111 textView
->DisallowChar(disallowedCharacters
[i
]);
114 fTypeMenu
= new BPopUpMenu("type");
115 BMenuItem
* item
= NULL
;
116 for (int32 i
= 0; kTypeMap
[i
].name
!= NULL
; i
++) {
117 BMessage
* message
= new BMessage(kMsgTypeChosen
);
118 message
->AddInt32("type", kTypeMap
[i
].type
);
120 item
= new BMenuItem(kTypeMap
[i
].name
, message
);
121 fTypeMenu
->AddItem(item
);
123 if (kTypeMap
[i
].type
== fAttribute
.Type())
124 item
->SetMarked(true);
127 BMenuField
* typeMenuField
= new BMenuField("types" , B_TRANSLATE("Type:"),
129 typeMenuField
->SetAlignment(B_ALIGN_RIGHT
);
130 // we must set the color manually when adding a menuField directly
132 typeMenuField
->SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
133 typeMenuField
->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
135 fVisibleCheckBox
= new BCheckBox("visible", B_TRANSLATE("Visible"),
136 new BMessage(kMsgVisibilityChanged
));
137 fVisibleCheckBox
->SetValue(fAttribute
.Visible());
139 BMenu
* menu
= new BPopUpMenu("display as");
140 for (int32 i
= 0; kDisplayAsMap
[i
].name
!= NULL
; i
++) {
141 BMessage
* message
= new BMessage(kMsgDisplayAsChosen
);
142 if (kDisplayAsMap
[i
].identifier
!= NULL
) {
143 message
->AddString("identifier", kDisplayAsMap
[i
].identifier
);
144 for (int32 j
= 0; kDisplayAsMap
[i
].supported
[j
]; j
++) {
145 message
->AddInt32("supports", kDisplayAsMap
[i
].supported
[j
]);
149 item
= new BMenuItem(kDisplayAsMap
[i
].name
, message
);
152 if (compare_display_as(kDisplayAsMap
[i
].identifier
,
153 fAttribute
.DisplayAs()))
154 item
->SetMarked(true);
157 fDisplayAsMenuField
= new BMenuField("display as",
158 B_TRANSLATE_COMMENT("Display as:",
159 "Tracker offers different display modes for attributes."), menu
);
160 fDisplayAsMenuField
->SetAlignment(B_ALIGN_RIGHT
);
162 fEditableCheckBox
= new BCheckBox("editable",
163 B_TRANSLATE_COMMENT("Editable",
164 "If Tracker allows to edit this attribute."),
165 new BMessage(kMsgAttributeUpdated
));
166 fEditableCheckBox
->SetValue(fAttribute
.Editable());
168 fSpecialControl
= new BTextControl(B_TRANSLATE("Special:"),
169 display_as_parameter(fAttribute
.DisplayAs()), NULL
);
170 fSpecialControl
->SetModificationMessage(
171 new BMessage(kMsgAttributeUpdated
));
172 fSpecialControl
->SetAlignment(B_ALIGN_RIGHT
, B_ALIGN_LEFT
);
173 fSpecialControl
->SetEnabled(false);
176 snprintf(text
, sizeof(text
), "%" B_PRId32
, fAttribute
.Width());
177 fWidthControl
= new BTextControl(B_TRANSLATE_COMMENT("Width:",
178 "Default column width in Tracker for this attribute."),
180 fWidthControl
->SetModificationMessage(
181 new BMessage(kMsgAttributeUpdated
));
182 fWidthControl
->SetAlignment(B_ALIGN_RIGHT
, B_ALIGN_LEFT
);
184 // filter out invalid characters that can't be part of a width
185 textView
= fWidthControl
->TextView();
186 for (int32 i
= 0; i
< 256; i
++) {
188 textView
->DisallowChar(i
);
190 textView
->SetMaxBytes(4);
192 const struct alignment_map
{
195 } kAlignmentMap
[] = {
196 {B_ALIGN_LEFT
, B_TRANSLATE_COMMENT("Left",
197 "Attribute column alignment in Tracker")},
198 {B_ALIGN_RIGHT
, B_TRANSLATE_COMMENT("Right",
199 "Attribute column alignment in Tracker")},
200 {B_ALIGN_CENTER
, B_TRANSLATE_COMMENT("Center",
201 "Attribute column alignment in Tracker")},
205 menu
= new BPopUpMenu("alignment");
206 for (int32 i
= 0; kAlignmentMap
[i
].name
!= NULL
; i
++) {
207 BMessage
* message
= new BMessage(kMsgAlignmentChosen
);
208 message
->AddInt32("alignment", kAlignmentMap
[i
].alignment
);
210 item
= new BMenuItem(kAlignmentMap
[i
].name
, message
);
213 if (kAlignmentMap
[i
].alignment
== fAttribute
.Alignment())
214 item
->SetMarked(true);
217 fAlignmentMenuField
= new BMenuField("alignment",
218 B_TRANSLATE("Alignment:"), menu
);
219 fAlignmentMenuField
->SetAlignment(B_ALIGN_RIGHT
);
221 fAcceptButton
= new BButton("add",
222 item
? B_TRANSLATE("Done") : B_TRANSLATE("Add"),
223 new BMessage(kMsgAccept
));
224 fAcceptButton
->SetEnabled(false);
226 BButton
* cancelButton
= new BButton("cancel", B_TRANSLATE("Cancel"),
227 new BMessage(B_QUIT_REQUESTED
));
230 BLayoutBuilder::Group
<>(this, B_VERTICAL
, padding
)
231 .SetInsets(padding
, padding
, padding
, padding
)
232 .AddGrid(padding
, padding
/ 2)
233 .Add(fPublicNameControl
->CreateLabelLayoutItem(), 0, 0)
234 .Add(fPublicNameControl
->CreateTextViewLayoutItem(), 1, 0)
235 .Add(fAttributeControl
->CreateLabelLayoutItem(), 0, 1)
236 .Add(fAttributeControl
->CreateTextViewLayoutItem(), 1, 1)
237 .Add(typeMenuField
->CreateLabelLayoutItem(), 0, 2)
238 .Add(typeMenuField
->CreateMenuBarLayoutItem(), 1, 2)
240 .Add(visibleBox
= new BBox(B_FANCY_BORDER
,
241 BLayoutBuilder::Grid
<>(padding
, padding
/ 2)
242 .Add(fDisplayAsMenuField
->CreateLabelLayoutItem(), 0, 0)
243 .Add(fDisplayAsMenuField
->CreateMenuBarLayoutItem(), 1, 0)
244 .Add(fEditableCheckBox
, 2, 0)
245 .Add(fSpecialControl
->CreateLabelLayoutItem(), 0, 1)
246 .Add(fSpecialControl
->CreateTextViewLayoutItem(), 1, 1, 2)
247 .Add(fWidthControl
->CreateLabelLayoutItem(), 0, 2)
248 .Add(fWidthControl
->CreateTextViewLayoutItem(), 1, 2, 2)
249 .Add(fAlignmentMenuField
->CreateLabelLayoutItem(), 0, 3)
250 .Add(fAlignmentMenuField
->CreateMenuBarLayoutItem(), 1, 3, 2)
251 .SetInsets(padding
, padding
, padding
, padding
)
254 .AddGroup(B_HORIZONTAL
, padding
)
259 visibleBox
->SetLabel(fVisibleCheckBox
);
261 fAcceptButton
->MakeDefault(true);
262 fPublicNameControl
->MakeFocus(true);
264 target
->PlaceSubWindow(this);
272 AttributeWindow::~AttributeWindow()
278 AttributeWindow::_CurrentType() const
280 type_code type
= B_STRING_TYPE
;
281 BMenuItem
* item
= fTypeMenu
->FindMarked();
282 if (item
!= NULL
&& item
->Message() != NULL
) {
284 if (item
->Message()->FindInt32("type", &value
) == B_OK
)
293 AttributeWindow::_DefaultDisplayAs() const
295 return fDisplayAsMenuField
->Menu()->ItemAt(0);
300 AttributeWindow::_CheckDisplayAs()
302 // check display as suported types
304 type_code currentType
= _CurrentType();
306 BMenu
* menu
= fDisplayAsMenuField
->Menu();
307 for (int32 i
= menu
->CountItems(); i
-- > 0;) {
308 BMenuItem
* item
= menu
->ItemAt(i
);
309 bool supported
= item
== _DefaultDisplayAs();
310 // the default type is always supported
312 for (int32 j
= 0; item
->Message()->FindInt32("supports",
313 j
, (int32
*)&type
) == B_OK
; j
++) {
314 if (type
== currentType
) {
320 item
->SetEnabled(supported
);
321 if (item
->IsMarked() && !supported
)
322 menu
->ItemAt(0)->SetMarked(true);
325 fSpecialControl
->SetEnabled(!_DefaultDisplayAs()->IsMarked());
330 AttributeWindow::_CheckAcceptable()
332 bool enabled
= fAttributeControl
->Text() != NULL
333 && fAttributeControl
->Text()[0] != '\0'
334 && fPublicNameControl
->Text() != NULL
335 && fPublicNameControl
->Text()[0] != '\0';
338 // check for equality
339 AttributeItem
* item
= _NewItemFromCurrent();
340 enabled
= fAttribute
!= *item
;
346 if (fAcceptButton
->IsEnabled() != enabled
)
347 fAcceptButton
->SetEnabled(enabled
);
352 AttributeWindow::_NewItemFromCurrent()
354 const char* newAttribute
= fAttributeControl
->Text();
356 type_code type
= _CurrentType();
358 int32 alignment
= B_ALIGN_LEFT
;
359 BMenuItem
* item
= fAlignmentMenuField
->Menu()->FindMarked();
360 if (item
!= NULL
&& item
->Message() != NULL
) {
362 if (item
->Message()->FindInt32("alignment", &value
) == B_OK
)
366 int32 width
= atoi(fWidthControl
->Text());
371 item
= fDisplayAsMenuField
->Menu()->FindMarked();
373 const char* identifier
;
374 if (item
->Message()->FindString("identifier", &identifier
) == B_OK
) {
375 displayAs
= identifier
;
377 if (fSpecialControl
->Text() && fSpecialControl
->Text()[0]) {
379 displayAs
+= fSpecialControl
->Text();
384 return new AttributeItem(newAttribute
,
385 fPublicNameControl
->Text(), type
, displayAs
.String(), alignment
,
386 width
, fVisibleCheckBox
->Value() == B_CONTROL_ON
,
387 fEditableCheckBox
->Value() == B_CONTROL_ON
);
392 AttributeWindow::MessageReceived(BMessage
* message
)
394 switch (message
->what
) {
395 case kMsgAttributeUpdated
:
396 case kMsgAlignmentChosen
:
402 case kMsgDisplayAsChosen
:
403 fSpecialControl
->SetEnabled(!_DefaultDisplayAs()->IsMarked());
407 case kMsgVisibilityChanged
:
409 bool enabled
= fVisibleCheckBox
->Value() != B_CONTROL_OFF
;
411 fDisplayAsMenuField
->SetEnabled(enabled
);
412 fWidthControl
->SetEnabled(enabled
);
413 fAlignmentMenuField
->SetEnabled(enabled
);
414 fEditableCheckBox
->SetEnabled(enabled
);
424 status_t status
= fMimeType
.GetAttrInfo(&attributes
);
425 if (status
== B_OK
) {
426 // replace the entry, and remove any equivalent entries
429 const char* newAttribute
= fAttributeControl
->Text();
431 const char* attribute
;
432 for (int32 i
= 0; attributes
.FindString("attr:name", i
,
433 &attribute
) == B_OK
; i
++) {
434 if (!strcmp(fAttribute
.Name(), attribute
)
435 || !strcmp(newAttribute
, attribute
)) {
440 AttributeItem
* item
= create_attribute_item(attributes
, i
);
445 list
.AddItem(_NewItemFromCurrent());
447 // Copy them to a new message (their memory is still part of the
448 // original BMessage)
449 BMessage newAttributes
;
450 for (int32 i
= 0; i
< list
.CountItems(); i
++) {
451 AttributeItem
* item
= (AttributeItem
*)list
.ItemAt(i
);
453 newAttributes
.AddString("attr:name", item
->Name());
454 newAttributes
.AddString("attr:public_name", item
->PublicName());
455 newAttributes
.AddInt32("attr:type", (int32
)item
->Type());
456 newAttributes
.AddString("attr:display_as", item
->DisplayAs());
457 newAttributes
.AddInt32("attr:alignment", item
->Alignment());
458 newAttributes
.AddInt32("attr:width", item
->Width());
459 newAttributes
.AddBool("attr:viewable", item
->Visible());
460 newAttributes
.AddBool("attr:editable", item
->Editable());
465 status
= fMimeType
.SetAttrInfo(&newAttributes
);
468 if (status
!= B_OK
) {
469 error_alert(B_TRANSLATE("Could not change attributes"),
473 PostMessage(B_QUIT_REQUESTED
);
478 BWindow::MessageReceived(message
);
485 AttributeWindow::QuitRequested()