LCOV - code coverage report
Current view: top level - src/tui - dialog_pane.c (source / functions) Coverage Total Hit
Test: coverage-functional.info Lines: 82.3 % 62 51
Test Date: 2026-04-20 19:54:24 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-3.0-or-later */
       2              : /* Copyright 2026 Peter Csaszar */
       3              : 
       4              : /**
       5              :  * @file tui/dialog_pane.c
       6              :  * @brief Dialog list view-model implementation (US-11 v2).
       7              :  */
       8              : 
       9              : #include "tui/dialog_pane.h"
      10              : 
      11              : #include <stdio.h>
      12              : #include <string.h>
      13              : 
      14            3 : static char kind_prefix(DialogPeerKind k) {
      15            3 :     switch (k) {
      16            3 :     case DIALOG_PEER_USER:    return 'u';
      17            0 :     case DIALOG_PEER_CHAT:    return 't';  /* "team" / group chat */
      18            0 :     case DIALOG_PEER_CHANNEL: return 'c';
      19            0 :     default:                  return '?';
      20              :     }
      21              : }
      22              : 
      23            3 : void dialog_pane_init(DialogPane *dp) {
      24            3 :     if (!dp) return;
      25            3 :     memset(dp, 0, sizeof(*dp));
      26            3 :     list_view_init(&dp->lv);
      27              : }
      28              : 
      29            3 : void dialog_pane_set_entries(DialogPane *dp,
      30              :                               const DialogEntry *src, int n) {
      31            3 :     if (!dp) return;
      32            3 :     if (n < 0) n = 0;
      33            3 :     if (n > DIALOG_PANE_MAX) n = DIALOG_PANE_MAX;
      34            3 :     if (src && n > 0) {
      35            3 :         memcpy(dp->entries, src, (size_t)n * sizeof(DialogEntry));
      36              :     }
      37            3 :     dp->count = n;
      38              :     /* Always reset selection to the top after a refresh so the user lands
      39              :      * on the most-recent dialog rather than a now-invalid index. */
      40            3 :     dp->lv.selected = (n > 0) ? 0 : -1;
      41            3 :     dp->lv.scroll_top = 0;
      42            3 :     list_view_set_count(&dp->lv, n);
      43              : }
      44              : 
      45            3 : int dialog_pane_refresh(DialogPane *dp,
      46              :                          const ApiConfig *cfg,
      47              :                          MtProtoSession *s, Transport *t) {
      48            3 :     if (!dp || !cfg || !s || !t) return -1;
      49            3 :     DialogEntry tmp[DIALOG_PANE_MAX] = {0};
      50            3 :     int count = 0;
      51            3 :     if (domain_get_dialogs(cfg, s, t, DIALOG_PANE_MAX, 0, tmp, &count, NULL) != 0)
      52            0 :         return -1;
      53            3 :     dialog_pane_set_entries(dp, tmp, count);
      54            3 :     return 0;
      55              : }
      56              : 
      57            2 : const DialogEntry *dialog_pane_selected(const DialogPane *dp) {
      58            2 :     if (!dp || dp->count == 0) return NULL;
      59            2 :     int idx = dp->lv.selected;
      60            2 :     if (idx < 0 || idx >= dp->count) return NULL;
      61            2 :     return &dp->entries[idx];
      62              : }
      63              : 
      64              : /* Format one dialog entry as a single line. Returns bytes written
      65              :  * (excluding NUL). Produces strings like:
      66              :  *     u  Alice                    (plain DM, 0 unread)
      67              :  *     c [12] Tech channel         (channel with 12 unread)
      68              :  */
      69            3 : static int format_row(const DialogEntry *e, char *out, size_t cap) {
      70            3 :     char prefix = kind_prefix(e->kind);
      71            3 :     const char *title = e->title[0] ? e->title : "(no title)";
      72            3 :     if (e->unread_count > 0) {
      73            3 :         return snprintf(out, cap, "%c [%d] %s",
      74            3 :                         prefix, e->unread_count, title);
      75              :     }
      76            0 :     return snprintf(out, cap, "%c     %s", prefix, title);
      77              : }
      78              : 
      79            3 : void dialog_pane_render(const DialogPane *dp,
      80              :                          const Pane *pane, Screen *screen,
      81              :                          int focused) {
      82            3 :     if (!dp || !pane || !screen || !pane_is_valid(pane)) return;
      83            3 :     pane_clear(pane, screen);
      84              : 
      85            3 :     if (dp->count == 0) {
      86            0 :         const char *msg = "(no dialogs)";
      87            0 :         int mid_row = pane->rows / 2;
      88            0 :         int col = (pane->cols - (int)strlen(msg)) / 2;
      89            0 :         if (col < 0) col = 0;
      90            0 :         pane_put_str(pane, screen, mid_row, col, msg, SCREEN_ATTR_DIM);
      91            0 :         return;
      92              :     }
      93              : 
      94            3 :     int first = dp->lv.scroll_top;
      95            3 :     int last  = first + dp->lv.rows_visible;
      96            3 :     if (last > dp->count) last = dp->count;
      97              : 
      98            6 :     for (int i = first; i < last; i++) {
      99            3 :         const DialogEntry *e = &dp->entries[i];
     100              :         char line[192];
     101            3 :         format_row(e, line, sizeof(line));
     102            3 :         uint8_t attrs = 0;
     103            3 :         if (e->unread_count > 0) attrs |= SCREEN_ATTR_BOLD;
     104            3 :         if (focused && i == dp->lv.selected) {
     105            2 :             attrs |= SCREEN_ATTR_REVERSE;
     106              :             /* Paint the full row so the highlight extends to the right edge
     107              :              * even when the title is short. */
     108            2 :             pane_fill(pane, screen, i - first, 0, pane->cols, attrs);
     109              :         }
     110            3 :         pane_put_str(pane, screen, i - first, 0, line, attrs);
     111              :     }
     112              : }
        

Generated by: LCOV version 2.0-1