core/vgui/impl/wx/vgui_wx_dialog_impl.cxx
Go to the documentation of this file.
00001 // This is core/vgui/impl/wx/vgui_wx_dialog_impl.cxx
00002 #include "vgui_wx_dialog_impl.h"
00003 //=========================================================================
00004 //:
00005 // \file
00006 // \brief  wxWidgets implementation of vgui_dialog_impl.
00007 //
00008 // See vgui_wx_dialog_impl.h for details.
00009 //=========================================================================
00010 
00011 #include "vgui_wx_adaptor.h"
00012 
00013 #include <vgui/vgui_color_text.h>
00014 #include <vgui/internals/vgui_dialog_field.h>
00015 #include <vgui/internals/vgui_simple_field.h>
00016 
00017 #include <wx/log.h>
00018 #include <wx/filename.h>
00019 
00020 #include <wx/dialog.h>
00021 #include <wx/filedlg.h>
00022 #include <wx/colordlg.h>
00023 
00024 #include <wx/sizer.h>
00025 #include <wx/valgen.h>
00026 #include <wx/checkbox.h>
00027 #include <wx/textctrl.h>
00028 #include <wx/arrstr.h>
00029 #include <wx/choice.h>
00030 #include <wx/statline.h>
00031 #include <wx/stattext.h>
00032 #include <wx/statbox.h>
00033 #include <wx/button.h>
00034 
00035 #ifndef wxCommandEventHandler        // wxWidgets-2.5.3 doesn't define this
00036 #define wxCommandEventHandler(func) \
00037     (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(wxCommandEventFunction, &func)
00038 #endif
00039 
00040 #include <vcl_iostream.h>
00041 #include <vcl_sstream.h>
00042 #include <vcl_algorithm.h>
00043 #include <vcl_map.h>
00044 #include <vcl_utility.h>
00045 #include <vcl_cassert.h>
00046 
00047 //-------------------------------------------------------------------------
00048 // Private helpers - declarations.
00049 //-------------------------------------------------------------------------
00050 namespace
00051 {
00052   class vgui_wx_event_handler : public wxEvtHandler
00053   {
00054    public:
00055     void insert_handle(int id, wxTextCtrl* control)
00056     {
00057       handles_.insert(vcl_pair<int,wxTextCtrl*>(id, control));
00058     }
00059 
00060     void file_browser(wxCommandEvent& e)
00061     {
00062       // ***** should add wildcard value,
00063       //       but need to pass it through from element->widget
00064       wxFileName filename(handles_[e.GetId()]->GetValue());
00065       wxFileDialog file_dialog(handles_[e.GetId()],
00066                                wxT("Choose a file"),
00067                                filename.GetPath(),
00068                                filename.GetFullName());
00069 
00070       if (file_dialog.ShowModal() == wxID_OK)
00071       {
00072         // update wxTextCtrl value
00073         handles_[e.GetId()]->SetValue(file_dialog.GetPath());
00074       }
00075     }
00076 
00077     void color_chooser(wxCommandEvent& e)
00078     {
00079       wxString text = handles_[e.GetId()]->GetValue();
00080 
00081       wxColour color;
00082       color.Set(int(red_value(vcl_string(text.mb_str()))   * 255.0),
00083                 int(green_value(vcl_string(text.mb_str())) * 255.0),
00084                 int(blue_value(vcl_string(text.mb_str()))  * 255.0));
00085       vcl_cout << "current color: "<<int(color.Red())<<' '<<int(color.Green())<<' '<<int(color.Blue())<<vcl_endl;
00086       wxColourData cdata;
00087       cdata.SetColour(color);
00088 
00089       wxColourDialog color_dialog(handles_[e.GetId()], &cdata);
00090 
00091       if (color_dialog.ShowModal() == wxID_OK)
00092       {
00093         color = color_dialog.GetColourData().GetColour();
00094 
00095         vcl_cout << "new color: "<<int(color.Red())<<' '<<int(color.Green())<<' '<<int(color.Blue())<<vcl_endl;
00096         vcl_stringstream sstr;
00097         //text.Clear();
00098         sstr << color.Red() / 255.0 << ' '
00099              << color.Green() / 255.0 << ' '
00100              << color.Blue() / 255.0;
00101         vcl_cout << "new color text: "<< sstr.str() << vcl_endl;
00102 
00103         // update wxTextCtrl value
00104         handles_[e.GetId()]->SetValue(wxString(sstr.str().c_str(),wxConvUTF8));
00105       }
00106     }
00107 
00108    private:
00109     vcl_map<int,wxTextCtrl*> handles_;
00110   };
00111 
00112   //: Used to transfer data between a vgui_dialog_field and wxTextControl.
00113   class vgui_wx_text_validator : public wxValidator
00114   {
00115    public:
00116     //: Constructor - default.
00117     vgui_wx_text_validator(vgui_dialog_field* field = 0)
00118       : field_(field)
00119     {
00120     }
00121 
00122     //: Constructor.
00123     vgui_wx_text_validator(const vgui_wx_text_validator& validator)
00124       : wxValidator(), field_(validator.field_)
00125     {
00126       wxValidator::Copy(validator);
00127     }
00128 
00129     //: Destructor.
00130     virtual ~vgui_wx_text_validator() {}
00131 
00132     //: Make a clone of this validator.
00133     virtual wxObject* Clone() const
00134     {
00135       return new vgui_wx_text_validator(*this);
00136     }
00137 
00138     //: Called to transfer data to the window.
00139     virtual bool TransferToWindow()
00140     {
00141       if (!check_validator()) { return false; }
00142       if (field_)
00143       {
00144         dynamic_cast<wxTextCtrl*>(
00145           m_validatorWindow)->SetValue(wxString(field_->current_value().c_str(),wxConvUTF8));
00146       }
00147       return true;
00148     }
00149 
00150     //: Called to transfer data from the window.
00151     virtual bool TransferFromWindow()
00152     {
00153       if (!check_validator()) { return false; }
00154       if (field_)
00155       {
00156         field_->update_value(
00157           vcl_string(dynamic_cast<wxTextCtrl*>(m_validatorWindow)->GetValue().mb_str()));
00158       }
00159       return true;
00160     }
00161 
00162     //: Called when validation of the control data must be validated.
00163     virtual bool Validate(wxWindow* parent)
00164     {
00165       if (!check_validator()) { return false; }
00166       return true;
00167     }
00168 
00169    private:
00170     //: Check if validator parent window is valid.
00171     bool check_validator() const
00172     {
00173       wxCHECK_MSG(m_validatorWindow,
00174                   false,
00175                   wxT("No window associated with validator"));
00176 
00177       wxCHECK_MSG(m_validatorWindow->IsKindOf(CLASSINFO(wxTextCtrl)),
00178                   false,
00179                   wxT("wxTextValidator is only for wxTextCtrl's") );
00180 
00181       return true;
00182     }
00183 
00184     //: vgui dialog field used to store the data.
00185     vgui_dialog_field* field_;
00186   };
00187 } // unnamed namespace
00188 
00189 //-------------------------------------------------------------------------
00190 // vgui_wx_dialog_impl implementation - construction & destruction.
00191 //-------------------------------------------------------------------------
00192 //: Constructor - create an empty dialog with the given title.
00193 vgui_wx_dialog_impl::vgui_wx_dialog_impl(const char* name)
00194   : vgui_dialog_impl(name)
00195   , title_(name)
00196   , dialog_(0)
00197   , last_element_count_(0)
00198   , max_label_width_(0)
00199   , is_modal_(true)
00200   , adaptor_(0)
00201 {
00202   wxLogTrace(wxTRACE_RefCount, wxT("vgui_wx_dialog_impl::vgui_wx_dialog_impl"));
00203 }
00204 
00205 //: Destructor.
00206 vgui_wx_dialog_impl::~vgui_wx_dialog_impl(void)
00207 {
00208   wxLogTrace(wxTRACE_RefCount, wxT("vgui_wx_dialog_impl::~vgui_wx_dialog_impl"));
00209   destroy_wx_dialog();
00210 }
00211 
00212 //-------------------------------------------------------------------------
00213 // vgui_wx_dialog_impl implementation.
00214 //-------------------------------------------------------------------------
00215 //: Display the dialog box form and collect data from the user.
00216 //
00217 // In modal mode, it returns true if ok was pressed and false otherwise.
00218 // In modeless mode, it returns false if the dialog was already displayed
00219 // and true if it wasn't.
00220 //
00221 // The vgui_dialog API doesn't really support modeless dialogs for data
00222 // collection nor data monitoring. For example, for data monitoring we
00223 // would need some sort of update() function to allow the user to
00224 // periodically refresh the dialog values.
00225 bool vgui_wx_dialog_impl::ask(void)
00226 {
00227   // rebuild the dialog, if the elements have changed
00228   if (has_changed()) { build_wx_dialog(); }
00229 
00230   // show the dialog
00231   if (is_modal_)
00232   {
00233     return dialog_->ShowModal() == wxID_OK ? true : false;
00234   }
00235   else
00236   {
00237     return dialog_->Show();
00238   }
00239 }
00240 
00241 struct vgui_wx_dialog_choice
00242 {
00243   vcl_vector<vcl_string> names;
00244   int index;
00245 };
00246 
00247 //: Create a choice widget.
00248 void* vgui_wx_dialog_impl::
00249 choice_field_widget(const char* WXUNUSED(txt),
00250                     const vcl_vector<vcl_string>& labels,
00251                     int& val)
00252 {
00253   vgui_wx_dialog_choice* choice_data = new vgui_wx_dialog_choice;
00254   choice_data->names = labels;
00255   choice_data->index = val;
00256   return static_cast<void*>(choice_data);
00257 }
00258 
00259 struct vgui_wx_dialog_inline_tab
00260 {
00261   vgui_tableau_sptr  tab;
00262   unsigned int       height;
00263   unsigned int       width;
00264 };
00265 
00266 //: Create the inline_tableau_widget (OpenGL area).
00267 void*
00268 vgui_wx_dialog_impl::inline_tableau_widget(const vgui_tableau_sptr tab,
00269                                            unsigned width,
00270                                            unsigned height)
00271 {
00272   vgui_wx_dialog_inline_tab* tab_data = new vgui_wx_dialog_inline_tab;
00273   tab_data->tab    = tab;
00274   tab_data->height = height;
00275   tab_data->width  = width;
00276   return static_cast<void*>(tab_data);
00277 }
00278 
00279 //-------------------------------------------------------------------------
00280 // vgui_wx_dialog_impl implementation - private helpers.
00281 //-------------------------------------------------------------------------
00282 void vgui_wx_dialog_impl::build_wx_dialog(void)
00283 {
00284   // clean any previous construction
00285   destroy_wx_dialog();
00286 
00287   dialog_ = new wxDialog(0,
00288                          wxID_ANY,
00289                          wxString(title_.c_str(),wxConvUTF8),
00290                          wxDefaultPosition,
00291                          wxDefaultSize,
00292                          wxDEFAULT_DIALOG_STYLE);
00293 
00294   // handler for dynamic connection
00295   vgui_wx_event_handler* handler = new vgui_wx_event_handler;
00296   int id = wxID_HIGHEST;
00297 
00298   // probe for column sizes
00299   max_label_width_ = probe_for_max_label_width();
00300   const int min_dialog_width = max_label_width_ + 200;
00301 
00302   // for outer separation border
00303   wxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL);
00304 
00305   // sizer that will hold all elements
00306   wxSizer* holder = new wxFlexGridSizer(1);
00307   holder->SetMinSize(max_label_width_+200, 0);
00308   top_sizer->Add(holder, 0, wxGROW | wxALL, 3);
00309 
00310   // flags to use on each element
00311   wxSizerFlags flags(wxSizerFlags(0).Expand().Border(wxALL, 2));
00312 
00313   // process each element
00314   for (vcl_vector<element>::const_iterator e = elements.begin();
00315        e != elements.end(); ++e)
00316   {
00317     switch (e->type)
00318     {
00319     case bool_elem:
00320       holder->Add(bool_element(e->field), flags);
00321       break;
00322 
00323     case int_elem   :
00324     case long_elem  :
00325     case float_elem :
00326     case double_elem:
00327     case string_elem:
00328       holder->Add(text_element(e->field), flags);
00329       break;
00330 
00331     case choice_elem:
00332       holder->Add(
00333         choice_element(e->field,
00334                        static_cast<vgui_wx_dialog_choice*>(e->widget)),
00335         flags);
00336       break;
00337 
00338     case text_msg:
00339       //holder->Add(separator_element(min_dialog_width), flags);
00340       holder->Add(new wxStaticText(dialog_,
00341                                    wxID_STATIC,
00342                                    wxString(e->field->label.c_str(),wxConvUTF8)),
00343                   flags);
00344       //holder->Add(separator_element(min_dialog_width), flags);
00345       break;
00346 
00347     case file_bsr:
00348     case inline_file_bsr: // ***** this should be different...
00349       { // create variable scope for txt_ctrl
00350       wxTextCtrl* txt_ctrl;
00351       holder->Add(
00352         text_with_button_element(e->field, txt_ctrl, "Browse...", ++id),
00353         flags);
00354 
00355       // dynamically connect a handler that launches a wxFileSelector
00356       handler->insert_handle(id, txt_ctrl);
00357       handler->Connect(id,
00358                        wxEVT_COMMAND_BUTTON_CLICKED,
00359                        wxCommandEventHandler(vgui_wx_event_handler::file_browser),
00360                        0,
00361                        handler);
00362       }
00363       break;
00364 
00365     case color_csr:
00366     case inline_color_csr: // ***** this should be different...
00367       { // create variable scope for txt_ctrl
00368       wxTextCtrl* txt_ctrl;
00369       holder->Add(
00370         text_with_button_element(e->field, txt_ctrl, "Color...", ++id),
00371         flags);
00372 
00373       // dynamically connect a handler that launches a wxFileSelector
00374       handler->insert_handle(id, txt_ctrl);
00375       handler->Connect(id,
00376                        wxEVT_COMMAND_BUTTON_CLICKED,
00377                        wxCommandEventHandler(vgui_wx_event_handler::color_chooser),
00378                        0,
00379                        handler);
00380       }
00381       break;
00382 
00383     case inline_tabl:
00384       { // create variable scope for adaptor
00385       // ***** error if more than one inline tableau in this dialog
00386       vgui_wx_adaptor* adaptor = new vgui_wx_adaptor(
00387         dialog_,
00388         wxID_ANY,
00389         wxDefaultPosition,
00390         wxSize(static_cast<vgui_wx_dialog_inline_tab*>(e->widget)->width,
00391                static_cast<vgui_wx_dialog_inline_tab*>(e->widget)->height),
00392         wxBORDER_SUNKEN);
00393       adaptor->set_tableau(static_cast<vgui_wx_dialog_inline_tab*>(e->widget)->tab);
00394       //adaptor->SetSize(static_cast<vgui_wx_dialog_inline_tab*>(e->widget)->width,
00395       //                 static_cast<vgui_wx_dialog_inline_tab*>(e->widget)->height);
00396 
00397       //wxBoxSizer* box_sizer = new wxBoxSizer(wxVERTICAL);
00398       //box_sizer->Add(adaptor);
00399       //holder->Add(box_sizer, flags);
00400       holder->Add(adaptor, flags);
00401 
00402       //adaptor->post_redraw();
00403       adaptor_ = adaptor;
00404       }
00405       break;
00406 
00407     default:
00408       vcl_cerr << "Unknown type = " << e->type << vcl_endl;
00409     }
00410   }
00411 
00412   // separator line and enforcer of the dialog's minimum width
00413   holder->Add(separator_element(min_dialog_width), flags);
00414 
00415   // add ok/cancel buttons
00416   holder->Add(exit_buttons_element(), 0, wxALIGN_RIGHT | wxALL, 2);
00417 
00418   // resize to contents
00419   // ***** fix for case when larger than screen, make scrollable maybe?
00420   dialog_->SetSize(dialog_->GetEffectiveMinSize());
00421   dialog_->PushEventHandler(handler);
00422 
00423   dialog_->SetSizer(top_sizer);
00424   top_sizer->SetSizeHints(dialog_);
00425 
00426   // save the element count to determine change
00427   last_element_count_ = elements.size();
00428 }
00429 
00430 void vgui_wx_dialog_impl::destroy_wx_dialog(void)
00431 {
00432   if (dialog_)
00433   {
00434     dialog_->PopEventHandler(true);
00435     if (adaptor_)
00436     {
00437       adaptor_->post_destroy();
00438       //delete adaptor_;
00439       adaptor_ = 0;
00440     }
00441     dialog_->Destroy();
00442     dialog_ = 0;
00443   }
00444 }
00445 
00446 //: Determine if dialog has changed since last construction (i.e., ask()).
00447 //
00448 // Note that we assume that the only change that can occur is that more
00449 // fields are added.
00450 bool vgui_wx_dialog_impl::has_changed(void) const
00451 {
00452   return last_element_count_ != elements.size();
00453 }
00454 
00455 int vgui_wx_dialog_impl::probe_for_max_label_width(void)
00456 {
00457   wxStaticText temp(dialog_, wxID_ANY, wxString());
00458   int max_width = temp.GetSize().GetX();
00459   for (vcl_vector<element>::const_iterator e = elements.begin();
00460        e != elements.end(); ++e)
00461   {
00462     switch (e->type)
00463     {
00464      case int_elem:
00465      case long_elem:
00466      case float_elem:
00467      case double_elem:
00468      case string_elem:
00469      case choice_elem:
00470       temp.SetLabel(wxString(e->field->label.c_str(),wxConvUTF8));
00471       max_width = vcl_max(max_width, temp.GetSize().GetX());
00472       break;
00473      default: // do nothing
00474       break;
00475     }
00476   }
00477   temp.Show(false);
00478   return max_width;
00479 }
00480 
00481 wxSizer* vgui_wx_dialog_impl::separator_element(int min_width)
00482 {
00483   wxSizer* cell = new wxBoxSizer(wxHORIZONTAL);
00484   cell->Add(new wxStaticLine(dialog_,
00485                              wxID_ANY,
00486                              wxDefaultPosition,
00487                              wxSize(min_width, -1)), 1);
00488   return cell;
00489 }
00490 
00491 wxSizer* vgui_wx_dialog_impl::bool_element(vgui_dialog_field* field)
00492 {
00493   assert(field);
00494 
00495   wxSizer* cell = new wxBoxSizer(wxHORIZONTAL);
00496   bool* var = &dynamic_cast<vgui_bool_field*>(field)->var;
00497   cell->Add(new wxCheckBox(dialog_,
00498                            wxID_ANY,
00499                            wxString(field->label.c_str(),wxConvUTF8),
00500                            wxDefaultPosition,
00501                            wxDefaultSize,
00502                            wxCHK_2STATE,
00503                            wxGenericValidator(var)),
00504             0, wxALIGN_CENTER_VERTICAL);
00505   return cell;
00506 }
00507 
00508 wxSizer* vgui_wx_dialog_impl::choice_element(vgui_dialog_field* field,
00509                                              vgui_wx_dialog_choice* choices)
00510 {
00511   assert(field);
00512   assert(choices);
00513 
00514   wxSizer* cell = new wxBoxSizer(wxHORIZONTAL);
00515   wxStaticText* st = new wxStaticText(dialog_,
00516                                       wxID_STATIC,
00517                                       wxString(field->label.c_str(),wxConvUTF8),
00518                                       wxDefaultPosition,
00519                                       wxSize(max_label_width_, -1),
00520                                       wxALIGN_RIGHT);
00521   cell->Add(st, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 2);
00522 
00523   wxArrayString choice_labels;
00524   for (vcl_vector<vcl_string>::const_iterator label = choices->names.begin();
00525        label != choices->names.end(); ++label)
00526   {
00527     choice_labels.Add(wxString(label->c_str(),wxConvUTF8));
00528   }
00529 
00530   int* var = &dynamic_cast<vgui_int_field*>(field)->var;
00531   wxChoice* wx_choice = new wxChoice(dialog_,
00532                                      wxID_ANY,
00533                                      wxDefaultPosition,
00534                                      wxDefaultSize,
00535                                      choice_labels,
00536                                      0,
00537                                      wxGenericValidator(var));
00538   wx_choice->SetSelection(*var);
00539   cell->Add(wx_choice, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 2);
00540 
00541   return cell;
00542 }
00543 
00544 wxSizer* vgui_wx_dialog_impl::text_element(vgui_dialog_field* field)
00545 {
00546   assert(field);
00547 
00548   wxSizer* cell = new wxBoxSizer(wxHORIZONTAL);
00549 
00550   wxStaticText* st = new wxStaticText(dialog_,
00551                                       wxID_STATIC,
00552                                       wxString(field->label.c_str(),wxConvUTF8),
00553                                       wxDefaultPosition,
00554                                       wxSize(max_label_width_, -1),
00555                                       wxALIGN_RIGHT);
00556   cell->Add(st, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 2);
00557 
00558   wxTextCtrl* t_control;
00559   t_control = new wxTextCtrl(dialog_,
00560                              wxID_ANY,
00561                              wxString(field->current_value().c_str(),wxConvUTF8),
00562                              wxDefaultPosition,
00563                              wxDefaultSize,
00564                              0,
00565                              vgui_wx_text_validator(field));
00566   cell->Add(t_control, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 2);
00567 
00568   return cell;
00569 }
00570 
00571 wxSizer*
00572 vgui_wx_dialog_impl::text_with_button_element(vgui_dialog_field* field,
00573                                               wxTextCtrl*& text_control,
00574                                               const vcl_string& button,
00575                                               int event_id)
00576 {
00577   assert(field);
00578 
00579   text_control = new wxTextCtrl(dialog_,
00580                                 wxID_ANY,
00581                                 wxString(field->current_value().c_str(),wxConvUTF8),
00582                                 wxDefaultPosition,
00583                                 wxDefaultSize,
00584                                 0,
00585                                 vgui_wx_text_validator(field));
00586 
00587   // ***** this constructor not available in wxWidgets-2.5.3
00588   //wxSizer* box = new wxStaticBoxSizer(wxVERTICAL, dialog_, field->label.c_str());
00589   wxSizer* box = new wxStaticBoxSizer(
00590     new wxStaticBox(dialog_, wxID_ANY, wxString(field->label.c_str(),wxConvUTF8)), wxVERTICAL);
00591   box->Add(text_control, 0, wxGROW | wxALL, 2);
00592   box->Add(new wxButton(dialog_, event_id, wxString(button.c_str(),wxConvUTF8)),
00593            0, wxALIGN_RIGHT | wxALL, 2);
00594 
00595   return box;
00596 }
00597 
00598 wxSizer* vgui_wx_dialog_impl::exit_buttons_element(void)
00599 {
00600   wxSizer* button_row = new wxBoxSizer(wxHORIZONTAL);
00601   if (ok_button_text_ != "")
00602   {
00603     button_row->Add(
00604       new wxButton(dialog_, wxID_OK, wxString(ok_button_text_.c_str(),wxConvUTF8)));
00605   }
00606   if (cancel_button_text_ != "")
00607   {
00608     button_row->Add(
00609       new wxButton(dialog_, wxID_CANCEL, wxString(cancel_button_text_.c_str(),wxConvUTF8)));
00610   }
00611   return button_row;
00612 }
00613 
00614 //-------------------------------------------------------------------------
00615 // Private helpers - definitions.
00616 // - Left definition above. Getting error in VS8. (miguelfv)
00617 //-------------------------------------------------------------------------