/[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 3161 - (show annotations) (download)
Mon May 8 22:25:46 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 17622 byte(s)
- Minor bugfix regarding time displayed.

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
15 MacrosSetup::MacrosSetup() :
16 m_modified(false),
17 m_clipboardContent(NULL),
18 m_addFromClipboardButton(" " + Glib::ustring(_("From Clipboard")) + " " + UNICODE_PRIMARY_KEY_SYMBOL + "B"),
19 m_addFromSelectionButton(" " + Glib::ustring(_("From Selection")) + " " + UNICODE_PRIMARY_KEY_SYMBOL + "S"),
20 m_statusLabel("", Gtk::ALIGN_START),
21 m_deleteButton(Glib::ustring(_("Delete")) + " " + UNICODE_PRIMARY_KEY_SYMBOL + UNICODE_ERASE_KEY_SYMBOL),
22 m_inverseDeleteButton(Glib::ustring(_("Inverse Delete")) + " " + UNICODE_ALT_KEY_SYMBOL + UNICODE_ERASE_KEY_SYMBOL),
23 m_applyButton(_("_Apply"), true),
24 m_cancelButton(_("_Cancel"), true),
25 m_altKeyDown(false),
26 m_primaryKeyDown(false)
27 {
28 add(m_vbox);
29
30 set_title(_("Setup Macros"));
31
32 set_default_size(800, 600);
33
34 m_addFromClipboardButton.set_image(
35 *new Gtk::Image(Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON)
36 );
37 m_addFromSelectionButton.set_image(
38 *new Gtk::Image(Gtk::Stock::ADD, Gtk::ICON_SIZE_BUTTON)
39 );
40 m_addFromClipboardButton.set_tooltip_text(_("Create a new macro from the content currently available on the clipboard."));
41 m_addFromSelectionButton.set_tooltip_text(_("Create a new macro from the currently selected dimension region's parameters currently shown on the main window."));
42 m_addHBox.pack_start(m_addFromClipboardButton);
43 m_addHBox.pack_start(m_addFromSelectionButton);
44 m_vbox.pack_start(m_addHBox, Gtk::PACK_SHRINK);
45
46 // create Macro list treeview (including its data model)
47 m_treeStoreMacros = MacroListTreeStore::create(m_treeModelMacros);
48 m_treeViewMacros.set_model(m_treeStoreMacros);
49 m_treeViewMacros.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
50 //m_treeViewMacro.set_tooltip_text(_(""));
51 m_treeViewMacros.append_column(_("Key"), m_treeModelMacros.m_col_key);
52 m_treeViewMacros.append_column_editable(_("Name"), m_treeModelMacros.m_col_name);
53 m_treeViewMacros.append_column(_("Created"), m_treeModelMacros.m_col_created);
54 m_treeViewMacros.append_column(_("Modified"), m_treeModelMacros.m_col_modified);
55 m_treeViewMacros.set_tooltip_column(m_treeModelMacros.m_col_comment.index());
56 // make all rows gray text, except of "Name" column
57 for (int i = 0; i <= 3; ++i) {
58 if (i == m_treeModelMacros.m_col_name.index())
59 continue;
60 Gtk::TreeViewColumn* column = m_treeViewMacros.get_column(i);
61 Gtk::CellRendererText* cellrenderer =
62 dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
63 cellrenderer->property_foreground().set_value("#bababa");
64 }
65 /*{
66 Gtk::TreeViewColumn* column = m_treeViewMacro.get_column(0);
67 Gtk::CellRendererText* cellrenderer =
68 dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
69 column->add_attribute(
70 cellrenderer->property_foreground(), m_SamplesModel.m_color
71 );
72 }*/
73 /*{
74 Gtk::TreeViewColumn* column = m_treeViewMacro.get_column(1);
75 Gtk::CellRendererText* cellrenderer =
76 dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
77 column->add_attribute(
78 cellrenderer->property_foreground(), m_SamplesModel.m_color
79 );
80 }*/
81 m_treeViewMacros.set_headers_visible(true);
82 m_treeViewMacros.get_selection()->signal_changed().connect(
83 sigc::mem_fun(*this, &MacrosSetup::onTreeViewSelectionChanged)
84 );
85 m_treeViewMacros.signal_key_release_event().connect_notify(
86 sigc::mem_fun(*this, &MacrosSetup::onMacroTreeViewKeyRelease)
87 );
88 m_treeStoreMacros->signal_row_changed().connect(
89 sigc::mem_fun(*this, &MacrosSetup::onMacroTreeViewRowValueChanged)
90 );
91 m_ignoreTreeViewValueChange = false;
92
93 m_scrolledWindow.add(m_treeViewMacros);
94 m_scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
95 m_vbox.pack_start(m_scrolledWindow);
96
97 m_buttonBoxL.set_layout(Gtk::BUTTONBOX_START);
98 m_buttonBoxL.pack_start(m_deleteButton);
99 m_buttonBoxL.pack_start(m_inverseDeleteButton);
100 m_deleteButton.set_sensitive(false);
101 m_inverseDeleteButton.set_sensitive(false);
102
103 m_buttonBox.set_layout(Gtk::BUTTONBOX_END);
104 m_buttonBox.pack_start(m_applyButton);
105 m_buttonBox.pack_start(m_cancelButton);
106 m_applyButton.set_can_default();
107 m_applyButton.set_sensitive(false);
108 m_applyButton.grab_focus();
109
110 #if GTKMM_MAJOR_VERSION >= 3
111 m_statusLabel.set_margin_left(6);
112 m_statusLabel.set_margin_right(6);
113 #else
114 m_statusHBox.set_spacing(6);
115 #endif
116
117 m_statusHBox.pack_start(m_statusLabel);
118 m_statusHBox.show_all_children();
119
120 m_footerHBox.pack_start(m_buttonBoxL, Gtk::PACK_SHRINK);
121 m_footerHBox.pack_start(m_statusHBox);
122 m_footerHBox.pack_start(m_buttonBox, Gtk::PACK_SHRINK);
123
124 m_vbox.pack_start(m_footerHBox, Gtk::PACK_SHRINK);
125
126 m_addFromClipboardButton.signal_clicked().connect(
127 sigc::mem_fun(*this, &MacrosSetup::onButtonAddFromClipboard)
128 );
129
130 m_addFromSelectionButton.signal_clicked().connect(
131 sigc::mem_fun(*this, &MacrosSetup::onButtonAddFromSelection)
132 );
133
134 m_applyButton.signal_clicked().connect(
135 sigc::mem_fun(*this, &MacrosSetup::onButtonApply)
136 );
137
138 m_cancelButton.signal_clicked().connect(
139 sigc::mem_fun(*this, &MacrosSetup::onButtonCancel)
140 );
141
142 m_deleteButton.signal_clicked().connect(
143 sigc::mem_fun(*this, &MacrosSetup::deleteSelectedRows)
144 );
145
146 m_inverseDeleteButton.signal_clicked().connect(
147 sigc::mem_fun(*this, &MacrosSetup::inverseDeleteSelectedRows)
148 );
149
150 signal_hide().connect(
151 sigc::mem_fun(*this, &MacrosSetup::onWindowHide)
152 );
153
154 signal_delete_event().connect(
155 sigc::mem_fun(*this, &MacrosSetup::onWindowDelete)
156 );
157
158 signal_key_press_event().connect(
159 sigc::mem_fun(*this, &MacrosSetup::onKeyPressed)
160 );
161 signal_key_release_event().connect(
162 sigc::mem_fun(*this, &MacrosSetup::onKeyReleased)
163 );
164
165 show_all_children();
166 updateStatus();
167 }
168
169 MacrosSetup::~MacrosSetup() {
170 printf("MacrosSetup destruct\n");
171 }
172
173 void MacrosSetup::setMacros(const std::vector<Serialization::Archive>& macros,
174 Serialization::Archive* pClipboardContent,
175 gig::DimensionRegion* pSelectedDimRgn)
176 {
177 // copy for non-destructive editing
178 m_macros = macros;
179
180 m_clipboardContent = pClipboardContent;
181 m_selectedDimRgn = pSelectedDimRgn;
182
183 reloadTreeView();
184 }
185
186 void MacrosSetup::onButtonAddFromClipboard() {
187 printf("+fromClipboard\n");
188 if (!m_clipboardContent) return;
189 if (!m_clipboardContent->rootObject()) return;
190 m_macros.push_back(*m_clipboardContent);
191 m_modified = true;
192 reloadTreeView();
193 }
194
195 void MacrosSetup::onButtonAddFromSelection() {
196 printf("+fromSelection\n");
197 if (!m_selectedDimRgn) return;
198 std::string errorText;
199 try {
200 Serialization::Archive archive;
201 archive.serialize(m_selectedDimRgn);
202 //archive.setName("Unnamed Macro");
203 m_macros.push_back(archive);
204 m_modified = true;
205 reloadTreeView();
206 } catch (Serialization::Exception e) {
207 errorText = e.Message;
208 } catch (...) {
209 errorText = _("Unknown exception while creating macro");
210 }
211 if (!errorText.empty()) {
212 Glib::ustring txt = _("Couldn't create macro:\n") + errorText;
213 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
214 msg.run();
215 }
216 }
217
218 static Glib::ustring indexToAccKey(uint index) {
219 if (index >= 12) return "";
220 return "F" + ToString(index+1);
221 }
222
223 static int daysAgo(const tm& t) {
224 time_t now;
225 time(&now);
226 tm* pNow = localtime(&now);
227 if (!pNow) return 0;
228 if (pNow->tm_year == t.tm_year &&
229 pNow->tm_mon == t.tm_mon &&
230 pNow->tm_mday == t.tm_mday) return 0;
231 time_t past = mktime((tm*)&t);
232 return ceil(difftime(now, past) / 60.0 / 60.0 / 24.0);
233 }
234
235 static Glib::ustring humanShortStr(const tm& t) {
236 int iDaysAgo = daysAgo(t);
237 char buf[70];
238 if (iDaysAgo == 0) {
239 // C-Time specification for a time somewhere today (see 'man strftime()').
240 if (strftime(buf, sizeof buf, _("%R"), &t))
241 return buf;
242 } else if (iDaysAgo == 1) {
243 // C-Time specification for a time somewhere yesterday (see 'man strftime()').
244 if (strftime(buf, sizeof buf, _("Yesterday %R"), &t))
245 return buf;
246 } else if (iDaysAgo == 2) {
247 // C-Time specification for a time somewhere 2 days ago (see 'man strftime()').
248 if (strftime(buf, sizeof buf, _("2 days ago %R"), &t))
249 return buf;
250 } else {
251 // C-Time specification for a time far more than 2 days ago (see 'man strftime()').
252 if (strftime(buf, sizeof buf, "%d %b %Y", &t))
253 return buf;
254 }
255 return "";
256 }
257
258 void MacrosSetup::reloadTreeView() {
259 m_ignoreTreeViewValueChange = true;
260
261 m_treeStoreMacros->clear();
262
263 for (int iMacro = 0; iMacro < m_macros.size(); ++iMacro) {
264 const Serialization::Archive& macro = m_macros[iMacro];
265
266 Gtk::TreeModel::iterator iter = m_treeStoreMacros->append();
267 Gtk::TreeModel::Row row = *iter;
268 row[m_treeModelMacros.m_col_key] = indexToAccKey(iMacro);
269 row[m_treeModelMacros.m_col_name] = macro.name().empty() ? _("Unnamed Macro") : gig_to_utf8(macro.name());
270 row[m_treeModelMacros.m_col_comment] = macro.comment().empty() ? _("No comment assigned yet.") : gig_to_utf8(macro.comment());
271 row[m_treeModelMacros.m_col_created] = humanShortStr(macro.dateTimeCreated());
272 row[m_treeModelMacros.m_col_modified] = humanShortStr(macro.dateTimeModified());
273 row[m_treeModelMacros.m_col_index] = iMacro;
274 }
275
276 m_treeViewMacros.expand_all();
277
278 updateStatus();
279
280 m_ignoreTreeViewValueChange = false;
281 }
282
283 void MacrosSetup::onTreeViewSelectionChanged() {
284 std::vector<Gtk::TreeModel::Path> v = m_treeViewMacros.get_selection()->get_selected_rows();
285 const bool bValidSelection = !v.empty();
286 m_deleteButton.set_sensitive(bValidSelection);
287 m_inverseDeleteButton.set_sensitive(bValidSelection);
288 }
289
290 // Cmd key on Mac, Ctrl key on all other OSs
291 static const guint primaryKeyL =
292 #if defined(__APPLE__)
293 GDK_KEY_Meta_L;
294 #else
295 GDK_KEY_Control_L;
296 #endif
297
298 static const guint primaryKeyR =
299 #if defined(__APPLE__)
300 GDK_KEY_Meta_R;
301 #else
302 GDK_KEY_Control_R;
303 #endif
304
305 bool MacrosSetup::onKeyPressed(GdkEventKey* key) {
306 //printf("key down 0x%x\n", key->keyval);
307 if (key->keyval == GDK_KEY_Alt_L || key->keyval == GDK_KEY_Alt_R)
308 m_altKeyDown = true;
309 if (key->keyval == primaryKeyL || key->keyval == primaryKeyR)
310 m_primaryKeyDown = true;
311 return false;
312 }
313
314 bool MacrosSetup::onKeyReleased(GdkEventKey* key) {
315 //printf("key up 0x%x\n", key->keyval);
316 if (key->keyval == GDK_KEY_Alt_L || key->keyval == GDK_KEY_Alt_R)
317 m_altKeyDown = false;
318 if (key->keyval == primaryKeyL || key->keyval == primaryKeyR)
319 m_primaryKeyDown = false;
320 if (m_primaryKeyDown && key->keyval == GDK_KEY_b)
321 onButtonAddFromClipboard();
322 if (m_primaryKeyDown && key->keyval == GDK_KEY_s)
323 onButtonAddFromSelection();
324 return false;
325 }
326
327 void MacrosSetup::onMacroTreeViewKeyRelease(GdkEventKey* key) {
328 if (key->keyval == GDK_KEY_BackSpace || key->keyval == GDK_KEY_Delete) {
329 if (m_altKeyDown)
330 inverseDeleteSelectedRows();
331 else if (m_primaryKeyDown)
332 deleteSelectedRows();
333 }
334 }
335
336 void MacrosSetup::onMacroTreeViewRowValueChanged(const Gtk::TreeModel::Path& path,
337 const Gtk::TreeModel::iterator& iter)
338 {
339 if (m_ignoreTreeViewValueChange) return;
340 if (!iter) return;
341 Gtk::TreeModel::Row row = *iter;
342 Glib::ustring name = row[m_treeModelMacros.m_col_name];
343 int index = row[m_treeModelMacros.m_col_index];
344 m_macros[index].setName(name);
345 //reloadTreeView();
346 m_modified = true;
347 updateStatus();
348 }
349
350 void MacrosSetup::deleteSelectedRows() {
351 Glib::RefPtr<Gtk::TreeSelection> sel = m_treeViewMacros.get_selection();
352 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
353 deleteRows(rows);
354 }
355
356 void MacrosSetup::deleteRows(const std::vector<Gtk::TreeModel::Path>& rows) {
357 m_modified = !rows.empty();
358 std::set<int> macros;
359 for (int r = rows.size() - 1; r >= 0; --r) {
360 Gtk::TreeModel::iterator it = m_treeStoreMacros->get_iter(rows[r]);
361 if (!it) continue;
362 Gtk::TreeModel::Row row = *it;
363 macros.insert(
364 row[m_treeModelMacros.m_col_index]
365 );
366 }
367 for (std::set<int>::const_reverse_iterator it = macros.rbegin();
368 it != macros.rend(); ++it)
369 {
370 m_macros.erase(m_macros.begin() + *it);
371 }
372 reloadTreeView();
373 }
374
375 static bool _onEachTreeRow(const Gtk::TreeModel::Path& input, std::vector<Gtk::TreeModel::Path>* output) {
376 output->push_back(input);
377 return false; // continue walking the tree
378 }
379
380 void MacrosSetup::inverseDeleteSelectedRows() {
381 // get all rows of tree view
382 std::vector<Gtk::TreeModel::Path> rows;
383 m_treeViewMacros.get_model()->foreach_path(
384 sigc::bind(
385 sigc::ptr_fun(&_onEachTreeRow),
386 &rows
387 )
388 );
389
390 // erase all entries from "rows" which are currently selected
391 std::vector<Gtk::TreeModel::Path> vSelected = m_treeViewMacros.get_selection()->get_selected_rows();
392 for (int i = rows.size() - 1; i >= 0; --i) {
393 bool bIsSelected = std::find(vSelected.begin(), vSelected.end(),
394 rows[i]) != vSelected.end();
395 if (bIsSelected)
396 rows.erase(rows.begin() + i);
397 }
398
399 // delete those 'inverse' selected rows
400 deleteRows(rows);
401 }
402
403 void MacrosSetup::updateStatus() {
404 m_addFromClipboardButton.set_sensitive(
405 m_clipboardContent && m_clipboardContent->rootObject()
406 );
407 m_addFromSelectionButton.set_sensitive(m_selectedDimRgn);
408 m_applyButton.set_sensitive(isModified());
409 updateStatusBar();
410 }
411
412 void MacrosSetup::updateStatusBar() {
413 // update status text
414 std::string txt;
415 m_statusLabel.set_markup(txt);
416 }
417
418 sigc::signal<void, const std::vector<Serialization::Archive>& >& MacrosSetup::signal_macros_changed()
419 {
420 return m_macros_changed;
421 }
422
423 bool MacrosSetup::onWindowDelete(GdkEventAny* e) {
424 //printf("onWindowDelete\n");
425
426 if (!isModified()) return false; // propagate event further (which will close this window)
427
428 //gchar* msg = g_strdup_printf(_("Apply changes to macro \"%s\" before closing?"),
429 // m_macroOriginal->Name.c_str());
430 gchar* msg = g_strdup_printf(_("Apply changes to macro list before closing?"));
431 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
432 g_free(msg);
433 dialog.set_secondary_text(_("If you close without applying, your changes will be lost."));
434 dialog.add_button(_("Close _Without Applying"), Gtk::RESPONSE_NO);
435 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
436 dialog.add_button(_("_Apply"), Gtk::RESPONSE_YES);
437 dialog.set_default_response(Gtk::RESPONSE_YES);
438 int response = dialog.run();
439 dialog.hide();
440
441 // user decided to close this window without saving
442 if (response == Gtk::RESPONSE_NO)
443 return false; // propagate event further (which will close this window)
444
445 // user cancelled dialog, thus don't close this window
446 if (response == Gtk::RESPONSE_CANCEL) {
447 show();
448 return true; // drop event (prevents closing this window)
449 }
450
451 // user wants to apply the changes, afterwards close window
452 if (response == Gtk::RESPONSE_YES) {
453 onButtonApply();
454 return false; // propagate event further (which will close this window)
455 }
456
457 // should never ever make it to this point actually
458 return false;
459 }
460
461 bool MacrosSetup::isModified() const {
462 if (m_modified) return true;
463 bool bModified = false;
464 for (int i = 0; i < m_macros.size(); ++i) {
465 if (m_macros[i].isModified()) {
466 bModified = true;
467 break;
468 }
469 }
470 return bModified;
471 }
472
473 void MacrosSetup::onButtonCancel() {
474 bool dropEvent = onWindowDelete(NULL);
475 if (dropEvent) return;
476 hide();
477 }
478
479 void MacrosSetup::onButtonApply() {
480 std::string errorText;
481 try {
482 for (int i = 0; i < m_macros.size(); ++i) {
483 if (!m_macros[i].isModified()) continue;
484 // enforce re-encoding the abstract object model and resetting the
485 // 'modified' state
486 m_macros[i].rawData();
487 }
488 m_modified = false;
489 } catch (Serialization::Exception e) {
490 errorText = e.Message;
491 } catch (...) {
492 errorText = _("Unknown exception while applying macro changes");
493 }
494 if (!errorText.empty()) {
495 Glib::ustring txt = _("Couldn't apply macro changes:\n") + errorText;
496 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
497 msg.run();
498 } else {
499 // update MainWindow with edited list of macros
500 m_macros_changed.emit(m_macros);
501 }
502 updateStatus();
503 }
504
505 void MacrosSetup::onWindowHide() {
506 delete this; // this is the end, my friend
507 }

  ViewVC Help
Powered by ViewVC