Harmattan Ready, Fremantle Testing Pending
[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     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
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         Component{
364             id: stockChartComponentLandscape
365
366             Item {
367                 id: chartAreaWrapperLand
368                 anchors.fill: parent
369
370                 Rectangle {
371                     id: chartAreaLand
372                     border.width: 1
373                     border.color: "#BFBFBF"
374                     //color:"#2E2E2E"
375                     color:"white"
376                     anchors { top: parent.top;topMargin: 40;
377                               bottom: parent.bottom; bottomMargin: 40;
378                               left: parent.left; right: parent.right}
379                     radius: 10
380
381                     Library.Loading { anchors.centerIn: parent; visible: chartImg.status == Image.Loading}
382
383                     Image {
384                         id: chartImg
385                         anchors {left: parent.left; leftMargin: 10; verticalCenter: parent.verticalCenter}
386                         source: chartURL
387                         sourceSize.width: 512
388                         sourceSize.height: 288
389                         smooth: true
390                         fillMode: Image.PreserveAspectFit
391                         onStatusChanged: {
392                             switch(status){
393                                 case Image.Ready:
394                                     logRequest("Image is ready");
395                                     break;
396                                 case Image.Loading:
397                                     logRequest("Image is loading");
398                                     break;
399                                 case Image.Error:
400                                     logRequest("Image loading failed");
401                                     break;
402                                 default:
403                                     logRequest("No image specified");
404                                     break;
405                             }
406
407                         }
408
409                         Connections{
410                             target: stockDetailsScreen
411                             onLoadChart: {
412                                 chartURL = baseChartURL+"&t="+duration+"&s="+symbol;
413                                 logRequest(chartURL);
414                             }
415                         }
416                     }
417
418                     Column {
419                         width: 130
420                         spacing: 20
421                         anchors {top: parent.top; topMargin: 40; bottom: parent.bottom;
422                                  right: chartAreaLand.right;rightMargin: 10}
423
424                         Row {
425                             height: 40
426                             spacing: 20
427                             anchors.horizontalCenter: parent.horizontalCenter
428                             Library.Button {
429                                 text:  "1d"
430                                 anchors { verticalCenter: parent.verticalCenter}
431                                 width: 50; height: 32
432                                 onClicked: loadChart("1d");
433                             }
434
435                             Library.Button {
436                                 text:  "5d"
437                                 anchors { verticalCenter: parent.verticalCenter}
438                                 width: 50; height: 32
439                                 onClicked: loadChart("5d");
440                             }
441                         }
442
443                         Row {
444                             height: 40
445                             spacing: 20
446                             anchors.horizontalCenter: parent.horizontalCenter
447                             Library.Button {
448                                 text:  "3m"
449                                 anchors { verticalCenter: parent.verticalCenter}
450                                 width: 50; height: 32
451                                 onClicked: loadChart("3m");
452                             }
453
454                             Library.Button {
455                                 text:  "6m"
456                                 anchors { verticalCenter: parent.verticalCenter}
457                                 width: 50; height: 32
458                                 onClicked: loadChart("6m");
459                             }
460                         }
461                         Row {
462                             height: 40
463                             spacing: 20
464                             anchors.horizontalCenter: parent.horizontalCenter
465                             Library.Button {
466                                 text:  "1y"
467                                 anchors { verticalCenter: parent.verticalCenter}
468                                 width: 50; height: 32
469                                 onClicked: loadChart("1y");
470                             }
471
472                             Library.Button {
473                                 text:  "2y"
474                                 anchors { verticalCenter: parent.verticalCenter}
475                                 width: 50; height: 32
476                                 onClicked: loadChart("2y");
477                             }
478                         }
479                         Row {
480                             height: 40
481                             spacing: 20
482                             anchors.horizontalCenter: parent.horizontalCenter
483                             Library.Button {
484                                 text:  "5y"
485                                 anchors { verticalCenter: parent.verticalCenter}
486                                 width: 50; height: 32
487                                 onClicked: loadChart("5y");
488                             }
489
490                             Library.Button {
491                                 text:  "max"
492                                 anchors { verticalCenter: parent.verticalCenter}
493                                 width: 50; height: 32
494                                 onClicked: loadChart("my");
495                             }
496                         }
497                     }
498                 }
499             }
500         }
501
502         Component{
503             id: stockChartComponentPortrait
504
505             Item {
506                 id: chartAreaWrapperPort
507                 anchors.fill: parent
508
509                 Rectangle {
510                     id: chartAreaPort
511                     border.width: 1
512                     border.color: "#BFBFBF"
513                     //color:"#2E2E2E"
514                     color:"white"
515                     anchors { top: parent.top;topMargin: 40;
516                               bottom: parent.bottom; bottomMargin: 40;
517                               left: parent.left; right: parent.right}
518                     radius: 10
519
520                     Library.Loading { anchors.centerIn: parent; visible: chartImgPort.status == Image.Loading}
521
522                     Image {
523                         id: chartImgPort
524                         //anchors {left: parent.left; leftMargin: 10; horizontalCenter: parent.horizontalCenter}
525                         anchors {horizontalCenter: parent.horizontalCenter; top: parent.top; topMargin: 40}
526                         source: chartURL
527                         sourceSize.width: 512
528                         sourceSize.height: 288
529                         smooth: true
530                         fillMode: Image.PreserveAspectFit
531                         width: parent.width - 20
532                         onStatusChanged: {
533                             switch(status){
534                                 case Image.Ready:
535                                     logRequest("Image is ready");
536                                     break;
537                                 case Image.Loading:
538                                     logRequest("Image is loading");
539                                     break;
540                                 case Image.Error:
541                                     logRequest("Image loading failed");
542                                     break;
543                                 default:
544                                     logRequest("No image specified");
545                                     break;
546                             }
547
548                         }
549
550                         Connections{
551                             target: stockDetailsScreen
552                             onLoadChart: {
553                                 chartURL = baseChartURL+"&t="+duration+"&s="+symbol;
554                                 logRequest(chartURL);
555                             }
556                         }
557                     }
558
559                     Column {
560                         width: 280
561                         spacing: 20
562                         anchors {verticalCenter: parent.verticalCenter;verticalCenterOffset: 80; horizontalCenter: parent.horizontalCenter}
563
564                         Row {
565                             height: 40
566                             spacing: 20
567                             anchors.horizontalCenter: parent.horizontalCenter
568                             Library.Button {
569                                 text:  "1d"
570                                 anchors { verticalCenter: parent.verticalCenter}
571                                 width: 50; height: 32
572                                 onClicked: loadChart("1d");
573                             }
574
575                             Library.Button {
576                                 text:  "5d"
577                                 anchors { verticalCenter: parent.verticalCenter}
578                                 width: 50; height: 32
579                                 onClicked: loadChart("5d");
580                             }
581
582                             Library.Button {
583                                 text:  "3m"
584                                 anchors { verticalCenter: parent.verticalCenter}
585                                 width: 50; height: 32
586                                 onClicked: loadChart("3m");
587                             }
588
589                             Library.Button {
590                                 text:  "6m"
591                                 anchors { verticalCenter: parent.verticalCenter}
592                                 width: 50; height: 32
593                                 onClicked: loadChart("6m");
594                             }
595                         }
596
597                         Row {
598                             height: 40
599                             spacing: 20
600                             anchors.horizontalCenter: parent.horizontalCenter
601                             Library.Button {
602                                 text:  "1y"
603                                 anchors { verticalCenter: parent.verticalCenter}
604                                 width: 50; height: 32
605                                 onClicked: loadChart("1y");
606                             }
607
608                             Library.Button {
609                                 text:  "2y"
610                                 anchors { verticalCenter: parent.verticalCenter}
611                                 width: 50; height: 32
612                                 onClicked: loadChart("2y");
613                             }
614
615                             Library.Button {
616                                 text:  "5y"
617                                 anchors { verticalCenter: parent.verticalCenter}
618                                 width: 50; height: 32
619                                 onClicked: loadChart("5y");
620                             }
621
622                             Library.Button {
623                                 text:  "max"
624                                 anchors { verticalCenter: parent.verticalCenter}
625                                 width: 50; height: 32
626                                 onClicked: loadChart("my");
627                             }
628                         }
629                     }
630                 }
631             }
632         }
633
634     }
635 }