xref: /haiku/src/preferences/filetypes/ApplicationTypeWindow.cpp (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
1 /*
2  * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ApplicationTypeWindow.h"
8 #include "DropTargetListView.h"
9 #include "FileTypes.h"
10 #include "IconView.h"
11 #include "PreferredAppMenu.h"
12 #include "StringView.h"
13 #include "TypeListWindow.h"
14 
15 #include <AppFileInfo.h>
16 #include <Application.h>
17 #include <Bitmap.h>
18 #include <Box.h>
19 #include <Button.h>
20 #include <CheckBox.h>
21 #include <File.h>
22 #include <ListView.h>
23 #include <MenuBar.h>
24 #include <MenuField.h>
25 #include <MenuItem.h>
26 #include <Mime.h>
27 #include <NodeInfo.h>
28 #include <PopUpMenu.h>
29 #include <RadioButton.h>
30 #include <Roster.h>
31 #include <ScrollView.h>
32 #include <TextControl.h>
33 
34 #include <ctype.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 
39 
40 const uint32 kMsgSave = 'save';
41 const uint32 kMsgFlagsChanged = 'flgc';
42 
43 const uint32 kMsgTypeSelected = 'tpsl';
44 const uint32 kMsgAddType = 'adtp';
45 const uint32 kMsgTypeAdded = 'tpad';
46 const uint32 kMsgRemoveType = 'rmtp';
47 
48 
49 class SupportedTypeItem : public BStringItem {
50 	public:
51 		SupportedTypeItem(const char* type);
52 		~SupportedTypeItem();
53 
54 		const char* Type() const { return fType.String(); }
55 		::Icon& Icon() { return fIcon; }
56 		void SetIcon(::Icon* icon);
57 		void SetIcon(entry_ref& ref, const char* type);
58 
59 		static int Compare(const void* _a, const void* _b);
60 
61 	private:
62 		BString	fType;
63 		::Icon	fIcon;
64 };
65 
66 class SupportedTypeListView : public DropTargetListView {
67 	public:
68 		SupportedTypeListView(BRect frame, const char* name,
69 			list_view_type type = B_SINGLE_SELECTION_LIST,
70 			uint32 resizeMask = B_FOLLOW_LEFT | B_FOLLOW_TOP,
71 			uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE);
72 		virtual ~SupportedTypeListView();
73 
74 		virtual void MessageReceived(BMessage* message);
75 		virtual bool AcceptsDrag(const BMessage* message);
76 };
77 
78 
79 SupportedTypeItem::SupportedTypeItem(const char* type)
80 	: BStringItem(type),
81 	fType(type)
82 {
83 	BMimeType mimeType(type);
84 
85 	char description[B_MIME_TYPE_LENGTH];
86 	if (mimeType.GetShortDescription(description) == B_OK && description[0])
87 		SetText(description);
88 }
89 
90 
91 SupportedTypeItem::~SupportedTypeItem()
92 {
93 }
94 
95 
96 void
97 SupportedTypeItem::SetIcon(::Icon* icon)
98 {
99 	if (icon != NULL)
100 		fIcon = *icon;
101 	else
102 		fIcon.Unset();
103 }
104 
105 
106 void
107 SupportedTypeItem::SetIcon(entry_ref& ref, const char* type)
108 {
109 	fIcon.SetTo(ref, type);
110 }
111 
112 
113 /*static*/
114 int
115 SupportedTypeItem::Compare(const void* _a, const void* _b)
116 {
117 	const SupportedTypeItem* a = *(const SupportedTypeItem**)_a;
118 	const SupportedTypeItem* b = *(const SupportedTypeItem**)_b;
119 
120 	int compare = strcasecmp(a->Text(), b->Text());
121 	if (compare != 0)
122 		return compare;
123 
124 	return strcasecmp(a->Type(), b->Type());
125 }
126 
127 
128 //	#pragma mark -
129 
130 
131 SupportedTypeListView::SupportedTypeListView(BRect frame, const char* name,
132 		list_view_type type, uint32 resizeMask, uint32 flags)
133 	: DropTargetListView(frame, name, type, resizeMask, flags)
134 {
135 }
136 
137 
138 SupportedTypeListView::~SupportedTypeListView()
139 {
140 }
141 
142 
143 void
144 SupportedTypeListView::MessageReceived(BMessage* message)
145 {
146 	if (message->WasDropped() && AcceptsDrag(message)) {
147 		// Add unique types
148 		entry_ref ref;
149 		for (int32 index = 0; message->FindRef("refs", index++, &ref) == B_OK; ) {
150 			BNode node(&ref);
151 			BNodeInfo info(&node);
152 			if (node.InitCheck() != B_OK || info.InitCheck() != B_OK)
153 				continue;
154 
155 			// TODO: we could identify the file in case it doesn't have a type...
156 			char type[B_MIME_TYPE_LENGTH];
157 			if (info.GetType(type) != B_OK)
158 				continue;
159 
160 			// check if that type is already in our list
161 			bool found = false;
162 			for (int32 i = CountItems(); i-- > 0;) {
163 				SupportedTypeItem* item = (SupportedTypeItem*)ItemAt(i);
164 				if (!strcmp(item->Text(), type)) {
165 					found = true;
166 					break;
167 				}
168 			}
169 
170 			if (!found) {
171 				// add type
172 				AddItem(new SupportedTypeItem(type));
173 			}
174 		}
175 
176 		SortItems(&SupportedTypeItem::Compare);
177 	} else
178 		DropTargetListView::MessageReceived(message);
179 }
180 
181 
182 bool
183 SupportedTypeListView::AcceptsDrag(const BMessage* message)
184 {
185 	type_code type;
186 	return message->GetInfo("refs", &type) == B_OK && type == B_REF_TYPE;
187 }
188 
189 
190 //	#pragma mark -
191 
192 
193 ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entry)
194 	: BWindow(BRect(0.0f, 0.0f, 250.0f, 340.0f).OffsetBySelf(position),
195 		"Application Type", B_TITLED_WINDOW,
196 		B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS)
197 {
198 	// add the menu
199 
200 	BMenuBar* menuBar = new BMenuBar(BRect(0, 0, 0, 0), NULL);
201 	AddChild(menuBar);
202 
203 	BMenu* menu = new BMenu("File");
204 	menu->AddItem(new BMenuItem("Save", new BMessage(kMsgSave), 'S', B_COMMAND_KEY));
205 	BMenuItem* item;
206 	menu->AddItem(item = new BMenuItem("Save Into Resource File" B_UTF8_ELLIPSIS,
207 		NULL));
208 	item->SetEnabled(false);
209 
210 	menu->AddSeparatorItem();
211 	menu->AddItem(new BMenuItem("Close", new BMessage(B_QUIT_REQUESTED),
212 		'W', B_COMMAND_KEY));
213 	menuBar->AddItem(menu);
214 
215 	// Top view and signature
216 
217 	BRect rect = Bounds();
218 	rect.top = menuBar->Bounds().Height() + 1.0f;
219 	BView* topView = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW);
220 	topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
221 	AddChild(topView);
222 
223 	rect = topView->Bounds().InsetByCopy(8.0f, 8.0f);
224 	fSignatureControl = new BTextControl(rect, "signature", "Signature:", NULL,
225 		NULL, B_FOLLOW_LEFT_RIGHT);
226 	fSignatureControl->SetDivider(fSignatureControl->StringWidth(
227 		fSignatureControl->Label()) + 4.0f);
228 	float width, height;
229 	fSignatureControl->GetPreferredSize(&width, &height);
230 	fSignatureControl->ResizeTo(rect.Width(), height);
231 	topView->AddChild(fSignatureControl);
232 
233 	// filter out invalid characters that can't be part of a MIME type name
234 	BTextView* textView = fSignatureControl->TextView();
235 	textView->SetMaxBytes(B_MIME_TYPE_LENGTH);
236 	const char* disallowedCharacters = "<>@,;:\"()[]?=";
237 	for (int32 i = 0; disallowedCharacters[i]; i++) {
238 		textView->DisallowChar(disallowedCharacters[i]);
239 	}
240 
241 	// "Application Flags" group
242 
243 	BFont font(be_bold_font);
244 	font_height fontHeight;
245 	font.GetHeight(&fontHeight);
246 
247 	width = font.StringWidth("Icon") + 16.0f;
248 	if (width < B_LARGE_ICON + 16.0f)
249 		width = B_LARGE_ICON + 16.0f;
250 
251 	rect.top = fSignatureControl->Frame().bottom + 4.0f;
252 	rect.bottom = rect.top + 100.0f;
253 	rect.right -= width + 8.0f;
254 	BBox* box = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT);
255 	topView->AddChild(box);
256 
257 	fFlagsCheckBox = new BCheckBox(rect, "flags", "Application Flags",
258 		new BMessage(kMsgFlagsChanged));
259 	fFlagsCheckBox->SetValue(B_CONTROL_ON);
260 	fFlagsCheckBox->ResizeToPreferred();
261 	box->SetLabel(fFlagsCheckBox);
262 
263 	rect.top = fFlagsCheckBox->Bounds().Height() + 4.0f;
264 	fSingleLaunchButton = new BRadioButton(rect, "single", "Single Launch", NULL);
265 	fSingleLaunchButton->ResizeToPreferred();
266 	box->AddChild(fSingleLaunchButton);
267 
268 	rect.OffsetBy(0.0f, fSingleLaunchButton->Bounds().Height() + 0.0f);
269 	fMultipleLaunchButton = new BRadioButton(rect, "multiple", "Multiple Launch", NULL);
270 	fMultipleLaunchButton->ResizeToPreferred();
271 	box->AddChild(fMultipleLaunchButton);
272 
273 	rect.OffsetBy(0.0f, fSingleLaunchButton->Bounds().Height() + 0.0f);
274 	fExclusiveLaunchButton = new BRadioButton(rect, "exclusive", "Exclusive Launch", NULL);
275 	fExclusiveLaunchButton->ResizeToPreferred();
276 	box->AddChild(fExclusiveLaunchButton);
277 
278 	rect.top = fSingleLaunchButton->Frame().top;
279 	rect.left = fExclusiveLaunchButton->Frame().right + 4.0f;
280 	fArgsOnlyCheckBox = new BCheckBox(rect, "args only", "Args Only", NULL);
281 	fArgsOnlyCheckBox->ResizeToPreferred();
282 	box->AddChild(fArgsOnlyCheckBox);
283 
284 	rect.top += fArgsOnlyCheckBox->Bounds().Height();
285 	fBackgroundAppCheckBox = new BCheckBox(rect, "background", "Background App", NULL);
286 	fBackgroundAppCheckBox->ResizeToPreferred();
287 	box->AddChild(fBackgroundAppCheckBox);
288 
289 	box->ResizeTo(box->Bounds().Width(), fExclusiveLaunchButton->Frame().bottom + 8.0f);
290 
291 	// "Icon" group
292 
293 	rect = box->Frame();
294 #ifdef __HAIKU__
295 	rect.top += box->TopBorderOffset();
296 #endif
297 	rect.left = rect.right + 8.0f;
298 	rect.right += width + 8.0f;
299 	float iconBoxWidth = rect.Width();
300 	box = new BBox(rect, NULL, B_FOLLOW_RIGHT | B_FOLLOW_TOP);
301 	box->SetLabel("Icon");
302 #ifdef __HAIKU__
303 	box->MoveBy(0.0f, -box->TopBorderOffset());
304 	box->ResizeBy(0.0f, box->TopBorderOffset());
305 #endif
306 	topView->AddChild(box);
307 
308 	rect = BRect(8.0f, 0.0f, 7.0f + B_LARGE_ICON, B_LARGE_ICON - 1.0f);
309 #ifdef __HAIKU__
310 	rect.OffsetBy(0.0f, (box->Bounds().Height() + box->TopBorderOffset()
311 		- rect.Height()) / 2.0f);
312 #else
313 	rect.OffsetBy(0.0f, (box->Bounds().Height() - rect.Height()) / 2.0f);
314 #endif
315 	if (rect.top < fontHeight.ascent + fontHeight.descent + 4.0f)
316 		rect.top = fontHeight.ascent + fontHeight.descent + 4.0f;
317 	fIconView = new IconView(rect, "icon");
318 	box->AddChild(fIconView);
319 
320 	// "Supported Types" group
321 
322 	rect.top = box->Frame().bottom + 8.0f;
323 	rect.bottom = rect.top + box->Bounds().Height();
324 	rect.left = 8.0f;
325 	rect.right = Bounds().Width() - 8.0f;
326 	BBox* typeBox = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT);
327 	typeBox->SetLabel("Supported Types");
328 	topView->AddChild(typeBox);
329 
330 	rect = typeBox->Bounds().InsetByCopy(8.0f, 6.0f);
331 	rect.top += ceilf(fontHeight.ascent);
332 	fAddTypeButton = new BButton(rect, "add type", "Add" B_UTF8_ELLIPSIS,
333 		new BMessage(kMsgAddType), B_FOLLOW_RIGHT);
334 	fAddTypeButton->ResizeToPreferred();
335 	fAddTypeButton->MoveBy(rect.right - fAddTypeButton->Bounds().Width()
336 		- B_LARGE_ICON - 16.0f, 0.0f);
337 	typeBox->AddChild(fAddTypeButton);
338 
339 	rect = fAddTypeButton->Frame();
340 	rect.OffsetBy(0, rect.Height() + 4.0f);
341 	fRemoveTypeButton = new BButton(rect, "remove type", "Remove",
342 		new BMessage(kMsgRemoveType), B_FOLLOW_RIGHT);
343 	typeBox->AddChild(fRemoveTypeButton);
344 
345 	rect.right = rect.left - 10.0f - B_V_SCROLL_BAR_WIDTH;
346 	rect.left = 10.0f;
347 	rect.top = 8.0f + ceilf(fontHeight.ascent);
348 	rect.bottom -= 2.0f;
349 		// take scrollview border into account
350 	fTypeListView = new SupportedTypeListView(rect, "type listview",
351 		B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL);
352 	fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected));
353 
354 	BScrollView* scrollView = new BScrollView("type scrollview", fTypeListView,
355 		B_FOLLOW_ALL, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
356 
357 	typeBox->ResizeTo(typeBox->Bounds().Width(), fRemoveTypeButton->Frame().bottom + 8.0f);
358 	typeBox->AddChild(scrollView);
359 
360 	rect.left = fRemoveTypeButton->Frame().right + 8.0f;
361 #ifdef __HAIKU__
362 	rect.top = (box->Bounds().Height() + box->TopBorderOffset() - B_LARGE_ICON) / 2.0f;
363 #else
364 	rect.top = (box->Bounds().Height() - B_LARGE_ICON) / 2.0f;
365 #endif
366 	rect.right = rect.left + B_LARGE_ICON - 1.0f;
367 	rect.bottom = rect.top + B_LARGE_ICON - 1.0f;
368 	fTypeIconView = new IconView(rect, "type icon", B_FOLLOW_RIGHT | B_FOLLOW_TOP);
369 	typeBox->AddChild(fTypeIconView);
370 
371 	// "Version Info" group
372 
373 	rect.top = typeBox->Frame().bottom + 8.0f;
374 	rect.bottom = rect.top + typeBox->Bounds().Height();
375 	rect.left = 8.0f;
376 	rect.right = Bounds().Width() - 8.0f;
377 	box = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT);
378 		// the resizing mode will later also be set to B_FOLLOW_BOTTOM
379 	box->SetLabel("Version Info");
380 	topView->AddChild(box);
381 
382 	BMenuField* menuField;
383 #if 0
384 	BPopUpMenu *popUpMenu = new BPopUpMenu("version info", true, true);
385 	item = new BMenuItem("Version Info", NULL);
386 	item->SetMarked(true);
387 	popUpMenu->AddItem(item);
388 	item = new BMenuItem("System Version Info", NULL);
389 	popUpMenu->AddItem(item);
390 
391 	menuField = new BMenuField(BRect(0, 0, 100, 15),
392 		"version kind", NULL, popUpMenu, true);
393 	menuField->ResizeToPreferred();
394 	box->SetLabel(menuField);
395 #endif
396 
397 	rect.top = 4.0f + ceilf(fontHeight.ascent + fontHeight.descent);
398 	rect.bottom = rect.top + height;
399 	fMajorVersionControl = new BTextControl(rect, "major", "Version:", NULL,
400 		NULL);
401 	fMajorVersionControl->SetDivider(fMajorVersionControl->StringWidth(
402 		fMajorVersionControl->Label()) + 4.0f);
403 	fMajorVersionControl->GetPreferredSize(&width, &height);
404 	width = 12.0f + fMajorVersionControl->StringWidth("99");
405 	fMajorVersionControl->ResizeTo(fMajorVersionControl->Divider() + width, height);
406 	_MakeNumberTextControl(fMajorVersionControl);
407 	box->AddChild(fMajorVersionControl);
408 
409 	rect.left = fMajorVersionControl->Frame().right + 1.0f;
410 	fMiddleVersionControl = new BTextControl(rect, "middle", ".", NULL,
411 		NULL);
412 	fMiddleVersionControl->SetDivider(fMiddleVersionControl->StringWidth(
413 		fMiddleVersionControl->Label()) + 4.0f);
414 	fMiddleVersionControl->ResizeTo(fMiddleVersionControl->Divider() + width, height);
415 	_MakeNumberTextControl(fMiddleVersionControl);
416 	box->AddChild(fMiddleVersionControl);
417 
418 	rect.left = fMiddleVersionControl->Frame().right + 1.0f;
419 	fMinorVersionControl = new BTextControl(rect, "middle", ".", NULL,
420 		NULL);
421 	fMinorVersionControl->SetDivider(fMinorVersionControl->StringWidth(
422 		fMinorVersionControl->Label()) + 4.0f);
423 	fMinorVersionControl->ResizeTo(fMinorVersionControl->Divider() + width, height);
424 	_MakeNumberTextControl(fMinorVersionControl);
425 	box->AddChild(fMinorVersionControl);
426 
427 	fVarietyMenu = new BPopUpMenu("variety", true, true);
428 	fVarietyMenu->AddItem(new BMenuItem("Development", NULL));
429 	fVarietyMenu->AddItem(new BMenuItem("Alpha", NULL));
430 	fVarietyMenu->AddItem(new BMenuItem("Beta", NULL));
431 	fVarietyMenu->AddItem(new BMenuItem("Gamma", NULL));
432 	fVarietyMenu->AddItem(item = new BMenuItem("Golden Master", NULL));
433 	item->SetMarked(true);
434 	fVarietyMenu->AddItem(new BMenuItem("Final", NULL));
435 
436 	rect.top--;
437 		// BMenuField oddity
438 	rect.left = fMinorVersionControl->Frame().right + 6.0f;
439 	menuField = new BMenuField(rect,
440 		"variety", NULL, fVarietyMenu, true);
441 	menuField->ResizeToPreferred();
442 	box->AddChild(menuField);
443 
444 	rect.top++;
445 	rect.left = menuField->Frame().right;
446 	rect.right = rect.left + 30.0f;
447 	fInternalVersionControl = new BTextControl(rect, "internal", "/", NULL,
448 		NULL);
449 	fInternalVersionControl->SetDivider(fInternalVersionControl->StringWidth(
450 		fInternalVersionControl->Label()) + 4.0f);
451 	fInternalVersionControl->ResizeTo(fInternalVersionControl->Divider() + width, height);
452 	box->AddChild(fInternalVersionControl);
453 
454 	rect = box->Bounds().InsetByCopy(8.0f, 0.0f);
455 	rect.top = fInternalVersionControl->Frame().bottom + 8.0f;
456 	fShortDescriptionControl = new BTextControl(rect, "short desc", "Short Description:",
457 		NULL, NULL, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
458 	float labelWidth = fShortDescriptionControl->StringWidth(
459 		fShortDescriptionControl->Label()) + 4.0f;
460 	fShortDescriptionControl->SetDivider(labelWidth);
461 	fShortDescriptionControl->GetPreferredSize(&width, &height);
462 	fShortDescriptionControl->ResizeTo(rect.Width(), height);
463 
464 	// TODO: workaround for a GCC 4.1.0 bug? Or is that really what the standard says?
465 	version_info versionInfo;
466 	fShortDescriptionControl->TextView()->SetMaxBytes(sizeof(versionInfo.short_info));
467 	box->AddChild(fShortDescriptionControl);
468 
469 	rect.OffsetBy(0.0f, fShortDescriptionControl->Bounds().Height() + 5.0f);
470 	rect.right = rect.left + labelWidth;
471 	StringView* label = new StringView(rect, NULL, "Long Description:", NULL);
472 	label->SetDivider(labelWidth);
473 	box->AddChild(label);
474 
475 	rect.left = rect.right + 3.0f;
476 	rect.top += 1.0f;
477 	rect.right = box->Bounds().Width() - 10.0f - B_V_SCROLL_BAR_WIDTH;
478 	rect.bottom = rect.top + fShortDescriptionControl->Bounds().Height() * 3.0f - 1.0f;
479 	fLongDescriptionView = new BTextView(rect, "long desc",
480 		rect.OffsetToCopy(B_ORIGIN), B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS);
481 	fLongDescriptionView->SetMaxBytes(sizeof(versionInfo.long_info));
482 
483 	scrollView = new BScrollView("desc scrollview", fLongDescriptionView,
484 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
485 	box->ResizeTo(box->Bounds().Width(), scrollView->Frame().bottom + 8.0f);
486 	box->AddChild(scrollView);
487 
488 	// Adjust window size and limits
489 
490 	width = fInternalVersionControl->Frame().right + 16.0f;
491 	float minWidth = fBackgroundAppCheckBox->Frame().right + iconBoxWidth + 32.0f;
492 	if (width > minWidth)
493 		minWidth = width;
494 
495 	ResizeTo(Bounds().Width() > minWidth ? Bounds().Width() : minWidth,
496 		box->Frame().bottom + topView->Frame().top + 8.0f);
497 	SetSizeLimits(minWidth, 32767.0f, Bounds().Height(), 32767.0f);
498 	typeBox->SetResizingMode(B_FOLLOW_ALL);
499 	box->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
500 
501 	fSignatureControl->MakeFocus(true);
502 
503 	BMimeType::StartWatching(this);
504 	_SetTo(entry);
505 }
506 
507 
508 ApplicationTypeWindow::~ApplicationTypeWindow()
509 {
510 	BMimeType::StopWatching(this);
511 }
512 
513 
514 BString
515 ApplicationTypeWindow::_Title(const BEntry& entry)
516 {
517 	char name[B_FILE_NAME_LENGTH];
518 	if (entry.GetName(name) != B_OK)
519 		strcpy(name, "\"-\"");
520 
521 	BString title(name);
522 	title.Append(" Application Type");
523 	return title;
524 }
525 
526 
527 void
528 ApplicationTypeWindow::_SetTo(const BEntry& entry)
529 {
530 	SetTitle(_Title(entry).String());
531 	fEntry = entry;
532 
533 	// Retrieve Info
534 
535 	BFile file(&entry, B_READ_ONLY);
536 	if (file.InitCheck() != B_OK)
537 		return;
538 
539 	BAppFileInfo info(&file);
540 	if (info.InitCheck() != B_OK)
541 		return;
542 
543 	char signature[B_MIME_TYPE_LENGTH];
544 	if (info.GetSignature(signature) != B_OK)
545 		signature[0] = '\0';
546 
547 	bool gotFlags = false;
548 	uint32 flags;
549 	if (info.GetAppFlags(&flags) == B_OK)
550 		gotFlags = true;
551 	else
552 		flags = B_MULTIPLE_LAUNCH;
553 
554 	BMessage supportedTypes;
555 	info.GetSupportedTypes(&supportedTypes);
556 
557 	version_info versionInfo;
558 	if (info.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) != B_OK)
559 		memset(&versionInfo, 0, sizeof(version_info));
560 
561 	// Set Controls
562 
563 	fSignatureControl->SetText(signature);
564 
565 	// flags
566 
567 	switch (flags & (B_SINGLE_LAUNCH | B_MULTIPLE_LAUNCH | B_EXCLUSIVE_LAUNCH)) {
568 		case B_SINGLE_LAUNCH:
569 			fSingleLaunchButton->SetValue(B_CONTROL_ON);
570 			break;
571 
572 		case B_EXCLUSIVE_LAUNCH:
573 			fExclusiveLaunchButton->SetValue(B_CONTROL_ON);
574 			break;
575 
576 		case B_MULTIPLE_LAUNCH:
577 		default:
578 			fMultipleLaunchButton->SetValue(B_CONTROL_ON);
579 			break;
580 	}
581 
582 	fArgsOnlyCheckBox->SetValue((flags & B_ARGV_ONLY) != 0);
583 	fBackgroundAppCheckBox->SetValue((flags & B_BACKGROUND_APP) != 0);
584 	fFlagsCheckBox->SetValue(gotFlags);
585 
586 	_UpdateAppFlagsEnabled();
587 
588 	// icon
589 
590 	entry_ref ref;
591 	if (entry.GetRef(&ref) == B_OK)
592 		fIcon.SetTo(ref);
593 	else
594 		fIcon.Unset();
595 
596 	fIconView->SetTo(&fIcon);
597 
598 	// supported types
599 
600 	for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
601 		BListItem* item = fTypeListView->RemoveItem(i);
602 		delete item;
603 	}
604 
605 	const char* type;
606 	for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) {
607 		SupportedTypeItem* item = new SupportedTypeItem(type);
608 
609 		entry_ref ref;
610 		if (fEntry.GetRef(&ref) == B_OK)
611 			item->SetIcon(ref, type);
612 
613 		fTypeListView->AddItem(item);
614 	}
615 	fTypeListView->SortItems(&SupportedTypeItem::Compare);
616 	fTypeIconView->SetTo(NULL);
617 	fTypeIconView->SetEnabled(false);
618 	fRemoveTypeButton->SetEnabled(false);
619 
620 	// version info
621 
622 	char text[256];
623 	snprintf(text, sizeof(text), "%ld", versionInfo.major);
624 	fMajorVersionControl->SetText(text);
625 	snprintf(text, sizeof(text), "%ld", versionInfo.middle);
626 	fMiddleVersionControl->SetText(text);
627 	snprintf(text, sizeof(text), "%ld", versionInfo.minor);
628 	fMinorVersionControl->SetText(text);
629 
630 	if (versionInfo.variety >= (uint32)fVarietyMenu->CountItems())
631 		versionInfo.variety = 0;
632 	BMenuItem* item = fVarietyMenu->ItemAt(versionInfo.variety);
633 	if (item != NULL)
634 		item->SetMarked(true);
635 
636 	snprintf(text, sizeof(text), "%ld", versionInfo.internal);
637 	fInternalVersionControl->SetText(text);
638 
639 	fShortDescriptionControl->SetText(versionInfo.short_info);
640 	fLongDescriptionView->SetText(versionInfo.long_info);
641 }
642 
643 
644 void
645 ApplicationTypeWindow::_UpdateAppFlagsEnabled()
646 {
647 	bool enabled = fFlagsCheckBox->Value() != B_CONTROL_OFF;
648 
649 	fSingleLaunchButton->SetEnabled(enabled);
650 	fMultipleLaunchButton->SetEnabled(enabled);
651 	fExclusiveLaunchButton->SetEnabled(enabled);
652 	fArgsOnlyCheckBox->SetEnabled(enabled);
653 	fBackgroundAppCheckBox->SetEnabled(enabled);
654 }
655 
656 
657 void
658 ApplicationTypeWindow::_MakeNumberTextControl(BTextControl* control)
659 {
660 	// filter out invalid characters that can't be part of a MIME type name
661 	BTextView* textView = control->TextView();
662 	textView->SetMaxBytes(10);
663 
664 	for (int32 i = 0; i < 256; i++) {
665 		if (!isdigit(i))
666 			textView->DisallowChar(i);
667 	}
668 }
669 
670 
671 void
672 ApplicationTypeWindow::_Save()
673 {
674 	BFile file;
675 	status_t status = file.SetTo(&fEntry, B_READ_WRITE);
676 	if (status != B_OK)
677 		return;
678 
679 	BAppFileInfo info(&file);
680 	status = info.InitCheck();
681 	if (status != B_OK)
682 		return;
683 
684 	// Retrieve Info
685 
686 	uint32 flags = 0;
687 	if (fFlagsCheckBox->Value() != B_CONTROL_OFF) {
688 		if (fSingleLaunchButton->Value() != B_CONTROL_OFF)
689 			flags |= B_SINGLE_LAUNCH;
690 		else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF)
691 			flags |= B_MULTIPLE_LAUNCH;
692 		else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF)
693 			flags |= B_EXCLUSIVE_LAUNCH;
694 
695 		if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF)
696 			flags |= B_ARGV_ONLY;
697 		if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF)
698 			flags |= B_BACKGROUND_APP;
699 	}
700 
701 	BMessage supportedTypes;
702 	for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
703 		SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
704 			fTypeListView->ItemAt(i));
705 
706 		supportedTypes.AddString("types", item->Type());
707 	}
708 
709 	version_info versionInfo;
710 	versionInfo.major = atol(fMajorVersionControl->Text());
711 	versionInfo.middle = atol(fMiddleVersionControl->Text());
712 	versionInfo.minor = atol(fMinorVersionControl->Text());
713 	versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked());
714 	versionInfo.internal = atol(fInternalVersionControl->Text());
715 	strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(),
716 		sizeof(versionInfo.short_info));
717 	strlcpy(versionInfo.long_info, fLongDescriptionView->Text(),
718 		sizeof(versionInfo.long_info));
719 
720 	// Save
721 
722 	status = info.SetSignature(fSignatureControl->Text());
723 	if (status == B_OK)
724 		status = info.SetAppFlags(flags);
725 	if (status == B_OK)
726 		status = info.SetVersionInfo(&versionInfo, B_APP_VERSION_KIND);
727 	if (status == B_OK)
728 		fIcon.CopyTo(info, NULL, true);
729 
730 	// supported types and their icons
731 	if (status == B_OK)
732 		status = info.SetSupportedTypes(&supportedTypes);
733 
734 	for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
735 		SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
736 			fTypeListView->ItemAt(i));
737 
738 		item->Icon().CopyTo(info, item->Type(), true);
739 	}
740 }
741 
742 
743 void
744 ApplicationTypeWindow::FrameResized(float width, float height)
745 {
746 	// This works around a flaw of BTextView
747 	fLongDescriptionView->SetTextRect(fLongDescriptionView->Bounds());
748 }
749 
750 
751 void
752 ApplicationTypeWindow::MessageReceived(BMessage* message)
753 {
754 	switch (message->what) {
755 		case kMsgFlagsChanged:
756 			_UpdateAppFlagsEnabled();
757 			break;
758 
759 		case kMsgSave:
760 			_Save();
761 			break;
762 
763 		case kMsgTypeSelected:
764 		{
765 			int32 index;
766 			if (message->FindInt32("index", &index) == B_OK) {
767 				SupportedTypeItem* item = (SupportedTypeItem*)fTypeListView->ItemAt(index);
768 
769 				fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL);
770 				fTypeIconView->SetEnabled(item != NULL);
771 				fRemoveTypeButton->SetEnabled(item != NULL);
772 			}
773 			break;
774 		}
775 
776 		case kMsgAddType:
777 		{
778 			BWindow* window = new TypeListWindow(NULL,
779 				kMsgTypeAdded, this);
780 			window->Show();
781 			break;
782 		}
783 
784 		case kMsgTypeAdded:
785 		{
786 			const char* type;
787 			if (message->FindString("type", &type) != B_OK)
788 				break;
789 
790 			// check if this type already exists
791 
792 			SupportedTypeItem* newItem = new SupportedTypeItem(type);
793 			int32 insertAt = 0;
794 
795 			for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
796 				SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
797 					fTypeListView->ItemAt(i));
798 				if (item == NULL)
799 					continue;
800 
801 				int compare = strcasecmp(item->Type(), type);
802 				if (!compare) {
803 					// type does already exist, select it and bail out
804 					delete newItem;
805 					newItem = NULL;
806 					fTypeListView->Select(i);
807 					break;
808 				}
809 				if (compare < 0)
810 					insertAt = i + 1;
811 			}
812 
813 			if (newItem == NULL)
814 				break;
815 
816 			fTypeListView->AddItem(newItem, insertAt);
817 			fTypeListView->Select(insertAt);
818 			break;
819 		}
820 
821 		case kMsgRemoveType:
822 		{
823 			int32 index = fTypeListView->CurrentSelection();
824 			if (index < 0)
825 				break;
826 
827 			delete fTypeListView->RemoveItem(index);
828 			fTypeIconView->SetTo(NULL);
829 			fTypeIconView->SetEnabled(false);
830 			fRemoveTypeButton->SetEnabled(false);
831 			break;
832 		}
833 
834 		case B_SIMPLE_DATA:
835 		{
836 			entry_ref ref;
837 			if (message->FindRef("refs", &ref) != B_OK)
838 				break;
839 
840 			// TODO: add to supported types
841 			break;
842 		}
843 
844 		case B_META_MIME_CHANGED:
845 			const char* type;
846 			int32 which;
847 			if (message->FindString("be:type", &type) != B_OK
848 				|| message->FindInt32("be:which", &which) != B_OK)
849 				break;
850 
851 			// TODO: update supported types names
852 //			if (which == B_MIME_TYPE_DELETED)
853 			break;
854 
855 		default:
856 			BWindow::MessageReceived(message);
857 	}
858 }
859 
860 
861 bool
862 ApplicationTypeWindow::QuitRequested()
863 {
864 	be_app->PostMessage(kMsgTypeWindowClosed);
865 	return true;
866 }
867 
868