xref: /haiku/src/preferences/filetypes/FileTypes.cpp (revision b46615c55ad2c8fe6de54412055a0713da3d610a)
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 <string.h>
28 
29 
30 #undef B_TRANSLATE_CONTEXT
31 #define B_TRANSLATE_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 			(new BAlert(B_TRANSLATE("FileTypes request"),
231 				buffer, B_TRANSLATE("OK"), NULL, NULL,
232 				B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go();
233 
234 			message->RemoveData("refs", --index);
235 			continue;
236 		}
237 
238 		if (!is_application(file) && !is_resource(file)) {
239 			if (entry.GetRef(&ref) == B_OK)
240 				message->ReplaceRef("refs", index - 1, &ref);
241 			continue;
242 		}
243 
244 		// remove application from list
245 		message->RemoveData("refs", --index);
246 
247 		// There are some refs left that want to be handled by the type window
248 		BPoint point(100.0f + kCascadeOffset * fTypeWindowCount,
249 			110.0f + kCascadeOffset * fTypeWindowCount);
250 
251 		BWindow* window = new ApplicationTypeWindow(point, entry);
252 		window->Show();
253 
254 		fTypeWindowCount++;
255 		fWindowCount++;
256 	}
257 
258 	if (message->FindRef("refs", &ref) != B_OK)
259 		return;
260 
261 	// There are some refs left that want to be handled by the type window
262 	BPoint point(100.0f + kCascadeOffset * fTypeWindowCount,
263 		110.0f + kCascadeOffset * fTypeWindowCount);
264 
265 	BWindow* window = new FileTypeWindow(point, *message);
266 	window->Show();
267 
268 	fTypeWindowCount++;
269 	fWindowCount++;
270 }
271 
272 
273 void
274 FileTypes::ArgvReceived(int32 argc, char** argv)
275 {
276 	if (argc == 3 && strcmp(argv[1], "-type") == 0) {
277 		fArgvType = argv[2];
278 		return;
279 	}
280 
281 	BMessage* message = CurrentMessage();
282 
283 	BDirectory currentDirectory;
284 	if (message != NULL)
285 		currentDirectory.SetTo(message->FindString("cwd"));
286 
287 	BMessage refs;
288 
289 	for (int i = 1 ; i < argc ; i++) {
290 		BPath path;
291 		if (argv[i][0] == '/')
292 			path.SetTo(argv[i]);
293 		else
294 			path.SetTo(&currentDirectory, argv[i]);
295 
296 		status_t status;
297 		entry_ref ref;
298 		BEntry entry;
299 
300 		if ((status = entry.SetTo(path.Path(), false)) != B_OK
301 			|| (status = entry.GetRef(&ref)) != B_OK) {
302 			fprintf(stderr, "Could not open file \"%s\": %s\n",
303 				path.Path(), strerror(status));
304 			continue;
305 		}
306 
307 		refs.AddRef("refs", &ref);
308 	}
309 
310 	RefsReceived(&refs);
311 }
312 
313 
314 void
315 FileTypes::MessageReceived(BMessage* message)
316 {
317 	switch (message->what) {
318 		case kMsgSettingsChanged:
319 			fSettings.UpdateFrom(message);
320 			break;
321 
322 		case kMsgOpenTypesWindow:
323 			if (fTypesWindow == NULL) {
324 				fTypesWindow = new FileTypesWindow(fSettings.Message());
325 				if (fArgvType.Length() > 0) {
326 					// Set the window to the type that was requested on the
327 					// command line (-type), we do this only once, if we
328 					// ever opened more than one FileTypesWindow.
329 					fTypesWindow->SelectType(fArgvType.String());
330 					fArgvType = "";
331 				}
332 				fTypesWindow->Show();
333 				fWindowCount++;
334 			} else
335 				fTypesWindow->Activate(true);
336 			break;
337 		case kMsgTypesWindowClosed:
338 			fTypesWindow = NULL;
339 			_WindowClosed();
340 			break;
341 
342 		case kMsgOpenApplicationTypesWindow:
343 			if (fApplicationTypesWindow == NULL) {
344 				fApplicationTypesWindow = new ApplicationTypesWindow(
345 					fSettings.Message());
346 				fApplicationTypesWindow->Show();
347 				fWindowCount++;
348 			} else
349 				fApplicationTypesWindow->Activate(true);
350 			break;
351 		case kMsgApplicationTypesWindowClosed:
352 			fApplicationTypesWindow = NULL;
353 			_WindowClosed();
354 			break;
355 
356 		case kMsgTypeWindowClosed:
357 			fTypeWindowCount--;
358 			// supposed to fall through
359 
360 		case kMsgWindowClosed:
361 			_WindowClosed();
362 			break;
363 
364 
365 		case kMsgOpenFilePanel:
366 		{
367 			// the open file panel sends us a message when it's done
368 			const char* subTitle;
369 			if (message->FindString("title", &subTitle) != B_OK)
370 				subTitle = B_TRANSLATE("Open file");
371 
372 			int32 what;
373 			if (message->FindInt32("message", &what) != B_OK)
374 				what = B_REFS_RECEIVED;
375 
376 			BMessenger target;
377 			if (message->FindMessenger("target", &target) != B_OK)
378 				target = be_app_messenger;
379 
380 			BString title = B_TRANSLATE("FileTypes");
381 			if (subTitle != NULL && subTitle[0]) {
382 				title.Append(": ");
383 				title.Append(subTitle);
384 			}
385 
386 			fFilePanel->SetMessage(new BMessage(what));
387 			fFilePanel->Window()->SetTitle(title.String());
388 			fFilePanel->SetTarget(target);
389 
390 			if (!fFilePanel->IsShowing())
391 				fFilePanel->Show();
392 			break;
393 		}
394 
395 		case B_SILENT_RELAUNCH:
396 			// In case we were launched via the add-on, there is no types
397 			// window yet.
398 			if (fTypesWindow == NULL)
399 				PostMessage(kMsgOpenTypesWindow);
400 			break;
401 
402 		case B_CANCEL:
403 			if (fWindowCount == 0)
404 				PostMessage(B_QUIT_REQUESTED);
405 			break;
406 
407 		case B_SIMPLE_DATA:
408 			RefsReceived(message);
409 			break;
410 
411 		default:
412 			BApplication::MessageReceived(message);
413 			break;
414 	}
415 }
416 
417 
418 bool
419 FileTypes::QuitRequested()
420 {
421 	return true;
422 }
423 
424 
425 void
426 FileTypes::_WindowClosed()
427 {
428 	if (--fWindowCount == 0 && !fFilePanel->IsShowing())
429 		PostMessage(B_QUIT_REQUESTED);
430 }
431 
432 
433 //	#pragma mark -
434 
435 
436 bool
437 is_application(BFile& file)
438 {
439 	BAppFileInfo appInfo(&file);
440 	if (appInfo.InitCheck() != B_OK)
441 		return false;
442 
443 	char type[B_MIME_TYPE_LENGTH];
444 	if (appInfo.GetType(type) != B_OK
445 		|| strcasecmp(type, B_APP_MIME_TYPE))
446 		return false;
447 
448 	return true;
449 }
450 
451 
452 bool
453 is_resource(BFile& file)
454 {
455 	BResources resources(&file);
456 	if (resources.InitCheck() != B_OK)
457 		return false;
458 
459 	BNodeInfo nodeInfo(&file);
460 	char type[B_MIME_TYPE_LENGTH];
461 	if (nodeInfo.GetType(type) != B_OK
462 		|| strcasecmp(type, B_RESOURCE_MIME_TYPE))
463 		return false;
464 
465 	return true;
466 }
467 
468 
469 void
470 error_alert(const char* message, status_t status, alert_type type)
471 {
472 	char warning[512];
473 	if (status != B_OK) {
474 		snprintf(warning, sizeof(warning), "%s:\n\t%s\n", message,
475 			strerror(status));
476 	}
477 
478 	(new BAlert(B_TRANSLATE("FileTypes request"),
479 		status == B_OK ? message : warning,
480 		B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, type))->Go();
481 }
482 
483 
484 int
485 main(int argc, char** argv)
486 {
487 	FileTypes probe;
488 	probe.Run();
489 	return 0;
490 }
491