xref: /haiku/src/apps/icon-o-matic/IconEditorApp.cpp (revision 302f62604763c95777d6d04cca456e876f471c4f)
1 /*
2  * Copyright 2006, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  */
8 
9 #include "IconEditorApp.h"
10 
11 #include <Alert.h>
12 #include <Directory.h>
13 #include <Entry.h>
14 #include <File.h>
15 #include <FilePanel.h>
16 
17 #include <new>
18 #include <stdio.h>
19 #include <string.h>
20 
21 #include "AutoLocker.h"
22 #include "BitmapExporter.h"
23 #include "CommandStack.h"
24 #include "Document.h"
25 #include "FlatIconExporter.h"
26 #include "FlatIconFormat.h"
27 #include "FlatIconImporter.h"
28 #include "Icon.h"
29 #include "MainWindow.h"
30 #include "MessageExporter.h"
31 #include "MessageImporter.h"
32 #include "PathContainer.h"
33 #include "RDefExporter.h"
34 #include "ShapeContainer.h"
35 #include "SVGExporter.h"
36 #include "SVGImporter.h"
37 
38 using std::nothrow;
39 
40 // constructor
41 IconEditorApp::IconEditorApp()
42 	: BApplication("application/x-vnd.haiku-icon_o_matic"),
43 	  fMainWindow(NULL),
44 	  fDocument(new Document("test")),
45 
46 	  fOpenPanel(NULL),
47 	  fSavePanel(NULL)
48 {
49 }
50 
51 // destructor
52 IconEditorApp::~IconEditorApp()
53 {
54 	// NOTE: it is important that the GUI has been deleted
55 	// at this point, so that all the listener/observer
56 	// stuff is properly detached
57 	delete fDocument;
58 
59 	delete fOpenPanel;
60 	delete fSavePanel;
61 }
62 
63 // #pragma mark -
64 
65 // QuitRequested
66 bool
67 IconEditorApp::QuitRequested()
68 {
69 	// TODO: ask main window if quitting is ok
70 	fMainWindow->Lock();
71 	fMainWindow->Quit();
72 	fMainWindow = NULL;
73 
74 	return true;
75 }
76 
77 // MessageReceived
78 void
79 IconEditorApp::MessageReceived(BMessage* message)
80 {
81 	switch (message->what) {
82 		case MSG_NEW:
83 			_MakeIconEmpty();
84 			break;
85 		case MSG_OPEN: {
86 //			fOpenPanel->Refresh();
87 			BMessage openMessage(B_REFS_RECEIVED);
88 			fOpenPanel->SetMessage(&openMessage);
89 			fOpenPanel->Show();
90 			break;
91 		}
92 		case MSG_APPEND: {
93 //			fOpenPanel->Refresh();
94 			BMessage openMessage(B_REFS_RECEIVED);
95 			openMessage.AddBool("append", true);
96 			fOpenPanel->SetMessage(&openMessage);
97 			fOpenPanel->Show();
98 			break;
99 		}
100 		case MSG_SAVE:
101 		case MSG_EXPORT: {
102 			const entry_ref* ref;
103 			if (message->what == MSG_SAVE)
104 				ref = fDocument->Ref();
105 			else
106 				ref = fDocument->ExportRef();
107 			if (ref) {
108 				_Save(*ref, message->what == MSG_EXPORT ?
109 								EXPORT_MODE_FLAT_ICON :
110 								EXPORT_MODE_MESSAGE);
111 				break;
112 			} // else fall through
113 		}
114 		case MSG_SAVE_AS:
115 		case MSG_EXPORT_AS:
116 		case MSG_EXPORT_BITMAP:
117 		case MSG_EXPORT_BITMAP_SET:
118 		case MSG_EXPORT_SVG:
119 		case MSG_EXPORT_ICON_ATTRIBUTE:
120 		case MSG_EXPORT_ICON_MIME_ATTRIBUTE:
121 		case MSG_EXPORT_ICON_RDEF: {
122 			uint32 exportMode;
123 			if (message->FindInt32("mode", (int32*)&exportMode) < B_OK)
124 				exportMode = EXPORT_MODE_MESSAGE;
125 			entry_ref ref;
126 			const char* name;
127 			if (message->FindRef("directory", &ref) == B_OK
128 				&& message->FindString("name", &name) == B_OK) {
129 				// this message comes from the file panel
130 				BDirectory dir(&ref);
131 				BEntry entry;
132 				if (dir.InitCheck() >= B_OK
133 					&& entry.SetTo(&dir, name, true) >= B_OK
134 					&& entry.GetRef(&ref) >= B_OK) {
135 
136 					_Save(ref, exportMode);
137 				}
138 				_SyncPanels(fSavePanel, fOpenPanel);
139 			} else {
140 				switch (message->what) {
141 					case MSG_EXPORT_AS:
142 					case MSG_EXPORT:
143 						exportMode = EXPORT_MODE_FLAT_ICON;
144 						break;
145 					case MSG_EXPORT_BITMAP:
146 						exportMode = EXPORT_MODE_BITMAP;
147 						break;
148 					case MSG_EXPORT_BITMAP_SET:
149 						exportMode = EXPORT_MODE_BITMAP_SET;
150 						break;
151 					case MSG_EXPORT_SVG:
152 						exportMode = EXPORT_MODE_SVG;
153 						break;
154 					case MSG_EXPORT_ICON_ATTRIBUTE:
155 						exportMode = EXPORT_MODE_ICON_ATTR;
156 						break;
157 					case MSG_EXPORT_ICON_MIME_ATTRIBUTE:
158 						exportMode = EXPORT_MODE_ICON_MIME_ATTR;
159 						break;
160 					case MSG_EXPORT_ICON_RDEF:
161 						exportMode = EXPORT_MODE_ICON_RDEF;
162 						break;
163 					case MSG_SAVE_AS:
164 					case MSG_SAVE:
165 					default:
166 						exportMode = EXPORT_MODE_MESSAGE;
167 						break;
168 				}
169 				BMessage fpMessage(MSG_SAVE_AS);
170 				fpMessage.AddInt32("mode", exportMode);
171 
172 				fSavePanel->SetMessage(&fpMessage);
173 //				fSavePanel->Refresh();
174 				fSavePanel->Show();
175 			}
176 			break;
177 		}
178 
179 		default:
180 			BApplication::MessageReceived(message);
181 			break;
182 	}
183 }
184 
185 // ReadyToRun
186 void
187 IconEditorApp::ReadyToRun()
188 {
189 	// create file panels
190 	BMessenger messenger(this, this);
191 	fOpenPanel = new BFilePanel(B_OPEN_PANEL,
192 								&messenger,
193 								NULL,
194 								B_FILE_NODE,
195 								true,
196 								new BMessage(B_REFS_RECEIVED));
197 
198 	fSavePanel = new BFilePanel(B_SAVE_PANEL,
199 								&messenger,
200 								NULL,
201 								B_FILE_NODE
202 									| B_DIRECTORY_NODE
203 									| B_SYMLINK_NODE,
204 								false,
205 								new BMessage(MSG_SAVE));
206 
207 	// create main window
208 	fMainWindow = new MainWindow(this, fDocument);
209 	fMainWindow->Show();
210 }
211 
212 // RefsReceived
213 void
214 IconEditorApp::RefsReceived(BMessage* message)
215 {
216 	// TODO: multiple documents (iterate over refs)
217 	bool append;
218 	if (message->FindBool("append", &append) < B_OK)
219 		append = false;
220 	entry_ref ref;
221 	if (message->FindRef("refs", &ref) == B_OK)
222 		_Open(ref, append);
223 
224 	if (fOpenPanel && fSavePanel)
225 		_SyncPanels(fOpenPanel, fSavePanel);
226 }
227 
228 // ArgvReceived
229 void
230 IconEditorApp::ArgvReceived(int32 argc, char** argv)
231 {
232 	if (argc < 2)
233 		return;
234 
235 	// TODO: multiple documents (iterate over argv)
236 	entry_ref ref;
237 	if (get_ref_for_path(argv[1], &ref) == B_OK)
238 		_Open(ref);
239 }
240 
241 // #pragma mark -
242 
243 void
244 IconEditorApp::_MakeIconEmpty()
245 {
246 	bool mainWindowLocked = fMainWindow && fMainWindow->Lock();
247 
248 	AutoWriteLocker locker(fDocument);
249 
250 	if (fMainWindow)
251 		fMainWindow->MakeEmpty();
252 
253 	fDocument->MakeEmpty();
254 
255 	locker.Unlock();
256 
257 	if (mainWindowLocked)
258 		fMainWindow->Unlock();
259 }
260 
261 // _Open
262 void
263 IconEditorApp::_Open(const entry_ref& ref, bool append)
264 {
265 	BFile file(&ref, B_READ_ONLY);
266 	if (file.InitCheck() < B_OK)
267 		return;
268 
269 	Icon* icon;
270 	if (append)
271 		icon = new (nothrow) Icon(*fDocument->Icon());
272 	else
273 		icon = new (nothrow) Icon();
274 
275 	if (!icon)
276 		return;
277 
278 	enum {
279 		REF_NONE = 0,
280 		REF_MESSAGE,
281 		REF_FLAT
282 	};
283 	uint32 refMode = REF_NONE;
284 
285 	// try different file types
286 	FlatIconImporter flatImporter;
287 	status_t ret = flatImporter.Import(icon, &file);
288 	if (ret >= B_OK) {
289 		refMode = REF_FLAT;
290 	} else {
291 		file.Seek(0, SEEK_SET);
292 		MessageImporter msgImporter;
293 		ret = msgImporter.Import(icon, &file);
294 		if (ret >= B_OK) {
295 			refMode = REF_MESSAGE;
296 		} else {
297 			file.Seek(0, SEEK_SET);
298 			SVGImporter svgImporter;
299 			ret = svgImporter.Import(icon, &ref);
300 		}
301 	}
302 
303 	if (ret < B_OK) {
304 		// inform user of failure at this point
305 		BString helper("Opening the document failed!");
306 		helper << "\n\n" << "Error: " << strerror(ret);
307 		BAlert* alert = new BAlert("bad news", helper.String(),
308 								   "Bummer", NULL, NULL);
309 		// launch alert asynchronously
310 		alert->Go(NULL);
311 
312 		delete icon;
313 		return;
314 	}
315 
316 	// keep the mainwindow locked while switching icons
317 	bool mainWindowLocked = fMainWindow && fMainWindow->Lock();
318 
319 	AutoWriteLocker locker(fDocument);
320 
321 	if (mainWindowLocked)
322 		fMainWindow->SetIcon(NULL);
323 
324 	fDocument->MakeEmpty();
325 
326 	fDocument->SetIcon(icon);
327 
328 	switch (refMode) {
329 		case REF_MESSAGE:
330 			fDocument->SetRef(ref);
331 			break;
332 		case REF_FLAT:
333 			fDocument->SetExportRef(ref);
334 			break;
335 	}
336 
337 	locker.Unlock();
338 
339 	if (mainWindowLocked) {
340 		fMainWindow->Unlock();
341 		// cause the mainwindow to adopt icon in
342 		// it's own thread
343 		fMainWindow->PostMessage(MSG_SET_ICON);
344 	}
345 }
346 
347 // _Save
348 void
349 IconEditorApp::_Save(const entry_ref& ref, uint32 exportMode)
350 {
351 	Exporter* exporter;
352 	switch (exportMode) {
353 		case EXPORT_MODE_FLAT_ICON:
354 			exporter = new FlatIconExporter();
355 			fDocument->SetExportRef(ref);
356 			break;
357 
358 		case EXPORT_MODE_ICON_ATTR:
359 		case EXPORT_MODE_ICON_MIME_ATTR: {
360 			BNode node(&ref);
361 			FlatIconExporter iconExporter;
362 			const char* attrName
363 				= exportMode == EXPORT_MODE_ICON_ATTR ?
364 					kVectorAttrNodeName : kVectorAttrMimeName;
365 			iconExporter.Export(fDocument->Icon(), &node, attrName);
366 			return;
367 		}
368 
369 		case EXPORT_MODE_ICON_RDEF:
370 			exporter = new RDefExporter();
371 			break;
372 
373 		case EXPORT_MODE_BITMAP:
374 			exporter = new BitmapExporter(64);
375 			break;
376 
377 		case EXPORT_MODE_BITMAP_SET: {
378 			entry_ref smallRef(ref);
379 			// 64x64
380 			char name[B_OS_NAME_LENGTH];
381 			sprintf(name, "%s_64.png", ref.name);
382 			smallRef.set_name(name);
383 			exporter = new BitmapExporter(64);
384 			exporter->SetSelfDestroy(true);
385 			exporter->Export(fDocument, smallRef);
386 			// 16x16
387 			sprintf(name, "%s_16.png", ref.name);
388 			smallRef.set_name(name);
389 			Exporter* smallExporter = new BitmapExporter(16);
390 			smallExporter->SetSelfDestroy(true);
391 			smallExporter->Export(fDocument, smallRef);
392 			// 32x32
393 			sprintf(name, "%s_32.png", ref.name);
394 			smallRef.set_name(name);
395 			smallExporter = new BitmapExporter(32);
396 			smallExporter->SetSelfDestroy(true);
397 			smallExporter->Export(fDocument, smallRef);
398 			return;
399 		}
400 
401 		case EXPORT_MODE_SVG:
402 			exporter = new SVGExporter();
403 			break;
404 
405 		case EXPORT_MODE_MESSAGE:
406 		default:
407 			exporter = new MessageExporter();
408 			fDocument->SetRef(ref);
409 			break;
410 	}
411 
412 	exporter->SetSelfDestroy(true);
413 		// we don't wait for the export thread to finish here
414 	exporter->Export(fDocument, ref);
415 }
416 
417 // _SyncPanels
418 void
419 IconEditorApp::_SyncPanels(BFilePanel* from, BFilePanel* to)
420 {
421 	if (from->Window()->Lock()) {
422 		// location
423 		if (to->Window()->Lock()) {
424 			BRect frame = from->Window()->Frame();
425 			to->Window()->MoveTo(frame.left, frame.top);
426 			to->Window()->ResizeTo(frame.Width(), frame.Height());
427 			to->Window()->Unlock();
428 		}
429 		// current folder
430 		entry_ref panelDir;
431 		from->GetPanelDirectory(&panelDir);
432 		to->SetPanelDirectory(&panelDir);
433 		from->Window()->Unlock();
434 	}
435 }
436