2 Editor initialisation and callback handler.
4 Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2004, 2005, 2006,
6 The Free Software Foundation, Inc.
11 This file is part of the Midnight Commander.
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.
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.
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/>.
28 * \brief Source: editor initialisation and callback handler
37 #include <sys/types.h>
45 #include "lib/global.h"
47 #include "lib/tty/tty.h" /* LINES, COLS */
48 #include "lib/tty/key.h" /* is_idle() */
49 #include "lib/tty/color.h" /* tty_setcolor() */
50 #include "lib/skin.h" /* EDITOR_NORMAL_COLOR */
51 #include "lib/strutil.h" /* str_term_trim() */
52 #include "lib/util.h" /* mc_build_filename() */
53 #include "lib/widget.h"
54 #include "lib/mcconfig.h"
56 #include "src/keybind-defaults.h"
57 #include "src/main.h" /* home_dir */
59 #include "edit-impl.h"
60 #include "editwidget.h"
62 /*** global variables ****************************************************************************/
64 /*** file scope macro definitions ****************************************************************/
66 #define WINDOW_MIN_LINES (2 + 2)
67 #define WINDOW_MIN_COLS (2 + LINE_STATE_WIDTH)
69 /*** file scope type declarations ****************************************************************/
71 /*** file scope variables ************************************************************************/
73 /*** file scope functions ************************************************************************/
75 static cb_ret_t
edit_callback (Widget
* w
, widget_msg_t msg
, int parm
);
77 /* --------------------------------------------------------------------------------------------- */
79 * Restore saved window size.
81 * @param edit editor object
85 edit_restore_size (WEdit
* edit
)
87 edit
->drag_state
= MCEDIT_DRAG_NORMAL
;
88 widget_set_size ((Widget
*) edit
, edit
->y_prev
, edit
->x_prev
,
89 edit
->lines_prev
, edit
->cols_prev
);
90 dlg_redraw (((Widget
*) edit
)->owner
);
93 /* --------------------------------------------------------------------------------------------- */
95 * Move window by one row or column in any direction.
97 * @param edit editor object
98 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
102 edit_window_move (WEdit
* edit
, unsigned long command
)
104 Widget
*w
= (Widget
*) edit
;
105 Dlg_head
*h
= w
->owner
;
110 if (w
->y
> h
->y
+ 1) /* menubar */
114 if (w
->y
< h
->y
+ h
->lines
- 2) /* buttonbar */
118 if (w
->x
+ w
->cols
> h
->x
)
122 if (w
->x
< h
->x
+ h
->cols
)
129 edit
->force
|= REDRAW_PAGE
;
133 /* --------------------------------------------------------------------------------------------- */
135 * Resize window by one row or column in any direction.
137 * @param edit editor object
138 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
142 edit_window_resize (WEdit
* edit
, unsigned long command
)
144 Widget
*w
= (Widget
*) edit
;
145 Dlg_head
*h
= w
->owner
;
150 if (w
->lines
> WINDOW_MIN_LINES
)
154 if (w
->y
+ w
->lines
< h
->y
+ h
->lines
- 1) /* buttonbar */
158 if (w
->cols
> WINDOW_MIN_COLS
)
162 if (w
->x
+ w
->cols
< h
->x
+ h
->cols
)
169 edit
->force
|= REDRAW_COMPLETELY
;
173 /* --------------------------------------------------------------------------------------------- */
176 edit_get_shortcut (unsigned long command
)
179 const char *shortcut
= NULL
;
181 shortcut
= keybind_lookup_keymap_shortcut (editor_map
, command
);
182 if (shortcut
!= NULL
)
183 return g_strdup (shortcut
);
185 ext_map
= keybind_lookup_keymap_shortcut (editor_map
, CK_ExtendedKeyMap
);
187 shortcut
= keybind_lookup_keymap_shortcut (editor_x_map
, command
);
188 if (shortcut
!= NULL
)
189 return g_strdup_printf ("%s %s", ext_map
, shortcut
);
194 /* --------------------------------------------------------------------------------------------- */
197 edit_get_title (const Dlg_head
* h
, size_t len
)
199 const WEdit
*edit
= (const WEdit
*) find_widget_type (h
, edit_callback
);
200 const char *modified
= edit
->modified
? "(*) " : " ";
201 const char *file_label
;
206 filename
= vfs_path_to_str (edit
->filename_vpath
);
207 file_label
= str_term_trim (filename
, len
- str_term_width1 (_("Edit: ")));
210 return g_strconcat (_("Edit: "), modified
, file_label
, (char *) NULL
);
213 /* --------------------------------------------------------------------------------------------- */
216 edit_event (Gpm_Event
* event
, void *data
)
218 WEdit
*edit
= (WEdit
*) data
;
219 Widget
*w
= (Widget
*) data
;
222 if (!mouse_global_in_widget (event
, w
))
223 return MOU_UNHANDLED
;
225 local
= mouse_get_local (event
, w
);
227 /* Unknown event type */
228 if ((event
->type
& (GPM_DOWN
| GPM_DRAG
| GPM_UP
)) == 0)
231 edit_update_curs_row (edit
);
232 edit_update_curs_col (edit
);
234 if (!EDIT_WITH_FRAME
|| (local
.buttons
& GPM_B_LEFT
) == 0 || (local
.type
& GPM_UP
) != 0)
235 edit
->drag_state
= MCEDIT_DRAG_NORMAL
;
236 else if (local
.y
== 1 && edit
->drag_state
!= MCEDIT_DRAG_RESIZE
)
238 /* click on the top line (move) */
240 /* start move; save x coordinate of mouse */
241 if ((local
.type
& GPM_DOWN
) != 0)
242 edit
->drag_state_start
= local
.x
;
245 if ((local
.type
& (GPM_DOWN
| GPM_DRAG
)) != 0)
246 edit
->drag_state
= MCEDIT_DRAG_MOVE
;
248 else if (local
.y
== w
->lines
&& local
.x
== w
->cols
)
250 /* click on bottom-right corner (resize) */
251 if ((local
.type
& (GPM_DOWN
| GPM_DRAG
)) != 0)
252 edit
->drag_state
= MCEDIT_DRAG_RESIZE
;
255 if (edit
->drag_state
== MCEDIT_DRAG_NORMAL
)
257 gboolean done
= TRUE
;
260 if ((local
.type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
))
262 edit_mark_current_word_cmd (edit
);
267 if ((local
.type
& (GPM_TRIPLE
| GPM_UP
)) == (GPM_UP
| GPM_TRIPLE
))
269 edit_mark_current_line_cmd (edit
);
274 if ((local
.buttons
& GPM_B_UP
) != 0 && (local
.type
& GPM_DOWN
) != 0)
276 edit_move_up (edit
, 2, 1);
279 if ((local
.buttons
& GPM_B_DOWN
) != 0 && (local
.type
& GPM_DOWN
) != 0)
281 edit_move_down (edit
, 2, 1);
285 /* continue handle current event */
288 /* handle DRAG mouse event, don't use standard way to continue
289 * event handling outside of widget */
294 c
= tty_get_event (event
, FALSE
, TRUE
);
295 if (c
== EV_NONE
|| c
!= EV_MOUSE
)
298 local
= mouse_get_local (event
, w
);
301 /* A lone up mustn't do anything */
302 if (edit
->mark2
!= -1 && (local
.type
& (GPM_UP
| GPM_DRAG
)) != 0)
305 if ((local
.type
& (GPM_DOWN
| GPM_UP
)) != 0)
306 edit_push_key_press (edit
);
309 if (!option_cursor_beyond_eol
)
310 edit
->prev_col
= local
.x
- edit
->start_col
- option_line_state_width
- 1;
313 long line_len
= edit_move_forward3 (edit
, edit_bol (edit
, edit
->curs1
), 0,
314 edit_eol (edit
, edit
->curs1
));
316 if (local
.x
> line_len
)
319 local
.x
- line_len
- edit
->start_col
- option_line_state_width
- 1;
320 edit
->prev_col
= line_len
;
325 edit
->prev_col
= local
.x
- option_line_state_width
- edit
->start_col
- 1;
331 if (local
.y
> (edit
->curs_row
+ 1))
332 edit_move_down (edit
, local
.y
- (edit
->curs_row
+ 1), 0);
333 else if (local
.y
< (edit
->curs_row
+ 1))
334 edit_move_up (edit
, (edit
->curs_row
+ 1) - local
.y
, 0);
336 edit_move_to_prev_col (edit
, edit_bol (edit
, edit
->curs1
));
338 if ((local
.type
& GPM_DOWN
) != 0)
340 edit_mark_cmd (edit
, 1); /* reset */
344 done
= (local
.type
& GPM_DRAG
) == 0;
346 edit_mark_cmd (edit
, 0);
349 edit_find_bracket (edit
);
350 edit
->force
|= REDRAW_COMPLETELY
;
351 edit_update_curs_row (edit
);
352 edit_update_curs_col (edit
);
353 edit_update_screen (edit
);
355 while (EDIT_WITH_FRAME
&& !done
);
358 while (edit
->drag_state
!= MCEDIT_DRAG_NORMAL
)
362 c
= tty_get_event (event
, FALSE
, TRUE
);
364 if (c
== EV_NONE
|| c
!= EV_MOUSE
)
367 edit
->drag_state
= MCEDIT_DRAG_NORMAL
;
368 edit
->force
|= REDRAW_COMPLETELY
;
369 edit_update_screen (edit
);
373 Dlg_head
*h
= w
->owner
;
375 if (edit
->drag_state
== MCEDIT_DRAG_MOVE
)
377 int y
= event
->y
- 1;
378 int x
= event
->x
- 1;
380 y
= max (y
, h
->y
+ 1); /* status line */
381 y
= min (y
, h
->y
+ h
->lines
- 2); /* buttonbar */
383 x
= min (x
, h
->x
+ h
->cols
- 1);
384 /* don't use widget_set_size() here to avoid double draw */
386 w
->x
= x
- edit
->drag_state_start
;
387 edit
->force
|= REDRAW_PAGE
;
389 else if (edit
->drag_state
== MCEDIT_DRAG_RESIZE
)
391 event
->y
= min (event
->y
, h
->y
+ h
->lines
- 1); /* buttonbar */
392 event
->x
= min (event
->x
, h
->x
+ h
->cols
);
393 local
= mouse_get_local (event
, w
);
395 /* don't use widget_set_size() here to avoid double draw */
396 w
->lines
= max (WINDOW_MIN_LINES
, local
.y
);
397 w
->cols
= max (WINDOW_MIN_COLS
, local
.x
);
398 edit
->force
|= REDRAW_COMPLETELY
;
408 /* --------------------------------------------------------------------------------------------- */
411 edit_dialog_command_execute (Dlg_head
* h
, unsigned long command
)
413 gboolean ret
= MSG_HANDLED
;
423 Widget
*w
= (Widget
*) h
->current
->data
;
425 if (!edit_widget_is_editor (w
) || ((WEdit
*) w
)->drag_state
== MCEDIT_DRAG_NORMAL
)
428 edit_restore_size ((WEdit
*) w
);
432 case CK_WindowResize
:
433 if (edit_widget_is_editor ((Widget
*) h
->current
->data
))
434 edit_handle_move_resize ((WEdit
*) h
->current
->data
, command
);
438 ret
= MSG_NOT_HANDLED
;
445 /* --------------------------------------------------------------------------------------------- */
448 edit_set_buttonbar (WEdit
* edit
, WButtonBar
* bb
)
450 buttonbar_set_label (bb
, 1, Q_ ("ButtonBar|Help"), editor_map
, (Widget
*) edit
);
451 buttonbar_set_label (bb
, 2, Q_ ("ButtonBar|Save"), editor_map
, (Widget
*) edit
);
452 buttonbar_set_label (bb
, 3, Q_ ("ButtonBar|Mark"), editor_map
, (Widget
*) edit
);
453 buttonbar_set_label (bb
, 4, Q_ ("ButtonBar|Replac"), editor_map
, (Widget
*) edit
);
454 buttonbar_set_label (bb
, 5, Q_ ("ButtonBar|Copy"), editor_map
, (Widget
*) edit
);
455 buttonbar_set_label (bb
, 6, Q_ ("ButtonBar|Move"), editor_map
, (Widget
*) edit
);
456 buttonbar_set_label (bb
, 7, Q_ ("ButtonBar|Search"), editor_map
, (Widget
*) edit
);
457 buttonbar_set_label (bb
, 8, Q_ ("ButtonBar|Delete"), editor_map
, (Widget
*) edit
);
458 buttonbar_set_label (bb
, 9, Q_ ("ButtonBar|PullDn"), editor_map
, NULL
);
459 buttonbar_set_label (bb
, 10, Q_ ("ButtonBar|Quit"), editor_map
, (Widget
*) edit
);
462 /* --------------------------------------------------------------------------------------------- */
463 /** Callback for the edit dialog */
466 edit_dialog_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
470 WButtonBar
*buttonbar
;
472 edit
= (WEdit
*) find_widget_type (h
, edit_callback
);
473 menubar
= find_menubar (h
);
474 buttonbar
= find_buttonbar (h
);
479 edit_set_buttonbar (edit
, buttonbar
);
483 /* don't use common_dialog_repaint() -- we don't need a frame */
484 tty_setcolor (EDITOR_NORMAL_COLOR
);
489 /* dlg_set_size() is surplus for this case */
492 widget_set_size (&buttonbar
->widget
, h
->lines
- 1, h
->x
, 1, h
->cols
);
493 widget_set_size (&menubar
->widget
, h
->y
, h
->x
, 1, h
->cols
);
494 menubar_arrange (menubar
);
500 return edit_dialog_command_execute (h
, parm
);
501 /* message from menu */
502 if (sender
== (Widget
*) menubar
)
504 if (edit_dialog_command_execute (h
, parm
) == MSG_HANDLED
)
506 /* try send command to the current window */
507 return send_message ((Widget
*) h
->current
->data
, WIDGET_COMMAND
, parm
);
509 /* message from buttonbar */
510 if (sender
== (Widget
*) buttonbar
)
513 return send_message ((Widget
*) data
, WIDGET_COMMAND
, parm
);
514 return edit_dialog_command_execute (h
, parm
);
516 return MSG_NOT_HANDLED
;
520 Widget
*w
= h
->current
->data
;
521 cb_ret_t ret
= MSG_NOT_HANDLED
;
523 if (edit_widget_is_editor (w
))
525 WEdit
*e
= (WEdit
*) w
;
526 unsigned long command
;
529 command
= keybind_lookup_keymap_command (editor_map
, parm
);
533 command
= keybind_lookup_keymap_command (editor_x_map
, parm
);
536 if (command
!= CK_IgnoreKey
)
537 ret
= edit_dialog_command_execute (h
, command
);
543 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */
544 case DLG_UNHANDLED_KEY
:
545 return edit_drop_hotkey_menu (h
, parm
) ? MSG_HANDLED
: MSG_NOT_HANDLED
;
548 h
->state
= DLG_ACTIVE
; /* don't stop the dialog before final decision */
549 if (edit
->drag_state
!= MCEDIT_DRAG_NORMAL
)
550 edit_restore_size (edit
);
551 else if (edit_ok_to_exit (edit
))
552 h
->state
= DLG_CLOSED
;
556 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
560 /* --------------------------------------------------------------------------------------------- */
563 edit_callback (Widget
* w
, widget_msg_t msg
, int parm
)
565 WEdit
*e
= (WEdit
*) w
;
570 e
->force
|= REDRAW_COMPLETELY
;
574 edit_update_screen (e
);
580 cb_ret_t ret
= MSG_NOT_HANDLED
;
582 /* The user may override the access-keys for the menu bar. */
583 if (macro_index
== -1 && edit_execute_macro (e
, parm
))
585 edit_update_screen (e
);
588 else if (edit_translate_key (e
, parm
, &cmd
, &ch
))
590 edit_execute_key_command (e
, cmd
, ch
);
591 edit_update_screen (e
);
599 /* command from menubar or buttonbar */
600 edit_execute_key_command (e
, parm
, -1);
601 edit_update_screen (e
);
605 widget_move (w
, e
->curs_row
+ EDIT_TEXT_VERTICAL_OFFSET
+ EDIT_WITH_FRAME
,
606 e
->curs_col
+ e
->start_col
+ e
->over_col
+
607 EDIT_TEXT_HORIZONTAL_OFFSET
+ EDIT_WITH_FRAME
+ option_line_state_width
);
615 return default_proc (msg
, parm
);
619 /* --------------------------------------------------------------------------------------------- */
620 /*** public functions ****************************************************************************/
621 /* --------------------------------------------------------------------------------------------- */
624 edit_file (const vfs_path_t
* _file_vpath
, int line
)
626 static gboolean made_directory
= FALSE
;
635 dir
= mc_build_filename (mc_config_get_cache_path (), EDIT_DIR
, NULL
);
636 made_directory
= (mkdir (dir
, 0700) != -1 || errno
== EEXIST
);
639 dir
= mc_build_filename (mc_config_get_path (), EDIT_DIR
, NULL
);
640 made_directory
= (mkdir (dir
, 0700) != -1 || errno
== EEXIST
);
643 dir
= mc_build_filename (mc_config_get_data_path (), EDIT_DIR
, NULL
);
644 made_directory
= (mkdir (dir
, 0700) != -1 || errno
== EEXIST
);
648 wedit
= edit_init (NULL
, 1, 0, LINES
- 2, COLS
, _file_vpath
, line
);
653 /* Create a new dialog and add it widgets to it */
655 create_dlg (FALSE
, 0, 0, LINES
, COLS
, NULL
, edit_dialog_callback
, NULL
,
656 "[Internal File Editor]", NULL
, DLG_WANT_TAB
);
658 edit_dlg
->get_shortcut
= edit_get_shortcut
;
659 edit_dlg
->get_title
= edit_get_title
;
661 menubar
= menubar_new (0, 0, COLS
, NULL
);
662 add_widget (edit_dlg
, menubar
);
663 edit_init_menu (menubar
);
665 init_widget (&wedit
->widget
, wedit
->widget
.y
, wedit
->widget
.x
,
666 wedit
->widget
.lines
, wedit
->widget
.cols
, edit_callback
, edit_event
);
667 widget_want_cursor (wedit
->widget
, TRUE
);
669 add_widget (edit_dlg
, wedit
);
671 add_widget (edit_dlg
, buttonbar_new (TRUE
));
675 if (edit_dlg
->state
== DLG_CLOSED
)
676 destroy_dlg (edit_dlg
);
681 /* --------------------------------------------------------------------------------------------- */
684 edit_get_file_name (const WEdit
* edit
)
686 return vfs_path_to_str (edit
->filename_vpath
);
689 /* --------------------------------------------------------------------------------------------- */
691 * Check if widget is an WEdit class.
693 * @param w probably editor object
694 * @return TRUE if widget is an WEdit class, FALSE otherwise
698 edit_widget_is_editor (const Widget
* w
)
700 return (w
!= NULL
&& w
->callback
== edit_callback
);
703 /* --------------------------------------------------------------------------------------------- */
706 edit_update_screen (WEdit
* e
)
708 edit_scroll_screen_over_cursor (e
);
709 edit_update_curs_col (e
);
711 if (!EDIT_WITH_FRAME
)
715 if ((e
->force
& REDRAW_COMPLETELY
) != 0)
717 /* draw a frame around edit area */
718 tty_setcolor (EDITOR_NORMAL_COLOR
);
719 tty_draw_box (e
->widget
.y
, e
->widget
.x
, e
->widget
.lines
, e
->widget
.cols
, TRUE
);
722 edit_info_status (e
);
725 /* pop all events for this window for internal handling */
727 e
->force
|= REDRAW_PAGE
;
730 if (e
->force
& REDRAW_COMPLETELY
)
731 e
->force
|= REDRAW_PAGE
;
732 edit_render_keypress (e
);
736 /* --------------------------------------------------------------------------------------------- */
738 * Save current window size.
740 * @param edit editor object
744 edit_save_size (WEdit
* edit
)
746 edit
->y_prev
= edit
->widget
.y
;
747 edit
->x_prev
= edit
->widget
.x
;
748 edit
->lines_prev
= edit
->widget
.lines
;
749 edit
->cols_prev
= edit
->widget
.cols
;
752 /* --------------------------------------------------------------------------------------------- */
754 * Handle move/resize events.
756 * @param edit editor object
757 * @param command action id
758 * @return TRUE if mouse actions was handled, FALSE otherwise
762 edit_handle_move_resize (WEdit
* edit
, unsigned long command
)
764 gboolean ret
= FALSE
;
766 switch (edit
->drag_state
)
768 case MCEDIT_DRAG_NORMAL
:
769 /* possible start move/resize */
773 edit
->drag_state
= MCEDIT_DRAG_MOVE
;
774 edit_save_size (edit
);
777 case CK_WindowResize
:
778 edit
->drag_state
= MCEDIT_DRAG_RESIZE
;
779 edit_save_size (edit
);
787 case MCEDIT_DRAG_MOVE
:
790 case CK_WindowResize
:
791 edit
->drag_state
= MCEDIT_DRAG_RESIZE
;
798 edit_window_move (edit
, command
);
803 edit
->drag_state
= MCEDIT_DRAG_NORMAL
;
810 case MCEDIT_DRAG_RESIZE
:
814 edit
->drag_state
= MCEDIT_DRAG_MOVE
;
821 edit_window_resize (edit
, command
);
825 case CK_WindowResize
:
826 edit
->drag_state
= MCEDIT_DRAG_NORMAL
;
837 /* --------------------------------------------------------------------------------------------- */