view src/gaim-disclosure.c @ 9456:0577bb34622d

[gaim-migrate @ 10280] This fixes bug #964043 We were truncating the port to 4 ascii digits, because our buffer was too small. Oops. Let this be an example to you, not all users are on crack. This bug was somewhat confusing by the fact that the spin button to choose the port is only big enough to show 4 digits at a time. That's completely unrelated though, you can hit HOME and END and stuff to see all the digits and it saves file and all. committer: Tailor Script <tailor@pidgin.im>
author Tim Ringenbach <marv@pidgin.im>
date Tue, 06 Jul 2004 06:22:14 +0000
parents ef881489396e
children
line wrap: on
line source

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  Gaim is the legal property of its developers, whose names are too numerous
 *  to list here.  Please refer to the COPYRIGHT file distributed with this
 *  source distribution.
 *
 *  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 of the License, 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 Street #330, Boston, MA 02111-1307, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gtk/gtktogglebutton.h>

#include "gaim-disclosure.h"

#ifdef ENABLE_NLS
#  include <libintl.h>
#  define _(x) gettext(x)
#  ifdef gettext_noop
#    define N_(String) gettext_noop (String)
#  else
#    define N_(String) (String)
#  endif
#else
#  define N_(String) (String)
#  define _(x) (x)
#endif

static GtkCheckButtonClass *parent_class = NULL;

struct _GaimDisclosurePrivate {
	GtkWidget *container;
	char *shown;
	char *hidden;
	
	guint32 expand_id;
	GtkExpanderStyle style;

	int expander_size;
	int direction;
};

static void
finalize (GObject *object)
{
	GaimDisclosure *disclosure;

	disclosure = GAIM_DISCLOSURE (object);
	if (disclosure->priv == NULL) {
		return;
	}

	g_free (disclosure->priv->hidden);
	g_free (disclosure->priv->shown);

	if (disclosure->priv->container != NULL) {
		g_object_unref (G_OBJECT (disclosure->priv->container));
	}
	
	g_free (disclosure->priv);
	disclosure->priv = NULL;

	G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
get_x_y (GaimDisclosure *disclosure,
	 int *x,
	 int *y,
	 GtkStateType *state_type)
{
	GtkCheckButton *check_button;
	int indicator_size = 0;
	int focus_width;
	int focus_pad;
	gboolean interior_focus;
	GtkWidget *widget = GTK_WIDGET (disclosure);
	GtkBin *bin = GTK_BIN (disclosure);
	int width;
	
	if (GTK_WIDGET_VISIBLE (disclosure) &&
	    GTK_WIDGET_MAPPED (disclosure)) {
		check_button = GTK_CHECK_BUTTON (disclosure);
		
		gtk_widget_style_get (widget,
				      "interior_focus", &interior_focus,
				      "focus-line-width", &focus_width,
				      "focus-padding", &focus_pad,
				      NULL);
		
		*state_type = GTK_WIDGET_STATE (widget);
		if ((*state_type != GTK_STATE_NORMAL) &&
		    (*state_type != GTK_STATE_PRELIGHT)) {
			*state_type = GTK_STATE_NORMAL;
		}

		if (bin->child) {
			width = bin->child->allocation.x - widget->allocation.x - (2 * GTK_CONTAINER (widget)->border_width);
		} else {
			width = widget->allocation.width;
		}
		
		*x = widget->allocation.x + (width) / 2;
		*y = widget->allocation.y + widget->allocation.height / 2;

		if (interior_focus == FALSE) {
			*x += focus_width + focus_pad;
		}

		*state_type = GTK_WIDGET_STATE (widget) == GTK_STATE_ACTIVE ? GTK_STATE_NORMAL : GTK_WIDGET_STATE (widget);

		if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) {
			*x = widget->allocation.x + widget->allocation.width - (indicator_size + *x - widget->allocation.x);
		}
	} else {
		*x = 0;
		*y = 0;
		*state_type = GTK_STATE_NORMAL;
	}
}

