Release 0.3-1 (Including all changes)
[marketstoday] / src / qml / StockDetailsComponent.qml
index ad1e724..ef08dcf 100644 (file)
 /*
-@version: 0.1
+@version: 0.2
 @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 - 120
     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 int currentScreenIndex: 1
 
     signal logRequest(string strMessage)
+    signal loadChart(string duration)
 
     Rectangle {
         anchors.fill: parent
         color:"#343434"
 
-        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 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++) {
 
-                StockDetailsRow{
-                    label1: "Last Trade Time"
-                    value1: lastTradedTime
-                    cell1Width: stockDetailsColumn.width/2
+                        if (itemNodes[i].nodeName === 'item'){
+                            var newsElements = itemNodes[i].childNodes;
+                            var j = 0;
+                            var newsTitle,newsLink
+                            for (j = 0; j < newsElements.length; j++){
 
-                    label2: "52w Range"
-                    value2: ((fiftyTwoWeekHigh != "" && fiftyTwoWeekLow != "")? (fiftyTwoWeekLow + " - " + fiftyTwoWeekHigh) : "")
-                    cell2Width: stockDetailsColumn.width/2
+                                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: "Change"
-                    value1: ((change != "" && changePercentage != "")? change + " ("+changePercentage+")":"")
-                    cell1Width: stockDetailsColumn.width/2
+        Component {
+            id: stockNewsDelegate
 
-                    label2: "Volume"
-                    value2: marketVolume
-                    cell2Width: stockDetailsColumn.width/2
+            Item {
+                id: newsWrapper; width: componentWidth; 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"
+                    }
+                    MouseArea{
+                        anchors.fill: parent                        
+                        onClicked: {
+                            logRequest("Opening news article: "+link);
+                            Qt.openUrlExternally(link);
+                        }
+                    }
                 }
+            }
+        }
+
+        Component {
+            id: stockDetailsComponent
 
-                StockDetailsRow{
-                    label1: "Prev. Close"
-                    value1: prevClose
-                    cell1Width: stockDetailsColumn.width/2
+            Item {
+                anchors.fill: parent
 
-                    label2: "Market Cap"
-                    value2: marketCap
-                    cell2Width: stockDetailsColumn.width/2
+                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
+                }
+
+
+                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 - 10)
+
+                        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;bottomMargin: 20;
+                             left: parent.left;
+                             right: parent.right}
+                    ListView {
+                        flickDeceleration: 500
+                        anchors.fill: parent
+                        model: stockNewsDataModel
+                        delegate: stockNewsDelegate
+                        focus:true
+                        snapMode: ListView.SnapToItem
+                    }
+                }
+
+            }
+        }
+
+        Rectangle{
+            id: arrowLeftArea
+            color: "#343434"
+            width:40; height: 40
+            visible: (currentScreenIndex > 1)
+            anchors {verticalCenter: parent.verticalCenter; left: parent.left; leftMargin: 10}
+            Image {
+                width: 32
+                height: 32
+                smooth: true
+                source: "Library/images/arrow_left.png"
+            }
+            MouseArea{
+                anchors.fill: parent
+                onClicked: {
+                    switch (currentScreenIndex){
+                    case 1:
+                        //Do Nothing
+                        break;
+                    case 2:
+                        stockDetailsLoader.sourceComponent = stockDetailsComponent;
+                        currentScreenIndex = currentScreenIndex - 1;
+                        break;
+                    default:
+                        //Do Nothing
+                    }
+                    logRequest("currentScreenIndex = "+currentScreenIndex);
                 }
             }
         }
 
         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
+                  left: parent.left; leftMargin: 60
+                  right:  parent.right; rightMargin: 60}
+          sourceComponent: stockDetailsComponent
+        }
+
+        Rectangle{
+            id: arrowRightArea
+            color: "#343434"
+            width:40; height: 40
+            visible: (currentScreenIndex < 2)
+            anchors {verticalCenter: parent.verticalCenter; right: parent.right; rightMargin: 10}
+            Image {
+                width: 32
+                height: 32
+                smooth: true
+                source: "Library/images/arrow_right.png"
+            }
+            MouseArea{
+                anchors.fill: parent
+                onClicked: {
+                    switch (currentScreenIndex){
+                    case 1:
+                        stockDetailsLoader.sourceComponent = stockChartComponent;
+                        currentScreenIndex = currentScreenIndex + 1;
+                        break;
+                    case 2:
+                        //Do Nothing
+                        break;
+                    default:
+                        //Do Nothing
+                    }
+                    logRequest("currentScreenIndex = "+currentScreenIndex);
+                }
+            }
         }
 
         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
+
+            Item {
+                id: chartAreaWrapper
+                anchors.fill: parent
+
+                Rectangle {
+                    id: chartArea
+                    border.width: 1
+                    border.color: "#BFBFBF"
+                    //color:"#2E2E2E"
+                    color:"white"
+                    anchors { top: parent.top;topMargin: 40;
+                              bottom: parent.bottom; bottomMargin: 40;
+                              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: chartArea.right;rightMargin: 10}
+
+                        Row {
+                            height: 40
+                            spacing: 20
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            Library.Button {
+                                id: oneDayButton
+                                text:  "1d"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("1d");
+                            }
+
+                            Library.Button {
+                                id: fiveDayButton
+                                text:  "5d"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("5d");
+                            }
+                        }
+
+                        Row {
+                            height: 40
+                            spacing: 20
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            Library.Button {
+                                id: threeMonthButton
+                                text:  "3m"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("3m");
+                            }
+
+                            Library.Button {
+                                id: sixMonthButton
+                                text:  "6m"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("6m");
+                            }
+                        }
+                        Row {
+                            height: 40
+                            spacing: 20
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            Library.Button {
+                                id: oneYearButton
+                                text:  "1y"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("1y");
+                            }
+
+                            Library.Button {
+                                id: twoYearButton
+                                text:  "2y"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("2y");
+                            }
+                        }
+                        Row {
+                            height: 40
+                            spacing: 20
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            Library.Button {
+                                id: fiveYearButton
+                                text:  "5y"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("5y");
+                            }
+
+                            Library.Button {
+                                id: maxButton
+                                text:  "max"
+                                anchors { verticalCenter: parent.verticalCenter}
+                                width: 50; height: 32
+                                onClicked: loadChart("my");
+                            }
+                        }
+                    }
                 }
             }
         }