From 674097f6729b0f866c8095ede3632ae500666d15 Mon Sep 17 00:00:00 2001 From: Vincent Wong Date: Sun, 20 Mar 2016 14:54:29 -0700 Subject: [PATCH 1/3] Add new custom command settings in General These custom commands are profile-specific --- app/qml/ApplicationSettings.qml | 28 ++++++++++++++++++---------- app/qml/SettingsGeneralTab.qml | 28 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/app/qml/ApplicationSettings.qml b/app/qml/ApplicationSettings.qml index ec77000..077c0a4 100644 --- a/app/qml/ApplicationSettings.qml +++ b/app/qml/ApplicationSettings.qml @@ -51,6 +51,9 @@ QtObject{ // PROFILE SETTINGS /////////////////////////////////////////////////////// + property bool useCustomCommand: false + property string customCommand: "" + property string _backgroundColor: "#000000" property string _fontColor: "#ff8100" property string saturatedColor: Utils.mix(Utils.strToColor("#FFFFFF"), Utils.strToColor(_fontColor), saturationColor * 0.5) @@ -240,7 +243,9 @@ QtObject{ ambientLight: ambientLight, windowOpacity: windowOpacity, fontName: fontNames[rasterization], - fontWidth: fontWidth + fontWidth: fontWidth, + useCustomCommand: useCustomCommand, + customCommand: customCommand } return settings; } @@ -328,6 +333,9 @@ QtObject{ fontNames[rasterization] = settings.fontName !== undefined ? settings.fontName : fontNames[rasterization]; fontWidth = settings.fontWidth !== undefined ? settings.fontWidth : fontWidth; + useCustomCommand = settings.useCustomCommand !== undefined ? settings.useCustomCommand : useCustomCommand + customCommand = settings.customCommand !== undefined ? settings.customCommand : customCommand + handleFontChanged(); } @@ -377,47 +385,47 @@ QtObject{ property ListModel profilesList: ListModel{ ListElement{ text: "Default Amber" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.65,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#ff8100","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.16,"jitter":0.18,"burnIn":0.4,"staticNoise":0.1,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.65,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#ff8100","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.16,"jitter":0.18,"burnIn":0.4,"staticNoise":0.1,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Default Green" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#0ccc68","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.16,"jitter":0.18,"burnIn":0.45,"staticNoise":0.1,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#0ccc68","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.16,"jitter":0.18,"burnIn":0.45,"staticNoise":0.1,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Default Scanlines" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"COMMODORE_PET","fontColor":"#00ff5b","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.14,"jitter":0.11,"burnIn":0.4,"staticNoise":0.05,"rasterization":1,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"COMMODORE_PET","fontColor":"#00ff5b","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.14,"jitter":0.11,"burnIn":0.4,"staticNoise":0.05,"rasterization":1,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Default Pixelated" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0,"brightness":0.5,"flickering":0.2,"contrast":0.85,"fontName":"COMMODORE_PET","fontColor":"#ffffff","frameName":"ROUGH_BLACK_FRAME","glowingLine":0.2,"horizontalSync":0.2,"jitter":0,"burnIn":0.45,"staticNoise":0.19,"rasterization":2,"screenCurvature":0.05,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0,"brightness":0.5,"flickering":0.2,"contrast":0.85,"fontName":"COMMODORE_PET","fontColor":"#ffffff","frameName":"ROUGH_BLACK_FRAME","glowingLine":0.2,"horizontalSync":0.2,"jitter":0,"burnIn":0.45,"staticNoise":0.19,"rasterization":2,"screenCurvature":0.05,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Apple ][" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.5,"brightness":0.5,"flickering":0.2,"contrast":0.85,"fontName":"APPLE_II","fontColor":"#2fff91","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.22,"horizontalSync":0.16,"jitter":0.1,"burnIn":0.65,"staticNoise":0.08,"rasterization":1,"screenCurvature":0.18,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.5,"brightness":0.5,"flickering":0.2,"contrast":0.85,"fontName":"APPLE_II","fontColor":"#2fff91","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.22,"horizontalSync":0.16,"jitter":0.1,"burnIn":0.65,"staticNoise":0.08,"rasterization":1,"screenCurvature":0.18,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Vintage" - obj_string: '{"ambientLight":0.5,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.9,"contrast":0.80,"fontName":"COMMODORE_PET","fontColor":"#00ff3e","frameName":"ROUGH_BLACK_FRAME","glowingLine":0.3,"horizontalSync":0.42,"jitter":0.4,"burnIn":0.75,"staticNoise":0.2,"rasterization":1,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.5,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.9,"contrast":0.80,"fontName":"COMMODORE_PET","fontColor":"#00ff3e","frameName":"ROUGH_BLACK_FRAME","glowingLine":0.3,"horizontalSync":0.42,"jitter":0.4,"burnIn":0.75,"staticNoise":0.2,"rasterization":1,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "IBM Dos" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.07,"contrast":0.85,"fontName":"IBM_DOS","fontColor":"#ffffff","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.13,"horizontalSync":0,"jitter":0.16,"burnIn":0.3,"staticNoise":0.03,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":1,"saturationColor":0,"rbgShift":0.35,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.07,"contrast":0.85,"fontName":"IBM_DOS","fontColor":"#ffffff","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.13,"horizontalSync":0,"jitter":0.16,"burnIn":0.3,"staticNoise":0.03,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":1,"saturationColor":0,"rbgShift":0.35,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "IBM 3278" - obj_string: '{"ambientLight":0.1,"backgroundColor":"#000000","bloom":0.15,"brightness":0.5,"flickering":0,"contrast":0.85,"fontName":"IBM_3278","fontColor":"#0ccc68","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0,"horizontalSync":0,"jitter":0,"burnIn":0.6,"staticNoise":0,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.1,"backgroundColor":"#000000","bloom":0.15,"brightness":0.5,"flickering":0,"contrast":0.85,"fontName":"IBM_3278","fontColor":"#0ccc68","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0,"horizontalSync":0,"jitter":0,"burnIn":0.6,"staticNoise":0,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Transparent Green" - obj_string: '{"ambientLight":0.2,"backgroundColor":"#000000","bloom":0.45,"brightness":0.5,"flickering":0.20,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#0ccc68","frameName":"NO_FRAME","glowingLine":0.16,"horizontalSync":0.1,"jitter":0.20,"burnIn":0.25,"staticNoise":0.20,"rasterization":0,"screenCurvature":0.05,"windowOpacity":0.60,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.2,"backgroundColor":"#000000","bloom":0.45,"brightness":0.5,"flickering":0.20,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#0ccc68","frameName":"NO_FRAME","glowingLine":0.16,"horizontalSync":0.1,"jitter":0.20,"burnIn":0.25,"staticNoise":0.20,"rasterization":0,"screenCurvature":0.05,"windowOpacity":0.60,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } } diff --git a/app/qml/SettingsGeneralTab.qml b/app/qml/SettingsGeneralTab.qml index 18f72a6..82d386d 100644 --- a/app/qml/SettingsGeneralTab.qml +++ b/app/qml/SettingsGeneralTab.qml @@ -159,6 +159,34 @@ Tab{ } } } + + GroupBox{ + anchors {left: parent.left; right: parent.right} + title: qsTr("Command") + ColumnLayout { + anchors.fill: parent + CheckBox{ + id: useCustomCommand + text: qsTr("Use custom command instead of shell at startup") + checked: appSettings.useCustomCommand + onCheckedChanged: appSettings.useCustomCommand = checked + } + // workaround for QTBUG-31627 for pre 5.3.0 + Binding{ + target: useCustomCommand + property: "checked" + value: appSettings.useCustomCommand + } + TextField{ + id: customCommand + text: appSettings.customCommand + enabled: useCustomCommand.checked + onEditingFinished: appSettings.customCommand = text + anchors {left: parent.left; right: parent.right} + } + } + } + // DIALOGS //////////////////////////////////////////////////////////////// InsertNameDialog{ id: insertname From cc57fbdcd5aee0ecc2f37c6c69ca43f8300e43ee Mon Sep 17 00:00:00 2001 From: Vincent Wong Date: Mon, 21 Mar 2016 12:55:09 -0700 Subject: [PATCH 2/3] Profile-bound custom commands now execute Rather than starting ksession right away, PreprocessedTerminal now waits for ApplicationSettings to finish loading custom command settings from storage. If a custom command is specified, PreprocessedTerminal will tokenize it and pass it onto ksession as a shell program similar to the -e option. If both a -e command and a custom command is specified, the -e version overrides the custom command. --- app/qml/ApplicationSettings.qml | 4 ++ app/qml/PreprocessedTerminal.qml | 12 +++++- app/qml/utils.js | 71 ++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/app/qml/ApplicationSettings.qml b/app/qml/ApplicationSettings.qml index 077c0a4..2c645f6 100644 --- a/app/qml/ApplicationSettings.qml +++ b/app/qml/ApplicationSettings.qml @@ -97,6 +97,8 @@ QtObject{ signal terminalFontChanged(string fontSource, int pixelSize, int lineSpacing, real screenScaling, real fontWidth) + signal initializedSettings() + property Loader fontManager: Loader{ states: [ State { when: rasterization == no_rasterization @@ -463,6 +465,8 @@ QtObject{ fullscreen = true; showMenubar = false; } + + initializedSettings(); } Component.onDestruction: { storeSettings(); diff --git a/app/qml/PreprocessedTerminal.qml b/app/qml/PreprocessedTerminal.qml index f1ecfdb..76803f4 100644 --- a/app/qml/PreprocessedTerminal.qml +++ b/app/qml/PreprocessedTerminal.qml @@ -129,13 +129,17 @@ Item{ kterminal.lineSpacing = lineSpacing; } - Component.onCompleted: { - appSettings.terminalFontChanged.connect(handleFontChange); + function startSession() { + appSettings.initializedSettings.disconnect(startSession); // Retrieve the variable set in main.cpp if arguments are passed. if (defaultCmd) { ksession.setShellProgram(defaultCmd); ksession.setArgs(defaultCmdArgs); + } else if (appSettings.useCustomCommand) { + var args = Utils.tokenizeCommandLine(appSettings.customCommand); + ksession.setShellProgram(args[0]); + ksession.setArgs(args.slice(1)); } else if (!defaultCmd && Qt.platform.os === "osx") { // OSX Requires the following default parameters for auto login. ksession.setArgs(["-i", "-l"]); @@ -147,6 +151,10 @@ Item{ ksession.startShellProgram(); forceActiveFocus(); } + Component.onCompleted: { + appSettings.terminalFontChanged.connect(handleFontChange); + appSettings.initializedSettings.connect(startSession); + } } Component { id: linuxContextMenu diff --git a/app/qml/utils.js b/app/qml/utils.js index ba8ae04..a8e1af0 100644 --- a/app/qml/utils.js +++ b/app/qml/utils.js @@ -21,3 +21,74 @@ function strToColor(s){ var b = parseInt(s.substring(5,7), 16) / 256; return Qt.rgba(r, g, b, 1.0); } + +/* Tokenizes a command into program and arguments, taking into account quoted + * strings and backslashes. + * Based on GLib's tokenizer, used by Gnome Terminal + */ +function tokenizeCommandLine(s){ + var args = []; + var currentToken = ""; + var quoteChar = ""; + var escaped = false; + var nextToken = function() { + args.push(currentToken); + currentToken = ""; + } + var appendToCurrentToken = function(c) { + currentToken += c; + } + + for (var i = 0; i < s.length; i++) { + + // char followed by backslash, append literally + if (escaped) { + escaped = false; + appendToCurrentToken(s[i]); + + // char inside quotes, either close or append + } else if (quoteChar) { + escaped = s[i] === '\\'; + if (quoteChar === s[i]) { + quoteChar = ""; + nextToken(); + } else if (!escaped) { + appendToCurrentToken(s[i]); + } + + // regular char + } else { + escaped = s[i] === '\\'; + switch (s[i]) { + case '\\': + // begin escape + break; + case '\n': + // newlines always delimits + nextToken(); + break; + case ' ': + case '\t': + // delimit on new whitespace + if (currentToken) { + nextToken(); + } + break; + case '\'': + case '"': + // begin quoted section + quoteChar = s[i]; + break; + default: + appendToCurrentToken(s[i]); + } + } + } + + // ignore last token if broken quotes/backslash + if (currentToken && !escaped && !quoteChar) { + nextToken(); + } + + return args; +} From e94801ee024c0ac127aaa44a604fd86d52cf85f1 Mon Sep 17 00:00:00 2001 From: Vincent Wong Date: Thu, 7 Apr 2016 18:15:37 -0700 Subject: [PATCH 3/3] Fix issue with custom command not saving on exit If the user directly closes the settings window without triggering the custom command Textfield's onEditingFinished (via unfocus or pressing enter), the custom command may not save. This change adds a listener to the Textfield so that if the settings window closes, the change will always be recorded. --- app/qml/SettingsGeneralTab.qml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/qml/SettingsGeneralTab.qml b/app/qml/SettingsGeneralTab.qml index 82d386d..5c10dd9 100644 --- a/app/qml/SettingsGeneralTab.qml +++ b/app/qml/SettingsGeneralTab.qml @@ -171,7 +171,7 @@ Tab{ checked: appSettings.useCustomCommand onCheckedChanged: appSettings.useCustomCommand = checked } - // workaround for QTBUG-31627 for pre 5.3.0 + // Workaround for QTBUG-31627 for pre 5.3.0 Binding{ target: useCustomCommand property: "checked" @@ -179,10 +179,16 @@ Tab{ } TextField{ id: customCommand + anchors {left: parent.left; right: parent.right} text: appSettings.customCommand enabled: useCustomCommand.checked onEditingFinished: appSettings.customCommand = text - anchors {left: parent.left; right: parent.right} + + // Save text even if user forgets to press enter or unfocus + function saveSetting() { + appSettings.customCommand = text; + } + Component.onCompleted: settings_window.closing.connect(saveSetting) } } }