diff --git a/ChangeLog b/ChangeLog index 7ede92050..236a4cae7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2004-05-17 Jody Goldberg + + * configure.in : check for, but do not require nautilus libs. These + is necessary if we move to the osx/ximian style shell. + 2004-04-16 Jody Goldberg * configure.in : post branch release bump to 2.7.0 diff --git a/NEWS b/NEWS index 73a5e9e09..49146fe63 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ gnome-control-center 2.7.0 +Michael Terry: + * Fix ui-review comments for keybinding capplet + Shailesh Mittal: http://bugzilla.gnome.org/show_bug.cgi?id=114832 * IPV6 support in the file types capplet diff --git a/configure.in b/configure.in index 580d7e56a..4417ee76c 100644 --- a/configure.in +++ b/configure.in @@ -260,6 +260,11 @@ AC_SUBST(LIBBONOBO_IDL_DIR) BONOBO_ACTIVATION_IDL_DIR="`$PKG_CONFIG --variable=idldir bonobo-activation-2.0`" AC_SUBST(BONOBO_ACTIVATION_IDL_DIR) +# check for nautilus +PKG_CHECK_MODULES(NAUTILUS, libnautilus) +AC_SUBST(NAUTILUS_LIBS) +AC_SUBST(NAUTILUS_CFLAGS) + # esd ESDSERVERDIR=`$PKG_CONFIG --variable=esd_serverdir esound` if test x"$ESDSERVERDIR" = x; then diff --git a/control-center/control-center-categories.c b/control-center/control-center-categories.c new file mode 100644 index 000000000..4b7603039 --- /dev/null +++ b/control-center/control-center-categories.c @@ -0,0 +1,656 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* capplet-dir.c + * Copyright (C) 2000, 2001 Ximian, Inc. + * Copyright (C) 1998 Red Hat Software, Inc. + * + * Written by Bradford Hovinen , + * Jonathan Blandford + * + * 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, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "control-center-categories.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +static char * +find_icon (GnomeDesktopItem *dentry) +{ + char *icon_file = gnome_desktop_item_get_icon (dentry, NULL); + if (!icon_file) + icon_file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP, + "gnome-unknown.png", TRUE, NULL); + return icon_file; +} + + +#if 0 +#include "capplet-dir.h" +#include "capplet-dir-view.h" + +CappletDirView *(*get_view_cb) (CappletDir *dir, CappletDirView *launcher); + +/* nice global table for capplet lookup */ +GHashTable *capplet_hash = NULL; + +CappletDirEntry * +capplet_new (CappletDir *dir, gchar *desktop_path) +{ + Capplet *capplet; + CappletDirEntry *entry; + GnomeDesktopItem *dentry; + gchar *path, **vec; + const gchar *execstr; + + g_return_val_if_fail (desktop_path != NULL, NULL); + + entry = g_hash_table_lookup (capplet_hash, desktop_path); + if (entry) { + return entry; + } + + dentry = gnome_desktop_item_new_from_uri (desktop_path, + GNOME_DESKTOP_ITEM_TYPE_NULL, + NULL); + if (dentry == NULL) + return NULL; + + execstr = gnome_desktop_item_get_string (dentry, + GNOME_DESKTOP_ITEM_EXEC); + /* Perhaps use poptParseArgvString here */ + vec = g_strsplit (execstr, " ", 0); + if (!(execstr && execstr[0]) || !(vec && (path = g_find_program_in_path (vec[0])))) + { + g_strfreev (vec); + gnome_desktop_item_unref (dentry); + return NULL; + } + g_free (path); + + capplet = g_new0 (Capplet, 1); + capplet->launching = FALSE; + + entry = CAPPLET_DIR_ENTRY (capplet); + + entry->type = TYPE_CAPPLET; + entry->entry = dentry; + + entry->label = g_strdup (gnome_desktop_item_get_localestring (dentry, + GNOME_DESKTOP_ITEM_NAME)); + entry->icon = find_icon (, dentry); + entry->pb = gdk_pixbuf_new_from_file (entry->icon, NULL); + entry->uri = gnome_vfs_uri_new (desktop_path); + entry->exec = vec; + entry->dir = dir; + + g_hash_table_insert (capplet_hash, g_strdup (desktop_path), entry); + + return entry; +} + +CappletDirEntry * +capplet_dir_new (CappletDir *dir, gchar *dir_path) +{ + CappletDir *capplet_dir; + CappletDirEntry *entry; + GnomeVFSURI *desktop_uri; + GnomeVFSURI *dir_uri; + char *desktop_uri_string; + + g_return_val_if_fail (dir_path != NULL, NULL); + + + entry = g_hash_table_lookup (capplet_hash, dir_path); + if (entry) { + return entry; + } + + desktop_uri = gnome_vfs_uri_append_file_name (dir_uri, ".directory"); + desktop_uri_string = gnome_vfs_uri_to_string (desktop_uri, GNOME_VFS_URI_HIDE_NONE); + + capplet_dir = g_new0 (CappletDir, 1); + entry = CAPPLET_DIR_ENTRY (capplet_dir); + + entry->type = TYPE_CAPPLET_DIR; + entry->entry = gnome_desktop_item_new_from_uri (desktop_uri_string, + GNOME_DESKTOP_ITEM_TYPE_NULL, + NULL); + entry->dir = dir; + entry->uri = dir_uri; + + gnome_vfs_uri_unref (desktop_uri); + g_free (desktop_uri_string); + + if (entry->entry) { + entry->label = g_strdup (gnome_desktop_item_get_localestring ( + entry->entry, + GNOME_DESKTOP_ITEM_NAME)); + entry->icon = find_icon (gnome_desktop_item_get_string (entry->entry, + GNOME_DESKTOP_ITEM_ICON), + entry->entry); + + if (!entry->icon) + entry->icon = gnome_program_locate_file + (gnome_program_get (), GNOME_FILE_DOMAIN_APP_PIXMAP, + "control-center2.png", TRUE, NULL); + + entry->pb = gdk_pixbuf_new_from_file (entry->icon, NULL); + } else { + /* If the .directory file could not be found or read, abort */ + g_free (capplet_dir); + return NULL; + } + + entry->dir = dir; + + g_hash_table_insert (capplet_hash, g_strdup (dir_path), entry); + + capplet_dir_load (CAPPLET_DIR (entry)); + + return entry; +} + +CappletDirEntry * +capplet_lookup (const char *path) +{ + return g_hash_table_lookup (capplet_hash, path); +} + +void +capplet_dir_entry_destroy (CappletDirEntry *entry) +{ + if (entry->type == TYPE_CAPPLET) { + capplet_shutdown (CAPPLET (entry)); + } else { + capplet_dir_shutdown (CAPPLET_DIR (entry)); + } + + g_free (entry->label); + g_free (entry->icon); + gnome_vfs_uri_unref (entry->uri); + g_strfreev (entry->exec); + if (entry->entry) + gnome_desktop_item_unref (entry->entry); + g_free (entry); +} + +void +capplet_dir_entry_activate (CappletDirEntry *entry, + CappletDirView *launcher) +{ + g_return_if_fail (entry != NULL); + + if (entry->type == TYPE_CAPPLET) + capplet_activate (CAPPLET (entry)); + else if (entry->type == TYPE_CAPPLET_DIR) + capplet_dir_activate (CAPPLET_DIR (entry), launcher); + else + g_assert_not_reached (); +} + +void +capplet_dir_entry_shutdown (CappletDirEntry *entry) +{ + if (entry->type == TYPE_CAPPLET) + capplet_shutdown (CAPPLET (entry)); + else if (entry->type == TYPE_CAPPLET_DIR) + capplet_dir_shutdown (CAPPLET_DIR (entry)); + else + g_assert_not_reached (); +} + +static gint +capplet_reset_cb (Capplet *capplet) +{ + capplet->launching = FALSE; + return FALSE; +} + +static void +capplet_activate (Capplet *capplet) +{ + CappletDirEntry *entry; + + entry = CAPPLET_DIR_ENTRY (capplet); + + if (capplet->launching) { + return; + } else { + capplet->launching = TRUE; + gtk_timeout_add (1000, (GtkFunction) capplet_reset_cb, capplet); + gnome_desktop_item_launch (entry->entry, NULL, 0, NULL); + } +} + +void +capplet_dir_load (CappletDir *capplet_dir) +{ + if (capplet_dir->entries) return; + capplet_dir->entries = read_entries (capplet_dir); +} + +static void +capplet_dir_activate (CappletDir *capplet_dir, CappletDirView *launcher) +{ + capplet_dir_load (capplet_dir); + capplet_dir->view = get_view_cb (capplet_dir, launcher); + + capplet_dir_view_load_dir (capplet_dir->view, capplet_dir); + capplet_dir_view_show (capplet_dir->view); +} + +static void +capplet_shutdown (Capplet *capplet) +{ + /* Can't do much here ... :-( */ +} + +static void +cde_destroy (CappletDirEntry *e, gpointer null) +{ + capplet_dir_entry_destroy (e); +} + +static void +capplet_dir_shutdown (CappletDir *capplet_dir) +{ + if (capplet_dir->view) + g_object_unref (G_OBJECT (capplet_dir->view)); + + g_slist_foreach (capplet_dir->entries, (GFunc) cde_destroy, NULL); + + g_slist_free (capplet_dir->entries); +} + +static gint +node_compare (gconstpointer a, gconstpointer b) +{ + return strcmp (CAPPLET_DIR_ENTRY (a)->label, + CAPPLET_DIR_ENTRY (b)->label); +} +#endif +/* Adapted from the original control center... */ + +GnomeDesktopItem * +get_directory_entry (GnomeVFSURI *uri) +{ + GnomeVFSURI *desktop_uri; + char *desktop_uri_string; + GnomeDesktopItem *entry; + + desktop_uri = gnome_vfs_uri_append_file_name (uri, ".directory"); + desktop_uri_string = gnome_vfs_uri_to_string (desktop_uri, GNOME_VFS_URI_HIDE_NONE); + + entry = gnome_desktop_item_new_from_uri (desktop_uri_string, + GNOME_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS, + NULL); + gnome_vfs_uri_unref (desktop_uri); + g_free (desktop_uri_string); + return entry; +} + +typedef void (*DirectoryCallback) (GnomeVFSURI *uri, const char *name, GnomeDesktopItem *entry, gpointer user_data); +typedef void (*EntryCallback) (GnomeVFSURI *uri, const char *name, GnomeDesktopItem *entry, gpointer user_data); + +static void +read_entries (GnomeVFSURI *uri, gint auto_recurse_level, DirectoryCallback dcb, EntryCallback ecb, gpointer user_data) +{ + gchar *test; + GnomeVFSDirectoryHandle *parent_dir; + GnomeVFSResult result; + + GnomeVFSFileInfo *child = gnome_vfs_file_info_new (); + + result = gnome_vfs_directory_open_from_uri (&parent_dir, uri, + GNOME_VFS_FILE_INFO_DEFAULT); + + if (result != GNOME_VFS_OK) + return; + + while (gnome_vfs_directory_read_next (parent_dir, child) == GNOME_VFS_OK) { + GnomeVFSURI *fulluri; + char *fullpath; + + if (child->name[0] == '.') + continue; + + fulluri = gnome_vfs_uri_append_path (uri, child->name); + fullpath = gnome_vfs_uri_to_string (fulluri, GNOME_VFS_URI_HIDE_NONE); + + if (child->type == GNOME_VFS_FILE_TYPE_DIRECTORY) { + if (auto_recurse_level != 0) { + gint recurse; + if (auto_recurse_level < 0) + recurse = auto_recurse_level; + else + recurse = auto_recurse_level - 1; + read_entries (fulluri, recurse, dcb, ecb, user_data); + } else { + GnomeDesktopItem *entry; + + entry = get_directory_entry (fulluri); + dcb (fulluri, child->name, entry, user_data); + if (entry) + gnome_desktop_item_unref (entry); + } + } else { + + test = rindex(child->name, '.'); + + /* if it's a .desktop file, it's interesting for sure! */ + if (test && !strcmp (".desktop", test)) { + GnomeDesktopItem *entry; + entry = gnome_desktop_item_new_from_uri (fullpath, + GNOME_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS, + NULL); + if (entry) { + ecb (fulluri, child->name, entry, user_data); + gnome_desktop_item_unref (entry); + } + } + } + gnome_vfs_uri_unref (fulluri); + g_free (fullpath); + } + + gnome_vfs_directory_close (parent_dir); +} + +typedef struct { + GSList *entries; + GSList *names; + GnomeDesktopItem *directory_entry; + char *name; +} CategoryLoadInformation; + +typedef struct { + CategoryLoadInformation *other; + GSList *categories; +} FullLoadInformation; + +static void +do_sort (GnomeDesktopItem *item, void **base, size_t nmemb, + int (*compar)(const void *, const void *), + const char *(*get_name)(const void *)) +{ + char **sort_orders = NULL; + qsort (base, nmemb, sizeof (void *), compar); + if (item) + sort_orders = gnome_desktop_item_get_strings (item, GNOME_DESKTOP_ITEM_SORT_ORDER); + if (sort_orders) { + int i; + int j = 0; + for (i = 0; sort_orders[i]; i++) { + int k; + for (k = j; k < nmemb; k++) { + const char *name = get_name (base[k]); + if (name && !strcmp (name, sort_orders[i])) { + void *temp = base[k]; + memmove (base + j + 1, base + j, (k - j) * sizeof (void *)); + base[j] = temp; + j++; + /* if (j >= nmemb), then k >= j >= nmemb + and thus we don't need a break + here. */ + } + } + if (j >= nmemb) + break; + } + g_strfreev (sort_orders); + } +} + + +static int +compare_entries (const void *ap, const void *bp) +{ + ControlCenterEntry *a = *(ControlCenterEntry **)ap; + ControlCenterEntry *b = *(ControlCenterEntry **)bp; + if (a->title == NULL && b->title == NULL) + return 0; + if (a->title == NULL) + return 1; + if (b->title == NULL) + return -1; + return strcmp (a->title, + b->title); +} + +static const char * +get_entry_name (const void *ap) +{ + ControlCenterEntry *a = (ControlCenterEntry *)ap; + return a->name; +} + +static void +free_entry (ControlCenterEntry *entry) +{ + g_free (entry->icon); + g_free (entry->name); + if (entry->desktop_entry) + gnome_desktop_item_unref (entry->desktop_entry); +} + +static void +free_category (ControlCenterCategory *category) +{ + int i; + for (i = 0; i < category->count; i++) { + free_entry (category->entries[i]); + } + g_free (category->entries); + if (category->directory_entry) + gnome_desktop_item_unref (category->directory_entry); +} + +static ControlCenterCategory * +load_information_to_category (CategoryLoadInformation *info, gboolean real_category) +{ + ControlCenterCategory *category = g_new (ControlCenterCategory, 1); + int i; + GSList *iterator, *name_iterator; + + category->count = g_slist_length (info->entries); + category->entries = g_new (ControlCenterEntry *, category->count); + category->directory_entry = info->directory_entry; + category->title = NULL; + category->name = info->name; + category->user_data = NULL; + category->real_category = real_category; + if (category->directory_entry) + category->title = gnome_desktop_item_get_localestring (category->directory_entry, + GNOME_DESKTOP_ITEM_NAME); + + if (!category->title || !category->title[0]) + category->title = category->name; + if (!category->title || !category->title[0]) + category->title = _("Others"); + + for (i = 0, iterator = info->entries, name_iterator = info->names; + i < category->count; + i++, iterator = iterator->next, name_iterator = name_iterator->next) { + category->entries[i] = g_new (ControlCenterEntry, 1); + + category->entries[i]->desktop_entry = iterator->data; + category->entries[i]->icon = find_icon (category->entries[i]->desktop_entry); + category->entries[i]->user_data = NULL; + category->entries[i]->name = name_iterator->data; + category->entries[i]->category = category; + if (category->entries[i]->desktop_entry) { + category->entries[i]->title = + gnome_desktop_item_get_localestring (category->entries[i]->desktop_entry, + GNOME_DESKTOP_ITEM_NAME); + category->entries[i]->comment = + gnome_desktop_item_get_localestring (category->entries[i]->desktop_entry, + GNOME_DESKTOP_ITEM_COMMENT); + } else { + category->entries[i]->title = NULL; + category->entries[i]->comment = NULL; + } + } + + do_sort (category->directory_entry, (void **) category->entries, category->count, compare_entries, get_entry_name); + + g_slist_free (info->entries); + g_slist_free (info->names); + + return category; +} + +static int +compare_categories (const void *ap, const void *bp) +{ + ControlCenterCategory *a = *(ControlCenterCategory **)ap; + ControlCenterCategory *b = *(ControlCenterCategory **)bp; + if (a->title == NULL && b->title == NULL) + return 0; + if (a->title == NULL) + return 1; + if (b->title == NULL) + return -1; + return strcmp (a->title, + b->title); +} + +static const char * +get_category_name (const void *ap) +{ + ControlCenterCategory *a = (ControlCenterCategory *)ap; + return a->name; +} + +static ControlCenterInformation * +load_information_to_information (GnomeVFSURI *base_uri, FullLoadInformation *info) +{ + ControlCenterInformation *information = g_new (ControlCenterInformation, 1); + int i; + GSList *iterator; + + information->count = g_slist_length (info->categories); + information->categories = g_new (ControlCenterCategory *, information->count); + + i = 0; + for (iterator = info->categories; iterator != NULL; iterator = iterator->next) { + ControlCenterCategory *category = iterator->data; + if (category->count) + information->categories[i++] = category; + else { + free_category (category); + } + } + + if (information->count != i) { + information->count = i; + information->categories = g_renew (ControlCenterCategory *, information->categories, information->count); + } + + g_slist_free (info->categories); + + information->directory_entry = get_directory_entry (base_uri); + information->title = NULL; + if (information->directory_entry) { + do_sort (information->directory_entry, (void **) information->categories, information->count, compare_categories, get_category_name); + information->title = gnome_desktop_item_get_localestring (information->directory_entry, + GNOME_DESKTOP_ITEM_NAME); + } + + if (!information->title || !information->title[0]) + information->title = _("Gnome Control Center"); + + return information; +} + +static void +add_to_category (GnomeVFSURI *uri, const char *name, GnomeDesktopItem *entry, gpointer user_data) +{ + CategoryLoadInformation *catinfo = user_data; + + catinfo->entries = g_slist_prepend (catinfo->entries, entry); + catinfo->names = g_slist_prepend (catinfo->names, g_strdup (name)); + + gnome_desktop_item_ref (entry); +} + +static void +add_to_other (GnomeVFSURI *uri, const char *name, GnomeDesktopItem *entry, gpointer user_data) +{ + FullLoadInformation *info = user_data; + if (info->other == NULL) { + info->other = g_new (CategoryLoadInformation, 1); + info->other->entries = NULL; + info->other->names = NULL; + info->other->directory_entry = NULL; + info->other->name = NULL; + } + add_to_category (uri, name, entry, info->other); +} + +static void +create_category (GnomeVFSURI *uri, const char *name, GnomeDesktopItem *entry, gpointer user_data) +{ + FullLoadInformation *info = user_data; + CategoryLoadInformation catinfo; + + catinfo.entries = NULL; + catinfo.names = NULL; + catinfo.directory_entry = entry; + catinfo.name = g_strdup (name); + if (entry) + gnome_desktop_item_ref (entry); + + read_entries (uri, -1, NULL, add_to_category, &catinfo); + + info->categories = g_slist_prepend (info->categories, + load_information_to_category (&catinfo, TRUE)); +} + +ControlCenterInformation * +control_center_get_categories (const gchar *prefsuri) +{ + FullLoadInformation info; + GnomeVFSURI *uri; + ControlCenterInformation *information; + + info.categories = NULL; + info.other = NULL; + + uri = gnome_vfs_uri_new (prefsuri); + read_entries (uri, 0, create_category, add_to_other, &info); + + if (info.other) + info.categories = g_slist_prepend (info.categories, load_information_to_category (info.other, FALSE)); + g_free (info.other); + + information = load_information_to_information (uri, &info); + gnome_vfs_uri_unref (uri); + + return information; +} diff --git a/control-center/control-center-categories.h b/control-center/control-center-categories.h new file mode 100644 index 000000000..688e120d8 --- /dev/null +++ b/control-center/control-center-categories.h @@ -0,0 +1,32 @@ +#include + +typedef struct ControlCenterCategory_ ControlCenterCategory; + +typedef struct { + GnomeDesktopItem *desktop_entry; + char *icon; + const char *title; + const char *comment; + char *name; + ControlCenterCategory *category; + gpointer user_data; +} ControlCenterEntry; + +struct ControlCenterCategory_ { + int count; + ControlCenterEntry **entries; + GnomeDesktopItem *directory_entry; + const char *title; + char *name; + gpointer user_data; + gboolean real_category; +}; + +typedef struct { + int count; + ControlCenterCategory **categories; + GnomeDesktopItem *directory_entry; + const char *title; +} ControlCenterInformation; + +ControlCenterInformation *control_center_get_categories (const gchar *prefsuri); /* Of type Category * */ diff --git a/control-center/control-center.c b/control-center/control-center.c new file mode 100644 index 000000000..3a96e8276 --- /dev/null +++ b/control-center/control-center.c @@ -0,0 +1,1331 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +#include + +#include "control-center-categories.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gnomecc-event-box.h" + +static gboolean use_nautilus = FALSE; + +typedef struct ControlCenter_ ControlCenter; + +typedef void (*ControlCenterStatusCallback) (ControlCenter *cc, const gchar *status, void *data); + +struct ControlCenter_ { + GtkWidget *widget; /* widget to embed. */ + + GnomeCanvas *canvas; + GnomeCanvasItem *under_cover; + double height; + double width; + + double min_height; + double max_width; + ControlCenterInformation *info; + gboolean firstlayout; + ControlCenterEntry *selected; + int last_x; + int click_count; + + int line_count; + + ControlCenterStatusCallback status_cb; + void *status_data; + ControlCenterEntry *current_status; +}; + +typedef struct { + ControlCenter *cc; + GnomeCanvasGroup *group; + GnomeCanvasItem *text; + GnomeCanvasItem *pixbuf; + GnomeCanvasItem *highlight_pixbuf; + GnomeCanvasItem *cover; + GnomeCanvasItem *selection; + double height; + double width; + double icon_height; + double icon_width; + double text_height; + double text_width; + guint launching : 1; + guint selected : 1; + guint highlighted : 1; + guint line_start : 1; +} EntryInfo; + +typedef struct { + GnomeCanvasGroup *group; + GnomeCanvasItem *title; + GnomeCanvasItem *line; + + int line_count; +} CategoryInfo; + +static guchar +lighten_component (guchar cur_value) +{ + int new_value = cur_value; + new_value += 24 + (new_value >> 3); + if (new_value > 255) { + new_value = 255; + } + return (guchar) new_value; +} + +static GdkPixbuf * +create_new_pixbuf (GdkPixbuf *src) +{ + g_return_val_if_fail (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB, NULL); + g_return_val_if_fail ((!gdk_pixbuf_get_has_alpha (src) + && gdk_pixbuf_get_n_channels (src) == 3) + || (gdk_pixbuf_get_has_alpha (src) + && gdk_pixbuf_get_n_channels (src) == 4), NULL); + + return gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src), + gdk_pixbuf_get_has_alpha (src), + gdk_pixbuf_get_bits_per_sample (src), + gdk_pixbuf_get_width (src), + gdk_pixbuf_get_height (src)); +} + +static GdkPixbuf * +create_spotlight_pixbuf (GdkPixbuf* src) +{ + GdkPixbuf *dest; + int i, j; + int width, height, has_alpha, src_row_stride, dst_row_stride; + guchar *target_pixels, *original_pixels; + guchar *pixsrc, *pixdest; + + g_return_val_if_fail (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB, NULL); + g_return_val_if_fail ((!gdk_pixbuf_get_has_alpha (src) + && gdk_pixbuf_get_n_channels (src) == 3) + || (gdk_pixbuf_get_has_alpha (src) + && gdk_pixbuf_get_n_channels (src) == 4), NULL); + g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (src) == 8, NULL); + + dest = create_new_pixbuf (src); + + has_alpha = gdk_pixbuf_get_has_alpha (src); + width = gdk_pixbuf_get_width (src); + height = gdk_pixbuf_get_height (src); + dst_row_stride = gdk_pixbuf_get_rowstride (dest); + src_row_stride = gdk_pixbuf_get_rowstride (src); + target_pixels = gdk_pixbuf_get_pixels (dest); + original_pixels = gdk_pixbuf_get_pixels (src); + + for (i = 0; i < height; i++) { + pixdest = target_pixels + i * dst_row_stride; + pixsrc = original_pixels + i * src_row_stride; + for (j = 0; j < width; j++) { + *pixdest++ = lighten_component (*pixsrc++); + *pixdest++ = lighten_component (*pixsrc++); + *pixdest++ = lighten_component (*pixsrc++); + if (has_alpha) { + *pixdest++ = *pixsrc++; + } + } + } + return dest; +} + +static void +gnome_canvas_item_move_absolute (GnomeCanvasItem *item, double dx, double dy) +{ + double translate[6]; + + g_return_if_fail (item != NULL); + g_return_if_fail (GNOME_IS_CANVAS_ITEM (item)); + + art_affine_translate (translate, dx, dy); + + gnome_canvas_item_affine_absolute (item, translate); +} + +#define ABOVE_LINE_SPACING 4 +#define UNDER_LINE_SPACING 0 +#define UNDER_TITLE_SPACING 0 /* manually insert 1 blank line of text */ +#define LINE_HEIGHT 1 +#define BETWEEN_CAT_SPACING 2 +#define BORDERS 7 +#define LINE_WITHIN FALSE +#define MAX_ITEM_WIDTH 135 + +static void +relayout_canvas (ControlCenter *cc) +{ + int count, i, j, line, line_i; + int vert_pos, category_vert_pos, category_horiz_pos; + gboolean keep_going; + double max_width, height, max_text_height, max_icon_height; + PangoRectangle rectangle; + EntryInfo *ei; + GArray *breaks = g_array_new (FALSE, FALSE, sizeof (int)); + + i = 0; + g_array_append_val (breaks, i); + + /* Do this in several iterations to keep things straight + * 0) walk down each column to decide when to wrap */ +start_again : + count = cc->info->count; + cc->line_count = 0; + keep_going = TRUE; + for (i = 0 ; keep_going; i++) { + keep_going = FALSE; + + max_width = 0.; + /* 0.1) Find the maximum width for this column */ + for (line = 0 ; line < breaks->len ; ) { + line_i = i + g_array_index (breaks, int, line); + line++; + if (line < breaks->len && line_i >= g_array_index (breaks, int, line)) + break; + + /* 0.2) check the nth row within a category */ + for (j = 0; j < count; j++) { + ControlCenterCategory *cat = cc->info->categories[j]; + PangoLayout *layout; + if (line_i >= cat->count) + continue; + ei = cat->entries[line_i]->user_data; + if (ei == NULL) + continue; + keep_going = TRUE; + + /* Try it first with no wrapping */ + layout = GNOME_CANVAS_TEXT (ei->text)->layout, + pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_width (layout, -1); + pango_layout_get_pixel_extents (layout, NULL, &rectangle); + + /* If its too big wrap at the max and regen to find the layout */ + if (rectangle.width > MAX_ITEM_WIDTH) { + pango_layout_set_width (layout, MAX_ITEM_WIDTH * PANGO_SCALE); + pango_layout_get_pixel_extents (layout, NULL, &rectangle); + rectangle.width = MAX_ITEM_WIDTH; + } + + ei->text_height = rectangle.height; + ei->text_width = rectangle.width; + if (max_width < ei->text_width) + max_width = ei->text_width; + if (max_width < ei->icon_width) + max_width = ei->icon_width; + } + } + + /* 0.3) Now go back and assign the max width */ + for (line = 0 ; line < breaks->len ; ) { + line_i = i + g_array_index (breaks, int, line); + line++; + if (line < breaks->len && line_i >= g_array_index (breaks, int, line)) + break; + + for (j = 0; j < count; j++) { + ControlCenterCategory *cat = cc->info->categories[j]; + if (line_i >= cat->count) + continue; + ei = cat->entries[line_i]->user_data; + if (ei != NULL) { + ei->width = max_width; + pango_layout_set_width (GNOME_CANVAS_TEXT (ei->text)->layout, + ei->width * PANGO_SCALE); + } + } + } + } + + /* 1) now walk each row looking for the max text and icon heights */ + vert_pos = BORDERS; + category_vert_pos = 0; + category_horiz_pos = BORDERS; + for (i = 0; i < count; i++) { + ControlCenterCategory *cat = cc->info->categories[i]; + CategoryInfo *catinfo = cat->user_data; + if (catinfo == NULL) + continue; + + /* 1.1) find the bounds */ + max_text_height = max_icon_height = 0.; + for (j = 0; j < cat->count; j++) { + ei = cat->entries[j]->user_data; + if (ei == NULL) + continue; + if (ei->pixbuf != NULL && max_icon_height < ei->icon_height) + max_icon_height = ei->icon_height; + if (max_text_height < ei->text_height) + max_text_height = ei->text_height; + } + + /* 1.2) position things */ + gnome_canvas_item_move_absolute (GNOME_CANVAS_ITEM (catinfo->group), + 0, vert_pos); + + category_vert_pos = 0; + category_horiz_pos = BORDERS; + if (!LINE_WITHIN && catinfo->line) { + gnome_canvas_item_move_absolute (catinfo->line, + BORDERS, category_vert_pos + ABOVE_LINE_SPACING); + category_vert_pos = ABOVE_LINE_SPACING + LINE_HEIGHT + UNDER_LINE_SPACING; + } + + if (catinfo->title) { + double text_height; + + g_object_get (catinfo->title, + "text_height", &text_height, + NULL); + + category_vert_pos += text_height; /* move it down 1 line */ + gnome_canvas_item_move_absolute (catinfo->title, BORDERS, category_vert_pos); + category_vert_pos += text_height + text_height/2 + UNDER_TITLE_SPACING; + } + + if (LINE_WITHIN) { + gnome_canvas_item_move_absolute (catinfo->line, + BORDERS, category_vert_pos + ABOVE_LINE_SPACING); + category_vert_pos = ABOVE_LINE_SPACING + LINE_HEIGHT + UNDER_LINE_SPACING; + } + + category_vert_pos += UNDER_LINE_SPACING; + + catinfo->line_count = 1; + cc->line_count ++; + + height = max_text_height + max_icon_height; + for (j = 0; j < cat->count; j++) { + ei = cat->entries[j]->user_data; + ei->line_start = (j == 0); + + if (category_horiz_pos + ei->width > cc->max_width - BORDERS && j > 0) { + category_horiz_pos = BORDERS; + category_vert_pos += height; + ei->line_start = TRUE; + catinfo->line_count ++; + cc->line_count ++; + + /* If this a new line break start again. + * The new layout will never be narrower, but + * the content of the extra line may expand */ + for (line = 0 ; line < breaks->len ; line++) + if (g_array_index (breaks, int, line) == j) + break; + if (line >= breaks->len) { + g_array_append_val (breaks, j); + goto start_again; + } + } + + gnome_canvas_item_move_absolute (GNOME_CANVAS_ITEM (ei->group), + category_horiz_pos, + category_vert_pos); + ei->height = height; + gnome_canvas_item_set (ei->selection, + "x2", (double) ei->width, + "y2", (double) ei->text_height, + NULL); + gnome_canvas_item_set (ei->cover, + "x2", (double) ei->width, + "y2", (double) ei->height, + NULL); + /* canvas asks layout for its extent, layout gives real + * size, not fixed width and drawing gets confused. + */ + gnome_canvas_item_set (ei->text, + "clip_width", (double) ei->width, + "clip_height", (double) ei->height, + NULL); + + gnome_canvas_item_move_absolute (ei->selection, 0, max_icon_height); + if (ei->text) /* text is centered by pango */ + gnome_canvas_item_move_absolute (ei->text, + 0, max_icon_height); + + if (ei->pixbuf) { + /* manually cc the icon */ + gnome_canvas_item_move_absolute (ei->pixbuf, + (ei->width - ei->icon_width) / 2, 0); + gnome_canvas_item_move_absolute (ei->highlight_pixbuf, + (ei->width - ei->icon_width) / 2, 0); + } + + if (category_horiz_pos + ei->width + BORDERS > max_width) + max_width = category_horiz_pos + ei->width + BORDERS; + category_horiz_pos += ei->width + 16; + } + category_vert_pos += height; + vert_pos += category_vert_pos; + vert_pos += BETWEEN_CAT_SPACING; + } + + cc->height = MAX (vert_pos, cc->min_height); + cc->width = MAX (cc->max_width, max_width); + + for (i = 0; i < count; i++) { + CategoryInfo *catinfo = cc->info->categories[i]->user_data; + if (LINE_WITHIN || catinfo->line) { + g_object_set (catinfo->line, + "x2", cc->width - 2 * BORDERS, + NULL); + } + } + g_array_free (breaks, TRUE); +} + +static gboolean +cb_entry_info_reset (gpointer data) +{ + EntryInfo *ei = data; + ei->launching = FALSE; + return FALSE; +} + +static void +gnome_canvas_item_show_hide (GnomeCanvasItem *item, gboolean show) +{ + if (show) + gnome_canvas_item_show (item); + else + gnome_canvas_item_hide (item); +} + +static void +setup_entry (ControlCenterEntry *entry) +{ + if (entry) { + EntryInfo *ei = entry->user_data; + GdkColor *color; + GtkWidget *widget = GTK_WIDGET (ei->cc->canvas); + + if (ei->pixbuf) { + gnome_canvas_item_show_hide (ei->highlight_pixbuf, ei->highlighted); + gnome_canvas_item_show_hide (ei->pixbuf, !ei->highlighted); + } + gnome_canvas_item_show_hide (ei->selection, ei->selected); + if (ei->selected) { + color = &widget->style->text[GTK_STATE_SELECTED]; + } else { + color = &widget->style->text[GTK_STATE_NORMAL]; + } + g_object_set (ei->text, + "fill_color_gdk", color, + NULL); + + if (ei->cc->status_cb) { + if (ei->selected) { + ei->cc->status_cb (ei->cc, + entry->comment, + ei->cc->status_data); + ei->cc->current_status = entry; + } else { + if (entry == ei->cc->current_status) + ei->cc->status_cb (ei->cc, + NULL, + ei->cc->status_data); + ei->cc->current_status = NULL; + } + } + } +} + +static int +get_x (ControlCenter *cc, ControlCenterEntry *entry) +{ + int i; + int x; + if (entry != NULL) { + ControlCenterCategory *category = entry->category; + for (i = 0, x = 0; i < category->count; i++, x++) { + EntryInfo *ei = category->entries[i]->user_data; + if (ei->line_start) + x = 0; + if (category->entries[i] == entry) + return x; + } + } + return -1; +} + +static int +get_y (ControlCenter *cc, ControlCenterEntry *entry) +{ + int i; + int line_count = 0; + if (entry != NULL) { + ControlCenterCategory *category = entry->category; + for (i = 0; i < cc->info->count; i++) { + CategoryInfo *catinfo = cc->info->categories[i]->user_data; + + if (cc->info->categories[i] == category) { + for (i = 0; i < category->count; i++) { + EntryInfo *ei = category->entries[i]->user_data; + if (i > 0 && ei->line_start) + line_count ++; + if (category->entries[i] == entry) + return line_count; + } + return -1; + } + + line_count += catinfo->line_count; + } + } + return -1; +} + +static ControlCenterEntry * +get_entry (ControlCenter *cc, int x, int y) +{ + int i; + for (i = 0; i < cc->info->count; i++) { + CategoryInfo *catinfo = cc->info->categories[i]->user_data; + if (y < catinfo->line_count) { + int j; + for (j = 0; j < cc->info->categories[i]->count; j++) { + EntryInfo *ei = cc->info->categories[i]->entries[j]->user_data; + if (ei->line_start) { + if (y == 0) { + g_assert (j + x < cc->info->categories[i]->count); + return cc->info->categories[i]->entries[j + x]; + } else { + y --; + } + } + } + g_assert_not_reached (); + } + y -= catinfo->line_count; + } + return NULL; +} + +static int +get_line_length (ControlCenter *cc, int y) +{ + int i; + for (i = 0; i < cc->info->count; i++) { + CategoryInfo *catinfo = cc->info->categories[i]->user_data; + if (y < catinfo->line_count) { + int j; + int last_start = 0; + + for (j = 1; j < cc->info->categories[i]->count; j++) { + EntryInfo *ei = cc->info->categories[i]->entries[j]->user_data; + if (ei->line_start) { + if (y == 0) { + return j - last_start; + } else { + y--; + last_start = j; + } + } + } + return j - last_start; + } + + y -= catinfo->line_count; + } + return -1; +} + +static void +set_x (ControlCenter *cc) +{ + cc->last_x = get_x (cc, cc->selected); +} + +static void +select_entry (ControlCenter *cc, ControlCenterEntry *entry) +{ + if (cc->selected == entry) + return; + + if (cc->selected && cc->selected->user_data) + ((EntryInfo *)cc->selected->user_data)->selected = FALSE; + setup_entry (cc->selected); + + cc->selected = entry; + + if (cc->selected && cc->selected->user_data) + ((EntryInfo *)cc->selected->user_data)->selected = TRUE; + setup_entry (cc->selected); +} + +static void +activate_entry (ControlCenterEntry *entry) +{ + EntryInfo *ei = entry->user_data; + if (!ei->launching) { + ei->launching = TRUE; + gtk_timeout_add (1000, cb_entry_info_reset, ei); + gnome_desktop_item_launch (entry->desktop_entry, NULL, 0, NULL); + } +} + +static gboolean +cover_event (GnomeCanvasItem *item, GdkEvent *event, ControlCenterEntry *entry) +{ + EntryInfo *ei = entry->user_data; + ControlCenter *cc = ei->cc; + switch (event->type) { + case GDK_ENTER_NOTIFY: + ei->highlighted = TRUE; + setup_entry (entry); /* highlight even if it is already selected */ + set_x (cc); + return TRUE; + case GDK_LEAVE_NOTIFY: + ei->highlighted = FALSE; + setup_entry (entry); + return TRUE; + case GDK_BUTTON_PRESS: + select_entry (cc, entry); + set_x (cc); + return TRUE; + case GDK_2BUTTON_PRESS: + activate_entry (entry); + return TRUE; + default: + return FALSE; + } +} + +static gboolean +key_press (GnomeCanvasItem *item, GdkEvent *event, ControlCenter *cc) +{ + int x, y; + int do_set_x = FALSE; + switch (event->type) { + case GDK_BUTTON_PRESS: + select_entry (cc, NULL); + set_x (cc); + return TRUE; + case GDK_KEY_PRESS: + switch (event->key.keyval) { + case GDK_KP_Up: + case GDK_Up: + if (cc->selected) { + y = get_y (cc, cc->selected); + if (y > 0) { + y--; + x = cc->last_x; + if (x == -1) + x = get_x (cc, cc->selected); + break; + } + } else { + x = y = 0; + break; + } + return FALSE; + case GDK_KP_Down: + case GDK_Down: + if (cc->selected) { + y = get_y (cc, cc->selected); + if (y < cc->line_count - 1) { + y++; + x = cc->last_x; + if (x == -1) + x = get_x (cc, cc->selected); + break; + } + } else { + x = y = 0; + break; + } + return FALSE; + case GDK_KP_Right: + case GDK_Right: + case GDK_Tab: + case GDK_KP_Tab: + do_set_x = TRUE; + if (cc->selected) { + x = get_x (cc, cc->selected); + y = get_y (cc, cc->selected); + + g_return_val_if_fail (x != -1 && y != -1, FALSE); + + x++; + + if (x >= get_line_length (cc, y)) { + y++; + x = 0; + } + if (y >= cc->line_count) { + return FALSE; + } + } else { + x = y = 0; + } + break; + case GDK_KP_Left: + case GDK_Left: + case GDK_ISO_Left_Tab: + do_set_x = TRUE; + if (cc->selected) { + x = get_x (cc, cc->selected); + y = get_y (cc, cc->selected); + + g_return_val_if_fail (x != -1 && y != -1, FALSE); + + x--; + + if (x < 0) { + if (y == 0) + return FALSE; + y--; + x = get_line_length (cc, y) - 1; + } + } else { + x = y = 0; + } + break; + case GDK_Return: + case GDK_KP_Enter: + if (cc->selected) { + activate_entry (cc->selected); + set_x (cc); + return TRUE; + } else { + return FALSE; + } + case GDK_Escape: + gtk_main_quit (); + return TRUE; + + case 'w': + case 'q': + case 'W': + case 'Q': + if (event->key.state == GDK_CONTROL_MASK) { + gtk_main_quit (); + } + return TRUE; + default: + return FALSE; + } + if (y < 0) + y = 0; + if (y >= cc->line_count) + y = cc->line_count - 1; + if (y < 0) + return FALSE; + if (x < 0) + x = 0; + if (x >= get_line_length (cc, y)) + x = get_line_length (cc, y) - 1; + select_entry (cc, get_entry (cc, x, y)); + if (do_set_x) + set_x (cc); + return TRUE; + default: + return FALSE; + } + +} + +static void +set_style (ControlCenter *cc, gboolean font_changed) +{ + int i, j; + GtkWidget *widget = GTK_WIDGET (cc->canvas); + + if (!GTK_WIDGET_REALIZED (widget)) + return; + + for (i = 0; i < cc->info->count; i++) { + CategoryInfo *catinfo = cc->info->categories[i]->user_data; + + if (LINE_WITHIN || catinfo->line) { + g_object_set (catinfo->line, + "fill_color_gdk", &widget->style->text_aa[GTK_STATE_NORMAL], + NULL); + } + if (catinfo->title) { + g_object_set (catinfo->title, + "fill_color_gdk", &widget->style->text[GTK_STATE_NORMAL], + NULL); + + if (font_changed) + g_object_set (catinfo->title, + "font", NULL, + NULL); + } + + for (j = 0; j < cc->info->categories[i]->count; j++) { + ControlCenterEntry *entry = cc->info->categories[i]->entries[j]; + EntryInfo *entryinfo = entry->user_data; + g_object_set (entryinfo->selection, + "fill_color_gdk", &widget->style->base[GTK_STATE_SELECTED], + NULL); + if (font_changed && entryinfo->text) + g_object_set (entryinfo->text, + "font", NULL, + NULL); + setup_entry (entry); /* Sets ei->text color */ + } + } + if (font_changed) + relayout_canvas (cc); +} + +static void +canvas_realize (GtkWidget *canvas, ControlCenter *cc) +{ + set_style (cc, FALSE); +} + +static void +canvas_style_set (GtkWidget *canvas, GtkStyle *previous_style, ControlCenter *cc) +{ + if (!GTK_WIDGET_REALIZED (canvas)) + return; + + set_style (cc, previous_style && canvas->style && !pango_font_description_equal (canvas->style->font_desc, previous_style->font_desc)); +} + +static void +rebuild_canvas (ControlCenter *cc, ControlCenterInformation *info) +{ + int count; + int i; + int j; + int vert_pos = BORDERS; +#if 0 + int preferred_height; + int preferred_width; + int preferred_max_width; +#endif + + cc->info = info; + cc->under_cover = gnome_canvas_item_new (gnome_canvas_root (cc->canvas), + gnomecc_event_box_get_type(), + NULL); + + gnome_canvas_item_grab_focus (GNOME_CANVAS_ITEM (gnome_canvas_root (cc->canvas))); + g_signal_connect (gnome_canvas_root (cc->canvas), + "event", + G_CALLBACK (key_press), cc); + + count = cc->info->count; + + cc->line_count = 0; + for (i = 0; i < count; i++) { + CategoryInfo *catinfo; + + if (cc->info->categories[i]->user_data == NULL) + cc->info->categories[i]->user_data = g_new (CategoryInfo, 1); + + catinfo = cc->info->categories[i]->user_data; + catinfo->group = NULL; + catinfo->title = NULL; + catinfo->line = NULL; + + catinfo->group = + GNOME_CANVAS_GROUP (gnome_canvas_item_new (gnome_canvas_root (cc->canvas), + gnome_canvas_group_get_type (), + NULL)); + gnome_canvas_item_move_absolute (GNOME_CANVAS_ITEM (catinfo->group), 0, vert_pos); + if (LINE_WITHIN || i > 0) + catinfo->line = gnome_canvas_item_new (catinfo->group, + gnome_canvas_rect_get_type (), + "x2", (double) cc->max_width - 2 * BORDERS, + "y2", (double) LINE_HEIGHT, + NULL); + + catinfo->title = NULL; + if (cc->info->categories[i] && (cc->info->count != 1 || cc->info->categories[0]->real_category)) { + char *label = g_strdup_printf ("%s", cc->info->categories[i]->title); + catinfo->title = gnome_canvas_item_new (catinfo->group, + gnome_canvas_text_get_type (), + "text", cc->info->categories[i]->title, + "markup", label, + "anchor", GTK_ANCHOR_NW, + NULL); + g_free (label); + } + + catinfo->line_count = 1; + cc->line_count ++; + for (j = 0; j < cc->info->categories[i]->count; j++) { + EntryInfo *ei; + + if (cc->info->categories[i]->entries[j]->user_data == NULL) + cc->info->categories[i]->entries[j]->user_data = g_new0 (EntryInfo, 1); + + ei = cc->info->categories[i]->entries[j]->user_data; + ei->cc = cc; + ei->group = GNOME_CANVAS_GROUP ( + gnome_canvas_item_new (catinfo->group, + gnome_canvas_group_get_type (), + NULL)); + ei->selection = gnome_canvas_item_new ( + ei->group, + gnome_canvas_rect_get_type(), + NULL); + + if (cc->info->categories[i]->entries[j]->title) { + ei->text = gnome_canvas_item_new (ei->group, + gnome_canvas_text_get_type (), + "anchor", GTK_ANCHOR_NW, + "justification", GTK_JUSTIFY_CENTER, + "clip", TRUE, + NULL); + pango_layout_set_alignment (GNOME_CANVAS_TEXT (ei->text)->layout, + PANGO_ALIGN_CENTER); + pango_layout_set_justify (GNOME_CANVAS_TEXT (ei->text)->layout, + FALSE); + g_object_set (ei->text, + "text", cc->info->categories[i]->entries[j]->title, + NULL); + } else + ei->text = NULL; + + if (cc->info->categories[i]->entries[j]->icon) { + GdkPixbuf *pixbuf = + gdk_pixbuf_new_from_file (cc->info->categories[i]->entries[j]->icon, NULL); + GdkPixbuf *highlight_pixbuf = + create_spotlight_pixbuf (pixbuf); + ei->icon_height = gdk_pixbuf_get_height (pixbuf); + ei->icon_width = gdk_pixbuf_get_width (pixbuf); + ei->pixbuf = gnome_canvas_item_new (ei->group, + gnome_canvas_pixbuf_get_type (), + "pixbuf", pixbuf, + NULL); + g_object_unref (pixbuf); + ei->highlight_pixbuf = gnome_canvas_item_new (ei->group, + gnome_canvas_pixbuf_get_type (), + "pixbuf", highlight_pixbuf, + NULL); + g_object_unref (highlight_pixbuf); + } else { + ei->pixbuf = NULL; + ei->highlight_pixbuf = NULL; + } + + ei->cover = gnome_canvas_item_new (ei->group, + gnomecc_event_box_get_type(), + NULL); + g_signal_connect (ei->cover, "event", + G_CALLBACK (cover_event), + cc->info->categories[i]->entries[j]); + + setup_entry (cc->info->categories[i]->entries[j]); + } + } +} + +static void +size_allocate(GtkWidget *widget, GtkAllocation *allocation, ControlCenter *cc) +{ + if (allocation->height == 1 || allocation->width == 1) + return; + + cc->max_width = allocation->width; + cc->min_height = allocation->height; + if (cc->firstlayout) { + rebuild_canvas (cc, cc->info); + cc->firstlayout = FALSE; + } + + relayout_canvas (cc); + gnome_canvas_set_scroll_region (GNOME_CANVAS (cc->canvas), 0, 0, cc->width - 1, cc->height - 1); + g_object_set (cc->under_cover, + "x2", cc->width, + "y2", cc->height, + NULL); +} + +static gboolean +is_nautilus_running (void) +{ + Atom window_id_atom; + Window nautilus_xid; + Atom actual_type; + int actual_format; + unsigned long nitems, bytes_after; + unsigned char *data; + int retval; + Atom wmclass_atom; + gboolean running; + gint error; + + window_id_atom = XInternAtom (GDK_DISPLAY (), + "NAUTILUS_DESKTOP_WINDOW_ID", True); + + if (window_id_atom == None) return FALSE; + + retval = XGetWindowProperty (GDK_DISPLAY (), GDK_ROOT_WINDOW (), + window_id_atom, 0, 1, False, XA_WINDOW, + &actual_type, &actual_format, &nitems, + &bytes_after, &data); + + if (data != NULL) { + nautilus_xid = *(Window *) data; + XFree (data); + } else { + return FALSE; + } + + if (actual_type != XA_WINDOW) return FALSE; + if (actual_format != 32) return FALSE; + + wmclass_atom = XInternAtom (GDK_DISPLAY (), "WM_CLASS", False); + + gdk_error_trap_push (); + + retval = XGetWindowProperty (GDK_DISPLAY (), nautilus_xid, + wmclass_atom, 0, 24, False, XA_STRING, + &actual_type, &actual_format, &nitems, + &bytes_after, &data); + + error = gdk_error_trap_pop (); + + if (error == BadWindow) return FALSE; + + if (actual_type == XA_STRING && + nitems == 24 && + bytes_after == 0 && + actual_format == 8 && + data != NULL && + !strcmp (data, "desktop_window") && + !strcmp (data + strlen (data) + 1, "Nautilus")) + running = TRUE; + else + running = FALSE; + + if (data != NULL) + XFree (data); + + return running; +} + +static gboolean +gnome_cc_save_yourself (GnomeClient *client, gint phase, GnomeSaveStyle save_style, + gboolean shutdown, GnomeInteractStyle interact_style, + gboolean fast, gchar *argv0) +{ + gchar *argv[3]; + gint argc; + + argv[0] = argv0; + argv[1] = "--use-shell"; + argc = 2; + gnome_client_set_clone_command (client, argc, argv); + gnome_client_set_restart_command (client, argc, argv); + + return TRUE; +} + +static void +gnome_cc_die (void) +{ + gtk_main_quit (); +} + +#if 0 +static void +gnome_cc_about (void) +{ + static GtkWidget *about; + const char *authors[] = { "Christopher James Lahey ", NULL }; + const char *documenters[] = { NULL }; + if (about) { + gdk_window_raise (about->window); + } else { + gtk_widget_show (about = gnome_about_new (_("Gnome Control Center"), + VERSION, + "Copyright 2002, Ximian, Inc.", + NULL, + authors, + documenters, + NULL, + NULL)); + g_object_add_weak_pointer (G_OBJECT (about), (void **) &about); + } +} +#endif + +static void +canvas_draw_background (GnomeCanvas *canvas, GdkDrawable *drawable, + int x, int y, int width, int height, ControlCenter *user_data) +{ + /* By default, we use the style background. */ + gdk_gc_set_foreground (canvas->pixmap_gc, + >K_WIDGET (canvas)->style->base[GTK_STATE_NORMAL]); + gdk_draw_rectangle (drawable, + canvas->pixmap_gc, + TRUE, + 0, 0, + width, height); + + g_signal_stop_emission_by_name (canvas, "draw_background"); +} + +static ControlCenter * +create_control_center () +{ + ControlCenter *cc; + GtkWidget *scroll_window; + + cc = g_new (ControlCenter, 1); + + cc->canvas = GNOME_CANVAS (gnome_canvas_new ()); + + cc->max_width = 300; + cc->min_height = 0; + cc->info = NULL; + cc->selected = NULL; + cc->last_x = -1; + cc->click_count = 0; + cc->status_cb = NULL; + cc->status_data = NULL; + cc->current_status = NULL; + cc->firstlayout = FALSE; + + g_signal_connect (cc->canvas, "size_allocate", + G_CALLBACK (size_allocate), cc); + g_signal_connect (cc->canvas, "realize", + G_CALLBACK (canvas_realize), cc); + g_signal_connect (cc->canvas, "style_set", + G_CALLBACK (canvas_style_set), cc); + g_signal_connect (cc->canvas, "draw_background", + G_CALLBACK (canvas_draw_background), cc); + gtk_widget_show_all (GTK_WIDGET (cc->canvas)); + + scroll_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (scroll_window), GTK_WIDGET (cc->canvas)); + + cc->widget = scroll_window; + return cc; +} + +static void +control_center_set_info (ControlCenter *cc, ControlCenterInformation *info) +{ + cc->info = info; + cc->firstlayout = TRUE; +} + +static void +control_center_set_status_cb (ControlCenter *cc, + ControlCenterStatusCallback status_cb, + void *status_data) +{ + cc->status_cb = status_cb; + cc->status_data = status_data; +} + +static void +change_status (ControlCenter *cc, const gchar *status, void *data) +{ + GnomeAppBar *bar = data; + + if (!status) + status = ""; + + gnome_appbar_set_status (bar, status); +} + + +static struct poptOption cap_options[] = { + {"use-nautilus", '\0', POPT_ARG_NONE, &use_nautilus, 0, + N_("Use nautilus if it is running."), NULL}, + {NULL, '\0', 0, NULL, 0} +}; +#if 0 +static GnomeUIInfo file_menu[] = { + GNOMEUIINFO_MENU_CLOSE_ITEM (gnome_cc_die, NULL), + GNOMEUIINFO_END +}; + +static GnomeUIInfo help_menu[] = { + GNOMEUIINFO_HELP("gnomecc"), + GNOMEUIINFO_SEPARATOR, + GNOMEUIINFO_MENU_ABOUT_ITEM (gnome_cc_about, NULL), + GNOMEUIINFO_END +}; + +static GnomeUIInfo menus[] = { + GNOMEUIINFO_MENU_FILE_TREE(file_menu), + GNOMEUIINFO_MENU_HELP_TREE(help_menu), + GNOMEUIINFO_END +}; +#endif + +static GtkWindow * +create_window (const gchar *appname, + const gchar *uri) +{ + GtkWidget *window; + GnomeClient *client; + GtkWidget *appbar; + ControlCenter *cc; + ControlCenterInformation *info; + + client = gnome_master_client (); + g_signal_connect (G_OBJECT (client), "save_yourself", + G_CALLBACK (gnome_cc_save_yourself), (void *) appname); + g_signal_connect (G_OBJECT (client), "die", + G_CALLBACK (gnome_cc_die), NULL); + + info = control_center_get_categories (uri); + + window = gnome_app_new ("gnomecc", info->title); + gnome_window_icon_set_from_file (GTK_WINDOW (window), + PIXMAP_DIR "/control-center.png"); + gtk_window_set_default_size (GTK_WINDOW (window), 760, 530); + + appbar = gnome_appbar_new (FALSE, TRUE, GNOME_PREFERENCES_USER); + gnome_app_set_statusbar (GNOME_APP (window), appbar); + + cc = create_control_center (); + control_center_set_info (cc, info); + control_center_set_status_cb (cc, change_status, appbar); + + gnome_app_set_contents (GNOME_APP (window), cc->widget); + +#if 0 + gnome_app_create_menus (GNOME_APP (window), menus); +#endif + + gtk_widget_show_all (window); + + g_object_weak_ref (G_OBJECT (window), (GWeakNotify) gnome_cc_die, NULL); + + return GTK_WINDOW (window); +} + +static void +change_status_view (ControlCenter *cc, const gchar *status, void *data) +{ + NautilusView *view = data; + + if (!status) + status = ""; + + nautilus_view_report_status (view, status); +} + +static void +cb_load_location (NautilusView *view, + char const *location, + ControlCenter *cc) +{ + ControlCenterInformation *info; + + nautilus_view_report_load_underway (view); + + info = control_center_get_categories (location); + control_center_set_info (cc, info); + control_center_set_status_cb (cc, change_status_view, view); + + gtk_widget_show_all (cc->widget); + + nautilus_view_report_load_complete (view); +} + +#define GNOMECC_VIEW_OAFIID "OAFIID:GNOME_ControlCenter_View" +#define GNOMECC_FACTORY_OAFIID "OAFIID:GNOME_ControlCenter_Factory" + +static BonoboObject * +factory_create_cb (BonoboGenericFactory *factory, + gchar const *iid, + gpointer closure) +{ + ControlCenter *cc; + NautilusView *view; + + if (strcmp (iid, GNOMECC_VIEW_OAFIID) != 0) { + return NULL; + } + + cc = create_control_center (); + view = nautilus_view_new (cc->widget); + + g_signal_connect (view, + "load_location", + G_CALLBACK (cb_load_location), + cc); + + return BONOBO_OBJECT (view); +} + +int +main (int argc, char *argv[]) +{ + GnomeProgram *ccprogram; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + ccprogram = gnome_program_init ("gnome-control-center", + VERSION, LIBGNOMEUI_MODULE, + argc, argv, + GNOME_PARAM_APP_DATADIR, GNOMECC_DATA_DIR, + GNOME_PARAM_POPT_TABLE, cap_options, + NULL); + + if (use_nautilus && is_nautilus_running ()) + execlp ("nautilus", "nautilus", "preferences:///", NULL); + + if (bonobo_activation_iid_get ()) { + /* Look for an existing factory */ + CORBA_Object existing_factory = bonobo_activation_activate_from_id ( + GNOMECC_FACTORY_OAFIID, Bonobo_ACTIVATION_FLAG_EXISTING_ONLY, + NULL, NULL); + + if (existing_factory == CORBA_OBJECT_NIL) { + /* Not started, start now */ + gchar *registration_id = bonobo_activation_make_registration_id ( + GNOMECC_FACTORY_OAFIID, DisplayString (gdk_display)); + BonoboGenericFactory *factory = bonobo_generic_factory_new ( + registration_id, factory_create_cb, NULL); + g_free (registration_id); + + bonobo_running_context_auto_exit_unref ( + BONOBO_OBJECT (factory)); + + bonobo_main (); + } + } else { + const gchar **args; + poptContext ctx; + GValue context = { 0 }; + + g_object_get_property (G_OBJECT (ccprogram), + GNOME_PARAM_POPT_CONTEXT, + g_value_init (&context, G_TYPE_POINTER)); + ctx = g_value_get_pointer (&context); + + /* Create a standalone window */ + args = poptGetArgs (ctx); + if (args != NULL) { + create_window (argv[0], args[0]); + } else { + create_window (argv[0], "preferences:///"); + } + poptFreeContext (ctx); + + gtk_main (); + } + +#if 0 + if (gnome_unique_window_create ("gnome-control-center", create_unique_window, argv[0])) + gtk_main (); +#endif + + return 0; +} diff --git a/control-center/gnomecc-event-box.c b/control-center/gnomecc-event-box.c new file mode 100644 index 000000000..a48d2edb4 --- /dev/null +++ b/control-center/gnomecc-event-box.c @@ -0,0 +1,99 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation + * All rights reserved. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome Library 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 Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ +/* Event box item type for GnomeCanvas widget + * + * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is + * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties. + * + * + * Author: Chris Lahey + */ + +#include + +#include "gnomecc-event-box.h" + +#define noVERBOSE + +static GnomeCanvasItemClass *parent_class; + +static double +gnomecc_event_box_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, + GnomeCanvasItem **actual_item) +{ + double x1, x2, y1, y2; + g_object_get (item, + "x1", &x1, + "x2", &x2, + "y1", &y1, + "y2", &y2, + NULL); + if (x <= x2 && x >= x1 && + y <= y2 && y >= y1) { + *actual_item = item; + + return 0.0; + } + return 1e12; +} + +static void +gnomecc_event_box_class_init (GnomeccEventBoxClass *class) +{ + GnomeCanvasItemClass *item_class; + + item_class = (GnomeCanvasItemClass *) class; + + parent_class = g_type_class_peek_parent (class); + + item_class->point = gnomecc_event_box_point; +} + +GType +gnomecc_event_box_get_type (void) +{ + static GType type; + + if (!type) { + static const GTypeInfo object_info = { + sizeof (GnomeccEventBoxClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gnomecc_event_box_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (GnomeccEventBox), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + NULL /* value_table */ + }; + + type = g_type_register_static (GNOME_TYPE_CANVAS_RECT, "GnomeccEventBox", + &object_info, 0); + } + + return type; +} diff --git a/control-center/gnomecc-event-box.h b/control-center/gnomecc-event-box.h new file mode 100644 index 000000000..b9f2d3942 --- /dev/null +++ b/control-center/gnomecc-event-box.h @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation + * All rights reserved. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome Library 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 Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ +/* Event Box item type for GnomeCanvas widget + * + * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is + * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties. + * + * + * Author: Chris Lahey + */ + +#ifndef GNOMECC_EVENT_BOX_H +#define GNOMECC_EVENT_BOX_H + + +#include + +#include + +G_BEGIN_DECLS + +/* Event Box item. No configurable or queryable arguments are available (use those in + * GnomeCanvasRE). + */ + + +#define GNOMECC_TYPE_EVENT_BOX (gnomecc_event_box_get_type ()) +#define GNOMECC_EVENT_BOX(obj) (GTK_CHECK_CAST ((obj), GNOMECC_TYPE_EVENT_BOX, GnomeccEventBox)) +#define GNOMECC_EVENT_BOX_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GNOMECC_TYPE_EVENT_BOX, GnomeccEventBoxClass)) +#define GNOMECC_IS_EVENT_BOX(obj) (GTK_CHECK_TYPE ((obj), GNOMECC_TYPE_EVENT_BOX)) +#define GNOMECC_IS_EVENT_BOX_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOMECC_TYPE_EVENT_BOX)) +#define GNOMECC_EVENT_BOX_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GNOMECC_TYPE_EVENT_BOX, GnomeccEventBoxClass)) + + +typedef struct _GnomeccEventBox GnomeccEventBox; +typedef struct _GnomeccEventBoxClass GnomeccEventBoxClass; + +struct _GnomeccEventBox { + GnomeCanvasRect item; +}; + +struct _GnomeccEventBoxClass { + GnomeCanvasRectClass parent_class; +}; + + +/* Standard Gtk function */ +GType gnomecc_event_box_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif diff --git a/po/ChangeLog b/po/ChangeLog index c8c8d8284..a97014e6b 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,3 +1,8 @@ +2004-05-17 Jody Goldberg + + * POTFILES.in : add some files for the new shell + * POTFILES.skip : ditto. + 2004-05-15 Alexander Winston * en_CA.po: Updated Canadian English translation. diff --git a/po/POTFILES.in b/po/POTFILES.in index 4d4e43777..39534af60 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -71,6 +71,8 @@ control-center/capplet-dir-view-list.c control-center/capplet-dir-view.c control-center/capplet-dir.c control-center/gnomecc-ui.xml +control-center/control-center-categories.c +control-center/control-center.c control-center/gnomecc.desktop.in control-center/main.c gnome-settings-daemon/factory.c diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 2e8d660d5..4c3593a8c 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -42,3 +42,4 @@ gnome-settings-daemon/GNOME_SettingsDaemon.server.in root-manager/root-manager-helper.c root-manager/root-manager-wrap.c root-manager/root-manager.c +control-center/gnomecc-event-box.c diff --git a/schemas/apps_gnome_settings_daemon_keybindings.schemas.in b/schemas/apps_gnome_settings_daemon_keybindings.schemas.in index a0ce869d3..7491dc839 100644 --- a/schemas/apps_gnome_settings_daemon_keybindings.schemas.in +++ b/schemas/apps_gnome_settings_daemon_keybindings.schemas.in @@ -55,8 +55,8 @@ string - Log Out - Log Out's shortcut. + Log out + Log out's shortcut. @@ -79,8 +79,8 @@ string - Home Folder - My Home Folder's shortcut. + Home folder + Home folder's shortcut. @@ -127,8 +127,8 @@ string - Lock Screen - Lock Screen's shortcut. + Lock screen + Lock screen's shortcut. @@ -139,8 +139,8 @@ string - Launch Help Browser - Launch Help Browser's shortcut. + Launch help browser + Launch help browser's shortcut. @@ -151,8 +151,8 @@ string - Launch Web Browser - Launch Web Browser's shortcut. + Launch web browser + Launch web browser's shortcut. @@ -187,8 +187,8 @@ string - Play (or Play/Pause) - Play (or Play/Pause) key's shortcut. + Play (or play/pause) + Play (or play/pause) key's shortcut. @@ -223,7 +223,7 @@ string - Skip to Previous track + Skip to previous track Previous track key's shortcut. @@ -235,7 +235,7 @@ string - Skip to Next track + Skip to next track Next track key's shortcut.