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