1 /**
2 * @file
3 * Pager Bar
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 pager_pbar Pager Bar
25 *
26 * The Pager Bar Window displays status info about the email.
27 *
28 * ## Windows
29 *
30 * | Name | Type | See Also |
31 * | :--------------- | :------------ | :--------- |
32 * | Pager Bar Window | WT_STATUS_BAR | pbar_new() |
33 *
34 * **Parent**
35 * - @ref pager_ppanel
36 *
37 * **Children**
38 *
39 * None.
40 *
41 * ## Data
42 * - #PBarPrivateData
43 *
44 * The Pager Bar Window stores its data (#PBarPrivateData) in
45 * MuttWindow::wdata.
46 *
47 * ## Events
48 *
49 * Once constructed, it is controlled by the following events:
50 *
51 * | Event Type | Handler |
52 * | :-------------------- | :---------------------- |
53 * | #NT_COLOR | pbar_color_observer() |
54 * | #NT_CONFIG | pbar_config_observer() |
55 * | #NT_PAGER | pbar_pager_observer() |
56 * | #NT_INDEX | pbar_index_observer() |
57 * | #NT_WINDOW | pbar_window_observer() |
58 * | MuttWindow::recalc() | pbar_recalc() |
59 * | MuttWindow::repaint() | pbar_repaint() |
60 */
61
62 #include "config.h"
63 #include <inttypes.h> // IWYU pragma: keep
64 #include <stdio.h>
65 #include <sys/stat.h>
66 #include "mutt/lib.h"
67 #include "config/lib.h"
68 #include "core/lib.h"
69 #include "gui/lib.h"
70 #include "pbar.h"
71 #include "lib.h"
72 #include "color/lib.h"
73 #include "index/lib.h"
74 #include "context.h"
75 #include "display.h"
76 #include "format_flags.h"
77 #include "hdrline.h"
78 #include "private_data.h"
79
80 /**
81 * struct PBarPrivateData - Data to draw the Pager Bar
82 */
83 struct PBarPrivateData
84 {
85 struct IndexSharedData *shared; ///< Shared Index data
86 struct PagerPrivateData *priv; ///< Private Pager data
87 char *pager_format; ///< Cached status string
88 };
89
90 /**
91 * pbar_recalc - Recalculate the Window data - Implements MuttWindow::recalc() - @ingroup window_recalc
92 */
pbar_recalc(struct MuttWindow * win)93 static int pbar_recalc(struct MuttWindow *win)
94 {
95 if (!mutt_window_is_visible(win))
96 return 0;
97
98 char buf[1024] = { 0 };
99
100 struct PBarPrivateData *pbar_data = win->wdata;
101 struct IndexSharedData *shared = pbar_data->shared;
102 struct PagerPrivateData *priv = pbar_data->priv;
103
104 char pager_progress_str[65]; /* Lots of space for translations */
105
106 long offset;
107 if (priv->lines && (priv->cur_line <= priv->lines_used))
108 offset = priv->lines[priv->cur_line].offset;
109 else
110 offset = priv->bytes_read;
111
112 if (offset < (priv->st.st_size - 1))
113 {
114 snprintf(pager_progress_str, sizeof(pager_progress_str), OFF_T_FMT "%%",
115 (100 * offset / priv->st.st_size));
116 }
117 else
118 {
119 const char *msg = (priv->top_line == 0) ?
120 /* L10N: Status bar message: the entire email is visible in the pager */
121 _("all") :
122 /* L10N: Status bar message: the end of the email is visible in the pager */
123 _("end");
124 mutt_str_copy(pager_progress_str, msg, sizeof(pager_progress_str));
125 }
126
127 if ((priv->pview->mode == PAGER_MODE_EMAIL) || (priv->pview->mode == PAGER_MODE_ATTACH_E))
128 {
129 int msg_in_pager = shared->ctx ? shared->ctx->msg_in_pager : -1;
130
131 const char *c_pager_format = cs_subset_string(shared->sub, "pager_format");
132 mutt_make_string(buf, sizeof(buf), win->state.cols, NONULL(c_pager_format),
133 shared->mailbox, msg_in_pager, shared->email,
134 MUTT_FORMAT_NO_FLAGS, pager_progress_str);
135 }
136 else
137 {
138 snprintf(buf, sizeof(buf), "%s (%s)", priv->pview->banner, pager_progress_str);
139 }
140
141 if (!mutt_str_equal(buf, pbar_data->pager_format))
142 {
143 mutt_str_replace(&pbar_data->pager_format, buf);
144 win->actions |= WA_REPAINT;
145 }
146
147 return 0;
148 }
149
150 /**
151 * pbar_repaint - Repaint the Window - Implements MuttWindow::repaint() - @ingroup window_repaint
152 */
pbar_repaint(struct MuttWindow * win)153 static int pbar_repaint(struct MuttWindow *win)
154 {
155 if (!mutt_window_is_visible(win))
156 return 0;
157
158 struct PBarPrivateData *pbar_data = win->wdata;
159 // struct IndexSharedData *shared = pbar_data->shared;
160
161 mutt_window_move(win, 0, 0);
162 mutt_curses_set_color_by_id(MT_COLOR_STATUS);
163 mutt_window_clrtoeol(win);
164
165 mutt_window_move(win, 0, 0);
166 mutt_draw_statusline(win, win->state.cols, pbar_data->pager_format,
167 mutt_str_len(pbar_data->pager_format));
168 mutt_curses_set_color_by_id(MT_COLOR_NORMAL);
169
170 mutt_debug(LL_DEBUG5, "repaint done\n");
171 return 0;
172 }
173
174 /**
175 * pbar_color_observer - Notification that a Color has changed - Implements ::observer_t - @ingroup observer_api
176 */
pbar_color_observer(struct NotifyCallback * nc)177 static int pbar_color_observer(struct NotifyCallback *nc)
178 {
179 if ((nc->event_type != NT_COLOR) || !nc->global_data || !nc->event_data)
180 return -1;
181
182 struct EventColor *ev_c = nc->event_data;
183 enum ColorId color = ev_c->color;
184
185 if (color != MT_COLOR_STATUS)
186 return 0;
187
188 struct MuttWindow *win_pbar = nc->global_data;
189 win_pbar->actions |= WA_REPAINT;
190 mutt_debug(LL_DEBUG5, "color done, request WA_REPAINT\n");
191
192 return 0;
193 }
194
195 /**
196 * pbar_config_observer - Notification that a Config Variable has changed - Implements ::observer_t - @ingroup observer_api
197 */
pbar_config_observer(struct NotifyCallback * nc)198 static int pbar_config_observer(struct NotifyCallback *nc)
199 {
200 if ((nc->event_type != NT_CONFIG) || !nc->global_data || !nc->event_data)
201 return -1;
202
203 struct EventConfig *ev_c = nc->event_data;
204 if (!mutt_str_equal(ev_c->name, "pager_format"))
205 return 0;
206
207 struct MuttWindow *win_pbar = nc->global_data;
208 win_pbar->actions |= WA_RECALC;
209 mutt_debug(LL_DEBUG5, "config done, request WA_RECALC\n");
210
211 return 0;
212 }
213
214 /**
215 * pbar_index_observer - Notification that the Index has changed - Implements ::observer_t - @ingroup observer_api
216 */
pbar_index_observer(struct NotifyCallback * nc)217 static int pbar_index_observer(struct NotifyCallback *nc)
218 {
219 if ((nc->event_type != NT_INDEX) || !nc->global_data)
220 return -1;
221
222 struct MuttWindow *win_pbar = nc->global_data;
223 if (!win_pbar)
224 return 0;
225
226 struct IndexSharedData *shared = nc->event_data;
227 if (!shared)
228 return 0;
229
230 if (nc->event_subtype & NT_INDEX_MAILBOX)
231 {
232 win_pbar->actions |= WA_RECALC;
233 mutt_debug(LL_DEBUG5, "index done, request WA_RECALC\n");
234 }
235
236 if (nc->event_subtype & NT_INDEX_EMAIL)
237 {
238 win_pbar->actions |= WA_RECALC;
239 mutt_debug(LL_DEBUG5, "index done, request WA_RECALC\n");
240 }
241
242 return 0;
243 }
244
245 /**
246 * pbar_pager_observer - Notification that the Pager has changed - Implements ::observer_t - @ingroup observer_api
247 */
pbar_pager_observer(struct NotifyCallback * nc)248 static int pbar_pager_observer(struct NotifyCallback *nc)
249 {
250 if ((nc->event_type != NT_PAGER) || !nc->global_data)
251 return -1;
252
253 struct MuttWindow *win_pbar = nc->global_data;
254 if (!win_pbar)
255 return 0;
256
257 if (nc->event_subtype & NT_PAGER_VIEW)
258 {
259 win_pbar->actions |= WA_RECALC;
260 mutt_debug(LL_DEBUG5, "pager done, request WA_RECALC\n");
261 }
262
263 return 0;
264 }
265
266 /**
267 * pbar_window_observer - Notification that a Window has changed - Implements ::observer_t - @ingroup observer_api
268 */
pbar_window_observer(struct NotifyCallback * nc)269 static int pbar_window_observer(struct NotifyCallback *nc)
270 {
271 if ((nc->event_type != NT_WINDOW) || !nc->global_data || !nc->event_data)
272 return -1;
273
274 struct MuttWindow *win_pbar = nc->global_data;
275 struct EventWindow *ev_w = nc->event_data;
276 if (ev_w->win != win_pbar)
277 return 0;
278
279 if (nc->event_subtype == NT_WINDOW_STATE)
280 {
281 win_pbar->actions |= WA_RECALC | WA_REPAINT;
282 mutt_debug(LL_NOTIFY, "window state done, request WA_RECALC\n");
283 }
284 else if (nc->event_subtype == NT_WINDOW_DELETE)
285 {
286 struct PBarPrivateData *pbar_data = win_pbar->wdata;
287 struct IndexSharedData *shared = pbar_data->shared;
288
289 notify_observer_remove(NeoMutt->notify, pbar_color_observer, win_pbar);
290 notify_observer_remove(NeoMutt->notify, pbar_config_observer, win_pbar);
291 notify_observer_remove(shared->notify, pbar_index_observer, win_pbar);
292 notify_observer_remove(pbar_data->priv->notify, pbar_pager_observer, win_pbar);
293 notify_observer_remove(win_pbar->notify, pbar_window_observer, win_pbar);
294
295 mutt_debug(LL_DEBUG5, "window delete done\n");
296 }
297
298 return 0;
299 }
300
301 /**
302 * pbar_data_free - Free the private data attached to the MuttWindow - Implements MuttWindow::wdata_free() - @ingroup window_wdata_free
303 */
pbar_data_free(struct MuttWindow * win,void ** ptr)304 static void pbar_data_free(struct MuttWindow *win, void **ptr)
305 {
306 struct PBarPrivateData *pbar_data = *ptr;
307
308 FREE(&pbar_data->pager_format);
309
310 FREE(ptr);
311 }
312
313 /**
314 * pbar_data_new - Free the private data attached to the MuttWindow
315 */
pbar_data_new(struct IndexSharedData * shared,struct PagerPrivateData * priv)316 static struct PBarPrivateData *pbar_data_new(struct IndexSharedData *shared,
317 struct PagerPrivateData *priv)
318 {
319 struct PBarPrivateData *pbar_data = mutt_mem_calloc(1, sizeof(struct PBarPrivateData));
320
321 pbar_data->shared = shared;
322 pbar_data->priv = priv;
323
324 return pbar_data;
325 }
326
327 /**
328 * pbar_new - Create the Pager Bar
329 * @param shared Shared Pager data
330 * @param priv Private Pager data
331 * @retval ptr New Pager Bar
332 */
pbar_new(struct IndexSharedData * shared,struct PagerPrivateData * priv)333 struct MuttWindow *pbar_new(struct IndexSharedData *shared, struct PagerPrivateData *priv)
334 {
335 struct MuttWindow *win_pbar =
336 mutt_window_new(WT_STATUS_BAR, MUTT_WIN_ORIENT_VERTICAL,
337 MUTT_WIN_SIZE_FIXED, MUTT_WIN_SIZE_UNLIMITED, 1);
338
339 win_pbar->wdata = pbar_data_new(shared, priv);
340 win_pbar->wdata_free = pbar_data_free;
341 win_pbar->recalc = pbar_recalc;
342 win_pbar->repaint = pbar_repaint;
343
344 notify_observer_add(NeoMutt->notify, NT_COLOR, pbar_color_observer, win_pbar);
345 notify_observer_add(NeoMutt->notify, NT_CONFIG, pbar_config_observer, win_pbar);
346 notify_observer_add(shared->notify, NT_INDEX, pbar_index_observer, win_pbar);
347 notify_observer_add(priv->notify, NT_PAGER, pbar_pager_observer, win_pbar);
348 notify_observer_add(win_pbar->notify, NT_WINDOW, pbar_window_observer, win_pbar);
349
350 return win_pbar;
351 }
352