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 3 : void history_pane_init(HistoryPane *hp) {
15 3 : if (!hp) return;
16 3 : memset(hp, 0, sizeof(*hp));
17 3 : list_view_init(&hp->lv);
18 : }
19 :
20 2 : void history_pane_set_entries(HistoryPane *hp,
21 : const HistoryPeer *peer,
22 : const HistoryEntry *src, int n) {
23 2 : if (!hp) return;
24 2 : if (n < 0) n = 0;
25 2 : if (n > HISTORY_PANE_MAX) n = HISTORY_PANE_MAX;
26 2 : if (src && n > 0) {
27 2 : memcpy(hp->entries, src, (size_t)n * sizeof(HistoryEntry));
28 : }
29 2 : hp->count = n;
30 2 : if (peer) hp->peer = *peer;
31 2 : hp->peer_loaded = 1;
32 2 : hp->lv.selected = (n > 0) ? 0 : -1;
33 2 : hp->lv.scroll_top = 0;
34 2 : list_view_set_count(&hp->lv, n);
35 : }
36 :
37 2 : int history_pane_load(HistoryPane *hp,
38 : const ApiConfig *cfg,
39 : MtProtoSession *s, Transport *t,
40 : const HistoryPeer *peer) {
41 2 : if (!hp || !cfg || !s || !t || !peer) return -1;
42 2 : HistoryEntry tmp[HISTORY_PANE_MAX] = {0};
43 2 : int count = 0;
44 2 : if (domain_get_history(cfg, s, t, peer, 0,
45 : HISTORY_PANE_MAX, tmp, &count) != 0) {
46 0 : return -1;
47 : }
48 2 : history_pane_set_entries(hp, peer, tmp, count);
49 2 : 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 2 : static int format_row(const HistoryEntry *e, char *out, size_t cap) {
58 2 : char arrow = e->out ? '>' : '<';
59 : const char *body;
60 2 : if (e->complex) body = "(complex)";
61 2 : else if (e->text[0]) body = e->text;
62 0 : else if (e->media) body = "(media)";
63 0 : else body = "";
64 2 : return snprintf(out, cap, "%c [%d] %s", arrow, e->id, body);
65 : }
66 :
67 3 : void history_pane_render(const HistoryPane *hp,
68 : const Pane *pane, Screen *screen) {
69 3 : if (!hp || !pane || !screen || !pane_is_valid(pane)) return;
70 3 : pane_clear(pane, screen);
71 :
72 3 : const char *placeholder = NULL;
73 3 : if (!hp->peer_loaded) placeholder = "(select a dialog)";
74 2 : else if (hp->count==0) placeholder = "(no messages)";
75 :
76 3 : if (placeholder) {
77 1 : int mid_row = pane->rows / 2;
78 1 : int col = (pane->cols - (int)strlen(placeholder)) / 2;
79 1 : if (col < 0) col = 0;
80 1 : pane_put_str(pane, screen, mid_row, col,
81 : placeholder, SCREEN_ATTR_DIM);
82 1 : return;
83 : }
84 :
85 2 : int first = hp->lv.scroll_top;
86 2 : int last = first + hp->lv.rows_visible;
87 2 : if (last > hp->count) last = hp->count;
88 :
89 4 : for (int i = first; i < last; i++) {
90 2 : const HistoryEntry *e = &hp->entries[i];
91 : char line[HISTORY_TEXT_MAX + 32];
92 2 : format_row(e, line, sizeof(line));
93 2 : uint8_t attrs = 0;
94 2 : if (e->complex) attrs |= SCREEN_ATTR_DIM;
95 2 : pane_put_str(pane, screen, i - first, 0, line, attrs);
96 : }
97 : }
|