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