/*
* gedit-commands-search.c
* This file is part of gedit
*
* Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
* Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
* Copyright (C) 2002-2006 Paolo Maggi
* Copyright (C) 2013 Sébastien Wilmet
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, see .
*/
#include "config.h"
#include "gedit-commands.h"
#include "gedit-commands-private.h"
#include
#include
#include
#include
#include "gedit-debug.h"
#include "gedit-statusbar.h"
#include "gedit-tab.h"
#include "gedit-tab-private.h"
#include "gedit-view-frame.h"
#include "gedit-window.h"
#include "gedit-utils.h"
#include "gedit-replace-dialog.h"
#define GEDIT_REPLACE_DIALOG_KEY "gedit-replace-dialog-key"
#define GEDIT_LAST_SEARCH_DATA_KEY "gedit-last-search-data-key"
typedef struct _LastSearchData LastSearchData;
struct _LastSearchData
{
gint x;
gint y;
};
static void
last_search_data_free (LastSearchData *data)
{
g_slice_free (LastSearchData, data);
}
static void
last_search_data_restore_position (GeditReplaceDialog *dlg)
{
LastSearchData *data;
data = g_object_get_data (G_OBJECT (dlg), GEDIT_LAST_SEARCH_DATA_KEY);
if (data != NULL)
{
gtk_window_move (GTK_WINDOW (dlg),
data->x,
data->y);
}
}
static void
last_search_data_store_position (GeditReplaceDialog *dlg)
{
LastSearchData *data;
data = g_object_get_data (G_OBJECT (dlg), GEDIT_LAST_SEARCH_DATA_KEY);
if (data == NULL)
{
data = g_slice_new (LastSearchData);
g_object_set_data_full (G_OBJECT (dlg),
GEDIT_LAST_SEARCH_DATA_KEY,
data,
(GDestroyNotify) last_search_data_free);
}
gtk_window_get_position (GTK_WINDOW (dlg),
&data->x,
&data->y);
}
/* Use occurences only for Replace All */
static void
text_found (GeditWindow *window,
gint occurrences)
{
GeditStatusbar *statusbar = GEDIT_STATUSBAR (gedit_window_get_statusbar (window));
if (occurrences > 1)
{
_gedit_statusbar_flash_generic_message (statusbar,
ngettext("Found and replaced %d occurrence",
"Found and replaced %d occurrences",
occurrences),
occurrences);
}
else if (occurrences == 1)
{
_gedit_statusbar_flash_generic_message (statusbar,
_("Found and replaced one occurrence"));
}
else
{
_gedit_statusbar_flash_generic_message (statusbar, " ");
}
}
#define MAX_MSG_LENGTH 40
static void
text_not_found (GeditWindow *window,
GeditReplaceDialog *replace_dialog)
{
const gchar *search_text;
gchar *truncated_text;
GeditStatusbar *statusbar;
search_text = gedit_replace_dialog_get_search_text (replace_dialog);
truncated_text = tepl_utils_str_end_truncate (search_text, MAX_MSG_LENGTH);
statusbar = GEDIT_STATUSBAR (gedit_window_get_statusbar (window));
_gedit_statusbar_flash_generic_message (statusbar,
/* Translators: %s is replaced by the text entered
* by the user in the search box.
*/
_("“%s” not found"), truncated_text);
g_free (truncated_text);
}
static void
finish_search_from_dialog (GeditWindow *window,
gboolean found)
{
GeditReplaceDialog *replace_dialog;
replace_dialog = g_object_get_data (G_OBJECT (window), GEDIT_REPLACE_DIALOG_KEY);
g_return_if_fail (replace_dialog != NULL);
if (found)
{
text_found (window, 0);
}
else
{
text_not_found (window, replace_dialog);
}
}
static gboolean
forward_search_finished (GtkSourceSearchContext *search_context,
GAsyncResult *result,
GeditView *view)
{
gboolean found;
GtkSourceBuffer *buffer;
GtkTextIter match_start;
GtkTextIter match_end;
found = gtk_source_search_context_forward_finish (search_context,
result,
&match_start,
&match_end,
NULL,
NULL);
buffer = gtk_source_search_context_get_buffer (search_context);
if (found)
{
gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer),
&match_start,
&match_end);
tepl_view_scroll_to_cursor (TEPL_VIEW (view));
}
else
{
GtkTextIter end_selection;
gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer),
NULL,
&end_selection);
gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer),
&end_selection,
&end_selection);
}
return found;
}
static void
forward_search_from_dialog_finished (GtkSourceSearchContext *search_context,
GAsyncResult *result,
GeditWindow *window)
{
GeditView *view = gedit_window_get_active_view (window);
gboolean found;
if (view == NULL)
{
return;
}
found = forward_search_finished (search_context, result, view);
finish_search_from_dialog (window, found);
}
static void
run_forward_search (GeditWindow *window,
gboolean from_dialog)
{
GeditView *view;
GtkTextBuffer *buffer;
GtkTextIter start_at;
GtkSourceSearchContext *search_context;
view = gedit_window_get_active_view (window);
if (view == NULL)
{
return;
}
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
search_context = gedit_document_get_search_context (GEDIT_DOCUMENT (buffer));
if (search_context == NULL)
{
return;
}
gtk_text_buffer_get_selection_bounds (buffer, NULL, &start_at);
if (from_dialog)
{
gtk_source_search_context_forward_async (search_context,
&start_at,
NULL,
(GAsyncReadyCallback)forward_search_from_dialog_finished,
window);
}
else
{
gtk_source_search_context_forward_async (search_context,
&start_at,
NULL,
(GAsyncReadyCallback)forward_search_finished,
view);
}
}
static gboolean
backward_search_finished (GtkSourceSearchContext *search_context,
GAsyncResult *result,
GeditView *view)
{
gboolean found;
GtkTextIter match_start;
GtkTextIter match_end;
GtkSourceBuffer *buffer;
found = gtk_source_search_context_backward_finish (search_context,
result,
&match_start,
&match_end,
NULL,
NULL);
buffer = gtk_source_search_context_get_buffer (search_context);
if (found)
{
gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer),
&match_start,
&match_end);
tepl_view_scroll_to_cursor (TEPL_VIEW (view));
}
else
{
GtkTextIter start_selection;
gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer),
&start_selection,
NULL);
gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer),
&start_selection,
&start_selection);
}
return found;
}
static void
backward_search_from_dialog_finished (GtkSourceSearchContext *search_context,
GAsyncResult *result,
GeditWindow *window)
{
GeditView *view = gedit_window_get_active_view (window);
gboolean found;
if (view == NULL)
{
return;
}
found = backward_search_finished (search_context, result, view);
finish_search_from_dialog (window, found);
}
static void
run_backward_search (GeditWindow *window,
gboolean from_dialog)
{
GeditView *view;
GtkTextBuffer *buffer;
GtkTextIter start_at;
GtkSourceSearchContext *search_context;
view = gedit_window_get_active_view (window);
if (view == NULL)
{
return;
}
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
search_context = gedit_document_get_search_context (GEDIT_DOCUMENT (buffer));
if (search_context == NULL)
{
return;
}
gtk_text_buffer_get_selection_bounds (buffer, &start_at, NULL);
if (from_dialog)
{
gtk_source_search_context_backward_async (search_context,
&start_at,
NULL,
(GAsyncReadyCallback)backward_search_from_dialog_finished,
window);
}
else
{
gtk_source_search_context_backward_async (search_context,
&start_at,
NULL,
(GAsyncReadyCallback)backward_search_finished,
view);
}
}
static void
do_find (GeditReplaceDialog *dialog,
GeditWindow *window)
{
if (gedit_replace_dialog_get_backwards (dialog))
{
run_backward_search (window, TRUE);
}
else
{
run_forward_search (window, TRUE);
}
}
static void
do_replace (GeditReplaceDialog *dialog,
GeditWindow *window)
{
GeditDocument *doc;
GtkSourceSearchContext *search_context;
const gchar *replace_entry_text;
gchar *unescaped_replace_text;
GtkTextIter start;
GtkTextIter end;
GError *error = NULL;
doc = gedit_window_get_active_document (window);
if (doc == NULL)
{
return;
}
search_context = gedit_document_get_search_context (doc);
if (search_context == NULL)
{
return;
}
/* replace text may be "", we just delete */
replace_entry_text = gedit_replace_dialog_get_replace_text (dialog);
g_return_if_fail (replace_entry_text != NULL);
unescaped_replace_text = gtk_source_utils_unescape_search_text (replace_entry_text);
gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc), &start, &end);
gtk_source_search_context_replace (search_context,
&start,
&end,
unescaped_replace_text,
-1,
&error);
g_free (unescaped_replace_text);
if (error != NULL)
{
gedit_replace_dialog_set_replace_error (dialog, error->message);
g_error_free (error);
}
do_find (dialog, window);
}
static void
do_replace_all (GeditReplaceDialog *dialog,
GeditWindow *window)
{
GeditView *view;
GtkSourceSearchContext *search_context;
GtkTextBuffer *buffer;
GtkSourceCompletion *completion;
const gchar *replace_entry_text;
gchar *unescaped_replace_text;
gint count;
GError *error = NULL;
view = gedit_window_get_active_view (window);
if (view == NULL)
{
return;
}
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
search_context = gedit_document_get_search_context (GEDIT_DOCUMENT (buffer));
if (search_context == NULL)
{
return;
}
/* FIXME: this should really be done automatically in gtksoureview, but
* it is an important performance fix, so let's do it here for now.
*/
completion = gtk_source_view_get_completion (GTK_SOURCE_VIEW (view));
gtk_source_completion_block_interactive (completion);
/* replace text may be "", we just delete all occurrences */
replace_entry_text = gedit_replace_dialog_get_replace_text (dialog);
g_return_if_fail (replace_entry_text != NULL);
unescaped_replace_text = gtk_source_utils_unescape_search_text (replace_entry_text);
count = gtk_source_search_context_replace_all (search_context,
unescaped_replace_text,
-1,
&error);
g_free (unescaped_replace_text);
gtk_source_completion_unblock_interactive (completion);
if (count > 0)
{
text_found (window, count);
}
else if (error == NULL)
{
text_not_found (window, dialog);
}
if (error != NULL)
{
gedit_replace_dialog_set_replace_error (dialog, error->message);
g_error_free (error);
}
}
static void
replace_dialog_response_cb (GeditReplaceDialog *dialog,
gint response_id,
GeditWindow *window)
{
gedit_debug (DEBUG_COMMANDS);
switch (response_id)
{
case GEDIT_REPLACE_DIALOG_FIND_RESPONSE:
do_find (dialog, window);
break;
case GEDIT_REPLACE_DIALOG_REPLACE_RESPONSE:
do_replace (dialog, window);
break;
case GEDIT_REPLACE_DIALOG_REPLACE_ALL_RESPONSE:
do_replace_all (dialog, window);
break;
default:
last_search_data_store_position (dialog);
gtk_widget_hide (GTK_WIDGET (dialog));
}
}
static void
replace_dialog_destroyed (GeditWindow *window,
GeditReplaceDialog *dialog)
{
gedit_debug (DEBUG_COMMANDS);
g_object_set_data (G_OBJECT (window),
GEDIT_REPLACE_DIALOG_KEY,
NULL);
g_object_set_data (G_OBJECT (dialog),
GEDIT_LAST_SEARCH_DATA_KEY,
NULL);
}
static GtkWidget *
create_dialog (GeditWindow *window)
{
GtkWidget *dialog = gedit_replace_dialog_new (window);
g_signal_connect (dialog,
"response",
G_CALLBACK (replace_dialog_response_cb),
window);
g_object_set_data (G_OBJECT (window),
GEDIT_REPLACE_DIALOG_KEY,
dialog);
g_object_weak_ref (G_OBJECT (dialog),
(GWeakNotify) replace_dialog_destroyed,
window);
return dialog;
}
void
_gedit_cmd_search_find (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GeditWindow *window = GEDIT_WINDOW (user_data);
GeditTab *active_tab;
GeditViewFrame *frame;
gedit_debug (DEBUG_COMMANDS);
active_tab = gedit_window_get_active_tab (window);
if (active_tab == NULL)
{
return;
}
frame = _gedit_tab_get_view_frame (active_tab);
gedit_view_frame_popup_search (frame);
}
void
_gedit_cmd_search_replace (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GeditWindow *window = GEDIT_WINDOW (user_data);
gpointer data;
GtkWidget *replace_dialog;
gedit_debug (DEBUG_COMMANDS);
data = g_object_get_data (G_OBJECT (window), GEDIT_REPLACE_DIALOG_KEY);
if (data == NULL)
{
replace_dialog = create_dialog (window);
}
else
{
g_return_if_fail (GEDIT_IS_REPLACE_DIALOG (data));
replace_dialog = GTK_WIDGET (data);
}
gtk_widget_show (replace_dialog);
last_search_data_restore_position (GEDIT_REPLACE_DIALOG (replace_dialog));
gedit_replace_dialog_present_with_time (GEDIT_REPLACE_DIALOG (replace_dialog),
GDK_CURRENT_TIME);
}
void
_gedit_cmd_search_find_next (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GeditWindow *window = GEDIT_WINDOW (user_data);
gedit_debug (DEBUG_COMMANDS);
run_forward_search (window, FALSE);
}
void
_gedit_cmd_search_find_prev (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GeditWindow *window = GEDIT_WINDOW (user_data);
gedit_debug (DEBUG_COMMANDS);
run_backward_search (window, FALSE);
}
void
_gedit_cmd_search_clear_highlight (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GeditWindow *window = GEDIT_WINDOW (user_data);
GeditTab *active_tab;
GeditViewFrame *frame;
GeditDocument *doc;
gedit_debug (DEBUG_COMMANDS);
active_tab = gedit_window_get_active_tab (window);
if (active_tab == NULL)
{
return;
}
frame = _gedit_tab_get_view_frame (active_tab);
gedit_view_frame_clear_search (frame);
doc = gedit_tab_get_document (active_tab);
gedit_document_set_search_context (doc, NULL);
}
void
_gedit_cmd_search_goto_line (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GeditWindow *window = GEDIT_WINDOW (user_data);
GeditTab *active_tab;
GeditViewFrame *frame;
gedit_debug (DEBUG_COMMANDS);
active_tab = gedit_window_get_active_tab (window);
if (active_tab == NULL)
{
return;
}
frame = _gedit_tab_get_view_frame (active_tab);
gedit_view_frame_popup_goto_line (frame);
}
/* ex:set ts=8 noet: */