construct_dialog.cpp

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

Generated by doxygen 1.7.1 on Thu May 24 2012 01:02:33 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs