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 }