X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fwx%2Feditable_list.h;h=f3a0dd957d01a11989640d4f6493df9caad5930f;hb=b7e65adf286ce20918797a06a910ededf8f07b7b;hp=eb06f4cf8869a835273f2c44ac8b2a04941db5ba;hpb=2eab969654eca2ef4b222790c8b730a6eb709565;p=dcpomatic.git diff --git a/src/wx/editable_list.h b/src/wx/editable_list.h index eb06f4cf8..f3a0dd957 100644 --- a/src/wx/editable_list.h +++ b/src/wx/editable_list.h @@ -18,17 +18,62 @@ */ + #ifndef DCPOMATIC_EDITABLE_LIST_H #define DCPOMATIC_EDITABLE_LIST_H + +#include "dcpomatic_button.h" #include "wx_util.h" -#include +#include "lib/scope_guard.h" +#include +LIBDCP_DISABLE_WARNINGS #include -#include +#include +LIBDCP_ENABLE_WARNINGS #include + +class EditableListColumn +{ +public: + EditableListColumn (wxString name_) + : name (name_) + , growable (false) + {} + + EditableListColumn (wxString name_, boost::optional width_, bool growable_) + : name (name_) + , width (width_) + , growable (growable_) + {} + + wxString name; + boost::optional width; + bool growable; +}; + + +namespace EditableListButton +{ + static int constexpr NEW = 0x1; + static int constexpr EDIT = 0x2; + static int constexpr REMOVE = 0x4; +}; + + +enum class EditableListTitle +{ + VISIBLE, + INVISIBLE +}; + + /** @param T type of things being edited. * @param S dialog to edit a thing. + * @param get Function to get a std::vector of the things being edited. + * @param set Function set the things from a a std::vector. + * @param column Function to get the display string for a given column in a given item. */ template class EditableList : public wxPanel @@ -36,62 +81,95 @@ class EditableList : public wxPanel public: EditableList ( wxWindow* parent, - std::vector columns, - boost::function ()> get, - boost::function)> set, - boost::function column, - bool can_edit = true, - bool title = true, - int column_width = 200 + std::vector columns, + std::function ()> get, + std::function)> set, + std::function column, + EditableListTitle title, + int buttons ) : wxPanel (parent) , _get (get) , _set (set) - , _columns (columns.size ()) + , _columns (columns) , _column (column) - , _edit (0) + , _default_width (200) { _sizer = new wxBoxSizer (wxHORIZONTAL); SetSizer (_sizer); long style = wxLC_REPORT | wxLC_SINGLE_SEL; - if (title) { + if (title == EditableListTitle::INVISIBLE) { style |= wxLC_NO_HEADER; } - _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (columns.size() * column_width, 100), style); - for (size_t i = 0; i < columns.size(); ++i) { + int total_width = 0; + for (auto i: _columns) { + total_width += i.width.get_value_or (_default_width); + } + +#ifdef __WXGTK3__ + /* With the GTK3 backend wxListCtrls are hard to pick out from the background of the + * window, so put a border in to help. + */ + auto border = new wxPanel (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_THEME); + _list = new wxListCtrl (border, wxID_ANY, wxDefaultPosition, wxSize(total_width, 100), style); + auto border_sizer = new wxBoxSizer (wxHORIZONTAL); + border_sizer->Add (_list, 1, wxALL | wxEXPAND, 2); + border->SetSizer (border_sizer); +#else + _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize(total_width, 100), style); +#endif + + int j = 0; + for (auto i: _columns) { wxListItem ip; - ip.SetId (i); - ip.SetText (std_to_wx (columns[i])); - ip.SetWidth (column_width); - _list->InsertColumn (i, ip); + ip.SetId (j); + ip.SetText (i.name); + _list->InsertColumn (j, ip); + ++j; } +#ifdef __WXGTK3__ + _sizer->Add (border, 1, wxEXPAND); +#else _sizer->Add (_list, 1, wxEXPAND); +#endif { - wxSizer* s = new wxBoxSizer (wxVERTICAL); - _add = new wxButton (this, wxID_ANY, _("Add...")); - s->Add (_add, 0, wxTOP | wxBOTTOM, 2); - if (can_edit) { - _edit = new wxButton (this, wxID_ANY, _("Edit...")); - s->Add (_edit, 0, wxTOP | wxBOTTOM, 2); + auto s = new wxBoxSizer (wxVERTICAL); + if (buttons & EditableListButton::NEW) { + _add = new Button (this, _("Add...")); + s->Add (_add, 1, wxEXPAND | wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + } + if (buttons & EditableListButton::EDIT) { + _edit = new Button (this, _("Edit...")); + s->Add (_edit, 1, wxEXPAND | wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + } + if (buttons & EditableListButton::REMOVE) { + _remove = new Button (this, _("Remove")); + s->Add (_remove, 1, wxEXPAND | wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); } - _remove = new wxButton (this, wxID_ANY, _("Remove")); - s->Add (_remove, 0, wxTOP | wxBOTTOM, 2); _sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP); } - _add->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::add_clicked, this)); + if (_add) { + _add->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::add_clicked, this)); + } if (_edit) { _edit->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::edit_clicked, this)); } - _remove->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::remove_clicked, this)); + if (_remove) { + _remove->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::remove_clicked, this)); + } _list->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&EditableList::selection_changed, this)); _list->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&EditableList::selection_changed, this)); +#if BOOST_VERSION >= 106100 + _list->Bind (wxEVT_SIZE, boost::bind (&EditableList::resized, this, boost::placeholders::_1)); +#else _list->Bind (wxEVT_SIZE, boost::bind (&EditableList::resized, this, _1)); +#endif refresh (); selection_changed (); @@ -101,9 +179,9 @@ public: { _list->DeleteAllItems (); - std::vector current = _get (); - for (typename std::vector::iterator i = current.begin (); i != current.end(); ++i) { - add_to_control (*i); + auto current = _get (); + for (auto const& i: current) { + add_to_control (i); } } @@ -111,10 +189,10 @@ public: { int item = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); if (item == -1) { - return boost::optional (); + return {}; } - std::vector all = _get (); + auto all = _get (); DCPOMATIC_ASSERT (item >= 0 && item < int (all.size ())); return all[item]; } @@ -135,7 +213,7 @@ private: list_item.SetId (n); _list->InsertItem (list_item); - for (int i = 0; i < _columns; ++i) { + for (size_t i = 0; i < _columns.size(); ++i) { _list->SetItem (n, i, std_to_wx (_column (item, i))); } } @@ -146,7 +224,9 @@ private: if (_edit) { _edit->Enable (i >= 0); } - _remove->Enable (i >= 0); + if (_remove) { + _remove->Enable (i >= 0); + } SelectionChanged (); } @@ -154,18 +234,18 @@ private: void add_clicked () { S* dialog = new S (this); + ScopeGuard sg = [dialog]() { dialog->Destroy(); }; if (dialog->ShowModal() == wxID_OK) { - boost::optional const v = dialog->get (); + auto const v = dialog->get (); + static_assert(std::is_same::type, boost::optional>::value, "get() must return boost::optional"); if (v) { add_to_control (v.get ()); - std::vector all = _get (); + auto all = _get (); all.push_back (v.get ()); _set (all); } } - - dialog->Destroy (); } void edit_clicked () @@ -179,18 +259,19 @@ private: DCPOMATIC_ASSERT (item >= 0 && item < int (all.size ())); S* dialog = new S (this); + ScopeGuard sg = [dialog]() { dialog->Destroy(); }; dialog->set (all[item]); if (dialog->ShowModal() == wxID_OK) { - boost::optional const v = dialog->get (); + auto const v = dialog->get (); + static_assert(std::is_same::type, boost::optional>::value, "get() must return boost::optional"); if (!v) { return; } all[item] = v.get (); } - dialog->Destroy (); - for (int i = 0; i < _columns; ++i) { + for (size_t i = 0; i < _columns.size(); ++i) { _list->SetItem (item, i, std_to_wx (_column (all[item], i))); } @@ -205,7 +286,7 @@ private: } _list->DeleteItem (i); - std::vector all = _get (); + auto all = _get (); all.erase (all.begin() + i); _set (all); @@ -214,23 +295,43 @@ private: void resized (wxSizeEvent& ev) { - int const w = GetSize().GetWidth() / _columns; - for (int i = 0; i < _columns; ++i) { - _list->SetColumnWidth (i, w); + int const w = _list->GetSize().GetWidth() - 2; + + int fixed_width = 0; + int growable = 0; + int j = 0; + for (auto i: _columns) { + fixed_width += i.width.get_value_or (_default_width); + if (!i.growable) { + _list->SetColumnWidth (j, i.width.get_value_or(_default_width)); + } else { + ++growable; + } + ++j; } + + j = 0; + for (auto i: _columns) { + if (i.growable) { + _list->SetColumnWidth (j, i.width.get_value_or(_default_width) + (w - fixed_width) / growable); + } + ++j; + } + ev.Skip (); } - boost::function ()> _get; - boost::function )> _set; - int _columns; - boost::function _column; + std::function ()> _get; + std::function )> _set; + std::vector _columns; + std::function _column; - wxButton* _add; - wxButton* _edit; - wxButton* _remove; + wxButton* _add = nullptr; + wxButton* _edit = nullptr; + wxButton* _remove = nullptr; wxListCtrl* _list; wxBoxSizer* _sizer; + int _default_width; }; #endif