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

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