1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
3 * Copyright (C) 2009 Artem Garmash. All rights reserved.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 * Contact: Artem Garmash <artemgarmash@gmail.com>
24 #include "el-home-applet.h"
26 #include <hildon/hildon.h>
27 #include <rtcom-eventlogger/eventlogger.h>
31 #define EL_HOME_APPLET_GET_PRIVATE(obj) ( \
32 G_TYPE_INSTANCE_GET_PRIVATE (obj, \
33 EL_TYPE_HOME_APPLET, ELHomeAppletPrivate))
36 #define BOX_HEIGHT 256
38 #define C_WIDTH (BOX_WIDTH - 2*HILDON_MARGIN_HALF)
39 #define C_HEIGHT (BOX_HEIGHT - 2*HILDON_MARGIN_HALF)
40 #define C_X HILDON_MARGIN_HALF
41 #define C_Y HILDON_MARGIN_HALF
43 #define HEADER_HEIGHT 48
44 #define MESSAGE_HEIGHT (C_HEIGHT - HEADER_HEIGHT)
45 #define MESSAGE_WIDTH (C_WIDTH - 2*HILDON_MARGIN_DEFAULT)
47 #define BOX_RADIOUS 10
51 struct _ELHomeAppletPrivate
68 const gchar *current_font;
72 gboolean scroll_on_click;
77 HD_DEFINE_PLUGIN_MODULE (ELHomeApplet, el_home_applet, HD_TYPE_HOME_PLUGIN_ITEM);
79 const gchar* g_module_check_init(GModule *module);
81 g_module_check_init(GModule *module)
83 g_module_make_resident (module);
88 el_home_applet_class_finalize (ELHomeAppletClass *klass)
93 el_home_applet_realize (GtkWidget *widget)
97 screen = gtk_widget_get_screen (widget);
98 gtk_widget_set_colormap (widget,
99 gdk_screen_get_rgba_colormap (screen));
101 gtk_widget_set_app_paintable (widget,
104 GTK_WIDGET_CLASS (el_home_applet_parent_class)->realize (widget);
108 * Thanks http://cairographics.org/cookbook/roundedrectangles/
111 rounded_rectangle (cairo_t *cr,
118 cairo_move_to (cr, x + r, y);
119 cairo_line_to (cr, x + w - r, y);
120 cairo_curve_to (cr, x + w, y,
123 cairo_line_to (cr, x + w, y + h - r);
124 cairo_curve_to (cr, x + w, y + h,
127 cairo_line_to (cr, x + r, y + h);
128 cairo_curve_to (cr, x, y + h,
131 cairo_line_to (cr, x, y + r);
132 cairo_curve_to (cr, x, y,
138 draw_text (cairo_t *cr,
147 PangoFontDescription *desc;
152 x, y, width, height);
155 /* Create a PangoLayout, set the font and text */
156 layout = pango_cairo_create_layout (cr);
157 pango_layout_set_text (layout,
160 desc = pango_font_description_from_string ("Sans 17");
161 pango_layout_set_font_description (layout, desc);
162 pango_font_description_free (desc);
164 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
166 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
167 pango_layout_set_width (layout, PANGO_SCALE*width);
168 pango_layout_set_height (layout, PANGO_SCALE*height);
171 cairo_move_to (cr, x + 1, y + 1 - offset);
172 cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.8);
173 pango_cairo_show_layout (cr, layout);
176 cairo_move_to (cr, x, y - offset);
177 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
178 pango_cairo_show_layout (cr, layout);
180 result = !offset && pango_layout_is_ellipsized (layout);
182 g_object_unref (layout);
189 expose_event (GtkWidget *self, GdkEventExpose *event)
191 ELHomeAppletPrivate *priv = EL_HOME_APPLET(self)->priv;
195 float red, green, blue;
197 /* find theme active color */
198 gtk_style_lookup_color (self->style, "ActiveTextColor", &color);
199 red = color.red/(float)G_MAXUINT16;
200 green = color.green/(float)G_MAXUINT16;
201 blue = color.blue/(float)G_MAXUINT16;
203 cr = gdk_cairo_create (self->window);
204 gdk_cairo_region (cr, event->region);
208 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
210 cairo_set_source_rgba (cr, 0.4f, 0.4f, 0.4f, 0.1f);
211 cairo_set_line_width (cr, 3.0f);
213 rounded_rectangle (cr,
220 cairo_close_path(cr);
224 cairo_set_line_width (cr, 1.0f);
226 cairo_translate (cr, C_X, C_Y);
227 cairo_move_to (cr, 0, HEADER_HEIGHT);
228 cairo_line_to (cr, 0, BOX_RADIOUS);
229 cairo_curve_to (cr, 0, 0, 0, 0, BOX_RADIOUS, 0);
230 cairo_line_to (cr, C_WIDTH - BOX_RADIOUS, 0);
231 cairo_curve_to (cr, C_WIDTH, 0, C_WIDTH, 0, C_WIDTH, BOX_RADIOUS);
232 cairo_line_to (cr, C_WIDTH, HEADER_HEIGHT);
233 cairo_line_to (cr, 0, HEADER_HEIGHT);
235 cairo_close_path(cr);
237 cairo_set_source_rgba (cr, 0.2f, 0.2f, 0.2f, 0.8f);
238 cairo_fill_preserve (cr);
239 cairo_set_source_rgba (cr, red, green, blue, 1.0f);
243 cairo_move_to (cr, 0, HEADER_HEIGHT);
244 cairo_line_to (cr, 0, C_HEIGHT - BOX_RADIOUS);
245 cairo_curve_to (cr, 0, C_HEIGHT, 0, C_HEIGHT, BOX_RADIOUS, C_HEIGHT);
246 cairo_line_to (cr, C_WIDTH - BOX_RADIOUS, C_HEIGHT);
247 cairo_curve_to (cr, C_WIDTH, C_HEIGHT, C_WIDTH, C_HEIGHT, C_WIDTH, C_HEIGHT - BOX_RADIOUS);
248 cairo_line_to (cr, C_WIDTH, HEADER_HEIGHT);
249 cairo_line_to (cr, 0, HEADER_HEIGHT);
250 cairo_close_path(cr);
252 /* draw body filling depending on (in)active state */
253 cairo_pattern_t *grad;
254 grad = cairo_pattern_create_linear(0, HEADER_HEIGHT,
258 cairo_pattern_add_color_stop_rgba (grad, 0.5f,
259 red, green, blue, 0.8f);
260 cairo_pattern_add_color_stop_rgba (grad, 1.0f,
261 red/2, green/2, blue/2, 0.8f);
264 cairo_pattern_add_color_stop_rgba (grad, 0.5f,
265 0.4f, 0.4f, 0.4f, 0.8f);
266 cairo_pattern_add_color_stop_rgba (grad, 1.0f,
267 0.2f, 0.2f, 0.2f, 0.8f);
269 cairo_set_source (cr, grad);
272 /* cairo_set_source_rgba (cr, red, green, blue, 1.0f); */
273 /* cairo_translate (cr, -C_X, -C_Y); */
274 /* rounded_rectangle (cr, */
277 /* BOX_WIDTH - 2*C_X, */
278 /* BOX_HEIGHT - 2*C_Y, */
280 /* cairo_close_path(cr); */
281 /* cairo_stroke (cr); */
285 ellipsized = draw_text (cr, priv->message,
286 2*C_X, HEADER_HEIGHT,
288 MESSAGE_HEIGHT - 2*C_Y,
289 priv->scroll_offset);
290 if (!priv->scroll_offset && !priv->active)
291 priv->scroll_on_click = ellipsized;
293 cairo_pattern_destroy (grad);
296 return GTK_WIDGET_CLASS (el_home_applet_parent_class)->expose_event (self, event);
300 stop_scroll_anim (ELHomeAppletPrivate *priv)
302 priv->scroll_on_click = FALSE;
303 priv->scroll_offset = 0;
304 if (priv->scroll_anim_id > 0) {
305 g_source_remove (priv->scroll_anim_id);
306 priv->scroll_anim_id = 0;
311 dispose (GObject *self)
313 ELHomeAppletPrivate *priv = EL_HOME_APPLET(self)->priv;
315 stop_scroll_anim (priv);
317 g_source_remove (priv->idle_id);
320 if (priv->eventlogger){
321 g_object_unref (priv->eventlogger);
322 priv->eventlogger = NULL;
326 g_free (priv->message);
327 priv->message = NULL;
330 G_OBJECT_CLASS (el_home_applet_parent_class)->dispose (self);
334 finalize (GObject *self)
336 G_OBJECT_CLASS (el_home_applet_parent_class)->finalize (self);
340 format_time (time_t t)
342 static const guint RESULT_SIZE = 32;
345 struct tm now_tm, t_tm;
346 const gchar *format = "%Y.%m.%d %T";
347 gchar *result = g_malloc0 (RESULT_SIZE);
350 localtime_r (&now, &now_tm);
351 localtime_r (&t, &t_tm);
353 if ((now_tm.tm_year == t_tm.tm_year) &&
354 (now_tm.tm_mon == t_tm.tm_mon) &&
355 (now_tm.tm_mday == t_tm.tm_mday))
358 strftime (result, RESULT_SIZE, format, &t_tm);
364 show_event (ELHomeApplet *self, RTComElIter *it)
366 ELHomeAppletPrivate *priv = self->priv;
368 gchar *remote = NULL;
369 gchar *received = NULL;
370 const gchar *icon_name = NULL;
373 g_free (priv->message);
374 priv->message = NULL;
377 if (it && rtcom_el_iter_first (it)){
378 rtcom_el_iter_dup_string (it, "free-text", &priv->message);
380 const gchar *service;
383 rtcom_el_iter_get_int (it, "id", &priv->event_id);
384 if (rtcom_el_iter_get_int (it, "start-time", (gint*)&received_t))
385 received = format_time (received_t);
387 if(!rtcom_el_iter_dup_string (it, "remote-name", &remote))
388 rtcom_el_iter_dup_string (it, "remote-id", &remote);
389 service = rtcom_el_iter_get_service (it);
390 if (!g_strcmp0 (service, "RTCOM_EL_SERVICE_SMS"))
391 icon_name = "chat_unread_sms";
392 else if (!g_strcmp0 (service, "RTCOM_EL_SERVICE_CHAT"))
393 icon_name = "chat_unread_chat";
401 gtk_widget_hide (priv->empty);
403 gtk_widget_show (priv->empty);
405 gtk_label_set_text (GTK_LABEL (priv->sender), remote);
406 gtk_label_set_text (GTK_LABEL (priv->received), received);
409 const gchar *current_icon_name;
410 gtk_image_get_icon_name (GTK_IMAGE (priv->icon),
413 if (g_strcmp0 (current_icon_name, icon_name))
414 gtk_image_set_from_icon_name (GTK_IMAGE (priv->icon),
416 HILDON_ICON_SIZE_FINGER);
417 gtk_widget_show (priv->icon);
420 gtk_widget_hide (priv->icon);
424 gtk_widget_queue_draw (GTK_WIDGET (self));
428 make_query (RTComEl *el, gint event_id)
430 RTComElQuery *query = NULL;
431 RTComElIter *it = NULL;
433 static const gchar *services[] = {"RTCOM_EL_SERVICE_SMS",
434 "RTCOM_EL_SERVICE_CHAT",
436 static const gchar *event_types[] = {"RTCOM_EL_EVENTTYPE_SMS_INBOUND",
437 "RTCOM_EL_EVENTTYPE_CHAT_INBOUND",
440 query = rtcom_el_query_new (el);
441 rtcom_el_query_set_limit (query, 1);
443 rtcom_el_query_prepare (query,
444 "is-read", FALSE, RTCOM_EL_OP_EQUAL,
445 "id", event_id, RTCOM_EL_OP_EQUAL,
446 "service", services, RTCOM_EL_OP_IN_STRV,
447 "event-type", event_types, RTCOM_EL_OP_IN_STRV,
451 rtcom_el_query_prepare (query,
452 "is-read", FALSE, RTCOM_EL_OP_EQUAL,
453 "service", services, RTCOM_EL_OP_IN_STRV,
454 "event-type", event_types, RTCOM_EL_OP_IN_STRV,
457 it = rtcom_el_get_events (el, query);
458 g_object_unref(query);
464 update_unread_label (ELHomeApplet *self)
466 ELHomeAppletPrivate *priv = self->priv;
469 if (priv->unread_count > 0){
470 text = g_strdup_printf ("%d", priv->unread_count);
471 gtk_label_set_text (GTK_LABEL (priv->unread), text);
475 gtk_label_set_text (GTK_LABEL (priv->unread), NULL);
479 query_unread_events (RTComEl *el)
486 g_object_get (el, "db", &db, NULL);
488 if (sqlite3_prepare_v2 (db,
489 "SELECT SUM(total_events)-SUM(read_events) FROM GroupCache;",
493 g_error ("%s: can't compile SQL", G_STRFUNC);
497 while (SQLITE_BUSY == (ret = sqlite3_step (stmt)));
499 if (ret == SQLITE_ROW){
500 count = sqlite3_column_int (stmt, 0);
503 g_error ("%s: error while executing SQL", G_STRFUNC);
506 sqlite3_finalize (stmt);
512 read_event (ELHomeApplet *self)
514 ELHomeAppletPrivate *priv = self->priv;
515 RTComElIter *it = NULL;
517 it = make_query (priv->eventlogger, -1);
518 show_event (self, it);
519 if (it) g_object_unref (it);
523 mark_as_read (ELHomeApplet *self)
525 ELHomeAppletPrivate *priv = self->priv;
527 if (priv->event_id >= 0){
528 rtcom_el_set_read_event (priv->eventlogger,
533 priv->unread_count--;
534 update_unread_label (self);
539 read_new_event (ELHomeApplet *self)
541 ELHomeAppletPrivate *priv = self->priv;
544 priv->unread_count = query_unread_events (priv->eventlogger);
545 update_unread_label (self);
553 add_new_idle (ELHomeApplet *self)
555 ELHomeAppletPrivate *priv = self->priv;
558 g_source_remove (priv->idle_id);
559 priv->idle_id = g_idle_add ((GSourceFunc)read_new_event,
564 new_event_cb (RTComEl *backend,
566 const gchar *local_uid,
567 const gchar *remote_uid,
568 const gchar *remote_ebook_uid,
569 const gchar *group_uid,
570 const gchar *service,
577 scroll_anim_cb (ELHomeApplet *self)
579 ELHomeAppletPrivate *priv = self->priv;
581 priv->scroll_offset += 1;
582 gtk_widget_queue_draw (self);
588 button_press_event_cb (GtkWidget *widget,
589 GdkEventButton *event,
592 ELHomeAppletPrivate *priv = self->priv;
594 if (priv->event_id > 0){
596 gtk_widget_queue_draw (widget);
599 if (priv->scroll_on_click) {
600 stop_scroll_anim (priv);
601 priv->scroll_anim_id = g_timeout_add (100,
606 gtk_widget_queue_draw (widget);
611 button_release_event_cb (GtkWidget *widget,
612 GdkEventButton *event,
615 ELHomeAppletPrivate *priv = self->priv;
618 priv->active = FALSE;
619 /* stop_scroll_anim (priv); */
623 gtk_widget_queue_draw (widget);
630 leave_notify_event_cb (GtkWidget *widget,
631 GdkEventCrossing *event,
634 ELHomeAppletPrivate *priv = self->priv;
637 priv->active = FALSE;
638 stop_scroll_anim (priv);
639 gtk_widget_queue_draw (widget);
646 el_home_applet_init (ELHomeApplet *self)
648 ELHomeAppletPrivate *priv;
649 GtkWidget *event_box;
650 GtkWidget *hbox, *vbox, *align;
652 self->priv = EL_HOME_APPLET_GET_PRIVATE (self);
655 gtk_widget_set_app_paintable (GTK_WIDGET (self), TRUE);
657 priv->unread = gtk_label_new ("12");
658 hildon_helper_set_logical_color (priv->unread,
662 gtk_misc_set_alignment (GTK_MISC (priv->unread),
665 gtk_widget_set_size_request (priv->unread,
669 priv->icon = gtk_image_new_from_icon_name ("chat_unread_sms",
670 HILDON_ICON_SIZE_FINGER);
671 gtk_misc_set_alignment (GTK_MISC (priv->icon),
675 priv->sender = gtk_label_new ("asdf asdf asdf asdf asdf");
676 gtk_misc_set_alignment (GTK_MISC (priv->sender),
679 gtk_label_set_ellipsize (GTK_LABEL (priv->sender),
680 PANGO_ELLIPSIZE_END);
681 gtk_widget_set_name (priv->sender, "hildon-shadow-label");
682 hildon_helper_set_logical_font (priv->sender, "SystemFont");
684 priv->message = g_strdup ("One two three four five six seven eight nine ten"
685 "one two three four five six seven eight nine ten"
686 "one two three four five six seven eight nine ten"
687 "one two three four five six seven eight nine ten"
688 "one two three four five six seven eight nine ten"
689 "one two three four five six seven eight nine ten");
692 priv->empty = gtk_label_new ("No new messages");
693 gtk_widget_set_name (priv->empty, "hildon-shadow-label");
694 GTK_WIDGET_SET_FLAGS (priv->empty, GTK_NO_SHOW_ALL);
696 priv->received = gtk_label_new ("aewf aewf aewf awef");
697 gtk_misc_set_alignment (GTK_MISC (priv->received),
700 gtk_widget_set_size_request (priv->received,
703 hildon_helper_set_logical_font (priv->received, "SmallSystemFont");
704 gtk_widget_set_name (priv->received, "hildon-shadow-label");
706 hbox = gtk_hbox_new (FALSE, 0);
707 gtk_box_pack_start (GTK_BOX (hbox), priv->unread, FALSE, FALSE, 0);
708 gtk_box_pack_start (GTK_BOX (hbox), priv->icon, FALSE, FALSE, 0);
709 gtk_box_pack_start (GTK_BOX (hbox), priv->sender, TRUE, TRUE, 0);
711 vbox = gtk_vbox_new (FALSE, 0);
712 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
713 gtk_box_pack_start (GTK_BOX (vbox), priv->empty, TRUE, TRUE, 0);
714 gtk_box_pack_end (GTK_BOX (vbox), priv->received, FALSE, FALSE, 0);
716 align = gtk_alignment_new (0.5f, 0.0f, 1.0f, 1.0f);
717 gtk_alignment_set_padding (GTK_ALIGNMENT (align),
718 0, 0, HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DEFAULT);
720 gtk_container_set_border_width (GTK_CONTAINER (vbox), HILDON_MARGIN_HALF);
722 event_box = gtk_event_box_new ();
723 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
724 gtk_widget_set_size_request (event_box, BOX_WIDTH, BOX_HEIGHT);
726 gtk_container_add (GTK_CONTAINER (align), vbox);
727 gtk_container_add (GTK_CONTAINER (event_box), align);
728 gtk_container_add (GTK_CONTAINER (self), event_box);
730 g_signal_connect (event_box, "button-press-event",
731 G_CALLBACK (button_press_event_cb), self);
732 g_signal_connect (event_box, "button-release-event",
733 G_CALLBACK (button_release_event_cb), self);
734 g_signal_connect (event_box, "leave-notify-event",
735 G_CALLBACK (leave_notify_event_cb), self);
737 gtk_widget_show_all (GTK_WIDGET (event_box));
740 priv->eventlogger = rtcom_el_new ();
741 g_signal_connect (priv->eventlogger,
743 G_CALLBACK (new_event_cb),
745 g_signal_connect (priv->eventlogger,
747 G_CALLBACK (new_event_cb),
750 read_new_event (self);
755 el_home_applet_class_init (ELHomeAppletClass *klass)
757 GObjectClass *object_class = G_OBJECT_CLASS (klass);
758 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
760 object_class->dispose = dispose;
761 object_class->finalize = finalize;
762 widget_class->expose_event = expose_event;
763 widget_class->realize = el_home_applet_realize;
765 g_type_class_add_private (klass, sizeof (ELHomeAppletPrivate));