summaryrefslogtreecommitdiff
path: root/app/wlib/gtklib/single.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/wlib/gtklib/single.c')
-rw-r--r--app/wlib/gtklib/single.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/app/wlib/gtklib/single.c b/app/wlib/gtklib/single.c
new file mode 100644
index 0000000..45ed6e4
--- /dev/null
+++ b/app/wlib/gtklib/single.c
@@ -0,0 +1,335 @@
+/** \file single.c
+ * Single line entry field for text
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis, 2012 Martin Fischer
+ *
+ * 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.
+ */
+
+#define GTK_DISABLE_SINGLE_INCLUDES
+#define GDK_DISABLE_DEPRECATED
+// #define GTK_DISABLE_DEPRECATED
+#define GSEAL_ENABLE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+#include "gtkint.h"
+
+#define TIMEOUT_INACTIVITY (500) /**< timeout for entry fields in millisecs */
+
+/*
+ *****************************************************************************
+ *
+ * Text Boxes
+ *
+ *****************************************************************************
+ */
+
+struct wString_t {
+ WOBJ_COMMON
+ char *valueP; /**< pointer to result buffer */
+ wIndex_t valueL; /**< maximum length */
+ wStringCallBack_p action; /**< callback for changes */
+ wBool_t busy; /**< busy flag to prevent re-entry problems? */
+ wBool_t hasSignal; /** needs signal to be suppressed */
+ guint timer; /**< timer source for inactivity timer */
+};
+
+/**
+ * Set the string value in a string entry field
+ *
+ * \param b IN widget to be updated
+ * \param arg IN new string value
+ * \return
+ */
+
+void wStringSetValue(
+ wString_p b,
+ const char *arg)
+{
+ if (b->widget == NULL)
+ abort();
+
+ // the contents should not be changed programatically while
+ // the user is editing it
+ if( !(gtk_widget_has_focus(b->widget))) {
+ if (b->hasSignal)
+ gtk_signal_handler_block_by_data(GTK_OBJECT(b->widget), b);
+ gtk_entry_set_text(GTK_ENTRY(b->widget), arg);
+ if (b->hasSignal)
+ gtk_signal_handler_unblock_by_data(GTK_OBJECT(b->widget), b);
+ }
+}
+
+/**
+ * Set the width of the entry field
+ *
+ * \param b IN widget to be updated
+ * \param w IN new width
+ * \return
+ */
+
+void wStringSetWidth(
+ wString_p b,
+ wPos_t w)
+{
+ gtk_widget_set_size_request(b->widget, w, -1);
+ b->w = w;
+}
+
+/**
+ * Return the entered value
+ *
+ * \param b IN entry field
+ * \return the entered text
+ */
+
+const char *wStringGetValue(
+ wString_p b)
+{
+ if ( !b->widget )
+ abort();
+
+ return gtk_entry_get_text(GTK_ENTRY(b->widget));
+}
+
+/**
+ * Kill an active timer
+ *
+ * \param b IN entry field
+ * \return the entered text
+ */
+
+static gboolean killTimer(
+ GtkEntry *widget,
+ GdkEvent *event,
+ wString_p b)
+{
+
+ // remove all timers related to this widget
+ while( g_source_remove_by_user_data( b ))
+ ;
+ b->timer = 0;
+
+ if (b->action) {
+ const char *s;
+
+ s = gtk_entry_get_text(GTK_ENTRY(b->widget));
+ b->action(s, b->data);
+ }
+ gtk_editable_select_region( GTK_EDITABLE( widget ), 0, 0 );
+ return( FALSE );
+}
+
+/**
+ * Timer handler for string activity. This timer expires if the user
+ * doesn't change an entry value within the preset time.
+ */
+
+static gboolean
+timeoutString( wString_p bs )
+{
+
+ if ( !bs )
+ return( FALSE );
+ if (bs->widget == 0)
+ abort();
+
+ if (bs->action) {
+ const char *s;
+
+ s = gtk_entry_get_text(GTK_ENTRY(bs->widget));
+ if ( s )
+ bs->action(s, bs->data);
+ }
+
+ bs->timer = 0;
+ return( FALSE );
+}
+
+/**
+ * Signal handler for 'activate' signal: callback with the current value and then
+ * select the whole default value
+ *
+ * \param widget IN the edit field
+ * \param b IN the widget data structure
+ * \return
+ */
+
+static gboolean stringActivated(
+ GtkEntry *widget,
+ wString_p b)
+{
+ const char *s;
+
+ if ( !b )
+ return( FALSE );
+
+ s = wStringGetValue(b);
+
+ if (b->valueP)
+ strcpy(b->valueP, s);
+
+ if (b->action) {
+ b->action(s, b->data);
+ }
+
+ // select the complete default value to make editing it easier
+ gtk_editable_select_region( GTK_EDITABLE( widget ), 0, -1 );
+ return( FALSE );
+}
+
+/**
+ * Signal handler for changes in an entry field
+ *
+ * \param widget IN
+ * \param entry field IN
+ * \return
+ */
+
+static void stringChanged(
+ GtkEntry *widget,
+ wString_p b)
+{
+ const char *new_value;
+
+ if ( !b || b->busy )
+ return;
+
+ // get the entered value
+ new_value = wStringGetValue(b);
+ if (b->valueP != NULL)
+ strcpy(b->valueP, new_value);
+
+ //
+ if (b->action){
+ // if one exists, remove the inactivity timer
+ if( b->timer )
+ g_source_remove( b->timer );
+
+ // create a new timer
+ b->timer = g_timeout_add( TIMEOUT_INACTIVITY,
+ (GSourceFunc)timeoutString,
+ b );
+
+ }
+ return;
+}
+
+/**
+ * Create a single line entry field for a string value
+ *
+ * \param parent IN parent widget
+ * \param x IN x position
+ * \param y IN y position
+ * \param helpStr IN help anchor
+ * \param labelStr IN label
+ * \param option IN option (supported BO_READONLY )
+ * \param width IN width of entry field
+ * \param valueP IN default value
+ * \param valueL IN maximum length of entry
+ * \param action IN application callback function
+ * \param data IN application data
+ * \return the created widget
+ */
+
+wString_p wStringCreate(
+ wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char *helpStr,
+ const char *labelStr,
+ long option,
+ wPos_t width,
+ char *valueP,
+ wIndex_t valueL,
+ wStringCallBack_p action,
+ void *data)
+{
+ wString_p b;
+
+ // create and initialize the widget
+ b = (wString_p)wlibAlloc(parent, B_TEXT, x, y, labelStr, sizeof *b, data);
+ b->valueP = valueP;
+ b->action = action;
+ b->option = option;
+ b->valueL = valueL;
+ b->timer = 0;
+ b->hasSignal = 0;
+ wlibComputePos((wControl_p)b);
+
+ // create the gtk entry field and set maximum length if desired
+ b->widget = (GtkWidget *)gtk_entry_new();
+ if (b->widget == NULL) abort();
+
+ if( valueL )
+ gtk_entry_set_max_length( GTK_ENTRY( b->widget ), valueL );
+
+ // it is assumed that the parent is a fixed layout widget and the entry can
+ // be placed at a specific position
+ gtk_fixed_put(GTK_FIXED(parent->widget), b->widget, b->realX, b->realY);
+
+ // set minimum size for widget
+ if (width)
+ gtk_widget_set_size_request(b->widget, width, -1);
+
+ // get the resulting size
+ wlibControlGetSize((wControl_p)b);
+
+ // if desired, place a label in front of the created widget
+ if (labelStr)
+ b->labelW = wlibAddLabel((wControl_p)b, labelStr);
+
+ if (option & BO_READONLY)
+ gtk_editable_set_editable(GTK_EDITABLE(b->widget), FALSE);
+
+ // set the default text and select it to make replacing it easier
+ if (b->valueP) {
+ wStringSetValue(b, b->valueP);
+ // select the text only if text is editable
+ }
+
+ // show
+ gtk_widget_show(b->widget);
+
+ // add the new widget to the list of created widgets
+ wlibAddButton((wControl_p)b);
+
+ // link into help
+ wlibAddHelpString(b->widget, helpStr);
+
+ g_signal_connect(GTK_OBJECT(b->widget), "changed", G_CALLBACK(stringChanged), b);
+ //g_signal_connect(GTK_OBJECT(b->widget), "activate", G_CALLBACK(stringActivated), b);
+ b->hasSignal = 1;
+
+ // set the default text and select it to make replacing it easier
+ if (b->valueP) {
+ wStringSetValue(b, b->valueP);
+ // select the text only if text is editable
+ }
+
+ gtk_widget_add_events( b->widget, GDK_FOCUS_CHANGE_MASK );
+ g_signal_connect(GTK_OBJECT(b->widget), "focus-out-event", G_CALLBACK(killTimer), b);
+
+ return b;
+}