comparison ext/guichan-0.8.2/src/widgets/listbox.cpp @ 378:64738befdf3b

bringing in the changes from the build_system_rework branch in preparation for the 0.3.0 release. This commit will require the Jan2010 devkit. Clients will also need to be modified to the new way to import fife.
author vtchill@33b003aa-7bff-0310-803a-e67f0ece8222
date Mon, 11 Jan 2010 23:34:52 +0000
parents
children
comparison
equal deleted inserted replaced
377:fe6fb0e0ed23 378:64738befdf3b
1 /* _______ __ __ __ ______ __ __ _______ __ __
2 * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
3 * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
4 * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
5 * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
6 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
7 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
8 *
9 * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
10 *
11 *
12 * Per Larsson a.k.a finalman
13 * Olof Naessén a.k.a jansem/yakslem
14 *
15 * Visit: http://guichan.sourceforge.net
16 *
17 * License: (BSD)
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
20 * are met:
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in
25 * the documentation and/or other materials provided with the
26 * distribution.
27 * 3. Neither the name of Guichan nor the names of its contributors may
28 * be used to endorse or promote products derived from this software
29 * without specific prior written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
37 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
38 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
39 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
40 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 */
43
44 /*
45 * For comments regarding functions please see the header file.
46 */
47
48 #include "guichan/widgets/listbox.hpp"
49
50 #include "guichan/basiccontainer.hpp"
51 #include "guichan/font.hpp"
52 #include "guichan/graphics.hpp"
53 #include "guichan/key.hpp"
54 #include "guichan/listmodel.hpp"
55 #include "guichan/mouseinput.hpp"
56 #include "guichan/selectionlistener.hpp"
57
58 namespace gcn
59 {
60 ListBox::ListBox()
61 : mSelected(-1),
62 mListModel(NULL),
63 mWrappingEnabled(false)
64 {
65 setWidth(100);
66 setFocusable(true);
67
68 addMouseListener(this);
69 addKeyListener(this);
70 }
71
72 ListBox::ListBox(ListModel *listModel)
73 : mSelected(-1),
74 mWrappingEnabled(false)
75 {
76 setWidth(100);
77 setListModel(listModel);
78 setFocusable(true);
79
80 addMouseListener(this);
81 addKeyListener(this);
82 }
83
84 void ListBox::draw(Graphics* graphics)
85 {
86 graphics->setColor(getBackgroundColor());
87 graphics->fillRectangle(Rectangle(0, 0, getWidth(), getHeight()));
88
89 if (mListModel == NULL)
90 {
91 return;
92 }
93
94 graphics->setColor(getForegroundColor());
95 graphics->setFont(getFont());
96
97 // Check the current clip area so we don't draw unnecessary items
98 // that are not visible.
99 const ClipRectangle currentClipArea = graphics->getCurrentClipArea();
100 int rowHeight = getRowHeight();
101
102 // Calculate the number of rows to draw by checking the clip area.
103 // The addition of two makes covers a partial visible row at the top
104 // and a partial visible row at the bottom.
105 int numberOfRows = currentClipArea.height / rowHeight + 2;
106
107 if (numberOfRows > mListModel->getNumberOfElements())
108 {
109 numberOfRows = mListModel->getNumberOfElements();
110 }
111
112 // Calculate which row to start drawing. If the list box
113 // has a negative y coordinate value we should check if
114 // we should drop rows in the begining of the list as
115 // they might not be visible. A negative y value is very
116 // common if the list box for instance resides in a scroll
117 // area and the user has scrolled the list box downwards.
118 int startRow;
119 if (getY() < 0)
120 {
121 startRow = -1 * (getY() / rowHeight);
122 }
123 else
124 {
125 startRow = 0;
126 }
127
128 int i;
129 // The y coordinate where we start to draw the text is
130 // simply the y coordinate multiplied with the font height.
131 int y = rowHeight * startRow;
132 for (i = startRow; i < startRow + numberOfRows; ++i)
133 {
134 if (i == mSelected)
135 {
136 graphics->setColor(getSelectionColor());
137 graphics->fillRectangle(Rectangle(0, y, getWidth(), rowHeight));
138 graphics->setColor(getForegroundColor());
139 }
140
141 // If the row height is greater than the font height we
142 // draw the text with a center vertical alignment.
143 if (rowHeight > getFont()->getHeight())
144 {
145 graphics->drawText(mListModel->getElementAt(i), 1, y + rowHeight / 2 - getFont()->getHeight() / 2);
146 }
147 else
148 {
149 graphics->drawText(mListModel->getElementAt(i), 1, y);
150 }
151
152 y += rowHeight;
153 }
154 }
155
156 void ListBox::logic()
157 {
158 adjustSize();
159 }
160
161 int ListBox::getSelected() const
162 {
163 return mSelected;
164 }
165
166 void ListBox::setSelected(int selected)
167 {
168 if (mListModel == NULL)
169 {
170 mSelected = -1;
171 }
172 else
173 {
174 if (selected < 0)
175 {
176 mSelected = -1;
177 }
178 else if (selected >= mListModel->getNumberOfElements())
179 {
180 mSelected = mListModel->getNumberOfElements() - 1;
181 }
182 else
183 {
184 mSelected = selected;
185 }
186 }
187
188 Rectangle scroll;
189
190 if (mSelected < 0)
191 {
192 scroll.y = 0;
193 }
194 else
195 {
196 scroll.y = getRowHeight() * mSelected;
197 }
198
199 scroll.height = getRowHeight();
200 showPart(scroll);
201
202 distributeValueChangedEvent();
203 }
204
205 void ListBox::keyPressed(KeyEvent& keyEvent)
206 {
207 Key key = keyEvent.getKey();
208
209 if (key.getValue() == Key::ENTER || key.getValue() == Key::SPACE)
210 {
211 distributeActionEvent();
212 keyEvent.consume();
213 }
214 else if (key.getValue() == Key::UP)
215 {
216 setSelected(mSelected - 1);
217
218 if (mSelected == -1)
219 {
220 if (mWrappingEnabled)
221 {
222 setSelected(getListModel()->getNumberOfElements() - 1);
223 }
224 else
225 {
226 setSelected(0);
227 }
228 }
229
230 keyEvent.consume();
231 }
232 else if (key.getValue() == Key::DOWN)
233 {
234 if (mWrappingEnabled
235 && getSelected() == getListModel()->getNumberOfElements() - 1)
236 {
237 setSelected(0);
238 }
239 else
240 {
241 setSelected(getSelected() + 1);
242 }
243
244 keyEvent.consume();
245 }
246 else if (key.getValue() == Key::HOME)
247 {
248 setSelected(0);
249 keyEvent.consume();
250 }
251 else if (key.getValue() == Key::END)
252 {
253 setSelected(getListModel()->getNumberOfElements() - 1);
254 keyEvent.consume();
255 }
256 }
257
258 void ListBox::mousePressed(MouseEvent& mouseEvent)
259 {
260 if (mouseEvent.getButton() == MouseEvent::LEFT)
261 {
262 setSelected(mouseEvent.getY() / getRowHeight());
263 distributeActionEvent();
264 }
265 }
266
267 void ListBox::mouseWheelMovedUp(MouseEvent& mouseEvent)
268 {
269 if (isFocused())
270 {
271 if (getSelected() > 0 )
272 {
273 setSelected(getSelected() - 1);
274 }
275
276 mouseEvent.consume();
277 }
278 }
279
280 void ListBox::mouseWheelMovedDown(MouseEvent& mouseEvent)
281 {
282 if (isFocused())
283 {
284 setSelected(getSelected() + 1);
285
286 mouseEvent.consume();
287 }
288 }
289
290 void ListBox::mouseDragged(MouseEvent& mouseEvent)
291 {
292 mouseEvent.consume();
293 }
294
295 void ListBox::setListModel(ListModel *listModel)
296 {
297 mSelected = -1;
298 mListModel = listModel;
299 adjustSize();
300 }
301
302 ListModel* ListBox::getListModel()
303 {
304 return mListModel;
305 }
306
307 void ListBox::adjustSize()
308 {
309 if (mListModel != NULL)
310 {
311 setHeight(getRowHeight() * mListModel->getNumberOfElements());
312 }
313 }
314
315 bool ListBox::isWrappingEnabled() const
316 {
317 return mWrappingEnabled;
318 }
319
320 void ListBox::setWrappingEnabled(bool wrappingEnabled)
321 {
322 mWrappingEnabled = wrappingEnabled;
323 }
324
325 void ListBox::addSelectionListener(SelectionListener* selectionListener)
326 {
327 mSelectionListeners.push_back(selectionListener);
328 }
329
330 void ListBox::removeSelectionListener(SelectionListener* selectionListener)
331 {
332 mSelectionListeners.remove(selectionListener);
333 }
334
335 void ListBox::distributeValueChangedEvent()
336 {
337 SelectionListenerIterator iter;
338
339 for (iter = mSelectionListeners.begin(); iter != mSelectionListeners.end(); ++iter)
340 {
341 SelectionEvent event(this);
342 (*iter)->valueChanged(event);
343 }
344 }
345
346 unsigned int ListBox::getRowHeight() const
347 {
348 return getFont()->getHeight();
349 }
350 }