Logo Search packages:      
Sourcecode: wesnoth-1.9 version File versions  Download package

construct_dialog.cpp

/* $Id: construct_dialog.cpp 46186 2010-09-01 21:12:38Z silene $ */
/*
   Copyright (C) 2006 - 2010 by Patrick Parker <patrick_x99@hotmail.com>
   wesnoth widget Copyright (C) 2003-5 by David White <dave@whitevine.net>
   Part of the Battle for Wesnoth Project http://www.wesnoth.org/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/

#include "global.hpp"

#define GETTEXT_DOMAIN "wesnoth-lib"

#include "construct_dialog.hpp"
#include "display.hpp"
#include "game_config.hpp"
#include "gettext.hpp"
#include "sound.hpp"
#include "log.hpp"
#include "marked-up_text.hpp"


static lg::log_domain log_display("display");
#define ERR_DP LOG_STREAM(err, log_display)
#define LOG_DP LOG_STREAM(info, log_display)
#define DBG_DP LOG_STREAM(debug, log_display)
#define ERR_G  LOG_STREAM(err, lg::general)

namespace gui {

//static initialization
//note: style names are directly related to the panel image file names
const dialog::style& dialog::default_style = dialog_frame::default_style;
const dialog::style& dialog::message_style = dialog_frame::message_style;
const dialog::style dialog::hotkeys_style("menu2", 0);
const int dialog::message_font_size = font::SIZE_PLUS;
const int dialog::caption_font_size = font::SIZE_LARGE;
const size_t dialog::left_padding = font::relative_size(10);
const size_t dialog::right_padding = font::relative_size(10);
const size_t dialog::image_h_pad = font::relative_size(/*image_ == NULL ? 0 :*/ 10);
const size_t dialog::top_padding = font::relative_size(10);
const size_t dialog::bottom_padding = font::relative_size(10);


#ifdef USE_TINY_GUI
      const int dialog::max_menu_width = 300;
#else
      const int dialog::max_menu_width = -1;
#endif

}

namespace {

std::vector<std::string> empty_string_vector;

} //end anonymous namespace

