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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3162 - (show annotations) (download)
Tue May 9 14:35:23 2017 UTC (6 years, 11 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 /*
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 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 m_applyButton(_("_Apply"), true),
18 m_cancelButton(_("_Cancel"), true),
19 m_altKeyDown(false)
20 {
21 add(m_vbox);
22
23 set_default_size(800, 600);
24
25 //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 // 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 {
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 /*{
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 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
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 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 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 m_footerHBox.pack_start(m_buttonBoxL, Gtk::PACK_SHRINK);
106 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 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 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 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 show_all_children();
143 updateStatus();
144 }
145
146 MacroEditor::~MacroEditor() {
147 printf("MacroEditor destruct\n");
148 }
149
150 void MacroEditor::setMacro(Serialization::Archive* macro, bool isClipboard) {
151 m_macroOriginal = macro;
152 if (!macro) {
153 set_title(_("No Macro"));
154 return;
155 }
156
157 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
166 // copy for non-destructive editing
167 m_macro = *macro;
168
169 reloadTreeView();
170 }
171
172 sigc::signal<void>& MacroEditor::signal_changes_applied() {
173 return m_changes_applied;
174 }
175
176 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 m_ignoreTreeViewValueChange = true;
196
197 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
214 m_ignoreTreeViewValueChange = false;
215 }
216
217 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 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 void MacroEditor::onMacroTreeViewKeyRelease(GdkEventKey* key) {
239 if (key->keyval == GDK_KEY_BackSpace || key->keyval == GDK_KEY_Delete) {
240 if (m_altKeyDown)
241 inverseDeleteSelectedRows();
242 else
243 deleteSelectedRows();
244 }
245 }
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 deleteRows(rows);
276 }
277
278 void MacroEditor::deleteRows(const std::vector<Gtk::TreeModel::Path>& rows) {
279 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 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 void MacroEditor::inverseDeleteSelectedRows() {
304 // 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 }
325
326 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 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 m_changes_applied.emit();
406 }
407
408 void MacroEditor::onWindowHide() {
409 delete this; // this is the end, my friend
410 }

  ViewVC Help
Powered by ViewVC