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