Added initial revision of stock widget to repo.
[stockwidget] / lib-stock-home-widget.c
diff --git a/lib-stock-home-widget.c b/lib-stock-home-widget.c
new file mode 100644 (file)
index 0000000..9f9e054
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Simple stock widget
+ * Jon Parr
+ */
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <string.h>
+#include <hildon/hildon.h>
+#include "lib-stock-home-widget.h"
+#include "lib-stock-settings.h"
+
+HD_DEFINE_PLUGIN_MODULE (StockPlugin, stock_plugin, HD_TYPE_HOME_PLUGIN_ITEM)
+
+static void
+OnClickRefresh(GtkWidget *widget, StockPluginContext *psContext)
+{
+       DebugOut(("OnClickRefresh"));
+       
+       /* Disable button while getting latest stocks */
+       gtk_widget_set_sensitive(GTK_WIDGET(psContext->psButton),FALSE);
+       {
+               float   fPrice;
+               float   fChange;
+               char    szBuffer[1024];         
+
+               if(RetrieveStockPrice(psContext->hSG,psContext->psSettings->ppszTickers[0],&fPrice,NULL,&fChange) == SG_OK)
+               {
+                       char             szTime[80];
+                       time_t           rawtime;
+                       struct tm       *timeinfo;
+
+                       time(&rawtime);
+                       timeinfo = localtime(&rawtime);
+                       strftime(szTime,80,"(%I:%M%p)",timeinfo);
+                       
+                       /* Update to latest price */
+                       sprintf(szBuffer,"%s %.2f %.2f%% %s",psContext->psSettings->ppszTickers[0],fPrice,fChange,szTime);
+
+                       hildon_button_set_title(HILDON_BUTTON(psContext->psButton),szBuffer);
+                       gtk_widget_queue_draw(GTK_WIDGET(psContext->psBox));
+               }
+               /*
+                 If it fails, leave the old price as the user can see the timestamp
+                 and know it's not up-to-date. So just notify them of the failure.
+                */
+               else
+               {
+                       hildon_banner_show_information(GTK_WIDGET(widget),NULL,"Failed to retrieve stock information, check ticker symbol and try again.");
+               }
+       }
+       gtk_widget_set_sensitive(GTK_WIDGET(psContext->psButton),TRUE);
+}
+
+static gboolean
+UpdateStockOnTimeout(gpointer data)
+{
+       StockPluginContext *psContext = (StockPluginContext*)data;
+
+       DebugOut(("UpdateStockOnTimeout"));
+       
+       if(!psContext)
+               return FALSE;
+
+       /* If no longer a timeout, don't do anything and stop */
+       if(!psContext->psSettings->uiUpdateTime)
+               return FALSE;
+
+       /* Update stock price */
+       OnClickRefresh(GTK_WIDGET(psContext->psButton),psContext);
+       return TRUE;
+}
+
+static void
+stock_show_settings_dialog(GtkWidget* widget, gpointer data)
+{
+       StockPluginContext      *psContext = (StockPluginContext*)data;
+       GtkWidget                       *dialog;
+       GtkWidget                       *content;
+       GtkSizeGroup            *group;
+       GtkWidget                       *addselector;
+       GtkWidget                       *addbutton;
+       GtkWidget                       *timeselector;
+       GtkWidget                       *timebutton;
+
+       DebugOut(("stock_show_settings_dialog"));
+       
+       /* Stop timer while settings are one */
+       if(psContext->nTimerID != -1)
+       {
+               g_source_remove(psContext->nTimerID);
+               psContext->nTimerID = -1;
+       }
+       
+       dialog  = gtk_dialog_new_with_buttons("Settings",NULL,0,"Save",GTK_RESPONSE_ACCEPT,NULL);
+       content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+       group   = GTK_SIZE_GROUP(gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL));
+
+       /* Add new stock symbol */
+       {
+               int i;
+               
+               addbutton = hildon_picker_button_new (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
+                                                                                         HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
+
+               addselector = hildon_touch_selector_entry_new_text();
+
+               for(i=0; i < psContext->psSettings->uiNumTickers; i++)
+               {
+                       hildon_touch_selector_append_text(HILDON_TOUCH_SELECTOR(addselector),psContext->psSettings->ppszTickers[i]);
+               }
+
+               /* First ticker in ppszTickers is the chosen one */
+               hildon_touch_selector_set_active(HILDON_TOUCH_SELECTOR(addselector), 0, 0);
+               
+               hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (addbutton), HILDON_TOUCH_SELECTOR (addselector));
+               
+               hildon_button_set_title(HILDON_BUTTON(addbutton), "Select Ticker");
+       }
+
+       /* Set auto update time */
+       {
+               int i;
+               int sel = 0;
+
+               char *aszUpdateTimes[] =
+               {
+                       "No update",
+                       "1 minute",
+                       "15 minutes",
+                       "30 minutes",
+                       "1 hour"
+               };
+               
+               timebutton = hildon_picker_button_new (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
+                                                                                         HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
+
+               timeselector = hildon_touch_selector_new_text();
+
+               for(i=0; i < sizeof(aszUpdateTimes)/sizeof(aszUpdateTimes[0]); i++)
+               {
+                       hildon_touch_selector_append_text(HILDON_TOUCH_SELECTOR(timeselector),aszUpdateTimes[i]);
+               }
+
+               switch(psContext->psSettings->uiUpdateTime)
+               {
+                       case 0:  sel = 0; break;
+                       case 1:  sel = 1; break;
+                       case 15: sel = 2; break;
+                       case 30: sel = 3; break;
+                       case 60: sel = 4; break;
+               }
+               
+               hildon_touch_selector_set_active(HILDON_TOUCH_SELECTOR(timeselector), 0, sel);
+               hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (timebutton), HILDON_TOUCH_SELECTOR (timeselector));
+               hildon_button_set_title(HILDON_BUTTON(timebutton), "Automatic Update Period");
+       }
+
+       gtk_container_add(GTK_CONTAINER(content),addbutton);
+       gtk_container_add(GTK_CONTAINER(content),timebutton);
+       gtk_widget_show_all(dialog);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) 
+       {
+               const char *pszNewTicker;
+               char aszOldTicker[255]={0};
+               guint uiUpdateSelection = 0;
+
+               /* Only perform ops if they actually changed the ticker */
+               if(strcmp(aszOldTicker,psContext->psSettings->ppszTickers[0]))
+               {
+                       /* Save off old ticker */
+                       strcpy(aszOldTicker,psContext->psSettings->ppszTickers[0]);
+               
+                       /* Get new ticker */
+                       pszNewTicker = hildon_button_get_value(HILDON_BUTTON(addbutton));
+
+                       /* Check if ticker already exists */
+                       {
+                               int i;
+                               gboolean bFound = FALSE;
+                       
+                               /* Check it doesn't exist already */
+                               for(i=0; i < psContext->psSettings->uiNumTickers; i++)
+                               {
+                                       if(!strcmp(pszNewTicker,psContext->psSettings->ppszTickers[i]))
+                                       {
+                                               bFound = TRUE;
+                                               break;
+                                       }
+                               }
+
+                               /* If they just chose another one, just swap the pointers so new one is at the top:
+                                */
+                               if(bFound)
+                               {
+                                       /* Swapped ith and 0th element */
+                                       char *psTmp = psContext->psSettings->ppszTickers[i];
+                                       psContext->psSettings->ppszTickers[i] = psContext->psSettings->ppszTickers[0];
+                                       psContext->psSettings->ppszTickers[0] = psTmp;
+
+                                       DebugOut(("Swapped ticker %d (%s) with 0 (%s)",
+                                                         i,
+                                                         psContext->psSettings->ppszTickers[i],
+                                                         psContext->psSettings->ppszTickers[0]));
+                               }
+                               /* If it doesn't add it to the list, placing chosen one at the front */
+                               else
+                               {
+                                       gchar **ppszTickers = g_malloc0(sizeof(char*) * (psContext->psSettings->uiNumTickers+2));
+
+                                       /* Place the selected one first */
+                                       ppszTickers[0] = g_strdup(pszNewTicker);
+                               
+                                       /* Copy across old tickers */
+                                       for(i=0; i < psContext->psSettings->uiNumTickers; i++)
+                                       {
+                                               /* offset by one as the selected one is first */
+                                               ppszTickers[i+1] = g_strdup(psContext->psSettings->ppszTickers[i]);
+                                       }
+
+                                       /* Free current array */
+                                       g_strfreev(psContext->psSettings->ppszTickers);
+
+                                       /* Assign new array */
+                                       psContext->psSettings->ppszTickers = ppszTickers;
+
+                                       /* Update count */
+                                       psContext->psSettings->uiNumTickers++;
+                               }
+                       }
+
+                       /* Force a refresh as the ticker changed */
+                       OnClickRefresh(GTK_WIDGET(psContext->psButton),psContext);
+               }
+
+               uiUpdateSelection = hildon_touch_selector_get_active(HILDON_TOUCH_SELECTOR(timeselector),0);
+               
+               switch(uiUpdateSelection)
+               {
+                       /* No update */
+                       case 0: psContext->psSettings->uiUpdateTime =  0; break;
+                       case 1: psContext->psSettings->uiUpdateTime =  1; break;
+                       case 2: psContext->psSettings->uiUpdateTime = 15; break;
+                       case 3: psContext->psSettings->uiUpdateTime = 30; break;
+                       case 4: psContext->psSettings->uiUpdateTime = 60; break;
+               }
+
+               if(psContext->psSettings->uiUpdateTime != 0)
+               {
+                       /* Remove previous timeout if there was one */
+                       if(psContext->nTimerID != -1)
+                       {
+                               g_source_remove(psContext->nTimerID);
+                       }
+                       
+                       /* Add timer (in seconds) */
+                       psContext->nTimerID = g_timeout_add_seconds(psContext->psSettings->uiUpdateTime * 60,
+                                                                                                               UpdateStockOnTimeout,
+                                                                                                               psContext);
+               }
+
+               /* Save Settings */
+               stock_save_settings(psContext->psSettings);
+       }
+       
+       gtk_widget_destroy (dialog);
+}
+
+static GtkWidget *stock_create_gui(gchar                               *pszStockLabel,
+                                                                  StockPluginContext   *psContext)
+{
+       HildonButton *psButton = NULL;
+
+       DebugOut(("stock_create_gui"));
+       
+       psButton = (HildonButton*)hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
+                                                                                       HILDON_BUTTON_ARRANGEMENT_VERTICAL,
+                                                                                       pszStockLabel,
+                                                                                       NULL);
+
+       psContext->psButton = psButton;
+
+       /* Setup button click signal for refreshing stocks */
+       gtk_signal_connect (GTK_OBJECT(psButton),"clicked",GTK_SIGNAL_FUNC(OnClickRefresh),(gpointer)psContext);
+
+       /* Display */
+       gtk_widget_show_all(GTK_WIDGET(psButton));
+       
+       return GTK_WIDGET(psButton);
+}
+
+static void
+stock_plugin_finalize (GObject *object)
+{
+       StockPluginContext *psContext = ((StockPlugin*)object)->context;
+
+       DebugOut(("stock_plugin_finalize"));
+       
+       if(psContext)
+       {
+               if(psContext->psSettings)
+               {
+                       if(psContext->psSettings)
+                               g_free(psContext->psSettings->iD);
+                       
+                       stock_free_settings(psContext->psSettings);
+                       g_free(psContext->psSettings);
+               }
+
+               if(psContext->nTimerID != -1)
+               {
+                       g_source_remove(psContext->nTimerID);
+                       psContext->nTimerID = -1;
+               }
+
+               if(psContext->hSG)
+                       FreeStockGetter(psContext->hSG);
+               
+               g_free(psContext);
+       }
+}
+
+static void
+stock_plugin_init (StockPlugin *desktop_plugin)
+{
+       StockPluginContext *psContext;
+       SGHandle hSG;
+
+       DebugOut(("stock_plugin_init"));
+       
+       hSG = InitStockGetter();
+
+       if(!hSG)
+       {
+               return;
+       }
+
+       /* Create plugin context */
+       psContext = g_new0(StockPluginContext,1);
+
+       /* Always create a settings */
+       psContext->psSettings     = g_new0(StockPluginSettings,1);
+       psContext->psSettings->iD = g_strdup(STOCK_PLUGIN_APPLET_ID);
+
+       /* Attempt to load settings (defaulted if not present) */
+       stock_read_settings(psContext->psSettings);
+
+       /* Create GUI widget interface (and sets links in context) */
+       psContext->psBox = stock_create_gui("Click to Update",psContext);
+       
+       gtk_container_add (GTK_CONTAINER (desktop_plugin), psContext->psBox);
+       
+       psContext->hSG          = hSG;
+       psContext->nTimerID = -1;
+
+       /* Assign private data to plugin */
+       desktop_plugin->context = psContext;
+       
+       /* Remove previous timeout if there was one */
+       if(psContext->nTimerID != -1)
+       {
+               g_source_remove(psContext->nTimerID);
+       }
+
+       /* Setup new one if required */
+       if(psContext->psSettings->uiUpdateTime > 0)
+       {
+               psContext->nTimerID = g_timeout_add_seconds(psContext->psSettings->uiUpdateTime * 60,
+                                                                                                       UpdateStockOnTimeout,
+                                                                                                       psContext);
+       }
+
+       /* Show a settings button */
+       hd_home_plugin_item_set_settings (HD_HOME_PLUGIN_ITEM (desktop_plugin), TRUE);
+
+       /* Connect to the settings button */
+       g_signal_connect(desktop_plugin, "show-settings", G_CALLBACK(stock_show_settings_dialog), psContext);
+}
+
+static void
+stock_plugin_class_init (StockPluginClass *klass)
+{
+       G_OBJECT_CLASS(klass)->finalize = stock_plugin_finalize;
+}
+
+static void
+stock_plugin_class_finalize (StockPluginClass *klass)
+{
+}