LCOV - code coverage report
Current view: top level - src/tui - dialog_pane.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.8 % 62 60
Test Date: 2026-04-20 19:54:22 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           24 : static char kind_prefix(DialogPeerKind k) {
      15           24 :     switch (k) {
      16           22 :     case DIALOG_PEER_USER:    return 'u';
      17            1 :     case DIALOG_PEER_CHAT:    return 't';  /* "team" / group chat */
      18            1 :     case DIALOG_PEER_CHANNEL: return 'c';
      19            0 :     default:                  return '?';
      20              :     }
      21              : }
      22              : 
      23           35 : void dialog_pane_init(DialogPane *dp) {
      24           35 :     if (!dp) return;
      25           35 :     memset(dp, 0, sizeof(*dp));
      26           35 :     list_view_init(&dp->lv);
      27              : }
      28              : 
      29           27 : void dialog_pane_set_entries(DialogPane *dp,
      30              :                               const DialogEntry *src, int n) {
      31           27 :     if (!dp) return;
      32           27 :     if (n < 0) n = 0;
      33           27 :     if (n > DIALOG_PANE_MAX) n = DIALOG_PANE_MAX;
      34           27 :     if (src && n > 0) {
      35           22 :         memcpy(dp->entries, src, (size_t)n * sizeof(DialogEntry));
      36              :     }
      37           27 :     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           27 :     dp->lv.selected = (n > 0) ? 0 : -1;
      41           27 :     dp->lv.scroll_top = 0;
      42           27 :     list_view_set_count(&dp->lv, n);
      43              : }
      44              : 
      45           10 : int dialog_pane_refresh(DialogPane *dp,
      46              :                          const ApiConfig *cfg,
      47              :                          MtProtoSession *s, Transport *t) {
      48           10 :     if (!dp || !cfg || !s || !t) return -1;
      49           10 :     DialogEntry tmp[DIALOG_PANE_MAX] = {0};
      50           10 :     int count = 0;
      51           10 :     if (domain_get_dialogs(cfg, s, t, DIALOG_PANE_MAX, 0, tmp, &count, NULL) != 0)
      52            0 :         return -1;
      53           10 :     dialog_pane_set_entries(dp, tmp, count);
      54           10 :     return 0;
      55              : }
      56              : 
      57            9 : const DialogEntry *dialog_pane_selected(const DialogPane *dp) {
      58            9 :     if (!dp || dp->count == 0) return NULL;
      59            7 :     int idx = dp->lv.selected;
      60            7 :     if (idx < 0 || idx >= dp->count) return NULL;
      61            7 :     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           24 : static int format_row(const DialogEntry *e, char *out, size_t cap) {
      70           24 :     char prefix = kind_prefix(e->kind);
      71           24 :     const char *title = e->title[0] ? e->title : "(no title)";
      72           24 :     if (e->unread_count > 0) {
      73            8 :         return snprintf(out, cap, "%c [%d] %s",
      74            8 :                         prefix, e->unread_count, title);
      75              :     }
      76           16 :     return snprintf(out, cap, "%c     %s", prefix, title);
      77              : }
      78              : 
      79           23 : void dialog_pane_render(const DialogPane *dp,
      80              :                          const Pane *pane, Screen *screen,
      81              :                          int focused) {
      82           23 :     if (!dp || !pane || !screen || !pane_is_valid(pane)) return;
      83           20 :     pane_clear(pane, screen);
      84              : 
      85           20 :     if (dp->count == 0) {
      86            7 :         const char *msg = "(no dialogs)";
      87            7 :         int mid_row = pane->rows / 2;
      88            7 :         int col = (pane->cols - (int)strlen(msg)) / 2;
      89            7 :         if (col < 0) col = 0;
      90            7 :         pane_put_str(pane, screen, mid_row, col, msg, SCREEN_ATTR_DIM);
      91            7 :         return;
      92              :     }
      93              : 
      94           13 :     int first = dp->lv.scroll_top;
      95           13 :     int last  = first + dp->lv.rows_visible;
      96           13 :     if (last > dp->count) last = dp->count;
      97              : 
      98           37 :     for (int i = first; i < last; i++) {
      99           24 :         const DialogEntry *e = &dp->entries[i];
     100              :         char line[192];
     101           24 :         format_row(e, line, sizeof(line));
     102           24 :         uint8_t attrs = 0;
     103           24 :         if (e->unread_count > 0) attrs |= SCREEN_ATTR_BOLD;
     104           24 :         if (focused && i == dp->lv.selected) {
     105            8 :             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            8 :             pane_fill(pane, screen, i - first, 0, pane->cols, attrs);
     109              :         }
     110           24 :         pane_put_str(pane, screen, i - first, 0, line, attrs);
     111              :     }
     112              : }
        

Generated by: LCOV version 2.0-1