/[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 3176 - (show annotations) (download)
Thu May 11 11:36:33 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 25217 byte(s)
* Macros Setup: Implemented duplicating macros.
* Bumped version (1.0.0.svn44).

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

  ViewVC Help
Powered by ViewVC