namespace gui {

dialog_textbox::~dialog_textbox()
{
      delete label_;
}

dialog::dimension_measurements::dimension_measurements() :
      x(-1),
      y(-1),
      interior(empty_rect),
      message(empty_rect),
      textbox(empty_rect),
      menu_width(0),
      panes(),
      label_x(-1),
      label_y(-1),
      menu_x(-1),
      menu_y(-1),
      menu_height(-1),
      image_x(-1),
      image_y(-1),
      caption_x(-1),
      caption_y(-1),
      buttons()
{
      //note: this is not defined in the header file to C++ ODR (one-definition rule)
      //since each inclusion of the header file uses a different version of empty_rect
      //(unnamed namespace and/or const object defined at declaration time).
}

dialog::dialog(display &disp, const std::string& title, const std::string& message,
            const DIALOG_TYPE type, const style& dialog_style) :
      disp_(disp),
      image_(NULL),
      title_(title),
      style_(dialog_style),
      title_widget_(NULL),
      message_(NULL),
      type_(type),
      menu_(NULL),
      preview_panes_(),
      button_pool_(),
      standard_buttons_(),
      extra_buttons_(),
      frame_buttons_(),
      topic_(),
      help_button_(NULL),
      text_widget_(NULL),
      frame_(NULL),
      dim_(),
      result_(CONTINUE_DIALOG)
{
      CVideo& screen = disp_.video();

      switch(type)
      {
      case MESSAGE:
      default:
            break;
      case OK_ONLY:
            add_button(new standard_dialog_button(screen,_("OK"),0,true), BUTTON_STANDARD);
            break;
      case YES_NO:
            add_button(new standard_dialog_button(screen,_("Yes"),0,false), BUTTON_STANDARD);
            add_button(new standard_dialog_button(screen,_("No"),1,true), BUTTON_STANDARD);
            break;
      case OK_CANCEL:
            add_button(new standard_dialog_button(screen,_("OK"),0,false), BUTTON_STANDARD);
            add_button(new standard_dialog_button(screen,_("Cancel"),1,true), BUTTON_STANDARD);
            break;
      case CANCEL_ONLY:
            add_button(new standard_dialog_button(screen,_("Cancel"),0,true), BUTTON_STANDARD);
            break;
      case CLOSE_ONLY:
            add_button(new standard_dialog_button(screen,_("Close"),0,true), BUTTON_STANDARD);
            break;
      }
      //dialog creator should catch(button::error&) ?

      try {
            std::string msg = font::word_wrap_text(message, message_font_size, screen.getx() / 2, screen.gety() / 2);
            message_ = new label(screen, msg, message_font_size, font::NORMAL_COLOR, false);
      } catch(utils::invalid_utf8_exception&) {
            ERR_DP << "Problem handling utf8 in message '" << message << "'\n";
            throw;
      }

}

dialog::~dialog()
{
      if(menu_ != empty_menu)
      {
            delete menu_;
      }
      delete title_widget_;
      delete message_;
      delete text_widget_;
      delete image_;
      delete frame_;

      button_pool_iterator b;
      for (b = button_pool_.begin(); b != button_pool_.end(); ++b) {
            delete b->first;
      }
//    pp_iterator p;
//    for (p = preview_panes_.begin(); p != preview_panes_.end(); ++p) {
//          delete (*p);
//    }
}

bool dialog::option_checked(unsigned int option_index)
{
      unsigned int i = 0;
      button_pool_iterator b;
      for (b = button_pool_.begin(); b != button_pool_.end(); ++b) {
            if(b->first->is_option()) {
                  if(option_index == i++) {
                        return b->first->checked();
                  }
            }
      }
      return false;
}

void dialog::add_button(dialog_button *const btn, BUTTON_LOCATION loc)
{
      std::pair<dialog_button *, BUTTON_LOCATION> new_pair(btn,loc);
      button_pool_.push_back(new_pair);
      switch(loc)
      {
      case BUTTON_HELP:
            delete help_button_;
            help_button_ = btn;
            break;
      case BUTTON_EXTRA:
      case BUTTON_EXTRA_LEFT:
      case BUTTON_CHECKBOX:
      case BUTTON_CHECKBOX_LEFT:
            extra_buttons_.push_back(btn);
            break;
      case BUTTON_STANDARD:
            standard_buttons_.push_back(btn);
            break;
      default:
            break;
      }
      btn->set_parent(this);
}

void dialog::add_button(dialog_button_info btn_info, BUTTON_LOCATION loc)
{
      dialog_button *btn = new dialog_button(disp_.video(), btn_info.label, button::TYPE_PRESS, CONTINUE_DIALOG, btn_info.handler);
      add_button(btn, loc);
}

void dialog::add_option(const std::string& label, bool checked, BUTTON_LOCATION loc)
{
      gui::dialog_button *btn = new dialog_button(disp_.video(), label, button::TYPE_CHECK);
      btn->set_check(checked);
      add_button(btn, loc);
}

void dialog::set_textbox(const std::string& text_widget_label,
                        const std::string& text_widget_text,
                        const int text_widget_max_chars, const unsigned int text_box_width)
{
      label *label_ptr = new label(disp_.video(), text_widget_label, message_font_size, font::NORMAL_COLOR, false);
      const bool editable_textbox = std::find(text_widget_text.begin(),text_widget_text.end(),'\n') == text_widget_text.end();
      text_widget_ = new dialog_textbox(label_ptr, disp_.video(), text_box_width, text_widget_text, editable_textbox, text_widget_max_chars);
      text_widget_->set_wrap(!editable_textbox);
}

void dialog::set_menu(const std::vector<std::string> &menu_items, menu::sorter* sorter)
{
      set_menu(new gui::menu(disp_.video(), menu_items, (type_==MESSAGE),
            -1, dialog::max_menu_width, sorter, &menu::default_style, false));
}

void dialog::set_menu_items(const std::vector<std::string> &menu_items)
{
      if(menu_ == empty_menu) {
            set_menu(menu_items);
      } else {
            menu_->set_items(menu_items);
            menu_->reset_selection();

            for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
                  (**i).set_selection(menu_->selection());
            }
      }
}

