Logo Search packages:      
Sourcecode: gambit version File versions  Download package

dlnfglogit.cc

//
// $Source: /cvsroot/gambit/gambit/sources/gui/dlnfglogit.cc,v $
// $Date: 2006/01/19 20:25:11 $
// $Revision: 1.17 $
//
// DESCRIPTION:
// Dialog for monitoring progress of logit equilibrium computation
//
// This file is part of Gambit
// Copyright (c) 2005, The Gambit Project
//
// 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; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

#include <fstream>

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif  // WX_PRECOMP
#include <wx/txtstrm.h>
#include <wx/tokenzr.h>
#include <wx/process.h>
#include <wx/print.h>

#include "wx/sheet/sheet.h"
#include "wx/plotctrl/plotctrl.h"
#include "wx/wxthings/spinctld.h"   // for wxSpinCtrlDbl

#include "gamedoc.h"
#include "menuconst.h"            // for tool IDs

using namespace Gambit;


// Use an anonymous namespace to encapsulate the helper classes

namespace {

//========================================================================
//                    class LogitMixedBranch
//========================================================================

/// Represents one branch of a logit equilibrium correspondence
class LogitMixedBranch {
private:
  gbtGameDocument *m_doc;
  List<double> m_lambdas;
  List<MixedStrategyProfile<double> > m_profiles;

public:
  LogitMixedBranch(gbtGameDocument *p_doc) : m_doc(p_doc) { }

  void AddProfile(const wxString &p_text);

  int NumPoints(void) const { return m_lambdas.Length(); }
  double GetLambda(int p_index) const { return m_lambdas[p_index]; }
  const List<double> &GetLambdas(void) const { return m_lambdas; }
  const MixedStrategyProfile<double> &GetProfile(int p_index)
  { return m_profiles[p_index]; }
  const List<MixedStrategyProfile<double> > &GetProfiles(void) const 
  { return m_profiles; }
};
  
void LogitMixedBranch::AddProfile(const wxString &p_text)
{
  MixedStrategyProfile<double> profile(m_doc->GetGame());

  wxStringTokenizer tok(p_text, wxT(","));

  m_lambdas.Append((double) ToNumber(std::string((const char *) tok.GetNextToken().mb_str())));

  for (int i = 1; i <= profile.Length(); i++) {
    profile[i] = ToNumber(std::string((const char *) tok.GetNextToken().mb_str()));
  }

  m_profiles.Append(profile);
}

//========================================================================
//                      class LogitMixedSheet
//========================================================================

class LogitMixedSheet : public wxSheet {
private:
  gbtGameDocument *m_doc;
  LogitMixedBranch &m_branch;

  // Overriding wxSheet members for data access
  wxString GetCellValue(const wxSheetCoords &);
  wxSheetCellAttr GetAttr(const wxSheetCoords &p_coords, wxSheetAttr_Type) const;
  
  // Overriding wxSheet members to disable selection behavior
  bool SelectRow(int, bool = false, bool = false)
    { return false; }
  bool SelectRows(int, int, bool = false, bool = false)
    { return false; }
  bool SelectCol(int, bool = false, bool = false)
    { return false; }
  bool SelectCols(int, int, bool = false, bool = false)
    { return false; }
  bool SelectCell(const wxSheetCoords&, bool = false, bool = false)
    { return false; }
  bool SelectBlock(const wxSheetBlock&, bool = false, bool = false)
    { return false; }
  bool SelectAll(bool = false) { return false; }

