NeoMutt  2025-09-05-43-g177ed6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
msgwin.c
Go to the documentation of this file.
1
22
84
85#include "config.h"
86#include <stdbool.h>
87#include <stddef.h>
88#include <string.h>
89#include <wchar.h>
90#include "mutt/lib.h"
91#include "msgwin.h"
92#include "color/lib.h"
93#include "msgcont.h"
94#include "msgwin_wdata.h"
95#include "mutt_curses.h"
96#include "mutt_window.h"
97
106void measure(struct MwCharArray *chars, const char *str, const struct AttrColor *ac_color)
107{
108 if (!str || !*str)
109 return;
110
111 mbstate_t mbstate = { 0 };
112 struct MwChar mwc = { 0 };
113
114 size_t str_len = mutt_str_len(str);
115
116 while (*str && (str_len > 0))
117 {
118 wchar_t wc = L'\0';
119 size_t consumed = mbrtowc(&wc, str, str_len, &mbstate);
120 if (consumed == 0)
121 break;
122
123 if (consumed == ICONV_ILLEGAL_SEQ)
124 {
125 memset(&mbstate, 0, sizeof(mbstate));
126 wc = ReplacementChar;
127 consumed = 1;
128 }
129 else if (consumed == ICONV_BUF_TOO_SMALL)
130 {
131 wc = ReplacementChar;
132 consumed = str_len;
133 }
134
135 int wchar_width = wcwidth(wc);
136 if (wchar_width < 0)
137 wchar_width = 1;
138
139 if (wc == 0xfe0f) // Emoji variation
140 {
141 int size = ARRAY_SIZE(chars);
142 if (size > 0)
143 {
144 struct MwChar *es_prev = ARRAY_GET(chars, size - 1);
145 if (es_prev->width == 1)
146 es_prev->width = 2;
147 }
148 }
149
150 mwc = (struct MwChar) { wchar_width, consumed, ac_color };
151 ARRAY_ADD(chars, mwc);
152
153 str += consumed;
154 str_len -= consumed;
155 }
156}
157
161static int msgwin_recalc(struct MuttWindow *win)
162{
163 win->actions |= WA_REPAINT;
164 mutt_debug(LL_DEBUG5, "recalc done, request WA_REPAINT\n");
165 return 0;
166}
167
178int msgwin_calc_rows(struct MsgWinWindowData *wdata, int cols, const char *str)
179{
180 if (!wdata || !str || !*str)
181 return 0;
182
183 for (int i = 0; i < MSGWIN_MAX_ROWS; i++)
184 {
185 ARRAY_FREE(&wdata->rows[i]);
186 }
187
188 int width = 0;
189 int offset = 0;
190 int row = 0;
191 bool new_row = false;
192
193 struct MwChunk *chunk = NULL;
194 struct MwChar *mwc = NULL;
195 ARRAY_FOREACH(mwc, &wdata->chars)
196 {
197 const bool nl = (mwc->bytes == 1) && (str[offset] == '\n');
198 if (nl)
199 {
200 new_row = true;
201 offset += mwc->bytes;
202 continue;
203 }
204
205 if (((width + mwc->width) > cols) || new_row)
206 {
207 // ROW IS FULL
208 new_row = false;
209
210 row++;
211 if (row >= MSGWIN_MAX_ROWS)
212 {
213 // NO MORE ROOM
214 break;
215 }
216
217 // Start a new row
218 struct MwChunk tmp = { offset, mwc->bytes, mwc->width, mwc->ac_color };
219
220 mutt_debug(LL_DEBUG5, "row = %d\n", row);
221 ARRAY_ADD(&wdata->rows[row], tmp);
222 chunk = ARRAY_LAST(&wdata->rows[row]);
223
224 width = 0;
225 }
226 else if (!chunk || (mwc->ac_color != chunk->ac_color))
227 {
228 // CHANGE OF COLOUR
229 struct MwChunk tmp = { offset, mwc->bytes, mwc->width, mwc->ac_color };
230 ARRAY_ADD(&wdata->rows[row], tmp);
231 chunk = ARRAY_LAST(&wdata->rows[row]);
232 }
233 else
234 {
235 // MORE OF THE SAME
236 chunk->bytes += mwc->bytes;
237 chunk->width += mwc->width;
238 }
239
240 offset += mwc->bytes;
241 width += mwc->width;
242 }
243
244 mutt_debug(LL_DEBUG5, "msgwin_calc_rows() => %d\n", row + 1);
245 return row + 1;
246}
247
251static int msgwin_repaint(struct MuttWindow *win)
252{
253 struct MsgWinWindowData *wdata = win->wdata;
254
255 const char *str = buf_string(wdata->text);
256 for (int i = 0; i < MSGWIN_MAX_ROWS; i++)
257 {
258 mutt_window_move(win, i, 0);
259 if (ARRAY_EMPTY(&wdata->rows[i]))
260 break;
261
262 struct MwChunk *chunk = NULL;
263 ARRAY_FOREACH(chunk, &wdata->rows[i])
264 {
266 mutt_window_addnstr(win, str + chunk->offset, chunk->bytes);
267 }
270 }
273
274 mutt_window_get_coords(win, &wdata->row, &wdata->col);
275
276 mutt_debug(LL_DEBUG5, "msgwin repaint done\n");
277 return 0;
278}
279
283static bool msgwin_recursor(struct MuttWindow *win)
284{
285 struct MsgWinWindowData *wdata = win->wdata;
286
287 mutt_window_move(win, wdata->row, wdata->col);
289
290 mutt_debug(LL_DEBUG5, "msgwin recursor done\n");
291 return true;
292}
293
303void msgwin_set_rows(struct MuttWindow *win, short rows)
304{
305 if (!win)
306 win = msgcont_get_msgwin();
307 if (!win)
308 return;
309
311
312 if (rows != win->state.rows)
313 {
314 win->req_rows = rows;
315 mutt_window_reflow(NULL);
316 }
317}
318
328{
329 if (nc->event_type != NT_WINDOW)
330 return 0;
331 if (!nc->global_data || !nc->event_data)
332 return -1;
333
334 struct MuttWindow *win = nc->global_data;
335 struct EventWindow *ev_w = nc->event_data;
336 if (ev_w->win != win)
337 return 0;
338
340 {
341 if (ev_w->flags & WN_HIDDEN)
342 {
344 }
345
346 if (ev_w->flags & (WN_NARROWER | WN_WIDER))
347 {
348 struct MsgWinWindowData *wdata = win->wdata;
350 buf_string(wdata->text)));
351 win->actions |= WA_RECALC;
352 }
353 else
354 {
355 win->actions |= WA_REPAINT;
356 }
357 mutt_debug(LL_DEBUG5, "window state done, request WA_RECALC\n");
358 }
359 else if (nc->event_subtype == NT_WINDOW_DELETE)
360 {
362 mutt_debug(LL_DEBUG5, "window delete done\n");
363 }
364 return 0;
365}
366
371struct MuttWindow *msgwin_new(bool interactive)
372{
375
376 struct MsgWinWindowData *wdata = msgwin_wdata_new();
377
378 win->wdata = wdata;
380 win->recalc = msgwin_recalc;
381 win->repaint = msgwin_repaint;
382
383 if (interactive)
385
386 // Copy the container's dimensions
387 win->state = MessageContainer->state;
388
390
391 return win;
392}
393
401const char *msgwin_get_text(struct MuttWindow *win)
402{
403 if (!win)
404 win = msgcont_get_msgwin();
405 if (!win)
406 return NULL;
407
408 struct MsgWinWindowData *wdata = win->wdata;
409
410 return buf_string(wdata->text);
411}
412
419void msgwin_add_text(struct MuttWindow *win, const char *text, const struct AttrColor *ac_color)
420{
421 if (!win)
422 win = msgcont_get_msgwin();
423 if (!win)
424 return;
425
426 struct MsgWinWindowData *wdata = win->wdata;
427
428 if (text)
429 {
430 buf_addstr(wdata->text, text);
431 measure(&wdata->chars, text, ac_color);
432 mutt_debug(LL_DEBUG5, "MW ADD: %zu, %s\n", buf_len(wdata->text),
433 buf_string(wdata->text));
434 }
435 else
436 {
437 int rows = msgwin_calc_rows(wdata, win->state.cols, buf_string(wdata->text));
438 msgwin_set_rows(win, rows);
439 win->actions |= WA_RECALC;
440 }
441}
442
450void msgwin_add_text_n(struct MuttWindow *win, const char *text, int bytes,
451 const struct AttrColor *ac_color)
452{
453 if (!win)
454 win = msgcont_get_msgwin();
455 if (!win)
456 return;
457
458 struct MsgWinWindowData *wdata = win->wdata;
459
460 if (text)
461 {
462 const char *dptr = wdata->text->dptr;
463 buf_addstr_n(wdata->text, text, bytes);
464 measure(&wdata->chars, dptr, ac_color);
465 mutt_debug(LL_DEBUG5, "MW ADD: %zu, %s\n", buf_len(wdata->text),
466 buf_string(wdata->text));
467 }
468 else
469 {
470 int rows = msgwin_calc_rows(wdata, win->state.cols, buf_string(wdata->text));
471 msgwin_set_rows(win, rows);
472 win->actions |= WA_RECALC;
473 }
474}
475
484void msgwin_set_text(struct MuttWindow *win, const char *text, enum ColorId color)
485{
486 if (!win)
487 win = msgcont_get_msgwin();
488 if (!win)
489 return;
490
491 struct MsgWinWindowData *wdata = win->wdata;
492
493 if (mutt_str_equal(buf_string(wdata->text), text))
494 return;
495
496 buf_strcpy(wdata->text, text);
497 ARRAY_FREE(&wdata->chars);
498 if (wdata->text)
499 {
500 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
501 const struct AttrColor *ac_color = simple_color_get(color);
502 const struct AttrColor *ac_merge = merged_color_overlay(ac_normal, ac_color);
503
504 measure(&wdata->chars, buf_string(wdata->text), ac_merge);
505 }
506
507 mutt_debug(LL_DEBUG5, "MW SET: %zu, %s\n", buf_len(wdata->text),
508 buf_string(wdata->text));
509
510 int rows = msgwin_calc_rows(wdata, win->state.cols, buf_string(wdata->text));
511 msgwin_set_rows(win, rows);
512 win->actions |= WA_RECALC;
513}
514
520{
522}
523
531{
532 return msgcont_get_msgwin();
533}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:156
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:214
#define ARRAY_LAST(head)
Convenience method to get the last element.
Definition array.h:144
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition array.h:74
#define ARRAY_SIZE(head)
The number of elements stored.
Definition array.h:87
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:204
#define ARRAY_GET(head, idx)
Return the element at index.
Definition array.h:109
size_t buf_addstr_n(struct Buffer *buf, const char *s, size_t len)
Add a string to a Buffer, expanding it if necessary.
Definition buffer.c:96
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:491
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
Color and attribute parsing.
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition simple.c:95
ColorId
List of all coloured objects.
Definition color.h:36
@ MT_COLOR_NORMAL
Plain text.
Definition color.h:54
#define mutt_debug(LEVEL,...)
Definition logging2.h:90
static int msgwin_window_observer(struct NotifyCallback *nc)
Notification that a Window has changed - Implements observer_t -.
Definition msgwin.c:327
static int msgwin_recalc(struct MuttWindow *win)
Recalculate the display of the Message Window - Implements MuttWindow::recalc() -.
Definition msgwin.c:161
static bool msgwin_recursor(struct MuttWindow *win)
Recursor the Message Window - Implements MuttWindow::recursor() -.
Definition msgwin.c:283
static int msgwin_repaint(struct MuttWindow *win)
Redraw the Message Window - Implements MuttWindow::repaint() -.
Definition msgwin.c:251
void msgwin_wdata_free(struct MuttWindow *win, void **ptr)
Free the private data - Implements MuttWindow::wdata_free() -.
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:48
#define CLAMP(val, lo, hi)
Definition memory.h:38
const struct AttrColor * merged_color_overlay(const struct AttrColor *base, const struct AttrColor *over)
Combine two colours.
Definition merged.c:107
struct MuttWindow * MessageContainer
Window acting as a stack for the message windows.
Definition msgcont.c:40
struct MuttWindow * msgcont_get_msgwin(void)
Get the Message Window.
Definition msgcont.c:117
Message Window.
struct MuttWindow * msgwin_new(bool interactive)
Create the Message Window.
Definition msgwin.c:371
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition msgwin.c:519
void msgwin_add_text(struct MuttWindow *win, const char *text, const struct AttrColor *ac_color)
Add text to the Message Window.
Definition msgwin.c:419
int msgwin_calc_rows(struct MsgWinWindowData *wdata, int cols, const char *str)
How many rows will a string need?
Definition msgwin.c:178
struct MuttWindow * msgwin_get_window(void)
Get the Message Window pointer.
Definition msgwin.c:530
void msgwin_add_text_n(struct MuttWindow *win, const char *text, int bytes, const struct AttrColor *ac_color)
Add some text to the Message Window.
Definition msgwin.c:450
void measure(struct MwCharArray *chars, const char *str, const struct AttrColor *ac_color)
Measure a string in bytes and cells.
Definition msgwin.c:106
void msgwin_set_text(struct MuttWindow *win, const char *text, enum ColorId color)
Set the text for the Message Window.
Definition msgwin.c:484
const char * msgwin_get_text(struct MuttWindow *win)
Get the text from the Message Window.
Definition msgwin.c:401
void msgwin_set_rows(struct MuttWindow *win, short rows)
Resize the Message Window.
Definition msgwin.c:303
Message Window.
struct MsgWinWindowData * msgwin_wdata_new(void)
Create new private data for the Message Window.
Message Window private data.
#define MSGWIN_MAX_ROWS
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition charset.c:61
#define ICONV_BUF_TOO_SMALL
Error value for iconv() - Buffer too small.
Definition charset.h:98
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition charset.h:96
Convenience wrapper for the library headers.
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
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:660
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:498
enum MuttCursorState mutt_curses_set_cursor(enum MuttCursorState state)
Set the cursor state.
Definition mutt_curses.c:94
const struct AttrColor * mutt_curses_set_color_by_id(enum ColorId cid)
Set the colour and attributes by the Colour ID.
Definition mutt_curses.c:79
void mutt_curses_set_color(const struct AttrColor *ac)
Set the colour and attributes for text.
Definition mutt_curses.c:38
Define wrapper functions around Curses.
@ MUTT_CURSOR_VISIBLE
Display a normal cursor.
Definition mutt_curses.h:66
void mutt_window_reflow(struct MuttWindow *win)
Resize a Window and its children.
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.
void mutt_window_get_coords(struct MuttWindow *win, int *row, int *col)
Get the cursor position in the Window.
int mutt_window_addnstr(struct MuttWindow *win, const char *str, int num)
Write a partial string to a Window.
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Window management.
#define WA_RECALC
Recalculate the contents of the Window.
#define WN_WIDER
Window became wider.
@ WT_MESSAGE
Window for messages/errors.
Definition mutt_window.h:99
@ MUTT_WIN_ORIENT_VERTICAL
Window uses all available vertical space.
Definition mutt_window.h:39
@ NT_WINDOW_STATE
Window state has changed, e.g. WN_VISIBLE.
@ NT_WINDOW_DELETE
Window is about to be deleted.
#define WN_HIDDEN
Window became hidden.
#define WA_REPAINT
Redraw the contents of the Window.
#define MUTT_WIN_SIZE_UNLIMITED
Use as much space as possible.
Definition mutt_window.h:53
@ MUTT_WIN_SIZE_FIXED
Window has a fixed size.
Definition mutt_window.h:48
#define WN_NARROWER
Window became narrower.
@ NT_WINDOW
MuttWindow has changed, NotifyWindow, EventWindow.
Definition notify_type.h:57
A curses colour and its attributes.
Definition attr.h:66
char * dptr
Current read/write position.
Definition buffer.h:38
An Event that happened to a Window.
struct MuttWindow * win
Window that changed.
WindowNotifyFlags flags
Attributes of Window that changed.
Message Window private Window data.
struct Buffer * text
Cached display string.
struct MwCharArray chars
Text: Breakdown of bytes and widths.
struct MwChunkArray rows[MSGWIN_MAX_ROWS]
String byte counts for each row.
int row
Cursor row.
int col
Cursor column.
int(* repaint)(struct MuttWindow *win)
struct WindowState state
Current state of the Window.
void * wdata
Private data.
struct Notify * notify
Notifications: NotifyWindow, EventWindow.
short req_rows
Number of rows required.
int(* recalc)(struct MuttWindow *win)
void(* wdata_free)(struct MuttWindow *win, void **ptr)
WindowActionFlags actions
Actions to be performed, e.g. WA_RECALC.
bool(* recursor)(struct MuttWindow *win)
Description of a single character.
const struct AttrColor * ac_color
Colour to use.
unsigned char width
Width in screen cells.
unsigned char bytes
Number of bytes to represent.
A block of characters of one colour.
unsigned short bytes
Number of bytes in the row.
unsigned short width
Width of row in screen cells.
unsigned short offset
Offset into MsgWinWindowData.text.
const struct AttrColor * ac_color
Colour to use.
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
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition mutt_window.h:61
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition mutt_window.h:62