xref: /haiku/src/preferences/filetypes/FileTypes.cpp (revision 83b1a68c52ba3e0e8796282759f694b7fdddf06d)
1 /*
2  * Copyright 2006-2007, Axel Dörfler, axeld@pinc-software.de.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ApplicationTypesWindow.h"
8 #include "ApplicationTypeWindow.h"
9 #include "FileTypes.h"
10 #include "FileTypesWindow.h"
11 #include "FileTypeWindow.h"
12 
13 #include <AppFileInfo.h>
14 #include <Application.h>
15 #include <Alert.h>
16 #include <Catalog.h>
17 #include <Locale.h>
18 #include <TextView.h>
19 #include <FilePanel.h>
20 #include <FindDirectory.h>
21 #include <Directory.h>
22 #include <Entry.h>
23 #include <Path.h>
24 #include <Resources.h>
25 
26 #include <stdio.h>
27 #include <strings.h>
28 
29 
30 #undef B_TRANSLATION_CONTEXT
31 #define B_TRANSLATION_CONTEXT "FileTypes"
32 
33 
34 const char* kSignature = "application/x-vnd.Haiku-FileTypes";
35 
36 static const uint32 kMsgFileTypesSettings = 'FTst';
37 static const uint32 kCascadeOffset = 20;
38 
39 
40 class Settings {
41 public:
42 								Settings();
43 								~Settings();
44 
45 			const BMessage&		Message() const { return fMessage; }
46 			void				UpdateFrom(BMessage* message);
47 
48 private:
49 			void				_SetDefaults();
50 			status_t			_Open(BFile* file, int32 mode);
51 
52 			BMessage			fMessage;
53 			bool				fUpdated;
54 };
55 
56 class FileTypes : public BApplication {
57 public:
58 								FileTypes();
59 	virtual						~FileTypes();
60 
61 	virtual	void				ReadyToRun();
62 
63 	virtual	void				RefsReceived(BMessage* message);
64 	virtual	void				ArgvReceived(int32 argc, char** argv);
65 	virtual	void				MessageReceived(BMessage* message);
66 
67 	virtual	bool				QuitRequested();
68 
69 private:
70 			void				_WindowClosed();
71 
72 			Settings			fSettings;
73 			BFilePanel*			fFilePanel;
74 			BMessenger			fFilePanelTarget;
75 			FileTypesWindow*	fTypesWindow;
76 			BWindow*			fApplicationTypesWindow;
77 			uint32				fWindowCount;
78 			uint32				fTypeWindowCount;
79 			BString				fArgvType;
80 };
81 
82 
83 Settings::Settings()
84 	:
85 	fMessage(kMsgFileTypesSettings),
86 	fUpdated(false)
87 {
88 	_SetDefaults();
89 
90 	BFile file;
91 	if (_Open(&file, B_READ_ONLY) != B_OK)
92 		return;
93 
94 	BMessage settings;
95 	if (settings.Unflatten(&file) == B_OK) {
96 		// We don't unflatten into our default message to make sure
97 		// nothing is lost (because of old or corrupted on disk settings)
98 		UpdateFrom(&settings);
99 		fUpdated = false;
100 	}
101 }
102 
103 
104 Settings::~Settings()
105 {
106 	// only save the settings if something has changed
107 	if (!fUpdated)
108 		return;
109 
110 	BFile file;
111 	if (_Open(&file, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY) != B_OK)
112 		return;
113 
114 	fMessage.Flatten(&file);
115 }
116 
117 
118 void
119 Settings::UpdateFrom(BMessage* message)
120 {
121 	BRect frame;
122 	if (message->FindRect("file_types_frame", &frame) == B_OK)
123 		fMessage.ReplaceRect("file_types_frame", frame);
124 
125 	if (message->FindRect("app_types_frame", &frame) == B_OK)
126 		fMessage.ReplaceRect("app_types_frame", frame);
127 
128 	bool showIcons;
129 	if (message->FindBool("show_icons", &showIcons) == B_OK)
130 		fMessage.ReplaceBool("show_icons", showIcons);
131 
132 	bool showRule;
133 	if (message->FindBool("show_rule", &showRule) == B_OK)
134 		fMessage.ReplaceBool("show_rule", showRule);
135 
136 	float splitWeight;
137 	if (message->FindFloat("left_split_weight", &splitWeight) == B_OK)
138 		fMessage.ReplaceFloat("left_split_weight", splitWeight);
139 	if (message->FindFloat("right_split_weight", &splitWeight) == B_OK)
140 		fMessage.ReplaceFloat("right_split_weight", splitWeight);
141 
142 	fUpdated = true;
143 }
144 
145 
146 void
147 Settings::_SetDefaults()
148 {
149 	fMessage.AddRect("file_types_frame", BRect(80.0f, 80.0f, 600.0f, 480.0f));
150 	fMessage.AddRect("app_types_frame", BRect(100.0f, 100.0f, 540.0f, 480.0f));
151 	fMessage.AddBool("show_icons", true);
152 	fMessage.AddBool("show_rule", false);
153 	fMessage.AddFloat("left_split_weight", 0.2);
154 	fMessage.AddFloat("right_split_weight", 0.8);
155 }
156 
157 
158 status_t
159 Settings::_Open(BFile* file, int32 mode)
160 {
161 	BPath path;
162 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
163 		return B_ERROR;
164 
165 	path.Append("FileTypes settings");
166 
167 	return file->SetTo(path.Path(), mode);
168 }
169 
170 
171 //	#pragma mark -
172 
173 
174 FileTypes::FileTypes()
175 	:
176 	BApplication(kSignature),
177 	fTypesWindow(NULL),
178 	fApplicationTypesWindow(NULL),
179 	fWindowCount(0),
180 	fTypeWindowCount(0)
181 {
182 	fFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL,
183 		B_FILE_NODE | B_DIRECTORY_NODE, false);
184 }
185 
186 
187 FileTypes::~FileTypes()
188 {
189 	delete fFilePanel;
190 }
191 
192 
193 void
194 FileTypes::ReadyToRun()
195 {
196 	// are there already windows open?
197 	if (CountWindows() != 1)
198 		return;
199 
200 	// if not, open the FileTypes window
201 	PostMessage(kMsgOpenTypesWindow);
202 }
203 
204 
205 void
206 FileTypes::RefsReceived(BMessage* message)
207 {
208 	bool traverseLinks = (modifiers() & B_SHIFT_KEY) == 0;
209 
210 	// filter out applications and entries we can't open
211 	int32 index = 0;
212 	entry_ref ref;
213 	while (message->FindRef("refs", index++, &ref) == B_OK) {
214 		BEntry entry;
215 		BFile file;
216 
217 		status_t status = entry.SetTo(&ref, traverseLinks);
218 		if (status == B_OK)
219 			status = file.SetTo(&entry, B_READ_ONLY);
220 
221 		if (status != B_OK) {
222 			// file cannot be opened
223 
224 			char buffer[1024];
225 			snprintf(buffer, sizeof(buffer),
226 				B_TRANSLATE("Could not open \"%s\":\n"
227 				"%s"),
228 				ref.name, strerror(status));
229 
230 			BAlert* alert = new BAlert(B_TRANSLATE("FileTypes request"),
231 				buffer, B_TRANSLATE("OK"), NULL, NULL,
232 				B_WIDTH_AS_USUAL, B_STOP_ALERT);
233 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
234 			alert->Go();
235 
236 			message->RemoveData("refs", --index);
237 			continue;
238 		}
239 
240 		if (!is_application(file) && !is_resource(file)) {
241 			if (entry.GetRef(&ref) == B_OK)
242 				message->ReplaceRef("refs", index - 1, &ref);
243 			continue;
244 		}
245 
246 		// remove application from list
247 		message->RemoveData("refs", --index);
248 
249 		// There are some refs left that want to be handled by the type window
250 		BPoint point(100.0f + kCascadeOffset * fTypeWindowCount,
251 			110.0f + kCascadeOffset * fTypeWindowCount);
252 
253 		BWindow* window = new ApplicationTypeWindow(point, entry);
254 		window->Show();
255 
256 		fTypeWindowCount++;
257 		fWindowCount++;
258 	}
259 
260 	if (message->FindRef("refs", &ref) != B_OK)
261 		return;
262 
263 	// There are some refs left that want to be handled by the type window
264 	BPoint point(100.0f + kCascadeOffset * fTypeWindowCount,
265 		110.0f + kCascadeOffset * fTypeWindowCount);
266 
267 	BWindow* window = new FileTypeWindow(point, *message);
268 	window->Show();
269 
270 	fTypeWindowCount++;
271 	fWindowCount++;
272 }
273 
274 
275 void
276 FileTypes::ArgvReceived(int32 argc, char** argv)
277 {
278 	if (argc == 3 && strcmp(argv[1], "-type") == 0) {
279 		fArgvType = argv[2];
280 		return;
281 	}
282 
283 	BMessage* message = CurrentMessage();
284 
285 	BDirectory currentDirectory;
286 	if (message != NULL)
287 		currentDirectory.SetTo(message->FindString("cwd"));
288 
289 	BMessage refs;
290 
291 	for (int i = 1 ; i < argc ; i++) {
292 		BPath path;
293 		if (argv[i][0] == '/')
294 			path.SetTo(argv[i]);
295 		else
296 			path.SetTo(&currentDirectory, argv[i]);
297 
298 		status_t status;
299 		entry_ref ref;
300 		BEntry entry;
301 
302 		if ((status = entry.SetTo(path.Path(), false)) != B_OK
303 			|| (status = entry.GetRef(&ref)) != B_OK) {
304 			fprintf(stderr, "Could not open file \"%s\": %s\n",
305 				path.Path(), strerror(status));
306 			continue;
307 		}
308 
309 		refs.AddRef("refs", &ref);
310 	}
311 
312 	RefsReceived(&refs);
313 }
314 
315 
316 void
317 FileTypes::MessageReceived(BMessage* message)
318 {
319 	switch (message->what) {
320 		case kMsgSettingsChanged:
321 			fSettings.UpdateFrom(message);
322 			break;
323 
324 		case kMsgOpenTypesWindow:
325 			if (fTypesWindow == NULL) {
326 				fTypesWindow = new FileTypesWindow(fSettings.Message());
327 				if (fArgvType.Length() > 0) {
328 					// Set the window to the type that was requested on the
329 					// command line (-type), we do this only once, if we
330 					// ever opened more than one FileTypesWindow.
331 					fTypesWindow->SelectType(fArgvType.String());
332 					fArgvType = "";
333 				}
334 				fTypesWindow->Show();
335 				fWindowCount++;
336 			} else
337 				fTypesWindow->Activate(true);
338 			break;
339 		case kMsgTypesWindowClosed:
340 			fTypesWindow = NULL;
341 			_WindowClosed();
342 			break;
343 
344 		case kMsgOpenApplicationTypesWindow:
345 			if (fApplicationTypesWindow == NULL) {
346 				fApplicationTypesWindow = new ApplicationTypesWindow(
347 					fSettings.Message());
348 				fApplicationTypesWindow->Show();
349 				fWindowCount++;
350 			} else
351 				fApplicationTypesWindow->Activate(true);
352 			break;
353 		case kMsgApplicationTypesWindowClosed:
354 			fApplicationTypesWindow = NULL;
355 			_WindowClosed();
356 			break;
357 
358 		case kMsgTypeWindowClosed:
359 			fTypeWindowCount--;
360 			// supposed to fall through
361 
362 		case kMsgWindowClosed:
363 			_WindowClosed();
364 			break;
365 
366 
367 		case kMsgOpenFilePanel:
368 		{
369 			// the open file panel sends us a message when it's done
370 			const char* subTitle;
371 			if (message->FindString("title", &subTitle) != B_OK)
372 				subTitle = B_TRANSLATE("Open file");
373 
374 			int32 what;
375 			if (message->FindInt32("message", &what) != B_OK)
376 				what = B_REFS_RECEIVED;
377 
378 			BMessenger target;
379 			if (message->FindMessenger("target", &target) != B_OK)
380 				target = be_app_messenger;
381 
382 			BString title = B_TRANSLATE("FileTypes");
383 			if (subTitle != NULL && subTitle[0]) {
384 				title.Append(": ");
385 				title.Append(subTitle);
386 			}
387 
388 			fFilePanel->SetMessage(new BMessage(what));
389 			fFilePanel->Window()->SetTitle(title.String());
390 			fFilePanel->SetTarget(target);
391 
392 			if (!fFilePanel->IsShowing())
393 				fFilePanel->Show();
394 			break;
395 		}
396 
397 		case B_SILENT_RELAUNCH:
398 			// In case we were launched via the add-on, there is no types
399 			// window yet.
400 			if (fTypesWindow == NULL)
401 				PostMessage(kMsgOpenTypesWindow);
402 			break;
403 
404 		case B_CANCEL:
405 			if (fWindowCount == 0)
406 				PostMessage(B_QUIT_REQUESTED);
407 			break;
408 
409 		case B_SIMPLE_DATA:
410 			RefsReceived(message);
411 			break;
412 
413 		default:
414 			BApplication::MessageReceived(message);
415 			break;
416 	}
417 }
418 
419 
420 bool
421 FileTypes::QuitRequested()
422 {
423 	return true;
424 }
425 
426 
427 void
428 FileTypes::_WindowClosed()
429 {
430 	if (--fWindowCount == 0 && !fFilePanel->IsShowing())
431 		PostMessage(B_QUIT_REQUESTED);
432 }
433 
434 
435 //	#pragma mark -
436 
437 
438 bool
439 is_application(BFile& file)
440 {
441 	BAppFileInfo appInfo(&file);
442 	if (appInfo.InitCheck() != B_OK)
443 		return false;
444 
445 	char type[B_MIME_TYPE_LENGTH];
446 	if (appInfo.GetType(type) != B_OK
447 		|| strcasecmp(type, B_APP_MIME_TYPE))
448 		return false;
449 
450 	return true;
451 }
452 
453 
454 bool
455 is_resource(BFile& file)
456 {
457 	BResources resources(&file);
458 	if (resources.InitCheck() != B_OK)
459 		return false;
460 
461 	BNodeInfo nodeInfo(&file);
462 	char type[B_MIME_TYPE_LENGTH];
463 	if (nodeInfo.GetType(type) != B_OK
464 		|| strcasecmp(type, B_RESOURCE_MIME_TYPE))
465 		return false;
466 
467 	return true;
468 }
469 
470 
471 void
472 error_alert(const char* message, status_t status, alert_type type)
473 {
474 	char warning[512];
475 	if (status != B_OK) {
476 		snprintf(warning, sizeof(warning), "%s:\n\t%s\n", message,
477 			strerror(status));
478 	}
479 
480 	BAlert* alert = new BAlert(B_TRANSLATE("FileTypes request"),
481 		status == B_OK ? message : warning,
482 		B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, type);
483 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
484 		alert->Go();
485 }
486 
487 
488 int
489 main(int argc, char** argv)
490 {
491 	FileTypes probe;
492 	probe.Run();
493 	return 0;
494 }
495