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