  // Overriding wxSheet member to suppress drawing of cursor
  void DrawCursorCellHighlight(wxDC&, const wxSheetCellAttr &) { }

public:
  LogitMixedSheet(wxWindow *p_parent, gbtGameDocument *p_doc,
                 LogitMixedBranch &p_branch);
  virtual ~LogitMixedSheet();
};

LogitMixedSheet::LogitMixedSheet(wxWindow *p_parent, 
                               gbtGameDocument *p_doc,
                               LogitMixedBranch &p_branch) 
  : wxSheet(p_parent, -1), m_doc(p_doc), m_branch(p_branch)
{
  CreateGrid(p_branch.NumPoints(), p_doc->GetGame()->MixedProfileLength()+1);
  SetRowLabelWidth(40);
  SetColLabelHeight(25);
}

LogitMixedSheet::~LogitMixedSheet()
{ }

wxString LogitMixedSheet::GetCellValue(const wxSheetCoords &p_coords)
{
  if (!m_doc->GetGame())  return wxT("");

  if (IsRowLabelCell(p_coords)) {
    return wxString::Format(wxT("%d"), p_coords.GetRow() + 1);
  }
  else if (IsColLabelCell(p_coords)) {
    if (p_coords.GetCol() == 0) {
      return wxT("Lambda");
    }
    else {
      int index = 1;
      for (int pl = 1; pl <= m_doc->GetGame()->NumPlayers(); pl++) {
      GamePlayer player = m_doc->GetGame()->GetPlayer(pl);
      for (int st = 1; st <= player->NumStrategies(); st++) {
        if (index++ == p_coords.GetCol()) {
          return (wxString::Format(wxT("%d: "), pl) +
                wxString(player->GetStrategy(st)->GetLabel().c_str(),
                      *wxConvCurrent));
        }
      }
      }
      return wxT("");
    }
  }
  else if (IsCornerLabelCell(p_coords)) {
    return wxT("#");
  }

  if (p_coords.GetCol() == 0) {
    return wxString(ToText(m_branch.GetLambda(p_coords.GetRow()+1),
                     m_doc->GetStyle().NumDecimals()).c_str(),
                *wxConvCurrent);
  }
  else {
    const MixedStrategyProfile<double> &profile = 
      m_branch.GetProfile(p_coords.GetRow()+1);
    return wxString(ToText(profile[p_coords.GetCol()],
                           m_doc->GetStyle().NumDecimals()).c_str(), 
                *wxConvCurrent);
  }
}


static wxColour GetPlayerColor(gbtGameDocument *p_doc, int p_index)
{
  if (!p_doc->GetGame())  return *wxBLACK;

  int index = 1;
  for (int pl = 1; pl <= p_doc->GetGame()->NumPlayers(); pl++) {
    GamePlayer player = p_doc->GetGame()->GetPlayer(pl);
    for (int st = 1; st <= player->NumStrategies(); st++) {
      if (index++ == p_index) {
      return p_doc->GetStyle().GetPlayerColor(pl);
      }
    }
  }
  return *wxBLACK;
}

wxSheetCellAttr LogitMixedSheet::GetAttr(const wxSheetCoords &p_coords, 
                               wxSheetAttr_Type) const
{
  if (IsRowLabelCell(p_coords)) {
    wxSheetCellAttr attr(GetSheetRefData()->m_defaultRowLabelAttr);
    attr.SetFont(wxFont(10, wxSWISS, wxNORMAL, wxBOLD));
    attr.SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER);
    attr.SetOrientation(wxHORIZONTAL);
    attr.SetReadOnly(true);
    return attr;
  }
  else if (IsColLabelCell(p_coords)) {
    wxSheetCellAttr attr(GetSheetRefData()->m_defaultColLabelAttr);
    attr.SetFont(wxFont(10, wxSWISS, wxNORMAL, wxBOLD));
    attr.SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER);
    attr.SetOrientation(wxHORIZONTAL);
    attr.SetReadOnly(true);
    attr.SetForegroundColour(GetPlayerColor(m_doc, p_coords.GetCol()));
    return attr;
  }
  else if (IsCornerLabelCell(p_coords)) {
    return GetSheetRefData()->m_defaultCornerLabelAttr;
  }

  wxSheetCellAttr attr(GetSheetRefData()->m_defaultGridCellAttr);
  attr.SetFont(wxFont(10, wxSWISS, wxNORMAL, wxNORMAL));
  attr.SetAlignment(wxALIGN_RIGHT, wxALIGN_CENTER);
  attr.SetOrientation(wxHORIZONTAL);
  attr.SetForegroundColour(GetPlayerColor(m_doc, p_coords.GetCol()));
  attr.SetReadOnly(true);
  return attr;
}

class LogitBranchDialog : public wxDialog {
private:
  LogitMixedSheet *m_sheet;

public:
  LogitBranchDialog(wxWindow *p_parent,
                   gbtGameDocument *p_doc,
                   LogitMixedBranch &p_branch);
};

