fix #348: configurable system-wide shortcuts

This commit is contained in:
antelle 2019-09-14 16:36:30 +02:00
parent 3859beb543
commit 56d84e1d18
15 changed files with 299 additions and 77 deletions

View File

@ -291,6 +291,9 @@ const Launcher = {
} else { } else {
this.pendingFileToOpen = file; this.pendingFileToOpen = file;
} }
},
setGlobalShortcuts(appSettings) {
this.remoteApp().setGlobalShortcuts(appSettings);
} }
}; };

View File

@ -3,6 +3,7 @@ const Alerts = require('./alerts');
const Locale = require('../util/locale'); const Locale = require('../util/locale');
const Logger = require('../util/logger'); const Logger = require('../util/logger');
const FeatureDetector = require('../util/feature-detector'); const FeatureDetector = require('../util/feature-detector');
const Shortcuts = require('../comp/shortcuts');
const Otp = require('../util/otp'); const Otp = require('../util/otp');
const QrCode = require('jsqrcode'); const QrCode = require('jsqrcode');
@ -14,7 +15,7 @@ const OtpQrReader = {
fileInput: null, fileInput: null,
read() { read() {
let screenshotKey = FeatureDetector.screenshotToClipboardShortcut(); let screenshotKey = Shortcuts.screenshotToClipboardShortcut();
if (screenshotKey) { if (screenshotKey) {
screenshotKey = Locale.detSetupOtpAlertBodyWith.replace( screenshotKey = Locale.detSetupOtpAlertBodyWith.replace(
'{}', '{}',
@ -25,7 +26,7 @@ const OtpQrReader = {
? '' ? ''
: Locale.detSetupOtpAlertBodyWith.replace( : Locale.detSetupOtpAlertBodyWith.replace(
'{}', '{}',
'<code>' + FeatureDetector.actionShortcutSymbol() + 'V</code>' '<code>' + Shortcuts.actionShortcutSymbol() + 'V</code>'
); );
OtpQrReader.startListenClipoard(); OtpQrReader.startListenClipoard();
const buttons = [ const buttons = [

View File

@ -0,0 +1,147 @@
const FeatureDetector = require('../util/feature-detector');
const Keys = require('../const/keys');
const Format = require('../util/format');
const AppSettingsModel = require('../models/app-settings-model');
const Launcher = require('./launcher');
let allowedKeys;
function getAllowedKeys() {
if (!allowedKeys) {
allowedKeys = {};
for (const [name, code] of Object.entries(Keys)) {
const keyName = name.replace('DOM_VK_', '');
if (/^([0-9A-Z]|F\d{1,2})$/.test(keyName)) {
allowedKeys[code] = keyName;
}
}
}
return allowedKeys;
}
const globalShortcuts = {
copyPassword: { mac: 'Ctrl+Alt+C', all: 'Shift+Alt+C' },
copyUser: { mac: 'Ctrl+Alt+B', all: 'Shift+Alt+B' },
copyUrl: { mac: 'Ctrl+Alt+U', all: 'Shift+Alt+U' },
autoType: { mac: 'Ctrl+Alt+T', all: 'Shift+Alt+T' }
};
const Shortcuts = {
keyEventToShortcut(event) {
const modifiers = [];
if (event.ctrlKey) {
modifiers.push('Ctrl');
}
if (event.altKey) {
modifiers.push('Alt');
}
if (event.shiftKey) {
modifiers.push('Shift');
}
if (FeatureDetector.isMac && event.metaKey) {
modifiers.push('Meta');
}
const keyName = getAllowedKeys()[event.which];
return {
value: modifiers.join('+') + '+' + (keyName || '…'),
valid: modifiers.length > 0 && !!keyName
};
},
presentShortcut(shortcutValue, formatting) {
return shortcutValue
.split(/\+/g)
.map(part => {
switch (part) {
case 'Ctrl':
return this.ctrlShortcutSymbol(formatting);
case 'Alt':
return this.altShortcutSymbol(formatting);
case 'Shift':
return this.shiftShortcutSymbol(formatting);
case 'Meta':
return this.actionShortcutSymbol(formatting);
default:
return part;
}
})
.join('');
},
actionShortcutSymbol(formatting) {
return FeatureDetector.isMac
? '⌘'
: formatting
? '<span class="thin">ctrl + </span>'
: 'ctrl+';
},
altShortcutSymbol(formatting) {
return FeatureDetector.isMac
? '⌥'
: formatting
? '<span class="thin">alt + </span>'
: 'alt+';
},
shiftShortcutSymbol(formatting) {
return FeatureDetector.isMac
? '⇧'
: formatting
? '<span class="thin">shift + </span>'
: 'shift+';
},
ctrlShortcutSymbol(formatting) {
return FeatureDetector.isMac
? '⌃'
: formatting
? '<span class="thin">ctrl + </span>'
: 'ctrl+';
},
globalShortcutText(type, formatting) {
return this.presentShortcut(this.globalShortcut(type), formatting);
},
globalShortcut(type) {
const appSettingsShortcut = AppSettingsModel.instance.get(
this.globalShortcutAppSettingsKey(type)
);
if (appSettingsShortcut) {
return appSettingsShortcut;
}
const globalShortcut = globalShortcuts[type];
if (globalShortcut) {
if (FeatureDetector.isMac && globalShortcut.mac) {
return globalShortcut.mac;
}
return globalShortcut.all;
}
return undefined;
},
setGlobalShortcut(type, value) {
if (!globalShortcuts[type]) {
throw new Error('Bad shortcut: ' + type);
}
if (value) {
AppSettingsModel.instance.set(this.globalShortcutAppSettingsKey(type), value);
} else {
AppSettingsModel.instance.unset(this.globalShortcutAppSettingsKey(type));
}
Launcher.setGlobalShortcuts(AppSettingsModel.instance.attributes);
},
globalShortcutAppSettingsKey(type) {
return 'globalShortcut' + Format.capFirst(type);
},
screenshotToClipboardShortcut() {
if (FeatureDetector.isiOS) {
return 'Sleep+Home';
}
if (FeatureDetector.isMobile) {
return '';
}
if (FeatureDetector.isMac) {
return 'Command-Shift-Control-4';
}
if (FeatureDetector.isWindows) {
return 'Alt+PrintScreen';
}
return '';
}
};
module.exports = Shortcuts;

View File

@ -513,6 +513,7 @@
"setShCopyUrlGlobal": "copy website (when the app is in background)", "setShCopyUrlGlobal": "copy website (when the app is in background)",
"setShAutoTypeGlobal": "auto-type (when the app is in background)", "setShAutoTypeGlobal": "auto-type (when the app is in background)",
"setShLock": "lock database", "setShLock": "lock database",
"setShEdit": "Press a new key combination to set it as shortcut",
"setPlInstallTitle": "Install new plugins", "setPlInstallTitle": "Install new plugins",
"setPlInstallDesc": "KeeWeb plugins add features, themes, and languages to KeeWeb. Plugins run with the same privileges as KeeWeb, they can access and manage all your passwords. Never install plugins you don't trust.", "setPlInstallDesc": "KeeWeb plugins add features, themes, and languages to KeeWeb. Plugins run with the same privileges as KeeWeb, they can access and manage all your passwords. Never install plugins you don't trust.",

View File

@ -17,40 +17,6 @@ const FeatureDetector = {
!/^http(s?):\/\/((localhost:8085)|((app|beta)\.keeweb\.info))/.test(location.href), !/^http(s?):\/\/((localhost:8085)|((app|beta)\.keeweb\.info))/.test(location.href),
needFixClicks: /Edge\/14/.test(navigator.appVersion), needFixClicks: /Edge\/14/.test(navigator.appVersion),
actionShortcutSymbol(formatting) {
return this.isMac ? '⌘' : formatting ? '<span class="thin">ctrl + </span>' : 'ctrl-';
},
altShortcutSymbol(formatting) {
return this.isMac ? '⌥' : formatting ? '<span class="thin">alt + </span>' : 'alt-';
},
shiftShortcutSymbol(formatting) {
return this.isMac ? '⇧' : formatting ? '<span class="thin">shift + </span>' : 'shift-';
},
globalShortcutSymbol(formatting) {
return this.isMac
? '⌃⌥'
: formatting
? '<span class="thin">shift+alt+</span>'
: 'shift-alt-';
},
globalShortcutIsLarge() {
return !this.isMac;
},
screenshotToClipboardShortcut() {
if (this.isiOS) {
return 'Sleep+Home';
}
if (this.isMobile) {
return '';
}
if (this.isMac) {
return 'Command-Shift-Control-4';
}
if (this.isWindows) {
return 'Alt+PrintScreen';
}
return '';
},
supportsTitleBarStyles() { supportsTitleBarStyles() {
return this.isMac; return this.isMac;
}, },

View File

@ -5,7 +5,7 @@ const Locale = require('../../util/locale');
const AppSettingsModel = require('../../models/app-settings-model'); const AppSettingsModel = require('../../models/app-settings-model');
const EntryPresenter = require('../../presenters/entry-presenter'); const EntryPresenter = require('../../presenters/entry-presenter');
const Scrollable = require('../../mixins/scrollable'); const Scrollable = require('../../mixins/scrollable');
const FeatureDetector = require('../../util/feature-detector'); const Shortcuts = require('../../comp/shortcuts');
const DropdownView = require('../dropdown-view'); const DropdownView = require('../dropdown-view');
const Format = require('../../util/format'); const Format = require('../../util/format');
@ -108,9 +108,9 @@ const AutoTypePopupView = Backbone.View.extend({
filterText: this.model.filter.text, filterText: this.model.filter.text,
topMessage, topMessage,
itemsHtml, itemsHtml,
actionSymbol: FeatureDetector.actionShortcutSymbol(true), actionSymbol: Shortcuts.actionShortcutSymbol(true),
altSymbol: FeatureDetector.altShortcutSymbol(true), altSymbol: Shortcuts.altShortcutSymbol(true),
shiftSymbol: FeatureDetector.shiftShortcutSymbol(true), shiftSymbol: Shortcuts.shiftShortcutSymbol(true),
keyEnter: Locale.keyEnter keyEnter: Locale.keyEnter
}); });
document.activeElement.blur(); document.activeElement.blur();

View File

@ -1,5 +1,5 @@
const Backbone = require('backbone'); const Backbone = require('backbone');
const FeatureDetector = require('../../util/feature-detector'); const Shortcuts = require('../../comp/shortcuts');
const DetailsAttachmentView = Backbone.View.extend({ const DetailsAttachmentView = Backbone.View.extend({
template: require('templates/details/details-attachment.hbs'), template: require('templates/details/details-attachment.hbs'),
@ -9,7 +9,7 @@ const DetailsAttachmentView = Backbone.View.extend({
render(complete) { render(complete) {
this.renderTemplate({}, true); this.renderTemplate({}, true);
const shortcut = this.$el.find('.details__attachment-preview-download-text-shortcut'); const shortcut = this.$el.find('.details__attachment-preview-download-text-shortcut');
shortcut.html(FeatureDetector.actionShortcutSymbol(false)); shortcut.html(Shortcuts.actionShortcutSymbol(false));
const blob = new Blob([this.model.getBinary()], { type: this.model.mimeType }); const blob = new Blob([this.model.getBinary()], { type: this.model.mimeType });
const dataEl = this.$el.find('.details__attachment-preview-data'); const dataEl = this.$el.find('.details__attachment-preview-data');
switch ((this.model.mimeType || '').split('/')[0]) { switch ((this.model.mimeType || '').split('/')[0]) {

View File

@ -1,7 +1,7 @@
const Backbone = require('backbone'); const Backbone = require('backbone');
const AutoTypeHintView = require('../auto-type-hint-view'); const AutoTypeHintView = require('../auto-type-hint-view');
const Locale = require('../../util/locale'); const Locale = require('../../util/locale');
const FeatureDetector = require('../../util/feature-detector'); const Shortcuts = require('../../comp/shortcuts');
const AutoType = require('../../auto-type'); const AutoType = require('../../auto-type');
const DetailsAutoTypeView = Backbone.View.extend({ const DetailsAutoTypeView = Backbone.View.extend({
@ -22,8 +22,8 @@ const DetailsAutoTypeView = Backbone.View.extend({
render() { render() {
const detAutoTypeShortcutsDesc = Locale.detAutoTypeShortcutsDesc const detAutoTypeShortcutsDesc = Locale.detAutoTypeShortcutsDesc
.replace('{}', FeatureDetector.actionShortcutSymbol() + 'T') .replace('{}', Shortcuts.actionShortcutSymbol() + 'T')
.replace('{}', FeatureDetector.globalShortcutSymbol() + 'T'); .replace('{}', Shortcuts.globalShortcutText('autoType'));
this.renderTemplate({ this.renderTemplate({
enabled: this.model.getEffectiveEnableAutoType(), enabled: this.model.getEffectiveEnableAutoType(),
obfuscation: this.model.autoTypeObfuscation, obfuscation: this.model.autoTypeObfuscation,

View File

@ -3,6 +3,7 @@ const Keys = require('../const/keys');
const KeyHandler = require('../comp/key-handler'); const KeyHandler = require('../comp/key-handler');
const DropdownView = require('./dropdown-view'); const DropdownView = require('./dropdown-view');
const FeatureDetector = require('../util/feature-detector'); const FeatureDetector = require('../util/feature-detector');
const Shortcuts = require('../comp/shortcuts');
const Format = require('../util/format'); const Format = require('../util/format');
const Locale = require('../util/locale'); const Locale = require('../util/locale');
const Comparators = require('../util/comparators'); const Comparators = require('../util/comparators');
@ -139,7 +140,7 @@ const ListSearchView = Backbone.View.extend({
: ' <span class="muted-color">(' + : ' <span class="muted-color">(' +
Locale.searchShiftClickOr + Locale.searchShiftClickOr +
' ' + ' ' +
FeatureDetector.altShortcutSymbol(true) + Shortcuts.altShortcutSymbol(true) +
'N)</span>'; 'N)</span>';
this.createOptions = [ this.createOptions = [
{ value: 'entry', icon: 'key', text: Format.capFirst(Locale.entry) + entryDesc }, { value: 'entry', icon: 'key', text: Format.capFirst(Locale.entry) + entryDesc },

View File

@ -1,6 +1,6 @@
const Backbone = require('backbone'); const Backbone = require('backbone');
const OpenConfigView = require('../open-config-view'); const OpenConfigView = require('../open-config-view');
const FeatureDetector = require('../../util/feature-detector'); const Shortcuts = require('../../comp/shortcuts');
const PasswordGenerator = require('../../util/password-generator'); const PasswordGenerator = require('../../util/password-generator');
const Alerts = require('../../comp/alerts'); const Alerts = require('../../comp/alerts');
const Launcher = require('../../comp/launcher'); const Launcher = require('../../comp/launcher');
@ -84,7 +84,7 @@ const SettingsFileView = Backbone.View.extend({
storageProviders.sort((x, y) => (x.uipos || Infinity) - (y.uipos || Infinity)); storageProviders.sort((x, y) => (x.uipos || Infinity) - (y.uipos || Infinity));
const backup = this.model.get('backup'); const backup = this.model.get('backup');
this.renderTemplate({ this.renderTemplate({
cmd: FeatureDetector.actionShortcutSymbol(true), cmd: Shortcuts.actionShortcutSymbol(true),
supportFiles: !!Launcher, supportFiles: !!Launcher,
desktopLink: Links.Desktop, desktopLink: Links.Desktop,
name: this.model.get('name'), name: this.model.get('name'),

View File

@ -1,18 +1,98 @@
const Backbone = require('backbone'); const Backbone = require('backbone');
const Locale = require('../../util/locale');
const Keys = require('../../const/keys');
const Launcher = require('../../comp/launcher'); const Launcher = require('../../comp/launcher');
const Shortcuts = require('../../comp/shortcuts');
const FeatureDetector = require('../../util/feature-detector'); const FeatureDetector = require('../../util/feature-detector');
const SettingsShortcutsView = Backbone.View.extend({ const SettingsShortcutsView = Backbone.View.extend({
template: require('templates/settings/settings-shortcuts.hbs'), template: require('templates/settings/settings-shortcuts.hbs'),
systemShortcuts: [
'Meta+A',
'Alt+A',
'Alt+C',
'Alt+D',
'Meta+F',
'Meta+C',
'Meta+B',
'Meta+U',
'Meta+T',
'Alt+N',
'Meta+O',
'Meta+S',
'Meta+G',
'Meta+,',
'Meta+L'
],
events: {
'click button.shortcut': 'shortcutClick'
},
render() { render() {
this.renderTemplate({ this.renderTemplate({
cmd: FeatureDetector.actionShortcutSymbol(true), cmd: Shortcuts.actionShortcutSymbol(true),
alt: FeatureDetector.altShortcutSymbol(true), alt: Shortcuts.altShortcutSymbol(true),
global: FeatureDetector.globalShortcutSymbol(true), globalIsLarge: !FeatureDetector.isMac,
globalIsLarge: FeatureDetector.globalShortcutIsLarge(), autoTypeSupported: !!Launcher,
globalShortcutsSupported: !!Launcher, globalShortcuts: Launcher
autoTypeSupported: !!Launcher ? {
copyPassword: Shortcuts.globalShortcutText('copyPassword', true),
copyUser: Shortcuts.globalShortcutText('copyUser', true),
copyUrl: Shortcuts.globalShortcutText('copyUrl', true),
autoType: Shortcuts.globalShortcutText('autoType', true)
}
: undefined
});
},
shortcutClick(e) {
const globalShortcutType = e.target.dataset.shortcut;
const shortcutEditor = $('<div/>').addClass('shortcut__editor');
$('<div/>')
.text(Locale.setShEdit)
.appendTo(shortcutEditor);
const shortcutEditorInput = $('<input/>')
.addClass('shortcut__editor-input')
.val(Shortcuts.globalShortcutText(globalShortcutType))
.appendTo(shortcutEditor);
if (!FeatureDetector.isMac) {
shortcutEditorInput.addClass('shortcut__editor-input--large');
}
shortcutEditor.insertAfter($(e.target).parent());
shortcutEditorInput.focus();
shortcutEditorInput.on('blur', () => shortcutEditor.remove());
shortcutEditorInput.on('keypress', e => e.preventDefault());
shortcutEditorInput.on('keydown', e => {
e.preventDefault();
e.stopImmediatePropagation();
if (e.which === Keys.DOM_VK_DELETE || e.which === Keys.DOM_VK_BACK_SPACE) {
Shortcuts.setGlobalShortcut(globalShortcutType, undefined);
this.render();
return;
}
if (e.which === Keys.DOM_VK_ESCAPE) {
shortcutEditorInput.blur();
return;
}
const shortcut = Shortcuts.keyEventToShortcut(e);
const presentableShortcutText = Shortcuts.presentShortcut(shortcut.value);
shortcutEditorInput.val(presentableShortcutText);
const exists = this.systemShortcuts.includes(shortcut.text);
shortcutEditorInput.toggleClass('input--error', exists);
const isValid = shortcut.valid && !exists;
if (isValid) {
Shortcuts.setGlobalShortcut(globalShortcutType, shortcut.value);
this.render();
}
}); });
} }
}); });

View File

@ -26,16 +26,31 @@
border: 1px solid var(--muted-color); border: 1px solid var(--muted-color);
display: inline-block; display: inline-block;
border-radius: $base-border-radius; border-radius: $base-border-radius;
width: 40px; width: 8em;
text-align: center; text-align: center;
padding: $base-padding; padding: $base-padding;
margin: 0 $base-padding-h $base-padding-v $base-padding-h; margin: 0 $base-padding-h $base-padding-v $base-padding-h;
line-height: 1.5em;
min-width: unset;
box-sizing: border-box;
vertical-align: baseline;
&-large { &-large {
width: 80px; width: 12em;
} }
&:first-of-type { &:first-of-type {
margin-left: 0; margin-left: 0;
} }
&__editor {
margin-bottom: $base-padding-v;
&-input {
text-align: center;
margin: $base-padding-v 0 $medium-padding-v;
width: 15em;
&--large {
width: 30em;
}
}
}
} }
&__back-button { &__back-button {

View File

@ -9,7 +9,7 @@
<div><span class="shortcut">{{{cmd}}}B</span> {{res 'setShCopyUser'}}</div> <div><span class="shortcut">{{{cmd}}}B</span> {{res 'setShCopyUser'}}</div>
<div><span class="shortcut">{{{cmd}}}U</span> {{res 'setShCopyUrl'}}</div> <div><span class="shortcut">{{{cmd}}}U</span> {{res 'setShCopyUrl'}}</div>
{{#if autoTypeSupported}} {{#if autoTypeSupported}}
<div><span class="shortcut">{{{cmd}}}T</span> {{res 'setShAutoType'}}</div> <div><span class="shortcut">{{{cmd}}}T</span> {{res 'setShAutoType'}}</div>
{{/if}} {{/if}}
<div><span class="shortcut">&uarr;</span> {{res 'setShPrev'}}</div> <div><span class="shortcut">&uarr;</span> {{res 'setShPrev'}}</div>
<div><span class="shortcut">&darr;</span> {{res 'setShNext'}}</div> <div><span class="shortcut">&darr;</span> {{res 'setShNext'}}</div>
@ -19,10 +19,14 @@
<div><span class="shortcut">{{{cmd}}}G</span> {{res 'setShGen'}}</div> <div><span class="shortcut">{{{cmd}}}G</span> {{res 'setShGen'}}</div>
<div><span class="shortcut">{{{cmd}}},</span> {{res 'setShSet'}}</div> <div><span class="shortcut">{{{cmd}}},</span> {{res 'setShSet'}}</div>
<div><span class="shortcut">{{{cmd}}}L</span> {{res 'setShLock'}}</div> <div><span class="shortcut">{{{cmd}}}L</span> {{res 'setShLock'}}</div>
{{#if globalShortcutsSupported}} {{#if globalShortcuts}}
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}C</span> {{res 'setShCopyPassGlobal'}}</div> <div><button class="shortcut btn-silent {{#if globalIsLarge}}shortcut-large{{/if}}"
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}B</span> {{res 'setShCopyUserGlobal'}}</div> data-shortcut="copyPassword">{{{globalShortcuts.copyPassword}}}</button> {{res 'setShCopyPassGlobal'}}</div>
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}U</span> {{res 'setShCopyUrlGlobal'}}</div> <div><button class="shortcut btn-silent {{#if globalIsLarge}}shortcut-large{{/if}}"
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}T</span> {{res 'setShAutoTypeGlobal'}}</div> data-shortcut="copyUser">{{{globalShortcuts.copyUser}}}</button> {{res 'setShCopyUserGlobal'}}</div>
<div><button class="shortcut btn-silent {{#if globalIsLarge}}shortcut-large{{/if}}"
data-shortcut="copyUrl">{{{globalShortcuts.copyUrl}}}</button> {{res 'setShCopyUrlGlobal'}}</div>
<div><button class="shortcut btn-silent {{#if globalIsLarge}}shortcut-large{{/if}}"
data-shortcut="autoType">{{{globalShortcuts.autoType}}}</button> {{res 'setShAutoTypeGlobal'}}</div>
{{/if}} {{/if}}
</div> </div>

View File

@ -65,10 +65,11 @@ app.on('window-all-closed', () => {
}); });
app.on('ready', () => { app.on('ready', () => {
appReady = true; appReady = true;
const appSettings = readAppSettings() || {};
setAppOptions(); setAppOptions();
setSystemAppearance(); setSystemAppearance();
createMainWindow(); createMainWindow(appSettings);
setGlobalShortcuts(); setGlobalShortcuts(appSettings);
subscribePowerEvents(); subscribePowerEvents();
deleteOldTempFiles(); deleteOldTempFiles();
hookRequestHeaders(); hookRequestHeaders();
@ -135,6 +136,7 @@ app.getMainWindow = function() {
return mainWindow; return mainWindow;
}; };
app.emitBackboneEvent = emitBackboneEvent; app.emitBackboneEvent = emitBackboneEvent;
app.setGlobalShortcuts = setGlobalShortcuts;
function setAppOptions() { function setAppOptions() {
app.commandLine.appendSwitch('disable-background-timer-throttling'); app.commandLine.appendSwitch('disable-background-timer-throttling');
@ -156,8 +158,7 @@ function setSystemAppearance() {
} }
} }
function createMainWindow() { function createMainWindow(appSettings) {
const appSettings = readAppSettings() || {};
const isMacDarkTheme = appSettings.theme === 'macdark'; const isMacDarkTheme = appSettings.theme === 'macdark';
const windowOptions = { const windowOptions = {
show: false, show: false,
@ -396,23 +397,25 @@ function notifyOpenFile() {
} }
} }
function setGlobalShortcuts() { function setGlobalShortcuts(appSettings) {
const shortcutModifiers = process.platform === 'darwin' ? 'Ctrl+Alt+' : 'Shift+Alt+'; const defaultShortcutModifiers = process.platform === 'darwin' ? 'Ctrl+Alt+' : 'Shift+Alt+';
const shortcuts = { const defaultShortcuts = {
C: 'copy-password', CopyPassword: { shortcut: defaultShortcutModifiers + 'C', event: 'copy-password' },
B: 'copy-user', CopyUser: { shortcut: defaultShortcutModifiers + 'B', event: 'copy-user' },
U: 'copy-url', CopyUrl: { shortcut: defaultShortcutModifiers + 'U', event: 'copy-url' },
T: 'auto-type' AutoType: { shortcut: defaultShortcutModifiers + 'T', event: 'auto-type' }
}; };
Object.keys(shortcuts).forEach(key => { electron.globalShortcut.unregisterAll();
const shortcut = shortcutModifiers + key; for (const [key, shortcutDef] of Object.entries(defaultShortcuts)) {
const eventName = shortcuts[key]; const fromSettings = appSettings[`globalShortcut${key}`];
const shortcut = fromSettings || shortcutDef.shortcut;
const eventName = shortcutDef.event;
try { try {
electron.globalShortcut.register(shortcut, () => { electron.globalShortcut.register(shortcut, () => {
emitBackboneEvent(eventName); emitBackboneEvent(eventName);
}); });
} catch (e) {} } catch (e) {}
}); }
} }
function subscribePowerEvents() { function subscribePowerEvents() {

View File

@ -6,6 +6,7 @@ Release notes
`+` #1243: auto-type any field `+` #1243: auto-type any field
`+` #1255: file format version and kdf selection in settings `+` #1255: file format version and kdf selection in settings
`*` #502: increased the default value of encryption rounds `*` #502: increased the default value of encryption rounds
`+` #348: configurable system-wide shortcuts
`*` devtools are now opened with alt-cmd-I `*` devtools are now opened with alt-cmd-I
`-` fix #764: multiple attachments display `-` fix #764: multiple attachments display
`-` fix multi-line fields display in history `-` fix multi-line fields display in history