xref: /haiku/src/tests/servers/app/playground/ObjectWindow.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
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