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 "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 <Application.h>
16 #include <Bitmap.h>
17 #include <Box.h>
18 #include <Button.h>
19 #include <Catalog.h>
20 #include <CheckBox.h>
21 #include <ControlLook.h>
22 #include <File.h>
23 #include <GroupView.h>
24 #include <LayoutBuilder.h>
25 #include <ListView.h>
26 #include <Locale.h>
27 #include <MenuBar.h>
28 #include <MenuField.h>
29 #include <MenuItem.h>
30 #include <Mime.h>
31 #include <NodeInfo.h>
32 #include <PopUpMenu.h>
33 #include <RadioButton.h>
34 #include <Roster.h>
35 #include <ScrollView.h>
36 #include <StringView.h>
37 #include <TextControl.h>
38
39 #include <ctype.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <strings.h>
43
44
45 #undef B_TRANSLATION_CONTEXT
46 #define B_TRANSLATION_CONTEXT "Application Type Window"
47
48
49 const uint32 kMsgSave = 'save';
50 const uint32 kMsgSignatureChanged = 'sgch';
51 const uint32 kMsgToggleAppFlags = 'tglf';
52 const uint32 kMsgAppFlagsChanged = 'afch';
53
54 const uint32 kMsgIconChanged = 'icch';
55 const uint32 kMsgTypeIconsChanged = 'tich';
56
57 const uint32 kMsgVersionInfoChanged = 'tvch';
58
59 const uint32 kMsgTypeSelected = 'tpsl';
60 const uint32 kMsgAddType = 'adtp';
61 const uint32 kMsgTypeAdded = 'tpad';
62 const uint32 kMsgRemoveType = 'rmtp';
63 const uint32 kMsgTypeRemoved = 'tprm';
64
65
66 //! TextView that filters the tab key to be able to tab-navigate while editing
67 class TabFilteringTextView : public BTextView {
68 public:
69 TabFilteringTextView(const char* name,
70 uint32 changedMessageWhat = 0);
71 virtual ~TabFilteringTextView();
72
73 virtual void InsertText(const char* text, int32 length,
74 int32 offset, const text_run_array* runs);
75 virtual void DeleteText(int32 fromOffset, int32 toOffset);
76 virtual void KeyDown(const char* bytes, int32 count);
77 virtual void TargetedByScrollView(BScrollView* scroller);
78 virtual void MakeFocus(bool focused = true);
79
80 private:
81 BScrollView* fScrollView;
82 uint32 fChangedMessageWhat;
83 };
84
85
86 class SupportedTypeItem : public BStringItem {
87 public:
88 SupportedTypeItem(const char* type);
89 virtual ~SupportedTypeItem();
90
Type() const91 const char* Type() const { return fType.String(); }
Icon()92 ::Icon& Icon() { return fIcon; }
93
94 void SetIcon(::Icon* icon);
95 void SetIcon(entry_ref& ref, const char* type);
96
97 static int Compare(const void* _a, const void* _b);
98
99 private:
100 BString fType;
101 ::Icon fIcon;
102 };
103
104
105 class SupportedTypeListView : public DropTargetListView {
106 public:
107 SupportedTypeListView(const char* name,
108 list_view_type
109 type = B_SINGLE_SELECTION_LIST,
110 uint32 flags = B_WILL_DRAW
111 | B_FRAME_EVENTS | B_NAVIGABLE);
112 virtual ~SupportedTypeListView();
113
114 virtual void MessageReceived(BMessage* message);
115 virtual bool AcceptsDrag(const BMessage* message);
116 };
117
118
119 // #pragma mark -
120
121
TabFilteringTextView(const char * name,uint32 changedMessageWhat)122 TabFilteringTextView::TabFilteringTextView(const char* name,
123 uint32 changedMessageWhat)
124 :
125 BTextView(name, B_WILL_DRAW | B_PULSE_NEEDED | B_NAVIGABLE),
126 fScrollView(NULL),
127 fChangedMessageWhat(changedMessageWhat)
128 {
129 }
130
131
~TabFilteringTextView()132 TabFilteringTextView::~TabFilteringTextView()
133 {
134 }
135
136
137 void
InsertText(const char * text,int32 length,int32 offset,const text_run_array * runs)138 TabFilteringTextView::InsertText(const char* text, int32 length, int32 offset,
139 const text_run_array* runs)
140 {
141 BTextView::InsertText(text, length, offset, runs);
142 if (fChangedMessageWhat != 0)
143 Window()->PostMessage(fChangedMessageWhat);
144 }
145
146
147 void
DeleteText(int32 fromOffset,int32 toOffset)148 TabFilteringTextView::DeleteText(int32 fromOffset, int32 toOffset)
149 {
150 BTextView::DeleteText(fromOffset, toOffset);
151 if (fChangedMessageWhat != 0)
152 Window()->PostMessage(fChangedMessageWhat);
153 }
154
155
156 void
KeyDown(const char * bytes,int32 count)157 TabFilteringTextView::KeyDown(const char* bytes, int32 count)
158 {
159 if (bytes[0] == B_TAB)
160 BView::KeyDown(bytes, count);
161 else
162 BTextView::KeyDown(bytes, count);
163 }
164
165
166 void
TargetedByScrollView(BScrollView * scroller)167 TabFilteringTextView::TargetedByScrollView(BScrollView* scroller)
168 {
169 fScrollView = scroller;
170 }
171
172
173 void
MakeFocus(bool focused)174 TabFilteringTextView::MakeFocus(bool focused)
175 {
176 BTextView::MakeFocus(focused);
177
178 if (fScrollView)
179 fScrollView->SetBorderHighlighted(focused);
180 }
181
182
183 // #pragma mark -
184
185
SupportedTypeItem(const char * type)186 SupportedTypeItem::SupportedTypeItem(const char* type)
187 : BStringItem(type),
188 fType(type)
189 {
190 BMimeType mimeType(type);
191
192 char description[B_MIME_TYPE_LENGTH];
193 if (mimeType.GetShortDescription(description) == B_OK && description[0])
194 SetText(description);
195 }
196
197
~SupportedTypeItem()198 SupportedTypeItem::~SupportedTypeItem()
199 {
200 }
201
202
203 void
SetIcon(::Icon * icon)204 SupportedTypeItem::SetIcon(::Icon* icon)
205 {
206 if (icon != NULL)
207 fIcon = *icon;
208 else
209 fIcon.Unset();
210 }
211
212
213 void
SetIcon(entry_ref & ref,const char * type)214 SupportedTypeItem::SetIcon(entry_ref& ref, const char* type)
215 {
216 fIcon.SetTo(ref, type);
217 }
218
219
220 /*static*/
221 int
Compare(const void * _a,const void * _b)222 SupportedTypeItem::Compare(const void* _a, const void* _b)
223 {
224 const SupportedTypeItem* a = *(const SupportedTypeItem**)_a;
225 const SupportedTypeItem* b = *(const SupportedTypeItem**)_b;
226
227 int compare = strcasecmp(a->Text(), b->Text());
228 if (compare != 0)
229 return compare;
230
231 return strcasecmp(a->Type(), b->Type());
232 }
233
234
235 // #pragma mark -
236
237
SupportedTypeListView(const char * name,list_view_type type,uint32 flags)238 SupportedTypeListView::SupportedTypeListView(const char* name,
239 list_view_type type, uint32 flags)
240 :
241 DropTargetListView(name, type, flags)
242 {
243 }
244
245
~SupportedTypeListView()246 SupportedTypeListView::~SupportedTypeListView()
247 {
248 }
249
250
251 void
MessageReceived(BMessage * message)252 SupportedTypeListView::MessageReceived(BMessage* message)
253 {
254 if (message->WasDropped() && AcceptsDrag(message)) {
255 // Add unique types
256 entry_ref ref;
257 for (int32 index = 0; message->FindRef("refs", index++, &ref) == B_OK; ) {
258 BNode node(&ref);
259 BNodeInfo info(&node);
260 if (node.InitCheck() != B_OK || info.InitCheck() != B_OK)
261 continue;
262
263 // TODO: we could identify the file in case it doesn't have a type...
264 char type[B_MIME_TYPE_LENGTH];
265 if (info.GetType(type) != B_OK)
266 continue;
267
268 // check if that type is already in our list
269 bool found = false;
270 for (int32 i = CountItems(); i-- > 0;) {
271 SupportedTypeItem* item = (SupportedTypeItem*)ItemAt(i);
272 if (!strcmp(item->Text(), type)) {
273 found = true;
274 break;
275 }
276 }
277
278 if (!found) {
279 // add type
280 AddItem(new SupportedTypeItem(type));
281 }
282 }
283
284 SortItems(&SupportedTypeItem::Compare);
285 } else
286 DropTargetListView::MessageReceived(message);
287 }
288
289
290 bool
AcceptsDrag(const BMessage * message)291 SupportedTypeListView::AcceptsDrag(const BMessage* message)
292 {
293 type_code type;
294 return message->GetInfo("refs", &type) == B_OK && type == B_REF_TYPE;
295 }
296
297
298 // #pragma mark -
299
300
ApplicationTypeWindow(const BMessage & settings,const BEntry & entry)301 ApplicationTypeWindow::ApplicationTypeWindow(const BMessage& settings, const BEntry& entry)
302 :
303 BWindow(_Frame(settings), B_TRANSLATE("Application type"), B_TITLED_WINDOW,
304 B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS | B_FRAME_EVENTS | B_AUTO_UPDATE_SIZE_LIMITS),
305 fChangedProperties(0)
306 {
307 float padding = be_control_look->DefaultItemSpacing();
308 BAlignment labelAlignment = be_control_look->DefaultLabelAlignment();
309
310 BMenuBar* menuBar = new BMenuBar((char*)NULL);
311 menuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
312
313 BMenu* menu = new BMenu(B_TRANSLATE("File"));
314 fSaveMenuItem = new BMenuItem(B_TRANSLATE("Save"),
315 new BMessage(kMsgSave), 'S');
316 fSaveMenuItem->SetEnabled(false);
317 menu->AddItem(fSaveMenuItem);
318 BMenuItem* item;
319 menu->AddItem(item = new BMenuItem(
320 B_TRANSLATE("Save into resource file" B_UTF8_ELLIPSIS), NULL));
321 item->SetEnabled(false);
322
323 menu->AddSeparatorItem();
324 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
325 new BMessage(B_QUIT_REQUESTED), 'W', B_COMMAND_KEY));
326 menuBar->AddItem(menu);
327
328 // Signature
329
330 fSignatureControl = new BTextControl("signature",
331 B_TRANSLATE("Signature:"), NULL, new BMessage(kMsgSignatureChanged));
332 fSignatureControl->SetModificationMessage(
333 new BMessage(kMsgSignatureChanged));
334
335 // filter out invalid characters that can't be part of a MIME type name
336 BTextView* textView = fSignatureControl->TextView();
337 textView->SetMaxBytes(B_MIME_TYPE_LENGTH);
338 const char* disallowedCharacters = "<>@,;:\"()[]?= ";
339 for (int32 i = 0; disallowedCharacters[i]; i++) {
340 textView->DisallowChar(disallowedCharacters[i]);
341 }
342
343 // "Application Flags" group
344
345 BBox* flagsBox = new BBox("flagsBox");
346
347 fFlagsCheckBox = new BCheckBox("flags", B_TRANSLATE("Application flags"),
348 new BMessage(kMsgToggleAppFlags));
349 fFlagsCheckBox->SetValue(B_CONTROL_ON);
350
351 fSingleLaunchButton = new BRadioButton("single",
352 B_TRANSLATE("Single launch"), new BMessage(kMsgAppFlagsChanged));
353
354 fMultipleLaunchButton = new BRadioButton("multiple",
355 B_TRANSLATE("Multiple launch"), new BMessage(kMsgAppFlagsChanged));
356
357 fExclusiveLaunchButton = new BRadioButton("exclusive",
358 B_TRANSLATE("Exclusive launch"), new BMessage(kMsgAppFlagsChanged));
359
360 fArgsOnlyCheckBox = new BCheckBox("args only", B_TRANSLATE("Args only"),
361 new BMessage(kMsgAppFlagsChanged));
362
363 fBackgroundAppCheckBox = new BCheckBox("background",
364 B_TRANSLATE("Background app"), new BMessage(kMsgAppFlagsChanged));
365
366 BLayoutBuilder::Grid<>(flagsBox, 0, 0)
367 .SetInsets(padding, padding * 2, padding, padding)
368 .Add(fSingleLaunchButton, 0, 0).Add(fArgsOnlyCheckBox, 1, 0)
369 .Add(fMultipleLaunchButton, 0, 1).Add(fBackgroundAppCheckBox, 1, 1)
370 .Add(fExclusiveLaunchButton, 0, 2);
371 flagsBox->SetLabel(fFlagsCheckBox);
372
373 // "Icon" group
374
375 BBox* iconBox = new BBox("IconBox");
376 iconBox->SetLabel(B_TRANSLATE("Icon"));
377 fIconView = new IconView("icon");
378 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged));
379 BLayoutBuilder::Group<>(iconBox, B_HORIZONTAL)
380 .SetInsets(padding, padding * 2, padding, padding)
381 .Add(fIconView);
382
383 // "Supported Types" group
384
385 BBox* typeBox = new BBox("typesBox");
386 typeBox->SetLabel(B_TRANSLATE("Supported types"));
387
388 fTypeListView = new SupportedTypeListView("Suppported Types",
389 B_SINGLE_SELECTION_LIST);
390 fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected));
391
392 BScrollView* scrollView = new BScrollView("type scrollview", fTypeListView,
393 B_FRAME_EVENTS | B_WILL_DRAW, false, true);
394
395 fAddTypeButton = new BButton("add type",
396 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddType));
397
398 fRemoveTypeButton = new BButton("remove type", B_TRANSLATE("Remove"),
399 new BMessage(kMsgRemoveType));
400
401 fTypeIconView = new IconView("type icon");
402 BGroupView* iconHolder = new BGroupView(B_HORIZONTAL);
403 iconHolder->AddChild(fTypeIconView);
404 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged));
405
406 BLayoutBuilder::Grid<>(typeBox, padding, padding)
407 .SetInsets(padding, padding * 2, padding, padding)
408 .Add(scrollView, 0, 0, 1, 5)
409 .Add(fAddTypeButton, 1, 0, 1, 2)
410 .Add(fRemoveTypeButton, 1, 2, 1, 2)
411 .Add(iconHolder, 2, 1, 1, 2)
412 .SetColumnWeight(0, 3)
413 .SetColumnWeight(1, 2)
414 .SetColumnWeight(2, 1);
415
416 iconHolder->SetExplicitAlignment(
417 BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE));
418
419 // "Version Info" group
420
421 BBox* versionBox = new BBox("versionBox");
422 versionBox->SetLabel(B_TRANSLATE("Version info"));
423
424 fMajorVersionControl = new BTextControl("version major",
425 B_TRANSLATE("Version:"), NULL, new BMessage(kMsgVersionInfoChanged));
426 _MakeNumberTextControl(fMajorVersionControl);
427
428 fMiddleVersionControl = new BTextControl("version middle", ".", NULL,
429 new BMessage(kMsgVersionInfoChanged));
430 _MakeNumberTextControl(fMiddleVersionControl);
431
432 fMinorVersionControl = new BTextControl("version minor", ".", NULL,
433 new BMessage(kMsgVersionInfoChanged));
434 _MakeNumberTextControl(fMinorVersionControl);
435
436 fVarietyMenu = new BPopUpMenu("variety", true, true);
437 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Development"),
438 new BMessage(kMsgVersionInfoChanged)));
439 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Alpha"),
440 new BMessage(kMsgVersionInfoChanged)));
441 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Beta"),
442 new BMessage(kMsgVersionInfoChanged)));
443 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Gamma"),
444 new BMessage(kMsgVersionInfoChanged)));
445 item = new BMenuItem(B_TRANSLATE("Golden master"),
446 new BMessage(kMsgVersionInfoChanged));
447 fVarietyMenu->AddItem(item);
448 item->SetMarked(true);
449 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Final"),
450 new BMessage(kMsgVersionInfoChanged)));
451
452 BMenuField* varietyField = new BMenuField("", fVarietyMenu);
453 fInternalVersionControl = new BTextControl("version internal", "/", NULL,
454 new BMessage(kMsgVersionInfoChanged));
455 fShortDescriptionControl = new BTextControl("short description",
456 B_TRANSLATE("Short description:"), NULL,
457 new BMessage(kMsgVersionInfoChanged));
458
459 // TODO: workaround for a GCC 4.1.0 bug? Or is that really what the standard says?
460 version_info versionInfo;
461 fShortDescriptionControl->TextView()->SetMaxBytes(
462 sizeof(versionInfo.short_info));
463
464 BStringView* longLabel = new BStringView(NULL,
465 B_TRANSLATE("Long description:"));
466 longLabel->SetExplicitAlignment(labelAlignment);
467 fLongDescriptionView = new TabFilteringTextView("long desc",
468 kMsgVersionInfoChanged);
469 fLongDescriptionView->SetMaxBytes(sizeof(versionInfo.long_info));
470
471 scrollView = new BScrollView("desc scrollview", fLongDescriptionView,
472 B_FRAME_EVENTS | B_WILL_DRAW, false, true);
473
474 // Manually set a minimum size for the version text controls
475 // TODO: the same does not work when applied to the layout items
476 float width = be_plain_font->StringWidth("99") + 16;
477 fMajorVersionControl->TextView()->SetExplicitMinSize(
478 BSize(width, B_SIZE_UNSET));
479 fMiddleVersionControl->TextView()->SetExplicitMinSize(
480 BSize(width, B_SIZE_UNSET));
481 fMinorVersionControl->TextView()->SetExplicitMinSize(
482 BSize(width, B_SIZE_UNSET));
483 fInternalVersionControl->TextView()->SetExplicitMinSize(
484 BSize(width, B_SIZE_UNSET));
485
486 BLayoutBuilder::Grid<>(versionBox, padding / 2, padding / 2)
487 .SetInsets(padding, padding * 2, padding, padding)
488 .AddTextControl(fMajorVersionControl, 0, 0)
489 .Add(fMiddleVersionControl, 2, 0, 2)
490 .Add(fMinorVersionControl, 4, 0, 2)
491 .Add(varietyField, 6, 0, 3)
492 .Add(fInternalVersionControl, 9, 0, 2)
493 .AddTextControl(fShortDescriptionControl, 0, 1,
494 B_ALIGN_HORIZONTAL_UNSET, 1, 10)
495 .Add(longLabel, 0, 2)
496 .Add(scrollView, 1, 2, 10, 3)
497 .SetRowWeight(3, 3);
498
499 // put it all together
500 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
501 .SetInsets(0, 0, 0, 0)
502 .Add(menuBar)
503 .AddGroup(B_VERTICAL, padding)
504 .SetInsets(padding, padding, padding, padding)
505 .Add(fSignatureControl)
506 .AddGroup(B_HORIZONTAL, padding)
507 .Add(flagsBox, 3)
508 .Add(iconBox, 1)
509 .End()
510 .Add(typeBox, 2)
511 .Add(versionBox);
512
513 SetKeyMenuBar(menuBar);
514
515 fSignatureControl->MakeFocus(true);
516 BMimeType::StartWatching(this);
517 _SetTo(entry);
518
519 Layout(false);
520 }
521
522
~ApplicationTypeWindow()523 ApplicationTypeWindow::~ApplicationTypeWindow()
524 {
525 BMimeType::StopWatching(this);
526 }
527
528 BRect
_Frame(const BMessage & settings) const529 ApplicationTypeWindow::_Frame(const BMessage& settings) const
530 {
531 BRect rect;
532 if (settings.FindRect("app_type_next_frame", &rect) == B_OK)
533 return rect;
534
535 return BRect(100.0f, 110.0f, 250.0f, 340.0f);
536 }
537
538 BString
_Title(const BEntry & entry)539 ApplicationTypeWindow::_Title(const BEntry& entry)
540 {
541 char name[B_FILE_NAME_LENGTH];
542 if (entry.GetName(name) != B_OK)
543 strcpy(name, "\"-\"");
544
545 BString title = B_TRANSLATE("%1 application type");
546 title.ReplaceFirst("%1", name);
547 return title;
548 }
549
550
551 void
_SetTo(const BEntry & entry)552 ApplicationTypeWindow::_SetTo(const BEntry& entry)
553 {
554 SetTitle(_Title(entry).String());
555 fEntry = entry;
556
557 // Retrieve Info
558
559 BFile file(&entry, B_READ_ONLY);
560 if (file.InitCheck() != B_OK)
561 return;
562
563 BAppFileInfo info(&file);
564 if (info.InitCheck() != B_OK)
565 return;
566
567 char signature[B_MIME_TYPE_LENGTH];
568 if (info.GetSignature(signature) != B_OK)
569 signature[0] = '\0';
570
571 bool gotFlags = false;
572 uint32 flags;
573 if (info.GetAppFlags(&flags) == B_OK)
574 gotFlags = true;
575 else
576 flags = B_MULTIPLE_LAUNCH;
577
578 version_info versionInfo;
579 if (info.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) != B_OK)
580 memset(&versionInfo, 0, sizeof(version_info));
581
582 // Set Controls
583
584 fSignatureControl->SetModificationMessage(NULL);
585 fSignatureControl->SetText(signature);
586 fSignatureControl->SetModificationMessage(
587 new BMessage(kMsgSignatureChanged));
588
589 // flags
590
591 switch (flags & (B_SINGLE_LAUNCH | B_MULTIPLE_LAUNCH | B_EXCLUSIVE_LAUNCH)) {
592 case B_SINGLE_LAUNCH:
593 fSingleLaunchButton->SetValue(B_CONTROL_ON);
594 break;
595
596 case B_EXCLUSIVE_LAUNCH:
597 fExclusiveLaunchButton->SetValue(B_CONTROL_ON);
598 break;
599
600 case B_MULTIPLE_LAUNCH:
601 default:
602 fMultipleLaunchButton->SetValue(B_CONTROL_ON);
603 break;
604 }
605
606 fArgsOnlyCheckBox->SetValue((flags & B_ARGV_ONLY) != 0);
607 fBackgroundAppCheckBox->SetValue((flags & B_BACKGROUND_APP) != 0);
608 fFlagsCheckBox->SetValue(gotFlags);
609
610 _UpdateAppFlagsEnabled();
611
612 // icon
613
614 entry_ref ref;
615 if (entry.GetRef(&ref) == B_OK)
616 fIcon.SetTo(ref);
617 else
618 fIcon.Unset();
619
620 fIconView->SetModificationMessage(NULL);
621 fIconView->SetTo(&fIcon);
622 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged));
623
624 // supported types
625
626 BMessage supportedTypes;
627 info.GetSupportedTypes(&supportedTypes);
628
629 for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
630 BListItem* item = fTypeListView->RemoveItem(i);
631 delete item;
632 }
633
634 const char* type;
635 for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) {
636 SupportedTypeItem* item = new SupportedTypeItem(type);
637
638 entry_ref ref;
639 if (fEntry.GetRef(&ref) == B_OK)
640 item->SetIcon(ref, type);
641
642 fTypeListView->AddItem(item);
643 }
644 fTypeListView->SortItems(&SupportedTypeItem::Compare);
645 fTypeIconView->SetModificationMessage(NULL);
646 fTypeIconView->SetTo(NULL);
647 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged));
648 fTypeIconView->SetEnabled(false);
649 fRemoveTypeButton->SetEnabled(false);
650
651 // version info
652
653 char text[256];
654 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.major);
655 fMajorVersionControl->SetText(text);
656 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.middle);
657 fMiddleVersionControl->SetText(text);
658 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.minor);
659 fMinorVersionControl->SetText(text);
660
661 if (versionInfo.variety >= (uint32)fVarietyMenu->CountItems())
662 versionInfo.variety = 0;
663 BMenuItem* item = fVarietyMenu->ItemAt(versionInfo.variety);
664 if (item != NULL)
665 item->SetMarked(true);
666
667 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.internal);
668 fInternalVersionControl->SetText(text);
669
670 fShortDescriptionControl->SetText(versionInfo.short_info);
671 fLongDescriptionView->SetText(versionInfo.long_info);
672
673 // store original data
674
675 fOriginalInfo.signature = signature;
676 fOriginalInfo.gotFlags = gotFlags;
677 fOriginalInfo.flags = gotFlags ? flags : 0;
678 fOriginalInfo.versionInfo = versionInfo;
679 fOriginalInfo.supportedTypes = _SupportedTypes();
680 // The list view has the types sorted possibly differently
681 // to the supportedTypes message, so don't use that here, but
682 // get the sorted message instead.
683 fOriginalInfo.iconChanged = false;
684 fOriginalInfo.typeIconsChanged = false;
685
686 fChangedProperties = 0;
687 _CheckSaveMenuItem(0);
688 }
689
690
691 void
_UpdateAppFlagsEnabled()692 ApplicationTypeWindow::_UpdateAppFlagsEnabled()
693 {
694 bool enabled = fFlagsCheckBox->Value() != B_CONTROL_OFF;
695
696 fSingleLaunchButton->SetEnabled(enabled);
697 fMultipleLaunchButton->SetEnabled(enabled);
698 fExclusiveLaunchButton->SetEnabled(enabled);
699 fArgsOnlyCheckBox->SetEnabled(enabled);
700 fBackgroundAppCheckBox->SetEnabled(enabled);
701 }
702
703
704 void
_MakeNumberTextControl(BTextControl * control)705 ApplicationTypeWindow::_MakeNumberTextControl(BTextControl* control)
706 {
707 // filter out invalid characters that can't be part of a MIME type name
708 BTextView* textView = control->TextView();
709 textView->SetMaxBytes(10);
710
711 for (int32 i = 0; i < 256; i++) {
712 if (!isdigit(i))
713 textView->DisallowChar(i);
714 }
715 }
716
717
718 void
_Save()719 ApplicationTypeWindow::_Save()
720 {
721 BFile file;
722 status_t status = file.SetTo(&fEntry, B_READ_WRITE);
723 if (status != B_OK)
724 return;
725
726 BAppFileInfo info(&file);
727 status = info.InitCheck();
728 if (status != B_OK)
729 return;
730
731 // Retrieve Info
732
733 uint32 flags = 0;
734 bool gotFlags = _Flags(flags);
735 BMessage supportedTypes = _SupportedTypes();
736 version_info versionInfo = _VersionInfo();
737
738 // Save
739
740 status = info.SetSignature(fSignatureControl->Text());
741 if (status == B_OK) {
742 if (gotFlags)
743 status = info.SetAppFlags(flags);
744 else
745 status = info.RemoveAppFlags();
746 }
747 if (status == B_OK)
748 status = info.SetVersionInfo(&versionInfo, B_APP_VERSION_KIND);
749 if (status == B_OK)
750 fIcon.CopyTo(info, NULL, true);
751
752 // supported types and their icons
753 if (status == B_OK)
754 status = info.SetSupportedTypes(&supportedTypes);
755
756 for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
757 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
758 fTypeListView->ItemAt(i));
759
760 if (item != NULL)
761 item->Icon().CopyTo(info, item->Type(), true);
762 }
763
764 // reset the saved info
765 fOriginalInfo.signature = fSignatureControl->Text();
766 fOriginalInfo.gotFlags = gotFlags;
767 fOriginalInfo.flags = flags;
768 fOriginalInfo.versionInfo = versionInfo;
769 fOriginalInfo.supportedTypes = supportedTypes;
770 fOriginalInfo.iconChanged = false;
771 fOriginalInfo.typeIconsChanged = false;
772
773 fChangedProperties = 0;
774 _CheckSaveMenuItem(0);
775 }
776
777
778 void
_CheckSaveMenuItem(uint32 flags)779 ApplicationTypeWindow::_CheckSaveMenuItem(uint32 flags)
780 {
781 fChangedProperties = _NeedsSaving(flags);
782 fSaveMenuItem->SetEnabled(fChangedProperties != 0);
783 }
784
785
786 bool
operator !=(const version_info & a,const version_info & b)787 operator!=(const version_info& a, const version_info& b)
788 {
789 return a.major != b.major || a.middle != b.middle || a.minor != b.minor
790 || a.variety != b.variety || a.internal != b.internal
791 || strcmp(a.short_info, b.short_info) != 0
792 || strcmp(a.long_info, b.long_info) != 0;
793 }
794
795
796 uint32
_NeedsSaving(uint32 _flags) const797 ApplicationTypeWindow::_NeedsSaving(uint32 _flags) const
798 {
799 uint32 flags = fChangedProperties;
800 if (_flags & CHECK_SIGNATUR) {
801 if (fOriginalInfo.signature != fSignatureControl->Text())
802 flags |= CHECK_SIGNATUR;
803 else
804 flags &= ~CHECK_SIGNATUR;
805 }
806
807 if (_flags & CHECK_FLAGS) {
808 uint32 appFlags = 0;
809 bool gotFlags = _Flags(appFlags);
810 if (fOriginalInfo.gotFlags != gotFlags
811 || fOriginalInfo.flags != appFlags) {
812 flags |= CHECK_FLAGS;
813 } else
814 flags &= ~CHECK_FLAGS;
815 }
816
817 if (_flags & CHECK_VERSION) {
818 if (fOriginalInfo.versionInfo != _VersionInfo())
819 flags |= CHECK_VERSION;
820 else
821 flags &= ~CHECK_VERSION;
822 }
823
824 if (_flags & CHECK_ICON) {
825 if (fOriginalInfo.iconChanged)
826 flags |= CHECK_ICON;
827 else
828 flags &= ~CHECK_ICON;
829 }
830
831 if (_flags & CHECK_TYPES) {
832 if (!fOriginalInfo.supportedTypes.HasSameData(_SupportedTypes()))
833 flags |= CHECK_TYPES;
834 else
835 flags &= ~CHECK_TYPES;
836 }
837
838 if (_flags & CHECK_TYPE_ICONS) {
839 if (fOriginalInfo.typeIconsChanged)
840 flags |= CHECK_TYPE_ICONS;
841 else
842 flags &= ~CHECK_TYPE_ICONS;
843 }
844
845 return flags;
846 }
847
848
849 // #pragma mark -
850
851
852 bool
_Flags(uint32 & flags) const853 ApplicationTypeWindow::_Flags(uint32& flags) const
854 {
855 flags = 0;
856 if (fFlagsCheckBox->Value() != B_CONTROL_OFF) {
857 if (fSingleLaunchButton->Value() != B_CONTROL_OFF)
858 flags |= B_SINGLE_LAUNCH;
859 else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF)
860 flags |= B_MULTIPLE_LAUNCH;
861 else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF)
862 flags |= B_EXCLUSIVE_LAUNCH;
863
864 if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF)
865 flags |= B_ARGV_ONLY;
866 if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF)
867 flags |= B_BACKGROUND_APP;
868 return true;
869 }
870 return false;
871 }
872
873
874 BMessage
_SupportedTypes() const875 ApplicationTypeWindow::_SupportedTypes() const
876 {
877 BMessage supportedTypes;
878 for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
879 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
880 fTypeListView->ItemAt(i));
881
882 if (item != NULL)
883 supportedTypes.AddString("types", item->Type());
884 }
885 return supportedTypes;
886 }
887
888
889 version_info
_VersionInfo() const890 ApplicationTypeWindow::_VersionInfo() const
891 {
892 version_info versionInfo;
893 versionInfo.major = atol(fMajorVersionControl->Text());
894 versionInfo.middle = atol(fMiddleVersionControl->Text());
895 versionInfo.minor = atol(fMinorVersionControl->Text());
896 versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked());
897 versionInfo.internal = atol(fInternalVersionControl->Text());
898 strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(),
899 sizeof(versionInfo.short_info));
900 strlcpy(versionInfo.long_info, fLongDescriptionView->Text(),
901 sizeof(versionInfo.long_info));
902 return versionInfo;
903 }
904
905
906 // #pragma mark -
907
908
909 void
MessageReceived(BMessage * message)910 ApplicationTypeWindow::MessageReceived(BMessage* message)
911 {
912 switch (message->what) {
913 case kMsgToggleAppFlags:
914 _UpdateAppFlagsEnabled();
915 _CheckSaveMenuItem(CHECK_FLAGS);
916 break;
917
918 case kMsgSignatureChanged:
919 _CheckSaveMenuItem(CHECK_SIGNATUR);
920 break;
921
922 case kMsgAppFlagsChanged:
923 _CheckSaveMenuItem(CHECK_FLAGS);
924 break;
925
926 case kMsgIconChanged:
927 fOriginalInfo.iconChanged = true;
928 _CheckSaveMenuItem(CHECK_ICON);
929 break;
930
931 case kMsgTypeIconsChanged:
932 fOriginalInfo.typeIconsChanged = true;
933 _CheckSaveMenuItem(CHECK_TYPE_ICONS);
934 break;
935
936 case kMsgVersionInfoChanged:
937 _CheckSaveMenuItem(CHECK_VERSION);
938 break;
939
940 case kMsgSave:
941 _Save();
942 break;
943
944 case kMsgTypeSelected:
945 {
946 int32 index;
947 if (message->FindInt32("index", &index) == B_OK) {
948 SupportedTypeItem* item
949 = (SupportedTypeItem*)fTypeListView->ItemAt(index);
950
951 fTypeIconView->SetModificationMessage(NULL);
952 fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL);
953 fTypeIconView->SetModificationMessage(
954 new BMessage(kMsgTypeIconsChanged));
955 fTypeIconView->SetEnabled(item != NULL);
956 fRemoveTypeButton->SetEnabled(item != NULL);
957
958 _CheckSaveMenuItem(CHECK_TYPES);
959 }
960 break;
961 }
962
963 case kMsgAddType:
964 {
965 BWindow* window = new TypeListWindow(NULL,
966 kMsgTypeAdded, this);
967 window->Show();
968 break;
969 }
970
971 case kMsgTypeAdded:
972 {
973 const char* type;
974 if (message->FindString("type", &type) != B_OK)
975 break;
976
977 // check if this type already exists
978
979 SupportedTypeItem* newItem = new SupportedTypeItem(type);
980 int32 insertAt = 0;
981
982 for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
983 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
984 fTypeListView->ItemAt(i));
985 if (item == NULL)
986 continue;
987
988 int compare = strcasecmp(item->Type(), type);
989 if (!compare) {
990 // type does already exist, select it and bail out
991 delete newItem;
992 newItem = NULL;
993 fTypeListView->Select(i);
994 break;
995 }
996 if (compare < 0)
997 insertAt = i + 1;
998 }
999
1000 if (newItem == NULL)
1001 break;
1002
1003 fTypeListView->AddItem(newItem, insertAt);
1004 fTypeListView->Select(insertAt);
1005
1006 _CheckSaveMenuItem(CHECK_TYPES);
1007 break;
1008 }
1009
1010 case kMsgRemoveType:
1011 {
1012 int32 index = fTypeListView->CurrentSelection();
1013 if (index < 0)
1014 break;
1015
1016 delete fTypeListView->RemoveItem(index);
1017 fTypeIconView->SetModificationMessage(NULL);
1018 fTypeIconView->SetTo(NULL);
1019 fTypeIconView->SetModificationMessage(
1020 new BMessage(kMsgTypeIconsChanged));
1021 fTypeIconView->SetEnabled(false);
1022 fRemoveTypeButton->SetEnabled(false);
1023
1024 _CheckSaveMenuItem(CHECK_TYPES);
1025 break;
1026 }
1027
1028 case B_SIMPLE_DATA:
1029 {
1030 entry_ref ref;
1031 if (message->FindRef("refs", &ref) != B_OK)
1032 break;
1033
1034 // TODO: add to supported types
1035 break;
1036 }
1037
1038 case B_META_MIME_CHANGED:
1039 const char* type;
1040 int32 which;
1041 if (message->FindString("be:type", &type) != B_OK
1042 || message->FindInt32("be:which", &which) != B_OK)
1043 break;
1044
1045 // TODO: update supported types names
1046 // if (which == B_MIME_TYPE_DELETED)
1047
1048 // _CheckSaveMenuItem(...);
1049 break;
1050
1051 default:
1052 BWindow::MessageReceived(message);
1053 }
1054 }
1055
1056
1057 bool
QuitRequested()1058 ApplicationTypeWindow::QuitRequested()
1059 {
1060 if (_NeedsSaving(CHECK_ALL) != 0) {
1061 BAlert* alert = new BAlert(B_TRANSLATE("Save request"),
1062 B_TRANSLATE("Save changes before closing?"),
1063 B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"),
1064 B_TRANSLATE("Save"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING,
1065 B_WARNING_ALERT);
1066 alert->SetShortcut(0, B_ESCAPE);
1067 alert->SetShortcut(1, 'd');
1068 alert->SetShortcut(2, 's');
1069
1070 int32 choice = alert->Go();
1071 switch (choice) {
1072 case 0:
1073 return false;
1074 case 1:
1075 break;
1076 case 2:
1077 _Save();
1078 break;
1079 }
1080 }
1081
1082 BMessage update(kMsgSettingsChanged);
1083 update.AddRect("app_type_next_frame", Frame());
1084 be_app_messenger.SendMessage(&update);
1085
1086 be_app->PostMessage(kMsgTypeWindowClosed);
1087 return true;
1088 }
1089
1090