Manual pages: mcmcdiffmceditmcview

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-2025
   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 <https://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 #include "lib/charsets.h"
  55 
  56 #include "src/keymap.h"           // keybind_lookup_keymap_command()
  57 #include "src/setup.h"            // home_dir
  58 #include "src/execute.h"          // toggle_subshell()
  59 #include "src/filemanager/cmd.h"  // save_setup_cmd()
  60 #include "src/learn.h"            // learn_keys()
  61 
  62 #include "edit-impl.h"
  63 #include "editwidget.h"
  64 #include "editmacros.h"  // edit_execute_macro()
  65 #ifdef HAVE_ASPELL
  66 #include "spell.h"
  67 #endif
  68 
  69 /*** global variables ****************************************************************************/
  70 
  71 char *edit_window_state_char = NULL;
  72 char *edit_window_close_char = NULL;
  73 
  74 /*** file scope macro definitions ****************************************************************/
  75 
  76 #define WINDOW_MIN_LINES (2 + 2)
  77 #define WINDOW_MIN_COLS  (2 + LINE_STATE_WIDTH + 2)
  78 
  79 /*** file scope type declarations ****************************************************************/
  80 
  81 /*** forward declarations (file scope functions) *************************************************/
  82 
  83 /*** file scope variables ************************************************************************/
  84 
  85 static unsigned int edit_dlg_init_refcounter = 0;
  86 
  87 /* --------------------------------------------------------------------------------------------- */
  88 /*** file scope functions ************************************************************************/
  89 /* --------------------------------------------------------------------------------------------- */
  90 /**
  91  * Init the 'edit' subsystem
  92  */
  93 
  94 static void
  95 edit_dlg_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  96 {
  97     edit_dlg_init_refcounter++;
  98 
  99     if (edit_dlg_init_refcounter == 1)
 100     {
 101         edit_window_state_char = mc_skin_get ("widget-editor", "window-state-char", "*");
 102         edit_window_close_char = mc_skin_get ("widget-editor", "window-close-char", "X");
 103 
 104 #ifdef HAVE_ASPELL
 105         aspell_init ();
 106 #endif
 107     }
 108 }
 109 
 110 /* --------------------------------------------------------------------------------------------- */
 111 /**
 112  * Deinit the 'edit' subsystem
 113  */
 114 
 115 static void
 116 edit_dlg_deinit (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 117 {
 118     if (edit_dlg_init_refcounter == 1)
 119     {
 120         g_free (edit_window_state_char);
 121         g_free (edit_window_close_char);
 122 
 123 #ifdef HAVE_ASPELL
 124         aspell_clean ();
 125 #endif
 126     }
 127 
 128     if (edit_dlg_init_refcounter != 0)
 129         edit_dlg_init_refcounter--;
 130 }
 131 
 132 /* --------------------------------------------------------------------------------------------- */
 133 /**
 134  * Show info about editor
 135  */
 136 
 137 static void
 138 edit_about (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 139 {
 140     char *ver;
 141 
 142     ver = g_strdup_printf ("MCEdit %s", mc_global.mc_version);
 143 
 144     {
 145         quick_widget_t quick_widgets[] = {
 146             QUICK_LABEL (ver, NULL),
 147             QUICK_SEPARATOR (TRUE),
 148             QUICK_LABEL (N_ ("A user friendly text editor\n"
 149                              "written for the Midnight Commander."),
 150                          NULL),
 151             QUICK_SEPARATOR (FALSE),
 152             QUICK_LABEL (N_ ("Copyright (C) 1996-2025 the Free Software Foundation"), NULL),
 153             QUICK_START_BUTTONS (TRUE, TRUE),
 154             QUICK_BUTTON (N_ ("&OK"), B_ENTER, NULL, NULL),
 155             QUICK_END,
 156         };
 157 
 158         WRect r = { -1, -1, 0, 40 };
 159 
 160         quick_dialog_t qdlg = {
 161             .rect = r,
 162             .title = N_ ("About"),
 163             .help = "[Internal File Editor]",
 164             .widgets = quick_widgets,
 165             .callback = NULL,
 166             .mouse_callback = 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 != 0 ? '*' : ' ', _ ("NoName"));
 335             else
 336                 fname = g_strdup_printf ("%c%s", e->modified != 0 ? '*' : ' ',
 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 != 0 ? "(*) " : "    ";
 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         int c;
 518 
 519         if (edit->charpoint >= MB_LEN_MAX)
 520         {
 521             edit->charpoint = 0;
 522             edit->charbuf[edit->charpoint] = '\0';
 523         }
 524         if (edit->charpoint < MB_LEN_MAX)
 525         {
 526             edit->charbuf[edit->charpoint++] = x_key;
 527             edit->charbuf[edit->charpoint] = '\0';
 528         }
 529 
 530         // input from 8-bit locale
 531         if (!mc_global.utf8_display)
 532         {
 533             // source is in 8-bit codeset
 534             c = convert_from_input_c (x_key);
 535 
 536             if (is_printable (c))
 537             {
 538                 if (!edit->utf8)
 539                     char_for_insertion = c;
 540                 else
 541                     char_for_insertion = convert_from_8bit_to_utf_c2 ((char) x_key);
 542                 goto fin;
 543             }
 544         }
 545         else
 546         {
 547             // UTF-8 locale
 548             int res;
 549 
 550             res = str_is_valid_char (edit->charbuf, edit->charpoint);
 551             if (res < 0 && res != -2)
 552             {
 553                 edit->charpoint = 0;  // broken multibyte char, skip
 554                 goto fin;
 555             }
 556 
 557             if (edit->utf8)
 558             {
 559                 // source is in UTF-8 codeset
 560                 if (res < 0)
 561                 {
 562                     char_for_insertion = x_key;
 563                     goto fin;
 564                 }
 565 
 566                 edit->charbuf[edit->charpoint] = '\0';
 567                 edit->charpoint = 0;
 568                 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
 569                 {
 570                     char_for_insertion = x_key;
 571                     goto fin;
 572                 }
 573             }
 574             else
 575             {
 576                 // 8-bit source
 577                 if (res < 0)
 578                 {
 579                     // not finished multibyte input (we're in the middle of multibyte utf-8 char)
 580                     goto fin;
 581                 }
 582 
 583                 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
 584                 {
 585                     c = convert_from_utf_to_current (edit->charbuf);
 586                     edit->charbuf[0] = '\0';
 587                     edit->charpoint = 0;
 588                     char_for_insertion = c;
 589                     goto fin;
 590                 }
 591 
 592                 // non-printable utf-8 input, skip it
 593                 edit->charbuf[0] = '\0';
 594                 edit->charpoint = 0;
 595             }
 596         }
 597     }
 598 
 599     // Commands specific to the key emulation
 600     command = widget_lookup_key (w, x_key);
 601     if (command == CK_IgnoreKey)
 602         command = CK_InsertChar;
 603 
 604 fin:
 605     *cmd = (int) command;  // FIXME
 606     *ch = char_for_insertion;
 607 
 608     return !(command == CK_InsertChar && char_for_insertion == -1);
 609 }
 610 
 611 /* --------------------------------------------------------------------------------------------- */
 612 
 613 static inline void
 614 edit_quit (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 615 {
 616     GList *l;
 617     WEdit *e = NULL;
 618     GSList *m = NULL;
 619     GSList *me;
 620 
 621     // don't stop the dialog before final decision
 622     widget_set_state (WIDGET (h), WST_ACTIVE, TRUE);
 623 
 624     // check window state and get modified files
 625     for (l = GROUP (h)->widgets; l != NULL; l = g_list_next (l))
 626         if (edit_widget_is_editor (CONST_WIDGET (l->data)))
 627         {
 628             e = EDIT (l->data);
 629 
 630             if (e->drag_state != MCEDIT_DRAG_NONE)
 631             {
 632                 edit_restore_size (e);
 633                 g_slist_free (m);
 634                 return;
 635             }
 636 
 637             /* create separate list because widget_select()
 638                changes the window position in Z order */
 639             if (e->modified != 0)
 640                 m = g_slist_prepend (m, l->data);
 641         }
 642 
 643     for (me = m; me != NULL; me = g_slist_next (me))
 644     {
 645         e = EDIT (me->data);
 646 
 647         widget_select (WIDGET (e));
 648 
 649         if (!edit_ok_to_exit (e))
 650             break;
 651     }
 652 
 653     // if all files were checked, quit editor
 654     if (me == NULL)
 655         dlg_close (h);
 656 
 657     g_slist_free (m);
 658 }
 659 
 660 /* --------------------------------------------------------------------------------------------- */
 661 
 662 static inline void
 663 edit_set_buttonbar (WEdit *edit, WButtonBar *bb)
     /* [previous][next][first][last][top][bottom][index][help]  */
 664 {
 665     Widget *w = WIDGET (edit);
 666 
 667     buttonbar_set_label (bb, 1, Q_ ("ButtonBar|Help"), w->keymap, NULL);
 668     buttonbar_set_label (bb, 2, Q_ ("ButtonBar|Save"), w->keymap, w);
 669     buttonbar_set_label (bb, 3, Q_ ("ButtonBar|Mark"), w->keymap, w);
 670     buttonbar_set_label (bb, 4, Q_ ("ButtonBar|Replac"), w->keymap, w);
 671     buttonbar_set_label (bb, 5, Q_ ("ButtonBar|Copy"), w->keymap, w);
 672     buttonbar_set_label (bb, 6, Q_ ("ButtonBar|Move"), w->keymap, w);
 673     buttonbar_set_label (bb, 7, Q_ ("ButtonBar|Search"), w->keymap, w);
 674     buttonbar_set_label (bb, 8, Q_ ("ButtonBar|Delete"), w->keymap, w);
 675     buttonbar_set_label (bb, 9, Q_ ("ButtonBar|PullDn"), w->keymap, NULL);
 676     buttonbar_set_label (bb, 10, Q_ ("ButtonBar|Quit"), w->keymap, NULL);
 677 }
 678 
 679 /* --------------------------------------------------------------------------------------------- */
 680 
 681 static void
 682 edit_total_update (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 683 {
 684     edit_find_bracket (edit);
 685     edit->force |= REDRAW_COMPLETELY;
 686     edit_update_curs_row (edit);
 687     edit_update_screen (edit);
 688 }
 689 
 690 /* --------------------------------------------------------------------------------------------- */
 691 
 692 static gboolean
 693 edit_update_cursor (WEdit *edit, const mouse_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 694 {
 695     int x, y;
 696     gboolean done;
 697 
 698     x = event->x - (edit->fullscreen != 0 ? 0 : 1);
 699     y = event->y - (edit->fullscreen != 0 ? 0 : 1);
 700 
 701     if (edit->mark2 != -1 && event->msg == MSG_MOUSE_UP)
 702         return TRUE;  // don't do anything
 703 
 704     if (event->msg == MSG_MOUSE_DOWN || event->msg == MSG_MOUSE_UP)
 705         edit_push_key_press (edit);
 706 
 707     if (!edit_options.cursor_beyond_eol)
 708         edit->prev_col = x - edit->start_col - edit_options.line_state_width;
 709     else
 710     {
 711         long line_len;
 712 
 713         line_len = edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), 0,
 714                                        edit_buffer_get_current_eol (&edit->buffer));
 715 
 716         if (x > line_len - 1)
 717         {
 718             edit->over_col = x - line_len - edit->start_col - edit_options.line_state_width;
 719             edit->prev_col = line_len;
 720         }
 721         else
 722         {
 723             edit->over_col = 0;
 724             edit->prev_col = x - edit_options.line_state_width - edit->start_col;
 725         }
 726     }
 727 
 728     if (y > edit->curs_row)
 729         edit_move_down (edit, y - edit->curs_row, FALSE);
 730     else if (y < edit->curs_row)
 731         edit_move_up (edit, edit->curs_row - y, FALSE);
 732     else
 733         edit_move_to_prev_col (edit, edit_buffer_get_current_bol (&edit->buffer));
 734 
 735     if (event->msg == MSG_MOUSE_CLICK)
 736     {
 737         edit_mark_cmd (edit, TRUE);  // reset
 738         edit->highlight = 0;
 739     }
 740 
 741     done = (event->msg != MSG_MOUSE_DRAG);
 742     if (done)
 743         edit_mark_cmd (edit, FALSE);
 744 
 745     return done;
 746 }
 747 
 748 /* --------------------------------------------------------------------------------------------- */
 749 /** Callback for the edit dialog */
 750 
 751 static cb_ret_t
 752 edit_dialog_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 753 {
 754     WGroup *g = GROUP (w);
 755     WDialog *h = DIALOG (w);
 756 
 757     switch (msg)
 758     {
 759     case MSG_INIT:
 760         edit_dlg_init ();
 761         return MSG_HANDLED;
 762 
 763     case MSG_RESIZE:
 764         dlg_default_callback (w, NULL, MSG_RESIZE, 0, NULL);
 765         menubar_arrange (menubar_find (h));
 766         return MSG_HANDLED;
 767 
 768     case MSG_ACTION:
 769     {
 770         // Handle shortcuts, menu, and buttonbar.
 771 
 772         cb_ret_t result;
 773 
 774         result = edit_dialog_command_execute (h, parm);
 775 
 776         /* We forward any commands coming from the menu, and which haven't been
 777            handled by the dialog, to the focused WEdit window. */
 778         if (result == MSG_NOT_HANDLED && sender == WIDGET (menubar_find (h)))
 779             result = send_message (g->current->data, NULL, MSG_ACTION, parm, NULL);
 780 
 781         return result;
 782     }
 783 
 784     case MSG_KEY:
 785     {
 786         Widget *we = WIDGET (g->current->data);
 787         cb_ret_t ret = MSG_NOT_HANDLED;
 788 
 789         if (edit_widget_is_editor (we))
 790         {
 791             gboolean ext_mode;
 792             long command;
 793 
 794             // keep and then extmod flag
 795             ext_mode = we->ext_mode;
 796             command = widget_lookup_key (we, parm);
 797             we->ext_mode = ext_mode;
 798 
 799             if (command == CK_IgnoreKey)
 800                 we->ext_mode = FALSE;
 801             else
 802             {
 803                 ret = edit_dialog_command_execute (h, command);
 804                 /* if command was not handled, keep the extended mode
 805                    for the further key processing */
 806                 if (ret == MSG_HANDLED)
 807                     we->ext_mode = FALSE;
 808             }
 809         }
 810 
 811         /*
 812          * Due to the "end of bracket" escape the editor sees input with is_idle() == false
 813          * (expects more characters) and hence doesn't yet refresh the screen, but then
 814          * no further characters arrive (there's only an "end of bracket" which is swallowed
 815          * by tty_get_event()), so you end up with a screen that's not refreshed after pasting.
 816          * So let's trigger an IDLE signal.
 817          */
 818         if (!is_idle ())
 819             widget_idle (w, TRUE);
 820         return ret;
 821     }
 822 
 823         // hardcoded menu hotkeys (see edit_drop_hotkey_menu)
 824     case MSG_UNHANDLED_KEY:
 825         return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
 826 
 827     case MSG_VALIDATE:
 828         edit_quit (h);
 829         return MSG_HANDLED;
 830 
 831     case MSG_DESTROY:
 832         edit_dlg_deinit ();
 833         return MSG_HANDLED;
 834 
 835     case MSG_IDLE:
 836         widget_idle (w, FALSE);
 837         return send_message (g->current->data, NULL, MSG_IDLE, 0, NULL);
 838 
 839     default:
 840         return dlg_default_callback (w, sender, msg, parm, data);
 841     }
 842 }
 843 
 844 /* --------------------------------------------------------------------------------------------- */
 845 
 846 /**
 847  * Handle mouse events of editor screen.
 848  *
 849  * @param w Widget object (the editor)
 850  * @param msg mouse event message
 851  * @param event mouse event data
 852  */
 853 static void
 854 edit_dialog_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 855 {
 856     gboolean unhandled = TRUE;
 857 
 858     if (msg == MSG_MOUSE_DOWN && event->y == 0)
 859     {
 860         WGroup *g = GROUP (w);
 861         WDialog *h = DIALOG (w);
 862         WMenuBar *b;
 863 
 864         b = menubar_find (h);
 865 
 866         if (!widget_get_state (WIDGET (b), WST_FOCUSED))
 867         {
 868             // menubar
 869 
 870             GList *l;
 871             GList *top = NULL;
 872             int x;
 873 
 874             // Try find top fullscreen window
 875             for (l = g->widgets; l != NULL; l = g_list_next (l))
 876                 if (edit_widget_is_editor (CONST_WIDGET (l->data))
 877                     && EDIT (l->data)->fullscreen != 0)
 878                     top = l;
 879 
 880             // Handle fullscreen/close buttons in the top line
 881             x = w->rect.cols - 6;
 882 
 883             if (top != NULL && event->x >= x)
 884             {
 885                 WEdit *e = EDIT (top->data);
 886 
 887                 if (top != g->current)
 888                 {
 889                     // Window is not active. Activate it
 890                     widget_select (WIDGET (e));
 891                 }
 892 
 893                 // Handle buttons
 894                 if (event->x - x <= 2)
 895                     edit_toggle_fullscreen (e);
 896                 else
 897                     send_message (h, NULL, MSG_ACTION, CK_Close, NULL);
 898 
 899                 unhandled = FALSE;
 900             }
 901 
 902             if (unhandled)
 903                 menubar_activate (b, drop_menus, -1);
 904         }
 905     }
 906 
 907     // Continue handling of unhandled event in window or menu
 908     event->result.abort = unhandled;
 909 }
 910 
 911 /* --------------------------------------------------------------------------------------------- */
 912 
 913 static cb_ret_t
 914 edit_dialog_bg_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 915 {
 916     switch (msg)
 917     {
 918     case MSG_INIT:
 919         w->rect = WIDGET (w->owner)->rect;
 920         rect_grow (&w->rect, -1, 0);
 921         w->pos_flags |= WPOS_KEEP_ALL;
 922         return MSG_HANDLED;
 923 
 924     default:
 925         return background_callback (w, sender, msg, parm, data);
 926     }
 927 }
 928 
 929 /* --------------------------------------------------------------------------------------------- */
 930 
 931 static cb_ret_t
 932 edit_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 933 {
 934     WEdit *e = EDIT (w);
 935 
 936     switch (msg)
 937     {
 938     case MSG_FOCUS:
 939         edit_set_buttonbar (e, buttonbar_find (DIALOG (w->owner)));
 940         return MSG_HANDLED;
 941 
 942     case MSG_DRAW:
 943         e->force |= REDRAW_COMPLETELY;
 944         edit_update_screen (e);
 945         return MSG_HANDLED;
 946 
 947     case MSG_KEY:
 948     {
 949         int cmd, ch;
 950         cb_ret_t ret = MSG_NOT_HANDLED;
 951 
 952         // The user may override the access-keys for the menu bar.
 953         if (macro_index == -1 && !bracketed_pasting_in_progress && edit_execute_macro (e, parm))
 954         {
 955             edit_update_screen (e);
 956             ret = MSG_HANDLED;
 957         }
 958         else if (edit_translate_key (e, parm, &cmd, &ch))
 959         {
 960             edit_execute_key_command (e, cmd, ch);
 961             edit_update_screen (e);
 962             ret = MSG_HANDLED;
 963         }
 964 
 965         return ret;
 966     }
 967 
 968     case MSG_ACTION:
 969         // command from menubar or buttonbar
 970         edit_execute_key_command (e, parm, -1);
 971         edit_update_screen (e);
 972         return MSG_HANDLED;
 973 
 974     case MSG_CURSOR:
 975     {
 976         int y, x;
 977 
 978         y = (e->fullscreen != 0 ? 0 : 1) + EDIT_TEXT_VERTICAL_OFFSET + e->curs_row;
 979         x = (e->fullscreen != 0 ? 0 : 1) + EDIT_TEXT_HORIZONTAL_OFFSET
 980             + edit_options.line_state_width + e->curs_col + e->start_col + e->over_col;
 981 
 982         widget_gotoyx (w, y, x);
 983         return MSG_HANDLED;
 984     }
 985 
 986     case MSG_IDLE:
 987         edit_update_screen (e);
 988         return MSG_HANDLED;
 989 
 990     case MSG_DESTROY:
 991         edit_clean (e);
 992         return MSG_HANDLED;
 993 
 994     default:
 995         return widget_default_callback (w, sender, msg, parm, data);
 996     }
 997 }
 998 
 999 /* --------------------------------------------------------------------------------------------- */
1000 
1001 /**
1002  * Handle move/resize mouse events.
1003  */
1004 static void
1005 edit_mouse_handle_move_resize (Widget *w, mouse_msg_t msg, mouse_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
1006 {
1007     WEdit *edit = EDIT (w);
1008     WRect *r = &w->rect;
1009     const WRect *h = &CONST_WIDGET (w->owner)->rect;
1010     int global_x, global_y;
1011 
1012     if (msg == MSG_MOUSE_UP)
1013     {
1014         // Exit move/resize mode
1015         edit_execute_cmd (edit, CK_Enter, -1);
1016         edit_update_screen (edit);  // Paint the buttonbar over our possibly overlapping frame.
1017         return;
1018     }
1019 
1020     if (msg != MSG_MOUSE_DRAG)
1021         /**
1022          * We ignore any other events. Specifically, MSG_MOUSE_DOWN.
1023          *
1024          * When the move/resize is initiated by the menu, we let the user
1025          * stop it by clicking with the mouse. Which is why we don't want
1026          * a mouse down to affect the window.
1027          */
1028         return;
1029 
1030     // Convert point to global coordinates for easier calculations.
1031     global_x = event->x + r->x;
1032     global_y = event->y + r->y;
1033 
1034     // Clamp the point to the dialog's client area.
1035     global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2);  // Status line, buttonbar
1036     global_x =
1037         CLAMP (global_x, h->x,
1038                h->x + h->cols - 1);  // Currently a no-op, as the dialog has no left/right margins
1039 
1040     if (edit->drag_state == MCEDIT_DRAG_MOVE)
1041     {
1042         r->y = global_y;
1043         r->x = global_x - edit->drag_state_start;
1044     }
1045     else if (edit->drag_state == MCEDIT_DRAG_RESIZE)
1046     {
1047         r->lines = MAX (WINDOW_MIN_LINES, global_y - r->y + 1);
1048         r->cols = MAX (WINDOW_MIN_COLS, global_x - r->x + 1);
1049     }
1050 
1051     edit->force |= REDRAW_COMPLETELY;  // Not really needed as WEdit's MSG_DRAW already does this.
1052 
1053     // We draw the whole dialog because dragging/resizing exposes area beneath
1054     widget_draw (WIDGET (w->owner));
1055 }
1056 
1057 /* --------------------------------------------------------------------------------------------- */
1058 
1059 /**
1060  * Handle mouse events of editor window
1061  *
1062  * @param w Widget object (the editor window)
1063  * @param msg mouse event message
1064  * @param event mouse event data
1065  */
1066 static void
1067 edit_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
1068 {
1069     WEdit *edit = EDIT (w);
1070     // buttons' distance from right edge
1071     int dx = edit->fullscreen != 0 ? 0 : 2;
1072     // location of 'Close' and 'Toggle fullscreen' pictograms
1073     int close_x, toggle_fullscreen_x;
1074 
1075     close_x = (w->rect.cols - 1) - dx - 1;
1076     toggle_fullscreen_x = close_x - 3;
1077 
1078     if (edit->drag_state != MCEDIT_DRAG_NONE)
1079     {
1080         // window is being resized/moved
1081         edit_mouse_handle_move_resize (w, msg, event);
1082         return;
1083     }
1084 
1085     /* If it's the last line on the screen, we abort the event to make the
1086      * system channel it to the overlapping buttonbar instead. We have to do
1087      * this because a WEdit has the WOP_TOP_SELECT flag, which makes it above
1088      * the buttonbar in Z-order. */
1089     if (msg == MSG_MOUSE_DOWN && (event->y + w->rect.y == LINES - 1))
1090     {
1091         event->result.abort = TRUE;
1092         return;
1093     }
1094 
1095     switch (msg)
1096     {
1097     case MSG_MOUSE_DOWN:
1098         widget_select (w);
1099         edit_update_curs_row (edit);
1100         edit_update_curs_col (edit);
1101 
1102         if (edit->fullscreen == 0)
1103         {
1104             if (event->y == 0)
1105             {
1106                 if (event->x >= close_x - 1 && event->x <= close_x + 1)
1107                     ;  // do nothing (see MSG_MOUSE_CLICK)
1108                 else if (event->x >= toggle_fullscreen_x - 1 && event->x <= toggle_fullscreen_x + 1)
1109                     ;  // do nothing (see MSG_MOUSE_CLICK)
1110                 else
1111                 {
1112                     // start window move
1113                     edit_execute_cmd (edit, CK_WindowMove, -1);
1114                     edit_update_screen (
1115                         edit);  // Paint the buttonbar over our possibly overlapping frame.
1116                     edit->drag_state_start = event->x;
1117                 }
1118                 break;
1119             }
1120 
1121             if (event->y == w->rect.lines - 1 && event->x == w->rect.cols - 1)
1122             {
1123                 // bottom-right corner -- start window resize
1124                 edit_execute_cmd (edit, CK_WindowResize, -1);
1125                 break;
1126             }
1127         }
1128 
1129         MC_FALLTHROUGH;  // to start/stop text selection
1130 
1131     case MSG_MOUSE_UP:
1132         edit_update_cursor (edit, event);
1133         edit_total_update (edit);
1134         break;
1135 
1136     case MSG_MOUSE_CLICK:
1137         if (event->y == 0)
1138         {
1139             if (event->x >= close_x - 1 && event->x <= close_x + 1)
1140                 send_message (w->owner, NULL, MSG_ACTION, CK_Close, NULL);
1141             else if (event->x >= toggle_fullscreen_x - 1 && event->x <= toggle_fullscreen_x + 1)
1142                 edit_toggle_fullscreen (edit);
1143             else if (edit->fullscreen == 0 && event->count == GPM_DOUBLE)
1144                 // double click on top line (toggle fullscreen)
1145                 edit_toggle_fullscreen (edit);
1146         }
1147         else if (event->count == GPM_DOUBLE)
1148         {
1149             // double click
1150             edit_mark_current_word_cmd (edit);
1151             edit_total_update (edit);
1152         }
1153         else if (event->count == GPM_TRIPLE)
1154         {
1155             // triple click: works in GPM only, not in xterm
1156             edit_mark_current_line_cmd (edit);
1157             edit_total_update (edit);
1158         }
1159         break;
1160 
1161     case MSG_MOUSE_DRAG:
1162         edit_update_cursor (edit, event);
1163         edit_total_update (edit);
1164         break;
1165 
1166     case MSG_MOUSE_SCROLL_UP:
1167         edit_move_up (edit, 2, TRUE);
1168         edit_total_update (edit);
1169         break;
1170 
1171     case MSG_MOUSE_SCROLL_DOWN:
1172         edit_move_down (edit, 2, TRUE);
1173         edit_total_update (edit);
1174         break;
1175 
1176     default:
1177         break;
1178     }
1179 }
1180 
1181 /* --------------------------------------------------------------------------------------------- */
1182 /*** public functions ****************************************************************************/
1183 /* --------------------------------------------------------------------------------------------- */
1184 /**
1185  * Edit one file.
1186  *
1187  * @param file_vpath file object
1188  * @param line       line number
1189  * @return TRUE if no errors was occurred, FALSE otherwise
1190  */
1191 
1192 gboolean
1193 edit_file (const edit_arg_t *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
1194 {
1195     GList *files;
1196     gboolean ok;
1197 
1198     files = g_list_prepend (NULL, (edit_arg_t *) arg);
1199     ok = edit_files (files);
1200     g_list_free (files);
1201 
1202     return ok;
1203 }
1204 
1205 /* --------------------------------------------------------------------------------------------- */
1206 
1207 gboolean
1208 edit_files (const GList *files)
     /* [previous][next][first][last][top][bottom][index][help]  */
1209 {
1210     static gboolean made_directory = FALSE;
1211     WDialog *edit_dlg;
1212     WGroup *g;
1213     WMenuBar *menubar;
1214     Widget *w, *wd;
1215     const GList *file;
1216     gboolean ok = FALSE;
1217 
1218     if (!made_directory)
1219     {
1220         char *dir;
1221 
1222         dir = mc_build_filename (mc_config_get_cache_path (), EDIT_HOME_DIR, (char *) NULL);
1223         made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1224         g_free (dir);
1225 
1226         dir = mc_build_filename (mc_config_get_path (), EDIT_HOME_DIR, (char *) NULL);
1227         made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1228         g_free (dir);
1229 
1230         dir = mc_build_filename (mc_config_get_data_path (), EDIT_HOME_DIR, (char *) NULL);
1231         made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1232         g_free (dir);
1233     }
1234 
1235     // Create a new dialog and add it widgets to it
1236     edit_dlg = dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, edit_dialog_callback,
1237                            edit_dialog_mouse_callback, "[Internal File Editor]", NULL);
1238     wd = WIDGET (edit_dlg);
1239     widget_want_tab (wd, TRUE);
1240     wd->keymap = editor_map;
1241     wd->ext_keymap = editor_x_map;
1242 
1243     edit_dlg->get_shortcut = edit_get_shortcut;
1244     edit_dlg->get_title = edit_get_title;
1245 
1246     g = GROUP (edit_dlg);
1247 
1248     edit_dlg->bg = WIDGET (background_new (1, 0, wd->rect.lines - 2, wd->rect.cols,
1249                                            EDITOR_BACKGROUND, ' ', edit_dialog_bg_callback));
1250     group_add_widget (g, edit_dlg->bg);
1251 
1252     menubar = menubar_new (NULL);
1253     w = WIDGET (menubar);
1254     group_add_widget_autopos (g, w, w->pos_flags, NULL);
1255     edit_init_menu (menubar);
1256 
1257     w = WIDGET (buttonbar_new ());
1258     group_add_widget_autopos (g, w, w->pos_flags, NULL);
1259 
1260     for (file = files; file != NULL; file = g_list_next (file))
1261     {
1262         gboolean f_ok;
1263 
1264         f_ok = edit_load_file_from_filename (edit_dlg, (const edit_arg_t *) file->data);
1265         // at least one file has been opened succefully
1266         ok = ok || f_ok;
1267     }
1268 
1269     if (ok)
1270         dlg_run (edit_dlg);
1271 
1272     if (!ok || widget_get_state (wd, WST_CLOSED))
1273         widget_destroy (wd);
1274 
1275     return ok;
1276 }
1277 
1278 /* --------------------------------------------------------------------------------------------- */
1279 
1280 WEdit *
1281 edit_find_editor (const WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1282 {
1283     const WGroup *g = CONST_GROUP (h);
1284 
1285     if (edit_widget_is_editor (CONST_WIDGET (g->current->data)))
1286         return EDIT (g->current->data);
1287     return EDIT (widget_find_by_type (CONST_WIDGET (h), edit_callback));
1288 }
1289 
1290 /* --------------------------------------------------------------------------------------------- */
1291 /**
1292  * Check if widget is an WEdit class.
1293  *
1294  * @param w probably editor object
1295  * @return TRUE if widget is an WEdit class, FALSE otherwise
1296  */
1297 
1298 gboolean
1299 edit_widget_is_editor (const Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
1300 {
1301     return (w != NULL && w->callback == edit_callback);
1302 }
1303 
1304 /* --------------------------------------------------------------------------------------------- */
1305 
1306 void
1307 edit_update_screen (WEdit *e)
     /* [previous][next][first][last][top][bottom][index][help]  */
1308 {
1309     edit_scroll_screen_over_cursor (e);
1310     edit_update_curs_col (e);
1311     edit_status (e, widget_get_state (WIDGET (e), WST_FOCUSED));
1312 
1313     // pop all events for this window for internal handling
1314     if (!is_idle ())
1315         e->force |= REDRAW_PAGE;
1316     else
1317     {
1318         if ((e->force & REDRAW_COMPLETELY) != 0)
1319             e->force |= REDRAW_PAGE;
1320         edit_render_keypress (e);
1321     }
1322 
1323     widget_draw (WIDGET (buttonbar_find (DIALOG (WIDGET (e)->owner))));
1324 }
1325 
1326 /* --------------------------------------------------------------------------------------------- */
1327 /**
1328  * Save current window size.
1329  *
1330  * @param edit editor object
1331  */
1332 
1333 void
1334 edit_save_size (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1335 {
1336     edit->loc_prev = WIDGET (edit)->rect;
1337 }
1338 
1339 /* --------------------------------------------------------------------------------------------- */
1340 /**
1341  * Create new editor window and insert it into editor screen.
1342  *
1343  * @param h     editor dialog (screen)
1344  * @param y     y coordinate
1345  * @param x     x coordinate
1346  * @param lines window height
1347  * @param cols  window width
1348  * @param f     file object
1349  * @param fline line number in file
1350  * @return TRUE if new window was successfully created and inserted into editor screen,
1351  *         FALSE otherwise
1352  */
1353 
1354 gboolean
1355 edit_add_window (WDialog *h, const WRect *r, const edit_arg_t *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
1356 {
1357     WEdit *edit;
1358     Widget *w;
1359 
1360     edit = edit_init (NULL, r, arg);
1361     if (edit == NULL)
1362         return FALSE;
1363 
1364     w = WIDGET (edit);
1365     w->callback = edit_callback;
1366     w->mouse_callback = edit_mouse_callback;
1367 
1368     group_add_widget_autopos (GROUP (h), w, WPOS_KEEP_ALL, NULL);
1369     edit_set_buttonbar (edit, buttonbar_find (h));
1370     widget_draw (WIDGET (h));
1371 
1372     return TRUE;
1373 }
1374 
1375 /* --------------------------------------------------------------------------------------------- */
1376 /**
1377  * Handle move/resize events.
1378  *
1379  * @param edit    editor object
1380  * @param command action id
1381  * @return TRUE if the action was handled, FALSE otherwise
1382  */
1383 
1384 gboolean
1385 edit_handle_move_resize (WEdit *edit, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
1386 {
1387     Widget *w = WIDGET (edit);
1388     gboolean ret = FALSE;
1389 
1390     if (edit->fullscreen != 0)
1391     {
1392         edit->drag_state = MCEDIT_DRAG_NONE;
1393         w->mouse.forced_capture = FALSE;
1394         return ret;
1395     }
1396 
1397     switch (edit->drag_state)
1398     {
1399     case MCEDIT_DRAG_NONE:
1400         // possible start move/resize
1401         switch (command)
1402         {
1403         case CK_WindowMove:
1404             edit->drag_state = MCEDIT_DRAG_MOVE;
1405             edit_save_size (edit);
1406             edit_status (edit, TRUE);  // redraw frame and status
1407             /**
1408              * If a user initiates a move by the menu, not by the mouse, we
1409              * make a subsequent mouse drag pull the frame from its middle.
1410              * (We can instead choose '0' to pull it from the corner.)
1411              */
1412             edit->drag_state_start = w->rect.cols / 2;
1413             ret = TRUE;
1414             break;
1415         case CK_WindowResize:
1416             edit->drag_state = MCEDIT_DRAG_RESIZE;
1417             edit_save_size (edit);
1418             edit_status (edit, TRUE);  // redraw frame and status
1419             ret = TRUE;
1420             break;
1421         default:
1422             break;
1423         }
1424         break;
1425 
1426     case MCEDIT_DRAG_MOVE:
1427         switch (command)
1428         {
1429         case CK_WindowResize:
1430             edit->drag_state = MCEDIT_DRAG_RESIZE;
1431             ret = TRUE;
1432             break;
1433         case CK_Up:
1434         case CK_Down:
1435         case CK_Left:
1436         case CK_Right:
1437             edit_window_move (edit, command);
1438             ret = TRUE;
1439             break;
1440         case CK_Enter:
1441         case CK_WindowMove:
1442             edit->drag_state = MCEDIT_DRAG_NONE;
1443             edit_status (edit, TRUE);  // redraw frame and status
1444             MC_FALLTHROUGH;
1445         default:
1446             ret = TRUE;
1447             break;
1448         }
1449         break;
1450 
1451     case MCEDIT_DRAG_RESIZE:
1452         switch (command)
1453         {
1454         case CK_WindowMove:
1455             edit->drag_state = MCEDIT_DRAG_MOVE;
1456             ret = TRUE;
1457             break;
1458         case CK_Up:
1459         case CK_Down:
1460         case CK_Left:
1461         case CK_Right:
1462             edit_window_resize (edit, command);
1463             ret = TRUE;
1464             break;
1465         case CK_Enter:
1466         case CK_WindowResize:
1467             edit->drag_state = MCEDIT_DRAG_NONE;
1468             edit_status (edit, TRUE);  // redraw frame and status
1469             MC_FALLTHROUGH;
1470         default:
1471             ret = TRUE;
1472             break;
1473         }
1474         break;
1475 
1476     default:
1477         break;
1478     }
1479 
1480     /**
1481      * - We let the user stop a resize/move operation by clicking with the
1482      *   mouse anywhere. ("clicking" = pressing and releasing a button.)
1483      * - We let the user perform a resize/move operation by a mouse drag
1484      *   initiated anywhere.
1485      *
1486      * "Anywhere" means: inside or outside the window. We make this happen
1487      * with the 'forced_capture' flag.
1488      */
1489     w->mouse.forced_capture = (edit->drag_state != MCEDIT_DRAG_NONE);
1490 
1491     return ret;
1492 }
1493 
1494 /* --------------------------------------------------------------------------------------------- */
1495 /**
1496  * Toggle window fuulscreen mode.
1497  *
1498  * @param edit editor object
1499  */
1500 
1501 void
1502 edit_toggle_fullscreen (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1503 {
1504     Widget *w = WIDGET (edit);
1505 
1506     edit->fullscreen = edit->fullscreen != 0 ? 0 : 1;
1507     edit->force = REDRAW_COMPLETELY;
1508 
1509     if (edit->fullscreen == 0)
1510     {
1511         edit_restore_size (edit);
1512         // do not follow screen size on resize
1513         w->pos_flags = WPOS_KEEP_DEFAULT;
1514     }
1515     else
1516     {
1517         WRect r;
1518 
1519         edit_save_size (edit);
1520         r = WIDGET (w->owner)->rect;
1521         rect_grow (&r, -1, 0);
1522         widget_set_size_rect (w, &r);
1523         // follow screen size on resize
1524         w->pos_flags = WPOS_KEEP_ALL;
1525         edit->force |= REDRAW_PAGE;
1526         edit_update_screen (edit);
1527     }
1528 }
1529 
1530 /* --------------------------------------------------------------------------------------------- */

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