NeoMutt  2025-12-11-911-gd8d604
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
preview.c
Go to the documentation of this file.
1
22
59
60#include "config.h"
61#include <stdbool.h>
62#include <stdio.h>
63#include "private.h"
64#include "mutt/lib.h"
65#include "email/lib.h"
66#include "core/lib.h"
67#include "gui/lib.h"
68#include "color/lib.h"
69#include "key/lib.h"
70
71// Maxixum body size in bytes to show in preview.
72const long MAX_PREVIEW_BODY_SIZE = 1024 * 1024 * 5;
73
86
100typedef int (*preview_function_t)(struct PreviewWindowData *wdata,
101 const struct KeyEvent *event);
102
111
115static void preview_wdata_free(struct MuttWindow *win, void **ptr)
116{
117 if (!ptr || !*ptr)
118 return;
119
120 FREE(ptr);
121}
122
128{
129 struct PreviewWindowData *wdata = mutt_mem_calloc(1, sizeof(struct PreviewWindowData));
130
131 return wdata;
132}
133
139static void draw_preview(struct MuttWindow *win, struct PreviewWindowData *wdata)
140{
141 struct Email *e = wdata->email;
142
143 // Reset preview window and status bar.
145 sbar_set_title(wdata->bar, _("-- Preview"));
146
147 // Check for valid content type and disposition
148 if ((e->body->disposition != DISP_INLINE) || (e->body->type != TYPE_TEXT))
149 {
150 mutt_error(_("Only inline attachments with content-type text/* can be previewed"));
151 return;
152 }
153
154 // Ensure file isn't to too large.
155 long file_size = mutt_file_get_size(e->body->filename);
156 if (file_size > MAX_PREVIEW_BODY_SIZE)
157 {
158 mutt_error(_("Email too large to preview"));
159 return;
160 }
161
162 FILE *fp = mutt_file_fopen(e->body->filename, "r");
163 if (!fp)
164 {
165 mutt_perror("%s", e->body->filename);
166 return;
167 }
168
169 wdata->more_content = false;
170
171 int content_lines = 0; // number of (wrapped) content lines
172 int row = 0; // window row to print
173 char *line = NULL;
174 size_t line_len = 0;
175 while ((line = mutt_file_read_line(line, &line_len, fp, NULL, MUTT_RL_NONE)))
176 {
177 size_t pos = 0;
178 bool text_left = true;
179 while (text_left)
180 {
181 /* Text wrapping loop
182 *
183 * Note: We need to do the text wrapping also for text outside the visible
184 * area to ensure the scrolling works correctly.
185 */
186
187 content_lines++;
188
189 mutt_str_expand_tabs(&line, &line_len, 8);
190
191 // Check how much of the string fits into the window width.
192 size_t width = 0;
193 size_t bytes = mutt_wstr_trunc(&line[pos], line_len - pos, win->state.cols, &width);
194
195 // If it doesn't fill the full width we're done wrapping.
196 if ((win->state.cols - width) > 0)
197 text_left = false;
198
199 // Only move the cursor and print if this line is currently visible.
200 if ((content_lines >= wdata->scroll_offset) && (row < win->state.rows))
201 {
202 int rc = mutt_window_move(win, row, 0);
203 if (rc == ERR)
204 mutt_warning(_("Failed to move cursor!"));
205
206 mutt_paddstr(win, win->state.cols, &line[pos]);
207
208 row++;
209 }
210
211 // Advance position in string.
212 pos += bytes;
213 }
214 }
215
216 FREE(&line);
217 mutt_file_fclose(&fp);
218
219 // Store content_lines for boundary checking
220 wdata->content_lines = content_lines;
221
222 // Clamp scroll_offset to prevent empty pages
223 // Maximum valid offset is when we can still show at least one line of content
224 if (content_lines > 0)
225 {
226 int max_offset = MAX(0, content_lines - win->state.rows);
227 if (wdata->scroll_offset > max_offset)
228 wdata->scroll_offset = max_offset;
229 }
230
231 // Show the scroll percentage in the status bar
232 if ((content_lines != 0) && (content_lines > win->state.rows))
233 {
234 char title[256] = { 0 };
235 double percent = 100.0;
236 if ((wdata->scroll_offset + row) < content_lines)
237 percent = 100.0 / content_lines * (wdata->scroll_offset + row);
238
239 // TODO: having the percentage right-aligned would be nice
240 snprintf(title, sizeof(title), _("-- Preview (%.0f%%)"), percent);
241 sbar_set_title(wdata->bar, title);
242
243 if (content_lines > (wdata->scroll_offset + row))
244 wdata->more_content = true;
245 }
246}
247
252{
253 if (nc->event_type != NT_COLOR)
254 return 0;
255 if (!nc->global_data || !nc->event_data)
256 return -1;
257
258 struct EventColor *ev_c = nc->event_data;
259 struct MuttWindow *win = nc->global_data;
260
261 enum ColorId cid = ev_c->cid;
262
263 switch (cid)
264 {
265 case MT_COLOR_BOLD:
266 case MT_COLOR_NORMAL:
267 case MT_COLOR_STATUS:
268 case MT_COLOR_MAX: // Sent on `uncolor *`
269 mutt_debug(LL_DEBUG5, "color done, request WA_REPAINT\n");
270 win->actions |= WA_REPAINT;
271 break;
272
273 default:
274 break;
275 }
276 return 0;
277}
278
283{
284 if (nc->event_type != NT_EMAIL)
285 return 0;
286 if (!nc->global_data)
287 return -1;
288
289 struct MuttWindow *win = nc->global_data;
290
291 win->actions |= WA_RECALC;
292 mutt_debug(LL_DEBUG5, "email done, request WA_RECALC\n");
293 return 0;
294}
295
300{
301 if (nc->event_type != NT_WINDOW)
302 return 0;
303 if (!nc->global_data || !nc->event_data)
304 return -1;
305
306 struct MuttWindow *win = nc->global_data;
307 struct EventWindow *ev_w = nc->event_data;
308 if (ev_w->win != win)
309 return 0;
310
312 {
314 mutt_debug(LL_DEBUG5, "window state done, request WA_RECALC\n");
315 }
316 else if (nc->event_subtype == NT_WINDOW_DELETE)
317 {
318 struct PreviewWindowData *wdata = win->wdata;
319
323 mutt_debug(LL_DEBUG5, "window delete done\n");
324 }
325
326 return 0;
327}
328
332static int preview_repaint(struct MuttWindow *win)
333{
334 struct PreviewWindowData *wdata = win->wdata;
335 draw_preview(win, wdata);
336
337 mutt_debug(LL_DEBUG5, "repaint done\n");
338 return 0;
339}
340
344static int preview_recalc(struct MuttWindow *win)
345{
346 if (!win)
347 return -1;
348
350 mutt_debug(LL_DEBUG5, "recalc done, request WA_REPAINT\n");
351 return 0;
352}
353
382
386static int preview_page_up(struct PreviewWindowData *wdata, const struct KeyEvent *event)
387{
388 if (wdata->scroll_offset <= 0)
389 {
390 if (event->count == 0)
391 mutt_message(_("Top of message is shown"));
392 return FR_NO_ACTION;
393 }
394
395 const int count = MAX(event->count, 1);
396 const int page = MAX(wdata->win->state.rows - 1, 1);
397 wdata->scroll_offset -= count * page;
398 if (wdata->scroll_offset < 0)
399 wdata->scroll_offset = 0;
400 draw_preview(wdata->win, wdata);
401
402 return FR_SUCCESS;
403}
404
408static int preview_page_down(struct PreviewWindowData *wdata, const struct KeyEvent *event)
409{
410 if (!wdata->more_content)
411 {
412 if (event->count == 0)
413 mutt_message(_("Bottom of message is shown"));
414 return FR_NO_ACTION;
415 }
416
417 const int count = MAX(event->count, 1);
418 const int page = MAX(wdata->win->state.rows - 1, 1);
419 wdata->scroll_offset += count * page;
420 draw_preview(wdata->win, wdata);
421
422 return FR_SUCCESS;
423}
424
428static const struct PreviewFunction PreviewFunctions[] = {
429 // clang-format off
430 { OP_PREVIEW_PAGE_DOWN, preview_page_down },
431 { OP_PREVIEW_PAGE_UP, preview_page_up },
432 { 0, NULL },
433 // clang-format on
434};
435
439int preview_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
440{
441 if (!event || !win || !win->wdata)
442 return FR_UNKNOWN;
443
444 const int op = event->op;
445 int rc = FR_UNKNOWN;
446 for (size_t i = 0; PreviewFunctions[i].op != OP_NULL; i++)
447 {
448 const struct PreviewFunction *fn = &PreviewFunctions[i];
449 if (fn->op == op)
450 {
451 struct PreviewWindowData *wdata = win->wdata;
452 rc = fn->function(wdata, event);
453 break;
454 }
455 }
456
457 if (rc == FR_UNKNOWN) // Not our function
458 return rc;
459
460 const char *result = dispatcher_get_retval_name(rc);
461 mutt_debug(LL_DEBUG1, "Handled %s (%d) -> %s\n", opcodes_get_name(op), op, NONULL(result));
462
464 return rc;
465}
Color and attribute parsing.
void mutt_color_observer_remove(observer_t callback, void *global_data)
Remove an observer.
Definition notify.c:73
void mutt_color_observer_add(observer_t callback, void *global_data)
Add an observer.
Definition notify.c:62
ColorId
List of all coloured objects.
Definition color.h:35
@ MT_COLOR_MAX
Definition color.h:97
@ MT_COLOR_STATUS
Status bar (takes a pattern)
Definition color.h:78
@ MT_COLOR_BOLD
Bold text.
Definition color.h:40
@ MT_COLOR_NORMAL
Plain text.
Definition color.h:53
Compose Private Data.
Convenience wrapper for the core headers.
size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
Work out how to truncate a widechar string.
Definition curs_lib.c:386
void mutt_paddstr(struct MuttWindow *win, int n, const char *s)
Display a string on screen, padded if necessary.
Definition curs_lib.c:344
void mutt_str_expand_tabs(char **str, size_t *len, int tabwidth)
Convert tabs to spaces in a string.
Definition curs_lib.c:597
const char * dispatcher_get_retval_name(int rv)
Get the name of a return value.
Definition dispatcher.c:55
void dispatcher_flush_on_error(int rv)
Flush pending keys after a dispatch error.
Definition dispatcher.c:65
@ FR_SUCCESS
Valid function - successfully performed.
Definition dispatcher.h:40
@ FR_UNKNOWN
Unknown function.
Definition dispatcher.h:34
@ FR_NO_ACTION
Valid function - no action performed.
Definition dispatcher.h:38
Structs that make up an email.
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition file.c:678
long mutt_file_get_size(const char *path)
Get the size of a file.
Definition file.c:1414
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
@ MUTT_RL_NONE
No flags are set.
Definition file.h:43
int preview_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
Perform a preview function - Implements function_dispatcher_t -.
Definition preview.c:439
#define mutt_warning(...)
Definition logging2.h:92
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
static int preview_email_observer(struct NotifyCallback *nc)
Notification that the Email has changed - Implements observer_t -.
Definition preview.c:282
static int preview_window_observer(struct NotifyCallback *nc)
Notification that a Window has changed - Implements observer_t -.
Definition preview.c:299
static int preview_color_observer(struct NotifyCallback *nc)
Notification that a Color has changed - Implements observer_t -.
Definition preview.c:251
static int preview_page_down(struct PreviewWindowData *wdata, const struct KeyEvent *event)
Show the next page of the message - Implements preview_function_t -.
Definition preview.c:408
static int preview_page_up(struct PreviewWindowData *wdata, const struct KeyEvent *event)
Show the previous page of the message - Implements preview_function_t -.
Definition preview.c:386
static int preview_recalc(struct MuttWindow *win)
Recalculate the Window data - Implements MuttWindow::recalc() -.
Definition preview.c:344
static int preview_repaint(struct MuttWindow *win)
Repaint the Window - Implements MuttWindow::repaint() -.
Definition preview.c:332
static void preview_wdata_free(struct MuttWindow *win, void **ptr)
Free the Preview Data - Implements MuttWindow::wdata_free() -.
Definition preview.c:115
Convenience wrapper for the gui headers.
Manage keymappings.
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition memory.c:76
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MAX(a, b)
Return the maximum of two values.
Definition memory.h:38
@ TYPE_TEXT
Type: 'text/*'.
Definition mime.h:38
@ DISP_INLINE
Content is inline.
Definition mime.h:62
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
bool notify_observer_remove(struct Notify *notify, const observer_t callback, const void *global_data)
Remove an observer from an object.
Definition notify.c:230
bool notify_observer_add(struct Notify *notify, enum NotifyType type, observer_t callback, void *global_data)
Add an observer to an object.
Definition notify.c:191
void mutt_window_clear(struct MuttWindow *win)
Clear a Window.
struct MuttWindow * mutt_window_new(enum WindowType type, enum MuttWindowOrientation orient, enum MuttWindowSize size, int cols, int rows)
Create a new Window.
int mutt_window_move(struct MuttWindow *win, int row, int col)
Move the cursor in a Window.
@ WT_CUSTOM
Window with a custom drawing function.
Definition mutt_window.h:95
@ MUTT_WIN_ORIENT_VERTICAL
Window uses all available vertical space.
Definition mutt_window.h:38
@ WA_REPAINT
Redraw the contents of the Window.
@ WA_RECALC
Recalculate the contents of the Window.
@ NT_WINDOW_STATE
Window state has changed, e.g. WN_VISIBLE.
@ NT_WINDOW_DELETE
Window is about to be deleted.
#define MUTT_WIN_SIZE_UNLIMITED
Use as much space as possible.
Definition mutt_window.h:52
@ MUTT_WIN_SIZE_MAXIMISE
Window wants as much space as possible.
Definition mutt_window.h:48
@ NT_WINDOW
MuttWindow has changed, NotifyWindow, EventWindow.
Definition notify_type.h:58
@ NT_COLOR
Colour has changed, NotifyColor, EventColor.
Definition notify_type.h:41
@ NT_EMAIL
Email has changed, NotifyEmail, EventEmail.
Definition notify_type.h:44
@ NT_ALL
Register for all notifications.
Definition notify_type.h:35
const char * opcodes_get_name(int op)
Get the name of an opcode.
Definition opcodes.c:48
struct MuttWindow * preview_window_new(struct Email *e, struct MuttWindow *bar)
Create the preview window.
Definition preview.c:359
static const struct PreviewFunction PreviewFunctions[]
All the functions that the preview window supports.
Definition preview.c:428
static void draw_preview(struct MuttWindow *win, struct PreviewWindowData *wdata)
Write the message preview to the compose window.
Definition preview.c:139
const long MAX_PREVIEW_BODY_SIZE
Definition preview.c:72
static struct PreviewWindowData * preview_wdata_new(void)
Create new Preview Data.
Definition preview.c:127
int(* preview_function_t)(struct PreviewWindowData *wdata, const struct KeyEvent *event)
Definition preview.c:100
void sbar_set_title(struct MuttWindow *win, const char *title)
Set the title for the Simple Bar.
Definition sbar.c:227
#define NONULL(x)
Definition string2.h:44
unsigned int disposition
content-disposition, ContentDisposition
Definition body.h:42
unsigned int type
content-type primary type, ContentType
Definition body.h:40
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
The envelope/body of an email.
Definition email.h:39
struct Body * body
List of MIME parts.
Definition email.h:69
struct Notify * notify
Notifications: NotifyEmail, EventEmail.
Definition email.h:73
An Event that happened to a Colour.
Definition notify2.h:52
enum ColorId cid
Colour ID that has changed.
Definition notify2.h:53
An Event that happened to a Window.
struct MuttWindow * win
Window that changed.
An event such as a keypress.
Definition get.h:75
int count
Optional count prefix, e.g. 3 for 3j
Definition get.h:78
int(* repaint)(struct MuttWindow *win)
struct WindowState state
Current state of the Window.
void * wdata
Private data.
struct Notify * notify
Notifications: NotifyWindow, EventWindow.
int(* recalc)(struct MuttWindow *win)
void(* wdata_free)(struct MuttWindow *win, void **ptr)
WindowActionFlags actions
Actions to be performed, e.g. WA_RECALC.
Data passed to a notification function.
Definition observer.h:34
void * event_data
Data from notify_send()
Definition observer.h:38
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition observer.h:36
int event_subtype
Send: Event subtype, e.g. NT_ACCOUNT_ADD.
Definition observer.h:37
void * global_data
Data from notify_observer_add()
Definition observer.h:39
A message preview function.
Definition preview.c:107
int op
Op code, e.g. OP_NEXT_PAGE.
Definition preview.c:108
preview_function_t function
Function to call.
Definition preview.c:109
Data to fill the Preview Window.
Definition preview.c:78
struct MuttWindow * win
Window holding the message preview.
Definition preview.c:81
struct MuttWindow * bar
Status bar above the preview window.
Definition preview.c:82
int content_lines
Total number of wrapped content lines.
Definition preview.c:84
int scroll_offset
Scroll offset.
Definition preview.c:80
struct Email * email
Email being composed.
Definition preview.c:79
bool more_content
Is there more content to scroll down to?
Definition preview.c:83
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition mutt_window.h:60
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition mutt_window.h:61