construct_dialog.cpp

Go to the documentation of this file.
00001 /* $Id: construct_dialog.cpp 24818 2008-03-19 15:28:14Z brunowolff $ */
00002 /*
00003    Copyright (C) 2006 - 2008 by Patrick Parker <patrick_x99@hotmail.com>
00004    wesnoth widget Copyright (C) 2003-5 by David White <dave@whitevine.net>
00005    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License version 2
00009    or at your option any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #include "global.hpp"
00017 
00018 #define GETTEXT_DOMAIN "wesnoth-lib"
00019 
00020 #include "construct_dialog.hpp"
00021 #include "config.hpp"
00022 #include "cursor.hpp"
00023 #include "display.hpp"
00024 #include "events.hpp"
00025 #include "gettext.hpp"
00026 #include "image.hpp"
00027 #include "key.hpp"
00028 #include "sound.hpp"
00029 #include "log.hpp"
00030 #include "marked-up_text.hpp"
00031 #include "thread.hpp"
00032 #include "language.hpp"
00033 #include "sdl_utils.hpp"
00034 #include "tooltips.hpp"
00035 #include "util.hpp"
00036 #include "video.hpp"
00037 #include "widgets/button.hpp"
00038 #include "widgets/menu.hpp"
00039 #include "widgets/progressbar.hpp"
00040 #include "widgets/textbox.hpp"
00041 
00042 #include "SDL_ttf.h"
00043 
00044 #include <iostream>
00045 #include <numeric>
00046 
00047 #define ERR_DP LOG_STREAM(err, display)
00048 #define LOG_DP LOG_STREAM(info, display)
00049 #define DBG_DP LOG_STREAM(debug, display)
00050 #define ERR_G  LOG_STREAM(err, general)
00051 
00052 namespace gui {
00053 
00054 //static initialization
00055 //note: style names are directly related to the panel image file names
00056 const dialog::style& dialog::default_style = dialog_frame::default_style;
00057 const dialog::style& dialog::message_style = dialog_frame::message_style;
00058 const dialog::style dialog::hotkeys_style("menu2", 0);
00059 const int dialog::message_font_size = font::SIZE_PLUS;
00060 const int dialog::caption_font_size = font::SIZE_LARGE;
00061 const size_t dialog::left_padding = font::relative_size(10);
00062 const size_t dialog::right_padding = font::relative_size(10);
00063 const size_t dialog::image_h_pad = font::relative_size(/*image_ == NULL ? 0 :*/ 10);
00064 const size_t dialog::top_padding = font::relative_size(10);
00065 const size_t dialog::bottom_padding = font::relative_size(10);
00066 
00067 
00068 #ifdef USE_TINY_GUI
00069     const int dialog::max_menu_width = 300;
00070 #else
00071     const int dialog::max_menu_width = -1;
00072 #endif
00073 
00074 }
00075 
00076 namespace {
00077 
00078 std::vector<std::string> empty_string_vector;
00079 
00080 } //end anonymous namespace
00081 
00082 namespace gui {
00083 
00084 dialog_textbox::~dialog_textbox()
00085 {
00086     delete label_;
00087 }
00088 
00089 dialog::dimension_measurements::dimension_measurements() :
00090     x(-1), 
00091     y(-1), 
00092     interior(empty_rect),
00093     message(empty_rect), 
00094     textbox(empty_rect),
00095     menu_width(0),
00096     panes(),
00097     label_x(-1),
00098     label_y(-1),
00099     menu_x(-1),
00100     menu_y(-1),
00101     menu_height(-1),
00102     image_x(-1),
00103     image_y(-1),
00104     caption_x(-1),
00105     caption_y(-1),
00106     buttons()
00107 {
00108     //note: this is not defined in the header file to C++ ODR (one-definition rule)
00109     //since each inclusion of the header file uses a different version of empty_rect
00110     //(unnamed namespace and/or const object defined at declaration time).
00111 }
00112 
00113 dialog::dialog(display &disp, const std::string& title, const std::string& message,
00114         const DIALOG_TYPE type, const style& dialog_style) : 
00115     disp_(disp), 
00116     image_(NULL),
00117     title_(title),
00118     style_(dialog_style),
00119     title_widget_(NULL),
00120     message_(NULL),
00121     type_(type),
00122     menu_(NULL),
00123     preview_panes_(),
00124     button_pool_(),
00125     standard_buttons_(),
00126     extra_buttons_(),
00127     frame_buttons_(),
00128     topic_(),
00129     help_button_(NULL),
00130     text_widget_(NULL),
00131     frame_(NULL),
00132     dim_(),
00133     result_(CONTINUE_DIALOG)
00134 {
00135     CVideo& screen = disp_.video();
00136 
00137     switch(type)
00138     {
00139     case MESSAGE:
00140     default:
00141         break;
00142     case OK_ONLY:
00143         add_button(new standard_dialog_button(screen,_("OK"),0,true), BUTTON_STANDARD);
00144         break;
00145     case YES_NO:
00146         add_button(new standard_dialog_button(screen,_("Yes"),0,false), BUTTON_STANDARD);
00147         add_button(new standard_dialog_button(screen,_("No"),1,true), BUTTON_STANDARD);
00148         break;
00149     case OK_CANCEL:
00150         add_button(new standard_dialog_button(screen,_("OK"),0,false), BUTTON_STANDARD);
00151         add_button(new standard_dialog_button(screen,_("Cancel"),1,true), BUTTON_STANDARD);
00152         break;
00153     case CANCEL_ONLY:
00154         add_button(new standard_dialog_button(screen,_("Cancel"),0,true), BUTTON_STANDARD);
00155         break;
00156     case CLOSE_ONLY:
00157         add_button(new standard_dialog_button(screen,_("Close"),0,true), BUTTON_STANDARD);
00158         break;
00159     }
00160     //dialog creator should catch(button::error&) ?
00161 
00162     try {
00163         std::string msg = font::word_wrap_text(message, message_font_size, screen.getx() / 2, screen.gety() / 2);
00164         message_ = new label(screen, msg, message_font_size, font::NORMAL_COLOUR, false);
00165     } catch(utils::invalid_utf8_exception&) {
00166         ERR_DP << "Problem handling utf8 in message '" << message << "'\n";
00167         throw;
00168     }
00169 
00170 }
00171 
00172 dialog::~dialog()
00173 {
00174     if(menu_ != empty_menu)
00175     {
00176         delete menu_;
00177     }
00178     delete title_widget_;
00179     delete message_;
00180     delete text_widget_;
00181     delete image_;
00182     delete frame_;
00183 
00184     button_pool_iterator b;
00185     for (b = button_pool_.begin(); b != button_pool_.end(); ++b) {
00186         delete b->first;
00187     }
00188 //  pp_iterator p;
00189 //  for (p = preview_panes_.begin(); p != preview_panes_.end(); ++p) {
00190 //      delete (*p);
00191 //  }
00192 }
00193 
00194 bool dialog::option_checked(unsigned int option_index)
00195 {
00196     unsigned int i = 0;
00197     button_pool_iterator b;
00198     for (b = button_pool_.begin(); b != button_pool_.end(); ++b) {
00199         if(b->first->is_option()) {
00200             if(option_index == i++) {
00201                 return b->first->checked();
00202             }
00203         }
00204     }
00205     return false;
00206 }
00207 
00208 void dialog::add_button(dialog_button *const btn, BUTTON_LOCATION loc)
00209 {
00210     std::pair<dialog_button *, BUTTON_LOCATION> new_pair(btn,loc);
00211     button_pool_.push_back(new_pair);
00212     switch(loc)
00213     {
00214     case BUTTON_HELP:
00215         delete help_button_;
00216         help_button_ = btn;
00217         break;
00218     case BUTTON_EXTRA:
00219     case BUTTON_EXTRA_LEFT:
00220     case BUTTON_CHECKBOX:
00221     case BUTTON_CHECKBOX_LEFT:
00222         extra_buttons_.push_back(btn);
00223         break;
00224     case BUTTON_STANDARD:
00225         standard_buttons_.push_back(btn);
00226         break;
00227     default:
00228         break;
00229     }
00230     btn->set_parent(this);
00231 }
00232 
00233 void dialog::add_button(dialog_button_info btn_info, BUTTON_LOCATION loc)
00234 {
00235     dialog_button *btn = new dialog_button(disp_.video(), btn_info.label, button::TYPE_PRESS, CONTINUE_DIALOG, btn_info.handler);
00236     add_button(btn, loc);
00237 }
00238 
00239 void dialog::add_option(const std::string& label, bool checked, BUTTON_LOCATION loc)
00240 {
00241     gui::dialog_button *btn = new dialog_button(disp_.video(), label, button::TYPE_CHECK);
00242     btn->set_check(checked);
00243     add_button(btn, loc);
00244 }
00245 
00246 void dialog::set_textbox(const std::string& text_widget_label,
00247                 const std::string& text_widget_text,
00248                 const int text_widget_max_chars, const unsigned int text_box_width)
00249 {
00250     label *label_ptr = new label(disp_.video(), text_widget_label, message_font_size, font::NORMAL_COLOUR, false);
00251     const bool editable_textbox = std::find(text_widget_text.begin(),text_widget_text.end(),'\n') == text_widget_text.end();
00252     text_widget_ = new dialog_textbox(label_ptr, disp_.video(), text_box_width, text_widget_text, editable_textbox, text_widget_max_chars);
00253     text_widget_->set_wrap(!editable_textbox);
00254 }
00255 
00256 void dialog::set_menu(const std::vector<std::string> &menu_items, menu::sorter* sorter)
00257 {
00258     set_menu(new gui::menu(disp_.video(), menu_items, (type_==MESSAGE),
00259         -1, dialog::max_menu_width, sorter, &menu::default_style, false));
00260 }
00261 
00262 void dialog::set_menu_items(const std::vector<std::string> &menu_items)
00263 {
00264     if(menu_ == empty_menu) {
00265         set_menu(menu_items);
00266     } else {
00267         menu_->set_items(menu_items);
00268 
00269         for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
00270             (**i).set_selection(menu_->selection());
00271         }
00272     }
00273 }
00274 
00275 menu& dialog::get_menu()
00276 {
00277     if(menu_ == NULL)
00278     {
00279         if(empty_menu == NULL) {
00280             empty_menu = new gui::menu(disp_.video(),empty_string_vector,false,-1,-1,NULL,&menu::simple_style);
00281             empty_menu->leave();
00282         }
00283         menu_ = empty_menu; //no menu, so fake it
00284     }
00285     return *menu_;
00286 }
00287 
00288 int dialog::show(int xloc, int yloc)
00289 {
00290     layout(xloc, yloc);
00291     return show();
00292 }
00293 
00294 int dialog::show()
00295 {
00296     if (disp_.video().faked()) return CLOSE_DIALOG;
00297 
00298     if(disp_.video().update_locked()) {
00299         ERR_DP << "display locked ignoring dialog '" << title_ << "' '" << message_->get_text() << "'\n";
00300         return CLOSE_DIALOG;
00301     }
00302 
00303     LOG_DP << "showing dialog '" << title_ << "' '" << message_->get_text() << "'\n";
00304     if(dim_.interior == empty_rect) { layout(); }
00305 
00306     //create the event context, remember to instruct any passed-in widgets to join it
00307     const events::event_context dialog_events_context;
00308     const dialog_manager manager;
00309     const resize_lock prevent_resizing;
00310 
00311     //draw
00312     draw_frame();
00313     update_widget_positions();
00314     draw_contents();
00315 
00316     //process
00317     dialog_process_info dp_info;
00318     do
00319     {
00320         events::pump();
00321         set_result(process(dp_info));
00322         if(!done()) {
00323             refresh();
00324         }
00325         action(dp_info);
00326         dp_info.cycle();
00327     } while(!done());
00328 
00329     clear_background();
00330     return result();
00331 }
00332 
00333 void dialog::draw_contents()
00334 {
00335     if(!preview_panes_.empty()) {
00336         for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
00337             preview_pane *pane = *i;
00338             if(!pane->handler_members().empty())
00339             {
00340                 pane->draw();
00341                 pane->needs_restore_ = false; //prevent panes from drawing over members
00342             }
00343         }
00344     }
00345     events::raise_draw_event(); //draw widgets
00346 
00347     disp_.flip();
00348     disp_.invalidate_all();
00349 }
00350 
00351 dialog_frame& dialog::get_frame()
00352 {
00353     if(frame_ == NULL) {
00354         CVideo& screen = disp_.video();
00355         frame_buttons_.clear();
00356         for(button_iterator b = standard_buttons_.begin(); b != standard_buttons_.end(); ++b)
00357         {
00358             frame_buttons_.push_back(*b);
00359         }
00360         frame_ = new dialog_frame(screen, title_, style_,  true, &frame_buttons_, help_button_);
00361     }
00362     return *frame_;
00363 }
00364 
00365 void dialog::clear_background() {
00366     delete frame_;
00367     frame_ = NULL;
00368 }
00369 
00370 void dialog::draw_frame()
00371 {
00372     get_frame().draw();
00373 }
00374 
00375 void dialog::update_widget_positions()
00376 {
00377     if(!preview_panes_.empty()) {
00378         for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
00379             preview_pane *pane = *i;
00380             pane->join();
00381             pane->set_location(dim_.panes.find(pane)->second);
00382         }
00383     }
00384     if(text_widget_) {
00385         text_widget_->join();
00386         text_widget_->set_location(dim_.textbox);
00387         if(text_widget_->get_label()) {
00388             text_widget_->get_label()->set_location(dim_.label_x, dim_.label_y);
00389         }
00390     }
00391     if(get_menu().height() > 0) {
00392         menu_->join();
00393         menu_->set_numeric_keypress_selection(text_widget_ == NULL);
00394         menu_->set_width( dim_.menu_width );
00395         menu_->set_max_width( dim_.menu_width ); //lock the menu width
00396         if(dim_.menu_height >= 0) {
00397             menu_->set_max_height( dim_.menu_height );
00398         }
00399         menu_->set_location( dim_.menu_x, dim_.menu_y );
00400     }
00401     if(image_) {
00402         image_->join();
00403         image_->set_location(dim_.image_x, dim_.image_y);
00404         if(image_->caption()) {
00405             image_->caption()->set_location(dim_.caption_x, dim_.caption_y);
00406         }
00407     }
00408     button_iterator b;
00409     for(b = extra_buttons_.begin(); b != extra_buttons_.end(); ++b) {
00410         dialog_button *btn = *b;
00411         btn->join();
00412         std::pair<int,int> coords = dim_.buttons.find(btn)->second;
00413         btn->set_location(coords.first, coords.second);
00414     }
00415     for(b = standard_buttons_.begin(); b != standard_buttons_.end(); ++b) {
00416         dialog_button *btn = *b;
00417         btn->join();
00418     }
00419     if(help_button_) {
00420         help_button_->join();
00421     }
00422     message_->set_location(dim_.message);
00423     message_->join();
00424 }
00425 
00426 void dialog::refresh()
00427 {
00428     disp_.flip();
00429     disp_.delay(10);
00430 }
00431 
00432 dialog::dimension_measurements dialog::layout(int xloc, int yloc)
00433 {
00434     CVideo& screen = disp_.video();
00435     surface const scr = screen.getSurface();
00436 
00437     dimension_measurements dim;
00438     dim.x = xloc;
00439     dim.y = yloc;
00440 
00441     const bool use_textbox = (text_widget_ != NULL);
00442     int text_widget_width = 0;
00443     int text_widget_height = 0;
00444     if(use_textbox) {
00445         const SDL_Rect& area = font::text_area(text_widget_->text(),message_font_size);
00446         dim.textbox.w = minimum<size_t>(screen.getx()/2,maximum<size_t>(area.w,text_widget_->width()));
00447         dim.textbox.h = minimum<size_t>(screen.gety()/2,maximum<size_t>(area.h,text_widget_->height()));
00448         text_widget_width = dim.textbox.w;
00449         text_widget_width += (text_widget_->get_label() == NULL) ? 0 : text_widget_->get_label()->width();
00450         text_widget_height = dim.textbox.h + message_font_size;
00451     }
00452 
00453     const bool use_menu = (get_menu().height() > 0);
00454     if(!message_->get_text().empty()) {
00455         dim.message.w = message_->width();
00456         dim.message.h = message_->height();
00457     }
00458     unsigned int caption_width = 0;
00459     unsigned int caption_height = 0;
00460     if (image_ != NULL && image_->caption() != NULL) {
00461         caption_width = image_->caption()->width();
00462         caption_height = image_->caption()->height();
00463     }
00464 
00465     int check_button_height = 0;
00466     int left_check_button_height = 0;
00467     const int button_height_padding = 5;
00468 
00469     for(button_pool_const_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
00470         dialog_button const *const btn = b->first;
00471         switch(b->second)
00472         {
00473         case BUTTON_EXTRA:
00474         case BUTTON_CHECKBOX:
00475             check_button_height += btn->height() + button_height_padding;
00476             break;
00477         case BUTTON_EXTRA_LEFT:
00478         case BUTTON_CHECKBOX_LEFT:
00479             left_check_button_height += btn->height() + button_height_padding;
00480             break;
00481         case BUTTON_STANDARD:
00482         default:
00483             break;
00484         }
00485     }
00486     check_button_height = maximum<int>(check_button_height, left_check_button_height);
00487 
00488     size_t above_preview_pane_height = 0, above_left_preview_pane_width = 0, above_right_preview_pane_width = 0;
00489     size_t preview_pane_height = 0, left_preview_pane_width = 0, right_preview_pane_width = 0;
00490     if(!preview_panes_.empty()) {
00491         for(pp_const_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
00492             preview_pane const *const pane = *i;
00493             const SDL_Rect& rect = pane->location();
00494             if(pane->show_above() == false) {
00495                 preview_pane_height = maximum<size_t>(rect.h,preview_pane_height);
00496                 if(pane->left_side()) {
00497                     left_preview_pane_width += rect.w;
00498                 } else {
00499                     right_preview_pane_width += rect.w;
00500                 }
00501             } else {
00502                 above_preview_pane_height = maximum<size_t>(rect.h,above_preview_pane_height);
00503                 if(pane->left_side()) {
00504                     above_left_preview_pane_width += rect.w;
00505                 } else {
00506                     above_right_preview_pane_width += rect.w;
00507                 }
00508             }
00509         }
00510     }
00511 
00512     const int menu_hpadding = font::relative_size((dim.message.h > 0 && use_menu) ? 10 : 0);
00513     const size_t image_h_padding = (image_ == NULL)? 0 : image_h_pad;
00514     const size_t padding_width = left_padding + right_padding + image_h_padding;
00515     const size_t padding_height = top_padding + bottom_padding + menu_hpadding;
00516     const size_t image_width = (image_ == NULL) ? 0 : image_->width();
00517     const size_t image_height = (image_ == NULL) ? 0 : image_->height();
00518     const size_t total_text_height = dim.message.h + caption_height;
00519 
00520     size_t text_width = dim.message.w;
00521     if(caption_width > text_width)
00522         text_width = caption_width;
00523 
00524     // Prevent the menu to be larger than the screen
00525     dim.menu_width = menu_->width();
00526     if(dim.menu_width + image_width + padding_width + left_preview_pane_width + right_preview_pane_width > static_cast<size_t>(scr->w))
00527         dim.menu_width = scr->w - image_width - padding_width - left_preview_pane_width - right_preview_pane_width;
00528     if(dim.menu_width > text_width)
00529         text_width = dim.menu_width;
00530 
00531 
00532     size_t total_width = image_width + text_width + padding_width;
00533 
00534     if(text_widget_width+left_padding+right_padding > total_width)
00535         total_width = text_widget_width+left_padding+right_padding;
00536 
00537     //Prevent the menu from being too skinny
00538     if(use_menu && preview_panes_.empty() &&
00539         total_width > dim.menu_width + image_width + padding_width) {
00540         dim.menu_width = total_width - image_width - padding_width;
00541     }
00542 
00543     const size_t text_and_image_height = image_height > total_text_height ? image_height : total_text_height;
00544 
00545     const int total_height = text_and_image_height + padding_height + menu_->height() +
00546         text_widget_height + check_button_height;
00547 
00548     dim.interior.w = maximum<int>(total_width,above_left_preview_pane_width + above_right_preview_pane_width);
00549     dim.interior.h = maximum<int>(total_height,static_cast<int>(preview_pane_height));
00550     dim.interior.x = maximum<int>(0,dim.x >= 0 ? dim.x : scr->w/2 - (dim.interior.w + left_preview_pane_width + right_preview_pane_width)/2);
00551     dim.interior.y = maximum<int>(0,dim.y >= 0 ? dim.y : scr->h/2 - (dim.interior.h + above_preview_pane_height)/2);
00552 
00553     DBG_DP << "above_preview_pane_height: " << above_preview_pane_height << "; "
00554         << "dim.interior.y: " << scr->h/2 << " - " << (dim.interior.h + above_preview_pane_height)/2 << " = "
00555         << dim.interior.y << "; " << "dim.interior.h: " << dim.interior.h << "\n";
00556 
00557     if(dim.x <= -1 || dim.y <= -1) {
00558         dim.x = dim.interior.x + left_preview_pane_width;
00559         dim.y = dim.interior.y + above_preview_pane_height;
00560     }
00561 
00562     if(dim.x + dim.interior.w > scr->w) {
00563         dim.x = scr->w - dim.interior.w;
00564         if(dim.x < dim.interior.x) {
00565             dim.interior.x = dim.x;
00566         }
00567     }
00568 
00569     const int frame_top_pad = get_frame().top_padding();
00570     const int frame_bottom_pad = get_frame().bottom_padding();
00571     if(dim.y + dim.interior.h + frame_bottom_pad > scr->h) {
00572         dim.y = maximum<int>(frame_top_pad, scr->h - dim.interior.h - frame_bottom_pad);
00573         if(dim.y < dim.interior.y) {
00574             dim.interior.y = dim.y;
00575         }
00576     }
00577 
00578     dim.interior.w += left_preview_pane_width + right_preview_pane_width;
00579     dim.interior.h += above_preview_pane_height;
00580 
00581     const int max_height = scr->h - dim.interior.y - frame_bottom_pad;
00582     if(dim.interior.h > max_height) {
00583         //try to rein in the menu height a little bit
00584         const int menu_height = menu_->height();
00585         if(menu_height > 0) {
00586             dim.menu_height = maximum<int>(1, max_height - dim.interior.h + menu_height);
00587             dim.interior.h -= menu_height - dim.menu_height;
00588         }
00589     }
00590 
00591     //calculate the positions of the preview panes to the sides of the dialog
00592     if(!preview_panes_.empty()) {
00593 
00594         int left_preview_pane = dim.interior.x;
00595         int right_preview_pane = dim.interior.x + total_width + left_preview_pane_width;
00596         int above_left_preview_pane = dim.interior.x + dim.interior.w/2;
00597         int above_right_preview_pane = above_left_preview_pane;
00598 
00599         for(pp_const_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
00600         preview_pane const *const pane = *i;
00601             SDL_Rect area = pane->location();
00602 
00603             if(pane->show_above() == false) {
00604                 area.y = dim.y;
00605                 area.h = dim.interior.h;
00606                 if(pane->left_side()) {
00607                     area.x = left_preview_pane;
00608                     left_preview_pane += area.w;
00609                 } else {
00610                     area.x = right_preview_pane;
00611                     right_preview_pane += area.w;
00612                 }
00613             } else {
00614                 area.y = dim.interior.y;
00615                 area.h = above_preview_pane_height;
00616                 if(pane->left_side()) {
00617                     area.x = above_left_preview_pane - area.w;
00618                     above_left_preview_pane -= area.w;
00619                 } else {
00620                     area.x = above_right_preview_pane;
00621                     above_right_preview_pane += area.w;
00622                 }
00623             }
00624             dim.panes[*i] = area;
00625         }
00626     }
00627 
00628     const int text_widget_y = dim.y+top_padding+text_and_image_height-6+menu_hpadding;
00629 
00630     if(use_textbox) {
00631         dim.textbox.x = dim.x + left_padding + text_widget_width - dim.textbox.w;
00632         dim.textbox.y = text_widget_y + (text_widget_height - dim.textbox.h)/2;
00633         dim.label_x = dim.x+left_padding;
00634         dim.label_y = dim.textbox.y;
00635     }
00636 
00637     dim.menu_x = dim.x+image_width+left_padding+image_h_padding;
00638     dim.menu_y = dim.y+top_padding+text_and_image_height+menu_hpadding+ (use_textbox ? text_widget_->location().h + top_padding : 0);
00639 
00640     dim.message.x = dim.x + left_padding;
00641     dim.message.y = dim.y + top_padding + caption_height;
00642 
00643     if(image_ != NULL) {
00644         const int x = dim.x + left_padding;
00645         const int y = dim.y + top_padding;
00646         dim.message.x += image_width + image_h_padding;
00647         dim.image_x = x;
00648         dim.image_y = y;
00649         dim.caption_x = dim.x + image_width + left_padding + image_h_padding;
00650         dim.caption_y = dim.y + top_padding;
00651     }
00652 
00653     //set the position of any tick boxes. by default, they go right below the menu,
00654     //slammed against the right side of the dialog
00655     if(extra_buttons_.empty() == false) {
00656         int options_y = text_widget_y + text_widget_height + menu_->height() + button_height_padding + menu_hpadding;
00657         int options_left_y = options_y;
00658         for(button_pool_const_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
00659         dialog_button const *const btn = b->first;
00660         std::pair<int,int> coords;
00661             switch(b->second)
00662             {
00663             case BUTTON_EXTRA:
00664             case BUTTON_CHECKBOX:
00665                 coords.first = dim.x + total_width - btn->width() - ButtonHPadding;
00666                 coords.second = options_y;
00667                 dim.buttons[b->first] = coords;
00668                 options_y += btn->height() + button_height_padding;
00669                 break;
00670             case BUTTON_EXTRA_LEFT:
00671             case BUTTON_CHECKBOX_LEFT:
00672                 coords.first = dim.x + ButtonHPadding;
00673                 coords.second = options_left_y;
00674                 dim.buttons[b->first] = coords;
00675                 options_left_y += btn->height() + button_height_padding;
00676                 break;
00677             case BUTTON_STANDARD:
00678             default:
00679                 break;
00680             }
00681         }
00682     }
00683     set_layout(dim);
00684     return dim;
00685 }
00686 
00687 void dialog::set_layout(dimension_measurements &new_dim) {
00688     get_frame().layout(new_dim.interior);
00689     dim_ = new_dim;
00690 }
00691 
00692 
00693 int dialog::process(dialog_process_info &info)
00694 {
00695 
00696     int mousex, mousey;
00697     int mouse_flags = SDL_GetMouseState(&mousex,&mousey);
00698 
00699     info.new_right_button = (mouse_flags&SDL_BUTTON_RMASK) != 0;
00700     info.new_left_button = (mouse_flags&SDL_BUTTON_LMASK) != 0;
00701     info.new_key_down = info.key[SDLK_SPACE] || info.key[SDLK_RETURN] ||
00702                     info.key[SDLK_ESCAPE] || info.key[SDLK_KP_ENTER];
00703     info.double_clicked = menu_->double_clicked();
00704     get_menu();
00705     const bool use_menu = (menu_ != empty_menu);
00706     const bool use_text_input = (text_widget_!=NULL);
00707     const bool has_input = (use_menu||use_text_input);//input of any sort has to be made
00708 
00709     if(((!info.key_down && info.key[SDLK_RETURN]) || info.key[SDLK_KP_ENTER] || info.double_clicked) &&
00710        (type_ == YES_NO || type_ == OK_CANCEL || type_ == OK_ONLY || type_ == CLOSE_ONLY)) {
00711 
00712         return (use_menu ? menu_->selection() : 0);
00713     }
00714 
00715     //escape quits from the dialog -- unless it's an "ok" dialog with input
00716     if(!info.key_down && info.key[SDLK_ESCAPE] && !(type_ == OK_ONLY && has_input)) {
00717         return (CLOSE_DIALOG);
00718     }
00719 
00720     //inform preview panes when there is a new menu selection
00721     if((menu_->selection() != info.selection) || info.first_time) {
00722         info.selection = menu_->selection();
00723         int selection = info.selection;
00724         if(selection < 0) {
00725             selection = 0;
00726         }
00727         if(!preview_panes_.empty()) {
00728             for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
00729                 (**i).set_selection(selection);
00730                 if(info.first_time) {
00731                     (**i).set_dirty();
00732                 }
00733             }
00734         }
00735     }
00736 
00737     info.first_time = false;
00738 
00739     if(use_menu) {
00740         //get any drop-down choice or context-menu click
00741         const int selection = menu_->process();
00742         if(selection != -1)
00743         {
00744             return (selection);
00745         }
00746     }
00747 
00748     events::raise_process_event();
00749     events::raise_draw_event();
00750 
00751     //left-clicking outside of a drop-down or context-menu should close it
00752     if (info.new_left_button && !info.left_button) {
00753         if (standard_buttons_.empty() && !point_in_rect(mousex,mousey, menu_->location())) {
00754             if (use_menu)
00755                 sound::play_UI_sound(game_config::sounds::button_press);
00756             return CLOSE_DIALOG;
00757             }
00758     }
00759 
00760     //right-clicking outside of a dialog should close it unless a choice is required
00761     //note: this will also close any context-menu or drop-down when it is right-clicked
00762     //      but that may be changed to allow right-click selection instead.
00763     if (info.new_right_button && !info.right_button) {
00764         if( standard_buttons_.empty()
00765         || (!point_in_rect(mousex,mousey,get_frame().get_layout().exterior)
00766         && type_ != YES_NO && !(type_ == OK_ONLY && has_input))) {
00767             sound::play_UI_sound(game_config::sounds::button_press);
00768             return CLOSE_DIALOG;
00769         }
00770     }
00771 
00772     //any keypress should close a dialog if it has one standard button (or less)
00773     //and no menu options.
00774     if (info.new_key_down && !info.key_down) {
00775         if (standard_buttons_.size() < 2 && !has_input)
00776             return CLOSE_DIALOG;
00777     }
00778 
00779     //now handle any button presses
00780     for(button_pool_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
00781         if(b->first->pressed()) {
00782             return b->first->action(info);
00783         }
00784     }
00785 
00786     return CONTINUE_DIALOG;
00787 }
00788 
00789 int dialog_button::action(dialog_process_info &info) {
00790     if(handler_ != NULL) {
00791         menu &menu_ref = parent_->get_menu();
00792         dialog_button_action::RESULT res = handler_->button_pressed(menu_ref.selection());
00793 
00794         if(res == DELETE_ITEM || res == CLOSE_DIALOG) {
00795             return res;
00796         }
00797 
00798         //reset button-tracking flags so that if the action displays a dialog, a button-press
00799         //at the end of the dialog won't be mistaken for a button-press in this dialog.
00800         //(We should eventually use a proper event-handling system instead of tracking
00801         //flags to avoid problems like this altogether).
00802         info.clear_buttons();
00803         return CONTINUE_DIALOG;
00804     }
00805     return simple_result_;
00806 }
00807 
00808 void dialog::action(dialog_process_info& info)
00809 {
00810     //default way of handling a "delete item" request
00811     if(result() == DELETE_ITEM) {
00812         menu &menu_ref = get_menu();
00813         const int selection = menu_ref.selection();
00814         if(selection >= 0) {
00815             menu_ref.erase_item(selection);
00816         }
00817         if(menu_ref.nitems() == 0) {
00818             set_result(CLOSE_DIALOG);
00819         } else {
00820             set_result(CONTINUE_DIALOG);
00821             info.first_time = true;
00822         }
00823     }
00824 }
00825 
00826 int standard_dialog_button::action(dialog_process_info &/*info*/) {
00827     //if the menu is not used, then return the index of the
00828     //button pressed, otherwise return the index of the menu
00829     //item selected if the last button is not pressed, and
00830     //cancel (-1) otherwise
00831     if(dialog()->get_menu().height() <= 0) {
00832         return simple_result_;
00833     } else if((simple_result_ == 0 && is_last_) || !is_last_) {
00834         return (dialog()->get_menu().selection());
00835     }
00836     return CLOSE_DIALOG;
00837 }
00838 
00839 void dialog::set_image(surface surf, const std::string &caption)
00840 {
00841     label *label_ptr = NULL;
00842     if(!caption.empty()) {
00843         label_ptr = new label(disp_.video(), caption, caption_font_size, font::NORMAL_COLOUR, false);
00844     }
00845     set_image( new dialog_image(label_ptr, disp_.video(), surf ));
00846 }
00847 
00848 void dialog_image::draw_contents()
00849 {
00850     video().blit_surface(location().x, location().y, surf_);
00851 }
00852 
00853 int message_dialog::show(msecs minimum_lifetime)
00854 {
00855     prevent_misclick_until_ = SDL_GetTicks() + minimum_lifetime;
00856     return dialog::show();
00857 }
00858 
00859 void message_dialog::action(gui::dialog_process_info &dp_info)
00860 {
00861     dialog::action(dp_info);
00862     if(done() && SDL_GetTicks() < prevent_misclick_until_ && result() != gui::ESCAPE_DIALOG) {
00863         //discard premature results
00864         set_result(gui::CONTINUE_DIALOG);
00865     }
00866 }
00867 
00868 message_dialog::~message_dialog()
00869 {
00870 }
00871 
00872 }//end namespace gui

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs