Line data Source code
1 : /* SPDX-License-Identifier: GPL-3.0-or-later */
2 : /* Copyright 2026 Peter Csaszar */
3 :
4 : /**
5 : * @file tui/history_pane.c
6 : * @brief Message history view-model implementation (US-11 v2).
7 : */
8 :
9 : #include "tui/history_pane.h"
10 :
11 : #include <stdio.h>
12 : #include <string.h>
13 :
14 34 : void history_pane_init(HistoryPane *hp) {
15 34 : if (!hp) return;
16 34 : memset(hp, 0, sizeof(*hp));
17 34 : list_view_init(&hp->lv);
18 : }
19 :
20 12 : void history_pane_set_entries(HistoryPane *hp,
21 : const HistoryPeer *peer,
22 : const HistoryEntry *src, int n) {
23 12 : if (!hp) return;
24 12 : if (n < 0) n = 0;
25 12 : if (n > HISTORY_PANE_MAX) n = HISTORY_PANE_MAX;
26 12 : if (src && n > 0) {
27 10 : memcpy(hp->entries, src, (size_t)n * sizeof(HistoryEntry));
28 : }
29 12 : hp->count = n;
30 12 : if (peer) hp->peer = *peer;
31 12 : hp->peer_loaded = 1;
32 12 : hp->lv.selected = (n > 0) ? 0 : -1;
33 12 : hp->lv.scroll_top = 0;
34 12 : list_view_set_count(&hp->lv, n);
35 : }
36 :
37 4 : int history_pane_load(HistoryPane *hp,
38 : const ApiConfig *cfg,
39 : MtProtoSession *s, Transport *t,
40 : const HistoryPeer *peer) {
41 4 : if (!hp || !cfg || !s || !t || !peer) return -1;
42 4 : HistoryEntry tmp[HISTORY_PANE_MAX] = {0};
43 4 : int count = 0;
44 4 : if (domain_get_history(cfg, s, t, peer, 0,
45 : HISTORY_PANE_MAX, tmp, &count) != 0) {
46 0 : return -1;
47 : }
48 4 : history_pane_set_entries(hp, peer, tmp, count);
49 4 : return 0;
50 : }
51 :
52 : /* Format one history row into a flat line. Returns bytes written
53 : * (excluding NUL). Examples:
54 : * > [42] hello
55 : * < [41] (media)
56 : * < [40] (complex) */
57 12 : static int format_row(const HistoryEntry *e, char *out, size_t cap) {
58 12 : char arrow = e->out ? '>' : '<';
59 : const char *body;
60 12 : if (e->complex) body = "(complex)";
61 10 : else if (e->text[0]) body = e->text;
62 1 : else if (e->media) body = "(media)";
63 0 : else body = "";
64 12 : return snprintf(out, cap, "%c [%d] %s", arrow, e->id, body);
65 : }
66 :
67 23 : void history_pane_render(const HistoryPane *hp,
68 : const Pane *pane, Screen *screen) {
69 23 : if (!hp || !pane || !screen || !pane_is_valid(pane)) return;
70 20 : pane_clear(pane, screen);
71 :
72 20 : const char *placeholder = NULL;
73 20 : if (!hp->peer_loaded) placeholder = "(select a dialog)";
74 9 : else if (hp->count==0) placeholder = "(no messages)";
75 :
76 20 : if (placeholder) {
77 12 : int mid_row = pane->rows / 2;
78 12 : int col = (pane->cols - (int)strlen(placeholder)) / 2;
79 12 : if (col < 0) col = 0;
80 12 : pane_put_str(pane, screen, mid_row, col,
81 : placeholder, SCREEN_ATTR_DIM);
82 12 : return;
83 : }
84 :
85 8 : int first = hp->lv.scroll_top;
86 8 : int last = first + hp->lv.rows_visible;
87 8 : if (last > hp->count) last = hp->count;
88 :
89 20 : for (int i = first; i < last; i++) {
90 12 : const HistoryEntry *e = &hp->entries[i];
91 : char line[HISTORY_TEXT_MAX + 32];
92 12 : format_row(e, line, sizeof(line));
93 12 : uint8_t attrs = 0;
94 12 : if (e->complex) attrs |= SCREEN_ATTR_DIM;
95 12 : pane_put_str(pane, screen, i - first, 0, line, attrs);
96 : }
97 : }
|