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