diff --git a/ChangeLog b/ChangeLog index 611e525..5a23fd2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +ClipIt-1.4.0-20110521001 - 21 May. 2011 + + Added: We are now using the shipped icon in the systray. + +ClipIt-1.4.0-20110520002 - 20 May. 2011 + + Fixed: Remove duplicate truncate_history functionality. + + Added: Added basic history item structure. + +ClipIt-1.4.0-20110520001 - 20 May. 2011 + + Fixed: Deleted items are now properly removed from the history. + + Fixed: Double clicking an item in the "Manage history" dialog now + properly selects it again. + + Added: [Enter] (for selecting the item) and [Delete] (for removing + the item(s)) now works in the "Manage history" dialog. + +ClipIt-1.4.0-20110518002 - 18 May. 2011 + + Added: Also added 1-0 shortcuts to the history menu. + +ClipIt-1.4.0-20110518001 - 18 May. 2011 + + Fixed: Re-enabled indicator support. + + Added: We are now installing a sepparate icon for clipit (not using it + yet, though). + + Added: Search as you type in "Manage history" dialog. + + Added: 1-0 shortcuts for fast selecting of menu items. + + Added: Possiblity to select multiple items for deletion in the + "Manage history" dialog. + +ClipIt-1.4.0-20110506001 - 06 May. 2011 + + Added: Added option to automatically paste an entry after selecting it. + + Fixed: Re-enabled indicator since it should now work properly. + ClipIt-1.3.13-20110503001 - 03 May. 2011 + Fixed: Bug in the indicator code that makes ClipIt crash X under certain circumstances. @@ -64,25 +94,26 @@ ClipIt-v1.3.3-23112010001 - 23 Nov. 2010 item properly. ClipIt-v1.3.2-22112010001 - 22 Nov. 2010 - + Fixed: Fixed most of the markup and indentation (replaced double spaces - with tabs). - + Fixed: Removed clipboard restoring from history, as this seems to cause - more problems than it solves. + + Fixed: Fixed most of the markup and indentation (replaced double + spaces with tabs). + + Fixed: Removed clipboard restoring from history, as this seems to + cause more problems than it solves. ClipIt-v1.3.1-17112010001 - 17 Nov. 2010 + Added: Added autostart support for LXDE. + Fixed: Fixed problem with "Edit" window not appearing. ClipIt-v1.3.0-14112010001 - 14 Nov. 2010 - + Added: Added "Edit" and "Remove" buttons to the "Manage History" dialog. + + Added: Added "Edit" and "Remove" buttons to the "Manage History" + dialog. + Fixed: Major speed improvements for big history entries while: searching through the history; populating the "Manage History" dialog; generating the popup menus; All of these should now be more than 99% faster, at the expense of an (at most) 1MB overhead. I think it's worth it, though. - + Fixed: Fixed a drawing issue with the small history menu when activated - by clicking on the systray icon. + + Fixed: Fixed a drawing issue with the small history menu when + activated by clicking on the systray icon. ClipIt-v1.2.6-12112010003 - 12 Nov. 2010 + Fixed: Removed reference to pthread.h from configure.in. @@ -91,8 +122,8 @@ ClipIt-v1.2.6-12112010003 - 12 Nov. 2010 ClipIt-v1.2.5-12112010002 - 12 Nov. 2010 + Fixed: Now using autopoint instead of gettextize to avoid waiting for user input. - + Fixed: Added AM_MAINTAINER_MODE, to get rid of the disable-maintainer-mode - warning. + + Fixed: Added AM_MAINTAINER_MODE, to get rid of the + disable-maintainer-mode warning. + Fixed: Fixed a small typo in main.c. + Fixed: removed dependency of pthread.h (the pthread_exit function that we use is also included in glib.h). @@ -135,9 +166,10 @@ ClipIt-v1.0.0-05112010001 - 05 Nov. 2010 + Created "Small history" for quick access to last used items. + Renamed the old history to "Full history". + "Full history" can now hold up to 1000 items. - + Ability to put the button for the "Full history" in the systray - menu. - + Created a Search function, which allows searching through history. + + Ability to put the button for the "Full history" in the + systray menu. + + Created a Search function, which allows searching through + history. + Added Search hotkey. + Created Exceptions: you can enter C or Perl regex into the window and diff --git a/Makefile.am b/Makefile.am index 258eb28..3465d7b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,6 +11,7 @@ EXTRA_DIST = \ m4/ChangeLog \ $(desktop_DATA) \ $(autostart_DATA) \ + $(pixmaps_DATA) \ intltool-merge.in \ intltool-update.in \ intltool-extract.in diff --git a/Makefile.in b/Makefile.in index d587e98..f58ad44 100644 --- a/Makefile.in +++ b/Makefile.in @@ -259,6 +259,7 @@ EXTRA_DIST = \ m4/ChangeLog \ $(desktop_DATA) \ $(autostart_DATA) \ + $(pixmaps_DATA) \ intltool-merge.in \ intltool-update.in \ intltool-extract.in diff --git a/TODO b/TODO index 04aeb1b..bd0c6d1 100644 --- a/TODO +++ b/TODO @@ -4,4 +4,4 @@ actually use that return value to display an error if there was one. + Redo Preferences dialog. + Clean up the code and indent it properly. -+ Move all pop-up and menu functions from main.c to menus.c. ++ history structure has to be changed in: main.c, manage.c, history.c diff --git a/configure b/configure index 14c8957..598d6df 100644 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.67 for clipit 1.3.13. +# Generated by GNU Autoconf 2.67 for clipit 1.4.0. # # Report bugs to . # @@ -552,8 +552,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='clipit' PACKAGE_TARNAME='clipit' -PACKAGE_VERSION='1.3.13' -PACKAGE_STRING='clipit 1.3.13' +PACKAGE_VERSION='1.4.0' +PACKAGE_STRING='clipit 1.4.0' PACKAGE_BUGREPORT='oss@rspwn.com' PACKAGE_URL='' @@ -1328,7 +1328,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures clipit 1.3.13 to adapt to many kinds of systems. +\`configure' configures clipit 1.4.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1398,7 +1398,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of clipit 1.3.13:";; + short | recursive ) echo "Configuration of clipit 1.4.0:";; esac cat <<\_ACEOF @@ -1511,7 +1511,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -clipit configure 1.3.13 +clipit configure 1.4.0 generated by GNU Autoconf 2.67 Copyright (C) 2010 Free Software Foundation, Inc. @@ -1880,7 +1880,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by clipit $as_me 1.3.13, which was +It was created by clipit $as_me 1.4.0, which was generated by GNU Autoconf 2.67. Invocation command line was $ $0 $@ @@ -2696,7 +2696,7 @@ fi # Define the identity of the package. PACKAGE=clipit - VERSION=1.3.13 + VERSION=1.4.0 cat >>confdefs.h <<_ACEOF @@ -9764,7 +9764,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by clipit $as_me 1.3.13, which was +This file was extended by clipit $as_me 1.4.0, which was generated by GNU Autoconf 2.67. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -9830,7 +9830,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -clipit config.status 1.3.13 +clipit config.status 1.4.0 configured by $0, generated by GNU Autoconf 2.67, with options \\"\$ac_cs_config\\" diff --git a/configure.in b/configure.in index b285c46..bf35536 100644 --- a/configure.in +++ b/configure.in @@ -2,7 +2,7 @@ # Autoconf/automake. # ------------------------------------------------------------------------------- AC_PREREQ([2.5]) -AC_INIT([clipit], [1.3.13], [oss@rspwn.com]) +AC_INIT([clipit], [1.4.0], [oss@rspwn.com]) AM_INIT_AUTOMAKE([AC_PACKAGE_TARNAME()], [AC_PACKAGE_VERSION()]) AC_CONFIG_MACRO_DIR([m4]) @@ -37,7 +37,7 @@ APPINDICATOR_REQUIRED_VERSION=0.2.4 AC_ARG_ENABLE([appindicator], AS_HELP_STRING([--enable-appindicator[=@<:@no/auto/yes@:>@]],[Build support for application indicators]), [enable_appindicator=$enableval], - [enable_appindicator="no"]) + [enable_appindicator="auto"]) if test x$enable_appindicator = xauto ; then diff --git a/data/Makefile.am b/data/Makefile.am index be564c2..f9baacd 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -8,9 +8,13 @@ autostartdir = $(sysconfdir)/xdg/autostart autostart_in_file = clipit-startup.desktop.in autostart_DATA = $(autostart_in_file:.desktop.in=.desktop) +pixmapsdir = $(datarootdir)/pixmaps/clipit +pixmaps_DATA = trayicon.svg + EXTRA_DIST = \ $(desktop_in_file) \ - $(autostart_in_file) + $(autostart_in_file) \ + $(pixmaps_DATA) CLEANFILES = \ $(desktop_DATA) \ diff --git a/data/Makefile.in b/data/Makefile.in index 9116eb6..7d9b323 100644 --- a/data/Makefile.in +++ b/data/Makefile.in @@ -73,8 +73,8 @@ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(autostartdir)" \ - "$(DESTDIR)$(desktopdir)" -DATA = $(autostart_DATA) $(desktop_DATA) + "$(DESTDIR)$(desktopdir)" "$(DESTDIR)$(pixmapsdir)" +DATA = $(autostart_DATA) $(desktop_DATA) $(pixmaps_DATA) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ @@ -224,9 +224,12 @@ desktop_DATA = $(desktop_in_file:.desktop.in=.desktop) autostartdir = $(sysconfdir)/xdg/autostart autostart_in_file = clipit-startup.desktop.in autostart_DATA = $(autostart_in_file:.desktop.in=.desktop) +pixmapsdir = $(datarootdir)/pixmaps/clipit +pixmaps_DATA = trayicon.svg EXTRA_DIST = \ $(desktop_in_file) \ - $(autostart_in_file) + $(autostart_in_file) \ + $(pixmaps_DATA) CLEANFILES = \ $(desktop_DATA) \ @@ -309,6 +312,26 @@ uninstall-desktopDATA: test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(desktopdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(desktopdir)" && rm -f $$files +install-pixmapsDATA: $(pixmaps_DATA) + @$(NORMAL_INSTALL) + test -z "$(pixmapsdir)" || $(MKDIR_P) "$(DESTDIR)$(pixmapsdir)" + @list='$(pixmaps_DATA)'; test -n "$(pixmapsdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pixmapsdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pixmapsdir)" || exit $$?; \ + done + +uninstall-pixmapsDATA: + @$(NORMAL_UNINSTALL) + @list='$(pixmaps_DATA)'; test -n "$(pixmapsdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(pixmapsdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(pixmapsdir)" && rm -f $$files tags: TAGS TAGS: @@ -350,7 +373,7 @@ check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: - for dir in "$(DESTDIR)$(autostartdir)" "$(DESTDIR)$(desktopdir)"; do \ + for dir in "$(DESTDIR)$(autostartdir)" "$(DESTDIR)$(desktopdir)" "$(DESTDIR)$(pixmapsdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am @@ -400,7 +423,8 @@ info: info-am info-am: -install-data-am: install-autostartDATA install-desktopDATA +install-data-am: install-autostartDATA install-desktopDATA \ + install-pixmapsDATA install-dvi: install-dvi-am @@ -444,7 +468,8 @@ ps: ps-am ps-am: -uninstall-am: uninstall-autostartDATA uninstall-desktopDATA +uninstall-am: uninstall-autostartDATA uninstall-desktopDATA \ + uninstall-pixmapsDATA .MAKE: install-am install-strip @@ -454,11 +479,12 @@ uninstall-am: uninstall-autostartDATA uninstall-desktopDATA install-data-am install-desktopDATA install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ - install-pdf-am install-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ - pdf-am ps ps-am uninstall uninstall-am uninstall-autostartDATA \ - uninstall-desktopDATA + install-pdf-am install-pixmapsDATA install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am \ + uninstall-autostartDATA uninstall-desktopDATA \ + uninstall-pixmapsDATA @INTLTOOL_DESKTOP_RULE@ diff --git a/data/trayicon.svg b/data/trayicon.svg new file mode 100644 index 0000000..0504856 --- /dev/null +++ b/data/trayicon.svg @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 5829907..33038ea 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,6 @@ -AM_CFLAGS = -I$(top_srcdir) -DCLIPITLOCALEDIR=\""$(clipitlocaledir)"\" +AM_CPPFLAGS = -I$(top_srcdir) +AM_CFLAGS = -DCLIPITLOCALEDIR=\""$(clipitlocaledir)"\" \ + -DCLIPITPIXMAPSDIR=\""$(datarootdir)"/pixmaps/clipit\" INCLUDES = $(GTK_CFLAGS) $(APPINDICATOR_CFLAGS) LDADD = $(GTK_LIBS) $(APPINDICATOR_LIBS) $(X11_LIBS) AM_LDFLAGS = -Wl,--as-needed @@ -8,11 +10,11 @@ bin_PROGRAMS = clipit DISTCLEANFILES = *.bak *.log *~ .deps/*.P clipit_SOURCES = main.c main.h \ - utils.c utils.h \ - clipit-i18n.h\ - daemon.c daemon.h \ - history.c history.h \ - keybinder.c keybinder.h \ - preferences.c preferences.h \ - manage.c manage.h \ - eggaccelerators.c eggaccelerators.h + utils.c utils.h \ + clipit-i18n.h\ + daemon.c daemon.h \ + history.c history.h \ + keybinder.c keybinder.h \ + preferences.c preferences.h \ + manage.c manage.h \ + eggaccelerators.c eggaccelerators.h diff --git a/src/Makefile.in b/src/Makefile.in index 2f6528e..48e8728 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -215,20 +215,23 @@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -AM_CFLAGS = -I$(top_srcdir) -DCLIPITLOCALEDIR=\""$(clipitlocaledir)"\" +AM_CPPFLAGS = -I$(top_srcdir) +AM_CFLAGS = -DCLIPITLOCALEDIR=\""$(clipitlocaledir)"\" \ + -DCLIPITPIXMAPSDIR=\""$(datarootdir)"/pixmaps/clipit\" + INCLUDES = $(GTK_CFLAGS) $(APPINDICATOR_CFLAGS) LDADD = $(GTK_LIBS) $(APPINDICATOR_LIBS) $(X11_LIBS) AM_LDFLAGS = -Wl,--as-needed DISTCLEANFILES = *.bak *.log *~ .deps/*.P clipit_SOURCES = main.c main.h \ - utils.c utils.h \ - clipit-i18n.h\ - daemon.c daemon.h \ - history.c history.h \ - keybinder.c keybinder.h \ - preferences.c preferences.h \ - manage.c manage.h \ - eggaccelerators.c eggaccelerators.h + utils.c utils.h \ + clipit-i18n.h\ + daemon.c daemon.h \ + history.c history.h \ + keybinder.c keybinder.h \ + preferences.c preferences.h \ + manage.c manage.h \ + eggaccelerators.c eggaccelerators.h all: all-am diff --git a/src/history.c b/src/history.c index 5ef6274..05a9c60 100644 --- a/src/history.c +++ b/src/history.c @@ -33,7 +33,7 @@ GSList *history; /* Reads history from DATADIR/clipit/history */ -void read_history () +void read_history() { /* Build file path */ gchar *history_path = g_build_filename(g_get_user_data_dir(), @@ -140,18 +140,7 @@ void check_and_append(gchar *item) void append_item(gchar *item) { history = g_slist_prepend(history, g_strdup(item)); - /* Shorten history if necessary */ - GSList *last_possible_element = g_slist_nth(history, - prefs.history_limit - 1); - if (last_possible_element) - { - /* Free last posible element and subsequent elements */ - g_slist_free(last_possible_element->next); - last_possible_element->next = NULL; - } - /* Save changes */ - if (prefs.save_history) - save_history(); + truncate_history(); } /* Truncates history to history_limit items */ diff --git a/src/history.h b/src/history.h index 9cdbf06..d0871dc 100644 --- a/src/history.h +++ b/src/history.h @@ -32,21 +32,26 @@ G_BEGIN_DECLS * 1 GB of RAM. If you don't want that, set this lower. */ #define ENTRY_MAX_SIZE 1048576 -extern GSList* history; +typedef struct { + gboolean is_static; + gpointer content; +} history_item; + +extern GSList *history; void read_history(); void save_history(); -void check_and_append(gchar* item); +void check_and_append(gchar *item); -void append_item(gchar* item); +void append_item(gchar *item); void truncate_history(); gpointer get_last_item(); -void delete_duplicate(gchar* item); +void delete_duplicate(gchar *item); G_END_DECLS diff --git a/src/main.c b/src/main.c index 73ad3e1..6cf24b9 100644 --- a/src/main.c +++ b/src/main.c @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef HAVE_APPINDICATOR #include #endif @@ -43,6 +44,7 @@ static gchar* clipboard_text; static gchar* synchronized_text; static GtkClipboard* primary; static GtkClipboard* clipboard; +static char *trayicon_path; #ifdef HAVE_APPINDICATOR static AppIndicator *indicator; static GtkWidget *indicator_menu = NULL; @@ -55,13 +57,13 @@ static gboolean status_menu_lock = FALSE; static gboolean actions_lock = FALSE; /* Init preferences structure */ -prefs_t prefs = {DEF_USE_COPY, DEF_USE_PRIMARY, DEF_SYNCHRONIZE, - DEF_SHOW_INDEXES, DEF_SAVE_URIS, DEF_SAVE_HISTORY, - DEF_HISTORY_LIMIT, DEF_ITEMS_MENU, DEF_HYPERLINKS_ONLY, - DEF_CONFIRM_CLEAR, DEF_SINGLE_LINE, DEF_REVERSE_HISTORY, - DEF_ITEM_LENGTH, DEF_ELLIPSIZE, INIT_HISTORY_KEY, - INIT_ACTIONS_KEY, INIT_MENU_KEY, INIT_SEARCH_KEY, - DEF_NO_ICON}; +prefs_t prefs = {DEF_USE_COPY, DEF_USE_PRIMARY, DEF_SYNCHRONIZE, + DEF_AUTOMATIC_PASTE, DEF_SHOW_INDEXES, DEF_SAVE_URIS, + DEF_SAVE_HISTORY, DEF_HISTORY_LIMIT, DEF_ITEMS_MENU, + DEF_HYPERLINKS_ONLY, DEF_CONFIRM_CLEAR, DEF_SINGLE_LINE, + DEF_REVERSE_HISTORY, DEF_ITEM_LENGTH, DEF_ELLIPSIZE, + INIT_HISTORY_KEY, INIT_ACTIONS_KEY, INIT_MENU_KEY, + INIT_SEARCH_KEY, DEF_NO_ICON}; /* Called every CHECK_INTERVAL seconds to check for new items */ static gboolean item_check(gpointer data) @@ -128,7 +130,7 @@ static gboolean item_check(gpointer data) } } } - + /* Check if clipboard contents were lost */ if ((clipboard_temp == NULL) && (clipboard_text != NULL)) { @@ -169,7 +171,7 @@ static gboolean item_check(gpointer data) } } } - + /* Synchronization */ if (prefs.synchronize) { @@ -186,7 +188,7 @@ static gboolean item_check(gpointer data) gtk_clipboard_set_text(primary, clipboard_text, -1); } } - + /* Cleanup */ g_free(primary_temp); g_free(clipboard_temp); @@ -194,6 +196,15 @@ static gboolean item_check(gpointer data) return TRUE; } +static void set_icon_paths() { + gchar *pixmap_dir = g_build_path(G_DIR_SEPARATOR_S, g_get_user_data_dir(), DATA_DIR, "pixmaps", NULL); + gchar *test_path = g_build_path(G_DIR_SEPARATOR_S, pixmap_dir, "trayicon.svg", NULL); + if (g_file_test(test_path, G_FILE_TEST_EXISTS)) + trayicon_path = test_path; + else + trayicon_path = g_build_path(G_DIR_SEPARATOR_S, CLIPITPIXMAPSDIR, "trayicon.svg", NULL); +} + /* Thread function called for each action performed */ static void *execute_action(void *command) { @@ -229,7 +240,7 @@ static void action_selected(GtkButton *button, gpointer user_data) g_free(command); gchar* cmd = g_strconcat("/bin/sh -c ", shell_command, NULL); g_free(shell_command); - + /* Execute action */ GPid pid; gchar **argv; @@ -256,6 +267,17 @@ static void item_selected(GtkMenuItem *menu_item, gpointer user_data) GSList* element = g_slist_nth(history, GPOINTER_TO_INT(user_data)); gtk_clipboard_set_text(clipboard, (gchar*)element->data, -1); gtk_clipboard_set_text(primary, (gchar*)element->data, -1); + /* Paste the clipboard contents automatically if enabled */ + if (prefs.automatic_paste) { + gchar* cmd = g_strconcat("/bin/sh -c 'xdotool key ctrl+v'", NULL); + GPid pid; + gchar **argv; + g_shell_parse_argv(cmd, NULL, &argv, NULL); + g_free(cmd); + g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL); + g_child_watch_add(pid, (GChildWatchFunc)action_exit, NULL); + g_strfreev(argv); + } } /* Clears all local data (specific to main.c) */ @@ -291,22 +313,22 @@ static void show_about_dialog(GtkMenuItem *menu_item, gpointer user_data) "\n" "You should have received a copy of the GNU General Public License\n" "along with this program. If not, see ."; - + /* Create the about dialog */ GtkWidget* about_dialog = gtk_about_dialog_new(); gtk_window_set_icon((GtkWindow*)about_dialog, gtk_widget_render_icon(about_dialog, GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU, NULL)); - + gtk_about_dialog_set_name((GtkAboutDialog*)about_dialog, "ClipIt"); #ifdef HAVE_CONFIG_H gtk_about_dialog_set_version((GtkAboutDialog*)about_dialog, VERSION); #endif gtk_about_dialog_set_comments((GtkAboutDialog*)about_dialog, _("Lightweight GTK+ clipboard manager.")); - + gtk_about_dialog_set_website((GtkAboutDialog*)about_dialog, "http://clipit.rspwn.com/"); - + gtk_about_dialog_set_copyright((GtkAboutDialog*)about_dialog, "Copyright (C) 2010 Cristian Henzel"); gtk_about_dialog_set_authors((GtkAboutDialog*)about_dialog, authors); gtk_about_dialog_set_translator_credits ((GtkAboutDialog*)about_dialog, @@ -328,7 +350,7 @@ static void show_about_dialog(GtkMenuItem *menu_item, gpointer user_data) "Hedef Türkçe \n" "Lyman Li \n" "Gilberto \"Xyhthyx\" Miralla "); - + gtk_about_dialog_set_license((GtkAboutDialog*)about_dialog, license); gtk_about_dialog_set_logo_icon_name((GtkAboutDialog*)about_dialog, GTK_STOCK_PASTE); /* Run the about dialog */ @@ -361,7 +383,7 @@ static gboolean show_actions_menu(gpointer data) /* Declare some variables */ GtkWidget *menu, *menu_item, *menu_image, *item_label; - + /* Create menu */ menu = gtk_menu_new(); g_signal_connect((GObject*)menu, "selection-done", (GCallback)gtk_widget_destroy, NULL); @@ -465,6 +487,53 @@ static gboolean show_actions_menu(gpointer data) return FALSE; } +static gboolean menu_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer user_data) +{ + switch (event->keyval) { + case XK_0: + item_selected((GtkMenuItem*)widget, GINT_TO_POINTER(9)); + gtk_widget_destroy(widget); + break; + case XK_1: + item_selected((GtkMenuItem*)widget, GINT_TO_POINTER(0)); + gtk_widget_destroy(widget); + break; + case XK_2: + item_selected((GtkMenuItem*)widget, GINT_TO_POINTER(1)); + gtk_widget_destroy(widget); + break; + case XK_3: + item_selected((GtkMenuItem*)widget, GINT_TO_POINTER(2)); + gtk_widget_destroy(widget); + break; + case XK_4: + item_selected((GtkMenuItem*)widget, GINT_TO_POINTER(3)); + gtk_widget_destroy(widget); + break; + case XK_5: + item_selected((GtkMenuItem*)widget, GINT_TO_POINTER(4)); + gtk_widget_destroy(widget); + break; + case XK_6: + item_selected((GtkMenuItem*)widget, GINT_TO_POINTER(5)); + gtk_widget_destroy(widget); + break; + case XK_7: + item_selected((GtkMenuItem*)widget, GINT_TO_POINTER(6)); + gtk_widget_destroy(widget); + break; + case XK_8: + item_selected((GtkMenuItem*)widget, GINT_TO_POINTER(7)); + gtk_widget_destroy(widget); + break; + case XK_9: + item_selected((GtkMenuItem*)widget, GINT_TO_POINTER(8)); + gtk_widget_destroy(widget); + break; + } + return FALSE; +} + /* Generates the small history menu */ static gboolean show_history_menu(gpointer data) { @@ -474,6 +543,7 @@ static gboolean show_history_menu(gpointer data) /* Create the menu */ menu = gtk_menu_new(); g_signal_connect((GObject*)menu, "selection-done", (GCallback)gtk_widget_destroy, NULL); + g_signal_connect((GObject*)menu, "key-press-event", (GCallback)menu_key_pressed, NULL); /* Items */ if ((history != NULL) && (history->data != NULL)) { @@ -507,11 +577,11 @@ static gboolean show_history_menu(gpointer data) } menu_item = gtk_menu_item_new_with_label(list_item); g_signal_connect((GObject*)menu_item, "activate", (GCallback)item_selected, GINT_TO_POINTER(element_number)); - + /* Modify menu item label properties */ item_label = gtk_bin_get_child((GtkBin*)menu_item); gtk_label_set_single_line_mode((GtkLabel*)item_label, prefs.single_line); - + /* Check if item is also clipboard text and make bold */ if ((clipboard_temp) && (g_strcmp0((gchar*)element->data, clipboard_temp) == 0)) { @@ -693,6 +763,7 @@ static gboolean show_clipit_menu() /* Create the menu */ statusicon_menu = gtk_menu_new(); g_signal_connect((GObject*)statusicon_menu, "selection-done", (GCallback)gtk_widget_destroy, NULL); + g_signal_connect((GObject*)statusicon_menu, "key-press-event", (GCallback)menu_key_pressed, NULL); /* Items */ if ((history != NULL) && (history->data != NULL)) @@ -876,10 +947,11 @@ static void clipit_init() /* Create status icon */ if (!prefs.no_icon) { + set_icon_paths(); #ifdef HAVE_APPINDICATOR create_app_indicator(1); #else - status_icon = gtk_status_icon_new_from_stock(GTK_STOCK_PASTE); + status_icon = gtk_status_icon_new_from_file(trayicon_path); gtk_status_icon_set_tooltip((GtkStatusIcon*)status_icon, _("Clipboard Manager")); g_signal_connect((GObject*)status_icon, "button_press_event", (GCallback)status_icon_clicked, NULL); #endif @@ -936,7 +1008,7 @@ int main(int argc, char **argv) g_string_free(piped_string, TRUE); } } - + /* Init ClipIt */ clipit_init(); diff --git a/src/main.h b/src/main.h index 276fa13..c25b3bd 100644 --- a/src/main.h +++ b/src/main.h @@ -30,11 +30,11 @@ G_BEGIN_DECLS #define POPUP_DELAY 30 #define CHECK_INTERVAL 500 -typedef struct -{ +typedef struct { gboolean use_copy; /* Use copy */ gboolean use_primary; /* Use primary */ gboolean synchronize; /* Synchronize copy and primary */ + gboolean automatic_paste; /* Automatically paste entry after selecting it */ gboolean show_indexes; /* Show index numbers in history menu */ gboolean save_uris; /* Save URIs in history */ @@ -58,8 +58,7 @@ typedef struct gchar* search_key; /* ClipIt search hotkey */ gboolean no_icon; /* No icon */ -} -prefs_t; +} prefs_t; extern prefs_t prefs; diff --git a/src/manage.c b/src/manage.c index 1c60081..606e211 100644 --- a/src/manage.c +++ b/src/manage.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "main.h" #include "utils.h" #include "history.h" @@ -107,13 +108,18 @@ static void edit_selected() { GtkTreeIter sel_iter; GtkTreeSelection* search_selection = gtk_tree_view_get_selection((GtkTreeView*)treeview_search); - /* This helps prevent multiple instances and checks if there's anything selected */ - if (gtk_tree_selection_get_selected(search_selection, NULL, &sel_iter)) - { + /* 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; - gtk_tree_model_get((GtkTreeModel*)search_list, &sel_iter, 0, &selected_item_nr, -1); + GList *selected_rows = gtk_tree_selection_get_selected_rows(search_selection, NULL); + GList *row_loop = g_list_first(selected_rows); + selected_item_nr = atoi((gchar*)gtk_tree_path_to_string(row_loop->data)); + g_list_foreach(selected_rows, (GFunc)gtk_tree_path_free, NULL); + g_list_free(selected_rows); GSList* element = g_slist_nth(history, selected_item_nr); + GSList* elementafter = element->next; GString* s_selected_item = g_string_new((gchar*)element->data); GtkTextBuffer* clipboard_buffer = gtk_text_buffer_new(NULL); gtk_text_buffer_set_text(clipboard_buffer, s_selected_item->str, -1); @@ -151,13 +157,13 @@ static void edit_selected() /* Delete any duplicate */ delete_duplicate(gtk_text_buffer_get_text(clipboard_buffer, &start, &end, TRUE)); - /* Insert new element before the old one */ - history = g_slist_insert_before(history, element->next, - g_strdup(gtk_text_buffer_get_text(clipboard_buffer, &start, &end, TRUE))); - /* Remove old entry */ history = g_slist_remove(history, element->data); + /* Insert new element where the old one was */ + history = g_slist_insert_before(history, elementafter, + g_strdup(gtk_text_buffer_get_text(clipboard_buffer, &start, &end, TRUE))); + if(selected_item_nr == 0) { GtkClipboard* clipboard; @@ -173,21 +179,34 @@ static void edit_selected() } } +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); +} + /* Called when Remove is selected from Manage dialog */ static void remove_selected() { - GtkTreeIter sel_iter; GtkTreeSelection* search_selection = gtk_tree_view_get_selection((GtkTreeView*)treeview_search); - /* This checks if there's anything selected */ - if (gtk_tree_selection_get_selected(search_selection, NULL, &sel_iter)) - { - /* Get item to delete */ - gint selected_item_nr; - gtk_tree_model_get((GtkTreeModel*)search_list, &sel_iter, 0, &selected_item_nr, -1); - GSList* element = g_slist_nth(history, selected_item_nr); - /* Remove entry */ - history = g_slist_remove(history, element->data); - search_history(); + gint selected_count = gtk_tree_selection_count_selected_rows(search_selection); + if (selected_count > 0) { + GtkListStore *store; + GArray *sel; + gint i; + store = GTK_LIST_STORE(gtk_tree_view_get_model((GtkTreeView*)treeview_search)); + 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); + history = g_slist_remove(history, g_slist_nth_data(history, remove_item)); + gtk_list_store_remove(store, iter); + } + g_array_free(sel, TRUE); } } @@ -232,13 +251,16 @@ static void remove_all_selected(gpointer user_data) static void search_doubleclick() { - GtkTreeIter sel_iter; GtkTreeSelection* search_selection = gtk_tree_view_get_selection((GtkTreeView*)treeview_search); /* Check if selected */ - if (gtk_tree_selection_get_selected(search_selection, NULL, &sel_iter)) - { + gint selected_count = gtk_tree_selection_count_selected_rows(search_selection); + if (selected_count == 1) { gint selected_item_nr; - gtk_tree_model_get((GtkTreeModel*)search_list, &sel_iter, 0, &selected_item_nr, -1); + GList *selected_rows = gtk_tree_selection_get_selected_rows(search_selection, NULL); + GList *row_loop = g_list_first(selected_rows); + selected_item_nr = atoi((gchar*)gtk_tree_path_to_string(row_loop->data)); + g_list_foreach(selected_rows, (GFunc)gtk_tree_path_free, NULL); + g_list_free(selected_rows); GSList *element = g_slist_nth(history, selected_item_nr); GtkClipboard* prim = gtk_clipboard_get(GDK_SELECTION_PRIMARY); GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); @@ -247,7 +269,7 @@ static void search_doubleclick() } } -static gint search_click(GtkWidget *widget, GdkEventButton *event, GtkWidget *search_window) +static gboolean search_click(GtkWidget *widget, GdkEventButton *event, GtkWidget *search_window) { if(event->type==GDK_2BUTTON_PRESS || event->type==GDK_3BUTTON_PRESS) { @@ -257,14 +279,38 @@ static gint search_click(GtkWidget *widget, GdkEventButton *event, GtkWidget *se return FALSE; } -static gint search_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer user_data) +static gboolean search_key_released(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { - /* Check if [Return] key was pressed */ - if ((event->keyval == 0xff0d) || (event->keyval == 0xff8d)) - search_history(); + 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: + 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) @@ -301,7 +347,7 @@ gboolean show_search() 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-press-event", (GCallback)search_key_pressed, NULL); + 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( @@ -336,8 +382,9 @@ gboolean show_search() 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_BROWSE); + 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); diff --git a/src/preferences.c b/src/preferences.c index 8f6fc8e..0d17db7 100644 --- a/src/preferences.c +++ b/src/preferences.c @@ -32,6 +32,7 @@ GtkWidget *copy_check, *primary_check, *synchronize_check, + *paste_check, *show_indexes_check, *save_uris_check, *history_spin, @@ -74,6 +75,7 @@ static void apply_preferences() prefs.use_copy = gtk_toggle_button_get_active((GtkToggleButton*)copy_check); prefs.use_primary = gtk_toggle_button_get_active((GtkToggleButton*)primary_check); prefs.synchronize = gtk_toggle_button_get_active((GtkToggleButton*)synchronize_check); + prefs.automatic_paste = gtk_toggle_button_get_active((GtkToggleButton*)paste_check); prefs.show_indexes = gtk_toggle_button_get_active((GtkToggleButton*)show_indexes_check); prefs.save_uris = gtk_toggle_button_get_active((GtkToggleButton*)save_uris_check); prefs.save_history = gtk_toggle_button_get_active((GtkToggleButton*)save_check); @@ -108,6 +110,7 @@ static void save_preferences() g_key_file_set_boolean(rc_key, "rc", "use_copy", prefs.use_copy); g_key_file_set_boolean(rc_key, "rc", "use_primary", prefs.use_primary); g_key_file_set_boolean(rc_key, "rc", "synchronize", prefs.synchronize); + g_key_file_set_boolean(rc_key, "rc", "automatic_paste", prefs.automatic_paste); g_key_file_set_boolean(rc_key, "rc", "show_indexes", prefs.show_indexes); g_key_file_set_boolean(rc_key, "rc", "save_uris", prefs.save_uris); g_key_file_set_boolean(rc_key, "rc", "save_history", prefs.save_history); @@ -211,6 +214,7 @@ void read_preferences() prefs.use_copy = g_key_file_get_boolean(rc_key, "rc", "use_copy", NULL); prefs.use_primary = g_key_file_get_boolean(rc_key, "rc", "use_primary", NULL); prefs.synchronize = g_key_file_get_boolean(rc_key, "rc", "synchronize", NULL); + prefs.automatic_paste = g_key_file_get_boolean(rc_key, "rc", "automatic_paste", NULL); prefs.show_indexes = g_key_file_get_boolean(rc_key, "rc", "show_indexes", NULL); prefs.save_uris = g_key_file_get_boolean(rc_key, "rc", "save_uris", NULL); prefs.save_history = g_key_file_get_boolean(rc_key, "rc", "save_history", NULL); @@ -651,6 +655,9 @@ void show_preferences(gint tab) gtk_box_pack_start((GtkBox*)vbox, primary_check, FALSE, FALSE, 0); synchronize_check = gtk_check_button_new_with_mnemonic(_("S_ynchronize clipboards")); gtk_box_pack_start((GtkBox*)vbox, synchronize_check, FALSE, FALSE, 0); + paste_check = gtk_check_button_new_with_mnemonic(_("_Automatically paste selected item")); + g_signal_connect((GObject*)paste_check, "toggled", (GCallback)check_toggled, NULL); + gtk_box_pack_start((GtkBox*)vbox, paste_check, FALSE, FALSE, 0); gtk_box_pack_start((GtkBox*)vbox_settings, frame, FALSE, FALSE, 0); /* Build the miscellaneous frame */ @@ -957,6 +964,7 @@ void show_preferences(gint tab) gtk_toggle_button_set_active((GtkToggleButton*)copy_check, prefs.use_copy); gtk_toggle_button_set_active((GtkToggleButton*)primary_check, prefs.use_primary); gtk_toggle_button_set_active((GtkToggleButton*)synchronize_check, prefs.synchronize); + gtk_toggle_button_set_active((GtkToggleButton*)paste_check, prefs.automatic_paste); gtk_toggle_button_set_active((GtkToggleButton*)show_indexes_check, prefs.show_indexes); gtk_toggle_button_set_active((GtkToggleButton*)save_uris_check, prefs.save_uris); gtk_toggle_button_set_active((GtkToggleButton*)save_check, prefs.save_history); diff --git a/src/preferences.h b/src/preferences.h index 41a9a1a..2997a92 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -32,6 +32,7 @@ G_BEGIN_DECLS #define DEF_USE_COPY TRUE #define DEF_USE_PRIMARY FALSE #define DEF_SYNCHRONIZE FALSE +#define DEF_AUTOMATIC_PASTE FALSE #define DEF_SHOW_INDEXES FALSE #define DEF_SAVE_URIS TRUE #define DEF_SAVE_HISTORY TRUE diff --git a/src/utils.c b/src/utils.c index 7e8e0e5..937a69a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -28,11 +28,11 @@ #include "clipit-i18n.h" /* Creates program related directories if needed */ -void -check_dirs() +void check_dirs() { gchar *data_dir = g_build_path(G_DIR_SEPARATOR_S, g_get_user_data_dir(), DATA_DIR, NULL); gchar *config_dir = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), CONFIG_DIR, NULL); + gchar *pixmap_dir = g_build_path(G_DIR_SEPARATOR_S, g_get_user_data_dir(), DATA_DIR, "pixmaps", NULL); /* Check if data directory exists */ if (!g_file_test(data_dir, G_FILE_TEST_EXISTS)) { @@ -47,9 +47,17 @@ check_dirs() if (g_mkdir_with_parents(config_dir, 0755) != 0) g_warning(_("Couldn't create directory: %s\n"), config_dir); } + /* Check if pixmap directory exists */ + if (!g_file_test(pixmap_dir, G_FILE_TEST_EXISTS)) + { + /* Try to make pixmap directory */ + if (g_mkdir_with_parents(pixmap_dir, 0755) != 0) + g_warning(_("Couldn't create directory: %s\n"), pixmap_dir); + } /* Cleanup */ g_free(data_dir); g_free(config_dir); + g_free(pixmap_dir); } /* Returns TRUE if text is a hyperlink */