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