root/src/editor/editdraw.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. printwstr
  2. status_string
  3. edit_status_fullscreen
  4. edit_status_window
  5. edit_draw_frame
  6. edit_draw_window_icons
  7. print_to_widget
  8. edit_draw_this_line
  9. edit_draw_this_char
  10. render_edit_text
  11. edit_render
  12. edit_status
  13. edit_scroll_screen_over_cursor
  14. edit_render_keypress

   1 /*
   2    Editor text drawing.
   3 
   4    Copyright (C) 1996-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Paul Sheer, 1996, 1997
   9    Andrew Borodin <aborodin@vmail.ru> 2012-2022
  10    Slava Zanko <slavazanko@gmail.com>, 2013
  11 
  12    This file is part of the Midnight Commander.
  13 
  14    The Midnight Commander is free software: you can redistribute it
  15    and/or modify it under the terms of the GNU General Public License as
  16    published by the Free Software Foundation, either version 3 of the License,
  17    or (at your option) any later version.
  18 
  19    The Midnight Commander is distributed in the hope that it will be useful,
  20    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22    GNU General Public License for more details.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  26  */
  27 
  28 /** \file
  29  *  \brief Source: editor text drawing
  30  *  \author Paul Sheer
  31  *  \date 1996, 1997
  32  */
  33 
  34 #include <config.h>
  35 #include <stdlib.h>
  36 #include <stdio.h>
  37 #include <stdarg.h>
  38 #include <sys/types.h>
  39 #include <unistd.h>
  40 #include <string.h>
  41 #include <ctype.h>
  42 #include <sys/stat.h>
  43 
  44 #include "lib/global.h"
  45 #include "lib/tty/tty.h"  // tty_printf()
  46 #include "lib/tty/key.h"  // is_idle()
  47 #include "lib/skin.h"
  48 #include "lib/strutil.h"  // utf string functions
  49 #include "lib/util.h"     // is_printable()
  50 #include "lib/widget.h"
  51 #ifdef HAVE_CHARSET
  52 #    include "lib/charsets.h"
  53 #endif
  54 
  55 #include "edit-impl.h"
  56 #include "editwidget.h"
  57 
  58 /*** global variables ****************************************************************************/
  59 
  60 /*** file scope macro definitions ****************************************************************/
  61 
  62 #define MAX_LINE_LEN 1024
  63 
  64 /* Text styles */
  65 #define MOD_ABNORMAL                  (1 << 8)
  66 #define MOD_BOLD                      (1 << 9)
  67 #define MOD_MARKED                    (1 << 10)
  68 #define MOD_CURSOR                    (1 << 11)
  69 #define MOD_WHITESPACE                (1 << 12)
  70 
  71 #define edit_move(x, y)               widget_gotoyx (edit, y, x);
  72 
  73 #define key_pending(x)                (!is_idle ())
  74 
  75 #define EDITOR_MINIMUM_TERMINAL_WIDTH 30
  76 
  77 /*** file scope type declarations ****************************************************************/
  78 
  79 typedef struct
  80 {
  81     unsigned int ch;
  82     unsigned int style;
  83 } line_s;
  84 
  85 /*** forward declarations (file scope functions) *************************************************/
  86 
  87 /*** file scope variables ************************************************************************/
  88 
  89 /*** file scope functions ************************************************************************/
  90 
  91 static inline void
  92 printwstr (const char *s, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
  93 {
  94     if (len > 0)
  95         tty_printf ("%-*.*s", len, len, s);
  96 }
  97 
  98 /* --------------------------------------------------------------------------------------------- */
  99 
 100 static inline void
 101 status_string (WEdit *edit, char *s, int w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 102 {
 103     char byte_str[16];
 104 
 105     /*
 106      * If we are at the end of file, print <EOF>,
 107      * otherwise print the current character as is (if printable),
 108      * as decimal and as hex.
 109      */
 110     if (edit->buffer.curs1 >= edit->buffer.size)
 111         strcpy (byte_str, "<EOF>     ");
 112 #ifdef HAVE_CHARSET
 113     else if (edit->utf8)
 114     {
 115         unsigned int cur_utf;
 116         int char_length = 1;
 117 
 118         cur_utf = edit_buffer_get_utf (&edit->buffer, edit->buffer.curs1, &char_length);
 119         if (char_length > 0)
 120             g_snprintf (byte_str, sizeof (byte_str), "%04u 0x%03X", (unsigned) cur_utf,
 121                         (unsigned) cur_utf);
 122         else
 123         {
 124             cur_utf = edit_buffer_get_current_byte (&edit->buffer);
 125             g_snprintf (byte_str, sizeof (byte_str), "%04d 0x%03X", (int) cur_utf,
 126                         (unsigned) cur_utf);
 127         }
 128     }
 129 #endif
 130     else
 131     {
 132         unsigned char cur_byte;
 133 
 134         cur_byte = edit_buffer_get_current_byte (&edit->buffer);
 135         g_snprintf (byte_str, sizeof (byte_str), "%4d 0x%03X", (int) cur_byte, (unsigned) cur_byte);
 136     }
 137 
 138     // The field lengths just prevent the status line from shortening too much
 139     if (edit_options.simple_statusbar)
 140         g_snprintf (s, w, "%c%c%c%c %3ld %5ld/%ld %6ld/%ld %s %s",
 141                     edit->mark1 != edit->mark2 ? (edit->column_highlight ? 'C' : 'B') : '-',  //
 142                     edit->modified != 0 ? 'M' : '-',                                          //
 143                     macro_index < 0 ? '-' : 'R',                                              //
 144                     edit->overwrite == 0 ? '-' : 'O',                                         //
 145                     edit->curs_col + edit->over_col,                                          //
 146                     edit->buffer.curs_line + 1,                                               //
 147                     edit->buffer.lines + 1,                                                   //
 148                     (long) edit->buffer.curs1,                                                //
 149                     (long) edit->buffer.size,                                                 //
 150                     byte_str,
 151 #ifdef HAVE_CHARSET
 152                     mc_global.source_codepage >= 0 ? get_codepage_id (mc_global.source_codepage) :
 153 #endif
 154                                                    "");
 155     else
 156         g_snprintf (s, w, "[%c%c%c%c] %2ld L:[%3ld+%2ld %3ld/%3ld] *(%-4ld/%4ldb) %s  %s",
 157                     edit->mark1 != edit->mark2 ? (edit->column_highlight ? 'C' : 'B') : '-',  //
 158                     edit->modified != 0 ? 'M' : '-',                                          //
 159                     macro_index < 0 ? '-' : 'R',                                              //
 160                     edit->overwrite == 0 ? '-' : 'O',                                         //
 161                     edit->curs_col + edit->over_col,                                          //
 162                     edit->start_line + 1,                                                     //
 163                     edit->curs_row,                                                           //
 164                     edit->buffer.curs_line + 1,                                               //
 165                     edit->buffer.lines + 1,                                                   //
 166                     (long) edit->buffer.curs1,                                                //
 167                     (long) edit->buffer.size,                                                 //
 168                     byte_str,
 169 #ifdef HAVE_CHARSET
 170                     mc_global.source_codepage >= 0 ? get_codepage_id (mc_global.source_codepage) :
 171 #endif
 172                                                    "");
 173 }
 174 
 175 /* --------------------------------------------------------------------------------------------- */
 176 /**
 177  * Draw the status line at the top of the screen for fullscreen editor window.
 178  *
 179  * @param edit  editor object
 180  * @param color color pair
 181  */
 182 
 183 static inline void
 184 edit_status_fullscreen (WEdit *edit, int color)
     /* [previous][next][first][last][top][bottom][index][help]  */
 185 {
 186     Widget *h = WIDGET (WIDGET (edit)->owner);
 187     const int w = h->rect.cols;
 188     const int gap = 3;        // between the filename and the status
 189     const int right_gap = 5;  // at the right end of the screen
 190     const int preferred_fname_len = 16;
 191     char *status;
 192     size_t status_size;
 193     int status_len;
 194     const char *fname = "";
 195     int fname_len;
 196 
 197     status_size = w + 1;
 198     status = g_malloc (status_size);
 199     status_string (edit, status, status_size);
 200     status_len = (int) str_term_width1 (status);
 201 
 202     if (edit->filename_vpath != NULL)
 203     {
 204         fname = vfs_path_get_last_path_str (edit->filename_vpath);
 205 
 206         if (!edit_options.state_full_filename)
 207             fname = x_basename (fname);
 208     }
 209 
 210     fname_len = str_term_width1 (fname);
 211     if (fname_len < preferred_fname_len)
 212         fname_len = preferred_fname_len;
 213 
 214     if (fname_len + gap + status_len + right_gap >= w)
 215     {
 216         if (preferred_fname_len + gap + status_len + right_gap >= w)
 217             fname_len = preferred_fname_len;
 218         else
 219             fname_len = w - (gap + status_len + right_gap);
 220         fname = str_trunc (fname, fname_len);
 221     }
 222 
 223     widget_gotoyx (h, 0, 0);
 224     tty_setcolor (color);
 225     printwstr (fname, fname_len + gap);
 226     printwstr (status, w - (fname_len + gap));
 227 
 228     if (edit_options.simple_statusbar && w > EDITOR_MINIMUM_TERMINAL_WIDTH)
 229     {
 230         int percent;
 231 
 232         percent = edit_buffer_calc_percent (&edit->buffer, edit->buffer.curs1);
 233         widget_gotoyx (h, 0, w - 6 - 6);
 234         tty_printf (" %3d%%", percent);
 235     }
 236 
 237     g_free (status);
 238 }
 239 
 240 /* --------------------------------------------------------------------------------------------- */
 241 /**
 242  * Draw status line for editor window if window is not in fullscreen mode.
 243  *
 244  * @param edit editor object
 245  */
 246 
 247 static inline void
 248 edit_status_window (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 249 {
 250     Widget *w = WIDGET (edit);
 251     int y, x;
 252     int cols = w->rect.cols;
 253 
 254     tty_setcolor (STATUSBAR_COLOR);
 255 
 256     if (cols > 5)
 257     {
 258         const char *fname = N_ ("NoName");
 259 
 260         if (edit->filename_vpath != NULL)
 261         {
 262             fname = vfs_path_get_last_path_str (edit->filename_vpath);
 263 
 264             if (!edit_options.state_full_filename)
 265                 fname = x_basename (fname);
 266         }
 267 #ifdef ENABLE_NLS
 268         else
 269             fname = _ (fname);
 270 #endif
 271 
 272         edit_move (2, 0);
 273         tty_printf ("[%s]", str_term_trim (fname, w->rect.cols - 8 - 6));
 274     }
 275 
 276     tty_getyx (&y, &x);
 277     x -= w->rect.x;
 278     x += 4;
 279     if (x + 6 <= cols - 2 - 6)
 280     {
 281         edit_move (x, 0);
 282         tty_printf ("[%c%c%c%c]",
 283                     edit->mark1 != edit->mark2 ? (edit->column_highlight ? 'C' : 'B') : '-',
 284                     edit->modified != 0 ? 'M' : '-', macro_index < 0 ? '-' : 'R',
 285                     edit->overwrite == 0 ? '-' : 'O');
 286     }
 287 
 288     if (cols > 30)
 289     {
 290         edit_move (2, w->rect.lines - 1);
 291         tty_printf ("%3ld %5ld/%ld %6ld/%ld", edit->curs_col + edit->over_col,
 292                     edit->buffer.curs_line + 1, edit->buffer.lines + 1, (long) edit->buffer.curs1,
 293                     (long) edit->buffer.size);
 294     }
 295 
 296     /*
 297      * If we are at the end of file, print <EOF>,
 298      * otherwise print the current character as is (if printable),
 299      * as decimal and as hex.
 300      */
 301     if (cols > 46)
 302     {
 303         edit_move (32, w->rect.lines - 1);
 304         if (edit->buffer.curs1 >= edit->buffer.size)
 305             tty_print_string ("[<EOF>       ]");
 306 #ifdef HAVE_CHARSET
 307         else if (edit->utf8)
 308         {
 309             unsigned int cur_utf;
 310             int char_length = 1;
 311 
 312             cur_utf = edit_buffer_get_utf (&edit->buffer, edit->buffer.curs1, &char_length);
 313             if (char_length <= 0)
 314                 cur_utf = edit_buffer_get_current_byte (&edit->buffer);
 315             tty_printf ("[%05u 0x%04X]", cur_utf, cur_utf);
 316         }
 317 #endif
 318         else
 319         {
 320             unsigned char cur_byte;
 321 
 322             cur_byte = edit_buffer_get_current_byte (&edit->buffer);
 323             tty_printf ("[%05u 0x%04X]", (unsigned int) cur_byte, (unsigned int) cur_byte);
 324         }
 325     }
 326 }
 327 
 328 /* --------------------------------------------------------------------------------------------- */
 329 /**
 330  * Draw a frame around edit area.
 331  *
 332  * @param edit   editor object
 333  * @param color  color pair
 334  * @param active TRUE if editor object is focused
 335  */
 336 
 337 static inline void
 338 edit_draw_frame (const WEdit *edit, int color, gboolean active)
     /* [previous][next][first][last][top][bottom][index][help]  */
 339 {
 340     const Widget *w = CONST_WIDGET (edit);
 341 
 342     // draw a frame around edit area
 343     tty_setcolor (color);
 344     // draw double frame for active window if skin supports that
 345     tty_draw_box (w->rect.y, w->rect.x, w->rect.lines, w->rect.cols, !active);
 346     // draw a drag marker
 347     if (edit->drag_state == MCEDIT_DRAG_NONE)
 348     {
 349         tty_setcolor (EDITOR_FRAME_DRAG);
 350         widget_gotoyx (w, w->rect.lines - 1, w->rect.cols - 1);
 351         tty_print_alt_char (ACS_LRCORNER, TRUE);
 352     }
 353 }
 354 
 355 /* --------------------------------------------------------------------------------------------- */
 356 /**
 357  * Draw a window control buttons.
 358  *
 359  * @param edit  editor object
 360  * @param color color pair
 361  */
 362 
 363 static inline void
 364 edit_draw_window_icons (const WEdit *edit, int color)
     /* [previous][next][first][last][top][bottom][index][help]  */
 365 {
 366     const Widget *w = CONST_WIDGET (edit);
 367     char tmp[17];
 368 
 369     tty_setcolor (color);
 370     if (edit->fullscreen != 0)
 371         widget_gotoyx (w->owner, 0, WIDGET (w->owner)->rect.cols - 6);
 372     else
 373         widget_gotoyx (w, 0, w->rect.cols - 8);
 374     g_snprintf (tmp, sizeof (tmp), "[%s][%s]", edit_window_state_char, edit_window_close_char);
 375     tty_print_string (tmp);
 376 }
 377 
 378 /* --------------------------------------------------------------------------------------------- */
 379 
 380 static inline void
 381 print_to_widget (WEdit *edit, long row, int start_col, int start_col_real, long end_col,
     /* [previous][next][first][last][top][bottom][index][help]  */
 382                  line_s line[], char *status, int bookmarked)
 383 {
 384     Widget *w = WIDGET (edit);
 385     line_s *p;
 386     int x, x1, y, cols_to_skip;
 387     int i;
 388     int wrap_start;
 389     int len;
 390 
 391     x = start_col_real;
 392     x1 = start_col + EDIT_TEXT_HORIZONTAL_OFFSET + edit_options.line_state_width;
 393     y = row + EDIT_TEXT_VERTICAL_OFFSET;
 394     cols_to_skip = abs (x);
 395 
 396     if (edit->fullscreen == 0)
 397     {
 398         x1++;
 399         y++;
 400     }
 401 
 402     tty_setcolor (EDITOR_NORMAL_COLOR);
 403     if (bookmarked != 0)
 404         tty_setcolor (bookmarked);
 405 
 406     len = end_col + 1 - start_col;
 407     wrap_start = edit_options.word_wrap_line_length + edit->start_col;
 408 
 409     if (len > 0 && w->rect.y + y >= 0)
 410     {
 411         if (!edit_options.show_right_margin || wrap_start > end_col)
 412             tty_draw_hline (w->rect.y + y, w->rect.x + x1, ' ', len);
 413         else if (wrap_start < 0)
 414         {
 415             tty_setcolor (EDITOR_RIGHT_MARGIN_COLOR);
 416             tty_draw_hline (w->rect.y + y, w->rect.x + x1, ' ', len);
 417         }
 418         else
 419         {
 420             if (wrap_start > 0)
 421                 tty_draw_hline (w->rect.y + y, w->rect.x + x1, ' ', wrap_start);
 422 
 423             len -= wrap_start;
 424             if (len > 0)
 425             {
 426                 tty_setcolor (EDITOR_RIGHT_MARGIN_COLOR);
 427                 tty_draw_hline (w->rect.y + y, w->rect.x + x1 + wrap_start, ' ', len);
 428             }
 429         }
 430     }
 431 
 432     if (edit_options.line_state)
 433     {
 434         tty_setcolor (LINE_STATE_COLOR);
 435 
 436         for (i = 0; i < LINE_STATE_WIDTH; i++)
 437         {
 438             edit_move (x1 + i - edit_options.line_state_width, y);
 439             if (status[i] == '\0')
 440                 status[i] = ' ';
 441             tty_print_char (status[i]);
 442         }
 443     }
 444 
 445     edit_move (x1, y);
 446 
 447     i = 1;
 448     for (p = line; p->ch != 0; p++)
 449     {
 450         int style;
 451         unsigned int textchar;
 452 
 453         if (cols_to_skip != 0)
 454         {
 455             cols_to_skip--;
 456             continue;
 457         }
 458 
 459         style = p->style & 0xFF00;
 460         textchar = p->ch;
 461 
 462         if ((style & MOD_WHITESPACE) != 0)
 463         {
 464             if ((style & MOD_MARKED) == 0)
 465                 tty_setcolor (EDITOR_WHITESPACE_COLOR);
 466             else
 467             {
 468                 textchar = ' ';
 469                 tty_setcolor (EDITOR_MARKED_COLOR);
 470             }
 471         }
 472         else if ((style & MOD_BOLD) != 0)
 473             tty_setcolor (EDITOR_BOLD_COLOR);
 474         else if ((style & MOD_MARKED) != 0)
 475             tty_setcolor (EDITOR_MARKED_COLOR);
 476         else if ((style & MOD_ABNORMAL) != 0)
 477             tty_setcolor (EDITOR_NONPRINTABLE_COLOR);
 478         else
 479             tty_lowlevel_setcolor (p->style >> 16);
 480 
 481         if (edit_options.show_right_margin)
 482         {
 483             if (i > edit_options.word_wrap_line_length + edit->start_col)
 484                 tty_setcolor (EDITOR_RIGHT_MARGIN_COLOR);
 485             i++;
 486         }
 487 
 488         tty_print_anychar (textchar);
 489     }
 490 }
 491 
 492 /* --------------------------------------------------------------------------------------------- */
 493 /** b is a pointer to the beginning of the line */
 494 
 495 static void
 496 edit_draw_this_line (WEdit *edit, off_t b, long row, long start_col, long end_col)
     /* [previous][next][first][last][top][bottom][index][help]  */
 497 {
 498     Widget *w = WIDGET (edit);
 499     line_s line[MAX_LINE_LEN];
 500     line_s *p = line;
 501     off_t q;
 502     int col, start_col_real;
 503     int abn_style;
 504     int book_mark = 0;
 505     char line_stat[LINE_STATE_WIDTH + 1] = "\0";
 506 
 507     if (row > w->rect.lines - 1 - EDIT_TEXT_VERTICAL_OFFSET - 2 * (edit->fullscreen != 0 ? 0 : 1))
 508         return;
 509 
 510     if (book_mark_query_color (edit, edit->start_line + row, BOOK_MARK_COLOR))
 511         book_mark = BOOK_MARK_COLOR;
 512     else if (book_mark_query_color (edit, edit->start_line + row, BOOK_MARK_FOUND_COLOR))
 513         book_mark = BOOK_MARK_FOUND_COLOR;
 514 
 515     if (book_mark != 0)
 516         abn_style = book_mark << 16;
 517     else
 518         abn_style = MOD_ABNORMAL;
 519 
 520     end_col -= EDIT_TEXT_HORIZONTAL_OFFSET + edit_options.line_state_width;
 521     if (edit->fullscreen == 0)
 522     {
 523         end_col--;
 524         if (w->rect.x + w->rect.cols <= WIDGET (w->owner)->rect.cols)
 525             end_col--;
 526     }
 527 
 528     q = edit_move_forward3 (edit, b, start_col - edit->start_col, 0);
 529     col = (int) edit_move_forward3 (edit, b, 0, q);
 530     start_col_real = col + edit->start_col;
 531 
 532     if (edit_options.line_state)
 533     {
 534         long cur_line;
 535 
 536         cur_line = edit->start_line + row;
 537         if (cur_line <= edit->buffer.lines)
 538             g_snprintf (line_stat, sizeof (line_stat), "%7ld ", cur_line + 1);
 539         else
 540         {
 541             memset (line_stat, ' ', LINE_STATE_WIDTH);
 542             line_stat[LINE_STATE_WIDTH] = '\0';
 543         }
 544 
 545         if (book_mark_query_color (edit, cur_line, BOOK_MARK_COLOR))
 546             g_snprintf (line_stat, 2, "*");
 547     }
 548 
 549     if (col <= -(edit->start_col + 16))
 550         start_col_real = start_col = 0;
 551     else
 552     {
 553         off_t m1 = 0, m2 = 0;
 554 
 555         eval_marks (edit, &m1, &m2);
 556 
 557         if (row <= edit->buffer.lines - edit->start_line)
 558         {
 559             off_t tws = 0;
 560 
 561             if (edit_options.visible_tws && tty_use_colors ())
 562                 for (tws = edit_buffer_get_eol (&edit->buffer, b); tws > b; tws--)
 563                 {
 564                     unsigned int c;
 565 
 566                     c = edit_buffer_get_byte (&edit->buffer, tws - 1);
 567                     if (!whitespace (c))
 568                         break;
 569                 }
 570 
 571             while (col <= end_col - edit->start_col)
 572             {
 573                 int char_length = 1;
 574                 unsigned int c;
 575                 gboolean wide_width_char = FALSE;
 576                 gboolean control_char = FALSE;
 577                 gboolean printable;
 578 
 579                 p->ch = 0;
 580                 p->style = q == edit->buffer.curs1 ? MOD_CURSOR : 0;
 581 
 582                 if (q >= m1 && q < m2)
 583                 {
 584                     if (!edit->column_highlight)
 585                         p->style |= MOD_MARKED;
 586                     else
 587                     {
 588                         long x, cl;
 589 
 590                         x = (long) edit_move_forward3 (edit, b, 0, q);
 591                         cl = MIN (edit->column1, edit->column2);
 592                         if (x >= cl)
 593                         {
 594                             cl = MAX (edit->column1, edit->column2);
 595                             if (x < cl)
 596                                 p->style |= MOD_MARKED;
 597                         }
 598                     }
 599                 }
 600 
 601                 if (q == edit->bracket)
 602                     p->style |= MOD_BOLD;
 603                 if (q >= edit->found_start && q < (off_t) (edit->found_start + edit->found_len))
 604                     p->style |= MOD_BOLD;
 605 
 606 #ifdef HAVE_CHARSET
 607                 if (edit->utf8)
 608                     c = edit_buffer_get_utf (&edit->buffer, q, &char_length);
 609                 else
 610 #endif
 611                     c = edit_buffer_get_byte (&edit->buffer, q);
 612 
 613                 // we don't use bg for mc - fg contains both
 614                 if (book_mark != 0)
 615                     p->style |= book_mark << 16;
 616                 else
 617                 {
 618                     int color;
 619 
 620                     color = edit_get_syntax_color (edit, q);
 621                     p->style |= color << 16;
 622                 }
 623 
 624                 switch (c)
 625                 {
 626                 case '\n':
 627                     col = end_col - edit->start_col + 1;  // quit
 628                     break;
 629 
 630                 case '\t':
 631                 {
 632                     int tab_over;
 633                     int i;
 634 
 635                     i = TAB_SIZE - ((int) col % TAB_SIZE);
 636                     tab_over = (end_col - edit->start_col) - (col + i - 1);
 637                     if (tab_over < 0)
 638                         i += tab_over;
 639                     col += i;
 640                     if ((edit_options.visible_tabs || (edit_options.visible_tws && q >= tws))
 641                         && enable_show_tabs_tws && tty_use_colors ())
 642                     {
 643                         if ((p->style & MOD_MARKED) != 0)
 644                             c = p->style;
 645                         else if (book_mark != 0)
 646                             c |= book_mark << 16;
 647                         else
 648                             c = p->style | MOD_WHITESPACE;
 649                         if (i > 2)
 650                         {
 651                             p->ch = '<';
 652                             p->style = c;
 653                             p++;
 654                             while (--i > 1)
 655                             {
 656                                 p->ch = '-';
 657                                 p->style = c;
 658                                 p++;
 659                             }
 660                             p->ch = '>';
 661                             p->style = c;
 662                             p++;
 663                         }
 664                         else if (i > 1)
 665                         {
 666                             p->ch = '<';
 667                             p->style = c;
 668                             p++;
 669                             p->ch = '>';
 670                             p->style = c;
 671                             p++;
 672                         }
 673                         else
 674                         {
 675                             p->ch = '>';
 676                             p->style = c;
 677                             p++;
 678                         }
 679                     }
 680                     else if (edit_options.visible_tws && q >= tws && enable_show_tabs_tws
 681                              && tty_use_colors ())
 682                     {
 683                         p->ch = '.';
 684                         p->style |= MOD_WHITESPACE;
 685                         c = p->style & ~MOD_CURSOR;
 686                         p++;
 687                         while (--i != 0)
 688                         {
 689                             p->ch = ' ';
 690                             p->style = c;
 691                             p++;
 692                         }
 693                     }
 694                     else
 695                     {
 696                         p->ch |= ' ';
 697                         c = p->style & ~MOD_CURSOR;
 698                         p++;
 699                         while (--i != 0)
 700                         {
 701                             p->ch = ' ';
 702                             p->style = c;
 703                             p++;
 704                         }
 705                     }
 706                 }
 707                 break;
 708 
 709                 case ' ':
 710                     if (edit_options.visible_tws && q >= tws && enable_show_tabs_tws
 711                         && tty_use_colors ())
 712                     {
 713                         p->ch = '.';
 714                         p->style |= MOD_WHITESPACE;
 715                         p++;
 716                         col++;
 717                         break;
 718                     }
 719                     MC_FALLTHROUGH;
 720 
 721                 default:
 722 #ifdef HAVE_CHARSET
 723                     if (mc_global.utf8_display)
 724                     {
 725                         if (!edit->utf8)
 726                             c = convert_from_8bit_to_utf_c ((unsigned char) c, edit->converter);
 727                         else if (g_unichar_iswide (c))
 728                         {
 729                             wide_width_char = TRUE;
 730                             col++;
 731                         }
 732                     }
 733                     else if (edit->utf8)
 734                         c = convert_from_utf_to_current_c (c, edit->converter);
 735                     else
 736                         c = convert_to_display_c (c);
 737 #endif
 738 
 739                     // Caret notation for control characters
 740                     if (c < 32)
 741                     {
 742                         p->ch = '^';
 743                         p->style = abn_style;
 744                         p++;
 745                         p->ch = c + 0x40;
 746                         p->style = abn_style;
 747                         p++;
 748                         col += 2;
 749                         control_char = TRUE;
 750                         break;
 751                     }
 752                     if (c == 127)
 753                     {
 754                         p->ch = '^';
 755                         p->style = abn_style;
 756                         p++;
 757                         p->ch = '?';
 758                         p->style = abn_style;
 759                         p++;
 760                         col += 2;
 761                         control_char = TRUE;
 762                         break;
 763                     }
 764 
 765 #ifdef HAVE_CHARSET
 766                     if (edit->utf8)
 767                     {
 768                         if (mc_global.utf8_display)
 769                             // c is gunichar
 770                             printable = g_unichar_isprint (c);
 771                         else
 772                             // c was gunichar; now c is 8-bit char converted from gunichar
 773                             printable = is_printable (c);
 774                     }
 775                     else
 776 #endif
 777                         // c is 8-bit char
 778                         printable = is_printable (c);
 779 
 780                     if (printable)
 781                         p->ch = c;
 782                     else
 783                     {
 784                         p->ch = '.';
 785                         p->style = abn_style;
 786                     }
 787                     p++;
 788                     col++;
 789                     break;
 790                 }  // case
 791 
 792                 q++;
 793                 if (char_length > 1)
 794                     q += char_length - 1;
 795 
 796                 if (col > (end_col - edit->start_col + 1))
 797                 {
 798                     if (wide_width_char)
 799                     {
 800                         p--;
 801                         break;
 802                     }
 803                     if (control_char)
 804                     {
 805                         p -= 2;
 806                         break;
 807                     }
 808                 }
 809             }
 810         }
 811     }
 812 
 813     p->ch = 0;
 814 
 815     print_to_widget (edit, row, start_col, start_col_real, end_col, line, line_stat, book_mark);
 816 }
 817 
 818 /* --------------------------------------------------------------------------------------------- */
 819 
 820 static inline void
 821 edit_draw_this_char (WEdit *edit, off_t curs, long row, long start_column, long end_column)
     /* [previous][next][first][last][top][bottom][index][help]  */
 822 {
 823     off_t b;
 824 
 825     b = edit_buffer_get_bol (&edit->buffer, curs);
 826     edit_draw_this_line (edit, b, row, start_column, end_column);
 827 }
 828 
 829 /* --------------------------------------------------------------------------------------------- */
 830 /** cursor must be in screen for other than REDRAW_PAGE passed in force */
 831 
 832 static inline void
 833 render_edit_text (WEdit *edit, long start_row, long start_column, long end_row, long end_column)
     /* [previous][next][first][last][top][bottom][index][help]  */
 834 {
 835     static long prev_curs_row = 0;
 836     static off_t prev_curs = 0;
 837 
 838     Widget *we = WIDGET (edit);
 839     Widget *wh = WIDGET (we->owner);
 840     WRect *w = &we->rect;
 841 
 842     int force = edit->force;
 843     int y1, x1, y2, x2;
 844     int last_line, last_column;
 845 
 846     // draw only visible region
 847 
 848     last_line = wh->rect.y + wh->rect.lines - 1;
 849 
 850     y1 = w->y;
 851     if (y1 > last_line - 1)  // buttonbar
 852         return;
 853 
 854     last_column = wh->rect.x + wh->rect.cols - 1;
 855 
 856     x1 = w->x;
 857     if (x1 > last_column)
 858         return;
 859 
 860     y2 = w->y + w->lines - 1;
 861     if (y2 < wh->rect.y + 1)  // menubar
 862         return;
 863 
 864     x2 = w->x + w->cols - 1;
 865     if (x2 < wh->rect.x)
 866         return;
 867 
 868     if ((force & REDRAW_IN_BOUNDS) == 0)
 869     {
 870         // !REDRAW_IN_BOUNDS means to ignore bounds and redraw whole rows
 871         // draw only visible region
 872 
 873         if (y2 <= last_line - 1)  // buttonbar
 874             end_row = w->lines - 1;
 875         else if (y1 >= wh->rect.y + 1)  // menubar
 876             end_row = wh->rect.lines - 1 - y1 - 1;
 877         else
 878             end_row = start_row + wh->rect.lines - 1 - 1;
 879 
 880         if (x2 <= last_column)
 881             end_column = w->cols - 1;
 882         else if (x1 >= wh->rect.x)
 883             end_column = wh->rect.cols - 1 - x1;
 884         else
 885             end_column = start_column + wh->rect.cols - 1;
 886     }
 887 
 888     /*
 889      * If the position of the page has not moved then we can draw the cursor
 890      * character only.  This will prevent line flicker when using arrow keys.
 891      */
 892     if ((force & REDRAW_CHAR_ONLY) == 0 || (force & REDRAW_PAGE) != 0)
 893     {
 894         long row = 0;
 895         long b;
 896 
 897         if ((force & REDRAW_PAGE) != 0)
 898         {
 899             b = edit_buffer_get_forward_offset (&edit->buffer, edit->start_display, start_row, 0);
 900             for (row = start_row; row <= end_row; row++)
 901             {
 902                 if (key_pending (edit))
 903                     return;
 904                 edit_draw_this_line (edit, b, row, start_column, end_column);
 905                 b = edit_buffer_get_forward_offset (&edit->buffer, b, 1, 0);
 906             }
 907         }
 908         else
 909         {
 910             long curs_row = edit->curs_row;
 911 
 912             if ((force & REDRAW_BEFORE_CURSOR) != 0 && start_row < curs_row)
 913             {
 914                 long upto;
 915 
 916                 b = edit->start_display;
 917                 upto = MIN (curs_row - 1, end_row);
 918                 for (row = start_row; row <= upto; row++)
 919                 {
 920                     if (key_pending (edit))
 921                         return;
 922                     edit_draw_this_line (edit, b, row, start_column, end_column);
 923                     b = edit_buffer_get_forward_offset (&edit->buffer, b, 1, 0);
 924                 }
 925             }
 926 
 927             //          if (force & REDRAW_LINE)          ---> default
 928             b = edit_buffer_get_current_bol (&edit->buffer);
 929             if (curs_row >= start_row && curs_row <= end_row)
 930             {
 931                 if (key_pending (edit))
 932                     return;
 933                 edit_draw_this_line (edit, b, curs_row, start_column, end_column);
 934             }
 935 
 936             if ((force & REDRAW_AFTER_CURSOR) != 0 && end_row > curs_row)
 937             {
 938                 b = edit_buffer_get_forward_offset (&edit->buffer, b, 1, 0);
 939                 for (row = MAX (curs_row + 1, start_row); row <= end_row; row++)
 940                 {
 941                     if (key_pending (edit))
 942                         return;
 943                     edit_draw_this_line (edit, b, row, start_column, end_column);
 944                     b = edit_buffer_get_forward_offset (&edit->buffer, b, 1, 0);
 945                 }
 946             }
 947 
 948             if ((force & REDRAW_LINE_ABOVE) != 0 && curs_row >= 1)
 949             {
 950                 row = curs_row - 1;
 951                 b = edit_buffer_get_current_bol (&edit->buffer);
 952                 b = edit_buffer_get_backward_offset (&edit->buffer, b, 1);
 953                 if (row >= start_row && row <= end_row)
 954                 {
 955                     if (key_pending (edit))
 956                         return;
 957                     edit_draw_this_line (edit, b, row, start_column, end_column);
 958                 }
 959             }
 960 
 961             if ((force & REDRAW_LINE_BELOW) != 0 && row < w->lines - 1)
 962             {
 963                 row = curs_row + 1;
 964                 b = edit_buffer_get_current_bol (&edit->buffer);
 965                 b = edit_buffer_get_forward_offset (&edit->buffer, b, 1, 0);
 966                 if (row >= start_row && row <= end_row)
 967                 {
 968                     if (key_pending (edit))
 969                         return;
 970                     edit_draw_this_line (edit, b, row, start_column, end_column);
 971                 }
 972             }
 973         }
 974     }
 975     else if (prev_curs_row < edit->curs_row)
 976     {
 977         // with the new text highlighting, we must draw from the top down
 978         edit_draw_this_char (edit, prev_curs, prev_curs_row, start_column, end_column);
 979         edit_draw_this_char (edit, edit->buffer.curs1, edit->curs_row, start_column, end_column);
 980     }
 981     else
 982     {
 983         edit_draw_this_char (edit, edit->buffer.curs1, edit->curs_row, start_column, end_column);
 984         edit_draw_this_char (edit, prev_curs, prev_curs_row, start_column, end_column);
 985     }
 986 
 987     edit->force = 0;
 988 
 989     prev_curs_row = edit->curs_row;
 990     prev_curs = edit->buffer.curs1;
 991 }
 992 
 993 /* --------------------------------------------------------------------------------------------- */
 994 
 995 static inline void
 996 edit_render (WEdit *edit, int page, int row_start, int col_start, int row_end, int col_end)
     /* [previous][next][first][last][top][bottom][index][help]  */
 997 {
 998     if (page != 0)  // if it was an expose event, 'page' would be set
 999         edit->force |= REDRAW_PAGE | REDRAW_IN_BOUNDS;
1000 
1001     render_edit_text (edit, row_start, col_start, row_end, col_end);
1002 
1003     /*
1004      * edit->force != 0 means a key was pending and the redraw
1005      * was halted, so next time we must redraw everything in case stuff
1006      * was left undrawn from a previous key press.
1007      */
1008     if (edit->force != 0)
1009         edit->force |= REDRAW_PAGE;
1010 }
1011 
1012 /* --------------------------------------------------------------------------------------------- */
1013 /*** public functions ****************************************************************************/
1014 /* --------------------------------------------------------------------------------------------- */
1015 
1016 void
1017 edit_status (WEdit *edit, gboolean active)
     /* [previous][next][first][last][top][bottom][index][help]  */
1018 {
1019     int color;
1020 
1021     if (edit->fullscreen != 0)
1022     {
1023         color = STATUSBAR_COLOR;
1024         edit_status_fullscreen (edit, color);
1025     }
1026     else
1027     {
1028         color = edit->drag_state != MCEDIT_DRAG_NONE ? EDITOR_FRAME_DRAG
1029             : active                                 ? EDITOR_FRAME_ACTIVE
1030                                                      : EDITOR_FRAME;
1031         edit_draw_frame (edit, color, active);
1032         edit_status_window (edit);
1033     }
1034 
1035     edit_draw_window_icons (edit, color);
1036 }
1037 
1038 /* --------------------------------------------------------------------------------------------- */
1039 
1040 /** this scrolls the text so that cursor is on the screen */
1041 void
1042 edit_scroll_screen_over_cursor (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1043 {
1044     WRect *w = &WIDGET (edit)->rect;
1045 
1046     long p;
1047     long outby;
1048     int b_extreme, t_extreme, l_extreme, r_extreme;
1049 
1050     if (w->lines <= 0 || w->cols <= 0)
1051         return;
1052 
1053     rect_resize (w, -EDIT_TEXT_VERTICAL_OFFSET,
1054                  -(EDIT_TEXT_HORIZONTAL_OFFSET + edit_options.line_state_width));
1055 
1056     if (edit->fullscreen == 0)
1057         rect_grow (w, -1, -1);
1058 
1059     r_extreme = EDIT_RIGHT_EXTREME;
1060     l_extreme = EDIT_LEFT_EXTREME;
1061     b_extreme = EDIT_BOTTOM_EXTREME;
1062     t_extreme = EDIT_TOP_EXTREME;
1063     if (edit->found_len != 0)
1064     {
1065         b_extreme = MAX (w->lines / 4, b_extreme);
1066         t_extreme = MAX (w->lines / 4, t_extreme);
1067     }
1068     if (b_extreme + t_extreme + 1 > w->lines)
1069     {
1070         int n;
1071 
1072         n = b_extreme + t_extreme;
1073         if (n == 0)
1074             n = 1;
1075         b_extreme = (b_extreme * (w->lines - 1)) / n;
1076         t_extreme = (t_extreme * (w->lines - 1)) / n;
1077     }
1078     if (l_extreme + r_extreme + 1 > w->cols)
1079     {
1080         int n;
1081 
1082         n = l_extreme + r_extreme;
1083         if (n == 0)
1084             n = 1;
1085         l_extreme = (l_extreme * (w->cols - 1)) / n;
1086         r_extreme = (r_extreme * (w->cols - 1)) / n;
1087     }
1088     p = edit_get_col (edit) + edit->over_col;
1089     edit_update_curs_row (edit);
1090     outby = p + edit->start_col - w->cols + 1 + (r_extreme + edit->found_len);
1091     if (outby > 0)
1092         edit_scroll_right (edit, outby);
1093     outby = l_extreme - p - edit->start_col;
1094     if (outby > 0)
1095         edit_scroll_left (edit, outby);
1096     p = edit->curs_row;
1097     outby = p - w->lines + 1 + b_extreme;
1098     if (outby > 0)
1099         edit_scroll_downward (edit, outby);
1100     outby = t_extreme - p;
1101     if (outby > 0)
1102         edit_scroll_upward (edit, outby);
1103     edit_update_curs_row (edit);
1104 
1105     rect_resize (w, EDIT_TEXT_VERTICAL_OFFSET,
1106                  EDIT_TEXT_HORIZONTAL_OFFSET + edit_options.line_state_width);
1107     if (edit->fullscreen == 0)
1108         rect_grow (w, 1, 1);
1109 }
1110 
1111 /* --------------------------------------------------------------------------------------------- */
1112 
1113 void
1114 edit_render_keypress (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1115 {
1116     edit_render (edit, 0, 0, 0, 0, 0);
1117 }
1118 
1119 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */