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

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