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