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