comparison ext/guichan-0.8.1/src/widgets/listbox.cpp @ 0:4a0efb7baf70

* Datasets becomes the new trunk and retires after that :-)
author mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222
date Sun, 29 Jun 2008 18:44:17 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4a0efb7baf70
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 Rectangle scroll;
188
189 if (mSelected < 0)
190 {
191 scroll.y = 0;
192 }
193 else
194 {
195 scroll.y = getRowHeight() * mSelected;
196 }
197
198 scroll.height = getRowHeight();
199 showPart(scroll);
200 }
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 }