mail-notification

Fork of Jean-Yves Lefort's mail-notification, a tray icon to notify of new mail
git clone https://code.djc.id.au/git/mail-notification/

src/mn-popups.gob (7049B) - raw

      1 /*
      2  * Mail Notification
      3  * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
      4  *
      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 3 of the License, or
      8  * (at your option) any later version.
      9  *
     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.
     14  *
     15  * You should have received a copy of the GNU General Public License along
     16  * with this program; if not, write to the Free Software Foundation, Inc.,
     17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     18  */
     19 
     20 %{
     21 #include <libnotify/notify.h>
     22 #include "mn-message.h"
     23 #include "mn-shell.h"
     24 #include "mn-util.h"
     25 #include "mn-conf.h"
     26 #include "mn-locked-callback.h"
     27 #include "mn-popup.h"
     28 #include "mn-properties-dialog.h"
     29 %}
     30 
     31 class MN:Popups from G:Object
     32 {
     33   /*
     34    * Maps message IDs to popups. If a message is known but has no
     35    * popup (because popups were disabled when that message was first
     36    * encountered), the value is NULL.
     37    */
     38   private GHashTable *popups = {g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) mn_g_object_null_unref)} destroywith g_hash_table_destroy;
     39 
     40   init (self)
     41   {
     42     if (! notify_is_initted())
     43       return;			/* libnotify init failed */
     44 
     45     mn_g_object_gconf_notifications_add_gdk_locked(self,
     46 						   MN_CONF_POPUPS_NAMESPACE, self_notify_cb, self,
     47 						   NULL);
     48 
     49     g_signal_connect(mn_shell->mailboxes, "messages-changed", G_CALLBACK(self_messages_changed_h), self);
     50   }
     51 
     52   private void
     53     close_cb (const char *id, MNPopup *popup, gpointer user_data)
     54   {
     55     if (popup)
     56       mn_popup_close(popup);
     57   }
     58 
     59   private void
     60     close_popups (self)
     61   {
     62     g_hash_table_foreach(selfp->popups, (GHFunc) self_close_cb, NULL);
     63   }
     64 
     65   finalize (self)
     66   {
     67     /* close popups on exit */
     68     self_close_popups(self);
     69   }
     70 
     71   private void
     72     messages_changed_h (MNMailboxes *mailboxes,
     73 			gboolean has_new,
     74 			gpointer user_data)
     75   {
     76     Self *self = user_data;
     77 
     78     self_update(self, FALSE);
     79   }
     80 
     81   private void
     82     notify_cb (GConfClient *client,
     83 	       unsigned int cnxn_id,
     84 	       GConfEntry *entry,
     85 	       gpointer user_data)
     86   {
     87     Self *self = user_data;
     88 
     89     self_update(self, TRUE);
     90   }
     91 
     92   private int
     93     popups_show_timestamp_compare_cb (MNPopup *a, MNPopup *b)
     94   {
     95     int cmp;
     96 
     97     cmp = a->show_timestamp.tv_sec - b->show_timestamp.tv_sec;
     98     if (cmp != 0)
     99       return cmp;
    100 
    101     return a->show_timestamp.tv_usec - b->show_timestamp.tv_usec;
    102   }
    103 
    104   private void
    105     get_visible_popups_cb (const char *id, MNPopup *popup, GSList **list)
    106   {
    107     if (popup && popup->visible)
    108       *list = g_slist_insert_sorted(*list, popup, (GCompareFunc) self_popups_show_timestamp_compare_cb);
    109   }
    110 
    111   /* returns the visible popups sorted by show timestamp, oldest first */
    112   private GSList *
    113     get_visible_popups (self)
    114   {
    115     GSList *popups = NULL;
    116 
    117     g_hash_table_foreach(selfp->popups, (GHFunc) self_get_visible_popups_cb, &popups);
    118 
    119     return popups;
    120   }
    121 
    122   private gboolean
    123     close_and_remove_stale_cb (const char *id, MNPopup *popup, GHashTable *messages)
    124   {
    125     if (! g_hash_table_lookup(mn_shell->mailboxes->messages_hash_table, id))
    126       {
    127 	if (popup)
    128 	  mn_popup_close(popup);
    129 
    130 	return TRUE;		/* remove */
    131       }
    132     else
    133       return FALSE;		/* keep */
    134   }
    135 
    136   private gboolean
    137     close_and_remove_visible_cb (const char *id, MNPopup *popup, gpointer user_data)
    138   {
    139     if (popup && popup->visible)
    140       {
    141 	mn_popup_close(popup);
    142 
    143 	return TRUE;		/* remove */
    144       }
    145     else
    146       return FALSE;		/* keep */
    147   }
    148 
    149   private void
    150     update (self, gboolean reshow_current_popups)
    151   {
    152     int i;
    153 
    154     /* close and remove stale popups */
    155     g_hash_table_foreach_remove(selfp->popups, (GHRFunc) self_close_and_remove_stale_cb, NULL);
    156 
    157     if (reshow_current_popups)
    158       /*
    159        * We have to reshow the currently visible popups, so close and
    160        * remove them.
    161        */
    162       g_hash_table_foreach_remove(selfp->popups, (GHRFunc) self_close_and_remove_visible_cb, NULL);
    163 
    164     if (mn_conf_get_bool(MN_CONF_POPUPS_ENABLED))
    165       {
    166 	GPtrArray *messages;	/* for O(1) length retrieval and indexing */
    167 	GSList *visible_popups;
    168 	int num_visible_popups;
    169 	int popup_limit;
    170 	int new_popup_count;
    171 	int messages_to_popup;	/* the number of messages to popup */
    172 
    173 	if (mn_conf_get_enum_value(MN_TYPE_POPUP_POSITION, MN_CONF_POPUPS_POSITION) == MN_POPUP_POSITION_ATTACHED)
    174 	  /* the popup is attached to the icon: allow at most one popup */
    175 	  popup_limit = 1;
    176 	else
    177 	  popup_limit = mn_conf_get_int(MN_CONF_POPUPS_LIMIT);
    178 
    179 	/* build a list of messages which are not already known */
    180 
    181 	messages = g_ptr_array_new();
    182 
    183 	MN_ARRAY_FOREACH(i, mn_shell->mailboxes->messages)
    184 	  {
    185 	    MNMessage *message = g_ptr_array_index(mn_shell->mailboxes->messages, i);
    186 	    gpointer ptr;
    187 
    188 	    if (! g_hash_table_lookup_extended(selfp->popups, message->id, NULL, &ptr))
    189 	      g_ptr_array_add(messages, message);
    190 	  }
    191 
    192 	visible_popups = self_get_visible_popups(self);
    193 	num_visible_popups = g_slist_length(visible_popups);
    194 
    195 	new_popup_count = num_visible_popups + messages->len;
    196 	if (new_popup_count > popup_limit)
    197 	  {
    198 	    int to_remove;
    199 
    200 	    /* make room for n new popups by closing the n oldest popups */
    201 
    202 	    to_remove = new_popup_count - popup_limit;
    203 	    to_remove = MIN(num_visible_popups, to_remove);
    204 
    205 	    while (to_remove > 0)
    206 	      {
    207 		MNPopup *oldest;
    208 
    209 		g_assert(visible_popups != NULL);
    210 
    211 		oldest = visible_popups->data;
    212 		mn_popup_close(oldest);
    213 
    214 		visible_popups = g_slist_delete_link(visible_popups, visible_popups);
    215 		to_remove--;
    216 	      }
    217 	  }
    218 
    219 	g_slist_free(visible_popups);
    220 
    221 	messages_to_popup = MIN(messages->len, popup_limit);
    222 
    223 	/*
    224 	 * Below we iterate over the messages array backwards because
    225 	 * it is sorted by sent time most recent first, while we want
    226 	 * to popup the oldest messages first.
    227 	 */
    228 
    229 	/* register the messages which we will not popup */
    230 
    231 	for (i = messages->len - 1; i >= messages_to_popup; i--)
    232 	  {
    233 	    MNMessage *message = g_ptr_array_index(messages, i);
    234 
    235 	    g_hash_table_replace(selfp->popups, g_strdup(message->id), NULL);
    236 	  }
    237 
    238 	/* popup the first messages_to_popup messages */
    239 
    240 	for (i = messages_to_popup - 1; i >= 0; i--)
    241 	  {
    242 	    MNMessage *message = g_ptr_array_index(messages, i);
    243 	    MNPopup *popup;
    244 
    245 	    popup = mn_popup_new(message);
    246 	    mn_popup_show(popup);
    247 
    248 	    g_hash_table_replace(selfp->popups, g_strdup(message->id), popup);
    249 	  }
    250 
    251 	g_ptr_array_free(messages, TRUE);
    252       }
    253     else
    254       {
    255 	/* register the new messages */
    256 
    257 	MN_ARRAY_FOREACH(i, mn_shell->mailboxes->messages)
    258 	  {
    259 	    MNMessage *message = g_ptr_array_index(mn_shell->mailboxes->messages, i);
    260 	    g_hash_table_replace(selfp->popups, g_strdup(message->id), NULL);
    261 	  }
    262       }
    263   }
    264 
    265   public MNPopups *
    266     new (void)
    267   {
    268     return GET_NEW;
    269   }
    270 }