menu& dialog::get_menu()
{
      if(menu_ == NULL)
      {
            if(empty_menu == NULL) {
                  empty_menu = new gui::menu(disp_.video(),empty_string_vector,false,-1,-1,NULL,&menu::simple_style);
                  empty_menu->leave();
            }
            menu_ = empty_menu; //no menu, so fake it
      }
      return *menu_;
}

int dialog::show(int xloc, int yloc)
{
      layout(xloc, yloc);
      return show();
}

int dialog::show()
{
    if (disp_.video().faked()) return CLOSE_DIALOG;

      if(disp_.video().update_locked()) {
            ERR_DP << "display locked ignoring dialog '" << title_ << "' '" << message_->get_text() << "'\n";
            return CLOSE_DIALOG;
      }

      LOG_DP << "showing dialog '" << title_ << "' '" << message_->get_text() << "'\n";
      if(dim_.interior == empty_rect) { layout(); }

      //create the event context, remember to instruct any passed-in widgets to join it
      const events::event_context dialog_events_context;
      const dialog_manager manager;
      const resize_lock prevent_resizing;

      //draw
      draw_frame();
      update_widget_positions();
      draw_contents();

      //process
      dialog_process_info dp_info;
      do
      {
            events::pump();
            set_result(process(dp_info));
            if(!done()) {
                  refresh();
            }
            action(dp_info);
            dp_info.cycle();
      } while(!done());

      clear_background();
      return result();
}

void dialog::draw_contents()
{
      if(!preview_panes_.empty()) {
            for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
                  preview_pane *pane = *i;
                  if(!pane->handler_members().empty())
                  {
                        pane->draw();
                        pane->needs_restore_ = false; //prevent panes from drawing over members
                  }
            }
      }
      events::raise_draw_event(); //draw widgets

      disp_.flip();
      disp_.invalidate_all();
}

dialog_frame& dialog::get_frame()
{
      if(frame_ == NULL) {
            CVideo& screen = disp_.video();
            frame_buttons_.clear();
            for(button_iterator b = standard_buttons_.begin(); b != standard_buttons_.end(); ++b)
            {
                  frame_buttons_.push_back(*b);
            }
            frame_ = new dialog_frame(screen, title_, style_,  true, &frame_buttons_, help_button_);
      }
      return *frame_;
}

void dialog::clear_background() {
      delete frame_;
      frame_ = NULL;
}

void dialog::draw_frame()
{
      get_frame().draw();
}

void dialog::update_widget_positions()
{
      if(!preview_panes_.empty()) {
            for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
                  preview_pane *pane = *i;
                  pane->join();
                  pane->set_location(dim_.panes.find(pane)->second);
            }
      }
      if(text_widget_) {
            text_widget_->join();
            text_widget_->set_location(dim_.textbox);
            if(text_widget_->get_label()) {
                  text_widget_->get_label()->set_location(dim_.label_x, dim_.label_y);
            }
      }
      if(get_menu().height() > 0) {
            menu_->join();
            menu_->set_numeric_keypress_selection(text_widget_ == NULL);
            menu_->set_width( dim_.menu_width );
            menu_->set_max_width( dim_.menu_width ); //lock the menu width
            if(dim_.menu_height >= 0) {
                  menu_->set_max_height( dim_.menu_height );
            }
            menu_->set_location( dim_.menu_x, dim_.menu_y );
      }
      if(image_) {
            image_->join();
            image_->set_location(dim_.image_x, dim_.image_y);
            if(image_->caption()) {
                  image_->caption()->set_location(dim_.caption_x, dim_.caption_y);
            }
      }
      button_iterator b;
      for(b = extra_buttons_.begin(); b != extra_buttons_.end(); ++b) {
            dialog_button *btn = *b;
            btn->join();
            std::pair<int,int> coords = dim_.buttons.find(btn)->second;
            btn->set_location(coords.first, coords.second);
      }
      for(b = standard_buttons_.begin(); b != standard_buttons_.end(); ++b) {
            dialog_button *btn = *b;
            btn->join();
      }
      if(help_button_) {
            help_button_->join();
      }
      message_->set_location(dim_.message);
      message_->join();
}

