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

construct_dialog.cpp

/* $Id: construct_dialog.cpp 40489 2010-01-01 13:16:49Z mordante $ */
/*
   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 version 2
   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_COLOUR, 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_COLOUR, 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_COLOUR, 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