xref: /haiku/src/apps/icon-o-matic/IconEditorApp.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
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 			fSavePanel->SetTarget(messenger);
183 			fSavePanel->Show();
184 			break;
185 		}
186 
187 		case MSG_WINDOW_CLOSED:
188 		{
189 			fWindowCount--;
190 			if (fWindowCount == 0)
191 				PostMessage(B_QUIT_REQUESTED);
192 			BMessage settings;
193 			if (message->FindMessage("settings", &settings) == B_OK)
194 				fLastWindowSettings = settings;
195 			BRect frame;
196 			if (message->FindRect("window frame", &frame) == B_OK) {
197 				fLastWindowFrame = frame;
198 				fLastWindowFrame.OffsetBy(-kWindowOffset, -kWindowOffset);
199 			}
200 			break;
201 		}
202 
203 		default:
204 			BApplication::MessageReceived(message);
205 			break;
206 	}
207 }
208 
209 
210 void
211 IconEditorApp::ReadyToRun()
212 {
213 	// create main window
214 	if (fWindowCount == 0)
215 		_NewWindow()->Show();
216 
217 	_InstallDocumentMimeType();
218 }
219 
220 
221 void
222 IconEditorApp::RefsReceived(BMessage* message)
223 {
224 	bool append;
225 	if (message->FindBool("append", &append) != B_OK)
226 		append = false;
227 	bool referenceImage;
228 	if (message->FindBool("reference image", &referenceImage) != B_OK)
229 		referenceImage = false;
230 	MainWindow* window;
231 	if (message->FindPointer("window", (void**)&window) != B_OK)
232 		window = NULL;
233 	// When appending, we need to know a window.
234 	if (append && window == NULL)
235 		return;
236 	entry_ref ref;
237 	if (append || referenceImage) {
238 		if (!window->Lock())
239 			return;
240 		for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
241 			if (append)
242 				window->Open(ref, true);
243 			if (referenceImage)
244 				window->AddReferenceImage(ref);
245 		}
246 		window->Unlock();
247 	} else {
248 		for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
249 			if (window != NULL && i == 0) {
250 				window->Lock();
251 				window->Open(ref, false);
252 				window->Unlock();
253 			} else {
254 				window = _NewWindow();
255 				window->Open(ref, false);
256 				window->Show();
257 			}
258 		}
259 	}
260 
261 	if (fOpenPanel != NULL && fSavePanel != NULL)
262 		_SyncPanels(fOpenPanel, fSavePanel);
263 }
264 
265 
266 void
267 IconEditorApp::ArgvReceived(int32 argc, char** argv)
268 {
269 	if (argc < 2)
270 		return;
271 
272 	entry_ref ref;
273 
274 	for (int32 i = 1; i < argc; i++) {
275 		if (get_ref_for_path(argv[i], &ref) == B_OK) {
276 		 	MainWindow* window = _NewWindow();
277 			window->Open(ref);
278 			window->Show();
279 		}
280 	}
281 }
282 
283 
284 // #pragma mark -
285 
286 
287 MainWindow*
288 IconEditorApp::_NewWindow()
289 {
290 	fLastWindowFrame.OffsetBy(kWindowOffset, kWindowOffset);
291 	MainWindow* window = new MainWindow(fLastWindowFrame, this,
292 		&fLastWindowSettings);
293 	fWindowCount++;
294 
295 	window->MoveOnScreen(B_MOVE_IF_PARTIALLY_OFFSCREEN);
296 
297 	return window;
298 }
299 
300 
301 void
302 IconEditorApp::_SyncPanels(BFilePanel* from, BFilePanel* to)
303 {
304 	if (from->Window()->Lock()) {
305 		// location
306 		if (to->Window()->Lock()) {
307 			BRect frame = from->Window()->Frame();
308 			to->Window()->MoveTo(frame.left, frame.top);
309 			to->Window()->ResizeTo(frame.Width(), frame.Height());
310 			to->Window()->Unlock();
311 		}
312 		// current folder
313 		entry_ref panelDir;
314 		from->GetPanelDirectory(&panelDir);
315 		to->SetPanelDirectory(&panelDir);
316 		from->Window()->Unlock();
317 	}
318 }
319 
320 
321 const char*
322 IconEditorApp::_LastFilePath(path_kind which)
323 {
324 	const char* path = NULL;
325 
326 	switch (which) {
327 		case LAST_PATH_OPEN:
328 			if (fLastOpenPath.Length() > 0)
329 				path = fLastOpenPath.String();
330 			else if (fLastSavePath.Length() > 0)
331 				path = fLastSavePath.String();
332 			else if (fLastExportPath.Length() > 0)
333 				path = fLastExportPath.String();
334 			break;
335 		case LAST_PATH_SAVE:
336 			if (fLastSavePath.Length() > 0)
337 				path = fLastSavePath.String();
338 			else if (fLastExportPath.Length() > 0)
339 				path = fLastExportPath.String();
340 			else if (fLastOpenPath.Length() > 0)
341 				path = fLastOpenPath.String();
342 			break;
343 		case LAST_PATH_EXPORT:
344 			if (fLastExportPath.Length() > 0)
345 				path = fLastExportPath.String();
346 			else if (fLastSavePath.Length() > 0)
347 				path = fLastSavePath.String();
348 			else if (fLastOpenPath.Length() > 0)
349 				path = fLastOpenPath.String();
350 			break;
351 	}
352 	if (path == NULL) {
353 
354 		BPath homePath;
355 		if (find_directory(B_USER_DIRECTORY, &homePath) == B_OK)
356 			path = homePath.Path();
357 		else
358 			path = "/boot/home";
359 	}
360 
361 	return path;
362 }
363 
364 
365 // #pragma mark -
366 
367 
368 void
369 IconEditorApp::_StoreSettings()
370 {
371 	BMessage settings('stns');
372 
373 	settings.AddRect("window frame", fLastWindowFrame);
374 	settings.AddMessage("window settings", &fLastWindowSettings);
375 	settings.AddInt32("export mode", fSavePanel->ExportMode());
376 
377 	save_settings(&settings, "Icon-O-Matic");
378 }
379 
380 
381 void
382 IconEditorApp::_RestoreSettings()
383 {
384 	BMessage settings('stns');
385 	load_settings(&settings, "Icon-O-Matic");
386 
387 	BRect frame;
388 	if (settings.FindRect("window frame", &frame) == B_OK) {
389 		fLastWindowFrame = frame;
390 		// Compensate offset for next window...
391 		fLastWindowFrame.OffsetBy(-kWindowOffset, -kWindowOffset);
392 	}
393 	BMessage lastSettings;
394 	if (settings.FindMessage("window settings", &lastSettings)
395 		== B_OK) {
396 		fLastWindowSettings = lastSettings;
397 	}
398 
399 	int32 mode;
400 	if (settings.FindInt32("export mode", &mode) >= B_OK)
401 		fSavePanel->SetExportMode(mode);
402 }
403 
404 
405 void
406 IconEditorApp::_InstallDocumentMimeType()
407 {
408 	// install mime type of documents
409 	BMimeType mime(kNativeIconMimeType);
410 	status_t ret = mime.InitCheck();
411 	if (ret < B_OK) {
412 		fprintf(stderr, "Could not init native document mime type (%s): %s.\n",
413 			kNativeIconMimeType, strerror(ret));
414 		return;
415 	}
416 
417 	if (mime.IsInstalled() && !(modifiers() & B_SHIFT_KEY)) {
418 		// mime is already installed, and the user is not
419 		// pressing the shift key to force a re-install
420 		return;
421 	}
422 
423 	ret = mime.Install();
424 	if (ret < B_OK) {
425 		fprintf(stderr, "Could not install native document mime type (%s): "
426 			"%s.\n", kNativeIconMimeType, strerror(ret));
427 		return;
428 	}
429 	// set preferred app
430 	ret = mime.SetPreferredApp(kAppSig);
431 	if (ret < B_OK)
432 		fprintf(stderr, "Could not set native document preferred app: %s\n",
433 			strerror(ret));
434 
435 	// set descriptions
436 	ret = mime.SetShortDescription("Haiku Icon");
437 	if (ret < B_OK)
438 		fprintf(stderr, "Could not set short description of mime type: %s\n",
439 			strerror(ret));
440 	ret = mime.SetLongDescription("Native Haiku vector icon");
441 	if (ret < B_OK)
442 		fprintf(stderr, "Could not set long description of mime type: %s\n",
443 			strerror(ret));
444 
445 	// set extensions
446 	BMessage message('extn');
447 	message.AddString("extensions", "icon");
448 	ret = mime.SetFileExtensions(&message);
449 	if (ret < B_OK)
450 		fprintf(stderr, "Could not set extensions of mime type: %s\n",
451 			strerror(ret));
452 
453 	// set sniffer rule
454 	const char* snifferRule = "0.9 ('IMSG')";
455 	ret = mime.SetSnifferRule(snifferRule);
456 	if (ret < B_OK) {
457 		BString parseError;
458 		BMimeType::CheckSnifferRule(snifferRule, &parseError);
459 		fprintf(stderr, "Could not set sniffer rule of mime type: %s\n",
460 			parseError.String());
461 	}
462 }
463 
464