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