File: MultiButtonGL.h

package info (click to toggle)
transcend 0.3.dfsg2-2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 5,768 kB
  • sloc: cpp: 26,891; ansic: 693; sh: 210; makefile: 96; perl: 67
file content (314 lines) | stat: -rw-r--r-- 7,199 bytes parent folder | download | duplicates (18)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/*
 * Modification History
 *
 * 2001-September-16		Jason Rohrer
 * Created.
 *
 * 2001-November-3		Jason Rohrer
 * Fixed a gutter size bug that occurred when there was
 * only one row or column.
 * Changed so that rows are filled before adding new columns.
 * Fixed a bug in setSelectedIndex.
 */
 
 
#ifndef MULTI_BUTTON_GL_INCLUDED
#define MULTI_BUTTON_GL_INCLUDED 

#include "GUIContainerGL.h"
#include "StickyButtonGL.h"

#include "minorGems/graphics/Image.h"

#include "minorGems/ui/event/ActionListenerList.h"
#include "minorGems/ui/event/ActionListener.h"
#include "minorGems/ui/GUIComponent.h"

#include <math.h>
#include <stdio.h>

/**
 * A set of buttons that allows only one to be depressed
 * at a time.
 *
 * @author Jason Rohrer
 */
class MultiButtonGL : public GUIContainerGL,
					public ActionListener,
					public ActionListenerList {


	public:


		
		/**
		 * Constructs a set of buttons.
		 *
		 * @param inAnchorX the x position of the upper left corner
		 *   of this component.
		 * @param inAnchorY the y position of the upper left corner
		 *   of this component.
		 * @param inWidth the width of this component.
		 * @param inHeight the height of this component.
		 * @param inNumButtons the number of buttons in the set.
		 * @param inUnpressedImages the images to display
		 *   when each button is unpressed.  Images must have dimensions
		 *   that are powers of 2.
		 *   Will be destroyed when this class is destroyed.
		 * @param inPressedImages the images to display
		 *   when each button is pressed.  Images must have dimensions
		 *   that are powers of 2.
		 *   Will be destroyed when this class is destroyed.
		 * @param inGutterFraction the fraction of each row
		 *   and column that should be taken up by the gutters
		 *   (the spaces between buttons).  In [0, 1.0].
		 */
		MultiButtonGL(
			double inAnchorX, double inAnchorY, double inWidth,
			double inHeight, int inNumButtons,
			Image **inUnpressedImages,
			Image **inPressedImages,
			double inGutterFraction );


		
		~MultiButtonGL();


		
		/**
		 * Sets the currently depressed button.
		 *
		 * Note that if this function causes a change
		 * in the state of the set, an action will
		 * be fired to all registered listeners.
		 *
		 * @param inButtonIndex the index of the button to depress.
		 */
		void setSelectedButton( int inButtonIndex );



		/**
		 * Gets the index of the currently depressed button.
		 *
		 * Note that if this function causes a change
		 * in the state of the set, an action will
		 * be fired to all registered listeners.
		 *
		 * @return the index of the button that is depressed.
		 */
		int getSelectedButton();


		
		// we don't need to override any mouse
		// or redraw functions, since the container
		// and the buttons should handle these correctly


		// implements the ActionListener interface
		virtual void actionPerformed( GUIComponent *inTarget );
		
		
	protected:
		int mNumButtons;
		
		StickyButtonGL **mButtons;


		int mSelectedIndex;


		char mIgnoreEvents;
		
		/**
		 * Maps a button in mButtons to its index in the
		 * mButtons array.
		 *
		 * @param inButton the button to get an index for.
		 *
		 * @return the index of inButton in the mButtons array.
		 */
		int buttonToIndex( StickyButtonGL *inButton );

		
		
	};



