/* This is -*- C -*- */
/* $Id: guppi-metric-entry.c,v 1.11 2002/01/14 05:01:23 trow Exp $ */
/*
* guppi-metric-entry.c
*
* Copyright (C) 2000 EMC Capital Management, Inc.
* Copyright (C) 2001 The Free Software Foundation
*
* Developed by Jon Trowbridge <trow@gnu.org> and
* Havoc Pennington <hp@pobox.com>.
*
* 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 Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <config.h>
#include "guppi-metric-entry.h"
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <glib.h>
#include <gtk/gtkeditable.h>
#include <gtk/gtkentry.h>
#include <gtk/gtksignal.h>
#include <gtk/gtkmenu.h>
#include <gtk/gtkmenuitem.h>
#include <gtk/gtkoptionmenu.h>
#include "guppi-defaults.h"
#include "guppi-memory.h"
static GtkObjectClass *parent_class = NULL;
enum {
CHANGED_VALUE,
CHANGED_UNITS,
LAST_SIGNAL
};
guint me_signals[LAST_SIGNAL] = { 0 };
static void
guppi_metric_entry_destroy (GtkObject *obj)
{
if (parent_class->destroy)
parent_class->destroy (obj);
}
static void
guppi_metric_entry_finalize (GtkObject *obj)
{
if (parent_class->finalize)
parent_class->finalize (obj);
}
/**********************************************************************/
static void
read_value (GtkEditable *editable, gpointer data)
{
GuppiMetricEntry *me = GUPPI_METRIC_ENTRY (data);
gchar *txt = gtk_editable_get_chars (editable, 0, -1);
double x = atof (txt);
guppi_metric_entry_set_pt_value (me, guppi_to_pt (x, me->displayed_units));
guppi_free (txt);
}
static void
write_value (GuppiMetricEntry *me)
{
double x = guppi_from_pt (guppi_metric_entry_pt_value (me), me->displayed_units);
gchar *str = g_strdup_printf ("%g", x);
gint pos = 0;
gtk_editable_delete_text (GTK_EDITABLE (me->entry), 0, -1);
gtk_editable_insert_text (GTK_EDITABLE (me->entry), str, strlen (str), &pos);
g_free (str);
}
static void
changed_units (GuppiMetricEntry *me)
{
gchar *str;
double new_pt_val;
new_pt_val = guppi_from_pt (me->pt_val, me->displayed_units);
str = guppi_strdup_printf ("%g", new_pt_val);
gtk_entry_set_text (GTK_ENTRY (me->entry), str);
guppi_free (str);
}
/**********************************************************************/
static void
guppi_metric_entry_class_init (GuppiMetricEntryClass *klass)
{
GtkObjectClass *object_class = (GtkObjectClass *) klass;
parent_class = gtk_type_class (GTK_TYPE_HBOX);
object_class->destroy = guppi_metric_entry_destroy;
object_class->finalize = guppi_metric_entry_finalize;
klass->changed_units = changed_units;
me_signals[CHANGED_VALUE] =
gtk_signal_new ("changed_value",
GTK_RUN_FIRST,
object_class->type,
GTK_SIGNAL_OFFSET (GuppiMetricEntryClass, changed_value),
gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
me_signals[CHANGED_UNITS] =
gtk_signal_new ("changed_units",
GTK_RUN_FIRST,
object_class->type,
GTK_SIGNAL_OFFSET (GuppiMetricEntryClass, changed_units),
gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
gtk_object_class_add_signals (object_class, me_signals, LAST_SIGNAL);
}
static void
guppi_metric_entry_init (GuppiMetricEntry *obj)
{
}
GtkType
guppi_metric_entry_get_type (void)
{
static GtkType guppi_metric_entry_type = 0;
if (!guppi_metric_entry_type) {
static const GtkTypeInfo guppi_metric_entry_info = {
"GuppiMetricEntry",
sizeof (GuppiMetricEntry),
sizeof (GuppiMetricEntryClass),
(GtkClassInitFunc) guppi_metric_entry_class_init,
(GtkObjectInitFunc) guppi_metric_entry_init,
NULL, NULL, (GtkClassInitFunc) NULL
};
guppi_metric_entry_type =
gtk_type_unique (GTK_TYPE_HBOX, &guppi_metric_entry_info);
}
return guppi_metric_entry_type;
}
static void
unit_select_cb (GtkWidget *w, gpointer ptr)
{
GuppiMetricEntry *me = GUPPI_METRIC_ENTRY (ptr);
guppi_metric_t units;
/* Make sure that what appears in the entry is actually what we have stored
as the point value. */
read_value (GTK_EDITABLE (me->entry), me);
units = (guppi_metric_t)
GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (w), "units"));
guppi_metric_entry_set_displayed_units (me, units);
}
static gboolean
valid_number (GuppiMetricEntry *me, const gchar *str)
{
gchar *end_ptr;
gint len = strlen (str);
double x;
if (len == 1 && *str == '-' && !me->no_negatives)
return TRUE;
if (len == 1 && *str == '.')
return TRUE;
x = strtod (str, &end_ptr);
if (end_ptr == NULL)
return FALSE;
if (*end_ptr != '\0')
return FALSE;
if (me->no_negatives && x < 0)
return FALSE;
if (me->no_zero && x == 0)
return FALSE;
return TRUE;
}
static void
insert_text_handler (GtkEditable *editable, const gchar *text, gint length,
gint *position, gpointer data)
{
GuppiMetricEntry *me = GUPPI_METRIC_ENTRY (data);
gint i, j;
gchar *result = guppi_new (gchar, length);
gchar *pre_insert_text;
gchar *post_insert_text;
gint saved_pos;
/* Filter out any characters that couldn't possibly be part of a number. */
j = 0;
for (i = 0; i < length; i++)
if (isdigit ((guchar)text[i]) || text[i] == '.' || text[i] == ','
|| text[i] == '-') {
result[j] = text[i];
++j;
}
gtk_signal_handler_block_by_func (GTK_OBJECT (editable),
GTK_SIGNAL_FUNC (insert_text_handler),
data);
pre_insert_text = gtk_editable_get_chars (editable, 0, -1);
saved_pos = gtk_editable_get_position (editable);
gtk_editable_insert_text (editable, result, j, position);
post_insert_text = gtk_editable_get_chars (editable, 0, -1);
/* Check that our entry is a valid number. If not, we undo the insert. */
if (!valid_number (me, post_insert_text)) {
gint pos = 0;
gtk_editable_delete_text (editable, 0, -1);
gtk_editable_insert_text (editable, pre_insert_text,
strlen (pre_insert_text), &pos);
/* This doesn't seem to work... */
gtk_editable_set_position (editable, saved_pos);
} else {
read_value (editable, me);
}
gtk_signal_handler_unblock_by_func (GTK_OBJECT (editable),
GTK_SIGNAL_FUNC (insert_text_handler),
data);
gtk_signal_emit_stop_by_name (GTK_OBJECT (editable), "insert_text");
guppi_free (result);
guppi_free (pre_insert_text);
guppi_free (post_insert_text);
}
static void
delete_text_handler (GtkEditable *editable, gint start_pos, gint end_pos,
gpointer user_data)
{
read_value (editable, GUPPI_METRIC_ENTRY (user_data));
}
static void
focus_out (GtkEditable *editable, GdkEventFocus *ev, gpointer data)
{
read_value (editable, data);
}
void
guppi_metric_entry_construct (GuppiMetricEntry *me)
{
const guppi_metric_t units[] = {
GUPPI_PT, GUPPI_IN, GUPPI_CM, GUPPI_MM, GUPPI_PX,
GUPPI_INVALID_METRIC
};
gint i, j = 0;
GtkWidget *menu;
g_return_if_fail (me != NULL && GUPPI_IS_METRIC_ENTRY (me));
me->pt_val = 0;
me->displayed_units = guppi_default_units ();
me->entry = gtk_entry_new ();
gtk_signal_connect (GTK_OBJECT (me->entry),
"insert_text",
GTK_SIGNAL_FUNC (insert_text_handler), me);
gtk_signal_connect (GTK_OBJECT (me->entry),
"delete_text",
GTK_SIGNAL_FUNC (delete_text_handler), me);
gtk_signal_connect (GTK_OBJECT (me->entry),
"activate", GTK_SIGNAL_FUNC (read_value), me);
gtk_signal_connect (GTK_OBJECT (me->entry),
"focus_out_event", GTK_SIGNAL_FUNC (focus_out), me);
gtk_widget_show (me->entry);
menu = gtk_menu_new ();
for (i = 0; units[i] != GUPPI_INVALID_METRIC; ++i) {
GtkWidget *mi;
if (units[i] == me->displayed_units)
j = i;
mi = gtk_menu_item_new_with_label (guppi_metric_name (units[i]));
gtk_object_set_data (GTK_OBJECT (mi), "units",
GINT_TO_POINTER (units[i]));
gtk_signal_connect (GTK_OBJECT (mi), "activate",
GTK_SIGNAL_FUNC (unit_select_cb), me);
gtk_widget_show (mi);
gtk_menu_append (GTK_MENU (menu), mi);
}
me->units_opt = gtk_option_menu_new ();
gtk_option_menu_set_menu (GTK_OPTION_MENU (me->units_opt), menu);
gtk_option_menu_set_history (GTK_OPTION_MENU (me->units_opt), j);
gtk_widget_show (menu);
gtk_widget_show (me->units_opt);
gtk_box_pack_start (GTK_BOX (me), me->entry, TRUE, TRUE, 0);
gtk_box_pack_end (GTK_BOX (me), me->units_opt, FALSE, FALSE, 0);
}
GtkWidget *
guppi_metric_entry_new (void)
{
GtkObject *obj = guppi_type_new (guppi_metric_entry_get_type ());
guppi_metric_entry_construct (GUPPI_METRIC_ENTRY (obj));
return GTK_WIDGET (obj);
}
double
guppi_metric_entry_pt_value (GuppiMetricEntry *me)
{
g_return_val_if_fail (me != NULL && GUPPI_IS_METRIC_ENTRY (me), 0);
return me->pt_val;
}
void
guppi_metric_entry_set_pt_value (GuppiMetricEntry *me, double x)
{
g_return_if_fail (me != NULL && GUPPI_IS_METRIC_ENTRY (me));
if (me->no_negatives)
g_return_if_fail (x >= 0);
if (me->no_zero)
g_return_if_fail (x != 0);
if (x != me->pt_val) {
me->pt_val = x;
gtk_signal_emit (GTK_OBJECT (me), me_signals[CHANGED_VALUE]);
write_value (me);
}
}
guppi_metric_t
guppi_metric_entry_displayed_units (GuppiMetricEntry *me)
{
g_return_val_if_fail (me != NULL && GUPPI_IS_METRIC_ENTRY (me),
GUPPI_INVALID_METRIC);
return me->displayed_units;
}
void
guppi_metric_entry_set_displayed_units (GuppiMetricEntry *me,
guppi_metric_t units)
{
g_return_if_fail (me != NULL && GUPPI_IS_METRIC_ENTRY (me));
g_return_if_fail (units != GUPPI_INVALID_METRIC);
if (me->displayed_units != units) {
me->displayed_units = units;
gtk_signal_emit (GTK_OBJECT (me), me_signals[CHANGED_UNITS]);
}
}
/* $Id: guppi-metric-entry.c,v 1.11 2002/01/14 05:01:23 trow Exp $ */
syntax highlighted by Code2HTML, v. 0.9.1