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