LogitBranchDialog::LogitBranchDialog(wxWindow *p_parent,
                                 gbtGameDocument *p_doc,
                                 LogitMixedBranch &p_branch)
  : wxDialog(p_parent, wxID_ANY, wxT("Logit equilibrium correspondence"),
           wxDefaultPosition)
{
  m_sheet = new LogitMixedSheet(this, p_doc, p_branch);
  m_sheet->AutoSizeCol(0);

  wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
  sizer->Add(m_sheet, 0, wxALL, 5);
  sizer->Add(CreateButtonSizer(wxOK), 0, wxALL | wxEXPAND, 5);

  SetSizer(sizer);
  sizer->Fit(this);
  sizer->SetSizeHints(this);
  Layout();
  CenterOnParent();
}


//========================================================================
//                      class gbtLogitPlotCtrl
//========================================================================

class gbtLogitPlotCtrl : public wxPlotCtrl {
private:
  gbtGameDocument *m_doc;
  double m_scaleFactor;

  /// Overriding x (lambda) axis labeling
  void CalcXAxisTickPositions(void);
  
public:
  gbtLogitPlotCtrl(wxWindow *p_parent, gbtGameDocument *p_doc);

  double LambdaToX(double p_lambda)
  { return m_scaleFactor*p_lambda / (1.0 + m_scaleFactor*p_lambda); }
  double XToLambda(double p_x)
  { return p_x / (m_scaleFactor * (1.0 - p_x)); }

  void SetScaleFactor(double p_scale) { m_scaleFactor = p_scale; }
};

gbtLogitPlotCtrl::gbtLogitPlotCtrl(wxWindow *p_parent, 
                           gbtGameDocument *p_doc)
  : wxPlotCtrl(p_parent), m_doc(p_doc), m_scaleFactor(1.0)
{
  SetAxisLabelColour(*wxBLUE);
  wxFont labelFont(8, wxSWISS, wxNORMAL, wxBOLD);
  SetAxisLabelFont(labelFont);
  SetAxisColour(*wxBLUE);
  SetAxisFont(labelFont);
  SetDrawSymbols(false);

  // SetAxisFont resets the width of the y axis labels, assuming
  // a fairly long label.
  int x=6, y=12, descent=0, leading=0;
  GetTextExtent(wxT("0.88"), &x, &y, &descent, &leading, &labelFont);
  m_y_axis_text_width = x + leading;

  SetXAxisLabel(wxT("Lambda"));
  SetShowXAxisLabel(true);
  SetYAxisLabel(wxT("Probability"));
  SetShowYAxisLabel(true);

  SetShowKey(true);

  m_xAxisTick_step = 0.2;
  SetViewRect(wxRect2DDouble(0, 0, 1, 1));
} 

//
// This differs from the wxPlotWindow original only by the use of
// XToLambda() to construct the tick labels.
//
void gbtLogitPlotCtrl::CalcXAxisTickPositions(void)
{
  double current = ceil(m_viewRect.GetLeft() / m_xAxisTick_step) * m_xAxisTick_step;
  m_xAxisTicks.Clear();
  m_xAxisTickLabels.Clear();
  int i, x, windowWidth = GetPlotAreaRect().width;
  for (i=0; i<m_xAxisTick_count; i++) {
    if (!IsFinite(current, wxT("axis label is not finite"))) return;
                
    x = GetClientCoordFromPlotX( current );
            
    if ((x >= -1) && (x < windowWidth+2)) {
      m_xAxisTicks.Add(x);
      m_xAxisTickLabels.Add(wxString::Format(m_xAxisTickFormat.c_str(), 
                                   XToLambda(current)));
    }

    current += m_xAxisTick_step;
  }
}


//========================================================================
//                     class gbtLogitPlotStrategyList
//========================================================================

class gbtLogitPlotStrategyList : public wxSheet {
private:
  gbtGameDocument *m_doc;

