diff --git a/CHANGELOG b/CHANGELOG
index 2ac0e89986..f12e957b82 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,4 @@
+* Add keyboard shortcuts
* Update states of both toolbar and menu
* Improve toolbar order
* Use login details in title
diff --git a/bower.json b/bower.json
index 8ff97da98b..9d6136f7fc 100644
--- a/bower.json
+++ b/bower.json
@@ -23,7 +23,8 @@
"d3": "^3.5",
"c3": "^0.4",
"papaparse": "^4.1",
- "fullcalendar": "^3.0"
+ "fullcalendar": "^3.0",
+ "mousetrap": "^1.6"
},
"devDependencies": {
"qunit": "^1.18"
diff --git a/index.html b/index.html
index d6afce75a2..3d0d06387d 100644
--- a/index.html
+++ b/index.html
@@ -20,6 +20,7 @@ this repository contains the full copyright notices and license terms. -->
+
diff --git a/src/sao.js b/src/sao.js
index b85082d8cb..ee3d7c15ca 100644
--- a/src/sao.js
+++ b/src/sao.js
@@ -529,7 +529,8 @@ var Sao = {};
'class': 'global-search-container'
});
this.search_entry = jQuery('', {
- 'class': 'form-control',
+ 'id': 'global-search-entry',
+ 'class': 'form-control mousetrap',
'placeholder': Sao.i18n.gettext('Search...')
});
this.el.append(this.search_entry);
@@ -591,6 +592,166 @@ var Sao = {};
}
});
+ function shortcuts_defs() {
+ // Shortcuts available on Tab on this format:
+ // {shortcut, label, id of tab button or callback method}
+ return [
+ {
+ shortcut: 'ctrl+a',
+ label: Sao.i18n.gettext('New'),
+ id: 'new_',
+ }, {
+ shortcut: 'ctrl+s',
+ label: Sao.i18n.gettext('Save'),
+ id: 'save',
+ }, {
+ shortcut: 'ctrl+l',
+ label: Sao.i18n.gettext('Switch'),
+ id: 'switch_',
+ }, {
+ shortcut: 'ctrl+r',
+ label: Sao.i18n.gettext('Reload/Undo'),
+ id: 'reload',
+ }, {
+ shortcut: 'ctrl+shift+d',
+ label: Sao.i18n.gettext('Duplicate'),
+ id: 'copy',
+ }, {
+ shortcut: 'ctrl+d',
+ label: Sao.i18n.gettext('Delete'),
+ id: 'delete_',
+ }, {
+ shortcut: 'ctrl+up',
+ label: Sao.i18n.gettext('Previous'),
+ id: 'previous',
+ }, {
+ shortcut: 'ctrl+down',
+ label: Sao.i18n.gettext('Next'),
+ id: 'next',
+ }, {
+ shortcut: 'ctrl+f',
+ label: Sao.i18n.gettext('Search'),
+ id: 'search',
+ }, {
+ shortcut: 'ctrl+x',
+ label: Sao.i18n.gettext('Close Tab'),
+ id: 'close',
+ }, {
+ shortcut: 'ctrl+shift+t',
+ label: Sao.i18n.gettext('Attachment'),
+ id: 'attach',
+ }, {
+ shortcut: 'ctrl+shift+o',
+ label: Sao.i18n.gettext('Note'),
+ id: 'note',
+ }, {
+ shortcut: 'ctrl+e',
+ label: Sao.i18n.gettext('Action'),
+ id: 'action',
+ }, {
+ shortcut: 'ctrl+shift+r',
+ label: Sao.i18n.gettext('Relate'),
+ id: 'relate',
+ }, {
+ shortcut: 'ctrl+p',
+ label: Sao.i18n.gettext('Print'),
+ id: 'print',
+ }, {
+ shortcut: 'ctrl+left',
+ label: Sao.i18n.gettext('Previous tab'),
+ callback: function() {
+ Sao.Tab.previous_tab();
+ },
+ }, {
+ shortcut: 'ctrl+right',
+ label: Sao.i18n.gettext('Next tab'),
+ callback: function() {
+ Sao.Tab.next_tab();
+ },
+ }, {
+ shortcut: 'ctrl+k',
+ label: Sao.i18n.gettext('Global search'),
+ callback: function() {
+ jQuery('#global-search-entry').focus();
+ },
+ }, {
+ shortcut: 'ctrl+h',
+ label: Sao.i18n.gettext('Show this help'),
+ callback: function() {
+ shortcuts_dialog();
+ },
+ },
+ ];
+ }
+
+ jQuery(document).ready(function() {
+ set_shortcuts();
+ });
+
+ function set_shortcuts() {
+ shortcuts_defs().forEach(function(definition) {
+ Mousetrap.bind(definition.shortcut, function() {
+ if (definition.id){
+ var current_tab = Sao.Tab.tabs.get_current();
+ if (current_tab) {
+ var focused = $(':focus');
+ focused.blur();
+ current_tab.el.find('a[id="' + definition.id + '"]').click();
+ focused.focus();
+ }
+ } else if (definition.callback) {
+ jQuery.when().then(definition.callback);
+ }
+ return false;
+ });
+ });
+ }
+
+ function shortcuts_dialog() {
+ var dialog = new Sao.Dialog(Sao.i18n.gettext('Keyboard shortcuts'),
+ 'shortcut-dialog', 'm');
+ jQuery('