xref: /haiku/src/preferences/filetypes/FileTypeWindow.cpp (revision 3ecb7fb4415b319b6aac606551d51efad21037df)
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, 2, 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 			panel.AddBool("allowDirs", true);
364 
365 			be_app_messenger.SendMessage(&panel);
366 			break;
367 		}
368 		case kMsgSameTypeAsOpened:
369 			_AdoptType(message);
370 			break;
371 
372 		// Preferred Application group
373 
374 		case kMsgPreferredAppChosen:
375 		{
376 			const char* signature;
377 			if (message->FindString("signature", &signature) == B_OK)
378 				fCommonPreferredApp = signature;
379 			else
380 				fCommonPreferredApp = "";
381 
382 			_AdoptPreferredApp();
383 			break;
384 		}
385 
386 		case kMsgSelectPreferredApp:
387 		{
388 			BMessage panel(kMsgOpenFilePanel);
389 			panel.AddString("title",
390 				B_TRANSLATE("Select preferred application"));
391 			panel.AddInt32("message", kMsgPreferredAppOpened);
392 			panel.AddMessenger("target", this);
393 
394 			be_app_messenger.SendMessage(&panel);
395 			break;
396 		}
397 		case kMsgPreferredAppOpened:
398 			_AdoptPreferredApp(message, false);
399 			break;
400 
401 		case kMsgSamePreferredAppAs:
402 		{
403 			BMessage panel(kMsgOpenFilePanel);
404 			panel.AddString("title",
405 				B_TRANSLATE("Select same preferred application as"));
406 			panel.AddInt32("message", kMsgSamePreferredAppAsOpened);
407 			panel.AddMessenger("target", this);
408 			panel.AddBool("allowDirs", true);
409 
410 			be_app_messenger.SendMessage(&panel);
411 			break;
412 		}
413 		case kMsgSamePreferredAppAsOpened:
414 			_AdoptPreferredApp(message, true);
415 			break;
416 
417 		// Other
418 
419 		case B_SIMPLE_DATA:
420 		{
421 			entry_ref ref;
422 			if (message->FindRef("refs", &ref) != B_OK)
423 				break;
424 
425 			BFile file(&ref, B_READ_ONLY);
426 			if (is_application(file))
427 				_AdoptPreferredApp(message, false);
428 			else
429 				_AdoptType(message);
430 			break;
431 		}
432 
433 		case B_META_MIME_CHANGED:
434 			const char* type;
435 			int32 which;
436 			if (message->FindString("be:type", &type) != B_OK
437 				|| message->FindInt32("be:which", &which) != B_OK)
438 				break;
439 
440 			if (which == B_MIME_TYPE_DELETED
441 				|| which == B_SUPPORTED_TYPES_CHANGED) {
442 				_UpdatePreferredApps();
443 			}
444 			break;
445 
446 		default:
447 			BWindow::MessageReceived(message);
448 	}
449 }
450 
451 
452 bool
453 FileTypeWindow::QuitRequested()
454 {
455 	be_app->PostMessage(kMsgTypeWindowClosed);
456 	return true;
457 }
458 
459