xref: /haiku/src/apps/icon-o-matic/MainWindow.cpp (revision 1c09002cbee8e797a0f8bbfc5678dfadd39ee1a7)
1 /*
2  * Copyright 2006-2011, Stephan Aßmus <superstippi@gmx.de>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 #include "MainWindow.h"
7 
8 #include <new>
9 #include <stdio.h>
10 
11 #include <Alert.h>
12 #include <Catalog.h>
13 #include <Clipboard.h>
14 #include <GridLayout.h>
15 #include <GroupLayout.h>
16 #include <GroupView.h>
17 #include <Directory.h>
18 #include <Entry.h>
19 #include <File.h>
20 #include <fs_attr.h>
21 #include <Locale.h>
22 #include <Menu.h>
23 #include <MenuBar.h>
24 #include <MenuItem.h>
25 #include <Message.h>
26 #include <Screen.h>
27 #include <ScrollView.h>
28 
29 #include "support_ui.h"
30 
31 #include "AddPathsCommand.h"
32 #include "AddShapesCommand.h"
33 #include "AddStylesCommand.h"
34 #include "AttributeSaver.h"
35 #include "BitmapExporter.h"
36 #include "BitmapSetSaver.h"
37 #include "CanvasView.h"
38 #include "CommandStack.h"
39 #include "CompoundCommand.h"
40 #include "CurrentColor.h"
41 #include "Document.h"
42 #include "FlatIconExporter.h"
43 #include "FlatIconFormat.h"
44 #include "FlatIconImporter.h"
45 #include "IconObjectListView.h"
46 #include "IconEditorApp.h"
47 #include "IconView.h"
48 #include "MessageExporter.h"
49 #include "MessageImporter.h"
50 #include "MessengerSaver.h"
51 #include "NativeSaver.h"
52 #include "PathListView.h"
53 #include "RDefExporter.h"
54 #include "ScrollView.h"
55 #include "SimpleFileSaver.h"
56 #include "ShapeListView.h"
57 #include "SourceExporter.h"
58 #include "StyleListView.h"
59 #include "StyleView.h"
60 #include "SVGExporter.h"
61 #include "SVGImporter.h"
62 #include "SwatchGroup.h"
63 #include "TransformerListView.h"
64 #include "TransformGradientBox.h"
65 #include "TransformShapesBox.h"
66 #include "Util.h"
67 
68 // TODO: just for testing
69 #include "AffineTransformer.h"
70 #include "GradientTransformable.h"
71 #include "Icon.h"
72 #include "MultipleManipulatorState.h"
73 #include "PathManipulator.h"
74 #include "Shape.h"
75 #include "ShapeContainer.h"
76 #include "ShapeListView.h"
77 #include "StrokeTransformer.h"
78 #include "Style.h"
79 #include "StyleContainer.h"
80 #include "VectorPath.h"
81 
82 #include "StyledTextImporter.h"
83 
84 
85 #undef B_TRANSLATE_CONTEXT
86 #define B_TRANSLATE_CONTEXT "Icon-O-Matic-Main"
87 
88 
89 using std::nothrow;
90 
91 enum {
92 	MSG_UNDO						= 'undo',
93 	MSG_REDO						= 'redo',
94 	MSG_UNDO_STACK_CHANGED			= 'usch',
95 
96 	MSG_PATH_SELECTED				= 'vpsl',
97 	MSG_STYLE_SELECTED				= 'stsl',
98 	MSG_SHAPE_SELECTED				= 'spsl',
99 
100 	MSG_SHAPE_RESET_TRANSFORMATION	= 'rtsh',
101 	MSG_STYLE_RESET_TRANSFORMATION	= 'rtst',
102 
103 	MSG_MOUSE_FILTER_MODE			= 'mfmd',
104 
105 	MSG_RENAME_OBJECT				= 'rnam',
106 };
107 
108 
109 MainWindow::MainWindow(BRect frame, IconEditorApp* app,
110 		const BMessage* settings)
111 	:
112 	BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Icon-O-Matic"),
113 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
114 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
115 	fApp(app),
116 	fDocument(new Document(B_TRANSLATE("Untitled"))),
117 	fCurrentColor(new CurrentColor()),
118 	fIcon(NULL),
119 	fMessageAfterSave(NULL)
120 {
121 	_Init();
122 
123 	RestoreSettings(settings);
124 }
125 
126 
127 MainWindow::~MainWindow()
128 {
129 	delete fState;
130 
131 	SetIcon(NULL);
132 
133 	// Make sure there are no listeners attached to the document anymore.
134 	while (BView* child = ChildAt(0L)) {
135 		child->RemoveSelf();
136 		delete child;
137 	}
138 
139 	fDocument->CommandStack()->RemoveObserver(this);
140 
141 	// NOTE: it is important that the GUI has been deleted
142 	// at this point, so that all the listener/observer
143 	// stuff is properly detached
144 	delete fDocument;
145 
146 	delete fMessageAfterSave;
147 }
148 
149 
150 // #pragma mark -
151 
152 
153 void
154 MainWindow::MessageReceived(BMessage* message)
155 {
156 	bool discard = false;
157 
158 	// Figure out if we need the write lock on the Document. For most
159 	// messages we do, but exporting takes place in another thread and
160 	// locking is taken care of there.
161 	bool requiresWriteLock = true;
162 	switch (message->what) {
163 		case MSG_SAVE:
164 		case MSG_EXPORT:
165 		case MSG_SAVE_AS:
166 		case MSG_EXPORT_AS:
167 			requiresWriteLock = false;
168 			break;
169 		default:
170 			break;
171 	}
172 	if (requiresWriteLock && !fDocument->WriteLock()) {
173 		BWindow::MessageReceived(message);
174 		return;
175 	}
176 
177 	if (message->WasDropped()) {
178 		const rgb_color* color;
179 		int32 length;
180 		// create styles from dropped colors
181 		for (int32 i = 0; message->FindData("RGBColor", B_RGB_COLOR_TYPE, i,
182 			(const void**)&color, &length) == B_OK; i++) {
183 			if (length != sizeof(rgb_color))
184 				continue;
185 			char name[30];
186 			sprintf(name,
187 				B_TRANSLATE_WITH_CONTEXT("Color (#%02x%02x%02x)",
188 					"Style name after dropping a color"),
189 				color->red, color->green, color->blue);
190 			Style* style = new (nothrow) Style(*color);
191 			style->SetName(name);
192 			Style* styles[1] = { style };
193 			AddStylesCommand* styleCommand = new (nothrow) AddStylesCommand(
194 				fDocument->Icon()->Styles(), styles, 1,
195 				fDocument->Icon()->Styles()->CountStyles());
196 			fDocument->CommandStack()->Perform(styleCommand);
197 			// don't handle anything else,
198 			// or we might paste the clipboard on B_PASTE
199 			discard = true;
200 		}
201 	}
202 
203 	switch (message->what) {
204 
205 		case B_REFS_RECEIVED:
206 		case B_SIMPLE_DATA:
207 			message->what = B_REFS_RECEIVED;
208 			if (modifiers() & B_SHIFT_KEY) {
209 				message->AddBool("append", true);
210 				message->AddPointer("window", this);
211 			}
212 			be_app->PostMessage(message);
213 			break;
214 
215 		case B_PASTE:
216 		case B_MIME_DATA:
217 		{
218 			BMessage* clip = message;
219 			status_t err;
220 
221 			if (discard)
222 				break;
223 
224 			if (message->what == B_PASTE) {
225 				if (!be_clipboard->Lock())
226 					break;
227 				clip = be_clipboard->Data();
228 			}
229 
230 			if (!clip || !clip->HasData("text/plain", B_MIME_TYPE)) {
231 				if (message->what == B_PASTE)
232 					be_clipboard->Unlock();
233 				break;
234 			}
235 
236 			Icon* icon = new (std::nothrow) Icon(*fDocument->Icon());
237 			if (icon != NULL) {
238 				StyledTextImporter importer;
239 				err = importer.Import(icon, clip);
240 				if (err >= B_OK) {
241 					AutoWriteLocker locker(fDocument);
242 
243 					SetIcon(NULL);
244 
245 					// incorporate the loaded icon into the document
246 					// (either replace it or append to it)
247 					fDocument->MakeEmpty(false);
248 						// if append, the document savers are preserved
249 					fDocument->SetIcon(icon);
250 					SetIcon(icon);
251 				}
252 			}
253 
254 			if (message->what == B_PASTE)
255 				be_clipboard->Unlock();
256 			break;
257 		}
258 
259 		case MSG_SAVE:
260 		case MSG_EXPORT:
261 		{
262 			DocumentSaver* saver;
263 			if (message->what == MSG_SAVE)
264 				saver = fDocument->NativeSaver();
265 			else
266 				saver = fDocument->ExportSaver();
267 			if (saver != NULL) {
268 				saver->Save(fDocument);
269 				_PickUpActionBeforeSave();
270 				break;
271 			} // else fall through
272 		}
273 		case MSG_SAVE_AS:
274 		case MSG_EXPORT_AS:
275 		{
276 			int32 exportMode;
277 			if (message->FindInt32("export mode", &exportMode) < B_OK)
278 				exportMode = EXPORT_MODE_MESSAGE;
279 			entry_ref ref;
280 			const char* name;
281 			if (message->FindRef("directory", &ref) == B_OK
282 				&& message->FindString("name", &name) == B_OK) {
283 				// this message comes from the file panel
284 				BDirectory dir(&ref);
285 				BEntry entry;
286 				if (dir.InitCheck() >= B_OK
287 					&& entry.SetTo(&dir, name, true) >= B_OK
288 					&& entry.GetRef(&ref) >= B_OK) {
289 
290 					// create the document saver and remember it for later
291 					DocumentSaver* saver = _CreateSaver(ref, exportMode);
292 					if (saver != NULL) {
293 						if (fDocument->WriteLock()) {
294 							if (exportMode == EXPORT_MODE_MESSAGE)
295 								fDocument->SetNativeSaver(saver);
296 							else
297 								fDocument->SetExportSaver(saver);
298 							_UpdateWindowTitle();
299 							fDocument->WriteUnlock();
300 						}
301 						saver->Save(fDocument);
302 						_PickUpActionBeforeSave();
303 					}
304 				}
305 // TODO: ...
306 //				_SyncPanels(fSavePanel, fOpenPanel);
307 			} else {
308 				// configure the file panel
309 				uint32 requestRefWhat = MSG_SAVE_AS;
310 				bool isExportMode = message->what == MSG_EXPORT_AS
311 					|| message->what == MSG_EXPORT;
312 				if (isExportMode)
313 					requestRefWhat = MSG_EXPORT_AS;
314 				const char* saveText = _FileName(isExportMode);
315 
316 				BMessage requestRef(requestRefWhat);
317 				if (saveText != NULL)
318 					requestRef.AddString("save text", saveText);
319 				requestRef.AddMessenger("target", BMessenger(this, this));
320 				be_app->PostMessage(&requestRef);
321 			}
322 			break;
323 		}
324 
325 		case MSG_UNDO:
326 			fDocument->CommandStack()->Undo();
327 			break;
328 		case MSG_REDO:
329 			fDocument->CommandStack()->Redo();
330 			break;
331 		case MSG_UNDO_STACK_CHANGED:
332 		{
333 			// relable Undo item and update enabled status
334 			BString label(B_TRANSLATE("Undo"));
335 			fUndoMI->SetEnabled(fDocument->CommandStack()->GetUndoName(label));
336 			if (fUndoMI->IsEnabled())
337 				fUndoMI->SetLabel(label.String());
338 			else {
339 				fUndoMI->SetLabel(B_TRANSLATE_WITH_CONTEXT("<nothing to undo>",
340 					"Icon-O-Matic-Menu-Edit"));
341 			}
342 
343 			// relable Redo item and update enabled status
344 			label.SetTo(B_TRANSLATE("Redo"));
345 			fRedoMI->SetEnabled(fDocument->CommandStack()->GetRedoName(label));
346 			if (fRedoMI->IsEnabled())
347 				fRedoMI->SetLabel(label.String());
348 			else {
349 				fRedoMI->SetLabel(B_TRANSLATE_WITH_CONTEXT("<nothing to redo>",
350 					"Icon-O-Matic-Menu-Edit"));
351 			}
352 			break;
353 		}
354 
355 		case MSG_MOUSE_FILTER_MODE:
356 		{
357 			uint32 mode;
358 			if (message->FindInt32("mode", (int32*)&mode) == B_OK)
359 				fCanvasView->SetMouseFilterMode(mode);
360 			break;
361 		}
362 
363 		case MSG_ADD_SHAPE: {
364 			AddStylesCommand* styleCommand = NULL;
365 			Style* style = NULL;
366 			if (message->HasBool("style")) {
367 				new_style(fCurrentColor->Color(),
368 					fDocument->Icon()->Styles(), &style, &styleCommand);
369 			}
370 
371 			AddPathsCommand* pathCommand = NULL;
372 			VectorPath* path = NULL;
373 			if (message->HasBool("path")) {
374 				new_path(fDocument->Icon()->Paths(), &path, &pathCommand);
375 			}
376 
377 			if (!style) {
378 				// use current or first style
379 				int32 currentStyle = fStyleListView->CurrentSelection(0);
380 				style = fDocument->Icon()->Styles()->StyleAt(currentStyle);
381 				if (!style)
382 					style = fDocument->Icon()->Styles()->StyleAt(0);
383 			}
384 
385 			Shape* shape = new (nothrow) Shape(style);
386 			Shape* shapes[1];
387 			shapes[0] = shape;
388 			AddShapesCommand* shapeCommand = new (nothrow) AddShapesCommand(
389 				fDocument->Icon()->Shapes(), shapes, 1,
390 				fDocument->Icon()->Shapes()->CountShapes(),
391 				fDocument->Selection());
392 
393 			if (path && shape)
394 				shape->Paths()->AddPath(path);
395 
396 			::Command* command = NULL;
397 			if (styleCommand || pathCommand) {
398 				if (styleCommand && pathCommand) {
399 					Command** commands = new Command*[3];
400 					commands[0] = styleCommand;
401 					commands[1] = pathCommand;
402 					commands[2] = shapeCommand;
403 					command = new CompoundCommand(commands, 3,
404 						B_TRANSLATE_WITH_CONTEXT("Add shape with path & style",
405 							"Icon-O-Matic-Menu-Shape"),
406 						0);
407 				} else if (styleCommand) {
408 					Command** commands = new Command*[2];
409 					commands[0] = styleCommand;
410 					commands[1] = shapeCommand;
411 					command = new CompoundCommand(commands, 2,
412 						B_TRANSLATE_WITH_CONTEXT("Add shape with style",
413 							"Icon-O-Matic-Menu-Shape"),
414 						0);
415 				} else {
416 					Command** commands = new Command*[2];
417 					commands[0] = pathCommand;
418 					commands[1] = shapeCommand;
419 					command = new CompoundCommand(commands, 2,
420 						B_TRANSLATE_WITH_CONTEXT("Add shape with path",
421 							"Icon-O-Matic-Menu-Shape"),
422 						0);
423 				}
424 			} else {
425 				command = shapeCommand;
426 			}
427 			fDocument->CommandStack()->Perform(command);
428 			break;
429 		}
430 
431 // TODO: listen to selection in CanvasView to add a manipulator
432 case MSG_PATH_SELECTED: {
433 	VectorPath* path;
434 	if (message->FindPointer("path", (void**)&path) < B_OK)
435 		path = NULL;
436 
437 	fPathListView->SetCurrentShape(NULL);
438 	fStyleListView->SetCurrentShape(NULL);
439 	fTransformerListView->SetShape(NULL);
440 
441 	fState->DeleteManipulators();
442 	if (fDocument->Icon()->Paths()->HasPath(path)) {
443 		PathManipulator* pathManipulator = new (nothrow) PathManipulator(path);
444 		fState->AddManipulator(pathManipulator);
445 	}
446 	break;
447 }
448 case MSG_STYLE_SELECTED:
449 case MSG_STYLE_TYPE_CHANGED: {
450 	Style* style;
451 	if (message->FindPointer("style", (void**)&style) < B_OK)
452 		style = NULL;
453 	if (!fDocument->Icon()->Styles()->HasStyle(style))
454 		style = NULL;
455 
456 	fStyleView->SetStyle(style);
457 	fPathListView->SetCurrentShape(NULL);
458 	fStyleListView->SetCurrentShape(NULL);
459 	fTransformerListView->SetShape(NULL);
460 
461 	fState->DeleteManipulators();
462 	Gradient* gradient = style ? style->Gradient() : NULL;
463 	if (gradient != NULL) {
464 		TransformGradientBox* transformBox
465 			= new (nothrow) TransformGradientBox(fCanvasView, gradient, NULL);
466 		fState->AddManipulator(transformBox);
467 	}
468 	break;
469 }
470 case MSG_SHAPE_SELECTED: {
471 	Shape* shape;
472 	if (message->FindPointer("shape", (void**)&shape) < B_OK)
473 		shape = NULL;
474 	if (!fIcon || !fIcon->Shapes()->HasShape(shape))
475 		shape = NULL;
476 
477 	fPathListView->SetCurrentShape(shape);
478 	fStyleListView->SetCurrentShape(shape);
479 	fTransformerListView->SetShape(shape);
480 
481 	BList selectedShapes;
482 	ShapeContainer* shapes = fDocument->Icon()->Shapes();
483 	int32 count = shapes->CountShapes();
484 	for (int32 i = 0; i < count; i++) {
485 		shape = shapes->ShapeAtFast(i);
486 		if (shape->IsSelected()) {
487 			selectedShapes.AddItem((void*)shape);
488 		}
489 	}
490 
491 	fState->DeleteManipulators();
492 	if (selectedShapes.CountItems() > 0) {
493 		TransformShapesBox* transformBox = new (nothrow) TransformShapesBox(
494 			fCanvasView,
495 			(const Shape**)selectedShapes.Items(),
496 			selectedShapes.CountItems());
497 		fState->AddManipulator(transformBox);
498 	}
499 	break;
500 }
501 		case MSG_RENAME_OBJECT:
502 			fPropertyListView->FocusNameProperty();
503 			break;
504 
505 		default:
506 			BWindow::MessageReceived(message);
507 	}
508 
509 	if (requiresWriteLock)
510 		fDocument->WriteUnlock();
511 }
512 
513 
514 bool
515 MainWindow::QuitRequested()
516 {
517 	if (!_CheckSaveIcon(CurrentMessage()))
518 		return false;
519 
520 	BMessage message(MSG_WINDOW_CLOSED);
521 
522 	BMessage settings;
523 	StoreSettings(&settings);
524 	message.AddMessage("settings", &settings);
525 	message.AddRect("window frame", Frame());
526 
527 	be_app->PostMessage(&message);
528 
529 	return true;
530 }
531 
532 
533 void
534 MainWindow::WorkspaceActivated(int32 workspace, bool active)
535 {
536 	BWindow::WorkspaceActivated(workspace, active);
537 
538 	// NOTE: hack to workaround buggy BScreen::DesktopColor() on R5
539 
540 	uint32 workspaces = Workspaces();
541 	if (!active || ((1 << workspace) & workspaces) == 0)
542 		return;
543 
544 	WorkspacesChanged(workspaces, workspaces);
545 }
546 
547 
548 void
549 MainWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
550 {
551 	if (oldWorkspaces != newWorkspaces)
552 		BWindow::WorkspacesChanged(oldWorkspaces, newWorkspaces);
553 
554 	BScreen screen(this);
555 
556 	// Unfortunately, this is buggy on R5: screen.DesktopColor()
557 	// as well as ui_color(B_DESKTOP_COLOR) return the color
558 	// of the *active* screen, not the one on which this window
559 	// is. So this trick only works when you drag this window
560 	// from another workspace onto the current workspace, not
561 	// when you drag the window from the current workspace onto
562 	// another workspace and then switch to the other workspace.
563 
564 	fIconPreview32Desktop->SetIconBGColor(screen.DesktopColor());
565 	fIconPreview64->SetIconBGColor(screen.DesktopColor());
566 }
567 
568 
569 // #pragma mark -
570 
571 
572 void
573 MainWindow::ObjectChanged(const Observable* object)
574 {
575 	if (!fDocument || !fDocument->ReadLock())
576 		return;
577 
578 	if (object == fDocument->CommandStack())
579 		PostMessage(MSG_UNDO_STACK_CHANGED);
580 
581 	fDocument->ReadUnlock();
582 }
583 
584 
585 // #pragma mark -
586 
587 
588 void
589 MainWindow::MakeEmpty()
590 {
591 	fPathListView->SetCurrentShape(NULL);
592 	fStyleListView->SetCurrentShape(NULL);
593 	fStyleView->SetStyle(NULL);
594 
595 	fTransformerListView->SetShape(NULL);
596 
597 	fState->DeleteManipulators();
598 }
599 
600 
601 void
602 MainWindow::Open(const entry_ref& ref, bool append)
603 {
604 	BFile file(&ref, B_READ_ONLY);
605 	if (file.InitCheck() < B_OK)
606 		return;
607 
608 	Icon* icon;
609 	if (append)
610 		icon = new (nothrow) Icon(*fDocument->Icon());
611 	else
612 		icon = new (nothrow) Icon();
613 
614 	if (icon == NULL) {
615 		// TODO: Report error to user.
616 		return;
617 	}
618 
619 	enum {
620 		REF_NONE = 0,
621 		REF_MESSAGE,
622 		REF_FLAT,
623 		REF_FLAT_ATTR,
624 		REF_SVG
625 	};
626 	uint32 refMode = REF_NONE;
627 
628 	// try different file types
629 	FlatIconImporter flatImporter;
630 	status_t ret = flatImporter.Import(icon, &file);
631 	if (ret >= B_OK) {
632 		refMode = REF_FLAT;
633 	} else {
634 		file.Seek(0, SEEK_SET);
635 		MessageImporter msgImporter;
636 		ret = msgImporter.Import(icon, &file);
637 		if (ret >= B_OK) {
638 			refMode = REF_MESSAGE;
639 		} else {
640 			file.Seek(0, SEEK_SET);
641 			SVGImporter svgImporter;
642 			ret = svgImporter.Import(icon, &ref);
643 			if (ret >= B_OK) {
644 				refMode = REF_SVG;
645 			} else {
646 				// fall back to flat icon format but use the icon attribute
647 				ret = B_OK;
648 				attr_info attrInfo;
649 				if (file.GetAttrInfo(kVectorAttrNodeName, &attrInfo) == B_OK) {
650 					if (attrInfo.type != B_VECTOR_ICON_TYPE)
651 						ret = B_ERROR;
652 					// If the attribute is there, we must succeed in reading
653 					// an icon! Otherwise we may overwrite an existing icon
654 					// when the user saves.
655 					uint8* buffer = NULL;
656 					if (ret == B_OK) {
657 						buffer = new(nothrow) uint8[attrInfo.size];
658 						if (buffer == NULL)
659 							ret = B_NO_MEMORY;
660 					}
661 					if (ret == B_OK) {
662 						ssize_t bytesRead = file.ReadAttr(kVectorAttrNodeName,
663 							B_VECTOR_ICON_TYPE, 0, buffer, attrInfo.size);
664 						if (bytesRead != (ssize_t)attrInfo.size) {
665 							ret = bytesRead < 0 ? (status_t)bytesRead
666 								: B_IO_ERROR;
667 						}
668 					}
669 					if (ret == B_OK) {
670 						ret = flatImporter.Import(icon, buffer, attrInfo.size);
671 						if (ret == B_OK)
672 							refMode = REF_FLAT_ATTR;
673 					}
674 
675 					delete[] buffer;
676 				} else {
677 					// If there is no icon attribute, simply fall back
678 					// to creating an icon for this file. TODO: We may or may
679 					// not want to display an alert asking the user if that is
680 					// what he wants to do.
681 					refMode = REF_FLAT_ATTR;
682 				}
683 			}
684 		}
685 	}
686 
687 	if (ret < B_OK) {
688 		// inform user of failure at this point
689 		BString helper(B_TRANSLATE("Opening the document failed!"));
690 		helper << "\n\n" << B_TRANSLATE("Error: ") << strerror(ret);
691 		BAlert* alert = new BAlert(
692 			B_TRANSLATE_WITH_CONTEXT("bad news", "Title of error alert"),
693 			helper.String(),
694 			B_TRANSLATE_WITH_CONTEXT("Bummer",
695 				"Cancel button - error alert"),
696 			NULL, NULL);
697 		// launch alert asynchronously
698 		alert->Go(NULL);
699 
700 		delete icon;
701 		return;
702 	}
703 
704 	AutoWriteLocker locker(fDocument);
705 
706 	// incorporate the loaded icon into the document
707 	// (either replace it or append to it)
708 	fDocument->MakeEmpty(!append);
709 		// if append, the document savers are preserved
710 	fDocument->SetIcon(icon);
711 	if (!append) {
712 		// document got replaced, but we have at
713 		// least one ref already
714 		switch (refMode) {
715 			case REF_MESSAGE:
716 				fDocument->SetNativeSaver(new NativeSaver(ref));
717 				break;
718 			case REF_FLAT:
719 				fDocument->SetExportSaver(
720 					new SimpleFileSaver(new FlatIconExporter(), ref));
721 				break;
722 			case REF_FLAT_ATTR:
723 				fDocument->SetNativeSaver(
724 					new AttributeSaver(ref, kVectorAttrNodeName));
725 				break;
726 			case REF_SVG:
727 				fDocument->SetExportSaver(
728 					new SimpleFileSaver(new SVGExporter(), ref));
729 				break;
730 		}
731 	}
732 
733 	locker.Unlock();
734 
735 	SetIcon(icon);
736 
737 	_UpdateWindowTitle();
738 }
739 
740 
741 void
742 MainWindow::Open(const BMessenger& externalObserver, const uint8* data,
743 	size_t size)
744 {
745 	if (!_CheckSaveIcon(CurrentMessage()))
746 		return;
747 
748 	if (!externalObserver.IsValid())
749 		return;
750 
751 	Icon* icon = new (nothrow) Icon();
752 	if (!icon)
753 		return;
754 
755 	if (data && size > 0) {
756 		// try to open the icon from the provided data
757 		FlatIconImporter flatImporter;
758 		status_t ret = flatImporter.Import(icon, const_cast<uint8*>(data),
759 			size);
760 			// NOTE: the const_cast is a bit ugly, but no harm is done
761 			// the reason is that the LittleEndianBuffer knows read and write
762 			// mode, in this case it is used read-only, and it does not assume
763 			// ownership of the buffer
764 
765 		if (ret < B_OK) {
766 			// inform user of failure at this point
767 			BString helper(B_TRANSLATE("Opening the icon failed!"));
768 			helper << "\n\n" << B_TRANSLATE("Error: ") << strerror(ret);
769 			BAlert* alert = new BAlert(
770 				B_TRANSLATE_WITH_CONTEXT("bad news", "Title of error alert"),
771 				helper.String(),
772 				B_TRANSLATE_WITH_CONTEXT("Bummer",
773 					"Cancel button - error alert"),
774 				NULL, NULL);
775 			// launch alert asynchronously
776 			alert->Go(NULL);
777 
778 			delete icon;
779 			return;
780 		}
781 	}
782 
783 	AutoWriteLocker locker(fDocument);
784 
785 	SetIcon(NULL);
786 
787 	// incorporate the loaded icon into the document
788 	// (either replace it or append to it)
789 	fDocument->MakeEmpty();
790 	fDocument->SetIcon(icon);
791 
792 	fDocument->SetNativeSaver(new MessengerSaver(externalObserver));
793 
794 	locker.Unlock();
795 
796 	SetIcon(icon);
797 }
798 
799 
800 void
801 MainWindow::SetIcon(Icon* icon)
802 {
803 	if (fIcon == icon)
804 		return;
805 
806 	Icon* oldIcon = fIcon;
807 
808 	fIcon = icon;
809 
810 	if (fIcon != NULL)
811 		fIcon->Acquire();
812 	else
813 		MakeEmpty();
814 
815 	fCanvasView->SetIcon(fIcon);
816 
817 	fPathListView->SetPathContainer(fIcon != NULL ? fIcon->Paths() : NULL);
818 	fPathListView->SetShapeContainer(fIcon != NULL ? fIcon->Shapes() : NULL);
819 
820 	fStyleListView->SetStyleContainer(fIcon != NULL ? fIcon->Styles() : NULL);
821 	fStyleListView->SetShapeContainer(fIcon != NULL ? fIcon->Shapes() : NULL);
822 
823 	fShapeListView->SetShapeContainer(fIcon != NULL ? fIcon->Shapes() : NULL);
824 
825 	// icon previews
826 	fIconPreview16Folder->SetIcon(fIcon);
827 	fIconPreview16Menu->SetIcon(fIcon);
828 	fIconPreview32Folder->SetIcon(fIcon);
829 	fIconPreview32Desktop->SetIcon(fIcon);
830 //	fIconPreview48->SetIcon(fIcon);
831 	fIconPreview64->SetIcon(fIcon);
832 
833 	// keep this last
834 	if (oldIcon != NULL)
835 		oldIcon->Release();
836 }
837 
838 
839 // #pragma mark -
840 
841 
842 void
843 MainWindow::StoreSettings(BMessage* archive)
844 {
845 	if (archive->ReplaceUInt32("mouse filter mode",
846 			fCanvasView->MouseFilterMode()) != B_OK) {
847 		archive->AddUInt32("mouse filter mode",
848 			fCanvasView->MouseFilterMode());
849 	}
850 }
851 
852 
853 void
854 MainWindow::RestoreSettings(const BMessage* archive)
855 {
856 	uint32 mouseFilterMode;
857 	if (archive->FindUInt32("mouse filter mode", &mouseFilterMode) == B_OK) {
858 		fCanvasView->SetMouseFilterMode(mouseFilterMode);
859 		fMouseFilterOffMI->SetMarked(mouseFilterMode == SNAPPING_OFF);
860 		fMouseFilter64MI->SetMarked(mouseFilterMode == SNAPPING_64);
861 		fMouseFilter32MI->SetMarked(mouseFilterMode == SNAPPING_32);
862 		fMouseFilter16MI->SetMarked(mouseFilterMode == SNAPPING_16);
863 	}
864 }
865 
866 
867 // #pragma mark -
868 
869 
870 void
871 MainWindow::_Init()
872 {
873 	// create the GUI
874 	_CreateGUI();
875 
876 	// fix up scrollbar layout in listviews
877 	_ImproveScrollBarLayout(fPathListView);
878 	_ImproveScrollBarLayout(fStyleListView);
879 	_ImproveScrollBarLayout(fShapeListView);
880 	_ImproveScrollBarLayout(fTransformerListView);
881 
882 	// TODO: move this to CanvasView?
883 	fState = new MultipleManipulatorState(fCanvasView);
884 	fCanvasView->SetState(fState);
885 
886 	fCanvasView->SetCatchAllEvents(true);
887 	fCanvasView->SetCommandStack(fDocument->CommandStack());
888 	fCanvasView->SetMouseFilterMode(SNAPPING_64);
889 	fMouseFilter64MI->SetMarked(true);
890 //	fCanvasView->SetSelection(fDocument->Selection());
891 
892 	fPathListView->SetMenu(fPathMenu);
893 	fPathListView->SetCommandStack(fDocument->CommandStack());
894 	fPathListView->SetSelection(fDocument->Selection());
895 
896 	fStyleListView->SetMenu(fStyleMenu);
897 	fStyleListView->SetCommandStack(fDocument->CommandStack());
898 	fStyleListView->SetSelection(fDocument->Selection());
899 	fStyleListView->SetCurrentColor(fCurrentColor);
900 
901 	fStyleView->SetCommandStack(fDocument->CommandStack());
902 	fStyleView->SetCurrentColor(fCurrentColor);
903 
904 	fShapeListView->SetMenu(fShapeMenu);
905 	fShapeListView->SetCommandStack(fDocument->CommandStack());
906 	fShapeListView->SetSelection(fDocument->Selection());
907 
908 	fTransformerListView->SetMenu(fTransformerMenu);
909 	fTransformerListView->SetCommandStack(fDocument->CommandStack());
910 	fTransformerListView->SetSelection(fDocument->Selection());
911 
912 	fPropertyListView->SetCommandStack(fDocument->CommandStack());
913 	fPropertyListView->SetSelection(fDocument->Selection());
914 	fPropertyListView->SetMenu(fPropertyMenu);
915 
916 	fDocument->CommandStack()->AddObserver(this);
917 
918 	fSwatchGroup->SetCurrentColor(fCurrentColor);
919 
920 	SetIcon(fDocument->Icon());
921 
922 	AddShortcut('Y', 0, new BMessage(MSG_UNDO));
923 	AddShortcut('Y', B_SHIFT_KEY, new BMessage(MSG_REDO));
924 	AddShortcut('E', 0, new BMessage(MSG_RENAME_OBJECT));
925 }
926 
927 
928 void
929 MainWindow::_CreateGUI()
930 {
931 	SetLayout(new BGroupLayout(B_HORIZONTAL));
932 
933 	BGridLayout* layout = new BGridLayout();
934 	layout->SetSpacing(0, 0);
935 	BView* rootView = new BView("root view", 0, layout);
936 	AddChild(rootView);
937 	rootView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
938 
939 	BGroupView* leftTopView = new BGroupView(B_VERTICAL, 0);
940 	layout->AddView(leftTopView, 0, 0);
941 
942 	// views along the left side
943 	leftTopView->AddChild(_CreateMenuBar());
944 
945 	float splitWidth = 13 * be_plain_font->Size();
946 	BSize minSize = leftTopView->MinSize();
947 	splitWidth = std::max(splitWidth, minSize.width);
948 	leftTopView->SetExplicitMaxSize(BSize(splitWidth, B_SIZE_UNSET));
949 	leftTopView->SetExplicitMinSize(BSize(splitWidth, B_SIZE_UNSET));
950 
951 	BGroupView* iconPreviews = new BGroupView(B_HORIZONTAL);
952 	iconPreviews->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
953 	iconPreviews->GroupLayout()->SetSpacing(5);
954 
955 	// icon previews
956 	fIconPreview16Folder = new IconView(BRect(0, 0, 15, 15),
957 		"icon preview 16 folder");
958 	fIconPreview16Menu = new IconView(BRect(0, 0, 15, 15),
959 		"icon preview 16 menu");
960 	fIconPreview16Menu->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR));
961 
962 	fIconPreview32Folder = new IconView(BRect(0, 0, 31, 31),
963 		"icon preview 32 folder");
964 	fIconPreview32Desktop = new IconView(BRect(0, 0, 31, 31),
965 		"icon preview 32 desktop");
966 	fIconPreview32Desktop->SetLowColor(ui_color(B_DESKTOP_COLOR));
967 
968 	fIconPreview64 = new IconView(BRect(0, 0, 63, 63), "icon preview 64");
969 	fIconPreview64->SetLowColor(ui_color(B_DESKTOP_COLOR));
970 
971 
972 	BGroupView* smallPreviews = new BGroupView(B_VERTICAL);
973 	smallPreviews->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
974 	smallPreviews->GroupLayout()->SetSpacing(5);
975 
976 	smallPreviews->AddChild(fIconPreview16Folder);
977 	smallPreviews->AddChild(fIconPreview16Menu);
978 
979 	BGroupView* mediumPreviews = new BGroupView(B_VERTICAL);
980 	mediumPreviews->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
981 	mediumPreviews->GroupLayout()->SetSpacing(5);
982 
983 	mediumPreviews->AddChild(fIconPreview32Folder);
984 	mediumPreviews->AddChild(fIconPreview32Desktop);
985 
986 //	iconPreviews->AddChild(fIconPreview48);
987 
988 	iconPreviews->AddChild(smallPreviews);
989 	iconPreviews->AddChild(mediumPreviews);
990 	iconPreviews->AddChild(fIconPreview64);
991 	iconPreviews->SetExplicitMaxSize(BSize(B_SIZE_UNSET, B_SIZE_UNLIMITED));
992 
993 	leftTopView->AddChild(iconPreviews);
994 
995 
996 	BGroupView* leftSideView = new BGroupView(B_VERTICAL, 0);
997 	layout->AddView(leftSideView, 0, 1);
998 	leftSideView->SetExplicitMaxSize(BSize(splitWidth, B_SIZE_UNSET));
999 
1000 	// path menu and list view
1001 	BMenuBar* menuBar = new BMenuBar("path menu bar");
1002 	menuBar->AddItem(fPathMenu);
1003 	leftSideView->AddChild(menuBar);
1004 
1005 	fPathListView = new PathListView(BRect(0, 0, splitWidth, 100),
1006 		"path list view", new BMessage(MSG_PATH_SELECTED), this);
1007 
1008 	BView* scrollView = new BScrollView("path list scroll view",
1009 		fPathListView, B_FOLLOW_NONE, 0, false, true, B_NO_BORDER);
1010 	leftSideView->AddChild(scrollView);
1011 
1012 	// shape list view
1013 	menuBar = new BMenuBar("shape menu bar");
1014 	menuBar->AddItem(fShapeMenu);
1015 	leftSideView->AddChild(menuBar);
1016 
1017 	fShapeListView = new ShapeListView(BRect(0, 0, splitWidth, 100),
1018 		"shape list view", new BMessage(MSG_SHAPE_SELECTED), this);
1019 	scrollView = new BScrollView("shape list scroll view",
1020 		fShapeListView, B_FOLLOW_NONE, 0, false, true, B_NO_BORDER);
1021 	leftSideView->AddChild(scrollView);
1022 
1023 	// transformer list view
1024 	menuBar = new BMenuBar("transformer menu bar");
1025 	menuBar->AddItem(fTransformerMenu);
1026 	leftSideView->AddChild(menuBar);
1027 
1028 	fTransformerListView = new TransformerListView(BRect(0, 0, splitWidth, 100),
1029 		"transformer list view");
1030 	scrollView = new BScrollView("transformer list scroll view",
1031 		fTransformerListView, B_FOLLOW_NONE, 0, false, true, B_NO_BORDER);
1032 	leftSideView->AddChild(scrollView);
1033 
1034 	// property list view
1035 	menuBar = new BMenuBar("property menu bar");
1036 	menuBar->AddItem(fPropertyMenu);
1037 	leftSideView->AddChild(menuBar);
1038 
1039 	fPropertyListView = new IconObjectListView();
1040 
1041 	// scroll view around property list view
1042 	ScrollView* propScrollView = new ScrollView(fPropertyListView,
1043 		SCROLL_VERTICAL, BRect(0, 0, splitWidth, 100), "property scroll view",
1044 		B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS, B_PLAIN_BORDER,
1045 		BORDER_RIGHT);
1046 	leftSideView->AddChild(propScrollView);
1047 
1048 	BGroupLayout* topSide = new BGroupLayout(B_HORIZONTAL);
1049 	topSide->SetSpacing(0);
1050 	BView* topSideView = new BView("top side view", 0, topSide);
1051 	layout->AddView(topSideView, 1, 0);
1052 
1053 	// canvas view
1054 	BRect canvasBounds = BRect(0, 0, 200, 200);
1055 	fCanvasView = new CanvasView(canvasBounds);
1056 
1057 	// scroll view around canvas view
1058 	canvasBounds.bottom += B_H_SCROLL_BAR_HEIGHT;
1059 	canvasBounds.right += B_V_SCROLL_BAR_WIDTH;
1060 	ScrollView* canvasScrollView = new ScrollView(fCanvasView, SCROLL_VERTICAL
1061 			| SCROLL_HORIZONTAL | SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS,
1062 		canvasBounds, "canvas scroll view", B_FOLLOW_NONE,
1063 		B_WILL_DRAW | B_FRAME_EVENTS, B_NO_BORDER);
1064 	layout->AddView(canvasScrollView, 1, 1);
1065 
1066 	// views along the top
1067 
1068 	BGroupLayout* styleGroup = new BGroupLayout(B_VERTICAL, 0);
1069 	BView* styleGroupView = new BView("style group", 0, styleGroup);
1070 	topSide->AddView(styleGroupView);
1071 
1072 	// style list view
1073 	menuBar = new BMenuBar("style menu bar");
1074 	menuBar->AddItem(fStyleMenu);
1075 	styleGroup->AddView(menuBar);
1076 
1077 	fStyleListView = new StyleListView(BRect(0, 0, splitWidth, 100),
1078 		"style list view", new BMessage(MSG_STYLE_SELECTED), this);
1079 	scrollView = new BScrollView("style list scroll view", fStyleListView,
1080 		B_FOLLOW_NONE, 0, false, true, B_NO_BORDER);
1081 	scrollView->SetExplicitMaxSize(BSize(splitWidth, B_SIZE_UNLIMITED));
1082 	styleGroup->AddView(scrollView);
1083 
1084 	// style view
1085 	fStyleView = new StyleView(BRect(0, 0, 200, 100));
1086 	topSide->AddView(fStyleView);
1087 
1088 	// swatch group
1089 	BGroupLayout* swatchGroup = new BGroupLayout(B_VERTICAL);
1090 	swatchGroup->SetSpacing(0);
1091 	BView* swatchGroupView = new BView("swatch group", 0, swatchGroup);
1092 	topSide->AddView(swatchGroupView);
1093 
1094 	menuBar = new BMenuBar("swatches menu bar");
1095 	menuBar->AddItem(fSwatchMenu);
1096 	swatchGroup->AddView(menuBar);
1097 
1098 	fSwatchGroup = new SwatchGroup(BRect(0, 0, 100, 100));
1099 	swatchGroup->AddView(fSwatchGroup);
1100 
1101 	swatchGroupView->SetExplicitMaxSize(swatchGroupView->MinSize());
1102 
1103 	// make sure the top side has fixed height
1104 	topSideView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
1105 		swatchGroupView->MinSize().height));
1106 }
1107 
1108 BMenuBar*
1109 MainWindow::_CreateMenuBar()
1110 {
1111 	BMenuBar* menuBar = new BMenuBar("main menu");
1112 
1113 
1114 	#undef B_TRANSLATE_CONTEXT
1115 	#define B_TRANSLATE_CONTEXT "Icon-O-Matic-Menus"
1116 
1117 
1118 	BMenu* fileMenu = new BMenu(B_TRANSLATE("File"));
1119 	BMenu* editMenu = new BMenu(B_TRANSLATE("Edit"));
1120 	BMenu* settingsMenu = new BMenu(B_TRANSLATE("Options"));
1121 	fPathMenu = new BMenu(B_TRANSLATE("Path"));
1122 	fStyleMenu = new BMenu(B_TRANSLATE("Style"));
1123 	fShapeMenu = new BMenu(B_TRANSLATE("Shape"));
1124 	fTransformerMenu = new BMenu(B_TRANSLATE("Transformer"));
1125 	fPropertyMenu = new BMenu(B_TRANSLATE("Properties"));
1126 	fSwatchMenu = new BMenu(B_TRANSLATE("Swatches"));
1127 
1128 	menuBar->AddItem(fileMenu);
1129 	menuBar->AddItem(editMenu);
1130 	menuBar->AddItem(settingsMenu);
1131 
1132 
1133 	// File
1134 	#undef B_TRANSLATE_CONTEXT
1135 	#define B_TRANSLATE_CONTEXT "Icon-O-Matic-Menu-File"
1136 
1137 
1138 	BMenuItem* item = new BMenuItem(B_TRANSLATE("New"),
1139 		new BMessage(MSG_NEW), 'N');
1140 	fileMenu->AddItem(item);
1141 	item->SetTarget(be_app);
1142 	item = new BMenuItem(B_TRANSLATE("Open"B_UTF8_ELLIPSIS),
1143 		new BMessage(MSG_OPEN), 'O');
1144 	fileMenu->AddItem(item);
1145 	item->SetTarget(be_app);
1146 	BMessage* appendMessage = new BMessage(MSG_APPEND);
1147 	appendMessage->AddPointer("window", this);
1148 	item = new BMenuItem(B_TRANSLATE("Append"B_UTF8_ELLIPSIS),
1149 		appendMessage, 'O', B_SHIFT_KEY);
1150 	fileMenu->AddItem(item);
1151 	item->SetTarget(be_app);
1152 	fileMenu->AddSeparatorItem();
1153 	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Save"),
1154 		new BMessage(MSG_SAVE), 'S'));
1155 	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Save as"B_UTF8_ELLIPSIS),
1156 		new BMessage(MSG_SAVE_AS), 'S', B_SHIFT_KEY));
1157 	fileMenu->AddSeparatorItem();
1158 	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Export"),
1159 		new BMessage(MSG_EXPORT), 'P'));
1160 	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Export as"B_UTF8_ELLIPSIS),
1161 		new BMessage(MSG_EXPORT_AS), 'P', B_SHIFT_KEY));
1162 	fileMenu->AddSeparatorItem();
1163 	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
1164 		new BMessage(B_QUIT_REQUESTED), 'W'));
1165 	item = new BMenuItem(B_TRANSLATE("Quit"),
1166 		new BMessage(B_QUIT_REQUESTED), 'Q');
1167 	fileMenu->AddItem(item);
1168 	item->SetTarget(be_app);
1169 
1170 	// Edit
1171 	#undef B_TRANSLATE_CONTEXT
1172 	#define B_TRANSLATE_CONTEXT "Icon-O-Matic-Menu-Edit"
1173 
1174 
1175 	fUndoMI = new BMenuItem(B_TRANSLATE("<nothing to undo>"),
1176 		new BMessage(MSG_UNDO), 'Z');
1177 	fRedoMI = new BMenuItem(B_TRANSLATE("<nothing to redo>"),
1178 		new BMessage(MSG_REDO), 'Z', B_SHIFT_KEY);
1179 
1180 	fUndoMI->SetEnabled(false);
1181 	fRedoMI->SetEnabled(false);
1182 
1183 	editMenu->AddItem(fUndoMI);
1184 	editMenu->AddItem(fRedoMI);
1185 
1186 
1187 	// Settings
1188 	#undef B_TRANSLATE_CONTEXT
1189 	#define B_TRANSLATE_CONTEXT "Icon-O-Matic-Menu-Settings"
1190 
1191 
1192 	BMenu* filterModeMenu = new BMenu(B_TRANSLATE("Snap to grid"));
1193 	BMessage* message = new BMessage(MSG_MOUSE_FILTER_MODE);
1194 	message->AddInt32("mode", SNAPPING_OFF);
1195 	fMouseFilterOffMI = new BMenuItem(B_TRANSLATE("Off"), message, '4');
1196 	filterModeMenu->AddItem(fMouseFilterOffMI);
1197 
1198 	message = new BMessage(MSG_MOUSE_FILTER_MODE);
1199 	message->AddInt32("mode", SNAPPING_64);
1200 	fMouseFilter64MI = new BMenuItem("64 x 64", message, '3');
1201 	filterModeMenu->AddItem(fMouseFilter64MI);
1202 
1203 	message = new BMessage(MSG_MOUSE_FILTER_MODE);
1204 	message->AddInt32("mode", SNAPPING_32);
1205 	fMouseFilter32MI = new BMenuItem("32 x 32", message, '2');
1206 	filterModeMenu->AddItem(fMouseFilter32MI);
1207 
1208 	message = new BMessage(MSG_MOUSE_FILTER_MODE);
1209 	message->AddInt32("mode", SNAPPING_16);
1210 	fMouseFilter16MI = new BMenuItem("16 x 16", message, '1');
1211 	filterModeMenu->AddItem(fMouseFilter16MI);
1212 
1213 	filterModeMenu->SetRadioMode(true);
1214 
1215 	settingsMenu->AddItem(filterModeMenu);
1216 
1217 	return menuBar;
1218 }
1219 
1220 
1221 void
1222 MainWindow::_ImproveScrollBarLayout(BView* target)
1223 {
1224 	// NOTE: The BListViews for which this function is used
1225 	// are directly below a BMenuBar. If the BScrollBar and
1226 	// the BMenuBar share bottom/top border respectively, the
1227 	// GUI looks a little more polished. This trick can be
1228 	// removed if/when the BScrollViews are embedded in a
1229 	// surounding border like in WonderBrush.
1230 
1231 	if (BScrollBar* scrollBar = target->ScrollBar(B_VERTICAL)) {
1232 		scrollBar->MoveBy(0, -1);
1233 		scrollBar->ResizeBy(0, 1);
1234 	}
1235 }
1236 
1237 
1238 // #pragma mark -
1239 
1240 
1241 bool
1242 MainWindow::_CheckSaveIcon(const BMessage* currentMessage)
1243 {
1244 	if (fDocument->IsEmpty() || fDocument->CommandStack()->IsSaved())
1245 		return true;
1246 
1247 	// Make sure the user sees us.
1248 	Activate();
1249 
1250 	BAlert* alert = new BAlert("save",
1251 		B_TRANSLATE("Save changes to current icon?"), B_TRANSLATE("Discard"),
1252 		 B_TRANSLATE("Cancel"), B_TRANSLATE("Save"));
1253 	int32 choice = alert->Go();
1254 	switch (choice) {
1255 		case 0:
1256 			// discard
1257 			return true;
1258 		case 1:
1259 			// cancel
1260 			return false;
1261 		case 2:
1262 		default:
1263 			// cancel (save first) but pick up what we were doing before
1264 			PostMessage(MSG_SAVE);
1265 			if (currentMessage != NULL) {
1266 				delete fMessageAfterSave;
1267 				fMessageAfterSave = new BMessage(*currentMessage);
1268 			}
1269 			return false;
1270 	}
1271 }
1272 
1273 
1274 void
1275 MainWindow::_PickUpActionBeforeSave()
1276 {
1277 	if (fDocument->WriteLock()) {
1278 		fDocument->CommandStack()->Save();
1279 		fDocument->WriteUnlock();
1280 	}
1281 
1282 	if (fMessageAfterSave == NULL)
1283 		return;
1284 
1285 	PostMessage(fMessageAfterSave);
1286 	delete fMessageAfterSave;
1287 	fMessageAfterSave = NULL;
1288 }
1289 
1290 
1291 // #pragma mark -
1292 
1293 
1294 void
1295 MainWindow::_MakeIconEmpty()
1296 {
1297 	if (!_CheckSaveIcon(CurrentMessage()))
1298 		return;
1299 
1300 	AutoWriteLocker locker(fDocument);
1301 
1302 	MakeEmpty();
1303 	fDocument->MakeEmpty();
1304 
1305 	locker.Unlock();
1306 }
1307 
1308 
1309 DocumentSaver*
1310 MainWindow::_CreateSaver(const entry_ref& ref, uint32 exportMode)
1311 {
1312 	DocumentSaver* saver;
1313 
1314 	switch (exportMode) {
1315 		case EXPORT_MODE_FLAT_ICON:
1316 			saver = new SimpleFileSaver(new FlatIconExporter(), ref);
1317 			break;
1318 
1319 		case EXPORT_MODE_ICON_ATTR:
1320 		case EXPORT_MODE_ICON_MIME_ATTR: {
1321 			const char* attrName
1322 				= exportMode == EXPORT_MODE_ICON_ATTR ?
1323 					kVectorAttrNodeName : kVectorAttrMimeName;
1324 			saver = new AttributeSaver(ref, attrName);
1325 			break;
1326 		}
1327 
1328 		case EXPORT_MODE_ICON_RDEF:
1329 			saver = new SimpleFileSaver(new RDefExporter(), ref);
1330 			break;
1331 		case EXPORT_MODE_ICON_SOURCE:
1332 			saver = new SimpleFileSaver(new SourceExporter(), ref);
1333 			break;
1334 
1335 		case EXPORT_MODE_BITMAP_16:
1336 			saver = new SimpleFileSaver(new BitmapExporter(16), ref);
1337 			break;
1338 		case EXPORT_MODE_BITMAP_32:
1339 			saver = new SimpleFileSaver(new BitmapExporter(32), ref);
1340 			break;
1341 		case EXPORT_MODE_BITMAP_64:
1342 			saver = new SimpleFileSaver(new BitmapExporter(64), ref);
1343 			break;
1344 
1345 		case EXPORT_MODE_BITMAP_SET:
1346 			saver = new BitmapSetSaver(ref);
1347 			break;
1348 
1349 		case EXPORT_MODE_SVG:
1350 			saver = new SimpleFileSaver(new SVGExporter(), ref);
1351 			break;
1352 
1353 		case EXPORT_MODE_MESSAGE:
1354 		default:
1355 			saver = new NativeSaver(ref);
1356 			break;
1357 	}
1358 
1359 	return saver;
1360 }
1361 
1362 
1363 const char*
1364 MainWindow::_FileName(bool preferExporter) const
1365 {
1366 	FileSaver* saver1;
1367 	FileSaver* saver2;
1368 	if (preferExporter) {
1369 		saver1 = dynamic_cast<FileSaver*>(fDocument->ExportSaver());
1370 		saver2 = dynamic_cast<FileSaver*>(fDocument->NativeSaver());
1371 	} else {
1372 		saver1 = dynamic_cast<FileSaver*>(fDocument->NativeSaver());
1373 		saver2 = dynamic_cast<FileSaver*>(fDocument->ExportSaver());
1374 	}
1375 	const char* fileName = NULL;
1376 	if (saver1 != NULL)
1377 		fileName = saver1->Ref()->name;
1378 	if ((fileName == NULL || fileName[0] == '\0') && saver2 != NULL)
1379 		fileName = saver2->Ref()->name;
1380 	return fileName;
1381 }
1382 
1383 
1384 void
1385 MainWindow::_UpdateWindowTitle()
1386 {
1387 	const char* fileName = _FileName(false);
1388 	if (fileName != NULL)
1389 		SetTitle(fileName);
1390 	else
1391 		SetTitle(B_TRANSLATE_SYSTEM_NAME("Icon-O-Matic"));
1392 }
1393 
1394