1 //
2 // This file is part of Gambit
3 // Copyright (c) 1994-2016, The Gambit Project (http://www.gambit-project.org)
4 //
5 // FILE: src/gui/efgpanel.cc
6 // Main viewing panel for extensive forms
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 //
22 
23 #include <wx/wxprec.h>
24 #ifndef WX_PRECOMP
25 #include <wx/wx.h>
26 #endif  // WX_PRECOMP
27 #include <wx/dnd.h>           // for drag-and-drop features
28 #include <wx/image.h>         // for creating drag-and-drop cursor
29 #include <wx/print.h>         // for printing support
30 #include <wx/colordlg.h>      // for picking player colors
31 #if wxCHECK_VERSION(2, 9, 0)
32 #include <wx/dcsvg.h>         // for SVG output
33 #else
34 #include "dcsvg.h"
35 #endif  /* wxCHECK_VERSION */
36 
37 #include "efgpanel.h"
38 #include "efgdisplay.h"  // FIXME: communicate with tree window via events.
39 #include "menuconst.h"
40 #include "edittext.h"
41 
42 //=====================================================================
43 //                class gbtBehavDominanceToolbar
44 //=====================================================================
45 
46 //!
47 //! This panel serves as a toolbar for interactively viewing
48 //! dominance information on extensive forms.
49 //!
50 class gbtBehavDominanceToolbar : public wxPanel, public gbtGameView {
51 private:
52   wxButton *m_topButton, *m_prevButton, *m_nextButton, *m_allButton;
53   wxStaticText *m_level;
54 
55   // Overriding gbtGameView members
56   void OnUpdate(void);
57 
58   // Event handlers
59   void OnStrength(wxCommandEvent &);
60   void OnTopLevel(wxCommandEvent &);
61   void OnPreviousLevel(wxCommandEvent &);
62   void OnNextLevel(wxCommandEvent &);
63   void OnLastLevel(wxCommandEvent &);
64   void OnShowReachable(wxCommandEvent &);
65 
66 public:
67   gbtBehavDominanceToolbar(wxWindow *p_parent, gbtGameDocument *p_doc);
~gbtBehavDominanceToolbar()68   virtual ~gbtBehavDominanceToolbar() { }
69 };
70 
71 #include "bitmaps/next.xpm"
72 #include "bitmaps/prev.xpm"
73 #include "bitmaps/tobegin.xpm"
74 #include "bitmaps/toend.xpm"
75 
gbtBehavDominanceToolbar(wxWindow * p_parent,gbtGameDocument * p_doc)76 gbtBehavDominanceToolbar::gbtBehavDominanceToolbar(wxWindow *p_parent,
77 						   gbtGameDocument *p_doc)
78   : wxPanel(p_parent, -1), gbtGameView(p_doc)
79 {
80   wxBoxSizer *topSizer = new wxBoxSizer(wxHORIZONTAL);
81 
82   topSizer->Add(new wxStaticText(this, wxID_STATIC,
83 				 wxT("Hide actions which are ")),
84 		0, wxALL | wxALIGN_CENTER, 5);
85 
86   wxString domChoices[] = { wxT("strictly"), wxT("strictly or weakly") };
87   wxChoice *choice = new wxChoice(this, -1, wxDefaultPosition, wxDefaultSize,
88 				  2, domChoices);
89   choice->SetSelection(0);
90   Connect(choice->GetId(), wxEVT_COMMAND_CHOICE_SELECTED,
91 	  wxCommandEventHandler(gbtBehavDominanceToolbar::OnStrength));
92   topSizer->Add(choice, 0, wxALL | wxALIGN_CENTER, 5);
93 
94   topSizer->Add(new wxStaticText(this, wxID_STATIC, wxT("dominated:")),
95 		0, wxALL | wxALIGN_CENTER, 5);
96 
97   m_topButton = new wxBitmapButton(this, -1, wxBitmap(tobegin_xpm));
98   m_topButton->SetToolTip(_("Show all strategies"));
99   Connect(m_topButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
100 	  wxCommandEventHandler(gbtBehavDominanceToolbar::OnTopLevel));
101   topSizer->Add(m_topButton, 0, wxALL | wxALIGN_CENTER, 5);
102 
103   m_prevButton = new wxBitmapButton(this, -1, wxBitmap(prev_xpm));
104   m_prevButton->SetToolTip(_("Previous round of elimination"));
105   Connect(m_prevButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
106 	  wxCommandEventHandler(gbtBehavDominanceToolbar::OnPreviousLevel));
107   topSizer->Add(m_prevButton, 0, wxALL | wxALIGN_CENTER, 5);
108 
109   m_level = new wxStaticText(this, wxID_STATIC,
110 			     wxT("All actions shown"),
111 			     wxDefaultPosition, wxDefaultSize,
112 			     wxALIGN_CENTER | wxST_NO_AUTORESIZE);
113   topSizer->Add(m_level, 0, wxALL | wxALIGN_CENTER, 5);
114 
115   m_nextButton = new wxBitmapButton(this, -1, wxBitmap(next_xpm));
116   m_nextButton->SetToolTip(_("Next round of elimination"));
117   Connect(m_nextButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
118 	  wxCommandEventHandler(gbtBehavDominanceToolbar::OnNextLevel));
119   topSizer->Add(m_nextButton, 0, wxALL | wxALIGN_CENTER, 5);
120 
121   m_allButton = new wxBitmapButton(this, -1, wxBitmap(toend_xpm));
122   m_allButton->SetToolTip(_("Eliminate iteratively"));
123   Connect(m_allButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
124 	  wxCommandEventHandler(gbtBehavDominanceToolbar::OnLastLevel));
125   topSizer->Add(m_allButton, 0, wxALL | wxALIGN_CENTER, 5);
126 
127   wxCheckBox *showReachable = new wxCheckBox(this, -1,
128 					     wxT("Show only reachable nodes"));
129   showReachable->SetValue(m_doc->GetStyle().RootReachable());
130   Connect(showReachable->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED,
131 	  wxCommandEventHandler(gbtBehavDominanceToolbar::OnShowReachable));
132   topSizer->Add(showReachable, 0, wxALL | wxALIGN_CENTER, 5);
133 
134   SetSizer(topSizer);
135   Layout();
136 }
137 
OnStrength(wxCommandEvent & p_event)138 void gbtBehavDominanceToolbar::OnStrength(wxCommandEvent &p_event)
139 {
140   m_doc->SetBehavElimStrength(p_event.GetSelection() == 0);
141 }
142 
OnTopLevel(wxCommandEvent &)143 void gbtBehavDominanceToolbar::OnTopLevel(wxCommandEvent &)
144 {
145   m_doc->TopBehavElimLevel();
146 }
147 
OnPreviousLevel(wxCommandEvent &)148 void gbtBehavDominanceToolbar::OnPreviousLevel(wxCommandEvent &)
149 {
150   m_doc->PreviousBehavElimLevel();
151 }
152 
OnNextLevel(wxCommandEvent &)153 void gbtBehavDominanceToolbar::OnNextLevel(wxCommandEvent &)
154 {
155   m_doc->NextBehavElimLevel();
156 }
157 
OnLastLevel(wxCommandEvent &)158 void gbtBehavDominanceToolbar::OnLastLevel(wxCommandEvent &)
159 {
160   while (m_doc->NextBehavElimLevel());
161 }
162 
OnShowReachable(wxCommandEvent &)163 void gbtBehavDominanceToolbar::OnShowReachable(wxCommandEvent &)
164 {
165   gbtStyle style = m_doc->GetStyle();
166   style.SetRootReachable(!style.RootReachable());
167   m_doc->SetStyle(style);
168 }
169 
OnUpdate(void)170 void gbtBehavDominanceToolbar::OnUpdate(void)
171 {
172   m_topButton->Enable(m_doc->GetBehavElimLevel() > 1);
173   m_prevButton->Enable(m_doc->GetBehavElimLevel() > 1);
174   m_nextButton->Enable(m_doc->CanBehavElim());
175   m_allButton->Enable(m_doc->CanBehavElim());
176   if (m_doc->GetBehavElimLevel() == 1) {
177     m_level->SetLabel(wxT("All actions shown"));
178   }
179   else if (m_doc->GetBehavElimLevel() == 2) {
180     m_level->SetLabel(wxT("Eliminated 1 level"));
181   }
182   else {
183     m_level->SetLabel(wxString::Format(wxT("Eliminated %d levels"),
184 				       m_doc->GetBehavElimLevel()-1));
185   }
186   GetSizer()->Layout();
187 }
188 
189 #include "bitmaps/color.xpm"
190 #include "bitmaps/person.xpm"
191 
192 class gbtTreePlayerIcon : public wxStaticBitmap {
193 private:
194   int m_player;
195 
196   // Event handlers
197   void OnLeftClick(wxMouseEvent &);
198 
199 public:
200   gbtTreePlayerIcon(wxWindow *p_parent, int p_player);
201 
202   DECLARE_EVENT_TABLE()
203 };
204 
BEGIN_EVENT_TABLE(gbtTreePlayerIcon,wxStaticBitmap)205 BEGIN_EVENT_TABLE(gbtTreePlayerIcon, wxStaticBitmap)
206   EVT_LEFT_DOWN(gbtTreePlayerIcon::OnLeftClick)
207 END_EVENT_TABLE()
208 
209 gbtTreePlayerIcon::gbtTreePlayerIcon(wxWindow *p_parent, int p_player)
210   : wxStaticBitmap(p_parent, -1, wxBitmap(person_xpm)), m_player(p_player)
211 { }
212 
OnLeftClick(wxMouseEvent &)213 void gbtTreePlayerIcon::OnLeftClick(wxMouseEvent &)
214 {
215   wxBitmap bitmap(person_xpm);
216 
217 #if defined( __WXMSW__) or defined(__WXMAC__)
218   wxImage image = bitmap.ConvertToImage();
219 #else
220   wxIcon image;
221   image.CopyFromBitmap(bitmap);
222 #endif // _WXMSW__
223 
224   wxTextDataObject textData(wxString::Format(wxT("P%d"), m_player));
225   wxDropSource source(textData, this, image, image, image);
226   source.DoDragDrop(wxDrag_DefaultMove);
227 }
228 
229 class gbtTreePlayerPanel : public wxPanel {
230 private:
231   gbtGameDocument *m_doc;
232   int m_player;
233   gbtEditableText *m_playerLabel;
234   wxStaticText *m_payoff, *m_nodeValue, *m_nodeProb;
235   wxStaticText *m_infosetValue, *m_infosetProb, *m_belief;
236 
237   /// @name Event handlers
238   //@{
239   /// The set color icon is clicked
240   void OnSetColor(wxCommandEvent &);
241   /// Start the editing of the player label
242   void OnEditPlayerLabel(wxCommandEvent &);
243   /// End the editing of the player label after enter is pressed
244   void OnAcceptPlayerLabel(wxCommandEvent &);
245   /// Process a keypress event
246   void OnChar(wxKeyEvent &);
247   //@}
248 
249 public:
250   gbtTreePlayerPanel(wxWindow *, gbtGameDocument *, int p_player);
251 
252   void OnUpdate(void);
253   void PostPendingChanges(void);
254 
255   DECLARE_EVENT_TABLE()
256 };
257 
BEGIN_EVENT_TABLE(gbtTreePlayerPanel,wxPanel)258 BEGIN_EVENT_TABLE(gbtTreePlayerPanel, wxPanel)
259   EVT_CHAR(gbtTreePlayerPanel::OnChar)
260 END_EVENT_TABLE()
261 
262 gbtTreePlayerPanel::gbtTreePlayerPanel(wxWindow *p_parent,
263 				       gbtGameDocument *p_doc,
264 				       int p_player)
265   : wxPanel(p_parent, -1), m_doc(p_doc), m_player(p_player)
266 {
267   wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL);
268 
269   wxBoxSizer *labelSizer = new wxBoxSizer(wxHORIZONTAL);
270 
271   wxStaticBitmap *playerIcon = new gbtTreePlayerIcon(this, m_player);
272   labelSizer->Add(playerIcon, 0, wxALL | wxALIGN_CENTER, 0);
273 
274   wxBitmapButton *setColorIcon =
275     new wxBitmapButton(this, -1, wxBitmap(color_xpm),
276 		       wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
277   setColorIcon->SetToolTip(_("Change the color for this player"));
278 
279   labelSizer->Add(setColorIcon, 0, wxALL | wxALIGN_CENTER, 0);
280   Connect(setColorIcon->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
281 	  wxCommandEventHandler(gbtTreePlayerPanel::OnSetColor));
282 
283   m_playerLabel = new gbtEditableText(this, -1, wxT(""),
284 				      wxDefaultPosition, wxSize(125, -1));
285   m_playerLabel->SetFont(wxFont(10, wxSWISS, wxNORMAL, wxBOLD));
286   labelSizer->Add(m_playerLabel, 1, wxLEFT | wxEXPAND, 10);
287   Connect(m_playerLabel->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
288 	  wxCommandEventHandler(gbtTreePlayerPanel::OnEditPlayerLabel));
289   Connect(m_playerLabel->GetId(), wxEVT_COMMAND_TEXT_ENTER,
290 	  wxCommandEventHandler(gbtTreePlayerPanel::OnAcceptPlayerLabel));
291 
292   topSizer->Add(labelSizer, 0, wxALL, 0);
293 
294   m_payoff = new wxStaticText(this, wxID_STATIC, wxT("Payoff:"),
295 			      wxDefaultPosition, wxDefaultSize,
296 			      wxALIGN_CENTER | wxST_NO_AUTORESIZE);
297   m_payoff->SetFont(wxFont(10, wxSWISS, wxNORMAL, wxNORMAL));
298   topSizer->Add(m_payoff, 0, wxALL | wxEXPAND, 0);
299   topSizer->Show(m_payoff, false);
300 
301   m_nodeValue = new wxStaticText(this, wxID_STATIC, wxT("Node value:"),
302 				 wxDefaultPosition, wxDefaultSize,
303 				 wxALIGN_CENTER | wxST_NO_AUTORESIZE);
304   m_nodeValue->SetFont(wxFont(10, wxSWISS, wxNORMAL, wxNORMAL));
305   topSizer->Add(m_nodeValue, 0, wxALL | wxEXPAND, 0);
306   topSizer->Show(m_nodeValue, false);
307 
308   m_nodeProb = new wxStaticText(this, wxID_STATIC, wxT("Node reached:"),
309 				wxDefaultPosition, wxDefaultSize,
310 				wxALIGN_CENTER | wxST_NO_AUTORESIZE);
311   m_nodeProb->SetFont(wxFont(10, wxSWISS, wxNORMAL, wxNORMAL));
312   topSizer->Add(m_nodeProb, 0, wxALL | wxEXPAND, 0);
313   topSizer->Show(m_nodeProb, false);
314 
315   m_infosetValue = new wxStaticText(this, wxID_STATIC, wxT("Infoset value:"),
316 				    wxDefaultPosition, wxDefaultSize,
317 				    wxALIGN_CENTER | wxST_NO_AUTORESIZE);
318   m_infosetValue->SetFont(wxFont(10, wxSWISS, wxNORMAL, wxNORMAL));
319   topSizer->Add(m_infosetValue, 0, wxALL | wxEXPAND, 0);
320   topSizer->Show(m_infosetValue, false);
321 
322   m_infosetProb = new wxStaticText(this, wxID_STATIC, wxT("Infoset reached:"),
323 				    wxDefaultPosition, wxDefaultSize,
324 				    wxALIGN_CENTER | wxST_NO_AUTORESIZE);
325   m_infosetProb->SetFont(wxFont(10, wxSWISS, wxNORMAL, wxNORMAL));
326   topSizer->Add(m_infosetProb, 0, wxALL | wxEXPAND, 0);
327   topSizer->Show(m_infosetProb, false);
328 
329   m_belief = new wxStaticText(this, wxID_STATIC, wxT("Belief:"),
330 			      wxDefaultPosition, wxDefaultSize,
331 			      wxALIGN_CENTER | wxST_NO_AUTORESIZE);
332   m_belief->SetFont(wxFont(10, wxSWISS, wxNORMAL, wxNORMAL));
333   topSizer->Add(m_belief, 0, wxALL | wxEXPAND, 0);
334   topSizer->Show(m_belief, false);
335 
336   SetSizer(topSizer);
337   topSizer->SetSizeHints(this);
338   topSizer->Fit(this);
339   Layout();
340   OnUpdate();
341 }
342 
OnUpdate(void)343 void gbtTreePlayerPanel::OnUpdate(void)
344 {
345   if (!m_doc->IsTree())  return;
346 
347   wxColour color = m_doc->GetStyle().GetPlayerColor(m_player);
348 
349   m_playerLabel->SetForegroundColour(color);
350   m_playerLabel->SetValue(wxString(m_doc->GetGame()->GetPlayer(m_player)->GetLabel().c_str(),
351 				   *wxConvCurrent));
352 
353   m_payoff->SetForegroundColour(color);
354   if (m_doc->GetCurrentProfile() > 0) {
355     std::string pay = m_doc->GetProfiles().GetPayoff(m_player);
356     m_payoff->SetLabel(wxT("Payoff: ") +
357 		       wxString(pay.c_str(), *wxConvCurrent));
358     GetSizer()->Show(m_payoff, true);
359 
360     Gambit::GameNode node = m_doc->GetSelectNode();
361 
362     if (node) {
363       m_nodeValue->SetForegroundColour(color);
364       std::string value = m_doc->GetProfiles().GetNodeValue(node, m_player);
365       m_nodeValue->SetLabel(wxT("Node value: ") +
366 			    wxString(value.c_str(), *wxConvCurrent));
367       GetSizer()->Show(m_nodeValue, true);
368 
369       if (node->GetInfoset() && node->GetPlayer()->GetNumber() == m_player) {
370 	m_nodeProb->SetForegroundColour(color);
371 	std::string value = m_doc->GetProfiles().GetRealizProb(node);
372 	m_nodeProb->SetLabel(wxT("Node reached: ") +
373 			     wxString(value.c_str(), *wxConvCurrent));
374 	GetSizer()->Show(m_nodeProb, true);
375 
376 	m_infosetValue->SetForegroundColour(color);
377 	value = m_doc->GetProfiles().GetInfosetValue(node);
378 	m_infosetValue->SetLabel(wxT("Infoset value: ") +
379 				 wxString(value.c_str(), *wxConvCurrent));
380 	GetSizer()->Show(m_infosetValue, true);
381 
382 	m_infosetProb->SetForegroundColour(color);
383 	value = m_doc->GetProfiles().GetInfosetProb(node);
384 	m_infosetProb->SetLabel(wxT("Infoset reached: ") +
385 				wxString(value.c_str(), *wxConvCurrent));
386 	GetSizer()->Show(m_infosetProb, true);
387 
388 	m_belief->SetForegroundColour(color);
389 	value = m_doc->GetProfiles().GetBeliefProb(node);
390 	m_belief->SetLabel(wxT("Belief: ") +
391 			   wxString(value.c_str(), *wxConvCurrent));
392 	GetSizer()->Show(m_belief, true);
393       }
394       else {
395 	GetSizer()->Show(m_nodeProb, false);
396 	GetSizer()->Show(m_infosetValue, false);
397 	GetSizer()->Show(m_infosetProb, false);
398 	GetSizer()->Show(m_belief, false);
399       }
400     }
401     else {
402       GetSizer()->Show(m_nodeValue, false);
403     }
404   }
405   else {
406     GetSizer()->Show(m_payoff, false);
407     GetSizer()->Show(m_nodeValue, false);
408     GetSizer()->Show(m_nodeProb, false);
409     GetSizer()->Show(m_infosetValue, false);
410     GetSizer()->Show(m_infosetProb, false);
411     GetSizer()->Show(m_belief, false);
412   }
413 
414   GetSizer()->Layout();
415   GetSizer()->SetSizeHints(this);
416   GetSizer()->Fit(this);
417 }
418 
OnChar(wxKeyEvent & p_event)419 void gbtTreePlayerPanel::OnChar(wxKeyEvent &p_event)
420 {
421   if (p_event.GetKeyCode() == WXK_ESCAPE) {
422     m_playerLabel->EndEdit(false);
423   }
424   else {
425     p_event.Skip();
426   }
427 }
428 
OnSetColor(wxCommandEvent &)429 void gbtTreePlayerPanel::OnSetColor(wxCommandEvent &)
430 {
431   wxColourData data;
432   data.SetColour(m_doc->GetStyle().GetPlayerColor(m_player));
433   wxColourDialog dialog(this, &data);
434   dialog.SetTitle(wxString::Format(_("Choose color for player %d"),
435 				   m_player));
436 
437   if (dialog.ShowModal() == wxID_OK) {
438     wxColour color = dialog.GetColourData().GetColour();
439     gbtStyle style = m_doc->GetStyle();
440     style.SetPlayerColor(m_player, color);
441     m_doc->SetStyle(style);
442   }
443 }
444 
OnEditPlayerLabel(wxCommandEvent &)445 void gbtTreePlayerPanel::OnEditPlayerLabel(wxCommandEvent &)
446 {
447   m_doc->PostPendingChanges();
448   m_playerLabel->BeginEdit();
449 }
450 
OnAcceptPlayerLabel(wxCommandEvent &)451 void gbtTreePlayerPanel::OnAcceptPlayerLabel(wxCommandEvent &)
452 {
453   m_doc->DoSetPlayerLabel(m_doc->GetGame()->GetPlayer(m_player),
454 			  m_playerLabel->GetValue());
455 }
456 
PostPendingChanges(void)457 void gbtTreePlayerPanel::PostPendingChanges(void)
458 {
459   if (m_playerLabel->IsEditing()) {
460     m_playerLabel->EndEdit(true);
461     m_doc->DoSetPlayerLabel(m_doc->GetGame()->GetPlayer(m_player),
462 			    m_playerLabel->GetValue());
463   }
464 }
465 
466 #include "bitmaps/dice.xpm"
467 
468 class gbtTreeChanceIcon : public wxStaticBitmap {
469 private:
470   // Event handlers
471   void OnLeftClick(wxMouseEvent &);
472 
473 public:
474   gbtTreeChanceIcon(wxWindow *p_parent);
475 
476   DECLARE_EVENT_TABLE()
477 };
478 
BEGIN_EVENT_TABLE(gbtTreeChanceIcon,wxStaticBitmap)479 BEGIN_EVENT_TABLE(gbtTreeChanceIcon, wxStaticBitmap)
480   EVT_LEFT_DOWN(gbtTreeChanceIcon::OnLeftClick)
481 END_EVENT_TABLE()
482 
483 gbtTreeChanceIcon::gbtTreeChanceIcon(wxWindow *p_parent)
484   : wxStaticBitmap(p_parent, -1, wxBitmap(dice_xpm))
485 { }
486 
OnLeftClick(wxMouseEvent &)487 void gbtTreeChanceIcon::OnLeftClick(wxMouseEvent &)
488 {
489   wxBitmap bitmap(dice_xpm);
490 
491 #if defined( __WXMSW__) or defined(__WXMAC__)
492   wxImage image = bitmap.ConvertToImage();
493 #else
494   wxIcon image;
495   image.CopyFromBitmap(bitmap);
496 #endif // _WXMSW__
497 
498   wxTextDataObject textData(wxT("P0"));
499   wxDropSource source(textData, this, image, image, image);
500   source.DoDragDrop(wxDrag_DefaultMove);
501 }
502 
503 class gbtTreeChancePanel : public wxPanel, public gbtGameView {
504 private:
505   wxStaticText *m_playerLabel;
506 
507   // Implementation of gbtGameView members
508   void OnUpdate(void);
509 
510   /// @name Event handlers
511   //@{
512   /// The set color icon is clicked
513   void OnSetColor(wxCommandEvent &);
514   //@}
515 
516 public:
517   gbtTreeChancePanel(wxWindow *, gbtGameDocument *);
518 };
519 
gbtTreeChancePanel(wxWindow * p_parent,gbtGameDocument * p_doc)520 gbtTreeChancePanel::gbtTreeChancePanel(wxWindow *p_parent,
521 				       gbtGameDocument *p_doc)
522   : wxPanel(p_parent, -1), gbtGameView(p_doc)
523 {
524   wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL);
525 
526   wxBoxSizer *labelSizer = new wxBoxSizer(wxHORIZONTAL);
527 
528   wxStaticBitmap *playerIcon = new gbtTreeChanceIcon(this);
529   labelSizer->Add(playerIcon, 0, wxALL | wxALIGN_CENTER, 0);
530 
531   wxBitmapButton *setColorIcon =
532     new wxBitmapButton(this, -1, wxBitmap(color_xpm),
533 		       wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
534   setColorIcon->SetToolTip(_("Change the color for this player"));
535 
536   labelSizer->Add(setColorIcon, 0, wxALL | wxALIGN_CENTER, 0);
537   Connect(setColorIcon->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
538 	  wxCommandEventHandler(gbtTreeChancePanel::OnSetColor));
539 
540   m_playerLabel = new wxStaticText(this, wxID_STATIC, wxT("Chance"),
541 				   wxDefaultPosition, wxSize(125, -1),
542 				   wxALIGN_LEFT);
543   m_playerLabel->SetFont(wxFont(10, wxSWISS, wxNORMAL, wxBOLD));
544   labelSizer->Add(m_playerLabel, 1, wxLEFT | wxALIGN_CENTER, 10);
545 
546   topSizer->Add(labelSizer, 0, wxALL, 0);
547 
548   SetSizer(topSizer);
549   topSizer->SetSizeHints(this);
550   topSizer->Fit(this);
551   Layout();
552 }
553 
OnUpdate(void)554 void gbtTreeChancePanel::OnUpdate(void)
555 {
556   if (!m_doc->GetGame())  return;
557 
558   m_playerLabel->SetForegroundColour(m_doc->GetStyle().ChanceColor());
559   GetSizer()->Layout();
560 }
561 
OnSetColor(wxCommandEvent &)562 void gbtTreeChancePanel::OnSetColor(wxCommandEvent &)
563 {
564   wxColourData data;
565   data.SetColour(m_doc->GetStyle().ChanceColor());
566   wxColourDialog dialog(this, &data);
567   dialog.SetTitle(wxT("Choose color for chance player"));
568 
569   if (dialog.ShowModal() == wxID_OK) {
570     wxColour color = dialog.GetColourData().GetColour();
571     gbtStyle style = m_doc->GetStyle();
572     style.SetChanceColor(color);
573     m_doc->SetStyle(style);
574   }
575 }
576 
577 //=====================================================================
578 //                  class gbtTreePlayerToolbar
579 //=====================================================================
580 
581 class gbtTreePlayerToolbar : public wxPanel, public gbtGameView {
582 private:
583   gbtTreeChancePanel *m_chancePanel;
584   Gambit::Array<gbtTreePlayerPanel *> m_playerPanels;
585 
586   // @name Implementation of gbtGameView members
587   //@{
588   void OnUpdate(void);
589   void PostPendingChanges(void);
590   //@}
591 
592 public:
593   gbtTreePlayerToolbar(wxWindow *p_parent, gbtGameDocument *p_doc);
594 };
595 
596 
gbtTreePlayerToolbar(wxWindow * p_parent,gbtGameDocument * p_doc)597 gbtTreePlayerToolbar::gbtTreePlayerToolbar(wxWindow *p_parent,
598 					   gbtGameDocument *p_doc)
599   : wxPanel(p_parent, -1, wxDefaultPosition, wxSize(210, -1)),
600     gbtGameView(p_doc)
601 {
602   wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL);
603 
604   m_chancePanel = new gbtTreeChancePanel(this, m_doc);
605   topSizer->Add(m_chancePanel, 0, wxALL | wxEXPAND, 5);
606 
607   for (int pl = 1; pl <= m_doc->NumPlayers(); pl++) {
608     m_playerPanels.Append(new gbtTreePlayerPanel(this, m_doc, pl));
609     topSizer->Add(m_playerPanels[pl], 0, wxALL | wxEXPAND, 5);
610   }
611 
612   SetSizer(topSizer);
613   Layout();
614 }
615 
OnUpdate(void)616 void gbtTreePlayerToolbar::OnUpdate(void)
617 {
618   while (m_playerPanels.Length() < m_doc->NumPlayers()) {
619     gbtTreePlayerPanel *panel = new gbtTreePlayerPanel(this, m_doc,
620 						       m_playerPanels.Length()+1);
621     m_playerPanels.Append(panel);
622     GetSizer()->Add(panel, 0, wxALL | wxEXPAND, 5);
623   }
624 
625   while (m_playerPanels.Length() > m_doc->NumPlayers()) {
626     gbtTreePlayerPanel *panel = m_playerPanels.Remove(m_playerPanels.Length());
627     GetSizer()->Detach(panel);
628     panel->Destroy();
629   }
630 
631   for (int pl = 1; pl <= m_playerPanels.Length(); pl++) {
632     m_playerPanels[pl]->OnUpdate();
633   }
634 
635   GetSizer()->Layout();
636 }
637 
PostPendingChanges(void)638 void gbtTreePlayerToolbar::PostPendingChanges(void)
639 {
640   for (int pl = 1; pl <= m_playerPanels.Length(); pl++) {
641     m_playerPanels[pl]->PostPendingChanges();
642   }
643 }
644 
645 //=====================================================================
646 //              Implementation of class gbtEfgPanel
647 //=====================================================================
648 
BEGIN_EVENT_TABLE(gbtEfgPanel,wxPanel)649 BEGIN_EVENT_TABLE(gbtEfgPanel, wxPanel)
650   EVT_MENU(GBT_MENU_TOOLS_DOMINANCE, gbtEfgPanel::OnToolsDominance)
651   EVT_MENU(GBT_MENU_VIEW_ZOOMIN, gbtEfgPanel::OnViewZoomIn)
652   EVT_MENU(GBT_MENU_VIEW_ZOOMOUT, gbtEfgPanel::OnViewZoomOut)
653   EVT_MENU(GBT_MENU_VIEW_ZOOM100, gbtEfgPanel::OnViewZoom100)
654   EVT_MENU(GBT_MENU_VIEW_ZOOMFIT, gbtEfgPanel::OnViewZoomFit)
655 END_EVENT_TABLE()
656 
657 gbtEfgPanel::gbtEfgPanel(wxWindow *p_parent, gbtGameDocument *p_doc)
658   : wxPanel(p_parent, -1), gbtGameView(p_doc)
659 {
660   m_treeWindow = new gbtEfgDisplay(this, m_doc);
661   m_playerToolbar = new gbtTreePlayerToolbar(this, m_doc);
662   m_dominanceToolbar = new gbtBehavDominanceToolbar(this, m_doc);
663 
664   wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL);
665   topSizer->Add(m_dominanceToolbar, 0, wxEXPAND, 0);
666   topSizer->Show(m_dominanceToolbar, false);
667 
668   wxBoxSizer *treeSizer = new wxBoxSizer(wxHORIZONTAL);
669   treeSizer->Add(m_playerToolbar, 0, wxEXPAND, 0);
670   treeSizer->Add(m_treeWindow, 1, wxEXPAND, 0);
671 
672   topSizer->Add(treeSizer, 1, wxEXPAND, 0);
673   SetSizer(topSizer);
674   Layout();
675 }
676 
OnToolsDominance(wxCommandEvent & p_event)677 void gbtEfgPanel::OnToolsDominance(wxCommandEvent &p_event)
678 {
679   GetSizer()->Show(m_dominanceToolbar, p_event.IsChecked(), true);
680   GetSizer()->Layout();
681 }
682 
OnViewZoomIn(wxCommandEvent &)683 void gbtEfgPanel::OnViewZoomIn(wxCommandEvent &)
684 {
685   int zoom = m_treeWindow->GetZoom();
686   if (zoom < 150)  zoom += 10;
687   m_treeWindow->SetZoom(zoom);
688 }
689 
OnViewZoomOut(wxCommandEvent &)690 void gbtEfgPanel::OnViewZoomOut(wxCommandEvent &)
691 {
692   int zoom = m_treeWindow->GetZoom();
693   if (zoom > 10)  zoom -= 10;
694   m_treeWindow->SetZoom(zoom);
695 }
696 
OnViewZoom100(wxCommandEvent &)697 void gbtEfgPanel::OnViewZoom100(wxCommandEvent &)
698 {
699   m_treeWindow->SetZoom(100);
700 }
701 
OnViewZoomFit(wxCommandEvent &)702 void gbtEfgPanel::OnViewZoomFit(wxCommandEvent &)
703 {
704   m_treeWindow->FitZoom();
705 }
706 
707 class gbtEfgPrintout : public wxPrintout {
708 private:
709   gbtEfgPanel *m_efgPanel;
710 
711 public:
gbtEfgPrintout(gbtEfgPanel * p_efgPanel,const wxString & p_label)712   gbtEfgPrintout(gbtEfgPanel *p_efgPanel, const wxString &p_label)
713     : wxPrintout(p_label), m_efgPanel(p_efgPanel) { }
~gbtEfgPrintout()714   virtual ~gbtEfgPrintout() { }
715 
OnPrintPage(int)716   bool OnPrintPage(int)
717   { m_efgPanel->RenderGame(*GetDC(), 50, 50);  return true; }
HasPage(int page)718   bool HasPage(int page) { return (page <= 1); }
GetPageInfo(int * minPage,int * maxPage,int * selPageFrom,int * selPageTo)719   void GetPageInfo(int *minPage, int *maxPage,
720 		   int *selPageFrom, int *selPageTo)
721   { *minPage = 1; *maxPage = 1; *selPageFrom = 1; *selPageTo = 1; }
722 };
723 
GetPrintout(void)724 wxPrintout *gbtEfgPanel::GetPrintout(void)
725 {
726   return new gbtEfgPrintout(this,
727 			    wxString(m_doc->GetGame()->GetTitle().c_str(),
728 				     *wxConvCurrent));
729 }
730 
GetBitmap(wxBitmap & p_bitmap,int p_marginX,int p_marginY)731 bool gbtEfgPanel::GetBitmap(wxBitmap &p_bitmap, int p_marginX, int p_marginY)
732 {
733   if (m_treeWindow->GetLayout().MaxX() > 65000 ||
734       m_treeWindow->GetLayout().MaxY() > 65000) {
735     // This is just too huge to export to graphics
736     return false;
737   }
738 
739   wxMemoryDC dc;
740   p_bitmap = wxBitmap(m_treeWindow->GetLayout().MaxX() + 2 * p_marginX,
741 		      m_treeWindow->GetLayout().MaxY() + 2 * p_marginY);
742   dc.SelectObject(p_bitmap);
743   RenderGame(dc, p_marginX, p_marginY);
744   return true;
745 }
746 
GetSVG(const wxString & p_filename,int p_marginX,int p_marginY)747 void gbtEfgPanel::GetSVG(const wxString &p_filename,
748 			 int p_marginX, int p_marginY)
749 {
750   // The size of the image to be drawn
751   int maxX = m_treeWindow->GetLayout().MaxX();
752   int maxY = m_treeWindow->GetLayout().MaxY();
753 
754   wxSVGFileDC dc(p_filename, maxX + 2*p_marginX, maxY + 2*p_marginY);
755   // For some reason, this needs to be initialized
756   dc.SetLogicalScale(1.0, 1.0);
757   RenderGame(dc, p_marginX, p_marginY);
758 }
759 
RenderGame(wxDC & p_dc,int p_marginX,int p_marginY)760 void gbtEfgPanel::RenderGame(wxDC &p_dc, int p_marginX, int p_marginY)
761 {
762   // The size of the image to be drawn
763   int maxX = m_treeWindow->GetLayout().MaxX();
764   int maxY = m_treeWindow->GetLayout().MaxY();
765 
766   // Get the size of the DC in pixels
767   wxCoord w, h;
768   p_dc.GetSize(&w, &h);
769 
770   // Calculate a scaling factor
771   double scaleX = (double) w / (double) (maxX + 2*p_marginX);
772   double scaleY = (double) h / (double) (maxY + 2*p_marginY);
773   double scale = (scaleX < scaleY) ? scaleX : scaleY;
774   // Never zoom in
775   if (scale > 1.0)  scale = 1.0;
776   p_dc.SetUserScale(scale, scale);
777 
778   // Calculate the position on the DC to center the tree
779   double posX = (double) ((w - (maxX * scale)) / 2.0);
780   double posY = (double) ((h - (maxY * scale)) / 2.0);
781   p_dc.SetDeviceOrigin((int) posX, (int) posY);
782 
783   printf("Drawing with scale %f\n", scale);
784 
785   // Draw!
786   m_treeWindow->OnDraw(p_dc, scale);
787 }
788