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