/[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 3184 - (show annotations) (download)
Wed May 17 12:28:39 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 27073 byte(s)
* Added bunch of help text and tooltips for the new
  "Macro Setup" and "Macro Editor" windows.
* wrapLabel: Fixed wrong dimensions when using
  padding.
* Bumped version (1.0.0.svn46).

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

  ViewVC Help
Powered by ViewVC