NeoMutt  2025-12-11-872-g385a04
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 "core/lib.h"
91#include "msgwin.h"
92#include "color/lib.h"
93#include "module_data.h"
94#include "msgcont.h"
95#include "msgwin_wdata.h"
96#include "mutt_curses.h"
97#include "mutt_window.h"
98
107void measure(struct MwCharArray *chars, const char *str, const struct AttrColor *ac_color)
108{
109 if (!str || !*str)
110 return;
111
112 mbstate_t mbstate = { 0 };
113 struct MwChar mwc = { 0 };
114
115 size_t str_len = mutt_str_len(str);
116
117 while (*str && (str_len > 0))
118 {
119 wchar_t wc = L'\0';
120 size_t consumed = mbrtowc(&wc, str, str_len, &mbstate);
121 if (consumed == 0)
122 break;
123
124 if (consumed == ICONV_ILLEGAL_SEQ)
125 {
126 memset(&mbstate, 0, sizeof(mbstate));
127 wc = ReplacementChar;
128 consumed = 1;
129 }
130 else if (consumed == ICONV_BUF_TOO_SMALL)
131 {
132 wc = ReplacementChar;
133 consumed = str_len;
134 }
135
136 int wchar_width = wcwidth(wc);
137 if (wchar_width < 0)
138 wchar_width = 1;
139
140 if (wc == 0xfe0f) // Emoji variation
141 {
142 int size = ARRAY_SIZE(chars);
143 if (size > 0)
144 {
145 struct MwChar *es_prev = ARRAY_GET(chars, size - 1);
146 if (es_prev->width == 1)
147 es_prev->width = 2;
148 }
149 }
150
151 mwc = (struct MwChar) { wchar_width, consumed, ac_color };
152 ARRAY_ADD(chars, mwc);
153
154 str += consumed;
155 str_len -= consumed;
156 }
157}
158
162static int msgwin_recalc(struct MuttWindow *win)
163{
164 win->actions |= WA_REPAINT;
165 mutt_debug(LL_DEBUG5, "recalc done, request WA_REPAINT\n");
166 return 0;
167}
168
179int msgwin_calc_rows(struct MsgWinWindowData *wdata, int cols, const char *str)
180{
181 if (!wdata || !str || !*str)
182 return 0;
183
184 for (int i = 0; i < MSGWIN_MAX_ROWS; i++)
185 {
186 ARRAY_FREE(&wdata->rows[i]);
187 }
188
189 int width = 0;
190 int offset = 0;
191 int row = 0;
192 bool new_row = false;
193
194 struct MwChunk *chunk = NULL;
195 struct MwChar *mwc = NULL;
196 ARRAY_FOREACH(mwc, &wdata->chars)
197 {
198 const bool nl = (mwc->bytes == 1) && (str[offset] == '\n');
199 if (nl)
200 {
201 new_row = true;
202 offset += mwc->bytes;
203 continue;
204 }
205
206 if (((width + mwc->width) > cols) || new_row)
207 {
208 // ROW IS FULL
209 new_row = false;
210
211 row++;
212 if (row >= MSGWIN_MAX_ROWS)
213 {
214 // NO MORE ROOM
215 break;
216 }
217
218 // Start a new row
219 struct MwChunk tmp = { offset, mwc->bytes, mwc->width, mwc->ac_color };
220
221 mutt_debug(LL_DEBUG5, "row = %d\n", row);
222 ARRAY_ADD(&wdata->rows[row], tmp);
223 chunk = ARRAY_LAST(&wdata->rows[row]);
224
225 width = 0;
226 }
227 else if (!chunk || (mwc->ac_color != chunk->ac_color))
228 {
229 // CHANGE OF COLOUR
230 struct MwChunk tmp = { offset, mwc->bytes, mwc->width, mwc->ac_color };
231 ARRAY_ADD(&wdata->rows[row], tmp);
232 chunk = ARRAY_LAST(&wdata->rows[row]);
233 }
234 else
235 {
236 // MORE OF THE SAME
237 chunk->bytes += mwc->bytes;
238 chunk->width += mwc->width;
239 }
240
241 offset += mwc->bytes;
242 width += mwc->width;
243 }
244
245 mutt_debug(LL_DEBUG5, "msgwin_calc_rows() => %d\n", row + 1);
246 return row + 1;
247}
248
252static int msgwin_repaint(struct MuttWindow *win)
253{
254 struct MsgWinWindowData *wdata = win->wdata;
255
256 const char *str = buf_string(wdata->text);
257 for (int i = 0; i < MSGWIN_MAX_ROWS; i++)
258 {
259 mutt_window_move(win, i, 0);
260 if (ARRAY_EMPTY(&wdata->rows[i]))
261 break;
262
263 struct MwChunk *chunk = NULL;
264 ARRAY_FOREACH(chunk, &wdata->rows[i])
265 {
267 mutt_window_addnstr(win, str + chunk->offset, chunk->bytes);
268 }
271 }
274
275 mutt_window_get_coords(win, &wdata->row, &wdata->col);
276
277 mutt_debug(LL_DEBUG5, "msgwin repaint done\n");
278 return 0;
279}
280
284static bool msgwin_recursor(struct MuttWindow *win)
285{
286 struct MsgWinWindowData *wdata = win->wdata;
287
288 mutt_window_move(win, wdata->row, wdata->col);
290
291 mutt_debug(LL_DEBUG5, "msgwin recursor done\n");
292 return true;
293}
294
304void msgwin_set_rows(struct MuttWindow *win, short rows)
305{
306 if (!win)
307 win = msgcont_get_msgwin();
308 if (!win)
309 return;
310
312
313 if (rows != win->req_rows)
314 {
315 win->req_rows = rows;
316
318 if (mod_data && mod_data->bottom_bar)
319 mod_data->bottom_bar->req_rows = rows;
320
321 mutt_window_reflow(NULL);
322 }
323}
324
334{
335 if (nc->event_type != NT_WINDOW)
336 return 0;
337 if (!nc->global_data || !nc->event_data)
338 return -1;
339
340 struct MuttWindow *win = nc->global_data;
341 struct EventWindow *ev_w = nc->event_data;
342 if (ev_w->win != win)
343 return 0;
344
346 {
347 if (ev_w->flags & WN_HIDDEN)
348 {
350 }
351
352 if (ev_w->flags & (WN_NARROWER | WN_WIDER))
353 {
354 struct MsgWinWindowData *wdata = win->wdata;
356 buf_string(wdata->text)));
357 win->actions |= WA_RECALC;
358 }
359 else
360 {
361 win->actions |= WA_REPAINT;
362 }
363 mutt_debug(LL_DEBUG5, "window state done, request WA_RECALC\n");
364 }
365 else if (nc->event_subtype == NT_WINDOW_DELETE)
366 {
368 mutt_debug(LL_DEBUG5, "window delete done\n");
369 }
370 return 0;
371}
372
377struct MuttWindow *msgwin_new(bool interactive)
378{
381
382 struct MsgWinWindowData *wdata = msgwin_wdata_new();
383
384 win->wdata = wdata;
386 win->recalc = msgwin_recalc;
387 win->repaint = msgwin_repaint;
388
389 if (interactive)
391
392 // Copy the container's dimensions
394 if (mod_data && mod_data->message_container)
395 win->state = mod_data->message_container->state;
396
398
399 return win;
400}
401
409const char *msgwin_get_text(struct MuttWindow *win)
410{
411 if (!win)
412 win = msgcont_get_msgwin();
413 if (!win)
414 return NULL;
415
416 struct MsgWinWindowData *wdata = win->wdata;
417
418 return buf_string(wdata->text);
419}
420
427void msgwin_add_text(struct MuttWindow *win, const char *text, const struct AttrColor *ac_color)
428{
429 if (!win)
430 win = msgcont_get_msgwin();
431 if (!win)
432 return;
433
434 struct MsgWinWindowData *wdata = win->wdata;
435
436 if (text)
437 {
438 buf_addstr(wdata->text, text);
439 measure(&wdata->chars, text, ac_color);
440 mutt_debug(LL_DEBUG5, "MW ADD: %zu, %s\n", buf_len(wdata->text),
441 buf_string(wdata->text));
442 }
443 else
444 {
445 int rows = msgwin_calc_rows(wdata, win->state.cols, buf_string(wdata->text));
446 msgwin_set_rows(win, rows);
447 win->actions |= WA_RECALC;
448 }
449}
450
458void msgwin_add_text_n(struct MuttWindow *win, const char *text, int bytes,
459 const struct AttrColor *ac_color)
460{
461 if (!win)
462 win = msgcont_get_msgwin();
463 if (!win)
464 return;
465
466 struct MsgWinWindowData *wdata = win->wdata;
467
468 if (text)
469 {
470 const char *dptr = wdata->text->dptr;
471 buf_addstr_n(wdata->text, text, bytes);
472 measure(&wdata->chars, dptr, ac_color);
473 mutt_debug(LL_DEBUG5, "MW ADD: %zu, %s\n", buf_len(wdata->text),
474 buf_string(wdata->text));
475 }
476 else
477 {
478 int rows = msgwin_calc_rows(wdata, win->state.cols, buf_string(wdata->text));
479 msgwin_set_rows(win, rows);
480 win->actions |= WA_RECALC;
481 }
482}
483
492void msgwin_set_text(struct MuttWindow *win, const char *text, enum ColorId color)
493{
494 if (!win)
495 win = msgcont_get_msgwin();
496 if (!win)
497 return;
498
499 struct MsgWinWindowData *wdata = win->wdata;
500
501 if (mutt_str_equal(buf_string(wdata->text), text))
502 return;
503
504 buf_strcpy(wdata->text, text);
505 ARRAY_FREE(&wdata->chars);
506 if (wdata->text)
507 {
508 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
509 const struct AttrColor *ac_color = simple_color_get(color);
510 const struct AttrColor *ac_merge = merged_color_overlay(ac_normal, ac_color);
511
512 measure(&wdata->chars, buf_string(wdata->text), ac_merge);
513 }
514
515 mutt_debug(LL_DEBUG5, "MW SET: %zu, %s\n", buf_len(wdata->text),
516 buf_string(wdata->text));
517
518 int rows = msgwin_calc_rows(wdata, win->state.cols, buf_string(wdata->text));
519 msgwin_set_rows(win, rows);
520 win->actions |= WA_RECALC;
521}
522
528{
530}
531
539{
540 return msgcont_get_msgwin();
541}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
#define ARRAY_LAST(head)
Convenience method to get the last element.
Definition array.h:145
#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:209
#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:98
ColorId
List of all coloured objects.
Definition color.h:35
@ MT_COLOR_NORMAL
Plain text.
Definition color.h:53
Convenience wrapper for the core headers.
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
static int msgwin_window_observer(struct NotifyCallback *nc)
Notification that a Window has changed - Implements observer_t -.
Definition msgwin.c:333
static int msgwin_recalc(struct MuttWindow *win)
Recalculate the display of the Message Window - Implements MuttWindow::recalc() -.
Definition msgwin.c:162
static bool msgwin_recursor(struct MuttWindow *win)
Recursor the Message Window - Implements MuttWindow::recursor() -.
Definition msgwin.c:284
static int msgwin_repaint(struct MuttWindow *win)
Redraw the Message Window - Implements MuttWindow::repaint() -.
Definition msgwin.c:252
void msgwin_wdata_free(struct MuttWindow *win, void **ptr)
Free the private data - Implements MuttWindow::wdata_free() -.
Gui private Module data.
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
#define CLAMP(val, lo, hi)
Clamp a value between a lower and upper bound.
Definition memory.h:42
const struct AttrColor * merged_color_overlay(const struct AttrColor *base, const struct AttrColor *over)
Combine two colours.
Definition merged.c:110
@ MODULE_ID_GUI
ModuleGui, Graphical code
Definition module_api.h:45
struct MuttWindow * msgcont_get_msgwin(void)
Get the Message Window.
Definition msgcont.c:140
Message Window.
struct MuttWindow * msgwin_new(bool interactive)
Create the Message Window.
Definition msgwin.c:377
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition msgwin.c:527
void msgwin_add_text(struct MuttWindow *win, const char *text, const struct AttrColor *ac_color)
Add text to the Message Window.
Definition msgwin.c:427
int msgwin_calc_rows(struct MsgWinWindowData *wdata, int cols, const char *str)
How many rows will a string need?
Definition msgwin.c:179
struct MuttWindow * msgwin_get_window(void)
Get the Message Window pointer.
Definition msgwin.c:538
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:458
void measure(struct MwCharArray *chars, const char *str, const struct AttrColor *ac_color)
Measure a string in bytes and cells.
Definition msgwin.c:107
void msgwin_set_text(struct MuttWindow *win, const char *text, enum ColorId color)
Set the text for the Message Window.
Definition msgwin.c:492
const char * msgwin_get_text(struct MuttWindow *win)
Get the text from the Message Window.
Definition msgwin.c:409
void msgwin_set_rows(struct MuttWindow *win, short rows)
Resize the Message Window.
Definition msgwin.c:304
Message Window.
struct MsgWinWindowData * msgwin_wdata_new(void)
Create new private data for the Message Window.
Message Window private data.
#define MSGWIN_MAX_ROWS
Maximum number of rows to show in the message window.
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:116
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition charset.h:114
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:665
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
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.
@ WT_MESSAGE
Window for messages/errors.
Definition mutt_window.h:98
@ 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.
@ WN_WIDER
Window became wider.
@ WN_HIDDEN
Window became hidden.
@ WN_NARROWER
Window became narrower.
#define MUTT_WIN_SIZE_UNLIMITED
Use as much space as possible.
Definition mutt_window.h:52
@ MUTT_WIN_SIZE_FIXED
Window has a fixed size.
Definition mutt_window.h:47
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:663
@ NT_WINDOW
MuttWindow has changed, NotifyWindow, EventWindow.
Definition notify_type.h:58
A curses colour and its attributes.
Definition attr.h:65
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.
Gui private Module data.
Definition module_data.h:32
struct MuttWindow * message_container
Message Container Window.
Definition module_data.h:40
struct MuttWindow * bottom_bar
Bottom Bar Container Window.
Definition module_data.h:39
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.
Container for Accounts, Notifications.
Definition neomutt.h:41
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:60