/[svn]/gigedit/trunk/src/gigedit/MacroEditor.cpp
ViewVC logotype

Annotation of /gigedit/trunk/src/gigedit/MacroEditor.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3162 - (hide annotations) (download)
Tue May 9 14:35:23 2017 UTC (3 years, 2 months ago) by schoenebeck
File size: 14682 byte(s)
* Macros Setup: Implemented reordering macros by "Up" and
  "Down" buttons (and accordingly also altering the
  F1 ... F12 acceleration keys they are assigned to).
* Macros Setup: Implemented calling Macro Editor from
  this macro setup dialog by hitting "Edit" button.
* Macros Setup: Implemented editing comment on macros.
* Macros Setup: Enabled various icons.
* Bumped version (1.0.0.svn41).

1 schoenebeck 3151 /*
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 "MacroEditor.h"
9     #include "global.h"
10     #include <assert.h>
11    
12     MacroEditor::MacroEditor() :
13     m_macroOriginal(NULL),
14     m_statusLabel("", Gtk::ALIGN_START),
15 schoenebeck 3155 m_deleteButton(Glib::ustring(_("Delete")) + " " + UNICODE_PRIMARY_KEY_SYMBOL + UNICODE_ERASE_KEY_SYMBOL),
16     m_inverseDeleteButton(Glib::ustring(_("Inverse Delete")) + " " + UNICODE_ALT_KEY_SYMBOL + UNICODE_ERASE_KEY_SYMBOL),
17 schoenebeck 3151 m_applyButton(_("_Apply"), true),
18 schoenebeck 3155 m_cancelButton(_("_Cancel"), true),
19     m_altKeyDown(false)
20 schoenebeck 3151 {
21     add(m_vbox);
22    
23     set_default_size(800, 600);
24    
25 schoenebeck 3162 //FIXME: Commented out since Gtk::Label is a disaster when it comes to multi line content.
26     /*m_labelIntro.set_text(
27     _("A macro is a list of parameters and corresponding values which "
28     "should be applied to the instrument editor when the macro is "
29     "triggered by the user. A macro is triggered either by selecting "
30     "the macro from the \"Macro\" menu, or by hitting the macro's "
31     "respective keyboard accelerator (F1 to F12).")
32     );
33     m_labelIntro.set_line_wrap();
34     m_vbox.pack_start(m_labelIntro, Gtk::PACK_SHRINK);*/
35    
36 schoenebeck 3151 // create Macro treeview (including its data model)
37     m_treeStoreMacro = MacroTreeStore::create(m_treeModelMacro);
38     m_treeViewMacro.set_model(m_treeStoreMacro);
39     m_treeViewMacro.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
40     //m_treeViewMacro.set_tooltip_text(_(""));
41     m_treeViewMacro.append_column(_("Key"), m_treeModelMacro.m_col_name);
42     m_treeViewMacro.append_column(_("Type"), m_treeModelMacro.m_col_type);
43     m_treeViewMacro.append_column_editable(_("Value"), m_treeModelMacro.m_col_value);
44 schoenebeck 3154 {
45     Gtk::TreeViewColumn* column = m_treeViewMacro.get_column(1);
46     Gtk::CellRendererText* cellrenderer =
47     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
48     cellrenderer->property_foreground().set_value("#bababa");
49     }
50 schoenebeck 3151 /*{
51     Gtk::TreeViewColumn* column = m_treeViewMacro.get_column(0);
52     Gtk::CellRendererText* cellrenderer =
53     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
54     column->add_attribute(
55     cellrenderer->property_foreground(), m_SamplesModel.m_color
56     );
57     }*/
58     /*{
59     Gtk::TreeViewColumn* column = m_treeViewMacro.get_column(1);
60     Gtk::CellRendererText* cellrenderer =
61     dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
62     column->add_attribute(
63     cellrenderer->property_foreground(), m_SamplesModel.m_color
64     );
65     }*/
66     m_treeViewMacro.set_headers_visible(true);
67 schoenebeck 3154 m_treeViewMacro.get_selection()->signal_changed().connect(
68     sigc::mem_fun(*this, &MacroEditor::onTreeViewSelectionChanged)
69     );
70     m_treeViewMacro.signal_key_release_event().connect_notify(
71     sigc::mem_fun(*this, &MacroEditor::onMacroTreeViewKeyRelease)
72     );
73     m_treeStoreMacro->signal_row_changed().connect(
74     sigc::mem_fun(*this, &MacroEditor::onMacroTreeViewRowValueChanged)
75     );
76     m_ignoreTreeViewValueChange = false;
77 schoenebeck 3151
78     m_scrolledWindow.add(m_treeViewMacro);
79     m_scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
80     m_vbox.pack_start(m_scrolledWindow);
81    
82 schoenebeck 3154 m_buttonBoxL.set_layout(Gtk::BUTTONBOX_START);
83     m_buttonBoxL.pack_start(m_deleteButton);
84     m_buttonBoxL.pack_start(m_inverseDeleteButton);
85     m_deleteButton.set_sensitive(false);
86     m_inverseDeleteButton.set_sensitive(false);
87    
88 schoenebeck 3151 m_buttonBox.set_layout(Gtk::BUTTONBOX_END);
89     m_buttonBox.pack_start(m_applyButton);
90     m_buttonBox.pack_start(m_cancelButton);
91     m_applyButton.set_can_default();
92     m_applyButton.set_sensitive(false);
93     m_applyButton.grab_focus();
94    
95     #if GTKMM_MAJOR_VERSION >= 3
96     m_statusLabel.set_margin_left(6);
97     m_statusLabel.set_margin_right(6);
98     #else
99     m_statusHBox.set_spacing(6);
100     #endif
101    
102     m_statusHBox.pack_start(m_statusLabel);
103     m_statusHBox.show_all_children();
104    
105 schoenebeck 3154 m_footerHBox.pack_start(m_buttonBoxL, Gtk::PACK_SHRINK);
106 schoenebeck 3151 m_footerHBox.pack_start(m_statusHBox);
107     m_footerHBox.pack_start(m_buttonBox, Gtk::PACK_SHRINK);
108    
109     m_vbox.pack_start(m_footerHBox, Gtk::PACK_SHRINK);
110    
111     m_applyButton.signal_clicked().connect(
112     sigc::mem_fun(*this, &MacroEditor::onButtonApply)
113     );
114    
115     m_cancelButton.signal_clicked().connect(
116     sigc::mem_fun(*this, &MacroEditor::onButtonCancel)
117     );
118    
119 schoenebeck 3154 m_deleteButton.signal_clicked().connect(
120     sigc::mem_fun(*this, &MacroEditor::deleteSelectedRows)
121     );
122    
123     m_inverseDeleteButton.signal_clicked().connect(
124     sigc::mem_fun(*this, &MacroEditor::inverseDeleteSelectedRows)
125     );
126    
127 schoenebeck 3151 signal_hide().connect(
128     sigc::mem_fun(*this, &MacroEditor::onWindowHide)
129     );
130    
131     signal_delete_event().connect(
132     sigc::mem_fun(*this, &MacroEditor::onWindowDelete)
133     );
134    
135 schoenebeck 3155 signal_key_press_event().connect(
136     sigc::mem_fun(*this, &MacroEditor::onKeyPressed)
137     );
138     signal_key_release_event().connect(
139     sigc::mem_fun(*this, &MacroEditor::onKeyReleased)
140     );
141    
142 schoenebeck 3151 show_all_children();
143     updateStatus();
144     }
145    
146     MacroEditor::~MacroEditor() {
147     printf("MacroEditor destruct\n");
148     }
149    
150 schoenebeck 3162 void MacroEditor::setMacro(Serialization::Archive* macro, bool isClipboard) {
151 schoenebeck 3151 m_macroOriginal = macro;
152     if (!macro) {
153     set_title(_("No Macro"));
154     return;
155     }
156    
157 schoenebeck 3162 if (isClipboard)
158     set_title(std::string(_("Macro Editor:")) + " " + _("Clipboard Content"));
159     else {
160     if (macro->name().empty())
161     set_title(std::string(_("Macro Editor:")) + " " + _("Unnamed Macro"));
162     else
163     set_title(std::string(_("Macro Editor:")) + " \"" + macro->name() + "\"");
164     }
165 schoenebeck 3151
166     // copy for non-destructive editing
167     m_macro = *macro;
168    
169     reloadTreeView();
170     }
171    
172 schoenebeck 3162 sigc::signal<void>& MacroEditor::signal_changes_applied() {
173     return m_changes_applied;
174     }
175    
176 schoenebeck 3151 void MacroEditor::buildTreeView(const Gtk::TreeModel::Row& parentRow, const Serialization::Object& parentObject) {
177     for (int iMember = 0; iMember < parentObject.members().size(); ++iMember) {
178     const Serialization::Member& member = parentObject.members()[iMember];
179     const Serialization::Object& object = m_macro.objectByUID(member.uid());
180     Gtk::TreeModel::iterator iterRow = m_treeStoreMacro->append(parentRow.children());
181     Gtk::TreeModel::Row row = *iterRow;
182     row[m_treeModelMacro.m_col_name] = gig_to_utf8(member.name());
183     row[m_treeModelMacro.m_col_type] = gig_to_utf8(member.type().asLongDescr());
184     row[m_treeModelMacro.m_col_uid] = object.uid();
185     if (object.type().isClass()) {
186     row[m_treeModelMacro.m_col_value] = "(class)";
187     buildTreeView(row, object);
188     } else {
189     row[m_treeModelMacro.m_col_value] = m_macro.valueAsString(object);
190     }
191     }
192     }
193    
194     void MacroEditor::reloadTreeView() {
195 schoenebeck 3154 m_ignoreTreeViewValueChange = true;
196    
197 schoenebeck 3151 m_treeStoreMacro->clear();
198    
199     const Serialization::Object& rootObject = m_macro.rootObject();
200    
201     Gtk::TreeModel::iterator iterRoot = m_treeStoreMacro->append();
202     Gtk::TreeModel::Row rowRoot = *iterRoot;
203     rowRoot[m_treeModelMacro.m_col_name] = "(Root)";
204     rowRoot[m_treeModelMacro.m_col_type] = gig_to_utf8(rootObject.type().asLongDescr());
205     rowRoot[m_treeModelMacro.m_col_value] = "";
206     rowRoot[m_treeModelMacro.m_col_uid] = rootObject.uid();
207    
208     buildTreeView(rowRoot, rootObject);
209    
210     m_treeViewMacro.expand_all();
211    
212     updateStatus();
213 schoenebeck 3154
214     m_ignoreTreeViewValueChange = false;
215 schoenebeck 3151 }
216    
217 schoenebeck 3154 void MacroEditor::onTreeViewSelectionChanged() {
218     std::vector<Gtk::TreeModel::Path> v = m_treeViewMacro.get_selection()->get_selected_rows();
219     const bool bValidSelection = !v.empty();
220     m_deleteButton.set_sensitive(bValidSelection);
221     m_inverseDeleteButton.set_sensitive(bValidSelection);
222     }
223    
224 schoenebeck 3155 bool MacroEditor::onKeyPressed(GdkEventKey* key) {
225     //printf("key down 0x%x\n", key->keyval);
226     if (key->keyval == GDK_KEY_Alt_L || key->keyval == GDK_KEY_Alt_R)
227     m_altKeyDown = true;
228     return false;
229     }
230    
231     bool MacroEditor::onKeyReleased(GdkEventKey* key) {
232     //printf("key up 0x%x\n", key->keyval);
233     if (key->keyval == GDK_KEY_Alt_L || key->keyval == GDK_KEY_Alt_R)
234     m_altKeyDown = false;
235     return false;
236     }
237    
238 schoenebeck 3154 void MacroEditor::onMacroTreeViewKeyRelease(GdkEventKey* key) {
239 schoenebeck 3155 if (key->keyval == GDK_KEY_BackSpace || key->keyval == GDK_KEY_Delete) {
240     if (m_altKeyDown)
241     inverseDeleteSelectedRows();
242     else
243     deleteSelectedRows();
244     }
245 schoenebeck 3154 }
246    
247     void MacroEditor::onMacroTreeViewRowValueChanged(const Gtk::TreeModel::Path& path,
248     const Gtk::TreeModel::iterator& iter)
249     {
250     if (m_ignoreTreeViewValueChange) return;
251     if (!iter) return;
252     Gtk::TreeModel::Row row = *iter;
253     Glib::ustring value = row[m_treeModelMacro.m_col_value];
254     Serialization::UID uid = row[m_treeModelMacro.m_col_uid];
255     Serialization::String gigvalue(gig_from_utf8(value));
256     Serialization::Object& object = m_macro.objectByUID(uid);
257     std::string errorText;
258     try {
259     m_macro.setAutoValue(object, gigvalue);
260     } catch (Serialization::Exception e) {
261     errorText = e.Message;
262     } catch (...) {
263     errorText = _("Unknown exception during object value change");
264     }
265     if (!errorText.empty()) {
266     Glib::ustring txt = _("Couldn't change value:\n") + errorText;
267     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
268     msg.run();
269     }
270     }
271    
272     void MacroEditor::deleteSelectedRows() {
273     Glib::RefPtr<Gtk::TreeSelection> sel = m_treeViewMacro.get_selection();
274     std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
275 schoenebeck 3155 deleteRows(rows);
276     }
277    
278     void MacroEditor::deleteRows(const std::vector<Gtk::TreeModel::Path>& rows) {
279 schoenebeck 3154 for (int r = rows.size() - 1; r >= 0; --r) {
280     Gtk::TreeModel::iterator it = m_treeStoreMacro->get_iter(rows[r]);
281     if (!it) continue;
282     Gtk::TreeModel::Row row = *it;
283     Serialization::UID uid = row[m_treeModelMacro.m_col_uid];
284     if (uid == m_macro.rootObject().uid()) continue; // prohibit deleting root object
285     Gtk::TreeModel::iterator itParent = row.parent();
286     if (!itParent) continue;
287     Gtk::TreeModel::Row rowParent = *itParent;
288     Serialization::UID uidParent = rowParent[m_treeModelMacro.m_col_uid];
289     //Serialization::Object& object = m_macro.objectByUID(uid);
290     Serialization::Object& parentObject = m_macro.objectByUID(uidParent);
291     const Serialization::Member& member = parentObject.memberByUID(uid);
292     m_macro.removeMember(parentObject, member);
293     //m_macro.remove(object);
294     }
295     reloadTreeView();
296     }
297    
298 schoenebeck 3155 static bool _onEachTreeRow(const Gtk::TreeModel::Path& input, std::vector<Gtk::TreeModel::Path>* output) {
299     output->push_back(input);
300     return false; // continue walking the tree
301     }
302    
303 schoenebeck 3154 void MacroEditor::inverseDeleteSelectedRows() {
304 schoenebeck 3155 // get all rows of tree view
305     std::vector<Gtk::TreeModel::Path> rows;
306     m_treeViewMacro.get_model()->foreach_path(
307     sigc::bind(
308     sigc::ptr_fun(&_onEachTreeRow),
309     &rows
310     )
311     );
312    
313     // erase all entries from "rows" which are currently selected
314     std::vector<Gtk::TreeModel::Path> vSelected = m_treeViewMacro.get_selection()->get_selected_rows();
315     for (int i = rows.size() - 1; i >= 0; --i) {
316     bool bIsSelected = std::find(vSelected.begin(), vSelected.end(),
317     rows[i]) != vSelected.end();
318     if (bIsSelected)
319     rows.erase(rows.begin() + i);
320     }
321    
322     // delete those 'inverse' selected rows
323     deleteRows(rows);
324 schoenebeck 3154 }
325    
326 schoenebeck 3151 void MacroEditor::updateStatus() {
327     m_applyButton.set_sensitive(isModified());
328     updateStatusBar();
329     }
330    
331     void MacroEditor::updateStatusBar() {
332     // update status text
333     std::string txt;
334     m_statusLabel.set_markup(txt);
335     }
336    
337     bool MacroEditor::onWindowDelete(GdkEventAny* e) {
338     //printf("onWindowDelete\n");
339    
340     if (!isModified()) return false; // propagate event further (which will close this window)
341    
342     //gchar* msg = g_strdup_printf(_("Apply changes to macro \"%s\" before closing?"),
343     // m_macroOriginal->Name.c_str());
344     gchar* msg = g_strdup_printf(_("Apply changes to macro before closing?"));
345     Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
346     g_free(msg);
347     dialog.set_secondary_text(_("If you close without applying, your changes will be lost."));
348     dialog.add_button(_("Close _Without Applying"), Gtk::RESPONSE_NO);
349     dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
350     dialog.add_button(_("_Apply"), Gtk::RESPONSE_YES);
351     dialog.set_default_response(Gtk::RESPONSE_YES);
352     int response = dialog.run();
353     dialog.hide();
354    
355     // user decided to close macro editor without saving
356     if (response == Gtk::RESPONSE_NO)
357     return false; // propagate event further (which will close this window)
358    
359     // user cancelled dialog, thus don't close macro editor
360     if (response == Gtk::RESPONSE_CANCEL) {
361     show();
362     return true; // drop event (prevents closing this window)
363     }
364    
365     // user wants to apply the changes, afterwards close window
366     if (response == Gtk::RESPONSE_YES) {
367     onButtonApply();
368     return false; // propagate event further (which will close this window)
369     }
370    
371     // should never ever make it to this point actually
372     return false;
373     }
374    
375     bool MacroEditor::isModified() const {
376     return m_macro.isModified();
377     }
378    
379     void MacroEditor::onButtonCancel() {
380     bool dropEvent = onWindowDelete(NULL);
381     if (dropEvent) return;
382     hide();
383     }
384    
385     void MacroEditor::onButtonApply() {
386 schoenebeck 3155 std::string errorText;
387     try {
388     // enforce re-encoding the abstract object model and resetting the
389     // 'modified' state
390     m_macro.rawData();
391     // replace actual effective Archive object which is effectively used
392     // for macro apply operations
393     *m_macroOriginal = m_macro;
394     } catch (Serialization::Exception e) {
395     errorText = e.Message;
396     } catch (...) {
397     errorText = _("Unknown exception while applying macro changes");
398     }
399     if (!errorText.empty()) {
400     Glib::ustring txt = _("Couldn't apply macro changes:\n") + errorText;
401     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
402     msg.run();
403     }
404     updateStatus();
405 schoenebeck 3162 m_changes_applied.emit();
406 schoenebeck 3151 }
407    
408     void MacroEditor::onWindowHide() {
409     delete this; // this is the end, my friend
410     }

  ViewVC Help
Powered by ViewVC