/[svn]/gigedit/trunk/src/gigedit/MacrosSetup.cpp
ViewVC logotype

Annotation of /gigedit/trunk/src/gigedit/MacrosSetup.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3157 - (hide annotations) (download)
Mon May 8 17:30:10 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 13639 byte(s)
* Fixed potentially unhandled exceptions when app is loading
  its config file.
* WIP: Introduced user configurable list of macros which are
  auto assigned to keyboard accelerators (F1 ... F12) and
  saved along with the app's config file (added under new
  main menu bar section "Macro").
* Bumped version (1.0.0.svn38).

1 schoenebeck 3157 /*
2     Copyright (c) MMXVII Christian Schoenebeck
3    
4     This file is part of "gigedit" and released under the terms of the
5     GNU General Public License version 2.
6     */
7    
8     #include "MacrosSetup.h"
9     #include "global.h"
10     #include <assert.h>
11     #include <set>
12    
13     MacrosSetup::MacrosSetup() :
14     m_statusLabel("", Gtk::ALIGN_START),
15     m_deleteButton(Glib::ustring(_("Delete")) + " " + UNICODE_PRIMARY_KEY_SYMBOL + UNICODE_ERASE_KEY_SYMBOL),
16     m_inverseDeleteButton(Glib::ustring(_("Inverse Delete")) + " " + UNICODE_ALT_KEY_SYMBOL + UNICODE_ERASE_KEY_SYMBOL),
17     m_applyButton(_("_Apply"), true),
18     m_cancelButton(_("_Cancel"), true),
19     m_altKeyDown(false)
20     {
21     add(m_vbox);
22    
23     set_title(_("Setup Macros"));
24    
25     set_default_size(800, 600);
26    
27     // create Macro list treeview (including its data model)
28     m_treeStoreMacros = MacroListTreeStore::create(m_treeModelMacros);
29     m_treeViewMacros.set_model(m_treeStoreMacros);
30     m_treeViewMacros.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
31     //m_treeViewMacro.set_tooltip_text(_(""));
32     m_treeViewMacros.append_column(_("Key"), m_treeModelMacros.m_col_key);
33     m_treeViewMacros.append_column_editable(_("Name"), m_treeModelMacros.m_col_name);
34     m_treeViewMacros.append_column(_("Created"), m_treeModelMacros.m_col_created);
35     m_treeViewMacros.append_column(_("Modified"), m_treeModelMacros.m_col_modified);
36     m_treeViewMacros.set_tooltip_column(m_treeModelMacros.m_col_comment.index());
37     // make all rows gray text, except of "Name" column
38     for (int i = 0; i <= 3; ++i) {
39     if (i == m_treeModelMacros.m_col_name.index())
40     continue;
41     Gtk::TreeViewColumn* column = m_treeViewMacros.get_column(i);
42     Gtk::CellRendererText* cellrenderer =
43     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
44     cellrenderer->property_foreground().set_value("#bababa");
45     }
46     /*{
47     Gtk::TreeViewColumn* column = m_treeViewMacro.get_column(0);
48     Gtk::CellRendererText* cellrenderer =
49     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
50     column->add_attribute(
51     cellrenderer->property_foreground(), m_SamplesModel.m_color
52     );
53     }*/
54     /*{
55     Gtk::TreeViewColumn* column = m_treeViewMacro.get_column(1);
56     Gtk::CellRendererText* cellrenderer =
57     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
58     column->add_attribute(
59     cellrenderer->property_foreground(), m_SamplesModel.m_color
60     );
61     }*/
62     m_treeViewMacros.set_headers_visible(true);
63     m_treeViewMacros.get_selection()->signal_changed().connect(
64     sigc::mem_fun(*this, &MacrosSetup::onTreeViewSelectionChanged)
65     );
66     /*m_treeViewMacros.signal_key_release_event().connect_notify(
67     sigc::mem_fun(*this, &MacrosSetup::onMacroTreeViewKeyRelease)
68     );*/
69     m_treeStoreMacros->signal_row_changed().connect(
70     sigc::mem_fun(*this, &MacrosSetup::onMacroTreeViewRowValueChanged)
71     );
72     m_ignoreTreeViewValueChange = false;
73    
74     m_scrolledWindow.add(m_treeViewMacros);
75     m_scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
76     m_vbox.pack_start(m_scrolledWindow);
77    
78     m_buttonBoxL.set_layout(Gtk::BUTTONBOX_START);
79     m_buttonBoxL.pack_start(m_deleteButton);
80     m_buttonBoxL.pack_start(m_inverseDeleteButton);
81     m_deleteButton.set_sensitive(false);
82     m_inverseDeleteButton.set_sensitive(false);
83    
84     m_buttonBox.set_layout(Gtk::BUTTONBOX_END);
85     m_buttonBox.pack_start(m_applyButton);
86     m_buttonBox.pack_start(m_cancelButton);
87     m_applyButton.set_can_default();
88     m_applyButton.set_sensitive(false);
89     m_applyButton.grab_focus();
90    
91     #if GTKMM_MAJOR_VERSION >= 3
92     m_statusLabel.set_margin_left(6);
93     m_statusLabel.set_margin_right(6);
94     #else
95     m_statusHBox.set_spacing(6);
96     #endif
97    
98     m_statusHBox.pack_start(m_statusLabel);
99     m_statusHBox.show_all_children();
100    
101     m_footerHBox.pack_start(m_buttonBoxL, Gtk::PACK_SHRINK);
102     m_footerHBox.pack_start(m_statusHBox);
103     m_footerHBox.pack_start(m_buttonBox, Gtk::PACK_SHRINK);
104    
105     m_vbox.pack_start(m_footerHBox, Gtk::PACK_SHRINK);
106    
107     m_applyButton.signal_clicked().connect(
108     sigc::mem_fun(*this, &MacrosSetup::onButtonApply)
109     );
110    
111     m_cancelButton.signal_clicked().connect(
112     sigc::mem_fun(*this, &MacrosSetup::onButtonCancel)
113     );
114    
115     m_deleteButton.signal_clicked().connect(
116     sigc::mem_fun(*this, &MacrosSetup::deleteSelectedRows)
117     );
118    
119     m_inverseDeleteButton.signal_clicked().connect(
120     sigc::mem_fun(*this, &MacrosSetup::inverseDeleteSelectedRows)
121     );
122    
123     signal_hide().connect(
124     sigc::mem_fun(*this, &MacrosSetup::onWindowHide)
125     );
126    
127     signal_delete_event().connect(
128     sigc::mem_fun(*this, &MacrosSetup::onWindowDelete)
129     );
130    
131     signal_key_press_event().connect(
132     sigc::mem_fun(*this, &MacrosSetup::onKeyPressed)
133     );
134     signal_key_release_event().connect(
135     sigc::mem_fun(*this, &MacrosSetup::onKeyReleased)
136     );
137    
138     show_all_children();
139     updateStatus();
140     }
141    
142     MacrosSetup::~MacrosSetup() {
143     printf("MacrosSetup destruct\n");
144     }
145    
146     void MacrosSetup::setMacros(const std::vector<Serialization::Archive>& macros) {
147     // copy for non-destructive editing
148     m_macros = macros;
149    
150     reloadTreeView();
151     }
152    
153     static Glib::ustring indexToAccKey(uint index) {
154     if (index >= 12) return "";
155     return "F" + ToString(index+1);
156     }
157    
158     static Glib::ustring humanShortStr(const tm& t) {
159     char buf[70];
160     int daysAgo;
161     if (daysAgo = 0) {
162     // C-Time specification for a time somewhere today (see 'man strftime()').
163     if (strftime(buf, sizeof buf, _("%R"), &t))
164     return buf;
165     } else if (daysAgo = 1) {
166     // C-Time specification for a time somewhere yesterday (see 'man strftime()').
167     if (strftime(buf, sizeof buf, _("Yesterday %R"), &t))
168     return buf;
169     } else if (daysAgo = 2) {
170     // C-Time specification for a time somewhere 2 days ago (see 'man strftime()').
171     if (strftime(buf, sizeof buf, _("2 days ago %R"), &t))
172     return buf;
173     } else {
174     // C-Time specification for a time far more than 2 days ago (see 'man strftime()').
175     if (strftime(buf, sizeof buf, "%d %b %Y", &t))
176     return buf;
177     }
178     return "";
179     }
180    
181     void MacrosSetup::reloadTreeView() {
182     m_ignoreTreeViewValueChange = true;
183    
184     m_treeStoreMacros->clear();
185    
186     for (int iMacro = 0; iMacro < m_macros.size(); ++iMacro) {
187     const Serialization::Archive& macro = m_macros[iMacro];
188    
189     Gtk::TreeModel::iterator iter = m_treeStoreMacros->append();
190     Gtk::TreeModel::Row row = *iter;
191     row[m_treeModelMacros.m_col_key] = indexToAccKey(iMacro);
192     row[m_treeModelMacros.m_col_name] = gig_to_utf8(macro.name());
193     row[m_treeModelMacros.m_col_comment] = gig_to_utf8(macro.comment());
194     row[m_treeModelMacros.m_col_created] = humanShortStr(macro.dateTimeCreated());
195     row[m_treeModelMacros.m_col_modified] = humanShortStr(macro.dateTimeModified());
196     row[m_treeModelMacros.m_col_index] = iMacro;
197     }
198    
199     m_treeViewMacros.expand_all();
200    
201     updateStatus();
202    
203     m_ignoreTreeViewValueChange = false;
204     }
205    
206     void MacrosSetup::onTreeViewSelectionChanged() {
207     std::vector<Gtk::TreeModel::Path> v = m_treeViewMacros.get_selection()->get_selected_rows();
208     const bool bValidSelection = !v.empty();
209     m_deleteButton.set_sensitive(bValidSelection);
210     m_inverseDeleteButton.set_sensitive(bValidSelection);
211     }
212    
213     bool MacrosSetup::onKeyPressed(GdkEventKey* key) {
214     //printf("key down 0x%x\n", key->keyval);
215     if (key->keyval == GDK_KEY_Alt_L || key->keyval == GDK_KEY_Alt_R)
216     m_altKeyDown = true;
217     return false;
218     }
219    
220     bool MacrosSetup::onKeyReleased(GdkEventKey* key) {
221     //printf("key up 0x%x\n", key->keyval);
222     if (key->keyval == GDK_KEY_Alt_L || key->keyval == GDK_KEY_Alt_R)
223     m_altKeyDown = false;
224     return false;
225     }
226    
227     void MacrosSetup::onMacroTreeViewKeyRelease(GdkEventKey* key) {
228     if (key->keyval == GDK_KEY_BackSpace || key->keyval == GDK_KEY_Delete) {
229     if (m_altKeyDown)
230     inverseDeleteSelectedRows();
231     else
232     deleteSelectedRows();
233     }
234     }
235    
236     void MacrosSetup::onMacroTreeViewRowValueChanged(const Gtk::TreeModel::Path& path,
237     const Gtk::TreeModel::iterator& iter)
238     {
239     if (m_ignoreTreeViewValueChange) return;
240     if (!iter) return;
241     Gtk::TreeModel::Row row = *iter;
242     Glib::ustring name = row[m_treeModelMacros.m_col_name];
243     int index = row[m_treeModelMacros.m_col_index];
244     m_macros[index].setName(name);
245     //reloadTreeView();
246     }
247    
248     void MacrosSetup::deleteSelectedRows() {
249     Glib::RefPtr<Gtk::TreeSelection> sel = m_treeViewMacros.get_selection();
250     std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
251     deleteRows(rows);
252     }
253    
254     void MacrosSetup::deleteRows(const std::vector<Gtk::TreeModel::Path>& rows) {
255     std::set<int> macros;
256     for (int r = rows.size() - 1; r >= 0; --r) {
257     Gtk::TreeModel::iterator it = m_treeStoreMacros->get_iter(rows[r]);
258     if (!it) continue;
259     Gtk::TreeModel::Row row = *it;
260     macros.insert(
261     row[m_treeModelMacros.m_col_index]
262     );
263     }
264     for (std::set<int>::const_reverse_iterator it = macros.rbegin();
265     it != macros.rend(); ++it)
266     {
267     m_macros.erase(m_macros.begin() + *it);
268     }
269     reloadTreeView();
270     }
271    
272     static bool _onEachTreeRow(const Gtk::TreeModel::Path& input, std::vector<Gtk::TreeModel::Path>* output) {
273     output->push_back(input);
274     return false; // continue walking the tree
275     }
276    
277     void MacrosSetup::inverseDeleteSelectedRows() {
278     // get all rows of tree view
279     std::vector<Gtk::TreeModel::Path> rows;
280     m_treeViewMacros.get_model()->foreach_path(
281     sigc::bind(
282     sigc::ptr_fun(&_onEachTreeRow),
283     &rows
284     )
285     );
286    
287     // erase all entries from "rows" which are currently selected
288     std::vector<Gtk::TreeModel::Path> vSelected = m_treeViewMacros.get_selection()->get_selected_rows();
289     for (int i = rows.size() - 1; i >= 0; --i) {
290     bool bIsSelected = std::find(vSelected.begin(), vSelected.end(),
291     rows[i]) != vSelected.end();
292     if (bIsSelected)
293     rows.erase(rows.begin() + i);
294     }
295    
296     // delete those 'inverse' selected rows
297     deleteRows(rows);
298     }
299    
300     void MacrosSetup::updateStatus() {
301     m_applyButton.set_sensitive(isModified());
302     updateStatusBar();
303     }
304    
305     void MacrosSetup::updateStatusBar() {
306     // update status text
307     std::string txt;
308     m_statusLabel.set_markup(txt);
309     }
310    
311     sigc::signal<void, const std::vector<Serialization::Archive>& >& MacrosSetup::signal_macros_changed()
312     {
313     return m_macros_changed;
314     }
315    
316     bool MacrosSetup::onWindowDelete(GdkEventAny* e) {
317     //printf("onWindowDelete\n");
318    
319     if (!isModified()) return false; // propagate event further (which will close this window)
320    
321     //gchar* msg = g_strdup_printf(_("Apply changes to macro \"%s\" before closing?"),
322     // m_macroOriginal->Name.c_str());
323     gchar* msg = g_strdup_printf(_("Apply changes to macro before closing?"));
324     Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
325     g_free(msg);
326     dialog.set_secondary_text(_("If you close without applying, your changes will be lost."));
327     dialog.add_button(_("Close _Without Applying"), Gtk::RESPONSE_NO);
328     dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
329     dialog.add_button(_("_Apply"), Gtk::RESPONSE_YES);
330     dialog.set_default_response(Gtk::RESPONSE_YES);
331     int response = dialog.run();
332     dialog.hide();
333    
334     // user decided to close this window without saving
335     if (response == Gtk::RESPONSE_NO)
336     return false; // propagate event further (which will close this window)
337    
338     // user cancelled dialog, thus don't close this window
339     if (response == Gtk::RESPONSE_CANCEL) {
340     show();
341     return true; // drop event (prevents closing this window)
342     }
343    
344     // user wants to apply the changes, afterwards close window
345     if (response == Gtk::RESPONSE_YES) {
346     onButtonApply();
347     return false; // propagate event further (which will close this window)
348     }
349    
350     // should never ever make it to this point actually
351     return false;
352     }
353    
354     bool MacrosSetup::isModified() const {
355     bool bModified = false;
356     for (int i = 0; i < m_macros.size(); ++i) {
357     if (m_macros[i].isModified()) {
358     bModified = true;
359     break;
360     }
361     }
362     return bModified;
363     }
364    
365     void MacrosSetup::onButtonCancel() {
366     bool dropEvent = onWindowDelete(NULL);
367     if (dropEvent) return;
368     hide();
369     }
370    
371     void MacrosSetup::onButtonApply() {
372     std::string errorText;
373     try {
374     for (int i = 0; i < m_macros.size(); ++i) {
375     if (!m_macros[i].isModified()) continue;
376     // enforce re-encoding the abstract object model and resetting the
377     // 'modified' state
378     m_macros[i].rawData();
379     }
380     } catch (Serialization::Exception e) {
381     errorText = e.Message;
382     } catch (...) {
383     errorText = _("Unknown exception while applying macro changes");
384     }
385     if (!errorText.empty()) {
386     Glib::ustring txt = _("Couldn't apply macro changes:\n") + errorText;
387     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
388     msg.run();
389     } else {
390     // update MainWindow with edited list of macros
391     m_macros_changed.emit(m_macros);
392     }
393     updateStatus();
394     }
395    
396     void MacrosSetup::onWindowHide() {
397     delete this; // this is the end, my friend
398     }

  ViewVC Help
Powered by ViewVC