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