1/*
2 * Copyright 2004 DarkWyrm <darkwyrm@earthlink.net>
3 * Copyright 2013 FeemanLou
4 * Copyright 2014-2015 Haiku, Inc. All rights reserved.
5 *
6 * Distributed under the terms of the MIT license.
7 *
8 * Originally written by DarkWyrm <darkwyrm@earthlink.net>
9 * Updated by FreemanLou as part of Google GCI 2013
10 *
11 * Authors:
12 *		DarkWyrm, darkwyrm@earthlink.net
13 *		FeemanLou
14 *		John Scipione, jscipione@gmail.com
15 */
16
17
18#include <AbstractSpinner.h>
19
20#include <algorithm>
21
22#include <AbstractLayoutItem.h>
23#include <Alignment.h>
24#include <ControlLook.h>
25#include <Font.h>
26#include <GradientLinear.h>
27#include <LayoutItem.h>
28#include <LayoutUtils.h>
29#include <Message.h>
30#include <MessageFilter.h>
31#include <Point.h>
32#include <PropertyInfo.h>
33#include <TextView.h>
34#include <View.h>
35#include <Window.h>
36
37#include "Thread.h"
38
39
40static const float kFrameMargin			= 2.0f;
41
42const char* const kFrameField			= "BAbstractSpinner:layoutItem:frame";
43const char* const kLabelItemField		= "BAbstractSpinner:labelItem";
44const char* const kTextViewItemField	= "BAbstractSpinner:textViewItem";
45
46
47static property_info sProperties[] = {
48	{
49		"Align",
50		{ B_GET_PROPERTY, 0 },
51		{ B_DIRECT_SPECIFIER, 0 },
52		"Returns the alignment of the spinner label.",
53		0,
54		{ B_INT32_TYPE }
55	},
56	{
57		"Align",
58		{ B_SET_PROPERTY, 0 },
59		{ B_DIRECT_SPECIFIER, 0},
60		"Sets the alignment of the spinner label.",
61		0,
62		{ B_INT32_TYPE }
63	},
64
65	{
66		"ButtonStyle",
67		{ B_GET_PROPERTY, 0 },
68		{ B_DIRECT_SPECIFIER, 0 },
69		"Returns the style of the spinner buttons.",
70		0,
71		{ B_INT32_TYPE }
72	},
73	{
74		"ButtonStyle",
75		{ B_SET_PROPERTY, 0 },
76		{ B_DIRECT_SPECIFIER, 0},
77		"Sets the style of the spinner buttons.",
78		0,
79		{ B_INT32_TYPE }
80	},
81
82	{
83		"Divider",
84		{ B_GET_PROPERTY, 0 },
85		{ B_DIRECT_SPECIFIER, 0 },
86		"Returns the divider position of the spinner.",
87		0,
88		{ B_FLOAT_TYPE }
89	},
90	{
91		"Divider",
92		{ B_SET_PROPERTY, 0 },
93		{ B_DIRECT_SPECIFIER, 0},
94		"Sets the divider position of the spinner.",
95		0,
96		{ B_FLOAT_TYPE }
97	},
98
99	{
100		"Enabled",
101		{ B_GET_PROPERTY, 0 },
102		{ B_DIRECT_SPECIFIER, 0 },
103		"Returns whether or not the spinner is enabled.",
104		0,
105		{ B_BOOL_TYPE }
106	},
107	{
108		"Enabled",
109		{ B_SET_PROPERTY, 0 },
110		{ B_DIRECT_SPECIFIER, 0},
111		"Sets whether or not the spinner is enabled.",
112		0,
113		{ B_BOOL_TYPE }
114	},
115
116	{
117		"Label",
118		{ B_GET_PROPERTY, 0 },
119		{ B_DIRECT_SPECIFIER, 0 },
120		"Returns the spinner label.",
121		0,
122		{ B_STRING_TYPE }
123	},
124	{
125		"Label",
126		{ B_SET_PROPERTY, 0 },
127		{ B_DIRECT_SPECIFIER, 0},
128		"Sets the spinner label.",
129		0,
130		{ B_STRING_TYPE }
131	},
132
133	{
134		"Message",
135		{ B_GET_PROPERTY, 0 },
136		{ B_DIRECT_SPECIFIER, 0 },
137		"Returns the spinner invocation message.",
138		0,
139		{ B_MESSAGE_TYPE }
140	},
141	{
142		"Message",
143		{ B_SET_PROPERTY, 0 },
144		{ B_DIRECT_SPECIFIER, 0},
145		"Sets the spinner invocation message.",
146		0,
147		{ B_MESSAGE_TYPE }
148	},
149
150	{ 0 }
151};
152
153
154typedef enum {
155	SPINNER_INCREMENT,
156	SPINNER_DECREMENT
157} spinner_direction;
158
159
160class SpinnerButton : public BView {
161public:
162								SpinnerButton(BRect frame, const char* name,
163									spinner_direction direction);
164	virtual						~SpinnerButton();
165
166	virtual	void				AttachedToWindow();
167	virtual	void				DetachedFromWindow();
168	virtual	void				Draw(BRect updateRect);
169	virtual	void				MouseDown(BPoint where);
170	virtual	void				MouseUp(BPoint where);
171	virtual	void				MouseMoved(BPoint where, uint32 transit,
172									const BMessage* message);
173
174			bool				IsEnabled() const { return fIsEnabled; }
175	virtual	void				SetEnabled(bool enable) { fIsEnabled = enable; };
176
177private:
178			void				_DoneTracking(BPoint where);
179			void				_Track(BPoint where, uint32);
180
181			spinner_direction	fSpinnerDirection;
182			BAbstractSpinner*	fParent;
183			bool				fIsEnabled;
184			bool				fIsMouseDown;
185			bool				fIsMouseOver;
186			bigtime_t			fRepeatDelay;
187};
188
189
190class SpinnerTextView : public BTextView {
191public:
192								SpinnerTextView(BRect rect, BRect textRect);
193	virtual						~SpinnerTextView();
194
195	virtual	void				AttachedToWindow();
196	virtual	void				DetachedFromWindow();
197	virtual	void				KeyDown(const char* bytes, int32 numBytes);
198	virtual	void				MakeFocus(bool focus);
199
200private:
201			BAbstractSpinner*	fParent;
202};
203
204
205class BAbstractSpinner::LabelLayoutItem : public BAbstractLayoutItem {
206public:
207								LabelLayoutItem(BAbstractSpinner* parent);
208								LabelLayoutItem(BMessage* archive);
209
210	virtual	bool				IsVisible();
211	virtual	void				SetVisible(bool visible);
212
213	virtual	BRect				Frame();
214	virtual	void				SetFrame(BRect frame);
215
216			void				SetParent(BAbstractSpinner* parent);
217	virtual	BView*				View();
218
219	virtual	BSize				BaseMinSize();
220	virtual	BSize				BaseMaxSize();
221	virtual	BSize				BasePreferredSize();
222	virtual	BAlignment			BaseAlignment();
223
224			BRect				FrameInParent() const;
225
226	virtual status_t			Archive(BMessage* into, bool deep = true) const;
227	static	BArchivable*		Instantiate(BMessage* from);
228
229private:
230			BAbstractSpinner*	fParent;
231			BRect				fFrame;
232};
233
234
235class BAbstractSpinner::TextViewLayoutItem : public BAbstractLayoutItem {
236public:
237								TextViewLayoutItem(BAbstractSpinner* parent);
238								TextViewLayoutItem(BMessage* archive);
239
240	virtual	bool				IsVisible();
241	virtual	void				SetVisible(bool visible);
242
243	virtual	BRect				Frame();
244	virtual	void				SetFrame(BRect frame);
245
246			void				SetParent(BAbstractSpinner* parent);
247	virtual	BView*				View();
248
249	virtual	BSize				BaseMinSize();
250	virtual	BSize				BaseMaxSize();
251	virtual	BSize				BasePreferredSize();
252	virtual	BAlignment			BaseAlignment();
253
254			BRect				FrameInParent() const;
255
256	virtual status_t			Archive(BMessage* into, bool deep = true) const;
257	static	BArchivable*		Instantiate(BMessage* from);
258
259private:
260			BAbstractSpinner*	fParent;
261			BRect				fFrame;
262};
263
264
265struct BAbstractSpinner::LayoutData {
266	LayoutData(float width, float height)
267	:
268	label_layout_item(NULL),
269	text_view_layout_item(NULL),
270	label_width(0),
271	label_height(0),
272	text_view_width(0),
273	text_view_height(0),
274	previous_width(width),
275	previous_height(height),
276	valid(false)
277	{
278	}
279
280	LabelLayoutItem* label_layout_item;
281	TextViewLayoutItem* text_view_layout_item;
282
283	font_height font_info;
284
285	float label_width;
286	float label_height;
287	float text_view_width;
288	float text_view_height;
289
290	float previous_width;
291	float previous_height;
292
293	BSize min;
294	BAlignment alignment;
295
296	bool valid;
297};
298
299
300//	#pragma mark - SpinnerButton
301
302
303SpinnerButton::SpinnerButton(BRect frame, const char* name,
304	spinner_direction direction)
305	:
306	BView(frame, name, B_FOLLOW_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW),
307	fSpinnerDirection(direction),
308	fParent(NULL),
309	fIsEnabled(true),
310	fIsMouseDown(false),
311	fIsMouseOver(false),
312	fRepeatDelay(100000)
313{
314}
315
316
317SpinnerButton::~SpinnerButton()
318{
319}
320
321
322void
323SpinnerButton::AttachedToWindow()
324{
325	fParent = static_cast<BAbstractSpinner*>(Parent());
326
327	AdoptParentColors();
328	BView::AttachedToWindow();
329}
330
331
332void
333SpinnerButton::DetachedFromWindow()
334{
335	fParent = NULL;
336
337	BView::DetachedFromWindow();
338}
339
340
341void
342SpinnerButton::Draw(BRect updateRect)
343{
344	BRect rect(Bounds());
345	if (!rect.IsValid() || !rect.Intersects(updateRect))
346		return;
347
348	BView::Draw(updateRect);
349
350	float frameTint = fIsEnabled ? B_DARKEN_1_TINT : B_NO_TINT;
351
352	float fgTint;
353	if (!fIsEnabled)
354		fgTint = B_DARKEN_1_TINT;
355	else if (fIsMouseDown)
356		fgTint = B_DARKEN_MAX_TINT;
357	else
358		fgTint = 1.777f;	// 216 --> 48.2 (48)
359
360	float bgTint;
361	if (fIsEnabled && fIsMouseOver)
362		bgTint = B_DARKEN_1_TINT;
363	else
364		bgTint = B_NO_TINT;
365
366	rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR);
367	if (bgColor.red + bgColor.green + bgColor.blue <= 128 * 3) {
368		// if dark background make the tint lighter
369		frameTint = 2.0f - frameTint;
370		fgTint = 2.0f - fgTint;
371		bgTint = 2.0f - bgTint;
372	}
373
374	uint32 borders = be_control_look->B_TOP_BORDER
375		| be_control_look->B_BOTTOM_BORDER;
376
377	if (fSpinnerDirection == SPINNER_INCREMENT)
378		borders |= be_control_look->B_RIGHT_BORDER;
379	else
380		borders |= be_control_look->B_LEFT_BORDER;
381
382	uint32 flags = fIsMouseDown ? BControlLook::B_ACTIVATED : 0;
383	flags |= !fIsEnabled ? BControlLook::B_DISABLED : 0;
384
385	// draw the button
386	be_control_look->DrawButtonFrame(this, rect, updateRect,
387		tint_color(bgColor, frameTint), bgColor, flags, borders);
388	be_control_look->DrawButtonBackground(this, rect, updateRect,
389		tint_color(bgColor, bgTint), flags, borders);
390
391	switch (fParent->ButtonStyle()) {
392		case SPINNER_BUTTON_HORIZONTAL_ARROWS:
393		{
394			int32 arrowDirection = fSpinnerDirection == SPINNER_INCREMENT
395				? be_control_look->B_RIGHT_ARROW
396				: be_control_look->B_LEFT_ARROW;
397
398			rect.InsetBy(0.0f, 1.0f);
399			be_control_look->DrawArrowShape(this, rect, updateRect, bgColor,
400				arrowDirection, 0, fgTint);
401			break;
402		}
403
404		case SPINNER_BUTTON_VERTICAL_ARROWS:
405		{
406			int32 arrowDirection = fSpinnerDirection == SPINNER_INCREMENT
407				? be_control_look->B_UP_ARROW
408				: be_control_look->B_DOWN_ARROW;
409
410			rect.InsetBy(0.0f, 1.0f);
411			be_control_look->DrawArrowShape(this, rect, updateRect, bgColor,
412				arrowDirection, 0, fgTint);
413			break;
414		}
415
416		default:
417		case SPINNER_BUTTON_PLUS_MINUS:
418		{
419			BFont font;
420			fParent->GetFont(&font);
421			float inset = floorf(font.Size() / 4);
422			rect.InsetBy(inset, inset);
423
424			if (rect.IntegerWidth() % 2 != 0)
425				rect.right -= 1;
426
427			if (rect.IntegerHeight() % 2 != 0)
428				rect.bottom -= 1;
429
430			SetHighColor(tint_color(bgColor, fgTint));
431
432			// draw the +/-
433			float halfHeight = floorf(rect.Height() / 2);
434			StrokeLine(BPoint(rect.left, rect.top + halfHeight),
435				BPoint(rect.right, rect.top + halfHeight));
436			if (fSpinnerDirection == SPINNER_INCREMENT) {
437				float halfWidth = floorf(rect.Width() / 2);
438				StrokeLine(BPoint(rect.left + halfWidth, rect.top + 1),
439					BPoint(rect.left + halfWidth, rect.bottom - 1));
440			}
441		}
442	}
443}
444
445
446void
447SpinnerButton::MouseDown(BPoint where)
448{
449	if (fIsEnabled) {
450		fIsMouseDown = true;
451		Invalidate();
452		fRepeatDelay = 100000;
453		MouseDownThread<SpinnerButton>::TrackMouse(this,
454			&SpinnerButton::_DoneTracking, &SpinnerButton::_Track);
455	}
456
457	BView::MouseDown(where);
458}
459
460
461void
462SpinnerButton::MouseMoved(BPoint where, uint32 transit,
463	const BMessage* message)
464{
465	switch (transit) {
466		case B_ENTERED_VIEW:
467		case B_INSIDE_VIEW:
468		{
469			BPoint where;
470			uint32 buttons;
471			GetMouse(&where, &buttons);
472			fIsMouseOver = Bounds().Contains(where) && buttons == 0;
473			if (!fIsMouseDown)
474				Invalidate();
475
476			break;
477		}
478
479		case B_EXITED_VIEW:
480		case B_OUTSIDE_VIEW:
481			fIsMouseOver = false;
482			MouseUp(Bounds().LeftTop());
483			break;
484	}
485
486	BView::MouseMoved(where, transit, message);
487}
488
489
490void
491SpinnerButton::MouseUp(BPoint where)
492{
493	fIsMouseDown = false;
494	Invalidate();
495
496	BView::MouseUp(where);
497}
498
499
500//	#pragma mark  - SpinnerButton private methods
501
502
503void
504SpinnerButton::_DoneTracking(BPoint where)
505{
506	if (fIsMouseDown || !Bounds().Contains(where))
507		fIsMouseDown = false;
508}
509
510
511void
512SpinnerButton::_Track(BPoint where, uint32)
513{
514	if (fParent == NULL || !Bounds().Contains(where)) {
515		fIsMouseDown = false;
516		return;
517	}
518	fIsMouseDown = true;
519
520	fSpinnerDirection == SPINNER_INCREMENT
521		? fParent->Increment()
522		: fParent->Decrement();
523
524	snooze(fRepeatDelay);
525	fRepeatDelay = 10000;
526}
527
528
529//	#pragma mark - SpinnerTextView
530
531
532SpinnerTextView::SpinnerTextView(BRect rect, BRect textRect)
533	:
534	BTextView(rect, "textview", textRect, B_FOLLOW_ALL,
535		B_WILL_DRAW | B_NAVIGABLE),
536	fParent(NULL)
537{
538	MakeResizable(true);
539}
540
541
542SpinnerTextView::~SpinnerTextView()
543{
544}
545
546
547void
548SpinnerTextView::AttachedToWindow()
549{
550	fParent = static_cast<BAbstractSpinner*>(Parent());
551
552	BTextView::AttachedToWindow();
553}
554
555
556void
557SpinnerTextView::DetachedFromWindow()
558{
559	fParent = NULL;
560
561	BTextView::DetachedFromWindow();
562}
563
564
565void
566SpinnerTextView::KeyDown(const char* bytes, int32 numBytes)
567{
568	if (fParent == NULL) {
569		BTextView::KeyDown(bytes, numBytes);
570		return;
571	}
572
573	switch (bytes[0]) {
574		case B_ENTER:
575		case B_SPACE:
576			fParent->SetValueFromText();
577			break;
578
579		case B_TAB:
580			fParent->KeyDown(bytes, numBytes);
581			break;
582
583		case B_LEFT_ARROW:
584			if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS
585				&& (modifiers() & B_CONTROL_KEY) != 0) {
586				// need to hold down control, otherwise can't move cursor
587				fParent->Decrement();
588			} else
589				BTextView::KeyDown(bytes, numBytes);
590			break;
591
592		case B_UP_ARROW:
593			if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS)
594				fParent->Increment();
595			else
596				BTextView::KeyDown(bytes, numBytes);
597			break;
598
599		case B_RIGHT_ARROW:
600			if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS
601				&& (modifiers() & B_CONTROL_KEY) != 0) {
602				// need to hold down control, otherwise can't move cursor
603				fParent->Increment();
604			} else
605				BTextView::KeyDown(bytes, numBytes);
606			break;
607
608		case B_DOWN_ARROW:
609			if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS)
610				fParent->Decrement();
611			else
612				BTextView::KeyDown(bytes, numBytes);
613			break;
614
615		default:
616			BTextView::KeyDown(bytes, numBytes);
617			break;
618	}
619}
620
621
622void
623SpinnerTextView::MakeFocus(bool focus)
624{
625	BTextView::MakeFocus(focus);
626
627	if (fParent == NULL)
628		return;
629
630	if (focus)
631		SelectAll();
632	else
633		fParent->SetValueFromText();
634
635	fParent->_DrawTextView(fParent->Bounds());
636}
637
638
639//	#pragma mark - BAbstractSpinner::LabelLayoutItem
640
641
642BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BAbstractSpinner* parent)
643	:
644	fParent(parent),
645	fFrame()
646{
647}
648
649
650BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BMessage* from)
651	:
652	BAbstractLayoutItem(from),
653	fParent(NULL),
654	fFrame()
655{
656	from->FindRect(kFrameField, &fFrame);
657}
658
659
660bool
661BAbstractSpinner::LabelLayoutItem::IsVisible()
662{
663	return !fParent->IsHidden(fParent);
664}
665
666
667void
668BAbstractSpinner::LabelLayoutItem::SetVisible(bool visible)
669{
670}
671
672
673BRect
674BAbstractSpinner::LabelLayoutItem::Frame()
675{
676	return fFrame;
677}
678
679
680void
681BAbstractSpinner::LabelLayoutItem::SetFrame(BRect frame)
682{
683	fFrame = frame;
684	fParent->_UpdateFrame();
685}
686
687
688void
689BAbstractSpinner::LabelLayoutItem::SetParent(BAbstractSpinner* parent)
690{
691	fParent = parent;
692}
693
694
695BView*
696BAbstractSpinner::LabelLayoutItem::View()
697{
698	return fParent;
699}
700
701
702BSize
703BAbstractSpinner::LabelLayoutItem::BaseMinSize()
704{
705	fParent->_ValidateLayoutData();
706
707	if (fParent->Label() == NULL)
708		return BSize(-1.0f, -1.0f);
709
710	return BSize(fParent->fLayoutData->label_width
711			+ be_control_look->DefaultLabelSpacing(),
712		fParent->fLayoutData->label_height);
713}
714
715
716BSize
717BAbstractSpinner::LabelLayoutItem::BaseMaxSize()
718{
719	return BaseMinSize();
720}
721
722
723BSize
724BAbstractSpinner::LabelLayoutItem::BasePreferredSize()
725{
726	return BaseMinSize();
727}
728
729
730BAlignment
731BAbstractSpinner::LabelLayoutItem::BaseAlignment()
732{
733	return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
734}
735
736
737BRect
738BAbstractSpinner::LabelLayoutItem::FrameInParent() const
739{
740	return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top);
741}
742
743
744status_t
745BAbstractSpinner::LabelLayoutItem::Archive(BMessage* into, bool deep) const
746{
747	BArchiver archiver(into);
748	status_t result = BAbstractLayoutItem::Archive(into, deep);
749
750	if (result == B_OK)
751		result = into->AddRect(kFrameField, fFrame);
752
753	return archiver.Finish(result);
754}
755
756
757BArchivable*
758BAbstractSpinner::LabelLayoutItem::Instantiate(BMessage* from)
759{
760	if (validate_instantiation(from, "BAbstractSpinner::LabelLayoutItem"))
761		return new LabelLayoutItem(from);
762
763	return NULL;
764}
765
766
767//	#pragma mark - BAbstractSpinner::TextViewLayoutItem
768
769
770BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BAbstractSpinner* parent)
771	:
772	fParent(parent),
773	fFrame()
774{
775	SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
776}
777
778
779BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BMessage* from)
780	:
781	BAbstractLayoutItem(from),
782	fParent(NULL),
783	fFrame()
784{
785	from->FindRect(kFrameField, &fFrame);
786}
787
788
789bool
790BAbstractSpinner::TextViewLayoutItem::IsVisible()
791{
792	return !fParent->IsHidden(fParent);
793}
794
795
796void
797BAbstractSpinner::TextViewLayoutItem::SetVisible(bool visible)
798{
799	// not allowed
800}
801
802
803BRect
804BAbstractSpinner::TextViewLayoutItem::Frame()
805{
806	return fFrame;
807}
808
809
810void
811BAbstractSpinner::TextViewLayoutItem::SetFrame(BRect frame)
812{
813	fFrame = frame;
814	fParent->_UpdateFrame();
815}
816
817
818void
819BAbstractSpinner::TextViewLayoutItem::SetParent(BAbstractSpinner* parent)
820{
821	fParent = parent;
822}
823
824
825BView*
826BAbstractSpinner::TextViewLayoutItem::View()
827{
828	return fParent;
829}
830
831
832BSize
833BAbstractSpinner::TextViewLayoutItem::BaseMinSize()
834{
835	fParent->_ValidateLayoutData();
836
837	BSize size(fParent->fLayoutData->text_view_width,
838		fParent->fLayoutData->text_view_height);
839
840	return size;
841}
842
843
844BSize
845BAbstractSpinner::TextViewLayoutItem::BaseMaxSize()
846{
847	return BaseMinSize();
848}
849
850
851BSize
852BAbstractSpinner::TextViewLayoutItem::BasePreferredSize()
853{
854	return BaseMinSize();
855}
856
857
858BAlignment
859BAbstractSpinner::TextViewLayoutItem::BaseAlignment()
860{
861	return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
862}
863
864
865BRect
866BAbstractSpinner::TextViewLayoutItem::FrameInParent() const
867{
868	return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top);
869}
870
871
872status_t
873BAbstractSpinner::TextViewLayoutItem::Archive(BMessage* into, bool deep) const
874{
875	BArchiver archiver(into);
876	status_t result = BAbstractLayoutItem::Archive(into, deep);
877
878	if (result == B_OK)
879		result = into->AddRect(kFrameField, fFrame);
880
881	return archiver.Finish(result);
882}
883
884
885BArchivable*
886BAbstractSpinner::TextViewLayoutItem::Instantiate(BMessage* from)
887{
888	if (validate_instantiation(from, "BAbstractSpinner::TextViewLayoutItem"))
889		return new LabelLayoutItem(from);
890
891	return NULL;
892}
893
894
895//	#pragma mark - BAbstractSpinner
896
897
898BAbstractSpinner::BAbstractSpinner(BRect frame, const char* name, const char* label,
899	BMessage* message, uint32 resizingMode, uint32 flags)
900	:
901	BControl(frame, name, label, message, resizingMode,
902		flags | B_WILL_DRAW | B_FRAME_EVENTS)
903{
904	_InitObject();
905}
906
907
908BAbstractSpinner::BAbstractSpinner(const char* name, const char* label, BMessage* message,
909	uint32 flags)
910	:
911	BControl(name, label, message, flags | B_WILL_DRAW | B_FRAME_EVENTS)
912{
913	_InitObject();
914}
915
916
917BAbstractSpinner::BAbstractSpinner(BMessage* data)
918	:
919	BControl(data),
920	fButtonStyle(SPINNER_BUTTON_PLUS_MINUS)
921{
922	_InitObject();
923
924	if (data->FindInt32("_align") != B_OK)
925		fAlignment = B_ALIGN_LEFT;
926
927	if (data->FindInt32("_button_style") != B_OK)
928		fButtonStyle = SPINNER_BUTTON_PLUS_MINUS;
929
930	if (data->FindInt32("_divider") != B_OK)
931		fDivider = 0.0f;
932}
933
934
935BAbstractSpinner::~BAbstractSpinner()
936{
937	delete fLayoutData;
938	fLayoutData = NULL;
939}
940
941
942BArchivable*
943BAbstractSpinner::Instantiate(BMessage* data)
944{
945	// cannot instantiate an abstract spinner
946	return NULL;
947}
948
949
950status_t
951BAbstractSpinner::Archive(BMessage* data, bool deep) const
952{
953	status_t status = BControl::Archive(data, deep);
954	data->AddString("class", "Spinner");
955
956	if (status == B_OK)
957		status = data->AddInt32("_align", fAlignment);
958
959	if (status == B_OK)
960		data->AddInt32("_button_style", fButtonStyle);
961
962	if (status == B_OK)
963		status = data->AddFloat("_divider", fDivider);
964
965	return status;
966}
967
968
969status_t
970BAbstractSpinner::GetSupportedSuites(BMessage* message)
971{
972	message->AddString("suites", "suite/vnd.Haiku-spinner");
973
974	BPropertyInfo prop_info(sProperties);
975	message->AddFlat("messages", &prop_info);
976
977	return BView::GetSupportedSuites(message);
978}
979
980
981BHandler*
982BAbstractSpinner::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
983	int32 form, const char* property)
984{
985	return BView::ResolveSpecifier(message, index, specifier, form,
986		property);
987}
988
989
990void
991BAbstractSpinner::AttachedToWindow()
992{
993	if (!Messenger().IsValid())
994		SetTarget(Window());
995
996	BControl::SetValue(Value());
997		// sets the text and enables or disables the arrows
998
999	_UpdateTextViewColors(IsEnabled());
1000	fTextView->MakeEditable(IsEnabled());
1001
1002	BView::AttachedToWindow();
1003}
1004
1005
1006void
1007BAbstractSpinner::Draw(BRect updateRect)
1008{
1009	_DrawLabel(updateRect);
1010	_DrawTextView(updateRect);
1011	fIncrement->Invalidate();
1012	fDecrement->Invalidate();
1013}
1014
1015
1016void
1017BAbstractSpinner::FrameResized(float width, float height)
1018{
1019	BView::FrameResized(width, height);
1020
1021	// TODO: this causes flickering still...
1022
1023	// changes in width
1024
1025	BRect bounds = Bounds();
1026
1027	if (bounds.Width() > fLayoutData->previous_width) {
1028		// invalidate the region between the old and the new right border
1029		BRect rect = bounds;
1030		rect.left += fLayoutData->previous_width - kFrameMargin;
1031		rect.right--;
1032		Invalidate(rect);
1033	} else if (bounds.Width() < fLayoutData->previous_width) {
1034		// invalidate the region of the new right border
1035		BRect rect = bounds;
1036		rect.left = rect.right - kFrameMargin;
1037		Invalidate(rect);
1038	}
1039
1040	// changes in height
1041
1042	if (bounds.Height() > fLayoutData->previous_height) {
1043		// invalidate the region between the old and the new bottom border
1044		BRect rect = bounds;
1045		rect.top += fLayoutData->previous_height - kFrameMargin;
1046		rect.bottom--;
1047		Invalidate(rect);
1048		// invalidate label area
1049		rect = bounds;
1050		rect.right = fDivider;
1051		Invalidate(rect);
1052	} else if (bounds.Height() < fLayoutData->previous_height) {
1053		// invalidate the region of the new bottom border
1054		BRect rect = bounds;
1055		rect.top = rect.bottom - kFrameMargin;
1056		Invalidate(rect);
1057		// invalidate label area
1058		rect = bounds;
1059		rect.right = fDivider;
1060		Invalidate(rect);
1061	}
1062
1063	fLayoutData->previous_width = bounds.Width();
1064	fLayoutData->previous_height = bounds.Height();
1065}
1066
1067
1068void
1069BAbstractSpinner::ValueChanged()
1070{
1071	// hook method - does nothing
1072}
1073
1074
1075void
1076BAbstractSpinner::MessageReceived(BMessage* message)
1077{
1078	if (!IsEnabled() && message->what == B_COLORS_UPDATED)
1079		_UpdateTextViewColors(false);
1080
1081	BControl::MessageReceived(message);
1082}
1083
1084
1085void
1086BAbstractSpinner::MakeFocus(bool focus)
1087{
1088	fTextView->MakeFocus(focus);
1089}
1090
1091
1092void
1093BAbstractSpinner::ResizeToPreferred()
1094{
1095	BView::ResizeToPreferred();
1096
1097	const char* label = Label();
1098	if (label != NULL) {
1099		fDivider = ceilf(StringWidth(label))
1100			+ be_control_look->DefaultLabelSpacing();
1101	} else
1102		fDivider = 0.0f;
1103
1104	_LayoutTextView();
1105}
1106
1107
1108void
1109BAbstractSpinner::SetFlags(uint32 flags)
1110{
1111	// If the textview is navigable, set it to not navigable if needed,
1112	// else if it is not navigable, set it to navigable if needed
1113	if (fTextView->Flags() & B_NAVIGABLE) {
1114		if (!(flags & B_NAVIGABLE))
1115			fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE);
1116	} else {
1117		if (flags & B_NAVIGABLE)
1118			fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE);
1119	}
1120
1121	// Don't make this one navigable
1122	flags &= ~B_NAVIGABLE;
1123
1124	BView::SetFlags(flags);
1125}
1126
1127
1128void
1129BAbstractSpinner::WindowActivated(bool active)
1130{
1131	_DrawTextView(fTextView->Frame());
1132}
1133
1134
1135void
1136BAbstractSpinner::SetAlignment(alignment align)
1137{
1138	fAlignment = align;
1139}
1140
1141
1142void
1143BAbstractSpinner::SetButtonStyle(spinner_button_style buttonStyle)
1144{
1145	fButtonStyle = buttonStyle;
1146}
1147
1148
1149void
1150BAbstractSpinner::SetDivider(float position)
1151{
1152	position = roundf(position);
1153
1154	float delta = fDivider - position;
1155	if (delta == 0.0f)
1156		return;
1157
1158	fDivider = position;
1159
1160	if ((Flags() & B_SUPPORTS_LAYOUT) != 0) {
1161		// We should never get here, since layout support means, we also
1162		// layout the divider, and don't use this method at all.
1163		Relayout();
1164	} else {
1165		_LayoutTextView();
1166		Invalidate();
1167	}
1168}
1169
1170
1171void
1172BAbstractSpinner::SetEnabled(bool enable)
1173{
1174	if (IsEnabled() == enable)
1175		return;
1176
1177	BControl::SetEnabled(enable);
1178
1179	fTextView->MakeEditable(enable);
1180	if (enable)
1181		fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE);
1182	else
1183		fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE);
1184
1185	_UpdateTextViewColors(enable);
1186	fTextView->Invalidate();
1187
1188	_LayoutTextView();
1189	Invalidate();
1190	if (Window() != NULL)
1191		Window()->UpdateIfNeeded();
1192}
1193
1194
1195void
1196BAbstractSpinner::SetLabel(const char* label)
1197{
1198	BControl::SetLabel(label);
1199
1200	if (Window() != NULL)
1201		Window()->UpdateIfNeeded();
1202}
1203
1204
1205bool
1206BAbstractSpinner::IsDecrementEnabled() const
1207{
1208	return fDecrement->IsEnabled();
1209}
1210
1211
1212void
1213BAbstractSpinner::SetDecrementEnabled(bool enable)
1214{
1215	if (IsDecrementEnabled() == enable)
1216		return;
1217
1218	fDecrement->SetEnabled(enable);
1219	fDecrement->Invalidate();
1220}
1221
1222
1223bool
1224BAbstractSpinner::IsIncrementEnabled() const
1225{
1226	return fIncrement->IsEnabled();
1227}
1228
1229
1230void
1231BAbstractSpinner::SetIncrementEnabled(bool enable)
1232{
1233	if (IsIncrementEnabled() == enable)
1234		return;
1235
1236	fIncrement->SetEnabled(enable);
1237	fIncrement->Invalidate();
1238}
1239
1240
1241BSize
1242BAbstractSpinner::MinSize()
1243{
1244	_ValidateLayoutData();
1245	return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min);
1246}
1247
1248
1249BSize
1250BAbstractSpinner::MaxSize()
1251{
1252	_ValidateLayoutData();
1253
1254	BSize max = fLayoutData->min;
1255	max.width = B_SIZE_UNLIMITED;
1256
1257	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
1258}
1259
1260
1261BSize
1262BAbstractSpinner::PreferredSize()
1263{
1264	_ValidateLayoutData();
1265	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
1266		fLayoutData->min);
1267}
1268
1269
1270BAlignment
1271BAbstractSpinner::LayoutAlignment()
1272{
1273	_ValidateLayoutData();
1274	return BLayoutUtils::ComposeAlignment(ExplicitAlignment(),
1275		BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER));
1276}
1277
1278
1279BLayoutItem*
1280BAbstractSpinner::CreateLabelLayoutItem()
1281{
1282	if (fLayoutData->label_layout_item == NULL)
1283		fLayoutData->label_layout_item = new LabelLayoutItem(this);
1284
1285	return fLayoutData->label_layout_item;
1286}
1287
1288
1289BLayoutItem*
1290BAbstractSpinner::CreateTextViewLayoutItem()
1291{
1292	if (fLayoutData->text_view_layout_item == NULL)
1293		fLayoutData->text_view_layout_item = new TextViewLayoutItem(this);
1294
1295	return fLayoutData->text_view_layout_item;
1296}
1297
1298
1299BTextView*
1300BAbstractSpinner::TextView() const
1301{
1302	return dynamic_cast<BTextView*>(fTextView);
1303}
1304
1305
1306//	#pragma mark - BAbstractSpinner protected methods
1307
1308
1309status_t
1310BAbstractSpinner::AllArchived(BMessage* into) const
1311{
1312	status_t result;
1313	if ((result = BControl::AllArchived(into)) != B_OK)
1314		return result;
1315
1316	BArchiver archiver(into);
1317
1318	BArchivable* textViewItem = fLayoutData->text_view_layout_item;
1319	if (archiver.IsArchived(textViewItem))
1320		result = archiver.AddArchivable(kTextViewItemField, textViewItem);
1321
1322	if (result != B_OK)
1323		return result;
1324
1325	BArchivable* labelBarItem = fLayoutData->label_layout_item;
1326	if (archiver.IsArchived(labelBarItem))
1327		result = archiver.AddArchivable(kLabelItemField, labelBarItem);
1328
1329	return result;
1330}
1331
1332
1333status_t
1334BAbstractSpinner::AllUnarchived(const BMessage* from)
1335{
1336	BUnarchiver unarchiver(from);
1337
1338	status_t result = B_OK;
1339	if ((result = BControl::AllUnarchived(from)) != B_OK)
1340		return result;
1341
1342	if (unarchiver.IsInstantiated(kTextViewItemField)) {
1343		TextViewLayoutItem*& textViewItem
1344			= fLayoutData->text_view_layout_item;
1345		result = unarchiver.FindObject(kTextViewItemField,
1346			BUnarchiver::B_DONT_ASSUME_OWNERSHIP, textViewItem);
1347
1348		if (result == B_OK)
1349			textViewItem->SetParent(this);
1350		else
1351			return result;
1352	}
1353
1354	if (unarchiver.IsInstantiated(kLabelItemField)) {
1355		LabelLayoutItem*& labelItem = fLayoutData->label_layout_item;
1356		result = unarchiver.FindObject(kLabelItemField,
1357			BUnarchiver::B_DONT_ASSUME_OWNERSHIP, labelItem);
1358
1359		if (result == B_OK)
1360			labelItem->SetParent(this);
1361	}
1362
1363	return result;
1364}
1365
1366
1367void
1368BAbstractSpinner::DoLayout()
1369{
1370	if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
1371		return;
1372
1373	if (GetLayout()) {
1374		BControl::DoLayout();
1375		return;
1376	}
1377
1378	_ValidateLayoutData();
1379
1380	BSize size(Bounds().Size());
1381	if (size.width < fLayoutData->min.width)
1382		size.width = fLayoutData->min.width;
1383
1384	if (size.height < fLayoutData->min.height)
1385		size.height = fLayoutData->min.height;
1386
1387	float divider = 0;
1388	if (fLayoutData->label_layout_item != NULL
1389		&& fLayoutData->text_view_layout_item != NULL
1390		&& fLayoutData->label_layout_item->Frame().IsValid()
1391		&& fLayoutData->text_view_layout_item->Frame().IsValid()) {
1392		divider = fLayoutData->text_view_layout_item->Frame().left
1393			- fLayoutData->label_layout_item->Frame().left;
1394	} else if (fLayoutData->label_width > 0) {
1395		divider = fLayoutData->label_width
1396			+ be_control_look->DefaultLabelSpacing();
1397	}
1398	fDivider = divider;
1399
1400	BRect dirty(fTextView->Frame());
1401	_LayoutTextView();
1402
1403	// invalidate dirty region
1404	dirty = dirty | fTextView->Frame();
1405	dirty = dirty | fIncrement->Frame();
1406	dirty = dirty | fDecrement->Frame();
1407
1408	Invalidate(dirty);
1409}
1410
1411
1412void
1413BAbstractSpinner::LayoutInvalidated(bool descendants)
1414{
1415	if (fLayoutData != NULL)
1416		fLayoutData->valid = false;
1417}
1418
1419
1420//	#pragma mark - BAbstractSpinner private methods
1421
1422
1423void
1424BAbstractSpinner::_DrawLabel(BRect updateRect)
1425{
1426	BRect rect(Bounds());
1427	rect.right = fDivider;
1428	if (!rect.IsValid() || !rect.Intersects(updateRect))
1429		return;
1430
1431	_ValidateLayoutData();
1432
1433	const char* label = Label();
1434	if (label == NULL)
1435		return;
1436
1437	// horizontal position
1438	float x;
1439	switch (fAlignment) {
1440		case B_ALIGN_RIGHT:
1441			x = fDivider - fLayoutData->label_width - 3.0f;
1442			break;
1443
1444		case B_ALIGN_CENTER:
1445			x = fDivider - roundf(fLayoutData->label_width / 2.0f);
1446			break;
1447
1448		default:
1449			x = 0.0f;
1450			break;
1451	}
1452
1453	// vertical position
1454	font_height& fontHeight = fLayoutData->font_info;
1455	float y = rect.top
1456		+ roundf((rect.Height() + 1.0f - fontHeight.ascent
1457			- fontHeight.descent) / 2.0f)
1458		+ fontHeight.ascent;
1459
1460	uint32 flags = be_control_look->Flags(this);
1461
1462	// erase the is control flag before drawing the label so that the label
1463	// will get drawn using B_PANEL_TEXT_COLOR.
1464	flags &= ~BControlLook::B_IS_CONTROL;
1465
1466	be_control_look->DrawLabel(this, label, LowColor(), flags, BPoint(x, y));
1467}
1468
1469
1470void
1471BAbstractSpinner::_DrawTextView(BRect updateRect)
1472{
1473	BRect rect = fTextView->Frame();
1474	rect.InsetBy(-kFrameMargin, -kFrameMargin);
1475	if (!rect.IsValid() || !rect.Intersects(updateRect))
1476		return;
1477
1478	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1479	uint32 flags = 0;
1480	if (!IsEnabled())
1481		flags |= BControlLook::B_DISABLED;
1482
1483	if (fTextView->IsFocus() && Window()->IsActive())
1484		flags |= BControlLook::B_FOCUSED;
1485
1486	be_control_look->DrawTextControlBorder(this, rect, updateRect, base,
1487		flags);
1488}
1489
1490
1491void
1492BAbstractSpinner::_InitObject()
1493{
1494	fAlignment = B_ALIGN_LEFT;
1495	fButtonStyle = SPINNER_BUTTON_PLUS_MINUS;
1496
1497	if (Label() != NULL) {
1498		fDivider = StringWidth(Label())
1499			+ be_control_look->DefaultLabelSpacing();
1500	} else
1501		fDivider = 0.0f;
1502
1503	BControl::SetEnabled(true);
1504	BControl::SetValue(0);
1505
1506	BRect rect(Bounds());
1507	fLayoutData = new LayoutData(rect.Width(), rect.Height());
1508
1509	rect.left = fDivider;
1510	rect.InsetBy(kFrameMargin, kFrameMargin);
1511	rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f;
1512	BRect textRect(rect.OffsetToCopy(B_ORIGIN));
1513
1514	fTextView = new SpinnerTextView(rect, textRect);
1515	AddChild(fTextView);
1516
1517	rect.InsetBy(0.0f, -kFrameMargin);
1518
1519	rect.left = rect.right + kFrameMargin * 2;
1520	rect.right = rect.left + rect.Height() - kFrameMargin * 2;
1521
1522	fDecrement = new SpinnerButton(rect, "decrement", SPINNER_DECREMENT);
1523	AddChild(fDecrement);
1524
1525	rect.left = rect.right + 1.0f;
1526	rect.right = rect.left + rect.Height() - kFrameMargin * 2;
1527
1528	fIncrement = new SpinnerButton(rect, "increment", SPINNER_INCREMENT);
1529	AddChild(fIncrement);
1530
1531	uint32 navigableFlags = Flags() & B_NAVIGABLE;
1532	if (navigableFlags != 0)
1533		BControl::SetFlags(Flags() & ~B_NAVIGABLE);
1534}
1535
1536
1537void
1538BAbstractSpinner::_LayoutTextView()
1539{
1540	BRect rect;
1541	if (fLayoutData->text_view_layout_item != NULL) {
1542		rect = fLayoutData->text_view_layout_item->FrameInParent();
1543	} else {
1544		rect = Bounds();
1545		rect.left = fDivider;
1546	}
1547	rect.InsetBy(kFrameMargin, kFrameMargin);
1548	rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f;
1549
1550	fTextView->MoveTo(rect.left, rect.top);
1551	fTextView->ResizeTo(rect.Width(), rect.Height());
1552	fTextView->SetTextRect(rect.OffsetToCopy(B_ORIGIN));
1553
1554	rect.InsetBy(0.0f, -kFrameMargin);
1555
1556	rect.left = rect.right + kFrameMargin * 2;
1557	rect.right = rect.left + rect.Height() - kFrameMargin * 2;
1558
1559	fDecrement->ResizeTo(rect.Width(), rect.Height());
1560	fDecrement->MoveTo(rect.LeftTop());
1561
1562	rect.left = rect.right + 1.0f;
1563	rect.right = rect.left + rect.Height() - kFrameMargin * 2;
1564
1565	fIncrement->ResizeTo(rect.Width(), rect.Height());
1566	fIncrement->MoveTo(rect.LeftTop());
1567}
1568
1569
1570void
1571BAbstractSpinner::_UpdateFrame()
1572{
1573	if (fLayoutData->label_layout_item == NULL
1574		|| fLayoutData->text_view_layout_item == NULL) {
1575		return;
1576	}
1577
1578	BRect labelFrame = fLayoutData->label_layout_item->Frame();
1579	BRect textViewFrame = fLayoutData->text_view_layout_item->Frame();
1580
1581	if (!labelFrame.IsValid() || !textViewFrame.IsValid())
1582		return;
1583
1584	// update divider
1585	fDivider = textViewFrame.left - labelFrame.left;
1586
1587	BRect frame = textViewFrame | labelFrame;
1588	MoveTo(frame.left, frame.top);
1589	BSize oldSize = Bounds().Size();
1590	ResizeTo(frame.Width(), frame.Height());
1591	BSize newSize = Bounds().Size();
1592
1593	// If the size changes, ResizeTo() will trigger a relayout, otherwise
1594	// we need to do that explicitly.
1595	if (newSize != oldSize)
1596		Relayout();
1597}
1598
1599
1600void
1601BAbstractSpinner::_UpdateTextViewColors(bool enable)
1602{
1603	// Mimick BTextControl's appearance.
1604	rgb_color textColor = ui_color(B_DOCUMENT_TEXT_COLOR);
1605
1606	if (enable) {
1607		fTextView->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
1608		fTextView->SetLowUIColor(ViewUIColor());
1609	} else {
1610		rgb_color color = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
1611		color = disable_color(ViewColor(), color);
1612		textColor = disable_color(textColor, ViewColor());
1613
1614		fTextView->SetViewColor(color);
1615		fTextView->SetLowColor(color);
1616	}
1617
1618	BFont font;
1619	fTextView->GetFontAndColor(0, &font);
1620	fTextView->SetFontAndColor(&font, B_FONT_ALL, &textColor);
1621}
1622
1623
1624void
1625BAbstractSpinner::_ValidateLayoutData()
1626{
1627	if (fLayoutData->valid)
1628		return;
1629
1630	font_height& fontHeight = fLayoutData->font_info;
1631	GetFontHeight(&fontHeight);
1632
1633	if (Label() != NULL) {
1634		fLayoutData->label_width = StringWidth(Label());
1635		fLayoutData->label_height = ceilf(fontHeight.ascent
1636			+ fontHeight.descent + fontHeight.leading);
1637	} else {
1638		fLayoutData->label_width = 0;
1639		fLayoutData->label_height = 0;
1640	}
1641
1642	float divider = 0;
1643	if (fLayoutData->label_width > 0) {
1644		divider = ceilf(fLayoutData->label_width
1645			+ be_control_look->DefaultLabelSpacing());
1646	}
1647
1648	if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
1649		divider = std::max(divider, fDivider);
1650
1651	float minTextWidth = fTextView->StringWidth("99999");
1652
1653	float textViewHeight = fTextView->LineHeight(0) + kFrameMargin * 2;
1654	float textViewWidth = minTextWidth + textViewHeight * 2;
1655
1656	fLayoutData->text_view_width = textViewWidth;
1657	fLayoutData->text_view_height = textViewHeight;
1658
1659	BSize min(textViewWidth, textViewHeight);
1660	if (divider > 0.0f)
1661		min.width += divider;
1662
1663	if (fLayoutData->label_height > min.height)
1664		min.height = fLayoutData->label_height;
1665
1666	fLayoutData->min = min;
1667	fLayoutData->valid = true;
1668
1669	ResetLayoutInvalidation();
1670}
1671
1672
1673// FBC padding
1674
1675void BAbstractSpinner::_ReservedAbstractSpinner20() {}
1676void BAbstractSpinner::_ReservedAbstractSpinner19() {}
1677void BAbstractSpinner::_ReservedAbstractSpinner18() {}
1678void BAbstractSpinner::_ReservedAbstractSpinner17() {}
1679void BAbstractSpinner::_ReservedAbstractSpinner16() {}
1680void BAbstractSpinner::_ReservedAbstractSpinner15() {}
1681void BAbstractSpinner::_ReservedAbstractSpinner14() {}
1682void BAbstractSpinner::_ReservedAbstractSpinner13() {}
1683void BAbstractSpinner::_ReservedAbstractSpinner12() {}
1684void BAbstractSpinner::_ReservedAbstractSpinner11() {}
1685void BAbstractSpinner::_ReservedAbstractSpinner10() {}
1686void BAbstractSpinner::_ReservedAbstractSpinner9() {}
1687void BAbstractSpinner::_ReservedAbstractSpinner8() {}
1688void BAbstractSpinner::_ReservedAbstractSpinner7() {}
1689void BAbstractSpinner::_ReservedAbstractSpinner6() {}
1690void BAbstractSpinner::_ReservedAbstractSpinner5() {}
1691void BAbstractSpinner::_ReservedAbstractSpinner4() {}
1692void BAbstractSpinner::_ReservedAbstractSpinner3() {}
1693void BAbstractSpinner::_ReservedAbstractSpinner2() {}
1694void BAbstractSpinner::_ReservedAbstractSpinner1() {}
1695