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