From 7eb45f7fcb9c51be6e6759c4ecba2f3d8ad75038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20H=C3=BCbschle-Schneider?= Date: Sat, 13 Sep 2014 19:59:32 +0100 Subject: [PATCH] Implement buffer history for alt+a and alt+<, alt+>, and alt+/ --- js/glowingbear.js | 22 ------- js/inputbar.js | 30 ++++++--- js/models.js | 154 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 161 insertions(+), 45 deletions(-) diff --git a/js/glowingbear.js b/js/glowingbear.js index 1be7dee..56cb8c4 100644 --- a/js/glowingbear.js +++ b/js/glowingbear.js @@ -609,28 +609,6 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', return true; }; -//XXX not sure whether this belongs here - $rootScope.switchToActivityBuffer = function() { - // Find next buffer with activity and switch to it - var sortedBuffers = _.sortBy($scope.getBuffers(), 'number'); - var i, buffer; - // Try to find buffer with notification - for (i in sortedBuffers) { - buffer = sortedBuffers[i]; - if (buffer.notification > 0) { - $scope.setActiveBuffer(buffer.id); - return; // return instead of break so that the second for loop isn't executed - } - } - // No notifications, find first buffer with unread lines instead - for (i in sortedBuffers) { - buffer = sortedBuffers[i]; - if (buffer.unread > 0) { - $scope.setActiveBuffer(buffer.id); - return; - } - } - }; // Helper function since the keypress handler is in a different scope $rootScope.toggleNicklist = function() { settings.nonicklist = !settings.nonicklist; diff --git a/js/inputbar.js b/js/inputbar.js index ab4394a..47e39f0 100644 --- a/js/inputbar.js +++ b/js/inputbar.js @@ -155,6 +155,7 @@ weechat.directive('inputBar', function() { // Support different browser quirks var code = $event.keyCode ? $event.keyCode : $event.charCode; + var keydown = $event.type === "keydown"; // reset quick keys display $rootScope.showQuickKeys = false; @@ -214,7 +215,7 @@ weechat.directive('inputBar', function() { // Alt+A -> switch to buffer with activity if ($event.altKey && (code === 97 || code === 65)) { $event.preventDefault(); - $rootScope.switchToActivityBuffer(); + models.switchToActivityBuffer(); return true; } @@ -226,14 +227,23 @@ weechat.directive('inputBar', function() { return true; } - // Alt+< -> switch to previous buffer + // Alt+< -> go back in history if ($event.altKey && (code === 60 || code === 226)) { - var previousBuffer = models.getPreviousBuffer(); - if (previousBuffer) { - models.setActiveBuffer(previousBuffer.id); - $event.preventDefault(); - return true; - } + $event.preventDefault(); + return models.switchToPrevNextBuffer(false); + } + + // Alt+> -> go forward in history + // TODO alternative codes for cross-browser stuff + if ($event.altKey && code === 62) { + $event.preventDefault(); + return models.switchToPrevNextBuffer(true); + } + + // Alt+/ -> switch to previous buffer + if ($event.altKey && code === 47) { + $event.preventDefault(); + return models.switchToLastBuffer(); } // Double-tap Escape -> disconnect @@ -272,7 +282,7 @@ weechat.directive('inputBar', function() { var caretPos; // Arrow up -> go up in history - if ($event.type === "keydown" && code === 38 && document.activeElement === inputNode) { + if (keydown && code === 38 && document.activeElement === inputNode) { caretPos = inputNode.selectionStart; if ($scope.command.slice(0, caretPos).indexOf("\n") !== -1) { return false; @@ -289,7 +299,7 @@ weechat.directive('inputBar', function() { } // Arrow down -> go down in history - if ($event.type === "keydown" && code === 40 && document.activeElement === inputNode) { + if (keydown && code === 40 && document.activeElement === inputNode) { caretPos = inputNode.selectionStart; if ($scope.command.slice(caretPos).indexOf("\n") !== -1) { return false; diff --git a/js/models.js b/js/models.js index 6ee7295..7719278 100644 --- a/js/models.js +++ b/js/models.js @@ -417,9 +417,88 @@ models.service('models', ['$rootScope', '$filter', function($rootScope, $filter) }; }; + /* + * Buffer switch history + */ + this.History = function() { + // The buffer history. Pointers are converted to ints to save space. + // This means that we can definitely save the entire history and not + // consume absurd amounts of memory, unlike UTF-16 strings of len ≥ 7 + var history = []; + var position = -1; + var firstSmartJump = -1; + var lastActionOffset = +1; // last is initially forward + + var _return = function(bufferId) { + return bufferId.toString(16); + }; + + var getFirstSmartJump = function() { + return _return(history[firstSmartJump]); + }; + + var resetSmartJump = function() { + firstSmartJump = history.length - 1; + }; + + var smartJump = function() { + firstSmartJump--; + }; + + var getPrevNextBuffer = function(next) { + if (next && position < history.length - 1) { + return _return(history[position + 1]); + } else if (!next && position > 0) { + return _return(history[position - 1]); + } + }; + + var getLastDisplayedBuffer = function() { + var newPosition = position - lastActionOffset; + if (newPosition >= 0 && newPosition < history.length) { + return _return(newPosition); + } + }; + + var switchToLastBuffer = function() { + lastActionOffset = -lastActionOffset; + position += lastActionOffset; + }; + + var moveInHistory = function(forward) { + lastActionOffset = forward ? +1 : -1; + position += lastActionOffset; + }; + + var recordBufferSwitch = function(buffer) { + // save buffer pointer as int + history.push(parseInt("0x" + buffer.id)); + firstSmartJump++; + position++; + lastActionOffset = +1; + }; + + var get = function() { + return {position: position, firstSmartJump: firstSmartJump, history: history}; + }; + + return { + getFirstSmartJump: getFirstSmartJump, + resetSmartJump: resetSmartJump, + smartJump: smartJump, + getPrevNextBuffer: getPrevNextBuffer, + moveInHistory: moveInHistory, + getLastDisplayedBuffer: getLastDisplayedBuffer, + switchToLastBuffer: switchToLastBuffer, + recordBufferSwitch: recordBufferSwitch, + get: get + }; + }; + var activeBuffer = null; var previousBuffer = null; + var history = this.History(); this.model = { 'buffers': {} }; @@ -442,15 +521,6 @@ models.service('models', ['$rootScope', '$filter', function($rootScope, $filter) return activeBuffer; }; - /* - * Returns the previous current active buffer - * - * @return previous buffer object - */ - this.getPreviousBuffer = function() { - return previousBuffer; - }; - /* * Sets the buffer specifiee by bufferId as active. * Deactivates the previous current buffer. @@ -458,8 +528,8 @@ models.service('models', ['$rootScope', '$filter', function($rootScope, $filter) * @param bufferId id of the new active buffer * @return true on success, false if buffer was not found */ - this.setActiveBuffer = function(bufferId, key) { - if (key === undefined) { + this.setActiveBuffer = function(bufferId, key, isMoveInHistory) { + if (!key) { key = 'id'; } @@ -467,8 +537,7 @@ models.service('models', ['$rootScope', '$filter', function($rootScope, $filter) if (key === 'id') { activeBuffer = this.model.buffers[bufferId]; - } - else { + } else { activeBuffer = _.find(this.model.buffers, function(buffer) { if (buffer[key] === bufferId) { return buffer; @@ -495,11 +564,70 @@ models.service('models', ['$rootScope', '$filter', function($rootScope, $filter) activeBuffer.unread = 0; activeBuffer.notification = 0; + if (!isMoveInHistory) { + history.recordBufferSwitch(activeBuffer); + } + + console.log('buffer switched to', activeBuffer.fullName, 'history:', history.get()); + $rootScope.$emit('activeBufferChanged', unreadSum); $rootScope.$emit('notificationChanged'); return true; }; + this.switchToActivityBuffer = function() { + var that = this; + var jumpTo = function(bufferId) { + that.setActiveBuffer(bufferId); + history.smartJump(); + }; + + // Find next buffer with activity and switch to it + var sortedBuffers = _.sortBy(this.getBuffers(), 'number'); + var i, buffer; + // Try to find buffer with notification + for (i in sortedBuffers) { + buffer = sortedBuffers[i]; + if (buffer.notification > 0) { + jumpTo(buffer.id); + return; // return instead of break so that the second for loop isn't executed + } + } + // No notifications, find first buffer with unread lines instead + for (i in sortedBuffers) { + buffer = sortedBuffers[i]; + if (buffer.unread > 0) { + jumpTo(buffer.id); + return; + } + } + + // Jump back to first buffer in the series + this.setActiveBuffer(history.getFirstSmartJump()); + history.resetSmartJump(); + }; + + this.switchToPrevNextBuffer = function(forward) { + var newBufferId = history.getPrevNextBuffer(forward); + console.log('switching to buffer:', newBufferId); + if (newBufferId) { + this.setActiveBuffer(newBufferId, null, true); + history.moveInHistory(forward); + return true; + } + return false; + }; + + this.switchToLastBuffer = function() { + var lastBufferId = history.getLastDisplayedBuffer(); + if (lastBufferId) { + this.setActiveBuffer(lastBufferId, null, true); + history.switchToLastBuffer(); + return true; + } + return false; + }; + /* * Returns the buffer list */