9 #include <hildon/hildon.h>
10 #include "lib-stock-home-widget.h"
11 #include "lib-stock-settings.h"
13 HD_DEFINE_PLUGIN_MODULE (StockPlugin, stock_plugin, HD_TYPE_HOME_PLUGIN_ITEM)
16 OnClickUpdate(GtkWidget *widget, GdkEventButton *event, StockPluginContext *psContext)
20 DebugOut(("OnClickUpdate"));
22 sprintf(szBuffer,"Retrieving <span foreground=\"red\">%s</span>...",psContext->psSettings->ppszTickers[0]);
24 /* Hide the stock arrow when we're updating */
25 psContext->bArrowActive = FALSE;
26 gtk_widget_hide(GTK_WIDGET(psContext->psArrow));
28 gtk_label_set_markup(GTK_LABEL(psContext->psLabel),szBuffer);
29 gtk_widget_queue_draw(GTK_WIDGET(psContext->psParent));
33 OnClickRefresh(GtkWidget *widget, GdkEventButton *event, StockPluginContext *psContext)
35 DebugOut(("OnClickRefresh"));
37 /* Disable button while getting latest stocks */
43 if(RetrieveStockPrice(psContext->hSG,psContext->psSettings->ppszTickers[0],&fPrice,NULL,&fChange) == SG_OK)
45 char *pszColour = NULL;
52 timeinfo = localtime(&rawtime);
53 strftime(szTime,80,"(%I:%M%p)",timeinfo);
55 /* Highlight the change in price */
58 nImageSel = STOCK_IMAGE_INCREASE;
63 nImageSel = STOCK_IMAGE_DECREASE;
67 gtk_image_set_from_pixbuf(GTK_IMAGE(psContext->psArrow),psContext->asStockImage[nImageSel]);
69 if(!psContext->bArrowActive)
71 gtk_widget_show(psContext->psArrow);
72 psContext->bArrowActive = TRUE;
75 /* Update to latest price */
77 "%s %.2f <span foreground=\"%s\">%.2f%%</span> <span foreground=\"grey\">%s</span>",
78 psContext->psSettings->ppszTickers[0],fPrice,pszColour,fChange,szTime);
80 gtk_label_set_markup(GTK_LABEL(psContext->psLabel),szBuffer);
81 gtk_widget_queue_draw(GTK_WIDGET(psContext->psParent));
84 If it fails, leave the old price as the user can see the timestamp
85 and know it's not up-to-date. So just notify them of the failure.
89 gtk_label_set_markup(GTK_LABEL(psContext->psLabel),"<span foreground=\"red\"><b>Failed</b></span>: Check ticker and try again.");
90 gtk_widget_queue_draw(GTK_WIDGET(psContext->psParent));
96 UpdateStockOnTimeout(gpointer data)
98 StockPluginContext *psContext = (StockPluginContext*)data;
100 DebugOut(("UpdateStockOnTimeout"));
105 /* If no longer a timeout, don't do anything and stop */
106 if(!psContext->psSettings->uiUpdateTime)
109 /* Update stock price */
110 OnClickRefresh(GTK_WIDGET(psContext->psLabel),NULL,psContext);
115 stock_show_settings_dialog(GtkWidget* widget, gpointer data)
117 StockPluginContext *psContext = (StockPluginContext*)data;
121 GtkWidget *addselector;
122 GtkWidget *addbutton;
123 GtkWidget *timeselector;
124 GtkWidget *timebutton;
126 DebugOut(("stock_show_settings_dialog"));
128 /* Stop timer while settings are one */
129 if(psContext->nTimerID != -1)
131 g_source_remove(psContext->nTimerID);
132 psContext->nTimerID = -1;
135 dialog = gtk_dialog_new_with_buttons("Settings",NULL,0,"Save",GTK_RESPONSE_ACCEPT,NULL);
136 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
137 group = GTK_SIZE_GROUP(gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL));
139 /* Add new stock symbol */
143 addbutton = hildon_picker_button_new (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
144 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
146 addselector = hildon_touch_selector_entry_new_text();
148 for(i=0; i < psContext->psSettings->uiNumTickers; i++)
150 hildon_touch_selector_append_text(HILDON_TOUCH_SELECTOR(addselector),psContext->psSettings->ppszTickers[i]);
153 /* First ticker in ppszTickers is the chosen one */
154 hildon_touch_selector_set_active(HILDON_TOUCH_SELECTOR(addselector), 0, 0);
156 hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (addbutton), HILDON_TOUCH_SELECTOR (addselector));
158 hildon_button_set_title(HILDON_BUTTON(addbutton), "Select Ticker");
161 /* Set auto update time */
166 char *aszUpdateTimes[] =
175 timebutton = hildon_picker_button_new (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
176 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
178 timeselector = hildon_touch_selector_new_text();
180 for(i=0; i < sizeof(aszUpdateTimes)/sizeof(aszUpdateTimes[0]); i++)
182 hildon_touch_selector_append_text(HILDON_TOUCH_SELECTOR(timeselector),aszUpdateTimes[i]);
185 switch(psContext->psSettings->uiUpdateTime)
187 case 0: sel = 0; break;
188 case 1: sel = 1; break;
189 case 15: sel = 2; break;
190 case 30: sel = 3; break;
191 case 60: sel = 4; break;
194 hildon_touch_selector_set_active(HILDON_TOUCH_SELECTOR(timeselector), 0, sel);
195 hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (timebutton), HILDON_TOUCH_SELECTOR (timeselector));
196 hildon_button_set_title(HILDON_BUTTON(timebutton), "Automatic Update Period");
199 gtk_container_add(GTK_CONTAINER(content),addbutton);
200 gtk_container_add(GTK_CONTAINER(content),timebutton);
201 gtk_widget_show_all(dialog);
203 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
205 const char *pszNewTicker;
206 char aszOldTicker[255]={0};
207 guint uiUpdateSelection = 0;
209 /* Only perform ops if they actually changed the ticker */
210 if(strcmp(aszOldTicker,psContext->psSettings->ppszTickers[0]))
212 /* Save off old ticker */
213 strcpy(aszOldTicker,psContext->psSettings->ppszTickers[0]);
216 pszNewTicker = hildon_button_get_value(HILDON_BUTTON(addbutton));
218 /* Check if ticker already exists */
221 gboolean bFound = FALSE;
223 /* Check it doesn't exist already */
224 for(i=0; i < psContext->psSettings->uiNumTickers; i++)
226 if(!strcmp(pszNewTicker,psContext->psSettings->ppszTickers[i]))
233 /* If they just chose another one, just swap the pointers so new one is at the top:
237 /* Swapped ith and 0th element */
238 char *psTmp = psContext->psSettings->ppszTickers[i];
239 psContext->psSettings->ppszTickers[i] = psContext->psSettings->ppszTickers[0];
240 psContext->psSettings->ppszTickers[0] = psTmp;
242 DebugOut(("Swapped ticker %d (%s) with 0 (%s)",
244 psContext->psSettings->ppszTickers[i],
245 psContext->psSettings->ppszTickers[0]));
247 /* If it doesn't add it to the list, placing chosen one at the front */
250 gchar **ppszTickers = g_malloc0(sizeof(char*) * (psContext->psSettings->uiNumTickers+2));
252 /* Place the selected one first */
253 ppszTickers[0] = g_strdup(pszNewTicker);
255 /* Copy across old tickers */
256 for(i=0; i < psContext->psSettings->uiNumTickers; i++)
258 /* offset by one as the selected one is first */
259 ppszTickers[i+1] = g_strdup(psContext->psSettings->ppszTickers[i]);
262 /* Free current array */
263 g_strfreev(psContext->psSettings->ppszTickers);
265 /* Assign new array */
266 psContext->psSettings->ppszTickers = ppszTickers;
269 psContext->psSettings->uiNumTickers++;
273 /* Force a refresh as the ticker changed */
274 OnClickRefresh(GTK_WIDGET(psContext->psLabel),NULL,psContext);
277 uiUpdateSelection = hildon_touch_selector_get_active(HILDON_TOUCH_SELECTOR(timeselector),0);
279 switch(uiUpdateSelection)
282 case 0: psContext->psSettings->uiUpdateTime = 0; break;
283 case 1: psContext->psSettings->uiUpdateTime = 1; break;
284 case 2: psContext->psSettings->uiUpdateTime = 15; break;
285 case 3: psContext->psSettings->uiUpdateTime = 30; break;
286 case 4: psContext->psSettings->uiUpdateTime = 60; break;
289 if(psContext->psSettings->uiUpdateTime != 0)
291 /* Remove previous timeout if there was one */
292 if(psContext->nTimerID != -1)
294 g_source_remove(psContext->nTimerID);
297 /* Add timer (in seconds) */
298 psContext->nTimerID = g_timeout_add_seconds(psContext->psSettings->uiUpdateTime * 60,
299 UpdateStockOnTimeout,
304 stock_save_settings(psContext->psSettings);
307 gtk_widget_destroy (dialog);
310 static GtkWidget *stock_create_gui(gchar *pszStockLabel,
311 StockPluginContext *psContext)
318 DebugOut(("stock_create_gui"));
320 psContext->asStockImage[STOCK_IMAGE_INCREASE] = gdk_pixbuf_new_from_file(STOCK_PLUGIN_RESOURCE_UP_ARROW, NULL);
321 psContext->asStockImage[STOCK_IMAGE_DECREASE] = gdk_pixbuf_new_from_file(STOCK_PLUGIN_RESOURCE_DOWN_ARROW, NULL);
323 label = gtk_label_new(pszStockLabel);
324 eventbox = gtk_event_box_new();
325 arrow = gtk_image_new_from_pixbuf(psContext->asStockImage[0]);
326 hbox = gtk_hbox_new(FALSE,5);
328 gtk_event_box_set_visible_window(GTK_EVENT_BOX(eventbox),FALSE);
329 gtk_container_set_border_width(GTK_CONTAINER(eventbox),10);
331 gtk_box_pack_start(GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
332 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
334 gtk_container_add(GTK_CONTAINER(eventbox),hbox);
336 g_signal_connect(GTK_CONTAINER(eventbox),"button-press-event",G_CALLBACK(OnClickUpdate),(gpointer)psContext);
337 g_signal_connect(GTK_CONTAINER(eventbox),"button-release-event",G_CALLBACK(OnClickRefresh),(gpointer)psContext);
339 /* Display all but the arrow */
340 gtk_widget_show_all(eventbox);
341 gtk_widget_hide(arrow);
342 psContext->bArrowActive = FALSE;
344 psContext->psArrow = arrow;
345 psContext->psLabel = label;
346 psContext->psEventBox = eventbox;
352 stock_plugin_finalize (GObject *object)
354 StockPluginContext *psContext = ((StockPlugin*)object)->context;
356 DebugOut(("stock_plugin_finalize"));
360 if(psContext->psSettings)
362 if(psContext->psSettings)
363 g_free(psContext->psSettings->iD);
365 stock_free_settings(psContext->psSettings);
366 g_free(psContext->psSettings);
369 if(psContext->nTimerID != -1)
371 g_source_remove(psContext->nTimerID);
372 psContext->nTimerID = -1;
375 if(psContext->asStockImage[STOCK_IMAGE_DECREASE] != NULL)
376 g_object_unref(psContext->asStockImage[STOCK_IMAGE_DECREASE]);
378 if(psContext->asStockImage[STOCK_IMAGE_INCREASE] != NULL)
379 g_object_unref(psContext->asStockImage[STOCK_IMAGE_INCREASE]);
382 FreeStockGetter(psContext->hSG);
389 stock_plugin_init (StockPlugin *desktop_plugin)
391 StockPluginContext *psContext;
394 DebugOut(("stock_plugin_init"));
396 hSG = InitStockGetter();
403 /* Create plugin context */
404 psContext = g_new0(StockPluginContext,1);
406 /* Always create a settings */
407 psContext->psSettings = g_new0(StockPluginSettings,1);
408 psContext->psSettings->iD = g_strdup(STOCK_PLUGIN_APPLET_ID);
410 /* Attempt to load settings (defaulted if not present) */
411 stock_read_settings(psContext->psSettings);
413 /* Create GUI widget interface (and sets links in context) */
414 stock_create_gui("Click to Update",psContext);
416 gtk_container_add(GTK_CONTAINER(desktop_plugin), psContext->psEventBox);
418 psContext->hSG = hSG;
419 psContext->nTimerID = -1;
420 psContext->psParent = GTK_WIDGET(desktop_plugin);
422 /* Assign private data to plugin */
423 desktop_plugin->context = psContext;
425 /* Remove previous timeout if there was one */
426 if(psContext->nTimerID != -1)
428 g_source_remove(psContext->nTimerID);
431 /* Setup new one if required */
432 if(psContext->psSettings->uiUpdateTime > 0)
434 psContext->nTimerID = g_timeout_add_seconds(psContext->psSettings->uiUpdateTime * 60,
435 UpdateStockOnTimeout,
439 /* Show a settings button */
440 hd_home_plugin_item_set_settings (HD_HOME_PLUGIN_ITEM (desktop_plugin), TRUE);
442 /* Connect to the settings button */
443 g_signal_connect(desktop_plugin, "show-settings", G_CALLBACK(stock_show_settings_dialog), psContext);
447 stock_plugin_realize(GtkWidget* widget)
449 GdkScreen *screen = gtk_widget_get_screen(widget);
450 gtk_widget_set_colormap(widget, gdk_screen_get_rgba_colormap(screen));
451 gtk_widget_set_app_paintable(widget, TRUE);
452 GTK_WIDGET_CLASS(stock_plugin_parent_class)->realize(widget);
456 cairo_rounded_rectangle(cairo_t *cr,
463 cairo_pattern_t *pat;
466 double corner_radius = height / corner_div;
467 double radius = corner_radius / aspect;
468 double degrees = 3.14159265 / 180.0;
470 cairo_new_sub_path (cr);
471 cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees);
472 cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees);
473 cairo_arc (cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees);
474 cairo_arc (cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
475 cairo_close_path (cr);
477 pat = cairo_pattern_create_linear(0.0, 0.0, 0.0, height);
479 cairo_pattern_add_color_stop_rgba(pat,0.0, 0.8, 0.8, 0.8, 0.7);
480 cairo_pattern_add_color_stop_rgba(pat,1.0, 0.0, 0.0, 0.0, 0.7);
482 cairo_set_source(cr,pat);
483 cairo_fill_preserve (cr);
485 cairo_set_line_width (cr, 0.0);
490 stock_plugin_expose(GtkWidget* widget, GdkEventExpose *event)
494 cr = gdk_cairo_create(GDK_DRAWABLE(widget->window));
496 cairo_rounded_rectangle(cr, 0, 0, event->area.width, event->area.height, 10.0);
499 return GTK_WIDGET_CLASS(stock_plugin_parent_class)->expose_event(widget, event);
503 stock_plugin_class_init (StockPluginClass *klass)
505 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
506 widget_class->realize = stock_plugin_realize;
507 widget_class->expose_event = stock_plugin_expose;
508 G_OBJECT_CLASS(klass)->finalize = stock_plugin_finalize;
512 stock_plugin_class_finalize (StockPluginClass *klass)