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