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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3225 - (show 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 /*
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 #include <math.h>
13 #include <gtkmm/stock.h>
14 #include "MacroEditor.h"
15
16 MacrosSetup::MacrosSetup() :
17 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 m_buttonUp(Gtk::Stock::GO_UP),
22 m_buttonDown(Gtk::Stock::GO_DOWN),
23 m_buttonEdit(Gtk::Stock::EDIT),
24 m_buttonDuplicate(_("Duplicate")),
25 m_statusLabel("", Gtk::ALIGN_START),
26 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 m_altKeyDown(false),
32 m_primaryKeyDown(false)
33 {
34 add(m_vbox);
35
36 set_title(_("Setup Macros"));
37
38 if (!Settings::singleton()->autoRestoreWindowDimension) {
39 set_default_size(680, 500);
40 set_position(Gtk::WIN_POS_CENTER);
41 }
42
43 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 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 m_buttonDuplicate.set_image(
64 *new Gtk::Image(Gtk::Stock::COPY, Gtk::ICON_SIZE_BUTTON)
65 );
66 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 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 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 m_addHBox.pack_start(m_addFromClipboardButton, Gtk::PACK_EXPAND_WIDGET/*, 15*/);
87 m_addHBox.pack_start(m_addFromSelectionButton, Gtk::PACK_EXPAND_WIDGET/*, 15*/);
88 m_vbox.pack_start(m_addHBox, Gtk::PACK_SHRINK);
89
90 m_vbox.pack_start(m_mainHBox);
91 m_vbox.set_spacing(5);
92
93 // 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 m_treeViewMacros.append_column_editable(_("Macro Name"), m_treeModelMacros.m_col_name);
100 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 m_treeViewMacros.signal_key_release_event().connect_notify(
133 sigc::mem_fun(*this, &MacrosSetup::onMacroTreeViewKeyRelease)
134 );
135 m_treeStoreMacros->signal_row_changed().connect(
136 sigc::mem_fun(*this, &MacrosSetup::onMacroTreeViewRowValueChanged)
137 );
138 m_ignoreTreeViewValueChange = false;
139 m_ignoreCommentTextViewChange = false;
140
141 m_scrolledWindow.add(m_treeViewMacros);
142 m_scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
143 m_mainHBox.pack_start(m_scrolledWindow);
144
145 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 m_rvbox.pack_start(m_buttonDuplicate, Gtk::PACK_SHRINK);
151 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 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 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 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 m_buttonDuplicate.set_sensitive(false);
179 m_buttonUp.set_sensitive(false);
180 m_buttonDown.set_sensitive(false);
181
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 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 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 m_buttonDuplicate.signal_clicked().connect(
226 sigc::mem_fun(*this, &MacrosSetup::onButtonDuplicate)
227 );
228
229 m_textViewComment.get_buffer()->signal_changed().connect(
230 sigc::mem_fun(*this, &MacrosSetup::onCommentTextViewChanged)
231 );
232
233 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 void MacrosSetup::setMacros(const std::vector<Serialization::Archive>& macros,
273 Serialization::Archive* pClipboardContent,
274 gig::DimensionRegion* pSelectedDimRgn)
275 {
276 // copy for non-destructive editing
277 m_macros = macros;
278
279 m_clipboardContent = pClipboardContent;
280 m_selectedDimRgn = pSelectedDimRgn;
281
282 reloadTreeView();
283 }
284
285 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 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 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 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 static Glib::ustring indexToAccKey(uint index) {
409 if (index >= 12) return "";
410 return "F" + ToString(index+1);
411 }
412
413 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 return ceil(difftime(now, past) / 60.0 / 60.0 / 24.0);
423 }
424
425 static Glib::ustring humanShortStr(const tm& t) {
426 int iDaysAgo = daysAgo(t);
427 char buf[70];
428 if (iDaysAgo == 0) {
429 // C-Time specification for a time somewhere today (see 'man strftime()').
430 if (strftime(buf, sizeof buf, _("%R"), &t))
431 return buf;
432 } else if (iDaysAgo == 1) {
433 // C-Time specification for a time somewhere yesterday (see 'man strftime()').
434 if (strftime(buf, sizeof buf, _("Yesterday %R"), &t))
435 return buf;
436 } else if (iDaysAgo == 2) {
437 // 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 row[m_treeModelMacros.m_col_name] = macro.name().empty() ? _("Unnamed Macro") : gig_to_utf8(macro.name());
460 row[m_treeModelMacros.m_col_comment] = macro.comment().empty() ? _("No comment assigned to this macro yet.") : gig_to_utf8(macro.comment());
461 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 m_buttonEdit.set_sensitive(bValidSelection);
479 m_buttonDuplicate.set_sensitive(bValidSelection);
480 m_buttonUp.set_sensitive(bValidSelection);
481 m_buttonDown.set_sensitive(bValidSelection);
482
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 }
493
494 // 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 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 if (key->keyval == primaryKeyL || key->keyval == primaryKeyR)
514 m_primaryKeyDown = true;
515 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 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 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 else if (m_primaryKeyDown)
536 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 m_modified = true;
551 updateStatus();
552 }
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 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 void MacrosSetup::deleteRows(const std::vector<Gtk::TreeModel::Path>& rows) {
601 if (!rows.empty()) m_modified = true;
602 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 bool bValidSelection = !m_treeViewMacros.get_selection()->get_selected_rows().empty();
649 m_addFromClipboardButton.set_sensitive(
650 m_clipboardContent && m_clipboardContent->rootObject()
651 );
652 m_addFromSelectionButton.set_sensitive(m_selectedDimRgn);
653 m_buttonEdit.set_sensitive(bValidSelection);
654 m_buttonDuplicate.set_sensitive(bValidSelection);
655 m_buttonUp.set_sensitive(bValidSelection);
656 m_buttonDown.set_sensitive(bValidSelection);
657 m_applyButton.set_sensitive(isModified());
658 m_textViewComment.set_sensitive(bValidSelection);
659 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 gchar* msg = g_strdup_printf(_("Apply changes to macro list before closing?"));
681 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 if (m_modified) return true;
713 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 m_modified = false;
739 } 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