1/*
2 * Copyright 2012, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Marc Flerackers (mflerackers@androme.be)
7 *		Stefano Ceccherini (stefano.ceccherini@gmail.com)
8 *		John Scipione (jscipione@gmail.com)
9 */
10
11
12#include "InlineScrollView.h"
13
14#include <ControlLook.h>
15#include <Debug.h>
16#include <InterfaceDefs.h>
17#include <Menu.h>
18#include <Point.h>
19#include <Screen.h>
20#include <Window.h>
21
22
23const int kDefaultScrollStep = 19;
24const int kScrollerDimension = 12;
25
26
27class ScrollArrow : public BView {
28public:
29							ScrollArrow(BRect frame);
30	virtual					~ScrollArrow();
31
32			bool			IsEnabled() const { return fEnabled; };
33			void			SetEnabled(bool enabled);
34
35private:
36			bool			fEnabled;
37};
38
39
40class UpScrollArrow : public ScrollArrow {
41public:
42							UpScrollArrow(BRect frame);
43	virtual					~UpScrollArrow();
44
45	virtual	void			Draw(BRect updateRect);
46	virtual	void			MouseDown(BPoint where);
47};
48
49
50class DownScrollArrow : public ScrollArrow {
51public:
52							DownScrollArrow(BRect frame);
53	virtual					~DownScrollArrow();
54
55	virtual	void			Draw(BRect updateRect);
56	virtual	void			MouseDown(BPoint where);
57};
58
59
60class LeftScrollArrow : public ScrollArrow {
61public:
62							LeftScrollArrow(BRect frame);
63	virtual					~LeftScrollArrow();
64
65	virtual	void			Draw(BRect updateRect);
66	virtual	void			MouseDown(BPoint where);
67};
68
69
70class RightScrollArrow : public ScrollArrow {
71public:
72							RightScrollArrow(BRect frame);
73	virtual					~RightScrollArrow();
74
75	virtual	void			Draw(BRect updateRect);
76	virtual	void			MouseDown(BPoint where);
77};
78
79
80//	#pragma mark -
81
82
83ScrollArrow::ScrollArrow(BRect frame)
84	:
85	BView(frame, "menu scroll arrow", B_FOLLOW_NONE, B_WILL_DRAW),
86	fEnabled(false)
87{
88	SetViewUIColor(B_MENU_BACKGROUND_COLOR);
89}
90
91
92ScrollArrow::~ScrollArrow()
93{
94}
95
96
97void
98ScrollArrow::SetEnabled(bool enabled)
99{
100	fEnabled = enabled;
101	Invalidate();
102}
103
104
105//	#pragma mark -
106
107
108UpScrollArrow::UpScrollArrow(BRect frame)
109	:
110	ScrollArrow(frame)
111{
112}
113
114
115UpScrollArrow::~UpScrollArrow()
116{
117}
118
119
120void
121UpScrollArrow::Draw(BRect updateRect)
122{
123	SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
124		B_DARKEN_1_TINT));
125
126	if (IsEnabled())
127		SetHighColor(0, 0, 0);
128	else {
129		SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
130			B_DARKEN_2_TINT));
131	}
132
133	FillRect(Bounds(), B_SOLID_LOW);
134
135	float middle = Bounds().right / 2;
136	FillTriangle(BPoint(middle, (kScrollerDimension / 2) - 3),
137		BPoint(middle + 5, (kScrollerDimension / 2) + 2),
138		BPoint(middle - 5, (kScrollerDimension / 2) + 2));
139}
140
141
142void
143UpScrollArrow::MouseDown(BPoint where)
144{
145	if (!IsEnabled())
146		return;
147
148	TInlineScrollView* parent = dynamic_cast<TInlineScrollView*>(Parent());
149	if (parent == NULL)
150		return;
151
152	float smallStep;
153	float largeStep;
154	parent->GetSteps(&smallStep, &largeStep);
155
156	BMessage* message = Window()->CurrentMessage();
157	int32 modifiers = 0;
158	message->FindInt32("modifiers", &modifiers);
159	// pressing the shift key scrolls faster
160	if ((modifiers & B_SHIFT_KEY) != 0)
161		parent->ScrollBy(-largeStep);
162	else
163		parent->ScrollBy(-smallStep);
164
165	snooze(5000);
166}
167
168
169//	#pragma mark -
170
171
172DownScrollArrow::DownScrollArrow(BRect frame)
173	:
174	ScrollArrow(frame)
175{
176}
177
178
179DownScrollArrow::~DownScrollArrow()
180{
181}
182
183
184void
185DownScrollArrow::Draw(BRect updateRect)
186{
187	SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
188		B_DARKEN_1_TINT));
189
190	if (IsEnabled())
191		SetHighColor(0, 0, 0);
192	else {
193		SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
194			B_DARKEN_2_TINT));
195	}
196
197	BRect frame = Bounds();
198	FillRect(frame, B_SOLID_LOW);
199
200	float middle = Bounds().right / 2;
201	FillTriangle(BPoint(middle, frame.bottom - (kScrollerDimension / 2) + 3),
202		BPoint(middle + 5, frame.bottom - (kScrollerDimension / 2) - 2),
203		BPoint(middle - 5, frame.bottom - (kScrollerDimension / 2) - 2));
204}
205
206
207void
208DownScrollArrow::MouseDown(BPoint where)
209{
210	if (!IsEnabled())
211		return;
212
213	TInlineScrollView* grandparent
214		= dynamic_cast<TInlineScrollView*>(Parent()->Parent());
215	if (grandparent == NULL)
216		return;
217
218	float smallStep;
219	float largeStep;
220	grandparent->GetSteps(&smallStep, &largeStep);
221
222	BMessage* message = Window()->CurrentMessage();
223	int32 modifiers = 0;
224	message->FindInt32("modifiers", &modifiers);
225	// pressing the shift key scrolls faster
226	if ((modifiers & B_SHIFT_KEY) != 0)
227		grandparent->ScrollBy(largeStep);
228	else
229		grandparent->ScrollBy(smallStep);
230
231	snooze(5000);
232}
233
234
235//	#pragma mark -
236
237
238LeftScrollArrow::LeftScrollArrow(BRect frame)
239	:
240	ScrollArrow(frame)
241{
242}
243
244
245LeftScrollArrow::~LeftScrollArrow()
246{
247}
248
249
250void
251LeftScrollArrow::Draw(BRect updateRect)
252{
253	SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT));
254
255	if (IsEnabled())
256		SetHighColor(0, 0, 0);
257	else {
258		SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
259			B_DARKEN_2_TINT));
260	}
261
262	FillRect(Bounds(), B_SOLID_LOW);
263
264	float middle = Bounds().bottom / 2;
265	FillTriangle(BPoint((kScrollerDimension / 2) - 3, middle),
266		BPoint((kScrollerDimension / 2) + 2, middle + 5),
267		BPoint((kScrollerDimension / 2) + 2, middle - 5));
268}
269
270
271void
272LeftScrollArrow::MouseDown(BPoint where)
273{
274	if (!IsEnabled())
275		return;
276
277	TInlineScrollView* parent = dynamic_cast<TInlineScrollView*>(Parent());
278	if (parent == NULL)
279		return;
280
281	float smallStep;
282	float largeStep;
283	parent->GetSteps(&smallStep, &largeStep);
284
285	BMessage* message = Window()->CurrentMessage();
286	int32 modifiers = 0;
287	message->FindInt32("modifiers", &modifiers);
288	// pressing the shift key scrolls faster
289	if ((modifiers & B_SHIFT_KEY) != 0)
290		parent->ScrollBy(-largeStep);
291	else
292		parent->ScrollBy(-smallStep);
293
294	snooze(5000);
295}
296
297
298//	#pragma mark -
299
300
301RightScrollArrow::RightScrollArrow(BRect frame)
302	:
303	ScrollArrow(frame)
304{
305}
306
307
308RightScrollArrow::~RightScrollArrow()
309{
310}
311
312
313void
314RightScrollArrow::Draw(BRect updateRect)
315{
316	SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT));
317
318	if (IsEnabled())
319		SetHighColor(0, 0, 0);
320	else {
321		SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
322			B_DARKEN_2_TINT));
323	}
324
325	BRect frame = Bounds();
326	FillRect(frame, B_SOLID_LOW);
327
328	float middle = Bounds().bottom / 2;
329	FillTriangle(BPoint(kScrollerDimension / 2 + 3, middle),
330		BPoint(kScrollerDimension / 2 - 2, middle + 5),
331		BPoint(kScrollerDimension / 2 - 2, middle - 5));
332}
333
334
335void
336RightScrollArrow::MouseDown(BPoint where)
337{
338	if (!IsEnabled())
339		return;
340
341	TInlineScrollView* grandparent
342		= dynamic_cast<TInlineScrollView*>(Parent()->Parent());
343	if (grandparent == NULL)
344		return;
345
346	float smallStep;
347	float largeStep;
348	grandparent->GetSteps(&smallStep, &largeStep);
349
350	BMessage* message = Window()->CurrentMessage();
351	int32 modifiers = 0;
352	message->FindInt32("modifiers", &modifiers);
353	// pressing the shift key scrolls faster
354	if ((modifiers & B_SHIFT_KEY) != 0)
355		grandparent->ScrollBy(largeStep);
356	else
357		grandparent->ScrollBy(smallStep);
358
359	snooze(5000);
360}
361
362
363//	#pragma mark -
364
365
366TInlineScrollView::TInlineScrollView(BView* target,
367	enum orientation orientation)
368	:
369	BView(BRect(0, 0, 0, 0), "inline scroll view", B_FOLLOW_NONE, B_WILL_DRAW),
370	fTarget(target),
371	fBeginScrollArrow(NULL),
372	fEndScrollArrow(NULL),
373	fScrollStep(kDefaultScrollStep),
374	fScrollValue(0),
375	fScrollLimit(0),
376	fOrientation(orientation)
377{
378}
379
380
381TInlineScrollView::~TInlineScrollView()
382{
383	if (fBeginScrollArrow != NULL) {
384		fBeginScrollArrow->RemoveSelf();
385		delete fBeginScrollArrow;
386		fBeginScrollArrow = NULL;
387	}
388
389	if (fEndScrollArrow != NULL) {
390		fEndScrollArrow->RemoveSelf();
391		delete fEndScrollArrow;
392		fEndScrollArrow = NULL;
393	}
394}
395
396
397void
398TInlineScrollView::AttachedToWindow()
399{
400	BView::AttachedToWindow();
401
402	if (fTarget == NULL)
403		return;
404
405	AddChild(fTarget);
406	fTarget->MoveTo(0, 0);
407}
408
409
410void
411TInlineScrollView::DetachedFromWindow()
412{
413	BView::DetachedFromWindow();
414
415	if (fTarget != NULL)
416		fTarget->RemoveSelf();
417
418	if (fBeginScrollArrow != NULL)
419		fBeginScrollArrow->RemoveSelf();
420
421	if (fEndScrollArrow != NULL)
422		fEndScrollArrow->RemoveSelf();
423}
424
425
426void
427TInlineScrollView::Draw(BRect updateRect)
428{
429	BRect frame = Bounds();
430	be_control_look->DrawButtonBackground(this, frame, updateRect,
431		ui_color(B_MENU_BACKGROUND_COLOR));
432}
433
434
435//	#pragma mark -
436
437
438void
439TInlineScrollView::AttachScrollers()
440{
441	if (fTarget == NULL)
442		return;
443
444	BRect frame = Bounds();
445
446	if (HasScrollers()) {
447		if (fOrientation == B_VERTICAL) {
448			fScrollLimit = fTarget->Bounds().Height()
449				- (frame.Height() - 2 * kScrollerDimension);
450		} else {
451			fScrollLimit = fTarget->Bounds().Width()
452				- (frame.Width() - 2 * kScrollerDimension);
453		}
454
455		if (fScrollValue > fScrollLimit) {
456			// If scroll value is above limit scroll back
457			float delta = fScrollLimit - fScrollValue;
458			if (fOrientation == B_VERTICAL)
459				fTarget->ScrollBy(0, delta);
460			else
461				fTarget->ScrollBy(delta, 0);
462
463			fScrollValue = fScrollLimit;
464		}
465		return;
466	}
467
468	fTarget->MakeFocus(true);
469
470	if (fOrientation == B_VERTICAL) {
471		if (fBeginScrollArrow == NULL) {
472			fBeginScrollArrow = new UpScrollArrow(
473				BRect(frame.left, frame.top, frame.right,
474					kScrollerDimension - 1));
475			AddChild(fBeginScrollArrow);
476		}
477
478		if (fEndScrollArrow == NULL) {
479			fEndScrollArrow = new DownScrollArrow(
480				BRect(0, frame.bottom - 2 * kScrollerDimension + 1, frame.right,
481					frame.bottom - kScrollerDimension));
482			fTarget->AddChild(fEndScrollArrow);
483		}
484
485		fTarget->MoveBy(0, kScrollerDimension);
486
487		fScrollLimit = fTarget->Bounds().Height()
488			- (frame.Height() - 2 * kScrollerDimension);
489	} else {
490		if (fBeginScrollArrow == NULL) {
491			fBeginScrollArrow = new LeftScrollArrow(
492				BRect(frame.left, frame.top,
493					frame.left + kScrollerDimension - 1, frame.bottom));
494			AddChild(fBeginScrollArrow);
495		}
496
497		if (fEndScrollArrow == NULL) {
498			fEndScrollArrow = new RightScrollArrow(
499				BRect(frame.right - 2 * kScrollerDimension + 1, frame.top,
500					frame.right, frame.bottom));
501			fTarget->AddChild(fEndScrollArrow);
502		}
503
504		fTarget->MoveBy(kScrollerDimension, 0);
505
506		fScrollLimit = fTarget->Bounds().Width()
507			- (frame.Width() - 2 * kScrollerDimension);
508	}
509
510	fBeginScrollArrow->SetEnabled(false);
511	fEndScrollArrow->SetEnabled(true);
512
513	fScrollValue = 0;
514}
515
516
517void
518TInlineScrollView::DetachScrollers()
519{
520	if (!HasScrollers())
521		return;
522
523	if (fEndScrollArrow) {
524		fEndScrollArrow->RemoveSelf();
525		delete fEndScrollArrow;
526		fEndScrollArrow = NULL;
527	}
528
529	if (fBeginScrollArrow) {
530		fBeginScrollArrow->RemoveSelf();
531		delete fBeginScrollArrow;
532		fBeginScrollArrow = NULL;
533	}
534
535	if (fTarget) {
536		// We don't remember the position where the last scrolling
537		// ended, so scroll back to the beginning.
538		if (fOrientation == B_VERTICAL)
539			fTarget->MoveBy(0, -kScrollerDimension);
540		else
541			fTarget->MoveBy(-kScrollerDimension, 0);
542
543		fTarget->ScrollTo(0, 0);
544		fScrollValue = 0;
545	}
546}
547
548
549bool
550TInlineScrollView::HasScrollers() const
551{
552	return fTarget != NULL && fBeginScrollArrow != NULL
553		&& fEndScrollArrow != NULL;
554}
555
556
557void
558TInlineScrollView::SetSmallStep(float step)
559{
560	fScrollStep = step;
561}
562
563
564void
565TInlineScrollView::GetSteps(float* _smallStep, float* _largeStep) const
566{
567	if (_smallStep != NULL)
568		*_smallStep = fScrollStep;
569	if (_largeStep != NULL) {
570		*_largeStep = fScrollStep * 3;
571	}
572}
573
574
575void
576TInlineScrollView::ScrollBy(const float& step)
577{
578	if (!HasScrollers())
579		return;
580
581	if (step > 0) {
582		if (fScrollValue == 0)
583			fBeginScrollArrow->SetEnabled(true);
584
585		if (fScrollValue + step >= fScrollLimit) {
586			// If we reached the limit, only scroll to the end
587			if (fOrientation == B_VERTICAL) {
588				fTarget->ScrollBy(0, fScrollLimit - fScrollValue);
589				fEndScrollArrow->MoveBy(0, fScrollLimit - fScrollValue);
590			} else {
591				fTarget->ScrollBy(fScrollLimit - fScrollValue, 0);
592				fEndScrollArrow->MoveBy(fScrollLimit - fScrollValue, 0);
593			}
594			fEndScrollArrow->SetEnabled(false);
595			fScrollValue = fScrollLimit;
596		} else {
597			if (fOrientation == B_VERTICAL) {
598				fTarget->ScrollBy(0, step);
599				fEndScrollArrow->MoveBy(0, step);
600			} else {
601				fTarget->ScrollBy(step, 0);
602				fEndScrollArrow->MoveBy(step, 0);
603			}
604			fScrollValue += step;
605		}
606	} else if (step < 0) {
607		if (fScrollValue == fScrollLimit)
608			fEndScrollArrow->SetEnabled(true);
609
610		if (fScrollValue + step <= 0) {
611			if (fOrientation == B_VERTICAL) {
612				fTarget->ScrollBy(0, -fScrollValue);
613				fEndScrollArrow->MoveBy(0, -fScrollValue);
614			} else {
615				fTarget->ScrollBy(-fScrollValue, 0);
616				fEndScrollArrow->MoveBy(-fScrollValue, 0);
617			}
618			fBeginScrollArrow->SetEnabled(false);
619			fScrollValue = 0;
620		} else {
621			if (fOrientation == B_VERTICAL) {
622				fTarget->ScrollBy(0, step);
623				fEndScrollArrow->MoveBy(0, step);
624			} else {
625				fTarget->ScrollBy(step, 0);
626				fEndScrollArrow->MoveBy(step, 0);
627			}
628			fScrollValue += step;
629		}
630	}
631
632	//fTarget->Invalidate();
633}
634