xref: /haiku/src/preferences/filetypes/ExtensionWindow.cpp (revision 2600324b57fa31cdea1627d584d314f2a579c4a8)
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 status_t
42 add_extensions(BMimeType& type, BList& list, const char* removeExtension)
43 {
44 	BMessage extensions;
45 	status_t status = type.GetFileExtensions(&extensions);
46 	if (status < B_OK)
47 		return status;
48 
49 	// replace the entry, and remove any equivalent entries
50 	BList newList;
51 	newList.AddList(&list);
52 
53 	const char* extension;
54 	for (int32 i = 0; extensions.FindString("extensions", i,
55 			&extension) == B_OK; i++) {
56 		bool add = true;
57 		for (int32 j = list.CountItems(); j-- > 0;) {
58 			if ((removeExtension && !strcmp(removeExtension, extension))
59 				|| !strcmp((const char*)list.ItemAt(j), extension)) {
60 				// remove this item
61 				continue;
62 			}
63 		}
64 
65 		if (add)
66 			newList.AddItem((void *)extension);
67 	}
68 
69 	newList.SortItems(compare_extensions);
70 
71 	// Copy them to a new message (their memory is still part of the
72 	// original BMessage)
73 	BMessage newExtensions;
74 	for (int32 i = 0; i < newList.CountItems(); i++) {
75 		newExtensions.AddString("extensions", (const char*)newList.ItemAt(i));
76 	}
77 
78 	return type.SetFileExtensions(&newExtensions);
79 }
80 
81 
82 status_t
83 replace_extension(BMimeType& type, const char* newExtension, const char* oldExtension)
84 {
85 	BList list;
86 	list.AddItem((void *)newExtension);
87 
88 	return add_extensions(type, list, oldExtension);
89 }
90 
91 
92 //	#pragma mark -
93 
94 
95 ExtensionWindow::ExtensionWindow(FileTypesWindow* target, BMimeType& type,
96 		const char* extension)
97 	: BWindow(BRect(100, 100, 350, 200), "Extension", B_MODAL_WINDOW_LOOK,
98 		B_MODAL_SUBSET_WINDOW_FEEL, B_NOT_ZOOMABLE | B_NOT_V_RESIZABLE
99 			| B_ASYNCHRONOUS_CONTROLS),
100 	fTarget(target),
101 	fMimeType(type.Type()),
102 	fExtension(extension)
103 {
104 	BRect rect = Bounds();
105 	BView* topView = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW);
106 	topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
107 	AddChild(topView);
108 
109 	rect.InsetBy(8.0f, 8.0f);
110 	fExtensionControl = new BTextControl(rect, "extension", "Extension:", extension,
111 		NULL, B_FOLLOW_LEFT_RIGHT);
112 
113 	float labelWidth = fExtensionControl->StringWidth(fExtensionControl->Label()) + 2.0f;
114 	fExtensionControl->SetModificationMessage(new BMessage(kMsgExtensionUpdated));
115 	fExtensionControl->SetDivider(labelWidth);
116 	fExtensionControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
117 
118 	// filter out invalid characters that can't be part of an extension
119 	BTextView* textView = fExtensionControl->TextView();
120 	const char* disallowedCharacters = "/:";
121 	for (int32 i = 0; disallowedCharacters[i]; i++) {
122 		textView->DisallowChar(disallowedCharacters[i]);
123 	}
124 
125 	float width, height;
126 	fExtensionControl->GetPreferredSize(&width, &height);
127 	fExtensionControl->ResizeTo(rect.Width(), height);
128 	topView->AddChild(fExtensionControl);
129 
130 	fAcceptButton = new BButton(rect, "add", extension ? "Done" : "Add",
131 		new BMessage(kMsgAccept), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
132 	fAcceptButton->ResizeToPreferred();
133 	fAcceptButton->MoveTo(Bounds().Width() - 8.0f - fAcceptButton->Bounds().Width(),
134 		Bounds().Height() - 8.0f - fAcceptButton->Bounds().Height());
135 	fAcceptButton->SetEnabled(false);
136 	topView->AddChild(fAcceptButton);
137 
138 	BButton* button = new BButton(rect, "cancel", "Cancel",
139 		new BMessage(B_QUIT_REQUESTED), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
140 	button->ResizeToPreferred();
141 	button->MoveTo(fAcceptButton->Frame().left - 10.0f - button->Bounds().Width(),
142 		fAcceptButton->Frame().top);
143 	topView->AddChild(button);
144 
145 	ResizeTo(labelWidth * 4.0f + 24.0f, fExtensionControl->Bounds().Height()
146 		+ fAcceptButton->Bounds().Height() + 28.0f);
147 	SetSizeLimits(button->Bounds().Width() + fAcceptButton->Bounds().Width() + 26.0f,
148 		32767.0f, Frame().Height(), Frame().Height());
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("Could not change file extensions", status);
197 
198 			PostMessage(B_QUIT_REQUESTED);
199 			break;
200 		}
201 
202 		default:
203 			BWindow::MessageReceived(message);
204 			break;
205 	}
206 }
207 
208