NeoMutt  2024-12-12-14-g7b49f7
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
msgwin.c
Go to the documentation of this file.
1
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 size_t 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, 0, i);
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->col, &wdata->row);
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->col, wdata->row);
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
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:212
#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:55
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
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() -.
Definition: msgwin_wdata.c:37
@ LL_DEBUG5
Log at debug level 5.
Definition: logging2.h:47
#define CLAMP(val, lo, hi)
Definition: memory.h:33
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.
Definition: msgwin_wdata.c:60
Message Window private data.
#define MSGWIN_MAX_ROWS
Definition: msgwin_wdata.h:30
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:496
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.
Definition: mutt_window.c:344
struct MuttWindow * mutt_window_new(enum WindowType type, enum MuttWindowOrientation orient, enum MuttWindowSize size, int cols, int rows)
Create a new Window.
Definition: mutt_window.c:182
int mutt_window_move(struct MuttWindow *win, int col, int row)
Move the cursor in a Window.
Definition: mutt_window.c:297
void mutt_window_get_coords(struct MuttWindow *win, int *col, int *row)
Get the cursor position in the Window.
Definition: mutt_window.c:277
int mutt_window_addnstr(struct MuttWindow *win, const char *str, int num)
Write a partial string to a Window.
Definition: mutt_window.c:401
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:244
Window management.
#define WA_RECALC
Recalculate the contents of the Window.
Definition: mutt_window.h:110
#define WN_WIDER
Window became wider.
Definition: mutt_window.h:212
@ 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.
Definition: mutt_window.h:230
@ NT_WINDOW_DELETE
Window is about to be deleted.
Definition: mutt_window.h:229
#define WN_HIDDEN
Window became hidden.
Definition: mutt_window.h:216
#define WA_REPAINT
Redraw the contents of the Window.
Definition: mutt_window.h:111
#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.
Definition: mutt_window.h:213
@ 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.
Definition: mutt_window.h:239
struct MuttWindow * win
Window that changed.
Definition: mutt_window.h:240
WindowNotifyFlags flags
Attributes of Window that changed.
Definition: mutt_window.h:241
Message Window private Window data.
Definition: msgwin_wdata.h:66
struct Buffer * text
Cached display string.
Definition: msgwin_wdata.h:67
struct MwCharArray chars
Text: Breakdown of bytes and widths.
Definition: msgwin_wdata.h:68
struct MwChunkArray rows[MSGWIN_MAX_ROWS]
String byte counts for each row.
Definition: msgwin_wdata.h:69
int row
Cursor row.
Definition: msgwin_wdata.h:70
int col
Cursor column.
Definition: msgwin_wdata.h:71
int(* repaint)(struct MuttWindow *win)
Definition: mutt_window.h:187
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
void * wdata
Private data.
Definition: mutt_window.h:145
struct Notify * notify
Notifications: NotifyWindow, EventWindow.
Definition: mutt_window.h:138
short req_rows
Number of rows required.
Definition: mutt_window.h:125
int(* recalc)(struct MuttWindow *win)
Definition: mutt_window.h:173
void(* wdata_free)(struct MuttWindow *win, void **ptr)
Definition: mutt_window.h:159
WindowActionFlags actions
Actions to be performed, e.g. WA_RECALC.
Definition: mutt_window.h:132
bool(* recursor)(struct MuttWindow *win)
Definition: mutt_window.h:205
Description of a single character.
Definition: msgwin_wdata.h:39
const struct AttrColor * ac_color
Colour to use.
Definition: msgwin_wdata.h:42
unsigned char width
Width in screen cells.
Definition: msgwin_wdata.h:40
unsigned char bytes
Number of bytes to represent.
Definition: msgwin_wdata.h:41
A block of characters of one colour.
Definition: msgwin_wdata.h:54
unsigned short bytes
Number of bytes in the row.
Definition: msgwin_wdata.h:56
unsigned short width
Width of row in screen cells.
Definition: msgwin_wdata.h:57
unsigned short offset
Offset into MsgWinWindowData.text.
Definition: msgwin_wdata.h:55
const struct AttrColor * ac_color
Colour to use.
Definition: msgwin_wdata.h:58
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