void dialog::refresh()
{
      disp_.flip();
      disp_.delay(10);
}

dialog::dimension_measurements dialog::layout(int xloc, int yloc)
{
      CVideo& screen = disp_.video();
      surface const scr = screen.getSurface();

      dimension_measurements dim;
      dim.x = xloc;
      dim.y = yloc;

      const bool use_textbox = (text_widget_ != NULL);
      int text_widget_width = 0;
      int text_widget_height = 0;
      if(use_textbox) {
            const SDL_Rect& area = font::text_area(text_widget_->text(),message_font_size);
            dim.textbox.w = std::min<size_t>(screen.getx()/2,std::max<size_t>(area.w,text_widget_->width()));
            dim.textbox.h = std::min<size_t>(screen.gety()/2,std::max<size_t>(area.h,text_widget_->height()));
            text_widget_width = dim.textbox.w;
            text_widget_width += (text_widget_->get_label() == NULL) ? 0 : text_widget_->get_label()->width();
            text_widget_height = dim.textbox.h + message_font_size;
      }

      const bool use_menu = (get_menu().height() > 0);
      if(!message_->get_text().empty()) {
            dim.message.w = message_->width();
            dim.message.h = message_->height();
      }
      unsigned int caption_width = 0;
      unsigned int caption_height = 0;
      if (image_ != NULL && image_->caption() != NULL) {
            caption_width = image_->caption()->width();
            caption_height = image_->caption()->height();
      }

      int check_button_height = 0;
      int left_check_button_height = 0;
      const int button_height_padding = 5;

      for(button_pool_const_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
            dialog_button const *const btn = b->first;
            switch(b->second)
            {
            case BUTTON_EXTRA:
            case BUTTON_CHECKBOX:
                  check_button_height += btn->height() + button_height_padding;
                  break;
            case BUTTON_EXTRA_LEFT:
            case BUTTON_CHECKBOX_LEFT:
                  left_check_button_height += btn->height() + button_height_padding;
                  break;
            case BUTTON_STANDARD:
            default:
                  break;
            }
      }
      check_button_height = std::max<int>(check_button_height, left_check_button_height);

      size_t above_preview_pane_height = 0, above_left_preview_pane_width = 0, above_right_preview_pane_width = 0;
      size_t preview_pane_height = 0, left_preview_pane_width = 0, right_preview_pane_width = 0;
      if(!preview_panes_.empty()) {
            for(pp_const_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
                  preview_pane const *const pane = *i;
                  const SDL_Rect& rect = pane->location();
                  if(pane->show_above() == false) {
                        preview_pane_height = std::max<size_t>(rect.h,preview_pane_height);
                        if(pane->left_side()) {
                              left_preview_pane_width += rect.w;
                        } else {
                              right_preview_pane_width += rect.w;
                        }
                  } else {
                        above_preview_pane_height = std::max<size_t>(rect.h,above_preview_pane_height);
                        if(pane->left_side()) {
                              above_left_preview_pane_width += rect.w;
                        } else {
                              above_right_preview_pane_width += rect.w;
                        }
                  }
            }
      }

      const int menu_hpadding = font::relative_size((dim.message.h > 0 && use_menu) ? 10 : 0);
      const size_t image_h_padding = (image_ == NULL)? 0 : image_h_pad;
      const size_t padding_width = left_padding + right_padding + image_h_padding;
      const size_t padding_height = top_padding + bottom_padding + menu_hpadding;
      const size_t image_width = (image_ == NULL) ? 0 : image_->width();
      const size_t image_height = (image_ == NULL) ? 0 : image_->height();
      const size_t total_text_height = dim.message.h + caption_height;

      size_t text_width = dim.message.w;
      if(caption_width > text_width)
            text_width = caption_width;

      // Prevent the menu to be larger than the screen
      dim.menu_width = menu_->width();
      if(dim.menu_width + image_width + padding_width + left_preview_pane_width + right_preview_pane_width > static_cast<size_t>(scr->w))
            dim.menu_width = scr->w - image_width - padding_width - left_preview_pane_width - right_preview_pane_width;
      if(dim.menu_width > text_width)
            text_width = dim.menu_width;


      size_t total_width = image_width + text_width + padding_width;

      if(text_widget_width+left_padding+right_padding > total_width)
            total_width = text_widget_width+left_padding+right_padding;

      //Prevent the menu from being too skinny
      if(use_menu && preview_panes_.empty() &&
            total_width > dim.menu_width + image_width + padding_width) {
            dim.menu_width = total_width - image_width - padding_width;
      }

      const size_t text_and_image_height = image_height > total_text_height ? image_height : total_text_height;

      const int total_height = text_and_image_height + padding_height + menu_->height() +
            text_widget_height + check_button_height;

      dim.interior.w = std::max<int>(total_width,above_left_preview_pane_width + above_right_preview_pane_width);
      dim.interior.h = std::max<int>(total_height,static_cast<int>(preview_pane_height));
      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);
      dim.interior.y = std::max<int>(0,dim.y >= 0 ? dim.y : scr->h/2 - (dim.interior.h + above_preview_pane_height)/2);

      DBG_DP << "above_preview_pane_height: " << above_preview_pane_height << "; "
            << "dim.interior.y: " << scr->h/2 << " - " << (dim.interior.h + above_preview_pane_height)/2 << " = "
            << dim.interior.y << "; " << "dim.interior.h: " << dim.interior.h << "\n";

      if(dim.x <= -1 || dim.y <= -1) {
            dim.x = dim.interior.x + left_preview_pane_width;
            dim.y = dim.interior.y + above_preview_pane_height;
      }

      if(dim.x + dim.interior.w > scr->w) {
            dim.x = scr->w - dim.interior.w;
            if(dim.x < dim.interior.x) {
                  dim.interior.x = dim.x;
            }
      }

      const int frame_top_pad = get_frame().top_padding();
      const int frame_bottom_pad = get_frame().bottom_padding();
      if(dim.y + dim.interior.h + frame_bottom_pad > scr->h) {
            dim.y = std::max<int>(frame_top_pad, scr->h - dim.interior.h - frame_bottom_pad);
            if(dim.y < dim.interior.y) {
                  dim.interior.y = dim.y;
            }
      }

      dim.interior.w += left_preview_pane_width + right_preview_pane_width;
      dim.interior.h += above_preview_pane_height;

      const int max_height = scr->h - dim.interior.y - frame_bottom_pad;
      if(dim.interior.h > max_height) {
            //try to rein in the menu height a little bit
            const int menu_height = menu_->height();
            if(menu_height > 0) {
                  dim.menu_height = std::max<int>(1, max_height - dim.interior.h + menu_height);
                  dim.interior.h -= menu_height - dim.menu_height;
            }
      }

      //calculate the positions of the preview panes to the sides of the dialog
      if(!preview_panes_.empty()) {

            int left_preview_pane = dim.interior.x;
            int right_preview_pane = dim.interior.x + total_width + left_preview_pane_width;
            int above_left_preview_pane = dim.interior.x + dim.interior.w/2;
            int above_right_preview_pane = above_left_preview_pane;

            for(pp_const_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
            preview_pane const *const pane = *i;
                  SDL_Rect area = pane->location();

                  if(pane->show_above() == false) {
                        area.y = dim.y;
                        area.h = dim.interior.h;
                        if(pane->left_side()) {
                              area.x = left_preview_pane;
                              left_preview_pane += area.w;
                        } else {
                              area.x = right_preview_pane;
                              right_preview_pane += area.w;
                        }
                  } else {
                        area.y = dim.interior.y;
                        area.h = above_preview_pane_height;
                        if(pane->left_side()) {
                              area.x = above_left_preview_pane - area.w;
                              above_left_preview_pane -= area.w;
                        } else {
                              area.x = above_right_preview_pane;
                              above_right_preview_pane += area.w;
                        }
                  }
                  dim.panes[*i] = area;
            }
      }

      const int text_widget_y = dim.y+top_padding+text_and_image_height-6+menu_hpadding;

      if(use_textbox) {
            dim.textbox.x = dim.x + left_padding + text_widget_width - dim.textbox.w;
            dim.textbox.y = text_widget_y + (text_widget_height - dim.textbox.h)/2;
            dim.label_x = dim.x+left_padding;
            dim.label_y = dim.textbox.y;
      }

      dim.menu_x = dim.x+image_width+left_padding+image_h_padding;
      dim.menu_y = dim.y+top_padding+text_and_image_height+menu_hpadding+ (use_textbox ? text_widget_->location().h + top_padding : 0);

      dim.message.x = dim.x + left_padding;
      dim.message.y = dim.y + top_padding + caption_height;

      if(image_ != NULL) {
            const int x = dim.x + left_padding;
            const int y = dim.y + top_padding;
            dim.message.x += image_width + image_h_padding;
            dim.image_x = x;
            dim.image_y = y;
            dim.caption_x = dim.x + image_width + left_padding + image_h_padding;
            dim.caption_y = dim.y + top_padding;
      }

      //set the position of any tick boxes. by default, they go right below the menu,
      //slammed against the right side of the dialog
      if(extra_buttons_.empty() == false) {
            int options_y = text_widget_y + text_widget_height + menu_->height() + button_height_padding + menu_hpadding;
            int options_left_y = options_y;
            for(button_pool_const_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
            dialog_button const *const btn = b->first;
            std::pair<int,int> coords;
                  switch(b->second)
                  {
                  case BUTTON_EXTRA:
                  case BUTTON_CHECKBOX:
                        coords.first = dim.x + total_width - btn->width() - ButtonHPadding;
                        coords.second = options_y;
                        dim.buttons[b->first] = coords;
                        options_y += btn->height() + button_height_padding;
                        break;
                  case BUTTON_EXTRA_LEFT:
                  case BUTTON_CHECKBOX_LEFT:
                        coords.first = dim.x + ButtonHPadding;
                        coords.second = options_left_y;
                        dim.buttons[b->first] = coords;
                        options_left_y += btn->height() + button_height_padding;
                        break;
                  case BUTTON_STANDARD:
                  default:
                        break;
                  }
            }
      }
      set_layout(dim);
      return dim;
}

