/src/neomutt/pager/pbar.c
Line | Count | Source (jump to first uncovered line) |
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 "display.h" |
75 | | #include "format_flags.h" |
76 | | #include "hdrline.h" |
77 | | #include "mview.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 | | */ |
93 | | static int pbar_recalc(struct MuttWindow *win) |
94 | 0 | { |
95 | 0 | char buf[1024] = { 0 }; |
96 | |
|
97 | 0 | struct PBarPrivateData *pbar_data = win->wdata; |
98 | 0 | struct IndexSharedData *shared = pbar_data->shared; |
99 | 0 | struct PagerPrivateData *priv = pbar_data->priv; |
100 | 0 | if (!priv || !priv->pview) |
101 | 0 | return 0; |
102 | | |
103 | 0 | char pager_progress_str[65] = { 0 }; /* Lots of space for translations */ |
104 | |
|
105 | 0 | long offset; |
106 | 0 | if (priv->lines && (priv->cur_line <= priv->lines_used)) |
107 | 0 | offset = priv->lines[priv->cur_line].offset; |
108 | 0 | else |
109 | 0 | offset = priv->bytes_read; |
110 | |
|
111 | 0 | if (offset < (priv->st.st_size - 1)) |
112 | 0 | { |
113 | 0 | const long percent = (100 * offset) / priv->st.st_size; |
114 | | /* L10N: Pager position percentage. |
115 | | `%ld` is the number, `%%` is the percent symbol. |
116 | | They may be reordered, or space inserted, if you wish. */ |
117 | 0 | snprintf(pager_progress_str, sizeof(pager_progress_str), _("%ld%%"), percent); |
118 | 0 | } |
119 | 0 | else |
120 | 0 | { |
121 | 0 | const char *msg = (priv->top_line == 0) ? |
122 | | /* L10N: Status bar message: the entire email is visible in the pager */ |
123 | 0 | _("all") : |
124 | | /* L10N: Status bar message: the end of the email is visible in the pager */ |
125 | 0 | _("end"); |
126 | 0 | mutt_str_copy(pager_progress_str, msg, sizeof(pager_progress_str)); |
127 | 0 | } |
128 | |
|
129 | 0 | if ((priv->pview->mode == PAGER_MODE_EMAIL) || (priv->pview->mode == PAGER_MODE_ATTACH_E)) |
130 | 0 | { |
131 | 0 | int msg_in_pager = shared->mailbox_view ? shared->mailbox_view->msg_in_pager : -1; |
132 | |
|
133 | 0 | const char *c_pager_format = cs_subset_string(shared->sub, "pager_format"); |
134 | 0 | mutt_make_string(buf, sizeof(buf), win->state.cols, NONULL(c_pager_format), |
135 | 0 | shared->mailbox, msg_in_pager, shared->email, |
136 | 0 | MUTT_FORMAT_NO_FLAGS, pager_progress_str); |
137 | 0 | } |
138 | 0 | else |
139 | 0 | { |
140 | 0 | snprintf(buf, sizeof(buf), "%s (%s)", priv->pview->banner, pager_progress_str); |
141 | 0 | } |
142 | |
|
143 | 0 | if (!mutt_str_equal(buf, pbar_data->pager_format)) |
144 | 0 | { |
145 | 0 | mutt_str_replace(&pbar_data->pager_format, buf); |
146 | 0 | win->actions |= WA_REPAINT; |
147 | 0 | } |
148 | |
|
149 | 0 | return 0; |
150 | 0 | } |
151 | | |
152 | | /** |
153 | | * pbar_repaint - Repaint the Window - Implements MuttWindow::repaint() - @ingroup window_repaint |
154 | | */ |
155 | | static int pbar_repaint(struct MuttWindow *win) |
156 | 0 | { |
157 | 0 | struct PBarPrivateData *pbar_data = win->wdata; |
158 | |
|
159 | 0 | mutt_window_move(win, 0, 0); |
160 | 0 | mutt_curses_set_normal_backed_color_by_id(MT_COLOR_STATUS); |
161 | 0 | mutt_window_clrtoeol(win); |
162 | |
|
163 | 0 | mutt_window_move(win, 0, 0); |
164 | 0 | mutt_draw_statusline(win, win->state.cols, pbar_data->pager_format, |
165 | 0 | mutt_str_len(pbar_data->pager_format)); |
166 | 0 | mutt_curses_set_color_by_id(MT_COLOR_NORMAL); |
167 | |
|
168 | 0 | mutt_debug(LL_DEBUG5, "repaint done\n"); |
169 | 0 | return 0; |
170 | 0 | } |
171 | | |
172 | | /** |
173 | | * pbar_color_observer - Notification that a Color has changed - Implements ::observer_t - @ingroup observer_api |
174 | | */ |
175 | | static int pbar_color_observer(struct NotifyCallback *nc) |
176 | 0 | { |
177 | 0 | if (nc->event_type != NT_COLOR) |
178 | 0 | return 0; |
179 | 0 | if (!nc->global_data || !nc->event_data) |
180 | 0 | return -1; |
181 | | |
182 | 0 | struct EventColor *ev_c = nc->event_data; |
183 | 0 | enum ColorId cid = ev_c->cid; |
184 | |
|
185 | 0 | if ((cid != MT_COLOR_STATUS) && (cid != MT_COLOR_NORMAL) && (cid != MT_COLOR_MAX)) |
186 | 0 | return 0; |
187 | | |
188 | 0 | struct MuttWindow *win_pbar = nc->global_data; |
189 | 0 | win_pbar->actions |= WA_REPAINT; |
190 | 0 | mutt_debug(LL_DEBUG5, "color done, request WA_REPAINT\n"); |
191 | |
|
192 | 0 | return 0; |
193 | 0 | } |
194 | | |
195 | | /** |
196 | | * pbar_config_observer - Notification that a Config Variable has changed - Implements ::observer_t - @ingroup observer_api |
197 | | */ |
198 | | static int pbar_config_observer(struct NotifyCallback *nc) |
199 | 0 | { |
200 | 0 | if (nc->event_type != NT_CONFIG) |
201 | 0 | return 0; |
202 | 0 | if (!nc->global_data || !nc->event_data) |
203 | 0 | return -1; |
204 | | |
205 | 0 | struct EventConfig *ev_c = nc->event_data; |
206 | 0 | if (!mutt_str_equal(ev_c->name, "pager_format")) |
207 | 0 | return 0; |
208 | | |
209 | 0 | struct MuttWindow *win_pbar = nc->global_data; |
210 | 0 | win_pbar->actions |= WA_RECALC; |
211 | 0 | mutt_debug(LL_DEBUG5, "config done, request WA_RECALC\n"); |
212 | |
|
213 | 0 | return 0; |
214 | 0 | } |
215 | | |
216 | | /** |
217 | | * pbar_index_observer - Notification that the Index has changed - Implements ::observer_t - @ingroup observer_api |
218 | | * |
219 | | * This function receives two sorts of notification: |
220 | | * - NT_INDEX: |
221 | | * User has changed to a different Mailbox/Email |
222 | | * - NT_CONTEXT/NT_ACCOUNT/NT_MAILBOX/NT_EMAIL: |
223 | | * The state of an object has changed |
224 | | */ |
225 | | static int pbar_index_observer(struct NotifyCallback *nc) |
226 | 0 | { |
227 | 0 | if (!nc->global_data) |
228 | 0 | return -1; |
229 | | |
230 | 0 | struct MuttWindow *win_pbar = nc->global_data; |
231 | 0 | win_pbar->actions |= WA_RECALC; |
232 | 0 | mutt_debug(LL_DEBUG5, "index done, request WA_RECALC\n"); |
233 | |
|
234 | 0 | return 0; |
235 | 0 | } |
236 | | |
237 | | /** |
238 | | * pbar_pager_observer - Notification that the Pager has changed - Implements ::observer_t - @ingroup observer_api |
239 | | */ |
240 | | static int pbar_pager_observer(struct NotifyCallback *nc) |
241 | 0 | { |
242 | 0 | if (nc->event_type != NT_PAGER) |
243 | 0 | return 0; |
244 | 0 | if (!nc->global_data) |
245 | 0 | return -1; |
246 | | |
247 | 0 | struct MuttWindow *win_pbar = nc->global_data; |
248 | |
|
249 | 0 | if (nc->event_subtype & NT_PAGER_VIEW) |
250 | 0 | { |
251 | 0 | win_pbar->actions |= WA_RECALC; |
252 | 0 | mutt_debug(LL_DEBUG5, "pager done, request WA_RECALC\n"); |
253 | 0 | } |
254 | |
|
255 | 0 | return 0; |
256 | 0 | } |
257 | | |
258 | | /** |
259 | | * pbar_window_observer - Notification that a Window has changed - Implements ::observer_t - @ingroup observer_api |
260 | | */ |
261 | | static int pbar_window_observer(struct NotifyCallback *nc) |
262 | 0 | { |
263 | 0 | if (nc->event_type != NT_WINDOW) |
264 | 0 | return 0; |
265 | 0 | if (!nc->global_data || !nc->event_data) |
266 | 0 | return -1; |
267 | | |
268 | 0 | struct MuttWindow *win_pbar = nc->global_data; |
269 | 0 | struct EventWindow *ev_w = nc->event_data; |
270 | 0 | if (ev_w->win != win_pbar) |
271 | 0 | return 0; |
272 | | |
273 | 0 | if (nc->event_subtype == NT_WINDOW_STATE) |
274 | 0 | { |
275 | 0 | win_pbar->actions |= WA_RECALC | WA_REPAINT; |
276 | 0 | mutt_debug(LL_NOTIFY, "window state done, request WA_RECALC\n"); |
277 | 0 | } |
278 | 0 | else if (nc->event_subtype == NT_WINDOW_DELETE) |
279 | 0 | { |
280 | 0 | struct PBarPrivateData *pbar_data = win_pbar->wdata; |
281 | 0 | struct IndexSharedData *shared = pbar_data->shared; |
282 | |
|
283 | 0 | notify_observer_remove(NeoMutt->notify, pbar_color_observer, win_pbar); |
284 | 0 | notify_observer_remove(NeoMutt->notify, pbar_config_observer, win_pbar); |
285 | 0 | notify_observer_remove(shared->notify, pbar_index_observer, win_pbar); |
286 | 0 | notify_observer_remove(pbar_data->priv->notify, pbar_pager_observer, win_pbar); |
287 | 0 | notify_observer_remove(win_pbar->notify, pbar_window_observer, win_pbar); |
288 | |
|
289 | 0 | mutt_debug(LL_DEBUG5, "window delete done\n"); |
290 | 0 | } |
291 | |
|
292 | 0 | return 0; |
293 | 0 | } |
294 | | |
295 | | /** |
296 | | * pbar_data_free - Free the private data attached to the MuttWindow - Implements MuttWindow::wdata_free() - @ingroup window_wdata_free |
297 | | */ |
298 | | static void pbar_data_free(struct MuttWindow *win, void **ptr) |
299 | 0 | { |
300 | 0 | struct PBarPrivateData *pbar_data = *ptr; |
301 | |
|
302 | 0 | FREE(&pbar_data->pager_format); |
303 | |
|
304 | 0 | FREE(ptr); |
305 | 0 | } |
306 | | |
307 | | /** |
308 | | * pbar_data_new - Free the private data attached to the MuttWindow |
309 | | * @param shared Shared Index data |
310 | | * @param priv Private Index data |
311 | | * @retval ptr New PBar |
312 | | */ |
313 | | static struct PBarPrivateData *pbar_data_new(struct IndexSharedData *shared, |
314 | | struct PagerPrivateData *priv) |
315 | 0 | { |
316 | 0 | struct PBarPrivateData *pbar_data = mutt_mem_calloc(1, sizeof(struct PBarPrivateData)); |
317 | |
|
318 | 0 | pbar_data->shared = shared; |
319 | 0 | pbar_data->priv = priv; |
320 | |
|
321 | 0 | return pbar_data; |
322 | 0 | } |
323 | | |
324 | | /** |
325 | | * pbar_new - Create the Pager Bar |
326 | | * @param shared Shared Pager data |
327 | | * @param priv Private Pager data |
328 | | * @retval ptr New Pager Bar |
329 | | */ |
330 | | struct MuttWindow *pbar_new(struct IndexSharedData *shared, struct PagerPrivateData *priv) |
331 | 0 | { |
332 | 0 | struct MuttWindow *win_pbar = mutt_window_new(WT_STATUS_BAR, MUTT_WIN_ORIENT_VERTICAL, |
333 | 0 | MUTT_WIN_SIZE_FIXED, |
334 | 0 | MUTT_WIN_SIZE_UNLIMITED, 1); |
335 | |
|
336 | 0 | win_pbar->wdata = pbar_data_new(shared, priv); |
337 | 0 | win_pbar->wdata_free = pbar_data_free; |
338 | 0 | win_pbar->recalc = pbar_recalc; |
339 | 0 | win_pbar->repaint = pbar_repaint; |
340 | |
|
341 | 0 | notify_observer_add(NeoMutt->notify, NT_COLOR, pbar_color_observer, win_pbar); |
342 | 0 | notify_observer_add(NeoMutt->notify, NT_CONFIG, pbar_config_observer, win_pbar); |
343 | 0 | notify_observer_add(shared->notify, NT_ALL, pbar_index_observer, win_pbar); |
344 | 0 | notify_observer_add(priv->notify, NT_PAGER, pbar_pager_observer, win_pbar); |
345 | 0 | notify_observer_add(win_pbar->notify, NT_WINDOW, pbar_window_observer, win_pbar); |
346 | |
|
347 | 0 | return win_pbar; |
348 | 0 | } |