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 }