void dialog::set_layout(dimension_measurements &new_dim) {
      get_frame().layout(new_dim.interior);
      dim_ = new_dim;
}


int dialog::process(dialog_process_info &info)
{

      int mousex, mousey;
      int mouse_flags = SDL_GetMouseState(&mousex,&mousey);

      info.new_right_button = (mouse_flags&SDL_BUTTON_RMASK) != 0;
      info.new_left_button = (mouse_flags&SDL_BUTTON_LMASK) != 0;
      info.new_key_down = info.key[SDLK_SPACE] || info.key[SDLK_RETURN] ||
                              info.key[SDLK_ESCAPE] || info.key[SDLK_KP_ENTER];
      info.double_clicked = menu_->double_clicked();
      get_menu();
      const bool use_menu = (menu_ != empty_menu);
      const bool use_text_input = (text_widget_!=NULL);
      const bool has_input = (use_menu||use_text_input);//input of any sort has to be made

      if((((!info.key_down && (info.key[SDLK_RETURN] || info.key[SDLK_KP_ENTER])) || info.double_clicked) &&
         (type_ == YES_NO || type_ == OK_CANCEL || type_ == OK_ONLY || type_ == CLOSE_ONLY))) {

            return (use_menu ? menu_->selection() : 0);
      }

      //escape quits from the dialog -- unless it's an "ok" dialog with input
      if(!info.key_down && info.key[SDLK_ESCAPE] && !(type_ == OK_ONLY && has_input)) {
            return (CLOSE_DIALOG);
      }

      //inform preview panes when there is a new menu selection
      if((menu_->selection() != info.selection) || info.first_time) {
            info.selection = menu_->selection();
            int selection = info.selection;
            if(selection < 0) {
                  selection = 0;
            }
            if(!preview_panes_.empty()) {
                  for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
                        (**i).set_selection(selection);
                        if(info.first_time) {
                              (**i).set_dirty();
                        }
                  }
            }
      }

      info.first_time = false;

      if(use_menu) {
            //get any drop-down choice or context-menu click
            const int selection = menu_->process();
            if(selection != -1)
            {
                  return (selection);
            }
      }

      events::raise_process_event();
      events::raise_draw_event();

      //left-clicking outside of a drop-down or context-menu should close it
      if (info.new_left_button && !info.left_button) {
            if (standard_buttons_.empty() && !point_in_rect(mousex,mousey, menu_->location())) {
                  if (use_menu)
                        sound::play_UI_sound(game_config::sounds::button_press);
                  return CLOSE_DIALOG;
                  }
      }

      //right-clicking outside of a dialog should close it unless a choice is required
      //note: this will also close any context-menu or drop-down when it is right-clicked
      //      but that may be changed to allow right-click selection instead.
      if (info.new_right_button && !info.right_button) {
            if( standard_buttons_.empty()
            || (!point_in_rect(mousex,mousey,get_frame().get_layout().exterior)
            && type_ != YES_NO && !(type_ == OK_ONLY && has_input))) {
                  sound::play_UI_sound(game_config::sounds::button_press);
                  return CLOSE_DIALOG;
            }
      }

      //any keypress should close a dialog if it has one standard button (or less)
      //and no menu options.
      if (info.new_key_down && !info.key_down) {
            if (standard_buttons_.size() < 2 && !has_input)
                  return CLOSE_DIALOG;
      }

      //now handle any button presses
      for(button_pool_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
            if(b->first->pressed()) {
                  return b->first->action(info);
            }
      }

      return CONTINUE_DIALOG;
}

