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