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