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

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