gnome-control-center/control-center/control-center.c
Jody Goldberg 7d324e378a skip lines that are finished, but keep processing up coming lines. This
2005-01-26  Jody Goldberg <jody@gnome.org>

	* control-center.c (relayout_canvas) : skip lines that are finished,
	  but keep processing up coming lines.  This should make alignment
	  work again.  While we're here add a bit more padding at the bottom.
2005-01-26 19:49:17 +00:00

1169 lines
30 KiB
C

/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
#include <config.h>
#include "control-center-categories.h"
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <gconf/gconf-client.h>
#include <libgnome/libgnome.h>
#include <libgnomeui/libgnomeui.h>
#include <libgnomecanvas/libgnomecanvas.h>
#include <libgnome/gnome-desktop-item.h>
#include "gnomecc-event-box.h"
#include "gnomecc-rounded-rect.h"
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 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;
#define PAD 5 /*when scrolling keep a few pixels above or below if possible */
#define d(stuff)
static gboolean
single_click_activates (void)
{
static gboolean needs_init = TRUE;
static gboolean use_single_click = FALSE;
if (needs_init) {
GConfClient *client = gconf_client_get_default ();
char *policy = gconf_client_get_string (client, "/apps/nautilus/preferences/click_policy", NULL);
g_object_unref (G_OBJECT (client));
if (policy != NULL) {
use_single_click = (0 == g_ascii_strcasecmp (policy, "single"));
g_free (policy);
}
needs_init = FALSE;
}
return use_single_click;
}
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_col, col;
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 :
d (fprintf (stderr, "START------------->\n"));
count = cc->info->n_categories;
cc->line_count = 0;
keep_going = TRUE;
for (col = 0 ; keep_going; col++) {
d (fprintf (stderr, "\tcol[%d]\n", col));
keep_going = FALSE;
max_width = 0.;
/* 0.1) Find the maximum width for this column */
for (line = 0 ; line < breaks->len ; ) {
line_col = col + g_array_index (breaks, int, line);
line++;
if (line < breaks->len && line_col >= g_array_index (breaks, int, line))
continue;
/* 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_col >= cat->n_entries)
continue;
ei = cat->entries[line_col]->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_col = col + g_array_index (breaks, int, line);
d (fprintf (stderr, "col[%d] line[%d] == %g\n", col, line, max_width));
line++;
if (line < breaks->len && line_col >= g_array_index (breaks, int, line))
continue;
for (j = 0; j < count; j++) {
ControlCenterCategory *cat = cc->info->categories[j];
if (line_col >= cat->n_entries)
continue;
ei = cat->entries[line_col]->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->n_entries; 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->n_entries; 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 + 2 * PAD,
"y2", (double) ei->text_height + 1, /* expand it down slightly */
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, -PAD, 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;
GtkWidget *widget = GTK_WIDGET (ei->cc->canvas);
GtkStateType state;
if (ei->pixbuf) {
gnome_canvas_item_show_hide (ei->highlight_pixbuf, ei->highlighted);
gnome_canvas_item_show_hide (ei->pixbuf, !ei->highlighted);
}
if (!ei->selected)
state = GTK_STATE_NORMAL;
else if (gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (widget))))
state = GTK_STATE_SELECTED;
else
state = GTK_STATE_ACTIVE;
gnome_canvas_item_show_hide (ei->selection, ei->selected);
g_object_set (ei->selection,
"fill_color_gdk", &widget->style->base [state],
NULL);
g_object_set (ei->text,
"fill_color_gdk", &widget->style->text [state],
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->n_entries; 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->n_categories; i++) {
CategoryInfo *catinfo = cc->info->categories[i]->user_data;
if (cc->info->categories[i] == category) {
for (i = 0; i < category->n_entries; 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->n_categories; i++) {
CategoryInfo *catinfo = cc->info->categories[i]->user_data;
if (y < catinfo->line_count) {
int j;
for (j = 0; j < cc->info->categories[i]->n_entries; 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]->n_entries);
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->n_categories; 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]->n_entries; 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)
{
EntryInfo *ei = NULL;
GtkAdjustment *pos;
double affine[6];
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);
if (entry == NULL)
return;
ei = entry->user_data;
gnome_canvas_item_i2c_affine (GNOME_CANVAS_ITEM (ei->group), affine);
pos = gtk_layout_get_vadjustment (GTK_LAYOUT (ei->cover->canvas));
if (affine[5] < pos->value)
gtk_adjustment_set_value (pos, MAX (affine[5] - PAD, 0));
else if ((affine[5] + ei->height) > (pos->value+pos->page_size))
gtk_adjustment_set_value (pos, MAX (MIN (affine[5] + ei->height + PAD, pos->upper) - pos->page_size, 0));
}
static void
activate_entry (ControlCenterEntry *entry)
{
EntryInfo *ei = entry->user_data;
if (!ei->launching) {
GnomeDesktopItem *desktop_item;
ei->launching = TRUE;
gtk_timeout_add (1000, cb_entry_info_reset, ei);
desktop_item = gnome_desktop_item_new_from_file (entry->desktop_entry,
GNOME_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS,
NULL);
if (desktop_item != NULL) {
gnome_desktop_item_launch (desktop_item, NULL, 0, NULL);
gnome_desktop_item_unref (desktop_item);
}
}
}
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:
if (single_click_activates ()) {
activate_entry (entry);
} else {
select_entry (cc, entry);
set_x (cc);
}
return TRUE;
case GDK_2BUTTON_PRESS:
activate_entry (entry);
return TRUE;
default:
return FALSE;
}
}
static gboolean
cb_canvas_event (GnomeCanvasItem *item, GdkEvent *event, ControlCenter *cc)
{
int x, y;
int do_set_x = FALSE;
if (event->type == GDK_BUTTON_PRESS) {
select_entry (cc, NULL);
set_x (cc);
return TRUE;
}
if (event->type != GDK_KEY_PRESS)
return FALSE;
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;
}
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->n_categories; 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]->n_entries; j++) {
ControlCenterEntry *entry = cc->info->categories[i]->entries[j];
EntryInfo *entryinfo = entry->user_data;
if (font_changed && entryinfo->text)
g_object_set (entryinfo->text,
"font", NULL,
NULL);
setup_entry (entry);
}
}
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 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 (cb_canvas_event), cc);
cc->line_count = 0;
for (i = 0; i < cc->info->n_categories; 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->n_categories != 1 || cc->info->categories[0]->real_category)) {
char *label = g_strdup_printf ("<span weight=\"bold\">%s</span>", 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]->n_entries; 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,
GNOMECC_TYPE_ROUNDED_RECT,
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_pixbuf) {
GdkPixbuf *pixbuf = cc->info->categories[i]->entries[j]->icon_pixbuf;
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 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 <clahey@ximian.com>", 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,
&GTK_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->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 void
cb_focus_changed (ControlCenter *cc)
{
if (cc->selected)
setup_entry (cc->selected);
}
static GtkWindow *
create_window (void)
{
GtkWidget *window;
GnomeClient *client;
GtkWidget *appbar;
ControlCenter *cc;
ControlCenterInformation *info;
client = gnome_master_client ();
g_signal_connect (G_OBJECT (client),
"die",
G_CALLBACK (gnome_cc_die), NULL);
info = control_center_get_information ();
window = gnome_app_new ("gnomecc", _("Desktop Preferences"));
gtk_window_set_icon_name (GTK_WINDOW (window), "gnome-control-center");
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);
gtk_widget_show_all (window);
g_object_weak_ref (G_OBJECT (window), (GWeakNotify) gnome_cc_die, NULL);
g_signal_connect_swapped (G_OBJECT (window),
"notify::has-toplevel-focus",
G_CALLBACK (cb_focus_changed), cc);
return GTK_WINDOW (window);
}
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,
NULL);
create_window ();
gtk_main ();
return 0;
}