xref: /haiku/src/apps/icon-o-matic/IconEditorApp.cpp (revision e688bf23d48bfd1216a0cacbdbda5e35a1bcd779)
1 /*
2  * Copyright 2006, 2011, Stephan Aßmus <superstippi@gmx.de>.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "IconEditorApp.h"
8 
9 #include <new>
10 #include <stdio.h>
11 #include <string.h>
12 
13 #include <Alert.h>
14 #include <Catalog.h>
15 #include <FilePanel.h>
16 #include <FindDirectory.h>
17 #include <IconEditorProtocol.h>
18 #include <Locale.h>
19 #include <Message.h>
20 #include <Mime.h>
21 #include <Path.h>
22 
23 #include "support_settings.h"
24 
25 #include "AutoLocker.h"
26 #include "Defines.h"
27 #include "MainWindow.h"
28 #include "SavePanel.h"
29 
30 
31 #undef B_TRANSLATION_CONTEXT
32 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Main"
33 
34 
35 using std::nothrow;
36 
37 static const char* kAppSig = "application/x-vnd.haiku-icon_o_matic";
38 
39 static const float kWindowOffset = 20;
40 
41 
42 IconEditorApp::IconEditorApp()
43 	:
44 	BApplication(kAppSig),
45 	fWindowCount(0),
46 	fLastWindowFrame(50, 50, 900, 750),
47 
48 	fOpenPanel(NULL),
49 	fSavePanel(NULL),
50 
51 	fLastOpenPath(""),
52 	fLastSavePath(""),
53 	fLastExportPath("")
54 {
55 	// create file panels
56 	BMessenger messenger(this, this);
57 	BMessage message(B_REFS_RECEIVED);
58 	fOpenPanel = new BFilePanel(B_OPEN_PANEL, &messenger, NULL, B_FILE_NODE,
59 		true, &message);
60 
61 	message.what = MSG_SAVE_AS;
62 	fSavePanel = new SavePanel("save panel", &messenger, NULL, B_FILE_NODE
63 		| B_DIRECTORY_NODE | B_SYMLINK_NODE, false, &message);
64 
65 	_RestoreSettings();
66 }
67 
68 
69 IconEditorApp::~IconEditorApp()
70 {
71 	delete fOpenPanel;
72 	delete fSavePanel;
73 }
74 
75 
76 // #pragma mark -
77 
78 
79 bool
80 IconEditorApp::QuitRequested()
81 {
82 	// Run the QuitRequested() hook in each window's own thread. Otherwise
83 	// the BAlert which a window shows when an icon is not saved will not
84 	// repaint the window. (BAlerts check which thread is running Go() and
85 	// will repaint windows when it's a BWindow.)
86 	bool quit = true;
87 	for (int32 i = 0; BWindow* window = WindowAt(i); i++) {
88 		if (!window->Lock())
89 			continue;
90 		// Try to cast the window while the pointer must be valid.
91 		MainWindow* mainWindow = dynamic_cast<MainWindow*>(window);
92 		window->Unlock();
93 		if (mainWindow == NULL)
94 			continue;
95 		BMessenger messenger(window, window);
96 		BMessage reply;
97 		if (messenger.SendMessage(B_QUIT_REQUESTED, &reply) != B_OK)
98 			continue;
99 		bool result;
100 		if (reply.FindBool("result", &result) == B_OK && !result)
101 			quit = false;
102 	}
103 
104 	if (!quit)
105 		return false;
106 
107 	_StoreSettings();
108 
109 	return true;
110 }
111 
112 
113 void
114 IconEditorApp::MessageReceived(BMessage* message)
115 {
116 	switch (message->what) {
117 		case MSG_NEW:
118 			_NewWindow()->Show();
119 			break;
120 		case MSG_OPEN:
121 		{
122 			BMessage openMessage(B_REFS_RECEIVED);
123 			MainWindow* window;
124 			if (message->FindPointer("window", (void**)&window) == B_OK)
125 				openMessage.AddPointer("window", window);
126 			fOpenPanel->SetMessage(&openMessage);
127 			fOpenPanel->Show();
128 			break;
129 		}
130 		case MSG_APPEND:
131 		{
132 			MainWindow* window;
133 			if (message->FindPointer("window", (void**)&window) != B_OK)
134 				break;
135 			BMessage openMessage(B_REFS_RECEIVED);
136 			openMessage.AddBool("append", true);
137 			openMessage.AddPointer("window", window);
138 			fOpenPanel->SetMessage(&openMessage);
139 			fOpenPanel->Show();
140 			break;
141 		}
142 		case B_EDIT_ICON_DATA:
143 		{
144 			BMessenger messenger;
145 			if (message->FindMessenger("reply to", &messenger) < B_OK) {
146 				// required
147 				break;
148 			}
149 			const uint8* data;
150 			ssize_t size;
151 			if (message->FindData("icon data", B_VECTOR_ICON_TYPE,
152 				(const void**)&data, &size) < B_OK) {
153 				// optional (new icon will be created)
154 				data = NULL;
155 				size = 0;
156 			}
157 			MainWindow* window = _NewWindow();
158 			window->Open(messenger, data, size);
159 			window->Show();
160 			break;
161 		}
162 		case MSG_SAVE_AS:
163 		case MSG_EXPORT_AS:
164 		{
165 			BMessenger messenger;
166 			if (message->FindMessenger("target", &messenger) != B_OK)
167 				break;
168 
169 			fSavePanel->SetExportMode(message->what == MSG_EXPORT_AS);
170 //			fSavePanel->Refresh();
171 			const char* saveText;
172 			if (message->FindString("save text", &saveText) == B_OK)
173 				fSavePanel->SetSaveText(saveText);
174 			fSavePanel->SetTarget(messenger);
175 			fSavePanel->Show();
176 			break;
177 		}
178 
179 		case MSG_WINDOW_CLOSED:
180 		{
181 			fWindowCount--;
182 			if (fWindowCount == 0)
183 				PostMessage(B_QUIT_REQUESTED);
184 			BMessage settings;
185 			if (message->FindMessage("settings", &settings) == B_OK)
186 				fLastWindowSettings = settings;
187 			BRect frame;
188 			if (message->FindRect("window frame", &frame) == B_OK) {
189 				fLastWindowFrame = frame;
190 				fLastWindowFrame.OffsetBy(-kWindowOffset, -kWindowOffset);
191 			}
192 			break;
193 		}
194 
195 		default:
196 			BApplication::MessageReceived(message);
197 			break;
198 	}
199 }
200 
201 
202 void
203 IconEditorApp::ReadyToRun()
204 {
205 	// create main window
206 	if (fWindowCount == 0)
207 		_NewWindow()->Show();
208 
209 	_InstallDocumentMimeType();
210 }
211 
212 
213 void
214 IconEditorApp::RefsReceived(BMessage* message)
215 {
216 	bool append;
217 	if (message->FindBool("append", &append) != B_OK)
218 		append = false;
219 	MainWindow* window;
220 	if (message->FindPointer("window", (void**)&window) != B_OK)
221 		window = NULL;
222 	// When appending, we need to know a window.
223 	if (append && window == NULL)
224 		return;
225 	entry_ref ref;
226 	if (append) {
227 		if (!window->Lock())
228 			return;
229 		for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++)
230 			window->Open(ref, true);
231 		window->Unlock();
232 	} else {
233 		for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
234 			if (window != NULL && i == 0) {
235 				window->Lock();
236 				window->Open(ref, false);
237 				window->Unlock();
238 			} else {
239 				window = _NewWindow();
240 				window->Open(ref, false);
241 				window->Show();
242 			}
243 		}
244 	}
245 
246 	if (fOpenPanel != NULL && fSavePanel != NULL)
247 		_SyncPanels(fOpenPanel, fSavePanel);
248 }
249 
250 
251 void
252 IconEditorApp::ArgvReceived(int32 argc, char** argv)
253 {
254 	if (argc < 2)
255 		return;
256 
257 	entry_ref ref;
258 
259 	for (int32 i = 1; i < argc; i++) {
260 		if (get_ref_for_path(argv[i], &ref) == B_OK) {
261 		 	MainWindow* window = _NewWindow();
262 			window->Open(ref);
263 			window->Show();
264 		}
265 	}
266 }
267 
268 
269 // #pragma mark -
270 
271 
272 MainWindow*
273 IconEditorApp::_NewWindow()
274 {
275 	fLastWindowFrame.OffsetBy(kWindowOffset, kWindowOffset);
276 	MainWindow* window = new MainWindow(fLastWindowFrame, this,
277 		&fLastWindowSettings);
278 	fWindowCount++;
279 	return window;
280 }
281 
282 
283 void
284 IconEditorApp::_SyncPanels(BFilePanel* from, BFilePanel* to)
285 {
286 	if (from->Window()->Lock()) {
287 		// location
288 		if (to->Window()->Lock()) {
289 			BRect frame = from->Window()->Frame();
290 			to->Window()->MoveTo(frame.left, frame.top);
291 			to->Window()->ResizeTo(frame.Width(), frame.Height());
292 			to->Window()->Unlock();
293 		}
294 		// current folder
295 		entry_ref panelDir;
296 		from->GetPanelDirectory(&panelDir);
297 		to->SetPanelDirectory(&panelDir);
298 		from->Window()->Unlock();
299 	}
300 }
301 
302 
303 const char*
304 IconEditorApp::_LastFilePath(path_kind which)
305 {
306 	const char* path = NULL;
307 
308 	switch (which) {
309 		case LAST_PATH_OPEN:
310 			if (fLastOpenPath.Length() > 0)
311 				path = fLastOpenPath.String();
312 			else if (fLastSavePath.Length() > 0)
313 				path = fLastSavePath.String();
314 			else if (fLastExportPath.Length() > 0)
315 				path = fLastExportPath.String();
316 			break;
317 		case LAST_PATH_SAVE:
318 			if (fLastSavePath.Length() > 0)
319 				path = fLastSavePath.String();
320 			else if (fLastExportPath.Length() > 0)
321 				path = fLastExportPath.String();
322 			else if (fLastOpenPath.Length() > 0)
323 				path = fLastOpenPath.String();
324 			break;
325 		case LAST_PATH_EXPORT:
326 			if (fLastExportPath.Length() > 0)
327 				path = fLastExportPath.String();
328 			else if (fLastSavePath.Length() > 0)
329 				path = fLastSavePath.String();
330 			else if (fLastOpenPath.Length() > 0)
331 				path = fLastOpenPath.String();
332 			break;
333 	}
334 	if (path == NULL) {
335 
336 		BPath homePath;
337 		if (find_directory(B_USER_DIRECTORY, &homePath) == B_OK)
338 			path = homePath.Path();
339 		else
340 			path = "/boot/home";
341 	}
342 
343 	return path;
344 }
345 
346 
347 // #pragma mark -
348 
349 
350 void
351 IconEditorApp::_StoreSettings()
352 {
353 	BMessage settings('stns');
354 
355 	settings.AddRect("window frame", fLastWindowFrame);
356 	settings.AddMessage("window settings", &fLastWindowSettings);
357 	settings.AddInt32("export mode", fSavePanel->ExportMode());
358 
359 	save_settings(&settings, "Icon-O-Matic");
360 }
361 
362 
363 void
364 IconEditorApp::_RestoreSettings()
365 {
366 	BMessage settings('stns');
367 	load_settings(&settings, "Icon-O-Matic");
368 
369 	BRect frame;
370 	if (settings.FindRect("window frame", &frame) == B_OK) {
371 		fLastWindowFrame = frame;
372 		// Compensate offset for next window...
373 		fLastWindowFrame.OffsetBy(-kWindowOffset, -kWindowOffset);
374 	}
375 	BMessage lastSettings;
376 	if (settings.FindMessage("window settings", &lastSettings)
377 		== B_OK) {
378 		fLastWindowSettings = lastSettings;
379 	}
380 
381 	int32 mode;
382 	if (settings.FindInt32("export mode", &mode) >= B_OK)
383 		fSavePanel->SetExportMode(mode);
384 }
385 
386 
387 void
388 IconEditorApp::_InstallDocumentMimeType()
389 {
390 	// install mime type of documents
391 	BMimeType mime(kNativeIconMimeType);
392 	status_t ret = mime.InitCheck();
393 	if (ret < B_OK) {
394 		fprintf(stderr, "Could not init native document mime type (%s): %s.\n",
395 			kNativeIconMimeType, strerror(ret));
396 		return;
397 	}
398 
399 	if (mime.IsInstalled() && !(modifiers() & B_SHIFT_KEY)) {
400 		// mime is already installed, and the user is not
401 		// pressing the shift key to force a re-install
402 		return;
403 	}
404 
405 	ret = mime.Install();
406 	if (ret < B_OK) {
407 		fprintf(stderr, "Could not install native document mime type (%s): "
408 			"%s.\n", kNativeIconMimeType, strerror(ret));
409 		return;
410 	}
411 	// set preferred app
412 	ret = mime.SetPreferredApp(kAppSig);
413 	if (ret < B_OK)
414 		fprintf(stderr, "Could not set native document preferred app: %s\n",
415 			strerror(ret));
416 
417 	// set descriptions
418 	ret = mime.SetShortDescription("Haiku Icon");
419 	if (ret < B_OK)
420 		fprintf(stderr, "Could not set short description of mime type: %s\n",
421 			strerror(ret));
422 	ret = mime.SetLongDescription("Native Haiku vector icon");
423 	if (ret < B_OK)
424 		fprintf(stderr, "Could not set long description of mime type: %s\n",
425 			strerror(ret));
426 
427 	// set extensions
428 	BMessage message('extn');
429 	message.AddString("extensions", "icon");
430 	ret = mime.SetFileExtensions(&message);
431 	if (ret < B_OK)
432 		fprintf(stderr, "Could not set extensions of mime type: %s\n",
433 			strerror(ret));
434 
435 	// set sniffer rule
436 	const char* snifferRule = "0.9 ('IMSG')";
437 	ret = mime.SetSnifferRule(snifferRule);
438 	if (ret < B_OK) {
439 		BString parseError;
440 		BMimeType::CheckSnifferRule(snifferRule, &parseError);
441 		fprintf(stderr, "Could not set sniffer rule of mime type: %s\n",
442 			parseError.String());
443 	}
444 }
445 
446