2 Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
21 #ifndef DCPOMATIC_EDITABLE_LIST_H
22 #define DCPOMATIC_EDITABLE_LIST_H
25 #include "dcpomatic_button.h"
27 #include <wx/listctrl.h>
28 #include <boost/function.hpp>
31 /** @param T type of things being edited.
32 * @param S dialog to edit a thing.
34 template<class T, class S>
35 class EditableList : public wxPanel
40 std::vector<std::string> columns,
41 boost::function<std::vector<T> ()> get,
42 boost::function<void (std::vector<T>)> set,
43 boost::function<std::string (T, int)> column,
46 int column_width = 200
51 , _columns (columns.size ())
55 _sizer = new wxBoxSizer (wxHORIZONTAL);
58 long style = wxLC_REPORT | wxLC_SINGLE_SEL;
60 style |= wxLC_NO_HEADER;
62 _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (columns.size() * column_width, 100), style);
64 for (size_t i = 0; i < columns.size(); ++i) {
67 ip.SetText (std_to_wx (columns[i]));
68 ip.SetWidth (column_width);
69 _list->InsertColumn (i, ip);
72 _sizer->Add (_list, 1, wxEXPAND);
75 wxSizer* s = new wxBoxSizer (wxVERTICAL);
76 _add = new Button (this, _("Add..."));
77 s->Add (_add, 0, wxTOP | wxBOTTOM, 2);
79 _edit = new Button (this, _("Edit..."));
80 s->Add (_edit, 0, wxTOP | wxBOTTOM, 2);
82 _remove = new Button (this, _("Remove"));
83 s->Add (_remove, 0, wxTOP | wxBOTTOM, 2);
84 _sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
87 _add->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::add_clicked, this));
89 _edit->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::edit_clicked, this));
91 _remove->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::remove_clicked, this));
93 _list->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&EditableList::selection_changed, this));
94 _list->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&EditableList::selection_changed, this));
95 #if BOOST_VERSION >= 106100
96 _list->Bind (wxEVT_SIZE, boost::bind (&EditableList::resized, this, boost::placeholders::_1));
98 _list->Bind (wxEVT_SIZE, boost::bind (&EditableList::resized, this, _1));
102 selection_changed ();
107 _list->DeleteAllItems ();
109 std::vector<T> current = _get ();
110 for (typename std::vector<T>::iterator i = current.begin (); i != current.end(); ++i) {
115 boost::optional<T> selection () const
117 int item = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
119 return boost::optional<T> ();
122 std::vector<T> all = _get ();
123 DCPOMATIC_ASSERT (item >= 0 && item < int (all.size ()));
132 boost::signals2::signal<void ()> SelectionChanged;
136 void add_to_control (T item)
138 wxListItem list_item;
139 int const n = _list->GetItemCount ();
141 _list->InsertItem (list_item);
143 for (int i = 0; i < _columns; ++i) {
144 _list->SetItem (n, i, std_to_wx (_column (item, i)));
148 void selection_changed ()
150 int const i = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
152 _edit->Enable (i >= 0);
154 _remove->Enable (i >= 0);
161 S* dialog = new S (this);
163 if (dialog->ShowModal() == wxID_OK) {
164 boost::optional<T> const v = dialog->get ();
166 add_to_control (v.get ());
167 std::vector<T> all = _get ();
168 all.push_back (v.get ());
178 int item = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
183 std::vector<T> all = _get ();
184 DCPOMATIC_ASSERT (item >= 0 && item < int (all.size ()));
186 S* dialog = new S (this);
187 dialog->set (all[item]);
188 if (dialog->ShowModal() == wxID_OK) {
189 boost::optional<T> const v = dialog->get ();
194 all[item] = v.get ();
198 for (int i = 0; i < _columns; ++i) {
199 _list->SetItem (item, i, std_to_wx (_column (all[item], i)));
205 void remove_clicked ()
207 int i = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
212 _list->DeleteItem (i);
213 std::vector<T> all = _get ();
214 all.erase (all.begin() + i);
217 selection_changed ();
220 void resized (wxSizeEvent& ev)
222 int const w = GetSize().GetWidth() / _columns;
223 for (int i = 0; i < _columns; ++i) {
224 _list->SetColumnWidth (i, w);
229 boost::function <std::vector<T> ()> _get;
230 boost::function <void (std::vector<T>)> _set;
232 boost::function<std::string (T, int)> _column;