  //!
  //! @name Overriding wxSheet members to disable selection behavior
  //!
  //@{
  bool SelectRow(int, bool = false, bool = false)
    { return false; }
  bool SelectRows(int, int, bool = false, bool = false)
    { return false; }
  bool SelectCol(int, bool = false, bool = false)
    { return false; }
  bool SelectCols(int, int, bool = false, bool = false)
    { return false; }
  bool SelectCell(const wxSheetCoords&, bool = false, bool = false)
    { return false; }
  bool SelectBlock(const wxSheetBlock&, bool = false, bool = false)
    { return false; }
  bool SelectAll(bool = false) { return false; }

  bool HasSelection(bool = true) const { return false; }
  bool IsCellSelected(const wxSheetCoords &) const { return false; }
  bool IsRowSelected(int) const { return false; }
  bool IsColSelected(int) const { return false; }
  bool DeselectBlock(const wxSheetBlock &, bool = false) { return false; }
  bool ClearSelection(bool = false) { return false; }
  //@}

  /// Overriding wxSheet member to suppress drawing of cursor
  void DrawCursorCellHighlight(wxDC&, const wxSheetCellAttr &) { }

  // Event handlers
  // This disables moving the (unseen) cursor
  void OnLeftDown(wxSheetEvent &) { }
  void OnLeftUp(wxSheetEvent &);

public:
  gbtLogitPlotStrategyList(wxWindow *p_parent, gbtGameDocument *p_doc);

  bool IsShown(int p_index)
  { return GetCellValue(wxSheetCoords(p_index-1, 2)) == wxT("1"); }
};

