00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #define GETTEXT_DOMAIN "wesnoth-lib"
00022
00023 #include "gui/widgets/window_private.hpp"
00024
00025 #include "font.hpp"
00026 #include "foreach.hpp"
00027 #include "game_display.hpp"
00028 #include "gettext.hpp"
00029 #include "log.hpp"
00030 #include "gui/auxiliary/event/distributor.hpp"
00031 #include "gui/auxiliary/event/message.hpp"
00032 #include "gui/auxiliary/log.hpp"
00033 #include "gui/auxiliary/layout_exception.hpp"
00034 #include "gui/auxiliary/window_builder/control.hpp"
00035 #include "gui/dialogs/title_screen.hpp"
00036 #include "gui/dialogs/tip.hpp"
00037 #include "gui/widgets/button.hpp"
00038 #include "gui/widgets/settings.hpp"
00039 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
00040 #include "gui/widgets/debug.hpp"
00041 #endif
00042 #include "preferences.hpp"
00043 #include "preferences_display.hpp"
00044 #include "video.hpp"
00045
00046 #include <boost/bind.hpp>
00047
00048 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
00049 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
00050
00051 #define LOG_IMPL_SCOPE_HEADER window.get_control_type() \
00052 + " [" + window.id() + "] " + __func__
00053 #define LOG_IMPL_HEADER LOG_IMPL_SCOPE_HEADER + ':'
00054
00055 namespace gui2{
00056
00057 namespace implementation {
00058
00059
00060 class tbuilder_window
00061 : public tbuilder_control
00062 {
00063 public:
00064 tbuilder_window(const config& cfg)
00065 : tbuilder_control(cfg)
00066 {
00067 }
00068
00069 twidget* build() const { return NULL; }
00070 };
00071
00072 }
00073 REGISTER_WIDGET(window)
00074
00075 unsigned twindow::sunset_ = 0;
00076
00077 namespace {
00078 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
00079 const unsigned MANUAL = tdebug_layout_graph::MANUAL;
00080 const unsigned SHOW = tdebug_layout_graph::SHOW;
00081 const unsigned LAYOUT = tdebug_layout_graph::LAYOUT;
00082 #else
00083
00084 const unsigned MANUAL = 0;
00085 const unsigned SHOW = 0;
00086 const unsigned LAYOUT = 0;
00087 #endif
00088
00089
00090
00091
00092
00093
00094
00095
00096 static int draw_interval = 0;
00097
00098
00099
00100
00101
00102
00103
00104
00105 static Uint32 draw_timer(Uint32, void*)
00106 {
00107
00108
00109 SDL_Event event;
00110 SDL_UserEvent data;
00111
00112 data.type = DRAW_EVENT;
00113 data.code = 0;
00114 data.data1 = NULL;
00115 data.data2 = NULL;
00116
00117 event.type = DRAW_EVENT;
00118 event.user = data;
00119
00120 SDL_PushEvent(&event);
00121 return draw_interval;
00122 }
00123
00124
00125
00126
00127
00128
00129
00130
00131 static Uint32 delay_event_callback(const Uint32, void* event)
00132 {
00133 SDL_PushEvent(static_cast<SDL_Event*>(event));
00134 return 0;
00135 }
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146 static void delay_event(const SDL_Event& event, const Uint32 delay)
00147 {
00148 SDL_AddTimer(delay, delay_event_callback, new SDL_Event(event));
00149 }
00150
00151
00152
00153
00154
00155
00156 static bool helptip()
00157 {
00158 DBG_GUI_E << "Pushing SHOW_HELPTIP_EVENT event in queue.\n";
00159
00160 SDL_Event event;
00161 SDL_UserEvent data;
00162
00163 data.type = SHOW_HELPTIP_EVENT;
00164 data.code = 0;
00165 data.data1 = NULL;
00166 data.data2 = NULL;
00167
00168 event.type = SHOW_HELPTIP_EVENT;
00169 event.user = data;
00170
00171 SDL_PushEvent(&event);
00172
00173 return true;
00174 }
00175
00176
00177
00178
00179
00180
00181
00182 class tmanager
00183 {
00184 tmanager();
00185 public:
00186
00187 static tmanager& instance();
00188
00189 void add(twindow& window);
00190
00191 void remove(twindow& window);
00192
00193 unsigned get_id(twindow& window);
00194
00195 twindow* window(const unsigned id);
00196
00197 private:
00198
00199
00200
00201 std::map<unsigned, twindow*> windows_;
00202 };
00203
00204 tmanager::tmanager()
00205 : windows_()
00206 {
00207 }
00208
00209 tmanager& tmanager::instance()
00210 {
00211 static tmanager window_manager;
00212 return window_manager;
00213 }
00214
00215 void tmanager::add(twindow& window)
00216 {
00217 static unsigned id;
00218 ++id;
00219 windows_[id] = &window;
00220 }
00221
00222 void tmanager::remove(twindow& window)
00223 {
00224 for(std::map<unsigned, twindow*>::iterator itor = windows_.begin();
00225 itor != windows_.end(); ++itor) {
00226
00227 if(itor->second == &window) {
00228 windows_.erase(itor);
00229 return;
00230 }
00231 }
00232 assert(false);
00233 }
00234
00235 unsigned tmanager::get_id(twindow& window)
00236 {
00237 for(std::map<unsigned, twindow*>::iterator itor = windows_.begin();
00238 itor != windows_.end(); ++itor) {
00239
00240 if(itor->second == &window) {
00241 return itor->first;
00242 }
00243 }
00244 assert(false);
00245
00246 return 0;
00247 }
00248
00249 twindow* tmanager::window(const unsigned id)
00250 {
00251 std::map<unsigned, twindow*>::iterator itor = windows_.find(id);
00252
00253 if(itor == windows_.end()) {
00254 return NULL;
00255 } else {
00256 return itor->second;
00257 }
00258 }
00259
00260 }
00261
00262 twindow::twindow(CVideo& video,
00263 tformula<unsigned>x,
00264 tformula<unsigned>y,
00265 tformula<unsigned>w,
00266 tformula<unsigned>h,
00267 const bool automatic_placement,
00268 const unsigned horizontal_placement,
00269 const unsigned vertical_placement,
00270 const unsigned maximum_width,
00271 const unsigned maximum_height,
00272 const std::string& definition,
00273 const twindow_builder::tresolution::ttip& tooltip,
00274 const twindow_builder::tresolution::ttip& helptip)
00275 : tpanel()
00276 , cursor::setter(cursor::NORMAL)
00277 , video_(video)
00278 , status_(NEW)
00279 , show_mode_(none)
00280 , retval_(NONE)
00281 , owner_(0)
00282 , need_layout_(true)
00283 , variables_()
00284 , invalidate_layout_blocked_(false)
00285 , suspend_drawing_(true)
00286 , restorer_()
00287 , automatic_placement_(automatic_placement)
00288 , horizontal_placement_(horizontal_placement)
00289 , vertical_placement_(vertical_placement)
00290 , maximum_width_(maximum_width)
00291 , maximum_height_(maximum_height)
00292 , x_(x)
00293 , y_(y)
00294 , w_(w)
00295 , h_(h)
00296 , tooltip_(tooltip)
00297 , helptip_(helptip)
00298 , click_dismiss_(false)
00299 , enter_disabled_(false)
00300 , escape_disabled_(false)
00301 , linked_size_()
00302 , dirty_list_()
00303 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
00304 , debug_layout_(new tdebug_layout_graph(this))
00305 #endif
00306 , event_distributor_(new event::tdistributor(
00307 *this, event::tdispatcher::front_child))
00308 {
00309
00310
00311 set_definition(definition);
00312 load_config();
00313
00314 tmanager::instance().add(*this);
00315
00316 connect();
00317
00318 connect_signal<event::DRAW>(boost::bind(&twindow::draw, this));
00319
00320 connect_signal<event::SDL_VIDEO_RESIZE>(
00321 boost::bind(&twindow::signal_handler_sdl_video_resize
00322 , this, _2, _3, _5));
00323
00324 connect_signal<event::SDL_ACTIVATE>(
00325 boost::bind(&event::tdistributor::initialize_state
00326 , event_distributor_));
00327
00328 connect_signal<event::SDL_LEFT_BUTTON_DOWN>(
00329 boost::bind(
00330 &twindow::signal_handler_click_dismiss, this, _2, _3, _4)
00331 , event::tdispatcher::front_child);
00332 connect_signal<event::SDL_MIDDLE_BUTTON_DOWN>(
00333 boost::bind(
00334 &twindow::signal_handler_click_dismiss, this, _2, _3, _4)
00335 , event::tdispatcher::front_child);
00336 connect_signal<event::SDL_RIGHT_BUTTON_DOWN>(
00337 boost::bind(
00338 &twindow::signal_handler_click_dismiss, this, _2, _3, _4)
00339 , event::tdispatcher::front_child);
00340
00341 connect_signal<event::SDL_KEY_DOWN>(
00342 boost::bind(&twindow::signal_handler_sdl_key_down
00343 , this, _2, _3, _5)
00344 , event::tdispatcher::back_pre_child);
00345 connect_signal<event::SDL_KEY_DOWN>(
00346 boost::bind(&twindow::signal_handler_sdl_key_down
00347 , this, _2, _3, _5));
00348
00349 connect_signal<event::MESSAGE_SHOW_TOOLTIP>(
00350 boost::bind(
00351 &twindow::signal_handler_message_show_tooltip
00352 , this
00353 , _2
00354 , _3
00355 , _5)
00356 , event::tdispatcher::back_pre_child);
00357
00358 connect_signal<event::MESSAGE_SHOW_HELPTIP>(
00359 boost::bind(
00360 &twindow::signal_handler_message_show_helptip
00361 , this
00362 , _2
00363 , _3
00364 , _5)
00365 , event::tdispatcher::back_pre_child);
00366
00367 connect_signal<event::REQUEST_PLACEMENT>(
00368 boost::bind(
00369 &twindow::signal_handler_request_placement
00370 , this
00371 , _2
00372 , _3)
00373 , event::tdispatcher::back_pre_child);
00374
00375 register_hotkey(hotkey::GLOBAL__HELPTIP, boost::bind(gui2::helptip));
00376 }
00377
00378 twindow::~twindow()
00379 {
00380
00381
00382
00383
00384
00385
00386
00387 for(unsigned row = 0; row < grid().get_rows(); ++row) {
00388 for(unsigned col = 0; col < grid().get_cols(); ++col) {
00389 grid().remove_child(row, col);
00390 }
00391 }
00392
00393
00394
00395
00396
00397
00398
00399
00400 if(show_mode_ == modal) {
00401 tip::remove();
00402 }
00403
00404 tmanager::instance().remove(*this);
00405
00406 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
00407
00408 delete debug_layout_;
00409
00410 #endif
00411 delete event_distributor_;
00412 }
00413
00414 twindow* twindow::window_instance(const unsigned handle)
00415 {
00416 return tmanager::instance().window(handle);
00417 }
00418
00419 void twindow::update_screen_size()
00420 {
00421
00422
00423 if(draw_interval == 0) {
00424 const SDL_Rect rect = screen_area();
00425 settings::screen_width = rect.w;
00426 settings::screen_height = rect.h;
00427
00428 settings::gamemap_width = settings::screen_width;
00429 settings::gamemap_height = settings::screen_height;
00430
00431 display* display = display::get_singleton();
00432 if(display) {
00433 const unsigned w = display->map_outside_area().w;
00434 const unsigned h = display->map_outside_area().h;
00435 if(w && h) {
00436 settings::gamemap_width = w;
00437 settings::gamemap_height = h;
00438 }
00439 }
00440 }
00441 }
00442
00443 twindow::tretval twindow::get_retval_by_id(const std::string& id)
00444 {
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456 if(id == "ok") {
00457 return OK;
00458 } else if(id == "cancel") {
00459 return CANCEL;
00460
00461
00462
00463
00464
00465
00466
00467
00468 } else if(id == "tutorial") {
00469 return static_cast<tretval>(ttitle_screen::TUTORIAL);
00470 } else if(id == "editor") {
00471 return static_cast<tretval>(ttitle_screen::START_MAP_EDITOR);
00472 } else if(id == "credits") {
00473 return static_cast<tretval>(ttitle_screen::SHOW_ABOUT);
00474 } else if(id == "quit") {
00475 return static_cast<tretval>(ttitle_screen::QUIT_GAME);
00476
00477
00478
00479
00480
00481
00482 } else if(id == "help") {
00483 return static_cast<tretval>(ttitle_screen::SHOW_HELP);
00484 } else if(id == "campaign") {
00485 return static_cast<tretval>(ttitle_screen::NEW_CAMPAIGN);
00486 } else if(id == "multiplayer") {
00487 return static_cast<tretval>(ttitle_screen::MULTIPLAYER);
00488 } else if(id == "load") {
00489 return static_cast<tretval>(ttitle_screen::LOAD_GAME);
00490 } else if(id == "addons") {
00491 return static_cast<tretval>(ttitle_screen::GET_ADDONS);
00492 } else if(id == "language") {
00493 return static_cast<tretval>(ttitle_screen::CHANGE_LANGUAGE);
00494 } else if(id == "preferences") {
00495 return static_cast<tretval>(ttitle_screen::EDIT_PREFERENCES);
00496
00497
00498 } else {
00499 return NONE;
00500 }
00501 }
00502
00503 void twindow::show_tooltip()
00504 {
00505 log_scope2(log_gui_draw, "Window: show as tooltip.");
00506
00507 generate_dot_file("show", SHOW);
00508
00509 assert(status_ == NEW);
00510
00511 set_mouse_behaviour(event::tdispatcher::none);
00512 set_want_keyboard_input(false);
00513
00514 show_mode_ = tooltip;
00515
00516
00517
00518
00519
00520
00521 invalidate_layout();
00522 suspend_drawing_ = false;
00523 }
00524
00525 void twindow::show_non_modal()
00526 {
00527 log_scope2(log_gui_draw, "Window: show non modal.");
00528
00529 generate_dot_file("show", SHOW);
00530
00531 assert(status_ == NEW);
00532
00533 set_mouse_behaviour(event::tdispatcher::hit);
00534
00535 show_mode_ = modal;
00536
00537
00538
00539
00540
00541
00542 invalidate_layout();
00543 suspend_drawing_ = false;
00544 }
00545
00546 int twindow::show(const bool restore, const unsigned auto_close_timeout)
00547 {
00548
00549
00550
00551
00552 tip::remove();
00553
00554 show_mode_ = modal;
00555
00556
00557
00558
00559
00560
00561
00562 class tdraw_interval_setter
00563 {
00564 public:
00565 tdraw_interval_setter()
00566 : interval_(draw_interval)
00567 {
00568 if(interval_ == 0) {
00569 draw_interval = 30;
00570 SDL_AddTimer(draw_interval, draw_timer, NULL);
00571
00572
00573
00574 update_screen_size();
00575
00576 }
00577 }
00578
00579 ~tdraw_interval_setter()
00580 {
00581 draw_interval = interval_;
00582 }
00583 private:
00584
00585 int interval_;
00586 };
00587
00588 log_scope2(log_gui_draw, LOG_SCOPE_HEADER);
00589
00590 generate_dot_file("show", SHOW);
00591
00592 assert(status_ == NEW);
00593
00594 tdraw_interval_setter draw_interval_setter;
00595
00596
00597
00598
00599
00600
00601 invalidate_layout();
00602 suspend_drawing_ = false;
00603
00604 if(auto_close_timeout) {
00605
00606
00607 draw();
00608
00609 SDL_Event event;
00610 SDL_UserEvent data;
00611
00612 data.type = CLOSE_WINDOW_EVENT;
00613 data.code = tmanager::instance().get_id(*this);
00614 data.data1 = NULL;
00615 data.data2 = NULL;
00616
00617 event.type = CLOSE_WINDOW_EVENT;
00618 event.user = data;
00619
00620 delay_event(event, auto_close_timeout);
00621 }
00622
00623 try {
00624
00625 for(status_ = SHOWING; status_ != REQUEST_CLOSE; ) {
00626
00627 events::pump();
00628
00629 SDL_Delay(10);
00630 }
00631 } catch(...) {
00632
00633
00634
00635
00636
00637
00638 suspend_drawing_ = true;
00639
00640
00641 if(restore) {
00642 SDL_Rect rect = get_rect();
00643 sdl_blit(restorer_, 0, video_.getSurface(), &rect);
00644 update_rect(get_rect());
00645 font::undraw_floating_labels(video_.getSurface());
00646 }
00647 throw;
00648 }
00649
00650 suspend_drawing_ = true;
00651
00652
00653 if(restore) {
00654 SDL_Rect rect = get_rect();
00655 sdl_blit(restorer_, 0, video_.getSurface(), &rect);
00656 update_rect(get_rect());
00657 font::undraw_floating_labels(video_.getSurface());
00658 }
00659
00660 return retval_;
00661 }
00662
00663 void twindow::draw()
00664 {
00665
00666
00667 if(suspend_drawing_) {
00668 return;
00669 }
00670
00671 surface frame_buffer = video_.getSurface();
00672
00673
00674 if(need_layout_) {
00675
00676
00677
00678 if(restorer_) {
00679 SDL_Rect rect = get_rect();
00680 sdl_blit(restorer_, 0, frame_buffer, &rect);
00681
00682
00683 update_rect(rect);
00684 }
00685
00686 layout();
00687
00688
00689 SDL_Rect rect = get_rect();
00690
00691
00692 font::draw_floating_labels(frame_buffer);
00693 restorer_ = get_surface_portion(frame_buffer, rect);
00694
00695
00696 dirty_list_.push_back(std::vector<twidget*>(1, this));
00697 } else {
00698
00699
00700 layout_children();
00701
00702
00703 std::vector<twidget*> call_stack;
00704 if(!new_widgets) {
00705 populate_dirty_list(*this, call_stack);
00706 } else {
00707
00708 dirty_list_.clear();
00709 dirty_list_.push_back(std::vector<twidget*>(1, this));
00710 update_rect(screen_area());
00711 }
00712 }
00713
00714 if(dirty_list_.empty()) {
00715 if(preferences::use_color_cursors() || sunset_) {
00716 surface frame_buffer = get_video_surface();
00717
00718 if(sunset_) {
00719
00720 static unsigned i = 0;
00721 if(++i % sunset_ == 0) {
00722 SDL_Rect r = ::create_rect(0, 0, frame_buffer->w, frame_buffer->h);
00723 const Uint32 color =
00724 SDL_MapRGBA(frame_buffer->format,0,0,0,255);
00725
00726 fill_rect_alpha(r, color, 1, frame_buffer);
00727 update_rect(r);
00728 }
00729 }
00730 }
00731 return;
00732 }
00733
00734 foreach(std::vector<twidget*>& item, dirty_list_) {
00735
00736 assert(!item.empty());
00737
00738 const SDL_Rect dirty_rect = new_widgets
00739 ? screen_area()
00740 : item.back()->get_dirty_rect();
00741
00742
00743
00744 #if 0
00745 dirty_list_.clear();
00746 dirty_list_.push_back(std::vector<twidget*>(1, this));
00747 update_rect(screen_area());
00748 #else
00749 clip_rect_setter clip(frame_buffer, &dirty_rect);
00750 #endif
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773 for(std::vector<twidget*>::iterator itor = item.begin();
00774 itor != item.end(); ++itor) {
00775
00776 if((**itor).get_visible() != twidget::VISIBLE
00777 || (**itor).get_drawing_action() == twidget::NOT_DRAWN) {
00778
00779 for(std::vector<twidget*>::iterator citor = itor;
00780 citor != item.end(); ++citor) {
00781
00782 (**citor).set_dirty(false);
00783 }
00784
00785 item.erase(itor, item.end());
00786 break;
00787 }
00788 }
00789
00790
00791 SDL_Rect rect = get_rect();
00792 sdl_blit(restorer_, 0, frame_buffer, &rect);
00793
00794 if(new_widgets) {
00795
00796 for(std::vector<twidget*>::iterator itor = item.begin();
00797 itor != item.end(); ++itor) {
00798
00799 (**itor).draw_background(frame_buffer, 0, 0);
00800 }
00801
00802
00803 if(!item.empty()) {
00804 item.back()->draw_children(frame_buffer, 0, 0);
00805 }
00806
00807
00808 for(std::vector<twidget*>::reverse_iterator ritor = item.rbegin();
00809 ritor != item.rend(); ++ritor) {
00810
00811 (**ritor).draw_foreground(frame_buffer, 0, 0);
00812 (**ritor).set_dirty(false);
00813 }
00814 } else {
00815
00816 for(std::vector<twidget*>::iterator itor = item.begin();
00817 itor != item.end(); ++itor) {
00818
00819 (**itor).draw_background(frame_buffer);
00820 }
00821
00822
00823 if(!item.empty()) {
00824 item.back()->draw_children(frame_buffer);
00825 }
00826
00827
00828 for(std::vector<twidget*>::reverse_iterator ritor = item.rbegin();
00829 ritor != item.rend(); ++ritor) {
00830
00831 (**ritor).draw_foreground(frame_buffer);
00832 (**ritor).set_dirty(false);
00833 }
00834 }
00835
00836 update_rect(dirty_rect);
00837 }
00838
00839 dirty_list_.clear();
00840
00841 std::vector<twidget*> call_stack;
00842 populate_dirty_list(*this, call_stack);
00843 assert(dirty_list_.empty());
00844
00845 SDL_Rect rect = get_rect();
00846 update_rect(rect);
00847 }
00848
00849 void twindow::undraw()
00850 {
00851 if(restorer_) {
00852 SDL_Rect rect = get_rect();
00853 sdl_blit(restorer_, 0, video_.getSurface(), &rect);
00854
00855
00856 update_rect(rect);
00857 }
00858 }
00859
00860 twindow::tinvalidate_layout_blocker::tinvalidate_layout_blocker(twindow& window)
00861 : window_(window)
00862 {
00863 assert(!window_.invalidate_layout_blocked_);
00864 window_.invalidate_layout_blocked_ = true;
00865 }
00866
00867 twindow::tinvalidate_layout_blocker::~tinvalidate_layout_blocker()
00868 {
00869 assert(window_.invalidate_layout_blocked_);
00870 window_.invalidate_layout_blocked_ = false;
00871 }
00872
00873 void twindow::invalidate_layout()
00874 {
00875 if(!invalidate_layout_blocked_) {
00876 need_layout_ = true;
00877 }
00878 }
00879
00880 void twindow::init_linked_size_group(const std::string& id,
00881 const bool fixed_width, const bool fixed_height)
00882 {
00883 assert(fixed_width || fixed_height);
00884 assert(!has_linked_size_group(id));
00885
00886 linked_size_[id] = tlinked_size(fixed_width, fixed_height);
00887 }
00888
00889 bool twindow::has_linked_size_group(const std::string& id)
00890 {
00891 return linked_size_.find(id) != linked_size_.end();
00892 }
00893
00894 void twindow::add_linked_widget(const std::string& id, twidget* widget)
00895 {
00896 assert(widget);
00897 assert(has_linked_size_group(id));
00898
00899 std::vector<twidget*>& widgets = linked_size_[id].widgets;
00900 if(std::find(widgets.begin(), widgets.end(), widget) == widgets.end()) {
00901 widgets.push_back(widget);
00902 }
00903 }
00904
00905 void twindow::remove_linked_widget(const std::string& id
00906 , const twidget* widget)
00907 {
00908 assert(widget);
00909 assert(has_linked_size_group(id));
00910
00911 std::vector<twidget*>& widgets = linked_size_[id].widgets;
00912
00913 std::vector<twidget*>::iterator itor =
00914 std::find(widgets.begin(), widgets.end(), widget);
00915
00916 if(itor != widgets.end()) {
00917 widgets.erase(itor);
00918
00919 assert(std::find(widgets.begin(), widgets.end(), widget)
00920 == widgets.end());
00921 }
00922 }
00923
00924 void twindow::layout()
00925 {
00926
00927
00928 boost::intrusive_ptr<const twindow_definition::tresolution> conf =
00929 boost::dynamic_pointer_cast<const twindow_definition::tresolution>
00930 (config());
00931 assert(conf);
00932
00933 log_scope2(log_gui_layout, LOG_SCOPE_HEADER);
00934
00935 get_screen_size_variables(variables_);
00936
00937 const int maximum_width = automatic_placement_
00938 ? maximum_width_
00939 ? std::min(maximum_width_, settings::screen_width)
00940 : settings::screen_width
00941 : w_(variables_);
00942
00943 const int maximum_height = automatic_placement_
00944 ? maximum_height_
00945 ? std::min(maximum_height_, settings::screen_height)
00946 : settings::screen_height
00947 : h_(variables_);
00948
00949
00950 tbutton* click_dismiss_button = NULL;
00951 if((click_dismiss_button
00952 = find_widget<tbutton>(this, "click_dismiss", false, false))) {
00953
00954 click_dismiss_button->set_visible(twidget::INVISIBLE);
00955 }
00956 if(click_dismiss_) {
00957 tbutton* button = find_widget<tbutton>(this, "ok", false, false);
00958 if(button) {
00959 button->set_visible(twidget::INVISIBLE);
00960 click_dismiss_button = button;
00961 }
00962 VALIDATE(click_dismiss_button
00963 , _("Click dismiss needs a 'click_dismiss' or 'ok' button."));
00964 }
00965
00966
00967 layout_init(true);
00968 generate_dot_file("layout_init", LAYOUT);
00969
00970 layout_linked_widgets();
00971
00972 try {
00973 twindow_implementation::layout(*this, maximum_width, maximum_height);
00974 } catch(tlayout_exception_resize_failed&) {
00975
00976
00977
00978 std::stringstream sstr;
00979 sstr << __FILE__ << ":" << __LINE__ << " in function '" << __func__
00980 << "' found the following problem: Failed to size window;"
00981 << " wanted size " << get_best_size()
00982 << " available size "
00983 << maximum_width << ',' << maximum_height
00984 << " screen size "
00985 << settings::screen_width << ',' << settings::screen_height
00986 << '.';
00987
00988 throw twml_exception(_("Failed to show a dialog, "
00989 "which doesn't fit on the screen."), sstr.str());
00990 }
00991
00992
00993 if(click_dismiss_ && disable_click_dismiss()) {
00994 assert(click_dismiss_button);
00995 click_dismiss_button->set_visible(twidget::VISIBLE);
00996
00997 connect_signal_mouse_left_click(
00998 *click_dismiss_button
00999 , boost::bind(
01000 &twindow::set_retval
01001 , this
01002 , OK
01003 , true));
01004
01005 layout_init(true);
01006 generate_dot_file("layout_init", LAYOUT);
01007
01008 layout_linked_widgets();
01009
01010 try {
01011 twindow_implementation::layout(
01012 *this, maximum_width, maximum_height);
01013
01014 } catch(tlayout_exception_resize_failed&) {
01015
01016
01017
01018 std::stringstream sstr;
01019 sstr << __FILE__ << ":" << __LINE__ << " in function '" << __func__
01020 << "' found the following problem: Failed to size window;"
01021 << " wanted size " << get_best_size()
01022 << " available size "
01023 << maximum_width << ',' << maximum_height
01024 << " screen size "
01025 << settings::screen_width << ',' << settings::screen_height
01026 << '.';
01027
01028 throw twml_exception(_("Failed to show a dialog, "
01029 "which doesn't fit on the screen."), sstr.str());
01030 }
01031 }
01032
01033
01034 tpoint size = get_best_size();
01035
01036 assert(size.x <= maximum_width && size.y <= maximum_height);
01037
01038 tpoint origin(0, 0);
01039
01040 if(automatic_placement_) {
01041
01042 switch(horizontal_placement_) {
01043 case tgrid::HORIZONTAL_ALIGN_LEFT :
01044
01045 break;
01046 case tgrid::HORIZONTAL_ALIGN_CENTER :
01047 origin.x = (settings::screen_width - size.x) / 2;
01048 break;
01049 case tgrid::HORIZONTAL_ALIGN_RIGHT :
01050 origin.x = settings::screen_width - size.x;
01051 break;
01052 default :
01053 assert(false);
01054 }
01055 switch(vertical_placement_) {
01056 case tgrid::VERTICAL_ALIGN_TOP :
01057
01058 break;
01059 case tgrid::VERTICAL_ALIGN_CENTER :
01060 origin.y = (settings::screen_height - size.y) / 2;
01061 break;
01062 case tgrid::VERTICAL_ALIGN_BOTTOM :
01063 origin.y = settings::screen_height - size.y;
01064 break;
01065 default :
01066 assert(false);
01067 }
01068 } else {
01069 origin.x = x_(variables_);
01070 origin.y = y_(variables_);
01071
01072 size.x = w_(variables_);
01073 size.y = h_(variables_);
01074 }
01075
01076
01077 place(origin, size);
01078
01079 generate_dot_file("layout_finished", LAYOUT);
01080 need_layout_ = false;
01081
01082 event::init_mouse_location();
01083 }
01084
01085 void twindow::layout_linked_widgets()
01086 {
01087
01088 typedef std::pair<const std::string, tlinked_size> hack;
01089 foreach(hack& linked_size, linked_size_) {
01090
01091 tpoint max_size(0, 0);
01092
01093
01094 foreach(twidget* widget, linked_size.second.widgets) {
01095
01096 const tpoint size = widget->get_best_size();
01097
01098 if(size.x > max_size.x) {
01099 max_size.x = size.x;
01100 }
01101 if(size.y > max_size.y) {
01102 max_size.y = size.y;
01103 }
01104 }
01105
01106
01107 foreach(twidget* widget, linked_size.second.widgets) {
01108
01109 tpoint size = widget->get_best_size();
01110
01111 if(linked_size.second.width) {
01112 size.x = max_size.x;
01113 }
01114 if(linked_size.second.height) {
01115 size.y = max_size.y;
01116 }
01117
01118 widget->set_layout_size(size);
01119 }
01120 }
01121 }
01122
01123 bool twindow::click_dismiss()
01124 {
01125 if(does_click_dismiss()) {
01126 set_retval(OK);
01127 return true;
01128 }
01129 return false;
01130 }
01131
01132 const std::string& twindow::get_control_type() const
01133 {
01134 static const std::string type = "window";
01135 return type;
01136 }
01137
01138 void twindow::draw(surface& , const bool ,
01139 const bool )
01140 {
01141 assert(false);
01142 }
01143
01144 namespace {
01145
01146
01147
01148 void swap_grid(tgrid* grid,
01149 tgrid* content_grid, twidget* widget, const std::string& id)
01150 {
01151 assert(content_grid);
01152 assert(widget);
01153
01154
01155 widget->set_id(id);
01156
01157
01158 tgrid* parent_grid = NULL;
01159 if(grid) {
01160 parent_grid = find_widget<tgrid>(grid, id, false, false);
01161 }
01162 if(!parent_grid) {
01163 parent_grid = find_widget<tgrid>(content_grid, id, true, false);
01164 assert(parent_grid);
01165 }
01166 if(tgrid* g = dynamic_cast<tgrid*>(parent_grid->parent())) {
01167 widget = g->swap_child(id, widget, false);
01168 } else if(tcontainer_* c
01169 = dynamic_cast<tcontainer_*>(parent_grid->parent())) {
01170
01171 widget = c->grid().swap_child(id, widget, true);
01172 } else {
01173 assert(false);
01174 }
01175
01176 assert(widget);
01177
01178 delete widget;
01179 }
01180
01181 }
01182
01183 void twindow::finalize(const boost::intrusive_ptr<tbuilder_grid>& content_grid)
01184 {
01185 swap_grid(NULL, &grid(), content_grid->build(), "_window_content_grid");
01186 }
01187
01188 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
01189
01190 void twindow::generate_dot_file(const std::string& generator,
01191 const unsigned domain)
01192 {
01193 debug_layout_->generate_dot_file(generator, domain);
01194 }
01195 #endif
01196
01197 void twindow_implementation::layout(twindow& window,
01198 const unsigned maximum_width, const unsigned maximum_height)
01199 {
01200 log_scope2(log_gui_layout, LOG_IMPL_SCOPE_HEADER);
01201
01202
01203
01204
01205
01206
01207
01208 try {
01209 tpoint size = window.get_best_size();
01210
01211 DBG_GUI_L << LOG_IMPL_HEADER
01212 << " best size : " << size
01213 << " maximum size : " << maximum_width << ',' << maximum_height
01214 << ".\n";
01215 if(size.x <= static_cast<int>(maximum_width)
01216 && size.y <= static_cast<int>(maximum_height)) {
01217
01218 DBG_GUI_L << LOG_IMPL_HEADER << " Result: Fits, nothing to do.\n";
01219 return;
01220 }
01221
01222 if(size.x > static_cast<int>(maximum_width)) {
01223 window.reduce_width(maximum_width);
01224
01225 size = window.get_best_size();
01226 if(size.x > static_cast<int>(maximum_width)) {
01227 DBG_GUI_L << LOG_IMPL_HEADER
01228 << " Result: Resize width failed."
01229 << " Wanted width " << maximum_width
01230 << " resulting width " << size.x
01231 << ".\n";
01232 throw tlayout_exception_width_resize_failed();
01233 }
01234 DBG_GUI_L << LOG_IMPL_HEADER
01235 << " Status: Resize width succeeded.\n";
01236 }
01237
01238 if(size.y > static_cast<int>(maximum_height)) {
01239 window.reduce_height(maximum_height);
01240
01241 size = window.get_best_size();
01242 if(size.y > static_cast<int>(maximum_height)) {
01243 DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resize height failed."
01244 << " Wanted height " << maximum_height
01245 << " resulting height " << size.y
01246 << ".\n";
01247 throw tlayout_exception_height_resize_failed();
01248 }
01249 DBG_GUI_L << LOG_IMPL_HEADER
01250 << " Status: Resize height succeeded.\n";
01251 }
01252
01253 assert(size.x <= static_cast<int>(maximum_width)
01254 && size.y <= static_cast<int>(maximum_height));
01255
01256
01257 DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resizing succeeded.\n";
01258 return;
01259
01260 } catch (tlayout_exception_width_modified&) {
01261 DBG_GUI_L << LOG_IMPL_HEADER
01262 << " Status: Width has been modified, rerun.\n";
01263
01264 window.layout_init(false);
01265 window.layout_linked_widgets();
01266 layout(window, maximum_width, maximum_height);
01267 return;
01268 }
01269 }
01270
01271 void twindow::mouse_capture(const bool capture)
01272 {
01273 assert(event_distributor_);
01274 event_distributor_->capture_mouse(capture);
01275 }
01276
01277 void twindow::keyboard_capture(twidget* widget)
01278 {
01279 assert(event_distributor_);
01280 event_distributor_->keyboard_capture(widget);
01281 }
01282
01283 void twindow::add_to_keyboard_chain(twidget* widget)
01284 {
01285 assert(event_distributor_);
01286 event_distributor_->keyboard_add_to_chain(widget);
01287 }
01288
01289 void twindow::remove_from_keyboard_chain(twidget* widget)
01290 {
01291 assert(event_distributor_);
01292 event_distributor_->keyboard_remove_from_chain(widget);
01293 }
01294
01295 void twindow::signal_handler_sdl_video_resize(
01296 const event::tevent event, bool& handled, const tpoint& new_size)
01297 {
01298 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
01299
01300 if(new_size.x < preferences::min_allowed_width()
01301 || new_size.y < preferences::min_allowed_height()) {
01302
01303 DBG_GUI_E << LOG_HEADER << " resize aborted, too small.\n";
01304 return;
01305 }
01306
01307 if(new_size.x == static_cast<int>(settings::screen_width)
01308 && new_size.y == static_cast<int>(settings::screen_height)) {
01309
01310 DBG_GUI_E << LOG_HEADER << " resize not needed.\n";
01311 handled = true;
01312 return;
01313 }
01314
01315 if(!preferences::set_resolution(video_ , new_size.x, new_size.y)) {
01316
01317 LOG_GUI_E << LOG_HEADER
01318 << " resize aborted, resize failed.\n";
01319 return;
01320 }
01321
01322 settings::gamemap_width += new_size.x - settings::screen_width ;
01323 settings::gamemap_height += new_size.y - settings::screen_height ;
01324 settings::screen_width = new_size.x;
01325 settings::screen_height = new_size.y;
01326 invalidate_layout();
01327
01328 handled = true;
01329 }
01330
01331 void twindow::signal_handler_click_dismiss(
01332 const event::tevent event, bool& handled, bool& halt)
01333 {
01334 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
01335
01336 handled = halt = click_dismiss();
01337 }
01338
01339 void twindow::signal_handler_sdl_key_down(
01340 const event::tevent event, bool& handled, SDLKey key)
01341 {
01342 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
01343
01344 if(!enter_disabled_ && (key == SDLK_KP_ENTER || key == SDLK_RETURN)) {
01345 set_retval(OK);
01346 handled = true;
01347 } else if(key == SDLK_ESCAPE && !escape_disabled_) {
01348 set_retval(CANCEL);
01349 handled = true;
01350 } else if(key == SDLK_SPACE) {
01351 handled = click_dismiss();
01352 }
01353 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
01354 if(key == SDLK_F12) {
01355 debug_layout_->generate_dot_file(
01356 "manual", tdebug_layout_graph::MANUAL);
01357 handled = true;
01358 }
01359 #endif
01360 }
01361
01362 void twindow::signal_handler_message_show_tooltip(
01363 const event::tevent event
01364 , bool& handled
01365 , event::tmessage& message)
01366 {
01367 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
01368
01369 event::tmessage_show_tooltip& request =
01370 dynamic_cast<event::tmessage_show_tooltip&>(message);
01371
01372 tip::show(video_, tooltip_.id, request.message, request.location);
01373
01374 handled = true;
01375 }
01376
01377 void twindow::signal_handler_message_show_helptip(
01378 const event::tevent event
01379 , bool& handled
01380 , event::tmessage& message)
01381 {
01382 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
01383
01384 event::tmessage_show_helptip& request =
01385 dynamic_cast<event::tmessage_show_helptip&>(message);
01386
01387 tip::show(video_, helptip_.id, request.message, request.location);
01388
01389 handled = true;
01390 }
01391
01392 void twindow::signal_handler_request_placement(
01393 const event::tevent event
01394 , bool& handled)
01395 {
01396 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
01397
01398 invalidate_layout();
01399
01400 handled = true;
01401 }
01402
01403 }
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413
01414
01415
01416
01417
01418
01419
01420
01421
01422
01423
01424
01425
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436
01437
01438
01439
01440
01441
01442
01443
01444
01445
01446
01447
01448
01449
01450
01451
01452
01453
01454
01455
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468
01469
01470
01471
01472
01473
01474
01475
01476
01477
01478
01479
01480
01481
01482
01483
01484
01485
01486
01487
01488
01489
01490
01491
01492
01493
01494
01495
01496
01497
01498
01499
01500
01501
01502
01503
01504
01505
01506
01507
01508
01509
01510
01511
01512
01513
01514
01515
01516
01517
01518
01519
01520
01521
01522
01523
01524
01525
01526
01527
01528
01529
01530
01531
01532
01533
01534
01535
01536
01537
01538
01539
01540
01541
01542
01543
01544
01545
01546
01547
01548
01549
01550
01551
01552
01553
01554
01555
01556
01557
01558
01559
01560
01561
01562
01563
01564
01565
01566
01567
01568
01569
01570