1 // Crimson Fields -- a game of tactical warfare
2 // Copyright (C) 2000-2007 Jens Granseuer
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 //
18 
19 /////////////////////////////////////////////////////////////////////////
20 // slider.cpp
21 ////////////////////////////////////////////////////////////////////////
22 
23 #include "slider.h"
24 #include "misc.h"
25 
26 ////////////////////////////////////////////////////////////////////////
27 // NAME       : SliderWidget::SliderWidget
28 // DESCRIPTION: Create a new slider widget.
29 // PARAMETERS : id     - widget identifier
30 //              x      - left edge of widget
31 //              y      - top edge of widget
32 //              w      - widget width
33 //              h      - widget height
34 //              vmin   - lowest slider level allowed
35 //              vmax   - highest slider level allowed
36 //              start  - default level
37 //              ksize  - size of the slider knob in items (if the slider
38 //                       has 10 possible positions and ksize is 2, the
39 //                       size will be claculated as if there were 12
40 //                       levels and two of them were represented by one
41 //                       slider position, so the knob would take up
42 //                       2/12th of the widget size)
43 //              flags  - widget flags (see widget.h for details)
44 //              title  - widget title (currently unused)
45 //              window - window to add this widget to
46 // RETURNS    : -
47 ////////////////////////////////////////////////////////////////////////
48 
SliderWidget(short id,short x,short y,unsigned short w,unsigned short h,short vmin,short vmax,short start,short ksize,unsigned short flags,const char * title,Window * window)49 SliderWidget::SliderWidget( short id, short x, short y, unsigned short w,
50     unsigned short h, short vmin, short vmax, short start, short ksize,
51     unsigned short flags, const char *title, Window *window ) :
52     Widget( id, x, y, w, h, flags, title, window ) {
53   mousehit = false;
54   current = start;
55   keystep = 1;
56 
57   Adjust( vmin, vmax, ksize );
58 }
59 
60 ////////////////////////////////////////////////////////////////////////
61 // NAME       : SliderWidget::Adjust
62 // DESCRIPTION: Set new minimum and maximum slider values.
63 // PARAMETERS : newmin  - new minimum level
64 //              newmax  - new maximum level
65 //              newsize - new knob size
66 // RETURNS    : -
67 ////////////////////////////////////////////////////////////////////////
68 
Adjust(short newmin,short newmax,short newsize)69 void SliderWidget::Adjust( short newmin, short newmax, short newsize ) {
70   min_level = newmin;
71   max_level = newmax;
72   size = MAX( newsize, 1 );
73 
74   if ( current < min_level ) current = min_level;
75   else if ( current > max_level ) current = max_level;
76 
77   short num = max_level - min_level;
78   if ( flags & WIDGET_HSCROLL ) {
79     knob.w = MAX( 2, (w - 2) * size / (size + num) );
80     knob.h = h - 2;
81     if ( max_level > min_level ) step = (float)(w - 2 - knob.w) / num;
82     else step = 0;
83     knob.x = (short)(x + 1 + (current - min_level) * step + 0.5);
84     knob.y = y + 1;
85   } else {
86     knob.w = w - 2;
87     knob.h = MAX( 2, (h - 2) * size / (size + num) );
88     if ( max_level > min_level ) step = (float)(h - 2 - knob.h) / num;
89     else step = 0;
90     knob.x = x + 1;
91     knob.y = (short)(y + 1 + (current - min_level) * step + 0.5);
92   }
93 }
94 
95 ////////////////////////////////////////////////////////////////////////
96 // NAME       : SliderWidget::Draw
97 // DESCRIPTION: Draw slider widget and knob.
98 // PARAMETERS : -
99 // RETURNS    : -
100 ////////////////////////////////////////////////////////////////////////
101 
Draw(void)102 void SliderWidget::Draw( void ) {
103   surface->DrawBack( *this );
104   surface->DrawBox( *this, BOX_RECESSED );
105 
106   // draw knob
107   surface->DrawBox( knob, Clicked() ? BOX_RECESSED : BOX_RAISED );
108   if ( Clicked() )
109     surface->FillRectAlpha( knob.x+1, knob.y+1, knob.w-2, knob.h-2, Color(CF_COLOR_SHADOW) );
110 
111   PrintTitle( surface->GetFGPen() );
112 }
113 
114 ////////////////////////////////////////////////////////////////////////
115 // NAME       : SliderWidget::ScrollTo
116 // DESCRIPTION: Set the slider to a new level.
117 // PARAMETERS : level - new slider position
118 // RETURNS    : -
119 ////////////////////////////////////////////////////////////////////////
120 
ScrollTo(short level)121 void SliderWidget::ScrollTo( short level ) {
122   current = level;
123   if ( current < min_level ) current = min_level;
124   else if ( current > max_level ) current = max_level;
125 
126   if ( flags & WIDGET_HSCROLL )
127     knob.x = (short)(x + 1 + (current - min_level) * step + 0.5);
128   else knob.y = (short)(y + 1 + (current - min_level) * step + 0.5);
129 
130   if ( hook ) hook->WidgetActivated( this, surface );
131   Draw();
132   Show();
133 }
134 
135 ////////////////////////////////////////////////////////////////////////
136 // NAME       : SliderWidget::MouseDown
137 // DESCRIPTION: Depress the slider or move the knob towards the mouse.
138 // PARAMETERS : button - SDL_MouseButtonEvent received from the event
139 //                       handler
140 // RETURNS    : GUI status
141 ////////////////////////////////////////////////////////////////////////
142 
MouseDown(const SDL_MouseButtonEvent & button)143 GUI_Status SliderWidget::MouseDown( const SDL_MouseButtonEvent &button ) {
144   short mx = button.x - surface->LeftEdge();
145   short my = button.y - surface->TopEdge();
146 
147   if ( Contains( mx, my ) ) {
148     if ( button.button == SDL_BUTTON_LEFT ) {
149       if ( knob.Contains( mx, my ) ) {
150         Push();
151         mousehit = true;
152       } else {
153         short diff = MAX( 1, size/2 );
154         if ( flags & WIDGET_HSCROLL ) {
155           if ( mx < knob.x ) ScrollTo( current - diff );
156           else if ( mx >= (knob.x + knob.w) ) ScrollTo( current + diff );
157         } else {
158           if ( my < knob.y ) ScrollTo( current - diff );
159           else if ( my >= (knob.y + knob.h) ) ScrollTo( current + diff );
160         }
161       }
162     } else if ( button.button == SDL_BUTTON_WHEELUP ) {
163       ScrollTo( current - MAX(1, size/4) );
164     } else if ( button.button == SDL_BUTTON_WHEELDOWN ) {
165       ScrollTo( current + MAX(1, size/4) );
166     }
167   }
168 
169   return GUI_OK;
170 }
171 
172 ////////////////////////////////////////////////////////////////////////
173 // NAME       : SliderWidget::MouseUp
174 // DESCRIPTION: Release the knob.
175 // PARAMETERS : button - SDL_MouseButtonEvent received from the event
176 //                       handler
177 // RETURNS    : GUI_OK
178 ////////////////////////////////////////////////////////////////////////
179 
MouseUp(const SDL_MouseButtonEvent & button)180 GUI_Status SliderWidget::MouseUp( const SDL_MouseButtonEvent &button ) {
181   if ( Clicked() && (button.button == SDL_BUTTON_LEFT) ) {
182     Release();
183     mousehit = false;
184   }
185   return GUI_OK;
186 }
187 
188 ////////////////////////////////////////////////////////////////////////
189 // NAME       : SliderWidget::KeyDown
190 // DESCRIPTION: Depress knob and move slider if correct key was hit.
191 // PARAMETERS : key - SDL_keysym received from the event handler
192 // RETURNS    : GUI_OK
193 ////////////////////////////////////////////////////////////////////////
194 
KeyDown(const SDL_keysym & key)195 GUI_Status SliderWidget::KeyDown( const SDL_keysym &key ) {
196   if ( key.sym == this->key ) {
197     Push();
198     if ( key.mod & (KMOD_LSHIFT|KMOD_RSHIFT) ) {
199       if ( current > min_level ) ScrollTo( current - keystep );
200     } else if ( current < max_level ) ScrollTo( current + keystep );
201   } else if ( flags & (WIDGET_HSCROLLKEY|WIDGET_VSCROLLKEY) ) {
202     switch( key.sym ) {
203     case SDLK_UP:
204       if ( (flags & WIDGET_VSCROLLKEY) && (current > min_level) ) {
205         Push();
206         ScrollTo( current - keystep );
207       }
208       break;
209     case SDLK_DOWN:
210       if ( (flags & WIDGET_VSCROLLKEY) && (current < max_level) ) {
211         Push();
212         ScrollTo( current + keystep );
213       }
214       break;
215 
216     case SDLK_LEFT:
217       if ( (flags & WIDGET_HSCROLLKEY) && (current > min_level) ) {
218         Push();
219         ScrollTo( current - keystep );
220       }
221       break;
222     case SDLK_RIGHT:
223       if ( (flags & WIDGET_HSCROLLKEY) && (current < max_level) ) {
224         Push();
225         ScrollTo( current + keystep );
226       }
227       break;
228     default:
229       break;
230     }
231   }
232 
233   return GUI_OK;
234 }
235 
236 ////////////////////////////////////////////////////////////////////////
237 // NAME       : SliderWidget::KeyUp
238 // DESCRIPTION: Release the knob if necessary.
239 // PARAMETERS : key - SDL_keysym received from the event handler
240 // RETURNS    : GUI_OK
241 ////////////////////////////////////////////////////////////////////////
242 
KeyUp(const SDL_keysym & key)243 GUI_Status SliderWidget::KeyUp( const SDL_keysym &key ) {
244   if ( Clicked() ) Release();
245   return GUI_OK;
246 }
247 
248 ////////////////////////////////////////////////////////////////////////
249 // NAME       : SliderWidget::MouseMove
250 // DESCRIPTION: Drag the knob after the mouse.
251 // PARAMETERS : motion - SDL_MouseMotionEvent from the event handler
252 // RETURNS    : GUI_OK
253 ////////////////////////////////////////////////////////////////////////
254 
MouseMove(const SDL_MouseMotionEvent & motion)255 GUI_Status SliderWidget::MouseMove( const SDL_MouseMotionEvent &motion ) {
256   if ( mousehit ) {
257 
258     if ( max_level != min_level ) {
259       short val;
260 
261       if ( flags & WIDGET_HSCROLL ) {
262         short dx = knob.x + motion.xrel;
263         knob.x = MIN( MAX( x + 1, dx ), x + w - 1 - knob.w );
264         val = (short)((knob.x - 1 - x + step/2) / step) + min_level;
265       } else {
266         short dy = knob.y + motion.yrel;
267         knob.y = MIN( MAX( y + 1, dy ), y + h - 1 - knob.h );
268         val = (short)((knob.y - 1 - y + step/2) / step) + min_level;
269       }
270 
271       Draw();
272       Show();
273       if ( val != current ) {
274         current = MAX( min_level, MIN( val, max_level ) );
275         if ( hook ) hook->WidgetActivated( this, surface );
276       }
277     }
278   }
279   return GUI_OK;
280 }
281 
282 
283 ////////////////////////////////////////////////////////////////////////
284 // NAME       : ProgressWidget::ProgressWidget
285 // DESCRIPTION: Create a new progress bar. Initially, the new progress
286 //              bar will be empty.
287 // PARAMETERS : id     - widget identifier
288 //              x      - left edge of widget
289 //              y      - top edge of widget
290 //              w      - widget width
291 //              h      - widget height
292 //              vmin   - value mapped to an empty bar
293 //              vmax   - value mapped to a completely filled bar
294 //              flags  - widget flags (see widget.h for details)
295 //              title  - widget title
296 //              window - window to add this widget to
297 // RETURNS    : -
298 ////////////////////////////////////////////////////////////////////////
299 
ProgressWidget(short id,short x,short y,unsigned short w,unsigned short h,short vmin,short vmax,unsigned short flags,const char * title,Window * window)300 ProgressWidget::ProgressWidget( short id, short x, short y, unsigned short w,
301     unsigned short h, short vmin, short vmax, unsigned short flags,
302     const char *title, Window *window ) :
303     Widget( id, x, y, w, h, flags, title, window ),
304     level(0), min_level(vmin), max_level(vmax), col(CF_COLOR_HIGHLIGHT),
305     title_x(0), title_y(0) {
306   if ( title ) {
307     title_x = x + (w - font->TextWidth( title )) / 2;
308     title_y = y + (h - font->Height()) / 2;
309   }
310 }
311 
312 ////////////////////////////////////////////////////////////////////////
313 // NAME       : ProgressWidget::SetLevel
314 // DESCRIPTION: Fill the progress bar.
315 // PARAMETERS : lev - level up to which the bar should be filled. Must
316 //                    be between min and max. min will clear the bar,
317 //                    max will fill it completely.
318 // RETURNS    : -
319 ////////////////////////////////////////////////////////////////////////
320 
SetLevel(short lev)321 void ProgressWidget::SetLevel( short lev ) {
322   if ( lev < min_level ) lev = min_level;
323   else if ( lev > max_level ) lev = max_level;
324 
325   level = lev - min_level;
326   Draw();
327   Show();
328 }
329 
330 ////////////////////////////////////////////////////////////////////////
331 // NAME       : ProgressWidget::Draw
332 // DESCRIPTION: Draw the progress bar.
333 // PARAMETERS : -
334 // RETURNS    : -
335 ////////////////////////////////////////////////////////////////////////
336 
Draw(void)337 void ProgressWidget::Draw( void ) {
338   surface->DrawBack( *this );
339   surface->DrawBox( *this, BOX_RECESSED );
340 
341   if ( !Disabled() && (level > 0) ) {
342     unsigned short levsize = level * (Width() - 2) / (max_level - min_level);
343     surface->FillRectAlpha( x + 1, y + 1, levsize, h - 2, col );
344   }
345 
346   PrintTitle( title_x, title_y, col );
347 }
348