xref: /haiku/src/apps/icon-o-matic/MainWindow.cpp (revision 893988af824e65e49e55f517b157db8386e8002b)
1 /*
2  * Copyright 2006-2008, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  */
8 
9 #include "MainWindow.h"
10 
11 #include <new>
12 #include <stdio.h>
13 
14 #include <Clipboard.h>
15 #include <Menu.h>
16 #include <MenuBar.h>
17 #include <MenuItem.h>
18 #include <Message.h>
19 #include <Screen.h>
20 #include <ScrollView.h>
21 
22 #ifdef __HAIKU__
23 # include <GridLayout.h>
24 # include <GroupLayout.h>
25 # include <GroupView.h>
26 #endif
27 
28 #include "support_ui.h"
29 
30 #include "AddPathsCommand.h"
31 #include "AddShapesCommand.h"
32 #include "AddStylesCommand.h"
33 #include "CanvasView.h"
34 #include "CommandStack.h"
35 #include "CompoundCommand.h"
36 #include "CurrentColor.h"
37 #include "Document.h"
38 #include "Exporter.h"
39 #include "IconObjectListView.h"
40 #include "IconEditorApp.h"
41 #include "IconView.h"
42 #include "PathListView.h"
43 #include "ScrollView.h"
44 #include "ShapeListView.h"
45 #include "StyleListView.h"
46 #include "StyleView.h"
47 #include "SwatchGroup.h"
48 #include "TransformerListView.h"
49 #include "TransformGradientBox.h"
50 #include "TransformShapesBox.h"
51 #include "Util.h"
52 
53 // TODO: just for testing
54 #include "AffineTransformer.h"
55 #include "GradientTransformable.h"
56 #include "Icon.h"
57 #include "MultipleManipulatorState.h"
58 #include "PathManipulator.h"
59 #include "Shape.h"
60 #include "ShapeContainer.h"
61 #include "ShapeListView.h"
62 #include "StrokeTransformer.h"
63 #include "Style.h"
64 #include "StyleContainer.h"
65 #include "VectorPath.h"
66 
67 #include "StyledTextImporter.h"
68 
69 using std::nothrow;
70 
71 enum {
72 	MSG_UNDO						= 'undo',
73 	MSG_REDO						= 'redo',
74 
75 	MSG_PATH_SELECTED				= 'vpsl',
76 	MSG_STYLE_SELECTED				= 'stsl',
77 	MSG_SHAPE_SELECTED				= 'spsl',
78 
79 	MSG_SHAPE_RESET_TRANSFORMATION	= 'rtsh',
80 	MSG_STYLE_RESET_TRANSFORMATION	= 'rtst',
81 
82 	MSG_MOUSE_FILTER_MODE			= 'mfmd',
83 };
84 
85 // constructor
86 MainWindow::MainWindow(IconEditorApp* app, Document* document,
87 		const BMessage* settings)
88 	: BWindow(BRect(50, 50, 900, 750), "Icon-O-Matic",
89 #ifdef __HAIKU__
90 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
91 			B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
92 #else
93 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_ASYNCHRONOUS_CONTROLS),
94 #endif
95 	  fApp(app),
96 	  fDocument(document),
97 	  fIcon(NULL)
98 {
99 	RestoreSettings(settings);
100 
101 	_Init();
102 }
103 
104 // destructor
105 MainWindow::~MainWindow()
106 {
107 	delete fState;
108 
109 	if (fIcon)
110 		fIcon->Release();
111 
112 	fDocument->CommandStack()->RemoveObserver(this);
113 }
114 
115 // #pragma mark -
116 
117 // MessageReceived
118 void
119 MainWindow::MessageReceived(BMessage* message)
120 {
121 	bool discard = false;
122 
123 	if (!fDocument || !fDocument->WriteLock()) {
124 		BWindow::MessageReceived(message);
125 		return;
126 	}
127 
128 	if (message->WasDropped()) {
129 		const rgb_color *color;
130 		int32 len;
131 		int32 i;
132 		// create styles from dropped colors
133 		for (i = 0; message->FindData("RGBColor", B_RGB_COLOR_TYPE, i,
134 			(const void **)&color, &len) == B_OK; i++) {
135 			if (len != sizeof(rgb_color))
136 				continue;
137 			char name[30];
138 			sprintf(name, "Color (#%02x%02x%02x)",
139 				color->red, color->green, color->blue);
140 			Style* style = new (nothrow) Style(*color);
141 			style->SetName(name);
142 			Style* styles[1] = { style };
143 			AddStylesCommand* styleCommand = new (nothrow) AddStylesCommand(
144 				fDocument->Icon()->Styles(), styles, 1,
145 				fDocument->Icon()->Styles()->CountStyles());
146 			fDocument->CommandStack()->Perform(styleCommand);
147 			// don't handle anything else,
148 			// or we might paste the clipboard on B_PASTE
149 			discard = true;
150 		}
151 	}
152 
153 	switch (message->what) {
154 
155 		case B_REFS_RECEIVED:
156 		case B_SIMPLE_DATA:
157 			message->what = B_REFS_RECEIVED;
158 			if (modifiers() & B_SHIFT_KEY)
159 				message->AddBool("append", true);
160 			be_app->PostMessage(message);
161 			break;
162 
163 		case B_PASTE:
164 		case B_MIME_DATA:
165 		{
166 			BMessage *clip = message;
167 			status_t err;
168 
169 			if (discard)
170 				break;
171 
172 			if (message->what == B_PASTE) {
173 				if (!be_clipboard->Lock())
174 					break;
175 				clip = be_clipboard->Data();
176 			}
177 
178 			if (!clip || !clip->HasData("text/plain", B_MIME_TYPE)) {
179 				if (message->what == B_PASTE)
180 					be_clipboard->Unlock();
181 				break;
182 			}
183 
184 			Icon* icon;
185 			icon = new (nothrow) Icon(*fDocument->Icon());
186 			if (icon) {
187 				StyledTextImporter importer;
188 				err = importer.Import(icon, clip);
189 				if (err >= B_OK) {
190 						AutoWriteLocker locker(fDocument);
191 
192 						SetIcon(NULL);
193 
194 						// incorporate the loaded icon into the document
195 						// (either replace it or append to it)
196 						fDocument->MakeEmpty(false);
197 							// if append, the document savers are preserved
198 						fDocument->SetIcon(icon);
199 						SetIcon(icon);
200 				}
201 			}
202 
203 			if (message->what == B_PASTE)
204 				be_clipboard->Unlock();
205 			break;
206 		}
207 
208 		case MSG_UNDO:
209 			fDocument->CommandStack()->Undo();
210 			break;
211 		case MSG_REDO:
212 			fDocument->CommandStack()->Redo();
213 			break;
214 
215 		case MSG_MOUSE_FILTER_MODE: {
216 			uint32 mode;
217 			if (message->FindInt32("mode", (int32*)&mode) == B_OK)
218 				fCanvasView->SetMouseFilterMode(mode);
219 			break;
220 		}
221 
222 		case MSG_SET_ICON:
223 			SetIcon(fDocument->Icon());
224 			break;
225 
226 		case MSG_ADD_SHAPE: {
227 			AddStylesCommand* styleCommand = NULL;
228 			Style* style = NULL;
229 			if (message->HasBool("style")) {
230 				new_style(CurrentColor::Default()->Color(),
231 						  fDocument->Icon()->Styles(), &style, &styleCommand);
232 			}
233 
234 			AddPathsCommand* pathCommand = NULL;
235 			VectorPath* path = NULL;
236 			if (message->HasBool("path")) {
237 				new_path(fDocument->Icon()->Paths(), &path, &pathCommand);
238 			}
239 
240 			if (!style) {
241 				// use current or first style
242 				int32 currentStyle = fStyleListView->CurrentSelection(0);
243 				style = fDocument->Icon()->Styles()->StyleAt(currentStyle);
244 				if (!style)
245 					style = fDocument->Icon()->Styles()->StyleAt(0);
246 			}
247 
248 			Shape* shape = new (nothrow) Shape(style);
249 			Shape* shapes[1];
250 			shapes[0] = shape;
251 			AddShapesCommand* shapeCommand = new (nothrow) AddShapesCommand(
252 				fDocument->Icon()->Shapes(), shapes, 1,
253 				fDocument->Icon()->Shapes()->CountShapes(),
254 				fDocument->Selection());
255 
256 			if (path && shape)
257 				shape->Paths()->AddPath(path);
258 
259 			::Command* command = NULL;
260 			if (styleCommand || pathCommand) {
261 				if (styleCommand && pathCommand) {
262 					Command** commands = new Command*[3];
263 					commands[0] = styleCommand;
264 					commands[1] = pathCommand;
265 					commands[2] = shapeCommand;
266 					command = new CompoundCommand(commands, 3,
267 										"Add Shape With Path & Style", 0);
268 				} else if (styleCommand) {
269 					Command** commands = new Command*[2];
270 					commands[0] = styleCommand;
271 					commands[1] = shapeCommand;
272 					command = new CompoundCommand(commands, 2,
273 										"Add Shape With Style", 0);
274 				} else {
275 					Command** commands = new Command*[2];
276 					commands[0] = pathCommand;
277 					commands[1] = shapeCommand;
278 					command = new CompoundCommand(commands, 2,
279 										"Add Shape With Path", 0);
280 				}
281 			} else {
282 				command = shapeCommand;
283 			}
284 			fDocument->CommandStack()->Perform(command);
285 			break;
286 		}
287 
288 // TODO: listen to selection in CanvasView to add a manipulator
289 case MSG_PATH_SELECTED: {
290 	VectorPath* path;
291 	if (message->FindPointer("path", (void**)&path) < B_OK)
292 		path = NULL;
293 
294 	fPathListView->SetCurrentShape(NULL);
295 	fStyleListView->SetCurrentShape(NULL);
296 	fTransformerListView->SetShape(NULL);
297 
298 	fState->DeleteManipulators();
299 	if (fDocument->Icon()->Paths()->HasPath(path)) {
300 		PathManipulator* pathManipulator = new (nothrow) PathManipulator(path);
301 		fState->AddManipulator(pathManipulator);
302 	}
303 	break;
304 }
305 case MSG_STYLE_SELECTED:
306 case MSG_STYLE_TYPE_CHANGED: {
307 	Style* style;
308 	if (message->FindPointer("style", (void**)&style) < B_OK)
309 		style = NULL;
310 	if (!fDocument->Icon()->Styles()->HasStyle(style))
311 		style = NULL;
312 
313 	fStyleView->SetStyle(style);
314 	fPathListView->SetCurrentShape(NULL);
315 	fStyleListView->SetCurrentShape(NULL);
316 	fTransformerListView->SetShape(NULL);
317 
318 	fState->DeleteManipulators();
319 	Gradient* gradient = style ? style->Gradient() : NULL;
320 
321 	if (gradient) {
322 		TransformGradientBox* transformBox
323 			= new (nothrow) TransformGradientBox(fCanvasView,
324 												 gradient,
325 												 NULL);
326 		fState->AddManipulator(transformBox);
327 	}
328 	break;
329 }
330 case MSG_SHAPE_SELECTED: {
331 	Shape* shape;
332 	if (message->FindPointer("shape", (void**)&shape) < B_OK)
333 		shape = NULL;
334 	if (!fIcon || !fIcon->Shapes()->HasShape(shape))
335 		shape = NULL;
336 
337 	fPathListView->SetCurrentShape(shape);
338 	fStyleListView->SetCurrentShape(shape);
339 	fTransformerListView->SetShape(shape);
340 
341 	BList selectedShapes;
342 	ShapeContainer* shapes = fDocument->Icon()->Shapes();
343 	int32 count = shapes->CountShapes();
344 	for (int32 i = 0; i < count; i++) {
345 		shape = shapes->ShapeAtFast(i);
346 		if (shape->IsSelected()) {
347 			selectedShapes.AddItem((void*)shape);
348 		}
349 	}
350 
351 	fState->DeleteManipulators();
352 	if (selectedShapes.CountItems() > 0) {
353 		TransformShapesBox* transformBox = new (nothrow) TransformShapesBox(
354 			fCanvasView,
355 			(const Shape**)selectedShapes.Items(),
356 			selectedShapes.CountItems());
357 		fState->AddManipulator(transformBox);
358 	}
359 	break;
360 }
361 		default:
362 			BWindow::MessageReceived(message);
363 	}
364 
365 	fDocument->WriteUnlock();
366 }
367 
368 // QuitRequested
369 bool
370 MainWindow::QuitRequested()
371 {
372 	// forward this to app but return "false" in order
373 	// to have a single code path for quitting
374 	be_app->PostMessage(B_QUIT_REQUESTED);
375 
376 	return false;
377 }
378 
379 // WorkspaceActivated
380 void
381 MainWindow::WorkspaceActivated(int32 workspace, bool active)
382 {
383 	BWindow::WorkspaceActivated(workspace, active);
384 
385 	// NOTE: hack to workaround buggy BScreen::DesktopColor() on R5
386 
387 	uint32 workspaces = Workspaces();
388 	if (!active || ((1 << workspace) & workspaces) == 0)
389 		return;
390 
391 	WorkspacesChanged(workspaces, workspaces);
392 }
393 
394 // WorkspacesChanged
395 void
396 MainWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
397 {
398 	if (oldWorkspaces != newWorkspaces)
399 		BWindow::WorkspacesChanged(oldWorkspaces, newWorkspaces);
400 
401 	BScreen screen(this);
402 
403 	// Unfortunately, this is buggy on R5: screen.DesktopColor()
404 	// as well as ui_color(B_DESKTOP_COLOR) return the color
405 	// of the *active* screen, not the one on which this window
406 	// is. So this trick only works when you drag this window
407 	// from another workspace onto the current workspace, not
408 	// when you drag the window from the current workspace onto
409 	// another workspace and then switch to the other workspace.
410 
411 	fIconPreview32Desktop->SetIconBGColor(screen.DesktopColor());
412 	fIconPreview64->SetIconBGColor(screen.DesktopColor());
413 }
414 
415 // #pragma mark -
416 
417 // ObjectChanged
418 void
419 MainWindow::ObjectChanged(const Observable* object)
420 {
421 	if (!fDocument)
422 		return;
423 
424 	if (!Lock())
425 		return;
426 
427 	if (object == fDocument->CommandStack()) {
428 		// relable Undo item and update enabled status
429 		BString label("Undo");
430 		fUndoMI->SetEnabled(fDocument->CommandStack()->GetUndoName(label));
431 		if (fUndoMI->IsEnabled())
432 			fUndoMI->SetLabel(label.String());
433 		else
434 			fUndoMI->SetLabel("<nothing to undo>");
435 
436 		// relable Redo item and update enabled status
437 		label.SetTo("Redo");
438 		fRedoMI->SetEnabled(fDocument->CommandStack()->GetRedoName(label));
439 		if (fRedoMI->IsEnabled())
440 			fRedoMI->SetLabel(label.String());
441 		else
442 			fRedoMI->SetLabel("<nothing to redo>");
443 	}
444 
445 	Unlock();
446 }
447 
448 // #pragma mark -
449 
450 // MakeEmpty
451 void
452 MainWindow::MakeEmpty()
453 {
454 	fPathListView->SetCurrentShape(NULL);
455 	fStyleListView->SetCurrentShape(NULL);
456 	fStyleView->SetStyle(NULL);
457 
458 	fTransformerListView->SetShape(NULL);
459 
460 	fState->DeleteManipulators();
461 }
462 
463 // SetIcon
464 void
465 MainWindow::SetIcon(Icon* icon)
466 {
467 	if (fIcon == icon)
468 		return;
469 
470 	Icon* oldIcon = fIcon;
471 
472 	fIcon = icon;
473 
474 	if (fIcon)
475 		fIcon->Acquire();
476 	else
477 		MakeEmpty();
478 
479 	fCanvasView->SetIcon(fIcon);
480 
481 	fPathListView->SetPathContainer(fIcon ? fIcon->Paths() : NULL);
482 	fPathListView->SetShapeContainer(fIcon ? fIcon->Shapes() : NULL);
483 
484 	fStyleListView->SetStyleContainer(fIcon ? fIcon->Styles() : NULL);
485 	fStyleListView->SetShapeContainer(fIcon ? fIcon->Shapes() : NULL);
486 
487 	fShapeListView->SetShapeContainer(fIcon ? fIcon->Shapes() : NULL);
488 
489 	// icon previews
490 	fIconPreview16Folder->SetIcon(fIcon);
491 	fIconPreview16Menu->SetIcon(fIcon);
492 	fIconPreview32Folder->SetIcon(fIcon);
493 	fIconPreview32Desktop->SetIcon(fIcon);
494 //	fIconPreview48->SetIcon(fIcon);
495 	fIconPreview64->SetIcon(fIcon);
496 
497 	// keep this last
498 	if (oldIcon)
499 		oldIcon->Release();
500 }
501 
502 // #pragma mark -
503 
504 // StoreSettings
505 void
506 MainWindow::StoreSettings(BMessage* archive)
507 {
508 	if (archive->ReplaceRect("main window frame", Frame()) < B_OK)
509 		archive->AddRect("main window frame", Frame());
510 }
511 
512 // RestoreSettings
513 void
514 MainWindow::RestoreSettings(const BMessage* archive)
515 {
516 	BRect frame;
517 	if (archive->FindRect("main window frame", &frame) == B_OK) {
518 		make_sure_frame_is_on_screen(frame, this);
519 		MoveTo(frame.LeftTop());
520 		ResizeTo(frame.Width(), frame.Height());
521 	}
522 }
523 
524 // #pragma mark -
525 
526 // _Init
527 void
528 MainWindow::_Init()
529 {
530 	// create the GUI
531 	_CreateGUI(Bounds());
532 
533 	// fix up scrollbar layout in listviews
534 	_ImproveScrollBarLayout(fPathListView);
535 	_ImproveScrollBarLayout(fStyleListView);
536 	_ImproveScrollBarLayout(fShapeListView);
537 	_ImproveScrollBarLayout(fTransformerListView);
538 
539 	// TODO: move this to CanvasView?
540 	fState = new MultipleManipulatorState(fCanvasView);
541 	fCanvasView->SetState(fState);
542 
543 	fCanvasView->SetCatchAllEvents(true);
544 	fCanvasView->SetCommandStack(fDocument->CommandStack());
545 	fCanvasView->SetMouseFilterMode(SNAPPING_64);
546 //	fCanvasView->SetSelection(fDocument->Selection());
547 
548 	fPathListView->SetMenu(fPathMenu);
549 	fPathListView->SetCommandStack(fDocument->CommandStack());
550 	fPathListView->SetSelection(fDocument->Selection());
551 
552 	fStyleListView->SetMenu(fStyleMenu);
553 	fStyleListView->SetCommandStack(fDocument->CommandStack());
554 	fStyleListView->SetSelection(fDocument->Selection());
555 
556 	fStyleView->SetCommandStack(fDocument->CommandStack());
557 	fStyleView->SetCurrentColor(CurrentColor::Default());
558 
559 	fShapeListView->SetMenu(fShapeMenu);
560 	fShapeListView->SetCommandStack(fDocument->CommandStack());
561 	fShapeListView->SetSelection(fDocument->Selection());
562 
563 	fTransformerListView->SetMenu(fTransformerMenu);
564 	fTransformerListView->SetCommandStack(fDocument->CommandStack());
565 	fTransformerListView->SetSelection(fDocument->Selection());
566 
567 	fPropertyListView->SetCommandStack(fDocument->CommandStack());
568 	fPropertyListView->SetSelection(fDocument->Selection());
569 	fPropertyListView->SetMenu(fPropertyMenu);
570 
571 	fDocument->CommandStack()->AddObserver(this);
572 
573 	fSwatchGroup->SetCurrentColor(CurrentColor::Default());
574 
575 	SetIcon(fDocument->Icon());
576 
577 	AddShortcut('Y', 0, new BMessage(MSG_UNDO));
578 	AddShortcut('Y', B_SHIFT_KEY, new BMessage(MSG_REDO));
579 }
580 
581 // _CreateGUI
582 void
583 MainWindow::_CreateGUI(BRect bounds)
584 {
585 	const float splitWidth = 13 * be_plain_font->Size();
586 
587 #ifdef __HAIKU__
588 
589 	SetLayout(new BGroupLayout(B_HORIZONTAL));
590 
591 	BGridLayout* layout = new BGridLayout();
592 	BView* rootView = new BView("root view", 0, layout);
593 	AddChild(rootView);
594 	rootView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
595 
596 	BGroupView* leftTopView = new BGroupView(B_VERTICAL);
597 	layout->AddView(leftTopView, 0, 0);
598 	leftTopView->SetExplicitMinSize(BSize(splitWidth, B_SIZE_UNSET));
599 
600 	// views along the left side
601 	leftTopView->AddChild(_CreateMenuBar(bounds));
602 
603 	BGroupView* iconPreviews = new BGroupView(B_HORIZONTAL);
604 	iconPreviews->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
605 	iconPreviews->GroupLayout()->SetSpacing(5);
606 
607 	// icon previews
608 	fIconPreview16Folder = new IconView(BRect(0, 0, 15, 15),
609 										"icon preview 16 folder");
610 	fIconPreview16Menu = new IconView(BRect(0, 0, 15, 15),
611 									  "icon preview 16 menu");
612 	fIconPreview16Menu->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR));
613 
614 	fIconPreview32Folder = new IconView(BRect(0, 0, 31, 31),
615 										"icon preview 32 folder");
616 	fIconPreview32Desktop = new IconView(BRect(0, 0, 31, 31),
617 										 "icon preview 32 desktop");
618 	fIconPreview32Desktop->SetLowColor(ui_color(B_DESKTOP_COLOR));
619 
620 //	fIconPreview48 = new IconView(bounds, "icon preview 48");
621 	fIconPreview64 = new IconView(BRect(0, 0, 63, 63), "icon preview 64");
622 	fIconPreview64->SetLowColor(ui_color(B_DESKTOP_COLOR));
623 
624 
625 	BGroupView* smallPreviews = new BGroupView(B_VERTICAL);
626 	smallPreviews->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
627 	smallPreviews->GroupLayout()->SetSpacing(5);
628 
629 	smallPreviews->AddChild(fIconPreview16Folder);
630 	smallPreviews->AddChild(fIconPreview16Menu);
631 
632 	BGroupView* mediumPreviews = new BGroupView(B_VERTICAL);
633 	mediumPreviews->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
634 	mediumPreviews->GroupLayout()->SetSpacing(5);
635 
636 	mediumPreviews->AddChild(fIconPreview32Folder);
637 	mediumPreviews->AddChild(fIconPreview32Desktop);
638 
639 //	iconPreviews->AddChild(fIconPreview48);
640 
641 	iconPreviews->AddChild(smallPreviews);
642 	iconPreviews->AddChild(mediumPreviews);
643 	iconPreviews->AddChild(fIconPreview64);
644 	iconPreviews->SetExplicitMaxSize(BSize(B_SIZE_UNSET, B_SIZE_UNLIMITED));
645 
646 	leftTopView->AddChild(iconPreviews);
647 
648 
649 	BGroupView* leftSideView = new BGroupView(B_VERTICAL);
650 	layout->AddView(leftSideView, 0, 1);
651 	leftSideView->SetExplicitMinSize(BSize(splitWidth, B_SIZE_UNSET));
652 
653 	// path menu and list view
654 	BMenuBar* menuBar = new BMenuBar(bounds, "path menu bar");
655 	menuBar->AddItem(fPathMenu);
656 	leftSideView->AddChild(menuBar);
657 
658 	fPathListView = new PathListView(BRect(0, 0, splitWidth, 100),
659 									 "path list view",
660 									 new BMessage(MSG_PATH_SELECTED), this);
661 
662 	BView* scrollView = new BScrollView("path list scroll view",
663 										fPathListView,
664 										B_FOLLOW_NONE, 0, false, true,
665 										B_NO_BORDER);
666 	leftSideView->AddChild(scrollView);
667 
668 	// shape list view
669 	menuBar = new BMenuBar(bounds, "shape menu bar");
670 	menuBar->AddItem(fShapeMenu);
671 	leftSideView->AddChild(menuBar);
672 
673 	fShapeListView = new ShapeListView(BRect(0, 0, splitWidth, 100),
674 									   "shape list view",
675 									   new BMessage(MSG_SHAPE_SELECTED), this);
676 	scrollView = new BScrollView("shape list scroll view",
677 								 fShapeListView,
678 								 B_FOLLOW_NONE, 0, false, true,
679 								 B_NO_BORDER);
680 	leftSideView->AddChild(scrollView);
681 
682 	// transformer list view
683 	menuBar = new BMenuBar(bounds, "transformer menu bar");
684 	menuBar->AddItem(fTransformerMenu);
685 	leftSideView->AddChild(menuBar);
686 
687 	fTransformerListView = new TransformerListView(BRect(0, 0, splitWidth, 100),
688 												   "transformer list view");
689 	scrollView = new BScrollView("transformer list scroll view",
690 								 fTransformerListView,
691 								 B_FOLLOW_NONE, 0, false, true,
692 								 B_NO_BORDER);
693 	leftSideView->AddChild(scrollView);
694 
695 	// property list view
696 	menuBar = new BMenuBar(bounds, "property menu bar");
697 	menuBar->AddItem(fPropertyMenu);
698 	leftSideView->AddChild(menuBar);
699 
700 	fPropertyListView = new IconObjectListView();
701 
702 	// scroll view around property list view
703 	ScrollView* propScrollView = new ScrollView(fPropertyListView,
704 		SCROLL_VERTICAL, BRect(0, 0, splitWidth, 100), "property scroll view",
705 		B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS, B_PLAIN_BORDER,
706 		BORDER_RIGHT);
707 	leftSideView->AddChild(propScrollView);
708 
709 	BGroupLayout* topSide = new BGroupLayout(B_HORIZONTAL);
710 	BView* topSideView = new BView("top side view", 0, topSide);
711 	layout->AddView(topSideView, 1, 0);
712 
713 	// canvas view
714 	BRect canvasBounds = BRect(0, 0, 200, 200);
715 	fCanvasView = new CanvasView(canvasBounds);
716 
717 	// scroll view around canvas view
718 	canvasBounds.bottom += B_H_SCROLL_BAR_HEIGHT;
719 	canvasBounds.right += B_V_SCROLL_BAR_WIDTH;
720 	ScrollView* canvasScrollView
721 		= new ScrollView(fCanvasView, SCROLL_VERTICAL | SCROLL_HORIZONTAL
722 				| SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS,
723 				canvasBounds, "canvas scroll view",
724 				B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS, B_NO_BORDER);
725 	layout->AddView(canvasScrollView, 1, 1);
726 
727 	// views along the top
728 
729 	BGroupLayout* styleGroup = new BGroupLayout(B_VERTICAL);
730 	BView* styleGroupView = new BView("style group", 0, styleGroup);
731 	topSide->AddView(styleGroupView);
732 
733 	// style list view
734 	menuBar = new BMenuBar(bounds, "style menu bar");
735 	menuBar->AddItem(fStyleMenu);
736 	styleGroup->AddView(menuBar);
737 
738 	fStyleListView = new StyleListView(BRect(0, 0, splitWidth, 100),
739 									   "style list view",
740 									   new BMessage(MSG_STYLE_SELECTED), this);
741 	scrollView = new BScrollView("style list scroll view",
742 								 fStyleListView,
743 								 B_FOLLOW_NONE, 0, false, true,
744 								 B_NO_BORDER);
745 	scrollView->SetExplicitMaxSize(BSize(splitWidth, B_SIZE_UNLIMITED));
746 	styleGroup->AddView(scrollView);
747 
748 	// style view
749 	fStyleView = new StyleView(BRect(0, 0, 200, 100));
750 	topSide->AddView(fStyleView);
751 
752 	// swatch group
753 	BGroupLayout* swatchGroup = new BGroupLayout(B_VERTICAL);
754 	BView* swatchGroupView = new BView("swatch group", 0, swatchGroup);
755 	topSide->AddView(swatchGroupView);
756 
757 	menuBar = new BMenuBar(bounds, "swatches menu bar");
758 	menuBar->AddItem(fSwatchMenu);
759 	swatchGroup->AddView(menuBar);
760 
761 	fSwatchGroup = new SwatchGroup(BRect(0, 0, 100, 100));
762 	swatchGroup->AddView(fSwatchGroup);
763 
764 	swatchGroupView->SetExplicitMaxSize(swatchGroupView->MinSize());
765 
766 	// make sure the top side has fixed height
767 	topSideView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
768 		swatchGroupView->MinSize().height));
769 
770 #else // !__HAIKU__
771 
772 	BView* bg = new BView(bounds, "bg", B_FOLLOW_ALL, 0);
773 	bg->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
774 	AddChild(bg);
775 
776 	BRect menuFrame = bounds;
777 	menuFrame.bottom = menuFrame.top + 15;
778 	BMenuBar* menuBar = _CreateMenuBar(menuFrame);
779 	bg->AddChild(menuBar);
780 	float menuWidth;
781 	float menuHeight;
782 	menuBar->GetPreferredSize(&menuWidth, &menuHeight);
783 	menuBar->ResizeTo(splitWidth - 1, menuHeight);
784 	menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP);
785 
786 	bounds.top = menuBar->Frame().bottom + 1;
787 
788 	// swatch group
789 	fSwatchGroup = new SwatchGroup(bounds);
790 		// SwatchGroup auto resizes itself
791 	fSwatchGroup->MoveTo(bounds.right - fSwatchGroup->Bounds().Width(),
792 						 bounds.top);
793 	fSwatchGroup->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
794 
795 	bounds.left = fSwatchGroup->Frame().left;
796 	bounds.right = bg->Bounds().right;
797 	bounds.top = bg->Bounds().top;
798 	bounds.bottom = bounds.top + menuHeight;
799 	menuBar = new BMenuBar(bounds, "swatches menu bar");
800 	menuBar->AddItem(fSwatchMenu);
801 	bg->AddChild(menuBar);
802 	menuBar->ResizeTo(bounds.Width(), menuHeight);
803 		// menu bars resize themselves to window width
804 	menuBar->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
805 
806 	// canvas view
807 	bounds.left = splitWidth;
808 	bounds.top = fSwatchGroup->Frame().bottom + 1;
809 	bounds.right = bg->Bounds().right - B_V_SCROLL_BAR_WIDTH;
810 	bounds.bottom = bg->Bounds().bottom - B_H_SCROLL_BAR_HEIGHT;
811 	fCanvasView = new CanvasView(bounds);
812 
813 	// scroll view around canvas view
814 	bounds.bottom += B_H_SCROLL_BAR_HEIGHT;
815 	bounds.right += B_V_SCROLL_BAR_WIDTH;
816 	ScrollView* canvasScrollView
817 		= new ScrollView(fCanvasView, SCROLL_HORIZONTAL | SCROLL_VERTICAL
818 			| SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS, bounds,
819 			"canvas scroll view", B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS,
820 			B_NO_BORDER);
821 
822 	// icon previews
823 	bounds.left = 5;
824 	bounds.top = fSwatchGroup->Frame().top + 5;
825 	bounds.right = bounds.left + 15;
826 	bounds.bottom = bounds.top + 15;
827 	fIconPreview16Folder = new IconView(bounds, "icon preview 16 folder");
828 
829 	bounds.top = fIconPreview16Folder->Frame().bottom + 5;
830 	bounds.bottom = bounds.top + 15;
831 	fIconPreview16Menu = new IconView(bounds, "icon preview 16 menu");
832 	fIconPreview16Menu->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR));
833 
834 	bounds.left = fIconPreview16Folder->Frame().right + 5;
835 	bounds.top = fSwatchGroup->Frame().top + 5;
836 	bounds.right = bounds.left + 31;
837 	bounds.bottom = bounds.top + 31;
838 	fIconPreview32Folder = new IconView(bounds, "icon preview 32 folder");
839 
840 	bounds.top = fIconPreview32Folder->Frame().bottom + 5;
841 	bounds.bottom = bounds.top + 31;
842 	fIconPreview32Desktop = new IconView(bounds, "icon preview 32 desktop");
843 	fIconPreview32Desktop->SetLowColor(ui_color(B_DESKTOP_COLOR));
844 
845 //	bounds.OffsetBy(bounds.Width() + 6, 0);
846 //	bounds.right = bounds.left + 47;
847 //	bounds.bottom = bounds.top + 47;
848 //	fIconPreview48 = new IconView(bounds, "icon preview 48");
849 
850 	bounds.left = fIconPreview32Folder->Frame().right + 5;
851 	bounds.top = fSwatchGroup->Frame().top + 5;
852 	bounds.right = bounds.left + 63;
853 	bounds.bottom = bounds.top + 63;
854 	fIconPreview64 = new IconView(bounds, "icon preview 64");
855 	fIconPreview64->SetLowColor(ui_color(B_DESKTOP_COLOR));
856 
857 	// style list view
858 	bounds.left = fCanvasView->Frame().left;
859 	bounds.right = bounds.left + splitWidth;
860 	bounds.top = bg->Bounds().top;
861 	bounds.bottom = bounds.top + menuHeight;
862 	menuBar = new BMenuBar(bounds, "style menu bar");
863 	menuBar->AddItem(fStyleMenu);
864 	bg->AddChild(menuBar);
865 	menuBar->ResizeTo(bounds.Width(), menuHeight);
866 		// menu bars resize themselves to window width
867 	menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP);
868 
869 	bounds.right -= B_V_SCROLL_BAR_WIDTH + 1;
870 	bounds.top = menuBar->Frame().bottom + 1;
871 	bounds.bottom = fCanvasView->Frame().top - 1;
872 
873 	fStyleListView = new StyleListView(bounds, "style list view",
874 									   new BMessage(MSG_STYLE_SELECTED), this);
875 
876 
877 	// style view
878 	bounds.left = menuBar->Frame().right + 1;
879 	bounds.top = bg->Bounds().top;
880 	bounds.right = fSwatchGroup->Frame().left - 1;
881 	bounds.bottom = fCanvasView->Frame().top - 1;
882 	fStyleView = new StyleView(bounds);
883 	fStyleView->SetResizingMode(B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT);
884 	bg->AddChild(fStyleView);
885 
886 	// path list view
887 	bounds.left = 0;
888 	bounds.right = fCanvasView->Frame().left - 1;
889 	bounds.top = fCanvasView->Frame().top;
890 	bounds.bottom = bounds.top + menuHeight;
891 	menuBar = new BMenuBar(bounds, "path menu bar");
892 	menuBar->AddItem(fPathMenu);
893 	bg->AddChild(menuBar);
894 	menuBar->ResizeTo(bounds.Width(), menuHeight);
895 		// menu bars resize themselves to window width
896 	menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP);
897 
898 	bounds.right -= B_V_SCROLL_BAR_WIDTH + 1;
899 	bounds.top = menuBar->Frame().bottom + 1;
900 	bounds.bottom = bounds.top + 130;
901 
902 	fPathListView = new PathListView(bounds, "path list view",
903 									 new BMessage(MSG_PATH_SELECTED), this);
904 
905 
906 	// shape list view
907 	bounds.right += B_V_SCROLL_BAR_WIDTH + 1;
908 	bounds.top = fPathListView->Frame().bottom + 1;
909 	bounds.bottom = bounds.top + menuHeight;
910 	menuBar = new BMenuBar(bounds, "shape menu bar");
911 	menuBar->AddItem(fShapeMenu);
912 	bg->AddChild(menuBar);
913 	menuBar->ResizeTo(bounds.Width(), menuHeight);
914 		// menu bars resize themselves to window width
915 	menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP);
916 
917 	bounds.right -= B_V_SCROLL_BAR_WIDTH + 1;
918 	bounds.top = menuBar->Frame().bottom + 1;
919 	bounds.bottom = bounds.top + 130;
920 
921 	fShapeListView = new ShapeListView(bounds, "shape list view",
922 									   new BMessage(MSG_SHAPE_SELECTED), this);
923 
924 	// transformer list view
925 	bounds.right += B_V_SCROLL_BAR_WIDTH + 1;
926 	bounds.top = fShapeListView->Frame().bottom + 1;
927 	bounds.bottom = bounds.top + menuHeight;
928 	menuBar = new BMenuBar(bounds, "transformer menu bar");
929 	menuBar->AddItem(fTransformerMenu);
930 	bg->AddChild(menuBar);
931 	menuBar->ResizeTo(bounds.Width(), bounds.Height());
932 		// menu bars resize themselves to window width
933 	menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP);
934 
935 	bounds.right -= B_V_SCROLL_BAR_WIDTH + 1;
936 	bounds.top = menuBar->Frame().bottom + 1;
937 	bounds.bottom = bounds.top + 80;
938 	fTransformerListView = new TransformerListView(bounds,
939 												   "transformer list view");
940 
941 	// property list view
942 	bounds.right += B_V_SCROLL_BAR_WIDTH + 1;
943 	bounds.top = fTransformerListView->Frame().bottom + 1;
944 	bounds.bottom = bounds.top + menuHeight;
945 	menuBar = new BMenuBar(bounds, "property menu bar");
946 	menuBar->AddItem(fPropertyMenu);
947 	bg->AddChild(menuBar);
948 	menuBar->ResizeTo(bounds.Width(), bounds.Height());
949 		// menu bars resize themselves to window width
950 	menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP);
951 
952 	fPropertyListView = new IconObjectListView();
953 
954 	bg->AddChild(fSwatchGroup);
955 
956 	bg->AddChild(fIconPreview16Folder);
957 	bg->AddChild(fIconPreview16Menu);
958 	bg->AddChild(fIconPreview32Folder);
959 	bg->AddChild(fIconPreview32Desktop);
960 //	bg->AddChild(fIconPreview48);
961 	bg->AddChild(fIconPreview64);
962 
963 	bg->AddChild(new BScrollView("path list scroll view",
964 								 fPathListView,
965 								 B_FOLLOW_LEFT | B_FOLLOW_TOP,
966 								 0, false, true,
967 								 B_NO_BORDER));
968 	bg->AddChild(new BScrollView("style list scroll view",
969 								 fStyleListView,
970 								 B_FOLLOW_LEFT | B_FOLLOW_TOP,
971 								 0, false, true,
972 								 B_NO_BORDER));
973 	bg->AddChild(new BScrollView("shape list scroll view",
974 								 fShapeListView,
975 								 B_FOLLOW_LEFT | B_FOLLOW_TOP,
976 								 0, false, true,
977 								 B_NO_BORDER));
978 	bg->AddChild(new BScrollView("transformer list scroll view",
979 								 fTransformerListView,
980 								 B_FOLLOW_LEFT | B_FOLLOW_TOP,
981 								 0, false, true,
982 								 B_NO_BORDER));
983 
984 	// scroll view around property list view
985 	bounds.top = menuBar->Frame().bottom + 1;
986 	bounds.bottom = bg->Bounds().bottom;
987 	bg->AddChild(new ScrollView(fPropertyListView, SCROLL_VERTICAL,
988 		bounds, "property scroll view", B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM,
989 		B_WILL_DRAW | B_FRAME_EVENTS, B_PLAIN_BORDER, BORDER_RIGHT));
990 
991 
992 	bg->AddChild(canvasScrollView);
993 #endif // __HAIKU__
994 }
995 
996 // _CreateMenuBar
997 BMenuBar*
998 MainWindow::_CreateMenuBar(BRect frame)
999 {
1000 	BMenuBar* menuBar = new BMenuBar(frame, "main menu");
1001 
1002 	BMenu* fileMenu = new BMenu("File");
1003 	BMenu* editMenu = new BMenu("Edit");
1004 	BMenu* settingsMenu = new BMenu("Options");
1005 	fPathMenu = new BMenu("Path");
1006 	fStyleMenu = new BMenu("Style");
1007 	fShapeMenu = new BMenu("Shape");
1008 	fTransformerMenu = new BMenu("Transformer");
1009 	fPropertyMenu = new BMenu("Properties");
1010 	fSwatchMenu = new BMenu("Swatches");
1011 
1012 	menuBar->AddItem(fileMenu);
1013 	menuBar->AddItem(editMenu);
1014 	menuBar->AddItem(settingsMenu);
1015 
1016 	// File
1017 	fileMenu->AddItem(new BMenuItem("New",
1018 		new BMessage(MSG_NEW), 'N'));
1019 	fileMenu->AddItem(new BMenuItem("Open"B_UTF8_ELLIPSIS,
1020 		new BMessage(MSG_OPEN), 'O'));
1021 	fileMenu->AddItem(new BMenuItem("Append"B_UTF8_ELLIPSIS,
1022 		new BMessage(MSG_APPEND), 'O', B_SHIFT_KEY));
1023 	fileMenu->AddSeparatorItem();
1024 	fileMenu->AddItem(new BMenuItem("Save",
1025 		new BMessage(MSG_SAVE), 'S'));
1026 	fileMenu->AddItem(new BMenuItem("Save As"B_UTF8_ELLIPSIS,
1027 		new BMessage(MSG_SAVE_AS), 'S', B_SHIFT_KEY));
1028 	fileMenu->AddSeparatorItem();
1029 	fileMenu->AddItem(new BMenuItem("Export",
1030 		new BMessage(MSG_EXPORT), 'E'));
1031 	fileMenu->AddItem(new BMenuItem("Export As"B_UTF8_ELLIPSIS,
1032 		new BMessage(MSG_EXPORT_AS), 'E', B_SHIFT_KEY));
1033 	fileMenu->AddSeparatorItem();
1034 	fileMenu->AddItem(new BMenuItem("Quit",
1035 		new BMessage(B_QUIT_REQUESTED), 'Q'));
1036 	fileMenu->SetTargetForItems(be_app);
1037 
1038 	// Edit
1039 	fUndoMI = new BMenuItem("<nothing to undo>",
1040 		new BMessage(MSG_UNDO), 'Z');
1041 	fRedoMI = new BMenuItem("<nothing to redo>",
1042 		new BMessage(MSG_REDO), 'Z', B_SHIFT_KEY);
1043 
1044 	fUndoMI->SetEnabled(false);
1045 	fRedoMI->SetEnabled(false);
1046 
1047 	editMenu->AddItem(fUndoMI);
1048 	editMenu->AddItem(fRedoMI);
1049 
1050 	// Settings
1051 	BMenu* filterModeMenu = new BMenu("Snap to Grid");
1052 	BMessage* message = new BMessage(MSG_MOUSE_FILTER_MODE);
1053 	message->AddInt32("mode", SNAPPING_OFF);
1054 	filterModeMenu->AddItem(new BMenuItem("Off", message, '4'));
1055 
1056 	message = new BMessage(MSG_MOUSE_FILTER_MODE);
1057 	message->AddInt32("mode", SNAPPING_64);
1058 	filterModeMenu->AddItem(new BMenuItem("64 x 64", message, '3'));
1059 
1060 	message = new BMessage(MSG_MOUSE_FILTER_MODE);
1061 	message->AddInt32("mode", SNAPPING_32);
1062 	filterModeMenu->AddItem(new BMenuItem("32 x 32", message, '2'));
1063 
1064 	message = new BMessage(MSG_MOUSE_FILTER_MODE);
1065 	message->AddInt32("mode", SNAPPING_16);
1066 	filterModeMenu->AddItem(new BMenuItem("16 x 16", message, '1'));
1067 
1068 	filterModeMenu->ItemAt(1)->SetMarked(true);
1069 	filterModeMenu->SetRadioMode(true);
1070 
1071 	settingsMenu->AddItem(filterModeMenu);
1072 
1073 	return menuBar;
1074 }
1075 
1076 //// _CreateDefaultIcon
1077 //void
1078 //MainWindow::_CreateDefaultIcon()
1079 //{
1080 //	// add some stuff to an empty document (NOTE: for testing only)
1081 //	VectorPath* path = new VectorPath();
1082 //
1083 //	fDocument->Icon()->Paths()->AddPath(path);
1084 //
1085 //	Style* style1 = new Style();
1086 //	style1->SetName("Style White");
1087 //	style1->SetColor((rgb_color){ 255, 255, 255, 255 });
1088 //
1089 //	fDocument->Icon()->Styles()->AddStyle(style1);
1090 //
1091 //	Style* style2 = new Style();
1092 //	style2->SetName("Style Gradient");
1093 //	Gradient gradient(true);
1094 //	gradient.AddColor((rgb_color){ 255, 211, 6, 255 }, 0.0);
1095 //	gradient.AddColor((rgb_color){ 255, 238, 160, 255 }, 0.5);
1096 //	gradient.AddColor((rgb_color){ 208, 43, 92, 255 }, 1.0);
1097 //	style2->SetGradient(&gradient);
1098 //
1099 //	fDocument->Icon()->Styles()->AddStyle(style2);
1100 //
1101 //	Shape* shape = new Shape(style2);
1102 //	shape->Paths()->AddPath(path);
1103 //
1104 //	shape->SetName("Gradient");
1105 //	fDocument->Icon()->Shapes()->AddShape(shape);
1106 //
1107 //	shape = new Shape(style1);
1108 //	shape->Paths()->AddPath(path);
1109 //	StrokeTransformer* transformer
1110 //		= new StrokeTransformer(shape->VertexSource());
1111 //	transformer->width(5.0);
1112 //	shape->AddTransformer(transformer);
1113 //
1114 //	shape->SetName("Outline");
1115 //	fDocument->Icon()->Shapes()->AddShape(shape);
1116 //
1117 //	Style* style3 = new Style();
1118 //	style3->SetName("Style Red");
1119 //	style3->SetColor((rgb_color){ 255, 0, 169,200 });
1120 //
1121 //	fDocument->Icon()->Styles()->AddStyle(style3);
1122 //
1123 //	shape = new Shape(style3);
1124 //	shape->Paths()->AddPath(path);
1125 //	AffineTransformer* transformer2
1126 //		= new AffineTransformer(shape->VertexSource());
1127 //	*transformer2 *= agg::trans_affine_translation(10.0, 6.0);
1128 //	*transformer2 *= agg::trans_affine_rotation(0.2);
1129 //	*transformer2 *= agg::trans_affine_scaling(0.8, 0.6);
1130 //	shape->AddTransformer(transformer2);
1131 //
1132 //	shape->SetName("Transformed");
1133 //	fDocument->Icon()->Shapes()->AddShape(shape);
1134 //
1135 //	PathManipulator* pathManipulator = new PathManipulator(path);
1136 //	fState->AddManipulator(pathManipulator);
1137 //}
1138 
1139 // _ImproveScrollBarLayout
1140 void
1141 MainWindow::_ImproveScrollBarLayout(BView* target)
1142 {
1143 	// NOTE: The BListViews for which this function is used
1144 	// are directly below a BMenuBar. If the BScrollBar and
1145 	// the BMenuBar share bottom/top border respectively, the
1146 	// GUI looks a little more polished. This trick can be
1147 	// removed if/when the BScrollViews are embedded in a
1148 	// surounding border like in WonderBrush.
1149 
1150 	if (BScrollBar* scrollBar = target->ScrollBar(B_VERTICAL)) {
1151 		scrollBar->MoveBy(0, -1);
1152 		scrollBar->ResizeBy(0, 1);
1153 	}
1154 }
1155