RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / preferences / filetypes / AttributeWindow.cpp
blobda958f75014d4d5c5da2dfb09e7e46ad81139246
1 /*
2 * Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "AttributeWindow.h"
8 #include "FileTypes.h"
9 #include "FileTypesWindow.h"
11 #include <Box.h>
12 #include <Button.h>
13 #include <Catalog.h>
14 #include <CheckBox.h>
15 #include <ControlLook.h>
16 #include <LayoutBuilder.h>
17 #include <Locale.h>
18 #include <MenuField.h>
19 #include <MenuItem.h>
20 #include <Mime.h>
21 #include <PopUpMenu.h>
22 #include <SpaceLayoutItem.h>
23 #include <String.h>
24 #include <TextControl.h>
26 #include <ctype.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <strings.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';
44 static bool
45 compare_display_as(const char* a, const char* b)
47 bool emptyA = a == NULL || !a[0];
48 bool emptyB = b == NULL || !b[0];
50 if (emptyA && emptyB)
51 return true;
52 if (emptyA || emptyB)
53 return false;
55 const char* end = strchr(a, ':');
56 int32 lengthA = end ? end - a : strlen(a);
57 end = strchr(b, ':');
58 int32 lengthB = end ? end - b : strlen(b);
60 if (lengthA != lengthB)
61 return false;
63 return !strncasecmp(a, b, lengthA);
67 static const char*
68 display_as_parameter(const char* special)
70 const char* parameter = strchr(special, ':');
71 if (parameter != NULL)
72 return parameter + 1;
74 return NULL;
78 // #pragma mark -
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),
87 fTarget(target),
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:"),
128 fTypeMenu);
129 typeMenuField->SetAlignment(B_ALIGN_RIGHT);
130 // we must set the color manually when adding a menuField directly
131 // into a window.
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);
150 menu->AddItem(item);
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);
175 char text[64];
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."),
179 text, NULL);
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++) {
187 if (!isdigit(i))
188 textView->DisallowChar(i);
190 textView->SetMaxBytes(4);
192 const struct alignment_map {
193 int32 alignment;
194 const char* name;
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")},
202 {0, NULL}
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);
211 menu->AddItem(item);
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));
229 BBox* visibleBox;
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)
239 .End()
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)
252 .View())
254 .AddGroup(B_HORIZONTAL, padding)
255 .AddGlue()
256 .Add(cancelButton)
257 .Add(fAcceptButton);
259 visibleBox->SetLabel(fVisibleCheckBox);
261 fAcceptButton->MakeDefault(true);
262 fPublicNameControl->MakeFocus(true);
264 target->PlaceSubWindow(this);
265 AddToSubset(target);
267 _CheckDisplayAs();
268 _CheckAcceptable();
272 AttributeWindow::~AttributeWindow()
277 type_code
278 AttributeWindow::_CurrentType() const
280 type_code type = B_STRING_TYPE;
281 BMenuItem* item = fTypeMenu->FindMarked();
282 if (item != NULL && item->Message() != NULL) {
283 int32 value;
284 if (item->Message()->FindInt32("type", &value) == B_OK)
285 type = value;
288 return type;
292 BMenuItem*
293 AttributeWindow::_DefaultDisplayAs() const
295 return fDisplayAsMenuField->Menu()->ItemAt(0);
299 void
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
311 type_code type;
312 for (int32 j = 0; item->Message()->FindInt32("supports",
313 j, (int32*)&type) == B_OK; j++) {
314 if (type == currentType) {
315 supported = true;
316 break;
320 item->SetEnabled(supported);
321 if (item->IsMarked() && !supported)
322 menu->ItemAt(0)->SetMarked(true);
325 fSpecialControl->SetEnabled(!_DefaultDisplayAs()->IsMarked());
329 void
330 AttributeWindow::_CheckAcceptable()
332 bool enabled = fAttributeControl->Text() != NULL
333 && fAttributeControl->Text()[0] != '\0'
334 && fPublicNameControl->Text() != NULL
335 && fPublicNameControl->Text()[0] != '\0';
337 if (enabled) {
338 // check for equality
339 AttributeItem* item = _NewItemFromCurrent();
340 enabled = fAttribute != *item;
341 delete item;
344 // Update button
346 if (fAcceptButton->IsEnabled() != enabled)
347 fAcceptButton->SetEnabled(enabled);
351 AttributeItem*
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) {
361 int32 value;
362 if (item->Message()->FindInt32("alignment", &value) == B_OK)
363 alignment = value;
366 int32 width = atoi(fWidthControl->Text());
367 if (width < 0)
368 width = 0;
370 BString displayAs;
371 item = fDisplayAsMenuField->Menu()->FindMarked();
372 if (item != NULL) {
373 const char* identifier;
374 if (item->Message()->FindString("identifier", &identifier) == B_OK) {
375 displayAs = identifier;
377 if (fSpecialControl->Text() && fSpecialControl->Text()[0]) {
378 displayAs += ":";
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);
391 void
392 AttributeWindow::MessageReceived(BMessage* message)
394 switch (message->what) {
395 case kMsgAttributeUpdated:
396 case kMsgAlignmentChosen:
397 case kMsgTypeChosen:
398 _CheckDisplayAs();
399 _CheckAcceptable();
400 break;
402 case kMsgDisplayAsChosen:
403 fSpecialControl->SetEnabled(!_DefaultDisplayAs()->IsMarked());
404 _CheckAcceptable();
405 break;
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);
416 _CheckDisplayAs();
417 _CheckAcceptable();
418 break;
421 case kMsgAccept:
423 BMessage attributes;
424 status_t status = fMimeType.GetAttrInfo(&attributes);
425 if (status == B_OK) {
426 // replace the entry, and remove any equivalent entries
427 BList list;
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)) {
436 // remove this item
437 continue;
440 AttributeItem* item = create_attribute_item(attributes, i);
441 if (item != NULL)
442 list.AddItem(item);
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());
462 delete item;
465 status = fMimeType.SetAttrInfo(&newAttributes);
468 if (status != B_OK) {
469 error_alert(B_TRANSLATE("Could not change attributes"),
470 status);
473 PostMessage(B_QUIT_REQUESTED);
474 break;
477 default:
478 BWindow::MessageReceived(message);
479 break;
484 bool
485 AttributeWindow::QuitRequested()
487 return true;