comparison ext/guichan-0.8.2/src/widgets/dropdown.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 #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 if (keyEvent.isConsumed())
292 return;
293
294 Key key = keyEvent.getKey();
295
296 if ((key.getValue() == Key::ENTER || key.getValue() == Key::SPACE)
297 && !mDroppedDown)
298 {
299 dropDown();
300 keyEvent.consume();
301 }
302 else if (key.getValue() == Key::UP)
303 {
304 setSelected(getSelected() - 1);
305 keyEvent.consume();
306 }
307 else if (key.getValue() == Key::DOWN)
308 {
309 setSelected(getSelected() + 1);
310 keyEvent.consume();
311 }
312 }
313
314 void DropDown::mousePressed(MouseEvent& mouseEvent)
315 {
316 // If we have a mouse press on the widget.
317 if (0 <= mouseEvent.getY()
318 && mouseEvent.getY() < getHeight()
319 && mouseEvent.getX() >= 0
320 && mouseEvent.getX() < getWidth()
321 && mouseEvent.getButton() == MouseEvent::LEFT
322 && !mDroppedDown
323 && mouseEvent.getSource() == this)
324 {
325 mPushed = true;
326 dropDown();
327 requestModalMouseInputFocus();
328 }
329 // Fold up the listbox if the upper part is clicked after fold down
330 else if (0 <= mouseEvent.getY()
331 && mouseEvent.getY() < mFoldedUpHeight
332 && mouseEvent.getX() >= 0
333 && mouseEvent.getX() < getWidth()
334 && mouseEvent.getButton() == MouseEvent::LEFT
335 && mDroppedDown
336 && mouseEvent.getSource() == this)
337 {
338 mPushed = false;
339 foldUp();
340 releaseModalMouseInputFocus();
341 }
342 // If we have a mouse press outside the widget
343 else if (0 > mouseEvent.getY()
344 || mouseEvent.getY() >= getHeight()
345 || mouseEvent.getX() < 0
346 || mouseEvent.getX() >= getWidth())
347 {
348 mPushed = false;
349 foldUp();
350 }
351 }
352
353 void DropDown::mouseReleased(MouseEvent& mouseEvent)
354 {
355 if (mIsDragged)
356 {
357 mPushed = false;
358 }
359
360 // Released outside of widget. Can happen when we have modal input focus.
361 if ((0 > mouseEvent.getY()
362 || mouseEvent.getY() >= getHeight()
363 || mouseEvent.getX() < 0
364 || mouseEvent.getX() >= getWidth())
365 && mouseEvent.getButton() == MouseEvent::LEFT
366 && isModalMouseInputFocused())
367 {
368 releaseModalMouseInputFocus();
369
370 if (mIsDragged)
371 {
372 foldUp();
373 }
374 }
375 else if (mouseEvent.getButton() == MouseEvent::LEFT)
376 {
377 mPushed = false;
378 }
379
380 mIsDragged = false;
381 }
382
383 void DropDown::mouseDragged(MouseEvent& mouseEvent)
384 {
385 mIsDragged = true;
386
387 mouseEvent.consume();
388 }
389
390 void DropDown::setListModel(ListModel *listModel)
391 {
392 mListBox->setListModel(listModel);
393
394 if (mListBox->getSelected() < 0)
395 {
396 mListBox->setSelected(0);
397 }
398
399 adjustHeight();
400 }
401
402 ListModel *DropDown::getListModel()
403 {
404 return mListBox->getListModel();
405 }
406
407 void DropDown::adjustHeight()
408 {
409 if (mScrollArea == NULL)
410 {
411 throw GCN_EXCEPTION("Scroll area has been deleted.");
412 }
413
414 if (mListBox == NULL)
415 {
416 throw GCN_EXCEPTION("List box has been deleted.");
417 }
418
419 int listBoxHeight = mListBox->getHeight();
420
421 // We add 2 for the border
422 int h2 = getFont()->getHeight() + 2;
423
424 setHeight(h2);
425
426 // The addition/subtraction of 2 compensates for the seperation lines
427 // seperating the selected element view and the scroll area.
428
429 if (mDroppedDown && getParent())
430 {
431 int h = getParent()->getChildrenArea().height - getY();
432
433 if (listBoxHeight > h - h2 - 2)
434 {
435 mScrollArea->setHeight(h - h2 - 2);
436 setHeight(h);
437 }
438 else
439 {
440 setHeight(listBoxHeight + h2 + 2);
441 mScrollArea->setHeight(listBoxHeight);
442 }
443 }
444
445 mScrollArea->setWidth(getWidth());
446 // Resize the ListBox to exactly fit the ScrollArea.
447 mListBox->setWidth(mScrollArea->getChildrenArea().width);
448 mScrollArea->setPosition(0, 0);
449 }
450
451 void DropDown::dropDown()
452 {
453 if (!mDroppedDown)
454 {
455 mDroppedDown = true;
456 mFoldedUpHeight = getHeight();
457 adjustHeight();
458
459 if (getParent())
460 {
461 getParent()->moveToTop(this);
462 }
463 }
464
465 mListBox->requestFocus();
466 }
467
468 void DropDown::foldUp()
469 {
470 if (mDroppedDown)
471 {
472 mDroppedDown = false;
473 adjustHeight();
474 mInternalFocusHandler.focusNone();
475 }
476 }
477
478 void DropDown::focusLost(const Event& event)
479 {
480 foldUp();
481 mInternalFocusHandler.focusNone();
482 }
483
484
485 void DropDown::death(const Event& event)
486 {
487 if (event.getSource() == mScrollArea)
488 {
489 mScrollArea = NULL;
490 }
491
492 BasicContainer::death(event);
493 }
494
495 void DropDown::action(const ActionEvent& actionEvent)
496 {
497 foldUp();
498 releaseModalMouseInputFocus();
499 distributeActionEvent();
500 }
501
502 Rectangle DropDown::getChildrenArea()
503 {
504 if (mDroppedDown)
505 {
506 // Calculate the children area (with the one pixel border in mind)
507 return Rectangle(1,
508 mFoldedUpHeight + 1,
509 getWidth() - 2,
510 getHeight() - mFoldedUpHeight - 2);
511 }
512
513 return Rectangle();
514 }
515
516 void DropDown::setBaseColor(const Color& color)
517 {
518 if (mInternalScrollArea)
519 {
520 mScrollArea->setBaseColor(color);
521 }
522
523 if (mInternalListBox)
524 {
525 mListBox->setBaseColor(color);
526 }
527
528 Widget::setBaseColor(color);
529 }
530
531 void DropDown::setBackgroundColor(const Color& color)
532 {
533 if (mInternalScrollArea)
534 {
535 mScrollArea->setBackgroundColor(color);
536 }
537
538 if (mInternalListBox)
539 {
540 mListBox->setBackgroundColor(color);
541 }
542
543 Widget::setBackgroundColor(color);
544 }
545
546 void DropDown::setForegroundColor(const Color& color)
547 {
548 if (mInternalScrollArea)
549 {
550 mScrollArea->setForegroundColor(color);
551 }
552
553 if (mInternalListBox)
554 {
555 mListBox->setForegroundColor(color);
556 }
557
558 Widget::setForegroundColor(color);
559 }
560
561 void DropDown::setFont(Font *font)
562 {
563 if (mInternalScrollArea)
564 {
565 mScrollArea->setFont(font);
566 }
567
568 if (mInternalListBox)
569 {
570 mListBox->setFont(font);
571 }
572
573 Widget::setFont(font);
574 }
575
576 void DropDown::mouseWheelMovedUp(MouseEvent& mouseEvent)
577 {
578 if (isFocused() && mouseEvent.getSource() == this)
579 {
580 mouseEvent.consume();
581
582 if (mListBox->getSelected() > 0)
583 {
584 mListBox->setSelected(mListBox->getSelected() - 1);
585 }
586 }
587 }
588
589 void DropDown::mouseWheelMovedDown(MouseEvent& mouseEvent)
590 {
591 if (isFocused() && mouseEvent.getSource() == this)
592 {
593 mouseEvent.consume();
594
595 mListBox->setSelected(mListBox->getSelected() + 1);
596 }
597 }
598
599 void DropDown::setSelectionColor(const Color& color)
600 {
601 Widget::setSelectionColor(color);
602
603 if (mInternalListBox)
604 {
605 mListBox->setSelectionColor(color);
606 }
607 }
608
609 void DropDown::valueChanged(const SelectionEvent& event)
610 {
611 distributeValueChangedEvent();
612 }
613
614 void DropDown::addSelectionListener(SelectionListener* selectionListener)
615 {
616 mSelectionListeners.push_back(selectionListener);
617 }
618
619 void DropDown::removeSelectionListener(SelectionListener* selectionListener)
620 {
621 mSelectionListeners.remove(selectionListener);
622 }
623
624 void DropDown::distributeValueChangedEvent()
625 {
626 SelectionListenerIterator iter;
627
628 for (iter = mSelectionListeners.begin(); iter != mSelectionListeners.end(); ++iter)
629 {
630 SelectionEvent event(this);
631 (*iter)->valueChanged(event);
632 }
633 }
634 }
635