int dialog_button::action(dialog_process_info &info) {
      if(handler_ != NULL) {
            menu &menu_ref = parent_->get_menu();
            dialog_button_action::RESULT res = handler_->button_pressed(menu_ref.selection());

            if(res == DELETE_ITEM || res == CLOSE_DIALOG) {
                  return res;
            }

            //reset button-tracking flags so that if the action displays a dialog, a button-press
            //at the end of the dialog won't be mistaken for a button-press in this dialog.
            //(We should eventually use a proper event-handling system instead of tracking
            //flags to avoid problems like this altogether).
            info.clear_buttons();
            return CONTINUE_DIALOG;
      }
      return simple_result_;
}

void dialog::action(dialog_process_info& info)
{
      //default way of handling a "delete item" request
      if(result() == DELETE_ITEM) {
            menu &menu_ref = get_menu();
            const int selection = menu_ref.selection();
            if(selection >= 0) {
                  menu_ref.erase_item(selection);
            }
            // was used before to auto close empty menu
            //if(menu_ref.nitems() == 0) {
            //    set_result(CLOSE_DIALOG);
            //} else {

            set_result(CONTINUE_DIALOG);
                        info.first_time = true;

      }
}

int standard_dialog_button::action(dialog_process_info &/*info*/) {
      //if the menu is not used, then return the index of the
      //button pressed, otherwise return the index of the menu
      //item selected if the last button is not pressed, and
      //cancel (-1) otherwise
      if(dialog()->get_menu().height() <= 0) {
            return simple_result_;
      } else if((simple_result_ == 0 && is_last_) || !is_last_) {
            return (dialog()->get_menu().selection());
      }
      return CLOSE_DIALOG;
}

