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