diff --git a/web/pgadmin/preferences/static/css/preferences.css b/web/pgadmin/preferences/static/css/preferences.css index 9f8de62..cf90e7d 100644 --- a/web/pgadmin/preferences/static/css/preferences.css +++ b/web/pgadmin/preferences/static/css/preferences.css @@ -25,10 +25,6 @@ div.pgadmin-preference-body div.ajs-content { bottom: 0px !important; } -.preferences_content .control-label, .preferences_content .pgadmin-controls { - min-width: 100px !important; -} - .pgadmin-preference-body { min-width: 300px !important; min-height: 400px !important; diff --git a/web/pgadmin/preferences/static/js/preferences.js b/web/pgadmin/preferences/static/js/preferences.js index d025be1..f487782 100644 --- a/web/pgadmin/preferences/static/js/preferences.js +++ b/web/pgadmin/preferences/static/js/preferences.js @@ -256,6 +256,8 @@ define('pgadmin.preferences', [ return 'textarea'; case 'switch': return 'switch'; + case 'keyboardshortcut': + return 'keyboardshortcut'; default: if (console && console.warn) { // Warning for developer only. diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index 4f3b144..f75270a 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -79,9 +79,11 @@ define([ return type; }; - var BackformControlInit = Backform.Control.prototype.initialize, - BackformControlRemove = Backform.Control.prototype.remove; + BackformControlRemove = Backform.Control.prototype.remove, + capitalizeFirstLetter = function(text) { + return text[0].toUpperCase() + text.slice(1); + }; // Override the Backform.Control to allow to track changes in dependencies, // and rerender the View element @@ -2504,5 +2506,213 @@ define([ }, }); + var KeyboardshortcutControl = Backform.KeyboardshortcutControl = Backform.Control.extend({ + default:{key_codes:{ + 3 : "break", + 8 : "backspace / delete", + 9 : "tab", + 12 : 'clear', + 13 : "enter", + 19 : "pause/break", + 20 : "caps lock", + 27 : "escape", + 32 : "spacebar", + 33 : "page up", + 34 : "page down", + 35 : "end", + 36 : "home", + 37 : "left arrow", + 38 : "up arrow", + 39 : "right arrow", + 40 : "down arrow", + 41 : "select", + 43 : "execute", + 44 : "Print Screen", + 45 : "insert", + 46 : "delete", + 47 : "help", + 48 : "0", + 49 : "1", + 50 : "2", + 51 : "3", + 52 : "4", + 53 : "5", + 54 : "6", + 55 : "7", + 56 : "8", + 57 : "9", + 58 : ":", + 59 : "semicolon (;)", + 60 : "<", + 61 : "equals (=)", + 65 : "a", + 66 : "b", + 67 : "c", + 68 : "d", + 69 : "e", + 70 : "f", + 71 : "g", + 72 : "h", + 73 : "i", + 74 : "j", + 75 : "k", + 76 : "l", + 77 : "m", + 78 : "n", + 79 : "o", + 80 : "p", + 81 : "q", + 82 : "r", + 83 : "s", + 84 : "t", + 85 : "u", + 86 : "v", + 87 : "w", + 88 : "x", + 89 : "y", + 90 : "z", + 91 : "Windows Key / Left ⌘ / Chromebook Search key", + 92 : "right window key", + 93 : "Windows Menu / Right ⌘", + 95: "sleep", + 96 : "numpad 0", + 97 : "numpad 1", + 98 : "numpad 2", + 99 : "numpad 3", + 100 : "numpad 4", + 101 : "numpad 5", + 102 : "numpad 6", + 103 : "numpad 7", + 104 : "numpad 8", + 105 : "numpad 9", + 106 : "multiply", + 107 : "add", + 108 : "numpad period (.)", + 109 : "subtract", + 110 : "decimal point", + 111 : "divide", + 112 : "f1", + 113 : "f2", + 114 : "f3", + 115 : "f4", + 116 : "f5", + 117 : "f6", + 118 : "f7", + 119 : "f8", + 120 : "f9", + 121 : "f10", + 122 : "f11", + 123 : "f12", + 144 : "num lock", + 145 : "scroll lock", + 166 : "page backward", + 167 : "page forward", + 172 : "home key", + 191 : "forward slash", + 219 : "open bracket", + 220 : "back slash", + 221 : "close bracket", + 222 : "single quote", + 223 : "`", + 225 : "altgr", + 240 : "alphanumeric" + }}, + initialize: function() { + + Backform.Control.prototype.initialize.apply(this, arguments); + + var self = this, + initial_value = {}, + value = self.model.get(self.field.get('name')); + + var fields = self.field.get('fields') + + if (fields == null || fields == undefined) { + throw new ReferenceError('"fields" not found in keyboard shortcut'); + } + + _.each(fields, function(field) { + initial_value[field['name']] = value[field['name']]; + }); + + self.innerModel = new Backbone.Model(initial_value); + + self.innerModel.on('change', function() { + var val = _.clone(self.model.get('value')); + if ('key_code' in val) { + val['key_code'] = parseInt(val['key_code']) + } + self.model.set('value', + _.extend(val, this.toJSON()) + ); + }); + + self.controls = []; + }, + cleanup: function() { + _.each(this.controls, function(c) { + c.remove(); + }); + this.controls.length = 0; + }, + template: _.template([ + '', + '
', + '
' + ].join("\n")), + render: function() { + this.cleanup(); + this.$el.empty(); + + var self = this, + field = this.field.toJSON(); + + this.$el.html(self.template(field)).addClass(field.name); + + var $container = $(self.$el.find('.pgadmin-controls')); + + var op = []; + + _.each(self.field.get('fields'), function(field) { + + if (field['name'] == 'key_code') { + for (var key_code in self.default.key_codes) { + op.push({ + label: capitalizeFirstLetter(self.default.key_codes[key_code]), + value: key_code + }); + } + } + + var f = new Backform.Field( + _.extend({}, { + id: field['name'], + name: field['name'], + control: field['type'], + label: capitalizeFirstLetter(field['name'].replace('_',' ')), + options: op, + }) + ), + cntr = new (f.get("control")) ({ + field: f, + model: self.innerModel, + }); + + $container.append($('
').append(cntr.render().$el)); + // We will keep track of all the controls rendered at the + // moment. + self.controls.push(cntr); + + }); + + return self; + }, + remove: function() { + /* First do the clean up */ + this.cleanup(); + Backform.Control.prototype.remove.apply(this, arguments); + } + }); + return Backform; }); diff --git a/web/pgadmin/utils/preferences.py b/web/pgadmin/utils/preferences.py index 2598da1..c9a8bc2 100644 --- a/web/pgadmin/utils/preferences.py +++ b/web/pgadmin/utils/preferences.py @@ -13,6 +13,7 @@ module within the system. """ import decimal +import simplejson as json import dateutil.parser as dateutil_parser from flask import current_app @@ -31,7 +32,7 @@ class _Preference(object): def __init__( self, cid, name, label, _type, default, help_str=None, min_val=None, - max_val=None, options=None, select2=None + max_val=None, options=None, select2=None, fields=None ): """ __init__ @@ -54,6 +55,8 @@ class _Preference(object): :param max_val: maximum value :param options: options (Array of list objects) :param select2: select2 options (object) + :param fields: field schema (if preference has more than one field to + take input from user e.g. keyboardshortcut preference) :returns: nothing """ @@ -67,6 +70,7 @@ class _Preference(object): self.max_val = max_val self.options = options self.select2 = select2 + self.fields = fields # Look into the configuration table to find out the id of the specific # preference. @@ -137,6 +141,12 @@ class _Preference(object): if self._type == 'text': if res.value == '': return self.default + if self._type == 'keyboardshortcut': + try: + return json.loads(res.value) + except Exception as e: + current_app.logger.exeception(e) + return self.default return res.value @@ -196,6 +206,14 @@ class _Preference(object): if not has_value and self.select2 and not self.select2['tags']: return False, gettext("Invalid value for an options option.") + elif self._type == 'keyboardshortcut': + try: + value = json.dumps(value) + except Exception as e: + current_app.logger.exeception(e) + return False, gettext( + "Invalid value for a keyboardshortcut option." + ) pref = UserPrefTable.query.filter_by( pid=self.pid @@ -231,7 +249,8 @@ class _Preference(object): 'max_val': self.max_val, 'options': self.options, 'select2': self.select2, - 'value': self.get() + 'value': self.get(), + 'fields': self.fields, } return res @@ -371,7 +390,7 @@ class Preferences(object): def register( self, category, name, label, _type, default, min_val=None, max_val=None, options=None, help_str=None, category_label=None, - select2=None + select2=None, fields=None ): """ register @@ -393,6 +412,8 @@ class Preferences(object): :param help_str: :param category_label: :param select2: select2 control extra options + :param fields: field schema (if preference has more than one field to + take input from user e.g. keyboardshortcut preference) """ cat = self.__category(category, category_label) if name in cat['preferences']: @@ -402,12 +423,13 @@ class Preferences(object): assert _type is not None, "Type for a preference cannot be none!" assert _type in ( 'boolean', 'integer', 'numeric', 'date', 'datetime', - 'options', 'multiline', 'switch', 'node', 'text' + 'options', 'multiline', 'switch', 'node', 'text', + 'keyboardshortcut' ), "Type cannot be found in the defined list!" (cat['preferences'])[name] = res = _Preference( cat['id'], name, label, _type, default, help_str, min_val, - max_val, options, select2 + max_val, options, select2, fields ) return res