NeoMutt  2025-09-05-55-g97fc89
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 <string.h>
88#include <wchar.h>
89#include "mutt/lib.h"
90#include "msgwin.h"
91#include "color/lib.h"
92#include "msgcont.h"
93#include "msgwin_wdata.h"
94#include "mutt_curses.h"
95#include "mutt_window.h"
96
105void measure(struct MwCharArray *chars, const char *str, const struct AttrColor *ac_color)
106{
107 if (!str || !*str)
108 return;
109
110 mbstate_t mbstate = { 0 };
111 struct MwChar mwc = { 0 };
112
113 size_t str_len = mutt_str_len(str);
114
115 while (*str && (str_len > 0))
116 {
117 wchar_t wc = L'\0';
118 size_t consumed = mbrtowc(&wc, str, str_len, &mbstate);
119 if (consumed == 0)
120 break;
121
122 if (consumed == ICONV_ILLEGAL_SEQ)
123 {
124 memset(&mbstate, 0, sizeof(mbstate));
125 wc = ReplacementChar;
126 consumed = 1;
127 }
128 else if (consumed == ICONV_BUF_TOO_SMALL)
129 {
130 wc = ReplacementChar;
131 consumed = str_len;
132 }
133
134 int wchar_width = wcwidth(wc);
135 if (wchar_width < 0)
136 wchar_width = 1;
137
138 if (wc == 0xfe0f) // Emoji variation
139 {
140 int size = ARRAY_SIZE(chars);
141 if (size > 0)
142 {
143 struct MwChar *es_prev = ARRAY_GET(chars, size - 1);
144 if (es_prev->width == 1)
145 es_prev->width = 2;
146 }
147 }
148
149 mwc = (struct MwChar) { wchar_width, consumed, ac_color };
150 ARRAY_ADD(chars, mwc);
151
152 str += consumed;
153 str_len -= consumed;
154 }
155}
156
160static int msgwin_recalc(struct MuttWindow *win)
161{
162 win->actions |= WA_REPAINT;
163 mutt_debug(LL_DEBUG5, "recalc done, request WA_REPAINT\n");
164 return 0;
165}
166
177int msgwin_calc_rows(struct MsgWinWindowData *wdata, int cols, const char *str)
178{
179 if (!wdata || !str || !*str)
180 return 0;
181
182 for (int i = 0; i < MSGWIN_MAX_ROWS; i++)
183 {
184 ARRAY_FREE(&wdata->rows[i]);
185 }
186
187 int width = 0;
188 int offset = 0;
189 int row = 0;
190 bool new_row = false;
191
192 struct MwChunk *chunk = NULL;
193 struct MwChar *mwc = NULL;
194 ARRAY_FOREACH(mwc, &wdata->chars)
195 {
196 const bool nl = (mwc->bytes == 1) && (str[offset] == '\n');
197 if (nl)
198 {
199 new_row = true;
200 offset += mwc->bytes;
201 continue;
202 }
203
204 if (((width + mwc->width) > cols) || new_row)
205 {
206 // ROW IS FULL
207 new_row = false;
208
209 row++;
210 if (row >= MSGWIN_MAX_ROWS)
211 {
212 // NO MORE ROOM
213 break;
214 }
215
216 // Start a new row
217 struct MwChunk tmp = { offset, mwc->bytes, mwc->width, mwc->ac_color };
218
219 mutt_debug(LL_DEBUG5, "row = %d\n", row);
220 ARRAY_ADD(&wdata->rows[row], tmp);
221 chunk = ARRAY_LAST(&wdata->rows[row]);
222
223 width = 0;
224 }
225 else if (!chunk || (mwc->ac_color != chunk->ac_color))
226 {
227 // CHANGE OF COLOUR
228 struct MwChunk tmp = { offset, mwc->bytes, mwc->width, mwc->ac_color };
229 ARRAY_ADD(&wdata->rows[row], tmp);
230 chunk = ARRAY_LAST(&wdata->rows[row]);
231 }
232 else
233 {
234 // MORE OF THE SAME
235 chunk->bytes += mwc->bytes;
236 chunk->width += mwc->width;
237 }
238
239 offset += mwc->bytes;
240 width += mwc->width;
241 }
242
243 mutt_debug(LL_DEBUG5, "msgwin_calc_rows() => %d\n", row + 1);
244 return row + 1;
245}
246
250static int msgwin_repaint(struct MuttWindow *win)
251{
252 struct MsgWinWindowData *wdata = win->wdata;
253
254 const char *str = buf_string(wdata->text);
255 for (int i = 0; i < MSGWIN_MAX_ROWS; i++)
256 {
257 mutt_window_move(win, i, 0);
258 if (ARRAY_EMPTY(&wdata->rows[i]))
259 break;
260
261 struct MwChunk *chunk = NULL;
262 ARRAY_FOREACH(chunk, &wdata->rows[i])
263 {
265 mutt_window_addnstr(win, str + chunk->offset, chunk->bytes);
266 }
269 }
272
273 mutt_window_get_coords(win, &wdata->row, &wdata->col);
274
275 mutt_debug(LL_DEBUG5, "msgwin repaint done\n");
276 return 0;
277}
278
282static bool msgwin_recursor(struct MuttWindow *win)
283{
284 struct MsgWinWindowData *wdata = win->wdata;
285
286 mutt_window_move(win, wdata->row, wdata->col);
288
289 mutt_debug(LL_DEBUG5, "msgwin recursor done\n");
290 return true;
291}
292
302void msgwin_set_rows(struct MuttWindow *win, short rows)
303{
304 if (!win)
305 win = msgcont_get_msgwin();
306 if (!win)
307 return;
308
310
311 if (rows != win->state.rows)
312 {
313 win->req_rows = rows;
314 mutt_window_reflow(NULL);
315 }
316}
317
327{
328 if (nc->event_type != NT_WINDOW)
329 return 0;
330 if (!nc->global_data || !nc->event_data)
331 return -1;
332
333 struct MuttWindow *win = nc->global_data;
334 struct EventWindow *ev_w = nc->event_data;
335 if (ev_w->win != win)
336 return 0;
337
339 {
340 if (ev_w->flags & WN_HIDDEN)
341 {
343 }
344
345 if (ev_w->flags & (WN_NARROWER | WN_WIDER))
346 {
347 struct MsgWinWindowData *wdata = win->wdata;
349 buf_string(wdata->text)));
350 win->actions |= WA_RECALC;
351 }
352 else
353 {
354 win->actions |= WA_REPAINT;
355 }
356 mutt_debug(LL_DEBUG5, "window state done, request WA_RECALC\n");
357 }
358 else if (nc->event_subtype == NT_WINDOW_DELETE)
359 {
361 mutt_debug(LL_DEBUG5, "window delete done\n");
362 }
363 return 0;
364}
365
370struct MuttWindow *msgwin_new(bool interactive)
371{
374
375 struct MsgWinWindowData *wdata = msgwin_wdata_new();
376
377 win->wdata = wdata;
379 win->recalc = msgwin_recalc;
380 win->repaint = msgwin_repaint;
381
382 if (interactive)
384
385 // Copy the container's dimensions
386 win->state = MessageContainer->state;
387
389
390 return win;
391}
392
400const char *msgwin_get_text(struct MuttWindow *win)
401{
402 if (!win)
403 win = msgcont_get_msgwin();
404 if (!win)
405 return NULL;
406
407 struct MsgWinWindowData *wdata = win->wdata;
408
409 return buf_string(wdata->text);
410}
411
418void msgwin_add_text(struct MuttWindow *win, const char *text, const struct AttrColor *ac_color)
419{
420 if (!win)
421 win = msgcont_get_msgwin();
422 if (!win)
423 return;
424
425 struct MsgWinWindowData *wdata = win->wdata;
426
427 if (text)
428 {
429 buf_addstr(wdata->text, text);
430 measure(&wdata->chars, text, ac_color);
431 mutt_debug(LL_DEBUG5, "MW ADD: %zu, %s\n", buf_len(wdata->text),
432 buf_string(wdata->text));
433 }
434 else
435 {
436 int rows = msgwin_calc_rows(wdata, win->state.cols, buf_string(wdata->text));
437 msgwin_set_rows(win, rows);
438 win->actions |= WA_RECALC;
439 }
440}
441
449void msgwin_add_text_n(struct MuttWindow *win, const char *text, int bytes,
450 const struct AttrColor *ac_color)
451{
452 if (!win)
453 win = msgcont_get_msgwin();
454 if (!win)
455 return;
456
457 struct MsgWinWindowData *wdata = win->wdata;
458
459 if (text)
460 {
461 const char *dptr = wdata->text->dptr;
462 buf_addstr_n(wdata->text, text, bytes);
463 measure(&wdata->chars, dptr, ac_color);
464 mutt_debug(LL_DEBUG5, "MW ADD: %zu, %s\n", buf_len(wdata->text),
465 buf_string(wdata->text));
466 }
467 else
468 {
469 int rows = msgwin_calc_rows(wdata, win->state.cols, buf_string(wdata->text));
470 msgwin_set_rows(win, rows);
471 win->actions |= WA_RECALC;
472 }
473}
474
483void msgwin_set_text(struct MuttWindow *win, const char *text, enum ColorId color)
484{
485 if (!win)
486 win = msgcont_get_msgwin();
487 if (!win)
488 return;
489
490 struct MsgWinWindowData *wdata = win->wdata;
491
492 if (mutt_str_equal(buf_string(wdata->text), text))
493 return;
494
495 buf_strcpy(wdata->text, text);
496 ARRAY_FREE(&wdata->chars);
497 if (wdata->text)
498 {
499 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
500 const struct AttrColor *ac_color = simple_color_get(color);
501 const struct AttrColor *ac_merge = merged_color_overlay(ac_normal, ac_color);
502
503 measure(&wdata->chars, buf_string(wdata->text), ac_merge);
504 }
505
506 mutt_debug(LL_DEBUG5, "MW SET: %zu, %s\n", buf_len(wdata->text),
507 buf_string(wdata->text));
508
509 int rows = msgwin_calc_rows(wdata, win->state.cols, buf_string(wdata->text));
510 msgwin_set_rows(win, rows);
511 win->actions |= WA_RECALC;
512}
513
519{
521}
522
530{
531 return msgcont_get_msgwin();
532}
#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:326
static int msgwin_recalc(struct MuttWindow *win)
Recalculate the display of the Message Window - Implements MuttWindow::recalc() -.
Definition msgwin.c:160
static bool msgwin_recursor(struct MuttWindow *win)
Recursor the Message Window - Implements MuttWindow::recursor() -.
Definition msgwin.c:282
static int msgwin_repaint(struct MuttWindow *win)
Redraw the Message Window - Implements MuttWindow::repaint() -.
Definition msgwin.c:250
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:370
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition msgwin.c:518
void msgwin_add_text(struct MuttWindow *win, const char *text, const struct AttrColor *ac_color)
Add text to the Message Window.
Definition msgwin.c:418
int msgwin_calc_rows(struct MsgWinWindowData *wdata, int cols, const char *str)
How many rows will a string need?
Definition msgwin.c:177
struct MuttWindow * msgwin_get_window(void)
Get the Message Window pointer.
Definition msgwin.c:529
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:449
void measure(struct MwCharArray *chars, const char *str, const struct AttrColor *ac_color)
Measure a string in bytes and cells.
Definition msgwin.c:105
void msgwin_set_text(struct MuttWindow *win, const char *text, enum ColorId color)
Set the text for the Message Window.
Definition msgwin.c:483
const char * msgwin_get_text(struct MuttWindow *win)
Get the text from the Message Window.
Definition msgwin.c:400
void msgwin_set_rows(struct MuttWindow *win, short rows)
Resize the Message Window.
Definition msgwin.c:302
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