/[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 3155 - (hide annotations) (download)
Sun May 7 15:32:43 2017 UTC (7 years ago) by schoenebeck
File size: 13749 byte(s)
* Macro Editor: implemented reverse selected deletion of rows
* Macro Editor: Fixed "modified" state not being updated after
  clicking on "Apply" button.
* Macro Editor: Show implemented keyboard accelerators as
  symbols on the "Delete" (Ctrl+Del) and "Inverse Delete"
  (Alt+Del) buttons.
* Bumped version (1.0.0.svn37).

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

  ViewVC Help
Powered by ViewVC