1 // main.cpp 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 #include <Application.h> 7 #include <Alert.h> 8 #include <Bitmap.h> 9 #include <Box.h> 10 #include <Button.h> 11 #include <CheckBox.h> 12 #include <ColorControl.h> 13 #include <ListItem.h> 14 #include <ListView.h> 15 #include <Menu.h> 16 #include <MenuBar.h> 17 #include <MenuField.h> 18 #include <MenuItem.h> 19 #include <PopUpMenu.h> 20 #include <ScrollBar.h> 21 #include <ScrollView.h> 22 #include <Slider.h> 23 #include <String.h> 24 #include <RadioButton.h> 25 #include <Region.h> 26 #include <TabView.h> 27 #include <TextControl.h> 28 #include <TextView.h> 29 30 #include "ObjectView.h" 31 #include "States.h" 32 //#include "StatusView.h" 33 34 #include "ObjectWindow.h" 35 36 enum { 37 MSG_SET_OBJECT_TYPE = 'stot', 38 MSG_SET_FILL_OR_STROKE = 'stfs', 39 MSG_SET_COLOR = 'stcl', 40 MSG_SET_PEN_SIZE = 'stps', 41 MSG_SET_DRAWING_MODE = 'stdm', 42 43 MSG_NEW_OBJECT = 'nobj', 44 45 MSG_UNDO = 'undo', 46 MSG_REDO = 'redo', 47 48 MSG_CLEAR = 'clir', 49 50 MSG_OBJECT_SELECTED = 'obsl', 51 MSG_REMOVE_OBJECT = 'rmob', 52 }; 53 54 // ObjectItem 55 class ObjectItem : public BStringItem { 56 public: 57 ObjectItem(const char* name, State* object) 58 : BStringItem(name), 59 fObject(object) 60 { 61 } 62 63 State* Object() const 64 { return fObject; } 65 66 private: 67 State* fObject; 68 }; 69 70 // ObjectListView 71 class ObjectListView : public BListView { 72 public: 73 ObjectListView(BRect frame, const char* name, list_view_type listType) 74 : BListView(frame, name, listType) 75 { 76 } 77 78 virtual void KeyDown(const char* bytes, int32 numBytes) 79 { 80 switch (*bytes) { 81 case B_DELETE: 82 Window()->PostMessage(MSG_REMOVE_OBJECT); 83 break; 84 default: 85 BListView::KeyDown(bytes, numBytes); 86 } 87 } 88 89 virtual bool InitiateDrag(BPoint point, int32 itemIndex, bool wasSelected) 90 { 91 printf("InitiateDrag(BPoint(%.1f, %.1f), itemIndex: %ld, wasSelected: %d)\n", 92 point.x, point.y, itemIndex, wasSelected); 93 SwapItems(itemIndex, itemIndex + 1); 94 return true; 95 } 96 97 virtual void SelectionChanged() 98 { 99 // printf("SelectionChanged() - first selected: %ld\n", CurrentSelection(0)); 100 } 101 }; 102 103 // #pragma mark - 104 105 class TestView : public BView { 106 public: 107 TestView(BRect frame, const char* name, uint32 resizeMode, uint32 flags) 108 : BView(frame, name, resizeMode, flags) 109 { 110 } 111 112 void AttachedToWindow() 113 { 114 SetViewColor(255, 0, 0); 115 } 116 }; 117 118 // constructor 119 ObjectWindow::ObjectWindow(BRect frame, const char* name) 120 : BWindow(frame, name, B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 121 B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE) 122 { 123 BRect b(Bounds()); 124 125 b.bottom = b.top + 8; 126 BMenuBar* menuBar = new BMenuBar(b, "menu bar"); 127 AddChild(menuBar); 128 129 BMenu* menu = new BMenu("File"); 130 menuBar->AddItem(menu); 131 132 menu->AddItem(new BMenu("Submenu")); 133 134 BMenuItem* menuItem = new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 135 'Q'); 136 menu->AddItem(menuItem); 137 138 b = Bounds(); 139 b.top = menuBar->Bounds().bottom + 1; 140 b.right = ceilf((b.left + b.right) / 2.0); 141 BBox* bg = new BBox(b, "bg box", B_FOLLOW_TOP_BOTTOM, B_WILL_DRAW, 142 B_PLAIN_BORDER); 143 144 AddChild(bg); 145 bg->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 146 147 // object view occupies the right side of the window 148 b.left = b.right + 1.0; 149 b.right = Bounds().right - B_V_SCROLL_BAR_WIDTH; 150 b.bottom -= B_H_SCROLL_BAR_HEIGHT; 151 fObjectView = new ObjectView(b, "object view", B_FOLLOW_ALL, 152 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); 153 // wrap a scroll view around the object view 154 BScrollView* scrollView = new BScrollView("object scroller", fObjectView, 155 B_FOLLOW_ALL, 0, true, true, B_NO_BORDER); 156 157 if (BScrollBar* scrollBar = fObjectView->ScrollBar(B_VERTICAL)) { 158 scrollBar->SetRange(0.0, fObjectView->Bounds().Height()); 159 scrollBar->SetProportion(0.5); 160 } 161 if (BScrollBar* scrollBar = fObjectView->ScrollBar(B_HORIZONTAL)) { 162 scrollBar->SetRange(0.0, fObjectView->Bounds().Width()); 163 scrollBar->SetProportion(0.5); 164 } 165 AddChild(scrollView); 166 167 b = bg->Bounds(); 168 // controls occupy the left side of the window 169 b.InsetBy(5.0, 5.0); 170 BBox* controlGroup = new BBox(b, "controls box", 171 B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM, B_WILL_DRAW, B_FANCY_BORDER); 172 173 controlGroup->SetLabel("Controls"); 174 bg->AddChild(controlGroup); 175 176 b = controlGroup->Bounds(); 177 b.top += controlGroup->InnerFrame().top; 178 b.bottom = b.top + 25.0; 179 b.InsetBy(10.0, 10.0); 180 b.right = b.left + b.Width() / 2.0 - 5.0; 181 182 // new button 183 fNewB = new BButton(b, "new button", "New Object", 184 new BMessage(MSG_NEW_OBJECT)); 185 controlGroup->AddChild(fNewB); 186 SetDefaultButton(fNewB); 187 188 // clear button 189 b.OffsetBy(0, fNewB->Bounds().Height() + 5.0); 190 fClearB = new BButton(b, "clear button", "Clear", new BMessage(MSG_CLEAR)); 191 controlGroup->AddChild(fClearB); 192 193 // object type radio buttons 194 BMessage* message; 195 BRadioButton* radioButton; 196 197 b.OffsetBy(0, fClearB->Bounds().Height() + 5.0); 198 message = new BMessage(MSG_SET_OBJECT_TYPE); 199 message->AddInt32("type", OBJECT_LINE); 200 radioButton = new BRadioButton(b, "radio 1", "Line", message); 201 controlGroup->AddChild(radioButton); 202 203 radioButton->SetValue(B_CONTROL_ON); 204 205 b.OffsetBy(0, radioButton->Bounds().Height() + 5.0); 206 message = new BMessage(MSG_SET_OBJECT_TYPE); 207 message->AddInt32("type", OBJECT_RECT); 208 radioButton = new BRadioButton(b, "radio 2", "Rect", message); 209 controlGroup->AddChild(radioButton); 210 211 b.OffsetBy(0, radioButton->Bounds().Height() + 5.0); 212 message = new BMessage(MSG_SET_OBJECT_TYPE); 213 message->AddInt32("type", OBJECT_ROUND_RECT); 214 radioButton = new BRadioButton(b, "radio 3", "Round Rect", message); 215 controlGroup->AddChild(radioButton); 216 217 b.OffsetBy(0, radioButton->Bounds().Height() + 5.0); 218 message = new BMessage(MSG_SET_OBJECT_TYPE); 219 message->AddInt32("type", OBJECT_ELLIPSE); 220 radioButton = new BRadioButton(b, "radio 4", "Ellipse", message); 221 controlGroup->AddChild(radioButton); 222 223 // drawing mode 224 BPopUpMenu* popupMenu = new BPopUpMenu("<pick>"); 225 226 message = new BMessage(MSG_SET_DRAWING_MODE); 227 message->AddInt32("mode", B_OP_COPY); 228 popupMenu->AddItem(new BMenuItem("Copy", message)); 229 230 message = new BMessage(MSG_SET_DRAWING_MODE); 231 message->AddInt32("mode", B_OP_OVER); 232 popupMenu->AddItem(new BMenuItem("Over", message)); 233 234 message = new BMessage(MSG_SET_DRAWING_MODE); 235 message->AddInt32("mode", B_OP_INVERT); 236 popupMenu->AddItem(new BMenuItem("Invert", message)); 237 238 message = new BMessage(MSG_SET_DRAWING_MODE); 239 message->AddInt32("mode", B_OP_BLEND); 240 popupMenu->AddItem(new BMenuItem("Blend", message)); 241 242 message = new BMessage(MSG_SET_DRAWING_MODE); 243 message->AddInt32("mode", B_OP_SELECT); 244 popupMenu->AddItem(new BMenuItem("Select", message)); 245 246 message = new BMessage(MSG_SET_DRAWING_MODE); 247 message->AddInt32("mode", B_OP_ERASE); 248 popupMenu->AddItem(new BMenuItem("Erase", message)); 249 250 message = new BMessage(MSG_SET_DRAWING_MODE); 251 message->AddInt32("mode", B_OP_ADD); 252 popupMenu->AddItem(new BMenuItem("Add", message)); 253 254 message = new BMessage(MSG_SET_DRAWING_MODE); 255 message->AddInt32("mode", B_OP_SUBTRACT); 256 popupMenu->AddItem(new BMenuItem("Subtract", message)); 257 258 message = new BMessage(MSG_SET_DRAWING_MODE); 259 message->AddInt32("mode", B_OP_MIN); 260 popupMenu->AddItem(new BMenuItem("Min", message)); 261 262 message = new BMessage(MSG_SET_DRAWING_MODE); 263 message->AddInt32("mode", B_OP_MAX); 264 popupMenu->AddItem(new BMenuItem("Max", message)); 265 266 message = new BMessage(MSG_SET_DRAWING_MODE); 267 message->AddInt32("mode", B_OP_ALPHA); 268 BMenuItem* item = new BMenuItem("Alpha", message); 269 item->SetMarked(true); 270 popupMenu->AddItem(item); 271 272 b.OffsetBy(0, radioButton->Bounds().Height() + 10.0); 273 fDrawingModeMF = new BMenuField(b, "drawing mode field", "Mode:", 274 popupMenu); 275 276 controlGroup->AddChild(fDrawingModeMF); 277 278 fDrawingModeMF->SetDivider(fDrawingModeMF->StringWidth( 279 fDrawingModeMF->Label()) + 10.0); 280 281 // color control 282 b.OffsetBy(0, fDrawingModeMF->Bounds().Height() + 10.0); 283 fColorControl = new BColorControl(b.LeftTop(), B_CELLS_16x16, 8, 284 "color control", new BMessage(MSG_SET_COLOR)); 285 controlGroup->AddChild(fColorControl); 286 287 // alpha text control 288 b.OffsetBy(0, fColorControl-> Bounds().Height() + 5.0); 289 fAlphaTC = new BTextControl(b, "alpha text control", "Alpha:", "", 290 new BMessage(MSG_SET_COLOR)); 291 controlGroup->AddChild(fAlphaTC); 292 293 // divide text controls the same 294 float mWidth = fDrawingModeMF->StringWidth(fDrawingModeMF->Label()); 295 float aWidth = fAlphaTC->StringWidth(fAlphaTC->Label()); 296 297 float width = max_c(mWidth, aWidth) + 20.0; 298 fDrawingModeMF->SetDivider(width); 299 fAlphaTC->SetDivider(width); 300 301 // fill check box 302 b.OffsetBy(0, fAlphaTC->Bounds().Height() + 5.0); 303 fFillCB = new BCheckBox(b, "fill check box", "Fill", 304 new BMessage(MSG_SET_FILL_OR_STROKE)); 305 controlGroup->AddChild(fFillCB); 306 307 // pen size text control 308 b.OffsetBy(0, radioButton->Bounds().Height() + 5.0); 309 b.bottom = b.top + 10.0;//35; 310 fPenSizeS = new BSlider(b, "width slider", "Width:", NULL, 1, 100, 311 B_TRIANGLE_THUMB); 312 fPenSizeS->SetLimitLabels("1", "100"); 313 fPenSizeS->SetModificationMessage(new BMessage(MSG_SET_PEN_SIZE)); 314 fPenSizeS->SetHashMarks(B_HASH_MARKS_BOTTOM); 315 fPenSizeS->SetHashMarkCount(10); 316 317 controlGroup->AddChild(fPenSizeS); 318 319 // list view with objects 320 b = controlGroup->Bounds(); 321 b.top += controlGroup->InnerFrame().top; 322 b.InsetBy(10.0, 10.0); 323 b.left = b.left + b.Width() / 2.0 + 6.0; 324 b.right -= B_V_SCROLL_BAR_WIDTH; 325 b.bottom = fDrawingModeMF->Frame().top - 10.0; 326 327 fObjectLV = new ObjectListView(b, "object list", B_SINGLE_SELECTION_LIST); 328 fObjectLV->SetSelectionMessage(new BMessage(MSG_OBJECT_SELECTED)); 329 330 // wrap a scroll view around the list view 331 scrollView = new BScrollView("list scroller", fObjectLV, 332 B_FOLLOW_NONE, 0, false, true, B_FANCY_BORDER); 333 controlGroup->AddChild(scrollView); 334 335 // enforce some size limits 336 float minWidth = controlGroup->Frame().Width() + 30.0; 337 float minHeight = fPenSizeS->Frame().bottom 338 + menuBar->Bounds().Height() + 15.0; 339 float maxWidth = minWidth * 4.0; 340 float maxHeight = minHeight + 100; 341 SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 342 343 ResizeTo(max_c(frame.Width(), minWidth), max_c(frame.Height(), minHeight)); 344 345 _UpdateControls(); 346 } 347 348 // destructor 349 ObjectWindow::~ObjectWindow() 350 { 351 } 352 353 // QuitRequested 354 bool 355 ObjectWindow::QuitRequested() 356 { 357 be_app->PostMessage(B_QUIT_REQUESTED); 358 return true; 359 } 360 361 // MessageReceived 362 void 363 ObjectWindow::MessageReceived(BMessage* message) 364 { 365 switch (message->what) { 366 case MSG_SET_OBJECT_TYPE: { 367 int32 type; 368 if (message->FindInt32("type", &type) >= B_OK) { 369 fObjectView->SetObjectType(type); 370 fFillCB->SetEnabled(type != OBJECT_LINE); 371 if (!fFillCB->IsEnabled()) 372 fPenSizeS->SetEnabled(true); 373 else 374 fPenSizeS->SetEnabled(fFillCB->Value() == B_CONTROL_OFF); 375 } 376 break; 377 } 378 case MSG_SET_FILL_OR_STROKE: { 379 int32 value; 380 if (message->FindInt32("be:value", &value) >= B_OK) { 381 fObjectView->SetStateFill(value); 382 fPenSizeS->SetEnabled(value == B_CONTROL_OFF); 383 } 384 break; 385 } 386 case MSG_SET_COLOR: 387 fObjectView->SetStateColor(_GetColor()); 388 _UpdateColorControls(); 389 break; 390 case MSG_OBJECT_ADDED: { 391 State* object; 392 if (message->FindPointer("object", (void**)&object) >= B_OK) { 393 fObjectLV->AddItem(new ObjectItem("Object", object)); 394 } 395 // fall through 396 } 397 case MSG_OBJECT_COUNT_CHANGED: 398 fClearB->SetEnabled(fObjectView->CountObjects() > 0); 399 break; 400 case MSG_OBJECT_SELECTED: 401 if (ObjectItem* item = (ObjectItem*)fObjectLV->ItemAt(fObjectLV->CurrentSelection(0))) { 402 fObjectView->SetState(item->Object()); 403 fObjectView->SetStateColor(item->Object()->Color()); 404 _UpdateControls(); 405 } else 406 fObjectView->SetState(NULL); 407 break; 408 case MSG_REMOVE_OBJECT: 409 while (ObjectItem* item = (ObjectItem*)fObjectLV->ItemAt(fObjectLV->CurrentSelection(0))) { 410 fObjectView->RemoveObject(item->Object()); 411 fObjectLV->RemoveItem(item); 412 delete item; 413 } 414 break; 415 case MSG_NEW_OBJECT: 416 fObjectView->SetState(NULL); 417 break; 418 case MSG_CLEAR: { 419 BAlert *alert = new BAlert("Playground", 420 "Do you really want to clear all drawing objects?", 421 "No", "Yes"); 422 if (alert->Go() == 1) { 423 fObjectView->MakeEmpty(); 424 fObjectLV->MakeEmpty(); 425 } 426 break; 427 } 428 case MSG_SET_PEN_SIZE: 429 fObjectView->SetStatePenSize((float)fPenSizeS->Value()); 430 break; 431 case MSG_SET_DRAWING_MODE: { 432 drawing_mode mode; 433 if (message->FindInt32("mode", (int32*)&mode) >= B_OK) { 434 fObjectView->SetStateDrawingMode(mode); 435 } 436 break; 437 } 438 default: 439 BWindow::MessageReceived(message); 440 } 441 } 442 443 // _UpdateControls 444 void 445 ObjectWindow::_UpdateControls() const 446 { 447 _UpdateColorControls(); 448 449 // update buttons 450 fClearB->SetEnabled(fObjectView->CountObjects() > 0); 451 452 fFillCB->SetEnabled(fObjectView->ObjectType() != OBJECT_LINE); 453 454 // pen size 455 fPenSizeS->SetValue((int32)fObjectView->StatePenSize()); 456 457 // disable penSize if fill is on 458 if (!fFillCB->IsEnabled()) 459 fPenSizeS->SetEnabled(true); 460 else 461 fPenSizeS->SetEnabled(fFillCB->Value() == B_CONTROL_OFF); 462 } 463 464 // _UpdateColorControls 465 void 466 ObjectWindow::_UpdateColorControls() const 467 { 468 // update color 469 rgb_color c = fObjectView->StateColor(); 470 char string[32]; 471 472 sprintf(string, "%d", c.alpha); 473 fAlphaTC->SetText(string); 474 475 fColorControl->SetValue(c); 476 } 477 478 // _GetColor 479 rgb_color 480 ObjectWindow::_GetColor() const 481 { 482 rgb_color c; 483 484 c = fColorControl->ValueAsColor(); 485 c.alpha = max_c(0, min_c(255, atoi(fAlphaTC->Text()))); 486 487 return c; 488 } 489 490