comparison ext/guichan-0.8.1/src/widgets/dropdown.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 #include "guichan/widgets/dropdown.hpp"
45
46 #include "guichan/exception.hpp"
47 #include "guichan/font.hpp"
48 #include "guichan/graphics.hpp"
49 #include "guichan/key.hpp"
50 #include "guichan/listmodel.hpp"
51 #include "guichan/mouseinput.hpp"
52 #include "guichan/widgets/listbox.hpp"
53 #include "guichan/widgets/scrollarea.hpp"
54
55 namespace gcn
56 {
57 DropDown::DropDown(ListModel *listModel,
58 ScrollArea *scrollArea,
59 ListBox *listBox)
60 {
61 setWidth(100);
62 setFocusable(true);
63 mDroppedDown = false;
64 mPushed = false;
65 mIsDragged = false;
66
67 setInternalFocusHandler(&mInternalFocusHandler);
68
69 mInternalScrollArea = (scrollArea == NULL);
70 mInternalListBox = (listBox == NULL);
71
72 if (mInternalScrollArea)
73 {
74 mScrollArea = new ScrollArea();
75 }
76 else
77 {
78 mScrollArea = scrollArea;
79 }
80
81 if (mInternalListBox)
82 {
83 mListBox = new ListBox();
84 }
85 else
86 {
87 mListBox = listBox;
88 }
89
90 mScrollArea->setContent(mListBox);
91 add(mScrollArea);
92
93 mListBox->addActionListener(this);
94 mListBox->addSelectionListener(this);
95
96 setListModel(listModel);
97
98 if (mListBox->getSelected() < 0)
99 {
100 mListBox->setSelected(0);
101 }
102
103 addMouseListener(this);
104 addKeyListener(this);
105 addFocusListener(this);
106
107 adjustHeight();
108 }
109
110 DropDown::~DropDown()
111 {
112 if (widgetExists(mListBox))
113 {
114 mListBox->removeActionListener(this);
115 mListBox->removeSelectionListener(this);
116 }
117
118 if (mInternalScrollArea)
119 {
120 delete mScrollArea;
121 }
122
123 if (mInternalListBox)
124 {
125 delete mListBox;
126 }
127
128 setInternalFocusHandler(NULL);
129 }
130
131 void DropDown::draw(Graphics* graphics)
132 {
133 int h;
134
135 if (mDroppedDown)
136 {
137 h = mFoldedUpHeight;
138 }
139 else
140 {
141 h = getHeight();
142 }
143
144 Color faceColor = getBaseColor();
145 Color highlightColor, shadowColor;
146 int alpha = getBaseColor().a;
147 highlightColor = faceColor + 0x303030;
148 highlightColor.a = alpha;
149 shadowColor = faceColor - 0x303030;
150 shadowColor.a = alpha;
151
152 // Draw a border.
153 graphics->setColor(shadowColor);
154 graphics->drawLine(0, 0, getWidth() - 1, 0);
155 graphics->drawLine(0, 1, 0, h - 2);
156 graphics->setColor(highlightColor);
157 graphics->drawLine(getWidth() - 1, 1, getWidth() - 1, h - 1);
158 graphics->drawLine(0, h - 1, getWidth() - 1, h - 1);
159
160 // Push a clip area so the other drawings don't need to worry
161 // about the border.
162 graphics->pushClipArea(Rectangle(1, 1, getWidth() - 2, h - 2));
163 const Rectangle currentClipArea = graphics->getCurrentClipArea();
164
165 graphics->setColor(getBackgroundColor());
166 graphics->fillRectangle(Rectangle(0, 0, currentClipArea.width, currentClipArea.height));
167
168 if (isFocused())
169 {
170 graphics->setColor(getSelectionColor());
171 graphics->fillRectangle(Rectangle(0,
172 0,
173 currentClipArea.width - currentClipArea.height,
174 currentClipArea.height));
175 graphics->setColor(getForegroundColor());
176 }
177
178 if (mListBox->getListModel()
179 && mListBox->getSelected() >= 0)
180 {
181 graphics->setColor(getForegroundColor());
182 graphics->setFont(getFont());
183
184 graphics->drawText(mListBox->getListModel()->getElementAt(mListBox->getSelected()), 1, 0);
185 }
186
187 // Push a clip area before drawing the button.
188 graphics->pushClipArea(Rectangle(currentClipArea.width - currentClipArea.height,
189 0,
190 currentClipArea.height,
191 currentClipArea.height));
192 drawButton(graphics);
193 graphics->popClipArea();
194 graphics->popClipArea();
195
196 if (mDroppedDown)
197 {
198 // Draw a border around the children.
199 graphics->setColor(shadowColor);
200 graphics->drawRectangle(Rectangle(0,
201 mFoldedUpHeight,
202 getWidth(),
203 getHeight() - mFoldedUpHeight));
204 drawChildren(graphics);
205 }
206 }
207
208 void DropDown::drawButton(Graphics *graphics)
209 {
210 Color faceColor, highlightColor, shadowColor;
211 int offset;
212 int alpha = getBaseColor().a;
213
214 if (mPushed)
215 {
216 faceColor = getBaseColor() - 0x303030;
217 faceColor.a = alpha;
218 highlightColor = faceColor - 0x303030;
219 highlightColor.a = alpha;
220 shadowColor = faceColor + 0x303030;
221 shadowColor.a = alpha;
222 offset = 1;
223 }
224 else
225 {
226 faceColor = getBaseColor();
227 faceColor.a = alpha;
228 highlightColor = faceColor + 0x303030;
229 highlightColor.a = alpha;
230 shadowColor = faceColor - 0x303030;
231 shadowColor.a = alpha;
232 offset = 0;
233 }
234
235 const Rectangle currentClipArea = graphics->getCurrentClipArea();
236 graphics->setColor(highlightColor);
237 graphics->drawLine(0,
238 0,
239 currentClipArea.width - 1,
240 0);
241 graphics->drawLine(0,
242 1,
243 0,
244 currentClipArea.height - 1);
245 graphics->setColor(shadowColor);
246 graphics->drawLine(currentClipArea.width - 1,
247 1,
248 currentClipArea.width - 1,
249 currentClipArea.height - 1);
250 graphics->drawLine(1,
251 currentClipArea.height - 1,
252 currentClipArea.width - 2,
253 currentClipArea.height - 1);
254
255 graphics->setColor(faceColor);
256 graphics->fillRectangle(Rectangle(1,
257 1,
258 currentClipArea.width - 2,
259 currentClipArea.height - 2));
260
261 graphics->setColor(getForegroundColor());
262
263 int i;
264 int n = currentClipArea.height / 3;
265 int dx = currentClipArea.height / 2;
266 int dy = (currentClipArea.height * 2) / 3;
267 for (i = 0; i < n; i++)
268 {
269 graphics->drawLine(dx - i + offset,
270 dy - i + offset,
271 dx + i + offset,
272 dy - i + offset);
273 }
274 }
275
276 int DropDown::getSelected() const
277 {
278 return mListBox->getSelected();
279 }
280
281 void DropDown::setSelected(int selected)
282 {
283 if (selected >= 0)
284 {
285 mListBox->setSelected(selected);
286 }
287 }
288
289 void DropDown::keyPressed(KeyEvent& keyEvent)
290 {
291 Key key = keyEvent.getKey();
292
293 if ((key.getValue() == Key::ENTER || key.getValue() == Key::SPACE)
294 && !mDroppedDown)
295 {
296 dropDown();
297 keyEvent.consume();
298 }
299 else if (key.getValue() == Key::UP)
300 {
301 setSelected(getSelected() - 1);
302 keyEvent.consume();
303 }
304 else if (key.getValue() == Key::DOWN)
305 {
306 setSelected(getSelected() + 1);
307 keyEvent.consume();
308 }
309 }
310
311 void DropDown::mousePressed(MouseEvent& mouseEvent)
312 {
313 // If we have a mouse press on the widget.
314 if (0 <= mouseEvent.getY()
315 && mouseEvent.getY() < getHeight()
316 && mouseEvent.getX() >= 0
317 && mouseEvent.getX() < getWidth()
318 && mouseEvent.getButton() == MouseEvent::LEFT
319 && !mDroppedDown
320 && mouseEvent.getSource() == this)
321 {
322 mPushed = true;
323 dropDown();
324 requestModalMouseInputFocus();
325 }
326 // Fold up the listbox if the upper part is clicked after fold down
327 else if (0 <= mouseEvent.getY()
328 && mouseEvent.getY() < mFoldedUpHeight
329 && mouseEvent.getX() >= 0
330 && mouseEvent.getX() < getWidth()
331 && mouseEvent.getButton() == MouseEvent::LEFT
332 && mDroppedDown
333 && mouseEvent.getSource() == this)
334 {
335 mPushed = false;
336 foldUp();
337 releaseModalMouseInputFocus();
338 }
339 // If we have a mouse press outside the widget
340 else if (0 > mouseEvent.getY()
341 || mouseEvent.getY() >= getHeight()
342 || mouseEvent.getX() < 0
343 || mouseEvent.getX() >= getWidth())
344 {
345 mPushed = false;
346 foldUp();
347 }
348 }
349
350 void DropDown::mouseReleased(MouseEvent& mouseEvent)
351 {
352 if (mIsDragged)
353 {
354 mPushed = false;
355 }
356
357 // Released outside of widget. Can happen when we have modal input focus.
358 if ((0 > mouseEvent.getY()
359 || mouseEvent.getY() >= getHeight()
360 || mouseEvent.getX() < 0
361 || mouseEvent.getX() >= getWidth())
362 && mouseEvent.getButton() == MouseEvent::LEFT
363 && isModalMouseInputFocused())
364 {
365 releaseModalMouseInputFocus();
366
367 if (mIsDragged)
368 {
369 foldUp();
370 }
371 }
372 else if (mouseEvent.getButton() == MouseEvent::LEFT)
373 {
374 mPushed = false;
375 }
376
377 mIsDragged = false;
378 }
379
380 void DropDown::mouseDragged(MouseEvent& mouseEvent)
381 {
382 mIsDragged = true;
383
384 mouseEvent.consume();
385 }
386
387 void DropDown::setListModel(ListModel *listModel)
388 {
389 mListBox->setListModel(listModel);
390
391 if (mListBox->getSelected() < 0)
392 {
393 mListBox->setSelected(0);
394 }
395
396 adjustHeight();
397 }
398
399 ListModel *DropDown::getListModel()
400 {
401 return mListBox->getListModel();
402 }
403
404 void DropDown::adjustHeight()
405 {
406 if (mScrollArea == NULL)
407 {
408 throw GCN_EXCEPTION("Scroll area has been deleted.");
409 }
410
411 if (mListBox == NULL)
412 {
413 throw GCN_EXCEPTION("List box has been deleted.");
414 }
415
416 int listBoxHeight = mListBox->getHeight();
417
418 // We add 2 for the border
419 int h2 = getFont()->getHeight() + 2;
420
421 setHeight(h2);
422
423 // The addition/subtraction of 2 compensates for the seperation lines
424 // seperating the selected element view and the scroll area.
425
426 if (mDroppedDown && getParent())
427 {
428 int h = getParent()->getChildrenArea().height - getY();
429
430 if (listBoxHeight > h - h2 - 2)
431 {
432 mScrollArea->setHeight(h - h2 - 2);
433 setHeight(h);
434 }
435 else
436 {
437 setHeight(listBoxHeight + h2 + 2);
438 mScrollArea->setHeight(listBoxHeight);
439 }
440 }
441
442 mScrollArea->setWidth(getWidth());
443 // Resize the ListBox to exactly fit the ScrollArea.
444 mListBox->setWidth(mScrollArea->getChildrenArea().width);
445 mScrollArea->setPosition(0, 0);
446 }
447
448 void DropDown::dropDown()
449 {
450 if (!mDroppedDown)
451 {
452 mDroppedDown = true;
453 mFoldedUpHeight = getHeight();
454 adjustHeight();
455
456 if (getParent())
457 {
458 getParent()->moveToTop(this);
459 }
460 }
461
462 mListBox->requestFocus();
463 }
464
465 void DropDown::foldUp()
466 {
467 if (mDroppedDown)
468 {
469 mDroppedDown = false;
470 adjustHeight();
471 mInternalFocusHandler.focusNone();
472 }
473 }
474
475 void DropDown::focusLost(const Event& event)
476 {
477 foldUp();
478 mInternalFocusHandler.focusNone();
479 }
480
481
482 void DropDown::death(const Event& event)
483 {
484 if (event.getSource() == mScrollArea)
485 {
486 mScrollArea = NULL;
487 }
488
489 BasicContainer::death(event);
490 }
491
492 void DropDown::action(const ActionEvent& actionEvent)
493 {
494 foldUp();
495 releaseModalMouseInputFocus();
496 distributeActionEvent();
497 }
498
499 Rectangle DropDown::getChildrenArea()
500 {
501 if (mDroppedDown)
502 {
503 // Calculate the children area (with the one pixel border in mind)
504 return Rectangle(1,
505 mFoldedUpHeight + 1,
506 getWidth() - 2,
507 getHeight() - mFoldedUpHeight - 2);
508 }
509
510 return Rectangle();
511 }
512
513 void DropDown::setBaseColor(const Color& color)
514 {
515 if (mInternalScrollArea)
516 {
517 mScrollArea->setBaseColor(color);
518 }
519
520 if (mInternalListBox)
521 {
522 mListBox->setBaseColor(color);
523 }
524
525 Widget::setBaseColor(color);
526 }
527
528 void DropDown::setBackgroundColor(const Color& color)
529 {
530 if (mInternalScrollArea)
531 {
532 mScrollArea->setBackgroundColor(color);
533 }
534
535 if (mInternalListBox)
536 {
537 mListBox->setBackgroundColor(color);
538 }
539
540 Widget::setBackgroundColor(color);
541 }
542
543 void DropDown::setForegroundColor(const Color& color)
544 {
545 if (mInternalScrollArea)
546 {
547 mScrollArea->setForegroundColor(color);
548 }
549
550 if (mInternalListBox)
551 {
552 mListBox->setForegroundColor(color);
553 }
554
555 Widget::setForegroundColor(color);
556 }
557
558 void DropDown::setFont(Font *font)
559 {
560 if (mInternalScrollArea)
561 {
562 mScrollArea->setFont(font);
563 }
564
565 if (mInternalListBox)
566 {
567 mListBox->setFont(font);
568 }
569
570 Widget::setFont(font);
571 }
572
573 void DropDown::mouseWheelMovedUp(MouseEvent& mouseEvent)
574 {
575 if (isFocused() && mouseEvent.getSource() == this)
576 {
577 mouseEvent.consume();
578
579 if (mListBox->getSelected() > 0)
580 {
581 mListBox->setSelected(mListBox->getSelected() - 1);
582 }
583 }
584 }
585
586 void DropDown::mouseWheelMovedDown(MouseEvent& mouseEvent)
587 {
588 if (isFocused() && mouseEvent.getSource() == this)
589 {
590 mouseEvent.consume();
591
592 mListBox->setSelected(mListBox->getSelected() + 1);
593 }
594 }
595
596 void DropDown::setSelectionColor(const Color& color)
597 {
598 Widget::setSelectionColor(color);
599
600 if (mInternalListBox)
601 {
602 mListBox->setSelectionColor(color);
603 }
604 }
605
606 void DropDown::valueChanged(const SelectionEvent& event)
607 {
608 distributeValueChangedEvent();
609 }
610
611 void DropDown::addSelectionListener(SelectionListener* selectionListener)
612 {
613 mSelectionListeners.push_back(selectionListener);
614 }
615
616 void DropDown::removeSelectionListener(SelectionListener* selectionListener)
617 {
618 mSelectionListeners.remove(selectionListener);
619 }
620
621 void DropDown::distributeValueChangedEvent()
622 {
623 SelectionListenerIterator iter;
624
625 for (iter = mSelectionListeners.begin(); iter != mSelectionListeners.end(); ++iter)
626 {
627 SelectionEvent event(this);
628 (*iter)->valueChanged(event);
629 }
630 }
631 }
632