gbtLogitPlotStrategyList::gbtLogitPlotStrategyList(wxWindow *p_parent,
                                       gbtGameDocument *p_doc)
  : wxSheet(p_parent, wxID_ANY), m_doc(p_doc)
{
  CreateGrid(m_doc->GetGame()->MixedProfileLength(), 3);

  SetRowLabelWidth(0);
  SetColLabelHeight(0);
  SetGridLineColour(*wxWHITE);

  for (int st = 1; st <= m_doc->GetGame()->MixedProfileLength(); st++) {
    GameStrategy strategy = m_doc->GetGame()->GetStrategy(st);
    GamePlayer player = strategy->GetPlayer();
    wxColour color = m_doc->GetStyle().GetPlayerColor(player->GetNumber());

    if (strategy->GetNumber() == 1) {
      SetCellSpan(wxSheetCoords(st-1, 0),
              wxSheetCoords(player->NumStrategies(), 1));
      SetCellValue(wxSheetCoords(st-1, 0),
               wxString(player->GetLabel().c_str(), *wxConvCurrent));
      SetAttrForegroundColour(wxSheetCoords(st-1, 0), color);
      SetAttrAlignment(wxSheetCoords(st-1, 0),
                   wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
    }
    

    SetCellValue(wxSheetCoords(st-1, 1),
             wxString(strategy->GetLabel().c_str(), *wxConvCurrent));
    SetAttrForegroundColour(wxSheetCoords(st-1, 1), color);

    SetCellValue(wxSheetCoords(st-1, 2), wxT("1"));
    SetAttrForegroundColour(wxSheetCoords(st-1, 2), color);
    SetAttrRenderer(wxSheetCoords(st-1, 2),
                wxSheetCellRenderer(new wxSheetCellBoolRendererRefData()));
    SetAttrEditor(wxSheetCoords(st-1, 2),
              wxSheetCellEditor(new wxSheetCellBoolEditorRefData()));
  }

  AutoSizeCols();

  Connect(GetId(), wxEVT_SHEET_CELL_LEFT_DOWN,
        (wxObjectEventFunction) (wxEventFunction) wxStaticCastEvent(wxSheetEventFunction, wxSheetEventFunction(&gbtLogitPlotStrategyList::OnLeftDown)));

  Connect(GetId(), wxEVT_SHEET_CELL_LEFT_UP,
        (wxObjectEventFunction) (wxEventFunction) wxStaticCastEvent(wxSheetEventFunction, wxSheetEventFunction(&gbtLogitPlotStrategyList::OnLeftUp)));
}

void gbtLogitPlotStrategyList::OnLeftUp(wxSheetEvent &p_event)
{
  if (p_event.GetCoords().GetCol() != 2) {
    return;
  }

  if (GetCellValue(p_event.GetCoords()) == wxT("1")) {
    SetCellValue(p_event.GetCoords(), wxT("0"));
  }
  else {
    SetCellValue(p_event.GetCoords(), wxT("1"));
  }

  // Allow normal processing -- parent window will want to update
  p_event.Skip();
}


//========================================================================
//                       class LogitPlotPanel
//========================================================================

class LogitPlotPanel : public wxPanel {
private:
  gbtGameDocument *m_doc;
  LogitMixedBranch m_branch;
  gbtLogitPlotStrategyList *m_plotStrategies;
  gbtLogitPlotCtrl *m_plotCtrl;

  // Event handlers
  void OnChangeStrategies(wxSheetEvent &) { Plot(); }

public:
  LogitPlotPanel(wxWindow *p_parent, gbtGameDocument *p_doc);

  void AddProfile(const wxString &p_text)
  { m_branch.AddProfile(p_text); }

  void SetScaleFactor(double p_scale);
  void FitZoom(void);
  void Plot(void);

  LogitMixedBranch &GetBranch(void) { return m_branch; }
  wxPlotCtrl *GetPlotCtrl(void) const { return m_plotCtrl; }
};

LogitPlotPanel::LogitPlotPanel(wxWindow *p_parent,
                             gbtGameDocument *p_doc)
  : wxPanel(p_parent, wxID_ANY), m_doc(p_doc), m_branch(p_doc)
{
  m_plotCtrl = new gbtLogitPlotCtrl(this, p_doc);
  m_plotCtrl->SetSizeHints(wxSize(600, 400));

  m_plotStrategies = new gbtLogitPlotStrategyList(this, p_doc);
  wxStaticBoxSizer *playerSizer =
    new wxStaticBoxSizer(wxHORIZONTAL, this, wxT("Show strategies"));
  playerSizer->Add(m_plotStrategies, 1, wxALL | wxEXPAND, 5);

  wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
  sizer->Add(playerSizer, 0, wxALL | wxEXPAND, 5);
  sizer->Add(m_plotCtrl, 0, wxALL, 5);

  Connect(m_plotStrategies->GetId(), wxEVT_SHEET_CELL_LEFT_UP,
        (wxObjectEventFunction) (wxEventFunction) wxStaticCastEvent(wxSheetEventFunction, wxSheetEventFunction(&LogitPlotPanel::OnChangeStrategies)));

  SetSizer(sizer);
  Layout();
}
                            
void LogitPlotPanel::Plot(void)
{
  if (m_branch.NumPoints() == 0)  return;

  m_plotCtrl->DeleteCurve(-1);

  for (int st = 1; st <= m_doc->GetGame()->MixedProfileLength(); st++) {
    if (!m_plotStrategies->IsShown(st)) continue;

    wxPlotData *curve = new wxPlotData(m_branch.NumPoints());

    GameStrategy strategy = m_doc->GetGame()->GetStrategy(st);
    GamePlayer player = strategy->GetPlayer();

    curve->SetFilename(wxString(player->GetLabel().c_str(), *wxConvCurrent) +
                   wxT(":") +
                   wxString(strategy->GetLabel().c_str(), *wxConvCurrent));
    
    for (int i = 0; i < m_branch.NumPoints(); i++) {
      curve->SetValue(i, m_plotCtrl->LambdaToX(m_branch.GetLambda(i+1)), 
                  m_branch.GetProfile(i+1)[st]);
    }

    curve->SetPen(wxPLOTPEN_NORMAL, 
              wxPen(m_doc->GetStyle().GetPlayerColor(player->GetNumber()),
                  1, wxSOLID));

    m_plotCtrl->AddCurve(curve, false);
  }
}

void LogitPlotPanel::SetScaleFactor(double p_scale)
{
  m_plotCtrl->SetScaleFactor(p_scale);
  Plot();
}

void LogitPlotPanel::FitZoom(void)
{
  m_plotCtrl->MakeCurveVisible(-1);
}

//========================================================================
//                       class LogitPrintout
//========================================================================

class LogitPrintout : public wxPrintout {
private:
  wxPlotCtrl *m_plot;
    
public:
  LogitPrintout(wxPlotCtrl *p_plot, const wxString &p_label)
    : wxPrintout(p_label), m_plot(p_plot) { }
  virtual ~LogitPrintout() { }

  bool OnPrintPage(int)
  { wxSize size = GetDC()->GetSize();
    m_plot->DrawWholePlot(GetDC(), wxRect(50, 50, 
                                size.GetWidth() - 100,
                                size.GetHeight() - 100));
    return true; 
  }
  bool HasPage(int page) { return (page <= 1); }
  void GetPageInfo(int *minPage, int *maxPage,
               int *selPageFrom, int *selPageTo)
  { *minPage = 1; *maxPage = 1; *selPageFrom = 1; *selPageTo = 1; }
};


//========================================================================
//                      class LogitMixedDialog
//========================================================================

class LogitMixedDialog : public wxDialog {
private:
  gbtGameDocument *m_doc;
  int m_pid;
  wxProcess *m_process;
  LogitPlotPanel *m_plot;
  wxSpinCtrlDbl *m_scaler;
  wxToolBar *m_toolBar;
  wxStaticText *m_statusText;
  wxButton *m_stopButton, *m_okButton;
  wxTimer m_timer;
  wxString m_output;

  void OnStop(wxCommandEvent &);
  void OnTimer(wxTimerEvent &);
  void OnIdle(wxIdleEvent &);
  void OnEndProcess(wxProcessEvent &);
  void OnSave(wxCommandEvent &);
  void OnPrint(wxCommandEvent &);

  void OnChangeScale(wxSpinEvent &);
  void OnZoomFit(wxCommandEvent &);
  void OnViewData(wxCommandEvent &);

  void Start(void);

public:
  LogitMixedDialog(wxWindow *p_parent, gbtGameDocument *p_doc);

  DECLARE_EVENT_TABLE()
};

const int GBT_ID_TIMER = 2000;
const int GBT_ID_PROCESS = 2001;
const int GBT_MENU_VIEW_DATA = 2002;

BEGIN_EVENT_TABLE(LogitMixedDialog, wxDialog)
  EVT_END_PROCESS(GBT_ID_PROCESS, LogitMixedDialog::OnEndProcess)
  EVT_IDLE(LogitMixedDialog::OnIdle)
  EVT_TIMER(GBT_ID_TIMER, LogitMixedDialog::OnTimer)
  EVT_MENU(wxID_SAVE, LogitMixedDialog::OnSave)
  EVT_MENU(wxID_PRINT, LogitMixedDialog::OnPrint)
  EVT_MENU(GBT_MENU_VIEW_ZOOMFIT, LogitMixedDialog::OnZoomFit)
  EVT_MENU(GBT_MENU_VIEW_DATA, LogitMixedDialog::OnViewData)
END_EVENT_TABLE()

#include "bitmaps/stop.xpm"
#include "bitmaps/print.xpm"
#include "bitmaps/savedata.xpm"
#include "bitmaps/datasrc.xpm"
#include "bitmaps/zoomfit.xpm"

LogitMixedDialog::LogitMixedDialog(wxWindow *p_parent, 
                           gbtGameDocument *p_doc)
  : wxDialog(p_parent, -1, wxT("Compute quantal response equilibria"),
           wxDefaultPosition),
    m_doc(p_doc), m_process(0), m_timer(this, GBT_ID_TIMER)
{
  wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);

  wxBoxSizer *startSizer = new wxBoxSizer(wxHORIZONTAL);

  m_statusText = new wxStaticText(this, wxID_STATIC,
                          wxT("The computation is currently in progress."));
  m_statusText->SetForegroundColour(*wxBLUE);
  startSizer->Add(m_statusText, 0, wxALL | wxALIGN_CENTER, 5);

  m_stopButton = new wxBitmapButton(this, wxID_CANCEL, wxBitmap(stop_xpm));
  m_stopButton->SetToolTip(_("Stop the computation"));
  startSizer->Add(m_stopButton, 0, wxALL | wxALIGN_CENTER, 5);
  Connect(wxID_CANCEL, wxEVT_COMMAND_BUTTON_CLICKED,
        wxCommandEventHandler(LogitMixedDialog::OnStop));

  sizer->Add(startSizer, 0, wxALL | wxALIGN_CENTER, 5);

  m_toolBar = new wxToolBar(this, wxID_ANY);
  m_toolBar->SetWindowStyle(wxTB_HORIZONTAL | wxTB_FLAT);
  m_toolBar->SetMargins(4, 4);
  m_toolBar->SetToolBitmapSize(wxSize(24, 24));

  m_toolBar->AddTool(wxID_SAVE, wxBitmap(savedata_xpm), wxNullBitmap, false,
                 -1, -1, 0,
                 _("Save the correspondence to a CSV file"),
                 _("Save the correspondence to a CSV file"));
  m_toolBar->EnableTool(wxID_SAVE, false);
  m_toolBar->AddTool(GBT_MENU_VIEW_DATA, wxBitmap(datasrc_xpm),
                 wxNullBitmap, false, -1, -1, 0,
                 _("View the points in the correspondence"),
                 _("View the points in the correspondence"));
  m_toolBar->EnableTool(GBT_MENU_VIEW_DATA, false);

  m_toolBar->AddTool(wxID_PRINT, wxBitmap(print_xpm), wxNullBitmap, false,
                 -1, -1, 0, _("Print the graph"), _("Print the graph"));

  m_toolBar->AddSeparator();
  m_toolBar->AddTool(GBT_MENU_VIEW_ZOOMFIT, wxBitmap(zoomfit_xpm),
                 wxNullBitmap, false, -1, -1, 0,
                 _("Show the whole graph"), _("Show the whole graph"));

  m_toolBar->AddControl(new wxStaticText(m_toolBar, wxID_STATIC,
                               wxT("Graph scaling:")));
  m_scaler = new wxSpinCtrlDbl(*m_toolBar, wxID_ANY,
                         wxT(""),
                         wxDefaultPosition, wxDefaultSize,
                         0, 0.1, 10.0, 1.0, 0.1);
  m_toolBar->AddControl(m_scaler);
  Connect(m_scaler->GetId(), wxEVT_COMMAND_SPINCTRL_UPDATED,
        wxSpinEventHandler(LogitMixedDialog::OnChangeScale));

  m_toolBar->Realize();
  sizer->Add(m_toolBar, 0, wxALL | wxEXPAND, 5);


  wxBoxSizer *midSizer = new wxBoxSizer(wxHORIZONTAL);
  m_plot = new LogitPlotPanel(this, m_doc);
  midSizer->Add(m_plot, 0, wxALL | wxALIGN_CENTER, 5);

  sizer->Add(midSizer, 0, wxALL | wxALIGN_CENTER, 5);

  wxBoxSizer *buttonSizer = new wxBoxSizer(wxHORIZONTAL);
  m_okButton = new wxButton(this, wxID_OK, wxT("OK"));
  buttonSizer->Add(m_okButton, 0, wxALL | wxALIGN_CENTER, 5);
  m_okButton->Enable(false);

  sizer->Add(buttonSizer, 0, wxALL | wxALIGN_RIGHT, 5);

  SetSizer(sizer);
  sizer->Fit(this);
  sizer->SetSizeHints(this);
  Layout();
  CenterOnParent();
  Start();
}

