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