xref: /haiku/src/preferences/filetypes/ApplicationTypeWindow.cpp (revision ba499cdc3336fb89429027418871bf263f1f5e14)
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 	fMajorVersionControl = new BTextControl(rect, "major", "Version:", NULL,
399 		NULL);
400 	fMajorVersionControl->SetDivider(fMajorVersionControl->StringWidth(
401 		fMajorVersionControl->Label()) + 4.0f);
402 	fMajorVersionControl->GetPreferredSize(&width, &height);
403 	width = 12.0f + fMajorVersionControl->StringWidth("99");
404 	fMajorVersionControl->ResizeTo(fMajorVersionControl->Divider() + width, height);
405 	_MakeNumberTextControl(fMajorVersionControl);
406 	box->AddChild(fMajorVersionControl);
407 
408 	rect.left = fMajorVersionControl->Frame().right + 1.0f;
409 	fMiddleVersionControl = new BTextControl(rect, "middle", ".", NULL,
410 		NULL);
411 	fMiddleVersionControl->SetDivider(fMiddleVersionControl->StringWidth(
412 		fMiddleVersionControl->Label()) + 4.0f);
413 	fMiddleVersionControl->ResizeTo(fMiddleVersionControl->Divider() + width, height);
414 	_MakeNumberTextControl(fMiddleVersionControl);
415 	box->AddChild(fMiddleVersionControl);
416 
417 	rect.left = fMiddleVersionControl->Frame().right + 1.0f;
418 	fMinorVersionControl = new BTextControl(rect, "middle", ".", NULL,
419 		NULL);
420 	fMinorVersionControl->SetDivider(fMinorVersionControl->StringWidth(
421 		fMinorVersionControl->Label()) + 4.0f);
422 	fMinorVersionControl->ResizeTo(fMinorVersionControl->Divider() + width, height);
423 	_MakeNumberTextControl(fMinorVersionControl);
424 	box->AddChild(fMinorVersionControl);
425 
426 	fVarietyMenu = new BPopUpMenu("variety", true, true);
427 	fVarietyMenu->AddItem(new BMenuItem("Development", NULL));
428 	fVarietyMenu->AddItem(new BMenuItem("Alpha", NULL));
429 	fVarietyMenu->AddItem(new BMenuItem("Beta", NULL));
430 	fVarietyMenu->AddItem(new BMenuItem("Gamma", NULL));
431 	fVarietyMenu->AddItem(item = new BMenuItem("Golden Master", NULL));
432 	item->SetMarked(true);
433 	fVarietyMenu->AddItem(new BMenuItem("Final", NULL));
434 
435 	rect.top--;
436 		// BMenuField oddity
437 	rect.left = fMinorVersionControl->Frame().right + 6.0f;
438 	menuField = new BMenuField(rect,
439 		"variety", NULL, fVarietyMenu, true);
440 	menuField->ResizeToPreferred();
441 	box->AddChild(menuField);
442 
443 	rect.top++;
444 	rect.left = menuField->Frame().right;
445 	rect.right = rect.left + 30.0f;
446 	fInternalVersionControl = new BTextControl(rect, "internal", "/", NULL,
447 		NULL);
448 	fInternalVersionControl->SetDivider(fInternalVersionControl->StringWidth(
449 		fInternalVersionControl->Label()) + 4.0f);
450 	fInternalVersionControl->ResizeTo(fInternalVersionControl->Divider() + width, height);
451 	box->AddChild(fInternalVersionControl);
452 
453 	rect = box->Bounds().InsetByCopy(8.0f, 0.0f);
454 	rect.top = fInternalVersionControl->Frame().bottom + 8.0f;
455 	fShortDescriptionControl = new BTextControl(rect, "short desc", "Short Description:",
456 		NULL, NULL, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
457 	float labelWidth = fShortDescriptionControl->StringWidth(
458 		fShortDescriptionControl->Label()) + 4.0f;
459 	fShortDescriptionControl->SetDivider(labelWidth);
460 	fShortDescriptionControl->GetPreferredSize(&width, &height);
461 	fShortDescriptionControl->ResizeTo(rect.Width(), height);
462 
463 	// TODO: workaround for a GCC 4.1.0 bug? Or is that really what the standard says?
464 	version_info versionInfo;
465 	fShortDescriptionControl->TextView()->SetMaxBytes(sizeof(versionInfo.short_info));
466 	box->AddChild(fShortDescriptionControl);
467 
468 	rect.OffsetBy(0.0f, fShortDescriptionControl->Bounds().Height() + 5.0f);
469 	rect.right = rect.left + labelWidth;
470 	StringView* label = new StringView(rect, NULL, "Long Description:", NULL);
471 	label->SetDivider(labelWidth);
472 	box->AddChild(label);
473 
474 	rect.left = rect.right + 3.0f;
475 	rect.top += 1.0f;
476 	rect.right = box->Bounds().Width() - 10.0f - B_V_SCROLL_BAR_WIDTH;
477 	rect.bottom = rect.top + fShortDescriptionControl->Bounds().Height() * 3.0f - 1.0f;
478 	fLongDescriptionView = new BTextView(rect, "long desc",
479 		rect.OffsetToCopy(B_ORIGIN), B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS);
480 	fLongDescriptionView->SetMaxBytes(sizeof(versionInfo.long_info));
481 
482 	scrollView = new BScrollView("desc scrollview", fLongDescriptionView,
483 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
484 	box->ResizeTo(box->Bounds().Width(), scrollView->Frame().bottom + 8.0f);
485 	box->AddChild(scrollView);
486 
487 	// Adjust window size and limits
488 
489 	width = fInternalVersionControl->Frame().right + 16.0f;
490 	float minWidth = fBackgroundAppCheckBox->Frame().right + iconBoxWidth + 32.0f;
491 	if (width > minWidth)
492 		minWidth = width;
493 
494 	ResizeTo(Bounds().Width() > minWidth ? Bounds().Width() : minWidth,
495 		box->Frame().bottom + topView->Frame().top + 8.0f);
496 	SetSizeLimits(minWidth, 32767.0f, Bounds().Height(), 32767.0f);
497 	typeBox->SetResizingMode(B_FOLLOW_ALL);
498 	box->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
499 
500 	fSignatureControl->MakeFocus(true);
501 
502 	BMimeType::StartWatching(this);
503 	_SetTo(entry);
504 }
505 
506 
507 ApplicationTypeWindow::~ApplicationTypeWindow()
508 {
509 	BMimeType::StopWatching(this);
510 }
511 
512 
513 BString
514 ApplicationTypeWindow::_Title(const BEntry& entry)
515 {
516 	char name[B_FILE_NAME_LENGTH];
517 	if (entry.GetName(name) != B_OK)
518 		strcpy(name, "\"-\"");
519 
520 	BString title(name);
521 	title.Append(" Application Type");
522 	return title;
523 }
524 
525 
526 void
527 ApplicationTypeWindow::_SetTo(const BEntry& entry)
528 {
529 	SetTitle(_Title(entry).String());
530 	fEntry = entry;
531 
532 	// Retrieve Info
533 
534 	BFile file(&entry, B_READ_ONLY);
535 	if (file.InitCheck() != B_OK)
536 		return;
537 
538 	BAppFileInfo info(&file);
539 	if (info.InitCheck() != B_OK)
540 		return;
541 
542 	char signature[B_MIME_TYPE_LENGTH];
543 	if (info.GetSignature(signature) != B_OK)
544 		signature[0] = '\0';
545 
546 	bool gotFlags = false;
547 	uint32 flags;
548 	if (info.GetAppFlags(&flags) == B_OK)
549 		gotFlags = true;
550 	else
551 		flags = B_MULTIPLE_LAUNCH;
552 
553 	BMessage supportedTypes;
554 	info.GetSupportedTypes(&supportedTypes);
555 
556 	version_info versionInfo;
557 	if (info.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) != B_OK)
558 		memset(&versionInfo, 0, sizeof(version_info));
559 
560 	// Set Controls
561 
562 	fSignatureControl->SetText(signature);
563 
564 	// flags
565 
566 	switch (flags & (B_SINGLE_LAUNCH | B_MULTIPLE_LAUNCH | B_EXCLUSIVE_LAUNCH)) {
567 		case B_SINGLE_LAUNCH:
568 			fSingleLaunchButton->SetValue(B_CONTROL_ON);
569 			break;
570 
571 		case B_EXCLUSIVE_LAUNCH:
572 			fExclusiveLaunchButton->SetValue(B_CONTROL_ON);
573 			break;
574 
575 		case B_MULTIPLE_LAUNCH:
576 		default:
577 			fMultipleLaunchButton->SetValue(B_CONTROL_ON);
578 			break;
579 	}
580 
581 	fArgsOnlyCheckBox->SetValue((flags & B_ARGV_ONLY) != 0);
582 	fBackgroundAppCheckBox->SetValue((flags & B_BACKGROUND_APP) != 0);
583 	fFlagsCheckBox->SetValue(gotFlags);
584 
585 	_UpdateAppFlagsEnabled();
586 
587 	// icon
588 
589 	entry_ref ref;
590 	if (entry.GetRef(&ref) == B_OK)
591 		fIcon.SetTo(ref);
592 	else
593 		fIcon.Unset();
594 
595 	fIconView->SetTo(&fIcon);
596 
597 	// supported types
598 
599 	for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
600 		BListItem* item = fTypeListView->RemoveItem(i);
601 		delete item;
602 	}
603 
604 	const char* type;
605 	for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) {
606 		SupportedTypeItem* item = new SupportedTypeItem(type);
607 
608 		entry_ref ref;
609 		if (fEntry.GetRef(&ref) == B_OK)
610 			item->SetIcon(ref, type);
611 
612 		fTypeListView->AddItem(item);
613 	}
614 	fTypeListView->SortItems(&SupportedTypeItem::Compare);
615 	fTypeIconView->SetTo(NULL);
616 	fTypeIconView->SetEnabled(false);
617 	fRemoveTypeButton->SetEnabled(false);
618 
619 	// version info
620 
621 	char text[256];
622 	snprintf(text, sizeof(text), "%ld", versionInfo.major);
623 	fMajorVersionControl->SetText(text);
624 	snprintf(text, sizeof(text), "%ld", versionInfo.middle);
625 	fMiddleVersionControl->SetText(text);
626 	snprintf(text, sizeof(text), "%ld", versionInfo.minor);
627 	fMinorVersionControl->SetText(text);
628 
629 	if (versionInfo.variety >= (uint32)fVarietyMenu->CountItems())
630 		versionInfo.variety = 0;
631 	BMenuItem* item = fVarietyMenu->ItemAt(versionInfo.variety);
632 	if (item != NULL)
633 		item->SetMarked(true);
634 
635 	snprintf(text, sizeof(text), "%ld", versionInfo.internal);
636 	fInternalVersionControl->SetText(text);
637 
638 	fShortDescriptionControl->SetText(versionInfo.short_info);
639 	fLongDescriptionView->SetText(versionInfo.long_info);
640 }
641 
642 
643 void
644 ApplicationTypeWindow::_UpdateAppFlagsEnabled()
645 {
646 	bool enabled = fFlagsCheckBox->Value() != B_CONTROL_OFF;
647 
648 	fSingleLaunchButton->SetEnabled(enabled);
649 	fMultipleLaunchButton->SetEnabled(enabled);
650 	fExclusiveLaunchButton->SetEnabled(enabled);
651 	fArgsOnlyCheckBox->SetEnabled(enabled);
652 	fBackgroundAppCheckBox->SetEnabled(enabled);
653 }
654 
655 
656 void
657 ApplicationTypeWindow::_MakeNumberTextControl(BTextControl* control)
658 {
659 	// filter out invalid characters that can't be part of a MIME type name
660 	BTextView* textView = control->TextView();
661 	textView->SetMaxBytes(10);
662 
663 	for (int32 i = 0; i < 256; i++) {
664 		if (!isdigit(i))
665 			textView->DisallowChar(i);
666 	}
667 }
668 
669 
670 void
671 ApplicationTypeWindow::_Save()
672 {
673 	BFile file;
674 	status_t status = file.SetTo(&fEntry, B_READ_WRITE);
675 	if (status != B_OK)
676 		return;
677 
678 	BAppFileInfo info(&file);
679 	status = info.InitCheck();
680 	if (status != B_OK)
681 		return;
682 
683 	// Retrieve Info
684 
685 	uint32 flags = 0;
686 	if (fFlagsCheckBox->Value() != B_CONTROL_OFF) {
687 		if (fSingleLaunchButton->Value() != B_CONTROL_OFF)
688 			flags |= B_SINGLE_LAUNCH;
689 		else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF)
690 			flags |= B_MULTIPLE_LAUNCH;
691 		else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF)
692 			flags |= B_EXCLUSIVE_LAUNCH;
693 
694 		if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF)
695 			flags |= B_ARGV_ONLY;
696 		if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF)
697 			flags |= B_BACKGROUND_APP;
698 	}
699 
700 	BMessage supportedTypes;
701 	for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
702 		SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
703 			fTypeListView->ItemAt(i));
704 
705 		supportedTypes.AddString("types", item->Type());
706 	}
707 
708 	version_info versionInfo;
709 	versionInfo.major = atol(fMajorVersionControl->Text());
710 	versionInfo.middle = atol(fMiddleVersionControl->Text());
711 	versionInfo.minor = atol(fMinorVersionControl->Text());
712 	versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked());
713 	versionInfo.internal = atol(fInternalVersionControl->Text());
714 	strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(),
715 		sizeof(versionInfo.short_info));
716 	strlcpy(versionInfo.long_info, fLongDescriptionView->Text(),
717 		sizeof(versionInfo.long_info));
718 
719 	// Save
720 
721 	status = info.SetSignature(fSignatureControl->Text());
722 	if (status == B_OK)
723 		status = info.SetAppFlags(flags);
724 	if (status == B_OK)
725 		status = info.SetVersionInfo(&versionInfo, B_APP_VERSION_KIND);
726 	if (status == B_OK)
727 		fIcon.CopyTo(info, NULL, true);
728 
729 	// supported types and their icons
730 	if (status == B_OK)
731 		status = info.SetSupportedTypes(&supportedTypes);
732 
733 	for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
734 		SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
735 			fTypeListView->ItemAt(i));
736 
737 		item->Icon().CopyTo(info, item->Type(), true);
738 	}
739 }
740 
741 
742 void
743 ApplicationTypeWindow::FrameResized(float width, float height)
744 {
745 	// This works around a flaw of BTextView
746 	fLongDescriptionView->SetTextRect(fLongDescriptionView->Bounds());
747 }
748 
749 
750 void
751 ApplicationTypeWindow::MessageReceived(BMessage* message)
752 {
753 	switch (message->what) {
754 		case kMsgFlagsChanged:
755 			_UpdateAppFlagsEnabled();
756 			break;
757 
758 		case kMsgSave:
759 			_Save();
760 			break;
761 
762 		case kMsgTypeSelected:
763 		{
764 			int32 index;
765 			if (message->FindInt32("index", &index) == B_OK) {
766 				SupportedTypeItem* item = (SupportedTypeItem*)fTypeListView->ItemAt(index);
767 
768 				fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL);
769 				fTypeIconView->SetEnabled(item != NULL);
770 				fRemoveTypeButton->SetEnabled(item != NULL);
771 			}
772 			break;
773 		}
774 
775 		case kMsgAddType:
776 		{
777 			BWindow* window = new TypeListWindow(NULL,
778 				kMsgTypeAdded, this);
779 			window->Show();
780 			break;
781 		}
782 
783 		case kMsgTypeAdded:
784 		{
785 			const char* type;
786 			if (message->FindString("type", &type) != B_OK)
787 				break;
788 
789 			// check if this type already exists
790 
791 			SupportedTypeItem* newItem = new SupportedTypeItem(type);
792 			int32 insertAt = 0;
793 
794 			for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
795 				SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
796 					fTypeListView->ItemAt(i));
797 				if (item == NULL)
798 					continue;
799 
800 				int compare = strcasecmp(item->Type(), type);
801 				if (!compare) {
802 					// type does already exist, select it and bail out
803 					delete newItem;
804 					newItem = NULL;
805 					fTypeListView->Select(i);
806 					break;
807 				}
808 				if (compare < 0)
809 					insertAt = i + 1;
810 			}
811 
812 			if (newItem == NULL)
813 				break;
814 
815 			fTypeListView->AddItem(newItem, insertAt);
816 			fTypeListView->Select(insertAt);
817 			break;
818 		}
819 
820 		case kMsgRemoveType:
821 		{
822 			int32 index = fTypeListView->CurrentSelection();
823 			if (index < 0)
824 				break;
825 
826 			delete fTypeListView->RemoveItem(index);
827 			fTypeIconView->SetTo(NULL);
828 			fTypeIconView->SetEnabled(false);
829 			fRemoveTypeButton->SetEnabled(false);
830 			break;
831 		}
832 
833 		case B_SIMPLE_DATA:
834 		{
835 			entry_ref ref;
836 			if (message->FindRef("refs", &ref) != B_OK)
837 				break;
838 
839 			// TODO: add to supported types
840 			break;
841 		}
842 
843 		case B_META_MIME_CHANGED:
844 			const char* type;
845 			int32 which;
846 			if (message->FindString("be:type", &type) != B_OK
847 				|| message->FindInt32("be:which", &which) != B_OK)
848 				break;
849 
850 			// TODO: update supported types names
851 //			if (which == B_MIME_TYPE_DELETED)
852 			break;
853 
854 		default:
855 			BWindow::MessageReceived(message);
856 	}
857 }
858 
859 
860 bool
861 ApplicationTypeWindow::QuitRequested()
862 {
863 	be_app->PostMessage(kMsgTypeWindowClosed);
864 	return true;
865 }
866 
867