diff --git a/web/pgadmin/preferences/static/css/preferences.css b/web/pgadmin/preferences/static/css/preferences.css
index 9f8de62..509a0a2 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;
@@ -40,3 +36,11 @@ div.pgadmin-preference-body div.ajs-content {
min-height: 480px !important;
}
}
+
+.keyboard-shortcut-label {
+ font-weight: normal !important;
+}
+
+.preferences_content input[type="checkbox"]:focus {
+ outline: 5px auto -webkit-focus-ring-color;
+}
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..4794827 100644
--- a/web/pgadmin/static/js/backform.pgadmin.js
+++ b/web/pgadmin/static/js/backform.pgadmin.js
@@ -79,7 +79,6 @@ define([
return type;
};
-
var BackformControlInit = Backform.Control.prototype.initialize,
BackformControlRemove = Backform.Control.prototype.remove;
@@ -2504,5 +2503,209 @@ define([
},
});
+ var ControlFormatter = Backform.KeyCodeControlFormatter = function() {};
+
+ _.extend(ControlFormatter.prototype, {
+ fromRaw: function (rawData) {
+ return rawData['char'];
+ },
+ // we don't need toRaw
+ toRaw: undefined,
+ });
+
+ Backform.KeyCodeControl = Backform.InputControl.extend({
+ defaults: _.defaults({
+ maxlength: 1,
+ }, Backform.InputControl.prototype.defaults),
+
+ events: {
+ 'keydown input': 'onkeyDown',
+ 'keyup input': 'preventEvent',
+ 'focus select': 'clearInvalid',
+ },
+
+ formatter: Backform.KeyCodeControlFormatter,
+
+ preventEvent: function(e) {
+ var key_code = e.which || e.keyCode;
+
+ if ([16, 17, 18, 27].indexOf(key_code) != -1) {
+ // Shift, Ctrl, Alt/Option, Escape
+ return;
+ }
+
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ e.stopPropagation();
+ },
+
+ template: _.template([
+ '',
+ '
',
+ ' <%=required ? "required" : ""%> />',
+ ' <% if (helpMessage && helpMessage.length) { %>',
+ ' <%=helpMessage%>',
+ ' <% } %>',
+ '
',
+ ].join('\n')),
+ onkeyDown: function(e) {
+ var self = this,
+ model = this.model,
+ attrArr = this.field.get('name').split('.'),
+ name = attrArr.shift(),
+ changes = {},
+ key_code = e.which || e.keyCode,
+ key;
+
+ if ([16, 17, 18, 27].indexOf(key_code) != -1) {
+ // Shift, Ctrl, Alt/Option, Escape
+ return;
+ }
+
+ if (this.model.errorModel instanceof Backbone.Model) {
+ this.model.errorModel.unset(name);
+ }
+ key = gettext(e.key);
+ if (key_code == 32) {
+ key = gettext('Space');
+ }
+ changes = {
+ 'key_code': e.which || e.keyCode,
+ 'char': key,
+ };
+
+ this.stopListening(this.model, 'change:' + name, this.render);
+ model.set(name, changes);
+ this.listenTo(this.model, 'change:' + name, this.render);
+ setTimeout(function() {
+ self.$el.find('input').val(key);
+ });
+ e.preventDefault();
+ },
+ keyPathAccessor: function(obj, path) {
+ var res = obj;
+ path = path.split('.');
+ for (var i = 0; i < path.length; i++) {
+ if (_.isNull(res)) return null;
+ if (_.isEmpty(path[i])) continue;
+ if (!_.isUndefined(res[path[i]])) res = res[path[i]];
+ }
+ return res;
+ },
+ });
+
+ Backform.KeyboardshortcutControl = Backform.Control.extend({
+
+ 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'));
+ self.model.set('value',
+ $.extend(true, 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 = _.defaults(this.field.toJSON(), this.defaults);
+
+ this.$el.html(self.template(field)).addClass(field.name);
+
+ var $container = $(self.$el.find('.pgadmin-controls'));
+
+ var op = [],
+ label,
+ controlLabel;
+
+ _.each(field['fields'], function(fld) {
+ // reset labels (of previous control If any)
+ label = undefined;
+ controlLabel = undefined;
+
+ if (fld['type'] == 'checkbox') {
+ controlLabel = fld['label'];
+ } else {
+ label = fld['label'];
+ }
+ var f = new Backform.Field(
+ _.extend({}, {
+ id: fld['name'],
+ name: fld['name'],
+ control: fld['type'],
+ label: label,
+ controlLabel: controlLabel,
+ options: op,
+ })
+ ),
+ cntr = new (f.get('control')) ({
+ field: f,
+ model: self.innerModel,
+ });
+
+ cntr.render();
+ $(cntr.$el.find('.control-label')).addClass('keyboard-shortcut-label');
+
+ if(fld['type'] == 'checkbox') {
+ $(cntr.$el.find('.control-label'))
+ .addClass('pg-el-sm-10')
+ .removeClass('pg-el-sm-3');
+
+ $(cntr.$el.find('.pgadmin-controls'))
+ .addClass('pg-el-sm-2')
+ .removeClass('pg-el-sm-9');
+ $container.append($('').append(cntr.$el));
+ } else {
+ $container.append($('').append(cntr.$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