454 lines
10 KiB
C
454 lines
10 KiB
C
#include "wmcliphist.h"
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
|
|
/* color of locked item */
|
|
gchar locked_color_str[32] = DEF_LOCKED_COLOR;
|
|
GdkRGBA locked_color;
|
|
|
|
/* Exec on middle click? */
|
|
int exec_middleclick = 1;
|
|
|
|
/* main window widget */
|
|
GtkWidget *main_window;
|
|
|
|
/* dock icon widget */
|
|
GtkWidget *dock_app;
|
|
|
|
/* clipboard history menu */
|
|
GtkWidget *menu_hist;
|
|
GtkWidget *menu_title;
|
|
gint submenu_count = 0;
|
|
|
|
/* application menu */
|
|
GtkWidget *menu_app;
|
|
GtkWidget *menu_app_clip_lock;
|
|
GtkWidget *menu_app_clip_ignore;
|
|
GtkWidget *menu_app_exit;
|
|
GtkWidget *menu_app_save;
|
|
|
|
/* button */
|
|
GtkWidget *button;
|
|
|
|
/* pixmap */
|
|
GtkWidget *pixmap;
|
|
|
|
/* ==========================================================================
|
|
* clipboard history menu
|
|
*/
|
|
|
|
/*
|
|
* history menu item button click function
|
|
*/
|
|
static gboolean
|
|
menu_item_button_released(GtkWidget *widget,
|
|
GdkEvent *event,
|
|
gpointer user_data)
|
|
{
|
|
GdkEventButton *bevent = (GdkEventButton *)event;
|
|
HISTORY_ITEM *data = user_data;
|
|
|
|
begin_func("menu_item_button_released");
|
|
|
|
/* button 2 or 3 - exec or (un)lock item respectively */
|
|
if (bevent->button == 2) {
|
|
if (exec_middleclick) {
|
|
gtk_menu_popdown(GTK_MENU(menu_hist));
|
|
exec_item(data->content, NULL);
|
|
}
|
|
return_val(TRUE);
|
|
} else if (bevent->button == 3) {
|
|
if (data->locked == 0) {
|
|
|
|
/* cannot lock all records */
|
|
if (locked_count == num_items_to_keep - 1) {
|
|
show_message("There must remain at least one "
|
|
"unlocked item\nwhen menu is full!",
|
|
"Warning", "OK", NULL, NULL);
|
|
return_val(TRUE);
|
|
}
|
|
|
|
gtk_widget_override_color(
|
|
gtk_bin_get_child(GTK_BIN(data->menu_item)),
|
|
GTK_STATE_FLAG_NORMAL, &locked_color);
|
|
data->locked = 1;
|
|
locked_count++;
|
|
|
|
} else {
|
|
gtk_widget_override_color(
|
|
gtk_bin_get_child(GTK_BIN(data->menu_item)),
|
|
GTK_STATE_FLAG_NORMAL, NULL);
|
|
data->locked = 0;
|
|
locked_count--;
|
|
}
|
|
} else {
|
|
return_val(FALSE);
|
|
}
|
|
|
|
return_val(TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* history menu item left click or keypress function
|
|
*/
|
|
static gboolean
|
|
menu_item_activated(GtkWidget *widget, gpointer user_data)
|
|
{
|
|
move_item_to_begin((HISTORY_ITEM *) user_data);
|
|
return_val(TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* checks, if there is already such item in menu,
|
|
* in which case it moves it to the begining
|
|
*/
|
|
HISTORY_ITEM *
|
|
menu_item_exists(gchar *content, GtkWidget *submenu)
|
|
{
|
|
HISTORY_ITEM *hist_item;
|
|
GList *list_node;
|
|
|
|
begin_func("menu_item_exists");
|
|
|
|
list_node = g_list_last(history_items);
|
|
while (list_node) {
|
|
hist_item = (HISTORY_ITEM *)list_node->data;
|
|
if (hist_item->menu == submenu
|
|
&& g_utf8_collate(hist_item->content, content)
|
|
== 0) {
|
|
gtk_menu_reorder_child(GTK_MENU(hist_item->menu),
|
|
hist_item->menu_item, 1);
|
|
history_items = g_list_remove_link(history_items,
|
|
list_node);
|
|
history_items = g_list_concat(list_node, history_items);
|
|
|
|
return_val(hist_item);
|
|
}
|
|
list_node = g_list_previous(list_node);
|
|
}
|
|
|
|
return_val(NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* add new item to menu
|
|
*/
|
|
HISTORY_ITEM *
|
|
menu_item_add(gchar *content, gint locked, GtkWidget *target_menu)
|
|
{
|
|
GList *list_node;
|
|
gint i;
|
|
gchar *menu_item_name;
|
|
gchar *fixed_menu_item_name;
|
|
gsize length;
|
|
HISTORY_ITEM *hist_item;
|
|
|
|
begin_func("menu_item_add");
|
|
|
|
hist_item = menu_item_exists(content, target_menu);
|
|
if (hist_item != NULL) {
|
|
dump_history_list("reorder");
|
|
return_val(hist_item);
|
|
}
|
|
|
|
if (num_items == num_items_to_keep) {
|
|
/* max item limit reached, destroy oldest one */
|
|
list_node = g_list_last(history_items);
|
|
while (1) {
|
|
hist_item = (HISTORY_ITEM*)
|
|
list_node->data;
|
|
if (hist_item->locked == 0)
|
|
break;
|
|
list_node = g_list_previous(list_node);
|
|
g_assert((list_node != NULL));
|
|
}
|
|
|
|
history_items = g_list_remove_link(history_items, list_node);
|
|
gtk_container_remove(GTK_CONTAINER(hist_item->menu),
|
|
hist_item->menu_item);
|
|
gtk_widget_destroy(hist_item->menu_item);
|
|
g_free(hist_item->content);
|
|
g_free(hist_item);
|
|
g_list_free_1(list_node);
|
|
num_items--;
|
|
dump_history_list("remove oldest");
|
|
}
|
|
|
|
/* prepare menu item name */
|
|
menu_item_name = g_new0(char, MAX_ITEM_LENGTH * 2 + 1);
|
|
memset(menu_item_name, 0, MAX_ITEM_LENGTH * 2 + 1);
|
|
length = g_utf8_strlen(content, -1);
|
|
if (length > (size_t) (MAX_ITEM_LENGTH)) {
|
|
g_utf8_strncpy(menu_item_name, content, MAX_ITEM_LENGTH - 4);
|
|
strcat(menu_item_name, "...");
|
|
} else {
|
|
strcpy(menu_item_name, content);
|
|
}
|
|
|
|
/* do some menu item name cleanups */
|
|
fixed_menu_item_name = g_new0(char, strlen(menu_item_name) + 1);
|
|
for (i = 0; i < g_utf8_strlen(menu_item_name, -1); i++) {
|
|
gchar *uchar_ptr = g_utf8_offset_to_pointer(menu_item_name, i);
|
|
gunichar uchar = g_utf8_get_char(uchar_ptr);
|
|
if (g_unichar_isprint(uchar)) {
|
|
gchar *decoded_char = g_ucs4_to_utf8(&uchar, 1, NULL,
|
|
NULL, NULL);
|
|
strcat(fixed_menu_item_name, decoded_char);
|
|
g_free(decoded_char);
|
|
} else {
|
|
strcat(fixed_menu_item_name, "_");
|
|
}
|
|
}
|
|
g_free(menu_item_name);
|
|
menu_item_name = fixed_menu_item_name;
|
|
|
|
/* create menu item */
|
|
hist_item = g_new0(HISTORY_ITEM, 1);
|
|
hist_item->menu_item = gtk_menu_item_new_with_label(menu_item_name);
|
|
hist_item->content = g_strdup(content);
|
|
hist_item->locked = locked;
|
|
hist_item->menu = target_menu;
|
|
|
|
if (locked == 1) {
|
|
gtk_widget_override_color(
|
|
gtk_bin_get_child(GTK_BIN(hist_item->menu_item)),
|
|
GTK_STATE_FLAG_NORMAL, &locked_color);
|
|
locked_count++;
|
|
}
|
|
|
|
/* add to menu */
|
|
gtk_menu_shell_insert(GTK_MENU_SHELL(hist_item->menu), hist_item->menu_item, 1);
|
|
|
|
|
|
/* connect actions to signals */
|
|
g_signal_connect(G_OBJECT(hist_item->menu_item),
|
|
"button-release-event",
|
|
G_CALLBACK(menu_item_button_released),
|
|
(gpointer)hist_item);
|
|
|
|
g_signal_connect(G_OBJECT(hist_item->menu_item),
|
|
"activate",
|
|
G_CALLBACK(menu_item_activated),
|
|
(gpointer)hist_item);
|
|
|
|
gtk_widget_show(hist_item->menu_item);
|
|
|
|
history_items = g_list_insert(history_items, hist_item, 0);
|
|
|
|
num_items++;
|
|
|
|
return_val(hist_item);
|
|
}
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
* application menu
|
|
*/
|
|
|
|
/*
|
|
* application main menu handler
|
|
*/
|
|
gboolean
|
|
menu_app_item_click(GtkWidget *menuitem, gpointer data)
|
|
{
|
|
gint button;
|
|
|
|
begin_func("menu_app_item_click");
|
|
|
|
switch (GPOINTER_TO_INT(data)) {
|
|
/* save history menu */
|
|
case 0:
|
|
if (history_save() != 0) {
|
|
button = show_message("History was NOT saved.\n",
|
|
"Warning", "OK", NULL, NULL);
|
|
}
|
|
return_val(TRUE);
|
|
|
|
/* exit menu */
|
|
case 1:
|
|
if (history_save() != 0) {
|
|
button = show_message("History was NOT saved.\n"
|
|
"Do you really want to exit?",
|
|
"Error", "Yes", "No", NULL);
|
|
if (button != 0)
|
|
return_val(TRUE);
|
|
}
|
|
history_free();
|
|
rcconfig_free();
|
|
|
|
exit(0);
|
|
return_val(TRUE);
|
|
}
|
|
return_val(FALSE);
|
|
}
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
* dock button press
|
|
*/
|
|
|
|
/*
|
|
* dock button click response
|
|
*/
|
|
gboolean
|
|
button_press(GtkWidget *widget, GdkEvent *event, gpointer data)
|
|
{
|
|
begin_func("button_press");
|
|
|
|
if (event->type == GDK_BUTTON_PRESS) {
|
|
GdkEventButton *bevent = (GdkEventButton *)event;
|
|
|
|
switch (bevent->button) {
|
|
case 1:
|
|
/* popup history menu */
|
|
gtk_menu_popup(GTK_MENU(menu_hist),
|
|
NULL, NULL,
|
|
NULL, NULL,
|
|
bevent->button,
|
|
bevent->time);
|
|
return_val(TRUE);
|
|
|
|
case 3:
|
|
/* popup application menu */
|
|
gtk_menu_popup(GTK_MENU(menu_app),
|
|
NULL, NULL,
|
|
NULL, NULL,
|
|
bevent->button,
|
|
bevent->time);
|
|
return_val(TRUE);
|
|
}
|
|
}
|
|
|
|
return_val(FALSE);
|
|
}
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
* message dialogs
|
|
*/
|
|
|
|
static GMainLoop *loop;
|
|
static gint button_pressed;
|
|
|
|
|
|
static gboolean
|
|
dialog_button_press(GtkWidget *button, gpointer data)
|
|
{
|
|
begin_func("dialog_button_press");
|
|
|
|
button_pressed = GPOINTER_TO_INT(data);
|
|
g_main_quit(loop);
|
|
|
|
return_val(TRUE);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
dialog_handle_key_press_event(GdkEventKey *event, gpointer data, guint key)
|
|
{
|
|
if(event->type != GDK_KEY_PRESS)
|
|
return_val(FALSE);
|
|
if(event->keyval != key)
|
|
return_val(FALSE);
|
|
button_pressed = GPOINTER_TO_INT(data);
|
|
g_main_quit(loop);
|
|
|
|
return_val(TRUE);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
dialog_key_press_yes(GtkWidget *button, GdkEventKey *event, gpointer data)
|
|
{
|
|
begin_func("dialog_key_press_yes");
|
|
return dialog_handle_key_press_event(event, data, GDK_KEY_Return);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
dialog_key_press_no(GtkWidget *button, GdkEventKey *event, gpointer data)
|
|
{
|
|
begin_func("dialog_key_press_no");
|
|
return dialog_handle_key_press_event(event, data, GDK_KEY_Escape);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* open dialog with specified message andbuttons
|
|
* and return number of button pressed
|
|
*/
|
|
gint
|
|
show_message(gchar *message, char *title,
|
|
char *b0_text, char *b1_text, char *b2_text)
|
|
{
|
|
GtkWidget *dialog,
|
|
*label,
|
|
*button_0,
|
|
*button_1,
|
|
*button_2;
|
|
|
|
begin_func("show_message");
|
|
|
|
/* create the main widgets */
|
|
dialog = gtk_dialog_new();
|
|
label = gtk_label_new(message);
|
|
|
|
/* create buttons and set signals */
|
|
gtk_dialog_add_button(GTK_DIALOG(dialog), b0_text, 0);
|
|
button_0 = gtk_dialog_get_widget_for_response(GTK_DIALOG(dialog), 0);
|
|
g_signal_connect(G_OBJECT(button_0), "clicked",
|
|
G_CALLBACK(dialog_button_press),
|
|
GINT_TO_POINTER(0));
|
|
if (!b2_text) {
|
|
g_signal_connect(G_OBJECT(dialog), "key-press-event",
|
|
G_CALLBACK(dialog_key_press_yes),
|
|
GINT_TO_POINTER(0));
|
|
}
|
|
|
|
if (b1_text != NULL) {
|
|
gtk_dialog_add_button(GTK_DIALOG(dialog), b1_text, 1);
|
|
button_1 = gtk_dialog_get_widget_for_response(GTK_DIALOG(dialog), 1);
|
|
g_signal_connect(G_OBJECT(button_1), "clicked",
|
|
G_CALLBACK(dialog_button_press),
|
|
GINT_TO_POINTER(1));
|
|
if (!b2_text) {
|
|
g_signal_connect(G_OBJECT(dialog), "key-press-event",
|
|
G_CALLBACK(dialog_key_press_no),
|
|
GINT_TO_POINTER(1));
|
|
}
|
|
}
|
|
|
|
if (b2_text) {
|
|
gtk_dialog_add_button(GTK_DIALOG(dialog), b2_text, 2);
|
|
button_2 = gtk_dialog_get_widget_for_response(GTK_DIALOG(dialog), 2);
|
|
g_signal_connect(G_OBJECT(button_2), "clicked",
|
|
G_CALLBACK(dialog_button_press),
|
|
GINT_TO_POINTER(2));
|
|
}
|
|
|
|
/* add the label, and show everything we've added to the dialog. */
|
|
gtk_misc_set_padding(>K_LABEL(label)->misc, 10, 10);
|
|
gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label);
|
|
gtk_widget_show_all(dialog);
|
|
|
|
/* set window title */
|
|
gtk_window_set_title(GTK_WINDOW(dialog), title);
|
|
|
|
loop = g_main_new(FALSE);
|
|
g_main_run(loop);
|
|
g_main_destroy(loop);
|
|
gtk_widget_destroy(dialog);
|
|
|
|
return_val(button_pressed);
|
|
}
|