root/src/editor/editwidget.c

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

DEFINITIONS

This source file includes following definitions.
  1. edit_dlg_init
  2. edit_dlg_deinit
  3. edit_about
  4. edit_help
  5. edit_restore_size
  6. edit_window_move
  7. edit_window_resize
  8. get_hotkey
  9. edit_window_list
  10. edit_get_shortcut
  11. edit_get_title
  12. edit_dialog_command_execute
  13. edit_translate_key
  14. edit_quit
  15. edit_set_buttonbar
  16. edit_total_update
  17. edit_update_cursor
  18. edit_dialog_callback
  19. edit_dialog_mouse_callback
  20. edit_dialog_bg_callback
  21. edit_callback
  22. edit_mouse_handle_move_resize
  23. edit_mouse_callback
  24. edit_file
  25. edit_files
  26. edit_get_file_name
  27. edit_find_editor
  28. edit_widget_is_editor
  29. edit_update_screen
  30. edit_save_size
  31. edit_add_window
  32. edit_handle_move_resize
  33. edit_toggle_fullscreen

   1 /*
   2    Editor initialisation and callback handler.
   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 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  25  */
  26 
  27 /** \file
  28  *  \brief Source: editor initialisation and callback handler
  29  *  \author Paul Sheer
  30  *  \date 1996, 1997
  31  */
  32 
  33 #include <config.h>
  34 
  35 #include <ctype.h>
  36 #include <errno.h>
  37 #include <stdlib.h>
  38 #include <string.h>
  39 #include <sys/types.h>
  40 #include <unistd.h>
  41 
  42 #include "lib/global.h"
  43 
  44 #include "lib/tty/tty.h"        /* LINES, COLS */
  45 #include "lib/tty/key.h"        /* is_idle() */
  46 #include "lib/tty/color.h"      /* tty_setcolor() */
  47 #include "lib/skin.h"
  48 #include "lib/fileloc.h"        /* EDIT_HOME_DIR */
  49 #include "lib/strutil.h"        /* str_term_trim() */
  50 #include "lib/util.h"           /* mc_build_filename() */
  51 #include "lib/widget.h"
  52 #include "lib/mcconfig.h"
  53 #include "lib/event.h"          /* mc_event_raise() */
  54 #ifdef HAVE_CHARSET
  55 #include "lib/charsets.h"
  56 #endif
  57 
  58 #include "src/keymap.h"         /* keybind_lookup_keymap_command() */
  59 #include "src/setup.h"          /* home_dir */
  60 #include "src/execute.h"        /* toggle_subshell()  */
  61 #include "src/filemanager/cmd.h"        /* save_setup_cmd()  */
  62 #include "src/learn.h"          /* learn_keys() */
  63 #include "src/args.h"           /* mcedit_arg_t */
  64 
  65 #include "edit-impl.h"
  66 #include "editwidget.h"
  67 #include "editmacros.h"         /* edit_execute_macro() */
  68 #ifdef HAVE_ASPELL
  69 #include "spell.h"
  70 #endif
  71 
  72 /*** global variables ****************************************************************************/
  73 
  74 char *edit_window_state_char = NULL;
  75 char *edit_window_close_char = NULL;
  76 
  77 /*** file scope macro definitions ****************************************************************/
  78 
  79 #define WINDOW_MIN_LINES (2 + 2)
  80 #define WINDOW_MIN_COLS (2 + LINE_STATE_WIDTH + 2)
  81 
  82 /*** file scope type declarations ****************************************************************/
  83 
  84 /*** forward declarations (file scope functions) *************************************************/
  85 
  86 /*** file scope variables ************************************************************************/
  87 
  88 static unsigned int edit_dlg_init_refcounter = 0;
  89 
  90 /* --------------------------------------------------------------------------------------------- */
  91 /*** file scope functions ************************************************************************/
  92 /* --------------------------------------------------------------------------------------------- */
  93 /**
  94  * Init the 'edit' subsystem
  95  */
  96 
  97 static void
  98 edit_dlg_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  99 {
 100     edit_dlg_init_refcounter++;
 101 
 102     if (edit_dlg_init_refcounter == 1)
 103     {
 104         edit_window_state_char = mc_skin_get ("widget-editor", "window-state-char", "*");
 105         edit_window_close_char = mc_skin_get ("widget-editor", "window-close-char", "X");
 106 
 107 #ifdef HAVE_ASPELL
 108         aspell_init ();
 109 #endif
 110     }
 111 }
 112 
 113 /* --------------------------------------------------------------------------------------------- */
 114 /**
 115  * Deinit the 'edit' subsystem
 116  */
 117 
 118 static void
 119 edit_dlg_deinit (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 120 {
 121     if (edit_dlg_init_refcounter == 1)
 122     {
 123         g_free (edit_window_state_char);
 124         g_free (edit_window_close_char);
 125 
 126 #ifdef HAVE_ASPELL
 127         aspell_clean ();
 128 #endif
 129     }
 130 
 131     if (edit_dlg_init_refcounter != 0)
 132         edit_dlg_init_refcounter--;
 133 }
 134 
 135 /* --------------------------------------------------------------------------------------------- */
 136 /**
 137  * Show info about editor
 138  */
 139 
 140 static void
 141 edit_about (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 142 {
 143     char *ver;
 144 
 145     ver = g_strdup_printf ("MCEdit %s", mc_global.mc_version);
 146 
 147     {
 148         quick_widget_t quick_widgets[] = {
 149             /* *INDENT-OFF* */
 150             QUICK_LABEL (ver, NULL),
 151             QUICK_SEPARATOR (TRUE),
 152             QUICK_LABEL (N_("A user friendly text editor\n"
 153                             "written for the Midnight Commander."), NULL),
 154             QUICK_SEPARATOR (FALSE),
 155             QUICK_LABEL (N_("Copyright (C) 1996-2024 the Free Software Foundation"), NULL),
 156             QUICK_START_BUTTONS (TRUE, TRUE),
 157             QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
 158             QUICK_END
 159             /* *INDENT-ON* */
 160         };
 161 
 162         WRect r = { -1, -1, 0, 40 };
 163 
 164         quick_dialog_t qdlg = {
 165             r, N_("About"), "[Internal File Editor]",
 166             quick_widgets, NULL, NULL
 167         };
 168 
 169         quick_widgets[0].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
 170         quick_widgets[2].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
 171         quick_widgets[4].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
 172 
 173         (void) quick_dialog (&qdlg);
 174     }
 175 
 176     g_free (ver);
 177 }
 178 
 179 /* --------------------------------------------------------------------------------------------- */
 180 /**
 181  * Show a help window
 182  */
 183 
 184 static void
 185 edit_help (const WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 186 {
 187     ev_help_t event_data = { NULL, h->help_ctx };
 188 
 189     mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
 190 }
 191 
 192 /* --------------------------------------------------------------------------------------------- */
 193 /**
 194  * Restore saved window size.
 195  *
 196  * @param edit editor object
 197  */
 198 
 199 static void
 200 edit_restore_size (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 201 {
 202     Widget *w = WIDGET (edit);
 203 
 204     edit->drag_state = MCEDIT_DRAG_NONE;
 205     w->mouse.forced_capture = FALSE;
 206     widget_set_size_rect (w, &edit->loc_prev);
 207     widget_draw (WIDGET (w->owner));
 208 }
 209 
 210 /* --------------------------------------------------------------------------------------------- */
 211 /**
 212  * Move window by one row or column in any direction.
 213  *
 214  * @param edit    editor object
 215  * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
 216  */
 217 
 218 static void
 219 edit_window_move (WEdit * edit, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 220 {
 221     Widget *we = WIDGET (edit);
 222     Widget *wo = WIDGET (we->owner);
 223     WRect *w = &we->rect;
 224     const WRect *wh = &wo->rect;
 225 
 226     switch (command)
 227     {
 228     case CK_Up:
 229         if (w->y > wh->y + 1)   /* menubar */
 230             w->y--;
 231         break;
 232     case CK_Down:
 233         if (w->y < wh->y + wh->lines - 2)       /* buttonbar */
 234             w->y++;
 235         break;
 236     case CK_Left:
 237         if (w->x + wh->cols > wh->x)
 238             w->x--;
 239         break;
 240     case CK_Right:
 241         if (w->x < wh->x + wh->cols)
 242             w->x++;
 243         break;
 244     default:
 245         return;
 246     }
 247 
 248     edit->force |= REDRAW_PAGE;
 249     widget_draw (wo);
 250 }
 251 
 252 /* --------------------------------------------------------------------------------------------- */
 253 /**
 254  * Resize window by one row or column in any direction.
 255  *
 256  * @param edit    editor object
 257  * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
 258  */
 259 
 260 static void
 261 edit_window_resize (WEdit * edit, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 262 {
 263     Widget *we = WIDGET (edit);
 264     Widget *wo = WIDGET (we->owner);
 265     WRect *w = &we->rect;
 266     const WRect *wh = &wo->rect;
 267 
 268     switch (command)
 269     {
 270     case CK_Up:
 271         if (w->lines > WINDOW_MIN_LINES)
 272             w->lines--;
 273         break;
 274     case CK_Down:
 275         if (w->y + w->lines < wh->y + wh->lines - 1)    /* buttonbar */
 276             w->lines++;
 277         break;
 278     case CK_Left:
 279         if (w->cols > WINDOW_MIN_COLS)
 280             w->cols--;
 281         break;
 282     case CK_Right:
 283         if (w->x + w->cols < wh->x + wh->cols)
 284             w->cols++;
 285         break;
 286     default:
 287         return;
 288     }
 289 
 290     edit->force |= REDRAW_COMPLETELY;
 291     widget_draw (wo);
 292 }
 293 
 294 /* --------------------------------------------------------------------------------------------- */
 295 /**
 296  * Get hotkey by number.
 297  *
 298  * @param n number
 299  * @return hotkey
 300  */
 301 
 302 static unsigned char
 303 get_hotkey (int n)
     /* [previous][next][first][last][top][bottom][index][help]  */
 304 {
 305     return (n <= 9) ? '0' + n : 'a' + n - 10;
 306 }
 307 
 308 /* --------------------------------------------------------------------------------------------- */
 309 
 310 static void
 311 edit_window_list (const WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 312 {
 313     const WGroup *g = CONST_GROUP (h);
 314     const size_t offset = 2;    /* skip menu and buttonbar */
 315     const size_t dlg_num = g_list_length (g->widgets) - offset;
 316     int lines, cols;
 317     Listbox *listbox;
 318     GList *w;
 319     WEdit *selected;
 320     int i = 0;
 321 
 322     lines = MIN ((size_t) (LINES * 2 / 3), dlg_num);
 323     cols = COLS * 2 / 3;
 324 
 325     listbox = listbox_window_new (lines, cols, _("Open files"), "[Open files]");
 326 
 327     for (w = g->widgets; w != NULL; w = g_list_next (w))
 328         if (edit_widget_is_editor (CONST_WIDGET (w->data)))
 329         {
 330             WEdit *e = EDIT (w->data);
 331             char *fname;
 332 
 333             if (e->filename_vpath == NULL)
 334                 fname = g_strdup_printf ("%c [%s]", e->modified ? '*' : ' ', _("NoName"));
 335             else
 336                 fname =
 337                     g_strdup_printf ("%c%s", e->modified ? '*' : ' ',
 338                                      vfs_path_as_str (e->filename_vpath));
 339 
 340             listbox_add_item (listbox->list, LISTBOX_APPEND_AT_END, get_hotkey (i++),
 341                               str_term_trim (fname, WIDGET (listbox->list)->rect.cols - 2), e,
 342                               FALSE);
 343             g_free (fname);
 344         }
 345 
 346     selected = listbox_run_with_data (listbox, g->current->data);
 347     if (selected != NULL)
 348         widget_select (WIDGET (selected));
 349 }
 350 
 351 /* --------------------------------------------------------------------------------------------- */
 352 
 353 static char *
 354 edit_get_shortcut (long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 355 {
 356     const char *ext_map;
 357     const char *shortcut = NULL;
 358 
 359     shortcut = keybind_lookup_keymap_shortcut (editor_map, command);
 360     if (shortcut != NULL)
 361         return g_strdup (shortcut);
 362 
 363     ext_map = keybind_lookup_keymap_shortcut (editor_map, CK_ExtendedKeyMap);
 364     if (ext_map != NULL)
 365         shortcut = keybind_lookup_keymap_shortcut (editor_x_map, command);
 366     if (shortcut != NULL)
 367         return g_strdup_printf ("%s %s", ext_map, shortcut);
 368 
 369     return NULL;
 370 }
 371 
 372 /* --------------------------------------------------------------------------------------------- */
 373 
 374 static char *
 375 edit_get_title (const WDialog * h, size_t len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 376 {
 377     const WEdit *edit;
 378     const char *modified;
 379     const char *file_label;
 380     char *filename;
 381 
 382     edit = edit_find_editor (h);
 383     modified = edit->modified ? "(*) " : "    ";
 384 
 385     len -= 4;
 386 
 387     if (edit->filename_vpath == NULL)
 388         filename = g_strdup (_("[NoName]"));
 389     else
 390         filename = g_strdup (vfs_path_as_str (edit->filename_vpath));
 391 
 392     file_label = str_term_trim (filename, len - str_term_width1 (_("Edit: ")));
 393     g_free (filename);
 394 
 395     return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL);
 396 }
 397 
 398 /* --------------------------------------------------------------------------------------------- */
 399 
 400 static cb_ret_t
 401 edit_dialog_command_execute (WDialog * h, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 402 {
 403     WGroup *g = GROUP (h);
 404     cb_ret_t ret = MSG_HANDLED;
 405 
 406     switch (command)
 407     {
 408     case CK_EditNew:
 409         edit_load_file_from_filename (h, NULL, 0);
 410         break;
 411     case CK_EditFile:
 412         edit_load_cmd (h);
 413         break;
 414     case CK_History:
 415         edit_load_file_from_history (h);
 416         break;
 417     case CK_EditSyntaxFile:
 418         edit_load_syntax_file (h);
 419         break;
 420     case CK_EditUserMenu:
 421         edit_load_menu_file (h);
 422         break;
 423     case CK_Close:
 424         /* if there are no opened files anymore, close MC editor */
 425         if (edit_widget_is_editor (CONST_WIDGET (g->current->data)) &&
 426             edit_close_cmd (EDIT (g->current->data)) && edit_find_editor (h) == NULL)
 427             dlg_close (h);
 428         break;
 429     case CK_Help:
 430         edit_help (h);
 431         break;
 432     case CK_Menu:
 433         edit_menu_cmd (h);
 434         break;
 435     case CK_Quit:
 436     case CK_Cancel:
 437         /* don't close editor due to SIGINT, but stop move/resize window */
 438         {
 439             Widget *w = WIDGET (g->current->data);
 440 
 441             if (edit_widget_is_editor (w) && EDIT (w)->drag_state != MCEDIT_DRAG_NONE)
 442                 edit_restore_size (EDIT (w));
 443             else if (command == CK_Quit)
 444                 dlg_close (h);
 445         }
 446         break;
 447     case CK_About:
 448         edit_about ();
 449         break;
 450     case CK_SyntaxOnOff:
 451         edit_syntax_onoff_cmd (h);
 452         break;
 453     case CK_ShowTabTws:
 454         edit_show_tabs_tws_cmd (h);
 455         break;
 456     case CK_ShowMargin:
 457         edit_show_margin_cmd (h);
 458         break;
 459     case CK_ShowNumbers:
 460         edit_show_numbers_cmd (h);
 461         break;
 462     case CK_Refresh:
 463         edit_refresh_cmd ();
 464         break;
 465     case CK_Shell:
 466         toggle_subshell ();
 467         break;
 468     case CK_LearnKeys:
 469         learn_keys ();
 470         break;
 471     case CK_WindowMove:
 472     case CK_WindowResize:
 473         if (edit_widget_is_editor (CONST_WIDGET (g->current->data)))
 474             edit_handle_move_resize (EDIT (g->current->data), command);
 475         break;
 476     case CK_WindowList:
 477         edit_window_list (h);
 478         break;
 479     case CK_WindowNext:
 480         group_select_next_widget (g);
 481         break;
 482     case CK_WindowPrev:
 483         group_select_prev_widget (g);
 484         break;
 485     case CK_Options:
 486         edit_options_dialog (h);
 487         break;
 488     case CK_OptionsSaveMode:
 489         edit_save_mode_cmd ();
 490         break;
 491     case CK_SaveSetup:
 492         save_setup_cmd ();
 493         break;
 494     default:
 495         ret = MSG_NOT_HANDLED;
 496         break;
 497     }
 498 
 499     return ret;
 500 }
 501 
 502 /* --------------------------------------------------------------------------------------------- */
 503 /*
 504  * Translate the keycode into either 'command' or 'char_for_insertion'.
 505  * 'command' is one of the editor commands from lib/keybind.h.
 506  */
 507 
 508 static gboolean
 509 edit_translate_key (WEdit * edit, long x_key, int *cmd, int *ch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 510 {
 511     Widget *w = WIDGET (edit);
 512     long command = CK_InsertChar;
 513     int char_for_insertion = -1;
 514 
 515     /* an ordinary insertable character */
 516     if (!w->ext_mode && x_key < 256)
 517     {
 518 #ifndef HAVE_CHARSET
 519         if (is_printable (x_key))
 520         {
 521             char_for_insertion = x_key;
 522             goto fin;
 523         }
 524 #else
 525         int c;
 526 
 527         if (edit->charpoint >= MB_LEN_MAX)
 528         {
 529             edit->charpoint = 0;
 530             edit->charbuf[edit->charpoint] = '\0';
 531         }
 532         if (edit->charpoint < MB_LEN_MAX)
 533         {
 534             edit->charbuf[edit->charpoint++] = x_key;
 535             edit->charbuf[edit->charpoint] = '\0';
 536         }
 537 
 538         /* input from 8-bit locale */
 539         if (!mc_global.utf8_display)
 540         {
 541             /* source is in 8-bit codeset */
 542             c = convert_from_input_c (x_key);
 543 
 544             if (is_printable (c))
 545             {
 546                 if (!edit->utf8)
 547                     char_for_insertion = c;
 548                 else
 549                     char_for_insertion = convert_from_8bit_to_utf_c2 ((char) x_key);
 550                 goto fin;
 551             }
 552         }
 553         else
 554         {
 555             /* UTF-8 locale */
 556             int res;
 557 
 558             res = str_is_valid_char (edit->charbuf, edit->charpoint);
 559             if (res < 0 && res != -2)
 560             {
 561                 edit->charpoint = 0;    /* broken multibyte char, skip */
 562                 goto fin;
 563             }
 564 
 565             if (edit->utf8)
 566             {
 567                 /* source is in UTF-8 codeset */
 568                 if (res < 0)
 569                 {
 570                     char_for_insertion = x_key;
 571                     goto fin;
 572                 }
 573 
 574                 edit->charbuf[edit->charpoint] = '\0';
 575                 edit->charpoint = 0;
 576                 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
 577                 {
 578                     char_for_insertion = x_key;
 579                     goto fin;
 580                 }
 581             }
 582             else
 583             {
 584                 /* 8-bit source */
 585                 if (res < 0)
 586                 {
 587                     /* not finished multibyte input (we're in the middle of multibyte utf-8 char) */
 588                     goto fin;
 589                 }
 590 
 591                 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
 592                 {
 593                     c = convert_from_utf_to_current (edit->charbuf);
 594                     edit->charbuf[0] = '\0';
 595                     edit->charpoint = 0;
 596                     char_for_insertion = c;
 597                     goto fin;
 598                 }
 599 
 600                 /* non-printable utf-8 input, skip it */
 601                 edit->charbuf[0] = '\0';
 602                 edit->charpoint = 0;
 603             }
 604         }
 605 #endif /* HAVE_CHARSET */
 606     }
 607 
 608     /* Commands specific to the key emulation */
 609     command = widget_lookup_key (w, x_key);
 610     if (command == CK_IgnoreKey)
 611         command = CK_InsertChar;
 612 
 613   fin:
 614     *cmd = (int) command;       /* FIXME */
 615     *ch = char_for_insertion;
 616 
 617     return !(command == CK_InsertChar && char_for_insertion == -1);
 618 }
 619 
 620 
 621 /* --------------------------------------------------------------------------------------------- */
 622 
 623 static inline void
 624 edit_quit (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 625 {
 626     GList *l;
 627     WEdit *e = NULL;
 628     GSList *m = NULL;
 629     GSList *me;
 630 
 631     /* don't stop the dialog before final decision */
 632     widget_set_state (WIDGET (h), WST_ACTIVE, TRUE);
 633 
 634     /* check window state and get modified files */
 635     for (l = GROUP (h)->widgets; l != NULL; l = g_list_next (l))
 636         if (edit_widget_is_editor (CONST_WIDGET (l->data)))
 637         {
 638             e = EDIT (l->data);
 639 
 640             if (e->drag_state != MCEDIT_DRAG_NONE)
 641             {
 642                 edit_restore_size (e);
 643                 g_slist_free (m);
 644                 return;
 645             }
 646 
 647             /* create separate list because widget_select()
 648                changes the window position in Z order */
 649             if (e->modified)
 650                 m = g_slist_prepend (m, l->data);
 651         }
 652 
 653     for (me = m; me != NULL; me = g_slist_next (me))
 654     {
 655         e = EDIT (me->data);
 656 
 657         widget_select (WIDGET (e));
 658 
 659         if (!edit_ok_to_exit (e))
 660             break;
 661     }
 662 
 663     /* if all files were checked, quit editor */
 664     if (me == NULL)
 665         dlg_close (h);
 666 
 667     g_slist_free (m);
 668 }
 669 
 670 /* --------------------------------------------------------------------------------------------- */
 671 
 672 static inline void
 673 edit_set_buttonbar (WEdit * edit, WButtonBar * bb)
     /* [previous][next][first][last][top][bottom][index][help]  */
 674 {
 675     Widget *w = WIDGET (edit);
 676 
 677     buttonbar_set_label (bb, 1, Q_ ("ButtonBar|Help"), w->keymap, NULL);
 678     buttonbar_set_label (bb, 2, Q_ ("ButtonBar|Save"), w->keymap, w);
 679     buttonbar_set_label (bb, 3, Q_ ("ButtonBar|Mark"), w->keymap, w);
 680     buttonbar_set_label (bb, 4, Q_ ("ButtonBar|Replac"), w->keymap, w);
 681     buttonbar_set_label (bb, 5, Q_ ("ButtonBar|Copy"), w->keymap, w);
 682     buttonbar_set_label (bb, 6, Q_ ("ButtonBar|Move"), w->keymap, w);
 683     buttonbar_set_label (bb, 7, Q_ ("ButtonBar|Search"), w->keymap, w);
 684     buttonbar_set_label (bb, 8, Q_ ("ButtonBar|Delete"), w->keymap, w);
 685     buttonbar_set_label (bb, 9, Q_ ("ButtonBar|PullDn"), w->keymap, NULL);
 686     buttonbar_set_label (bb, 10, Q_ ("ButtonBar|Quit"), w->keymap, NULL);
 687 }
 688 
 689 /* --------------------------------------------------------------------------------------------- */
 690 
 691 static void
 692 edit_total_update (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 693 {
 694     edit_find_bracket (edit);
 695     edit->force |= REDRAW_COMPLETELY;
 696     edit_update_curs_row (edit);
 697     edit_update_screen (edit);
 698 }
 699 
 700 /* --------------------------------------------------------------------------------------------- */
 701 
 702 static gboolean
 703 edit_update_cursor (WEdit * edit, const mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 704 {
 705     int x, y;
 706     gboolean done;
 707 
 708     x = event->x - (edit->fullscreen ? 0 : 1);
 709     y = event->y - (edit->fullscreen ? 0 : 1);
 710 
 711     if (edit->mark2 != -1 && event->msg == MSG_MOUSE_UP)
 712         return TRUE;            /* don't do anything */
 713 
 714     if (event->msg == MSG_MOUSE_DOWN || event->msg == MSG_MOUSE_UP)
 715         edit_push_key_press (edit);
 716 
 717     if (!edit_options.cursor_beyond_eol)
 718         edit->prev_col = x - edit->start_col - edit_options.line_state_width;
 719     else
 720     {
 721         long line_len;
 722 
 723         line_len =
 724             edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), 0,
 725                                 edit_buffer_get_current_eol (&edit->buffer));
 726 
 727         if (x > line_len - 1)
 728         {
 729             edit->over_col = x - line_len - edit->start_col - edit_options.line_state_width;
 730             edit->prev_col = line_len;
 731         }
 732         else
 733         {
 734             edit->over_col = 0;
 735             edit->prev_col = x - edit_options.line_state_width - edit->start_col;
 736         }
 737     }
 738 
 739     if (y > edit->curs_row)
 740         edit_move_down (edit, y - edit->curs_row, FALSE);
 741     else if (y < edit->curs_row)
 742         edit_move_up (edit, edit->curs_row - y, FALSE);
 743     else
 744         edit_move_to_prev_col (edit, edit_buffer_get_current_bol (&edit->buffer));
 745 
 746     if (event->msg == MSG_MOUSE_CLICK)
 747     {
 748         edit_mark_cmd (edit, TRUE);     /* reset */
 749         edit->highlight = 0;
 750     }
 751 
 752     done = (event->msg != MSG_MOUSE_DRAG);
 753     if (done)
 754         edit_mark_cmd (edit, FALSE);
 755 
 756     return done;
 757 }
 758 
 759 /* --------------------------------------------------------------------------------------------- */
 760 /** Callback for the edit dialog */
 761 
 762 static cb_ret_t
 763 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 764 {
 765     WGroup *g = GROUP (w);
 766     WDialog *h = DIALOG (w);
 767 
 768     switch (msg)
 769     {
 770     case MSG_INIT:
 771         edit_dlg_init ();
 772         return MSG_HANDLED;
 773 
 774     case MSG_RESIZE:
 775         dlg_default_callback (w, NULL, MSG_RESIZE, 0, NULL);
 776         menubar_arrange (menubar_find (h));
 777         return MSG_HANDLED;
 778 
 779     case MSG_ACTION:
 780         {
 781             /* Handle shortcuts, menu, and buttonbar. */
 782 
 783             cb_ret_t result;
 784 
 785             result = edit_dialog_command_execute (h, parm);
 786 
 787             /* We forward any commands coming from the menu, and which haven't been
 788                handled by the dialog, to the focused WEdit window. */
 789             if (result == MSG_NOT_HANDLED && sender == WIDGET (menubar_find (h)))
 790                 result = send_message (g->current->data, NULL, MSG_ACTION, parm, NULL);
 791 
 792             return result;
 793         }
 794 
 795     case MSG_KEY:
 796         {
 797             Widget *we = WIDGET (g->current->data);
 798             cb_ret_t ret = MSG_NOT_HANDLED;
 799 
 800             if (edit_widget_is_editor (we))
 801             {
 802                 gboolean ext_mode;
 803                 long command;
 804 
 805                 /* keep and then extmod flag */
 806                 ext_mode = we->ext_mode;
 807                 command = widget_lookup_key (we, parm);
 808                 we->ext_mode = ext_mode;
 809 
 810                 if (command == CK_IgnoreKey)
 811                     we->ext_mode = FALSE;
 812                 else
 813                 {
 814                     ret = edit_dialog_command_execute (h, command);
 815                     /* if command was not handled, keep the extended mode
 816                        for the further key processing */
 817                     if (ret == MSG_HANDLED)
 818                         we->ext_mode = FALSE;
 819                 }
 820             }
 821 
 822             /*
 823              * Due to the "end of bracket" escape the editor sees input with is_idle() == false
 824              * (expects more characters) and hence doesn't yet refresh the screen, but then
 825              * no further characters arrive (there's only an "end of bracket" which is swallowed
 826              * by tty_get_event()), so you end up with a screen that's not refreshed after pasting.
 827              * So let's trigger an IDLE signal.
 828              */
 829             if (!is_idle ())
 830                 widget_idle (w, TRUE);
 831             return ret;
 832         }
 833 
 834         /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */
 835     case MSG_UNHANDLED_KEY:
 836         return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
 837 
 838     case MSG_VALIDATE:
 839         edit_quit (h);
 840         return MSG_HANDLED;
 841 
 842     case MSG_DESTROY:
 843         edit_dlg_deinit ();
 844         return MSG_HANDLED;
 845 
 846     case MSG_IDLE:
 847         widget_idle (w, FALSE);
 848         return send_message (g->current->data, NULL, MSG_IDLE, 0, NULL);
 849 
 850     default:
 851         return dlg_default_callback (w, sender, msg, parm, data);
 852     }
 853 }
 854 
 855 /* --------------------------------------------------------------------------------------------- */
 856 
 857 /**
 858  * Handle mouse events of editor screen.
 859  *
 860  * @param w Widget object (the editor)
 861  * @param msg mouse event message
 862  * @param event mouse event data
 863  */
 864 static void
 865 edit_dialog_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 866 {
 867     gboolean unhandled = TRUE;
 868 
 869     if (msg == MSG_MOUSE_DOWN && event->y == 0)
 870     {
 871         WGroup *g = GROUP (w);
 872         WDialog *h = DIALOG (w);
 873         WMenuBar *b;
 874 
 875         b = menubar_find (h);
 876 
 877         if (!widget_get_state (WIDGET (b), WST_FOCUSED))
 878         {
 879             /* menubar */
 880 
 881             GList *l;
 882             GList *top = NULL;
 883             int x;
 884 
 885             /* Try find top fullscreen window */
 886             for (l = g->widgets; l != NULL; l = g_list_next (l))
 887                 if (edit_widget_is_editor (CONST_WIDGET (l->data)) && EDIT (l->data)->fullscreen)
 888                     top = l;
 889 
 890             /* Handle fullscreen/close buttons in the top line */
 891             x = w->rect.cols - 6;
 892 
 893             if (top != NULL && event->x >= x)
 894             {
 895                 WEdit *e = EDIT (top->data);
 896 
 897                 if (top != g->current)
 898                 {
 899                     /* Window is not active. Activate it */
 900                     widget_select (WIDGET (e));
 901                 }
 902 
 903                 /* Handle buttons */
 904                 if (event->x - x <= 2)
 905                     edit_toggle_fullscreen (e);
 906                 else
 907                     send_message (h, NULL, MSG_ACTION, CK_Close, NULL);
 908 
 909                 unhandled = FALSE;
 910             }
 911 
 912             if (unhandled)
 913                 menubar_activate (b, drop_menus, -1);
 914         }
 915     }
 916 
 917     /* Continue handling of unhandled event in window or menu */
 918     event->result.abort = unhandled;
 919 }
 920 
 921 /* --------------------------------------------------------------------------------------------- */
 922 
 923 static cb_ret_t
 924 edit_dialog_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 925 {
 926     switch (msg)
 927     {
 928     case MSG_INIT:
 929         w->rect = WIDGET (w->owner)->rect;
 930         rect_grow (&w->rect, -1, 0);
 931         w->pos_flags |= WPOS_KEEP_ALL;
 932         return MSG_HANDLED;
 933 
 934     default:
 935         return background_callback (w, sender, msg, parm, data);
 936     }
 937 }
 938 
 939 /* --------------------------------------------------------------------------------------------- */
 940 
 941 static cb_ret_t
 942 edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 943 {
 944     WEdit *e = EDIT (w);
 945 
 946     switch (msg)
 947     {
 948     case MSG_FOCUS:
 949         edit_set_buttonbar (e, buttonbar_find (DIALOG (w->owner)));
 950         return MSG_HANDLED;
 951 
 952     case MSG_DRAW:
 953         e->force |= REDRAW_COMPLETELY;
 954         edit_update_screen (e);
 955         return MSG_HANDLED;
 956 
 957     case MSG_KEY:
 958         {
 959             int cmd, ch;
 960             cb_ret_t ret = MSG_NOT_HANDLED;
 961 
 962             /* The user may override the access-keys for the menu bar. */
 963             if (macro_index == -1 && edit_execute_macro (e, parm))
 964             {
 965                 edit_update_screen (e);
 966                 ret = MSG_HANDLED;
 967             }
 968             else if (edit_translate_key (e, parm, &cmd, &ch))
 969             {
 970                 edit_execute_key_command (e, cmd, ch);
 971                 edit_update_screen (e);
 972                 ret = MSG_HANDLED;
 973             }
 974 
 975             return ret;
 976         }
 977 
 978     case MSG_ACTION:
 979         /* command from menubar or buttonbar */
 980         edit_execute_key_command (e, parm, -1);
 981         edit_update_screen (e);
 982         return MSG_HANDLED;
 983 
 984     case MSG_CURSOR:
 985         {
 986             int y, x;
 987 
 988             y = (e->fullscreen ? 0 : 1) + EDIT_TEXT_VERTICAL_OFFSET + e->curs_row;
 989             x = (e->fullscreen ? 0 : 1) + EDIT_TEXT_HORIZONTAL_OFFSET +
 990                 edit_options.line_state_width + e->curs_col + e->start_col + e->over_col;
 991 
 992             widget_gotoyx (w, y, x);
 993             return MSG_HANDLED;
 994         }
 995 
 996     case MSG_IDLE:
 997         edit_update_screen (e);
 998         return MSG_HANDLED;
 999 
1000     case MSG_DESTROY:
1001         edit_clean (e);
1002         return MSG_HANDLED;
1003 
1004     default:
1005         return widget_default_callback (w, sender, msg, parm, data);
1006     }
1007 }
1008 
1009 /* --------------------------------------------------------------------------------------------- */
1010 
1011 /**
1012  * Handle move/resize mouse events.
1013  */
1014 static void
1015 edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
1016 {
1017     WEdit *edit = EDIT (w);
1018     WRect *r = &w->rect;
1019     const WRect *h = &CONST_WIDGET (w->owner)->rect;
1020     int global_x, global_y;
1021 
1022     if (msg == MSG_MOUSE_UP)
1023     {
1024         /* Exit move/resize mode. */
1025         edit_execute_cmd (edit, CK_Enter, -1);
1026         edit_update_screen (edit);      /* Paint the buttonbar over our possibly overlapping frame. */
1027         return;
1028     }
1029 
1030     if (msg != MSG_MOUSE_DRAG)
1031         /**
1032          * We ignore any other events. Specifically, MSG_MOUSE_DOWN.
1033          *
1034          * When the move/resize is initiated by the menu, we let the user
1035          * stop it by clicking with the mouse. Which is why we don't want
1036          * a mouse down to affect the window.
1037          */
1038         return;
1039 
1040     /* Convert point to global coordinates for easier calculations. */
1041     global_x = event->x + r->x;
1042     global_y = event->y + r->y;
1043 
1044     /* Clamp the point to the dialog's client area. */
1045     global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */
1046     global_x = CLAMP (global_x, h->x, h->x + h->cols - 1);      /* Currently a no-op, as the dialog has no left/right margins. */
1047 
1048     if (edit->drag_state == MCEDIT_DRAG_MOVE)
1049     {
1050         r->y = global_y;
1051         r->x = global_x - edit->drag_state_start;
1052     }
1053     else if (edit->drag_state == MCEDIT_DRAG_RESIZE)
1054     {
1055         r->lines = MAX (WINDOW_MIN_LINES, global_y - r->y + 1);
1056         r->cols = MAX (WINDOW_MIN_COLS, global_x - r->x + 1);
1057     }
1058 
1059     edit->force |= REDRAW_COMPLETELY;   /* Not really needed as WEdit's MSG_DRAW already does this. */
1060 
1061     /* We draw the whole dialog because dragging/resizing exposes area beneath. */
1062     widget_draw (WIDGET (w->owner));
1063 }
1064 
1065 /* --------------------------------------------------------------------------------------------- */
1066 
1067 /**
1068  * Handle mouse events of editor window
1069  *
1070  * @param w Widget object (the editor window)
1071  * @param msg mouse event message
1072  * @param event mouse event data
1073  */
1074 static void
1075 edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
1076 {
1077     WEdit *edit = EDIT (w);
1078     /* buttons' distance from right edge */
1079     int dx = edit->fullscreen ? 0 : 2;
1080     /* location of 'Close' and 'Toggle fullscreen' pictograms */
1081     int close_x, toggle_fullscreen_x;
1082 
1083     close_x = (w->rect.cols - 1) - dx - 1;
1084     toggle_fullscreen_x = close_x - 3;
1085 
1086     if (edit->drag_state != MCEDIT_DRAG_NONE)
1087     {
1088         /* window is being resized/moved */
1089         edit_mouse_handle_move_resize (w, msg, event);
1090         return;
1091     }
1092 
1093     /* If it's the last line on the screen, we abort the event to make the
1094      * system channel it to the overlapping buttonbar instead. We have to do
1095      * this because a WEdit has the WOP_TOP_SELECT flag, which makes it above
1096      * the buttonbar in Z-order. */
1097     if (msg == MSG_MOUSE_DOWN && (event->y + w->rect.y == LINES - 1))
1098     {
1099         event->result.abort = TRUE;
1100         return;
1101     }
1102 
1103     switch (msg)
1104     {
1105     case MSG_MOUSE_DOWN:
1106         widget_select (w);
1107         edit_update_curs_row (edit);
1108         edit_update_curs_col (edit);
1109 
1110         if (!edit->fullscreen)
1111         {
1112             if (event->y == 0)
1113             {
1114                 if (event->x >= close_x - 1 && event->x <= close_x + 1)
1115                     ;           /* do nothing (see MSG_MOUSE_CLICK) */
1116                 else if (event->x >= toggle_fullscreen_x - 1 && event->x <= toggle_fullscreen_x + 1)
1117                     ;           /* do nothing (see MSG_MOUSE_CLICK) */
1118                 else
1119                 {
1120                     /* start window move */
1121                     edit_execute_cmd (edit, CK_WindowMove, -1);
1122                     edit_update_screen (edit);  /* Paint the buttonbar over our possibly overlapping frame. */
1123                     edit->drag_state_start = event->x;
1124                 }
1125                 break;
1126             }
1127 
1128             if (event->y == w->rect.lines - 1 && event->x == w->rect.cols - 1)
1129             {
1130                 /* bottom-right corner -- start window resize */
1131                 edit_execute_cmd (edit, CK_WindowResize, -1);
1132                 break;
1133             }
1134         }
1135 
1136         MC_FALLTHROUGH;         /* to start/stop text selection */
1137 
1138     case MSG_MOUSE_UP:
1139         edit_update_cursor (edit, event);
1140         edit_total_update (edit);
1141         break;
1142 
1143     case MSG_MOUSE_CLICK:
1144         if (event->y == 0)
1145         {
1146             if (event->x >= close_x - 1 && event->x <= close_x + 1)
1147                 send_message (w->owner, NULL, MSG_ACTION, CK_Close, NULL);
1148             else if (event->x >= toggle_fullscreen_x - 1 && event->x <= toggle_fullscreen_x + 1)
1149                 edit_toggle_fullscreen (edit);
1150             else if (!edit->fullscreen && event->count == GPM_DOUBLE)
1151                 /* double click on top line (toggle fullscreen) */
1152                 edit_toggle_fullscreen (edit);
1153         }
1154         else if (event->count == GPM_DOUBLE)
1155         {
1156             /* double click */
1157             edit_mark_current_word_cmd (edit);
1158             edit_total_update (edit);
1159         }
1160         else if (event->count == GPM_TRIPLE)
1161         {
1162             /* triple click: works in GPM only, not in xterm */
1163             edit_mark_current_line_cmd (edit);
1164             edit_total_update (edit);
1165         }
1166         break;
1167 
1168     case MSG_MOUSE_DRAG:
1169         edit_update_cursor (edit, event);
1170         edit_total_update (edit);
1171         break;
1172 
1173     case MSG_MOUSE_SCROLL_UP:
1174         edit_move_up (edit, 2, TRUE);
1175         edit_total_update (edit);
1176         break;
1177 
1178     case MSG_MOUSE_SCROLL_DOWN:
1179         edit_move_down (edit, 2, TRUE);
1180         edit_total_update (edit);
1181         break;
1182 
1183     default:
1184         break;
1185     }
1186 }
1187 
1188 /* --------------------------------------------------------------------------------------------- */
1189 /*** public functions ****************************************************************************/
1190 /* --------------------------------------------------------------------------------------------- */
1191 /**
1192  * Edit one file.
1193  *
1194  * @param file_vpath file object
1195  * @param line       line number
1196  * @return TRUE if no errors was occurred, FALSE otherwise
1197  */
1198 
1199 gboolean
1200 edit_file (const vfs_path_t * file_vpath, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
1201 {
1202     mcedit_arg_t arg = { (vfs_path_t *) file_vpath, line };
1203     GList *files;
1204     gboolean ok;
1205 
1206     files = g_list_prepend (NULL, &arg);
1207     ok = edit_files (files);
1208     g_list_free (files);
1209 
1210     return ok;
1211 }
1212 
1213 /* --------------------------------------------------------------------------------------------- */
1214 
1215 gboolean
1216 edit_files (const GList * files)
     /* [previous][next][first][last][top][bottom][index][help]  */
1217 {
1218     static gboolean made_directory = FALSE;
1219     WDialog *edit_dlg;
1220     WGroup *g;
1221     WMenuBar *menubar;
1222     Widget *w, *wd;
1223     const GList *file;
1224     gboolean ok = FALSE;
1225 
1226     if (!made_directory)
1227     {
1228         char *dir;
1229 
1230         dir = mc_build_filename (mc_config_get_cache_path (), EDIT_HOME_DIR, (char *) NULL);
1231         made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1232         g_free (dir);
1233 
1234         dir = mc_build_filename (mc_config_get_path (), EDIT_HOME_DIR, (char *) NULL);
1235         made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1236         g_free (dir);
1237 
1238         dir = mc_build_filename (mc_config_get_data_path (), EDIT_HOME_DIR, (char *) NULL);
1239         made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1240         g_free (dir);
1241     }
1242 
1243     /* Create a new dialog and add it widgets to it */
1244     edit_dlg =
1245         dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, edit_dialog_callback,
1246                     edit_dialog_mouse_callback, "[Internal File Editor]", NULL);
1247     wd = WIDGET (edit_dlg);
1248     widget_want_tab (wd, TRUE);
1249     wd->keymap = editor_map;
1250     wd->ext_keymap = editor_x_map;
1251 
1252     edit_dlg->get_shortcut = edit_get_shortcut;
1253     edit_dlg->get_title = edit_get_title;
1254 
1255     g = GROUP (edit_dlg);
1256 
1257     edit_dlg->bg =
1258         WIDGET (background_new
1259                 (1, 0, wd->rect.lines - 2, wd->rect.cols, EDITOR_BACKGROUND, ' ',
1260                  edit_dialog_bg_callback));
1261     group_add_widget (g, edit_dlg->bg);
1262 
1263     menubar = menubar_new (NULL);
1264     w = WIDGET (menubar);
1265     group_add_widget_autopos (g, w, w->pos_flags, NULL);
1266     edit_init_menu (menubar);
1267 
1268     w = WIDGET (buttonbar_new ());
1269     group_add_widget_autopos (g, w, w->pos_flags, NULL);
1270 
1271     for (file = files; file != NULL; file = g_list_next (file))
1272     {
1273         mcedit_arg_t *f = (mcedit_arg_t *) file->data;
1274         gboolean f_ok;
1275 
1276         f_ok = edit_load_file_from_filename (edit_dlg, f->file_vpath, f->line_number);
1277         /* at least one file has been opened succefully */
1278         ok = ok || f_ok;
1279     }
1280 
1281     if (ok)
1282         dlg_run (edit_dlg);
1283 
1284     if (!ok || widget_get_state (wd, WST_CLOSED))
1285         widget_destroy (wd);
1286 
1287     return ok;
1288 }
1289 
1290 /* --------------------------------------------------------------------------------------------- */
1291 
1292 const char *
1293 edit_get_file_name (const WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1294 {
1295     return vfs_path_as_str (edit->filename_vpath);
1296 }
1297 
1298 /* --------------------------------------------------------------------------------------------- */
1299 
1300 WEdit *
1301 edit_find_editor (const WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1302 {
1303     const WGroup *g = CONST_GROUP (h);
1304 
1305     if (edit_widget_is_editor (CONST_WIDGET (g->current->data)))
1306         return EDIT (g->current->data);
1307     return EDIT (widget_find_by_type (CONST_WIDGET (h), edit_callback));
1308 }
1309 
1310 /* --------------------------------------------------------------------------------------------- */
1311 /**
1312  * Check if widget is an WEdit class.
1313  *
1314  * @param w probably editor object
1315  * @return TRUE if widget is an WEdit class, FALSE otherwise
1316  */
1317 
1318 gboolean
1319 edit_widget_is_editor (const Widget * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
1320 {
1321     return (w != NULL && w->callback == edit_callback);
1322 }
1323 
1324 /* --------------------------------------------------------------------------------------------- */
1325 
1326 void
1327 edit_update_screen (WEdit * e)
     /* [previous][next][first][last][top][bottom][index][help]  */
1328 {
1329     edit_scroll_screen_over_cursor (e);
1330     edit_update_curs_col (e);
1331     edit_status (e, widget_get_state (WIDGET (e), WST_FOCUSED));
1332 
1333     /* pop all events for this window for internal handling */
1334     if (!is_idle ())
1335         e->force |= REDRAW_PAGE;
1336     else
1337     {
1338         if ((e->force & REDRAW_COMPLETELY) != 0)
1339             e->force |= REDRAW_PAGE;
1340         edit_render_keypress (e);
1341     }
1342 
1343     widget_draw (WIDGET (buttonbar_find (DIALOG (WIDGET (e)->owner))));
1344 }
1345 
1346 /* --------------------------------------------------------------------------------------------- */
1347 /**
1348  * Save current window size.
1349  *
1350  * @param edit editor object
1351  */
1352 
1353 void
1354 edit_save_size (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1355 {
1356     edit->loc_prev = WIDGET (edit)->rect;
1357 }
1358 
1359 /* --------------------------------------------------------------------------------------------- */
1360 /**
1361  * Create new editor window and insert it into editor screen.
1362  *
1363  * @param h     editor dialog (screen)
1364  * @param y     y coordinate
1365  * @param x     x coordinate
1366  * @param lines window height
1367  * @param cols  window width
1368  * @param f     file object
1369  * @param fline line number in file
1370  * @return TRUE if new window was successfully created and inserted into editor screen,
1371  *         FALSE otherwise
1372  */
1373 
1374 gboolean
1375 edit_add_window (WDialog * h, const WRect * r, const vfs_path_t * f, long fline)
     /* [previous][next][first][last][top][bottom][index][help]  */
1376 {
1377     WEdit *edit;
1378     Widget *w;
1379 
1380     edit = edit_init (NULL, r, f, fline);
1381     if (edit == NULL)
1382         return FALSE;
1383 
1384     w = WIDGET (edit);
1385     w->callback = edit_callback;
1386     w->mouse_callback = edit_mouse_callback;
1387 
1388     group_add_widget_autopos (GROUP (h), w, WPOS_KEEP_ALL, NULL);
1389     edit_set_buttonbar (edit, buttonbar_find (h));
1390     widget_draw (WIDGET (h));
1391 
1392     return TRUE;
1393 }
1394 
1395 /* --------------------------------------------------------------------------------------------- */
1396 /**
1397  * Handle move/resize events.
1398  *
1399  * @param edit    editor object
1400  * @param command action id
1401  * @return TRUE if the action was handled, FALSE otherwise
1402  */
1403 
1404 gboolean
1405 edit_handle_move_resize (WEdit * edit, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
1406 {
1407     Widget *w = WIDGET (edit);
1408     gboolean ret = FALSE;
1409 
1410     if (edit->fullscreen)
1411     {
1412         edit->drag_state = MCEDIT_DRAG_NONE;
1413         w->mouse.forced_capture = FALSE;
1414         return ret;
1415     }
1416 
1417     switch (edit->drag_state)
1418     {
1419     case MCEDIT_DRAG_NONE:
1420         /* possible start move/resize */
1421         switch (command)
1422         {
1423         case CK_WindowMove:
1424             edit->drag_state = MCEDIT_DRAG_MOVE;
1425             edit_save_size (edit);
1426             edit_status (edit, TRUE);   /* redraw frame and status */
1427             /**
1428              * If a user initiates a move by the menu, not by the mouse, we
1429              * make a subsequent mouse drag pull the frame from its middle.
1430              * (We can instead choose '0' to pull it from the corner.)
1431              */
1432             edit->drag_state_start = w->rect.cols / 2;
1433             ret = TRUE;
1434             break;
1435         case CK_WindowResize:
1436             edit->drag_state = MCEDIT_DRAG_RESIZE;
1437             edit_save_size (edit);
1438             edit_status (edit, TRUE);   /* redraw frame and status */
1439             ret = TRUE;
1440             break;
1441         default:
1442             break;
1443         }
1444         break;
1445 
1446     case MCEDIT_DRAG_MOVE:
1447         switch (command)
1448         {
1449         case CK_WindowResize:
1450             edit->drag_state = MCEDIT_DRAG_RESIZE;
1451             ret = TRUE;
1452             break;
1453         case CK_Up:
1454         case CK_Down:
1455         case CK_Left:
1456         case CK_Right:
1457             edit_window_move (edit, command);
1458             ret = TRUE;
1459             break;
1460         case CK_Enter:
1461         case CK_WindowMove:
1462             edit->drag_state = MCEDIT_DRAG_NONE;
1463             edit_status (edit, TRUE);   /* redraw frame and status */
1464             MC_FALLTHROUGH;
1465         default:
1466             ret = TRUE;
1467             break;
1468         }
1469         break;
1470 
1471     case MCEDIT_DRAG_RESIZE:
1472         switch (command)
1473         {
1474         case CK_WindowMove:
1475             edit->drag_state = MCEDIT_DRAG_MOVE;
1476             ret = TRUE;
1477             break;
1478         case CK_Up:
1479         case CK_Down:
1480         case CK_Left:
1481         case CK_Right:
1482             edit_window_resize (edit, command);
1483             ret = TRUE;
1484             break;
1485         case CK_Enter:
1486         case CK_WindowResize:
1487             edit->drag_state = MCEDIT_DRAG_NONE;
1488             edit_status (edit, TRUE);   /* redraw frame and status */
1489             MC_FALLTHROUGH;
1490         default:
1491             ret = TRUE;
1492             break;
1493         }
1494         break;
1495 
1496     default:
1497         break;
1498     }
1499 
1500     /**
1501      * - We let the user stop a resize/move operation by clicking with the
1502      *   mouse anywhere. ("clicking" = pressing and releasing a button.)
1503      * - We let the user perform a resize/move operation by a mouse drag
1504      *   initiated anywhere.
1505      *
1506      * "Anywhere" means: inside or outside the window. We make this happen
1507      * with the 'forced_capture' flag.
1508      */
1509     w->mouse.forced_capture = (edit->drag_state != MCEDIT_DRAG_NONE);
1510 
1511     return ret;
1512 }
1513 
1514 /* --------------------------------------------------------------------------------------------- */
1515 /**
1516  * Toggle window fuulscreen mode.
1517  *
1518  * @param edit editor object
1519  */
1520 
1521 void
1522 edit_toggle_fullscreen (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1523 {
1524     Widget *w = WIDGET (edit);
1525 
1526     edit->fullscreen = !edit->fullscreen;
1527     edit->force = REDRAW_COMPLETELY;
1528 
1529     if (!edit->fullscreen)
1530     {
1531         edit_restore_size (edit);
1532         /* do not follow screen size on resize */
1533         w->pos_flags = WPOS_KEEP_DEFAULT;
1534     }
1535     else
1536     {
1537         WRect r;
1538 
1539         edit_save_size (edit);
1540         r = WIDGET (w->owner)->rect;
1541         rect_grow (&r, -1, 0);
1542         widget_set_size_rect (w, &r);
1543         /* follow screen size on resize */
1544         w->pos_flags = WPOS_KEEP_ALL;
1545         edit->force |= REDRAW_PAGE;
1546         edit_update_screen (edit);
1547     }
1548 }
1549 
1550 /* --------------------------------------------------------------------------------------------- */

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