xref: /haiku/src/kits/tracker/DialogPane.cpp (revision 97dfeb96704e5dbc5bec32ad7b21379d0125e031)
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 
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 
46 const uint32 kValueChanged = 'swch';
47 
48 const rgb_color kNormalColor = {150, 150, 150, 255};
49 const rgb_color kHighlightColor = {100, 100, 0, 255};
50 
51 
52 static void
53 AddSelf(BView* self, BView* to)
54 {
55 	to->AddChild(self);
56 }
57 
58 
59 void
60 ViewList::RemoveAll(BView*)
61 {
62 	EachListItemIgnoreResult(this, &BView::RemoveSelf);
63 }
64 
65 
66 void
67 ViewList::AddAll(BView* toParent)
68 {
69 	EachListItem(this, &AddSelf, toParent);
70 }
71 
72 
73 //	#pragma mark - DialogPane
74 
75 
76 DialogPane::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 
90 DialogPane::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 
104 DialogPane::~DialogPane()
105 {
106 	fMode3Items.RemoveAll(this);
107 	fMode2Items.RemoveAll(this);
108 }
109 
110 
111 void
112 DialogPane::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 
202 void
203 DialogPane::AttachedToWindow()
204 {
205 	AdoptParentColors();
206 }
207 
208 
209 void
210 DialogPane::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 
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 
232 	if (fMode >= toMode)
233 		AddChild(view);
234 }
235 
236 
237 BRect
238 DialogPane::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 
255 BRect
256 DialogPane::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 
278 BRect
279 DialogPane::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 
297 void
298 DialogPane::SetSwitch(BControl* control)
299 {
300 	fLatch = control;
301 	control->SetMessage(new BMessage(kValueChanged));
302 	control->SetTarget(this);
303 }
304 
305 
306 void
307 DialogPane::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 
321 PaneSwitch::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 
333 PaneSwitch::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 
344 PaneSwitch::~PaneSwitch()
345 {
346 	free(fLabelOn);
347 	free(fLabelOff);
348 }
349 
350 
351 void
352 PaneSwitch::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 
402 void
403 PaneSwitch::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 
415 void
416 PaneSwitch::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 
427 BSize
428 PaneSwitch::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 
447 BSize
448 PaneSwitch::MaxSize()
449 {
450 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), MinSize());
451 }
452 
453 
454 BSize
455 PaneSwitch::PreferredSize()
456 {
457 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), MinSize());
458 }
459 
460 
461 void
462 PaneSwitch::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 
482 void
483 PaneSwitch::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 
497 void
498 PaneSwitch::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 
511 void
512 PaneSwitch::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