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