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