1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All rights reserved.
33*/
34
35
36#include "DialogPane.h"
37
38#include <ControlLook.h>
39#include <LayoutUtils.h>
40
41#include "Thread.h"
42#include "Utilities.h"
43#include "Window.h"
44
45
46const uint32 kValueChanged = 'swch';
47
48const rgb_color kNormalColor = {150, 150, 150, 255};
49const rgb_color kHighlightColor = {100, 100, 0, 255};
50
51
52static void
53AddSelf(BView* self, BView* to)
54{
55	to->AddChild(self);
56}
57
58
59void
60ViewList::RemoveAll(BView*)
61{
62	EachListItemIgnoreResult(this, &BView::RemoveSelf);
63}
64
65
66void
67ViewList::AddAll(BView* toParent)
68{
69	EachListItem(this, &AddSelf, toParent);
70}
71
72
73//	#pragma mark - DialogPane
74
75
76DialogPane::DialogPane(BRect mode1Frame, BRect mode2Frame, int32 initialMode,
77	const char* name, uint32 followFlags, uint32 flags)
78	:
79	BView(FrameForMode(initialMode, mode1Frame, mode2Frame, mode2Frame),
80		name, followFlags, flags),
81	fMode(initialMode),
82	fMode1Frame(mode1Frame),
83	fMode2Frame(mode2Frame),
84	fMode3Frame(mode2Frame)
85{
86	SetMode(fMode, true);
87}
88
89
90DialogPane::DialogPane(BRect mode1Frame, BRect mode2Frame, BRect mode3Frame,
91	int32 initialMode, const char* name, uint32 followFlags, uint32 flags)
92	:
93	BView(FrameForMode(initialMode, mode1Frame, mode2Frame, mode3Frame),
94		name, followFlags, flags),
95	fMode(initialMode),
96	fMode1Frame(mode1Frame),
97	fMode2Frame(mode2Frame),
98	fMode3Frame(mode3Frame)
99{
100	SetMode(fMode, true);
101}
102
103
104DialogPane::~DialogPane()
105{
106	fMode3Items.RemoveAll(this);
107	fMode2Items.RemoveAll(this);
108}
109
110
111void
112DialogPane::SetMode(int32 mode, bool initialSetup)
113{
114	ASSERT(mode < 3 && mode >= 0);
115
116	if (!initialSetup && mode == fMode)
117		return;
118
119	int32 oldMode = fMode;
120	fMode = mode;
121
122	bool followBottom = (ResizingMode() & B_FOLLOW_BOTTOM) != 0;
123	// if we are follow bottom, we will move ourselves, need to place us back
124	float bottomOffset = 0;
125	if (followBottom && Window() != NULL)
126		bottomOffset = Window()->Bounds().bottom - Frame().bottom;
127
128	BRect newBounds(BoundsForMode(fMode));
129	if (!initialSetup)
130		ResizeParentWindow(fMode, oldMode);
131
132	ResizeTo(newBounds.Width(), newBounds.Height());
133
134	float delta = 0;
135	if (followBottom && Window() != NULL)
136		delta = (Window()->Bounds().bottom - Frame().bottom) - bottomOffset;
137
138	if (delta != 0) {
139		MoveBy(0, delta);
140		if (fLatch && (fLatch->ResizingMode() & B_FOLLOW_BOTTOM))
141			fLatch->MoveBy(0, delta);
142	}
143
144	switch (fMode) {
145		case 0:
146		{
147			if (oldMode > 1)
148				fMode3Items.RemoveAll(this);
149			if (oldMode > 0)
150				fMode2Items.RemoveAll(this);
151
152			BView* separator = FindView("separatorLine");
153			if (separator) {
154				BRect frame(separator->Frame());
155				frame.InsetBy(-1, -1);
156				RemoveChild(separator);
157				Invalidate();
158			}
159
160			AddChild(new SeparatorLine(BPoint(newBounds.left, newBounds.top
161				+ newBounds.Height() / 2), newBounds.Width(), false,
162				"separatorLine"));
163			break;
164		}
165
166		case 1:
167		{
168			if (oldMode > 1)
169				fMode3Items.RemoveAll(this);
170			else
171				fMode2Items.AddAll(this);
172
173			BView* separator = FindView("separatorLine");
174			if (separator) {
175				BRect frame(separator->Frame());
176				frame.InsetBy(-1, -1);
177				RemoveChild(separator);
178				Invalidate();
179			}
180			break;
181		}
182
183		case 2:
184		{
185			fMode3Items.AddAll(this);
186			if (oldMode < 1)
187				fMode2Items.AddAll(this);
188
189			BView* separator = FindView("separatorLine");
190			if (separator) {
191				BRect frame(separator->Frame());
192				frame.InsetBy(-1, -1);
193				RemoveChild(separator);
194				Invalidate();
195			}
196			break;
197		}
198	}
199}
200
201
202void
203DialogPane::AttachedToWindow()
204{
205	AdoptParentColors();
206}
207
208
209void
210DialogPane::ResizeParentWindow(int32 from, int32 to)
211{
212	if (Window() == NULL)
213		return;
214
215	BRect oldBounds = BoundsForMode(from);
216	BRect newBounds = BoundsForMode(to);
217
218	BPoint by = oldBounds.RightBottom() - newBounds.RightBottom();
219	if (by != BPoint(0, 0))
220		Window()->ResizeBy(by.x, by.y);
221}
222
223
224void
225DialogPane::AddItem(BView* view, int32 toMode)
226{
227	if (toMode == 1)
228		fMode2Items.AddItem(view);
229	else if (toMode == 2)
230		fMode3Items.AddItem(view);
231
232	if (fMode >= toMode)
233		AddChild(view);
234}
235
236
237BRect
238DialogPane::FrameForMode(int32 mode)
239{
240	switch (mode) {
241		case 0:
242			return fMode1Frame;
243
244		case 1:
245			return fMode2Frame;
246
247		case 2:
248			return fMode3Frame;
249	}
250
251	return fMode1Frame;
252}
253
254
255BRect
256DialogPane::BoundsForMode(int32 mode)
257{
258	BRect result;
259	switch (mode) {
260		case 0:
261			result = fMode1Frame;
262			break;
263
264		case 1:
265			result = fMode2Frame;
266			break;
267
268		case 2:
269			result = fMode3Frame;
270			break;
271	}
272	result.OffsetTo(0, 0);
273
274	return result;
275}
276
277
278BRect
279DialogPane::FrameForMode(int32 mode, BRect mode1Frame, BRect mode2Frame,
280	BRect mode3Frame)
281{
282	switch (mode) {
283		case 0:
284			return mode1Frame;
285
286		case 1:
287			return mode2Frame;
288
289		case 2:
290			return mode3Frame;
291	}
292
293	return mode1Frame;
294}
295
296
297void
298DialogPane::SetSwitch(BControl* control)
299{
300	fLatch = control;
301	control->SetMessage(new BMessage(kValueChanged));
302	control->SetTarget(this);
303}
304
305
306void
307DialogPane::MessageReceived(BMessage* message)
308{
309	if (message->what == kValueChanged) {
310		int32 value;
311		if (message->FindInt32("be:value", &value) == B_OK)
312			SetMode(value);
313	} else
314		_inherited::MessageReceived(message);
315}
316
317
318//	#pragma mark - PaneSwitch
319
320
321PaneSwitch::PaneSwitch(BRect frame, const char* name, bool leftAligned,
322		uint32 resizeMask, uint32 flags)
323	:
324	BControl(frame, name, "", 0, resizeMask, flags),
325	fLeftAligned(leftAligned),
326	fPressing(false),
327	fLabelOn(NULL),
328	fLabelOff(NULL)
329{
330}
331
332
333PaneSwitch::PaneSwitch(const char* name, bool leftAligned, uint32 flags)
334	:
335	BControl(name, "", 0, flags),
336	fLeftAligned(leftAligned),
337	fPressing(false),
338	fLabelOn(NULL),
339	fLabelOff(NULL)
340{
341}
342
343
344PaneSwitch::~PaneSwitch()
345{
346	free(fLabelOn);
347	free(fLabelOff);
348}
349
350
351void
352PaneSwitch::Draw(BRect)
353{
354	BRect bounds(Bounds());
355
356	// Draw the label, if any
357	const char* label = fLabelOff;
358	if (fLabelOn != NULL && Value() == B_CONTROL_ON)
359		label = fLabelOn;
360
361	if (label != NULL) {
362		BPoint point;
363		float labelDist = sLatchSize + ceilf(sLatchSize / 2.0);
364		if (fLeftAligned)
365			point.x = labelDist;
366		else
367			point.x = bounds.right - labelDist - StringWidth(label);
368
369		SetHighUIColor(B_PANEL_TEXT_COLOR);
370		font_height fontHeight;
371		GetFontHeight(&fontHeight);
372		point.y = (bounds.top + bounds.bottom
373			- ceilf(fontHeight.ascent) - ceilf(fontHeight.descent)) / 2
374			+ ceilf(fontHeight.ascent);
375
376		DrawString(label, point);
377	}
378
379	// draw the latch
380	if (fPressing)
381		DrawInState(kPressed);
382	else if (Value())
383		DrawInState(kExpanded);
384	else
385		DrawInState(kCollapsed);
386
387	// ...and the focus indication
388	if (!IsFocus() || !Window()->IsActive())
389		return;
390
391	rgb_color markColor = ui_color(B_KEYBOARD_NAVIGATION_COLOR);
392
393	BeginLineArray(2);
394	AddLine(BPoint(bounds.left + 2, bounds.bottom - 1),
395		BPoint(bounds.right - 2, bounds.bottom - 1), markColor);
396	AddLine(BPoint(bounds.left + 2, bounds.bottom),
397		BPoint(bounds.right - 2, bounds.bottom), kWhite);
398	EndLineArray();
399}
400
401
402void
403PaneSwitch::MouseDown(BPoint)
404{
405	if (!IsEnabled())
406		return;
407
408	fPressing = true;
409	MouseDownThread<PaneSwitch>::TrackMouse(this, &PaneSwitch::DoneTracking,
410		&PaneSwitch::Track);
411	Invalidate();
412}
413
414
415void
416PaneSwitch::GetPreferredSize(float* _width, float* _height)
417{
418	BSize size = MinSize();
419	if (_width != NULL)
420		*_width = size.width;
421
422	if (_height != NULL)
423		*_height = size.height;
424}
425
426
427BSize
428PaneSwitch::MinSize()
429{
430	BSize size;
431	float onLabelWidth = StringWidth(fLabelOn);
432	float offLabelWidth = StringWidth(fLabelOff);
433	float labelWidth = max_c(onLabelWidth, offLabelWidth);
434	size.width = sLatchSize;
435	if (labelWidth > 0.0)
436		size.width += ceilf(sLatchSize / 2.0) + labelWidth;
437
438	font_height fontHeight;
439	GetFontHeight(&fontHeight);
440	size.height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
441	size.height = max_c(size.height, sLatchSize);
442
443	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
444}
445
446
447BSize
448PaneSwitch::MaxSize()
449{
450	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), MinSize());
451}
452
453
454BSize
455PaneSwitch::PreferredSize()
456{
457	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), MinSize());
458}
459
460
461void
462PaneSwitch::SetLabels(const char* labelOn, const char* labelOff)
463{
464	free(fLabelOn);
465	free(fLabelOff);
466
467	if (labelOn != NULL)
468		fLabelOn = strdup(labelOn);
469	else
470		fLabelOn = NULL;
471
472	if (labelOff != NULL)
473		fLabelOff = strdup(labelOff);
474	else
475		fLabelOff = NULL;
476
477	Invalidate();
478	InvalidateLayout();
479}
480
481
482void
483PaneSwitch::DoneTracking(BPoint point)
484{
485	BRect bounds(Bounds());
486	bounds.InsetBy(-3, -3);
487
488	fPressing = false;
489	Invalidate();
490	if (bounds.Contains(point)) {
491		SetValue(!Value());
492		Invoke();
493	}
494}
495
496
497void
498PaneSwitch::Track(BPoint point, uint32)
499{
500	BRect bounds(Bounds());
501	bounds.InsetBy(-3, -3);
502
503	bool newPressing = bounds.Contains(point);
504	if (newPressing != fPressing) {
505		fPressing = newPressing;
506		Invalidate();
507	}
508}
509
510
511void
512PaneSwitch::DrawInState(PaneSwitch::State state)
513{
514	BRect rect(0, 0, be_plain_font->Size(), be_plain_font->Size());
515	rect.OffsetBy(1, 1);
516
517	rgb_color arrowColor = state == kPressed ? kHighlightColor : kNormalColor;
518	int32 arrowDirection = BControlLook::B_RIGHT_ARROW;
519	float tint = IsEnabled() && Window()->IsActive() ? B_DARKEN_3_TINT
520		: B_DARKEN_1_TINT;
521
522	switch (state) {
523		case kCollapsed:
524			arrowDirection = BControlLook::B_RIGHT_ARROW;
525			break;
526
527		case kPressed:
528			arrowDirection = BControlLook::B_RIGHT_DOWN_ARROW;
529			break;
530
531		case kExpanded:
532			arrowDirection = BControlLook::B_DOWN_ARROW;
533			break;
534	}
535
536	SetDrawingMode(B_OP_COPY);
537	be_control_look->DrawArrowShape(this, rect, rect, arrowColor,
538		arrowDirection, 0, tint);
539}
540