xref: /haiku/src/preferences/filetypes/ExtensionWindow.cpp (revision 91054f1d38dd7827c0f0ba9490c213775ec7b471)
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 //! newExtensionsList contains all the entries (char*) which are to be added.
42 status_t
43 merge_extensions(BMimeType& type, const BList& newExtensionsList,
44 	const char* removeExtension)
45 {
46 	BMessage extensions;
47 	status_t status = type.GetFileExtensions(&extensions);
48 	if (status < B_OK)
49 		return status;
50 
51 	// replace the entry, and remove any equivalent entries
52 	BList mergedList;
53 	mergedList.AddList(&newExtensionsList);
54 	int32 originalCount = mergedList.CountItems();
55 
56 	const char* extension;
57 	for (int32 i = 0; extensions.FindString("extensions", i,
58 			&extension) == B_OK; i++) {
59 
60 		for (int32 j = originalCount; j-- > 0;) {
61 			if (!strcmp((const char*)mergedList.ItemAt(j), extension)) {
62 				// Do not add this old item again, since it's already
63 				// there.
64 				mergedList.RemoveItem(j);
65 				originalCount--;
66 			}
67 		}
68 
69 		// The item will be added behind "originalCount", so we cannot
70 		// remove it accidentally in the next iterations, it's is added
71 		// for good.
72 		if (removeExtension == NULL || strcmp(removeExtension, extension))
73 			mergedList.AddItem((void *)extension);
74 	}
75 
76 	mergedList.SortItems(compare_extensions);
77 
78 	// Copy them to a new message (their memory is still part of the
79 	// original BMessage)
80 	BMessage newExtensions;
81 	for (int32 i = 0; i < mergedList.CountItems(); i++) {
82 		newExtensions.AddString("extensions",
83 			(const char*)mergedList.ItemAt(i));
84 	}
85 
86 	return type.SetFileExtensions(&newExtensions);
87 }
88 
89 
90 status_t
91 replace_extension(BMimeType& type, const char* newExtension,
92 	const char* oldExtension)
93 {
94 	BList list;
95 	list.AddItem((void *)newExtension);
96 
97 	return merge_extensions(type, list, oldExtension);
98 }
99 
100 
101 //	#pragma mark -
102 
103 
104 ExtensionWindow::ExtensionWindow(FileTypesWindow* target, BMimeType& type,
105 		const char* extension)
106 	: BWindow(BRect(100, 100, 350, 200), "Extension", B_MODAL_WINDOW_LOOK,
107 		B_MODAL_SUBSET_WINDOW_FEEL, B_NOT_ZOOMABLE | B_NOT_V_RESIZABLE
108 			| B_ASYNCHRONOUS_CONTROLS),
109 	fTarget(target),
110 	fMimeType(type.Type()),
111 	fExtension(extension)
112 {
113 	BRect rect = Bounds();
114 	BView* topView = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW);
115 	topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
116 	AddChild(topView);
117 
118 	rect.InsetBy(8.0f, 8.0f);
119 	fExtensionControl = new BTextControl(rect, "extension", "Extension:", extension,
120 		NULL, B_FOLLOW_LEFT_RIGHT);
121 
122 	float labelWidth = fExtensionControl->StringWidth(fExtensionControl->Label()) + 2.0f;
123 	fExtensionControl->SetModificationMessage(new BMessage(kMsgExtensionUpdated));
124 	fExtensionControl->SetDivider(labelWidth);
125 	fExtensionControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
126 
127 	// filter out invalid characters that can't be part of an extension
128 	BTextView* textView = fExtensionControl->TextView();
129 	const char* disallowedCharacters = "/:";
130 	for (int32 i = 0; disallowedCharacters[i]; i++) {
131 		textView->DisallowChar(disallowedCharacters[i]);
132 	}
133 
134 	float width, height;
135 	fExtensionControl->GetPreferredSize(&width, &height);
136 	fExtensionControl->ResizeTo(rect.Width(), height);
137 	topView->AddChild(fExtensionControl);
138 
139 	fAcceptButton = new BButton(rect, "add", extension ? "Done" : "Add",
140 		new BMessage(kMsgAccept), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
141 	fAcceptButton->ResizeToPreferred();
142 	fAcceptButton->MoveTo(Bounds().Width() - 8.0f - fAcceptButton->Bounds().Width(),
143 		Bounds().Height() - 8.0f - fAcceptButton->Bounds().Height());
144 	fAcceptButton->SetEnabled(false);
145 	topView->AddChild(fAcceptButton);
146 
147 	BButton* button = new BButton(rect, "cancel", "Cancel",
148 		new BMessage(B_QUIT_REQUESTED), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
149 	button->ResizeToPreferred();
150 	button->MoveTo(fAcceptButton->Frame().left - 10.0f - button->Bounds().Width(),
151 		fAcceptButton->Frame().top);
152 	topView->AddChild(button);
153 
154 	ResizeTo(labelWidth * 4.0f + 24.0f, fExtensionControl->Bounds().Height()
155 		+ fAcceptButton->Bounds().Height() + 28.0f);
156 	SetSizeLimits(button->Bounds().Width() + fAcceptButton->Bounds().Width() + 26.0f,
157 		32767.0f, Frame().Height(), Frame().Height());
158 
159 	// omit the leading dot
160 	if (fExtension.ByteAt(0) == '.')
161 		fExtension.Remove(0, 1);
162 
163 	fAcceptButton->MakeDefault(true);
164 	fExtensionControl->MakeFocus(true);
165 
166 	target->PlaceSubWindow(this);
167 	AddToSubset(target);
168 }
169 
170 
171 ExtensionWindow::~ExtensionWindow()
172 {
173 }
174 
175 
176 void
177 ExtensionWindow::MessageReceived(BMessage* message)
178 {
179 	switch (message->what) {
180 		case kMsgExtensionUpdated:
181 		{
182 			bool enabled = fExtensionControl->Text() != NULL
183 				&& fExtensionControl->Text()[0] != '\0';
184 			if (enabled) {
185 				// There is some text, but we only accept it, if it
186 				// changed the previous extension
187 				enabled = strcmp(fExtensionControl->Text(), fExtension.String());
188 			}
189 
190 			if (fAcceptButton->IsEnabled() != enabled)
191 				fAcceptButton->SetEnabled(enabled);
192 			break;
193 		}
194 
195 		case kMsgAccept:
196 		{
197 			const char* newExtension = fExtensionControl->Text();
198 			// omit the leading dot
199 			if (newExtension[0] == '.')
200 				newExtension++;
201 
202 			status_t status = replace_extension(fMimeType, newExtension,
203 				fExtension.String());
204 			if (status != B_OK)
205 				error_alert("Could not change file extensions", status);
206 
207 			PostMessage(B_QUIT_REQUESTED);
208 			break;
209 		}
210 
211 		default:
212 			BWindow::MessageReceived(message);
213 			break;
214 	}
215 }
216 
217