2 * Copyright (C) 2002,2003,2004 Daniel Heck
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "gui/widgets.hh"
22 #include "SoundEffectManager.hh"
27 #include "ecl_font.hh"
33 using namespace enigma::gui
;
37 #define SCREEN ecl::Screen::get_instance()
39 /* -------------------- Widget -------------------- */
41 Widget::Widget(Container
*parent
)
42 : area(), m_parent(parent
), m_listener(0)
45 void Widget::invalidate() {
47 m_parent
->invalidate_area(get_area());
49 void Widget::invalidate_area(const ecl::Rect
& r
) {
51 m_parent
->invalidate_area(r
);
54 void Widget::reconfigure()
57 m_parent
->reconfigure_child(this);
60 void Widget::invoke_listener() {
62 m_listener
->on_action(this);
65 void Widget::move(int x
, int y
) {
66 area
.x
= x
; area
.y
= y
;
69 void Widget::resize (int w
, int h
) {
70 area
.w
= w
; area
.h
= h
;
73 bool Widget::on_event(const SDL_Event
&e
) {
76 modifierKeys
= e
.key
.keysym
.mod
;
77 mouseButton
= SDL_BUTTON_LEFT
;
79 case SDL_MOUSEBUTTONDOWN
:
80 modifierKeys
= SDL_GetModState();
81 mouseButton
= e
.button
.button
;
87 /* -------------------- Image -------------------- */
89 void Image::draw (ecl::GC
&gc
, const ecl::Rect
&/*r*/) {
90 // if (ecl::Surface *s = enigma::GetImage(imgname.c_str()))
91 // blit(gc, get_x(), get_y(), s);
92 if (ecl::Surface
*s
= enigma::GetImage(imgname
.c_str())) {
95 int x
= get_x() + (get_w()-w
)/2;
96 int y
= get_y() + (get_h()-h
)/2;
102 /* -------------------- AreaManager -------------------- */
104 AreaManager::AreaManager(Container
*c
)
107 assert(top_container
->get_parent() == 0); // otherwise it's not the top_container
110 void AreaManager::invalidate_area(const ecl::Rect
&r
) {
114 void AreaManager::invalidate_all() {
116 dirtyrects
.push_back(SCREEN
->size());
119 void AreaManager::refresh() {
120 if (!dirtyrects
.empty()) {
122 GC
gc(SCREEN
->get_surface());
123 for (RectList::iterator i
= dirtyrects
.begin(); i
!=dirtyrects
.end(); ++i
) {
124 top_container
->draw(gc
, *i
);
125 SCREEN
->update_rect(*i
);
132 /* -------------------- Container -------------------- */
134 Container::Container()
138 Container::~Container() {
143 AreaManager
*Container::getAreaManager() {
144 if (Container
*p
= get_parent()) {
146 return p
->getAreaManager();
150 managed_by
= new AreaManager(this);
155 void Container::invalidate_area(const ecl::Rect
&r
) { getAreaManager()->invalidate_area(r
); }
156 void Container::invalidate_all() { getAreaManager()->invalidate_all(); }
157 void Container::refresh() { getAreaManager()->refresh(); }
159 void Container::clear() {
160 delete_sequence(m_widgets
.begin(), m_widgets
.end());
164 void Container::add_child (Widget
*w
) {
165 if (w
&& w
!= this) {
166 m_widgets
.push_back(w
);
168 // w->move (get_x() + w->get_x(),
169 // get_y() + w->get_y());
173 void Container::remove_child (Widget
*w
) {
174 for (iterator it
= begin(); it
!= end(); it
++) {
182 void Container::exchange_child (Widget
*oldChild
, Widget
*newChild
) {
183 for (unsigned int i
= 0; i
< m_widgets
.size(); i
++) {
184 if (m_widgets
[i
] == oldChild
) {
185 m_widgets
[i
] = newChild
;
186 newChild
->set_parent(this);
192 void Container::draw (ecl::GC
& gc
, const ecl::Rect
&r
) {
193 for (iterator i
=begin(); i
!=end(); ++i
) {
195 Rect rr
= intersect(r
, w
->get_area());
201 void Container::draw_all() {
206 void Container::reconfigure_child (Widget
*)
211 Widget
* Container::find_widget(int x
, int y
) {
212 for (iterator i
=begin(); i
!=end(); ++i
) {
214 if (w
->get_area().contains(x
,y
)) {
215 Container
*c
= dynamic_cast<Container
*> (w
);
217 w
= c
->find_widget (x
, y
);
226 Widget
* Container::find_adjacent_widget(Widget
*from
, int x
, int y
) {
227 // valid values for x/y : 1/0, -1/0, 0/1, 0/-1
228 assert(from
&& x
>=-1 && x
<=1 && y
>=-1 && y
<=1 && abs(x
+y
) == 1);
232 int best_distance
= INT_MAX
;
233 Widget
*best_widget
= 0;
234 ecl::Rect farea
= from
->get_area();
236 for (iterator i
=begin(); i
!=end(); ++i
) {
238 ecl::Rect warea
= w
->get_area();
239 bool adjacent
= true;
242 if (x
) { // check for y-overlap
243 if (farea
.y
>(warea
.y
+warea
.h
-1) || warea
.y
>(farea
.y
+farea
.h
-1)) {
247 distance
= (warea
.x
-farea
.x
)*x
;
250 else { // check for x-overlap
251 if (farea
.x
>(warea
.x
+warea
.h
-1) || warea
.x
>(farea
.x
+farea
.h
-1)) {
255 distance
= (warea
.y
-farea
.y
)*y
;
259 if (adjacent
&& distance
>0 && distance
<best_distance
) {
260 best_distance
= distance
;
268 void Container::move (int x
, int y
) {
277 for (iterator i
=begin(); i
!=end(); ++i
) {
279 w
->move(dx
+ w
->get_x(), dy
+w
->get_y());
283 ecl::Rect
Container::boundingbox() {
284 if (!m_widgets
.empty()) {
286 Rect bbox
=(*i
)->get_area();
287 for (++i
; i
!=end(); ++i
)
288 bbox
= ecl::boundingbox(bbox
, (*i
)->get_area());
294 void Container::set_key_focus(Widget
*newfocus
) {
295 if (get_parent() != NULL
) {
296 get_parent()->set_key_focus(newfocus
);
300 bool Container::is_key_focus(Widget
*focus
) {
301 if (get_parent() != NULL
) {
302 return get_parent()->is_key_focus(focus
);
307 /* -------------------- List -------------------- */
309 List::List (int spacing
)
310 : m_spacing(spacing
),
311 has_default_size (false),
314 m_halign (HALIGN_LEFT
),
315 m_valign (VALIGN_TOP
)
318 void List::remove_child (Widget
*w
) {
319 Container::remove_child(w
);
323 void List::exchange_child(Widget
*oldChild
, Widget
*newChild
) {
324 Container::exchange_child(oldChild
, newChild
);
328 void List::set_spacing (int pixels
)
333 int List::get_spacing () const
338 int List::calc_minimum_height() const
341 const WidgetList
&wl
= m_widgets
;
343 sum
= (wl
.size() - 1) * m_spacing
;
344 for (WidgetList::const_iterator i
=wl
.begin(); i
!=wl
.end(); ++i
) {
346 get_size (*i
, nw
, nh
);
353 int List::calc_minimum_width () const
356 const WidgetList
&wl
= m_widgets
;
358 sum
= (wl
.size() - 1) * m_spacing
;
359 for (WidgetList::const_iterator i
=wl
.begin(); i
!=wl
.end(); ++i
) {
361 get_size (*i
, nw
, nh
);
368 void List::set_default_size (int w
, int h
)
370 has_default_size
= true;
376 void List::get_size (const Widget
*widget
, int &w
, int &h
) const
378 if (has_default_size
)
379 w
= defaultw
, h
= defaulth
;
381 widget
->naturalsize (w
, h
);
384 void List::resize (int w
, int h
)
386 Container::resize (w
, h
);
390 void List::move (int x
, int y
)
392 Container::move (x
, y
);
396 void List::reconfigure_child (Widget
*w
)
398 Container::reconfigure_child (w
);
403 void List::add_back (Widget
*w
, ExpansionMode m
)
406 m_expansionmodes
.push_back (m
);
410 void List::set_alignment (HAlignment halign
, VAlignment valign
)
412 if (halign
!= m_halign
|| valign
!= m_valign
) {
421 /* -------------------- HList -------------------- */
425 int targetw
= this->get_w(); // The available space
426 int naturalw
= calc_minimum_width();
427 int excessw
= targetw
- naturalw
;
429 int num_expand
= std::count (m_expansionmodes
.begin(),
430 m_expansionmodes
.end(),
433 WidgetList::iterator i
= m_widgets
.begin(),
434 end
= m_widgets
.end();
435 int x
= get_x(), y
= get_y();
438 if (num_expand
== 0 && excessw
> 0) {
449 for (; i
!= end
; ++i
, ++j
) {
451 List::get_size (*i
, w
, h
);
453 if (excessw
> 0 && m_expansionmodes
[j
] == List::EXPAND
) {
454 w
+= excessw
/ num_expand
;
455 excessw
-= excessw
/ num_expand
;
459 (*i
)->resize (w
, get_h());
460 x
+= w
+ get_spacing();
465 int targetw
= this->get_w(); // The available space
466 int naturalw
= calc_minimum_width();
467 return targetw
>= naturalw
;
470 /* -------------------- VList -------------------- */
474 int targeth
= this->get_h(); // The available space
475 int naturalh
= calc_minimum_height();
476 int excessh
= targeth
- naturalh
;
478 int num_expand
= std::count (m_expansionmodes
.begin(),
479 m_expansionmodes
.end(),
482 WidgetList::iterator i
= m_widgets
.begin(),
483 end
= m_widgets
.end();
484 int x
= get_x(), y
= get_y();
487 if (num_expand
== 0 && excessh
> 0) {
498 for (; i
!= end
; ++i
, ++j
) {
500 List::get_size (*i
, w
, h
);
502 if (excessh
> 0 && m_expansionmodes
[j
] == List::EXPAND
) {
503 h
+= excessh
/ num_expand
;
504 excessh
-= excessh
/ num_expand
;
508 (*i
)->resize (get_w(), h
);
509 y
+= h
+ get_spacing();
514 int targeth
= this->get_h(); // The available space
515 int naturalh
= calc_minimum_height();
516 return targeth
>= naturalh
;
519 /* -------------------- Label -------------------- */
521 Label::Label (const std::string
&text
,
525 m_font(enigma::GetFont("menufont")),
531 void Label::set_text (const std::string
&text
) {
532 if (text
!= m_text
) {
539 string
Label::get_text() const {
540 return _(m_text
.c_str());
543 string
Label::getText() const {
547 void Label::set_font (ecl::Font
*font
) {
548 if (m_font
!= font
) {
555 bool Label::text_fits(double area_fraction
) {
558 return w
<= get_w()*area_fraction
;
561 void Label::draw (ecl::GC
&gc
, const ecl::Rect
&)
567 int x
= get_x(), y
=get_y();
569 case HALIGN_LEFT
: break;
570 case HALIGN_RIGHT
: x
+= get_w() - w
; break;
571 case HALIGN_CENTER
: x
+= (get_w()-w
)/2; break;
574 case VALIGN_TOP
: break;
575 case VALIGN_BOTTOM
: y
+= get_h() - h
; break;
576 case VALIGN_CENTER
: y
+= (get_h()-h
)/2; break;
578 // translate if not an empty string
579 f
->render (gc
, x
, y
, m_text
== "" ? "" : get_text().c_str());
582 void Label::set_alignment (HAlignment halign
, VAlignment valign
) {
583 if (halign
!= m_halign
|| valign
!= m_valign
) {
590 void Label::naturalsize (int &w
, int &h
) const
592 h
= m_font
->get_height();
593 // width of translation if not an empty string
594 w
= m_font
->get_width (m_text
== "" ? "" : get_text().c_str());
597 /* -------------------- UntranslatedLabel -------------------- */
599 UntranslatedLabel::UntranslatedLabel (const std::string
&text
,
600 HAlignment halign
, VAlignment valign
) : Label(text
, halign
, valign
) {
603 string
UntranslatedLabel::get_text() const {
604 return Label::m_text
;
608 /* -------------------- Button -------------------- */
611 : m_activep (false), highlight (false)
615 void Button::activate()
617 sound::EmitSoundEvent ("menuswitch");
622 void Button::deactivate() {
627 void Button::setHighlight(bool shouldHighlight
) {
628 highlight
= shouldHighlight
;
631 bool Button::isHighlight() {
635 void Button::draw(ecl::GC
&gc
, const ecl::Rect
&r
) {
636 const int borderw
= 4;
638 ecl::Surface
*s
= enigma::GetImage(m_activep
? "buttonhl" : "button");
640 if (s
) { // Ugly, but hey, it works
641 Rect
srcrect (0,0,borderw
, borderw
);
642 Rect area
= get_area();
646 set_color (gc
, 70, 70, 70);
648 set_color (gc
, 0,0,0);
649 box (gc
, smaller(area
, borderw
));
651 set_color (gc
, 0,0,0);
653 blit (gc
, area
.x
, area
.y
, s
, srcrect
);
654 srcrect
.x
+= s
->width()-borderw
;
655 blit (gc
, area
.x
+area
.w
-borderw
, area
.y
, s
, srcrect
);
657 srcrect
.y
+= s
->height()-borderw
;
658 blit (gc
, area
.x
, area
.y
+area
.h
-borderw
, s
, srcrect
);
659 srcrect
.x
+= s
->width()-borderw
;
660 blit (gc
, area
.x
+area
.w
-borderw
, area
.y
+area
.h
-borderw
, s
, srcrect
);
662 // horizontal borders
664 int tilew
= s
->width() - 2*borderw
;
665 int ntiles
= (area
.w
- 2*borderw
) / tilew
;
666 int x
= area
.x
+ borderw
;
667 for (int i
=0; i
<ntiles
; ++i
) {
668 blit (gc
, x
, area
.y
, s
, Rect (borderw
, 0, tilew
, borderw
));
669 blit (gc
, x
, area
.y
+area
.h
-borderw
, s
,
670 Rect (borderw
, s
->height()-borderw
, tilew
, borderw
));
673 int restw
= (area
.w
- 2*borderw
) - tilew
*ntiles
;
674 blit (gc
, x
, area
.y
, s
, Rect (borderw
, 0, restw
, borderw
));
675 blit (gc
, x
, area
.y
+area
.h
-borderw
, s
,
676 Rect (borderw
, s
->height()-borderw
, restw
, borderw
));
680 int tileh
= s
->height() - 2*borderw
;
681 int ntiles
= (area
.h
- 2*borderw
) / tileh
;
682 int y
= area
.y
+ borderw
;
683 for (int i
=0; i
<ntiles
; ++i
) {
684 blit (gc
, area
.x
, y
, s
, Rect (0, borderw
, borderw
, tileh
));
685 blit (gc
, area
.x
+area
.w
-borderw
, y
, s
,
686 Rect (s
->width()-borderw
, borderw
, borderw
, tileh
));
689 int resth
= (area
.h
- 2*borderw
) - tileh
*ntiles
;
690 blit (gc
, area
.x
, y
, s
, Rect (0, borderw
, borderw
, resth
));
691 blit (gc
, area
.x
+area
.w
-borderw
, y
, s
,
692 Rect (s
->width()-borderw
, borderw
, borderw
, resth
));
696 set_color (gc
, 0,0,0);
698 set_color (gc
, 160,160,160);
700 frame (gc
, smaller(r
, 1));
704 /* -------------------- PushButton -------------------- */
706 PushButton::PushButton() : m_pressedp (false) {
709 bool PushButton::on_event(const SDL_Event
&e
) {
711 bool was_pressed
= m_pressedp
;
712 bool handeled
= false;
716 if (e
.key
.keysym
.sym
!= SDLK_RETURN
) break;
718 case SDL_MOUSEBUTTONDOWN
:
724 if (e
.key
.keysym
.sym
!= SDLK_RETURN
) break;
725 lastUpSym
= e
.key
.keysym
.sym
;
730 case SDL_MOUSEBUTTONUP
:
731 lastUpSym
= SDLK_UNKNOWN
;
732 lastUpBotton
= e
.button
.button
;
738 bool changed
= (was_pressed
!= m_pressedp
);
743 sound::EmitSoundEvent("menuok");
751 void PushButton::deactivate() {
753 lastUpSym
= SDLK_UNKNOWN
;
756 Button::deactivate();
759 SDLKey
PushButton::getLastUpSym() {
763 Uint8
PushButton::getLastUpButton() {
767 bool PushButton::soundOk() {
771 /* -------------------- TextButton -------------------- */
773 TextButton::TextButton(ActionListener
*al
) {
774 menufont
= enigma::GetFont("menufont");
775 menufont_pressed
= enigma::GetFont("menufontsel");
779 void TextButton::draw(ecl::GC
&gc
, const ecl::Rect
&r
) {
781 Font
*f
= is_pressed() ? menufont_pressed
: menufont
;
782 string text
= get_text();
783 int h
= f
->get_height();
784 int w
= f
->get_width(text
.c_str());
785 int x
= get_x() + (get_w()-w
)/2;
786 int y
= get_y() + (get_h()-h
)/2;
788 f
->render (gc
, x
, y
, text
.c_str());
792 /* -------------------- StaticTextButton -------------------- */
794 StaticTextButton::StaticTextButton(const string
&t
, ActionListener
*al
)
800 void StaticTextButton::set_text(const std::string
&t
) {
807 string
StaticTextButton::get_text() const {
808 return _(text
.c_str()); // translate
811 /* -------------------- UntranslatedStaticTextButton -------------------- */
813 UntranslatedStaticTextButton::UntranslatedStaticTextButton(const string
&t
,
815 : StaticTextButton(t
, al
)
820 string
UntranslatedStaticTextButton::get_text() const {
821 return StaticTextButton::text
;
825 /* -------------------- Buttons for Options -------------------- */
827 BoolOptionButton::BoolOptionButton(const char *option_name
,
828 const string
& true_text
, const string
& false_text
,
831 optionName(option_name
),
833 falseText(false_text
)
837 bool BoolOptionButton::toggle() {
838 bool newval
= !enigma_options::GetBool(optionName
);
839 enigma_options::SetOption(optionName
, newval
);
844 void BoolOptionButton::on_action(Widget
*) {
848 string
BoolOptionButton::get_text() const {
849 return enigma_options::GetBool(optionName
) ? _(trueText
.c_str()) : _(falseText
.c_str());
852 /* -------------------- ValueButton -------------------- */
854 ValueButton::ValueButton(int min_value_
, int max_value_
)
856 min_value(min_value_
),
857 max_value(max_value_
)
861 void ValueButton::setMaxValue(int max
) {
865 void ValueButton::init() {
866 update_value(-1, get_value()); // fixes wrong values (e.g. from .enimarc)
869 bool ValueButton::inc_value(int offset
) {
870 int old_value
= get_value();
871 return update_value(old_value
, old_value
+offset
);
874 string
ValueButton::get_text() const {
875 return get_text(get_value());
878 bool ValueButton::update_value(int old_value
, int new_value
) {
879 new_value
= Clamp(new_value
, min_value
, max_value
);
880 if (new_value
!= old_value
) {
881 set_value(new_value
);
889 void ValueButton::on_action(Widget
*) {
892 if (getLastUpSym() == SDLK_PAGEDOWN
||
893 getLastUpButton() == SDL_BUTTON_RIGHT
||
894 getLastUpButton() == 5) { // wheel down
897 if (getLastUpSym() == SDLK_PAGEDOWN
||
898 getLastUpSym() == SDLK_PAGEUP
||
899 getLastUpButton() == SDL_BUTTON_RIGHT
||
900 getLastUpButton() == SDL_BUTTON_LEFT
||
901 getLastUpButton() == 4 || getLastUpButton() == 5) {
904 if (inc_value(incr
)) {
905 sound::EmitSoundEvent("menuswitch");
908 sound::EmitSoundEvent("menustop");
910 sound::EmitSoundEvent("menuswitch");
912 update_value(get_value(), min_value
);
914 update_value(get_value(), max_value
);
919 bool ValueButton::soundOk() {
923 /* -------------------- ImageButton -------------------- */
925 ImageButton::ImageButton(const string
&unselected
,
926 const string
&selected
,
928 : fname_sel(selected
), fname_unsel(unselected
)
933 void ImageButton::set_images(const string
&unselected
, const string
&selected
) {
934 fname_sel
= selected
;
935 fname_unsel
= unselected
;
938 void ImageButton::draw(ecl::GC
&gc
, const ecl::Rect
&r
) {
940 string
&fname
= is_pressed() ? fname_sel
: fname_unsel
;
942 if (Surface
*s
= enigma::GetImage(fname
.c_str())) {
945 int x
= get_x() + (get_w()-w
)/2;
946 int y
= get_y() + (get_h()-h
)/2;
951 /* -------------------- BorderlessImageButton -------------------- */
953 BorderlessImageButton::BorderlessImageButton(const string
&unselected
,
954 const string
&selected
, const string
&mouseover
, bool isSelected
, ActionListener
*al
)
955 : fname_sel(selected
), fname_unsel(unselected
), fname_mouse(mouseover
), state (isSelected
)
960 void BorderlessImageButton::set_images(const string
&unselected
,
961 const string
&selected
, const string
&mouseover
) {
962 fname_sel
= selected
;
963 fname_unsel
= unselected
;
964 fname_mouse
= mouseover
;
967 void BorderlessImageButton::draw(ecl::GC
&gc
, const ecl::Rect
&r
) {
968 string
&fname
= m_activep
? fname_mouse
: (state
? fname_sel
: fname_unsel
);
970 if (Surface
*s
= enigma::GetImage(fname
.c_str())) {
973 int x
= get_x() + (get_w()-w
)/2;
974 int y
= get_y() + (get_h()-h
)/2;
979 void BorderlessImageButton::setState(bool isSelected
) {
984 bool BorderlessImageButton::getState() const {