void LogitMixedDialog::Start(void)
{
  m_doc->BuildNfg();

  m_process = new wxProcess(this, GBT_ID_PROCESS);
  m_process->Redirect();

  m_pid = wxExecute(wxT("gambit-logit -S"), wxEXEC_ASYNC, m_process);
  
  std::ostringstream s;
  m_doc->GetGame()->WriteNfgFile(s);
  wxString str(wxString(s.str().c_str(), *wxConvCurrent));
  
  // It is possible that the whole string won't write on one go, so
  // we should take this possibility into account.  If the write doesn't
  // complete the whole way, we take a 100-millisecond siesta and try
  // again.  (This seems to primarily be an issue with -- you guessed it --
  // Windows!)
  while (str.length() > 0) {
    wxTextOutputStream os(*m_process->GetOutputStream());

    // It appears that (at least with mingw) the string itself contains
    // only '\n' for newlines.  If we don't SetMode here, these get
    // converted to '\r\n' sequences, and so the number of characters
    // LastWrite() returns does not match the number of characters in
    // our string.  Setting this explicitly solves this problem.
    os.SetMode(wxEOL_UNIX);
    os.WriteString(str);
    str.Remove(0, m_process->GetOutputStream()->LastWrite());
    wxMilliSleep(100);
  }
  m_process->CloseOutput();

  m_timer.Start(1000, false);
}

