Coverage Report

Created: 2023-06-07 06:15

/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
}