inline MultiButtonGL::MultiButtonGL(
	double inAnchorX, double inAnchorY, double inWidth,
	double inHeight, int inNumButtons,
	Image **inUnpressedImages,
	Image **inPressedImages,
	double inGutterFraction )
	: GUIContainerGL( inAnchorX, inAnchorY, inWidth, inHeight ),
	  mNumButtons( inNumButtons ),
	  mButtons( new StickyButtonGL*[inNumButtons] ),
	  mSelectedIndex( 0 ),
	  mIgnoreEvents( false ) {

	int numColumns = (int)( sqrt( mNumButtons ) );
	int numRows = mNumButtons / numColumns;

	
	while( numRows * numColumns < mNumButtons ) {
		numRows++;
		}

	// determine gutter and button sizes
	
	double gutterSizeX;
	double gutterSizeY;
	
	if( numRows == 1 ) {
		gutterSizeY = 0;
		}
	else {
		gutterSizeY =
			( inHeight * inGutterFraction ) / ( numRows - 1 );
		}
	if( numColumns == 1 ) {
		gutterSizeX = 0;
		}
	else {
		gutterSizeX =
			( inWidth * inGutterFraction ) / ( numColumns - 1 );
		}
	
	
	double buttonSizeX =
		( inWidth * ( 1 - inGutterFraction ) ) / ( numColumns );
	double buttonSizeY =
		( inHeight * ( 1 - inGutterFraction ) ) / ( numRows );


	// setup each button
	
	int buttonIndex = 0;

	for( int r=0; r<numRows; r++ ) {
		for( int c=0; c<numColumns; c++ ) {
			
			double anchorX = inAnchorX +
				c * ( buttonSizeX + gutterSizeX );
			double anchorY = inAnchorY +
				r * ( buttonSizeY + gutterSizeY );

			mButtons[ buttonIndex ] =
				new StickyButtonGL( anchorX, anchorY,
									buttonSizeX, buttonSizeY,
									inUnpressedImages[ buttonIndex ],
									inPressedImages[ buttonIndex ] );

			GUIContainerGL::add( mButtons[ buttonIndex ] );
			
			buttonIndex++;

			if( buttonIndex >= mNumButtons ) {
				// jump out of our loop if we run out of buttons
				c = numColumns;
				r = numRows;
				}
			}
		}

	// now we've added all of our buttons

	// press one of them before we add any listeners
	mButtons[ mSelectedIndex ]->setPressed( true );
	

	// now add ourselves as an action listener to each button
	for( int i=0; i<mNumButtons; i++ ) {
		mButtons[ i ]->addActionListener( this );
		}

	// delete the arrays pointing to the images, since
	// they are no longer needed
	delete [] inUnpressedImages;
	delete [] inPressedImages;
	
	}


		
inline MultiButtonGL::~MultiButtonGL() {
	// note that we don't need to delete the buttons
	// themselves, since they will be deleted by GUIContainer
	// destructor
	
	delete [] mButtons;
	}



inline void MultiButtonGL::setSelectedButton( int inButtonIndex ) {
	// simply press the appropriate button, and let the
	// action handlers do the rest
	
	mButtons[ inButtonIndex ]->setPressed( true );
	}



inline int MultiButtonGL::getSelectedButton() {
	return mSelectedIndex;
	}



inline void MultiButtonGL::actionPerformed( GUIComponent *inTarget ) {
	if( !mIgnoreEvents ) {
		int buttonIndex = buttonToIndex( (StickyButtonGL*)inTarget );

		// if another button is being pressed
		if( buttonIndex != mSelectedIndex
			&& mButtons[ buttonIndex ]->isPressed() ) {

		
			// unpress the old button, ignoring the resulting event
			mIgnoreEvents = true;
			mButtons[ mSelectedIndex ]->setPressed( false );
			mIgnoreEvents = false;
			
			mSelectedIndex = buttonIndex;
			
			fireActionPerformed( this );
			}
		// else if our selected button is being unpressed
		else if( buttonIndex == mSelectedIndex
				 && !( mButtons[ buttonIndex ]->isPressed() ) ) {
			// don't allow it to become unpressed

			// re-press it, ignoring the resulting event
			mIgnoreEvents = true;
			mButtons[ mSelectedIndex ]->setPressed( true );
			mIgnoreEvents = false;

			// don't fire an action, since our state
			// has not changed
			}
		}
	}



inline int MultiButtonGL::buttonToIndex( StickyButtonGL *inButton ) {
	for( int i=0; i<mNumButtons; i++ ) {
		if( mButtons[i] == inButton ) {
			return i;
			}
		}
	
	// return the first button index by default
	printf( "MultiButtonGL:  no matching button found.\n" );
	return 0;
	}



#endif