1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-26 12:56:37 +02:00
Files
weechat/src/gui/gui-input.c
T
2023-03-26 20:30:17 +02:00

1784 lines
50 KiB
C

/*
* gui-input.c - input functions (used by all GUI)
*
* Copyright (C) 2003-2023 Sébastien Helleu <flashcode@flashtux.org>
*
* This file is part of WeeChat, the extensible chat client.
*
* WeeChat is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* WeeChat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WeeChat. If not, see <https://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include "../core/weechat.h"
#include "../core/wee-config.h"
#include "../core/wee-hook.h"
#include "../core/wee-input.h"
#include "../core/wee-string.h"
#include "../core/wee-utf8.h"
#include "../plugins/plugin.h"
#include "gui-input.h"
#include "gui-buffer.h"
#include "gui-completion.h"
#include "gui-cursor.h"
#include "gui-history.h"
#include "gui-hotlist.h"
#include "gui-key.h"
#include "gui-line.h"
#include "gui-mouse.h"
#include "gui-window.h"
char *gui_input_clipboard = NULL; /* internal clipboard content */
/*
* Optimizes input buffer size by adding or deleting data block (predefined
* size).
*
* Returns:
* 1: input size optimized
* 0: error (input and its size are not changed)
*/
int
gui_input_optimize_size (struct t_gui_buffer *buffer,
int new_size, int new_length)
{
int optimal_size;
char *input_buffer2;
if (!buffer->input)
return 0;
optimal_size = ((new_size / GUI_BUFFER_INPUT_BLOCK_SIZE) *
GUI_BUFFER_INPUT_BLOCK_SIZE) + GUI_BUFFER_INPUT_BLOCK_SIZE;
if (buffer->input_buffer_alloc != optimal_size)
{
input_buffer2 = realloc (buffer->input_buffer, optimal_size);
if (!input_buffer2)
return 0;
buffer->input_buffer = input_buffer2;
buffer->input_buffer_alloc = optimal_size;
}
buffer->input_buffer_size = new_size;
buffer->input_buffer_length = new_length;
return 1;
}
/*
* Replaces full input by another string, trying to keep cursor position if new
* string is long enough.
*/
void
gui_input_replace_input (struct t_gui_buffer *buffer, const char *new_input)
{
int size, length;
char *input_utf8;
input_utf8 = strdup ((new_input) ? new_input : "");
if (input_utf8)
{
utf8_normalize (input_utf8, '?');
size = strlen (input_utf8);
length = utf8_strlen (input_utf8);
/* compute new buffer size */
if (gui_input_optimize_size (buffer, size, length))
{
/* copy new string to input */
strcpy (buffer->input_buffer, input_utf8);
/* move cursor to the end of new input if it is now after the end */
if (buffer->input_buffer_pos > buffer->input_buffer_length)
buffer->input_buffer_pos = buffer->input_buffer_length;
}
free (input_utf8);
}
}
/*
* Sends signal "input_paste_pending".
*/
void
gui_input_paste_pending_signal ()
{
if (CONFIG_BOOLEAN(config_look_bare_display_exit_on_input)
&& gui_window_bare_display)
{
gui_window_bare_display_toggle (NULL);
}
(void) hook_signal_send ("input_paste_pending",
WEECHAT_HOOK_SIGNAL_STRING, NULL);
}
/*
* Sends modifier and signal "input_text_changed".
*/
void
gui_input_text_changed_modifier_and_signal (struct t_gui_buffer *buffer,
int save_undo,
int stop_completion)
{
char str_buffer[128], *new_input;
if (CONFIG_BOOLEAN(config_look_bare_display_exit_on_input)
&& gui_window_bare_display)
{
gui_window_bare_display_toggle (NULL);
}
if (!gui_cursor_mode)
{
if (save_undo)
gui_buffer_undo_add (buffer);
/* send modifier, and change input if needed */
snprintf (str_buffer, sizeof (str_buffer),
"0x%lx", (unsigned long)buffer);
new_input = hook_modifier_exec (NULL,
"input_text_content",
str_buffer,
(buffer->input_buffer) ?
buffer->input_buffer : "");
if (new_input)
{
if (!buffer->input_buffer
|| strcmp (new_input, buffer->input_buffer) != 0)
{
/* input has been changed by modifier, use it */
gui_input_replace_input (buffer, new_input);
}
free (new_input);
}
}
if (stop_completion && !gui_completion_freeze)
gui_completion_stop (buffer->completion);
/* send signal */
(void) hook_signal_send ("input_text_changed",
WEECHAT_HOOK_SIGNAL_POINTER, buffer);
}
/*
* Sends signal "input_text_cursor_moved".
*/
void
gui_input_text_cursor_moved_signal (struct t_gui_buffer *buffer)
{
if (CONFIG_BOOLEAN(config_look_bare_display_exit_on_input)
&& gui_window_bare_display)
{
gui_window_bare_display_toggle (NULL);
}
(void) hook_signal_send ("input_text_cursor_moved",
WEECHAT_HOOK_SIGNAL_POINTER, buffer);
}
/*
* Sends signal "input_search".
*/
void
gui_input_search_signal (struct t_gui_buffer *buffer)
{
if (CONFIG_BOOLEAN(config_look_bare_display_exit_on_input)
&& gui_window_bare_display)
{
gui_window_bare_display_toggle (NULL);
}
(void) hook_signal_send ("input_search",
WEECHAT_HOOK_SIGNAL_POINTER, buffer);
}
/*
* Sets position in input line.
*/
void
gui_input_set_pos (struct t_gui_buffer *buffer, int pos)
{
if ((pos >= 0) && (buffer->input_buffer_pos != pos))
{
buffer->input_buffer_pos = pos;
if (buffer->input_buffer_pos > buffer->input_buffer_length)
buffer->input_buffer_pos = buffer->input_buffer_length;
gui_input_text_cursor_moved_signal (buffer);
}
}
/*
* Inserts a string into the input buffer at cursor position.
*/
void
gui_input_insert_string (struct t_gui_buffer *buffer, const char *string)
{
int size, length;
char *string_utf8, *ptr_start;
if (!buffer->input || !string)
return;
string_utf8 = strdup (string);
if (!string_utf8)
return;
utf8_normalize (string_utf8, '?');
size = strlen (string_utf8);
length = utf8_strlen (string_utf8);
if (gui_input_optimize_size (buffer,
buffer->input_buffer_size + size,
buffer->input_buffer_length + length))
{
buffer->input_buffer[buffer->input_buffer_size] = '\0';
/* move end of string to the right */
ptr_start = (char *)utf8_add_offset (buffer->input_buffer, buffer->input_buffer_pos);
memmove (ptr_start + size, ptr_start, strlen (ptr_start));
/* insert new string */
memcpy (ptr_start, string_utf8, size);
buffer->input_buffer_pos += length;
}
free (string_utf8);
}
/*
* Copies string into the internal clipboard.
*/
void
gui_input_clipboard_copy (const char *buffer, int size)
{
if (!buffer || (size <= 0))
return;
if (gui_input_clipboard != NULL)
free (gui_input_clipboard);
gui_input_clipboard = malloc ((size + 1) * sizeof (*gui_input_clipboard));
if (gui_input_clipboard)
{
memcpy (gui_input_clipboard, buffer, size);
gui_input_clipboard[size] = '\0';
}
}
/*
* Pastes the internal clipboard at cursor pos in input line
* (default key: ctrl-y).
*/
void
gui_input_clipboard_paste (struct t_gui_buffer *buffer)
{
if (buffer->input && gui_input_clipboard)
{
gui_buffer_undo_snap (buffer);
gui_input_insert_string (buffer, gui_input_clipboard);
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
}
/*
* Terminates line:
* - saves text in history
* - stops completion
* - frees all undos
* - sends modifier and signal
* - sends data to buffer.
*/
void
gui_input_return (struct t_gui_buffer *buffer)
{
char *command;
if (CONFIG_BOOLEAN(config_look_bare_display_exit_on_input)
&& gui_window_bare_display)
{
gui_window_bare_display_toggle (NULL);
}
if (buffer->input
&& (buffer->input_get_empty || (buffer->input_buffer_size > 0)))
{
buffer->input_buffer[buffer->input_buffer_size] = '\0';
command = strdup (buffer->input_buffer);
if (command)
{
gui_history_add (buffer, buffer->input_buffer);
if (gui_input_optimize_size (buffer, 0, 0))
{
buffer->input_buffer[0] = '\0';
buffer->input_buffer_pos = 0;
buffer->input_buffer_1st_display = 0;
}
gui_buffer_undo_free_all (buffer);
buffer->ptr_history = NULL;
gui_history_ptr = NULL;
gui_input_text_changed_modifier_and_signal (buffer,
0, /* save undo */
1); /* stop completion */
(void) input_data (buffer, command, NULL);
free (command);
}
}
}
/*
* Completes a word in input buffer.
*/
void
gui_input_complete (struct t_gui_buffer *buffer)
{
int i;
if (!buffer->completion || !buffer->completion->word_found)
return;
/* replace word with new completed word into input buffer */
if (buffer->completion->diff_size > 0)
{
if (gui_input_optimize_size (
buffer,
buffer->input_buffer_size + buffer->completion->diff_size,
buffer->input_buffer_length + buffer->completion->diff_length))
{
buffer->input_buffer[buffer->input_buffer_size] = '\0';
for (i = buffer->input_buffer_size - 1;
i >= buffer->completion->position_replace +
(int)strlen (buffer->completion->word_found); i--)
{
buffer->input_buffer[i] =
buffer->input_buffer[i - buffer->completion->diff_size];
}
}
}
else
{
for (i = buffer->completion->position_replace +
strlen (buffer->completion->word_found);
i < buffer->input_buffer_size; i++)
{
buffer->input_buffer[i] =
buffer->input_buffer[i - buffer->completion->diff_size];
}
if (gui_input_optimize_size (
buffer,
buffer->input_buffer_size + buffer->completion->diff_size,
buffer->input_buffer_length += buffer->completion->diff_length))
{
buffer->input_buffer[buffer->input_buffer_size] = '\0';
}
}
strncpy (buffer->input_buffer + buffer->completion->position_replace,
buffer->completion->word_found,
strlen (buffer->completion->word_found));
buffer->input_buffer_pos =
utf8_pos (buffer->input_buffer,
buffer->completion->position_replace) +
utf8_strlen (buffer->completion->word_found);
/*
* position is < 0 this means only one word was found to complete,
* so reinit to stop completion
*/
if (buffer->completion->position >= 0)
buffer->completion->position = utf8_real_pos (buffer->input_buffer,
buffer->input_buffer_pos);
/* add space if needed after completion */
if (buffer->completion->add_space)
{
if (buffer->input_buffer[utf8_real_pos (buffer->input_buffer,
buffer->input_buffer_pos)] != ' ')
{
gui_input_insert_string (buffer, " ");
}
else
buffer->input_buffer_pos++;
if (buffer->completion->position >= 0)
buffer->completion->position++;
}
}
/*
* Completes with next word (default key: tab).
*/
void
gui_input_complete_next (struct t_gui_buffer *buffer)
{
if (!buffer->input ||(buffer->text_search != GUI_TEXT_SEARCH_DISABLED))
return;
gui_buffer_undo_snap (buffer);
if (gui_completion_search (buffer->completion,
buffer->input_buffer,
buffer->input_buffer_pos,
1))
{
gui_input_complete (buffer);
gui_input_text_changed_modifier_and_signal (
buffer,
1, /* save undo */
0); /* stop completion */
}
}
/*
* Completes with previous word (default key: shift-tab).
*/
void
gui_input_complete_previous (struct t_gui_buffer *buffer)
{
if (!buffer->input || (buffer->text_search != GUI_TEXT_SEARCH_DISABLED))
return;
gui_buffer_undo_snap (buffer);
if (gui_completion_search (buffer->completion,
buffer->input_buffer,
buffer->input_buffer_pos,
-1))
{
gui_input_complete (buffer);
gui_input_text_changed_modifier_and_signal (
buffer,
1, /* save undo */
0); /* stop completion */
}
}
/*
* Searches for text in buffer at current position (default key: ctrl-r).
*/
void
gui_input_search_text_here (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window && (window->buffer->text_search == GUI_TEXT_SEARCH_DISABLED))
{
gui_window_search_start (window, window->scroll->start_line);
gui_input_search_signal (buffer);
}
}
/*
* Searches for text in buffer.
*/
void
gui_input_search_text (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window && (window->buffer->text_search == GUI_TEXT_SEARCH_DISABLED))
{
gui_window_search_start (window, NULL);
gui_input_search_signal (buffer);
}
}
/*
* Compiles regex used to search text in buffer.
*/
void
gui_input_search_compile_regex (struct t_gui_buffer *buffer)
{
int flags;
/* remove the compiled regex */
if (buffer->text_search_regex_compiled)
{
regfree (buffer->text_search_regex_compiled);
free (buffer->text_search_regex_compiled);
buffer->text_search_regex_compiled = NULL;
}
/* compile regex */
if (buffer->text_search_regex)
{
buffer->text_search_regex_compiled = malloc (sizeof (*buffer->text_search_regex_compiled));
if (buffer->text_search_regex_compiled)
{
flags = REG_EXTENDED;
if (!buffer->text_search_exact)
flags |= REG_ICASE;
if (string_regcomp (buffer->text_search_regex_compiled,
buffer->input_buffer, flags) != 0)
{
free (buffer->text_search_regex_compiled);
buffer->text_search_regex_compiled = NULL;
}
}
}
}
/*
* Switches case for search in buffer (default key: alt-c during search).
*/
void
gui_input_search_switch_case (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window && (window->buffer->text_search != GUI_TEXT_SEARCH_DISABLED))
{
window->buffer->text_search_exact ^= 1;
gui_window_search_restart (window);
gui_input_search_signal (buffer);
}
}
/*
* Switches string/regex for search in buffer (default key: ctrl-r during
* search).
*/
void
gui_input_search_switch_regex (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window && (window->buffer->text_search != GUI_TEXT_SEARCH_DISABLED))
{
window->buffer->text_search_regex ^= 1;
gui_window_search_restart (window);
gui_input_search_signal (buffer);
}
}
/*
* Switches search in messages/prefixes (default key: tab during search).
*/
void
gui_input_search_switch_where (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window && (window->buffer->text_search != GUI_TEXT_SEARCH_DISABLED))
{
/* it's not possible to change that in a buffer not "formatted" */
if (window->buffer->type != GUI_BUFFER_TYPE_FORMATTED)
return;
if (window->buffer->text_search_where == GUI_TEXT_SEARCH_IN_MESSAGE)
window->buffer->text_search_where = GUI_TEXT_SEARCH_IN_PREFIX;
else if (window->buffer->text_search_where == GUI_TEXT_SEARCH_IN_PREFIX)
window->buffer->text_search_where = GUI_TEXT_SEARCH_IN_MESSAGE | GUI_TEXT_SEARCH_IN_PREFIX;
else
window->buffer->text_search_where = GUI_TEXT_SEARCH_IN_MESSAGE;
gui_window_search_restart (window);
gui_input_search_signal (buffer);
}
}
/*
* Searches backward in buffer (default key: up during search).
*/
void
gui_input_search_previous (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window && (window->buffer->text_search != GUI_TEXT_SEARCH_DISABLED))
{
window->buffer->text_search = GUI_TEXT_SEARCH_BACKWARD;
(void) gui_window_search_text (window);
}
}
/*
* Searches forward in buffer (default key: down during search).
*/
void
gui_input_search_next (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window && (window->buffer->text_search != GUI_TEXT_SEARCH_DISABLED))
{
window->buffer->text_search = GUI_TEXT_SEARCH_FORWARD;
(void) gui_window_search_text (window);
}
}
/*
* Stops text search at current position (default key: return during search).
*/
void
gui_input_search_stop_here (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window && (window->buffer->text_search != GUI_TEXT_SEARCH_DISABLED))
{
gui_window_search_stop_here (window);
gui_input_search_signal (buffer);
}
}
/*
* Stops text search (default key: ctrl-q during search).
*/
void
gui_input_search_stop (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window && (window->buffer->text_search != GUI_TEXT_SEARCH_DISABLED))
{
gui_window_search_stop (window);
gui_input_search_signal (buffer);
}
}
/*
* Deletes previous char (default key: backspace).
*/
void
gui_input_delete_previous_char (struct t_gui_buffer *buffer)
{
char *pos, *pos_last;
int char_size, size_to_move;
if (!buffer->input || (buffer->input_buffer_pos <= 0))
return;
gui_buffer_undo_snap (buffer);
pos = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
pos_last = (char *)utf8_prev_char (buffer->input_buffer, pos);
char_size = pos - pos_last;
size_to_move = strlen (pos);
memmove (pos_last, pos, size_to_move);
if (gui_input_optimize_size (buffer,
buffer->input_buffer_size - char_size,
buffer->input_buffer_length - 1))
{
buffer->input_buffer_pos--;
buffer->input_buffer[buffer->input_buffer_size] = '\0';
}
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
/*
* Deletes next char (default key: del).
*/
void
gui_input_delete_next_char (struct t_gui_buffer *buffer)
{
char *pos, *pos_next;
int char_size, size_to_move;
if (!buffer->input
|| (buffer->input_buffer_pos >= buffer->input_buffer_length))
{
return;
}
gui_buffer_undo_snap (buffer);
pos = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
pos_next = (char *)utf8_next_char (pos);
char_size = pos_next - pos;
size_to_move = strlen (pos_next);
memmove (pos, pos_next, size_to_move);
if (gui_input_optimize_size (buffer,
buffer->input_buffer_size - char_size,
buffer->input_buffer_length - 1))
{
buffer->input_buffer[buffer->input_buffer_size] = '\0';
}
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
/*
* Delete the range between two positions and copy the content to the
* clipboard.
*/
void
gui_input_delete_range (struct t_gui_buffer *buffer,
char *start,
char *end)
{
int size_deleted, length_deleted;
size_deleted = utf8_next_char (end) - start;
length_deleted = utf8_strnlen (start, size_deleted);
gui_input_clipboard_copy (start, size_deleted);
memmove (start, start + size_deleted, strlen (start + size_deleted));
if (gui_input_optimize_size (
buffer,
buffer->input_buffer_size - size_deleted,
buffer->input_buffer_length - length_deleted))
{
buffer->input_buffer[buffer->input_buffer_size] = '\0';
buffer->input_buffer_pos -= length_deleted;
}
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
/*
* Deletes previous word (default key: alt-backspace).
*/
void
gui_input_delete_previous_word (struct t_gui_buffer *buffer)
{
char *start, *string;
if (!buffer->input || (buffer->input_buffer_pos <= 0))
return;
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos - 1);
string = start;
/* move to the left until we reach a word char */
while (string && !string_is_word_char_input (string))
{
string = (char *)utf8_prev_char (buffer->input_buffer, string);
}
/* move to the left to skip the whole word */
if (string)
{
while (string && string_is_word_char_input (string))
{
string = (char *)utf8_prev_char (buffer->input_buffer, string);
}
}
if (string)
string = (char *)utf8_next_char (string);
else
string = buffer->input_buffer;
gui_input_delete_range (buffer, string, start);
}
/*
* Deletes previous word until whitespace (default key: ctrl-w).
*/
void
gui_input_delete_previous_word_whitespace (struct t_gui_buffer *buffer)
{
char *start, *string;
if (!buffer->input || (buffer->input_buffer_pos <= 0))
return;
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos - 1);
string = start;
/* move to the left, skipping whitespace */
while (string && string_is_whitespace_char (string))
{
string = (char *)utf8_prev_char (buffer->input_buffer, string);
}
/* move to the left until we reach a char which is not whitespace */
if (string)
{
while (string && !string_is_whitespace_char (string))
{
string = (char *)utf8_prev_char (buffer->input_buffer, string);
}
}
if (string)
string = (char *)utf8_next_char (string);
else
string = buffer->input_buffer;
gui_input_delete_range (buffer, string, start);
}
/*
* Deletes next word (default key: alt-d).
*/
void
gui_input_delete_next_word (struct t_gui_buffer *buffer)
{
int size_deleted, length_deleted;
char *start, *string;
if (!buffer->input)
return;
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
string = start;
length_deleted = 0;
/* move to the right until we reach a word char */
while (string[0] && !string_is_word_char_input (string))
{
string = (char *)utf8_next_char (string);
length_deleted++;
}
/* move to the right to skip the whole word */
while (string[0] && string_is_word_char_input (string))
{
string = (char *)utf8_next_char (string);
length_deleted++;
}
size_deleted = string - start;
gui_input_clipboard_copy (start, size_deleted);
memmove (start, string, strlen (string));
if (gui_input_optimize_size (
buffer,
buffer->input_buffer_size - size_deleted,
buffer->input_buffer_length - length_deleted))
{
buffer->input_buffer[buffer->input_buffer_size] = '\0';
}
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
/*
* Deletes all from cursor pos to beginning of line (default key: ctrl-u).
*
* If cursor is at beginning of line, deletes to beginning of previous line.
*/
void
gui_input_delete_beginning_of_line (struct t_gui_buffer *buffer)
{
int length_deleted, size_deleted;
char *start, *beginning_of_line;
if (!buffer->input || (buffer->input_buffer_pos <= 0))
return;
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
beginning_of_line = (char *)utf8_beginning_of_line (buffer->input_buffer,
start);
if (beginning_of_line == start)
{
beginning_of_line = (char *)utf8_prev_char (buffer->input_buffer,
beginning_of_line);
beginning_of_line = (char *)utf8_beginning_of_line (buffer->input_buffer,
beginning_of_line);
}
size_deleted = start - beginning_of_line;
length_deleted = utf8_strnlen (beginning_of_line, size_deleted);
gui_input_clipboard_copy (beginning_of_line, size_deleted);
memmove (beginning_of_line, start, strlen (start));
if (gui_input_optimize_size (buffer,
buffer->input_buffer_size - size_deleted,
buffer->input_buffer_length - length_deleted))
{
buffer->input_buffer[buffer->input_buffer_size] = '\0';
buffer->input_buffer_pos = utf8_pos (
buffer->input_buffer,
beginning_of_line - buffer->input_buffer);
}
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
/*
* Deletes all from cursor pos to end of line (default key: ctrl-k).
*
* If cursor is at end of line, deletes to end of next line.
*/
void
gui_input_delete_end_of_line (struct t_gui_buffer *buffer)
{
int length_deleted, size_deleted;
char *start, *end_of_line;
if (!buffer->input)
return;
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
if (start[0] && (start[0] == '\n'))
end_of_line = (char *)utf8_next_char (start);
else
end_of_line = start;
end_of_line = (char *)utf8_end_of_line (end_of_line);
size_deleted = end_of_line - start;
length_deleted = utf8_strnlen (start, size_deleted);
gui_input_clipboard_copy (start, size_deleted);
memmove (start, end_of_line, strlen (end_of_line));
if (gui_input_optimize_size (buffer,
buffer->input_buffer_size - size_deleted,
buffer->input_buffer_length - length_deleted))
{
buffer->input_buffer[buffer->input_buffer_size] = '\0';
buffer->input_buffer_pos = utf8_pos (buffer->input_buffer,
start - buffer->input_buffer);
}
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
/*
* Deletes all from cursor pos to beginning of input (default key: alt-ctrl-u).
*/
void
gui_input_delete_beginning_of_input (struct t_gui_buffer *buffer)
{
int length_deleted, size_deleted;
char *start;
if (!buffer->input || (buffer->input_buffer_pos <= 0))
return;
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
size_deleted = start - buffer->input_buffer;
length_deleted = utf8_strnlen (buffer->input_buffer, size_deleted);
gui_input_clipboard_copy (buffer->input_buffer,
start - buffer->input_buffer);
memmove (buffer->input_buffer, start, strlen (start));
if (gui_input_optimize_size (
buffer,
buffer->input_buffer_size - size_deleted,
buffer->input_buffer_length - length_deleted))
{
buffer->input_buffer[buffer->input_buffer_size] = '\0';
buffer->input_buffer_pos = 0;
}
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
/*
* Deletes all from cursor pos to end of input (default key: alt-ctrl-k).
*/
void
gui_input_delete_end_of_input (struct t_gui_buffer *buffer)
{
char *start;
int size_deleted;
if (!buffer->input)
return;
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
size_deleted = strlen (start);
gui_input_clipboard_copy (start, size_deleted);
start[0] = '\0';
(void) gui_input_optimize_size (buffer,
strlen (buffer->input_buffer),
utf8_strlen (buffer->input_buffer));
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
/*
* Deletes current line (default key: alt-r).
*/
void
gui_input_delete_line (struct t_gui_buffer *buffer)
{
int length_deleted, size_deleted;
char *start, *beginning_of_line, *end_of_line;
if (!buffer->input)
return;
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
beginning_of_line = (char *)utf8_beginning_of_line (buffer->input_buffer,
start);
end_of_line = (char *)utf8_end_of_line (start);
size_deleted = end_of_line - beginning_of_line;
length_deleted = utf8_strnlen (start, size_deleted);
memmove (beginning_of_line, end_of_line, strlen (end_of_line));
if (gui_input_optimize_size (buffer,
buffer->input_buffer_size - size_deleted,
buffer->input_buffer_length - length_deleted))
{
buffer->input_buffer[buffer->input_buffer_size] = '\0';
buffer->input_buffer_pos = utf8_pos (
buffer->input_buffer,
beginning_of_line - buffer->input_buffer);
}
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
/*
* Deletes entire input (default key: alt-R).
*/
void
gui_input_delete_input (struct t_gui_buffer *buffer)
{
if (!buffer->input)
return;
gui_buffer_undo_snap (buffer);
if (gui_input_optimize_size (buffer, 0, 0))
{
buffer->input_buffer[0] = '\0';
buffer->input_buffer_pos = 0;
}
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
/*
* Transposes chars at cursor pos (default key: ctrl-t).
*/
void
gui_input_transpose_chars (struct t_gui_buffer *buffer)
{
char *start, *prev_char, saved_char[5];
int size_prev_char, size_start_char;
if (!buffer->input
|| (buffer->input_buffer_pos <= 0)
|| (buffer->input_buffer_length <= 1))
{
return;
}
gui_buffer_undo_snap (buffer);
if (buffer->input_buffer_pos == buffer->input_buffer_length)
buffer->input_buffer_pos--;
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
prev_char = (char *)utf8_prev_char (buffer->input_buffer, start);
size_prev_char = start - prev_char;
size_start_char = utf8_char_size (start);
memcpy (saved_char, prev_char, size_prev_char);
memcpy (prev_char, start, size_start_char);
memcpy (prev_char + size_start_char, saved_char, size_prev_char);
buffer->input_buffer_pos++;
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
}
/*
* Moves cursor to beginning of line (default key: home).
*
* If cursor is at beginning of line, moves to beginning of previous line.
*/
void
gui_input_move_beginning_of_line (struct t_gui_buffer *buffer)
{
char *pos, *original_pos;
if (!buffer->input || (buffer->input_buffer_pos <= 0))
return;
pos = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
original_pos = pos;
pos = (char *)utf8_beginning_of_line (buffer->input_buffer, pos);
if (pos == original_pos)
{
pos = (char *)utf8_prev_char (buffer->input_buffer, pos);
pos = (char *)utf8_beginning_of_line (buffer->input_buffer, pos);
}
buffer->input_buffer_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
gui_input_text_cursor_moved_signal (buffer);
}
/*
* Moves cursor to end of line (default key: end).
*
* If cursor is at end of line, moves to end of next line.
*/
void
gui_input_move_end_of_line (struct t_gui_buffer *buffer)
{
char *pos;
if (!buffer->input
|| (buffer->input_buffer_pos >= buffer->input_buffer_length))
{
return;
}
pos = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
if (pos[0] && pos[0] == '\n')
{
pos = (char *)utf8_next_char (pos);
}
pos = (char *)utf8_end_of_line (pos);
if (pos[0])
{
buffer->input_buffer_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
}
else
{
buffer->input_buffer_pos = buffer->input_buffer_length;
}
gui_input_text_cursor_moved_signal (buffer);
}
/*
* Moves cursor to beginning of input (default key: shift-home).
*/
void
gui_input_move_beginning_of_input (struct t_gui_buffer *buffer)
{
if (!buffer->input || (buffer->input_buffer_pos <= 0))
return;
buffer->input_buffer_pos = 0;
gui_input_text_cursor_moved_signal (buffer);
}
/*
* Moves cursor to end of input (default key: shift-end).
*/
void
gui_input_move_end_of_input (struct t_gui_buffer *buffer)
{
if (!buffer->input
|| (buffer->input_buffer_pos >= buffer->input_buffer_length))
{
return;
}
buffer->input_buffer_pos = buffer->input_buffer_length;
gui_input_text_cursor_moved_signal (buffer);
}
/*
* Moves cursor to previous char (default key: left).
*/
void
gui_input_move_previous_char (struct t_gui_buffer *buffer)
{
if (!buffer->input || (buffer->input_buffer_pos <= 0))
return;
buffer->input_buffer_pos--;
gui_input_text_cursor_moved_signal (buffer);
}
/*
* Moves cursor to next char (default key: right).
*/
void
gui_input_move_next_char (struct t_gui_buffer *buffer)
{
if (!buffer->input
|| (buffer->input_buffer_pos >= buffer->input_buffer_length))
{
return;
}
buffer->input_buffer_pos++;
gui_input_text_cursor_moved_signal (buffer);
}
/*
* Moves cursor to beginning of previous word (default key: alt-b or
* ctrl-left).
*/
void
gui_input_move_previous_word (struct t_gui_buffer *buffer)
{
char *pos;
if (!buffer->input || (buffer->input_buffer_pos <= 0))
return;
pos = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos - 1);
while (pos && !string_is_word_char_input (pos))
{
pos = (char *)utf8_prev_char (buffer->input_buffer, pos);
}
if (pos)
{
while (pos && string_is_word_char_input (pos))
{
pos = (char *)utf8_prev_char (buffer->input_buffer, pos);
}
if (pos)
pos = (char *)utf8_next_char (pos);
else
pos = buffer->input_buffer;
buffer->input_buffer_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
}
else
buffer->input_buffer_pos = 0;
gui_input_text_cursor_moved_signal (buffer);
}
/*
* Moves cursor to the beginning of next word (default key: alt-f or
* ctrl-right).
*/
void
gui_input_move_next_word (struct t_gui_buffer *buffer)
{
char *pos;
if (!buffer->input
|| (buffer->input_buffer_pos >= buffer->input_buffer_length))
{
return;
}
pos = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
while (pos[0] && !string_is_word_char_input (pos))
{
pos = (char *)utf8_next_char (pos);
}
if (pos[0])
{
while (pos[0] && string_is_word_char_input (pos))
{
pos = (char *)utf8_next_char (pos);
}
if (pos[0])
{
buffer->input_buffer_pos =
utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
}
else
buffer->input_buffer_pos = buffer->input_buffer_length;
}
else
buffer->input_buffer_pos = buffer->input_buffer_length;
gui_input_text_cursor_moved_signal (buffer);
}
/*
* Moves cursor to previous line (default key: shift-up).
*/
void
gui_input_move_previous_line (struct t_gui_buffer *buffer)
{
int beginning_of_line_pos, length_from_beginning, i;
char *pos;
if (!buffer->input || (buffer->input_buffer_pos <= 0))
return;
pos = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
pos = (char *)utf8_beginning_of_line (buffer->input_buffer, pos);
if (pos != buffer->input_buffer)
{
beginning_of_line_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
length_from_beginning = buffer->input_buffer_pos - beginning_of_line_pos;
pos = (char *)utf8_prev_char (buffer->input_buffer, pos);
pos = (char *)utf8_beginning_of_line (buffer->input_buffer, pos);
for (i = 0;
pos[0] && (pos[0] != '\n') && (i < length_from_beginning);
i++)
{
pos = (char *)utf8_next_char (pos);
}
buffer->input_buffer_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
gui_input_text_cursor_moved_signal (buffer);
}
}
/*
* Moves cursor to next line (default key: shift-down).
*/
void
gui_input_move_next_line (struct t_gui_buffer *buffer)
{
int beginning_of_line_pos, length_from_beginning, i;
char *pos;
if (!buffer->input
|| (buffer->input_buffer_pos >= buffer->input_buffer_length))
{
return;
}
pos = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
pos = (char *)utf8_beginning_of_line (buffer->input_buffer, pos);
beginning_of_line_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
length_from_beginning = buffer->input_buffer_pos - beginning_of_line_pos;
pos = (char *)utf8_end_of_line (pos);
if (pos[0])
{
pos = (char *)utf8_next_char (pos);
for (i = 0;
pos[0] && (pos[0] != '\n') && (i < length_from_beginning);
i++)
{
pos = (char *)utf8_next_char (pos);
}
buffer->input_buffer_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
gui_input_text_cursor_moved_signal (buffer);
}
}
/*
* Recalls previous command from local or global history.
*/
void
gui_input_history_previous (struct t_gui_window *window,
struct t_gui_history *history,
struct t_gui_history **ptr_history)
{
if (!window->buffer->input)
return;
if (*ptr_history)
{
if (!(*ptr_history)->next_history)
return;
*ptr_history = (*ptr_history)->next_history;
}
if (!(*ptr_history))
*ptr_history = history;
if (!(*ptr_history))
return;
/* bash/readline like use of history */
if (window->buffer->input_buffer_size > 0)
{
if ((*ptr_history)->prev_history)
{
/* replace text in history with current input */
window->buffer->input_buffer[window->buffer->input_buffer_size] = '\0';
if ((*ptr_history)->prev_history->text)
free ((*ptr_history)->prev_history->text);
(*ptr_history)->prev_history->text =
strdup (window->buffer->input_buffer);
}
else
{
/* add current input in history */
window->buffer->input_buffer[window->buffer->input_buffer_size] = '\0';
gui_history_add (window->buffer,
window->buffer->input_buffer);
}
}
if (gui_input_optimize_size (window->buffer,
strlen ((*ptr_history)->text),
utf8_strlen ((*ptr_history)->text)))
{
window->buffer->input_buffer_pos = window->buffer->input_buffer_length;
window->buffer->input_buffer_1st_display = 0;
strcpy (window->buffer->input_buffer, (*ptr_history)->text);
}
gui_input_text_changed_modifier_and_signal (window->buffer,
0, /* save undo */
1); /* stop completion */
gui_buffer_undo_free_all (window->buffer);
}
/*
* Recalls next command from local or global history.
*/
void
gui_input_history_next (struct t_gui_window *window,
struct t_gui_history *history,
struct t_gui_history **ptr_history)
{
int input_changed, rc;
/* make C compiler happy */
(void) history;
input_changed = 0;
if (!window->buffer->input)
return;
if (*ptr_history)
{
/* replace text in history with current input */
window->buffer->input_buffer[window->buffer->input_buffer_size] = '\0';
if ((*ptr_history)->text)
free ((*ptr_history)->text);
(*ptr_history)->text = strdup (window->buffer->input_buffer);
*ptr_history = (*ptr_history)->prev_history;
if (*ptr_history)
{
rc = gui_input_optimize_size (window->buffer,
strlen ((*ptr_history)->text),
utf8_strlen ((*ptr_history)->text));
}
else
{
rc = gui_input_optimize_size (window->buffer, 0, 0);
if (rc)
window->buffer->input_buffer[0] = '\0';
}
if (rc)
{
window->buffer->input_buffer_pos =
window->buffer->input_buffer_length;
window->buffer->input_buffer_1st_display = 0;
if (*ptr_history)
{
strcpy (window->buffer->input_buffer, (*ptr_history)->text);
}
}
input_changed = 1;
}
else
{
/* add line to history then clear input */
if (window->buffer->input_buffer_size > 0)
{
window->buffer->input_buffer[window->buffer->input_buffer_size] = '\0';
gui_history_add (window->buffer,
window->buffer->input_buffer);
if (gui_input_optimize_size (window->buffer, 0, 0))
{
window->buffer->input_buffer[0] = '\0';
window->buffer->input_buffer_pos = 0;
window->buffer->input_buffer_1st_display = 0;
}
input_changed = 1;
}
}
if (input_changed)
{
gui_input_text_changed_modifier_and_signal (window->buffer,
0, /* save undo */
1); /* stop completion */
gui_buffer_undo_free_all (window->buffer);
}
}
/*
* Recalls previous command from local history (default key: up).
*/
void
gui_input_history_local_previous (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window)
{
gui_input_history_previous (window,
window->buffer->history,
&(window->buffer->ptr_history));
}
}
/*
* Recalls next command from local history (default key: down).
*/
void
gui_input_history_local_next (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window)
{
gui_input_history_next (window,
window->buffer->history,
&(window->buffer->ptr_history));
}
}
/*
* Recalls previous command from global history (default key: ctrl-up).
*/
void
gui_input_history_global_previous (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window)
{
gui_input_history_previous (window,
gui_history,
&gui_history_ptr);
}
}
/*
* Recalls next command from global history (default key: ctrl-down).
*/
void
gui_input_history_global_next (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
window = gui_window_search_with_buffer (buffer);
if (window)
{
gui_input_history_next (window,
gui_history,
&gui_history_ptr);
}
}
/*
* Initializes "grab key mode" (next key will be inserted into input buffer)
* (default key: alt-k).
*/
void
gui_input_grab_key (struct t_gui_buffer *buffer, int raw_key, int command,
const char *delay)
{
if (buffer->input)
gui_key_grab_init (raw_key, command, delay);
}
/*
* Initializes "grab mouse mode" (next mouse event will be inserted into input
* buffer) (default key: button2 of mouse in input bar).
*/
void
gui_input_grab_mouse (struct t_gui_buffer *buffer, int area)
{
if (buffer->input)
gui_mouse_grab_init (area);
}
/*
* Inserts a string in command line.
*/
void
gui_input_insert (struct t_gui_buffer *buffer, const char *args)
{
char *args2;
if (!args)
return;
gui_buffer_undo_snap (buffer);
args2 = string_convert_escaped_chars (args);
gui_input_insert_string (buffer, (args2) ? args2 : args);
gui_input_text_changed_modifier_and_signal (buffer,
1, /* save undo */
1); /* stop completion */
if (args2)
free (args2);
}
/*
* Uses a undo: replace input with undo content.
*/
void
gui_input_undo_use (struct t_gui_buffer *buffer, struct t_gui_input_undo *undo)
{
if ((!undo->data) || (strcmp (undo->data, buffer->input_buffer) == 0))
return;
gui_input_replace_input (buffer, undo->data);
gui_input_set_pos (buffer, undo->pos);
gui_input_text_changed_modifier_and_signal (buffer,
0, /* save undo */
1); /* stop completion */
}
/*
* Undoes last action on input buffer (default key: ctrl-_).
*/
void
gui_input_undo (struct t_gui_buffer *buffer)
{
if (!buffer->ptr_input_undo)
return;
/*
* if we are doing undo and that undo pointer is to the end of list
* (for example first time undo is used), then save current input
* content in undo list
*/
if ((buffer->ptr_input_undo == buffer->last_input_undo)
&& (buffer->ptr_input_undo)->data
&& (strcmp (buffer->input_buffer, (buffer->ptr_input_undo)->data) != 0))
{
gui_buffer_undo_snap_free (buffer);
gui_buffer_undo_add (buffer);
}
if (buffer->ptr_input_undo
&& (buffer->ptr_input_undo)->prev_undo)
{
buffer->ptr_input_undo = (buffer->ptr_input_undo)->prev_undo;
gui_input_undo_use (buffer, buffer->ptr_input_undo);
}
}
/*
* Redoes last action on input buffer (default key: alt-_).
*/
void
gui_input_redo (struct t_gui_buffer *buffer)
{
if (!buffer->ptr_input_undo || !(buffer->ptr_input_undo)->next_undo)
return;
buffer->ptr_input_undo = (buffer->ptr_input_undo)->next_undo;
gui_input_undo_use (buffer, buffer->ptr_input_undo);
}