1 // main.cpp 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 #include <Application.h> 7 #include <Alert.h> 8 #include <Box.h> 9 #include <Button.h> 10 #include <CheckBox.h> 11 #include <Menu.h> 12 #include <MenuBar.h> 13 #include <MenuField.h> 14 #include <MenuItem.h> 15 #include <PopUpMenu.h> 16 #include <ScrollBar.h> 17 #include <ScrollView.h> 18 #include <Slider.h> 19 #include <String.h> 20 #include <RadioButton.h> 21 #include <TextControl.h> 22 #include <TextView.h> 23 24 #include "ObjectView.h" 25 #include "States.h" 26 27 #include "ObjectWindow.h" 28 29 enum { 30 MSG_SET_OBJECT_TYPE = 'stot', 31 MSG_SET_FILL_OR_STROKE = 'stfs', 32 MSG_SET_COLOR = 'stcl', 33 MSG_SET_PEN_SIZE = 'stps', 34 MSG_SET_DRAWING_MODE = 'stdm', 35 36 MSG_NEW_OBJECT = 'nobj', 37 38 MSG_UNDO = 'undo', 39 MSG_REDO = 'redo', 40 41 MSG_CLEAR = 'clir', 42 }; 43 44 // constructor 45 ObjectWindow::ObjectWindow(BRect frame, const char* name) 46 : BWindow(frame, name, B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 47 B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE) 48 // : BWindow(frame, name, B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 49 // B_ASYNCHRONOUS_CONTROLS) 50 // : BWindow(frame, name, B_FLOATING_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 51 // B_ASYNCHRONOUS_CONTROLS) 52 // : BWindow(frame, name, B_BORDERED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 53 // B_ASYNCHRONOUS_CONTROLS) 54 // : BWindow(frame, name, B_NO_BORDER_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 55 // B_ASYNCHRONOUS_CONTROLS) 56 { 57 BRect b(Bounds()); 58 59 b.bottom = b.top + 8; 60 BMenuBar* menuBar = new BMenuBar(b, "menu bar"); 61 AddChild(menuBar); 62 63 BMenu* menu = new BMenu("File"); 64 menuBar->AddItem(menu); 65 66 BMenuItem* menuItem = new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 67 'Q'); 68 menu->AddItem(menuItem); 69 70 b = Bounds(); 71 b.top = menuBar->Bounds().bottom + 1; 72 b.right = ceilf((b.left + b.right) / 3.0); 73 BBox* bg = new BBox(b, "bg box", B_FOLLOW_TOP_BOTTOM, B_WILL_DRAW, B_PLAIN_BORDER); 74 75 AddChild(bg); 76 bg->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 77 78 // object view occupies the right side of the window 79 b.left = b.right + 1.0; 80 b.right = Bounds().right - B_V_SCROLL_BAR_WIDTH; 81 b.bottom -= B_H_SCROLL_BAR_HEIGHT; 82 fObjectView = new ObjectView(b, "object view", B_FOLLOW_ALL, 83 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); 84 // wrap a scroll view around the object view 85 BScrollView* scrollView = new BScrollView("object scroller", fObjectView, 86 B_FOLLOW_ALL, 0, true, true, 87 B_NO_BORDER); 88 89 if (BScrollBar* scrollBar = fObjectView->ScrollBar(B_VERTICAL)) { 90 scrollBar->SetRange(0.0, fObjectView->Bounds().Height()); 91 scrollBar->SetProportion(0.5); 92 } 93 if (BScrollBar* scrollBar = fObjectView->ScrollBar(B_HORIZONTAL)) { 94 scrollBar->SetRange(0.0, fObjectView->Bounds().Width()); 95 scrollBar->SetProportion(0.5); 96 } 97 AddChild(scrollView); 98 99 b = bg->Bounds(); 100 // controls occupy the left side of the window 101 b.InsetBy(5.0, 5.0); 102 BBox* controlGroup = new BBox(b, "controls box", B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM, 103 B_WILL_DRAW, B_FANCY_BORDER); 104 105 controlGroup->SetLabel("Controls"); 106 bg->AddChild(controlGroup); 107 108 b = controlGroup->Bounds(); 109 b.top += 10.0; 110 b.bottom = b.top + 25.0; 111 b.InsetBy(5.0, 5.0); 112 113 // new button 114 fNewB = new BButton(b, "new button", "New Object", new BMessage(MSG_NEW_OBJECT)); 115 controlGroup->AddChild(fNewB); 116 117 // clear button 118 b.OffsetBy(0, fNewB->Bounds().Height() + 5.0); 119 fClearB = new BButton(b, "clear button", "Clear", new BMessage(MSG_CLEAR)); 120 controlGroup->AddChild(fClearB); 121 122 // object type radio buttons 123 BMessage* message; 124 BRadioButton* radioButton; 125 126 b.OffsetBy(0, fClearB->Bounds().Height() + 5.0); 127 message = new BMessage(MSG_SET_OBJECT_TYPE); 128 message->AddInt32("type", OBJECT_LINE); 129 radioButton = new BRadioButton(b, "radio 1", "Line", message); 130 controlGroup->AddChild(radioButton); 131 132 radioButton->SetValue(B_CONTROL_ON); 133 134 b.OffsetBy(0, radioButton->Bounds().Height() + 5.0); 135 message = new BMessage(MSG_SET_OBJECT_TYPE); 136 message->AddInt32("type", OBJECT_RECT); 137 radioButton = new BRadioButton(b, "radio 2", "Rect", message); 138 controlGroup->AddChild(radioButton); 139 140 b.OffsetBy(0, radioButton->Bounds().Height() + 5.0); 141 message = new BMessage(MSG_SET_OBJECT_TYPE); 142 message->AddInt32("type", OBJECT_ROUND_RECT); 143 radioButton = new BRadioButton(b, "radio 3", "Round Rect", message); 144 controlGroup->AddChild(radioButton); 145 146 b.OffsetBy(0, radioButton->Bounds().Height() + 5.0); 147 message = new BMessage(MSG_SET_OBJECT_TYPE); 148 message->AddInt32("type", OBJECT_ELLIPSE); 149 radioButton = new BRadioButton(b, "radio 4", "Ellipse", message); 150 controlGroup->AddChild(radioButton); 151 152 // drawing mode 153 BPopUpMenu* popupMenu = new BPopUpMenu("<pick>"); 154 155 message = new BMessage(MSG_SET_DRAWING_MODE); 156 message->AddInt32("mode", B_OP_COPY); 157 popupMenu->AddItem(new BMenuItem("Copy", message)); 158 159 message = new BMessage(MSG_SET_DRAWING_MODE); 160 message->AddInt32("mode", B_OP_OVER); 161 popupMenu->AddItem(new BMenuItem("Over", message)); 162 163 message = new BMessage(MSG_SET_DRAWING_MODE); 164 message->AddInt32("mode", B_OP_INVERT); 165 popupMenu->AddItem(new BMenuItem("Invert", message)); 166 167 message = new BMessage(MSG_SET_DRAWING_MODE); 168 message->AddInt32("mode", B_OP_BLEND); 169 popupMenu->AddItem(new BMenuItem("Blend", message)); 170 171 message = new BMessage(MSG_SET_DRAWING_MODE); 172 message->AddInt32("mode", B_OP_SELECT); 173 popupMenu->AddItem(new BMenuItem("Select", message)); 174 175 message = new BMessage(MSG_SET_DRAWING_MODE); 176 message->AddInt32("mode", B_OP_ERASE); 177 popupMenu->AddItem(new BMenuItem("Erase", message)); 178 179 message = new BMessage(MSG_SET_DRAWING_MODE); 180 message->AddInt32("mode", B_OP_ADD); 181 popupMenu->AddItem(new BMenuItem("Add", message)); 182 183 message = new BMessage(MSG_SET_DRAWING_MODE); 184 message->AddInt32("mode", B_OP_SUBTRACT); 185 popupMenu->AddItem(new BMenuItem("Subtract", message)); 186 187 message = new BMessage(MSG_SET_DRAWING_MODE); 188 message->AddInt32("mode", B_OP_MIN); 189 popupMenu->AddItem(new BMenuItem("Min", message)); 190 191 message = new BMessage(MSG_SET_DRAWING_MODE); 192 message->AddInt32("mode", B_OP_MAX); 193 popupMenu->AddItem(new BMenuItem("Max", message)); 194 195 message = new BMessage(MSG_SET_DRAWING_MODE); 196 message->AddInt32("mode", B_OP_ALPHA); 197 popupMenu->AddItem(new BMenuItem("Alpha", message)); 198 199 b.OffsetBy(0, radioButton->Bounds().Height() + 5.0); 200 fDrawingModeMF = new BMenuField(b, "drawing mode field", "Mode", 201 popupMenu); 202 203 controlGroup->AddChild(fDrawingModeMF); 204 205 fDrawingModeMF->SetDivider(fDrawingModeMF->StringWidth(fDrawingModeMF->Label()) + 10.0); 206 207 // red text control 208 b.OffsetBy(0, fDrawingModeMF->Bounds().Height() + 5.0); 209 fRedTC = new BTextControl(b, "red text control", "Red", "", 210 new BMessage(MSG_SET_COLOR)); 211 controlGroup->AddChild(fRedTC); 212 213 // green text control 214 b.OffsetBy(0, fRedTC->Bounds().Height() + 5.0); 215 fGreenTC = new BTextControl(b, "green text control", "Green", "", 216 new BMessage(MSG_SET_COLOR)); 217 controlGroup->AddChild(fGreenTC); 218 219 // blue text control 220 b.OffsetBy(0, fGreenTC->Bounds().Height() + 5.0); 221 fBlueTC = new BTextControl(b, "blue text control", "Blue", "", 222 new BMessage(MSG_SET_COLOR)); 223 controlGroup->AddChild(fBlueTC); 224 225 // alpha text control 226 b.OffsetBy(0, fBlueTC->Bounds().Height() + 5.0); 227 fAlphaTC = new BTextControl(b, "alpha text control", "Alpha", "", 228 new BMessage(MSG_SET_COLOR)); 229 controlGroup->AddChild(fAlphaTC); 230 231 // TODO: while this block of code works in the Haiku app_server running under R5, 232 // it crashes pretty badly under Haiku. I have no idea why this happens, because 233 // I was doing the same thing before at other places. 234 // divide text controls the same 235 float mWidth = fDrawingModeMF->StringWidth(fDrawingModeMF->Label()); 236 float rWidth = fRedTC->StringWidth(fRedTC->Label()); 237 float gWidth = fGreenTC->StringWidth(fGreenTC->Label()); 238 float bWidth = fBlueTC->StringWidth(fBlueTC->Label()); 239 float aWidth = fAlphaTC->StringWidth(fAlphaTC->Label()); 240 241 float width = max_c(mWidth, max_c(rWidth, max_c(gWidth, max_c(bWidth, aWidth)))) + 10.0; 242 fDrawingModeMF->SetDivider(width); 243 fRedTC->SetDivider(width); 244 fGreenTC->SetDivider(width); 245 fBlueTC->SetDivider(width); 246 fAlphaTC->SetDivider(width); 247 248 // fill check box 249 b.OffsetBy(0, fAlphaTC->Bounds().Height() + 5.0); 250 fFillCB = new BCheckBox(b, "fill check box", "Fill", 251 new BMessage(MSG_SET_FILL_OR_STROKE)); 252 controlGroup->AddChild(fFillCB); 253 254 // pen size text control 255 b.OffsetBy(0, radioButton->Bounds().Height() + 5.0); 256 b.bottom = b.top + 10.0;//35; 257 fPenSizeS = new BSlider(b, "width slider", "Width", 258 NULL, 1, 100, B_TRIANGLE_THUMB); 259 fPenSizeS->SetLimitLabels("1", "100"); 260 fPenSizeS->SetModificationMessage(new BMessage(MSG_SET_PEN_SIZE)); 261 fPenSizeS->SetHashMarks(B_HASH_MARKS_BOTTOM); 262 fPenSizeS->SetHashMarkCount(10); 263 264 controlGroup->AddChild(fPenSizeS); 265 266 // enforce some size limits 267 float minWidth = controlGroup->Frame().Width() + 30.0; 268 float minHeight = fPenSizeS->Frame().bottom + 269 menuBar->Bounds().Height() + 15.0; 270 float maxWidth = minWidth * 4.0; 271 float maxHeight = minHeight; 272 SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 273 274 ResizeTo(max_c(frame.Width(), minWidth), max_c(frame.Height(), minHeight)); 275 276 _UpdateControls(); 277 } 278 279 // destructor 280 ObjectWindow::~ObjectWindow() 281 { 282 } 283 284 // QuitRequested 285 bool 286 ObjectWindow::QuitRequested() 287 { 288 be_app->PostMessage(B_QUIT_REQUESTED); 289 return true; 290 } 291 292 // MessageReceived 293 void 294 ObjectWindow::MessageReceived(BMessage* message) 295 { 296 switch (message->what) { 297 case MSG_SET_OBJECT_TYPE: { 298 int32 type; 299 if (message->FindInt32("type", &type) >= B_OK) { 300 fObjectView->SetObjectType(type); 301 fFillCB->SetEnabled(type != OBJECT_LINE); 302 if (!fFillCB->IsEnabled()) 303 fPenSizeS->SetEnabled(true); 304 else 305 fPenSizeS->SetEnabled(fFillCB->Value() == B_CONTROL_OFF); 306 } 307 break; 308 } 309 case MSG_SET_FILL_OR_STROKE: { 310 int32 value; 311 if (message->FindInt32("be:value", &value) >= B_OK) { 312 fObjectView->SetStateFill(value); 313 fPenSizeS->SetEnabled(value == B_CONTROL_OFF); 314 } 315 break; 316 } 317 case MSG_SET_COLOR: 318 fObjectView->SetStateColor(_GetColor()); 319 _UpdateColorControls(); 320 break; 321 case MSG_OBJECT_COUNT_CHANGED: 322 fClearB->SetEnabled(fObjectView->CountObjects() > 0); 323 break; 324 case MSG_NEW_OBJECT: 325 fObjectView->SetState(NULL); 326 break; 327 case MSG_CLEAR: { 328 BAlert *alert = new BAlert("Playground", "Do you really want to clear all drawing objects?", "No", "Yes"); 329 if (alert->Go() == 1) { 330 fObjectView->MakeEmpty(); 331 } 332 break; 333 } 334 case MSG_SET_PEN_SIZE: 335 fObjectView->SetStatePenSize((float)fPenSizeS->Value()); 336 break; 337 case MSG_SET_DRAWING_MODE: { 338 drawing_mode mode; 339 if (message->FindInt32("mode", (int32*)&mode) >= B_OK) { 340 fObjectView->SetStateDrawingMode(mode); 341 } 342 break; 343 } 344 default: 345 BWindow::MessageReceived(message); 346 } 347 } 348 349 // _UpdateControls 350 void 351 ObjectWindow::_UpdateControls() const 352 { 353 _UpdateColorControls(); 354 355 // update buttons 356 fClearB->SetEnabled(fObjectView->CountObjects() > 0); 357 358 fFillCB->SetEnabled(fObjectView->ObjectType() != OBJECT_LINE); 359 360 // pen size 361 fPenSizeS->SetValue((int32)fObjectView->StatePenSize()); 362 363 // disable penSize if fill is on 364 if (!fFillCB->IsEnabled()) 365 fPenSizeS->SetEnabled(true); 366 else 367 fPenSizeS->SetEnabled(fFillCB->Value() == B_CONTROL_OFF); 368 } 369 370 // _UpdateColorControls 371 void 372 ObjectWindow::_UpdateColorControls() const 373 { 374 // update color 375 rgb_color c = fObjectView->StateColor(); 376 char string[32]; 377 378 sprintf(string, "%d", c.red); 379 fRedTC->SetText(string); 380 381 sprintf(string, "%d", c.green); 382 fGreenTC->SetText(string); 383 384 sprintf(string, "%d", c.blue); 385 fBlueTC->SetText(string); 386 387 sprintf(string, "%d", c.alpha); 388 fAlphaTC->SetText(string); 389 } 390 391 // _GetColor 392 rgb_color 393 ObjectWindow::_GetColor() const 394 { 395 rgb_color c; 396 c.red = max_c(0, min_c(255, atoi(fRedTC->Text()))); 397 c.green = max_c(0, min_c(255, atoi(fGreenTC->Text()))); 398 c.blue = max_c(0, min_c(255, atoi(fBlueTC->Text()))); 399 c.alpha = max_c(0, min_c(255, atoi(fAlphaTC->Text()))); 400 401 return c; 402 } 403 404