Replaced YQL data source with CSV
[marketstoday] / src / qml / MarketsTodayApp.qml
1 /*
2 @version: 0.4
3 @author: Sudheer K. <scifi1947 at gmail.com>
4 @license: GNU General Public License
5 */
6
7 import QtQuick 1.1
8 import com.nokia.meego 1.0
9
10 import "Library" as Library
11 import "Library/js/ISODate.js" as DateLib
12 import "Library/js/DBUtility.js" as DBUtility
13 import "Library/js/CSVUtility.js" as CSVUtility
14 import "Library/js/Common.js" as Common
15 import "Library/js/CoreLogic.js" as CoreLib
16
17 PageStackWindow {
18     id: appWindow
19     initialPage: mainPage
20     showToolBar: false
21     showStatusBar: false
22
23     property int autoUpdateInterval: 300000
24     property bool updateWeekDaysOnly: false
25     property bool updateOnSavedNetworksOnly: false
26     property bool isDesktopWidget: false
27     property string rssURL: "http://finance.yahoo.com/rss/topfinstories"
28     property string lastUpdatedTimeStamp
29     //property string selectedSymbol:"YHOO"
30     property string selectedSymbol:sharedContext.getStockSymbol()
31
32     signal showConfigInNewWindow
33     signal showStockDetails(string strSymbol)
34     signal quoteRefreshStarted
35     signal quoteRefreshCompleted(bool success, string strMessage)
36     signal newsReloadCompleted(bool success, string strMessage)
37     signal checkNetworkStatus
38
39     function initialize(){
40         mainPage.initializePage();
41     }
42
43     Page {
44         id: mainPage
45
46         property int itemHeight: 50
47         property int titleBarHeight: 60
48         property int toolBarHeight: 40
49         property int componentWidth: mainPage.width
50
51         function reloadData(){
52             CoreLib.reloadQuotes();
53             CoreLib.reloadNews();
54         }
55
56         function initializePage(){
57             var componentToDisplay = sharedContext.getComponentToDisplay();
58             if (componentToDisplay === "StockQuoteDetails"){
59                 uiLoader.sourceComponent = stockDetailsComponent;
60                 titleBar.buttonType = "Close";
61                 titleBar.displayMenu = false;
62                 toolBar.displayIcons = false;
63             }
64             else{
65                 DBUtility.initialize();
66                 uiLoader.sourceComponent = stockQuotesUIComponent;
67                 CoreLib.initialize();
68             }
69         }
70
71         Component.onCompleted: {
72             initializePage();
73         }
74
75         Timer {
76             id: autoUpdateTimer
77             interval: autoUpdateInterval
78             //running: (autoUpdateInterval == 0? false:true)
79             repeat: true
80             onTriggered: {
81                 if (!updateWeekDaysOnly){
82                     logUtility.logMessage("Allowed to update all days of the week");
83                     mainPage.reloadData();
84                     //checkNetworkStatus();
85                 }
86                 else if (Common.isTodayAWeekDay()){
87                     logUtility.logMessage("Today is a weekday");
88                     mainPage.reloadData();
89                     //checkNetworkStatus();
90                 }
91                 else{
92                     logUtility.logMessage("Update not triggered: Today is not a weekday");
93                 }
94             }
95         }
96
97         ListModel{
98             id: stockQuoteDataModel
99         }
100
101         ListModel {
102             id: newsDataModel
103         }
104
105         Rectangle {
106             id: background
107             anchors.fill: parent;
108             color: "#343434"
109             clip: true
110
111             Component {
112                 id: stockQuotesDelegateLandscape
113
114                 Item {
115                     id: wrapper; width: mainPage.componentWidth; height: mainPage.itemHeight
116                     Item {
117                         Rectangle { color: "black"; opacity: index % 2 ? 0.2 : 0.4; height: wrapper.height - 2; width: wrapper.width; y: 1
118                             MouseArea {
119                                 anchors.fill: parent
120                                 onDoubleClicked: {
121                                     appWindow.selectedSymbol = symbol;
122                                     uiLoader.sourceComponent = stockDetailsComponent;
123                                     titleBar.buttonType = "Back";
124                                     titleBar.displayMenu = false;
125                                     toolBar.displayIcons = false;
126                                 }
127                             }
128                         }
129
130                         Row {
131                             x: 30;y: 15;
132                             width: mainPage.componentWidth - 60;
133                             spacing: 5
134
135                             Text { text: stockName; width: parent.width * 30/100; font.pixelSize: 18; font.bold: true; elide: Text.ElideRight; color: "white"; style: Text.Raised; styleColor: "black" }
136                             Text { text: lastTradedPrice; width: parent.width * 15/100; font.pixelSize: 18; horizontalAlignment: Text.AlignLeft; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
137                             Text { text: change !== ""? (change + " ("+changePercentage+")"):""; width: parent.width * 25/100;  font.pixelSize: 18; horizontalAlignment: Text.AlignLeft; elide: Text.ElideRight
138                                     color: if(change >= 0){"green";} else {"red";}
139                                         style: Text.Raised; styleColor: "black" }
140                             Text { text: volume; width: parent.width * 15/100; font.pixelSize: 18; horizontalAlignment: Text.AlignLeft; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
141                             Text { text: marketCap; width: parent.width * 15/100; font.pixelSize: 18; horizontalAlignment: Text.AlignLeft; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
142                         }
143                     }
144                 }
145             }
146
147             Component {
148                 id: stockQuotesDelegatePortrait
149
150                 Item {
151                     id: wrapperItem; width: mainPage.componentWidth; height: mainPage.itemHeight
152                     Item {
153                         Rectangle { color: "black"; opacity: index % 2 ? 0.2 : 0.4; height: wrapperItem.height - 2; width: wrapperItem.width; y: 1                            
154                             MouseArea {
155                                 anchors.fill: parent
156                                 onDoubleClicked: {
157                                     appWindow.selectedSymbol = symbol;
158                                     uiLoader.sourceComponent = stockDetailsComponent;
159                                     titleBar.buttonType = "Back";
160                                     titleBar.displayMenu = false;
161                                     toolBar.displayIcons = false;
162                                 }
163                             }
164                         }
165
166                         Row {
167                             x: 10;y: 15;
168                             width: mainPage.componentWidth - 5;
169                             spacing: 3
170
171                             Text { text: stockName; width: parent.width * 42/100; font.pixelSize: 18; font.bold: true; elide: Text.ElideRight; color: "white"; style: Text.Raised; styleColor: "black" }
172                             Text { text: lastTradedPrice; width: parent.width * 20/100; font.pixelSize: 18; horizontalAlignment: Text.AlignLeft; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
173                             Column {
174                                 y: -15;
175                                 width: parent.width * 18/100; height: parent.height
176                                 spacing: 2
177                                 Text { text: change; font.pixelSize: 16; horizontalAlignment: Text.AlignLeft; elide: Text.ElideRight
178                                     color: if(change >= 0){"green";} else {"red";}
179                                         style: Text.Raised; styleColor: "black" }
180                                 Text { text: changePercentage; font.pixelSize: 16; horizontalAlignment: Text.AlignLeft; elide: Text.ElideRight;
181                                     color: if(change >= 0){"green";} else {"red";}
182                                         style: Text.Raised; styleColor: "black" }
183                             }
184                             Text { text: volume; width: parent.width * 20/100; font.pixelSize: 18; horizontalAlignment: Text.AlignLeft; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
185                         }
186                     }
187                 }
188             }
189
190             Component {
191                 id: newsDelegate
192
193                 Item {
194                     id: newsWrapper; width: mainPage.componentWidth; height: mainPage.itemHeight
195                     Item {
196                         anchors.fill: parent
197                         Rectangle { color: "black"; opacity: index % 2 ? 0.2 : 0.4; height: newsWrapper.height - 2; width: newsWrapper.width; y: 1 }
198                         Text {
199                             anchors.verticalCenter: parent.verticalCenter
200                             anchors.left: parent.left
201                             anchors.leftMargin: 10
202                             anchors.right: parent.right
203                             text: title; font.pixelSize: 18
204                             font.bold: false;
205                             verticalAlignment: Text.AlignVCenter
206                             horizontalAlignment: Text.AlignLeft
207                             elide: Text.ElideRight;
208                             color: "white";
209                             style: Text.Raised;
210                             styleColor: "black"
211                         }
212                         MouseArea{
213                             anchors.fill: parent
214                             onDoubleClicked: Qt.openUrlExternally(link);
215                         }
216                     }
217                 }
218             }
219
220             Library.TitleBar {
221                 id: titleBar;
222                 width: parent.width; height: mainPage.titleBarHeight;
223                 anchors.top: parent.top
224                 title: "Markets Today";
225                 buttonType: "Close";
226                 z: 5  //required to keep Titlebar and Menu buttons on top of everything else
227
228                 onCloseClicked: Qt.quit()
229
230                 onTickersClicked: {
231                     uiLoader.sourceComponent = configTickersComponent;
232                     titleBar.buttonType = "Back";
233                     titleBar.displayMenu = false;
234                     toolBar.displayIcons = false;
235                 }
236
237                 onOptionsClicked: {
238                     uiLoader.sourceComponent = configParamsComponent;
239                     titleBar.buttonType = "Back";
240                     titleBar.displayMenu = false;
241                     toolBar.displayIcons = false;
242                 }
243
244                 onBackClicked: {
245                     uiLoader.sourceComponent = stockQuotesUIComponent;
246                     titleBar.buttonType = "Close";
247                     titleBar.displayMenu = false;
248                     toolBar.displayIcons = true;
249                     mainPage.initializePage();
250                 }
251             }
252
253             Loader {
254                 id: uiLoader
255                 width: parent.width
256                 anchors.top: titleBar.bottom
257                 anchors.bottom: toolBar.top
258             }
259
260             Library.ToolBar {
261                 id:toolBar
262                 width: parent.width; height: mainPage.toolBarHeight
263                 anchors.bottom: parent.bottom
264                 opacity: 0.9
265                 displayNavigation: false
266                 onReloadButtonClicked: mainPage.reloadData();
267
268                 onNewsButtonClicked: {
269                     uiLoader.sourceComponent = newsComponent;
270                     toolBar.displayIcons = true;
271                     toolBar.targetContentType = "Stocks";
272                 }
273
274                 onStocksButtonClicked: {
275                     uiLoader.sourceComponent = stockQuotesUIComponent;
276                     toolBar.displayIcons = true;
277                     toolBar.targetContentType = "News";
278                 }
279
280
281                 Connections {
282                     target: appWindow
283                     onQuoteRefreshStarted:{
284                         if (!toolBar.updatePending) toolBar.updatePending = true;
285                     }
286                     onQuoteRefreshCompleted:{
287                         toolBar.updatePending = false;
288                     }
289                 }
290             }
291
292
293
294             Component {
295                 id: stockQuotesUIComponent
296                 Item {
297                     Rectangle{
298                         id: listViewWrapper
299                         width: parent.width
300                         anchors.top: parent.top
301                         anchors.bottom: footerTextArea.top
302                         color: "#343434"
303
304                         ListView {
305                             id: stockQuotesView
306                             anchors.fill: parent
307                             flickDeceleration: 500
308                             model: stockQuoteDataModel
309                             delegate:  listViewWrapper.height > listViewWrapper.width ? stockQuotesDelegatePortrait : stockQuotesDelegateLandscape
310                             focus:true
311                             snapMode: ListView.SnapToItem
312                         }
313                     }
314
315                     Rectangle {
316                         id: stockStatusMsgArea
317                         height: 100
318                         color: "#343434"
319                         anchors {left: parent.left; leftMargin: 15; right: parent.right; rightMargin: 15;
320                                 verticalCenter: parent.verticalCenter}
321                         visible: false
322
323                         Text {
324                             id: stockStatusText
325                             anchors.fill: parent
326                             text: "Loading quotes.."
327                             horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter
328                             width: parent.width; font.pixelSize: 16; elide: Text.ElideNone;
329                             color: "#cccccc"
330                             wrapMode: Text.WrapAtWordBoundaryOrAnywhere
331                             style: Text.Raised; styleColor: "black"
332
333                             Connections {
334                                 target: appWindow
335                                 onQuoteRefreshCompleted: {
336                                     if (success){
337                                         stockStatusMsgArea.visible = false;
338                                         listViewWrapper.visible = true;
339                                     }
340                                     else{
341                                         stockStatusText.text = strMessage;
342                                         stockStatusMsgArea.visible = true;
343                                         listViewWrapper.visible = true;
344                                     }
345                                 }
346                             }
347                         }
348                     }
349
350                     Rectangle{
351                         id: footerTextArea
352                         width: parent.width
353                         height: 25
354                         color: "#343434"
355                         anchors.bottom: parent.bottom
356                         Text {
357                             id: footerMessage
358                             anchors.fill: parent
359                             text: lastUpdatedTimeStamp
360                             horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignVCenter
361                             width: parent.width; font.pixelSize: 12; elide: Text.ElideRight;
362                             color: "#cccccc"
363                             style: Text.Raised; styleColor: "black"
364                         }
365
366                         Timer {
367                             id: footerMessageTimer
368                             interval: 10000
369                             repeat: false
370                             onTriggered: {
371                                 footerMessage.text = appWindow.lastUpdatedTimeStamp;
372                             }
373                         }
374
375                         Connections {
376                             target: appWindow
377                             onQuoteRefreshCompleted:{
378                                 if (success){
379                                     footerMessage.text = "Double-tap on a row to display more details.";
380                                     footerMessageTimer.start();
381                                 }
382                                 else{
383                                     footerMessage.text = appWindow.lastUpdatedTimeStamp;
384                                 }
385                             }
386                         }
387                     }
388                 }
389             }
390
391             Component{
392                 id: stockDetailsComponent
393                 StockDetailsComponent {
394                     id: detailsComponent
395                     symbol: selectedSymbol
396                     onLogRequest: logUtility.logMessage(strMessage)
397                     orientation: (appWindow.inPortrait)? "Portrait":"Landscape";  //Initial Orientation
398                     states: [
399                                 State {
400                                     name: "inLandscape"
401                                     when: !appWindow.inPortrait
402                                     PropertyChanges {
403                                         target: detailsComponent
404                                         orientation: "Landscape"
405                                     }
406                                 },
407                                 State {
408                                     name: "inPortrait"
409                                     when: appWindow.inPortrait
410                                     PropertyChanges {
411                                         target: detailsComponent
412                                         orientation: "Portrait"
413                                     }
414                                 }
415                     ]
416
417                     Component.onCompleted: {
418                         if (appWindow.inPortrait){
419                             logUtility.logMessage("Initial orientation is Portrait");
420                         }
421                         else
422                         {
423                             logUtility.logMessage("Initial orientation is Landscape");
424                         }
425                     }
426                 }                
427             }
428
429             Component {
430                 id: newsComponent
431                 Item {
432                     Rectangle{
433                         id: newsViewArea
434                         width: parent.width
435                         anchors.top: parent.top
436                         anchors.bottom: parent.bottom
437                         color: "#343434"
438
439                         ListView {
440                             id: newsView
441                             anchors.fill: parent
442                             flickDeceleration: 500
443                             model: newsDataModel
444                             delegate:  newsDelegate
445                             focus:true
446                             snapMode: ListView.SnapToItem
447                         }
448                     }
449
450                     Rectangle {
451                         id: newsStatusMsgArea
452                         height: 100
453                         color: "#343434"
454                         anchors {left: parent.left; leftMargin: 15; right: parent.right; rightMargin: 15;
455                                 verticalCenter: parent.verticalCenter}
456                         visible: false
457
458                         Text {
459                             id: newsStatusText
460                             anchors.fill: parent
461                             text: "Loading news.."
462                             horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter
463                             width: parent.width; font.pixelSize: 16; elide: Text.ElideNone;
464                             color: "#cccccc"
465                             wrapMode: Text.WrapAtWordBoundaryOrAnywhere
466                             style: Text.Raised; styleColor: "black"
467
468                             Connections {
469                                 target: appWindow
470                                 onNewsReloadCompleted: {
471                                     if (success){
472                                         newsStatusMsgArea.visible = false;
473                                         newsViewArea.visible = true;
474                                     }
475                                     else{
476                                         newsStatusText.text = strMessage;
477                                         newsViewArea.visible = false;
478                                         newsStatusMsgArea.visible = true;
479                                     }
480                                 }
481                             }
482                         }
483                     }
484
485                 }
486             }
487
488             Component {
489                 id: configTickersComponent
490
491                 ConfigTickersComponent{
492                     itemHeight: mainPage.itemHeight
493                     componentWidth: mainPage.componentWidth
494                     onLogRequest: logUtility.logMessage(strMessage)
495                 }
496             }
497
498             Component {
499                 id: configParamsComponent
500                 ConfigParametersComponent{
501                     onLogRequest: logUtility.logMessage(strMessage)
502                 }
503             }
504         }
505     }
506 }