Release 0.4-0
[marketstoday] / src / qml / StockDetailsComponent.qml
index ad1e724..ff62e44 100644 (file)
 /*
-@version: 0.1
+@version: 0.4
 @author: Sudheer K. <scifi1947 at gmail.com>
 @license: GNU General Public License
 */
 
 import Qt 4.7
+import "Library" as Library
 
 Item {
 
     width: 800
     height: 380
 
-    id: stockDetailsComponent
+    id: stockDetailsScreen
+    property int componentWidth: width
     property int itemHeight: 50
     property string symbol: "YHOO"
-    property string stockName: "Yahoo"
+    property string stockName: ""
     property string lastTradedPrice: ""
-    property string lastTradedTime: ""
+    property string lastTradedDateTime: ""
     property string change: ""
     property string changePercentage: ""
-    property string dayLow: ""
-    property string dayHigh: ""
-    property string fiftyTwoWeekLow: ""
-    property string fiftyTwoWeekHigh: ""
+    property string daysRange: ""
+    property string yearRange: ""
     property string marketVolume: ""
     property string prevClose: ""
     property string marketCap: ""
+    property string baseChartURL: "http://chart.finance.yahoo.com/z?q=&l=&z=m&p=s&a=v&p=s&lang=en-US&region=US"
     property string chartURL: ""
+    property string rssURL: ""
+    property string orientation: "Portrait"
+
+    property int currentScreenIndex: 1
 
     signal logRequest(string strMessage)
+    signal loadChart(string duration)
 
     Rectangle {
         anchors.fill: parent
         color:"#343434"
+        id: detailsRect
+
+        Library.CustomGestureArea {
+            anchors.fill: parent
+            onSwipeLeft: detailsRect.switchScreen()
+            onSwipeRight: detailsRect.switchScreen()
+        }
 
-        Component.onCompleted: {
-            loadDetails();
+        Component.onCompleted: {                                   
             if (symbol !== "") {
-                chartURL = "http://chart.finance.yahoo.com/z?t=1d&q=&l=&z=m&p=s&a=v&p=s&lang=en-US&region=US&s="+symbol;
-                stockDetailsLoader.sourceComponent = stockChartComponent;
-                console.log(stockDetailsLoader.width + " x "+ stockDetailsLoader.height);
+                loadDetails();
+                loadNews();
+                chartURL = baseChartURL+"&t=1d&s="+symbol;
+            }
+        }
+
+        function switchScreen(){
+            switch (currentScreenIndex){
+            case 1:
+                stockDetailsLoader.sourceComponent = (stockDetailsScreen.width > stockDetailsScreen.height)? stockChartComponentLandscape : stockChartComponentPortrait;
+                currentScreenIndex = currentScreenIndex + 1;
+                break;
+            case 2:
+                stockDetailsLoader.sourceComponent = stockDetailsComponent;
+                currentScreenIndex = currentScreenIndex - 1;
+                break;
+            default:
+                //Do Nothing
             }
+            logRequest("currentScreenIndex = "+currentScreenIndex);
         }
 
         function loadDetails(){
-            if (symbol === "") return;
+            var queryURL = 'http://query.yahooapis.com/v1/public/yql?q=select Symbol,Name,LastTradePriceOnly,LastTradeDate,LastTradeTime,Change,ChangeinPercent,DaysRange,YearRange,Volume,PreviousClose,MarketCapitalization from yahoo.finance.quotes where symbol in ("'+symbol+'")&env=store://datatables.org/alltableswithkeys';
+            logRequest("Loading stock details from "+queryURL);
+            var response = new XMLHttpRequest();
+            response.onreadystatechange = function() {
+                if (response.readyState == XMLHttpRequest.DONE) {
+                    refreshDetails(response.responseXML);
+                }
+            }
 
+            response.open("GET", queryURL);
+            response.send();
         }
 
-        Text {
-            id: stockNameLabel
-            anchors.top: parent.top
-            width: parent.width
-            anchors.horizontalCenter: parent.horizontalCenter
-            height: 30
-            horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter
-            font.pixelSize: 18; font.bold: true; elide: Text.ElideMiddle; color: "#B8B8B8"; style: Text.Raised; styleColor: "black"
-            text: (stockName != "")? (stockName +" ("+symbol+")"):symbol
+        function loadNews(){
+            rssURL = "http://feeds.finance.yahoo.com/rss/2.0/headline?region=US&lang=en-US&s="+symbol;
+            logRequest("Loading news from "+rssURL);
+            var response = new XMLHttpRequest();
+            response.onreadystatechange = function() {
+                if (response.readyState == XMLHttpRequest.DONE) {
+                    refreshNewsModel(response.responseXML);
+                }
+            }
+
+            response.open("GET", rssURL);
+            response.send();
         }
 
-        Rectangle {
-            id: stockDetailsSection
-            border.width: 1
-            border.color: "#BFBFBF"
-            color:"#2E2E2E"
-            anchors {top: stockNameLabel.bottom;left: parent.left;leftMargin: 40;right: parent.right;rightMargin: 40}
-            height: 125
-            radius: 15
-
-            Column{
-                id: stockDetailsColumn
-                anchors {top: parent.top; left: parent.left; leftMargin: 10}
-                width: (parent.width - 10)
-
-                StockDetailsRow{
-                    label1: "Last Traded"
-                    value1: lastTradedPrice
-                    cell1Width: stockDetailsColumn.width/2
-
-                    label2: "Day's Range"
-                    value2: ((dayHigh != "" && dayLow != "")? (dayLow + " - " + dayHigh) : "")
-                    cell2Width: stockDetailsColumn.width/2
+        function refreshDetails(responseXML){
+            if (!responseXML) return;
+            var xmlDoc = responseXML.documentElement;
+            var results = xmlDoc.firstChild;
+
+            if (results) {
+                var quoteNodes = results.childNodes;
+                if (quoteNodes){
+                    if (quoteNodes.length === 0) {
+                        logRequest("No results for stock quote details");
+                    }
+                    else{
+                        //We are only expecting one quote node per symbol.
+                        var quoteElements = quoteNodes[0].childNodes;
+                        var j = 0;
+                        var lastTradedDate = "", lastTradedTime ="";
+                        for (j = 0; j < quoteElements.length; j++){
+
+                            if (quoteElements[j].childNodes[0]) {
+                                switch (quoteElements[j].nodeName){
+                                    case 'Name':
+                                        stockName = quoteElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    case 'LastTradePriceOnly':
+                                        lastTradedPrice = quoteElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    case 'LastTradeDate':
+                                        lastTradedDate = quoteElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    case 'LastTradeTime':
+                                        lastTradedTime = quoteElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    case 'Change':
+                                        change = quoteElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    case 'ChangeinPercent':
+                                        changePercentage = quoteElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    case 'DaysRange':
+                                        daysRange = quoteElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    case 'YearRange':
+                                        yearRange = quoteElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    case 'Volume':
+                                        marketVolume = quoteElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    case 'PreviousClose':
+                                        prevClose = quoteElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    case 'MarketCapitalization':
+                                        marketCap = quoteElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    default:
+                                }
+                            }
+                        }
+                        if (lastTradedDate !== "") lastTradedDateTime = lastTradedDate + " " + lastTradedTime;
+                    }
+                }
+            }
+        }
+
+        function refreshNewsModel(responseXML){
+            if (!(responseXML && stockNewsDataModel)) return;
+
+            var xmlDoc = responseXML.documentElement;
+            var channel = xmlDoc.firstChild;
+
+            //Not the best code I ever wrote, but got no choice
+            //Refer to Memory leak issue with XMLListModel --> http://bugreports.qt.nokia.com/browse/QTBUG-15191
+
+            if (channel) {
+                var itemNodes = channel.childNodes;
+                if (itemNodes){
+
+                    logRequest("Clearing News Model");
+                    stockNewsDataModel.clear();
+
+                    var i = 0;
+                    for (i = 0; i < itemNodes.length; i++) {
+
+                        if (itemNodes[i].nodeName === 'item'){
+                            var newsElements = itemNodes[i].childNodes;
+                            var j = 0;
+                            var newsTitle,newsLink
+                            for (j = 0; j < newsElements.length; j++){
+
+                                switch (newsElements[j].nodeName){
+                                    case 'title':
+                                        newsTitle = newsElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    case 'link':
+                                        newsLink = newsElements[j].childNodes[0].nodeValue;
+                                        break;
+                                    default:
+                                }
+                            }
+                            stockNewsDataModel.append({"title":newsTitle,"link":newsLink});
+                        }
+                    }
                 }
+            }
+        }
+
+        ListModel{
+            id: stockNewsDataModel
+        }
 
-                StockDetailsRow{
-                    label1: "Last Trade Time"
-                    value1: lastTradedTime
-                    cell1Width: stockDetailsColumn.width/2
+        Component {
+            id: stockNewsDelegate
 
-                    label2: "52w Range"
-                    value2: ((fiftyTwoWeekHigh != "" && fiftyTwoWeekLow != "")? (fiftyTwoWeekLow + " - " + fiftyTwoWeekHigh) : "")
-                    cell2Width: stockDetailsColumn.width/2
+            Item {
+                id: newsWrapper; width: stockDetailsLoader.width; height: itemHeight
+                Item {
+                    anchors.fill: parent
+                    Rectangle { color: "black"; opacity: index % 2 ? 0.2 : 0.4; height: newsWrapper.height - 2; width: newsWrapper.width; y: 1 }
+                    Text {
+                        anchors {verticalCenter: parent.verticalCenter;left: parent.left;leftMargin: 10;right: parent.right}
+                        text: title; font.pixelSize: 14
+                        font.bold: false;
+                        verticalAlignment: Text.AlignVCenter
+                        horizontalAlignment: Text.AlignLeft
+                        elide: Text.ElideRight;
+                        color: "white";
+                        style: Text.Raised;
+                        styleColor: "black"
+                    }
+                    Library.CustomGestureArea {
+                        anchors.fill: parent
+                        onDoubleClicked: {
+                            logRequest("Opening news article: "+link);
+                            Qt.openUrlExternally(link);
+                        }
+                        onSwipeLeft: detailsRect.switchScreen()
+                        onSwipeRight: detailsRect.switchScreen()
+                    }
                 }
+            }
+        }
 
-                StockDetailsRow{
-                    label1: "Change"
-                    value1: ((change != "" && changePercentage != "")? change + " ("+changePercentage+")":"")
-                    cell1Width: stockDetailsColumn.width/2
+        Component {
+            id: stockDetailsComponent
 
-                    label2: "Volume"
-                    value2: marketVolume
-                    cell2Width: stockDetailsColumn.width/2
+            Item {
+                anchors.fill: parent
+
+                Text {
+                    id: stockNameLabel
+                    anchors.top: parent.top
+                    width: parent.width
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    height: 30
+                    horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter
+                    font.pixelSize: 18; font.bold: true; elide: Text.ElideMiddle; color: "#B8B8B8"; style: Text.Raised; styleColor: "black"
+                    text: (stockName != "")? (stockName +" ("+symbol+")"):symbol
                 }
 
-                StockDetailsRow{
-                    label1: "Prev. Close"
-                    value1: prevClose
-                    cell1Width: stockDetailsColumn.width/2
 
-                    label2: "Market Cap"
-                    value2: marketCap
-                    cell2Width: stockDetailsColumn.width/2
+                Rectangle {
+                    id: stockDetailsSection
+                    border.width: 1
+                    border.color: "#BFBFBF"
+                    color:"#2E2E2E"
+                    anchors {top: stockNameLabel.bottom;left: parent.left;right: parent.right}
+                    height: 125
+                    radius: 15
+
+                    Column{
+                        id: stockDetailsColumn
+                        anchors {top: parent.top; left: parent.left; leftMargin: 10}
+                        width: parent.width
+
+                        StockDetailsRow{
+                            label1: "Last Traded"
+                            value1: lastTradedPrice
+                            cell1Width: stockDetailsColumn.width/2
+
+                            label2: "Day's Range"
+                            value2: daysRange
+                            cell2Width: stockDetailsColumn.width/2
+                        }
+
+                        StockDetailsRow{
+                            label1: "Last Trade Time"
+                            value1: lastTradedDateTime
+                            cell1Width: stockDetailsColumn.width/2
+
+                            label2: "52w Range"
+                            value2: yearRange
+                            cell2Width: stockDetailsColumn.width/2
+                        }
+
+                        StockDetailsRow{
+                            label1: "Change"
+                            value1: ((change != "" && changePercentage != "")? change + " ("+changePercentage+")":"")
+                            cell1Width: stockDetailsColumn.width/2
+
+                            label2: "Volume"
+                            value2: marketVolume
+                            cell2Width: stockDetailsColumn.width/2
+                        }
+
+                        StockDetailsRow{
+                            label1: "Prev. Close"
+                            value1: prevClose
+                            cell1Width: stockDetailsColumn.width/2
+
+                            label2: "Market Cap"
+                            value2: marketCap
+                            cell2Width: stockDetailsColumn.width/2
+                        }
+                    }
+                }
+
+                Rectangle{
+                    border.width: 1
+                    border.color: "#BFBFBF"
+                    color:"#2E2E2E"
+                    width: parent.width
+                    anchors {top: stockDetailsSection.bottom;topMargin: 5;
+                             bottom: parent.bottom;
+                             left: parent.left;
+                             right: parent.right}
+                    ListView {
+                        flickDeceleration: 500
+                        anchors.fill: parent
+                        model: stockNewsDataModel
+                        delegate: stockNewsDelegate
+                        focus:true
+                        snapMode: ListView.SnapToItem
+                    }
                 }
+
             }
         }
 
         Loader {
           id: stockDetailsLoader
-          anchors {top: stockDetailsSection.bottom;bottom: parent.bottom; horizontalCenter: parent.horizontalCenter}
-          //width: parent.width
-          width: 480
+          anchors{top: parent.top; bottom: parent.bottom; bottomMargin: 20;
+                  left: parent.left; leftMargin: 10
+                  right:  parent.right; rightMargin: 10}
+          sourceComponent: stockDetailsComponent
+
+          Connections{
+              target: stockDetailsScreen
+              onOrientationChanged: {
+                  logRequest("Orientation Changed");
+                  logRequest("New orientation is "+stockDetailsScreen.orientation);
+                  logRequest("Margins for StockDetailsLoader are "+stockDetailsLoader.anchors.leftMargin+", "+stockDetailsLoader.anchors.rightMargin);
+
+                  if (currentScreenIndex === 2){
+                      if (stockDetailsScreen.orientation == "Landscape")
+                          stockDetailsLoader.sourceComponent = stockChartComponentLandscape;
+                      else
+                          stockDetailsLoader.sourceComponent = stockChartComponentPortrait;
+                  }
+              }
+          }
+        }
+
+        Rectangle{
+            id: footerText
+            width: parent.width
+            height: 20
+            z: 5
+            color: "#343434"
+            anchors.top: stockDetailsLoader.bottom
+            Text {
+                id: footerMessage
+                anchors.fill: parent
+                text: "Swipe horizontally to switch between details and charts."
+                horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignVCenter
+                width: parent.width; font.pixelSize: 12; elide: Text.ElideRight;
+                color: "#cccccc"
+                style: Text.Raised; styleColor: "black"
+            }
+
+            Timer {
+                id: footerMessageTimer
+                interval: 10000
+                repeat: false
+                onTriggered: {
+                    footerMessage.text = "";
+                }
+            }
+
+            Component.onCompleted: {
+                footerMessageTimer.start();
+            }
         }
 
         Component{
-            id: stockChartComponent
-            Rectangle {
-                id: rectangleChart
-                border.width: 1
-                border.color: "#BFBFBF"
-                color:"#2E2E2E"
-                anchors {top: parent.top;topMargin: 5;bottom: parent.bottom; left: parent.left;leftMargin: 40;right: parent.right;rightMargin: 40}
-                radius: 15
-                Image {
-                    anchors.fill: parent
-                    id: name
-                    source: chartURL
-                    fillMode: Image.PreserveAspectFit
+            id: stockChartComponentLandscape
+
+            Item {
+                id: chartAreaWrapperLand
+                anchors.fill: parent
+
+                Rectangle {
+                    id: chartAreaLand
+                    border.width: 1
+                    border.color: "#BFBFBF"
+                    //color:"#2E2E2E"
+                    color:"white"
+                    anchors { top: parent.top;topMargin: 40;
+                              bottom: parent.bottom; bottomMargin: 20;
+                              left: parent.left; right: parent.right}
+                    radius: 10
+
+                    Library.Loading { anchors.centerIn: parent; visible: chartImg.status == Image.Loading}
+
+                    Image {
+                        id: chartImg
+                        anchors {left: parent.left; leftMargin: 10; verticalCenter: parent.verticalCenter}
+                        source: chartURL
+                        sourceSize.width: 512
+                        sourceSize.height: 288
+                        smooth: true
+                        fillMode: Image.PreserveAspectFit
+                        onStatusChanged: {
+                            switch(status){
+                                case Image.Ready:
+                                    logRequest("Image is ready");
+                                    break;
+                                case Image.Loading:
+                                    logRequest("Image is loading");
+                                    break;
+                                case Image.Error:
+                                    logRequest("Image loading failed");
+                                    break;
+                                default:
+                                    logRequest("No image specified");
+                                    break;
+                            }
+
+                        }
+
+                        Connections{
+                            target: stockDetailsScreen
+                            onLoadChart: {
+                                chartURL = baseChartURL+"&t="+duration+"&s="+symbol;
+                                logRequest(chartURL);
+                            }
+                        }
+                    }
+
+                    Column {
+                        width: 130
+                        spacing: 20
+                        anchors {top: parent.top; topMargin: 40; bottom: parent.bottom;
+                                 right: chartAreaLand.right;rightMargin: 10}
+
+                        Row {
+                            height: 40
+                            spacing: 20
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            Library.Button {
+                                text:  "1d"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("1d");
+                            }
+
+                            Library.Button {
+                                text:  "5d"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("5d");
+                            }
+                        }
+
+                        Row {
+                            height: 40
+                            spacing: 20
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            Library.Button {
+                                text:  "3m"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("3m");
+                            }
+
+                            Library.Button {
+                                text:  "6m"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("6m");
+                            }
+                        }
+                        Row {
+                            height: 40
+                            spacing: 20
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            Library.Button {
+                                text:  "1y"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("1y");
+                            }
+
+                            Library.Button {
+                                text:  "2y"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("2y");
+                            }
+                        }
+                        Row {
+                            height: 40
+                            spacing: 20
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            Library.Button {
+                                text:  "5y"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("5y");
+                            }
+
+                            Library.Button {
+                                text:  "max"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("my");
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        Component{
+            id: stockChartComponentPortrait
+
+            Item {
+                id: chartAreaWrapperPort
+                anchors.fill: parent
+
+                Rectangle {
+                    id: chartAreaPort
+                    border.width: 1
+                    border.color: "#BFBFBF"
+                    //color:"#2E2E2E"
+                    color:"white"
+                    anchors { top: parent.top;topMargin: 40;
+                              bottom: parent.bottom; bottomMargin: 20;
+                              left: parent.left; right: parent.right}
+                    radius: 10
+
+                    Library.Loading { anchors.centerIn: parent; visible: chartImgPort.status == Image.Loading}
+
+                    Image {
+                        id: chartImgPort
+                        //anchors {left: parent.left; leftMargin: 10; horizontalCenter: parent.horizontalCenter}
+                        anchors {horizontalCenter: parent.horizontalCenter; top: parent.top; topMargin: 40}
+                        source: chartURL
+                        sourceSize.width: 512
+                        sourceSize.height: 288
+                        smooth: true
+                        fillMode: Image.PreserveAspectFit
+                        width: parent.width - 20
+                        onStatusChanged: {
+                            switch(status){
+                                case Image.Ready:
+                                    logRequest("Image is ready");
+                                    break;
+                                case Image.Loading:
+                                    logRequest("Image is loading");
+                                    break;
+                                case Image.Error:
+                                    logRequest("Image loading failed");
+                                    break;
+                                default:
+                                    logRequest("No image specified");
+                                    break;
+                            }
+
+                        }
+
+                        Connections{
+                            target: stockDetailsScreen
+                            onLoadChart: {
+                                chartURL = baseChartURL+"&t="+duration+"&s="+symbol;
+                                logRequest(chartURL);
+                            }
+                        }
+                    }
+
+                    Column {
+                        width: 280
+                        spacing: 20
+                        anchors {verticalCenter: parent.verticalCenter;verticalCenterOffset: 80; horizontalCenter: parent.horizontalCenter}
+
+                        Row {
+                            height: 40
+                            spacing: 20
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            Library.Button {
+                                text:  "1d"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("1d");
+                            }
+
+                            Library.Button {
+                                text:  "5d"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("5d");
+                            }
+
+                            Library.Button {
+                                text:  "3m"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("3m");
+                            }
+
+                            Library.Button {
+                                text:  "6m"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("6m");
+                            }
+                        }
+
+                        Row {
+                            height: 40
+                            spacing: 20
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            Library.Button {
+                                text:  "1y"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("1y");
+                            }
+
+                            Library.Button {
+                                text:  "2y"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("2y");
+                            }
+
+                            Library.Button {
+                                text:  "5y"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("5y");
+                            }
+
+                            Library.Button {
+                                text:  "max"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("my");
+                            }
+                        }
+                    }
                 }
             }
         }
+
     }
 }