1 /*      _______   __   __   __   ______   __   __   _______   __   __
2  *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
3  *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
4  *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
5  *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
6  * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
7  * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
8  *
9  * Copyright (c) 2004, 2005 darkbits                        Js_./
10  * Per Larsson a.k.a finalman                          _RqZ{a<^_aa
11  * Olof Naess�n a.k.a jansem/yakslem                _asww7!uY`>  )\a//
12  *                                                 _Qhm`] _f "'c  1!5m
13  * Visit: http://guichan.darkbits.org             )Qk<P ` _: :+' .'  "{[
14  *                                               .)j(] .d_/ '-(  P .   S
15  * License: (BSD)                                <Td/Z <fP"5(\"??"\a.  .L
16  * Redistribution and use in source and          _dV>ws?a-?'      ._/L  #'
17  * binary forms, with or without                 )4d[#7r, .   '     )d`)[
18  * modification, are permitted provided         _Q-5'5W..j/?'   -?!\)cam'
19  * that the following conditions are met:       j<<WP+k/);.        _W=j f
20  * 1. Redistributions of source code must       .$%w\/]Q  . ."'  .  mj$
21  *    retain the above copyright notice,        ]E.pYY(Q]>.   a     J@\
22  *    this list of conditions and the           j(]1u<sE"L,. .   ./^ ]{a
23  *    following disclaimer.                     4'_uomm\.  )L);-4     (3=
24  * 2. Redistributions in binary form must        )_]X{Z('a_"a7'<a"a,  ]"[
25  *    reproduce the above copyright notice,       #}<]m7`Za??4,P-"'7. ).m
26  *    this list of conditions and the            ]d2e)Q(<Q(  ?94   b-  LQ/
27  *    following disclaimer in the                <B!</]C)d_, '(<' .f. =C+m
28  *    documentation and/or other materials      .Z!=J ]e []('-4f _ ) -.)m]'
29  *    provided with the distribution.          .w[5]' _[ /.)_-"+?   _/ <W"
30  * 3. Neither the name of Guichan nor the      :$we` _! + _/ .        j?
31  *    names of its contributors may be used     =3)= _f  (_yQmWW$#(    "
32  *    to endorse or promote products derived     -   W,  sQQQQmZQ#Wwa]..
33  *    from this software without specific        (js, \[QQW$QWW#?!V"".
34  *    prior written permission.                    ]y:.<\..          .
35  *                                                 -]n w/ '         [.
36  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT       )/ )/           !
37  * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY         <  (; sac    ,    '
38  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,               ]^ .-  %
39  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF            c <   r
40  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR            aga<  <La
41  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE          5%  )P'-3L
42  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR        _bQf` y`..)a
43  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,          ,J?4P'.P"_(\?d'.,
44  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES               _Pa,)!f/<[]/  ?"
45  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT      _2-..:. .r+_,.. .
46  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     ?a.<%"'  " -'.a_ _,
47  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION)                     ^
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
49  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
50  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
51  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
52  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53  */
54 
55 #include <assert.h>
56 #include "guichan/exception.h"
57 #include "guichan/widgets/dropdown.h"
58 
59 namespace gcn
60 {
DropDown()61     DropDown::DropDown()
62     {
63         mDroppedDown = false;
64         mPushed = false;
65         mOldH = 0;
66 
67         setWidth(100);
68         setFocusable(true);
69 
70         mDefaultScrollArea = new ScrollArea();
71         mDefaultScrollArea->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
72         mDefaultListBox = new ListBox();
73 
74         mScrollArea = mDefaultScrollArea;
75         mScrollArea->_setFocusHandler(&mFocusHandler);
76         mScrollArea->_setParent(this);
77 
78         mListBox = mDefaultListBox;
79         mListBox->addActionListener(this);
80         mScrollArea->setContent(mListBox);
81 
82         addMouseListener(this);
83         addKeyListener(this);
84         adjustHeight();
85         setBorderSize(1);
86     }
87 
DropDown(ListModel * listModel)88     DropDown::DropDown(ListModel *listModel)
89     {
90         setWidth(100);
91         setFocusable(true);
92         mDroppedDown = false;
93         mPushed = false;
94         mOldH = 0;
95 
96         mDefaultScrollArea = new ScrollArea();
97         mDefaultScrollArea->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
98         mDefaultListBox = new ListBox();
99 
100         mScrollArea = mDefaultScrollArea;
101         mScrollArea->_setParent(this);
102         mListBox = mDefaultListBox;
103         mListBox->addActionListener(this);
104 
105         mScrollArea->setContent(mListBox);
106         mScrollArea->_setFocusHandler(&mFocusHandler);
107         mScrollArea->_setParent(this);
108 
109         setListModel(listModel);
110 
111         if (mListBox->getSelected() < 0)
112         {
113             mListBox->setSelected(0);
114         }
115 
116         addMouseListener(this);
117         addKeyListener(this);
118         adjustHeight();
119         setBorderSize(1);
120     }
121 
DropDown(ListModel * listModel,ScrollArea * scrollArea,ListBox * listBox)122     DropDown::DropDown(ListModel *listModel,
123                        ScrollArea *scrollArea,
124                        ListBox *listBox)
125     {
126         setWidth(100);
127         setFocusable(true);
128         mDroppedDown = false;
129         mPushed = false;
130         mOldH = 0;
131 
132         mDefaultScrollArea = NULL;
133         mDefaultListBox = NULL;
134 
135         mScrollArea = scrollArea;
136         mScrollArea->_setFocusHandler(&mFocusHandler);
137 
138         mListBox = listBox;
139         mListBox->addActionListener(this);
140         mScrollArea->setContent(mListBox);
141         mScrollArea->_setParent(this);
142 
143         setListModel(listModel);
144 
145         if (mListBox->getSelected() < 0)
146         {
147             mListBox->setSelected(0);
148         }
149 
150         addMouseListener(this);
151         addKeyListener(this);
152         adjustHeight();
153         setBorderSize(1);
154     }
155 
~DropDown()156     DropDown::~DropDown()
157     {
158         if (mScrollArea != NULL)
159         {
160             mScrollArea->_setFocusHandler(NULL);
161         }
162 
163         if (mDefaultScrollArea != NULL)
164         {
165             delete mDefaultScrollArea;
166         }
167 
168         if (mDefaultListBox != NULL)
169         {
170             delete mDefaultListBox;
171         }
172 
173         if (widgetExists(mListBox))
174         {
175             mListBox->removeActionListener(this);
176         }
177     }
178 
logic()179     void DropDown::logic()
180     {
181         if (mScrollArea == NULL || mScrollArea->getContent() == NULL)
182         {
183             //throw GCN_EXCEPTION("ScrollArea or ListBox is NULL.");
184             assert(!"ScrollArea or ListBox is NULL.");
185         }
186 
187         mScrollArea->logic();
188         mFocusHandler.applyChanges();
189     }
190 
draw(Graphics * graphics)191     void DropDown::draw(Graphics* graphics)
192     {
193         if (mScrollArea == NULL || mScrollArea->getContent() == NULL)
194         {
195             //throw GCN_EXCEPTION("ScrollArea or ListBox is NULL.");
196            assert(!"ScrollArea or ListBox is NULL.");
197         }
198 
199         int h;
200 
201         if (mDroppedDown)
202         {
203             h = mOldH;
204         }
205         else
206         {
207             h = getHeight();
208         }
209 
210         int alpha = getBaseColor().a;
211         Color faceColor = getBaseColor();
212         faceColor.a = alpha;
213         Color highlightColor = faceColor + 0x303030;
214         highlightColor.a = alpha;
215         Color shadowColor = faceColor - 0x303030;
216         shadowColor.a = alpha;
217 
218 
219         graphics->setColor(getBackgroundColor());
220         graphics->fillRectangle(Rectangle(0, 0, getWidth(), h));
221 
222         graphics->setColor(getForegroundColor());
223         graphics->setFont(getFont());
224 
225         if (mListBox->getListModel() && mListBox->getSelected() >= 0)
226         {
227             graphics->drawText(mListBox->getListModel()->getElementAt(mListBox->getSelected()),
228                 1, (h - getFont()->getHeight()) / 2);
229         }
230 
231         if (hasFocus())
232         {
233             graphics->drawRectangle(Rectangle(0, 0, getWidth() - h, h));
234         }
235 
236         drawButton(graphics);
237 
238          if (mDroppedDown)
239          {
240              graphics->pushClipArea(mScrollArea->getDimension());
241              mScrollArea->draw(graphics);
242              graphics->popClipArea();
243 
244             // Draw two lines separating the ListBox with se selected
245             // element view.
246             graphics->setColor(highlightColor);
247             graphics->drawLine(0, h, getWidth(), h);
248             graphics->setColor(shadowColor);
249             graphics->drawLine(0, h + 1,getWidth(),h + 1);
250          }
251     }
252 
drawBorder(Graphics * graphics)253     void DropDown::drawBorder(Graphics* graphics)
254     {
255         Color faceColor = getBaseColor();
256         Color highlightColor, shadowColor;
257         int alpha = getBaseColor().a;
258         int width = getWidth() + getBorderSize() * 2 - 1;
259         int height = getHeight() + getBorderSize() * 2 - 1;
260         highlightColor = faceColor + 0x303030;
261         highlightColor.a = alpha;
262         shadowColor = faceColor - 0x303030;
263         shadowColor.a = alpha;
264 
265         unsigned int i;
266         for (i = 0; i < getBorderSize(); ++i)
267         {
268             graphics->setColor(shadowColor);
269             graphics->drawLine(i,i, width - i, i);
270             graphics->drawLine(i,i + 1, i, height - i - 1);
271             graphics->setColor(highlightColor);
272             graphics->drawLine(width - i,i + 1, width - i, height - i);
273             graphics->drawLine(i,height - i, width - i - 1, height - i);
274         }
275     }
276 
drawButton(Graphics * graphics)277     void DropDown::drawButton(Graphics *graphics)
278     {
279         Color faceColor, highlightColor, shadowColor;
280         int offset;
281         int alpha = getBaseColor().a;
282 
283         if (mPushed)
284         {
285             faceColor = getBaseColor() - 0x303030;
286             faceColor.a = alpha;
287             highlightColor = faceColor - 0x303030;
288             highlightColor.a = alpha;
289             shadowColor = faceColor + 0x303030;
290             shadowColor.a = alpha;
291             offset = 1;
292         }
293         else
294         {
295             faceColor = getBaseColor();
296             faceColor.a = alpha;
297             highlightColor = faceColor + 0x303030;
298             highlightColor.a = alpha;
299             shadowColor = faceColor - 0x303030;
300             shadowColor.a = alpha;
301             offset = 0;
302         }
303 
304         int h;
305         if (mDroppedDown)
306         {
307             h = mOldH;
308         }
309         else
310         {
311             h = getHeight();
312         }
313         int x = getWidth() - h;
314         int y = 0;
315 
316         graphics->setColor(faceColor);
317         graphics->fillRectangle(Rectangle(x+1, y+1, h-2, h-2));
318 
319         graphics->setColor(highlightColor);
320         graphics->drawLine(x, y, x+h-1, y);
321         graphics->drawLine(x, y+1, x, y+h-1);
322 
323         graphics->setColor(shadowColor);
324         graphics->drawLine(x+h-1, y+1, x+h-1, y+h-1);
325         graphics->drawLine(x+1, y+h-1, x+h-2, y+h-1);
326 
327         graphics->setColor(getForegroundColor());
328 
329         int i;
330         int hh = h / 3;
331         int hx = x + h / 2;
332         int hy = y + (h * 2) / 3;
333         for (i=0; i<hh; i++)
334         {
335             graphics->drawLine(hx - i + offset,
336                                hy - i + offset,
337                                hx + i + offset,
338                                hy - i + offset);
339         }
340     }
341 
getSelected()342     int DropDown::getSelected()
343     {
344         if (mScrollArea == NULL || mScrollArea->getContent() == NULL)
345         {
346             assert(!"ScrollArea or ListBox is NULL.");
347             //throw GCN_EXCEPTION("ScrollArea or ListBox is NULL.");
348         }
349 
350         return mListBox->getSelected();
351     }
352 
setSelected(int selected)353     void DropDown::setSelected(int selected)
354     {
355         if (mScrollArea == NULL || mScrollArea->getContent() == NULL)
356         {
357             assert(!"ScrollArea or ListBox is NULL.");
358             //throw GCN_EXCEPTION("ScrollArea or ListBox is NULL.");
359         }
360 
361         if (selected >= 0)
362         {
363             mListBox->setSelected(selected);
364         }
365     }
366 
keyPress(const Key & key)367     bool DropDown::keyPress(const Key& key)
368     {
369         if (mScrollArea == NULL || mScrollArea->getContent() == NULL)
370         {
371             //throw GCN_EXCEPTION("ScrollArea or ListBox is NULL.");
372             assert(!"ScrollArea or ListBox is NULL.");
373         }
374 
375         if ((key.getValue() == Key::K_ENTER || key.getValue() == Key::K_SPACE)
376             && !mDroppedDown)
377         {
378             dropDown();
379             return true;
380         }
381 		return false;
382     }
383 
mousePress(int,int y,int button)384     void DropDown::mousePress(int, int y, int button)
385     {
386         if (button == MouseInput::LEFT && hasMouse() && !mDroppedDown)
387         {
388             mPushed = true;
389             dropDown();
390         }
391         // Fold up the listbox if the upper part is clicked after fold down
392         else if (button == MouseInput::LEFT && hasMouse() && mDroppedDown
393                  && y < mOldH)
394         {
395             foldUp();
396         }
397         else if (!hasMouse())
398         {
399             foldUp();
400         }
401     }
402 
mouseRelease(int,int,int button)403     void DropDown::mouseRelease(int, int, int button)
404     {
405         if (button == MouseInput::LEFT)
406         {
407             mPushed = false;
408         }
409     }
410 
setListModel(ListModel * listModel)411     void DropDown::setListModel(ListModel *listModel)
412     {
413         if (mScrollArea == NULL || mScrollArea->getContent() == NULL)
414         {
415             //throw GCN_EXCEPTION("ScrollArea or ListBox is NULL.");
416             assert(!"ScrollArea or ListBox is NULL.");
417         }
418 
419         mListBox->setListModel(listModel);
420 
421         if (mListBox->getSelected() < 0)
422         {
423             mListBox->setSelected(0);
424         }
425 
426         adjustHeight();
427     }
428 
getListModel()429     ListModel *DropDown::getListModel()
430     {
431         if (mScrollArea == NULL || mScrollArea->getContent() == NULL)
432         {
433             //throw GCN_EXCEPTION("ScrollArea or ListBox is NULL.");
434             assert(!"ScrollArea or ListBox is NULL.");
435         }
436 
437         return mListBox->getListModel();
438     }
439 
setScrollArea(ScrollArea * scrollArea)440     void DropDown::setScrollArea(ScrollArea *scrollArea)
441     {
442         mScrollArea->_setFocusHandler(NULL);
443         mScrollArea->_setParent(NULL);
444         mScrollArea = scrollArea;
445         mScrollArea->_setFocusHandler(&mFocusHandler);
446         mScrollArea->setContent(mListBox);
447         mScrollArea->_setParent(this);
448         adjustHeight();
449     }
450 
getScrollArea()451     ScrollArea *DropDown::getScrollArea()
452     {
453         return mScrollArea;
454     }
455 
setListBox(ListBox * listBox)456     void DropDown::setListBox(ListBox *listBox)
457     {
458         listBox->setSelected(mListBox->getSelected());
459         listBox->setListModel(mListBox->getListModel());
460         listBox->addActionListener(this);
461 
462         if (mScrollArea->getContent() != NULL)
463         {
464             mListBox->removeActionListener(this);
465         }
466 
467         mListBox = listBox;
468 
469         mScrollArea->setContent(mListBox);
470 
471         if (mListBox->getSelected() < 0)
472         {
473             mListBox->setSelected(0);
474         }
475     }
476 
getListBox()477     ListBox *DropDown::getListBox()
478     {
479         return mListBox;
480     }
481 
adjustHeight()482     void DropDown::adjustHeight()
483     {
484         if (mScrollArea == NULL || mScrollArea->getContent() == NULL)
485         {
486             //throw GCN_EXCEPTION("ScrollArea or ListBox is NULL.");
487             assert(!"ScrollArea or ListBox is NULL.");
488         }
489 
490         int listBoxHeight = mListBox->getHeight();
491         int h2 = mOldH ? mOldH : getFont()->getHeight();
492 
493         setHeight(h2);
494 
495         // The addition/subtraction of 2 compensates for the seperation lines
496         // seperating the selected element view and the scroll area.
497 
498         if (mDroppedDown && getParent())
499         {
500             int h = getParent()->getHeight() - getY();
501 
502             if (listBoxHeight > h - h2 - 2)
503             {
504                 mScrollArea->setHeight(h - h2 - 2);
505                 setHeight(h);
506             }
507             else
508             {
509                 setHeight(listBoxHeight + h2 + 2);
510                 mScrollArea->setHeight(listBoxHeight);
511             }
512         }
513 
514         mScrollArea->setWidth(getWidth());
515         mScrollArea->setPosition(0, h2 + 2);
516     }
517 
dropDown()518     void DropDown::dropDown()
519     {
520         if (!mDroppedDown)
521         {
522             mDroppedDown = true;
523             mOldH = getHeight();
524             adjustHeight();
525 
526             if (getParent())
527             {
528                 getParent()->moveToTop(this);
529             }
530         }
531 
532         mFocusHandler.requestFocus(mScrollArea->getContent());
533     }
534 
foldUp()535     void DropDown::foldUp()
536     {
537         if (mDroppedDown)
538         {
539             mDroppedDown = false;
540             mFocusHandler.focusNone();
541             adjustHeight();
542         }
543     }
544 
_keyInputMessage(const KeyInput & keyInput)545     bool DropDown::_keyInputMessage(const KeyInput& keyInput)
546     {
547         if (mDroppedDown)
548         {
549             if (mScrollArea == NULL || mScrollArea->getContent() == NULL)
550             {
551                 //throw GCN_EXCEPTION("ScrollArea or ListBox is NULL.");
552                 assert(!"ScrollArea or ListBox is NULL.");
553             }
554 
555             if (mFocusHandler.getFocused() != NULL)
556             {
557                 return mFocusHandler.getFocused()->_keyInputMessage(keyInput);
558             }
559             else
560             {
561                 return false;
562             }
563         }
564         else
565         {
566             return BasicContainer::_keyInputMessage(keyInput);
567         }
568     }
569 
_mouseInputMessage(const MouseInput & mouseInput)570     void DropDown::_mouseInputMessage(const MouseInput &mouseInput)
571     {
572         BasicContainer::_mouseInputMessage(mouseInput);
573 
574         if (mDroppedDown)
575         {
576             if (mScrollArea == NULL || mScrollArea->getContent() == NULL)
577             {
578                 //throw GCN_EXCEPTION("ScrollArea or ListBox is NULL.");
579                 assert(!"ScrollArea or ListBox is NULL.");
580             }
581 
582             if (mouseInput.y >= mOldH)
583             {
584                 MouseInput mi = mouseInput;
585                 mi.y -= mScrollArea->getY();
586                 mScrollArea->_mouseInputMessage(mi);
587 
588                 if (mListBox->hasFocus())
589                 {
590                     mi.y -= mListBox->getY();
591                     mListBox->_mouseInputMessage(mi);
592                 }
593             }
594         }
595     }
596 
lostFocus()597     void DropDown::lostFocus()
598     {
599         foldUp();
600     }
601 
moveToTop(Widget *)602     void DropDown::moveToTop(Widget*)
603     {
604         if (getParent())
605         {
606             getParent()->moveToTop(this);
607         }
608     }
609 
moveToBottom(Widget *)610     void DropDown::moveToBottom(Widget*)
611     {
612         if (getParent())
613         {
614             getParent()->moveToBottom(this);
615         }
616     }
617 
_announceDeath(Widget * widget)618     void DropDown::_announceDeath(Widget* widget)
619     {
620         if (widget == mScrollArea)
621         {
622             mScrollArea = NULL;
623         }
624         else
625         {
626             assert(!"Death announced for unknown widget..");
627             //throw GCN_EXCEPTION("Death announced for unknown widget..");
628         }
629     }
630 
action(const std::string &)631     void DropDown::action(const std::string&)
632     {
633         foldUp();
634         generateAction();
635     }
636 
getDrawSize(int & width,int & height,Widget * widget)637     void DropDown::getDrawSize(int& width, int& height, Widget* widget)
638     {
639         if (widget == mScrollArea)
640         {
641             if (mDroppedDown)
642             {
643                 height = getHeight() - mOldH;
644                 width = getWidth();
645             }
646             else
647             {
648                 width = height = 0;
649             }
650         }
651         else
652         {
653 			assert(!"DropDown::getDrawSize. widget is not the ScrollArea (wieeerd...)");
654             //throw GCN_EXCEPTION("DropDown::getDrawSize. widget is not the ScrollArea (wieeerd...)");
655         }
656     }
657 
setBaseColor(const Color & color)658     void DropDown::setBaseColor(const Color& color)
659     {
660         if (mDefaultScrollArea == mScrollArea && mScrollArea != NULL)
661         {
662             mScrollArea->setBaseColor(color);
663         }
664 
665         if (mDefaultListBox == mListBox && mListBox != NULL)
666         {
667             mListBox->setBaseColor(color);
668         }
669 
670         Widget::setBaseColor(color);
671     }
672 
setBackgroundColor(const Color & color)673     void DropDown::setBackgroundColor(const Color& color)
674     {
675         if (mDefaultScrollArea == mScrollArea && mScrollArea != NULL)
676         {
677             mScrollArea->setBackgroundColor(color);
678         }
679 
680         if (mDefaultListBox == mListBox && mListBox != NULL)
681         {
682             mListBox->setBackgroundColor(color);
683         }
684 
685         Widget::setBackgroundColor(color);
686     }
687 
setForegroundColor(const Color & color)688     void DropDown::setForegroundColor(const Color& color)
689     {
690         if (mDefaultScrollArea == mScrollArea && mScrollArea != NULL)
691         {
692             mScrollArea->setForegroundColor(color);
693         }
694 
695         if (mDefaultListBox == mListBox && mListBox != NULL)
696         {
697             mListBox->setForegroundColor(color);
698         }
699 
700         Widget::setForegroundColor(color);
701     }
702 
setFont(Font * font)703     void DropDown::setFont(Font *font)
704     {
705         Widget::setFont(font);
706         mListBox->setFont(font);
707     }
708 
getDirty() const709 	bool DropDown::getDirty() const
710 	{
711 		if (mDroppedDown)
712 		{
713 			return mScrollArea->getDirty();
714 		}
715 		return mDirty;
716 	}
717 }
718 
719