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