/[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 3184 - (hide annotations) (download)
Wed May 17 12:28:39 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 27073 byte(s)
* Added bunch of help text and tooltips for the new
  "Macro Setup" and "Macro Editor" windows.
* wrapLabel: Fixed wrong dimensions when using
  padding.
* Bumped version (1.0.0.svn46).

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

  ViewVC Help
Powered by ViewVC