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