libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / preferences / filetypes / FileTypesWindow.cpp
blobc771569f97730d7f9f1487b3f65452128beec8f5
1 /*
2 * Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "AttributeListView.h"
8 #include "AttributeWindow.h"
9 #include "DropTargetListView.h"
10 #include "ExtensionWindow.h"
11 #include "FileTypes.h"
12 #include "FileTypesWindow.h"
13 #include "IconView.h"
14 #include "MimeTypeListView.h"
15 #include "NewFileTypeWindow.h"
16 #include "PreferredAppMenu.h"
17 #include "StringView.h"
19 #include <Alignment.h>
20 #include <AppFileInfo.h>
21 #include <Application.h>
22 #include <Bitmap.h>
23 #include <Box.h>
24 #include <Button.h>
25 #include <Catalog.h>
26 #include <ControlLook.h>
27 #include <LayoutBuilder.h>
28 #include <ListView.h>
29 #include <Locale.h>
30 #include <MenuBar.h>
31 #include <MenuField.h>
32 #include <MenuItem.h>
33 #include <Mime.h>
34 #include <NodeInfo.h>
35 #include <OutlineListView.h>
36 #include <PopUpMenu.h>
37 #include <ScrollView.h>
38 #include <SpaceLayoutItem.h>
39 #include <SplitView.h>
40 #include <TextControl.h>
42 #include <OverrideAlert.h>
43 #include <be_apps/Tracker/RecentItems.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <strings.h>
50 #undef B_TRANSLATION_CONTEXT
51 #define B_TRANSLATION_CONTEXT "FileTypes Window"
54 const uint32 kMsgTypeSelected = 'typs';
55 const uint32 kMsgAddType = 'atyp';
56 const uint32 kMsgRemoveType = 'rtyp';
58 const uint32 kMsgExtensionSelected = 'exts';
59 const uint32 kMsgExtensionInvoked = 'exti';
60 const uint32 kMsgAddExtension = 'aext';
61 const uint32 kMsgRemoveExtension = 'rext';
62 const uint32 kMsgRuleEntered = 'rule';
64 const uint32 kMsgAttributeSelected = 'atrs';
65 const uint32 kMsgAttributeInvoked = 'atri';
66 const uint32 kMsgAddAttribute = 'aatr';
67 const uint32 kMsgRemoveAttribute = 'ratr';
68 const uint32 kMsgMoveUpAttribute = 'muat';
69 const uint32 kMsgMoveDownAttribute = 'mdat';
71 const uint32 kMsgPreferredAppChosen = 'papc';
72 const uint32 kMsgSelectPreferredApp = 'slpa';
73 const uint32 kMsgSamePreferredAppAs = 'spaa';
75 const uint32 kMsgPreferredAppOpened = 'paOp';
76 const uint32 kMsgSamePreferredAppAsOpened = 'spaO';
78 const uint32 kMsgTypeEntered = 'type';
79 const uint32 kMsgDescriptionEntered = 'dsce';
81 const uint32 kMsgToggleIcons = 'tgic';
82 const uint32 kMsgToggleRule = 'tgrl';
85 static const char* kAttributeNames[] = {
86 "attr:public_name",
87 "attr:name",
88 "attr:type",
89 "attr:editable",
90 "attr:viewable",
91 "attr:extra",
92 "attr:alignment",
93 "attr:width",
94 "attr:display_as"
98 class TypeIconView : public IconView {
99 public:
100 TypeIconView(const char* name);
101 virtual ~TypeIconView();
103 virtual void Draw(BRect updateRect);
104 virtual void GetPreferredSize(float* _width, float* _height);
106 protected:
107 virtual BRect BitmapRect() const;
111 class ExtensionListView : public DropTargetListView {
112 public:
113 ExtensionListView(const char* name,
114 list_view_type type = B_SINGLE_SELECTION_LIST,
115 uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE);
116 virtual ~ExtensionListView();
118 virtual BSize MinSize();
120 virtual void MessageReceived(BMessage* message);
121 virtual bool AcceptsDrag(const BMessage* message);
123 void SetType(BMimeType* type);
125 private:
126 BMimeType fType;
127 BSize fMinSize;
131 // #pragma mark -
134 TypeIconView::TypeIconView(const char* name)
135 : IconView(name)
137 ShowEmptyFrame(false);
138 SetIconSize(48);
142 TypeIconView::~TypeIconView()
147 void
148 TypeIconView::Draw(BRect updateRect)
150 if (!IsEnabled())
151 return;
153 IconView::Draw(updateRect);
155 const char* text = NULL;
157 switch (IconSource()) {
158 case kNoIcon:
159 text = B_TRANSLATE("no icon");
160 break;
161 case kApplicationIcon:
162 text = B_TRANSLATE("(from application)");
163 break;
164 case kSupertypeIcon:
165 text = B_TRANSLATE("(from super type)");
166 break;
168 default:
169 return;
172 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
173 B_DISABLED_LABEL_TINT));
174 SetLowColor(ViewColor());
176 font_height fontHeight;
177 GetFontHeight(&fontHeight);
179 float y = fontHeight.ascent;
180 if (IconSource() == kNoIcon) {
181 // center text in the middle of the icon
182 y += (IconSize() - fontHeight.ascent - fontHeight.descent) / 2.0f;
183 } else
184 y += IconSize() + 3.0f;
186 DrawString(text, BPoint(ceilf((Bounds().Width() - StringWidth(text)) / 2.0f),
187 ceilf(y)));
191 void
192 TypeIconView::GetPreferredSize(float* _width, float* _height)
194 if (_width) {
195 float a = StringWidth(B_TRANSLATE("(from application)"));
196 float b = StringWidth(B_TRANSLATE("(from super type)"));
197 float width = max_c(a, b);
198 if (width < IconSize())
199 width = IconSize();
201 *_width = ceilf(width);
204 if (_height) {
205 font_height fontHeight;
206 GetFontHeight(&fontHeight);
208 *_height = IconSize() + 3.0f + ceilf(fontHeight.ascent
209 + fontHeight.descent);
214 BRect
215 TypeIconView::BitmapRect() const
217 if (IconSource() == kNoIcon) {
218 // this also defines the drop target area
219 font_height fontHeight;
220 GetFontHeight(&fontHeight);
222 float width = StringWidth(B_TRANSLATE("no icon")) + 8.0f;
223 float height = ceilf(fontHeight.ascent + fontHeight.descent) + 6.0f;
224 float x = (Bounds().Width() - width) / 2.0f;
225 float y = ceilf((IconSize() - fontHeight.ascent - fontHeight.descent)
226 / 2.0f) - 3.0f;
228 return BRect(x, y, x + width, y + height);
231 float x = (Bounds().Width() - IconSize()) / 2.0f;
232 return BRect(x, 0.0f, x + IconSize() - 1, IconSize() - 1);
236 // #pragma mark -
239 ExtensionListView::ExtensionListView(const char* name,
240 list_view_type type, uint32 flags)
242 DropTargetListView(name, type, flags)
247 ExtensionListView::~ExtensionListView()
252 BSize
253 ExtensionListView::MinSize()
255 if (!fMinSize.IsWidthSet()) {
256 BFont font;
257 GetFont(&font);
258 fMinSize.width = font.StringWidth(".mmmmm");
260 font_height height;
261 font.GetHeight(&height);
262 fMinSize.height = (height.ascent + height.descent + height.leading) * 3;
265 return fMinSize;
269 void
270 ExtensionListView::MessageReceived(BMessage* message)
272 if (message->WasDropped() && AcceptsDrag(message)) {
273 // create extension list
274 BList list;
275 entry_ref ref;
276 for (int32 index = 0; message->FindRef("refs", index, &ref) == B_OK;
277 index++) {
278 const char* point = strchr(ref.name, '.');
279 if (point != NULL && point[1])
280 list.AddItem(strdup(++point));
283 merge_extensions(fType, list);
285 // delete extension list
286 for (int32 index = list.CountItems(); index-- > 0;) {
287 free(list.ItemAt(index));
289 } else
290 DropTargetListView::MessageReceived(message);
294 bool
295 ExtensionListView::AcceptsDrag(const BMessage* message)
297 if (fType.Type() == NULL)
298 return false;
300 int32 count = 0;
301 entry_ref ref;
303 for (int32 index = 0; message->FindRef("refs", index, &ref) == B_OK;
304 index++) {
305 const char* point = strchr(ref.name, '.');
306 if (point != NULL && point[1])
307 count++;
310 return count > 0;
314 void
315 ExtensionListView::SetType(BMimeType* type)
317 if (type != NULL)
318 fType.SetTo(type->Type());
319 else
320 fType.Unset();
324 // #pragma mark -
327 FileTypesWindow::FileTypesWindow(const BMessage& settings)
329 BWindow(_Frame(settings), B_TRANSLATE_SYSTEM_NAME("FileTypes"),
330 B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS
331 | B_AUTO_UPDATE_SIZE_LIMITS),
332 fNewTypeWindow(NULL)
334 bool showIcons;
335 bool showRule;
336 if (settings.FindBool("show_icons", &showIcons) != B_OK)
337 showIcons = true;
338 if (settings.FindBool("show_rule", &showRule) != B_OK)
339 showRule = false;
341 float padding = be_control_look->DefaultItemSpacing();
342 BAlignment labelAlignment = be_control_look->DefaultLabelAlignment();
343 BAlignment fullAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
345 // add the menu
346 BMenuBar* menuBar = new BMenuBar("");
348 BMenu* menu = new BMenu(B_TRANSLATE("File"));
349 BMenuItem* item = new BMenuItem(
350 B_TRANSLATE("New resource file" B_UTF8_ELLIPSIS), NULL, 'N',
351 B_COMMAND_KEY);
352 item->SetEnabled(false);
353 menu->AddItem(item);
355 BMenu* recentsMenu = BRecentFilesList::NewFileListMenu(
356 B_TRANSLATE("Open" B_UTF8_ELLIPSIS), NULL, NULL,
357 be_app, 10, false, NULL, kSignature);
358 item = new BMenuItem(recentsMenu, new BMessage(kMsgOpenFilePanel));
359 item->SetShortcut('O', B_COMMAND_KEY);
360 menu->AddItem(item);
362 menu->AddItem(new BMenuItem(
363 B_TRANSLATE("Application types" B_UTF8_ELLIPSIS),
364 new BMessage(kMsgOpenApplicationTypesWindow)));
365 menu->AddSeparatorItem();
367 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
368 new BMessage(B_QUIT_REQUESTED), 'Q', B_COMMAND_KEY));
369 menu->SetTargetForItems(be_app);
370 menuBar->AddItem(menu);
372 menu = new BMenu(B_TRANSLATE("Settings"));
373 item = new BMenuItem(B_TRANSLATE("Show icons in list"),
374 new BMessage(kMsgToggleIcons));
375 item->SetMarked(showIcons);
376 item->SetTarget(this);
377 menu->AddItem(item);
379 item = new BMenuItem(B_TRANSLATE("Show recognition rule"),
380 new BMessage(kMsgToggleRule));
381 item->SetMarked(showRule);
382 item->SetTarget(this);
383 menu->AddItem(item);
384 menuBar->AddItem(menu);
385 menuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
387 // MIME Types list
388 BButton* addTypeButton = new BButton("add",
389 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddType));
391 fRemoveTypeButton = new BButton("remove", B_TRANSLATE("Remove"),
392 new BMessage(kMsgRemoveType) );
394 fTypeListView = new MimeTypeListView("typeview", NULL, showIcons, false);
395 fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected));
396 fTypeListView->SetExplicitMinSize(BSize(200, B_SIZE_UNSET));
398 BScrollView* typeListScrollView = new BScrollView("scrollview",
399 fTypeListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
401 // "Icon" group
403 fIconView = new TypeIconView("icon");
404 fIconBox = new BBox("Icon BBox");
405 fIconBox->SetLabel(B_TRANSLATE("Icon"));
406 BLayoutBuilder::Group<>(fIconBox, B_VERTICAL, padding)
407 .SetInsets(padding)
408 .AddGlue(1)
409 .Add(fIconView, 3)
410 .AddGlue(1);
412 // "File Recognition" group
414 fRecognitionBox = new BBox("Recognition Box");
415 fRecognitionBox->SetLabel(B_TRANSLATE("File recognition"));
416 fRecognitionBox->SetExplicitAlignment(fullAlignment);
418 fExtensionLabel = new StringView(B_TRANSLATE("Extensions:"), NULL);
419 fExtensionLabel->LabelView()->SetExplicitAlignment(labelAlignment);
421 fAddExtensionButton = new BButton("add ext",
422 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddExtension));
423 fAddExtensionButton->SetExplicitMaxSize(
424 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
426 fRemoveExtensionButton = new BButton("remove ext", B_TRANSLATE("Remove"),
427 new BMessage(kMsgRemoveExtension));
429 fExtensionListView = new ExtensionListView("listview ext",
430 B_SINGLE_SELECTION_LIST);
431 fExtensionListView->SetSelectionMessage(
432 new BMessage(kMsgExtensionSelected));
433 fExtensionListView->SetInvocationMessage(
434 new BMessage(kMsgExtensionInvoked));
436 BScrollView* scrollView = new BScrollView("scrollview ext",
437 fExtensionListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
439 fRuleControl = new BTextControl("rule", B_TRANSLATE("Rule:"), "",
440 new BMessage(kMsgRuleEntered));
441 fRuleControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
442 fRuleControl->Hide();
444 BLayoutBuilder::Grid<>(fRecognitionBox, padding, padding / 2)
445 .SetInsets(padding, padding * 2, padding, padding)
446 .Add(fExtensionLabel->LabelView(), 0, 0)
447 .Add(scrollView, 0, 1, 2, 2)
448 .Add(fAddExtensionButton, 2, 1)
449 .Add(fRemoveExtensionButton, 2, 2)
450 .Add(fRuleControl, 0, 3, 3, 1);
452 // "Description" group
454 fDescriptionBox = new BBox("description BBox");
455 fDescriptionBox->SetLabel(B_TRANSLATE("Description"));
456 fDescriptionBox->SetExplicitAlignment(fullAlignment);
458 fInternalNameView = new StringView(B_TRANSLATE("Internal name:"), NULL);
459 fInternalNameView->SetEnabled(false);
460 fTypeNameControl = new BTextControl("type", B_TRANSLATE("Type name:"), "",
461 new BMessage(kMsgTypeEntered));
462 fDescriptionControl = new BTextControl("description",
463 B_TRANSLATE("Description:"), "", new BMessage(kMsgDescriptionEntered));
465 BLayoutBuilder::Grid<>(fDescriptionBox, padding / 2, padding / 2)
466 .SetInsets(padding, padding * 2, padding, padding)
467 .Add(fInternalNameView->LabelView(), 0, 0)
468 .Add(fInternalNameView->TextView(), 1, 0)
469 .Add(fTypeNameControl->CreateLabelLayoutItem(), 0, 1)
470 .Add(fTypeNameControl->CreateTextViewLayoutItem(), 1, 1, 2)
471 .Add(fDescriptionControl->CreateLabelLayoutItem(), 0, 2)
472 .Add(fDescriptionControl->CreateTextViewLayoutItem(), 1, 2, 2);
474 // "Preferred Application" group
476 fPreferredBox = new BBox("preferred BBox");
477 fPreferredBox->SetLabel(B_TRANSLATE("Preferred application"));
479 menu = new BPopUpMenu("preferred");
480 menu->AddItem(item = new BMenuItem(B_TRANSLATE("None"),
481 new BMessage(kMsgPreferredAppChosen)));
482 item->SetMarked(true);
483 fPreferredField = new BMenuField("preferred", (char*)NULL, menu);
485 fSelectButton = new BButton("select",
486 B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
487 new BMessage(kMsgSelectPreferredApp));
489 fSameAsButton = new BButton("same as",
490 B_TRANSLATE("Same as" B_UTF8_ELLIPSIS),
491 new BMessage(kMsgSamePreferredAppAs));
493 BLayoutBuilder::Group<>(fPreferredBox, B_HORIZONTAL, padding)
494 .SetInsets(padding, padding * 2, padding, padding)
495 .Add(fPreferredField)
496 .Add(fSelectButton)
497 .Add(fSameAsButton);
499 // "Extra Attributes" group
501 fAttributeBox = new BBox("Attribute Box");
502 fAttributeBox->SetLabel(B_TRANSLATE("Extra attributes"));
504 fAddAttributeButton = new BButton("add attr",
505 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddAttribute));
506 fAddAttributeButton->SetExplicitMaxSize(
507 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
509 fRemoveAttributeButton = new BButton("remove attr", B_TRANSLATE("Remove"),
510 new BMessage(kMsgRemoveAttribute));
511 fRemoveAttributeButton->SetExplicitMaxSize(
512 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
514 fMoveUpAttributeButton = new BButton("move up attr", B_TRANSLATE("Move up"),
515 new BMessage(kMsgMoveUpAttribute));
516 fMoveUpAttributeButton->SetExplicitMaxSize(
517 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
518 fMoveDownAttributeButton = new BButton("move down attr",
519 B_TRANSLATE("Move down"), new BMessage(kMsgMoveDownAttribute));
520 fMoveDownAttributeButton->SetExplicitMaxSize(
521 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
523 fAttributeListView = new AttributeListView("listview attr");
524 fAttributeListView->SetSelectionMessage(
525 new BMessage(kMsgAttributeSelected));
526 fAttributeListView->SetInvocationMessage(
527 new BMessage(kMsgAttributeInvoked));
529 BScrollView* attributesScroller = new BScrollView("scrollview attr",
530 fAttributeListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
532 BLayoutBuilder::Group<>(fAttributeBox, B_HORIZONTAL, padding)
533 .SetInsets(padding, padding * 2, padding, padding)
534 .Add(attributesScroller, 1.0f)
535 .AddGroup(B_VERTICAL, padding / 2, 0.0f)
536 .SetInsets(0)
537 .Add(fAddAttributeButton)
538 .Add(fRemoveAttributeButton)
539 .AddStrut(padding)
540 .Add(fMoveUpAttributeButton)
541 .Add(fMoveDownAttributeButton)
542 .AddGlue();
544 fMainSplitView = new BSplitView(B_HORIZONTAL, floorf(padding / 2));
546 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
547 .SetInsets(0)
548 .Add(menuBar)
549 .AddGroup(B_HORIZONTAL, 0)
550 .SetInsets(B_USE_WINDOW_SPACING)
551 .AddSplit(fMainSplitView)
552 .AddGroup(B_VERTICAL, padding)
553 .Add(typeListScrollView)
554 .AddGroup(B_HORIZONTAL, padding)
555 .Add(addTypeButton)
556 .Add(fRemoveTypeButton)
557 .AddGlue()
558 .End()
559 .End()
560 // Right side
561 .AddGroup(B_VERTICAL, padding)
562 .AddGroup(B_HORIZONTAL, padding)
563 .Add(fIconBox, 1)
564 .Add(fRecognitionBox, 3)
565 .End()
566 .Add(fDescriptionBox)
567 .Add(fPreferredBox)
568 .Add(fAttributeBox, 5);
570 _SetType(NULL);
571 _ShowSnifferRule(showRule);
573 float leftWeight;
574 float rightWeight;
575 if (settings.FindFloat("left_split_weight", &leftWeight) != B_OK
576 || settings.FindFloat("right_split_weight", &rightWeight) != B_OK) {
577 leftWeight = 0.2;
578 rightWeight = 1.0 - leftWeight;
580 fMainSplitView->SetItemWeight(0, leftWeight, false);
581 fMainSplitView->SetItemWeight(1, rightWeight, true);
583 BMimeType::StartWatching(this);
587 FileTypesWindow::~FileTypesWindow()
589 BMimeType::StopWatching(this);
593 void
594 FileTypesWindow::MessageReceived(BMessage* message)
596 switch (message->what) {
597 case B_SIMPLE_DATA:
599 type_code type;
600 if (message->GetInfo("refs", &type) == B_OK
601 && type == B_REF_TYPE) {
602 be_app->PostMessage(message);
604 break;
607 case kMsgToggleIcons:
609 BMenuItem* item;
610 if (message->FindPointer("source", (void **)&item) != B_OK)
611 break;
613 item->SetMarked(!fTypeListView->IsShowingIcons());
614 fTypeListView->ShowIcons(item->IsMarked());
616 // update settings
617 BMessage update(kMsgSettingsChanged);
618 update.AddBool("show_icons", item->IsMarked());
619 be_app_messenger.SendMessage(&update);
620 break;
623 case kMsgToggleRule:
625 BMenuItem* item;
626 if (message->FindPointer("source", (void **)&item) != B_OK)
627 break;
629 item->SetMarked(fRuleControl->IsHidden());
630 _ShowSnifferRule(item->IsMarked());
632 // update settings
633 BMessage update(kMsgSettingsChanged);
634 update.AddBool("show_rule", item->IsMarked());
635 be_app_messenger.SendMessage(&update);
636 break;
639 case kMsgTypeSelected:
641 int32 index;
642 if (message->FindInt32("index", &index) == B_OK) {
643 MimeTypeItem* item
644 = (MimeTypeItem*)fTypeListView->ItemAt(index);
645 if (item != NULL) {
646 BMimeType type(item->Type());
647 _SetType(&type);
648 } else
649 _SetType(NULL);
651 break;
654 case kMsgAddType:
655 if (fNewTypeWindow == NULL) {
656 fNewTypeWindow
657 = new NewFileTypeWindow(this, fCurrentType.Type());
658 fNewTypeWindow->Show();
659 } else
660 fNewTypeWindow->Activate();
661 break;
663 case kMsgNewTypeWindowClosed:
664 fNewTypeWindow = NULL;
665 break;
667 case kMsgRemoveType:
669 if (fCurrentType.Type() == NULL)
670 break;
672 BAlert* alert;
673 if (fCurrentType.IsSupertypeOnly()) {
674 alert = new BPrivate::OverrideAlert(
675 B_TRANSLATE("FileTypes request"),
676 B_TRANSLATE("Removing a super type cannot be reverted.\n"
677 "All file types that belong to this super type "
678 "will be lost!\n\n"
679 "Are you sure you want to do this? To remove the whole "
680 "group, hold down the Shift key and press \"Remove\"."),
681 B_TRANSLATE("Remove"), B_SHIFT_KEY, B_TRANSLATE("Cancel"),
682 0, NULL, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
683 alert->SetShortcut(1, B_ESCAPE);
684 } else {
685 alert = new BAlert(B_TRANSLATE("FileTypes request"),
686 B_TRANSLATE("Removing a file type cannot be reverted.\n"
687 "Are you sure you want to remove it?"),
688 B_TRANSLATE("Remove"), B_TRANSLATE("Cancel"),
689 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
690 alert->SetShortcut(1, B_ESCAPE);
692 if (alert->Go())
693 break;
695 status_t status = fCurrentType.Delete();
696 if (status != B_OK) {
697 fprintf(stderr, B_TRANSLATE(
698 "Could not remove file type: %s\n"), strerror(status));
700 break;
703 case kMsgSelectNewType:
705 const char* type;
706 if (message->FindString("type", &type) == B_OK)
707 fTypeListView->SelectNewType(type);
708 break;
711 // File Recognition group
713 case kMsgExtensionSelected:
715 int32 index;
716 if (message->FindInt32("index", &index) == B_OK) {
717 BStringItem* item
718 = (BStringItem*)fExtensionListView->ItemAt(index);
719 fRemoveExtensionButton->SetEnabled(item != NULL);
721 break;
724 case kMsgExtensionInvoked:
726 if (fCurrentType.Type() == NULL)
727 break;
729 int32 index;
730 if (message->FindInt32("index", &index) == B_OK) {
731 BStringItem* item
732 = (BStringItem*)fExtensionListView->ItemAt(index);
733 if (item == NULL)
734 break;
736 BWindow* window
737 = new ExtensionWindow(this, fCurrentType, item->Text());
738 window->Show();
740 break;
743 case kMsgAddExtension:
745 if (fCurrentType.Type() == NULL)
746 break;
748 BWindow* window = new ExtensionWindow(this, fCurrentType, NULL);
749 window->Show();
750 break;
753 case kMsgRemoveExtension:
755 int32 index = fExtensionListView->CurrentSelection();
756 if (index < 0 || fCurrentType.Type() == NULL)
757 break;
759 BMessage extensions;
760 if (fCurrentType.GetFileExtensions(&extensions) == B_OK) {
761 extensions.RemoveData("extensions", index);
762 fCurrentType.SetFileExtensions(&extensions);
764 break;
767 case kMsgRuleEntered:
769 // check rule
770 BString parseError;
771 if (BMimeType::CheckSnifferRule(fRuleControl->Text(),
772 &parseError) != B_OK) {
773 parseError.Prepend(
774 B_TRANSLATE("Recognition rule is not valid:\n\n"));
775 error_alert(parseError.String());
776 } else
777 fCurrentType.SetSnifferRule(fRuleControl->Text());
778 break;
781 // Description group
783 case kMsgTypeEntered:
785 fCurrentType.SetShortDescription(fTypeNameControl->Text());
786 break;
789 case kMsgDescriptionEntered:
791 fCurrentType.SetLongDescription(fDescriptionControl->Text());
792 break;
795 // Preferred Application group
797 case kMsgPreferredAppChosen:
799 const char* signature;
800 if (message->FindString("signature", &signature) != B_OK)
801 signature = NULL;
803 fCurrentType.SetPreferredApp(signature);
804 break;
807 case kMsgSelectPreferredApp:
809 BMessage panel(kMsgOpenFilePanel);
810 panel.AddString("title",
811 B_TRANSLATE("Select preferred application"));
812 panel.AddInt32("message", kMsgPreferredAppOpened);
813 panel.AddMessenger("target", this);
815 be_app_messenger.SendMessage(&panel);
816 break;
818 case kMsgPreferredAppOpened:
819 _AdoptPreferredApplication(message, false);
820 break;
822 case kMsgSamePreferredAppAs:
824 BMessage panel(kMsgOpenFilePanel);
825 panel.AddString("title",
826 B_TRANSLATE("Select same preferred application as"));
827 panel.AddInt32("message", kMsgSamePreferredAppAsOpened);
828 panel.AddMessenger("target", this);
829 panel.AddBool("allowDirs", true);
831 be_app_messenger.SendMessage(&panel);
832 break;
834 case kMsgSamePreferredAppAsOpened:
835 _AdoptPreferredApplication(message, true);
836 break;
838 // Extra Attributes group
840 case kMsgAttributeSelected:
842 int32 index;
843 if (message->FindInt32("index", &index) == B_OK) {
844 AttributeItem* item
845 = (AttributeItem*)fAttributeListView->ItemAt(index);
846 fRemoveAttributeButton->SetEnabled(item != NULL);
847 fMoveUpAttributeButton->SetEnabled(index > 0);
848 fMoveDownAttributeButton->SetEnabled(index >= 0
849 && index < fAttributeListView->CountItems() - 1);
851 break;
854 case kMsgAttributeInvoked:
856 if (fCurrentType.Type() == NULL)
857 break;
859 int32 index;
860 if (message->FindInt32("index", &index) == B_OK) {
861 AttributeItem* item
862 = (AttributeItem*)fAttributeListView->ItemAt(index);
863 if (item == NULL)
864 break;
866 BWindow* window = new AttributeWindow(this, fCurrentType,
867 item);
868 window->Show();
870 break;
873 case kMsgAddAttribute:
875 if (fCurrentType.Type() == NULL)
876 break;
878 BWindow* window = new AttributeWindow(this, fCurrentType, NULL);
879 window->Show();
880 break;
883 case kMsgRemoveAttribute:
885 int32 index = fAttributeListView->CurrentSelection();
886 if (index < 0 || fCurrentType.Type() == NULL)
887 break;
889 BMessage attributes;
890 if (fCurrentType.GetAttrInfo(&attributes) == B_OK) {
891 for (uint32 i = 0; i <
892 sizeof(kAttributeNames) / sizeof(kAttributeNames[0]);
893 i++) {
894 attributes.RemoveData(kAttributeNames[i], index);
897 fCurrentType.SetAttrInfo(&attributes);
899 break;
902 case kMsgMoveUpAttribute:
904 int32 index = fAttributeListView->CurrentSelection();
905 if (index < 1 || fCurrentType.Type() == NULL)
906 break;
908 _MoveUpAttributeIndex(index);
909 break;
912 case kMsgMoveDownAttribute:
914 int32 index = fAttributeListView->CurrentSelection();
915 if (index < 0 || index == fAttributeListView->CountItems() - 1
916 || fCurrentType.Type() == NULL) {
917 break;
920 _MoveUpAttributeIndex(index + 1);
921 break;
924 case B_META_MIME_CHANGED:
926 const char* type;
927 int32 which;
928 if (message->FindString("be:type", &type) != B_OK
929 || message->FindInt32("be:which", &which) != B_OK)
930 break;
932 if (fCurrentType.Type() == NULL)
933 break;
935 if (!strcasecmp(fCurrentType.Type(), type)) {
936 if (which != B_MIME_TYPE_DELETED)
937 _SetType(&fCurrentType, which);
938 else
939 _SetType(NULL);
940 } else {
941 // this change could still affect our current type
943 if (which == B_MIME_TYPE_DELETED
944 || which == B_SUPPORTED_TYPES_CHANGED
945 || which == B_PREFERRED_APP_CHANGED) {
946 _UpdatePreferredApps(&fCurrentType);
949 break;
952 default:
953 BWindow::MessageReceived(message);
958 void
959 FileTypesWindow::SelectType(const char* type)
961 fTypeListView->SelectType(type);
965 bool
966 FileTypesWindow::QuitRequested()
968 BMessage update(kMsgSettingsChanged);
969 update.AddRect("file_types_frame", Frame());
970 update.AddFloat("left_split_weight", fMainSplitView->ItemWeight((int32)0));
971 update.AddFloat("right_split_weight", fMainSplitView->ItemWeight(1));
972 be_app_messenger.SendMessage(&update);
974 be_app->PostMessage(kMsgTypesWindowClosed);
975 return true;
979 void
980 FileTypesWindow::PlaceSubWindow(BWindow* window)
982 window->MoveTo(Frame().left + (Frame().Width() - window->Frame().Width())
983 / 2.0f, Frame().top + (Frame().Height() - window->Frame().Height())
984 / 2.0f);
988 // #pragma mark - private
991 BRect
992 FileTypesWindow::_Frame(const BMessage& settings) const
994 BRect rect;
995 if (settings.FindRect("file_types_frame", &rect) == B_OK)
996 return rect;
998 return BRect(80.0f, 80.0f, 0.0f, 0.0f);
1002 void
1003 FileTypesWindow::_ShowSnifferRule(bool show)
1005 if (fRuleControl->IsHidden() == !show)
1006 return;
1008 if (!show)
1009 fRuleControl->Hide();
1010 else
1011 fRuleControl->Show();
1015 void
1016 FileTypesWindow::_UpdateExtensions(BMimeType* type)
1018 // clear list
1020 for (int32 i = fExtensionListView->CountItems(); i-- > 0;) {
1021 delete fExtensionListView->ItemAt(i);
1023 fExtensionListView->MakeEmpty();
1025 // fill it again
1027 if (type == NULL)
1028 return;
1030 BMessage extensions;
1031 if (type->GetFileExtensions(&extensions) != B_OK)
1032 return;
1034 const char* extension;
1035 int32 i = 0;
1036 while (extensions.FindString("extensions", i++, &extension) == B_OK) {
1037 char dotExtension[B_FILE_NAME_LENGTH];
1038 snprintf(dotExtension, B_FILE_NAME_LENGTH, ".%s", extension);
1040 fExtensionListView->AddItem(new BStringItem(dotExtension));
1045 void
1046 FileTypesWindow::_AdoptPreferredApplication(BMessage* message, bool sameAs)
1048 if (fCurrentType.Type() == NULL)
1049 return;
1051 BString preferred;
1052 if (retrieve_preferred_app(message, sameAs, fCurrentType.Type(), preferred)
1053 != B_OK) {
1054 return;
1057 status_t status = fCurrentType.SetPreferredApp(preferred.String());
1058 if (status != B_OK)
1059 error_alert(B_TRANSLATE("Could not set preferred application"),
1060 status);
1064 void
1065 FileTypesWindow::_UpdatePreferredApps(BMimeType* type)
1067 update_preferred_app_menu(fPreferredField->Menu(), type,
1068 kMsgPreferredAppChosen);
1072 void
1073 FileTypesWindow::_UpdateIcon(BMimeType* type)
1075 if (type != NULL)
1076 fIconView->SetTo(*type);
1077 else
1078 fIconView->Unset();
1082 void
1083 FileTypesWindow::_SetType(BMimeType* type, int32 forceUpdate)
1085 bool enabled = type != NULL;
1087 // update controls
1089 if (type != NULL) {
1090 if (fCurrentType == *type) {
1091 if (!forceUpdate)
1092 return;
1093 } else
1094 forceUpdate = B_EVERYTHING_CHANGED;
1096 if (&fCurrentType != type)
1097 fCurrentType.SetTo(type->Type());
1099 fInternalNameView->SetText(type->Type());
1101 char description[B_MIME_TYPE_LENGTH];
1103 if ((forceUpdate & B_SHORT_DESCRIPTION_CHANGED) != 0) {
1104 if (type->GetShortDescription(description) != B_OK)
1105 description[0] = '\0';
1106 fTypeNameControl->SetText(description);
1109 if ((forceUpdate & B_LONG_DESCRIPTION_CHANGED) != 0) {
1110 if (type->GetLongDescription(description) != B_OK)
1111 description[0] = '\0';
1112 fDescriptionControl->SetText(description);
1115 if ((forceUpdate & B_SNIFFER_RULE_CHANGED) != 0) {
1116 BString rule;
1117 if (type->GetSnifferRule(&rule) != B_OK)
1118 rule = "";
1119 fRuleControl->SetText(rule.String());
1122 fExtensionListView->SetType(&fCurrentType);
1123 } else {
1124 fCurrentType.Unset();
1125 fInternalNameView->SetText(NULL);
1126 fTypeNameControl->SetText(NULL);
1127 fDescriptionControl->SetText(NULL);
1128 fRuleControl->SetText(NULL);
1129 fPreferredField->Menu()->ItemAt(0)->SetMarked(true);
1130 fExtensionListView->SetType(NULL);
1131 fAttributeListView->SetTo(NULL);
1134 if ((forceUpdate & B_FILE_EXTENSIONS_CHANGED) != 0)
1135 _UpdateExtensions(type);
1137 if ((forceUpdate & B_PREFERRED_APP_CHANGED) != 0)
1138 _UpdatePreferredApps(type);
1140 if ((forceUpdate & (B_ICON_CHANGED | B_PREFERRED_APP_CHANGED)) != 0)
1141 _UpdateIcon(type);
1143 if ((forceUpdate & B_ATTR_INFO_CHANGED) != 0)
1144 fAttributeListView->SetTo(type);
1146 // enable/disable controls
1148 fIconView->SetEnabled(enabled);
1150 fInternalNameView->SetEnabled(enabled);
1151 fTypeNameControl->SetEnabled(enabled);
1152 fDescriptionControl->SetEnabled(enabled);
1153 fPreferredField->SetEnabled(enabled);
1155 fRemoveTypeButton->SetEnabled(enabled);
1157 fSelectButton->SetEnabled(enabled);
1158 fSameAsButton->SetEnabled(enabled);
1160 fExtensionLabel->SetEnabled(enabled);
1161 fAddExtensionButton->SetEnabled(enabled);
1162 fRemoveExtensionButton->SetEnabled(false);
1163 fRuleControl->SetEnabled(enabled);
1165 fAddAttributeButton->SetEnabled(enabled);
1166 fRemoveAttributeButton->SetEnabled(false);
1167 fMoveUpAttributeButton->SetEnabled(false);
1168 fMoveDownAttributeButton->SetEnabled(false);
1172 void
1173 FileTypesWindow::_MoveUpAttributeIndex(int32 index)
1175 BMessage attributes;
1176 if (fCurrentType.GetAttrInfo(&attributes) != B_OK)
1177 return;
1179 // Iterate over all known attribute fields, and for each field,
1180 // iterate over all fields of the same name and build a copy
1181 // of the attributes message with the field at the given index swapped
1182 // with the previous field.
1183 BMessage resortedAttributes;
1184 for (uint32 i = 0; i <
1185 sizeof(kAttributeNames) / sizeof(kAttributeNames[0]);
1186 i++) {
1188 type_code type;
1189 int32 count;
1190 bool isFixedSize;
1191 if (attributes.GetInfo(kAttributeNames[i], &type, &count,
1192 &isFixedSize) != B_OK) {
1193 // Apparently the message does not contain this name,
1194 // so just ignore this attribute name.
1195 // NOTE: This shows that the attribute description is
1196 // too fragile. It would have been better to pack each
1197 // attribute description into a separate BMessage.
1198 continue;
1201 for (int32 j = 0; j < count; j++) {
1202 const void* data;
1203 ssize_t size;
1204 int32 originalIndex;
1205 if (j == index - 1)
1206 originalIndex = j + 1;
1207 else if (j == index)
1208 originalIndex = j - 1;
1209 else
1210 originalIndex = j;
1211 attributes.FindData(kAttributeNames[i], type,
1212 originalIndex, &data, &size);
1213 if (j == 0) {
1214 resortedAttributes.AddData(kAttributeNames[i], type,
1215 data, size, isFixedSize);
1216 } else {
1217 resortedAttributes.AddData(kAttributeNames[i], type,
1218 data, size);
1223 // Setting it directly on the type will trigger an update of the GUI as
1224 // well. TODO: FileTypes is heavily descructive, it should use an
1225 // Undo/Redo stack.
1226 fCurrentType.SetAttrInfo(&resortedAttributes);