1 /**
2  * @file
3  * Data shared between Index, Pager and Sidebar
4  *
5  * @authors
6  * Copyright (C) 2021 Richard Russon <rich@flatcap.org>
7  *
8  * @copyright
9  * This program is free software: you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation, either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /**
24  * @page index_shared_data Shared data
25  *
26  * Data shared between Index, Pager and Sidebar
27  */
28 
29 #include "config.h"
30 #include <stdbool.h>
31 #include "mutt/lib.h"
32 #include "email/lib.h"
33 #include "core/lib.h"
34 #include "shared_data.h"
35 #include "lib.h"
36 #include "context.h"
37 #include "mutt_globals.h"
38 
39 /**
40  * index_shared_context_observer - Notification that the Context has changed - Implements ::observer_t - @ingroup observer_api
41  */
index_shared_context_observer(struct NotifyCallback * nc)42 static int index_shared_context_observer(struct NotifyCallback *nc)
43 {
44   if ((nc->event_type != NT_CONTEXT) || !nc->global_data || !nc->event_data)
45     return -1;
46 
47   struct EventContext *ev_c = nc->event_data;
48   if (nc->event_subtype == NT_CONTEXT_ADD)
49     return 0;
50 
51   struct IndexSharedData *shared = nc->global_data;
52   if (ev_c->ctx != shared->ctx)
53     return 0;
54 
55   if (nc->event_subtype == NT_CONTEXT_DELETE)
56     shared->ctx = NULL;
57 
58   // Relay the message
59   mutt_debug(LL_NOTIFY, "NT_INDEX_CONTEXT\n");
60   notify_send(shared->notify, NT_INDEX, NT_INDEX_CONTEXT, shared);
61   return 0;
62 }
63 
64 /**
65  * index_shared_account_observer - Notification that an Account has changed - Implements ::observer_t - @ingroup observer_api
66  */
index_shared_account_observer(struct NotifyCallback * nc)67 static int index_shared_account_observer(struct NotifyCallback *nc)
68 {
69   if ((nc->event_type != NT_ACCOUNT) || !nc->global_data || !nc->event_data)
70     return -1;
71 
72   struct EventAccount *ev_a = nc->event_data;
73   if (nc->event_subtype == NT_ACCOUNT_ADD)
74     return 0;
75 
76   struct IndexSharedData *shared = nc->global_data;
77   if (ev_a->account != shared->account)
78     return 0;
79 
80   if (nc->event_subtype == NT_ACCOUNT_DELETE)
81     shared->account = NULL;
82 
83   // Relay the message
84   mutt_debug(LL_NOTIFY, "NT_INDEX_ACCOUNT\n");
85   notify_send(shared->notify, NT_INDEX, NT_INDEX_ACCOUNT, shared);
86   return 0;
87 }
88 
89 /**
90  * index_shared_mailbox_observer - Notification that a Mailbox has changed - Implements ::observer_t - @ingroup observer_api
91  */
index_shared_mailbox_observer(struct NotifyCallback * nc)92 static int index_shared_mailbox_observer(struct NotifyCallback *nc)
93 {
94   if ((nc->event_type != NT_MAILBOX) || !nc->global_data || !nc->event_data)
95     return -1;
96 
97   struct EventMailbox *ev_m = nc->event_data;
98   if (nc->event_subtype == NT_MAILBOX_ADD)
99     return 0;
100 
101   struct IndexSharedData *shared = nc->global_data;
102   if (ev_m->mailbox != shared->mailbox)
103     return 0;
104 
105   if (nc->event_subtype == NT_MAILBOX_DELETE)
106     shared->mailbox = NULL;
107 
108   // Relay the message
109   mutt_debug(LL_NOTIFY, "NT_INDEX_MAILBOX\n");
110   notify_send(shared->notify, NT_INDEX, NT_INDEX_MAILBOX, shared);
111   return 0;
112 }
113 
114 /**
115  * index_shared_email_observer - Notification that an Email has changed - Implements ::observer_t - @ingroup observer_api
116  */
index_shared_email_observer(struct NotifyCallback * nc)117 static int index_shared_email_observer(struct NotifyCallback *nc)
118 {
119   if ((nc->event_type != NT_EMAIL) || !nc->global_data || !nc->event_data)
120     return -1;
121 
122   struct EventEmail *ev_e = nc->event_data;
123   if (nc->event_subtype == NT_EMAIL_ADD)
124     return 0;
125 
126   struct IndexSharedData *shared = nc->global_data;
127   bool match = false;
128   for (int i = 0; i < ev_e->num_emails; i++)
129   {
130     if (ev_e->emails[i] == shared->email)
131     {
132       match = true;
133       break;
134     }
135   }
136 
137   if (!match)
138     return 0;
139 
140   if (nc->event_subtype == NT_EMAIL_DELETE)
141     shared->email = NULL;
142 
143   // Relay the message
144   mutt_debug(LL_NOTIFY, "NT_INDEX_EMAIL: %p\n", shared->email);
145   notify_send(shared->notify, NT_INDEX, NT_INDEX_EMAIL, shared);
146   return 0;
147 }
148 
149 /**
150  * index_shared_data_set_context - Set the Context for the Index and friends
151  * @param shared Shared Index data
152  * @param ctx    New Context, may be NULL
153  */
index_shared_data_set_context(struct IndexSharedData * shared,struct Context * ctx)154 void index_shared_data_set_context(struct IndexSharedData *shared, struct Context *ctx)
155 {
156   if (!shared)
157     return;
158 
159   NotifyIndex subtype = NT_INDEX_NO_FLAGS;
160 
161   if (shared->ctx != ctx)
162   {
163     if (shared->ctx)
164       notify_observer_remove(shared->ctx->notify, index_shared_context_observer, shared);
165 
166     shared->ctx = ctx;
167     subtype |= NT_INDEX_CONTEXT;
168 
169     if (ctx)
170       notify_observer_add(ctx->notify, NT_CONTEXT, index_shared_context_observer, shared);
171 
172     Context = ctx;
173   }
174 
175   struct Mailbox *m = ctx_mailbox(ctx);
176   if (shared->mailbox != m)
177   {
178     if (shared->mailbox)
179       notify_observer_remove(shared->mailbox->notify, index_shared_mailbox_observer, shared);
180 
181     shared->mailbox = m;
182     shared->email = NULL;
183     shared->email_seq = 0;
184     subtype |= NT_INDEX_MAILBOX | NT_INDEX_EMAIL;
185 
186     if (m)
187       notify_observer_add(m->notify, NT_MAILBOX, index_shared_mailbox_observer, shared);
188   }
189 
190   struct Account *a = m ? m->account : NULL;
191   if (shared->account != a)
192   {
193     if (shared->account)
194       notify_observer_remove(shared->account->notify, index_shared_account_observer, shared);
195 
196     shared->account = a;
197     subtype |= NT_INDEX_ACCOUNT;
198 
199     if (a)
200       notify_observer_add(a->notify, NT_ACCOUNT, index_shared_account_observer, shared);
201   }
202 
203   struct ConfigSubset *sub = NeoMutt->sub;
204 #if 0
205   if (m)
206     sub = m->sub;
207   else if (a)
208     sub = a->sub;
209 #endif
210   if (shared->sub != sub)
211   {
212     shared->sub = sub;
213     subtype |= NT_INDEX_SUBSET;
214   }
215 
216   if (subtype != NT_INDEX_NO_FLAGS)
217   {
218     mutt_debug(LL_NOTIFY, "NT_INDEX: %p\n", shared);
219     notify_send(shared->notify, NT_INDEX, subtype, shared);
220   }
221 }
222 
223 /**
224  * index_shared_data_set_email - Set the current Email for the Index and friends
225  * @param shared Shared Index data
226  * @param e      Current Email, may be NULL
227  */
index_shared_data_set_email(struct IndexSharedData * shared,struct Email * e)228 void index_shared_data_set_email(struct IndexSharedData *shared, struct Email *e)
229 {
230   if (!shared)
231     return;
232 
233   size_t seq = e ? e->sequence : 0;
234   if ((shared->email != e) || (shared->email_seq != seq))
235   {
236     if (shared->email)
237       notify_observer_remove(shared->email->notify, index_shared_email_observer, shared);
238 
239     shared->email = e;
240     shared->email_seq = seq;
241 
242     if (e)
243       notify_observer_add(e->notify, NT_EMAIL, index_shared_email_observer, shared);
244 
245     mutt_debug(LL_NOTIFY, "NT_INDEX_EMAIL: %p\n", shared->email);
246     notify_send(shared->notify, NT_INDEX, NT_INDEX_EMAIL, shared);
247   }
248 }
249 
250 /**
251  * index_shared_data_is_cur_email - Check whether an email is the currently selected Email
252  * @param shared Shared Index data
253  * @param e      Email to check
254  * @retval true  e is current
255  * @retval false e is not current
256  */
index_shared_data_is_cur_email(const struct IndexSharedData * shared,const struct Email * e)257 bool index_shared_data_is_cur_email(const struct IndexSharedData *shared,
258                                     const struct Email *e)
259 {
260   if (!shared)
261     return false;
262 
263   return shared->email_seq == e->sequence;
264 }
265 
266 /**
267  * index_shared_data_free - Free Shared Index Data - Implements MuttWindow::wdata_free() - @ingroup window_wdata_free
268  *
269  * Only `notify` is owned by IndexSharedData and should be freed.
270  */
index_shared_data_free(struct MuttWindow * win,void ** ptr)271 void index_shared_data_free(struct MuttWindow *win, void **ptr)
272 {
273   if (!ptr || !*ptr)
274     return;
275 
276   struct IndexSharedData *shared = *ptr;
277 
278   mutt_debug(LL_NOTIFY, "NT_INDEX_DELETE: %p\n", shared);
279   notify_send(shared->notify, NT_INDEX, NT_INDEX_DELETE, shared);
280   notify_free(&shared->notify);
281 
282   if (shared->account)
283     notify_observer_remove(shared->account->notify, index_shared_account_observer, shared);
284   if (shared->ctx)
285     notify_observer_remove(shared->ctx->notify, index_shared_context_observer, shared);
286   if (shared->mailbox)
287     notify_observer_remove(shared->mailbox->notify, index_shared_mailbox_observer, shared);
288   if (shared->email)
289     notify_observer_remove(shared->email->notify, index_shared_email_observer, shared);
290 
291   FREE(ptr);
292 }
293 
294 /**
295  * index_shared_data_new - Create new Index Data
296  * @retval ptr New IndexSharedData
297  */
index_shared_data_new(void)298 struct IndexSharedData *index_shared_data_new(void)
299 {
300   struct IndexSharedData *shared = mutt_mem_calloc(1, sizeof(struct IndexSharedData));
301 
302   shared->notify = notify_new();
303   shared->sub = NeoMutt->sub;
304 
305   mutt_debug(LL_NOTIFY, "NT_INDEX_ADD: %p\n", shared);
306   notify_send(shared->notify, NT_INDEX, NT_INDEX_ADD, shared);
307 
308   return shared;
309 }
310