Some cleaning for VGA
[mokosuite2:mokopanel.git] / src / panel.c
1 /*
2  * MokoPanel
3  * Notification panel
4  * Copyright (C) 2009-2010 Daniele Ricci <daniele.athome@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include <Ecore_X.h>
22 #include <Elementary.h>
23 #include <Efreet.h>
24
25 #include <mokosuite/utils/utils.h>
26 #include <mokosuite/utils/misc.h>
27
28 #include "panel.h"
29 #include "clock.h"
30 #include "gps.h"
31 #include "battery.h"
32 #include "gsm.h"
33 #include "notifications-win.h"
34 #include "notifications-service.h"
35 #include "idle.h"
36
37 #define ICON_THEME          "shr"
38
39 static void process_notification_queue(gpointer data);
40
41 typedef struct {
42     int id;
43     char* icon;
44     char* text;
45 } notification_push_data_t;
46
47 static char* get_real_icon(const char* name)
48 {
49     if (!name) return NULL;
50
51     EINA_LOG_DBG("retrieving icon \"%s\"", name);
52     if (g_str_has_prefix(name, "file://")) {
53         return g_strdup(name + strlen("file://"));
54     }
55
56     return g_strdup(efreet_icon_path_find(ICON_THEME, name, ICON_SIZE));
57 }
58
59 static char* strip_body(const char* body)
60 {
61     // ehm :)
62     if (!body) return g_strdup("");
63
64     char* copy = g_malloc0(strlen(body));
65     char* _copy = copy;
66     bool inside_tag = FALSE;
67
68     for (; *body != '\0'; body++) {
69         //EINA_LOG_DBG("Char = '%c', inside_tag = %s", *body, inside_tag ? "TRUE" : "FALSE");
70         if (inside_tag && *body == '>') {
71             inside_tag = FALSE;
72             continue;
73         }
74         if (*body == '<')
75             inside_tag = TRUE;
76
77         if (!inside_tag) {
78             *copy = *body;
79             copy++;
80         }
81     }
82
83     EINA_LOG_DBG("Stripped body: \"%s\"", _copy);
84     return _copy;
85 }
86
87 static void _panel_mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info)
88 {
89     notify_window_start();
90 }
91
92 static void _panel_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info)
93 {
94     notify_window_end();
95 }
96
97 static void free_notification(MokoNotification* no)
98 {
99     // cancella icona solo se tutte le notifiche di quel tipo sono andate
100     MokoPanel* panel = no->panel;
101     gboolean update_only = TRUE;
102
103     Eina_List* iter;
104     MokoNotification* cur;
105     EINA_LIST_FOREACH(panel->list, iter, cur) {
106         if (no != cur && !strcmp(no->category, cur->category)) goto no_icon;
107     }
108
109     // tutte le notifiche di quel tipo sono andate, cancella icona
110     g_debug("Deleting notification icon %p for category '%s'", no->icon, no->category);
111     evas_object_del(no->icon);
112
113     // cancella notifica in lista :)
114     update_only = FALSE;
115
116 no_icon:
117     // rimuovi dalla finestra delle notifiche
118     notification_window_remove(no, update_only);
119
120     g_debug("Freeing notification %d", no->id);
121     g_free(no->summary);
122     g_free(no->body);
123     g_free(no->category);
124     g_strfreev(no->actions);
125
126     g_free(no);
127 }
128
129 static gboolean do_pop_text_notification(gpointer data)
130 {
131     evas_object_del((Evas_Object *) data);
132     return FALSE;
133 }
134
135 // rimuove la hbox contenente una notifica di testo
136 static gboolean pop_text_notification(gpointer data)
137 {
138     Evas_Object* obj = (Evas_Object *) data;
139     MokoPanel* panel = (MokoPanel *) evas_object_data_get(obj, "panel");
140
141     g_queue_pop_head(panel->queue);
142     EINA_LOG_DBG("queue: %d left", panel->queue->length);
143
144     // se c'e' dell'altro, continua a processare
145     if (panel->queue->length > 0)
146         process_notification_queue(panel);
147     else {
148         if (panel->topmost == panel->date && !panel->date_pushed) {
149             evas_object_show(panel->topmost);
150             elm_pager_content_push(panel->pager, panel->date);
151         }
152
153         elm_pager_content_promote(panel->pager, panel->topmost);
154     }
155
156     g_timeout_add(500, do_pop_text_notification, obj);
157
158     return FALSE;
159 }
160
161 // processa una notifica di testo dalla coda
162 static void process_notification_queue(gpointer data)
163 {
164     MokoPanel* panel = (MokoPanel *) data;
165
166     // recupera la notifica in testa alla coda e visualizzala
167     notification_push_data_t* in_data = (notification_push_data_t*) g_queue_peek_head(panel->queue);
168
169     // questo non dovrebbe accadare
170     g_return_if_fail(in_data != NULL);
171
172     Evas_Object* msgbox = elm_box_add(panel->win);
173     elm_box_horizontal_set(msgbox, TRUE);
174     evas_object_size_hint_weight_set (msgbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
175     evas_object_show(msgbox);
176
177     // salva il panel per uso futuro
178     evas_object_data_set(msgbox, "panel", panel);
179
180     Evas_Object *ic = elm_icon_add(panel->win);
181     elm_icon_file_set(ic, (const char*) in_data->icon, NULL);
182     elm_icon_smooth_set(ic, TRUE);
183     elm_icon_scale_set(ic, TRUE, TRUE);
184
185     evas_object_size_hint_min_set(ic, ICON_SIZE, ICON_SIZE);
186     evas_object_size_hint_align_set(ic, 0.5, 0.5);
187     evas_object_show(ic);
188
189     elm_box_pack_end(msgbox, ic);
190
191     Evas_Object* lmsg = elm_label_add(panel->win);
192     elm_label_label_set(lmsg, (const char*) in_data->text);
193     elm_object_style_set(lmsg, "panel");
194
195     evas_object_size_hint_weight_set (lmsg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
196     evas_object_size_hint_align_set(lmsg, -1.0, 0.5);
197     evas_object_show(lmsg);
198
199     elm_box_pack_end(msgbox, lmsg);
200
201     elm_pager_content_push(panel->pager, msgbox);
202
203     // aggiungi il timeout per la rimozione
204     g_timeout_add_seconds(3, pop_text_notification, msgbox);
205
206     // libera tutto
207     g_free(in_data->icon);
208     g_free(in_data->text);
209     g_free(in_data);
210 }
211
212 /**
213  * Prepara un'array di strutture per uso dei pusher interni.
214  */
215 static notification_push_data_t** prepare_text_struct(int id, const char* text, const char* icon, int* length)
216 {
217     // split string
218     char** lines = g_strsplit(text, "\n", 0);
219     int len = g_strv_length(lines);
220
221     notification_push_data_t** data = calloc(len, sizeof(notification_push_data_t*));
222     *length = len;
223
224     int i;
225     for ( i = 0; i < len; i++ ) {
226         notification_push_data_t* t = malloc(sizeof(notification_push_data_t));
227         data[i] = t;
228
229         t->id = id;
230         t->icon = g_strdup(icon);
231         t->text = g_strdup(lines[i]);
232     }
233
234     g_strfreev(lines);
235     return data;
236 }
237
238 // aggiunge la notifica di testo al pannello
239 static void push_text_notification(MokoPanel* panel, const char* text, const char* icon)
240 {
241     int len, old_len, i;
242     EINA_LOG_DBG("pushing notification text: \"%s\"", text);
243     notification_push_data_t** data = prepare_text_struct(-1, text, icon, &len);
244
245     EINA_LOG_DBG("pushing %d lines of notifications", len);
246     old_len = panel->queue->length;
247     for (i=0; i < len; i++)
248         g_queue_push_tail(panel->queue, data[i]);
249
250     // fai partire il processamento se e' la prima notifica
251     if (!old_len)
252         process_notification_queue(panel);
253 }
254
255 // aggiunge la notifica alla coda dei ripresentati
256 static void push_represent(MokoPanel* panel, int id, const char* text, const char* icon)
257 {
258     // check previous notification
259     GList* iter = panel->represent->head;
260     while (iter) {
261         notification_push_data_t* data = iter->data;
262         if (data->id == id) {
263             g_free(data->text);
264             g_free(data->icon);
265
266             data->text = g_strdup(text);
267             data->icon = g_strdup(icon);
268             return;
269         }
270         iter = iter->next;
271     }
272
273     notification_push_data_t* data = calloc(1, sizeof(notification_push_data_t));
274     data->id = id;
275     data->text = g_strdup(text);
276     data->icon = g_strdup(icon);
277
278     g_queue_push_tail(panel->represent, data);
279 }
280
281 /**
282  * Gestore degli eventi del pannello predefinito.
283  */
284 void mokopanel_event(MokoPanel* panel, int event, gpointer data)
285 {
286     g_debug("Mokopanel event %d", event);
287
288     switch (event) {
289         case MOKOPANEL_CALLBACK_NOTIFICATION_START:
290             if (panel->date == NULL) {
291                 panel->date = elm_label_add(panel->win);
292
293                 evas_object_data_set(panel->date, "panel", panel);
294                 evas_object_size_hint_weight_set (panel->date, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
295                 evas_object_size_hint_align_set (panel->date, EVAS_HINT_FILL, EVAS_HINT_FILL);
296             }
297
298             // update date label
299             guint64 now = get_current_time();
300             struct tm* timestamp_tm = localtime((const time_t*)&now);
301             char strf[50+1] = {0, };
302             strftime(strf, 50, "%Y-%m-%d", timestamp_tm);
303             elm_object_style_set(panel->date, "panel");
304             elm_label_label_set(panel->date, strf);
305
306             if (elm_pager_content_top_get(panel->pager) == panel->hbox) {
307                 panel->date_pushed = TRUE;
308                 evas_object_show(panel->date);
309                 elm_pager_content_push(panel->pager, panel->date);
310             }
311             panel->topmost = panel->date;
312
313             break;
314
315         case MOKOPANEL_CALLBACK_NOTIFICATION_HIDE:
316             panel->topmost = panel->hbox;
317             if (panel->date && elm_pager_content_top_get(panel->pager) == panel->date) {
318                 elm_pager_content_promote(panel->pager, panel->topmost);
319             }
320
321             break;
322     }
323 }
324
325 /**
326  * Notifica un evento al pannello.
327  */
328 void mokopanel_fire_event(MokoPanel* panel, int event, gpointer data)
329 {
330     if (panel && panel->callback) {
331         MokoPanelCallback cb = panel->callback;
332         (cb)(panel, event, data);
333     }
334 }
335
336 /**
337  * Conta le notifiche di una categoria dato.
338  * @param panel
339  * @param category
340  * @return il conteggio
341  */
342 int mokopanel_count_notifications(MokoPanel* panel, const char* category)
343 {
344     guint count = 0;
345     Eina_List* iter;
346     MokoNotification* data;
347     EINA_LIST_FOREACH(panel->list, iter, data) {
348         if (!strcmp(data->category, category))
349             count++;
350     }
351
352     return count;
353 }
354
355 /**
356  * Restituisce l'item della lista della categoria data.
357  * @param panel
358  * @param category
359  * @return l'item
360  */
361 Elm_Genlist_Item* mokopanel_get_list_item(MokoPanel* panel, const char* category)
362 {
363     Eina_List* iter;
364     MokoNotification* data;
365     EINA_LIST_FOREACH(panel->list, iter, data) {
366         if (!strcmp(data->category, category) && data->item)
367             return data->item;
368     }
369
370     return NULL;
371 }
372
373 /**
374  * Restituisce la lista delle notifiche della categoria data
375  * @param panel
376  * @param category
377  * @return la lista
378  */
379 Eina_List* mokopanel_get_category(MokoPanel* panel, const char* category)
380 {
381     return (Eina_List*) g_hash_table_lookup(panel->categories, category);
382 }
383
384 /**
385  * Ri-pusha le notifiche con flag represent.
386  */
387 void mokopanel_notification_represent(MokoPanel* panel)
388 {
389     // parti dal primo elemento (il piu' vecchio) e pushalo
390     GList* iter = panel->represent->head;
391     while (iter) {
392         notification_push_data_t* data = iter->data;
393         push_text_notification(panel, data->text, data->icon);
394         iter = iter->next;
395     }
396 }
397
398 void mokopanel_append_object(MokoPanel* panel, Evas_Object* obj)
399 {
400     elm_box_pack_after(panel->hbox, obj, panel->fill);
401 }
402
403 /**
404  * TODO
405  */
406 char** mokopanel_notification_caps(MokoPanel* panel, int* length)
407 {
408     char** caps = g_new0(char*, 4);
409     if (length) *length = 4;
410     caps[0] = g_strdup("actions");
411     caps[1] = g_strdup("body");
412     caps[2] = g_strdup("icon-static");
413     caps[3] = g_strdup("sound");
414
415     return caps;
416 }
417
418 /**
419  * Rimuove una notifica.
420  */
421 void mokopanel_notification_remove(MokoPanel* panel, guint id)
422 {
423     g_return_if_fail(panel != NULL);
424     EINA_LOG_DBG("Removing notification %d", id);
425
426     MokoNotification* data = NULL, *data2 = NULL;
427     Eina_List* iter;
428
429     EINA_LIST_FOREACH(panel->list, iter, data2) {
430         if (data2 && data2->id == id) {
431             data = data2;
432             break;
433         }
434     }
435
436     if (data != NULL) {
437         //EINA_LOG_DBG("Found notification %d (%p)", id, data);
438
439         // rimozione dai represent
440         GList* giter = panel->represent->head;
441         while (giter) {
442             notification_push_data_t* gdata = giter->data;
443             if (gdata->id == id) {
444                 g_free(gdata->text);
445                 g_free(gdata->icon);
446                 g_free(gdata);
447
448                 g_queue_delete_link(panel->represent, giter);
449                 break;
450             }
451             giter = giter->next;
452         }
453
454         Eina_List* catg_list = g_hash_table_lookup(panel->categories, data->category);
455         g_hash_table_insert(panel->categories, g_strdup(data->category), eina_list_remove(catg_list, data));
456
457         // rimozione dalla lista e liberazione
458         panel->list = eina_list_remove_list(panel->list, iter);
459         free_notification(data);
460     }
461 }
462
463 static void _dump_map(gpointer key, gpointer value, gpointer data)
464 {
465     EINA_LOG_DBG("key=\"%s\", value=%p", (char*) key, value);
466 }
467
468 /**
469  * Aggiunge una notifica in coda e ne restituisce l'ID per una futura rimozione.
470  * Se il testo è NULL, la notifica è inserita solamente in prima pagina.
471  * Se il testo è diverso da NULL, la notifica sarà visualizzata per qualche
472  * secondo, riga per riga; dopodiché sarà inserita in prima pagina.
473  *
474  * TODO
475  * @return l'ID univoco della notifica
476  */
477 guint mokopanel_notification_queue(MokoPanel* panel,
478         const char* app_name, guint id, const char* icon, const char* summary,
479         const char* body, char** actions, int actions_length, GHashTable* hints,
480         int timeout)
481 {
482     // some check...
483     g_return_val_if_fail(panel != NULL, 0);
484
485     EINA_LOG_DBG("New notification id=%d", id);
486     EINA_LOG_DBG("\tapp_name=\"%s\"", app_name);
487     EINA_LOG_DBG("\ticon=\"%s\"", icon);
488     EINA_LOG_DBG("\tsummary=\"%s\"", summary);
489     EINA_LOG_DBG("\tbody=\"%s\"", body);
490     EINA_LOG_DBG("\tactions.length=%d", actions_length);
491     EINA_LOG_DBG("\thints=%p", hints);
492     if (hints)
493         EINA_LOG_DBG("\thints.length=%d", g_hash_table_size(hints));
494     EINA_LOG_DBG("\ttimeout=%d", timeout);
495
496     Evas_Object *ic = NULL;
497     MokoNotification *data = NULL;
498     guint seq;
499
500     Eina_List *iter;
501     void* _data;
502     char* icon_path = NULL;
503     const char* icon_hint = NULL;
504
505     // get category
506     const char* catg = map_get_string(hints, "category");
507     Eina_List* catg_list = NULL;
508
509     if (!catg || !strlen(catg))
510         // FIXME ehm...
511         catg = app_name;
512
513     // TODO image_data hint
514     icon_hint = map_get_string(hints, "image_path");
515
516     if (icon_hint) {
517         EINA_LOG_DBG("found image_path hint (%s)", icon_hint);
518         icon = icon_hint;
519     }
520
521     // get icon
522     if (!icon) {
523         EINA_LOG_DBG("no icon found, using default (%s)", DEFAULT_ICON);
524         icon_path = g_strdup(DEFAULT_ICON);
525     }
526
527     else {
528         // retrieve real icon path
529         icon_path = get_real_icon(icon);
530     }
531
532     if (!icon_path) {
533         EINA_LOG_DBG("icon real path not found, using default (%s)", DEFAULT_ICON);
534         icon_path = g_strdup(DEFAULT_ICON);
535     }
536
537     EINA_LOG_DBG("will use icon in: \"%s\"", icon_path);
538
539     catg_list = g_hash_table_lookup(panel->categories, catg);
540
541     // intanto appendi l'icona in prima pagina se non e' gia' presente
542     // della stessa categoria
543     EINA_LIST_FOREACH(panel->list, iter, _data) {
544         data = _data;
545         if (!strcmp(data->category, catg)) {
546             ic = data->icon;
547             goto no_icon;
548         }
549     }
550
551     ic = elm_icon_add(panel->win);
552     elm_icon_file_set(ic, icon_path, NULL);
553     elm_icon_smooth_set(ic, TRUE);
554     elm_icon_scale_set(ic, TRUE, TRUE);
555
556     evas_object_size_hint_align_set(ic, 0.5, 0.5);
557     evas_object_size_hint_min_set(ic, ICON_SIZE, ICON_SIZE);
558     evas_object_show(ic);
559     elm_box_pack_before(panel->hbox, ic, panel->fill);
560
561 no_icon:
562     if (!id) {
563         if ((panel->sequence + 1) >= G_MAXUINT) panel->sequence = 1;
564         else panel->sequence++;
565
566         seq = panel->sequence;
567     }
568     else {
569         seq = id;
570     }
571
572     MokoNotification* no;
573     data = NULL;
574     EINA_LIST_FOREACH(panel->list, iter, no) {
575         if (no->id == id) {
576             data = no;
577             break;
578         }
579     }
580
581     if (!data) {
582         data = g_new0(MokoNotification, 1);
583         data->panel = panel;
584     }
585     else {
586         g_free(data->summary);
587         g_free(data->body);
588         g_free(data->category);
589         g_free(data->icon_path);
590     }
591
592     data->id = seq;
593     data->icon = ic;
594     data->icon_path = icon_path;
595     data->summary = g_strdup(summary);
596     data->body = strip_body(body);
597     data->category = g_strdup(catg);
598     data->timeout = timeout;
599
600     // actions
601     if (actions && actions_length > 1) {
602         data->actions = g_new(char*, actions_length + 1);
603         int i;
604         for (i = 0; i < actions_length; i++) {
605             EINA_LOG_DBG("action[%d]=%s", i, actions[i]);
606             data->actions[i] = g_strdup(actions[i]);
607         }
608         data->actions[i] = NULL;
609     }
610
611     g_hash_table_foreach(hints, _dump_map, NULL);
612
613     // flags
614     data->dont_push = map_get_bool(hints, "x-mokosuite.flags.dont-push", TRUE);
615     data->show_on_resume = map_get_bool(hints, "x-mokosuite.flags.show-on-resume", TRUE);
616     data->insistent = map_get_bool(hints, "x-mokosuite.flags.insistent", TRUE);
617     data->ongoing = map_get_bool(hints, "x-mokosuite.flags.ongoing", TRUE);
618     data->no_clear = map_get_bool(hints, "x-mokosuite.flags.noclear", TRUE);
619     data->autodel = map_get_bool(hints, "x-mokosuite.flags.autodel", TRUE);
620
621     // automatic delay -- set autodel
622     if (timeout < 0 || !data->actions)
623         data->autodel = TRUE;
624
625     // new notification, insert
626     if (!id) {
627         g_hash_table_insert(panel->categories, g_strdup(data->category), eina_list_append(catg_list, data));
628         panel->list = eina_list_append(panel->list, data);
629     }
630
631     // aggiungi alla finestra delle notifiche
632     notification_window_add(data);
633
634     if (body != NULL) {
635         // pusha se richiesto
636         if (!data->dont_push)
637             push_text_notification(panel, data->body, icon_path);
638
639         // ripresenta notifica
640         if (data->show_on_resume)
641             push_represent(panel, seq, data->body, icon_path);
642     }
643
644     // ongoing notification, activate display
645     if (data->ongoing) {
646         screensaver_off();
647         idle_hide();
648     }
649
650     EINA_LOG_DBG((!id) ?
651         "Created notification with id %d" :
652         "Updated notification %d",
653         seq);
654     return seq;
655 }
656
657 MokoPanel* mokopanel_new(const char* name, const char* title)
658 {
659     Ecore_X_Window xwin;
660     Ecore_X_Window_State states[3];
661
662     MokoPanel* panel = g_new0(MokoPanel, 1);
663
664     panel->queue = g_queue_new();
665     panel->represent = g_queue_new();
666     panel->callback = mokopanel_event;
667
668     /*
669      * The categories table maps notifications sharing the same category.
670      * It must not have a free function for value since notification deallocation
671      * is manually managed by panel.
672      */
673     panel->categories = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
674
675     // Notification Service
676     panel->service = (GObject *) notifications_service_new(panel);
677
678     // the panel window
679     panel->win = elm_win_add(NULL, name, ELM_WIN_DOCK);
680     elm_win_title_set(panel->win, title);
681     elm_win_layer_set(panel->win, 200);    // indicator layer :)
682
683     xwin = elm_win_xwindow_get(panel->win);
684     ecore_x_icccm_hints_set(xwin, 0, 0, 0, 0, 0, 0, 0);
685     states[0] = ECORE_X_WINDOW_STATE_SKIP_TASKBAR;
686     states[1] = ECORE_X_WINDOW_STATE_SKIP_PAGER;
687     states[2] = ECORE_X_WINDOW_STATE_ABOVE;
688     ecore_x_netwm_window_state_set(xwin, states, 3);
689
690     Evas_Object *bg = elm_bg_add(panel->win);
691     elm_object_style_set(bg, "panel");
692     evas_object_size_hint_weight_set (bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
693     elm_win_resize_object_add (panel->win, bg);
694     evas_object_show(bg);
695
696     // main layout
697     panel->layout = elm_layout_add(panel->win);
698     elm_layout_file_set(panel->layout, MOKOPANEL_DATADIR "/theme.edj", "panel");
699
700     evas_object_size_hint_weight_set (panel->layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
701     elm_win_resize_object_add (panel->win, panel->layout);
702
703     evas_object_show(panel->layout);
704
705     // the front page! :)
706     panel->pager = elm_pager_add(panel->win);
707     elm_object_style_set(panel->pager, "panel");
708
709     evas_object_size_hint_weight_set (panel->pager, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
710     evas_object_size_hint_weight_set (panel->pager, EVAS_HINT_FILL, EVAS_HINT_FILL);
711
712     evas_object_show(panel->pager);
713
714     elm_layout_content_set(panel->layout, "content", panel->pager);
715
716     // main hbox
717     panel->hbox = panel->topmost = elm_box_add(panel->win);
718     elm_box_horizontal_set(panel->hbox, TRUE);
719     evas_object_size_hint_weight_set (panel->hbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
720     evas_object_show(panel->hbox);
721
722     elm_pager_content_push(panel->pager, panel->hbox);
723
724     // filler bg
725     panel->fill = elm_bg_add(panel->win);
726     evas_object_size_hint_weight_set (panel->fill, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
727     evas_object_size_hint_align_set(panel->fill, EVAS_HINT_FILL, EVAS_HINT_FILL);
728     evas_object_show(panel->fill);
729     elm_box_pack_end(panel->hbox, panel->fill);
730
731     // clock
732     panel->time = clock_applet_new(panel);
733
734     // battery
735     panel->battery = battery_applet_new(panel);
736
737     // gsm
738     panel->gsm = gsm_applet_new(panel);
739
740     // gps
741     panel->gps = gps_applet_new(panel);
742
743     // mouse callback
744     Evas_Object* ev = evas_object_rectangle_add(evas_object_evas_get(panel->win));
745     evas_object_color_set(ev, 0, 0, 0, 0);
746
747     evas_object_event_callback_add(ev, EVAS_CALLBACK_MOUSE_DOWN, _panel_mouse_down, NULL);
748     evas_object_event_callback_add(ev, EVAS_CALLBACK_MOUSE_UP, _panel_mouse_up, NULL);
749
750     elm_win_resize_object_add(panel->win, ev);
751     evas_object_show(ev);
752
753     ecore_x_event_mask_set(xwin, ECORE_X_EVENT_MASK_WINDOW_VISIBILITY);
754
755     //evas_object_size_hint_min_set(panel->layout, PANEL_WIDTH, PANEL_HEIGHT);
756     //evas_object_resize(panel->win, PANEL_WIDTH, PANEL_HEIGHT);
757
758     evas_object_show(panel->win);
759
760     // finestra notifiche
761     notify_window_init(panel);
762
763     return panel;
764 }