Home | History | Annotate | Line # | Download | only in tui
tui-winsource.c revision 1.12
      1 /* TUI display source/assembly window.
      2 
      3    Copyright (C) 1998-2024 Free Software Foundation, Inc.
      4 
      5    Contributed by Hewlett-Packard Company.
      6 
      7    This file is part of GDB.
      8 
      9    This program is free software; you can redistribute it and/or modify
     10    it under the terms of the GNU General Public License as published by
     11    the Free Software Foundation; either version 3 of the License, or
     12    (at your option) any later version.
     13 
     14    This program is distributed in the hope that it will be useful,
     15    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17    GNU General Public License for more details.
     18 
     19    You should have received a copy of the GNU General Public License
     20    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     21 
     22 #include "observable.h"
     23 #include "symtab.h"
     24 #include "frame.h"
     25 #include "breakpoint.h"
     26 #include "value.h"
     27 #include "source.h"
     28 #include "objfiles.h"
     29 #include "gdbsupport/gdb-safe-ctype.h"
     30 
     31 #include "tui/tui.h"
     32 #include "tui/tui-data.h"
     33 #include "tui/tui-io.h"
     34 #include "tui/tui-status.h"
     35 #include "tui/tui-win.h"
     36 #include "tui/tui-winsource.h"
     37 #include "tui/tui-source.h"
     38 #include "tui/tui-disasm.h"
     39 #include "tui/tui-location.h"
     40 #include "gdb_curses.h"
     41 
     42 /* ncurses returns -1, but BSD segfaults; the code assumes ncurses */
     43 #define tui_getmaxx(w)	((w) ? getmaxx (w) : -1)
     44 #define tui_getmaxy(w)	((w) ? getmaxy (w) : -1)
     45 
     46 /* Function to display the "main" routine.  */
     47 void
     48 tui_display_main ()
     49 {
     50   auto adapter = tui_source_windows ();
     51   if (adapter.begin () != adapter.end ())
     52     {
     53       struct gdbarch *gdbarch;
     54       CORE_ADDR addr;
     55 
     56       tui_get_begin_asm_address (&gdbarch, &addr);
     57       if (addr != (CORE_ADDR) 0)
     58 	{
     59 	  struct symtab *s;
     60 
     61 	  tui_update_source_windows_with_addr (gdbarch, addr);
     62 	  s = find_pc_line_symtab (addr);
     63 	  tui_location.set_location (s);
     64 	}
     65     }
     66 }
     67 
     68 /* See tui-winsource.h.  */
     69 
     70 std::string
     71 tui_copy_source_line (const char **ptr, int *length)
     72 {
     73   const char *lineptr = *ptr;
     74 
     75   /* Init the line with the line number.  */
     76   std::string result;
     77 
     78   int column = 0;
     79   char c;
     80   do
     81     {
     82       int skip_bytes;
     83 
     84       c = *lineptr;
     85       if (c == '\033' && skip_ansi_escape (lineptr, &skip_bytes))
     86 	{
     87 	  /* We always have to preserve escapes.  */
     88 	  result.append (lineptr, lineptr + skip_bytes);
     89 	  lineptr += skip_bytes;
     90 	  continue;
     91 	}
     92       if (c == '\0')
     93 	break;
     94 
     95       ++lineptr;
     96       ++column;
     97 
     98       auto process_tab = [&] ()
     99 	{
    100 	  int max_tab_len = tui_tab_width;
    101 
    102 	  --column;
    103 	  for (int j = column % max_tab_len;
    104 	       j < max_tab_len;
    105 	       column++, j++)
    106 	    result.push_back (' ');
    107 	};
    108 
    109       if (c == '\n' || c == '\r' || c == '\0')
    110 	{
    111 	  /* Nothing.  */
    112 	}
    113       else if (c == '\t')
    114 	process_tab ();
    115       else if (ISCNTRL (c))
    116 	{
    117 	  result.push_back ('^');
    118 	  result.push_back (c + 0100);
    119 	  ++column;
    120 	}
    121       else if (c == 0177)
    122 	{
    123 	  result.push_back ('^');
    124 	  result.push_back ('?');
    125 	  ++column;
    126 	}
    127       else
    128 	result.push_back (c);
    129     }
    130   while (c != '\0' && c != '\n' && c != '\r');
    131 
    132   if (c == '\r' && *lineptr == '\n')
    133     ++lineptr;
    134   *ptr = lineptr;
    135 
    136   if (length != nullptr)
    137     *length = column;
    138 
    139   return result;
    140 }
    141 
    142 void
    143 tui_source_window_base::style_changed ()
    144 {
    145   if (tui_active && is_visible ())
    146     refill ();
    147 }
    148 
    149 /* Function to display source in the source window.  This function
    150    initializes the horizontal scroll to 0.  */
    151 void
    152 tui_source_window_base::update_source_window
    153   (struct gdbarch *gdbarch,
    154    const struct symtab_and_line &sal)
    155 {
    156   m_horizontal_offset = 0;
    157   update_source_window_as_is (gdbarch, sal);
    158 }
    159 
    160 
    161 /* Function to display source in the source/asm window.  This function
    162    shows the source as specified by the horizontal offset.  */
    163 void
    164 tui_source_window_base::update_source_window_as_is
    165   (struct gdbarch *gdbarch,
    166    const struct symtab_and_line &sal)
    167 {
    168   bool ret = set_contents (gdbarch, sal);
    169 
    170   if (!ret)
    171     erase_source_content ();
    172   else
    173     {
    174       validate_scroll_offsets ();
    175       update_breakpoint_info (nullptr, false);
    176       update_exec_info (false);
    177       show_source_content ();
    178     }
    179 }
    180 
    181 
    182 /* See tui-winsource.h.  */
    183 void
    184 tui_source_window_base::update_source_window_with_addr (struct gdbarch *gdbarch,
    185 							CORE_ADDR addr)
    186 {
    187   struct symtab_and_line sal {};
    188   if (addr != 0)
    189     sal = find_pc_line (addr, 0);
    190 
    191   update_source_window (gdbarch, sal);
    192 }
    193 
    194 /* Function to ensure that the source and/or disassembly windows
    195    reflect the input address.  */
    196 void
    197 tui_update_source_windows_with_addr (struct gdbarch *gdbarch, CORE_ADDR addr)
    198 {
    199   struct symtab_and_line sal {};
    200   if (addr != 0)
    201     sal = find_pc_line (addr, 0);
    202 
    203   for (struct tui_source_window_base *win_info : tui_source_windows ())
    204     win_info->update_source_window (gdbarch, sal);
    205 }
    206 
    207 /* Function to ensure that the source and/or disassembly windows
    208    reflect the symtab and line.  */
    209 void
    210 tui_update_source_windows_with_line (struct symtab_and_line sal)
    211 {
    212   struct gdbarch *gdbarch = nullptr;
    213   if (sal.symtab != nullptr)
    214     {
    215       find_line_pc (sal.symtab, sal.line, &sal.pc);
    216       gdbarch = sal.symtab->compunit ()->objfile ()->arch ();
    217     }
    218 
    219   for (struct tui_source_window_base *win_info : tui_source_windows ())
    220     win_info->update_source_window (gdbarch, sal);
    221 }
    222 
    223 void
    224 tui_source_window_base::do_erase_source_content (const char *str)
    225 {
    226   m_content.clear ();
    227   if (handle != nullptr)
    228     center_string (str);
    229 }
    230 
    231 /* See tui-winsource.h.  */
    232 
    233 void
    234 tui_source_window_base::puts_to_pad_with_skip (const char *string, int skip)
    235 {
    236   gdb_assert (m_pad.get () != nullptr);
    237   WINDOW *w = m_pad.get ();
    238 
    239   while (skip > 0)
    240     {
    241       const char *next = strpbrk (string, "\033");
    242 
    243       /* Print the plain text prefix.  */
    244       size_t n_chars = next == nullptr ? strlen (string) : next - string;
    245       if (n_chars > 0)
    246 	{
    247 	  if (skip > 0)
    248 	    {
    249 	      if (skip < n_chars)
    250 		{
    251 		  string += skip;
    252 		  n_chars -= skip;
    253 		  skip = 0;
    254 		}
    255 	      else
    256 		{
    257 		  skip -= n_chars;
    258 		  string += n_chars;
    259 		  n_chars = 0;
    260 		}
    261 	    }
    262 
    263 	  if (n_chars > 0)
    264 	    {
    265 	      std::string copy (string, n_chars);
    266 	      tui_puts (copy.c_str (), w);
    267 	    }
    268 	}
    269 
    270       /* We finished.  */
    271       if (next == nullptr)
    272 	break;
    273 
    274       gdb_assert (*next == '\033');
    275 
    276       int n_read;
    277       if (skip_ansi_escape (next, &n_read))
    278 	{
    279 	  std::string copy (next, n_read);
    280 	  tui_puts (copy.c_str (), w);
    281 	  next += n_read;
    282 	}
    283       else
    284 	gdb_assert_not_reached ("unhandled escape");
    285 
    286       string = next;
    287     }
    288 
    289   if (*string != '\0')
    290     tui_puts (string, w);
    291 }
    292 
    293 /* Redraw the complete line of a source or disassembly window.  */
    294 void
    295 tui_source_window_base::show_source_line (int lineno)
    296 {
    297   struct tui_source_element *line;
    298 
    299   line = &m_content[lineno];
    300   if (line->is_exec_point)
    301     tui_set_reverse_mode (m_pad.get (), true);
    302 
    303   wmove (m_pad.get (), lineno, 0);
    304   puts_to_pad_with_skip (line->line.c_str (), m_pad_offset);
    305 
    306   if (line->is_exec_point)
    307     tui_set_reverse_mode (m_pad.get (), false);
    308 }
    309 
    310 /* See tui-winsource.h.  */
    311 
    312 void
    313 tui_source_window_base::refresh_window ()
    314 {
    315   TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
    316 
    317   /* tui_win_info::refresh_window would draw the empty background window to
    318      the screen, potentially creating a flicker.  */
    319   wnoutrefresh (handle.get ());
    320 
    321   if (m_content.empty ())
    322     return;
    323 
    324   int pad_width = tui_getmaxx (m_pad.get ());
    325   int left_margin = this->left_margin ();
    326   int view_width = this->view_width ();
    327   int content_width = m_max_length;
    328   int pad_x = m_horizontal_offset - m_pad_offset;
    329 
    330   tui_debug_printf ("pad_width = %d, left_margin = %d, view_width = %d",
    331 		    pad_width, left_margin, view_width);
    332   tui_debug_printf ("content_width = %d, pad_x = %d, m_horizontal_offset = %d",
    333 		    content_width, pad_x, m_horizontal_offset);
    334   tui_debug_printf ("m_pad_offset = %d", m_pad_offset);
    335 
    336   gdb_assert (m_pad_offset >= 0);
    337   gdb_assert (m_horizontal_offset + view_width
    338 	      <= std::max (content_width, view_width));
    339   gdb_assert (pad_x >= 0);
    340   gdb_assert (m_horizontal_offset >= 0);
    341 
    342   /* This function can be called before the pad has been allocated, this
    343      should only occur during the initial startup.  In this case the first
    344      condition in the following asserts will not be true, but the nullptr
    345      check will.  */
    346   gdb_assert (pad_width > 0 || m_pad.get () == nullptr);
    347   gdb_assert (pad_x + view_width <= pad_width || m_pad.get () == nullptr);
    348 
    349   int sminrow = y + box_width ();
    350   int smincol = x + box_width () + left_margin;
    351   int smaxrow = sminrow + m_content.size () - 1;
    352   int smaxcol = smincol + view_width - 1;
    353   if (m_pad.get ())
    354     pnoutrefresh (m_pad.get (), 0, pad_x, sminrow, smincol, smaxrow, smaxcol);
    355 }
    356 
    357 void
    358 tui_source_window_base::show_source_content ()
    359 {
    360   TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
    361 
    362   gdb_assert (!m_content.empty ());
    363 
    364   /* The pad should be at least as wide as the window, but ideally, as wide
    365      as the content, however, for some very wide content this might not be
    366      possible.  */
    367   int required_pad_width = std::max (m_max_length, width);
    368   int required_pad_height = m_content.size ();
    369 
    370   /* If the required pad width is wider than the previously requested pad
    371      width, then we might want to grow the pad.  */
    372   if (required_pad_width > m_pad_requested_width
    373       || required_pad_height > tui_getmaxy (m_pad.get ()))
    374     {
    375       /* The current pad width.  */
    376       int pad_width = m_pad == nullptr ? 0 : tui_getmaxx (m_pad.get ());
    377 
    378       gdb_assert (pad_width <= m_pad_requested_width);
    379 
    380       /* If the current pad width is smaller than the previously requested
    381 	 pad width, then this means we previously failed to allocate a
    382 	 bigger pad.  There's no point asking again, so we'll just make so
    383 	 with the pad we currently have.  */
    384       if (pad_width == m_pad_requested_width
    385 	  || required_pad_height > tui_getmaxy (m_pad.get ()))
    386 	{
    387 	  pad_width = required_pad_width;
    388 
    389 	  do
    390 	    {
    391 	      /* Try to allocate a new pad.  */
    392 	      m_pad.reset (newpad (required_pad_height, pad_width));
    393 
    394 	      if (m_pad == nullptr)
    395 		{
    396 		  int reduced_width = std::max (pad_width / 2, width);
    397 		  if (reduced_width == pad_width)
    398 		    error (_("failed to setup source window"));
    399 		  pad_width = reduced_width;
    400 		}
    401 	    }
    402 	  while (m_pad == nullptr);
    403 	}
    404 
    405       m_pad_requested_width = required_pad_width;
    406       tui_debug_printf ("requested width %d, allocated width %d",
    407 			required_pad_width, tui_getmaxx (m_pad.get ()));
    408     }
    409 
    410   gdb_assert (m_pad != nullptr);
    411   werase (m_pad.get ());
    412   for (int lineno = 0; lineno < m_content.size (); lineno++)
    413     show_source_line (lineno);
    414 
    415   /* Calling check_and_display_highlight_if_needed will call
    416      refresh_window.  */
    417   check_and_display_highlight_if_needed ();
    418 }
    419 
    420 tui_source_window_base::tui_source_window_base ()
    421 {
    422   m_start_line_or_addr.loa = LOA_ADDRESS;
    423   m_start_line_or_addr.u.addr = 0;
    424 
    425   gdb::observers::styling_changed.attach
    426     (std::bind (&tui_source_window::style_changed, this),
    427      m_observable, "tui-winsource");
    428 }
    429 
    430 tui_source_window_base::~tui_source_window_base ()
    431 {
    432   gdb::observers::styling_changed.detach (m_observable);
    433 }
    434 
    435 /* See tui-data.h.  */
    436 
    437 void
    438 tui_source_window_base::update_tab_width ()
    439 {
    440   werase (handle.get ());
    441   rerender ();
    442 }
    443 
    444 void
    445 tui_source_window_base::rerender ()
    446 {
    447   TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
    448 
    449   if (!m_content.empty ())
    450     {
    451       symtab_and_line cursal
    452 	= get_current_source_symtab_and_line (current_program_space);
    453 
    454       if (m_start_line_or_addr.loa == LOA_LINE)
    455 	cursal.line = m_start_line_or_addr.u.line_no;
    456       else
    457 	cursal.pc = m_start_line_or_addr.u.addr;
    458       update_source_window (m_gdbarch, cursal);
    459     }
    460   else if (deprecated_safe_get_selected_frame () != NULL)
    461     {
    462       symtab_and_line cursal
    463 	= get_current_source_symtab_and_line (current_program_space);
    464       frame_info_ptr frame = deprecated_safe_get_selected_frame ();
    465       struct gdbarch *gdbarch = get_frame_arch (frame);
    466 
    467       struct symtab *s = find_pc_line_symtab (get_frame_pc (frame));
    468       if (this != tui_src_win ())
    469 	find_line_pc (s, cursal.line, &cursal.pc);
    470 
    471       /* This centering code is copied from tui_source_window::maybe_update.
    472 	 It would be nice to do centering more often, and do it in just one
    473 	 location.  But since this is a regression fix, handle this
    474 	 conservatively for now.  */
    475       int start_line = (cursal.line - ((height - box_size ()) / 2)) + 1;
    476       if (start_line <= 0)
    477 	start_line = 1;
    478       cursal.line = start_line;
    479 
    480       update_source_window (gdbarch, cursal);
    481     }
    482   else
    483     {
    484       CORE_ADDR addr;
    485       struct gdbarch *gdbarch;
    486       tui_get_begin_asm_address (&gdbarch, &addr);
    487       if (addr == 0)
    488 	erase_source_content ();
    489       else
    490 	update_source_window_with_addr (gdbarch, addr);
    491     }
    492 }
    493 
    494 /* See tui-data.h.  */
    495 
    496 void
    497 tui_source_window_base::refill ()
    498 {
    499   symtab_and_line sal {};
    500 
    501   if (this == tui_src_win ())
    502     {
    503       sal = get_current_source_symtab_and_line (current_program_space);
    504       if (sal.symtab == NULL)
    505 	{
    506 	  frame_info_ptr fi = deprecated_safe_get_selected_frame ();
    507 	  if (fi != nullptr)
    508 	    sal = find_pc_line (get_frame_pc (fi), 0);
    509 	}
    510     }
    511 
    512   if (sal.pspace == nullptr)
    513     sal.pspace = current_program_space;
    514 
    515   if (m_start_line_or_addr.loa == LOA_LINE)
    516     sal.line = m_start_line_or_addr.u.line_no;
    517   else
    518     sal.pc = m_start_line_or_addr.u.addr;
    519 
    520   update_source_window_as_is (m_gdbarch, sal);
    521 }
    522 
    523 /* See tui-winsource.h.  */
    524 
    525 bool
    526 tui_source_window_base::validate_scroll_offsets ()
    527 {
    528   TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
    529 
    530   int original_pad_offset = m_pad_offset;
    531 
    532   if (m_horizontal_offset < 0)
    533     m_horizontal_offset = 0;
    534 
    535   int content_width = m_max_length;
    536   int pad_width = tui_getmaxx (m_pad.get ());
    537   int view_width = this->view_width ();
    538 
    539   tui_debug_printf ("pad_width = %d, view_width = %d, content_width = %d",
    540 		    pad_width, view_width, content_width);
    541   tui_debug_printf ("original_pad_offset = %d, m_horizontal_offset = %d",
    542 		    original_pad_offset, m_horizontal_offset);
    543 
    544   if (m_horizontal_offset + view_width > content_width)
    545     m_horizontal_offset = std::max (content_width - view_width, 0);
    546 
    547   if ((m_horizontal_offset + view_width) > (m_pad_offset + pad_width))
    548     {
    549       m_pad_offset = std::min (m_horizontal_offset, content_width - pad_width);
    550       m_pad_offset = std::max (m_pad_offset, 0);
    551     }
    552   else if (m_horizontal_offset < m_pad_offset)
    553     m_pad_offset = std::max (m_horizontal_offset + view_width - pad_width, 0);
    554 
    555   gdb_assert (m_pad_offset >= 0);
    556   return (original_pad_offset != m_pad_offset);
    557 }
    558 
    559 /* Scroll the source forward or backward horizontally.  */
    560 
    561 void
    562 tui_source_window_base::do_scroll_horizontal (int num_to_scroll)
    563 {
    564   if (!m_content.empty ())
    565     {
    566       m_horizontal_offset += num_to_scroll;
    567 
    568       if (validate_scroll_offsets ())
    569 	show_source_content ();
    570 
    571       refresh_window ();
    572     }
    573 }
    574 
    575 
    576 /* Set or clear the is_exec_point flag in the line whose line is
    577    line_no.  */
    578 
    579 void
    580 tui_source_window_base::set_is_exec_point_at (struct tui_line_or_address l)
    581 {
    582   bool changed = false;
    583   int i;
    584 
    585   i = 0;
    586   while (i < m_content.size ())
    587     {
    588       bool new_state;
    589       struct tui_line_or_address content_loa =
    590 	m_content[i].line_or_addr;
    591 
    592       if (content_loa.loa == l.loa
    593 	  && ((l.loa == LOA_LINE && content_loa.u.line_no == l.u.line_no)
    594 	      || (l.loa == LOA_ADDRESS && content_loa.u.addr == l.u.addr)))
    595 	new_state = true;
    596       else
    597 	new_state = false;
    598       if (new_state != m_content[i].is_exec_point)
    599 	{
    600 	  changed = true;
    601 	  m_content[i].is_exec_point = new_state;
    602 	}
    603       i++;
    604     }
    605   if (changed)
    606     refill ();
    607 }
    608 
    609 /* See tui-winsource.h.  */
    610 
    611 void
    612 tui_update_all_breakpoint_info (struct breakpoint *being_deleted)
    613 {
    614   for (tui_source_window_base *win : tui_source_windows ())
    615     {
    616       if (win->update_breakpoint_info (being_deleted, false))
    617 	win->update_exec_info ();
    618     }
    619 }
    620 
    621 
    622 /* Scan the source window and the breakpoints to update the break_mode
    623    information for each line.
    624 
    625    Returns true if something changed and the execution window must be
    626    refreshed.  */
    627 
    628 bool
    629 tui_source_window_base::update_breakpoint_info
    630   (struct breakpoint *being_deleted, bool current_only)
    631 {
    632   int i;
    633   bool need_refresh = false;
    634 
    635   for (i = 0; i < m_content.size (); i++)
    636     {
    637       struct tui_source_element *line;
    638 
    639       line = &m_content[i];
    640       if (current_only && !line->is_exec_point)
    641 	 continue;
    642 
    643       /* Scan each breakpoint to see if the current line has something to
    644 	 do with it.  Identify enable/disabled breakpoints as well as
    645 	 those that we already hit.  */
    646       tui_bp_flags mode = 0;
    647       for (breakpoint &bp : all_breakpoints ())
    648 	{
    649 	  if (&bp == being_deleted)
    650 	    continue;
    651 
    652 	  for (bp_location &loc : bp.locations ())
    653 	    {
    654 	      if (location_matches_p (&loc, i))
    655 		{
    656 		  if (bp.enable_state == bp_disabled)
    657 		    mode |= TUI_BP_DISABLED;
    658 		  else
    659 		    mode |= TUI_BP_ENABLED;
    660 		  if (bp.hit_count)
    661 		    mode |= TUI_BP_HIT;
    662 		  if (bp.first_loc ().cond)
    663 		    mode |= TUI_BP_CONDITIONAL;
    664 		  if (bp.type == bp_hardware_breakpoint)
    665 		    mode |= TUI_BP_HARDWARE;
    666 		}
    667 	    }
    668 	}
    669 
    670       if (line->break_mode != mode)
    671 	{
    672 	  line->break_mode = mode;
    673 	  need_refresh = true;
    674 	}
    675     }
    676   return need_refresh;
    677 }
    678 
    679 /* See tui-winsource.h.  */
    680 
    681 void
    682 tui_source_window_base::update_exec_info (bool refresh_p)
    683 {
    684   update_breakpoint_info (nullptr, true);
    685   for (int i = 0; i < m_content.size (); i++)
    686     {
    687       struct tui_source_element *src_element = &m_content[i];
    688       /* Add 1 for '\0'.  */
    689       char element[TUI_EXECINFO_SIZE + 1];
    690       /* Initialize all but last element.  */
    691       char space = tui_left_margin_verbose ? '_' : ' ';
    692       memset (element, space, TUI_EXECINFO_SIZE);
    693       /* Initialize last element.  */
    694       element[TUI_EXECINFO_SIZE] = '\0';
    695 
    696       /* Now update the exec info content based upon the state
    697 	 of each line as indicated by the source content.  */
    698       tui_bp_flags mode = src_element->break_mode;
    699       if (mode & TUI_BP_HIT)
    700 	element[TUI_BP_HIT_POS] = (mode & TUI_BP_HARDWARE) ? 'H' : 'B';
    701       else if (mode & (TUI_BP_ENABLED | TUI_BP_DISABLED))
    702 	element[TUI_BP_HIT_POS] = (mode & TUI_BP_HARDWARE) ? 'h' : 'b';
    703 
    704       if (mode & TUI_BP_ENABLED)
    705 	element[TUI_BP_BREAK_POS] = '+';
    706       else if (mode & TUI_BP_DISABLED)
    707 	element[TUI_BP_BREAK_POS] = '-';
    708 
    709       if (src_element->is_exec_point)
    710 	element[TUI_EXEC_POS] = '>';
    711 
    712       mvwaddstr (handle.get (), i + box_width (), box_width (), element);
    713 
    714       show_line_number (i);
    715     }
    716   if (refresh_p)
    717     refresh_window ();
    718 }
    719