void dialog::set_image(surface surf, const std::string &caption)
{
      label *label_ptr = NULL;
      if(!caption.empty()) {
            label_ptr = new label(disp_.video(), caption, caption_font_size, font::NORMAL_COLOR, false);
      }
      set_image( new dialog_image(label_ptr, disp_.video(), surf ));
}

void dialog_image::draw_contents()
{
      video().blit_surface(location().x, location().y, surf_);
}

int filter_textbox::get_index(int selection) const {
      // don't translate special values
      if(selection < 0) {
            return selection;
      }
      //we must the header row value to the index to ignore this row and
      //then subtract it from the result to return the index not including
      //the possible header row.

      if (size_t(selection+header_row_) >= index_map_.size()) {
            return -1; // bad index, cancel
      }

      return index_map_[selection+header_row_]-header_row_;
}

void filter_textbox::delete_item(int selection) {
      // use the real selection
      size_t adjusted_selection = selection + header_row_;

      if (adjusted_selection >= index_map_.size())
            return;

      filtered_items_.erase(filtered_items_.begin() + adjusted_selection);
      items_to_filter_.erase(items_to_filter_.begin() + index_map_[adjusted_selection]);
      items_.erase(items_.begin() + index_map_[adjusted_selection]);
      index_map_.erase(index_map_.begin() + adjusted_selection);

      // don't forget to also shift the next index values
      // this assume that index_map_and items_ have the same order
      for(size_t i = adjusted_selection; i < index_map_.size(); ++i) {
            index_map_[i] = index_map_[i]-1;
      }

      //for now, assume the dialog menu item is deleted using DELETE_ITEM
      /* dialog_.set_menu_items(filtered_items_); */
}

