Add user interface for trusted devices.
[dcpomatic.git] / src / wx / editable_list.h
1 /*
2     Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifndef DCPOMATIC_EDITABLE_LIST_H
21 #define DCPOMATIC_EDITABLE_LIST_H
22
23 #include "wx_util.h"
24 #include <wx/wx.h>
25 #include <wx/listctrl.h>
26 #include <boost/function.hpp>
27 #include <vector>
28
29 /** @param T type of things being edited.
30  *  @param S dialog to edit a thing.
31  */
32 template<class T, class S>
33 class EditableList : public wxPanel
34 {
35 public:
36         EditableList (
37                 wxWindow* parent,
38                 std::vector<std::string> columns,
39                 boost::function<std::vector<T> ()> get,
40                 boost::function<void (std::vector<T>)> set,
41                 boost::function<std::string (T, int)> column,
42                 bool can_edit = true
43                 )
44                 : wxPanel (parent)
45                 , _get (get)
46                 , _set (set)
47                 , _columns (columns.size ())
48                 , _column (column)
49                 , _edit (0)
50         {
51                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
52                 SetSizer (s);
53
54                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
55                 table->AddGrowableCol (0, 1);
56                 s->Add (table, 1, wxEXPAND);
57
58                 _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (columns.size() * 200, 100), wxLC_REPORT | wxLC_SINGLE_SEL);
59
60                 for (size_t i = 0; i < columns.size(); ++i) {
61                         wxListItem ip;
62                         ip.SetId (i);
63                         ip.SetText (std_to_wx (columns[i]));
64                         ip.SetWidth (200);
65                         _list->InsertColumn (i, ip);
66                 }
67
68                 table->Add (_list, 1, wxEXPAND | wxALL);
69
70                 {
71                         wxSizer* s = new wxBoxSizer (wxVERTICAL);
72                         _add = new wxButton (this, wxID_ANY, _("Add..."));
73                         s->Add (_add, 0, wxTOP | wxBOTTOM, 2);
74                         if (can_edit) {
75                                 _edit = new wxButton (this, wxID_ANY, _("Edit..."));
76                                 s->Add (_edit, 0, wxTOP | wxBOTTOM, 2);
77                         }
78                         _remove = new wxButton (this, wxID_ANY, _("Remove"));
79                         s->Add (_remove, 0, wxTOP | wxBOTTOM, 2);
80                         table->Add (s, 0);
81                 }
82
83                 _add->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::add_clicked, this));
84                 if (_edit) {
85                         _edit->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::edit_clicked, this));
86                 }
87                 _remove->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::remove_clicked, this));
88
89                 _list->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&EditableList::selection_changed, this));
90                 _list->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&EditableList::selection_changed, this));
91                 _list->Bind (wxEVT_SIZE, boost::bind (&EditableList::resized, this, _1));
92
93                 refresh ();
94                 selection_changed ();
95         }
96
97         void refresh ()
98         {
99                 _list->DeleteAllItems ();
100
101                 std::vector<T> current = _get ();
102                 for (typename std::vector<T>::iterator i = current.begin (); i != current.end(); ++i) {
103                         add_to_control (*i);
104                 }
105         }
106
107 private:
108
109         void add_to_control (T item)
110         {
111                 wxListItem list_item;
112                 int const n = _list->GetItemCount ();
113                 list_item.SetId (n);
114                 _list->InsertItem (list_item);
115
116                 for (int i = 0; i < _columns; ++i) {
117                         _list->SetItem (n, i, std_to_wx (_column (item, i)));
118                 }
119         }
120
121         void selection_changed ()
122         {
123                 int const i = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
124                 if (_edit) {
125                         _edit->Enable (i >= 0);
126                 }
127                 _remove->Enable (i >= 0);
128         }
129
130         void add_clicked ()
131         {
132                 T new_item;
133                 S* dialog = new S (this);
134                 dialog->set (new_item);
135
136                 if (dialog->ShowModal() == wxID_OK) {
137                         add_to_control (dialog->get ());
138                         std::vector<T> all = _get ();
139                         all.push_back (dialog->get ());
140                         _set (all);
141                 }
142
143                 dialog->Destroy ();
144         }
145
146         void edit_clicked ()
147         {
148                 int item = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
149                 if (item == -1) {
150                         return;
151                 }
152
153                 std::vector<T> all = _get ();
154                 DCPOMATIC_ASSERT (item >= 0 && item < int (all.size ()));
155
156                 S* dialog = new S (this);
157                 dialog->set (all[item]);
158                 if (dialog->ShowModal() == wxID_OK) {
159                         all[item] = dialog->get ();
160                 }
161                 dialog->Destroy ();
162
163                 for (int i = 0; i < _columns; ++i) {
164                         _list->SetItem (item, i, std_to_wx (_column (all[item], i)));
165                 }
166
167                 _set (all);
168         }
169
170         void remove_clicked ()
171         {
172                 int i = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
173                 if (i == -1) {
174                         return;
175                 }
176
177                 _list->DeleteItem (i);
178                 std::vector<T> all = _get ();
179                 all.erase (all.begin() + i);
180                 _set (all);
181
182                 selection_changed ();
183         }
184
185         void resized (wxSizeEvent& ev)
186         {
187                 int const w = GetSize().GetWidth() / _columns;
188                 for (int i = 0; i < _columns; ++i) {
189                         _list->SetColumnWidth (i, w);
190                 }
191                 ev.Skip ();
192         }
193
194         boost::function <std::vector<T> ()> _get;
195         boost::function <void (std::vector<T>)> _set;
196         int _columns;
197         boost::function<std::string (T, int)> _column;
198
199         wxButton* _add;
200         wxButton* _edit;
201         wxButton* _remove;
202         wxListCtrl* _list;
203 };
204
205 #endif