LCOV - code coverage report
Current view: top level - gcc/text-art - table.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 98.0 % 599 587
Test Date: 2025-06-21 16:26:05 Functions: 97.4 % 39 38
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* Support for tabular/grid-based content.
       2                 :             :    Copyright (C) 2023-2025 Free Software Foundation, Inc.
       3                 :             :    Contributed by David Malcolm <dmalcolm@redhat.com>.
       4                 :             : 
       5                 :             : This file is part of GCC.
       6                 :             : 
       7                 :             : GCC is free software; you can redistribute it and/or modify it under
       8                 :             : the terms of the GNU General Public License as published by the Free
       9                 :             : Software Foundation; either version 3, or (at your option) any later
      10                 :             : version.
      11                 :             : 
      12                 :             : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      13                 :             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      14                 :             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      15                 :             : for more details.
      16                 :             : 
      17                 :             : You should have received a copy of the GNU General Public License
      18                 :             : along with GCC; see the file COPYING3.  If not see
      19                 :             : <http://www.gnu.org/licenses/>.  */
      20                 :             : 
      21                 :             : #include "config.h"
      22                 :             : #define INCLUDE_VECTOR
      23                 :             : #include "system.h"
      24                 :             : #include "coretypes.h"
      25                 :             : #include "pretty-print.h"
      26                 :             : #include "diagnostic.h"
      27                 :             : #include "selftest.h"
      28                 :             : #include "text-art/selftests.h"
      29                 :             : #include "text-art/table.h"
      30                 :             : 
      31                 :             : using namespace text_art;
      32                 :             : 
      33                 :             : /* class text_art::table_cell_content.  */
      34                 :             : 
      35                 :        2008 : table_cell_content::table_cell_content (styled_string &&s)
      36                 :        2008 : : m_str (std::move (s)),
      37                 :             :   /* We assume here that the content occupies a single canvas row.  */
      38                 :        2008 :   m_size (m_str.calc_canvas_width (), 1)
      39                 :             : {
      40                 :        2008 : }
      41                 :             : 
      42                 :             : void
      43                 :        2327 : table_cell_content::paint_to_canvas (canvas &canvas,
      44                 :             :                                      canvas::coord_t top_left) const
      45                 :             : {
      46                 :        2327 :   canvas.paint_text (top_left, m_str);
      47                 :        2327 : }
      48                 :             : 
      49                 :             : /* struct text_art::table_dimension_sizes.  */
      50                 :             : 
      51                 :         365 : table_dimension_sizes::table_dimension_sizes (unsigned num)
      52                 :         365 : : m_requirements (num, 0)
      53                 :             : {
      54                 :         365 : }
      55                 :             : 
      56                 :             : /* class text_art::table::cell_placement.  */
      57                 :             : 
      58                 :             : void
      59                 :        2327 : table::cell_placement::paint_cell_contents_to_canvas(canvas &canvas,
      60                 :             :                                                      canvas::coord_t offset,
      61                 :             :                                                      const table_geometry &tg) const
      62                 :             : {
      63                 :        2327 :   const canvas::size_t req_canvas_size = get_min_canvas_size ();
      64                 :        2327 :   const canvas::size_t alloc_canvas_size = tg.get_canvas_size (m_rect);
      65                 :        2327 :   gcc_assert (req_canvas_size.w <= alloc_canvas_size.w);
      66                 :        2327 :   gcc_assert (req_canvas_size.h <= alloc_canvas_size.h);
      67                 :        2327 :   const int x_padding = alloc_canvas_size.w - req_canvas_size.w;
      68                 :        2327 :   const int y_padding = alloc_canvas_size.h - req_canvas_size.h;
      69                 :        2327 :   const table::coord_t table_top_left = m_rect.m_top_left;
      70                 :        2327 :   const canvas::coord_t canvas_top_left = tg.table_to_canvas (table_top_left);
      71                 :             : 
      72                 :        2327 :   gcc_assert (x_padding >= 0);
      73                 :        2327 :   int x_align_offset;
      74                 :        2327 :   switch (m_x_align)
      75                 :             :     {
      76                 :           0 :     default:
      77                 :           0 :       gcc_unreachable ();
      78                 :             :     case x_align::LEFT:
      79                 :             :       x_align_offset = 0;
      80                 :             :       break;
      81                 :        2303 :     case x_align::CENTER:
      82                 :        2303 :       x_align_offset = x_padding / 2;
      83                 :        2303 :       break;
      84                 :          12 :     case x_align::RIGHT:
      85                 :          12 :       x_align_offset = x_padding;
      86                 :          12 :       break;
      87                 :             :     }
      88                 :             : 
      89                 :        2327 :   gcc_assert (y_padding >= 0);
      90                 :        2327 :   int y_align_offset;
      91                 :        2327 :   switch (m_y_align)
      92                 :             :     {
      93                 :           0 :     default:
      94                 :           0 :       gcc_unreachable ();
      95                 :             :     case y_align::TOP:
      96                 :             :       y_align_offset = 0;
      97                 :             :       break;
      98                 :        2303 :     case y_align::CENTER:
      99                 :        2303 :       y_align_offset = y_padding / 2;
     100                 :        2303 :       break;
     101                 :          12 :     case y_align::BOTTOM:
     102                 :          12 :       y_align_offset = y_padding;
     103                 :          12 :       break;
     104                 :             :     }
     105                 :        2327 :   const canvas::coord_t content_rel_coord
     106                 :        2327 :     (canvas_top_left.x + 1 + x_align_offset,
     107                 :        2327 :      canvas_top_left.y + 1 + y_align_offset);
     108                 :        2327 :   m_content.paint_to_canvas (canvas, offset + content_rel_coord);
     109                 :        2327 : }
     110                 :             : 
     111                 :             : /* class text_art::table.  */
     112                 :             : 
     113                 :             : 
     114                 :         215 : table::table (size_t size)
     115                 :         215 : : m_size (size),
     116                 :         215 :   m_placements (),
     117                 :         215 :   m_occupancy (size)
     118                 :             : {
     119                 :         215 :   m_occupancy.fill (-1);
     120                 :         215 : }
     121                 :             : 
     122                 :             : void
     123                 :         738 : table::set_cell (coord_t coord,
     124                 :             :                  table_cell_content &&content,
     125                 :             :                  enum x_align x_align,
     126                 :             :                  enum y_align y_align)
     127                 :             : {
     128                 :         738 :   set_cell_span (rect_t (coord, table::size_t (1, 1)),
     129                 :             :                  std::move (content), x_align, y_align);
     130                 :         738 : }
     131                 :             : 
     132                 :             : void
     133                 :        2090 : table::set_cell_span (rect_t span,
     134                 :             :                       table_cell_content &&content,
     135                 :             :                       enum x_align x_align,
     136                 :             :                       enum y_align y_align)
     137                 :             : {
     138                 :        2090 :   gcc_assert (span.m_size.w > 0);
     139                 :        2090 :   gcc_assert (span.m_size.h > 0);
     140                 :        2090 :   int placement_idx = m_placements.size ();
     141                 :        2090 :   m_placements.emplace_back (cell_placement (span, std::move (content),
     142                 :        2090 :                                              x_align, y_align));
     143                 :        4355 :   for (int y = span.get_min_y (); y < span.get_next_y (); y++)
     144                 :        8249 :     for (int x = span.get_min_x (); x < span.get_next_x (); x++)
     145                 :             :       {
     146                 :        5984 :         gcc_assert (m_occupancy.get (coord_t (x, y)) == -1);
     147                 :        5984 :         m_occupancy.set (coord_t (x, y), placement_idx);
     148                 :             :       }
     149                 :        2090 : }
     150                 :             : 
     151                 :             : /* If SPAN is unoccuped, set it to CONTENT.
     152                 :             :    Otherwise, discard CONTENT.  */
     153                 :             : 
     154                 :             : void
     155                 :         108 : table::maybe_set_cell_span (rect_t span,
     156                 :             :                             table_cell_content &&content,
     157                 :             :                             enum x_align x_align,
     158                 :             :                             enum y_align y_align)
     159                 :             : {
     160                 :         108 :   gcc_assert (span.m_size.w > 0);
     161                 :         108 :   gcc_assert (span.m_size.h > 0);
     162                 :         194 :   for (int y = span.get_min_y (); y < span.get_next_y (); y++)
     163                 :         194 :     for (int x = span.get_min_x (); x < span.get_next_x (); x++)
     164                 :             :       {
     165                 :         108 :         if (m_occupancy.get (coord_t (x, y)) != -1)
     166                 :             :           return;
     167                 :             :       }
     168                 :          86 :   set_cell_span (span, std::move (content), x_align, y_align);
     169                 :             : }
     170                 :             : 
     171                 :             : canvas
     172                 :          76 : table::to_canvas (const theme &theme, const style_manager &sm) const
     173                 :             : {
     174                 :          76 :   table_dimension_sizes col_widths (m_size.w);
     175                 :          76 :   table_dimension_sizes row_heights (m_size.h);
     176                 :          76 :   table_cell_sizes cell_sizes (col_widths, row_heights);
     177                 :          76 :   cell_sizes.pass_1 (*this);
     178                 :          76 :   cell_sizes.pass_2 (*this);
     179                 :          76 :   table_geometry tg (*this, cell_sizes);
     180                 :          76 :   canvas canvas (tg.get_canvas_size (), sm);
     181                 :          76 :   paint_to_canvas (canvas, canvas::coord_t (0, 0), tg, theme);
     182                 :          76 :   return canvas;
     183                 :          76 : }
     184                 :             : 
     185                 :             : void
     186                 :         221 : table::paint_to_canvas (canvas &canvas,
     187                 :             :                         canvas::coord_t offset,
     188                 :             :                         const table_geometry &tg,
     189                 :             :                         const theme &theme) const
     190                 :             : {
     191                 :         221 :   canvas.fill (canvas::rect_t (offset, tg.get_canvas_size ()),
     192                 :         221 :                styled_unichar (' '));
     193                 :         221 :   paint_cell_borders_to_canvas (canvas, offset, tg, theme);
     194                 :         221 :   paint_cell_contents_to_canvas (canvas, offset, tg);
     195                 :         221 : }
     196                 :             : 
     197                 :             : /* Print this table to stderr.  */
     198                 :             : 
     199                 :             : DEBUG_FUNCTION void
     200                 :           0 : table::debug () const
     201                 :             : {
     202                 :             :   /* Use a temporary style manager.
     203                 :             :      Styles in the table will be meaningless, so
     204                 :             :      print the canvas with styling disabled.  */
     205                 :           0 :   style_manager sm;
     206                 :           0 :   canvas canvas (to_canvas (unicode_theme (), sm));
     207                 :           0 :   canvas.debug (false);
     208                 :           0 : }
     209                 :             : 
     210                 :             : /* Move OTHER's content this table, starting at OFFSET.  */
     211                 :             : 
     212                 :             : void
     213                 :          24 : table::add_other_table (table &&other,
     214                 :             :                         table::coord_t offset)
     215                 :             : {
     216                 :         252 :   for (auto &&placement : other.m_placements)
     217                 :             :     {
     218                 :         228 :       set_cell_span (placement.m_rect + offset,
     219                 :         228 :                      std::move (placement.m_content),
     220                 :             :                      placement.m_x_align,
     221                 :             :                      placement.m_y_align);
     222                 :             :     }
     223                 :          24 : }
     224                 :             : 
     225                 :             : const table::cell_placement *
     226                 :         362 : table::get_placement_at (coord_t coord) const
     227                 :             : {
     228                 :         362 :   const int placement_idx = m_occupancy.get (coord);
     229                 :         362 :   if (placement_idx == -1)
     230                 :             :     return nullptr;
     231                 :         142 :   return &m_placements[placement_idx];
     232                 :             : }
     233                 :             : 
     234                 :             : int
     235                 :       42528 : table::get_occupancy_safe (coord_t coord) const
     236                 :             : {
     237                 :       42528 :   if (coord.x < 0)
     238                 :             :     return -1;
     239                 :       40882 :   if (coord.x >= m_size.w)
     240                 :             :     return -1;
     241                 :       39236 :   if (coord.y < 0)
     242                 :             :     return -1;
     243                 :       35530 :   if (coord.y >= m_size.h)
     244                 :             :     return -1;
     245                 :       31824 :   return m_occupancy.get (coord);
     246                 :             : }
     247                 :             : 
     248                 :             : /* Determine if the "?" edges need borders for table cell D
     249                 :             :    in the following, for the directions relative to "X", based
     250                 :             :    on whether each of table cell boundaries AB, CD, AC, and BD
     251                 :             :    are boundaries between cell spans:
     252                 :             : 
     253                 :             :    #            up?
     254                 :             :    #      +-----+-----+
     255                 :             :    #      |           |
     256                 :             :    #      |     ?     |
     257                 :             :    #      |  A  ?  B  |
     258                 :             :    #      |     ?     |
     259                 :             :    #      |           |
     260                 :             :    # left?+ ??? X ??? + right?
     261                 :             :    #      |           |
     262                 :             :    #      |     ?     |
     263                 :             :    #      |  C  ?  D  |
     264                 :             :    #      |     ?     |
     265                 :             :    #      |           |
     266                 :             :    #      +-----+-----+
     267                 :             :    #          down?
     268                 :             : */
     269                 :             : 
     270                 :             : directions
     271                 :       10632 : table::get_connections (int table_x, int table_y) const
     272                 :             : {
     273                 :       10632 :   int cell_a = get_occupancy_safe (coord_t (table_x - 1, table_y - 1));
     274                 :       10632 :   int cell_b = get_occupancy_safe (coord_t (table_x, table_y - 1));
     275                 :       10632 :   int cell_c = get_occupancy_safe (coord_t (table_x - 1, table_y));
     276                 :       10632 :   int cell_d = get_occupancy_safe (coord_t (table_x, table_y));
     277                 :       10632 :   const bool up = (cell_a != cell_b);
     278                 :       10632 :   const bool down = (cell_c != cell_d);
     279                 :       10632 :   const bool left = (cell_a != cell_c);
     280                 :       10632 :   const bool right = (cell_b != cell_d);
     281                 :       10632 :   return directions (up, down, left, right);
     282                 :             : }
     283                 :             : 
     284                 :             : /* Paint the grid lines.
     285                 :             : 
     286                 :             :    Consider painting
     287                 :             :    - a grid of cells,
     288                 :             :    - plus a right-hand border
     289                 :             :    - and a bottom border
     290                 :             : 
     291                 :             :    Then we need to paint to the canvas like this:
     292                 :             : 
     293                 :             :    #         PER-TABLE-COLUMN     R BORDER
     294                 :             :    #      +-------------------+   +-----+
     295                 :             :    #
     296                 :             :    #             TABLE CELL WIDTH (in canvas units)
     297                 :             :    #            +-------------+
     298                 :             :    #      .     .     . .     .   .     .
     299                 :             :    #   ...+-----+-----+.+-----+...+-----+ +
     300                 :             :    #      |  U  |     |.|     |   |  U  | |
     301                 :             :    #      |  U  |     |.|     |   |  U  | |
     302                 :             :    #      |LL+RR|RRRRR|.|RRRRR|   |LL+  | |
     303                 :             :    #      |  D  |     |.|     |   |  D  | |
     304                 :             :    #      |  D  |     |.|     |   |  D  | |
     305                 :             :    #   ...+-----+-----+.+-----+...+-----+ |
     306                 :             :    #      .....................   ......  +-- PER-TABLE-ROW
     307                 :             :    #   ...+-----+-----+.+-----+...+-----+ | +
     308                 :             :    #      |  D  |     |.|     |   |  D  | | |
     309                 :             :    #      |  D  |     |.|     |   |  D  | | |
     310                 :             :    #      |  D  |     |.|     |   |  D  | | +---- TABLE CELL HEIGHT (in canvas units)
     311                 :             :    #      |  D  |     |.|     |   |  D  | | |
     312                 :             :    #      |  D  |     |.|     |   |  D  | | |
     313                 :             :    #   ...+-----+-----+.+-----+...+-----+ + +
     314                 :             :    #      .     .     .     .   .     .
     315                 :             :    #   ...+-----+-----+.+-----+...+-----+  +
     316                 :             :    #      |  D  |     |.|     |   |  U  |  |
     317                 :             :    #      |  D  |     |.|     |   |  U  |  |
     318                 :             :    #      |LL+RR|RRRRR|.|RRRRR|   |LL+  |  | BOTTOM BORDER
     319                 :             :    #      |     |     |.|     |   |     |  |
     320                 :             :    #      |     |     |.|     |   |     |  |
     321                 :             :    #   ...+-----+-----+.+-----+...+-----+  +
     322                 :             : 
     323                 :             :    where each:
     324                 :             : 
     325                 :             :    #    +-----+
     326                 :             :    #    |     |
     327                 :             :    #    |     |
     328                 :             :    #    |     |
     329                 :             :    #    |     |
     330                 :             :    #    |     |
     331                 :             :    #    +-----+
     332                 :             : 
     333                 :             :    is a canvas cell, and the U, L, R, D express the connections
     334                 :             :    that are present with neighboring table cells.  These affect
     335                 :             :    the kinds of borders that we draw for a particular table cell.  */
     336                 :             : 
     337                 :             : void
     338                 :         221 : table::paint_cell_borders_to_canvas (canvas &canvas,
     339                 :             :                                      canvas::coord_t offset,
     340                 :             :                                      const table_geometry &tg,
     341                 :             :                                      const theme &theme) const
     342                 :             : {
     343                 :             :   /* The per-table-cell left and top borders are either drawn or not,
     344                 :             :      but if they are, they aren't affected by per-table-cell connections.  */
     345                 :         221 :   const canvas::cell_t left_border
     346                 :         221 :     = theme.get_line_art (directions (true, /* up */
     347                 :             :                                       true, /* down */
     348                 :             :                                       false, /* left */
     349                 :         221 :                                       false /* right */));
     350                 :         221 :   const canvas::cell_t top_border
     351                 :         221 :     = theme.get_line_art (directions (false, /* up */
     352                 :             :                                       false, /* down */
     353                 :             :                                       true, /* left */
     354                 :         221 :                                       true)); /* right */
     355                 :         823 :   for (int table_y = 0; table_y < m_size.h; table_y++)
     356                 :             :     {
     357                 :         602 :       const int canvas_y = tg.table_y_to_canvas_y (table_y);
     358                 :        8558 :       for (int table_x = 0; table_x < m_size.w; table_x++)
     359                 :             :         {
     360                 :        7956 :           canvas::coord_t canvas_top_left
     361                 :        7956 :             = tg.table_to_canvas(table::coord_t (table_x, table_y));
     362                 :             : 
     363                 :        7956 :           const directions c (get_connections (table_x, table_y));
     364                 :             : 
     365                 :             :           /* Paint top-left corner of border, if any.  */
     366                 :        7956 :           canvas.paint (offset + canvas_top_left,
     367                 :        7956 :                         theme.get_line_art (c));
     368                 :             : 
     369                 :             :           /* Paint remainder of left border of cell, if any.
     370                 :             :              We assume here that the content occupies a single canvas row.  */
     371                 :        7956 :           if (c.m_down)
     372                 :        5484 :             canvas.paint (offset + canvas::coord_t (canvas_top_left.x,
     373                 :        2742 :                                                     canvas_y + 1),
     374                 :             :                           left_border);
     375                 :             : 
     376                 :             :           /* Paint remainder of top border of cell, if any.  */
     377                 :        7956 :           if (c.m_right)
     378                 :             :             {
     379                 :        6002 :               const int col_width = tg.get_col_width (table_x);
     380                 :       27147 :               for (int x_offset = 0; x_offset < col_width; x_offset++)
     381                 :             :                 {
     382                 :       21145 :                   const int canvas_x = canvas_top_left.x + 1 + x_offset;
     383                 :       21145 :                   canvas.paint (offset + canvas::coord_t (canvas_x, canvas_y),
     384                 :             :                                 top_border);
     385                 :             :                 }
     386                 :             :             }
     387                 :             :         }
     388                 :             : 
     389                 :             :       /* Paint right-hand border of row.  */
     390                 :         602 :       const int table_x = m_size.w;
     391                 :         602 :       const int canvas_x = tg.table_x_to_canvas_x (table_x);
     392                 :         602 :       const directions c (get_connections (m_size.w, table_y));
     393                 :         602 :       canvas.paint(offset + canvas::coord_t (canvas_x, canvas_y),
     394                 :         602 :                    theme.get_line_art (directions (c.m_up,
     395                 :         602 :                                                    c.m_down,
     396                 :         602 :                                                    c.m_left,
     397                 :         602 :                                                    false))); /* right */
     398                 :             :       /* We assume here that the content occupies a single canvas row.  */
     399                 :         602 :       canvas.paint(offset + canvas::coord_t (canvas_x, canvas_y + 1),
     400                 :         602 :                    theme.get_line_art (directions (c.m_down, /* up */
     401                 :             :                                                    c.m_down, /* down */
     402                 :             :                                                    false, /* left */
     403                 :         602 :                                                    false))); /* right */
     404                 :             :     }
     405                 :             : 
     406                 :             :   /* Draw bottom border of table.  */
     407                 :         221 :   {
     408                 :         221 :     const int canvas_y = tg.get_canvas_size ().h - 1;
     409                 :        2074 :     for (int table_x = 0; table_x < m_size.w; table_x++)
     410                 :             :       {
     411                 :        1853 :         const directions c (get_connections (table_x, m_size.h));
     412                 :        1853 :         const int left_canvas_x = tg.table_x_to_canvas_x (table_x);
     413                 :        1853 :         canvas.paint (offset + canvas::coord_t (left_canvas_x, canvas_y),
     414                 :        1853 :                       theme.get_line_art (directions (c.m_up,
     415                 :             :                                                       false, /* down */
     416                 :        1853 :                                                       c.m_left,
     417                 :        1853 :                                                       c.m_right)));
     418                 :        1853 :         const int col_width = tg.get_col_width (table_x);
     419                 :       12917 :         for (int x_offset = 0; x_offset < col_width; x_offset++)
     420                 :             :           {
     421                 :       11064 :             const int canvas_x = left_canvas_x + 1 + x_offset;
     422                 :       11064 :             canvas.paint(offset + canvas::coord_t (canvas_x, canvas_y),
     423                 :       22128 :                          theme.get_line_art (directions (false, // up
     424                 :             :                                                          false, // down
     425                 :             :                                                          c.m_right, // left
     426                 :       11064 :                                                          c.m_right))); // right
     427                 :             :           }
     428                 :             :       }
     429                 :             : 
     430                 :             :     /* Bottom-right corner of table.  */
     431                 :         221 :     const int table_x = m_size.w;
     432                 :         221 :     const int canvas_x = tg.table_x_to_canvas_x (table_x);
     433                 :         221 :     const directions c (get_connections (m_size.w, m_size.h));
     434                 :         221 :     canvas.paint (offset + canvas::coord_t (canvas_x, canvas_y),
     435                 :         442 :                   theme.get_line_art (directions (c.m_up, // up
     436                 :             :                                                   false, // down
     437                 :         221 :                                                   c.m_left, // left
     438                 :         221 :                                                   false))); // right
     439                 :             :   }
     440                 :         221 : }
     441                 :             : 
     442                 :             : void
     443                 :         221 : table::paint_cell_contents_to_canvas(canvas &canvas,
     444                 :             :                                      canvas::coord_t offset,
     445                 :             :                                      const table_geometry &tg) const
     446                 :             : {
     447                 :        2548 :   for (auto &placement : m_placements)
     448                 :        2327 :     placement.paint_cell_contents_to_canvas (canvas, offset, tg);
     449                 :         221 : }
     450                 :             : 
     451                 :             : /* class table_cell_sizes.  */
     452                 :             : 
     453                 :             : /* Consider 1x1 cells.  */
     454                 :             : 
     455                 :             : void
     456                 :         217 : table_cell_sizes::pass_1 (const table &table)
     457                 :             : {
     458                 :        2508 :   for (auto &placement : table.m_placements)
     459                 :        4117 :     if (placement.one_by_one_p ())
     460                 :             :       {
     461                 :        1783 :         canvas::size_t canvas_size (placement.get_min_canvas_size ());
     462                 :        1783 :         table::coord_t table_coord (placement.m_rect.m_top_left);
     463                 :        1783 :         m_col_widths.require (table_coord.x, canvas_size.w);
     464                 :        2232 :         m_row_heights.require (table_coord.y, canvas_size.h);
     465                 :             :       }
     466                 :         217 : }
     467                 :             : 
     468                 :             : /* Consider cells that span more than one row or column.  */
     469                 :             : 
     470                 :             : void
     471                 :         217 : table_cell_sizes::pass_2 (const table &table)
     472                 :             : {
     473                 :        2508 :   for (auto &placement : table.m_placements)
     474                 :        4582 :     if (!placement.one_by_one_p ())
     475                 :             :       {
     476                 :         508 :         const canvas::size_t req_canvas_size (placement.get_min_canvas_size ());
     477                 :         508 :         const canvas::size_t current_canvas_size
     478                 :         508 :           = get_canvas_size (placement.m_rect);
     479                 :             :         /* Grow columns as necessary.  */
     480                 :         508 :         if (req_canvas_size.w > current_canvas_size.w)
     481                 :             :           {
     482                 :             :             /* Spread the deficit amongst the columns.  */
     483                 :          88 :             int deficit = req_canvas_size.w - current_canvas_size.w;
     484                 :          88 :             const int per_col = deficit / placement.m_rect.m_size.w;
     485                 :          88 :             for (int table_x = placement.m_rect.get_min_x ();
     486                 :         327 :                  table_x < placement.m_rect.get_next_x ();
     487                 :             :                  table_x++)
     488                 :             :             {
     489                 :         239 :               m_col_widths.m_requirements[table_x] += per_col;
     490                 :         239 :               deficit -= per_col;
     491                 :             :             }
     492                 :             :             /* Make sure we allocate all of the deficit.  */
     493                 :          88 :             if (deficit > 0)
     494                 :             :               {
     495                 :          45 :                 const int table_x = placement.m_rect.get_max_x ();
     496                 :          45 :                 m_col_widths.m_requirements[table_x] += deficit;
     497                 :             :               }
     498                 :             :           }
     499                 :             :         /* Grow rows as necessary.  */
     500                 :         508 :         if (req_canvas_size.h > current_canvas_size.h)
     501                 :             :           {
     502                 :             :             /* Spread the deficit amongst the rows.  */
     503                 :         105 :             int deficit = req_canvas_size.h - current_canvas_size.h;
     504                 :         105 :             const int per_row = deficit / placement.m_rect.m_size.h;
     505                 :         105 :             for (int table_y = placement.m_rect.get_min_y ();
     506                 :         210 :                  table_y < placement.m_rect.get_next_y ();
     507                 :             :                  table_y++)
     508                 :             :             {
     509                 :         105 :               m_row_heights.m_requirements[table_y] += per_row;
     510                 :         105 :               deficit -= per_row;
     511                 :             :             }
     512                 :             :             /* Make sure we allocate all of the deficit.  */
     513                 :         105 :             if (deficit > 0)
     514                 :             :               {
     515                 :           0 :                 const int table_y = placement.m_rect.get_max_y ();
     516                 :           0 :                 m_row_heights.m_requirements[table_y] += deficit;
     517                 :             :               }
     518                 :             :           }
     519                 :             :       }
     520                 :         217 : }
     521                 :             : 
     522                 :             : canvas::size_t
     523                 :        2835 : table_cell_sizes::get_canvas_size (const table::rect_t &rect) const
     524                 :             : {
     525                 :        2835 :   canvas::size_t result (0, 0);
     526                 :       12836 :   for (int table_x = rect.get_min_x ();
     527                 :       12836 :        table_x < rect.get_next_x ();
     528                 :             :        table_x ++)
     529                 :       10001 :     result.w += m_col_widths.m_requirements[table_x];
     530                 :        6072 :   for (int table_y = rect.get_min_y ();
     531                 :        6072 :        table_y < rect.get_next_y ();
     532                 :             :        table_y ++)
     533                 :        3237 :     result.h += m_row_heights.m_requirements[table_y];
     534                 :             :   /* Allow space for the borders.  */
     535                 :        2835 :   result.w += rect.m_size.w - 1;
     536                 :        2835 :   result.h += rect.m_size.h - 1;
     537                 :        2835 :   return result;
     538                 :             : }
     539                 :             : 
     540                 :             : /* class text_art::table_geometry.  */
     541                 :             : 
     542                 :         217 : table_geometry::table_geometry (const table &table, table_cell_sizes &cell_sizes)
     543                 :         217 : : m_cell_sizes (cell_sizes),
     544                 :         217 :   m_canvas_size (canvas::size_t (0, 0)),
     545                 :         217 :   m_col_start_x (table.get_size ().w),
     546                 :         217 :   m_row_start_y (table.get_size ().h)
     547                 :             : {
     548                 :         217 :   recalc_coords ();
     549                 :         217 : }
     550                 :             : 
     551                 :             : void
     552                 :         358 : table_geometry::recalc_coords ()
     553                 :             : {
     554                 :             :   /* Start canvas column of table cell, including leading border.  */
     555                 :         358 :   m_col_start_x.clear ();
     556                 :         358 :   int iter_canvas_x = 0;
     557                 :        3328 :   for (auto w : m_cell_sizes.m_col_widths.m_requirements)
     558                 :             :     {
     559                 :        2970 :       m_col_start_x.push_back (iter_canvas_x);
     560                 :        2970 :       iter_canvas_x += w + 1;
     561                 :             :     }
     562                 :             : 
     563                 :             :   /* Start canvas row of table cell, including leading border.  */
     564                 :         358 :   m_row_start_y.clear ();
     565                 :         358 :   int iter_canvas_y = 0;
     566                 :        1194 :   for (auto h : m_cell_sizes.m_row_heights.m_requirements)
     567                 :             :     {
     568                 :         836 :       m_row_start_y.push_back (iter_canvas_y);
     569                 :         836 :       iter_canvas_y += h + 1;
     570                 :             :     }
     571                 :             : 
     572                 :         358 :   m_canvas_size = canvas::size_t (iter_canvas_x + 1,
     573                 :         358 :                                   iter_canvas_y + 1);
     574                 :         358 : }
     575                 :             : 
     576                 :             : /* Get the TL corner of the table cell at TABLE_COORD
     577                 :             :    in canvas coords (including the border).  */
     578                 :             : 
     579                 :             : canvas::coord_t
     580                 :       10283 : table_geometry::table_to_canvas (table::coord_t table_coord) const
     581                 :             : {
     582                 :       10283 :   return canvas::coord_t (table_x_to_canvas_x (table_coord.x),
     583                 :       10283 :                           table_y_to_canvas_y (table_coord.y));
     584                 :             : }
     585                 :             : 
     586                 :             : /* Get the left border of the table cell at column TABLE_X
     587                 :             :    in canvas coords (including the border).  */
     588                 :             : 
     589                 :             : int
     590                 :       12959 : table_geometry::table_x_to_canvas_x (int table_x) const
     591                 :             : {
     592                 :             :   /* Allow one beyond the end, for the right-hand border of the table.  */
     593                 :       12959 :   if (table_x == (int)m_col_start_x.size ())
     594                 :         823 :     return m_canvas_size.w - 1;
     595                 :       12136 :   return m_col_start_x[table_x];
     596                 :             : }
     597                 :             : 
     598                 :             : /* Get the top border of the table cell at column TABLE_Y
     599                 :             :    in canvas coords (including the border).  */
     600                 :             : 
     601                 :             : int
     602                 :       10885 : table_geometry::table_y_to_canvas_y (int table_y) const
     603                 :             : {
     604                 :             :   /* Allow one beyond the end, for the right-hand border of the table.  */
     605                 :       10885 :   if (table_y == (int)m_row_start_y.size ())
     606                 :           0 :     return m_canvas_size.h - 1;
     607                 :       10885 :   return m_row_start_y[table_y];
     608                 :             : }
     609                 :             : 
     610                 :             : /* class text_art::simple_table_geometry.  */
     611                 :             : 
     612                 :           4 : simple_table_geometry::simple_table_geometry (const table &table)
     613                 :           4 : : m_col_widths (table.get_size ().w),
     614                 :           4 :   m_row_heights (table.get_size ().h),
     615                 :           4 :   m_cell_sizes (m_col_widths, m_row_heights),
     616                 :           4 :   m_tg (table, m_cell_sizes)
     617                 :             : {
     618                 :           4 :   m_cell_sizes.pass_1 (table);
     619                 :           4 :   m_cell_sizes.pass_2 (table);
     620                 :           4 :   m_tg.recalc_coords ();
     621                 :           4 : }
     622                 :             : 
     623                 :             : #if CHECKING_P
     624                 :             : 
     625                 :             : namespace selftest {
     626                 :             : 
     627                 :             : static void
     628                 :           4 : test_tic_tac_toe ()
     629                 :             : {
     630                 :           4 :   style_manager sm;
     631                 :           4 :   table t (table::size_t (3, 3));
     632                 :           4 :   t.set_cell (table::coord_t (0, 0), styled_string (sm, "X"));
     633                 :           4 :   t.set_cell (table::coord_t (1, 0), styled_string (sm, ""));
     634                 :           4 :   t.set_cell (table::coord_t (2, 0), styled_string (sm, ""));
     635                 :           4 :   t.set_cell (table::coord_t (0, 1), styled_string (sm, "O"));
     636                 :           4 :   t.set_cell (table::coord_t (1, 1), styled_string (sm, "O"));
     637                 :           4 :   t.set_cell (table::coord_t (2, 1), styled_string (sm, ""));
     638                 :           4 :   t.set_cell (table::coord_t (0, 2), styled_string (sm, "X"));
     639                 :           4 :   t.set_cell (table::coord_t (1, 2), styled_string (sm, ""));
     640                 :           4 :   t.set_cell (table::coord_t (2, 2), styled_string (sm, "O"));
     641                 :             : 
     642                 :           4 :   {
     643                 :           4 :     canvas canvas (t.to_canvas (ascii_theme (), sm));
     644                 :           4 :     ASSERT_CANVAS_STREQ
     645                 :             :       (canvas, false,
     646                 :             :        ("+-+-+-+\n"
     647                 :             :         "|X| | |\n"
     648                 :             :         "+-+-+-+\n"
     649                 :             :         "|O|O| |\n"
     650                 :             :         "+-+-+-+\n"
     651                 :             :         "|X| |O|\n"
     652                 :             :         "+-+-+-+\n"));
     653                 :           4 :   }
     654                 :             : 
     655                 :           4 :   {
     656                 :           4 :     canvas canvas (t.to_canvas (unicode_theme (), sm));
     657                 :           4 :     ASSERT_CANVAS_STREQ
     658                 :             :       (canvas, false,
     659                 :             :        // FIXME: are we allowed unicode chars in string literals in our source?
     660                 :             :        ("┌─┬─┬─┐\n"
     661                 :             :         "│X│ │ │\n"
     662                 :             :         "├─┼─┼─┤\n"
     663                 :             :         "│O│O│ │\n"
     664                 :             :         "├─┼─┼─┤\n"
     665                 :             :         "│X│ │O│\n"
     666                 :             :         "└─┴─┴─┘\n"));
     667                 :           4 :   }
     668                 :           4 : }
     669                 :             : 
     670                 :             : static table
     671                 :           8 : make_text_table ()
     672                 :             : {
     673                 :           8 :   style_manager sm;
     674                 :           8 :   table t (table::size_t (3, 3));
     675                 :           8 :   t.set_cell (table::coord_t (0, 0), styled_string (sm, "top left"));
     676                 :           8 :   t.set_cell (table::coord_t (1, 0), styled_string (sm, "top middle"));
     677                 :           8 :   t.set_cell (table::coord_t (2, 0), styled_string (sm, "top right"));
     678                 :           8 :   t.set_cell (table::coord_t (0, 1), styled_string (sm, "middle left"));
     679                 :           8 :   t.set_cell (table::coord_t (1, 1), styled_string (sm, "middle middle"));
     680                 :           8 :   t.set_cell (table::coord_t (2, 1), styled_string (sm, "middle right"));
     681                 :           8 :   t.set_cell (table::coord_t (0, 2), styled_string (sm, "bottom left"));
     682                 :           8 :   t.set_cell (table::coord_t (1, 2), styled_string (sm, "bottom middle"));
     683                 :           8 :   t.set_cell (table::coord_t (2, 2), styled_string (sm, "bottom right"));
     684                 :           8 :   return t;
     685                 :           8 : }
     686                 :             : 
     687                 :             : static void
     688                 :           4 : test_text_table ()
     689                 :             : {
     690                 :           4 :   style_manager sm;
     691                 :           4 :   table table = make_text_table ();
     692                 :           4 :   {
     693                 :           4 :     canvas canvas (table.to_canvas (ascii_theme(), sm));
     694                 :           4 :     ASSERT_CANVAS_STREQ
     695                 :             :       (canvas, false,
     696                 :             :        ("+-----------+-------------+------------+\n"
     697                 :             :         "| top left  | top middle  | top right  |\n"
     698                 :             :         "+-----------+-------------+------------+\n"
     699                 :             :         "|middle left|middle middle|middle right|\n"
     700                 :             :         "+-----------+-------------+------------+\n"
     701                 :             :         "|bottom left|bottom middle|bottom right|\n"
     702                 :             :         "+-----------+-------------+------------+\n"));
     703                 :           4 :   }
     704                 :           4 :   {
     705                 :           4 :     canvas canvas (table.to_canvas (unicode_theme(), sm));
     706                 :           4 :     ASSERT_CANVAS_STREQ
     707                 :             :       (canvas, false,
     708                 :             :        // FIXME: are we allowed unicode chars in string literals in our source?
     709                 :             :        ("┌───────────┬─────────────┬────────────┐\n"
     710                 :             :         "│ top left  │ top middle  │ top right  │\n"
     711                 :             :         "├───────────┼─────────────┼────────────┤\n"
     712                 :             :         "│middle left│middle middle│middle right│\n"
     713                 :             :         "├───────────┼─────────────┼────────────┤\n"
     714                 :             :         "│bottom left│bottom middle│bottom right│\n"
     715                 :             :         "└───────────┴─────────────┴────────────┘\n"));
     716                 :           4 :   }
     717                 :           4 : }
     718                 :             : 
     719                 :             : static void
     720                 :           4 : test_offset_table ()
     721                 :             : {
     722                 :           4 :   style_manager sm;
     723                 :           4 :   table table = make_text_table ();
     724                 :           4 :   simple_table_geometry stg (table);
     725                 :           4 :   const canvas::size_t tcs = stg.m_tg.get_canvas_size();
     726                 :           4 :   {
     727                 :           4 :     canvas canvas (canvas::size_t (tcs.w + 5, tcs.h + 5), sm);
     728                 :           4 :     canvas.debug_fill ();
     729                 :           4 :     table.paint_to_canvas (canvas, canvas::coord_t (3, 3),
     730                 :             :                            stg.m_tg,
     731                 :           4 :                            ascii_theme());
     732                 :           4 :     ASSERT_CANVAS_STREQ
     733                 :             :       (canvas, false,
     734                 :             :        ("*********************************************\n"
     735                 :             :         "*********************************************\n"
     736                 :             :         "*********************************************\n"
     737                 :             :         "***+-----------+-------------+------------+**\n"
     738                 :             :         "***| top left  | top middle  | top right  |**\n"
     739                 :             :         "***+-----------+-------------+------------+**\n"
     740                 :             :         "***|middle left|middle middle|middle right|**\n"
     741                 :             :         "***+-----------+-------------+------------+**\n"
     742                 :             :         "***|bottom left|bottom middle|bottom right|**\n"
     743                 :             :         "***+-----------+-------------+------------+**\n"
     744                 :             :         "*********************************************\n"
     745                 :             :         "*********************************************\n"));
     746                 :           4 :   }
     747                 :           4 :   {
     748                 :           4 :     canvas canvas (canvas::size_t (tcs.w + 5, tcs.h + 5), sm);
     749                 :           4 :     canvas.debug_fill ();
     750                 :           4 :     table.paint_to_canvas (canvas, canvas::coord_t (3, 3),
     751                 :             :                            stg.m_tg,
     752                 :           4 :                            unicode_theme());
     753                 :           4 :     ASSERT_CANVAS_STREQ
     754                 :             :       (canvas, false,
     755                 :             :        // FIXME: are we allowed unicode chars in string literals in our source?
     756                 :             :        ("*********************************************\n"
     757                 :             :         "*********************************************\n"
     758                 :             :         "*********************************************\n"
     759                 :             :         "***┌───────────┬─────────────┬────────────┐**\n"
     760                 :             :         "***│ top left  │ top middle  │ top right  │**\n"
     761                 :             :         "***├───────────┼─────────────┼────────────┤**\n"
     762                 :             :         "***│middle left│middle middle│middle right│**\n"
     763                 :             :         "***├───────────┼─────────────┼────────────┤**\n"
     764                 :             :         "***│bottom left│bottom middle│bottom right│**\n"
     765                 :             :         "***└───────────┴─────────────┴────────────┘**\n"
     766                 :             :         "*********************************************\n"
     767                 :             :         "*********************************************\n"));
     768                 :           4 :   }
     769                 :           8 : }
     770                 :             : 
     771                 :             : #define ASSERT_TABLE_CELL_STREQ(TABLE, TABLE_X, TABLE_Y, EXPECTED_STR)  \
     772                 :             :   SELFTEST_BEGIN_STMT                                                   \
     773                 :             :     table::coord_t coord ((TABLE_X), (TABLE_Y));                        \
     774                 :             :     const table::cell_placement *cp = (TABLE).get_placement_at (coord); \
     775                 :             :     ASSERT_NE (cp, nullptr);                                            \
     776                 :             :     ASSERT_EQ (cp->get_content (), styled_string (sm, EXPECTED_STR)); \
     777                 :             :   SELFTEST_END_STMT
     778                 :             : 
     779                 :             : #define ASSERT_TABLE_NULL_CELL(TABLE, TABLE_X, TABLE_Y)                 \
     780                 :             :   SELFTEST_BEGIN_STMT                                                   \
     781                 :             :     table::coord_t coord ((TABLE_X), (TABLE_Y));                        \
     782                 :             :     const table::cell_placement *cp = (TABLE).get_placement_at (coord); \
     783                 :             :     ASSERT_EQ (cp, nullptr);                                            \
     784                 :             :   SELFTEST_END_STMT
     785                 :             : 
     786                 :             : static void
     787                 :           4 : test_spans ()
     788                 :             : {
     789                 :           4 :   style_manager sm;
     790                 :           4 :   table table (table::size_t (3, 3));
     791                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (0, 0),
     792                 :           4 :                                       table::size_t (3, 1)),
     793                 :           8 :                        styled_string (sm, "ABC"));
     794                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (0, 1),
     795                 :           4 :                                       table::size_t (2, 1)),
     796                 :           8 :                        styled_string (sm, "DE"));
     797                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (2, 1),
     798                 :           4 :                                       table::size_t (1, 1)),
     799                 :           8 :                        styled_string (sm, "F"));
     800                 :           4 :   table.set_cell (table::coord_t (0, 2), styled_string (sm, "G"));
     801                 :           4 :   table.set_cell (table::coord_t (1, 2), styled_string (sm, "H"));
     802                 :           4 :   table.set_cell (table::coord_t (2, 2), styled_string (sm, "I"));
     803                 :           4 :   {
     804                 :           4 :     canvas canvas (table.to_canvas (ascii_theme(), sm));
     805                 :           4 :     ASSERT_CANVAS_STREQ
     806                 :             :       (canvas, false,
     807                 :             :        ("+-----+\n"
     808                 :             :         "| ABC |\n"
     809                 :             :         "+---+-+\n"
     810                 :             :         "|DE |F|\n"
     811                 :             :         "+-+-+-+\n"
     812                 :             :         "|G|H|I|\n"
     813                 :             :         "+-+-+-+\n"));
     814                 :           4 :   }
     815                 :           4 :   {
     816                 :           4 :     canvas canvas (table.to_canvas (unicode_theme(), sm));
     817                 :           4 :     ASSERT_CANVAS_STREQ
     818                 :             :       (canvas, false,
     819                 :             :        // FIXME: are we allowed unicode chars in string literals in our source?
     820                 :             :        ("┌─────┐\n"
     821                 :             :         "│ ABC │\n"
     822                 :             :         "├───┬─┤\n"
     823                 :             :         "│DE │F│\n"
     824                 :             :         "├─┬─┼─┤\n"
     825                 :             :         "│G│H│I│\n"
     826                 :             :         "└─┴─┴─┘\n"));
     827                 :           4 :   }
     828                 :           4 : }
     829                 :             : 
     830                 :             : /* Verify building this 5x5 table with spans:
     831                 :             :      |0|1|2|3|4|
     832                 :             :      +-+-+-+-+-+
     833                 :             :     0|A A A|B|C|0
     834                 :             :      +     +-+ +
     835                 :             :     1|A A A|D|C|1
     836                 :             :      +     +-+-+
     837                 :             :     2|A A A|E|F|2
     838                 :             :      +-+-+-+-+-+
     839                 :             :     3|G G|H|I I|3
     840                 :             :      |   | +-+-+
     841                 :             :     4|G G|H|J J|4
     842                 :             :      +-+-+-+-+-+
     843                 :             :      |0|1|2|3|4|
     844                 :             : */
     845                 :             : 
     846                 :             : static void
     847                 :           4 : test_spans_2 ()
     848                 :             : {
     849                 :           4 :   style_manager sm;
     850                 :           4 :   table table (table::size_t (5, 5));
     851                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (0, 0),
     852                 :           4 :                                       table::size_t (3, 3)),
     853                 :           8 :                        styled_string (sm, "A"));
     854                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (3, 0),
     855                 :           4 :                                       table::size_t (1, 1)),
     856                 :           8 :                        styled_string (sm, "B"));
     857                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (4, 0),
     858                 :           4 :                                       table::size_t (1, 2)),
     859                 :           8 :                        styled_string (sm, "C"));
     860                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (3, 1),
     861                 :           4 :                                       table::size_t (1, 1)),
     862                 :           8 :                        styled_string (sm, "D"));
     863                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (3, 2),
     864                 :           4 :                                       table::size_t (1, 1)),
     865                 :           8 :                        styled_string (sm, "E"));
     866                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (4, 2),
     867                 :           4 :                                       table::size_t (1, 1)),
     868                 :           8 :                        styled_string (sm, "F"));
     869                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (0, 3),
     870                 :           4 :                                       table::size_t (2, 2)),
     871                 :           8 :                        styled_string (sm, "G"));
     872                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (2, 3),
     873                 :           4 :                                       table::size_t (1, 2)),
     874                 :           8 :                        styled_string (sm, "H"));
     875                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (3, 3),
     876                 :           4 :                                       table::size_t (2, 1)),
     877                 :           8 :                        styled_string (sm, "I"));
     878                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (3, 4),
     879                 :           4 :                                       table::size_t (2, 1)),
     880                 :           8 :                        styled_string (sm, "J"));
     881                 :             : 
     882                 :             :   /* Check occupancy at each table coordinate.  */
     883                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 0, 0, "A");
     884                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 1, 0, "A");
     885                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 2, 0, "A");
     886                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 3, 0, "B");
     887                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 4, 0, "C");
     888                 :             : 
     889                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 0, 1, "A");
     890                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 1, 1, "A");
     891                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 2, 1, "A");
     892                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 3, 1, "D");
     893                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 4, 1, "C");
     894                 :             : 
     895                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 0, 2, "A");
     896                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 1, 2, "A");
     897                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 2, 2, "A");
     898                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 3, 2, "E");
     899                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 4, 2, "F");
     900                 :             : 
     901                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 0, 3, "G");
     902                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 1, 3, "G");
     903                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 2, 3, "H");
     904                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 3, 3, "I");
     905                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 4, 3, "I");
     906                 :             : 
     907                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 0, 4, "G");
     908                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 1, 4, "G");
     909                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 2, 4, "H");
     910                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 3, 4, "J");
     911                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 4, 4, "J");
     912                 :             : 
     913                 :           4 :   {
     914                 :           4 :     canvas canvas (table.to_canvas (ascii_theme(), sm));
     915                 :           4 :     ASSERT_CANVAS_STREQ
     916                 :             :       (canvas, false,
     917                 :             :        ("+---+-+-+\n"
     918                 :             :         "|   |B| |\n"
     919                 :             :         "|   +-+C|\n"
     920                 :             :         "| A |D| |\n"
     921                 :             :         "|   +-+-+\n"
     922                 :             :         "|   |E|F|\n"
     923                 :             :         "+-+-+-+-+\n"
     924                 :             :         "| | | I |\n"
     925                 :             :         "|G|H+---+\n"
     926                 :             :         "| | | J |\n"
     927                 :             :         "+-+-+---+\n"));
     928                 :           4 :   }
     929                 :           4 :   {
     930                 :           4 :     canvas canvas (table.to_canvas (unicode_theme(), sm));
     931                 :           4 :     ASSERT_CANVAS_STREQ
     932                 :             :       (canvas, false,
     933                 :             :        // FIXME: are we allowed unicode chars in string literals in our source?
     934                 :             :        ("┌───┬─┬─┐\n"
     935                 :             :         "│   │B│ │\n"
     936                 :             :         "│   ├─┤C│\n"
     937                 :             :         "│ A │D│ │\n"
     938                 :             :         "│   ├─┼─┤\n"
     939                 :             :         "│   │E│F│\n"
     940                 :             :         "├─┬─┼─┴─┤\n"
     941                 :             :         "│ │ │ I │\n"
     942                 :             :         "│G│H├───┤\n"
     943                 :             :         "│ │ │ J │\n"
     944                 :             :         "└─┴─┴───┘\n"));
     945                 :           4 :   }
     946                 :           4 : }
     947                 :             : 
     948                 :             : /* Experiment with adding a 1-table-column gap at the boundary between
     949                 :             :    valid vs invalid for visualizing a buffer overflow.  */
     950                 :             : static void
     951                 :           4 : test_spans_3 ()
     952                 :             : {
     953                 :           4 :   const char * const str = "hello world!";
     954                 :           4 :   const size_t buf_size = 10;
     955                 :           4 :   const size_t str_size = strlen (str) + 1;
     956                 :             : 
     957                 :           4 :   style_manager sm;
     958                 :           4 :   table table (table::size_t (str_size + 1, 3));
     959                 :             : 
     960                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (0, 0),
     961                 :           4 :                                       table::size_t (str_size + 1, 1)),
     962                 :           8 :                        styled_string (sm, "String literal"));
     963                 :             : 
     964                 :          56 :   for (size_t i = 0; i < str_size; i++)
     965                 :             :     {
     966                 :          52 :       table::coord_t c (i, 1);
     967                 :          52 :       if (i >= buf_size)
     968                 :          12 :         c.x++;
     969                 :          52 :       if (str[i] == '\0')
     970                 :           4 :         table.set_cell (c, styled_string (sm, "NUL"));
     971                 :             :       else
     972                 :          48 :         table.set_cell (c, styled_string ((cppchar_t)str[i]));
     973                 :             :     }
     974                 :             : 
     975                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (0, 2),
     976                 :           4 :                                       table::size_t (buf_size, 1)),
     977                 :           8 :                        styled_string::from_fmt (sm,
     978                 :             :                                                      nullptr,
     979                 :             :                                                      "'buf' (char[%i])",
     980                 :             :                                                      (int)buf_size));
     981                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (buf_size + 1, 2),
     982                 :           4 :                                       table::size_t (str_size - buf_size, 1)),
     983                 :           8 :                        styled_string (sm, "overflow"));
     984                 :             : 
     985                 :           4 :   {
     986                 :           4 :     canvas canvas (table.to_canvas (ascii_theme (), sm));
     987                 :           4 :     ASSERT_CANVAS_STREQ
     988                 :             :       (canvas, false,
     989                 :             :        "+-----------------------------+\n"
     990                 :             :        "|       String literal        |\n"
     991                 :             :        "+-+-+-+-+-+-+-+-+-+-++-+-+----+\n"
     992                 :             :        "|h|e|l|l|o| |w|o|r|l||d|!|NUL |\n"
     993                 :             :        "+-+-+-+-+-+-+-+-+-+-++-+-+----+\n"
     994                 :             :        "| 'buf' (char[10])  ||overflow|\n"
     995                 :             :        "+-------------------++--------+\n");
     996                 :           4 :   }
     997                 :           4 :   {
     998                 :           4 :     canvas canvas (table.to_canvas (unicode_theme (), sm));
     999                 :           4 :     ASSERT_CANVAS_STREQ
    1000                 :             :       (canvas, false,
    1001                 :             :        // FIXME: are we allowed unicode chars in string literals in our source?
    1002                 :             :        ("┌─────────────────────────────┐\n"
    1003                 :             :         "│       String literal        │\n"
    1004                 :             :         "├─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬┬─┬─┬────┤\n"
    1005                 :             :         "│h│e│l│l│o│ │w│o│r│l││d│!│NUL │\n"
    1006                 :             :         "├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤├─┴─┴────┤\n"
    1007                 :             :         "│ 'buf' (char[10])  ││overflow│\n"
    1008                 :             :         "└───────────────────┘└────────┘\n"));
    1009                 :           4 :   }
    1010                 :           4 : }
    1011                 :             : 
    1012                 :             : static void
    1013                 :           4 : test_double_width_chars ()
    1014                 :             : {
    1015                 :           4 :   table_cell_content tcc (styled_string ((cppchar_t)0x1f642));
    1016                 :           4 :   ASSERT_EQ (tcc.get_canvas_size ().w, 2);
    1017                 :           4 :   ASSERT_EQ (tcc.get_canvas_size ().h, 1);
    1018                 :             : 
    1019                 :           4 :   style_manager sm;
    1020                 :           4 :   table table (table::size_t (1, 1));
    1021                 :           4 :   table.set_cell (table::coord_t (0,0),
    1022                 :           8 :                   styled_string ((cppchar_t)0x1f642));
    1023                 :             : 
    1024                 :           4 :   canvas canvas (table.to_canvas (unicode_theme(), sm));
    1025                 :           4 :   ASSERT_CANVAS_STREQ
    1026                 :             :     (canvas, false,
    1027                 :             :      // FIXME: are we allowed unicode chars in string literals in our source?
    1028                 :             :      ("┌──┐\n"
    1029                 :             :       "│🙂│\n"
    1030                 :             :       "└──┘\n"));
    1031                 :           8 : }
    1032                 :             : 
    1033                 :             : static void
    1034                 :           4 : test_ipv4_header ()
    1035                 :             : {
    1036                 :           4 :   style_manager sm;
    1037                 :           4 :   table table (table::size_t (34, 10));
    1038                 :           4 :   table.set_cell (table::coord_t (0, 0), styled_string (sm, "Offsets"));
    1039                 :           4 :   table.set_cell (table::coord_t (1, 0), styled_string (sm, "Octet"));
    1040                 :           4 :   table.set_cell (table::coord_t (0, 1), styled_string (sm, "Octet"));
    1041                 :          20 :   for (int octet = 0; octet < 4; octet++)
    1042                 :          16 :     table.set_cell_span (table::rect_t (table::coord_t (2 + (octet * 8), 0),
    1043                 :          16 :                                         table::size_t (8, 1)),
    1044                 :          32 :                          styled_string::from_fmt (sm, nullptr, "%i", octet));
    1045                 :           4 :   table.set_cell (table::coord_t (1, 1), styled_string (sm, "Bit"));
    1046                 :         132 :   for (int bit = 0; bit < 32; bit++)
    1047                 :         128 :     table.set_cell (table::coord_t (bit + 2, 1),
    1048                 :         256 :                     styled_string::from_fmt (sm, nullptr, "%i", bit));
    1049                 :          28 :   for (int word = 0; word < 6; word++)
    1050                 :             :     {
    1051                 :          24 :       table.set_cell (table::coord_t (0, word + 2),
    1052                 :          48 :                       styled_string::from_fmt (sm, nullptr, "%i", word * 4));
    1053                 :          24 :       table.set_cell (table::coord_t (1, word + 2),
    1054                 :          48 :                       styled_string::from_fmt (sm, nullptr, "%i", word * 32));
    1055                 :             :     }
    1056                 :             : 
    1057                 :           4 :   table.set_cell (table::coord_t (0, 8), styled_string (sm, "..."));
    1058                 :           4 :   table.set_cell (table::coord_t (1, 8), styled_string (sm, "..."));
    1059                 :           4 :   table.set_cell (table::coord_t (0, 9), styled_string (sm, "56"));
    1060                 :           4 :   table.set_cell (table::coord_t (1, 9), styled_string (sm, "448"));
    1061                 :             : 
    1062                 :             : #define SET_BITS(FIRST, LAST, NAME)                                     \
    1063                 :             :   do {                                                                  \
    1064                 :             :     const int first = (FIRST);                                          \
    1065                 :             :     const int last = (LAST);                                            \
    1066                 :             :     const char *name = (NAME);                                          \
    1067                 :             :     const int row = first / 32;                                         \
    1068                 :             :     gcc_assert (last / 32 == row);                                      \
    1069                 :             :     table::rect_t rect (table::coord_t ((first % 32) + 2, row + 2),     \
    1070                 :             :                         table::size_t (last + 1 - first , 1));          \
    1071                 :             :     table.set_cell_span (rect, styled_string (sm, name));               \
    1072                 :             :   } while (0)
    1073                 :             : 
    1074                 :           4 :   SET_BITS (0, 3, "Version");
    1075                 :           4 :   SET_BITS (4, 7, "IHL");
    1076                 :           4 :   SET_BITS (8, 13, "DSCP");
    1077                 :           4 :   SET_BITS (14, 15, "ECN");
    1078                 :           4 :   SET_BITS (16, 31, "Total Length");
    1079                 :             : 
    1080                 :           4 :   SET_BITS (32 +  0, 32 + 15, "Identification");
    1081                 :           4 :   SET_BITS (32 + 16, 32 + 18, "Flags");
    1082                 :           4 :   SET_BITS (32 + 19, 32 + 31, "Fragment Offset");
    1083                 :             : 
    1084                 :           4 :   SET_BITS (64 +  0, 64 +  7, "Time To Live");
    1085                 :           4 :   SET_BITS (64 +  8, 64 + 15, "Protocol");
    1086                 :           4 :   SET_BITS (64 + 16, 64 + 31, "Header Checksum");
    1087                 :             : 
    1088                 :           4 :   SET_BITS (96 +  0, 96 + 31, "Source IP Address");
    1089                 :           4 :   SET_BITS (128 +  0, 128 + 31, "Destination IP Address");
    1090                 :             : 
    1091                 :           4 :   table.set_cell_span(table::rect_t (table::coord_t (2, 7),
    1092                 :           4 :                                      table::size_t (32, 3)),
    1093                 :           8 :                       styled_string (sm, "Options"));
    1094                 :           4 :   {
    1095                 :           4 :     canvas canvas (table.to_canvas (ascii_theme(), sm));
    1096                 :           4 :     ASSERT_CANVAS_STREQ
    1097                 :             :       (canvas, false,
    1098                 :             :        ("+-------+-----+---------------+---------------------+-----------------------+-----------------------+\n"
    1099                 :             :         "|Offsets|Octet|       0       |          1          |           2           |           3           |\n"
    1100                 :             :         "+-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\n"
    1101                 :             :         "| Octet | Bit |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|\n"
    1102                 :             :         "+-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\n"
    1103                 :             :         "|   0   |  0  |Version|  IHL  |     DSCP      | ECN |                 Total Length                  |\n"
    1104                 :             :         "+-------+-----+-------+-------+---------------+-----+--------+--------------------------------------+\n"
    1105                 :             :         "|   4   | 32  |           Identification            | Flags  |           Fragment Offset            |\n"
    1106                 :             :         "+-------+-----+---------------+---------------------+--------+--------------------------------------+\n"
    1107                 :             :         "|   8   | 64  | Time To Live  |      Protocol       |                Header Checksum                |\n"
    1108                 :             :         "+-------+-----+---------------+---------------------+-----------------------------------------------+\n"
    1109                 :             :         "|  12   | 96  |                                  Source IP Address                                  |\n"
    1110                 :             :         "+-------+-----+-------------------------------------------------------------------------------------+\n"
    1111                 :             :         "|  16   | 128 |                               Destination IP Address                                |\n"
    1112                 :             :         "+-------+-----+-------------------------------------------------------------------------------------+\n"
    1113                 :             :         "|  20   | 160 |                                                                                     |\n"
    1114                 :             :         "+-------+-----+                                                                                     |\n"
    1115                 :             :         "|  ...  | ... |                                       Options                                       |\n"
    1116                 :             :         "+-------+-----+                                                                                     |\n"
    1117                 :             :         "|  56   | 448 |                                                                                     |\n"
    1118                 :             :         "+-------+-----+-------------------------------------------------------------------------------------+\n"));
    1119                 :           4 :   }
    1120                 :           4 :   {
    1121                 :           4 :     canvas canvas (table.to_canvas (unicode_theme(), sm));
    1122                 :           4 :     ASSERT_CANVAS_STREQ
    1123                 :             :       (canvas, false,
    1124                 :             :        // FIXME: are we allowed unicode chars in string literals in our source?
    1125                 :             :        ("┌───────┬─────┬───────────────┬─────────────────────┬───────────────────────┬───────────────────────┐\n"
    1126                 :             :         "│Offsets│Octet│       0       │          1          │           2           │           3           │\n"
    1127                 :             :         "├───────┼─────┼─┬─┬─┬─┬─┬─┬─┬─┼─┬─┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┤\n"
    1128                 :             :         "│ Octet │ Bit │0│1│2│3│4│5│6│7│8│9│10│11│12│13│14│15│16│17│18│19│20│21│22│23│24│25│26│27│28│29│30│31│\n"
    1129                 :             :         "├───────┼─────┼─┴─┴─┴─┼─┴─┴─┴─┼─┴─┴──┴──┴──┴──┼──┴──┼──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┤\n"
    1130                 :             :         "│   0   │  0  │Version│  IHL  │     DSCP      │ ECN │                 Total Length                  │\n"
    1131                 :             :         "├───────┼─────┼───────┴───────┴───────────────┴─────┼────────┬──────────────────────────────────────┤\n"
    1132                 :             :         "│   4   │ 32  │           Identification            │ Flags  │           Fragment Offset            │\n"
    1133                 :             :         "├───────┼─────┼───────────────┬─────────────────────┼────────┴──────────────────────────────────────┤\n"
    1134                 :             :         "│   8   │ 64  │ Time To Live  │      Protocol       │                Header Checksum                │\n"
    1135                 :             :         "├───────┼─────┼───────────────┴─────────────────────┴───────────────────────────────────────────────┤\n"
    1136                 :             :         "│  12   │ 96  │                                  Source IP Address                                  │\n"
    1137                 :             :         "├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤\n"
    1138                 :             :         "│  16   │ 128 │                               Destination IP Address                                │\n"
    1139                 :             :         "├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤\n"
    1140                 :             :         "│  20   │ 160 │                                                                                     │\n"
    1141                 :             :         "├───────┼─────┤                                                                                     │\n"
    1142                 :             :         "│  ...  │ ... │                                       Options                                       │\n"
    1143                 :             :         "├───────┼─────┤                                                                                     │\n"
    1144                 :             :         "│  56   │ 448 │                                                                                     │\n"
    1145                 :             :         "└───────┴─────┴─────────────────────────────────────────────────────────────────────────────────────┘\n"));
    1146                 :           4 :   }
    1147                 :           4 : }
    1148                 :             : 
    1149                 :             : static void
    1150                 :           4 : test_missing_cells ()
    1151                 :             : {
    1152                 :           4 :   style_manager sm;
    1153                 :           4 :   table table (table::size_t (3, 3));
    1154                 :           4 :   table.set_cell (table::coord_t (1, 0), styled_string (sm, "A"));
    1155                 :           4 :   table.set_cell (table::coord_t (0, 1), styled_string (sm, "B"));
    1156                 :           4 :   table.set_cell (table::coord_t (1, 1), styled_string (sm, "C"));
    1157                 :           4 :   table.set_cell (table::coord_t (2, 1), styled_string (sm, "D"));
    1158                 :           4 :   table.set_cell (table::coord_t (1, 2), styled_string (sm, "E"));
    1159                 :             : 
    1160                 :           4 :   ASSERT_TABLE_NULL_CELL (table, 0, 0);
    1161                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 1, 0, "A");
    1162                 :           4 :   ASSERT_TABLE_NULL_CELL (table, 2, 0);
    1163                 :             : 
    1164                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 0, 1, "B");
    1165                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 1, 1, "C");
    1166                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 2, 1, "D");
    1167                 :             : 
    1168                 :           4 :   ASSERT_TABLE_NULL_CELL (table, 0, 2);
    1169                 :           4 :   ASSERT_TABLE_CELL_STREQ (table, 1, 2, "E");
    1170                 :           4 :   ASSERT_TABLE_NULL_CELL (table, 2, 2);
    1171                 :             : 
    1172                 :           4 :   {
    1173                 :           4 :     canvas canvas (table.to_canvas (ascii_theme(), sm));
    1174                 :           4 :     ASSERT_CANVAS_STREQ
    1175                 :             :       (canvas, false,
    1176                 :             :        ("  +-+\n"
    1177                 :             :         "  |A|\n"
    1178                 :             :         "+-+-+-+\n"
    1179                 :             :         "|B|C|D|\n"
    1180                 :             :         "+-+-+-+\n"
    1181                 :             :         "  |E|\n"
    1182                 :             :         "  +-+\n"));
    1183                 :           4 :   }
    1184                 :           4 :   {
    1185                 :           4 :     canvas canvas (table.to_canvas (unicode_theme(), sm));
    1186                 :           4 :     ASSERT_CANVAS_STREQ
    1187                 :             :       (canvas, false,
    1188                 :             :        ("  ┌─┐\n"
    1189                 :             :         "  │A│\n"
    1190                 :             :         "┌─┼─┼─┐\n"
    1191                 :             :         "│B│C│D│\n"
    1192                 :             :         "└─┼─┼─┘\n"
    1193                 :             :         "  │E│\n"
    1194                 :             :         "  └─┘\n"));
    1195                 :           4 :   }
    1196                 :           4 : }
    1197                 :             : 
    1198                 :             : static void
    1199                 :           4 : test_add_row ()
    1200                 :             : {
    1201                 :           4 :   style_manager sm;
    1202                 :           4 :   table table (table::size_t (3, 0));
    1203                 :          24 :   for (int i = 0; i < 5; i++)
    1204                 :             :     {
    1205                 :          20 :       const int y = table.add_row ();
    1206                 :          80 :       for (int x = 0; x < 3; x++)
    1207                 :          60 :         table.set_cell (table::coord_t (x, y),
    1208                 :         120 :                         styled_string::from_fmt (sm, nullptr,
    1209                 :             :                                                  "%i, %i", x, y));
    1210                 :             :     }
    1211                 :           4 :   canvas canvas (table.to_canvas (ascii_theme(), sm));
    1212                 :           4 :   ASSERT_CANVAS_STREQ
    1213                 :             :     (canvas, false,
    1214                 :             :      ("+----+----+----+\n"
    1215                 :             :       "|0, 0|1, 0|2, 0|\n"
    1216                 :             :       "+----+----+----+\n"
    1217                 :             :       "|0, 1|1, 1|2, 1|\n"
    1218                 :             :       "+----+----+----+\n"
    1219                 :             :       "|0, 2|1, 2|2, 2|\n"
    1220                 :             :       "+----+----+----+\n"
    1221                 :             :       "|0, 3|1, 3|2, 3|\n"
    1222                 :             :       "+----+----+----+\n"
    1223                 :             :       "|0, 4|1, 4|2, 4|\n"
    1224                 :             :       "+----+----+----+\n"));
    1225                 :           8 : }
    1226                 :             : 
    1227                 :             : static void
    1228                 :           4 : test_alignment ()
    1229                 :             : {
    1230                 :           4 :   style_manager sm;
    1231                 :           4 :   table table (table::size_t (9, 9));
    1232                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (0, 0),
    1233                 :           4 :                                       table::size_t (3, 3)),
    1234                 :           8 :                        styled_string (sm, "left top"),
    1235                 :             :                       x_align::LEFT, y_align::TOP);
    1236                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (3, 0),
    1237                 :           4 :                                       table::size_t (3, 3)),
    1238                 :           8 :                        styled_string (sm, "center top"),
    1239                 :             :                        x_align::CENTER, y_align::TOP);
    1240                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (6, 0),
    1241                 :           4 :                                       table::size_t (3, 3)),
    1242                 :           8 :                        styled_string (sm, "right top"),
    1243                 :             :                        x_align::RIGHT, y_align::TOP);
    1244                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (0, 3),
    1245                 :           4 :                                       table::size_t (3, 3)),
    1246                 :           8 :                        styled_string (sm, "left center"),
    1247                 :             :                        x_align::LEFT, y_align::CENTER);
    1248                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (3, 3),
    1249                 :           4 :                                       table::size_t (3, 3)),
    1250                 :           8 :                        styled_string (sm, "center center"),
    1251                 :             :                        x_align::CENTER, y_align::CENTER);
    1252                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (6, 3),
    1253                 :           4 :                                       table::size_t (3, 3)),
    1254                 :           8 :                        styled_string (sm, "right center"),
    1255                 :             :                        x_align::RIGHT, y_align::CENTER);
    1256                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (0, 6),
    1257                 :           4 :                                       table::size_t (3, 3)),
    1258                 :           8 :                        styled_string (sm, "left bottom"),
    1259                 :             :                        x_align::LEFT, y_align::BOTTOM);
    1260                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (3, 6),
    1261                 :           4 :                                       table::size_t (3, 3)),
    1262                 :           8 :                        styled_string (sm, "center bottom"),
    1263                 :             :                        x_align::CENTER, y_align::BOTTOM);
    1264                 :           4 :   table.set_cell_span (table::rect_t (table::coord_t (6, 6),
    1265                 :           4 :                                       table::size_t (3, 3)),
    1266                 :           8 :                        styled_string (sm, "right bottom"),
    1267                 :             :                        x_align::RIGHT, y_align::BOTTOM);
    1268                 :             : 
    1269                 :           4 :   canvas canvas (table.to_canvas (ascii_theme(), sm));
    1270                 :           4 :   ASSERT_CANVAS_STREQ
    1271                 :             :     (canvas, false,
    1272                 :             :      ("+-----------+-------------+------------+\n"
    1273                 :             :       "|left top   | center top  |   right top|\n"
    1274                 :             :       "|           |             |            |\n"
    1275                 :             :       "+-----------+-------------+------------+\n"
    1276                 :             :       "|left center|center center|right center|\n"
    1277                 :             :       "|           |             |            |\n"
    1278                 :             :       "+-----------+-------------+------------+\n"
    1279                 :             :       "|           |             |            |\n"
    1280                 :             :       "|left bottom|center bottom|right bottom|\n"
    1281                 :             :       "+-----------+-------------+------------+\n"));
    1282                 :           8 : }
    1283                 :             : 
    1284                 :             : /* Run all selftests in this file.  */
    1285                 :             : 
    1286                 :             : void
    1287                 :           4 : text_art_table_cc_tests ()
    1288                 :             : {
    1289                 :           4 :   test_tic_tac_toe ();
    1290                 :           4 :   test_text_table ();
    1291                 :           4 :   test_offset_table ();
    1292                 :           4 :   test_spans ();
    1293                 :           4 :   test_spans_2 ();
    1294                 :           4 :   test_spans_3 ();
    1295                 :           4 :   test_double_width_chars ();
    1296                 :           4 :   test_ipv4_header ();
    1297                 :           4 :   test_missing_cells ();
    1298                 :           4 :   test_add_row ();
    1299                 :           4 :   test_alignment ();
    1300                 :           4 : }
    1301                 :             : 
    1302                 :             : } // namespace selftest
    1303                 :             : 
    1304                 :             : 
    1305                 :             : #endif /* #if CHECKING_P */
        

Generated by: LCOV version 2.1-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.