/[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 3160 - (hide annotations) (download)
Mon May 8 21:31:45 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 17601 byte(s)
* Macro Setup (WIP): Implemented adding new macro from
  clipboard or from currently selected DimensionRegion.
* Bumped version (1.0.0.svn40).

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 schoenebeck 3160 #include <math.h>
13     #include <gtkmm/stock.h>
14 schoenebeck 3157
15     MacrosSetup::MacrosSetup() :
16 schoenebeck 3160 m_modified(false),
17     m_clipboardContent(NULL),
18     m_addFromClipboardButton(" " + Glib::ustring(_("From Clipboard")) + " " + UNICODE_PRIMARY_KEY_SYMBOL + "B"),
19     m_addFromSelectionButton(" " + Glib::ustring(_("From Selection")) + " " + UNICODE_PRIMARY_KEY_SYMBOL + "S"),
20 schoenebeck 3157 m_statusLabel("", Gtk::ALIGN_START),
21 schoenebeck 3160 m_deleteButton(Glib::ustring(_("Delete")) + " " + UNICODE_PRIMARY_KEY_SYMBOL + UNICODE_ERASE_KEY_SYMBOL),
22     m_inverseDeleteButton(Glib::ustring(_("Inverse Delete")) + " " + UNICODE_ALT_KEY_SYMBOL + UNICODE_ERASE_KEY_SYMBOL),
23 schoenebeck 3157 m_applyButton(_("_Apply"), true),
24     m_cancelButton(_("_Cancel"), true),
25 schoenebeck 3160 m_altKeyDown(false),
26     m_primaryKeyDown(false)
27 schoenebeck 3157 {
28     add(m_vbox);
29    
30     set_title(_("Setup Macros"));
31    
32     set_default_size(800, 600);
33    
34 schoenebeck 3160 m_addFromClipboardButton.set_image(
35     *new Gtk::Image(Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON)
36     );
37     m_addFromSelectionButton.set_image(
38     *new Gtk::Image(Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON)
39     );
40     m_addFromClipboardButton.set_tooltip_text(_("Create a new macro from the content currently available on the clipboard."));
41     m_addFromSelectionButton.set_tooltip_text(_("Create a new macro from the currently selected dimension region's parameters currently shown on the main window."));
42     m_addHBox.pack_start(m_addFromClipboardButton);
43     m_addHBox.pack_start(m_addFromSelectionButton);
44     m_vbox.pack_start(m_addHBox, Gtk::PACK_SHRINK);
45    
46 schoenebeck 3157 // create Macro list treeview (including its data model)
47     m_treeStoreMacros = MacroListTreeStore::create(m_treeModelMacros);
48     m_treeViewMacros.set_model(m_treeStoreMacros);
49     m_treeViewMacros.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
50     //m_treeViewMacro.set_tooltip_text(_(""));
51     m_treeViewMacros.append_column(_("Key"), m_treeModelMacros.m_col_key);
52     m_treeViewMacros.append_column_editable(_("Name"), m_treeModelMacros.m_col_name);
53     m_treeViewMacros.append_column(_("Created"), m_treeModelMacros.m_col_created);
54     m_treeViewMacros.append_column(_("Modified"), m_treeModelMacros.m_col_modified);
55     m_treeViewMacros.set_tooltip_column(m_treeModelMacros.m_col_comment.index());
56     // make all rows gray text, except of "Name" column
57     for (int i = 0; i <= 3; ++i) {
58     if (i == m_treeModelMacros.m_col_name.index())
59     continue;
60     Gtk::TreeViewColumn* column = m_treeViewMacros.get_column(i);
61     Gtk::CellRendererText* cellrenderer =
62     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
63     cellrenderer->property_foreground().set_value("#bababa");
64     }
65     /*{
66     Gtk::TreeViewColumn* column = m_treeViewMacro.get_column(0);
67     Gtk::CellRendererText* cellrenderer =
68     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
69     column->add_attribute(
70     cellrenderer->property_foreground(), m_SamplesModel.m_color
71     );
72     }*/
73     /*{
74     Gtk::TreeViewColumn* column = m_treeViewMacro.get_column(1);
75     Gtk::CellRendererText* cellrenderer =
76     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
77     column->add_attribute(
78     cellrenderer->property_foreground(), m_SamplesModel.m_color
79     );
80     }*/
81     m_treeViewMacros.set_headers_visible(true);
82     m_treeViewMacros.get_selection()->signal_changed().connect(
83     sigc::mem_fun(*this, &MacrosSetup::onTreeViewSelectionChanged)
84     );
85 schoenebeck 3160 m_treeViewMacros.signal_key_release_event().connect_notify(
86 schoenebeck 3157 sigc::mem_fun(*this, &MacrosSetup::onMacroTreeViewKeyRelease)
87 schoenebeck 3160 );
88 schoenebeck 3157 m_treeStoreMacros->signal_row_changed().connect(
89     sigc::mem_fun(*this, &MacrosSetup::onMacroTreeViewRowValueChanged)
90     );
91     m_ignoreTreeViewValueChange = false;
92    
93     m_scrolledWindow.add(m_treeViewMacros);
94     m_scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
95     m_vbox.pack_start(m_scrolledWindow);
96    
97     m_buttonBoxL.set_layout(Gtk::BUTTONBOX_START);
98     m_buttonBoxL.pack_start(m_deleteButton);
99     m_buttonBoxL.pack_start(m_inverseDeleteButton);
100     m_deleteButton.set_sensitive(false);
101     m_inverseDeleteButton.set_sensitive(false);
102    
103     m_buttonBox.set_layout(Gtk::BUTTONBOX_END);
104     m_buttonBox.pack_start(m_applyButton);
105     m_buttonBox.pack_start(m_cancelButton);
106     m_applyButton.set_can_default();
107     m_applyButton.set_sensitive(false);
108     m_applyButton.grab_focus();
109    
110     #if GTKMM_MAJOR_VERSION >= 3
111     m_statusLabel.set_margin_left(6);
112     m_statusLabel.set_margin_right(6);
113     #else
114     m_statusHBox.set_spacing(6);
115     #endif
116    
117     m_statusHBox.pack_start(m_statusLabel);
118     m_statusHBox.show_all_children();
119    
120     m_footerHBox.pack_start(m_buttonBoxL, Gtk::PACK_SHRINK);
121     m_footerHBox.pack_start(m_statusHBox);
122     m_footerHBox.pack_start(m_buttonBox, Gtk::PACK_SHRINK);
123    
124     m_vbox.pack_start(m_footerHBox, Gtk::PACK_SHRINK);
125    
126 schoenebeck 3160 m_addFromClipboardButton.signal_clicked().connect(
127     sigc::mem_fun(*this, &MacrosSetup::onButtonAddFromClipboard)
128     );
129    
130     m_addFromSelectionButton.signal_clicked().connect(
131     sigc::mem_fun(*this, &MacrosSetup::onButtonAddFromSelection)
132     );
133    
134 schoenebeck 3157 m_applyButton.signal_clicked().connect(
135     sigc::mem_fun(*this, &MacrosSetup::onButtonApply)
136     );
137    
138     m_cancelButton.signal_clicked().connect(
139     sigc::mem_fun(*this, &MacrosSetup::onButtonCancel)
140     );
141    
142     m_deleteButton.signal_clicked().connect(
143     sigc::mem_fun(*this, &MacrosSetup::deleteSelectedRows)
144     );
145    
146     m_inverseDeleteButton.signal_clicked().connect(
147     sigc::mem_fun(*this, &MacrosSetup::inverseDeleteSelectedRows)
148     );
149    
150     signal_hide().connect(
151     sigc::mem_fun(*this, &MacrosSetup::onWindowHide)
152     );
153    
154     signal_delete_event().connect(
155     sigc::mem_fun(*this, &MacrosSetup::onWindowDelete)
156     );
157    
158     signal_key_press_event().connect(
159     sigc::mem_fun(*this, &MacrosSetup::onKeyPressed)
160     );
161     signal_key_release_event().connect(
162     sigc::mem_fun(*this, &MacrosSetup::onKeyReleased)
163     );
164    
165     show_all_children();
166     updateStatus();
167     }
168    
169     MacrosSetup::~MacrosSetup() {
170     printf("MacrosSetup destruct\n");
171     }
172    
173 schoenebeck 3160 void MacrosSetup::setMacros(const std::vector<Serialization::Archive>& macros,
174     Serialization::Archive* pClipboardContent,
175     gig::DimensionRegion* pSelectedDimRgn)
176     {
177 schoenebeck 3157 // copy for non-destructive editing
178     m_macros = macros;
179    
180 schoenebeck 3160 m_clipboardContent = pClipboardContent;
181     m_selectedDimRgn = pSelectedDimRgn;
182    
183 schoenebeck 3157 reloadTreeView();
184     }
185    
186 schoenebeck 3160 void MacrosSetup::onButtonAddFromClipboard() {
187     printf("+fromClipboard\n");
188     if (!m_clipboardContent) return;
189     if (!m_clipboardContent->rootObject()) return;
190     m_macros.push_back(*m_clipboardContent);
191     m_modified = true;
192     reloadTreeView();
193     }
194    
195     void MacrosSetup::onButtonAddFromSelection() {
196     printf("+fromSelection\n");
197     if (!m_selectedDimRgn) return;
198     std::string errorText;
199     try {
200     Serialization::Archive archive;
201     archive.serialize(m_selectedDimRgn);
202     //archive.setName("Unnamed Macro");
203     m_macros.push_back(archive);
204     m_modified = true;
205     reloadTreeView();
206     } catch (Serialization::Exception e) {
207     errorText = e.Message;
208     } catch (...) {
209     errorText = _("Unknown exception while creating macro");
210     }
211     if (!errorText.empty()) {
212     Glib::ustring txt = _("Couldn't create macro:\n") + errorText;
213     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
214     msg.run();
215     }
216     }
217    
218 schoenebeck 3157 static Glib::ustring indexToAccKey(uint index) {
219     if (index >= 12) return "";
220     return "F" + ToString(index+1);
221     }
222    
223 schoenebeck 3160 static int daysAgo(const tm& t) {
224     time_t now;
225     time(&now);
226     tm* pNow = localtime(&now);
227     if (!pNow) return 0;
228     if (pNow->tm_year == t.tm_year &&
229     pNow->tm_mon == t.tm_mon &&
230     pNow->tm_mday == t.tm_mday) return 0;
231     time_t past = mktime((tm*)&t);
232     return ceil(difftime(now, past));
233     }
234    
235 schoenebeck 3157 static Glib::ustring humanShortStr(const tm& t) {
236 schoenebeck 3160 int iDaysAgo = daysAgo(t);
237 schoenebeck 3157 char buf[70];
238 schoenebeck 3160 if (iDaysAgo == 0) {
239 schoenebeck 3157 // C-Time specification for a time somewhere today (see 'man strftime()').
240     if (strftime(buf, sizeof buf, _("%R"), &t))
241     return buf;
242 schoenebeck 3160 } else if (iDaysAgo == 1) {
243 schoenebeck 3157 // C-Time specification for a time somewhere yesterday (see 'man strftime()').
244     if (strftime(buf, sizeof buf, _("Yesterday %R"), &t))
245     return buf;
246 schoenebeck 3160 } else if (iDaysAgo == 2) {
247 schoenebeck 3157 // C-Time specification for a time somewhere 2 days ago (see 'man strftime()').
248     if (strftime(buf, sizeof buf, _("2 days ago %R"), &t))
249     return buf;
250     } else {
251     // C-Time specification for a time far more than 2 days ago (see 'man strftime()').
252     if (strftime(buf, sizeof buf, "%d %b %Y", &t))
253     return buf;
254     }
255     return "";
256     }
257    
258     void MacrosSetup::reloadTreeView() {
259     m_ignoreTreeViewValueChange = true;
260    
261     m_treeStoreMacros->clear();
262    
263     for (int iMacro = 0; iMacro < m_macros.size(); ++iMacro) {
264     const Serialization::Archive& macro = m_macros[iMacro];
265    
266     Gtk::TreeModel::iterator iter = m_treeStoreMacros->append();
267     Gtk::TreeModel::Row row = *iter;
268     row[m_treeModelMacros.m_col_key] = indexToAccKey(iMacro);
269 schoenebeck 3160 row[m_treeModelMacros.m_col_name] = macro.name().empty() ? _("Unnamed Macro") : gig_to_utf8(macro.name());
270     row[m_treeModelMacros.m_col_comment] = macro.comment().empty() ? _("No comment assigned yet.") : gig_to_utf8(macro.comment());
271 schoenebeck 3157 row[m_treeModelMacros.m_col_created] = humanShortStr(macro.dateTimeCreated());
272     row[m_treeModelMacros.m_col_modified] = humanShortStr(macro.dateTimeModified());
273     row[m_treeModelMacros.m_col_index] = iMacro;
274     }
275    
276     m_treeViewMacros.expand_all();
277    
278     updateStatus();
279    
280     m_ignoreTreeViewValueChange = false;
281     }
282    
283     void MacrosSetup::onTreeViewSelectionChanged() {
284     std::vector<Gtk::TreeModel::Path> v = m_treeViewMacros.get_selection()->get_selected_rows();
285     const bool bValidSelection = !v.empty();
286     m_deleteButton.set_sensitive(bValidSelection);
287     m_inverseDeleteButton.set_sensitive(bValidSelection);
288     }
289    
290 schoenebeck 3160 // Cmd key on Mac, Ctrl key on all other OSs
291     static const guint primaryKeyL =
292     #if defined(__APPLE__)
293     GDK_KEY_Meta_L;
294     #else
295     GDK_KEY_Control_L;
296     #endif
297    
298     static const guint primaryKeyR =
299     #if defined(__APPLE__)
300     GDK_KEY_Meta_R;
301     #else
302     GDK_KEY_Control_R;
303     #endif
304    
305 schoenebeck 3157 bool MacrosSetup::onKeyPressed(GdkEventKey* key) {
306     //printf("key down 0x%x\n", key->keyval);
307     if (key->keyval == GDK_KEY_Alt_L || key->keyval == GDK_KEY_Alt_R)
308     m_altKeyDown = true;
309 schoenebeck 3160 if (key->keyval == primaryKeyL || key->keyval == primaryKeyR)
310     m_primaryKeyDown = true;
311 schoenebeck 3157 return false;
312     }
313    
314     bool MacrosSetup::onKeyReleased(GdkEventKey* key) {
315     //printf("key up 0x%x\n", key->keyval);
316     if (key->keyval == GDK_KEY_Alt_L || key->keyval == GDK_KEY_Alt_R)
317     m_altKeyDown = false;
318 schoenebeck 3160 if (key->keyval == primaryKeyL || key->keyval == primaryKeyR)
319     m_primaryKeyDown = false;
320     if (m_primaryKeyDown && key->keyval == GDK_KEY_b)
321     onButtonAddFromClipboard();
322     if (m_primaryKeyDown && key->keyval == GDK_KEY_s)
323     onButtonAddFromSelection();
324 schoenebeck 3157 return false;
325     }
326    
327     void MacrosSetup::onMacroTreeViewKeyRelease(GdkEventKey* key) {
328     if (key->keyval == GDK_KEY_BackSpace || key->keyval == GDK_KEY_Delete) {
329     if (m_altKeyDown)
330     inverseDeleteSelectedRows();
331 schoenebeck 3160 else if (m_primaryKeyDown)
332 schoenebeck 3157 deleteSelectedRows();
333     }
334     }
335    
336     void MacrosSetup::onMacroTreeViewRowValueChanged(const Gtk::TreeModel::Path& path,
337     const Gtk::TreeModel::iterator& iter)
338     {
339     if (m_ignoreTreeViewValueChange) return;
340     if (!iter) return;
341     Gtk::TreeModel::Row row = *iter;
342     Glib::ustring name = row[m_treeModelMacros.m_col_name];
343     int index = row[m_treeModelMacros.m_col_index];
344     m_macros[index].setName(name);
345     //reloadTreeView();
346 schoenebeck 3160 m_modified = true;
347     updateStatus();
348 schoenebeck 3157 }
349    
350     void MacrosSetup::deleteSelectedRows() {
351     Glib::RefPtr<Gtk::TreeSelection> sel = m_treeViewMacros.get_selection();
352     std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
353     deleteRows(rows);
354     }
355    
356     void MacrosSetup::deleteRows(const std::vector<Gtk::TreeModel::Path>& rows) {
357 schoenebeck 3160 m_modified = !rows.empty();
358 schoenebeck 3157 std::set<int> macros;
359     for (int r = rows.size() - 1; r >= 0; --r) {
360     Gtk::TreeModel::iterator it = m_treeStoreMacros->get_iter(rows[r]);
361     if (!it) continue;
362     Gtk::TreeModel::Row row = *it;
363     macros.insert(
364     row[m_treeModelMacros.m_col_index]
365     );
366     }
367     for (std::set<int>::const_reverse_iterator it = macros.rbegin();
368     it != macros.rend(); ++it)
369     {
370     m_macros.erase(m_macros.begin() + *it);
371     }
372     reloadTreeView();
373     }
374    
375     static bool _onEachTreeRow(const Gtk::TreeModel::Path& input, std::vector<Gtk::TreeModel::Path>* output) {
376     output->push_back(input);
377     return false; // continue walking the tree
378     }
379    
380     void MacrosSetup::inverseDeleteSelectedRows() {
381     // get all rows of tree view
382     std::vector<Gtk::TreeModel::Path> rows;
383     m_treeViewMacros.get_model()->foreach_path(
384     sigc::bind(
385     sigc::ptr_fun(&_onEachTreeRow),
386     &rows
387     )
388     );
389    
390     // erase all entries from "rows" which are currently selected
391     std::vector<Gtk::TreeModel::Path> vSelected = m_treeViewMacros.get_selection()->get_selected_rows();
392     for (int i = rows.size() - 1; i >= 0; --i) {
393     bool bIsSelected = std::find(vSelected.begin(), vSelected.end(),
394     rows[i]) != vSelected.end();
395     if (bIsSelected)
396     rows.erase(rows.begin() + i);
397     }
398    
399     // delete those 'inverse' selected rows
400     deleteRows(rows);
401     }
402    
403     void MacrosSetup::updateStatus() {
404 schoenebeck 3160 m_addFromClipboardButton.set_sensitive(
405     m_clipboardContent && m_clipboardContent->rootObject()
406     );
407     m_addFromSelectionButton.set_sensitive(m_selectedDimRgn);
408 schoenebeck 3157 m_applyButton.set_sensitive(isModified());
409     updateStatusBar();
410     }
411    
412     void MacrosSetup::updateStatusBar() {
413     // update status text
414     std::string txt;
415     m_statusLabel.set_markup(txt);
416     }
417    
418     sigc::signal<void, const std::vector<Serialization::Archive>& >& MacrosSetup::signal_macros_changed()
419     {
420     return m_macros_changed;
421     }
422    
423     bool MacrosSetup::onWindowDelete(GdkEventAny* e) {
424     //printf("onWindowDelete\n");
425    
426     if (!isModified()) return false; // propagate event further (which will close this window)
427    
428     //gchar* msg = g_strdup_printf(_("Apply changes to macro \"%s\" before closing?"),
429     // m_macroOriginal->Name.c_str());
430 schoenebeck 3160 gchar* msg = g_strdup_printf(_("Apply changes to macro list before closing?"));
431 schoenebeck 3157 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
432     g_free(msg);
433     dialog.set_secondary_text(_("If you close without applying, your changes will be lost."));
434     dialog.add_button(_("Close _Without Applying"), Gtk::RESPONSE_NO);
435     dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
436     dialog.add_button(_("_Apply"), Gtk::RESPONSE_YES);
437     dialog.set_default_response(Gtk::RESPONSE_YES);
438     int response = dialog.run();
439     dialog.hide();
440    
441     // user decided to close this window without saving
442     if (response == Gtk::RESPONSE_NO)
443     return false; // propagate event further (which will close this window)
444    
445     // user cancelled dialog, thus don't close this window
446     if (response == Gtk::RESPONSE_CANCEL) {
447     show();
448     return true; // drop event (prevents closing this window)
449     }
450    
451     // user wants to apply the changes, afterwards close window
452     if (response == Gtk::RESPONSE_YES) {
453     onButtonApply();
454     return false; // propagate event further (which will close this window)
455     }
456    
457     // should never ever make it to this point actually
458     return false;
459     }
460    
461     bool MacrosSetup::isModified() const {
462 schoenebeck 3160 if (m_modified) return true;
463 schoenebeck 3157 bool bModified = false;
464     for (int i = 0; i < m_macros.size(); ++i) {
465     if (m_macros[i].isModified()) {
466     bModified = true;
467     break;
468     }
469     }
470     return bModified;
471     }
472    
473     void MacrosSetup::onButtonCancel() {
474     bool dropEvent = onWindowDelete(NULL);
475     if (dropEvent) return;
476     hide();
477     }
478    
479     void MacrosSetup::onButtonApply() {
480     std::string errorText;
481     try {
482     for (int i = 0; i < m_macros.size(); ++i) {
483     if (!m_macros[i].isModified()) continue;
484     // enforce re-encoding the abstract object model and resetting the
485     // 'modified' state
486     m_macros[i].rawData();
487     }
488 schoenebeck 3160 m_modified = false;
489 schoenebeck 3157 } catch (Serialization::Exception e) {
490     errorText = e.Message;
491     } catch (...) {
492     errorText = _("Unknown exception while applying macro changes");
493     }
494     if (!errorText.empty()) {
495     Glib::ustring txt = _("Couldn't apply macro changes:\n") + errorText;
496     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
497     msg.run();
498     } else {
499     // update MainWindow with edited list of macros
500     m_macros_changed.emit(m_macros);
501     }
502     updateStatus();
503     }
504    
505     void MacrosSetup::onWindowHide() {
506     delete this; // this is the end, my friend
507     }

  ViewVC Help
Powered by ViewVC