Merge branch '4524_cleanup'
[midnight-commander.git] / src / editor / editcmd.c
blob38a9b76d7e2fe6855ce7b0fed1119b68e5efbf23
1 /*
2 Editor high level editing commands
4 Copyright (C) 1996-2024
5 Free Software Foundation, Inc.
7 Written by:
8 Paul Sheer, 1996, 1997
9 Andrew Borodin <aborodin@vmail.ru>, 2012-2022
10 Ilia Maslakov <il.smind@gmail.com>, 2012
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /** \file
29 * \brief Source: editor high level editing commands
30 * \author Paul Sheer
31 * \date 1996, 1997
34 /* #define PIPE_BLOCKS_SO_READ_BYTE_BY_BYTE */
36 #include <config.h>
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <sys/stat.h>
45 #include <stdlib.h>
47 #include "lib/global.h"
48 #include "lib/tty/tty.h"
49 #include "lib/tty/key.h" /* XCTRL */
50 #include "lib/strutil.h" /* utf string functions */
51 #include "lib/fileloc.h"
52 #include "lib/lock.h"
53 #include "lib/util.h" /* tilde_expand() */
54 #include "lib/vfs/vfs.h"
55 #include "lib/widget.h"
56 #include "lib/event.h" /* mc_event_raise() */
57 #ifdef HAVE_CHARSET
58 #include "lib/charsets.h"
59 #endif
61 #include "src/history.h"
62 #include "src/file_history.h" /* show_file_history() */
63 #ifdef HAVE_CHARSET
64 #include "src/selcodepage.h"
65 #endif
66 #include "src/util.h" /* check_for_default() */
68 #include "edit-impl.h"
69 #include "editwidget.h"
70 #include "editsearch.h"
71 #include "etags.h"
73 /*** global variables ****************************************************************************/
75 /* search and replace: */
76 int search_create_bookmark = FALSE;
78 /*** file scope macro definitions ****************************************************************/
80 #define space_width 1
82 #define TEMP_BUF_LEN 1024
84 /*** file scope type declarations ****************************************************************/
86 /*** forward declarations (file scope functions) *************************************************/
88 /*** file scope variables ************************************************************************/
90 static unsigned long edit_save_mode_radio_id, edit_save_mode_input_id;
92 /* --------------------------------------------------------------------------------------------- */
93 /*** file scope functions ************************************************************************/
94 /* --------------------------------------------------------------------------------------------- */
96 static cb_ret_t
97 edit_save_mode_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
99 switch (msg)
101 case MSG_CHANGED_FOCUS:
102 if (sender != NULL && sender->id == edit_save_mode_radio_id)
104 Widget *ww;
106 ww = widget_find_by_id (w, edit_save_mode_input_id);
107 widget_disable (ww, RADIO (sender)->sel != 2);
108 return MSG_HANDLED;
110 return MSG_NOT_HANDLED;
112 default:
113 return dlg_default_callback (w, sender, msg, parm, data);
117 /* --------------------------------------------------------------------------------------------- */
119 /* If 0 (quick save) then a) create/truncate <filename> file,
120 b) save to <filename>;
121 if 1 (safe save) then a) save to <tempnam>,
122 b) rename <tempnam> to <filename>;
123 if 2 (do backups) then a) save to <tempnam>,
124 b) rename <filename> to <filename.backup_ext>,
125 c) rename <tempnam> to <filename>. */
127 /* returns 0 on error, -1 on abort */
129 static int
130 edit_save_file (WEdit * edit, const vfs_path_t * filename_vpath)
132 char *p;
133 gchar *tmp;
134 off_t filelen = 0;
135 int this_save_mode, rv, fd = -1;
136 vfs_path_t *real_filename_vpath;
137 vfs_path_t *savename_vpath = NULL;
138 const char *start_filename;
139 const vfs_path_element_t *vpath_element;
140 struct stat sb;
142 vpath_element = vfs_path_get_by_index (filename_vpath, 0);
143 if (vpath_element == NULL)
144 return 0;
146 start_filename = vpath_element->path;
147 if (*start_filename == '\0')
148 return 0;
150 if (!IS_PATH_SEP (*start_filename) && edit->dir_vpath != NULL)
151 real_filename_vpath = vfs_path_append_vpath_new (edit->dir_vpath, filename_vpath, NULL);
152 else
153 real_filename_vpath = vfs_path_clone (filename_vpath);
155 this_save_mode = edit_options.save_mode;
156 if (this_save_mode != EDIT_QUICK_SAVE)
158 if (!vfs_file_is_local (real_filename_vpath))
159 /* The file does not exists yet, so no safe save or backup are necessary. */
160 this_save_mode = EDIT_QUICK_SAVE;
161 else
163 fd = mc_open (real_filename_vpath, O_RDONLY | O_BINARY);
164 if (fd == -1)
165 /* The file does not exists yet, so no safe save or backup are necessary. */
166 this_save_mode = EDIT_QUICK_SAVE;
169 if (fd != -1)
170 mc_close (fd);
173 rv = mc_stat (real_filename_vpath, &sb);
174 if (rv == 0)
176 if (this_save_mode == EDIT_QUICK_SAVE && !edit->skip_detach_prompt && sb.st_nlink > 1)
178 rv = edit_query_dialog3 (_("Warning"),
179 _("File has hard-links. Detach before saving?"),
180 _("&Yes"), _("&No"), _("&Cancel"));
181 switch (rv)
183 case 0:
184 this_save_mode = EDIT_SAFE_SAVE;
185 MC_FALLTHROUGH;
186 case 1:
187 edit->skip_detach_prompt = 1;
188 break;
189 default:
190 vfs_path_free (real_filename_vpath, TRUE);
191 return -1;
195 /* Prevent overwriting changes from other editor sessions. */
196 if (edit->stat1.st_mtime != 0 && edit->stat1.st_mtime != sb.st_mtime)
198 /* The default action is "Cancel". */
199 query_set_sel (1);
201 rv = edit_query_dialog2 (_("Warning"),
202 _("The file has been modified in the meantime. Save anyway?"),
203 _("&Yes"), _("&Cancel"));
204 if (rv != 0)
206 vfs_path_free (real_filename_vpath, TRUE);
207 return -1;
212 if (this_save_mode == EDIT_QUICK_SAVE)
213 savename_vpath = vfs_path_clone (real_filename_vpath);
214 else
216 char *savedir, *saveprefix;
218 savedir = vfs_path_tokens_get (real_filename_vpath, 0, -1);
219 if (savedir == NULL)
220 savedir = g_strdup (".");
222 /* Token-related function never return leading slash, so we need add it manually */
223 saveprefix = mc_build_filename (PATH_SEP_STR, savedir, "cooledit", (char *) NULL);
224 g_free (savedir);
225 fd = mc_mkstemps (&savename_vpath, saveprefix, NULL);
226 g_free (saveprefix);
227 if (savename_vpath == NULL)
229 vfs_path_free (real_filename_vpath, TRUE);
230 return 0;
232 /* FIXME:
233 * Close for now because mc_mkstemps use pure open system call
234 * to create temporary file and it needs to be reopened by
235 * VFS-aware mc_open().
237 close (fd);
240 (void) mc_chown (savename_vpath, edit->stat1.st_uid, edit->stat1.st_gid);
241 (void) mc_chmod (savename_vpath, edit->stat1.st_mode);
242 if (edit->attrs_ok)
243 (void) mc_fsetflags (savename_vpath, edit->attrs);
245 fd = mc_open (savename_vpath, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, edit->stat1.st_mode);
246 if (fd == -1)
247 goto error_save;
249 /* pipe save */
250 p = edit_get_write_filter (savename_vpath, real_filename_vpath);
251 if (p != NULL)
253 FILE *file;
255 mc_close (fd);
256 file = (FILE *) popen (p, "w");
258 if (file != NULL)
260 filelen = edit_write_stream (edit, file);
261 #if 1
262 pclose (file);
263 #else
264 if (pclose (file) != 0)
266 tmp = g_strdup_printf (_("Error writing to pipe: %s"), p);
267 edit_error_dialog (_("Error"), tmp);
268 g_free (tmp);
269 g_free (p);
270 goto error_save;
272 #endif
274 else
276 tmp = g_strdup_printf (_("Cannot open pipe for writing: %s"), p);
277 edit_error_dialog (_("Error"), get_sys_error (tmp));
278 g_free (p);
279 g_free (tmp);
280 goto error_save;
282 g_free (p);
284 else if (edit->lb == LB_ASIS)
285 { /* do not change line breaks */
286 filelen = edit_buffer_write_file (&edit->buffer, fd);
288 if (filelen != edit->buffer.size)
290 mc_close (fd);
291 goto error_save;
294 if (mc_close (fd) != 0)
295 goto error_save;
297 /* Update the file information, especially the mtime. */
298 if (mc_stat (savename_vpath, &edit->stat1) == -1)
299 goto error_save;
301 else
302 { /* change line breaks */
303 FILE *file;
304 const char *savename;
306 mc_close (fd);
308 savename = vfs_path_get_last_path_str (savename_vpath);
309 file = (FILE *) fopen (savename, "w");
310 if (file != NULL)
312 filelen = edit_write_stream (edit, file);
313 fclose (file);
315 else
317 char *msg;
319 msg = g_strdup_printf (_("Cannot open file for writing: %s"), savename);
320 edit_error_dialog (_("Error"), msg);
321 g_free (msg);
322 goto error_save;
326 if (filelen != edit->buffer.size)
327 goto error_save;
329 if (this_save_mode == EDIT_DO_BACKUP)
331 char *tmp_store_filename;
332 vfs_path_element_t *last_vpath_element;
333 vfs_path_t *tmp_vpath;
334 gboolean ok;
336 g_assert (edit_options.backup_ext != NULL);
338 /* add backup extension to the path */
339 tmp_vpath = vfs_path_clone (real_filename_vpath);
340 last_vpath_element = (vfs_path_element_t *) vfs_path_get_by_index (tmp_vpath, -1);
341 tmp_store_filename = last_vpath_element->path;
342 last_vpath_element->path =
343 g_strdup_printf ("%s%s", tmp_store_filename, edit_options.backup_ext);
344 g_free (tmp_store_filename);
346 ok = (mc_rename (real_filename_vpath, tmp_vpath) != -1);
347 vfs_path_free (tmp_vpath, TRUE);
348 if (!ok)
349 goto error_save;
352 if (this_save_mode != EDIT_QUICK_SAVE && mc_rename (savename_vpath, real_filename_vpath) == -1)
353 goto error_save;
355 vfs_path_free (real_filename_vpath, TRUE);
356 vfs_path_free (savename_vpath, TRUE);
357 return 1;
358 error_save:
359 /* FIXME: Is this safe ?
360 * if (this_save_mode != EDIT_QUICK_SAVE)
361 * mc_unlink (savename);
363 vfs_path_free (real_filename_vpath, TRUE);
364 vfs_path_free (savename_vpath, TRUE);
365 return 0;
368 /* --------------------------------------------------------------------------------------------- */
370 static gboolean
371 edit_check_newline (const edit_buffer_t * buf)
373 return !(edit_options.check_nl_at_eof && buf->size > 0
374 && edit_buffer_get_byte (buf, buf->size - 1) != '\n'
375 && edit_query_dialog2 (_("Warning"),
376 _("The file you are saving does not end with a newline."),
377 _("C&ontinue"), _("&Cancel")) != 0);
380 /* --------------------------------------------------------------------------------------------- */
382 static vfs_path_t *
383 edit_get_save_file_as (WEdit * edit)
385 static LineBreaks cur_lb = LB_ASIS;
386 char *filename_res;
387 vfs_path_t *ret_vpath = NULL;
389 const char *lb_names[LB_NAMES] = {
390 N_("&Do not change"),
391 N_("&Unix format (LF)"),
392 N_("&Windows/DOS format (CR LF)"),
393 N_("&Macintosh format (CR)")
396 quick_widget_t quick_widgets[] = {
397 /* *INDENT-OFF* */
398 QUICK_LABELED_INPUT (N_("Enter file name:"), input_label_above,
399 vfs_path_as_str (edit->filename_vpath), "save-as",
400 &filename_res, NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
401 QUICK_SEPARATOR (TRUE),
402 QUICK_LABEL (N_("Change line breaks to:"), NULL),
403 QUICK_RADIO (LB_NAMES, lb_names, (int *) &cur_lb, NULL),
404 QUICK_BUTTONS_OK_CANCEL,
405 QUICK_END
406 /* *INDENT-ON* */
409 WRect r = { -1, -1, 0, 64 };
411 quick_dialog_t qdlg = {
412 r, N_("Save As"), "[Save File As]",
413 quick_widgets, NULL, NULL
416 if (quick_dialog (&qdlg) != B_CANCEL)
418 char *fname;
420 edit->lb = cur_lb;
421 fname = tilde_expand (filename_res);
422 g_free (filename_res);
423 ret_vpath = vfs_path_from_str (fname);
424 g_free (fname);
427 return ret_vpath;
430 /* --------------------------------------------------------------------------------------------- */
432 /** returns TRUE on success */
434 static gboolean
435 edit_save_cmd (WEdit * edit)
437 int res, save_lock = 0;
439 if (!edit->locked && !edit->delete_file)
440 save_lock = lock_file (edit->filename_vpath);
441 res = edit_save_file (edit, edit->filename_vpath);
443 /* Maintain modify (not save) lock on failure */
444 if ((res > 0 && edit->locked) || save_lock)
445 edit->locked = unlock_file (edit->filename_vpath);
447 /* On failure try 'save as', it does locking on its own */
448 if (res == 0)
449 return edit_save_as_cmd (edit);
451 if (res > 0)
453 edit->delete_file = 0;
454 edit->modified = 0;
457 edit->force |= REDRAW_COMPLETELY;
459 return TRUE;
462 /* --------------------------------------------------------------------------------------------- */
464 static void
465 edit_delete_column_of_text (WEdit * edit, off_t m1, off_t m2)
467 off_t n;
468 off_t r;
469 long b, c, d;
471 n = edit_buffer_get_forward_offset (&edit->buffer, m1, 0, m2) + 1;
472 r = edit_buffer_get_bol (&edit->buffer, m1);
473 c = (long) edit_move_forward3 (edit, r, 0, m1);
474 r = edit_buffer_get_bol (&edit->buffer, m2);
475 d = (long) edit_move_forward3 (edit, r, 0, m2);
476 b = MAX (MIN (c, d), MIN (edit->column1, edit->column2));
477 c = MAX (c, MAX (edit->column1, edit->column2));
479 while (n-- != 0)
481 off_t p, q;
483 r = edit_buffer_get_current_bol (&edit->buffer);
484 p = edit_move_forward3 (edit, r, b, 0);
485 q = edit_move_forward3 (edit, r, c, 0);
486 p = MAX (p, m1);
487 q = MIN (q, m2);
488 edit_cursor_move (edit, p - edit->buffer.curs1);
489 /* delete line between margins */
490 for (; q > p; q--)
491 if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
492 edit_delete (edit, TRUE);
494 /* move to next line except on the last delete */
495 if (n != 0)
497 r = edit_buffer_get_forward_offset (&edit->buffer, edit->buffer.curs1, 1, 0);
498 edit_cursor_move (edit, r - edit->buffer.curs1);
503 /* --------------------------------------------------------------------------------------------- */
504 /** if success return TRUE */
506 static gboolean
507 edit_block_delete (WEdit * edit, off_t start_mark, off_t end_mark)
509 off_t curs_pos;
510 long curs_line, c1, c2;
512 if (edit->column_highlight && edit->mark2 < 0)
513 edit_mark_cmd (edit, FALSE);
515 /* Warning message with a query to continue or cancel the operation */
516 if ((end_mark - start_mark) > max_undo / 2 &&
517 edit_query_dialog2 (_("Warning"),
518 ("Block is large, you may not be able to undo this action"),
519 _("C&ontinue"), _("&Cancel")) != 0)
520 return FALSE;
522 c1 = MIN (edit->column1, edit->column2);
523 c2 = MAX (edit->column1, edit->column2);
524 edit->column1 = c1;
525 edit->column2 = c2;
527 edit_push_markers (edit);
529 curs_line = edit->buffer.curs_line;
531 curs_pos = edit->curs_col + edit->over_col;
533 /* move cursor to start of selection */
534 edit_cursor_move (edit, start_mark - edit->buffer.curs1);
535 edit_scroll_screen_over_cursor (edit);
537 if (start_mark < end_mark)
539 if (edit->column_highlight)
541 off_t b, e;
542 off_t line_width;
544 if (edit->mark2 < 0)
545 edit_mark_cmd (edit, FALSE);
546 edit_delete_column_of_text (edit, start_mark, end_mark);
547 /* move cursor to the saved position */
548 edit_move_to_line (edit, curs_line);
549 /* calculate line width and cursor position before cut */
550 b = edit_buffer_get_current_bol (&edit->buffer);
551 e = edit_buffer_get_current_eol (&edit->buffer);
552 line_width = edit_move_forward3 (edit, b, 0, e);
553 if (edit_options.cursor_beyond_eol && curs_pos > line_width)
554 edit->over_col = curs_pos - line_width;
556 else
558 off_t count;
560 for (count = start_mark; count < end_mark; count++)
561 edit_delete (edit, TRUE);
565 edit_set_markers (edit, 0, 0, 0, 0);
566 edit->force |= REDRAW_PAGE;
568 return TRUE;
571 /* --------------------------------------------------------------------------------------------- */
572 /** Return a null terminated length of text. Result must be g_free'd */
574 static unsigned char *
575 edit_get_block (WEdit * edit, off_t start, off_t finish, off_t * l)
577 unsigned char *s, *r;
579 r = s = g_malloc0 (finish - start + 1);
581 if (edit->column_highlight)
583 *l = 0;
585 /* copy from buffer, excluding chars that are out of the column 'margins' */
586 for (; start < finish; start++)
588 int c;
589 off_t x;
591 x = edit_buffer_get_bol (&edit->buffer, start);
592 x = edit_move_forward3 (edit, x, 0, start);
593 c = edit_buffer_get_byte (&edit->buffer, start);
594 if ((x >= edit->column1 && x < edit->column2)
595 || (x >= edit->column2 && x < edit->column1) || c == '\n')
597 *s++ = c;
598 (*l)++;
602 else
604 *l = finish - start;
606 for (; start < finish; start++)
607 *s++ = edit_buffer_get_byte (&edit->buffer, start);
610 *s = '\0';
612 return r;
615 /* --------------------------------------------------------------------------------------------- */
616 /** copies a block to clipboard file */
618 static gboolean
619 edit_save_block_to_clip_file (WEdit * edit, off_t start, off_t finish)
621 gboolean ret;
622 gchar *tmp;
624 tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
625 ret = edit_save_block (edit, tmp, start, finish);
626 g_free (tmp);
628 return ret;
631 /* --------------------------------------------------------------------------------------------- */
633 static void
634 pipe_mail (const edit_buffer_t * buf, char *to, char *subject, char *cc)
636 FILE *p = 0;
637 char *s = NULL;
639 to = name_quote (to, FALSE);
640 if (to != NULL)
642 subject = name_quote (subject, FALSE);
643 if (subject != NULL)
645 cc = name_quote (cc, FALSE);
646 if (cc == NULL)
647 s = g_strdup_printf ("mail -s %s %s", subject, to);
648 else
650 s = g_strdup_printf ("mail -s %s -c %s %s", subject, cc, to);
651 g_free (cc);
654 g_free (subject);
657 g_free (to);
660 if (s != NULL)
662 p = popen (s, "w");
663 g_free (s);
666 if (p != NULL)
668 off_t i;
670 for (i = 0; i < buf->size; i++)
671 if (fputc (edit_buffer_get_byte (buf, i), p) < 0)
672 break;
673 pclose (p);
677 /* --------------------------------------------------------------------------------------------- */
679 static void
680 edit_insert_column_of_text (WEdit * edit, unsigned char *data, off_t size, long width,
681 off_t * start_pos, off_t * end_pos, long *col1, long *col2)
683 off_t i, cursor;
684 long col;
686 cursor = edit->buffer.curs1;
687 col = edit_get_col (edit);
689 for (i = 0; i < size; i++)
691 if (data[i] != '\n')
692 edit_insert (edit, data[i]);
693 else
694 { /* fill in and move to next line */
695 long l;
696 off_t p;
698 if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
700 for (l = width - (edit_get_col (edit) - col); l > 0; l -= space_width)
701 edit_insert (edit, ' ');
703 for (p = edit->buffer.curs1;; p++)
705 if (p == edit->buffer.size)
707 edit_cursor_move (edit, edit->buffer.size - edit->buffer.curs1);
708 edit_insert_ahead (edit, '\n');
709 p++;
710 break;
712 if (edit_buffer_get_byte (&edit->buffer, p) == '\n')
714 p++;
715 break;
718 edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->buffer.curs1);
720 for (l = col - edit_get_col (edit); l >= space_width; l -= space_width)
721 edit_insert (edit, ' ');
725 *col1 = col;
726 *col2 = col + width;
727 *start_pos = cursor;
728 *end_pos = edit->buffer.curs1;
729 edit_cursor_move (edit, cursor - edit->buffer.curs1);
732 /* --------------------------------------------------------------------------------------------- */
734 * Callback for the iteration of objects in the 'editors' array.
735 * Toggle syntax highlighting in editor object.
737 * @param data probably WEdit object
738 * @param user_data unused
741 static void
742 edit_syntax_onoff_cb (void *data, void *user_data)
744 (void) user_data;
746 if (edit_widget_is_editor (CONST_WIDGET (data)))
748 WEdit *edit = EDIT (data);
750 if (edit_options.syntax_highlighting)
751 edit_load_syntax (edit, NULL, edit->syntax_type);
752 edit->force |= REDRAW_PAGE;
756 /* --------------------------------------------------------------------------------------------- */
758 static cb_ret_t
759 editcmd_dialog_raw_key_query_cb (Widget * w, Widget * sender, widget_msg_t msg, int parm,
760 void *data)
762 WDialog *h = DIALOG (w);
764 switch (msg)
766 case MSG_KEY:
767 h->ret_value = parm;
768 dlg_close (h);
769 return MSG_HANDLED;
770 default:
771 return dlg_default_callback (w, sender, msg, parm, data);
775 /* --------------------------------------------------------------------------------------------- */
776 /*** public functions ****************************************************************************/
777 /* --------------------------------------------------------------------------------------------- */
779 void
780 edit_refresh_cmd (void)
782 tty_clear_screen ();
783 repaint_screen ();
784 tty_keypad (TRUE);
787 /* --------------------------------------------------------------------------------------------- */
789 * Toggle syntax highlighting in all editor windows.
791 * @param h root widget for all windows
794 void
795 edit_syntax_onoff_cmd (WDialog * h)
797 edit_options.syntax_highlighting = !edit_options.syntax_highlighting;
798 g_list_foreach (GROUP (h)->widgets, edit_syntax_onoff_cb, NULL);
799 widget_draw (WIDGET (h));
802 /* --------------------------------------------------------------------------------------------- */
804 * Toggle tabs showing in all editor windows.
806 * @param h root widget for all windows
809 void
810 edit_show_tabs_tws_cmd (WDialog * h)
812 enable_show_tabs_tws = !enable_show_tabs_tws;
813 widget_draw (WIDGET (h));
816 /* --------------------------------------------------------------------------------------------- */
818 * Toggle right margin showing in all editor windows.
820 * @param h root widget for all windows
823 void
824 edit_show_margin_cmd (WDialog * h)
826 edit_options.show_right_margin = !edit_options.show_right_margin;
827 widget_draw (WIDGET (h));
830 /* --------------------------------------------------------------------------------------------- */
832 * Toggle line numbers showing in all editor windows.
834 * @param h root widget for all windows
837 void
838 edit_show_numbers_cmd (WDialog * h)
840 edit_options.line_state = !edit_options.line_state;
841 edit_options.line_state_width = edit_options.line_state ? LINE_STATE_WIDTH : 0;
842 widget_draw (WIDGET (h));
845 /* --------------------------------------------------------------------------------------------- */
847 void
848 edit_save_mode_cmd (void)
850 char *str_result = NULL;
852 const char *str[] = {
853 N_("&Quick save"),
854 N_("&Safe save"),
855 N_("&Do backups with following extension:")
858 #ifdef ENABLE_NLS
859 size_t i;
861 for (i = 0; i < 3; i++)
862 str[i] = _(str[i]);
863 #endif
865 g_assert (edit_options.backup_ext != NULL);
868 quick_widget_t quick_widgets[] = {
869 /* *INDENT-OFF* */
870 QUICK_RADIO (3, str, &edit_options.save_mode, &edit_save_mode_radio_id),
871 QUICK_INPUT (edit_options.backup_ext, "edit-backup-ext", &str_result,
872 &edit_save_mode_input_id, FALSE, FALSE, INPUT_COMPLETE_NONE),
873 QUICK_SEPARATOR (TRUE),
874 QUICK_CHECKBOX (N_("Check &POSIX new line"), &edit_options.check_nl_at_eof, NULL),
875 QUICK_BUTTONS_OK_CANCEL,
876 QUICK_END
877 /* *INDENT-ON* */
880 WRect r = { -1, -1, 0, 38 };
882 quick_dialog_t qdlg = {
883 r, N_("Edit Save Mode"), "[Edit Save Mode]",
884 quick_widgets, edit_save_mode_callback, NULL
887 if (quick_dialog (&qdlg) != B_CANCEL)
889 g_free (edit_options.backup_ext);
890 edit_options.backup_ext = str_result;
895 /* --------------------------------------------------------------------------------------------- */
897 void
898 edit_set_filename (WEdit * edit, const vfs_path_t * name_vpath)
900 vfs_path_free (edit->filename_vpath, TRUE);
901 edit->filename_vpath = vfs_path_clone (name_vpath);
903 if (edit->dir_vpath == NULL)
904 edit->dir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
907 /* --------------------------------------------------------------------------------------------- */
908 /* Here we want to warn the users of overwriting an existing file,
909 but only if they have made a change to the filename */
910 /* returns TRUE on success */
911 gboolean
912 edit_save_as_cmd (WEdit * edit)
914 /* This heads the 'Save As' dialog box */
915 vfs_path_t *exp_vpath;
916 int save_lock = 0;
917 gboolean different_filename = FALSE;
918 gboolean ret = FALSE;
920 if (!edit_check_newline (&edit->buffer))
921 return FALSE;
923 exp_vpath = edit_get_save_file_as (edit);
924 edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
926 if (exp_vpath != NULL && vfs_path_len (exp_vpath) != 0)
928 int rv;
930 if (!vfs_path_equal (edit->filename_vpath, exp_vpath))
932 int file;
933 struct stat sb;
935 if (mc_stat (exp_vpath, &sb) == 0 && !S_ISREG (sb.st_mode))
937 edit_error_dialog (_("Save as"),
938 get_sys_error (_
939 ("Cannot save: destination is not a regular file")));
940 goto ret;
943 different_filename = TRUE;
944 file = mc_open (exp_vpath, O_RDONLY | O_BINARY);
946 if (file == -1)
947 edit->stat1.st_mode |= S_IWUSR;
948 else
950 /* the file exists */
951 mc_close (file);
952 /* Overwrite the current file or cancel the operation */
953 if (edit_query_dialog2
954 (_("Warning"),
955 _("A file already exists with this name"), _("&Overwrite"), _("&Cancel")))
956 goto ret;
959 save_lock = lock_file (exp_vpath);
961 else if (!edit->locked && !edit->delete_file)
962 /* filenames equal, check if already locked */
963 save_lock = lock_file (exp_vpath);
965 if (different_filename)
966 /* Allow user to write into saved (under another name) file
967 * even if original file had r/o user permissions. */
968 edit->stat1.st_mode |= S_IWUSR;
970 rv = edit_save_file (edit, exp_vpath);
971 switch (rv)
973 case 1:
974 /* Successful, so unlock both files */
975 if (different_filename)
977 if (save_lock)
978 unlock_file (exp_vpath);
979 if (edit->locked)
980 edit->locked = unlock_file (edit->filename_vpath);
982 else if (edit->locked || save_lock)
983 edit->locked = unlock_file (edit->filename_vpath);
985 edit_set_filename (edit, exp_vpath);
986 if (edit->lb != LB_ASIS)
987 edit_reload (edit, exp_vpath);
988 edit->modified = 0;
989 edit->delete_file = 0;
990 if (different_filename)
991 edit_load_syntax (edit, NULL, edit->syntax_type);
992 ret = TRUE;
993 break;
995 default:
996 edit_error_dialog (_("Save as"), get_sys_error (_("Cannot save file")));
997 MC_FALLTHROUGH;
999 case -1:
1000 /* Failed, so maintain modify (not save) lock */
1001 if (save_lock)
1002 unlock_file (exp_vpath);
1003 break;
1007 ret:
1008 vfs_path_free (exp_vpath, TRUE);
1009 edit->force |= REDRAW_COMPLETELY;
1010 return ret;
1013 /* --------------------------------------------------------------------------------------------- */
1014 /** returns TRUE on success */
1016 gboolean
1017 edit_save_confirm_cmd (WEdit * edit)
1019 if (edit->filename_vpath == NULL)
1020 return edit_save_as_cmd (edit);
1022 if (!edit_check_newline (&edit->buffer))
1023 return FALSE;
1025 if (edit_options.confirm_save)
1027 char *f;
1028 gboolean ok;
1030 f = g_strdup_printf (_("Confirm save file: \"%s\""),
1031 vfs_path_as_str (edit->filename_vpath));
1032 ok = (edit_query_dialog2 (_("Save file"), f, _("&Save"), _("&Cancel")) == 0);
1033 g_free (f);
1034 if (!ok)
1035 return FALSE;
1038 return edit_save_cmd (edit);
1041 /* --------------------------------------------------------------------------------------------- */
1043 * Ask file to edit and load it.
1045 * @return TRUE on success or cancel of ask.
1048 gboolean
1049 edit_load_cmd (WDialog * h)
1051 char *exp;
1052 gboolean ret = TRUE; /* possible cancel */
1054 exp = input_expand_dialog (_("Load"), _("Enter file name:"),
1055 MC_HISTORY_EDIT_LOAD, INPUT_LAST_TEXT,
1056 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
1058 if (exp != NULL && *exp != '\0')
1060 vfs_path_t *exp_vpath;
1061 edit_arg_t arg;
1063 exp_vpath = vfs_path_from_str (exp);
1064 edit_arg_init (&arg, exp_vpath, 0);
1065 ret = edit_load_file_from_filename (h, &arg);
1066 vfs_path_free (exp_vpath, TRUE);
1069 g_free (exp);
1071 return ret;
1074 /* --------------------------------------------------------------------------------------------- */
1076 * Load file content
1078 * @param h screen the owner of editor window
1079 * @param vpath vfs file path
1080 * @param line line number
1082 * @return TRUE if file content was successfully loaded, FALSE otherwise
1085 gboolean
1086 edit_load_file_from_filename (WDialog * h, const edit_arg_t * arg)
1088 WRect r = WIDGET (h)->rect;
1090 rect_grow (&r, -1, 0);
1092 return edit_add_window (h, &r, arg);
1095 /* --------------------------------------------------------------------------------------------- */
1097 * Show history od edited or viewed files and open selected file.
1099 * @return TRUE on success, FALSE otherwise.
1102 gboolean
1103 edit_load_file_from_history (WDialog * h)
1105 char *exp;
1106 int action;
1107 gboolean ret = TRUE; /* possible cancel */
1109 exp = show_file_history (CONST_WIDGET (h), &action);
1110 if (exp != NULL && (action == CK_Edit || action == CK_Enter))
1112 vfs_path_t *exp_vpath;
1113 edit_arg_t arg;
1115 exp_vpath = vfs_path_from_str (exp);
1116 edit_arg_init (&arg, exp_vpath, 0);
1117 ret = edit_load_file_from_filename (h, &arg);
1118 vfs_path_free (exp_vpath, TRUE);
1121 g_free (exp);
1123 return ret;
1126 /* --------------------------------------------------------------------------------------------- */
1128 * Load syntax file to edit.
1130 * @return TRUE on success
1133 gboolean
1134 edit_load_syntax_file (WDialog * h)
1136 vfs_path_t *extdir_vpath;
1137 int dir = 0;
1138 edit_arg_t arg;
1139 gboolean ret = FALSE;
1141 if (geteuid () == 0)
1142 dir = query_dialog (_("Syntax file edit"),
1143 _("Which syntax file you want to edit?"), D_NORMAL, 2,
1144 _("&User"), _("&System wide"));
1146 extdir_vpath =
1147 vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_SYNTAX_FILE, (char *) NULL);
1148 if (!exist_file (vfs_path_get_last_path_str (extdir_vpath)))
1150 vfs_path_free (extdir_vpath, TRUE);
1151 extdir_vpath =
1152 vfs_path_build_filename (mc_global.share_data_dir, EDIT_SYNTAX_FILE, (char *) NULL);
1155 if (dir == 0)
1157 vfs_path_t *user_syntax_file_vpath;
1159 user_syntax_file_vpath = mc_config_get_full_vpath (EDIT_SYNTAX_FILE);
1160 check_for_default (extdir_vpath, user_syntax_file_vpath);
1161 edit_arg_init (&arg, user_syntax_file_vpath, 0);
1162 ret = edit_load_file_from_filename (h, &arg);
1163 vfs_path_free (user_syntax_file_vpath, TRUE);
1165 else if (dir == 1)
1167 edit_arg_init (&arg, extdir_vpath, 0);
1168 ret = edit_load_file_from_filename (h, &arg);
1171 vfs_path_free (extdir_vpath, TRUE);
1173 return ret;
1176 /* --------------------------------------------------------------------------------------------- */
1178 * Load menu file to edit.
1180 * @return TRUE on success
1183 gboolean
1184 edit_load_menu_file (WDialog * h)
1186 vfs_path_t *buffer_vpath;
1187 vfs_path_t *menufile_vpath;
1188 int dir;
1189 edit_arg_t arg;
1190 gboolean ret;
1192 query_set_sel (1);
1193 dir = query_dialog (_("Menu edit"),
1194 _("Which menu file do you want to edit?"), D_NORMAL,
1195 geteuid () != 0 ? 2 : 3, _("&Local"), _("&User"), _("&System wide"));
1197 menufile_vpath =
1198 vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1199 if (!exist_file (vfs_path_get_last_path_str (menufile_vpath)))
1201 vfs_path_free (menufile_vpath, TRUE);
1202 menufile_vpath =
1203 vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1206 switch (dir)
1208 case 0:
1209 buffer_vpath = vfs_path_from_str (EDIT_LOCAL_MENU);
1210 check_for_default (menufile_vpath, buffer_vpath);
1211 chmod (vfs_path_get_last_path_str (buffer_vpath), 0600);
1212 break;
1214 case 1:
1215 buffer_vpath = mc_config_get_full_vpath (EDIT_HOME_MENU);
1216 check_for_default (menufile_vpath, buffer_vpath);
1217 break;
1219 case 2:
1220 buffer_vpath =
1221 vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1222 if (!exist_file (vfs_path_get_last_path_str (buffer_vpath)))
1224 vfs_path_free (buffer_vpath, TRUE);
1225 buffer_vpath =
1226 vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1228 break;
1230 default:
1231 vfs_path_free (menufile_vpath, TRUE);
1232 return FALSE;
1235 edit_arg_init (&arg, buffer_vpath, 0);
1236 ret = edit_load_file_from_filename (h, &arg);
1238 vfs_path_free (buffer_vpath, TRUE);
1239 vfs_path_free (menufile_vpath, TRUE);
1241 return ret;
1244 /* --------------------------------------------------------------------------------------------- */
1246 * Close window with opened file.
1248 * @return TRUE if file was closed.
1251 gboolean
1252 edit_close_cmd (WEdit * edit)
1254 gboolean ret;
1256 ret = (edit != NULL) && edit_ok_to_exit (edit);
1258 if (ret)
1260 Widget *w = WIDGET (edit);
1261 WGroup *g = w->owner;
1263 if (edit->locked != 0)
1264 unlock_file (edit->filename_vpath);
1266 group_remove_widget (w);
1267 widget_destroy (w);
1269 if (edit_widget_is_editor (CONST_WIDGET (g->current->data)))
1270 edit = EDIT (g->current->data);
1271 else
1273 edit = edit_find_editor (DIALOG (g));
1274 if (edit != NULL)
1275 widget_select (WIDGET (edit));
1279 if (edit != NULL)
1280 edit->force |= REDRAW_COMPLETELY;
1282 return ret;
1285 /* --------------------------------------------------------------------------------------------- */
1287 void
1288 edit_block_copy_cmd (WEdit * edit)
1290 off_t start_mark, end_mark, current = edit->buffer.curs1;
1291 off_t mark1 = 0, mark2 = 0;
1292 long c1 = 0, c2 = 0;
1293 off_t size;
1294 unsigned char *copy_buf;
1296 edit_update_curs_col (edit);
1297 if (!eval_marks (edit, &start_mark, &end_mark))
1298 return;
1300 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
1302 /* all that gets pushed are deletes hence little space is used on the stack */
1304 edit_push_markers (edit);
1306 if (edit->column_highlight)
1308 long col_delta;
1310 col_delta = labs (edit->column2 - edit->column1);
1311 edit_insert_column_of_text (edit, copy_buf, size, col_delta, &mark1, &mark2, &c1, &c2);
1313 else
1315 int size_orig = size;
1317 while (size-- != 0)
1318 edit_insert_ahead (edit, copy_buf[size]);
1320 /* Place cursor at the end of text selection */
1321 if (edit_options.cursor_after_inserted_block)
1322 edit_cursor_move (edit, size_orig);
1325 g_free (copy_buf);
1326 edit_scroll_screen_over_cursor (edit);
1328 if (edit->column_highlight)
1329 edit_set_markers (edit, edit->buffer.curs1, mark2, c1, c2);
1330 else if (start_mark < current && end_mark > current)
1331 edit_set_markers (edit, start_mark, end_mark + end_mark - start_mark, 0, 0);
1333 edit->force |= REDRAW_PAGE;
1337 /* --------------------------------------------------------------------------------------------- */
1339 void
1340 edit_block_move_cmd (WEdit * edit)
1342 off_t current;
1343 unsigned char *copy_buf = NULL;
1344 off_t start_mark, end_mark;
1346 if (!eval_marks (edit, &start_mark, &end_mark))
1347 return;
1349 if (!edit->column_highlight && edit->buffer.curs1 > start_mark && edit->buffer.curs1 < end_mark)
1350 return;
1352 if (edit->mark2 < 0)
1353 edit_mark_cmd (edit, FALSE);
1354 edit_push_markers (edit);
1356 if (edit->column_highlight)
1358 off_t mark1, mark2;
1359 off_t size;
1360 long c1, c2, b_width;
1361 long x, x2;
1362 off_t b1, b2;
1364 c1 = MIN (edit->column1, edit->column2);
1365 c2 = MAX (edit->column1, edit->column2);
1366 b_width = c2 - c1;
1368 edit_update_curs_col (edit);
1370 x = edit->curs_col;
1371 x2 = x + edit->over_col;
1373 /* do nothing when cursor inside first line of selected area */
1374 b1 = edit_buffer_get_eol (&edit->buffer, edit->buffer.curs1);
1375 b2 = edit_buffer_get_eol (&edit->buffer, start_mark);
1376 if (b1 == b2 && x2 > c1 && x2 <= c2)
1377 return;
1379 if (edit->buffer.curs1 > start_mark
1380 && edit->buffer.curs1 < edit_buffer_get_eol (&edit->buffer, end_mark))
1382 if (x > c2)
1383 x -= b_width;
1384 else if (x > c1 && x <= c2)
1385 x = c1;
1387 /* save current selection into buffer */
1388 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
1390 /* remove current selection */
1391 edit_block_delete_cmd (edit);
1393 edit->over_col = MAX (0, edit->over_col - b_width);
1394 /* calculate the cursor pos after delete block */
1395 b1 = edit_buffer_get_current_bol (&edit->buffer);
1396 current = edit_move_forward3 (edit, b1, x, 0);
1397 edit_cursor_move (edit, current - edit->buffer.curs1);
1398 edit_scroll_screen_over_cursor (edit);
1400 /* add TWS if need before block insertion */
1401 if (edit_options.cursor_beyond_eol && edit->over_col > 0)
1402 edit_insert_over (edit);
1404 edit_insert_column_of_text (edit, copy_buf, size, b_width, &mark1, &mark2, &c1, &c2);
1405 edit_set_markers (edit, mark1, mark2, c1, c2);
1407 else
1409 off_t count, count_orig;
1410 off_t x;
1412 current = edit->buffer.curs1;
1413 copy_buf = g_malloc0 (end_mark - start_mark);
1414 edit_cursor_move (edit, start_mark - edit->buffer.curs1);
1415 edit_scroll_screen_over_cursor (edit);
1417 for (count = start_mark; count < end_mark; count++)
1418 copy_buf[end_mark - count - 1] = edit_delete (edit, TRUE);
1420 edit_scroll_screen_over_cursor (edit);
1421 x = current > edit->buffer.curs1 ? end_mark - start_mark : 0;
1422 edit_cursor_move (edit, current - edit->buffer.curs1 - x);
1423 edit_scroll_screen_over_cursor (edit);
1424 count_orig = count;
1425 while (count-- > start_mark)
1426 edit_insert_ahead (edit, copy_buf[end_mark - count - 1]);
1428 edit_set_markers (edit, edit->buffer.curs1, edit->buffer.curs1 + end_mark - start_mark, 0,
1431 /* Place cursor at the end of text selection */
1432 if (edit_options.cursor_after_inserted_block)
1433 edit_cursor_move (edit, count_orig - start_mark);
1436 edit_scroll_screen_over_cursor (edit);
1437 g_free (copy_buf);
1438 edit->force |= REDRAW_PAGE;
1441 /* --------------------------------------------------------------------------------------------- */
1442 /** returns FALSE if canceelled by user */
1444 gboolean
1445 edit_block_delete_cmd (WEdit * edit)
1447 off_t start_mark, end_mark;
1449 if (eval_marks (edit, &start_mark, &end_mark))
1450 return edit_block_delete (edit, start_mark, end_mark);
1452 edit_delete_line (edit);
1454 return TRUE;
1457 /* --------------------------------------------------------------------------------------------- */
1459 * Check if it's OK to close the file. If there are unsaved changes, ask user.
1461 * @return TRUE if it's OK to exit, FALSE to continue editing.
1464 gboolean
1465 edit_ok_to_exit (WEdit * edit)
1467 const char *fname = N_("[NoName]");
1468 char *msg;
1469 int act;
1471 if (!edit->modified)
1472 return TRUE;
1474 if (edit->filename_vpath != NULL)
1475 fname = vfs_path_as_str (edit->filename_vpath);
1476 #ifdef ENABLE_NLS
1477 else
1478 fname = _(fname);
1479 #endif
1481 if (!mc_global.midnight_shutdown)
1483 query_set_sel (2);
1485 msg = g_strdup_printf (_("File %s was modified.\nSave before close?"), fname);
1486 act = edit_query_dialog3 (_("Close file"), msg, _("&Yes"), _("&No"), _("&Cancel"));
1488 else
1490 msg = g_strdup_printf (_("Midnight Commander is being shut down.\nSave modified file %s?"),
1491 fname);
1492 act = edit_query_dialog2 (_("Quit"), msg, _("&Yes"), _("&No"));
1494 /* Esc is No */
1495 if (act == -1)
1496 act = 1;
1499 g_free (msg);
1501 switch (act)
1503 case 0: /* Yes */
1504 if (!mc_global.midnight_shutdown && !edit_check_newline (&edit->buffer))
1505 return FALSE;
1506 edit_push_markers (edit);
1507 edit_set_markers (edit, 0, 0, 0, 0);
1508 if (!edit_save_cmd (edit) || mc_global.midnight_shutdown)
1509 return mc_global.midnight_shutdown;
1510 break;
1511 case 1: /* No */
1512 default:
1513 break;
1514 case 2: /* Cancel quit */
1515 case -1: /* Esc */
1516 return FALSE;
1519 return TRUE;
1522 /* --------------------------------------------------------------------------------------------- */
1523 /** save block, returns TRUE on success */
1525 gboolean
1526 edit_save_block (WEdit * edit, const char *filename, off_t start, off_t finish)
1528 int file;
1529 off_t len = 1;
1530 vfs_path_t *vpath;
1532 vpath = vfs_path_from_str (filename);
1533 file = mc_open (vpath, O_CREAT | O_WRONLY | O_TRUNC,
1534 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
1535 vfs_path_free (vpath, TRUE);
1536 if (file == -1)
1537 return FALSE;
1539 if (edit->column_highlight)
1541 int r;
1543 r = mc_write (file, VERTICAL_MAGIC, sizeof (VERTICAL_MAGIC));
1544 if (r > 0)
1546 unsigned char *block, *p;
1548 p = block = edit_get_block (edit, start, finish, &len);
1549 while (len != 0)
1551 r = mc_write (file, p, len);
1552 if (r < 0)
1553 break;
1554 p += r;
1555 len -= r;
1557 g_free (block);
1560 else
1562 unsigned char *buf;
1563 off_t i = start;
1565 len = finish - start;
1566 buf = g_malloc0 (TEMP_BUF_LEN);
1567 while (start != finish)
1569 off_t end;
1571 end = MIN (finish, start + TEMP_BUF_LEN);
1572 for (; i < end; i++)
1573 buf[i - start] = edit_buffer_get_byte (&edit->buffer, i);
1574 len -= mc_write (file, (char *) buf, end - start);
1575 start = end;
1577 g_free (buf);
1579 mc_close (file);
1581 return (len == 0);
1584 /* --------------------------------------------------------------------------------------------- */
1586 void
1587 edit_paste_from_history (WEdit * edit)
1589 (void) edit;
1590 edit_error_dialog (_("Error"), _("This function is not implemented"));
1593 /* --------------------------------------------------------------------------------------------- */
1595 gboolean
1596 edit_copy_to_X_buf_cmd (WEdit * edit)
1598 off_t start_mark, end_mark;
1600 if (!eval_marks (edit, &start_mark, &end_mark))
1601 return TRUE;
1603 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark))
1605 edit_error_dialog (_("Copy to clipboard"), get_sys_error (_("Unable to save to file")));
1606 return FALSE;
1608 /* try use external clipboard utility */
1609 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
1611 if (edit_options.drop_selection_on_copy)
1612 edit_mark_cmd (edit, TRUE);
1614 return TRUE;
1617 /* --------------------------------------------------------------------------------------------- */
1619 gboolean
1620 edit_cut_to_X_buf_cmd (WEdit * edit)
1622 off_t start_mark, end_mark;
1624 if (!eval_marks (edit, &start_mark, &end_mark))
1625 return TRUE;
1627 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark))
1629 edit_error_dialog (_("Cut to clipboard"), _("Unable to save to file"));
1630 return FALSE;
1632 /* try use external clipboard utility */
1633 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
1635 edit_block_delete_cmd (edit);
1636 edit_mark_cmd (edit, TRUE);
1638 return TRUE;
1641 /* --------------------------------------------------------------------------------------------- */
1643 gboolean
1644 edit_paste_from_X_buf_cmd (WEdit * edit)
1646 vfs_path_t *tmp;
1647 gboolean ret;
1649 /* try use external clipboard utility */
1650 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
1651 tmp = mc_config_get_full_vpath (EDIT_HOME_CLIP_FILE);
1652 ret = (edit_insert_file (edit, tmp) >= 0);
1653 vfs_path_free (tmp, TRUE);
1655 return ret;
1658 /* --------------------------------------------------------------------------------------------- */
1660 * Ask user for the line and go to that line.
1661 * Negative numbers mean line from the end (i.e. -1 is the last line).
1664 void
1665 edit_goto_cmd (WEdit * edit)
1667 static gboolean first_run = TRUE;
1669 char *f;
1670 long l;
1671 char *error;
1673 f = input_dialog (_("Goto line"), _("Enter line:"), MC_HISTORY_EDIT_GOTO_LINE,
1674 first_run ? NULL : INPUT_LAST_TEXT, INPUT_COMPLETE_NONE);
1675 if (f == NULL || *f == '\0')
1677 g_free (f);
1678 return;
1681 l = strtol (f, &error, 0);
1682 if (*error != '\0')
1684 g_free (f);
1685 return;
1688 if (l < 0)
1689 l = edit->buffer.lines + l + 2;
1691 edit_move_display (edit, l - WIDGET (edit)->rect.lines / 2 - 1);
1692 edit_move_to_line (edit, l - 1);
1693 edit->force |= REDRAW_COMPLETELY;
1695 g_free (f);
1696 first_run = FALSE;
1699 /* --------------------------------------------------------------------------------------------- */
1700 /** Return TRUE on success */
1702 gboolean
1703 edit_save_block_cmd (WEdit * edit)
1705 off_t start_mark, end_mark;
1706 char *exp, *tmp;
1707 gboolean ret = FALSE;
1709 if (!eval_marks (edit, &start_mark, &end_mark))
1710 return TRUE;
1712 tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
1713 exp =
1714 input_expand_dialog (_("Save block"), _("Enter file name:"),
1715 MC_HISTORY_EDIT_SAVE_BLOCK, tmp, INPUT_COMPLETE_FILENAMES);
1716 g_free (tmp);
1717 edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
1719 if (exp != NULL && *exp != '\0')
1721 if (edit_save_block (edit, exp, start_mark, end_mark))
1722 ret = TRUE;
1723 else
1724 edit_error_dialog (_("Save block"), get_sys_error (_("Cannot save file")));
1726 edit->force |= REDRAW_COMPLETELY;
1729 g_free (exp);
1731 return ret;
1734 /* --------------------------------------------------------------------------------------------- */
1736 /** returns TRUE on success */
1737 gboolean
1738 edit_insert_file_cmd (WEdit * edit)
1740 char *tmp;
1741 char *exp;
1742 gboolean ret = FALSE;
1744 tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
1745 exp = input_expand_dialog (_("Insert file"), _("Enter file name:"),
1746 MC_HISTORY_EDIT_INSERT_FILE, tmp, INPUT_COMPLETE_FILENAMES);
1747 g_free (tmp);
1749 edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
1751 if (exp != NULL && *exp != '\0')
1753 vfs_path_t *exp_vpath;
1755 exp_vpath = vfs_path_from_str (exp);
1756 ret = (edit_insert_file (edit, exp_vpath) >= 0);
1757 vfs_path_free (exp_vpath, TRUE);
1759 if (!ret)
1760 edit_error_dialog (_("Insert file"), get_sys_error (_("Cannot insert file")));
1763 g_free (exp);
1765 edit->force |= REDRAW_COMPLETELY;
1766 return ret;
1769 /* --------------------------------------------------------------------------------------------- */
1770 /** sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */
1773 edit_sort_cmd (WEdit * edit)
1775 char *exp, *tmp, *tmp_edit_block_name, *tmp_edit_temp_name;
1776 off_t start_mark, end_mark;
1777 int e;
1779 if (!eval_marks (edit, &start_mark, &end_mark))
1781 edit_error_dialog (_("Sort block"), _("You must first highlight a block of text"));
1782 return 0;
1785 tmp = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
1786 edit_save_block (edit, tmp, start_mark, end_mark);
1787 g_free (tmp);
1789 exp = input_dialog (_("Run sort"),
1790 _("Enter sort options (see sort(1) manpage) separated by whitespace:"),
1791 MC_HISTORY_EDIT_SORT, INPUT_LAST_TEXT, INPUT_COMPLETE_NONE);
1793 if (exp == NULL)
1794 return 1;
1796 tmp_edit_block_name = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
1797 tmp_edit_temp_name = mc_config_get_full_path (EDIT_HOME_TEMP_FILE);
1798 tmp =
1799 g_strconcat (" sort ", exp, " ", tmp_edit_block_name,
1800 " > ", tmp_edit_temp_name, (char *) NULL);
1801 g_free (tmp_edit_temp_name);
1802 g_free (tmp_edit_block_name);
1803 g_free (exp);
1805 e = system (tmp);
1806 g_free (tmp);
1807 if (e != 0)
1809 if (e == -1 || e == 127)
1810 edit_error_dialog (_("Sort"), get_sys_error (_("Cannot execute sort command")));
1811 else
1813 char q[8];
1815 sprintf (q, "%d ", e);
1816 tmp = g_strdup_printf (_("Sort returned non-zero: %s"), q);
1817 edit_error_dialog (_("Sort"), tmp);
1818 g_free (tmp);
1821 return -1;
1824 edit->force |= REDRAW_COMPLETELY;
1826 if (!edit_block_delete_cmd (edit))
1827 return 1;
1830 vfs_path_t *tmp_vpath;
1832 tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE);
1833 edit_insert_file (edit, tmp_vpath);
1834 vfs_path_free (tmp_vpath, TRUE);
1837 return 0;
1840 /* --------------------------------------------------------------------------------------------- */
1842 * Ask user for a command, execute it and paste its output back to the
1843 * editor.
1847 edit_ext_cmd (WEdit * edit)
1849 char *exp, *tmp, *tmp_edit_temp_file;
1850 int e;
1852 exp =
1853 input_dialog (_("Paste output of external command"),
1854 _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, INPUT_LAST_TEXT,
1855 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES
1856 | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS |
1857 INPUT_COMPLETE_SHELL_ESC);
1859 if (!exp)
1860 return 1;
1862 tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE);
1863 tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL);
1864 g_free (tmp_edit_temp_file);
1865 e = system (tmp);
1866 g_free (tmp);
1867 g_free (exp);
1869 if (e != 0)
1871 edit_error_dialog (_("External command"), get_sys_error (_("Cannot execute command")));
1872 return -1;
1875 edit->force |= REDRAW_COMPLETELY;
1878 vfs_path_t *tmp_vpath;
1880 tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE);
1881 edit_insert_file (edit, tmp_vpath);
1882 vfs_path_free (tmp_vpath, TRUE);
1885 return 0;
1888 /* --------------------------------------------------------------------------------------------- */
1889 /** if block is 1, a block must be highlighted and the shell command
1890 processes it. If block is 0 the shell command is a straight system
1891 command, that just produces some output which is to be inserted */
1893 void
1894 edit_block_process_cmd (WEdit * edit, int macro_number)
1896 char *fname;
1897 char *macros_fname = NULL;
1899 fname = g_strdup_printf ("%s.%i.sh", EDIT_HOME_MACRO_FILE, macro_number);
1900 macros_fname = g_build_filename (mc_config_get_data_path (), fname, (char *) NULL);
1901 edit_user_menu (edit, macros_fname, 0);
1902 g_free (fname);
1903 g_free (macros_fname);
1904 edit->force |= REDRAW_COMPLETELY;
1907 /* --------------------------------------------------------------------------------------------- */
1909 void
1910 edit_mail_dialog (WEdit * edit)
1912 char *mail_to, *mail_subject, *mail_cc;
1914 quick_widget_t quick_widgets[] = {
1915 /* *INDENT-OFF* */
1916 QUICK_LABEL (N_("mail -s <subject> -c <cc> <to>"), NULL),
1917 QUICK_LABELED_INPUT (N_("To"), input_label_above,
1918 INPUT_LAST_TEXT, "mail-dlg-input-3",
1919 &mail_to, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES),
1920 QUICK_LABELED_INPUT (N_("Subject"), input_label_above,
1921 INPUT_LAST_TEXT, "mail-dlg-input-2",
1922 &mail_subject, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE),
1923 QUICK_LABELED_INPUT (N_("Copies to"), input_label_above,
1924 INPUT_LAST_TEXT, "mail-dlg-input",
1925 &mail_cc, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES),
1926 QUICK_BUTTONS_OK_CANCEL,
1927 QUICK_END
1928 /* *INDENT-ON* */
1931 WRect r = { -1, -1, 0, 50 };
1933 quick_dialog_t qdlg = {
1934 r, N_("Mail"), "[Input Line Keys]",
1935 quick_widgets, NULL, NULL
1938 if (quick_dialog (&qdlg) != B_CANCEL)
1940 pipe_mail (&edit->buffer, mail_to, mail_subject, mail_cc);
1941 g_free (mail_to);
1942 g_free (mail_subject);
1943 g_free (mail_cc);
1947 /* --------------------------------------------------------------------------------------------- */
1949 #ifdef HAVE_CHARSET
1950 void
1951 edit_select_codepage_cmd (WEdit * edit)
1953 if (do_select_codepage ())
1954 edit_set_codeset (edit);
1956 edit->force = REDRAW_PAGE;
1957 widget_draw (WIDGET (edit));
1959 #endif
1961 /* --------------------------------------------------------------------------------------------- */
1963 void
1964 edit_insert_literal_cmd (WEdit * edit)
1966 int char_for_insertion;
1968 char_for_insertion = editcmd_dialog_raw_key_query (_("Insert literal"),
1969 _("Press any key:"), FALSE);
1970 edit_execute_key_command (edit, -1, ascii_alpha_to_cntrl (char_for_insertion));
1973 /* --------------------------------------------------------------------------------------------- */
1975 gboolean
1976 edit_load_forward_cmd (WEdit * edit)
1978 if (edit->modified
1979 && edit_query_dialog2 (_("Warning"),
1980 _("Current text was modified without a file save.\n"
1981 "Continue discards these changes."), _("C&ontinue"),
1982 _("&Cancel")) == 1)
1984 edit->force |= REDRAW_COMPLETELY;
1985 return TRUE;
1988 if (edit_stack_iterator + 1 >= MAX_HISTORY_MOVETO)
1989 return FALSE;
1991 if (edit_history_moveto[edit_stack_iterator + 1].line_number < 1)
1992 return FALSE;
1994 edit_stack_iterator++;
1995 if (edit_history_moveto[edit_stack_iterator].file_vpath != NULL)
1996 return edit_reload_line (edit, &edit_history_moveto[edit_stack_iterator]);
1998 return FALSE;
2001 /* --------------------------------------------------------------------------------------------- */
2003 gboolean
2004 edit_load_back_cmd (WEdit * edit)
2006 if (edit->modified
2007 && edit_query_dialog2 (_("Warning"),
2008 _("Current text was modified without a file save.\n"
2009 "Continue discards these changes."), _("C&ontinue"),
2010 _("&Cancel")) == 1)
2012 edit->force |= REDRAW_COMPLETELY;
2013 return TRUE;
2016 /* we are in the bottom of the stack, NO WAY! */
2017 if (edit_stack_iterator == 0)
2018 return FALSE;
2020 edit_stack_iterator--;
2021 if (edit_history_moveto[edit_stack_iterator].file_vpath != NULL)
2022 return edit_reload_line (edit, &edit_history_moveto[edit_stack_iterator]);
2024 return FALSE;
2027 /* --------------------------------------------------------------------------------------------- */
2029 /* gets a raw key from the keyboard. Passing cancel = 1 draws
2030 a cancel button thus allowing c-c etc. Alternatively, cancel = 0
2031 will return the next key pressed. ctrl-a (=B_CANCEL), ctrl-g, ctrl-c,
2032 and Esc are cannot returned */
2035 editcmd_dialog_raw_key_query (const char *heading, const char *query, gboolean cancel)
2037 int w, wq;
2038 int y = 2;
2039 WDialog *raw_dlg;
2040 WGroup *g;
2042 w = str_term_width1 (heading) + 6;
2043 wq = str_term_width1 (query);
2044 w = MAX (w, wq + 3 * 2 + 1 + 2);
2046 raw_dlg =
2047 dlg_create (TRUE, 0, 0, cancel ? 7 : 5, w, WPOS_CENTER | WPOS_TRYUP, FALSE, dialog_colors,
2048 editcmd_dialog_raw_key_query_cb, NULL, NULL, heading);
2049 g = GROUP (raw_dlg);
2050 widget_want_tab (WIDGET (raw_dlg), TRUE);
2052 group_add_widget (g, label_new (y, 3, query));
2053 group_add_widget (g,
2054 input_new (y++, 3 + wq + 1, input_colors, w - (6 + wq + 1), "", 0,
2055 INPUT_COMPLETE_NONE));
2056 if (cancel)
2058 group_add_widget (g, hline_new (y++, -1, -1));
2059 /* Button w/o hotkey to allow use any key as raw or macro one */
2060 group_add_widget_autopos (g, button_new (y, 1, B_CANCEL, NORMAL_BUTTON, _("Cancel"), NULL),
2061 WPOS_KEEP_TOP | WPOS_CENTER_HORZ, NULL);
2064 w = dlg_run (raw_dlg);
2065 widget_destroy (WIDGET (raw_dlg));
2067 return (cancel && (w == ESC_CHAR || w == B_CANCEL)) ? 0 : w;
2070 /* --------------------------------------------------------------------------------------------- */