1/*
2 * Copyright 2006, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 */
8
9#include "IconOptionsControl.h"
10
11#include <stdio.h>
12
13#include <Window.h>
14
15#include "IconButton.h"
16
17#define LABEL_DIST 8.0
18
19// constructor
20IconOptionsControl::IconOptionsControl(const char* name,
21									   const char* label,
22									   BMessage* message,
23									   BHandler* target)
24	: MView(),
25	  BControl(BRect(0.0, 0.0, 10.0, 10.0), name, label, message,
26	  		   B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS),
27	  fTargetCache(target)
28{
29	if (Label()) {
30		labelwidth = StringWidth(Label()) + LABEL_DIST;
31	} else {
32		labelwidth = 0.0;
33	}
34}
35
36// destructor
37IconOptionsControl::~IconOptionsControl()
38{
39}
40
41// layoutprefs
42minimax
43IconOptionsControl::layoutprefs()
44{
45	// sanity checks
46	if (rolemodel)
47		labelwidth = rolemodel->LabelWidth();
48	if (labelwidth < LabelWidth())
49		labelwidth = LabelWidth();
50
51	mpm.mini.x = 0.0 + labelwidth + 5.0;
52	mpm.mini.y = 0.0;
53
54	for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
55		minimax childPrefs = button->layoutprefs();
56		mpm.mini.x += childPrefs.mini.x + 1;
57		if (mpm.mini.y < childPrefs.mini.y + 1)
58			mpm.mini.y = childPrefs.mini.y + 1;
59	}
60
61	mpm.maxi.x = mpm.mini.x;
62	mpm.maxi.y = mpm.mini.y;
63
64	mpm.weight = 1.0;
65
66	return mpm;
67}
68
69// layout
70BRect
71IconOptionsControl::layout(BRect frame)
72{
73	if (frame.Width() < mpm.mini.x + 1)
74		frame.right = frame.left + mpm.mini.x + 1;
75
76	MoveTo(frame.LeftTop());
77	ResizeTo(frame.Width(), frame.Height());
78
79	return Frame();
80}
81
82// LabelWidth
83float
84IconOptionsControl::LabelWidth()
85{
86	float width = ceilf(StringWidth(Label()));
87	if (width > 0.0)
88		width += LABEL_DIST;
89	return width;
90}
91
92// SetLabel
93void
94IconOptionsControl::SetLabel(const char* label)
95{
96	BControl::SetLabel(label);
97	float width = LabelWidth();
98	if (rolemodel)
99		labelwidth = rolemodel->LabelWidth() > labelwidth ?
100						rolemodel->LabelWidth() : labelwidth;
101
102	labelwidth = width > labelwidth ? width : labelwidth;
103
104	_TriggerRelayout();
105}
106
107// AttachedToWindow
108void
109IconOptionsControl::AttachedToWindow()
110{
111	BControl::AttachedToWindow();
112
113	SetViewColor(B_TRANSPARENT_32_BIT);
114	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
115}
116
117// AllAttached
118void
119IconOptionsControl::AllAttached()
120{
121	for (int32 i = 0; IconButton* button = _FindIcon(i); i++)
122		button->SetTarget(this);
123	if (fTargetCache)
124		SetTarget(fTargetCache);
125}
126
127// Draw
128void
129IconOptionsControl::Draw(BRect updateRect)
130{
131	FillRect(updateRect, B_SOLID_LOW);
132
133	if (Label()) {
134		if (!IsEnabled())
135			SetHighColor(tint_color(LowColor(), B_DISABLED_LABEL_TINT));
136		else
137			SetHighColor(tint_color(LowColor(), B_DARKEN_MAX_TINT));
138
139		font_height fh;
140		GetFontHeight(&fh);
141		BPoint p(Bounds().LeftTop());
142		p.y += floorf(Bounds().Height() / 2.0 + (fh.ascent + fh.descent) / 2.0) - 2.0;
143		DrawString(Label(), p);
144	}
145}
146
147// FrameResized
148void
149IconOptionsControl::FrameResized(float width, float height)
150{
151	_LayoutIcons(Bounds());
152}
153
154// SetValue
155void
156IconOptionsControl::SetValue(int32 value)
157{
158	if (IconButton* valueButton = _FindIcon(value)) {
159		for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
160			button->SetPressed(button == valueButton);
161		}
162	}
163}
164
165// Value
166int32
167IconOptionsControl::Value() const
168{
169	for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
170		if (button->IsPressed())
171			return i;
172	}
173	return -1;
174}
175
176// SetEnabled
177void
178IconOptionsControl::SetEnabled(bool enable)
179{
180	for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
181		button->SetEnabled(enable);
182	}
183	BControl::SetEnabled(enable);
184	Invalidate();
185}
186
187// MessageReceived
188void
189IconOptionsControl::MessageReceived(BMessage* message)
190{
191	// catch a message from the attached IconButtons to
192	// handle switching the pressed icon
193	BView* source;
194	if (message->FindPointer("be:source", (void**)&source) >= B_OK) {
195		if (IconButton* sourceIcon = dynamic_cast<IconButton*>(source)) {
196			for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
197				if (button == sourceIcon) {
198					SetValue(i);
199					break;
200				}
201			}
202			// forward the message
203			Invoke(message);
204			return;
205		}
206	}
207	BControl::MessageReceived(message);
208}
209
210// Invoke
211status_t
212IconOptionsControl::Invoke(BMessage* message)
213{
214	return BInvoker::Invoke(message);
215}
216
217// AddOption
218void
219IconOptionsControl::AddOption(IconButton* icon)
220{
221	if (icon) {
222		if (!_FindIcon(0)) {
223			// first icon added, mark it
224			icon->SetPressed(true);
225		}
226		AddChild(icon);
227		icon->SetTarget(this);
228		layoutprefs();
229		_TriggerRelayout();
230	}
231}
232
233// _FindIcon
234IconButton*
235IconOptionsControl::_FindIcon(int32 index) const
236{
237	if (BView* view = ChildAt(index))
238		return dynamic_cast<IconButton*>(view);
239	return NULL;
240}
241
242// _TriggerRelayout
243void
244IconOptionsControl::_TriggerRelayout()
245{
246	if (!Parent())
247		return;
248
249	MView* mParent = dynamic_cast<MView*>(Parent());
250	if (mParent) {
251		if (BWindow* window = Window()) {
252			window->PostMessage(M_RECALCULATE_SIZE);
253		}
254	} else {
255		_LayoutIcons(Bounds());
256	}
257}
258
259// _LayoutIcons
260void
261IconOptionsControl::_LayoutIcons(BRect frame)
262{
263	BPoint lt = frame.LeftTop();
264
265	// sanity checks
266	if (rolemodel)
267		labelwidth = rolemodel->LabelWidth();
268	if (labelwidth < LabelWidth())
269		labelwidth = LabelWidth();
270
271	lt.x += labelwidth;
272
273	for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
274		if (i == 0) {
275			lt.y = ceilf((frame.top + frame.bottom - button->mpm.mini.y) / 2.0);
276		}
277		button->MoveTo(lt);
278		button->ResizeTo(button->mpm.mini.x, button->mpm.mini.y);
279		lt = button->Frame().RightTop() + BPoint(1.0, 0.0);
280	}
281}
282