From 7b358424ebad9349421acd533c2fa1cbf6cf3e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 28 Dec 2016 16:52:56 +0100 Subject: Initial import of xtrkcad version 1:4.0.2-2 --- app/wlib/CMakeLists.txt | 10 + app/wlib/gtklib/.directory | 7 + app/wlib/gtklib/CMakeLists.txt | 37 + app/wlib/gtklib/ChangeLog | 250 ++++ app/wlib/gtklib/dynarr.h | 49 + app/wlib/gtklib/filesel.c | 174 +++ app/wlib/gtklib/gtkbitmap.c | 85 ++ app/wlib/gtklib/gtkbutton.c | 461 +++++++ app/wlib/gtklib/gtkcolor.c | 476 +++++++ app/wlib/gtklib/gtkdraw-cairo.c | 1212 +++++++++++++++++ app/wlib/gtklib/gtkdraw.c | 1044 ++++++++++++++ app/wlib/gtklib/gtkfont.c | 320 +++++ app/wlib/gtklib/gtkhelp.c | 733 ++++++++++ app/wlib/gtklib/gtkint.h | 180 +++ app/wlib/gtklib/gtklist.c | 1109 +++++++++++++++ app/wlib/gtklib/gtkmenu.c | 873 ++++++++++++ app/wlib/gtklib/gtkmisc.c | 1210 +++++++++++++++++ app/wlib/gtklib/gtksimple.c | 366 +++++ app/wlib/gtklib/gtksingle.c | 645 +++++++++ app/wlib/gtklib/gtksplash.c | 142 ++ app/wlib/gtklib/gtktext.c | 524 +++++++ app/wlib/gtklib/gtkwindow.c | 856 ++++++++++++ app/wlib/gtklib/gtkxpm.c | 177 +++ app/wlib/gtklib/psprint.c | 1599 ++++++++++++++++++++++ app/wlib/gtklib/square10.bmp | 8 + app/wlib/gtklib/uthash.h | 960 +++++++++++++ app/wlib/gtklib/wpref.c | 502 +++++++ app/wlib/include/ctl3d.h | 5 + app/wlib/include/getopt.h | 25 + app/wlib/include/mswlib.h | 9 + app/wlib/include/mswlib.rc | 13 + app/wlib/include/wcolors.h | 42 + app/wlib/include/wlib.h | 690 ++++++++++ app/wlib/mswlib/CMakeLists.txt | 39 + app/wlib/mswlib/ChangeLog | 146 ++ app/wlib/mswlib/checksum.c | 42 + app/wlib/mswlib/dynarr.h | 40 + app/wlib/mswlib/getopt.c | 87 ++ app/wlib/mswlib/gwin32.c | 146 ++ app/wlib/mswlib/mswbitmap.c | 508 +++++++ app/wlib/mswlib/mswbox.c | 119 ++ app/wlib/mswlib/mswbutt.c | 387 ++++++ app/wlib/mswlib/mswchksm.c | 125 ++ app/wlib/mswlib/mswchoic.c | 423 ++++++ app/wlib/mswlib/mswcolor.c | 362 +++++ app/wlib/mswlib/mswdraw.c | 1783 ++++++++++++++++++++++++ app/wlib/mswlib/mswedit.c | 726 ++++++++++ app/wlib/mswlib/mswint.h | 193 +++ app/wlib/mswlib/mswlines.c | 98 ++ app/wlib/mswlib/mswlist.c | 1173 ++++++++++++++++ app/wlib/mswlib/mswmenu.c | 1062 +++++++++++++++ app/wlib/mswlib/mswmisc.c | 2778 ++++++++++++++++++++++++++++++++++++++ app/wlib/mswlib/mswmsg.c | 212 +++ app/wlib/mswlib/mswpref.c | 274 ++++ app/wlib/mswlib/mswprint.c | 387 ++++++ app/wlib/mswlib/mswsplash.c | 266 ++++ app/wlib/mswlib/mswtext.c | 383 ++++++ app/wlib/mswlib/simple-gettext.c | 522 +++++++ app/wlib/mswlib/square10.bmp | 6 + app/wlib/test/alarmtst.c | 59 + app/wlib/test/alarmtst.mak | 2 + app/wlib/test/bits.bmp | 6 + app/wlib/test/boxtest.c | 31 + app/wlib/test/boxtest.mak | 2 + app/wlib/test/btest.c | 212 +++ app/wlib/test/btest.mak | 2 + app/wlib/test/colortst.c | 87 ++ app/wlib/test/colortst.mak | 2 + app/wlib/test/draw.c | 82 ++ app/wlib/test/draw.mak | 2 + app/wlib/test/dtest.bmp | 18 + app/wlib/test/fred.bmp | 9 + app/wlib/test/fred.c | 169 +++ app/wlib/test/fred.mak | 2 + app/wlib/test/listtest.c | 96 ++ app/wlib/test/listtest.mak | 2 + app/wlib/test/splash.rc | 17 + app/wlib/test/test.vcproj | 202 +++ app/wlib/test/testapp.c | 125 ++ app/wlib/test/wtest.def | 9 + app/wlib/test/wtest.ico | Bin 0 -> 1278 bytes app/wlib/test/wtest.mak | 23 + app/wlib/test/wtest.rc | 22 + 83 files changed, 28261 insertions(+) create mode 100644 app/wlib/CMakeLists.txt create mode 100644 app/wlib/gtklib/.directory create mode 100644 app/wlib/gtklib/CMakeLists.txt create mode 100644 app/wlib/gtklib/ChangeLog create mode 100644 app/wlib/gtklib/dynarr.h create mode 100644 app/wlib/gtklib/filesel.c create mode 100644 app/wlib/gtklib/gtkbitmap.c create mode 100644 app/wlib/gtklib/gtkbutton.c create mode 100644 app/wlib/gtklib/gtkcolor.c create mode 100644 app/wlib/gtklib/gtkdraw-cairo.c create mode 100644 app/wlib/gtklib/gtkdraw.c create mode 100644 app/wlib/gtklib/gtkfont.c create mode 100644 app/wlib/gtklib/gtkhelp.c create mode 100644 app/wlib/gtklib/gtkint.h create mode 100644 app/wlib/gtklib/gtklist.c create mode 100644 app/wlib/gtklib/gtkmenu.c create mode 100644 app/wlib/gtklib/gtkmisc.c create mode 100644 app/wlib/gtklib/gtksimple.c create mode 100644 app/wlib/gtklib/gtksingle.c create mode 100644 app/wlib/gtklib/gtksplash.c create mode 100644 app/wlib/gtklib/gtktext.c create mode 100644 app/wlib/gtklib/gtkwindow.c create mode 100644 app/wlib/gtklib/gtkxpm.c create mode 100644 app/wlib/gtklib/psprint.c create mode 100644 app/wlib/gtklib/square10.bmp create mode 100644 app/wlib/gtklib/uthash.h create mode 100644 app/wlib/gtklib/wpref.c create mode 100644 app/wlib/include/ctl3d.h create mode 100644 app/wlib/include/getopt.h create mode 100644 app/wlib/include/mswlib.h create mode 100644 app/wlib/include/mswlib.rc create mode 100644 app/wlib/include/wcolors.h create mode 100644 app/wlib/include/wlib.h create mode 100644 app/wlib/mswlib/CMakeLists.txt create mode 100644 app/wlib/mswlib/ChangeLog create mode 100644 app/wlib/mswlib/checksum.c create mode 100644 app/wlib/mswlib/dynarr.h create mode 100644 app/wlib/mswlib/getopt.c create mode 100644 app/wlib/mswlib/gwin32.c create mode 100644 app/wlib/mswlib/mswbitmap.c create mode 100644 app/wlib/mswlib/mswbox.c create mode 100644 app/wlib/mswlib/mswbutt.c create mode 100644 app/wlib/mswlib/mswchksm.c create mode 100644 app/wlib/mswlib/mswchoic.c create mode 100644 app/wlib/mswlib/mswcolor.c create mode 100644 app/wlib/mswlib/mswdraw.c create mode 100644 app/wlib/mswlib/mswedit.c create mode 100644 app/wlib/mswlib/mswint.h create mode 100644 app/wlib/mswlib/mswlines.c create mode 100644 app/wlib/mswlib/mswlist.c create mode 100644 app/wlib/mswlib/mswmenu.c create mode 100644 app/wlib/mswlib/mswmisc.c create mode 100644 app/wlib/mswlib/mswmsg.c create mode 100644 app/wlib/mswlib/mswpref.c create mode 100644 app/wlib/mswlib/mswprint.c create mode 100644 app/wlib/mswlib/mswsplash.c create mode 100644 app/wlib/mswlib/mswtext.c create mode 100644 app/wlib/mswlib/simple-gettext.c create mode 100644 app/wlib/mswlib/square10.bmp create mode 100644 app/wlib/test/alarmtst.c create mode 100644 app/wlib/test/alarmtst.mak create mode 100644 app/wlib/test/bits.bmp create mode 100644 app/wlib/test/boxtest.c create mode 100644 app/wlib/test/boxtest.mak create mode 100644 app/wlib/test/btest.c create mode 100644 app/wlib/test/btest.mak create mode 100644 app/wlib/test/colortst.c create mode 100644 app/wlib/test/colortst.mak create mode 100644 app/wlib/test/draw.c create mode 100644 app/wlib/test/draw.mak create mode 100644 app/wlib/test/dtest.bmp create mode 100644 app/wlib/test/fred.bmp create mode 100644 app/wlib/test/fred.c create mode 100644 app/wlib/test/fred.mak create mode 100644 app/wlib/test/listtest.c create mode 100644 app/wlib/test/listtest.mak create mode 100644 app/wlib/test/splash.rc create mode 100644 app/wlib/test/test.vcproj create mode 100644 app/wlib/test/testapp.c create mode 100644 app/wlib/test/wtest.def create mode 100644 app/wlib/test/wtest.ico create mode 100644 app/wlib/test/wtest.mak create mode 100644 app/wlib/test/wtest.rc (limited to 'app/wlib') diff --git a/app/wlib/CMakeLists.txt b/app/wlib/CMakeLists.txt new file mode 100644 index 0000000..4fa7469 --- /dev/null +++ b/app/wlib/CMakeLists.txt @@ -0,0 +1,10 @@ +PROJECT(wlib) + +INCLUDE_DIRECTORIES("${wlib_SOURCE_DIR}/include") + +IF(XTRKCAD_USE_GTK) + ADD_SUBDIRECTORY(gtklib) +ELSE(XTRKCAD_USE_GTK) + ADD_SUBDIRECTORY(mswlib) +ENDIF(XTRKCAD_USE_GTK) + 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 + 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 + wpref.c: Create directory .xtrkcad silently + +Jun 24, 2009 + ENH: Martin Fischer + gtkwindow.c gtkmisc.c gtkint.h wpref.c: add option + to select configuration file + +Version 4.0.3a +============== + + +Jun 09, 2009 + FIX: Martin Fischer + gtkdraw.c: Fix compiler warning +May 31, 2009 + FIX: Martin Fischer + gtksplash.c: popups during startup are now shown above + splash screen + +May 31, 2009 + FIX: Martin Fischer + gtkmisc.c: fixed problem with some icons + +May 30, 2009 + FIX: Martin Fischer + gtklist.c: fixed the 'missing scrollbar' bug - finally! + +May 29, 2008 + FIX: Martin Fischer + gtkmisc.c: bug fix in wNoticeEx + +May 21, 2009 + ENH: Martin Fischer + gtkhelp.c: better error message + +May 15, 2009 + ENH: Martin Fischer + gtkwindow.c, gtkmisc.c, psprint.c, gtktext.c + gtkfilsel.c, gtksingle.c: new message box with icon + +Jul 11, 2008 + ENH: Martin Fischer + 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 + gtksplash.c: added #ifdef's for backward compatibility to GTK 2.4 + +Feb 01. 2008 + ENH: Martin Fischer + 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 + gtkfilsel.c: Gettext support added. + +Jan 28, 2008 + FIX: Mikko Nissinen + gtkwindow.c: Dynamically allocate and form some global translatable + strings. + +Jan 27, 2008 + FIX: Mikko Nissinen + gtkhelp.c: String XTrkCad changed to XTrackCAD. + +Jan, 27, 2008 + FIX: Mikko Nissinen + gtkwindows.c: fixed problem with missing scroll bars + +Jan 24,2008 + IMPROVMENT: Martin Fischer + wpref.c: increase floting point precision when storing floats in rc + file + +Jan 22, 2008 + ENH: Mikko Nissinen + 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 + 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 + gtkmisc.c: gtkConvertInput(): If the input string is already + UTF-8 encoded, then return it untouched. + +Jan 15, 2008 + IMPROVEMENT: Mikko Nissinen + 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 + gtkhelp.c: an existing help window is now brought to the foreground + if the user selects Help + +Nov 12, 2007 + FIX: Mikko Nissinen + 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 + 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 + gtkmenu.c: Help drop-down is no longer right aligned. + +Sep 28, 2007 + IMPROVEMENT: Martin Fischer + 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 + gtksplash.c: added splash window for program startup + +Jul 24, 2007 + IMPROVEMENT: Martin Fischer + gtkdraw.c: added support for wheel mouse + +Jun 16, 2007 + IMPROVEMENT: Martin Fischer + wpref.c: added wGetUserHomeDir() + +Feb 25, 2007 + BUGFIX: Martin Fischer + wpref.c: Rephrased error message for lib-directory not found + +Feb 23, 2007 + BUGFIX: Martin Fischer + gtkfont.c: Typo in window title corrected + +Version 4.0.1 +============= + +Mar, 30th 2006 + BUGFIX: Martin Fischer + gtkmisc.c: changed wPause to use SYSV signal handling funtions + +Mar, 29th 2006 + BUGFIX: Martin Fischer + gtkmisc.c, gtkbutton.c, gtksimple.c: small changes to help Solaris port + + IMPROVEMENT: Martin Fischer + wpref.c: Optimized the checking for directories in wGetAppLibDir and + rephrased the error message if initialization files cannot be found + + BUGFIX: Martin Fischer + 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 +#include +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include + +#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 +#include +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include +#include +#include + +#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 +#include +#include +#include + +#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<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<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 +#include +#include +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include + +#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; gintlen; 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; gintlen; 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; gintlen; 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 +#include +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include +#include + +#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 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; iw; i++ ) + for ( j=0; jh; 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?"":"<>" ); + 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 +#include +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include +#include +#include + +#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 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; iw; i++ ) + for ( j=0; jh; 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?"":"<>" ); + 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 +#include +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#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 "" \ + "Help Error

Error - help information can not be found.

" \ + "The help information you requested cannot be found on this system.

%s: %s

" \ + "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.

" + + +#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 . +*/ +{ + 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 to . +*/ +{ + 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 . + * + * \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 +#include +#include + +#include +#include +#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 . +*/ +{ + 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 th entry (0-origin) the current selection. +If 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 && collist==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; inxcount; 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 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; inxcount; 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; colcolCnt; 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; inxcolCnt ) { + 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 with 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; collist), 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 +#include +#include +#include +#include +#include +#include + +#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; imlist->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; icount; 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; icount; 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; icount; 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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; rowh; row++ ) { + pixmapData[row+3] = (char*)malloc( (ip->w+1) * sizeof **pixmapData ); + for (col = 0; colw; 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; rowh; 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 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 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 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 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