void filter_textbox::handle_text_changed(const wide_string& text) {
      const std::vector<std::string> words = utils::split(utils::wstring_to_string(text),' ');
      if (words == last_words)
            return;
      last_words = words;

      filtered_items_.clear();
      index_map_.clear();

      if(header_row_ == 1) {
                  filtered_items_.push_back(items_[0]);
                  index_map_.push_back(0);
      }

      // we keep all items containing each word
      for(size_t n = header_row_; n < items_to_filter_.size(); ++n) {
            std::vector<std::string>::const_iterator w = words.begin();
            for(; w != words.end(); ++w)
            {
                  if (std::search(items_to_filter_[n].begin(), items_to_filter_[n].end(),
                                    w->begin(), w->end(),
                                    chars_equal_insensitive) == items_to_filter_[n].end())
                        break; // one word doesn't match, we don't reach words.end()
            }
            if (w == words.end()) {
                  // all words have matched, keep the item
                  filtered_items_.push_back(items_[n]);
                  index_map_.push_back(n);
            }
      }

      dialog_.set_menu_items(filtered_items_);
}

int message_dialog::show(msecs minimum_lifetime)
{
      prevent_misclick_until_ = SDL_GetTicks() + minimum_lifetime;
      return dialog::show();
}

void message_dialog::action(gui::dialog_process_info &dp_info)
{
      dialog::action(dp_info);
      if(done() && SDL_GetTicks() < prevent_misclick_until_ && result() != gui::ESCAPE_DIALOG) {
            //discard premature results
            set_result(gui::CONTINUE_DIALOG);
      }
}

message_dialog::~message_dialog()
{
}

}//end namespace gui

Generated by  Doxygen 1.6.0   Back to index