Clipboard
/* Copyright (C) 2010-2012 by Cristian Henzel <oss@rspwn.com>
* Copyright (C) 2011 by Eugene Nikolsky <pluton.od@gmail.com>
*
* forked from parcellite, which is
* Copyright (C) 2007-2008 by Xyhthyx <xyhthyx@gmail.com>
*
* This file is part of ClipIt.
*
* ClipIt 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.
*
* ClipIt 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 <http://www.gnu.org/licenses/>.
*/
#include <glib.h>
#include <gtk/gtk.h>
#include <string.h>
#include <stdlib.h>
#include <X11/keysym.h>
#include "main.h"
#include "utils.h"
#include "history.h"
#include "keybinder.h"
#include "preferences.h"
#include "clipit-i18n.h"
GtkListStore *search_list;
GtkWidget *search_entry;
GtkWidget *treeview_search;
static void add_iter(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *piter, gpointer userdata)
{
GArray *sel = (GArray*)userdata;
GtkTreeIter iter = *piter;
g_array_append_val(sel, iter);
}
/* Search through the history */
static void search_history()
{
guint16 search_len = gtk_entry_get_text_length((GtkEntry*)search_entry);
/* Test if there is text in the search box */
if(search_len > 0)
{
if ((history != NULL) && (history->data != NULL))
{
char* search_input = (char *)g_strdup(gtk_entry_get_text((GtkEntry*)search_entry));
GtkTreeIter search_iter;
while(gtk_tree_model_get_iter_first((GtkTreeModel*)search_list, &search_iter))
gtk_list_store_remove(search_list, &search_iter);
/* Declare some variables */
GList* element;
gint element_number = 0;
/* Go through each element and adding each */
for (element = history; element != NULL; element = element->next)
{
history_item *elem_data = element->data;
GString* string = g_string_new((gchar*)elem_data->content);
gchar* strn_cmp_to = g_utf8_strdown(string->str, string->len);
gchar* strn_find = g_utf8_strdown(search_input, search_len);
char* result = strstr(strn_cmp_to, strn_find);
if(result)
{
GtkTreeIter row_iter;
gtk_list_store_append(search_list, &row_iter);
string = ellipsize_string(string);
string = remove_newlines_string(string);
int row_num = g_list_position(history, element);
gtk_list_store_set(search_list, &row_iter, 0, row_num, 1, string->str, -1);
}
/* Prepare for next item */
g_string_free(string, TRUE);
element_number++;
}
}
}
else
{
/* If nothing is searched for, we show all items */
if ((history != NULL) && (history->data != NULL))
{
GtkTreeIter search_iter;
/* First, we remove all items */
while(gtk_tree_model_get_iter_first((GtkTreeModel*)search_list, &search_iter))
gtk_list_store_remove(search_list, &search_iter);
/* Declare some variables */
GList *element;
/* Go through each element and adding each */
for (element = history; element != NULL; element = element->next)
{
history_item *elem_data = element->data;
GString *string = g_string_new((gchar*)elem_data->content);
GtkTreeIter row_iter;
gtk_list_store_append(search_list, &row_iter);
string = ellipsize_string(string);
string = remove_newlines_string(string);
int row_num = g_list_position(history, element);
gtk_list_store_set(search_list, &row_iter, 0, row_num, 1, string->str, -1);
/* Prepare for next item */
g_string_free(string, TRUE);
}
}
}
}
/* Called when Edit is selected from Manage dialog */
static void edit_selected()
{
GtkTreeSelection* search_selection = gtk_tree_view_get_selection((GtkTreeView*)treeview_search);
/* This checks if there's anything selected */
gint selected_count = gtk_tree_selection_count_selected_rows(search_selection);
if (selected_count > 0) {
/* Create clipboard buffer and set its text */
gint selected_item_nr;
GArray *sel = g_array_new(FALSE, FALSE, sizeof(GtkTreeIter));
gtk_tree_selection_selected_foreach(search_selection, add_iter, sel);
gtk_tree_selection_unselect_all(search_selection);
GtkTreeIter *iter = &g_array_index(sel, GtkTreeIter, 0);
gtk_tree_model_get((GtkTreeModel*)search_list, iter, 0, &selected_item_nr, -1);
g_array_free(sel, TRUE);
GList *element = g_list_nth(history, selected_item_nr);
history_item *elem_data = element->data;
GList* elementafter = element->next;
GtkTextBuffer* clipboard_buffer = gtk_text_buffer_new(NULL);
gtk_text_buffer_set_text(clipboard_buffer, (gchar*)elem_data->content, -1);
/* Create the dialog */
GtkWidget* dialog = gtk_dialog_new_with_buttons(_("Editing Clipboard"), NULL,
(GTK_DIALOG_MODAL + GTK_DIALOG_NO_SEPARATOR),
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
gtk_window_set_default_size((GtkWindow*)dialog, 450, 300);
gtk_window_set_icon((GtkWindow*)dialog, gtk_widget_render_icon(dialog, GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU, NULL));
/* Build the scrolled window with the text view */
GtkWidget* scrolled_window = gtk_scrolled_window_new((GtkAdjustment*) gtk_adjustment_new(0, 0, 0, 0, 0, 0),
(GtkAdjustment*) gtk_adjustment_new(0, 0, 0, 0, 0, 0));
gtk_scrolled_window_set_policy((GtkScrolledWindow*)scrolled_window,
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), scrolled_window, TRUE, TRUE, 2);
GtkWidget* text_view = gtk_text_view_new_with_buffer(clipboard_buffer);
gtk_text_view_set_left_margin((GtkTextView*)text_view, 2);
gtk_text_view_set_right_margin((GtkTextView*)text_view, 2);
gtk_container_add((GtkContainer*)scrolled_window, text_view);
GtkWidget *static_check = gtk_check_button_new_with_mnemonic(_("_Static item"));
gtk_toggle_button_set_active((GtkToggleButton*)static_check, elem_data->is_static);
gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), static_check, FALSE, FALSE, 2);
/* Run the dialog */
gtk_widget_show_all(dialog);
if (gtk_dialog_run((GtkDialog*)dialog) == GTK_RESPONSE_ACCEPT)
{
/* Save changes done to the clipboard */
GtkTextIter start, end;
gtk_text_buffer_get_start_iter(clipboard_buffer, &start);
gtk_text_buffer_get_end_iter(clipboard_buffer, &end);
/* Delete any duplicate */
delete_duplicate(gtk_text_buffer_get_text(clipboard_buffer, &start, &end, TRUE));
/* Remove old entry */
history = g_list_remove(history, elem_data);
/* Insert new element where the old one was */
history_item *hitem = g_new0(history_item, 1);
hitem->content = g_strdup(gtk_text_buffer_get_text(clipboard_buffer, &start, &end, TRUE));
hitem->is_static = gtk_toggle_button_get_active((GtkToggleButton*)static_check);
history = g_list_insert_before(history, elementafter, hitem);
if(selected_item_nr == 0)
{
GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
history_item *hitem = history->data;
gtk_clipboard_set_text(clipboard, hitem->content, -1);
}
}
gtk_widget_destroy(dialog);
search_history();
}
}
/* Called when Remove is selected from Manage dialog */
static void remove_selected()
{
GtkTreeSelection* search_selection = gtk_tree_view_get_selection((GtkTreeView*)treeview_search);
gint selected_count = gtk_tree_selection_count_selected_rows(search_selection);
if (selected_count > 0) {
gint i;
GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView*)treeview_search);
GtkListStore *store = GTK_LIST_STORE(model);
GArray *sel = g_array_new(FALSE, FALSE, sizeof(GtkTreeIter));
gtk_tree_selection_selected_foreach(search_selection, add_iter, sel);
gtk_tree_selection_unselect_all(search_selection);
for (i = 0; i < sel->len; i++) {
gint remove_item;
GtkTreeIter *iter = &g_array_index(sel, GtkTreeIter, i);
gtk_tree_model_get((GtkTreeModel*)search_list, iter, 0, &remove_item, -1);
if ((i == 0) && (remove_item == 0)) {
gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), "", -1);
gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), "", -1);
}
history = g_list_remove(history, g_list_nth_data(history, remove_item));
if (!gtk_list_store_remove(store, iter))
continue;
do {
gint item;
gtk_tree_model_get(model, iter, 0, &item, -1);
gtk_list_store_set(store, iter, 0, item - 1, -1);
} while (gtk_tree_model_iter_next(model, iter));
}
g_array_free(sel, TRUE);
}
}
/* Called when Remove all is selected from Manage dialog */
static void remove_all_selected(gpointer user_data)
{
/* Check for confirm clear option */
if (prefs.confirm_clear)
{
GtkWidget* confirm_dialog = gtk_message_dialog_new(NULL,
GTK_DIALOG_MODAL,
GTK_MESSAGE_OTHER,
GTK_BUTTONS_OK_CANCEL,
_("Clear the history?"));
gtk_window_set_title((GtkWindow*)confirm_dialog, _("Clear history"));
if (gtk_dialog_run((GtkDialog*)confirm_dialog) == GTK_RESPONSE_OK)
{
/* Clear history and free history-related variables */
g_list_free(history);
history = NULL;
save_history();
clear_main_data();
GtkTreeIter search_iter;
while(gtk_tree_model_get_iter_first((GtkTreeModel*)search_list, &search_iter))
gtk_list_store_remove(search_list, &search_iter);
}
gtk_widget_destroy(confirm_dialog);
}
else
{
/* Clear history and free history-related variables */
g_list_free(history);
history = NULL;
save_history();
clear_main_data();
GtkTreeIter search_iter;
while(gtk_tree_model_get_iter_first((GtkTreeModel*)search_list, &search_iter))
gtk_list_store_remove(search_list, &search_iter);
}
}
static void search_doubleclick()
{
GtkTreeSelection* search_selection = gtk_tree_view_get_selection((GtkTreeView*)treeview_search);
/* Check if selected */
gint selected_count = gtk_tree_selection_count_selected_rows(search_selection);
if (selected_count == 1) {
gint selected_item_nr;
GArray *sel = g_array_new(FALSE, FALSE, sizeof(GtkTreeIter));
gtk_tree_selection_selected_foreach(search_selection, add_iter, sel);
gtk_tree_selection_unselect_all(search_selection);
GtkTreeIter *iter = &g_array_index(sel, GtkTreeIter, 0);
gtk_tree_model_get((GtkTreeModel*)search_list, iter, 0, &selected_item_nr, -1);
g_array_free(sel, TRUE);
GList *element = g_list_nth(history, selected_item_nr);
history_item *elem_data = element->data;
GtkClipboard* prim = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
gtk_clipboard_set_text(prim, (gchar*)elem_data->content, -1);
gtk_clipboard_set_text(clip, (gchar*)elem_data->content, -1);
}
}
static gboolean search_click(GtkWidget *widget, GdkEventButton *event, GtkWidget *search_window)
{
if(event->type==GDK_2BUTTON_PRESS || event->type==GDK_3BUTTON_PRESS) {
search_doubleclick();
gtk_widget_destroy(search_window);
}
return FALSE;
}
static gboolean search_key_released(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
search_history();
return FALSE;
}
static gboolean treeview_key_pressed(GtkWidget *widget, GdkEventKey *event, GtkWidget *search_window)
{
switch (event->keyval) {
case XK_Escape:
case XK_Home:
case XK_Up:
case XK_Down:
case XK_Page_Up:
case XK_Page_Down:
case XK_End:
case XK_Shift_L:
case XK_Shift_R:
case XK_Control_L:
case XK_Control_R:
case XK_Tab: // allow to switch focus by the Tab key
return FALSE;
case XK_Return:
search_doubleclick();
gtk_widget_destroy(search_window);
break;
case XK_Delete:
remove_selected();
break;
}
return TRUE;
}
void search_window_response(GtkDialog *dialog, gint response_id, gpointer user_data)
{
if(response_id < 0)
{
save_history();
gtk_widget_destroy((GtkWidget*)dialog);
}
}
/* Shows the search dialog */
gboolean show_search()
{
/* Prevent multiple instances */
if(gtk_grab_get_current()) {
/* A window is already open, so we present it to the user */
GtkWidget *toplevel = gtk_widget_get_toplevel(gtk_grab_get_current());
gtk_window_present((GtkWindow*)toplevel);
return FALSE;
}
/* Declare some variables */
GtkWidget *hbox;
GtkTreeViewColumn *tree_column;
/* Create the dialog */
GtkWidget* search_dialog = gtk_dialog_new();
gtk_window_set_icon((GtkWindow*)search_dialog, gtk_widget_render_icon(search_dialog, GTK_STOCK_FIND, GTK_ICON_SIZE_MENU, NULL));
gchar *orig_title = _("Manage History");
gchar *title = 0;
if (prefs.offline_mode)
title = g_strconcat(orig_title, _(" (Offline mode)"), NULL);
else
title = g_strdup(orig_title);
gtk_window_set_title((GtkWindow*)search_dialog, title);
g_free(title);
gtk_window_set_resizable((GtkWindow*)search_dialog, TRUE);
gtk_window_set_position((GtkWindow*)search_dialog, GTK_WIN_POS_CENTER);
GdkScreen* screen = gtk_window_get_screen(GTK_WINDOW(search_dialog));
gint screen_height = gdk_screen_get_height(screen)-120;
gint win_height = 600;
if (win_height > screen_height)
win_height = screen_height;
GtkWidget* vbox_search = gtk_vbox_new(FALSE, 10);
gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area (GTK_DIALOG(search_dialog))), vbox_search, TRUE, TRUE, 2);
gtk_widget_set_size_request((GtkWidget*)vbox_search, 400, win_height);
hbox = gtk_hbox_new(TRUE, 4);
gtk_box_pack_start((GtkBox*)vbox_search, hbox, FALSE, FALSE, 0);
search_entry = gtk_entry_new();
gtk_box_pack_end((GtkBox*)hbox, search_entry, TRUE, TRUE, 0);
g_signal_connect((GObject*)search_entry, "key-release-event", (GCallback)search_key_released, NULL);
/* Build the exclude treeview */
GtkWidget* scrolled_window_search = gtk_scrolled_window_new(
(GtkAdjustment*)gtk_adjustment_new(0, 0, 0, 0, 0, 0),
(GtkAdjustment*)gtk_adjustment_new(0, 0, 0, 0, 0, 0));
gtk_scrolled_window_set_policy((GtkScrolledWindow*)scrolled_window_search, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type((GtkScrolledWindow*)scrolled_window_search, GTK_SHADOW_ETCHED_OUT);
treeview_search = gtk_tree_view_new();
gtk_tree_view_set_rules_hint((GtkTreeView*)treeview_search, TRUE);
search_list = gtk_list_store_new(2, G_TYPE_UINT, G_TYPE_STRING);
gtk_tree_view_set_model((GtkTreeView*)treeview_search, (GtkTreeModel*)search_list);
GtkCellRenderer* cell_renderer = gtk_cell_renderer_text_new();
tree_column = gtk_tree_view_column_new_with_attributes("ID", cell_renderer, NULL);
gtk_tree_view_column_set_visible(tree_column, FALSE);
gtk_tree_view_append_column((GtkTreeView*)treeview_search, tree_column);
cell_renderer = gtk_cell_renderer_text_new();
g_object_set(cell_renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
g_object_set(cell_renderer, "single-paragraph-mode", TRUE, NULL);
tree_column = gtk_tree_view_column_new_with_attributes(_("Results"), cell_renderer, "text", 1, NULL);
gtk_tree_view_append_column((GtkTreeView*)treeview_search, tree_column);
gtk_container_add((GtkContainer*)scrolled_window_search, treeview_search);
gtk_box_pack_start((GtkBox*)vbox_search, scrolled_window_search, TRUE, TRUE, 0);
GtkWidget* edit_button = gtk_dialog_add_button((GtkDialog*)search_dialog, GTK_STOCK_EDIT, 1);
g_signal_connect((GObject*)edit_button, "clicked", (GCallback)edit_selected, NULL);
GtkWidget* remove_button = gtk_dialog_add_button((GtkDialog*)search_dialog, GTK_STOCK_REMOVE, 1);
g_signal_connect((GObject*)remove_button, "clicked", (GCallback)remove_selected, NULL);
GtkWidget* remove_all_button = gtk_dialog_add_button((GtkDialog*)search_dialog, _("Remove all"), 1);
g_signal_connect((GObject*)remove_all_button, "clicked", (GCallback)remove_all_selected, NULL);
GtkWidget* close_button = gtk_dialog_add_button((GtkDialog*)search_dialog, GTK_STOCK_CLOSE, GTK_RESPONSE_OK);
g_signal_connect((GObject*)close_button, "clicked", (GCallback)search_history, NULL);
GtkTreeSelection* search_selection = gtk_tree_view_get_selection((GtkTreeView*)treeview_search);
gtk_tree_selection_set_mode(search_selection, GTK_SELECTION_MULTIPLE);
g_signal_connect((GObject*)treeview_search, "button_press_event", (GCallback)search_click, search_dialog);
g_signal_connect((GObject*)treeview_search, "key-press-event", (GCallback)treeview_key_pressed, search_dialog);
g_signal_connect((GtkDialog*)search_dialog, "response", (GCallback)search_window_response, search_dialog);
gtk_widget_show_all(vbox_search);
/* show the dialog */
gtk_widget_show_all((GtkWidget*)search_dialog);
gtk_widget_set_sensitive((GtkWidget*)search_dialog, TRUE);
gtk_grab_add((GtkWidget*)search_dialog);
/* Populate the list */
search_history();
return FALSE;
}