1 /**
2  * @file
3  * Postponed Email Selection Dialog
4  *
5  * @authors
6  * Copyright (C) 1996-2002,2012-2013 Michael R. Elkins <me@mutt.org>
7  * Copyright (C) 1999-2002,2004 Thomas Roessler <roessler@does-not-exist.org>
8  *
9  * @copyright
10  * This program is free software: you can redistribute it and/or modify it under
11  * the terms of the GNU General Public License as published by the Free Software
12  * Foundation, either version 2 of the License, or (at your option) any later
13  * version.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License along with
21  * this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 /**
25  * @page neo_dlg_postpone Postponed Email Selection Dialog
26  *
27  * The Postponed Email Selection Dialog lets the user set a postponed (draft)
28  * email.
29  *
30  * This is a @ref gui_simple
31  *
32  * ## Windows
33  *
34  * | Name                             | Type            | See Also                     |
35  * | :------------------------------- | :-------------- | :--------------------------- |
36  * | Postponed Email Selection Dialog | WT_DLG_POSTPONE | dlg_select_postponed_email() |
37  *
38  * **Parent**
39  * - @ref gui_dialog
40  *
41  * **Children**
42  * - See: @ref gui_simple
43  *
44  * ## Data
45  * - #Menu
46  * - #Menu::mdata
47  * - #Mailbox
48  *
49  * The @ref gui_simple holds a Menu.  The Autocrypt Account Dialog stores its
50  * data (#Mailbox) in Menu::mdata.
51  *
52  * ## Events
53  *
54  * Once constructed, it is controlled by the following events:
55  *
56  * | Event Type  | Handler                     |
57  * | :---------- | :-------------------------- |
58  * | #NT_CONFIG  | postponed_config_observer() |
59  * | #NT_WINDOW  | postponed_window_observer() |
60  *
61  * The Postponed Email Selection Dialog doesn't have any specific colours, so
62  * it doesn't need to support #NT_COLOR.
63  *
64  * The Postponed Email Selection Dialog does not implement MuttWindow::recalc()
65  * or MuttWindow::repaint().
66  *
67  * Some other events are handled by the @ref gui_simple.
68  */
69 
70 #include "config.h"
71 #include <stdbool.h>
72 #include <stdio.h>
73 #include "mutt/lib.h"
74 #include "config/lib.h"
75 #include "core/lib.h"
76 #include "gui/lib.h"
77 #include "mutt.h"
78 #include "menu/lib.h"
79 #include "pattern/lib.h"
80 #include "format_flags.h"
81 #include "hdrline.h"
82 #include "opcodes.h"
83 #include "protos.h"
84 
85 struct Email;
86 
87 /// Help Bar for the Postponed email selection dialog
88 static const struct Mapping PostponeHelp[] = {
89   // clang-format off
90   { N_("Exit"),  OP_EXIT },
91   { N_("Del"),   OP_DELETE },
92   { N_("Undel"), OP_UNDELETE },
93   { N_("Help"),  OP_HELP },
94   { NULL, 0 },
95   // clang-format on
96 };
97 
98 /**
99  * post_make_entry - Format a menu item for the email list - Implements Menu::make_entry() - @ingroup menu_make_entry
100  */
post_make_entry(struct Menu * menu,char * buf,size_t buflen,int line)101 static void post_make_entry(struct Menu *menu, char *buf, size_t buflen, int line)
102 {
103   struct Mailbox *m = menu->mdata;
104 
105   const char *const c_index_format =
106       cs_subset_string(NeoMutt->sub, "index_format");
107   mutt_make_string(buf, buflen, menu->win->state.cols, NONULL(c_index_format),
108                    m, -1, m->emails[line], MUTT_FORMAT_ARROWCURSOR, NULL);
109 }
110 
111 /**
112  * postponed_config_observer - Notification that a Config Variable has changed - Implements ::observer_t - @ingroup observer_api
113  *
114  * The Address Book Window is affected by changes to `$sort_postponed`.
115  */
postponed_config_observer(struct NotifyCallback * nc)116 static int postponed_config_observer(struct NotifyCallback *nc)
117 {
118   if ((nc->event_type != NT_CONFIG) || !nc->global_data || !nc->event_data)
119     return -1;
120 
121   struct EventConfig *ev_c = nc->event_data;
122 
123   if (!mutt_str_equal(ev_c->name, "index_format") && !mutt_str_equal(ev_c->name, "sort"))
124     return 0;
125 
126   struct Menu *menu = nc->global_data;
127   menu_queue_redraw(menu, MENU_REDRAW_FULL);
128   mutt_debug(LL_DEBUG5, "config done, request WA_RECALC, MENU_REDRAW_FULL\n");
129 
130   return 0;
131 }
132 
133 /**
134  * postponed_window_observer - Notification that a Window has changed - Implements ::observer_t - @ingroup observer_api
135  *
136  * This function is triggered by changes to the windows.
137  *
138  * - Delete (this window): clean up the resources held by the Help Bar
139  */
postponed_window_observer(struct NotifyCallback * nc)140 static int postponed_window_observer(struct NotifyCallback *nc)
141 {
142   if ((nc->event_type != NT_WINDOW) || !nc->global_data || !nc->event_data)
143     return -1;
144 
145   if (nc->event_subtype != NT_WINDOW_DELETE)
146     return 0;
147 
148   struct MuttWindow *win_menu = nc->global_data;
149   struct EventWindow *ev_w = nc->event_data;
150   if (ev_w->win != win_menu)
151     return 0;
152 
153   struct Menu *menu = win_menu->wdata;
154 
155   notify_observer_remove(NeoMutt->notify, postponed_config_observer, menu);
156   notify_observer_remove(win_menu->notify, postponed_window_observer, win_menu);
157 
158   mutt_debug(LL_DEBUG5, "window delete done\n");
159   return 0;
160 }
161 
162 /**
163  * dlg_select_postponed_email - Create a Menu to select a postponed message
164  * @param m Mailbox
165  * @retval ptr Email
166  */
dlg_select_postponed_email(struct Mailbox * m)167 struct Email *dlg_select_postponed_email(struct Mailbox *m)
168 {
169   int r = -1;
170   bool done = false;
171 
172   struct MuttWindow *dlg = simple_dialog_new(MENU_POSTPONE, WT_DLG_POSTPONE, PostponeHelp);
173 
174   struct Menu *menu = dlg->wdata;
175   menu->make_entry = post_make_entry;
176   menu->max = m->msg_count;
177   menu->mdata = m;
178   menu->custom_search = true;
179 
180   struct MuttWindow *win_menu = menu->win;
181 
182   // NT_COLOR is handled by the SimpleDialog
183   notify_observer_add(NeoMutt->notify, NT_CONFIG, postponed_config_observer, menu);
184   notify_observer_add(win_menu->notify, NT_WINDOW, postponed_window_observer, win_menu);
185 
186   struct MuttWindow *sbar = window_find_child(dlg, WT_STATUS_BAR);
187   sbar_set_title(sbar, _("Postponed Messages"));
188 
189   /* The postponed mailbox is setup to have sorting disabled, but the global
190    * `$sort` variable may indicate something different.   Sorting has to be
191    * disabled while the postpone menu is being displayed. */
192   const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
193   cs_subset_str_native_set(NeoMutt->sub, "sort", SORT_ORDER, NULL);
194 
195   while (!done)
196   {
197     const int op = menu_loop(menu);
198     switch (op)
199     {
200       case OP_DELETE:
201       case OP_UNDELETE:
202       {
203         const int index = menu_get_index(menu);
204         /* should deleted draft messages be saved in the trash folder? */
205         mutt_set_flag(m, m->emails[index], MUTT_DELETE, (op == OP_DELETE));
206         PostCount = m->msg_count - m->msg_deleted;
207         const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
208         if (c_resolve && (index < (menu->max - 1)))
209         {
210           menu_set_index(menu, index + 1);
211           if (index >= (menu->top + menu->pagelen))
212           {
213             menu->top = index;
214             menu_queue_redraw(menu, MENU_REDRAW_INDEX);
215           }
216         }
217         else
218           menu_queue_redraw(menu, MENU_REDRAW_CURRENT);
219         break;
220       }
221 
222       // All search operations must exist to show the menu
223       case OP_SEARCH_REVERSE:
224       case OP_SEARCH_NEXT:
225       case OP_SEARCH_OPPOSITE:
226       case OP_SEARCH:
227       {
228         int index = menu_get_index(menu);
229         index = mutt_search_command(m, menu, index, op);
230         if (index != -1)
231           menu_set_index(menu, index);
232         break;
233       }
234 
235       case OP_GENERIC_SELECT_ENTRY:
236         r = menu_get_index(menu);
237         done = true;
238         break;
239 
240       case OP_EXIT:
241         done = true;
242         break;
243     }
244   }
245 
246   cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
247   simple_dialog_free(&dlg);
248 
249   return (r > -1) ? m->emails[r] : NULL;
250 }
251