xref: /haiku/src/preferences/filetypes/FileTypes.cpp (revision e7d5c75dce28921de0dc981ed840205a67a0c0e5)
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 
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::_SetDefaults()
120 {
121 	fMessage.AddRect("file_types_frame", BRect(80.0f, 80.0f, 600.0f, 480.0f));
122 	fMessage.AddRect("app_types_frame", BRect(100.0f, 100.0f, 540.0f, 480.0f));
123 	fMessage.AddBool("show_icons", true);
124 	fMessage.AddBool("show_rule", false);
125 }
126 
127 
128 status_t
129 Settings::_Open(BFile *file, int32 mode)
130 {
131 	BPath path;
132 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
133 		return B_ERROR;
134 
135 	path.Append("FileTypes settings");
136 
137 	return file->SetTo(path.Path(), mode);
138 }
139 
140 
141 void
142 Settings::UpdateFrom(BMessage *message)
143 {
144 	BRect frame;
145 	if (message->FindRect("file_types_frame", &frame) == B_OK)
146 		fMessage.ReplaceRect("file_types_frame", frame);
147 
148 	if (message->FindRect("app_types_frame", &frame) == B_OK)
149 		fMessage.ReplaceRect("app_types_frame", frame);
150 
151 	bool showIcons;
152 	if (message->FindBool("show_icons", &showIcons) == B_OK)
153 		fMessage.ReplaceBool("show_icons", showIcons);
154 
155 	bool showRule;
156 	if (message->FindBool("show_rule", &showRule) == B_OK)
157 		fMessage.ReplaceBool("show_rule", showRule);
158 
159 	fUpdated = true;
160 }
161 
162 
163 //	#pragma mark -
164 
165 
166 FileTypes::FileTypes()
167 	: BApplication(kSignature),
168 	fTypesWindow(NULL),
169 	fApplicationTypesWindow(NULL),
170 	fWindowCount(0),
171 	fTypeWindowCount(0)
172 {
173 	fFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL,
174 		B_FILE_NODE | B_DIRECTORY_NODE, false);
175 }
176 
177 
178 FileTypes::~FileTypes()
179 {
180 	delete fFilePanel;
181 }
182 
183 
184 void
185 FileTypes::ReadyToRun()
186 {
187 	// are there already windows open?
188 	if (CountWindows() != 1)
189 		return;
190 
191 	// if not, open the FileTypes window
192 	PostMessage(kMsgOpenTypesWindow);
193 }
194 
195 
196 void
197 FileTypes::RefsReceived(BMessage *message)
198 {
199 	bool traverseLinks = (modifiers() & B_SHIFT_KEY) == 0;
200 
201 	// filter out applications and entries we can't open
202 	int32 index = 0;
203 	entry_ref ref;
204 	while (message->FindRef("refs", index++, &ref) == B_OK) {
205 		BEntry entry;
206 		BFile file;
207 
208 		status_t status = entry.SetTo(&ref, traverseLinks);
209 		if (status == B_OK)
210 			status = file.SetTo(&entry, B_READ_ONLY);
211 
212 		if (status != B_OK) {
213 			// file cannot be opened
214 
215 			char buffer[1024];
216 			snprintf(buffer, sizeof(buffer),
217 				B_TRANSLATE("Could not open \"%s\":\n"
218 				"%s"),
219 				ref.name, strerror(status));
220 
221 			(new BAlert(B_TRANSLATE("FileTypes request"),
222 				buffer, B_TRANSLATE("OK"), NULL, NULL,
223 				B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go();
224 
225 			message->RemoveData("refs", --index);
226 			continue;
227 		}
228 
229 		if (!is_application(file) && !is_resource(file)) {
230 			if (entry.GetRef(&ref) == B_OK)
231 				message->ReplaceRef("refs", index - 1, &ref);
232 			continue;
233 		}
234 
235 		// remove application from list
236 		message->RemoveData("refs", --index);
237 
238 		// There are some refs left that want to be handled by the type window
239 		BPoint point(100.0f + kCascadeOffset * fTypeWindowCount,
240 			110.0f + kCascadeOffset * fTypeWindowCount);
241 
242 		BWindow* window = new ApplicationTypeWindow(point, entry);
243 		window->Show();
244 
245 		fTypeWindowCount++;
246 		fWindowCount++;
247 	}
248 
249 	if (message->FindRef("refs", &ref) != B_OK)
250 		return;
251 
252 	// There are some refs left that want to be handled by the type window
253 	BPoint point(100.0f + kCascadeOffset * fTypeWindowCount,
254 		110.0f + kCascadeOffset * fTypeWindowCount);
255 
256 	BWindow* window = new FileTypeWindow(point, *message);
257 	window->Show();
258 
259 	fTypeWindowCount++;
260 	fWindowCount++;
261 }
262 
263 
264 void
265 FileTypes::ArgvReceived(int32 argc, char **argv)
266 {
267 	BMessage *message = CurrentMessage();
268 
269 	BDirectory currentDirectory;
270 	if (message)
271 		currentDirectory.SetTo(message->FindString("cwd"));
272 
273 	BMessage refs;
274 
275 	for (int i = 1 ; i < argc ; i++) {
276 		BPath path;
277 		if (argv[i][0] == '/')
278 			path.SetTo(argv[i]);
279 		else
280 			path.SetTo(&currentDirectory, argv[i]);
281 
282 		status_t status;
283 		entry_ref ref;
284 		BEntry entry;
285 
286 		if ((status = entry.SetTo(path.Path(), false)) != B_OK
287 			|| (status = entry.GetRef(&ref)) != B_OK) {
288 			fprintf(stderr, "Could not open file \"%s\": %s\n",
289 				path.Path(), strerror(status));
290 			continue;
291 		}
292 
293 		refs.AddRef("refs", &ref);
294 	}
295 
296 	RefsReceived(&refs);
297 }
298 
299 
300 void
301 FileTypes::_WindowClosed()
302 {
303 	if (--fWindowCount == 0 && !fFilePanel->IsShowing())
304 		PostMessage(B_QUIT_REQUESTED);
305 }
306 
307 
308 void
309 FileTypes::MessageReceived(BMessage *message)
310 {
311 	switch (message->what) {
312 		case kMsgSettingsChanged:
313 			fSettings.UpdateFrom(message);
314 			break;
315 
316 		case kMsgOpenTypesWindow:
317 			if (fTypesWindow == NULL) {
318 				fTypesWindow = new FileTypesWindow(fSettings.Message());
319 				fTypesWindow->Show();
320 				fWindowCount++;
321 			} else
322 				fTypesWindow->Activate(true);
323 			break;
324 		case kMsgTypesWindowClosed:
325 			fTypesWindow = NULL;
326 			_WindowClosed();
327 			break;
328 
329 		case kMsgOpenApplicationTypesWindow:
330 			if (fApplicationTypesWindow == NULL) {
331 				fApplicationTypesWindow = new ApplicationTypesWindow(
332 					fSettings.Message());
333 				fApplicationTypesWindow->Show();
334 				fWindowCount++;
335 			} else
336 				fApplicationTypesWindow->Activate(true);
337 			break;
338 		case kMsgApplicationTypesWindowClosed:
339 			fApplicationTypesWindow = NULL;
340 			_WindowClosed();
341 			break;
342 
343 		case kMsgTypeWindowClosed:
344 			fTypeWindowCount--;
345 			// supposed to fall through
346 
347 		case kMsgWindowClosed:
348 			_WindowClosed();
349 			break;
350 
351 
352 		case kMsgOpenFilePanel:
353 		{
354 			// the open file panel sends us a message when it's done
355 			const char* subTitle;
356 			if (message->FindString("title", &subTitle) != B_OK)
357 				subTitle = B_TRANSLATE("Open file");
358 
359 			int32 what;
360 			if (message->FindInt32("message", &what) != B_OK)
361 				what = B_REFS_RECEIVED;
362 
363 			BMessenger target;
364 			if (message->FindMessenger("target", &target) != B_OK)
365 				target = be_app_messenger;
366 
367 			BString title = B_TRANSLATE("FileTypes");
368 			if (subTitle != NULL && subTitle[0]) {
369 				title.Append(": ");
370 				title.Append(subTitle);
371 			}
372 
373 			fFilePanel->SetMessage(new BMessage(what));
374 			fFilePanel->Window()->SetTitle(title.String());
375 			fFilePanel->SetTarget(target);
376 
377 			if (!fFilePanel->IsShowing())
378 				fFilePanel->Show();
379 			break;
380 		}
381 
382 		case B_SILENT_RELAUNCH:
383 			// In case we were launched via the add-on, there is no types
384 			// window yet.
385 			if (fTypesWindow == NULL)
386 				PostMessage(kMsgOpenTypesWindow);
387 			break;
388 
389 		case B_CANCEL:
390 			if (fWindowCount == 0)
391 				PostMessage(B_QUIT_REQUESTED);
392 			break;
393 
394 		case B_SIMPLE_DATA:
395 			RefsReceived(message);
396 			break;
397 
398 		default:
399 			BApplication::MessageReceived(message);
400 			break;
401 	}
402 }
403 
404 
405 void
406 FileTypes::AboutRequested()
407 {
408 	BString aboutText(B_TRANSLATE("FileTypes"));
409 	int32 titleLength = aboutText.Length();
410 	aboutText << "\n";
411 	aboutText << B_TRANSLATE("\twritten by Axel Dörfler\n"
412 		"\tCopyright 2006-2007, Haiku.\n");
413 	BAlert *alert = new BAlert("about", aboutText.String(), B_TRANSLATE("OK"));
414 	BTextView *view = alert->TextView();
415 	BFont font;
416 
417 	view->SetStylable(true);
418 
419 	view->GetFont(&font);
420 	font.SetSize(18);
421 	font.SetFace(B_BOLD_FACE);
422 	view->SetFontAndColor(0, titleLength, &font);
423 
424 	alert->Go();
425 }
426 
427 
428 bool
429 FileTypes::QuitRequested()
430 {
431 	return true;
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 	(new BAlert(B_TRANSLATE("FileTypes request"),
481 		status == B_OK ? message : warning,
482 		B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, type))->Go();
483 }
484 
485 
486 int
487 main(int argc, char **argv)
488 {
489 	FileTypes probe;
490 
491 	probe.Run();
492 	return 0;
493 }
494