From efbf4ba81d14eef493446f9e3a20df0834acafd4 Mon Sep 17 00:00:00 2001 From: Mikko Ahlroth Date: Mon, 22 Sep 2014 20:54:52 +0300 Subject: [PATCH] Moar progress, sorry for no commit message --- harbour-weechatrelay.pro | 5 +- qml/js/buffers.js | 102 +++++++++++++++++++++++++++--- qml/js/config.js | 62 ++++++++++++++++++ qml/js/connection.js | 25 ++++++-- qml/js/debug.js | 35 ++++++++--- qml/js/eventqueue.js | 2 +- qml/pages/BufferList.qml | 108 ++++++++++++++++++++++++++++++++ qml/pages/BufferView.qml | 101 +++++++++++++++++++++++++++++ qml/pages/ConnectionList.qml | 13 ++-- qml/pages/DebugView.qml | 45 +------------ qml/pages/TextListComponent.qml | 27 ++++---- 11 files changed, 434 insertions(+), 91 deletions(-) create mode 100644 qml/js/config.js create mode 100644 qml/pages/BufferList.qml create mode 100644 qml/pages/BufferView.qml diff --git a/harbour-weechatrelay.pro b/harbour-weechatrelay.pro index 7f146ce..852ca71 100644 --- a/harbour-weechatrelay.pro +++ b/harbour-weechatrelay.pro @@ -36,7 +36,10 @@ OTHER_FILES += qml/harbour-weechatrelay.qml \ qml/js/debug.js \ qml/js/buffers.js \ qml/js/eventqueue.js \ - qml/js/uniqid.js + qml/js/uniqid.js \ + qml/pages/BufferView.qml \ + qml/js/config.js \ + qml/pages/BufferList.qml HEADERS += \ src/relayconnection.h \ diff --git a/qml/js/buffers.js b/qml/js/buffers.js index b4b9e6a..5658e20 100644 --- a/qml/js/buffers.js +++ b/qml/js/buffers.js @@ -10,24 +10,75 @@ .pragma library +.import QtQuick 2.0 as QQ + .import "eventqueue.js" as EQ +.import "moment.js" as M .import "debug.js" as D -function Buffer(id) { - this.id = id; - this.window = null; +var _textListComponent = Qt.createComponent('../pages/TextListComponent.qml'); + +function Buffer(pointer, number, name, title) { + this.pointer = pointer; + this.number = number; + this.name = name; + this.title = title; + + this.textList = _textListComponent.createObject(_parentPage); + + // Add line to this buffer + this.add = function(moment, str) { + this.textList.add(moment, str); + }; } +// Dict of buffers, pointer as key, for fast referencing var _buffers = {}; +// List of all buffers for easy iteration, will be sorted ascending +// according to buffer number whenever requested +var _buffersList = []; + +// Page that will own all of our textlistcomponents +var _parentPage = null; + function init() { EQ.addHandler("__weecrapp_ready", connected); + EQ.addHandler("_buffer_line_added", lineAdded); +} + +// Set the given page to be used for the core buffer +// This is so that we can reuse the debug page as the +// WeeChat core buffer +function setDebugPage(page) { + _parentPage = page; + + var pointer = '0'; + var buffer = new Buffer(pointer, 1, 'weechat', 'Debug/core'); + _buffers[pointer] = buffer; + _buffersList.push(buffer); + D.setDebugBuffer(buffer); + page.changeBuffer(buffer); +} + +function getBuffer(i) { + if (_buffers.hasOwnProperty(i)) { + return _buffers[i]; + } + + return null; +} + +// Return bufferlist sorted by buffer number +function getBuffersByNumber() { + return _buffersList; } function connected() { - EQ.command("hdata buffer:gui_buffers(*) name,number", bufferListData); + D.d("Fetching all buffers..."); + EQ.command("hdata buffer:gui_buffers(*) full_name,short_name,number,type,title", bufferListData); EQ.command("sync"); } @@ -38,18 +89,51 @@ function disconnected() { function bufferListData(data) { - D.d("Got buffer list data:"); - data = data[0]; var buffers = data.objectSets; for (var i = 0; i < buffers.length; ++i) { - D.d(buffers[i].number + " " + buffers[i].name); + var pointer = buffers[i]['__path'][0]; + var number = buffers[i].number; + var name = buffers[i].short_name; + var title = buffers[i].title; + var buffer = null; - //EQ.command("sync " + buffers[i].name + " buffer,nicklist"); + if (buffers[i].type !== 0) { + // Currently only sync IRC buffers + continue; + } + + // If this buffer is the core, merge it to the debug buffer + if (_parentPage !== null && number === 1 && buffers[i].full_name === 'core.weechat') { + _buffers[pointer] = _buffers['0']; + buffer = _buffers['0']; + buffer.pointer = pointer; + buffer.name = name; + buffer.title = title; + buffer.number = number; + } + else { + buffer = new Buffer(pointer, number, name, title); + _buffers[pointer] = buffer; + _buffersList.push(buffer); + } } + + _sortBuffersList(); } -function bufferSyncData(data) { +// A new line was added to a buffer +function lineAdded(data) { + data = data[0].objectSets[0]; + var buffer = data.buffer; + var date = M.moment(data.date); + _buffers[buffer].add(date, data.message); +} + +function _sortBuffersList() { + _buffersList.sort(function (a, b) { + return a.number - b.number; + }); } diff --git a/qml/js/config.js b/qml/js/config.js new file mode 100644 index 0000000..a27e3f7 --- /dev/null +++ b/qml/js/config.js @@ -0,0 +1,62 @@ +.pragma library + +/* + * © Mikko Ahlroth 2014 + * WeeCRApp is open source software. For licensing information, please check + * the LICENCE file. + */ + +/* + * This file contains the configuration settings of the app (both hardcoded + * and user-settable) and the API to change them. Connection-specific settings + * are handled in AddConnection.qml. + */ + +.import "storage.js" as S + + + +/* + * Constants that cannot be changed + */ + +// Regex to match weechat version strings +var SUPPORTED_WEECHAT_VERSIONS = /^(?:0\.4)|(?:1\.0)/; + + +/* + * All settings and their defaults + */ + +var _s = { + timestamp_format: 'HH:mm:ss' +}; + +var _init = false; + +// Load all settings on first config access +if (!_init) { + _loadConfig(); +} + + +function set(key, value) { + _s[key] = value; + + var db = S.connect(); + S.storeSetting(db, key, value); +} + +function get(key) { + return _s[key]; +} + + + +function _loadConfig() { + var db = S.connect(); + + for (var key in _s) { + var val = S.readSetting(db, key, _s[key]); + } +} diff --git a/qml/js/connection.js b/qml/js/connection.js index b6f28f1..a09913b 100644 --- a/qml/js/connection.js +++ b/qml/js/connection.js @@ -5,6 +5,7 @@ .import "debug.js" as D .import "eventqueue.js" as EQ .import "buffers.js" as B +.import "config.js" as C /* * © Mikko Ahlroth 2014 @@ -25,6 +26,7 @@ var ps = null; // The PageStack // Views var debugViewPage = null; +var bufferListPage = null; var sslVerifyDialog = null; @@ -40,11 +42,20 @@ function init(connectionHandler, pageStack) { }); // Wait for init reply to let system know it's ready to send - EQ.addHandler("init", function() { - EQ.handleEvent("__weecrapp_ready"); - }); + EQ.addHandler("init", function(data) { + // Check the received version, it must match the minimum supported + var version = data[0]['value']; + if (!C.SUPPORTED_WEECHAT_VERSIONS.test(version)) { + D.d("This version of WeeChat is not supported."); + disconnect(); + } + else { + D.d("Welcome to WeeChat " + version); + EQ.handleEvent("__weecrapp_ready"); + } + }, true); - B.init(); + B.init(ps); } @@ -94,8 +105,9 @@ function onSslError(errorStrings, // Public API function connect(connObj) { - debugViewPage = ps.replace("../pages/DebugView.qml"); - D.setDebugPage(debugViewPage); + debugViewPage = ps.replace("../pages/BufferView.qml"); + bufferListPage = ps.pushAttached("../pages/BufferList.qml"); + B.setDebugPage(debugViewPage); D.d("Connecting..."); var connType = (connObj.type === "ssl") @@ -158,6 +170,7 @@ function clearConnection() { connection = null; connected = false; handler.clearData(); + ps.replace("../pages/ConnectionList.qml"); } // Send a command to the WeeChat server diff --git a/qml/js/debug.js b/qml/js/debug.js index 2af8ab0..d575afe 100644 --- a/qml/js/debug.js +++ b/qml/js/debug.js @@ -6,21 +6,23 @@ .pragma library +.import "moment.js" as M -var _page = null; +// Buffer to push messages to +var _buffer = null; // Write a line to the debug view (will go to console if debug view isn't open) function debug(str) { - if (_page != null) { - _page.display(str); + if (_buffer !== undefined && _buffer !== null) { + _buffer.add(M.moment(), str); } else { console.log(str); } } -function setDebugPage(o) { - _page = o; +function setDebugBuffer(o) { + _buffer = o; } @@ -28,19 +30,34 @@ function d(str) { debug(str); } +// Go straight to console +function c(str) { + console.log(str); +} + function e(o) { enumerate(o); } function enumerate(o) { - d("Enumerating " + o); - d("---"); + c("Enumerating object"); + c("---"); var keys = Object.keys(o); for (var i = 0; i < keys.length; ++i) { - d(keys[i] + ": " + o[keys[i]]); + var val = o[keys[i]]; + + if (typeof val === 'undefined') { + c(keys[i] + ": undefined"); + } + else if (val === null) { + c(keys[i] + ": null"); + } + else { + c(keys[i] + ": " + val); + } } - d(""); + c(""); } diff --git a/qml/js/eventqueue.js b/qml/js/eventqueue.js index 7f2ddac..b03b867 100644 --- a/qml/js/eventqueue.js +++ b/qml/js/eventqueue.js @@ -45,7 +45,7 @@ function command(command, callback) { var id = UID.get(); command = "(" + id + ") " + command; - D.d("Adding handler for " + id); + //D.d("Adding handler for " + id); addHandler(id, callback, true); } diff --git a/qml/pages/BufferList.qml b/qml/pages/BufferList.qml new file mode 100644 index 0000000..7370722 --- /dev/null +++ b/qml/pages/BufferList.qml @@ -0,0 +1,108 @@ +/* + * © Mikko Ahlroth 2014 + * WeeCRApp is open source software. For licensing information, please check + * the LICENCE file. + */ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +import "../js/buffers.js" as B + +Page { + id: bufferListPage + + onVisibleChanged: bufferList.updateList(); + + SilicaListView { + id: bufferList + model: ListModel { id: bufferListModel } + anchors.fill: parent + + function updateList() { + var buffers = B.getBuffersByNumber(); + + for (var i = 0; i < buffers.length; ++i) { + bufferListModel.append({ + number: buffers[i].number, + name: buffers[i].name + }); + } + } + + header: PageHeader { + width: bufferList.width + title: "Buffers" + } + + delegate: ListItem { + id: listItem + contentHeight: Theme.itemSizeSmall + menu: contextMenu + ListView.onRemove: animateRemoval(listItem); + + function close() { + remorseAction("Closing buffer", + function() { + // TODO Close buffer forrealz + console.log("Closed " + name + "!"); + }); + } + + Row { + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.paddingLarge + + anchors { + left: parent.left + right: parent.right + } + + Label { + text: number + color: listItem.highlighted ? Theme.secondaryHighlightColor + : Theme.secondaryColor; + + width: parent.width / 7 + horizontalAlignment: Qt.AlignRight + } + + Label { + text: name + color: listItem.highlighted ? Theme.highlightColor + : Theme.primaryColor; + } + } + + Component { + id: contextMenu + ContextMenu { + MenuItem { + text: "Close" + onClicked: close(); + } + } + } + + onClicked: { + console.log(name + " clicked!"); + } + } + + VerticalScrollDecorator { flickable: connectionList } + + + PullDownMenu { + MenuItem { + text: "???" + } + } + + PushUpMenu { + MenuItem { + text: "Go to top" + onClicked: bufferList.scrollToTop(); + } + } + } +} diff --git a/qml/pages/BufferView.qml b/qml/pages/BufferView.qml new file mode 100644 index 0000000..e70e8c6 --- /dev/null +++ b/qml/pages/BufferView.qml @@ -0,0 +1,101 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import "." + +import "../js/debug.js" as D +import "../js/connection.js" as C +import "../js/moment.js" as M +import "../js/buffers.js" as B + +Page { + id: bufferViewPage + + property bool isConnected: false; + property string pointer: ""; + property int number: 0; + property string name: ""; + + function setTitle(str) { + topic.title = str; + } + + function connected() { + isConnected = true; + } + + function disconnected() { + isConnected = false; + } + + function changeBuffer(buffer) { + var textList = buffer.textList; + + // Replace current list with new list + textListItem = textList; + textListItem.parent = mainFlickable; + // HOW DO I SHOT WEB + //mainFlickable.children.push(textListItem); + //textListItem.anchors.top = topic.bottom; + //textListItem.anchors.bottom = inpuRow.top; + textListItem.width = textListItem.parent.width; + textListItem.clip = true; + + topic.title = buffer.title; + } + + SilicaFlickable { + id: mainFlickable + anchors.fill: parent + + PageHeader { + id: topic + title: "Debug" + anchors.top: parent.top + } + + Item { + id: textListItem + } + + Row { + id: inputRow + + width: parent.width + anchors.bottom: parent.bottom + + TextField { + id: inputField + width: parent.width + anchors.bottom: parent.bottom + + placeholderText: "Type here" + + EnterKey.enabled: text.length > 0 + //EnterKey.iconSource: "image://theme/icon-m-send" + EnterKey.onClicked: { + var buffer = B.getBuffer(inputField.text); + if (buffer !== null) { + changeBuffer(buffer); + } + + inputField.text = ""; + } + } + } + + PushUpMenu { + MenuItem { + text: isConnected? "Disconnect" : "Close"; + onClicked: { + if (isConnected) { + C.disconnect(); + isConnected = false; + } + else { + C.clearConnection(); + } + } + } + } + } +} diff --git a/qml/pages/ConnectionList.qml b/qml/pages/ConnectionList.qml index 81ced0c..4cb2c3e 100644 --- a/qml/pages/ConnectionList.qml +++ b/qml/pages/ConnectionList.qml @@ -15,15 +15,16 @@ Page { onVisibleChanged: connectionList.updateList(); - PageHeader { - title: "Connections" - } - SilicaListView { id: connectionList model: ListModel { id: connectionModel } anchors.fill: parent + header: PageHeader { + width: connectionList.width + title: "Connections" + } + delegate: ListItem { id: listItem contentHeight: Theme.itemSizeLarge @@ -45,7 +46,7 @@ Page { Label { text: name color: listItem.highlighted ? Theme.highlightColor - : Theme.primaryColor + : Theme.primaryColor; anchors { left: parent.left right: parent.right @@ -56,7 +57,7 @@ Page { Label { text: host + ":" + port color: listItem.highlighted ? Theme.secondaryHighlightColor - : Theme.secondaryColor + : Theme.secondaryColor; anchors { left: parent.left right: parent.right diff --git a/qml/pages/DebugView.qml b/qml/pages/DebugView.qml index 0cb787a..4e3e4e1 100644 --- a/qml/pages/DebugView.qml +++ b/qml/pages/DebugView.qml @@ -6,49 +6,8 @@ import QtQuick 2.0 import Sailfish.Silica 1.0 -import harbour.weechatrelay.connectionhandler 1.0 import "." -import "../js/connection.js" as C - -Page { - id: debugViewPage - - property bool isConnected: false; - - function display(str) { - debugList.add(new Date(), str); - } - - function connected() { - isConnected = true; - } - - function disconnected() { - isConnected = false; - } - - TextListComponent { - id: debugList - - anchors.fill: parent - - PushUpMenu { - MenuItem { - text: isConnected? "Disconnect" : "Close"; - onClicked: { - if (isConnected) { - C.disconnect(); - isConnected = false; - } - else { - C.clearConnection(); - pageStack.replace("ConnectionList.qml"); - } - } - } - } - - VerticalScrollDecorator { flickable: debugList } - } +BufferView { + id: fakeDebugView } diff --git a/qml/pages/TextListComponent.qml b/qml/pages/TextListComponent.qml index bc98105..e7a28f3 100644 --- a/qml/pages/TextListComponent.qml +++ b/qml/pages/TextListComponent.qml @@ -8,42 +8,35 @@ SilicaListView { function add(datetime, str) { var snap = false; - if (textList.atYEnd) { + if (textList.atYBeginning) { snap = true; } - textListModel.append({ "time": datetime, "str": str }); + // The top is the latest message on the bottom + textListModel.insert(0, { "time": datetime.format('HH:mm:ss'), "str": str }); if (snap) { - textList.positionViewAtEnd(); + textList.positionViewAtBeginning(); } } - function formatTime(datetime) { + function colorTime(time) { return U.colored(Theme.secondaryHighlightColor, - padTime(datetime.getHours()) + ':' - + padTime(datetime.getMinutes()) + ':' - + padTime(datetime.getSeconds())); - } - - function padTime(timeInt) { - if (timeInt < 10) { - return '0' + timeInt; - } - - return '' + timeInt; + time); } model: ListModel { id: textListModel } + verticalLayoutDirection: ListView.BottomToTop + delegate: ListItem { width: parent.width contentHeight: messageLabel.height Label { id: messageLabel - text: formatTime(time) + " " + U.escapeStyled(str) + text: colorTime(time) + " " + U.escapeStyled(str) wrapMode: Text.WrapAtWordBoundaryOrAnywhere color: Theme.highlightColor @@ -55,4 +48,6 @@ SilicaListView { textFormat: Text.StyledText } } + + VerticalScrollDecorator { flickable: textList } }