static gboolean
expand_collapse_timeout (gpointer data)
{
	GtkWidget *widget = data;
	GaimDisclosure *disclosure = data;
	GtkStateType state_type;
	int x, y;
	
	gdk_window_invalidate_rect (widget->window, &widget->allocation, TRUE);
	get_x_y (disclosure, &x, &y, &state_type);
	
	gtk_paint_expander (widget->style,
			    widget->window,
			    state_type,
			    &widget->allocation,
			    widget,
			    "disclosure",
			    x, y,
			    disclosure->priv->style);

	disclosure->priv->style += disclosure->priv->direction;
	if ((int) disclosure->priv->style > (int) GTK_EXPANDER_EXPANDED) {
		disclosure->priv->style = GTK_EXPANDER_EXPANDED;

		if (disclosure->priv->container != NULL) {
			gtk_widget_show (disclosure->priv->container);
		}

		g_object_set (G_OBJECT (disclosure),
			      "label", disclosure->priv->hidden,
			      NULL);
		return FALSE;
	} else if ((int) disclosure->priv->style < (int) GTK_EXPANDER_COLLAPSED) {
		disclosure->priv->style = GTK_EXPANDER_COLLAPSED;

		if (disclosure->priv->container != NULL) {
			gtk_widget_hide (disclosure->priv->container);
		}

		g_object_set (G_OBJECT (disclosure),
			      "label", disclosure->priv->shown,
			      NULL);

		return FALSE;
	} else {
		return TRUE;
	}
}

static void
do_animation (GaimDisclosure *disclosure,
	      gboolean opening)
{
	if (disclosure->priv->expand_id > 0) {
		gaim_timeout_remove(disclosure->priv->expand_id);
	}

	disclosure->priv->direction = opening ? 1 : -1;
	disclosure->priv->expand_id = gaim_timeout_add (50, expand_collapse_timeout, disclosure);
}

static void
toggled (GtkToggleButton *tb)
{
	GaimDisclosure *disclosure;

	disclosure = GAIM_DISCLOSURE (tb);
	do_animation (disclosure, gtk_toggle_button_get_active (tb));

	if (disclosure->priv->container == NULL) {
		return;
	}
}

static void
draw_indicator (GtkCheckButton *check,
		GdkRectangle *area)
{
	GtkWidget *widget = GTK_WIDGET (check);
	GaimDisclosure *disclosure = GAIM_DISCLOSURE (check);
	GtkStateType state_type;
	int x, y;

	get_x_y (disclosure, &x, &y, &state_type);
	gtk_paint_expander (widget->style,
			    widget->window,
			    state_type,
			    area,
			    widget,
			    "treeview",
			    x, y,
			    disclosure->priv->style);
}

static void
class_init (GaimDisclosureClass *klass)
{
	GObjectClass *object_class;
	GtkWidgetClass *widget_class;
	GtkCheckButtonClass *button_class;
	GtkToggleButtonClass *toggle_class;
	
	object_class = G_OBJECT_CLASS (klass);
	widget_class = GTK_WIDGET_CLASS (klass);
	button_class = GTK_CHECK_BUTTON_CLASS (klass);
	toggle_class = GTK_TOGGLE_BUTTON_CLASS (klass);
	
	toggle_class->toggled = toggled;
	button_class->draw_indicator = draw_indicator;

	object_class->finalize = finalize;

	parent_class = g_type_class_peek_parent (klass);

	gtk_widget_class_install_style_property (widget_class,
						 g_param_spec_int ("expander_size",
								   _("Expander Size"),
								   _("Size of the expander arrow"),
								   0, G_MAXINT,
								   10, G_PARAM_READABLE));
}

static void
init (GaimDisclosure *disclosure)
{
	disclosure->priv = g_new0 (GaimDisclosurePrivate, 1);
	disclosure->priv->expander_size = 10;
}

GType
gaim_disclosure_get_type (void)
{
	static GType type = 0;

	if (type == 0) {
		GTypeInfo info = {
			sizeof (GaimDisclosureClass),
			NULL, NULL, (GClassInitFunc) class_init, NULL, NULL,
			sizeof (GaimDisclosure), 0, (GInstanceInitFunc) init
		};

		type = g_type_register_static (GTK_TYPE_CHECK_BUTTON, "GaimDisclosure", &info, 0);
	}

	return type;
}

GtkWidget *
gaim_disclosure_new (const char *shown,
		     const char *hidden)
{
	GaimDisclosure *disclosure;

	disclosure = g_object_new (gaim_disclosure_get_type (), "label", shown, NULL);

	disclosure->priv->shown = g_strdup (shown);
	disclosure->priv->hidden = g_strdup (hidden);
	return GTK_WIDGET (disclosure);
}

void
gaim_disclosure_set_container (GaimDisclosure *disclosure,
			       GtkWidget *container)
{
	g_object_ref (G_OBJECT (container));
	disclosure->priv->container = container;
}