xref: /haiku/src/preferences/filetypes/ExtensionWindow.cpp (revision 8a990d5228b2d1099e3062180532ba709dfeef6d)
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 "ExtensionWindow.h"
8 #include "FileTypes.h"
9 #include "FileTypesWindow.h"
10 
11 #include <Button.h>
12 #include <Catalog.h>
13 #include <ControlLook.h>
14 #include <LayoutBuilder.h>
15 #include <Locale.h>
16 #include <MenuField.h>
17 #include <MenuItem.h>
18 #include <Mime.h>
19 #include <PopUpMenu.h>
20 #include <String.h>
21 #include <TextControl.h>
22 
23 #include <string.h>
24 
25 
26 #undef B_TRANSLATE_CONTEXT
27 #define B_TRANSLATE_CONTEXT "Extension Window"
28 
29 
30 const uint32 kMsgExtensionUpdated = 'exup';
31 const uint32 kMsgAccept = 'acpt';
32 
33 
34 static int
35 compare_extensions(const void* _a, const void* _b)
36 {
37 	const char* a = *(const char **)_a;
38 	const char* b = *(const char **)_b;
39 
40 	int compare = strcasecmp(a, b);
41 	if (compare != 0)
42 		return compare;
43 
44 	// sort lower case characters first
45 	return -strcmp(a, b);
46 }
47 
48 
49 //! newExtensionsList contains all the entries (char*) which are to be added.
50 status_t
51 merge_extensions(BMimeType& type, const BList& newExtensionsList,
52 	const char* removeExtension)
53 {
54 	BMessage extensions;
55 	status_t status = type.GetFileExtensions(&extensions);
56 	if (status < B_OK)
57 		return status;
58 
59 	// replace the entry, and remove any equivalent entries
60 	BList mergedList;
61 	mergedList.AddList(&newExtensionsList);
62 	int32 originalCount = mergedList.CountItems();
63 
64 	const char* extension;
65 	for (int32 i = 0; extensions.FindString("extensions", i,
66 			&extension) == B_OK; i++) {
67 
68 		for (int32 j = originalCount; j-- > 0;) {
69 			if (!strcmp((const char*)mergedList.ItemAt(j), extension)) {
70 				// Do not add this old item again, since it's already
71 				// there.
72 				mergedList.RemoveItem(j);
73 				originalCount--;
74 			}
75 		}
76 
77 		// The item will be added behind "originalCount", so we cannot
78 		// remove it accidentally in the next iterations, it's is added
79 		// for good.
80 		if (removeExtension == NULL || strcmp(removeExtension, extension))
81 			mergedList.AddItem((void *)extension);
82 	}
83 
84 	mergedList.SortItems(compare_extensions);
85 
86 	// Copy them to a new message (their memory is still part of the
87 	// original BMessage)
88 	BMessage newExtensions;
89 	for (int32 i = 0; i < mergedList.CountItems(); i++) {
90 		newExtensions.AddString("extensions",
91 			(const char*)mergedList.ItemAt(i));
92 	}
93 
94 	return type.SetFileExtensions(&newExtensions);
95 }
96 
97 
98 status_t
99 replace_extension(BMimeType& type, const char* newExtension,
100 	const char* oldExtension)
101 {
102 	BList list;
103 	list.AddItem((void *)newExtension);
104 
105 	return merge_extensions(type, list, oldExtension);
106 }
107 
108 
109 //	#pragma mark -
110 
111 
112 ExtensionWindow::ExtensionWindow(FileTypesWindow* target, BMimeType& type,
113 		const char* extension)
114 	: BWindow(BRect(100, 100, 350, 200), B_TRANSLATE("Extension"),
115 		B_MODAL_WINDOW_LOOK, B_MODAL_SUBSET_WINDOW_FEEL,
116 		B_NOT_ZOOMABLE | B_NOT_RESIZABLE
117 			| B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
118 	fTarget(target),
119 	fMimeType(type.Type()),
120 	fExtension(extension)
121 {
122 	fExtensionControl = new BTextControl(B_TRANSLATE("Extension:"),
123 		extension, NULL);
124 	fExtensionControl->SetModificationMessage(
125 		new BMessage(kMsgExtensionUpdated));
126 	fExtensionControl->SetAlignment(B_ALIGN_LEFT, B_ALIGN_LEFT);
127 
128 	// filter out invalid characters that can't be part of an extension
129 	BTextView* textView = fExtensionControl->TextView();
130 	const char* disallowedCharacters = "/:";
131 	for (int32 i = 0; disallowedCharacters[i]; i++) {
132 		textView->DisallowChar(disallowedCharacters[i]);
133 	}
134 
135 	fAcceptButton = new BButton(extension
136 		? B_TRANSLATE("Done") : B_TRANSLATE("Add"),
137 		new BMessage(kMsgAccept));
138 	fAcceptButton->SetEnabled(false);
139 
140 	BButton* button = new BButton(B_TRANSLATE("Cancel"),
141 		new BMessage(B_QUIT_REQUESTED));
142 
143 	float padding = be_control_look->DefaultItemSpacing();
144 	BLayoutBuilder::Grid<>(this, padding, padding)
145 		.SetInsets(padding, padding, padding, padding)
146 		.AddTextControl(fExtensionControl, 0, 0)
147 		.Add(fAcceptButton, 0, 1)
148 		.Add(button, 1, 1);
149 
150 	// omit the leading dot
151 	if (fExtension.ByteAt(0) == '.')
152 		fExtension.Remove(0, 1);
153 
154 	fAcceptButton->MakeDefault(true);
155 	fExtensionControl->MakeFocus(true);
156 
157 	target->PlaceSubWindow(this);
158 	AddToSubset(target);
159 }
160 
161 
162 ExtensionWindow::~ExtensionWindow()
163 {
164 }
165 
166 
167 void
168 ExtensionWindow::MessageReceived(BMessage* message)
169 {
170 	switch (message->what) {
171 		case kMsgExtensionUpdated:
172 		{
173 			bool enabled = fExtensionControl->Text() != NULL
174 				&& fExtensionControl->Text()[0] != '\0';
175 			if (enabled) {
176 				// There is some text, but we only accept it, if it
177 				// changed the previous extension
178 				enabled = strcmp(fExtensionControl->Text(), fExtension.String());
179 			}
180 
181 			if (fAcceptButton->IsEnabled() != enabled)
182 				fAcceptButton->SetEnabled(enabled);
183 			break;
184 		}
185 
186 		case kMsgAccept:
187 		{
188 			const char* newExtension = fExtensionControl->Text();
189 			// omit the leading dot
190 			if (newExtension[0] == '.')
191 				newExtension++;
192 
193 			status_t status = replace_extension(fMimeType, newExtension,
194 				fExtension.String());
195 			if (status != B_OK)
196 				error_alert(B_TRANSLATE("Could not change file extensions"),
197 					status);
198 
199 			PostMessage(B_QUIT_REQUESTED);
200 			break;
201 		}
202 
203 		default:
204 			BWindow::MessageReceived(message);
205 			break;
206 	}
207 }
208