1 /*
2 * Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
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"
18
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>
41
42 #include <OverrideAlert.h>
43 #include <be_apps/Tracker/RecentItems.h>
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <strings.h>
48
49
50 #undef B_TRANSLATION_CONTEXT
51 #define B_TRANSLATION_CONTEXT "FileTypes Window"
52
53
54 const uint32 kMsgTypeSelected = 'typs';
55 const uint32 kMsgAddType = 'atyp';
56 const uint32 kMsgRemoveType = 'rtyp';
57
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';
63
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';
70
71 const uint32 kMsgPreferredAppChosen = 'papc';
72 const uint32 kMsgSelectPreferredApp = 'slpa';
73 const uint32 kMsgSamePreferredAppAs = 'spaa';
74
75 const uint32 kMsgPreferredAppOpened = 'paOp';
76 const uint32 kMsgSamePreferredAppAsOpened = 'spaO';
77
78 const uint32 kMsgTypeEntered = 'type';
79 const uint32 kMsgDescriptionEntered = 'dsce';
80
81 const uint32 kMsgToggleIcons = 'tgic';
82 const uint32 kMsgToggleRule = 'tgrl';
83
84
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"
95 };
96
97
98 class TypeIconView : public IconView {
99 typedef IconView _inherited;
100
101 public:
102 TypeIconView(const char* name);
103 virtual ~TypeIconView();
104
105 virtual void Draw(BRect updateRect);
106 virtual void GetPreferredSize(float* _width, float* _height);
107
108 protected:
109 virtual BRect BitmapRect() const;
110 };
111
112
113 class ExtensionListView : public DropTargetListView {
114 public:
115 ExtensionListView(const char* name,
116 list_view_type type = B_SINGLE_SELECTION_LIST,
117 uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE);
118 virtual ~ExtensionListView();
119
120 virtual BSize MinSize();
121
122 virtual void MessageReceived(BMessage* message);
123 virtual bool AcceptsDrag(const BMessage* message);
124
125 void SetType(BMimeType* type);
126
127 private:
128 BMimeType fType;
129 BSize fMinSize;
130 };
131
132
133 // #pragma mark -
134
135
TypeIconView(const char * name)136 TypeIconView::TypeIconView(const char* name)
137 : IconView(name)
138 {
139 ShowEmptyFrame(false);
140 SetIconSize((icon_size)48);
141 }
142
143
~TypeIconView()144 TypeIconView::~TypeIconView()
145 {
146 }
147
148
149 void
Draw(BRect updateRect)150 TypeIconView::Draw(BRect updateRect)
151 {
152 if (!IsEnabled())
153 return;
154
155 IconView::Draw(updateRect);
156
157 const char* text = NULL;
158
159 switch (IconSource()) {
160 case kNoIcon:
161 text = B_TRANSLATE("no icon");
162 break;
163 case kApplicationIcon:
164 text = B_TRANSLATE("(from application)");
165 break;
166 case kSupertypeIcon:
167 text = B_TRANSLATE("(from super type)");
168 break;
169
170 default:
171 return;
172 }
173
174 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
175 B_DISABLED_LABEL_TINT));
176 SetLowColor(ViewColor());
177
178 font_height fontHeight;
179 GetFontHeight(&fontHeight);
180
181 const BRect bitmapRect = _inherited::BitmapRect();
182 float y = fontHeight.ascent;
183 if (IconSource() == kNoIcon) {
184 // center text in the middle of the icon
185 y += (bitmapRect.Height() - fontHeight.ascent - fontHeight.descent) / 2.0f;
186 } else
187 y += bitmapRect.Height() + 3.0f;
188
189 DrawString(text, BPoint(ceilf((Bounds().Width() - StringWidth(text)) / 2.0f),
190 ceilf(y)));
191 }
192
193
194 void
GetPreferredSize(float * _width,float * _height)195 TypeIconView::GetPreferredSize(float* _width, float* _height)
196 {
197 const BRect bitmapRect = _inherited::BitmapRect();
198
199 if (_width) {
200 float a = StringWidth(B_TRANSLATE("(from application)"));
201 float b = StringWidth(B_TRANSLATE("(from super type)"));
202 float width = max_c(a, b);
203 if (width < bitmapRect.Width())
204 width = bitmapRect.Width();
205
206 *_width = ceilf(width);
207 }
208
209 if (_height) {
210 font_height fontHeight;
211 GetFontHeight(&fontHeight);
212
213 *_height = bitmapRect.Height() + 3.0f + ceilf(fontHeight.ascent
214 + fontHeight.descent);
215 }
216 }
217
218
219 BRect
BitmapRect() const220 TypeIconView::BitmapRect() const
221 {
222 const BRect bitmapRect = _inherited::BitmapRect();
223
224 if (IconSource() == kNoIcon) {
225 // this also defines the drop target area
226 font_height fontHeight;
227 GetFontHeight(&fontHeight);
228
229 float width = StringWidth(B_TRANSLATE("no icon")) + 8.0f;
230 float height = ceilf(fontHeight.ascent + fontHeight.descent) + 6.0f;
231 float x = (Bounds().Width() - width) / 2.0f;
232 float y = ceilf((bitmapRect.Height() - fontHeight.ascent - fontHeight.descent)
233 / 2.0f) - 3.0f;
234
235 return BRect(x, y, x + width, y + height);
236 }
237
238 float x = (Bounds().Width() - bitmapRect.Width()) / 2.0f;
239 return BRect(x, 0.0f, x + bitmapRect.Width(), bitmapRect.Height());
240 }
241
242
243 // #pragma mark -
244
245
ExtensionListView(const char * name,list_view_type type,uint32 flags)246 ExtensionListView::ExtensionListView(const char* name,
247 list_view_type type, uint32 flags)
248 :
249 DropTargetListView(name, type, flags)
250 {
251 }
252
253
~ExtensionListView()254 ExtensionListView::~ExtensionListView()
255 {
256 }
257
258
259 BSize
MinSize()260 ExtensionListView::MinSize()
261 {
262 if (!fMinSize.IsWidthSet()) {
263 BFont font;
264 GetFont(&font);
265 fMinSize.width = font.StringWidth(".mmmmm");
266
267 font_height height;
268 font.GetHeight(&height);
269 fMinSize.height = (height.ascent + height.descent + height.leading) * 3;
270 }
271
272 return fMinSize;
273 }
274
275
276 void
MessageReceived(BMessage * message)277 ExtensionListView::MessageReceived(BMessage* message)
278 {
279 if (message->WasDropped() && AcceptsDrag(message)) {
280 // create extension list
281 BList list;
282 entry_ref ref;
283 for (int32 index = 0; message->FindRef("refs", index, &ref) == B_OK;
284 index++) {
285 const char* point = strchr(ref.name, '.');
286 if (point != NULL && point[1])
287 list.AddItem(strdup(++point));
288 }
289
290 merge_extensions(fType, list);
291
292 // delete extension list
293 for (int32 index = list.CountItems(); index-- > 0;) {
294 free(list.ItemAt(index));
295 }
296 } else
297 DropTargetListView::MessageReceived(message);
298 }
299
300
301 bool
AcceptsDrag(const BMessage * message)302 ExtensionListView::AcceptsDrag(const BMessage* message)
303 {
304 if (fType.Type() == NULL)
305 return false;
306
307 int32 count = 0;
308 entry_ref ref;
309
310 for (int32 index = 0; message->FindRef("refs", index, &ref) == B_OK;
311 index++) {
312 const char* point = strchr(ref.name, '.');
313 if (point != NULL && point[1])
314 count++;
315 }
316
317 return count > 0;
318 }
319
320
321 void
SetType(BMimeType * type)322 ExtensionListView::SetType(BMimeType* type)
323 {
324 if (type != NULL)
325 fType.SetTo(type->Type());
326 else
327 fType.Unset();
328 }
329
330
331 // #pragma mark -
332
333
FileTypesWindow(const BMessage & settings)334 FileTypesWindow::FileTypesWindow(const BMessage& settings)
335 :
336 BWindow(_Frame(settings), B_TRANSLATE_SYSTEM_NAME("FileTypes"),
337 B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS
338 | B_AUTO_UPDATE_SIZE_LIMITS),
339 fNewTypeWindow(NULL)
340 {
341 bool showIcons;
342 bool showRule;
343 if (settings.FindBool("show_icons", &showIcons) != B_OK)
344 showIcons = true;
345 if (settings.FindBool("show_rule", &showRule) != B_OK)
346 showRule = false;
347
348 float padding = be_control_look->DefaultItemSpacing();
349 BAlignment labelAlignment = be_control_look->DefaultLabelAlignment();
350 BAlignment fullAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
351
352 // add the menu
353 BMenuBar* menuBar = new BMenuBar("");
354
355 BMenu* menu = new BMenu(B_TRANSLATE("File"));
356 BMenuItem* item = new BMenuItem(
357 B_TRANSLATE("New resource file" B_UTF8_ELLIPSIS), NULL, 'N',
358 B_COMMAND_KEY);
359 item->SetEnabled(false);
360 menu->AddItem(item);
361
362 BMenu* recentsMenu = BRecentFilesList::NewFileListMenu(
363 B_TRANSLATE("Open" B_UTF8_ELLIPSIS), NULL, NULL,
364 be_app, 10, false, NULL, kSignature);
365 item = new BMenuItem(recentsMenu, new BMessage(kMsgOpenFilePanel));
366 item->SetShortcut('O', B_COMMAND_KEY);
367 menu->AddItem(item);
368
369 menu->AddItem(new BMenuItem(
370 B_TRANSLATE("Application types" B_UTF8_ELLIPSIS),
371 new BMessage(kMsgOpenApplicationTypesWindow)));
372 menu->AddSeparatorItem();
373
374 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
375 new BMessage(B_QUIT_REQUESTED), 'Q', B_COMMAND_KEY));
376 menu->SetTargetForItems(be_app);
377 menuBar->AddItem(menu);
378
379 menu = new BMenu(B_TRANSLATE("Settings"));
380 item = new BMenuItem(B_TRANSLATE("Show icons in list"),
381 new BMessage(kMsgToggleIcons));
382 item->SetMarked(showIcons);
383 item->SetTarget(this);
384 menu->AddItem(item);
385
386 item = new BMenuItem(B_TRANSLATE("Show recognition rule"),
387 new BMessage(kMsgToggleRule));
388 item->SetMarked(showRule);
389 item->SetTarget(this);
390 menu->AddItem(item);
391 menuBar->AddItem(menu);
392 menuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
393
394 // MIME Types list
395 BButton* addTypeButton = new BButton("add",
396 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddType));
397
398 fRemoveTypeButton = new BButton("remove", B_TRANSLATE("Remove"),
399 new BMessage(kMsgRemoveType) );
400
401 fTypeListView = new MimeTypeListView("typeview", NULL, showIcons, false);
402 fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected));
403 fTypeListView->SetExplicitMinSize(BSize(200, B_SIZE_UNSET));
404
405 BScrollView* typeListScrollView = new BScrollView("scrollview",
406 fTypeListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
407
408 // "Icon" group
409
410 fIconView = new TypeIconView("icon");
411 fIconBox = new BBox("Icon BBox");
412 fIconBox->SetLabel(B_TRANSLATE("Icon"));
413 BLayoutBuilder::Group<>(fIconBox, B_VERTICAL, padding)
414 .SetInsets(padding)
415 .AddGlue(1)
416 .Add(fIconView, 3)
417 .AddGlue(1);
418
419 // "File Recognition" group
420
421 fRecognitionBox = new BBox("Recognition Box");
422 fRecognitionBox->SetLabel(B_TRANSLATE("File recognition"));
423 fRecognitionBox->SetExplicitAlignment(fullAlignment);
424
425 fExtensionLabel = new StringView(B_TRANSLATE("Extensions:"), NULL);
426 fExtensionLabel->LabelView()->SetExplicitAlignment(labelAlignment);
427
428 fAddExtensionButton = new BButton("add ext",
429 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddExtension));
430 fAddExtensionButton->SetExplicitMaxSize(
431 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
432
433 fRemoveExtensionButton = new BButton("remove ext", B_TRANSLATE("Remove"),
434 new BMessage(kMsgRemoveExtension));
435
436 fExtensionListView = new ExtensionListView("listview ext",
437 B_SINGLE_SELECTION_LIST);
438 fExtensionListView->SetSelectionMessage(
439 new BMessage(kMsgExtensionSelected));
440 fExtensionListView->SetInvocationMessage(
441 new BMessage(kMsgExtensionInvoked));
442
443 BScrollView* scrollView = new BScrollView("scrollview ext",
444 fExtensionListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
445
446 fRuleControl = new BTextControl("rule", B_TRANSLATE("Rule:"), "",
447 new BMessage(kMsgRuleEntered));
448 fRuleControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
449 fRuleControl->Hide();
450
451 BLayoutBuilder::Grid<>(fRecognitionBox, padding, padding / 2)
452 .SetInsets(padding, padding * 2, padding, padding)
453 .Add(fExtensionLabel->LabelView(), 0, 0)
454 .Add(scrollView, 0, 1, 2, 2)
455 .Add(fAddExtensionButton, 2, 1)
456 .Add(fRemoveExtensionButton, 2, 2)
457 .Add(fRuleControl, 0, 3, 3, 1);
458
459 // "Description" group
460
461 fDescriptionBox = new BBox("description BBox");
462 fDescriptionBox->SetLabel(B_TRANSLATE("Description"));
463 fDescriptionBox->SetExplicitAlignment(fullAlignment);
464
465 fInternalNameView = new StringView(B_TRANSLATE("Internal name:"), NULL);
466 fInternalNameView->SetEnabled(false);
467 fTypeNameControl = new BTextControl("type", B_TRANSLATE("Type name:"), "",
468 new BMessage(kMsgTypeEntered));
469 fDescriptionControl = new BTextControl("description",
470 B_TRANSLATE("Description:"), "", new BMessage(kMsgDescriptionEntered));
471
472 BLayoutBuilder::Grid<>(fDescriptionBox, padding / 2, padding / 2)
473 .SetInsets(padding, padding * 2, padding, padding)
474 .Add(fInternalNameView->LabelView(), 0, 0)
475 .Add(fInternalNameView->TextView(), 1, 0)
476 .Add(fTypeNameControl->CreateLabelLayoutItem(), 0, 1)
477 .Add(fTypeNameControl->CreateTextViewLayoutItem(), 1, 1, 2)
478 .Add(fDescriptionControl->CreateLabelLayoutItem(), 0, 2)
479 .Add(fDescriptionControl->CreateTextViewLayoutItem(), 1, 2, 2);
480
481 // "Preferred Application" group
482
483 fPreferredBox = new BBox("preferred BBox");
484 fPreferredBox->SetLabel(B_TRANSLATE("Preferred application"));
485
486 menu = new BPopUpMenu("preferred");
487 menu->AddItem(item = new BMenuItem(B_TRANSLATE("None"),
488 new BMessage(kMsgPreferredAppChosen)));
489 item->SetMarked(true);
490 fPreferredField = new BMenuField("preferred", (char*)NULL, menu);
491
492 fSelectButton = new BButton("select",
493 B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
494 new BMessage(kMsgSelectPreferredApp));
495
496 fSameAsButton = new BButton("same as",
497 B_TRANSLATE("Same as" B_UTF8_ELLIPSIS),
498 new BMessage(kMsgSamePreferredAppAs));
499
500 BLayoutBuilder::Group<>(fPreferredBox, B_HORIZONTAL, padding)
501 .SetInsets(padding, padding * 2, padding, padding)
502 .Add(fPreferredField)
503 .Add(fSelectButton)
504 .Add(fSameAsButton);
505
506 // "Extra Attributes" group
507
508 fAttributeBox = new BBox("Attribute Box");
509 fAttributeBox->SetLabel(B_TRANSLATE("Extra attributes"));
510
511 fAddAttributeButton = new BButton("add attr",
512 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddAttribute));
513 fAddAttributeButton->SetExplicitMaxSize(
514 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
515
516 fRemoveAttributeButton = new BButton("remove attr", B_TRANSLATE("Remove"),
517 new BMessage(kMsgRemoveAttribute));
518 fRemoveAttributeButton->SetExplicitMaxSize(
519 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
520
521 fMoveUpAttributeButton = new BButton("move up attr", B_TRANSLATE("Move up"),
522 new BMessage(kMsgMoveUpAttribute));
523 fMoveUpAttributeButton->SetExplicitMaxSize(
524 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
525 fMoveDownAttributeButton = new BButton("move down attr",
526 B_TRANSLATE("Move down"), new BMessage(kMsgMoveDownAttribute));
527 fMoveDownAttributeButton->SetExplicitMaxSize(
528 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
529
530 fAttributeListView = new AttributeListView("listview attr");
531 fAttributeListView->SetSelectionMessage(
532 new BMessage(kMsgAttributeSelected));
533 fAttributeListView->SetInvocationMessage(
534 new BMessage(kMsgAttributeInvoked));
535
536 BScrollView* attributesScroller = new BScrollView("scrollview attr",
537 fAttributeListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
538
539 BLayoutBuilder::Group<>(fAttributeBox, B_HORIZONTAL, padding)
540 .SetInsets(padding, padding * 2, padding, padding)
541 .Add(attributesScroller, 1.0f)
542 .AddGroup(B_VERTICAL, padding / 2, 0.0f)
543 .SetInsets(0)
544 .Add(fAddAttributeButton)
545 .Add(fRemoveAttributeButton)
546 .AddStrut(padding)
547 .Add(fMoveUpAttributeButton)
548 .Add(fMoveDownAttributeButton)
549 .AddGlue();
550
551 fMainSplitView = new BSplitView(B_HORIZONTAL, floorf(padding / 2));
552
553 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
554 .SetInsets(0)
555 .Add(menuBar)
556 .AddGroup(B_HORIZONTAL, 0)
557 .SetInsets(B_USE_WINDOW_SPACING)
558 .AddSplit(fMainSplitView)
559 .AddGroup(B_VERTICAL, padding)
560 .Add(typeListScrollView)
561 .AddGroup(B_HORIZONTAL, padding)
562 .Add(addTypeButton)
563 .Add(fRemoveTypeButton)
564 .AddGlue()
565 .End()
566 .End()
567 // Right side
568 .AddGroup(B_VERTICAL, padding)
569 .AddGroup(B_HORIZONTAL, padding)
570 .Add(fIconBox, 1)
571 .Add(fRecognitionBox, 3)
572 .End()
573 .Add(fDescriptionBox)
574 .Add(fPreferredBox)
575 .Add(fAttributeBox, 5);
576
577 _SetType(NULL);
578 _ShowSnifferRule(showRule);
579
580 float leftWeight;
581 float rightWeight;
582 if (settings.FindFloat("left_split_weight", &leftWeight) != B_OK
583 || settings.FindFloat("right_split_weight", &rightWeight) != B_OK) {
584 leftWeight = 0.2;
585 rightWeight = 1.0 - leftWeight;
586 }
587 fMainSplitView->SetItemWeight(0, leftWeight, false);
588 fMainSplitView->SetItemWeight(1, rightWeight, true);
589
590 BMimeType::StartWatching(this);
591 }
592
593
~FileTypesWindow()594 FileTypesWindow::~FileTypesWindow()
595 {
596 BMimeType::StopWatching(this);
597 }
598
599
600 void
MessageReceived(BMessage * message)601 FileTypesWindow::MessageReceived(BMessage* message)
602 {
603 switch (message->what) {
604 case B_SIMPLE_DATA:
605 {
606 type_code type;
607 if (message->GetInfo("refs", &type) == B_OK
608 && type == B_REF_TYPE) {
609 be_app->PostMessage(message);
610 }
611 break;
612 }
613
614 case kMsgToggleIcons:
615 {
616 BMenuItem* item;
617 if (message->FindPointer("source", (void **)&item) != B_OK)
618 break;
619
620 item->SetMarked(!fTypeListView->IsShowingIcons());
621 fTypeListView->ShowIcons(item->IsMarked());
622
623 // update settings
624 BMessage update(kMsgSettingsChanged);
625 update.AddBool("show_icons", item->IsMarked());
626 be_app_messenger.SendMessage(&update);
627 break;
628 }
629
630 case kMsgToggleRule:
631 {
632 BMenuItem* item;
633 if (message->FindPointer("source", (void **)&item) != B_OK)
634 break;
635
636 item->SetMarked(fRuleControl->IsHidden());
637 _ShowSnifferRule(item->IsMarked());
638
639 // update settings
640 BMessage update(kMsgSettingsChanged);
641 update.AddBool("show_rule", item->IsMarked());
642 be_app_messenger.SendMessage(&update);
643 break;
644 }
645
646 case kMsgTypeSelected:
647 {
648 int32 index;
649 if (message->FindInt32("index", &index) == B_OK) {
650 MimeTypeItem* item
651 = (MimeTypeItem*)fTypeListView->ItemAt(index);
652 if (item != NULL) {
653 BMimeType type(item->Type());
654 _SetType(&type);
655 } else
656 _SetType(NULL);
657 }
658 break;
659 }
660
661 case kMsgAddType:
662 if (fNewTypeWindow == NULL) {
663 fNewTypeWindow
664 = new NewFileTypeWindow(this, fCurrentType.Type());
665 fNewTypeWindow->Show();
666 } else
667 fNewTypeWindow->Activate();
668 break;
669
670 case kMsgNewTypeWindowClosed:
671 fNewTypeWindow = NULL;
672 break;
673
674 case kMsgRemoveType:
675 {
676 if (fCurrentType.Type() == NULL)
677 break;
678
679 BAlert* alert;
680 if (fCurrentType.IsSupertypeOnly()) {
681 alert = new BPrivate::OverrideAlert(
682 B_TRANSLATE("FileTypes request"),
683 B_TRANSLATE("Removing a super type cannot be reverted.\n"
684 "All file types that belong to this super type "
685 "will be lost!\n\n"
686 "Are you sure you want to do this? To remove the whole "
687 "group, hold down the Shift key and press \"Remove\"."),
688 B_TRANSLATE("Remove"), B_SHIFT_KEY, B_TRANSLATE("Cancel"),
689 0, NULL, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
690 alert->SetShortcut(1, B_ESCAPE);
691 } else {
692 alert = new BAlert(B_TRANSLATE("FileTypes request"),
693 B_TRANSLATE("Removing a file type cannot be reverted.\n"
694 "Are you sure you want to remove it?"),
695 B_TRANSLATE("Remove"), B_TRANSLATE("Cancel"),
696 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
697 alert->SetShortcut(1, B_ESCAPE);
698 }
699 if (alert->Go())
700 break;
701
702 status_t status = fCurrentType.Delete();
703 if (status != B_OK) {
704 fprintf(stderr, B_TRANSLATE(
705 "Could not remove file type: %s\n"), strerror(status));
706 }
707 break;
708 }
709
710 case kMsgSelectNewType:
711 {
712 const char* type;
713 if (message->FindString("type", &type) == B_OK)
714 fTypeListView->SelectNewType(type);
715 break;
716 }
717
718 // File Recognition group
719
720 case kMsgExtensionSelected:
721 {
722 int32 index;
723 if (message->FindInt32("index", &index) == B_OK) {
724 BStringItem* item
725 = (BStringItem*)fExtensionListView->ItemAt(index);
726 fRemoveExtensionButton->SetEnabled(item != NULL);
727 }
728 break;
729 }
730
731 case kMsgExtensionInvoked:
732 {
733 if (fCurrentType.Type() == NULL)
734 break;
735
736 int32 index;
737 if (message->FindInt32("index", &index) == B_OK) {
738 BStringItem* item
739 = (BStringItem*)fExtensionListView->ItemAt(index);
740 if (item == NULL)
741 break;
742
743 BWindow* window
744 = new ExtensionWindow(this, fCurrentType, item->Text());
745 window->Show();
746 }
747 break;
748 }
749
750 case kMsgAddExtension:
751 {
752 if (fCurrentType.Type() == NULL)
753 break;
754
755 BWindow* window = new ExtensionWindow(this, fCurrentType, NULL);
756 window->Show();
757 break;
758 }
759
760 case kMsgRemoveExtension:
761 {
762 int32 index = fExtensionListView->CurrentSelection();
763 if (index < 0 || fCurrentType.Type() == NULL)
764 break;
765
766 BMessage extensions;
767 if (fCurrentType.GetFileExtensions(&extensions) == B_OK) {
768 extensions.RemoveData("extensions", index);
769 fCurrentType.SetFileExtensions(&extensions);
770 }
771 break;
772 }
773
774 case kMsgRuleEntered:
775 {
776 // check rule
777 BString parseError;
778 if (BMimeType::CheckSnifferRule(fRuleControl->Text(),
779 &parseError) != B_OK) {
780 parseError.Prepend(
781 B_TRANSLATE("Recognition rule is not valid:\n\n"));
782 error_alert(parseError.String());
783 } else
784 fCurrentType.SetSnifferRule(fRuleControl->Text());
785 break;
786 }
787
788 // Description group
789
790 case kMsgTypeEntered:
791 {
792 fCurrentType.SetShortDescription(fTypeNameControl->Text());
793 break;
794 }
795
796 case kMsgDescriptionEntered:
797 {
798 fCurrentType.SetLongDescription(fDescriptionControl->Text());
799 break;
800 }
801
802 // Preferred Application group
803
804 case kMsgPreferredAppChosen:
805 {
806 const char* signature;
807 if (message->FindString("signature", &signature) != B_OK)
808 signature = NULL;
809
810 fCurrentType.SetPreferredApp(signature);
811 break;
812 }
813
814 case kMsgSelectPreferredApp:
815 {
816 BMessage panel(kMsgOpenFilePanel);
817 panel.AddString("title",
818 B_TRANSLATE("Select preferred application"));
819 panel.AddInt32("message", kMsgPreferredAppOpened);
820 panel.AddMessenger("target", this);
821
822 be_app_messenger.SendMessage(&panel);
823 break;
824 }
825 case kMsgPreferredAppOpened:
826 _AdoptPreferredApplication(message, false);
827 break;
828
829 case kMsgSamePreferredAppAs:
830 {
831 BMessage panel(kMsgOpenFilePanel);
832 panel.AddString("title",
833 B_TRANSLATE("Select same preferred application as"));
834 panel.AddInt32("message", kMsgSamePreferredAppAsOpened);
835 panel.AddMessenger("target", this);
836 panel.AddBool("allowDirs", true);
837
838 be_app_messenger.SendMessage(&panel);
839 break;
840 }
841 case kMsgSamePreferredAppAsOpened:
842 _AdoptPreferredApplication(message, true);
843 break;
844
845 // Extra Attributes group
846
847 case kMsgAttributeSelected:
848 {
849 int32 index;
850 if (message->FindInt32("index", &index) == B_OK) {
851 AttributeItem* item
852 = (AttributeItem*)fAttributeListView->ItemAt(index);
853 fRemoveAttributeButton->SetEnabled(item != NULL);
854 fMoveUpAttributeButton->SetEnabled(index > 0);
855 fMoveDownAttributeButton->SetEnabled(index >= 0
856 && index < fAttributeListView->CountItems() - 1);
857 }
858 break;
859 }
860
861 case kMsgAttributeInvoked:
862 {
863 if (fCurrentType.Type() == NULL)
864 break;
865
866 int32 index;
867 if (message->FindInt32("index", &index) == B_OK) {
868 AttributeItem* item
869 = (AttributeItem*)fAttributeListView->ItemAt(index);
870 if (item == NULL)
871 break;
872
873 BWindow* window = new AttributeWindow(this, fCurrentType,
874 item);
875 window->Show();
876 }
877 break;
878 }
879
880 case kMsgAddAttribute:
881 {
882 if (fCurrentType.Type() == NULL)
883 break;
884
885 BWindow* window = new AttributeWindow(this, fCurrentType, NULL);
886 window->Show();
887 break;
888 }
889
890 case kMsgRemoveAttribute:
891 {
892 int32 index = fAttributeListView->CurrentSelection();
893 if (index < 0 || fCurrentType.Type() == NULL)
894 break;
895
896 BMessage attributes;
897 if (fCurrentType.GetAttrInfo(&attributes) == B_OK) {
898 for (uint32 i = 0; i <
899 sizeof(kAttributeNames) / sizeof(kAttributeNames[0]);
900 i++) {
901 attributes.RemoveData(kAttributeNames[i], index);
902 }
903
904 fCurrentType.SetAttrInfo(&attributes);
905 }
906 break;
907 }
908
909 case kMsgMoveUpAttribute:
910 {
911 int32 index = fAttributeListView->CurrentSelection();
912 if (index < 1 || fCurrentType.Type() == NULL)
913 break;
914
915 _MoveUpAttributeIndex(index);
916 break;
917 }
918
919 case kMsgMoveDownAttribute:
920 {
921 int32 index = fAttributeListView->CurrentSelection();
922 if (index < 0 || index == fAttributeListView->CountItems() - 1
923 || fCurrentType.Type() == NULL) {
924 break;
925 }
926
927 _MoveUpAttributeIndex(index + 1);
928 break;
929 }
930
931 case B_META_MIME_CHANGED:
932 {
933 const char* type;
934 int32 which;
935 if (message->FindString("be:type", &type) != B_OK
936 || message->FindInt32("be:which", &which) != B_OK)
937 break;
938
939 if (fCurrentType.Type() == NULL)
940 break;
941
942 if (!strcasecmp(fCurrentType.Type(), type)) {
943 if (which != B_MIME_TYPE_DELETED)
944 _SetType(&fCurrentType, which);
945 else
946 _SetType(NULL);
947 } else {
948 // this change could still affect our current type
949
950 if (which == B_MIME_TYPE_DELETED
951 || which == B_SUPPORTED_TYPES_CHANGED
952 || which == B_PREFERRED_APP_CHANGED) {
953 _UpdatePreferredApps(&fCurrentType);
954 }
955 }
956 break;
957 }
958
959 default:
960 BWindow::MessageReceived(message);
961 }
962 }
963
964
965 void
SelectType(const char * type)966 FileTypesWindow::SelectType(const char* type)
967 {
968 fTypeListView->SelectType(type);
969 }
970
971
972 bool
QuitRequested()973 FileTypesWindow::QuitRequested()
974 {
975 BMessage update(kMsgSettingsChanged);
976 update.AddRect("file_types_frame", Frame());
977 update.AddFloat("left_split_weight", fMainSplitView->ItemWeight((int32)0));
978 update.AddFloat("right_split_weight", fMainSplitView->ItemWeight(1));
979 be_app_messenger.SendMessage(&update);
980
981 be_app->PostMessage(kMsgTypesWindowClosed);
982 return true;
983 }
984
985
986 void
PlaceSubWindow(BWindow * window)987 FileTypesWindow::PlaceSubWindow(BWindow* window)
988 {
989 window->MoveTo(Frame().left + (Frame().Width() - window->Frame().Width())
990 / 2.0f, Frame().top + (Frame().Height() - window->Frame().Height())
991 / 2.0f);
992 }
993
994
995 // #pragma mark - private
996
997
998 BRect
_Frame(const BMessage & settings) const999 FileTypesWindow::_Frame(const BMessage& settings) const
1000 {
1001 BRect rect;
1002 if (settings.FindRect("file_types_frame", &rect) == B_OK)
1003 return rect;
1004
1005 return BRect(80.0f, 80.0f, 0.0f, 0.0f);
1006 }
1007
1008
1009 void
_ShowSnifferRule(bool show)1010 FileTypesWindow::_ShowSnifferRule(bool show)
1011 {
1012 if (fRuleControl->IsHidden() == !show)
1013 return;
1014
1015 if (!show)
1016 fRuleControl->Hide();
1017 else
1018 fRuleControl->Show();
1019 }
1020
1021
1022 void
_UpdateExtensions(BMimeType * type)1023 FileTypesWindow::_UpdateExtensions(BMimeType* type)
1024 {
1025 // clear list
1026
1027 for (int32 i = fExtensionListView->CountItems(); i-- > 0;) {
1028 delete fExtensionListView->ItemAt(i);
1029 }
1030 fExtensionListView->MakeEmpty();
1031
1032 // fill it again
1033
1034 if (type == NULL)
1035 return;
1036
1037 BMessage extensions;
1038 if (type->GetFileExtensions(&extensions) != B_OK)
1039 return;
1040
1041 const char* extension;
1042 int32 i = 0;
1043 while (extensions.FindString("extensions", i++, &extension) == B_OK) {
1044 char dotExtension[B_FILE_NAME_LENGTH];
1045 snprintf(dotExtension, B_FILE_NAME_LENGTH, ".%s", extension);
1046
1047 fExtensionListView->AddItem(new BStringItem(dotExtension));
1048 }
1049 }
1050
1051
1052 void
_AdoptPreferredApplication(BMessage * message,bool sameAs)1053 FileTypesWindow::_AdoptPreferredApplication(BMessage* message, bool sameAs)
1054 {
1055 if (fCurrentType.Type() == NULL)
1056 return;
1057
1058 BString preferred;
1059 if (retrieve_preferred_app(message, sameAs, fCurrentType.Type(), preferred)
1060 != B_OK) {
1061 return;
1062 }
1063
1064 status_t status = fCurrentType.SetPreferredApp(preferred.String());
1065 if (status != B_OK)
1066 error_alert(B_TRANSLATE("Could not set preferred application"),
1067 status);
1068 }
1069
1070
1071 void
_UpdatePreferredApps(BMimeType * type)1072 FileTypesWindow::_UpdatePreferredApps(BMimeType* type)
1073 {
1074 update_preferred_app_menu(fPreferredField->Menu(), type,
1075 kMsgPreferredAppChosen);
1076 }
1077
1078
1079 void
_UpdateIcon(BMimeType * type)1080 FileTypesWindow::_UpdateIcon(BMimeType* type)
1081 {
1082 if (type != NULL)
1083 fIconView->SetTo(*type);
1084 else
1085 fIconView->Unset();
1086 }
1087
1088
1089 void
_SetType(BMimeType * type,int32 forceUpdate)1090 FileTypesWindow::_SetType(BMimeType* type, int32 forceUpdate)
1091 {
1092 bool enabled = type != NULL;
1093
1094 // update controls
1095
1096 if (type != NULL) {
1097 if (fCurrentType == *type) {
1098 if (!forceUpdate)
1099 return;
1100 } else
1101 forceUpdate = B_EVERYTHING_CHANGED;
1102
1103 if (&fCurrentType != type)
1104 fCurrentType.SetTo(type->Type());
1105
1106 fInternalNameView->SetText(type->Type());
1107
1108 char description[B_MIME_TYPE_LENGTH];
1109
1110 if ((forceUpdate & B_SHORT_DESCRIPTION_CHANGED) != 0) {
1111 if (type->GetShortDescription(description) != B_OK)
1112 description[0] = '\0';
1113 fTypeNameControl->SetText(description);
1114 }
1115
1116 if ((forceUpdate & B_LONG_DESCRIPTION_CHANGED) != 0) {
1117 if (type->GetLongDescription(description) != B_OK)
1118 description[0] = '\0';
1119 fDescriptionControl->SetText(description);
1120 }
1121
1122 if ((forceUpdate & B_SNIFFER_RULE_CHANGED) != 0) {
1123 BString rule;
1124 if (type->GetSnifferRule(&rule) != B_OK)
1125 rule = "";
1126 fRuleControl->SetText(rule.String());
1127 }
1128
1129 fExtensionListView->SetType(&fCurrentType);
1130 } else {
1131 fCurrentType.Unset();
1132 fInternalNameView->SetText(NULL);
1133 fTypeNameControl->SetText(NULL);
1134 fDescriptionControl->SetText(NULL);
1135 fRuleControl->SetText(NULL);
1136 fPreferredField->Menu()->ItemAt(0)->SetMarked(true);
1137 fExtensionListView->SetType(NULL);
1138 fAttributeListView->SetTo(NULL);
1139 }
1140
1141 if ((forceUpdate & B_FILE_EXTENSIONS_CHANGED) != 0)
1142 _UpdateExtensions(type);
1143
1144 if ((forceUpdate & B_PREFERRED_APP_CHANGED) != 0)
1145 _UpdatePreferredApps(type);
1146
1147 if ((forceUpdate & (B_ICON_CHANGED | B_PREFERRED_APP_CHANGED)) != 0)
1148 _UpdateIcon(type);
1149
1150 if ((forceUpdate & B_ATTR_INFO_CHANGED) != 0)
1151 fAttributeListView->SetTo(type);
1152
1153 // enable/disable controls
1154
1155 fIconView->SetEnabled(enabled);
1156
1157 fInternalNameView->SetEnabled(enabled);
1158 fTypeNameControl->SetEnabled(enabled);
1159 fDescriptionControl->SetEnabled(enabled);
1160 fPreferredField->SetEnabled(enabled);
1161
1162 fRemoveTypeButton->SetEnabled(enabled);
1163
1164 fSelectButton->SetEnabled(enabled);
1165 fSameAsButton->SetEnabled(enabled);
1166
1167 fExtensionLabel->SetEnabled(enabled);
1168 fAddExtensionButton->SetEnabled(enabled);
1169 fRemoveExtensionButton->SetEnabled(false);
1170 fRuleControl->SetEnabled(enabled);
1171
1172 fAddAttributeButton->SetEnabled(enabled);
1173 fRemoveAttributeButton->SetEnabled(false);
1174 fMoveUpAttributeButton->SetEnabled(false);
1175 fMoveDownAttributeButton->SetEnabled(false);
1176 }
1177
1178
1179 void
_MoveUpAttributeIndex(int32 index)1180 FileTypesWindow::_MoveUpAttributeIndex(int32 index)
1181 {
1182 BMessage attributes;
1183 if (fCurrentType.GetAttrInfo(&attributes) != B_OK)
1184 return;
1185
1186 // Iterate over all known attribute fields, and for each field,
1187 // iterate over all fields of the same name and build a copy
1188 // of the attributes message with the field at the given index swapped
1189 // with the previous field.
1190 BMessage resortedAttributes;
1191 for (uint32 i = 0; i <
1192 sizeof(kAttributeNames) / sizeof(kAttributeNames[0]);
1193 i++) {
1194
1195 type_code type;
1196 int32 count;
1197 bool isFixedSize;
1198 if (attributes.GetInfo(kAttributeNames[i], &type, &count,
1199 &isFixedSize) != B_OK) {
1200 // Apparently the message does not contain this name,
1201 // so just ignore this attribute name.
1202 // NOTE: This shows that the attribute description is
1203 // too fragile. It would have been better to pack each
1204 // attribute description into a separate BMessage.
1205 continue;
1206 }
1207
1208 for (int32 j = 0; j < count; j++) {
1209 const void* data;
1210 ssize_t size;
1211 int32 originalIndex;
1212 if (j == index - 1)
1213 originalIndex = j + 1;
1214 else if (j == index)
1215 originalIndex = j - 1;
1216 else
1217 originalIndex = j;
1218 attributes.FindData(kAttributeNames[i], type,
1219 originalIndex, &data, &size);
1220 if (j == 0) {
1221 resortedAttributes.AddData(kAttributeNames[i], type,
1222 data, size, isFixedSize);
1223 } else {
1224 resortedAttributes.AddData(kAttributeNames[i], type,
1225 data, size);
1226 }
1227 }
1228 }
1229
1230 // Setting it directly on the type will trigger an update of the GUI as
1231 // well. TODO: FileTypes is heavily descructive, it should use an
1232 // Undo/Redo stack.
1233 fCurrentType.SetAttrInfo(&resortedAttributes);
1234 }
1235
1236
1237