xref: /haiku/src/apps/icon-o-matic/IconEditorApp.cpp (revision f23596149e0d173463f70629581aa10cc305d32e)
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 				const char* saveText = NULL;
141 				if (fDocument->Ref())
142 					saveText = fDocument->Ref()->name;
143 
144 				switch (message->what) {
145 					case MSG_EXPORT_AS:
146 					case MSG_EXPORT:
147 						exportMode = EXPORT_MODE_FLAT_ICON;
148 						if (fDocument->ExportRef())
149 							saveText = fDocument->ExportRef()->name;
150 						break;
151 					case MSG_EXPORT_BITMAP:
152 						exportMode = EXPORT_MODE_BITMAP;
153 						break;
154 					case MSG_EXPORT_BITMAP_SET:
155 						exportMode = EXPORT_MODE_BITMAP_SET;
156 						break;
157 					case MSG_EXPORT_SVG:
158 						exportMode = EXPORT_MODE_SVG;
159 						break;
160 					case MSG_EXPORT_ICON_ATTRIBUTE:
161 						exportMode = EXPORT_MODE_ICON_ATTR;
162 						break;
163 					case MSG_EXPORT_ICON_MIME_ATTRIBUTE:
164 						exportMode = EXPORT_MODE_ICON_MIME_ATTR;
165 						break;
166 					case MSG_EXPORT_ICON_RDEF:
167 						exportMode = EXPORT_MODE_ICON_RDEF;
168 						break;
169 					case MSG_SAVE_AS:
170 					case MSG_SAVE:
171 					default:
172 						exportMode = EXPORT_MODE_MESSAGE;
173 						break;
174 				}
175 				BMessage fpMessage(MSG_SAVE_AS);
176 				fpMessage.AddInt32("mode", exportMode);
177 
178 				fSavePanel->SetMessage(&fpMessage);
179 //				fSavePanel->Refresh();
180 				if (saveText)
181 					fSavePanel->SetSaveText(saveText);
182 				fSavePanel->Show();
183 			}
184 			break;
185 		}
186 
187 		default:
188 			BApplication::MessageReceived(message);
189 			break;
190 	}
191 }
192 
193 // ReadyToRun
194 void
195 IconEditorApp::ReadyToRun()
196 {
197 	// create file panels
198 	BMessenger messenger(this, this);
199 	fOpenPanel = new BFilePanel(B_OPEN_PANEL,
200 								&messenger,
201 								NULL,
202 								B_FILE_NODE,
203 								true,
204 								new BMessage(B_REFS_RECEIVED));
205 
206 	fSavePanel = new BFilePanel(B_SAVE_PANEL,
207 								&messenger,
208 								NULL,
209 								B_FILE_NODE
210 									| B_DIRECTORY_NODE
211 									| B_SYMLINK_NODE,
212 								false,
213 								new BMessage(MSG_SAVE));
214 
215 	// create main window
216 	fMainWindow = new MainWindow(this, fDocument);
217 	fMainWindow->Show();
218 }
219 
220 // RefsReceived
221 void
222 IconEditorApp::RefsReceived(BMessage* message)
223 {
224 	// TODO: multiple documents (iterate over refs)
225 	bool append;
226 	if (message->FindBool("append", &append) < B_OK)
227 		append = false;
228 	entry_ref ref;
229 	if (message->FindRef("refs", &ref) == B_OK)
230 		_Open(ref, append);
231 
232 	if (fOpenPanel && fSavePanel)
233 		_SyncPanels(fOpenPanel, fSavePanel);
234 }
235 
236 // ArgvReceived
237 void
238 IconEditorApp::ArgvReceived(int32 argc, char** argv)
239 {
240 	if (argc < 2)
241 		return;
242 
243 	// TODO: multiple documents (iterate over argv)
244 	entry_ref ref;
245 	if (get_ref_for_path(argv[1], &ref) == B_OK)
246 		_Open(ref);
247 }
248 
249 // #pragma mark -
250 
251 void
252 IconEditorApp::_MakeIconEmpty()
253 {
254 	bool mainWindowLocked = fMainWindow && fMainWindow->Lock();
255 
256 	AutoWriteLocker locker(fDocument);
257 
258 	if (fMainWindow)
259 		fMainWindow->MakeEmpty();
260 
261 	fDocument->MakeEmpty();
262 
263 	locker.Unlock();
264 
265 	if (mainWindowLocked)
266 		fMainWindow->Unlock();
267 }
268 
269 // _Open
270 void
271 IconEditorApp::_Open(const entry_ref& ref, bool append)
272 {
273 	BFile file(&ref, B_READ_ONLY);
274 	if (file.InitCheck() < B_OK)
275 		return;
276 
277 	Icon* icon;
278 	if (append)
279 		icon = new (nothrow) Icon(*fDocument->Icon());
280 	else
281 		icon = new (nothrow) Icon();
282 
283 	if (!icon)
284 		return;
285 
286 	enum {
287 		REF_NONE = 0,
288 		REF_MESSAGE,
289 		REF_FLAT
290 	};
291 	uint32 refMode = REF_NONE;
292 
293 	// try different file types
294 	FlatIconImporter flatImporter;
295 	status_t ret = flatImporter.Import(icon, &file);
296 	if (ret >= B_OK) {
297 		refMode = REF_FLAT;
298 	} else {
299 		file.Seek(0, SEEK_SET);
300 		MessageImporter msgImporter;
301 		ret = msgImporter.Import(icon, &file);
302 		if (ret >= B_OK) {
303 			refMode = REF_MESSAGE;
304 		} else {
305 			file.Seek(0, SEEK_SET);
306 			SVGImporter svgImporter;
307 			ret = svgImporter.Import(icon, &ref);
308 		}
309 	}
310 
311 	if (ret < B_OK) {
312 		// inform user of failure at this point
313 		BString helper("Opening the document failed!");
314 		helper << "\n\n" << "Error: " << strerror(ret);
315 		BAlert* alert = new BAlert("bad news", helper.String(),
316 								   "Bummer", NULL, NULL);
317 		// launch alert asynchronously
318 		alert->Go(NULL);
319 
320 		delete icon;
321 		return;
322 	}
323 
324 	// keep the mainwindow locked while switching icons
325 	bool mainWindowLocked = fMainWindow && fMainWindow->Lock();
326 
327 	AutoWriteLocker locker(fDocument);
328 
329 	if (mainWindowLocked)
330 		fMainWindow->SetIcon(NULL);
331 
332 	fDocument->MakeEmpty();
333 
334 	fDocument->SetIcon(icon);
335 
336 	if (!append) {
337 		switch (refMode) {
338 			case REF_MESSAGE:
339 				fDocument->SetRef(ref);
340 				break;
341 			case REF_FLAT:
342 				fDocument->SetExportRef(ref);
343 				break;
344 		}
345 	}
346 
347 	locker.Unlock();
348 
349 	if (mainWindowLocked) {
350 		fMainWindow->Unlock();
351 		// cause the mainwindow to adopt icon in
352 		// it's own thread
353 		fMainWindow->PostMessage(MSG_SET_ICON);
354 	}
355 }
356 
357 // _Save
358 void
359 IconEditorApp::_Save(const entry_ref& ref, uint32 exportMode)
360 {
361 	Exporter* exporter;
362 	switch (exportMode) {
363 		case EXPORT_MODE_FLAT_ICON:
364 			exporter = new FlatIconExporter();
365 			fDocument->SetExportRef(ref);
366 			break;
367 
368 		case EXPORT_MODE_ICON_ATTR:
369 		case EXPORT_MODE_ICON_MIME_ATTR: {
370 			BNode node(&ref);
371 			FlatIconExporter iconExporter;
372 			const char* attrName
373 				= exportMode == EXPORT_MODE_ICON_ATTR ?
374 					kVectorAttrNodeName : kVectorAttrMimeName;
375 			iconExporter.Export(fDocument->Icon(), &node, attrName);
376 			return;
377 		}
378 
379 		case EXPORT_MODE_ICON_RDEF:
380 			exporter = new RDefExporter();
381 			break;
382 
383 		case EXPORT_MODE_BITMAP:
384 			exporter = new BitmapExporter(64);
385 			break;
386 
387 		case EXPORT_MODE_BITMAP_SET: {
388 			entry_ref smallRef(ref);
389 			// 64x64
390 			char name[B_OS_NAME_LENGTH];
391 			sprintf(name, "%s_64.png", ref.name);
392 			smallRef.set_name(name);
393 			exporter = new BitmapExporter(64);
394 			exporter->SetSelfDestroy(true);
395 			exporter->Export(fDocument, smallRef);
396 			// 16x16
397 			sprintf(name, "%s_16.png", ref.name);
398 			smallRef.set_name(name);
399 			Exporter* smallExporter = new BitmapExporter(16);
400 			smallExporter->SetSelfDestroy(true);
401 			smallExporter->Export(fDocument, smallRef);
402 			// 32x32
403 			sprintf(name, "%s_32.png", ref.name);
404 			smallRef.set_name(name);
405 			smallExporter = new BitmapExporter(32);
406 			smallExporter->SetSelfDestroy(true);
407 			smallExporter->Export(fDocument, smallRef);
408 			return;
409 		}
410 
411 		case EXPORT_MODE_SVG:
412 			exporter = new SVGExporter();
413 			break;
414 
415 		case EXPORT_MODE_MESSAGE:
416 		default:
417 			exporter = new MessageExporter();
418 			fDocument->SetRef(ref);
419 			break;
420 	}
421 
422 	exporter->SetSelfDestroy(true);
423 		// we don't wait for the export thread to finish here
424 	exporter->Export(fDocument, ref);
425 }
426 
427 // _SyncPanels
428 void
429 IconEditorApp::_SyncPanels(BFilePanel* from, BFilePanel* to)
430 {
431 	if (from->Window()->Lock()) {
432 		// location
433 		if (to->Window()->Lock()) {
434 			BRect frame = from->Window()->Frame();
435 			to->Window()->MoveTo(frame.left, frame.top);
436 			to->Window()->ResizeTo(frame.Width(), frame.Height());
437 			to->Window()->Unlock();
438 		}
439 		// current folder
440 		entry_ref panelDir;
441 		from->GetPanelDirectory(&panelDir);
442 		to->SetPanelDirectory(&panelDir);
443 		from->Window()->Unlock();
444 	}
445 }
446