322f9e9e7504f33b068ea7c9c73fd546a182e8ad
[marketstoday] / src / qml / StockDetailsComponent.qml
1 /*
2 @version: 0.2
3 @author: Sudheer K. <scifi1947 at gmail.com>
4 @license: GNU General Public License
5 */
6
7 import Qt 4.7
8 import "Library" as Library
9
10 Item {
11
12     width: 800
13     height: 380
14
15     id: stockDetailsScreen
16     property int componentWidth: width
17     property int itemHeight: 50
18     property string symbol: "YHOO"
19     property string stockName: ""
20     property string lastTradedPrice: ""
21     property string lastTradedDateTime: ""
22     property string change: ""
23     property string changePercentage: ""
24     property string daysRange: ""
25     property string yearRange: ""
26     property string marketVolume: ""
27     property string prevClose: ""
28     property string marketCap: ""
29     property string baseChartURL: "http://chart.finance.yahoo.com/z?q=&l=&z=m&p=s&a=v&p=s&lang=en-US&region=US"
30     property string chartURL: ""
31     property string rssURL: ""
32
33     property int currentScreenIndex: 1
34
35     signal logRequest(string strMessage)
36     signal loadChart(string duration)
37     signal lockInLandscape()
38     signal unlockOrientation()
39
40     Rectangle {
41         anchors.fill: parent
42         color:"#343434"
43         id: detailsRect
44
45         Library.CustomGestureArea {
46             anchors.fill: parent
47             onSwipeLeft: detailsRect.switchScreen()
48             onSwipeRight: detailsRect.switchScreen()
49         }
50
51         Component.onCompleted: {                                   
52             if (symbol !== "") {
53                 loadDetails();
54                 loadNews();
55                 chartURL = baseChartURL+"&t=1d&s="+symbol;
56             }
57         }
58
59         function switchScreen(){
60             switch (currentScreenIndex){
61             case 1:
62                 stockDetailsScreen.lockInLandscape();
63                 stockDetailsLoader.sourceComponent = stockChartComponent;
64                 currentScreenIndex = currentScreenIndex + 1;
65                 break;
66             case 2:
67                 stockDetailsScreen.unlockOrientation();
68                 stockDetailsLoader.sourceComponent = stockDetailsComponent;
69                 currentScreenIndex = currentScreenIndex - 1;
70                 break;
71             default:
72                 //Do Nothing
73             }
74             logRequest("currentScreenIndex = "+currentScreenIndex);
75         }
76
77         function loadDetails(){
78             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';
79             logRequest("Loading stock details from "+queryURL);
80             var response = new XMLHttpRequest();
81             response.onreadystatechange = function() {
82                 if (response.readyState == XMLHttpRequest.DONE) {
83                     refreshDetails(response.responseXML);
84                 }
85             }
86
87             response.open("GET", queryURL);
88             response.send();
89         }
90
91         function loadNews(){
92             rssURL = "http://feeds.finance.yahoo.com/rss/2.0/headline?region=US&lang=en-US&s="+symbol;
93             logRequest("Loading news from "+rssURL);
94             var response = new XMLHttpRequest();
95             response.onreadystatechange = function() {
96                 if (response.readyState == XMLHttpRequest.DONE) {
97                     refreshNewsModel(response.responseXML);
98                 }
99             }
100
101             response.open("GET", rssURL);
102             response.send();
103         }
104
105         function refreshDetails(responseXML){
106             if (!responseXML) return;
107             var xmlDoc = responseXML.documentElement;
108             var results = xmlDoc.firstChild;
109
110             if (results) {
111                 var quoteNodes = results.childNodes;
112                 if (quoteNodes){
113                     if (quoteNodes.length === 0) {
114                         logRequest("No results for stock quote details");
115                     }
116                     else{
117                         //We are only expecting one quote node per symbol.
118                         var quoteElements = quoteNodes[0].childNodes;
119                         var j = 0;
120                         var lastTradedDate = "", lastTradedTime ="";
121                         for (j = 0; j < quoteElements.length; j++){
122
123                             if (quoteElements[j].childNodes[0]) {
124                                 switch (quoteElements[j].nodeName){
125                                     case 'Name':
126                                         stockName = quoteElements[j].childNodes[0].nodeValue;
127                                         break;
128                                     case 'LastTradePriceOnly':
129                                         lastTradedPrice = quoteElements[j].childNodes[0].nodeValue;
130                                         break;
131                                     case 'LastTradeDate':
132                                         lastTradedDate = quoteElements[j].childNodes[0].nodeValue;
133                                         break;
134                                     case 'LastTradeTime':
135                                         lastTradedTime = quoteElements[j].childNodes[0].nodeValue;
136                                         break;
137                                     case 'Change':
138                                         change = quoteElements[j].childNodes[0].nodeValue;
139                                         break;
140                                     case 'ChangeinPercent':
141                                         changePercentage = quoteElements[j].childNodes[0].nodeValue;
142                                         break;
143                                     case 'DaysRange':
144                                         daysRange = quoteElements[j].childNodes[0].nodeValue;
145                                         break;
146                                     case 'YearRange':
147                                         yearRange = quoteElements[j].childNodes[0].nodeValue;
148                                         break;
149                                     case 'Volume':
150                                         marketVolume = quoteElements[j].childNodes[0].nodeValue;
151                                         break;
152                                     case 'PreviousClose':
153                                         prevClose = quoteElements[j].childNodes[0].nodeValue;
154                                         break;
155                                     case 'MarketCapitalization':
156                                         marketCap = quoteElements[j].childNodes[0].nodeValue;
157                                         break;
158                                     default:
159                                 }
160                             }
161                         }
162                         if (lastTradedDate !== "") lastTradedDateTime = lastTradedDate + " " + lastTradedTime;
163                     }
164                 }
165             }
166         }
167
168         function refreshNewsModel(responseXML){
169             if (!(responseXML && stockNewsDataModel)) return;
170
171             var xmlDoc = responseXML.documentElement;
172             var channel = xmlDoc.firstChild;
173
174             //Not the best code I ever wrote, but got no choice
175             //Refer to Memory leak issue with XMLListModel --> http://bugreports.qt.nokia.com/browse/QTBUG-15191
176
177             if (channel) {
178                 var itemNodes = channel.childNodes;
179                 if (itemNodes){
180
181                     logRequest("Clearing News Model");
182                     stockNewsDataModel.clear();
183
184                     var i = 0;
185                     for (i = 0; i < itemNodes.length; i++) {
186
187                         if (itemNodes[i].nodeName === 'item'){
188                             var newsElements = itemNodes[i].childNodes;
189                             var j = 0;
190                             var newsTitle,newsLink
191                             for (j = 0; j < newsElements.length; j++){
192
193                                 switch (newsElements[j].nodeName){
194                                     case 'title':
195                                         newsTitle = newsElements[j].childNodes[0].nodeValue;
196                                         break;
197                                     case 'link':
198                                         newsLink = newsElements[j].childNodes[0].nodeValue;
199                                         break;
200                                     default:
201                                 }
202                             }
203                             stockNewsDataModel.append({"title":newsTitle,"link":newsLink});
204                         }
205                     }
206                 }
207             }
208         }
209
210         ListModel{
211             id: stockNewsDataModel
212         }
213
214         Component {
215             id: stockNewsDelegate
216
217             Item {
218                 id: newsWrapper; width: stockDetailsLoader.width; height: itemHeight
219                 Item {
220                     anchors.fill: parent
221                     Rectangle { color: "black"; opacity: index % 2 ? 0.2 : 0.4; height: newsWrapper.height - 2; width: newsWrapper.width; y: 1 }
222                     Text {
223                         anchors {verticalCenter: parent.verticalCenter;left: parent.left;leftMargin: 10;right: parent.right}
224                         text: title; font.pixelSize: 14
225                         font.bold: false;
226                         verticalAlignment: Text.AlignVCenter
227                         horizontalAlignment: Text.AlignLeft
228                         elide: Text.ElideRight;
229                         color: "white";
230                         style: Text.Raised;
231                         styleColor: "black"
232                     }
233                     Library.CustomGestureArea {
234                         anchors.fill: parent
235                         onDoubleClicked: {
236                             logRequest("Opening news article: "+link);
237                             Qt.openUrlExternally(link);
238                         }
239                         onSwipeLeft: detailsRect.switchScreen()
240                         onSwipeRight: detailsRect.switchScreen()
241                     }
242                 }
243             }
244         }
245
246         Component {
247             id: stockDetailsComponent
248
249             Item {
250                 anchors.fill: parent
251
252                 Text {
253                     id: stockNameLabel
254                     anchors.top: parent.top
255                     width: parent.width
256                     anchors.horizontalCenter: parent.horizontalCenter
257                     height: 30
258                     horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter
259                     font.pixelSize: 18; font.bold: true; elide: Text.ElideMiddle; color: "#B8B8B8"; style: Text.Raised; styleColor: "black"
260                     text: (stockName != "")? (stockName +" ("+symbol+")"):symbol
261                 }
262
263
264                 Rectangle {
265                     id: stockDetailsSection
266                     border.width: 1
267                     border.color: "#BFBFBF"
268                     color:"#2E2E2E"
269                     anchors {top: stockNameLabel.bottom;left: parent.left;right: parent.right}
270                     height: 125
271                     radius: 15
272
273                     Column{
274                         id: stockDetailsColumn
275                         anchors {top: parent.top; left: parent.left; leftMargin: 10}
276                         width: parent.width
277
278                         StockDetailsRow{
279                             label1: "Last Traded"
280                             value1: lastTradedPrice
281                             cell1Width: stockDetailsColumn.width/2
282
283                             label2: "Day's Range"
284                             value2: daysRange
285                             cell2Width: stockDetailsColumn.width/2
286                         }
287
288                         StockDetailsRow{
289                             label1: "Last Trade Time"
290                             value1: lastTradedDateTime
291                             cell1Width: stockDetailsColumn.width/2
292
293                             label2: "52w Range"
294                             value2: yearRange
295                             cell2Width: stockDetailsColumn.width/2
296                         }
297
298                         StockDetailsRow{
299                             label1: "Change"
300                             value1: ((change != "" && changePercentage != "")? change + " ("+changePercentage+")":"")
301                             cell1Width: stockDetailsColumn.width/2
302
303                             label2: "Volume"
304                             value2: marketVolume
305                             cell2Width: stockDetailsColumn.width/2
306                         }
307
308                         StockDetailsRow{
309                             label1: "Prev. Close"
310                             value1: prevClose
311                             cell1Width: stockDetailsColumn.width/2
312
313                             label2: "Market Cap"
314                             value2: marketCap
315                             cell2Width: stockDetailsColumn.width/2
316                         }
317                     }
318                 }
319
320                 Rectangle{
321                     border.width: 1
322                     border.color: "#BFBFBF"
323                     color:"#2E2E2E"
324                     width: parent.width
325                     anchors {top: stockDetailsSection.bottom;topMargin: 5;
326                              bottom: parent.bottom;
327                              left: parent.left;
328                              right: parent.right}
329                     ListView {
330                         flickDeceleration: 500
331                         anchors.fill: parent
332                         model: stockNewsDataModel
333                         delegate: stockNewsDelegate
334                         focus:true
335                         snapMode: ListView.SnapToItem
336                     }
337                 }
338
339             }
340         }
341
342         Loader {
343           id: stockDetailsLoader
344           anchors{top: parent.top; bottom: parent.bottom
345                   left: parent.left; leftMargin: 10
346                   right:  parent.right; rightMargin: 10}
347           sourceComponent: stockDetailsComponent
348         }
349
350
351         Component{
352             id: stockChartComponent
353
354             Item {
355                 id: chartAreaWrapper
356                 anchors.fill: parent
357
358                 Rectangle {
359                     id: chartArea
360                     border.width: 1
361                     border.color: "#BFBFBF"
362                     //color:"#2E2E2E"
363                     color:"white"
364                     anchors { top: parent.top;topMargin: 40;
365                               bottom: parent.bottom; bottomMargin: 40;
366                               left: parent.left; right: parent.right}
367                     radius: 10
368
369                     Library.Loading { anchors.centerIn: parent; visible: chartImg.status == Image.Loading}
370
371                     Image {
372                         id: chartImg
373                         anchors {left: parent.left; leftMargin: 10; verticalCenter: parent.verticalCenter}
374                         source: chartURL
375                         sourceSize.width: 512
376                         sourceSize.height: 288
377                         smooth: true
378                         fillMode: Image.PreserveAspectFit
379                         onStatusChanged: {
380                             switch(status){
381                                 case Image.Ready:
382                                     logRequest("Image is ready");
383                                     break;
384                                 case Image.Loading:
385                                     logRequest("Image is loading");
386                                     break;
387                                 case Image.Error:
388                                     logRequest("Image loading failed");
389                                     break;
390                                 default:
391                                     logRequest("No image specified");
392                                     break;
393                             }
394
395                         }
396
397                         Connections{
398                             target: stockDetailsScreen
399                             onLoadChart: {
400                                 chartURL = baseChartURL+"&t="+duration+"&s="+symbol;
401                                 logRequest(chartURL);
402                             }
403                         }
404                     }
405
406                     Column {
407                         width: 130
408                         spacing: 20
409                         anchors {top: parent.top; topMargin: 40; bottom: parent.bottom;
410                                  right: chartArea.right;rightMargin: 10}
411
412                         Row {
413                             height: 40
414                             spacing: 20
415                             anchors.horizontalCenter: parent.horizontalCenter
416                             Library.Button {
417                                 id: oneDayButton
418                                 text:  "1d"
419                                 anchors { verticalCenter: parent.verticalCenter}
420                                 width: 50; height: 32
421                                 onClicked: loadChart("1d");
422                             }
423
424                             Library.Button {
425                                 id: fiveDayButton
426                                 text:  "5d"
427                                 anchors { verticalCenter: parent.verticalCenter}
428                                 width: 50; height: 32
429                                 onClicked: loadChart("5d");
430                             }
431                         }
432
433                         Row {
434                             height: 40
435                             spacing: 20
436                             anchors.horizontalCenter: parent.horizontalCenter
437                             Library.Button {
438                                 id: threeMonthButton
439                                 text:  "3m"
440                                 anchors { verticalCenter: parent.verticalCenter}
441                                 width: 50; height: 32
442                                 onClicked: loadChart("3m");
443                             }
444
445                             Library.Button {
446                                 id: sixMonthButton
447                                 text:  "6m"
448                                 anchors { verticalCenter: parent.verticalCenter}
449                                 width: 50; height: 32
450                                 onClicked: loadChart("6m");
451                             }
452                         }
453                         Row {
454                             height: 40
455                             spacing: 20
456                             anchors.horizontalCenter: parent.horizontalCenter
457                             Library.Button {
458                                 id: oneYearButton
459                                 text:  "1y"
460                                 anchors { verticalCenter: parent.verticalCenter}
461                                 width: 50; height: 32
462                                 onClicked: loadChart("1y");
463                             }
464
465                             Library.Button {
466                                 id: twoYearButton
467                                 text:  "2y"
468                                 anchors { verticalCenter: parent.verticalCenter}
469                                 width: 50; height: 32
470                                 onClicked: loadChart("2y");
471                             }
472                         }
473                         Row {
474                             height: 40
475                             spacing: 20
476                             anchors.horizontalCenter: parent.horizontalCenter
477                             Library.Button {
478                                 id: fiveYearButton
479                                 text:  "5y"
480                                 anchors { verticalCenter: parent.verticalCenter}
481                                 width: 50; height: 32
482                                 onClicked: loadChart("5y");
483                             }
484
485                             Library.Button {
486                                 id: maxButton
487                                 text:  "max"
488                                 anchors { verticalCenter: parent.verticalCenter}
489                                 width: 50; height: 32
490                                 onClicked: loadChart("my");
491                             }
492                         }
493                     }
494                 }
495             }
496         }
497     }
498 }