1 /***********************************************************************************************
2  * SliderCtrlX
3  *
4  * Enhanced Slider Control / Slider indicator
5  *
6  * Autors : avpavp, jerson, jibe, koldo
7  * Last modified : June 09, 2011
8  *
9  * 2do : adjust default values when changing CONTROL/INDICATOR
10  *		Better .usc file
11  * Known bugs : Invalid memory access when choosing FillColor with .lay designer (seems to be
12  *      only with ubuntu. See http://www.ultimatepp.org/forum/index.php?t=msg&th=6022&start=0&
13  **********************************************************************************************/
14 
15 #include <CtrlLib/CtrlLib.h>
16 
17 #include "SliderCtrlX.h"
18 
19 
20 #define IMAGECLASS SliderThumbsImg
21 #define IMAGEFILE <Controls4U/SliderCtrlX.iml>
22 #include <Draw/iml.h>
23 
24 namespace Upp {
25 
SliderCtrlX()26 SliderCtrlX::SliderCtrlX()
27 : m_nMin(0)
28 , m_nMax(100)
29 , m_bInverted(false)
30 , m_nStep(1)
31 , m_bRound_step(false)
32 , m_bJump(false)
33 , m_bUseCustomThumbs( 0 )
34 , m_nMajorTicks( 10 )
35 , m_nMinorTicks( 2 )
36 , m_nMajorTickSize( 30 )
37 , m_nMinorTickSize( 20 )
38 , m_nTickPosition( TOP )
39 , m_nThickness( 2 )
40 , m_nSliderType( 0 )
41 , m_nThumbNumber( 1 )
42 /***********************************************************************************************
43  * Constructor
44  * public
45  **********************************************************************************************/
46 {
47 	SetValue( 0 );
48 
49 	border2 = m_nThickness * BORDER_SIZE;
50 	Transparent();
51 
52 	m_ThumbImg.Clear();
53 }
54 
~SliderCtrlX()55 SliderCtrlX::~SliderCtrlX()
56 /***********************************************************************************************
57  * Destructor
58  * virtual - public
59  **********************************************************************************************/
60 {
61 }
62 
AddOutCtrl(Ctrl * c)63 SliderCtrlX& SliderCtrlX::AddOutCtrl( Ctrl* c )
64 /***********************************************************************************************
65  * public
66  **********************************************************************************************/
67 {
68 	m_vctrlOutput.Add( c );
69 
70 	return *this;
71 }
72 
ClientToSlider(int p) const73 int SliderCtrlX::ClientToSlider(int p) const
74 /***********************************************************************************************
75  * Converts Slider values to canvas coordinates
76  * private
77  **********************************************************************************************/
78 {
79 	p -= HoVe(m_ThumbSize.cx/2, m_ThumbSize.cy/2);
80 
81 	return minmax(m_nMin + iscale(p, m_nMax - m_nMin,
82 	                           HoVe(GetSize().cx - border2 - m_ThumbSize.cx, GetSize().cy - border2 - m_ThumbSize.cy)), Min(), Max());
83 }
84 
Dec()85 void SliderCtrlX::Dec()
86 /***********************************************************************************************
87  * decrements the slider value
88  * public
89  **********************************************************************************************/
90 {
91 	int n = m_vValues[0];
92 	if(IsNull(m_vValues[0]))
93 		n = Max();
94 	else
95 	if(n > Min()) {
96 		if(m_bRound_step && m_nStep > 1)
97 			n = idivfloor(n - 1, m_nStep) * m_nStep;
98 		else
99 			n -= m_nStep;
100 		if(n < Min())
101 			n = Min();
102 	}
103 	if(n != m_vValues[0]) {
104 		SetValue( n );
105 		WhenSlideFinish();
106 		UpdateActionRefresh();
107 	}
108 }
109 
DrawTick(Draw & w,MAJORMINOR Type,HOVE Orientation,int nPos,int nVal)110 void	SliderCtrlX::DrawTick( Draw &w, MAJORMINOR Type, HOVE Orientation, int nPos, int nVal )
111 /***********************************************************************************************
112  * draws a tickmark at nPos on canvas
113  * protected
114  **********************************************************************************************/
115 {
116 	Size size = GetSize();
117 	int		sz = Orientation == HORZ ? size.cy : size.cx;
118 	int		nMajorSize = (int)( m_nMajorTickSize / 100.0f * sz * .5 + 0.5f );
119 	int		nMinorSize = (int)( m_nMinorTickSize / 100.0f * sz * .5 + 0.5f );
120 	int		nMajorWidth = 3;
121 	int		TickBottomMaj = 0, TickBottomMin = 0, TickTop = 0;
122 
123 	String txt;
124 
125 	if (LabelFormat)
126 		LabelFormat(txt, nVal);
127 	else
128 		txt = AsString(nVal);
129 
130 	switch (m_nTickPosition) {
131 		case TOP : TickTop = -2;
132 				TickBottomMaj = -nMajorSize -2;
133 				TickBottomMin = -nMinorSize -2;
134 				break;
135 		case MIDDLE_TOP : TickTop = +2 +m_nThickness;
136 				TickBottomMaj = -nMajorSize +2 +m_nThickness;
137 				TickBottomMin = -nMinorSize +2 +m_nThickness;
138 				break;
139 		case MIDDLE_BOTTOM : TickTop = 0;
140 				TickBottomMaj = nMajorSize;
141 				TickBottomMin = nMinorSize;
142 				break;
143 		case BOTTOM : TickTop = +2 +m_nThickness;
144 				TickBottomMaj = nMajorSize +2 +m_nThickness;
145 				TickBottomMin = nMinorSize +2 +m_nThickness;
146 				break;
147 	}
148 
149 	if( Orientation == HORZ ) {
150 		if( Type == MAJOR ) {
151 			w.DrawLine( max( nPos, nMajorWidth >> 1 ),
152 				(int)( size.cy * .5 + TickTop),
153 				max( nPos, nMajorWidth >> 1 ),
154 				(int)(size.cy * .5 + TickBottomMaj),
155 				nMajorWidth );
156 
157 			Size sz = GetTextSize( txt, StdFont() );
158 			int nTextPos = nPos - (int)( sz.cx / 2.0f + 0.5f );
159 			nTextPos = min( max( 0, nTextPos ), size.cx - sz.cx );
160 			w.DrawText( nTextPos, 0, txt );
161 		}
162 		else {
163 			w.DrawLine( nPos,
164 				(int)( size.cy * .5 + TickTop),
165 				nPos,
166 				(int)(size.cy * .5 + TickBottomMin ) );
167 		}
168 	}
169 	else { // vert
170 		if( Type == MAJOR ) {
171 			w.DrawLine( (int)( size.cx * .5 + TickTop),
172 				max( nPos, nMajorWidth >> 1 ),
173 				(int)(size.cx * .5 + TickBottomMaj),
174 				max( nPos, nMajorWidth >> 1 ),
175 				nMajorWidth );
176 
177 			Size sz = GetTextSize( txt, StdFont() );
178 			int nTextPos = nPos - (int)( sz.cy / 2.0f + 0.5f );
179 			nTextPos = min( max( 0, nTextPos ), size.cy - sz.cy );
180 			w.DrawText( 0, nTextPos, txt );
181 		}
182 		else {
183 			w.DrawLine( (int)(size.cx / 2 + TickTop),
184 				nPos,
185 				(int)(size.cx * .5 + TickBottomMin),
186 				nPos );
187 		}
188 	}
189 }
190 
GetData() const191 Value  SliderCtrlX::GetData() const
192 /***********************************************************************************************
193  * default read of slider value
194  * virtual - public
195  **********************************************************************************************/
196 {
197 	return m_vValues[0];
198 }
199 
GetData(int nIndex) const200 Value SliderCtrlX::GetData( int nIndex ) const
201 /***********************************************************************************************
202  * reads the slider[n] value
203  * virtual - public
204  **********************************************************************************************/
205 {
206 	return m_vValues[nIndex];
207 }
208 
GotFocus()209 void SliderCtrlX::GotFocus()
210 /***********************************************************************************************
211  * virtual - public
212  **********************************************************************************************/
213 {
214 	Refresh();
215 }
216 
HoVe(int x,int y) const217 int  SliderCtrlX::HoVe(int  x, int  y) const
218 /***********************************************************************************************
219  * returns x for Hslider or y for Vslider
220  * private
221  **********************************************************************************************/
222 {
223 	return IsVert() ? y : x;
224 }
225 
HoVeR(int & x,int & y) const226 int& SliderCtrlX::HoVeR(int& x, int& y) const
227 /***********************************************************************************************
228  * returns &x for Hslider or &y for Vslider
229  * private
230  **********************************************************************************************/
231 {
232 	return IsVert() ? y : x;
233 }
234 
Inc()235 void SliderCtrlX::Inc()
236 /***********************************************************************************************
237  * increments
238  * virtual - public
239  **********************************************************************************************/
240 {
241 	int n = m_vValues[0];
242 	if(IsNull(m_vValues[0]))
243 		n = Min();
244 	else
245 	if(n < Max()) {
246 		if(m_bRound_step && m_nStep > 1)
247 			n = idivceil(n + 1, m_nStep) * m_nStep;
248 		else
249 			n += m_nStep;
250 		if(n > Max())
251 			n = Max();
252 	}
253 	if(n != m_vValues[0]) {
254 		SetValue( n );
255 		WhenSlideFinish();
256 		UpdateActionRefresh();
257 	}
258 }
259 
IsVert() const260 bool SliderCtrlX::IsVert() const
261 /***********************************************************************************************
262  * Checks if the slider is vertically oriented
263  * public
264  **********************************************************************************************/
265 {
266 	return GetSize().cx < GetSize().cy;
267 }
268 
Key(dword key,int repcnt)269 bool SliderCtrlX::Key(dword key, int repcnt)
270 /***********************************************************************************************
271  * handles keys addressed to the slider (GotFocussed)
272  * only if it is a CONTROL
273  * virtual - public
274  **********************************************************************************************/
275 {
276 	// No thumb action for indicator
277 	if (m_nSliderType == INDICATOR)	return Ctrl::Key(key, repcnt);
278 
279 	if(IsEditable())
280 		switch(key) {
281 		case K_LEFT:
282 		case K_DOWN:
283 			Dec();
284 			return true;
285 		case K_RIGHT:
286 		case K_UP:
287 			Inc();
288 			return true;
289 		}
290 	return Ctrl::Key(key, repcnt);
291 }
292 
LeftDown(Point pos,dword keyflags)293 void SliderCtrlX::LeftDown(Point pos, dword keyflags)
294 /***********************************************************************************************
295  * handles the left mouse button if the slider is s CONTROL
296  * virtual - public
297  **********************************************************************************************/
298 {
299 	if(!IsEditable())
300 		return;
301 
302 	// No thumb action for indicator
303 	if (m_nSliderType == INDICATOR)	return;
304 
305 	SetWantFocus();
306 	int thumbPos = SliderToClient(m_vValues[0]);
307 	int p = HoVe(pos.x, pos.y);
308 
309 	if(IsNull(thumbPos)) {
310 		SetValue( ClientToSlider(p) );
311 		WhenSlideFinish();
312 		UpdateActionRefresh();
313 	}
314 	// Did we click on the thumb?
315 	else if( ( p >= ( thumbPos - nHalfThumb ) ) &&
316 		( p < ( thumbPos + nHalfThumb ) ) )
317 		SetCapture();
318 
319 	else if( m_bJump )
320 	{
321 		m_vValues[0] = ClientToSlider(p);
322 		WhenSlideFinish();
323 		UpdateActionRefresh();
324 	}
325 	else
326 	{
327 		if( ( ( p < thumbPos) && (m_nMin == Min() ) ) || ( (p > thumbPos) && ( m_nMin == Max() ) ) )
328 			IsInverted() ? Inc() : Dec();
329 		else
330 			IsInverted() ? Dec() : Inc();
331 	}
332 
333 	Refresh();
334 }
335 
LeftRepeat(Point p,dword f)336 void SliderCtrlX::LeftRepeat(Point p, dword f)
337 /***********************************************************************************************
338  * virtual - public
339  **********************************************************************************************/
340 {
341 	if(!HasCapture())
342 		LeftDown(p, f);
343 }
344 
LeftUp(Point pos,dword keyflags)345 void SliderCtrlX::LeftUp(Point pos, dword keyflags)
346 /***********************************************************************************************
347  * virtual - public
348  **********************************************************************************************/
349 {
350 	if (HasCapture())
351 		WhenSlideFinish();
352 	Refresh();
353 }
354 
LostFocus()355 void SliderCtrlX::LostFocus()
356 /***********************************************************************************************
357  * virtual - public
358  **********************************************************************************************/
359 {
360 	Refresh();
361 }
362 
MinMax(int _min,int _max)363 SliderCtrlX& SliderCtrlX::MinMax(int _min, int _max)
364 /***********************************************************************************************
365  * Restricts slider to Min, Max values
366  * public
367  **********************************************************************************************/
368 {
369 	if(m_nMin != _min || m_nMax != _max) {
370 		m_nMin = _min;
371 		m_nMax = _max;
372 		if(!IsNull(m_vValues[0])) {
373 			int v = minmax(m_vValues[0], Min(), Max());
374 			if(m_vValues[0] != v) {
375 				SetValue( v );
376 				Update();
377 			}
378 		}
379 		Refresh();
380 	}
381 	return *this;
382 }
383 
MouseMove(Point pos,dword keyflags)384 void SliderCtrlX::MouseMove(Point pos, dword keyflags)
385 /***********************************************************************************************
386  * virtual - public
387  **********************************************************************************************/
388 {
389 	if(HasCapture()) {
390 		int n;
391 		//int p = HoVe(pos.x, pos.y);
392 		//int thumbPos = SliderToClient(m_vValues[0]);
393 		if (IsInverted())
394 			n = ClientToSlider(HoVe(GetSize().cx-pos.x, GetSize().cy-pos.y));
395 		else
396 			n = ClientToSlider(HoVe(pos.x, pos.y));
397 		if(n != m_vValues[0]) {
398 			SetValue( n );
399 			UpdateActionRefresh();
400 		}
401 	}
402 }
403 
Paint(Draw & w)404 void SliderCtrlX::Paint(Draw& w)
405 /***********************************************************************************************
406  * Draws the slider on the canvas w
407  * virtual - public
408  **********************************************************************************************/
409 {
410 	Size size = GetSize();
411 	if (m_ThumbImg.IsEmpty())
412 		SetThumbType(m_nThumbNumber);
413 
414 	if(IsVert()) { // Vertical slider
415 		// Draw Line Border
416 		int half = size.cx >> 1;
417 		DrawBorder(w, half - BORDER1, BORDER1, border2, size.cy - border2, InsetBorder);
418 
419 		if (IsInverted())
420 			// draw the fill where bottom is the min value
421 			w.DrawRect(half - BORDER1+1, SliderToClient(m_vValues[0])+1,
422 				   border2-2, size.cy-SliderToClient(m_vValues[0])-BORDER1-2,
423 				   m_FillColor);
424 		else
425 			// draw the fill where top is the min value
426 			w.DrawRect(half - BORDER1+1, BORDER1+1,
427 				   border2-2, SliderToClient(m_vValues[0])-BORDER1-2,
428 				   m_FillColor);
429 	}
430 	else { // Horz slider
431 		// Draw Line Border
432 		int half = size.cy >> 1;
433 		DrawBorder(w, BORDER1, half - BORDER1, size.cx - border2, border2, InsetBorder);
434 
435 		if (IsInverted())
436 			// draw the fill where left is the min value
437 			w.DrawRect(SliderToClient(m_vValues[0]), half - BORDER1+2, size.cx-SliderToClient(m_vValues[0])-2, border2-3, m_FillColor);
438 		else
439 			// draw the fill where right is the min value
440 			w.DrawRect(BORDER1, half - BORDER1+2, SliderToClient(m_vValues[0])-2, border2-3, m_FillColor);
441 	}
442 
443 	// draw gradations
444 	for( 	int i = Min();
445 			( m_nMajorTicks > 0 ) && ( i <= Max() ) ;
446 			i += ( m_nMinorTicks == 0 ? m_nMajorTicks : m_nMinorTicks ) ) {
447 
448 		int nPos = SliderToClient( i );
449 
450 		if( ( m_nMajorTicks != 0 ) && ( i % m_nMajorTicks ) == 0 )
451 			DrawTick( w, MAJOR, (HOVE)HoVe( HORZ, VERT ), nPos, i );
452 		else if( ( m_nMinorTicks != 0 ) && ( i % m_nMinorTicks ) == 0 )
453 			DrawTick( w, MINOR, (HOVE)HoVe( HORZ, VERT ), nPos, i );
454 	}
455 
456 	// Draw thumbs
457 	if(IsVert()) { // Vertical slider
458 		for( int i = m_vValues.GetCount() - 1 ; i >= 0 ; i-- ) {
459 			if(!IsNull(m_vValues[i])) {
460 				w.DrawImage((size.cx - m_ThumbSize.cx ) >> 1,
461 					SliderToClient(m_vValues[i]) - ( m_ThumbSize.cy >> 1 ),
462 				    HasCapture() || HasFocus() ? ( m_bUseCustomThumbs ? m_vThumbImgsFocus[i] : m_ThumbFImg ) : ( m_bUseCustomThumbs ? m_vThumbImgs[i] : m_ThumbImg ) );
463 			}
464 		}
465 	}
466 	else { // Horz slider
467 		for( int i = m_vValues.GetCount() - 1 ; i >= 0 ; i-- ) {
468 			if(!IsNull(m_vValues[i])) {
469 				w.DrawImage(SliderToClient(m_vValues[i]) - ( m_ThumbSize.cx >> 1 ),
470 					m_nThickness + ((size.cy - m_ThumbSize.cy) >> 1),
471 					HasCapture() || HasFocus() ? (m_bUseCustomThumbs ? m_vThumbImgsFocus[i] : m_ThumbFImg) : (m_bUseCustomThumbs ? m_vThumbImgs[i] : m_ThumbImg));
472 			}
473 		}
474 	}
475 
476 	if(HasFocus())
477 		DrawFocus(w, size);
478 }
479 
SetData(const Value & v)480 void SliderCtrlX::SetData(const Value& v)
481 /***********************************************************************************************
482  * sets the slider value within its' range
483  * virtual - public
484  **********************************************************************************************/
485 {
486 	SetValue( v );
487 }
488 
SetThumbType(int n)489 SliderCtrlX& SliderCtrlX::SetThumbType(int n)
490 /***********************************************************************************************
491  * Selects the thumb according to the choosen type
492  * public
493  **********************************************************************************************/
494 {
495 	m_nThumbNumber = n;
496 	// This is called before size is known, so we cannot choose the right thumb...
497 	if (GetSize().IsEmpty()) {
498 		m_ThumbImg.Clear();
499 		return *this;
500 	}
501 	m_nThumbType = m_nThumbNumber*2;
502 	if (IsVert()) m_nThumbNumber = 0;
503 	m_ThumbImg = SliderThumbsImg::Get(m_nThumbType);
504 	m_ThumbFImg = SliderThumbsImg::Get(m_nThumbType+1);
505 	if (IsVert()) {
506 		m_ThumbImg = RotateAntiClockwise(m_ThumbImg);
507 		m_ThumbFImg = RotateAntiClockwise(m_ThumbFImg);
508 	}
509 	if (m_nTickPosition == BOTTOM) {
510 		IsVert() ? m_ThumbImg = MirrorHorz(m_ThumbImg) : m_ThumbImg = MirrorVert(m_ThumbImg);
511 		IsVert() ? m_ThumbFImg = MirrorHorz(m_ThumbFImg) : m_ThumbFImg = MirrorVert(m_ThumbFImg);
512 	}
513 	m_ThumbSize = m_bUseCustomThumbs ? m_vThumbImgs[0].GetSize() : m_ThumbImg.GetSize();
514 	nHalfThumb = HoVe(m_ThumbSize.cx, m_ThumbSize.cy) >> 1;
515 	return *this;
516 }
517 
SetValue(const Value & v,int nIndex)518 Value SliderCtrlX::SetValue( const Value& v, int nIndex /*= 0 */ )
519 /***********************************************************************************************
520  * sets the value for the slider position
521  * public
522  **********************************************************************************************/
523 {
524 	int n = v;
525 
526 	if(!IsNull(n))
527 	{
528 		n = minmax(n, Min(), Max() );
529 
530 		if(n < Max() && n > Min()) {
531 			if(m_bRound_step && m_nStep > 1)
532 				n = idivceil(n, m_nStep) * m_nStep;
533 			if(n > Max())
534 				n = Max();
535 			if (n < Min())
536 				n = Min();
537 		}
538 
539 		if( m_vValues.At(nIndex) != n )
540 		{
541 			m_vValues.At(nIndex) = n;
542 
543 			if( m_vctrlOutput.GetCount() > nIndex )
544 				m_vctrlOutput[nIndex]->SetData( n );
545 
546 			UpdateRefresh();
547 		}
548 	}
549 
550 	return m_vValues.At(nIndex);
551 }
552 
SliderToClient(int v) const553 int SliderCtrlX::SliderToClient(int v) const
554 /***********************************************************************************************
555  * CONVERTS SLIDER VALUES TO Canvas coordinates
556  * private
557  **********************************************************************************************/
558 {
559 	if(IsNull(v))
560 		return Null;
561 	v = minmax(v, Min(), Max());
562 
563 	if( v < 0 )
564 		v = iscalefloor(v - m_nMin, HoVe(GetSize().cx - border2 - m_ThumbSize.cx,
565 		                         GetSize().cy - border2 - m_ThumbSize.cy), m_nMax - m_nMin);
566 	else
567 		v = iscaleceil(v - m_nMin, HoVe(GetSize().cx - border2 - m_ThumbSize.cx,
568 		                         GetSize().cy - border2 - m_ThumbSize.cy), m_nMax - m_nMin);
569 
570 	v += HoVe(m_ThumbSize.cx/2, m_ThumbSize.cy/2);
571 
572 	if (IsInverted())
573 		IsVert() ? v = GetSize().cy - v : v = GetSize().cx - v;
574 
575 	return v;
576 }
577 
578 }