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;
149 PangoRectangle extent;
153 x, y, width, height);
156 /* Create a PangoLayout, set the font and text */
157 layout = pango_cairo_create_layout (cr);
158 pango_layout_set_text (layout,
161 desc = pango_font_description_from_string ("Sans 17");
162 pango_layout_set_font_description (layout, desc);
163 pango_font_description_free (desc);
165 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
167 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
168 pango_layout_set_width (layout, PANGO_SCALE*width);
169 pango_layout_set_height (layout, PANGO_SCALE*height);
172 cairo_move_to (cr, x + 1, y + 1 - offset);
173 cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.8);
174 pango_cairo_show_layout (cr, layout);
177 cairo_move_to (cr, x, y - offset);
178 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
179 pango_cairo_show_layout (cr, layout);
181 pango_layout_get_pixel_extents (layout, NULL, &extent);
183 result = height < (extent.height - offset);
185 result = pango_layout_is_ellipsized (layout);
187 g_object_unref (layout);
194 stop_scroll_anim (ELHomeAppletPrivate *priv)
196 priv->scroll_on_click = FALSE;
197 priv->scroll_offset = 0;
198 if (priv->scroll_anim_id > 0) {
199 g_source_remove (priv->scroll_anim_id);
200 priv->scroll_anim_id = 0;
205 expose_event (GtkWidget *self, GdkEventExpose *event)
207 ELHomeAppletPrivate *priv = EL_HOME_APPLET(self)->priv;
211 float red, green, blue;
214 message_height = C_HEIGHT - priv->received->allocation.height - HEADER_HEIGHT;
216 /* find theme active color */
217 gtk_style_lookup_color (self->style, "ActiveTextColor", &color);
218 red = color.red/(float)G_MAXUINT16;
219 green = color.green/(float)G_MAXUINT16;
220 blue = color.blue/(float)G_MAXUINT16;
222 cr = gdk_cairo_create (self->window);
223 gdk_cairo_region (cr, event->region);
227 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
229 cairo_set_source_rgba (cr, 0.4f, 0.4f, 0.4f, 0.1f);
230 cairo_set_line_width (cr, 3.0f);
232 rounded_rectangle (cr,
239 cairo_close_path(cr);
243 cairo_set_line_width (cr, 1.0f);
245 cairo_translate (cr, C_X, C_Y);
246 cairo_move_to (cr, 0, HEADER_HEIGHT);
247 cairo_line_to (cr, 0, BOX_RADIOUS);
248 cairo_curve_to (cr, 0, 0, 0, 0, BOX_RADIOUS, 0);
249 cairo_line_to (cr, C_WIDTH - BOX_RADIOUS, 0);
250 cairo_curve_to (cr, C_WIDTH, 0, C_WIDTH, 0, C_WIDTH, BOX_RADIOUS);
251 cairo_line_to (cr, C_WIDTH, HEADER_HEIGHT);
252 cairo_line_to (cr, 0, HEADER_HEIGHT);
254 cairo_close_path(cr);
256 cairo_set_source_rgba (cr, 0.2f, 0.2f, 0.2f, 0.8f);
257 cairo_fill_preserve (cr);
258 cairo_set_source_rgba (cr, red, green, blue, 1.0f);
262 cairo_move_to (cr, 0, HEADER_HEIGHT);
263 cairo_line_to (cr, 0, C_HEIGHT - BOX_RADIOUS);
264 cairo_curve_to (cr, 0, C_HEIGHT, 0, C_HEIGHT, BOX_RADIOUS, C_HEIGHT);
265 cairo_line_to (cr, C_WIDTH - BOX_RADIOUS, C_HEIGHT);
266 cairo_curve_to (cr, C_WIDTH, C_HEIGHT, C_WIDTH, C_HEIGHT, C_WIDTH, C_HEIGHT - BOX_RADIOUS);
267 cairo_line_to (cr, C_WIDTH, HEADER_HEIGHT);
268 cairo_line_to (cr, 0, HEADER_HEIGHT);
269 cairo_close_path(cr);
271 /* draw body filling depending on (in)active state */
272 cairo_pattern_t *grad;
273 grad = cairo_pattern_create_linear(0, HEADER_HEIGHT,
277 cairo_pattern_add_color_stop_rgba (grad, 0.5f,
278 red, green, blue, 0.8f);
279 cairo_pattern_add_color_stop_rgba (grad, 1.0f,
280 red/2, green/2, blue/2, 0.8f);
283 cairo_pattern_add_color_stop_rgba (grad, 0.5f,
284 0.4f, 0.4f, 0.4f, 0.8f);
285 cairo_pattern_add_color_stop_rgba (grad, 1.0f,
286 0.2f, 0.2f, 0.2f, 0.8f);
288 cairo_set_source (cr, grad);
291 /* cairo_set_source_rgba (cr, red, green, blue, 1.0f); */
292 /* cairo_translate (cr, -C_X, -C_Y); */
293 /* rounded_rectangle (cr, */
296 /* BOX_WIDTH - 2*C_X, */
297 /* BOX_HEIGHT - 2*C_Y, */
299 /* cairo_close_path(cr); */
300 /* cairo_stroke (cr); */
304 ellipsized = draw_text (cr, priv->message,
305 2*C_X, HEADER_HEIGHT,
308 priv->scroll_offset);
309 if (!priv->scroll_offset && !priv->active)
310 priv->scroll_on_click = ellipsized;
312 cairo_pattern_destroy (grad);
315 if (!priv->scroll_on_click && !ellipsized && priv->scroll_offset)
316 stop_scroll_anim (priv);
318 return GTK_WIDGET_CLASS (el_home_applet_parent_class)->expose_event (self, event);
322 dispose (GObject *self)
324 ELHomeAppletPrivate *priv = EL_HOME_APPLET(self)->priv;
326 stop_scroll_anim (priv);
328 g_source_remove (priv->idle_id);
331 if (priv->eventlogger){
332 g_object_unref (priv->eventlogger);
333 priv->eventlogger = NULL;
337 g_free (priv->message);
338 priv->message = NULL;
341 G_OBJECT_CLASS (el_home_applet_parent_class)->dispose (self);
345 finalize (GObject *self)
347 G_OBJECT_CLASS (el_home_applet_parent_class)->finalize (self);
351 format_time (time_t t)
353 static const guint RESULT_SIZE = 32;
356 struct tm now_tm, t_tm;
357 const gchar *format = "%Y.%m.%d %T";
358 gchar *result = g_malloc0 (RESULT_SIZE);
361 localtime_r (&now, &now_tm);
362 localtime_r (&t, &t_tm);
364 if ((now_tm.tm_year == t_tm.tm_year) &&
365 (now_tm.tm_mon == t_tm.tm_mon) &&
366 (now_tm.tm_mday == t_tm.tm_mday))
369 strftime (result, RESULT_SIZE, format, &t_tm);
375 show_event (ELHomeApplet *self, RTComElIter *it)
377 ELHomeAppletPrivate *priv = self->priv;
379 gchar *remote = NULL;
380 gchar *received = NULL;
381 const gchar *icon_name = NULL;
384 g_free (priv->message);
385 priv->message = NULL;
388 if (it && rtcom_el_iter_first (it)){
389 rtcom_el_iter_dup_string (it, "free-text", &priv->message);
391 const gchar *service;
394 rtcom_el_iter_get_int (it, "id", &priv->event_id);
395 if (rtcom_el_iter_get_int (it, "start-time", (gint*)&received_t))
396 received = format_time (received_t);
398 if(!rtcom_el_iter_dup_string (it, "remote-name", &remote))
399 rtcom_el_iter_dup_string (it, "remote-id", &remote);
400 service = rtcom_el_iter_get_service (it);
401 if (!g_strcmp0 (service, "RTCOM_EL_SERVICE_SMS"))
402 icon_name = "chat_unread_sms";
403 else if (!g_strcmp0 (service, "RTCOM_EL_SERVICE_CHAT"))
404 icon_name = "chat_unread_chat";
412 gtk_widget_hide (priv->empty);
414 gtk_widget_show (priv->empty);
416 gtk_label_set_text (GTK_LABEL (priv->sender), remote);
417 gtk_label_set_text (GTK_LABEL (priv->received), received);
420 const gchar *current_icon_name;
421 gtk_image_get_icon_name (GTK_IMAGE (priv->icon),
424 if (g_strcmp0 (current_icon_name, icon_name))
425 gtk_image_set_from_icon_name (GTK_IMAGE (priv->icon),
427 HILDON_ICON_SIZE_FINGER);
428 gtk_widget_show (priv->icon);
431 gtk_widget_hide (priv->icon);
435 gtk_widget_queue_draw (GTK_WIDGET (self));
439 make_query (RTComEl *el, gint event_id)
441 RTComElQuery *query = NULL;
442 RTComElIter *it = NULL;
444 static const gchar *services[] = {"RTCOM_EL_SERVICE_SMS",
445 "RTCOM_EL_SERVICE_CHAT",
447 static const gchar *event_types[] = {"RTCOM_EL_EVENTTYPE_SMS_INBOUND",
448 "RTCOM_EL_EVENTTYPE_CHAT_INBOUND",
451 query = rtcom_el_query_new (el);
452 rtcom_el_query_set_limit (query, 1);
454 rtcom_el_query_prepare (query,
455 "is-read", FALSE, RTCOM_EL_OP_EQUAL,
456 "id", event_id, RTCOM_EL_OP_EQUAL,
457 "service", services, RTCOM_EL_OP_IN_STRV,
458 "event-type", event_types, RTCOM_EL_OP_IN_STRV,
462 rtcom_el_query_prepare (query,
463 "is-read", FALSE, RTCOM_EL_OP_EQUAL,
464 "service", services, RTCOM_EL_OP_IN_STRV,
465 "event-type", event_types, RTCOM_EL_OP_IN_STRV,
468 it = rtcom_el_get_events (el, query);
469 g_object_unref(query);
475 update_unread_label (ELHomeApplet *self)
477 ELHomeAppletPrivate *priv = self->priv;
480 if (priv->unread_count > 0){
481 text = g_strdup_printf ("%d", priv->unread_count);
482 gtk_label_set_text (GTK_LABEL (priv->unread), text);
486 gtk_label_set_text (GTK_LABEL (priv->unread), NULL);
490 query_unread_events (RTComEl *el)
497 g_object_get (el, "db", &db, NULL);
499 if (sqlite3_prepare_v2 (db,
500 "SELECT SUM(total_events)-SUM(read_events) FROM GroupCache;",
504 g_error ("%s: can't compile SQL", G_STRFUNC);
508 while (SQLITE_BUSY == (ret = sqlite3_step (stmt)));
510 if (ret == SQLITE_ROW){
511 count = sqlite3_column_int (stmt, 0);
514 g_error ("%s: error while executing SQL", G_STRFUNC);
517 sqlite3_finalize (stmt);
523 read_event (ELHomeApplet *self)
525 ELHomeAppletPrivate *priv = self->priv;
526 RTComElIter *it = NULL;
528 it = make_query (priv->eventlogger, -1);
529 show_event (self, it);
530 if (it) g_object_unref (it);
534 mark_as_read (ELHomeApplet *self)
536 ELHomeAppletPrivate *priv = self->priv;
538 if (priv->event_id >= 0){
539 rtcom_el_set_read_event (priv->eventlogger,
544 priv->unread_count--;
545 update_unread_label (self);
550 read_new_event (ELHomeApplet *self)
552 ELHomeAppletPrivate *priv = self->priv;
555 priv->unread_count = query_unread_events (priv->eventlogger);
556 update_unread_label (self);
564 add_new_idle (ELHomeApplet *self)
566 ELHomeAppletPrivate *priv = self->priv;
569 g_source_remove (priv->idle_id);
570 priv->idle_id = g_idle_add ((GSourceFunc)read_new_event,
575 new_event_cb (RTComEl *backend,
577 const gchar *local_uid,
578 const gchar *remote_uid,
579 const gchar *remote_ebook_uid,
580 const gchar *group_uid,
581 const gchar *service,
588 scroll_anim_cb (ELHomeApplet *self)
590 ELHomeAppletPrivate *priv = self->priv;
592 priv->scroll_offset += 1;
593 gtk_widget_queue_draw (self);
599 button_press_event_cb (GtkWidget *widget,
600 GdkEventButton *event,
603 ELHomeAppletPrivate *priv = self->priv;
605 if (priv->event_id > 0){
607 gtk_widget_queue_draw (widget);
610 if (priv->scroll_on_click) {
611 stop_scroll_anim (priv);
612 priv->scroll_anim_id = g_timeout_add (100,
617 gtk_widget_queue_draw (widget);
622 button_release_event_cb (GtkWidget *widget,
623 GdkEventButton *event,
626 ELHomeAppletPrivate *priv = self->priv;
629 priv->active = FALSE;
630 /* stop_scroll_anim (priv); */
634 gtk_widget_queue_draw (widget);
641 leave_notify_event_cb (GtkWidget *widget,
642 GdkEventCrossing *event,
645 ELHomeAppletPrivate *priv = self->priv;
648 priv->active = FALSE;
649 stop_scroll_anim (priv);
650 gtk_widget_queue_draw (widget);
657 el_home_applet_init (ELHomeApplet *self)
659 ELHomeAppletPrivate *priv;
660 GtkWidget *event_box;
661 GtkWidget *hbox, *vbox, *align;
663 self->priv = EL_HOME_APPLET_GET_PRIVATE (self);
666 gtk_widget_set_app_paintable (GTK_WIDGET (self), TRUE);
668 priv->unread = gtk_label_new ("12");
669 hildon_helper_set_logical_color (priv->unread,
673 gtk_misc_set_alignment (GTK_MISC (priv->unread),
676 gtk_widget_set_size_request (priv->unread,
680 priv->icon = gtk_image_new_from_icon_name ("chat_unread_sms",
681 HILDON_ICON_SIZE_FINGER);
682 gtk_misc_set_alignment (GTK_MISC (priv->icon),
686 priv->sender = gtk_label_new ("asdf asdf asdf asdf asdf");
687 gtk_misc_set_alignment (GTK_MISC (priv->sender),
690 gtk_label_set_ellipsize (GTK_LABEL (priv->sender),
691 PANGO_ELLIPSIZE_END);
692 gtk_widget_set_name (priv->sender, "hildon-shadow-label");
693 hildon_helper_set_logical_font (priv->sender, "SystemFont");
695 priv->message = g_strdup ("One two three four five six seven eight nine ten"
696 "one two three four five six seven eight nine ten"
697 "one two three four five six seven eight nine ten"
698 "one two three four five six seven eight nine ten"
699 "one two three four five six seven eight nine ten"
700 "one two three four five six seven eight nine ten");
703 priv->empty = gtk_label_new ("No new messages");
704 gtk_widget_set_name (priv->empty, "hildon-shadow-label");
705 GTK_WIDGET_SET_FLAGS (priv->empty, GTK_NO_SHOW_ALL);
707 priv->received = gtk_label_new ("aewf aewf aewf awef");
708 gtk_misc_set_alignment (GTK_MISC (priv->received),
711 gtk_widget_set_size_request (priv->received,
714 hildon_helper_set_logical_font (priv->received, "SmallSystemFont");
715 gtk_widget_set_name (priv->received, "hildon-shadow-label");
717 hbox = gtk_hbox_new (FALSE, 0);
718 gtk_box_pack_start (GTK_BOX (hbox), priv->unread, FALSE, FALSE, 0);
719 gtk_box_pack_start (GTK_BOX (hbox), priv->icon, FALSE, FALSE, 0);
720 gtk_box_pack_start (GTK_BOX (hbox), priv->sender, TRUE, TRUE, 0);
722 vbox = gtk_vbox_new (FALSE, 0);
723 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
724 gtk_box_pack_start (GTK_BOX (vbox), priv->empty, TRUE, TRUE, 0);
725 gtk_box_pack_end (GTK_BOX (vbox), priv->received, FALSE, FALSE, 0);
727 align = gtk_alignment_new (0.5f, 0.0f, 1.0f, 1.0f);
728 gtk_alignment_set_padding (GTK_ALIGNMENT (align),
729 0, 0, HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DEFAULT);
731 gtk_container_set_border_width (GTK_CONTAINER (vbox), HILDON_MARGIN_HALF);
733 event_box = gtk_event_box_new ();
734 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
735 gtk_widget_set_size_request (event_box, BOX_WIDTH, BOX_HEIGHT);
737 gtk_container_add (GTK_CONTAINER (align), vbox);
738 gtk_container_add (GTK_CONTAINER (event_box), align);
739 gtk_container_add (GTK_CONTAINER (self), event_box);
741 g_signal_connect (event_box, "button-press-event",
742 G_CALLBACK (button_press_event_cb), self);
743 g_signal_connect (event_box, "button-release-event",
744 G_CALLBACK (button_release_event_cb), self);
745 g_signal_connect (event_box, "leave-notify-event",
746 G_CALLBACK (leave_notify_event_cb), self);
748 gtk_widget_show_all (GTK_WIDGET (event_box));
751 priv->eventlogger = rtcom_el_new ();
752 g_signal_connect (priv->eventlogger,
754 G_CALLBACK (new_event_cb),
756 g_signal_connect (priv->eventlogger,
758 G_CALLBACK (new_event_cb),
761 read_new_event (self);
766 el_home_applet_class_init (ELHomeAppletClass *klass)
768 GObjectClass *object_class = G_OBJECT_CLASS (klass);
769 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
771 object_class->dispose = dispose;
772 object_class->finalize = finalize;
773 widget_class->expose_event = expose_event;
774 widget_class->realize = el_home_applet_realize;
776 g_type_class_add_private (klass, sizeof (ELHomeAppletPrivate));