Assorted GTK3 layout tidying in KDM dialogs.
[dcpomatic.git] / src / wx / recipients_panel.cc
1 /*
2     Copyright (C) 2015-2020 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21 #include "recipients_panel.h"
22 #include "wx_util.h"
23 #include "recipient_dialog.h"
24 #include "dcpomatic_button.h"
25 #include "lib/config.h"
26 #include <boost/foreach.hpp>
27 #include <list>
28 #include <iostream>
29
30 using std::list;
31 using std::pair;
32 using std::cout;
33 using std::map;
34 using std::string;
35 using std::make_pair;
36 using boost::shared_ptr;
37 using boost::optional;
38 using namespace dcpomatic;
39
40 RecipientsPanel::RecipientsPanel (wxWindow* parent)
41         : wxPanel (parent, wxID_ANY)
42         , _ignore_selection_change (false)
43 {
44         wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
45
46 #ifdef __WXGTK3__
47         int const height = 30;
48 #else
49         int const height = -1;
50 #endif
51
52         _search = new wxSearchCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (200, height));
53 #ifndef __WXGTK3__
54         /* The cancel button seems to be strangely broken in GTK3; clicking on it twice sometimes works */
55         _search->ShowCancelButton (true);
56 #endif
57         sizer->Add (_search, 0, wxBOTTOM, DCPOMATIC_SIZER_GAP);
58
59         wxBoxSizer* targets = new wxBoxSizer (wxHORIZONTAL);
60         _targets = new wxTreeCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_HIDE_ROOT | wxTR_MULTIPLE | wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT);
61         targets->Add (_targets, 1, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
62
63         add_recipients ();
64
65         wxBoxSizer* target_buttons = new wxBoxSizer (wxVERTICAL);
66
67         _add_recipient = new Button (this, _("Add..."));
68         target_buttons->Add (_add_recipient, 1, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
69         _edit_recipient = new Button (this, _("Edit..."));
70         target_buttons->Add (_edit_recipient, 1, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
71         _remove_recipient = new Button (this, _("Remove"));
72         target_buttons->Add (_remove_recipient, 1, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
73
74         targets->Add (target_buttons, 0, 0);
75
76         sizer->Add (targets, 1, wxEXPAND);
77
78         _search->Bind  (wxEVT_TEXT, boost::bind (&RecipientsPanel::search_changed, this));
79         _targets->Bind (wxEVT_TREE_SEL_CHANGED, &RecipientsPanel::selection_changed_shim, this);
80
81         _add_recipient->Bind    (wxEVT_BUTTON, boost::bind (&RecipientsPanel::add_recipient_clicked, this));
82         _edit_recipient->Bind   (wxEVT_BUTTON, boost::bind (&RecipientsPanel::edit_recipient_clicked, this));
83         _remove_recipient->Bind (wxEVT_BUTTON, boost::bind (&RecipientsPanel::remove_recipient_clicked, this));
84
85         SetSizer (sizer);
86 }
87
88
89 RecipientsPanel::~RecipientsPanel ()
90 {
91         _targets->Unbind (wxEVT_TREE_SEL_CHANGED, &RecipientsPanel::selection_changed_shim, this);
92 }
93
94
95 void
96 RecipientsPanel::setup_sensitivity ()
97 {
98         _edit_recipient->Enable (_selected.size() == 1);
99         _remove_recipient->Enable (_selected.size() >= 1);
100 }
101
102
103 void
104 RecipientsPanel::add_recipient (shared_ptr<DKDMRecipient> r)
105 {
106         string search = wx_to_std (_search->GetValue());
107         transform (search.begin(), search.end(), search.begin(), ::tolower);
108
109         if (!search.empty()) {
110                 string name = r->name;
111                 transform (name.begin(), name.end(), name.begin(), ::tolower);
112                 if (name.find(search) == string::npos) {
113                         return;
114                 }
115         }
116
117         _recipients[_targets->AppendItem(_root, std_to_wx(r->name))] = r;
118
119         _targets->SortChildren (_root);
120 }
121
122
123 void
124 RecipientsPanel::add_recipient_clicked ()
125 {
126         RecipientDialog* d = new RecipientDialog (GetParent(), _("Add recipient"));
127         if (d->ShowModal() == wxID_OK) {
128                 shared_ptr<DKDMRecipient> r (new DKDMRecipient(d->name(), d->notes(), d->recipient(), d->emails(), d->utc_offset_hour(), d->utc_offset_minute()));
129                 Config::instance()->add_dkdm_recipient (r);
130                 add_recipient (r);
131         }
132
133         d->Destroy ();
134 }
135
136
137 void
138 RecipientsPanel::edit_recipient_clicked ()
139 {
140         if (_selected.size() != 1) {
141                 return;
142         }
143
144         pair<wxTreeItemId, shared_ptr<DKDMRecipient> > c = *_selected.begin();
145
146         RecipientDialog* d = new RecipientDialog (
147                 GetParent(), _("Edit recipient"), c.second->name, c.second->notes, c.second->emails, c.second->utc_offset_hour, c.second->utc_offset_minute, c.second->recipient
148                 );
149
150         if (d->ShowModal () == wxID_OK) {
151                 c.second->name = d->name ();
152                 c.second->emails = d->emails ();
153                 c.second->notes = d->notes ();
154                 c.second->utc_offset_hour = d->utc_offset_hour ();
155                 c.second->utc_offset_minute = d->utc_offset_minute ();
156                 _targets->SetItemText (c.first, std_to_wx (d->name()));
157                 Config::instance()->changed (Config::DKDM_RECIPIENTS);
158         }
159
160         d->Destroy ();
161 }
162
163
164 void
165 RecipientsPanel::remove_recipient_clicked ()
166 {
167         for (RecipientMap::iterator i = _selected.begin(); i != _selected.end(); ++i) {
168                 Config::instance()->remove_dkdm_recipient (i->second);
169                 _targets->Delete (i->first);
170         }
171
172         selection_changed ();
173 }
174
175
176 list<shared_ptr<DKDMRecipient> >
177 RecipientsPanel::recipients () const
178 {
179         list<shared_ptr<DKDMRecipient> > r;
180
181         for (RecipientMap::const_iterator i = _selected.begin(); i != _selected.end(); ++i) {
182                 r.push_back (i->second);
183         }
184
185         r.sort ();
186         r.unique ();
187
188         return r;
189 }
190
191
192 void
193 RecipientsPanel::selection_changed_shim (wxTreeEvent &)
194 {
195         selection_changed ();
196 }
197
198
199 void
200 RecipientsPanel::selection_changed ()
201 {
202         if (_ignore_selection_change) {
203                 return;
204         }
205
206         wxArrayTreeItemIds s;
207         _targets->GetSelections (s);
208
209         _selected.clear ();
210
211         for (size_t i = 0; i < s.GetCount(); ++i) {
212                 RecipientMap::const_iterator j = _recipients.find (s[i]);
213                 if (j != _recipients.end ()) {
214                         _selected[j->first] = j->second;
215                 }
216         }
217
218         setup_sensitivity ();
219         RecipientsChanged ();
220 }
221
222
223 void
224 RecipientsPanel::add_recipients ()
225 {
226         _root = _targets->AddRoot ("Foo");
227
228         BOOST_FOREACH (shared_ptr<DKDMRecipient> i, Config::instance()->dkdm_recipients()) {
229                 add_recipient (i);
230         }
231 }
232
233
234 void
235 RecipientsPanel::search_changed ()
236 {
237         _targets->DeleteAllItems ();
238         _recipients.clear ();
239
240         add_recipients ();
241
242         _ignore_selection_change = true;
243
244         for (RecipientMap::const_iterator i = _selected.begin(); i != _selected.end(); ++i) {
245                 /* The wxTreeItemIds will now be different, so we must search by recipient */
246                 RecipientMap::const_iterator j = _recipients.begin ();
247                 while (j != _recipients.end() && j->second != i->second) {
248                         ++j;
249                 }
250
251                 if (j != _recipients.end ()) {
252                         _targets->SelectItem (j->first);
253                 }
254         }
255
256         _ignore_selection_change = false;
257 }