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