void LogitMixedDialog::OnIdle(wxIdleEvent &p_event)
{
  if (!m_process)  return;

  if (m_process->IsInputAvailable()) {
    wxTextInputStream tis(*m_process->GetInputStream());

    wxString msg;
    msg << tis.ReadLine();
    m_plot->AddProfile(msg);
    //m_mixedList->AddProfile(msg, false);
    m_output += msg;
    m_output += wxT("\n");

    p_event.RequestMore();
  }
  else {
    m_timer.Start(1000, false);
  }
}

void LogitMixedDialog::OnTimer(wxTimerEvent &p_event)
{
  wxWakeUpIdle();
}

void LogitMixedDialog::OnEndProcess(wxProcessEvent &p_event)
{
  m_stopButton->Enable(false);
  m_timer.Stop();

  while (m_process->IsInputAvailable()) {
    wxTextInputStream tis(*m_process->GetInputStream());

    wxString msg;
    msg << tis.ReadLine();

    if (msg != wxT("")) {
      m_plot->AddProfile(msg);
      //m_mixedList->AddProfile(msg, true);
      m_output += msg;
      m_output += wxT("\n");
    }
  }

  if (p_event.GetExitCode() == 0) {
    m_statusText->SetLabel(wxT("The computation has completed."));
    m_statusText->SetForegroundColour(wxColour(0, 192, 0));
  }
  else {
    m_statusText->SetLabel(wxT("The computation ended abnormally."));
    m_statusText->SetForegroundColour(*wxRED);
  }

  m_okButton->Enable(true);
  m_toolBar->EnableTool(wxID_SAVE, true);
  m_toolBar->EnableTool(GBT_MENU_VIEW_DATA, true);
  m_plot->Plot();
}

