/[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 3176 - (hide annotations) (download)
Thu May 11 11:36:33 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 25217 byte(s)
* Macros Setup: Implemented duplicating macros.
* Bumped version (1.0.0.svn44).

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

  ViewVC Help
Powered by ViewVC