root/src/editor/editcmd_dialogs.c

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

DEFINITIONS

This source file includes following definitions.
  1. editcmd_dialog_raw_key_query_cb
  2. editcmd_dialog_select_definition_add
  3. editcmd_dialog_search_show
  4. editcmd_dialog_replace_show
  5. editcmd_dialog_replace_prompt_show
  6. editcmd_dialog_raw_key_query
  7. editcmd_dialog_completion_show
  8. editcmd_dialog_select_definition_show

   1 /*
   2    Editor dialogs for high level editing commands
   3 
   4    Copyright (C) 2009-2021
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Slava Zanko <slavazanko@gmail.com>, 2009
   9    Andrew Borodin, <aborodin@vmail.ru>, 2012, 2013
  10 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  25  */
  26 
  27 #include <config.h>
  28 
  29 #include "lib/global.h"
  30 #include "lib/tty/tty.h"
  31 #include "lib/skin.h"           /* INPUT_COLOR */
  32 #include "lib/tty/key.h"
  33 #include "lib/search.h"
  34 #include "lib/strutil.h"
  35 #include "lib/widget.h"
  36 #ifdef HAVE_CHARSET
  37 #include "lib/charsets.h"
  38 #endif
  39 
  40 #include "src/history.h"
  41 
  42 #include "src/editor/editwidget.h"
  43 #include "src/editor/etags.h"
  44 #include "src/editor/editcmd_dialogs.h"
  45 
  46 #ifdef HAVE_ASPELL
  47 #include "src/editor/spell.h"
  48 #endif
  49 
  50 /*** global variables ****************************************************************************/
  51 
  52 edit_search_options_t edit_search_options = {
  53     .type = MC_SEARCH_T_NORMAL,
  54     .case_sens = FALSE,
  55     .backwards = FALSE,
  56     .only_in_selection = FALSE,
  57     .whole_words = FALSE,
  58     .all_codepages = FALSE
  59 };
  60 
  61 /*** file scope macro definitions ****************************************************************/
  62 
  63 /*** file scope type declarations ****************************************************************/
  64 
  65 /*** file scope variables ************************************************************************/
  66 
  67 static int def_max_width;
  68 
  69 /* --------------------------------------------------------------------------------------------- */
  70 /*** file scope functions ************************************************************************/
  71 /* --------------------------------------------------------------------------------------------- */
  72 
  73 static cb_ret_t
  74 editcmd_dialog_raw_key_query_cb (Widget * w, Widget * sender, widget_msg_t msg, int parm,
     /* [previous][next][first][last][top][bottom][index][help]  */
  75                                  void *data)
  76 {
  77     WDialog *h = DIALOG (w);
  78 
  79     switch (msg)
  80     {
  81     case MSG_KEY:
  82         h->ret_value = parm;
  83         dlg_stop (h);
  84         return MSG_HANDLED;
  85     default:
  86         return dlg_default_callback (w, sender, msg, parm, data);
  87     }
  88 }
  89 
  90 /* --------------------------------------------------------------------------------------------- */
  91 
  92 static void
  93 editcmd_dialog_select_definition_add (gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
  94 {
  95     etags_hash_t *def_hash = (etags_hash_t *) data;
  96     WListbox *def_list = (WListbox *) user_data;
  97     char *label_def;
  98     int def_width;
  99 
 100     label_def =
 101         g_strdup_printf ("%s -> %s:%ld", def_hash->short_define, def_hash->filename,
 102                          def_hash->line);
 103     listbox_add_item (def_list, LISTBOX_APPEND_AT_END, 0, label_def, def_hash, FALSE);
 104     def_width = str_term_width1 (label_def);
 105     g_free (label_def);
 106     def_max_width = MAX (def_max_width, def_width);
 107 }
 108 
 109 /* --------------------------------------------------------------------------------------------- */
 110 /*** public functions ****************************************************************************/
 111 /* --------------------------------------------------------------------------------------------- */
 112 
 113 gboolean
 114 editcmd_dialog_search_show (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 115 {
 116     char *search_text;
 117     size_t num_of_types = 0;
 118     gchar **list_of_types;
 119     int dialog_result;
 120 
 121     list_of_types = mc_search_get_types_strings_array (&num_of_types);
 122 
 123     {
 124         quick_widget_t quick_widgets[] = {
 125             /* *INDENT-OFF* */
 126             QUICK_LABELED_INPUT (N_("Enter search string:"), input_label_above, INPUT_LAST_TEXT, 
 127                                  MC_HISTORY_SHARED_SEARCH, &search_text, NULL, FALSE, FALSE,
 128                                  INPUT_COMPLETE_NONE),
 129             QUICK_SEPARATOR (TRUE),
 130             QUICK_START_COLUMNS,
 131                 QUICK_RADIO (num_of_types, (const char **) list_of_types,
 132                              (int *) &edit_search_options.type, NULL),
 133             QUICK_NEXT_COLUMN,
 134                 QUICK_CHECKBOX (N_("Cas&e sensitive"), &edit_search_options.case_sens, NULL),
 135                 QUICK_CHECKBOX (N_("&Backwards"), &edit_search_options.backwards, NULL),
 136                 QUICK_CHECKBOX (N_("In se&lection"), &edit_search_options.only_in_selection, NULL),
 137                 QUICK_CHECKBOX (N_("&Whole words"), &edit_search_options.whole_words, NULL),
 138 #ifdef HAVE_CHARSET
 139                 QUICK_CHECKBOX (N_("&All charsets"), &edit_search_options.all_codepages, NULL),
 140 #endif
 141             QUICK_STOP_COLUMNS,
 142             QUICK_START_BUTTONS (TRUE, TRUE),
 143                 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
 144                 QUICK_BUTTON (N_("&Find all"), B_USER, NULL, NULL),
 145                 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
 146             QUICK_END
 147             /* *INDENT-ON* */
 148         };
 149 
 150         quick_dialog_t qdlg = {
 151             -1, -1, 58,
 152             N_("Search"), "[Input Line Keys]",
 153             quick_widgets, NULL, NULL
 154         };
 155 
 156         dialog_result = quick_dialog (&qdlg);
 157     }
 158 
 159     g_strfreev (list_of_types);
 160 
 161     if ((dialog_result == B_CANCEL) || (search_text == NULL) || (search_text[0] == '\0'))
 162     {
 163         g_free (search_text);
 164         return FALSE;
 165     }
 166 
 167     if (dialog_result == B_USER)
 168         search_create_bookmark = TRUE;
 169 
 170 #ifdef HAVE_CHARSET
 171     {
 172         GString *tmp;
 173 
 174         tmp = str_convert_to_input (search_text);
 175         g_free (search_text);
 176         search_text = g_string_free (tmp, FALSE);
 177     }
 178 #endif
 179 
 180     g_free (edit->last_search_string);
 181     edit->last_search_string = search_text;
 182     mc_search_free (edit->search);
 183 
 184 #ifdef HAVE_CHARSET
 185     edit->search = mc_search_new (edit->last_search_string, cp_source);
 186 #else
 187     edit->search = mc_search_new (edit->last_search_string, NULL);
 188 #endif
 189     if (edit->search != NULL)
 190     {
 191         edit->search->search_type = edit_search_options.type;
 192 #ifdef HAVE_CHARSET
 193         edit->search->is_all_charsets = edit_search_options.all_codepages;
 194 #endif
 195         edit->search->is_case_sensitive = edit_search_options.case_sens;
 196         edit->search->whole_words = edit_search_options.whole_words;
 197         edit->search->search_fn = edit_search_cmd_callback;
 198         edit->search->update_fn = edit_search_update_callback;
 199     }
 200 
 201     return (edit->search != NULL);
 202 }
 203 
 204 /* --------------------------------------------------------------------------------------------- */
 205 
 206 void
 207 editcmd_dialog_replace_show (WEdit * edit, const char *search_default, const char *replace_default,
     /* [previous][next][first][last][top][bottom][index][help]  */
 208                              /*@out@ */ char **search_text, /*@out@ */ char **replace_text)
 209 {
 210     size_t num_of_types = 0;
 211     gchar **list_of_types;
 212 
 213     if ((search_default == NULL) || (*search_default == '\0'))
 214         search_default = INPUT_LAST_TEXT;
 215 
 216     list_of_types = mc_search_get_types_strings_array (&num_of_types);
 217 
 218     {
 219         quick_widget_t quick_widgets[] = {
 220             /* *INDENT-OFF* */
 221             QUICK_LABELED_INPUT (N_("Enter search string:"), input_label_above, search_default,
 222                                  MC_HISTORY_SHARED_SEARCH, search_text, NULL, FALSE, FALSE,
 223                                  INPUT_COMPLETE_NONE),
 224             QUICK_LABELED_INPUT (N_("Enter replacement string:"), input_label_above, replace_default,
 225                                  "replace", replace_text, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE),
 226             QUICK_SEPARATOR (TRUE),
 227             QUICK_START_COLUMNS,
 228                 QUICK_RADIO (num_of_types, (const char **) list_of_types,
 229                              (int *) &edit_search_options.type, NULL),
 230             QUICK_NEXT_COLUMN,
 231                 QUICK_CHECKBOX (N_("Cas&e sensitive"), &edit_search_options.case_sens, NULL),
 232                 QUICK_CHECKBOX (N_("&Backwards"), &edit_search_options.backwards, NULL),
 233                 QUICK_CHECKBOX (N_("In se&lection"), &edit_search_options.only_in_selection, NULL),
 234                 QUICK_CHECKBOX (N_("&Whole words"), &edit_search_options.whole_words, NULL),
 235 #ifdef HAVE_CHARSET
 236                 QUICK_CHECKBOX (N_("&All charsets"), &edit_search_options.all_codepages, NULL),
 237 #endif
 238             QUICK_STOP_COLUMNS,
 239             QUICK_BUTTONS_OK_CANCEL,
 240             QUICK_END
 241             /* *INDENT-ON* */
 242         };
 243 
 244         quick_dialog_t qdlg = {
 245             -1, -1, 58,
 246             N_("Replace"), "[Input Line Keys]",
 247             quick_widgets, NULL, NULL
 248         };
 249 
 250         if (quick_dialog (&qdlg) != B_CANCEL)
 251             edit->replace_mode = 0;
 252         else
 253         {
 254             *replace_text = NULL;
 255             *search_text = NULL;
 256         }
 257     }
 258 
 259     g_strfreev (list_of_types);
 260 }
 261 
 262 /* --------------------------------------------------------------------------------------------- */
 263 
 264 int
 265 editcmd_dialog_replace_prompt_show (WEdit * edit, char *from_text, char *to_text, int xpos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 266                                     int ypos)
 267 {
 268     Widget *w = WIDGET (edit);
 269 
 270     /* dialog size */
 271     int dlg_height = 10;
 272     int dlg_width;
 273 
 274     char tmp[BUF_MEDIUM];
 275     char *repl_from, *repl_to;
 276     int retval;
 277 
 278     if (xpos == -1)
 279         xpos = w->x + option_line_state_width + 1;
 280     if (ypos == -1)
 281         ypos = w->y + w->lines / 2;
 282     /* Sometimes menu can hide replaced text. I don't like it */
 283     if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + dlg_height - 1))
 284         ypos -= dlg_height;
 285 
 286     dlg_width = WIDGET (w->owner)->cols - xpos - 1;
 287 
 288     g_snprintf (tmp, sizeof (tmp), "\"%s\"", from_text);
 289     repl_from = g_strdup (str_trunc (tmp, dlg_width - 7));
 290 
 291     g_snprintf (tmp, sizeof (tmp), "\"%s\"", to_text);
 292     repl_to = g_strdup (str_trunc (tmp, dlg_width - 7));
 293 
 294     {
 295         quick_widget_t quick_widgets[] = {
 296             /* *INDENT-OFF* */
 297             QUICK_LABEL (repl_from, NULL),
 298             QUICK_LABEL (N_("Replace with:"), NULL),
 299             QUICK_LABEL (repl_to, NULL),
 300             QUICK_START_BUTTONS (TRUE, TRUE),
 301                 QUICK_BUTTON (N_("&Replace"), B_ENTER, NULL, NULL),
 302                 QUICK_BUTTON (N_("A&ll"), B_REPLACE_ALL, NULL, NULL),
 303                 QUICK_BUTTON (N_("&Skip"), B_SKIP_REPLACE, NULL, NULL),
 304                 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
 305             QUICK_END
 306             /* *INDENT-ON* */
 307         };
 308 
 309         quick_dialog_t qdlg = {
 310             ypos, xpos, -1,
 311             N_("Confirm replace"), NULL,
 312             quick_widgets, NULL, NULL
 313         };
 314 
 315         retval = quick_dialog (&qdlg);
 316     }
 317 
 318     g_free (repl_from);
 319     g_free (repl_to);
 320 
 321     return retval;
 322 }
 323 
 324 /* --------------------------------------------------------------------------------------------- */
 325 /* gets a raw key from the keyboard. Passing cancel = 1 draws
 326    a cancel button thus allowing c-c etc.  Alternatively, cancel = 0
 327    will return the next key pressed.  ctrl-a (=B_CANCEL), ctrl-g, ctrl-c,
 328    and Esc are cannot returned */
 329 
 330 int
 331 editcmd_dialog_raw_key_query (const char *heading, const char *query, gboolean cancel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 332 {
 333     int w, wq;
 334     int y = 2;
 335     WDialog *raw_dlg;
 336     WGroup *g;
 337 
 338     w = str_term_width1 (heading) + 6;
 339     wq = str_term_width1 (query);
 340     w = MAX (w, wq + 3 * 2 + 1 + 2);
 341 
 342     raw_dlg =
 343         dlg_create (TRUE, 0, 0, cancel ? 7 : 5, w, WPOS_CENTER | WPOS_TRYUP, FALSE, dialog_colors,
 344                     editcmd_dialog_raw_key_query_cb, NULL, NULL, heading);
 345     g = GROUP (raw_dlg);
 346     widget_want_tab (WIDGET (raw_dlg), TRUE);
 347 
 348     group_add_widget (g, label_new (y, 3, query));
 349     group_add_widget (g,
 350                       input_new (y++, 3 + wq + 1, input_colors, w - (6 + wq + 1), "", 0,
 351                                  INPUT_COMPLETE_NONE));
 352     if (cancel)
 353     {
 354         group_add_widget (g, hline_new (y++, -1, -1));
 355         /* Button w/o hotkey to allow use any key as raw or macro one */
 356         group_add_widget_autopos (g, button_new (y, 1, B_CANCEL, NORMAL_BUTTON, _("Cancel"), NULL),
 357                                   WPOS_KEEP_TOP | WPOS_CENTER_HORZ, NULL);
 358     }
 359 
 360     w = dlg_run (raw_dlg);
 361     dlg_destroy (raw_dlg);
 362 
 363     return (cancel && (w == ESC_CHAR || w == B_CANCEL)) ? 0 : w;
 364 }
 365 
 366 /* --------------------------------------------------------------------------------------------- */
 367 /* let the user select its preferred completion */
 368 
 369 char *
 370 editcmd_dialog_completion_show (const WEdit * edit, GQueue * compl, int max_width)
     /* [previous][next][first][last][top][bottom][index][help]  */
 371 {
 372     const Widget *we = CONST_WIDGET (edit);
 373     int start_x, start_y, offset;
 374     char *curr = NULL;
 375     WDialog *compl_dlg;
 376     WListbox *compl_list;
 377     int compl_dlg_h;            /* completion dialog height */
 378     int compl_dlg_w;            /* completion dialog width */
 379     GList *i;
 380 
 381     /* calculate the dialog metrics */
 382     compl_dlg_h = g_queue_get_length (compl) + 2;
 383     compl_dlg_w = max_width + 4;
 384     start_x = we->x + edit->curs_col + edit->start_col + EDIT_TEXT_HORIZONTAL_OFFSET +
 385         (edit->fullscreen ? 0 : 1) + option_line_state_width;
 386     start_y = we->y + edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + (edit->fullscreen ? 0 : 1) + 1;
 387 
 388     if (start_x < 0)
 389         start_x = 0;
 390     if (start_x < we->x + 1)
 391         start_x = we->x + 1 + option_line_state_width;
 392     if (compl_dlg_w > COLS)
 393         compl_dlg_w = COLS;
 394     if (compl_dlg_h > LINES - 2)
 395         compl_dlg_h = LINES - 2;
 396 
 397     offset = start_x + compl_dlg_w - COLS;
 398     if (offset > 0)
 399         start_x -= offset;
 400     offset = start_y + compl_dlg_h - LINES;
 401     if (offset > 0)
 402         start_y -= offset;
 403 
 404     /* create the dialog */
 405     compl_dlg =
 406         dlg_create (TRUE, start_y, start_x, compl_dlg_h, compl_dlg_w, WPOS_KEEP_DEFAULT, TRUE,
 407                     dialog_colors, NULL, NULL, "[Completion]", NULL);
 408 
 409     /* create the listbox */
 410     compl_list = listbox_new (1, 1, compl_dlg_h - 2, compl_dlg_w - 2, FALSE, NULL);
 411 
 412     /* fill the listbox with the completions in the reverse order */
 413     for (i = g_queue_peek_tail_link (compl); i != NULL; i = g_list_previous (i))
 414         listbox_add_item (compl_list, LISTBOX_APPEND_AT_END, 0, ((GString *) i->data)->str, NULL,
 415                           FALSE);
 416 
 417     group_add_widget (GROUP (compl_dlg), compl_list);
 418 
 419     /* pop up the dialog and apply the chosen completion */
 420     if (dlg_run (compl_dlg) == B_ENTER)
 421     {
 422         listbox_get_current (compl_list, &curr, NULL);
 423         curr = g_strdup (curr);
 424     }
 425 
 426     /* destroy dialog before return */
 427     dlg_destroy (compl_dlg);
 428 
 429     return curr;
 430 }
 431 
 432 /* --------------------------------------------------------------------------------------------- */
 433 /* let the user select where function definition */
 434 
 435 void
 436 editcmd_dialog_select_definition_show (WEdit * edit, char *match_expr, GPtrArray * def_hash)
     /* [previous][next][first][last][top][bottom][index][help]  */
 437 {
 438     const Widget *we = CONST_WIDGET (edit);
 439     int start_x, start_y, offset;
 440     char *curr = NULL;
 441     WDialog *def_dlg;
 442     WListbox *def_list;
 443     int def_dlg_h;              /* dialog height */
 444     int def_dlg_w;              /* dialog width */
 445 
 446     /* calculate the dialog metrics */
 447     def_dlg_h = def_hash->len + 2;
 448     def_dlg_w = COLS - 2;       /* will be clarified later */
 449     start_x = we->x + edit->curs_col + edit->start_col + EDIT_TEXT_HORIZONTAL_OFFSET +
 450         (edit->fullscreen ? 0 : 1) + option_line_state_width;
 451     start_y = we->y + edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + (edit->fullscreen ? 0 : 1) + 1;
 452 
 453     if (start_x < 0)
 454         start_x = 0;
 455     if (start_x < we->x + 1)
 456         start_x = we->x + 1 + option_line_state_width;
 457 
 458     if (def_dlg_h > LINES - 2)
 459         def_dlg_h = LINES - 2;
 460 
 461     offset = start_y + def_dlg_h - LINES;
 462     if (offset > 0)
 463         start_y -= (offset + 1);
 464 
 465     def_dlg = dlg_create (TRUE, start_y, start_x, def_dlg_h, def_dlg_w, WPOS_KEEP_DEFAULT, TRUE,
 466                           dialog_colors, NULL, NULL, "[Definitions]", match_expr);
 467     def_list = listbox_new (1, 1, def_dlg_h - 2, def_dlg_w - 2, FALSE, NULL);
 468     group_add_widget_autopos (GROUP (def_dlg), def_list, WPOS_KEEP_ALL, NULL);
 469 
 470     /* fill the listbox with the completions and get the maximim width */
 471     def_max_width = 0;
 472     g_ptr_array_foreach (def_hash, editcmd_dialog_select_definition_add, def_list);
 473 
 474     /* adjust dialog width */
 475     def_dlg_w = def_max_width + 4;
 476     offset = start_x + def_dlg_w - COLS;
 477     if (offset > 0)
 478         start_x -= offset;
 479 
 480     widget_set_size (WIDGET (def_dlg), start_y, start_x, def_dlg_h, def_dlg_w);
 481 
 482     /* pop up the dialog and apply the chosen completion */
 483     if (dlg_run (def_dlg) == B_ENTER)
 484     {
 485         etags_hash_t *curr_def = NULL;
 486         gboolean do_moveto = FALSE;
 487 
 488         listbox_get_current (def_list, &curr, (void **) &curr_def);
 489 
 490         if (!edit->modified)
 491             do_moveto = TRUE;
 492         else if (!edit_query_dialog2
 493                  (_("Warning"),
 494                   _("Current text was modified without a file save.\n"
 495                     "Continue discards these changes."), _("C&ontinue"), _("&Cancel")))
 496         {
 497             edit->force |= REDRAW_COMPLETELY;
 498             do_moveto = TRUE;
 499         }
 500 
 501         if (curr != NULL && do_moveto && edit_stack_iterator + 1 < MAX_HISTORY_MOVETO)
 502         {
 503             vfs_path_free (edit_history_moveto[edit_stack_iterator].filename_vpath, TRUE);
 504 
 505             /* Is file path absolute? Prepend with dir_vpath if necessary */
 506             if (edit->filename_vpath != NULL && edit->filename_vpath->relative
 507                 && edit->dir_vpath != NULL)
 508                 edit_history_moveto[edit_stack_iterator].filename_vpath =
 509                     vfs_path_append_vpath_new (edit->dir_vpath, edit->filename_vpath, NULL);
 510             else
 511                 edit_history_moveto[edit_stack_iterator].filename_vpath =
 512                     vfs_path_clone (edit->filename_vpath);
 513 
 514             edit_history_moveto[edit_stack_iterator].line = edit->start_line + edit->curs_row + 1;
 515             edit_stack_iterator++;
 516             vfs_path_free (edit_history_moveto[edit_stack_iterator].filename_vpath, TRUE);
 517             edit_history_moveto[edit_stack_iterator].filename_vpath =
 518                 vfs_path_from_str ((char *) curr_def->fullpath);
 519             edit_history_moveto[edit_stack_iterator].line = curr_def->line;
 520             edit_reload_line (edit, edit_history_moveto[edit_stack_iterator].filename_vpath,
 521                               edit_history_moveto[edit_stack_iterator].line);
 522         }
 523     }
 524 
 525     /* destroy dialog before return */
 526     dlg_destroy (def_dlg);
 527 }
 528 
 529 /* --------------------------------------------------------------------------------------------- */

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