| 1 |  |  | /* | 
    
    | 2 |  |  |  *  The ManaPlus Client | 
    
    | 3 |  |  |  *  Copyright (C) 2004-2009  The Mana World Development Team | 
    
    | 4 |  |  |  *  Copyright (C) 2009-2010  The Mana Developers | 
    
    | 5 |  |  |  *  Copyright (C) 2011-2019  The ManaPlus Developers | 
    
    | 6 |  |  |  *  Copyright (C) 2019-2021  Andrei Karas | 
    
    | 7 |  |  |  * | 
    
    | 8 |  |  |  *  This file is part of The ManaPlus Client. | 
    
    | 9 |  |  |  * | 
    
    | 10 |  |  |  *  This program is free software; you can redistribute it and/or modify | 
    
    | 11 |  |  |  *  it under the terms of the GNU General Public License as published by | 
    
    | 12 |  |  |  *  the Free Software Foundation; either version 2 of the License, or | 
    
    | 13 |  |  |  *  any later version. | 
    
    | 14 |  |  |  * | 
    
    | 15 |  |  |  *  This program is distributed in the hope that it will be useful, | 
    
    | 16 |  |  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
    
    | 17 |  |  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
    
    | 18 |  |  |  *  GNU General Public License for more details. | 
    
    | 19 |  |  |  * | 
    
    | 20 |  |  |  *  You should have received a copy of the GNU General Public License | 
    
    | 21 |  |  |  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
    
    | 22 |  |  |  */ | 
    
    | 23 |  |  |  | 
    
    | 24 |  |  | /*      _______   __   __   __   ______   __   __   _______   __   __ | 
    
    | 25 |  |  |  *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\ | 
    
    | 26 |  |  |  *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / / | 
    
    | 27 |  |  |  *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / / | 
    
    | 28 |  |  |  *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / / | 
    
    | 29 |  |  |  * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ / | 
    
    | 30 |  |  |  * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/ | 
    
    | 31 |  |  |  * | 
    
    | 32 |  |  |  * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson | 
    
    | 33 |  |  |  * | 
    
    | 34 |  |  |  * | 
    
    | 35 |  |  |  * Per Larsson a.k.a finalman | 
    
    | 36 |  |  |  * Olof Naessén a.k.a jansem/yakslem | 
    
    | 37 |  |  |  * | 
    
    | 38 |  |  |  * Visit: http://guichan.sourceforge.net | 
    
    | 39 |  |  |  * | 
    
    | 40 |  |  |  * License: (BSD) | 
    
    | 41 |  |  |  * Redistribution and use in source and binary forms, with or without | 
    
    | 42 |  |  |  * modification, are permitted provided that the following conditions | 
    
    | 43 |  |  |  * are met: | 
    
    | 44 |  |  |  * 1. Redistributions of source code must retain the above copyright | 
    
    | 45 |  |  |  *    notice, this list of conditions and the following disclaimer. | 
    
    | 46 |  |  |  * 2. Redistributions in binary form must reproduce the above copyright | 
    
    | 47 |  |  |  *    notice, this list of conditions and the following disclaimer in | 
    
    | 48 |  |  |  *    the documentation and/or other materials provided with the | 
    
    | 49 |  |  |  *    distribution. | 
    
    | 50 |  |  |  * 3. Neither the name of Guichan nor the names of its contributors may | 
    
    | 51 |  |  |  *    be used to endorse or promote products derived from this software | 
    
    | 52 |  |  |  *    without specific prior written permission. | 
    
    | 53 |  |  |  * | 
    
    | 54 |  |  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
    
    | 55 |  |  |  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
    
    | 56 |  |  |  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
    
    | 57 |  |  |  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
    
    | 58 |  |  |  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
    
    | 59 |  |  |  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | 
    
    | 60 |  |  |  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
    
    | 61 |  |  |  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | 
    
    | 62 |  |  |  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | 
    
    | 63 |  |  |  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 
    
    | 64 |  |  |  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
    
    | 65 |  |  |  */ | 
    
    | 66 |  |  |  | 
    
    | 67 |  |  | #include "gui/widgets/listbox.h" | 
    
    | 68 |  |  |  | 
    
    | 69 |  |  | #include "settings.h" | 
    
    | 70 |  |  |  | 
    
    | 71 |  |  | #include "gui/focushandler.h" | 
    
    | 72 |  |  | #include "gui/gui.h" | 
    
    | 73 |  |  | #include "gui/skin.h" | 
    
    | 74 |  |  |  | 
    
    | 75 |  |  | #include "gui/fonts/font.h" | 
    
    | 76 |  |  |  | 
    
    | 77 |  |  | #include "gui/models/listmodel.h" | 
    
    | 78 |  |  |  | 
    
    | 79 |  |  | #include "listeners/selectionlistener.h" | 
    
    | 80 |  |  |  | 
    
    | 81 |  |  | #include "utils/foreach.h" | 
    
    | 82 |  |  |  | 
    
    | 83 |  |  | #include "render/graphics.h" | 
    
    | 84 |  |  |  | 
    
    | 85 |  |  | #include "debug.h" | 
    
    | 86 |  |  |  | 
    
    | 87 |  |  | float ListBox::mAlpha = 1.0; | 
    
    | 88 |  |  |  | 
    
    | 89 |  | 130 | ListBox::ListBox(const Widget2 *const widget, | 
    
    | 90 |  |  |                  ListModel *const listModel, | 
    
    | 91 |  | 130 |                  const std::string &skin) : | 
    
    | 92 |  |  |     Widget(widget), | 
    
    | 93 |  |  |     MouseListener(), | 
    
    | 94 |  |  |     KeyListener(), | 
    
    | 95 |  |  |     mSelected(-1), | 
    
    | 96 |  |  |     mListModel(listModel), | 
    
    | 97 |  |  |     mWrappingEnabled(false), | 
    
    | 98 |  |  |     mSelectionListeners(), | 
    
    | 99 |  | 260 |     mHighlightColor(getThemeColor(ThemeColorId::HIGHLIGHT, 255U)), | 
    
    | 100 |  |  |     mForegroundSelectedColor(getThemeColor(ThemeColorId::LISTBOX_SELECTED, | 
    
    | 101 |  | 260 |         255U)), | 
    
    | 102 |  |  |     mForegroundSelectedColor2(getThemeColor( | 
    
    | 103 |  | 260 |         ThemeColorId::LISTBOX_SELECTED_OUTLINE, 255U)), | 
    
    | 104 |  |  |     mOldSelected(-1), | 
    
    | 105 |  |  |     mPadding(0), | 
    
    | 106 |  |  |     mPressedIndex(-2), | 
    
    | 107 |  |  |     mRowHeight(0), | 
    
    | 108 |  |  |     mItemPadding(1), | 
    
    | 109 |  |  |     mSkin(nullptr), | 
    
    | 110 |  |  |     mDistributeMousePressed(true), | 
    
    | 111 |  | 910 |     mCenterText(false) | 
    
    | 112 |  |  | { | 
    
    | 113 | ✓✗ | 130 |     setWidth(100); | 
    
    | 114 | ✓✗ | 130 |     setFocusable(true); | 
    
    | 115 | ✓✗ | 130 |     addMouseListener(this); | 
    
    | 116 | ✓✗ | 130 |     addKeyListener(this); | 
    
    | 117 |  |  |  | 
    
    | 118 |  | 260 |     mForegroundColor = getThemeColor(ThemeColorId::LISTBOX, 255U); | 
    
    | 119 |  | 260 |     mForegroundColor2 = getThemeColor(ThemeColorId::LISTBOX_OUTLINE, 255U); | 
    
    | 120 |  |  |  | 
    
    | 121 | ✓✗ | 130 |     if (theme != nullptr) | 
    
    | 122 |  |  |     { | 
    
    | 123 | ✓✗✓✗ 
 | 520 |         mSkin = theme->load(skin, | 
    
    | 124 |  |  |             "listbox.xml", | 
    
    | 125 |  |  |             true, | 
    
    | 126 |  | 130 |             Theme::getThemePath()); | 
    
    | 127 |  |  |     } | 
    
    | 128 |  |  |  | 
    
    | 129 | ✓✗ | 130 |     if (mSkin != nullptr) | 
    
    | 130 |  |  |     { | 
    
    | 131 |  | 260 |         mPadding = mSkin->getPadding(); | 
    
    | 132 | ✓✗✓✗ 
 | 520 |         mItemPadding = mSkin->getOption("itemPadding"); | 
    
    | 133 |  |  |     } | 
    
    | 134 |  |  |  | 
    
    | 135 | ✓✗ | 130 |     const Font *const font = getFont(); | 
    
    | 136 |  | 130 |     mRowHeight = CAST_U32( | 
    
    | 137 | ✓✗ | 130 |         font->getHeight() + 2 * mItemPadding); | 
    
    | 138 |  | 130 | } | 
    
    | 139 |  |  |  | 
    
    | 140 |  | 130 | void ListBox::postInit() | 
    
    | 141 |  |  | { | 
    
    | 142 |  | 130 |     adjustSize(); | 
    
    | 143 |  | 130 | } | 
    
    | 144 |  |  |  | 
    
    | 145 |  | 757 | ListBox::~ListBox() | 
    
    | 146 |  |  | { | 
    
    | 147 | ✓✗ | 130 |     if (gui != nullptr) | 
    
    | 148 |  | 130 |         gui->removeDragged(this); | 
    
    | 149 |  |  |  | 
    
    | 150 | ✓✗ | 130 |     if (theme != nullptr) | 
    
    | 151 |  | 130 |         theme->unload(mSkin); | 
    
    | 152 |  | 237 | } | 
    
    | 153 |  |  |  | 
    
    | 154 |  | 2 | void ListBox::updateAlpha() | 
    
    | 155 |  |  | { | 
    
    | 156 |  |  |     const float alpha = std::max(settings.guiAlpha, | 
    
    | 157 |  | 12 |         theme->getMinimumOpacity()); | 
    
    | 158 |  |  |  | 
    
    | 159 | ✗✓✗✓ 
 | 4 |     if (mAlpha != alpha) | 
    
    | 160 |  |  |         mAlpha = alpha; | 
    
    | 161 |  | 2 | } | 
    
    | 162 |  |  |  | 
    
    | 163 |  | 2 | void ListBox::draw(Graphics *const graphics) | 
    
    | 164 |  |  | { | 
    
    | 165 | ✓✗ | 2 |     if (mListModel == nullptr) | 
    
    | 166 |  |  |         return; | 
    
    | 167 |  |  |  | 
    
    | 168 |  |  |     BLOCK_START("ListBox::draw") | 
    
    | 169 |  | 2 |     updateAlpha(); | 
    
    | 170 |  |  |  | 
    
    | 171 |  | 2 |     mHighlightColor.a = CAST_U32(mAlpha * 255.0F); | 
    
    | 172 |  | 2 |     graphics->setColor(mHighlightColor); | 
    
    | 173 |  | 2 |     Font *const font = getFont(); | 
    
    | 174 |  | 2 |     const int rowHeight = CAST_S32(getRowHeight()); | 
    
    | 175 |  | 2 |     const int width = mDimension.width; | 
    
    | 176 |  |  |  | 
    
    | 177 | ✗✓ | 2 |     if (mCenterText) | 
    
    | 178 |  |  |     { | 
    
    | 179 |  |  |         // Draw filled rectangle around the selected list element | 
    
    | 180 |  |  |         if (mSelected >= 0) | 
    
    | 181 |  |  |         { | 
    
    | 182 |  |  |             graphics->fillRectangle(Rect(mPadding, | 
    
    | 183 |  |  |                 rowHeight * mSelected + mPadding, | 
    
    | 184 |  |  |                 mDimension.width - 2 * mPadding, rowHeight)); | 
    
    | 185 |  |  |  | 
    
    | 186 |  |  |             const std::string str = mListModel->getElementAt(mSelected); | 
    
    | 187 |  |  |             font->drawString(graphics, | 
    
    | 188 |  |  |                 mForegroundSelectedColor, | 
    
    | 189 |  |  |                 mForegroundSelectedColor2, | 
    
    | 190 |  |  |                 str, | 
    
    | 191 |  |  |                 (width - font->getWidth(str)) / 2, | 
    
    | 192 |  |  |                 mSelected * rowHeight + mPadding + mItemPadding); | 
    
    | 193 |  |  |         } | 
    
    | 194 |  |  |         // Draw the list elements | 
    
    | 195 |  |  |         const int sz = mListModel->getNumberOfElements(); | 
    
    | 196 |  |  |         for (int i = 0, y = mPadding + mItemPadding; | 
    
    | 197 |  |  |              i < sz; ++i, y += rowHeight) | 
    
    | 198 |  |  |         { | 
    
    | 199 |  |  |             if (i != mSelected) | 
    
    | 200 |  |  |             { | 
    
    | 201 |  |  |                 const std::string str = mListModel->getElementAt(i); | 
    
    | 202 |  |  |                 font->drawString(graphics, | 
    
    | 203 |  |  |                     mForegroundColor, | 
    
    | 204 |  |  |                     mForegroundColor2, | 
    
    | 205 |  |  |                     str, | 
    
    | 206 |  |  |                     (width - font->getWidth(str)) / 2, y); | 
    
    | 207 |  |  |             } | 
    
    | 208 |  |  |         } | 
    
    | 209 |  |  |     } | 
    
    | 210 |  |  |     else | 
    
    | 211 |  |  |     { | 
    
    | 212 |  |  |         // Draw filled rectangle around the selected list element | 
    
    | 213 | ✗✓ | 2 |         if (mSelected >= 0) | 
    
    | 214 |  |  |         { | 
    
    | 215 |  |  |             graphics->fillRectangle(Rect(mPadding, | 
    
    | 216 |  |  |                 rowHeight * mSelected + mPadding, | 
    
    | 217 |  |  |                 mDimension.width - 2 * mPadding, rowHeight)); | 
    
    | 218 |  |  |  | 
    
    | 219 |  |  |             const std::string str = mListModel->getElementAt(mSelected); | 
    
    | 220 |  |  |             font->drawString(graphics, | 
    
    | 221 |  |  |                 mForegroundSelectedColor, | 
    
    | 222 |  |  |                 mForegroundSelectedColor2, | 
    
    | 223 |  |  |                 str, | 
    
    | 224 |  |  |                 mPadding, | 
    
    | 225 |  |  |                 mSelected * rowHeight + mPadding + mItemPadding); | 
    
    | 226 |  |  |         } | 
    
    | 227 |  |  |         // Draw the list elements | 
    
    | 228 |  | 2 |         const int sz = mListModel->getNumberOfElements(); | 
    
    | 229 | ✗✓ | 2 |         for (int i = 0, y = mPadding + mItemPadding; i < sz; | 
    
    | 230 |  |  |              ++i, y += rowHeight) | 
    
    | 231 |  |  |         { | 
    
    | 232 |  |  |             if (i != mSelected) | 
    
    | 233 |  |  |             { | 
    
    | 234 |  |  |                 const std::string str = mListModel->getElementAt(i); | 
    
    | 235 |  |  |                 font->drawString(graphics, | 
    
    | 236 |  |  |                     mForegroundColor, | 
    
    | 237 |  |  |                     mForegroundColor2, | 
    
    | 238 |  |  |                     str, | 
    
    | 239 |  |  |                     mPadding, y); | 
    
    | 240 |  |  |             } | 
    
    | 241 |  |  |         } | 
    
    | 242 |  |  |     } | 
    
    | 243 |  |  |     BLOCK_END("ListBox::draw") | 
    
    | 244 |  |  | } | 
    
    | 245 |  |  |  | 
    
    | 246 |  |  | void ListBox::keyPressed(KeyEvent &event) | 
    
    | 247 |  |  | { | 
    
    | 248 |  |  |     const InputActionT action = event.getActionId(); | 
    
    | 249 |  |  |     if (action == InputAction::GUI_SELECT) | 
    
    | 250 |  |  |     { | 
    
    | 251 |  |  |         distributeActionEvent(); | 
    
    | 252 |  |  |         event.consume(); | 
    
    | 253 |  |  |     } | 
    
    | 254 |  |  |     else if (action == InputAction::GUI_UP) | 
    
    | 255 |  |  |     { | 
    
    | 256 |  |  |         if (mSelected > 0) | 
    
    | 257 |  |  |         { | 
    
    | 258 |  |  |             setSelected(mSelected - 1); | 
    
    | 259 |  |  |         } | 
    
    | 260 |  |  |         else if (mSelected == 0 && | 
    
    | 261 |  |  |                  mWrappingEnabled && | 
    
    | 262 |  |  |                  getListModel() != nullptr) | 
    
    | 263 |  |  |         { | 
    
    | 264 |  |  |             setSelected(getListModel()->getNumberOfElements() - 1); | 
    
    | 265 |  |  |         } | 
    
    | 266 |  |  |         event.consume(); | 
    
    | 267 |  |  |     } | 
    
    | 268 |  |  |     else if (action == InputAction::GUI_DOWN) | 
    
    | 269 |  |  |     { | 
    
    | 270 |  |  |         const int num = getListModel()->getNumberOfElements() - 1; | 
    
    | 271 |  |  |         if (mSelected < num) | 
    
    | 272 |  |  |             setSelected(mSelected + 1); | 
    
    | 273 |  |  |         else if (mSelected == num && mWrappingEnabled) | 
    
    | 274 |  |  |             setSelected(0); | 
    
    | 275 |  |  |         event.consume(); | 
    
    | 276 |  |  |     } | 
    
    | 277 |  |  |     else if (action == InputAction::GUI_HOME) | 
    
    | 278 |  |  |     { | 
    
    | 279 |  |  |         setSelected(0); | 
    
    | 280 |  |  |         event.consume(); | 
    
    | 281 |  |  |     } | 
    
    | 282 |  |  |     else if (action == InputAction::GUI_END && (getListModel() != nullptr)) | 
    
    | 283 |  |  |     { | 
    
    | 284 |  |  |         setSelected(getListModel()->getNumberOfElements() - 1); | 
    
    | 285 |  |  |         event.consume(); | 
    
    | 286 |  |  |     } | 
    
    | 287 |  |  | } | 
    
    | 288 |  |  |  | 
    
    | 289 |  |  | void ListBox::safeDraw(Graphics *const graphics) | 
    
    | 290 |  |  | { | 
    
    | 291 |  |  |     ListBox::draw(graphics); | 
    
    | 292 |  |  | } | 
    
    | 293 |  |  |  | 
    
    | 294 |  |  | // Don't do anything on scrollwheel. ScrollArea will deal with that. | 
    
    | 295 |  |  |  | 
    
    | 296 |  |  | void ListBox::mouseWheelMovedUp(MouseEvent &event A_UNUSED) | 
    
    | 297 |  |  | { | 
    
    | 298 |  |  | } | 
    
    | 299 |  |  |  | 
    
    | 300 |  |  | void ListBox::mouseWheelMovedDown(MouseEvent &event A_UNUSED) | 
    
    | 301 |  |  | { | 
    
    | 302 |  |  | } | 
    
    | 303 |  |  |  | 
    
    | 304 |  |  | void ListBox::mousePressed(MouseEvent &event) | 
    
    | 305 |  |  | { | 
    
    | 306 |  |  |     mPressedIndex = getSelectionByMouse(event.getY()); | 
    
    | 307 |  |  |     if (mMouseConsume && mPressedIndex != -1) | 
    
    | 308 |  |  |         event.consume(); | 
    
    | 309 |  |  | } | 
    
    | 310 |  |  |  | 
    
    | 311 |  |  | void ListBox::mouseReleased(MouseEvent &event) | 
    
    | 312 |  |  | { | 
    
    | 313 |  |  |     if (mPressedIndex != getSelectionByMouse(event.getY())) | 
    
    | 314 |  |  |         return; | 
    
    | 315 |  |  |  | 
    
    | 316 |  |  |     if (mDistributeMousePressed) | 
    
    | 317 |  |  |     { | 
    
    | 318 |  |  |         mouseReleased1(event); | 
    
    | 319 |  |  |     } | 
    
    | 320 |  |  |     else | 
    
    | 321 |  |  |     { | 
    
    | 322 |  |  |         switch (event.getClickCount()) | 
    
    | 323 |  |  |         { | 
    
    | 324 |  |  |             case 1: | 
    
    | 325 |  |  |                 mouseDragged(event); | 
    
    | 326 |  |  |                 mOldSelected = mSelected; | 
    
    | 327 |  |  |                 break; | 
    
    | 328 |  |  |             case 2: | 
    
    | 329 |  |  |                 if (gui != nullptr) | 
    
    | 330 |  |  |                     gui->resetClickCount(); | 
    
    | 331 |  |  |                 if (mOldSelected == mSelected) | 
    
    | 332 |  |  |                     mouseReleased1(event); | 
    
    | 333 |  |  |                 else | 
    
    | 334 |  |  |                     mouseDragged(event); | 
    
    | 335 |  |  |                 mOldSelected = mSelected; | 
    
    | 336 |  |  |                 break; | 
    
    | 337 |  |  |             default: | 
    
    | 338 |  |  |                 mouseDragged(event); | 
    
    | 339 |  |  |                 mOldSelected = mSelected; | 
    
    | 340 |  |  |                 break; | 
    
    | 341 |  |  |         } | 
    
    | 342 |  |  |     } | 
    
    | 343 |  |  |     mPressedIndex = -2; | 
    
    | 344 |  |  | } | 
    
    | 345 |  |  |  | 
    
    | 346 |  |  | void ListBox::mouseReleased1(const MouseEvent &event) | 
    
    | 347 |  |  | { | 
    
    | 348 |  |  |     if (event.getButton() == MouseButton::LEFT) | 
    
    | 349 |  |  |     { | 
    
    | 350 |  |  |         setSelected(std::max(0, getSelectionByMouse(event.getY()))); | 
    
    | 351 |  |  |         distributeActionEvent(); | 
    
    | 352 |  |  |     } | 
    
    | 353 |  |  | } | 
    
    | 354 |  |  |  | 
    
    | 355 |  |  | void ListBox::mouseDragged(MouseEvent &event) | 
    
    | 356 |  |  | { | 
    
    | 357 |  |  |     if (event.getButton() != MouseButton::LEFT || getRowHeight() == 0) | 
    
    | 358 |  |  |         return; | 
    
    | 359 |  |  |  | 
    
    | 360 |  |  |     // Make list selection update on drag, but guard against negative y | 
    
    | 361 |  |  |     if (getRowHeight() != 0U) | 
    
    | 362 |  |  |         setSelected(std::max(0, getSelectionByMouse(event.getY()))); | 
    
    | 363 |  |  | } | 
    
    | 364 |  |  |  | 
    
    | 365 |  |  | void ListBox::refocus() | 
    
    | 366 |  |  | { | 
    
    | 367 |  |  |     if (mFocusHandler == nullptr) | 
    
    | 368 |  |  |         return; | 
    
    | 369 |  |  |  | 
    
    | 370 |  |  |     if (isFocusable()) | 
    
    | 371 |  |  |         mFocusHandler->requestFocus(this); | 
    
    | 372 |  |  | } | 
    
    | 373 |  |  |  | 
    
    | 374 |  | 515 | void ListBox::adjustSize() | 
    
    | 375 |  |  | { | 
    
    | 376 |  |  |     BLOCK_START("ListBox::adjustSize") | 
    
    | 377 | ✓✗ | 515 |     if (mListModel != nullptr) | 
    
    | 378 |  |  |     { | 
    
    | 379 |  | 1545 |         setHeight(CAST_S32(getRowHeight()) * | 
    
    | 380 |  | 1545 |             mListModel->getNumberOfElements() + 2 * mPadding); | 
    
    | 381 |  |  |     } | 
    
    | 382 |  |  |     BLOCK_END("ListBox::adjustSize") | 
    
    | 383 |  | 515 | } | 
    
    | 384 |  |  |  | 
    
    | 385 |  |  | void ListBox::logic() | 
    
    | 386 |  |  | { | 
    
    | 387 |  |  |     BLOCK_START("ListBox::logic") | 
    
    | 388 |  |  |     adjustSize(); | 
    
    | 389 |  |  |     BLOCK_END("ListBox::logic") | 
    
    | 390 |  |  | } | 
    
    | 391 |  |  |  | 
    
    | 392 |  |  | int ListBox::getSelectionByMouse(const int y) const | 
    
    | 393 |  |  | { | 
    
    | 394 |  |  |     if (y < mPadding) | 
    
    | 395 |  |  |         return -1; | 
    
    | 396 |  |  |     return (y - mPadding) / CAST_S32(getRowHeight()); | 
    
    | 397 |  |  | } | 
    
    | 398 |  |  |  | 
    
    | 399 |  | 210 | void ListBox::setSelected(const int selected) | 
    
    | 400 |  |  | { | 
    
    | 401 | ✗✓ | 210 |     if (mListModel == nullptr) | 
    
    | 402 |  |  |     { | 
    
    | 403 |  |  |         mSelected = -1; | 
    
    | 404 |  |  |     } | 
    
    | 405 |  |  |     else | 
    
    | 406 |  |  |     { | 
    
    | 407 | ✗✓ | 210 |         if (selected < 0) | 
    
    | 408 |  |  |             mSelected = -1; | 
    
    | 409 | ✓✓ | 210 |         else if (selected >= mListModel->getNumberOfElements()) | 
    
    | 410 |  | 6 |             mSelected = mListModel->getNumberOfElements() - 1; | 
    
    | 411 |  |  |         else | 
    
    | 412 |  | 204 |             mSelected = selected; | 
    
    | 413 |  |  |     } | 
    
    | 414 |  |  |  | 
    
    | 415 |  | 420 |     Rect scroll; | 
    
    | 416 |  |  |  | 
    
    | 417 | ✓✓ | 210 |     if (mSelected < 0) | 
    
    | 418 |  |  |         scroll.y = 0; | 
    
    | 419 |  |  |     else | 
    
    | 420 |  | 204 |         scroll.y = CAST_S32(getRowHeight()) * mSelected; | 
    
    | 421 |  |  |  | 
    
    | 422 |  | 210 |     scroll.height = CAST_S32(getRowHeight()); | 
    
    | 423 |  | 210 |     showPart(scroll); | 
    
    | 424 |  |  |  | 
    
    | 425 |  | 210 |     distributeValueChangedEvent(); | 
    
    | 426 |  | 210 | } | 
    
    | 427 |  |  |  | 
    
    | 428 |  | 99 | void ListBox::setListModel(ListModel *const listModel) | 
    
    | 429 |  |  | { | 
    
    | 430 |  | 99 |     mSelected = -1; | 
    
    | 431 |  | 99 |     mListModel = listModel; | 
    
    | 432 |  | 99 |     adjustSize(); | 
    
    | 433 |  | 99 | } | 
    
    | 434 |  |  |  | 
    
    | 435 |  | 15 | void ListBox::addSelectionListener(SelectionListener *const selectionListener) | 
    
    | 436 |  |  | { | 
    
    | 437 |  | 30 |     mSelectionListeners.push_back(selectionListener); | 
    
    | 438 |  | 15 | } | 
    
    | 439 |  |  |  | 
    
    | 440 |  |  | void ListBox::removeSelectionListener(SelectionListener *const | 
    
    | 441 |  |  |                                       selectionListener) | 
    
    | 442 |  |  | { | 
    
    | 443 |  |  |     mSelectionListeners.remove(selectionListener); | 
    
    | 444 |  |  | } | 
    
    | 445 |  |  |  | 
    
    | 446 |  |  | void ListBox::distributeValueChangedEvent() | 
    
    | 447 |  |  | { | 
    
    | 448 | ✗✗✓✓ 
 | 630 |     FOR_EACH (SelectionListenerIterator, iter, mSelectionListeners) | 
    
    | 449 |  |  |     { | 
    
    | 450 |  | 12 |         SelectionEvent event(this); | 
    
    | 451 |  | 4 |         (*iter)->valueChanged(event); | 
    
    | 452 |  |  |     } | 
    
    | 453 |  |  | } |