summaryrefslogtreecommitdiff
path: root/app/wlib/gtklib
diff options
context:
space:
mode:
Diffstat (limited to 'app/wlib/gtklib')
-rw-r--r--app/wlib/gtklib/.directory7
-rw-r--r--app/wlib/gtklib/CMakeLists.txt37
-rw-r--r--app/wlib/gtklib/ChangeLog250
-rw-r--r--app/wlib/gtklib/dynarr.h49
-rw-r--r--app/wlib/gtklib/filesel.c174
-rw-r--r--app/wlib/gtklib/gtkbitmap.c85
-rw-r--r--app/wlib/gtklib/gtkbutton.c461
-rw-r--r--app/wlib/gtklib/gtkcolor.c476
-rw-r--r--app/wlib/gtklib/gtkdraw-cairo.c1212
-rw-r--r--app/wlib/gtklib/gtkdraw.c1044
-rw-r--r--app/wlib/gtklib/gtkfont.c320
-rw-r--r--app/wlib/gtklib/gtkhelp.c733
-rw-r--r--app/wlib/gtklib/gtkint.h180
-rw-r--r--app/wlib/gtklib/gtklist.c1109
-rw-r--r--app/wlib/gtklib/gtkmenu.c873
-rw-r--r--app/wlib/gtklib/gtkmisc.c1210
-rw-r--r--app/wlib/gtklib/gtksimple.c366
-rw-r--r--app/wlib/gtklib/gtksingle.c645
-rw-r--r--app/wlib/gtklib/gtksplash.c142
-rw-r--r--app/wlib/gtklib/gtktext.c524
-rw-r--r--app/wlib/gtklib/gtkwindow.c856
-rw-r--r--app/wlib/gtklib/gtkxpm.c177
-rw-r--r--app/wlib/gtklib/psprint.c1599
-rw-r--r--app/wlib/gtklib/square10.bmp8
-rw-r--r--app/wlib/gtklib/uthash.h960
-rw-r--r--app/wlib/gtklib/wpref.c502
26 files changed, 13999 insertions, 0 deletions
diff --git a/app/wlib/gtklib/.directory b/app/wlib/gtklib/.directory
new file mode 100644
index 0000000..2482e35
--- /dev/null
+++ b/app/wlib/gtklib/.directory
@@ -0,0 +1,7 @@
+[Dolphin]
+HeaderColumnWidths=500,64,124
+SortOrder=1
+SortRole=date
+Timestamp=2015,10,6,20,51,0
+Version=3
+ViewMode=1
diff --git a/app/wlib/gtklib/CMakeLists.txt b/app/wlib/gtklib/CMakeLists.txt
new file mode 100644
index 0000000..fabc5d8
--- /dev/null
+++ b/app/wlib/gtklib/CMakeLists.txt
@@ -0,0 +1,37 @@
+FILE(GLOB HEADERS *.h)
+
+SET(SOURCES
+ gtkbitmap.c
+ gtkbutton.c
+ gtkcolor.c
+ filesel.c
+ gtkfont.c
+ gtkhelp.c
+ gtklist.c
+ gtkmenu.c
+ gtkmisc.c
+ gtksimple.c
+ gtksingle.c
+ gtksplash.c
+ gtktext.c
+ gtkwindow.c
+ gtkxpm.c
+ psprint.c
+ wpref.c
+ )
+
+IF(XTRKCAD_USE_GTK_CAIRO)
+ SET(SOURCES ${SOURCES} gtkdraw-cairo.c)
+ELSE(XTRKCAD_USE_GTK_CAIRO)
+ SET(SOURCES ${SOURCES} gtkdraw.c)
+ENDIF(XTRKCAD_USE_GTK_CAIRO)
+
+SET_SOURCE_FILES_PROPERTIES(wpref.c PROPERTIES COMPILE_FLAGS -DEXPORT=)
+
+INCLUDE_DIRECTORIES(${XTrkCAD_BINARY_DIR})
+INCLUDE_DIRECTORIES(${GTK_INCLUDE_DIRS})
+INCLUDE_DIRECTORIES(${GTK_WEBKIT_INCLUDE_DIRS})
+
+ADD_LIBRARY(xtrkcad-wlib ${HEADERS} ${SOURCES})
+TARGET_LINK_LIBRARIES(xtrkcad-wlib ${GTK_LIBRARIES})
+TARGET_LINK_LIBRARIES(xtrkcad-wlib ${GTK_WEBKIT_LIBRARIES})
diff --git a/app/wlib/gtklib/ChangeLog b/app/wlib/gtklib/ChangeLog
new file mode 100644
index 0000000..ef2bd09
--- /dev/null
+++ b/app/wlib/gtklib/ChangeLog
@@ -0,0 +1,250 @@
+Apr 28, 2010
+ FIX: Daniel Spagnol
+ gtkwindow.c, wpref.c: now, wGetAppLibDir can be called before
+ wWinMainCreate is called.
+
+Dec 12, 2009
+ FIX: Martin Fischer
+ gtkint.h, gtkwindow.c: refactoring, remove unused globals and
+ added some comments.
+
+Dec 07, 2010
+ FIX: Martin Fischer / Robert Heller
+ gtkfont.c: use newer Pango functions only after checking for correct
+ version at compile time.
+
+Oct 03, 2009
+ FIX: Daniel Spagnol
+ gtkdraw-cairo.c: linux still crashed due to a cairo context access
+ after its drawable destruction
+
+Oct 03, 2009
+ FIX: Daniel Spagnol
+ gtkbutton.c gtkint.h gtkmenu.c gtkmisc.c: workaround for OSX with
+ GTK-Quartz -> pixmaps are not rendered when using the mask;
+ and replaced gtk_pixmap_new deprecated function with
+ gtk_image_new_from_pixmap
+
+Oct 02, 2009
+ FIX: Daniel Spagnol
+ gtkdraw-cairo.c: linux crashed due to a cairo context access after its
+ drawable destruction
+
+Sep 27, 2009
+ FIX: Daniel Spagnol
+ gtkbitmap.c: image in about dialog box was not being displayed
+
+Sep 26, 2009
+ FIX: Daniel Spagnol
+ gtkfont: deallocate PangoFontDescription using the right function
+
+Sep 25, 2009
+ FIX: Daniel Spagnol
+ gtkbitmap.c: EXC_BAD_ACCESS when displaying about dialog
+
+Sep 25, 2009
+ ENH: Daniel Spagnol
+ gtkdraw-cairo.c gtkdraw.c gtkfont.c gtkint.h gtksimple.c wlib.h:
+ replace the old font select dialog with the GTK standard one, and some
+ code cleanup
+
+Sep 23, 2009
+ FIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkbitmap.c gtkmisc.c: implement wCreateBitmap
+
+Sep 22, 2009
+ FIX: Daniel Spagnol
+ gtkdraw-cairo.c: text in layout and selection were not aligned
+
+Sep 22, 2009
+ FIX: Daniel Spagnol
+ CMakeLists.txt gtkbitmap.c gtkint.h: file created as a workaround to
+ get the source compiled under POSIX and OSX after wCreateBitmap
+ feature
+
+Aug 12, 2009
+ ENH: Matthew Sheets
+ wpref.c: initialize with system default config from /etc
+
+Jul 29, 2009
+ ENH: Martin Fischer <m_fischer@users.sourceforge.net>
+ wpref.c: Create directory .xtrkcad silently
+
+Jun 24, 2009
+ ENH: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkwindow.c gtkmisc.c gtkint.h wpref.c: add option
+ to select configuration file
+
+Version 4.0.3a
+==============
+
+
+Jun 09, 2009
+ FIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkdraw.c: Fix compiler warning
+May 31, 2009
+ FIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtksplash.c: popups during startup are now shown above
+ splash screen
+
+May 31, 2009
+ FIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkmisc.c: fixed problem with some icons
+
+May 30, 2009
+ FIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtklist.c: fixed the 'missing scrollbar' bug - finally!
+
+May 29, 2008
+ FIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkmisc.c: bug fix in wNoticeEx
+
+May 21, 2009
+ ENH: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkhelp.c: better error message
+
+May 15, 2009
+ ENH: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkwindow.c, gtkmisc.c, psprint.c, gtktext.c
+ gtkfilsel.c, gtksingle.c: new message box with icon
+
+Jul 11, 2008
+ ENH: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkwindow.c: closing app can now be canceled by the user
+
+Jul 11, 2008
+ ENH: Steve DeCaux
+ gtdraw-cairo.c: convert strings to UTF8
+
+Jul 01, 2008
+ ENH: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtksplash.c: added #ifdef's for backward compatibility to GTK 2.4
+
+Feb 01. 2008
+ ENH: Martin Fischer <m_fischer@users.sourceforge.net>
+ psprint.c, gtkint.h: added file selector for print to file, made
+ Postscript digit representation independent of current locale
+
+Jan 28, 2008
+ ENH: Mikko Nissinen <mni77@users.sourceforge.net>
+ gtkfilsel.c: Gettext support added.
+
+Jan 28, 2008
+ FIX: Mikko Nissinen <mni77@users.sourceforge.net>
+ gtkwindow.c: Dynamically allocate and form some global translatable
+ strings.
+
+Jan 27, 2008
+ FIX: Mikko Nissinen <mni77@users.sourceforge.net>
+ gtkhelp.c: String XTrkCad changed to XTrackCAD.
+
+Jan, 27, 2008
+ FIX: Mikko Nissinen <mni77@users.sourceforge.net>
+ gtkwindows.c: fixed problem with missing scroll bars
+
+Jan 24,2008
+ IMPROVMENT: Martin Fischer <m_fischer@users.sourceforge.net>
+ wpref.c: increase floting point precision when storing floats in rc
+ file
+
+Jan 22, 2008
+ ENH: Mikko Nissinen <mni77@users.sourceforge.net>
+ gtkwindow.c: wExit(): Free user locale before exit.
+
+Jan 21, 2008
+ ENH: Gettext support added. Modified files:
+ gtkbutton.c
+ gtkfont.c
+ gtkhelp.c
+ gtklist.c
+ psprint.c
+ wpref.c
+
+Jan 20, 2008
+ FIX: Mikko Nissinen <mni77@users.sourceforge.net>
+ gtkdraw.c/gtkdraw-cairo.c: wDrawSetSize(): Return immediately,
+ if given width or height is negative. Negative values crashed
+ the program. This can be seen at least in Add Turnout and Add
+ Structure dialogs by resizing the dialog vertically smaller.
+
+Jan 16, 2008
+ FIX: Mikko Nissinen <mni77@users.sourceforge.net>
+ gtkmisc.c: gtkConvertInput(): If the input string is already
+ UTF-8 encoded, then return it untouched.
+
+Jan 15, 2008
+ IMPROVEMENT: Mikko Nissinen <mni77@users.sourceforge.net>
+ Basic gettext support added.
+ CMakeLists.txt
+
+Nov 30, 2007
+ FIX: Timothy M. Shead
+ gtkfont.c: make sure that font initialization is run first
+
+Nov 29, 2007
+ FIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkhelp.c: an existing help window is now brought to the foreground
+ if the user selects Help
+
+Nov 12, 2007
+ FIX: Mikko Nissinen <mni77@users.sourceforge.net>
+ gtksimple.c: wMessageCreateEx -> Reset the pango font size back
+ to normal before returning the function. All dialogs created after
+ the tip of the day dialog had incorrectly positioned labels,
+ because the width of the text was calculated with the large font.
+
+Nov 12, 2007
+ IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkhelp.c: Converted help system to gtkhtml-2. This allows to
+ us standard HTML files to be used for help documentation.
+
+Oct 28, 2007
+ IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkmenu.c: Help drop-down is no longer right aligned.
+
+Sep 28, 2007
+ IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtksimple.c: wMessageCreate has been extended to
+ wMessageCreateEx. New function allows adding flags. Setting
+ a large or a small font are first uses. Added a compatibility
+ macro wMessageCreate for older code.
+
+Sep 15, 2007
+ IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtksplash.c: added splash window for program startup
+
+Jul 24, 2007
+ IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkdraw.c: added support for wheel mouse
+
+Jun 16, 2007
+ IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
+ wpref.c: added wGetUserHomeDir()
+
+Feb 25, 2007
+ BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ wpref.c: Rephrased error message for lib-directory not found
+
+Feb 23, 2007
+ BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkfont.c: Typo in window title corrected
+
+Version 4.0.1
+=============
+
+Mar, 30th 2006
+ BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkmisc.c: changed wPause to use SYSV signal handling funtions
+
+Mar, 29th 2006
+ BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkmisc.c, gtkbutton.c, gtksimple.c: small changes to help Solaris port
+
+ IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
+ wpref.c: Optimized the checking for directories in wGetAppLibDir and
+ rephrased the error message if initialization files cannot be found
+
+ BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
+ gtkwindow.c: Fixed resizing problems when enlarging dialog boxes
+
+ BUGFIX:
diff --git a/app/wlib/gtklib/dynarr.h b/app/wlib/gtklib/dynarr.h
new file mode 100644
index 0000000..13c0ede
--- /dev/null
+++ b/app/wlib/gtklib/dynarr.h
@@ -0,0 +1,49 @@
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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.
+ */
+
+typedef struct {
+ int cnt;
+ int max;
+ void * ptr;
+ } dynArr_t;
+
+#define DYNARR_APPEND(T,DA,INCR) \
+ { if ((DA).cnt >= (DA).max) { \
+ (DA).max += INCR; \
+ (DA).ptr = realloc( (DA).ptr, (DA).max * sizeof *(T*)NULL ); \
+ if ( (DA).ptr == NULL ) \
+ abort(); \
+ } \
+ (DA).cnt++; }
+#define DYNARR_ADD(T,DA,INCR) DYNARR_APPEND(T,DA,INCR)
+
+#define DYNARR_LAST(T,DA) \
+ (((T*)(DA).ptr)[(DA).cnt-1])
+#define DYNARR_N(T,DA,N) \
+ (((T*)(DA).ptr)[N])
+#define DYNARR_RESET(T,DA) \
+ (DA).cnt=0
+#define DYNARR_SET(T,DA,N) \
+ { if ((DA).max < N) { \
+ (DA).max = N; \
+ (DA).ptr = realloc( (DA).ptr, (DA).max * sizeof *(T*)NULL ); \
+ if ( (DA).ptr == NULL ) \
+ abort(); \
+ } \
+ (DA).cnt = 0; }
diff --git a/app/wlib/gtklib/filesel.c b/app/wlib/gtklib/filesel.c
new file mode 100644
index 0000000..4c737ae
--- /dev/null
+++ b/app/wlib/gtklib/filesel.c
@@ -0,0 +1,174 @@
+/** \file filesel.c
+ * Create and handle file selectors
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <unistd.h>
+#include <string.h>
+
+#include "gtkint.h"
+#include "i18n.h"
+
+struct wFilSel_t {
+ GtkWidget * window;
+ wFilSelCallBack_p action;
+ void * data;
+ int pattCount;
+ GtkFileFilter *filter[ 10 ];
+ wFilSelMode_e mode;
+ int opt;
+ const char * title;
+ wWin_p parent;
+ };
+
+
+/**
+ * Create a new file selector. Only the internal data structures are
+ * set up, no dialog is created.
+ *
+ * \param w IN parent window
+ * \param mode IN ?
+ * \param opt IN ?
+ * \param title IN dialog title
+ * \param pattList IN list of selection patterns
+ * \param action IN callback
+ * \param data IN ?
+ * \return the newly created file selector structure
+ */
+
+struct wFilSel_t * wFilSelCreate(
+ wWin_p w,
+ wFilSelMode_e mode,
+ int opt,
+ const char * title,
+ const char * pattList,
+ wFilSelCallBack_p action,
+ void * data )
+{
+ struct wFilSel_t *fs;
+ int count;
+ char * cp;
+ GtkFileFilter *filter;
+
+ fs = (struct wFilSel_t*)malloc(sizeof *fs);
+ if (!fs)
+ return NULL;
+
+ fs->parent = w;
+ fs->window = 0;
+ fs->mode = mode;
+ fs->opt = opt;
+ fs->title = strdup( title );
+ fs->action = action;
+ fs->data = data;
+
+ if (pattList) {
+ //create filters for the passed filter list
+ cp = strdup(pattList);
+ count = 0;
+ // names and patterns are separated by |
+ cp = strtok( cp, "|" );
+ while ( cp && count < 9 ) {
+ fs->filter[ count ] = gtk_file_filter_new ();
+ gtk_file_filter_set_name ( fs->filter[ count ], cp );
+ cp = strtok( NULL, "|" );
+ gtk_file_filter_add_pattern (fs->filter[ count ], cp );
+ cp = strtok( NULL, "|" );
+ count++;
+ }
+ // finally add the all files pattern
+ fs->filter[ count ] = gtk_file_filter_new ();
+ gtk_file_filter_set_name( fs->filter[ count ], _("All files") );
+ gtk_file_filter_add_pattern( fs->filter[ count ], "*" );
+ fs->pattCount = count++;
+ } else {
+ fs->filter[ 0 ] = NULL;
+ fs->pattCount = 0;
+ }
+ return fs;
+}
+
+/**
+ * Show and handle the file selection dialog.
+ *
+ * \param fs IN file selection
+ * \param dirName IN starting directory
+ * \return always TRUE
+ */
+
+int wFilSelect( struct wFilSel_t * fs, const char * dirName )
+{
+ char name[1024];
+ char *fileName;
+ const char *base;
+ int i;
+
+ char * cp;
+ if (fs->window == NULL) {
+ fs->window = gtk_file_chooser_dialog_new( fs->title,
+ GTK_WINDOW( fs->parent->gtkwin ),
+ (fs->mode == FS_LOAD ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE ),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ (fs->mode == FS_LOAD ? GTK_STOCK_OPEN : GTK_STOCK_SAVE ), GTK_RESPONSE_ACCEPT,
+ NULL );
+ if (fs->window==0) abort();
+ // get confirmation before overwritting an existing file
+ gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(fs->window), TRUE );
+
+ // add the file filters to the dialog box
+ if( fs->pattCount ) {
+ for( i = 0; i <= fs->pattCount; i++ ) {
+ gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( fs->window ), fs->filter[ i ] );
+ }
+ }
+ /** \todo for loading a shortcut folder could be added linking to the example directory */
+
+ }
+ strcpy( name, dirName );
+ cp = name+strlen(name);
+ if (cp[-1] != '/') {
+ *cp++ = '/';
+ *cp = 0;
+ }
+ if( fs->mode == FS_SAVE )
+ gtk_file_chooser_set_current_name( GTK_FILE_CHOOSER(fs->window), name );
+
+ if( gtk_dialog_run( GTK_DIALOG( fs->window )) == GTK_RESPONSE_ACCEPT ) {
+ fileName = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(fs->window) );
+ if (fs->data)
+ strcpy( fs->data, fileName );
+ if (fs->action) {
+ base = strrchr( fileName, '/' );
+ if (base==0) {
+ fprintf(stderr,"no / in %s\n", fileName );
+ return 1;
+ }
+ fs->action( fileName, base+1, fs->data );
+ }
+ }
+ gtk_widget_hide( GTK_WIDGET( fs->window ));
+
+ return 1;
+}
diff --git a/app/wlib/gtklib/gtkbitmap.c b/app/wlib/gtklib/gtkbitmap.c
new file mode 100644
index 0000000..8f85951
--- /dev/null
+++ b/app/wlib/gtklib/gtkbitmap.c
@@ -0,0 +1,85 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtkbitmap.c,v 1.5 2009-09-27 04:28:03 dspagnol Exp $
+ */
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2009 Daniel Spagnol
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "gtkint.h"
+
+
+struct wBitmap_t {
+ WOBJ_COMMON
+};
+
+/**
+ * Create a static control for displaying a bitmap.
+ *
+ * \param parent IN parent window
+ * \param x, y IN position in parent window
+ * \param option IN ignored for now
+ * \param iconP IN icon to use
+ * \return the control
+ */
+
+wControl_p
+wBitmapCreate( wWin_p parent, wPos_t x, wPos_t y, long options, wIcon_p iconP )
+{
+ wBitmap_p bt;
+ GdkPixbuf *pixbuf;
+
+ bt = gtkAlloc( parent, B_BITMAP, x, y, NULL, sizeof *bt, NULL );
+ bt->w = iconP->w;
+ bt->h = iconP->h;
+ bt->option = options;
+
+ /*
+ * Depending on the platform, parent->widget->window might still be null
+ * at this point. The window allocation should be forced before creating
+ * the pixmap.
+ */
+ if ( parent->widget->window == NULL )
+ gtk_widget_realize( parent->widget ); /* force allocation, if pending */
+
+ pixbuf = gdk_pixbuf_new_from_xpm_data( (const char**)iconP->bits );
+ GtkWidget *image = gtk_image_new_from_pixbuf( pixbuf );
+
+ gtk_widget_show( image );
+
+ bt->widget = gtk_fixed_new();
+ gtk_container_add( GTK_CONTAINER(bt->widget), image );
+ gtk_widget_show( bt->widget );
+
+ gtkComputePos( (wControl_p)bt );
+ gtkControlGetSize( (wControl_p)bt );
+ gtk_fixed_put( GTK_FIXED( parent->widget ), bt->widget, bt->realX, bt->realY );
+
+ g_object_unref( pixbuf );
+
+ return( (wControl_p)bt );
+}
+
diff --git a/app/wlib/gtklib/gtkbutton.c b/app/wlib/gtklib/gtkbutton.c
new file mode 100644
index 0000000..7780535
--- /dev/null
+++ b/app/wlib/gtklib/gtkbutton.c
@@ -0,0 +1,461 @@
+/** \file gtkbutton.c
+ * Toolbar button creation and handling
+ *
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtkbutton.c,v 1.8 2009-10-03 04:49:01 dspagnol Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "gtkint.h"
+#include "i18n.h"
+
+#define MIN_BUTTON_WIDTH (80)
+
+/*
+ *****************************************************************************
+ *
+ * Simple Buttons
+ *
+ *****************************************************************************
+ */
+
+struct wButton_t {
+ WOBJ_COMMON
+ GtkLabel * labelG;
+ GtkWidget * imageG;
+ wButtonCallBack_p action;
+ int busy;
+ int recursion;
+ };
+
+
+void wButtonSetBusy( wButton_p bb, int value ) {
+ bb->recursion++;
+ gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(bb->widget), value );
+ bb->recursion--;
+ bb->busy = value;
+}
+
+
+void gtkSetLabel(
+ GtkWidget *widget,
+ long option,
+ const char * labelStr,
+ GtkLabel * * labelG,
+ GtkWidget * * imageG )
+{
+ wIcon_p bm;
+ GdkPixbuf *pixbuf;
+
+ GdkPixmap * pixmap;
+ GdkBitmap * mask;
+
+ GtkWidget * hbox;
+ if (widget == 0) abort();
+ if (labelStr){
+ if (option&BO_ICON) {
+ bm = (wIcon_p)labelStr;
+
+ // for XPM files use the pixbuf functions
+ if( bm->gtkIconType == gtkIcon_pixmap ) {
+ pixbuf = gdk_pixbuf_new_from_xpm_data( (const char**)bm->bits );
+ if (*imageG==NULL) {
+ *imageG = gtk_image_new_from_pixbuf( pixbuf );
+ gtk_container_add( GTK_CONTAINER( widget ), *imageG );
+ gtk_widget_show( *imageG );
+ } else {
+ gtk_image_set_from_pixbuf( GTK_IMAGE(*imageG), pixbuf );
+ }
+ g_object_unref( pixbuf );
+ } else {
+ // otherwise use the conversion to XPM
+ /** \todo { Should use the way via a pixbuf as well } */
+ pixmap = gtkMakeIcon( widget, bm, &mask );
+ if (*imageG==NULL) {
+ *imageG = gtk_image_new_from_pixmap( pixmap, NULL );
+ gtk_widget_show( *imageG );
+ gtk_container_add( GTK_CONTAINER( widget ), *imageG );
+ } else {
+ gtk_image_set_from_pixmap( GTK_IMAGE(*imageG), pixmap, NULL );
+ }
+ gdk_pixmap_unref( pixmap );
+ gdk_bitmap_unref( mask );
+ }
+ } else {
+ if (*labelG==NULL) {
+ *labelG = (GtkLabel*)gtk_label_new( gtkConvertInput(labelStr) );
+ gtk_container_add( GTK_CONTAINER(widget), (GtkWidget*)*labelG );
+ gtk_widget_show( (GtkWidget*)*labelG );
+ } else {
+ gtk_label_set( *labelG, gtkConvertInput(labelStr) );
+ }
+ }
+ }
+}
+
+void wButtonSetLabel( wButton_p bb, const char * labelStr) {
+ gtkSetLabel( bb->widget, bb->option, labelStr, &bb->labelG, &bb->imageG );
+}
+
+
+
+void gtkButtonDoAction(
+ wButton_p bb )
+{
+ if (bb->action)
+ bb->action( bb->data );
+}
+
+
+static void pushButt(
+ GtkWidget *widget,
+ gpointer value )
+{
+ wButton_p b = (wButton_p)value;
+ if (debugWindow >= 2) printf("%s button pushed\n", b->labelStr?b->labelStr:"No label" );
+ if (b->recursion)
+ return;
+ if (b->action)
+ b->action(b->data);
+ if (!b->busy) {
+ b->recursion++;
+ gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(b->widget), FALSE );
+ b->recursion--;
+ }
+}
+
+wButton_p wButtonCreate(
+ wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char * helpStr,
+ const char * labelStr,
+ long option,
+ wPos_t width,
+ wButtonCallBack_p action,
+ void * data )
+{
+ wButton_p b;
+ b = gtkAlloc( parent, B_BUTTON, x, y, labelStr, sizeof *b, data );
+ b->option = option;
+ b->action = action;
+ gtkComputePos( (wControl_p)b );
+
+ b->widget = gtk_toggle_button_new();
+
+ gtk_signal_connect (GTK_OBJECT(b->widget), "clicked",
+ GTK_SIGNAL_FUNC(pushButt), b );
+ if (width > 0)
+ gtk_widget_set_size_request( b->widget, width, -1 );
+ wButtonSetLabel( b, labelStr );
+
+ gtk_fixed_put( GTK_FIXED(parent->widget), b->widget, b->realX, b->realY );
+ if (option & BB_DEFAULT) {
+ GTK_WIDGET_SET_FLAGS( b->widget, GTK_CAN_DEFAULT );
+ gtk_widget_grab_default( b->widget );
+ gtk_window_set_default( GTK_WINDOW(parent->gtkwin), b->widget );
+ }
+ gtkControlGetSize( (wControl_p)b );
+ if (width == 0 && b->w < MIN_BUTTON_WIDTH && (b->option&BO_ICON)==0) {
+ b->w = MIN_BUTTON_WIDTH;
+ gtk_widget_set_size_request( b->widget, b->w, b->h );
+ }
+ gtk_widget_show( b->widget );
+ gtkAddButton( (wControl_p)b );
+ gtkAddHelpString( b->widget, helpStr );
+ return b;
+}
+
+
+/*
+ *****************************************************************************
+ *
+ * Choice Boxes
+ *
+ *****************************************************************************
+ */
+
+struct wChoice_t {
+ WOBJ_COMMON
+ long *valueP;
+ wChoiceCallBack_p action;
+ int recursion;
+ };
+
+
+static long choiceGetValue(
+ wChoice_p bc )
+{
+ GList * child, * children;
+ long value, inx;
+ if (bc->type == B_TOGGLE)
+ value = 0;
+ else
+ value = -1;
+ for ( children=child=gtk_container_children(GTK_CONTAINER(bc->widget)),inx=0; child; child=child->next,inx++ ) {
+ if ( GTK_TOGGLE_BUTTON(child->data)->active ) {
+ if (bc->type == B_TOGGLE) {
+ value |= (1<<inx);
+ } else {
+ value = inx;
+ }
+ }
+ }
+ if ( children )
+ g_list_free( children );
+ return value;
+}
+
+EXPORT void wRadioSetValue(
+ wChoice_p bc, /* Radio box */
+ long value ) /* Value */
+/*
+*/
+{
+ GList * child, * children;
+ long inx;
+ for ( children=child=gtk_container_children(GTK_CONTAINER(bc->widget)),inx=0; child; child=child->next,inx++ ) {
+ if (inx == value) {
+ bc->recursion++;
+ gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(child->data), TRUE );
+ bc->recursion--;
+ }
+ }
+ if ( children )
+ g_list_free( children );
+}
+
+
+EXPORT long wRadioGetValue(
+ wChoice_p bc ) /* Radio box */
+/*
+*/
+{
+ return choiceGetValue(bc);
+}
+
+
+EXPORT void wToggleSetValue(
+ wChoice_p bc, /* Toggle box */
+ long value ) /* Values */
+/*
+*/
+{
+ GList * child, * children;
+ long inx;
+ bc->recursion++;
+ for ( children=child=gtk_container_children(GTK_CONTAINER(bc->widget)),inx=0; child; child=child->next,inx++ ) {
+ gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(child->data), (value&(1<<inx))!=0 );
+ }
+ if ( children )
+ g_list_free( children );
+ bc->recursion--;
+}
+
+
+EXPORT long wToggleGetValue(
+ wChoice_p b ) /* Toggle box */
+/*
+*/
+{
+ return choiceGetValue(b);
+}
+
+
+static int pushChoice(
+ GtkWidget *widget,
+ gpointer b )
+{
+ wChoice_p bc = (wChoice_p)b;
+ long value = choiceGetValue( bc );
+ if (debugWindow >= 2) printf("%s choice pushed = %ld\n", bc->labelStr?bc->labelStr:"No label", value );
+ if ( bc->type == B_RADIO && !(GTK_TOGGLE_BUTTON(widget))->active )
+ return 1;
+ if (bc->recursion)
+ return 1;
+ if (bc->valueP)
+ *bc->valueP = value;
+ if (bc->action)
+ bc->action( value, bc->data);
+ return 1;
+}
+
+
+static void choiceRepaint(
+ wControl_p b )
+{
+ wChoice_p bc = (wChoice_p)b;
+ if ( GTK_WIDGET_VISIBLE( b->widget ) )
+ gtkDrawBox( bc->parent, wBoxBelow, bc->realX-1, bc->realY-1, bc->w+1, bc->h+1 );
+}
+
+
+EXPORT wChoice_p wRadioCreate(
+ wWin_p parent, /* Parent window */
+ wPos_t x, /* X-position */
+ wPos_t y, /* Y-position */
+ const char * helpStr, /* Help string */
+ const char * labelStr, /* Label */
+ long option, /* Options */
+ const char **labels, /* Labels */
+ long *valueP, /* Selected value */
+ wChoiceCallBack_p action, /* Callback */
+ void *data ) /* Context */
+/*
+*/
+{
+ wChoice_p b;
+ const char ** label;
+ GtkWidget *butt0=NULL, *butt;
+
+ if ((option & BC_NOBORDER)==0) {
+ if (x>=0)
+ x++;
+ else
+ x--;
+ if (y>=0)
+ y++;
+ else
+ y--;
+ }
+ b = gtkAlloc( parent, B_RADIO, x, y, labelStr, sizeof *b, data );
+ b->option = option;
+ b->action = action;
+ b->valueP = valueP;
+ gtkComputePos( (wControl_p)b );
+
+ if (option&BC_HORZ)
+ b->widget = gtk_hbox_new( FALSE, 0 );
+ else
+ b->widget = gtk_vbox_new( FALSE, 0 );
+ if (b->widget == 0) abort();
+ for ( label=labels; *label; label++ ) {
+ butt = gtk_radio_button_new_with_label(
+ butt0?gtk_radio_button_group(GTK_RADIO_BUTTON(butt0)):NULL, _(*label) );
+ if (butt0==NULL)
+ butt0 = butt;
+ gtk_box_pack_start( GTK_BOX(b->widget), butt, TRUE, TRUE, 0 );
+ gtk_widget_show( butt );
+ gtk_signal_connect (GTK_OBJECT(butt), "toggled",
+ GTK_SIGNAL_FUNC( pushChoice ), b );
+ gtkAddHelpString( butt, helpStr );
+ }
+ if (option & BB_DEFAULT) {
+ GTK_WIDGET_SET_FLAGS( b->widget, GTK_CAN_DEFAULT );
+ gtk_widget_grab_default( b->widget );
+ /*gtk_window_set_default( GTK_WINDOW(parent->gtkwin), b->widget );*/
+ }
+ if (valueP)
+ wRadioSetValue( b, *valueP );
+
+ if ((option & BC_NOBORDER)==0) {
+ if (parent->gc == NULL) {
+ parent->gc = gdk_gc_new( parent->gtkwin->window );
+ gdk_gc_copy( parent->gc, parent->gtkwin->style->base_gc[GTK_STATE_NORMAL] );
+ parent->gc_linewidth = 0;
+ gdk_gc_set_line_attributes( parent->gc, parent->gc_linewidth, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+ b->repaintProc = choiceRepaint;
+ b->w += 2;
+ b->h += 2;
+ }
+
+ gtk_fixed_put( GTK_FIXED(parent->widget), b->widget, b->realX, b->realY );
+ gtkControlGetSize( (wControl_p)b );
+ if (labelStr)
+ b->labelW = gtkAddLabel( (wControl_p)b, labelStr );
+ gtk_widget_show( b->widget );
+ gtkAddButton( (wControl_p)b );
+ return b;
+}
+
+wChoice_p wToggleCreate(
+ wWin_p parent, /* Parent window */
+ wPos_t x, /* X-position */
+ wPos_t y, /* Y-position */
+ const char * helpStr, /* Help string */
+ const char * labelStr, /* Label */
+ long option, /* Options */
+ const char **labels, /* Labels */
+ long *valueP, /* Selected value */
+ wChoiceCallBack_p action, /* Callback */
+ void *data ) /* Context */
+/*
+*/
+{
+ wChoice_p b;
+ const char ** label;
+ GtkWidget *butt;
+
+ if ((option & BC_NOBORDER)==0) {
+ if (x>=0)
+ x++;
+ else
+ x--;
+ if (y>=0)
+ y++;
+ else
+ y--;
+ }
+ b = gtkAlloc( parent, B_TOGGLE, x, y, labelStr, sizeof *b, data );
+ b->option = option;
+ b->action = action;
+ gtkComputePos( (wControl_p)b );
+
+ if (option&BC_HORZ)
+ b->widget = gtk_hbox_new( FALSE, 0 );
+ else
+ b->widget = gtk_vbox_new( FALSE, 0 );
+ if (b->widget == 0) abort();
+ for ( label=labels; *label; label++ ) {
+ butt = gtk_check_button_new_with_label(_(*label));
+ gtk_box_pack_start( GTK_BOX(b->widget), butt, TRUE, TRUE, 0 );
+ gtk_widget_show( butt );
+ gtk_signal_connect (GTK_OBJECT(butt), "toggled",
+ GTK_SIGNAL_FUNC( pushChoice ), b );
+ gtkAddHelpString( butt, helpStr );
+ }
+ if (valueP)
+ wToggleSetValue( b, *valueP );
+
+ if ((option & BC_NOBORDER)==0) {
+ if (parent->gc == NULL) {
+ parent->gc = gdk_gc_new( parent->gtkwin->window );
+ gdk_gc_copy( parent->gc, parent->gtkwin->style->base_gc[GTK_STATE_NORMAL] );
+ parent->gc_linewidth = 0;
+ gdk_gc_set_line_attributes( parent->gc, parent->gc_linewidth, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+ b->repaintProc = choiceRepaint;
+ b->w += 2;
+ b->h += 2;
+ }
+
+ gtk_fixed_put( GTK_FIXED(parent->widget), b->widget, b->realX, b->realY );
+ gtkControlGetSize( (wControl_p)b );
+ if (labelStr)
+ b->labelW = gtkAddLabel( (wControl_p)b, labelStr );
+ gtk_widget_show( b->widget );
+ gtkAddButton( (wControl_p)b );
+ return b;
+}
diff --git a/app/wlib/gtklib/gtkcolor.c b/app/wlib/gtklib/gtkcolor.c
new file mode 100644
index 0000000..3856f2a
--- /dev/null
+++ b/app/wlib/gtklib/gtkcolor.c
@@ -0,0 +1,476 @@
+/** \file gtkcolor.c
+ * code for the color selection dialog and color button
+ *
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtkcolor.c,v 1.3 2007-11-24 19:48:21 tshead Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "gtkint.h"
+
+#include "square10.bmp"
+
+EXPORT wDrawColor wDrawColorWhite;
+EXPORT wDrawColor wDrawColorBlack;
+
+#define RGB(R,G,B) ( ((long)(255&0xFF))<<24 | (((long)((R)&0xFF))<<16) | (((long)((G)&0xFF))<<8) | ((long)((B)&0xFF)) )
+
+#define MAX_COLOR_DISTANCE (3)
+
+typedef struct {
+ unsigned char red;
+ unsigned char green;
+ unsigned char blue;
+ GdkColor normalColor;
+ GdkColor invertColor;
+ long rgb;
+ int colorChar;
+ } colorMap_t;
+static GArray *colorMap_garray = NULL; // Change to use glib array
+
+static colorMap_t colorMap[] = {
+ { 255, 255, 255 }, /* White */
+ { 0, 0, 0 }, /* Black */
+ { 255, 0, 0 }, /* Red */
+ { 0, 255, 0 }, /* Green */
+ { 0, 0, 255 }, /* Blue */
+ { 255, 255, 0 }, /* Yellow */
+ { 255, 0, 255 }, /* Purple */
+ { 0, 255, 255 }, /* Aqua */
+ { 128, 0, 0 }, /* Dk. Red */
+ { 0, 128, 0 }, /* Dk. Green */
+ { 0, 0, 128 }, /* Dk. Blue */
+ { 128, 128, 0 }, /* Dk. Yellow */
+ { 128, 0, 128 }, /* Dk. Purple */
+ { 0, 128, 128 }, /* Dk. Aqua */
+ { 65, 105, 225 }, /* Royal Blue */
+ { 0, 191, 255 }, /* DeepSkyBlue */
+ { 125, 206, 250 }, /* LightSkyBlue */
+ { 70, 130, 180 }, /* Steel Blue */
+ { 176, 224, 230 }, /* Powder Blue */
+ { 127, 255, 212 }, /* Aquamarine */
+ { 46, 139, 87 }, /* SeaGreen */
+ { 152, 251, 152 }, /* PaleGreen */
+ { 124, 252, 0 }, /* LawnGreen */
+ { 50, 205, 50 }, /* LimeGreen */
+ { 34, 139, 34 }, /* ForestGreen */
+ { 255, 215, 0 }, /* Gold */
+ { 188, 143, 143 }, /* RosyBrown */
+ { 139, 69, 19 }, /* SaddleBrown */
+ { 245, 245, 220 }, /* Beige */
+ { 210, 180, 140 }, /* Tan */
+ { 210, 105, 30 }, /* Chocolate */
+ { 165, 42, 42 }, /* Brown */
+ { 255, 165, 0 }, /* Orange */
+ { 255, 127, 80 }, /* Coral */
+ { 255, 99, 71 }, /* Tomato */
+ { 255, 105, 180 }, /* HotPink */
+ { 255, 192, 203 }, /* Pink */
+ { 176, 48, 96 }, /* Maroon */
+ { 238, 130, 238 }, /* Violet */
+ { 160, 32, 240 }, /* Purple */
+ { 16, 16, 16 }, /* Gray */
+ { 32, 32, 32 }, /* Gray */
+ { 48, 48, 48 }, /* Gray */
+ { 64, 64, 64 }, /* Gray */
+ { 80, 80, 80 }, /* Gray */
+ { 96, 96, 96 }, /* Gray */
+ { 112, 112, 122 }, /* Gray */
+ { 128, 128, 128 }, /* Gray */
+ { 144, 144, 144 }, /* Gray */
+ { 160, 160, 160 }, /* Gray */
+ { 176, 176, 176 }, /* Gray */
+ { 192, 192, 192 }, /* Gray */
+ { 208, 208, 208 }, /* Gray */
+ { 224, 224, 224 }, /* Gray */
+ { 240, 240, 240 }, /* Gray */
+ { 0, 0, 0 } /* BlackPixel */
+ };
+
+#define NUM_GRAYS (16)
+
+static GdkColormap * gtkColorMap;
+
+static char lastColorChar = '!';
+
+/*****************************************************************************
+ *
+ *
+ *
+ */
+
+
+EXPORT wDrawColor wDrawColorGray(
+ int percent )
+{
+ int n;
+ long rgb;
+ n = (percent * (NUM_GRAYS+1)) / 100;
+ if ( n <= 0 )
+ return wDrawColorBlack;
+ else if ( n > NUM_GRAYS )
+ return wDrawColorWhite;
+ else {
+ n = (n*256)/NUM_GRAYS;
+ rgb = RGB( n, n, n );
+ return wDrawFindColor( rgb );
+ }
+}
+
+
+void gtkGetColorMap( void )
+{
+ if (gtkColorMap)
+ return;
+ gtkColorMap = gtk_widget_get_colormap( gtkMainW->widget );
+ return;
+}
+
+void init_colorMapValue( colorMap_t * t) {
+
+ t->rgb = RGB( t->red, t->green, t->blue );
+ t->normalColor.red = t->red*65535/255;
+ t->normalColor.green = t->green*65535/255;
+ t->normalColor.blue = t->blue*65535/255;
+ gdk_color_alloc( gtkColorMap, &t->normalColor );
+ t->invertColor = t->normalColor;
+ t->invertColor.pixel ^= g_array_index(colorMap_garray, colorMap_t, wDrawColorWhite).normalColor.pixel;
+ t->colorChar = lastColorChar++;
+ if (lastColorChar >= 0x7F)
+ lastColorChar = '!'+1;
+ else if (lastColorChar == '"')
+ lastColorChar++;
+
+}
+
+
+void init_colorMap( void )
+{
+ colorMap_garray = g_array_sized_new(TRUE, TRUE, sizeof(colorMap_t), sizeof(colorMap)/sizeof(colorMap_t));
+ g_array_append_vals(colorMap_garray, &colorMap, sizeof(colorMap)/sizeof(colorMap_t));
+
+ int gint;
+
+ for(gint=0; gint<colorMap_garray->len; gint++) {
+ init_colorMapValue(&g_array_index(colorMap_garray, colorMap_t, gint));
+ }
+}
+
+
+EXPORT wDrawColor wDrawFindColor(
+ long rgb0 )
+{
+ wDrawColor cc;
+ int r0, g0, b0;
+ int d0, d1;
+ long rgb1;
+ colorMap_t * cm_p;
+
+ gtkGetColorMap();
+
+ cc = wDrawColorBlack;
+ r0 = (int)(rgb0>>16)&0xFF;
+ g0 = (int)(rgb0>>8)&0xFF;
+ b0 = (int)(rgb0)&0xFF;
+ d0 = 256*3;
+
+ // Initialize garray if needed
+ if (colorMap_garray == NULL) {
+ init_colorMap();
+ }
+
+ int gint;
+
+ // Iterate over entire garray
+ for (gint=0; gint<colorMap_garray->len; gint++) {
+ cm_p = &g_array_index(colorMap_garray, colorMap_t, gint);
+ rgb1 = cm_p->rgb;
+ d1 = abs(r0-cm_p->red) + abs(g0-cm_p->green) + abs(b0-cm_p->blue);
+ if (d1 == 0)
+ return gint;
+ if (d1 < d0) {
+ d0 = d1;
+ cc = gint;
+ }
+ }
+ if (d0 <= MAX_COLOR_DISTANCE) {
+ return cc;
+ }
+ // No good value - so add one
+ colorMap_t tempMapValue;
+ //DYNARR_APPEND( colorMap_t, colorMap_da, 10 );
+ tempMapValue.red = r0;
+ tempMapValue.green = g0;
+ tempMapValue.blue = b0;
+ init_colorMapValue(&tempMapValue);
+ g_array_append_val(colorMap_garray,tempMapValue);
+ return gint;
+}
+
+
+EXPORT long wDrawGetRGB(
+ wDrawColor color )
+{
+ gtkGetColorMap();
+
+ if(colorMap_garray == NULL)
+ init_colorMap();
+
+ if (color < 0 || color > colorMap_garray->len)
+ abort();
+ colorMap_t * colorMap_e;
+ colorMap_e = &g_array_index(colorMap_garray, colorMap_t, color);
+ return colorMap_e->rgb;
+}
+
+
+EXPORT GdkColor* gtkGetColor(
+ wDrawColor color,
+ wBool_t normal )
+{
+ gtkGetColorMap();
+
+ if(colorMap_garray == NULL)
+ init_colorMap();
+
+ if (color < 0 || color > colorMap_garray->len)
+ abort();
+ colorMap_t * colorMap_e;
+ colorMap_e = &g_array_index(colorMap_garray, colorMap_t, color);
+
+ if ( normal )
+ return &colorMap_e->normalColor;
+ else
+ return &colorMap_e->invertColor;
+}
+
+
+EXPORT int gtkGetColorChar(
+ wDrawColor color )
+{
+ /*gtkGetColorMap();*/
+ if(colorMap_garray == NULL)
+ init_colorMap();
+
+ if (color < 0 || color > colorMap_garray->len)
+ abort();
+ colorMap_t * colorMap_e;
+ colorMap_e = &g_array_index(colorMap_garray, colorMap_t, color);
+ return colorMap_e->colorChar;
+}
+
+
+EXPORT int gtkMapPixel(
+ long pixel )
+{
+ colorMap_t * mapValue;
+ int gint;
+
+ if(colorMap_garray == NULL)
+ init_colorMap();
+
+ for (gint=0; gint<colorMap_garray->len; gint++ ) {
+ mapValue = &g_array_index(colorMap_garray, colorMap_t, gint);
+ if ( mapValue->normalColor.pixel == pixel ) {
+ return mapValue->colorChar;
+ }
+ }
+ mapValue = &g_array_index(colorMap_garray, colorMap_t, wDrawColorBlack);
+ return mapValue->colorChar;
+}
+
+
+/*
+ *****************************************************************************
+ *
+ * Color Selection Dialog
+ *
+ *****************************************************************************
+ */
+
+
+static int colorSelectValid;
+static int colorSelectOk(
+ GtkWidget * widget,
+ GtkWidget * * window )
+{
+ gtkDoModal( NULL, FALSE );
+ gtk_widget_hide( GTK_WIDGET(*window) );
+ colorSelectValid = TRUE;
+ return FALSE;
+}
+
+
+static int colorSelectCancel(
+ GtkWidget * widget,
+ GtkWidget * * window )
+{
+ gtkDoModal( NULL, FALSE );
+ gtk_widget_hide( GTK_WIDGET(*window) );
+ colorSelectValid = FALSE;
+ if (widget == *window)
+ /* Called by destroy event, window is gone */
+ *window = NULL;
+ return FALSE;
+}
+
+
+EXPORT wBool_t wColorSelect(
+ const char * title,
+ wDrawColor * color )
+{
+ static GtkWidget * colorSelectD = NULL;
+ long rgb;
+ gdouble colors[4]; // Remember opacity!
+
+ if (colorSelectD == NULL) {
+ colorSelectD = gtk_color_selection_dialog_new( title );
+ gtk_signal_connect( GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(colorSelectD)->ok_button), "clicked", (GtkSignalFunc)colorSelectOk, (gpointer)&colorSelectD );
+ gtk_signal_connect( GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(colorSelectD)->cancel_button), "clicked", (GtkSignalFunc)colorSelectCancel, (gpointer)&colorSelectD );
+ gtk_signal_connect( GTK_OBJECT(colorSelectD), "destroy", (GtkSignalFunc)colorSelectCancel, (gpointer)&colorSelectD );
+ } else {
+ gtk_window_set_title( GTK_WINDOW(colorSelectD), title );
+ }
+
+ colorMap_t * colorMap_e;
+
+ if (!colorMap_garray) {
+ init_colorMap();
+ }
+
+ colorMap_e = &g_array_index(colorMap_garray, colorMap_t, *color);
+ colors[0] = colorMap_e->red/255.0;
+ colors[1] = colorMap_e->green/255.0;
+ colors[2] = colorMap_e->blue/255.0;
+ colors[3] = 1.0; // Override to Fully opaque
+ gtk_color_selection_set_color( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorSelectD)->colorsel), colors );
+ gtk_widget_show( colorSelectD );
+ gtkDoModal( NULL, TRUE );
+ if (colorSelectValid) {
+ gtk_color_selection_get_color( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorSelectD)->colorsel), colors );
+ rgb = RGB( (int)(colors[0]*255), (int)(colors[1]*255), (int)(colors[2]*255) );
+ * color = wDrawFindColor( rgb );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ *****************************************************************************
+ *
+ * Color Selection Button
+ *
+ *****************************************************************************
+ */
+
+typedef struct {
+ wDrawColor * valueP;
+ wColorSelectButtonCallBack_p action;
+ const char * labelStr;
+ void * data;
+ wDrawColor color;
+ wButton_p button;
+ } colorData_t;
+
+static void doColorButton(
+ void * data )
+{
+ colorData_t * cd = (colorData_t *)data;
+ wDrawColor newColor;
+
+ newColor = cd->color;
+ if (wColorSelect( cd->labelStr, &newColor )) {
+ cd->color = newColor;
+ wColorSelectButtonSetColor( cd->button, newColor );
+ if (cd->valueP)
+ *(cd->valueP) = newColor;
+ if (cd->action)
+ cd->action( cd->data, newColor );
+ }
+}
+
+
+void wColorSelectButtonSetColor(
+ wButton_p bb,
+ wDrawColor color )
+{
+ wIcon_p bm;
+ bm = wIconCreateBitMap( square10_width, square10_height, square10_bits, color );
+ wButtonSetLabel( bb, (const char*)bm );
+ ((colorData_t*)((wControl_p)bb)->data)->color = color;
+}
+
+
+wDrawColor wColorSelectButtonGetColor(
+ wButton_p bb )
+{
+ return ((colorData_t*)((wControl_p)bb)->data)->color;
+}
+
+/** Create the button showing the current paint color and starting the color selection dialog.
+ * \param IN parent parent window
+ * \param IN x x coordinate
+ * \param IN Y y coordinate
+ * \param IN helpStr balloon help string
+ * \param IN labelStr Button label ???
+ * \param IN option
+ * \param IN width
+ * \param IN valueP Current color ???
+ * \param IN action Button callback procedure
+ * \param IN data ???
+ * \return bb handle for created button
+ */
+
+wButton_p wColorSelectButtonCreate(
+ wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char * helpStr,
+ const char * labelStr,
+ long option,
+ wPos_t width,
+ wDrawColor *valueP,
+ wColorSelectButtonCallBack_p action,
+ void * data )
+{
+ wButton_p bb;
+ wIcon_p bm;
+ colorData_t * cd;
+ bm = wIconCreateBitMap( square10_width, square10_height, square10_bits, (valueP?*valueP:0) );
+ cd = malloc( sizeof( colorData_t ));
+ cd->valueP = valueP;
+ cd->action = action;
+ cd->data = data;
+ cd->labelStr = labelStr;
+ cd->color = (valueP?*valueP:0);
+ bb = wButtonCreate( parent, x, y, helpStr, (const char*)bm, option|BO_ICON, width, doColorButton, cd );
+ cd->button = bb;
+ if (labelStr)
+ ((wControl_p)bb)->labelW = gtkAddLabel( (wControl_p)bb, labelStr );
+ return bb;
+}
diff --git a/app/wlib/gtklib/gtkdraw-cairo.c b/app/wlib/gtklib/gtkdraw-cairo.c
new file mode 100644
index 0000000..e9b6447
--- /dev/null
+++ b/app/wlib/gtklib/gtkdraw-cairo.c
@@ -0,0 +1,1212 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtkdraw-cairo.c,v 1.11 2009-10-03 17:34:37 dspagnol Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+
+#include "gtkint.h"
+#include "gdk/gdkkeysyms.h"
+
+
+#define CENTERMARK_LENGTH (6)
+
+static long drawVerbose = 0;
+
+struct wDrawBitMap_t {
+ int w;
+ int h;
+ int x;
+ int y;
+ const char * bits;
+ GdkPixmap * pixmap;
+ GdkBitmap * mask;
+ };
+
+struct wDraw_t {
+ WOBJ_COMMON
+ void * context;
+ wDrawActionCallBack_p action;
+ wDrawRedrawCallBack_p redraw;
+
+ GdkPixmap * pixmap;
+ GdkPixmap * pixmapBackup;
+
+ double dpi;
+
+ GdkGC * gc;
+ wDrawWidth lineWidth;
+ wDrawOpts opts;
+ wPos_t maxW;
+ wPos_t maxH;
+ unsigned long lastColor;
+ wBool_t lastColorInverted;
+ const char * helpStr;
+
+ wPos_t lastX;
+ wPos_t lastY;
+
+ wBool_t delayUpdate;
+ };
+
+struct wDraw_t psPrint_d;
+
+/*****************************************************************************
+ *
+ * MACROS
+ *
+ */
+
+#define LBORDER (22)
+#define RBORDER (6)
+#define TBORDER (6)
+#define BBORDER (20)
+
+#define INMAPX(D,X) (X)
+#define INMAPY(D,Y) (((D)->h-1) - (Y))
+#define OUTMAPX(D,X) (X)
+#define OUTMAPY(D,Y) (((D)->h-1) - (Y))
+
+
+/*******************************************************************************
+ *
+ * Basic Drawing Functions
+ *
+*******************************************************************************/
+
+
+
+static GdkGC * selectGC(
+ wDraw_p bd,
+ wDrawWidth width,
+ wDrawLineType_e lineType,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ if(width < 0.0)
+ {
+ width = - width;
+ }
+
+ if(opts & wDrawOptTemp)
+ {
+ if(bd->lastColor != color || !bd->lastColorInverted)
+ {
+ gdk_gc_set_foreground( bd->gc, gtkGetColor(color,FALSE) );
+ bd->lastColor = color;
+ bd->lastColorInverted = TRUE;
+ }
+ gdk_gc_set_function( bd->gc, GDK_XOR );
+ gdk_gc_set_line_attributes( bd->gc, width, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+ else
+ {
+ if(bd->lastColor != color || bd->lastColorInverted)
+ {
+ gdk_gc_set_foreground( bd->gc, gtkGetColor(color,TRUE) );
+ bd->lastColor = color;
+ bd->lastColorInverted = FALSE;
+ }
+ gdk_gc_set_function( bd->gc, GDK_COPY );
+ if (lineType==wDrawLineDash)
+ {
+ gdk_gc_set_line_attributes( bd->gc, width, GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+ else
+ {
+ gdk_gc_set_line_attributes( bd->gc, width, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+
+ gdk_gc_set_function(bd->gc, GDK_NOOP);
+ }
+ return bd->gc;
+}
+
+static cairo_t* gtkDrawCreateCairoContext(
+ wDraw_p bd,
+ wDrawWidth width,
+ wDrawLineType_e lineType,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ cairo_t* cairo = gdk_cairo_create(bd->pixmap);
+
+ width = width ? abs(width) : 1;
+ cairo_set_line_width(cairo, width);
+
+ cairo_set_line_cap(cairo, CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_join(cairo, CAIRO_LINE_JOIN_MITER);
+
+ switch(lineType)
+ {
+ case wDrawLineSolid:
+ {
+ cairo_set_dash(cairo, 0, 0, 0);
+ break;
+ }
+ case wDrawLineDash:
+ {
+ double dashes[] = { 5, 5 };
+ cairo_set_dash(cairo, dashes, 2, 0);
+ break;
+ }
+ }
+
+ if(opts & wDrawOptTemp)
+ {
+ cairo_set_source_rgba(cairo, 0, 0, 0, 0);
+ }
+ else
+ {
+ GdkColor* const gcolor = gtkGetColor(color, TRUE);
+ cairo_set_source_rgb(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0);
+
+ cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
+ }
+
+ return cairo;
+}
+
+static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {
+ cairo_destroy(cairo);
+}
+
+EXPORT void wDrawDelayUpdate(
+ wDraw_p bd,
+ wBool_t delay )
+{
+ GdkRectangle update_rect;
+
+ if ( (!delay) && bd->delayUpdate ) {
+ update_rect.x = 0;
+ update_rect.y = 0;
+ update_rect.width = bd->w;
+ update_rect.height = bd->h;
+ gtk_widget_draw( bd->widget, &update_rect );
+ }
+ bd->delayUpdate = delay;
+}
+
+
+EXPORT void wDrawLine(
+ wDraw_p bd,
+ wPos_t x0, wPos_t y0,
+ wPos_t x1, wPos_t y1,
+ wDrawWidth width,
+ wDrawLineType_e lineType,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ GdkGC * gc;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ psPrintLine( x0, y0, x1, y1, width, lineType, color, opts );
+ return;
+ }
+ gc = selectGC( bd, width, lineType, color, opts );
+ x0 = INMAPX(bd,x0);
+ y0 = INMAPY(bd,y0);
+ x1 = INMAPX(bd,x1);
+ y1 = INMAPY(bd,y1);
+ gdk_draw_line( bd->pixmap, gc, x0, y0, x1, y1 );
+
+ cairo_t* cairo = gtkDrawCreateCairoContext(bd, width, lineType, color, opts);
+ cairo_move_to(cairo, x0 + 0.5, y0 + 0.5);
+ cairo_line_to(cairo, x1 + 0.5, y1 + 0.5);
+ cairo_stroke(cairo);
+ gtkDrawDestroyCairoContext(cairo);
+
+ if ( bd->delayUpdate || bd->widget == NULL ) return;
+ width /= 2;
+ if (x0 < x1) {
+ update_rect.x = x0-1-width;
+ update_rect.width = x1-x0+2+width+width;
+ } else {
+ update_rect.x = x1-1-width;
+ update_rect.width = x0-x1+2+width+width;
+ }
+ if (y0 < y1) {
+ update_rect.y = y0-1-width;
+ update_rect.height = y1-y0+2+width+width;
+ } else {
+ update_rect.y = y1-1-width;
+ update_rect.height = y0-y1+2+width+width;
+ }
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+/**
+ * Draw an arc around a specified center
+ *
+ * \param bd IN ?
+ * \param x0, y0 IN center of arc
+ * \param r IN radius
+ * \param angle0, angle1 IN start and end angle
+ * \param drawCenter draw marking for center
+ * \param width line width
+ * \param lineType
+ * \param color color
+ * \param opts ?
+ */
+
+
+EXPORT void wDrawArc(
+ wDraw_p bd,
+ wPos_t x0, wPos_t y0,
+ wPos_t r,
+ wAngle_t angle0,
+ wAngle_t angle1,
+ int drawCenter,
+ wDrawWidth width,
+ wDrawLineType_e lineType,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ int x, y, w, h;
+ GdkGC * gc;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ psPrintArc( x0, y0, r, angle0, angle1, drawCenter, width, lineType, color, opts );
+ return;
+ }
+ gc = selectGC( bd, width, lineType, color, opts );
+ if (r < 6.0/75.0) return;
+ x = INMAPX(bd,x0-r);
+ y = INMAPY(bd,y0+r);
+ w = 2*r;
+ h = 2*r;
+
+ // remove the old arc
+ gdk_draw_arc( bd->pixmap, gc, FALSE, x, y, w, h, (int)((-angle0 + 90)*64.0), (int)(-angle1*64.0) );
+
+ // and its center point
+ if (drawCenter) {
+ x = INMAPX(bd,x0);
+ y = INMAPY(bd,y0);
+ gdk_draw_line( bd->pixmap, gc, x - ( CENTERMARK_LENGTH/2), y, x + ( CENTERMARK_LENGTH/2), y );
+ gdk_draw_line( bd->pixmap, gc, x, y - ( CENTERMARK_LENGTH/2), x, y + ( CENTERMARK_LENGTH/2));
+ }
+
+ // now create the new arc
+ cairo_t* cairo = gtkDrawCreateCairoContext(bd, width, lineType, color, opts);
+ cairo_new_path(cairo);
+
+ // its center point marker
+ if(drawCenter)
+ {
+ // draw a small crosshair to mark the center of the curve
+ cairo_move_to(cairo, INMAPX(bd, x0 - (CENTERMARK_LENGTH / 2)), INMAPY(bd, y0 ));
+ cairo_line_to(cairo, INMAPX(bd, x0 + (CENTERMARK_LENGTH / 2)), INMAPY(bd, y0 ));
+ cairo_move_to(cairo, INMAPX(bd, x0), INMAPY(bd, y0 - (CENTERMARK_LENGTH / 2 )));
+ cairo_line_to(cairo, INMAPX(bd, x0) , INMAPY(bd, y0 + (CENTERMARK_LENGTH / 2)));
+ cairo_new_sub_path( cairo );
+ }
+
+ // draw the curve itself
+ cairo_arc_negative(cairo, INMAPX(bd, x0), INMAPY(bd, y0), r, (angle0 - 90 + angle1) * (M_PI / 180.0), (angle0 - 90) * (M_PI / 180.0));
+ cairo_stroke(cairo);
+
+ gtkDrawDestroyCairoContext(cairo);
+
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ width /= 2;
+ update_rect.x = x-1-width;
+ update_rect.y = y-1-width;
+ update_rect.width = w+2+width+width;
+ update_rect.height = h+2+width+width;
+ gtk_widget_draw( bd->widget, &update_rect );
+
+}
+
+EXPORT void wDrawPoint(
+ wDraw_p bd,
+ wPos_t x0, wPos_t y0,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ GdkGC * gc;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ /*psPrintArc( x0, y0, r, angle0, angle1, drawCenter, width, lineType, color, opts );*/
+ return;
+ }
+ gc = selectGC( bd, 0, wDrawLineSolid, color, opts );
+ gdk_draw_point( bd->pixmap, gc, INMAPX(bd, x0 ), INMAPY(bd, y0 ) );
+
+ cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, color, opts);
+ cairo_new_path(cairo);
+ cairo_arc(cairo, INMAPX(bd, x0), INMAPY(bd, y0), 0.75, 0, 2 * M_PI);
+ cairo_stroke(cairo);
+ gtkDrawDestroyCairoContext(cairo);
+
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ update_rect.x = INMAPX(bd, x0 )-1;
+ update_rect.y = INMAPY(bd, y0 )-1;
+ update_rect.width = 2;
+ update_rect.height = 2;
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+/*******************************************************************************
+ *
+ * Strings
+ *
+ ******************************************************************************/
+
+EXPORT void wDrawString(
+ wDraw_p bd,
+ wPos_t x, wPos_t y,
+ wAngle_t a,
+ const char * s,
+ wFont_p fp,
+ wFontSize_t fs,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ PangoLayout *layout;
+ GdkRectangle update_rect;
+ int w;
+ int h;
+ gint ascent;
+ gint descent;
+ double angle = -M_PI * a / 180.0;
+
+ if ( bd == &psPrint_d ) {
+ psPrintString( x, y, a, (char *) s, fp, fs, color, opts );
+ return;
+ }
+
+ x = INMAPX(bd,x);
+ y = INMAPY(bd,y);
+
+ /* draw text */
+ cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, color, opts);
+
+ cairo_save( cairo );
+ cairo_translate( cairo, x, y );
+ cairo_rotate( cairo, angle );
+
+ layout = gtkFontCreatePangoLayout(bd->widget, cairo, fp, fs, s,
+ (int *) &w, (int *) &h,
+ (int *) &ascent, (int *) &descent);
+
+ /* cairo does not support the old method of text removal by overwrite; force always write here and
+ refresh on cancel event */
+ GdkColor* const gcolor = gtkGetColor(color, TRUE);
+ cairo_set_source_rgb(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0);
+
+ cairo_move_to( cairo, 0, -ascent );
+
+ pango_cairo_show_layout(cairo, layout);
+ gtkFontDestroyPangoLayout(layout);
+ cairo_restore( cairo );
+ gtkDrawDestroyCairoContext(cairo);
+
+ if (bd->delayUpdate || bd->widget == NULL) return;
+
+ /* recalculate the area to be updated
+ * for simplicity sake I added plain text height ascent and descent,
+ * mathematically correct would be to use the trigonometrical functions as well
+ */
+ update_rect.x = (gint) x - ascent - descent - 1;
+ update_rect.y = (gint) y - (gint) ascent - 1;
+ update_rect.width = (gint) (w * cos( angle ) + 2 + ascent + descent);
+ update_rect.height = (gint) (w * sin( angle ) + ascent + descent + 2 );
+ gtk_widget_draw(bd->widget, &update_rect);
+}
+
+EXPORT void wDrawGetTextSize(
+ wPos_t *w,
+ wPos_t *h,
+ wPos_t *d,
+ wDraw_p bd,
+ const char * s,
+ wFont_p fp,
+ wFontSize_t fs )
+{
+ int textWidth;
+ int textHeight;
+ int ascent;
+ int descent;
+
+ *w = 0;
+ *h = 0;
+
+ gtkFontDestroyPangoLayout(
+ gtkFontCreatePangoLayout(bd->widget, NULL, fp, fs, s,
+ &textWidth, (int *) &textHeight,
+ (int *) &ascent, (int *) &descent));
+
+ *w = (wPos_t) textWidth;
+ *h = (wPos_t) ascent;
+ *d = (wPos_t) descent;
+
+ if (debugWindow >= 3)
+ fprintf(stderr, "text metrics: w=%d, h=%d, d=%d\n", *w, *h, *d);
+}
+
+
+/*******************************************************************************
+ *
+ * Basic Drawing Functions
+ *
+*******************************************************************************/
+
+EXPORT void wDrawFilledRectangle(
+ wDraw_p bd,
+ wPos_t x,
+ wPos_t y,
+ wPos_t w,
+ wPos_t h,
+ wDrawColor color,
+ wDrawOpts opt )
+{
+ GdkGC * gc;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ psPrintFillRectangle( x, y, w, h, color, opt );
+ return;
+ }
+
+ gc = selectGC( bd, 0, wDrawLineSolid, color, opt );
+ x = INMAPX(bd,x);
+ y = INMAPY(bd,y)-h;
+ gdk_draw_rectangle( bd->pixmap, gc, TRUE, x, y, w, h );
+
+ cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, color, opt);
+
+ cairo_move_to(cairo, x, y);
+ cairo_rel_line_to(cairo, w, 0);
+ cairo_rel_line_to(cairo, 0, h);
+ cairo_rel_line_to(cairo, -w, 0);
+ cairo_fill(cairo);
+ gtkDrawDestroyCairoContext(cairo);
+
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ update_rect.x = x-1;
+ update_rect.y = y-1;
+ update_rect.width = w+2;
+ update_rect.height = h+2;
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+EXPORT void wDrawFilledPolygon(
+ wDraw_p bd,
+ wPos_t p[][2],
+ int cnt,
+ wDrawColor color,
+ wDrawOpts opt )
+{
+ GdkGC * gc;
+ static int maxCnt = 0;
+ static GdkPoint *points;
+ int i;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ psPrintFillPolygon( p, cnt, color, opt );
+ return;
+ }
+
+ if (cnt > maxCnt) {
+ if (points == NULL)
+ points = (GdkPoint*)malloc( cnt*sizeof *points );
+ else
+ points = (GdkPoint*)realloc( points, cnt*sizeof *points );
+ if (points == NULL)
+ abort();
+ maxCnt = cnt;
+ }
+
+ update_rect.x = bd->w;
+ update_rect.y = bd->h;
+ update_rect.width = 0;
+ update_rect.height = 0;
+ for (i=0; i<cnt; i++) {
+ points[i].x = INMAPX(bd,p[i][0]);
+ points[i].y = INMAPY(bd,p[i][1]);
+ if (update_rect.x > points[i].x)
+ update_rect.x = points[i].x;
+ if (update_rect.width < points[i].x)
+ update_rect.width = points[i].x;
+ if (update_rect.y > points[i].y)
+ update_rect.y = points[i].y;
+ if (update_rect.height < points[i].y)
+ update_rect.height = points[i].y;
+ }
+ update_rect.x -= 1;
+ update_rect.y -= 1;
+ update_rect.width -= update_rect.x-2;
+ update_rect.height -= update_rect.y-2;
+ gc = selectGC( bd, 0, wDrawLineSolid, color, opt );
+ gdk_draw_polygon( bd->pixmap, gc, TRUE, points, cnt );
+
+ cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, color, opt);
+ for(i = 0; i < cnt; ++i)
+ {
+ if(i)
+ cairo_line_to(cairo, points[i].x, points[i].y);
+ else
+ cairo_move_to(cairo, points[i].x, points[i].y);
+ }
+ cairo_fill(cairo);
+ gtkDrawDestroyCairoContext(cairo);
+
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+EXPORT void wDrawFilledCircle(
+ wDraw_p bd,
+ wPos_t x0,
+ wPos_t y0,
+ wPos_t r,
+ wDrawColor color,
+ wDrawOpts opt )
+{
+ GdkGC * gc;
+ int x, y, w, h;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ psPrintFillCircle( x0, y0, r, color, opt );
+ return;
+ }
+
+ gc = selectGC( bd, 0, wDrawLineSolid, color, opt );
+ x = INMAPX(bd,x0-r);
+ y = INMAPY(bd,y0+r);
+ w = 2*r;
+ h = 2*r;
+ gdk_draw_arc( bd->pixmap, gc, TRUE, x, y, w, h, 0, 360*64 );
+
+ cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, color, opt);
+ cairo_arc(cairo, INMAPX(bd, x0), INMAPY(bd, y0), r, 0, 2 * M_PI);
+ cairo_fill(cairo);
+ gtkDrawDestroyCairoContext(cairo);
+
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ update_rect.x = x-1;
+ update_rect.y = y-1;
+ update_rect.width = w+2;
+ update_rect.height = h+2;
+ gtk_widget_draw( bd->widget, &update_rect );
+
+}
+
+
+EXPORT void wDrawClear(
+ wDraw_p bd )
+{
+ GdkGC * gc;
+ GdkRectangle update_rect;
+
+ gc = selectGC( bd, 0, wDrawLineSolid, wDrawColorWhite, 0 );
+ gdk_draw_rectangle(bd->pixmap, gc, TRUE, 0, 0, bd->w, bd->h);
+
+ cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, wDrawColorWhite, 0);
+ cairo_move_to(cairo, 0, 0);
+ cairo_rel_line_to(cairo, bd->w, 0);
+ cairo_rel_line_to(cairo, 0, bd->h);
+ cairo_rel_line_to(cairo, -bd->w, 0);
+ cairo_fill(cairo);
+ gtkDrawDestroyCairoContext(cairo);
+
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ update_rect.x = 0;
+ update_rect.y = 0;
+ update_rect.width = bd->w;
+ update_rect.height = bd->h;
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+EXPORT void * wDrawGetContext(
+ wDraw_p bd )
+{
+ return bd->context;
+}
+
+/*******************************************************************************
+ *
+ * Bit Maps
+ *
+*******************************************************************************/
+
+
+EXPORT wDrawBitMap_p wDrawBitMapCreate(
+ wDraw_p bd,
+ int w,
+ int h,
+ int x,
+ int y,
+ const char * fbits )
+{
+ wDrawBitMap_p bm;
+
+ bm = (wDrawBitMap_p)malloc( sizeof *bm );
+ bm->w = w;
+ bm->h = h;
+ /*bm->pixmap = gtkMakeIcon( NULL, fbits, w, h, wDrawColorBlack, &bm->mask );*/
+ bm->bits = fbits;
+ bm->x = x;
+ bm->y = y;
+ return bm;
+}
+
+
+EXPORT void wDrawBitMap(
+ wDraw_p bd,
+ wDrawBitMap_p bm,
+ wPos_t x, wPos_t y,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ GdkGC * gc;
+ GdkRectangle update_rect;
+ int i, j, wb;
+ wPos_t xx, yy;
+ wControl_p b;
+ GdkDrawable * gdk_window;
+
+ x = INMAPX( bd, x-bm->x );
+ y = INMAPY( bd, y-bm->y )-bm->h;
+ wb = (bm->w+7)/8;
+ gc = selectGC( bd, 0, wDrawLineSolid, color, opts );
+
+ for ( i=0; i<bm->w; i++ )
+ for ( j=0; j<bm->h; j++ )
+ if ( bm->bits[ j*wb+(i>>3) ] & (1<<(i&07)) ) {
+ xx = x+i;
+ yy = y+j;
+ if ( 0 <= xx && xx < bd->w &&
+ 0 <= yy && yy < bd->h ) {
+ gdk_window = bd->pixmap;
+ b = (wControl_p)bd;
+ } else if ( (opts&wDrawOptNoClip) != 0 ) {
+ xx += bd->realX;
+ yy += bd->realY;
+ b = gtkGetControlFromPos( bd->parent, xx, yy );
+ if ( b ) {
+ if ( b->type == B_DRAW )
+ gdk_window = ((wDraw_p)b)->pixmap;
+ else
+ gdk_window = b->widget->window;
+ xx -= b->realX;
+ yy -= b->realY;
+ } else {
+ gdk_window = bd->parent->widget->window;
+ }
+ } else {
+ continue;
+ }
+/*printf( "gdk_draw_point( %ld, gc, %d, %d )\n", (long)gdk_window, xx, yy );*/
+ gdk_draw_point( gdk_window, gc, xx, yy );
+ if ( b && b->type == B_DRAW ) {
+ update_rect.x = xx-1;
+ update_rect.y = yy-1;
+ update_rect.width = 3;
+ update_rect.height = 3;
+ gtk_widget_draw( b->widget, &update_rect );
+ }
+ }
+#ifdef LATER
+ gdk_draw_pixmap(bd->pixmap, gc,
+ bm->pixmap,
+ 0, 0,
+ x, y,
+ bm->w, bm->h );
+#endif
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+
+ update_rect.x = x;
+ update_rect.y = y;
+ update_rect.width = bm->w;
+ update_rect.height = bm->h;
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+
+/*******************************************************************************
+ *
+ * Event Handlers
+ *
+*******************************************************************************/
+
+
+
+EXPORT void wDrawSaveImage(
+ wDraw_p bd )
+{
+ if ( bd->pixmapBackup ) {
+ gdk_pixmap_unref( bd->pixmapBackup );
+ }
+ bd->pixmapBackup = gdk_pixmap_new( bd->widget->window, bd->w, bd->h, -1 );
+
+ selectGC( bd, 0, wDrawLineSolid, bd->lastColor, 0 );
+ gdk_gc_set_function(bd->gc, GDK_COPY);
+
+ gdk_draw_pixmap( bd->pixmapBackup, bd->gc,
+ bd->pixmap,
+ 0, 0,
+ 0, 0,
+ bd->w, bd->h );
+}
+
+
+EXPORT void wDrawRestoreImage(
+ wDraw_p bd )
+{
+ GdkRectangle update_rect;
+ if ( bd->pixmapBackup ) {
+
+ selectGC( bd, 0, wDrawLineSolid, bd->lastColor, 0 );
+ gdk_gc_set_function(bd->gc, GDK_COPY);
+
+ gdk_draw_pixmap( bd->pixmap, bd->gc,
+ bd->pixmapBackup,
+ 0, 0,
+ 0, 0,
+ bd->w, bd->h );
+
+ if ( bd->delayUpdate || bd->widget == NULL ) return;
+ update_rect.x = 0;
+ update_rect.y = 0;
+ update_rect.width = bd->w;
+ update_rect.height = bd->h;
+ gtk_widget_draw( bd->widget, &update_rect );
+ }
+}
+
+
+EXPORT void wDrawSetSize(
+ wDraw_p bd,
+ wPos_t w,
+ wPos_t h )
+{
+ wBool_t repaint;
+ if (bd == NULL) {
+ fprintf(stderr,"resizeDraw: no client data\n");
+ return;
+ }
+
+ /* Negative values crashes the program */
+ if (w < 0 || h < 0)
+ return;
+
+ repaint = (w != bd->w || h != bd->h);
+ bd->w = w;
+ bd->h = h;
+ gtk_widget_set_size_request( bd->widget, w, h );
+ if (repaint)
+ {
+ if (bd->pixmap)
+ gdk_pixmap_unref( bd->pixmap );
+ bd->pixmap = gdk_pixmap_new( bd->widget->window, w, h, -1 );
+
+ wDrawClear( bd );
+ /*bd->redraw( bd, bd->context, w, h );*/
+ }
+ /*wRedraw( bd );*/
+}
+
+
+EXPORT void wDrawGetSize(
+ wDraw_p bd,
+ wPos_t *w,
+ wPos_t *h )
+{
+ if (bd->widget)
+ gtkControlGetSize( (wControl_p)bd );
+ *w = bd->w-2;
+ *h = bd->h-2;
+}
+
+
+EXPORT double wDrawGetDPI(
+ wDraw_p d )
+{
+ if (d == &psPrint_d)
+ return 1440.0;
+ else
+ return d->dpi;
+}
+
+
+EXPORT double wDrawGetMaxRadius(
+ wDraw_p d )
+{
+ if (d == &psPrint_d)
+ return 10e9;
+ else
+ return 32767.0;
+}
+
+
+EXPORT void wDrawClip(
+ wDraw_p d,
+ wPos_t x,
+ wPos_t y,
+ wPos_t w,
+ wPos_t h )
+{
+ GdkRectangle rect;
+ rect.width = w;
+ rect.height = h;
+ rect.x = INMAPX( d, x );
+ rect.y = INMAPY( d, y ) - rect.height;
+ gdk_gc_set_clip_rectangle( d->gc, &rect );
+
+}
+
+
+static gint draw_expose_event(
+ GtkWidget *widget,
+ GdkEventExpose *event,
+ wDraw_p bd)
+{
+ gdk_draw_pixmap(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ bd->pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+ return FALSE;
+}
+
+
+static gint draw_configure_event(
+ GtkWidget *widget,
+ GdkEventConfigure *event,
+ wDraw_p bd)
+{
+ return FALSE;
+}
+
+static const char * actionNames[] = { "None", "Move", "LDown", "LDrag", "LUp", "RDown", "RDrag", "RUp", "Text", "ExtKey", "WUp", "WDown" };
+
+/**
+ * Handler for scroll events, ie mouse wheel activity
+ */
+
+static gint draw_scroll_event(
+ GtkWidget *widget,
+ GdkEventScroll *event,
+ wDraw_p bd)
+{
+ wAction_t action;
+
+ switch( event->direction ) {
+ case GDK_SCROLL_UP:
+ action = wActionWheelUp;
+ break;
+ case GDK_SCROLL_DOWN:
+ action = wActionWheelDown;
+ break;
+ default:
+ action = 0;
+ break;
+ }
+
+ if (action != 0) {
+ if (drawVerbose >= 2)
+ printf( "%s[%dx%d]\n", actionNames[action], bd->lastX, bd->lastY );
+ bd->action( bd, bd->context, action, bd->lastX, bd->lastY );
+ }
+
+ return TRUE;
+}
+
+
+
+static gint draw_leave_event(
+ GtkWidget *widget,
+ GdkEvent * event )
+{
+ gtkHelpHideBalloon();
+ return FALSE;
+}
+
+
+/**
+ * Handler for mouse button clicks.
+ */
+
+static gint draw_button_event(
+ GtkWidget *widget,
+ GdkEventButton *event,
+ wDraw_p bd )
+{
+ wAction_t action = 0;
+ if (bd->action == NULL)
+ return TRUE;
+
+ bd->lastX = OUTMAPX(bd, event->x);
+ bd->lastY = OUTMAPY(bd, event->y);
+
+ switch ( event->button ) {
+ case 1: /* left mouse button */
+ action = event->type==GDK_BUTTON_PRESS?wActionLDown:wActionLUp;
+ /*bd->action( bd, bd->context, event->type==GDK_BUTTON_PRESS?wActionLDown:wActionLUp, bd->lastX, bd->lastY );*/
+ break;
+ case 3: /* right mouse button */
+ action = event->type==GDK_BUTTON_PRESS?wActionRDown:wActionRUp;
+ /*bd->action( bd, bd->context, event->type==GDK_BUTTON_PRESS?wActionRDown:wActionRUp, bd->lastX, bd->lastY );*/
+ break;
+ }
+ if (action != 0) {
+ if (drawVerbose >= 2)
+ printf( "%s[%dx%d]\n", actionNames[action], bd->lastX, bd->lastY );
+ bd->action( bd, bd->context, action, bd->lastX, bd->lastY );
+ }
+ gtk_widget_grab_focus( bd->widget );
+ return TRUE;
+}
+
+static gint draw_motion_event(
+ GtkWidget *widget,
+ GdkEventMotion *event,
+ wDraw_p bd )
+{
+ int x, y;
+ GdkModifierType state;
+ wAction_t action;
+
+ if (bd->action == NULL)
+ return TRUE;
+
+ if (event->is_hint) {
+ gdk_window_get_pointer (event->window, &x, &y, &state);
+ } else {
+ x = event->x;
+ y = event->y;
+ state = event->state;
+ }
+
+ if (state & GDK_BUTTON1_MASK) {
+ action = wActionLDrag;
+ } else if (state & GDK_BUTTON3_MASK) {
+ action = wActionRDrag;
+ } else {
+ action = wActionMove;
+ }
+ bd->lastX = OUTMAPX(bd, x);
+ bd->lastY = OUTMAPY(bd, y);
+ if (drawVerbose >= 2)
+ printf( "%lx: %s[%dx%d] %s\n", (long)bd, actionNames[action], bd->lastX, bd->lastY, event->is_hint?"<Hint>":"<>" );
+ bd->action( bd, bd->context, action, bd->lastX, bd->lastY );
+ gtk_widget_grab_focus( bd->widget );
+ return TRUE;
+}
+
+
+static gint draw_char_event(
+ GtkWidget * widget,
+ GdkEventKey *event,
+ wDraw_p bd )
+{
+ guint key = event->keyval;
+ wAccelKey_e extKey = wAccelKey_None;
+ switch (key) {
+ case GDK_Escape: key = 0x1B; break;
+ case GDK_Return: key = 0x0D; break;
+ case GDK_Linefeed: key = 0x0A; break;
+ case GDK_Tab: key = 0x09; break;
+ case GDK_BackSpace: key = 0x08; break;
+ case GDK_Delete: extKey = wAccelKey_Del; break;
+ case GDK_Insert: extKey = wAccelKey_Ins; break;
+ case GDK_Home: extKey = wAccelKey_Home; break;
+ case GDK_End: extKey = wAccelKey_End; break;
+ case GDK_Page_Up: extKey = wAccelKey_Pgup; break;
+ case GDK_Page_Down: extKey = wAccelKey_Pgdn; break;
+ case GDK_Up: extKey = wAccelKey_Up; break;
+ case GDK_Down: extKey = wAccelKey_Down; break;
+ case GDK_Right: extKey = wAccelKey_Right; break;
+ case GDK_Left: extKey = wAccelKey_Left; break;
+ case GDK_F1: extKey = wAccelKey_F1; break;
+ case GDK_F2: extKey = wAccelKey_F2; break;
+ case GDK_F3: extKey = wAccelKey_F3; break;
+ case GDK_F4: extKey = wAccelKey_F4; break;
+ case GDK_F5: extKey = wAccelKey_F5; break;
+ case GDK_F6: extKey = wAccelKey_F6; break;
+ case GDK_F7: extKey = wAccelKey_F7; break;
+ case GDK_F8: extKey = wAccelKey_F8; break;
+ case GDK_F9: extKey = wAccelKey_F9; break;
+ case GDK_F10: extKey = wAccelKey_F10; break;
+ case GDK_F11: extKey = wAccelKey_F11; break;
+ case GDK_F12: extKey = wAccelKey_F12; break;
+ default: ;
+ }
+
+ if (extKey != wAccelKey_None) {
+ if ( gtkFindAccelKey( event ) == NULL ) {
+ bd->action( bd, bd->context, wActionExtKey + ((int)extKey<<8), bd->lastX, bd->lastY );
+ }
+ return TRUE;
+ } else if (key <= 0xFF && (event->state&(GDK_CONTROL_MASK|GDK_MOD1_MASK)) == 0 && bd->action) {
+ bd->action( bd, bd->context, wActionText+(key<<8), bd->lastX, bd->lastY );
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * Create
+ *
+*******************************************************************************/
+
+
+
+int XW = 0;
+int XH = 0;
+int xw, xh, cw, ch;
+
+EXPORT wDraw_p wDrawCreate(
+ wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char * helpStr,
+ long option,
+ wPos_t width,
+ wPos_t height,
+ void * context,
+ wDrawRedrawCallBack_p redraw,
+ wDrawActionCallBack_p action )
+{
+ wDraw_p bd;
+
+ bd = (wDraw_p)gtkAlloc( parent, B_DRAW, x, y, NULL, sizeof *bd, NULL );
+ bd->option = option;
+ bd->context = context;
+ bd->redraw = redraw;
+ bd->action = action;
+ gtkComputePos( (wControl_p)bd );
+
+ bd->widget = gtk_drawing_area_new();
+ gtk_drawing_area_size( GTK_DRAWING_AREA(bd->widget), width, height );
+ gtk_widget_set_size_request( GTK_WIDGET(bd->widget), width, height );
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "expose_event",
+ (GtkSignalFunc) draw_expose_event, bd);
+ gtk_signal_connect (GTK_OBJECT(bd->widget),"configure_event",
+ (GtkSignalFunc) draw_configure_event, bd);
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "motion_notify_event",
+ (GtkSignalFunc) draw_motion_event, bd);
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "button_press_event",
+ (GtkSignalFunc) draw_button_event, bd);
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "button_release_event",
+ (GtkSignalFunc) draw_button_event, bd);
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "scroll_event",
+ (GtkSignalFunc) draw_scroll_event, bd);
+ gtk_signal_connect_after (GTK_OBJECT (bd->widget), "key_press_event",
+ (GtkSignalFunc) draw_char_event, bd);
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "leave_notify_event",
+ (GtkSignalFunc) draw_leave_event, bd);
+ GTK_WIDGET_SET_FLAGS(GTK_WIDGET(bd->widget), GTK_CAN_FOCUS);
+ gtk_widget_set_events (bd->widget, GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+/* | GDK_SCROLL_MASK */
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_KEY_RELEASE_MASK );
+ bd->lastColor = -1;
+ bd->dpi = 75;
+ bd->maxW = bd->w = width;
+ bd->maxH = bd->h = height;
+
+ gtk_fixed_put( GTK_FIXED(parent->widget), bd->widget, bd->realX, bd->realY );
+ gtkControlGetSize( (wControl_p)bd );
+ gtk_widget_realize( bd->widget );
+ bd->pixmap = gdk_pixmap_new( bd->widget->window, width, height, -1 );
+ bd->gc = gdk_gc_new( parent->gtkwin->window );
+ gdk_gc_copy( bd->gc, parent->gtkwin->style->base_gc[GTK_STATE_NORMAL] );
+{
+ GdkCursor * cursor;
+ cursor = gdk_cursor_new ( GDK_TCROSS );
+ gdk_window_set_cursor ( bd->widget->window, cursor);
+ gdk_cursor_destroy (cursor);
+}
+#ifdef LATER
+ if (labelStr)
+ bd->labelW = gtkAddLabel( (wControl_p)bd, labelStr );
+#endif
+ gtk_widget_show( bd->widget );
+ gtkAddButton( (wControl_p)bd );
+ gtkAddHelpString( bd->widget, helpStr );
+
+ return bd;
+}
+
+/*******************************************************************************
+ *
+ * BitMaps
+ *
+*******************************************************************************/
+
+wDraw_p wBitMapCreate( wPos_t w, wPos_t h, int arg )
+{
+ wDraw_p bd;
+
+ bd = (wDraw_p)gtkAlloc( gtkMainW, B_DRAW, 0, 0, NULL, sizeof *bd, NULL );
+
+ bd->lastColor = -1;
+ bd->dpi = 75;
+ bd->maxW = bd->w = w;
+ bd->maxH = bd->h = h;
+
+ bd->pixmap = gdk_pixmap_new( gtkMainW->widget->window, w, h, -1 );
+ if ( bd->pixmap == NULL ) {
+ wNoticeEx( NT_ERROR, "CreateBitMap: pixmap_new failed", "Ok", NULL );
+ return FALSE;
+ }
+ bd->gc = gdk_gc_new( gtkMainW->gtkwin->window );
+ if ( bd->gc == NULL ) {
+ wNoticeEx( NT_ERROR, "CreateBitMap: gc_new failed", "Ok", NULL );
+ return FALSE;
+ }
+ gdk_gc_copy( bd->gc, gtkMainW->gtkwin->style->base_gc[GTK_STATE_NORMAL] );
+
+ wDrawClear( bd );
+ return bd;
+}
+
+
+wBool_t wBitMapDelete( wDraw_p d )
+{
+ gdk_pixmap_unref( d->pixmap );
+ d->pixmap = NULL;
+ return TRUE;
+}
+
diff --git a/app/wlib/gtklib/gtkdraw.c b/app/wlib/gtklib/gtkdraw.c
new file mode 100644
index 0000000..b8f07ef
--- /dev/null
+++ b/app/wlib/gtklib/gtkdraw.c
@@ -0,0 +1,1044 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtkdraw.c,v 1.9 2009-09-25 05:38:15 dspagnol Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "gtkint.h"
+#include "gdk/gdkkeysyms.h"
+
+static long drawVerbose = 0;
+
+struct wDrawBitMap_t {
+ int w;
+ int h;
+ int x;
+ int y;
+ const char * bits;
+ GdkPixmap * pixmap;
+ GdkBitmap * mask;
+ };
+
+struct wDraw_t {
+ WOBJ_COMMON
+ void * context;
+ wDrawActionCallBack_p action;
+ wDrawRedrawCallBack_p redraw;
+
+ GdkPixmap * pixmap;
+ GdkPixmap * pixmapBackup;
+
+ double dpi;
+
+ GdkGC * gc;
+ wDrawWidth lineWidth;
+ wDrawOpts opts;
+ wPos_t maxW;
+ wPos_t maxH;
+ unsigned long lastColor;
+ wBool_t lastColorInverted;
+ const char * helpStr;
+
+ wPos_t lastX;
+ wPos_t lastY;
+
+ wBool_t delayUpdate;
+ };
+
+struct wDraw_t psPrint_d;
+
+/*****************************************************************************
+ *
+ * MACROS
+ *
+ */
+
+#define LBORDER (22)
+#define RBORDER (6)
+#define TBORDER (6)
+#define BBORDER (20)
+
+#define INMAPX(D,X) (X)
+#define INMAPY(D,Y) (((D)->h-1) - (Y))
+#define OUTMAPX(D,X) (X)
+#define OUTMAPY(D,Y) (((D)->h-1) - (Y))
+
+
+/*******************************************************************************
+ *
+ * Basic Drawing Functions
+ *
+*******************************************************************************/
+
+
+
+static GdkGC * selectGC(
+ wDraw_p bd,
+ wDrawWidth width,
+ wDrawLineType_e lineType,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ if (width < 0.0) {
+ width = - width;
+ }
+/*
+ if ( color != bd->lastColor ) {
+ unsigned long pixColor;
+ unsigned long black, white;
+ white = WhitePixel(bd->display,DefaultScreen(bd->display));
+ black = BlackPixel(bd->display,DefaultScreen(bd->display));
+ pixColor = bd->colors[color] ^ white;
+ XSetForeground( bd->display, bd->normGc, pixColor );
+ bd->lastColor = color;
+ }
+*/
+ if (opts&wDrawOptTemp) {
+ if (bd->lastColor != color || !bd->lastColorInverted) {
+ gdk_gc_set_foreground( bd->gc, gtkGetColor(color,FALSE) );
+ bd->lastColor = color;
+ bd->lastColorInverted = TRUE;
+ }
+ gdk_gc_set_function( bd->gc, GDK_XOR );
+ gdk_gc_set_line_attributes( bd->gc, width,
+ GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ } else {
+ if (bd->lastColor != color || bd->lastColorInverted) {
+ gdk_gc_set_foreground( bd->gc, gtkGetColor(color,TRUE) );
+ bd->lastColor = color;
+ bd->lastColorInverted = FALSE;
+ }
+ gdk_gc_set_function( bd->gc, GDK_COPY );
+ if (lineType==wDrawLineDash) {
+ gdk_gc_set_line_attributes( bd->gc, width,
+ GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ /*XSetDashes( bd->display, bd->normGc, 0, "\003\003", 2 );*/
+ } else {
+ gdk_gc_set_line_attributes( bd->gc, width,
+ GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+ }
+ return bd->gc;
+}
+
+
+EXPORT void wDrawDelayUpdate(
+ wDraw_p bd,
+ wBool_t delay )
+{
+ GdkRectangle update_rect;
+
+ if ( (!delay) && bd->delayUpdate ) {
+ update_rect.x = 0;
+ update_rect.y = 0;
+ update_rect.width = bd->w;
+ update_rect.height = bd->h;
+ gtk_widget_draw( bd->widget, &update_rect );
+ }
+ bd->delayUpdate = delay;
+}
+
+
+EXPORT void wDrawLine(
+ wDraw_p bd,
+ wPos_t x0, wPos_t y0,
+ wPos_t x1, wPos_t y1,
+ wDrawWidth width,
+ wDrawLineType_e lineType,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ GdkGC * gc;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ psPrintLine( x0, y0, x1, y1, width, lineType, color, opts );
+ return;
+ }
+ gc = selectGC( bd, width, lineType, color, opts );
+ x0 = INMAPX(bd,x0);
+ y0 = INMAPY(bd,y0);
+ x1 = INMAPX(bd,x1);
+ y1 = INMAPY(bd,y1);
+ gdk_draw_line( bd->pixmap, gc, x0, y0, x1, y1 );
+ if ( bd->delayUpdate || bd->widget == NULL ) return;
+ width /= 2;
+ if (x0 < x1) {
+ update_rect.x = x0-1-width;
+ update_rect.width = x1-x0+2+width+width;
+ } else {
+ update_rect.x = x1-1-width;
+ update_rect.width = x0-x1+2+width+width;
+ }
+ if (y0 < y1) {
+ update_rect.y = y0-1-width;
+ update_rect.height = y1-y0+2+width+width;
+ } else {
+ update_rect.y = y1-1-width;
+ update_rect.height = y0-y1+2+width+width;
+ }
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+EXPORT void wDrawArc(
+ wDraw_p bd,
+ wPos_t x0, wPos_t y0,
+ wPos_t r,
+ wAngle_t angle0,
+ wAngle_t angle1,
+ int drawCenter,
+ wDrawWidth width,
+ wDrawLineType_e lineType,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ int x, y, w, h;
+ GdkGC * gc;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ psPrintArc( x0, y0, r, angle0, angle1, drawCenter, width, lineType, color, opts );
+ return;
+ }
+ gc = selectGC( bd, width, lineType, color, opts );
+ if (r < 6.0/75.0) return;
+ x = INMAPX(bd,x0-r);
+ y = INMAPY(bd,y0+r);
+ w = 2*r;
+ h = 2*r;
+ if (drawCenter)
+ gdk_draw_point( bd->pixmap, gc,
+ INMAPX(bd, x0 ), INMAPY(bd, y0 ) );
+ angle1 = -angle1;
+ angle0 = (-angle0) + 90.0;
+ gdk_draw_arc( bd->pixmap, gc, FALSE, x, y, w, h,
+ (int)(angle0*64.0), (int)(angle1*64.0) );
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ width /= 2;
+ update_rect.x = x-1-width;
+ update_rect.y = y-1-width;
+ update_rect.width = w+2+width+width;
+ update_rect.height = h+2+width+width;
+ gtk_widget_draw( bd->widget, &update_rect );
+
+}
+
+EXPORT void wDrawPoint(
+ wDraw_p bd,
+ wPos_t x0, wPos_t y0,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ GdkGC * gc;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ /*psPrintArc( x0, y0, r, angle0, angle1, drawCenter, width, lineType, color, opts );*/
+ return;
+ }
+ gc = selectGC( bd, 0, wDrawLineSolid, color, opts );
+ gdk_draw_point( bd->pixmap, gc,
+ INMAPX(bd, x0 ), INMAPY(bd, y0 ) );
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ update_rect.x = INMAPX(bd, x0 )-1;
+ update_rect.y = INMAPY(bd, y0 )-1;
+ update_rect.width = 2;
+ update_rect.height = 2;
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+/*******************************************************************************
+ *
+ * Strings
+ *
+ ******************************************************************************/
+
+EXPORT void wDrawString(
+ wDraw_p bd,
+ wPos_t x, wPos_t y,
+ wAngle_t a,
+ const char * s,
+ wFont_p fp,
+ wFontSize_t fs,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ GdkGC * gc;
+ PangoLayout *layout;
+ GdkRectangle update_rect;
+ int w;
+ int h;
+ gint ascent;
+ gint descent;
+
+ if ( bd == &psPrint_d ) {
+ psPrintString( x, y, a, (char *)s, fp, fs, color, opts );
+ return;
+ }
+
+ x = INMAPX(bd,x);
+ y = INMAPY(bd,y);
+
+ gc = selectGC( bd, 0, wDrawLineSolid, color, opts );
+
+ layout = gtkFontCreatePangoLayout(bd->widget, NULL, fp, fs, s,
+ (int *) &w, (int *) &h,
+ (int *) &ascent, (int *) &descent);
+
+ gdk_draw_layout(bd->pixmap, gc, x, y - ascent, layout);
+ gtkFontDestroyPangoLayout(layout);
+
+ if (bd->delayUpdate || bd->widget == NULL) return;
+ update_rect.x = (gint) x - 1;
+ update_rect.y = (gint) y - ascent - 1;
+ update_rect.width = (gint) w + 2;
+ update_rect.height = (gint) ascent + (gint) descent + 2;
+ gtk_widget_draw(bd->widget, &update_rect);
+}
+
+EXPORT void wDrawGetTextSize(
+ wPos_t *w,
+ wPos_t *h,
+ wPos_t *d,
+ wDraw_p bd,
+ const char * s,
+ wFont_p fp,
+ wFontSize_t fs )
+{
+ int textWidth;
+ int textHeight;
+ int ascent;
+ int descent;
+
+ *w = 0;
+ *h = 0;
+
+ gtkFontDestroyPangoLayout(
+ gtkFontCreatePangoLayout(bd->widget, NULL, fp, fs, s,
+ &textWidth, (int *) &textHeight,
+ (int *) &ascent, (int *) &descent));
+
+ *w = (wPos_t) textWidth;
+ *h = (wPos_t) ascent;
+ *d = (wPos_t) descent;
+
+ if (debugWindow >= 3)
+ fprintf(stderr, "text metrics: w=%d, h=%d, d=%d\n", *w, *h, *d);
+}
+
+
+/*******************************************************************************
+ *
+ * Basic Drawing Functions
+ *
+*******************************************************************************/
+
+EXPORT void wDrawFilledRectangle(
+ wDraw_p bd,
+ wPos_t x,
+ wPos_t y,
+ wPos_t w,
+ wPos_t h,
+ wDrawColor color,
+ wDrawOpts opt )
+{
+ GdkGC * gc;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ psPrintFillRectangle( x, y, w, h, color, opt );
+ return;
+ }
+
+ gc = selectGC( bd, 0, wDrawLineSolid, color, opt );
+ x = INMAPX(bd,x);
+ y = INMAPY(bd,y)-h;
+ gdk_draw_rectangle( bd->pixmap, gc, TRUE, x, y, w, h );
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ update_rect.x = x-1;
+ update_rect.y = y-1;
+ update_rect.width = w+2;
+ update_rect.height = h+2;
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+EXPORT void wDrawFilledPolygon(
+ wDraw_p bd,
+ wPos_t p[][2],
+ int cnt,
+ wDrawColor color,
+ wDrawOpts opt )
+{
+ GdkGC * gc;
+ static int maxCnt = 0;
+ static GdkPoint *points;
+ int i;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ psPrintFillPolygon( p, cnt, color, opt );
+ return;
+ }
+
+ if (cnt > maxCnt) {
+ if (points == NULL)
+ points = (GdkPoint*)malloc( cnt*sizeof *points );
+ else
+ points = (GdkPoint*)realloc( points, cnt*sizeof *points );
+ if (points == NULL)
+ abort();
+ maxCnt = cnt;
+ }
+
+ update_rect.x = bd->w;
+ update_rect.y = bd->h;
+ update_rect.width = 0;
+ update_rect.height = 0;
+ for (i=0; i<cnt; i++) {
+ points[i].x = INMAPX(bd,p[i][0]);
+ points[i].y = INMAPY(bd,p[i][1]);
+ if (update_rect.x > points[i].x)
+ update_rect.x = points[i].x;
+ if (update_rect.width < points[i].x)
+ update_rect.width = points[i].x;
+ if (update_rect.y > points[i].y)
+ update_rect.y = points[i].y;
+ if (update_rect.height < points[i].y)
+ update_rect.height = points[i].y;
+ }
+ update_rect.x -= 1;
+ update_rect.y -= 1;
+ update_rect.width -= update_rect.x-2;
+ update_rect.height -= update_rect.y-2;
+ gc = selectGC( bd, 0, wDrawLineSolid, color, opt );
+ gdk_draw_polygon( bd->pixmap, gc, TRUE,
+ points, cnt );
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+EXPORT void wDrawFilledCircle(
+ wDraw_p bd,
+ wPos_t x0,
+ wPos_t y0,
+ wPos_t r,
+ wDrawColor color,
+ wDrawOpts opt )
+{
+ GdkGC * gc;
+ int x, y, w, h;
+ GdkRectangle update_rect;
+
+ if ( bd == &psPrint_d ) {
+ psPrintFillCircle( x0, y0, r, color, opt );
+ return;
+ }
+
+ gc = selectGC( bd, 0, wDrawLineSolid, color, opt );
+ x = INMAPX(bd,x0-r);
+ y = INMAPY(bd,y0+r);
+ w = 2*r;
+ h = 2*r;
+ gdk_draw_arc( bd->pixmap, gc, TRUE, x, y, w, h, 0, 360*64 );
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ update_rect.x = x-1;
+ update_rect.y = y-1;
+ update_rect.width = w+2;
+ update_rect.height = h+2;
+ gtk_widget_draw( bd->widget, &update_rect );
+
+}
+
+
+EXPORT void wDrawClear(
+ wDraw_p bd )
+{
+ GdkGC * gc;
+ GdkRectangle update_rect;
+
+ gc = selectGC( bd, 0, wDrawLineSolid, wDrawColorWhite, 0 );
+ gdk_draw_rectangle(bd->pixmap, gc, TRUE, 0, 0,
+ bd->w, bd->h);
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+ update_rect.x = 0;
+ update_rect.y = 0;
+ update_rect.width = bd->w;
+ update_rect.height = bd->h;
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+EXPORT void * wDrawGetContext(
+ wDraw_p bd )
+{
+ return bd->context;
+}
+
+/*******************************************************************************
+ *
+ * Bit Maps
+ *
+*******************************************************************************/
+
+
+EXPORT wDrawBitMap_p wDrawBitMapCreate(
+ wDraw_p bd,
+ int w,
+ int h,
+ int x,
+ int y,
+ const char * fbits )
+{
+ wDrawBitMap_p bm;
+
+ bm = (wDrawBitMap_p)malloc( sizeof *bm );
+ bm->w = w;
+ bm->h = h;
+ /*bm->pixmap = gtkMakeIcon( NULL, fbits, w, h, wDrawColorBlack, &bm->mask );*/
+ bm->bits = fbits;
+ bm->x = x;
+ bm->y = y;
+ return bm;
+}
+
+
+EXPORT void wDrawBitMap(
+ wDraw_p bd,
+ wDrawBitMap_p bm,
+ wPos_t x, wPos_t y,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ GdkGC * gc;
+ GdkRectangle update_rect;
+ int i, j, wb;
+ wPos_t xx, yy;
+ wControl_p b;
+ GdkDrawable * gdk_window;
+
+ x = INMAPX( bd, x-bm->x );
+ y = INMAPY( bd, y-bm->y )-bm->h;
+ wb = (bm->w+7)/8;
+ gc = selectGC( bd, 0, wDrawLineSolid, color, opts );
+ for ( i=0; i<bm->w; i++ )
+ for ( j=0; j<bm->h; j++ )
+ if ( bm->bits[ j*wb+(i>>3) ] & (1<<(i&07)) ) {
+ xx = x+i;
+ yy = y+j;
+ if ( 0 <= xx && xx < bd->w &&
+ 0 <= yy && yy < bd->h ) {
+ gdk_window = bd->pixmap;
+ b = (wControl_p)bd;
+ } else if ( (opts&wDrawOptNoClip) != 0 ) {
+ xx += bd->realX;
+ yy += bd->realY;
+ b = gtkGetControlFromPos( bd->parent, xx, yy );
+ if ( b ) {
+ if ( b->type == B_DRAW )
+ gdk_window = ((wDraw_p)b)->pixmap;
+ else
+ gdk_window = b->widget->window;
+ xx -= b->realX;
+ yy -= b->realY;
+ } else {
+ gdk_window = bd->parent->widget->window;
+ }
+ } else {
+ continue;
+ }
+/*printf( "gdk_draw_point( %ld, gc, %d, %d )\n", (long)gdk_window, xx, yy );*/
+ gdk_draw_point( gdk_window, gc, xx, yy );
+ if ( b && b->type == B_DRAW ) {
+ update_rect.x = xx-1;
+ update_rect.y = yy-1;
+ update_rect.width = 3;
+ update_rect.height = 3;
+ gtk_widget_draw( b->widget, &update_rect );
+ }
+ }
+#ifdef LATER
+ gdk_draw_pixmap(bd->pixmap, gc,
+ bm->pixmap,
+ 0, 0,
+ x, y,
+ bm->w, bm->h );
+#endif
+ if ( bd->delayUpdate || bd->widget == NULL) return;
+
+ update_rect.x = x;
+ update_rect.y = y;
+ update_rect.width = bm->w;
+ update_rect.height = bm->h;
+ gtk_widget_draw( bd->widget, &update_rect );
+}
+
+
+/*******************************************************************************
+ *
+ * Event Handlers
+ *
+*******************************************************************************/
+
+
+
+EXPORT void wDrawSaveImage(
+ wDraw_p bd )
+{
+ if ( bd->pixmapBackup ) {
+ gdk_pixmap_unref( bd->pixmapBackup );
+ }
+ bd->pixmapBackup = gdk_pixmap_new( bd->widget->window, bd->w, bd->h, -1 );
+ selectGC( bd, 0, wDrawLineSolid, bd->lastColor, 0 );
+ gdk_draw_pixmap( bd->pixmapBackup, bd->gc,
+ bd->pixmap,
+ 0, 0,
+ 0, 0,
+ bd->w, bd->h );
+}
+
+
+EXPORT void wDrawRestoreImage(
+ wDraw_p bd )
+{
+ GdkRectangle update_rect;
+ if ( bd->pixmapBackup ) {
+ selectGC( bd, 0, wDrawLineSolid, bd->lastColor, 0 );
+ gdk_draw_pixmap( bd->pixmap, bd->gc,
+ bd->pixmapBackup,
+ 0, 0,
+ 0, 0,
+ bd->w, bd->h );
+ if ( bd->delayUpdate || bd->widget == NULL ) return;
+ update_rect.x = 0;
+ update_rect.y = 0;
+ update_rect.width = bd->w;
+ update_rect.height = bd->h;
+ gtk_widget_draw( bd->widget, &update_rect );
+ }
+}
+
+
+EXPORT void wDrawSetSize(
+ wDraw_p bd,
+ wPos_t w,
+ wPos_t h )
+{
+ wBool_t repaint;
+ if (bd == NULL) {
+ fprintf(stderr,"resizeDraw: no client data\n");
+ return;
+ }
+
+ /* Negative values crashes the program */
+ if (w < 0 || h < 0)
+ return;
+
+ repaint = (w != bd->w || h != bd->h);
+ bd->w = w;
+ bd->h = h;
+ gtk_widget_set_size_request( bd->widget, w, h );
+ if (repaint) {
+ if (bd->pixmap)
+ gdk_pixmap_unref( bd->pixmap );
+ bd->pixmap = gdk_pixmap_new( bd->widget->window, w, h, -1 );
+ wDrawClear( bd );
+ /*bd->redraw( bd, bd->context, w, h );*/
+ }
+ /*wRedraw( bd );*/
+}
+
+
+EXPORT void wDrawGetSize(
+ wDraw_p bd,
+ wPos_t *w,
+ wPos_t *h )
+{
+ if (bd->widget)
+ gtkControlGetSize( (wControl_p)bd );
+ *w = bd->w-2;
+ *h = bd->h-2;
+}
+
+
+EXPORT double wDrawGetDPI(
+ wDraw_p d )
+{
+ if (d == &psPrint_d)
+ return 1440.0;
+ else
+ return d->dpi;
+}
+
+
+EXPORT double wDrawGetMaxRadius(
+ wDraw_p d )
+{
+ if (d == &psPrint_d)
+ return 10e9;
+ else
+ return 32767.0;
+}
+
+
+EXPORT void wDrawClip(
+ wDraw_p d,
+ wPos_t x,
+ wPos_t y,
+ wPos_t w,
+ wPos_t h )
+{
+ GdkRectangle rect;
+ rect.width = w;
+ rect.height = h;
+ rect.x = INMAPX( d, x );
+ rect.y = INMAPY( d, y ) - rect.height;
+ gdk_gc_set_clip_rectangle( d->gc, &rect );
+
+}
+
+
+static gint draw_expose_event(
+ GtkWidget *widget,
+ GdkEventExpose *event,
+ wDraw_p bd)
+{
+ gdk_draw_pixmap(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ bd->pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+ return FALSE;
+}
+
+
+static gint draw_configure_event(
+ GtkWidget *widget,
+ GdkEventConfigure *event,
+ wDraw_p bd)
+{
+ return FALSE;
+}
+
+static const char * actionNames[] = { "None", "Move", "LDown", "LDrag", "LUp", "RDown", "RDrag", "RUp", "Text", "ExtKey", "WUp", "WDown" };
+
+/**
+ * Handler for scroll events, ie mouse wheel activity
+ */
+
+static gint draw_scroll_event(
+ GtkWidget *widget,
+ GdkEventScroll *event,
+ wDraw_p bd)
+{
+ wAction_t action;
+
+ switch( event->direction ) {
+ case GDK_SCROLL_UP:
+ action = wActionWheelUp;
+ break;
+ case GDK_SCROLL_DOWN:
+ action = wActionWheelDown;
+ break;
+ default:
+ action = 0;
+ break;
+ }
+
+ if (action != 0) {
+ if (drawVerbose >= 2)
+ printf( "%s[%dx%d]\n", actionNames[action], bd->lastX, bd->lastY );
+ bd->action( bd, bd->context, action, bd->lastX, bd->lastY );
+ }
+
+ return TRUE;
+}
+
+
+
+static gint draw_leave_event(
+ GtkWidget *widget,
+ GdkEvent * event )
+{
+ gtkHelpHideBalloon();
+ return FALSE;
+}
+
+
+/**
+ * Handler for mouse button clicks.
+ */
+
+static gint draw_button_event(
+ GtkWidget *widget,
+ GdkEventButton *event,
+ wDraw_p bd )
+{
+ wAction_t action = 0;
+ if (bd->action == NULL)
+ return TRUE;
+
+ bd->lastX = OUTMAPX(bd, event->x);
+ bd->lastY = OUTMAPY(bd, event->y);
+
+ switch ( event->button ) {
+ case 1: /* left mouse button */
+ action = event->type==GDK_BUTTON_PRESS?wActionLDown:wActionLUp;
+ /*bd->action( bd, bd->context, event->type==GDK_BUTTON_PRESS?wActionLDown:wActionLUp, bd->lastX, bd->lastY );*/
+ break;
+ case 3: /* right mouse button */
+ action = event->type==GDK_BUTTON_PRESS?wActionRDown:wActionRUp;
+ /*bd->action( bd, bd->context, event->type==GDK_BUTTON_PRESS?wActionRDown:wActionRUp, bd->lastX, bd->lastY );*/
+ break;
+ }
+ if (action != 0) {
+ if (drawVerbose >= 2)
+ printf( "%s[%dx%d]\n", actionNames[action], bd->lastX, bd->lastY );
+ bd->action( bd, bd->context, action, bd->lastX, bd->lastY );
+ }
+ gtk_widget_grab_focus( bd->widget );
+ return TRUE;
+}
+
+static gint draw_motion_event(
+ GtkWidget *widget,
+ GdkEventMotion *event,
+ wDraw_p bd )
+{
+ int x, y;
+ GdkModifierType state;
+ wAction_t action;
+
+ if (bd->action == NULL)
+ return TRUE;
+
+ if (event->is_hint) {
+ gdk_window_get_pointer (event->window, &x, &y, &state);
+ } else {
+ x = event->x;
+ y = event->y;
+ state = event->state;
+ }
+
+ if (state & GDK_BUTTON1_MASK) {
+ action = wActionLDrag;
+ } else if (state & GDK_BUTTON3_MASK) {
+ action = wActionRDrag;
+ } else {
+ action = wActionMove;
+ }
+ bd->lastX = OUTMAPX(bd, x);
+ bd->lastY = OUTMAPY(bd, y);
+ if (drawVerbose >= 2)
+ printf( "%lx: %s[%dx%d] %s\n", (long)bd, actionNames[action], bd->lastX, bd->lastY, event->is_hint?"<Hint>":"<>" );
+ bd->action( bd, bd->context, action, bd->lastX, bd->lastY );
+ gtk_widget_grab_focus( bd->widget );
+ return TRUE;
+}
+
+
+static gint draw_char_event(
+ GtkWidget * widget,
+ GdkEventKey *event,
+ wDraw_p bd )
+{
+ guint key = event->keyval;
+ wAccelKey_e extKey = wAccelKey_None;
+ switch (key) {
+ case GDK_Escape: key = 0x1B; break;
+ case GDK_Return: key = 0x0D; break;
+ case GDK_Linefeed: key = 0x0A; break;
+ case GDK_Tab: key = 0x09; break;
+ case GDK_BackSpace: key = 0x08; break;
+ case GDK_Delete: extKey = wAccelKey_Del; break;
+ case GDK_Insert: extKey = wAccelKey_Ins; break;
+ case GDK_Home: extKey = wAccelKey_Home; break;
+ case GDK_End: extKey = wAccelKey_End; break;
+ case GDK_Page_Up: extKey = wAccelKey_Pgup; break;
+ case GDK_Page_Down: extKey = wAccelKey_Pgdn; break;
+ case GDK_Up: extKey = wAccelKey_Up; break;
+ case GDK_Down: extKey = wAccelKey_Down; break;
+ case GDK_Right: extKey = wAccelKey_Right; break;
+ case GDK_Left: extKey = wAccelKey_Left; break;
+ case GDK_F1: extKey = wAccelKey_F1; break;
+ case GDK_F2: extKey = wAccelKey_F2; break;
+ case GDK_F3: extKey = wAccelKey_F3; break;
+ case GDK_F4: extKey = wAccelKey_F4; break;
+ case GDK_F5: extKey = wAccelKey_F5; break;
+ case GDK_F6: extKey = wAccelKey_F6; break;
+ case GDK_F7: extKey = wAccelKey_F7; break;
+ case GDK_F8: extKey = wAccelKey_F8; break;
+ case GDK_F9: extKey = wAccelKey_F9; break;
+ case GDK_F10: extKey = wAccelKey_F10; break;
+ case GDK_F11: extKey = wAccelKey_F11; break;
+ case GDK_F12: extKey = wAccelKey_F12; break;
+ default: ;
+ }
+
+ if (extKey != wAccelKey_None) {
+ if ( gtkFindAccelKey( event ) == NULL ) {
+ bd->action( bd, bd->context, wActionExtKey + ((int)extKey<<8), bd->lastX, bd->lastY );
+ }
+ return TRUE;
+ } else if (key <= 0xFF && (event->state&(GDK_CONTROL_MASK|GDK_MOD1_MASK)) == 0 && bd->action) {
+ bd->action( bd, bd->context, wActionText+(key<<8), bd->lastX, bd->lastY );
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * Create
+ *
+*******************************************************************************/
+
+
+
+int XW = 0;
+int XH = 0;
+int xw, xh, cw, ch;
+
+EXPORT wDraw_p wDrawCreate(
+ wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char * helpStr,
+ long option,
+ wPos_t width,
+ wPos_t height,
+ void * context,
+ wDrawRedrawCallBack_p redraw,
+ wDrawActionCallBack_p action )
+{
+ wDraw_p bd;
+
+ bd = (wDraw_p)gtkAlloc( parent, B_DRAW, x, y, NULL, sizeof *bd, NULL );
+ bd->option = option;
+ bd->context = context;
+ bd->redraw = redraw;
+ bd->action = action;
+ gtkComputePos( (wControl_p)bd );
+
+ bd->widget = gtk_drawing_area_new();
+ gtk_drawing_area_size( GTK_DRAWING_AREA(bd->widget), width, height );
+ gtk_widget_set_size_request( GTK_WIDGET(bd->widget), width, height );
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "expose_event",
+ (GtkSignalFunc) draw_expose_event, bd);
+ gtk_signal_connect (GTK_OBJECT(bd->widget),"configure_event",
+ (GtkSignalFunc) draw_configure_event, bd);
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "motion_notify_event",
+ (GtkSignalFunc) draw_motion_event, bd);
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "button_press_event",
+ (GtkSignalFunc) draw_button_event, bd);
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "button_release_event",
+ (GtkSignalFunc) draw_button_event, bd);
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "scroll_event",
+ (GtkSignalFunc) draw_scroll_event, bd);
+ gtk_signal_connect_after (GTK_OBJECT (bd->widget), "key_press_event",
+ (GtkSignalFunc) draw_char_event, bd);
+ gtk_signal_connect (GTK_OBJECT (bd->widget), "leave_notify_event",
+ (GtkSignalFunc) draw_leave_event, bd);
+ GTK_WIDGET_SET_FLAGS(GTK_WIDGET(bd->widget), GTK_CAN_FOCUS);
+ gtk_widget_set_events (bd->widget, GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+/* | GDK_SCROLL_MASK */
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_KEY_RELEASE_MASK );
+ bd->lastColor = -1;
+ bd->dpi = 75;
+ bd->maxW = bd->w = width;
+ bd->maxH = bd->h = height;
+
+ gtk_fixed_put( GTK_FIXED(parent->widget), bd->widget, bd->realX, bd->realY );
+ gtkControlGetSize( (wControl_p)bd );
+ gtk_widget_realize( bd->widget );
+ bd->pixmap = gdk_pixmap_new( bd->widget->window, width, height, -1 );
+ bd->gc = gdk_gc_new( parent->gtkwin->window );
+ gdk_gc_copy( bd->gc, parent->gtkwin->style->base_gc[GTK_STATE_NORMAL] );
+{
+ GdkCursor * cursor;
+ cursor = gdk_cursor_new ( GDK_TCROSS );
+ gdk_window_set_cursor ( bd->widget->window, cursor);
+ gdk_cursor_destroy (cursor);
+}
+#ifdef LATER
+ if (labelStr)
+ bd->labelW = gtkAddLabel( (wControl_p)bd, labelStr );
+#endif
+ gtk_widget_show( bd->widget );
+ gtkAddButton( (wControl_p)bd );
+ gtkAddHelpString( bd->widget, helpStr );
+ return bd;
+}
+
+/*******************************************************************************
+ *
+ * BitMaps
+ *
+*******************************************************************************/
+
+wDraw_p wBitMapCreate( wPos_t w, wPos_t h, int arg )
+{
+ wDraw_p bd;
+
+ bd = (wDraw_p)gtkAlloc( gtkMainW, B_DRAW, 0, 0, NULL, sizeof *bd, NULL );
+
+ bd->lastColor = -1;
+ bd->dpi = 75;
+ bd->maxW = bd->w = w;
+ bd->maxH = bd->h = h;
+
+ bd->pixmap = gdk_pixmap_new( gtkMainW->widget->window, w, h, -1 );
+ if ( bd->pixmap == NULL ) {
+ wNoticeEx( NT_ERROR, "CreateBitMap: pixmap_new failed", "Ok", NULL );
+ return FALSE;
+ }
+ bd->gc = gdk_gc_new( gtkMainW->gtkwin->window );
+ if ( bd->gc == NULL ) {
+ wNoticeEx( NT_ERROR, "CreateBitMap: gc_new failed", "Ok", NULL );
+ return FALSE;
+ }
+ gdk_gc_copy( bd->gc, gtkMainW->gtkwin->style->base_gc[GTK_STATE_NORMAL] );
+ wDrawClear( bd );
+ return bd;
+}
+
+
+wBool_t wBitMapDelete( wDraw_p d )
+{
+ gdk_pixmap_unref( d->pixmap );
+ d->pixmap = NULL;
+ return TRUE;
+}
diff --git a/app/wlib/gtklib/gtkfont.c b/app/wlib/gtklib/gtkfont.c
new file mode 100644
index 0000000..1a707f8
--- /dev/null
+++ b/app/wlib/gtklib/gtkfont.c
@@ -0,0 +1,320 @@
+/** \file gtkfont.c
+ * Font selection and loading.
+ *
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtkfont.c,v 1.12 2009-12-07 19:31:31 m_fischer Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "wlib.h"
+#include "gtkint.h"
+#include "i18n.h"
+
+#ifndef TRUE
+#define TRUE (1)
+#define FALSE (0)
+#endif
+
+/*
+ * Macro for debug purposes. Possible debug macro values:
+ *
+ * 0 - no messages to console (use this value when building in release mode)
+ * 1 - send errors
+ * 2 - send details
+ * 3 - send more details
+ */
+#define WLIB_FONT_DEBUG 0
+
+static gchar sampleText[] = "AbCdE0129!@$&()[]{}";
+
+static GtkWidget *fontSelectionDialog;
+
+
+/*****************************************************************************
+ * FONT HANDLERS
+ */
+
+#define FW_MEDIUM (0)
+#define FW_BOLD (1)
+#define FS_REGULAR (0)
+#define FS_ITALIC (1)
+
+/* absoluteFontSize was introduced to keep the font size information synchron
+ * between the Dt.size of ctext.c and it's drop list on the status bar and
+ * the font size coming from the gtk font dialog which is located in this file */
+int absoluteFontSize = 18;
+
+struct wFont_t {
+ PangoFontDescription *fontDescription;
+ };
+
+static wFont_p standardFonts[F_HELV-F_TIMES+1][2][2];
+static wFont_p curFont = NULL;
+
+
+static void fontSelectionDialogCallback(GtkFontSelectionDialog *fontSelectionDialog, gint response, gpointer data)
+{
+ gchar *fontName;
+
+ switch (response)
+ {
+ case GTK_RESPONSE_APPLY: /* once the apply button is hidden, this should not be used */
+ case GTK_RESPONSE_OK:
+ fontName = gtk_font_selection_dialog_get_font_name(fontSelectionDialog);
+ wPrefSetString( "font", "name", fontName );
+ pango_font_description_free(curFont->fontDescription);
+ curFont->fontDescription = pango_font_description_from_string(fontName);
+ absoluteFontSize = (pango_font_description_get_size(curFont->fontDescription))/PANGO_SCALE;
+#if WLIB_FONT_DEBUG >= 2
+ fprintf(stderr, "new font selection:\n");
+ fprintf(stderr, " font name \"%s\"\n", fontName);
+ fprintf(stderr, " font size is %d\n",pango_font_description_get_size(curFont->fontDescription)/PANGO_SCALE);
+ fprintf(stderr, " font size is absolute %d\n", pango_font_description_get_size_is_absolute(curFont->fontDescription));
+#endif
+ g_free(fontName);
+ break;
+ default:
+ gtk_widget_hide(GTK_WIDGET(fontSelectionDialog));
+ }
+ if (response == GTK_RESPONSE_OK)
+ gtk_widget_hide(GTK_WIDGET(fontSelectionDialog));
+}
+
+static wBool_t fontInitted = FALSE;
+
+static wBool_t fontInit()
+{
+ const char *fontNames[] = {
+ "times 18",
+ "times italic 18",
+ "times bold 18",
+ "times bold italic 18",
+ "helvetica 18",
+ "helvetica oblique 18",
+ "helvetica bold 18",
+ "helvetica bold oblique 18",
+ };
+
+ int s = 0;
+ int i, j, k;
+
+ for (i = F_TIMES; i <= F_HELV; ++i) {
+ for (j = FW_MEDIUM; j <= FW_BOLD; ++j) {
+ for (k = FS_REGULAR; k <= FS_ITALIC; ++k) {
+ PangoFontDescription *fontDescription = pango_font_description_from_string(fontNames[s++]);
+ wFont_p standardFont = (wFont_p) malloc(sizeof(struct wFont_t));
+ standardFont->fontDescription = fontDescription;
+ standardFonts[i-F_TIMES][j][k] = standardFont;
+ }
+ }
+ }
+
+ if (curFont == NULL) {
+ curFont = (wFont_p) malloc(sizeof(struct wFont_t));
+ if (curFont == NULL)
+ return FALSE;
+ const char *fontName = wPrefGetString("font", "name");
+ curFont->fontDescription = pango_font_description_from_string(fontName ? fontName : "helvetica 18");
+ absoluteFontSize = (int) PANGO_PIXELS(pango_font_description_get_size(curFont->fontDescription));
+ }
+
+ fontInitted = TRUE;
+ return TRUE;
+}
+
+
+static double fontFactor = 1.0;
+
+#define FONTSIZE_TO_PANGOSIZE(fs) ((gint) ((fs) * (fontFactor) + .5))
+
+PangoLayout *gtkFontCreatePangoLayout(GtkWidget *widget,
+ void *cairo,
+ wFont_p fp,
+ wFontSize_t fs,
+ const char *s,
+ int *width_p,
+ int *height_p,
+ int *ascent_p,
+ int *descent_p)
+{
+ if (!fontInitted)
+ fontInit();
+
+ PangoLayout *layout = NULL;
+
+ gchar *utf8 = gtkConvertInput(s);
+
+/* RPH -- pango_cairo_create_layout() is missing in CentOS 4.8.
+ CentOS 4.8 only has GTK 2.4.13 and Pango 1.6.0 and does not have
+ libpangocairo at all.
+ pango_cairo_create_layout() was introduced with Pango 1.10. */
+
+#if PANGO_VERSION_MAJOR >= 1 && PANGO_VERSION_MINOR >= 10
+ if (cairo != NULL) {
+ layout = pango_cairo_create_layout((cairo_t *) cairo);
+ pango_layout_set_text(layout, utf8, -1);
+ }
+ else
+#endif
+ layout = gtk_widget_create_pango_layout(widget, utf8);
+
+ PangoFontDescription *fontDescription = (fp ? fp : curFont)->fontDescription;
+
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+
+ /* set attributes */
+ pango_font_description_set_size(fontDescription,
+ FONTSIZE_TO_PANGOSIZE(fs) * PANGO_SCALE);
+ pango_layout_set_font_description(layout, fontDescription);
+
+ /* get layout measures */
+ pango_layout_get_pixel_size(layout, width_p, height_p);
+ context = gtk_widget_get_pango_context(widget);
+ metrics = pango_context_get_metrics(context, fontDescription,
+ pango_context_get_language(context));
+ *ascent_p = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));
+ *descent_p = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
+ pango_font_metrics_unref(metrics);
+
+#if WLIB_FONT_DEBUG >= 3
+ fprintf(stderr, "font layout created:\n");
+ fprintf(stderr, " widget: %p\n", widget);
+ //fprintf(stderr, " font description:%p\n", fp);
+ fprintf(stderr, " font size: %f\n", fs);
+ fprintf(stderr, " layout text: \"%s\" (utf8)\n", utf8);
+ fprintf(stderr, " layout width: %d\n", *width_p);
+ fprintf(stderr, " layout height: %d\n", *height_p);
+ fprintf(stderr, " layout ascent: %d (pixels)\n", *ascent_p);
+ fprintf(stderr, " layout descent: %d (pixels)\n", *descent_p);
+#endif
+
+ return layout;
+}
+
+void gtkFontDestroyPangoLayout(PangoLayout *layout)
+{
+ g_object_unref(G_OBJECT(layout));
+}
+
+void wInitializeFonts()
+{
+ if (!fontInitted)
+ fontInit();
+}
+
+void wSelectFont(
+ const char * title )
+{
+ if (!fontInitted)
+ fontInit();
+
+ if (fontSelectionDialog == NULL) {
+ fontSelectionDialog = gtk_font_selection_dialog_new(_("Font Select"));
+ gtk_window_set_position(GTK_WINDOW(fontSelectionDialog), GTK_WIN_POS_MOUSE);
+ gtk_window_set_modal(GTK_WINDOW(fontSelectionDialog), TRUE);
+ gtk_font_selection_dialog_set_preview_text(GTK_FONT_SELECTION_DIALOG(fontSelectionDialog), sampleText);
+ g_signal_connect(G_OBJECT(fontSelectionDialog), "response", G_CALLBACK(fontSelectionDialogCallback), NULL);
+ gtk_signal_connect(GTK_OBJECT(fontSelectionDialog), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &fontSelectionDialog);
+ }
+ gtk_window_set_title(GTK_WINDOW(fontSelectionDialog), title);
+
+ if (curFont != NULL) {
+ /* the curFont description contains the latest font info
+ * which is depended on the current scale
+ * overwrite it with the absoluteFontSize */
+ pango_font_description_set_size(curFont->fontDescription,FONTSIZE_TO_PANGOSIZE(absoluteFontSize) * PANGO_SCALE);
+ gchar *fontName = pango_font_description_to_string(curFont->fontDescription);
+ gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(fontSelectionDialog), fontName);
+ g_free(fontName);
+ }
+
+ gtk_widget_show(fontSelectionDialog);
+}
+
+static wFont_p gtkSelectedFont( void )
+{
+ if (!fontInitted)
+ fontInit();
+
+ return curFont;
+}
+
+wFontSize_t wSelectedFontSize( void )
+{
+ if (!fontInitted)
+ fontInit();
+
+#if WLIB_FONT_DEBUG >= 3
+ fprintf(stderr, "the font size of current font description is: %d\n",pango_font_description_get_size(curFont->fontDescription)/PANGO_SCALE);
+ fprintf(stderr, "the font size of absoluteFontSize is: %d\n",absoluteFontSize);
+#endif
+
+ //return (wFontSize_t) PANGO_PIXELS(pango_font_description_get_size(curFont->fontDescription));
+ return absoluteFontSize;
+}
+
+void wSetSelectedFontSize(int size){
+ absoluteFontSize = (wFontSize_t)size;
+}
+
+
+const char *gtkFontTranslate( wFont_p fp )
+{
+ static gchar *fontName = NULL;
+
+ if (fontName != NULL)
+ g_free(fontName);
+
+ if (!fontInitted)
+ fontInit();
+
+ if (fp == NULL)
+ fp = gtkSelectedFont();
+
+ if (fp == NULL)
+ fp = standardFonts[0][FW_MEDIUM][FS_REGULAR];
+
+ fontName = pango_font_description_to_string(fp->fontDescription);
+
+#if WLIB_FONT_DEBUG >= 2
+ fprintf(stderr, "font translation: ");
+ fprintf(stderr, " \"%s\"\n", fontName);
+#endif
+
+ return (const char *) fontName;
+}
+
+wFont_p wStandardFont( int face, wBool_t bold, wBool_t italic )
+{
+ if (!fontInitted)
+ fontInit();
+
+ return standardFonts[face-F_TIMES][bold][italic];
+}
diff --git a/app/wlib/gtklib/gtkhelp.c b/app/wlib/gtklib/gtkhelp.c
new file mode 100644
index 0000000..b02f555
--- /dev/null
+++ b/app/wlib/gtklib/gtkhelp.c
@@ -0,0 +1,733 @@
+/** \file gtkhelp.c
+ * Balloon help ( tooltips) and main help functions.
+ *
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtkhelp.c,v 1.12 2009-10-02 04:30:32 dspagnol Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis and (C) 2007 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <stdint.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include <webkit/webkit.h>
+
+#include "gtkint.h"
+#include "i18n.h"
+
+void load_into_view (char *file, int requested_view); // Prototype to please clang.
+
+/* globals and defines related to the HTML help window */
+
+#define HTMLERRORTEXT "<html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=US-ASCII\">" \
+ "<title>Help Error</title><body><h1>Error - help information can not be found.</h1><p>" \
+ "The help information you requested cannot be found on this system.<br><pre>%s: %s</pre><p>" \
+ "Usually this is an installation problem, Make sure that XTrackCAD and the included " \
+ "HTML files are installed properly and can be found via the XTRKCADLIB environment " \
+ "variable. Also make sure that the user has sufficient access rights to read these" \
+ "files.</p></body></html>"
+
+
+#define SLIDERPOSDEFAULT 180 /**< default value for slider position */
+
+#define HTMLHELPSECTION "gtklib html help" /**< section name for html help window preferences */
+#define SLIDERPREFNAME "sliderpos" /**< name for the slider position preference */
+#define WINDOWPOSPREFNAME "position" /**< name for the window position preference */
+#define WINDOWSIZEPREFNAME "size" /**< name for the window size preference */
+
+#define BACKBUTTON "back"
+#define FORWARDBUTTON "forward"
+#define HOMEBUTTON "home"
+#define CONTENTBUTTON "contents"
+#define TOCDOC "tocDoc"
+#define CONTENTSDOC "contentsDoc"
+#define TOCVIEW "viewLeft"
+#define CONTENTSVIEW "viewRight"
+#define PANED "hpane"
+
+enum pane_views { MAIN_VIEW, CONTENTS_VIEW };
+
+#define MAXHISTORYSIZE 20
+
+/** \struct htmlHistory
+ * for storing information about the browse history
+ */
+struct htmlHistory {
+ int curShownPage; /**< index of page that is shown currently */
+ int newestPage; /**< index of newest page loaded */
+ int oldestPage; /**< index of earliest page loaded */
+ int bInUse; /**< does buffer have at least one entry */
+ char *url[ MAXHISTORYSIZE ]; /**< array of pages in history */
+};
+
+static struct htmlHistory sHtmlHistory;
+
+#define INCBUFFERINDEX( x ) (((x) + 1 ) % MAXHISTORYSIZE )
+#define DECBUFFERINDEX( x ) ((x) == 0 ? MAXHISTORYSIZE - 1 : (x)-1)
+
+static char *directory; /**< base directory for HTML files */
+
+static GtkWidget *wHelpWindow; /**< handle for the help window */
+static GtkWidget *main_view; /** handle for the help main data pane */
+static GtkWidget *contents_view; /** handle for the help contents pane */
+
+#define GLADE_HOOKUP_OBJECT(component,widget,name) \
+ g_object_set_data_full (G_OBJECT (component), name, \
+ gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref)
+
+#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \
+ g_object_set_data (G_OBJECT (component), name, widget)
+
+static GtkWidget*
+lookup_widget(GtkWidget *widget, const gchar *widget_name)
+{
+ GtkWidget *parent, *found_widget;
+
+ for (;;)
+ {
+ if (GTK_IS_MENU (widget))
+ parent = gtk_menu_get_attach_widget (GTK_MENU (widget));
+ else
+ parent = widget->parent;
+ if (!parent)
+ parent = (GtkWidget*) g_object_get_data (G_OBJECT (widget), "GladeParentKey");
+ if (parent == NULL)
+ break;
+ widget = parent;
+ }
+
+ found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget),
+ widget_name);
+ if (!found_widget)
+ g_warning ("Widget not found: %s", widget_name);
+ return found_widget;
+}
+
+
+
+/*
+ ******************************************************************************
+ *
+ * Help
+ *
+ ******************************************************************************
+ */
+
+wBool_t listHelpStrings = FALSE;
+wBool_t listMissingHelpStrings = FALSE;
+static char HelpDataKey[] = "HelpDataKey";
+
+static GtkWidget * balloonF;
+static GtkWidget * balloonPI;
+static wBalloonHelp_t * balloonHelpStrings;
+static int enableBalloonHelp = 1;
+static const char * balloonMsg;
+static wControl_p balloonB;
+static wPos_t balloonDx, balloonDy;
+static wBool_t balloonVisible = FALSE;
+static wControl_p balloonHelpB;
+static GtkTooltips * tooltips;
+
+
+void wSetBalloonHelp( wBalloonHelp_t * bh )
+{
+ balloonHelpStrings = bh;
+ if (!tooltips)
+ tooltips = gtk_tooltips_new();
+}
+
+void wEnableBalloonHelp( int enable )
+{
+ enableBalloonHelp = enable;
+ if (tooltips) {
+ if (enable)
+ gtk_tooltips_enable( tooltips );
+ else
+ gtk_tooltips_disable( tooltips );
+ }
+}
+
+
+void wControlSetBalloon( wControl_p b, wPos_t dx, wPos_t dy, const char * msg )
+{
+ PangoLayout * layout;
+ wPos_t x, y;
+ wPos_t w, h;
+ wPos_t xx, yy;
+ const char * msgConverted;
+ if (balloonVisible && balloonB == b &&
+ balloonDx == dx && balloonDy == dy && balloonMsg == msg )
+ return;
+
+ if ( msg == NULL ) {
+ if ( balloonF != NULL ) {
+ gtk_widget_hide( balloonF );
+ balloonVisible = FALSE;
+ }
+ balloonMsg = "";
+ return;
+ }
+ msgConverted = gtkConvertInput(msg);
+ if ( balloonF == NULL ) {
+ balloonF = gtk_window_new( GTK_WINDOW_POPUP );
+ gtk_window_set_policy( GTK_WINDOW (balloonF), FALSE, FALSE, TRUE );
+ gtk_widget_realize( balloonF );
+ balloonPI = gtk_label_new(msgConverted);
+ gtk_container_add( GTK_CONTAINER(balloonF), balloonPI );
+ gtk_container_border_width( GTK_CONTAINER(balloonF), 1 );
+ gtk_widget_show( balloonPI );
+ } else {
+ gtk_label_set( GTK_LABEL(balloonPI), msgConverted );
+ }
+ balloonDx = dx;
+ balloonDy = dy;
+ balloonB = b;
+ balloonMsg = msg;
+ gtk_widget_hide( balloonF );
+ layout = gtk_widget_create_pango_layout( balloonPI, msgConverted );
+ pango_layout_get_pixel_size( layout, &w, &h );
+ g_object_unref(G_OBJECT(layout));
+ h = 16;
+ gdk_window_get_position( GTK_WIDGET(b->parent->gtkwin)->window, &x, &y );
+ gdk_window_get_origin( GTK_WIDGET(b->parent->gtkwin)->window, &x, &y );
+ x += b->realX + dx;
+ y += b->realY + b->h - dy;
+ xx = gdk_screen_width();
+ yy = gdk_screen_height();
+ if ( x < 0 ) {
+ x = 0;
+ } else if ( x+w > xx ) {
+ x = xx - w;
+ }
+ if ( y < 0 ) {
+ y = 0;
+ } else if ( y+h > yy ) {
+ y = yy - h ;
+ }
+ gtk_widget_set_usize( balloonPI, w, h );
+ gtk_widget_set_usize( balloonF, w+2, h+2 );
+ gtk_widget_show( balloonF );
+ gtk_widget_set_uposition( balloonF, x, y );
+ /*gtk_widget_popup( balloonF, x, y );*/
+ gdk_draw_rectangle( balloonF->window, balloonF->style->fg_gc[GTK_STATE_NORMAL], FALSE, 0, 0, w+1, h+1 );
+ gtk_widget_queue_resize( GTK_WIDGET(balloonF) );
+ /*gtk_widget_set_uposition( balloonF, x, y );*/
+ balloonVisible = TRUE;
+}
+
+
+void wControlSetBalloonText(
+ wControl_p b,
+ const char * label )
+{
+ const char * helpStr;
+ if ( b->widget == NULL) abort();
+ helpStr = (char*)gtk_object_get_data( GTK_OBJECT(b->widget), HelpDataKey );
+ if ( helpStr == NULL ) helpStr = "NoHelp";
+ if (tooltips)
+ gtk_tooltips_set_tip( tooltips, b->widget, label, helpStr );
+}
+
+
+EXPORT void gtkHelpHideBalloon( void )
+{
+ if (balloonF != NULL && balloonVisible) {
+ gtk_widget_hide( balloonF );
+ balloonVisible = FALSE;
+ }
+}
+
+#ifdef XV_help
+static Notify_value showBalloonHelp( Notify_client client, int which )
+{
+ wControlSetBalloon( balloonHelpB, 0, 0, balloonHelpString );
+ return NOTIFY_DONE;
+}
+#endif
+
+static wWin_p balloonHelp_w;
+static wPos_t balloonHelp_x;
+static wPos_t balloonHelp_y;
+static void prepareBalloonHelp( wWin_p w, wPos_t x, wPos_t y )
+{
+#ifdef XV
+ wControl_p b;
+ char * hs;
+ int appNameLen = strlen( wAppName ) + 1;
+ if (w == NULL)
+ return;
+#ifdef LATER
+ if (!enableBalloonHelp)
+ return;
+#endif
+ if (!balloonHelpStrings)
+ return;
+
+ balloonHelp_w = w;
+ balloonHelp_x = x;
+ balloonHelp_y = y;
+
+ for ( b=w->first; b; b=b->next ) {
+ switch ( b->type ) {
+ case B_BUTTON:
+ case B_CANCEL:
+ case B_TEXT:
+ case B_INTEGER:
+ case B_LIST:
+ case B_DROPLIST:
+ case B_COMBOLIST:
+ case B_RADIO:
+ case B_TOGGLE:
+ case B_DRAW:
+ case B_MULTITEXT:
+ if (x >= b->realX && x < b->realX+b->w &&
+ y >= b->realY && y < b->realY+b->h ) {
+ hs = (char*)gtk_get( b->panel_item, XV_HELP_DATA );
+ if ( hs ) {
+ hs += appNameLen;
+ for ( currBalloonHelp = balloonHelpStrings; currBalloonHelp->name && strcmp(currBalloonHelp->name,hs) != 0; currBalloonHelp++ );
+ if (currBalloonHelp->name && balloonHelpB != b && currBalloonHelp->value ) {
+ balloonHelpB = b;
+ balloonHelpString = currBalloonHelp->value;
+ if (enableBalloonHelp) {
+ wControlSetBalloon( balloonHelpB, 0, 0, balloonHelpString );
+ /*setTimer( balloonHelpTimeOut, showBalloonHelp );*/
+ } else {
+ /*resetBalloonHelp();*/
+ }
+ }
+ return;
+ }
+ }
+ default:
+ ;
+ }
+ }
+ cancelTimer( showBalloonHelp );
+ resetBalloonHelp();
+#endif
+}
+
+
+void wBalloonHelpUpdate( void )
+{
+ balloonHelpB = NULL;
+ balloonMsg = NULL;
+ prepareBalloonHelp( balloonHelp_w, balloonHelp_x, balloonHelp_y );
+}
+
+
+void gtkAddHelpString(
+ GtkWidget * widget,
+ const char * helpStr )
+{
+ int rc;
+ char *string;
+ wBalloonHelp_t * bhp;
+ rc = 0;
+ if (helpStr==NULL || *helpStr==0)
+ return;
+ if ( balloonHelpStrings == NULL )
+ return;
+ for ( bhp = balloonHelpStrings; bhp->name && strcmp(bhp->name,helpStr) != 0; bhp++ );
+ if (listMissingHelpStrings && !bhp->name) {
+ printf( "Missing Help String: %s\n", helpStr );
+ return;
+ }
+ string = malloc( strlen(wAppName) + 5 + strlen(helpStr) + 1 );
+ sprintf( string, "%sHelp/%s", wAppName, helpStr );
+ if (tooltips)
+ gtk_tooltips_set_tip( tooltips, widget, _(bhp->value), string );
+ gtk_object_set_data( GTK_OBJECT( widget ), HelpDataKey, string );
+ if (listHelpStrings)
+ printf( "HELPSTR - %s\n", string );
+}
+
+
+EXPORT const char * wControlGetHelp(
+ wControl_p b ) /* Control */
+/*
+Returns the help topic string associated with <b>.
+*/
+{
+ const char * helpStr;
+ helpStr = (char*)gtk_object_get_data( GTK_OBJECT(b->widget), HelpDataKey );
+ return helpStr;
+}
+
+
+EXPORT void wControlSetHelp(
+ wControl_p b, /* Control */
+ const char * help ) /* topic string */
+/*
+Set the help topic string for <b> to <help>.
+*/
+{
+ const char * helpStr;
+ if (b->widget == 0) abort();
+ helpStr = wControlGetHelp(b);
+ if (tooltips)
+ gtk_tooltips_set_tip( tooltips, b->widget, help, helpStr );
+}
+
+
+/**
+ * create a new horizontal pane and place it into container.
+ * The separator position is read from the resource configuration and set accordingly.
+ * Also a callback is specified that will be executed when the slider has been moved.
+ *
+ * \PARAM container IN the container into which the pane will be stuffed.
+ * \PARAM property IN the name of the property for the slider position
+ *
+ * \return the HPaned handle
+ */
+
+GtkWidget *
+CreateHPaned( GtkBox *container, char *property )
+{
+ GtkWidget *hpaned;
+ long posSlider;
+
+ /* the horizontal slider */
+ hpaned = gtk_hpaned_new ();
+ gtk_container_set_border_width (GTK_CONTAINER (hpaned), 6);
+
+ wPrefGetInteger( HTMLHELPSECTION, SLIDERPREFNAME, &posSlider, SLIDERPOSDEFAULT );
+ gtk_paned_set_position (GTK_PANED (hpaned), (int)posSlider);
+
+ /* pack the horizontal slider into the main window */
+ gtk_box_pack_start( container, hpaned, TRUE, TRUE, 0 );
+ gtk_widget_show( hpaned );
+
+ return( hpaned );
+}
+
+/**
+ * Handler for the delete-event issued on the help window.We are saving window
+ * information (eg. position) and are hiding the window instead of closing it.
+ *
+ * \PARAM win IN the window to be destroyed
+ * \PARAM event IN unused
+ * \PARAM ptr IN unused
+ *
+ * \RETURN FALSE
+ */
+
+static gboolean
+DestroyHelpWindow( GtkWidget *win, GdkEvent *event, void *ptr )
+{
+ int i;
+ GtkWidget *widget;
+ char tmp[ 20 ];
+
+ gint x, y;
+
+ /* get the slider position and save it */
+ widget = lookup_widget( win, PANED );
+ i = gtk_paned_get_position( GTK_PANED( widget ));
+ wPrefSetInteger( HTMLHELPSECTION, SLIDERPREFNAME, i );
+
+ /* get the window position */
+ gtk_window_get_position( (GtkWindow *)win, &x, &y );
+ sprintf( tmp, "%d %d", x, y );
+ wPrefSetString( HTMLHELPSECTION, WINDOWPOSPREFNAME, tmp );
+
+ /* get the window size */
+ gtk_window_get_size( (GtkWindow *)win , &x, &y );
+ sprintf( tmp, "%d %d", x, y );
+ wPrefSetString( HTMLHELPSECTION, WINDOWSIZEPREFNAME, tmp );
+
+ gtk_widget_hide( win );
+ return TRUE;
+}
+
+void back_button_clicked(GtkWidget *widget, gpointer data) {
+ webkit_web_view_go_back(WEBKIT_WEB_VIEW(data));
+}
+
+void forward_button_clicked(GtkWidget *widget, gpointer data) {
+ webkit_web_view_go_forward(WEBKIT_WEB_VIEW(data));
+}
+
+void home_button_clicked(GtkWidget *widget, gpointer data) {
+ load_into_view("index.html", MAIN_VIEW);
+}
+
+/* Toggles the contents pane */
+void contents_button_clicked(GtkWidget *widget, gpointer data) {
+ if (gtk_paned_get_position(GTK_PANED(data)) < 50) {
+ gtk_paned_set_position(GTK_PANED(data), 370);
+ }
+ else {
+ gtk_paned_set_position(GTK_PANED(data), 0);
+ }
+}
+
+gboolean contents_click_handler(
+ WebKitWebView *web_view,
+ WebKitWebFrame *frame,
+ WebKitNetworkRequest *request,
+ WebKitWebNavigationAction *navigation_action,
+ WebKitWebPolicyDecision *policy_decision,
+ gpointer data) {
+
+ webkit_web_view_load_uri(WEBKIT_WEB_VIEW(data), webkit_network_request_get_uri(request));
+
+ return TRUE;
+}
+
+/**
+ * Initialize the buttons for the help window
+ */
+void initialize_buttons (GtkWidget *main_vbox, GtkWidget *content_hpane) {
+ GtkWidget *buttons_hbuttonbox;
+ GtkWidget *back_button;
+ GtkWidget *forward_button;
+ GtkWidget *home_button;
+ GtkWidget *contents_button;
+
+ // define and attach signals to buttons
+ back_button = gtk_button_new_with_label(_("Back"));
+ g_signal_connect(back_button, "clicked", G_CALLBACK(back_button_clicked), G_OBJECT(main_view));
+
+ forward_button = gtk_button_new_with_label(_("Forward"));
+ g_signal_connect(forward_button, "clicked", G_CALLBACK(forward_button_clicked), G_OBJECT(main_view));
+
+ home_button = gtk_button_new_with_label(_("Home"));
+ g_signal_connect(home_button, "clicked", G_CALLBACK(home_button_clicked), G_OBJECT(main_view));
+
+ contents_button = gtk_button_new_with_label(_("Contents"));
+ g_signal_connect(contents_button, "clicked", G_CALLBACK(contents_button_clicked), G_OBJECT(content_hpane));
+
+ // button layout
+ buttons_hbuttonbox = gtk_hbutton_box_new();
+ gtk_container_add(GTK_CONTAINER(buttons_hbuttonbox), back_button);
+ gtk_container_add(GTK_CONTAINER(buttons_hbuttonbox), forward_button);
+ gtk_container_add(GTK_CONTAINER(buttons_hbuttonbox), home_button);
+ gtk_container_add(GTK_CONTAINER(buttons_hbuttonbox), contents_button);
+ gtk_box_pack_start(GTK_BOX(main_vbox), buttons_hbuttonbox, FALSE, TRUE, 0);
+ gtk_box_set_spacing(GTK_BOX(buttons_hbuttonbox), 6);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(buttons_hbuttonbox), GTK_BUTTONBOX_START);
+
+ /* Store pointers to all widgets, for use by lookup_widget(). */
+ GLADE_HOOKUP_OBJECT (main_view, back_button, BACKBUTTON);
+ GLADE_HOOKUP_OBJECT (main_view, forward_button, FORWARDBUTTON);
+ GLADE_HOOKUP_OBJECT (main_view, home_button, HOMEBUTTON);
+ GLADE_HOOKUP_OBJECT (main_view, contents_button, CONTENTBUTTON);
+}
+
+/**
+ * Create the help windows including all contained widgets and the needed HTML documents.
+ *
+ * \RETURN handle of the created window.
+ */
+
+GtkWidget*
+CreateHelpWindow (void)
+{
+ GtkWidget *main_vbox;
+ GtkWidget *main_view_scroller;
+ GtkWidget *contents_view_scroller;
+ GtkWidget *content_hpane;
+
+ int width;
+ int height;
+ int x, y;
+ int w = 0, h = 0;
+ const char *pref;
+ char title[100];
+
+ wHelpWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ width = gdk_screen_get_width( gtk_window_get_screen( (GtkWindow *)wHelpWindow ));
+ height = gdk_screen_get_height( gtk_window_get_screen( (GtkWindow *)wHelpWindow ));
+
+ pref = wPrefGetString( HTMLHELPSECTION, WINDOWSIZEPREFNAME );
+ if( pref ) {
+ sscanf( pref, "%d %d", &w, &h );
+ if( w > width )
+ w = width;
+ if( h > height )
+ h = height;
+ }
+ else {
+ w = ( width * 2 )/ 5;
+ h = height - 100;
+ }
+
+ pref = wPrefGetString( HTMLHELPSECTION, WINDOWPOSPREFNAME );
+ if( pref ) {
+ sscanf( pref, "%d %d", &x, &y );
+ if( y > height - h )
+ y = height - h;
+
+ if( x > width - w )
+ x = width - w;
+ }
+ else {
+ x = ( width * 3 ) / 5 - 10;
+ y = 70;
+ }
+
+ gtk_window_resize( (GtkWindow *)wHelpWindow, w, h );
+ gtk_window_move( (GtkWindow *)wHelpWindow, x, y );
+
+ gtk_window_set_title (GTK_WINDOW (wHelpWindow), "XTrkCad Help");
+
+ g_signal_connect( G_OBJECT( wHelpWindow ), "delete-event", G_CALLBACK( DestroyHelpWindow ), NULL );
+
+ main_view_scroller = gtk_scrolled_window_new(NULL, NULL);
+ contents_view_scroller = gtk_scrolled_window_new(NULL, NULL);
+ main_view = webkit_web_view_new();
+ contents_view = webkit_web_view_new();
+ // must be done here as it gets locked down later
+ load_into_view ("contents.html", CONTENTS_VIEW);
+ gtk_widget_set_size_request(GTK_WIDGET(wHelpWindow), x, y);
+
+ main_vbox = gtk_vbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(wHelpWindow), main_vbox);
+
+ gtk_container_add(GTK_CONTAINER(main_view_scroller), main_view);
+
+ gtk_container_add(GTK_CONTAINER(contents_view_scroller), contents_view);
+
+ content_hpane = gtk_hpaned_new();
+ initialize_buttons(main_vbox, content_hpane);
+ gtk_container_add(GTK_CONTAINER(content_hpane), contents_view_scroller);
+ gtk_container_add(GTK_CONTAINER(content_hpane), main_view_scroller);
+ gtk_box_pack_start(GTK_BOX(main_vbox), content_hpane, TRUE, TRUE, 0);
+
+ gtk_paned_set_position(GTK_PANED(content_hpane), 370);
+
+ g_signal_connect(contents_view, "navigation-policy-decision-requested", G_CALLBACK(contents_click_handler), G_OBJECT(main_view));
+
+ /* Store pointers to all widgets, for use by lookup_widget(). */
+ GLADE_HOOKUP_OBJECT_NO_REF (wHelpWindow, wHelpWindow, "wHelpWindow");
+ GLADE_HOOKUP_OBJECT (wHelpWindow, content_hpane, PANED );
+ GLADE_HOOKUP_OBJECT (wHelpWindow, contents_view, TOCVIEW );
+ GLADE_HOOKUP_OBJECT (wHelpWindow, main_view, CONTENTSVIEW );
+
+ return wHelpWindow;
+}
+
+void load_into_view (char *file, int requested_view) {
+ GtkWidget *view;
+
+ switch (requested_view) {
+ case MAIN_VIEW:
+ view = main_view;
+ break;
+ case CONTENTS_VIEW:
+ view = contents_view;
+ break;
+ default:
+ printf("*** error, could not find view");
+ break;
+ }
+
+ char fileToLoad[100] = "file://";
+ strcat(fileToLoad,directory);
+ strcat(fileToLoad,file);
+
+ //debug printf("*** loading %s into pane %d.\n", fileToLoad, requested_view);
+ webkit_web_view_load_uri(WEBKIT_WEB_VIEW(view), fileToLoad);
+}
+
+/**
+ * Invoke the help system to display help for <topic>.
+ *
+ * \param topic IN topic string
+ */
+
+EXPORT void wHelp( const char * topic )
+{
+ char *htmlFile;
+
+ if( !wHelpWindow )
+ {
+ directory = malloc( BUFSIZ );
+ assert( directory != NULL );
+
+ sprintf( directory, "%s/html/", wGetAppLibDir());
+
+ wHelpWindow = CreateHelpWindow();
+ /* load the default content */
+ load_into_view ("index.html", MAIN_VIEW);
+ }
+
+ /* need space for the 'html' extension plus dot plus \0 */
+ htmlFile = malloc( strlen( topic ) + 6 );
+ assert( htmlFile != NULL );
+
+ sprintf( htmlFile, "%s.html", topic );
+
+ load_into_view (htmlFile, MAIN_VIEW);
+ gtk_widget_show_all(wHelpWindow);
+ gtk_window_present(GTK_WINDOW(wHelpWindow));
+}
+
+/**
+ * Handle the commands issued from the Help drop-down. Currently, we only have a table
+ * of contents, but search etc. might be added in the future.
+ *
+ * \PARAM data IN command value
+ *
+ */
+
+static void
+DoHelpMenu( void *data )
+{
+ int func = (intptr_t)data;
+
+ switch( func )
+ {
+ case 1:
+ wHelp( "index" );
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+/**
+ * Add the entries for Help to the drop-down.
+ *
+ * \PARAM m IN handle of drop-down
+ *
+ */
+
+void wMenuAddHelp( wMenu_p m )
+{
+ wMenuPushCreate( m, NULL, _("&Contents"), 0, DoHelpMenu, (void*)1 );
+}
diff --git a/app/wlib/gtklib/gtkint.h b/app/wlib/gtklib/gtkint.h
new file mode 100644
index 0000000..4cbeace
--- /dev/null
+++ b/app/wlib/gtklib/gtkint.h
@@ -0,0 +1,180 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtkint.h,v 1.8 2009-12-12 17:16:08 m_fischer Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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.
+ */
+
+#ifndef GTKINT_H
+#define GTKINT_H
+#include "wlib.h"
+
+#include "gdk/gdk.h"
+#include "gtk/gtk.h"
+
+#define EXPORT
+
+#ifdef WINDOWS
+#define strcasecmp _stricmp
+#endif
+
+#include "dynarr.h"
+
+extern wWin_p gtkMainW;
+
+typedef enum {
+ W_MAIN, W_POPUP,
+ B_BUTTON, B_CANCEL, B_POPUP, B_TEXT, B_INTEGER, B_FLOAT,
+ B_LIST, B_DROPLIST, B_COMBOLIST,
+ B_RADIO, B_TOGGLE,
+ B_DRAW, B_MENU, B_MULTITEXT, B_MESSAGE, B_LINES,
+ B_MENUITEM, B_BOX,
+ B_BITMAP } wType_e;
+
+typedef void (*repaintProcCallback_p)( wControl_p );
+typedef void (*doneProcCallback_p)( wControl_p b );
+typedef void (*setTriggerCallback_p)( wControl_p b );
+#define WOBJ_COMMON \
+ wType_e type; \
+ wControl_p next; \
+ wControl_p synonym; \
+ wWin_p parent; \
+ wPos_t origX, origY; \
+ wPos_t realX, realY; \
+ wPos_t labelW; \
+ wPos_t w, h; \
+ long option; \
+ const char * labelStr; \
+ repaintProcCallback_p repaintProc; \
+ GtkWidget * widget; \
+ GtkWidget * label; \
+ doneProcCallback_p doneProc; \
+ void * data;
+
+struct wWin_t {
+ WOBJ_COMMON
+ GtkWidget *gtkwin; /**< GTK window */
+ wPos_t lastX, lastY;
+ wControl_p first, last;
+ wWinCallBack_p winProc; /**< window procedure */
+ wBool_t shown; /**< visibility state */
+ const char * nameStr; /**< window name (not title) */
+ GtkWidget * menubar; /**< menubar handle (if exists) */
+ GdkGC * gc; /**< graphics context */
+ int gc_linewidth; /**< ??? */
+ wBool_t busy;
+ int modalLevel;
+ };
+
+struct wControl_t {
+ WOBJ_COMMON
+ };
+
+#define gtkIcon_bitmap (1)
+#define gtkIcon_pixmap (2)
+struct wIcon_t {
+ int gtkIconType;
+ wPos_t w;
+ wPos_t h;
+ wDrawColor color;
+ const void * bits;
+ };
+
+extern char wAppName[];
+extern char wConfigName[];
+extern wDrawColor wDrawColorWhite;
+extern wDrawColor wDrawColorBlack;
+
+/* gtkmisc.c */
+void * gtkAlloc( wWin_p, wType_e, wPos_t, wPos_t, const char *, int, void * );
+void gtkComputePos( wControl_p );
+void gtkAddButton( wControl_p );
+int gtkAddLabel( wControl_p, const char * );
+void gtkControlGetSize( wControl_p );
+struct accelData_t;
+struct accelData_t * gtkFindAccelKey( GdkEventKey * event );
+wBool_t gtkHandleAccelKey( GdkEventKey * );
+wBool_t catch_shift_ctrl_alt_keys( GtkWidget *, GdkEventKey *, void * );
+void gtkSetReadonly( wControl_p, wBool_t );
+wControl_p gtkGetControlFromPos( wWin_p, wPos_t, wPos_t );
+void gtkSetTrigger( wControl_p, setTriggerCallback_p );
+GdkPixmap * gtkMakeIcon( GtkWidget *, wIcon_p, GdkBitmap ** );
+char * gtkConvertInput( const char * );
+char * gtkConvertOutput( const char * );
+
+/* gtkwindow.c */
+void gtkDoModal( wWin_p, wBool_t );
+
+/* gtkhelp.c */
+void load_into_view( char *, int );
+void gtkAddHelpString( GtkWidget *, const char * );
+void gtkHelpHideBalloon( void );
+
+/* gtksimple.c */
+void gtkDrawBox( wWin_p, wBoxType_e, wPos_t, wPos_t, wPos_t, wPos_t );
+void gtkLineShow( wLine_p, wBool_t );
+
+/* gktlist.c */
+void gtkListShow( wList_p, wBool_t );
+void gtkListSetPos( wList_p );
+void gtkListActive( wList_p, wBool_t );
+void gtkDropListPos( wList_p );
+
+/* gtktext.c */
+void gtkTextFreeze( wText_p );
+void gtkTextThaw( wText_p );
+
+/* gtkfont.c */
+const char * gtkFontTranslate( wFont_p );
+PangoLayout *gtkFontCreatePangoLayout( GtkWidget *, void *cairo,
+ wFont_p, wFontSize_t, const char *,
+ int *, int *, int *, int * );
+
+/* gtkbutton.c */
+void gtkButtonDoAction( wButton_p );
+void gtkSetLabel( GtkWidget*, long, const char *, GtkLabel**, GtkWidget** );
+
+/* gtkcolor.c */
+void gtkGetColorMap( void );
+GdkColor * gtkGetColor( wDrawColor, wBool_t );
+int gtkGetColorChar( wDrawColor );
+void gtkPrintColorMap( FILE *, int, int );
+int gtkMapPixel( long );
+
+/* psprint.c */
+typedef struct {
+ wIndex_t cmdOrFile;
+ FILE * f;
+ } wPrinterStream_t;
+typedef wPrinterStream_t * wPrinterStream_p;
+
+wPrinterStream_p wPrinterOpen( void );
+void wPrinterWrite( wPrinterStream_p p, char * buff, int siz );
+void wPrinterClose( wPrinterStream_p );
+void psPrintLine( wPos_t, wPos_t, wPos_t, wPos_t,
+ wDrawWidth, wDrawLineType_e, wDrawColor, wDrawOpts );
+void psPrintArc( wPos_t, wPos_t, wPos_t, double, double, int,
+ wDrawWidth, wDrawLineType_e, wDrawColor, wDrawOpts );
+void psPrintString( wPos_t x, wPos_t y, double a, char * s,
+ wFont_p fp, double fs, wDrawColor color, wDrawOpts opts );
+
+void psPrintFillRectangle( wPos_t, wPos_t, wPos_t, wPos_t, wDrawColor, wDrawOpts );
+void psPrintFillPolygon( wPos_t [][2], int, wDrawColor, wDrawOpts );
+void psPrintFillCircle( wPos_t, wPos_t, wPos_t, wDrawColor, wDrawOpts );
+
+#endif
diff --git a/app/wlib/gtklib/gtklist.c b/app/wlib/gtklib/gtklist.c
new file mode 100644
index 0000000..b2c9799
--- /dev/null
+++ b/app/wlib/gtklib/gtklist.c
@@ -0,0 +1,1109 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtklist.c,v 1.4 2009-05-30 11:11:26 m_fischer Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include "gtkint.h"
+#include "i18n.h"
+
+#define ROW_HEIGHT (15)
+#define PIX_TEXT_SEP (5)
+static char ListItemDataKey[] = "ListItemDataKey";
+
+/*
+ *****************************************************************************
+ *
+ * List Boxes
+ *
+ *****************************************************************************
+ */
+
+typedef struct wListItem_t * wListItem_p;
+
+struct wList_t {
+ WOBJ_COMMON
+ GtkWidget *list;
+ int count;
+ int number;
+ int colCnt;
+ wPos_t *colWidths;
+ wBool_t *colRightJust;
+ int last;
+ wPos_t listX;
+ long * valueP;
+ wListCallBack_p action;
+ int recursion;
+ int editted;
+ int editable;
+ };
+
+
+struct wListItem_t {
+ wBool_t active;
+ void * itemData;
+ const char * label;
+ GtkLabel * labelG;
+ wBool_t selected;
+ wList_p listP;
+ };
+
+
+static wListItem_p getListItem(
+ wList_p b,
+ wIndex_t inx,
+ GList ** childR )
+{
+ GList * child;
+ GtkObject * listItem;
+ wListItem_p id_p;
+ if (childR)
+ *childR = NULL;
+ if (b->list == 0) abort();
+ if (inx < 0)
+ return NULL;
+ if ( b->type == B_LIST )
+ return (wListItem_p)gtk_clist_get_row_data( GTK_CLIST(b->list), inx );
+
+ for ( child=GTK_LIST(b->list)->children; inx>0&&child; child=child->next,inx-- );
+ if (child==NULL) {
+ fprintf( stderr, "wListGetValues - End Of List\n" );
+ return NULL;
+ }
+ listItem = GTK_OBJECT(child->data);
+ id_p = (wListItem_p)gtk_object_get_data(listItem, ListItemDataKey );
+ if (id_p==NULL) {
+ fprintf( stderr, "wListGetValues - id_p == NULL\n" );
+ return NULL;
+ }
+ if (childR)
+ *childR = child;
+ return id_p;
+}
+
+
+EXPORT void wListClear(
+ wList_p b ) /* List */
+/*
+Remove all entries from the list <b>.
+*/
+{
+ if (b->list == 0) abort();
+ b->recursion++;
+ if ( b->type == B_DROPLIST )
+ gtk_list_clear_items( GTK_LIST(b->list), 0, b->count );
+ else
+ gtk_clist_clear( GTK_CLIST(b->list) );
+ b->recursion--;
+ b->last = -1;
+ b->count = 0;
+}
+
+
+EXPORT void wListSetIndex(
+ wList_p b, /* List */
+ int val ) /* Index */
+/*
+Makes the <val>th entry (0-origin) the current selection.
+If <val> if '-1' then no entry is selected.
+*/
+{
+ int cur;
+ wListItem_p id_p;
+
+ if (b->widget == 0) abort();
+ b->recursion++;
+ cur = b->last;
+ if ( b->type == B_DROPLIST ) {
+ if ((b->option&BL_NONE)!=0 && val < 0) {
+ if (cur != -1) {
+ gtk_list_unselect_item( GTK_LIST(b->list), cur );
+ }
+ } else {
+ if (cur != -1)
+ gtk_list_unselect_item( GTK_LIST(b->list), cur );
+ if (val != -1)
+ gtk_list_select_item( GTK_LIST(b->list), val );
+ }
+ } else {
+ if (cur != -1) {
+ gtk_clist_unselect_row( GTK_CLIST(b->list), cur, -1 );
+ id_p = getListItem( b, cur, NULL );
+ if ( id_p )
+ id_p->selected = FALSE;
+ }
+ if (val != -1) {
+ gtk_clist_select_row( GTK_CLIST(b->list), val, -1 );
+ id_p = getListItem( b, val, NULL );
+ if ( id_p )
+ id_p->selected = TRUE;
+ }
+ }
+ b->last = val;
+ b->recursion--;
+}
+
+
+static void parseLabelStr(
+ const char * labelStr,
+ int count,
+ char * * * texts )
+{
+ static char * labelBuffer;
+ static int labelBufferLen = 0;
+ static char * * textBuffer;
+ static int textBufferCount = 0;
+ char * cp;
+ int col;
+ int len;
+
+ labelStr = gtkConvertInput( labelStr );
+ len = strlen(labelStr)+1;
+ if ( len > labelBufferLen ) {
+ if ( labelBuffer )
+ labelBuffer = realloc( labelBuffer, len );
+ else
+ labelBuffer = (char*)malloc( len );
+ labelBufferLen = len;
+ }
+ if ( count > textBufferCount ) {
+ if ( textBuffer )
+ textBuffer = (char**)malloc( count * sizeof *textBuffer );
+ else
+ textBuffer = (char**)realloc( textBuffer, count * sizeof *textBuffer );
+ textBufferCount = count;
+ }
+
+ strcpy( labelBuffer, labelStr );
+ cp = labelBuffer;
+ for ( col=0; cp && col<count; col++ ) {
+ textBuffer[col] = cp;
+ cp = strchr( cp, '\t' );
+ if ( cp != NULL )
+ *cp++ = '\0';
+ }
+ for ( ; col<count; col++ )
+ textBuffer[col] = "";
+ *texts = textBuffer;
+}
+
+
+EXPORT void wListSetValue(
+ wList_p bl,
+ const char * val )
+{
+ if (bl->list==NULL) abort();
+ bl->recursion++;
+ if (bl->type == B_DROPLIST) {
+ bl->editted = TRUE;
+ gtk_entry_set_text( GTK_ENTRY(GTK_COMBO(bl->widget)->entry), val );
+ if (bl->action) {
+ bl->action( -1, val, 0, bl->data, NULL );
+ }
+ }
+ bl->recursion--;
+}
+
+
+EXPORT wIndex_t wListFindValue(
+ wList_p b,
+ const char * val )
+{
+ GList * child;
+ GtkObject * listItem;
+ wListItem_p id_p;
+ wIndex_t inx;
+
+ if (b->list==NULL) abort();
+ if (b->type == B_DROPLIST) {
+ for ( child=GTK_LIST(b->list)->children,inx=0; child; child=child->next,inx++ ) {
+ listItem = GTK_OBJECT(child->data);
+ id_p = (wListItem_p)gtk_object_get_data(listItem, ListItemDataKey );
+ if ( id_p && id_p->label && strcmp( id_p->label, val ) == 0 ) {
+ return inx;
+ }
+ }
+ } else {
+ for ( inx=0; inx<b->count; inx++ ) {
+ id_p = (wListItem_p)gtk_clist_get_row_data( GTK_CLIST(b->list), inx );
+ if ( id_p && id_p->label && strcmp( id_p->label, val ) == 0 )
+ return inx;
+ }
+ }
+ return -1;
+}
+
+EXPORT wIndex_t wListGetIndex(
+ wList_p b ) /* List */
+/*
+Returns the current selected list entry.
+If <val> if '-1' then no entry is selected.
+*/
+{
+ if (b->list == 0) abort();
+ return b->last;
+}
+
+EXPORT wIndex_t wListGetValues(
+ wList_p bl,
+ char * labelStr,
+ int labelSize,
+ void * * listDataRet,
+ void * * itemDataRet )
+
+{
+ wListItem_p id_p;
+ wIndex_t inx = bl->last;
+ const char * entry_value = "";
+ void * item_data = NULL;
+
+ if ( bl->list == 0 ) abort();
+ if ( bl->type == B_DROPLIST && bl->editted ) {
+ entry_value = gtk_entry_get_text( GTK_ENTRY(GTK_COMBO(bl->widget)->entry) );
+ inx = bl->last = -1;
+ } else {
+ inx = bl->last;
+ if (inx >= 0) {
+ id_p = getListItem( bl, inx, NULL );
+ if (id_p==NULL) {
+ fprintf( stderr, "wListGetValues - id_p == NULL\n" );
+ inx = -1;
+ } else {
+ entry_value = id_p->label;
+ item_data = id_p->itemData;
+ }
+ }
+ }
+ if ( labelStr ) {
+ strncpy( labelStr, entry_value, labelSize );
+ }
+ if ( listDataRet )
+ *listDataRet = bl->data;
+ if ( itemDataRet )
+ *itemDataRet = item_data;
+ return bl->last;
+}
+
+
+EXPORT wIndex_t wListGetCount(
+ wList_p b )
+{
+ return b->count;
+}
+
+
+EXPORT void * wListGetItemContext(
+ wList_p b,
+ wIndex_t inx )
+{
+ wListItem_p id_p;
+ if ( inx < 0 )
+ return NULL;
+ id_p = getListItem( b, inx, NULL );
+ if ( id_p )
+ return id_p->itemData;
+ else
+ return NULL;
+}
+
+
+EXPORT wBool_t wListGetItemSelected(
+ wList_p b,
+ wIndex_t inx )
+{
+ wListItem_p id_p;
+ if ( inx < 0 )
+ return FALSE;
+ id_p = getListItem( b, inx, NULL );
+ if ( id_p )
+ return id_p->selected;
+ else
+ return FALSE;
+}
+
+
+EXPORT wIndex_t wListGetSelectedCount(
+ wList_p b )
+{
+ wIndex_t selcnt, inx;
+ for ( selcnt=inx=0; inx<b->count; inx++ )
+ if ( wListGetItemSelected( b, inx ) )
+ selcnt++;
+ return selcnt;
+}
+
+
+EXPORT wBool_t wListSetValues(
+ wList_p b,
+ wIndex_t row,
+ const char * labelStr,
+ wIcon_p bm,
+ void *itemData )
+
+{
+ wListItem_p id_p;
+ GList * child;
+ GdkPixmap *pixmap;
+ GdkBitmap *bitmap = NULL;
+ char ** texts;
+ int col;
+
+ if (b->list == 0) abort();
+ b->recursion++;
+ id_p = getListItem( b, row, &child );
+ if (id_p != NULL) {
+ if ( b->type == B_DROPLIST ) {
+ gtk_label_set( id_p->labelG, labelStr?gtkConvertInput(labelStr):"" );
+ id_p->label = strdup( labelStr?labelStr:"" );
+ } else {
+ parseLabelStr( labelStr, b->colCnt, &texts );
+ for ( col=0; col<b->colCnt; col++ )
+ gtk_clist_set_text( GTK_CLIST(b->list), row, col, texts[col] );
+ if ( bm ) {
+ pixmap = gtkMakeIcon( b->widget, bm, &bitmap );
+ gtk_clist_set_pixtext( GTK_CLIST(b->list), row, 0, texts[0], 5, pixmap, bitmap );
+ gdk_pixmap_unref( pixmap );
+ gdk_bitmap_unref( bitmap );
+ }
+ }
+ id_p->itemData = itemData;
+ }
+ b->recursion--;
+ return TRUE;
+}
+
+
+EXPORT void wListDelete(
+ wList_p b,
+ wIndex_t inx )
+
+{
+ wListItem_p id_p;
+ GList * child;
+
+ if (b->list == 0) abort();
+ b->recursion++;
+ if ( b->type == B_DROPLIST ) {
+ id_p = getListItem( b, inx, &child );
+ if (id_p != NULL) {
+ gtk_container_remove( GTK_CONTAINER(b->list), child->data );
+ b->count--;
+ }
+ } else {
+ gtk_clist_remove( GTK_CLIST(b->list), inx );
+ b->count--;
+ }
+ b->recursion--;
+ return;
+}
+
+
+int wListGetColumnWidths(
+ wList_p bl,
+ int colCnt,
+ wPos_t * colWidths )
+{
+ int inx;
+
+ if ( bl->type != B_LIST )
+ return 0;
+ if ( bl->colWidths == NULL )
+ return 0;
+ for ( inx=0; inx<colCnt; inx++ ) {
+ if ( inx < bl->colCnt ) {
+ colWidths[inx] = bl->colWidths[inx];
+ } else {
+ colWidths[inx] = 0;
+ }
+ }
+ return bl->colCnt;
+}
+
+
+static void gtkDropListAddValue(
+ wList_p b,
+ wListItem_p id_p )
+{
+ GtkWidget * listItem;
+
+ if ( id_p == NULL )
+ return;
+ id_p->labelG = (GtkLabel*)gtk_label_new( gtkConvertInput(id_p->label) );
+ /*gtk_misc_set_alignment( GTK_MISC(id_p->labelG), 0.0, 0.5 );*/
+ gtk_widget_show( GTK_WIDGET(id_p->labelG) );
+
+ listItem = gtk_list_item_new();
+ gtk_object_set_data( GTK_OBJECT(listItem), ListItemDataKey, id_p );
+ gtk_container_add( GTK_CONTAINER(listItem), GTK_WIDGET(id_p->labelG) );
+ gtk_misc_set_alignment( GTK_MISC(id_p->labelG), 0.0, 0.5 );
+ gtk_container_add( GTK_CONTAINER(b->list), listItem );
+ gtk_widget_show( listItem );
+}
+
+
+EXPORT wIndex_t wListAddValue(
+ wList_p b, /* List */
+ const char * labelStr, /* Entry name */
+ wIcon_p bm, /* Entry bitmap */
+ void * itemData ) /* User context */
+/*
+Adds a entry to the list <b> with name <name>.
+If list is created with 'BL_
+*/
+{
+ wListItem_p id_p;
+ GdkPixmap * pixmap = NULL;
+ GdkBitmap * bitmap;
+ static char ** texts;
+ GtkAdjustment *adj;
+
+ if (b->list == 0) abort();
+ b->recursion++;
+ id_p = (wListItem_p)malloc( sizeof *id_p );
+ memset( id_p, 0, sizeof *id_p );
+ id_p->itemData = itemData;
+ id_p->active = TRUE;
+ if ( labelStr == NULL )
+ labelStr = "";
+ id_p->label = strdup( labelStr );
+ id_p->listP = b;
+ if ( b->type == B_DROPLIST ) {
+ gtkDropListAddValue( b, id_p );
+ } else {
+ parseLabelStr( labelStr, b->colCnt, &texts );
+ gtk_clist_append( GTK_CLIST(b->list), texts );
+
+ /*
+ * this has taken forever to find out: the adjustment has to be notified
+ * about the list change by the program. So we need to get the current alignment.
+ * increment the upper value and then inform the scrolled window about the update.
+ * The upper value is increased only if the current value is smaller than the size
+ * of the list box.
+ */
+
+ adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(b->widget));
+
+ if( adj->upper < adj->step_increment * (b->count+1)) {
+ adj->upper += adj->step_increment;
+ gtk_adjustment_changed( adj );
+ }
+ if ( bm ) {
+ pixmap = gtkMakeIcon( b->widget, bm, &bitmap );
+ gtk_clist_set_pixtext( GTK_CLIST(b->list), b->count, 0, texts[0], 5, pixmap, bitmap );
+ gdk_pixmap_unref( pixmap );
+ gdk_bitmap_unref( bitmap );
+ }
+ gtk_clist_set_row_data( GTK_CLIST(b->list), b->count, id_p );
+ }
+
+ b->count++;
+ b->recursion--;
+ if ( b->count == 1 ) {
+ b->last = 0;
+ }
+ return b->count-1;
+}
+
+
+void wListSetSize( wList_p bl, wPos_t w, wPos_t h )
+{
+ /*gtk_widget_set_usize( bl->list, w, h );*/
+ if (bl->type == B_DROPLIST) {
+ /*gtk_widget_set_usize( GTK_COMBO(bl->widget)->entry, w, -1 );
+ gtk_widget_set_usize( GTK_COMBO(bl->widget)->list, w, -1 );*/
+#ifndef GTK1
+ gtk_widget_set_size_request( bl->widget, w, -1 );
+#else
+ gtk_widget_set_usize( bl->widget, w, -1 );
+#endif
+ } else {
+#ifndef GTK1
+ gtk_widget_set_size_request( bl->widget, w, h );
+#else
+ gtk_widget_set_usize( bl->widget, w, h );
+#endif
+ }
+ bl->w = w;
+ bl->h = h;
+}
+
+
+
+
+EXPORT void wListSetActive(
+ wList_p b, /* List */
+ int inx, /* Index */
+ wBool_t active ) /* Command */
+/*
+*/
+{
+ wListItem_p id_p;
+ GList * child;
+
+ if (b->list == 0) abort();
+ id_p = getListItem( b, inx, &child );
+ if (id_p == NULL)
+ return;
+ gtk_widget_set_sensitive( GTK_WIDGET(child->data), active );
+}
+
+
+EXPORT void wListSetEditable(
+ wList_p b,
+ wBool_t editable )
+{
+ b->editable = editable;
+ if ( b->type == B_DROPLIST )
+ gtk_widget_set_sensitive( GTK_WIDGET(GTK_COMBO(b->widget)->entry), b->editable );
+}
+
+
+static int selectCList(
+ GtkWidget * clist,
+ int row,
+ int col,
+ GdkEventButton* event,
+ gpointer data )
+{
+ wList_p bl = (wList_p)data;
+ wListItem_p id_p;
+
+ if (gdk_pointer_is_grabbed()) {
+ gdk_pointer_ungrab(0);
+ }
+ wFlush();
+ if (bl->recursion)
+ return 0;
+ id_p = gtk_clist_get_row_data( GTK_CLIST(clist), row );
+ if ( id_p == NULL ) return 1;
+ bl->editted = FALSE;
+ if ( (bl->option&BL_MANY)==0 && bl->last == row )
+ return 1;
+ bl->last = row;
+ id_p->selected = TRUE;
+ if (bl->valueP)
+ *bl->valueP = row;
+ if (bl->action)
+ bl->action( row, id_p->label, 1, bl->data, id_p->itemData );
+ return 1;
+}
+
+
+static int unselectCList(
+ GtkWidget * clist,
+ int row,
+ int col,
+ GdkEventButton* event,
+ gpointer data )
+{
+ wList_p bl = (wList_p)data;
+ wListItem_p id_p;
+
+ if (gdk_pointer_is_grabbed()) {
+ gdk_pointer_ungrab(0);
+ }
+ wFlush();
+ if (bl->recursion)
+ return 0;
+ id_p = gtk_clist_get_row_data( GTK_CLIST(clist), row );
+ if ( id_p == NULL ) return 1;
+ id_p->selected = FALSE;
+ if (bl->action)
+ bl->action( row, id_p->label, 2, bl->data, id_p->itemData );
+ return 1;
+}
+
+
+static int resizeColumnCList(
+ GtkWidget * clist,
+ int col,
+ int width,
+ gpointer data )
+{
+ wList_p bl = (wList_p)data;
+
+ if (gdk_pointer_is_grabbed()) {
+ gdk_pointer_ungrab(0);
+ }
+ wFlush();
+ if (bl->recursion)
+ return 0;
+ if ( col >= 0 && col < bl->colCnt )
+ bl->colWidths[col] = width;
+ return 0;
+}
+
+
+
+static int DropListSelectChild(
+ GtkWidget * list,
+ GtkWidget * listItem,
+ gpointer data )
+{
+ wList_p bl = (wList_p)data;
+ wListItem_p id_p=NULL;
+ wIndex_t inx;
+ GList * child;
+
+ if (gdk_pointer_is_grabbed()) {
+ gdk_pointer_ungrab(0);
+ }
+#ifdef LATER
+ printf( "DropListSelectChild %p %p %p\n", list, listItem, data );
+ printf( " b: recurs=%d widget=%p\n", bl->recursion, bl->list );
+#endif
+ if (bl->recursion)
+ return 0;
+ wFlush();
+ id_p = gtk_object_get_data(GTK_OBJECT(listItem), ListItemDataKey );
+ if ( id_p == NULL ) {
+ fprintf( stderr, " id_p = NULL\n");
+ return 0;
+ }
+#ifdef LATER
+ printf( " id_p = %s %lx, %d %d\n", id_p->label, (long)id_p->itemData, id_p->active, id_p->selected );
+#endif
+ if ( bl->type == B_DROPLIST && bl->editable ) {
+ if ( bl->editted == FALSE )
+ return 0;
+ gtkSetTrigger( NULL, NULL );
+ }
+ bl->editted = FALSE;
+ for ( inx=0,child=GTK_LIST(bl->list)->children,inx=0; child&&child->data!=listItem; child=child->next ) inx++;
+ if ( bl->last == inx )
+ return 1;
+ bl->last = inx;
+ if (bl->valueP)
+ *bl->valueP = inx;
+ if (id_p && bl->action)
+ bl->action( (wIndex_t)inx, id_p->label, 1, bl->data, id_p->itemData );
+ gtkSetTrigger( NULL, NULL );
+ return 1;
+}
+
+
+#ifdef LATER
+static int DropListSelectionChanged(
+ GtkWidget * list,
+ gpointer data )
+{
+ wList_p bl = (wList_p)data;
+ wListItem_p id_p=NULL;
+ GList * child;
+ GList * dlist;
+ wIndex_t inx;
+ GtkObject * listItem;
+ if (gdk_pointer_is_grabbed()) {
+ gdk_pointer_ungrab(0);
+ }
+ if (bl->recursion)
+ return 0;
+ wFlush();
+ if ( bl->type == B_DROPLIST && bl->editable ) {
+ if ( bl->editted == FALSE )
+ return 0;
+ gtkSetTrigger( NULL, NULL );
+ }
+
+ dlist = GTK_LIST(bl->list)->selection;
+ if (dlist == NULL) {
+ return 0;
+ }
+ inx = 0;
+ for ( child=GTK_LIST(bl->list)->children,inx=0; child&&child->data!=dlist->data; child=child->next ) inx++;
+ while (dlist) {
+ listItem = GTK_OBJECT(dlist->data);
+ id_p = gtk_object_get_data(listItem, ListItemDataKey );
+ printf( "DropListSelectionChanged: id_p = %s %lx\n", id_p->label, (long)id_p->itemData );
+ dlist = dlist->next;
+ }
+ return 0;
+#ifdef LATER
+ bl->editted = FALSE;
+ if ( bl->last == inx )
+ return 1;
+ bl->last = inx;
+ if (bl->valueP)
+ *bl->valueP = inx;
+ if (id_p && bl->action)
+ bl->action( inx, id_p->label, 1, bl->data, id_p->itemData );
+ gtkSetTrigger( NULL, NULL );
+ return 1;
+#endif
+}
+
+#endif
+
+
+static void triggerDListEntry(
+ wControl_p b )
+{
+ wList_p bl = (wList_p)b;
+ const char * entry_value;
+
+ if (bl == 0)
+ return;
+ if (bl->widget == 0) abort();
+ entry_value = gtk_entry_get_text( GTK_ENTRY(GTK_COMBO(bl->widget)->entry) );
+ if (entry_value == NULL) return;
+ if (debugWindow >= 2) printf("triggerListEntry: %s text = %s\n", bl->labelStr?bl->labelStr:"No label", entry_value );
+ if (bl->action) {
+ bl->recursion++;
+ bl->action( -1, entry_value, 0, bl->data, NULL );
+ bl->recursion--;
+ }
+ gtkSetTrigger( NULL, NULL );
+ return;
+}
+
+
+static void updateDListEntry(
+ GtkEntry * widget,
+ wList_p bl )
+{
+ const char *entry_value;
+ if (bl == 0)
+ return;
+ if (bl->recursion)
+ return;
+ if (!bl->editable)
+ return;
+ entry_value = gtk_entry_get_text( GTK_ENTRY(GTK_COMBO(bl->widget)->entry) );
+ bl->editted = TRUE;
+ if (bl->valueP != NULL)
+ *bl->valueP = -1;
+ bl->last = -1;
+ if (bl->action)
+ gtkSetTrigger( (wControl_p)bl, triggerDListEntry );
+ return;
+}
+
+
+
+#ifdef LATER
+EXPORT wList_p wListCreate(
+ wWin_p parent, /* Parent window */
+ wPos_t x, /* X-position */
+ wPos_t y, /* Y-position */
+ const char * helpStr, /* Help string */
+ const char * labelStr, /* Label */
+ long option, /* Options */
+ long number, /* Number of displayed entries */
+ wPos_t width, /* Width of list */
+ long *valueP, /* Selected index */
+ wListCallBack_p action, /* Callback */
+ void *data ) /* Context */
+/*
+*/
+{
+ wList_p b;
+
+ b = (wList_p)gtkAlloc( parent, B_LIST, x, y, labelStr, sizeof *b, data );
+ b->option = option;
+ b->number = number;
+ b->count = 0;
+ b->last = -1;
+ b->valueP = valueP;
+ b->action = action;
+ b->listX = b->realX;
+ b->colCnt = 0;
+ b->colWidths = NULL;
+ b->colRightJust = NULL;
+ gtkComputePos( (wControl_p)b );
+
+ b->list = (GtkWidget*)gtk_clist_new(1);
+ if (b->list == 0) abort();
+ b->widget = gtk_scrolled_window_new( NULL, NULL );
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (b->widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+ /*gtk_container_add( GTK_CONTAINER(b->widget), b->list );*/
+ gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(b->widget), b->list );
+ if (width == 0)
+ width = 100;
+ gtk_clist_set_column_width( GTK_CLIST(b->list), 0, width );
+#ifndef GTK1
+ gtk_widget_set_size_request( b->widget, width, (number+1)*ROW_HEIGHT );
+#else
+ gtk_widget_set_usize( b->widget, width, (number+1)*ROW_HEIGHT );
+#endif
+ gtk_signal_connect( GTK_OBJECT(b->list), "select_row", GTK_SIGNAL_FUNC(selectCList), b );
+ gtk_signal_connect( GTK_OBJECT(b->list), "unselect_row", GTK_SIGNAL_FUNC(unselectCList), b );
+ gtk_list_set_selection_mode( GTK_LIST(b->list), (option&BL_MANY)?GTK_SELECTION_MULTIPLE:GTK_SELECTION_BROWSE );
+/* gtk_container_set_focus_vadjustment (GTK_CONTAINER (b->list),
+ gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (b->widget))); */
+ gtk_widget_show( b->list );
+
+#ifndef GTK1
+ gtk_fixed_put( GTK_FIXED(parent->widget), b->widget, b->realX, b->realY );
+#else
+ gtk_container_add( GTK_CONTAINER(parent->widget), b->widget );
+ gtk_widget_set_uposition( b->widget, b->realX, b->realY );
+#endif
+ gtkControlGetSize( (wControl_p)b );
+ if (labelStr)
+ b->labelW = gtkAddLabel( (wControl_p)b, labelStr );
+ gtk_widget_show( b->widget );
+ gtkAddButton( (wControl_p)b );
+ gtkAddHelpString( b->widget, helpStr );
+ return b;
+}
+#endif
+
+/** Create a drop down list. The drop down is created and intialized with the supplied values.
+ *
+ * \param IN parent Parent window
+ * \param IN x, X-position
+ * \param IN y Y-position
+ * \param IN helpStr Help string
+ * \param IN labelStr Label
+ * \param IN option Options
+ * \param IN number Number of displayed entries
+ * \param IN width Width
+ * \param IN valueP Selected index
+ * \param IN action Callback
+ * \param IN data Context
+ */
+
+EXPORT wList_p wDropListCreate(
+ wWin_p parent, /* Parent window */
+ wPos_t x, /* X-position */
+ wPos_t y, /* Y-position */
+ const char * helpStr, /* Help string */
+ const char * labelStr, /* Label */
+ long option, /* Options */
+ long number, /* Number of displayed entries */
+ wPos_t width, /* Width */
+ long *valueP, /* Selected index */
+ wListCallBack_p action, /* Callback */
+ void *data ) /* Context */
+/*
+*/
+{
+ wList_p b;
+ b = (wList_p)gtkAlloc( parent, B_DROPLIST, x, y, labelStr, sizeof *b, data );
+ b->option = option;
+ b->number = number;
+ b->count = 0;
+ b->last = -1;
+ b->valueP = valueP;
+ b->action = action;
+ b->listX = b->realX;
+ b->colCnt = 0;
+ b->colWidths = NULL;
+ b->colRightJust = NULL;
+ gtkComputePos( (wControl_p)b );
+
+ b->widget = (GtkWidget*)gtk_combo_new();
+ if (b->widget == 0) abort();
+ b->list = GTK_COMBO(b->widget)->list;
+#ifdef LATER
+ gtk_signal_connect( GTK_OBJECT(b->list), "selection_changed", GTK_SIGNAL_FUNC(DropListSelectionChanged), b );
+#endif
+ gtk_signal_connect( GTK_OBJECT(b->list), "select_child", GTK_SIGNAL_FUNC(DropListSelectChild), b );
+ if (width == 0)
+ width = 100;
+#ifndef GTK1
+ gtk_widget_set_size_request( b->widget, width, -1 );
+ gtk_widget_set_size_request( GTK_COMBO(b->widget)->entry, width, -1 );
+#else
+ gtk_widget_set_usize( b->widget, width, -1 );
+ gtk_widget_set_usize( GTK_COMBO(b->widget)->entry, width, -1 );
+#endif
+
+ gtk_signal_connect( GTK_OBJECT(GTK_COMBO(b->widget)->entry), "changed", GTK_SIGNAL_FUNC(updateDListEntry), b );
+ if ( (option&BL_EDITABLE) == 0 )
+ gtk_widget_set_sensitive( GTK_WIDGET(GTK_COMBO(b->widget)->entry), FALSE );
+ else {
+ b->editable = TRUE;
+ }
+
+#ifndef GTK1
+ gtk_fixed_put( GTK_FIXED(parent->widget), b->widget, b->realX, b->realY );
+#else
+ gtk_container_add( GTK_CONTAINER(parent->widget), b->widget );
+ gtk_widget_set_uposition( b->widget, b->realX, b->realY );
+#endif
+ gtkControlGetSize( (wControl_p)b );
+ if (labelStr)
+ b->labelW = gtkAddLabel( (wControl_p)b, labelStr );
+ gtk_widget_show( b->widget );
+ gtkAddButton( (wControl_p)b );
+ gtkAddHelpString( b->widget, helpStr );
+ return b;
+
+}
+
+
+EXPORT wList_p wComboListCreate(
+ wWin_p parent, /* Parent window */
+ wPos_t x, /* X-position */
+ wPos_t y, /* Y-position */
+ const char * helpStr, /* Help string */
+ const char * labelStr, /* Label */
+ long option, /* Options */
+ long number, /* Number of displayed list entries */
+ wPos_t width, /* Width */
+ long *valueP, /* Selected index */
+ wListCallBack_p action, /* Callback */
+ void *data ) /* Context */
+/*
+*/
+{
+ return wListCreate( parent, x, y, helpStr, labelStr, option, number, width, 0, NULL, NULL, NULL, valueP, action, data );
+#ifdef LATER
+ wList_p b;
+
+ b = (wList_p)gtkAlloc( parent, B_LIST, x, y, labelStr, sizeof *b, data );
+ b->option = option;
+ b->number = number;
+ b->count = 0;
+ b->last = -1;
+ b->valueP = valueP;
+ b->action = action;
+ b->listX = b->realX;
+ gtkComputePos( (wControl_p)b );
+
+ b->widget = (GtkWidget*)gtk_combo_new();
+ if (b->widget == 0) abort();
+ if (width == 0)
+ width = 100;
+ /*gtk_clist_set_column_width( GTK_CLIST(b->widget), 0, width );*/
+#ifndef GTK1
+ gtk_widget_set_size_request( b->widget, width, -1 );
+#else
+ gtk_widget_set_usize( b->widget, width, -1 );
+#endif
+
+#ifndef GTK1
+ gtk_fixed_put( GTK_FIXED(parent->widget), b->widget, b->realX, b->realY );
+#else
+ gtk_container_add( GTK_CONTAINER(parent->widget), b->widget );
+ gtk_widget_set_uposition( b->widget, b->realX, b->realY );
+#endif
+ gtkControlGetSize( (wControl_p)b );
+ if (labelStr)
+ b->labelW = gtkAddLabel( (wControl_p)b, labelStr );
+ gtk_widget_show( b->widget );
+ gtkAddButton( (wControl_p)b );
+ gtkAddHelpString( b->widget, helpStr );
+ return b;
+#endif
+}
+
+
+
+EXPORT wList_p wListCreate(
+ wWin_p parent, /* Parent window */
+ wPos_t x, /* X-position */
+ wPos_t y, /* Y-position */
+ const char * helpStr, /* Help string */
+ const char * labelStr, /* Label */
+ long option, /* Options */
+ long number, /* Number of displayed entries */
+ wPos_t width, /* Width of list */
+ int colCnt, /* Number of columns */
+ wPos_t * colWidths, /* Width of columns */
+ wBool_t * colRightJust, /* justification of columns */
+ const char ** colTitles, /* Title of columns */
+ long *valueP, /* Selected index */
+ wListCallBack_p action, /* Callback */
+ void *data ) /* Context */
+/*
+*/
+{
+ wList_p bl;
+ long col;
+ static wPos_t zeroPos = 0;
+
+ bl = (wList_p)gtkAlloc( parent, B_LIST, x, y, labelStr, sizeof *bl, data );
+ bl->option = option;
+ bl->number = number;
+ bl->count = 0;
+ bl->last = -1;
+ bl->valueP = valueP;
+ bl->action = action;
+ bl->listX = bl->realX;
+
+ if ( colCnt <= 0 ) {
+ colCnt = 1;
+ colWidths = &zeroPos;
+ }
+ bl->colCnt = colCnt;
+ bl->colWidths = (wPos_t*)malloc( colCnt * sizeof *(wPos_t*)0 );
+ memcpy( bl->colWidths, colWidths, colCnt * sizeof *(wPos_t*)0 );
+
+ gtkComputePos( (wControl_p)bl );
+
+ bl->list = (GtkWidget*)gtk_clist_new( bl->colCnt );
+ if (bl->list == 0) abort();
+ if (colTitles)
+ {
+ for (col = 0; col < colCnt; col++)
+ gtk_clist_set_column_title(GTK_CLIST(bl->list), col, _(((char*)colTitles[col])));
+ gtk_clist_column_titles_show(GTK_CLIST(bl->list));
+ }
+
+ bl->widget = gtk_scrolled_window_new( NULL, NULL );
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (bl->widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+ /* gtk_container_add( GTK_CONTAINER(bl->widget), bl->list ); */
+ gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(bl->widget), bl->list );
+ if (width == 0)
+ width = 100;
+ for ( col=0; col<colCnt; col++ ) {
+ gtk_clist_set_column_auto_resize( GTK_CLIST(bl->list), col, TRUE );
+ gtk_clist_set_column_resizeable( GTK_CLIST(bl->list), col, TRUE );
+ gtk_clist_set_column_justification( GTK_CLIST(bl->list), col,
+ (colRightJust==NULL||colRightJust[col]==FALSE)?GTK_JUSTIFY_LEFT:GTK_JUSTIFY_RIGHT );
+ gtk_clist_set_column_width( GTK_CLIST(bl->list), col, bl->colWidths[col] );
+ }
+#ifndef GTK1
+ gtk_widget_set_size_request( bl->widget, width, (number+1)*ROW_HEIGHT );
+#else
+ gtk_widget_set_usize( bl->widget, width, (number+1)*ROW_HEIGHT );
+#endif
+ gtk_signal_connect( GTK_OBJECT(bl->list), "select_row", GTK_SIGNAL_FUNC(selectCList), bl );
+ gtk_signal_connect( GTK_OBJECT(bl->list), "unselect_row", GTK_SIGNAL_FUNC(unselectCList), bl );
+ gtk_signal_connect( GTK_OBJECT(bl->list), "resize_column", GTK_SIGNAL_FUNC(resizeColumnCList), bl );
+ gtk_clist_set_selection_mode( GTK_CLIST(bl->list), (option&BL_MANY)?GTK_SELECTION_MULTIPLE:GTK_SELECTION_BROWSE );
+ gtk_container_set_focus_vadjustment (GTK_CONTAINER (bl->list),
+ gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (bl->widget)));
+
+ gtk_widget_show( bl->list );
+
+#ifndef GTK1
+ gtk_fixed_put( GTK_FIXED(parent->widget), bl->widget, bl->realX, bl->realY );
+#else
+ gtk_container_add( GTK_CONTAINER(parent->widget), bl->widget );
+ gtk_widget_set_uposition( bl->widget, bl->realX, bl->realY );
+#endif
+ gtkControlGetSize( (wControl_p)bl );
+ if (labelStr)
+ bl->labelW = gtkAddLabel( (wControl_p)bl, labelStr );
+ gtk_widget_show( bl->widget );
+ gtkAddButton( (wControl_p)bl );
+ gtkAddHelpString( bl->widget, helpStr );
+ return bl;
+}
diff --git a/app/wlib/gtklib/gtkmenu.c b/app/wlib/gtklib/gtkmenu.c
new file mode 100644
index 0000000..0782f02
--- /dev/null
+++ b/app/wlib/gtklib/gtkmenu.c
@@ -0,0 +1,873 @@
+/** \file gtkmenu.c
+ * Menu creation and handling stuff.
+ *
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtkmenu.c,v 1.5 2009-10-03 04:49:01 dspagnol Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include "gtkint.h"
+
+int testMenuPopup = 1;
+
+static char MenuItemDataKey[] = "MenuItemDataKey";
+
+extern char gtkAccelChar;
+
+/*
+ *****************************************************************************
+ *
+ * Menus
+ *
+ *****************************************************************************
+ */
+
+typedef enum { M_MENU, M_SEPARATOR, M_PUSH, M_LIST, M_LISTITEM, M_TOGGLE, M_RADIO } mtype_e;
+typedef enum { MM_BUTT, MM_MENU, MM_BAR, MM_POPUP } mmtype_e;
+
+
+#define MOBJ_COMMON \
+ WOBJ_COMMON \
+ mtype_e mtype; \
+ GtkWidget * menu_item; \
+ wMenu_p parentMenu; \
+ int recursion;
+
+struct wMenuItem_t {
+ MOBJ_COMMON
+ };
+typedef struct wMenuItem_t * wMenuItem_p;
+
+struct wMenu_t {
+ MOBJ_COMMON
+ mmtype_e mmtype;
+ wMenuItem_p first, last;
+ GSList *radioGroup; /* in case menu holds a radio button group */
+ GtkWidget * menu;
+ wMenuTraceCallBack_p traceFunc;
+ void * traceData;
+ GtkLabel * labelG;
+ GtkWidget * imageG;
+ };
+
+struct wMenuPush_t {
+ MOBJ_COMMON
+ wMenuCallBack_p action;
+ wBool_t enabled;
+ };
+
+struct wMenuRadio_t {
+ MOBJ_COMMON
+ wMenuCallBack_p action;
+ wBool_t enabled;
+ };
+
+
+typedef struct wMenuListItem_t * wMenuListItem_p;
+
+struct wMenuList_t {
+ MOBJ_COMMON
+ int max;
+ int count;
+ wMenuListCallBack_p action;
+ };
+
+struct wMenuListItem_t {
+ MOBJ_COMMON
+ wMenuList_p mlist;
+ };
+
+struct wMenuToggle_t {
+ MOBJ_COMMON
+ wMenuToggleCallBack_p action;
+ wBool_t enabled;
+ wBool_t set;
+ };
+
+
+/*-----------------------------------------------------------------*/
+
+static void pushMenuItem(
+ GtkWidget * widget,
+ gpointer value )
+{
+ wMenuItem_p m = (wMenuItem_p)value;
+ wMenuToggle_p mt;
+ if (gdk_pointer_is_grabbed()) {
+ gdk_pointer_ungrab(0);
+ }
+/* wFlush(); */
+ if (m->recursion)
+ return;
+ switch (m->mtype) {
+ case M_PUSH:
+ if ( ((wMenuPush_p)m)->enabled == FALSE )
+ wBeep();
+ else
+ ((wMenuPush_p)m)->action( ((wMenuPush_p)m)->data );
+ break;
+ case M_TOGGLE:
+ mt = (wMenuToggle_p)m;
+ if ( mt->enabled == FALSE ) {
+ wBeep();
+ } else {
+ wMenuToggleSet( mt, !mt->set );
+ mt->action( mt->set, mt->data );
+ }
+ break;
+ case M_RADIO:
+ /* NOTE: action is only called when radio button is activated, not when deactivated */
+ if ( ((wMenuRadio_p)m)->enabled == FALSE )
+ wBeep();
+ else
+ if( ((GtkCheckMenuItem *)widget)->active == TRUE )
+ ((wMenuRadio_p)m)->action( ((wMenuRadio_p)m)->data );
+ break;
+ case M_MENU:
+ return;
+ default:
+ /*fprintf(stderr," Oops menu\n");*/
+ return;
+ }
+ if ( (m->parentMenu)->traceFunc ) {
+ (m->parentMenu)->traceFunc( m->parentMenu, m->labelStr, ((wMenu_p)m->parentMenu)->traceData );
+ }
+}
+
+static wMenuItem_p createMenuItem(
+ wMenu_p m,
+ mtype_e mtype,
+ const char * helpStr,
+ const char * labelStr,
+ int size )
+{
+ wMenuItem_p mi;
+ mi = (wMenuItem_p)gtkAlloc( NULL, B_MENUITEM, 0, 0, labelStr, size, NULL );
+ mi->mtype = mtype;
+ switch ( mtype ) {
+ case M_LIST:
+ m->menu_item = NULL;
+ break;
+ case M_SEPARATOR:
+ mi->menu_item = gtk_separator_menu_item_new();
+ break;
+ case M_TOGGLE:
+ mi->menu_item = gtk_check_menu_item_new_with_mnemonic(gtkConvertInput(mi->labelStr));
+ break;
+ case M_RADIO:
+ mi->menu_item = gtk_radio_menu_item_new_with_mnemonic(m->radioGroup, gtkConvertInput(mi->labelStr));
+ m->radioGroup = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (mi->menu_item));
+ break;
+ default:
+ mi->menu_item = gtk_menu_item_new_with_mnemonic(gtkConvertInput(mi->labelStr));
+ break;
+ }
+ if (mi->menu_item) {
+ if (m)
+ gtk_menu_append( GTK_MENU(m->menu), mi->menu_item );
+
+ gtk_signal_connect( GTK_OBJECT(mi->menu_item), "activate",
+ GTK_SIGNAL_FUNC(pushMenuItem), mi );
+ gtk_widget_show(mi->menu_item);
+ }
+ if (m) {
+ if (m->first == NULL) {
+ m->first = mi;
+ } else {
+ m->last->next = (wControl_p)mi;
+ }
+ m->last = mi;
+ }
+ mi->next = NULL;
+ if (helpStr != NULL) {
+ gtkAddHelpString( mi->menu_item, helpStr );
+ }
+ mi->parentMenu = m;
+ return mi;
+}
+
+
+static void setAcclKey( wWin_p w, GtkWidget * menu, GtkWidget * menu_item, int acclKey )
+{
+ char acclStr[40];
+ int len;
+ int mask;
+ static GtkAccelGroup * accel_alpha_group = NULL;
+ static GtkAccelGroup * accel_nonalpha_group = NULL;
+ guint oldmods;
+
+
+ if (accel_alpha_group == NULL) {
+ accel_alpha_group = gtk_accel_group_new();
+ /*gtk_accel_group_set_mod_mask( accel_group, GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK );*/
+ gtk_window_add_accel_group(GTK_WINDOW(gtkMainW->gtkwin), accel_alpha_group );
+ }
+ if (accel_nonalpha_group == NULL) {
+ oldmods = gtk_accelerator_get_default_mod_mask();
+ gtk_accelerator_set_default_mod_mask( GDK_CONTROL_MASK | GDK_MOD1_MASK );
+ accel_nonalpha_group = gtk_accel_group_new();
+ /*gtk_accel_group_set_mod_mask( accel_group, GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK );*/
+ gtk_window_add_accel_group(GTK_WINDOW(gtkMainW->gtkwin), accel_nonalpha_group );
+ gtk_accelerator_set_default_mod_mask( oldmods );
+ }
+
+ mask = 0;
+ if (acclKey) {
+#ifdef LATER
+ switch ( (acclKey&0xFF) ) {
+ case '+': acclKey = (acclKey&0xFF00) | WSHIFT | '='; break;
+ case '?': acclKey = (acclKey&0xFF00) | WSHIFT | '/'; break;
+ }
+#endif
+ len = 0;
+ if (acclKey&WALT) {
+ mask |= GDK_MOD1_MASK;
+ strcpy( acclStr+len, "Meta+" );
+ len += 5;
+ }
+ if (acclKey&WSHIFT) {
+ mask |= GDK_SHIFT_MASK;
+ strcpy( acclStr+len, "Shift+" );
+ len += 6;
+ switch ( (acclKey&0xFF) ) {
+ case '0': acclKey += ')'-'0'; break;
+ case '1': acclKey += '!'-'1'; break;
+ case '2': acclKey += '@'-'2'; break;
+ case '3': acclKey += '#'-'3'; break;
+ case '4': acclKey += '$'-'4'; break;
+ case '5': acclKey += '%'-'5'; break;
+ case '6': acclKey += '^'-'6'; break;
+ case '7': acclKey += '&'-'7'; break;
+ case '8': acclKey += '*'-'8'; break;
+ case '9': acclKey += '('-'9'; break;
+ case '`': acclKey += '~'-'`'; break;
+ case '-': acclKey += '_'-'-'; break;
+ case '=': acclKey += '+'-'='; break;
+ case '\\': acclKey += '|'-'\\'; break;
+ case '[': acclKey += '{'-'['; break;
+ case ']': acclKey += '}'-']'; break;
+ case ';': acclKey += ':'-';'; break;
+ case '\'': acclKey += '"'-'\''; break;
+ case ',': acclKey += '<'-','; break;
+ case '.': acclKey += '>'-'.'; break;
+ case '/': acclKey += '?'-'/'; break;
+ default: break;
+ }
+ }
+ if (acclKey&WCTL) {
+ mask |= GDK_CONTROL_MASK;
+ strcpy( acclStr+len, "Ctrl+" );
+ len += 5;
+ }
+ acclStr[len++] = (acclKey & 0xFF);
+ acclStr[len++] = '\0';
+ gtk_widget_add_accelerator( menu_item, "activate",
+ (isalpha(acclKey&0xFF)?accel_alpha_group:accel_nonalpha_group),
+ toupper(acclKey&0xFF), mask, GTK_ACCEL_VISIBLE|GTK_ACCEL_LOCKED );
+ }
+}
+
+/*-----------------------------------------------------------------*/
+
+wMenuRadio_p wMenuRadioCreate(
+ wMenu_p m,
+ const char * helpStr,
+ const char * labelStr,
+ long acclKey,
+ wMenuCallBack_p action,
+ void *data )
+{
+ wMenuRadio_p mi;
+
+ mi = (wMenuRadio_p)createMenuItem( m, M_RADIO, helpStr, labelStr, sizeof *mi );
+ if (m->mmtype == MM_POPUP && !testMenuPopup)
+ return mi;
+ setAcclKey( m->parent, m->menu, mi->menu_item, acclKey );
+ mi->action = action;
+ mi->data = data;
+ mi->enabled = TRUE;
+ return mi;
+}
+
+void wMenuRadioSetActive(
+ wMenuRadio_p mi )
+{
+ gtk_check_menu_item_set_active( (GtkCheckMenuItem *)mi->menu_item, TRUE );
+}
+
+/*-----------------------------------------------------------------*/
+
+wMenuPush_p wMenuPushCreate(
+ wMenu_p m,
+ const char * helpStr,
+ const char * labelStr,
+ long acclKey,
+ wMenuCallBack_p action,
+ void *data )
+{
+ wMenuPush_p mi;
+
+ mi = (wMenuPush_p)createMenuItem( m, M_PUSH, helpStr, labelStr, sizeof *mi );
+ if (m->mmtype == MM_POPUP && !testMenuPopup)
+ return mi;
+ setAcclKey( m->parent, m->menu, mi->menu_item, acclKey );
+ mi->action = action;
+ mi->data = data;
+ mi->enabled = TRUE;
+ return mi;
+}
+
+
+void wMenuPushEnable(
+ wMenuPush_p mi,
+ wBool_t enable )
+{
+ mi->enabled = enable;
+ gtk_widget_set_sensitive( GTK_WIDGET(mi->menu_item), enable );
+}
+
+
+/*-----------------------------------------------------------------*/
+
+wMenu_p wMenuMenuCreate(
+ wMenu_p m,
+ const char * helpStr,
+ const char * labelStr )
+{
+ wMenu_p mi;
+ mi = (wMenu_p)createMenuItem( m, M_MENU, helpStr, labelStr, sizeof *mi );
+ mi->mmtype = MM_MENU;
+ mi->menu = gtk_menu_new();
+ /*gtk_widget_set_sensitive( GTK_WIDGET(mi->menu_item), FALSE );*/
+ gtk_menu_item_set_submenu( GTK_MENU_ITEM(mi->menu_item), mi->menu );
+ return mi;
+}
+
+
+/*-----------------------------------------------------------------*/
+
+void wMenuSeparatorCreate(
+ wMenu_p m )
+{
+ wMenuItem_p mi;
+ mi = createMenuItem( m, M_SEPARATOR, NULL, "", sizeof *mi );
+}
+
+
+/*-----------------------------------------------------------------*/
+
+int getMlistOrigin( wMenu_p m, wMenuList_p ml )
+{
+ wMenuItem_p mi;
+ int count;
+ count = 0; /* Menu counts as one */
+ for ( mi = m->first; mi != NULL; mi = (wMenuItem_p)mi->next ) {
+ switch( mi->mtype ) {
+ case M_SEPARATOR:
+ case M_PUSH:
+ case M_MENU:
+ count++;
+ break;
+ case M_LIST:
+ if (mi == (wMenuItem_p)ml)
+ return count;
+ count += ((wMenuList_p)mi)->count;
+ break;
+ default:
+ /*fprintf(stderr, "Oops: getMlistOrigin\n");*/
+ break;
+ }
+ }
+ return count;
+}
+
+wMenuList_p wMenuListCreate(
+ wMenu_p m,
+ const char * helpStr,
+ int max,
+ wMenuListCallBack_p action )
+{
+ wMenuList_p mi;
+ mi = (wMenuList_p)createMenuItem( m, M_LIST, NULL, NULL, sizeof *mi );
+ mi->next = NULL;
+ mi->count = 0;
+ mi->max = max;
+ mi->parentMenu = m;
+ mi->action = action;
+ return (wMenuList_p)mi;
+}
+
+
+static void pushMenuList(
+ GtkWidget * widget,
+ gpointer value )
+{
+ wMenuListItem_p ml = (wMenuListItem_p)value;
+ int i;
+ int origin;
+ GtkWidget * item;
+ char * itemLabel;
+ GList * children;
+ GList * child;
+ GtkWidget *label;
+
+ if (gdk_pointer_is_grabbed()) {
+ gdk_pointer_ungrab(0);
+ }
+ wFlush();
+
+ if (ml->recursion)
+ return;
+ if (ml->mlist->count <= 0) {
+ fprintf( stderr, "pushMenuItem: empty list\n" );
+ return;
+ }
+ if (ml->mlist->action) {
+ origin = getMlistOrigin(ml->mlist->parentMenu, ml->mlist);
+ children = gtk_container_children( GTK_CONTAINER(ml->mlist->parentMenu->menu) );
+ if (children == NULL) abort();
+ child = g_list_nth( children, origin );
+ for (i=origin; i<origin+ml->mlist->count; i++, child=g_list_next(child) ) {
+ if (child == NULL) abort();
+ item = (GtkWidget*)child->data;
+ if (item == NULL) abort();
+ if (item == widget) {
+ children = gtk_container_children(GTK_CONTAINER(item));
+ label = (GtkWidget*)children->data;
+ gtk_label_get( GTK_LABEL(label), &itemLabel );
+ ml->mlist->action( i-origin, itemLabel, ml->data );
+ return;
+ }
+ }
+ }
+ fprintf( stderr, "pushMenuItem: item (%lx) not found\n", (long)widget );
+}
+
+
+void wMenuListAdd(
+ wMenuList_p ml,
+ int index,
+ const char * labelStr,
+ const void * data )
+{
+ int i;
+ int origin;
+ GtkWidget * item;
+ char * itemLabel;
+ GList * children;
+ GList * child;
+ GList * itemList;
+ GtkWidget * label;
+ wMenuListItem_p mi;
+ char * labelStrConverted;
+
+ origin = getMlistOrigin(ml->parentMenu, ml);
+ if (ml->count > 0) {
+ children = gtk_container_children( GTK_CONTAINER(ml->parentMenu->menu) );
+ if (children == NULL) abort();
+ child = g_list_nth( children, origin );
+ labelStrConverted = gtkConvertInput(labelStr);
+ for (i=origin; i<origin+ml->count; i++, child=g_list_next(child) ) {
+ if (child == NULL) abort();
+ item = (GtkWidget*)child->data;
+ if (item == NULL) abort();
+ itemList = gtk_container_children(GTK_CONTAINER(item));
+ label = (GtkWidget*)itemList->data;
+ gtk_label_get( GTK_LABEL(label), &itemLabel );
+ if (strcmp( labelStrConverted, itemLabel ) == 0) {
+ if (i != ml->count+index) {
+ gtk_container_remove( GTK_CONTAINER(ml->parentMenu->menu), item );
+ ml->count--;
+ break;
+ }
+ return;
+ }
+ }
+ if (ml->max >= 0 && ml->count >= ml->max) {
+ child = g_list_nth( children, origin+ml->count-1 );
+ if (child == NULL) abort();
+ item = (GtkWidget*)child->data;
+ if (item == NULL) abort();
+ gtk_container_remove( GTK_CONTAINER(ml->parentMenu->menu), item );
+ ml->count--;
+ }
+ }
+ mi = (wMenuListItem_p)gtkAlloc( NULL, B_MENUITEM, 0, 0, labelStr, sizeof *mi, NULL );
+ mi->mtype = M_LISTITEM;
+ mi->menu_item = gtk_menu_item_new_with_label(gtkConvertInput(mi->labelStr));
+ mi->data = (void *)data;
+ mi->mlist = ml;
+ if (index < 0 || index > ml->count)
+ index = ml->count;
+ gtk_menu_insert( GTK_MENU(ml->parentMenu->menu), mi->menu_item, origin+index );
+ gtk_signal_connect( GTK_OBJECT(mi->menu_item), "activate",
+ GTK_SIGNAL_FUNC(pushMenuList), mi );
+ gtk_object_set_data( GTK_OBJECT(mi->menu_item), MenuItemDataKey, mi );
+ gtk_widget_show(mi->menu_item);
+
+ ml->count++;
+}
+
+
+void wMenuListDelete(
+ wMenuList_p ml,
+ const char * labelStr )
+{
+ int i;
+ int origin;
+ GtkWidget * item;
+ char * itemLabel;
+ GList * children;
+ GList * child;
+ GtkWidget * label;
+ char * labelStrConverted;
+
+ if (ml->count <= 0) abort();
+ origin = getMlistOrigin(ml->parentMenu, ml);
+ children = gtk_container_children( GTK_CONTAINER(ml->parentMenu->menu) );
+ if (children == NULL) abort();
+ child = g_list_nth( children, origin );
+ labelStrConverted = gtkConvertInput( labelStr );
+ for (i=origin; i<origin+ml->count; i++, child=g_list_next(child) ) {
+ if (child == NULL) abort();
+ item = (GtkWidget*)child->data;
+ if (item == NULL) abort();
+ children = gtk_container_children(GTK_CONTAINER(item));
+ label = (GtkWidget*)children->data;
+ gtk_label_get( GTK_LABEL(label), &itemLabel );
+ if (strcmp( labelStrConverted, itemLabel ) == 0) {
+ gtk_container_remove( GTK_CONTAINER(ml->parentMenu->menu), item );
+ gtk_widget_queue_resize( GTK_WIDGET(ml->parentMenu->menu) );
+ ml->count--;
+ return;
+ }
+ }
+}
+
+
+const char * wMenuListGet( wMenuList_p ml, int index, void ** data )
+{
+ int origin;
+ GtkWidget * item;
+ GList * children;
+ GList * child;
+ GtkWidget * label;
+ char * itemLabel;
+ wMenuListItem_p mi;
+
+ if (ml->count <= 0)
+ return NULL;
+
+ if (index >= ml->count) {
+ if (data)
+ *data = NULL;
+ return NULL;
+ }
+ origin = getMlistOrigin(ml->parentMenu, ml);
+ children = gtk_container_children( GTK_CONTAINER(ml->parentMenu->menu) );
+ if (children == NULL) abort();
+ child = g_list_nth( children, origin+index );
+ if (child == NULL) abort();
+ item = (GtkWidget*)child->data;
+ if (item == NULL) abort();
+ children = gtk_container_children(GTK_CONTAINER(item));
+ label = (GtkWidget*)children->data;
+ gtk_label_get( GTK_LABEL(label), &itemLabel );
+ if (data) {
+ mi = (wMenuListItem_p)gtk_object_get_data( GTK_OBJECT(item), MenuItemDataKey );
+ if (mi)
+ *data = mi->data;
+ }
+ return itemLabel;
+}
+
+
+void wMenuListClear(
+ wMenuList_p ml )
+{
+ int i;
+ int origin;
+ GtkWidget * item;
+ GList * children;
+ GList * child;
+
+ if (ml->count == 0)
+ return;
+ origin = getMlistOrigin(ml->parentMenu, ml);
+ children = gtk_container_children( GTK_CONTAINER(ml->parentMenu->menu) );
+ if (children == NULL) abort();
+ child = g_list_nth( children, origin );
+ for (i=origin; i<origin+ml->count; i++, child=g_list_next(child) ) {
+ if (child == NULL) abort();
+ item = (GtkWidget*)child->data;
+ if (item == NULL) abort();
+ gtk_container_remove( GTK_CONTAINER(ml->parentMenu->menu), item );
+ }
+ ml->count = 0;
+ gtk_widget_queue_resize( GTK_WIDGET(ml->parentMenu->menu) );
+}
+/*-----------------------------------------------------------------*/
+
+wMenuToggle_p wMenuToggleCreate(
+ wMenu_p m,
+ const char * helpStr,
+ const char * labelStr,
+ long acclKey,
+ wBool_t set,
+ wMenuToggleCallBack_p action,
+ void * data )
+{
+ wMenuToggle_p mt;
+
+ mt = (wMenuToggle_p)createMenuItem( m, M_TOGGLE, helpStr, labelStr, sizeof *mt );
+ setAcclKey( m->parent, m->menu, mt->menu_item, acclKey );
+ mt->action = action;
+ mt->data = data;
+ mt->enabled = TRUE;
+ mt->parentMenu = m;
+ wMenuToggleSet( mt, set );
+
+ return mt;
+}
+
+
+wBool_t wMenuToggleGet(
+ wMenuToggle_p mt )
+{
+ return mt->set;
+}
+
+
+wBool_t wMenuToggleSet(
+ wMenuToggle_p mt,
+ wBool_t set )
+{
+ wBool_t rc;
+ if (mt==NULL) return 0;
+ mt->recursion++;
+ gtk_check_menu_item_set_state( GTK_CHECK_MENU_ITEM(mt->menu_item), set );
+ mt->recursion--;
+ rc = mt->set;
+ mt->set = set;
+ return rc;
+}
+
+
+void wMenuToggleEnable(
+ wMenuToggle_p mt,
+ wBool_t enable )
+{
+ mt->enabled = enable;
+}
+
+
+/*-----------------------------------------------------------------*/
+
+void wMenuSetLabel( wMenu_p m, const char * labelStr) {
+ gtkSetLabel( m->widget, m->option, labelStr, &m->labelG, &m->imageG );
+}
+
+
+static gint pushMenu(
+ GtkWidget * widget,
+ wMenu_p m )
+{
+ gtk_menu_popup( GTK_MENU(m->menu), NULL, NULL, NULL, NULL, 0, 0 );
+ /* Tell calling code that we have handled this event; the buck
+ * stops here. */
+ return TRUE;
+}
+
+
+wMenu_p wMenuCreate(
+ wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char * helpStr,
+ const char * labelStr,
+ long option )
+{
+ wMenu_p m;
+ m = gtkAlloc( parent, B_MENU, x, y, labelStr, sizeof *m, NULL );
+ m->mmtype = MM_BUTT;
+ m->option = option;
+ m->traceFunc = NULL;
+ m->traceData = NULL;
+ gtkComputePos( (wControl_p)m );
+
+ m->widget = gtk_button_new();
+ gtk_signal_connect (GTK_OBJECT(m->widget), "clicked",
+ GTK_SIGNAL_FUNC(pushMenu), m );
+
+ m->menu = gtk_menu_new();
+
+ wMenuSetLabel( m, labelStr );
+#ifdef MENUOPTION
+ gtk_option_menu_set_menu( GTK_OPTION_MENU(m->widget), m->menu );
+ ((GtkOptionMenu*)m->widget)->width = 25;
+ ((GtkOptionMenu*)m->widget)->height = 16;
+#endif
+
+ gtk_fixed_put( GTK_FIXED(parent->widget), m->widget, m->realX, m->realY );
+ gtkControlGetSize( (wControl_p)m );
+ if ( m->w < 80 && (m->option&BO_ICON)==0) {
+ m->w = 80;
+ gtk_widget_set_usize( m->widget, m->w, m->h );
+ }
+ gtk_widget_show( m->widget );
+ gtkAddButton( (wControl_p)m );
+ gtkAddHelpString( m->widget, helpStr );
+ return m;
+}
+
+/**
+ * Add a drop-down menu to the menu bar.
+ *
+ * \param[IN] w main window handle
+ * \param[IN] helpStr unused (should be help topic )
+ * \param[IN] labelStr label for the drop-down menu
+ * \return pointer to the created drop-down menu
+ */
+
+
+wMenu_p wMenuBarAdd(
+ wWin_p w,
+ const char * helpStr,
+ const char * labelStr )
+{
+ wMenu_p m;
+ GtkWidget * menuItem;
+ static GtkAccelGroup * accel_group = NULL;
+
+ m = gtkAlloc( w, B_MENU, 0, 0, labelStr, sizeof *m, NULL );
+ m->mmtype = MM_BAR;
+ m->realX = 0;
+ m->realY = 0;
+
+ menuItem = gtk_menu_item_new_with_label( gtkConvertInput(m->labelStr) );
+ m->menu = gtk_menu_new();
+ gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem), m->menu );
+ gtk_menu_bar_append( GTK_MENU_BAR(w->menubar), menuItem );
+ gtk_widget_show( menuItem );
+
+ m->w = 0;
+ m->h = 0;
+
+ /* TODO: why is help not supported here? */
+ /*gtkAddHelpString( m->panel_item, helpStr );*/
+
+ if ( gtkAccelChar ) {
+ if ( accel_group == NULL ) {
+ accel_group = gtk_accel_group_new();
+ gtk_window_add_accel_group( GTK_WINDOW(w->gtkwin), accel_group );
+ }
+ gtk_widget_add_accelerator( menuItem, "activate", accel_group, tolower(gtkAccelChar), GDK_MOD1_MASK, GTK_ACCEL_LOCKED );
+ }
+ return m;
+}
+
+
+/*-----------------------------------------------------------------*/
+
+
+wMenu_p wMenuPopupCreate(
+ wWin_p w,
+ const char * labelStr )
+{
+ wMenu_p b;
+ b = gtkAlloc( w, B_MENU, 0, 0, labelStr, sizeof *b, NULL );
+ b->mmtype = MM_POPUP;
+ b->option = 0;
+
+ b->menu = gtk_menu_new();
+ b->w = 0;
+ b->h = 0;
+ gtk_signal_connect( GTK_OBJECT (b->menu), "key_press_event",
+ GTK_SIGNAL_FUNC (catch_shift_ctrl_alt_keys), b);
+ gtk_signal_connect( GTK_OBJECT (b->menu), "key_release_event",
+ GTK_SIGNAL_FUNC (catch_shift_ctrl_alt_keys), b);
+ gtk_widget_set_events ( GTK_WIDGET(b->menu), GDK_EXPOSURE_MASK|GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK );
+ return b;
+}
+
+
+void wMenuPopupShow( wMenu_p mp )
+{
+ gtk_menu_popup( GTK_MENU(mp->menu), NULL, NULL, NULL, NULL, 0, 0 );
+}
+
+
+/*-----------------------------------------------------------------*/
+
+void wMenuSetTraceCallBack(
+ wMenu_p m,
+ wMenuTraceCallBack_p func,
+ void * data )
+{
+ m->traceFunc = func;
+ m->traceData = data;
+}
+
+wBool_t wMenuAction(
+ wMenu_p m,
+ const char * label )
+{
+ wMenuItem_p mi;
+ wMenuToggle_p mt;
+ for ( mi = m->first; mi != NULL; mi = (wMenuItem_p)mi->next ) {
+ if ( strcmp( mi->labelStr, label ) == 0 ) {
+ switch( mi->mtype ) {
+ case M_SEPARATOR:
+ break;
+ case M_PUSH:
+ if ( ((wMenuPush_p)mi)->enabled == FALSE )
+ wBeep();
+ else
+ ((wMenuPush_p)mi)->action( ((wMenuPush_p)mi)->data );
+ break;
+ case M_TOGGLE:
+ mt = (wMenuToggle_p)mi;
+ if ( mt->enabled == FALSE ) {
+ wBeep();
+ } else {
+ wMenuToggleSet( mt, !mt->set );
+ mt->action( mt->set, mt->data );
+ }
+ break;
+ case M_MENU:
+ break;
+ case M_LIST:
+ break;
+ default:
+ /*fprintf(stderr, "Oops: wMenuAction\n");*/
+ break;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
diff --git a/app/wlib/gtklib/gtkmisc.c b/app/wlib/gtklib/gtkmisc.c
new file mode 100644
index 0000000..acc123a
--- /dev/null
+++ b/app/wlib/gtklib/gtkmisc.c
@@ -0,0 +1,1210 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtkmisc.c,v 1.15 2009-10-03 04:49:01 dspagnol Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <locale.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "gtkint.h"
+#include "i18n.h"
+
+wWin_p gtkMainW;
+
+long debugWindow = 0;
+
+char wAppName[256];
+char wConfigName[ 256 ];
+
+#define FOUR (4)
+#ifndef GTK1
+#define MENUH (24)
+#else
+#define MENUH (24)
+#endif
+#define LABEL_OFFSET (3)
+
+const char * wNames[] = {
+ "MAIN",
+ "POPUP",
+ "BUTT",
+ "CANCEL",
+ "POPUP",
+ "TEXT",
+ "INTEGER",
+ "FLOAT",
+ "LIST",
+ "DROPLIST",
+ "COMBOLIST",
+ "RADIO",
+ "TOGGLE",
+ "DRAW",
+ "MENU"
+ "MULTITEXT",
+ "MESSAGE",
+ "LINES",
+ "MENUITEM",
+ "BOX"
+ };
+
+static struct timeval startTime;
+
+static wBool_t reverseIcon =
+#if defined(linux)
+ FALSE;
+#else
+ TRUE;
+#endif
+
+char gtkAccelChar;
+
+
+/*
+ *****************************************************************************
+ *
+ * Internal Utility functions
+ *
+ *****************************************************************************
+ */
+
+unsigned char gtkBitrotate(
+ char v )
+{
+ unsigned char r = 0;
+ int i;
+ static unsigned char bits[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+ for (i=0;i<8;i++)
+ if (v & bits[i])
+ r |= bits[7-i];
+ return r;
+}
+
+GdkPixmap* gtkMakeIcon(
+ GtkWidget * widget,
+ wIcon_p ip,
+ GdkBitmap ** mask )
+{
+ GdkPixmap * pixmap;
+ char ** pixmapData;
+ char * oldline1;
+ static char newline1[] = " \tc None s None";
+ char line0[40];
+ char line2[40];
+ int row,col,wb;
+ long rgb;
+ const char * bits;
+ GdkColor *transparent;
+
+ transparent = &gtk_widget_get_style( gtkMainW->gtkwin )->bg[GTK_WIDGET_STATE( gtkMainW->gtkwin )];
+
+ if ( ip->gtkIconType == gtkIcon_pixmap ) {
+ pixmap = gdk_pixmap_create_from_xpm_d( gtkMainW->gtkwin->window, mask, transparent, (char**)ip->bits );
+ }
+ else {
+ wb = (ip->w+7)/8;
+ pixmapData = (char**)malloc( (3+ip->h) * sizeof *pixmapData );
+ pixmapData[0] = line0;
+ rgb = wDrawGetRGB(ip->color);
+ sprintf( line0, " %d %d 2 1", ip->w, ip->h );
+ sprintf( line2, "# c #%2.2lx%2.2lx%2.2lx", (rgb>>16)&0xFF, (rgb>>8)&0xFF, rgb&0xFF );
+ pixmapData[1] = ". c None s None";
+ pixmapData[2] = line2;
+ bits = ip->bits;
+ for (row = 0; row<ip->h; row++ ) {
+ pixmapData[row+3] = (char*)malloc( (ip->w+1) * sizeof **pixmapData );
+ for (col = 0; col<ip->w; col++ ) {
+ if ( bits[ row*wb+(col>>3) ] & (1<<(col&07)) ) {
+ pixmapData[row+3][col] = '#';
+ } else {
+ pixmapData[row+3][col] = '.';
+ }
+ }
+ pixmapData[row+3][ip->w] = 0;
+ }
+ pixmap = gdk_pixmap_create_from_xpm_d( gtkMainW->gtkwin->window, mask, transparent, pixmapData );
+ for (row = 0; row<ip->h; row++ ) {
+ free( pixmapData[row+3] );
+ }
+ }
+ return pixmap;
+}
+
+
+int gtkAddLabel( wControl_p b, const char * labelStr )
+{
+ GtkRequisition requisition;
+ if (labelStr == NULL)
+ return 0;
+ b->label = gtk_label_new(gtkConvertInput(labelStr));
+ gtk_widget_size_request( b->label, &requisition );
+ gtk_container_add( GTK_CONTAINER(b->parent->widget), b->label );
+#ifndef GTK1
+ gtk_fixed_move( GTK_FIXED(b->parent->widget), b->label, b->realX-requisition.width-8, b->realY+LABEL_OFFSET );
+#else
+ gtk_widget_set_uposition( b->label, b->realX-requisition.width-8, b->realY+LABEL_OFFSET );
+#endif
+ gtk_widget_show( b->label );
+ return requisition.width+8;
+}
+
+
+void * gtkAlloc(
+ wWin_p parent,
+ wType_e type,
+ wPos_t origX,
+ wPos_t origY,
+ const char * labelStr,
+ int size,
+ void * data )
+{
+ wControl_p w = (wControl_p)malloc( size );
+ char * cp;
+ memset( w, 0, size );
+ if (w == NULL)
+ abort();
+ w->type = type;
+ w->parent = parent;
+ w->origX = origX;
+ w->origY = origY;
+ gtkAccelChar = 0;
+ if (labelStr) {
+ cp = (char*)malloc(strlen(labelStr)+1);
+ w->labelStr = cp;
+ for ( ; *labelStr; labelStr++ )
+ if ( *labelStr != '&' )
+ *cp++ = *labelStr;
+ else {
+/* *cp++ = '_';
+ gtkAccelChar = labelStr[1]; */
+ }
+ *cp = 0;
+ }
+ w->doneProc = NULL;
+ w->data = data;
+ return w;
+}
+
+
+void gtkComputePos(
+ wControl_p b )
+{
+ wWin_p w = b->parent;
+
+ if (b->origX >= 0)
+ b->realX = b->origX;
+ else
+ b->realX = w->lastX + (-b->origX) - 1;
+ if (b->origY >= 0)
+ b->realY = b->origY + FOUR + ((w->option&F_MENUBAR)?MENUH:0);
+ else
+ b->realY = w->lastY + (-b->origY) - 1;
+}
+
+
+void gtkControlGetSize(
+ wControl_p b )
+{
+ GtkRequisition requisition;
+ gtk_widget_size_request( b->widget, &requisition );
+ b->w = requisition.width;
+ b->h = requisition.height;
+}
+
+
+void gtkAddButton(
+ wControl_p b )
+{
+ wWin_p win = b->parent;
+ wBool_t resize = FALSE;
+ if (win->first == NULL) {
+ win->first = b;
+ } else {
+ win->last->next = b;
+ }
+ win->last = b;
+ b->next = NULL;
+ b->parent = win;
+ win->lastX = b->realX + b->w;
+ win->lastY = b->realY + b->h;
+ if (win->option&F_AUTOSIZE) {
+ if (win->lastX > win->realX) {
+ win->realX = win->lastX;
+ if (win->w != (win->realX + win->origX)) {
+ resize = TRUE;
+ win->w = (win->realX + win->origX);
+ }
+ }
+ if (win->lastY > win->realY) {
+ win->realY = win->lastY;
+ if (win->h != (win->realY + win->origY)) {
+ resize = TRUE;
+ win->h = (win->realY + win->origY);
+ }
+ }
+ if (win->shown) {
+ if ( resize ) {
+#ifndef GTK1
+ gtk_widget_set_size_request( win->gtkwin, win->w, win->h );
+ gtk_widget_set_size_request( win->widget, win->w, win->h );
+#else
+ gtk_widget_set_usize( win->gtkwin, win->w, win->h );
+ gtk_widget_set_usize( win->widget, win->w, win->h );
+#endif
+ }
+ }
+ }
+}
+
+
+void gtkSetReadonly( wControl_p b, wBool_t ro )
+{
+ if (ro)
+ b->option |= BO_READONLY;
+ else
+ b->option &= ~BO_READONLY;
+}
+
+
+wControl_p gtkGetControlFromPos(
+ wWin_p win,
+ wPos_t x,
+ wPos_t y )
+{
+ wControl_p b;
+ wPos_t xx, yy;
+ for (b=win->first; b != NULL; b = b->next) {
+ if ( b->widget && GTK_WIDGET_VISIBLE(b->widget) ) {
+ xx = b->realX;
+ yy = b->realY;
+ if ( xx <= x && x < xx+b->w &&
+ yy <= y && y < yy+b->h ) {
+ return b;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* \brief Convert label string from Windows mnemonic to GTK
+ *
+ * The first occurence of '&' in the passed string is changed to '_'
+ *
+ * \param label the string to convert
+ * \return pointer to modified string, has to be free'd after usage
+ *
+ */
+static
+char * gtkChgMnemonic( char *label )
+{
+ char *ptr;
+ char *cp;
+
+ cp = strdup( label );
+
+ ptr = strchr( cp, '&' );
+ if( ptr )
+ *ptr = '_';
+
+ return( cp );
+}
+
+
+/*
+ *****************************************************************************
+ *
+ * Exported Utility Functions
+ *
+ *****************************************************************************
+ */
+
+EXPORT void wBeep(
+ void )
+/*
+Beep!
+*/
+{
+ gdk_display_beep(gdk_display_get_default());
+}
+
+typedef struct {
+ GtkWidget * win;
+ GtkWidget * label;
+ GtkWidget * butt[3];
+ } notice_win;
+static notice_win noticeW;
+static long noticeValue;
+
+static void doNotice(
+ GtkWidget * widget,
+ long value )
+{
+ noticeValue = value;
+ gtk_widget_destroy( noticeW.win );
+ gtkDoModal( NULL, FALSE );
+}
+
+/**
+ * Show a notification window with a yes/no reply and an icon.
+ *
+ * \param type IN type of message: Information, Warning, Error
+ * \param msg IN message to display
+ * \param yes IN text for accept button
+ * \param no IN text for cancel button
+ * \return True when accept was selected, false otherwise
+ */
+
+int wNoticeEx( int type,
+ const char * msg,
+ const char * yes,
+ const char * no )
+{
+
+ int res;
+ unsigned flag;
+ char *headline;
+ GtkWidget *dialog;
+ GtkWindow *parent = GTK_WINDOW_TOPLEVEL;
+
+ switch( type ) {
+ case NT_INFORMATION:
+ flag = GTK_MESSAGE_INFO;
+ headline = _("Information");
+ break;
+ case NT_WARNING:
+ flag = GTK_MESSAGE_WARNING;
+ headline = _("Warning");
+ break;
+ case NT_ERROR:
+ flag = GTK_MESSAGE_ERROR;
+ headline = _("Error");
+ break;
+ }
+
+ if( gtkMainW )
+ parent = GTK_WINDOW( gtkMainW->gtkwin);
+
+ dialog = gtk_message_dialog_new( parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ flag,
+ ((no==NULL)?GTK_BUTTONS_OK:GTK_BUTTONS_YES_NO),
+ "%s", msg );
+ gtk_window_set_title( GTK_WINDOW(dialog), headline );
+
+ res = gtk_dialog_run( GTK_DIALOG(dialog));
+ gtk_widget_destroy( dialog );
+
+ return res == GTK_RESPONSE_OK || res == GTK_RESPONSE_YES;
+}
+
+
+EXPORT int wNotice(
+ const char * msg, /* Message */
+ const char * yes, /* First button label */
+ const char * no ) /* Second label (or 'NULL') */
+/*
+Popup up a notice box with one or two buttons.
+When this notice box is displayed the application is paused and
+will not response to other actions.
+
+Pushing the first button returns 'TRUE'.
+Pushing the second button (if present) returns 'FALSE'.
+*/
+{
+ return wNotice3( msg, yes, no, NULL );
+}
+
+/** \brief Popup a notice box with three buttons.
+ *
+ * Popup up a notice box with three buttons.
+ * When this notice box is displayed the application is paused and
+ * will not response to other actions.
+ *
+ * Pushing the first button returns 1
+ * Pushing the second button returns 0
+ * Pushing the third button returns -1
+ *
+ * \param msg Text to display in message box
+ * \param yes First button label
+ * \param no Second label (or 'NULL')
+ * \param cancel Third button label (or 'NULL')
+ *
+ * \returns 1, 0 or -1
+ */
+
+EXPORT int wNotice3(
+ const char * msg, /* Message */
+ const char * affirmative, /* First button label */
+ const char * cancel, /* Second label (or 'NULL') */
+ const char * alternate )
+{
+ notice_win *nw;
+ GtkWidget * vbox;
+ GtkWidget * hbox;
+ GtkWidget * hbox1;
+ GtkWidget * image;
+ nw = &noticeW;
+
+ char *aff = NULL;
+ char *can = NULL;
+ char *alt = NULL;
+
+#ifndef GTK1
+ nw->win = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ /*gtk_window_set_decorated( GTK_WINDOW(nw->win), FALSE );*/
+#else
+ nw->win = gtk_window_new( GTK_WINDOW_DIALOG );
+#endif
+ gtk_window_position( GTK_WINDOW(nw->win), GTK_WIN_POS_CENTER );
+ gtk_container_set_border_width (GTK_CONTAINER (nw->win), 0);
+ gtk_window_set_resizable (GTK_WINDOW (nw->win), FALSE);
+ gtk_window_set_modal (GTK_WINDOW (nw->win), TRUE);
+ gtk_window_set_type_hint (GTK_WINDOW (nw->win), GDK_WINDOW_TYPE_HINT_DIALOG);
+
+ vbox = gtk_vbox_new( FALSE, 12 );
+ gtk_widget_show( vbox );
+ gtk_container_add( GTK_CONTAINER(nw->win), vbox );
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+ hbox = gtk_hbox_new( FALSE, 12 );
+ gtk_box_pack_start( GTK_BOX(vbox), hbox, TRUE, TRUE, 0 );
+ gtk_widget_show(hbox);
+
+ image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);
+ gtk_widget_show (image);
+ gtk_box_pack_start (GTK_BOX (hbox), image, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC (image), 0, 0);
+
+ /* create the text label, allow GTK to wrap and allow for markup (for future enhancements) */
+ nw->label = gtk_label_new(msg);
+ gtk_widget_show( nw->label );
+ gtk_box_pack_end (GTK_BOX (hbox), nw->label, TRUE, TRUE, 0);
+ gtk_label_set_use_markup (GTK_LABEL (nw->label), FALSE);
+ gtk_label_set_line_wrap (GTK_LABEL (nw->label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (nw->label), 0, 0);
+
+ /* this hbox will include the button bar */
+ hbox1 = gtk_hbox_new (TRUE, 0);
+ gtk_widget_show (hbox1);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox1, FALSE, TRUE, 0);
+
+ /* add the respective buttons */
+ aff = gtkChgMnemonic( (char *) affirmative);
+ nw->butt[ 0 ] = gtk_button_new_with_mnemonic (aff);
+ gtk_widget_show (nw->butt[ 0 ]);
+ gtk_box_pack_end (GTK_BOX (hbox1), nw->butt[ 0 ], TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (nw->butt[ 0 ]), 3);
+ gtk_signal_connect( GTK_OBJECT(nw->butt[0]), "clicked", GTK_SIGNAL_FUNC(doNotice), (void*)1 );
+ GTK_WIDGET_SET_FLAGS (nw->butt[ 0 ], GTK_CAN_DEFAULT);
+
+ if( cancel ) {
+ can = gtkChgMnemonic( (char *) cancel);
+ nw->butt[ 1 ] = gtk_button_new_with_mnemonic (can);
+ gtk_widget_show (nw->butt[ 1 ]);
+ gtk_box_pack_end (GTK_BOX (hbox1), nw->butt[ 1 ], TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (nw->butt[ 1 ]), 3);
+ gtk_signal_connect( GTK_OBJECT(nw->butt[1]), "clicked", GTK_SIGNAL_FUNC(doNotice), (void*)0 );
+ GTK_WIDGET_SET_FLAGS (nw->butt[ 1 ], GTK_CAN_DEFAULT);
+
+ if( alternate ) {
+ alt = gtkChgMnemonic( (char *) alternate);
+ nw->butt[ 2 ] = gtk_button_new_with_mnemonic (alt);
+ gtk_widget_show (nw->butt[ 2 ]);
+ gtk_box_pack_start (GTK_BOX (hbox1), nw->butt[ 2 ], TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (nw->butt[ 2 ]), 3);
+ gtk_signal_connect( GTK_OBJECT(nw->butt[2]), "clicked", GTK_SIGNAL_FUNC(doNotice), (void*)-1 );
+ GTK_WIDGET_SET_FLAGS (nw->butt[ 2 ], GTK_CAN_DEFAULT);
+ }
+ }
+
+ gtk_widget_grab_default (nw->butt[ 0 ]);
+ gtk_widget_grab_focus (nw->butt[ 0 ]);
+
+ gtk_widget_show( nw->win );
+
+ if ( gtkMainW ) {
+ gtk_window_set_transient_for( GTK_WINDOW(nw->win), GTK_WINDOW( gtkMainW->gtkwin) );
+/* gdk_window_set_group( nw->win->window, gtkMainW->gtkwin->window ); */
+ }
+ gtkDoModal( NULL, TRUE );
+
+ if( aff )
+ free( aff );
+
+ if( can )
+ free( can );
+
+ if( alt )
+ free( alt );
+
+ return noticeValue;
+}
+
+
+EXPORT void wFlush(
+ void )
+/*
+Flushs all commands to the Window.
+*/
+{
+ while ( gtk_events_pending() )
+ gtk_main_iteration();
+
+ gdk_display_sync(gdk_display_get_default());
+}
+
+
+void wWinTop( wWin_p win )
+{
+}
+
+
+void wSetCursor( wCursor_t cursor )
+{
+}
+
+
+const char * wMemStats( void )
+{
+#ifdef LATER
+ static char msg[80];
+ struct mstats stats;
+ stats = mstats();
+ sprintf( msg, "Total %d, used %d(%d), free %d(%d)",
+ stats.bytes_total,
+ stats.bytes_used, stats.chunks_used,
+ stats.bytes_free, stats.chunks_free );
+ return msg;
+#else
+ return "No stats available";
+#endif
+}
+
+
+wBool_t wCheckExecutable( void )
+{
+ return TRUE;
+}
+
+
+void wGetDisplaySize( wPos_t * w, wPos_t * h )
+{
+
+ *w = gdk_screen_width();
+ *h = gdk_screen_height();
+}
+
+
+wIcon_p wIconCreateBitMap( wPos_t w, wPos_t h, const char * bits, wDrawColor color )
+{
+ wIcon_p ip;
+ ip = (wIcon_p)malloc( sizeof *ip );
+ ip->gtkIconType = gtkIcon_bitmap;
+ ip->w = w;
+ ip->h = h;
+ ip->color = color;
+ ip->bits = bits;
+ return ip;
+}
+
+wIcon_p wIconCreatePixMap( char *pm[] )
+{
+ wIcon_p ip;
+ ip = (wIcon_p)malloc( sizeof *ip );
+ ip->gtkIconType = gtkIcon_pixmap;
+ ip->w = 0;
+ ip->h = 0;
+ ip->color = 0;
+ ip->bits = pm;
+ return ip;
+}
+
+
+void wIconSetColor( wIcon_p ip, wDrawColor color )
+{
+ ip->color = color;
+}
+
+void wConvertToCharSet( char * buffPtr, int buffMax )
+{
+}
+
+
+void wConvertFromCharSet( char * buffPtr, int buffMax )
+{
+}
+
+static dynArr_t conversionBuffer_da;
+#define convesionBuffer(N) DYNARR_N( char, conversionBuffer_da, N )
+
+char * gtkConvertInput( const char * inString )
+{
+#ifndef GTK1
+ const char * cp;
+ char * cq;
+ int extCharCnt, inCharCnt;
+
+ /* Already UTF-8 encoded? */
+ if (g_utf8_validate(inString, -1, NULL))
+ /* Yes, do not double-convert */
+ return (char*)inString;
+#ifdef VERBOSE
+ fprintf(stderr, "gtkConvertInput(%s): Invalid UTF-8, converting...\n", inString);
+#endif
+
+ for ( cp=inString, extCharCnt=0; *cp; cp++ ) {
+ if ( ((*cp)&0x80) != 0 )
+ extCharCnt++;
+ }
+ inCharCnt = cp-inString;
+ if ( extCharCnt == 0 )
+ return (char*)inString;
+ DYNARR_SET( char, conversionBuffer_da, inCharCnt+extCharCnt+1 );
+ for ( cp=inString, cq=(char*)conversionBuffer_da.ptr; *cp; cp++ ) {
+ if ( ((*cp)&0x80) != 0 ) {
+ *cq++ = 0xC0+(((*cp)&0xC0)>>6);
+ *cq++ = 0x80+((*cp)&0x3F);
+ } else {
+ *cq++ = *cp;
+ }
+ }
+ *cq = 0;
+ return (char*)conversionBuffer_da.ptr;
+#else
+ return (char*)inString;
+#endif
+}
+
+
+char * gtkConvertOutput( const char * inString )
+{
+#ifndef GTK1
+ const char * cp;
+ char * cq;
+ int extCharCnt, inCharCnt;
+ for ( cp=inString, extCharCnt=0; *cp; cp++ ) {
+ if ( ((*cp)&0xC0) == 0x80 )
+ extCharCnt++;
+ }
+ inCharCnt = cp-inString;
+ if ( extCharCnt == 0 )
+ return (char*)inString;
+ DYNARR_SET( char, conversionBuffer_da, inCharCnt+1 );
+ for ( cp=inString, cq=(char*)conversionBuffer_da.ptr; *cp; cp++ ) {
+ if ( ((*cp)&0x80) != 0 ) {
+ *cq++ = 0xC0+(((*cp)&0xC0)>>6);
+ *cq++ = 0x80+((*cp)&0x3F);
+ } else {
+ *cq++ = *cp;
+ }
+ }
+ *cq = 0;
+ return (char*)conversionBuffer_da.ptr;
+#else
+ return (char*)inString;
+#endif
+}
+
+/*-----------------------------------------------------------------*/
+
+typedef struct accelData_t {
+ wAccelKey_e key;
+ int modifier;
+ wAccelKeyCallBack_p action;
+ void * data;
+ } accelData_t;
+static dynArr_t accelData_da;
+#define accelData(N) DYNARR_N( accelData_t, accelData_da, N )
+
+
+static guint accelKeyMap[] = {
+ 0, /* wAccelKey_None, */
+ GDK_Delete, /* wAccelKey_Del, */
+ GDK_Insert, /* wAccelKey_Ins, */
+ GDK_Home, /* wAccelKey_Home, */
+ GDK_End, /* wAccelKey_End, */
+ GDK_Page_Up, /* wAccelKey_Pgup, */
+ GDK_Page_Down, /* wAccelKey_Pgdn, */
+ GDK_Up, /* wAccelKey_Up, */
+ GDK_Down, /* wAccelKey_Down, */
+ GDK_Right, /* wAccelKey_Right, */
+ GDK_Left, /* wAccelKey_Left, */
+ GDK_BackSpace, /* wAccelKey_Back, */
+ GDK_F1, /* wAccelKey_F1, */
+ GDK_F2, /* wAccelKey_F2, */
+ GDK_F3, /* wAccelKey_F3, */
+ GDK_F4, /* wAccelKey_F4, */
+ GDK_F5, /* wAccelKey_F5, */
+ GDK_F6, /* wAccelKey_F6, */
+ GDK_F7, /* wAccelKey_F7, */
+ GDK_F8, /* wAccelKey_F8, */
+ GDK_F9, /* wAccelKey_F9, */
+ GDK_F10, /* wAccelKey_F10, */
+ GDK_F11, /* wAccelKey_F11, */
+ GDK_F12 /* wAccelKey_F12, */
+ };
+
+
+EXPORT void wAttachAccelKey(
+ wAccelKey_e key,
+ int modifier,
+ wAccelKeyCallBack_p action,
+ void * data )
+{
+ accelData_t * ad;
+ if ( key < 1 || key > wAccelKey_F12 ) {
+ fprintf( stderr, "wAttachAccelKey(%d) out of range\n", (int)key );
+ return;
+ }
+ DYNARR_APPEND( accelData_t, accelData_da, 10 );
+ ad = &accelData(accelData_da.cnt-1);
+ ad->key = key;
+ ad->modifier = modifier;
+ ad->action = action;
+ ad->data = data;
+}
+
+
+EXPORT struct accelData_t * gtkFindAccelKey(
+ GdkEventKey * event )
+{
+ accelData_t * ad;
+ int modifier = 0;
+ if ( ( event->state & GDK_SHIFT_MASK ) )
+ modifier |= WKEY_SHIFT;
+ if ( ( event->state & GDK_CONTROL_MASK ) )
+ modifier |= WKEY_CTRL;
+ if ( ( event->state & GDK_MOD1_MASK ) )
+ modifier |= WKEY_ALT;
+ for ( ad=&accelData(0); ad<&accelData(accelData_da.cnt); ad++ )
+ if ( event->keyval == accelKeyMap[ad->key] &&
+ modifier == ad->modifier )
+ return ad;
+ return NULL;
+}
+
+
+EXPORT wBool_t gtkHandleAccelKey(
+ GdkEventKey *event )
+{
+ accelData_t * ad = gtkFindAccelKey( event );
+ if ( ad ) {
+ ad->action( ad->key, ad->data );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ *****************************************************************************
+ *
+ * Timer Functions
+ *
+ *****************************************************************************
+ */
+
+static wBool_t gtkPaused = FALSE;
+static int alarmTimer = 0;
+
+static gint doAlarm(
+ gpointer data )
+{
+ wAlarmCallBack_p func = (wAlarmCallBack_p)data;
+ if (alarmTimer)
+ gtk_timeout_remove( alarmTimer );
+ func();
+ alarmTimer = 0;
+ return 0;
+}
+
+
+EXPORT void wAlarm(
+ long count,
+ wAlarmCallBack_p func ) /* milliseconds */
+/*
+Alarm for <count> milliseconds.
+*/
+{
+ gtkPaused = TRUE;
+ if (alarmTimer)
+ gtk_timeout_remove( alarmTimer );
+ alarmTimer = gtk_timeout_add( count, doAlarm, (void *) (GtkFunction)func );
+}
+
+
+static wControl_p triggerControl = NULL;
+static setTriggerCallback_p triggerFunc = NULL;
+
+static void doTrigger( void )
+{
+ if (triggerControl && triggerFunc) {
+ triggerFunc( triggerControl );
+ triggerFunc = NULL;
+ triggerControl = NULL;
+ }
+}
+
+void gtkSetTrigger(
+ wControl_p b,
+ setTriggerCallback_p trigger )
+{
+ triggerControl = b;
+ triggerFunc = trigger;
+ wAlarm( 500, doTrigger );
+}
+
+
+EXPORT void wPause(
+ long count ) /* milliseconds */
+/*
+Pause for <count> milliseconds.
+*/
+{
+ struct timeval timeout;
+ sigset_t signal_mask;
+ sigset_t oldsignal_mask;
+
+ gdk_display_sync(gdk_display_get_default());
+
+ timeout.tv_sec = count/1000;
+ timeout.tv_usec = (count%1000)*1000;
+
+ sigemptyset( &signal_mask );
+ sigaddset( &signal_mask, SIGIO );
+ sigaddset( &signal_mask, SIGALRM );
+ sigprocmask( SIG_BLOCK, &signal_mask, &oldsignal_mask );
+
+ if (select( 0, NULL, NULL, NULL, &timeout ) == -1) {
+ perror("wPause:select");
+ }
+ sigprocmask( SIG_BLOCK, &oldsignal_mask, NULL );
+}
+
+
+unsigned long wGetTimer( void )
+{
+ struct timeval tv;
+ struct timezone tz;
+ int rc;
+ rc = gettimeofday( &tv, &tz );
+ return (tv.tv_sec-startTime.tv_sec+1) * 1000 + tv.tv_usec /1000;
+}
+
+
+
+/**
+ * Add control to circular list of synonymous controls. Synonymous controls are kept in sync by
+ * calling wControlLinkedActive for one member of the list
+ *
+ * \param b1 IN first control
+ * \param b2 IN second control
+ * \return none
+ */
+
+EXPORT void wControlLinkedSet( wControl_p b1, wControl_p b2 )
+{
+
+ b2->synonym = b1->synonym;
+ if( b2->synonym == NULL )
+ b2->synonym = b1;
+
+ b1->synonym = b2;
+}
+
+/**
+ * Activate/deactivate a group of synonymous controls.
+ *
+ * \param b IN control
+ * \param active IN state
+ * \return none
+ */
+
+
+EXPORT void wControlLinkedActive( wControl_p b, int active )
+{
+ wControl_p savePtr = b;
+
+ if( savePtr->type == B_MENUITEM )
+ wMenuPushEnable( (wMenuPush_p)savePtr, active );
+ else
+ wControlActive( savePtr, active );
+
+ savePtr = savePtr->synonym;
+
+ while( savePtr && savePtr != b ) {
+
+ if( savePtr->type == B_MENUITEM )
+ wMenuPushEnable( (wMenuPush_p)savePtr, active );
+ else
+ wControlActive( savePtr, active );
+
+ savePtr = savePtr->synonym;
+ }
+}
+
+/*
+ *****************************************************************************
+ *
+ * Control Utilities
+ *
+ *****************************************************************************
+ */
+
+EXPORT void wControlShow(
+ wControl_p b, /* Control */
+ wBool_t show ) /* Command */
+/*
+Cause the control <b> to be displayed or hidden.
+Used to hide control (such as a list) while it is being updated.
+*/
+{
+ if ( b->type == B_LINES ) {
+ gtkLineShow( (wLine_p)b, show );
+ return;
+ }
+ if (b->widget == 0) abort();
+ if (show) {
+ gtk_widget_show( b->widget );
+ if (b->label)
+ gtk_widget_show( b->label );
+ } else {
+ gtk_widget_hide( b->widget );
+ if (b->label)
+ gtk_widget_hide( b->label );
+ }
+}
+
+EXPORT void wControlActive(
+ wControl_p b, /* Control */
+ int active ) /* Command */
+/*
+Cause the control <b> to be marked active or inactive.
+Inactive controls donot respond to actions.
+*/
+{
+ if (b->widget == 0) abort();
+ gtk_widget_set_sensitive( GTK_WIDGET(b->widget), active );
+}
+
+
+EXPORT wPos_t wLabelWidth(
+ const char * label ) /* Label */
+/*
+Returns the width of <label>.
+This is used for computing window layout.
+Typically the width to the longest label is computed and used as
+the X-position for <controls>.
+*/
+{
+ GtkWidget * widget;
+ GtkRequisition requisition;
+ widget = gtk_label_new( gtkConvertInput(label) );
+ gtk_widget_size_request( widget, &requisition );
+ gtk_widget_destroy( widget );
+ return requisition.width+8;
+}
+
+
+EXPORT wPos_t wControlGetWidth(
+ wControl_p b) /* Control */
+{
+ return b->w;
+}
+
+
+EXPORT wPos_t wControlGetHeight(
+ wControl_p b) /* Control */
+{
+ return b->h;
+}
+
+
+EXPORT wPos_t wControlGetPosX(
+ wControl_p b) /* Control */
+{
+ return b->realX;
+}
+
+
+EXPORT wPos_t wControlGetPosY(
+ wControl_p b) /* Control */
+{
+ return b->realY - FOUR - ((b->parent->option&F_MENUBAR)?MENUH:0);
+}
+
+
+EXPORT void wControlSetPos(
+ wControl_p b, /* Control */
+ wPos_t x, /* X-position */
+ wPos_t y ) /* Y-position */
+{
+ b->realX = x;
+ b->realY = y + FOUR + ((b->parent->option&F_MENUBAR)?MENUH:0);
+#ifndef GTK1
+ if (b->widget)
+ gtk_fixed_move( GTK_FIXED(b->parent->widget), b->widget, b->realX, b->realY );
+ if (b->label)
+ gtk_fixed_move( GTK_FIXED(b->parent->widget), b->label, b->realX-b->labelW, b->realY+LABEL_OFFSET );
+#else
+ if (b->widget)
+ gtk_widget_set_uposition( b->widget, b->realX, b->realY );
+ if (b->label)
+ gtk_widget_set_uposition( b->label, b->realX-b->labelW, b->realY+LABEL_OFFSET );
+#endif
+}
+
+
+EXPORT void wControlSetLabel(
+ wControl_p b,
+ const char * labelStr )
+{
+ GtkRequisition requisition;
+ if (b->label) {
+ gtk_label_set( GTK_LABEL(b->label), gtkConvertInput(labelStr) );
+ gtk_widget_size_request( b->label, &requisition );
+ b->labelW = requisition.width+8;
+#ifndef GTK1
+ gtk_fixed_move( GTK_FIXED(b->parent->widget), b->label, b->realX-b->labelW, b->realY+LABEL_OFFSET );
+#else
+ gtk_widget_set_uposition( b->label, b->realX-b->labelW, b->realY+LABEL_OFFSET );
+#endif
+ } else {
+ b->labelW = gtkAddLabel( b, labelStr );
+ }
+}
+
+EXPORT void wControlSetContext(
+ wControl_p b,
+ void * context )
+{
+ b->data = context;
+}
+
+
+EXPORT void wControlSetFocus(
+ wControl_p b )
+{
+}
+
+
+static int gtkControlHiliteWidth = 3;
+EXPORT void wControlHilite(
+ wControl_p b,
+ wBool_t hilite )
+{
+ int off = gtkControlHiliteWidth/2+1;
+ if ( b->parent->gc == NULL ) {
+ b->parent->gc = gdk_gc_new( b->parent->gtkwin->window );
+ gdk_gc_copy( b->parent->gc, b->parent->gtkwin->style->base_gc[GTK_STATE_NORMAL] );
+ b->parent->gc_linewidth = 0;
+ gdk_gc_set_line_attributes( b->parent->gc, b->parent->gc_linewidth, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+ if ( b->widget == NULL )
+ return;
+ if ( ! GTK_WIDGET_VISIBLE( b->widget ) )
+ return;
+ if ( ! GTK_WIDGET_VISIBLE( b->parent->widget ) )
+ return;
+ gdk_gc_set_foreground( b->parent->gc, gtkGetColor( wDrawColorBlack, FALSE ) );
+ gdk_gc_set_function( b->parent->gc, GDK_XOR );
+ gdk_gc_set_line_attributes( b->parent->gc, gtkControlHiliteWidth, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ gdk_draw_line( b->parent->widget->window, b->parent->gc,
+ b->realX - gtkControlHiliteWidth,
+ b->realY - off,
+ b->realX + b->w + gtkControlHiliteWidth,
+ b->realY - off );
+ gdk_draw_line( b->parent->widget->window, b->parent->gc,
+ b->realX - gtkControlHiliteWidth,
+ b->realY + b->h + off - 1,
+ b->realX + b->w + gtkControlHiliteWidth,
+ b->realY + b->h + off - 1 );
+ gdk_draw_line( b->parent->widget->window, b->parent->gc,
+ b->realX - off,
+ b->realY,
+ b->realX - off,
+ b->realY + b->h );
+ gdk_draw_line( b->parent->widget->window, b->parent->gc,
+ b->realX + b->w + off - 1,
+ b->realY,
+ b->realX + b->w + off - 1,
+ b->realY + b->h );
+}
+
+/*
+ *******************************************************************************
+ *
+ * Main
+ *
+ *******************************************************************************
+ */
+
+#ifdef GTK
+static wBool_t wAbortOnErrors = FALSE;
+#endif
+
+int do_rgb_init = 1;
+
+int main( int argc, char *argv[] )
+{
+ wWin_p win;
+ wControl_p b;
+ const char *ld, *hp;
+ static char buff[BUFSIZ];
+
+ if ( getenv( "GTKLIB_NOLOCALE" ) == 0 )
+ setlocale( LC_ALL, "en_US" );
+ gtk_init( &argc, &argv );
+ gettimeofday( &startTime, NULL );
+
+ if ( getenv( "XVLIB_REVERSEICON" ) != 0 )
+ reverseIcon = !reverseIcon;
+
+ if ( do_rgb_init )
+ gdk_rgb_init(); /* before we try to draw */
+
+ if ((win=wMain( argc, argv )) == (wWin_p)0)
+ exit(1);
+
+ ld = wGetAppLibDir();
+ if (ld != NULL) {
+ sprintf( buff, "HELPPATH=/usr/lib/help:%s:", ld );
+ if ( (hp = getenv("HELPPATH")) != NULL )
+ strcat( buff, hp );
+ putenv( buff );
+ }
+
+ if (!win->shown)
+ wWinShow( win, TRUE );
+ for (b=win->first; b != NULL; b = b->next) {
+ if (b->repaintProc)
+ b->repaintProc( b );
+ }
+ gtk_main();
+ exit(0);
+}
diff --git a/app/wlib/gtklib/gtksimple.c b/app/wlib/gtklib/gtksimple.c
new file mode 100644
index 0000000..244c0a3
--- /dev/null
+++ b/app/wlib/gtklib/gtksimple.c
@@ -0,0 +1,366 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtksimple.c,v 1.6 2009-09-25 05:38:15 dspagnol Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "gtkint.h"
+
+static int windowxxx = 0;
+/*
+ *****************************************************************************
+ *
+ * Message Boxes
+ *
+ *****************************************************************************
+ */
+
+struct wMessage_t {
+ WOBJ_COMMON
+ GtkWidget * labelWidget;
+ const char * message;
+ wPos_t labelWidth;
+ };
+
+EXPORT void wMessageSetValue(
+ wMessage_p b,
+ const char * arg )
+{
+ if (b->widget == 0) abort();
+ gtk_label_set( GTK_LABEL( b->labelWidget ), gtkConvertInput(arg) );
+}
+
+
+EXPORT void wMessageSetWidth(
+ wMessage_p b,
+ wPos_t width )
+{
+ b->labelWidth = width;
+#ifndef GTK1
+ gtk_widget_set_size_request( b->widget, width, -1 );
+#else
+ gtk_widget_set_usize( b->widget, width, -1 );
+#endif
+}
+
+
+EXPORT wPos_t wMessageGetHeight(
+ long flags )
+{
+ return 14;
+}
+
+/**
+ * Create a window for a simple text.
+ *
+ * \param IN parent Handle of parent window
+ * \param IN x position in x direction
+ * \param IN y position in y direction
+ * \param IN labelStr ???
+ * \param IN width horizontal size of window
+ * \param IN message message to display ( null terminated )
+ * \param IN flags display options
+ * \return handle for created window
+ */
+
+EXPORT wMessage_p wMessageCreateEx(
+ wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char * labelStr,
+ wPos_t width,
+ const char *message,
+ long flags )
+{
+ wMessage_p b;
+ GtkRequisition requisition;
+ PangoFontDescription *fontDesc;
+ int fontSize;
+
+ b = (wMessage_p)gtkAlloc( parent, B_MESSAGE, x, y, NULL, sizeof *b, NULL );
+ gtkComputePos( (wControl_p)b );
+ b->message = message;
+ b->labelWidth = width;
+
+ b->labelWidget = gtk_label_new( message?gtkConvertInput(message):"" );
+
+ /* do we need to set a special font? */
+ if( wMessageSetFont( flags )) {
+ /* get the current font descriptor */
+ fontDesc = (b->labelWidget)->style->font_desc;
+
+ /* get the current font size */
+ fontSize = PANGO_PIXELS(pango_font_description_get_size( fontDesc ));
+
+ /* calculate the new font size */
+ if( flags & BM_LARGE ) {
+ pango_font_description_set_size( fontDesc, fontSize * 1.4 * PANGO_SCALE );
+ } else {
+ pango_font_description_set_size( fontDesc, fontSize * 0.7 * PANGO_SCALE );
+ }
+
+ /* set the new font size */
+ gtk_widget_modify_font( (GtkWidget *)b->labelWidget, fontDesc );
+ }
+
+ b->widget = gtk_fixed_new();
+ gtk_widget_size_request( GTK_WIDGET(b->labelWidget), &requisition );
+ gtk_container_add( GTK_CONTAINER(b->widget), b->labelWidget );
+
+ gtk_widget_set_size_request( b->widget, width?width:requisition.width, requisition.height );
+ gtkControlGetSize( (wControl_p)b );
+ gtk_fixed_put( GTK_FIXED(parent->widget), b->widget, b->realX, b->realY );
+
+ gtk_widget_show( b->widget );
+ gtk_widget_show( b->labelWidget );
+ gtkAddButton( (wControl_p)b );
+
+ /* Reset font size to normal */
+ if( wMessageSetFont( flags )) {
+ if( flags & BM_LARGE ) {
+ pango_font_description_set_size(fontDesc, fontSize * PANGO_SCALE);
+ } else {
+ pango_font_description_set_size(fontDesc, fontSize * PANGO_SCALE);
+ }
+ }
+ return b;
+}
+
+/*
+ *****************************************************************************
+ *
+ * Lines
+ *
+ *****************************************************************************
+ */
+
+struct wLine_t {
+ WOBJ_COMMON
+ wBool_t visible;
+ int count;
+ wLines_t * lines;
+ };
+
+static void linesRepaint( wControl_p b )
+{
+ wLine_p bl = (wLine_p)(b);
+ int i;
+ wWin_p win = (wWin_p)(bl->parent);
+ GdkDrawable * window;
+ GdkColor *black;
+
+ if (!bl->visible)
+ return;
+if (windowxxx)
+ window = win->gtkwin->window;
+else
+ window = win->widget->window;
+
+ /* get the GC attached to the panel in main() */
+ black = gtkGetColor( wDrawColorBlack, TRUE );
+ gdk_gc_set_foreground( win->gc, black );
+ gdk_gc_set_function( win->gc, GDK_COPY );
+ for (i=0; i<bl->count; i++) {
+ if (win->gc_linewidth != bl->lines[i].width) {
+ win->gc_linewidth = bl->lines[i].width;
+ gdk_gc_set_line_attributes( win->gc, win->gc_linewidth, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+ gdk_draw_line( window, win->gc,
+ bl->lines[i].x0, bl->lines[i].y0,
+ bl->lines[i].x1, bl->lines[i].y1 );
+ }
+}
+
+
+void gtkLineShow(
+ wLine_p bl,
+ wBool_t visible )
+{
+ bl->visible = visible;
+}
+
+
+wLine_p wLineCreate(
+ wWin_p parent,
+ const char * labelStr,
+ int count,
+ wLines_t * lines )
+{
+ wLine_p b;
+ int i;
+ b = (wLine_p)gtkAlloc( parent, B_LINES, 0, 0, labelStr, sizeof *b, NULL );
+ if (parent->gc == NULL) {
+ parent->gc = gdk_gc_new( parent->gtkwin->window );
+ gdk_gc_copy( parent->gc, parent->gtkwin->style->base_gc[GTK_STATE_NORMAL] );
+ parent->gc_linewidth = 0;
+ gdk_gc_set_line_attributes( parent->gc, parent->gc_linewidth, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+ b->visible = TRUE;
+ b->count = count;
+ b->lines = lines;
+
+ b->w = b->h = 0;
+ for ( i=0; i<count; i++ ) {
+ if (lines[i].x0 > b->w)
+ b->w = lines[i].x0;
+ if (lines[i].y0 > b->h)
+ b->h = lines[i].y0;
+ if (lines[i].x1 > b->w)
+ b->w = lines[i].x1;
+ if (lines[i].y1 > b->h)
+ b->h = lines[i].y1;
+ }
+ b->repaintProc = linesRepaint;
+ gtkAddButton( (wControl_p)b );
+ b->widget = 0;
+ return b;
+}
+
+
+/*
+ *****************************************************************************
+ *
+ * Boxes
+ *
+ *****************************************************************************
+ */
+
+struct wBox_t {
+ WOBJ_COMMON
+ wBoxType_e boxTyp;
+ };
+
+#define B (1)
+#define W (2)
+#define SETCOLOR( S, N ) \
+ if ( lastColor != colors[style][S][N] ) { \
+ lastColor = colors[style][S][N]; \
+ gdk_gc_set_foreground( win->gc, (lastColor==B)?black:white ); \
+ }
+
+EXPORT void wBoxSetSize(
+ wBox_p b, /* */
+ wPos_t w, /* */
+ wPos_t h ) /* */
+{
+ b->w = w;
+ b->h = h;
+}
+
+
+EXPORT void gtkDrawBox(
+ wWin_p win,
+ wBoxType_e style,
+ wPos_t x,
+ wPos_t y,
+ wPos_t w,
+ wPos_t h )
+{
+ wPos_t x0, y0, x1, y1;
+ char lastColor;
+ GdkColor *white;
+ GdkColor *black;
+ GdkDrawable * window;
+ static char colors[8][4][2] = {
+ { /* ThinB */ {B,0}, {B,0}, {B,0}, {B,0} },
+ { /* ThinW */ {W,0}, {W,0}, {W,0}, {W,0} },
+ { /* AboveW */ {W,0}, {W,0}, {B,0}, {B,0} },
+ { /* BelowW */ {B,0}, {B,0}, {W,0}, {W,0} },
+ { /* ThickB */ {B,B}, {B,B}, {B,B}, {B,B} },
+ { /* ThickW */ {W,W}, {W,W}, {W,W}, {W,W} },
+ { /* RidgeW */ {W,B}, {W,B}, {B,W}, {B,W} },
+ { /* TroughW*/ {B,W}, {B,W}, {W,B}, {W,B} } };
+
+if (windowxxx)
+ window = win->gtkwin->window;
+else
+ window = win->widget->window;
+ white = gtkGetColor( wDrawColorWhite, TRUE );
+ black = gtkGetColor( wDrawColorBlack, TRUE );
+ win->gc_linewidth = 0;
+ gdk_gc_set_line_attributes( win->gc, 0, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ gdk_gc_set_function( win->gc, GDK_COPY );
+ x0 = x;
+ x1 = x+w;
+ y0 = y;
+ y1 = y+h;
+ lastColor = colors[style][0][0];
+ gdk_gc_set_foreground( win->gc, (lastColor==B)?black:white );
+ gdk_draw_line( window, win->gc, x0, y0, x0, y1 );
+ SETCOLOR( 1, 0 );
+ gdk_draw_line( window, win->gc, x0+1, y0, x1, y0 );
+ SETCOLOR( 2, 0 );
+ gdk_draw_line( window, win->gc, x1, y1, x0+1, y1 );
+ SETCOLOR( 3, 0 );
+ gdk_draw_line( window, win->gc, x1, y1-1, x1, y0+1 );
+ if (style < wBoxThickB)
+ return;
+ x0++; y0++; x1--; y1--;
+ SETCOLOR( 0, 1 );
+ gdk_draw_line( window, win->gc, x0, y0, x0, y1 );
+ SETCOLOR( 1, 1 );
+ gdk_draw_line( window, win->gc, x0+1, y0, x1, y0 );
+ SETCOLOR( 2, 1 );
+ gdk_draw_line( window, win->gc, x1, y1, x0+1, y1 );
+ SETCOLOR( 3, 1 );
+ gdk_draw_line( window, win->gc, x1, y1-1, x1, y0+1 );
+ gdk_gc_set_foreground( win->gc, black );
+}
+
+
+static void boxRepaint( wControl_p b )
+{
+ wBox_p bb = (wBox_p)(b);
+ wWin_p win = bb->parent;
+
+ gtkDrawBox( win, bb->boxTyp, bb->realX, bb->realY, bb->w, bb->h );
+}
+
+
+wBox_p wBoxCreate(
+ wWin_p parent,
+ wPos_t bx,
+ wPos_t by,
+ const char * labelStr,
+ wBoxType_e boxTyp,
+ wPos_t bw,
+ wPos_t bh )
+{
+ wBox_p b;
+ b = (wBox_p)gtkAlloc( parent, B_BOX, bx, by, labelStr, sizeof *b, NULL );
+ gtkComputePos( (wControl_p)b );
+ b->boxTyp = boxTyp;
+ b->w = bw;
+ b->h = bh;
+ if (parent->gc == NULL) {
+ parent->gc = gdk_gc_new( parent->gtkwin->window );
+ gdk_gc_copy( parent->gc, parent->gtkwin->style->base_gc[GTK_STATE_NORMAL] );
+ parent->gc_linewidth = 0;
+ gdk_gc_set_line_attributes( parent->gc, parent->gc_linewidth, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+ b->repaintProc = boxRepaint;
+ gtkAddButton( (wControl_p)b );
+ return b;
+}
+
diff --git a/app/wlib/gtklib/gtksingle.c b/app/wlib/gtklib/gtksingle.c
new file mode 100644
index 0000000..d106e17
--- /dev/null
+++ b/app/wlib/gtklib/gtksingle.c
@@ -0,0 +1,645 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtksingle.c,v 1.2 2009-05-15 18:54:20 m_fischer Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <gtk/gtk.h>
+
+#include "gtkint.h"
+
+/*
+ *****************************************************************************
+ *
+ * Text Boxes
+ *
+ *****************************************************************************
+ */
+
+struct wString_t {
+ WOBJ_COMMON
+ char * valueP;
+ wIndex_t valueL;
+ wStringCallBack_p action;
+ wBool_t busy;
+ };
+
+void wStringSetValue(
+ wString_p b,
+ const char * arg )
+{
+ wBool_t busy;
+ if (b->widget == 0) abort();
+ busy = b->busy;
+ b->busy = TRUE;
+ gtk_entry_set_text( GTK_ENTRY(b->widget), arg );
+ b->busy = busy;
+}
+
+
+void wStringSetWidth(
+ wString_p b,
+ wPos_t w )
+{
+#ifndef GTK1
+ gtk_widget_set_size_request( b->widget, w, -1 );
+#else
+ gtk_widget_set_usize( b->widget, w, -1 );
+#endif
+ b->w = w;
+}
+
+
+const char * wStringGetValue(
+ wString_p b )
+{
+ if (b->widget == 0) abort();
+ return gtk_entry_get_text( GTK_ENTRY(b->widget) );
+}
+
+
+static void triggerString(
+ wControl_p b )
+{
+ wString_p bs = (wString_p)b;
+ const char * s;
+
+ if (b == 0)
+ return;
+ if (bs->widget == 0) abort();
+ s = gtk_entry_get_text( GTK_ENTRY(bs->widget) );
+ if (debugWindow >= 2) printf("%s text = %s\n", bs->labelStr?bs->labelStr:"No label", s );
+ if (s == NULL)
+ return;
+ if (debugWindow >= 2) printf("triggerString( %s )\n", s );
+ if (bs->action) {
+ bs->busy = TRUE;
+ bs->action( s, bs->data );
+ bs->busy = FALSE;
+ }
+ gtkSetTrigger( NULL, NULL );
+ return;
+}
+
+
+static void stringActivated(
+ GtkEntry * widget,
+ wString_p b )
+{
+ const char * s;
+ if (b == 0)
+ return;
+ s = wStringGetValue(b);
+
+ if (debugWindow >= 2) printf("%s text = %s\n", b->labelStr?b->labelStr:"No label", s );
+ if (b->valueP)
+ strcpy( b->valueP, s );
+ if (b->action) {
+ b->busy = TRUE;
+ b->action( s, b->data );
+ b->busy = FALSE;
+ }
+}
+
+static void stringChanged(
+ GtkEntry * widget,
+ wString_p b )
+{
+ const char *new_value;
+ if (b == 0)
+ return;
+ if (b->busy)
+ return;
+ new_value = wStringGetValue(b);
+ if (b->valueP != NULL)
+ strcpy( b->valueP, new_value );
+ if (b->action)
+ gtkSetTrigger( (wControl_p)b, triggerString );
+ return;
+}
+
+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;
+
+ b = (wString_p)gtkAlloc( parent, B_TEXT, x, y, labelStr, sizeof *b, data );
+ b->valueP = valueP;
+ b->action = action;
+ b->option = option;
+ b->valueL = valueL;
+ gtkComputePos( (wControl_p)b );
+
+ if (valueL) {
+ b->widget = (GtkWidget*)gtk_entry_new_with_max_length( valueL );
+ } else {
+ b->widget = (GtkWidget*)gtk_entry_new();
+ }
+ if (b->widget == 0) abort();
+
+#ifndef GTK1
+ gtk_fixed_put( GTK_FIXED(parent->widget), b->widget, b->realX, b->realY );
+#else
+ gtk_container_add( GTK_CONTAINER(parent->widget), b->widget );
+ gtk_widget_set_uposition( b->widget, b->realX, b->realY );
+#endif
+ if ( width )
+#ifndef GTK1
+ gtk_widget_set_size_request( b->widget, width, -1 );
+#else
+ gtk_widget_set_usize( b->widget, width, -1 );
+#endif
+ gtkControlGetSize( (wControl_p)b );
+ if (labelStr)
+ b->labelW = gtkAddLabel( (wControl_p)b, labelStr );
+ /*b->w += 4;*/
+ /*b->h += 4;*/
+ if (b->valueP)
+ wStringSetValue( b, b->valueP );
+ gtk_widget_show( b->widget );
+ gtkAddButton( (wControl_p)b );
+ gtkAddHelpString( b->widget, helpStr );
+ gtk_signal_connect( GTK_OBJECT(b->widget), "changed", GTK_SIGNAL_FUNC(stringChanged), b );
+ gtk_signal_connect( GTK_OBJECT(b->widget), "activate", GTK_SIGNAL_FUNC(stringActivated), b );
+ if (option & BO_READONLY)
+ gtk_entry_set_editable( GTK_ENTRY(b->widget), FALSE );
+ return b;
+}
+
+/*
+ *****************************************************************************
+ *
+ * Floating Point Value Boxes
+ *
+ *****************************************************************************
+ */
+
+
+struct wFloat_t {
+ WOBJ_COMMON
+ double low, high;
+ double oldValue;
+ double * valueP;
+ wFloatCallBack_p action;
+ wBool_t busy;
+ };
+
+
+void wFloatSetValue(
+ wFloat_p b,
+ double arg )
+{
+ char message[80];
+ if (b->widget == 0) abort();
+ sprintf( message, "%0.3f", arg );
+ if (!b->busy) {
+ b->busy = TRUE;
+ gtk_entry_set_text( GTK_ENTRY(b->widget), message );
+ b->busy = FALSE;
+ }
+ if (b->valueP)
+ *b->valueP = arg;
+}
+
+
+double wFloatGetValue(
+ wFloat_p b )
+{
+ double ret;
+ const char * cp;
+ if (b->widget == 0) abort();
+ cp = gtk_entry_get_text( GTK_ENTRY(b->widget) );
+ ret = atof( cp );
+ return ret;
+}
+
+
+static void triggerFloat(
+ wControl_p b )
+{
+ wFloat_p bf = (wFloat_p)b;
+ const char * s;
+ char * cp;
+ double v;
+
+ if (b == 0)
+ return;
+ if (bf->widget == 0) abort();
+ s = gtk_entry_get_text( GTK_ENTRY(bf->widget) );
+ if (debugWindow >= 2) printf("%s text = %s\n", bf->labelStr?bf->labelStr:"No label", s );
+ if (s == NULL)
+ return;
+ v = strtod( s, &cp );
+ if (*cp!=0 || v < bf->low || v > bf->high)
+ return;
+ /*if (bf->oldValue == v)
+ return;*/
+ if (debugWindow >= 2) printf("triggerFloat( %0.3f )\n", v );
+ bf->oldValue = v;
+ if (bf->valueP)
+ *bf->valueP = v;
+ if (bf->action) {
+ bf->busy = TRUE;
+ bf->action( v, bf->data );
+ bf->busy = FALSE;
+ }
+ gtkSetTrigger( NULL, NULL );
+ return;
+}
+
+
+static void floatActivated(
+ GtkEntry *widget,
+ wFloat_p b )
+{
+ const char * s;
+ char * cp;
+ double v;
+ char val_s[80];
+
+ if (b == 0)
+ return;
+ if (b->widget == 0) abort();
+ s = gtk_entry_get_text( GTK_ENTRY(b->widget) );
+ if (debugWindow >= 2) printf("%s text = %s\n", b->labelStr?b->labelStr:"No label", s );
+ if (s != NULL) {
+ v = strtod( s, &cp );
+ if (*cp != '\n' && *cp != '\0') {
+ wNoticeEx( NT_ERROR, "The value you have entered is not a valid number\nPlease try again", "Ok", NULL );
+ } else if (v < b->low || v > b->high) {
+ sprintf( val_s, "Please enter a value between %0.3f and %0.3f", b->low, b->high );
+ wNoticeEx( NT_ERROR, val_s, "Ok", NULL );
+ } else {
+ if (debugWindow >= 2) printf("floatActivated( %0.3f )\n", v );
+ b->oldValue = v;
+ if (b->valueP)
+ *b->valueP = v;
+ if (b->action) {
+ gtkSetTrigger( NULL, NULL );
+ b->busy = TRUE;
+ b->action( v, b->data );
+ b->busy = FALSE;
+ }
+ return;
+ }
+ sprintf( val_s, "%0.3f", b->oldValue);
+ b->busy = TRUE;
+ gtk_entry_set_text( GTK_ENTRY(b->widget), val_s );
+ b->busy = FALSE;
+ }
+ return;
+}
+
+static void floatChanged(
+ GtkEntry *widget,
+ wFloat_p b )
+{
+ const char * s;
+ char * cp;
+ double v;
+
+ if (b == 0)
+ return;
+ if (b->widget == 0) abort();
+ if (b->busy)
+ return;
+ s = gtk_entry_get_text( GTK_ENTRY(b->widget) );
+ if (s == NULL)
+ return;
+ if (debugWindow >= 2) printf("%s text = %s\n", b->labelStr?b->labelStr:"No label", s );
+ if ( s[0] == '\0' ||
+ strcmp( s, "-" ) == 0 ||
+ strcmp( s, "." ) == 0 ) {
+ v = 0;
+ } else {
+ v = strtod( s, &cp );
+ if (*cp != '\0'
+#ifdef LATER
+ || v < b->low || v > b->high
+#endif
+ ) {
+ wBeep();
+ wFloatSetValue( b, b->oldValue );
+ return;
+ }
+ }
+ b->oldValue = v;
+ if (b->valueP != NULL) {
+ *b->valueP = v;
+ }
+ if (b->action)
+ gtkSetTrigger( (wControl_p)b, triggerFloat );
+ return;
+}
+
+wFloat_p wFloatCreate(
+ wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char * helpStr,
+ const char * labelStr,
+ long option,
+ wPos_t width,
+ double low,
+ double high,
+ double *valueP,
+ wFloatCallBack_p action,
+ void *data )
+{
+ wFloat_p b;
+
+ b = (wFloat_p)gtkAlloc( parent, B_TEXT, x, y, labelStr, sizeof *b, data );
+ b->valueP = valueP;
+ b->action = action;
+ b->option = option;
+ b->low = low;
+ b->high = high;
+ gtkComputePos( (wControl_p)b );
+
+ b->widget = (GtkWidget*)gtk_entry_new_with_max_length( 20 );
+ if (b->widget == 0) abort();
+
+#ifndef GTK1
+ gtk_fixed_put( GTK_FIXED(parent->widget), b->widget, b->realX, b->realY );
+#else
+ gtk_container_add( GTK_CONTAINER(parent->widget), b->widget );
+ gtk_widget_set_uposition( b->widget, b->realX, b->realY );
+#endif
+ if ( width )
+#ifndef GTK1
+ gtk_widget_set_size_request( b->widget, width, -1 );
+#else
+ gtk_widget_set_usize( b->widget, width, -1 );
+#endif
+ gtkControlGetSize( (wControl_p)b );
+ if (labelStr)
+ b->labelW = gtkAddLabel( (wControl_p)b, labelStr );
+ /*b->w += 4;*/
+ /*b->h += 4;*/
+ if (b->valueP)
+ wFloatSetValue( b, *b->valueP );
+ else
+ wFloatSetValue( b, b->low>0?b->low:0.0 );
+ gtk_widget_show( b->widget );
+ gtkAddButton( (wControl_p)b );
+ gtkAddHelpString( b->widget, helpStr );
+ gtk_signal_connect( GTK_OBJECT(b->widget), "changed", GTK_SIGNAL_FUNC(floatChanged), b );
+ gtk_signal_connect( GTK_OBJECT(b->widget), "activate", GTK_SIGNAL_FUNC(floatActivated), b );
+ if (option & BO_READONLY)
+ gtk_entry_set_editable( GTK_ENTRY(b->widget), FALSE );
+ return b;
+}
+
+/*
+ *****************************************************************************
+ *
+ * Integer Value Boxes
+ *
+ *****************************************************************************
+ */
+
+
+struct wInteger_t {
+ WOBJ_COMMON
+ long low, high;
+ long oldValue;
+ long * valueP;
+ wIntegerCallBack_p action;
+ wBool_t busy;
+ };
+
+
+void wIntegerSetValue(
+ wInteger_p b,
+ long arg )
+{
+ char message[80];
+ if (b->widget == 0) abort();
+ sprintf( message, "%ld", arg );
+ if (!b->busy) {
+ b->busy = TRUE;
+ gtk_entry_set_text( GTK_ENTRY(b->widget), message );
+ b->busy = FALSE;
+ }
+ if (b->valueP)
+ *b->valueP = arg;
+}
+
+
+long wIntegerGetValue(
+ wInteger_p b )
+{
+ long ret;
+ const char * cp;
+ if (b->widget == 0) abort();
+ cp = gtk_entry_get_text( GTK_ENTRY(b->widget) );
+ ret = atol( cp );
+ return ret;
+}
+
+
+static void triggerInteger(
+ wControl_p b )
+{
+ wInteger_p bi = (wInteger_p)b;
+ const char * s;
+ char * cp;
+ long v;
+
+ if (b == 0)
+ return;
+ if (bi->widget == 0) abort();
+ s = gtk_entry_get_text( GTK_ENTRY(bi->widget) );
+ if (debugWindow >= 2) printf("%s text = %s\n", bi->labelStr?bi->labelStr:"No label", s );
+ if (s == NULL)
+ return;
+ v = strtol( s, &cp, 10 );
+ if (*cp!=0 || v < bi->low || v > bi->high)
+ return;
+ /*if (bi->oldValue == v)
+ return;*/
+ if (debugWindow >= 2) printf("triggerInteger( %ld )\n", v );
+ bi->oldValue = v;
+ if (bi->valueP)
+ *bi->valueP = v;
+ if (bi->action) {
+ bi->busy = TRUE;
+ bi->action( v, bi->data );
+ bi->busy = FALSE;
+ }
+ gtkSetTrigger( NULL, NULL );
+ return;
+}
+
+
+
+static void integerActivated(
+ GtkEntry *widget,
+ wInteger_p b )
+{
+ const char * s;
+ char * cp;
+ long v;
+ char val_s[80];
+
+ if (b == 0)
+ return;
+ if (b->widget == 0) abort();
+ s = gtk_entry_get_text( GTK_ENTRY(b->widget) );
+ if (debugWindow >= 2) printf("%s text = %s\n", b->labelStr?b->labelStr:"No label", s );
+ if (s != NULL) {
+ v = strtod( s, &cp );
+ if (*cp != '\n' && *cp != '\0') {
+ wNoticeEx( NT_ERROR, "The value you have entered is not a valid number\nPlease try again", "Ok", NULL );
+ } else if (v < b->low || v > b->high) {
+ sprintf( val_s, "Please enter a value between %ld and %ld", b->low, b->high );
+ wNoticeEx( NT_ERROR, val_s, "Ok", NULL );
+ } else {
+ if (debugWindow >= 2) printf("integerActivated( %ld )\n", v );
+ b->oldValue = v;
+ if (b->valueP)
+ *b->valueP = v;
+ if (b->action) {
+ gtkSetTrigger( NULL, NULL );
+ b->busy = TRUE;
+ b->action( v, b->data );
+ b->busy = FALSE;
+ }
+ return;
+ }
+ sprintf( val_s, "%ld", b->oldValue);
+ b->busy = TRUE;
+ gtk_entry_set_text( GTK_ENTRY(b->widget), val_s );
+ b->busy = FALSE;
+ }
+ return;
+}
+
+static void integerChanged(
+ GtkEntry *widget,
+ wInteger_p b )
+{
+ const char * s;
+ char * cp;
+ long v;
+
+ if (b == 0)
+ return;
+ if (b->widget == 0) abort();
+ if (b->busy)
+ return;
+ s = gtk_entry_get_text( GTK_ENTRY(b->widget) );
+ if (s == NULL)
+ return;
+ if (debugWindow >= 2) printf("%s text = %s\n", b->labelStr?b->labelStr:"No label", s );
+ if ( s[0] == '\0' ||
+ strcmp( s, "-" ) == 0 ) {
+ v = 0;
+ } else {
+ v = strtol( s, &cp, 10 );
+ if (*cp != '\0'
+#ifdef LATER
+ || v < b->low || v > b->high
+#endif
+ ) {
+ wBeep();
+ wIntegerSetValue( b, b->oldValue );
+ return;
+ }
+ }
+ b->oldValue = v;
+ if (b->valueP != NULL) {
+ *b->valueP = v;
+ }
+ if (b->action)
+ gtkSetTrigger( (wControl_p)b, triggerInteger );
+ return;
+}
+
+wInteger_p wIntegerCreate(
+ wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char * helpStr,
+ const char * labelStr,
+ long option,
+ wPos_t width,
+ long low,
+ long high,
+ long *valueP,
+ wIntegerCallBack_p action,
+ void *data )
+{
+ wInteger_p b;
+
+ b = (wInteger_p)gtkAlloc( parent, B_TEXT, x, y, labelStr, sizeof *b, data );
+ b->valueP = valueP;
+ b->action = action;
+ b->option = option;
+ b->low = low;
+ b->high = high;
+ gtkComputePos( (wControl_p)b );
+
+ b->widget = (GtkWidget*)gtk_entry_new_with_max_length( 20 );
+ if (b->widget == 0) abort();
+
+#ifndef GTK1
+ gtk_fixed_put( GTK_FIXED(parent->widget), b->widget, b->realX, b->realY );
+#else
+ gtk_container_add( GTK_CONTAINER(parent->widget), b->widget );
+ gtk_widget_set_uposition( b->widget, b->realX, b->realY );
+#endif
+ if ( width )
+#ifndef GTK1
+ gtk_widget_set_size_request( b->widget, width, -1 );
+#else
+ gtk_widget_set_usize( b->widget, width, -1 );
+#endif
+ gtkControlGetSize( (wControl_p)b );
+ if (labelStr)
+ b->labelW = gtkAddLabel( (wControl_p)b, labelStr );
+ /*b->w += 4;*/
+ /*b->h += 4;*/
+ if (b->valueP)
+ wIntegerSetValue( b, *b->valueP );
+ else
+ wIntegerSetValue( b, b->low>0?b->low:0.0 );
+ gtk_widget_show( b->widget );
+ gtkAddButton( (wControl_p)b );
+ gtkAddHelpString( b->widget, helpStr );
+ gtk_signal_connect( GTK_OBJECT(b->widget), "changed", GTK_SIGNAL_FUNC(integerChanged), b );
+ gtk_signal_connect( GTK_OBJECT(b->widget), "activate", GTK_SIGNAL_FUNC(integerActivated), b );
+ if (option & BO_READONLY)
+ gtk_entry_set_editable( GTK_ENTRY(b->widget), FALSE );
+ return b;
+}
diff --git a/app/wlib/gtklib/gtksplash.c b/app/wlib/gtklib/gtksplash.c
new file mode 100644
index 0000000..0f49774
--- /dev/null
+++ b/app/wlib/gtklib/gtksplash.c
@@ -0,0 +1,142 @@
+/** \file gtksplash.c
+ * Handling of the Splash Window functions
+ *
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtksplash.c,v 1.5 2009-05-31 14:48:58 m_fischer Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2007 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.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "gtkint.h"
+
+#define LOGOFILENAME "logo.bmp"
+
+static GtkWidget *window; /** splash window handle */
+static GtkWidget *message; /** window handle for progress message */
+
+/**
+ * Create the splash window shown during startup. The function loads the logo
+ * bitmap and displays the program name and version as passed.
+ *
+ * \param IN appName the product name to be shown
+ * \param IN appVer the product version to be shown
+ * \return TRUE if window was created, FALSE if an error occured
+ */
+
+int
+wCreateSplash( char *appName, char *appVer )
+{
+ GtkWidget *vbox;
+ GtkWidget *image;
+ GtkWidget *label;
+ char *temp;
+ char logoPath[BUFSIZ];
+
+ /* create the basic window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_decorated( GTK_WINDOW (window), FALSE );
+ gtk_window_set_title (GTK_WINDOW (window), appName);
+ gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
+ gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
+ gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
+#if GTK_MINOR_VERSION > 5
+ gtk_window_set_focus_on_map (GTK_WINDOW (window), FALSE);
+#endif
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_widget_show (vbox);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+
+ /* add the logo image to the top of the splash window */
+ sprintf( logoPath, "%s/" LOGOFILENAME, wGetAppLibDir());
+ image = gtk_image_new_from_file ( logoPath );
+ gtk_widget_show (image);
+ gtk_box_pack_start (GTK_BOX (vbox), image, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC (image), 0, 0);
+
+ /* put the product name into the window */
+
+ temp = malloc( strlen( appName ) + strlen( appVer ) + 2 );
+ if( !temp )
+ return( FALSE );
+
+ sprintf( temp, "%s %s", appName, appVer );
+
+ label = gtk_label_new ( temp );
+ gtk_widget_show (label);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_FILL);
+ gtk_label_set_selectable (GTK_LABEL (label), FALSE);
+ gtk_misc_set_padding (GTK_MISC (label), 6, 2);
+
+ free( temp );
+
+ label = gtk_label_new ("Application is starting...");
+ gtk_widget_show (label);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), FALSE);
+ gtk_misc_set_padding (GTK_MISC (label), 6, 2);
+#if GTK_MINOR_VERSION > 5
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_START);
+#endif
+ message = label;
+
+ gtk_widget_show( window );
+
+ return( TRUE );
+}
+
+/**
+ * Update the progress message inside the splash window
+ * msg text message to display
+ * return nonzero if ok
+ */
+
+int
+wSetSplashInfo( char *msg )
+{
+ if( msg ) {
+ gtk_label_set_text( (GtkLabel *)message, msg );
+ wFlush();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Destroy the splash window.
+ *
+ */
+
+void
+wDestroySplash( void )
+{
+ /* kill window */
+ gtk_widget_destroy( window );
+ return;
+}
diff --git a/app/wlib/gtklib/gtktext.c b/app/wlib/gtklib/gtktext.c
new file mode 100644
index 0000000..8da31d3
--- /dev/null
+++ b/app/wlib/gtklib/gtktext.c
@@ -0,0 +1,524 @@
+/** \file gtktext.c
+ * Multi line text entry
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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_ENABLE_BROKEN
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gtkint.h"
+
+/*
+ * Disable USE_TEXTVIEW to use the deprecated gtk_text
+ */
+#define USE_TEXTVIEW
+
+/*
+ *****************************************************************************
+ *
+ * Multi-line Text Boxes
+ *
+ *****************************************************************************
+ */
+
+struct wText_t {
+ WOBJ_COMMON
+ wPos_t width, height;
+ int changed;
+ GtkWidget * text;
+ GtkWidget * vscroll;
+ };
+
+EXPORT void wTextClear(
+ wText_p bt )
+{
+#ifdef USE_TEXTVIEW
+ GtkTextBuffer * tb;
+#endif
+ if (bt->text == 0) abort();
+#ifdef USE_TEXTVIEW
+ tb = gtk_text_view_get_buffer( GTK_TEXT_VIEW(bt->text) );
+ gtk_text_buffer_set_text( tb, "", -1 );
+ if (bt->option & BO_READONLY)
+ gtk_text_view_set_editable( GTK_TEXT_VIEW(bt->text), FALSE );
+#else
+ gtk_text_set_point( GTK_TEXT(bt->text), 0 );
+ gtk_text_forward_delete( GTK_TEXT(bt->text), gtk_text_get_length( GTK_TEXT(bt->text) ) );
+ if (bt->option & BO_READONLY)
+ gtk_text_set_editable( GTK_TEXT(bt->text), FALSE );
+#endif
+ bt->changed = FALSE;
+}
+
+/**
+ * Add text to a multiline text field. Font is selected as requested.
+ * Bold is supported if the flags BT_BOLD is set as flags for the entry
+ * field. For bold, pango text markup is used
+ *
+ *
+ * \param bt IN the text field
+ * \param text IN text to add
+ */
+
+EXPORT void wTextAppend(
+ wText_p bt,
+ const char * text )
+{
+#ifdef USE_TEXTVIEW
+ GtkTextBuffer * tb;
+ GtkTextIter ti1, ti2;
+// PangoFontDescription *pfd;
+#else
+ static GdkFont * fixedRegularFont = NULL;
+ static GdkFont * fixedBoldFont = NULL;
+ static GdkFont * variableRegularFont = NULL;
+ static GdkFont * variableBoldFont = NULL;
+ GdkFont * regularFont = NULL;
+ GdkFont * boldFont = NULL;
+#endif
+ wBool_t doBold;
+ char * cp;
+ int len;
+
+ if (bt->text == 0) abort();
+#ifdef USE_TEXTVIEW
+ tb = gtk_text_view_get_buffer( GTK_TEXT_VIEW(bt->text) );
+ //if ((bt->option&BT_FIXEDFONT)) {
+ ///* creating PangoFontDescription from string, specified in entry */
+ //pfd = pango_font_description_from_string("Monospace");
+ ///* setting label's font */
+ //gtk_widget_modify_font(GTK_WIDGET(tb), pfd);
+ ///* freeing PangoFontDescription, cause it has been copied by prev. call */
+ //pango_font_description_free(pfd);
+ //}
+#else
+ if ((bt->option&BT_FIXEDFONT)) {
+ if (fixedRegularFont==NULL)
+ fixedRegularFont = gdk_font_load( "-*-courier-medium-r-*-*-12-*-*-*-*-*-iso8859-*" );
+ if (fixedBoldFont==NULL)
+ fixedBoldFont = gdk_font_load( "-*-courier-bold-r-*-*-12-*-*-*-*-*-iso8859-*" );
+ regularFont = fixedRegularFont;
+ boldFont = fixedBoldFont;
+ } else {
+ if (variableRegularFont==NULL)
+ variableRegularFont = gdk_font_load( "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-*" );
+ if (variableBoldFont==NULL)
+ variableBoldFont = gdk_font_load( "-*-helvetica-bold-r-*-*-12-*-*-*-*-*-iso8859-*" );
+ regularFont = variableRegularFont;
+ boldFont = variableBoldFont;
+ }
+#endif
+ /*gtk_text_freeze( GTK_TEXT (bt->text) );*/
+ doBold = FALSE;
+ text = gtkConvertInput( text );
+ while ( text && *text ) {
+ if ( (bt->option & BT_DOBOLD) != 0 &&
+ ( cp = strchr( text, doBold?'>':'<' ) ) != NULL ) {
+ len = cp-text;
+ cp++;
+ } else {
+ len = -1;
+ cp = NULL;
+ }
+ if ( len != 0 ) {
+#ifdef USE_TEXTVIEW
+ gtk_text_buffer_get_bounds( tb, &ti1, &ti2 );
+ if ( !doBold )
+ gtk_text_buffer_insert( tb, &ti2, text, len );
+ else
+ gtk_text_buffer_insert_with_tags_by_name( tb, &ti2, text, len, "bold", NULL );
+#else
+ gtk_text_insert( GTK_TEXT(bt->text), doBold?boldFont:regularFont, &bt->text->style->black, NULL, text, len );
+#endif
+ }
+ text = cp;
+ doBold = !doBold;
+ }
+ /*gtk_text_set_point( GTK_TEXT(bt->text), gtk_text_get_length( GTK_TEXT(bt->text) )-1 );*/
+ /*gtk_text_thaw( GTK_TEXT (bt->text) );*/
+ bt->changed = FALSE;
+}
+
+EXPORT void gtkTextFreeze(
+ wText_p bt )
+{
+#ifdef USE_TEXTVIEW
+ gtk_text_view_set_editable( GTK_TEXT_VIEW(bt->text), FALSE );
+#else
+ gtk_text_freeze( GTK_TEXT (bt->text) );
+#endif
+}
+
+EXPORT void gtkTextThaw(
+ wText_p bt )
+{
+#ifdef USE_TEXTVIEW
+ gtk_text_view_set_editable( GTK_TEXT_VIEW(bt->text), TRUE );
+#else
+ gtk_text_thaw( GTK_TEXT (bt->text) );
+#endif
+}
+
+EXPORT void wTextReadFile(
+ wText_p bt,
+ const char * fileName )
+{
+ FILE * f;
+ char buff[BUFSIZ+1];
+ if (fileName) {
+ f = fopen( fileName, "r" );
+ if (f == NULL) {
+ perror( fileName );
+ return;
+ }
+ while (fgets( buff, sizeof buff, f ) != NULL ) {
+ wTextAppend( bt, buff );
+ }
+ }
+}
+
+
+#ifdef USE_TEXTVIEW
+static char * gtkGetText(
+ wText_p bt )
+{
+ GtkTextBuffer * tb;
+ GtkTextIter ti1, ti2;
+ char * cp;
+ if (bt->text == 0) abort();
+ tb = gtk_text_view_get_buffer( GTK_TEXT_VIEW(bt->text) );
+ gtk_text_buffer_get_bounds( tb, &ti1, &ti2 );
+ cp = gtk_text_buffer_get_text( tb, &ti1, &ti2, FALSE );
+ cp = gtkConvertOutput( cp );
+ return cp;
+}
+#endif
+
+
+EXPORT wBool_t wTextSave(
+ wText_p bt,
+ const char * fileName )
+{
+#ifndef USE_TEXTVIEW
+ int siz, pos, cnt;
+#endif
+ FILE * f;
+ char * cp;
+
+ f = fopen( fileName, "w" );
+ if (f==NULL) {
+ wNoticeEx( NT_ERROR, fileName, "Ok", NULL );
+ return FALSE;
+ }
+#ifdef USE_TEXTVIEW
+ cp = gtkGetText( bt );
+ fwrite( cp, 1, strlen(cp), f );
+ free(cp);
+#else
+ siz = gtk_text_get_length( GTK_TEXT(bt->text) );
+ pos = 0;
+ cnt = BUFSIZ;
+ while (pos<siz) {
+ if (pos+cnt>siz)
+ cnt = siz-pos;
+ cp = gtk_editable_get_chars( GTK_EDITABLE(bt->text), pos, pos+cnt );
+ if (cp == NULL)
+ break;
+ fwrite( cp, 1, cnt, f );
+ free(cp);
+ pos += cnt;
+ }
+#endif
+ fclose(f);
+ return TRUE;
+}
+
+
+EXPORT wBool_t wTextPrint(
+ wText_p bt )
+{
+ wPrinterStream_p f;
+#ifndef USE_TEXTVIEW
+ int siz, pos, cnt;
+#endif
+ char * cp;
+
+ f = wPrinterOpen();
+ if (f==NULL) {
+ return FALSE;
+ }
+#ifdef USE_TEXTVIEW
+ cp = gtkGetText( bt );
+ wPrinterWrite( f, cp, strlen(cp) );
+ free(cp);
+
+#else
+ siz = gtk_text_get_length( GTK_TEXT(bt->text) );
+ pos = 0;
+ cnt = BUFSIZ;
+ while (pos<siz) {
+ if (pos+cnt>siz)
+ cnt = siz-pos;
+ cp = gtk_editable_get_chars( GTK_EDITABLE(bt->text), pos, pos+cnt );
+ if (cp == NULL)
+ break;
+ wPrinterWrite( f, cp, cnt );
+ free(cp);
+ pos += cnt;
+ }
+#endif
+ wPrinterClose(f);
+ return TRUE;
+}
+
+
+EXPORT int wTextGetSize(
+ wText_p bt )
+{
+#ifdef USE_TEXTVIEW
+ char * cp = gtkGetText( bt );
+ int len = strlen( cp );
+ free( cp );
+ return len;
+#else
+ return (int)gtk_text_get_length( GTK_TEXT(bt->text) );
+#endif
+}
+
+
+EXPORT void wTextGetText(
+ wText_p bt,
+ char * text,
+ int len )
+{
+ char * cp;
+#ifdef USE_TEXTVIEW
+ cp = gtkGetText(bt);
+ strncpy( text, cp, len );
+ free( cp );
+#else
+ cp = gtk_editable_get_chars( GTK_EDITABLE(bt->text), 0, len );
+ strncpy( text, cp, len );
+#endif
+}
+
+
+EXPORT void wTextSetReadonly (
+ wText_p bt,
+ wBool_t ro )
+{
+#ifdef USE_TEXTVIEW
+ gtk_text_view_set_editable( GTK_TEXT_VIEW(bt->text), !ro );
+#else
+ gtk_text_set_editable( GTK_TEXT(bt->text), !ro );
+#endif
+ if (ro) {
+ bt->option |= BO_READONLY;
+ } else {
+ bt->option &= ~BO_READONLY;
+ }
+}
+
+
+EXPORT wBool_t wTextGetModified(
+ wText_p bt )
+{
+ return bt->changed;
+}
+
+
+EXPORT void wTextSetSize(
+ wText_p bt,
+ wPos_t w,
+ wPos_t h )
+{
+#ifdef USE_TEXTVIEW
+ gtk_widget_set_size_request( bt->widget, w, h );
+// gtk_widget_set_size_request( bt->text, w-15, h );
+// gtk_widget_set_size_request( bt->vscroll, 15, h );
+#else
+ gtk_widget_set_usize( bt->widget, w, h );
+ gtk_widget_set_usize( bt->text, w-15, h );
+ gtk_widget_set_usize( bt->vscroll, 15, h );
+ gtk_widget_queue_resize( GTK_WIDGET(bt->widget) );
+ gtk_widget_queue_resize( GTK_WIDGET(bt->text) );
+ gtk_widget_queue_resize( GTK_WIDGET(bt->vscroll) );
+#endif
+ bt->w = w;
+ bt->h = h;
+}
+
+
+EXPORT void wTextComputeSize(
+ wText_p bt,
+ int rows,
+ int cols,
+ wPos_t *width,
+ wPos_t *height )
+{
+ *width = rows * 7;
+ *height = cols * 14;
+}
+
+
+EXPORT void wTextSetPosition(
+ wText_p bt,
+ int pos )
+{
+#ifdef USE_TEXTVIEW
+ /* TODO */
+#else
+ GTK_TEXT(bt->text)->first_line_start_index = pos;
+ gtk_text_set_word_wrap( GTK_TEXT(bt->text), TRUE );
+ gtk_text_set_point( GTK_TEXT(bt->text), pos );
+#endif
+}
+
+static void textChanged(
+ GtkWidget * widget,
+ wText_p bt )
+{
+ if (bt == 0)
+ return;
+ bt->changed = TRUE;
+}
+
+/**
+ * Create a multi line text entry widget. The created widget is
+ * configured as requested by the BT_* flags. This includes Monospaced
+ * font for BT_FIXEDFONT, readonly for BT_READONLY and a markup for
+ * bold when setup via BT_BOLD.
+ *
+ * \param parent IN parent window
+ * \param x,y IN position
+ * \param helpstr IN label for linking into help system
+ * \param labelStr IN label
+ * \param option IN widget options
+ * \param width, height IN size of widget
+ * \return handle for new widget
+ */
+
+EXPORT wText_p wTextCreate(
+ wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char * helpStr,
+ const char * labelStr,
+ long option,
+ wPos_t width,
+ wPos_t height )
+{
+ wText_p bt;
+#ifdef USE_TEXTVIEW
+ GtkTextBuffer * tb;
+ PangoFontDescription *pfd;
+#else
+ GtkRequisition requisition;
+#endif
+ bt = gtkAlloc( parent, B_MULTITEXT, x, y, labelStr, sizeof *bt, NULL );
+ gtkComputePos( (wControl_p)bt );
+ bt->width = width;
+ bt->height = height;
+ bt->option = option;
+ gtkComputePos( (wControl_p)bt );
+
+#ifdef USE_TEXTVIEW
+ bt->widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (bt->widget),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ bt->text = gtk_text_view_new();
+ if (bt->text == 0) abort();
+ gtk_container_add (GTK_CONTAINER (bt->widget), bt->text);
+ tb = gtk_text_view_get_buffer( GTK_TEXT_VIEW(bt->text) );
+ gtk_text_buffer_create_tag( tb, "bold", "weight", PANGO_WEIGHT_BOLD, NULL);
+/* gtk_text_buffer_create_tag( tb, "italic", "style", PANGO_STYLE_ITALIC, NULL); */
+/* gtk_text_buffer_create_tag( tb, "bolditalic", "weight", PANGO_WEIGHT_BOLD, "style", PANGO_STYLE_ITALIC, NULL); */
+ if ((bt->option & BT_FIXEDFONT)) {
+ /* creating PangoFontDescription from string, specified in entry */
+ pfd = pango_font_description_from_string("Monospace");
+ /* setting label's font */
+ gtk_widget_modify_font(GTK_WIDGET(bt->text), pfd);
+ /* freeing PangoFontDescription, cause it has been copied by prev. call */
+ pango_font_description_free(pfd);
+ }
+ bt->vscroll = gtk_vscrollbar_new( GTK_TEXT_VIEW(bt->text)->vadjustment );
+ if (bt->vscroll == 0) abort();
+#else
+ bt->widget = gtk_hbox_new( FALSE, 0 );
+ bt->text = gtk_text_new( NULL, NULL );
+ if (bt->text == 0) abort();
+ gtk_box_pack_start( GTK_BOX(bt->widget), bt->text, FALSE, FALSE, 0 );
+ bt->vscroll = gtk_vscrollbar_new( GTK_TEXT(bt->text)->vadj );
+ if (bt->vscroll == 0) abort();
+ gtk_box_pack_start( GTK_BOX(bt->widget), bt->vscroll, FALSE, FALSE, 0 );
+#endif
+ if (option&BT_CHARUNITS) {
+ width *= 7;
+ height *= 14;
+ }
+ gtk_widget_show( bt->text );
+ gtk_widget_show( bt->vscroll );
+ gtk_widget_show( bt->widget );
+#ifdef USE_TEXTVIEW
+// gtk_widget_set_size_request( GTK_WIDGET(bt->text), width, height );
+// gtk_widget_set_size_request( GTK_WIDGET(bt->vscroll), -1, height );
+ gtk_widget_set_size_request( GTK_WIDGET(bt->widget), width+15/*requisition.width*/, height );
+#else
+ gtk_widget_set_usize( GTK_WIDGET(bt->text), width, height );
+ gtk_widget_set_usize( GTK_WIDGET(bt->vscroll), -1, height );
+ gtk_widget_size_request( GTK_WIDGET(bt->vscroll), &requisition );
+ gtk_widget_set_usize( GTK_WIDGET(bt->widget), width+15/*requisition.width*/, height );
+#endif
+ if (bt->option&BO_READONLY) {
+#ifdef USE_TEXTVIEW
+ gtk_text_view_set_editable( GTK_TEXT_VIEW(bt->text), FALSE );
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(bt->text), FALSE);
+#else
+ gtk_text_set_editable( GTK_TEXT(bt->text), FALSE );
+#endif
+ }
+
+#ifdef USE_TEXTVIEW
+ gtk_fixed_put( GTK_FIXED(parent->widget), bt->widget, bt->realX, bt->realY );
+#else
+ gtk_container_add( GTK_CONTAINER(parent->widget), bt->widget );
+ gtk_widget_set_uposition( bt->widget, bt->realX, bt->realY );
+#endif
+ gtkControlGetSize( (wControl_p)bt );
+ if (labelStr)
+ bt->labelW = gtkAddLabel( (wControl_p)bt, labelStr );
+#ifdef USE_TEXTVIEW
+ gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(bt->text), GTK_WRAP_WORD );
+#else
+ gtk_text_set_word_wrap( GTK_TEXT(bt->text), TRUE );
+#endif
+ gtk_widget_realize( bt->text );
+ gtkAddButton( (wControl_p)bt );
+ gtkAddHelpString( bt->widget, helpStr );
+#ifdef USE_TEXTVIEW
+ g_signal_connect( G_OBJECT(tb), "changed", GTK_SIGNAL_FUNC(textChanged), bt );
+#else
+ gtk_signal_connect( GTK_OBJECT(bt->text), "changed", GTK_SIGNAL_FUNC(textChanged), bt );
+#endif
+ return bt;
+}
diff --git a/app/wlib/gtklib/gtkwindow.c b/app/wlib/gtklib/gtkwindow.c
new file mode 100644
index 0000000..b86b173
--- /dev/null
+++ b/app/wlib/gtklib/gtkwindow.c
@@ -0,0 +1,856 @@
+/** \file gtkwindow.c
+ * Basic window handling stuff.
+ *
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/gtkwindow.c,v 1.12 2010-04-28 04:04:38 dspagnol Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "gtkint.h"
+
+wWin_p gtkMainW;
+
+#define FOUR (4)
+#define MENUH (24)
+
+#define MIN_WIN_WIDTH (50)
+#define MIN_WIN_HEIGHT (50)
+
+#define SECTIONWINDOWSIZE "gtklib window size"
+#define SECTIONWINDOWPOS "gtklib window pos"
+
+extern wBool_t listHelpStrings;
+
+static wControl_p firstWin = NULL, lastWin;
+static int keyState;
+static wBool_t gtkBlockEnabled = TRUE;
+
+/*
+ *****************************************************************************
+ *
+ * Window Utilities
+ *
+ *****************************************************************************
+ */
+
+/**
+ * Get the window size from the resource (.rc) file. The size is saved under the key
+ * SECTIONWINDOWSIZE.window name
+ *
+ * \param win IN window
+ * \param nameStr IN window name
+ */
+
+static void getWinSize( wWin_p win, const char * nameStr )
+{
+ int w, h;
+ const char *cp;
+ char *cp1, *cp2;
+
+ if ( (win->option&F_RESIZE) &&
+ (win->option&F_RECALLPOS) &&
+ (cp = wPrefGetString( SECTIONWINDOWSIZE, nameStr)) &&
+ (w = strtod( cp, &cp1 ), cp != cp1) &&
+ (h = strtod( cp1, &cp2 ), cp1 != cp2) ) {
+ if (w < 10)
+ w = 10;
+ if (h < 10)
+ h = 10;
+ win->w = win->origX = w;
+ win->h = win->origY = h;
+ win->option &= ~F_AUTOSIZE;
+ }
+}
+
+/**
+ * Save the window size to the resource (.rc) file. The size is saved under the key
+ * SECTIONWINDOWSIZE.window name
+ *
+ * \param win IN window
+ */
+
+static void saveSize( wWin_p win )
+{
+ char pos_s[20];
+ if ( (win->option&F_RESIZE) &&
+ (win->option&F_RECALLPOS) &&
+ GTK_WIDGET_VISIBLE( GTK_WIDGET(win->gtkwin) ) ) {
+ sprintf( pos_s, "%d %d", win->w, win->h-(FOUR + ((win->option&F_MENUBAR)?MENUH:0) ) );
+ wPrefSetString( SECTIONWINDOWSIZE, win->nameStr, pos_s );
+ }
+}
+
+/**
+ * Get the window position from the resource (.rc) file. The position is saved under the key
+ * SECTIONWINDOWPOS.window name
+ *
+ * \param win IN window
+ */
+
+static void getPos( wWin_p win )
+{
+ int x, y;
+ const char *cp;
+ char *cp1, *cp2;
+ wPos_t gtkDisplayWidth = gdk_screen_width();
+ wPos_t gtkDisplayHeight = gdk_screen_height();
+
+ if ( (win->option&F_RECALLPOS) &&
+ (!win->shown) ) {
+ if ((cp = wPrefGetString( SECTIONWINDOWPOS, win->nameStr))) {
+ x = strtod( cp, &cp1 );
+ if (cp == cp1)
+ return;
+ y = strtod( cp1, &cp2 );
+ if (cp2 == cp1)
+ return;
+ if ( y > gtkDisplayHeight-win->h )
+ y = gtkDisplayHeight-win->h;
+ if ( x > gtkDisplayWidth-win->w )
+ x = gtkDisplayWidth-win->w;
+ if ( x <= 0 )
+ x = 1;
+ if ( y <= 0 )
+ y = 1;
+ gtk_window_move( GTK_WINDOW(win->gtkwin), x, y );
+ gtk_window_resize( GTK_WINDOW(win->gtkwin), win->w, win->h );
+ }
+ }
+}
+
+/**
+ * Save the window position to the resource (.rc) file. The position is saved under the key
+ * SECTIONWINDOWPOS.window name
+ *
+ * \param win IN window
+ */
+
+static void savePos( wWin_p win )
+{
+ int x, y;
+ char pos_s[20];
+ if ( (win->option&F_RECALLPOS) ) {
+ gdk_window_get_position( GTK_WIDGET(win->gtkwin)->window, &x, &y );
+ x -= 5;
+ y -= 25;
+ sprintf( pos_s, "%d %d", x, y );
+ wPrefSetString( SECTIONWINDOWPOS, win->nameStr, pos_s );
+ }
+}
+
+/**
+ * Returns the dimensions of <win>.
+ *
+ * \param win IN window handle
+ * \param width OUT width of window
+ * \param height OUT height of window minus menu bar size
+ */
+
+EXPORT void wWinGetSize(
+ wWin_p win, /* Window */
+ wPos_t * width, /* Returned window width */
+ wPos_t * height ) /* Returned window height */
+{
+ GtkRequisition requisition;
+ wPos_t w, h;
+ gtk_widget_size_request( win->gtkwin, &requisition );
+ w = win->w;
+ h = win->h;
+ if ( win->option&F_AUTOSIZE ) {
+ if ( win->realX > w )
+ w = win->realX;
+ if ( win->realY > h )
+ h = win->realY;
+ }
+ *width = w;
+ *height = h - FOUR - ((win->option&F_MENUBAR)?MENUH:0);
+}
+
+/**
+ * Sets the dimensions of <w> to <widht> and <height>.
+ *
+ * \param win IN window
+ * \param width IN new width
+ * \param height IN new height
+ */
+
+EXPORT void wWinSetSize(
+ wWin_p win, /* Window */
+ wPos_t width, /* Window width */
+ wPos_t height ) /* Window height */
+{
+ win->busy = TRUE;
+ win->w = width;
+ win->h = height + FOUR + ((win->option&F_MENUBAR)?MENUH:0);
+ gtk_widget_set_size_request( win->gtkwin, win->w, win->h );
+ gtk_widget_set_size_request( win->widget, win->w, win->h );
+ win->busy = FALSE;
+
+}
+
+/**
+ * Shows or hides window <win>. If <win> is created with 'F_BLOCK' option then the applications other
+ * windows are disabled and 'wWinShow' doesnot return until the window <win> is closed by calling
+ * 'wWinShow(<win>,FALSE)'.
+ *
+ * \param win IN window
+ * \param show IN visibility state
+ */
+
+EXPORT void wWinShow(
+ wWin_p win, /* Window */
+ wBool_t show ) /* Command */
+{
+ GtkRequisition requisition;
+ if (debugWindow >= 2) printf("Set Show %s\n", win->labelStr?win->labelStr:"No label" );
+ if (win->widget == 0) abort();
+ if (show) {
+ keyState = 0;
+ getPos( win );
+ if ( win->option & F_AUTOSIZE ) {
+ gtk_widget_size_request( win->gtkwin, &requisition );
+ if ( requisition.width != win->w || requisition.height != win->h ) {
+ gtk_widget_set_size_request( win->gtkwin, win->w, win->h );
+ gtk_widget_set_size_request( win->widget, win->w, win->h );
+ if (win->option&F_MENUBAR) {
+ gtk_widget_set_size_request( win->menubar, win->w, MENUH );
+ }
+ }
+ }
+ if ( !win->shown ) {
+ gtk_widget_show( win->gtkwin );
+ gtk_widget_show( win->widget );
+ }
+ gdk_window_raise( win->gtkwin->window );
+ if ( win->shown && win->modalLevel > 0 )
+ gtk_widget_set_sensitive( GTK_WIDGET(win->gtkwin), TRUE );
+ win->shown = show;
+ win->modalLevel = 0;
+
+ if ( (!gtkBlockEnabled) || (win->option & F_BLOCK) == 0) {
+ wFlush();
+ } else {
+ gtkDoModal( win, TRUE );
+ }
+ } else {
+ wFlush();
+ saveSize( win );
+ savePos( win );
+ win->shown = show;
+ if ( gtkBlockEnabled && (win->option & F_BLOCK) != 0) {
+ gtkDoModal( win, FALSE );
+ }
+ gtk_widget_hide( win->gtkwin );
+ gtk_widget_hide( win->widget );
+ }
+}
+
+/**
+ * Block windows against user interactions. Done during demo mode etc.
+ *
+ * \param enabled IN blocked if TRUE
+ */
+
+EXPORT void wWinBlockEnable(
+ wBool_t enabled )
+{
+ gtkBlockEnabled = enabled;
+}
+
+/**
+ * Returns whether the window is visible.
+ *
+ * \param win IN window
+ * \return TRUE if visible, FALSE otherwise
+ */
+
+EXPORT wBool_t wWinIsVisible(
+ wWin_p win )
+{
+ return win->shown;
+}
+
+/**
+ * Sets the title of <win> to <title>.
+ *
+ * \param varname1 IN window
+ * \param varname2 IN new title
+ */
+
+EXPORT void wWinSetTitle(
+ wWin_p win, /* Window */
+ const char * title ) /* New title */
+{
+ gtk_window_set_title( GTK_WINDOW(win->gtkwin), title );
+}
+
+/**
+ * Sets the window <win> to busy or not busy. Sets the cursor accordingly
+ *
+ * \param varname1 IN window
+ * \param varname2 IN TRUE if busy, FALSE otherwise
+ */
+
+EXPORT void wWinSetBusy(
+ wWin_p win, /* Window */
+ wBool_t busy ) /* Command */
+{
+ GdkCursor * cursor;
+ if (win->gtkwin == 0) abort();
+ if ( busy )
+ cursor = gdk_cursor_new ( GDK_WATCH );
+ else
+ cursor = NULL;
+ gdk_window_set_cursor (win->gtkwin->window, cursor);
+ if ( cursor )
+ gdk_cursor_destroy (cursor);
+ gtk_widget_set_sensitive( GTK_WIDGET(win->gtkwin), busy==0 );
+}
+
+
+EXPORT void gtkDoModal(
+ wWin_p win0,
+ wBool_t modal )
+{
+ wWin_p win;
+ for ( win=(wWin_p)firstWin; win; win=(wWin_p)win->next ) {
+ if ( win->shown && win != win0 ) {
+ if ( modal ) {
+ if ( win->modalLevel == 0 )
+ gtk_widget_set_sensitive( GTK_WIDGET(win->gtkwin), FALSE );
+ win->modalLevel++;
+ } else {
+ if ( win->modalLevel > 0 ) {
+ win->modalLevel--;
+ if ( win->modalLevel == 0 )
+ gtk_widget_set_sensitive( GTK_WIDGET(win->gtkwin), TRUE );
+ }
+ }
+ if ( win->modalLevel < 0 ) {
+ fprintf( stderr, "DoModal: %s modalLevel < 0", win->nameStr?win->nameStr:"<NULL>" );
+ abort();
+ }
+ }
+ }
+ if ( modal ) {
+ gtk_main();
+ } else {
+ gtk_main_quit();
+ }
+}
+
+/**
+ * Returns the Title of <win>.
+ *
+ * \param win IN window
+ * \return pointer to window title
+ */
+
+EXPORT const char * wWinGetTitle(
+ wWin_p win ) /* Window */
+{
+ return win->labelStr;
+}
+
+
+EXPORT void wWinClear(
+ wWin_p win,
+ wPos_t x,
+ wPos_t y,
+ wPos_t width,
+ wPos_t height )
+{
+}
+
+
+EXPORT void wWinDoCancel(
+ wWin_p win )
+{
+ wControl_p b;
+ for (b=win->first; b; b=b->next) {
+ if ((b->type == B_BUTTON) && (b->option & BB_CANCEL) ) {
+ gtkButtonDoAction( (wButton_p)b );
+ }
+ }
+}
+
+/*
+ ******************************************************************************
+ *
+ * Call Backs
+ *
+ ******************************************************************************
+ */
+
+static gint window_delete_event(
+ GtkWidget *widget,
+ GdkEvent *event,
+ wWin_p win )
+{
+ wControl_p b;
+ /* if you return FALSE in the "delete_event" signal handler,
+ * GTK will emit the "destroy" signal. Returning TRUE means
+ * you don't want the window to be destroyed.
+ * This is useful for popping up 'are you sure you want to quit ?'
+ * type dialogs. */
+
+ /* Change TRUE to FALSE and the main window will be destroyed with
+ * a "delete_event". */
+
+ for ( b = win->first; b; b=b->next )
+ if (b->doneProc)
+ b->doneProc( b );
+ if (win->winProc) {
+ win->winProc( win, wClose_e, win->data );
+ if (win != gtkMainW)
+ wWinShow( win, FALSE );
+ }
+ return (TRUE);
+}
+
+static int window_redraw(
+ wWin_p win,
+ wBool_t doWinProc )
+{
+ wControl_p b;
+
+ if (win==NULL)
+ return FALSE;
+
+ for (b=win->first; b != NULL; b = b->next) {
+ if (b->repaintProc)
+ b->repaintProc( b );
+ }
+
+ return FALSE;
+}
+
+static int fixed_expose_event(
+ GtkWidget * widget,
+ GdkEventExpose * event,
+ wWin_p win )
+{
+ if (event->count==0)
+ return window_redraw( win, TRUE );
+ else
+ return FALSE;
+}
+
+static int window_configure_event(
+ GtkWidget * widget,
+ GdkEventConfigure * event,
+ wWin_p win )
+{
+ wPos_t h;
+
+ if (win==NULL)
+ return FALSE;
+
+ h = event->height - FOUR;
+ if (win->option&F_MENUBAR)
+ h -= MENUH;
+ if (win->option&F_RESIZE) {
+ if ( event->width < 10 || event->height < 10 )
+ return TRUE;
+ if (win->w != event->width || win->h != event->height) {
+ win->w = event->width;
+ win->h = event->height;
+ if ( win->w < MIN_WIN_WIDTH )
+ win->w = MIN_WIN_WIDTH;
+ if ( win->h < MIN_WIN_HEIGHT )
+ win->h = MIN_WIN_HEIGHT;
+ if (win->option&F_MENUBAR)
+ gtk_widget_set_size_request( win->menubar, win->w, MENUH );
+
+ if (win->busy==FALSE && win->winProc) {
+ win->winProc( win, wResize_e, win->data );
+ }
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Get current state of shift, ctrl or alt keys.
+ *
+ * \return or'ed value of WKEY_SHIFT, WKEY_CTRL and WKEY_ALT depending on state
+ */
+
+int wGetKeyState( void )
+{
+ return keyState;
+}
+
+wBool_t catch_shift_ctrl_alt_keys(
+ GtkWidget * widget,
+ GdkEventKey *event,
+ void * data )
+{
+ int state;
+
+ state = 0;
+ switch (event->keyval) {
+ case GDK_Shift_L:
+ case GDK_Shift_R:
+ state |= WKEY_SHIFT;
+ break;
+ case GDK_Control_L:
+ case GDK_Control_R:
+ state |= WKEY_CTRL;
+ break;
+ case GDK_Alt_L:
+ case GDK_Alt_R:
+ state |= WKEY_ALT;
+ break;
+ }
+ if ( state != 0 ) {
+ if (event->type == GDK_KEY_PRESS)
+ keyState |= state;
+ else
+ keyState &= ~state;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gint window_char_event(
+ GtkWidget * widget,
+ GdkEventKey *event,
+ wWin_p win )
+{
+ wControl_p bb;
+ if ( catch_shift_ctrl_alt_keys( widget, event, win ) )
+ return FALSE;
+ if (event->type == GDK_KEY_RELEASE)
+ return FALSE;
+
+ if ( event->state == 0 ) {
+ if ( event->keyval == GDK_Escape ) {
+ for ( bb=win->first; bb; bb=bb->next ) {
+ if ( bb->type == B_BUTTON && (bb->option&BB_CANCEL) ) {
+ gtkButtonDoAction( (wButton_p)bb );
+ return TRUE;
+ }
+ }
+ }
+ }
+ if ( gtkHandleAccelKey( event ) ) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+
+/*
+ *******************************************************************************
+ *
+ * Main and Popup Windows
+ *
+ *******************************************************************************
+ */
+
+
+
+static wWin_p wWinCommonCreate(
+ wWin_p parent,
+ int winType,
+ wPos_t x,
+ wPos_t y,
+ const char * labelStr,
+ const char * nameStr,
+ long option,
+ wWinCallBack_p winProc,
+ void * data )
+{
+ wWin_p w;
+ int h;
+
+ w = gtkAlloc( NULL, winType, x, y, labelStr, sizeof *w, data );
+ w->busy = TRUE;
+ w->option = option;
+ getWinSize( w, nameStr );
+
+ h = FOUR;
+ if (w->option&F_MENUBAR)
+ h += MENUH;
+
+ if ( winType == W_MAIN ) {
+ w->gtkwin = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ } else {
+ w->gtkwin = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ if ( gtkMainW )
+ gtk_window_set_transient_for( GTK_WINDOW(w->gtkwin), GTK_WINDOW(gtkMainW->gtkwin) );
+ }
+
+ if( option & F_HIDE )
+ gtk_widget_hide( w->gtkwin );
+
+ /* center window on top of parent window */
+ if( option & F_CENTER )
+ gtk_window_set_position(GTK_WINDOW(w->gtkwin), GTK_WIN_POS_CENTER_ON_PARENT );
+
+ w->widget = gtk_fixed_new();
+ if (w->widget == 0) abort();
+
+ if (w->option&F_MENUBAR) {
+ w->menubar = gtk_menu_bar_new();
+ gtk_container_add( GTK_CONTAINER(w->widget), w->menubar );
+ gtk_widget_show( w->menubar );
+ gtk_widget_set_size_request( w->menubar, -1, MENUH );
+ }
+
+ gtk_container_add( GTK_CONTAINER(w->gtkwin), w->widget );
+
+ if (w->option&F_AUTOSIZE) {
+ w->realX = 0;
+ w->w = 0;
+ w->realY = h;
+ w->h = 0;
+ } else {
+ w->w = w->realX = w->origX;
+ w->h = w->realY = w->origY+h;
+ gtk_widget_set_size_request( w->gtkwin, w->w, w->h );
+ gtk_widget_set_size_request( w->widget, w->w, w->h );
+ if (w->option&F_MENUBAR) {
+ gtk_widget_set_size_request( w->menubar, w->w, MENUH );
+ }
+ }
+
+ w->first = w->last = NULL;
+
+ w->winProc = winProc;
+
+ gtk_signal_connect (GTK_OBJECT (w->gtkwin), "delete_event",
+ GTK_SIGNAL_FUNC (window_delete_event), w);
+ gtk_signal_connect (GTK_OBJECT (w->widget), "expose_event",
+ GTK_SIGNAL_FUNC (fixed_expose_event), w);
+ gtk_signal_connect (GTK_OBJECT (w->gtkwin), "configure_event",
+ GTK_SIGNAL_FUNC (window_configure_event), w);
+ gtk_signal_connect (GTK_OBJECT (w->gtkwin), "key_press_event",
+ GTK_SIGNAL_FUNC (window_char_event), w);
+ gtk_signal_connect (GTK_OBJECT (w->gtkwin), "key_release_event",
+ GTK_SIGNAL_FUNC (window_char_event), w);
+ gtk_widget_set_events (w->widget, GDK_EXPOSURE_MASK );
+ gtk_widget_set_events ( GTK_WIDGET(w->gtkwin), GDK_EXPOSURE_MASK|GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK );
+
+ /**
+ * \todo { set_policy is deprecated and should be replaced by set_resizable. In order to do that
+ * the library has to be cleared up from calls to set_size_request as these set the minimum widget size
+ * to the current size preventing the user from re-sizing the window to a smaller size. At least this
+ * is my current assumption ;-) }
+ */
+ if (w->option & F_RESIZE) {
+ gtk_window_set_policy( GTK_WINDOW(w->gtkwin), TRUE, TRUE, TRUE );
+// gtk_window_set_resizable( GTK_WINDOW(w->gtkwin), TRUE );
+ } else {
+// gtk_window_set_resizable( GTK_WINDOW(w->gtkwin), FALSE );
+ gtk_window_set_policy( GTK_WINDOW(w->gtkwin), FALSE, FALSE, TRUE );
+ }
+
+ w->lastX = 0;
+ w->lastY = h;
+ w->shown = FALSE;
+ w->nameStr = nameStr?strdup( nameStr ):NULL;
+ if (labelStr)
+ gtk_window_set_title( GTK_WINDOW(w->gtkwin), labelStr );
+ if (listHelpStrings)
+ printf( "WINDOW - %s\n", nameStr?nameStr:"<NULL>" );
+
+ if (firstWin) {
+ lastWin->next = (wControl_p)w;
+ } else {
+ firstWin = (wControl_p)w;
+ }
+ lastWin = (wControl_p)w;
+ gtk_widget_show( w->widget );
+ gtk_widget_realize( w->gtkwin );
+
+ w->busy = FALSE;
+ return w;
+}
+
+/**
+ * \todo { investigate and implement this function for setting the correct icon as necessary.
+ * It looks like these functions are never called at the moment. }
+ */
+
+EXPORT void wWinSetBigIcon(
+ wWin_p win, /* Main window */
+ wIcon_p ip ) /* The icon */
+/*
+Create an Icon from a X-bitmap.
+*/
+{
+#ifdef LATER
+ GdkPixmap * pixmap;
+ GdkBitmap * mask;
+ pixmap = gtkMakeIcon( win->gtkwin, ip, &mask );
+ gdk_window_set_icon( win->gtkwin->window, NULL, pixmap, mask );
+ gdk_pixmap_unref( pixmap );
+ gdk_bitmap_unref( mask );
+#endif
+}
+
+
+/**
+ * \todo { investigate and implement this function for setting the correct icon as necessary.
+ * It looks like these functions are never called at the moment. }
+ */
+
+
+EXPORT void wWinSetSmallIcon(
+ wWin_p win, /* Main window */
+ wIcon_p ip ) /* The icon */
+/*
+Create an Icon from a X-bitmap.
+*/
+{
+ GdkBitmap * mask;
+ if ( ip->gtkIconType == gtkIcon_bitmap ) {
+ mask = gdk_bitmap_create_from_data( win->gtkwin->window, ip->bits, ip->w, ip->h );
+ gdk_window_set_icon( win->gtkwin->window, NULL, mask, mask );
+ /*gdk_bitmap_unref( mask );*/
+ }
+}
+
+/**
+ * Initialize the application's main window. This function does the necessary initialization
+ * of the application including creation of the main window.
+ *
+ * \param name IN internal name of the application. Used for filenames etc.
+ * \param x IN Initial window width
+ * \param y IN Initial window height
+ * \param helpStr IN Help topic string
+ * \param labelStr IN window title
+ * \param nameStr IN Window name
+ * \param option IN options for window creation
+ * \param winProc IN pointer to main window procedure
+ * \param data IN User context
+ * \return window handle or NULL on error
+ */
+
+EXPORT wWin_p wWinMainCreate(
+ const char * name, /* Application name */
+ wPos_t x, /* Initial window width */
+ wPos_t y, /* Initial window height */
+ const char * helpStr, /* Help topic string */
+ const char * labelStr, /* Window title */
+ const char * nameStr, /* Window name */
+ long option, /* Options */
+ wWinCallBack_p winProc, /* Call back function */
+ void * data ) /* User context */
+{
+ char *pos;
+
+ if( pos = strchr( name, ';' )) {
+ /* if found, split application name and configuration name */
+ strcpy( wConfigName, pos + 1 );
+ } else {
+ /* if not found, application name and configuration name are same */
+ strcpy( wConfigName, name );
+ }
+
+ gtkMainW = wWinCommonCreate( NULL, W_MAIN, x, y, labelStr, nameStr, option, winProc, data );
+
+ wDrawColorWhite = wDrawFindColor( 0xFFFFFF );
+ wDrawColorBlack = wDrawFindColor( 0x000000 );
+
+ gdk_window_set_group( gtkMainW->gtkwin->window, gtkMainW->gtkwin->window );
+ return gtkMainW;
+}
+
+/**
+ * Create a new popup window.
+ *
+ * \param parent IN Parent window (may be NULL)
+ * \param x IN Initial window width
+ * \param y IN Initial window height
+ * \param helpStr IN Help topic string
+ * \param labelStr IN Window title
+ * \param nameStr IN Window name
+ * \param option IN Options
+ * \param winProc IN call back function
+ * \param data IN User context information
+ * \return handle for new window
+ */
+
+EXPORT wWin_p wWinPopupCreate(
+ wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char * helpStr,
+ const char * labelStr,
+ const char * nameStr,
+ long option,
+ wWinCallBack_p winProc,
+ void * data )
+{
+ wWin_p win;
+
+ if (parent == NULL) {
+ if (gtkMainW == NULL)
+ abort();
+ parent = gtkMainW;
+ }
+ win = wWinCommonCreate( parent, W_POPUP, x, y, labelStr, nameStr, option, winProc, data );
+ gdk_window_set_group( win->gtkwin->window, gtkMainW->gtkwin->window );
+
+ return win;
+}
+
+
+/**
+ * Terminates the applicaton with code <rc>. Before closing the main window
+ * call back is called with wQuit_e. The program is terminated without exiting
+ * the main message loop.
+ *
+ * \param rc IN exit code
+ * \return never returns
+ */
+
+
+EXPORT void wExit(
+ int rc ) /* Application return code */
+{
+ wWin_p win;
+ for ( win = (wWin_p)firstWin; win; win = (wWin_p)win->next ) {
+ if ( GTK_WIDGET_VISIBLE( GTK_WIDGET(win->gtkwin) ) ) {
+ saveSize( win );
+ savePos( win );
+ }
+ }
+ wPrefFlush();
+ if (gtkMainW && gtkMainW->winProc != NULL)
+ gtkMainW->winProc( gtkMainW, wQuit_e, gtkMainW->data );
+
+ exit( rc );
+}
diff --git a/app/wlib/gtklib/gtkxpm.c b/app/wlib/gtklib/gtkxpm.c
new file mode 100644
index 0000000..022c973
--- /dev/null
+++ b/app/wlib/gtklib/gtkxpm.c
@@ -0,0 +1,177 @@
+/** \file gtkxpm.c
+ * XPM creation functions
+ */
+
+/* XTrackCad - Model Railroad CAD
+ * Copyright (C) 2015 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.
+ */
+
+ #include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include "gtkint.h"
+
+ #include "uthash.h"
+
+struct xpmColTable {
+ int color; /* color value (rgb) */
+ char name[ 5 ]; /* corresponding character representation */
+ UT_hash_handle hh; /* makes this structure hashable */
+};
+
+static struct xpmColTable *colTable = NULL;
+
+// must be 64 chars long
+static char colVal[] = ".*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+
+struct wDraw_t {
+ WOBJ_COMMON
+ void * context;
+ wDrawActionCallBack_p action;
+ wDrawRedrawCallBack_p redraw;
+
+ GdkPixmap * pixmap;
+ GdkPixmap * pixmapBackup;
+
+ double dpi;
+
+ GdkGC * gc;
+ wDrawWidth lineWidth;
+ wDrawOpts opts;
+ wPos_t maxW;
+ wPos_t maxH;
+ unsigned long lastColor;
+ wBool_t lastColorInverted;
+ const char * helpStr;
+
+ wPos_t lastX;
+ wPos_t lastY;
+
+ wBool_t delayUpdate;
+ };
+
+ /**
+ * Export as XPM bitmap file. During creation of the color table, a 4 byte color
+ * encoding is assumed and a table created accordingly. Once the whole picture has been scanned
+ * the correct number ist known. When writing to disk only the needed number of bytes per entry
+ * is written.
+ * This routine was heavily inspired by on implementation for TK written by Jan Nijtmans.
+ *
+ * \param d IN the drawing area ?
+ * \param fileName IN fully qualified filename for the bitmap file.
+ * \return TRUE on success, FALSE on error
+ */
+
+wBool_t wBitMapWriteFile( wDraw_p d, const char * fileName )
+{
+ GdkImage * image;
+ gint x, y;
+ guint32 pixel;
+ FILE * f;
+ int cc = 0;
+ struct xpmColTable *ct, *tmp;
+ int numChars;
+
+ image = gdk_image_get( (GdkWindow*)d->pixmap, 0, 0, d->w, d->h );
+ if (!image) {
+ wNoticeEx( NT_ERROR, "WriteBitMap: image_get failed", "Ok", NULL );
+ return FALSE;
+ }
+
+ f = fopen( fileName, "w" );
+ if (!f) {
+ perror( fileName );
+ return FALSE;
+ }
+ fprintf( f, "/* XPM */\n" );
+ fprintf( f, "static char * xtrkcad_bitmap[] = {\n" );
+ fprintf( f, "/* width height num_colors chars_per_pixel */\n" );
+
+ // count colors used and create the color table in the same pass
+ for( y = 0; y < d->h;y ++ ) {
+ for (x = 0; x < d->w; x++ ) {
+
+ pixel = gdk_image_get_pixel( image, x, y );
+ //check whether color is new
+
+ HASH_FIND(hh, colTable, &pixel, sizeof( guint32 ), ct);
+ if( !ct ) {
+ // not found previously, so add a new color table entry
+ int i;
+ int c;
+
+ ct = malloc( sizeof( struct xpmColTable ) );
+ ct->name[ 4 ] = '\0';
+ for( i = 3, c = cc; i >= 0; i--, c>>=6 ) {
+ (ct->name)[ i ] = colVal[ c & 0x3F ];
+ }
+ ct->color = pixel;
+
+ HASH_ADD(hh, colTable, color, sizeof( guint32 ), ct);
+ cc++;
+ }
+ }
+ }
+
+ // calculate how many characters are needed for the color table
+ numChars = 1;
+ if( cc > 0x3ffff ) {
+ numChars = 4;
+ } else {
+ if( cc > 0xfff ) {
+ numChars = 3;
+ } else {
+ if( cc > 0x3f ) {
+ numChars = 2;
+ }
+ }
+ }
+ // print color table
+ fprintf( f, "\"%d %d %d %d\"\n", d->w, d->h, cc, numChars );
+ fprintf( f, "/* colors */\n" );
+ for( ct = colTable; ct != NULL; ct = ct->hh.next )
+ fprintf( f, "\"%s c #%6.6x\",\n", (ct->name) + (4 - numChars ), ct->color );
+
+ // print the pixels
+ fprintf( f, "/* pixels */\n" );
+ for ( y=0; y<d->h; y++ ) {
+ fprintf( f, "\"" );
+ for ( x=0; x<d->w; x++ ) {
+ pixel = gdk_image_get_pixel( image, x, y );
+ HASH_FIND( hh, colTable, &pixel, sizeof(guint32), ct );
+ fputs( (ct->name) + (4 - numChars ), f );
+ }
+ fprintf( f, "\"%s\n", (y<d->h-1)?",":"" );
+ }
+
+ // delete the hash and free the content
+ HASH_ITER(hh, colTable, ct, tmp) {
+ HASH_DEL(colTable,ct);
+ free(ct);
+ }
+
+ gdk_image_destroy( image );
+ fprintf( f, "};\n" );
+ fclose( f );
+ return TRUE;
+}
diff --git a/app/wlib/gtklib/psprint.c b/app/wlib/gtklib/psprint.c
new file mode 100644
index 0000000..8e7cbe6
--- /dev/null
+++ b/app/wlib/gtklib/psprint.c
@@ -0,0 +1,1599 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/psprint.c,v 1.5 2009-05-15 18:54:20 m_fischer Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <pwd.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <math.h>
+#include <locale.h>
+
+#include <stdint.h>
+
+#include <gtk/gtk.h>
+
+#include "gtkint.h"
+#include "wlib.h"
+/* #include "dynarr.h" */
+#include "i18n.h"
+
+#ifndef TRUE
+#define TRUE (1)
+#define FALSE (0)
+#endif
+
+#define MM(m) ((m)/25.4)
+
+/* char * gtkFontTranslate( wFont_p ); */
+extern wDrawColor wDrawColorWhite;
+extern wDrawColor wDrawColorBlack;
+
+/*****************************************************************************
+ *
+ * MACROS
+ *
+ */
+
+#define PRINT_COMMAND (0)
+#define PRINT_FILE (1)
+
+#define PRINT_PORTRAIT (0)
+#define PRINT_LANDSCAPE (1)
+
+/* #define MAXIMUM(a,b) ((a)>(b) ? (a) : (b)) */
+#define min(a,b) ((a)<(b) ? (a) : (b))
+#define PPI (72.0)
+#define P2I( P ) ((P)/PPI)
+
+#define DPI (1440.0)
+#define D2I( D ) (((double)(D))/DPI)
+
+#define CENTERMARK_LENGTH 60
+
+#define WFONT "WFONT"
+#define WPRINTER "WPRINTER"
+#define WMARGIN "WMARGIN"
+#define WMARGINMAP "WMARGINMAP"
+#define WPRINTFONT "WPRINTFONT"
+
+/*****************************************************************************
+ *
+ * VARIABLES
+ *
+ */
+
+extern struct wDraw_t psPrint_d;
+
+/*
+typedef struct {
+ wIndex_t cmdOrFile;
+ FILE * f;
+ } wPrinterStream_t;
+typedef wPrinterStream_t * wPrinterStream_p;
+*/
+static wBool_t printContinue;
+static wWin_p printAbortW;
+static wMessage_p printAbortT;
+static wMessage_p printAbortM;
+
+static wWin_p printFileW;
+static wWin_p newFontAliasW;
+static wWin_p printSetupW;
+static wList_p optPrinterB;
+static wList_p optPaperSizeB;
+static wMessage_p newFontAliasXFntB;
+static wList_p optMarginB;
+static wButton_p optMarginDelB;
+static wFloat_p optTopMargin;
+static wFloat_p optBottomMargin;
+static wFloat_p optRightMargin;
+static wFloat_p optLeftMargin;
+static wChoice_p optFormat;
+static wList_p optXFontL;
+static wString_p optPSFontS;
+static wFloat_p optFontSizeFactor;
+static long optXFontX;
+static const char * optXFont;
+static char optPSFont[200];
+
+#ifdef LATER
+static char addPrinterName[80];
+static char addPrinterCommand[80];
+static wWin_p addPrinterW;
+static wString_p addPrinterN;
+static wString_p addPrinterC;
+static char addMarginName[80];
+static wWin_p addMarginW;
+static wString_p addMarginN;
+#endif
+
+static FILE * psFile;
+static wPrinterStream_p psFileStream;
+static wIndex_t pageCount;
+static wIndex_t totalPageCount;
+
+static long newPPrinter;
+static long newPPaper;
+static wPrintSetupCallBack_p printSetupCallBack;
+
+static double tBorder;
+static double rBorder;
+static double lBorder;
+static double bBorder;
+
+static long printFormat = PRINT_LANDSCAPE;
+static double currLineWidth = 0;
+
+static long curPrinter = 0;
+static char *sPrintFileName;
+static long curMargin = 0;
+
+static const char * prefName;
+static const char * prefPaper;
+static const char * prefMargin;
+static const char * prefFormat;
+
+static char newMarginName[256];
+
+typedef enum { PS_LT_SOLID, PS_LT_DASH } PS_LT_E;
+static PS_LT_E currentLT = PS_LT_SOLID;
+
+static double fontSizeFactor = 1.0;
+
+static struct {
+ const char * name;
+ double w, h;
+ } papers[] = {
+ { "Letter", 8.5, 11.0 },
+ { "Legal", 8.5, 14.0 },
+ { "Tabloid", 11.0, 17.0 },
+ { "Ledger", 17.0, 11.0 },
+ { "Fan Fold", 13.2, 11.0 },
+ { "Statement", 5.5, 8.5 },
+ { "Executive", 7.5, 10.0 },
+ { "Folio", 8.27, 13 },
+ { "A0", MM(841), MM(1189) },
+ { "A1", MM(594), MM(841) },
+ { "A2", MM(420), MM(594) },
+ { "A3", MM(297), MM(420) },
+ { "A4", MM(210), MM(297) },
+ { "A5", MM(148), MM(210) },
+ { "A6", MM(105), MM(148) },
+ { "A7", MM(74), MM(105) },
+ { "A8", MM(52), MM(74) },
+ { "A9", MM(37), MM(52) },
+ { "A10", MM(26), MM(37) },
+ { "B0", MM(1000), MM(1414) },
+ { "B1", MM(707), MM(1000) },
+ { "B2", MM(500), MM(707) },
+ { "B3", MM(353), MM(500) },
+ { "B4", MM(250), MM(353) },
+ { "B5", MM(176), MM(250) },
+ { "B6", MM(125), MM(176) },
+ { "B7", MM(88), MM(125) },
+ { "B8", MM(62), MM(88) },
+ { "B9", MM(44), MM(62) },
+ { "B10", MM(31), MM(44) },
+ { "C0", MM(917), MM(1297) },
+ { "C1", MM(648), MM(917) },
+ { "C2", MM(458), MM(648) },
+ { "C3", MM(324), MM(458) },
+ { "C4", MM(229), MM(324) },
+ { "C5", MM(162), MM(229) },
+ { "C6", MM(114), MM(162) },
+ { "C7", MM(81), MM(114) },
+ { "DL", MM(110), MM(220) },
+ { NULL } };
+wIndex_t curPaper = 0;
+
+typedef struct {
+ const char * name;
+ const char * cmd;
+ wIndex_t class;
+ } printers_t;
+dynArr_t printers_da;
+#define printers(N) DYNARR_N(printers_t,printers_da,N)
+
+typedef struct {
+ const char * name;
+ double t, b, r, l;
+ } margins_t;
+dynArr_t margins_da;
+#define margins(N) DYNARR_N(margins_t,margins_da,N)
+
+static void printFileNameSel( void * junk );
+static void printInit( void );
+
+/*
+ * Stuff related to determining the list of fonts used in the
+ * Postscript file. A simple linked-list is used to implement a
+ * stack. Everything is specialized to this application.
+ */
+
+/**
+ * Nodes of the \a fontsUsed list.
+ */
+struct list_node {
+ struct list_node *next;
+ char *data;
+} ;
+
+/**
+ * Pointer to the \a fontsUsed list.
+ */
+static struct list_node *fontsUsed = NULL;
+
+
+/**
+ * Pushes its argument on to the \a fontsUsed list.
+ * \param item - IN pointer to a string to put on the list
+ * \return nothing
+ */
+void fontsUsedPush( const char *item) {
+ struct list_node *newitem;
+ newitem = malloc(sizeof(struct list_node));
+ if (newitem == NULL) exit (2);
+ newitem->next=fontsUsed;
+ newitem->data = strdup(item);
+ if (newitem->data == NULL) exit(3);
+ fontsUsed=newitem;
+}
+
+/**
+ * Pops the top node from the \a fontsUsed list.
+ * Note that a pointer to the complete node is returned. The
+ * caller is responsible for freeing both the data and the list
+ * node when it is finished using them.
+ * \return pointer to the list node.
+ */
+struct list_node * fontsUsedPop() {
+ struct list_node *item;
+ if (fontsUsed == NULL) return NULL;
+ item = fontsUsed;
+ fontsUsed = item->next;
+ return item ;
+}
+
+/**
+ * \a fontsUsed list (re-)initializer.
+ */
+void fontsUsedInit() {
+ struct list_node *p;
+ while ((p=fontsUsedPop()) != NULL) {
+ free(p->data);
+ free(p);
+ }
+ fontsUsed=NULL;
+}
+
+/**
+ * Checks if \a s is already in \a fontsUsed list.
+ * \param s - IN string to be checked.
+ * \return TRUE if found, FALSE if not.
+ */
+int fontsUsedContains( const char *s ) {
+ struct list_node *ptr;
+ ptr = fontsUsed;
+ while ( ptr != NULL ) {
+ if ( strcmp(s, ptr->data) == 0 ) return TRUE;
+ ptr= ptr->next;
+ }
+ return FALSE ;
+}
+
+/**
+ * Adds the \a fontName to the list of fonts being used.
+ * Only if it is not already in the list.
+ *
+ * This function should be called anywhere the string "findfont"
+ * is being emitted to the Postscript file.
+ * \param \a fontName IN - string contaning the name to add.
+ */
+void addFontName( const char * fontName){
+ if (fontsUsedContains(fontName)) return;
+ fontsUsedPush(fontName);
+}
+
+/* ***************************************** */
+
+/**
+ * This function does a normal printf but uses the default C
+ * locale as decimal separator.
+ *
+ * \param template IN printf-like format string
+ * ... IN parameters according to format string
+ * \return describe the return value
+ */
+
+static void
+psPrintf (FILE *ps, const char *template, ...)
+{
+ va_list ap;
+
+ setlocale( LC_NUMERIC, "C" );
+
+ va_start( ap, template );
+ vfprintf( ps, template, ap );
+ va_end( ap );
+
+ setlocale( LC_NUMERIC, "" );
+}
+
+void wPrintSetup( wPrintSetupCallBack_p callback )
+{
+ printInit();
+ newPPrinter = curPrinter;
+ newPPaper = curPaper;
+ printSetupCallBack = callback;
+ wListSetIndex( optPrinterB, newPPrinter );
+ wListSetIndex( optPaperSizeB, newPPaper );
+ wWinShow( printSetupW, TRUE );
+}
+
+static void pSetupOk( void )
+{
+ curPrinter = newPPrinter;
+ curPaper = newPPaper;
+ wWinShow( printSetupW, FALSE );
+ wPrefSetString( "printer", "name", printers(curPrinter).name );
+ wPrefSetString( "printer", "paper", papers[curPaper].name );
+ if ( curMargin < margins_da.cnt )
+ wPrefSetString( "printer", "margin", margins(curMargin).name );
+ wPrefSetString( "printer", "format", (printFormat==PRINT_LANDSCAPE?"landscape":"portrait") );
+ if (printSetupCallBack)
+ printSetupCallBack( TRUE );
+ wPrefSetFloat( WPRINTFONT, "factor", fontSizeFactor );
+}
+
+static void pSetupCancel( void )
+{
+ wWinShow( printSetupW, FALSE );
+ if (printSetupCallBack)
+ printSetupCallBack( FALSE );
+}
+
+
+/*****************************************************************************
+ *
+ * PRINTER LIST MANAGEMENT
+ *
+ */
+
+
+static wBool_t wPrintNewPrinter(
+ const char * name )
+{
+ char * cp;
+ const char *cpEqual;
+
+ printInit();
+ DYNARR_APPEND( printers_t, printers_da, 10 );
+ cpEqual = strchr( name, '=' );
+ if (cpEqual == NULL) {
+ printers(printers_da.cnt-1).cmd = strdup( "lpr -P%s" );
+ printers(printers_da.cnt-1).name = name;
+ } else {
+ cp = strdup( name );
+ cp[cpEqual-name] = 0;
+ printers(printers_da.cnt-1).name = cp;
+ printers(printers_da.cnt-1).cmd = cp+(cpEqual-name+1);
+ name = cp;
+ }
+ if (optPrinterB) {
+ wListAddValue( optPrinterB, printers(printers_da.cnt-1).name, NULL, (void*)(intptr_t)(printers_da.cnt-1) );
+ if ( prefName && strcasecmp( prefName, name ) == 0 ) {
+ curPrinter = printers_da.cnt-1;
+ wListSetIndex( optPrinterB, curPrinter );
+ }
+ }
+ return TRUE;
+}
+
+
+static void doMarginSel(
+ wIndex_t inx,
+ const char * name,
+ wIndex_t op,
+ void * listData,
+ void * itemData )
+{
+ margins_t * p;
+ static margins_t dummy = { "", 0, 0, 0, 0 };
+ if ( inx < 0 ) {
+ for ( inx=0,p=&margins(0); inx<margins_da.cnt; inx++,p++ ) {
+ if ( strcasecmp( name, margins(inx).name ) == 0 )
+ break;
+ }
+ if ( inx >= margins_da.cnt ) {
+ strncpy( newMarginName, name, sizeof newMarginName );
+ p = &dummy;
+ }
+ } else {
+ p = &margins(inx);
+ }
+ curMargin = inx;
+ tBorder = p->t;
+ bBorder = p->b;
+ rBorder = p->r;
+ lBorder = p->l;
+ wFloatSetValue( optTopMargin, tBorder );
+ wFloatSetValue( optBottomMargin, bBorder );
+ wFloatSetValue( optRightMargin, rBorder );
+ wFloatSetValue( optLeftMargin, lBorder );
+}
+
+static wIndex_t wPrintNewMargin(
+ const char * name,
+ const char * value )
+{
+ margins_t * m;
+ int rc;
+ DYNARR_APPEND( margins_t, margins_da, 10 );
+ m = &margins(margins_da.cnt-1);
+
+ setlocale( LC_NUMERIC, "C" );
+ if ((rc=sscanf( value, "%lf %lf %lf %lf", &m->t, &m->b, &m->r, &m->l ))!=4) {
+ margins_da.cnt--;
+ setlocale( LC_NUMERIC, "" );
+ return FALSE;
+ }
+ setlocale( LC_NUMERIC, "" );
+
+ m->name = strdup( name );
+ if (optMarginB)
+ wListAddValue( optMarginB, name, NULL, NULL );
+ if ( prefMargin && strcasecmp( prefMargin, name ) == 0 ) {
+ curMargin = margins_da.cnt-1;
+ wListSetIndex( optMarginB, curMargin );
+ tBorder = m->t;
+ bBorder = m->b;
+ rBorder = m->r;
+ lBorder = m->l;
+ wFloatSetValue( optTopMargin, tBorder );
+ wFloatSetValue( optBottomMargin, bBorder );
+ wFloatSetValue( optRightMargin, rBorder );
+ wFloatSetValue( optLeftMargin, lBorder );
+ }
+ return TRUE;
+}
+
+
+static void doChangeMargin( void )
+{
+ static char marginValue[256];
+ margins_t * m;
+ sprintf( marginValue, "%0.3f %0.3f %0.3f %0.3f", tBorder, bBorder, rBorder, lBorder );
+ if ( curMargin >= margins_da.cnt ) {
+ DYNARR_APPEND( margins_t, margins_da, 10 );
+ curMargin = margins_da.cnt-1;
+ margins(curMargin).name = strdup( newMarginName );
+ wListAddValue( optMarginB, margins(curMargin).name, NULL, NULL );
+ wListSetIndex( optMarginB, curMargin );
+ }
+ m = &margins(curMargin);
+ m->t = tBorder;
+ m->b = bBorder;
+ m->r = rBorder;
+ m->l = lBorder;
+ wPrefSetString( WMARGIN, m->name, marginValue );
+}
+
+
+static void doMarginDelete( void )
+{
+ int inx;
+ if ( curMargin >= margins_da.cnt || margins_da.cnt <= 1 || curMargin == 0 )
+ return;
+ wPrefSetString( WMARGIN, margins(curMargin).name, "" );
+ free( (char*)margins(curMargin).name );
+ for ( inx=curMargin+1; inx<margins_da.cnt; inx++ )
+ margins(inx-1) = margins(inx);
+ margins_da.cnt--;
+ wListDelete( optMarginB, curMargin );
+ if ( curMargin >= margins_da.cnt )
+ curMargin--;
+ doMarginSel( curMargin, margins(curMargin).name, 0, NULL, NULL );
+}
+
+
+static const char * curPsFont = NULL;
+static const char * curXFont = NULL;
+
+
+static void newFontAliasSel( const char * alias, void * data )
+{
+ wPrefSetString( WFONT, curXFont, alias );
+ curPsFont = wPrefGetString( WFONT, curXFont );
+ wWinShow( newFontAliasW, FALSE );
+ wListAddValue( optXFontL, curXFont, NULL, NULL );
+}
+
+
+static const char * findPSFont( wFont_p fp )
+{
+ const char *f;
+ static const char * oldXFont = NULL;
+
+ curXFont = gtkFontTranslate(fp);
+ if (curXFont != NULL &&
+ oldXFont != NULL &&
+ strcasecmp(oldXFont, curXFont) == 0 &&
+ curPsFont != NULL )
+ return curPsFont;
+ if (curXFont == NULL)
+ return "Times-Roman";
+ oldXFont = curXFont;
+ printInit();
+ f = wPrefGetString( WFONT, curXFont );
+ if (f)
+ return curPsFont = f;
+ wMessageSetValue( newFontAliasXFntB, curXFont );
+ wWinShow( newFontAliasW, TRUE );
+ return curPsFont;
+}
+
+/*****************************************************************************
+ *
+ * BASIC PRINTING
+ *
+ */
+
+static void setLineType(
+ double lineWidth,
+ wDrawLineType_e lineType,
+ wDrawOpts opts )
+{
+ PS_LT_E want;
+
+ if (lineWidth < 0.0) {
+ lineWidth = P2I(-lineWidth)*2.0;
+ }
+
+ if (lineWidth != currLineWidth) {
+ currLineWidth = lineWidth;
+ psPrintf( psFile, "%0.3f setlinewidth\n", currLineWidth / (PPI*10) );
+ }
+
+ if (lineType == wDrawLineDash)
+ want = PS_LT_DASH;
+ else
+ want = PS_LT_SOLID;
+ if (want != currentLT) {
+ currentLT = want;
+ switch (want) {
+ case PS_LT_DASH:
+ psPrintf( psFile, "[%0.3f %0.3f] 0 setdash\n", P2I(2), P2I(2) );
+ break;
+ case PS_LT_SOLID:
+ psPrintf( psFile, "[] 0 setdash\n" );
+ break;
+ }
+ }
+}
+
+
+void psSetColor(
+ wDrawColor color )
+{
+ static long currColor = 0;
+ long newColor;
+
+ newColor = wDrawGetRGB( color );
+ if (newColor != currColor) {
+ psPrintf( psFile, "%0.3f %0.3f %0.3f setrgbcolor\n",
+ (float)((newColor>>16)&0xFF)/256.0,
+ (float)((newColor>>8)&0xFF)/256.0,
+ (float)((newColor)&0xFF)/256.0 );
+ currColor = newColor;
+ }
+}
+
+
+void psPrintLine(
+ wPos_t x0, wPos_t y0,
+ wPos_t x1, wPos_t y1,
+ wDrawWidth width,
+ wDrawLineType_e lineType,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ if (color == wDrawColorWhite)
+ return;
+ if (opts&wDrawOptTemp)
+ return;
+ psSetColor(color);
+ setLineType( width, lineType, opts );
+ psPrintf(psFile,
+ "%0.3f %0.3f moveto %0.3f %0.3f lineto closepath stroke\n",
+ D2I(x0), D2I(y0), D2I(x1), D2I(y1) );
+}
+
+/**
+ * Print an arc around a specified center
+ *
+ * \param x0, y0 IN center of arc
+ * \param r IN radius
+ * \param angle0, angle1 IN start and end angle
+ * \param drawCenter draw marking for center
+ * \param width line width
+ * \param lineType
+ * \param color color
+ * \param opts ?
+ */
+
+void psPrintArc(
+ wPos_t x0, wPos_t y0,
+ wPos_t r,
+ double angle0,
+ double angle1,
+ wBool_t drawCenter,
+ wDrawWidth width,
+ wDrawLineType_e lineType,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ if (color == wDrawColorWhite)
+ return;
+ if (opts&wDrawOptTemp)
+ return;
+ psSetColor(color);
+ setLineType(width, lineType, opts);
+ if (angle1 >= 360.0)
+ angle1 = 359.999;
+ angle1 = 90.0-(angle0+angle1);
+ while (angle1 < 0.0) angle1 += 360.0;
+ while (angle1 >= 360.0) angle1 -= 360.0;
+ angle0 = 90.0-angle0;
+ while (angle0 < 0.0) angle0 += 360.0;
+ while (angle0 >= 360.0) angle0 -= 360.0;
+ psPrintf(psFile,
+ "newpath %0.3f %0.3f %0.3f %0.3f %0.3f arc stroke\n",
+ D2I(x0), D2I(y0), D2I(r), angle1, angle0 );
+
+ if( drawCenter ) {
+ psPrintf(psFile,
+ "%0.3f %0.3f moveto %0.3f %0.3f lineto closepath stroke\n",
+ D2I(x0 - CENTERMARK_LENGTH / 2), D2I(y0), D2I(x0 + CENTERMARK_LENGTH / 2), D2I(y0) );
+ psPrintf(psFile,
+ "%0.3f %0.3f moveto %0.3f %0.3f lineto closepath stroke\n",
+ D2I(x0), D2I(y0 - CENTERMARK_LENGTH / 2), D2I(x0), D2I(y0 + CENTERMARK_LENGTH / 2) );
+
+ }
+}
+
+
+void psPrintFillRectangle(
+ wPos_t x0, wPos_t y0,
+ wPos_t x1, wPos_t y1,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ if (color == wDrawColorWhite)
+ return;
+ if (opts&wDrawOptTemp)
+ return;
+ psSetColor(color);
+ psPrintf(psFile,
+ "%0.3f %0.3f moveto %0.3f %0.3f lineto closepath fill\n",
+ D2I(x0), D2I(y0), D2I(x1), D2I(y1) );
+}
+
+
+void psPrintFillPolygon(
+ wPos_t p[][2],
+ int cnt,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ int inx;
+ if (color == wDrawColorWhite)
+ return;
+ if (opts&wDrawOptTemp)
+ return;
+ psSetColor(color);
+ psPrintf( psFile, "%0.3f %0.3f moveto ", D2I(p[0][0]), D2I(p[0][1]) );
+ for (inx=0; inx<cnt; inx++)
+ psPrintf( psFile, "%0.3f %0.3f lineto ", D2I(p[inx][0]), D2I(p[inx][1]) );
+ psPrintf( psFile, "closepath fill\n" );
+}
+
+
+void psPrintFillCircle(
+ wPos_t x0, wPos_t y0,
+ wPos_t r,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ if (color == wDrawColorWhite)
+ return;
+ if (opts&wDrawOptTemp)
+ return;
+ psSetColor(color);
+ psPrintf(psFile,
+ "newpath %0.3f %0.3f %0.3f 0.0 360.0 arc fill\n",
+ D2I(x0), D2I(y0), D2I(r) );
+}
+
+
+void psPrintString(
+ wPos_t x, wPos_t y,
+ double a,
+ char * s,
+ wFont_p fp,
+ double fs,
+ wDrawColor color,
+ wDrawOpts opts )
+{
+ char * cp;
+
+ fs = P2I(fs*fontSizeFactor);
+ if (fs < 0.05*72.0/1440.0)
+ return;
+#ifdef NOWHITE
+ if (color == wDrawColorWhite)
+ return;
+#endif
+ if (opts&wDrawOptTemp)
+ return;
+ psSetColor( color );
+ setLineType(currLineWidth, wDrawLineSolid, opts);
+ psPrintf(psFile,
+ "/%s findfont %0.3f scalefont setfont\n"
+ "gsave\n"
+ "%0.3f %0.3f translate %0.3f rotate 0 0 moveto\n(",
+ findPSFont(fp), fs, D2I(x), D2I(y), a );
+ addFontName(findPSFont(fp));
+ for (cp=s; *cp; cp++) {
+ if (*cp == '(' || *cp == ')')
+ psPrintf(psFile, "\\" );
+ psPrintf(psFile, "%c", *cp);
+ }
+ psPrintf(psFile, ") show\ngrestore\n" );
+}
+
+void wPrintClip( wPos_t x, wPos_t y, wPos_t w, wPos_t h )
+{
+ psPrintf( psFile, "\
+%0.3f %0.3f moveto \n\
+%0.3f %0.3f lineto \n\
+%0.3f %0.3f lineto \n\
+%0.3f %0.3f lineto \n\
+closepath clip newpath\n",
+ D2I(x), D2I(y),
+ D2I(x+w), D2I(y),
+ D2I(x+w), D2I(y+h),
+ D2I(x), D2I(y+h) );
+}
+
+/*****************************************************************************
+ *
+ * PAGE FUNCTIONS
+ *
+ */
+
+void wPrintGetPageSize(
+ double * w,
+ double * h )
+{
+ printInit();
+ if (printFormat == PRINT_LANDSCAPE) {
+ *w = papers[curPaper].h - tBorder - bBorder;
+ *h = papers[curPaper].w - lBorder - rBorder;
+ } else {
+ *w = papers[curPaper].w - lBorder - rBorder;
+ *h = papers[curPaper].h - tBorder - bBorder;
+ }
+}
+
+void wPrintGetPhysSize(
+ double * w,
+ double * h )
+{
+ printInit();
+ if (printFormat == PRINT_LANDSCAPE) {
+ *w = papers[curPaper].h;
+ *h = papers[curPaper].w;
+ } else {
+ *w = papers[curPaper].w;
+ *h = papers[curPaper].h;
+ }
+}
+
+
+static void printAbort( void * context )
+{
+ printContinue = FALSE;
+ wWinShow( printAbortW, FALSE );
+}
+
+/**
+ * Initialize new page.
+ *
+ * \return ???
+ */
+wDraw_p wPrintPageStart( void )
+{
+ char tmp[80];
+
+ if (psFile == NULL)
+ return NULL;
+
+ pageCount++;
+ psPrintf( psFile,
+ "%%%%Page: %d %d\n" \
+ "save\n" \
+ "gsave\n" \
+ "0 setlinewidth\n"\
+ "1 setlinecap\n",
+ pageCount,
+ (totalPageCount>0?totalPageCount:pageCount) );
+
+ if (printFormat == PRINT_LANDSCAPE) {
+ psPrintf(psFile, "%0.3f %0.3f translate -90 rotate\n", lBorder*PPI, (papers[curPaper].h-tBorder)*PPI);
+ } else {
+ psPrintf(psFile, "%0.3f %0.3f translate 0 rotate\n", lBorder*PPI, bBorder*PPI);
+ }
+
+ psPrintf( psFile, "%0.1f %0.1f scale\n", PPI, PPI );
+
+ psPrintf( psFile, "/Times-Bold findfont %0.3f scalefont setfont\n",
+ P2I(16) );
+ addFontName("Times-Bold");
+ sprintf( tmp, _("Page %d"), pageCount );
+ wMessageSetValue( printAbortM, tmp );
+ wFlush();
+
+ currLineWidth = 0;
+ return &psPrint_d;
+}
+
+/**
+ * End of page
+ *
+ * \param p IN ignored
+ * \return always printContinue
+ */
+
+
+wBool_t wPrintPageEnd( wDraw_p p )
+{
+ psPrintf( psFile,
+ "grestore\n" \
+ "restore\n" \
+ "showpage\n"\
+ "%%%%EndPage\n");
+
+ return printContinue;
+}
+
+/*****************************************************************************
+ *
+ * PRINT START/END
+ *
+ */
+
+/**
+ * Allow the user to enter a new file name and location for the file.
+ * Thanks to Andrew Krause's great book Foundations of GTK+ Development
+ * for this code snippet.
+ *
+ * \param junk IN ignored
+ */
+
+static void printFileNameSel( void * junk )
+{
+ GtkWidget *dialog;
+ gchar *filename;
+ gint result;
+
+ dialog = gtk_file_chooser_dialog_new (_("Print to file ..."), (GtkWindow *)printSetupW->gtkwin,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (result == GTK_RESPONSE_ACCEPT)
+ {
+ filename = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER ( dialog ));
+ if( filename ) {
+ sPrintFileName = malloc( strlen( filename ) + 1 );
+ if( sPrintFileName ) {
+ strcpy( sPrintFileName, filename );
+ }
+ else {
+ fputs( "Insufficient memory for printing to file\n", stderr );
+ abort();
+ }
+ g_free( filename );
+ }
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+
+/*
+ * open the printer output stream. In case print to file is selected, the filename for
+ * the print out is fetched from the user and the file opened.
+ *
+ * \return the printer stream
+ */
+
+wPrinterStream_p wPrinterOpen( void )
+{
+ char * fn;
+ char sPrintCmdName[80];
+ char tmp[80+8];
+ FILE * f;
+ wIndex_t cmdOrFile;
+ wPrinterStream_p p;
+
+ printInit();
+ pageCount = 0;
+ f = NULL;
+ curPsFont = NULL;
+ if (curPrinter == 0 ) {
+
+ printFileNameSel( NULL );
+
+ // did the user cancel the file dialog? If yes, cancel operation
+ if( !sPrintFileName ) {
+ return( NULL );
+ }
+ if ( sPrintFileName[0] == '\0' ) {
+ wNoticeEx( NT_ERROR, _("No file name specified"), _("Ok"), NULL );
+ return NULL;
+ }
+ if ( access(sPrintFileName, F_OK ) == 0 ) {
+ sprintf( tmp, _("%s exists"), sPrintFileName );
+ if (!wNoticeEx( NT_INFORMATION, tmp, _("Overwrite"), _("Cancel") ))
+ return NULL;
+ }
+ f = fopen( sPrintFileName, "w" );
+ if (f == NULL) {
+ strcat( sPrintFileName, _(": cannot open") );
+ wNoticeEx( NT_ERROR, sPrintFileName, _("Ok"), NULL );
+ return NULL;
+ }
+ fn = sPrintFileName;
+ cmdOrFile = PRINT_FILE;
+ } else {
+ sprintf( sPrintCmdName, printers(curPrinter).cmd, printers(curPrinter).name );
+ f = popen( sPrintCmdName, "w" );
+ fn = sPrintCmdName;
+ cmdOrFile = PRINT_COMMAND;
+ }
+ if (f == NULL) {
+ strcat( sPrintFileName, _(": cannot open") );
+ wNoticeEx( NT_ERROR, sPrintFileName, _("Ok"), NULL );
+ return NULL;
+ }
+ p = (wPrinterStream_p)malloc( sizeof *p );
+ p->f = f;
+ p->cmdOrFile = cmdOrFile;
+ return p;
+}
+
+
+void wPrinterWrite( wPrinterStream_p p, char * buff, int siz )
+{
+ fwrite( buff, 1, siz, p->f );
+}
+
+void wPrinterClose( wPrinterStream_p p )
+{
+ if (p->cmdOrFile == PRINT_FILE)
+ fclose( p->f );
+ else
+ pclose( p->f );
+
+ // free the filename again
+ if( sPrintFileName ) {
+ free( sPrintFileName );
+ sPrintFileName = NULL;
+ }
+}
+
+/**
+ * Start a new Postscript document
+ *
+ * Opens the output file and emits the Adobe DSC Prolog comments,
+ * etc. Note that the 3.0 in "PS-Adobe-3.0" refers to the
+ * version of the Document Structuring Conventions Specification,
+ * not to the Postscript language level.
+ *
+ * \param title IN title of document ( name of layout )
+ * \param fTotalPageCount IN number of pages to print
+ * \param copiesP OUT ???
+ * \return TRUE if successful
+ */
+
+wBool_t wPrintDocStart( const char * title, int fTotalPageCount, int * copiesP )
+{
+ char tmp[80];
+ pageCount = 0;
+ totalPageCount = fTotalPageCount;
+ psFile = NULL;
+ psFileStream = wPrinterOpen();
+ if (psFileStream == NULL)
+ return FALSE;
+ psFile = psFileStream->f;
+
+ /* Initialize the list of fonts used */
+ fontsUsedInit(); /* in case a document had been
+ produced earlier */
+
+ psPrintf( psFile,
+ "%%!PS-Adobe-3.0\n\
+%%%%DocumentFonts: (atend)\n\
+%%%%Title: %s\n\
+%%%%Creator: XTrackCAD\n\
+%%%%Pages: (atend)\n\
+%%%%BoundingBox: %ld %ld %ld %ld\n\
+%%%%EndComments\n\n\
+%%%%Prolog\n\
+/mp_stm usertime def\n\
+/mp_pgc statusdict begin pagecount end def\n\
+statusdict begin /jobname (<stdin>) def end\n\
+%%%%EndProlog\n", \
+ title,
+ (long)floor(margins(curMargin).l*72),
+ (long)floor(margins(curMargin).b*72),
+ (long)floor((papers[curPaper].w-margins(curMargin).r)*72),
+ (long)floor((papers[curPaper].h-margins(curMargin).t)*72) );
+
+ printContinue = TRUE;
+ sprintf( tmp, ("Now printing %s"), title );
+ wMessageSetValue( printAbortT, tmp );
+ wMessageSetValue( printAbortM, _("Page 1") );
+ pageCount = 0;
+ wWinShow( printAbortW, TRUE );
+ if (copiesP)
+ *copiesP = 1;
+ return TRUE;
+}
+
+/**
+ * Outputs the Adobe Document Structure Comments.
+ * These are needed at the
+ * end of a Postscript document destined for modern (2012) print
+ * spoolers. E.g. CUPS
+ */
+
+void wPrintDocEnd( void )
+{
+ struct list_node *p;
+ int i;
+ if (psFile == NULL)
+ return;
+
+ psPrintf( psFile,
+ "%%%%Trailer\n%%%%Pages: %d\n",
+ pageCount );
+
+ /* Postscript lines are <255 chars so print fonts list 4
+ per line
+ */
+ psPrintf( psFile, "%%%%DocumentFonts: " );
+ p = fontsUsed;
+ i = 0;
+ while ((p=fontsUsedPop()) != NULL) {
+ if ((i % 4) == 0 ) psPrintf( psFile, "\n%%%%+ ");
+ psPrintf( psFile, " %s", p->data);
+ free(p->data);
+ free(p);
+ i++;
+ }
+ psPrintf( psFile, "\n");
+
+ psPrintf( psFile, "%%%%EOF\n");
+ /* Reset the fonts list to empty for the next document.
+ */
+ fontsUsedInit();
+
+ wPrinterClose( psFileStream );
+ wWinShow( printAbortW, FALSE );
+}
+
+
+wBool_t wPrintQuit( void )
+{
+ return FALSE;
+}
+
+
+static void pLine( double x0, double y0, double x1, double y1 )
+{
+ psPrintf( psFile, "%0.3f %0.3f moveto %0.3f %0.3f lineto stroke\n",
+ x0, y0, x1, y1 );
+}
+
+/**
+ * Generate a test page that helps setting up printer margins.
+ */
+
+static void pTestPage( void )
+{
+ double w, h;
+ long oldPrinter;
+ int i, j, k, run;
+ double x0, x1, y0, y1;
+ const char * psFont, * xFont;
+ long curMargin0;
+
+ oldPrinter = curPrinter;
+ curPrinter = newPPrinter;
+ curMargin0 = curMargin;
+ curMargin = 0;
+ wPrintDocStart( _("Printer Margin Test Page"), 1, NULL );
+ wPrintPageStart();
+ curMargin = curMargin0;
+ w = papers[curPaper].w;
+ h = papers[curPaper].h;
+ if ( psFile == NULL )
+ return;
+
+#define MAXIMUM (100)
+
+ psPrintf( psFile, "/Times-Roman findfont 0.06 scalefont setfont\n" );
+ addFontName("Times-Roman");
+ for ( i=5; i<=MAXIMUM; i+=5 ) {
+ x0 = ((double)i)/100;
+ pLine( 0.5, x0, w-0.5, x0 );
+ pLine( 0.5, h-x0, w-0.5, h-x0 );
+ pLine( x0, 0.5, x0, h-0.5 );
+ pLine( w-x0, 0.5, w-x0, h-0.5 );
+
+ psPrintf( psFile, "%0.3f %0.3f moveto (%0.2f) show\n",
+ 1.625 + x0*5 - 0.05, 0.2+MAXIMUM/100.0, x0 );
+ pLine( 1.625 + x0*5, (0.2+MAXIMUM/100.0), 1.625 + x0*5, x0 );
+ psPrintf( psFile, "%0.3f %0.3f moveto (%0.2f) show\n",
+ 1.625 + x0*5 - 0.05, h-(0.2+MAXIMUM/100.0)-0.05, x0 );
+ pLine( 1.625 + x0*5, h-(0.2+MAXIMUM/100.0), 1.625 + x0*5, h-x0 );
+
+ psPrintf( psFile, "%0.3f %0.3f moveto (%0.2f) show\n",
+ (0.2+MAXIMUM/100.0), 1.625 + x0*5-0.020, x0 );
+ pLine( (0.2+MAXIMUM/100.0), 1.625 + x0*5, x0, 1.625 + x0*5 );
+ psPrintf( psFile, "%0.3f %0.3f moveto (%0.2f) show\n",
+ w-(0.2+MAXIMUM/100.0)-0.10, 1.625 + x0*5-0.020, x0 );
+ pLine( w-(0.2+MAXIMUM/100.0), 1.625 + x0*5, w-x0, 1.625 + x0*5 );
+ }
+
+ psPrintf( psFile, "/Times-Bold findfont 0.20 scalefont setfont\n" );
+ addFontName("Times-Bold");
+ psPrintf( psFile, "%0.3f %0.3f moveto (%s) show\n", 2.0, h-2.0, "Printer Margin Setup" );
+ psPrintf( psFile, "/Times-Roman findfont 0.12 scalefont setfont\n" );
+ addFontName("Times-Roman");
+ psPrintf( psFile, "%0.3f %0.3f moveto (%s) show\n", 2.0, h-2.15,
+ "Enter the position of the first visible line for each margin on the Printer Setup dialog");
+ if ( curMargin < margins_da.cnt )
+ psPrintf( psFile, "%0.3f %0.3f moveto ("
+ "Current margins for the %s printer are: Top: %0.3f, Left: %0.3f, Right: %0.3f, Bottom: %0.3f"
+ ") show\n", 2.0, h-2.30,
+ margins(curMargin).name, margins(curMargin).t, margins(curMargin).l, margins(curMargin).r, margins(curMargin).b );
+
+
+ psPrintf( psFile, "/Times-Bold findfont 0.20 scalefont setfont\n" );
+ addFontName("Times-Bold");
+ psPrintf( psFile, "%0.3f %0.3f moveto (%s) show\n", 2.0, h-3.0, "Font Map" );
+ for (i=j=0; 0.2*j < h-5.0 && (psFont = wPrefGetSectionItem( WFONT, &i, &xFont )) != NULL; j++ ) {
+ if ( psFont[0] == '\0' ) continue;
+ psPrintf( psFile, "/Times-Roman findfont 0.12 scalefont setfont\n" );
+ addFontName("Times-Roman");
+ psPrintf( psFile, "%0.3f %0.3f moveto (%s -> %s) show\n", 2.0, h-3.15-0.15*j, xFont, psFont );
+ psPrintf( psFile, "/%s findfont 0.12 scalefont setfont\n", psFont );
+ addFontName(psFont);
+ psPrintf( psFile, "%0.3f %0.3f moveto (%s) show\n", 5.5, h-3.15-0.15*j, "ABCD wxyz 0123 -+$!" );
+ }
+ x0 = 0.5;
+ run = TRUE;
+ i = 0;
+ while (run) {
+ x1 = x0 + 0.25;
+ if (x1 >= w-0.5) {
+ x1 = w-0.5;
+ run = FALSE;
+ }
+ for ( j = 1; j<5; j++ ) {
+ y0 = ((double)(i+j))/100;
+ for (k=0; k<MAXIMUM/25; k++) {
+ pLine( x0, y0+k*0.25, x1, y0+k*0.25 );
+ pLine( x0, h-y0-k*0.25, x1, h-y0-k*0.25 );
+ }
+ }
+ x0 += 0.25;
+ i += 5;
+ if (i >= 25)
+ i = 0;
+ }
+
+ y0 = 0.5;
+ run = TRUE;
+ i = 0;
+ while (run) {
+ y1 = y0 + 0.25;
+ if (y1 >= h-0.5) {
+ y1 = h-0.5;
+ run = FALSE;
+ }
+ for ( j = 1; j<5; j++ ) {
+ x0 = ((double)(i+j))/100;
+ for (k=0; k<MAXIMUM/25; k++) {
+ pLine( x0+k*0.25, y0, x0+k*0.25, y1 );
+ pLine( w-x0-k*0.25, y0, w-x0-k*0.25, y1 );
+ }
+ }
+ y0 += 0.25;
+ i += 5;
+ if (i >= 25)
+ i = 0;
+ }
+
+ /* psPrintf( psFile, "showpage\n"); */
+ wPrintPageEnd(NULL);
+ wPrintDocEnd();
+ curPrinter = oldPrinter;
+}
+
+
+#ifdef LATER
+static void newPrinter( void * context )
+{
+ wStringSetValue( addPrinterN, "" );
+ wStringSetValue( addPrinterC, "" );
+ addPrinterName[0] = 0;
+ addPrinterCommand[0] = 0;
+ wWinShow( addPrinterW, TRUE );
+}
+
+
+static void addPrinterOk( const char * str, void * context )
+{
+ char tmp[80];
+ if (strlen(addPrinterName) == 0 || strlen(addPrinterCommand) == 0) {
+ wNotice( _("Enter both printer name and command"), _("Ok"), NULL );
+ return;
+ }
+ if (printerDefine)
+ printerDefine( addPrinterName, addPrinterCommand );
+ else
+ wNotice( _("Can not save New Printer definition"), _("Ok"), NULL );
+ sprintf( tmp, "%s=%s", addPrinterName, addPrinterCommand );
+ wPrintNewPrinter( tmp );
+}
+
+
+static void newMargin( void * context )
+{
+ wStringSetValue( addMarginN, "" );
+ addMarginName[0] = 0;
+ wWinShow( addMarginW, TRUE );
+ gtkSetReadonly((wControl_p)optTopMargin,FALSE);
+ gtkSetReadonly((wControl_p)optBottomMargin,FALSE);
+ gtkSetReadonly((wControl_p)optLeftMargin,FALSE);
+ gtkSetReadonly((wControl_p)optRightMargin,FALSE);
+}
+
+
+static void addMarginOk( const char * str, void * context )
+{
+ margins_t * m;
+ if (strlen(addMarginName) == 0) {
+ wNotice( _("Enter printer name"), _("Ok"), NULL );
+ return;
+ }
+ if (marginDefine)
+ marginDefine( addMarginName, tBorder, bBorder, rBorder, lBorder );
+ else
+ wNotice( _("Can not save New Margin definition"), _("Ok"), NULL );
+ DYNARR_APPEND( margins_t, margins_da, 10 );
+ m = &margins(margins_da.cnt-1);
+ m->name = strdup( addMarginName );
+ m->t = tBorder;
+ m->b = bBorder;
+ m->r = rBorder;
+ m->l = lBorder;
+ wListAddValue( optMarginB, addMarginName, NULL, NULL );
+ gtkSetReadonly((wControl_p)optTopMargin,TRUE);
+ gtkSetReadonly((wControl_p)optBottomMargin,TRUE);
+ gtkSetReadonly((wControl_p)optLeftMargin,TRUE);
+ gtkSetReadonly((wControl_p)optRightMargin,TRUE);
+}
+#endif
+
+
+static wLines_t lines[] = {
+ { 1, 25, 11, 95, 11 },
+ { 1, 95, 11, 95, 111 },
+ { 1, 95, 111, 25, 111 },
+ { 1, 25, 111, 25, 11 }};
+#ifdef LATER
+ { 1, 97, 10, 125, 10 },
+ { 1, 160, 10, 177, 10 },
+ { 1, 97, 10, 97, 50 },
+ { 1, 97, 67, 97, 110 },
+ { 1, 177, 10, 177, 50 },
+ { 1, 177, 67, 177, 110 },
+ { 1, 97, 110, 125, 110 },
+ { 1, 160, 110, 177, 110 } };
+#endif
+
+static const char * printFmtLabels[] = { N_("Portrait"), N_("Landscape"), NULL };
+
+static struct {
+ const char * xfontname, * psfontname;
+ } fontmap[] = {
+ { "times-medium-r", "Times-Roman" },
+ { "times-medium-i", "Times-Italic" },
+ { "times-bold-r", "Times-Bold" },
+ { "times-bold-i", "Times-BoldItalic" },
+ { "helvetica-medium-r", "Helvetica" },
+ { "helvetica-medium-o", "Helvetica-Oblique" },
+ { "helvetica-bold-r", "Helvetica-Bold" },
+ { "helvetica-bold-o", "Helvetica-BoldOblique" },
+ { "courier-medium-r", "Courier" },
+ { "courier-medium-o", "Courier-Oblique" },
+ { "courier-medium-i", "Courier-Oblique" },
+ { "courier-bold-r", "Courier-Bold" },
+ { "courier-bold-o", "Courier-BoldOblique" },
+ { "courier-bold-i", "Courier-BoldOblique" },
+ { "avantgarde-book-r", "AvantGarde-Book" },
+ { "avantgarde-book-o", "AvantGarde-BookOblique" },
+ { "avantgarde-demi-r", "AvantGarde-Demi" },
+ { "avantgarde-demi-o", "AvantGarde-DemiOblique" },
+ { "palatino-medium-r", "Palatino-Roman" },
+ { "palatino-medium-i", "Palatino-Italic" },
+ { "palatino-bold-r", "Palatino-Bold" },
+ { "palatino-bold-i", "Palatino-BoldItalic" },
+ { "new century schoolbook-medium-r", "NewCenturySchlbk-Roman" },
+ { "new century schoolbook-medium-i", "NewCenturySchlbk-Italic" },
+ { "new century schoolbook-bold-r", "NewCenturySchlbk-Bold" },
+ { "new century schoolbook-bold-i", "NewCenturySchlbk-BoldItalic" },
+ { "zapfchancery-medium-i", "ZapfChancery-MediumItalic" } };
+
+static struct {
+ const char * name, * value;
+ } pagemargins [] = {
+ { "None", "0.00 0.00 0.00 0.00" },
+ { "BJC-600", "0.10 0.44 0.38 0.13" },
+ { "DeskJet", "0.167 0.50 0.25 0.25" },
+ { "PaintJet", "0.167 0.167 0.167 0.167" },
+ { "DJ505", "0.25 0.668 0.125 0.125" },
+ { "DJ560C", "0.37 0.46 0.25 0.25" },
+ { "LaserJet", "0.43 0.21 0.43 0.28" } };
+
+
+static void doSetOptXFont(
+ wIndex_t inx,
+ const char * xFont,
+ wIndex_t inx2,
+ void * itemData,
+ void * listData )
+{
+ const char * cp;
+ optXFont = xFont;
+ cp = wPrefGetString( WFONT, xFont );
+ if ( !cp )
+ cp = "";
+ wStringSetValue( optPSFontS, cp );
+}
+
+
+static void doSetOptPSFont(
+ const char * psFont,
+ void * data )
+{
+ if ( optXFont &&
+ psFont[0] )
+ wPrefSetString( WFONT, optXFont, psFont );
+}
+
+
+static void printInit( void )
+{
+ wIndex_t i;
+ wPos_t x, y;
+ static wBool_t printInitted = FALSE;
+ const char * cp, * cq;
+ char num[10];
+
+ if (printInitted)
+ return;
+
+ printInitted = TRUE;
+ prefName = wPrefGetString( "printer", "name" );
+ prefPaper = wPrefGetString( "printer", "paper" );
+ prefMargin = wPrefGetString( "printer", "margin" );
+ prefFormat = wPrefGetString( "printer", "format" );
+ if (prefFormat && strcasecmp(prefFormat, "landscape") == 0)
+ printFormat = PRINT_LANDSCAPE;
+ else
+ printFormat = PRINT_PORTRAIT;
+ wPrefGetFloat( WPRINTFONT, "factor", &fontSizeFactor, 1.0 );
+ if ( fontSizeFactor < 0.5 || fontSizeFactor > 2.0 ) {
+ fontSizeFactor = 1.0;
+ wPrefSetFloat( WPRINTFONT, "factor", fontSizeFactor );
+ }
+
+ x = wLabelWidth( _("Paper Size") )+4;
+ printSetupW = wWinPopupCreate( NULL, 4, 4, "printSetupW", _("Print Setup"), "xvprintsetup", F_AUTOSIZE|F_RECALLPOS, NULL, NULL );
+ optPrinterB = wDropListCreate( printSetupW, x, -4, "printSetupPrinter", _("Printer"), 0, 4, 100, &newPPrinter, NULL, NULL );
+#ifdef LATER
+ wButtonCreate( printSetupW, -10, 2, "printSetupPrinter", _("New"), 0, 0, newPrinter, NULL );
+#endif
+ optPaperSizeB = wDropListCreate( printSetupW, x, -4, "printSetupPaper", _("Paper Size"), 0, 4, 100, &newPPaper, NULL, NULL );
+ y = wControlGetPosY( (wControl_p)optPaperSizeB ) + wControlGetHeight( (wControl_p)optPaperSizeB ) + 10;
+ for ( i=0; i<sizeof lines / sizeof lines[0]; i++ ) {
+ lines[i].x0 += x;
+ lines[i].x1 += x;
+ lines[i].y0 += y;
+ lines[i].y1 += y;
+ }
+ wLineCreate( printSetupW, NULL, sizeof lines / sizeof lines[0], lines );
+ optTopMargin = wFloatCreate( printSetupW, x+35, y, "printSetupMargin", NULL, 0, 50, 0.0, 1.0, &tBorder, (wFloatCallBack_p)doChangeMargin, NULL );
+ optLeftMargin = wFloatCreate( printSetupW, x, y+50, "printSetupMargin", _("Margin"), 0, 50, 0.0, 1.0, &lBorder, (wFloatCallBack_p)doChangeMargin, NULL );
+ optRightMargin = wFloatCreate( printSetupW, x+70, y+50, "printSetupMargin", NULL, 0, 50, 0.0, 1.0, &rBorder, (wFloatCallBack_p)doChangeMargin, NULL );
+ optBottomMargin = wFloatCreate( printSetupW, x+35, y+100, "printSetupMargin", NULL, 0, 50, 0.0, 1.0, &bBorder, (wFloatCallBack_p)doChangeMargin, NULL );
+ optMarginB = wDropListCreate( printSetupW, x, -5, "printSetupMargin", NULL, BL_EDITABLE, 4, 100, NULL, doMarginSel, NULL );
+ optMarginDelB = wButtonCreate( printSetupW, wControlGetPosX((wControl_p)optMarginB)+wControlGetWidth((wControl_p)optMarginB)+5, wControlGetPosY((wControl_p)optMarginB), "printSetupMarginDelete", "Delete", 0, 0, (wButtonCallBack_p)doMarginDelete, NULL );
+#ifdef LATER
+ wButtonCreate( printSetupW, -10, wControlGetPosY((wControl_p)optMarginB), "printSetupMargin", _("New"), 0, 0, newMargin, NULL );
+#endif
+ optFormat = wRadioCreate( printSetupW, x, -5, "printSetupFormat", _("Format"), BC_HORZ,
+ printFmtLabels, &printFormat, NULL, NULL );
+ optXFontL = wDropListCreate( printSetupW, x, -6, "printSetupXFont", _("X Font"), 0, 4, 200, &optXFontX, doSetOptXFont, NULL );
+ optPSFontS = wStringCreate( printSetupW, x, -4, "printSetupPSFont", _("PS Font"), 0, 200, optPSFont, 0, doSetOptPSFont, NULL );
+ optFontSizeFactor = wFloatCreate( printSetupW, x, -4, "printSetupFontSizeFactor", _("Factor"), 0, 50, 0.5, 2.0, &fontSizeFactor, (wFloatCallBack_p)NULL, NULL );
+ y = wControlGetPosY( (wControl_p)optFontSizeFactor ) + wControlGetHeight( (wControl_p)optFontSizeFactor ) + 10;
+ x = wControlGetPosX( (wControl_p)optPrinterB ) + wControlGetWidth( (wControl_p)optPrinterB ) + 10;
+ wButtonCreate( printSetupW, x, 4, "printSetupOk", _("Ok"), 0, 0, (wButtonCallBack_p)pSetupOk, NULL );
+ wButtonCreate( printSetupW, x, -4, "printSetupCancel", _("Cancel"), 0, 0, (wButtonCallBack_p)pSetupCancel, NULL );
+ wButtonCreate( printSetupW, x, -14, "printSetupTest", _("Print Test Page"), 0, 0, (wButtonCallBack_p)pTestPage, NULL );
+
+#ifdef LATER
+ addPrinterW = wWinPopupCreate( printSetupW, 2, 2, "printSetupPrinter", _("Add Printer"), "xvaddprinter", F_AUTOSIZE|F_RECALLPOS, NULL, NULL );
+ addPrinterN = wStringCreate( addPrinterW, 100, -3, "printSetupPrinter",
+ _("Name: "), 0, 150, addPrinterName, sizeof addPrinterName,
+ addPrinterOk, NULL );
+ addPrinterC = wStringCreate( addPrinterW, 100, -3, "printSetupPrinter",
+ _("Command: "), 0, 150, addPrinterCommand, sizeof addPrinterCommand,
+ addPrinterOk, NULL );
+
+ addMarginW = wWinPopupCreate( printSetupW, 2, 2, "printSetupMargin", _("Add Margin"), "xvaddmargin", F_AUTOSIZE|F_RECALLPOS, NULL, NULL );
+ addMarginN = wStringCreate( addMarginW, 100, -3, "printSetupMargin",
+ _("Name: "), 0, 150, addMarginName, sizeof addMarginName,
+ addMarginOk, NULL );
+#endif
+
+ printFileW = wWinPopupCreate( printSetupW, 2, 2, "printFileNameW", _("Print To File"), "xvprinttofile", F_BLOCK|F_AUTOSIZE|F_RECALLPOS, NULL, NULL );
+ wStringCreate( printFileW, 100, 3, "printFileName",
+ _("File Name? "), 0, 150, sPrintFileName, sizeof sPrintFileName,
+ NULL, NULL );
+ wButtonCreate( printFileW, -4, 3, "printFileNameOk", _("Ok"), BB_DEFAULT, 0, printFileNameSel, NULL );
+
+ newFontAliasW = wWinPopupCreate( printSetupW, 2, 2, "printFontAliasW", _("Font Alias"), "xvfontalias", F_BLOCK|F_AUTOSIZE|F_RECALLPOS, NULL, NULL );
+ wMessageCreate( newFontAliasW, 0, 0, NULL, 200, _("Enter a post-script font name for:") );
+ newFontAliasXFntB = wMessageCreate( newFontAliasW, 0, -3, NULL, 200, "" );
+ wStringCreate( newFontAliasW, 0, -3, "printFontAlias", NULL, 0, 200, NULL, 0, newFontAliasSel, NULL );
+
+ for (i=0; papers[i].name; i++ ) {
+ wListAddValue( optPaperSizeB, papers[i].name, NULL, (void*)(intptr_t)i );
+ if ( prefPaper && strcasecmp( prefPaper, papers[i].name ) == 0 ) {
+ curPaper = i;
+ wListSetIndex( optPaperSizeB, i );
+ }
+ }
+
+ printAbortW = wWinPopupCreate( printSetupW, 2, 2, "printAbortW", _("Printing"), "xvprintabort", F_AUTOSIZE|F_RECALLPOS, NULL, NULL );
+ printAbortT = wMessageCreate( printAbortW, 0, 0, "printAbortW", 200, _("Now printing") );
+ printAbortM = wMessageCreate( printAbortW, 0, -4, "printAbortW", 200, NULL );
+ wButtonCreate( printAbortW, 0, 80, "printAbortW", _("Abort Print"), 0, 0, printAbort, NULL );
+
+ for (i=0;i<sizeof fontmap/sizeof fontmap[0]; i++) {
+ cp = wPrefGetString( WFONT, fontmap[i].xfontname );
+ if (!cp)
+ wPrefSetString( WFONT, fontmap[i].xfontname, fontmap[i].psfontname );
+ }
+
+ cp = wPrefGetString( WPRINTER, "1" );
+ if (!cp)
+ wPrefSetString( WPRINTER, "1", "lp=lpr -P%s" );
+ wPrintNewPrinter( "FILE" );
+ for (i=1; ;i++) {
+ sprintf( num, "%d", i );
+ cp = wPrefGetString( WPRINTER, num );
+ if (!cp)
+ break;
+ wPrintNewPrinter(cp);
+ }
+
+ for (i=0;i<sizeof pagemargins/sizeof pagemargins[0]; i++) {
+ cp = wPrefGetString( WMARGIN, pagemargins[i].name );
+ if (!cp)
+ wPrefSetString( WMARGIN, pagemargins[i].name, pagemargins[i].value );
+ sprintf( num, "%d", i );
+ wPrefSetString( WMARGINMAP, num, pagemargins[i].name );
+ }
+ for (i=0; (cq = wPrefGetSectionItem( WMARGIN, &i, &cp )); ) {
+ wPrintNewMargin(cp, cq);
+ }
+
+ for ( i=0, optXFont=NULL; wPrefGetSectionItem( WFONT, &i, &cp ); ) {
+ if ( optXFont == NULL )
+ optXFont = cp;
+ wListAddValue( optXFontL, cp, NULL, NULL );
+ }
+ wListSetIndex( optXFontL, 0 );
+ if ( optXFont ) {
+ cp = wPrefGetString( WFONT, optXFont );
+ wStringSetValue( optPSFontS, cp );
+ }
+
+}
+
+
+wBool_t wPrintInit( void )
+{
+ return TRUE;
+}
+
+/*****************************************************************************
+ *
+ * TEST
+ *
+ */
+
+#ifdef TEST
+
+void main ( INT_T argc, char * argv[] )
+{
+ if (argc != 7) {
+ fprintf( stderr, "%s <L|P> <origX> <origY> <roomSizeX> <roomSizeY>\n", argv[0] );
+ exit(1);
+ }
+ argv++;
+ printFormat = (*(*argv++)=='L')?PRINT_LANDSCAPE:PRINT_PORTRAIT;
+ printDraw_d.orig.x = atof(*argv++);
+ printDraw_d.orig.y = atof(*argv++);
+ printRoomSize.x = atof(*argv++);
+ printRoomSize.y = atof(*argv++);
+ fprintf( stderr, "Fmt=%c, orig=(%0.3f %0.3f) RS=(%0.3f %0.3f)\n",
+ (printFormat==PRINT_LANDSCAPE)?'L':'P',
+ printDraw_d.orig.x, printDraw_d.orig.y,
+ printRoomSize.x, printRoomSize.y );
+ wPrintGetPageSize(PRINT_GAUDY, printFormat);
+ fprintf( stderr, "PageSize= (%0.3f %0.3f)\n", printDraw_d.size.x, printDraw_d.size.y );
+
+ wPrintDocStart( PRINT_GAUDY );
+ wPrintPage( PRINT_GAUDY, 0, 0 );
+ wPrintDocEnd( );
+}
+
+#endif
diff --git a/app/wlib/gtklib/square10.bmp b/app/wlib/gtklib/square10.bmp
new file mode 100644
index 0000000..b2eee6e
--- /dev/null
+++ b/app/wlib/gtklib/square10.bmp
@@ -0,0 +1,8 @@
+#define square10_width 14
+#define square10_height 14
+// Changed to eliminate compile-time warning
+//static unsigned char square10_bits[] = {
+static char square10_bits[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff};
diff --git a/app/wlib/gtklib/uthash.h b/app/wlib/gtklib/uthash.h
new file mode 100644
index 0000000..39fd891
--- /dev/null
+++ b/app/wlib/gtklib/uthash.h
@@ -0,0 +1,960 @@
+/*
+Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H
+
+#include <string.h> /* memcmp,strlen */
+#include <stddef.h> /* ptrdiff_t */
+#include <stdlib.h> /* exit() */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+ As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+ when compiling c++ source) this code uses whatever method is needed
+ or, for VS2008 where neither is available, uses casting workarounds. */
+#if defined(_MSC_VER) /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#endif
+#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__)
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#else /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ char **_da_dst = (char**)(&(dst)); \
+ *_da_dst = (char*)(src); \
+} while(0)
+#else
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ (dst) = DECLTYPE(dst)(src); \
+} while(0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
+#if defined (_WIN32)
+#if defined(_MSC_VER) && _MSC_VER >= 1600
+#include <stdint.h>
+#elif defined(__WATCOMC__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+#else
+#include <stdint.h>
+#endif
+
+#define UTHASH_VERSION 1.9.9
+
+#ifndef uthash_fatal
+#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
+#endif
+#ifndef uthash_malloc
+#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
+#endif
+#ifndef uthash_free
+#define uthash_free(ptr,sz) free(ptr) /* free fcn */
+#endif
+
+#ifndef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
+#endif
+#ifndef uthash_expand_fyi
+#define uthash_expand_fyi(tbl) /* can be defined to log expands */
+#endif
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhe */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+
+#define HASH_FIND(hh,head,keyptr,keylen,out) \
+do { \
+ out=NULL; \
+ if (head) { \
+ unsigned _hf_bkt,_hf_hashv; \
+ HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
+ if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \
+ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
+ keyptr,keylen,out); \
+ } \
+ } \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
+#define HASH_BLOOM_MAKE(tbl) \
+do { \
+ (tbl)->bloom_nbits = HASH_BLOOM; \
+ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
+ if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
+ memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
+ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
+} while (0)
+
+#define HASH_BLOOM_FREE(tbl) \
+do { \
+ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
+} while (0)
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
+
+#define HASH_BLOOM_ADD(tbl,hashv) \
+ HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#define HASH_BLOOM_TEST(tbl,hashv) \
+ HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#define HASH_BLOOM_BYTELEN 0
+#endif
+
+#define HASH_MAKE_TABLE(hh,head) \
+do { \
+ (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
+ sizeof(UT_hash_table)); \
+ if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
+ (head)->hh.tbl->tail = &((head)->hh); \
+ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
+ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
+ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
+ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl->buckets, 0, \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_MAKE((head)->hh.tbl); \
+ (head)->hh.tbl->signature = HASH_SIGNATURE; \
+} while(0)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
+ HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add)
+
+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
+do { \
+ replaced=NULL; \
+ HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \
+ if (replaced!=NULL) { \
+ HASH_DELETE(hh,head,replaced); \
+ } \
+ HASH_ADD(hh,head,fieldname,keylen_in,add); \
+} while(0)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
+do { \
+ unsigned _ha_bkt; \
+ (add)->hh.next = NULL; \
+ (add)->hh.key = (char*)(keyptr); \
+ (add)->hh.keylen = (unsigned)(keylen_in); \
+ if (!(head)) { \
+ head = (add); \
+ (head)->hh.prev = NULL; \
+ HASH_MAKE_TABLE(hh,head); \
+ } else { \
+ (head)->hh.tbl->tail->next = (add); \
+ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
+ (head)->hh.tbl->tail = &((add)->hh); \
+ } \
+ (head)->hh.tbl->num_items++; \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
+ (add)->hh.hashv, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
+ HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \
+ HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
+ HASH_FSCK(hh,head); \
+} while(0)
+
+#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
+do { \
+ bkt = ((hashv) & ((num_bkts) - 1)); \
+} while(0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ * HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr) \
+do { \
+ struct UT_hash_handle *_hd_hh_del; \
+ if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ head = NULL; \
+ } else { \
+ unsigned _hd_bkt; \
+ _hd_hh_del = &((delptr)->hh); \
+ if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
+ (head)->hh.tbl->tail = \
+ (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho); \
+ } \
+ if ((delptr)->hh.prev) { \
+ ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho))->next = (delptr)->hh.next; \
+ } else { \
+ DECLTYPE_ASSIGN(head,(delptr)->hh.next); \
+ } \
+ if (_hd_hh_del->next) { \
+ ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \
+ (head)->hh.tbl->hho))->prev = \
+ _hd_hh_del->prev; \
+ } \
+ HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
+ (head)->hh.tbl->num_items--; \
+ } \
+ HASH_FSCK(hh,head); \
+} while (0)
+
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out) \
+ HASH_FIND(hh,head,findstr,(unsigned)strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add) \
+ HASH_ADD(hh,head,strfield[0],strlen(add->strfield),add)
+#define HASH_REPLACE_STR(head,strfield,add,replaced) \
+ HASH_REPLACE(hh,head,strfield[0],(unsigned)strlen(add->strfield),add,replaced)
+#define HASH_FIND_INT(head,findint,out) \
+ HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add) \
+ HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_REPLACE_INT(head,intfield,add,replaced) \
+ HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
+#define HASH_FIND_PTR(head,findptr,out) \
+ HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add) \
+ HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \
+ HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
+#define HASH_DEL(head,delptr) \
+ HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head) \
+do { \
+ struct UT_hash_handle *_thh; \
+ if (head) { \
+ unsigned _bkt_i; \
+ unsigned _count; \
+ char *_prev; \
+ _count = 0; \
+ for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
+ unsigned _bkt_count = 0; \
+ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
+ _prev = NULL; \
+ while (_thh) { \
+ if (_prev != (char*)(_thh->hh_prev)) { \
+ HASH_OOPS("invalid hh_prev %p, actual %p\n", \
+ _thh->hh_prev, _prev ); \
+ } \
+ _bkt_count++; \
+ _prev = (char*)(_thh); \
+ _thh = _thh->hh_next; \
+ } \
+ _count += _bkt_count; \
+ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
+ HASH_OOPS("invalid bucket count %u, actual %u\n", \
+ (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
+ } \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid hh item count %u, actual %u\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ /* traverse hh in app order; check next/prev integrity, count */ \
+ _count = 0; \
+ _prev = NULL; \
+ _thh = &(head)->hh; \
+ while (_thh) { \
+ _count++; \
+ if (_prev !=(char*)(_thh->prev)) { \
+ HASH_OOPS("invalid prev %p, actual %p\n", \
+ _thh->prev, _prev ); \
+ } \
+ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
+ _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
+ (head)->hh.tbl->hho) : NULL ); \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid app item count %u, actual %u\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ } \
+} while (0)
+#else
+#define HASH_FSCK(hh,head)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
+do { \
+ unsigned _klen = fieldlen; \
+ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
+ write(HASH_EMIT_KEYS, keyptr, fieldlen); \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
+#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _hb_keylen=keylen; \
+ const char *_hb_key=(const char*)(key); \
+ (hashv) = 0; \
+ while (_hb_keylen--) { (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; } \
+ bkt = (hashv) & (num_bkts-1); \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _sx_i; \
+ const char *_hs_key=(const char*)(key); \
+ hashv = 0; \
+ for(_sx_i=0; _sx_i < keylen; _sx_i++) \
+ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
+ bkt = hashv & (num_bkts-1); \
+} while (0)
+/* FNV-1a variation */
+#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _fn_i; \
+ const char *_hf_key=(const char*)(key); \
+ hashv = 2166136261UL; \
+ for(_fn_i=0; _fn_i < keylen; _fn_i++) { \
+ hashv = hashv ^ _hf_key[_fn_i]; \
+ hashv = hashv * 16777619; \
+ } \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _ho_i; \
+ const char *_ho_key=(const char*)(key); \
+ hashv = 0; \
+ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
+ hashv += _ho_key[_ho_i]; \
+ hashv += (hashv << 10); \
+ hashv ^= (hashv >> 6); \
+ } \
+ hashv += (hashv << 3); \
+ hashv ^= (hashv >> 11); \
+ hashv += (hashv << 15); \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+#define HASH_JEN_MIX(a,b,c) \
+do { \
+ a -= b; a -= c; a ^= ( c >> 13 ); \
+ b -= c; b -= a; b ^= ( a << 8 ); \
+ c -= a; c -= b; c ^= ( b >> 13 ); \
+ a -= b; a -= c; a ^= ( c >> 12 ); \
+ b -= c; b -= a; b ^= ( a << 16 ); \
+ c -= a; c -= b; c ^= ( b >> 5 ); \
+ a -= b; a -= c; a ^= ( c >> 3 ); \
+ b -= c; b -= a; b ^= ( a << 10 ); \
+ c -= a; c -= b; c ^= ( b >> 15 ); \
+} while (0)
+
+#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _hj_i,_hj_j,_hj_k; \
+ unsigned const char *_hj_key=(unsigned const char*)(key); \
+ hashv = 0xfeedbeef; \
+ _hj_i = _hj_j = 0x9e3779b9; \
+ _hj_k = (unsigned)(keylen); \
+ while (_hj_k >= 12) { \
+ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ + ( (unsigned)_hj_key[2] << 16 ) \
+ + ( (unsigned)_hj_key[3] << 24 ) ); \
+ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ + ( (unsigned)_hj_key[6] << 16 ) \
+ + ( (unsigned)_hj_key[7] << 24 ) ); \
+ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ + ( (unsigned)_hj_key[10] << 16 ) \
+ + ( (unsigned)_hj_key[11] << 24 ) ); \
+ \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ \
+ _hj_key += 12; \
+ _hj_k -= 12; \
+ } \
+ hashv += keylen; \
+ switch ( _hj_k ) { \
+ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
+ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
+ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
+ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
+ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
+ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
+ case 5: _hj_j += _hj_key[4]; \
+ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
+ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
+ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
+ case 1: _hj_i += _hj_key[0]; \
+ } \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned const char *_sfh_key=(unsigned const char*)(key); \
+ uint32_t _sfh_tmp, _sfh_len = keylen; \
+ \
+ int _sfh_rem = _sfh_len & 3; \
+ _sfh_len >>= 2; \
+ hashv = 0xcafebabe; \
+ \
+ /* Main loop */ \
+ for (;_sfh_len > 0; _sfh_len--) { \
+ hashv += get16bits (_sfh_key); \
+ _sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \
+ hashv = (hashv << 16) ^ _sfh_tmp; \
+ _sfh_key += 2*sizeof (uint16_t); \
+ hashv += hashv >> 11; \
+ } \
+ \
+ /* Handle end cases */ \
+ switch (_sfh_rem) { \
+ case 3: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 16; \
+ hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \
+ hashv += hashv >> 11; \
+ break; \
+ case 2: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 11; \
+ hashv += hashv >> 17; \
+ break; \
+ case 1: hashv += *_sfh_key; \
+ hashv ^= hashv << 10; \
+ hashv += hashv >> 1; \
+ } \
+ \
+ /* Force "avalanching" of final 127 bits */ \
+ hashv ^= hashv << 3; \
+ hashv += hashv >> 5; \
+ hashv ^= hashv << 4; \
+ hashv += hashv >> 17; \
+ hashv ^= hashv << 25; \
+ hashv += hashv >> 6; \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+#ifdef HASH_USING_NO_STRICT_ALIASING
+/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
+ * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
+ * MurmurHash uses the faster approach only on CPU's where we know it's safe.
+ *
+ * Note the preprocessor built-in defines can be emitted using:
+ *
+ * gcc -m64 -dM -E - < /dev/null (on gcc)
+ * cc -## a.c (where a.c is a simple test file) (Sun Studio)
+ */
+#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
+#define MUR_GETBLOCK(p,i) p[i]
+#else /* non intel */
+#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0)
+#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1)
+#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2)
+#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3)
+#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
+#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
+#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
+#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8))
+#else /* assume little endian non-intel */
+#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
+#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8))
+#endif
+#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \
+ (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
+ (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \
+ MUR_ONE_THREE(p))))
+#endif
+#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
+#define MUR_FMIX(_h) \
+do { \
+ _h ^= _h >> 16; \
+ _h *= 0x85ebca6b; \
+ _h ^= _h >> 13; \
+ _h *= 0xc2b2ae35l; \
+ _h ^= _h >> 16; \
+} while(0)
+
+#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ const uint8_t *_mur_data = (const uint8_t*)(key); \
+ const int _mur_nblocks = (keylen) / 4; \
+ uint32_t _mur_h1 = 0xf88D5353; \
+ uint32_t _mur_c1 = 0xcc9e2d51; \
+ uint32_t _mur_c2 = 0x1b873593; \
+ uint32_t _mur_k1 = 0; \
+ const uint8_t *_mur_tail; \
+ const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \
+ int _mur_i; \
+ for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \
+ _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \
+ _mur_k1 *= _mur_c1; \
+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \
+ _mur_k1 *= _mur_c2; \
+ \
+ _mur_h1 ^= _mur_k1; \
+ _mur_h1 = MUR_ROTL32(_mur_h1,13); \
+ _mur_h1 = _mur_h1*5+0xe6546b64; \
+ } \
+ _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \
+ _mur_k1=0; \
+ switch((keylen) & 3) { \
+ case 3: _mur_k1 ^= _mur_tail[2] << 16; \
+ case 2: _mur_k1 ^= _mur_tail[1] << 8; \
+ case 1: _mur_k1 ^= _mur_tail[0]; \
+ _mur_k1 *= _mur_c1; \
+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \
+ _mur_k1 *= _mur_c2; \
+ _mur_h1 ^= _mur_k1; \
+ } \
+ _mur_h1 ^= (keylen); \
+ MUR_FMIX(_mur_h1); \
+ hashv = _mur_h1; \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+#endif /* HASH_USING_NO_STRICT_ALIASING */
+
+/* key comparison function; return 0 if keys equal */
+#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
+do { \
+ if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \
+ else out=NULL; \
+ while (out) { \
+ if ((out)->hh.keylen == keylen_in) { \
+ if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \
+ } \
+ if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \
+ else out = NULL; \
+ } \
+} while(0)
+
+/* add an item to a bucket */
+#define HASH_ADD_TO_BKT(head,addhh) \
+do { \
+ head.count++; \
+ (addhh)->hh_next = head.hh_head; \
+ (addhh)->hh_prev = NULL; \
+ if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
+ (head).hh_head=addhh; \
+ if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
+ && (addhh)->tbl->noexpand != 1) { \
+ HASH_EXPAND_BUCKETS((addhh)->tbl); \
+ } \
+} while(0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(hh,head,hh_del) \
+ (head).count--; \
+ if ((head).hh_head == hh_del) { \
+ (head).hh_head = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_prev) { \
+ hh_del->hh_prev->hh_next = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_next) { \
+ hh_del->hh_next->hh_prev = hh_del->hh_prev; \
+ }
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ * ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(tbl) \
+do { \
+ unsigned _he_bkt; \
+ unsigned _he_bkt_i; \
+ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
+ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
+ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
+ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
+ memset(_he_new_buckets, 0, \
+ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ tbl->ideal_chain_maxlen = \
+ (tbl->num_items >> (tbl->log2_num_buckets+1)) + \
+ ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
+ tbl->nonideal_items = 0; \
+ for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
+ { \
+ _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
+ while (_he_thh) { \
+ _he_hh_nxt = _he_thh->hh_next; \
+ HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
+ _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
+ if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
+ tbl->nonideal_items++; \
+ _he_newbkt->expand_mult = _he_newbkt->count / \
+ tbl->ideal_chain_maxlen; \
+ } \
+ _he_thh->hh_prev = NULL; \
+ _he_thh->hh_next = _he_newbkt->hh_head; \
+ if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
+ _he_thh; \
+ _he_newbkt->hh_head = _he_thh; \
+ _he_thh = _he_hh_nxt; \
+ } \
+ } \
+ uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+ tbl->num_buckets *= 2; \
+ tbl->log2_num_buckets++; \
+ tbl->buckets = _he_new_buckets; \
+ tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
+ (tbl->ineff_expands+1) : 0; \
+ if (tbl->ineff_expands > 1) { \
+ tbl->noexpand=1; \
+ uthash_noexpand_fyi(tbl); \
+ } \
+ uthash_expand_fyi(tbl); \
+} while(0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn) \
+do { \
+ unsigned _hs_i; \
+ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
+ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
+ if (head) { \
+ _hs_insize = 1; \
+ _hs_looping = 1; \
+ _hs_list = &((head)->hh); \
+ while (_hs_looping) { \
+ _hs_p = _hs_list; \
+ _hs_list = NULL; \
+ _hs_tail = NULL; \
+ _hs_nmerges = 0; \
+ while (_hs_p) { \
+ _hs_nmerges++; \
+ _hs_q = _hs_p; \
+ _hs_psize = 0; \
+ for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
+ _hs_psize++; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ if (! (_hs_q) ) break; \
+ } \
+ _hs_qsize = _hs_insize; \
+ while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
+ if (_hs_psize == 0) { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
+ _hs_e = _hs_p; \
+ if (_hs_p){ \
+ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ } \
+ _hs_psize--; \
+ } else if (( \
+ cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
+ ) <= 0) { \
+ _hs_e = _hs_p; \
+ if (_hs_p){ \
+ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ } \
+ _hs_psize--; \
+ } else { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } \
+ if ( _hs_tail ) { \
+ _hs_tail->next = ((_hs_e) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
+ } else { \
+ _hs_list = _hs_e; \
+ } \
+ if (_hs_e) { \
+ _hs_e->prev = ((_hs_tail) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
+ } \
+ _hs_tail = _hs_e; \
+ } \
+ _hs_p = _hs_q; \
+ } \
+ if (_hs_tail){ \
+ _hs_tail->next = NULL; \
+ } \
+ if ( _hs_nmerges <= 1 ) { \
+ _hs_looping=0; \
+ (head)->hh.tbl->tail = _hs_tail; \
+ DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
+ } \
+ _hs_insize *= 2; \
+ } \
+ HASH_FSCK(hh,head); \
+ } \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
+do { \
+ unsigned _src_bkt, _dst_bkt; \
+ void *_last_elt=NULL, *_elt; \
+ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
+ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
+ if (src) { \
+ for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
+ for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
+ _src_hh; \
+ _src_hh = _src_hh->hh_next) { \
+ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
+ if (cond(_elt)) { \
+ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
+ _dst_hh->key = _src_hh->key; \
+ _dst_hh->keylen = _src_hh->keylen; \
+ _dst_hh->hashv = _src_hh->hashv; \
+ _dst_hh->prev = _last_elt; \
+ _dst_hh->next = NULL; \
+ if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
+ if (!dst) { \
+ DECLTYPE_ASSIGN(dst,_elt); \
+ HASH_MAKE_TABLE(hh_dst,dst); \
+ } else { \
+ _dst_hh->tbl = (dst)->hh_dst.tbl; \
+ } \
+ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
+ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
+ (dst)->hh_dst.tbl->num_items++; \
+ _last_elt = _elt; \
+ _last_elt_hh = _dst_hh; \
+ } \
+ } \
+ } \
+ } \
+ HASH_FSCK(hh_dst,dst); \
+} while (0)
+
+#define HASH_CLEAR(hh,head) \
+do { \
+ if (head) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head)=NULL; \
+ } \
+} while(0)
+
+#define HASH_OVERHEAD(hh,head) \
+ ((head) ? ( \
+ (size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \
+ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \
+ (sizeof(UT_hash_table)) + \
+ (HASH_BLOOM_BYTELEN)))) : 0)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp) \
+for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \
+ el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL))
+#else
+#define HASH_ITER(hh,head,el,tmp) \
+for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \
+ el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0)
+
+typedef struct UT_hash_bucket {
+ struct UT_hash_handle *hh_head;
+ unsigned count;
+
+ /* expand_mult is normally set to 0. In this situation, the max chain length
+ * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+ * the bucket's chain exceeds this length, bucket expansion is triggered).
+ * However, setting expand_mult to a non-zero value delays bucket expansion
+ * (that would be triggered by additions to this particular bucket)
+ * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+ * (The multiplier is simply expand_mult+1). The whole idea of this
+ * multiplier is to reduce bucket expansions, since they are expensive, in
+ * situations where we know that a particular bucket tends to be overused.
+ * It is better to let its chain length grow to a longer yet-still-bounded
+ * value, than to do an O(n) bucket expansion too often.
+ */
+ unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1
+#define HASH_BLOOM_SIGNATURE 0xb12220f2
+
+typedef struct UT_hash_table {
+ UT_hash_bucket *buckets;
+ unsigned num_buckets, log2_num_buckets;
+ unsigned num_items;
+ struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
+ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+ /* in an ideal situation (all buckets used equally), no bucket would have
+ * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+ unsigned ideal_chain_maxlen;
+
+ /* nonideal_items is the number of items in the hash whose chain position
+ * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+ * hash distribution; reaching them in a chain traversal takes >ideal steps */
+ unsigned nonideal_items;
+
+ /* ineffective expands occur when a bucket doubling was performed, but
+ * afterward, more than half the items in the hash had nonideal chain
+ * positions. If this happens on two consecutive expansions we inhibit any
+ * further expansion, as it's not helping; this happens when the hash
+ * function isn't a good fit for the key domain. When expansion is inhibited
+ * the hash will still work, albeit no longer in constant time. */
+ unsigned ineff_expands, noexpand;
+
+ uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+ uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+ uint8_t *bloom_bv;
+ char bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+ struct UT_hash_table *tbl;
+ void *prev; /* prev element in app order */
+ void *next; /* next element in app order */
+ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
+ struct UT_hash_handle *hh_next; /* next hh in bucket order */
+ void *key; /* ptr to enclosing struct's key */
+ unsigned keylen; /* enclosing struct's key len */
+ unsigned hashv; /* result of hash-fcn(key) */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
diff --git a/app/wlib/gtklib/wpref.c b/app/wlib/gtklib/wpref.c
new file mode 100644
index 0000000..b79c8d2
--- /dev/null
+++ b/app/wlib/gtklib/wpref.c
@@ -0,0 +1,502 @@
+/** \file wpref.c Handle loading and saving preferences.
+ *
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/gtklib/wpref.c,v 1.15 2010-04-28 04:04:38 dspagnol Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+#include "wlib.h"
+#include "dynarr.h"
+#include "i18n.h"
+
+#ifndef TRUE
+#define TRUE (1)
+#define FALSE (0)
+#endif
+
+#ifdef XTRKCAD_CMAKE_BUILD
+#include "xtrkcad-config.h"
+#endif
+
+extern char wAppName[];
+extern char wConfigName[];
+static char appLibDir[BUFSIZ];
+static char appWorkDir[BUFSIZ];
+static char userHomeDir[BUFSIZ];
+
+EXPORT void wInitAppName(char *appName)
+{
+ strcpy(wAppName, appName);
+}
+
+/*
+ *******************************************************************************
+ *
+ * Get Dir Names
+ *
+ *******************************************************************************
+ */
+
+
+EXPORT const char * wGetAppLibDir( void )
+/** Find the directory where configuration files, help, demos etc are installed.
+ * The search order is:
+ * 1. Directory specified by the XTRKCADLIB environment variable
+ * 2. Directory specified by XTRKCAD_INSTALL_PREFIX/share/xtrkcad
+ * 3. /usr/lib/xtrkcad
+ * 4. /usr/local/lib/xtrkcad
+ *
+ * \return pointer to directory name
+ */
+{
+ char * cp, *ep;
+ char msg[BUFSIZ*2];
+ char envvar[80];
+ struct stat buf;
+
+ if (appLibDir[0] != '\0') {
+ return appLibDir;
+ }
+
+ for (cp=wAppName,ep=envvar; *cp; cp++,ep++)
+ *ep = toupper(*cp);
+ strcpy( ep, "LIB" );
+ ep = getenv( envvar );
+ if (ep != NULL) {
+ if ((stat( ep, &buf) == 0 ) && S_ISDIR( buf.st_mode)) {
+ strncpy( appLibDir, ep, sizeof appLibDir );
+ return appLibDir;
+ }
+ }
+
+#ifdef XTRKCAD_CMAKE_BUILD
+ strcpy(appLibDir, XTRKCAD_INSTALL_PREFIX);
+ strcat(appLibDir, "/share/");
+ strcat(appLibDir, wAppName);
+
+ if ((stat( appLibDir, &buf) == 0 ) && S_ISDIR( buf.st_mode)) {
+ return appLibDir;
+ }
+#endif
+
+ strcpy( appLibDir, "/usr/lib/" );
+ strcat( appLibDir, wAppName );
+ if ((stat( appLibDir, &buf) == 0 ) && S_ISDIR( buf.st_mode)) {
+ return appLibDir;
+ }
+
+ strcpy( appLibDir, "/usr/local/lib/" );
+ strcat( appLibDir, wAppName );
+ if ((stat( appLibDir, &buf) == 0 ) && S_ISDIR( buf.st_mode)) {
+ return appLibDir;
+ }
+
+ sprintf( msg,
+ _("The required configuration files could not be located in the expected location.\n\n"
+ "Usually this is an installation problem. Make sure that these files are installed in either \n"
+ " %s/share/xtrkcad or\n"
+ " /usr/lib/%s or\n"
+ " /usr/local/lib/%s\n"
+ "If this is not possible, the environment variable %s must contain "
+ "the name of the correct directory."),
+ XTRKCAD_INSTALL_PREFIX, wAppName, wAppName, envvar );
+ wNoticeEx( NT_ERROR, msg, _("Ok"), NULL );
+ appLibDir[0] = '\0';
+ wExit(0);
+ return NULL;
+}
+
+/**
+ * Get the working directory for the application. This directory is used for storing
+ * internal files including rc files. If it doesn't exist, the directory is created
+ * silently.
+ *
+ * \return pointer to the working directory
+ */
+
+
+EXPORT const char * wGetAppWorkDir(
+ void )
+{
+ char tmp[BUFSIZ+20];
+ char * homeDir;
+ DIR *dirp;
+
+ if (appWorkDir[0] != '\0')
+ return appWorkDir;
+
+ if ((homeDir = getenv( "HOME" )) == NULL) {
+ wNoticeEx( NT_ERROR, _("HOME is not set"), _("Exit"), NULL);
+ wExit(0);
+ }
+ sprintf( appWorkDir, "%s/.%s", homeDir, wAppName );
+ if ( (dirp = opendir(appWorkDir)) != NULL ) {
+ closedir(dirp);
+ } else {
+ if ( mkdir( appWorkDir, 0777 ) == -1 ) {
+ sprintf( tmp, _("Cannot create %s"), appWorkDir );
+ wNoticeEx( NT_ERROR, tmp, _("Exit"), NULL );
+ wExit(0);
+ } else {
+ /*
+ * check for default configuration file and copy to
+ * the workdir if it exists
+ */
+ struct stat stFileInfo;
+ char appEtcConfig[BUFSIZ];
+ sprintf( appEtcConfig, "/etc/%s.rc", wAppName );
+
+ if ( stat( appEtcConfig, &stFileInfo ) == 0 ) {
+ char copyConfigCmd[(BUFSIZ * 2) + 3];
+ sprintf( copyConfigCmd, "cp %s %s", appEtcConfig, appWorkDir );
+ system( copyConfigCmd );
+ }
+ }
+ }
+ return appWorkDir;
+}
+
+/**
+ * Get the user's home directory. The environment variable HOME is
+ * assumed to contain the proper directory.
+ *
+ * \return pointer to the user's home directory
+ */
+
+EXPORT const char *wGetUserHomeDir( void )
+{
+ char *homeDir;
+
+ if( userHomeDir[ 0 ] != '\0' )
+ return userHomeDir;
+
+ if ((homeDir = getenv( "HOME" )) == NULL) {
+ wNoticeEx( NT_ERROR, _("HOME is not set"), _("Exit"), NULL);
+ wExit(0);
+ } else {
+ strcpy( userHomeDir, homeDir );
+ }
+
+ return userHomeDir;
+}
+
+
+/*
+ *******************************************************************************
+ *
+ * Preferences
+ *
+ *******************************************************************************
+ */
+
+typedef struct {
+ char * section;
+ char * name;
+ wBool_t present;
+ wBool_t dirty;
+ char * val;
+ } prefs_t;
+dynArr_t prefs_da;
+#define prefs(N) DYNARR_N(prefs_t,prefs_da,N)
+wBool_t prefInitted = FALSE;
+
+static void readPrefs( void )
+{
+ char tmp[BUFSIZ], *sp, *np, *vp, *cp;
+ const char * workDir;
+ FILE * prefFile;
+ prefs_t * p;
+
+ prefInitted = TRUE;
+ workDir = wGetAppWorkDir();
+ sprintf( tmp, "%s/%s.rc", workDir, wConfigName );
+ prefFile = fopen( tmp, "r" );
+ if (prefFile == NULL)
+ return;
+ while ( ( fgets(tmp, sizeof tmp, prefFile) ) != NULL ) {
+ sp = tmp;
+ while ( *sp==' ' || *sp=='\t' ) sp++;
+ if ( *sp == '\n' || *sp == '#' )
+ continue;
+ np = strchr( sp, '.' );
+ if (np == NULL) {
+ wNoticeEx( NT_INFORMATION, tmp, _("Continue"), NULL );
+ continue;
+ }
+ *np++ = '\0';
+ while ( *np==' ' || *np=='\t' ) np++;
+ vp = strchr( np, ':' );
+ if (vp == NULL) {
+ wNoticeEx( NT_INFORMATION, tmp, _("Continue"), NULL );
+ continue;
+ }
+ *vp++ = '\0';
+ while ( *vp==' ' || *vp=='\t' ) vp++;
+ cp = vp + strlen(vp) -1;
+ while ( cp >= vp && (*cp=='\n' || *cp==' ' || *cp=='\t') ) cp--;
+ cp[1] = '\0';
+ DYNARR_APPEND( prefs_t, prefs_da, 10 );
+ p = &prefs(prefs_da.cnt-1);
+ p->name = strdup(np);
+ p->section = strdup(sp);
+ p->dirty = FALSE;
+ p->val = strdup(vp);
+ }
+ fclose( prefFile );
+}
+
+/**
+ * Store a string in the user preferences.
+ *
+ * \param section IN section in preferences file
+ * \param name IN name of parameter
+ * \param sval IN value to save
+ */
+
+EXPORT void wPrefSetString(
+ const char * section, /* Section */
+ const char * name, /* Name */
+ const char * sval ) /* Value */
+{
+ prefs_t * p;
+
+ if (!prefInitted)
+ readPrefs();
+
+ for (p=&prefs(0); p<&prefs(prefs_da.cnt); p++) {
+ if ( strcmp( p->section, section ) == 0 && strcmp( p->name, name ) == 0 ) {
+ if (p->val)
+ free(p->val);
+ p->dirty = TRUE;
+ p->val = strdup( sval );
+ return;
+ }
+ }
+ DYNARR_APPEND( prefs_t, prefs_da, 10 );
+ p = &prefs(prefs_da.cnt-1);
+ p->name = strdup(name);
+ p->section = strdup(section);
+ p->dirty = TRUE;
+ p->val = strdup(sval);
+}
+
+
+EXPORT const char * wPrefGetString(
+ const char * section, /* Section */
+ const char * name ) /* Name */
+/*
+*/
+{
+ prefs_t * p;
+
+ if (!prefInitted)
+ readPrefs();
+
+ for (p=&prefs(0); p<&prefs(prefs_da.cnt); p++) {
+ if ( strcmp( p->section, section ) == 0 && strcmp( p->name, name ) == 0 ) {
+ return p->val;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Store an integer value in the user preferences.
+ *
+ * \param section IN section in preferences file
+ * \param name IN name of parameter
+ * \param lval IN value to save
+ */
+
+EXPORT void wPrefSetInteger(
+ const char * section, /* Section */
+ const char * name, /* Name */
+ long lval ) /* Value */
+{
+ char tmp[20];
+
+ sprintf(tmp, "%ld", lval );
+ wPrefSetString( section, name, tmp );
+}
+
+/**
+ * Read an integer value from the user preferences.
+ *
+ * \param section IN section in preferences file
+ * \param name IN name of parameter
+ * \param res OUT resulting value
+ * \param default IN default value
+ * \return TRUE if value differs from default, FALSE if the same
+ */
+
+EXPORT wBool_t wPrefGetInteger(
+ const char * section, /* Section */
+ const char * name, /* Name */
+ long * res, /* Address of result */
+ long def ) /* Default value */
+{
+ const char * cp;
+ char *cp1;
+
+ cp = wPrefGetString( section, name );
+ if (cp == NULL) {
+ *res = def;
+ return FALSE;
+ }
+ *res = strtol(cp,&cp1,0);
+ if (cp==cp1) {
+ *res = def;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Save a float value in the preferences file.
+ *
+ * \param section IN the file section into which the value should be saved
+ * \param name IN the name of the preference
+ * \param lval IN the value
+ */
+
+EXPORT void wPrefSetFloat(
+ const char * section, /* Section */
+ const char * name, /* Name */
+ double lval ) /* Value */
+{
+ char tmp[20];
+
+ sprintf(tmp, "%0.6f", lval );
+ wPrefSetString( section, name, tmp );
+}
+
+/**
+ * Read a float from the preferencesd file.
+ *
+ * \param section IN the file section from which the value should be read
+ * \param name IN the name of the preference
+ * \param res OUT pointer for the value
+ * \param def IN default value
+ * \return TRUE if value was read, FALSE if default value is used
+ */
+
+
+EXPORT wBool_t wPrefGetFloat(
+ const char * section, /* Section */
+ const char * name, /* Name */
+ double * res, /* Address of result */
+ double def ) /* Default value */
+{
+ const char * cp;
+ char *cp1;
+
+ cp = wPrefGetString( section, name );
+ if (cp == NULL) {
+ *res = def;
+ return FALSE;
+ }
+ *res = strtod(cp, &cp1);
+ if (cp == cp1) {
+ *res = def;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+EXPORT const char * wPrefGetSectionItem(
+ const char * sectionName,
+ wIndex_t * index,
+ const char ** name )
+{
+ prefs_t * p;
+
+ if (!prefInitted)
+ readPrefs();
+
+ if ( *index >= prefs_da.cnt )
+ return NULL;
+
+ for (p=&prefs((*index)++); p<&prefs(prefs_da.cnt); p++,(*index)++) {
+ if ( strcmp( p->section, sectionName ) == 0 ) {
+ if ( name )
+ *name = p->name;
+ return p->val;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Save the configuration to a file. The config parameters are held and updated in an array.
+ * To make the settings persistant, this function has to be called.
+ *
+ */
+
+EXPORT void wPrefFlush(
+ void )
+/*
+*/
+{
+ prefs_t * p;
+ char tmp[BUFSIZ];
+ const char *workDir;
+ FILE * prefFile;
+
+ if (!prefInitted)
+ return;
+
+ workDir = wGetAppWorkDir();
+ sprintf( tmp, "%s/%s.rc", workDir, wConfigName );
+ prefFile = fopen( tmp, "w" );
+ if (prefFile == NULL)
+ return;
+
+ for (p=&prefs(0); p<&prefs(prefs_da.cnt); p++) {
+ fprintf( prefFile, "%s.%s: %s\n", p->section, p->name, p->val );
+ }
+ fclose( prefFile );
+}
+
+
+EXPORT void wPrefReset(
+ void )
+/*
+*/
+{
+ prefs_t * p;
+
+ prefInitted = FALSE;
+ for (p=&prefs(0); p<&prefs(prefs_da.cnt); p++) {
+ if (p->section)
+ free( p->section );
+ if (p->name)
+ free( p->name );
+ if (p->val)
+ free( p->val );
+ }
+ prefs_da.cnt = 0;
+}