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