xref: /haiku/src/preferences/filetypes/ExtensionWindow.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 "ExtensionWindow.h"
8 #include "FileTypes.h"
9 #include "FileTypesWindow.h"
10 
11 #include <Button.h>
12 #include <MenuField.h>
13 #include <MenuItem.h>
14 #include <Mime.h>
15 #include <PopUpMenu.h>
16 #include <String.h>
17 #include <TextControl.h>
18 
19 #include <string.h>
20 
21 
22 const uint32 kMsgExtensionUpdated = 'exup';
23 const uint32 kMsgAccept = 'acpt';
24 
25 
26 static int
27 compare_extensions(const void* _a, const void* _b)
28 {
29 	const char* a = *(const char **)_a;
30 	const char* b = *(const char **)_b;
31 
32 	int compare = strcasecmp(a, b);
33 	if (compare != 0)
34 		return compare;
35 
36 	// sort lower case characters first
37 	return -strcmp(a, b);
38 }
39 
40 
41 //	#pragma mark -
42 
43 
44 ExtensionWindow::ExtensionWindow(FileTypesWindow* target, BMimeType& type,
45 		const char* extension)
46 	: BWindow(BRect(100, 100, 350, 200), "Extension", B_MODAL_WINDOW_LOOK,
47 		B_MODAL_SUBSET_WINDOW_FEEL, B_NOT_ZOOMABLE | B_NOT_V_RESIZABLE
48 			| B_ASYNCHRONOUS_CONTROLS),
49 	fTarget(target),
50 	fMimeType(type.Type()),
51 	fExtension(extension)
52 {
53 	BRect rect = Bounds();
54 	BView* topView = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW);
55 	topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
56 	AddChild(topView);
57 
58 	rect.InsetBy(8.0f, 8.0f);
59 	fExtensionControl = new BTextControl(rect, "extension", "Extension:", extension,
60 		NULL, B_FOLLOW_LEFT_RIGHT);
61 
62 	float labelWidth = fExtensionControl->StringWidth(fExtensionControl->Label()) + 2.0f;
63 	fExtensionControl->SetModificationMessage(new BMessage(kMsgExtensionUpdated));
64 	fExtensionControl->SetDivider(labelWidth);
65 	fExtensionControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
66 
67 	// filter out invalid characters that can't be part of an extension
68 	BTextView* textView = fExtensionControl->TextView();
69 	const char* disallowedCharacters = "/:";
70 	for (int32 i = 0; disallowedCharacters[i]; i++) {
71 		textView->DisallowChar(disallowedCharacters[i]);
72 	}
73 
74 	float width, height;
75 	fExtensionControl->GetPreferredSize(&width, &height);
76 	fExtensionControl->ResizeTo(rect.Width(), height);
77 	topView->AddChild(fExtensionControl);
78 
79 	fAcceptButton = new BButton(rect, "add", extension ? "Done" : "Add",
80 		new BMessage(kMsgAccept), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
81 	fAcceptButton->ResizeToPreferred();
82 	fAcceptButton->MoveTo(Bounds().Width() - 8.0f - fAcceptButton->Bounds().Width(),
83 		Bounds().Height() - 8.0f - fAcceptButton->Bounds().Height());
84 	fAcceptButton->SetEnabled(false);
85 	topView->AddChild(fAcceptButton);
86 
87 	BButton* button = new BButton(rect, "cancel", "Cancel",
88 		new BMessage(B_QUIT_REQUESTED), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
89 	button->ResizeToPreferred();
90 	button->MoveTo(fAcceptButton->Frame().left - 10.0f - button->Bounds().Width(),
91 		fAcceptButton->Frame().top);
92 	topView->AddChild(button);
93 
94 	ResizeTo(labelWidth * 4.0f + 24.0f, fExtensionControl->Bounds().Height()
95 		+ fAcceptButton->Bounds().Height() + 28.0f);
96 	SetSizeLimits(button->Bounds().Width() + fAcceptButton->Bounds().Width() + 26.0f,
97 		32767.0f, Frame().Height(), Frame().Height());
98 
99 	// omit the leading dot
100 	if (fExtension.ByteAt(0) == '.')
101 		fExtension.Remove(0, 1);
102 
103 	fAcceptButton->MakeDefault(true);
104 	fExtensionControl->MakeFocus(true);
105 
106 	target->PlaceSubWindow(this);
107 	AddToSubset(target);
108 }
109 
110 
111 ExtensionWindow::~ExtensionWindow()
112 {
113 }
114 
115 
116 void
117 ExtensionWindow::MessageReceived(BMessage* message)
118 {
119 	switch (message->what) {
120 		case kMsgExtensionUpdated:
121 		{
122 			bool enabled = fExtensionControl->Text() != NULL
123 				&& fExtensionControl->Text()[0] != '\0';
124 			if (enabled) {
125 				// There is some text, but we only accept it, if it
126 				// changed the previous extension
127 				enabled = strcmp(fExtensionControl->Text(), fExtension.String());
128 			}
129 
130 			if (fAcceptButton->IsEnabled() != enabled)
131 				fAcceptButton->SetEnabled(enabled);
132 			break;
133 		}
134 
135 		case kMsgAccept:
136 		{
137 			const char* newExtension = fExtensionControl->Text();
138 			// omit the leading dot
139 			if (newExtension[0] == '.')
140 				newExtension++;
141 
142 			BMessage extensions;
143 			status_t status = fMimeType.GetFileExtensions(&extensions);
144 			if (status == B_OK) {
145 				// replace the entry, and remove any equivalent entries
146 				BList list;
147 				list.AddItem((void *)newExtension);
148 
149 				const char* extension;
150 				for (int32 i = 0; extensions.FindString("extensions", i,
151 						&extension) == B_OK; i++) {
152 					if (!strcmp(fExtension.String(), extension)
153 						|| !strcmp(newExtension, extension)) {
154 						// remove this item
155 						continue;
156 					}
157 
158 					list.AddItem((void *)extension);
159 				}
160 
161 				list.SortItems(compare_extensions);
162 
163 				// Copy them to a new message (their memory is still part of the
164 				// original BMessage)
165 				BMessage newExtensions;
166 				for (int32 i = 0; i < list.CountItems(); i++) {
167 					newExtensions.AddString("extensions", (const char*)list.ItemAt(i));
168 				}
169 
170 				status = fMimeType.SetFileExtensions(&newExtensions);
171 			}
172 
173 			if (status != B_OK)
174 				error_alert("Could not change file extensions", status);
175 
176 			PostMessage(B_QUIT_REQUESTED);
177 			break;
178 		}
179 
180 		default:
181 			BWindow::MessageReceived(message);
182 			break;
183 	}
184 }
185 
186