/[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 3225 - (hide annotations) (download)
Fri May 26 22:10:16 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 27188 byte(s)
* Assigned more useful default dimensions (and default position) for various
  windows and dialogs (if auto-restore of user's own custom window
  dimensions is disabled).
* Bumped version (1.0.0.svn51).

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

  ViewVC Help
Powered by ViewVC