1 /* 2 * Copyright 2006, 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 <ScrollView.h> 19 20 #include "AddPathsCommand.h" 21 #include "AddShapesCommand.h" 22 #include "AddStylesCommand.h" 23 #include "Document.h" 24 #include "CanvasView.h" 25 #include "CommandStack.h" 26 #include "CurrentColor.h" 27 #include "IconObjectListView.h" 28 #include "IconEditorApp.h" 29 #include "IconView.h" 30 #include "PathListView.h" 31 #include "ScrollView.h" 32 #include "ShapeListView.h" 33 #include "StyleListView.h" 34 #include "StyleView.h" 35 #include "SwatchGroup.h" 36 #include "TransformerFactory.h" 37 #include "TransformerListView.h" 38 #include "TransformShapesBox.h" 39 40 // TODO: just for testing 41 #include "AffineTransformer.h" 42 #include "Gradient.h" 43 #include "Icon.h" 44 #include "MultipleManipulatorState.h" 45 #include "PathManipulator.h" 46 #include "Shape.h" 47 #include "ShapeContainer.h" 48 #include "ShapeListView.h" 49 #include "StrokeTransformer.h" 50 #include "Style.h" 51 #include "StyleManager.h" 52 #include "VectorPath.h" 53 54 using std::nothrow; 55 56 enum { 57 MSG_UNDO = 'undo', 58 MSG_REDO = 'redo', 59 60 MSG_NEW_PATH = 'nwvp', 61 MSG_PATH_SELECTED = 'vpsl', 62 MSG_NEW_STYLE = 'nwst', 63 MSG_STYLE_SELECTED = 'stsl', 64 MSG_NEW_SHAPE = 'nwsp', 65 MSG_SHAPE_SELECTED = 'spsl', 66 67 MSG_ADD_TRANSFORMER = 'adtr', 68 }; 69 70 // constructor 71 MainWindow::MainWindow(IconEditorApp* app, Document* document) 72 : BWindow(BRect(50, 50, 900, 750), "Icon-O-Matic", 73 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 74 B_ASYNCHRONOUS_CONTROLS), 75 fApp(app), 76 fDocument(document) 77 { 78 _Init(); 79 } 80 81 // destructor 82 MainWindow::~MainWindow() 83 { 84 delete fState; 85 86 fDocument->CommandStack()->RemoveObserver(this); 87 } 88 89 // #pragma mark - 90 91 // MessageReceived 92 void 93 MainWindow::MessageReceived(BMessage* message) 94 { 95 if (!fDocument->WriteLock()) 96 return; 97 98 switch (message->what) { 99 100 case MSG_UNDO: 101 fDocument->CommandStack()->Undo(); 102 break; 103 case MSG_REDO: 104 fDocument->CommandStack()->Redo(); 105 break; 106 107 // TODO: listen to selection in CanvasView to add a manipulator 108 case MSG_NEW_PATH: { 109 VectorPath* path = new (nothrow) VectorPath(); 110 VectorPath* paths[1]; 111 paths[0] = path; 112 PathContainer* container = fDocument->Icon()->Paths(); 113 AddPathsCommand* command = new (nothrow) AddPathsCommand( 114 container, paths, 1, true, 115 container->CountPaths()); 116 fDocument->CommandStack()->Perform(command); 117 break; 118 } 119 case MSG_PATH_SELECTED: { 120 VectorPath* path; 121 if (message->FindPointer("path", (void**)&path) < B_OK) 122 path = NULL; 123 124 fState->DeleteManipulators(); 125 if (path) { 126 PathManipulator* pathManipulator = new (nothrow) PathManipulator(path); 127 fState->AddManipulator(pathManipulator); 128 } 129 break; 130 } 131 case MSG_NEW_STYLE: { 132 Style* style = new (nothrow) Style(); 133 if (style) { 134 style->SetColor((rgb_color){ rand() % 255, 135 rand() % 255, 136 rand() % 255, 137 255 }); 138 Style* styles[1]; 139 styles[0] = style; 140 StyleManager* container = StyleManager::Default(); 141 AddStylesCommand* command = new (nothrow) AddStylesCommand( 142 container, styles, 1, 143 container->CountStyles()); 144 fDocument->CommandStack()->Perform(command); 145 } 146 break; 147 } 148 case MSG_STYLE_SELECTED: { 149 Style* style; 150 if (message->FindPointer("style", (void**)&style) < B_OK) 151 style = NULL; 152 fStyleView->SetStyle(style); 153 break; 154 } 155 case MSG_NEW_SHAPE: { 156 Shape* shape = new (nothrow) Shape(StyleManager::Default()->StyleAt(0)); 157 Shape* shapes[1]; 158 shapes[0] = shape; 159 AddShapesCommand* command = new (nothrow) AddShapesCommand( 160 fDocument->Icon()->Shapes(), shapes, 1, 161 fDocument->Icon()->Shapes()->CountShapes()); 162 fDocument->CommandStack()->Perform(command); 163 break; 164 } 165 case MSG_SHAPE_SELECTED: { 166 Shape* shape; 167 if (message->FindPointer("shape", (void**)&shape) < B_OK) 168 shape = NULL; 169 fPathListView->SetCurrentShape(shape); 170 fStyleListView->SetCurrentShape(shape); 171 fTransformerListView->SetShape(shape); 172 173 BList selectedShapes; 174 ShapeContainer* shapes = fDocument->Icon()->Shapes(); 175 int32 count = shapes->CountShapes(); 176 for (int32 i = 0; i < count; i++) { 177 shape = shapes->ShapeAtFast(i); 178 if (shape->IsSelected()) { 179 selectedShapes.AddItem((void*)shape); 180 } 181 } 182 183 if (selectedShapes.CountItems() > 0) { 184 TransformShapesBox* transformBox = new (nothrow) TransformShapesBox( 185 fCanvasView, 186 (const Shape**)selectedShapes.Items(), 187 selectedShapes.CountItems()); 188 fState->DeleteManipulators(); 189 fState->AddManipulator(transformBox); 190 } 191 break; 192 } 193 194 case MSG_ADD_TRANSFORMER: { 195 Shape* shape = fPathListView->CurrentShape(); 196 if (!shape) 197 break; 198 199 uint32 type; 200 if (message->FindInt32("type", (int32*)&type) < B_OK) 201 break; 202 203 Transformer* transformer 204 = TransformerFactory::TransformerFor(type, 205 shape->VertexSource()); 206 // TODO: command 207 if (transformer) 208 shape->AddTransformer(transformer); 209 break; 210 } 211 212 default: 213 BWindow::MessageReceived(message); 214 } 215 216 fDocument->WriteUnlock(); 217 } 218 219 // QuitRequested 220 bool 221 MainWindow::QuitRequested() 222 { 223 // forward this to app but return "false" in order 224 // to have a single code path for quitting 225 be_app->PostMessage(B_QUIT_REQUESTED); 226 227 return false; 228 } 229 230 // #pragma mark - 231 // ObjectChanged 232 void 233 MainWindow::ObjectChanged(const Observable* object) 234 { 235 if (!fDocument) 236 return; 237 238 if (!Lock()) 239 return; 240 241 if (object == fDocument->CommandStack()) { 242 // relable Undo item and update enabled status 243 BString label("Undo"); 244 fUndoMI->SetEnabled(fDocument->CommandStack()->GetUndoName(label)); 245 if (fUndoMI->IsEnabled()) 246 fUndoMI->SetLabel(label.String()); 247 else 248 fUndoMI->SetLabel("<nothing to undo>"); 249 250 // relable Redo item and update enabled status 251 label.SetTo("Redo"); 252 fRedoMI->SetEnabled(fDocument->CommandStack()->GetRedoName(label)); 253 if (fRedoMI->IsEnabled()) 254 fRedoMI->SetLabel(label.String()); 255 else 256 fRedoMI->SetLabel("<nothing to redo>"); 257 } 258 259 Unlock(); 260 } 261 262 // #pragma mark - 263 264 // _Init 265 void 266 MainWindow::_Init() 267 { 268 // create the GUI 269 _CreateGUI(Bounds()); 270 271 fCanvasView->SetCatchAllEvents(true); 272 fCanvasView->SetCommandStack(fDocument->CommandStack()); 273 // fCanvasView->SetSelection(fDocument->Selection()); 274 fCanvasView->SetIcon(fDocument->Icon()); 275 276 fPathListView->SetPathContainer(fDocument->Icon()->Paths()); 277 fPathListView->SetShapeContainer(fDocument->Icon()->Shapes()); 278 fPathListView->SetCommandStack(fDocument->CommandStack()); 279 fPathListView->SetSelection(fDocument->Selection()); 280 281 fStyleListView->SetStyleManager(StyleManager::Default()); 282 fStyleListView->SetShapeContainer(fDocument->Icon()->Shapes()); 283 // fStyleListView->SetCommandStack(fDocument->CommandStack()); 284 fStyleListView->SetSelection(fDocument->Selection()); 285 286 fStyleView->SetCommandStack(fDocument->CommandStack()); 287 fStyleView->SetCurrentColor(CurrentColor::Default()); 288 289 fShapeListView->SetShapeContainer(fDocument->Icon()->Shapes()); 290 fShapeListView->SetCommandStack(fDocument->CommandStack()); 291 fShapeListView->SetSelection(fDocument->Selection()); 292 293 fTransformerListView->SetCommandStack(fDocument->CommandStack()); 294 fTransformerListView->SetSelection(fDocument->Selection()); 295 296 fPropertyListView->SetCommandStack(fDocument->CommandStack()); 297 fPropertyListView->SetSelection(fDocument->Selection()); 298 fPropertyListView->SetMenu(fPropertyMenu); 299 300 fIconPreview16->SetIcon(fDocument->Icon()); 301 fIconPreview32->SetIcon(fDocument->Icon()); 302 // fIconPreview48->SetIcon(fDocument->Icon()); 303 fIconPreview64->SetIcon(fDocument->Icon()); 304 305 fDocument->CommandStack()->AddObserver(this); 306 307 fSwatchGroup->SetCurrentColor(CurrentColor::Default()); 308 309 // TODO: for testing only: 310 MultipleManipulatorState* state = new MultipleManipulatorState(fCanvasView); 311 fCanvasView->SetState(state); 312 313 VectorPath* path = new VectorPath(); 314 315 fDocument->Icon()->Paths()->AddPath(path); 316 317 Style* style1 = new Style(); 318 style1->SetName("Style White"); 319 style1->SetColor((rgb_color){ 255, 255, 255, 255 }); 320 321 StyleManager::Default()->AddStyle(style1); 322 323 Style* style2 = new Style(); 324 style2->SetName("Style Gradient"); 325 Gradient gradient(true); 326 gradient.AddColor((rgb_color){ 255, 211, 6, 255 }, 0.0); 327 gradient.AddColor((rgb_color){ 255, 238, 160, 255 }, 0.5); 328 gradient.AddColor((rgb_color){ 208, 43, 92, 255 }, 1.0); 329 style2->SetGradient(&gradient); 330 331 StyleManager::Default()->AddStyle(style2); 332 333 Shape* shape = new Shape(style2); 334 shape->Paths()->AddPath(path); 335 336 shape->SetName("Gradient"); 337 fDocument->Icon()->Shapes()->AddShape(shape); 338 339 shape = new Shape(style1); 340 shape->Paths()->AddPath(path); 341 StrokeTransformer* transformer 342 = new StrokeTransformer(shape->VertexSource()); 343 transformer->width(5.0); 344 shape->AddTransformer(transformer); 345 346 shape->SetName("Outline"); 347 fDocument->Icon()->Shapes()->AddShape(shape); 348 349 Style* style3 = new Style(); 350 style3->SetName("Style Red"); 351 style3->SetColor((rgb_color){ 255, 0, 169,200 }); 352 353 StyleManager::Default()->AddStyle(style3); 354 355 shape = new Shape(style3); 356 shape->Paths()->AddPath(path); 357 AffineTransformer* transformer2 358 = new AffineTransformer(shape->VertexSource()); 359 *transformer2 *= agg::trans_affine_translation(10.0, 6.0); 360 *transformer2 *= agg::trans_affine_rotation(0.2); 361 *transformer2 *= agg::trans_affine_scaling(0.8, 0.6); 362 shape->AddTransformer(transformer2); 363 364 shape->SetName("Transformed"); 365 fDocument->Icon()->Shapes()->AddShape(shape); 366 367 PathManipulator* pathManipulator = new PathManipulator(path); 368 state->AddManipulator(pathManipulator); 369 fState = state; 370 // ---- 371 } 372 373 // _CreateGUI 374 void 375 MainWindow::_CreateGUI(BRect bounds) 376 { 377 const float splitWidth = 150; 378 379 BView* bg = new BView(bounds, "bg", B_FOLLOW_ALL, 0); 380 bg->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 381 AddChild(bg); 382 383 BRect menuFrame = bounds; 384 menuFrame.bottom = menuFrame.top + 15; 385 BMenuBar* menuBar = _CreateMenuBar(menuFrame); 386 bg->AddChild(menuBar); 387 float menuWidth; 388 float menuHeight; 389 menuBar->GetPreferredSize(&menuWidth, &menuHeight); 390 menuBar->ResizeTo(splitWidth - 1, menuHeight); 391 menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP); 392 393 bounds.top = menuBar->Frame().bottom + 1; 394 395 // swatch group 396 fSwatchGroup = new SwatchGroup(bounds); 397 // SwatchGroup auto resizes itself 398 fSwatchGroup->MoveTo(bounds.right - fSwatchGroup->Bounds().Width(), 399 bounds.top); 400 fSwatchGroup->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 401 402 bounds.left = fSwatchGroup->Frame().left; 403 bounds.right = bg->Bounds().right; 404 bounds.top = bg->Bounds().top; 405 bounds.bottom = bounds.top + menuHeight; 406 menuBar = new BMenuBar(bounds, "swatches menu bar"); 407 menuBar->AddItem(fSwatchMenu); 408 bg->AddChild(menuBar); 409 menuBar->ResizeTo(bounds.Width(), menuHeight); 410 // menu bars resize themselves to window width 411 menuBar->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 412 413 // canvas view 414 bounds.left = splitWidth; 415 bounds.top = fSwatchGroup->Frame().bottom + 1; 416 bounds.right = bg->Bounds().right; 417 bounds.bottom = bg->Bounds().bottom; 418 fCanvasView = new CanvasView(bounds); 419 420 // icon previews 421 bounds.left = 5; 422 bounds.top = fSwatchGroup->Frame().top + 5; 423 bounds.right = bounds.left + 15; 424 bounds.bottom = bounds.top + 15; 425 fIconPreview16 = new IconView(bounds, "icon preview 16"); 426 427 bounds.OffsetBy(bounds.Width() + 6, 0); 428 bounds.right = bounds.left + 31; 429 bounds.bottom = bounds.top + 31; 430 fIconPreview32 = new IconView(bounds, "icon preview 32"); 431 432 // bounds.OffsetBy(bounds.Width() + 6, 0); 433 // bounds.right = bounds.left + 47; 434 // bounds.bottom = bounds.top + 47; 435 // fIconPreview48 = new IconView(bounds, "icon preview 48"); 436 437 bounds.OffsetBy(bounds.Width() + 6, 0); 438 bounds.right = bounds.left + 63; 439 bounds.bottom = bounds.top + 63; 440 fIconPreview64 = new IconView(bounds, "icon preview 64"); 441 442 // style list view 443 bounds.left = fCanvasView->Frame().left; 444 bounds.right = bounds.left + splitWidth; 445 bounds.top = bg->Bounds().top; 446 bounds.bottom = bounds.top + menuHeight; 447 menuBar = new BMenuBar(bounds, "style menu bar"); 448 menuBar->AddItem(fStyleMenu); 449 bg->AddChild(menuBar); 450 menuBar->ResizeTo(bounds.Width(), menuHeight); 451 // menu bars resize themselves to window width 452 menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP); 453 454 bounds.right -= B_V_SCROLL_BAR_WIDTH + 1; 455 bounds.top = menuBar->Frame().bottom + 1; 456 bounds.bottom = fCanvasView->Frame().top - 1; 457 458 fStyleListView = new StyleListView(bounds, "style list view", 459 new BMessage(MSG_STYLE_SELECTED), this); 460 461 462 // style view 463 bounds.left = menuBar->Frame().right + 1; 464 bounds.top = bg->Bounds().top; 465 bounds.right = fSwatchGroup->Frame().left - 1; 466 bounds.bottom = fCanvasView->Frame().top - 1; 467 fStyleView = new StyleView(bounds); 468 bg->AddChild(fStyleView); 469 470 // path list view 471 bounds.left = 0; 472 bounds.right = fCanvasView->Frame().left - 1; 473 bounds.top = fCanvasView->Frame().top; 474 bounds.bottom = bounds.top + menuHeight; 475 menuBar = new BMenuBar(bounds, "path menu bar"); 476 menuBar->AddItem(fPathMenu); 477 bg->AddChild(menuBar); 478 menuBar->ResizeTo(bounds.Width(), menuHeight); 479 // menu bars resize themselves to window width 480 menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP); 481 482 bounds.right -= B_V_SCROLL_BAR_WIDTH + 1; 483 bounds.top = menuBar->Frame().bottom + 1; 484 bounds.bottom = bounds.top + 130; 485 486 fPathListView = new PathListView(bounds, "path list view", 487 new BMessage(MSG_PATH_SELECTED), this); 488 489 490 // shape list view 491 bounds.right += B_V_SCROLL_BAR_WIDTH + 1; 492 bounds.top = fPathListView->Frame().bottom + 1; 493 bounds.bottom = bounds.top + menuHeight; 494 menuBar = new BMenuBar(bounds, "shape menu bar"); 495 menuBar->AddItem(fShapeMenu); 496 bg->AddChild(menuBar); 497 menuBar->ResizeTo(bounds.Width(), menuHeight); 498 // menu bars resize themselves to window width 499 menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP); 500 501 bounds.right -= B_V_SCROLL_BAR_WIDTH + 1; 502 bounds.top = menuBar->Frame().bottom + 1; 503 bounds.bottom = bounds.top + 130; 504 505 fShapeListView = new ShapeListView(bounds, "shape list view", 506 new BMessage(MSG_SHAPE_SELECTED), this); 507 508 // transformer list view 509 bounds.right += B_V_SCROLL_BAR_WIDTH + 1; 510 bounds.top = fShapeListView->Frame().bottom + 1; 511 bounds.bottom = bounds.top + menuHeight; 512 menuBar = new BMenuBar(bounds, "transformer menu bar"); 513 menuBar->AddItem(fTransformerMenu); 514 bg->AddChild(menuBar); 515 menuBar->ResizeTo(bounds.Width(), bounds.Height()); 516 // menu bars resize themselves to window width 517 menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP); 518 519 bounds.right -= B_V_SCROLL_BAR_WIDTH + 1; 520 bounds.top = menuBar->Frame().bottom + 1; 521 bounds.bottom = bounds.top + 80; 522 fTransformerListView = new TransformerListView(bounds, 523 "transformer list view"); 524 525 // property list view 526 bounds.right += B_V_SCROLL_BAR_WIDTH + 1; 527 bounds.top = fTransformerListView->Frame().bottom + 1; 528 bounds.bottom = bounds.top + menuHeight; 529 menuBar = new BMenuBar(bounds, "property menu bar"); 530 menuBar->AddItem(fPropertyMenu); 531 bg->AddChild(menuBar); 532 menuBar->ResizeTo(bounds.Width(), bounds.Height()); 533 // menu bars resize themselves to window width 534 menuBar->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP); 535 536 fPropertyListView = new IconObjectListView(); 537 538 bg->AddChild(fSwatchGroup); 539 540 bg->AddChild(fIconPreview16); 541 bg->AddChild(fIconPreview32); 542 // bg->AddChild(fIconPreview48); 543 bg->AddChild(fIconPreview64); 544 545 bg->AddChild(new BScrollView("path list scroll view", 546 fPathListView, 547 B_FOLLOW_LEFT | B_FOLLOW_TOP, 548 0, false, true, 549 B_NO_BORDER)); 550 bg->AddChild(new BScrollView("style list scroll view", 551 fStyleListView, 552 B_FOLLOW_LEFT | B_FOLLOW_TOP, 553 0, false, true, 554 B_NO_BORDER)); 555 bg->AddChild(new BScrollView("shape list scroll view", 556 fShapeListView, 557 B_FOLLOW_LEFT | B_FOLLOW_TOP, 558 0, false, true, 559 B_NO_BORDER)); 560 bg->AddChild(new BScrollView("transformer list scroll view", 561 fTransformerListView, 562 B_FOLLOW_LEFT | B_FOLLOW_TOP, 563 0, false, true, 564 B_NO_BORDER)); 565 566 // scroll view around property list view 567 bounds.top = menuBar->Frame().bottom + 1; 568 bounds.bottom = bg->Bounds().bottom; 569 bg->AddChild(new ScrollView(fPropertyListView, 570 SCROLL_VERTICAL | SCROLL_NO_FRAME, 571 bounds, "property scroll view", 572 B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM, 573 B_WILL_DRAW | B_FRAME_EVENTS)); 574 575 576 bg->AddChild(fCanvasView); 577 } 578 579 // _CreateMenuBar 580 BMenuBar* 581 MainWindow::_CreateMenuBar(BRect frame) 582 { 583 BMenuBar* menuBar = new BMenuBar(frame, "main menu"); 584 585 BMenu* fileMenu = new BMenu("File"); 586 BMenu* editMenu = new BMenu("Edit"); 587 fPathMenu = new BMenu("Path"); 588 fStyleMenu = new BMenu("Style"); 589 fShapeMenu = new BMenu("Shape"); 590 fTransformerMenu = new BMenu("Transformer"); 591 fPropertyMenu = new BMenu("Property"); 592 fSwatchMenu = new BMenu("Swatches"); 593 594 menuBar->AddItem(fileMenu); 595 menuBar->AddItem(editMenu); 596 597 // Edit 598 fUndoMI = new BMenuItem("<nothing to undo>", 599 new BMessage(MSG_UNDO), 'Z'); 600 fRedoMI = new BMenuItem("<nothing to redo>", 601 new BMessage(MSG_REDO), 'Z', B_SHIFT_KEY); 602 603 fUndoMI->SetEnabled(false); 604 fRedoMI->SetEnabled(false); 605 606 editMenu->AddItem(fUndoMI); 607 editMenu->AddItem(fRedoMI); 608 609 // Path 610 fPathMenu->AddItem(new BMenuItem("Add", new BMessage(MSG_NEW_PATH))); 611 612 // Style 613 fStyleMenu->AddItem(new BMenuItem("Add", new BMessage(MSG_NEW_STYLE))); 614 615 // Shape 616 fShapeMenu->AddItem(new BMenuItem("Add", new BMessage(MSG_NEW_SHAPE))); 617 618 619 // Transformer 620 BMenu* addMenu = new BMenu("Add"); 621 int32 cookie = 0; 622 uint32 type; 623 BString name; 624 while (TransformerFactory::NextType(&cookie, &type, &name)) { 625 BMessage* message = new BMessage(MSG_ADD_TRANSFORMER); 626 message->AddInt32("type", type); 627 addMenu->AddItem(new BMenuItem(name.String(), message)); 628 } 629 addMenu->SetTargetForItems(this); 630 fTransformerMenu->AddItem(addMenu); 631 632 return menuBar; 633 } 634 635