1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-25 12:26:40 +02:00
Files
weechat/src/gui/gui-input.c
T
Sébastien Helleu db0bdc2ac6 core: add flag "input_get_empty" in buffer
The default value is 0 (legacy behavior).
When it is set to 1, an empty input (just by pressing Return with nothing in
input) is sent to the input callback, which receives an empty string.
2017-08-21 07:41:28 +02:00

1871 lines
54 KiB
C

/*
* gui-input.c - input functions (used by all GUI)
*
* Copyright (C) 2003-2017 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 <http://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);
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", (long unsigned int)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.
*
* If pos == -1, string is inserted at cursor position.
*/
void
gui_input_insert_string (struct t_gui_buffer *buffer, const char *string,
int pos)
{
int size, length;
char *string_utf8, *ptr_start;
if (buffer->input)
{
string_utf8 = strdup (string);
if (!string_utf8)
return;
if (pos == -1)
pos = buffer->input_buffer_pos;
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, pos);
memmove (ptr_start + size, ptr_start, strlen (ptr_start));
/* insert new string */
ptr_start = (char *)utf8_add_offset (buffer->input_buffer, pos);
strncpy (ptr_start, string_utf8, size);
buffer->input_buffer_pos += length;
}
free (string_utf8);
}
}
/*
* Moves input content and undo data from a buffer to another buffer.
*/
void
gui_input_move_to_buffer (struct t_gui_buffer *from_buffer,
struct t_gui_buffer *to_buffer)
{
int is_command;
/*
* move of input is allowed if:
* - 2 buffers are different
* - input_share is not set to "none"
* - input buffer in first buffer is not empty
*/
if (!from_buffer || !to_buffer || (from_buffer == to_buffer)
|| (CONFIG_INTEGER(config_look_input_share) == CONFIG_LOOK_INPUT_SHARE_NONE)
|| !from_buffer->input_buffer || !from_buffer->input_buffer[0])
return;
/*
* if input is command and that only text is allowed,
* or if input is text and that only command is allowed,
* then do nothing
*/
is_command = (string_input_for_buffer (from_buffer->input_buffer) == NULL) ? 1 : 0;
if ((is_command && (CONFIG_INTEGER(config_look_input_share) == CONFIG_LOOK_INPUT_SHARE_TEXT))
|| (!is_command && (CONFIG_INTEGER(config_look_input_share) == CONFIG_LOOK_INPUT_SHARE_COMMANDS)))
return;
/*
* if overwrite is off and that input of target buffer is not empty,
* then do nothing
*/
if ((!CONFIG_BOOLEAN(config_look_input_share_overwrite))
&& to_buffer->input_buffer && to_buffer->input_buffer[0])
return;
/* move input_buffer */
if (to_buffer->input_buffer)
free (to_buffer->input_buffer);
to_buffer->input_buffer = from_buffer->input_buffer;
to_buffer->input_buffer_alloc = from_buffer->input_buffer_alloc;
to_buffer->input_buffer_size = from_buffer->input_buffer_size;
to_buffer->input_buffer_length = from_buffer->input_buffer_length;
to_buffer->input_buffer_pos = from_buffer->input_buffer_pos;
to_buffer->input_buffer_1st_display = from_buffer->input_buffer_1st_display;
gui_buffer_input_buffer_init (from_buffer);
/* move undo data */
gui_buffer_undo_free_all (to_buffer);
(to_buffer->input_undo_snap)->data = (from_buffer->input_undo_snap)->data;
(to_buffer->input_undo_snap)->pos = (from_buffer->input_undo_snap)->pos;
to_buffer->input_undo = from_buffer->input_undo;
to_buffer->last_input_undo = from_buffer->last_input_undo;
to_buffer->ptr_input_undo = from_buffer->ptr_input_undo;
to_buffer->input_undo_count = from_buffer->input_undo_count;
(from_buffer->input_undo_snap)->data = NULL;
(from_buffer->input_undo_snap)->pos = 0;
from_buffer->input_undo = NULL;
from_buffer->last_input_undo = NULL;
from_buffer->ptr_input_undo = NULL;
from_buffer->input_undo_count = 0;
gui_completion_stop (from_buffer->completion);
}
/*
* Copies string into the internal clipboard.
*/
void
gui_input_clipboard_copy (const char *buffer, int size)
{
if (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, -1);
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);
free (command);
}
}
}
/*
* Completes a word in input buffer.
*/
void
gui_input_complete (struct t_gui_buffer *buffer)
{
int i;
if (!buffer->completion)
return;
if (buffer->completion->word_found)
{
/* 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, " ",
buffer->input_buffer_pos);
}
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))
{
gui_buffer_undo_snap (buffer);
gui_completion_search (buffer->completion,
1,
buffer->input_buffer,
buffer->input_buffer_size,
utf8_real_pos (buffer->input_buffer,
buffer->input_buffer_pos));
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))
{
gui_buffer_undo_snap (buffer);
gui_completion_search (buffer->completion,
-1,
buffer->input_buffer,
buffer->input_buffer_size,
utf8_real_pos (buffer->input_buffer,
buffer->input_buffer_pos));
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: meta-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 with free content */
if (window->buffer->type == GUI_BUFFER_TYPE_FREE)
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))
{
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))
{
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 */
}
}
/*
* Deletes previous word (default key: ctrl-W).
*/
void
gui_input_delete_previous_word (struct t_gui_buffer *buffer)
{
int length_deleted, size_deleted;
char *start, *string;
if (buffer->input && (buffer->input_buffer_pos > 0))
{
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos - 1);
string = start;
while (string && !string_is_word_char_input (string))
{
string = (char *)utf8_prev_char (buffer->input_buffer, string);
}
if (string)
{
while (string && string_is_word_char_input (string))
{
string = (char *)utf8_prev_char (buffer->input_buffer, string);
}
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 (utf8_next_char (string));
else
string = buffer->input_buffer;
size_deleted = utf8_next_char (start) - string;
length_deleted = utf8_strnlen (string, size_deleted);
gui_input_clipboard_copy (string, size_deleted);
memmove (string, string + size_deleted, strlen (string + 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 next word (default key: meta-d).
*/
void
gui_input_delete_next_word (struct t_gui_buffer *buffer)
{
int size_deleted, length_deleted;
char *start, *string;
if (buffer->input)
{
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
string = start;
length_deleted = 0;
while (string[0])
{
if (!string_is_word_char_input (string) && (string > start))
break;
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).
*/
void
gui_input_delete_beginning_of_line (struct t_gui_buffer *buffer)
{
int length_deleted, size_deleted;
char *start;
if (buffer->input && (buffer->input_buffer_pos > 0))
{
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 line (default key: ctrl-K).
*/
void
gui_input_delete_end_of_line (struct t_gui_buffer *buffer)
{
char *start;
int size_deleted;
if (buffer->input)
{
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 entire line (default key: meta-r).
*/
void
gui_input_delete_line (struct t_gui_buffer *buffer)
{
if (buffer->input)
{
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))
{
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).
*/
void
gui_input_move_beginning_of_line (struct t_gui_buffer *buffer)
{
if (buffer->input && (buffer->input_buffer_pos > 0))
{
buffer->input_buffer_pos = 0;
gui_input_text_cursor_moved_signal (buffer);
}
}
/*
* Moves cursor to end of line (default key: end).
*/
void
gui_input_move_end_of_line (struct t_gui_buffer *buffer)
{
if (buffer->input
&& (buffer->input_buffer_pos < buffer->input_buffer_length))
{
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))
{
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))
{
buffer->input_buffer_pos++;
gui_input_text_cursor_moved_signal (buffer);
}
}
/*
* Moves cursor to beginning of previous word (default key: meta-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))
{
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: meta-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))
{
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 =
utf8_pos (buffer->input_buffer,
utf8_prev_char (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);
}
}
/*
* Jumps to buffer with activity (default key: alt-a).
*/
void
gui_input_jump_smart (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
int scroll_to_bottom;
scroll_to_bottom = 0;
window = gui_window_search_with_buffer (buffer);
if (window
&& (window->buffer->text_search == GUI_TEXT_SEARCH_DISABLED))
{
if (gui_hotlist)
{
if (!gui_hotlist_initial_buffer)
gui_hotlist_initial_buffer = window->buffer;
gui_window_switch_to_buffer (window, gui_hotlist->buffer, 1);
gui_hotlist_remove_buffer (window->buffer, 0);
scroll_to_bottom = 1;
}
else
{
if (gui_hotlist_initial_buffer)
{
if (CONFIG_BOOLEAN(config_look_jump_smart_back_to_buffer))
{
gui_window_switch_to_buffer (window,
gui_hotlist_initial_buffer, 1);
scroll_to_bottom = 1;
}
gui_hotlist_initial_buffer = NULL;
}
else
{
gui_hotlist_initial_buffer = NULL;
}
}
/*
* scroll to bottom if window was scrolled (except if scrolled
* beyond the end)
*/
if (scroll_to_bottom
&& window->scroll
&& window->scroll->start_line
&& (window->scroll->start_line_pos >= 0))
{
gui_window_scroll_bottom (window);
}
}
}
/*
* Jumps to last buffer displayed (before last jump to a buffer) (default key:
* meta-/).
*/
void
gui_input_jump_last_buffer_displayed (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))
{
if (gui_buffer_last_displayed)
gui_buffer_switch_by_number (window,
gui_buffer_last_displayed->number);
}
}
/*
* Jumps to previously visited buffer (buffer displayed before current one)
* (default key: meta-<).
*/
void
gui_input_jump_previously_visited_buffer (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
int index;
struct t_gui_buffer_visited *ptr_buffer_visited;
window = gui_window_search_with_buffer (buffer);
if (window
&& (window->buffer->text_search == GUI_TEXT_SEARCH_DISABLED))
{
index = gui_buffer_visited_get_index_previous ();
if (index >= 0)
{
gui_buffers_visited_index = index;
ptr_buffer_visited =
gui_buffer_visited_search_by_number (gui_buffers_visited_index);
if (ptr_buffer_visited)
{
gui_buffers_visited_frozen = 1;
gui_buffer_switch_by_number (window,
ptr_buffer_visited->buffer->number);
gui_buffers_visited_frozen = 0;
}
}
}
}
/*
* Jumps to next visited buffer (buffer displayed after current one) (default
* key: meta->).
*/
void
gui_input_jump_next_visited_buffer (struct t_gui_buffer *buffer)
{
struct t_gui_window *window;
int index;
struct t_gui_buffer_visited *ptr_buffer_visited;
window = gui_window_search_with_buffer (buffer);
if (window
&& (window->buffer->text_search == GUI_TEXT_SEARCH_DISABLED))
{
index = gui_buffer_visited_get_index_next ();
if (index >= 0)
{
gui_buffers_visited_index = index;
ptr_buffer_visited = gui_buffer_visited_search_by_number (gui_buffers_visited_index);
if (ptr_buffer_visited)
{
gui_buffers_visited_frozen = 1;
gui_buffer_switch_by_number (window,
ptr_buffer_visited->buffer->number);
gui_buffers_visited_frozen = 0;
}
}
}
}
/*
* Clears hotlist (default key: meta-h).
*/
void
gui_input_hotlist_clear (struct t_gui_buffer *buffer,
const char *str_level_mask)
{
long level_mask;
char *error;
struct t_gui_hotlist *ptr_hotlist;
int priority;
if (str_level_mask)
{
if (strcmp (str_level_mask, "lowest") == 0)
{
/* clear only lowest priority currently in hotlist */
priority = GUI_HOTLIST_MAX + 1;
for (ptr_hotlist = gui_hotlist; ptr_hotlist;
ptr_hotlist = ptr_hotlist->next_hotlist)
{
if ((int)ptr_hotlist->priority < priority)
priority = ptr_hotlist->priority;
}
if (priority <= GUI_HOTLIST_MAX)
{
gui_hotlist_clear (1 << priority);
gui_hotlist_initial_buffer = buffer;
}
}
else if (strcmp (str_level_mask, "highest") == 0)
{
/* clear only highest priority currently in hotlist */
priority = GUI_HOTLIST_MIN - 1;
for (ptr_hotlist = gui_hotlist; ptr_hotlist;
ptr_hotlist = ptr_hotlist->next_hotlist)
{
if ((int)ptr_hotlist->priority > priority)
priority = ptr_hotlist->priority;
}
if (priority >= GUI_HOTLIST_MIN)
{
gui_hotlist_clear (1 << priority);
gui_hotlist_initial_buffer = buffer;
}
}
else
{
/* clear hotlist using a mask of levels */
error = NULL;
level_mask = strtol (str_level_mask, &error, 10);
if (error && !error[0] && (level_mask > 0))
{
gui_hotlist_clear ((int)level_mask);
gui_hotlist_initial_buffer = buffer;
}
}
}
else
{
gui_hotlist_clear (GUI_HOTLIST_MASK_MAX);
gui_hotlist_initial_buffer = buffer;
}
}
/*
* Initializes "grab key mode" (next key will be inserted into input buffer)
* (default key: meta-k).
*/
void
gui_input_grab_key (struct t_gui_buffer *buffer, int command, const char *delay)
{
if (buffer->input)
gui_key_grab_init (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);
}
/*
* Sets unread marker for all buffers (default key: ctrl-S, ctrl-U).
*/
void
gui_input_set_unread ()
{
struct t_gui_buffer *ptr_buffer;
/* set read marker for all standard buffers */
for (ptr_buffer = gui_buffers; ptr_buffer;
ptr_buffer = ptr_buffer->next_buffer)
{
gui_buffer_set_unread (ptr_buffer);
}
}
/*
* Sets unread marker for a buffer.
*/
void
gui_input_set_unread_current (struct t_gui_buffer *buffer)
{
gui_buffer_set_unread (buffer);
}
/*
* Switches active buffer to next buffer (when many buffers are merged) (default
* key: ctrl-X).
*/
void
gui_input_switch_active_buffer (struct t_gui_buffer *buffer)
{
struct t_gui_buffer *ptr_buffer;
struct t_gui_window *window;
ptr_buffer = gui_buffer_get_next_active_buffer (buffer, 0);
if (ptr_buffer)
{
gui_buffer_set_active_buffer (ptr_buffer);
window = gui_window_search_with_buffer (buffer);
if (window)
gui_window_switch_to_buffer (window, ptr_buffer, 1);
}
}
/*
* Switches active buffer to previous buffer (when many buffers are merged).
*/
void
gui_input_switch_active_buffer_previous (struct t_gui_buffer *buffer)
{
struct t_gui_buffer *ptr_buffer;
struct t_gui_window *window;
ptr_buffer = gui_buffer_get_previous_active_buffer (buffer, 0);
if (ptr_buffer)
{
gui_buffer_set_active_buffer (ptr_buffer);
window = gui_window_search_with_buffer (buffer);
if (window)
gui_window_switch_to_buffer (window, ptr_buffer, 1);
}
}
/*
* Zooms on current active merged buffer, or display all merged buffers if zoom
* was active (default key: alt-x).
*/
void
gui_input_zoom_merged_buffer (struct t_gui_buffer *buffer)
{
struct t_gui_window *ptr_window;
struct t_gui_buffer *ptr_buffer;
int buffer_was_zoomed;
/* do nothing if current buffer is not merged with another buffer */
if (gui_buffer_count_merged_buffers (buffer->number) < 2)
return;
buffer_was_zoomed = (buffer->active == 2);
/* reset scroll in all windows displaying this buffer number */
for (ptr_window = gui_windows; ptr_window;
ptr_window = ptr_window->next_window)
{
if ((ptr_window->buffer->number == buffer->number)
&& ptr_window->scroll && ptr_window->scroll->start_line)
{
gui_window_scroll_bottom (ptr_window);
}
}
/* first make buffer active if it is not */
if (!buffer->active)
{
gui_buffer_set_active_buffer (buffer);
ptr_window = gui_window_search_with_buffer (buffer);
if (ptr_window)
gui_window_switch_to_buffer (ptr_window, buffer, 1);
}
/*
* toggle active flag between 1 and 2
* (1 = active with other merged buffers displayed, 2 = the only active)
*/
if (buffer->active == 1)
{
buffer->active = 2;
buffer->lines = buffer->own_lines;
}
else if (buffer->active == 2)
{
buffer->active = 1;
buffer->lines = buffer->mixed_lines;
}
/* set "zoomed" in merged buffers */
for (ptr_buffer = gui_buffers; ptr_buffer;
ptr_buffer = ptr_buffer->next_buffer)
{
if (ptr_buffer->number > buffer->number)
break;
if (ptr_buffer->number == buffer->number)
{
ptr_buffer->zoomed = (buffer->active == 2) ? 1 : 0;
}
}
gui_buffer_compute_num_displayed ();
gui_buffer_ask_chat_refresh (buffer, 2);
(void) hook_signal_send ((buffer_was_zoomed) ?
"buffer_unzoomed" : "buffer_zoomed",
WEECHAT_HOOK_SIGNAL_POINTER, buffer);
}
/*
* Inserts a string in command line.
*/
void
gui_input_insert (struct t_gui_buffer *buffer, const char *args)
{
char *args2;
if (args)
{
gui_buffer_undo_snap (buffer);
args2 = string_convert_escaped_chars (args);
gui_input_insert_string (buffer, (args2) ? args2 : args, -1);
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))
{
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)
{
/*
* 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)
{
buffer->ptr_input_undo = (buffer->ptr_input_undo)->next_undo;
gui_input_undo_use (buffer, buffer->ptr_input_undo);
}
}