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