void LogitMixedDialog::OnStop(wxCommandEvent &)
{
  // Per the wxWidgets wiki, under Windows, programs that run
  // without a console window don't respond to the more polite
  // SIGTERM, so instead we must be rude and SIGKILL it.
  m_stopButton->Enable(false);

#ifdef __WXMSW__
  wxProcess::Kill(m_pid, wxSIGKILL);
#else
  wxProcess::Kill(m_pid, wxSIGTERM);
#endif  // __WXMSW__
}

void LogitMixedDialog::OnSave(wxCommandEvent &)
{
  wxFileDialog dialog(this, _("Choose file"), wxT(""), wxT(""),
                  wxT("CSV files (*.csv)|*.csv|"
                    "All files (*.*)|*.*"),
                  wxSAVE | wxOVERWRITE_PROMPT);

  if (dialog.ShowModal() == wxID_OK) {
    std::ofstream file((const char *) dialog.GetPath().mb_str());
    file << ((const char *) m_output.mb_str());
  }
}

void LogitMixedDialog::OnPrint(wxCommandEvent &)
{
  wxPrintDialogData data;
  wxPrinter printer(&data);

  wxPrintout *printout = new LogitPrintout(m_plot->GetPlotCtrl(),
                                 wxT("Logit correspondence"));

  if (!printer.Print(this, printout, true)) {
    if (wxPrinter::GetLastError() == wxPRINTER_ERROR) {
      wxMessageBox(_("There was an error in printing"), _("Error"), wxOK);
    }
    // Otherwise, user hit "cancel"; just be quiet and return.
    return;
  }
}

void LogitMixedDialog::OnChangeScale(wxSpinEvent &)
{
  m_plot->SetScaleFactor(m_scaler->GetValue());
}

void LogitMixedDialog::OnZoomFit(wxCommandEvent &)
{
  m_plot->FitZoom();
}

void LogitMixedDialog::OnViewData(wxCommandEvent &)
{
  LogitBranchDialog dialog(this, m_doc, m_plot->GetBranch());
  dialog.ShowModal();
}


}  // end encapsulating anonymous namespace

//========================================================================
//                        External interface
//========================================================================

void LogitStrategic(wxWindow *p_parent, gbtGameDocument *p_doc)
{
  LogitMixedDialog(p_parent, p_doc).ShowModal();
}

Generated by  Doxygen 1.6.0   Back to index