// Copyright (c) 2010, Amar Takhar // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /// @file preferences_base.cpp /// @brief Base preferences dialogue classes /// @ingroup configuration_ui #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "preferences_base.h" #include "colour_button.h" #include "compat.h" #include "include/aegisub/audio_player.h" #include "include/aegisub/audio_provider.h" #include "libresrc/libresrc.h" #include "main.h" #include "preferences.h" #include "standard_paths.h" #include "video_provider_manager.h" #define OPTION_UPDATER(type, evttype, opt, body) \ class type { \ std::string name; \ Preferences *parent; \ public: \ type(std::string const& n, Preferences *p) : name(n), parent(p) { } \ void operator()(evttype& evt) { \ evt.Skip(); \ parent->SetOption(new agi::opt(name, body)); \ } \ } OPTION_UPDATER(StringUpdater, wxCommandEvent, OptionValueString, STD_STR(evt.GetString())); OPTION_UPDATER(IntUpdater, wxSpinEvent, OptionValueInt, evt.GetInt()); OPTION_UPDATER(IntCBUpdater, wxCommandEvent, OptionValueInt, evt.GetInt()); OPTION_UPDATER(DoubleUpdater, wxSpinEvent, OptionValueDouble, evt.GetInt()); OPTION_UPDATER(BoolUpdater, wxCommandEvent, OptionValueBool, !!evt.GetInt()); class ColourUpdater { const char *name; Preferences *parent; public: ColourUpdater(const char *n = "", Preferences *p = nullptr) : name(n), parent(p) { } void operator()(wxCommandEvent& evt) { ColourButton *btn = static_cast(evt.GetClientData()); if (btn) parent->SetOption(new agi::OptionValueColor(name, btn->GetColor())); evt.Skip(); } }; static void browse_button(wxTextCtrl *ctrl) { wxString def = StandardPaths::DecodePath(ctrl->GetValue()); wxDirDialog dlg(0, _("Please choose the folder:"), def); if (dlg.ShowModal() == wxID_OK) { wxString dir = dlg.GetPath(); if (!dir.empty()) ctrl->SetValue(dir); } } static void font_button(Preferences *parent, wxTextCtrl *name, wxSpinCtrl *size) { wxFont font; font.SetFaceName(name->GetValue()); font.SetPointSize(size->GetValue()); font = wxGetFontFromUser(parent, font); if (font.IsOk()) { name->SetValue(font.GetFaceName()); size->SetValue(font.GetPointSize()); // wxGTK doesn't generate wxEVT_COMMAND_SPINCTRL_UPDATED from SetValue wxSpinEvent evt(wxEVT_COMMAND_SPINCTRL_UPDATED); evt.SetInt(font.GetPointSize()); size->ProcessWindowEvent(evt); } } OptionPage::OptionPage(wxTreebook *book, Preferences *parent, wxString name, int style) : wxScrolled(book, -1, wxDefaultPosition, wxDefaultSize, wxVSCROLL) , sizer(new wxBoxSizer(wxVERTICAL)) , parent(parent) { if (style & PAGE_SUB) book->AddSubPage(this, name, true); else book->AddPage(this, name, true); if (style & PAGE_SCROLL) SetScrollbars(0, 20, 0, 50); else SetScrollbars(0, 0, 0, 0); DisableKeyboardScrolling(); } template void OptionPage::Add(wxSizer *sizer, wxString const& label, T *control) { sizer->Add(new wxStaticText(this, -1, label), 1, wxALIGN_CENTRE_VERTICAL); sizer->Add(control, wxSizerFlags().Expand()); } void OptionPage::CellSkip(wxFlexGridSizer *flex) { flex->AddStretchSpacer(); } wxControl *OptionPage::OptionAdd(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, double min, double max, double inc) { parent->AddChangeableOption(opt_name); const agi::OptionValue *opt = OPT_GET(opt_name); switch (opt->GetType()) { case agi::OptionValue::Type_Bool: { wxCheckBox *cb = new wxCheckBox(this, -1, name); flex->Add(cb, 1, wxEXPAND, 0); cb->SetValue(opt->GetBool()); cb->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, BoolUpdater(opt_name, parent)); return cb; } case agi::OptionValue::Type_Int: { wxSpinCtrl *sc = new wxSpinCtrl(this, -1, wxString::Format("%d", (int)opt->GetInt()), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, opt->GetInt()); sc->Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, IntUpdater(opt_name, parent)); Add(flex, name, sc); return sc; } case agi::OptionValue::Type_Double: { wxSpinCtrlDouble *scd = new wxSpinCtrlDouble(this, -1, wxString::Format("%g", opt->GetDouble()), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, opt->GetDouble(), inc); scd->Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, DoubleUpdater(opt_name, parent)); Add(flex, name, scd); return scd; } case agi::OptionValue::Type_String: { wxTextCtrl *text = new wxTextCtrl(this, -1 , lagi_wxString(opt->GetString())); text->Bind(wxEVT_COMMAND_TEXT_UPDATED, StringUpdater(opt_name, parent)); Add(flex, name, text); return text; } case agi::OptionValue::Type_Color: { ColourButton *cb = new ColourButton(this, -1, wxSize(40,10), opt->GetColor()); cb->Bind(wxEVT_COMMAND_BUTTON_CLICKED, ColourUpdater(opt_name, parent)); Add(flex, name, cb); return cb; } default: throw PreferenceNotSupported("Unsupported type"); } } void OptionPage::OptionChoice(wxFlexGridSizer *flex, const wxString &name, const wxArrayString &choices, const char *opt_name) { parent->AddChangeableOption(opt_name); const agi::OptionValue *opt = OPT_GET(opt_name); wxComboBox *cb = new wxComboBox(this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, choices, wxCB_READONLY | wxCB_DROPDOWN); Add(flex, name, cb); switch (opt->GetType()) { case agi::OptionValue::Type_Int: { int val = opt->GetInt(); cb->Select(val < (int)choices.size() ? val : opt->GetDefaultInt()); cb->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, IntCBUpdater(opt_name, parent)); break; } case agi::OptionValue::Type_String: { wxString val(lagi_wxString(opt->GetString())); if (cb->FindString(val) != wxNOT_FOUND) cb->SetStringSelection(val); else if (!choices.empty()) cb->SetSelection(0); cb->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, StringUpdater(opt_name, parent)); break; } default: throw PreferenceNotSupported("Unsupported type"); } } wxFlexGridSizer* OptionPage::PageSizer(wxString name) { wxSizer *tmp_sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, name); sizer->Add(tmp_sizer, 0,wxEXPAND, 5); wxFlexGridSizer *flex = new wxFlexGridSizer(2,5,5); flex->AddGrowableCol(0,1); tmp_sizer->Add(flex, 1, wxEXPAND, 5); sizer->AddSpacer(8); return flex; } void OptionPage::OptionBrowse(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, wxControl *enabler, bool do_enable) { parent->AddChangeableOption(opt_name); const agi::OptionValue *opt = OPT_GET(opt_name); if (opt->GetType() != agi::OptionValue::Type_String) throw PreferenceIncorrectType("Option must be agi::OptionValue::Type_String for BrowseButton."); wxTextCtrl *text = new wxTextCtrl(this, -1 , opt->GetString()); text->SetMinSize(wxSize(160, -1)); text->Bind(wxEVT_COMMAND_TEXT_UPDATED, StringUpdater(opt_name, parent)); wxButton *browse = new wxButton(this, -1, _("Browse...")); browse->Bind(wxEVT_COMMAND_BUTTON_CLICKED, std::bind(browse_button, text)); wxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL); button_sizer->Add(text, wxSizerFlags(1).Expand()); button_sizer->Add(browse, wxSizerFlags().Expand()); Add(flex, name, button_sizer); if (enabler) { if (do_enable) { EnableIfChecked(enabler, text); EnableIfChecked(enabler, browse); } else { DisableIfChecked(enabler, text); DisableIfChecked(enabler, browse); } } } void OptionPage::OptionFont(wxSizer *sizer, std::string opt_prefix) { const agi::OptionValue *face_opt = OPT_GET(opt_prefix + "Font Face"); const agi::OptionValue *size_opt = OPT_GET(opt_prefix + "Font Size"); parent->AddChangeableOption(face_opt->GetName()); parent->AddChangeableOption(size_opt->GetName()); wxTextCtrl *font_name = new wxTextCtrl(this, -1, face_opt->GetString()); font_name->SetMinSize(wxSize(160, -1)); font_name->Bind(wxEVT_COMMAND_TEXT_UPDATED, StringUpdater(face_opt->GetName().c_str(), parent)); wxSpinCtrl *font_size = new wxSpinCtrl(this, -1, wxString::Format("%d", (int)size_opt->GetInt()), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 3, 42, size_opt->GetInt()); font_size->Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, IntUpdater(size_opt->GetName().c_str(), parent)); wxButton *pick_btn = new wxButton(this, -1, _("Choose...")); pick_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, std::bind(font_button, parent, font_name, font_size)); wxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL); button_sizer->Add(font_name, wxSizerFlags(1).Expand()); button_sizer->Add(pick_btn, wxSizerFlags().Expand()); Add(sizer, _("Font Face"), button_sizer); Add(sizer, _("Font Size"), font_size); } struct disabler { wxControl *ctrl; bool enable; disabler(wxControl *ctrl, bool enable) : ctrl(ctrl), enable(enable) { } void operator()(wxCommandEvent &evt) { ctrl->Enable(!!evt.GetInt() == enable); evt.Skip(); } }; void OptionPage::EnableIfChecked(wxControl *cbx, wxControl *ctrl) { wxCheckBox *cb = dynamic_cast(cbx); if (!cb) return; ctrl->Enable(cb->IsChecked()); cb->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, disabler(ctrl, true)); } void OptionPage::DisableIfChecked(wxControl *cbx, wxControl *ctrl) { wxCheckBox *cb = dynamic_cast(cbx); if (!cb) return; ctrl->Enable(!cb->IsChecked()); cb->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, disabler(ctrl, false)); }