xref: /haiku/src/preferences/filetypes/FileTypeWindow.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
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 "FileTypes.h"
8 #include "FileTypeWindow.h"
9 #include "IconView.h"
10 #include "PreferredAppMenu.h"
11 #include "TypeListWindow.h"
12 
13 #include <Application.h>
14 #include <Bitmap.h>
15 #include <Box.h>
16 #include <Button.h>
17 #include <Catalog.h>
18 #include <ControlLook.h>
19 #include <File.h>
20 #include <LayoutBuilder.h>
21 #include <Locale.h>
22 #include <MenuField.h>
23 #include <MenuItem.h>
24 #include <Mime.h>
25 #include <NodeInfo.h>
26 #include <PopUpMenu.h>
27 #include <SpaceLayoutItem.h>
28 #include <TextControl.h>
29 
30 #include <stdio.h>
31 
32 
33 #undef B_TRANSLATION_CONTEXT
34 #define B_TRANSLATION_CONTEXT "FileType Window"
35 
36 
37 const uint32 kMsgTypeEntered = 'type';
38 const uint32 kMsgSelectType = 'sltp';
39 const uint32 kMsgTypeSelected = 'tpsd';
40 const uint32 kMsgSameTypeAs = 'stpa';
41 const uint32 kMsgSameTypeAsOpened = 'stpO';
42 
43 const uint32 kMsgPreferredAppChosen = 'papc';
44 const uint32 kMsgSelectPreferredApp = 'slpa';
45 const uint32 kMsgSamePreferredAppAs = 'spaa';
46 const uint32 kMsgPreferredAppOpened = 'paOp';
47 const uint32 kMsgSamePreferredAppAsOpened = 'spaO';
48 
49 
50 FileTypeWindow::FileTypeWindow(BPoint position, const BMessage& refs)
51 	:
52 	BWindow(BRect(0.0f, 0.0f, 300.0f, 200.0f).OffsetBySelf(position),
53 		B_TRANSLATE("File type"), B_TITLED_WINDOW,
54 		B_NOT_V_RESIZABLE | B_NOT_ZOOMABLE
55 			| B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
56 {
57 	float padding = be_control_look->DefaultItemSpacing();
58 
59 	// "File Type" group
60 	BBox* fileTypeBox = new BBox("file type BBox");
61 	fileTypeBox->SetLabel(B_TRANSLATE("File type"));
62 
63 	fTypeControl = new BTextControl("type", NULL, "Type Control",
64 		new BMessage(kMsgTypeEntered));
65 
66 	// filter out invalid characters that can't be part of a MIME type name
67 	BTextView* textView = fTypeControl->TextView();
68 	const char* disallowedCharacters = "<>@,;:\"()[]?=";
69 	for (int32 i = 0; disallowedCharacters[i]; i++) {
70 		textView->DisallowChar(disallowedCharacters[i]);
71 	}
72 
73 	fSelectTypeButton = new BButton("select type",
74 		B_TRANSLATE("Select" B_UTF8_ELLIPSIS), new BMessage(kMsgSelectType));
75 
76 	fSameTypeAsButton = new BButton("same type as",
77 		B_TRANSLATE_COMMENT("Same as" B_UTF8_ELLIPSIS,
78 			"The same TYPE as ..."), new BMessage(kMsgSameTypeAs));
79 
80 	BLayoutBuilder::Grid<>(fileTypeBox, padding, padding / 2)
81 		.SetInsets(padding, padding * 2, padding, padding)
82 		.Add(fTypeControl, 0, 0, 3, 1)
83 		.Add(fSelectTypeButton, 0, 1)
84 		.Add(fSameTypeAsButton, 1, 1);
85 
86 	// "Icon" group
87 
88 	BBox* iconBox = new BBox("icon BBox");
89 	iconBox->SetLabel(B_TRANSLATE("Icon"));
90 	fIconView = new IconView("icon");
91 	BLayoutBuilder::Group<>(iconBox, B_HORIZONTAL)
92 		.SetInsets(padding, padding * 2, padding, padding)
93 		.Add(fIconView);
94 
95 	// "Preferred Application" group
96 
97 	BBox* preferredBox = new BBox("preferred BBox");
98 	preferredBox->SetLabel(B_TRANSLATE("Preferred application"));
99 
100 	BMenu* menu = new BPopUpMenu("preferred");
101 	BMenuItem* item;
102 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Default application"),
103 		new BMessage(kMsgPreferredAppChosen)));
104 	item->SetMarked(true);
105 
106 	fPreferredField = new BMenuField("preferred", NULL, menu);
107 
108 	fSelectAppButton = new BButton("select app",
109 		B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
110 		new BMessage(kMsgSelectPreferredApp));
111 
112 	fSameAppAsButton = new BButton("same app as",
113 		B_TRANSLATE_COMMENT("Same as" B_UTF8_ELLIPSIS,
114 			"The same APPLICATION as ..."),
115 			new BMessage(kMsgSamePreferredAppAs));
116 
117 	BLayoutBuilder::Grid<>(preferredBox, padding, padding / 2)
118 		.SetInsets(padding, padding * 2, padding, padding)
119 		.Add(fPreferredField, 0, 0, 3, 1)
120 		.Add(fSelectAppButton, 0, 1)
121 		.Add(fSameAppAsButton, 1, 1);
122 
123 	BLayoutBuilder::Grid<>(this)
124 		.SetInsets(padding)
125 		.Add(fileTypeBox, 0, 0, 2, 1)
126 		.Add(preferredBox, 0, 1, 1, 1)
127 		.Add(iconBox, 1, 1, 1, 1);
128 
129 	fTypeControl->MakeFocus(true);
130 	BMimeType::StartWatching(this);
131 	_SetTo(refs);
132 }
133 
134 
135 FileTypeWindow::~FileTypeWindow()
136 {
137 	BMimeType::StopWatching(this);
138 }
139 
140 
141 BString
142 FileTypeWindow::_Title(const BMessage& refs)
143 {
144 	BString title;
145 
146 	entry_ref ref;
147 	if (refs.FindRef("refs", 1, &ref) == B_OK) {
148 		bool same = false;
149 		BEntry entry, parent;
150 		if (entry.SetTo(&ref) == B_OK
151 			&& entry.GetParent(&parent) == B_OK) {
152 			same = true;
153 
154 			// Check if all entries have the same parent directory
155 			for (int32 i = 0; refs.FindRef("refs", i, &ref) == B_OK; i++) {
156 				BEntry directory;
157 				if (entry.SetTo(&ref) == B_OK
158 					&& entry.GetParent(&directory) == B_OK) {
159 					if (directory != parent) {
160 						same = false;
161 						break;
162 					}
163 				}
164 			}
165 		}
166 
167 		char name[B_FILE_NAME_LENGTH];
168 		if (same && parent.GetName(name) == B_OK) {
169 			char buffer[512];
170 			snprintf(buffer, sizeof(buffer),
171 				B_TRANSLATE("Multiple files from \"%s\" file type"), name);
172 			title = buffer;
173 		} else
174 			title = B_TRANSLATE("[Multiple files] file types");
175 	} else if (refs.FindRef("refs", 0, &ref) == B_OK) {
176 		char buffer[512];
177 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("%s file type"), ref.name);
178 		title = buffer;
179 	}
180 
181 	return title;
182 }
183 
184 
185 void
186 FileTypeWindow::_SetTo(const BMessage& refs)
187 {
188 	SetTitle(_Title(refs).String());
189 
190 	// get common info and icons
191 
192 	fCommonPreferredApp = "";
193 	fCommonType = "";
194 	entry_ref ref;
195 	for (int32 i = 0; refs.FindRef("refs", i, &ref) == B_OK; i++) {
196 		BNode node(&ref);
197 		if (node.InitCheck() != B_OK)
198 			continue;
199 
200 		BNodeInfo info(&node);
201 		if (info.InitCheck() != B_OK)
202 			continue;
203 
204 		// TODO: watch entries?
205 
206 		entry_ref* copiedRef = new entry_ref(ref);
207 		fEntries.AddItem(copiedRef);
208 
209 		char type[B_MIME_TYPE_LENGTH];
210 		if (info.GetType(type) != B_OK)
211 			type[0] = '\0';
212 
213 		if (i > 0) {
214 			if (fCommonType != type)
215 				fCommonType = "";
216 		} else
217 			fCommonType = type;
218 
219 		char preferredApp[B_MIME_TYPE_LENGTH];
220 		if (info.GetPreferredApp(preferredApp) != B_OK)
221 			preferredApp[0] = '\0';
222 
223 		if (i > 0) {
224 			if (fCommonPreferredApp != preferredApp)
225 				fCommonPreferredApp = "";
226 		} else
227 			fCommonPreferredApp = preferredApp;
228 
229 		if (i == 0)
230 			fIconView->SetTo(ref);
231 	}
232 
233 	fTypeControl->SetText(fCommonType.String());
234 	_UpdatePreferredApps();
235 
236 	fIconView->ShowIconHeap(fEntries.CountItems() != 1);
237 }
238 
239 
240 void
241 FileTypeWindow::_AdoptType(BMessage* message)
242 {
243 	entry_ref ref;
244 	if (message == NULL || message->FindRef("refs", &ref) != B_OK)
245 		return;
246 
247 	BNode node(&ref);
248 	status_t status = node.InitCheck();
249 
250 	char type[B_MIME_TYPE_LENGTH];
251 
252 	if (status == B_OK) {
253 			// get type from file
254 		BNodeInfo nodeInfo(&node);
255 		status = nodeInfo.InitCheck();
256 		if (status == B_OK) {
257 			if (nodeInfo.GetType(type) != B_OK)
258 				type[0] = '\0';
259 		}
260 	}
261 
262 	if (status != B_OK) {
263 		error_alert(B_TRANSLATE("Could not open file"), status);
264 		return;
265 	}
266 
267 	fCommonType = type;
268 	fTypeControl->SetText(type);
269 	_AdoptType();
270 }
271 
272 
273 void
274 FileTypeWindow::_AdoptType()
275 {
276 	for (int32 i = 0; i < fEntries.CountItems(); i++) {
277 		BNode node(fEntries.ItemAt(i));
278 		BNodeInfo info(&node);
279 		if (node.InitCheck() != B_OK
280 			|| info.InitCheck() != B_OK)
281 			continue;
282 
283 		info.SetType(fCommonType.String());
284 	}
285 }
286 
287 
288 void
289 FileTypeWindow::_AdoptPreferredApp(BMessage* message, bool sameAs)
290 {
291 	if (retrieve_preferred_app(message, sameAs, fCommonType.String(),
292 			fCommonPreferredApp) == B_OK) {
293 		_AdoptPreferredApp();
294 		_UpdatePreferredApps();
295 	}
296 }
297 
298 
299 void
300 FileTypeWindow::_AdoptPreferredApp()
301 {
302 	for (int32 i = 0; i < fEntries.CountItems(); i++) {
303 		BNode node(fEntries.ItemAt(i));
304 		if (fCommonPreferredApp.Length() == 0) {
305 			node.RemoveAttr("BEOS:PREF_APP");
306 			continue;
307 		}
308 
309 		BNodeInfo info(&node);
310 		if (node.InitCheck() != B_OK
311 			|| info.InitCheck() != B_OK)
312 			continue;
313 
314 		info.SetPreferredApp(fCommonPreferredApp.String());
315 	}
316 }
317 
318 
319 void
320 FileTypeWindow::_UpdatePreferredApps()
321 {
322 	BMimeType type(fCommonType.String());
323 	update_preferred_app_menu(fPreferredField->Menu(), &type,
324 		kMsgPreferredAppChosen, fCommonPreferredApp.String());
325 }
326 
327 
328 void
329 FileTypeWindow::MessageReceived(BMessage* message)
330 {
331 	switch (message->what) {
332 		// File Type group
333 
334 		case kMsgTypeEntered:
335 			fCommonType = fTypeControl->Text();
336 			_AdoptType();
337 			break;
338 
339 		case kMsgSelectType:
340 		{
341 			BWindow* window = new TypeListWindow(fCommonType.String(),
342 				kMsgTypeSelected, this);
343 			window->Show();
344 			break;
345 		}
346 		case kMsgTypeSelected:
347 		{
348 			const char* type;
349 			if (message->FindString("type", &type) == B_OK) {
350 				fCommonType = type;
351 				fTypeControl->SetText(type);
352 				_AdoptType();
353 			}
354 			break;
355 		}
356 
357 		case kMsgSameTypeAs:
358 		{
359 			BMessage panel(kMsgOpenFilePanel);
360 			panel.AddString("title", B_TRANSLATE("Select same type as"));
361 			panel.AddInt32("message", kMsgSameTypeAsOpened);
362 			panel.AddMessenger("target", this);
363 
364 			be_app_messenger.SendMessage(&panel);
365 			break;
366 		}
367 		case kMsgSameTypeAsOpened:
368 			_AdoptType(message);
369 			break;
370 
371 		// Preferred Application group
372 
373 		case kMsgPreferredAppChosen:
374 		{
375 			const char* signature;
376 			if (message->FindString("signature", &signature) == B_OK)
377 				fCommonPreferredApp = signature;
378 			else
379 				fCommonPreferredApp = "";
380 
381 			_AdoptPreferredApp();
382 			break;
383 		}
384 
385 		case kMsgSelectPreferredApp:
386 		{
387 			BMessage panel(kMsgOpenFilePanel);
388 			panel.AddString("title",
389 				B_TRANSLATE("Select preferred application"));
390 			panel.AddInt32("message", kMsgPreferredAppOpened);
391 			panel.AddMessenger("target", this);
392 
393 			be_app_messenger.SendMessage(&panel);
394 			break;
395 		}
396 		case kMsgPreferredAppOpened:
397 			_AdoptPreferredApp(message, false);
398 			break;
399 
400 		case kMsgSamePreferredAppAs:
401 		{
402 			BMessage panel(kMsgOpenFilePanel);
403 			panel.AddString("title",
404 				B_TRANSLATE("Select same preferred application as"));
405 			panel.AddInt32("message", kMsgSamePreferredAppAsOpened);
406 			panel.AddMessenger("target", this);
407 
408 			be_app_messenger.SendMessage(&panel);
409 			break;
410 		}
411 		case kMsgSamePreferredAppAsOpened:
412 			_AdoptPreferredApp(message, true);
413 			break;
414 
415 		// Other
416 
417 		case B_SIMPLE_DATA:
418 		{
419 			entry_ref ref;
420 			if (message->FindRef("refs", &ref) != B_OK)
421 				break;
422 
423 			BFile file(&ref, B_READ_ONLY);
424 			if (is_application(file))
425 				_AdoptPreferredApp(message, false);
426 			else
427 				_AdoptType(message);
428 			break;
429 		}
430 
431 		case B_META_MIME_CHANGED:
432 			const char* type;
433 			int32 which;
434 			if (message->FindString("be:type", &type) != B_OK
435 				|| message->FindInt32("be:which", &which) != B_OK)
436 				break;
437 
438 			if (which == B_MIME_TYPE_DELETED
439 				|| which == B_SUPPORTED_TYPES_CHANGED) {
440 				_UpdatePreferredApps();
441 			}
442 			break;
443 
444 		default:
445 			BWindow::MessageReceived(message);
446 	}
447 }
448 
449 
450 bool
451 FileTypeWindow::QuitRequested()
452 {
453 	be_app->PostMessage(kMsgTypeWindowClosed);
454 	return true;
455 }
456 
457