ff62e440d7741c0500f0cd80c4d82655f81e4b23
[marketstoday] / src / qml / StockDetailsComponent.qml
1 /*
2 @version: 0.4
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     property string orientation: "Portrait"
33
34     property int currentScreenIndex: 1
35
36     signal logRequest(string strMessage)
37     signal loadChart(string duration)
38
39     Rectangle {
40         anchors.fill: parent
41         color:"#343434"
42         id: detailsRect
43
44         Library.CustomGestureArea {
45             anchors.fill: parent
46             onSwipeLeft: detailsRect.switchScreen()
47             onSwipeRight: detailsRect.switchScreen()
48         }
49
50         Component.onCompleted: {                                   
51             if (symbol !== "") {
52                 loadDetails();
53                 loadNews();
54                 chartURL = baseChartURL+"&t=1d&s="+symbol;
55             }
56         }
57
58         function switchScreen(){
59             switch (currentScreenIndex){
60             case 1:
61                 stockDetailsLoader.sourceComponent = (stockDetailsScreen.width > stockDetailsScreen.height)? stockChartComponentLandscape : stockChartComponentPortrait;
62                 currentScreenIndex = currentScreenIndex + 1;
63                 break;
64             case 2:
65                 stockDetailsLoader.sourceComponent = stockDetailsComponent;
66                 currentScreenIndex = currentScreenIndex - 1;
67                 break;
68             default:
69                 //Do Nothing
70             }
71             logRequest("currentScreenIndex = "+currentScreenIndex);
72         }
73
74         function loadDetails(){
75             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';
76             logRequest("Loading stock details from "+queryURL);
77             var response = new XMLHttpRequest();
78             response.onreadystatechange = function() {
79                 if (response.readyState == XMLHttpRequest.DONE) {
80                     refreshDetails(response.responseXML);
81                 }
82             }
83
84             response.open("GET", queryURL);
85             response.send();
86         }
87
88         function loadNews(){
89             rssURL = "http://feeds.finance.yahoo.com/rss/2.0/headline?region=US&lang=en-US&s="+symbol;
90             logRequest("Loading news from "+rssURL);
91             var response = new XMLHttpRequest();
92             response.onreadystatechange = function() {
93                 if (response.readyState == XMLHttpRequest.DONE) {
94                     refreshNewsModel(response.responseXML);
95                 }
96             }
97
98             response.open("GET", rssURL);
99             response.send();
100         }
101
102         function refreshDetails(responseXML){
103             if (!responseXML) return;
104             var xmlDoc = responseXML.documentElement;
105             var results = xmlDoc.firstChild;
106
107             if (results) {
108                 var quoteNodes = results.childNodes;
109                 if (quoteNodes){
110                     if (quoteNodes.length === 0) {
111                         logRequest("No results for stock quote details");
112                     }
113                     else{
114                         //We are only expecting one quote node per symbol.
115                         var quoteElements = quoteNodes[0].childNodes;
116                         var j = 0;
117                         var lastTradedDate = "", lastTradedTime ="";
118                         for (j = 0; j < quoteElements.length; j++){
119
120                             if (quoteElements[j].childNodes[0]) {
121                                 switch (quoteElements[j].nodeName){
122                                     case 'Name':
123                                         stockName = quoteElements[j].childNodes[0].nodeValue;
124                                         break;
125                                     case 'LastTradePriceOnly':
126                                         lastTradedPrice = quoteElements[j].childNodes[0].nodeValue;
127                                         break;
128                                     case 'LastTradeDate':
129                                         lastTradedDate = quoteElements[j].childNodes[0].nodeValue;
130                                         break;
131                                     case 'LastTradeTime':
132                                         lastTradedTime = quoteElements[j].childNodes[0].nodeValue;
133                                         break;
134                                     case 'Change':
135                                         change = quoteElements[j].childNodes[0].nodeValue;
136                                         break;
137                                     case 'ChangeinPercent':
138                                         changePercentage = quoteElements[j].childNodes[0].nodeValue;
139                                         break;
140                                     case 'DaysRange':
141                                         daysRange = quoteElements[j].childNodes[0].nodeValue;
142                                         break;
143                                     case 'YearRange':
144                                         yearRange = quoteElements[j].childNodes[0].nodeValue;
145                                         break;
146                                     case 'Volume':
147                                         marketVolume = quoteElements[j].childNodes[0].nodeValue;
148                                         break;
149                                     case 'PreviousClose':
150                                         prevClose = quoteElements[j].childNodes[0].nodeValue;
151                                         break;
152                                     case 'MarketCapitalization':
153                                         marketCap = quoteElements[j].childNodes[0].nodeValue;
154                                         break;
155                                     default:
156                                 }
157                             }
158                         }
159                         if (lastTradedDate !== "") lastTradedDateTime = lastTradedDate + " " + lastTradedTime;
160                     }
161                 }
162             }
163         }
164
165         function refreshNewsModel(responseXML){
166             if (!(responseXML && stockNewsDataModel)) return;
167
168             var xmlDoc = responseXML.documentElement;
169             var channel = xmlDoc.firstChild;
170
171             //Not the best code I ever wrote, but got no choice
172             //Refer to Memory leak issue with XMLListModel --> http://bugreports.qt.nokia.com/browse/QTBUG-15191
173
174             if (channel) {
175                 var itemNodes = channel.childNodes;
176                 if (itemNodes){
177
178                     logRequest("Clearing News Model");
179                     stockNewsDataModel.clear();
180
181                     var i = 0;
182                     for (i = 0; i < itemNodes.length; i++) {
183
184                         if (itemNodes[i].nodeName === 'item'){
185                             var newsElements = itemNodes[i].childNodes;
186                             var j = 0;
187                             var newsTitle,newsLink
188                             for (j = 0; j < newsElements.length; j++){
189
190                                 switch (newsElements[j].nodeName){
191                                     case 'title':
192                                         newsTitle = newsElements[j].childNodes[0].nodeValue;
193                                         break;
194                                     case 'link':
195                                         newsLink = newsElements[j].childNodes[0].nodeValue;
196                                         break;
197                                     default:
198                                 }
199                             }
200                             stockNewsDataModel.append({"title":newsTitle,"link":newsLink});
201                         }
202                     }
203                 }
204             }
205         }
206
207         ListModel{
208             id: stockNewsDataModel
209         }
210
211         Component {
212             id: stockNewsDelegate
213
214             Item {
215                 id: newsWrapper; width: stockDetailsLoader.width; height: itemHeight
216                 Item {
217                     anchors.fill: parent
218                     Rectangle { color: "black"; opacity: index % 2 ? 0.2 : 0.4; height: newsWrapper.height - 2; width: newsWrapper.width; y: 1 }
219                     Text {
220                         anchors {verticalCenter: parent.verticalCenter;left: parent.left;leftMargin: 10;right: parent.right}
221                         text: title; font.pixelSize: 14
222                         font.bold: false;
223                         verticalAlignment: Text.AlignVCenter
224                         horizontalAlignment: Text.AlignLeft
225                         elide: Text.ElideRight;
226                         color: "white";
227                         style: Text.Raised;
228                         styleColor: "black"
229                     }
230                     Library.CustomGestureArea {
231                         anchors.fill: parent
232                         onDoubleClicked: {
233                             logRequest("Opening news article: "+link);
234                             Qt.openUrlExternally(link);
235                         }
236                         onSwipeLeft: detailsRect.switchScreen()
237                         onSwipeRight: detailsRect.switchScreen()
238                     }
239                 }
240             }
241         }
242
243         Component {
244             id: stockDetailsComponent
245
246             Item {
247                 anchors.fill: parent
248
249                 Text {
250                     id: stockNameLabel
251                     anchors.top: parent.top
252                     width: parent.width
253                     anchors.horizontalCenter: parent.horizontalCenter
254                     height: 30
255                     horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter
256                     font.pixelSize: 18; font.bold: true; elide: Text.ElideMiddle; color: "#B8B8B8"; style: Text.Raised; styleColor: "black"
257                     text: (stockName != "")? (stockName +" ("+symbol+")"):symbol
258                 }
259
260
261                 Rectangle {
262                     id: stockDetailsSection
263                     border.width: 1
264                     border.color: "#BFBFBF"
265                     color:"#2E2E2E"
266                     anchors {top: stockNameLabel.bottom;left: parent.left;right: parent.right}
267                     height: 125
268                     radius: 15
269
270                     Column{
271                         id: stockDetailsColumn
272                         anchors {top: parent.top; left: parent.left; leftMargin: 10}
273                         width: parent.width
274
275                         StockDetailsRow{
276                             label1: "Last Traded"
277                             value1: lastTradedPrice
278                             cell1Width: stockDetailsColumn.width/2
279
280                             label2: "Day's Range"
281                             value2: daysRange
282                             cell2Width: stockDetailsColumn.width/2
283                         }
284
285                         StockDetailsRow{
286                             label1: "Last Trade Time"
287                             value1: lastTradedDateTime
288                             cell1Width: stockDetailsColumn.width/2
289
290                             label2: "52w Range"
291                             value2: yearRange
292                             cell2Width: stockDetailsColumn.width/2
293                         }
294
295                         StockDetailsRow{
296                             label1: "Change"
297                             value1: ((change != "" && changePercentage != "")? change + " ("+changePercentage+")":"")
298                             cell1Width: stockDetailsColumn.width/2
299
300                             label2: "Volume"
301                             value2: marketVolume
302                             cell2Width: stockDetailsColumn.width/2
303                         }
304
305                         StockDetailsRow{
306                             label1: "Prev. Close"
307                             value1: prevClose
308                             cell1Width: stockDetailsColumn.width/2
309
310                             label2: "Market Cap"
311                             value2: marketCap
312                             cell2Width: stockDetailsColumn.width/2
313                         }
314                     }
315                 }
316
317                 Rectangle{
318                     border.width: 1
319                     border.color: "#BFBFBF"
320                     color:"#2E2E2E"
321                     width: parent.width
322                     anchors {top: stockDetailsSection.bottom;topMargin: 5;
323                              bottom: parent.bottom;
324                              left: parent.left;
325                              right: parent.right}
326                     ListView {
327                         flickDeceleration: 500
328                         anchors.fill: parent
329                         model: stockNewsDataModel
330                         delegate: stockNewsDelegate
331                         focus:true
332                         snapMode: ListView.SnapToItem
333                     }
334                 }
335
336             }
337         }
338
339         Loader {
340           id: stockDetailsLoader
341           anchors{top: parent.top; bottom: parent.bottom; bottomMargin: 20;
342                   left: parent.left; leftMargin: 10
343                   right:  parent.right; rightMargin: 10}
344           sourceComponent: stockDetailsComponent
345
346           Connections{
347               target: stockDetailsScreen
348               onOrientationChanged: {
349                   logRequest("Orientation Changed");
350                   logRequest("New orientation is "+stockDetailsScreen.orientation);
351                   logRequest("Margins for StockDetailsLoader are "+stockDetailsLoader.anchors.leftMargin+", "+stockDetailsLoader.anchors.rightMargin);
352
353                   if (currentScreenIndex === 2){
354                       if (stockDetailsScreen.orientation == "Landscape")
355                           stockDetailsLoader.sourceComponent = stockChartComponentLandscape;
356                       else
357                           stockDetailsLoader.sourceComponent = stockChartComponentPortrait;
358                   }
359               }
360           }
361         }
362
363         Rectangle{
364             id: footerText
365             width: parent.width
366             height: 20
367             z: 5
368             color: "#343434"
369             anchors.top: stockDetailsLoader.bottom
370             Text {
371                 id: footerMessage
372                 anchors.fill: parent
373                 text: "Swipe horizontally to switch between details and charts."
374                 horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignVCenter
375                 width: parent.width; font.pixelSize: 12; elide: Text.ElideRight;
376                 color: "#cccccc"
377                 style: Text.Raised; styleColor: "black"
378             }
379
380             Timer {
381                 id: footerMessageTimer
382                 interval: 10000
383                 repeat: false
384                 onTriggered: {
385                     footerMessage.text = "";
386                 }
387             }
388
389             Component.onCompleted: {
390                 footerMessageTimer.start();
391             }
392         }
393
394         Component{
395             id: stockChartComponentLandscape
396
397             Item {
398                 id: chartAreaWrapperLand
399                 anchors.fill: parent
400
401                 Rectangle {
402                     id: chartAreaLand
403                     border.width: 1
404                     border.color: "#BFBFBF"
405                     //color:"#2E2E2E"
406                     color:"white"
407                     anchors { top: parent.top;topMargin: 40;
408                               bottom: parent.bottom; bottomMargin: 20;
409                               left: parent.left; right: parent.right}
410                     radius: 10
411
412                     Library.Loading { anchors.centerIn: parent; visible: chartImg.status == Image.Loading}
413
414                     Image {
415                         id: chartImg
416                         anchors {left: parent.left; leftMargin: 10; verticalCenter: parent.verticalCenter}
417                         source: chartURL
418                         sourceSize.width: 512
419                         sourceSize.height: 288
420                         smooth: true
421                         fillMode: Image.PreserveAspectFit
422                         onStatusChanged: {
423                             switch(status){
424                                 case Image.Ready:
425                                     logRequest("Image is ready");
426                                     break;
427                                 case Image.Loading:
428                                     logRequest("Image is loading");
429                                     break;
430                                 case Image.Error:
431                                     logRequest("Image loading failed");
432                                     break;
433                                 default:
434                                     logRequest("No image specified");
435                                     break;
436                             }
437
438                         }
439
440                         Connections{
441                             target: stockDetailsScreen
442                             onLoadChart: {
443                                 chartURL = baseChartURL+"&t="+duration+"&s="+symbol;
444                                 logRequest(chartURL);
445                             }
446                         }
447                     }
448
449                     Column {
450                         width: 130
451                         spacing: 20
452                         anchors {top: parent.top; topMargin: 40; bottom: parent.bottom;
453                                  right: chartAreaLand.right;rightMargin: 10}
454
455                         Row {
456                             height: 40
457                             spacing: 20
458                             anchors.horizontalCenter: parent.horizontalCenter
459                             Library.Button {
460                                 text:  "1d"
461                                 anchors { verticalCenter: parent.verticalCenter}
462                                 width: 50; height: 32
463                                 onClicked: loadChart("1d");
464                             }
465
466                             Library.Button {
467                                 text:  "5d"
468                                 anchors { verticalCenter: parent.verticalCenter}
469                                 width: 50; height: 32
470                                 onClicked: loadChart("5d");
471                             }
472                         }
473
474                         Row {
475                             height: 40
476                             spacing: 20
477                             anchors.horizontalCenter: parent.horizontalCenter
478                             Library.Button {
479                                 text:  "3m"
480                                 anchors { verticalCenter: parent.verticalCenter}
481                                 width: 50; height: 32
482                                 onClicked: loadChart("3m");
483                             }
484
485                             Library.Button {
486                                 text:  "6m"
487                                 anchors { verticalCenter: parent.verticalCenter}
488                                 width: 50; height: 32
489                                 onClicked: loadChart("6m");
490                             }
491                         }
492                         Row {
493                             height: 40
494                             spacing: 20
495                             anchors.horizontalCenter: parent.horizontalCenter
496                             Library.Button {
497                                 text:  "1y"
498                                 anchors { verticalCenter: parent.verticalCenter}
499                                 width: 50; height: 32
500                                 onClicked: loadChart("1y");
501                             }
502
503                             Library.Button {
504                                 text:  "2y"
505                                 anchors { verticalCenter: parent.verticalCenter}
506                                 width: 50; height: 32
507                                 onClicked: loadChart("2y");
508                             }
509                         }
510                         Row {
511                             height: 40
512                             spacing: 20
513                             anchors.horizontalCenter: parent.horizontalCenter
514                             Library.Button {
515                                 text:  "5y"
516                                 anchors { verticalCenter: parent.verticalCenter}
517                                 width: 50; height: 32
518                                 onClicked: loadChart("5y");
519                             }
520
521                             Library.Button {
522                                 text:  "max"
523                                 anchors { verticalCenter: parent.verticalCenter}
524                                 width: 50; height: 32
525                                 onClicked: loadChart("my");
526                             }
527                         }
528                     }
529                 }
530             }
531         }
532
533         Component{
534             id: stockChartComponentPortrait
535
536             Item {
537                 id: chartAreaWrapperPort
538                 anchors.fill: parent
539
540                 Rectangle {
541                     id: chartAreaPort
542                     border.width: 1
543                     border.color: "#BFBFBF"
544                     //color:"#2E2E2E"
545                     color:"white"
546                     anchors { top: parent.top;topMargin: 40;
547                               bottom: parent.bottom; bottomMargin: 20;
548                               left: parent.left; right: parent.right}
549                     radius: 10
550
551                     Library.Loading { anchors.centerIn: parent; visible: chartImgPort.status == Image.Loading}
552
553                     Image {
554                         id: chartImgPort
555                         //anchors {left: parent.left; leftMargin: 10; horizontalCenter: parent.horizontalCenter}
556                         anchors {horizontalCenter: parent.horizontalCenter; top: parent.top; topMargin: 40}
557                         source: chartURL
558                         sourceSize.width: 512
559                         sourceSize.height: 288
560                         smooth: true
561                         fillMode: Image.PreserveAspectFit
562                         width: parent.width - 20
563                         onStatusChanged: {
564                             switch(status){
565                                 case Image.Ready:
566                                     logRequest("Image is ready");
567                                     break;
568                                 case Image.Loading:
569                                     logRequest("Image is loading");
570                                     break;
571                                 case Image.Error:
572                                     logRequest("Image loading failed");
573                                     break;
574                                 default:
575                                     logRequest("No image specified");
576                                     break;
577                             }
578
579                         }
580
581                         Connections{
582                             target: stockDetailsScreen
583                             onLoadChart: {
584                                 chartURL = baseChartURL+"&t="+duration+"&s="+symbol;
585                                 logRequest(chartURL);
586                             }
587                         }
588                     }
589
590                     Column {
591                         width: 280
592                         spacing: 20
593                         anchors {verticalCenter: parent.verticalCenter;verticalCenterOffset: 80; horizontalCenter: parent.horizontalCenter}
594
595                         Row {
596                             height: 40
597                             spacing: 20
598                             anchors.horizontalCenter: parent.horizontalCenter
599                             Library.Button {
600                                 text:  "1d"
601                                 anchors { verticalCenter: parent.verticalCenter}
602                                 width: 50; height: 32
603                                 onClicked: loadChart("1d");
604                             }
605
606                             Library.Button {
607                                 text:  "5d"
608                                 anchors { verticalCenter: parent.verticalCenter}
609                                 width: 50; height: 32
610                                 onClicked: loadChart("5d");
611                             }
612
613                             Library.Button {
614                                 text:  "3m"
615                                 anchors { verticalCenter: parent.verticalCenter}
616                                 width: 50; height: 32
617                                 onClicked: loadChart("3m");
618                             }
619
620                             Library.Button {
621                                 text:  "6m"
622                                 anchors { verticalCenter: parent.verticalCenter}
623                                 width: 50; height: 32
624                                 onClicked: loadChart("6m");
625                             }
626                         }
627
628                         Row {
629                             height: 40
630                             spacing: 20
631                             anchors.horizontalCenter: parent.horizontalCenter
632                             Library.Button {
633                                 text:  "1y"
634                                 anchors { verticalCenter: parent.verticalCenter}
635                                 width: 50; height: 32
636                                 onClicked: loadChart("1y");
637                             }
638
639                             Library.Button {
640                                 text:  "2y"
641                                 anchors { verticalCenter: parent.verticalCenter}
642                                 width: 50; height: 32
643                                 onClicked: loadChart("2y");
644                             }
645
646                             Library.Button {
647                                 text:  "5y"
648                                 anchors { verticalCenter: parent.verticalCenter}
649                                 width: 50; height: 32
650                                 onClicked: loadChart("5y");
651                             }
652
653                             Library.Button {
654                                 text:  "max"
655                                 anchors { verticalCenter: parent.verticalCenter}
656                                 width: 50; height: 32
657                                 onClicked: loadChart("my");
658                             }
659                         }
660                     }
661                 }
662             }
663         }
664
665     }
666 }