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

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