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