/[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 3364 - (hide annotations) (download)
Tue Nov 14 18:07:25 2017 UTC (6 years, 4 months ago) by schoenebeck
File size: 30336 byte(s)
* Added experimental support for upcoming GTK(MM)4
  (for now up to GTKMM 3.91.2 while still preserving backward compatibility
  down to GTKMM 2).
* Re-merged r2845 to compile now with and without Gtk "Stock ID" API
  (see also r3158).

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 schoenebeck 3364 #include "compat.h"
10 schoenebeck 3157 #include "global.h"
11     #include <assert.h>
12     #include <set>
13 schoenebeck 3160 #include <math.h>
14 schoenebeck 3364 #if HAS_GTKMM_STOCK
15     # include <gtkmm/stock.h>
16     #endif
17 schoenebeck 3162 #include "MacroEditor.h"
18 schoenebeck 3157
19     MacrosSetup::MacrosSetup() :
20 schoenebeck 3160 m_modified(false),
21     m_clipboardContent(NULL),
22     m_addFromClipboardButton(" " + Glib::ustring(_("From Clipboard")) + " " + UNICODE_PRIMARY_KEY_SYMBOL + "B"),
23     m_addFromSelectionButton(" " + Glib::ustring(_("From Selection")) + " " + UNICODE_PRIMARY_KEY_SYMBOL + "S"),
24 schoenebeck 3364 #if HAS_GTKMM_STOCK
25 schoenebeck 3162 m_buttonUp(Gtk::Stock::GO_UP),
26     m_buttonDown(Gtk::Stock::GO_DOWN),
27     m_buttonEdit(Gtk::Stock::EDIT),
28 schoenebeck 3364 #else
29     m_buttonUp(_("Up"), true),
30     m_buttonDown(_("Down"), true),
31     m_buttonEdit(_("Edit"), true),
32     #endif
33 schoenebeck 3176 m_buttonDuplicate(_("Duplicate")),
34 schoenebeck 3157 m_statusLabel("", Gtk::ALIGN_START),
35 schoenebeck 3162 m_labelComment(_("Comment"), Gtk::ALIGN_START),
36     m_deleteButton(" " + Glib::ustring(_("Delete")) + " " + UNICODE_PRIMARY_KEY_SYMBOL + UNICODE_ERASE_KEY_SYMBOL),
37     m_inverseDeleteButton(" " + Glib::ustring(_("Inverse Delete")) + " " + UNICODE_ALT_KEY_SYMBOL + UNICODE_ERASE_KEY_SYMBOL),
38 schoenebeck 3364 #if HAS_GTKMM_STOCK
39 schoenebeck 3162 m_applyButton(Gtk::Stock::APPLY),
40     m_cancelButton(Gtk::Stock::CANCEL),
41 schoenebeck 3364 #else
42     m_applyButton(_("Apply"), true),
43     m_cancelButton(_("_Cancel"), true),
44     #endif
45 schoenebeck 3160 m_altKeyDown(false),
46     m_primaryKeyDown(false)
47 schoenebeck 3157 {
48     add(m_vbox);
49    
50     set_title(_("Setup Macros"));
51    
52 schoenebeck 3364 #if !HAS_GTKMM_STOCK
53     // see : https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
54     m_buttonUp.set_icon_name("go-up");
55     m_buttonDown.set_icon_name("go-down");
56     m_buttonEdit.set_icon_name("insert-text");
57     #endif
58    
59 schoenebeck 3225 if (!Settings::singleton()->autoRestoreWindowDimension) {
60     set_default_size(680, 500);
61     set_position(Gtk::WIN_POS_CENTER);
62     }
63 schoenebeck 3157
64 schoenebeck 3364 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION >= 12)
65     m_labelIntro.set_margin_start(10);
66     m_labelIntro.set_margin_end(10);
67     #else
68 schoenebeck 3184 m_labelIntro.set_padding(10, 10);
69 schoenebeck 3364 #endif
70    
71 schoenebeck 3184 #if GTKMM_MAJOR_VERSION >= 3
72     m_labelIntro.set_line_wrap();
73     #endif
74     m_labelIntro.set_text(
75     _("A macro is a list of parameters and corresponding values which "
76     "should be applied to the instrument editor when the macro is "
77     "triggered by the user. A macro is triggered either by selecting "
78     "the macro from the \"Macro\" menu, or by hitting the macro's "
79     "respective keyboard accelerator (F1 to F12), or by applying a "
80     "previously copied macro from the clipboard.")
81     );
82     m_vbox.pack_start(m_labelIntro, Gtk::PACK_SHRINK);
83    
84 schoenebeck 3364 #if HAS_GTKMM_STOCK
85 schoenebeck 3160 m_addFromClipboardButton.set_image(
86     *new Gtk::Image(Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON)
87     );
88     m_addFromSelectionButton.set_image(
89     *new Gtk::Image(Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON)
90     );
91 schoenebeck 3176 m_buttonDuplicate.set_image(
92     *new Gtk::Image(Gtk::Stock::COPY, Gtk::ICON_SIZE_BUTTON)
93     );
94 schoenebeck 3162 m_deleteButton.set_image(
95     *new Gtk::Image(Gtk::Stock::DELETE, Gtk::ICON_SIZE_BUTTON)
96     );
97     m_inverseDeleteButton.set_image(
98     *new Gtk::Image(Gtk::Stock::DELETE, Gtk::ICON_SIZE_BUTTON)
99     );
100 schoenebeck 3364 #else // since GTKMM 3.90 ...
101     // see : https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
102     m_addFromClipboardButton.set_icon_name("list-add");
103     m_addFromSelectionButton.set_icon_name("list-add");
104     m_buttonDuplicate.set_icon_name("edit-copy");
105     m_deleteButton.set_icon_name("edit-delete");
106     m_inverseDeleteButton.set_icon_name("edit-delete");
107     #endif
108 schoenebeck 3160 m_addFromClipboardButton.set_tooltip_text(_("Create a new macro from the content currently available on the clipboard."));
109     m_addFromSelectionButton.set_tooltip_text(_("Create a new macro from the currently selected dimension region's parameters currently shown on the main window."));
110 schoenebeck 3184 m_buttonDuplicate.set_tooltip_text(_("Duplicate the selected macro(s). The new macro(s) will be appended to the end of the list."));
111     m_buttonUp.set_tooltip_text(
112     _("Move the selected macro up in the list, which changes its order of "
113     "appearance in the main window's \"Macro\" menu and changes to which "
114     "keyboard accelerator key (F1 to F12) the macro is assigned to."));
115     m_buttonDown.set_tooltip_text(
116     _("Move the selected macro down in the list, which changes its order of "
117     "appearance in the main window's \"Macro\" menu and changes to which "
118     "keyboard accelerator key (F1 to F12) the macro is assigned to."));
119     m_buttonEdit.set_tooltip_text(_("Open the Macro Editor for the selected macro."));
120     m_deleteButton.set_tooltip_text(_("Delete the selected macros."));
121     m_inverseDeleteButton.set_tooltip_text(_("Delete all macros except the selected ones."));
122 schoenebeck 3162 m_addHBox.pack_start(m_addFromClipboardButton, Gtk::PACK_EXPAND_WIDGET/*, 15*/);
123     m_addHBox.pack_start(m_addFromSelectionButton, Gtk::PACK_EXPAND_WIDGET/*, 15*/);
124 schoenebeck 3160 m_vbox.pack_start(m_addHBox, Gtk::PACK_SHRINK);
125    
126 schoenebeck 3162 m_vbox.pack_start(m_mainHBox);
127     m_vbox.set_spacing(5);
128    
129 schoenebeck 3157 // create Macro list treeview (including its data model)
130     m_treeStoreMacros = MacroListTreeStore::create(m_treeModelMacros);
131     m_treeViewMacros.set_model(m_treeStoreMacros);
132     m_treeViewMacros.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
133     //m_treeViewMacro.set_tooltip_text(_(""));
134     m_treeViewMacros.append_column(_("Key"), m_treeModelMacros.m_col_key);
135 schoenebeck 3162 m_treeViewMacros.append_column_editable(_("Macro Name"), m_treeModelMacros.m_col_name);
136 schoenebeck 3157 m_treeViewMacros.append_column(_("Created"), m_treeModelMacros.m_col_created);
137     m_treeViewMacros.append_column(_("Modified"), m_treeModelMacros.m_col_modified);
138     m_treeViewMacros.set_tooltip_column(m_treeModelMacros.m_col_comment.index());
139     // make all rows gray text, except of "Name" column
140     for (int i = 0; i <= 3; ++i) {
141     if (i == m_treeModelMacros.m_col_name.index())
142     continue;
143     Gtk::TreeViewColumn* column = m_treeViewMacros.get_column(i);
144     Gtk::CellRendererText* cellrenderer =
145     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
146     cellrenderer->property_foreground().set_value("#bababa");
147     }
148     /*{
149     Gtk::TreeViewColumn* column = m_treeViewMacro.get_column(0);
150     Gtk::CellRendererText* cellrenderer =
151     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
152     column->add_attribute(
153     cellrenderer->property_foreground(), m_SamplesModel.m_color
154     );
155     }*/
156     /*{
157     Gtk::TreeViewColumn* column = m_treeViewMacro.get_column(1);
158     Gtk::CellRendererText* cellrenderer =
159     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
160     column->add_attribute(
161     cellrenderer->property_foreground(), m_SamplesModel.m_color
162     );
163     }*/
164     m_treeViewMacros.set_headers_visible(true);
165     m_treeViewMacros.get_selection()->signal_changed().connect(
166     sigc::mem_fun(*this, &MacrosSetup::onTreeViewSelectionChanged)
167     );
168 schoenebeck 3364 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
169     m_treeViewMacros.signal_key_release_event().connect(
170     #else
171 schoenebeck 3160 m_treeViewMacros.signal_key_release_event().connect_notify(
172 schoenebeck 3364 #endif
173 schoenebeck 3157 sigc::mem_fun(*this, &MacrosSetup::onMacroTreeViewKeyRelease)
174 schoenebeck 3160 );
175 schoenebeck 3157 m_treeStoreMacros->signal_row_changed().connect(
176     sigc::mem_fun(*this, &MacrosSetup::onMacroTreeViewRowValueChanged)
177     );
178     m_ignoreTreeViewValueChange = false;
179 schoenebeck 3162 m_ignoreCommentTextViewChange = false;
180 schoenebeck 3157
181     m_scrolledWindow.add(m_treeViewMacros);
182     m_scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
183 schoenebeck 3162 m_mainHBox.pack_start(m_scrolledWindow);
184 schoenebeck 3157
185 schoenebeck 3162 m_rvbox.set_spacing(5);
186    
187     m_mainHBox.pack_start(m_rvbox, Gtk::PACK_SHRINK);
188     m_mainHBox.set_spacing(5),
189     m_rvbox.set_spacing(5);
190 schoenebeck 3176 m_rvbox.pack_start(m_buttonDuplicate, Gtk::PACK_SHRINK);
191 schoenebeck 3162 m_rvbox.pack_start(m_detailsButtonBox, Gtk::PACK_SHRINK);
192    
193     //m_textViewComment.set_left_margin(3);
194     //m_textViewComment.set_right_margin(3);
195     m_textViewComment.set_indent(2);
196 schoenebeck 3184 m_textViewComment.set_tooltip_text(
197     _("Write arbitrary comments for the selected macro which help you to "
198     "remember the purpose of your macro. The comment will be shown as "
199     "tooltip in the main window's \"Macro\" menu for example.")
200     );
201 schoenebeck 3162 m_scrolledWindowComment.add(m_textViewComment);
202     m_scrolledWindowComment.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
203     m_labelComment.set_markup(
204     "<b>" + m_labelComment.get_text() + "</b>"
205     );
206     m_rvbox.pack_start(m_labelComment, Gtk::PACK_SHRINK);
207     m_rvbox.pack_start(m_scrolledWindowComment);
208    
209     m_detailsButtonBox.pack_start(m_buttonUp);
210     m_detailsButtonBox.pack_start(m_buttonDown);
211     m_detailsButtonBox.pack_start(m_buttonEdit);
212    
213 schoenebeck 3157 m_buttonBoxL.set_layout(Gtk::BUTTONBOX_START);
214     m_buttonBoxL.pack_start(m_deleteButton);
215     m_buttonBoxL.pack_start(m_inverseDeleteButton);
216     m_deleteButton.set_sensitive(false);
217     m_inverseDeleteButton.set_sensitive(false);
218 schoenebeck 3176 m_buttonDuplicate.set_sensitive(false);
219     m_buttonUp.set_sensitive(false);
220     m_buttonDown.set_sensitive(false);
221 schoenebeck 3157
222     m_buttonBox.set_layout(Gtk::BUTTONBOX_END);
223     m_buttonBox.pack_start(m_applyButton);
224     m_buttonBox.pack_start(m_cancelButton);
225     m_applyButton.set_can_default();
226     m_applyButton.set_sensitive(false);
227     m_applyButton.grab_focus();
228    
229 schoenebeck 3364 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION >= 12)
230     m_statusLabel.set_margin_start(6);
231     m_statusLabel.set_margin_end(6);
232     #elif GTKMM_MAJOR_VERSION >= 3
233 schoenebeck 3157 m_statusLabel.set_margin_left(6);
234     m_statusLabel.set_margin_right(6);
235     #else
236 schoenebeck 3364 m_statusLabel.set_spacing(6);
237 schoenebeck 3157 #endif
238    
239     m_statusHBox.pack_start(m_statusLabel);
240 schoenebeck 3364 #if HAS_GTKMM_SHOW_ALL_CHILDREN
241 schoenebeck 3157 m_statusHBox.show_all_children();
242 schoenebeck 3364 #endif
243 schoenebeck 3157
244     m_footerHBox.pack_start(m_buttonBoxL, Gtk::PACK_SHRINK);
245     m_footerHBox.pack_start(m_statusHBox);
246     m_footerHBox.pack_start(m_buttonBox, Gtk::PACK_SHRINK);
247    
248     m_vbox.pack_start(m_footerHBox, Gtk::PACK_SHRINK);
249    
250 schoenebeck 3160 m_addFromClipboardButton.signal_clicked().connect(
251     sigc::mem_fun(*this, &MacrosSetup::onButtonAddFromClipboard)
252     );
253    
254     m_addFromSelectionButton.signal_clicked().connect(
255     sigc::mem_fun(*this, &MacrosSetup::onButtonAddFromSelection)
256     );
257    
258 schoenebeck 3162 m_buttonUp.signal_clicked().connect(
259     sigc::mem_fun(*this, &MacrosSetup::onButtonUp)
260     );
261    
262     m_buttonDown.signal_clicked().connect(
263     sigc::mem_fun(*this, &MacrosSetup::onButtonDown)
264     );
265    
266     m_buttonEdit.signal_clicked().connect(
267     sigc::mem_fun(*this, &MacrosSetup::onButtonEdit)
268     );
269    
270 schoenebeck 3176 m_buttonDuplicate.signal_clicked().connect(
271     sigc::mem_fun(*this, &MacrosSetup::onButtonDuplicate)
272     );
273    
274 schoenebeck 3162 m_textViewComment.get_buffer()->signal_changed().connect(
275     sigc::mem_fun(*this, &MacrosSetup::onCommentTextViewChanged)
276     );
277    
278 schoenebeck 3157 m_applyButton.signal_clicked().connect(
279     sigc::mem_fun(*this, &MacrosSetup::onButtonApply)
280     );
281    
282     m_cancelButton.signal_clicked().connect(
283     sigc::mem_fun(*this, &MacrosSetup::onButtonCancel)
284     );
285    
286     m_deleteButton.signal_clicked().connect(
287     sigc::mem_fun(*this, &MacrosSetup::deleteSelectedRows)
288     );
289    
290     m_inverseDeleteButton.signal_clicked().connect(
291     sigc::mem_fun(*this, &MacrosSetup::inverseDeleteSelectedRows)
292     );
293    
294     signal_hide().connect(
295     sigc::mem_fun(*this, &MacrosSetup::onWindowHide)
296     );
297    
298     signal_delete_event().connect(
299 schoenebeck 3364 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
300 schoenebeck 3157 sigc::mem_fun(*this, &MacrosSetup::onWindowDelete)
301 schoenebeck 3364 #else
302     sigc::mem_fun(*this, &MacrosSetup::onWindowDeleteP)
303     #endif
304 schoenebeck 3157 );
305    
306     signal_key_press_event().connect(
307     sigc::mem_fun(*this, &MacrosSetup::onKeyPressed)
308     );
309     signal_key_release_event().connect(
310     sigc::mem_fun(*this, &MacrosSetup::onKeyReleased)
311     );
312    
313 schoenebeck 3364 #if HAS_GTKMM_SHOW_ALL_CHILDREN
314 schoenebeck 3157 show_all_children();
315 schoenebeck 3364 #endif
316 schoenebeck 3157 updateStatus();
317     }
318    
319     MacrosSetup::~MacrosSetup() {
320     printf("MacrosSetup destruct\n");
321     }
322    
323 schoenebeck 3160 void MacrosSetup::setMacros(const std::vector<Serialization::Archive>& macros,
324     Serialization::Archive* pClipboardContent,
325     gig::DimensionRegion* pSelectedDimRgn)
326     {
327 schoenebeck 3157 // copy for non-destructive editing
328     m_macros = macros;
329    
330 schoenebeck 3160 m_clipboardContent = pClipboardContent;
331     m_selectedDimRgn = pSelectedDimRgn;
332    
333 schoenebeck 3157 reloadTreeView();
334     }
335    
336 schoenebeck 3160 void MacrosSetup::onButtonAddFromClipboard() {
337     printf("+fromClipboard\n");
338     if (!m_clipboardContent) return;
339     if (!m_clipboardContent->rootObject()) return;
340     m_macros.push_back(*m_clipboardContent);
341     m_modified = true;
342     reloadTreeView();
343     }
344    
345     void MacrosSetup::onButtonAddFromSelection() {
346     printf("+fromSelection\n");
347     if (!m_selectedDimRgn) return;
348     std::string errorText;
349     try {
350     Serialization::Archive archive;
351     archive.serialize(m_selectedDimRgn);
352     //archive.setName("Unnamed Macro");
353     m_macros.push_back(archive);
354     m_modified = true;
355     reloadTreeView();
356     } catch (Serialization::Exception e) {
357     errorText = e.Message;
358     } catch (...) {
359     errorText = _("Unknown exception while creating macro");
360     }
361     if (!errorText.empty()) {
362     Glib::ustring txt = _("Couldn't create macro:\n") + errorText;
363     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
364     msg.run();
365     }
366     }
367    
368 schoenebeck 3162 void MacrosSetup::moveByDir(int d) {
369     if (d < -1 || d > 1) return;
370     int index = getSelectedMacroIndex();
371     if (index < 0) return;
372     if (d == -1 && index == 0) return;
373     if (d == +1 && index >= m_macros.size() - 1) return;
374    
375     // swap macros
376     std::swap(m_macros[index + d], m_macros[index]);
377    
378     // swap tree view rows
379     Gtk::TreePath p1(ToString(index + d));
380     Gtk::TreePath p2(ToString(index));
381     Gtk::TreeModel::iterator it1 = m_treeStoreMacros->get_iter(p1);
382     Gtk::TreeModel::iterator it2 = m_treeStoreMacros->get_iter(p2);
383     m_treeStoreMacros->iter_swap(it1, it2);
384     int idx1 = (*it1)[m_treeModelMacros.m_col_index];
385     int idx2 = (*it2)[m_treeModelMacros.m_col_index];
386     (*it1)[m_treeModelMacros.m_col_index] = idx2;
387     (*it2)[m_treeModelMacros.m_col_index] = idx1;
388     Glib::ustring s1 = (*it1)[m_treeModelMacros.m_col_key];
389     Glib::ustring s2 = (*it2)[m_treeModelMacros.m_col_key];
390     (*it1)[m_treeModelMacros.m_col_key] = s2;
391     (*it2)[m_treeModelMacros.m_col_key] = s1;
392    
393     m_modified = true;
394     }
395    
396     void MacrosSetup::onButtonUp() {
397     moveByDir(-1);
398     }
399    
400     void MacrosSetup::onButtonDown() {
401     moveByDir(+1);
402     }
403    
404 schoenebeck 3176 void MacrosSetup::onButtonDuplicate() {
405     Glib::RefPtr<Gtk::TreeSelection> sel = m_treeViewMacros.get_selection();
406     std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
407     duplicateRows(rows);
408     }
409    
410 schoenebeck 3162 void MacrosSetup::onButtonEdit() {
411     Serialization::Archive* macro = getSelectedMacro();
412     if (!macro) return;
413    
414     m_modifiedBeforeMacroEditor = isModified();
415    
416     MacroEditor* editor = new MacroEditor();
417     editor->setMacro(macro, false);
418     editor->signal_changes_applied().connect(
419     sigc::mem_fun(*this, &MacrosSetup::onMacroEditorAppliedChanges)
420     );
421     editor->show();
422     }
423    
424     void MacrosSetup::onMacroEditorAppliedChanges() {
425     // so that the user does not need to click on a Apply buttons twice
426     if (!m_modifiedBeforeMacroEditor)
427     onButtonApply();
428     updateStatus();
429     }
430    
431     void MacrosSetup::onCommentTextViewChanged() {
432     if (m_ignoreCommentTextViewChange) return;
433     //printf("textChanged\n");
434     Serialization::Archive* macro = getSelectedMacro();
435     if (!macro) return;
436     macro->setComment(
437     m_textViewComment.get_buffer()->get_text()
438     );
439     updateStatus();
440     }
441    
442     int MacrosSetup::getSelectedMacroIndex() const {
443     std::vector<Gtk::TreeModel::Path> v = m_treeViewMacros.get_selection()->get_selected_rows();
444     if (v.empty()) return -1;
445     Gtk::TreeModel::iterator it = m_treeStoreMacros->get_iter(v[0]);
446     if (!it) return -1;
447     const Gtk::TreeModel::Row& row = *it;
448     int index = row[m_treeModelMacros.m_col_index];
449     if (index < 0 || index >= m_macros.size()) return -1;
450     return index;
451     }
452    
453     Serialization::Archive* MacrosSetup::getSelectedMacro() {
454     int index = getSelectedMacroIndex();
455     if (index < 0) return NULL;
456     return &m_macros[index];
457     }
458    
459 schoenebeck 3157 static Glib::ustring indexToAccKey(uint index) {
460     if (index >= 12) return "";
461     return "F" + ToString(index+1);
462     }
463    
464 schoenebeck 3160 static int daysAgo(const tm& t) {
465     time_t now;
466     time(&now);
467     tm* pNow = localtime(&now);
468     if (!pNow) return 0;
469     if (pNow->tm_year == t.tm_year &&
470     pNow->tm_mon == t.tm_mon &&
471     pNow->tm_mday == t.tm_mday) return 0;
472     time_t past = mktime((tm*)&t);
473 schoenebeck 3161 return ceil(difftime(now, past) / 60.0 / 60.0 / 24.0);
474 schoenebeck 3160 }
475    
476 schoenebeck 3157 static Glib::ustring humanShortStr(const tm& t) {
477 schoenebeck 3160 int iDaysAgo = daysAgo(t);
478 schoenebeck 3157 char buf[70];
479 schoenebeck 3160 if (iDaysAgo == 0) {
480 schoenebeck 3157 // C-Time specification for a time somewhere today (see 'man strftime()').
481     if (strftime(buf, sizeof buf, _("%R"), &t))
482     return buf;
483 schoenebeck 3160 } else if (iDaysAgo == 1) {
484 schoenebeck 3157 // C-Time specification for a time somewhere yesterday (see 'man strftime()').
485     if (strftime(buf, sizeof buf, _("Yesterday %R"), &t))
486     return buf;
487 schoenebeck 3160 } else if (iDaysAgo == 2) {
488 schoenebeck 3157 // C-Time specification for a time somewhere 2 days ago (see 'man strftime()').
489     if (strftime(buf, sizeof buf, _("2 days ago %R"), &t))
490     return buf;
491     } else {
492     // C-Time specification for a time far more than 2 days ago (see 'man strftime()').
493     if (strftime(buf, sizeof buf, "%d %b %Y", &t))
494     return buf;
495     }
496     return "";
497     }
498    
499     void MacrosSetup::reloadTreeView() {
500     m_ignoreTreeViewValueChange = true;
501    
502     m_treeStoreMacros->clear();
503    
504     for (int iMacro = 0; iMacro < m_macros.size(); ++iMacro) {
505     const Serialization::Archive& macro = m_macros[iMacro];
506    
507     Gtk::TreeModel::iterator iter = m_treeStoreMacros->append();
508     Gtk::TreeModel::Row row = *iter;
509     row[m_treeModelMacros.m_col_key] = indexToAccKey(iMacro);
510 schoenebeck 3160 row[m_treeModelMacros.m_col_name] = macro.name().empty() ? _("Unnamed Macro") : gig_to_utf8(macro.name());
511 schoenebeck 3184 row[m_treeModelMacros.m_col_comment] = macro.comment().empty() ? _("No comment assigned to this macro yet.") : gig_to_utf8(macro.comment());
512 schoenebeck 3157 row[m_treeModelMacros.m_col_created] = humanShortStr(macro.dateTimeCreated());
513     row[m_treeModelMacros.m_col_modified] = humanShortStr(macro.dateTimeModified());
514     row[m_treeModelMacros.m_col_index] = iMacro;
515     }
516    
517     m_treeViewMacros.expand_all();
518    
519     updateStatus();
520    
521     m_ignoreTreeViewValueChange = false;
522     }
523    
524     void MacrosSetup::onTreeViewSelectionChanged() {
525     std::vector<Gtk::TreeModel::Path> v = m_treeViewMacros.get_selection()->get_selected_rows();
526     const bool bValidSelection = !v.empty();
527     m_deleteButton.set_sensitive(bValidSelection);
528     m_inverseDeleteButton.set_sensitive(bValidSelection);
529 schoenebeck 3162 m_buttonEdit.set_sensitive(bValidSelection);
530 schoenebeck 3176 m_buttonDuplicate.set_sensitive(bValidSelection);
531     m_buttonUp.set_sensitive(bValidSelection);
532     m_buttonDown.set_sensitive(bValidSelection);
533 schoenebeck 3162
534     // update comment text view
535     std::string sComment;
536     Serialization::Archive* macro = getSelectedMacro();
537     if (macro)
538     sComment = macro->comment();
539     m_ignoreCommentTextViewChange = true;
540     m_textViewComment.get_buffer()->set_text(sComment);
541     m_ignoreCommentTextViewChange = false;
542     m_textViewComment.set_sensitive(bValidSelection);
543 schoenebeck 3157 }
544    
545 schoenebeck 3160 // Cmd key on Mac, Ctrl key on all other OSs
546     static const guint primaryKeyL =
547     #if defined(__APPLE__)
548     GDK_KEY_Meta_L;
549     #else
550     GDK_KEY_Control_L;
551     #endif
552    
553     static const guint primaryKeyR =
554     #if defined(__APPLE__)
555     GDK_KEY_Meta_R;
556     #else
557     GDK_KEY_Control_R;
558     #endif
559    
560 schoenebeck 3364 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
561     bool MacrosSetup::onKeyPressed(Gdk::EventKey& _key) {
562     GdkEventKey* key = _key.gobj();
563     #else
564 schoenebeck 3157 bool MacrosSetup::onKeyPressed(GdkEventKey* key) {
565 schoenebeck 3364 #endif
566 schoenebeck 3157 //printf("key down 0x%x\n", key->keyval);
567     if (key->keyval == GDK_KEY_Alt_L || key->keyval == GDK_KEY_Alt_R)
568     m_altKeyDown = true;
569 schoenebeck 3160 if (key->keyval == primaryKeyL || key->keyval == primaryKeyR)
570     m_primaryKeyDown = true;
571 schoenebeck 3157 return false;
572     }
573    
574 schoenebeck 3364 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
575     bool MacrosSetup::onKeyReleased(Gdk::EventKey& _key) {
576     GdkEventKey* key = _key.gobj();
577     #else
578 schoenebeck 3157 bool MacrosSetup::onKeyReleased(GdkEventKey* key) {
579 schoenebeck 3364 #endif
580 schoenebeck 3157 //printf("key up 0x%x\n", key->keyval);
581     if (key->keyval == GDK_KEY_Alt_L || key->keyval == GDK_KEY_Alt_R)
582     m_altKeyDown = false;
583 schoenebeck 3160 if (key->keyval == primaryKeyL || key->keyval == primaryKeyR)
584     m_primaryKeyDown = false;
585     if (m_primaryKeyDown && key->keyval == GDK_KEY_b)
586     onButtonAddFromClipboard();
587     if (m_primaryKeyDown && key->keyval == GDK_KEY_s)
588     onButtonAddFromSelection();
589 schoenebeck 3157 return false;
590     }
591    
592 schoenebeck 3364 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
593     bool MacrosSetup::onMacroTreeViewKeyRelease(Gdk::EventKey& _key) {
594     GdkEventKey* key = _key.gobj();
595     #else
596 schoenebeck 3157 void MacrosSetup::onMacroTreeViewKeyRelease(GdkEventKey* key) {
597 schoenebeck 3364 #endif
598 schoenebeck 3157 if (key->keyval == GDK_KEY_BackSpace || key->keyval == GDK_KEY_Delete) {
599     if (m_altKeyDown)
600     inverseDeleteSelectedRows();
601 schoenebeck 3160 else if (m_primaryKeyDown)
602 schoenebeck 3157 deleteSelectedRows();
603     }
604 schoenebeck 3364 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
605     return false;
606     #endif
607 schoenebeck 3157 }
608    
609     void MacrosSetup::onMacroTreeViewRowValueChanged(const Gtk::TreeModel::Path& path,
610     const Gtk::TreeModel::iterator& iter)
611     {
612     if (m_ignoreTreeViewValueChange) return;
613     if (!iter) return;
614     Gtk::TreeModel::Row row = *iter;
615     Glib::ustring name = row[m_treeModelMacros.m_col_name];
616     int index = row[m_treeModelMacros.m_col_index];
617     m_macros[index].setName(name);
618     //reloadTreeView();
619 schoenebeck 3160 m_modified = true;
620     updateStatus();
621 schoenebeck 3157 }
622    
623     void MacrosSetup::deleteSelectedRows() {
624     Glib::RefPtr<Gtk::TreeSelection> sel = m_treeViewMacros.get_selection();
625     std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
626     deleteRows(rows);
627     }
628    
629 schoenebeck 3176 void MacrosSetup::duplicateRows(const std::vector<Gtk::TreeModel::Path>& rows) {
630     if (!rows.empty()) m_modified = true;
631     bool bError = false;
632     for (int r = 0; r < rows.size(); ++r) {
633     Gtk::TreeModel::iterator it = m_treeStoreMacros->get_iter(rows[r]);
634     if (!it) continue;
635     Gtk::TreeModel::Row row = *it;
636     int index = row[m_treeModelMacros.m_col_index];
637     if (index < 0 || index >= m_macros.size()) continue;
638    
639     Serialization::Archive clone = m_macros[index];
640     if (!endsWith(clone.name(), "COPY", true)) {
641     clone.setName(
642     (clone.name().empty()) ? "Unnamed COPY" : (clone.name() + " COPY")
643     );
644     }
645     try {
646     // enforce re-encoding the abstract object model and resetting the
647     // 'modified' state
648     clone.rawData();
649     } catch (Serialization::Exception e) {
650     bError = true;
651     e.PrintMessage();
652     continue;
653     } catch (...) {
654     bError = true;
655     std::cerr << "Unknown exception while cloning macro." << std::endl;
656     continue;
657     }
658     // finally add new cloned macro
659     m_macros.push_back(clone);
660     }
661     reloadTreeView();
662     if (bError) {
663     Glib::ustring txt = _("At least one of the macros could not be cloned due to an error (check console output).");
664     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
665     msg.run();
666     }
667     }
668    
669 schoenebeck 3157 void MacrosSetup::deleteRows(const std::vector<Gtk::TreeModel::Path>& rows) {
670 schoenebeck 3176 if (!rows.empty()) m_modified = true;
671 schoenebeck 3157 std::set<int> macros;
672     for (int r = rows.size() - 1; r >= 0; --r) {
673     Gtk::TreeModel::iterator it = m_treeStoreMacros->get_iter(rows[r]);
674     if (!it) continue;
675     Gtk::TreeModel::Row row = *it;
676     macros.insert(
677     row[m_treeModelMacros.m_col_index]
678     );
679     }
680     for (std::set<int>::const_reverse_iterator it = macros.rbegin();
681     it != macros.rend(); ++it)
682     {
683     m_macros.erase(m_macros.begin() + *it);
684     }
685     reloadTreeView();
686     }
687    
688     static bool _onEachTreeRow(const Gtk::TreeModel::Path& input, std::vector<Gtk::TreeModel::Path>* output) {
689     output->push_back(input);
690     return false; // continue walking the tree
691     }
692    
693     void MacrosSetup::inverseDeleteSelectedRows() {
694     // get all rows of tree view
695     std::vector<Gtk::TreeModel::Path> rows;
696     m_treeViewMacros.get_model()->foreach_path(
697     sigc::bind(
698     sigc::ptr_fun(&_onEachTreeRow),
699     &rows
700     )
701     );
702    
703     // erase all entries from "rows" which are currently selected
704     std::vector<Gtk::TreeModel::Path> vSelected = m_treeViewMacros.get_selection()->get_selected_rows();
705     for (int i = rows.size() - 1; i >= 0; --i) {
706     bool bIsSelected = std::find(vSelected.begin(), vSelected.end(),
707     rows[i]) != vSelected.end();
708     if (bIsSelected)
709     rows.erase(rows.begin() + i);
710     }
711    
712     // delete those 'inverse' selected rows
713     deleteRows(rows);
714     }
715    
716     void MacrosSetup::updateStatus() {
717 schoenebeck 3162 bool bValidSelection = !m_treeViewMacros.get_selection()->get_selected_rows().empty();
718 schoenebeck 3160 m_addFromClipboardButton.set_sensitive(
719     m_clipboardContent && m_clipboardContent->rootObject()
720     );
721     m_addFromSelectionButton.set_sensitive(m_selectedDimRgn);
722 schoenebeck 3162 m_buttonEdit.set_sensitive(bValidSelection);
723 schoenebeck 3176 m_buttonDuplicate.set_sensitive(bValidSelection);
724     m_buttonUp.set_sensitive(bValidSelection);
725     m_buttonDown.set_sensitive(bValidSelection);
726 schoenebeck 3157 m_applyButton.set_sensitive(isModified());
727 schoenebeck 3162 m_textViewComment.set_sensitive(bValidSelection);
728 schoenebeck 3157 updateStatusBar();
729     }
730    
731     void MacrosSetup::updateStatusBar() {
732     // update status text
733     std::string txt;
734     m_statusLabel.set_markup(txt);
735     }
736    
737     sigc::signal<void, const std::vector<Serialization::Archive>& >& MacrosSetup::signal_macros_changed()
738     {
739     return m_macros_changed;
740     }
741 schoenebeck 3364
742     #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
743     bool MacrosSetup::onWindowDelete(Gdk::Event& e) {
744     return onWindowDeleteP(NULL);
745     }
746     #endif
747 schoenebeck 3157
748 schoenebeck 3364 bool MacrosSetup::onWindowDeleteP(GdkEventAny* /*e*/) {
749 schoenebeck 3157 //printf("onWindowDelete\n");
750    
751     if (!isModified()) return false; // propagate event further (which will close this window)
752    
753     //gchar* msg = g_strdup_printf(_("Apply changes to macro \"%s\" before closing?"),
754     // m_macroOriginal->Name.c_str());
755 schoenebeck 3160 gchar* msg = g_strdup_printf(_("Apply changes to macro list before closing?"));
756 schoenebeck 3157 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
757     g_free(msg);
758     dialog.set_secondary_text(_("If you close without applying, your changes will be lost."));
759     dialog.add_button(_("Close _Without Applying"), Gtk::RESPONSE_NO);
760     dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
761     dialog.add_button(_("_Apply"), Gtk::RESPONSE_YES);
762     dialog.set_default_response(Gtk::RESPONSE_YES);
763     int response = dialog.run();
764     dialog.hide();
765    
766     // user decided to close this window without saving
767     if (response == Gtk::RESPONSE_NO)
768     return false; // propagate event further (which will close this window)
769    
770     // user cancelled dialog, thus don't close this window
771     if (response == Gtk::RESPONSE_CANCEL) {
772     show();
773     return true; // drop event (prevents closing this window)
774     }
775    
776     // user wants to apply the changes, afterwards close window
777     if (response == Gtk::RESPONSE_YES) {
778     onButtonApply();
779     return false; // propagate event further (which will close this window)
780     }
781    
782     // should never ever make it to this point actually
783     return false;
784     }
785    
786     bool MacrosSetup::isModified() const {
787 schoenebeck 3160 if (m_modified) return true;
788 schoenebeck 3157 bool bModified = false;
789     for (int i = 0; i < m_macros.size(); ++i) {
790     if (m_macros[i].isModified()) {
791     bModified = true;
792     break;
793     }
794     }
795     return bModified;
796     }
797    
798     void MacrosSetup::onButtonCancel() {
799 schoenebeck 3364 bool dropEvent = onWindowDeleteP(NULL);
800 schoenebeck 3157 if (dropEvent) return;
801     hide();
802     }
803    
804     void MacrosSetup::onButtonApply() {
805     std::string errorText;
806     try {
807     for (int i = 0; i < m_macros.size(); ++i) {
808     if (!m_macros[i].isModified()) continue;
809     // enforce re-encoding the abstract object model and resetting the
810     // 'modified' state
811     m_macros[i].rawData();
812     }
813 schoenebeck 3160 m_modified = false;
814 schoenebeck 3157 } catch (Serialization::Exception e) {
815     errorText = e.Message;
816     } catch (...) {
817     errorText = _("Unknown exception while applying macro changes");
818     }
819     if (!errorText.empty()) {
820     Glib::ustring txt = _("Couldn't apply macro changes:\n") + errorText;
821     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
822     msg.run();
823     } else {
824     // update MainWindow with edited list of macros
825     m_macros_changed.emit(m_macros);
826     }
827     updateStatus();
828     }
829    
830     void MacrosSetup::onWindowHide() {
831     delete this; // this is the end, my friend
832     }

  ViewVC Help
Powered by ViewVC