Mercurial > fife-parpg
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 |