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	SetViewColor(ui_color(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 option/command/control key scrolls faster
160	if ((modifiers & (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_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 option/command/control key scrolls faster
226	if ((modifiers & (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_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 option/command/control key scrolls faster
289	if ((modifiers & (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_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 option/command/control key scrolls faster
354	if ((modifiers & (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_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(BRect frame, BView* target,
367	enum orientation orientation)
368	:
369	BView(frame, "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		return;
455	}
456
457	fTarget->MakeFocus(true);
458
459	if (fOrientation == B_VERTICAL) {
460		if (fBeginScrollArrow == NULL) {
461			fBeginScrollArrow = new UpScrollArrow(
462				BRect(frame.left, frame.top, frame.right,
463					kScrollerDimension - 1));
464			AddChild(fBeginScrollArrow);
465		}
466
467		if (fEndScrollArrow == NULL) {
468			fEndScrollArrow = new DownScrollArrow(
469				BRect(0, frame.bottom - 2 * kScrollerDimension + 1, frame.right,
470					frame.bottom - kScrollerDimension));
471			fTarget->AddChild(fEndScrollArrow);
472		}
473
474		fTarget->MoveBy(0, kScrollerDimension);
475
476		fScrollLimit = fTarget->Bounds().Height()
477			- (frame.Height() - 2 * kScrollerDimension);
478	} else {
479		if (fBeginScrollArrow == NULL) {
480			fBeginScrollArrow = new LeftScrollArrow(
481				BRect(frame.left, frame.top,
482					frame.left + kScrollerDimension - 1, frame.bottom));
483			AddChild(fBeginScrollArrow);
484		}
485
486		if (fEndScrollArrow == NULL) {
487			fEndScrollArrow = new RightScrollArrow(
488				BRect(frame.right - 2 * kScrollerDimension + 1, frame.top,
489					frame.right, frame.bottom));
490			fTarget->AddChild(fEndScrollArrow);
491		}
492
493		fTarget->MoveBy(kScrollerDimension, 0);
494
495		fScrollLimit = fTarget->Bounds().Width()
496			- (frame.Width() - 2 * kScrollerDimension);
497	}
498
499	fBeginScrollArrow->SetEnabled(false);
500	fEndScrollArrow->SetEnabled(true);
501
502	fScrollValue = 0;
503}
504
505
506void
507TInlineScrollView::DetachScrollers()
508{
509	if (!HasScrollers())
510		return;
511
512	if (fEndScrollArrow) {
513		fEndScrollArrow->RemoveSelf();
514		delete fEndScrollArrow;
515		fEndScrollArrow = NULL;
516	}
517
518	if (fBeginScrollArrow) {
519		fBeginScrollArrow->RemoveSelf();
520		delete fBeginScrollArrow;
521		fBeginScrollArrow = NULL;
522	}
523
524	if (fTarget) {
525		// We don't remember the position where the last scrolling
526		// ended, so scroll back to the beginning.
527		if (fOrientation == B_VERTICAL)
528			fTarget->MoveBy(0, -kScrollerDimension);
529		else
530			fTarget->MoveBy(-kScrollerDimension, 0);
531
532		fTarget->ScrollTo(0, 0);
533		fScrollValue = 0;
534	}
535}
536
537
538bool
539TInlineScrollView::HasScrollers() const
540{
541	return fTarget != NULL && fBeginScrollArrow != NULL
542		&& fEndScrollArrow != NULL;
543}
544
545
546void
547TInlineScrollView::SetSmallStep(float step)
548{
549	fScrollStep = step;
550}
551
552
553void
554TInlineScrollView::GetSteps(float* _smallStep, float* _largeStep) const
555{
556	if (_smallStep != NULL)
557		*_smallStep = fScrollStep;
558	if (_largeStep != NULL) {
559		*_largeStep = fScrollStep * 3;
560	}
561}
562
563
564void
565TInlineScrollView::ScrollBy(const float& step)
566{
567	if (!HasScrollers())
568		return;
569
570	if (step > 0) {
571		if (fScrollValue == 0)
572			fBeginScrollArrow->SetEnabled(true);
573
574		if (fScrollValue + step >= fScrollLimit) {
575			// If we reached the limit, only scroll to the end
576			if (fOrientation == B_VERTICAL) {
577				fTarget->ScrollBy(0, fScrollLimit - fScrollValue);
578				fEndScrollArrow->MoveBy(0, fScrollLimit - fScrollValue);
579			} else {
580				fTarget->ScrollBy(fScrollLimit - fScrollValue, 0);
581				fEndScrollArrow->MoveBy(fScrollLimit - fScrollValue, 0);
582			}
583			fEndScrollArrow->SetEnabled(false);
584			fScrollValue = fScrollLimit;
585		} else {
586			if (fOrientation == B_VERTICAL) {
587				fTarget->ScrollBy(0, step);
588				fEndScrollArrow->MoveBy(0, step);
589			} else {
590				fTarget->ScrollBy(step, 0);
591				fEndScrollArrow->MoveBy(step, 0);
592			}
593			fScrollValue += step;
594		}
595	} else if (step < 0) {
596		if (fScrollValue == fScrollLimit)
597			fEndScrollArrow->SetEnabled(true);
598
599		if (fScrollValue + step <= 0) {
600			if (fOrientation == B_VERTICAL) {
601				fTarget->ScrollBy(0, -fScrollValue);
602				fEndScrollArrow->MoveBy(0, -fScrollValue);
603			} else {
604				fTarget->ScrollBy(-fScrollValue, 0);
605				fEndScrollArrow->MoveBy(-fScrollValue, 0);
606			}
607			fBeginScrollArrow->SetEnabled(false);
608			fScrollValue = 0;
609		} else {
610			if (fOrientation == B_VERTICAL) {
611				fTarget->ScrollBy(0, step);
612				fEndScrollArrow->MoveBy(0, step);
613			} else {
614				fTarget->ScrollBy(step, 0);
615				fEndScrollArrow->MoveBy(step, 0);
616			}
617			fScrollValue += step;
618		}
619	}
620
621	//fTarget->Invalidate();
622}
623