diff options
Diffstat (limited to 'app/wlib/gtklib')
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 = >k_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 = ¬iceW; + + 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; +} |