9f9e05426b300257ff9804a675042d5ad7c0b379
[stockwidget] / lib-stock-home-widget.c
1 /*
2  * Simple stock widget
3  * Jon Parr
4  */
5
6 #include <gtk/gtk.h>
7 #include <gdk/gdk.h>
8 #include <string.h>
9 #include <hildon/hildon.h>
10 #include "lib-stock-home-widget.h"
11 #include "lib-stock-settings.h"
12
13 HD_DEFINE_PLUGIN_MODULE (StockPlugin, stock_plugin, HD_TYPE_HOME_PLUGIN_ITEM)
14
15 static void
16 OnClickRefresh(GtkWidget *widget, StockPluginContext *psContext)
17 {
18         DebugOut(("OnClickRefresh"));
19         
20         /* Disable button while getting latest stocks */
21         gtk_widget_set_sensitive(GTK_WIDGET(psContext->psButton),FALSE);
22         {
23                 float   fPrice;
24                 float   fChange;
25                 char    szBuffer[1024];         
26
27                 if(RetrieveStockPrice(psContext->hSG,psContext->psSettings->ppszTickers[0],&fPrice,NULL,&fChange) == SG_OK)
28                 {
29                         char             szTime[80];
30                         time_t           rawtime;
31                         struct tm       *timeinfo;
32
33                         time(&rawtime);
34                         timeinfo = localtime(&rawtime);
35                         strftime(szTime,80,"(%I:%M%p)",timeinfo);
36                         
37                         /* Update to latest price */
38                         sprintf(szBuffer,"%s %.2f %.2f%% %s",psContext->psSettings->ppszTickers[0],fPrice,fChange,szTime);
39
40                         hildon_button_set_title(HILDON_BUTTON(psContext->psButton),szBuffer);
41                         gtk_widget_queue_draw(GTK_WIDGET(psContext->psBox));
42                 }
43                 /*
44                   If it fails, leave the old price as the user can see the timestamp
45                   and know it's not up-to-date. So just notify them of the failure.
46                  */
47                 else
48                 {
49                         hildon_banner_show_information(GTK_WIDGET(widget),NULL,"Failed to retrieve stock information, check ticker symbol and try again.");
50                 }
51         }
52         gtk_widget_set_sensitive(GTK_WIDGET(psContext->psButton),TRUE);
53 }
54
55 static gboolean
56 UpdateStockOnTimeout(gpointer data)
57 {
58         StockPluginContext *psContext = (StockPluginContext*)data;
59
60         DebugOut(("UpdateStockOnTimeout"));
61         
62         if(!psContext)
63                 return FALSE;
64
65         /* If no longer a timeout, don't do anything and stop */
66         if(!psContext->psSettings->uiUpdateTime)
67                 return FALSE;
68
69         /* Update stock price */
70         OnClickRefresh(GTK_WIDGET(psContext->psButton),psContext);
71         return TRUE;
72 }
73
74 static void
75 stock_show_settings_dialog(GtkWidget* widget, gpointer data)
76 {
77         StockPluginContext      *psContext = (StockPluginContext*)data;
78         GtkWidget                       *dialog;
79         GtkWidget                       *content;
80         GtkSizeGroup            *group;
81         GtkWidget                       *addselector;
82         GtkWidget                       *addbutton;
83         GtkWidget                       *timeselector;
84         GtkWidget                       *timebutton;
85
86         DebugOut(("stock_show_settings_dialog"));
87         
88         /* Stop timer while settings are one */
89         if(psContext->nTimerID != -1)
90         {
91                 g_source_remove(psContext->nTimerID);
92                 psContext->nTimerID = -1;
93         }
94         
95         dialog  = gtk_dialog_new_with_buttons("Settings",NULL,0,"Save",GTK_RESPONSE_ACCEPT,NULL);
96         content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
97         group   = GTK_SIZE_GROUP(gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL));
98
99         /* Add new stock symbol */
100         {
101                 int i;
102                 
103                 addbutton = hildon_picker_button_new (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
104                                                                                           HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
105
106                 addselector = hildon_touch_selector_entry_new_text();
107
108                 for(i=0; i < psContext->psSettings->uiNumTickers; i++)
109                 {
110                         hildon_touch_selector_append_text(HILDON_TOUCH_SELECTOR(addselector),psContext->psSettings->ppszTickers[i]);
111                 }
112
113                 /* First ticker in ppszTickers is the chosen one */
114                 hildon_touch_selector_set_active(HILDON_TOUCH_SELECTOR(addselector), 0, 0);
115                 
116                 hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (addbutton), HILDON_TOUCH_SELECTOR (addselector));
117                 
118                 hildon_button_set_title(HILDON_BUTTON(addbutton), "Select Ticker");
119         }
120
121         /* Set auto update time */
122         {
123                 int i;
124                 int sel = 0;
125
126                 char *aszUpdateTimes[] =
127                 {
128                         "No update",
129                         "1 minute",
130                         "15 minutes",
131                         "30 minutes",
132                         "1 hour"
133                 };
134                 
135                 timebutton = hildon_picker_button_new (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
136                                                                                           HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
137
138                 timeselector = hildon_touch_selector_new_text();
139
140                 for(i=0; i < sizeof(aszUpdateTimes)/sizeof(aszUpdateTimes[0]); i++)
141                 {
142                         hildon_touch_selector_append_text(HILDON_TOUCH_SELECTOR(timeselector),aszUpdateTimes[i]);
143                 }
144
145                 switch(psContext->psSettings->uiUpdateTime)
146                 {
147                         case 0:  sel = 0; break;
148                         case 1:  sel = 1; break;
149                         case 15: sel = 2; break;
150                         case 30: sel = 3; break;
151                         case 60: sel = 4; break;
152                 }
153                 
154                 hildon_touch_selector_set_active(HILDON_TOUCH_SELECTOR(timeselector), 0, sel);
155                 hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (timebutton), HILDON_TOUCH_SELECTOR (timeselector));
156                 hildon_button_set_title(HILDON_BUTTON(timebutton), "Automatic Update Period");
157         }
158
159         gtk_container_add(GTK_CONTAINER(content),addbutton);
160         gtk_container_add(GTK_CONTAINER(content),timebutton);
161         gtk_widget_show_all(dialog);
162
163         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) 
164         {
165                 const char *pszNewTicker;
166                 char aszOldTicker[255]={0};
167                 guint uiUpdateSelection = 0;
168
169                 /* Only perform ops if they actually changed the ticker */
170                 if(strcmp(aszOldTicker,psContext->psSettings->ppszTickers[0]))
171                 {
172                         /* Save off old ticker */
173                         strcpy(aszOldTicker,psContext->psSettings->ppszTickers[0]);
174                 
175                         /* Get new ticker */
176                         pszNewTicker = hildon_button_get_value(HILDON_BUTTON(addbutton));
177
178                         /* Check if ticker already exists */
179                         {
180                                 int i;
181                                 gboolean bFound = FALSE;
182                         
183                                 /* Check it doesn't exist already */
184                                 for(i=0; i < psContext->psSettings->uiNumTickers; i++)
185                                 {
186                                         if(!strcmp(pszNewTicker,psContext->psSettings->ppszTickers[i]))
187                                         {
188                                                 bFound = TRUE;
189                                                 break;
190                                         }
191                                 }
192
193                                 /* If they just chose another one, just swap the pointers so new one is at the top:
194                                  */
195                                 if(bFound)
196                                 {
197                                         /* Swapped ith and 0th element */
198                                         char *psTmp = psContext->psSettings->ppszTickers[i];
199                                         psContext->psSettings->ppszTickers[i] = psContext->psSettings->ppszTickers[0];
200                                         psContext->psSettings->ppszTickers[0] = psTmp;
201
202                                         DebugOut(("Swapped ticker %d (%s) with 0 (%s)",
203                                                           i,
204                                                           psContext->psSettings->ppszTickers[i],
205                                                           psContext->psSettings->ppszTickers[0]));
206                                 }
207                                 /* If it doesn't add it to the list, placing chosen one at the front */
208                                 else
209                                 {
210                                         gchar **ppszTickers = g_malloc0(sizeof(char*) * (psContext->psSettings->uiNumTickers+2));
211
212                                         /* Place the selected one first */
213                                         ppszTickers[0] = g_strdup(pszNewTicker);
214                                 
215                                         /* Copy across old tickers */
216                                         for(i=0; i < psContext->psSettings->uiNumTickers; i++)
217                                         {
218                                                 /* offset by one as the selected one is first */
219                                                 ppszTickers[i+1] = g_strdup(psContext->psSettings->ppszTickers[i]);
220                                         }
221
222                                         /* Free current array */
223                                         g_strfreev(psContext->psSettings->ppszTickers);
224
225                                         /* Assign new array */
226                                         psContext->psSettings->ppszTickers = ppszTickers;
227
228                                         /* Update count */
229                                         psContext->psSettings->uiNumTickers++;
230                                 }
231                         }
232
233                         /* Force a refresh as the ticker changed */
234                         OnClickRefresh(GTK_WIDGET(psContext->psButton),psContext);
235                 }
236
237                 uiUpdateSelection = hildon_touch_selector_get_active(HILDON_TOUCH_SELECTOR(timeselector),0);
238                 
239                 switch(uiUpdateSelection)
240                 {
241                         /* No update */
242                         case 0: psContext->psSettings->uiUpdateTime =  0; break;
243                         case 1: psContext->psSettings->uiUpdateTime =  1; break;
244                         case 2: psContext->psSettings->uiUpdateTime = 15; break;
245                         case 3: psContext->psSettings->uiUpdateTime = 30; break;
246                         case 4: psContext->psSettings->uiUpdateTime = 60; break;
247                 }
248
249                 if(psContext->psSettings->uiUpdateTime != 0)
250                 {
251                         /* Remove previous timeout if there was one */
252                         if(psContext->nTimerID != -1)
253                         {
254                                 g_source_remove(psContext->nTimerID);
255                         }
256                         
257                         /* Add timer (in seconds) */
258                         psContext->nTimerID = g_timeout_add_seconds(psContext->psSettings->uiUpdateTime * 60,
259                                                                                                                 UpdateStockOnTimeout,
260                                                                                                                 psContext);
261                 }
262
263                 /* Save Settings */
264                 stock_save_settings(psContext->psSettings);
265         }
266         
267         gtk_widget_destroy (dialog);
268 }
269
270 static GtkWidget *stock_create_gui(gchar                                *pszStockLabel,
271                                                                    StockPluginContext   *psContext)
272 {
273         HildonButton *psButton = NULL;
274
275         DebugOut(("stock_create_gui"));
276         
277         psButton = (HildonButton*)hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
278                                                                                         HILDON_BUTTON_ARRANGEMENT_VERTICAL,
279                                                                                         pszStockLabel,
280                                                                                         NULL);
281
282         psContext->psButton = psButton;
283
284         /* Setup button click signal for refreshing stocks */
285         gtk_signal_connect (GTK_OBJECT(psButton),"clicked",GTK_SIGNAL_FUNC(OnClickRefresh),(gpointer)psContext);
286
287         /* Display */
288         gtk_widget_show_all(GTK_WIDGET(psButton));
289         
290         return GTK_WIDGET(psButton);
291 }
292
293 static void
294 stock_plugin_finalize (GObject *object)
295 {
296         StockPluginContext *psContext = ((StockPlugin*)object)->context;
297
298         DebugOut(("stock_plugin_finalize"));
299         
300         if(psContext)
301         {
302                 if(psContext->psSettings)
303                 {
304                         if(psContext->psSettings)
305                                 g_free(psContext->psSettings->iD);
306                         
307                         stock_free_settings(psContext->psSettings);
308                         g_free(psContext->psSettings);
309                 }
310
311                 if(psContext->nTimerID != -1)
312                 {
313                         g_source_remove(psContext->nTimerID);
314                         psContext->nTimerID = -1;
315                 }
316
317                 if(psContext->hSG)
318                         FreeStockGetter(psContext->hSG);
319                 
320                 g_free(psContext);
321         }
322 }
323
324 static void
325 stock_plugin_init (StockPlugin *desktop_plugin)
326 {
327         StockPluginContext *psContext;
328         SGHandle hSG;
329
330         DebugOut(("stock_plugin_init"));
331         
332         hSG = InitStockGetter();
333
334         if(!hSG)
335         {
336                 return;
337         }
338
339         /* Create plugin context */
340         psContext = g_new0(StockPluginContext,1);
341
342         /* Always create a settings */
343         psContext->psSettings     = g_new0(StockPluginSettings,1);
344         psContext->psSettings->iD = g_strdup(STOCK_PLUGIN_APPLET_ID);
345
346         /* Attempt to load settings (defaulted if not present) */
347         stock_read_settings(psContext->psSettings);
348
349         /* Create GUI widget interface (and sets links in context) */
350         psContext->psBox = stock_create_gui("Click to Update",psContext);
351         
352         gtk_container_add (GTK_CONTAINER (desktop_plugin), psContext->psBox);
353         
354         psContext->hSG          = hSG;
355         psContext->nTimerID = -1;
356
357         /* Assign private data to plugin */
358         desktop_plugin->context = psContext;
359         
360         /* Remove previous timeout if there was one */
361         if(psContext->nTimerID != -1)
362         {
363                 g_source_remove(psContext->nTimerID);
364         }
365
366         /* Setup new one if required */
367         if(psContext->psSettings->uiUpdateTime > 0)
368         {
369                 psContext->nTimerID = g_timeout_add_seconds(psContext->psSettings->uiUpdateTime * 60,
370                                                                                                         UpdateStockOnTimeout,
371                                                                                                         psContext);
372         }
373
374         /* Show a settings button */
375         hd_home_plugin_item_set_settings (HD_HOME_PLUGIN_ITEM (desktop_plugin), TRUE);
376
377         /* Connect to the settings button */
378         g_signal_connect(desktop_plugin, "show-settings", G_CALLBACK(stock_show_settings_dialog), psContext);
379 }
380
381 static void
382 stock_plugin_class_init (StockPluginClass *klass)
383 {
384         G_OBJECT_CLASS(klass)->finalize = stock_plugin_finalize;
385 }
386
387 static void
388 stock_plugin_class_finalize (StockPluginClass *klass)
389 {
390 }