summaryrefslogtreecommitdiff
path: root/app/wlib/gtklib/single.c
blob: 600f1dd72c7ac001cadcf659494db771f30c56b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/** \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 notice_activate; /** if flag set to observe enter key **/
	wBool_t enter_pressed;	/**< flag if enter was pressed */
	wBool_t hasSignal;		/** needs signal to be suppressed */
	int count;				/** number of 100ms since last entry **/
	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 checks the input if the user
 * 	doesn't change an entry value for the preset time (0.5s).
 */

static gboolean
timeoutString( wString_p bs ) 
{
	const char *new_value;
	if ( !bs )
		return( FALSE );
	if (bs->widget == 0) 
		abort();
	
	bs->count--;

	if (bs->count==0) {
		// get the currently entered value
	    new_value = wStringGetValue(bs);
		if (bs->valueP != NULL)
			strcpy(bs->valueP, new_value);

		if (bs->action) {
			bs->enter_pressed = FALSE;     //Normal input
			if ( new_value )
				bs->action(new_value,bs->data);
		}
	}
	if (bs->count<=0) {
		bs->timer = 0;
		return( FALSE );   //Stop timer
	} else {
		return TRUE;       //Wait 100ms
	}
}

/**
 * Signal handler for 'activate' signal: enter pressed - 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;
	const char * output = "\n";

	if ( !b )
		return( FALSE );
	
	s = wStringGetValue(b);

	if (b->valueP)
		strcpy(b->valueP, s);

	if (b->action) {
		b->enter_pressed = TRUE;
		b->action( output, b->data);
	}
	
	// select the complete default value to make editing it easier
	gtk_editable_select_region( GTK_EDITABLE( widget ), 0, -1 );
	return( TRUE );
}

static gboolean stringExposed(GtkWidget* widget, GdkEventExpose * event, gpointer g )
{
	wControl_p b = (wControl_p)g;
	return wControlExpose(widget,event,b);
}


/**
 * 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  )
		return;

	b->count = 5;              /* set ~500 ms from now */

	// 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/5,
								  (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);
	//if (option&BO_ENTER)
		g_signal_connect(GTK_OBJECT(b->widget), "activate", G_CALLBACK(stringActivated), b);
	b->hasSignal = 1;
		g_signal_connect_after(GTK_OBJECT(b->widget), "expose-event",
	    							G_CALLBACK(stringExposed), b);
	
	// 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;
}