/* * xfce4_mcs_cursor_plugin - Cursor theme plugin for xfce4 mcs manager * Copyright (c) 2005 Pasi Orovuo * Copyright (c) 2005 Brian Tarricone * * 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; version 2 of the License ONLY. * * 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 Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_XCURSOR_EXTENSION #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include "mouse-plugin-internal.h" #define BORDER ( 6 ) #define CURSOR_SIZE_MIN ( 8 ) #define CURSOR_SIZE_MAX ( 64 ) static gchar *cursor_theme = NULL; static guint cursor_size = 0; /* XDG? */ static gchar *cursor_dirs[][2] = { { "%s/.icons/", "HOME" }, { "%s/.themes/", "HOME" }, { "/usr/share/cursors/xorg-x11/", NULL }, { "/usr/share/cursors/xfree/", NULL }, { "/usr/X11R6/lib/X11/icons/", NULL }, { "/usr/Xorg/lib/X11/icons/", NULL }, { "/usr/share/icons", NULL }, { NULL, NULL } }; /* Number of icons in preview */ #define NUM_PREVIEW ( 6 ) #define PREVIEW_SIZE ( 24 ) /* List from kde kcontrol */ const static gchar *preview_filenames[] = { "left_ptr", "left_ptr_watch", "watch", "hand2", "question_arrow", "sb_h_double_arrow", "sb_v_double_arrow", "bottom_left_corner", "bottom_right_corner", "fleur", "pirate", "cross", "X_cursor", "right_ptr", "right_side", "right_tee", "sb_right_arrow", "sb_right_tee", "base_arrow_down", "base_arrow_up", "bottom_side", "bottom_tee", "center_ptr", "circle", "dot", "dot_box_mask", "dot_box_mask", "double_arrow", "draped_box", "left_side", "left_tee", "ll_angle", "top_side", "top_tee" }; #define NUM_PREVIEW_NAMES ( sizeof( preview_filenames ) / sizeof( preview_filenames[0] ) ) static void show_cursor_apply_warning_dlg(Itf *dialog) { GtkWidget *dlg, *hbox, *vbox, *img, *lbl, *chk; dlg = gtk_dialog_new_with_buttons(_( "Mouse Settings" ), GTK_WINDOW(dialog->mouse_dialog), GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_NO_SEPARATOR, GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT, NULL); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->vbox), vbox, TRUE, TRUE, 0); hbox = gtk_hbox_new(FALSE, BORDER); gtk_container_set_border_width(GTK_CONTAINER(hbox), BORDER); gtk_widget_show(hbox); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); img = gtk_image_new_from_stock(GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG); gtk_misc_set_alignment(GTK_MISC(img), 0.0, 0.0); gtk_widget_show(img); gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); lbl = gtk_label_new(""); gtk_label_set_markup(GTK_LABEL(lbl), _("Cursor settings saved.\n\nMouse cursor settings may not be applied until you restart Xfce.")); gtk_label_set_use_markup(GTK_LABEL(lbl), TRUE); gtk_label_set_line_wrap(GTK_LABEL(lbl), TRUE); gtk_widget_show(lbl); gtk_box_pack_start(GTK_BOX(hbox), lbl, TRUE, TRUE, 0); chk = gtk_check_button_new_with_mnemonic(_("_Don't show this again")); gtk_widget_show(chk); gtk_box_pack_start(GTK_BOX(vbox), chk, FALSE, FALSE, 0); gtk_dialog_run(GTK_DIALOG(dlg)); if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chk))) { mcs_manager_set_int(dialog->mcs_plugin->manager, "Cursor/ShowApplyWarning", CHANNEL2, 0); mcs_manager_notify(dialog->mcs_plugin->manager, CHANNEL2); mouse_plugin_write_options(dialog->mcs_plugin); } gtk_widget_destroy(dlg); } static void cursor_theme_set( const gchar *theme, guint size ) { gchar *xrdb_file, *xrdb_file_new, *cmd; FILE *fp; GError *error = NULL; if ( !theme || size <= 0 ) { g_warning( "Mouse Settings: Can't set theme %s (%u)", theme ? theme : "(null)", size ); return; } xrdb_file = xfce_resource_save_location(XFCE_RESOURCE_CONFIG, "xfce4/Xcursor.xrdb", TRUE); if(!xrdb_file) { g_critical(_("Mouse Settings: Unable to create %s"), xrdb_file); g_free(xrdb_file); return; } xrdb_file_new = g_strconcat(xrdb_file, ".new", NULL); fp = fopen(xrdb_file_new, "w"); if(!fp) { g_critical(_("Mouse Settings: Unable to create %s"), xrdb_file_new); g_free(xrdb_file_new); g_free(xrdb_file); return; } fprintf(fp, "Xcursor.theme: %s\n" "Xcursor.theme_core: true\n" "Xcursor.size: %d\n", theme, size ); fclose(fp); if(rename(xrdb_file_new, xrdb_file)) { g_critical(_("Mouse Settings: Unable to move %s to %s. Cursor settings may not be reapplied correctly on restart."), xrdb_file_new, xrdb_file); g_free(xrdb_file_new); g_free(xrdb_file); return; } g_free(xrdb_file_new); cmd = g_strdup_printf("xrdb -nocpp -merge \"%s\"", xrdb_file); if(!g_spawn_command_line_async(cmd, &error)) { g_critical(_("Mouse Settings: Failed to run xrdb. Cursor settings may not be applied correctly. (Error was: %s)"), error->message); g_error_free(error); } g_free(cmd); g_free(xrdb_file); return; } static void cursor_plugin_pixbuf_destroy_notify_cb( guchar *pixels, gpointer data ) { g_free( pixels ); } static GdkPixbuf * cursor_image_get_pixbuf( XcursorImage *cursor ) { GdkPixbuf *pixbuf = NULL; guchar *data, *p; gsize dlen = cursor->width * cursor->height * sizeof( XcursorPixel ); guint i; data = g_malloc( dlen ); for ( i = 0, p = (guchar *) cursor->pixels; i < dlen; i += 4, p += 4 ) { data[i] = p[2]; data[i + 1] = p[1]; data[i + 2] = p[0]; data[i + 3] = p[3]; } pixbuf = gdk_pixbuf_new_from_data( data, GDK_COLORSPACE_RGB, TRUE, 8, cursor->width, cursor->height, cursor->width * sizeof( XcursorPixel ), cursor_plugin_pixbuf_destroy_notify_cb, NULL ); if ( pixbuf == NULL ) { g_free( data ); return ( NULL ); } if ( cursor->width != PREVIEW_SIZE || cursor->height != PREVIEW_SIZE ) { gfloat f; GdkPixbuf *tmp; guint w, h; f = (gfloat) cursor->width / (gfloat) cursor->height; if ( f >= 1.0f ) { w = (gfloat) PREVIEW_SIZE / f; h = PREVIEW_SIZE; } else { w = PREVIEW_SIZE; h = (gfloat) PREVIEW_SIZE * f; } tmp = gdk_pixbuf_scale_simple( pixbuf, w, h, GDK_INTERP_BILINEAR ); g_return_val_if_fail( tmp != NULL, pixbuf ); gdk_pixbuf_unref( pixbuf ); pixbuf = tmp; } return ( pixbuf ); } static GdkPixbuf * generate_preview_image( GtkWidget *widget, const gchar *theme_path ) { gint i, num_loaded; GdkPixbuf *preview_pix = NULL; GdkPixmap *pmap; GtkStyle *style; if(!GTK_WIDGET_REALIZED(widget)) gtk_widget_realize(widget); pmap = gdk_pixmap_new(GDK_DRAWABLE(widget->window), NUM_PREVIEW*PREVIEW_SIZE, PREVIEW_SIZE, -1); style = gtk_widget_get_style(widget); gdk_draw_rectangle(GDK_DRAWABLE(pmap), style->bg_gc[GTK_STATE_NORMAL], TRUE, 0, 0, NUM_PREVIEW*PREVIEW_SIZE, PREVIEW_SIZE); for ( i = 0, num_loaded = 0; i < NUM_PREVIEW_NAMES && num_loaded < NUM_PREVIEW; i++ ) { XcursorImage *cursor; gchar *fn = g_build_filename( theme_path, preview_filenames[i], NULL ); cursor = XcursorFilenameLoadImage( fn, PREVIEW_SIZE ); if ( cursor ) { GdkPixbuf *pb = cursor_image_get_pixbuf( cursor ); if ( pb ) { gdk_draw_pixbuf(GDK_DRAWABLE(pmap), style->bg_gc[GTK_STATE_NORMAL], pb, 0, 0, num_loaded*PREVIEW_SIZE, 0, gdk_pixbuf_get_width(pb), gdk_pixbuf_get_height(pb), GDK_RGB_DITHER_NONE, 0, 0); g_object_unref( G_OBJECT(pb) ); num_loaded++; } else { g_warning( "pb == NULL" ); } XcursorImageDestroy( cursor ); } } if(num_loaded > 0) { preview_pix = gdk_pixbuf_get_from_drawable(NULL, GDK_DRAWABLE(pmap), NULL, 0, 0, 0, 0, NUM_PREVIEW*PREVIEW_SIZE, PREVIEW_SIZE); } g_object_unref(G_OBJECT(pmap)); return preview_pix; } static GtkWidget * preview_list_create( void ) { GtkWidget *preview_list; preview_list = gtk_image_new(); gtk_widget_set_size_request( preview_list, NUM_PREVIEW * PREVIEW_SIZE, PREVIEW_SIZE + BORDER ); return ( preview_list ); } enum { TLIST_THEME_NAME, TLIST_THEME_PATH, TLIST_NUM_COLUMNS }; static gint tree_sort_cmp_alpha(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data) { gchar *a_s = NULL, *b_s = NULL; gint ret = 0; gtk_tree_model_get(model, a, TLIST_THEME_NAME, &a_s, -1); gtk_tree_model_get(model, b, TLIST_THEME_NAME, &b_s, -1); if(!a_s) ret = -1; else if(!b_s) ret = 1; else ret = g_ascii_strcasecmp(a_s, b_s); g_free(a_s); g_free(b_s); return ret; } static void theme_list_populate( GtkWidget *widget, const gchar *current_theme ) { GDir *dir = NULL; guint i; GtkTreeIter iter; GtkListStore *store; GHashTable *themes; store = GTK_LIST_STORE( gtk_tree_view_get_model( GTK_TREE_VIEW( widget ) ) ); gtk_list_store_append( store, &iter ); gtk_list_store_set( store, &iter, 0, "default", -1 ); themes = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL); for ( i = 0; cursor_dirs[i][0]; i++ ) { gchar *curdir = cursor_dirs[i][0]; if ( cursor_dirs[i][1] ) { curdir = g_strdup_printf( cursor_dirs[i][0], g_getenv( cursor_dirs[i][1] ) ); } if ( ( dir = g_dir_open( curdir, 0, NULL ) ) ) { const gchar *theme; for ( theme = g_dir_read_name( dir ); theme != NULL; theme = g_dir_read_name( dir ) ) { gchar *full_path = g_build_filename( curdir, theme, "cursors", NULL ); if ( g_file_test( full_path, G_FILE_TEST_IS_DIR ) && !g_hash_table_lookup(themes, theme)) { gtk_list_store_append( store, &iter ); gtk_list_store_set( store, &iter, TLIST_THEME_NAME, theme, TLIST_THEME_PATH, full_path, -1 ); g_hash_table_insert(themes, g_strdup(theme), GINT_TO_POINTER(1)); if ( current_theme && !strcmp( current_theme, theme ) ) { GtkTreePath *path; path = gtk_tree_model_get_path( GTK_TREE_MODEL( store ), &iter ); gtk_tree_view_set_cursor( GTK_TREE_VIEW( widget ), path, NULL, FALSE ); gtk_tree_view_scroll_to_cell( GTK_TREE_VIEW( widget ), path, NULL, FALSE, 0.5, 0.0 ); gtk_tree_path_free( path ); } } g_free( full_path ); } g_dir_close( dir ); } if ( cursor_dirs[i][1] ) { g_free( curdir ); } } g_hash_table_destroy(themes); gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), TLIST_THEME_NAME, tree_sort_cmp_alpha, NULL, NULL); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), TLIST_THEME_NAME, GTK_SORT_ASCENDING); } static void theme_list_selection_changed_cb( GtkTreeSelection *selection, Itf *dialog ) { GtkTreeIter iter; GtkTreeModel *theme_model = NULL; if ( gtk_tree_selection_get_selected( selection, &theme_model, &iter ) ) { gchar *path = NULL, *theme = NULL; GdkPixbuf *pb = NULL; gtk_tree_model_get( theme_model, &iter, TLIST_THEME_PATH, &path, TLIST_THEME_NAME, &theme, -1 ); if ( path ) { pb = generate_preview_image( dialog->cursor_preview_list, path ); g_free( path ); } gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->cursor_preview_list), pb); if(pb) g_object_unref(G_OBJECT(pb)); if(theme) { McsSetting *setting; g_free(cursor_theme); cursor_theme = theme; mcs_manager_set_string( dialog->mcs_plugin->manager, "Gtk/CursorThemeName", CHANNEL1, cursor_theme ); mcs_manager_notify( dialog->mcs_plugin->manager, CHANNEL1 ); mouse_plugin_write_options( dialog->mcs_plugin ); cursor_theme_set(cursor_theme, cursor_size); setting = mcs_manager_setting_lookup(dialog->mcs_plugin->manager, "Cursor/ShowApplyWarning", CHANNEL2); if(!setting || setting->data.v_int) show_cursor_apply_warning_dlg(dialog); } } } static void cursor_size_changed_cb(GtkWidget *w, gpointer user_data) { Itf *dialog = user_data; guint new_size; new_size = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( dialog->cursor_size_spinbtn ) ); if(new_size != cursor_size) { cursor_size = new_size; mcs_manager_set_int( dialog->mcs_plugin->manager, "Gtk/CursorThemeSize", CHANNEL1, cursor_size ); mcs_manager_notify( dialog->mcs_plugin->manager, CHANNEL1 ); mouse_plugin_write_options( dialog->mcs_plugin ); cursor_theme_set(cursor_theme, cursor_size); } } static GtkWidget * theme_list_create() { GtkWidget *theme_list; GtkCellRenderer *renderer; GtkListStore *store; GtkTreeSelection *selection; store = gtk_list_store_new( TLIST_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING ); theme_list = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) ); g_object_unref( store ); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( theme_list ), -1, _( "Cursor theme" ), renderer, "text", TLIST_THEME_NAME, NULL ); selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( theme_list ) ); gtk_tree_selection_set_mode( selection, GTK_SELECTION_SINGLE ); gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( theme_list ), FALSE ); return ( theme_list ); } void mouse_plugin_set_initial_cursor_values(McsPlugin *mcs_plugin) { McsSetting *setting; setting = mcs_manager_setting_lookup( mcs_plugin->manager, "Gtk/CursorThemeName", CHANNEL1 ); if ( setting ) { cursor_theme = g_strdup( setting->data.v_string ); } else { cursor_theme = g_strdup( "default" ); mcs_manager_set_string( mcs_plugin->manager, "Gtk/CursorThemeName", CHANNEL1, cursor_theme ); } setting = mcs_manager_setting_lookup( mcs_plugin->manager, "Gtk/CursorThemeSize", CHANNEL1 ); if ( setting ) { cursor_size = setting->data.v_int; } else { cursor_size = XcursorGetDefaultSize( GDK_DISPLAY() ); mcs_manager_set_int( mcs_plugin->manager, "Gtk/CursorThemeSize", CHANNEL1, cursor_size ); } /* if ( strcmp( cursor_theme, "default" ) ) { cursor_theme_set( cursor_theme, cursor_size ); } */ } void mouse_plugin_create_cursor_page(Itf *dialog) { GtkWidget *hbox, *vbox, *fbox, *frame_bin; GtkWidget *scrolledwindow; GtkTreeSelection *sel; GtkTreeModel *model = NULL; GtkTreeIter itr; dialog->cursor_page = gtk_hbox_new( FALSE, 0 ); gtk_container_set_border_width(GTK_CONTAINER(dialog->cursor_page), BORDER); gtk_widget_show( dialog->cursor_page ); scrolledwindow = gtk_scrolled_window_new( NULL, NULL ); gtk_widget_show( scrolledwindow ); gtk_box_pack_start( GTK_BOX( dialog->cursor_page ), scrolledwindow, TRUE, TRUE, 0 ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolledwindow ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC ); gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scrolledwindow ), GTK_SHADOW_IN ); dialog->cursor_theme_list = theme_list_create(); gtk_widget_show( dialog->cursor_theme_list ); gtk_container_add( GTK_CONTAINER( scrolledwindow ), dialog->cursor_theme_list ); vbox = gtk_vbox_new( FALSE, 0 ); gtk_widget_show( vbox ); gtk_box_pack_start( GTK_BOX( dialog->cursor_page ), vbox, TRUE, TRUE, 0 ); fbox = xfce_create_framebox( _( "Cursor Size" ), &frame_bin ); gtk_widget_show( fbox ); gtk_box_pack_start( GTK_BOX( vbox ), fbox, FALSE, FALSE, 0 ); hbox = gtk_hbox_new( FALSE, 0 ); gtk_widget_show( hbox ); gtk_container_add( GTK_CONTAINER( frame_bin ), hbox ); dialog->cursor_size_spinbtn = gtk_spin_button_new_with_range( CURSOR_SIZE_MIN, CURSOR_SIZE_MAX, 1.0 ); gtk_widget_show( dialog->cursor_size_spinbtn ); gtk_box_pack_start( GTK_BOX( hbox ), dialog->cursor_size_spinbtn, FALSE, FALSE, 0 ); gtk_spin_button_set_numeric( GTK_SPIN_BUTTON( dialog->cursor_size_spinbtn ), TRUE ); gtk_spin_button_set_value( GTK_SPIN_BUTTON( dialog->cursor_size_spinbtn ), cursor_size ); gtk_spin_button_set_wrap( GTK_SPIN_BUTTON( dialog->cursor_size_spinbtn ), FALSE ); g_signal_connect(G_OBJECT(dialog->cursor_size_spinbtn), "changed", G_CALLBACK(cursor_size_changed_cb), dialog); fbox = xfce_create_framebox( _( "Preview" ), &frame_bin ); gtk_widget_show( fbox ); gtk_box_pack_start( GTK_BOX( vbox ), fbox, FALSE, FALSE, 0 ); hbox = gtk_hbox_new( FALSE, 0 ); gtk_widget_show( hbox ); gtk_container_add( GTK_CONTAINER( frame_bin ), hbox ); gtk_container_set_border_width( GTK_CONTAINER( hbox ), BORDER ); dialog->cursor_preview_list = preview_list_create(); gtk_widget_show( dialog->cursor_preview_list ); gtk_box_pack_start( GTK_BOX( hbox ), dialog->cursor_preview_list, FALSE, FALSE, 0 ); theme_list_populate( dialog->cursor_theme_list, cursor_theme ); sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->cursor_theme_list)); if(gtk_tree_selection_get_selected(sel, &model, &itr)) { gchar *path = NULL; gtk_tree_model_get(model, &itr, TLIST_THEME_PATH, &path, -1); if(path) { GdkPixbuf *pb = generate_preview_image(dialog->mouse_dialog, path); if(pb) { gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->cursor_preview_list), pb); g_object_unref(G_OBJECT(pb)); } g_free(path); } } g_signal_connect( G_OBJECT(sel), "changed", G_CALLBACK( theme_list_selection_changed_cb ), dialog ); } #endif /* HAVE_XCURSOR_EXTENSION */