diff options
Diffstat (limited to 'app/bin')
258 files changed, 71340 insertions, 0 deletions
diff --git a/app/bin/CMakeLists.txt b/app/bin/CMakeLists.txt new file mode 100644 index 0000000..59aa496 --- /dev/null +++ b/app/bin/CMakeLists.txt @@ -0,0 +1,181 @@ +ADD_EXECUTABLE(cnvdsgn cnvdsgn.c utility.c) +GET_TARGET_PROPERTY(cnvdsgn_EXE cnvdsgn LOCATION) +IF(NOT WIN32) + TARGET_LINK_LIBRARIES(cnvdsgn m) +ENDIF(NOT WIN32) + +MACRO(GENERATE_LIN lin_name) + ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${lin_name}.lin + DEPENDS cnvdsgn ${CMAKE_CURRENT_SOURCE_DIR}/${lin_name}.src + COMMAND ${cnvdsgn_EXE} < ${CMAKE_CURRENT_SOURCE_DIR}/${lin_name}.src > ${CMAKE_CURRENT_BINARY_DIR}/${lin_name}.lin + ) +ENDMACRO(GENERATE_LIN) + +GENERATE_LIN(to3way) +GENERATE_LIN(tocrv) +GENERATE_LIN(tocrvsct) +GENERATE_LIN(todcross) +GENERATE_LIN(todslip) +GENERATE_LIN(tolcross) +GENERATE_LIN(torcross) +GENERATE_LIN(toreg) +GENERATE_LIN(tosslip) +GENERATE_LIN(tostrsct) +GENERATE_LIN(towye) +GENERATE_LIN(toxing) + +SET(LIN_SOURCES + ${CMAKE_CURRENT_BINARY_DIR}/to3way.lin + ${CMAKE_CURRENT_BINARY_DIR}/tocrv.lin + ${CMAKE_CURRENT_BINARY_DIR}/tocrvsct.lin + ${CMAKE_CURRENT_BINARY_DIR}/todcross.lin + ${CMAKE_CURRENT_BINARY_DIR}/todslip.lin + ${CMAKE_CURRENT_BINARY_DIR}/tolcross.lin + ${CMAKE_CURRENT_BINARY_DIR}/torcross.lin + ${CMAKE_CURRENT_BINARY_DIR}/toreg.lin + ${CMAKE_CURRENT_BINARY_DIR}/tosslip.lin + ${CMAKE_CURRENT_BINARY_DIR}/tostrsct.lin + ${CMAKE_CURRENT_BINARY_DIR}/towye.lin + ${CMAKE_CURRENT_BINARY_DIR}/toxing.lin + ) + +GET_TARGET_PROPERTY(genhelp_EXE genhelp LOCATION) + +IF(XTRKCAD_USE_GETTEXT) + SET(GENHELP_OPTS "-bhi") + # + # Find the GnuWin32 installation directory, the gettext include should be located in subdir include + # + IF(WIN32) + ADD_DEFINITIONS(-DUSE_SIMPLE_GETTEXT ) + ENDIF(WIN32) +ELSE(XTRKCAD_USE_GETTEXT) + SET(GENHELP_OPTS "-bh") +ENDIF(XTRKCAD_USE_GETTEXT) + +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bllnhlp.c + DEPENDS genhelp ${help_SOURCE_DIR}/genhelp.in + COMMAND ${genhelp_EXE} ${GENHELP_OPTS} ${help_SOURCE_DIR}/genhelp.in ${CMAKE_CURRENT_BINARY_DIR}/bllnhlp.c + ) + +SET(SOURCES + ${LIN_SOURCES} + bllnhlp.c + ccurve.c + cdraw.c + celev.c + cgroup.c + chndldto.c + chotbar.c + cjoin.c + cmisc.c + cmodify.c + cnote.c + compound.c + cparalle.c + cprint.c + cprofile.c + cpull.c + cruler.c + cselect.c + csnap.c + csplit.c + cstraigh.c + cstruct.c + ctext.c + ctodesgn.c + ctrain.c + cturnout.c + cturntbl.c + cundo.c + custom.c + dbench.c + dbitmap.c + dcar.c + dcmpnd.c + dcustmgm.c + dease.c + denum.c + dlayer.c + doption.c + dpricels.c + dprmfile.c + draw.c + drawgeom.c + elev.c + fileio.c + i18n.c + lprintf.c + macro.c + misc2.c + param.c + shrtpath.c + smalldlg.c + tcurve.c + tease.c + track.c + trkseg.c + tstraigh.c + utility.c + ) + +IF(XTRKCAD_USE_LAYOUTCONTROL) + SET(SOURCES + ${SOURCES} + cblock.c + cswitchmotor.c + ) +ENDIF(XTRKCAD_USE_LAYOUTCONTROL) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${XTrkCAD_BINARY_DIR}) +INCLUDE_DIRECTORIES(${help_BINARY_DIR}) +INCLUDE_DIRECTORIES(${wlib_SOURCE_DIR}/include) + +# find libintl.h and use it +find_path ( INTL_PATH libintl.h ) +if(INTL_PATH) + INCLUDE_DIRECTORIES(${INTL_PATH}) +endif(INTL_PATH) + +LINK_DIRECTORIES(${GTK_LIBRARY_DIRS}) +LINK_DIRECTORIES(${GTK_WEBKIT_LIBRARY_DIRS}) + +ADD_LIBRARY(xtrkcad-lib ${SOURCES}) + +# This ensures that messages.h has been generated before we build xtrkcad-lib +ADD_DEPENDENCIES(xtrkcad-lib Help) + +ADD_EXECUTABLE(xtrkcad WIN32 + misc.c + xtrkcad.rc + ) +TARGET_LINK_LIBRARIES(xtrkcad xtrkcad-lib) +TARGET_LINK_LIBRARIES(xtrkcad xtrkcad-wlib) + +ADD_EXECUTABLE(mkturnout + ${LIN_SOURCES} + ctodesgn.c + utility.c + ) +SET_TARGET_PROPERTIES(mkturnout PROPERTIES COMPILE_FLAGS -DMKTURNOUT) + +IF(NOT WIN32) + TARGET_LINK_LIBRARIES(mkturnout m) + TARGET_LINK_LIBRARIES(xtrkcad m) + + # Link libintl for systems where it is a separate library + find_library( INTL_LIBRARY intl ) + if(INTL_LIBRARY) + target_link_libraries( mkturnout ${INTL_LIBRARY} ) + endif(INTL_LIBRARY) +ELSE(NOT WIN32) + TARGET_LINK_LIBRARIES(mkturnout xtrkcad-wlib) +ENDIF(NOT WIN32) + +INSTALL( + TARGETS xtrkcad + RUNTIME DESTINATION ${XTRKCAD_BIN_INSTALL_DIR} + ) diff --git a/app/bin/ChangeLog b/app/bin/ChangeLog new file mode 100644 index 0000000..034812e --- /dev/null +++ b/app/bin/ChangeLog @@ -0,0 +1,495 @@ +Apr 28, 2010 + FIX: Daniel Spagnol + i18n.c, misc.c: replaced hard-coded XTRKCAD_LOCALE_DIR path with + 'locale' based on application library directory (XTRKCAD_LOCALE_DIR is + defined at makefiles generation time and does not reflect the place + where the application is actually installed) + +Jan 01, 2010 + FIX: Martin Fischer + custom.c, custom.h: fix compile warnings + +Dec 30, 2009 + FIX: Martin Fischer + misc.c: make load last layout option work + +Dec 29, 2009 + FIX: Martin Fischer + denum.c: remove signed / unsigned mismatch + +Dec 19, 2009 + FIX: Robert Heller + cswitchmotor.c: Patch to fix Visual C++ compile error + +Dec 12, 2009 + FIX: Martin Fischer + draw.c, custom.c: refactoring, move some functionality from the lowlevel + library to the more appropriate core application modules + +Oct 14, 2009 + ENH: Daniel Spagnol + chotbar.c: undone Oct 03, 2009 changes due to gtk+-2.18 fixed it for us. + Actually gtk+-2.18 fixed all surface drawing performance issues for + quartz. + +Oct 09, 2009 + FIX: Daniel Spagnol + denum.c: application crash due to a double value used as a "%*" sprintf + argument. scenario is "Manage" -> "Parts List..." -> "Price" (checkbox). + denum.c: added a character counter function for utf-8 strings + +Oct 04, 2009 + FIX: Martin Fischer + misc2.c: minimum radius is correctly changed + +Oct 03, 2009 + FIX: Daniel Spagnol + chotbar.c: hotbar redraw too slow under gtk-quartz + +Sep 21, 2009 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + custom.c, misc.c, param.c, param.h, smalldlg.c smalldlg.h: + New 'About' dialog + +Sep 16, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + cblock.c, cswitchmotor.c: remove some unused locals + +Aug 16, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + CMakeLists.txt cprint.c denum.c i18n.c i18n.h misc.c + Improve internationalization support, use simple gettext on Win32 + +Aug 16, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + custom.c cturnout.c: Code cleanup + +Jul 30, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + dcustmgm.c: set locale when exporting to parameter + +Jul 24, 2009 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c: add command line option for configuration + file selection + +Jul 10, 2009 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c: use getopt() for access to command line arguments + +Jul 09, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + custom.c, misc.c, denum.c, doption.c: remove some + obsolete flags + +Jul 08, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + cblock.c, cswitchmotor.c: make compile under MSVC + +Jul 08, 2009 + ENH: Robert Heller + cblock.c, cswitchmotor.c: new functionality for layout + control: blocks and switchmotors + +Version 4.0.3a +============== + +Jul 05, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + track.c: Bug fix #2816663: Block gaps are not printed + +Jul 01, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + CMakeList.txt: remove dependency from unneeded cmisc2.c + +Jun 26, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + custom.c: correct handling of export file extensions + +Jun 20, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + ctodesgn.c: convert roadbed width as necessary + (Robert Myers) + +Jun 15, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + tcurve.c, drawgeom.c: fix variable initialization + problems. + +Jun 14, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + macro.c: make demos work again with new dialogs + +Jun 13, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + dlayer.c: "changed" state of layout is updated with + layer changes. (DonDASA) + +Version 4.0.3 +============= + +Jun 10, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + ctodesgn.c: remove unneeded local variables + +Jun 08, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + draw.c: no tooltip for the main drawing area + +Jun 06, 2009 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + draw.c: fix compiler warning + +May 26, 2009 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + ctrain.c: update icons + +May 25, 2009 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + ctrain.c: change default for train running to 'Go' + beautify throttle slider + +May 25, 2009 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + cturnout.c, track.c, track.h,utility.c, cparalle.c + parallel command also works for straight pieces of + sectional track + +May 15, 2009 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + macro.c, misc.c: more message boxes with icon + +May 08, 2009 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + fileio.c, misc.c: use new message box with icon + + +Oct 11, 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + draw.h: fixed prototype for DoZoom as suggested by + Stefan Gruenendahl + +Sep 05, 2008 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c, cselect.c, track.c: create full partlist + when no track is selected + +Sep 01, 2008 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c, common.h: add new toolbar icons for file ops + +Aug 29, 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + draw.c: fixed bug #1821257: no zoom larger than 1:1 + +Jul 11, 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c: update map on loading initial layout + +Jul 10, 2008 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c, misc.h, draw.c: allow user to cancel close request + +Jun 04, 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + cselect.c: Rescale dialog wasn't updated correctly + misc2.c: fixed bug when rescale same piece several times + +Jun 03. 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + CMakeLists.txt: find getext on Win32 + +Jun 03, 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + cselect.c: fixed bug when rescale same piece several times + csnap.c: initialize grid spacing value + +Apr 13, 2008 + ENH: Bob Blackwell + ctrain.c: updated label text + +Mar 27, 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + csnap.c: working default value for grid spacing + +Mar 21, 2008 + FIX: Bob Blackwell + doption.c: uppdated labels in option dialogs + +Mar 18, 2008 + FIX: Bob Blackwell + doption.c: rearrange option settings in display / command / preferences + dialog + +Feb 04, 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + CMakeLists.txt: Fix missing icon problem for Windows exe + +Feb 04, 2008 + FIX: Mikko Nissinen <mni77@users.sourceforge.net> + misc.c: Fixed an internationalization bug in MenuPlayback. + +Feb 04, 2008 + FIX: Mikko Nissinen <mni77@users.sourceforge.net> + cnote.c: Minor fix to internationalization. + +Feb 03, 2008 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + cprint.c: printout of date is correctly localized now. + +Feb 03, 2008 + ENH: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c, misc.h doption.c: on startup last file can now be loaded automatically. + This behavior is controled by an option in the preferences dialog. + +Jan 28, 2008 + FIX: Mikko Nissinen <mni77@users.sourceforge.net> + misc.c: Product name changed in font selection dialog. + +Jan 28, 2008 + FIX: Mikko Nissinen <mni77@users.sourceforge.net> + common.c: Dynamically allocate and form some global translatable + strings. + +Jan 27, 2008 + FIX: Mikko Nissinen <mni77@users.sourceforge.net> + macro.c: String XTrkCad changed to XTrackCAD. + +Jan 27, 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c, fileio.c: fixed product name + +Jan 27, 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + dcar.c: corrected problem in CarPartWrite() + +Jan 25, 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + custom.c, version.h: Changed product name to XTrackCAD and version + to 4.1.0b1 + +Jan 23, 2008 + FIX: Mikko Nissinen <mni77@users.sourceforge.net> + ctodesgn.c: Removed '_()' around turnout label from InitNewTurn() + and ShowTurnoutDesigner(). + dcustmgm.c: Saving custom stuff in demo mode changed the locale + to "C" without restoring it back to original. + +Jan 23, 2008 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + fileio.c: increase precision for roomsize to 6 digits . + +Jan 23, 2008 + FIX: Mikko Nissinen <mni77@users.sourceforge.net> + param.c: ParamPlayback(): If parameter type is PD_FLOAT, then use the + locale "C" during atof(). + +Jan 22, 2008 + ENH: Mikko Nissinen <mni77@users.sourceforge.net> + misc.c: Save user locale when program initializes. + macro.c: Gettext support added. + +Jan 21, 2008 + ENH: Mikko Nissinen <mni77@users.sourceforge.net> + Gettext support added. The following 48 files were modified: + ccurve.c, cdraw.c, celev.c, cgroup.c, chndldto.c, cjoin.c, cmisc.c, + cmisc2.c, cmodify.c, cnote.c, compound.c, cparalle.c, cpull.c, + cruler.c, cselect.c, csnap.c, csplit.c, cstraigh.c, cstruct.c, + ctext.c, ctodesgn.c, ctrain.c, cturnout.c, cturntbl.c, cundo.c, + custom.c, dbench.c, dbitmap.c, dcar.c, dcmpnd.c, dcustmgm.c, dease.c, + denum.c, dlayer.c, doption.c, dpricels.c, dprmfile.c, draw.c, + drawgeom.c, misc2.c, param.c, smalldlg.c, tcurve.c, tease.c, track.c, + tstraigh.c + +Jan 18, 2008 + FIX: Mikko Nissinen <mni77@users.sourceforge.net> + dcar.c: CarInvSaveText() Car list text file is now created to + selected path instead of current working directory. + +Jan 15, 2008 + IMPROVEMENT: Mikko Nissinen <mni77@users.sourceforge.net> + Basic gettext support added. Gettext is initialized in misc.c:wMain(). + The initialization routine is defined in i18n.[ch] along with all + other gettext definitions. + CMakeLists.txt + fileio.[ch] + i18n.[ch] + misc.c + Also the following CMakeLists were modified for gettext: + xtrkcad/CMakeLists.txt + xtrkcad/app/CMakeLists.txt + xtrkcad/app/help/CMakeLists.txt + xtrkcad/app/i18n/CMakeLists.txt (Initial import) + xtrkcad/app/wlib/gtklib/CMakeLists.txt + +Dec 13, 2007 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + fileio.c: fixed segfault when locale is saved + +Dec. 12. 2007 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + dlayer.c: layers lists are updated properly after file is loaded + fileio.c: fixed segfault when locale is saved + Makefile: updated dependencies for dlayer.c + +Dec 08, 2007 + FIX: Martin Fischer <m_fischer@users.sourceforge.net> + xtrkcad.ico: create a new color icon + +Dec. 01, 2007 + BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net> + acclkeys.h: removed non-working accelerator key for deselect all + +Nov. 30, 2007 + FIX: Timothy M. Shead + misc.c: make sure that font initialization is run first + +Oct 29, 2007 + BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net> + dlayer.c: Shortened button text to 'Defaults' + +Oct 10, 2007 + BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net> + csnap.c cprint.c, misc.c: Accelerator keys for Print and + Snap Grid Dialog work again. + +Oct 10, 2007 + BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net> + acclkeys.h: Revert and Redo used the same accelerator key. + Fixed, Revert doesn't have an acclerator now. + +Sep 28, 2007 + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c, smalldlg.c: Use large message for tip of the day + teaser line. Changed to a more generous spacing in dialogs. + +Sep 23, 2007 + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c, smalldlg.c: separated tip window code into new + source file. Slightly improved the "tip of the day" dialog + (jump to next and prev tip). + +Sep 15, 2007 + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c: XTrkCad now has a real splash window during startup + +Jul 22, 2007 + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + draw.c: the mouse wheel can be used for zooming in and out + +Jun 27, 2007 + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + dlayer.c: some cleanup and modified layer buttons. Also all + layer buttons where moved to the bitmaps directory. + +Jun 16, 2007 + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + fileio.c: default directory for storing files is the user's + home directory now. + +Jun 15, 2007 + BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net> + dlayer.c: fixed function prototype for Windows compile + +Jun 15, 2007 + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + dlayer.c: layer buttons now are push buttons that are in + 'pressed' state when layer is visible. + +Jun 15, 2007 + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + dlayer.c, fileio.c, misc.c: settings for the layers can now + be saved in the preferences. On opening a new layout or upon + startup of XTrkCad these settings are automatically loaded. + +May 18, 2007 + + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + draw.c misc.c: disable zoom up and zoom down buttons when + end of list is reached + +Apr 30, 2007 + + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + draw.c, misc.c, draw.h: use radio buttons for selecting zoom factor + zoom in and out goes through all available zoom factors step by step + setting zoom is available from the pulldown menu as well + +Apr 11, 2007 + + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + draw.c: changed layout of status bar to include labels. + Part count is no longer shown. + +Feb 23, 2007 + BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net> + cmisc.c, cselect.c rescale / resize works again. UI change to + allow changing scale and gauge independently + +Feb 16, 2007 + + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + Recently used files list is only updated after successful load + + +Version 4.0.1 +============= + +May 26th, 2006 + + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + Visual Studio C++ 2005 Express is now supported under Windows + +Mar 26th, 2006 + + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c, fileio,c, draw.c If the application crashed the user can decide + to resume work at the last state saved in a checkpoint file + checkpoint files (ckp and ck1) are removed on normal exit + +Mar 25th, 2006 + + BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net> + misc2.c prevent warning in DoSetScaleDesc + +Mar 02nd, 2006 + + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + cturnout.c Improvements to the select turnout dialog, new turnout is drawn + blue + +Feb. 26th, 2006 + + NEW FEATURE: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c, cselect.c, 'Select orphaned track' command added to set all + unconnected track pieces. + +Feb, 22nd, 2006 + + NEW FEATURE: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c, misc2.c, doption.c Scale and gauge are two independant seetings + now. + + NEW FEATURE: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c, cselect.c Add new function 'Invert Selection' which inverts + the selection state of all visible objects on the layout + + NEW FEATURE: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c Add new function 'Revert' to main menu, implemented in ChkRevert + acclkeys.h Added Ctrl-r as accelerator for 'Revert' + + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + cselect.c Optimized performance for 'Select Connected' operation + + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + bllnhelp.c: removed inconsistencies in usage of 'track' and 'object' + + IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net> + misc.c: moved 'Join' command to 'Change' menu + + BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net> + fileio.c Setting locale to portable 'C' before reading/writing parameters + and trackplans to prevent problems with locales that use comma as decimal + separator ( eg. Germany ) + + BUGFIX: diff --git a/app/bin/acclkeys.h b/app/bin/acclkeys.h new file mode 100644 index 0000000..7770f1a --- /dev/null +++ b/app/bin/acclkeys.h @@ -0,0 +1,157 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/acclkeys.h,v 1.6 2009-07-08 18:40:27 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. + */ + +/* + * use 'sort +2 acclkeys.h' to check usage + */ + +#ifndef ACCLKEYS_H +#define ACCLKEYS_H + +/* commands */ +#define ACCL_DESCRIBE (WCTL+'?') +#define ACCL_SELECT (WCTL+'e') +#define ACCL_STRAIGHT (WCTL+'g') +#define ACCL_CURVE1 (WCTL+'4') +#define ACCL_CURVE2 (WCTL+'5') +#define ACCL_CURVE3 (WCTL+'6') +#define ACCL_CURVE4 (WCTL+'7') +#define ACCL_CIRCLE1 (WCTL+'8') +#define ACCL_CIRCLE2 (WCTL+'9') +#define ACCL_CIRCLE3 (WCTL+'0') +#define ACCL_TURNOUT (WCTL+'t') +#define ACCL_TURNTABLE (WCTL+WSHIFT+'n') +#define ACCL_PARALLEL (WCTL+WSHIFT+'p') +#define ACCL_MOVE (WCTL+WSHIFT+'m') +#define ACCL_ROTATE (WCTL+WSHIFT+'r') +#define ACCL_FLIP (0) +#define ACCL_MOVEDESC (WCTL+WSHIFT+'z') +#define ACCL_MODIFY (WCTL+'m') +#define ACCL_JOIN (WCTL+'j') +#define ACCL_CONNECT (WCTL+WSHIFT+'j') +#define ACCL_HELIX (WCTL+WSHIFT+'h') +#define ACCL_SPLIT (WCTL+WSHIFT+'s') +#define ACCL_ELEVATION (WCTL+WSHIFT+'e') +#define ACCL_PROFILE (WCTL+WSHIFT+'f') +#define ACCL_DELETE (WCTL+'d') +#define ACCL_TUNNEL (WCTL+WSHIFT+'t') +#define ACCL_HNDLDTO (WCTL+WSHIFT+'i') +#define ACCL_TEXT (WCTL+WSHIFT+'x') +#define ACCL_DRAWLINE (WCTL+WSHIFT+'1') +#define ACCL_DRAWDIMLINE (WCTL+WSHIFT+'d') +#define ACCL_DRAWBENCH (WCTL+'b') +#define ACCL_DRAWTBLEDGE (WCTL+WSHIFT+'3') +#define ACCL_DRAWCURVE1 (WCTL+WSHIFT+'4') +#define ACCL_DRAWCURVE2 (WCTL+WSHIFT+'5') +#define ACCL_DRAWCURVE3 (WCTL+WSHIFT+'6') +#define ACCL_DRAWCURVE4 (WCTL+WSHIFT+'7') +#define ACCL_DRAWCIRCLE1 (WCTL+WSHIFT+'8') +#define ACCL_DRAWCIRCLE2 (WCTL+WSHIFT+'9') +#define ACCL_DRAWCIRCLE3 (WCTL+WSHIFT+'0') +#define ACCL_DRAWFILLCIRCLE1 (WALT+WCTL+'8') +#define ACCL_DRAWFILLCIRCLE2 (WALT+WCTL+'9') +#define ACCL_DRAWFILLCIRCLE3 (WALT+WCTL+'0') +#define ACCL_DRAWBOX (WCTL+WSHIFT+'[') +#define ACCL_DRAWFILLBOX (WALT+WCTL+'[') +#define ACCL_DRAWPOLYLINE (WCTL+WSHIFT+'2') +#define ACCL_DRAWPOLYGON (WALT+WCTL+'2') +#define ACCL_NOTE (WALT+WCTL+'n') +#define ACCL_STRUCTURE (WCTL+WSHIFT+'c') +#define ACCL_ABOVE (WCTL+WSHIFT+'b') +#define ACCL_BELOW (WCTL+WSHIFT+'w') +#define ACCL_RULER (0) + +/* fileM */ +#define ACCL_NEW (WCTL+'n') +#define ACCL_OPEN (WCTL+'o') +#define ACCL_SAVE (WCTL+'s') +#define ACCL_SAVEAS (WCTL+'a') +#define ACCL_REVERT (0) +#define ACCL_PARAMFILES (WALT+WCTL+'s') +#define ACCL_PRICELIST (WALT+WCTL+'q') +#define ACCL_PRINT (WCTL+'p') +#define ACCL_PRINTSETUP (0) +#define ACCL_PRINTBM (WCTL+WSHIFT+'q') +#define ACCL_PARTSLIST (WALT+WCTL+'l') +#define ACCL_NOTES (WALT+WCTL+'t') +#define ACCL_REGISTER (0) + +/* editM */ +#define ACCL_UNDO (WCTL+'z') +#define ACCL_REDO (WCTL+'r') +#define ACCL_COPY (WCTL+'c') +#define ACCL_CUT (WCTL+'x') +#define ACCL_PASTE (WCTL+'v') +#define ACCL_SELECTALL (WCTL+WSHIFT+'a') +#define ACCL_DESELECTALL (0) +#define ACCL_THIN (WCTL+'1') +#define ACCL_MEDIUM (WCTL+'2') +#define ACCL_THICK (WCTL+'3') +#define ACCL_EXPORT (WALT+WCTL+'x') +#define ACCL_IMPORT (WALT+WCTL+'i') +#define ACCL_EXPORTDXF (0) +#define ACCL_LOOSEN (WCTL+WSHIFT+'k') +#define ACCL_GROUP (WCTL+WSHIFT+'g') +#define ACCL_UNGROUP (WCTL+WSHIFT+'u') +#define ACCL_CUSTMGM (WALT+WCTL+'u') +#define ACCL_CARINV (WALT+WCTL+'v') +#define ACCL_LAYERS (WALT+WCTL+'y') +#define ACCL_SETCURLAYER (0) +#define ACCL_MOVCURLAYER (0) +#define ACCL_CLRELEV (0) +#define ACCL_CHGELEV (0) + +/* viewM */ +#define ACCL_REDRAW (WCTL+'l') +#define ACCL_REDRAWALL (WCTL+WSHIFT+'l') +#define ACCL_ZOOMIN (WCTL+'+') +#define ACCL_ZOOMOUT (WCTL+'-') +#define ACCL_SNAPSHOW (WCTL+']') +#define ACCL_SNAPENABLE (WCTL+'[') + +/* optionsM */ +#define ACCL_LAYOUTW (WALT+WCTL+'a') +#define ACCL_DISPLAYW (WALT+WCTL+'d') +#define ACCL_CMDOPTW (WALT+WCTL+'m') +#define ACCL_EASEW (WALT+WCTL+'e') +#define ACCL_FONTW (WALT+WCTL+'f') +#define ACCL_GRIDW (WALT+WCTL+'g') +#define ACCL_STICKY (WALT+WCTL+'k') +#define ACCL_PREFERENCES (WALT+WCTL+'p') +#define ACCL_COLORW (WALT+WCTL+'c') + +/* macroM */ +#define ACCL_RECORD (WALT+WCTL+'r') +#define ACCL_PLAYBACK (WALT+WCTL+'b') + +#define ACCL_BRIDGE (0) + +/* Blocks */ +#define ACCL_BLOCK1 (WALT+WSHIFT+'b') +#define ACCL_BLOCK2 (WALT+WCTL+WSHIFT+'b') +#define ACCL_BLOCK3 (0) +/* Switch Motors */ +#define ACCL_SWITCHMOTOR1 (WSHIFT+'s') +#define ACCL_SWITCHMOTOR2 (WALT+WSHIFT+'s') +#define ACCL_SWITCHMOTOR3 (0) + +#endif diff --git a/app/bin/bdf2xtp.c b/app/bin/bdf2xtp.c new file mode 100644 index 0000000..0efeff9 --- /dev/null +++ b/app/bin/bdf2xtp.c @@ -0,0 +1,1231 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/bdf2xtp.c,v 1.1 2005-12-07 15:46:58 rc-flyer Exp $ + */ + + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#ifndef _MSDOS +#include <unistd.h> +#else +#define M_PI 3.14159265358979323846 +#define strncasecmp strnicmp +#endif +#include <stdlib.h> + +char helpStr[] = +"Bdf2xtp translates .bdf files (which are source files for Winrail track\n" +"libraries) to .xtp files (which are XTrkCad parameter files).\n" +"Bdf2xtp is a MS-DOS command and must be in run in a DOS box under MS-Windows.\n" +"\n" +"Usage: bdf2xtp OPTIONS SOURCE.BDF TARGET.XTP\n" +"\n" +"OPTIONS:\n" +" -c CONTENTS description of contents\n" +" -k COLOR color of non-track segments\n" +" -s SCALE scale of turnouts (ie. HO HOn3 N O S ... )\n" +" -v verbose - include .bdf source as comments in .xtp file\n" +"\n" +"For example:\n" +" bdf2xtp -c \"Faller HO Structures\" -k ff0000 -s HO fallerh0.bdf fallerh0.xtp\n" +"\n" +"Turnouts are composed of rails (which are Black) and lines. Structures are\n" +"composed of only lines. By default lines are Purple but you change this with\n" +"the -k optioon. The color is specified as a 6 digit hexidecimal value, where\n" +"the first 2 digits are the Red value, the middle 2 digits are the Green value\n" +"and the last 2 digits are the Blue value\n" +" ff0000 Red\n" +" 00ff00 Green\n" +" 00ffff Yellow\n" +; + +/* NOTES: +BDF files have a number of constructors for different types of turnouts +with different ways of describing the track segements that comprise them. +XTP files have a orthogonal description which is: + TURNOUT .... header line + P ... paths + E ... endpoints + S ... straight track segments + C ... curved track segments + L ... straight line segments + A ... curved (arc) line segments +Structures are similar but with only L and A lines. + +Paths describe the routing from one end-point to some other. +The routes are a sequence of indices (1-based) in the list of segments. +Some things (like crossings, crossovers and slip switches) have more than +one route for a path (which are then separated by 0: + --1--+--2--+--3-- + \ / + 4 5 + x + / \ + / \ + --6--+--7--+--8-- +The normal routes would be 1,2,3 and 6,7,8. +The reverse routes would be 1,4,8 and 6,5,3. +The path lines are: + P "Normal" 1 2 3 0 6 7 8 + P "Reverse" 1 4 8 0 6 5 3 +Paths are not currently being used but will be when you can run trains +on the layout. + + +Processing: +A table (tokens) describes each type of source line. +For each type the segments and end-points are computed and added to +lists (segs and endPoints). +When the END for a turnout is reached the Path lines are computed by +searching for routes between end-points through the segments. +Then the list of segments is written out to the output file. +*/ + + + +#define MAXSEG (40) /* Maximum number of segments in an object */ + +typedef struct { /* a co-ordinate */ + double x; + double y; + } coOrd; + +FILE * fin; /* input file */ +FILE * fout; /* output file */ +int inch; /* metric or english units */ +char * scale = NULL; /* scale from command line */ +int verbose = 0; /* include source as comments? */ +char line[1024]; /* input line buffer */ +int lineCount; /* source line number */ +int lineLen; /* source line length */ +int inBody; /* seen header? */ +long color = 0x00FF00FF;/* default color */ + +double normalizeAngle( double angle ) +/* make sure <angle> is >= 0.0 and < 360.0 */ +{ + while (angle<0) angle += 360.0; + while (angle>=360) angle -= 360.0; + return angle; +} + +double D2R( double angle ) +/* convert degrees to radians: for trig functions */ +{ + return angle/180.0 * M_PI; +} + +double R2D( double R ) +/* concert radians to degrees */ +{ + return normalizeAngle( R * 360.0 / (M_PI*2) ); +} + + +double findDistance( coOrd p0, coOrd p1 ) +/* find distance between two points */ +{ + double dx = p1.x-p0.x, dy = p1.y-p0.y; + return sqrt( dx*dx + dy*dy ); +} + +int small(double v ) +/* is <v> close to 0.0 */ +{ + return (fabs(v) < 0.0001); +} + +double findAngle( coOrd p0, coOrd p1 ) +/* find angle between two points */ +{ + double dx = p1.x-p0.x, dy = p1.y-p0.y; + if (small(dx)) { + if (dy >=0) return 0.0; + else return 180.0; + } + if (small(dy)) { + if (dx >=0) return 90.0; + else return 270.0; + } + return R2D(atan2( dx,dy )); +} + + +/* Where do we expect each input line? */ +typedef enum { + CLS_NULL, + CLS_START, + CLS_END, + CLS_BODY + } class_e; + +/* Type of input line */ +typedef enum { + ACT_UNKNOWN, + ACT_DONE, + ACT_STRAIGHT, + ACT_CURVE, + ACT_TURNOUT_LEFT, + ACT_TURNOUT_RIGHT, + ACT_CURVEDTURNOUT_LEFT, + ACT_CURVEDTURNOUT_RIGHT, + ACT_THREEWAYTURNOUT, + ACT_CROSSING_LEFT, + ACT_CROSSING_RIGHT, + ACT_DOUBLESLIP_LEFT, + ACT_DOUBLESLIP_RIGHT, + ACT_CROSSING_SYMMETRIC, + ACT_DOUBLESLIP_SYMMETRIC, + ACT_TURNTABLE, + ACT_ENDTURNTABLE, + ACT_TRANSFERTABLE, + ACT_ENDTRANSFERTABLE, + ACT_TRACK, + ACT_STRUCTURE, + ACT_ENDSTRUCTURE, + + ACT_FILL_POINT, + ACT_LINE, + ACT_CURVEDLINE, + ACT_CIRCLE, + ACT_DESCRIPTIONPOS, + ACT_ARTICLENOPOS, + ACT_CONNECTINGPOINT, + ACT_STRAIGHTTRACK, + ACT_CURVEDTRACK, + ACT_STRAIGHT_BODY, + ACT_CURVE_BODY, + ACT_PRICE + } action_e; + +/* input line description */ +typedef struct { + char * name; /* first token on line */ + class_e class; /* where do we expect this? */ + action_e action;/* what type of line is it */ + char *args; /* what else is on the line */ + } tokenDesc_t; + +/* first token on each line tells what kind of line it is */ +tokenDesc_t tokens[] = { + + { "Straight", CLS_START, ACT_STRAIGHT, "SSNN" }, + { "EndStraight", CLS_END, ACT_DONE, NULL }, + { "Curve", CLS_START, ACT_CURVE, "SSNNN" }, + { "EndCurve", CLS_END, ACT_DONE, NULL }, + { "Turnout_Left", CLS_START, ACT_TURNOUT_LEFT, "SSN" }, + { "Turnout_Right", CLS_START, ACT_TURNOUT_RIGHT, "SSN" }, + { "EndTurnout", CLS_END, ACT_DONE, NULL }, + { "CurvedTurnout_Left", CLS_START, ACT_CURVEDTURNOUT_LEFT, "SSN" }, + { "CurvedTurnout_Right", CLS_START, ACT_CURVEDTURNOUT_RIGHT, "SSN" }, + { "ThreeWayTurnout", CLS_START, ACT_THREEWAYTURNOUT, "SSN" }, + { "Crossing_Left", CLS_START, ACT_CROSSING_LEFT, "SSNNNN" }, + { "Crossing_Right", CLS_START, ACT_CROSSING_RIGHT, "SSNNNN" }, + { "DoubleSlip_Left", CLS_START, ACT_DOUBLESLIP_LEFT, "SSNNNNN" }, + { "DoubleSlip_Right", CLS_START, ACT_DOUBLESLIP_RIGHT, "SSNNNNN" }, + { "Crossing_Symetric", CLS_START, ACT_CROSSING_SYMMETRIC, "SSNNN" }, + { "DoubleSlip_Symetric", CLS_START, ACT_DOUBLESLIP_SYMMETRIC, "SSNNNN" }, + { "EndCrossing", CLS_END, ACT_DONE, NULL }, + { "Turntable", CLS_START, ACT_TURNTABLE, "SSNNNN" }, + { "EndTurntable", CLS_END, ACT_ENDTURNTABLE, NULL }, + { "TravellingPlatform", CLS_START, ACT_TRANSFERTABLE, "SSNNNNN" }, + { "EndTravellingPlatform", CLS_END, ACT_ENDTRANSFERTABLE, NULL }, + { "Track", CLS_START, ACT_TRACK, "SSN" }, + { "EndTrack", CLS_END, ACT_DONE, NULL }, + { "Structure", CLS_START, ACT_STRUCTURE, "SS" }, + { "EndStructure", CLS_END, ACT_ENDSTRUCTURE, NULL }, + + { "FillPoint", CLS_BODY, ACT_FILL_POINT, "NNI" }, + { "Line", CLS_BODY, ACT_LINE, "NNNN" }, + { "CurvedLine", CLS_BODY, ACT_CURVEDLINE, "NNNNN" }, + { "CurveLine", CLS_BODY, ACT_CURVEDLINE, "NNNNN" }, + { "Circle", CLS_BODY, ACT_CIRCLE, "NNN" }, + { "DescriptionPos", CLS_BODY, ACT_DESCRIPTIONPOS, "NN" }, + { "ArticleNoPos", CLS_BODY, ACT_DESCRIPTIONPOS, "NN" }, + { "ConnectingPoint", CLS_BODY, ACT_CONNECTINGPOINT, "NNN" }, + { "StraightTrack", CLS_BODY, ACT_STRAIGHTTRACK, "NNNN" }, + { "CurvedTrack", CLS_BODY, ACT_CURVEDTRACK, "NNNNN" }, + { "Straight", CLS_BODY, ACT_STRAIGHT_BODY, "N" }, + { "Curve", CLS_BODY, ACT_CURVE_BODY, "NNN" }, + { "Price", CLS_BODY, ACT_PRICE, "N" }, + + { "Gerade", CLS_START, ACT_STRAIGHT, "SSNN" }, + { "EndGerade", CLS_END, ACT_DONE, NULL }, + { "Bogen", CLS_START, ACT_CURVE, "SSNNN" }, + { "EndBogen", CLS_END, ACT_DONE, NULL }, + { "Weiche_links", CLS_START, ACT_TURNOUT_LEFT, "SSN" }, + { "Weiche_Rechts", CLS_START, ACT_TURNOUT_RIGHT, "SSN" }, + { "EndWeiche", CLS_END, ACT_DONE, NULL }, + { "Bogenweiche_Links", CLS_START, ACT_CURVEDTURNOUT_LEFT, "SSN" }, + { "Bogenweiche_Rechts", CLS_START, ACT_CURVEDTURNOUT_RIGHT, "SSN" }, + { "Dreiwegweiche", CLS_START, ACT_THREEWAYTURNOUT, "SSN" }, + { "Kreuzung_Links", CLS_START, ACT_CROSSING_LEFT, "SSNNNN" }, + { "Kreuzung_Rechts", CLS_START, ACT_CROSSING_RIGHT, "SSNNNN" }, + { "DKW_Links", CLS_START, ACT_DOUBLESLIP_LEFT, "SSNNNNN" }, + { "DKW_Rechts", CLS_START, ACT_DOUBLESLIP_RIGHT, "SSNNNNN" }, + { "Kreuzung_Symmetrisch", CLS_START, ACT_CROSSING_SYMMETRIC, "SSNNN" }, + { "DKW_Symmetrisch", CLS_START, ACT_DOUBLESLIP_SYMMETRIC, "SSNNNN" }, + { "EndKreuzung", CLS_END, ACT_DONE, NULL }, + { "Drehscheibe", CLS_START, ACT_TURNTABLE, "SSNNNN" }, + { "EndDrehscheibe", CLS_END, ACT_ENDTURNTABLE, NULL }, + { "Schiebebuehne", CLS_START, ACT_TRANSFERTABLE, "SSNNNNN" }, + { "EndSchiebebuehne", CLS_END, ACT_ENDTRANSFERTABLE, NULL }, + { "Schiene", CLS_START, ACT_TRACK, "SSN" }, + { "EndSchiene", CLS_END, ACT_DONE, NULL }, + { "Haus", CLS_START, ACT_STRUCTURE, "SS" }, + { "EndHaus", CLS_END, ACT_ENDSTRUCTURE, NULL }, + + { "FuellPunkt", CLS_BODY, ACT_FILL_POINT, "NNI" }, + { "Linie", CLS_BODY, ACT_LINE, "NNNN" }, + { "Bogenlinie", CLS_BODY, ACT_CURVEDLINE, "NNNNN" }, + { "Kreislinie", CLS_BODY, ACT_CIRCLE, "NNN" }, + { "BezeichnungsPos", CLS_BODY, ACT_DESCRIPTIONPOS, "NN" }, + { "ArtikelNrPos", CLS_BODY, ACT_DESCRIPTIONPOS, "NN" }, + { "Anschlusspunkt", CLS_BODY, ACT_CONNECTINGPOINT, "NNN" }, + { "GeradesGleis", CLS_BODY, ACT_STRAIGHTTRACK, "NNNN" }, + { "BogenGleis", CLS_BODY, ACT_CURVEDTRACK, "NNNNN" }, + { "Gerade", CLS_BODY, ACT_STRAIGHT_BODY, "N" }, + { "Bogen", CLS_BODY, ACT_CURVE_BODY, "NNN" }, + { "Preis", CLS_BODY, ACT_PRICE, "N" } }; + + +/* argument description */ +typedef union { + char * string; + double number; + long integer; + } arg_t; + +/* description of a curve */ +typedef struct { + char type; + coOrd pos[2]; + double radius, a0, a1; + coOrd center; + } line_t; + +/* state info for the current object */ +int curAction; +line_t lines[MAXSEG]; +line_t *line_p; +char * name; +char * partNo; +double params[10]; +int right = 0; + +/* A XTrkCad End-Point */ +typedef struct { + int busy; + coOrd pos; + double a; + } endPoint_t; +endPoint_t endPoints[MAXSEG]; +endPoint_t *endPoint_p; + +/* the segments */ +typedef struct { + double radius; + coOrd pos[2]; + int mark; + endPoint_t * ep[2]; + } segs_t; +segs_t segs[MAXSEG]; +segs_t *seg_p; + + +/* the segment paths */ +typedef struct { + int index; + int count; + int segs[MAXSEG]; + } paths_t; +paths_t paths[MAXSEG]; +paths_t *paths_p; + +int curPath[MAXSEG]; +int curPathInx; + +char * pathNames[] = { + "Normal", + "Reverse" }; + +int isclose( coOrd a, coOrd b ) +{ + if ( fabs(a.x-b.x) < 0.1 && + fabs(a.y-b.y) < 0.1 ) + return 1; + else + return 0; +} + + +void searchSegs( segs_t * sp, int ep ) +/* Recursively search the segs looking for the next segement that begins + where this (sp->pos[ep]) one ends. We mark the ones we have already + used (sp->mark). + Returns when we can't continue. + Leaves the path in curPath[] +*/ +{ + segs_t *sp1; + int inx; + + sp->mark = 1; + curPath[curPathInx] = (ep==0?-((sp-segs)+1):((sp-segs)+1)); + if (sp->ep[ep] != NULL) { + inx = abs(curPath[0]); + if ( (sp-segs)+1 < inx ) + return; + paths_p->index = 0; + paths_p->count = curPathInx+1; + for (inx=0;inx<=curPathInx;inx++) + paths_p->segs[inx] = curPath[inx]; + paths_p++; + return; + } + curPathInx++; + for ( sp1 = segs; sp1<seg_p; sp1++ ) { + if (!sp1->mark) { + if ( isclose( sp->pos[ep], sp1->pos[0] ) ) + searchSegs( sp1, 1 ); + else if ( isclose( sp->pos[ep], sp1->pos[1] ) ) + searchSegs( sp1, 0 ); + } + } + curPathInx--; +} + + +void computePaths( void ) +/* Generate the path lines. Search the segments for nonoverlapping + routes between end-points. + */ +{ + char **name = pathNames; + segs_t * sp, *sp1; + endPoint_t *ep, *ep2; + int inx; + char bitmap[MAXSEG]; + paths_t * pp; + int pathIndex; + int pathCount; + int firstPath; + int segNo; + int epNo; + + paths_p = paths; + for ( sp = segs; sp<seg_p; sp++ ) { + sp->ep[0] = sp->ep[1] = NULL; + for ( ep = endPoints; ep<endPoint_p; ep++ ) { + if ( isclose( ep->pos, sp->pos[0] ) ) { + sp->ep[0] = ep; + } else if ( isclose( ep->pos, sp->pos[1] ) ) { + sp->ep[1] = ep; + } + } + } + for ( sp = segs; sp<seg_p; sp++ ) { + for ( sp1 = segs; sp1<seg_p; sp1++ ) + sp1->mark = 0; + curPathInx = 0; + if ( sp->ep[0] ) { + searchSegs( sp, 1 ); + } else if ( sp->ep[1] ) { + searchSegs( sp, 0 ); + } + } + pathIndex = 0; + pathCount = paths_p-paths; + while (pathCount>0) { + if (pathIndex < 2) + fprintf( fout, "\tP \"%s\"", pathNames[pathIndex] ); + else + fprintf( fout, "\tP \"%d\"", pathIndex+1 ); + pathIndex++; + firstPath = 1; + memset( bitmap, 0, sizeof bitmap ); + for ( ep = endPoints; ep<endPoint_p; ep++ ) { + ep->busy = 0; + } + for (pp = paths; pp < paths_p; pp++) { + if (pp->count == 0) + continue; + segNo = pp->segs[0]; + epNo = (segNo>0?0:1); + ep = segs[abs(segNo)-1].ep[epNo]; + segNo = pp->segs[pp->count-1]; + epNo = (segNo>0?1:0); + ep2 = segs[abs(segNo)-1].ep[epNo]; + if ( (ep && ep->busy) || (ep2 && ep2->busy) ) { + goto nextPath; + } + if (ep) ep->busy = 1; + if (ep2) ep2->busy = 1; + for (inx=0; inx<pp->count; inx++) { + segNo = abs(pp->segs[inx]); + if (bitmap[segNo]) + goto nextPath; + } + if (!firstPath) { + fprintf( fout, " 0"); + } else { + firstPath = 0; + } + for (inx=0; inx<pp->count; inx++) { + segNo = abs(pp->segs[inx]); + bitmap[segNo] = 1; + fprintf( fout, " %d", pp->segs[inx] ); + } + pp->count = 0; + pathCount--; +nextPath: + ; + } + fprintf( fout, "\n" ); + } +} + + +void translate( coOrd *res, coOrd orig, double a, double d ) +{ + res->x = orig.x + d * sin( D2R(a) ); + res->y = orig.y + d * cos( D2R(a) ); +} + + +static void computeCurve( coOrd pos0, coOrd pos1, double radius, coOrd * center, double * a0, double * a1 ) +/* translate between curves described by 2 end-points and a radius to + a curve described by a center, radius and angles. +*/ +{ + double d, a, aa, aaa, s; + + d = findDistance( pos0, pos1 )/2.0; + a = findAngle( pos0, pos1 ); + s = fabs(d/radius); + if (s > 1.0) + s = 1.0; + aa = R2D(asin( s )); + if (radius > 0) { + aaa = a + (90.0 - aa); + *a0 = normalizeAngle( aaa + 180.0 ); + translate( center, pos0, aaa, radius ); + } else { + aaa = a - (90.0 - aa); + *a0 = normalizeAngle( aaa + 180.0 - aa *2.0 ); + translate( center, pos0, aaa, -radius ); + } + *a1 = aa*2.0; +} + + +double X( double v ) +{ + if ( -0.000001 < v && v < 0.000001 ) + return 0.0; + else + return v; +} + + +void generateTurnout( void ) +/* Seen the END so pump out the the TURNOUT + Write out the header and the segment descriptions. + */ +{ + segs_t *sp; + line_t *lp; + endPoint_t *ep; + double d, a, aa, aaa, a0, a1; + coOrd center; + + fprintf( fout, "TURNOUT %s \"%s %s\"\n", scale, partNo, name ); + computePaths(); + for (ep=endPoints; ep<endPoint_p; ep++) + fprintf( fout, "\tE %0.6f %0.6f %0.6f\n", + X(ep->pos.x), X(ep->pos.y), X(ep->a) ); + for (lp=lines; lp<line_p; lp++) { + switch (lp->type) { + case 'L': + fprintf( fout, "\tL %ld 0 %0.6f %0.6f %0.6f %0.6f\n", color, + X(lp->pos[0].x), X(lp->pos[0].y), X(lp->pos[1].x), X(lp->pos[1].y) ); + break; + case 'A': + fprintf( fout, "\tA %ld 0 %0.6f %0.6f %0.6f %0.6f %0.6f\n", color, + X(lp->radius), X(lp->center.x), X(lp->center.y), X(lp->a0), X(lp->a1) ); + break; + } + } + for (sp=segs; sp<seg_p; sp++) + if (sp->radius == 0.0) { + fprintf( fout, "\tS 0 0 %0.6f %0.6f %0.6f %0.6f\n", + X(sp->pos[0].x), X(sp->pos[0].y), X(sp->pos[1].x), X(sp->pos[1].y) ); + } else { + computeCurve( sp->pos[0], sp->pos[1], sp->radius, ¢er, &a0, &a1 ); + fprintf( fout, "\tC 0 0 %0.6f %0.6f %0.6f %0.6f %0.6f\n", + X(sp->radius), X(center.x), X(center.y), X(a0), X(a1) ); + } + fprintf( fout, "\tEND\n" ); +} + + +void reset( tokenDesc_t * tp, arg_t *args ) +/* Start of a new turnout or structure */ +{ + int inx; + curAction = tp->action; + line_p = lines; + seg_p = segs; + endPoint_p = endPoints; + partNo = strdup( args[0].string ); + name = strdup( args[1].string ); + for (inx=2; tp->args[inx]; inx++) + params[inx-2] = args[inx].number; +} + +double getDim( double value ) +/* convert to inches from tenths of a an inch or millimeters. */ +{ + if (inch) + return value/10.0; + else + return value/25.4; +} + + +char * getLine( void ) +/* Get a source line, trim CR/LF, handle comments */ +{ + char * cp; + while (1) { + if (fgets(line, sizeof line, fin) == NULL) + return NULL; + lineCount++; + lineLen = strlen(line); + if (lineLen > 0 && line[lineLen-1] == '\n') { + line[lineLen-1] = '\0'; + lineLen--; + } + if (lineLen > 0 && line[lineLen-1] == '\r') { + line[lineLen-1] = '\0'; + lineLen--; + } + cp = strchr( line, ';'); + if (cp) { + *cp = '\0'; + lineLen = cp-line; + } + cp = line; + while ( isspace(*cp) ) { + cp++; + lineLen--; + } + if (lineLen <= 0) + continue; + if (verbose) + fprintf( fout, "# %s\n", line ); + return cp; + } +} + + +void flushInput( void ) +/* Eat source until we see an END - error recovery */ +{ + char *cp; + while (cp=getLine()) { + if (strncasecmp( cp, "End", 3 ) == 0 ) + break; + } + inBody = 0; +} + + +void process( tokenDesc_t * tp, arg_t *args ) +/* process a tokenized line */ +{ + + int inx; + int count; + int endNo; + double radius, radius2; + static double angle; + double length, length2; + double width, width2, offset; + double a0, a1; + static char bits[128]; + int rc; + char * cp; + line_t * lp; + coOrd pos0, pos1; + static int threeway; + + switch (tp->action) { + + case ACT_DONE: + generateTurnout(); + right = 0; + threeway = 0; + break; + + case ACT_STRAIGHT: + reset( tp, args ); + seg_p->radius = 0.0; + endPoint_p->pos.x = seg_p->pos[0].x = 0.0; + endPoint_p->pos.y = seg_p->pos[0].y = 0.0; + endPoint_p->a = 270.0; + endPoint_p++; + endPoint_p->pos.x = seg_p->pos[1].x = getDim(args[2].number); + endPoint_p->pos.y = seg_p->pos[1].y = 0.0; + endPoint_p->a = 90.0; + endPoint_p++; + seg_p++; + break; + + case ACT_CURVE: + reset( tp, args ); + radius = getDim(args[2].number); + seg_p->radius = -radius; + endPoint_p->pos.y = seg_p->pos[0].y = 0.0; + endPoint_p->pos.x = seg_p->pos[0].x = 0.0; + endPoint_p->a = 270.0; + endPoint_p++; + angle = args[3].number; + endPoint_p->a = 90.0-angle; + endPoint_p->pos.x = seg_p->pos[1].x = radius * sin(D2R(angle)); + endPoint_p->pos.y = seg_p->pos[1].y = radius * (1-cos(D2R(angle))); + endPoint_p++; + seg_p++; + break; + + case ACT_TURNOUT_RIGHT: + right = 1; + case ACT_TURNOUT_LEFT: + reset( tp, args ); + break; + + case ACT_CURVEDTURNOUT_RIGHT: + right = 1; + case ACT_CURVEDTURNOUT_LEFT: + reset( tp, args ); + endPoint_p->pos.y = 0.0; + endPoint_p->pos.x = 0.0; + endPoint_p->a = 270.0; + endPoint_p++; + if ((cp=getLine())==NULL) + return; + if ((rc=sscanf( line, "%lf %lf", &radius, &angle ) ) != 2) { + fprintf( stderr, "syntax error: %d: %s\n", lineCount, line ); + flushInput(); + return; + } + radius = getDim( radius ); + endPoint_p->pos.x = radius*sin(D2R(angle)); + endPoint_p->pos.y = radius*(1-cos(D2R(angle))); + endPoint_p->a = 90.0-angle; + seg_p->pos[0].y = 0; + seg_p->pos[0].x = 0; + seg_p->pos[1] = endPoint_p->pos; + seg_p->radius = -radius; + endPoint_p++; + seg_p++; + if ((cp=getLine())==NULL) + return; + if ((rc=sscanf( line, "%lf %lf", &radius2, &angle ) ) != 2) { + fprintf( stderr, "syntax error: %d: %s\n", lineCount, line ); + flushInput(); + return; + } + radius2 = getDim( radius2 ); + endPoint_p->pos.x = radius*sin(D2R(angle)) + (radius2-radius); + endPoint_p->pos.y = radius*(1-cos(D2R(angle))); + endPoint_p->a = 90.0-angle; + seg_p->pos[0] = seg_p[-1].pos[0]; + seg_p->pos[1].x = radius2-radius; + seg_p->pos[1].y = 0; + seg_p->radius = 0; + seg_p++; + seg_p->pos[0].x = radius2-radius; + seg_p->pos[0].y = 0; + seg_p->pos[1] = endPoint_p->pos; + seg_p->radius = -radius; + endPoint_p++; + seg_p++; + if (tp->action == ACT_CURVEDTURNOUT_RIGHT) { + endPoint_p[-1].pos.y = -endPoint_p[-1].pos.y; + endPoint_p[-1].a = 180.0-endPoint_p[-1].a; + seg_p[-1].pos[0].y = -seg_p[-1].pos[0].y; + seg_p[-1].pos[1].y = -seg_p[-1].pos[1].y; + seg_p[-1].radius = -seg_p[-1].radius; + endPoint_p[-2].pos.y = -endPoint_p[-2].pos.y; + endPoint_p[-2].a = 180.0-endPoint_p[-2].a; + seg_p[-3].pos[0].y = -seg_p[-3].pos[0].y; + seg_p[-3].pos[1].y = -seg_p[-3].pos[1].y; + seg_p[-3].radius = -seg_p[-3].radius; + } + break; + case ACT_THREEWAYTURNOUT: + reset( tp, args ); + threeway = 1; + break; + + case ACT_CROSSING_LEFT: + case ACT_CROSSING_RIGHT: + case ACT_CROSSING_SYMMETRIC: + case ACT_DOUBLESLIP_LEFT: + case ACT_DOUBLESLIP_RIGHT: + case ACT_DOUBLESLIP_SYMMETRIC: + reset( tp, args ); + angle = args[3].number; + length = getDim(args[4].number); + seg_p->radius = 0.0; + endPoint_p->pos.y = seg_p->pos[0].y = 0.0; + endPoint_p->pos.x = seg_p->pos[0].x = 0.0; + endPoint_p->a = 270.0; + endPoint_p++; + endPoint_p->pos.x = seg_p->pos[1].x = length; + endPoint_p->pos.y = seg_p->pos[1].y = 0.0; + endPoint_p->a = 90.0; + endPoint_p++; + seg_p++; + length /= 2.0; + if (tp->action == ACT_CROSSING_SYMMETRIC || + tp->action == ACT_DOUBLESLIP_SYMMETRIC) { + length2 = length; + } else { + length2 = getDim( args[5].number )/2.0; + } + seg_p->radius = 0.0; + endPoint_p->pos.x = seg_p->pos[0].x = length - length2*cos(D2R(angle)); + endPoint_p->pos.y = seg_p->pos[0].y = length2*sin(D2R(angle)); + endPoint_p->a = normalizeAngle(270.0+angle); + endPoint_p++; + endPoint_p->pos.x = seg_p->pos[1].x = length*2.0-seg_p->pos[0].x; + endPoint_p->pos.y = seg_p->pos[1].y = -seg_p->pos[0].y; + endPoint_p->a = normalizeAngle(90.0+angle); + endPoint_p++; + seg_p++; + if (tp->action == ACT_CROSSING_RIGHT || + tp->action == ACT_DOUBLESLIP_RIGHT ) { + endPoint_p[-1].pos.y = -endPoint_p[-1].pos.y; + endPoint_p[-2].pos.y = -endPoint_p[-2].pos.y; + seg_p[-1].pos[0].y = -seg_p[-1].pos[0].y; + seg_p[-1].pos[1].y = -seg_p[-1].pos[1].y; + endPoint_p[-1].a = normalizeAngle( 180.0 - endPoint_p[-1].a ); + endPoint_p[-2].a = normalizeAngle( 180.0 - endPoint_p[-2].a ); + } + break; + + case ACT_TURNTABLE: + reset( tp, args ); + if ((cp=getLine())==NULL) + return; + if ((rc=sscanf( line, "%lf %s", &angle, bits ) ) != 2) { + fprintf( stderr, "syntax error: %d: %s\n", lineCount, line ); + flushInput(); + return; + } + fprintf( fout, "TURNOUT %s \"%s %s\"\n", scale, partNo, name ); + count = 360.0/angle; + angle = 0; + length = strlen( bits ); + if (length < count) + count = length; + length = getDim( args[3].number ); + length2 = getDim( args[5].number ); + endNo = 1; + for ( inx=0; inx<count; inx++ ) { + if (bits[inx]!='0') { + fprintf( fout, "\tP \"%d\" %d\n", endNo, endNo ); + endNo++; + } + } + for ( inx=0; inx<count; inx++ ) { + angle = normalizeAngle( 90.0 - inx * ( 360.0 / count ) ); + if (bits[inx]!='0') + fprintf( fout, "\tE %0.6f %0.6f %0.6f\n", + X(length * sin(D2R(angle))), + X(length * cos(D2R(angle))), + X(angle) ); + } + for ( inx=0; inx<count; inx++ ) { + angle = normalizeAngle( 90.0 - inx * ( 360.0 / count ) ); + if (bits[inx]!='0') + fprintf( fout, "\tS 0 0 %0.6f %0.6f %0.6f %0.6f\n", + X(length * sin(D2R(angle))), + X(length * cos(D2R(angle))), + X(length2 * sin(D2R(angle))), + X(length2 * cos(D2R(angle))) ); + } + fprintf( fout, "\tA %ld 0 %0.6f 0.000000 0.000000 0.000000 360.000000\n", + color, length2 ); + if (length != length2) + fprintf( fout, "\tA %ld 0 %0.6f 0.000000 0.000000 0.000000 360.000000\n", + color, length ); + break; + + case ACT_ENDTURNTABLE: + for (lp=lines; lp<line_p; lp++) { + switch (lp->type) { + case 'L': + fprintf( fout, "\tL %ld 0 %0.6f %0.6f %0.6f %0.6f\n", color, + X(lp->pos[0].x), X(lp->pos[0].y), X(lp->pos[1].x), X(lp->pos[1].y) ); + break; + case 'A': + fprintf( fout, "\tA %ld 0 %0.6f %0.6f %0.6f %0.6f %0.6f\n", color, + X(lp->radius), X(lp->center.x), X(lp->center.y), X(lp->a0), X(lp->a1) ); + break; + } + } + fprintf( fout, "\tEND\n" ); + break; + + case ACT_TRANSFERTABLE: + reset( tp, args ); + fprintf( fout, "TURNOUT %s \"%s %s\"\n", scale, partNo, name ); + width = getDim(args[3].number); + width2 = getDim(args[5].number); + length = getDim( args[6].number); + fprintf( fout, "\tL %ld 0 0.0000000 0.000000 0.000000 %0.6f\n", color, length ); + fprintf( fout, "\tL %ld 0 0.0000000 %0.6f %0.6f %0.6f\n", color, length, width, length ); + fprintf( fout, "\tL %ld 0 %0.6f %0.6f %0.6f 0.000000\n", color, width, length, width ); + fprintf( fout, "\tL %ld 0 %0.6f 0.0000000 0.000000 0.000000\n", color, width ); + fprintf( fout, "\tL %ld 0 %0.6f %0.6f %0.6f %0.6f\n", color, + (width-width2)/2.0, 0.0, (width-width2)/2.0, length ); + fprintf( fout, "\tL %ld 0 %0.6f %0.6f %0.6f %0.6f\n", color, + width-(width-width2)/2.0, 0.0, width-(width-width2)/2.0, length ); + if ((cp=getLine())==NULL) + return; + if ((rc=sscanf( line, "%lf %lf %s", &length2, &offset, bits ) ) != 3) { + fprintf( stderr, "syntax error: %d: %s\n", lineCount, line ); + flushInput(); + return; + } + offset = getDim( offset ); + length2 = getDim( length2 ); + for (inx=0; bits[inx]; inx++) { + if (bits[inx]=='1') { + fprintf( fout, "\tE 0.000000 %0.6f 270.0\n", + offset ); + fprintf( fout, "\tS 0 0 0.000000 %0.6f %0.6f %0.6f\n", + offset, (width-width2)/2.0, offset ); + } + offset += length2; + } + if ((cp=getLine())==NULL) + return; + if ((rc=sscanf( line, "%lf %lf %s", &length2, &offset, bits ) ) != 3) { + fprintf( stderr, "syntax error: %d: %s\n", lineCount, line ); + flushInput(); + return; + } + offset = getDim( offset ); + length2 = getDim( length2 ); + for (inx=0; bits[inx]; inx++) { + if (bits[inx]=='1') { + fprintf( fout, "\tE %0.6f %0.6f 90.0\n", + width, offset ); + fprintf( fout, "\tS 0 0 %0.6f %0.6f %0.6f %0.6f\n", + width-(width-width2)/2.0, offset, width, offset ); + } + offset += length2; + } + fprintf( fout, "\tEND\n"); + break; + + case ACT_ENDTRANSFERTABLE: + break; + + case ACT_TRACK: + reset( tp, args ); + break; + + case ACT_STRUCTURE: + reset( tp, args ); + break; + + case ACT_ENDSTRUCTURE: + fprintf( fout, "STRUCTURE %s \"%s %s\"\n", scale, partNo, name ); + for (lp=lines; lp<line_p; lp++) { + switch (lp->type) { + case 'L': + fprintf( fout, "\tL %ld 0 %0.6f %0.6f %0.6f %0.6f\n", color, + X(lp->pos[0].x), X(lp->pos[0].y), X(lp->pos[1].x), X(lp->pos[1].y) ); + break; + case 'A': + fprintf( fout, "\tA %ld 0 %0.6f %0.6f %0.6f %0.6f %0.6f\n", color, + X(lp->radius), X(lp->center.x), X(lp->center.y), X(lp->a0), X(lp->a1) ); + break; + } + } + fprintf( fout, "\tEND\n" ); + break; + + case ACT_FILL_POINT: + break; + + case ACT_LINE: + line_p->type = 'L'; + line_p->pos[0].x = getDim(args[0].number); + line_p->pos[0].y = getDim(args[1].number); + line_p->pos[1].x = getDim(args[2].number); + line_p->pos[1].y = getDim(args[3].number); + line_p++; + break; + + case ACT_CURVEDLINE: + line_p->type = 'A'; + pos0.x = getDim(args[0].number); + pos0.y = getDim(args[1].number); + line_p->radius = getDim(args[2].number); + length2 = 2*line_p->radius*sin(D2R(args[3].number/2.0)); + angle = args[3].number/2.0 + args[4].number; + pos1.x = pos0.x + length2*cos(D2R(angle)); + pos1.y = pos0.y + length2*sin(D2R(angle)); + computeCurve( pos0, pos1, line_p->radius, &line_p->center, &line_p->a0, &line_p->a1 ); + line_p++; + break; + + case ACT_CIRCLE: + line_p->type = 'A'; + line_p->center.x = getDim( args[0].number ); + line_p->center.y = getDim( args[1].number ); + line_p->radius = getDim( args[2].number ); + line_p->a0 = 0.0; + line_p->a1 = 360.0; + line_p++; + break; + + case ACT_DESCRIPTIONPOS: + break; + + case ACT_ARTICLENOPOS: + break; + + case ACT_CONNECTINGPOINT: + endPoint_p->pos.x = getDim(args[0].number); + endPoint_p->pos.y = getDim(args[1].number); + endPoint_p->a = normalizeAngle( 90.0 - args[2].number ); + endPoint_p++; + break; + + case ACT_STRAIGHTTRACK: + seg_p->radius = 0.0; + seg_p->pos[0].x = getDim(args[0].number); + seg_p->pos[0].y = getDim(args[1].number); + seg_p->pos[1].x = getDim(args[2].number); + seg_p->pos[1].y = getDim(args[3].number); + seg_p++; + break; + + case ACT_CURVEDTRACK: + seg_p->pos[0].x = getDim(args[0].number); + seg_p->pos[0].y = getDim(args[1].number); + seg_p->radius = getDim(args[2].number); + length2 = 2*seg_p->radius*sin(D2R(args[3].number/2.0)); + angle = 90.0-args[4].number - args[3].number/2.0; + seg_p->pos[1].x = seg_p->pos[0].x + length2*sin(D2R(angle)); + seg_p->pos[1].y = seg_p->pos[0].y + length2*cos(D2R(angle)); + seg_p->radius = - seg_p->radius; + seg_p++; + break; + + case ACT_STRAIGHT_BODY: + seg_p->radius = 0; + seg_p->pos[0].x = 0.0; + seg_p->pos[0].y = 0.0; + seg_p->pos[1].x = getDim(args[0].number); + seg_p->pos[1].y = 0.0; + endPoint_p->pos = seg_p->pos[0]; + endPoint_p->a = 270.0; + endPoint_p++; + endPoint_p->pos = seg_p->pos[1]; + endPoint_p->a = 90.0; + endPoint_p++; + seg_p++; + break; + + case ACT_CURVE_BODY: + if (endPoint_p == endPoints) { + endPoint_p->pos.y = 0.0; + endPoint_p->pos.x = 0.0; + endPoint_p->a = 270.0; + endPoint_p++; + } + seg_p->radius = getDim(args[0].number); + angle = args[1].number; + seg_p->pos[0].x = getDim(args[2].number); + seg_p->pos[0].y = 0.0; + seg_p->pos[1].x = seg_p->pos[0].x + seg_p->radius * sin( D2R( angle ) ); + seg_p->pos[1].y = seg_p->radius * (1-cos( D2R( angle )) ); + if (right || (threeway && (seg_p-segs == 2)) ) { + seg_p->pos[1].y = - seg_p->pos[1].y; + angle = - angle; + } else { + seg_p->radius = - seg_p->radius; + } + endPoint_p->pos = seg_p->pos[1]; + endPoint_p->a = normalizeAngle( 90.0-angle ); + endPoint_p++; + seg_p++; + break; + + case ACT_PRICE: + break; + + } +} + + +void parse( void ) +/* parse a line: + figure out what it is, read the arguments, call process() + */ +{ + char *cp, *cpp; + char strings[512], *sp; + int len; + tokenDesc_t *tp; + int tlen; + arg_t args[10]; + int inx; + + inBody = 0; + lineCount = 0; + while ( cp=getLine() ) { + if (strncasecmp( cp, "INCH", strlen("INCH") ) == 0) { + inch++; + continue; + } + for ( tp=tokens; tp<&tokens[ sizeof tokens / sizeof *tp ]; tp++ ){ + tlen = strlen(tp->name); + if ( strncasecmp( cp, tp->name, tlen) != 0 ) + continue; + if ( cp[tlen] != '\0' && cp[tlen] != ' ' && cp[tlen] != ',' ) + continue; + if ( (inBody) == (tp->class==CLS_START) ) { + continue; + } + cp += tlen+1; + if (tp->args) + for ( inx=0, sp=strings; tp->args[inx]; inx++ ) { + if (*cp == '\0') { + fprintf( stderr, "%d: unexpected end of line\n", lineCount ); + goto nextLine; + } + switch( tp->args[inx] ) { + case 'S': + args[inx].string = sp; + while (isspace(*cp)) cp++; + if (*cp != '"') { + fprintf( stderr, "%d: expected a \": %s\n", lineCount, cp ); + goto nextLine; + } + cp++; + while ( *cp ) { + if ( *cp != '"' ) { + *sp++ = *cp++; + } else if ( cp[1] == '"' ) { + *sp++ = '"'; + *sp++ = '"'; + cp += 2; + } else { + cp++; + *sp++ = '\0'; + break; + } + } + break; + + case 'N': + args[inx].number = strtod( cp, &cpp ); + if (cpp == cp) { + fprintf( stderr, "%d: expected a number: %s\n", lineCount, cp ); + goto nextLine; + } + cp = cpp; + break; + + case 'I': + args[inx].integer = strtol( cp, &cpp, 10 ); + if (cpp == cp) { + fprintf( stderr, "%d: expected an integer: %s\n", lineCount, cp ); + goto nextLine; + } + cp = cpp; + break; + + } + } + process( tp, args ); + if (tp->class == CLS_START) + inBody = 1; + else if (tp->class == CLS_END) + inBody = 0; + tp = NULL; + break; + } + if (tp) { + fprintf( stderr, "%d: Unknown token: %s\n", lineCount, cp ); + } +nextLine: + ; + } +} + + +int main ( int argc, char * argv[] ) +/* main: handle options, open files */ +{ + char * contents = NULL; + argv++; + argc--; + while (argc > 2) { + if (strcmp(*argv,"-v")==0) { + verbose++; + argv++; argc--; + } else if (strcmp( *argv, "-s" )==0) { + argv++; argc--; + scale = *argv; + argv++; argc--; + } else if (strcmp( *argv, "-c" )==0) { + argv++; argc--; + contents = *argv; + argv++; argc--; + } else if (strcmp( *argv, "-k" )==0) { + argv++; argc--; + color = strtol(*argv, NULL, 16); + argv++; argc--; + } + } + if (argc < 2) { + fprintf( stderr, helpStr ); + exit(1); + } + if (scale == NULL) { + fprintf( stderr, "scale must be defined\n" ); + exit(1); + } + + if ( strcmp( argv[0], argv[1] ) == 0 ) { + fprintf( stderr, "Input and output file names are the same!" ); + exit(1); + } + + fin = fopen( *argv, "r" ); + if (fin == NULL) { + perror(*argv); + exit(1); + } + argv++; + fout = fopen( *argv, "w" ); + if (fout == NULL) { + perror(*argv); + exit(1); + } + if (contents) + fprintf( fout, "CONTENTS %s\n", contents ); + parse(); +} diff --git a/app/bin/bitmaps/SVG/block.svg b/app/bin/bitmaps/SVG/block.svg new file mode 100755 index 0000000..221d631 --- /dev/null +++ b/app/bin/bitmaps/SVG/block.svg @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16px" + height="16px" + id="svg2160" + sodipodi:version="0.32" + inkscape:version="0.45.1" + sodipodi:docbase="C:\Dokumente und Einstellungen\MF\Eigene Dateien\xtc\source\xtrkcad\app\bin\bitmaps\SVG" + sodipodi:docname="block.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs2162"> + <linearGradient + id="linearGradient3240"> + <stop + style="stop-color:#c6ffc7;stop-opacity:1;" + offset="0" + id="stop3242" /> + <stop + style="stop-color:#000000;stop-opacity:0;" + offset="1" + id="stop3244" /> + </linearGradient> + <filter + inkscape:collect="always" + x="-0.40165289" + width="1.8033058" + y="-0.40165289" + height="1.8033058" + id="filter4024"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.34075874" + id="feGaussianBlur4026" /> + </filter> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.197802" + inkscape:cx="8" + inkscape:cy="8" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:window-width="797" + inkscape:window-height="573" + inkscape:window-x="43" + inkscape:window-y="33" /> + <metadata + id="metadata2165"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.39860046px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 1.6993002,4.8001142 C 1.6993002,4.0323881 1.6993002,6.1874076 1.6993002,7.1314013 C 1.6993002,8.3269519 1.6993002,9.5225027 1.6993002,10.718053" + id="path3197" + inkscape:transform-center-x="-0.69930023" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.30206299px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 16.340038,7.9045778 C 16.170995,7.9045778 15.782267,7.9045778 15.510057,7.9045778 C 15.332995,7.9045778 15.155932,7.9045778 14.97887,7.9045778 C 14.646878,7.9045778 14.314885,7.9045778 13.982894,7.9045778 C 13.756061,7.9045778 14.109982,7.9045778 14.182089,7.9045778" + id="path3204" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#00c3ff;stroke-width:1.39256012px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 4.8935538,8.0622511 C 5.3383788,8.0622511 6.3612827,8.0622511 7.0775815,8.0622511 C 7.5435072,8.0622511 8.0094329,8.0622511 8.4753585,8.0622511 C 9.3489697,8.0622511 10.22258,8.0622511 11.096191,8.0622511 C 11.693085,8.0622511 10.761766,8.0622511 10.572024,8.0622511" + id="path3214" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#00c3ff;stroke-width:1.38903475px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 11.694517,4.8941729 C 11.694517,4.1226995 11.694517,6.2882379 11.694517,7.2368393 C 11.694517,8.4382256 11.694517,9.639612 11.694517,10.840998" + id="path3216" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#00c3ff;stroke-width:1.3360846px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 4.2655817,10.718063 C 4.2569707,11.44917 4.2811417,9.396941 4.2917297,8.4979743 C 4.305139,7.3594493 4.3185482,6.2209242 4.3319577,5.0823988" + id="path3220" + inkscape:transform-center-x="2.6989442" + inkscape:transform-center-y="1.5477264" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.34471488px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 14.327643,4.8345845 C 14.327643,4.0668584 14.327643,6.2218779 14.327643,7.1658716 C 14.327643,8.3614222 14.327643,9.5569733 14.327643,10.752524" + id="path3222" /> + <path + sodipodi:type="arc" + style="fill:#00cb05;fill-opacity:1;fill-rule:nonzero;stroke:#00cb05;stroke-linejoin:round;stroke-opacity:1" + id="path3224" + sodipodi:cx="8.1990099" + sodipodi:cy="3.1608911" + sodipodi:rx="1.5316832" + sodipodi:ry="1.5316832" + d="M 9.7306931 3.1608911 A 1.5316832 1.5316832 0 1 1 6.6673267,3.1608911 A 1.5316832 1.5316832 0 1 1 9.7306931 3.1608911 z" + transform="matrix(1.3326023,0,0,1.3326023,-3.0745345,-1.504785)" /> + <path + sodipodi:type="arc" + style="fill:#8bf68e;fill-opacity:1;fill-rule:nonzero;stroke:#d3fcd3;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4024)" + id="path3250" + sodipodi:cx="7.5007424" + sodipodi:cy="2.4626236" + sodipodi:rx="0.51806933" + sodipodi:ry="0.51806933" + d="M 8.0188118 2.4626236 A 0.51806933 0.51806933 0 1 1 6.9826731,2.4626236 A 0.51806933 0.51806933 0 1 1 8.0188118 2.4626236 z" + transform="matrix(1.2433747,0,0,1.2433747,-2.0603915,-0.8322608)" /> + <path + sodipodi:type="arc" + style="fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:#ff0000;stroke-linejoin:round;stroke-opacity:1" + id="path4034" + sodipodi:cx="8.1990099" + sodipodi:cy="3.1608911" + sodipodi:rx="1.5316832" + sodipodi:ry="1.5316832" + d="M 9.7306931 3.1608911 A 1.5316832 1.5316832 0 1 1 6.6673267,3.1608911 A 1.5316832 1.5316832 0 1 1 9.7306931 3.1608911 z" + transform="matrix(1.3326023,0,0,1.3326023,-3.0379006,8.949671)" /> + <path + sodipodi:type="arc" + style="fill:#ed0202;fill-opacity:0.9766355;fill-rule:nonzero;stroke:#fff6f6;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4024)" + id="path4036" + sodipodi:cx="7.5007424" + sodipodi:cy="2.4626236" + sodipodi:rx="0.51806933" + sodipodi:ry="0.51806933" + d="M 8.0188118 2.4626236 A 0.51806933 0.51806933 0 1 1 6.9826731,2.4626236 A 0.51806933 0.51806933 0 1 1 8.0188118 2.4626236 z" + transform="matrix(1.3434079,0,0,1.3868861,-2.6614572,9.403929)" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.30206299px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 2.3873088,7.8910891 C 2.2182658,7.8910891 1.8295378,7.8910891 1.5573278,7.8910891 C 1.3802658,7.8910891 1.2032028,7.8910891 1.0261408,7.8910891 C 0.69414879,7.8910891 0.36215579,7.8910891 0.030164793,7.8910891 C -0.19666821,7.8910891 0.15725279,7.8910891 0.22935979,7.8910891" + id="path4052" /> + </g> +</svg> diff --git a/app/bin/bitmaps/SVG/blockdel.svg b/app/bin/bitmaps/SVG/blockdel.svg new file mode 100755 index 0000000..014101e --- /dev/null +++ b/app/bin/bitmaps/SVG/blockdel.svg @@ -0,0 +1,202 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16px" + height="16px" + id="svg2160" + sodipodi:version="0.32" + inkscape:version="0.46+devel" + sodipodi:docname="blockdel.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs2162"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective938" /> + <linearGradient + id="linearGradient3240"> + <stop + style="stop-color:#c6ffc7;stop-opacity:1;" + offset="0" + id="stop3242" /> + <stop + style="stop-color:#000000;stop-opacity:0;" + offset="1" + id="stop3244" /> + </linearGradient> + <filter + inkscape:collect="always" + x="-0.40165289" + width="1.8033058" + y="-0.40165289" + height="1.8033058" + id="filter4024"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.34075874" + id="feGaussianBlur4026" /> + </filter> + <filter + inkscape:collect="always" + id="filter4262"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.34439655" + id="feGaussianBlur4264" /> + </filter> + <filter + inkscape:collect="always" + id="filter4590"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.56189548" + id="feGaussianBlur4592" /> + </filter> + <filter + inkscape:collect="always" + id="filter1458"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.0419981" + id="feGaussianBlur1460" /> + </filter> + <filter + inkscape:collect="always" + id="filter1462"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.0419981" + id="feGaussianBlur1464" /> + </filter> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.197802" + inkscape:cx="8" + inkscape:cy="8" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:window-width="980" + inkscape:window-height="697" + inkscape:window-x="43" + inkscape:window-y="33" /> + <metadata + id="metadata2165"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.39860046px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 1.6993002,4.8001142 C 1.6993002,4.0323881 1.6993002,6.1874076 1.6993002,7.1314013 C 1.6993002,8.3269519 1.6993002,9.5225027 1.6993002,10.718053" + id="path3197" + inkscape:transform-center-x="-0.69930023" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.30206299px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 16.340038,7.9045778 C 16.170995,7.9045778 15.782267,7.9045778 15.510057,7.9045778 C 15.332995,7.9045778 15.155932,7.9045778 14.97887,7.9045778 C 14.646878,7.9045778 14.314885,7.9045778 13.982894,7.9045778 C 13.756061,7.9045778 14.109982,7.9045778 14.182089,7.9045778" + id="path3204" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#acb5b7;stroke-width:1.39256011999999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 4.8935538,8.0622511 C 5.3383788,8.0622511 6.3612827,8.0622511 7.0775815,8.0622511 C 7.5435072,8.0622511 8.0094329,8.0622511 8.4753585,8.0622511 C 9.3489697,8.0622511 10.22258,8.0622511 11.096191,8.0622511 C 11.693085,8.0622511 10.761766,8.0622511 10.572024,8.0622511" + id="path3214" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#acb5b7;stroke-width:1.38903474999999998px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 11.694517,4.8941729 C 11.694517,4.1226995 11.694517,6.2882379 11.694517,7.2368393 C 11.694517,8.4382256 11.694517,9.639612 11.694517,10.840998" + id="path3216" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#acb5b7;stroke-width:1.33608459999999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 4.2655817,10.718063 C 4.2569707,11.44917 4.2811417,9.396941 4.2917297,8.4979743 C 4.305139,7.3594493 4.3185482,6.2209242 4.3319577,5.0823988" + id="path3220" + inkscape:transform-center-x="2.6989442" + inkscape:transform-center-y="1.5477264" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.34471488px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 14.327643,4.8345845 C 14.327643,4.0668584 14.327643,6.2218779 14.327643,7.1658716 C 14.327643,8.3614222 14.327643,9.5569733 14.327643,10.752524" + id="path3222" /> + <path + sodipodi:type="arc" + style="fill:#5eb160;fill-opacity:1;fill-rule:nonzero;stroke:#64ab67;stroke-linejoin:round;stroke-opacity:1;opacity:0.59999999999999998;filter:url(#filter1462)" + id="path3224" + sodipodi:cx="8.1990099" + sodipodi:cy="3.1608911" + sodipodi:rx="1.5316832" + sodipodi:ry="1.5316832" + d="M 9.7306931 3.1608911 A 1.5316832 1.5316832 0 1 1 6.6673267,3.1608911 A 1.5316832 1.5316832 0 1 1 9.7306931 3.1608911 z" + transform="matrix(1.3326023,0,0,1.3326023,-3.0745345,-1.504785)" /> + <path + sodipodi:type="arc" + style="fill:#8bf68e;fill-opacity:1;fill-rule:nonzero;stroke:#d3fcd3;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4024)" + id="path3250" + sodipodi:cx="7.5007424" + sodipodi:cy="2.4626236" + sodipodi:rx="0.51806933" + sodipodi:ry="0.51806933" + d="M 8.0188118 2.4626236 A 0.51806933 0.51806933 0 1 1 6.9826731,2.4626236 A 0.51806933 0.51806933 0 1 1 8.0188118 2.4626236 z" + transform="matrix(1.2433747,0,0,1.2433747,-2.0603915,-0.8322608)" /> + <path + sodipodi:type="arc" + style="fill:#c64343;fill-opacity:1;fill-rule:nonzero;stroke:#bd4747;stroke-linejoin:round;stroke-opacity:1;opacity:0.59999999999999998;filter:url(#filter1458)" + id="path4034" + sodipodi:cx="8.1990099" + sodipodi:cy="3.1608911" + sodipodi:rx="1.5316832" + sodipodi:ry="1.5316832" + d="M 9.7306931 3.1608911 A 1.5316832 1.5316832 0 1 1 6.6673267,3.1608911 A 1.5316832 1.5316832 0 1 1 9.7306931 3.1608911 z" + transform="matrix(1.3326023,0,0,1.3326023,-3.0379006,8.949671)" /> + <path + sodipodi:type="arc" + style="fill:#ed0202;fill-opacity:0.9766355;fill-rule:nonzero;stroke:#fff6f6;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4024)" + id="path4036" + sodipodi:cx="7.5007424" + sodipodi:cy="2.4626236" + sodipodi:rx="0.51806933" + sodipodi:ry="0.51806933" + d="M 8.0188118 2.4626236 A 0.51806933 0.51806933 0 1 1 6.9826731,2.4626236 A 0.51806933 0.51806933 0 1 1 8.0188118 2.4626236 z" + transform="matrix(1.3434079,0,0,1.3868861,-2.6614572,9.403929)" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.30206299px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 2.3873088,7.8910891 C 2.2182658,7.8910891 1.8295378,7.8910891 1.5573278,7.8910891 C 1.3802658,7.8910891 1.2032028,7.8910891 1.0261408,7.8910891 C 0.69414879,7.8910891 0.36215579,7.8910891 0.030164793,7.8910891 C -0.19666821,7.8910891 0.15725279,7.8910891 0.22935979,7.8910891" + id="path4052" /> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Kreuz"> + <path + style="opacity:1;fill:#ffbcbc;fill-opacity:0.9766355;fill-rule:nonzero;stroke:#fe0c28;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4262)" + id="path4120" + d="M 1.6435248,1.2975405 C 1.9595667,1.3765484 2.2002947,1.6483632 2.4416995,1.8566649 C 3.2373367,2.5705845 4.0213749,3.2977861 4.7915955,4.0391247 C 5.4415093,4.6596851 6.0597973,5.3147026 6.6342298,6.005753 C 7.2568986,6.754229 7.859818,7.5169919 8.5114947,8.2408278 C 9.0586715,8.8242157 9.6133079,9.3992859 10.113943,10.023681 C 10.478987,10.485782 10.808409,10.973353 11.104351,11.482134 C 11.336857,11.932116 11.552026,12.391977 11.744823,12.86042 C 11.899032,13.259443 12.042474,13.662523 12.198994,14.060663 C 12.286033,14.327094 12.407326,14.577266 12.542823,14.821368 C 12.600501,14.923636 12.668864,15.019112 12.734963,15.115794 L 12.172674,15.446454 C 12.110596,15.345012 12.046512,15.244666 11.988707,15.140615 C 11.858183,14.888794 11.744276,14.631527 11.661839,14.359116 C 11.517598,13.956085 11.373567,13.552965 11.221243,13.152898 C 11.037284,12.683276 10.825695,12.224316 10.595777,11.775477 C 10.308657,11.264538 9.9826539,10.779502 9.623231,10.316254 C 9.1407305,9.6842321 8.6144381,9.0899817 8.0778158,8.5034329 C 7.4257047,7.7772906 6.8108601,7.020749 6.1709235,6.2841824 C 5.5923741,5.6031984 5.0074194,4.9260316 4.3716643,4.2972277 C 3.6065779,3.5434852 2.8176443,2.812626 1.9668049,2.1554922 C 1.7046979,1.967348 1.4364078,1.7114383 1.1025551,1.6905781 L 1.6435248,1.2975405 z " /> + <path + style="opacity:1;fill:#ffbcbc;fill-opacity:0.9766355;fill-rule:nonzero;stroke:#ff0000;stroke-width:0.97403181;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4590)" + id="path4362" + d="M 1.4870159,15.313791 C 1.5822724,15.197807 1.6688171,15.070473 1.7561343,14.944192 C 1.972201,14.657592 2.1877349,14.370272 2.4070051,14.087955 C 2.8008215,13.579655 3.2072567,13.090057 3.6179882,12.606144 C 4.1271638,12.019005 4.6462737,11.447655 5.1578606,10.864154 C 5.6821133,10.229884 6.1922548,9.575884 6.7255265,8.9547746 C 7.2752277,8.3152345 7.8423787,7.7028731 8.400678,7.0767971 C 8.9291468,6.4552048 9.4751363,5.8642703 10.04859,5.3178007 C 10.821748,4.5892592 11.648276,3.972091 12.477845,3.3661481 C 13.142978,2.8901926 13.898733,2.3394567 14.393623,1.9322919 C 14.615862,1.7541839 14.829261,1.5589115 15.025316,1.3330263 L 15.512984,1.0703325 C 15.301,1.3104258 15.071009,1.5183264 14.831411,1.7072367 C 13.951951,2.4166646 13.04311,3.0573276 12.144837,3.723013 C 11.576836,4.1400553 11.010921,4.5773554 10.47852,5.0922189 C 9.8992238,5.6374375 9.3473562,6.227327 8.8162061,6.8535098 C 8.26037,7.4778595 7.6962121,8.0893376 7.1485091,8.7263782 C 6.6120331,9.3475396 6.0993003,10.002822 5.5737503,10.640039 C 5.0692549,11.219891 4.5598036,11.791456 4.0544491,12.369864 C 3.6535345,12.845636 3.2544407,13.323224 2.8758266,13.830708 C 2.6662807,14.105991 2.4621051,14.387902 2.2547002,14.665968 C 2.1736846,14.787465 2.0948039,14.911438 2.0086923,15.026663 L 1.4870159,15.313791 z " /> + </g> +</svg> diff --git a/app/bin/bitmaps/SVG/blockedit.svg b/app/bin/bitmaps/SVG/blockedit.svg new file mode 100755 index 0000000..3ed69c3 --- /dev/null +++ b/app/bin/bitmaps/SVG/blockedit.svg @@ -0,0 +1,239 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16px" + height="16px" + id="svg2160" + sodipodi:version="0.32" + inkscape:version="0.46+devel" + sodipodi:docname="blockedit.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs2162"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective29" /> + <linearGradient + id="linearGradient12512"> + <stop + style="stop-color:#ffffff;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop12513" /> + <stop + style="stop-color:#fff520;stop-opacity:0.89108908;" + offset="0.50000000" + id="stop12517" /> + <stop + style="stop-color:#fff300;stop-opacity:0.0000000;" + offset="1.0000000" + id="stop12514" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient12512" + id="radialGradient278" + gradientUnits="userSpaceOnUse" + cx="55.000000" + cy="125.00000" + fx="55.000000" + fy="125.00000" + r="14.375000" /> + <linearGradient + id="linearGradient3240"> + <stop + style="stop-color:#c6ffc7;stop-opacity:1;" + offset="0" + id="stop3242" /> + <stop + style="stop-color:#000000;stop-opacity:0;" + offset="1" + id="stop3244" /> + </linearGradient> + <filter + inkscape:collect="always" + x="-0.40165289" + width="1.8033058" + y="-0.40165289" + height="1.8033058" + id="filter4024"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.34075874" + id="feGaussianBlur4026" /> + </filter> + <inkscape:perspective + id="perspective1446" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2984" + id="radialGradient12692" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.66077,-0.5114749,0.3584765,1.1380119,-52.478445,-7.4863015)" + cx="29.053354" + cy="27.640751" + fx="29.053354" + fy="27.640751" + r="3.2408545" /> + <linearGradient + id="linearGradient2984" + inkscape:collect="always"> + <stop + id="stop2986" + offset="0" + style="stop-color:#e7e2b8;stop-opacity:1;" /> + <stop + id="stop2988" + offset="1" + style="stop-color:#e7e2b8;stop-opacity:0;" /> + </linearGradient> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.197802" + inkscape:cx="-1.7532179" + inkscape:cy="8" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:window-width="1152" + inkscape:window-height="793" + inkscape:window-x="0" + inkscape:window-y="25" /> + <metadata + id="metadata2165"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.39860046px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 1.6993002,4.8001142 C 1.6993002,4.0323881 1.6993002,6.1874076 1.6993002,7.1314013 C 1.6993002,8.3269519 1.6993002,9.5225027 1.6993002,10.718053" + id="path3197" + inkscape:transform-center-x="-0.69930023" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.30206299px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 16.340038,7.9045778 C 16.170995,7.9045778 15.782267,7.9045778 15.510057,7.9045778 C 15.332995,7.9045778 15.155932,7.9045778 14.97887,7.9045778 C 14.646878,7.9045778 14.314885,7.9045778 13.982894,7.9045778 C 13.756061,7.9045778 14.109982,7.9045778 14.182089,7.9045778" + id="path3204" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.39256011999999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 4.8935538,8.0622511 C 5.3383788,8.0622511 6.3612827,8.0622511 7.0775815,8.0622511 C 7.5435072,8.0622511 8.0094329,8.0622511 8.4753585,8.0622511 C 9.3489697,8.0622511 10.22258,8.0622511 11.096191,8.0622511 C 11.693085,8.0622511 10.761766,8.0622511 10.572024,8.0622511" + id="path3214" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.389;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="M 11.694517,4.8941729 C 11.694517,4.1226995 11.694517,6.2882379 11.694517,7.2368393 C 11.694517,8.4382256 11.694517,9.639612 11.694517,10.840998" + id="path3216" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.336;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="M 4.2655817,10.718063 C 4.2569707,11.44917 4.2811417,9.396941 4.2917297,8.4979743 C 4.305139,7.3594493 4.3185482,6.2209242 4.3319577,5.0823988" + id="path3220" + inkscape:transform-center-x="2.6989442" + inkscape:transform-center-y="1.5477264" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.34471488px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 14.327643,4.8345845 C 14.327643,4.0668584 14.327643,6.2218779 14.327643,7.1658716 C 14.327643,8.3614222 14.327643,9.5569733 14.327643,10.752524" + id="path3222" /> + <path + sodipodi:type="arc" + style="fill:#008c03;fill-opacity:1;fill-rule:nonzero;stroke:#008c04;stroke-linejoin:round;stroke-opacity:1;opacity:0.62101910999999999" + id="path3224" + sodipodi:cx="8.1990099" + sodipodi:cy="3.1608911" + sodipodi:rx="1.5316832" + sodipodi:ry="1.5316832" + d="M 9.7306931 3.1608911 A 1.5316832 1.5316832 0 1 1 6.6673267,3.1608911 A 1.5316832 1.5316832 0 1 1 9.7306931 3.1608911 z" + transform="matrix(1.3326023,0,0,1.3326023,-3.0745345,-1.504785)" /> + <path + sodipodi:type="arc" + style="fill:#8bf68e;fill-opacity:1;fill-rule:nonzero;stroke:#d3fcd3;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4024);opacity:0.62101911" + id="path3250" + sodipodi:cx="7.5007424" + sodipodi:cy="2.4626236" + sodipodi:rx="0.51806933" + sodipodi:ry="0.51806933" + d="M 8.0188118 2.4626236 A 0.51806933 0.51806933 0 1 1 6.9826731,2.4626236 A 0.51806933 0.51806933 0 1 1 8.0188118 2.4626236 z" + transform="matrix(1.2433747,0,0,1.2433747,-2.0603915,-0.8322608)" /> + <path + sodipodi:type="arc" + style="fill:#ea0000;fill-opacity:1;fill-rule:nonzero;stroke:#ea0000;stroke-linejoin:round;stroke-opacity:1;opacity:0.60099999999999998" + id="path4034" + sodipodi:cx="8.1990099" + sodipodi:cy="3.1608911" + sodipodi:rx="1.5316832" + sodipodi:ry="1.5316832" + d="M 9.7306931 3.1608911 A 1.5316832 1.5316832 0 1 1 6.6673267,3.1608911 A 1.5316832 1.5316832 0 1 1 9.7306931 3.1608911 z" + transform="matrix(1.3326023,0,0,1.3326023,-3.0379006,8.949671)" /> + <path + sodipodi:type="arc" + style="fill:#ed0202;fill-opacity:0.97663549999999999;fill-rule:nonzero;stroke:#fff6f6;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4024);opacity:0.62101911" + id="path4036" + sodipodi:cx="7.5007424" + sodipodi:cy="2.4626236" + sodipodi:rx="0.51806933" + sodipodi:ry="0.51806933" + d="M 8.0188118 2.4626236 A 0.51806933 0.51806933 0 1 1 6.9826731,2.4626236 A 0.51806933 0.51806933 0 1 1 8.0188118 2.4626236 z" + transform="matrix(1.3434079,0,0,1.3868861,-2.6614572,9.403929)" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.30206299px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 2.3873088,7.8910891 C 2.2182658,7.8910891 1.8295378,7.8910891 1.5573278,7.8910891 C 1.3802658,7.8910891 1.2032028,7.8910891 1.0261408,7.8910891 C 0.69414879,7.8910891 0.36215579,7.8910891 0.030164793,7.8910891 C -0.19666821,7.8910891 0.15725279,7.8910891 0.22935979,7.8910891" + id="path4052" /> + <g + id="g12687" + transform="translate(1.8925749,-1.1877081)"> + <path + sodipodi:nodetypes="cccccc" + id="path2960" + d="M 3.1781506,12.311899 5.380053,8.1740123 15.072589,-0.80795299 C 16.698027,-2.0773811 18.61549,0.17677763 17.269305,1.5908536 L 7.5460431,10.351631 3.1781506,12.311899 z" + style="color:#000000;fill:#cb9022;fill-opacity:1;fill-rule:evenodd;stroke:#5c410c;stroke-width:0.55234361;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + <path + sodipodi:nodetypes="cccc" + id="path2982" + d="M 3.6881537,11.914091 5.4496756,8.6037819 c 0.9955939,0.193125 1.676718,0.8098537 1.7219019,1.7569811 l -3.4834238,1.553328 z" + style="color:#000000;fill:url(#radialGradient12692);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + <path + sodipodi:nodetypes="ccccc" + id="path3004" + d="M 7.1926465,10.395805 7.156667,9.7679658 17.407525,0.54092818 c 0,0 0.0635,0.50320052 0.01347,0.63421122 L 7.1926465,10.395805 z" + style="color:#000000;fill:#000000;fill-opacity:0.36363639;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + </g> + </g> +</svg> diff --git a/app/bin/bitmaps/SVG/blocknew.svg b/app/bin/bitmaps/SVG/blocknew.svg new file mode 100755 index 0000000..e8c51e3 --- /dev/null +++ b/app/bin/bitmaps/SVG/blocknew.svg @@ -0,0 +1,216 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16px" + height="16px" + id="svg2160" + sodipodi:version="0.32" + inkscape:version="0.46+devel" + sodipodi:docname="blocknew.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:export-filename="/home/martin/xtcng/src/xtrkcad/app/bin/bitmaps/SVG/blocknew.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <defs + id="defs2162"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective29" /> + <linearGradient + id="linearGradient12512"> + <stop + style="stop-color:#ffffff;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop12513" /> + <stop + style="stop-color:#fff520;stop-opacity:0.89108908;" + offset="0.50000000" + id="stop12517" /> + <stop + style="stop-color:#fff300;stop-opacity:0.0000000;" + offset="1.0000000" + id="stop12514" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient12512" + id="radialGradient278" + gradientUnits="userSpaceOnUse" + cx="55.000000" + cy="125.00000" + fx="55.000000" + fy="125.00000" + r="14.375000" /> + <linearGradient + id="linearGradient3240"> + <stop + style="stop-color:#c6ffc7;stop-opacity:1;" + offset="0" + id="stop3242" /> + <stop + style="stop-color:#000000;stop-opacity:0;" + offset="1" + id="stop3244" /> + </linearGradient> + <filter + inkscape:collect="always" + x="-0.40165289" + width="1.8033058" + y="-0.40165289" + height="1.8033058" + id="filter4024"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.34075874" + id="feGaussianBlur4026" /> + </filter> + <inkscape:perspective + id="perspective1410" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.197802" + inkscape:cx="8.0827131" + inkscape:cy="9.3514852" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:window-width="1152" + inkscape:window-height="793" + inkscape:window-x="0" + inkscape:window-y="25" + inkscape:snap-global="false"> + <inkscape:grid + type="xygrid" + id="grid1400" /> + </sodipodi:namedview> + <metadata + id="metadata2165"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <path + style="fill:none;stroke:#000000;stroke-width:1.12710667000000009px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 1.4966449,5 c 0,-0.9081004 0,1.6409521 0,2.7575495 0,1.4141503 0,2.8283005 0,4.2424505" + id="path3197" + inkscape:transform-center-x="-0.38395373" /> + <path + style="fill:none;stroke:#000000;stroke-width:1.10537732000000011px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 16.146084,8.5134887 c -0.121831,0 -0.401989,0 -0.598171,0 -0.127609,0 -0.25522,0 -0.382829,0 -0.239268,0 -0.478537,0 -0.717804,0 -0.16348,0 0.09159,0 0.143561,0" + id="path3204" /> + <path + style="fill:#729fcf;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.35949695000000004px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 3.8025562,8.4684391 c 0.5980866,0 1.9734259,0 2.9365211,0 0.6264575,0 1.2529151,0 1.8793725,0 1.1746084,0 2.3492152,0 3.5238232,0 0.802551,0 -0.449649,0 -0.704765,0" + id="path3214" /> + <path + style="fill:#729fcf;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.09699999999999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 3.4703704,12.016263 c -0.00461,0.919965 0.00833,-1.662391 0.013999,-2.793578 0.00718,-1.4326262 0.014358,-2.8652528 0.021537,-4.2978799" + id="path3220" + inkscape:transform-center-x="1.4449532" + inkscape:transform-center-y="1.947532" /> + <path + style="fill:none;stroke:#000000;stroke-width:1.11496520000000010px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 14.575483,5.0049233 c 0,-0.910738 0,1.6457179 0,2.7655584 0,1.4182573 0,2.8365153 0,4.2547733" + id="path3222" /> + <path + sodipodi:type="arc" + style="fill:#4e9a06;fill-opacity:1;fill-rule:nonzero;stroke:#4e9a06;stroke-linejoin:round;stroke-opacity:1;opacity:1" + id="path3224" + sodipodi:cx="8.1990099" + sodipodi:cy="3.1608911" + sodipodi:rx="1.5316832" + sodipodi:ry="1.5316832" + d="M 9.7306931 3.1608911 A 1.5316832 1.5316832 0 1 1 6.6673267,3.1608911 A 1.5316832 1.5316832 0 1 1 9.7306931 3.1608911 z" + transform="matrix(1.3326023,0,0,1.3326023,-3.0745345,-1.504785)" /> + <path + sodipodi:type="arc" + style="fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4024);opacity:1" + id="path3250" + sodipodi:cx="7.5007424" + sodipodi:cy="2.4626236" + sodipodi:rx="0.51806933" + sodipodi:ry="0.51806933" + d="M 8.0188118 2.4626236 A 0.51806933 0.51806933 0 1 1 6.9826731,2.4626236 A 0.51806933 0.51806933 0 1 1 8.0188118 2.4626236 z" + transform="matrix(1.2433747,0,0,1.2433747,-2.0603915,-0.8322608)" /> + <path + sodipodi:type="arc" + style="fill:#ef2929;fill-opacity:1;fill-rule:nonzero;stroke:#ef2929;stroke-linejoin:round;stroke-opacity:1;opacity:1" + id="path4034" + sodipodi:cx="8.1990099" + sodipodi:cy="3.1608911" + sodipodi:rx="1.5316832" + sodipodi:ry="1.5316832" + d="M 9.7306931 3.1608911 A 1.5316832 1.5316832 0 1 1 6.6673267,3.1608911 A 1.5316832 1.5316832 0 1 1 9.7306931 3.1608911 z" + transform="matrix(1.3326023,0,0,1.3326023,-3.0379006,8.949671)" /> + <path + sodipodi:type="arc" + style="fill:#eeeeec;fill-opacity:0.9766355;fill-rule:nonzero;stroke:#eeeeec;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4024)" + id="path4036" + sodipodi:cx="7.5007424" + sodipodi:cy="2.4626236" + sodipodi:rx="0.51806933" + sodipodi:ry="0.51806933" + d="m 8.0188118,2.4626236 a 0.51806933,0.51806933 0 1 1 -1.0361387,0 0.51806933,0.51806933 0 1 1 1.0361387,0 z" + transform="matrix(1.3434079,0,0,1.3868861,-2.9768036,9.223731)" + inkscape:transform-center-x="-1.9821782" + inkscape:transform-center-y="-2.5352146" /> + <path + style="fill:none;stroke:#000000;stroke-width:1.19332730999999992px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 2.0185518,8.5 c -0.1419883,0 -0.4685017,0 -0.6971454,0 -0.1487238,0 -0.2974485,0 -0.44617232,0 -0.27885782,0 -0.55771648,0 -0.83657345,0 -0.19052916,0 0.106748,0 0.16731452,0" + id="path4052" /> + <path + sodipodi:type="arc" + style="color:#000000;fill:url(#radialGradient278);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000024;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block" + id="path12511" + sodipodi:cx="55" + sodipodi:cy="125" + sodipodi:rx="14.375" + sodipodi:ry="14.375" + d="M 69.375 125 A 14.375 14.375 0 1 1 40.625,125 A 14.375 14.375 0 1 1 69.375 125 z" + transform="matrix(0.3476829,0,0,0.344549,-7.1205012,-39.69775)" + inkscape:export-filename="/home/jimmac/ximian_art/icons/nautilus/suse93/stock_new-16.png" + inkscape:export-xdpi="33.852203" + inkscape:export-ydpi="33.852203" /> + <path + style="fill:#729fcf;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.09662747000000005px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 12.460945,12.022461 c -0.0046,0.919965 0.0083,-1.662391 0.014,-2.7935772 0.0072,-1.4326264 0.01436,-2.865253 0.02154,-4.2978801" + id="path3220-6" + inkscape:transform-center-x="1.4449532" + inkscape:transform-center-y="1.947532" /> + </g> +</svg> diff --git a/app/bin/bitmaps/SVG/switchm.svg b/app/bin/bitmaps/SVG/switchm.svg new file mode 100644 index 0000000..5064f4e --- /dev/null +++ b/app/bin/bitmaps/SVG/switchm.svg @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16px" + height="16px" + id="svg22" + sodipodi:version="0.32" + inkscape:version="0.46+devel" + sodipodi:docname="switchm.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs24"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective30" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.197802" + inkscape:cx="3.3817025" + inkscape:cy="8" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:snap-global="true" + showguides="false" + inkscape:window-width="1152" + inkscape:window-height="793" + inkscape:window-x="0" + inkscape:window-y="25" + inkscape:snap-grids="false"> + <inkscape:grid + type="xygrid" + id="grid32" /> + </sodipodi:namedview> + <metadata + id="metadata27"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <path + style="stroke-width:1px" + d="m 7.928713,9.9183168 0,2.2074262" + id="path58" /> + <path + style="fill:#00ff00;stroke-width:1px" + d="M 7.9737625,10.50396 C 6.1717823,15.549505 6.1717823,15.549505 6.1717823,15.549505" + id="path64" /> + <path + style="stroke-width:1px" + d="m 3.9643565,8.1613861 c 0.04505,6.3519799 0.04505,6.3519799 0.04505,6.3519799 l 0,0" + id="path34" /> + <rect + style="fill:#888a85;fill-opacity:1;stroke:#888a85;stroke-width:0.40673011999999997;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect818" + width="0.46330088" + height="9.3764267" + x="4.3126459" + y="8.0054026" + transform="matrix(0.96374482,-0.26682563,0.27375433,0.96179965,0,0)" /> + <rect + style="fill:#cc0000;fill-opacity:1;stroke:#ef2929;stroke-width:1.26722789;stroke-opacity:1" + id="rect28" + width="1.2625616" + height="6.4170895" + x="3.7529984" + y="2.3978021" + ry="0.62467241" + transform="matrix(0.96822704,-0.25007277,0.29182642,0.9564713,0,0)" /> + <path + sodipodi:type="star" + style="fill:#e9b96e;fill-opacity:0;stroke:#8f5902;stroke-width:0.57858991999999998;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;opacity:1;stroke-linejoin:bevel" + id="path828" + sodipodi:sides="3" + sodipodi:cx="4.1896038" + sodipodi:cy="15.369307" + sodipodi:r1="5.1855874" + sodipodi:r2="2.2652025" + sodipodi:arg1="-1.6142472" + sodipodi:arg2="-0.61129667" + inkscape:flatsided="false" + inkscape:rounded="0.079953976" + inkscape:randomized="0" + d="m 3.9643564,10.188614 c 0.3506863,-0.03083 1.8911358,3.583684 2.080232,3.880625 0.1976792,0.31042 2.8996375,3.361739 2.7442509,3.695344 -0.1486398,0.319121 -4.0491295,-0.15407 -4.4008365,-0.138779 -0.367671,0.01599 -4.36116969,0.83029 -4.57238707,0.528919 -0.2020465,-0.288286 2.15799367,-3.429614 2.32060457,-3.741847 0.1699918,-0.326405 1.4615321,-4.192029 1.8281361,-4.224262 z" + transform="matrix(0.80582785,0.02450966,-0.02314404,0.85337599,4.8820158,0.50401861)" /> + </g> +</svg> diff --git a/app/bin/bitmaps/SVG/switchmdel.svg b/app/bin/bitmaps/SVG/switchmdel.svg new file mode 100644 index 0000000..cdf6d81 --- /dev/null +++ b/app/bin/bitmaps/SVG/switchmdel.svg @@ -0,0 +1,191 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16px" + height="16px" + id="svg22" + sodipodi:version="0.32" + inkscape:version="0.46+devel" + sodipodi:docname="switchdel.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs24"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective30" /> + <inkscape:perspective + id="perspective1018" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient12512" + id="radialGradient278" + gradientUnits="userSpaceOnUse" + cx="55" + cy="125" + fx="55" + fy="125" + r="14.375" /> + <linearGradient + id="linearGradient12512"> + <stop + style="stop-color:#ffffff;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop12513" /> + <stop + style="stop-color:#fff520;stop-opacity:0.89108908;" + offset="0.50000000" + id="stop12517" /> + <stop + style="stop-color:#fff300;stop-opacity:0.0000000;" + offset="1.0000000" + id="stop12514" /> + </linearGradient> + <inkscape:perspective + id="perspective1220" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <inkscape:perspective + id="perspective1403" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <filter + color-interpolation-filters="sRGB" + inkscape:collect="always" + id="filter4590"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.56189548" + id="feGaussianBlur4592" /> + </filter> + <filter + color-interpolation-filters="sRGB" + inkscape:collect="always" + id="filter4262"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.34439655" + id="feGaussianBlur4264" /> + </filter> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.197802" + inkscape:cx="3.336653" + inkscape:cy="8" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:snap-global="true" + showguides="false" + inkscape:window-width="1152" + inkscape:window-height="793" + inkscape:window-x="0" + inkscape:window-y="25" + inkscape:snap-grids="false"> + <inkscape:grid + type="xygrid" + id="grid32" /> + </sodipodi:namedview> + <metadata + id="metadata27"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <path + style="stroke-width:1px" + d="m 7.928713,9.9183168 0,2.2074262" + id="path58" /> + <path + style="fill:#00ff00;stroke-width:1px" + d="M 7.9737625,10.50396 C 6.1717823,15.549505 6.1717823,15.549505 6.1717823,15.549505" + id="path64" /> + <path + style="stroke-width:1px" + d="m 3.9643565,8.1613861 c 0.04505,6.3519799 0.04505,6.3519799 0.04505,6.3519799 l 0,0" + id="path34" /> + <rect + style="fill:#888a85;fill-opacity:1;stroke:#555753;stroke-width:0.40673011999999997;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect818" + width="0.46330088" + height="9.3764267" + x="4.3126459" + y="8.0054026" + transform="matrix(0.96374482,-0.26682563,0.27375433,0.96179965,0,0)" /> + <rect + style="fill:#cc0000;fill-opacity:1;stroke:#cc0000;stroke-width:1.26722789000000002;stroke-opacity:1" + id="rect28" + width="1.2625616" + height="6.4170895" + x="3.7529984" + y="2.3978021" + ry="0.62467241" + transform="matrix(0.96822704,-0.25007277,0.29182642,0.9564713,0,0)" /> + <path + sodipodi:type="star" + style="fill:#555753;fill-opacity:0;stroke:#555753;stroke-width:0.57858991999999998;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;opacity:1;stroke-linejoin:bevel" + id="path828" + sodipodi:sides="3" + sodipodi:cx="4.1896038" + sodipodi:cy="15.369307" + sodipodi:r1="5.1855874" + sodipodi:r2="2.2652025" + sodipodi:arg1="-1.6142472" + sodipodi:arg2="-0.61129667" + inkscape:flatsided="false" + inkscape:rounded="0.079953976" + inkscape:randomized="0" + d="m 3.9643564,10.188614 c 0.3506863,-0.03083 1.8911358,3.583684 2.080232,3.880625 0.1976792,0.31042 2.8996375,3.361739 2.7442509,3.695344 -0.1486398,0.319121 -4.0491295,-0.15407 -4.4008365,-0.138779 -0.367671,0.01599 -4.36116969,0.83029 -4.57238707,0.528919 -0.2020465,-0.288286 2.15799367,-3.429614 2.32060457,-3.741847 0.1699918,-0.326405 1.4615321,-4.192029 1.8281361,-4.224262 z" + transform="matrix(0.80582785,0.02450966,-0.02314404,0.85337599,4.8820158,0.50401861)" /> + <path + style="fill:#ffbcbc;fill-opacity:0.9766355;fill-rule:nonzero;stroke:#fe0c28;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4262)" + id="path4120" + d="m 2.1083765,1.0866666 c 0.3160419,0.079008 0.5567699,0.3508227 0.7981747,0.5591244 0.7956372,0.7139196 1.5796754,1.4411212 2.349896,2.1824598 0.6499138,0.6205604 1.2682018,1.2755779 1.8426343,1.9666283 0.6226688,0.748476 1.2255882,1.5112389 1.8772649,2.2350751 0.5471768,0.583388 1.1018136,1.158457 1.6024486,1.782853 0.365044,0.4621018 0.694466,0.9496728 0.990408,1.4584538 0.232506,0.449982 0.447675,0.909843 0.640472,1.378286 0.154209,0.399023 0.297651,0.802103 0.454171,1.200243 0.08704,0.266431 0.208332,0.516603 0.343829,0.760705 0.05768,0.102268 0.126041,0.197744 0.19214,0.294426 l -0.562289,0.33066 C 12.575448,15.134139 12.511364,15.033793 12.453559,14.929742 12.323035,14.677921 12.209128,14.420654 12.126691,14.148243 11.98245,13.745212 11.838419,13.342092 11.686095,12.942025 11.502136,12.472403 11.290547,12.013443 11.060629,11.564604 10.773509,11.053665 10.447506,10.568629 10.088083,10.105381 9.6055822,9.4733582 9.0792898,8.8791082 8.5426675,8.2925592 7.8905564,7.5664167 7.2757118,6.8098751 6.6357752,6.0733085 6.0572258,5.3923245 5.4722711,4.7151577 4.836516,4.0863538 4.0714296,3.3326113 3.282496,2.6017521 2.4316566,1.9446183 2.1695496,1.7564741 1.9012595,1.5005644 1.5674068,1.4797042 L 2.1083765,1.0866666 z" + transform="matrix(0.87171898,-0.19675615,0.18155103,0.94472654,-0.53378643,2.7148317)" /> + <path + style="fill:#ffbcbc;fill-opacity:0.9766355;fill-rule:nonzero;stroke:#ff0000;stroke-width:0.97403181;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter4590)" + id="path4362" + d="m 1.5464221,15.057868 c 0.095256,-0.115984 0.1818012,-0.243318 0.2691184,-0.369599 0.2160667,-0.2866 0.4316006,-0.57392 0.6508708,-0.856237 0.3938164,-0.5083 0.8002516,-0.997898 1.2109831,-1.481811 C 4.18657,11.763082 4.7056799,11.191732 5.2172668,10.608231 5.7415195,9.973961 6.251661,9.3199607 6.7849327,8.6988507 7.3346339,8.0593107 7.9017849,7.4469497 8.4600842,6.8208737 8.988553,6.1992814 9.5345425,5.6083469 10.107996,5.0618773 10.881154,4.3333358 11.707682,3.7161676 12.537251,3.1102247 13.202384,2.6342692 13.958139,2.0835333 14.453029,1.6763685 14.675268,1.4982605 14.888667,1.3029881 15.084722,1.0771029 L 15.57239,0.81440911 C 15.360406,1.0545024 15.130415,1.262403 14.890817,1.4513133 14.011357,2.1607412 13.102516,2.8014042 12.204243,3.4670896 11.636242,3.8841319 11.070327,4.321432 10.537926,4.8362955 9.95863,5.3815141 9.4067624,5.9714036 8.8756123,6.5975864 8.3197762,7.2219361 7.7556183,7.8334137 7.2079153,8.4704547 c -0.536476,0.621161 -1.0492088,1.276444 -1.5747588,1.9136613 -0.5044954,0.579852 -1.0139467,1.151417 -1.5193012,1.729825 -0.4009146,0.475772 -0.8000084,0.95336 -1.1786225,1.460844 -0.2095459,0.275283 -0.4137215,0.557194 -0.6211264,0.83526 -0.081016,0.121497 -0.1598963,0.24547 -0.2460079,0.360695 l -0.5216764,0.287128 z" + transform="matrix(0.89825723,0.15347844,-0.14440383,0.95470545,1.1384012,-0.68392346)" /> + </g> +</svg> diff --git a/app/bin/bitmaps/SVG/switchmedit.svg b/app/bin/bitmaps/SVG/switchmedit.svg new file mode 100644 index 0000000..9a2008a --- /dev/null +++ b/app/bin/bitmaps/SVG/switchmedit.svg @@ -0,0 +1,214 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16px" + height="16px" + id="svg22" + sodipodi:version="0.32" + inkscape:version="0.46+devel" + sodipodi:docname="switchmnew.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs24"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective30" /> + <inkscape:perspective + id="perspective1018" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient12512" + id="radialGradient278" + gradientUnits="userSpaceOnUse" + cx="55" + cy="125" + fx="55" + fy="125" + r="14.375" /> + <linearGradient + id="linearGradient12512"> + <stop + style="stop-color:#ffffff;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop12513" /> + <stop + style="stop-color:#fff520;stop-opacity:0.89108908;" + offset="0.50000000" + id="stop12517" /> + <stop + style="stop-color:#fff300;stop-opacity:0.0000000;" + offset="1.0000000" + id="stop12514" /> + </linearGradient> + <radialGradient + r="14.375" + fy="125" + fx="55" + cy="125" + cx="55" + gradientUnits="userSpaceOnUse" + id="radialGradient1028" + xlink:href="#linearGradient12512" + inkscape:collect="always" /> + <inkscape:perspective + id="perspective1220" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2984" + id="radialGradient12692" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.66077,-0.5114749,0.3584765,1.1380119,-52.478445,-7.4863015)" + cx="29.053354" + cy="27.640751" + fx="29.053354" + fy="27.640751" + r="3.2408545" /> + <linearGradient + id="linearGradient2984" + inkscape:collect="always"> + <stop + id="stop2986" + offset="0" + style="stop-color:#e7e2b8;stop-opacity:1;" /> + <stop + id="stop2988" + offset="1" + style="stop-color:#e7e2b8;stop-opacity:0;" /> + </linearGradient> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.197802" + inkscape:cx="3.3817025" + inkscape:cy="8" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:snap-global="true" + showguides="false" + inkscape:window-width="1152" + inkscape:window-height="793" + inkscape:window-x="0" + inkscape:window-y="25" + inkscape:snap-grids="false"> + <inkscape:grid + type="xygrid" + id="grid32" /> + </sodipodi:namedview> + <metadata + id="metadata27"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <path + style="stroke-width:1px" + d="m 7.928713,9.9183168 0,2.2074262" + id="path58" /> + <path + style="fill:#00ff00;stroke-width:1px" + d="M 7.9737625,10.50396 C 6.1717823,15.549505 6.1717823,15.549505 6.1717823,15.549505" + id="path64" /> + <path + style="stroke-width:1px" + d="m 3.9643565,8.1613861 c 0.04505,6.3519799 0.04505,6.3519799 0.04505,6.3519799 l 0,0" + id="path34" /> + <rect + style="fill:#888a85;fill-opacity:1;stroke:#888a85;stroke-width:0.40673011999999997;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect818" + width="0.46330088" + height="9.3764267" + x="4.3126459" + y="8.0054026" + transform="matrix(0.96374482,-0.26682563,0.27375433,0.96179965,0,0)" /> + <rect + style="fill:#cc0000;fill-opacity:1;stroke:#ef2929;stroke-width:1.26722789;stroke-opacity:1" + id="rect28" + width="1.2625616" + height="6.4170895" + x="3.7529984" + y="2.3978021" + ry="0.62467241" + transform="matrix(0.96822704,-0.25007277,0.29182642,0.9564713,0,0)" /> + <path + sodipodi:type="star" + style="fill:#e9b96e;fill-opacity:0;stroke:#8f5902;stroke-width:0.57858991999999998;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;opacity:1;stroke-linejoin:bevel" + id="path828" + sodipodi:sides="3" + sodipodi:cx="4.1896038" + sodipodi:cy="15.369307" + sodipodi:r1="5.1855874" + sodipodi:r2="2.2652025" + sodipodi:arg1="-1.6142472" + sodipodi:arg2="-0.61129667" + inkscape:flatsided="false" + inkscape:rounded="0.079953976" + inkscape:randomized="0" + d="m 3.9643564,10.188614 c 0.3506863,-0.03083 1.8911358,3.583684 2.080232,3.880625 0.1976792,0.31042 2.8996375,3.361739 2.7442509,3.695344 -0.1486398,0.319121 -4.0491295,-0.15407 -4.4008365,-0.138779 -0.367671,0.01599 -4.36116969,0.83029 -4.57238707,0.528919 -0.2020465,-0.288286 2.15799367,-3.429614 2.32060457,-3.741847 0.1699918,-0.326405 1.4615321,-4.192029 1.8281361,-4.224262 z" + transform="matrix(0.80582785,0.02450966,-0.02314404,0.85337599,4.8820158,0.50401861)" /> + <g + id="g12687" + transform="translate(0.45099069,0.74942062)"> + <path + sodipodi:nodetypes="cccccc" + id="path2960" + d="M 3.1781506,12.311899 5.380053,8.1740123 15.072589,-0.80795299 C 16.698027,-2.0773811 18.61549,0.17677763 17.269305,1.5908536 L 7.5460431,10.351631 3.1781506,12.311899 z" + style="color:#000000;fill:#cb9022;fill-opacity:1;fill-rule:evenodd;stroke:#5c410c;stroke-width:0.55234361;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + <path + sodipodi:nodetypes="cccc" + id="path2982" + d="M 3.6881537,11.914091 5.4496756,8.6037819 c 0.9955939,0.193125 1.676718,0.8098537 1.7219019,1.7569811 l -3.4834238,1.553328 z" + style="color:#000000;fill:url(#radialGradient12692);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + <path + sodipodi:nodetypes="ccccc" + id="path3004" + d="M 7.1926465,10.395805 7.156667,9.7679658 17.407525,0.54092818 c 0,0 0.0635,0.50320052 0.01347,0.63421122 L 7.1926465,10.395805 z" + style="color:#000000;fill:#000000;fill-opacity:0.36363639;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + </g> + </g> +</svg> diff --git a/app/bin/bitmaps/SVG/switchmnew.svg b/app/bin/bitmaps/SVG/switchmnew.svg new file mode 100644 index 0000000..4f8a2e6 --- /dev/null +++ b/app/bin/bitmaps/SVG/switchmnew.svg @@ -0,0 +1,172 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16px" + height="16px" + id="svg22" + sodipodi:version="0.32" + inkscape:version="0.46+devel" + sodipodi:docname="switchm.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs24"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective30" /> + <inkscape:perspective + id="perspective1018" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient12512" + id="radialGradient278" + gradientUnits="userSpaceOnUse" + cx="55" + cy="125" + fx="55" + fy="125" + r="14.375" /> + <linearGradient + id="linearGradient12512"> + <stop + style="stop-color:#ffffff;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop12513" /> + <stop + style="stop-color:#fff520;stop-opacity:0.89108908;" + offset="0.50000000" + id="stop12517" /> + <stop + style="stop-color:#fff300;stop-opacity:0.0000000;" + offset="1.0000000" + id="stop12514" /> + </linearGradient> + <radialGradient + r="14.375" + fy="125" + fx="55" + cy="125" + cx="55" + gradientUnits="userSpaceOnUse" + id="radialGradient1028" + xlink:href="#linearGradient12512" + inkscape:collect="always" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.197802" + inkscape:cx="3.3817025" + inkscape:cy="8" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:snap-global="true" + showguides="false" + inkscape:window-width="1152" + inkscape:window-height="793" + inkscape:window-x="0" + inkscape:window-y="25" + inkscape:snap-grids="false"> + <inkscape:grid + type="xygrid" + id="grid32" /> + </sodipodi:namedview> + <metadata + id="metadata27"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <path + style="stroke-width:1px" + d="m 7.928713,9.9183168 0,2.2074262" + id="path58" /> + <path + style="fill:#00ff00;stroke-width:1px" + d="M 7.9737625,10.50396 C 6.1717823,15.549505 6.1717823,15.549505 6.1717823,15.549505" + id="path64" /> + <path + style="stroke-width:1px" + d="m 3.9643565,8.1613861 c 0.04505,6.3519799 0.04505,6.3519799 0.04505,6.3519799 l 0,0" + id="path34" /> + <rect + style="fill:#888a85;fill-opacity:1;stroke:#888a85;stroke-width:0.40673011999999997;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect818" + width="0.46330088" + height="9.3764267" + x="4.3126459" + y="8.0054026" + transform="matrix(0.96374482,-0.26682563,0.27375433,0.96179965,0,0)" /> + <rect + style="fill:#cc0000;fill-opacity:1;stroke:#ef2929;stroke-width:1.26722789;stroke-opacity:1" + id="rect28" + width="1.2625616" + height="6.4170895" + x="3.7529984" + y="2.3978021" + ry="0.62467241" + transform="matrix(0.96822704,-0.25007277,0.29182642,0.9564713,0,0)" /> + <path + sodipodi:type="star" + style="fill:#e9b96e;fill-opacity:0;stroke:#8f5902;stroke-width:0.57858991999999998;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;opacity:1;stroke-linejoin:bevel" + id="path828" + sodipodi:sides="3" + sodipodi:cx="4.1896038" + sodipodi:cy="15.369307" + sodipodi:r1="5.1855874" + sodipodi:r2="2.2652025" + sodipodi:arg1="-1.6142472" + sodipodi:arg2="-0.61129667" + inkscape:flatsided="false" + inkscape:rounded="0.079953976" + inkscape:randomized="0" + d="m 3.9643564,10.188614 c 0.3506863,-0.03083 1.8911358,3.583684 2.080232,3.880625 0.1976792,0.31042 2.8996375,3.361739 2.7442509,3.695344 -0.1486398,0.319121 -4.0491295,-0.15407 -4.4008365,-0.138779 -0.367671,0.01599 -4.36116969,0.83029 -4.57238707,0.528919 -0.2020465,-0.288286 2.15799367,-3.429614 2.32060457,-3.741847 0.1699918,-0.326405 1.4615321,-4.192029 1.8281361,-4.224262 z" + transform="matrix(0.80582785,0.02450966,-0.02314404,0.85337599,4.8820158,0.50401861)" /> + <path + sodipodi:type="arc" + style="color:#000000;fill:url(#radialGradient1028);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000024;marker:none;visibility:visible;display:block" + id="path12511" + sodipodi:cx="55" + sodipodi:cy="125" + sodipodi:rx="14.375" + sodipodi:ry="14.375" + d="m 69.375,125 a 14.375,14.375 0 1 1 -28.75,0 14.375,14.375 0 1 1 28.75,0 z" + transform="matrix(0.3476829,0,0,0.344549,-7.4547377,-39.36714)" + inkscape:export-filename="/home/jimmac/ximian_art/icons/nautilus/suse93/stock_new-16.png" + inkscape:export-xdpi="33.852203" + inkscape:export-ydpi="33.852203" /> + </g> +</svg> diff --git a/app/bin/bitmaps/SVG/tipofday.svg b/app/bin/bitmaps/SVG/tipofday.svg new file mode 100644 index 0000000..c83540d --- /dev/null +++ b/app/bin/bitmaps/SVG/tipofday.svg @@ -0,0 +1,1176 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:ns="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + sodipodi:docname="tipofday.svg" + sodipodi:docbase="C:\Dokumente und Einstellungen\MF\Eigene Dateien\xtc\source\xtrkcad\app\bin\bitmaps\SVG" + inkscape:version="0.45.1" + sodipodi:version="0.32" + id="svg19655" + height="63.500313" + width="51" + inkscape:export-filename="/home/jimmac/Desktop/poing.png" + inkscape:export-xdpi="392.72742" + inkscape:export-ydpi="392.72742" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.0"> + <defs + id="defs3"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 24 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="48 : 24 : 1" + inkscape:persp3d-origin="24 : 16 : 1" + id="perspective155" /> + <linearGradient + inkscape:collect="always" + id="linearGradient3300"> + <stop + style="stop-color:#4c4c28;stop-opacity:1;" + offset="0" + id="stop3302" /> + <stop + style="stop-color:#4c4c28;stop-opacity:0;" + offset="1" + id="stop3304" /> + </linearGradient> + <linearGradient + id="linearGradient3311"> + <stop + id="stop3313" + offset="0" + style="stop-color:#d6d7a5;stop-opacity:1;" /> + <stop + id="stop3315" + offset="1.0000000" + style="stop-color:#8e8f6d;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient3265"> + <stop + id="stop3267" + offset="0" + style="stop-color:#929470;stop-opacity:1;" /> + <stop + style="stop-color:#60614a;stop-opacity:1.0000000;" + offset="0.26470590" + id="stop3269" /> + <stop + id="stop3271" + offset="0.63235295" + style="stop-color:#f3f5ba;stop-opacity:1.0000000;" /> + <stop + id="stop3273" + offset="1.0000000" + style="stop-color:#929470;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient3175" + inkscape:collect="always"> + <stop + id="stop3177" + offset="0" + style="stop-color:#f1f3ff;stop-opacity:1;" /> + <stop + id="stop3179" + offset="1" + style="stop-color:#f1f3ff;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient2399"> + <stop + style="stop-color:#929470;stop-opacity:1;" + offset="0" + id="stop2401" /> + <stop + id="stop2407" + offset="0.26470590" + style="stop-color:#fcffc1;stop-opacity:1.0000000;" /> + <stop + style="stop-color:#f3f5ba;stop-opacity:1.0000000;" + offset="0.63235295" + id="stop2409" /> + <stop + style="stop-color:#929470;stop-opacity:1.0000000;" + offset="1.0000000" + id="stop2403" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient6339"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop6341" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop6343" /> + </linearGradient> + <linearGradient + id="linearGradient20428"> + <stop + id="stop20430" + offset="0.0000000" + style="stop-color:#a3a3a3;stop-opacity:1.0000000;" /> + <stop + id="stop20432" + offset="1" + style="stop-color:#b5b5b5;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient20393"> + <stop + id="stop20395" + offset="0" + style="stop-color:#ffffff;stop-opacity:1;" /> + <stop + style="stop-color:#ffffff;stop-opacity:0.44117647;" + offset="0.41176471" + id="stop2427" /> + <stop + id="stop20397" + offset="1.0000000" + style="stop-color:#000000;stop-opacity:0.48039216;" /> + </linearGradient> + <linearGradient + id="linearGradient20210"> + <stop + id="stop20212" + offset="0.0000000" + style="stop-color:#000000;stop-opacity:0.51546389;" /> + <stop + style="stop-color:#000000;stop-opacity:0.14432989;" + offset="0.55172414" + id="stop20218" /> + <stop + id="stop20214" + offset="1" + style="stop-color:#000000;stop-opacity:0;" /> + </linearGradient> + <radialGradient + gradientUnits="userSpaceOnUse" + fy="11.4873" + fx="17.8335" + r="22.709299" + cy="11.4873" + cx="17.8335" + id="aigrd7"> + <stop + id="stop19512" + style="stop-color:#ffffff;stop-opacity:0.17525773;" + offset="0.0000000" /> + <stop + id="stop19514" + style="stop-color:#709ac8;stop-opacity:1.0000000;" + offset="0.88200003" /> + <stop + id="stop19516" + style="stop-color:#6f96dd;stop-opacity:1.0000000;" + offset="1.0000000" /> + </radialGradient> + <linearGradient + y2="43.165001" + x2="26.4785" + y1="43.165001" + x1="23.124001" + gradientUnits="userSpaceOnUse" + id="aigrd1"> + <stop + id="stop19415" + style="stop-color:#686868" + offset="5.618000e-003" /> + <stop + id="stop19417" + style="stop-color:#777777" + offset="3.012137e-002" /> + <stop + id="stop19419" + style="stop-color:#929292" + offset="8.366583e-002" /> + <stop + id="stop19421" + style="stop-color:#A7A7A7" + offset="0.1422" /> + <stop + id="stop19423" + style="stop-color:#B6B6B6" + offset="0.2074" /> + <stop + id="stop19425" + style="stop-color:#BEBEBE" + offset="0.2846" /> + <stop + id="stop19427" + style="stop-color:#C1C1C1" + offset="0.4045" /> + <stop + id="stop19429" + style="stop-color:#BCBCBC" + offset="0.4962" /> + <stop + id="stop19431" + style="stop-color:#ADADAD" + offset="0.6057" /> + <stop + id="stop19433" + style="stop-color:#959595" + offset="0.7245" /> + <stop + id="stop19435" + style="stop-color:#747474" + offset="0.8497" /> + <stop + id="stop19437" + style="stop-color:#494949" + offset="0.9789" /> + <stop + id="stop19439" + style="stop-color:#414141" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient19894" + gradientUnits="userSpaceOnUse" + x1="18.9951" + y1="37.226601" + x2="30.169901" + y2="37.226601"> + <stop + offset="5.618000e-003" + style="stop-color:#A3A349" + id="stop19896" /> + <stop + offset="2.078677e-002" + style="stop-color:#ACAC54" + id="stop19898" /> + <stop + offset="6.600059e-002" + style="stop-color:#C1C172" + id="stop19900" /> + <stop + offset="0.1148" + style="stop-color:#D4D68E" + id="stop19902" /> + <stop + offset="0.1677" + style="stop-color:#E2E4A6" + id="stop19904" /> + <stop + offset="0.2265" + style="stop-color:#EDF0B8" + id="stop19906" /> + <stop + offset="0.2963" + style="stop-color:#F3F6C3" + id="stop19908" /> + <stop + offset="0.4045" + style="stop-color:#F5F8C7" + id="stop19910" /> + <stop + offset="0.5239" + style="stop-color:#EEF0BE" + id="stop19912" /> + <stop + offset="0.6666" + style="stop-color:#DBDDA9" + id="stop19914" /> + <stop + offset="0.8211" + style="stop-color:#BEBD88" + id="stop19916" /> + <stop + offset="0.9832" + style="stop-color:#989564" + id="stop19918" /> + <stop + offset="1" + style="stop-color:#949160" + id="stop19920" /> + </linearGradient> + <linearGradient + gradientTransform="matrix(1.639127,0,0,1.639127,-15.97035,-29.79355)" + y2="43.165001" + x2="26.4785" + y1="43.165001" + x1="23.124001" + gradientUnits="userSpaceOnUse" + id="linearGradient20109" + xlink:href="#aigrd1" + inkscape:collect="always" /> + <radialGradient + gradientUnits="userSpaceOnUse" + r="7.8289828" + fy="74.20993" + fx="14.772334" + cy="74.20993" + cx="14.772334" + gradientTransform="scale(1.764278,0.566804)" + id="radialGradient20216" + xlink:href="#linearGradient20210" + inkscape:collect="always" /> + <linearGradient + y2="36.726292" + x2="32.096882" + y1="10.061084" + x1="16.998856" + gradientTransform="matrix(1.140494,0,0,0.926002,0.27233,-3.24717)" + gradientUnits="userSpaceOnUse" + id="linearGradient7708" + xlink:href="#linearGradient6339" + inkscape:collect="always" /> + <radialGradient + r="33.93409" + fy="29.869318" + fx="68.137589" + cy="29.869318" + cx="68.137589" + gradientTransform="matrix(0.55129,0,0,0.766034,-10.48701,3.514312)" + gradientUnits="userSpaceOnUse" + id="radialGradient7720" + xlink:href="#aigrd7" + inkscape:collect="always" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="3.8557322" + x2="-5.2517161" + y1="16.651863" + x1="37.940434" + gradientTransform="matrix(0.894129,0,0,0.98523,1.515981,2.4498e-2)" + id="linearGradient3181" + xlink:href="#linearGradient3175" + inkscape:collect="always" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient20393" + id="linearGradient1700" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.6293,0,0,1.589068,50.68808,3.804378)" + x1="30.620375" + y1="10.313651" + x2="32.16608" + y2="18.162935" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient20393" + id="linearGradient1702" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.6293,0,0,1.589068,1.411612,3.929378)" + x1="30.620375" + y1="10.313651" + x2="32.16608" + y2="18.162935" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient20428" + id="linearGradient1704" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.985083,0,0,0.503757,1.786612,4.554378)" + x1="14.637301" + y1="31.504122" + x2="9.3648205" + y2="32.25098" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient1725" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient1727" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient1729" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient1731" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3311" + id="linearGradient2516" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.60344,0,0,0.549396,0.614167,2.4498e-2)" + x1="17.879995" + y1="55.362793" + x2="11.906206" + y2="54.863026" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3265" + id="linearGradient2518" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.905728,-4.386156e-2,0.18951,-0.963437,0.614167,2.4498e-2)" + x1="-29.007195" + y1="-29.799353" + x2="-37.641232" + y2="-29.598314" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient2522" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.618682,-0.132027,6.262726e-2,0.741184,31.12021,8.30041)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient2524" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.618682,-0.132027,6.262726e-2,0.741184,31.12021,8.30041)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient2529" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.566621,2.988977e-2,-0.118557,0.656541,36.18544,20.08311)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient2531" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.566621,2.988977e-2,-0.118557,0.656541,36.18544,20.08311)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3300" + id="linearGradient3306" + gradientTransform="scale(1.002656,0.997352)" + x1="24.613028" + y1="31.146202" + x2="24.613028" + y2="26.739624" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3311" + id="linearGradient3127" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.60344,0,0,0.549396,0.614167,2.4498e-2)" + x1="17.879995" + y1="55.362793" + x2="11.906206" + y2="54.863026" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3265" + id="linearGradient3129" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.905728,-4.386156e-2,0.18951,-0.963437,0.614167,2.4498e-2)" + x1="-29.007195" + y1="-29.799353" + x2="-37.641232" + y2="-29.598314" /> + <radialGradient + inkscape:collect="always" + xlink:href="#aigrd7" + id="radialGradient3131" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.55129,0,0,0.766034,-10.48701,3.514312)" + cx="68.137589" + cy="29.869318" + fx="68.137589" + fy="29.869318" + r="33.93409" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient6339" + id="linearGradient3133" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.140494,0,0,0.926002,0.27233,-3.24717)" + x1="16.998856" + y1="10.061084" + x2="32.096882" + y2="36.726292" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3175" + id="linearGradient3135" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.894129,0,0,0.98523,1.515981,2.4498e-2)" + x1="37.940434" + y1="16.651863" + x2="-5.2517161" + y2="3.8557322" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3311" + id="linearGradient3157" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.60344,0,0,0.549396,0.614167,2.4498e-2)" + x1="17.879995" + y1="55.362793" + x2="11.906206" + y2="54.863026" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3265" + id="linearGradient3159" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.905728,-4.386156e-2,0.18951,-0.963437,0.614167,2.4498e-2)" + x1="-29.007195" + y1="-29.799353" + x2="-37.641232" + y2="-29.598314" /> + <radialGradient + inkscape:collect="always" + xlink:href="#aigrd7" + id="radialGradient3161" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.55129,0,0,0.766034,-10.48701,3.514312)" + cx="68.137589" + cy="29.869318" + fx="68.137589" + fy="29.869318" + r="33.93409" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3175" + id="linearGradient3163" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.894129,0,0,0.98523,1.515981,2.4498e-2)" + x1="37.940434" + y1="16.651863" + x2="-5.2517161" + y2="3.8557322" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient20393" + id="linearGradient3165" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.6293,0,0,1.589068,50.68808,3.804378)" + x1="30.620375" + y1="10.313651" + x2="32.16608" + y2="18.162935" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient20393" + id="linearGradient3167" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.6293,0,0,1.589068,1.411612,3.929378)" + x1="30.620375" + y1="10.313651" + x2="32.16608" + y2="18.162935" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient20428" + id="linearGradient3169" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.985083,0,0,0.503757,1.786612,4.554378)" + x1="14.637301" + y1="31.504122" + x2="9.3648205" + y2="32.25098" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient6339" + id="linearGradient3171" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.140494,0,0,0.926002,0.27233,-3.24717)" + x1="16.998856" + y1="10.061084" + x2="32.096882" + y2="36.726292" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3300" + id="linearGradient3185" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.002656,0.997352)" + x1="24.613028" + y1="31.146202" + x2="24.613028" + y2="26.739624" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient3187" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.566621,2.988977e-2,-0.118557,0.656541,36.18544,20.08311)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient3189" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.566621,2.988977e-2,-0.118557,0.656541,36.18544,20.08311)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient3191" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient3193" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient3195" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient3197" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient3199" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.618682,-0.132027,6.262726e-2,0.741184,31.12021,8.30041)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient3201" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.618682,-0.132027,6.262726e-2,0.741184,31.12021,8.30041)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#aigrd1" + id="linearGradient4100" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.639127,0,0,1.639127,-15.97035,-29.79355)" + x1="23.124001" + y1="43.165001" + x2="26.4785" + y2="43.165001" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3300" + id="linearGradient4102" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.002656,0.997352)" + x1="24.613028" + y1="31.146202" + x2="24.613028" + y2="26.739624" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient4104" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.566621,2.988977e-2,-0.118557,0.656541,36.18544,20.08311)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient4106" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.566621,2.988977e-2,-0.118557,0.656541,36.18544,20.08311)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient4108" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient4110" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient4112" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient4114" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.02645,0.974232)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient19894" + id="linearGradient4116" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.618682,-0.132027,6.262726e-2,0.741184,31.12021,8.30041)" + x1="-22.87417" + y1="38.675991" + x2="-4.3908315" + y2="38.675991" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2399" + id="linearGradient4118" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.618682,-0.132027,6.262726e-2,0.741184,31.12021,8.30041)" + x1="-10.480865" + y1="39.033951" + x2="-23.851389" + y2="39.142845" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3311" + id="linearGradient4120" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.60344,0,0,0.549396,0.614167,2.4498e-2)" + x1="17.879995" + y1="55.362793" + x2="11.906206" + y2="54.863026" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3265" + id="linearGradient4122" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.905728,-4.386156e-2,0.18951,-0.963437,0.614167,2.4498e-2)" + x1="-29.007195" + y1="-29.799353" + x2="-37.641232" + y2="-29.598314" /> + <radialGradient + inkscape:collect="always" + xlink:href="#aigrd7" + id="radialGradient4124" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.55129,0,0,0.766034,-10.48701,3.514312)" + cx="68.137589" + cy="29.869318" + fx="68.137589" + fy="29.869318" + r="33.93409" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3175" + id="linearGradient4126" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.894129,0,0,0.98523,1.515981,2.4498e-2)" + x1="37.940434" + y1="16.651863" + x2="-5.2517161" + y2="3.8557322" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient20393" + id="linearGradient4128" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.6293,0,0,1.589068,50.68808,3.804378)" + x1="30.620375" + y1="10.313651" + x2="32.16608" + y2="18.162935" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient20393" + id="linearGradient4130" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.6293,0,0,1.589068,1.411612,3.929378)" + x1="30.620375" + y1="10.313651" + x2="32.16608" + y2="18.162935" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient20428" + id="linearGradient4132" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.985083,0,0,0.503757,1.786612,4.554378)" + x1="14.637301" + y1="31.504122" + x2="9.3648205" + y2="32.25098" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient6339" + id="linearGradient4134" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.140494,0,0,0.926002,0.27233,-3.24717)" + x1="16.998856" + y1="10.061084" + x2="32.096882" + y2="36.726292" /> + </defs> + <sodipodi:namedview + inkscape:window-y="81" + inkscape:window-x="212" + inkscape:window-height="818" + inkscape:window-width="1060" + inkscape:document-units="px" + inkscape:grid-bbox="true" + showgrid="true" + inkscape:current-layer="layer1" + inkscape:cy="-15.580292" + inkscape:cx="-132.96706" + inkscape:zoom="1" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + borderopacity="0.55294118" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + inkscape:showpageshadow="false"> + <inkscape:grid + id="GridFromPre046Settings" + type="xygrid" + originx="0px" + originy="0px" + spacingx="1px" + spacingy="1px" + color="#0000ff" + empcolor="#0000ff" + opacity="0.2" + empopacity="0.4" + empspacing="4" /> + </sodipodi:namedview> + <metadata + id="metadata4"> + <rdf:RDF> + <ns:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title>Info</dc:title> + <dc:creator> + <ns:Agent> + <dc:title>Jakub Steiner</dc:title> + </ns:Agent> + </dc:creator> + <dc:subject> + <rdf:Bag> + <rdf:li>dialog</rdf:li> + <rdf:li>info</rdf:li> + </rdf:Bag> + </dc:subject> + <dc:source>http://jimmac.musichall.cz</dc:source> + <ns:license + rdf:resource="http://creativecommons.org/licenses/publicdomain/" /> + <dc:contributor> + <ns:Agent> + <dc:title>Garrett LeSage</dc:title> + </ns:Agent> + </dc:contributor> + </ns:Work> + <ns:License + rdf:about="http://creativecommons.org/licenses/publicdomain/"> + <ns:permits + rdf:resource="http://creativecommons.org/ns#Reproduction" /> + <ns:permits + rdf:resource="http://creativecommons.org/ns#Distribution" /> + <ns:permits + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> + </ns:License> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + inkscape:label="Layer 1" + id="layer1" + transform="translate(8.5,-1.9996886)"> + <path + transform="matrix(1.197183,0,0,1.098591,-6.201582,-3.209507)" + d="M 39.875 42.0625 A 13.8125 4.4375 0 1 1 12.25,42.0625 A 13.8125 4.4375 0 1 1 39.875 42.0625 z" + sodipodi:ry="4.4375" + sodipodi:rx="13.8125" + sodipodi:cy="42.0625" + sodipodi:cx="26.0625" + id="path20208" + style="opacity:0.8;color:#000000;fill:url(#radialGradient20216);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + sodipodi:type="arc" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + <g + id="g4076" + transform="translate(0,1)" + inkscape:r_cx="true" + inkscape:r_cy="true"> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + transform="matrix(1.075823,0,0,0.937493,-2.551335,3.047213)" + id="path19509" + d="M 21.893504,38.885945 L 21.893504,40.36116 C 21.893504,41.836375 23.204807,43.147679 24.680022,43.147679 C 26.155237,43.147679 27.466539,41.836375 27.466539,40.36116 L 27.466539,38.885945 L 21.893504,38.885945 z " + style="fill:url(#linearGradient4100);fill-rule:nonzero;stroke:#565656;stroke-miterlimit:4;stroke-opacity:1" /> + <g + inkscape:r_cy="true" + inkscape:r_cx="true" + transform="matrix(0.989073,0,0,0.993556,-0.408739,7.920479e-3)" + id="g3173"> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + sodipodi:nodetypes="cccccccscccccccs" + id="path3209" + d="M 24.511725,27.668867 C 21.208844,27.660897 17.463275,28.632054 19.492913,30.467931 C 18.98969,30.670934 18.270371,31.124313 18.355167,32.185222 C 18.401983,32.739286 18.989243,33.079394 19.79236,33.32911 C 18.881908,33.967722 18.302581,34.642557 18.355167,35.264921 C 18.401438,35.812525 18.976334,36.187531 19.76303,36.43814 C 18.875519,37.069403 18.303301,37.760121 18.355167,38.373951 C 18.434436,39.312088 20.457743,40.362928 24.838928,40.2419 C 27.993329,40.155914 30.776913,39.590514 30.996599,38.373951 C 31.082862,37.896248 30.691907,37.450531 30.087355,37.05408 C 30.539926,36.597918 30.85698,36.135242 30.820616,35.704878 C 30.774128,35.154694 30.205993,34.781923 29.412754,34.53166 C 30.300265,33.900397 30.872482,33.209679 30.820616,32.595849 C 30.774128,32.045664 30.205993,31.702225 29.412754,31.45196 C 30.310848,30.817288 30.872816,30.133928 30.820616,29.516149 C 30.762593,28.829446 27.61599,27.676358 24.511725,27.668867 z " + style="color:#000000;fill:#aeae57;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4102);stroke-width:2.01752925;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" /> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + sodipodi:nodetypes="csccc" + id="path3183" + d="M 30.920208,38.329767 C 30.700522,39.546331 27.591422,40.232861 22.615132,39.983673 C 19.463507,39.825856 19.283163,38.944055 19.502848,37.727491 C 19.722534,36.510926 22.458318,35.65848 25.609509,35.824708 C 28.7607,35.990936 31.139893,37.113203 30.920208,38.329767 z " + style="color:#000000;fill:url(#linearGradient4104);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4106);stroke-width:0.08906282;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" /> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + sodipodi:type="arc" + style="color:#000000;fill:url(#linearGradient4108);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4110);stroke-width:0.13035245;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + id="path1603" + sodipodi:cx="-13.87697" + sodipodi:cy="27.228739" + sodipodi:rx="10.341436" + sodipodi:ry="3.2703688" + d="M -3.5355339 27.228739 A 10.341436 3.2703688 0 1 1 -24.218407,27.228739 A 10.341436 3.2703688 0 1 1 -3.5355339 27.228739 z" + transform="matrix(0.60274,-0.128625,6.428372e-2,0.760788,31.12021,14.49141)" /> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + transform="matrix(0.60274,-0.128625,6.428372e-2,0.760788,31.12021,11.39591)" + d="M -3.5355339 27.228739 A 10.341436 3.2703688 0 1 1 -24.218407,27.228739 A 10.341436 3.2703688 0 1 1 -3.5355339 27.228739 z" + sodipodi:ry="3.2703688" + sodipodi:rx="10.341436" + sodipodi:cy="27.228739" + sodipodi:cx="-13.87697" + id="path2364" + style="color:#000000;fill:url(#linearGradient4112);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4114);stroke-width:0.13035245;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + sodipodi:type="arc" /> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + sodipodi:nodetypes="cccss" + id="path2366" + d="M 30.698087,29.636386 C 30.698087,31.014688 28.157326,32.55444 24.716601,33.288693 C 21.275876,34.022945 18.38922,33.50421 18.273172,32.130802 C 18.157124,30.757395 20.509679,29.155466 23.952388,28.968827 C 27.422379,28.780711 30.698087,28.924901 30.698087,29.636386 z " + style="color:#000000;fill:url(#linearGradient4116);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4118);stroke-width:0.08906286;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" /> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + transform="matrix(0.335464,0,0,0.335464,11.74678,27.2261)" + d="M 31 22.375 A 3.25 3.25 0 1 1 24.5,22.375 A 3.25 3.25 0 1 1 31 22.375 z" + sodipodi:ry="3.25" + sodipodi:rx="3.25" + sodipodi:cy="22.375" + sodipodi:cx="27.75" + id="path20372" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + sodipodi:type="arc" /> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + sodipodi:nodetypes="cscc" + id="path3241" + d="M 19.342183,33.378865 C 22.736592,33.883533 26.320992,33.346192 29.214315,31.470807 C 30.025582,30.944962 30.147604,30.343945 30.520921,29.873844 C 29.09679,31.000705 25.494982,34.035625 19.342183,33.378865 z " + style="fill:#000000;fill-opacity:0.23391807;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + sodipodi:type="arc" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + id="path2435" + sodipodi:cx="27.75" + sodipodi:cy="22.375" + sodipodi:rx="3.25" + sodipodi:ry="3.25" + d="M 31 22.375 A 3.25 3.25 0 1 1 24.5,22.375 A 3.25 3.25 0 1 1 31 22.375 z" + transform="matrix(0.335464,0,0,0.335464,11.74678,30.23376)" /> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + sodipodi:nodetypes="cscc" + id="path3237" + d="M 19.466621,39.517838 C 22.86103,40.022506 26.44543,39.485165 29.338753,37.60978 C 30.15002,37.083935 30.272043,36.482919 30.645359,36.012817 C 29.221228,37.139678 25.61942,40.174598 19.466621,39.517838 z " + style="fill:#000000;fill-opacity:0.23391807;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + style="fill:#000000;fill-opacity:0.23391807;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 19.487361,36.406872 C 22.88177,36.91154 26.46617,36.374199 29.359492,34.498814 C 30.17076,33.972969 30.292782,33.371953 30.666099,32.901851 C 29.241968,34.028712 25.64016,37.063632 19.487361,36.406872 z " + id="path3239" + sodipodi:nodetypes="cscc" /> + </g> + <g + inkscape:r_cy="true" + inkscape:r_cx="true" + transform="translate(-0.988797,0)" + id="g3146"> + <g + inkscape:r_cy="true" + inkscape:r_cx="true" + id="g3141"> + <path + transform="matrix(0.954439,0,0,0.989869,1.433222,0.639881)" + sodipodi:nodetypes="csscs" + id="path3243" + d="M 18.87103,29.628128 C 18.87103,28.836695 20.445135,27.889988 24.419234,27.942972 C 28.101154,27.992059 30.526608,28.83866 30.526608,30.105404 C 30.526608,31.345281 27.307242,32.174416 23.874677,32.008188 C 20.442113,31.84196 18.87103,30.868005 18.87103,29.628128 z " + style="color:#000000;fill:url(#linearGradient4120);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4122);stroke-width:0.09083303;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + <path + transform="matrix(0.954439,0,0,0.989869,1.433222,0.639881)" + sodipodi:nodetypes="csssssc" + id="path6305" + d="M 24.680021,0.8622936 C 16.858005,0.8622936 10.506261,6.8372628 10.506261,14.195288 C 10.506261,21.737851 16.247826,22.573217 16.247826,25.352995 C 16.247826,28.619061 19.614103,32.322687 25.149309,32.188995 C 31.035159,32.046835 33.464182,28.825655 33.464182,25.352995 C 33.464182,22.384064 38.853781,22.304889 38.853781,14.195288 C 38.853781,6.8372628 32.502038,0.8622936 24.680021,0.8622936 z " + style="color:#000000;fill:url(#radialGradient4124);fill-opacity:1;fill-rule:nonzero;stroke:#616471;stroke-width:1.01595449;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + <path + transform="matrix(0.954439,0,0,0.989869,1.433222,0.639881)" + style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4126);stroke-width:0.94685698;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + d="M 24.680021,1.9277146 C 17.389999,1.9277146 11.470252,7.4963123 11.470252,14.353901 C 11.470252,21.383476 16.82132,22.162027 16.82132,24.752746 C 16.82132,27.79668 19.958648,31.248413 25.117392,31.123813 C 30.602931,30.991321 32.866751,27.989222 32.866751,24.752746 C 32.866751,21.98574 37.889791,21.911948 37.889791,14.353901 C 37.889791,7.4963123 31.970044,1.9277146 24.680021,1.9277146 z " + id="path2429" + sodipodi:nodetypes="csssssc" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + </g> + <g + id="g1695" + transform="matrix(0.9375,0,0,0.926938,0.569221,0.25176)" + inkscape:r_cx="true" + inkscape:r_cy="true"> + <path + style="fill:url(#linearGradient4128);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-miterlimit:4" + d="M 31.947292,19.22274 C 32.260034,19.326988 32.468529,19.63973 32.364281,19.952471 L 28.507134,31.523913 C 28.402887,31.836655 28.090145,32.045149 27.777403,31.940902 C 27.464662,31.836655 27.256168,31.523913 27.360415,31.211172 L 31.217562,19.63973 C 31.321809,19.326988 31.634551,19.118493 31.947292,19.22274 z " + id="path1691" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + <path + id="path19612" + d="M 20.152404,19.34774 C 19.839662,19.451988 19.631167,19.76473 19.735415,20.077471 L 23.592562,31.648913 C 23.696809,31.961655 24.009551,32.170149 24.322293,32.065902 C 24.635034,31.961655 24.843528,31.648913 24.739281,31.336172 L 20.882134,19.76473 C 20.777887,19.451988 20.465145,19.243493 20.152404,19.34774 z " + style="fill:url(#linearGradient4130);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-miterlimit:4" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + <path + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4132);stroke-width:0.21454535;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" + d="M 20.255362,19.273128 C 20.009452,19.315194 19.816806,19.507772 19.774653,19.753667 C 19.732499,19.999562 19.850004,20.245309 20.067862,20.366878 C 20.067862,20.366878 21.910084,21.447747 24.317862,21.991878 C 26.72564,22.536009 29.806763,22.571305 32.130362,20.304378 C 32.305608,20.165345 32.386854,19.938963 32.340007,19.720224 C 32.29316,19.501485 32.126325,19.328233 31.909509,19.273168 C 31.692693,19.218103 31.463406,19.290751 31.317862,19.460628 C 29.367326,21.36359 26.773024,21.36522 24.567862,20.866878 C 22.3627,20.368536 20.661612,19.366878 20.661612,19.366878 C 20.542178,19.287089 20.397682,19.253744 20.255362,19.273128 z " + id="path19614" + inkscape:r_cx="true" + inkscape:r_cy="true" /> + </g> + <path + inkscape:r_cy="true" + inkscape:r_cx="true" + style="opacity:0.5977654;color:#000000;fill:url(#linearGradient4134);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.98750001;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + d="M 25.001158,3.5644322 C 18.737608,3.5644322 13.655359,7.5900329 13.655359,12.547843 C 13.655359,14.527956 14.632918,16.261758 16.006008,17.747035 C 17.558672,18.378895 19.249827,18.832941 21.114752,18.832941 C 27.378302,18.832941 32.460549,14.807341 32.460551,9.849528 C 32.460551,7.857476 31.466744,6.1074629 30.07856,4.6174331 C 28.533139,3.9930601 26.854241,3.5644321 25.001158,3.5644322 z " + id="path6334" + transform="matrix(0.954439,0,0,0.989869,1.433222,0.639881)" /> + </g> + </g> + <image + y="36.5" + x="-8.5" + id="image2986" + height="29" + width="51" + sodipodi:absref="C:\Dokumente und Einstellungen\MF\Eigene Dateien\xtc\source\xtrkcad\app\lib\icon.gif" + xlink:href="C:\Dokumente und Einstellungen\MF\Eigene Dateien\xtc\source\xtrkcad\app\lib\icon.gif" /> + </g> +</svg> diff --git a/app/bin/bitmaps/above.xpm b/app/bin/bitmaps/above.xpm new file mode 100644 index 0000000..07f5daf --- /dev/null +++ b/app/bin/bitmaps/above.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * above_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ", +" ..........", +" ..... . . . .", +" ..XXX... . . ..", +" .XXXXXX.. . . .", +".XXXXXXX. . . ..", +".XXXXXXXX. . . .", +".XXXXXXXX.. . ..", +".XXXXXXXX. . . .", +".XXXXXXX. . . ..", +" .XXXXXX.. . . .", +" ..XXX... . . ..", +" ..... . . . .", +" ..........", +" ", +" "}; diff --git a/app/bin/bitmaps/arrow0.xbm b/app/bin/bitmaps/arrow0.xbm new file mode 100644 index 0000000..60fb2aa --- /dev/null +++ b/app/bin/bitmaps/arrow0.xbm @@ -0,0 +1,9 @@ +#define arrow0_width 24 +#define arrow0_height 24 +static char arrow0_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x04, 0x02, + 0x00, 0x04, 0x01, 0x00, 0x84, 0x00, 0x00, 0x04, 0x01, 0x00, 0x24, 0x02, + 0x00, 0x54, 0x04, 0x00, 0x8c, 0x08, 0x00, 0x04, 0x11, 0x00, 0x00, 0x22, + 0x00, 0x00, 0x44, 0x00, 0x00, 0x88, 0x00, 0x00, 0x50, 0x00, 0x00, 0x20}; diff --git a/app/bin/bitmaps/arrow3.xbm b/app/bin/bitmaps/arrow3.xbm new file mode 100644 index 0000000..5f85bc0 --- /dev/null +++ b/app/bin/bitmaps/arrow3.xbm @@ -0,0 +1,9 @@ +#define arrow3_width 24 +#define arrow3_height 24 +static char arrow3_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0x00, 0xfc, 0x03, + 0x00, 0xfc, 0x01, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x01, 0x00, 0xfc, 0x03, + 0x00, 0xdc, 0x07, 0x00, 0x8c, 0x0f, 0x00, 0x04, 0x1f, 0x00, 0x00, 0x3e, + 0x00, 0x00, 0x7c, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x70, 0x00, 0x00, 0x20}; diff --git a/app/bin/bitmaps/arrows.xbm b/app/bin/bitmaps/arrows.xbm new file mode 100644 index 0000000..494b8de --- /dev/null +++ b/app/bin/bitmaps/arrows.xbm @@ -0,0 +1,10 @@ +#define arrows_width 24 +#define arrows_height 24 +// static unsigned char arrows_bits[] = { +static char arrows_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x1f, 0x00, 0x01, 0x10, 0x00, 0xfd, 0x17, 0x00, 0xfd, 0x13, + 0x00, 0xfd, 0x09, 0x00, 0xfd, 0x04, 0x00, 0xfd, 0x09, 0x00, 0xfd, 0x13, + 0x00, 0xdd, 0x27, 0x00, 0x8d, 0x4f, 0x00, 0x25, 0x9f, 0x00, 0x51, 0x3e, + 0x00, 0x8f, 0x7c, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x72, 0x00, 0x00, 0x24}; diff --git a/app/bin/bitmaps/ballgreen.xpm b/app/bin/bitmaps/ballgreen.xpm new file mode 100644 index 0000000..14fb1e2 --- /dev/null +++ b/app/bin/bitmaps/ballgreen.xpm @@ -0,0 +1,35 @@ +/* XPM */ +static char * ballgreen[] = { +"16 16 16 1", +" c None", +". c #292929", +"+ c #292B29", +"@ c #29322B", +"# c #365233", +"$ c #2D432E", +"% c #57A572", +"& c #B0D6C1", +"* c #3F9159", +"= c #7EC097", +"- c #296932", +"; c #2D8840", +"> c #2C9231", +", c #44C530", +"' c #45BA32", +") c #7BF737", +" .+@#$@+. ", +" +$%&&&&%$+ ", +" +#&&&&&&&&*+ ", +" +#=&===&&&==#+ ", +".@%%%%%%%%%%%*@.", +"+-;%%;*%**%%%*$+", +"@-**;*;;;;;;*;-@", +"@--;-;;;;;;;;;-@", +"@---->>>;->>---$", +"@->->>>>>;>>;--@", +"+->>>>,>>,>>>>-+", +".$>>>,,,,,,>>>@.", +" +->',,)),,,>-+ ", +" +#,)))))),>+ ", +" +#,)))),$+ ", +" .+$##$+. "}; diff --git a/app/bin/bitmaps/ballred.xpm b/app/bin/bitmaps/ballred.xpm new file mode 100644 index 0000000..8d56dd4 --- /dev/null +++ b/app/bin/bitmaps/ballred.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char *ballred[] = { +/* columns rows colors chars-per-pixel */ +"16 16 16 1", +" c #20F301EC00EA", +". c #060400000000", +"X c #297B14040000", +"o c #ED34010C0000", +"O c #FFFF07880000", +"+ c #E2BA07180718", +"@ c #A1E800000000", +"# c #F4791D5E016C", +"$ c #D2A11FA11FA1", +"% c #6D77055602AD", +"& c #FB014C2607BE", +"* c #FB747AB40D88", +"= c #CDB757875787", +"- c #E5DEA339A339", +"; c #000000000000", +": c None", +/* pixels */ +"::::;. .;::::", +":::. =----= .:::", +"::.%--------$.::", +":.%=====-====%.:", +"; $$$=$$$$=$$% ;", +".%+$$$$$$$$$$$%.", +" @++++++++++++@ ", +" @++@+++++@+++@ ", +" @ooooOoooooooo ", +" oooooOOOOoooo@ ", +".@ooOOOOOOOOoo@.", +"; oOO######OOo ;", +":.@O##&&&###O@.:", +"::.%#&&*&*&#%.::", +":::.X#***&#X.:::", +"::::;. XX .;::::" +}; diff --git a/app/bin/bitmaps/below.xpm b/app/bin/bitmaps/below.xpm new file mode 100644 index 0000000..43bc22a --- /dev/null +++ b/app/bin/bitmaps/below.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * below_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ", +" ...........", +" ... . . . . .", +" ..XX.. . . . ..", +" .XXX. . . . . .", +".XXXX.. . . . ..", +".XXXX. . . . . .", +".XXXX.. . . . ..", +".XXXX. . . . . .", +".XXXX.. . . . ..", +" .XXX. . . . . .", +" ..XX.. . . . ..", +" ... . . . . .", +" ...........", +" ", +" "}; diff --git a/app/bin/bitmaps/bigdot.xbm b/app/bin/bitmaps/bigdot.xbm new file mode 100644 index 0000000..332225a --- /dev/null +++ b/app/bin/bitmaps/bigdot.xbm @@ -0,0 +1,5 @@ +#define bigdot_width 3 +#define bigdot_height 3 +// static unsigned char bigdot_bits[] = { +static char bigdot_bits[] = { + 0x02, 0x05, 0x02}; diff --git a/app/bin/bitmaps/blockdel.xpm b/app/bin/bitmaps/blockdel.xpm new file mode 100644 index 0000000..5a02815 --- /dev/null +++ b/app/bin/bitmaps/blockdel.xpm @@ -0,0 +1,52 @@ +/* XPM */ +static char * blockdel_xpm[] = { +"16 16 33 1", +" c None", +". c #FE0C28", +"+ c #FE102B", +"@ c #D1FCD1", +"# c #D2FCD2", +"$ c #FF0000", +"% c #FF102A", +"& c #D3FCD3", +"* c #FF0101", +"= c #000000", +"- c #C38790", +"; c #FE0D29", +"> c #B2A8AA", +", c #ACB5B7", +"' c #FE122B", +") c #ACB6B7", +"! c #DE5162", +"~ c #B5A0A2", +"{ c #F51819", +"] c #FE0A23", +"^ c #D66070", +"/ c #FF0202", +"( c #C67C7E", +"_ c #B6A0A5", +": c #FF0303", +"< c #FFF5F5", +"[ c #FEEEEE", +"} c #FFF6F6", +"| c #FDF1F1", +"1 c #FE142F", +"2 c #FFF0F0", +"3 c #FD102C", +"4 c #FE0D28", +" ", +" .+ @# $$", +" %.. @& $$ ", +" .. *$$ ", +" = .. $$ = ", +" = -.; $$> = ", +" = , .'$$ , = ", +"=== ,)!$$~,, ===", +"=== ,,{$]^,, ===", +" = ,/$ +. , = ", +" = ($ .._ = ", +" $: < .+ ", +" $* [}| 1. ", +" $$ 2} .3 ", +" *$ 4. ", +" * . "}; diff --git a/app/bin/bitmaps/blockedit.xpm b/app/bin/bitmaps/blockedit.xpm new file mode 100644 index 0000000..cfe3e5e --- /dev/null +++ b/app/bin/bitmaps/blockedit.xpm @@ -0,0 +1,89 @@ +/* XPM */ +static char * blockedit_xpm[] = { +"16 16 70 1", +" c None", +". c #028D05", +"+ c #008B04", +"@ c #815C14", +"# c #C88F21", +"$ c #6AC66C", +"% c #94DA95", +"& c #008C03", +"* c #008D03", +"= c #875F15", +"- c #CA8F22", +"; c #CB9022", +"> c #008C04", +", c #8CD68D", +"' c #99DD9A", +") c #008B03", +"! c #8E6516", +"~ c #B7821F", +"{ c #575B0E", +"] c #976A17", +"^ c #B07D1D", +"/ c #6C4C10", +"( c #000000", +"_ c #4E610C", +": c #9F7119", +"< c #A8781C", +"[ c #5D420D", +"} c #00C3FF", +"| c #63540F", +"1 c #A7771A", +"2 c #A0711B", +"3 c #61450F", +"4 c #090601", +"5 c #704E11", +"6 c #AE7C1C", +"7 c #956B1B", +"8 c #5E5526", +"9 c #34898C", +"0 c #BA9E58", +"a c #CE972E", +"b c #C88E21", +"c c #8D661B", +"d c #4C6B4F", +"e c #00C4FF", +"f c #01C1FB", +"g c #7F7C4C", +"h c #E3D6A3", +"i c #DDC482", +"j c #82621C", +"k c #467461", +"l c #01C2FC", +"m c #C9AB64", +"n c #CBB06D", +"o c #997C3E", +"p c #6A4E10", +"q c #03BEF7", +"r c #634813", +"s c #886628", +"t c #95360D", +"u c #EE2C2C", +"v c #F46E6E", +"w c #EC1C1C", +"x c #EA0000", +"y c #F99A9A", +"z c #F9B3B3", +"A c #F37E7E", +"B c #F48080", +"C c #F16262", +"D c #EB0000", +"E c #E90000", +" .+ @#", +" $%&* =-;", +" >,'&) !;;~", +" *&&&{];;^/", +" ( &&)_:;;<[ ", +" ( } |1;;234 ", +" ( } 56;-78 ( ", +"((( }}90abcd (((", +"((( efghijkl (((", +" ( e mnop } ( ", +" ( qrst } ( ", +" uvwx ", +" yzAx ", +" BzCx ", +" DEEx ", +" "}; diff --git a/app/bin/bitmaps/blocknew.xpm b/app/bin/bitmaps/blocknew.xpm new file mode 100644 index 0000000..92685f4 --- /dev/null +++ b/app/bin/bitmaps/blocknew.xpm @@ -0,0 +1,90 @@ +/* XPM */ +static char * blocknew_xpm[] = { +"16 16 71 1", +" c None", +". c #028D05", +"+ c #3DA505", +"@ c #C1DB0D", +"# c #FFF417", +"$ c #FFF41A", +"% c #6AC66C", +"& c #96DC91", +"* c #7BBE08", +"= c #C2DB12", +"- c #FCF427", +"; c #FFF750", +"> c #FFF528", +", c #FFF518", +"' c #008C04", +") c #8CD68D", +"! c #A9E084", +"~ c #9CCC0B", +"{ c #E4EA1B", +"] c #F9F560", +"^ c #FFFAA5", +"/ c #FFF864", +"( c #FFF41E", +"_ c #008D03", +": c #40A604", +"< c #A5CF0D", +"[ c #EDED1D", +"} c #FBF676", +"| c #FFFCD0", +"1 c #FFFCD1", +"2 c #FFF878", +"3 c #FFF51F", +"4 c #FFF413", +"5 c #000000", +"6 c #008C03", +"7 c #299C05", +"8 c #95C90A", +"9 c #E4E919", +"0 c #FFF756", +"a c #F4F795", +"b c #FCFA91", +"c c #FAF255", +"d c #D3CA17", +"e c #00C3FF", +"f c #FDF215", +"g c #E6F14D", +"h c #F4F443", +"i c #EFE61C", +"j c #9B940D", +"k c #A4E36A", +"l c #D0EC42", +"m c #C3BA0E", +"n c #585404", +"o c #0DC6F2", +"p c #3ACEC6", +"q c #49D1B9", +"r c #7EDB86", +"s c #4C4901", +"t c #080700", +"u c #00C4FF", +"v c #EE2C2C", +"w c #F46E6E", +"x c #EC1C1C", +"y c #EA0000", +"z c #F99A9A", +"A c #F9B3B3", +"B c #F37E7E", +"C c #F48080", +"D c #F16262", +"E c #EB0000", +"F c #E90000", +" .+@#$$# ", +" %&*=-;;>, ", +" ')!~{]^^/( ", +" _:<[}|1234", +" 5 67890abcd ", +" 5 e f(ghij ", +" 5 e 4klmn ", +"555 eeeeeopqrst5", +"555 ueeeeeee 555", +" 5 u e 5 ", +" 5 e e 5 ", +" vwxy ", +" zABy ", +" CADy ", +" EFFy ", +" "}; diff --git a/app/bin/bitmaps/bma0.xbm b/app/bin/bitmaps/bma0.xbm new file mode 100644 index 0000000..e0a2815 --- /dev/null +++ b/app/bin/bitmaps/bma0.xbm @@ -0,0 +1,6 @@ +#define bma0_width 16 +#define bma0_height 16 +static char bma0_bits[] = { + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/bma135.xbm b/app/bin/bitmaps/bma135.xbm new file mode 100644 index 0000000..e0c5f4a --- /dev/null +++ b/app/bin/bitmaps/bma135.xbm @@ -0,0 +1,6 @@ +#define bma135_width 16 +#define bma135_height 16 +static char bma135_bits[] = { + 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, + 0x40, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, + 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00}; diff --git a/app/bin/bitmaps/bma45.xbm b/app/bin/bitmaps/bma45.xbm new file mode 100644 index 0000000..c4717b4 --- /dev/null +++ b/app/bin/bitmaps/bma45.xbm @@ -0,0 +1,6 @@ +#define bma45_width 16 +#define bma45_height 16 +static char bma45_bits[] = { + 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, + 0x00, 0x01, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/bma90.xbm b/app/bin/bitmaps/bma90.xbm new file mode 100644 index 0000000..cf03ee3 --- /dev/null +++ b/app/bin/bitmaps/bma90.xbm @@ -0,0 +1,6 @@ +#define bma90_width 16 +#define bma90_height 16 +static char bma90_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/bmendpt.xbm b/app/bin/bitmaps/bmendpt.xbm new file mode 100644 index 0000000..1bea7b7 --- /dev/null +++ b/app/bin/bitmaps/bmendpt.xbm @@ -0,0 +1,6 @@ +#define bmendpt_width 16 +#define bmendpt_height 16 +static char bmendpt_bits[] = { + 0x81, 0x40, 0x82, 0x20, 0x84, 0x10, 0x88, 0x08, 0x90, 0x04, 0xa0, 0x02, + 0xc0, 0x01, 0xff, 0x7f, 0xc0, 0x01, 0xa0, 0x02, 0x90, 0x04, 0x88, 0x08, + 0x84, 0x10, 0x82, 0x20, 0x81, 0x40, 0x00, 0x00}; diff --git a/app/bin/bitmaps/bo_edge.xpm b/app/bin/bitmaps/bo_edge.xpm new file mode 100644 index 0000000..da936f9 --- /dev/null +++ b/app/bin/bitmaps/bo_edge.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_edge_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ...... ", +" .XXXX. ", +" .XXXX. ", +" .XXXX. ", +" .XXXX. ", +" .XXXX. ", +" .XXXX. ", +" .XXXX. ", +" .XXXX. ", +" .XXXX. ", +" .XXXX. ", +" .XXXX. ", +" .XXXX. ", +" ...... "}; diff --git a/app/bin/bitmaps/bo_flat.xpm b/app/bin/bitmaps/bo_flat.xpm new file mode 100644 index 0000000..1402d10 --- /dev/null +++ b/app/bin/bitmaps/bo_flat.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_flat_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ", +" ", +" ", +" ", +" .............. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .............. ", +" ", +" ", +" ", +" "}; diff --git a/app/bin/bitmaps/bo_ll.xpm b/app/bin/bitmaps/bo_ll.xpm new file mode 100644 index 0000000..a3bfcdb --- /dev/null +++ b/app/bin/bitmaps/bo_ll.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_ll_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ........ ", +" .XXXXXX. ", +" .XXXXXX. ", +" .XXXXXX. ", +" .XXX.... ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" ..... "}; diff --git a/app/bin/bitmaps/bo_lld.xpm b/app/bin/bitmaps/bo_lld.xpm new file mode 100644 index 0000000..f1068cf --- /dev/null +++ b/app/bin/bitmaps/bo_lld.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_lld_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ", +" ", +" ", +" .............. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" ..........XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" ..... ", +" ", +" "}; diff --git a/app/bin/bitmaps/bo_lli.xpm b/app/bin/bitmaps/bo_lli.xpm new file mode 100644 index 0000000..e5d5bc7 --- /dev/null +++ b/app/bin/bitmaps/bo_lli.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_lli_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ..... ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .....XXX. ", +" .XXXXXXX. ", +" .XXXXXXX. ", +" .XXXXXXX. ", +" ......... "}; diff --git a/app/bin/bitmaps/bo_llu.xpm b/app/bin/bitmaps/bo_llu.xpm new file mode 100644 index 0000000..79ad1f1 --- /dev/null +++ b/app/bin/bitmaps/bo_llu.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_llu_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ", +" ", +" ..... ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX.......... ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .............. ", +" ", +" ", +" "}; diff --git a/app/bin/bitmaps/bo_lr.xpm b/app/bin/bitmaps/bo_lr.xpm new file mode 100644 index 0000000..869a0ba --- /dev/null +++ b/app/bin/bitmaps/bo_lr.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_lr_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ........ ", +" .XXXXXX. ", +" .XXXXXX. ", +" .XXXXXX. ", +" ....XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" ..... "}; diff --git a/app/bin/bitmaps/bo_lrd.xpm b/app/bin/bitmaps/bo_lrd.xpm new file mode 100644 index 0000000..29f6550 --- /dev/null +++ b/app/bin/bitmaps/bo_lrd.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_lrd_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ", +" ", +" ", +" .............. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .XXX.......... ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" ..... ", +" ", +" "}; diff --git a/app/bin/bitmaps/bo_lri.xpm b/app/bin/bitmaps/bo_lri.xpm new file mode 100644 index 0000000..e58e682 --- /dev/null +++ b/app/bin/bitmaps/bo_lri.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_lri_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ..... ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX..... ", +" .XXXXXXX. ", +" .XXXXXXX. ", +" .XXXXXXX. ", +" ......... "}; diff --git a/app/bin/bitmaps/bo_lru.xpm b/app/bin/bitmaps/bo_lru.xpm new file mode 100644 index 0000000..2c093b5 --- /dev/null +++ b/app/bin/bitmaps/bo_lru.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_lru_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ", +" ", +" ..... ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" ..........XXX. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .............. ", +" ", +" ", +" "}; diff --git a/app/bin/bitmaps/bo_t.xpm b/app/bin/bitmaps/bo_t.xpm new file mode 100644 index 0000000..6842404 --- /dev/null +++ b/app/bin/bitmaps/bo_t.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_t_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ............. ", +" .XXXXXXXXXXX. ", +" .XXXXXXXXXXX. ", +" .XXXXXXXXXXX. ", +" .....XXX..... ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" ..... "}; diff --git a/app/bin/bitmaps/bo_ti.xpm b/app/bin/bitmaps/bo_ti.xpm new file mode 100644 index 0000000..3c86b96 --- /dev/null +++ b/app/bin/bitmaps/bo_ti.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_ti_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ..... ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .....XXX..... ", +" .XXXXXXXXXXX. ", +" .XXXXXXXXXXX. ", +" .XXXXXXXXXXX. ", +" ............. "}; diff --git a/app/bin/bitmaps/bo_tl.xpm b/app/bin/bitmaps/bo_tl.xpm new file mode 100644 index 0000000..5d63874 --- /dev/null +++ b/app/bin/bitmaps/bo_tl.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_tl_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ..... ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" ..........XXX. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" ..........XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" ..... ", +" "}; diff --git a/app/bin/bitmaps/bo_tr.xpm b/app/bin/bitmaps/bo_tr.xpm new file mode 100644 index 0000000..4acbcef --- /dev/null +++ b/app/bin/bitmaps/bo_tr.xpm @@ -0,0 +1,20 @@ +/* XPM */ +static char * bo_tr_xpm[] = { +"16 14 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" ..... ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX.......... ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +" .XXX.......... ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" .XXX. ", +" ..... "}; diff --git a/app/bin/bitmaps/bridge.xbm b/app/bin/bitmaps/bridge.xbm new file mode 100644 index 0000000..fd5857b --- /dev/null +++ b/app/bin/bitmaps/bridge.xbm @@ -0,0 +1,7 @@ +#define bridge_width 16 +#define bridge_height 16 +// static unsigned char bridge_bits[] = { +static char bridge_bits[] = { + 0x01, 0x80, 0x01, 0x80, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x82, 0x20, + 0xff, 0xff, 0x82, 0x20, 0x82, 0x20, 0xff, 0xff, 0x82, 0x20, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x7f, 0x01, 0x80, 0x01, 0x80}; diff --git a/app/bin/bitmaps/carpart.xpm b/app/bin/bitmaps/carpart.xpm new file mode 100644 index 0000000..215a7c1 --- /dev/null +++ b/app/bin/bitmaps/carpart.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char*carpart_xpm[]={ +"16 16 3 1", +". c None", +"# c #ffff00000000", +"a c #000000000000", +"................", +"................", +"................", +"................", +"................", +"................", +".##############.", +".##############.", +".##############.", +".##############.", +"a##############a", +"..a.a......a.a..", +".a.a.a....a.a.a.", +"..a.a......a.a..", +"................", +"................"}; diff --git a/app/bin/bitmaps/carproto.xpm b/app/bin/bitmaps/carproto.xpm new file mode 100644 index 0000000..eb07037 --- /dev/null +++ b/app/bin/bitmaps/carproto.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char*carproto_xpm[]={ +"16 16 4 1", +". c None", +"c c #ffff00000000", +"a c #00000000ffff", +"# c #000000000000", +"....a.......a...", +"...aa..aa..aa...", +"..aaa..aa.aaa...", +"...aa......aa...", +"...aa..aa..aa...", +"...aa..aa..aa...", +"..aaaa....aaaa..", +".cccccccccccccc.", +".cccccccccccccc.", +".cccccccccccccc.", +".cccccccccccccc.", +"#cccccccccccccc#", +"..#.#......#.#..", +".#.#.#....#.#.#.", +"..#.#......#.#..", +"................"}; diff --git a/app/bin/bitmaps/chkbox.xbm b/app/bin/bitmaps/chkbox.xbm new file mode 100644 index 0000000..c61d538 --- /dev/null +++ b/app/bin/bitmaps/chkbox.xbm @@ -0,0 +1,7 @@ +#define chkbox_width 16 +#define chkbox_height 16 +// static unsigned char chkbox_bits[] = { +static char chkbox_bits[] = { + 0x00, 0x80, 0xfe, 0x6f, 0x02, 0x30, 0x02, 0x58, 0x02, 0x4c, 0x02, 0x46, + 0x1a, 0x47, 0xbc, 0x43, 0xfe, 0x43, 0xfc, 0x41, 0xfa, 0x41, 0xf2, 0x40, + 0xe2, 0x40, 0x42, 0x40, 0xbe, 0x7f, 0x00, 0x00}; diff --git a/app/bin/bitmaps/circle1.xpm b/app/bin/bitmaps/circle1.xpm new file mode 100644 index 0000000..03426f0 --- /dev/null +++ b/app/bin/bitmaps/circle1.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * circle1_xpm[] = { +"16 16 4 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +"o c #FFFFFFFFFFFF", +" ...... ", +" . . ", +" .. .... .. ", +" .XXXXXXXXXXX . ", +" .XoooooooooX . ", +". XoXXXoXXXoX .", +". XoXoXoXoooX. .", +". XoXXXoXXooX. .", +". XoooXoooXoX. .", +". XoooXoooXoX. .", +". XoooXoXXooX .", +" .XoooooooooX . ", +" .XXXXXXXXXXX . ", +" .. .... .. ", +" . . ", +" ...... "}; diff --git a/app/bin/bitmaps/circle2.xpm b/app/bin/bitmaps/circle2.xpm new file mode 100644 index 0000000..343f29b --- /dev/null +++ b/app/bin/bitmaps/circle2.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * circle2_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ...... ", +" . . . . . ", +" .. .... .. ", +" . ... . ... . ", +" . .. .. . ", +". . . .", +". . . .", +". . XXXX ....", +".... XX . .", +". . X X . .", +". . X X . .", +" . .. X. . ", +" . ... . ..X . ", +" .. .... .X ", +" . . . . ", +" ...... "}; diff --git a/app/bin/bitmaps/circle3.xpm b/app/bin/bitmaps/circle3.xpm new file mode 100644 index 0000000..3a7c9ab --- /dev/null +++ b/app/bin/bitmaps/circle3.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * circle3_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ...... ", +" . . . . . ", +" .. .... .. ", +" . ... . ... . ", +" . .. .. . ", +". . . .", +". . . .", +". . XX ....", +".... XX . .", +". . X X. .", +". . X X .", +" . .. XX . ", +" . ... .XXXX . ", +" .. .... .. ", +" . . . . . ", +" ...... "}; diff --git a/app/bin/bitmaps/cnote.xpm b/app/bin/bitmaps/cnote.xpm new file mode 100644 index 0000000..c008c24 --- /dev/null +++ b/app/bin/bitmaps/cnote.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * cnote_xpm[] = { +"16 16 3 1", +". c None", +" c #000000000000", +"X c #FFFFFFFF0000", +" ......", +" XXXXXXXX .....", +" XXXXXX X X ....", +" XXXXXX X XX ...", +" XXXXXX X XXX ..", +" XXXXXX X .", +" XXXXXX XXXXXX .", +" XXXXXX XXXXXX .", +" XXXXXX XXXXXX .", +" XXXXXX XXXXXX .", +" XXXXXX XXXXXX .", +" XXXXXXXXXXXXX .", +" XXXXXX XXXXXX .", +" XXXXXXXXXXXXX .", +" XXXXXXXXXXXXX .", +" ."}; diff --git a/app/bin/bitmaps/cross0.xbm b/app/bin/bitmaps/cross0.xbm new file mode 100644 index 0000000..373d897 --- /dev/null +++ b/app/bin/bitmaps/cross0.xbm @@ -0,0 +1,5 @@ +#define cross0_width 8 +#define cross0_height 8 +//static unsigned char cross0_bits[] = { +static char cross0_bits[] = { + 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/curve1.xpm b/app/bin/bitmaps/curve1.xpm new file mode 100644 index 0000000..dd1a295 --- /dev/null +++ b/app/bin/bitmaps/curve1.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * curve1_xpm[] = { +"16 16 4 1", +" c None", +". c #FFFF00000000", +"X c #000000000000", +"o c #861782078617", +" . ", +" ... ", +" . . . ", +" . .X .", +" X XX.XX ", +" oXXX .X ", +" X XXX XX.XX ", +" X X. .X .", +" XooX X. . . ", +" X X ... ", +" XoX X X . ", +" XoX ", +" X XX ", +" X X ", +"XXXXX ", +" X X "}; diff --git a/app/bin/bitmaps/curve2.xpm b/app/bin/bitmaps/curve2.xpm new file mode 100644 index 0000000..e1ff0c6 --- /dev/null +++ b/app/bin/bitmaps/curve2.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * curve2_xpm[] = { +"16 16 4 1", +" c None", +". c #000000000000", +"X c #861782078617", +"o c #FFFF00000000", +" . X", +" ......", +" . .. . .", +" .. ..... ", +" . X.. . ..", +" . .o .. . ", +" X. o . ", +" . X. o ", +" . .X o ", +" . . X o ", +" . . o ", +"..... o o", +" . . o o", +" . . ooo", +" . . ooo", +" .... ooooo"}; diff --git a/app/bin/bitmaps/curve3.xpm b/app/bin/bitmaps/curve3.xpm new file mode 100644 index 0000000..97c447b --- /dev/null +++ b/app/bin/bitmaps/curve3.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * curve3_xpm[] = { +"16 16 4 1", +" c None", +". c #000000000000", +"X c #861782078617", +"o c #FFFF00000000", +" . X", +" ......", +" . .. . .", +" ..X ..... ", +" . .. . ..", +" . .ooooo . ", +" .Xooo . ", +" . .ooo ", +" . .o. o ", +" . .o o ", +" . . o ", +" . . o ", +"..... o ", +" . . o ", +" . . o ", +" .... o"}; diff --git a/app/bin/bitmaps/curve4.xpm b/app/bin/bitmaps/curve4.xpm new file mode 100644 index 0000000..0d26396 --- /dev/null +++ b/app/bin/bitmaps/curve4.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * curve4_xpm[] = { +"16 16 4 1", +" c None", +". c #000000000000", +"X c #861782078617", +"o c #FFFF00000000", +" . X", +" ......", +" . .. . .", +" ..X ..... ", +" . .. . ..", +" . .ooooo . ", +" .Xooo . ", +" . .ooo ", +" . .o. o o", +" . .o o o ", +" . . o o ", +" . . oo ", +"..... oo ", +" . . o ", +" . . o ", +" .... o "}; diff --git a/app/bin/bitmaps/dbench.xpm b/app/bin/bitmaps/dbench.xpm new file mode 100644 index 0000000..a880329 --- /dev/null +++ b/app/bin/bitmaps/dbench.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dbench_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF9A691861", +" .XXXX.XX", +" .XXXX.XXX", +" ..XXX..XXXX", +" .XXXX.XXXXXX", +" .XXXX.XXXXXXX", +" ......XXXXXXXX", +" .XXXX.XXXXXXXX", +" .XXXX.XXXXXXXX", +" .XXXX.XXXXXXX.", +" .XXXX.XXXXXX. ", +" .XXXX.XXXXX. ", +" .XXXX.XXXX. ", +" .XXXX.XX.. ", +" .XXXX.X. ", +" .XXXX.. ", +" ...... "}; diff --git a/app/bin/bitmaps/dbox.xpm b/app/bin/bitmaps/dbox.xpm new file mode 100644 index 0000000..83dbeec --- /dev/null +++ b/app/bin/bitmaps/dbox.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dbox_xpm[] = { +"16 16 3 1", +"X c None", +" c #000000000000", +". c #FFFF00000000", +" .....", +" XX..", +" XXXXXXXXXXX.X.", +" XXXXXXXXXX.XX.", +" XXXXXXXXX.XX .", +" XXXXXXXX.XXX ", +" XXXXXXX.XXXX ", +" XXXXXX.XXXXX ", +" XXXXX.XXXXXX ", +" XXXX.XXXXXXX ", +" XXX.XXXXXXXX ", +" XX.XXXXXXXXX ", +" X.XXXXXXXXXX ", +" .XXXXXXXXXXX ", +".. ", +".. "}; diff --git a/app/bin/bitmaps/dcircle1.xpm b/app/bin/bitmaps/dcircle1.xpm new file mode 100644 index 0000000..a17a56a --- /dev/null +++ b/app/bin/bitmaps/dcircle1.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dcircle1_xpm[] = { +"16 16 3 1", +" c #None", +". c #000000000000", +"X c #FFFF00000000", +" ...... ", +" ........ ", +" ... ... ", +" .XXXXXXXXXXX.. ", +" .X X.. ", +"..X XXX XXX X ..", +". X X X X X ..", +". X XXX XX X ..", +". X X X X ..", +". X X X X ..", +"..X X XX X ..", +" .X X.. ", +" .XXXXXXXXXXX.. ", +" ... ... ", +" ........ ", +" ...... "}; diff --git a/app/bin/bitmaps/dcircle2.xpm b/app/bin/bitmaps/dcircle2.xpm new file mode 100644 index 0000000..36e4763 --- /dev/null +++ b/app/bin/bitmaps/dcircle2.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dcircle2_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ...... ", +" ........ ", +" ... ... ", +" ... .. ", +" .. . ", +".. ..", +".. ..", +".. XXXXX ..", +".. XX ..", +".. X X ..", +".. X X ..", +" .. X X . ", +" ... X.. ", +" ... .X ", +" ......... ", +" ...... "}; diff --git a/app/bin/bitmaps/dcircle3.xpm b/app/bin/bitmaps/dcircle3.xpm new file mode 100644 index 0000000..ec0dc1f --- /dev/null +++ b/app/bin/bitmaps/dcircle3.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dcircle3_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ...... ", +" ........ ", +" ... ... ", +" .. .. ", +" .. . ", +".. ..", +".. ..", +".. XX ..", +".. XX X ..", +".. X X ..", +".. X X ..", +" . XX . ", +" .. XXXXX.. ", +" ... ... ", +" ........ ", +" ...... "}; diff --git a/app/bin/bitmaps/dcurve1.xpm b/app/bin/bitmaps/dcurve1.xpm new file mode 100644 index 0000000..286e8bc --- /dev/null +++ b/app/bin/bitmaps/dcurve1.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dcurve1_xpm[] = { +"16 16 3 1", +" c None", +". c #FFFF00000000", +"X c #000000000000", +" . ", +" ... ", +" . . . ", +" . . .", +" XXX.X ", +" XXXX.X ", +" XXX . ", +" XXX . . ", +" XXX . . . ", +" XXX ... ", +" XXX . ", +" XX ", +" XXX ", +" XX ", +" XX ", +" XX "}; diff --git a/app/bin/bitmaps/dcurve2.xpm b/app/bin/bitmaps/dcurve2.xpm new file mode 100644 index 0000000..2714d5e --- /dev/null +++ b/app/bin/bitmaps/dcurve2.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dcurve2_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ", +" ..... ", +" ........", +" .... ..", +" ... .", +" .X. ", +" ...X ", +" ... X ", +" .. X ", +" ... X ", +" .. X ", +" .. X X", +" .. X X", +" .. X X", +" .. XX", +" .. XXXXX"}; diff --git a/app/bin/bitmaps/dcurve3.xpm b/app/bin/bitmaps/dcurve3.xpm new file mode 100644 index 0000000..c452ffd --- /dev/null +++ b/app/bin/bitmaps/dcurve3.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dcurve3_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ", +" ..... ", +" ........", +" .... .", +" ... ", +" .XXXXXX ", +" ..XXX ", +" .. XXX ", +" .. X X ", +" ... X X ", +" .. X X ", +" .. X ", +" .. X ", +" .. X ", +" .. XX", +" .. XX"}; diff --git a/app/bin/bitmaps/dcurve4.xpm b/app/bin/bitmaps/dcurve4.xpm new file mode 100644 index 0000000..2474481 --- /dev/null +++ b/app/bin/bitmaps/dcurve4.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dcurve4_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ", +" ..... ", +" ........", +" .... ..", +" ... ", +" .XXXXXX ", +" ..XXX ", +" .. XXX ", +" .. X X .", +" .. X X . ", +" .. X X . ", +" .. X. ", +" .. .X ", +" .. . ", +" .. . ", +" .. . "}; diff --git a/app/bin/bitmaps/ddimlin.xpm b/app/bin/bitmaps/ddimlin.xpm new file mode 100644 index 0000000..3980e44 --- /dev/null +++ b/app/bin/bitmaps/ddimlin.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * ddimlin_xpm[] = { +"16 16 3 1", +" c None", +". c #FFFF00000000", +"X c #000000000000", +" .....", +" ..", +" . .", +" . .", +" . .", +" X XXX ", +" X X X ", +" XX XX ", +" X X ", +" XX XX ", +" . ", +". . ", +". . ", +". . ", +".. ", +"..... "}; diff --git a/app/bin/bitmaps/delete.xpm b/app/bin/bitmaps/delete.xpm new file mode 100644 index 0000000..1e88b80 --- /dev/null +++ b/app/bin/bitmaps/delete.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * delete_xpm[] = { +"16 16 2 1", +". c None", +" c #000000000000", +" ............ .", +" ......... ..", +".. ...... ...", +"... .... ....", +".. .. .. .. ..", +" .. . ", +".. ... ... ..", +".. .... .... ..", +".. ... ... ..", +" . . . ", +".. . .... . ..", +"... ...... ...", +".. ........ ..", +". ........... .", +" .............", +". ............. "}; diff --git a/app/bin/bitmaps/describe.xpm b/app/bin/bitmaps/describe.xpm new file mode 100644 index 0000000..e5506a5 --- /dev/null +++ b/app/bin/bitmaps/describe.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * describe_xpm[] = { +"16 16 3 1", +" c None", +". c #FFFF00000000", +"X c #000000000000", +" ...... ", +" ... ... ", +" ... ... ", +" ... ... ", +" ... ... ", +" X X ... X ", +"XXXXXXXX ... XX", +" X X ... X ", +" X .... X ", +" X ... X ", +"XXXX ... XXXXXX", +" X ... X X ", +" ... ", +" ", +" ... ", +" ... "}; diff --git a/app/bin/bitmaps/dfilbox.xpm b/app/bin/bitmaps/dfilbox.xpm new file mode 100644 index 0000000..4d78c26 --- /dev/null +++ b/app/bin/bitmaps/dfilbox.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * dfilbox_xpm[] = { +"16 16 4 1", +"o c None", +" c #000000000000", +". c #FFFF00000000", +"X c #0000FFFFFFFF", +" .....", +" XXXXXXXXXXXXX..", +" XXXXXXXXXXXX.X.", +" XXXXXXXXXXX.XX.", +" XXXXXXXXXX.XXX.", +" XXXXXXXXX.XXXX ", +" XXXXXXXX.XXXXX ", +" XXXXXXX.XXXXXX ", +" XXXXXX.XXXXXXX ", +" XXXXX.XXXXXXXX ", +" XXXX.XXXXXXXXX ", +" XXX.XXXXXXXXXX ", +" XX.XXXXXXXXXXX ", +" X.XXXXXXXXXXXX ", +"..XXXXXXXXXXXXX ", +".. "}; diff --git a/app/bin/bitmaps/dfilpoly.xpm b/app/bin/bitmaps/dfilpoly.xpm new file mode 100644 index 0000000..56a62ee --- /dev/null +++ b/app/bin/bitmaps/dfilpoly.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * dfilpoly_xpm[] = { +"16 16 4 1", +"X c None", +" c #FFFF00000000", +". c #000000000000", +"o c #0000FFFFFFFF", +" ..... XXXXXXXXX", +".oooooo..... XXX", +".ooooooooo..XXXX", +".oooooooo.XXXXXX", +".oooooo..XXXXXXX", +".ooooo.XXXXXXXXX", +".oooo.XXXXXXXXXX", +".oooo .XXXXXXXXX", +".oooooo..... XXX", +" oooooooooooo.. ", +"X.oooooooooooo.X", +"X.oooooooooo..XX", +"XX.oooooooo.XXXX", +"XXX.oooooo.XXXXX", +"XXX.oooo..XXXXXX", +"XXXX .. XXXXXXXX"}; diff --git a/app/bin/bitmaps/dflcrcl1.xpm b/app/bin/bitmaps/dflcrcl1.xpm new file mode 100644 index 0000000..404660d --- /dev/null +++ b/app/bin/bitmaps/dflcrcl1.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dflcrcl1_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ...... ", +" .. . . . ", +" . . . . . .. ", +" .XXXXXXXXXXX.. ", +" .X X . ", +". X XXX XXX X. .", +"..X X X X X ..", +". X XXX XX X. .", +"..X X X X ..", +". X X X X. .", +"..X X XX X ..", +". X X.. ", +" .XXXXXXXXXXX . ", +" .. . . . ... ", +" . . . .. ", +" ...... "}; diff --git a/app/bin/bitmaps/dflcrcl2.xpm b/app/bin/bitmaps/dflcrcl2.xpm new file mode 100644 index 0000000..c02d1ea --- /dev/null +++ b/app/bin/bitmaps/dflcrcl2.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * dflcrcl2_xpm[] = { +"16 16 4 1", +" c None", +". c #000000000000", +"X c #0000FFFFFFFF", +"o c #FFFF00000000", +" ...... ", +" .XXXXXX. ", +" ..XXXXXXXX.. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +".XXXXXXXXXXXXXX.", +".XXXXXXXXXXXXXX.", +".XXXXXXoooooXXX.", +".XXXXXXooXXXXXX.", +".XXXXXXoXoXXXXX.", +".XXXXXXoXXoXXXX.", +" .XXXXXoXXXoXX. ", +" .XXXXXXXXXXoX. ", +" ..XXXXXXXXXo ", +" .XXXXXX.. ", +" ...... "}; diff --git a/app/bin/bitmaps/dflcrcl3.xpm b/app/bin/bitmaps/dflcrcl3.xpm new file mode 100644 index 0000000..e1bd80a --- /dev/null +++ b/app/bin/bitmaps/dflcrcl3.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * dflcrcl3_xpm[] = { +"16 16 4 1", +" c None", +". c #000000000000", +"X c #0000FFFFFFFF", +"o c #FFFF00000000", +" ...... ", +" .XXXXXX. ", +" ..XXXXXXXX.. ", +" .XXXXXXXXXXXX. ", +" .XXXXXXXXXXXX. ", +".XXXXXXXXXXXXXX.", +".XXXXXXXXXXXXXX.", +".XXXXXXooXXXXXX.", +".XXXXXXooXXXoXX.", +".XXXXXXXXoXXoXX.", +".XXXXXXXXXoXoXX.", +" .XXXXXXXXXooX. ", +" .XXXXXXoooooX. ", +" ..XXXXXXXX.. ", +" .XXXXXX. ", +" ...... "}; diff --git a/app/bin/bitmaps/dline.xpm b/app/bin/bitmaps/dline.xpm new file mode 100644 index 0000000..2105dad --- /dev/null +++ b/app/bin/bitmaps/dline.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dline_xpm[] = { +"16 16 3 1", +" c None", +". c #FFFF00000000", +"X c #000000000000", +" .....", +" ..", +" . .", +" . .", +" . .", +" . ", +" X. ", +" XXX ", +" XXX ", +" XXX ", +" XXX ", +" XXX ", +" XXX ", +" XXX ", +"XXX ", +"XX "}; diff --git a/app/bin/bitmaps/document-new.xpm b/app/bin/bitmaps/document-new.xpm new file mode 100644 index 0000000..df4790d --- /dev/null +++ b/app/bin/bitmaps/document-new.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char *document_new[] = { +/* columns rows colors chars-per-pixel */ +"16 16 16 1", +" c #578757875787", +". c #D8CCC69C0972", +"X c #F68EE212077A", +"o c #4DB24DB22F3A", +"O c #6DBE6C5B24E5", +"+ c #F4FCE3151187", +"@ c #F755E8202525", +"# c #EC82D7FE0467", +"$ c #383338333833", +"% c #E683DCF75128", +"& c #F190EA107861", +"* c #E462E45CE3EB", +"= c #FFFDE56F00EE", +"- c #E1C4CC690469", +"; c None", +": c #FFFFE5710000", +/* pixels */ +" $$$$$$$$$oo.=;;", +"$********%@++#-;", +"$*******&@+@++#:", +"$*******%+@&&@X=", +"$*******%+@&&@X=", +"$*****o*&@+@@+#:", +"$***&****%@++@-;", +"$*********%%%o;;", +"$************$;;", +"$************$;;", +"$*&**********$;;", +"$*******&****$;;", +"$************$;;", +"$************$;;", +" $$$$$$$$$$$$ ;;", +";;;;;;;;;;;;;;;;" +}; diff --git a/app/bin/bitmaps/document-open.xpm b/app/bin/bitmaps/document-open.xpm new file mode 100644 index 0000000..be91cd7 --- /dev/null +++ b/app/bin/bitmaps/document-open.xpm @@ -0,0 +1,35 @@ +/* XPM */ +static char * document_open[] = { +"16 16 16 1", +" c None", +". c #181917", +"+ c #292928", +"@ c #DBDBDB", +"# c #5E5F5E", +"$ c #181919", +"% c #A6A6A5", +"& c #09215F", +"* c #092260", +"= c #88ABD2", +"- c #779BCA", +"; c #4A76B5", +"> c #265299", +", c #243F67", +"' c #0F2D6C", +") c #082160", +" ...+.++. ", +" +.@@@@@@#. ", +"++++.@@@@@@@#$ ", +"+%%#.@#####@@#$ ", +"+%%#+@@@@@@@@%. ", +".%%#.@######@%. ", +".%%#.@@@@@@@@%. ", +".%&&&&&&&&&&&&&*", +"+%&=====-======&", +".%&=;;;;;;;;;;-&", +".%&=;;-;;;-;;;-&", +"$%&=;;;-;;;;;;-&", +".%*;;;>>;>;>>>;&", +"$%&;>>>;>;>>;>;&", +".,&,'>'>>>>''>'*", +"$&&&&&&&&&&&&&*)"}; diff --git a/app/bin/bitmaps/document-print.xpm b/app/bin/bitmaps/document-print.xpm new file mode 100644 index 0000000..69802d6 --- /dev/null +++ b/app/bin/bitmaps/document-print.xpm @@ -0,0 +1,24 @@ +/* XPM */ +static char * document_print_xpm[] = { +"16 16 5 1", +" c None", +". c #BABDB6", +"+ c #EEEEEC", +"@ c #888A85", +"# c #D3D7CF", +" ", +" ........... ", +" .++++@++++. ", +" .+..@@@..+. ", +" .++@@@@@++. ", +" .+...@...+. ", +" .++++@++++. ", +" .+++++++++. ", +" @@@.........@@@", +" @+++++++++++++@", +" @+#+.+#+#+#+#+@", +" @+###########+@", +" @+.@@@@@@@@@.+@", +" @+...........+@", +" @+###########+@", +" @@@@@@@@@@@@@@@"}; diff --git a/app/bin/bitmaps/document-save.xpm b/app/bin/bitmaps/document-save.xpm new file mode 100644 index 0000000..afc2ce6 --- /dev/null +++ b/app/bin/bitmaps/document-save.xpm @@ -0,0 +1,35 @@ +/* XPM */ +static char * document_save[] = { +"16 16 16 1", +" c None", +". c #193A55", +"+ c #192933", +"@ c #0A2342", +"# c #122735", +"$ c #536974", +"% c #A7BDC6", +"& c #5180AA", +"* c #244E77", +"= c #272925", +"- c #102C4B", +"; c #E6E6E6", +"> c #7095AB", +", c #2D5C8D", +"' c #6E6E6E", +") c #9B9C9B", +".+..@@@#. ", +"...$%%&*@ ", +"+=#---&%*#=====.", +"=%;%>,@>&-%;;;;=", +"=;;%%$@,&-'%;;;=", +"=;;@@@@,*@@@@;;=", +"=;;)@&***,&@$%;=", +"=;;;)@&,,>@$;;;=", +"=;;;%)@>&@$%;;;=", +"=;;;;;)#@$;;;;;=", +"=;;;;;;;;;;;;;;=", +"=))))))))))))))=", +"=))')')')')')')=", +"=))')')')')')')=", +"=))))))))))))))=", +"+==============+"}; diff --git a/app/bin/bitmaps/dpoly.xpm b/app/bin/bitmaps/dpoly.xpm new file mode 100644 index 0000000..b46993e --- /dev/null +++ b/app/bin/bitmaps/dpoly.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * dpoly_xpm[] = { +"16 16 3 1", +"X c None", +" c #FFFF00000000", +". c #000000000000", +" ..... XXXXXXXXX", +".XXXXXX..... XXX", +".XXXXXXXXX..XXXX", +".XXXXXXXX.XXXXXX", +".XXXXXX..XXXXXXX", +".XXXXX.XXXXXXXXX", +".XXX..XXXXXXXXXX", +".XX ...XXXXXXXXX", +".XXXXXX......XXX", +" XXXXXXXXXXXX.. ", +"X.XXXXXXXXXXXX.X", +"X.XXXXXXXXXX..XX", +"XX.XXXXXXXX.XXXX", +"XXX.XXXXXX.XXXXX", +"XXX.XXXX..XXXXXX", +"XXXX .. XXXXXXXX"}; diff --git a/app/bin/bitmaps/dtbledge.xpm b/app/bin/bitmaps/dtbledge.xpm new file mode 100644 index 0000000..7772d10 --- /dev/null +++ b/app/bin/bitmaps/dtbledge.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * dtbledge_xpm[] = { +"16 16 4 1", +"o c None", +". c #000000000000", +" c #0000FFFF0000", +"X c #861782078617", +" .", +" ..", +" .X", +" ..X", +" ..XX", +" .XXX", +" ..XXX", +" ..XXXX", +" .XXXXo", +" ..XXXXo", +" .XXXXoo", +"..........XXXooo", +"..........XXXooo", +"XXXXXXXX..XXoooo", +"XXXXXXXX..Xooooo", +"XXXXXXXX..Xooooo"}; diff --git a/app/bin/bitmaps/ebroad.xpm b/app/bin/bitmaps/ebroad.xpm new file mode 100644 index 0000000..11bc4d6 --- /dev/null +++ b/app/bin/bitmaps/ebroad.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * ebroad_xpm[] = { +"41 16 2 1", +". c None", +" c #000000000000", +" .................................. .", +" ..................................... ", +" ...... ... .. .. . ... .. .. .", +" .. .. . .... .. . .. .. . .. . .. . .", +" ..... .. .. .. . .. .. . . .. . .", +" ..... . .... . .... .. .. . .... .. . .", +" .. . . ... . .. .. .. . .. .. ", +".........................................", +".........................................", +"........ .................... ........", +"........ ... ................... ........", +"........ ... . ... ... ... ........", +"........ .. .. . .. . .. . .. ........", +"........ ... . .... .. . .. . .. ........", +"........ ... . .... .. . . . .. ........", +"........ .. ..... ... . .. ........"}; diff --git a/app/bin/bitmaps/edit-redo.xpm b/app/bin/bitmaps/edit-redo.xpm new file mode 100644 index 0000000..3d3a70c --- /dev/null +++ b/app/bin/bitmaps/edit-redo.xpm @@ -0,0 +1,29 @@ +/* XPM */ +static char *edit_redo[] = { +/* columns rows colors chars-per-pixel */ +"16 16 7 1", +" c #4EE29B270680", +". c #73EAD2BE1616", +"X c #94FB9F050707", +"o c #A20EDAEE280A", +"O c #CC4BD6801D9D", +"+ c #AEC8ED136114", +"@ c None", +/* pixels */ +"@@@@@@@@@ @@@@@@", +"@@@@@@@@@ @@@@@", +"@@@@@@@@@ + @@@@", +"@@@@@@ ++ @@@", +"@@@@ X+++++.+ @@", +"@@@ ++ooooo..+ @", +"@@ o+ooooo....+ ", +"@ o+oooO.....+ @", +"@.+OOoooooO.o @@", +"@ +OX oo @@@", +"@ +X @@@@ o @@@@", +"@ o @@@@@ @@@@@", +"@ + @@@@@ @@@@@@", +"@ oX@@@@@@@@@@@@", +"@@ OX@@@@@@@@@@@", +"@@@@@@@@@@@@@@@@" +}; diff --git a/app/bin/bitmaps/edit-undo.xpm b/app/bin/bitmaps/edit-undo.xpm new file mode 100644 index 0000000..4840284 --- /dev/null +++ b/app/bin/bitmaps/edit-undo.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char *edit_undo[] = { +/* columns rows colors chars-per-pixel */ +"16 16 6 1", +" c #BD4FA17C1684", +". c #C482A29B05D3", +"X c #ECB0D6B82037", +"o c #F318E3AA67FF", +"O c #FA79F1F1A77C", +"+ c None", +/* pixels */ +"++++++.+++++++++", +"+++++..+++++++++", +"++++.O.+++++++++", +"+++.OX.. +++++", +"++.OXXoooo ++++", +"+.OXXoXXXXoo +++", +".OXXXXXXXXXXX ++", +"+.OXXXXXXXXXo +", +"++.oXoooooXX.o.+", +"+++.oo.....oXo +", +"++++.o.++++.oo +", +"+++++..+++++.O +", +"++++++.+++++.o +", +"++++++++++++Xo++", +"++++++++++++o ++", +"++++++++++++++++" +}; diff --git a/app/bin/bitmaps/egtbroad.xpm b/app/bin/bitmaps/egtbroad.xpm new file mode 100644 index 0000000..1a6ca6d --- /dev/null +++ b/app/bin/bitmaps/egtbroad.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * egtbroad_xpm[] = { +"41 16 2 1", +". c None", +" c #000000000000", +" .................................. .", +" ..................................... ", +" ...... ... .. .. . ... .. .. .", +" .. .. . .... .. . .. .. . .. . .. . .", +" ..... .. .. .. . .. .. . . .. . .", +" ..... . .... . .... .. .. . .... .. . .", +" .. . . ... . .. .. .. . .. .. ", +".........................................", +".........................................", +"..... ..... .................... .....", +"...... .... ... ................... .....", +"....... ... ... . ... ... ... .....", +"........ .. .. .. . .. . .. . .. .....", +"....... ... ... . .... .. . .. . .. .....", +"...... .... ... . .... .. . . . .. .....", +"..... ..... .. ..... .. . .. ......"}; diff --git a/app/bin/bitmaps/egtsharp.xpm b/app/bin/bitmaps/egtsharp.xpm new file mode 100644 index 0000000..63212a0 --- /dev/null +++ b/app/bin/bitmaps/egtsharp.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * egtsharp_xpm[] = { +"41 16 2 1", +". c None", +" c #000000000000", +" .................................. .", +" ..................................... ", +" ...... ... .. .. . ... .. .. .", +" .. .. . .... .. . .. .. . .. . .. . .", +" ..... .. .. .. . .. .. . . .. . .", +" ..... . .... . .... .. .. . .... .. . .", +" .. . . ... . .. .. .. . .. .. ", +".........................................", +"..... ...... . ......................", +"...... .... ...... ......................", +"....... ... ...... ... .. .. .....", +"........ ... . .. . .. . .. . .. ....", +"....... ........ . .. . .. . .... .. ....", +"...... ......... . .. . . . .... .....", +"..... ..... .. .. .. . . .... .......", +"................................. ......."}; diff --git a/app/bin/bitmaps/elev.xpm b/app/bin/bitmaps/elev.xpm new file mode 100644 index 0000000..de8cd96 --- /dev/null +++ b/app/bin/bitmaps/elev.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * elev_xpm[] = { +"16 16 4 1", +" c None", +". c #FFFF00000000", +"X c #FFFF0000FFFF", +"o c #000000000000", +" . ", +" ... ", +" . . . XXXXX", +" . . . X", +" . X ", +" . X ", +" . X ", +" . X ", +" . XXXXX", +" o o o o ", +"ooooooooooooooo ", +" o o o o ", +" o o o o ", +" o o o o ", +"ooooooooooooooo ", +" o o o o "}; diff --git a/app/bin/bitmaps/eltbroad.xpm b/app/bin/bitmaps/eltbroad.xpm new file mode 100644 index 0000000..a2fb83a --- /dev/null +++ b/app/bin/bitmaps/eltbroad.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * eltbroad_xpm[] = { +"41 16 2 1", +". c None", +" c #000000000000", +" .................................. .", +" ..................................... ", +" ...... ... .. .. . ... .. .. .", +" .. .. . .... .. . .. .. . .. . .. . .", +" ..... .. .. .. . .. .. . . .. . .", +" ..... . .... . .... .. .. . .... .. . .", +" .. . . ... . .. .. .. . .. .. ", +".........................................", +".........................................", +"........ .. .................... .....", +"....... ... ... ................... .....", +"...... .... ... . ... ... ... .....", +"..... ..... .. .. . .. . .. . .. .....", +"...... .... ... . .... .. . .. . .. .....", +"....... ... ... . .... .. . . . .. .....", +"........ .. .. ..... .. . .. ......"}; diff --git a/app/bin/bitmaps/enone.xpm b/app/bin/bitmaps/enone.xpm new file mode 100644 index 0000000..bcc479e --- /dev/null +++ b/app/bin/bitmaps/enone.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * enone_xpm[] = { +"41 16 2 1", +". c None", +" c #000000000000", +" .................................. .", +" ..................................... ", +" ...... ... .. .. . ... .. .. .", +" .. .. . .... .. . .. .. . .. . .. . .", +" ..... .. .. .. . .. .. . . .. . .", +" ..... .. .... . .... .. .. . .... .. . .", +" .. . ... . .. .. .. . .. .. ", +".........................................", +".......... ... ..........................", +".......... .. ..........................", +".......... . .. .. ... ............", +".......... . . . .. . .. . .. ...........", +".......... . . .. . .. . ............", +".......... .. . .. . .. . ..............", +".......... ... .. .. .. .. ...........", +"........................................."}; diff --git a/app/bin/bitmaps/enormal.xpm b/app/bin/bitmaps/enormal.xpm new file mode 100644 index 0000000..fa5890a --- /dev/null +++ b/app/bin/bitmaps/enormal.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * enormal_xpm[] = { +"41 16 2 1", +". c None", +" c #000000000000", +" .................................. .", +" ..................................... ", +" ...... ... .. .. . ... .. .. .", +" .. .. . .... .. . .. .. . .. . .. . .", +" ..... .. .. .. . .. .. . . .. . .", +" ..... .. .... . .... .. .. . .... .. . .", +" .. . ... . .. .. .. . .. .. ", +".........................................", +"..... ... ........................ ......", +"..... .. ........................ ......", +"..... . .. .. .. . ... .. ......", +"..... . . . .. . .. . .. .. . .. . ......", +"..... . . .. . .... .. .. . .. . ......", +"..... .. . .. . .... .. .. . .. . ......", +"..... ... .. .. .... .. .. .. .. .....", +"........................................."}; diff --git a/app/bin/bitmaps/esharp.xpm b/app/bin/bitmaps/esharp.xpm new file mode 100644 index 0000000..b7129f0 --- /dev/null +++ b/app/bin/bitmaps/esharp.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * esharp_xpm[] = { +"41 16 2 1", +". c None", +" c #000000000000", +" .................................. .", +" ..................................... ", +" ...... ... .. .. . ... .. .. .", +" .. .. . .... .. . .. .. . .. . .. . .", +" ..... .. .. .. . .. .. . . .. . .", +" ..... . .... . .... .. .. . .... .. . .", +" .. . . ... . .. .. .. . .. .. ", +".........................................", +"........ . ..........................", +"....... ...... ..........................", +"....... ...... ... .. .. .........", +"........ . .. . .. . .. . .. ........", +"............ . .. . .. . .... .. ........", +"............ . .. . . . .... .........", +"....... .. .. .. . . .... ...........", +"............................. ..........."}; diff --git a/app/bin/bitmaps/exit.xpm b/app/bin/bitmaps/exit.xpm new file mode 100644 index 0000000..ab8c191 --- /dev/null +++ b/app/bin/bitmaps/exit.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char*exit_xpm[]={ +"16 16 2 1", +". c None", +"# c #000000000000", +"................", +"................", +".###.#.#.#.###..", +".#...#.#.#..#...", +".#...#.#.#..#...", +".#...#.#.#..#...", +".#....#..#..#...", +".###..#..#..#...", +".#....#..#..#...", +".#...#.#.#..#...", +".#...#.#.#..#...", +".#...#.#.#..#...", +".###.#.#.#..#...", +"................", +"................", +"................"}; diff --git a/app/bin/bitmaps/export.xpm b/app/bin/bitmaps/export.xpm new file mode 100644 index 0000000..f6bc7d3 --- /dev/null +++ b/app/bin/bitmaps/export.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * export_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" .........", +" . .", +" . . .", +" . . .", +"...... . .", +" .. .", +" .. .", +"...... . .", +" . . .. .", +" . .. . ..", +" . .. ", +" ", +" . . ... .", +" . . .", +" . . . . .", +" "}; diff --git a/app/bin/bitmaps/extend.xpm b/app/bin/bitmaps/extend.xpm new file mode 100644 index 0000000..4fb45d1 --- /dev/null +++ b/app/bin/bitmaps/extend.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * extend_xpm[] = { +"16 16 4 1", +" c None", +". c #FFFF00000000", +"X c #000000000000", +"o c #0000FFFFFFFF", +" ", +" . ", +" ... ", +" . . . ", +" .X . X.", +" XXX.XXX", +" X . X ", +" X X . X ", +" X . X ", +"o o X X X. X ", +"oooooXX .X X ", +"o o X .. X ", +"o o... XX X ", +"o o X XX ", +"oooooXX ", +"o o X "}; diff --git a/app/bin/bitmaps/flash.xbm b/app/bin/bitmaps/flash.xbm new file mode 100644 index 0000000..677978d --- /dev/null +++ b/app/bin/bitmaps/flash.xbm @@ -0,0 +1,9 @@ +#define flash_width 24 +#define flash_height 24 +static char flash_bits[] = { + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x04, 0x04, 0x04, 0x08, 0x04, 0x02, + 0x10, 0x04, 0x01, 0x20, 0x84, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00, + 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf5, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x15, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x20, 0x84, 0x00, + 0x10, 0x04, 0x01, 0x08, 0x04, 0x02, 0x04, 0x04, 0x04, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/flip.xpm b/app/bin/bitmaps/flip.xpm new file mode 100644 index 0000000..03966eb --- /dev/null +++ b/app/bin/bitmaps/flip.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * flip_xpm[] = { +"16 16 4 1", +"# c None", +". c #0000ffffffff", +"b c #000000000000", +"a c #ffff00000000", +".######a#######b", +"..#####a######bb", +"...####a#####bbb", +"....###a####bbbb", +".....##a###bbbbb", +"......#a##bbbbbb", +".......a#bbbbbbb", +".......abbbbbbbb", +".......abbbbbbbb", +".......a#bbbbbbb", +"......#a##bbbbbb", +".....##a###bbbbb", +"....###a####bbbb", +"...####a#####bbb", +"..#####a######bb", +".######a#######b"}; diff --git a/app/bin/bitmaps/go.xpm b/app/bin/bitmaps/go.xpm new file mode 100644 index 0000000..b8a5478 --- /dev/null +++ b/app/bin/bitmaps/go.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * go_xpm[] = { +"16 16 3 1", +" c None", +"X c #000000000000", +"o c #0000FFFF0000", +" XXXXX ", +" XXXXXXXXX ", +" XXoooooooXX ", +" XXoooooooooXX ", +" XoooooooooooX ", +"XXoooooooooooXX ", +"XXoooooooooooXX ", +"XXoooooooooooXX ", +"XXoooooooooooXX ", +"XXoooooooooooXX ", +" XoooooooooooX ", +" XXoooooooooXX ", +" XXoooooooXX ", +" XXXXXXXXX ", +" XXXXX ", +" "}; diff --git a/app/bin/bitmaps/helix.xpm b/app/bin/bitmaps/helix.xpm new file mode 100644 index 0000000..ba0551e --- /dev/null +++ b/app/bin/bitmaps/helix.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * helix_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" ", +" ........... ", +" . ", +" . . ", +" . . ", +" .......... ", +" . . ", +" . . ", +" .......... ", +" . . ", +" . . ", +" ......... ", +" . ", +" . ", +" ............ ", +" "}; diff --git a/app/bin/bitmaps/hndldto.xpm b/app/bin/bitmaps/hndldto.xpm new file mode 100644 index 0000000..f49fef6 --- /dev/null +++ b/app/bin/bitmaps/hndldto.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * hndldto_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" . . ", +" .. ", +" . ... ", +" .. . . ", +" . .. ", +" . ...... . ", +"... ....... ... ", +" . ........ . ", +" . .. . ", +"..... .. ...... ", +" . .. . . ", +" .. ", +" .. ", +" .. ", +" .. ", +" "}; diff --git a/app/bin/bitmaps/hotbarl.xbm b/app/bin/bitmaps/hotbarl.xbm new file mode 100644 index 0000000..09b8309 --- /dev/null +++ b/app/bin/bitmaps/hotbarl.xbm @@ -0,0 +1,7 @@ +#define turnbarl_width 16 +#define turnbarl_height 16 +// static unsigned char turnbarl_bits[] = { +static char turnbarl_bits[] = { + 0x00, 0x30, 0x00, 0x3c, 0x00, 0x3f, 0xc0, 0x3f, 0xe0, 0x3f, 0xf8, 0x3f, + 0xfe, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xfe, 0x3f, 0xf8, 0x3f, 0xe0, 0x3f, + 0x80, 0x3f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x30}; diff --git a/app/bin/bitmaps/hotbarr.xbm b/app/bin/bitmaps/hotbarr.xbm new file mode 100644 index 0000000..bebcebf --- /dev/null +++ b/app/bin/bitmaps/hotbarr.xbm @@ -0,0 +1,7 @@ +#define turnbarr_width 16 +#define turnbarr_height 16 +// static unsigned char turnbarr_bits[] = { +static char turnbarr_bits[] = { + 0x0c, 0x00, 0x3c, 0x00, 0xfc, 0x00, 0xfc, 0x03, 0xfc, 0x07, 0xfc, 0x1f, + 0xfc, 0x7f, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0x7f, 0xfc, 0x1f, 0xfc, 0x07, + 0xfc, 0x01, 0xfc, 0x00, 0x3c, 0x00, 0x0c, 0x00}; diff --git a/app/bin/bitmaps/import.xpm b/app/bin/bitmaps/import.xpm new file mode 100644 index 0000000..f048333 --- /dev/null +++ b/app/bin/bitmaps/import.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * import_xpm[] = { +"16 16 2 1", +" c None", +". c #FFFFFFFFFFFF", +" .......", +" ....... .......", +" ....... ... ...", +" ....... .... ..", +" ....... . .", +" ....... ...... ", +" ....... ...... ", +" ....... . .", +" . .... .... ..", +" .. .. ... ...", +" .... .........", +"................", +".. . . . .....", +"... ... .. .....", +" . . .. .. .....", +"................"}; diff --git a/app/bin/bitmaps/join.xpm b/app/bin/bitmaps/join.xpm new file mode 100644 index 0000000..b42f2cb --- /dev/null +++ b/app/bin/bitmaps/join.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * join_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ", +" ", +" . .", +" . ", +" . . .", +" . ", +" . . .", +" . ", +" XXX . .", +". . XX ", +"..... X X X ", +". . X X ", +". .XXXX ", +". . X ", +"..... X ", +". . "}; diff --git a/app/bin/bitmaps/l1.xbm b/app/bin/bitmaps/l1.xbm new file mode 100644 index 0000000..9cadd73 --- /dev/null +++ b/app/bin/bitmaps/l1.xbm @@ -0,0 +1,6 @@ +#define l1_width 10 +#define l1_height 16 +static char l1_bits[] = { 0x00, 0x00, + 0x30, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, + 0xfc, 0x00, 0xfc, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l10.xbm b/app/bin/bitmaps/l10.xbm new file mode 100644 index 0000000..349f3cf --- /dev/null +++ b/app/bin/bitmaps/l10.xbm @@ -0,0 +1,6 @@ +#define l10_width 10 +#define l10_height 16 +static char l10_bits[] = { 0x00, 0x00, + 0xe6, 0x01, 0xe7, 0x01, 0x37, 0x03, 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, + 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, + 0xe6, 0x01, 0xef, 0x01, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l11.xbm b/app/bin/bitmaps/l11.xbm new file mode 100644 index 0000000..fa3ca0e --- /dev/null +++ b/app/bin/bitmaps/l11.xbm @@ -0,0 +1,6 @@ +#define l11_width 10 +#define l11_height 16 +static char l11_bits[] = { 0x00, 0x00, + 0xc6, 0x00, 0xe7, 0x00, 0xe7, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, + 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xc6, 0x00, + 0xc6, 0x00, 0xef, 0x01, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l12.xbm b/app/bin/bitmaps/l12.xbm new file mode 100644 index 0000000..c1a5274 --- /dev/null +++ b/app/bin/bitmaps/l12.xbm @@ -0,0 +1,6 @@ +#define l12_width 10 +#define l12_height 16 +static char l12_bits[] = { 0x00, 0x00, + 0xe6, 0x00, 0xf7, 0x01, 0x17, 0x03, 0x06, 0x03, 0x06, 0x03, 0x86, 0x01, + 0x86, 0x01, 0xc6, 0x00, 0xc6, 0x00, 0x66, 0x00, 0x66, 0x00, 0x36, 0x00, + 0xf6, 0x03, 0xf7, 0x03, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l13.xbm b/app/bin/bitmaps/l13.xbm new file mode 100644 index 0000000..1414eb0 --- /dev/null +++ b/app/bin/bitmaps/l13.xbm @@ -0,0 +1,6 @@ +#define l13_width 10 +#define l13_height 16 +static char l13_bits[] = { 0x00, 0x00, + 0xe6, 0x00, 0xf7, 0x01, 0x17, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, + 0xc6, 0x01, 0xc6, 0x01, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x16, 0x03, + 0xf6, 0x01, 0xe7, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l14.xbm b/app/bin/bitmaps/l14.xbm new file mode 100644 index 0000000..05e820f --- /dev/null +++ b/app/bin/bitmaps/l14.xbm @@ -0,0 +1,6 @@ +#define l14_width 10 +#define l14_height 16 +static char l14_bits[] = { 0x00, 0x00, + 0x06, 0x01, 0x87, 0x01, 0x87, 0x01, 0xc6, 0x01, 0xe6, 0x01, 0xa6, 0x01, + 0xb6, 0x01, 0x96, 0x01, 0xf6, 0x03, 0xf6, 0x03, 0x86, 0x01, 0x86, 0x01, + 0x86, 0x01, 0x8f, 0x01, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l15.xbm b/app/bin/bitmaps/l15.xbm new file mode 100644 index 0000000..22daac2 --- /dev/null +++ b/app/bin/bitmaps/l15.xbm @@ -0,0 +1,6 @@ +#define l15_width 10 +#define l15_height 16 +static char l15_bits[] = { 0x00, 0x00, + 0xf6, 0x03, 0xf7, 0x03, 0x37, 0x00, 0x36, 0x00, 0x36, 0x00, 0xf6, 0x00, + 0xf6, 0x01, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, + 0xf6, 0x01, 0xf7, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l16.xbm b/app/bin/bitmaps/l16.xbm new file mode 100644 index 0000000..9f86249 --- /dev/null +++ b/app/bin/bitmaps/l16.xbm @@ -0,0 +1,6 @@ +#define l16_width 10 +#define l16_height 16 +static char l16_bits[] = { 0x00, 0x00, + 0xc6, 0x01, 0xe7, 0x03, 0x37, 0x00, 0x36, 0x00, 0x36, 0x00, 0xf6, 0x01, + 0xf6, 0x01, 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, + 0xe6, 0x01, 0xcf, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l17.xbm b/app/bin/bitmaps/l17.xbm new file mode 100644 index 0000000..8169be6 --- /dev/null +++ b/app/bin/bitmaps/l17.xbm @@ -0,0 +1,6 @@ +#define l17_width 10 +#define l17_height 16 +static char l17_bits[] = { 0x00, 0x00, + 0xf6, 0x03, 0xf7, 0x03, 0x07, 0x03, 0x06, 0x03, 0x06, 0x03, 0x86, 0x01, + 0x86, 0x01, 0x86, 0x00, 0xc6, 0x00, 0x46, 0x00, 0x66, 0x00, 0x26, 0x00, + 0x36, 0x00, 0x17, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l18.xbm b/app/bin/bitmaps/l18.xbm new file mode 100644 index 0000000..68742a1 --- /dev/null +++ b/app/bin/bitmaps/l18.xbm @@ -0,0 +1,6 @@ +#define l18_width 10 +#define l18_height 16 +static char l18_bits[] = { 0x00, 0x00, + 0xc6, 0x00, 0xe7, 0x01, 0x37, 0x03, 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, + 0xe6, 0x01, 0xe6, 0x01, 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, + 0xe6, 0x01, 0xcf, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l19.xbm b/app/bin/bitmaps/l19.xbm new file mode 100644 index 0000000..0dc6ea6 --- /dev/null +++ b/app/bin/bitmaps/l19.xbm @@ -0,0 +1,6 @@ +#define l19_width 10 +#define l19_height 16 +static char l19_bits[] = { 0x00, 0x00, + 0xc6, 0x00, 0xe7, 0x01, 0x37, 0x03, 0x36, 0x03, 0x36, 0x03, 0x36, 0x03, + 0x36, 0x03, 0xe6, 0x03, 0xc6, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, + 0xf6, 0x01, 0xf7, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l2.xbm b/app/bin/bitmaps/l2.xbm new file mode 100644 index 0000000..6e09c68 --- /dev/null +++ b/app/bin/bitmaps/l2.xbm @@ -0,0 +1,6 @@ +#define l2_width 10 +#define l2_height 16 +static char l2_bits[] = { 0x00, 0x00, + 0xfc, 0x00, 0xfe, 0x01, 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x80, 0x01, + 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0xff, 0x03, 0xff, 0x03, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l20.xbm b/app/bin/bitmaps/l20.xbm new file mode 100644 index 0000000..473553b --- /dev/null +++ b/app/bin/bitmaps/l20.xbm @@ -0,0 +1,6 @@ +#define l20_width 10 +#define l20_height 16 +static char l20_bits[] = { 0x00, 0x00, + 0x8e, 0x01, 0xcf, 0x03, 0xdb, 0x02, 0x59, 0x03, 0xd8, 0x02, 0x58, 0x03, + 0xc8, 0x02, 0x4c, 0x03, 0xcc, 0x02, 0x46, 0x03, 0xc6, 0x02, 0x43, 0x03, + 0xdf, 0x03, 0x9f, 0x01, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l3.xbm b/app/bin/bitmaps/l3.xbm new file mode 100644 index 0000000..66ac3ab --- /dev/null +++ b/app/bin/bitmaps/l3.xbm @@ -0,0 +1,6 @@ +#define l3_width 10 +#define l3_height 16 +static char l3_bits[] = { 0x00, 0x00, + 0xfc, 0x00, 0xfe, 0x01, 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x00, 0x03, + 0xe0, 0x01, 0xe0, 0x01, 0x00, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, + 0xfe, 0x01, 0xfc, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l4.xbm b/app/bin/bitmaps/l4.xbm new file mode 100644 index 0000000..d818f48 --- /dev/null +++ b/app/bin/bitmaps/l4.xbm @@ -0,0 +1,6 @@ +#define l4_width 10 +#define l4_height 16 +static char l4_bits[] = { 0x00, 0x00, + 0x80, 0x01, 0xc0, 0x01, 0xe0, 0x01, 0xb0, 0x01, 0x98, 0x01, 0x8c, 0x01, + 0x86, 0x01, 0x83, 0x01, 0xff, 0x03, 0xff, 0x03, 0x80, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l5.xbm b/app/bin/bitmaps/l5.xbm new file mode 100644 index 0000000..c350ac3 --- /dev/null +++ b/app/bin/bitmaps/l5.xbm @@ -0,0 +1,6 @@ +#define l5_width 10 +#define l5_height 16 +static char l5_bits[] = { 0x00, 0x00, + 0xff, 0x03, 0xff, 0x03, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x7f, 0x00, + 0xff, 0x00, 0x80, 0x01, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x80, 0x01, + 0xff, 0x00, 0x7f, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l6.xbm b/app/bin/bitmaps/l6.xbm new file mode 100644 index 0000000..71351c2 --- /dev/null +++ b/app/bin/bitmaps/l6.xbm @@ -0,0 +1,6 @@ +#define l6_width 10 +#define l6_height 16 +static char l6_bits[] = { 0x00, 0x00, + 0xfc, 0x01, 0xfe, 0x03, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x01, + 0xff, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0xfe, 0x01, 0xfc, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l7.xbm b/app/bin/bitmaps/l7.xbm new file mode 100644 index 0000000..220e3d9 --- /dev/null +++ b/app/bin/bitmaps/l7.xbm @@ -0,0 +1,6 @@ +#define l7_width 10 +#define l7_height 16 +static char l7_bits[] = { 0x00, 0x00, + 0xff, 0x03, 0xff, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x80, 0x01, + 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x03, 0x00, 0x01, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l8.xbm b/app/bin/bitmaps/l8.xbm new file mode 100644 index 0000000..bb0e2bc --- /dev/null +++ b/app/bin/bitmaps/l8.xbm @@ -0,0 +1,6 @@ +#define l8_width 10 +#define l8_height 16 +static char l8_bits[] = { 0x00, 0x00, + 0xfc, 0x00, 0xfe, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0xfe, 0x01, 0xfe, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0xfe, 0x01, 0xfc, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/l9.xbm b/app/bin/bitmaps/l9.xbm new file mode 100644 index 0000000..1589fda --- /dev/null +++ b/app/bin/bitmaps/l9.xbm @@ -0,0 +1,6 @@ +#define l9_width 10 +#define l9_height 16 +static char l9_bits[] = { 0x00, 0x00, + 0xfc, 0x00, 0xfe, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0xfe, 0x03, 0xfe, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, + 0xff, 0x01, 0xfe, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/move.xpm b/app/bin/bitmaps/move.xpm new file mode 100644 index 0000000..ab90555 --- /dev/null +++ b/app/bin/bitmaps/move.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * move_xpm[] = { +"16 16 4 1", +" c None", +". c #0000FFFFFFFF", +"X c #000000000000", +"o c #FFFF00000000", +" . . X X ", +"...... XXXXXX ", +" . . X X ", +" . . o X X ", +" . . oX X ", +"...... XoXXXX ", +" . . Xo X ", +" . oooooooooXX ", +" . . Xo X ", +"...... XoXXXX ", +" . . oX X ", +" . . o X X ", +" . . X X ", +"...... XXXXXX ", +" . . X X ", +" "}; diff --git a/app/bin/bitmaps/movedesc.xpm b/app/bin/bitmaps/movedesc.xpm new file mode 100644 index 0000000..520cfd6 --- /dev/null +++ b/app/bin/bitmaps/movedesc.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * movedesc_xpm[] = { +"16 16 4 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +"o c #0000FFFFFFFF", +" .. .. .. .. ", +" . . . . . ", +" . . .. .. . ", +" . . . . . ", +" .. ..X.. .. ", +" XXX ", +" X X X ", +" X X X ", +" X ", +" . . X . ", +"..oo..ooX o.oo..", +" .o o o Xo o. ", +" .o o ooXoo o. ", +" .o o o X o o. ", +"..oo..oo.oo.oo..", +" . . . . "}; diff --git a/app/bin/bitmaps/mtbox.xbm b/app/bin/bitmaps/mtbox.xbm new file mode 100644 index 0000000..8d7f81c --- /dev/null +++ b/app/bin/bitmaps/mtbox.xbm @@ -0,0 +1,7 @@ +#define mtbox_width 16 +#define mtbox_height 16 +// static unsigned char mtbox_bits[] = { +static char mtbox_bits[] = { + 0x00, 0x00, 0xfe, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0xfe, 0x7f, 0x00, 0x00}; diff --git a/app/bin/bitmaps/newcar.xpm b/app/bin/bitmaps/newcar.xpm new file mode 100644 index 0000000..e1bc24a --- /dev/null +++ b/app/bin/bitmaps/newcar.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * newcar_xpm[] = { +"16 16 4 1", +". c None", +"X c #800080008000", +"o c #000000000000", +" c #FFFFFFFF0000", +" ...X. X.X.... ", +"oooo... ..XX. ", +".. o... ...X .", +"...o ..o ..X ..", +"...o ooo. oo...", +"...o. ooo oo...", +"oooooooooooooo ", +"oooooooooooooo ", +"oooooooooooooo..", +"oooooooooooooooo", +"oooooooooooooooo", +"...oo.. oo ...o", +"..oooo.oooo ..o", +". oooo.oooo. o.", +" .oo.. oo...oo.", +" ...... ..... "}; diff --git a/app/bin/bitmaps/note.xbm b/app/bin/bitmaps/note.xbm new file mode 100644 index 0000000..7ca281a --- /dev/null +++ b/app/bin/bitmaps/note.xbm @@ -0,0 +1,6 @@ +#define note_width 16 +#define note_height 16 +static char note_bits[] = { + 0xff, 0x03, 0x01, 0x06, 0x81, 0x0a, 0x81, 0x12, 0x81, 0x22, 0x81, 0x7e, + 0x81, 0x40, 0x81, 0x40, 0x81, 0x40, 0x81, 0x40, 0x81, 0x40, 0x01, 0x40, + 0x81, 0x40, 0x01, 0x40, 0x01, 0x40, 0xff, 0x7f}; diff --git a/app/bin/bitmaps/openbutt.xpm b/app/bin/bitmaps/openbutt.xpm new file mode 100644 index 0000000..99b9666 --- /dev/null +++ b/app/bin/bitmaps/openbutt.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * openbutt_xpm[] = { +"7 16 2 1", +" c None", +". c #000000000000", +" ", +" ", +" ", +" ", +" ", +" ", +".......", +" ..... ", +" ... ", +" . ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/app/bin/bitmaps/parallel.xpm b/app/bin/bitmaps/parallel.xpm new file mode 100644 index 0000000..3fe5591 --- /dev/null +++ b/app/bin/bitmaps/parallel.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * parallel_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" ", +" ", +" ", +"................", +" . ", +" . . ", +" . . ", +" ", +" ", +" . . . ", +"................", +" . . . ", +" . . . ", +" . . . ", +"................", +" . . . "}; diff --git a/app/bin/bitmaps/partlist.xpm b/app/bin/bitmaps/partlist.xpm new file mode 100644 index 0000000..78c4674 --- /dev/null +++ b/app/bin/bitmaps/partlist.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * partlist_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" . . . ", +"..... . . ", +" . . . ", +"..... . ", +" . . . ", +" ", +". .. .. .. ", +". . . . .", +". . .. .. .. ", +". . . . . .", +". .. .. . .", +" ... . . ", +" . . . . ", +" . .. ... . ", +" . . . ", +" . . ..."}; diff --git a/app/bin/bitmaps/profile.xpm b/app/bin/bitmaps/profile.xpm new file mode 100644 index 0000000..df22abe --- /dev/null +++ b/app/bin/bitmaps/profile.xpm @@ -0,0 +1,24 @@ +/* XPM */ +static char * profile_xpm[] = { +"16 16 5 1", +" c None", +". c #FFFF00000000", +"X c #0000FFFFFFFF", +"o c #00000000FFFF", +"O c #000000000000", +" ", +" . ", +" .Xo ", +" .Xo ", +" .XXXo o ", +".XXXXo .Xo", +"OXXXXXo .XO", +"OXXXXXo .XXO", +"OXXXXXXo .XXXO", +"OXXXXXXXo .XXXXO", +"OXXXXXXXXoXXXXXO", +"OXXXXXXXXXXXXXXO", +"OXXXXXXXXXXXXXXO", +"OOOOOOOOOOOOOOOO", +" O O O ", +" O O O "}; diff --git a/app/bin/bitmaps/pull.xpm b/app/bin/bitmaps/pull.xpm new file mode 100644 index 0000000..a25248e --- /dev/null +++ b/app/bin/bitmaps/pull.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * pull_xpm[] = { +"16 16 4 1", +" c None", +". c #0000FFFFFFFF", +"X c #FFFF00000000", +"o c #000000000000", +" . . . . ", +"...... ......", +" . . . . ", +" . . . . ", +"...... ......", +" . . . . ", +" X X ", +" XX XX ", +"XXXXXXX XXXXXXX", +" XX XX ", +" o X o Xo ", +"ooo oooo oooo oo", +" o o o ", +" o o o ", +"oo oooo oooo ooo", +" o o o "}; diff --git a/app/bin/bitmaps/rotate.xpm b/app/bin/bitmaps/rotate.xpm new file mode 100644 index 0000000..00b6b5a --- /dev/null +++ b/app/bin/bitmaps/rotate.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * rotate_xpm[] = { +"16 16 4 1", +" c None", +". c #0000FFFFFFFF", +"X c #FFFF00000000", +"o c #000000000000", +" . .XX ", +"....... XX ", +" . . XX ", +" . . X X", +"....... o X X", +" . . XX", +" . . o XXXX", +"....... ", +" . . o ", +" .o o o o o ", +".ooooooooooooooo", +" .o o o o o ", +" .oo o o o o ", +"..o..o. o o o ", +" ooooooooooooooo", +" o o o o o "}; diff --git a/app/bin/bitmaps/ruler.xpm b/app/bin/bitmaps/ruler.xpm new file mode 100644 index 0000000..55d4842 --- /dev/null +++ b/app/bin/bitmaps/ruler.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * ruler_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" .. ", +" . ", +" . ", +" . . ", +" . ... ", +" . ", +". ", +". ", +" .. ", +" .. . . ", +" .. . ", +" .. . ", +" .. . ", +" .. ", +" .. ", +" ."}; diff --git a/app/bin/bitmaps/select.xpm b/app/bin/bitmaps/select.xpm new file mode 100644 index 0000000..c630541 --- /dev/null +++ b/app/bin/bitmaps/select.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * select_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ", +" . . . . ", +"................", +" . . . . ", +" . . . ", +" . XX . ", +"... XXXXXX ..", +" . XXXXXXXX . ", +" XXXXXXXX ", +" XXXXXXX ", +" XXXXXX ", +" XXXXXX ", +" XXX XXX ", +" XX XXX ", +" XXX ", +" X "}; diff --git a/app/bin/bitmaps/snapcurs.xbm b/app/bin/bitmaps/snapcurs.xbm new file mode 100644 index 0000000..06db450 --- /dev/null +++ b/app/bin/bitmaps/snapcurs.xbm @@ -0,0 +1,7 @@ +#define snapcurs_width 16 +#define snapcurs_height 16 +// static unsigned char snapcurs_bits[] = { +static char snapcurs_bits[] = { + 0x00, 0x00, 0x44, 0x44, 0xaa, 0xaa, 0xfc, 0x40, 0xf8, 0x07, 0xfc, 0x47, + 0xfa, 0xab, 0xfc, 0x45, 0xf0, 0x03, 0x74, 0x47, 0xb2, 0xae, 0x44, 0x5c, + 0x00, 0x08, 0x44, 0x44, 0xaa, 0xaa, 0x44, 0x44}; diff --git a/app/bin/bitmaps/snapvis.xbm b/app/bin/bitmaps/snapvis.xbm new file mode 100644 index 0000000..cab6ee5 --- /dev/null +++ b/app/bin/bitmaps/snapvis.xbm @@ -0,0 +1,7 @@ +#define snapvis_width 16 +#define snapvis_height 16 +// static unsigned char snapvis_bits[] = { +static char snapvis_bits[] = { + 0x44, 0x44, 0x44, 0x44, 0xff, 0xff, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0xff, 0xff, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xff, 0xff, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0xff, 0xff, 0x44, 0x44}; diff --git a/app/bin/bitmaps/splittrk.xpm b/app/bin/bitmaps/splittrk.xpm new file mode 100644 index 0000000..3a03c77 --- /dev/null +++ b/app/bin/bitmaps/splittrk.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * splittrk_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" . . ", +" . ", +" X . . .", +" X . . . ", +" X . . .", +" XXXX . . . ", +" . ", +" . . ", +" . . ", +" . ", +" . . . XXXX ", +". . . X ", +" . . . X ", +". . . X ", +" . ", +" . . "}; diff --git a/app/bin/bitmaps/square10.xbm b/app/bin/bitmaps/square10.xbm new file mode 100644 index 0000000..d419974 --- /dev/null +++ b/app/bin/bitmaps/square10.xbm @@ -0,0 +1,7 @@ +#define square10_width 14 +#define square10_height 14 +// static unsigned char square10_bits[] = { +static char square10_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff}; diff --git a/app/bin/bitmaps/stop.xpm b/app/bin/bitmaps/stop.xpm new file mode 100644 index 0000000..5c66ba1 --- /dev/null +++ b/app/bin/bitmaps/stop.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * stop_xpm[] = { +"16 16 3 1", +" c None", +"X c #000000000000", +"o c #FFFF00000000", +" XXXXXXX ", +" XXXXXXXXX ", +" XXoooooooXX ", +" XXoooooooooXX ", +"XXoooooooooooXX ", +"XXoooooooooooXX ", +"XXoooooooooooXX ", +"XXoooooooooooXX ", +"XXoooooooooooXX ", +"XXoooooooooooXX ", +"XXoooooooooooXX ", +" XXoooooooooXX ", +" XXoooooooXX ", +" XXXXXXXXX ", +" XXXXXXX ", +" "}; diff --git a/app/bin/bitmaps/straight.xpm b/app/bin/bitmaps/straight.xpm new file mode 100644 index 0000000..b5fc178 --- /dev/null +++ b/app/bin/bitmaps/straight.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * straight_xpm[] = { +"16 16 3 1", +" c None", +". c #FFFF00000000", +"X c #000000000000", +" .....", +" X ..", +" X X . .", +" X . X.", +" X . X .", +" X X . X ", +" X . X X ", +" X . X ", +" X X . X ", +" X . X X ", +" X X X ", +"X X X ", +" X X X ", +"X X X ", +" X ", +" X X "}; diff --git a/app/bin/bitmaps/struct.xpm b/app/bin/bitmaps/struct.xpm new file mode 100644 index 0000000..a37979c --- /dev/null +++ b/app/bin/bitmaps/struct.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * struct_xpm[] = { +"16 16 3 1", +"o c None", +" c #000000000000", +". c #FFFFFFFFFFFF", +" ", +" ............ ", +" . .......... . ", +" .. ........ .. ", +" ... ...... ... ", +" .... .... ", +" ... ...... ... ", +" .. ........ .. ", +" . .......... . ", +" ............ ", +" ", +"o ............ o", +"oo .......... oo", +"ooo ........ ooo", +"oooo oo ", +"oooooooooooooo "}; diff --git a/app/bin/bitmaps/switchmdel.xpm b/app/bin/bitmaps/switchmdel.xpm new file mode 100644 index 0000000..a6dc5ae --- /dev/null +++ b/app/bin/bitmaps/switchmdel.xpm @@ -0,0 +1,54 @@ +/* XPM */ +static char * switchmdel_xpm[] = { +"16 16 35 1", +" c None", +". c #CC0000", +"+ c #CB0000", +"@ c #FF0000", +"# c #FE0C28", +"$ c #FE112B", +"% c #FF0101", +"& c #F00D21", +"* c #FE142E", +"= c #F80E27", +"- c #D00103", +"; c #E80306", +"> c #FF0303", +", c #FF0611", +"' c #FF060D", +") c #FF0202", +"! c #F51022", +"~ c #FD132E", +"{ c #5A5954", +"] c #E71B32", +"^ c #FE132D", +"/ c #595C58", +"( c #575954", +"_ c #FE102C", +": c #61635F", +"< c #5A5D5A", +"[ c #545652", +"} c #5E5F5C", +"| c #555753", +"1 c #FE1530", +"2 c #5B5C58", +"3 c #626460", +"4 c #565752", +"5 c #575854", +"6 c #575A56", +" ", +" .. ", +" +.+ @ ", +" #$ ... @@% ", +" ##&.. %@@ ", +" *#=-. @@ ", +" ##;@> ", +" ,@' ", +" )@!#~ ", +" %@ {]#^ ", +" @@ /( #_ ", +" @@ :< # ", +" @@ [ }| 1# ", +"@@ 2 #$ ", +" 3 # ", +" 4 56 | "}; diff --git a/app/bin/bitmaps/switchmedit.xpm b/app/bin/bitmaps/switchmedit.xpm new file mode 100644 index 0000000..05168b2 --- /dev/null +++ b/app/bin/bitmaps/switchmedit.xpm @@ -0,0 +1,78 @@ +/* XPM */ +static char * switchmedit_xpm[] = { +"16 16 59 1", +" c None", +". c #6F4D10", +"+ c #A0711A", +"@ c #EF2828", +"# c #EF2929", +"$ c #704E10", +"% c #A9781B", +"& c #CB9022", +"* c #EE2828", +"= c #ED2828", +"- c #B07D1D", +"; c #EE2929", +"> c #715010", +", c #B7821E", +"' c #CA8F22", +") c #9B6C1A", +"! c #71510F", +"~ c #BC851F", +"{ c #C98E22", +"] c #906717", +"^ c #684A0F", +"/ c #F02929", +"( c #755311", +"_ c #BF8820", +": c #C68D21", +"< c #886116", +"[ c #674A10", +"} c #D92D24", +"| c #775511", +"1 c #C38B21", +"2 c #C38A21", +"3 c #7E5B14", +"4 c #6D4B11", +"5 c #D03026", +"6 c #855517", +"7 c #C68D22", +"8 c #795613", +"9 c #815D19", +"0 c #C88F21", +"a c #B9831F", +"b c #725012", +"c c #896C2E", +"d c #D3AE5A", +"e c #CB9124", +"f c #B4801E", +"g c #6E4D11", +"h c #CDB678", +"i c #E2D29B", +"j c #BC9746", +"k c #8A6C2B", +"l c #D7BC78", +"m c #AD904F", +"n c #816632", +"o c #836C40", +"p c #65490C", +"q c #7A5B21", +"r c #785618", +"s c #878A85", +"t c #898B86", +" .+", +" @# $%&", +" *#= $-&&", +" ;#; >,&')", +" ## !~&{]^", +" ##/ (_&:<[ ", +" *#}|1&234 ", +" 567&_8 ", +" 90&ab ", +" cdefg ", +" hijg ", +" klmnog ", +" pqrg sg ", +" g ssg ", +" g tsg ", +" gggggggg "}; diff --git a/app/bin/bitmaps/switchmnew.xpm b/app/bin/bitmaps/switchmnew.xpm new file mode 100644 index 0000000..403a8b7 --- /dev/null +++ b/app/bin/bitmaps/switchmnew.xpm @@ -0,0 +1,66 @@ +/* XPM */ +static char * switchmnew_xpm[] = { +"16 16 47 1", +" c None", +". c #FFF414", +"+ c #FFF416", +"@ c #FFF314", +"# c #EF2828", +"$ c #EF2929", +"% c #FFF518", +"& c #FFF41E", +"* c #FFF639", +"= c #FFF52B", +"- c #FFF41A", +"; c #FFF512", +"> c #EE2828", +", c #ED2828", +"' c #FFF513", +") c #FFF521", +"! c #FFF969", +"~ c #FFFA92", +"{ c #FFF97C", +"] c #FFF63B", +"^ c #FFF317", +"/ c #EE2929", +"( c #FFF417", +"_ c #FFF63C", +": c #FFFA95", +"< c #FFFDE7", +"[ c #FFFBB3", +"} c #FFF75C", +"| c #FFF51B", +"1 c #FFF531", +"2 c #FFF980", +"3 c #FFFBB5", +"4 c #FFFA98", +"5 c #FFF64C", +"6 c #F35820", +"7 c #FFF63E", +"8 c #FFF74D", +"9 c #FFF51F", +"0 c #FFF515", +"a c #EC2828", +"b c #FFF312", +"c c #FFF319", +"d c #E92C2C", +"e c #D5403F", +"f c #898A83", +"g c #8B7247", +"h c #8A8577", +" .+@ ", +" #$ %&*=-; ", +" >$, ')!~{]^ ", +" /$/ (_:<[}| ", +" $$ (12345- ", +" $$6 |7}890 ", +" >$a bc|-0 ", +" de ", +" f ", +" g ", +" gg ", +" ghf ", +" g fg ", +" g f g ", +" g f g ", +" gggggggg "}; diff --git a/app/bin/bitmaps/switchmotormark.xbm b/app/bin/bitmaps/switchmotormark.xbm new file mode 100644 index 0000000..7a476d9 --- /dev/null +++ b/app/bin/bitmaps/switchmotormark.xbm @@ -0,0 +1,6 @@ +#define switchmotormark_width 16 +#define switchmotormark_height 16 +static char switchmotormark_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC8, 0x3F, + 0xC8, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xC8, 0x3F, 0xC8, 0x3F, 0x08, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, }; diff --git a/app/bin/bitmaps/text.xpm b/app/bin/bitmaps/text.xpm new file mode 100644 index 0000000..176621d --- /dev/null +++ b/app/bin/bitmaps/text.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * text_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" .. ", +" .. ", +" .... ", +" .... ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .......... ", +" ............ ", +" .. .. ", +" .. .. ", +" .. .. ", +".. ..", +"... ..."}; diff --git a/app/bin/bitmaps/train.xpm b/app/bin/bitmaps/train.xpm new file mode 100644 index 0000000..c94593e --- /dev/null +++ b/app/bin/bitmaps/train.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * train_xpm[]={ +"16 16 3 1", +". c None", +"# c #800080008000", +"a c #000000000000", +".....#..#.#.....", +"aaaa.......##...", +"...a........#...", +"...a...a...#....", +"...a..aaa..aa...", +"...a..aaa..aa...", +"aaaaaaaaaaaaaa..", +"aaaaaaaaaaaaaa..", +"aaaaaaaaaaaaaa..", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"...aa...aa.....a", +"..aaaa.aaaa....a", +"..aaaa.aaaa..aa.", +"...aa...aa...aa.", +"................"}; diff --git a/app/bin/bitmaps/tunnel.xpm b/app/bin/bitmaps/tunnel.xpm new file mode 100644 index 0000000..79aed20 --- /dev/null +++ b/app/bin/bitmaps/tunnel.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * tunnel_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" .. ", +" .. ", +" .. ", +" .. ", +". . .. ", +".......... . . ", +". . .. ", +". . .. ", +". . .. ", +".......... . . ", +". . .. ", +" .. ", +" .. ", +" .. ", +" .. ", +" "}; diff --git a/app/bin/bitmaps/turnout.xpm b/app/bin/bitmaps/turnout.xpm new file mode 100644 index 0000000..91d7af5 --- /dev/null +++ b/app/bin/bitmaps/turnout.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * turnout_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" ", +" ", +" . . ", +" .. ", +" . ... ", +" .. . . ", +" . ... .. ", +" . .. . .. . ", +"............... ", +" . . .. . ", +" . ... . . ", +"............... ", +" . . . . ", +" ", +" ", +" "}; diff --git a/app/bin/bitmaps/turntbl.xpm b/app/bin/bitmaps/turntbl.xpm new file mode 100644 index 0000000..ca6f359 --- /dev/null +++ b/app/bin/bitmaps/turntbl.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * turntbl_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" . ", +". .. ", +" . .. ..... ", +" . .. . . . ", +" . . . . . . ", +" . . . . ", +" . . . . .", +".... . . .", +" . . . . .", +".... . . .", +" . . . . .", +" . . . . ", +" . . . . . . ", +" . .. . . . ", +" . .. ..... ", +" .. "}; diff --git a/app/bin/bitmaps/xtc.xpm b/app/bin/bitmaps/xtc.xpm new file mode 100644 index 0000000..3c13e32 --- /dev/null +++ b/app/bin/bitmaps/xtc.xpm @@ -0,0 +1,83 @@ +/* XPM */ +static char * xtc_xpm[] = { +"64 64 16 1", +" c None", +". c #888A85", +"+ c #555753", +"@ c #D3D7CF", +"# c #BABDB6", +"$ c #EEEEEC", +"% c #4E9A06", +"& c #73D216", +"* c #8AE234", +"= c #E9B96E", +"- c #FCAF3E", +"; c #C4A000", +"> c #C17D11", +", c #8F5902", +"' c #F57900", +") c #2E3436", +" .......................................+++++++++++ ", +" .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.+ ", +" .#$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$. ", +" .#$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$. ", +" .#$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$. ", +" .#$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$. ", +" .#$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$. ", +" .#$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$. ", +" .#$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$. ", +" .#$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$. ", +" .#$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" .#$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ", +" ..$$$@$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@%%+ ", +" ..$$@#@$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$#%&@+ ", +" ..$$$@$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@%%*@@+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%&*@**+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@%*@=***+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$#%*=***&%+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%*@***&%%+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@%&*****%%%%+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@%&=@***%%%%@+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$=%*=***&%%%%#$+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$@%&*@***&%%%%@$$+ ", +" ..$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$@%&*=***%%%%%@$$$+ ", +" +.$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$#%**=**&%%%%#$$$$$+ ", +" +.$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$=%*=@**&%%%%@$$$$$$+ ", +" +.$$$$$$@$$$$$$$$$$$$$$$$$$$$$$=-*@***%%%%%@$$$$$$$+ ", +" +.$$$$$$@$$$===$$$$$$$$$$$$$$$=--=**&%%%%#$$$$$$$$$+ ", +" +.$$$$$$@$$@@@@===$$$$$$$$$$$@----;*%%%%@$$$$$$$$$$+ ", +" +.$$$@$$@$$@#.#=@=@=@$$$$$$$@------;%%%@$$$$$$$$$$$+ ", +" +.$$@#@$@$$$$=..#=@==@@@$$$$--------;%$$$$$$$$$$$$$+ ", +" +.$$$@$$@$$$@@=@#.=@=$===@==------=@$$$$$$$$$$$$$$$+ ", +" +.$$$$$$@$$$#@=@=@...@==@==------=@$$$$$$$$$$$$$$$$+ ", +" +.$$$$$$@$$@#.=##===@...#=-----====$==$@@$$@$$$$$$$+ ", +" +.$$$$$$@$$@==>,.>===-===>--=>======-==-'=='@==$$$$+ ", +" +.$$$$$$@$$$#===.,,,==>.,>>==..====>======='==@$$$$+ ", +" +.$$$$$$@$$$#==#==,.+.,++,=.=====>.,......=>==#$$$$+ ", +" +.$$$$$$@$$$@@@@===$-@=...=#=#,,#,#===.=...,..#@$$$+ ", +" +.$$$$$$@$$$@@=@=@=$======#.==#>#>@=====@===@=$$$$$+ ", +" +.$$$$$$@$$$####=#=#=#====#=#>#,.....,....#####$$$$+ ", +" +.$$$$$$@$$$####.#=#=#==#=###=#=#=#,.......####$$$$+ ", +" +.$$$$$$@$$$@==@===$=@====@===$-==='=$===@==@==$$$$+ ", +" +.$$$$$$@$$$$@=$@@@$@@@@@=$@@@$=@@$==$=-=$==$==@$$$+ ", +" +.$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$@$$@$$$$+ ", +" +.$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" +.$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" ++$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" ++$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" ++$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ", +" )+$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+) ", +" )+$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+) ", +" )+.###############################################.+ ", +" )+++++++++++++++++++++++++++++++++++++++++++++++++ ", +" ", +" ", +" "}; diff --git a/app/bin/bitmaps/xtc16.xbm b/app/bin/bitmaps/xtc16.xbm new file mode 100644 index 0000000..fc885c5 --- /dev/null +++ b/app/bin/bitmaps/xtc16.xbm @@ -0,0 +1,7 @@ +#define xtc16_width 14 +#define xtc16_height 14 +// static unsigned char xtc16_bits[] = { +static char xtc16_bits[] = { + 0xfc, 0x0f, 0x02, 0x10, 0x01, 0x22, 0x01, 0x21, 0xb9, 0x20, 0x45, 0x20, + 0xfd, 0x27, 0x45, 0x20, 0x79, 0x20, 0x81, 0x20, 0x02, 0x11, 0xfc, 0x0f, + 0x00, 0x04, 0xff, 0x03}; diff --git a/app/bin/bitmaps/xtc64.xbm b/app/bin/bitmaps/xtc64.xbm new file mode 100644 index 0000000..99b54fb --- /dev/null +++ b/app/bin/bitmaps/xtc64.xbm @@ -0,0 +1,47 @@ +#define xtc64_width 64 +#define xtc64_height 64 +// static unsigned char xtc64_bits[] = { +static char xtc64_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x01, 0x00, 0xfc, 0xff, 0xff, 0xff, 0x07, 0x80, 0x81, 0x07, 0x00, 0x00, + 0x30, 0x38, 0x18, 0x80, 0x61, 0x38, 0xfc, 0xff, 0x0f, 0x0c, 0x60, 0x80, + 0x11, 0xe0, 0x00, 0x00, 0x00, 0x02, 0x80, 0x80, 0x09, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x80, 0x80, 0x09, 0x00, 0x1c, 0x00, 0x00, 0x01, 0x00, 0x81, + 0x05, 0x00, 0x60, 0x00, 0x80, 0x00, 0x00, 0x81, 0x05, 0x00, 0x80, 0x03, + 0x40, 0x00, 0x00, 0x82, 0x05, 0x00, 0x00, 0x0c, 0x20, 0x00, 0x00, 0x82, + 0x05, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x82, 0x05, 0x00, 0x00, 0xc0, + 0x09, 0x00, 0x00, 0x82, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x82, + 0x05, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x81, 0x05, 0x00, 0x00, 0x00, + 0xc1, 0x00, 0x00, 0x81, 0x05, 0x00, 0x00, 0x00, 0x01, 0x03, 0x80, 0x80, + 0x05, 0x00, 0x00, 0x80, 0x00, 0x1c, 0x80, 0x80, 0x05, 0x00, 0x00, 0x40, + 0x00, 0x60, 0x60, 0x80, 0x05, 0x00, 0x00, 0x20, 0x00, 0x80, 0x1f, 0x80, + 0x05, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, + 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, + 0x05, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x05, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x05, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x80, + 0x05, 0x0c, 0x10, 0x00, 0x00, 0x00, 0xc0, 0x80, 0xc5, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x83, 0x35, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x8e, + 0x0d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x90, 0x0d, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0x05, 0x80, 0x00, 0xf0, 0xff, 0x03, 0x00, 0xc0, 0x05, 0x40, 0x00, 0x30, + 0x00, 0x03, 0x00, 0xc0, 0x05, 0x20, 0x00, 0xd0, 0xff, 0x02, 0x00, 0xc0, + 0x09, 0x10, 0x00, 0x30, 0x00, 0x03, 0x00, 0xc0, 0x09, 0x10, 0x00, 0xf0, + 0xff, 0x03, 0x00, 0xa0, 0x31, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, + 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x81, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x80, + 0x01, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x07, 0x8e, 0xfd, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x01, 0x91, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x80, 0xa8, + 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa4, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xa2, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x01, 0x63, 0xff, 0xc0, 0x01, 0x0f, 0xe0, 0x80, 0x01, 0x66, 0x99, 0x80, + 0x81, 0x09, 0xc0, 0x80, 0x01, 0x36, 0x18, 0x80, 0xc1, 0x00, 0xc0, 0x80, + 0x01, 0x1c, 0x18, 0x9f, 0xdd, 0xe0, 0xf9, 0x80, 0x01, 0x1c, 0x18, 0xa6, + 0xcd, 0x80, 0xcd, 0x80, 0x01, 0x36, 0x18, 0x86, 0xc7, 0xf0, 0xcd, 0x80, + 0x01, 0x33, 0x18, 0x86, 0x8d, 0xb9, 0xcd, 0x80, 0x01, 0x63, 0x3c, 0xcf, + 0x19, 0x6f, 0xbb, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; diff --git a/app/bin/bitmaps/zero.xpm b/app/bin/bitmaps/zero.xpm new file mode 100644 index 0000000..d3466d5 --- /dev/null +++ b/app/bin/bitmaps/zero.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * zero_xpm[] = { +"6 16 2 1", +" c None", +". c #000000000000", +" ", +" .... ", +"......", +".. ..", +".. ..", +".. ..", +".. ..", +".. ..", +".. ..", +".. ..", +".. ..", +".. ..", +".. ..", +"......", +" .... ", +" "}; diff --git a/app/bin/bitmaps/zoom.xpm b/app/bin/bitmaps/zoom.xpm new file mode 100644 index 0000000..6b845a5 --- /dev/null +++ b/app/bin/bitmaps/zoom.xpm @@ -0,0 +1,24 @@ +/* XPM */ +static char * zoom_xpm[] = { +"16 16 5 1", +" c None", +"- c #FFFFFFFFFFFF", +". c #000000000000", +"X c #0000FFFFFFFF", +"o c #FFFF7DF70000", +" ..... ", +" ..-XXXX.. ", +" .----XXX. ", +".--.---.--. ", +".-...-.---. ", +".--.-.----. ", +".---.-...-. ", +".XX.------. ", +" .XXX----. ", +" ..XXX--... ", +" ..... .o. ", +" .o. ", +" .o. ", +" .o. ", +" .. ", +" "}; diff --git a/app/bin/bitmaps/zoomin.xpm b/app/bin/bitmaps/zoomin.xpm new file mode 100644 index 0000000..4b6f8d5 --- /dev/null +++ b/app/bin/bitmaps/zoomin.xpm @@ -0,0 +1,24 @@ +/* XPM */ +static char * zoomin_xpm[] = { +"16 16 5 1", +" c None", +"- c #FFFFFFFFFFFF", +". c #000000000000", +"X c #0000FFFFFFFF", +"o c #FFFF7DF70000", +" ..... ", +" ..-XXXX.. ", +" .----XXX. ", +".-.--.--.-. ", +"........... . ", +".-.--.--.-. ....", +"........... . ", +".X.--.--.-. ", +" .XXX----. ", +" ..XXX--.o. ", +" ..... .o. ", +" .o. ", +" .o. ", +" .o. ", +" .. ", +" "}; diff --git a/app/bin/bitmaps/zoomout.xpm b/app/bin/bitmaps/zoomout.xpm new file mode 100644 index 0000000..8d8d442 --- /dev/null +++ b/app/bin/bitmaps/zoomout.xpm @@ -0,0 +1,24 @@ +/* XPM */ +static char * zoomout_xpm[] = { +"16 16 5 1", +" c None", +"- c #FFFFFFFFFFFF", +". c #000000000000", +"X c #0000FFFFFFFF", +"o c #FFFF7DF70000", +" ..... ", +" ..-XXXX.. ", +" .----XXX. ", +".---------. . ", +".-.--.--.-. ....", +"........... . ", +".-.--.--.-. ....", +".XX-------. . ", +" .XXX----. ", +" ..XXX--... ", +" ..... .o. ", +" .o. ", +" .o. ", +" .o. ", +" .. ", +" "}; diff --git a/app/bin/cblock.c b/app/bin/cblock.c new file mode 100644 index 0000000..06fd75a --- /dev/null +++ b/app/bin/cblock.c @@ -0,0 +1,658 @@ +/* + * ------------------------------------------------------------------ + * cblock.c - Implement blocks: a group of trackwork with a single occ. detector + * Created by Robert Heller on Thu Mar 12 09:43:02 2009 + * ------------------------------------------------------------------ + * Modification History: $Log: not supported by cvs2svn $ + * Modification History: Revision 1.4 2009/09/16 18:32:24 m_fischer + * Modification History: Remove unused locals + * Modification History: + * Modification History: Revision 1.3 2009/09/05 16:40:53 m_fischer + * Modification History: Make layout control commands a build-time choice + * Modification History: + * Modification History: Revision 1.2 2009/07/08 19:13:58 m_fischer + * Modification History: Make compile under MSVC + * Modification History: + * Modification History: Revision 1.1 2009/07/08 18:40:27 m_fischer + * Modification History: Add switchmotor and block for layout control + * Modification History: + * Modification History: Revision 1.1 2002/07/28 14:03:50 heller + * Modification History: Add it copyright notice headers + * Modification History: + * ------------------------------------------------------------------ + * Contents: + * ------------------------------------------------------------------ + * + * Generic Project + * Copyright (C) 2005 Robert Heller D/B/A Deepwoods Software + * 51 Locke Hill Road + * Wendell, MA 01379-9728 + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * T_BLOCK + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cblock.c,v 1.5 2009-11-23 19:46:16 rheller Exp $ + */ + +#include <ctype.h> +#include "track.h" +#include "compound.h" +#include "i18n.h" + +EXPORT TRKTYP_T T_BLOCK = -1; + +#define BLOCKCMD + +static int log_block = 0; + +#ifdef BLOCKCMD + +static void NoDrawLine(drawCmd_p d, coOrd p0, coOrd p1, wDrawWidth width, + wDrawColor color ) {} +static void NoDrawArc(drawCmd_p d, coOrd p, DIST_T r, ANGLE_T angle0, + ANGLE_T angle1, BOOL_T drawCenter, wDrawWidth width, + wDrawColor color ) {} +static void NoDrawString( drawCmd_p d, coOrd p, ANGLE_T a, char * s, + wFont_p fp, FONTSIZE_T fontSize, wDrawColor color ) {} +static void NoDrawBitMap( drawCmd_p d, coOrd p, wDrawBitMap_p bm, + wDrawColor color) {} +static void NoDrawFillPoly( drawCmd_p d, int cnt, coOrd * pts, + wDrawColor color ) {} +static void NoDrawFillCircle( drawCmd_p d, coOrd p, DIST_T r, + wDrawColor color ) {} + +static drawFuncs_t noDrawFuncs = { + 0, + NoDrawLine, + NoDrawArc, + NoDrawString, + NoDrawBitMap, + NoDrawFillPoly, + NoDrawFillCircle }; + +static drawCmd_t blockD = { + NULL, + &noDrawFuncs, + 0, + 1.0, + 0.0, + {0.0,0.0}, {0.0,0.0}, + Pix2CoOrd, CoOrd2Pix }; + +static char blockName[STR_SHORT_SIZE]; +static char blockScript[STR_LONG_SIZE]; +static long blockElementCount; + +static paramData_t blockPLs[] = { +/*0*/ { PD_STRING, blockName, "name", PDO_NOPREF, (void*)200, N_("Name") }, +/*1*/ { PD_STRING, blockScript, "script", PDO_NOPREF, (void*)350, N_("Script") } +}; +static paramGroup_t blockPG = { "block", 0, blockPLs, sizeof blockPLs/sizeof blockPLs[0] }; +static dynArr_t blockTrk_da; +#define blockTrk(N) DYNARR_N( track_p , blockTrk_da, N ) +static wWin_p blockW; +#endif + + +typedef struct blockData_t { + char * name; + char * script; + wIndex_t numTracks; + track_p trackList; +} blockData_t, *blockData_p; + +static blockData_p GetblockData ( track_p trk ) +{ + return (blockData_p) GetTrkExtraData(trk); +} + +static void DrawBlock (track_p t, drawCmd_p d, wDrawColor color ) +{ +} + +static struct { + char name[STR_SHORT_SIZE]; + char script[STR_LONG_SIZE]; + FLOAT_T length; + coOrd endPt[2]; +} blockData; + +typedef enum { NM, SC, LN, E0, E1 } blockDesc_e; +static descData_t blockDesc[] = { +/*NM*/ { DESC_STRING, N_("Name"), &blockData.name }, +/*SC*/ { DESC_STRING, N_("Script"), &blockData.script }, +/*LN*/ { DESC_DIM, N_("Length"), &blockData.length }, +/*E0*/ { DESC_POS, N_("End Pt 1: X"), &blockData.endPt[0] }, +/*E1*/ { DESC_POS, N_("End Pt 2: X"), &blockData.endPt[1] }, + { DESC_NULL } }; + +static void UpdateBlock (track_p trk, int inx, descData_p descUpd, BOOL_T needUndoStart ) +{ + blockData_p xx = GetblockData(trk); + const char * thename, *thescript; + char *newName, *newScript; + BOOL_T changed, nChanged, sChanged; + + LOG( log_block, 1, ("*** UpdateBlock(): needUndoStart = %d\n",needUndoStart)) + if ( inx == -1 ) { + nChanged = sChanged = changed = FALSE; + thename = wStringGetValue( (wString_p)blockDesc[NM].control0 ); + if ( strcmp( thename, xx->name ) != 0 ) { + nChanged = changed = TRUE; + newName = MyStrdup(thename); + } + thescript = wStringGetValue( (wString_p)blockDesc[SC].control0 ); + if ( strcmp( thescript, xx->script ) != 0 ) { + sChanged = changed = TRUE; + newScript = MyStrdup(thescript); + } + if ( ! changed ) return; + if ( needUndoStart ) + UndoStart( _("Change Block"), "Change Block" ); + UndoModify( trk ); + if (nChanged) { + MyFree(xx->name); + xx->name = newName; + } + if (sChanged) { + MyFree(xx->script); + xx->script = newScript; + } + return; + } +} + +static DIST_T DistanceBlock (track_p t, coOrd * p ) +{ + blockData_p xx = GetblockData(t); + DIST_T closest, current; + int iTrk = 1; + + closest = GetTrkDistance ((&(xx->trackList))[0], *p); + for (; iTrk < xx->numTracks; iTrk++) { + current = GetTrkDistance ((&(xx->trackList))[iTrk], *p); + if (current < closest) closest = current; + } + return closest; +} + +static void DescribeBlock (track_p trk, char * str, CSIZE_T len ) +{ + blockData_p xx = GetblockData(trk); + wIndex_t tcount = 0; + track_p lastTrk = NULL; + long listLabelsOption = listLabels; + + LOG( log_block, 1, ("*** DescribeBlock(): trk is T%d\n",GetTrkIndex(trk))) + FormatCompoundTitle( listLabelsOption, xx->name ); + if (message[0] == '\0') + FormatCompoundTitle( listLabelsOption|LABEL_DESCR, xx->name ); + strcpy( str, _(GetTrkTypeName( trk )) ); + str++; + while (*str) { + *str = tolower(*str); + str++; + } + sprintf( str, _("(%d): Layer=%d %s"), + GetTrkIndex(trk), GetTrkLayer(trk)+1, message ); + strncpy(blockData.name,xx->name,STR_SHORT_SIZE-1); + blockData.name[STR_SHORT_SIZE-1] = '\0'; + strncpy(blockData.script,xx->script,STR_LONG_SIZE-1); + blockData.script[STR_LONG_SIZE-1] = '\0'; + blockData.length = 0; + if (xx->numTracks > 0) { + blockData.endPt[0] = GetTrkEndPos((&(xx->trackList))[0],0); + } + for (tcount = 0; tcount < xx->numTracks; tcount++) { + blockData.length += GetTrkLength((&(xx->trackList))[tcount],0,1); + lastTrk = (&(xx->trackList))[tcount]; + } + if (lastTrk != NULL) blockData.endPt[1] = GetTrkEndPos(lastTrk,1); + blockDesc[E0].mode = + blockDesc[E1].mode = + blockDesc[LN].mode = DESC_RO; + blockDesc[NM].mode = + blockDesc[SC].mode = DESC_NOREDRAW; + DoDescribe(_("Block"), trk, blockDesc, UpdateBlock ); + +} + +static blockDebug (track_p trk) +{ + wIndex_t iTrack; + blockData_p xx = GetblockData(trk); + LOG( log_block, 1, ("*** blockDebug(): trk = %08x\n",trk)) + LOG( log_block, 1, ("*** blockDebug(): Index = %d\n",GetTrkIndex(trk))) + LOG( log_block, 1, ("*** blockDebug(): name = \"%s\"\n",xx->name)) + LOG( log_block, 1, ("*** blockDebug(): script = \"%s\"\n",xx->script)) + LOG( log_block, 1, ("*** blockDebug(): numTracks = %d\n",xx->numTracks)) + for (iTrack = 0; iTrack < xx->numTracks; iTrack++) { + LOG( log_block, 1, ("*** blockDebug(): trackList[%d] = T%d, ",iTrack,GetTrkIndex((&(xx->trackList))[iTrack]))) + LOG( log_block, 1, ("%s\n",GetTrkTypeName((&(xx->trackList))[iTrack]))) + } + +} + +static BOOL_T blockCheckContigiousPath() +{ + EPINX_T ep, epCnt, epN; + int inx; + track_p trk, trk1; + DIST_T dist; + ANGLE_T angle; + int pathElemStart = 0; + coOrd endPtOrig = zero; + BOOL_T IsConnectedP; + trkEndPt_p endPtP; + DYNARR_RESET( trkEndPt_t, tempEndPts_da ); + + for ( inx=0; inx<blockTrk_da.cnt; inx++ ) { + trk = blockTrk(inx); + epCnt = GetTrkEndPtCnt(trk); + IsConnectedP = FALSE; + for ( ep=0; ep<epCnt; ep++ ) { + trk1 = GetTrkEndTrk(trk,ep); + if ( trk1 == NULL || !GetTrkSelected(trk1) ) { + /* boundary EP */ + for ( epN=0; epN<tempEndPts_da.cnt; epN++ ) { + dist = FindDistance( GetTrkEndPos(trk,ep), tempEndPts(epN).pos ); + angle = NormalizeAngle( GetTrkEndAngle(trk,ep) - tempEndPts(epN).angle + connectAngle/2.0 ); + if ( dist < connectDistance && angle < connectAngle ) + break; + } + if ( epN>=tempEndPts_da.cnt ) { + DYNARR_APPEND( trkEndPt_t, tempEndPts_da, 10 ); + endPtP = &tempEndPts(tempEndPts_da.cnt-1); + memset( endPtP, 0, sizeof *endPtP ); + endPtP->pos = GetTrkEndPos(trk,ep); + endPtP->angle = GetTrkEndAngle(trk,ep); + /*endPtP->track = trk1;*/ + /* These End Points are dummies -- + we don't want DeleteTrack to look at + them. */ + endPtP->track = NULL; + endPtP->index = (trk1?GetEndPtConnectedToMe(trk1,trk):-1); + endPtOrig.x += endPtP->pos.x; + endPtOrig.y += endPtP->pos.y; + } + } else { + IsConnectedP = TRUE; + } + } + if (!IsConnectedP && blockTrk_da.cnt > 1) return FALSE; + } + return TRUE; +} + +static void DeleteBlock ( track_p t ) +{ + blockData_p xx = GetblockData(t); + MyFree(xx->name); xx->name = NULL; + MyFree(xx->script); xx->script = NULL; +} + +static BOOL_T WriteBlock ( track_p t, FILE * f ) +{ + BOOL_T rc = TRUE; + wIndex_t iTrack; + blockData_p xx = GetblockData(t); + + rc &= fprintf(f, "BLOCK %d \"%s\" \"%s\"\n", + GetTrkIndex(t), xx->name, xx->script)>0; + for (iTrack = 0; iTrack < xx->numTracks && rc; iTrack++) { + rc &= fprintf(f, "\tTRK %d\n", + GetTrkIndex((&(xx->trackList))[iTrack]))>0; + } + rc &= fprintf( f, "\tEND\n" )>0; + return rc; +} + +static void ReadBlock ( char * line ) +{ + TRKINX_T trkindex; + wIndex_t index; + track_p trk; + char * cp = NULL; + blockData_p xx; + wIndex_t iTrack; + EPINX_T ep; + trkEndPt_p endPtP; + char *name, *script; + + LOG( log_block, 1, ("*** ReadBlock: line is '%s'\n",line)) + if (!GetArgs(line+6,"dqq",&index,&name,&script)) { + return; + } + DYNARR_RESET( track_p , blockTrk_da ); + while ( (cp = GetNextLine()) != NULL ) { + while (isspace(*cp)) cp++; + if ( strncmp( cp, "END", 3 ) == 0 ) { + break; + } + if ( *cp == '\n' || *cp == '#' ) { + continue; + } + if ( strncmp( cp, "TRK", 3 ) == 0 ) { + if (!GetArgs(cp+4,"d",&trkindex)) return; + trk = FindTrack(trkindex); + DYNARR_APPEND( track_p *, blockTrk_da, 10 ); + blockTrk(blockTrk_da.cnt-1) = trk; + } + } + blockCheckContigiousPath(); + trk = NewTrack(index, T_BLOCK, tempEndPts_da.cnt, sizeof(blockData_t)+(sizeof(track_p)*(blockTrk_da.cnt-1))+1); + for ( ep=0; ep<tempEndPts_da.cnt; ep++) { + endPtP = &tempEndPts(ep); + SetTrkEndPoint( trk, ep, endPtP->pos, endPtP->angle ); + } + xx = GetblockData( trk ); + xx->name = name; + xx->script = script; + xx->numTracks = blockTrk_da.cnt; + for (iTrack = 0; iTrack < blockTrk_da.cnt; iTrack++) { + LOG( log_block, 1, ("*** ReadBlock(): copying track T%d\n",GetTrkIndex(blockTrk(iTrack)))) + (&(xx->trackList))[iTrack] = blockTrk(iTrack); + } + blockDebug(trk); +} + + +static void MoveBlock (track_p trk, coOrd orig ) {} +static void RotateBlock (track_p trk, coOrd orig, ANGLE_T angle ) {} +static void RescaleBlock (track_p trk, FLOAT_T ratio ) {} + +static trackCmd_t blockCmds = { + "BLOCK", + DrawBlock, + DistanceBlock, + DescribeBlock, + DeleteBlock, + WriteBlock, + ReadBlock, + MoveBlock, + RotateBlock, + RescaleBlock, + NULL, /* audit */ + NULL, /* getAngle */ + NULL, /* split */ + NULL, /* traverse */ + NULL, /* enumerate */ + NULL, /* redraw */ + NULL, /* trim */ + NULL, /* merge */ + NULL, /* modify */ + NULL, /* getLength */ + NULL, /* getTrkParams */ + NULL, /* moveEndPt */ + NULL, /* query */ + NULL, /* ungroup */ + NULL, /* flip */ + NULL, /* drawPositionIndicator */ + NULL, /* advancePositionIndicator */ + NULL, /* checkTraverse */ + NULL, /* makeParallel */ + NULL /* drawDesc */ +}; + + + +#ifdef BLOCKCMD +static BOOL_T TrackInBlock (track_p trk, track_p blk) { + wIndex_t iTrack; + blockData_p xx = GetblockData(blk); + for (iTrack = 0; iTrack < xx->numTracks; iTrack++) { + if (trk == (&(xx->trackList))[iTrack]) return TRUE; + } + return FALSE; +} + +static track_p FindBlock (track_p trk) { + track_p a_trk; + for (a_trk = NULL; TrackIterate( &a_trk ) ;) { + if (GetTrkType(a_trk) == T_BLOCK && + TrackInBlock(trk,a_trk)) return a_trk; + } + return NULL; +} + +static void BlockOk ( void * junk ) +{ + blockData_p xx; + track_p trk; + wIndex_t iTrack; + EPINX_T ep; + trkEndPt_p endPtP; + + LOG( log_block, 1, ("*** BlockOk()\n")) + DYNARR_RESET( track_p *, blockTrk_da ); + + ParamUpdate( &blockPG ); + if ( blockName[0]==0 ) { + NoticeMessage( 0, "Block must have a name!", _("Ok")); + return; + } + wDrawDelayUpdate( mainD.d, TRUE ); + /* + * Collect tracks + */ + trk = NULL; + while ( TrackIterate( &trk ) ) { + if ( GetTrkSelected( trk ) ) { + if ( IsTrack(trk) ) { + DYNARR_APPEND( track_p *, blockTrk_da, 10 ); + LOG( log_block, 1, ("*** BlockOk(): adding track T%d\n",GetTrkIndex(trk))) + blockTrk(blockTrk_da.cnt-1) = trk; + } + } + } + if ( blockTrk_da.cnt>0 ) { + if ( blockTrk_da.cnt > 128 ) { + NoticeMessage( MSG_TOOMANYSEGSINGROUP, _("Ok"), NULL ); + wDrawDelayUpdate( mainD.d, FALSE ); + wHide( blockW ); + return; + } + /* Need to check that all block elements are connected to each + other... */ + if (!blockCheckContigiousPath()) { + NoticeMessage( _("Block is discontigious!"), _("Ok"), NULL ); + wDrawDelayUpdate( mainD.d, FALSE ); + wHide( blockW ); + return; + } + UndoStart( _("Create Block"), "Create Block" ); + /* Create a block object */ + LOG( log_block, 1, ("*** BlockOk(): %d tracks in block\n",blockTrk_da.cnt)) + trk = NewTrack(0, T_BLOCK, tempEndPts_da.cnt, sizeof(blockData_t)+(sizeof(track_p)*(blockTrk_da.cnt-1))+1); + for ( ep=0; ep<tempEndPts_da.cnt; ep++) { + endPtP = &tempEndPts(ep); + SetTrkEndPoint( trk, ep, endPtP->pos, endPtP->angle ); + } + xx = GetblockData( trk ); + xx->name = MyStrdup(blockName); + xx->script = MyStrdup(blockScript); + xx->numTracks = blockTrk_da.cnt; + for (iTrack = 0; iTrack < blockTrk_da.cnt; iTrack++) { + LOG( log_block, 1, ("*** BlockOk(): copying track T%d\n",GetTrkIndex(blockTrk(iTrack)))) + (&(xx->trackList))[iTrack] = blockTrk(iTrack); + } + blockDebug(trk); + UndoEnd(); + } + wHide( blockW ); + +} + +static void NewBlockDialog() +{ + track_p trk = NULL; + + LOG( log_block, 1, ("*** NewBlockDialog()\n")) + blockElementCount = 0; + + while ( TrackIterate( &trk ) ) { + if ( GetTrkSelected( trk ) ) { + if ( !IsTrack( trk ) ) { + ErrorMessage( _("Non track object skipped!") ); + continue; + } + if ( FindBlock( trk ) != NULL ) { + ErrorMessage( _("Selected Track is already in a block, skipped!") ); + continue; + } + blockElementCount++; + } + } + + if (blockElementCount == 0) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return; + } + if ( log_block < 0 ) log_block = LogFindIndex( "block" ); + if ( !blockW ) { + ParamRegister( &blockPG ); + blockW = ParamCreateDialog (&blockPG, MakeWindowTitle(_("Create Block")), _("Ok"), BlockOk, wHide, TRUE, NULL, F_BLOCK, NULL ); + blockD.dpi = mainD.dpi; + } + ParamLoadControls( &blockPG ); + wShow( blockW ); +} + +static STATUS_T CmdBlockCreate( wAction_t action, coOrd pos ) +{ + LOG( log_block, 1, ("*** CmdBlockAction(%08x,{%f,%f})\n",action,pos.x,pos.y)) + switch (action & 0xFF) { + case C_START: + fprintf(stderr,"*** CmdBlockCreate(): C_START\n"); + NewBlockDialog(); + return C_TERMINATE; + default: + return C_CONTINUE; + } +} + +extern BOOL_T inDescribeCmd; + +static STATUS_T CmdBlockEdit( wAction_t action, coOrd pos ) +{ + track_p trk,btrk; + char msg[STR_SIZE]; + + switch (action) { + case C_START: + InfoMessage( _("Select a track") ); + inDescribeCmd = TRUE; + return C_CONTINUE; + case C_DOWN: + if ((trk = OnTrack(&pos, TRUE, TRUE )) == NULL) { + return C_CONTINUE; + } + btrk = FindBlock( trk ); + if ( !btrk ) { + ErrorMessage( _("Not a block!") ); + return C_CONTINUE; + } + DescribeTrack (btrk, msg, sizeof msg ); + InfoMessage( msg ); + return C_CONTINUE; + case C_REDRAW: + return C_CONTINUE; + case C_CANCEL: + inDescribeCmd = FALSE; + return C_TERMINATE; + default: + return C_CONTINUE; + } +} + +static STATUS_T CmdBlockDelete( wAction_t action, coOrd pos ) +{ + track_p trk,btrk; + blockData_p xx; + + switch (action) { + case C_START: + InfoMessage( _("Select a track") ); + return C_CONTINUE; + case C_DOWN: + if ((trk = OnTrack(&pos, TRUE, TRUE )) == NULL) { + return C_CONTINUE; + } + btrk = FindBlock( trk ); + if ( !btrk ) { + ErrorMessage( _("Not a block!") ); + return C_CONTINUE; + } + /* Confirm Delete Block */ + xx = GetblockData(btrk); + if ( NoticeMessage( _("Really delete block %s?"), _("Yes"), _("No"), xx->name) ) { + UndoStart( _("Delete Block"), "delete" ); + DeleteTrack (btrk, FALSE); + UndoEnd(); + return C_TERMINATE; + } + return C_CONTINUE; + case C_REDRAW: + return C_CONTINUE; + case C_CANCEL: + return C_TERMINATE; + default: + return C_CONTINUE; + } +} + + + +#define BLOCK_CREATE 0 +#define BLOCK_EDIT 1 +#define BLOCK_DELETE 2 + +static STATUS_T CmdBlock (wAction_t action, coOrd pos ) +{ + fprintf(stderr,"*** CmdBlock(%08x,{%f,%f})\n",action,pos.x,pos.y); + + switch ((long)commandContext) { + case BLOCK_CREATE: return CmdBlockCreate(action,pos); + case BLOCK_EDIT: return CmdBlockEdit(action,pos); + case BLOCK_DELETE: return CmdBlockDelete(action,pos); + default: return C_TERMINATE; + } +} + +#include "bitmaps/blocknew.xpm" +#include "bitmaps/blockedit.xpm" +#include "bitmaps/blockdel.xpm" + +EXPORT void InitCmdBlock( wMenu_p menu ) +{ + blockName[0] = '\0'; + blockScript[0] = '\0'; + ButtonGroupBegin( _("Block"), "cmdBlockSetCmd", _("Blocks") ); + AddMenuButton( menu, CmdBlock, "cmdBlockCreate", _("Create Block"), wIconCreatePixMap(blocknew_xpm), LEVEL0_50, IC_CANCEL|IC_POPUP, ACCL_BLOCK1, (void*)BLOCK_CREATE ); + AddMenuButton( menu, CmdBlock, "cmdBlockEdit", _("Edit Block"), wIconCreatePixMap(blockedit_xpm), LEVEL0_50, IC_CANCEL|IC_POPUP, ACCL_BLOCK2, (void*)BLOCK_EDIT ); + AddMenuButton( menu, CmdBlock, "cmdBlockDelete", _("Delete Block"), wIconCreatePixMap(blockdel_xpm), LEVEL0_50, IC_CANCEL|IC_POPUP, ACCL_BLOCK3, (void*)BLOCK_DELETE ); + ButtonGroupEnd(); + ParamRegister( &blockPG ); +} +#endif + + +EXPORT void InitTrkBlock( void ) +{ + T_BLOCK = InitObject ( &blockCmds ); + log_block = LogFindIndex ( "block" ); +} + + diff --git a/app/bin/ccurve.c b/app/bin/ccurve.c new file mode 100644 index 0000000..b284669 --- /dev/null +++ b/app/bin/ccurve.c @@ -0,0 +1,735 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/ccurve.c,v 1.4 2008-03-06 19:35:04 m_fischer Exp $ + * + * CURVE + * + */ + +/* 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 "track.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "cjoin.h" +#include "i18n.h" + + +/* + * STATE INFO + */ + +static struct { + STATE_T state; + coOrd pos0; + coOrd pos1; + curveData_t curveData; + } Da; + +static long curveMode; + + +static void DrawArrowHeads( + trkSeg_p sp, + coOrd pos, + ANGLE_T angle, + BOOL_T bidirectional, + wDrawColor color ) +{ + coOrd p0, p1; + DIST_T d, w; + int inx; + d = mainD.scale*0.25; + w = mainD.scale/mainD.dpi*2; + for ( inx=0; inx<5; inx++ ) { + sp[inx].type = SEG_STRLIN; + sp[inx].width = w; + sp[inx].color = color; + } + Translate( &p0, pos, angle, d ); + Translate( &p1, pos, angle+180, bidirectional?d:(d/2.0) ); + sp[0].u.l.pos[0] = p0; + sp[0].u.l.pos[1] = p1; + sp[1].u.l.pos[0] = p0; + Translate( &sp[1].u.l.pos[1], p0, angle+135, d/2.0 ); + sp[2].u.l.pos[0] = p0; + Translate( &sp[2].u.l.pos[1], p0, angle-135, d/2.0 ); + if (bidirectional) { + sp[3].u.l.pos[0] = p1; + Translate( &sp[3].u.l.pos[1], p1, angle-45, d/2.0 ); + sp[4].u.l.pos[0] = p1; + Translate( &sp[4].u.l.pos[1], p1, angle+45, d/2.0 ); + } +} + + + + +EXPORT STATUS_T CreateCurve( + wAction_t action, + coOrd pos, + BOOL_T track, + wDrawColor color, + DIST_T width, + long mode, + curveMessageProc message ) +{ + DIST_T d; + ANGLE_T a; + static coOrd pos0; + int inx; + + switch ( action ) { + case C_START: + DYNARR_SET( trkSeg_t, tempSegs_da, 8 ); + switch ( curveMode ) { + case crvCmdFromEP1: + InfoMessage( _("Drag from End-Point in direction of curve") ); + break; + case crvCmdFromTangent: + InfoMessage( _("Drag from End-Point to Center") ); + break; + case crvCmdFromCenter: + InfoMessage( _("Drag from Center to End-Point") ); + break; + case crvCmdFromChord: + InfoMessage( _("Drag to other end of chord") ); + break; + } + return C_CONTINUE; + case C_DOWN: + for ( inx=0; inx<8; inx++ ) { + tempSegs(inx).color = wDrawColorBlack; + tempSegs(inx).width = 0; + } + tempSegs_da.cnt = 0; + SnapPos( &pos ); + pos0 = pos; + switch (mode) { + case crvCmdFromEP1: + tempSegs(0).type = (track?SEG_STRTRK:SEG_STRLIN); + tempSegs(0).color = color; + tempSegs(0).width = width; + message( _("Drag to set angle") ); + break; + case crvCmdFromTangent: + case crvCmdFromCenter: + tempSegs(0).type = SEG_STRLIN; + tempSegs(1).type = SEG_CRVLIN; + tempSegs(1).u.c.radius = mainD.scale*0.05; + tempSegs(1).u.c.a0 = 0; + tempSegs(1).u.c.a1 = 360; + tempSegs(2).type = SEG_STRLIN; + message( mode==crvCmdFromTangent?_("Drag from End-Point to Center"):_("Drag from Center to End-Point") ); + break; + case crvCmdFromChord: + tempSegs(0).type = (track?SEG_STRTRK:SEG_STRLIN); + tempSegs(0).color = color; + tempSegs(0).width = width; + message( _("Drag to other end of chord") ); + break; + } + tempSegs(0).u.l.pos[0] = pos; + return C_CONTINUE; + + case C_MOVE: + tempSegs(0).u.l.pos[1] = pos; + d = FindDistance( pos0, pos ); + a = FindAngle( pos0, pos ); + switch ( mode ) { + case crvCmdFromEP1: + message( _("Angle=%0.3f"), PutAngle(a) ); + tempSegs_da.cnt = 1; + break; + case crvCmdFromTangent: + message( _("Radius=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) ); + tempSegs(1).u.c.center = pos; + DrawArrowHeads( &tempSegs(2), pos0, FindAngle(pos0,pos)+90, TRUE, wDrawColorBlack ); + tempSegs_da.cnt = 7; + break; + case crvCmdFromCenter: + message( _("Radius=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) ); + tempSegs(1).u.c.center = pos0; + DrawArrowHeads( &tempSegs(2), pos, FindAngle(pos,pos0)+90, TRUE, wDrawColorBlack ); + tempSegs_da.cnt = 7; + break; + case crvCmdFromChord: + message( _("Length=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) ); + if ( d > mainD.scale*0.25 ) { + pos.x = (pos.x+pos0.x)/2.0; + pos.y = (pos.y+pos0.y)/2.0; + DrawArrowHeads( &tempSegs(1), pos, FindAngle(pos,pos0)+90, TRUE, wDrawColorBlack ); + tempSegs_da.cnt = 6; + } else { + tempSegs_da.cnt = 1; + } + break; + } + return C_CONTINUE; + + case C_UP: + switch (mode) { + case crvCmdFromEP1: + DrawArrowHeads( &tempSegs(1), pos, FindAngle(pos,pos0)+90, TRUE, drawColorRed ); + tempSegs_da.cnt = 6; + break; + case crvCmdFromChord: + tempSegs(1).color = drawColorRed; + case crvCmdFromTangent: + case crvCmdFromCenter: + tempSegs(2).color = drawColorRed; + tempSegs(3).color = drawColorRed; + tempSegs(4).color = drawColorRed; + tempSegs(5).color = drawColorRed; + tempSegs(6).color = drawColorRed; + break; + } + message( _("Drag on Red arrows to adjust curve") ); + return C_CONTINUE; + + default: + return C_CONTINUE; + + } +} + + +static STATUS_T CmdCurve( wAction_t action, coOrd pos ) +{ + track_p t; + DIST_T d; + static int segCnt; + STATUS_T rc = C_CONTINUE; + + switch (action) { + + case C_START: + curveMode = (long)commandContext; + Da.state = -1; + tempSegs_da.cnt = 0; + return CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); + + case C_TEXT: + if ( Da.state == 0 ) + return CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); + else + return C_CONTINUE; + + case C_DOWN: + if ( Da.state == -1 ) { + SnapPos( &pos ); + Da.pos0 = pos; + Da.state = 0; + return CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); + } else { + tempSegs_da.cnt = segCnt; + return C_CONTINUE; + } + + case C_MOVE: + mainD.funcs->options = wDrawOptTemp; + DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + if ( Da.state == 0 ) { + SnapPos( &pos ); + Da.pos1 = pos; + rc = CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); + } else { + SnapPos( &pos ); + PlotCurve( curveMode, Da.pos0, Da.pos1, pos, &Da.curveData, TRUE ); + if (Da.curveData.type == curveTypeStraight) { + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).u.l.pos[0] = Da.pos0; + tempSegs(0).u.l.pos[1] = Da.curveData.pos1; + tempSegs_da.cnt = 1; + InfoMessage( _("Straight Track: Length=%s Angle=%0.3f"), + FormatDistance(FindDistance( Da.pos0, Da.curveData.pos1 )), + PutAngle(FindAngle( Da.pos0, Da.curveData.pos1 )) ); + } else if (Da.curveData.type == curveTypeNone) { + tempSegs_da.cnt = 0; + InfoMessage( _("Back") ); + } else if (Da.curveData.type == curveTypeCurve) { + tempSegs(0).type = SEG_CRVTRK; + tempSegs(0).u.c.center = Da.curveData.curvePos; + tempSegs(0).u.c.radius = Da.curveData.curveRadius; + tempSegs(0).u.c.a0 = Da.curveData.a0; + tempSegs(0).u.c.a1 = Da.curveData.a1; + tempSegs_da.cnt = 1; + d = D2R(Da.curveData.a1); + if (d < 0.0) + d = 2*M_PI+d; + if ( d*Da.curveData.curveRadius > mapD.size.x+mapD.size.y ) { + ErrorMessage( MSG_CURVE_TOO_LARGE ); + tempSegs_da.cnt = 0; + Da.curveData.type = curveTypeNone; + mainD.funcs->options = 0; + return C_CONTINUE; + } + InfoMessage( _("Curved Track: Radius=%s Angle=%0.3f Length=%s"), + FormatDistance(Da.curveData.curveRadius), Da.curveData.a1, + FormatDistance(Da.curveData.curveRadius*d) ); + } + } + DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + mainD.funcs->options = 0; + return rc; + + + case C_UP: + mainD.funcs->options = wDrawOptTemp; + DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + if (Da.state == 0) { + SnapPos( &pos ); + Da.pos1 = pos; + Da.state = 1; + CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); + DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + mainD.funcs->options = 0; + segCnt = tempSegs_da.cnt; + InfoMessage( _("Drag on Red arrows to adjust curve") ); + return C_CONTINUE; + } else { + mainD.funcs->options = 0; + tempSegs_da.cnt = 0; + Da.state = -1; + if (Da.curveData.type == curveTypeStraight) { + if ((d=FindDistance( Da.pos0, Da.curveData.pos1 )) <= minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, "Curved ", PutDim(fabs(minLength-d)) ); + return C_TERMINATE; + } + UndoStart( _("Create Straight Track"), "newCurve - straight" ); + t = NewStraightTrack( Da.pos0, Da.curveData.pos1 ); + UndoEnd(); + } else if (Da.curveData.type == curveTypeCurve) { + if ((d= Da.curveData.curveRadius * Da.curveData.a1 *2.0*M_PI/360.0) <= minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, "Curved ", PutDim(fabs(minLength-d)) ); + return C_TERMINATE; + } + UndoStart( _("Create Curved Track"), "newCurve - curve" ); + t = NewCurvedTrack( Da.curveData.curvePos, Da.curveData.curveRadius, + Da.curveData.a0, Da.curveData.a1, 0 ); + UndoEnd(); + } else { + return C_ERROR; + } + DrawNewTrack( t ); + return C_TERMINATE; + } + + case C_REDRAW: + if ( Da.state >= 0 ) { + mainD.funcs->options = wDrawOptTemp; + DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + mainD.funcs->options = 0; + } + return C_CONTINUE; + + case C_CANCEL: + if (Da.state == 1) { + mainD.funcs->options = wDrawOptTemp; + DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + mainD.funcs->options = 0; + tempSegs_da.cnt = 0; + } + Da.state = -1; + return C_CONTINUE; + + } + + return C_CONTINUE; + +} + + + +static DIST_T circleRadius = 18.0; +static long helixTurns = 5; +static ANGLE_T helixAngSep = 0.0; +static DIST_T helixElev = 0.0; +static DIST_T helixRadius = 18.0; +static DIST_T helixGrade = 0.0; +static DIST_T helixVertSep = 0.0; +static DIST_T origVertSep = 0.0; +static wWin_p helixW; +#define H_ELEV (0) +#define H_RADIUS (1) +#define H_TURNS (2) +#define H_ANGSEP (3) +#define H_GRADE (4) +#define H_VERTSEP (5) +static int h_orders[7]; +static int h_clock; + +EXPORT long circleMode; + +static void ComputeHelix( paramGroup_p, int, void * ); + +static paramFloatRange_t r0_360 = { 0, 360 }; +static paramFloatRange_t r0_1000000 = { 0, 1000000 }; +static paramIntegerRange_t i1_1000000 = { 1, 1000000 }; +static paramFloatRange_t r1_1000000 = { 1, 1000000 }; +static paramFloatRange_t r0_100= { 0, 100 }; + +static paramData_t helixPLs[] = { + { PD_FLOAT, &helixElev, "elev", PDO_DIM, &r0_1000000, N_("Elevation Difference") }, + { PD_FLOAT, &helixRadius, "radius", PDO_DIM, &r1_1000000, N_("Radius") }, + { PD_LONG, &helixTurns, "turns", 0, &i1_1000000, N_("Turns") }, + { PD_FLOAT, &helixAngSep, "angSep", 0, &r0_360, N_("Angular Separation") }, + { PD_FLOAT, &helixGrade, "grade", 0, &r0_100, N_("Grade") }, + { PD_FLOAT, &helixVertSep, "vertSep", PDO_DIM, &r0_1000000, N_("Vertical Separation") }, +#define I_HELIXMSG (6) + { PD_MESSAGE, N_("Total Length"), NULL, PDO_DLGRESETMARGIN, (void*)200 } }; +static paramGroup_t helixPG = { "helix", PGO_PREFMISCGROUP, helixPLs, sizeof helixPLs/sizeof helixPLs[0] }; + +static paramData_t circleRadiusPLs[] = { + { PD_FLOAT, &circleRadius, "radius", PDO_DIM, &r1_1000000 } }; +static paramGroup_t circleRadiusPG = { "circle", 0, circleRadiusPLs, sizeof circleRadiusPLs/sizeof circleRadiusPLs[0] }; + + +static void ComputeHelix( + paramGroup_p pg, + int h_inx, + void * data ) +{ + DIST_T totTurns; + DIST_T length; + long updates = 0; + if ( h_inx < 0 || h_inx >= sizeof h_orders/sizeof h_orders[0] ) + return; + ParamLoadData( &helixPG ); + totTurns = helixTurns + helixAngSep/360.0; + length = totTurns * helixRadius * (2 * M_PI); + h_orders[h_inx] = ++h_clock; + switch ( h_inx ) { + case H_ELEV: + if (h_orders[H_TURNS]<h_orders[H_VERTSEP] && + origVertSep > 0.0) { + helixTurns = (int)floor(helixElev/origVertSep - helixAngSep/360.0); + totTurns = helixTurns + helixAngSep/360.0; + updates |= (1<<H_TURNS); + } + if (totTurns > 0) { + helixVertSep = helixElev/totTurns; + updates |= (1<<H_VERTSEP); + } + break; + case H_TURNS: + case H_ANGSEP: + helixVertSep = helixElev/totTurns; + updates |= (1<<H_VERTSEP); + break; + case H_VERTSEP: + if (helixVertSep > 0.0) { + origVertSep = helixVertSep; + helixTurns = (int)floor(helixElev/origVertSep - helixAngSep/360.0); + updates |= (1<<H_TURNS); + totTurns = helixTurns + helixAngSep/360.0; + if (totTurns > 0) { + helixVertSep = helixElev/totTurns; + updates |= (1<<H_VERTSEP); + } + } + break; + case H_GRADE: + case H_RADIUS: + break; + } + if ( totTurns > 0.0 ) { + if ( h_orders[H_RADIUS]>=h_orders[H_GRADE] || + (helixGrade==0.0 && totTurns>0 && helixRadius>0) ) { + if ( helixRadius > 0.0 ) { + helixGrade = helixElev/(totTurns*helixRadius*(2*M_PI))*100.0; + updates |= (1<<H_GRADE); + } + } else { + if( helixGrade > 0.0 ) { + helixRadius = helixElev/(totTurns*(helixGrade/100.0)*2.0*M_PI); + updates |= (1<<H_RADIUS); + } + } + } + length = totTurns * helixRadius * (2 * M_PI); + for ( h_inx=0; updates; h_inx++,updates>>=1 ) { + if ( (updates&1) ) + ParamLoadControl( &helixPG, h_inx ); + } + if (length > 0.0) + sprintf( message, _("Total Length %s"), FormatDistance(length) ); + else + strcpy( message, " " ); + ParamLoadMessage( &helixPG, I_HELIXMSG, message ); +} + + +static void HelixCancel( wWin_p win ) +{ + wHide( helixW ); + Reset(); +} + + +static void ChangeHelixW( long changes ) +{ + if ( (changes & CHANGE_UNITS) && + helixW != NULL && + wWinIsVisible(helixW) ) { + ParamLoadControls( &helixPG ); + ComputeHelix( NULL, 6, NULL ); + } +} + + + + +static STATUS_T CmdCircleCommon( wAction_t action, coOrd pos, BOOL_T helix ) +{ + track_p t; + static coOrd pos0; + wControl_p controls[2]; + char * labels[1]; + + switch (action) { + + case C_START: + if (helix) { + if (helixW == NULL) + helixW = ParamCreateDialog( &helixPG, MakeWindowTitle(_("Helix")), NULL, NULL, HelixCancel, TRUE, NULL, 0, ComputeHelix ); + ParamLoadControls( &helixPG ); + ParamGroupRecord( &helixPG ); + ComputeHelix( NULL, 6, NULL ); + wShow( helixW ); + memset( h_orders, 0, sizeof h_orders ); + h_clock = 0; + } else { + ParamLoadControls( &circleRadiusPG ); + ParamGroupRecord( &circleRadiusPG ); + switch ( circleMode ) { + case circleCmdFixedRadius: + controls[0] = circleRadiusPLs[0].control; + controls[1] = NULL; + labels[0] = N_("Circle Radius"); + InfoSubstituteControls( controls, labels ); + break; + case circleCmdFromTangent: + InfoSubstituteControls( NULL, NULL ); + InfoMessage( _("Click on Circle Edge") ); + break; + case circleCmdFromCenter: + InfoSubstituteControls( NULL, NULL ); + InfoMessage( _("Click on Circle Center") ); + break; + } + } + tempSegs_da.cnt = 0; + return C_CONTINUE; + + case C_DOWN: + DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); + tempSegs_da.cnt = 0; + if (helix) { + if (helixRadius <= 0.0) { + ErrorMessage( MSG_RADIUS_GTR_0 ); + return C_ERROR; + } + if (helixTurns <= 0) { + ErrorMessage( MSG_HELIX_TURNS_GTR_0 ); + return C_ERROR; + } + ParamLoadData( &helixPG ); + } else { + ParamLoadData( &circleRadiusPG ); + switch( circleMode ) { + case circleCmdFixedRadius: + if (circleRadius <= 0.0) { + ErrorMessage( MSG_RADIUS_GTR_0 ); + return C_ERROR; + } + break; + case circleCmdFromTangent: + InfoSubstituteControls( NULL, NULL ); + InfoMessage( _("Drag to Center") ); + break; + case circleCmdFromCenter: + InfoSubstituteControls( NULL, NULL ); + InfoMessage( _("Drag to Edge") ); + break; + } + } + SnapPos( &pos ); + tempSegs(0).u.c.center = pos0 = pos; + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).width = 0; + return C_CONTINUE; + + case C_MOVE: + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + SnapPos( &pos ); + tempSegs(0).u.c.center = pos; + if ( !helix ) { + switch ( circleMode ) { + case circleCmdFixedRadius: + break; + case circleCmdFromCenter: + tempSegs(0).u.c.center = pos0; + circleRadius = FindDistance( tempSegs(0).u.c.center, pos ); + InfoMessage( _("Radius=%s"), FormatDistance(circleRadius) ); + break; + case circleCmdFromTangent: + circleRadius = FindDistance( tempSegs(0).u.c.center, pos0 ); + InfoMessage( _("Radius=%s"), FormatDistance(circleRadius) ); + break; + } + } + tempSegs(0).type = SEG_CRVTRK; + tempSegs(0).u.c.radius = helix?helixRadius:circleRadius; + tempSegs(0).u.c.a0 = 0.0; + tempSegs(0).u.c.a1 = 360.0; + tempSegs_da.cnt = 1; + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + return C_CONTINUE; + + case C_UP: + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + if ( helix ) { + UndoStart( _("Create Helix Track"), "newHelix" ); + t = NewCurvedTrack( tempSegs(0).u.c.center, helixRadius, 0.0, 0.0, helixTurns ); + } else { + if ( circleRadius <= 0 ) { + ErrorMessage( MSG_RADIUS_GTR_0 ); + return C_ERROR; + } + UndoStart( _("Create Circle Track"), "newCircle" ); + t = NewCurvedTrack( tempSegs(0).u.c.center, circleRadius, 0.0, 0.0, 0 ); + } + UndoEnd(); + DrawNewTrack(t); + if (helix) + wHide( helixW ); + else + InfoSubstituteControls( NULL, NULL ); + tempSegs_da.cnt = 0; + return C_TERMINATE; + + case C_REDRAW: + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + return C_CONTINUE; + + case C_CANCEL: + if (helix) + wHide( helixW ); + else + InfoSubstituteControls( NULL, NULL ); + return C_CONTINUE; + + default: + return C_CONTINUE; + } +} + + +static STATUS_T CmdCircle( wAction_t action, coOrd pos ) +{ + if ( action == C_START ) { + circleMode = (long)commandContext; + } + return CmdCircleCommon( action, pos, FALSE ); +} + + +static STATUS_T CmdHelix( wAction_t action, coOrd pos ) +{ + return CmdCircleCommon( action, pos, TRUE ); +} + +#ifdef LATER +static struct { + coOrd pos; + DIST_T radius; + } Dc2; + + +static STATUS_T CmdCircle2( wAction_t action, coOrd pos ) +{ + + switch (action) { + + case C_START: + InfoMessage( _("Place circle center") ); + return C_CONTINUE; + + case C_DOWN: + Dc2.pos = pos; + InfoMessage( _("Drag to set radius") ); + return C_CONTINUE; + + case C_MOVE: + dc2.radius = ConstrainR( FindDistance( Dc2.pos, pos ) ); + InfoMessage( "%s", FormatDistance(dc2.radius) ); + return C_CONTINUE; + + case C_UP: + curCommand = cmdCircle; + InfoMessage( _("Place circle") ); + return C_CONTINUE; + + default: + return C_CONTINUE; + } +} +#endif + + + +#include "bitmaps/helix.xpm" +#include "bitmaps/curve1.xpm" +#include "bitmaps/curve2.xpm" +#include "bitmaps/curve3.xpm" +#include "bitmaps/curve4.xpm" +#include "bitmaps/circle1.xpm" +#include "bitmaps/circle2.xpm" +#include "bitmaps/circle3.xpm" + + + +EXPORT void InitCmdCurve( wMenu_p menu ) +{ + + ButtonGroupBegin( _("Curve Track"), "cmdCircleSetCmd", _("Curve Tracks") ); + AddMenuButton( menu, CmdCurve, "cmdCurveEndPt", _("Curve from End-Pt"), wIconCreatePixMap( curve1_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CURVE1, (void*)0 ); + AddMenuButton( menu, CmdCurve, "cmdCurveTangent", _("Curve from Tangent"), wIconCreatePixMap( curve2_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CURVE2, (void*)1 ); + AddMenuButton( menu, CmdCurve, "cmdCurveCenter", _("Curve from Center"), wIconCreatePixMap( curve3_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CURVE3, (void*)2 ); + AddMenuButton( menu, CmdCurve, "cmdCurveChord", _("Curve from Chord"), wIconCreatePixMap( curve4_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CURVE4, (void*)3 ); + ButtonGroupEnd(); + + ButtonGroupBegin( _("Circle Track"), "cmdCurveSetCmd", _("Circle Tracks") ); + AddMenuButton( menu, CmdCircle, "cmdCircleFixedRadius", _("Fixed Radius Circle"), wIconCreatePixMap( circle1_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CIRCLE1, (void*)0 ); + AddMenuButton( menu, CmdCircle, "cmdCircleTangent", _("Circle from Tangent"), wIconCreatePixMap( circle2_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CIRCLE2, (void*)1 ); + AddMenuButton( menu, CmdCircle, "cmdCircleCenter", _("Circle from Center"), wIconCreatePixMap( circle3_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CIRCLE3, (void*)2 ); + ButtonGroupEnd(); + + ParamRegister( &circleRadiusPG ); + ParamCreateControls( &circleRadiusPG, NULL ); + +} + +EXPORT void InitCmdHelix( wMenu_p menu ) +{ + AddMenuButton( menu, CmdHelix, "cmdHelix", _("Helix"), wIconCreatePixMap(helix_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_HELIX, NULL ); + ParamRegister( &helixPG ); + RegisterChangeNotification( ChangeHelixW ); + +} diff --git a/app/bin/ccurve.h b/app/bin/ccurve.h new file mode 100644 index 0000000..1b2c7f6 --- /dev/null +++ b/app/bin/ccurve.h @@ -0,0 +1,48 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/ccurve.h,v 1.1 2005-12-07 15:47:36 rc-flyer 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. + */ + + +typedef struct { + curveType_e type; + coOrd curvePos; + coOrd pos1; + DIST_T curveRadius; + ANGLE_T a0, a1; + } curveData_t; + +#define crvCmdFromEP1 (0) +#define crvCmdFromTangent (1) +#define crvCmdFromCenter (2) +#define crvCmdFromChord (3) + +#define circleCmdFixedRadius (0) +#define circleCmdFromTangent (1) +#define circleCmdFromCenter (2) + +typedef void (*curveMessageProc)( char *, ... ); +STATUS_T CreateCurve( wAction_t, coOrd, BOOL_T, wDrawColor, DIST_T, long, curveMessageProc ); +int IsCurveCircle( track_p ); +void PlotCurve( long, coOrd, coOrd, coOrd, curveData_t *, BOOL_T ); +track_p NewCurvedTrack( coOrd, DIST_T, ANGLE_T, ANGLE_T, long ); +DIST_T CurveDescriptionDistance( coOrd, track_p ); +STATUS_T CurveDescriptionMove( track_p, wAction_t, coOrd ); +BOOL_T GetCurveMiddle( track_p, coOrd * ); diff --git a/app/bin/cdraw.c b/app/bin/cdraw.c new file mode 100644 index 0000000..59e45b8 --- /dev/null +++ b/app/bin/cdraw.c @@ -0,0 +1,1245 @@ +/** \file cdraw.c + * Drawing of geometric elements + */ + +/* 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 "track.h" +#include "ccurve.h" +#include "drawgeom.h" +#include "i18n.h" + +#include <stdint.h> + +extern void wSetSelectedFontSize(int size); + +static long fontSizeList[] = { + 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 24, 28, 32, 36, + 40, 48, 56, 64, 72, 80, 90, 100, 120, 140, 160, 180, + 200, 250, 300, 350, 400, 450, 500 }; + +EXPORT void LoadFontSizeList( + wList_p list, + long curFontSize ) +{ + wIndex_t curInx=0, inx1; + int inx; + wListClear( list ); + for ( inx=0; inx<sizeof fontSizeList/sizeof fontSizeList[0]; inx++ ) { + if ( ( inx==0 || curFontSize > fontSizeList[inx-1] ) && + ( curFontSize < fontSizeList[inx] ) ) { + sprintf( message, "%ld", curFontSize ); + curInx = wListAddValue( list, message, NULL, (void*)curFontSize ); + } + sprintf( message, "%ld", fontSizeList[inx] ); + inx1 = wListAddValue( list, message, NULL, (void*)fontSizeList[inx] ); + if ( curFontSize == fontSizeList[inx] ) + curInx = inx1; + } + if ( curFontSize > fontSizeList[(sizeof fontSizeList/sizeof fontSizeList[0])-1] ) { + sprintf( message, "%ld", curFontSize ); + curInx = wListAddValue( list, message, NULL, (void*)curFontSize ); + } + wListSetIndex( list, curInx ); + wFlush(); +} + + +EXPORT void UpdateFontSizeList( + long * fontSizeR, + wList_p list, + wIndex_t listInx ) +{ + long fontSize; + + if ( listInx >= 0 ) { + *fontSizeR = (long)wListGetItemContext( list, listInx ); + } else { + wListGetValues( list, message, sizeof message, NULL, NULL ); + if ( message[0] != '\0' ) { + fontSize = atol( message ); + if ( fontSize <= 0 ) { + NoticeMessage( _("Font Size must be > 0"), _("Ok"), NULL ); + sprintf( message, "%ld", *fontSizeR ); + wListSetValue( list, message ); + } else { + if ( fontSize <= 500 || NoticeMessage( MSG_LARGE_FONT, _("Yes"), _("No") ) > 0 ) { + + *fontSizeR = fontSize; + /* inform gtkfont dialog from change */ + wSetSelectedFontSize((int)fontSize); + /*LoadFontSizeList( list, *fontSizeR );*/ + } else { + sprintf( message, "%ld", *fontSizeR ); + wListSetValue( list, message ); + } + } + } + } +} + +/******************************************************************************* + * + * DRAW + * + */ + +struct extraData { + coOrd orig; + ANGLE_T angle; + wIndex_t segCnt; + trkSeg_t segs[1]; + }; + +static TRKTYP_T T_DRAW = -1; +static track_p ignoredTableEdge; +static track_p ignoredDraw; + + +static void ComputeDrawBoundingBox( track_p t ) +{ + struct extraData * xx = GetTrkExtraData(t); + coOrd lo, hi; + + GetSegBounds( xx->orig, xx->angle, xx->segCnt, xx->segs, &lo, &hi ); + hi.x += lo.x; + hi.y += lo.y; + SetBoundingBox( t, hi, lo ); +} + + +static track_p MakeDrawFromSeg1( + wIndex_t index, + coOrd pos, + ANGLE_T angle, + trkSeg_p sp ) +{ + struct extraData * xx; + track_p trk; + if ( sp->type == ' ' ) + return NULL; + trk = NewTrack( index, T_DRAW, 0, sizeof *xx ); + xx = GetTrkExtraData( trk ); + xx->orig = pos; + xx->angle = angle; + xx->segCnt = 1; + memcpy( xx->segs, sp, sizeof *(trkSeg_p)0 ); + ComputeDrawBoundingBox( trk ); + return trk; +} + +EXPORT track_p MakeDrawFromSeg( + coOrd pos, + ANGLE_T angle, + trkSeg_p sp ) +{ + return MakeDrawFromSeg1( 0, pos, angle, sp ); +} + + + + +static DIST_T DistanceDraw( track_p t, coOrd * p ) +{ + struct extraData * xx = GetTrkExtraData(t); + if ( ignoredTableEdge == t && xx->segs[0].type == SEG_TBLEDGE ) + return 100000.0; + if ( ignoredDraw == t ) + return 100000.0; + return DistanceSegs( xx->orig, xx->angle, xx->segCnt, xx->segs, p, NULL ); +} + + +static struct { + coOrd endPt[2]; + FLOAT_T length; + coOrd center; + DIST_T radius; + ANGLE_T angle0; + ANGLE_T angle1; + ANGLE_T angle; + long pointCount; + long lineWidth; + wDrawColor color; + wIndex_t benchChoice; + wIndex_t benchOrient; + wIndex_t dimenSize; + descPivot_t pivot; + wIndex_t fontSizeInx; + char text[STR_SIZE]; + LAYER_T layer; + } drawData; +typedef enum { E0, E1, CE, RA, LN, AL, A1, A2, VC, LW, CO, BE, OR, DS, TP, TA, TS, TX, PV, LY } drawDesc_e; +static descData_t drawDesc[] = { +/*E0*/ { DESC_POS, N_("End Pt 1: X"), &drawData.endPt[0] }, +/*E1*/ { DESC_POS, N_("End Pt 2: X"), &drawData.endPt[1] }, +/*CE*/ { DESC_POS, N_("Center: X"), &drawData.center }, +/*RA*/ { DESC_DIM, N_("Radius"), &drawData.radius }, +/*LN*/ { DESC_DIM, N_("Length"), &drawData.length }, +/*AL*/ { DESC_FLOAT, N_("Angle"), &drawData.angle }, +/*A1*/ { DESC_ANGLE, N_("CCW Angle"), &drawData.angle0 }, +/*A2*/ { DESC_ANGLE, N_("CW Angle"), &drawData.angle1 }, +/*VC*/ { DESC_LONG, N_("Point Count"), &drawData.pointCount }, +/*LW*/ { DESC_LONG, N_("Line Width"), &drawData.lineWidth }, +/*CO*/ { DESC_COLOR, N_("Color"), &drawData.color }, +/*BE*/ { DESC_LIST, N_("Lumber"), &drawData.benchChoice }, +/*OR*/ { DESC_LIST, N_("Orientation"), &drawData.benchOrient }, +/*DS*/ { DESC_LIST, N_("Size"), &drawData.dimenSize }, +/*TP*/ { DESC_POS, N_("Origin: X"), &drawData.endPt[0] }, +/*TA*/ { DESC_FLOAT, N_("Angle"), &drawData.angle }, +/*TS*/ { DESC_EDITABLELIST, N_("Font Size"), &drawData.fontSizeInx }, +/*TX*/ { DESC_STRING, N_("Text"), &drawData.text }, +/*PV*/ { DESC_PIVOT, N_("Pivot"), &drawData.pivot }, +/*LY*/ { DESC_LAYER, N_("Layer"), &drawData.layer }, + { DESC_NULL } }; +int drawSegInx; + +#define UNREORIGIN( Q, P, A, O ) { \ + (Q) = (P); \ + (Q).x -= (O).x; \ + (Q).y -= (O).y; \ + if ( (A) != 0.0 ) \ + Rotate( &(Q), zero, -(A) ); \ + } + +static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final ) +{ + struct extraData *xx = GetTrkExtraData(trk); + trkSeg_p segPtr; + coOrd mid; + const char * text; + long fontSize; + + if ( drawSegInx==-1 ) + return; + if ( inx == -1 ) + return; + segPtr = &xx->segs[drawSegInx]; + MainRedraw(); + //UndrawNewTrack( trk ); + switch ( inx ) { + case LW: + segPtr->width = drawData.lineWidth/mainD.dpi; + break; + case CO: + segPtr->color = drawData.color; + break; + case E0: + case E1: + if ( inx == E0 ) { + UNREORIGIN( segPtr->u.l.pos[0], drawData.endPt[0], xx->angle, xx->orig ); + } else { + UNREORIGIN( segPtr->u.l.pos[1], drawData.endPt[1], xx->angle, xx->orig ); + } + drawData.length = FindDistance( drawData.endPt[0], drawData.endPt[1] ); + drawData.angle = FindAngle( drawData.endPt[0], drawData.endPt[1] ); + drawDesc[LN].mode |= DESC_CHANGE; + drawDesc[AL].mode |= DESC_CHANGE; + break; + case LN: + case AL: + if ( segPtr->type == SEG_CRVLIN && inx == AL ) { + if ( drawData.angle <= 0.0 || drawData.angle >= 360.0 ) { + ErrorMessage( MSG_CURVE_OUT_OF_RANGE ); + drawData.angle = segPtr->u.c.a1; + drawDesc[AL].mode |= DESC_CHANGE; + break; + } + } else { + if ( drawData.length <= minLength ) { + ErrorMessage( MSG_OBJECT_TOO_SHORT ); + if ( segPtr->type != SEG_CRVLIN ) { + drawData.length = FindDistance( drawData.endPt[0], drawData.endPt[1] ); + } else { + drawData.length = segPtr->u.c.radius*2*M_PI*segPtr->u.c.a1/360.0; + } + drawDesc[LN].mode |= DESC_CHANGE; + break; + } + } + if ( segPtr->type != SEG_CRVLIN ) { + switch ( drawData.pivot ) { + case DESC_PIVOT_FIRST: + Translate( &drawData.endPt[1], drawData.endPt[0], drawData.angle, drawData.length ); + UNREORIGIN( segPtr->u.l.pos[1], drawData.endPt[1], xx->angle, xx->orig ); + drawDesc[E1].mode |= DESC_CHANGE; + break; + case DESC_PIVOT_SECOND: + Translate( &drawData.endPt[0], drawData.endPt[1], drawData.angle+180.0, drawData.length ); + UNREORIGIN( segPtr->u.l.pos[0], drawData.endPt[0], xx->angle, xx->orig ); + drawDesc[E0].mode |= DESC_CHANGE; + break; + case DESC_PIVOT_MID: + mid.x = (drawData.endPt[0].x+drawData.endPt[1].x)/2.0; + mid.y = (drawData.endPt[0].y+drawData.endPt[1].y)/2.0; + Translate( &drawData.endPt[0], mid, drawData.angle+180.0, drawData.length/2.0 ); + Translate( &drawData.endPt[1], mid, drawData.angle, drawData.length/2.0 ); + UNREORIGIN( segPtr->u.l.pos[0], drawData.endPt[0], xx->angle, xx->orig ); + UNREORIGIN( segPtr->u.l.pos[1], drawData.endPt[1], xx->angle, xx->orig ); + drawDesc[E0].mode |= DESC_CHANGE; + drawDesc[E1].mode |= DESC_CHANGE; + break; + default: + break; + } + } else { + if ( drawData.angle < 0.0 || drawData.angle >= 360.0 ) { + ErrorMessage( MSG_CURVE_OUT_OF_RANGE ); + drawData.angle = segPtr->u.c.a1; + drawDesc[AL].mode |= DESC_CHANGE; + } else { + segPtr->u.c.a0 = NormalizeAngle( segPtr->u.c.a0+segPtr->u.c.a1/2.0-drawData.angle/2.0); + segPtr->u.c.a1 = drawData.angle; + drawData.angle0 = NormalizeAngle( segPtr->u.c.a0+xx->angle ); + drawData.angle1 = NormalizeAngle( drawData.angle0+segPtr->u.c.a1 ); + drawDesc[A1].mode |= DESC_CHANGE; + drawDesc[A2].mode |= DESC_CHANGE; + } + } + break; + case CE: + UNREORIGIN( segPtr->u.c.center, drawData.center, xx->angle, xx->orig ); + break; + case RA: + segPtr->u.c.radius = drawData.radius; + break; + case A1: + segPtr->u.c.a0 = NormalizeAngle( drawData.angle0-xx->angle ); + drawData.angle1 = NormalizeAngle( drawData.angle0+drawData.angle ); + drawDesc[A2].mode |= DESC_CHANGE; + break; + case A2: + segPtr->u.c.a0 = NormalizeAngle( drawData.angle1-segPtr->u.c.a1-xx->angle ); + drawData.angle0 = NormalizeAngle( segPtr->u.c.a0+xx->angle ); + drawDesc[A1].mode |= DESC_CHANGE; + break; + case BE: + BenchUpdateOrientationList( (long)wListGetItemContext((wList_p)drawDesc[BE].control0, drawData.benchChoice ), (wList_p)drawDesc[OR].control0 ); + if ( drawData.benchOrient < wListGetCount( (wList_p)drawDesc[OR].control0 ) ) + wListSetIndex( (wList_p)drawDesc[OR].control0, drawData.benchOrient ); + else + drawData.benchOrient = 0; + segPtr->u.l.option = GetBenchData( (long)wListGetItemContext((wList_p)drawDesc[BE].control0, drawData.benchChoice ), drawData.benchOrient ); + break; + case OR: + segPtr->u.l.option = GetBenchData( (long)wListGetItemContext((wList_p)drawDesc[BE].control0, drawData.benchChoice ), drawData.benchOrient ); + break; + case DS: + segPtr->u.l.option = drawData.dimenSize; + break; + case TP: + UNREORIGIN( segPtr->u.t.pos, drawData.endPt[0], xx->angle, xx->orig ); + break; + case TA: + //segPtr->u.t.angle = NormalizeAngle( drawData.angle ); + xx->angle = NormalizeAngle( drawData.angle ); + break; + case TS: + fontSize = (long)segPtr->u.t.fontSize; + UpdateFontSizeList( &fontSize, (wList_p)drawDesc[TS].control0, drawData.fontSizeInx ); + segPtr->u.t.fontSize = fontSize; + break; + case TX: + text = wStringGetValue( (wString_p)drawDesc[TX].control0 ); + if ( text && text[0] && strcmp( segPtr->u.t.string, text ) != 0 ) { + MyFree( segPtr->u.t.string ); + segPtr->u.t.string = MyStrdup( text ); + /*(char*)drawDesc[TX].valueP = segPtr->u.t.string;*/ + } + break; + case LY: + SetTrkLayer( trk, drawData.layer); + break; + default: + AbortProg( "bad op" ); + } + ComputeDrawBoundingBox( trk ); + DrawNewTrack( trk ); +} + +static void DescribeDraw( track_p trk, char * str, CSIZE_T len ) +{ + struct extraData *xx = GetTrkExtraData(trk); + coOrd pos = oldMarker; + trkSeg_p segPtr; + int inx; + char * title = NULL; + + + DistanceSegs( xx->orig, xx->angle, xx->segCnt, xx->segs, &pos, &drawSegInx ); + if ( drawSegInx==-1 ) + return; + segPtr = &xx->segs[drawSegInx]; + for ( inx=0; inx<sizeof drawDesc/sizeof drawDesc[0]; inx++ ) { + drawDesc[inx].mode = DESC_IGNORE; + drawDesc[inx].control0 = NULL; + } + drawData.color = segPtr->color; + drawDesc[CO].mode = 0; + drawData.lineWidth = (long)floor(segPtr->width*mainD.dpi+0.5); + drawDesc[LW].mode = 0; + drawDesc[LY].mode = DESC_NOREDRAW; + drawDesc[BE].mode = + drawDesc[OR].mode = + drawDesc[DS].mode = DESC_IGNORE; + drawData.pivot = DESC_PIVOT_MID; + switch ( segPtr->type ) { + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + REORIGIN( drawData.endPt[0], segPtr->u.l.pos[0], xx->angle, xx->orig ); + REORIGIN( drawData.endPt[1], segPtr->u.l.pos[1], xx->angle, xx->orig ); + drawData.length = FindDistance( drawData.endPt[0], drawData.endPt[1] ); + drawData.angle = FindAngle( drawData.endPt[0], drawData.endPt[1] ); + drawDesc[LN].mode = + drawDesc[AL].mode = + drawDesc[PV].mode = 0; + drawDesc[E0].mode = + drawDesc[E1].mode = 0; + switch (segPtr->type) { + case SEG_STRLIN: + title = _("Straight Line"); + break; + case SEG_DIMLIN: + title = _("Dimension Line"); + drawDesc[CO].mode = DESC_IGNORE; + drawDesc[LW].mode = DESC_IGNORE; + drawData.dimenSize = (wIndex_t)segPtr->u.l.option; + drawDesc[DS].mode = 0; + break; + case SEG_BENCH: + title = _("Lumber"); + drawDesc[LW].mode = DESC_IGNORE; + drawDesc[BE].mode = + drawDesc[OR].mode = 0; + drawData.benchChoice = GetBenchListIndex( segPtr->u.l.option ); + drawData.benchOrient = (wIndex_t)(segPtr->u.l.option&0xFF); + break; + case SEG_TBLEDGE: + title = _("Table Edge"); + drawDesc[CO].mode = DESC_IGNORE; + drawDesc[LW].mode = DESC_IGNORE; + break; + } + break; + case SEG_CRVLIN: + REORIGIN( drawData.center, segPtr->u.c.center, xx->angle, xx->orig ); + drawData.radius = segPtr->u.c.radius; + drawDesc[CE].mode = + drawDesc[RA].mode = 0; + if ( segPtr->u.c.a1 >= 360.0 ) { + title = _("Circle"); + } else { + drawData.angle = segPtr->u.c.a1; + drawData.angle0 = NormalizeAngle( segPtr->u.c.a0+xx->angle ); + drawData.angle1 = NormalizeAngle( drawData.angle0+drawData.angle ); + drawDesc[AL].mode = + drawDesc[A1].mode = + drawDesc[A2].mode = 0; + title = _("Curved Line"); + } + break; + case SEG_FILCRCL: + REORIGIN( drawData.center, segPtr->u.c.center, xx->angle, xx->orig ); + drawData.radius = segPtr->u.c.radius; + drawDesc[CE].mode = + drawDesc[RA].mode = 0; + drawDesc[LW].mode = DESC_IGNORE; + title = _("Filled Circle"); + break; + case SEG_POLY: + drawData.pointCount = segPtr->u.p.cnt; + drawDesc[VC].mode = DESC_RO; + title = _("Poly Line"); + break; + case SEG_FILPOLY: + drawData.pointCount = segPtr->u.p.cnt; + drawDesc[VC].mode = DESC_RO; + drawDesc[LW].mode = DESC_IGNORE; + title = _("Polygon"); + break; + case SEG_TEXT: + REORIGIN( drawData.endPt[0], segPtr->u.t.pos, xx->angle, xx->orig ); + //drawData.angle = NormalizeAngle( segPtr->u.t.angle ); + drawData.angle = NormalizeAngle( xx->angle ); + strncpy( drawData.text, segPtr->u.t.string, sizeof drawData.text ); + /*drawData.fontSize = segPtr->u.t.fontSize;*/ + /*(char*)drawDesc[TX].valueP = segPtr->u.t.string;*/ + drawDesc[TP].mode = + drawDesc[TS].mode = + drawDesc[TX].mode = + drawDesc[TA].mode = + drawDesc[CO].mode = 0; /*Allow Text color setting*/ + drawDesc[LW].mode = DESC_IGNORE; + title = _("Text"); + break; + default: + AbortProg( "bad seg type" ); + } + + sprintf( str, _("%s: Layer=%d"), title, GetTrkLayer(trk)+1 ); + + DoDescribe( title, trk, drawDesc, UpdateDraw ); + if ( segPtr->type==SEG_BENCH && drawDesc[BE].control0!=NULL && drawDesc[OR].control0!=NULL) { + BenchLoadLists( (wList_p)drawDesc[BE].control0, (wList_p)drawDesc[OR].control0 ); + wListSetIndex( (wList_p)drawDesc[BE].control0, drawData.benchChoice ); + BenchUpdateOrientationList( (long)wListGetItemContext((wList_p)drawDesc[BE].control0, drawData.benchChoice ), (wList_p)drawDesc[OR].control0 ); + wListSetIndex( (wList_p)drawDesc[OR].control0, drawData.benchOrient ); + } + if ( segPtr->type==SEG_DIMLIN && drawDesc[DS].control0!=NULL ) { + wListClear( (wList_p)drawDesc[DS].control0 ); + wListAddValue( (wList_p)drawDesc[DS].control0, _("Tiny"), NULL, (void*)0 ); + wListAddValue( (wList_p)drawDesc[DS].control0, _("Small"), NULL, (void*)1 ); + wListAddValue( (wList_p)drawDesc[DS].control0, _("Medium"), NULL, (void*)2 ); + wListAddValue( (wList_p)drawDesc[DS].control0, _("Large"), NULL, (void*)3 ); + wListSetIndex( (wList_p)drawDesc[DS].control0, drawData.dimenSize ); + } + if ( segPtr->type==SEG_TEXT && drawDesc[TS].control0!=NULL ) { + LoadFontSizeList( (wList_p)drawDesc[TS].control0, (long)segPtr->u.t.fontSize ); + } +} + + +static void DrawDraw( track_p t, drawCmd_p d, wDrawColor color ) +{ + struct extraData * xx = GetTrkExtraData(t); + if ( (d->options&DC_QUICK) == 0 ) + DrawSegs( d, xx->orig, xx->angle, xx->segs, xx->segCnt, 0.0, color ); +} + + +static void DeleteDraw( track_p t ) +{ +} + + +static BOOL_T WriteDraw( track_p t, FILE * f ) +{ + struct extraData * xx = GetTrkExtraData(t); + BOOL_T rc = TRUE; + rc &= fprintf(f, "DRAW %d %d 0 0 0 %0.6f %0.6f 0 %0.6f\n", GetTrkIndex(t), GetTrkLayer(t), + xx->orig.x, xx->orig.y, xx->angle )>0; + rc &= WriteSegs( f, xx->segCnt, xx->segs ); + return rc; +} + + +static void ReadDraw( char * header ) +{ + track_p trk; + wIndex_t index; + coOrd orig; + DIST_T elev; + ANGLE_T angle; + wIndex_t layer; + struct extraData * xx; + + if ( !GetArgs( header+5, paramVersion<3?"dXpYf":paramVersion<9?"dL000pYf":"dL000pff", + &index, &layer, &orig, &elev, &angle ) ) + return; + ReadSegs(); + if (tempSegs_da.cnt == 1) { + trk = MakeDrawFromSeg1( index, orig, angle, &tempSegs(0) ); + SetTrkLayer( trk, layer ); + } else { + trk = NewTrack( index, T_DRAW, 0, sizeof *xx + (tempSegs_da.cnt-1) * sizeof *(trkSeg_p)0 ); + SetTrkLayer( trk, layer ); + xx = GetTrkExtraData(trk); + xx->orig = orig; + xx->angle = angle; + xx->segCnt = tempSegs_da.cnt; + memcpy( xx->segs, tempSegs_da.ptr, tempSegs_da.cnt * sizeof *(trkSeg_p)0 ); + ComputeDrawBoundingBox( trk ); + } +} + + +static void MoveDraw( track_p trk, coOrd orig ) +{ + struct extraData * xx = GetTrkExtraData(trk); + xx->orig.x += orig.x; + xx->orig.y += orig.y; + ComputeDrawBoundingBox( trk ); +} + + +static void RotateDraw( track_p trk, coOrd orig, ANGLE_T angle ) +{ + struct extraData * xx = GetTrkExtraData(trk); + Rotate( &xx->orig, orig, angle ); + xx->angle = NormalizeAngle( xx->angle + angle ); + ComputeDrawBoundingBox( trk ); +} + + +static void RescaleDraw( track_p trk, FLOAT_T ratio ) +{ + struct extraData * xx = GetTrkExtraData(trk); + xx->orig.x *= ratio; + xx->orig.y *= ratio; + RescaleSegs( xx->segCnt, xx->segs, ratio, ratio, ratio ); +} + + +static STATUS_T ModifyDraw( track_p trk, wAction_t action, coOrd pos ) +{ + struct extraData * xx = GetTrkExtraData(trk); + STATUS_T rc; + + if (action == C_DOWN) { + //UndrawNewTrack( trk ); + } + if ( action == C_MOVE ) + ignoredDraw = trk; + rc = DrawGeomModify( xx->orig, xx->angle, xx->segCnt, xx->segs, action, pos, GetTrkSelected(trk) ); + ignoredDraw = NULL; + if (action == C_UP) { + ComputeDrawBoundingBox( trk ); + DrawNewTrack( trk ); + } + return rc; +} + + +static void UngroupDraw( track_p trk ) +{ + struct extraData * xx = GetTrkExtraData(trk); + int inx; + if ( xx->segCnt <= 1 ) + return; + DeleteTrack( trk, FALSE ); + for ( inx=0; inx<xx->segCnt; inx++ ) { + trk = MakeDrawFromSeg( xx->orig, xx->angle, &xx->segs[inx] ); + if ( trk ) { + SetTrkBits( trk, TB_SELECTED ); + DrawNewTrack( trk ); + } + } +} + + +static ANGLE_T GetAngleDraw( + track_p trk, + coOrd pos, + EPINX_T * ep0, + EPINX_T * ep1 ) +{ + struct extraData * xx = GetTrkExtraData(trk); + ANGLE_T angle; + + pos.x -= xx->orig.x; + pos.y -= xx->orig.y; + Rotate( &pos, zero, -xx->angle ); + angle = GetAngleSegs( xx->segCnt, xx->segs, pos, NULL ); + if ( ep0 ) *ep0 = -1; + if ( ep1 ) *ep1 = -1; + return NormalizeAngle( angle + xx->angle ); +} + + + +static BOOL_T EnumerateDraw( + track_p trk ) +{ + struct extraData * xx; + int inx; + trkSeg_p segPtr; + + if ( trk ) { + xx = GetTrkExtraData(trk); + if ( xx->segCnt < 1 ) + return TRUE; + for ( inx=0; inx<xx->segCnt; inx++ ) { + segPtr = &xx->segs[inx]; + if ( segPtr->type == SEG_BENCH ) { + CountBench( segPtr->u.l.option, FindDistance( segPtr->u.l.pos[0], segPtr->u.l.pos[1] ) ); + } + } + } else { + TotalBench(); + } + return TRUE; +} + + +static void FlipDraw( + track_p trk, + coOrd orig, + ANGLE_T angle ) +{ + struct extraData * xx = GetTrkExtraData(trk); + FlipPoint( &xx->orig, orig, angle ); + xx->angle = NormalizeAngle( 2*angle - xx->angle + 180.0 ); + FlipSegs( xx->segCnt, xx->segs, zero, angle ); + ComputeDrawBoundingBox( trk ); +} + + +static trackCmd_t drawCmds = { + "DRAW", + DrawDraw, + DistanceDraw, + DescribeDraw, + DeleteDraw, + WriteDraw, + ReadDraw, + MoveDraw, + RotateDraw, + RescaleDraw, + NULL, + GetAngleDraw, /* getAngle */ + NULL, /* split */ + NULL, /* traverse */ + EnumerateDraw, + NULL, /* redraw */ + NULL, /* trim */ + NULL, /* merge */ + ModifyDraw, + NULL, /* getLength */ + NULL, /* getTrackParams */ + NULL, /* moveEndPt */ + NULL, /* query */ + UngroupDraw, + FlipDraw }; + +EXPORT BOOL_T OnTableEdgeEndPt( track_p trk, coOrd * pos ) +{ + track_p trk1; + struct extraData *xx; + coOrd pos1 = *pos; + + ignoredTableEdge = trk; + if ((trk1 = OnTrack( &pos1, FALSE, FALSE )) != NULL && + GetTrkType(trk1) == T_DRAW) { + ignoredTableEdge = NULL; + xx = GetTrkExtraData(trk1); + if (xx->segCnt < 1) + return FALSE; + if (xx->segs[0].type == SEG_TBLEDGE) { + if ( IsClose( FindDistance( *pos, xx->segs[0].u.l.pos[0] ) ) ) { + *pos = xx->segs[0].u.l.pos[0]; + return TRUE; + } else if ( IsClose( FindDistance( *pos, xx->segs[0].u.l.pos[1] ) ) ) { + *pos = xx->segs[0].u.l.pos[1]; + return TRUE; + } + } + } + ignoredTableEdge = NULL; + return FALSE; +} + + + + +static void DrawRedraw(void); +static drawContext_t drawCmdContext = { + InfoMessage, + DrawRedraw, + &mainD, + OP_LINE }; + +static void DrawRedraw( void ) +{ + MainRedraw(); +} + + +#ifdef LATER +static void DrawOk( void * context ) +{ + track_p t; + struct extraData * xx; + trkSeg_p sp; + wIndex_t cnt; + + for ( cnt=0,sp=&DrawLineSegs(0); sp < &DrawLineSegs(drawCmdContext.Segs_da.cnt); sp++ ) + if (sp->type != ' ') + cnt++; + if (cnt == 0) + return; + UndoStart( _("Create Lines"), "newDraw" ); + for ( sp=&DrawLineSegs(0); sp < &DrawLineSegs(drawCmdContext.Segs_da.cnt); sp++ ) { + if (sp->type != ' ') { + t = NewTrack( 0, T_DRAW, 0, sizeof *xx + sizeof *(trkSeg_p)0 ); + xx = GetTrkExtraData( t ); + xx->orig = zero; + xx->angle = 0.0; + xx->segCnt = 1; + memcpy( xx->segs, sp, sizeof *(trkSeg_p)0 ); + ComputeDrawBoundingBox( t ); + DrawNewTrack(t); + } + } + UndoEnd(); + DYNARR_RESET( trkSeg_t, drawCmdContext.Segs_da ); + Reset(); +} +#endif + + + +static wIndex_t benchChoice; +static wIndex_t benchOrient; +static wIndex_t dimArrowSize; +static wDrawColor lineColor; +static wDrawColor benchColor; +#ifdef LATER +static wIndex_t benchInx; +#endif + +static paramIntegerRange_t i0_100 = { 0, 100, 25 }; +static paramData_t drawPLs[] = { +#define drawWidthPD (drawPLs[0]) + { PD_LONG, &drawCmdContext.Width, "linewidth", PDO_NORECORD, &i0_100, N_("Line Width") }, +#define drawColorPD (drawPLs[1]) + { PD_COLORLIST, &lineColor, "linecolor", PDO_NORECORD, NULL, N_("Color") }, +#define drawBenchColorPD (drawPLs[2]) + { PD_COLORLIST, &benchColor, "benchcolor", PDO_NORECORD, NULL, N_("Color") }, +#define drawBenchChoicePD (drawPLs[3]) + { PD_DROPLIST, &benchChoice, "benchlist", PDO_NOPREF|PDO_NORECORD|PDO_LISTINDEX, (void*)80, N_("Lumber Type") }, +#define drawBenchOrientPD (drawPLs[4]) +#ifdef WINDOWS + { PD_DROPLIST, &benchOrient, "benchorient", PDO_NOPREF|PDO_NORECORD|PDO_LISTINDEX, (void*)45, "", 0 }, +#else + { PD_DROPLIST, &benchOrient, "benchorient", PDO_NOPREF|PDO_NORECORD|PDO_LISTINDEX, (void*)105, "", 0 }, +#endif +#define drawDimArrowSizePD (drawPLs[5]) + { PD_DROPLIST, &dimArrowSize, "arrowsize", PDO_NORECORD|PDO_LISTINDEX, (void*)80, N_("Size") } }; +static paramGroup_t drawPG = { "draw", 0, drawPLs, sizeof drawPLs/sizeof drawPLs[0] }; + +static char * objectName[] = { + N_("Straight"), + N_("Dimension"), + N_("Lumber"), + N_("Table Edge"), + N_("Curved"), + N_("Curved"), + N_("Curved"), + N_("Curved"), + N_("Circle"), + N_("Circle"), + N_("Circle"), + N_("Box"), + N_("Polyline"), + N_("Filled Circle"), + N_("Filled Circle"), + N_("Filled Circle"), + N_("Filled Box"), + N_("Polygon"), + NULL}; + +static STATUS_T CmdDraw( wAction_t action, coOrd pos ) + +{ + static BOOL_T infoSubst = FALSE; + wControl_p controls[4]; + char * labels[3]; + static char labelName[40]; + + switch (action&0xFF) { + + case C_START: + ParamLoadControls( &drawPG ); + /*drawContext = &drawCmdContext;*/ + drawWidthPD.option |= PDO_NORECORD; + drawColorPD.option |= PDO_NORECORD; + drawBenchColorPD.option |= PDO_NORECORD; + drawBenchChoicePD.option |= PDO_NORECORD; + drawBenchOrientPD.option |= PDO_NORECORD; + drawDimArrowSizePD.option |= PDO_NORECORD; + drawCmdContext.Op = (wIndex_t)(long)commandContext; + if ( drawCmdContext.Op < 0 || drawCmdContext.Op > OP_LAST ) { + NoticeMessage( "cmdDraw: Op %d", _("Ok"), NULL, drawCmdContext.Op ); + drawCmdContext.Op = OP_LINE; + } + /*DrawGeomOp( (void*)(drawCmdContext.Op>=0?drawCmdContext.Op:OP_LINE) );*/ + infoSubst = TRUE; + switch( drawCmdContext.Op ) { + case OP_LINE: + case OP_CURVE1: + case OP_CURVE2: + case OP_CURVE3: + case OP_CURVE4: + case OP_CIRCLE2: + case OP_CIRCLE3: + case OP_BOX: + case OP_POLY: + controls[0] = drawWidthPD.control; + controls[1] = drawColorPD.control; + controls[2] = NULL; + sprintf( labelName, _("%s Line Width"), _(objectName[drawCmdContext.Op]) ); + labels[0] = labelName; + labels[1] = N_("Color"); + InfoSubstituteControls( controls, labels ); + drawWidthPD.option &= ~PDO_NORECORD; + drawColorPD.option &= ~PDO_NORECORD; + break; + case OP_FILLCIRCLE2: + case OP_FILLCIRCLE3: + case OP_FILLBOX: + case OP_FILLPOLY: + controls[0] = drawColorPD.control; + controls[1] = NULL; + sprintf( labelName, _("%s Color"), _(objectName[drawCmdContext.Op]) ); + labels[0] = labelName; + ParamLoadControls( &drawPG ); + InfoSubstituteControls( controls, labels ); + drawColorPD.option &= ~PDO_NORECORD; + break; + case OP_BENCH: + controls[0] = drawBenchChoicePD.control; + controls[1] = drawBenchOrientPD.control; + controls[2] = drawBenchColorPD.control; + controls[3] = NULL; + labels[0] = N_("Lumber Type"); + labels[1] = ""; + labels[2] = N_("Color"); + if ( wListGetCount( (wList_p)drawBenchChoicePD.control ) == 0 ) + BenchLoadLists( (wList_p)drawBenchChoicePD.control, (wList_p)drawBenchOrientPD.control ); +#ifdef LATER + if ( benchInx >= 0 && benchInx < wListGetCount( (wList_p)drawBenchChoicePD.control ) ) + wListSetIndex( (wList_p)drawBenchChoicePD.control, benchInx ); +#endif + ParamLoadControls( &drawPG ); + BenchUpdateOrientationList( (long)wListGetItemContext( (wList_p)drawBenchChoicePD.control, benchChoice ), (wList_p)drawBenchOrientPD.control ); + wListSetIndex( (wList_p)drawBenchOrientPD.control, benchOrient ); + InfoSubstituteControls( controls, labels ); + drawBenchColorPD.option &= ~PDO_NORECORD; + drawBenchChoicePD.option &= ~PDO_NORECORD; + drawBenchOrientPD.option &= ~PDO_NORECORD; + break; + case OP_DIMLINE: + controls[0] = drawDimArrowSizePD.control; + controls[1] = NULL; + labels[0] = N_("Dimension Line Size"); + if ( wListGetCount( (wList_p)drawDimArrowSizePD.control ) == 0 ) { + wListAddValue( (wList_p)drawDimArrowSizePD.control, _("Tiny"), NULL, NULL ); + wListAddValue( (wList_p)drawDimArrowSizePD.control, _("Small"), NULL, NULL ); + wListAddValue( (wList_p)drawDimArrowSizePD.control, _("Medium"), NULL, NULL ); + wListAddValue( (wList_p)drawDimArrowSizePD.control, _("Large"), NULL, NULL ); + } + ParamLoadControls( &drawPG ); + InfoSubstituteControls( controls, labels ); + drawDimArrowSizePD.option &= ~PDO_NORECORD; + break; + case OP_TBLEDGE: + InfoSubstituteControls( NULL, NULL ); + InfoMessage( _("Drag to create Table Edge") ); + drawColorPD.option &= ~PDO_NORECORD; + break; + default: + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } + ParamGroupRecord( &drawPG ); + DrawGeomMouse( C_START, pos, &drawCmdContext ); + + return C_CONTINUE; + + case wActionLDown: + ParamLoadData( &drawPG ); + if ( drawCmdContext.Op == OP_BENCH ) { + drawCmdContext.benchOption = GetBenchData( (long)wListGetItemContext((wList_p)drawBenchChoicePD.control, benchChoice ), benchOrient ); + drawCmdContext.Color = benchColor; +#ifdef LATER + benchInx = wListGetIndex( (wList_p)drawBenchChoicePD.control ); +#endif + } else if ( drawCmdContext.Op == OP_DIMLINE ) { + drawCmdContext.benchOption = dimArrowSize; + } else { + drawCmdContext.Color = lineColor; + } + if ( infoSubst ) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } + case wActionLDrag: + ParamLoadData( &drawPG ); + case wActionMove: + case wActionLUp: + case wActionRDown: + case wActionRDrag: + case wActionRUp: + case wActionText: + case C_CMDMENU: + SnapPos( &pos ); + return DrawGeomMouse( action, pos, &drawCmdContext ); + + case C_CANCEL: + InfoSubstituteControls( NULL, NULL ); + return DrawGeomMouse( action, pos, &drawCmdContext ); + + case C_OK: + return DrawGeomMouse( (0x0D<<8|wActionText), pos, &drawCmdContext ); + /*DrawOk( NULL );*/ + + case C_FINISH: + return DrawGeomMouse( (0x0D<<8|wActionText), pos, &drawCmdContext ); + /*DrawOk( NULL );*/ + + case C_REDRAW: + return DrawGeomMouse( action, pos, &drawCmdContext ); + + default: + return C_CONTINUE; + } +} + +#include "bitmaps/dline.xpm" +#include "bitmaps/ddimlin.xpm" +#include "bitmaps/dbench.xpm" +#include "bitmaps/dtbledge.xpm" +#include "bitmaps/dcurve1.xpm" +#include "bitmaps/dcurve2.xpm" +#include "bitmaps/dcurve3.xpm" +#include "bitmaps/dcurve4.xpm" +/*#include "bitmaps/dcircle1.xpm"*/ +#include "bitmaps/dcircle2.xpm" +#include "bitmaps/dcircle3.xpm" +/*#include "bitmaps/dflcrcl1.xpm"*/ +#include "bitmaps/dflcrcl2.xpm" +#include "bitmaps/dflcrcl3.xpm" +#include "bitmaps/dbox.xpm" +#include "bitmaps/dfilbox.xpm" +#include "bitmaps/dpoly.xpm" +#include "bitmaps/dfilpoly.xpm" + +typedef struct { + char **xpm; + int OP; + char * shortName; + char * cmdName; + char * helpKey; + long acclKey; + } drawData_t; + +static drawData_t dlineCmds[] = { + { dline_xpm, OP_LINE, N_("Line"), N_("Draw Line"), "cmdDrawLine", ACCL_DRAWLINE }, + { ddimlin_xpm, OP_DIMLINE, N_("Dimension Line"), N_("Draw Dimension Line"), "cmdDrawDimLine", ACCL_DRAWDIMLINE }, + { dbench_xpm, OP_BENCH, N_("Benchwork"), N_("Draw Benchwork"), "cmdDrawBench", ACCL_DRAWBENCH }, + { dtbledge_xpm, OP_TBLEDGE, N_("Table Edge"), N_("Draw Table Edge"), "cmdDrawTableEdge", ACCL_DRAWTBLEDGE } }; +static drawData_t dcurveCmds[] = { + { dcurve1_xpm, OP_CURVE1, N_("Curve End"), N_("Draw Curve from End"), "cmdDrawCurveEndPt", ACCL_DRAWCURVE1 }, + { dcurve2_xpm, OP_CURVE2, N_("Curve Tangent"), N_("Draw Curve from Tangent"), "cmdDrawCurveTangent", ACCL_DRAWCURVE2 }, + { dcurve3_xpm, OP_CURVE3, N_("Curve Center"), N_("Draw Curve from Center"), "cmdDrawCurveCenter", ACCL_DRAWCURVE3 }, + { dcurve4_xpm, OP_CURVE4, N_("Curve Chord"), N_("Draw Curve from Chord"), "cmdDrawCurveChord", ACCL_DRAWCURVE4 } }; +static drawData_t dcircleCmds[] = { + /*{ dcircle1_xpm, OP_CIRCLE1, "Circle Fixed Radius", "Draw Fixed Radius Circle", "cmdDrawCircleFixedRadius", ACCL_DRAWCIRCLE1 },*/ + { dcircle2_xpm, OP_CIRCLE3, N_("Circle Tangent"), N_("Draw Circle from Tangent"), "cmdDrawCircleTangent", ACCL_DRAWCIRCLE2 }, + { dcircle3_xpm, OP_CIRCLE2, N_("Circle Center"), N_("Draw Circle from Center"), "cmdDrawCircleCenter", ACCL_DRAWCIRCLE3 }, + /*{ dflcrcl1_xpm, OP_FILLCIRCLE1, "Circle Filled Fixed Radius", "Draw Fixed Radius Filled Circle", "cmdDrawFilledCircleFixedRadius", ACCL_DRAWFILLCIRCLE1 },*/ + { dflcrcl2_xpm, OP_FILLCIRCLE3, N_("Circle Filled Tangent"), N_("Draw Filled Circle from Tangent"), "cmdDrawFilledCircleTangent", ACCL_DRAWFILLCIRCLE2 }, + { dflcrcl3_xpm, OP_FILLCIRCLE2, N_("Circle Filled Center"), N_("Draw Filled Circle from Center"), "cmdDrawFilledCircleCenter", ACCL_DRAWFILLCIRCLE3 } }; +static drawData_t dshapeCmds[] = { + { dbox_xpm, OP_BOX, N_("Box"), N_("Draw Box"), "cmdDrawBox", ACCL_DRAWBOX }, + { dfilbox_xpm, OP_FILLBOX, N_("Filled Box"), N_("Draw Filled Box"), "cmdDrawFilledBox", ACCL_DRAWFILLBOX }, + { dpoly_xpm, OP_POLY, N_("Poly Line"), N_("Draw Polyline"), "cmdDrawPolyline", ACCL_DRAWPOLYLINE }, + { dfilpoly_xpm, OP_FILLPOLY, N_("Polygon"), N_("Draw Polygon"), "cmdDrawPolygon", ACCL_DRAWPOLYGON } }; + +typedef struct { + char * helpKey; + char * menuTitle; + char * stickyLabel; + int cnt; + drawData_t * data; + long acclKey; + wIndex_t cmdInx; + int curr; + } drawStuff_t; +static drawStuff_t drawStuff[4]; + + +static drawStuff_t drawStuff[4] = { + { "cmdDrawLineSetCmd", N_("Straight Objects"), N_("Draw Straight Objects"), 4, dlineCmds }, + { "cmdDrawCurveSetCmd", N_("Curved Lines"), N_("Draw Curved Lines"), 4, dcurveCmds }, + { "cmdDrawCircleSetCmd", N_("Circle Lines"), N_("Draw Circles"), 4, dcircleCmds }, + { "cmdDrawShapeSetCmd", N_("Shapes"), N_("Draw Shapes"), 4, dshapeCmds} }; + + +#ifdef LATER +static void SetDrawMode( char * modeName ) +{ + wButton_p bb; + int inx1, inx2; + drawData_t * dp; + + for ( inx1=0; inx1<4; inx1++ ) { + for ( inx2=0; inx2<drawStuff[inx1].cnt; inx2++ ) { + dp = &drawStuff[inx1].data[inx2]; + if (strncmp( modeName, dp->modeS, strlen(dp->modeS) ) == 0 ) { + bb = GetCommandButton(drawStuff[inx1].cmdInx); + wButtonSetLabel( bb, (char*)(dp->icon) ); + wControlSetHelp( (wControl_p)bb, dp->help ); + drawStuff[inx1].curr = inx2; + DoCommandB( (void*)(drawStuff[inx1].cmdInx) ); + return; + } + } + } +} +#endif + + +static void ChangeDraw( long changes ) +{ + wIndex_t choice, orient; + if ( changes & CHANGE_UNITS ) { + if ( drawBenchChoicePD.control && drawBenchOrientPD.control ) { + choice = wListGetIndex( (wList_p)drawBenchChoicePD.control ); + orient = wListGetIndex( (wList_p)drawBenchOrientPD.control ); + BenchLoadLists( (wList_p)drawBenchChoicePD.control, (wList_p)drawBenchOrientPD.control ); + wListSetIndex( (wList_p)drawBenchChoicePD.control, choice ); + wListSetIndex( (wList_p)drawBenchOrientPD.control, orient ); + } + } +} + + + +static void DrawDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + if ( inx >= 0 && pg->paramPtr[inx].valueP == &benchChoice ) + BenchUpdateOrientationList( (long)wListGetItemContext( (wList_p)drawBenchChoicePD.control, (wIndex_t)*(long*)valueP ), (wList_p)drawBenchOrientPD.control ); +} + +EXPORT void InitCmdDraw( wMenu_p menu ) +{ + int inx1, inx2; + drawStuff_t * dsp; + drawData_t * ddp; + wIcon_p icon; + + drawCmdContext.Color = wDrawColorBlack; + lineColor = wDrawColorBlack; + benchColor = wDrawFindColor( wRGB(255,192,0) ); + ParamCreateControls( &drawPG, DrawDlgUpdate ); + + for ( inx1=0; inx1<4; inx1++ ) { + dsp = &drawStuff[inx1]; + ButtonGroupBegin( _(dsp->menuTitle), dsp->helpKey, _(dsp->stickyLabel) ); + for ( inx2=0; inx2<dsp->cnt; inx2++ ) { + ddp = &dsp->data[inx2]; + icon = wIconCreatePixMap( ddp->xpm ); + AddMenuButton( menu, CmdDraw, ddp->helpKey, _(ddp->cmdName), icon, LEVEL0_50, IC_STICKY|IC_POPUP2, ddp->acclKey, (void *)(intptr_t)ddp->OP ); + } + ButtonGroupEnd(); + } + + ParamRegister( &drawPG ); + RegisterChangeNotification( ChangeDraw ); +#ifdef LATER + InitCommand( cmdDraw, N_("Draw"), draw_bits, LEVEL0_50, IC_POPUP|IC_CMDMENU, ACCL_DRAW ); +#endif +} + + +BOOL_T ReadTableEdge( char * line ) +{ + track_p trk; + TRKINX_T index; + DIST_T elev0, elev1; + trkSeg_t seg; + wIndex_t layer; + + if ( !GetArgs( line, paramVersion<3?"dXpYpY":paramVersion<9?"dL000pYpY":"dL000pfpf", + &index, &layer, &seg.u.l.pos[0], &elev0, &seg.u.l.pos[1], &elev1 ) ) + return FALSE; + seg.type = SEG_TBLEDGE; + seg.color = wDrawColorBlack; + seg.width = 0; + trk = MakeDrawFromSeg1( index, zero, 0.0, &seg ); + SetTrkLayer(trk, layer); + return TRUE; +} + +/** + * Create a new segment for text. The data are stored in a trk structure. + * Storage for the string is dynamically allocated. + * + * \param index IN of new element + * \param pos IN coordinates of element + * \param angle IN orientation + * \param text IN text itself + * \param textSize IN font size in pts + * \param color IN text color + * \return the newly allocated trk structure + */ + +EXPORT track_p NewText( + wIndex_t index, + coOrd pos, + ANGLE_T angle, + char * text, + CSIZE_T textSize, + wDrawColor color ) +{ + trkSeg_t tempSeg; + track_p trk; + tempSeg.type = SEG_TEXT; + tempSeg.color = color; + tempSeg.width = 0; + tempSeg.u.t.pos = zero; + tempSeg.u.t.angle = angle; + tempSeg.u.t.fontP = NULL; + tempSeg.u.t.fontSize = textSize; + tempSeg.u.t.string = MyStrdup( text ); + trk = MakeDrawFromSeg1( index, pos, angle, &tempSeg ); + return trk; +} + + +EXPORT BOOL_T ReadText( char * line ) +{ + coOrd pos; + CSIZE_T textSize; + char * text; + wIndex_t index; + wIndex_t layer; + track_p trk; + ANGLE_T angle; + wDrawColor color = wDrawColorBlack; + if ( paramVersion<3 ) { + if (!GetArgs( line, "XXpYql", &index, &layer, &pos, &angle, &text, &textSize )) + return FALSE; + } else if (paramVersion<9 ) { + if (!GetArgs(line, "dL000pYql", &index, &layer, &pos, &angle, &text, &textSize)) + return FALSE; + } else { + if (!GetArgs(line, "dLl00pfql", &index, &layer, &color, &pos, &angle, &text, &textSize )) + return FALSE; + } + + trk = NewText( index, pos, angle, text, textSize, color ); + SetTrkLayer( trk, layer ); + MyFree(text); + return TRUE; +} + + +EXPORT void InitTrkDraw( void ) +{ + T_DRAW = InitObject( &drawCmds ); + AddParam( "TABLEEDGE", ReadTableEdge ); + AddParam( "TEXT", ReadText ); +} diff --git a/app/bin/celev.c b/app/bin/celev.c new file mode 100644 index 0000000..b4691d1 --- /dev/null +++ b/app/bin/celev.c @@ -0,0 +1,475 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/celev.c,v 1.4 2008-03-06 19:35:05 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 "track.h" +#include "cselect.h" +#include "i18n.h" + +/***************************************************************************** + * + * ELEVATION + * + */ + +static wWin_p elevW; + +static long elevModeV; +static char elevStationV[STR_SIZE]; +static DIST_T elevHeightV = 0.0; + +static DIST_T elevOldValue; +static track_p elevTrk; +static EPINX_T elevEp; +static BOOL_T elevUndo = FALSE; + +static char * elevModeLabels[] = { N_("None"), N_("Defined"), N_("Hidden"), + N_("Computed"), N_("Grade"), N_("Station"), N_("Ignore"), NULL }; +static paramFloatRange_t r_1000_1000 = { -1000, 1000 }; + +static paramData_t elevationPLs[] = { +#define I_MODE (0) + { PD_RADIO, &elevModeV, "mode", 0, elevModeLabels, "" }, +#define I_HEIGHT (1) + { PD_FLOAT, &elevHeightV, "value", PDO_DIM|PDO_DLGNEWCOLUMN, &r_1000_1000 }, +#define I_COMPUTED (2) + { PD_MESSAGE, NULL, "computed", 0, (void*)80 }, +#define I_GRADE (3) + { PD_MESSAGE, NULL, "grade", 0, (void*)80 }, +#define I_STATION (4) + { PD_STRING, elevStationV, "station", PDO_DLGUNDERCMDBUTT, (void*)200 } }; +static paramGroup_t elevationPG = { "elev", 0, elevationPLs, sizeof elevationPLs/sizeof elevationPLs[0] }; + + +static void LayoutElevW( + paramData_t * pd, + int inx, + wPos_t colX, + wPos_t * x, + wPos_t * y ) +{ + static wPos_t h = 0; + switch ( inx ) { + case I_HEIGHT: + h = wControlGetHeight( elevationPLs[I_MODE].control )/((sizeof elevModeLabels/sizeof elevModeLabels[0])-1); +#ifndef WINDOWS + h += 3; +#endif + *y = DlgSepTop+h+h/2; + break; + case I_COMPUTED: + case I_GRADE: + case I_STATION: + *y = DlgSepTop+h*(inx+1); + break; + } +} + + +static int GetElevMode( void ) +{ + int mode; + int newMode; + static int modeMap[7] = { ELEV_NONE, ELEV_DEF|ELEV_VISIBLE, ELEV_DEF, ELEV_COMP|ELEV_VISIBLE, ELEV_GRADE|ELEV_VISIBLE, ELEV_STATION|ELEV_VISIBLE, ELEV_IGNORE }; + mode = (int)elevModeV; + if (mode<0||mode>=7) + return -1; + newMode = modeMap[mode]; + return newMode; +} + + +#ifdef LATER +static void DoElevRadio( long mode, void * context ) +{ + if ( mode < 0 || mode >= 7 ) + return; +#ifdef ELEVM + ParamLoadMessage( elevMessageM, "" ); +#endif + ParamControlActive( &elevationPG, I_HEIGHT, FALSE ); + ParamControlActive( &elevationPG, I_STATION, FALSE ); + switch ( mode ) { + case 0: + break; + case 1: + case 2: + ParamControlActive( &elevationPG, I_HEIGHT, TRUE ); + break; + case 3: + case 4: +#ifdef OLDELEV + if ( !( (rc0 == FDE_DEF && rc1 == FDE_DEF) || + (rc0 == FDE_DEF && rc1 == FDE_END) || + (rc0 == FDE_END && rc1 == FDE_DEF) ) ) { + ParamLoadMessage( &elevationPG, I_MSG, _("There are no reachable Defined Elevations") ); + ParamLoadControl( &elevationPG, I_MODE ); + return; + } +#endif + break; + case 5: + wControlActive( (wControl_p)elevStationS, TRUE ); + break; + } + elevModeV = mode; + DoElevUpdate( NULL, 1, NULL ); +} +#endif + + +static void DoElevUpdate( paramGroup_p pg, int inx, void * valueP ) +{ + int oldMode, newMode; + coOrd pos; + DIST_T elevNewValue, elevOldValue, diff; + DIST_T radius; + + if ( inx == 0 ) { + long mode = *(long*)valueP; + if ( mode < 0 || mode >= 7 ) + return; +#ifdef ELEVM + ParamLoadMessage( elevMessageM, "" ); +#endif + ParamControlActive( &elevationPG, I_HEIGHT, FALSE ); + ParamControlActive( &elevationPG, I_STATION, FALSE ); + switch ( mode ) { + case 0: + break; + case 1: + case 2: + ParamControlActive( &elevationPG, I_HEIGHT, TRUE ); + break; + case 3: + case 4: +#ifdef OLDELEV + if ( !( (rc0 == FDE_DEF && rc1 == FDE_DEF) || + (rc0 == FDE_DEF && rc1 == FDE_END) || + (rc0 == FDE_END && rc1 == FDE_DEF) ) ) { + ParamLoadMessage( &elevationPG, I_MSG, _("There are no reachable Defined Elevations") ); + ParamLoadControl( &elevationPG, I_MODE ); + return; + } +#endif + break; + case 5: + ParamControlActive( &elevationPG, I_STATION, TRUE ); + break; + } + elevModeV = mode; + } + ParamLoadData( &elevationPG ); + newMode = GetElevMode(); + if (newMode == -1) + return; + if (elevTrk == NULL) + return; + oldMode = GetTrkEndElevUnmaskedMode( elevTrk, elevEp ); + elevNewValue = 0.0; + if ((newMode&ELEV_MASK) == ELEV_DEF) + elevNewValue = elevHeightV; + if (oldMode == newMode) { + if ((newMode&ELEV_MASK) == ELEV_DEF) { + elevOldValue = GetTrkEndElevHeight( elevTrk, elevEp ); + diff = fabs( elevOldValue-elevNewValue ); + if ( diff < 0.02 ) + return; + } else if ((newMode&ELEV_MASK) == ELEV_STATION) { + if ( strcmp(elevStationV, GetTrkEndElevStation( elevTrk, elevEp ) ) == 0) + return; + } else { + return; + } + } + if (elevUndo == FALSE) { + UndoStart( _("Set Elevation"), "Set Elevation" ); + elevUndo = TRUE; + } + pos = GetTrkEndPos( elevTrk, elevEp ); + radius = 0.05*mainD.scale; + if ( radius < trackGauge/2.0 ) + radius = trackGauge/2.0; + if ( (oldMode&ELEV_MASK)==ELEV_DEF || (oldMode&ELEV_MASK)==ELEV_IGNORE ) + DrawFillCircle( &tempD, pos, radius, + ((oldMode&ELEV_MASK)==ELEV_DEF?elevColorDefined:elevColorIgnore)); + HilightSelectedEndPt(FALSE, elevTrk, elevEp); + UpdateTrkEndElev( elevTrk, elevEp, newMode, elevNewValue, elevStationV ); + HilightSelectedEndPt(TRUE, elevTrk, elevEp); + if ( (newMode&ELEV_MASK)==ELEV_DEF || (newMode&ELEV_MASK)==ELEV_IGNORE ) + DrawFillCircle( &tempD, pos, radius, + ((newMode&ELEV_MASK)==ELEV_DEF?elevColorDefined:elevColorIgnore)); +} + + +static void DoElevDone( void * arg ) +{ + DoElevUpdate( NULL, 1, NULL ); + HilightElevations( FALSE ); + HilightSelectedEndPt( FALSE, elevTrk, elevEp ); + wHide( elevW ); + elevTrk = NULL; + Reset(); +} + + +static void DoElevHilight( void * junk ) +{ + HilightElevations( TRUE ); +} + + +static void ElevSelect( track_p trk, EPINX_T ep ) +{ + int mode; + DIST_T elevX, grade, elev, dist; + long radio; + BOOL_T computedOk; + BOOL_T gradeOk = TRUE; + track_p trk1; + EPINX_T ep1; + + DoElevUpdate( NULL, 1, NULL ); + elevOldValue = 0.0; + elevHeightV = 0.0; + elevStationV[0] = 0; + HilightSelectedEndPt(FALSE, elevTrk, elevEp); + elevTrk = trk; + elevEp = ep; + mode = GetTrkEndElevUnmaskedMode( trk, ep ); + ParamLoadControls( &elevationPG ); + ParamControlActive( &elevationPG, I_MODE, TRUE ); + ParamControlActive( &elevationPG, I_HEIGHT, FALSE ); + ParamControlActive( &elevationPG, I_STATION, FALSE ); + ParamLoadMessage( &elevationPG, I_COMPUTED, "" ); + ParamLoadMessage( &elevationPG, I_GRADE, "" ); + switch (mode & ELEV_MASK) { + case ELEV_NONE: + radio = 0; + break; + case ELEV_DEF: + if ( mode & ELEV_VISIBLE ) + radio = 1; + else + radio = 2; + elevHeightV = GetTrkEndElevHeight(trk,ep); + elevOldValue = elevHeightV; + ParamLoadControl( &elevationPG, I_HEIGHT ); + ParamControlActive( &elevationPG, I_HEIGHT, TRUE ); + break; + case ELEV_COMP: + radio = 3; + break; + case ELEV_GRADE: + radio = 4; + break; + case ELEV_STATION: + radio = 5; + strcpy( elevStationV, GetTrkEndElevStation(trk,ep) ); + ParamLoadControl( &elevationPG, I_STATION ); + ParamControlActive( &elevationPG, I_STATION, TRUE ); + break; + case ELEV_IGNORE: + radio = 6; + break; + default: + radio = 0; + } + elevModeV = radio; + ParamLoadControl( &elevationPG, I_MODE ); +#ifdef OLDELEV +if (oldElevationEvaluation) { + int dir; + ANGLE_T a; + int rc0, rc1; + DIST_T elev0, elev1, dist0, dist1; + a = GetTrkEndAngle( trk, ep ); + dir = ( a > 270 || a < 90 ); + rc0 = FindDefinedElev( trk, ep, dir, FALSE, &elev0, &dist0 ); + rc1 = FindDefinedElev( trk, ep, 1-dir, FALSE, &elev1, &dist1 ); + if ( rc0 == FDE_DEF ) { + sprintf( message, _("Elev = %s"), FormatDistance(elev0) ); + ParamLoadMessage( elev1ElevM, message ); + sprintf( message, _("Dist = %s"), FormatDistance(dist0) ); + ParamLoadMessage( elev1DistM, message ); +#ifdef LATER + if (dist0 > 0.1) + sprintf( message, "%0.1f%%", elev0/dist0 ); + else + sprintf( message, _("Undefined") ); + ParamLoadMessage( elev1GradeM, message ); +#endif + } else { + ParamLoadMessage( elev1ElevM, "" ); + ParamLoadMessage( elev1DistM, "" ); + /*ParamLoadMessage( elev1GradeM, "" );*/ + } + if ( rc1 == FDE_DEF ) { + sprintf( message, _("Elev = %s"), FormatDistance(elev1) ); + ParamLoadMessage( elev2ElevM, message ); + sprintf( message, _("Dist = %s"), FormatDistance(dist1) ); + ParamLoadMessage( elev2DistM, message ); +#ifdef LATER + if (dist1 > 0.1) + sprintf( message, "%0.1f%%", elev1/dist1 ); + else + sprintf( message, _("Undefined") ); + ParamLoadMessage( elev2GradeM, message ); +#endif + } else { + ParamLoadMessage( elev2ElevM, "" ); + ParamLoadMessage( elev2DistM, "" ); + /*ParamLoadMessage( elev2GradeM, "" );*/ + } + computedOk = TRUE; + if (rc0 == FDE_DEF && rc1 == FDE_DEF) { + grade = (elev1-elev0)/(dist0+dist1); + elevX = elev0 + grade*dist0; + } else if (rc0 == FDE_DEF && rc1 == FDE_END) { + grade = 0.0; + elevX = elev0; + } else if (rc0 == FDE_END && rc1 == FDE_DEF) { + elevX = elev1; + grade = 0.0; + } else { + gradeOk = FALSE; + computedOk = FALSE; + } +} else { +#endif + gradeOk = ComputeElev( trk, ep, FALSE, &elevX, &grade ); + computedOk = TRUE; +#ifdef OLDELEV +} +#endif + if (oldElevationEvaluation || computedOk) { + sprintf( message, "%0.2f%s", PutDim( elevX ), (units==UNITS_METRIC?"cm":"\"") ); + ParamLoadMessage( &elevationPG, I_COMPUTED, message ); + if (gradeOk) { + sprintf( message, "%0.1f%%", fabs(grade*100) ); + } else { + if ( EndPtIsDefinedElev(trk,ep) ) { + elev = GetElevation(trk); + dist = GetTrkLength(trk,ep,-1); + if (dist>0.1) + sprintf( message, "%0.1f%%", fabs((elev-elevX)/dist)*100.0 ); + else + sprintf( message, _("Undefined") ); + if ( (trk1=GetTrkEndTrk(trk,ep)) && (ep1=GetEndPtConnectedToMe(trk1,trk))>=0 ) { + elev = GetElevation(trk1); + dist = GetTrkLength(trk1,ep1,-1); + if (dist>0.1) + sprintf( message+strlen(message), " - %0.1f%%", fabs((elev-elevX)/dist)*100.0 ); + else + sprintf( message+strlen(message), " - %s", _("Undefined") ); + } + } else { + strcpy( message, _("Undefined") ); + } + } + ParamLoadMessage( &elevationPG, I_GRADE, message ); + if ( (mode&ELEV_MASK)!=ELEV_DEF ) { + elevHeightV = elevX; + ParamLoadControl( &elevationPG, I_HEIGHT ); + } + } + HilightSelectedEndPt(TRUE, elevTrk, elevEp); +} + + +static STATUS_T CmdElevation( wAction_t action, coOrd pos ) +{ + track_p trk0, trk1; + EPINX_T ep0; + int oldTrackCount; + + switch (action) { + case C_START: + if ( elevW == NULL ) + elevW = ParamCreateDialog( &elevationPG, MakeWindowTitle(_("Elevation")), _("Done"), DoElevDone, NULL, TRUE, LayoutElevW, 0, DoElevUpdate ); + elevModeV = 0; + elevHeightV = 0.0; + elevStationV[0] = 0; + ParamLoadControls( &elevationPG ); + ParamGroupRecord( &elevationPG ); + wShow( elevW ); + ParamControlActive( &elevationPG, I_MODE, FALSE ); + ParamControlActive( &elevationPG, I_HEIGHT, FALSE ); + ParamControlActive( &elevationPG, I_STATION, FALSE ); + ParamLoadMessage( &elevationPG, I_COMPUTED, "" ); + ParamLoadMessage( &elevationPG, I_GRADE, "" ); + InfoMessage( _("Select End-Point") ); + HilightElevations( TRUE ); + elevTrk = NULL; + elevUndo = FALSE; + return C_CONTINUE; + case C_RDOWN: + case C_RMOVE: + case C_RUP: + CmdMoveDescription( action-C_RDOWN+C_DOWN, pos ); + return C_CONTINUE; + case C_LCLICK: + if ((trk0 = OnTrack( &pos, TRUE, TRUE )) == NULL) { + return C_CONTINUE; + } + if ( (MyGetKeyState()&WKEY_SHIFT) ) { + ep0 = PickEndPoint( pos, trk0 ); + UndoStart( _("Split Track"), "SplitTrack( T%d[%d] )", GetTrkIndex(trk0), ep0 ); + oldTrackCount = trackCount; + if (!SplitTrack( trk0, pos, ep0, &trk1, FALSE )) + return C_CONTINUE; + ElevSelect( trk0, ep0 ); + UndoEnd(); + elevUndo = FALSE; + } else { + ep0 = PickEndPoint( pos, trk0 ); + ElevSelect( trk0, ep0 ); + return C_CONTINUE; + } + return C_CONTINUE; + case C_OK: + DoElevDone(NULL); + return C_TERMINATE; + case C_CANCEL: + HilightElevations( FALSE ); + HilightSelectedEndPt( FALSE, elevTrk, elevEp ); + elevTrk = NULL; + wHide( elevW ); + return C_TERMINATE; + case C_REDRAW: + DoElevHilight( NULL ); + HilightSelectedEndPt( TRUE, elevTrk, elevEp ); + return C_CONTINUE; + } + return C_CONTINUE; +} + + + + +#include "bitmaps/elev.xpm" + +EXPORT void InitCmdElevation( wMenu_p menu ) +{ + ParamRegister( &elevationPG ); + AddMenuButton( menu, CmdElevation, "cmdElevation", _("Elevation"), wIconCreatePixMap(elev_xpm), LEVEL0_50, IC_POPUP|IC_LCLICK, ACCL_ELEVATION, NULL ); +} + diff --git a/app/bin/cgroup.c b/app/bin/cgroup.c new file mode 100644 index 0000000..76b15ca --- /dev/null +++ b/app/bin/cgroup.c @@ -0,0 +1,1598 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cgroup.c,v 1.2 2008-01-20 23:29:15 mni77 Exp $ + * + * Compound tracks: Group + * + */ + +/* 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 <ctype.h> +#include "track.h" +#include "compound.h" +#include "shrtpath.h" +#include "i18n.h" + +/***************************************************************************** + * + * Ungroup / Group + * + */ + +static int log_group=-1; + +static dynArr_t pathPtr_da; +#define pathPtr(N) DYNARR_N( char, pathPtr_da, N ) + +static char groupManuf[STR_SIZE]; +static char groupDesc[STR_SIZE]; +static char groupPartno[STR_SIZE]; +static char groupTitle[STR_SIZE]; +static int groupCompoundCount = 0; + +typedef struct { + int segInx; + EPINX_T segEP; + int inx; + track_p trk; + } mergePt_t; +static dynArr_t mergePt_da; +#define mergePt(N) DYNARR_N( mergePt_t, mergePt_da, N ) +static void AddMergePt( + int segInx, + EPINX_T segEP ) +{ + int inx; + mergePt_t * mp; + for ( inx=0; inx<mergePt_da.cnt; inx++ ) { + mp = &mergePt(inx); + if ( mp->segInx == segInx && + mp->segEP == segEP ) + return; + } + DYNARR_APPEND( mergePt_t, mergePt_da, 10 ); + mp = &mergePt(mergePt_da.cnt-1); + mp->segInx = segInx; + mp->segEP = segEP; + mp->inx = mergePt_da.cnt-1; +LOG( log_group, 2, ( " MergePt: %d.%d\n", segInx, segEP ) ); +} + + +static EPINX_T FindEP( + EPINX_T epCnt, + trkEndPt_p endPts, + coOrd pos ) +{ + DIST_T dist; + EPINX_T ep; + for ( ep=0; ep<epCnt; ep++ ) { + dist = FindDistance( pos, endPts[ep].pos ); + if ( dist < connectDistance ) + return ep; + } + return -1; +} + + +static void SegOnMP( + int segInx, + int mpInx, + int segCnt, + int * map ) +{ + int inx; + mergePt_t * mp; + if ( map[segInx] < 0 ) { +LOG( log_group, 2, ( " S%d: on MP%d\n", segInx, mpInx ) ); + map[segInx] = mpInx; + return; + } +LOG( log_group, 2, ( " S%d: remapping MP%d to MP%d\n", segInx, mpInx, map[segInx] ) ); + for ( inx=0; inx<segCnt; inx++ ) + if ( map[inx] == mpInx ) + map[inx] = map[segInx]; + for ( inx=0; inx<mergePt_da.cnt; inx++ ) { + if ( inx == map[segInx] ) + continue; + mp = &mergePt(inx); + if ( mp->inx == mpInx ) + mp->inx = map[segInx]; + } +} + + +static void GroupCopyTitle( + char * title ) +{ + char *mP, *nP, *pP; + int mL, nL, pL; + + ParseCompoundTitle( title, &mP, &mL, &nP, &nL, &pP, &pL ); + if ( strncmp( nP, "Ungrouped ", 10 ) == 0 ) { + nP += 10; + nL -= 10; + } + if ( ++groupCompoundCount == 1 ) { + strncpy( groupManuf, mP, mL ); + groupManuf[mL] = '\0'; + strncpy( groupDesc, nP, nL ); + groupDesc[nL] = '\0'; + strncpy( groupPartno, pP, pL ); + groupPartno[pL] = '\0'; + } else { + if ( mL != (int)strlen( groupManuf ) || + strncmp( groupManuf, mP, mL ) != 0 ) + groupManuf[0] = '\0'; + if ( nL != (int)strlen( groupDesc ) || + strncmp( groupDesc, nP, nL ) != 0 ) + groupDesc[0] = '\0'; + if ( pL != (int)strlen( groupPartno ) || + strncmp( groupPartno, pP, pL ) != 0 ) + groupPartno[0] = '\0'; + } +} + + +EXPORT void UngroupCompound( + track_p trk ) +{ + struct extraData *xx = GetTrkExtraData(trk); + struct extraData *xx1; + trkSeg_p sp; + track_p trk0, trk1; + int segCnt, segInx, segInx1; + EPINX_T ep, epCnt, epCnt1=0, segEP, segEP1, eps[2]; + char * cp; + coOrd pos, orig, size; + ANGLE_T angle; + int inx; + int off; + mergePt_t * mp; + trkEndPt_p epp; + segProcData_t segProcData; + static dynArr_t refCount_da; +#define refCount(N) DYNARR_N( int, refCount_da, N ) + typedef struct { + track_p trk; + EPINX_T ep[2]; + } segTrack_t; +#define segTrack(N) DYNARR_N( segTrack_t, segTrack_da, N ) + static dynArr_t segTrack_da; + segTrack_t * stp, * stp1; + BOOL_T turnoutChanged; + + DYNARR_RESET( mergePt_t, mergePt_da ); + DYNARR_RESET( int, refCount_da ); + DYNARR_RESET( segTrack_t, segTrack_da ); + GroupCopyTitle( xtitle(xx) ); + +#ifdef LATER + for ( sp=sq=xx->segs; sp<&xx->segs[xx->segCnt]; sp++ ) { + if ( IsSegTrack(sp) ) { + *sq = *sp; + sq++; + } else { + trk1 = MakeDrawFromSeg( xx->orig, xx->angle, sp ); + if ( trk1 ) { + SetTrkBits( trk1, TB_SELECTED ); + DrawNewTrack( trk1 ); + } + } + } + if ( GetTrkEndPtCnt(trk) <= 0 ) { + UndoDelete( trk ); + return; + } +#endif + +LOG( log_group, 1, ( "Ungroup( T%d )\n", GetTrkIndex(trk) ) ); + epCnt = GetTrkEndPtCnt(trk); + for ( segCnt=0; segCnt<xx->segCnt&&IsSegTrack(&xx->segs[segCnt]); segCnt++ ); + ASSERT( (epCnt==0) == (segCnt==0) ); + turnoutChanged = FALSE; + if ( epCnt > 0 ) { + turnoutChanged = TRUE; + + /* 1: collect EPs + */ + DYNARR_SET( trkEndPt_t, tempEndPts_da, epCnt ); + DYNARR_SET( segTrack_t, segTrack_da, segCnt ); + memset( segTrack_da.ptr, 0, segCnt * sizeof segTrack(0) ); + for ( ep=0; ep<epCnt; ep++ ) { + epp = &tempEndPts(ep); + epp->pos = GetTrkEndPos( trk, ep ); + epp->angle = GetTrkEndAngle( trk, ep ); + Rotate( &epp->pos, xx->orig, -xx->angle ); + epp->pos.x -= xx->orig.x; + epp->pos.y -= xx->orig.y; + epp->track = GetTrkEndTrk( trk, ep ); + if ( epp->track ) + epp->index = GetEndPtConnectedToMe( epp->track, trk ); + else + epp->index = -1; +LOG( log_group, 1, ( " EP%d = [%0.3f %0.3f] A%0.3f T%d.%d\n", ep, epp->pos.x, epp->pos.y, epp->angle, epp->track?GetTrkIndex(epp->track):-1, epp->track?epp->index:-1 ) ); + } + + /* 3: Count number of times each segment is referenced + * If the refcount differs between adjacent segments + * add segment with smaller count to mergePts + * Treat EndPts as a phantom segment with inx above segCnt + * Path ends that don't map onto a real EndPt (bumpers) get a fake EP + */ + DYNARR_SET( int, refCount_da, segCnt+epCnt ); + memset( refCount_da.ptr, 0, refCount_da.cnt * sizeof *(int*)0 ); + cp = (char *)xx->paths; + while ( cp[0] ) { + cp += strlen(cp)+1; + while ( cp[0] ) { + GetSegInxEP( cp[0], &segInx, &segEP ); + pos = GetSegEndPt( xx->segs+segInx, segEP, FALSE, NULL ); + segInx1 = FindEP( tempEndPts_da.cnt, &tempEndPts(0), pos ); + if ( segInx1 >= 0 ) { + segInx1 += segCnt; + refCount(segInx1)++; + } else { + DYNARR_APPEND( trkEndPt_t, tempEndPts_da, 10 ); + DYNARR_APPEND( int, refCount_da, 10 ); + epp = &tempEndPts(tempEndPts_da.cnt-1); + epp->pos = pos; + epp->angle = 0; + segInx1 = refCount_da.cnt-1; + refCount(segInx1) = 2; + } + segEP1 = 0; + while ( cp[0] ) { + GetSegInxEP( cp[0], &segInx, &segEP ); + refCount(segInx)++; + if ( refCount(segInx) > refCount(segInx1) ) + AddMergePt( segInx, segEP ); + if ( refCount(segInx1) > refCount(segInx) ) + AddMergePt( segInx1, segEP1 ); + segInx1 = segInx; + segEP1 = 1-segEP; + cp++; + } + GetSegInxEP( cp[-1], &segInx, &segEP ); + pos = GetSegEndPt( xx->segs+segInx, 1-segEP, FALSE, NULL ); + segInx = FindEP( tempEndPts_da.cnt, &tempEndPts(0), pos ); + if ( segInx >= 0 ) { + segInx += segCnt; + refCount(segInx)++; + } else { + DYNARR_APPEND( trkEndPt_t, tempEndPts_da, 10 ); + DYNARR_APPEND( int, refCount_da, 10 ); + epp = &tempEndPts(tempEndPts_da.cnt-1); + epp->pos = pos; + epp->angle = 0; + segInx = refCount_da.cnt-1; + refCount(segInx) = 2; + } + if ( refCount(segInx) > refCount(segInx1) ) { + AddMergePt( segInx, 0 ); + } + cp++; + } + cp++; + } + epCnt1 = tempEndPts_da.cnt; + + /* 4: For each path element, map segment to a mergePt if the adjacent segment + * and EP is a mergePt + * If segment is already mapped then merge mergePts + */ + DYNARR_SET( int, refCount_da, segCnt ); + memset( refCount_da.ptr, -1, segCnt * sizeof *(int*)0 ); + cp = (char *)xx->paths; + while ( cp[0] ) { + cp += strlen(cp)+1; + while ( cp[0] ) { + GetSegInxEP( cp[0], &segInx, &segEP ); + pos = GetSegEndPt( xx->segs+segInx, segEP, FALSE, NULL ); + /*REORIGIN1( pos, xx->angle, xx->orig );*/ + segInx1 = FindEP( tempEndPts_da.cnt, &tempEndPts(0), pos ); + if ( segInx1 >= 0 ) { + segInx1 += segCnt; + } + segEP1 = 0; + while ( cp[0] ) { + GetSegInxEP( cp[0], &segInx, &segEP ); + if ( segInx1 >= 0 ) { + for ( inx=0; inx<mergePt_da.cnt; inx++ ) { + mp = &mergePt(inx); + if ( mp->segInx == segInx1 && mp->segEP == segEP1 ) { + SegOnMP( segInx, mp->inx, segCnt, &refCount(0) ); + } + if ( mp->segInx == segInx && mp->segEP == segEP ) { + SegOnMP( segInx1, mp->inx, segCnt, &refCount(0) ); + } + } + } + segInx1 = segInx; + segEP1 = 1-segEP; + cp++; + } + GetSegInxEP( cp[-1], &segInx, &segEP ); + pos = GetSegEndPt( xx->segs+segInx, 1-segEP, FALSE, NULL ); + /*REORIGIN1( pos, xx->angle, xx->orig );*/ + segInx = FindEP( tempEndPts_da.cnt, &tempEndPts(0), pos ); + if ( segInx >= 0 ) { + segInx += segCnt; + for ( inx=0; inx<mergePt_da.cnt; inx++ ) { + mp = &mergePt(inx); + if ( mp->segInx == segInx && mp->segEP == 0 ) { + SegOnMP( segInx1, mp->inx, segCnt, &refCount(0) ); + } + } + } + cp++; + } + cp++; + } + + /* 5: Check is all segments are on the same mergePt, which means there is nothing to do + */ + if ( mergePt_da.cnt > 0 ) { + for ( segInx=0; segInx<segCnt; segInx++ ) + if ( refCount(segInx) != mergePt(0).inx ) + break; + if ( segInx == segCnt ) { + /* all segments on same turnout, nothing we can do here */ + turnoutChanged = FALSE; + if ( segCnt == xx->segCnt ) { + /* no non-track segments to remove */ + return; + } + } + } + } + + /* 6: disconnect, undraw, remove non-track segs, return if there is nothing else to do + */ + wDrawDelayUpdate( mainD.d, TRUE ); + if ( turnoutChanged ) { + for ( ep=0; ep<epCnt; ep++ ) { + epp = &tempEndPts(ep); + if ( epp->track ) { + DrawEndPt( &mainD, epp->track, epp->index, wDrawColorWhite ); + DrawEndPt( &mainD, trk, ep, wDrawColorWhite ); + DisconnectTracks( trk, ep, epp->track, epp->index ); + } + } + } + UndrawNewTrack(trk); + for ( sp=xx->segs; sp<&xx->segs[xx->segCnt]; sp++ ) { + if ( ! IsSegTrack(sp) ) { + trk1 = MakeDrawFromSeg( xx->orig, xx->angle, sp ); + if ( trk1 ) { + SetTrkBits( trk1, TB_SELECTED ); + DrawNewTrack( trk1 ); + } + } + } + if ( !turnoutChanged ) { + if ( epCnt <= 0 ) { + trackCount--; + UndoDelete( trk ); + } else { + UndoModify( trk ); + xx->segCnt = segCnt; + DrawNewTrack( trk ); + } + wDrawDelayUpdate( mainD.d, FALSE ); + return; + } + + /* 7: for each valid mergePt, create a new turnout + */ + for ( inx=0; inx<mergePt_da.cnt; inx++ ) { + mp = &mergePt(inx); + if ( mp->inx != inx ) + continue; + DYNARR_RESET( trkSeg_t, tempSegs_da ); + DYNARR_SET( trkEndPt_t, tempEndPts_da, epCnt1 ); + DYNARR_RESET( char, pathPtr_da ); + for ( segInx=0; segInx<segCnt; segInx++ ) { + if ( refCount(segInx) == inx ) { + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + tempSegs(tempSegs_da.cnt-1) = xx->segs[segInx]; + sprintf( message, "P%d", segInx ); + off = pathPtr_da.cnt; + DYNARR_SET( char, pathPtr_da, off+(int)strlen(message)+4 ); + strcpy( &pathPtr(off), message ); + off = pathPtr_da.cnt-3; + pathPtr(off+0) = (char)tempSegs_da.cnt; + pathPtr(off+1) = '\0'; + pathPtr(off+2) = '\0'; + for ( ep=0; ep<2; ep++ ) { + pos = GetSegEndPt( xx->segs+segInx, ep, FALSE, &angle ); + segEP = FindEP( epCnt1, &tempEndPts(0), pos ); + if ( segEP >= 0 && segEP >= epCnt && segEP < epCnt1 ) { + /* was a bumper: no EP */ + eps[ep] = -1; + continue; + } + REORIGIN1( pos, xx->angle, xx->orig ); + angle = NormalizeAngle( xx->angle+angle ); + eps[ep] = FindEP( tempEndPts_da.cnt-epCnt1, &tempEndPts(epCnt1), pos ); + if ( eps[ep] < 0 ) { + DYNARR_APPEND( trkEndPt_t, tempEndPts_da, 10 ); + eps[ep] = tempEndPts_da.cnt-1-epCnt1; + epp = &tempEndPts(tempEndPts_da.cnt-1); + memset( epp, 0, sizeof *epp ); + epp->pos = pos; + epp->angle = angle; + } + } + segTrack(segInx).ep[0] = eps[0]; + segTrack(segInx).ep[1] = eps[1]; + } + } + DYNARR_SET( char, pathPtr_da, pathPtr_da.cnt+1 ); + pathPtr(pathPtr_da.cnt-1) = '\0'; + if ( tempSegs_da.cnt == 0 ) { + AbortProg( "tempSegs_da.cnt == 0" ); + continue; + } + GetSegBounds( zero, 0, tempSegs_da.cnt, &tempSegs(0), &orig, &size ); + orig.x = -orig.x; + orig.y = -orig.y; + MoveSegs( tempSegs_da.cnt, &tempSegs(0), orig ); + Rotate( &orig, zero, xx->angle ); + orig.x = xx->orig.x - orig.x; + orig.y = xx->orig.y - orig.y; + trk1 = NewCompound( T_TURNOUT, 0, orig, xx->angle, xx->title, tempEndPts_da.cnt-epCnt1, &tempEndPts(epCnt1), pathPtr_da.cnt, &pathPtr(0), tempSegs_da.cnt, &tempSegs(0) ); + xx1 = GetTrkExtraData(trk1); + xx1->ungrouped = TRUE; + + SetTrkVisible( trk1, TRUE ); + SetTrkBits( trk1, TB_SELECTED ); + for ( segInx=0; segInx<segCnt; segInx++ ) { + if ( refCount(segInx) == inx ) { + segTrack(segInx).trk = trk1; + } + } + mp->trk = trk1; + } + + /* 8: for remaining segments, create simple tracks + */ + for ( segInx=0; segInx<segCnt; segInx++ ) { + if ( refCount(segInx) >= 0 ) continue; + SegProc( SEGPROC_NEWTRACK, xx->segs+segInx, &segProcData ); + SetTrkScale( segProcData.newTrack.trk, GetTrkScale(trk) ); + SetTrkBits( segProcData.newTrack.trk, TB_SELECTED ); + MoveTrack( segProcData.newTrack.trk, xx->orig ); + RotateTrack( segProcData.newTrack.trk, xx->orig, xx->angle ); + segTrack(segInx).trk = segProcData.newTrack.trk; + segTrack(segInx).ep[0] = segProcData.newTrack.ep[0]; + segTrack(segInx).ep[1] = segProcData.newTrack.ep[1]; + } + + /* 9: reconnect tracks + */ + cp = (char *)xx->paths; + while ( cp[0] ) { + cp += strlen(cp)+1; + while ( cp[0] ) { + /* joint EP to this segment */ + GetSegInxEP( cp[0], &segInx, &segEP ); + stp = &segTrack(segInx); + ep = FindEP( epCnt, &tempEndPts(0), GetSegEndPt( xx->segs+segInx, segEP, FALSE, NULL ) ); + if ( ep >= 0 ) { + epp = &tempEndPts(ep); + if ( epp->track ) { + ConnectTracks( stp->trk, stp->ep[segEP], epp->track, epp->index ); + DrawEndPt( &mainD, epp->track, epp->index, GetTrkColor(epp->track,&mainD) ); + epp->track = NULL; + } + } + stp1 = stp; + segEP1 = 1-segEP; + cp++; + while ( cp[0] ) { + GetSegInxEP( cp[0], &segInx, &segEP ); + stp = &segTrack(segInx); + trk0 = GetTrkEndTrk( stp->trk, stp->ep[segEP] ); + trk1 = GetTrkEndTrk( stp1->trk, stp1->ep[segEP1] ); + if ( trk0 == NULL ) { + if ( trk1 != NULL ) + AbortProg( "ungroup: seg half connected" ); + ConnectTracks( stp->trk, stp->ep[segEP], stp1->trk, stp1->ep[segEP1] ); + } else { + if ( trk1 != stp->trk || stp1->trk != trk0 ) + AbortProg( "ungroup: last seg not connected to curr" ); + } + stp1 = stp; + segEP1 = 1-segEP; + cp++; + } + /* joint EP to last segment */ + ep = FindEP( epCnt, &tempEndPts(0), GetSegEndPt( xx->segs+segInx, segEP1, FALSE, NULL ) ); + if ( ep > 0 ) { + epp = &tempEndPts(ep); + if ( epp->track ) { + ConnectTracks( stp1->trk, stp1->ep[segEP1], epp->track, epp->index ); + DrawEndPt( &mainD, epp->track, epp->index, wDrawColorWhite ); + epp->track = NULL; + } + } + cp++; + } + cp++; + } + + /* 10: cleanup: delete old track, draw new tracks + */ + UndoDelete( trk ); + trackCount--; + for ( segInx=0; segInx<segCnt; segInx++ ) { + if ( refCount(segInx) >= 0 ) { + mp = &mergePt( refCount(segInx) ); + if ( mp->trk ) { + DrawNewTrack( mp->trk ); + mp->trk = NULL; + } + } else { + DrawNewTrack( segTrack(segInx).trk ); + } + } + wDrawDelayUpdate( mainD.d, FALSE ); +} + + + + +EXPORT void DoUngroup( void ) +{ + track_p trk = NULL; + int ungroupCnt; + int oldTrackCount; + TRKINX_T lastTrackIndex; + + if ( log_group < 0 ) + log_group = LogFindIndex( "group" ); + groupManuf[0] = 0; + groupDesc[0] = 0; + groupPartno[0] = 0; + ungroupCnt = 0; + oldTrackCount = trackCount; + UndoStart( _("Ungroup Object"), "Ungroup Objects" ); + lastTrackIndex = max_index; + groupCompoundCount = 0; + while ( TrackIterate( &trk ) ) { + if ( GetTrkSelected( trk ) && GetTrkIndex(trk) <= lastTrackIndex ) { + oldTrackCount = trackCount; + UngroupTrack( trk ); + if ( oldTrackCount != trackCount ) + ungroupCnt++; + } + } + if ( ungroupCnt ) + InfoMessage( _("%d objects ungrouped"), ungroupCnt ); + else + InfoMessage( _("No objects ungrouped") ); +} + + + +static drawCmd_t groupD = { + NULL, &tempSegDrawFuncs, DC_GROUP, 1, 0.0, {0.0, 0.0}, {0.0, 0.0}, Pix2CoOrd, CoOrd2Pix }; +static long groupSegCnt; +static long groupReplace; +char * groupReplaceLabels[] = { N_("Replace with new group?"), NULL }; + +static wWin_p groupW; +static paramIntegerRange_t r0_999999 = { 0, 999999 }; +static paramData_t groupPLs[] = { +/*0*/ { PD_STRING, groupManuf, "manuf", PDO_NOPREF, (void*)350, N_("Manufacturer") }, +/*1*/ { PD_STRING, groupDesc, "desc", PDO_NOPREF, (void*)230, N_("Description") }, +/*2*/ { PD_STRING, groupPartno, "partno", PDO_NOPREF|PDO_DLGHORZ|PDO_DLGIGNORELABELWIDTH, (void*)100, N_("#") }, +/*3*/ { PD_LONG, &groupSegCnt, "segcnt", PDO_NOPREF, &r0_999999, N_("# Segments"), BO_READONLY }, +/*4*/ { PD_TOGGLE, &groupReplace, "replace", 0, groupReplaceLabels, "", BC_HORZ|BC_NOBORDER } }; +static paramGroup_t groupPG = { "group", 0, groupPLs, sizeof groupPLs/sizeof groupPLs[0] }; + + +typedef struct { + track_p trk; + int segStart; + int segEnd; + } groupTrk_t, * groupTrk_p; +static dynArr_t groupTrk_da; +#define groupTrk(N) DYNARR_N( groupTrk_t, groupTrk_da, N ) +typedef struct { + int groupInx; + EPINX_T ep1, ep2; + PATHPTR_T path; + BOOL_T flip; + } pathElem_t, *pathElem_p; +typedef struct { + int pathElemStart; + int pathElemEnd; + EPINX_T ep1, ep2; + int conflicts; + BOOL_T inGroup; + BOOL_T done; + } path_t, *path_p; +static dynArr_t path_da; +#define path(N) DYNARR_N( path_t, path_da, N ) +static dynArr_t pathElem_da; +#define pathElem(N) DYNARR_N( pathElem_t, pathElem_da, N ) +static int pathElemStart; + + +static BOOL_T CheckTurnoutEndPoint( + trkSeg_p segs, + coOrd pos, + int end ) +{ + coOrd pos1; + DIST_T d; + pos1 = GetSegEndPt( segs, end, FALSE, NULL ); + d = FindDistance( pos, pos1 ); + return ( d < connectDistance ); +} + +static char * FindPathBtwEP( + track_p trk, + EPINX_T ep1, + EPINX_T ep2, + BOOL_T * flip ) +{ + struct extraData * xx = GetTrkExtraData( trk ); + char * cp, *cp0; + int epN; + coOrd pos1, pos2; + int segInx; + EPINX_T segEP; + + if ( GetTrkType(trk) != T_TURNOUT ) { + if ( ep1+ep2 != 1 ) + AbortProg( "findPathBtwEP" ); + *flip = ( ep1 == 1 ); + return "\1\0\0"; + } + cp = (char *)xx->paths; + pos1 = GetTrkEndPos(trk,ep1); + Rotate( &pos1, xx->orig, -xx->angle ); + pos1.x -= xx->orig.x; + pos1.y -= xx->orig.y; + pos2 = GetTrkEndPos(trk,ep2); + Rotate( &pos2, xx->orig, -xx->angle ); + pos2.x -= xx->orig.x; + pos2.y -= xx->orig.y; + while ( cp[0] ) { + cp += strlen(cp)+1; + while ( cp[0] ) { + cp0 = cp; + epN = -1; + GetSegInxEP( cp[0], &segInx, &segEP ); + if ( CheckTurnoutEndPoint( &xx->segs[segInx], pos1, segEP ) ) + epN = 1; + else if ( CheckTurnoutEndPoint( &xx->segs[segInx], pos2, segEP ) ) + epN = 0; + cp += strlen(cp); + if ( epN != -1 ) { + GetSegInxEP( cp[-1], &segInx, &segEP ); + if ( CheckTurnoutEndPoint( &xx->segs[segInx], epN==0?pos1:pos2, 1-segEP ) ) { + *flip = epN==0; + return cp0; + } + } + cp++; + } + cp++; + } + return NULL; +} + + +static int GroupShortestPathFunc( + SPTF_CMD cmd, + track_p trk, + EPINX_T ep1, + EPINX_T ep2, + DIST_T dist, + void * data ) +{ + track_p trk1; + path_t *pp; + pathElem_t *ppp; + BOOL_T flip; + int inx; + EPINX_T ep; + coOrd pos1, pos2; + ANGLE_T angle, ang1, ang2; + + switch ( cmd ) { + case SPTC_MATCH: + if ( !GetTrkSelected(trk) ) + return 0; + trk1 = GetTrkEndTrk(trk,ep1); + if ( trk1 == NULL ) + return 1; + if ( !GetTrkSelected(trk1) ) + return 1; + return 0; + + case SPTC_MATCHANY: + return -1; + + case SPTC_ADD_TRK: +if (log_shortPath<=0||logTable(log_shortPath).level<4) LOG( log_group, 2, ( " T%d[%d]\n", GetTrkIndex(trk), ep2 ) ) + DYNARR_APPEND( pathElem_t, pathElem_da, 10 ); + ppp = &pathElem(pathElem_da.cnt-1); + for ( inx=0; inx<groupTrk_da.cnt; inx++ ) { + if ( groupTrk(inx).trk == trk ) { + ppp->groupInx = inx; + ppp->ep1 = ep1; + ppp->ep2 = ep2; + ppp->path = (PATHPTR_T)FindPathBtwEP( trk, ep1, ep2, &ppp->flip ); + return 0; + } + } + AbortProg( "GroupShortestPathFunc(SPTC_ADD_TRK, T%d) - track not in group", GetTrkIndex(trk) ); + + case SPTC_TERMINATE: + ppp = &pathElem(pathElemStart); + trk = groupTrk(ppp->groupInx).trk; + pos1 = GetTrkEndPos( trk, ppp->ep2 ); + ang1 = GetTrkEndAngle( trk, ppp->ep2 ); + ppp = &pathElem(pathElem_da.cnt-1); + trk = groupTrk(ppp->groupInx).trk; + pos2 = GetTrkEndPos( trk, ppp->ep1 ); + ang2 = GetTrkEndAngle( trk, ppp->ep1 ); + ep1 = ep2 = -1; + for ( ep=0; ep<tempEndPts_da.cnt; ep++ ) { + if ( ep1 < 0 ) { + dist = FindDistance( pos1, tempEndPts(ep).pos ); + angle = NormalizeAngle( ang1 - tempEndPts(ep).angle + connectAngle/2.0 ); + if ( dist < connectDistance && angle < connectAngle ) + ep1 = ep; + } + if ( ep2 < 0 ) { + dist = FindDistance( pos2, tempEndPts(ep).pos ); + angle = NormalizeAngle( ang2 - tempEndPts(ep).angle + connectAngle/2.0 ); + if ( dist < connectDistance && angle < connectAngle ) + ep2 = ep; + } + } + if ( ep1<0 || ep2<0 ) { +LOG( log_group, 2, ( " Remove: ep not found\n" ) ) + pathElem_da.cnt = pathElemStart; + return 0; + } + for ( inx=0; inx<path_da.cnt; inx++ ) { + pp = &path(inx); + if ( ( ep1 < 0 || ( pp->ep1 == ep1 || pp->ep2 == ep1 ) ) && + ( ep2 < 0 || ( pp->ep1 == ep2 || pp->ep2 == ep2 ) ) ) { +LOG( log_group, 2, ( " Remove: duplicate path P%d\n", inx ) ) + pathElem_da.cnt = pathElemStart; + return 0; + } + } + DYNARR_APPEND( path_t, path_da, 10 ); + pp = &path(path_da.cnt-1); + memset( pp, 0, sizeof *pp ); + pp->pathElemStart = pathElemStart; + pp->pathElemEnd = pathElem_da.cnt-1; + pp->ep1 = ep1; + pp->ep2 = ep2; + pathElemStart = pathElem_da.cnt; +LOG( log_group, 2, ( " Keep\n" ) ) + return 0; + + case SPTC_IGNNXTTRK: + if ( !GetTrkSelected(trk) ) + return 1; + if ( ep1 == ep2 ) + return 1; + if ( GetTrkEndPtCnt(trk) == 2 ) + return 0; + if ( GetTrkType(trk) != T_TURNOUT ) + AbortProg( "GroupShortestPathFunc(IGNNXTTRK,T%d:%d,%d)", GetTrkIndex(trk), ep1, ep2 ); + return FindPathBtwEP( trk, ep2, ep1, &flip ) == NULL; + + case SPTC_VALID: + return 1; + + } + return 0; +} + + +static int CmpGroupOrder( + const void * ptr1, + const void * ptr2 ) +{ + int inx1 = *(int*)ptr1; + int inx2 = *(int*)ptr2; + return path(inx1).conflicts-path(inx2).conflicts; +} + +static coOrd endPtOrig; +static ANGLE_T endPtAngle; +static int CmpEndPtAngle( + const void * ptr1, + const void * ptr2 ) +{ + ANGLE_T angle; + trkEndPt_p epp1 = (trkEndPt_p)ptr1; + trkEndPt_p epp2 = (trkEndPt_p)ptr2; + + angle = NormalizeAngle(FindAngle(endPtOrig,epp1->pos)-endPtAngle) - NormalizeAngle(FindAngle(endPtOrig,epp2->pos)-endPtAngle); + return (int)angle; +} + + +static int ConflictPaths( + path_p path0, + path_p path1 ) +{ + /* do these paths share an EP? */ + if ( path0->ep1 == path1->ep1 ) return TRUE; + if ( path0->ep1 == path1->ep2 ) return TRUE; + if ( path0->ep2 == path1->ep1 ) return TRUE; + if ( path0->ep2 == path1->ep2 ) return TRUE; + return FALSE; +} + + +static BOOL_T CheckPathEndPt( + track_p trk, + char cc, + EPINX_T ep ) +{ + struct extraData *xx = GetTrkExtraData(trk); + wIndex_t segInx; + EPINX_T segEP, epCnt; + DIST_T d; + coOrd pos; + + GetSegInxEP( cc, &segInx, &segEP ); + if ( ep ) segEP = 1-segEP; + pos = GetSegEndPt( &xx->segs[segInx], segEP, FALSE, NULL ); + REORIGIN1( pos, xx->angle, xx->orig ); + epCnt = GetTrkEndPtCnt(trk); + for ( ep=0; ep<epCnt; ep++ ) { + d = FindDistance( pos, GetTrkEndPos( trk, ep ) ); + if ( d < connectDistance ) + return TRUE; + } + return FALSE; +} + +static BOOL_T CheckForBumper( + track_p trk ) +{ + struct extraData *xx = GetTrkExtraData(trk); + char * cp; + cp = (char *)xx->paths; + while ( cp[0] ) { + cp += strlen(cp)+1; + while ( cp[0] ) { + if ( !CheckPathEndPt( trk, cp[0], 0 ) ) return FALSE; + while ( cp[0] ) + cp++; + if ( !CheckPathEndPt( trk, cp[-1], 1 ) ) return FALSE; + cp++; + } + cp++; + } + return TRUE; +} + + +static void GroupOk( void * junk ) +{ + struct extraData *xx = NULL; + turnoutInfo_t * to; + int inx; + EPINX_T ep, epCnt, epN; + coOrd orig, size; + long oldOptions; + FILE * f = NULL; + BOOL_T rc = TRUE; + track_p trk, trk1; + path_t * pp, *ppN; + pathElem_p ppp; + groupTrk_p groupP; + BOOL_T flip, flip1, allDone; + DIST_T dist; + ANGLE_T angle, angleN; + pathElem_t pathElemTemp; + char * cp=NULL; +#ifdef SEGMAP + pathElem_p ppp1, ppp2; + int segInx1, segInx2; + coOrd pos1, pos2; + static dynArr_t segMap_da; +#define segMap(I,J) DYNARR_N( char, segMap_da, (2*(I)+0)*trackSegs_da.cnt+(J) ) +#define segAcc(I,J) DYNARR_N( char, segMap_da, (2*(I)+1)*trackSegs_da.cnt+(J) ) +#define segSum(I,J) DYNARR_N( char, segMap_da, (2*(groupTrk_da.cnt)+0)*trackSegs_da.cnt+(J) ) +#endif + static dynArr_t trackSegs_da; +#define trackSegs(N) DYNARR_N( trkSeg_t, trackSegs_da, N ) + trkSeg_p segPtr; + int segCnt; + static dynArr_t conflictMap_da; +#define conflictMap( I, J ) DYNARR_N( int, conflictMap_da, (I)*(path_da.cnt)+(J) ) +#define segFlip( N ) DYNARR_N( int, conflictMap_da, (N) ) + static dynArr_t groupOrder_da; +#define groupOrder( N ) DYNARR_N( int, groupOrder_da, N ) + static dynArr_t groupMap_da; +#define groupMap( I, J ) DYNARR_N( int, groupMap_da, (I)*(path_da.cnt+1)+(J) ) + int groupCnt; + int pinx, pinx2, ginx, ginx2, gpinx2; + trkEndPt_p endPtP; + PATHPTR_T path; + int pathLen; + signed char pathChar; + char *oldLocale = NULL; + +#ifdef SEGMAP + DYNARR_RESET( char, segMap_da ); +#endif + DYNARR_RESET( trkSeg_t, trackSegs_da ); + DYNARR_RESET( trkSeg_t, tempSegs_da ); + DYNARR_RESET( groupTrk_t, groupTrk_da ); + DYNARR_RESET( path_t, path_da ); + DYNARR_RESET( pathElem_t, pathElem_da ); + DYNARR_RESET( trkEndPt_t, tempEndPts_da ); + DYNARR_RESET( char, pathPtr_da ); + + ParamUpdate( &groupPG ); + if ( groupManuf[0]==0 || groupDesc[0]==0 || groupPartno[0]==0 ) { + NoticeMessage2( 0, MSG_GROUP_NONBLANK, _("Ok"), NULL ); + return; + } + sprintf( message, "%s\t%s\t%s", groupManuf, groupDesc, groupPartno ); + if ( strcmp( message, groupTitle ) != 0 ) { + if ( FindCompound( FIND_TURNOUT|FIND_STRUCT, curScaleName, message ) ) + if ( !NoticeMessage2( 1, MSG_TODSGN_REPLACE, _("Yes"), _("No") ) ) + return; + strcpy( groupTitle, message ); + } + + wDrawDelayUpdate( mainD.d, TRUE ); + /* + * Collect tracks + */ + trk = NULL; + while ( TrackIterate( &trk ) ) { + if ( GetTrkSelected( trk ) ) { + if ( IsTrack(trk) ) { + DYNARR_APPEND( groupTrk_t, groupTrk_da, 10 ); + groupP = &groupTrk(groupTrk_da.cnt-1); + groupP->trk = trk; + groupP->segStart = trackSegs_da.cnt; + if ( GetTrkType(trk) == T_TURNOUT ) { + xx = GetTrkExtraData(trk); + for ( pinx=0; pinx<xx->segCnt; pinx++ ) { + segPtr = &xx->segs[pinx]; + if ( IsSegTrack(segPtr) ) { + DYNARR_APPEND( trkSeg_t, trackSegs_da, 10 ); + trackSegs(trackSegs_da.cnt-1) = *segPtr; + RotateSegs( 1, &trackSegs(trackSegs_da.cnt-1), zero, xx->angle ); + MoveSegs( 1, &trackSegs(trackSegs_da.cnt-1), xx->orig ); + } else { + DrawSegs( &groupD, xx->orig, xx->angle, segPtr, 1, trackGauge, wDrawColorBlack ); + } + } + } else { + segCnt = tempSegs_da.cnt; + oldOptions = groupD.options; + groupD.options |= (DC_QUICK|DC_SIMPLE|DC_SEGTRACK); + DrawTrack( trk, &groupD, wDrawColorBlack ); + groupD.options = oldOptions; + DYNARR_APPEND( trkSeg_t, trackSegs_da, 10 ); + segPtr = &trackSegs(trackSegs_da.cnt-1); + *segPtr = tempSegs( segCnt ); + if ( tempSegs_da.cnt != segCnt+1 || + !IsSegTrack(segPtr) ) { + NoticeMessage2( 0, MSG_CANNOT_GROUP_TRACK, _("Ok"), NULL ); + wHide( groupW ); + return; + } + tempSegs_da.cnt = segCnt; + } + groupP->segEnd = trackSegs_da.cnt-1; + } else { + DrawTrack( trk, &groupD, wDrawColorBlack ); + } + } + } + + if ( groupTrk_da.cnt>0 ) { + if ( groupTrk_da.cnt > 128 ) { + NoticeMessage( MSG_TOOMANYSEGSINGROUP, _("Ok"), NULL ); + wDrawDelayUpdate( mainD.d, FALSE ); + wHide( groupW ); + return; + } + + /* + * Collect EndPts and find paths + */ + pathElemStart = 0; + endPtOrig = zero; + for ( inx=0; inx<groupTrk_da.cnt; inx++ ) { + trk = groupTrk(inx).trk; + epCnt = GetTrkEndPtCnt(trk); + for ( ep=0; ep<epCnt; ep++ ) { + trk1 = GetTrkEndTrk(trk,ep); + if ( trk1 == NULL || !GetTrkSelected(trk1) ) { + /* boundary EP */ + for ( epN=0; epN<tempEndPts_da.cnt; epN++ ) { + dist = FindDistance( GetTrkEndPos(trk,ep), tempEndPts(epN).pos ); + angle = NormalizeAngle( GetTrkEndAngle(trk,ep) - tempEndPts(epN).angle + connectAngle/2.0 ); + if ( dist < connectDistance && angle < connectAngle ) + break; + } + if ( epN>=tempEndPts_da.cnt ) { + DYNARR_APPEND( trkEndPt_t, tempEndPts_da, 10 ); + endPtP = &tempEndPts(tempEndPts_da.cnt-1); + memset( endPtP, 0, sizeof *endPtP ); + endPtP->pos = GetTrkEndPos(trk,ep); + endPtP->angle = GetTrkEndAngle(trk,ep); + endPtP->track = trk1; + endPtP->index = (trk1?GetEndPtConnectedToMe(trk1,trk):-1); + endPtOrig.x += endPtP->pos.x; + endPtOrig.y += endPtP->pos.y; + } + } + } + } + if ( tempEndPts_da.cnt <= 0 ) { + NoticeMessage( _("No endpts"), _("Ok"), NULL ); + wDrawDelayUpdate( mainD.d, FALSE ); + wHide( groupW ); + return; + } + if ( groupTrk_da.cnt == 1 && GetTrkType( groupTrk(0).trk ) == T_TURNOUT ) { + path = xx->paths; + pathLen = xx->pathLen; + goto groupSimpleTurnout; + } + + /* Make sure no turnouts in groupTrk list have a path end which is not an EndPt */ + for ( inx=0; inx<groupTrk_da.cnt; inx++ ) { + trk = groupTrk(0).trk; + if ( GetTrkType( trk ) == T_TURNOUT ) { + if ( GetTrkEndPtCnt( trk ) < 2 ) { + cp = MSG_CANT_GROUP_BUMPER1; + break; + } + if ( !CheckForBumper( trk ) ) { + cp = MSG_CANT_GROUP_BUMPER2; + break; + } + } + } + if ( inx < groupTrk_da.cnt ) { + NoticeMessage2( 0, cp, _("Ok"), NULL, GetTrkIndex( trk ) ); + DrawTrack( trk, &mainD, wDrawColorWhite ); + ClrTrkBits( trk, TB_SELECTED ); + /* TODO redraw the endpt of the trks this one is connected to */ + DrawTrack( trk, &mainD, wDrawColorBlack ); + wDrawDelayUpdate( mainD.d, FALSE ); + wHide( groupW ); + return; + } + + /* + * Sort EndPts by angle + */ + endPtOrig.x /= tempEndPts_da.cnt; + endPtOrig.y /= tempEndPts_da.cnt; + angleN = 270.0; + epN = -1; + for ( ep=0; ep<tempEndPts_da.cnt; ep++ ) { + angle = FindAngle(endPtOrig,tempEndPts(ep).pos); + if ( fabs(angle-270.0) < angleN ) { + epN = ep; + angleN = fabs(angle-270.0); + endPtAngle = angle; + } + } + qsort( tempEndPts_da.ptr, tempEndPts_da.cnt, sizeof *endPtP, CmpEndPtAngle ); + if ( NormalizeAngle( tempEndPts(0).angle - tempEndPts(tempEndPts_da.cnt-1).angle ) > + NormalizeAngle( tempEndPts(1).angle - tempEndPts(0).angle ) ) { +#ifdef LATER + if ( endPtAngle-FindAngle(endPtOrig,tempEndPts(tempEndPts_da.cnt-1).pos) > + FindAngle(endPtOrig,tempEndPts(1).pos)-endPtAngle ) { +#endif + for ( ep=1; ep<(tempEndPts_da.cnt+1)/2; ep++ ) { + trkEndPt_t tempEndPt; + tempEndPt = tempEndPts(ep); + tempEndPts(ep) = tempEndPts(tempEndPts_da.cnt-ep); + tempEndPts(tempEndPts_da.cnt-ep) = tempEndPt; + } + } + + /* + * Find shortest Paths + */ + for ( inx=0; inx<groupTrk_da.cnt; inx++ ) { + trk = groupTrk(inx).trk; + epCnt = GetTrkEndPtCnt(trk); + for ( ep=0; ep<epCnt; ep++ ) { + trk1 = GetTrkEndTrk(trk,ep); + if ( trk1 == NULL || !GetTrkSelected(trk1) ) { + /* boundary EP */ + rc = FindShortestPath( trk, ep, FALSE, GroupShortestPathFunc, NULL ); + } + } + } + + /* + * Flip paths so they align + */ + if ( path_da.cnt == 0 ) { + NoticeMessage( _("No paths"), _("Ok"), NULL ); + wDrawDelayUpdate( mainD.d, FALSE ); + wHide( groupW ); + return; + } + allDone = FALSE; + path(0).done = TRUE; + while ( !allDone ) { + allDone = TRUE; + inx = -1; + for ( pinx=0; pinx<path_da.cnt; pinx++ ) { + pp = &path(pinx); + if ( pp->done ) continue; + for ( pinx2=0; pinx2<path_da.cnt; pinx2++ ) { + if ( pinx2==pinx ) continue; + ppN = &path(pinx2); + if ( pp->ep1 == ppN->ep1 || + pp->ep2 == ppN->ep2 ) { + pp->done = TRUE; + allDone = FALSE; +LOG( log_group, 1, ( "P%d aligns with P%d\n", pinx, pinx2 ) ); + break; + } + if ( pp->ep1 == ppN->ep2 || + pp->ep2 == ppN->ep1 ) { + pp->done = TRUE; + allDone = FALSE; +LOG( log_group, 1, ( "P%d aligns flipped with P%d\n", pinx, pinx2 ) ); + inx = (pp->pathElemStart+pp->pathElemEnd-1)/2; + for ( ginx=pp->pathElemStart,ginx2=pp->pathElemEnd; ginx<=inx; ginx++,ginx2-- ) { + pathElemTemp = pathElem(ginx); + pathElem(ginx) = pathElem(ginx2); + pathElem(ginx2) = pathElemTemp; + } + for ( ginx=pp->pathElemStart; ginx<=pp->pathElemEnd; ginx++ ) { + ppp = &pathElem(ginx); + ep = ppp->ep1; + ppp->ep1 = ppp->ep2; + ppp->ep2 = ep; + ppp->flip = !ppp->flip; + } + ep = pp->ep1; + pp->ep1 = pp->ep2; + pp->ep2 = ep; + break; + } + } + if ( inx<0 && !pp->done ) + inx = pinx; + } + if ( allDone && inx>=0 ) { + allDone = FALSE; + path(inx).done = TRUE; + } + } +if ( log_group >= 1 && logTable(log_group).level > log_group ) { + LogPrintf( "Group Paths\n" ); + for ( pinx=0; pinx<path_da.cnt; pinx++ ) { + pp = &path(pinx); + LogPrintf( "P%2d:%d.%d ", pinx, pp->ep1, pp->ep2 ); + for ( pinx2=pp->pathElemEnd; pinx2>=pp->pathElemStart; pinx2-- ) { + ppp = &pathElem(pinx2); + LogPrintf( " %sT%d:%d.%d", ppp->flip?"-":"", GetTrkIndex(groupTrk(ppp->groupInx).trk), ppp->ep1, ppp->ep2 ); + } + LogPrintf( "\n" ); + } +} + +#ifdef SEGMAP + DYNARR_SET( char, segMap_da, 2 * trackSegs_da.cnt * path_da.cnt + 2 ); + memset( segMap_da.ptr, 0, segMap_da.max * sizeof segMap(0,0) ); + for ( inx=0; inx<path_da.cnt; inx++ ) { + pp = &path(inx); + for ( inx2=pp->pathElem_da.cnt-1; inx2>=0; inx2-- ) { + ppp = &pathElem(pp->pathElemStart+inx2); + groupP = &groupTrk(ppp->groupInx); + if ( GetTrkEndPtCnt(groupP->trk) == 2 ) { + segMap(inx,groupP->segStart) = 1; + continue; + } + cp = ppp->path; + if ( cp == NULL ) + continue; + segInx1 = cp[0]-1; + for ( ; *cp; cp++ ) + segMap(inx,groupP->segInx+cp[0]-1) = 1; + segInx2 = cp[-1]-1; + pos1 = GetSegEndPt( &trackSegs(groupP->segInx+segInx1), ppp->flip?1:0, FALSE, NULL ); + pos2 = GetSegEndPt( &trackSegs(groupP->segInx+segInx2), ppp->flip?0:1, FALSE, NULL ); + for ( inx3=0; inx3<groupP->segCnt; inx3++ ) { + if ( inx3 == segInx1 || inx3 == segInx2 ) continue; + if ( segMap(inx,groupP->segInx+inx3) != 0 ) continue; + if ( CheckTurnoutEndPoint( &trackSegs(groupP->segInx+inx3), pos1, 0 ) ) + segMap(inx,inx3) = 2; + else if ( CheckTurnoutEndPoint( &trackSegs(groupP->segInx+inx3), pos2, 0 ) ) + segMap(inx,groupP->segInx+inx3) = 2; + } + } + } +if ( log_group >= 1 && logTable(log_group).level > log_group ) { + LogPrintf( "Path to Segment Map\n "); + for ( inx=0; inx<groupTrk_da.cnt; inx++ ) { + groupP = &groupTrk(inx); + LogPrintf( "%2d", GetTrkIndex(groupP->trk) ); + for ( inx2=1; inx2<groupP->segCnt; inx2++ ) LogPrintf( "--" ); + } + LogPrintf( "\n " ); + for ( inx=0; inx<groupTrk_da.cnt; inx++ ) { + groupP = &groupTrk(inx); + for ( inx2=0; inx2<groupP->segCnt; inx2++ ) + LogPrintf( "%2d", inx2+1 ); + } + LogPrintf( "\n" ); + for ( inx=0; inx<path_da.cnt; inx++ ) { + LogPrintf( "%2d ", inx ); + for ( inx2=0; inx2<trackSegs_da.cnt; inx2++ ) + LogPrintf( "%2d", segMap(inx,inx2) ); + LogPrintf("\n"); + } +} +#endif + + /* + * Create Conflict Map + */ + DYNARR_SET( int, conflictMap_da, path_da.cnt*path_da.cnt ); + memset( conflictMap_da.ptr, 0, conflictMap_da.max * sizeof conflictMap(0,0) ); + for ( pinx=0; pinx<path_da.cnt; pinx++ ) { + for ( pinx2=pinx+1; pinx2<path_da.cnt; pinx2++ ) { + if ( ConflictPaths( &path(pinx), &path(pinx2) ) ) { + conflictMap( pinx, pinx2 ) = conflictMap( pinx2, pinx ) = TRUE; + path(pinx).conflicts++; + path(pinx2).conflicts++; + } + } + } + + /* + * Sort Paths by number of conflicts + */ + DYNARR_SET( int, groupOrder_da, path_da.cnt ); + for ( pinx=0; pinx<path_da.cnt; pinx++ ) groupOrder(pinx) = pinx; + qsort( groupOrder_da.ptr, path_da.cnt, sizeof groupOrder(0), CmpGroupOrder ); + + /* + * Group Paths, 1st pass: + */ + DYNARR_SET( int, groupMap_da, path_da.cnt*(path_da.cnt+1) ); + memset( groupMap_da.ptr, -1, groupMap_da.max * sizeof groupMap(0,0) ); + groupCnt = 0; + for ( pinx=0; pinx<path_da.cnt; pinx++ ) { + pp = &path(groupOrder(pinx)); + if ( pp->inGroup ) continue; + pp->inGroup = TRUE; + groupCnt++; + groupMap( groupCnt-1, 0 ) = groupOrder(pinx); + ginx = 1; + for ( pinx2=pinx+1; pinx2<path_da.cnt; pinx2++ ) { + gpinx2 = groupOrder(pinx2); + if ( path(gpinx2).inGroup ) continue; + for ( ginx2=0; ginx2<ginx && !conflictMap(groupMap(groupCnt-1,ginx2),gpinx2); ginx2++ ); + if ( ginx2<ginx ) continue; + path(gpinx2).inGroup = TRUE; + groupMap( groupCnt-1, ginx++ ) = gpinx2; + } + } + + /* + * Group Paths: 2nd pass: + */ + for ( pinx=0; pinx<groupCnt; pinx++ ) { + for ( ginx=0; groupMap(pinx,ginx)>=0; ginx++ ); + for ( pinx2=0; pinx2<path_da.cnt; pinx2++ ) { + gpinx2 = groupOrder(pinx2); + for ( ginx2=0; ginx2<ginx && groupMap(pinx,ginx2)!=gpinx2; ginx2++ ); + if ( ginx2<ginx ) continue; /* already on list */ + for ( ginx2=0; ginx2<ginx && !conflictMap(groupMap(pinx,ginx2),gpinx2); ginx2++ ); + if ( ginx2<ginx ) continue; /* conflicts with someone on list */ + groupMap(pinx,ginx++) = gpinx2; + } + } + +if ( log_group >= 1 && logTable(log_group).level > log_group ) { + LogPrintf( "Group Map\n"); + for ( pinx=0; pinx<groupCnt; pinx++ ) { + LogPrintf( "G%d:", pinx ); + for ( ginx=0; groupMap(pinx,ginx) >= 0; ginx++ ) + LogPrintf( " %d", groupMap(pinx,ginx) ); + LogPrintf( "\n" ); + } +} + +#ifdef SEGMAP + for ( inx=0; inx<path_da.cnt; inx++ ) { + for ( inx2=0; inx2<tempSegs_da.cnt; inx2++ ) { + groupInx = 0; + memset( &SegTotal(0), 0, tempSegs_da.cnt * sizeof SegAcc(0) ); + while (1) { + memcpy( &SegAcc(0), &SegTotal(0), tempSegs_da.cnt * sizeof SegAcc(0) ); + collision = FALSE; + for ( inx=0; inx<path_da.cnt; inx++ ) { + pp = path(0); + if ( pp->groupInx < 0 ) continue; + for ( inx2=0; inx2<tempSegs_da.cnt; inx2++ ) { + if ( !segMap(inx,inx2) ) continue; + if ( SegAcc(inx2) ) { + collision = TRUE; + break; + } + SegAcc(inx2) = TRUE; + } + } + if ( collision ) + } +#endif + + /* + * Count number of times each segment is used as flipped + */ + DYNARR_SET( int, conflictMap_da, trackSegs_da.cnt ); + memset( &segFlip(0), 0, trackSegs_da.cnt * sizeof segFlip(0) ); + for ( pinx=0; pinx<pathElem_da.cnt; pinx++ ) { + ppp = &pathElem(pinx); + for ( path=ppp->path; *path; path++ ) { + inx = *path; + if ( inx<0 ) + inx = - inx; + if ( inx > trackSegs_da.cnt ) + AbortProg( "inx > trackSegs_da.cnt" ); + flip = *path<0; + if ( ppp->flip ) + flip = !flip; + inx += groupTrk(ppp->groupInx).segStart - 1; + if ( !flip ) + segFlip(inx)++; + else + segFlip(inx)--; + } + } + + /* + * Flip each segment that is used as flipped more than not + */ + for ( pinx=0; pinx<trackSegs_da.cnt; pinx++ ) { + if ( segFlip(pinx) < 0 ) { +LOG( log_group, 1, ( "Flipping Segment %d\n", pinx+1 ) ); + SegProc( SEGPROC_FLIP, &trackSegs(pinx), NULL ); + } + } + + /* + * Output Path lists + */ + for ( pinx=0; pinx<groupCnt; pinx++ ) { + sprintf( message, "P%d", pinx ); + inx = pathPtr_da.cnt; + DYNARR_SET( char, pathPtr_da, inx+(int)strlen(message)+1 ); + memcpy( &pathPtr(inx), message, pathPtr_da.cnt-inx ); + for ( ginx=0; groupMap(pinx,ginx) >= 0; ginx++ ) { + pp = &path(groupMap(pinx,ginx)); + for ( pinx2=pp->pathElemEnd; pinx2>=pp->pathElemStart; pinx2-- ) { + ppp = &pathElem( pinx2 ); + groupP = &groupTrk( ppp->groupInx ); + path = ppp->path; + flip = ppp->flip; + if ( path == NULL ) + AbortProg( "Missing Path T%d:%d.%d", GetTrkIndex(groupP->trk), ppp->ep2, ppp->ep1 ); + if ( flip ) path += strlen((char *)path)-1; + while ( *path ) { + DYNARR_APPEND( char, pathPtr_da, 10 ); + pathChar = *path; + flip1 = flip; + if ( pathChar < 0 ) { + flip1 = !flip; + pathChar = - pathChar; + } + pathChar = groupP->segStart+pathChar; + if ( segFlip(pathChar-1)<0 ) + flip1 = ! flip1; + if ( flip1 ) pathChar = - pathChar; + pathPtr(pathPtr_da.cnt-1) = pathChar; + path += (flip?-1:1); + } + } + DYNARR_APPEND( char, pathPtr_da, 10 ); + pathPtr(pathPtr_da.cnt-1) = 0; + } + DYNARR_APPEND( char, pathPtr_da, 10 ); + pathPtr(pathPtr_da.cnt-1) = 0; + } + DYNARR_APPEND( char, pathPtr_da, 10 ); + pathPtr(pathPtr_da.cnt-1) = 0; + path = (PATHPTR_T)&pathPtr(0); + pathLen = pathPtr_da.cnt; + +groupSimpleTurnout: + /* + * Copy and Reorigin Segments + */ + if ( tempSegs_da.cnt > 0 ) { + inx = trackSegs_da.cnt; + DYNARR_SET( trkSeg_t, trackSegs_da, trackSegs_da.cnt+tempSegs_da.cnt ); + memcpy( &trackSegs(inx), tempSegs_da.ptr, tempSegs_da.cnt*sizeof trackSegs(0) ); + CloneFilledDraw( tempSegs_da.cnt, &trackSegs(inx), TRUE ); + } + GetSegBounds( zero, 0, trackSegs_da.cnt, &trackSegs(0), &orig, &size ); + orig.x = - tempEndPts(0).pos.x; + orig.y = - tempEndPts(0).pos.y; + MoveSegs( trackSegs_da.cnt, &trackSegs(0), orig ); + for ( ep=0; ep<tempEndPts_da.cnt; ep++ ) { + tempEndPts(ep).pos.x += orig.x; + tempEndPts(ep).pos.y += orig.y; + } + + /* + * Final: create new definition + */ + CheckPaths( trackSegs_da.cnt, &trackSegs(0), path ); + to = CreateNewTurnout( curScaleName, groupTitle, trackSegs_da.cnt, &trackSegs(0), pathLen, path, tempEndPts_da.cnt, &tempEndPts(0), TRUE ); +#ifdef LATER + if ( xx ) + to->customInfo = xx->customInfo; +#endif + f = OpenCustom("a"); + if (f && to) { + oldLocale = SaveLocale("C"); + rc &= fprintf( f, "TURNOUT %s \"%s\"\n", curScaleName, PutTitle(to->title) )>0; +#ifdef LATER + if ( to->customInfo ) + rc &= fprintf( f, "\tU %s\n", to->customInfo )>0; +#endif + rc &= WriteCompoundPathsEndPtsSegs( f, path, trackSegs_da.cnt, &trackSegs(0), tempEndPts_da.cnt, &tempEndPts(0) ); + } + if ( groupReplace ) { + UndoStart( _("Group Tracks"), "group" ); + orig.x = - orig.x; + orig.y = - orig.y; + for ( ep=0; ep<tempEndPts_da.cnt; ep++ ) { + endPtP = &tempEndPts(ep); + if ( endPtP->track ) { + trk = GetTrkEndTrk( endPtP->track, endPtP->index ); + epN = GetEndPtConnectedToMe( trk, endPtP->track ); + DrawEndPt( &mainD, endPtP->track, endPtP->index, wDrawColorWhite ); + DrawEndPt( &mainD, trk, epN, wDrawColorWhite ); + DisconnectTracks( trk, epN, endPtP->track, endPtP->index ); + } + endPtP->pos.x += orig.x; + endPtP->pos.y += orig.y; + } + trk = NULL; + while ( TrackIterate( &trk ) ) { + if ( GetTrkSelected( trk ) ) { + DrawTrack( trk, &mainD, wDrawColorWhite ); + UndoDelete( trk ); + trackCount--; + } + } + trk = NewCompound( T_TURNOUT, 0, orig, 0.0, to->title, tempEndPts_da.cnt, &tempEndPts(0), pathLen, (char *)path, trackSegs_da.cnt, &trackSegs(0) ); + SetTrkVisible( trk, TRUE ); + + SetTrkVisible( trk, TRUE ); + for ( ep=0; ep<tempEndPts_da.cnt; ep++ ) { + if ( tempEndPts(ep).track ) { + ConnectTracks( trk, ep, tempEndPts(ep).track, (EPINX_T)tempEndPts(ep).index ); + DrawEndPt( &mainD, tempEndPts(ep).track, (EPINX_T)tempEndPts(ep).index, GetTrkColor( tempEndPts(ep).track, &mainD ) ); + } + } + DrawNewTrack( trk ); + EnableCommands(); + } + } else { + CloneFilledDraw( tempSegs_da.cnt, &tempSegs(0), TRUE ); + GetSegBounds( zero, 0, tempSegs_da.cnt, &tempSegs(0), &orig, &size ); + orig.x = - orig.x; + orig.y = - orig.y; + MoveSegs( tempSegs_da.cnt, &tempSegs(0), orig ); + to = CreateNewStructure( curScaleName, groupTitle, tempSegs_da.cnt, &tempSegs(0), TRUE ); + f = OpenCustom("a"); + if (f && to) { + oldLocale = SaveLocale("C"); + rc &= fprintf( f, "STRUCTURE %s \"%s\"\n", curScaleName, PutTitle(groupTitle) )>0; +#ifdef LATER + if ( to->customInfo ) + rc &= fprintf( f, "\tU %s\n", to->customInfo )>0; +#endif + rc &= WriteSegs( f, tempSegs_da.cnt, &tempSegs(0) ); + } + if ( groupReplace ) { + UndoStart( _("Group Tracks"), "group" ); + trk = NULL; + while ( TrackIterate( &trk ) ) { + if ( GetTrkSelected( trk ) ) { + DrawTrack( trk, &mainD, wDrawColorWhite ); + UndoDelete( trk ); + trackCount--; + } + } + orig.x = - orig.x; + orig.y = - orig.y; + trk = NewCompound( T_STRUCTURE, 0, orig, 0.0, groupTitle, 0, NULL, 0, "", tempSegs_da.cnt, &tempSegs(0) ); + SetTrkVisible( trk, TRUE ); + DrawNewTrack( trk ); + EnableCommands(); + } + } + if (f) fclose(f); + RestoreLocale(oldLocale); + DoChangeNotification( CHANGE_PARAMS ); + wHide( groupW ); + wDrawDelayUpdate( mainD.d, FALSE ); + groupDesc[0] = '\0'; + groupPartno[0] = '\0'; +} + + +EXPORT void DoGroup( void ) +{ + track_p trk = NULL; + struct extraData *xx; + TRKTYP_T trkType; + xx = NULL; + groupSegCnt = 0; + groupCompoundCount = 0; + while ( TrackIterate( &trk ) ) { + if ( GetTrkSelected( trk ) ) { + trkType = GetTrkType(trk); + if ( trkType == T_TURNOUT || trkType == T_STRUCTURE ) { + xx = GetTrkExtraData(trk); + groupSegCnt += xx->segCnt; + GroupCopyTitle( xtitle(xx) ); + } else { + groupSegCnt += 1; + } + } + } + if ( groupSegCnt <= 0 ) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return; + } + sprintf( groupTitle, "%s\t%s\t%s", groupManuf, groupDesc, groupPartno ); + if ( log_group < 0 ) + log_group = LogFindIndex( "group" ); + if ( !groupW ) { + ParamRegister( &groupPG ); + groupW = ParamCreateDialog( &groupPG, MakeWindowTitle(_("Group Objects")), _("Ok"), GroupOk, wHide, TRUE, NULL, F_BLOCK, NULL ); + groupD.dpi = mainD.dpi; + } + ParamLoadControls( &groupPG ); + wShow( groupW ); +} + diff --git a/app/bin/chndldto.c b/app/bin/chndldto.c new file mode 100644 index 0000000..2e1f826 --- /dev/null +++ b/app/bin/chndldto.c @@ -0,0 +1,369 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/chndldto.c,v 1.4 2008-03-06 19:35:05 m_fischer Exp $ + * + * CURVE + * + */ + +/* 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 "track.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "cjoin.h" +#include "compound.h" +#include <math.h> +#include "i18n.h" + +#define PTRACE(X) + +/* + * STATE INFO + */ +static struct { + STATE_T state; + coOrd normalP; + ANGLE_T normalA; + track_p normalT; + coOrd reverseP; + coOrd reverseP1; + ANGLE_T reverseA; + DIST_T frogNo; + ANGLE_T frogA; + curveData_t curveData; + } Dhlt; + + +static STATUS_T CmdHandLaidTurnout( wAction_t action, coOrd pos ) +{ + ANGLE_T angle, angle2, angle3, reverseR, pointA, reverseA1, angle0; + EPINX_T ep, ep1, ep2, ep2a=-1, ep2b=-1, pointEp0, pointEp1; + DIST_T dist, reverseD, pointD; + coOrd off, intersectP; + coOrd pointP, pointC, pointP1, reverseC, point0; + track_p trk, trk1, trk2, trk2a=NULL, trk2b=NULL, pointT; + trkSeg_p segP; + BOOL_T right; + track_p trks[4], *trkpp; + + switch (action) { + + case C_START: + InfoMessage( _("Place frog and drag angle") ); + DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); + Dhlt.state = 0; + Dhlt.normalT = NULL; + tempSegs_da.cnt = 0; + DYNARR_SET( trkSeg_t, tempSegs_da, 2 ); + tempSegs(0).color = drawColorBlack; + tempSegs(0).width = 0; + tempSegs(1).color = drawColorBlack; + tempSegs(1).width = 0; + return C_CONTINUE; + + case C_DOWN: + if (Dhlt.state == 0) { + if ((Dhlt.normalT = OnTrack( &pos, TRUE, TRUE )) == NULL) + break; + if ( QueryTrack( Dhlt.normalT, Q_NOT_PLACE_FROGPOINTS ) ) { + ErrorMessage( MSG_CANT_PLACE_FROGPOINTS, _("frog") ); + Dhlt.normalT = NULL; + break; + } + Dhlt.normalP = Dhlt.reverseP = Dhlt.reverseP1 = pos; + Dhlt.normalA = GetAngleAtPoint( Dhlt.normalT, Dhlt.normalP, NULL, NULL ); + InfoMessage( _("Drag to set angle") ); + DrawLine( &tempD, Dhlt.reverseP, Dhlt.reverseP1, 0, wDrawColorBlack ); + Dhlt.state = 1; + pointC = pointP = pointP1 = reverseC = zero; + return C_CONTINUE; + } + + case C_MOVE: + case C_UP: + if (Dhlt.normalT == NULL) + break; + if (Dhlt.state == 1) { + DrawLine( &tempD, Dhlt.reverseP, Dhlt.reverseP1, 0, wDrawColorBlack ); + Dhlt.reverseP1 = pos; + Dhlt.reverseA = FindAngle( Dhlt.reverseP, Dhlt.reverseP1 ); + Dhlt.frogA = NormalizeAngle( Dhlt.reverseA - Dhlt.normalA ); +/*printf( "RA=%0.3f FA=%0.3f ", Dhlt.reverseA, Dhlt.frogA );*/ + if (Dhlt.frogA > 270.0) { + Dhlt.frogA = 360.0-Dhlt.frogA; + right = FALSE; + } else if (Dhlt.frogA > 180) { + Dhlt.frogA = Dhlt.frogA - 180.0; + Dhlt.normalA = NormalizeAngle( Dhlt.normalA + 180.0 ); + /*ep = Dhlt.normalEp0; Dhlt.normalEp0 = Dhlt.normalEp1; Dhlt.normalEp1 = ep;*/ + right = TRUE; + } else if (Dhlt.frogA > 90.0) { + Dhlt.frogA = 180.0 - Dhlt.frogA; + Dhlt.normalA = NormalizeAngle( Dhlt.normalA + 180.0 ); + /*ep = Dhlt.normalEp0; Dhlt.normalEp0 = Dhlt.normalEp1; Dhlt.normalEp1 = ep;*/ + right = FALSE; + } else { + right = TRUE; + } +/*printf( "NA=%0.3f FA=%0.3f R=%d\n", Dhlt.normalA, Dhlt.frogA, right );*/ + Dhlt.frogNo = tan(D2R(Dhlt.frogA)); + if (Dhlt.frogNo > 0.01) + Dhlt.frogNo = 1.0/Dhlt.frogNo; + else + Dhlt.frogNo = 0.0; + if (action == C_MOVE) { + if (Dhlt.frogNo != 0) { + InfoMessage( _("Angle = %0.2f Frog# = %0.2f"), Dhlt.frogA, Dhlt.frogNo ); + } else { + InfoMessage( _("Frog angle is too close to 0") ); + } + } else { + InfoMessage( _("Select point position") ); + Dhlt.state = 2; + Translate( &Dhlt.reverseP, Dhlt.reverseP, Dhlt.normalA+(right?+90:-90), trackGauge ); + Translate( &Dhlt.reverseP1, Dhlt.reverseP1, Dhlt.normalA+(right?+90:-90), trackGauge ); + } + DrawLine( &tempD, Dhlt.reverseP, Dhlt.reverseP1, 0, wDrawColorBlack ); + return C_CONTINUE; + } else if ( Dhlt.state == 2 ) { + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + tempSegs_da.cnt = 0; + pointP = pos; + if ((pointT = OnTrack( &pointP, TRUE, TRUE )) == NULL) + break; + if ( QueryTrack( pointT, Q_NOT_PLACE_FROGPOINTS ) ) { + ErrorMessage( MSG_CANT_PLACE_FROGPOINTS, _("points") ); + break; + } + dist = FindDistance( Dhlt.normalP, pointP ); + pointA = GetAngleAtPoint( pointT, pointP, &pointEp0, &pointEp1 ); + angle = NormalizeAngle( pointA + 180.0 - Dhlt.reverseA ); +PTRACE(( "rA=%0.1f pA=%0.1f a=%0.1f ", Dhlt.reverseA, pointA, angle )) + if ( angle > 90.0 && angle < 270.0 ) { + pointA = NormalizeAngle( pointA + 180.0 ); + angle = NormalizeAngle( angle + 180.0 ); +PTRACE(( " {pA=%0.1f a=%0.1f} ", pointA, angle )) + } else { + ep = pointEp0; pointEp0 = pointEp1; pointEp1 = ep; + } + if (angle > 180.0) { + angle = 360.0 - angle; + right = TRUE; + } else { + right = FALSE; + } +PTRACE(( "r=%c a=%0.1f ", right?'T':'F', angle )) + Translate( &off, pointP, pointA+180.0, trackGauge*2.0 ); + if ((trk = OnTrack( &off, TRUE, TRUE )) == NULL) + break; + if ( QueryTrack( trk, Q_NOT_PLACE_FROGPOINTS ) ) { + ErrorMessage( MSG_CANT_PLACE_FROGPOINTS, _("points") ); + break; + } + off = pointP; + Rotate( &off, Dhlt.reverseP, 180-Dhlt.reverseA ); + off.x -= Dhlt.reverseP.x; + off.y -= Dhlt.reverseP.y; + if (right) + off.x = -off.x; +PTRACE(( "off=[%0.3f %0.3f] ", off.x, off.y )) + if (off.y < 0) { + ErrorMessage( MSG_MOVE_POINTS_OTHER_SIDE ); +PTRACE(("\n")) + break; + } + if (off.x < 0) { + ErrorMessage( MSG_MOVE_POINTS_AWAY_CLOSE ); +PTRACE(("\n")) + break; + } + angle2 = FindAngle( zero, off ); +PTRACE(( "a2=%0.1f\n", angle2 )) + if (angle < 0.5) { + if ( off.x < connectDistance ) { + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).u.l.pos[0] = pointP; + tempSegs(0).u.l.pos[1] = Dhlt.reverseP; + tempSegs(1).type = SEG_STRTRK; + tempSegs(1).color = wDrawColorBlack; + tempSegs(1).u.l.pos[0] = Dhlt.reverseP; + Translate( &tempSegs(1).u.l.pos[1], Dhlt.reverseP, Dhlt.reverseA, trackGauge ); + tempSegs_da.cnt = 2; + } else { + ErrorMessage( MSG_MOVE_POINTS_AWAY_NO_INTERSECTION ); + break; + } + } else if (angle < angle2) { + ErrorMessage( MSG_MOVE_POINTS_AWAY_NO_INTERSECTION ); + break; + } else { + if (!FindIntersection( &intersectP, Dhlt.reverseP, Dhlt.reverseA+180.0, pointP, pointA+180.0 )) + break; + reverseD = FindDistance( Dhlt.reverseP, intersectP ); + pointD = FindDistance( pointP, intersectP ); + if (reverseD > pointD) { + reverseR = pointD/tan(D2R(angle/2.0)); + Translate( &reverseC, pointP, pointA+(right?-90:+90), reverseR ); +PTRACE(( "rR=%0.3f rC=[%0.3f %0.3f]\n", reverseR, reverseC.x, reverseC.y )) + tempSegs(0).type = SEG_CRVTRK; + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).u.c.center = reverseC; + tempSegs(0).u.c.radius = reverseR; + tempSegs(0).u.c.a0 = NormalizeAngle(pointA + (right?(+90.0):(-90.0-angle)) ); + tempSegs(0).u.c.a1 = angle; + tempSegs(1).type = SEG_STRTRK; + tempSegs(1).color = wDrawColorBlack; + PointOnCircle( &tempSegs(1).u.l.pos[0], reverseC, reverseR, tempSegs(0).u.c.a0 + (right?angle:0.0) ); + tempSegs(1).u.l.pos[1] = Dhlt.reverseP; + tempSegs(2).type = SEG_STRTRK; + tempSegs(2).color = wDrawColorBlack; + tempSegs(2).u.l.pos[0] = Dhlt.reverseP; + Translate( &tempSegs(2).u.l.pos[1], Dhlt.reverseP, Dhlt.reverseA, trackGauge ); + tempSegs_da.cnt = 3; + } else { + reverseR = reverseD/tan(D2R(angle/2.0)); + reverseR *= sqrt(reverseD/pointD); + Translate( &reverseC, Dhlt.reverseP, Dhlt.reverseA+(right?+90:-90), reverseR ); + Translate( &pointP1, pointP, pointA+(right?-90:+90), reverseR ); + dist = FindDistance( reverseC, pointP ); + angle2 = R2D( asin( reverseR/dist ) ); + angle3 = FindAngle( pointP, reverseC ); + if (right) + angle2 = NormalizeAngle(angle3 - pointA+180) - angle2; + else + angle2 = NormalizeAngle(pointA+180 - angle3) - angle2; + reverseA1 = angle-angle2; +PTRACE(( " a2=%0.1f rA1=%0.1f\n", angle2, reverseA1 )) + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).u.l.pos[0] = pointP; + tempSegs(1).u.c.a0 = NormalizeAngle(Dhlt.reverseA + (right?(-90.0-reverseA1):+90.0)); + PointOnCircle( &tempSegs(0).u.l.pos[1], reverseC, reverseR, tempSegs(1).u.c.a0 + (right?0.0:reverseA1) ); + tempSegs(1).type = SEG_CRVTRK; + tempSegs(1).color = wDrawColorBlack; + tempSegs(1).u.c.center = reverseC; + tempSegs(1).u.c.radius = reverseR; + tempSegs(1).u.c.a1 = reverseA1; + tempSegs(2).type = SEG_STRTRK; + tempSegs(2).color = wDrawColorBlack; + tempSegs(2).u.l.pos[0] = Dhlt.reverseP; + Translate( &tempSegs(2).u.l.pos[1], Dhlt.reverseP, Dhlt.reverseA, trackGauge ); + tempSegs_da.cnt = 3; + } + } + if (action != C_UP) { + dist = FindDistance( pointP, Dhlt.normalP ); + InfoMessage( _("Length = %0.2f Angle = %0.2f Frog# = %0.2f"), dist, Dhlt.frogA, Dhlt.frogNo ); + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + return C_CONTINUE; + } + UndoStart( _("Create Hand Laid Turnout"), "Hndldto( T%d[%d] )", GetTrkIndex(pointT), pointEp0 ); + UndoModify( pointT ); + if (!SplitTrack( pointT, pointP, pointEp0, &trk1, TRUE )) + break; + dist = trackGauge*2.0; + if ( !trk1 ) { + trk1 = pointT; + pointT = NULL; + } + ep1 = PickEndPoint( pointP, trk1 ); + if (!RemoveTrack( &trk1, &ep1, &dist )) + break; + point0 = GetTrkEndPos( trk1, ep1 ); + angle0 = NormalizeAngle(GetTrkEndAngle(trk1,ep1)+180.0); + trk2 = NULL; + trkpp = trks; + for (segP=&tempSegs(0); segP < &tempSegs(tempSegs_da.cnt); segP++ ) { + switch (segP->type) { + case SEG_STRTRK: + trk2b = NewStraightTrack( segP->u.l.pos[0], segP->u.l.pos[1] ); + ep2b = 0; + break; + case SEG_CRVTRK: + trk2b = NewCurvedTrack( segP->u.c.center, segP->u.c.radius, segP->u.c.a0, segP->u.c.a1, 0 ); + ep2b = (right?0:1); + } + if (trk2 == NULL) { + trk2 = trk2b; + ep2 = ep2b; + } else { + ConnectTracks( trk2a, ep2a, trk2b, ep2b ); + } + *trkpp++ = trk2a = trk2b; + ep2a = 1-ep2b; + } + *trkpp = NULL; + dist = trackGauge*2.0; + if (!RemoveTrack( &trk2, &ep2, &dist )) + break; + trk = NewHandLaidTurnout( pointP, pointA, + point0, angle0, + GetTrkEndPos(trk2,ep2), NormalizeAngle(GetTrkEndAngle(trk2,ep2)+180.0), Dhlt.frogA ); + DrawEndPt( &mainD, trk1, ep1, wDrawColorWhite ); + if ( pointT ) { + DrawEndPt( &mainD, pointT, pointEp0, wDrawColorWhite ); + ConnectTracks( trk, 0, pointT, pointEp0 ); + } + ConnectTracks( trk, 2, trk2, ep2 ); + ConnectTracks( trk, 1, trk1, ep1 ); + DrawEndPt( &mainD, trk1, ep1, wDrawColorBlack ); + DrawTrack( trk1, &mainD, wDrawColorBlack ); + if ( pointT ) { + DrawEndPt( &mainD, pointT, pointEp0, wDrawColorBlack ); + DrawTrack( pointT, &mainD, wDrawColorBlack ); + } + DrawTrack( trk, &mainD, wDrawColorBlack ); + for (trkpp=trks; *trkpp; trkpp++) + DrawTrack( *trkpp, &mainD, wDrawColorBlack ); + DrawLine( &tempD, Dhlt.reverseP, Dhlt.reverseP1, 0, wDrawColorBlack ); + + Dhlt.state = 0; + return C_TERMINATE; + } + + case C_REDRAW: + if (Dhlt.state >= 1) + DrawLine( &tempD, Dhlt.reverseP, Dhlt.reverseP1, 0, wDrawColorBlack ); + if (Dhlt.state >= 2) + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + return C_CONTINUE; + + case C_CANCEL: + if (Dhlt.state >= 1) + DrawLine( &tempD, Dhlt.reverseP, Dhlt.reverseP1, 0, wDrawColorBlack ); + if (Dhlt.state >= 2) { + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + tempSegs_da.cnt = 0; + } + return C_CONTINUE; + + } + + return C_CONTINUE; + +} + + +#include "bitmaps/hndldto.xpm" + +EXPORT void InitCmdHandLaidTurnout( wMenu_p menu ) +{ + AddMenuButton( menu, CmdHandLaidTurnout, "cmdHandLaidTurnout", _("HandLaidTurnout"), wIconCreatePixMap(hndldto_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_HNDLDTO, NULL ); +} diff --git a/app/bin/chotbar.c b/app/bin/chotbar.c new file mode 100644 index 0000000..b430f61 --- /dev/null +++ b/app/bin/chotbar.c @@ -0,0 +1,485 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/chotbar.c,v 1.4 2009-10-15 03:54:32 dspagnol Exp $ + * + * HOT BAR + * + */ + +/* 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 <ctype.h> +#include "track.h" +#include "compound.h" + +#include <stdint.h> + +EXPORT DIST_T curBarScale = -1; +EXPORT long hotBarLabels = 0; + +#include "bitmaps/hotbarl.xbm" +#include "bitmaps/hotbarr.xbm" + +static wButton_p hotBarLeftB = NULL; +static wButton_p hotBarRightB = NULL; +static wMenu_p hotbarPopupM; +static wMenuList_p hotBarML = NULL; +static wIndex_t hotBarMLcnt = 0; +static drawCmd_t hotBarD = { + NULL, + &screenDrawFuncs, + 0, + 1.0, + 0.0, + {0.0, 0.0}, {0.0, 0.0}, + Pix2CoOrd, CoOrd2Pix }; +static wPos_t hotBarDrawHeight = 28; +static wPos_t hotBarHeight = 28; +typedef struct { + DIST_T x; + DIST_T w; + DIST_T objectW; + DIST_T labelW; + coOrd size; + coOrd orig; + BOOL_T isTrack; + void * context; + hotBarProc_t proc; + DIST_T barScale; + } hotBarMap_t; +static dynArr_t hotBarMap_da; +#define hotBarMap(N) DYNARR_N( hotBarMap_t, hotBarMap_da, N ) +static int hotBarCurrSelects[2] = { -1, -1 }; +static int hotBarCurrStarts[2] = { -1, -1 }; +static int hotBarCurrEnds[2] = { -1, -1 }; +#define hotBarCurrSelect (hotBarCurrSelects[programMode]) +#define hotBarCurrStart (hotBarCurrStarts[programMode]) +#define hotBarCurrEnd (hotBarCurrEnds[programMode]) +static DIST_T hotBarWidth = 0.0; + +static void HotBarHighlight( int inx ) +{ + wPos_t x0; + if ( inx >= hotBarCurrStart && inx < hotBarCurrEnd ) { + x0 = (wPos_t)((hotBarMap(inx).x-hotBarMap((int)hotBarCurrStart).x)*hotBarD.dpi+2); + wDrawFilledRectangle( hotBarD.d, x0, 0, (wPos_t)(hotBarMap(inx).w*hotBarD.dpi-2), hotBarHeight, wDrawColorBlack, wDrawOptTemp ); + } +} + + +static wFont_p hotBarFp = NULL; +static wFontSize_t hotBarFs = 8; + +static void RedrawHotBar( wDraw_p dd, void * data, wPos_t w, wPos_t h ) +{ + DIST_T hh = (double)hotBarDrawHeight/hotBarD.dpi; + coOrd orig; + int inx; + hotBarMap_t * tbm; + DIST_T barHeight = (DIST_T)(wControlGetHeight( (wControl_p)hotBarD.d ) - 2)/hotBarD.dpi; + DIST_T barWidth = (DIST_T)(wControlGetWidth( (wControl_p)hotBarD.d ) - 2)/hotBarD.dpi; + DIST_T barScale; + DIST_T x; + + wDrawClear( hotBarD.d ); + wControlActive( (wControl_p)hotBarLeftB, hotBarCurrStart > 0 ); + if (hotBarCurrStart < 0) { + wControlActive( (wControl_p)hotBarRightB, FALSE ); + return; + } + if ( hotBarLabels && !hotBarFp ) + hotBarFp = wStandardFont( F_HELV, FALSE, FALSE ); + for ( inx=hotBarCurrStart; inx < hotBarMap_da.cnt; inx++ ) { + tbm = &hotBarMap(inx); + barScale = tbm->barScale; + x = tbm->x - hotBarMap(hotBarCurrStart).x + 2.0/hotBarD.dpi; + if ( x + tbm->w > barWidth ) { + break; + } + orig.y = hh/2.0*barScale - tbm->size.y/2.0 - tbm->orig.y; + if ( hotBarLabels ) { + orig.y += 8/hotBarD.dpi*barScale; + if ( tbm->labelW > tbm->objectW ) { + x += (tbm->labelW-tbm->objectW)/2; + } + } + x *= barScale; + orig.x = x - tbm->orig.x; + hotBarD.scale = barScale; + hotBarD.size.x = barWidth*barScale; + hotBarD.size.y = barHeight*barScale; + tbm->proc( HB_DRAW, tbm->context, &hotBarD, &orig ); + if ( hotBarLabels ) { + orig.x = x - (tbm->labelW-tbm->objectW)/2*barScale; + orig.y = 2*barScale/hotBarD.dpi; + DrawString( &hotBarD, orig, 0.0, tbm->proc( HB_BARTITLE, tbm->context, NULL, NULL ), hotBarFp, hotBarFs*barScale, drawColorBlack ); + } + } + hotBarCurrEnd = inx; + if (hotBarCurrSelect >= hotBarCurrStart && hotBarCurrSelect < hotBarCurrEnd ) + HotBarHighlight( hotBarCurrSelect ); +/* else + hotBarCurrSelect = -1;*/ + wControlActive( (wControl_p)hotBarRightB, hotBarCurrEnd < hotBarMap_da.cnt ); + wPrefSetInteger( "misc", "hotbar-start", hotBarCurrStart ); +} + + +static void DoHotBarRight( void * data ) +{ + DIST_T barWidth = ((DIST_T)wControlGetWidth( (wControl_p)hotBarD.d ) - 2.0)/hotBarD.dpi; + int inx = hotBarCurrStart; + DIST_T lastX = hotBarMap(hotBarMap_da.cnt-1).x + hotBarMap(hotBarMap_da.cnt-1).w + 2.0/hotBarD.dpi; + if (MyGetKeyState()&WKEY_SHIFT) { + inx += hotBarMap_da.cnt/8; + } else { + inx++; + } + if ( inx >= hotBarMap_da.cnt ) + inx = hotBarMap_da.cnt-1; + while ( inx > 1 && lastX - hotBarMap(inx-1).x <= barWidth ) + inx--; + if ( inx != hotBarCurrStart ) { + hotBarCurrStart = inx; + RedrawHotBar( hotBarD.d, NULL, 0, 0 ); + } +} + + +static void DoHotBarLeft( void * data ) +{ + int inx = hotBarCurrStart; + if (MyGetKeyState()&WKEY_SHIFT) { + inx -= hotBarMap_da.cnt/8; + } else { + inx --; + } + if ( inx < 0 ) + inx = 0; + if ( inx != hotBarCurrStart ) { + hotBarCurrStart = inx; + RedrawHotBar( hotBarD.d, NULL, 0, 0 ); + } +} + + +static void DoHotBarJump( int inx ) +{ + DIST_T x, barWidth; + + inx -= '0'; + if (inx < 0 || inx > 9) + return; + if (inx == 0) + inx = 9; + else + inx--; + barWidth = (DIST_T)wControlGetWidth( (wControl_p)hotBarD.d )/hotBarD.dpi; + x = (inx*(hotBarWidth-barWidth))/9.0; + for ( inx=0; inx<hotBarMap_da.cnt; inx++ ) { + if (x <= hotBarMap(inx).x) + break; + } + if ( hotBarCurrStart != inx ) { + hotBarCurrStart = inx; + RedrawHotBar( NULL, NULL, 0, 0 ); + } +} + + +static void SelectHotBar( wDraw_p d, void * context, wAction_t action, wPos_t w, wPos_t h ) +{ + int inx; + coOrd pos; + DIST_T x; + wPos_t px; + hotBarMap_t * tbm; + char * titleP; + + if ( hotBarMap_da.cnt <= 0 ) + return; +#if 0 + if ( !CommandEnabled( hotBarCmdInx ) ) + return; +#endif + if ( (action&0xFF) == wActionRUp ) { + wMenuPopupShow( hotbarPopupM ); + return; + } + x = w/hotBarD.dpi + hotBarMap(hotBarCurrStart).x; + for ( inx=hotBarCurrStart; inx<hotBarCurrEnd; inx++ ) { + if ( x < hotBarMap(inx).x + hotBarMap(inx).w ) { + break; + } + } + if (inx >= hotBarCurrEnd) + return; + tbm = &hotBarMap(inx); + px = (wPos_t)((tbm->x-hotBarMap(hotBarCurrStart).x)*hotBarD.dpi); + px += (wPos_t)(tbm->w*hotBarD.dpi/2); + titleP = tbm->proc( HB_LISTTITLE, tbm->context, NULL, NULL ); + px -= wLabelWidth( titleP ) / 2; + wControlSetBalloon( (wControl_p)hotBarD.d, px, -5, titleP ); + switch (action & 0xff) { + case wActionLDown: + pos.x = mainD.size.x+mainD.orig.x; + pos.y = mainD.size.y+mainD.orig.y; + if ( hotBarCurrSelect >= 0 ) { + HotBarHighlight( hotBarCurrSelect ); + hotBarCurrSelect = -1; + } + tbm->proc( HB_SELECT, tbm->context, NULL, NULL ); + hotBarCurrSelect = inx; + HotBarHighlight( hotBarCurrSelect ); + if (recordF) { + fprintf( recordF, "HOTBARSELECT %s\n", tbm->proc( HB_FULLTITLE, tbm->context, NULL, NULL ) ); + } + FakeDownMouseState(); + break; + case wActionExtKey: + switch ((wAccelKey_e)(action>>8)) { + case wAccelKey_Right: + DoHotBarRight(NULL); + break; + case wAccelKey_Left: + DoHotBarLeft(NULL); + break; + case wAccelKey_Up: + break; + case wAccelKey_Down: + break; + default: + break; + } + break; + case wActionText: + switch (action >> 8) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + DoHotBarJump( action >> 8 ); + break; + case 0x1B: + ConfirmReset(FALSE); + break; + } + break; + } +} + + +EXPORT void HotBarCancel( void ) +{ + if ( hotBarCurrSelect >= 0 ) + HotBarHighlight( hotBarCurrSelect ); + hotBarCurrSelect = -1; +} + + +static BOOL_T HotBarSelectPlayback( char * line ) +{ + int inx; + hotBarMap_t * tbm; + while (*line && isspace(*line) ) line++; + for ( inx=0; inx<hotBarMap_da.cnt; inx++ ) { + tbm = &hotBarMap(inx); + if ( strcmp( tbm->proc( HB_FULLTITLE, tbm->context, NULL, NULL ), line) == 0) { + if ( hotBarCurrSelect >= 0 ) { + HotBarHighlight( hotBarCurrSelect ); + } + hotBarCurrSelect = inx; + if ( hotBarCurrSelect < hotBarCurrStart || hotBarCurrSelect > hotBarCurrEnd ) { + hotBarCurrStart = hotBarCurrSelect; + RedrawHotBar( hotBarD.d, NULL, 0, 0 ); + } + HotBarHighlight( hotBarCurrSelect ); + hotBarMap(inx).proc( HB_SELECT, hotBarMap(inx).context, NULL, NULL ); + FakeDownMouseState(); + return TRUE; + } + } + return FALSE; +} + + +static void HotbarJump( int inx, const char * name, void * arg ) +{ + hotBarCurrStart = (int)(long)arg; + RedrawHotBar( hotBarD.d, NULL, 0, 0 ); +} + + +static BOOL_T SetHotBarScale( char * line ) +{ + curBarScale = atof( line + 9 ); + return TRUE; +} + + +static char curContentsLabel[STR_SHORT_SIZE]; +EXPORT void AddHotBarElement( + char * contentsLabel, + coOrd size, + coOrd orig, + BOOL_T isTrack, + DIST_T barScale, + void * context, + hotBarProc_t proc_p ) +{ + hotBarMap_t * tbm; + coOrd textsize; + + if ( contentsLabel && strncmp(contentsLabel, curContentsLabel, sizeof curContentsLabel) != 0 ) { + wMenuListAdd( hotBarML, hotBarMLcnt++, contentsLabel, (void*)(intptr_t)hotBarMap_da.cnt ); + strncpy( curContentsLabel, contentsLabel, sizeof curContentsLabel ); + } + + if (barScale <= 0) { + if (isTrack) + barScale = (trackGauge>0.1)?trackGauge*24:10; + else + barScale = size.y/((double)hotBarDrawHeight/hotBarD.dpi-0.07); + } + DYNARR_APPEND( hotBarMap_t, hotBarMap_da, 10 ); + tbm = &hotBarMap(hotBarMap_da.cnt-1); + if (barScale < 1) + barScale = 1; + if (size.x > barScale) + barScale = size.x; + tbm->context = context; + tbm->size = size; + tbm->orig = orig; + tbm->proc = proc_p; + tbm->barScale = barScale; + tbm->w = tbm->objectW = size.x/barScale + 5.0/hotBarD.dpi; + tbm->labelW = 0; + tbm->x = hotBarWidth; + if ( hotBarLabels ) { + DrawTextSize( &hotBarD, proc_p( HB_BARTITLE, context, NULL, NULL), hotBarFp, hotBarFs, FALSE, &textsize ); + tbm->labelW = textsize.x+5/hotBarD.dpi; + if ( tbm->labelW > tbm->w ) { + tbm->w = tbm->labelW; + } + } + hotBarWidth += tbm->w; +} + + +static void ChangeHotBar( long changes ) +{ +#ifdef LATER + int curFileIndex = -3; + char * name; +#endif + static long programModeOld = 0; + + if ( (changes&(CHANGE_SCALE|CHANGE_PARAMS|CHANGE_TOOLBAR)) == 0 ) + return; + if ( hotBarLabels && !hotBarFp ) + hotBarFp = wStandardFont( F_HELV, FALSE, FALSE ); + if (hotBarLeftB != NULL && curScaleName) { + hotBarWidth = 0.0; + hotBarMLcnt = 0; + wMenuListClear( hotBarML ); + DYNARR_RESET( hotBarMap_t, hotBarMap_da ); + curContentsLabel[0] = '\0'; + if ( programMode == MODE_DESIGN ) { + AddHotBarTurnouts(); + AddHotBarStructures(); + } else { + AddHotBarCarDesc(); + } + + if ( programModeOld != programMode ) { + hotBarCurrSelects[0] = hotBarCurrSelects[1] = -1; + programModeOld = programMode; + } + if (hotBarMap_da.cnt > 0 && (hotBarCurrStart >= hotBarMap_da.cnt||hotBarCurrStart < 0)) + hotBarCurrStart = 0; + RedrawHotBar( NULL, NULL, 0, 0 ); + } +} + + +EXPORT void InitHotBar( void ) +{ + long v; + + AddParam( "BARSCALE", SetHotBarScale ); + AddPlaybackProc( "HOTBARSELECT", (playbackProc_p)HotBarSelectPlayback, NULL ); + RegisterChangeNotification( ChangeHotBar ); + wPrefGetInteger( "misc", "hotbar-start", &v, hotBarCurrStart ); + hotBarCurrStart = (int)v; + hotbarPopupM = MenuRegister( "Hotbar Select" ); + hotBarML = wMenuListCreate( hotbarPopupM, "", -1, HotbarJump ); +} + +EXPORT void LayoutHotBar( void ) +{ + wPos_t buttonWidth, winWidth, winHeight; + BOOL_T initialize = FALSE; + + wWinGetSize( mainW, &winWidth, &winHeight ); + hotBarHeight = hotBarDrawHeight; + if ( hotBarLabels) + hotBarHeight += 8; + if (hotBarLeftB == NULL) { + wIcon_p bm_p; + if (winWidth < 50) + return; + bm_p = wIconCreateBitMap( 16, 16, turnbarl_bits, wDrawColorBlack ); + hotBarLeftB = wButtonCreate( mainW, 0, 0, "hotBarLeft", (char*)bm_p, BO_ICON, 0, DoHotBarLeft, NULL ); + bm_p = wIconCreateBitMap( 16, 16, turnbarr_bits, wDrawColorBlack ); + hotBarRightB = wButtonCreate( mainW, 0, 0, "hotBarRight", (char*)bm_p, BO_ICON, 0, DoHotBarRight, NULL ); + hotBarD.d = wDrawCreate( mainW, 0, 0, NULL, BD_NOCAPTURE, 100, hotBarHeight, NULL, RedrawHotBar, SelectHotBar ); + hotBarD.dpi = wDrawGetDPI( hotBarD.d ); + hotBarD.scale = 1.0; + initialize = TRUE; + } + buttonWidth = wControlGetWidth((wControl_p)hotBarLeftB); + wControlSetPos( (wControl_p)hotBarLeftB, 0, toolbarHeight ); + wControlSetPos( (wControl_p)hotBarRightB, winWidth-buttonWidth, toolbarHeight ); + wControlSetPos( (wControl_p)hotBarD.d, buttonWidth, toolbarHeight ); + wDrawSetSize( hotBarD.d, winWidth-buttonWidth*2, hotBarHeight+2 ); + hotBarD.size.x = ((double)(winWidth-buttonWidth*2))/hotBarD.dpi*hotBarD.scale; + hotBarD.size.y = (double)hotBarHeight/hotBarD.dpi*hotBarD.scale; + wControlShow( (wControl_p)hotBarLeftB, TRUE ); + wControlShow( (wControl_p)hotBarRightB, TRUE ); + wControlShow( (wControl_p)hotBarD.d, TRUE ); + if (initialize) + ChangeHotBar( CHANGE_PARAMS ); + else + RedrawHotBar( NULL, NULL, 0, 0 ); + toolbarHeight += hotBarHeight+3; +} + +void HideHotBar( void ) +{ + if (hotBarLeftB != NULL) { + wControlShow( (wControl_p)hotBarLeftB, FALSE ); + wControlShow( (wControl_p)hotBarRightB, FALSE ); + wControlShow( (wControl_p)hotBarD.d, FALSE ); + } +} diff --git a/app/bin/cjoin.c b/app/bin/cjoin.c new file mode 100644 index 0000000..e8d72eb --- /dev/null +++ b/app/bin/cjoin.c @@ -0,0 +1,901 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cjoin.c,v 1.4 2008-03-06 19:35:05 m_fischer Exp $ + * + * JOINS + * + */ + +/* 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 "track.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "cjoin.h" +#include "i18n.h" + + +static int log_join = 0; +typedef struct { + curveType_e type; + BOOL_T flip; + coOrd arcP; + DIST_T arcR; + ANGLE_T arcA0, arcA1; + coOrd pos[2]; + } joinRes_t; + +static struct { + STATE_T state; + int joinMoveState; + struct { + TRKTYP_T realType; + track_p trk; + coOrd pos; + EPINX_T ep; + trackParams_t params; +#ifdef LATER + curveType_e type; + ANGLE_T angle; + coOrd lineOrig; + coOrd lineEnd; + coOrd arcP; + DIST_T arcR; + ANGLE_T arcA0, arcA1; +#endif + } inp[2]; + joinRes_t jRes; + coOrd inp_pos[2]; + easementData_t jointD[2]; + } Dj; + + +/***************************************************************************** + * + * JOIN + * + */ + + +static BOOL_T JoinWithStraight( + coOrd pos0, + ANGLE_T a0, + coOrd pos1, + ANGLE_T a1, + joinRes_t * res ) +/* + * Determine a track from a point and angle (pos1,a1) to + * a straight (given by an origin and angle: pos0, a0) + */ +{ + coOrd Px; + ANGLE_T b, c; + DIST_T d; + DIST_T k; + coOrd off; + DOUBLE_T beyond; + + b = NormalizeAngle( a0 - a1 ); +LOG( log_join, 2, ( + "JwL: pos0=[%0.3f %0.3f] a0=%0.3f pos1=[%0.3f %0.3f] a1=%0.3f b=%0.3f\n", + pos0.x, pos0.y, a0, pos1.x, pos1.y, a1, b ) ) + +/* 3 - cases: */ + if (b >= 360.0-connectAngle/2.0 || b <= connectAngle/2.0) { +/* CASE 1: antiparallel */ + FindPos( &off, NULL, pos1, pos0, a0, 10000.0 ); + res->arcR = off.y/2.0; + res->arcA1 = 180.0; +LOG( log_join, 3, ("JwL: parallel: off.y=%0.3f\n", off.y ) ) + res->arcA0 = NormalizeAngle( a1 - 90.0 ); + Translate( &res->arcP, pos1, res->arcA0, res->arcR ); + if (res->arcR > 0.0) { + res->flip = 0; + } else { + res->arcR = -res->arcR; + res->flip = 1; + } + } else if (b >= 180.0-connectAngle/2.0 && b <= 180.0+connectAngle/2.0) { +/* CASE 2: parallel, possibly colinear? */ + FindPos( &off, &beyond, pos0, pos1, a0, 100000.0 ); +LOG( log_join, 3, ("JwL: colinear? off.y=%0.3f\n", off.y ) ) + if (off.y > -connectDistance && off.y < connectDistance) { + res->type = curveTypeStraight; + res->pos[0]=pos0; + res->pos[1]=pos1; +LOG( log_join, 2, (" = STRAIGHT [%0.3f %0.3f] [%0.3f %0.3f]\n", pos0.x, pos0.y, pos1.x, pos1.y ) ) + return TRUE; + } else { + res->type = curveTypeNone; + ErrorMessage( MSG_SELECTED_TRACKS_PARALLEL ); + return TRUE; + } + } else { +/* CASE 3: intersecting */ + if (!FindIntersection( &Px, pos0, a0, pos1, a1 )) { + res->type = curveTypeNone; + ErrorMessage( MSG_SELECTED_TRACKS_PARALLEL ); + return TRUE; + } + d = FindDistance( pos1, Px ); + k = NormalizeAngle( FindAngle(pos1, Px) - a1 ); + c = (b > 180.0) ? (360.0-b) : b; + if (k < 90.0 && k > 270.0) + c += 180.0; +LOG( log_join, 3, (" Px=[%0.3f %0.3f] b=%0.3f c=%0.3f d=%0.3f k=%0.3f\n", Px.x, Px.y, b, c, d, k ) ) + res->arcR = d * sin(D2R(c/2.0))/cos(D2R(c/2.0)); + res->arcA1 = 180.0-c; + if (90.0<k && k<270.0) + res->arcA1 = 360.0 - res->arcA1; + if ( (res->arcA1>180.0) == (b>180.0) ) { + Translate( &res->arcP, pos1, a1-90.0, res->arcR ); + res->arcA0 = NormalizeAngle( a0 - 90.0 ); + res->flip = FALSE; + } else { + Translate( &res->arcP, pos1, a1+90.0, res->arcR ); + res->arcA0 = NormalizeAngle( a1 - 90.0 ); + res->flip = TRUE; + } + } +LOG( log_join, 2, (" = CURVE @ Pc=[%0.3f %0.3f] R=%0.3f A0=%0.3f A1=%0.3f Flip=%d\n", + res->arcP.x, res->arcP.y, res->arcR, res->arcA0, res->arcA1, res->flip ) ) + if (res->arcR<0.0) res->arcR = - res->arcR; + res->type = curveTypeCurve; + d = D2R(res->arcA1); + if (d < 0.0) + d = 2*M_PI + d; + InfoMessage( _("Curved Track: Radius=%s Length=%s"), + FormatDistance(res->arcR), FormatDistance(res->arcR*d) ); + return TRUE; + +} + +static BOOL_T JoinWithCurve( + coOrd pos0, + DIST_T r0, + EPINX_T ep0, + coOrd pos1, + ANGLE_T a1, /* Angle perpendicular to track at (pos1) */ + joinRes_t * res ) +/* + * Determine a track point and angle (pos1,a1) to + * a curve (given by center and radius (pos0, r0). + * Curve endPt (ep0) determines whether the connection is + * clockwise or counterclockwise. + */ +{ + coOrd p1, pt; + DIST_T d, r; + ANGLE_T a, aa, A0, A1; + +/* Compute angle of line connecting endPoints: */ + Translate( &p1, pos1, a1, -r0 ); + aa = FindAngle( p1, pos0 ); + a = NormalizeAngle( aa - a1 ); +LOG( log_join, 2, ("JwA: pos0=[%0.3f %0.3f] r0=%0.3f ep0=%d pos1=[%0.3f %0.3f] a1=%0.3f\n", + pos0.x, pos0.y, r0, ep0, pos1.x, pos1.y, a1 ) ) +LOG( log_join, 3, (" p1=[%0.3f %0.3f] aa=%0.3f a=%0.3f\n", + p1.x, p1.y, aa, a ) ) + + if ( (ep0==1 && a > 89.5 && a < 90.5) || + (ep0==0 && a > 269.5 && a < 270.5) ) { +/* The long way around! */ + ErrorMessage( MSG_CURVE_TOO_LARGE ); + res->type = curveTypeNone; + + } else if ( (ep0==0 && a > 89.5 && a < 90.5) || + (ep0==1 && a > 269.5 && a < 270.5) ) { +/* Straight: */ + PointOnCircle( &pt, pos0, r0, a1); +LOG( log_join, 2, (" = STRAIGHT [%0.3f %0.3f] [%0.3f %0.3f]\n", pt.x, pt.y, pos1.x, pos1.y ) ) + InfoMessage( _("Straight Track: Length=%s Angle=%0.3f"), + FormatDistance(FindDistance( pt, pos1 )), PutAngle(FindAngle( pt, pos1 )) ); + res->type = curveTypeStraight; + res->pos[0]=pt; + res->pos[1]=pos1; + res->flip = FALSE; + + } else { +/* Curve: */ + d = FindDistance( p1, pos0 ) / 2.0; + r = d/cos(D2R(a)); + Translate( &res->arcP, p1, a1, r ); + res->arcR = r-r0; +LOG( log_join, 3, (" Curved d=%0.3f C=[%0.3f %0.3f], r=%0.3f a=%0.3f arcR=%0.3f\n", + d, res->arcP.x, res->arcP.y, r, a, res->arcR ) ) + if ( (ep0==0) == (res->arcR<0) ) { + A1 = 180 + 2*a; + A0 = a1; + res->flip = TRUE; + } else { + A1 = 180 - 2*a; + A0 = a1 - A1; + res->flip = FALSE; + } + if (res->arcR>=0) { + A0 += 180.0; + } else { + res->arcR = - res->arcR; + } + res->arcA0 = NormalizeAngle( A0 ); + res->arcA1 = NormalizeAngle( A1 ); + + if ( res->arcR*2.0*M_PI*res->arcA1/360.0 > mapD.size.x+mapD.size.y ) { + ErrorMessage( MSG_CURVE_TOO_LARGE ); + res->type = curveTypeNone; + return TRUE; + } + +LOG( log_join, 3, (" A0=%0.3f A1=%0.3f R=%0.3f\n", res->arcA0, res->arcA1, res->arcR ) ) + d = D2R(res->arcA1); + if (d < 0.0) + d = 2*M_PI + d; + InfoMessage( _("Curved Track: Radius=%s Length=%s Angle=%0.3f"), + FormatDistance(res->arcR), FormatDistance(res->arcR*d), PutAngle(res->arcA1) ); + res->type = curveTypeCurve; + } + return TRUE; +} + +/***************************************************************************** + * + * JOIN + * + */ + + +static STATUS_T AdjustJoint( + BOOL_T adjust, + ANGLE_T a1, + DIST_T eR[2], + ANGLE_T normalAngle ) +/* + * Compute how to join 2 tracks and then compute the transition-curve + * from the 2 tracks to the joint. + * The 2nd contact point (Dj.inp[1].pos) can be moved by (Dj.jointD[1].x) + * before computing the connection curve. This allows for the + * transition-curve. + * + * This function is called iteratively to fine-tune the offset (X) required + * for the transition-curves. + * The first call does not move the second contact point. Subsequent calls + * move the contact point by the previously computed offset. + * Hopefully, this converges on a stable value for the offset quickly. + */ +{ + coOrd p0, p1; + ANGLE_T a0=0; + coOrd pc; + DIST_T eRc; + DIST_T l, d=0; + + if (adjust) + Translate( &p1, Dj.inp[1].pos, a1, Dj.jointD[1].x ); + else + p1 = Dj.inp[1].pos; + + switch ( Dj.inp[0].params.type ) { + case curveTypeCurve: + if (adjust) { + a0 = FindAngle( Dj.inp[0].params.arcP, Dj.jRes.pos[0] ) + + ((Dj.jointD[0].Scurve==TRUE || Dj.jointD[0].flip==FALSE)?0:+180); + Translate( &pc, Dj.inp[0].params.arcP, a0, Dj.jointD[0].x ); +LOG( log_join, 2, (" Move P0 X%0.3f A%0.3f P1 X%0.3f A%0.3f)\n", + Dj.jointD[0].x, a0, Dj.jointD[1].x, a1 ) ) + } else { + pc = Dj.inp[0].params.arcP; + } + if (!JoinWithCurve( pc, Dj.inp[0].params.arcR, + Dj.inp[0].params.ep, p1, normalAngle, &Dj.jRes )) + return FALSE; + break; + case curveTypeStraight: + if (adjust) { + a0 = Dj.inp[0].params.angle + (Dj.jointD[0].negate?-90.0:+90.0); + Translate( &p0, Dj.inp[0].params.lineOrig, a0, Dj.jointD[0].x ); +LOG( log_join, 2, (" Move P0 X%0.3f A%0.3f P1 X%0.3f A%0.3f\n", + Dj.jointD[0].x, a0, Dj.jointD[1].x, a1 ) ) + } else { + p0 = Dj.inp[0].params.lineOrig; + } + if (!JoinWithStraight( p0, Dj.inp[0].params.angle, p1, Dj.inp[1].params.angle, &Dj.jRes )) + return FALSE; + break; + default: + break; + } + + if (Dj.jRes.type == curveTypeNone) { + return FALSE; + } + + if (Dj.jRes.type == curveTypeCurve) { + eRc = Dj.jRes.arcR; + if (Dj.jRes.flip==1) + eRc = -eRc; + } else + eRc = 0.0; + + if ( ComputeJoint( eR[0], eRc, &Dj.jointD[0] ) == E_ERROR || + ComputeJoint( -eR[1], -eRc, &Dj.jointD[1] ) == E_ERROR ) { + return FALSE; + } + +#ifdef LATER + for (inx=0; inx<2; inx++) { + if (Dj.inp[inx].params.type == curveTypeStraight ) { + d = FindDistance( Dj.inp[inx].params.lineOrig, Dj.inp_pos[inx] ); + if (d < Dj.jointD[inx].d0) { + InfoMessage( _("Track (%d) is too short for transition-curve by %0.3f"), + GetTrkIndex(Dj.inp[inx].trk), + PutDim(fabs(Dj.jointD[inx].d0-d)) ); + return FALSE; + } + } + } +#endif + + l = Dj.jointD[0].d0 + Dj.jointD[1].d0; + if (Dj.jRes.type == curveTypeCurve ) { + d = Dj.jRes.arcR * Dj.jRes.arcA1 * 2.0*M_PI/360.0; + } else if (Dj.jRes.type == curveTypeStraight ) { + d = FindDistance( Dj.jRes.pos[0], Dj.jRes.pos[1] ); + } + d -= l; + if ( d <= minLength ) { + InfoMessage( _("Connecting track is too short by %0.3f"), PutDim(fabs(minLength-d)) ); + return FALSE; + } + + if (Dj.jRes.type == curveTypeCurve) { + PointOnCircle( &Dj.jRes.pos[Dj.jRes.flip], Dj.jRes.arcP, + Dj.jRes.arcR, Dj.jRes.arcA0 ); + PointOnCircle( &Dj.jRes.pos[1-Dj.jRes.flip], Dj.jRes.arcP, + Dj.jRes.arcR, Dj.jRes.arcA0+Dj.jRes.arcA1 ); + } + + if (adjust) + Translate( &Dj.inp_pos[0], Dj.jRes.pos[0], a0+180.0, Dj.jointD[0].x ); + + return TRUE; +} + + +static STATUS_T DoMoveToJoin( coOrd pos ) +{ + if ( selectedTrackCount <= 0 ) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return C_CONTINUE; + } + if ( (Dj.inp[Dj.joinMoveState].trk = OnTrack( &pos, TRUE, TRUE )) == NULL ) + return C_CONTINUE; + if (!CheckTrackLayer( Dj.inp[Dj.joinMoveState].trk ) ) + return C_CONTINUE; + Dj.inp[Dj.joinMoveState].params.ep = PickUnconnectedEndPoint( pos, Dj.inp[Dj.joinMoveState].trk ); /* CHECKME */ + if ( Dj.inp[Dj.joinMoveState].params.ep == -1 ) { +#ifdef LATER + ErrorMessage( MSG_NO_ENDPTS ); +#endif + return C_CONTINUE; + } +#ifdef LATER + if ( GetTrkEndTrk( Dj.inp[Dj.joinMoveState].trk, Dj.inp[Dj.joinMoveState].params.ep ) ) { + ErrorMessage( MSG_SEL_EP_CONN ); + return C_CONTINUE; + } +#endif + if (Dj.joinMoveState == 0) { + Dj.joinMoveState++; + InfoMessage( GetTrkSelected(Dj.inp[0].trk)? + _("Click on an unselected End-Point"): + _("Click on a selected End-Point") ); + Dj.inp[0].pos = pos; + DrawFillCircle( &tempD, Dj.inp[0].pos, 0.10*mainD.scale, selectedColor ); + return C_CONTINUE; + } + if ( GetTrkSelected(Dj.inp[0].trk) == GetTrkSelected(Dj.inp[1].trk) ) { + ErrorMessage( MSG_2ND_TRK_NOT_SEL_UNSEL, GetTrkSelected(Dj.inp[0].trk) + ? _("unselected") : _("selected") ); + return C_CONTINUE; + } + DrawFillCircle( &tempD, Dj.inp[0].pos, 0.10*mainD.scale, selectedColor ); + if (GetTrkSelected(Dj.inp[0].trk)) + MoveToJoin( Dj.inp[0].trk, Dj.inp[0].params.ep, Dj.inp[1].trk, Dj.inp[1].params.ep ); + else + MoveToJoin( Dj.inp[1].trk, Dj.inp[1].params.ep, Dj.inp[0].trk, Dj.inp[0].params.ep ); + Dj.joinMoveState = 0; + return C_TERMINATE; +} + + +static STATUS_T CmdJoin( + wAction_t action, + coOrd pos ) +/* + * Join 2 tracks. + */ +{ + DIST_T d=0, l; + coOrd off, p1; + EPINX_T ep; + track_p trk=NULL; + DOUBLE_T beyond; + STATUS_T rc; + ANGLE_T normalAngle=0; + EPINX_T inx; + ANGLE_T a, a1; + DIST_T eR[2]; + BOOL_T ok; + + switch (action) { + + case C_START: + InfoMessage( _("Left click - join with track, Shift Left click - move to join") ); + Dj.state = 0; + Dj.joinMoveState = 0; + /*ParamGroupRecord( &easementPG );*/ + return C_CONTINUE; + + case C_DOWN: + if ( (Dj.state == 0 && (MyGetKeyState() & WKEY_SHIFT) != 0) || Dj.joinMoveState != 0 ) + return DoMoveToJoin( pos ); + + DYNARR_SET( trkSeg_t, tempSegs_da, 3 ); + tempSegs(0).color = drawColorBlack; + tempSegs(0).width = 0; + tempSegs(1).color = drawColorBlack; + tempSegs(1).width = 0; + tempSegs(2).color = drawColorBlack; + tempSegs(2).width = 0; + tempSegs_da.cnt = 0; + Dj.joinMoveState = 0; +/* Populate (Dj.inp[0]) and check for connecting abutting tracks */ + if (Dj.state == 0) { + if ( (Dj.inp[0].trk = OnTrack( &pos, TRUE, TRUE )) == NULL) + return C_CONTINUE; + if (!CheckTrackLayer( Dj.inp[0].trk ) ) + return C_CONTINUE; + Dj.inp[0].pos = pos; +LOG( log_join, 1, ("JOIN: 1st track %d @[%0.3f %0.3f]\n", + GetTrkIndex(Dj.inp[0].trk), Dj.inp[0].pos.x, Dj.inp[1].pos.y ) ) + if (!GetTrackParams( PARAMS_1ST_JOIN, Dj.inp[0].trk, pos, &Dj.inp[0].params )) + return C_CONTINUE; + Dj.inp[0].realType = GetTrkType(Dj.inp[0].trk); + InfoMessage( _("Select 2nd track") ); + Dj.state = 1; + DrawFillCircle( &tempD, Dj.inp[0].pos, 0.10*mainD.scale, selectedColor ); + return C_CONTINUE; + } else { + if ( (Dj.inp[1].trk = OnTrack( &pos, TRUE, TRUE )) == NULL) + return C_CONTINUE; + if (!CheckTrackLayer( Dj.inp[1].trk ) ) + return C_CONTINUE; + Dj.inp[1].pos = pos; + if (!GetTrackParams( PARAMS_2ND_JOIN, Dj.inp[1].trk, pos, &Dj.inp[1].params )) + return C_CONTINUE; + if ( Dj.inp[0].trk == Dj.inp[1].trk ) { + ErrorMessage( MSG_JOIN_SAME ); + return C_CONTINUE; + } + Dj.inp[1].realType = GetTrkType(Dj.inp[1].trk); + if ( IsCurveCircle( Dj.inp[0].trk ) ) + Dj.inp[0].params.ep = PickArcEndPt( Dj.inp[0].params.arcP, Dj.inp[0].pos, pos ); + if ( IsCurveCircle( Dj.inp[1].trk ) ) + Dj.inp[1].params.ep = PickArcEndPt( Dj.inp[1].params.arcP, pos, Dj.inp[0].pos ); + +LOG( log_join, 1, (" 2nd track %d, @[%0.3f %0.3f] EP0=%d EP1=%d\n", + GetTrkIndex(Dj.inp[1].trk), Dj.inp[1].pos.x, Dj.inp[1].pos.y, + Dj.inp[0].params.ep, Dj.inp[1].params.ep ) ) +LOG( log_join, 1, ("P1=[%0.3f %0.3f]\n", pos.x, pos.y ) ) + if ( GetTrkEndTrk(Dj.inp[0].trk,Dj.inp[0].params.ep) != NULL) { + ErrorMessage( MSG_TRK_ALREADY_CONN, _("First") ); + return C_CONTINUE; + } + if ( Dj.inp[1].params.ep >= 0 && + GetTrkEndTrk(Dj.inp[1].trk,Dj.inp[1].params.ep) != NULL) { + ErrorMessage( MSG_TRK_ALREADY_CONN, _("Second") ); + return C_CONTINUE; + } + + rc = C_CONTINUE; + if ( MergeTracks( Dj.inp[0].trk, Dj.inp[0].params.ep, + Dj.inp[1].trk, Dj.inp[1].params.ep ) ) + rc = C_TERMINATE; + else if ( Dj.inp[0].params.ep >= 0 && Dj.inp[1].params.ep >= 0 ) { + if ( Dj.inp[0].params.type == curveTypeStraight && + Dj.inp[1].params.type == curveTypeStraight && + ExtendStraightToJoin( Dj.inp[0].trk, Dj.inp[0].params.ep, + Dj.inp[1].trk, Dj.inp[1].params.ep ) ) + rc = C_TERMINATE; + if ( ConnectAbuttingTracks( Dj.inp[0].trk, Dj.inp[0].params.ep, + Dj.inp[1].trk, Dj.inp[1].params.ep ) ) + rc = C_TERMINATE; + } + if ( rc == C_TERMINATE ) { + DrawFillCircle( &tempD, Dj.inp[0].pos, 0.10*mainD.scale, selectedColor ); + return rc; + } + if ( QueryTrack( Dj.inp[0].trk, Q_CANNOT_BE_ON_END ) || + QueryTrack( Dj.inp[1].trk, Q_CANNOT_BE_ON_END ) ) { + ErrorMessage( MSG_JOIN_EASEMENTS ); + return C_CONTINUE; + } + + DrawFillCircle( &tempD, Dj.inp[0].pos, 0.10*mainD.scale, selectedColor ); + Dj.state = 2; + Dj.jRes.flip = FALSE; + } + tempSegs_da.cnt = 0; + + case C_MOVE: + +LOG( log_join, 3, ("P1=[%0.3f %0.3f]\n", pos.x, pos.y ) ) + if (Dj.state != 2) + return C_CONTINUE; + + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, drawColorBlack ); + tempSegs_da.cnt = 0; + tempSegs(0).color = drawColorBlack; + ok = FALSE; + +/* Populate (Dj.inp[1]) */ + if ( QueryTrack(Dj.inp[1].trk,Q_REFRESH_JOIN_PARAMS_ON_MOVE) ) { + if ( !GetTrackParams( PARAMS_2ND_JOIN, Dj.inp[1].trk, pos, &Dj.inp[1].params ) ) + return C_CONTINUE; + } + beyond = 1.0; + switch ( Dj.inp[1].params.type ) { + case curveTypeCurve: + normalAngle = FindAngle( Dj.inp[1].params.arcP, pos ); + Dj.inp[1].params.angle = NormalizeAngle( normalAngle + + ((Dj.inp[1].params.ep==0)?-90.0:90.0)); + PointOnCircle( &Dj.inp[1].pos, Dj.inp[1].params.arcP, + Dj.inp[1].params.arcR, normalAngle ); + if (Dj.inp[0].params.ep == Dj.inp[1].params.ep) + normalAngle = NormalizeAngle( normalAngle + 180.0 ); + break; + case curveTypeStraight: + FindPos( &off, &beyond, pos, Dj.inp[1].params.lineOrig, Dj.inp[1].params.angle, + 100000 ); + Translate( &Dj.inp[1].pos, Dj.inp[1].params.lineOrig, Dj.inp[1].params.angle, + off.x ); + normalAngle = NormalizeAngle( Dj.inp[1].params.angle + + ((Dj.inp[0].params.ep==0)?-90.0:90.0) ); + break; + case curveTypeNone: + break; + } + +/* Compute the radius of the 2 tracks, for ComputeE() */ + for (inx=0;inx<2;inx++) + if (Dj.inp[inx].params.type == curveTypeCurve) { + eR[inx] = Dj.inp[inx].params.arcR; + if (Dj.inp[inx].params.ep == inx) + eR[inx] = - eR[inx]; + } else + eR[inx] = 0.0; + + if (!AdjustJoint( FALSE, 0.0, eR, normalAngle )) + goto errorReturn; + /*return C_CONTINUE;*/ + + if (beyond < -0.000001) { +#ifdef VERBOSE +printf("pos=[%0.3f,%0.3f] lineOrig=[%0.3f,%0.3f], angle=%0.3f = off=[%0.3f,%0.3f], beyond=%0.3f\n", +pos.x, pos.y, Dj.inp[1].params.lineOrig.x, Dj.inp[1].params.lineOrig.y, Dj.inp[1].params.angle, off.x, off.y, beyond ); +#endif + InfoMessage( _("Beyond end of 2nd track") ); + goto errorReturn; + } + Dj.inp_pos[0] = Dj.jRes.pos[0]; + Dj.inp_pos[1] = Dj.jRes.pos[1]; + +LOG( log_join, 3, (" -E POS0=[%0.3f %0.3f] POS1=[%0.3f %0.3f]\n", + Dj.jRes.pos[0].x, Dj.jRes.pos[0].y, + Dj.jRes.pos[1].x, Dj.jRes.pos[1].y ) ) + + if ( Dj.jointD[0].x!=0.0 || Dj.jointD[1].x!=0.0 ) { + +/* Compute the transition-curve, hopefully twice is enough */ + a1 = Dj.inp[1].params.angle + (Dj.jointD[1].negate?-90.0:+90.0); + if ((!AdjustJoint( TRUE, a1, eR, normalAngle )) || + (!AdjustJoint( TRUE, a1, eR, normalAngle )) ) + goto errorReturn; + /*return C_CONTINUE;*/ + + if (logTable(log_join).level >= 3) { + Translate( &p1, Dj.jRes.pos[1], a1+180.0, Dj.jointD[1].x ); + LogPrintf(" X0=%0.3f, P1=[%0.3f %0.3f]\n", + FindDistance( Dj.inp_pos[0], Dj.jRes.pos[0] ), p1.x, p1.y ); + LogPrintf(" E+ POS0=[%0.3f %0.3f]..[%0.3f %0.3f] POS1=[%0.3f %0.3f]..[%0.3f %0.3f]\n", + Dj.inp_pos[0].x, Dj.inp_pos[0].y, + Dj.jRes.pos[0].x, Dj.jRes.pos[0].y, + p1.x, p1.y, Dj.jRes.pos[1].x, Dj.jRes.pos[1].y ); + } + } + + switch ( Dj.inp[0].params.type ) { + case curveTypeStraight: + FindPos( &off, &beyond, Dj.inp_pos[0], Dj.inp[0].params.lineOrig, + Dj.inp[0].params.angle, 100000.0 ); + if (beyond < 0.0) { + InfoMessage(_("Beyond end of 1st track")); + goto errorReturn; + /*Dj.jRes.type = curveTypeNone; + return C_CONTINUE;*/ + } + d = FindDistance( Dj.inp_pos[0], Dj.inp[0].params.lineOrig ); + break; + case curveTypeCurve: + if (IsCurveCircle(Dj.inp[0].trk)) { + d = 10000.0; + } else { + a = FindAngle( Dj.inp[0].params.arcP, Dj.inp_pos[0] ); + if (Dj.inp[0].params.ep == 0) + a1 = NormalizeAngle( Dj.inp[0].params.arcA0+Dj.inp[0].params.arcA1-a ); + else + a1 = NormalizeAngle( a-Dj.inp[0].params.arcA0 ); + d = Dj.inp[0].params.arcR * a1 * 2.0*M_PI/360.0; + } + break; + default: + AbortProg( "cmdJoin - unknown type[0]" ); + } + d -= Dj.jointD[0].d0; + if ( d <= minLength ) { + ErrorMessage( MSG_TRK_TOO_SHORT, _("First "), PutDim(fabs(minLength-d)) ); + goto errorReturn; + /*Dj.jRes.type = curveTypeNone; + return C_CONTINUE;*/ + } + + switch ( Dj.inp[1].params.type ) { + case curveTypeStraight: + d = FindDistance( Dj.inp_pos[1], Dj.inp[1].params.lineOrig ); + break; + case curveTypeCurve: + if (IsCurveCircle(Dj.inp[1].trk)) { + d = 10000.0; + } else { + a = FindAngle( Dj.inp[1].params.arcP, Dj.inp_pos[1] ); + if (Dj.inp[1].params.ep == 0) + a1 = NormalizeAngle( Dj.inp[1].params.arcA0+Dj.inp[1].params.arcA1-a ); + else + a1 = NormalizeAngle( a-Dj.inp[1].params.arcA0 ); + d = Dj.inp[1].params.arcR * a1 * 2.0*M_PI/360.0; + } + break; + default: + AbortProg( "cmdJoin - unknown type[1]" ); + } + d -= Dj.jointD[1].d0; + if ( d <= minLength ) { + ErrorMessage( MSG_TRK_TOO_SHORT, _("Second "), PutDim(fabs(minLength-d)) ); + goto errorReturn; + /*Dj.jRes.type = curveTypeNone; + return C_CONTINUE;*/ + } + + l = Dj.jointD[0].d0 + Dj.jointD[1].d0; + if ( l > 0.0 ) { + if ( Dj.jRes.type == curveTypeCurve ) { + d = Dj.jRes.arcR * Dj.jRes.arcA1 * 2.0*M_PI/360.0; + } else if ( Dj.jRes.type == curveTypeStraight ) { + d = FindDistance( Dj.jRes.pos[0], Dj.jRes.pos[1] ); + } + if ( d < l ) { + ErrorMessage( MSG_TRK_TOO_SHORT, _("Connecting "), PutDim(fabs(minLength-d)) ); + goto errorReturn; + /*Dj.jRes.type = curveTypeNone; + return C_CONTINUE;*/ + } + } + +/* Setup temp track */ + for ( ep=0; ep<2; ep++ ) { + switch( Dj.inp[ep].params.type ) { + case curveTypeCurve: + tempSegs(tempSegs_da.cnt).type = SEG_CRVTRK; + tempSegs(tempSegs_da.cnt).u.c.center = Dj.inp[ep].params.arcP; + tempSegs(tempSegs_da.cnt).u.c.radius = Dj.inp[ep].params.arcR; + if (IsCurveCircle( Dj.inp[ep].trk )) + break; + a = FindAngle( Dj.inp[ep].params.arcP, Dj.inp_pos[ep] ); + a1 = NormalizeAngle( a-Dj.inp[ep].params.arcA0 ); + if (a1 <= Dj.inp[ep].params.arcA1) + break; + if (Dj.inp[ep].params.ep == 0) { + tempSegs(tempSegs_da.cnt).u.c.a0 = a; + tempSegs(tempSegs_da.cnt).u.c.a1 = NormalizeAngle(Dj.inp[ep].params.arcA0-a); + } else { + tempSegs(tempSegs_da.cnt).u.c.a0 = Dj.inp[ep].params.arcA0+Dj.inp[ep].params.arcA1; + tempSegs(tempSegs_da.cnt).u.c.a1 = a1-Dj.inp[ep].params.arcA1; + } + tempSegs_da.cnt++; + break; + case curveTypeStraight: + if ( FindDistance( Dj.inp[ep].params.lineOrig, Dj.inp[ep].params.lineEnd ) < + FindDistance( Dj.inp[ep].params.lineOrig, Dj.inp_pos[ep] ) ) { + tempSegs(tempSegs_da.cnt).type = SEG_STRTRK; + tempSegs(tempSegs_da.cnt).u.l.pos[0] = Dj.inp[ep].params.lineEnd; + tempSegs(tempSegs_da.cnt).u.l.pos[1] = Dj.inp_pos[ep]; + tempSegs_da.cnt++; + } + break; + default: + ; + } + } + + ok = TRUE; +errorReturn: + if (!ok) + tempSegs(tempSegs_da.cnt).color = drawColorRed; + switch( Dj.jRes.type ) { + case curveTypeCurve: + tempSegs(tempSegs_da.cnt).type = SEG_CRVTRK; + tempSegs(tempSegs_da.cnt).u.c.center = Dj.jRes.arcP; + tempSegs(tempSegs_da.cnt).u.c.radius = Dj.jRes.arcR; + tempSegs(tempSegs_da.cnt).u.c.a0 = Dj.jRes.arcA0; + tempSegs(tempSegs_da.cnt).u.c.a1 = Dj.jRes.arcA1; + tempSegs_da.cnt++; + break; + case curveTypeStraight: + tempSegs(tempSegs_da.cnt).type = SEG_STRTRK; + tempSegs(tempSegs_da.cnt).u.l.pos[0] = Dj.jRes.pos[0]; + tempSegs(tempSegs_da.cnt).u.l.pos[1] = Dj.jRes.pos[1]; + tempSegs_da.cnt++; + break; + case curveTypeNone: + tempSegs_da.cnt = 0; + break; + default: + AbortProg( "Bad track type %d", Dj.jRes.type ); + } + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, drawColorBlack ); + if (!ok) + Dj.jRes.type = curveTypeNone; + return C_CONTINUE; + + case C_UP: + if (Dj.state == 0) + return C_CONTINUE; + if (Dj.state == 1) { + InfoMessage( _("Select 2nd track") ); + return C_CONTINUE; + } + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, drawColorBlack ); + tempSegs(0).color = drawColorBlack; + tempSegs_da.cnt = 0; + if (Dj.jRes.type == curveTypeNone) { + Dj.state = 1; + DrawFillCircle( &tempD, Dj.inp[0].pos, 0.10*mainD.scale, selectedColor ); + InfoMessage( _("Select 2nd track") ); + return C_CONTINUE; + } + UndoStart( _("Join Tracks"), "newJoin" ); + switch (Dj.jRes.type) { + case curveTypeStraight: + trk = NewStraightTrack( Dj.jRes.pos[0], Dj.jRes.pos[1] ); + Dj.jRes.flip = FALSE; + break; + case curveTypeCurve: + trk = NewCurvedTrack( Dj.jRes.arcP, Dj.jRes.arcR, + Dj.jRes.arcA0, Dj.jRes.arcA1, 0 ); + break; + case curveTypeNone: + return C_CONTINUE; + } + + CopyAttributes( Dj.inp[0].trk, trk ); + UndrawNewTrack( Dj.inp[0].trk ); + UndrawNewTrack( Dj.inp[1].trk ); + ep = Dj.jRes.flip?1:0; + Dj.state = 0; + rc = C_TERMINATE; + if ( (!JoinTracks( Dj.inp[0].trk, Dj.inp[0].params.ep, Dj.inp_pos[0], + trk, ep, Dj.jRes.pos[0], &Dj.jointD[0] ) ) || + (!JoinTracks( Dj.inp[1].trk, Dj.inp[1].params.ep, Dj.inp_pos[1], + trk, 1-ep, Dj.jRes.pos[1], &Dj.jointD[1] ) ) ) + rc = C_ERROR; + + UndoEnd(); + DrawNewTrack( Dj.inp[0].trk ); + DrawNewTrack( Dj.inp[1].trk ); + DrawNewTrack( trk ); + return rc; + +#ifdef LATER + case C_LCLICK: + if ( (MyGetKeyState() & WKEY_SHIFT) == 0 ) { + rc = CmdJoin( C_DOWN, pos ); + if (rc == C_TERMINATE) + return rc; + return CmdJoin( C_UP, pos ); + } + if ( selectedTrackCount <= 0 ) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return C_CONTINUE; + } + if ( (Dj.inp[Dj.joinMoveState].trk = OnTrack( &pos, TRUE, TRUE )) == NULL ) + return C_CONTINUE; + if (!CheckTrackLayer( Dj.inp[Dj.joinMoveState].trk ) ) + return C_CONTINUE; + Dj.inp[Dj.joinMoveState].params.ep = PickUnconnectedEndPoint( pos, Dj.inp[Dj.joinMoveState].trk ); /* CHECKME */ + if ( Dj.inp[Dj.joinMoveState].params.ep == -1 ) { +#ifdef LATER + ErrorMessage( MSG_NO_ENDPTS ); +#endif + return C_CONTINUE; + } +#ifdef LATER + if ( GetTrkEndTrk( Dj.inp[Dj.joinMoveState].trk, Dj.inp[Dj.joinMoveState].params.ep ) ) { + ErrorMessage( MSG_SEL_EP_CONN ); + return C_CONTINUE; + } +#endif + if (Dj.joinMoveState == 0) { + Dj.joinMoveState++; + InfoMessage( GetTrkSelected(Dj.inp[0].trk)? + _("Click on an unselected End-Point"): + _("Click on a selected End-Point") ); + return C_CONTINUE; + } + if ( GetTrkSelected(Dj.inp[0].trk) == GetTrkSelected(Dj.inp[1].trk) ) { + ErrorMessage( MSG_2ND_TRK_NOT_SEL_UNSEL, GetTrkSelected(Dj.inp[0].trk) + ? _("unselected") : _("selected") ); + return C_CONTINUE; + } + if (GetTrkSelected(Dj.inp[0].trk)) + MoveToJoin( Dj.inp[0].trk, Dj.inp[0].params.ep, Dj.inp[1].trk, Dj.inp[1].params.ep ); + else + MoveToJoin( Dj.inp[1].trk, Dj.inp[1].params.ep, Dj.inp[0].trk, Dj.inp[0].params.ep ); + Dj.joinMoveState = 0; + return C_TERMINATE; + break; +#endif + case C_CANCEL: + case C_REDRAW: + if ( Dj.joinMoveState == 1 || Dj.state == 1 ) { + DrawFillCircle( &tempD, Dj.inp[0].pos, 0.10*mainD.scale, selectedColor ); + } + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + break; + + + } + return C_CONTINUE; + +} + +/***************************************************************************** + * + * INITIALIZATION + * + */ + +#include "bitmaps/join.xpm" + +void InitCmdJoin( wMenu_p menu ) +{ + joinCmdInx = AddMenuButton( menu, CmdJoin, "cmdJoin", _("Join"), wIconCreatePixMap(join_xpm), LEVEL0_50, IC_STICKY|IC_POPUP, ACCL_JOIN, NULL ); + log_join = LogFindIndex( "join" ); +} + diff --git a/app/bin/cjoin.h b/app/bin/cjoin.h new file mode 100644 index 0000000..021e0a1 --- /dev/null +++ b/app/bin/cjoin.h @@ -0,0 +1,44 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cjoin.h,v 1.1 2005-12-07 15:47:39 rc-flyer 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. + */ + +#define E_NOTREQ (0) +#define E_REQ (1) +#define E_ERROR (2) + +typedef struct { + DIST_T x; + DIST_T r0, r1; + DIST_T l0, l1; + DIST_T d0, d1; + BOOL_T flip, negate, Scurve; + } easementData_t; + +extern DIST_T easementVal; +extern DIST_T easeR; +extern DIST_T easeL; + +STATUS_T ComputeJoint( DIST_T, DIST_T, easementData_t * ); +BOOL_T JoinTracks( track_p, EPINX_T, coOrd, track_p, EPINX_T, coOrd, easementData_t * ); +void UndoJoint( track_p, EPINX_T, track_p, EPINX_T ); +void DrawJointTrack( drawCmd_p, coOrd, ANGLE_T, DIST_T, DIST_T, DIST_T, DIST_T, BOOL_T, BOOL_T, BOOL_T, track_p, EPINX_T, EPINX_T, DIST_T, wDrawColor, long ); +DIST_T JointDistance( coOrd *, coOrd, ANGLE_T, DIST_T, DIST_T, DIST_T, DIST_T, BOOL_T, BOOL_T ); +coOrd GetJointSegEndPos( coOrd, ANGLE_T, DIST_T, DIST_T, DIST_T, DIST_T, BOOL_T, BOOL_T, BOOL_T, EPINX_T, ANGLE_T * ); diff --git a/app/bin/cmisc.c b/app/bin/cmisc.c new file mode 100644 index 0000000..fe8beea --- /dev/null +++ b/app/bin/cmisc.c @@ -0,0 +1,451 @@ +/** \file cmisc.c + * Handlimg of the 'Describe' dialog + * + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cmisc.c,v 1.7 2009-07-08 18:40:27 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 "track.h" +#include "common.h" +#include "i18n.h" + +/***************************************************************************** + * + * DESCRIPTION WINDOW + * + */ + + +EXPORT wIndex_t describeCmdInx; +EXPORT BOOL_T inDescribeCmd; + +static track_p descTrk; +static descData_p descData; +static descUpdate_t descUpdateFunc; +static coOrd descOrig, descSize; +static POS_T descBorder; +static wDrawColor descColor = 0; +static BOOL_T descUndoStarted; +static BOOL_T descNeedDrawHilite; +static wPos_t describeW_posy; +static wPos_t describeCmdButtonEnd; + + +static paramFloatRange_t rdata = { 0, 0, 100, PDO_NORANGECHECK_HIGH|PDO_NORANGECHECK_LOW }; +static paramIntegerRange_t idata = { 0, 0, 100, PDO_NORANGECHECK_HIGH|PDO_NORANGECHECK_LOW }; +static paramTextData_t tdata = { 300, 150 }; +static char * pivotLabels[] = { N_("First"), N_("Middle"), N_("Second"), NULL }; +static paramData_t describePLs[] = { +#define I_FLOAT_0 (0) + { PD_FLOAT, NULL, "F1", 0, &rdata }, + { PD_FLOAT, NULL, "F2", 0, &rdata }, + { PD_FLOAT, NULL, "F3", 0, &rdata }, + { PD_FLOAT, NULL, "F4", 0, &rdata }, + { PD_FLOAT, NULL, "F5", 0, &rdata }, + { PD_FLOAT, NULL, "F6", 0, &rdata }, + { PD_FLOAT, NULL, "F7", 0, &rdata }, + { PD_FLOAT, NULL, "F8", 0, &rdata }, + { PD_FLOAT, NULL, "F9", 0, &rdata }, + { PD_FLOAT, NULL, "F10", 0, &rdata }, + { PD_FLOAT, NULL, "F11", 0, &rdata }, + { PD_FLOAT, NULL, "F12", 0, &rdata }, + { PD_FLOAT, NULL, "F13", 0, &rdata }, + { PD_FLOAT, NULL, "F14", 0, &rdata }, + { PD_FLOAT, NULL, "F15", 0, &rdata }, + { PD_FLOAT, NULL, "F16", 0, &rdata }, + { PD_FLOAT, NULL, "F17", 0, &rdata }, + { PD_FLOAT, NULL, "F18", 0, &rdata }, + { PD_FLOAT, NULL, "F19", 0, &rdata }, + { PD_FLOAT, NULL, "F20", 0, &rdata }, +#define I_FLOAT_N I_FLOAT_0+20 + +#define I_LONG_0 I_FLOAT_N + { PD_LONG, NULL, "I1", 0, &idata }, + { PD_LONG, NULL, "I2", 0, &idata }, + { PD_LONG, NULL, "I3", 0, &idata }, + { PD_LONG, NULL, "I4", 0, &idata }, + { PD_LONG, NULL, "I5", 0, &idata }, +#define I_LONG_N I_LONG_0+5 + +#define I_STRING_0 I_LONG_N + { PD_STRING, NULL, "S1", 0, (void*)300 }, + { PD_STRING, NULL, "S2", 0, (void*)300 }, + { PD_STRING, NULL, "S3", 0, (void*)300 }, + { PD_STRING, NULL, "S4", 0, (void*)300 }, +#define I_STRING_N I_STRING_0+4 + +#define I_LAYER_0 I_STRING_N + { PD_DROPLIST, NULL, "Y1", 0, (void*)150, NULL, 0 }, +#define I_LAYER_N I_LAYER_0+1 + +#define I_COLOR_0 I_LAYER_N + { PD_COLORLIST, NULL, "C1", 0, NULL, N_("Color") }, +#define I_COLOR_N I_COLOR_0+1 + +#define I_LIST_0 I_COLOR_N + { PD_DROPLIST, NULL, "L1", 0, (void*)150, NULL, 0 }, + { PD_DROPLIST, NULL, "L2", 0, (void*)150, NULL, 0 }, +#define I_LIST_N I_LIST_0+2 + +#define I_EDITLIST_0 I_LIST_N + { PD_DROPLIST, NULL, "LE1", 0, (void*)150, NULL, BL_EDITABLE }, +#define I_EDITLIST_N I_EDITLIST_0+1 + +#define I_TEXT_0 I_EDITLIST_N + { PD_TEXT, NULL, "T1", 0, &tdata }, +#define I_TEXT_N I_TEXT_0+1 + +#define I_PIVOT_0 I_TEXT_N + { PD_RADIO, NULL, "P1", 0, pivotLabels, N_("Pivot"), BC_HORZ|BC_NOBORDER, 0 } +#define I_PIVOT_N I_PIVOT_0+1 + }; + +static paramGroup_t describePG = { "describe", 0, describePLs, sizeof describePLs/sizeof describePLs[0] }; + + +static void DrawDescHilite( void ) +{ + wPos_t x, y, w, h; + if ( descNeedDrawHilite == FALSE ) + return; + if (descColor==0) + descColor = wDrawColorGray(87); + w = (wPos_t)((descSize.x/mainD.scale)*mainD.dpi+0.5); + h = (wPos_t)((descSize.y/mainD.scale)*mainD.dpi+0.5); + mainD.CoOrd2Pix(&mainD,descOrig,&x,&y); + wDrawFilledRectangle( mainD.d, x, y, w, h, descColor, wDrawOptTemp ); +} + + + +static void DescribeUpdate( + paramGroup_p pg, + int inx, + void * data ) +{ + coOrd hi, lo; + descData_p ddp; + if ( inx < 0 ) + return; + ddp = (descData_p)pg->paramPtr[inx].context; + if ( (ddp->mode&(DESC_RO|DESC_IGNORE)) != 0 ) + return; + if ( ddp->type == DESC_PIVOT ) + return; + if ( (ddp->mode&DESC_NOREDRAW) == 0 ) + DrawDescHilite(); + if ( !descUndoStarted ) { + UndoStart( _("Change Track"), "Change Track" ); + descUndoStarted = TRUE; + } + UndoModify( descTrk ); + descUpdateFunc( descTrk, ddp-descData, descData, FALSE ); + if ( descTrk ) { + GetBoundingBox( descTrk, &hi, &lo ); + if ( OFF_D( mapD.orig, mapD.size, descOrig, descSize ) ) { + ErrorMessage( MSG_MOVE_OUT_OF_BOUNDS ); + } + } + if ( (ddp->mode&DESC_NOREDRAW) == 0 ) { + descOrig = lo; + descSize = hi; + descOrig.x -= descBorder; + descOrig.y -= descBorder; + descSize.x -= descOrig.x-descBorder; + descSize.y -= descOrig.y-descBorder; + DrawDescHilite(); + } + for ( inx = 0; inx < sizeof describePLs/sizeof describePLs[0]; inx++ ) { + if ( (describePLs[inx].option & PDO_DLGIGNORE) != 0 ) + continue; + ddp = (descData_p)describePLs[inx].context; + if ( (ddp->mode&DESC_IGNORE) != 0 ) + continue; + if ( (ddp->mode&DESC_CHANGE) == 0 ) + continue; + ddp->mode &= ~DESC_CHANGE; + ParamLoadControl( &describePG, inx ); + } +} + + +static void DescOk( void * junk ) +{ + wHide( describePG.win ); + if ( descTrk ) + DrawDescHilite(); + + descUpdateFunc( descTrk, -1, descData, !descUndoStarted ); + descTrk = NULL; + if (descUndoStarted) { + UndoEnd(); + descUndoStarted = FALSE; + } + descNeedDrawHilite = FALSE; + Reset(); +} + + +static struct { + parameterType pd_type; + long option; + int first; + int last; + } descTypeMap[] = { +/*NULL*/ { 0, 0 }, +/*POS*/ { PD_FLOAT, PDO_DIM, I_FLOAT_0, I_FLOAT_N }, +/*FLOAT*/ { PD_FLOAT, 0, I_FLOAT_0, I_FLOAT_N }, +/*ANGLE*/ { PD_FLOAT, PDO_ANGLE, I_FLOAT_0, I_FLOAT_N }, +/*LONG*/ { PD_LONG, 0, I_LONG_0, I_LONG_N }, +/*COLOR*/ { PD_LONG, 0, I_COLOR_0, I_COLOR_N }, +/*DIM*/ { PD_FLOAT, PDO_DIM, I_FLOAT_0, I_FLOAT_N }, +/*PIVOT*/ { PD_RADIO, 0, I_PIVOT_0, I_PIVOT_N }, +/*LAYER*/ { PD_DROPLIST,PDO_LISTINDEX, I_LAYER_0, I_LAYER_N }, +/*STRING*/ { PD_STRING,0, I_STRING_0, I_STRING_N }, +/*TEXT*/ { PD_TEXT, PDO_DLGNOLABELALIGN, I_TEXT_0, I_TEXT_N }, +/*LIST*/ { PD_DROPLIST, PDO_LISTINDEX, I_LIST_0, I_LIST_N }, +/*EDITABLELIST*/{ PD_DROPLIST, 0, I_EDITLIST_0, I_EDITLIST_N } }; + +static wControl_p AllocateButt( descData_p ddp, void * valueP, char * label, wPos_t sep ) +{ + int inx; + + for ( inx = descTypeMap[ddp->type].first; inx<descTypeMap[ddp->type].last; inx++ ) { + if ( (describePLs[inx].option & PDO_DLGIGNORE) != 0 ) { + describePLs[inx].option = descTypeMap[ddp->type].option; + if ( describeW_posy > describeCmdButtonEnd ) + describePLs[inx].option |= PDO_DLGUNDERCMDBUTT; + describeW_posy += wControlGetHeight( describePLs[inx].control ) + sep; + describePLs[inx].context = ddp; + describePLs[inx].valueP = valueP; + if ( label && ddp->type != DESC_TEXT ) { + wControlSetLabel( describePLs[inx].control, label ); + describePLs[inx].winLabel = label; + } + return describePLs[inx].control; + } + } + AbortProg( "allocateButt: can't find %d", ddp->type ); + return NULL; +} + + +static void DescribeLayout( + paramData_t * pd, + int inx, + wPos_t colX, + wPos_t * x, + wPos_t * y ) +{ + descData_p ddp; + wPos_t w, h; + + if ( inx < 0 ) + return; + if ( pd->context == NULL ) + return; + ddp = (descData_p)pd->context; + *y = ddp->posy; + if ( ddp->type == DESC_POS && + ddp->control0 != pd->control ) { + *y += wControlGetHeight( pd->control ) + 3; + } else if ( ddp->type == DESC_TEXT ) { + w = tdata.width; + h = tdata.height; + wTextSetSize( (wText_p)pd->control, w, h ); + } + wControlShow( pd->control, TRUE ); +} + + +/** + * Creation and modification of the Describe dialog box is handled here. As the number + * of values for a track element depends on the specific type, this dialog is dynamically + * updated to hsow the changable parameters only + * + * \param IN title Description of the selected part, shown in window title bar + * \param IN trk Track element to be described + * \param IN data + * \param IN update + * + */ +static wList_p setLayerL; +void DoDescribe( char * title, track_p trk, descData_p data, descUpdate_t update ) +{ + int inx; + descData_p ddp; + char * label; + int ro_mode; + + if (!inDescribeCmd) + return; + + descTrk = trk; + descData = data; + descUpdateFunc = update; + describeW_posy = 0; + if ( describePG.win == NULL ) { + /* SDB 5.13.2005 */ + ParamCreateDialog( &describePG, _("Description"), _("Done"), DescOk, + (paramActionCancelProc) DescribeCancel, + TRUE, DescribeLayout, F_RECALLPOS, + DescribeUpdate ); + describeCmdButtonEnd = wControlBelow( (wControl_p)describePG.helpB ); + } + for ( inx=0; inx<sizeof describePLs/sizeof describePLs[0]; inx++ ) { + describePLs[inx].option = PDO_DLGIGNORE; + wControlShow( describePLs[inx].control, FALSE ); + } + ro_mode = (GetLayerFrozen(GetTrkLayer(trk))?DESC_RO:0); + if (ro_mode) + for ( ddp=data; ddp->type != DESC_NULL; ddp++ ) { + if ( ddp->mode&DESC_IGNORE ) + continue; + ddp->mode |= ro_mode; + } + for ( ddp=data; ddp->type != DESC_NULL; ddp++ ) { + if ( ddp->mode&DESC_IGNORE ) + continue; + label = _(ddp->label); + + ddp->posy = describeW_posy; + ddp->control0 = AllocateButt( ddp, ddp->valueP, label, (ddp->type == DESC_POS?3:3) ); + wControlActive( ddp->control0, ((ddp->mode|ro_mode)&DESC_RO)==0 ); + switch (ddp->type) { + case DESC_POS: + ddp->control1 = AllocateButt( ddp, + &((coOrd*)(ddp->valueP))->y, + "Y", + 3 ); + wControlActive( ddp->control1, ((ddp->mode|ro_mode)&DESC_RO)==0 ); + break; + case DESC_LAYER: + wListClear(ddp->control0); // Rebuild list on each invovation + for ( inx = 0; inx<NUM_LAYERS; inx++ ) { + if (!GetLayerFrozen(inx)) // Avoid Frozen layers. + { + sprintf( message, "%2d : %s", inx+1, GetLayerName(inx) ); + wListAddValue( ddp->control0, message, NULL, (void*)inx ); + } + } + break; + default: + break; + } + } + ParamLayoutDialog( &describePG ); + ParamLoadControls( &describePG ); + sprintf( message, "%s (T%d)", title, GetTrkIndex(trk) ); + wWinSetTitle( describePG.win, message ); + wShow( describePG.win ); +} + + +static void DescChange( long changes ) +{ + if ( (changes&CHANGE_UNITS) && describePG.win && wWinIsVisible(describePG.win) ) + ParamLoadControls( &describePG ); +} + +/***************************************************************************** + * + * SIMPLE DESCRIPTION + * + */ + + +EXPORT void DescribeCancel( void ) +{ + if ( describePG.win && wWinIsVisible(describePG.win) ) { + if ( descTrk ) { + descUpdateFunc( descTrk, -1, descData, TRUE ); + descTrk = NULL; + DrawDescHilite(); + } + wHide( describePG.win ); + if ( descUndoStarted ) { + UndoEnd(); + descUndoStarted = FALSE; + } + } + descNeedDrawHilite = FALSE; +} + + +static STATUS_T CmdDescribe( wAction_t action, coOrd pos ) +{ + track_p trk; + char msg[STR_SIZE]; + + switch (action) { + case C_START: + InfoMessage( _("Select track to describe") ); + descUndoStarted = FALSE; + return C_CONTINUE; + + case C_DOWN: + if ((trk = OnTrack( &pos, FALSE, FALSE )) != NULL) { + if ( describePG.win && wWinIsVisible(describePG.win) && descTrk ) { + DrawDescHilite(); + descUpdateFunc( descTrk, -1, descData, TRUE ); + descTrk = NULL; + } + descBorder = mainD.scale*0.1; + if ( descBorder < trackGauge ) + descBorder = trackGauge; + inDescribeCmd = TRUE; + GetBoundingBox( trk, &descSize, &descOrig ); + descOrig.x -= descBorder; + descOrig.y -= descBorder; + descSize.x -= descOrig.x-descBorder; + descSize.y -= descOrig.y-descBorder; + descNeedDrawHilite = TRUE; + DrawDescHilite(); + DescribeTrack( trk, msg, 255 ); + inDescribeCmd = FALSE; + InfoMessage( msg ); + } else + InfoMessage( "" ); + return C_CONTINUE; + + case C_REDRAW: + if (describePG.win && wWinIsVisible(describePG.win) && descTrk) + DrawDescHilite(); + break; + + case C_CANCEL: + DescribeCancel(); + return C_CONTINUE; + } + return C_CONTINUE; +} + + + +#include "bitmaps/describe.xpm" + +void InitCmdDescribe( wMenu_p menu ) +{ + describeCmdInx = AddMenuButton( menu, CmdDescribe, "cmdDescribe", _("Properties"), wIconCreatePixMap(describe_xpm), + LEVEL0, IC_CANCEL|IC_POPUP, ACCL_DESCRIBE, NULL ); + RegisterChangeNotification( DescChange ); + ParamRegister( &describePG ); + /*AddPlaybackProc( "DESCRIBE", playbackDescribe, NULL );*/ +} diff --git a/app/bin/cmisc2.c b/app/bin/cmisc2.c new file mode 100644 index 0000000..c9daad2 --- /dev/null +++ b/app/bin/cmisc2.c @@ -0,0 +1,54 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cmisc2.c,v 1.4 2008-03-10 18:59:53 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 "track.h" +#include "i18n.h" + +/***************************************************************************** + * + * MISC2 + * + */ + + +static STATUS_T CmdBridge( wAction_t action, coOrd pos ) +{ + switch (action) { + case C_DOWN: + return C_INFO; + break; + } + return C_INFO; +} + + + + + +#include "bitmaps/bridge.xbm" + +void InitCmdMisc2( wMenu_p menu ) +{ + if (extraButtons) { + InitCommand( menu, CmdBridge, N_("Bridge"), bridge_bits, LEVEL2, IC_STICKY, ACCL_BRIDGE ); + } +} diff --git a/app/bin/cmodify.c b/app/bin/cmodify.c new file mode 100644 index 0000000..89fd548 --- /dev/null +++ b/app/bin/cmodify.c @@ -0,0 +1,407 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cmodify.c,v 1.4 2008-03-06 19:35:05 m_fischer Exp $ + * + * TRACK MODIFY + */ + +/* 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 "track.h" +#include "cjoin.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "i18n.h" + +/***************************************************************************** + * + * MODIFY + * + */ + + +static struct { + track_p Trk; + trackParams_t params; + coOrd pos00, pos00x, pos01; + ANGLE_T angle; + curveData_t curveData; + easementData_t jointD; + DIST_T r1; + BOOL_T valid; + BOOL_T first; + } Dex; + + +static int log_modify; + +static STATUS_T CmdModify( + wAction_t action, + coOrd pos ) +/* + * Extend a track with a curve or straight. + */ +{ + + track_p trk, trk1; + ANGLE_T a0; + DIST_T d; + ANGLE_T a; + EPINX_T inx; + curveType_e curveType; + static BOOL_T changeTrackMode; + static BOOL_T modifyRulerMode; + + STATUS_T rc; + static DIST_T trackGauge; + + if ( changeTrackMode ) { + if ( action == C_MOVE ) + action = C_RMOVE; + if ( action == C_UP ) + action = C_RUP; + } + + switch (action&0xFF) { + + case C_START: + InfoMessage( _("Select track to modify") ); + Dex.Trk = NULL; + tempSegs_da.cnt = 0; + /*ChangeParameter( &easementPD );*/ + trackGauge = 0.0; + changeTrackMode = modifyRulerMode = FALSE; + return C_CONTINUE; + + case C_DOWN: + changeTrackMode = modifyRulerMode = FALSE; + DYNARR_SET( trkSeg_t, tempSegs_da, 2 ); + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).width = 0; + tempSegs(1).color = wDrawColorBlack; + tempSegs(1).width = 0; + tempSegs_da.cnt = 0; + SnapPos( &pos ); + Dex.Trk = OnTrack( &pos, TRUE, FALSE ); + if (Dex.Trk == NULL) { + if ( ModifyRuler( C_DOWN, pos ) == C_CONTINUE ) + modifyRulerMode = TRUE; + return C_CONTINUE; + } + if (!CheckTrackLayer( Dex.Trk ) ) { + Dex.Trk = NULL; + return C_CONTINUE; + } + trackGauge = (IsTrack(Dex.Trk)?GetTrkGauge(Dex.Trk):0.0); + if ( (MyGetKeyState()&WKEY_SHIFT) && + QueryTrack( Dex.Trk, Q_CAN_MODIFYRADIUS ) && + (inx=PickUnconnectedEndPoint(pos,Dex.Trk)) >= 0 ) { + trk = Dex.Trk; + while ( (trk1=GetTrkEndTrk(trk,1-inx)) && + QueryTrack(trk1, Q_CANNOT_BE_ON_END) ) { + inx = GetEndPtConnectedToMe( trk1, trk ); + trk = trk1; + } + if (trk1) { + UndoStart( _("Change Track"), "Change( T%d[%d] )", GetTrkIndex(Dex.Trk), Dex.params.ep ); + inx = GetEndPtConnectedToMe( trk1, trk ); + Dex.Trk = NULL; + DeleteTrack(trk, TRUE); + if ( !GetTrkEndTrk( trk1, inx ) ) { + Dex.Trk = trk1; + Dex.pos00 = GetTrkEndPos( Dex.Trk, inx ); + changeTrackMode = TRUE; + goto CHANGE_TRACK; + } + } + ErrorMessage( MSG_CANNOT_CHANGE ); + } + rc = ModifyTrack( Dex.Trk, C_DOWN, pos ); + if ( rc != C_CONTINUE ) { + Dex.Trk = NULL; + rc = C_CONTINUE; + } + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + MainRedraw(); + return rc; + + case C_MOVE: + if ( modifyRulerMode ) + return ModifyRuler( C_MOVE, pos ); + if (Dex.Trk == NULL) + return C_CONTINUE; + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + tempSegs_da.cnt = 0; + SnapPos( &pos ); + rc = ModifyTrack( Dex.Trk, C_MOVE, pos ); + if ( rc != C_CONTINUE ) { + rc = C_CONTINUE; + Dex.Trk = NULL; + } + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + MainRedraw(); + return rc; + + + case C_UP: + if (Dex.Trk == NULL) + return C_CONTINUE; + if ( modifyRulerMode ) + return ModifyRuler( C_MOVE, pos ); + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + tempSegs_da.cnt = 0; + SnapPos( &pos ); + UndoStart( _("Modify Track"), "Modify( T%d[%d] )", GetTrkIndex(Dex.Trk), Dex.params.ep ); + UndoModify( Dex.Trk ); + rc = ModifyTrack( Dex.Trk, C_UP, pos ); + UndoEnd(); + //changeTrackMode = FALSE; + Dex.Trk = NULL; + MainRedraw(); + + return rc; + + case C_RDOWN: + changeTrackMode = TRUE; + modifyRulerMode = FALSE; + Dex.Trk = OnTrack( &pos, TRUE, TRUE ); + if (Dex.Trk) { + if (!CheckTrackLayer( Dex.Trk ) ) { + Dex.Trk = NULL; + return C_CONTINUE; + } + trackGauge = GetTrkGauge( Dex.Trk ); + Dex.pos00 = pos; +CHANGE_TRACK: + if (GetTrackParams( PARAMS_EXTEND, Dex.Trk, Dex.pos00, &Dex.params)) { + if (Dex.params.ep == -1) { + Dex.Trk = NULL; + return C_CONTINUE; + break; + } + if (Dex.params.ep == 0) { + Dex.params.arcR = -Dex.params.arcR; + } + Dex.pos00 = GetTrkEndPos(Dex.Trk,Dex.params.ep); + Dex.angle = GetTrkEndAngle( Dex.Trk,Dex.params.ep); + Translate( &Dex.pos00x, Dex.pos00, Dex.angle, 10.0 ); +LOG( log_modify, 1, ("extend endPt[%d] = [%0.3f %0.3f] A%0.3f\n", + Dex.params.ep, Dex.pos00.x, Dex.pos00.y, Dex.angle ) ) + InfoMessage( _("Drag to create new track segment") ); + } else { + return C_ERROR; + } + } + Dex.first = TRUE; + MainRedraw(); +#ifdef LATER + return C_CONTINUE; +#endif + + case C_RMOVE: + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + tempSegs_da.cnt = 0; + Dex.valid = FALSE; + if (Dex.Trk == NULL) return C_CONTINUE; + SnapPos( &pos ); + if ( Dex.first && FindDistance( pos, Dex.pos00 ) <= minLength ) + return C_CONTINUE; + Dex.first = FALSE; + Dex.pos01 = Dex.pos00; + PlotCurve( crvCmdFromEP1, Dex.pos00, Dex.pos00x, pos, &Dex.curveData, TRUE ); + curveType = Dex.curveData.type; + if ( curveType == curveTypeStraight ) { + Dex.r1 = 0.0; + if (Dex.params.type == curveTypeCurve) { + if (ComputeJoint( Dex.params.arcR, Dex.r1, &Dex.jointD ) == E_ERROR) + return C_CONTINUE; + d = Dex.params.len - Dex.jointD.d0; + if (d <= minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, "First ", PutDim(fabs(minLength-d)) ); + return C_CONTINUE; + } + a0 = Dex.angle + (Dex.jointD.negate?-90.0:+90.0); + Translate( &Dex.pos01, Dex.pos00, a0, Dex.jointD.x ); + Translate( &Dex.curveData.pos1, Dex.curveData.pos1, + a0, Dex.jointD.x ); +LOG( log_modify, 2, ("A=%0.3f X=%0.3f\n", a0, Dex.jointD.x ) ) + } else { + Dex.jointD.d1 = 0.0; + } + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).width = 0; + tempSegs(0).u.l.pos[0] = Dex.pos01; + tempSegs(0).u.l.pos[1] = Dex.curveData.pos1; + d = FindDistance( Dex.pos01, Dex.curveData.pos1 ); + d -= Dex.jointD.d1; + if (d <= minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, "Extending ", PutDim(fabs(minLength-d)) ); + return C_CONTINUE; + } + tempSegs_da.cnt = 1; + Dex.valid = TRUE; + if (action != C_RDOWN) + InfoMessage( _("Straight Track: Length=%s Angle=%0.3f"), + FormatDistance( FindDistance( Dex.curveData.pos1, Dex.pos01 ) ), + PutAngle( FindAngle( Dex.pos01, Dex.curveData.pos1 ) ) ); + } else if ( curveType == curveTypeNone ) { + if (action != C_RDOWN) + InfoMessage( _("Back") ); + return C_CONTINUE; + } else if ( curveType == curveTypeCurve ) { + Dex.r1 = Dex.curveData.curveRadius; + if ( easeR > 0.0 && Dex.r1 < easeR ) { + ErrorMessage( MSG_RADIUS_LSS_EASE_MIN, + FormatDistance( Dex.r1 ), FormatDistance( easeR ) ); + return C_CONTINUE; + } + if ( Dex.r1*2.0*M_PI*Dex.curveData.a1/360.0 > mapD.size.x+mapD.size.y ) { + ErrorMessage( MSG_CURVE_TOO_LARGE ); + return C_CONTINUE; + } + if ( NormalizeAngle( FindAngle( Dex.pos00, pos ) - Dex.angle ) > 180.0 ) + Dex.r1 = -Dex.r1; + if ( QueryTrack( Dex.Trk, Q_IGNORE_EASEMENT_ON_EXTEND ) ) { + /* Ignore easements when extending turnouts */ + Dex.jointD.x = + Dex.jointD.r0 = Dex.jointD.r1 = + Dex.jointD.l0 = Dex.jointD.l1 = + Dex.jointD.d0 = Dex.jointD.d1 = 0.0; + Dex.jointD.flip = Dex.jointD.negate = Dex.jointD.Scurve = FALSE; + } else { + if (ComputeJoint( Dex.params.arcR, Dex.r1, &Dex.jointD ) == E_ERROR) + return C_CONTINUE; + d = Dex.params.len - Dex.jointD.d0; + if (d <= minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, "First ", PutDim(fabs(minLength-d)) ); + return C_CONTINUE; + } + } + d = Dex.curveData.curveRadius * Dex.curveData.a1 * 2.0*M_PI/360.0; + d -= Dex.jointD.d1; + if (d <= minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, "Extending ", PutDim(fabs(minLength-d)) ); + return C_CONTINUE; + } + a0 = Dex.angle + (Dex.jointD.negate?-90.0:+90.0); + Translate( &Dex.pos01, Dex.pos00, a0, Dex.jointD.x ); + Translate( &Dex.curveData.curvePos, Dex.curveData.curvePos, + a0, Dex.jointD.x ); +LOG( log_modify, 2, ("A=%0.3f X=%0.3f\n", a0, Dex.jointD.x ) ) + tempSegs(0).type = SEG_CRVTRK; + tempSegs(0).width = 0; + tempSegs(0).u.c.center = Dex.curveData.curvePos; + tempSegs(0).u.c.radius = Dex.curveData.curveRadius, + tempSegs(0).u.c.a0 = Dex.curveData.a0; + tempSegs(0).u.c.a1 = Dex.curveData.a1; + tempSegs_da.cnt = 1; + d = D2R(Dex.curveData.a1); + if (d < 0.0) + d = 2*M_PI + d; + a = NormalizeAngle( Dex.angle - FindAngle( Dex.pos00, Dex.curveData.curvePos ) ); + if ( a < 180.0 ) + a = NormalizeAngle( Dex.curveData.a0-90 ); + else + a = NormalizeAngle( Dex.curveData.a0+Dex.curveData.a1+90.0 ); + Dex.valid = TRUE; + if (action != C_RDOWN) + InfoMessage( _("Curve Track: Radius=%s Length=%s Angle=%0.3f"), + FormatDistance( Dex.curveData.curveRadius ), + FormatDistance( Dex.curveData.curveRadius * d), + Dex.curveData.a1 ); + } + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + MainRedraw(); + return C_CONTINUE; + + case C_RUP: + changeTrackMode = FALSE; + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + tempSegs_da.cnt = 0; + if (Dex.Trk == NULL) return C_CONTINUE; + if (!Dex.valid) + return C_CONTINUE; + UndoStart( _("Extend Track"), "Extend( T%d[%d] )", GetTrkIndex(Dex.Trk), Dex.params.ep ); + trk = NULL; + curveType = Dex.curveData.type; + + if ( curveType == curveTypeStraight ) { + if ( Dex.params.type == curveTypeStraight && Dex.params.len > 0 ) { + //UndrawNewTrack( Dex.Trk ); + UndoModify( Dex.Trk ); + AdjustStraightEndPt( Dex.Trk, Dex.params.ep, Dex.curveData.pos1 ); + UndoEnd(); + DrawNewTrack(Dex.Trk ); + MainRedraw(); + return C_TERMINATE; + } + trk = NewStraightTrack( Dex.pos01, Dex.curveData.pos1 ); + inx = 0; + + } else if ( curveType == curveTypeCurve ) { +LOG( log_modify, 1, ("A0 = %0.3f, A1 = %0.3f\n", + Dex.curveData.a0, Dex.curveData.a1 ) ) + trk = NewCurvedTrack( Dex.curveData.curvePos, Dex.curveData.curveRadius, + Dex.curveData.a0, Dex.curveData.a1, 0 ); + inx = PickUnconnectedEndPoint( Dex.pos01, trk ); + if (inx == -1) + return C_ERROR; + + } else { + return C_ERROR; + } + //UndrawNewTrack( Dex.Trk ); + CopyAttributes( Dex.Trk, trk ); + JoinTracks( Dex.Trk, Dex.params.ep, Dex.pos00, trk, inx, Dex.pos01, &Dex.jointD ); + UndoEnd(); + DrawNewTrack( trk ); + DrawNewTrack( Dex.Trk ); + Dex.Trk = NULL; + MainRedraw(); + return C_TERMINATE; + + case C_REDRAW: + if ( (!changeTrackMode) && Dex.Trk && !QueryTrack( Dex.Trk, Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK ) ) + UndrawNewTrack( Dex.Trk ); + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + return C_CONTINUE; + + case C_TEXT: + if ( !Dex.Trk ) + return C_CONTINUE; + return ModifyTrack( Dex.Trk, action, pos ); + + default: + return C_CONTINUE; + } +} + + +/***************************************************************************** + * + * INITIALIZATION + * + */ + +#include "bitmaps/extend.xpm" + +void InitCmdModify( wMenu_p menu ) +{ + modifyCmdInx = AddMenuButton( menu, CmdModify, "cmdModify", _("Modify"), wIconCreatePixMap(extend_xpm), LEVEL0_50, IC_STICKY|IC_POPUP, ACCL_MODIFY, NULL ); + log_modify = LogFindIndex( "modify" ); +} diff --git a/app/bin/cnote.c b/app/bin/cnote.c new file mode 100644 index 0000000..88c9986 --- /dev/null +++ b/app/bin/cnote.c @@ -0,0 +1,409 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cnote.c,v 1.6 2008-03-10 18:59:53 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 "track.h" +#include "i18n.h" + +/***************************************************************************** + * + * NOTE + * + */ + +static TRKTYP_T T_NOTE = -1; + +static wDrawBitMap_p note_bm; +struct extraData { + coOrd pos; + char * text; + }; + +extern BOOL_T inDescribeCmd; + +#define NOTEHIDE "CNOTE HIDE" +#define NOTEDONE "CNOTE DONE" + +static char * mainText = NULL; +static wWin_p noteW; + +static paramTextData_t noteTextData = { 300, 150 }; +static paramData_t notePLs[] = { +#define I_NOTETEXT (0) +#define noteT ((wText_p)notePLs[I_NOTETEXT].control) + { PD_TEXT, NULL, "text", PDO_DLGRESIZE, ¬eTextData } }; +static paramGroup_t notePG = { "note", 0, notePLs, sizeof notePLs/sizeof notePLs[0] }; + + +static track_p NewNote( wIndex_t index, coOrd p, long size ) +{ + track_p t; + struct extraData * xx; + t = NewTrack( index, T_NOTE, 0, sizeof *xx ); + xx = GetTrkExtraData(t); + xx->pos = p; + xx->text = (char*)MyMalloc( (int)size + 2 ); + SetBoundingBox( t, p, p ); + return t; +} + +void ClearNote( void ) +{ + if (mainText) { + MyFree(mainText); + mainText = NULL; + } +} + +static void NoteOk( void * junk ) +{ + int len; + if ( wTextGetModified(noteT) ) { + ClearNote(); + len = wTextGetSize( noteT ); + mainText = (char*)MyMalloc( len+2 ); + wTextGetText( noteT, mainText, len ); + if (mainText[len-1] != '\n') { + mainText[len++] = '\n'; + } + mainText[len] = '\0'; + } + wHide( noteW ); +} + + +void DoNote( void ) +{ + if ( noteW == NULL ) { + noteW = ParamCreateDialog( ¬ePG, MakeWindowTitle(_("Note")), _("Ok"), NoteOk, NULL, FALSE, NULL, F_RESIZE, NULL ); + } + wTextClear( noteT ); + wTextAppend( noteT, mainText?mainText:_("Replace this text with your layout notes") ); + wTextSetReadonly( noteT, FALSE ); + wShow( noteW ); +} + + + +/***************************************************************************** + * NOTE OBJECT + */ + +static void DrawNote( track_p t, drawCmd_p d, wDrawColor color ) +{ + struct extraData *xx = GetTrkExtraData(t); + coOrd p[4]; + DIST_T dist; + if (d->scale >= 16) + return; + if ( (d->funcs->options & wDrawOptTemp) == 0 ) { + DrawBitMap( d, xx->pos, note_bm, color ); + } else { + dist = 0.1*d->scale; + p[0].x = p[1].x = xx->pos.x-dist; + p[2].x = p[3].x = xx->pos.x+dist; + p[1].y = p[2].y = xx->pos.y-dist; + p[3].y = p[0].y = xx->pos.y+dist; + DrawLine( d, p[0], p[1], 0, color ); + DrawLine( d, p[1], p[2], 0, color ); + DrawLine( d, p[2], p[3], 0, color ); + DrawLine( d, p[3], p[0], 0, color ); + } +} + +static DIST_T DistanceNote( track_p t, coOrd * p ) +{ + struct extraData *xx = GetTrkExtraData(t); + DIST_T d; + d = FindDistance( *p, xx->pos ); + if (d < 1.0) + return d; + return 100000.0; +} + + +static struct { + coOrd pos; + } noteData; +typedef enum { OR, LY, TX } noteDesc_e; +static descData_t noteDesc[] = { +/*OR*/ { DESC_POS, N_("Position"), ¬eData.pos }, +/*LY*/ { DESC_LAYER, N_("Layer"), NULL }, +/*TX*/ { DESC_TEXT, NULL, NULL }, + { DESC_NULL } }; + +static void UpdateNote( track_p trk, int inx, descData_p descUpd, BOOL_T needUndoStart ) +{ + struct extraData *xx = GetTrkExtraData(trk); + int len; + + switch ( inx ) { + case OR: + UndrawNewTrack( trk ); + xx->pos = noteData.pos; + SetBoundingBox( trk, xx->pos, xx->pos ); + DrawNewTrack( trk ); + break; + case -1: + if ( wTextGetModified((wText_p)noteDesc[TX].control0) ) { + if ( needUndoStart ) + UndoStart( _("Change Track"), "Change Track" ); + UndoModify( trk ); + MyFree( xx->text ); + len = wTextGetSize( (wText_p)noteDesc[TX].control0 ); + xx->text = (char*)MyMalloc( len+2 ); + wTextGetText( (wText_p)noteDesc[TX].control0, xx->text, len ); + if (xx->text[len-1] != '\n') { + xx->text[len++] = '\n'; + } + xx->text[len] = '\0'; + } + break; + default: + break; + } +} + + +static void DescribeNote( track_p trk, char * str, CSIZE_T len ) +{ + struct extraData * xx = GetTrkExtraData(trk); + + strcpy( str, _("Note: ") ); + len -= strlen(_("Note: ")); + str += strlen(_("Note: ")); + strncpy( str, xx->text, len ); + for (;*str;str++) { + if (*str=='\n') + *str = ' '; + } + noteData.pos = xx->pos; + noteDesc[TX].valueP = xx->text; + noteDesc[OR].mode = 0; + noteDesc[TX].mode = 0; + noteDesc[LY].mode = DESC_RO; + DoDescribe( _("Note"), trk, noteDesc, UpdateNote ); +} + +static void DeleteNote( track_p t ) +{ + struct extraData *xx = GetTrkExtraData(t); + if (xx->text) + MyFree( xx->text ); +} + +static BOOL_T WriteNote( track_p t, FILE * f ) +{ + struct extraData *xx = GetTrkExtraData(t); + int len; + BOOL_T addNL = FALSE; + BOOL_T rc = TRUE; + len = strlen(xx->text); + if ( xx->text[len-1] != '\n' ) { + len++; + addNL = TRUE; + } + rc &= fprintf(f, "NOTE %d %d 0 0 %0.6f %0.6f 0 %d\n", GetTrkIndex(t), GetTrkLayer(t), + xx->pos.x, xx->pos.y, len )>0; + rc &= fprintf(f, "%s%s", xx->text, addNL?"\n":"" )>0; + rc &= fprintf(f, " END\n")>0; + return rc; +} + + +static void ReadNote( char * line ) +{ + coOrd pos; + DIST_T elev; + CSIZE_T size; + char * cp; + track_p t; + struct extraData *xx; + int len; + wIndex_t index; + wIndex_t layer; + int lineCount; + + if ( strncmp( line, "NOTE MAIN", 9 ) == 0 ){ + if ( !GetArgs( line+9, paramVersion<3?"d":"0000d", &size ) ) + return; + if (mainText) + MyFree( mainText ); + mainText = (char*)MyMalloc( size+2 ); + cp = mainText; + } else { + if ( !GetArgs( line+5, paramVersion<3?"XXpYd":paramVersion<9?"dL00pYd":"dL00pfd", + &index, &layer, &pos, &elev, &size ) ) { + return; + } + t = NewNote( index, pos, size+2 ); + SetTrkLayer( t, layer ); + xx = GetTrkExtraData(t); + cp = xx->text; + } + lineCount = 0; + while (1) { + line = GetNextLine(); + if (strncmp(line, " END", 7) == 0) + break; + len = strlen(line); + if (size > 0 && size < len) { + InputError( "NOTE text overflow", TRUE ); + size = -1; + } + if (size > 0) { + if ( lineCount != 0 ) { + strcat( cp, "\n" ); + cp++; + size--; + } + strcpy( cp, line ); + cp += len; + size -= len; + } + lineCount++; + } + if (cp[-1] != '\n') + *cp++ = '\n'; + *cp = '\0'; +} + + +static void MoveNote( track_p trk, coOrd orig ) +{ + struct extraData * xx = GetTrkExtraData( trk ); + xx->pos.x += orig.x; + xx->pos.y += orig.y; + SetBoundingBox( trk, xx->pos, xx->pos ); +} + + +static void RotateNote( track_p trk, coOrd orig, ANGLE_T angle ) +{ + struct extraData * xx = GetTrkExtraData( trk ); + Rotate( &xx->pos, orig, angle ); + SetBoundingBox( trk, xx->pos, xx->pos ); +} + +static void RescaleNote( track_p trk, FLOAT_T ratio ) +{ + struct extraData * xx = GetTrkExtraData( trk ); + xx->pos.x *= ratio; + xx->pos.y *= ratio; +} + + +static trackCmd_t noteCmds = { + "NOTE", + DrawNote, + DistanceNote, + DescribeNote, + DeleteNote, + WriteNote, + ReadNote, + MoveNote, + RotateNote, + RescaleNote, + NULL, /* audit */ + NULL, /* getAngle */ + NULL, /* split */ + NULL, /* traverse */ + NULL, /* enumerate */ + NULL /* redraw */ }; + + +BOOL_T WriteMainNote( FILE* f ) +{ + BOOL_T rc = TRUE; + if (mainText && *mainText) { + rc &= fprintf(f, "NOTE MAIN 0 0 0 0 %d\n", strlen(mainText) )>0; + rc &= fprintf(f, "%s", mainText )>0; + rc &= fprintf(f, " END\n")>0; + } + return rc; +} + +/***************************************************************************** + * NOTE COMMAND + */ + + + +static STATUS_T CmdNote( wAction_t action, coOrd pos ) +{ + static coOrd oldPos; + track_p trk; + struct extraData * xx; + const char* tmpPtrText; + + switch (action) { + case C_START: + InfoMessage( _("Place a note on the layout") ); + return C_CONTINUE; + case C_DOWN: + DrawBitMap( &tempD, pos, note_bm, normalColor ); + oldPos = pos; + return C_CONTINUE; + case C_MOVE: + DrawBitMap( &tempD, oldPos, note_bm, normalColor ); + DrawBitMap( &tempD, pos, note_bm, normalColor ); + oldPos = pos; + return C_CONTINUE; + break; + case C_UP: + UndoStart( _("New Note"), "New Note" ); + trk = NewNote( -1, pos, 2 ); + DrawNewTrack( trk ); + xx = GetTrkExtraData(trk); + + tmpPtrText = _("Replace this text with your note"); + xx->text = (char*)MyMalloc( strlen(tmpPtrText) + 1 ); + strcpy( xx->text, tmpPtrText); + + inDescribeCmd = TRUE; + DescribeNote( trk, message, sizeof message ); + inDescribeCmd = FALSE; + return C_CONTINUE; + case C_REDRAW: + DrawBitMap( &tempD, oldPos, note_bm, normalColor ); + return C_CONTINUE; + case C_CANCEL: + DescribeCancel(); + return C_CONTINUE; + } + return C_INFO; +} + + +#include "bitmaps/note.xbm" +#include "bitmaps/cnote.xpm" + +void InitCmdNote( wMenu_p menu ) +{ + ParamRegister( ¬ePG ); + AddMenuButton( menu, CmdNote, "cmdNote", _("Note"), wIconCreatePixMap(cnote_xpm), LEVEL0_50, IC_POPUP2, ACCL_NOTE, NULL ); +} + +void InitTrkNote( void ) +{ + note_bm = wDrawBitMapCreate( mainD.d, note_width, note_width, 8, 8, note_bits ); + T_NOTE = InitObject( ¬eCmds ); +} diff --git a/app/bin/cnvdsgn.c b/app/bin/cnvdsgn.c new file mode 100644 index 0000000..6f75b11 --- /dev/null +++ b/app/bin/cnvdsgn.c @@ -0,0 +1,147 @@ +#include <stdio.h> +#include <math.h> +#include "common.h" +#include "utility.h" + +#include <string.h> +#include <stdlib.h> + + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2005 Dave Bullis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define GETMAXY \ + if (lp->y0 > maxY) maxY = lp->y0; \ + if (lp->y1 > maxY) maxY = lp->y1 + +static int trackSeparation = 20; +static int arrowHeadLength = 10; + +static double FindCenter( + coOrd * pos, + coOrd p0, + coOrd p1, + double radius ) +{ + double d; + double a0, a1; + d = FindDistance( p0, p1 )/2.0; + a0 = FindAngle( p0, p1 ); + a1 = NormalizeAngle(R2D(asin( d/radius ))); + if (a1 > 180) + a1 -= 360; + /*a0 = NormalizeAngle( a0 + (radius>0 ? +(90.0-a1) : -(90.0-a1) ) );*/ + a0 = NormalizeAngle( a0 + (90.0-a1) ); + Translate( pos, p0, a0, radius ); +/*fprintf(stderr,"Center = %0.3f %0.3f\n", pos->x, pos->y );*/ + return a1*2.0; +} + +static void buildDesignerLines( FILE * inf, FILE * outf ) +{ + char line[80]; + int j; + double num; + double radius; + coOrd p0, p1, q0, q1, pc; + double a0, a1; + double len; + + while ( fgets( line, sizeof line, inf ) != NULL ) { + + if ( strncmp( line, "ARROW", 5 ) == 0 ) { + if ( sscanf( line, "ARROW, %lf, %lf, %lf, %lf", + &p0.x, &p0.y, &p1.x, &p1.y ) != 4) { + fprintf( stderr, "SYNTAX: %s", line ); + exit (1); + } + a0 = FindAngle( p1, p0 ); + fprintf( outf, " { 1, %ld, %ld, %ld, %ld },\n", + (long)(p0.x+0.5), (long)(p0.y+0.5), (long)(p1.x+0.5), (long)(p1.y+0.5) ); + Translate( &p1, p0, a0+135, arrowHeadLength ); + fprintf( outf, " { 1, %ld, %ld, %ld, %ld },\n", + (long)(p0.x+0.5), (long)(p0.y+0.5), (long)(p1.x+0.5), (long)(p1.y+0.5) ); + Translate( &p1, p0, a0-135, arrowHeadLength ); + fprintf( outf, " { 1, %ld, %ld, %ld, %ld },\n", + (long)(p0.x+0.5), (long)(p0.y+0.5), (long)(p1.x+0.5), (long)(p1.y+0.5) ); + + } else if ( strncmp( line, "LINE", 4 ) == 0 ) { + if ( sscanf( line, "LINE, %lf, %lf, %lf, %lf", + &p0.x, &p0.y, &p1.x, &p1.y ) != 4) { + fprintf( stderr, "SYNTAX: %s", line ); + exit (1); + } + fprintf( outf, " { 1, %ld, %ld, %ld, %ld },\n", + (long)(p0.x+0.5), (long)(p0.y+0.5), (long)(p1.x+0.5), (long)(p1.y+0.5) ); + + } else if ( strncmp( line, "STRAIGHT", 8 ) == 0 ) { + if ( sscanf( line, "STRAIGHT, %lf, %lf, %lf, %lf", + &p0.x, &p0.y, &p1.x, &p1.y ) != 4) { + fprintf( stderr, "SYNTAX: %s", line ); + exit (1); + } + a0 = FindAngle( p0, p1 ); + Translate( &q0, p0, a0+90, trackSeparation/2.0 ); + Translate( &q1, p1, a0+90, trackSeparation/2.0 ); + fprintf( outf, " { 3, %ld, %ld, %ld, %ld },\n", + (long)(q0.x+0.5), (long)(q0.y+0.5), (long)(q1.x+0.5), (long)(q1.y+0.5) ); + Translate( &q0, p0, a0-90, trackSeparation/2.0 ); + Translate( &q1, p1, a0-90, trackSeparation/2.0 ); + fprintf( outf, " { 3, %ld, %ld, %ld, %ld },\n", + (long)(q0.x+0.5), (long)(q0.y+0.5), (long)(q1.x+0.5), (long)(q1.y+0.5) ); + + } else if ( strncmp( line, "CURVE", 5 ) == 0 ) { + if ( sscanf( line, "CURVE, %lf, %lf, %lf, %lf, %lf", + &p0.x, &p0.y, &p1.x, &p1.y, &radius ) != 5) { + fprintf( stderr, "SYNTAX: %s", line ); + exit (1); + } + a1 = FindCenter( &pc, p0, p1, radius ); + a0 = FindAngle( pc, p0 ); +/*fprintf(stderr, "A0 = %0.3f, A1 = %0.3f\n", a0, a1 );*/ + len = radius * M_PI * 2 * ( a1 / 360.0 ); + num = len/20; + if (num < 0) num = - num; + num++; + a1 /= num; + if (radius < 0) + radius = -radius; + for ( j=0; j<num; j++ ) { +/*fprintf( stderr, "A0 = %0.3f\n", a0 );*/ + Translate( &p0, pc, a0, radius+trackSeparation/2.0 ); + Translate( &p1, pc, a0+a1, radius+trackSeparation/2.0 ); + fprintf( outf, " { 3, %ld, %ld, %ld, %ld },\n", + (long)(p0.x+0.5), (long)(p0.y+0.5), (long)(p1.x+0.5), (long)(p1.y+0.5) ); + Translate( &p0, pc, a0, radius-trackSeparation/2.0 ); + Translate( &p1, pc, a0+a1, radius-trackSeparation/2.0 ); + fprintf( outf, " { 3, %ld, %ld, %ld, %ld },\n", + (long)(p0.x+0.5), (long)(p0.y+0.5), (long)(p1.x+0.5), (long)(p1.y+0.5) ); + a0 += a1; + p0 = p1; + } + } else { + fprintf( stderr, "SYNTAX2: %s", line ); + } + } +} + +int main( int argc, char * argv[] ) +{ + buildDesignerLines( stdin, stdout ); + exit(0); +} diff --git a/app/bin/common.h b/app/bin/common.h new file mode 100644 index 0000000..e238e33 --- /dev/null +++ b/app/bin/common.h @@ -0,0 +1,124 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/common.h,v 1.2 2008-02-23 07:27:15 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 COMMON_H +#define COMMON_H + +#ifndef TRUE +#define TRUE (1) +#define FALSE (0) +#endif + +#define NUM_LAYERS (99) + +typedef double FLOAT_T; +typedef double POS_T; +typedef double DIST_T; +typedef double ANGLE_T; +#define SCANF_FLOAT_FORMAT "%lf" + +typedef double DOUBLE_T; +typedef double WDOUBLE_T; +typedef double FONTSIZE_T; + +typedef struct { + POS_T x,y; + } coOrd; + +typedef int INT_T; + +typedef int BOOL_T; +typedef int EPINX_T; +typedef int CSIZE_T; +#ifndef WIN32 +typedef int SIZE_T; +#endif +typedef int STATE_T; +typedef int STATUS_T; +typedef signed char TRKTYP_T; +typedef int TRKINX_T; +typedef long DEBUGF_T; +typedef int REGION_T; + +typedef struct { + int cnt; + int max; + void * ptr; + } dynArr_t; + +#if defined(WINDOWS) && ! defined(WIN32) +#define CHECK_SIZE(T,DA) \ + if ( (long)((DA).max) * (long)(sizeof *(T*)NULL) > 65500L ) \ + AbortProg( "Dynamic array too large at %s:%d", __FILE__, __LINE__ ); +#else +#define CHECK_SIZE(T,DA) +#endif + +#define DYNARR_APPEND(T,DA,INCR) \ + { if ((DA).cnt >= (DA).max) { \ + (DA).max += INCR; \ + CHECK_SIZE(T,DA) \ + (DA).ptr = MyRealloc( (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; \ + CHECK_SIZE(T,DA) \ + (DA).ptr = MyRealloc( (DA).ptr, (DA).max * sizeof *(T*)NULL ); \ + if ( (DA).ptr == NULL ) \ + abort(); \ + } \ + (DA).cnt = N; } + +#ifdef WINDOWS +#ifdef FAR +#undef FAR +#endif +#ifndef WIN32 +#define FAR _far +#else +#define FAR +#endif +#define M_PI 3.14159 +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#else +#define FAR +#endif + +#if _MSC_VER >1300 + #define strdup _strdup +#endif + +#endif + diff --git a/app/bin/compound.c b/app/bin/compound.c new file mode 100644 index 0000000..cbc650b --- /dev/null +++ b/app/bin/compound.c @@ -0,0 +1,1265 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/compound.c,v 1.4 2008-01-20 23:29:15 mni77 Exp $ + * + * Compound tracks: Turnouts and Structures + * + */ + +/* 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 <ctype.h> +#include "track.h" +#include "compound.h" +#include "shrtpath.h" +#include "cjoin.h" +#include "i18n.h" + +#if _MSC_VER >=1400 +#define strdup _strdup +#endif + +/***************************************************************************** + * + * Misc + * + */ + +BOOL_T WriteCompoundPathsEndPtsSegs( + FILE * f, + PATHPTR_T paths, + wIndex_t segCnt, + trkSeg_p segs, + EPINX_T endPtCnt, + trkEndPt_t * endPts ) +{ + int i; + PATHPTR_T pp; + BOOL_T rc = TRUE; + for ( pp=paths; *pp; pp+=2 ) { + rc &= fprintf( f, "\tP \"%s\"", pp )>0; + for ( pp+=strlen((char *)pp)+1; pp[0]!=0||pp[1]!=0; pp++ ) + rc &= fprintf( f, " %d", *pp )>0; + rc &= fprintf( f, "\n" )>0; + } + for ( i=0; i<endPtCnt; i++ ) + rc &= fprintf( f, "\tE %0.6f %0.6f %0.6f\n", + endPts[i].pos.x, endPts[i].pos.y, endPts[i].angle )>0; + rc &= WriteSegs( f, segCnt, segs )>0; + return rc; +} + + +EXPORT void ParseCompoundTitle( + char * title, + char * * manufP, + int * manufL, + char * * nameP, + int * nameL, + char * * partnoP, + int * partnoL ) +{ + char * cp1, *cp2; + int len; + *manufP = *nameP = *partnoP = NULL; + *manufL = *nameL = *partnoL = 0; + len = strlen( title ); + cp1 = strchr( title, '\t' ); + if ( cp1 ) { + cp2 = strchr( cp1+1, '\t' ); + if ( cp2 ) { + cp2++; + *partnoP = cp2; + *partnoL = title+len-cp2; + len = cp2-title-1; + } + cp1++; + *nameP = cp1; + *nameL = title+len-cp1; + *manufP = title; + *manufL = cp1-title-1; + } else { + *nameP = title; + *nameL = len; + } +} + + +void FormatCompoundTitle( + long format, + char * title ) +{ + char *cp1, *cp2=NULL, *cq; + int len; + FLOAT_T price; + BOOL_T needSep; + cq = message; + if (format&LABEL_COST) { + FormatCompoundTitle( LABEL_MANUF|LABEL_DESCR|LABEL_PARTNO, title ); + wPrefGetFloat( "price list", message, &price, 0.0 ); + if (price > 0.00) { + sprintf( cq, "%7.2f\t", price ); + } else { + strcpy( cq, "\t" ); + } + cq += strlen(cq); + } + cp1 = strchr( title, '\t' ); + if ( cp1 != NULL ) + cp2 = strchr( cp1+1, '\t' ); + if (cp2 == NULL) { + if ( (format&LABEL_TABBED) ) { + *cq++ = '\t'; + *cq++ = '\t'; + } + strcpy( cq, title ); + } else { + len = 0; + needSep = FALSE; + if ((format&LABEL_MANUF) && cp1-title>1) { + len = cp1-title; + memcpy( cq, title, len ); + cq += len; + needSep = TRUE; + } + if ( (format&LABEL_TABBED) ) { + *cq++ = '\t'; + needSep = FALSE; + } + if ((format&LABEL_PARTNO) && *(cp2+1)) { + if ( needSep ) { + *cq++ = ' '; + needSep = FALSE; + } + strcpy( cq, cp2+1 ); + cq += strlen( cq ); + needSep = TRUE; + } + if ( (format&LABEL_TABBED) ) { + *cq++ = '\t'; + needSep = FALSE; + } + if ((format&LABEL_DESCR) || !(format&LABEL_PARTNO)) { + if ( needSep ) { + *cq++ = ' '; + needSep = FALSE; + } + if ( (format&LABEL_FLIPPED) ) { + memcpy( cq, "Flipped ", 8 ); + cq += 8; + } + if ( (format&LABEL_UNGROUPED) ) { + memcpy( cq, "Ungrouped ", 10 ); + cq += 10; + } + if ( (format&LABEL_SPLIT) ) { + memcpy( cq, "Split ", 6 ); + cq += 6; + } + memcpy( cq, cp1+1, cp2-cp1-1 ); + cq += cp2-cp1-1; + needSep = TRUE; + } + *cq = '\0'; + } +} + + + +void ComputeCompoundBoundingBox( + track_p trk ) +{ + struct extraData *xx; + coOrd hi, lo; + + xx = GetTrkExtraData(trk); + + GetSegBounds( xx->orig, xx->angle, xx->segCnt, xx->segs, &lo, &hi ); + hi.x += lo.x; + hi.y += lo.y; + SetBoundingBox( trk, hi, lo ); +} + + +turnoutInfo_t * FindCompound( long type, char * scale, char * title ) +{ + turnoutInfo_t * to; + wIndex_t inx; + SCALEINX_T scaleInx; + + if ( scale ) + scaleInx = LookupScale( scale ); + else + scaleInx = -1; + if ( type&FIND_TURNOUT ) + for (inx=0; inx<turnoutInfo_da.cnt; inx++) { + to = turnoutInfo(inx); + if ( IsParamValid(to->paramFileIndex) && + to->segCnt > 0 && + (scaleInx == -1 || to->scaleInx == scaleInx ) && + to->segCnt != 0 && + strcmp( to->title, title ) == 0 ) { + return to; + } + } + if ( type&FIND_STRUCT ) + for (inx=0; inx<structureInfo_da.cnt; inx++) { + to = structureInfo(inx); + if ( IsParamValid(to->paramFileIndex) && + to->segCnt > 0 && + (scaleInx == -1 || to->scaleInx == scaleInx ) && + to->segCnt != 0 && + strcmp( to->title, title ) == 0 ) { + return to; + } + } + return NULL; +} + + +char * CompoundGetTitle( turnoutInfo_t * to ) +{ + return to->title; +} + + +EXPORT void CompoundClearDemoDefns( void ) +{ + turnoutInfo_t * to; + wIndex_t inx; + + for (inx=0; inx<turnoutInfo_da.cnt; inx++) { + to = turnoutInfo(inx); + if ( to->paramFileIndex == PARAM_CUSTOM && strcasecmp( GetScaleName(to->scaleInx), "DEMO" ) == 0 ) + to->segCnt = 0; + } + for (inx=0; inx<structureInfo_da.cnt; inx++) { + to = structureInfo(inx); + if ( to->paramFileIndex == PARAM_CUSTOM && strcasecmp( GetScaleName(to->scaleInx), "DEMO" ) == 0 ) + to->segCnt = 0; + } +} + +/***************************************************************************** + * + * Descriptions + * + */ + +void SetDescriptionOrig( + track_p trk ) +{ + struct extraData *xx = GetTrkExtraData(trk); + int i, j; + coOrd p0, p1; + + for (i=0,j=-1;i<xx->segCnt;i++) { + if ( IsSegTrack( &xx->segs[i] ) ) { + if (j == -1) { + j = i; + } else { + j = -1; + break; + } + } + } + if (j != -1 && xx->segs[j].type == SEG_CRVTRK) { + REORIGIN( p0, xx->segs[j].u.c.center, xx->angle, xx->orig ) + Translate( &p0, p0, + xx->segs[j].u.c.a0 + xx->segs[j].u.c.a1/2.0 + xx->angle, + fabs(xx->segs[j].u.c.radius) ); + + } else { + GetBoundingBox( trk, (&p0), (&p1) ); + p0.x = (p0.x+p1.x)/2.0; + p0.y = (p0.y+p1.y)/2.0; + } + Rotate( &p0, xx->orig, -xx->angle ); + xx->descriptionOrig.x = p0.x - xx->orig.x; + xx->descriptionOrig.y = p0.y - xx->orig.y; +} + + +void DrawCompoundDescription( + track_p trk, + drawCmd_p d, + wDrawColor color ) +{ + wFont_p fp; + coOrd p1; + struct extraData *xx = GetTrkExtraData(trk); + char * desc; + long layoutLabelsOption = layoutLabels; + + if (layoutLabels == 0) + return; + if ((labelEnable&LABELENABLE_TRKDESC)==0) + return; + if ( (d->options&DC_GROUP) ) + return; + if ( xx->special == TOpier ) { + desc = xx->u.pier.name; + } else { + if ( xx->flipped ) + layoutLabelsOption |= LABEL_FLIPPED; + if ( xx->ungrouped ) + layoutLabelsOption |= LABEL_UNGROUPED; + if ( xx->split ) + layoutLabelsOption |= LABEL_SPLIT; + FormatCompoundTitle( layoutLabelsOption, xtitle(xx) ); + desc = message; + } + p1 = xx->descriptionOrig; + Rotate( &p1, zero, xx->angle ); + p1.x += xx->orig.x + xx->descriptionOff.x; + p1.y += xx->orig.y + xx->descriptionOff.y; +#ifdef LATER + maxInx = -1; + for ( inx=0,a=0.0; a<360.0; inx++,a+=45 ) { + Translate( &p1, p0, a, trackGauge*3 ); + dists[inx].p = p1; + if ((trk1 = dists[inx].trk = OnTrack( &p1, FALSE, TRUE )) == NULL || + trk1 == trk ) { + p1 = dists[inx].p; + dists[inx].d = DistanceSegs( xx->orig, xx->angle, xx->segCnt, xx->segs, &p1, NULL ); + } else if ( GetTrkType(trk1) == T_TURNOUT ) { + struct extraData *yy = GetTrkExtraData(trk1); + dists[inx].d = DistanceSegs( yy->orig, yy->angle, yy->segCnt, yy->segs, &p1, NULL ); + } else { + dists[inx].d = FindDistance( p0, p1 ); + } + } + maxD = 0; maxInx = -1; + for ( inx=0,a=0.0; a<360.0; inx++,a+=45 ) { + if (dists[inx].trk == NULL || dists[inx].trk == trk) { + if (dists[inx].d > maxD) { + maxD = dists[inx].d; + maxInx = inx; + } + } + } + if (maxInx == -1) { + if (dists[inx].d > maxD) { + maxD = dists[inx].d; + maxInx = inx; + } + } + if (maxInx != -1) { + p0 = dists[maxInx].p; + } +#endif + fp = wStandardFont( F_TIMES, FALSE, FALSE ); + DrawBoxedString( (xx->special==TOpier)?BOX_INVERT:BOX_NONE, d, p1, desc, fp, (wFontSize_t)descriptionFontSize, color, 0.0 ); +} + + +DIST_T CompoundDescriptionDistance( + coOrd pos, + track_p trk ) +{ + struct extraData *xx = GetTrkExtraData(trk); + coOrd p1; + if (GetTrkType(trk) != T_TURNOUT && GetTrkType(trk) != T_STRUCTURE) + return 100000; + p1 = xx->descriptionOrig; + Rotate( &p1, zero, xx->angle ); + p1.x += xx->orig.x + xx->descriptionOff.x; + p1.y += xx->orig.y + xx->descriptionOff.y; + return FindDistance( p1, pos ); +} + + +STATUS_T CompoundDescriptionMove( + track_p trk, + wAction_t action, + coOrd pos ) +{ + struct extraData *xx = GetTrkExtraData(trk); + static coOrd p0, p1; + wDrawColor color; + + switch (action) { + case C_DOWN: + REORIGIN( p0, xx->descriptionOrig, xx->angle, xx->orig ) + + case C_MOVE: + case C_UP: + if (action != C_DOWN) + DrawLine( &tempD, p0, p1, 0, wDrawColorBlack ); + color = GetTrkColor( trk, &mainD ); + DrawCompoundDescription( trk, &tempD, color ); + xx->descriptionOff.x = (pos.x-p0.x); + xx->descriptionOff.y = (pos.y-p0.y); + p1 = xx->descriptionOrig; + Rotate( &p1, zero, xx->angle ); + p1.x += xx->orig.x + xx->descriptionOff.x; + p1.y += xx->orig.y + xx->descriptionOff.y; + DrawCompoundDescription( trk, &tempD, color ); + if (action != C_UP) + DrawLine( &tempD, p0, p1, 0, wDrawColorBlack ); + MainRedraw(); + return action==C_UP?C_TERMINATE:C_CONTINUE; + } + return C_CONTINUE; +} + + + +/***************************************************************************** + * + * Generics + * + */ + + +EXPORT void GetSegInxEP( + signed char segChar, + int * segInx, + EPINX_T * segEP ) +{ + int inx; + inx = segChar; + if (inx > 0 ) { + *segInx = (inx)-1; + *segEP = 0; + } else { + *segInx = (-inx)-1; + *segEP = 1; + } +} + + +DIST_T DistanceCompound( + track_p t, + coOrd * p ) +{ + struct extraData *xx = GetTrkExtraData(t); + EPINX_T ep; + DIST_T d0, d1; + coOrd p0, p2; + PATHPTR_T path; + int segInx; + EPINX_T segEP; + segProcData_t segProcData; + + if ( onTrackInSplit && GetTrkEndPtCnt(t) > 0 ) { + d0 = DistanceSegs( xx->orig, xx->angle, xx->segCnt, xx->segs, p, NULL ); + } else if ( programMode != MODE_TRAIN || GetTrkEndPtCnt(t) <= 0 ) { + d0 = DistanceSegs( xx->orig, xx->angle, xx->segCnt, xx->segs, p, NULL ); + if (programMode != MODE_TRAIN && GetTrkEndPtCnt(t) > 0 && d0 < 10000.0) { + ep = PickEndPoint( *p, t ); + *p = GetTrkEndPos(t,ep); + } + } else { + p0 = *p; + Rotate( &p0, xx->orig, -xx->angle ); + p0.x -= xx->orig.x; + p0.y -= xx->orig.y; + d0 = 1000000.0; + path = xx->pathCurr; + for ( path=xx->pathCurr+strlen((char *)xx->pathCurr)+1; path[0] || path[1]; path++ ) { + if ( path[0] != 0 ) { + d1 = 1000000.0; + GetSegInxEP( *path, &segInx, &segEP ); + segProcData.distance.pos1 = p0; + SegProc( SEGPROC_DISTANCE, &xx->segs[segInx], &segProcData ); + if ( segProcData.distance.dd < d0 ) { + d0 = segProcData.distance.dd; + p2 = segProcData.distance.pos1; + } + } + } + if ( d0 < 1000000.0 ) { + p2.x += xx->orig.x; + p2.y += xx->orig.y; + Rotate( &p2, xx->orig, xx->angle ); + *p = p2; + } + } + return d0; +} + + +static struct { + coOrd endPt[2]; + FLOAT_T elev[2]; + coOrd orig; + ANGLE_T angle; + char manuf[STR_SIZE]; + char name[STR_SIZE]; + char partno[STR_SIZE]; + long epCnt; + long segCnt; + FLOAT_T grade; + DIST_T length; + LAYER_T layerNumber; + } compoundData; +typedef enum { E0, Z0, E1, Z1, GR, OR, AN, MN, NM, PN, EC, SC, LY } compoundDesc_e; +static descData_t compoundDesc[] = { +/*E0*/ { DESC_POS, N_("End Pt 1: X"), &compoundData.endPt[0] }, +/*Z0*/ { DESC_DIM, N_("Z"), &compoundData.elev[0] }, +/*E1*/ { DESC_POS, N_("End Pt 2: X"), &compoundData.endPt[1] }, +/*Z1*/ { DESC_DIM, N_("Z"), &compoundData.elev[1] }, +/*GR*/ { DESC_FLOAT, N_("Grade"), &compoundData.grade }, +/*OR*/ { DESC_POS, N_("Origin: X"), &compoundData.orig }, +/*AN*/ { DESC_ANGLE, N_("Angle"), &compoundData.angle }, +/*MN*/ { DESC_STRING, N_("Manufacturer"), &compoundData.manuf }, +/*NM*/ { DESC_STRING, N_("Name"), &compoundData.name }, +/*PN*/ { DESC_STRING, N_("Part No"), &compoundData.partno }, +/*EC*/ { DESC_LONG, N_("# End Pt"), &compoundData.epCnt }, +/*SC*/ { DESC_LONG, N_("# Segments"), &compoundData.segCnt }, +/*LY*/ { DESC_LAYER, N_("Layer"), &compoundData.layerNumber }, + { DESC_NULL } }; + + + +static void UpdateCompound( track_p trk, int inx, descData_p descUpd, BOOL_T needUndoStart ) +{ + struct extraData *xx = GetTrkExtraData(trk); + const char * manufS, * nameS, * partnoS; + char * mP, *nP, *pP; + int mL, nL, pL; + coOrd hi, lo; + coOrd pos; + EPINX_T ep; + BOOL_T titleChanged, flipped, ungrouped, split; + char * newTitle; + + if ( inx == -1 ) { + titleChanged = FALSE; + ParseCompoundTitle( xtitle(xx), &mP, &mL, &nP, &nL, &pP, &pL ); + if (mP == NULL) mP = ""; + if (nP == NULL) nP = ""; + if (pP == NULL) pP = ""; + manufS = wStringGetValue( (wString_p)compoundDesc[MN].control0 ); + strcpy( message, manufS ); + if ( strncmp( manufS, mP, mL ) != 0 || manufS[mL] != '\0' ) { + titleChanged = TRUE; + } + flipped = xx->flipped; + ungrouped = xx->ungrouped; + split = xx->split; + nameS = wStringGetValue( (wString_p)compoundDesc[NM].control0 ); + if ( strncmp( nameS, "Flipped ", 8 ) == 0 ) { + nameS += 8; + flipped = TRUE; + } else { + flipped = FALSE; + } + if ( strncmp( nameS, "Ungrouped ", 10 ) == 0 ) { + nameS += 10; + ungrouped = TRUE; + } else { + ungrouped = FALSE; + } + if ( strncmp( nameS, "Split ", 6 ) == 0 ) { + nameS += 6; + split = TRUE; + } else { + split = FALSE; + } + if ( strncmp( nameS, nP, nL ) != 0 || nameS[nL] != '\0' || + xx->flipped != flipped || + xx->ungrouped != ungrouped || + xx->split != split ) { + titleChanged = TRUE; + } + strcat( message, "\t" ); + strcat( message, nameS ); + partnoS = wStringGetValue( (wString_p)compoundDesc[PN].control0 ); + strcat( message, "\t" ); + strcat( message, partnoS ); + newTitle = MyStrdup( message ); + if ( strncmp( partnoS, pP, pL ) != 0 || partnoS[pL] != '\0' ) { + titleChanged = TRUE; + } + if ( ! titleChanged ) + return; + if ( needUndoStart ) + UndoStart( _("Change Track"), "Change Track" ); + UndoModify( trk ); + GetBoundingBox( trk, &hi, &lo ); + if ( labelScale >= mainD.scale && + !OFF_MAIND( lo, hi ) ) { + DrawCompoundDescription( trk, &tempD, GetTrkColor(trk,&tempD) ); + } + /*sprintf( message, "%s\t%s\t%s", manufS, nameS, partnoS );*/ + xx->title = newTitle; + xx->flipped = flipped; + xx->ungrouped = ungrouped; + xx->split = split; + if ( labelScale >= mainD.scale && + !OFF_MAIND( lo, hi ) ) { + DrawCompoundDescription( trk, &tempD, GetTrkColor(trk,&tempD) ); + } + return; + } + + UndrawNewTrack( trk ); + switch ( inx ) { + case OR: + pos.x = compoundData.orig.x - xx->orig.x; + pos.y = compoundData.orig.y - xx->orig.y; + MoveTrack( trk, pos ); + ComputeCompoundBoundingBox( trk ); + break; + case AN: + RotateTrack( trk, xx->orig, NormalizeAngle( compoundData.angle-xx->angle ) ); + ComputeCompoundBoundingBox( trk ); + break; + case E0: + case E1: + ep = (inx==E0?0:1); + pos = GetTrkEndPos(trk,ep); + pos.x = compoundData.endPt[ep].x - pos.x; + pos.y = compoundData.endPt[ep].y - pos.y; + MoveTrack( trk, pos ); + ComputeCompoundBoundingBox( trk ); + if ( compoundData.epCnt >= 2 ) { + compoundData.endPt[1-ep] = GetTrkEndPos(trk,1-ep); + compoundDesc[inx==E0?E1:E0].mode |= DESC_CHANGE; + } + break; + case Z0: + case Z1: + ep = (inx==Z0?0:1); + UpdateTrkEndElev( trk, ep, GetTrkEndElevUnmaskedMode(trk,ep), compoundData.elev[ep], NULL ); + if ( GetTrkEndPtCnt(trk) == 1 ) + break; + ComputeElev( trk, 1-ep, FALSE, &compoundData.elev[1-ep], NULL ); + if ( compoundData.length > minLength ) + compoundData.grade = fabs( (compoundData.elev[0]-compoundData.elev[1])/compoundData.length )*100.0; + else + compoundData.grade = 0.0; + compoundDesc[GR].mode |= DESC_CHANGE; + compoundDesc[inx==Z0?Z1:Z0].mode |= DESC_CHANGE; + break; + case LY: + SetTrkLayer( trk, compoundData.layerNumber); + break; + default: + break; + } + DrawNewTrack( trk ); + +} + + +void DescribeCompound( + track_p trk, + char * str, + CSIZE_T len ) +{ + struct extraData *xx = GetTrkExtraData(trk); + int fix; + EPINX_T ep, epCnt; + char * mP, *nP, *pP, *cnP; + int mL, nL, pL; + long mode; + long listLabelsOption = listLabels; + + if ( xx->flipped ) + listLabelsOption |= LABEL_FLIPPED; + if ( xx->ungrouped ) + listLabelsOption |= LABEL_UNGROUPED; + if ( xx->split ) + listLabelsOption |= LABEL_SPLIT; + FormatCompoundTitle( listLabelsOption, xtitle(xx) ); + if (message[0] == '\0') + FormatCompoundTitle( listLabelsOption|LABEL_DESCR, xtitle(xx) ); + strcpy( str, _(GetTrkTypeName( trk )) ); + str++; + while (*str) { + *str = tolower(*str); + str++; + } + sprintf( str, _("(%d): Layer=%d %s"), + GetTrkIndex(trk), GetTrkLayer(trk)+1, message ); + + epCnt = GetTrkEndPtCnt(trk); + fix = 0; + for ( ep=0; ep<epCnt; ep++ ) { + if (GetTrkEndTrk(trk,ep)) { + fix = 1; + break; + } + } + compoundData.orig = xx->orig; + compoundData.angle = xx->angle; + ParseCompoundTitle( xtitle(xx), &mP, &mL, &nP, &nL, &pP, &pL ); + if (mP) { + memcpy( compoundData.manuf, mP, mL ); + compoundData.manuf[mL] = 0; + } else { + compoundData.manuf[0] = 0; + } + if (nP) { + cnP = compoundData.name; + if ( xx->flipped ) { + memcpy( cnP, "Flipped ", 8 ); + cnP += 8; + } + if ( xx->ungrouped ) { + memcpy( cnP, "Ungrouped ", 10 ); + cnP += 10; + } + if ( xx->split ) { + memcpy( cnP, "Split ", 6 ); + cnP += 6; + } + memcpy( cnP, nP, nL ); + cnP[nL] = 0; + } else { + compoundData.name[0] = 0; + } + if (pP) { + memcpy( compoundData.partno, pP, pL ); + compoundData.partno[pL] = 0; + } else { + compoundData.partno[0] = 0; + } + compoundData.epCnt = GetTrkEndPtCnt(trk); + compoundData.segCnt = xx->segCnt; + compoundData.length = 0; + compoundData.layerNumber = GetTrkLayer( trk ); + compoundDesc[E0].mode = + compoundDesc[Z0].mode = + compoundDesc[E1].mode = + compoundDesc[Z1].mode = + compoundDesc[GR].mode = DESC_IGNORE; + compoundDesc[OR].mode = + compoundDesc[AN].mode = fix?DESC_RO:0; + compoundDesc[MN].mode = + compoundDesc[NM].mode = + compoundDesc[PN].mode = 0 /*DESC_NOREDRAW*/; + compoundDesc[EC].mode = + compoundDesc[SC].mode = + compoundDesc[LY].mode = DESC_NOREDRAW; + if ( compoundData.epCnt ) { + if ( compoundData.epCnt <=2 ) { + if ( GetTrkEndTrk(trk,0) || (compoundData.epCnt==2 && GetTrkEndTrk(trk,1)) ) + mode = DESC_RO; + else + mode = 0; + compoundDesc[OR].mode = DESC_IGNORE; + compoundDesc[AN].mode = DESC_IGNORE; + compoundDesc[EC].mode = DESC_IGNORE; + compoundData.endPt[0] = GetTrkEndPos(trk,0); + ComputeElev( trk, 0, FALSE, &compoundData.elev[0], NULL ); + compoundDesc[E0].mode = (int)mode; + compoundDesc[Z0].mode = (EndPtIsDefinedElev(trk,0)?0:DESC_RO)|DESC_NOREDRAW; + if ( compoundData.epCnt == 2 ) { + compoundData.length = GetTrkLength( trk, 0, 1 ); + compoundData.endPt[1] = GetTrkEndPos(trk,1); + ComputeElev( trk, 1, FALSE, &compoundData.elev[1], NULL ); + compoundDesc[E1].mode = (int)mode; + compoundDesc[Z1].mode = (EndPtIsDefinedElev(trk,1)?0:DESC_RO)|DESC_NOREDRAW; + compoundDesc[GR].mode = DESC_RO; + if ( compoundData.length > minLength ) + compoundData.grade = fabs( (compoundData.elev[0]-compoundData.elev[1])/compoundData.length )*100.0; + else + compoundData.grade = 0.0; + } + } + DoDescribe( compoundData.epCnt>2?_("Turnout"):_("Sectional Track"), trk, compoundDesc, UpdateCompound ); + } else { + compoundDesc[EC].mode |= DESC_IGNORE; + DoDescribe( _("Structure"), trk, compoundDesc, UpdateCompound ); + } +} + + +void DeleteCompound( + track_p t ) +{ +} + + +BOOL_T WriteCompound( + track_p t, + FILE * f ) +{ + struct extraData *xx = GetTrkExtraData(t); + EPINX_T ep, epCnt; + long options; + long position = 0; + PATHPTR_T path; + BOOL_T rc = TRUE; + + options = (long)GetTrkWidth(t); + if (xx->handlaid) + options |= 0x08; + if (xx->flipped) + options |= 0x10; + if (xx->ungrouped) + options |= 0x20; + if (xx->split) + options |= 0x40; + if ( ( GetTrkBits( t ) & TB_HIDEDESC ) != 0 ) + options |= 0x80; + epCnt = GetTrkEndPtCnt(t); + if ( epCnt > -0 ) { + path = xx->paths; + while ( path != xx->pathCurr ) { + path += strlen((char*)path)+1; + while ( path[0] || path[1] ) + path++; + path += 2; + if ( *path == 0 ) + break; + position++; + } + } + rc &= fprintf(f, "%s %d %d %ld %ld 0 %s %d %0.6f %0.6f 0 %0.6f \"%s\"\n", + GetTrkTypeName(t), + GetTrkIndex(t), GetTrkLayer(t), options, position, + GetTrkScaleName(t), GetTrkVisible(t), + xx->orig.x, xx->orig.y, xx->angle, + PutTitle(xtitle(xx)) )>0; + for (ep=0; ep<epCnt; ep++ ) + WriteEndPt( f, t, ep ); + switch ( xx->special ) { + case TOadjustable: + rc &= fprintf( f, "\tX %s %0.3f %0.3f\n", ADJUSTABLE, + xx->u.adjustable.minD, xx->u.adjustable.maxD )>0; + break; + case TOpier: + rc &= fprintf( f, "\tX %s %0.6f \"%s\"\n", PIER, xx->u.pier.height, xx->u.pier.name )>0; + default: + ; + } + rc &= fprintf( f, "\tD %0.6f %0.6f\n", xx->descriptionOff.x, xx->descriptionOff.y )>0; + rc &= WriteCompoundPathsEndPtsSegs( f, xpaths(xx), xx->segCnt, xx->segs, 0, NULL ); + return rc; +} + + + + +/***************************************************************************** + * + * Generic Functions + * + */ + + +EXPORT track_p NewCompound( + TRKTYP_T trkType, + TRKINX_T index, + coOrd pos, + ANGLE_T angle, + char * title, + EPINX_T epCnt, + trkEndPt_t * epp, + int pathLen, + char * paths, + wIndex_t segCnt, + trkSeg_p segs ) +{ + track_p trk; + struct extraData * xx; + EPINX_T ep; + + trk = NewTrack( index, trkType, epCnt, sizeof (*xx) + 1 ); + xx = GetTrkExtraData(trk); + xx->orig = pos; + xx->angle = angle; + xx->handlaid = FALSE; + xx->flipped = FALSE; + xx->ungrouped = FALSE; + xx->split = FALSE; + xx->descriptionOff = zero; + xx->descriptionSize = zero; + xx->title = MyStrdup( title ); + xx->customInfo = NULL; + xx->special = TOnormal; + if ( pathLen > 0 ) + xx->paths = memdup( paths, pathLen ); + else + xx->paths = (PATHPTR_T)""; + xx->pathLen = pathLen; + xx->pathCurr = xx->paths; + xx->segCnt = segCnt; + xx->segs = memdup( segs, segCnt * sizeof *segs ); + ComputeCompoundBoundingBox( trk ); + SetDescriptionOrig( trk ); + for ( ep=0; ep<epCnt; ep++ ) + SetTrkEndPoint( trk, ep, epp[ep].pos, epp[ep].angle ); + return trk; +} + + +void ReadCompound( + char * line, + TRKTYP_T trkType ) +{ + track_p trk; + struct extraData *xx; + TRKINX_T index; + BOOL_T visible; + coOrd orig; + DIST_T elev; + ANGLE_T angle; + char scale[10]; + char *title; + wIndex_t layer; + char *cp; + long options = 0; + long position = 0; + PATHPTR_T path=NULL; + + if (paramVersion<3) { + if ( !GetArgs( line, "dXsdpfq", + &index, &layer, scale, &visible, &orig, &angle, &title ) ) + return; + } else if (paramVersion <= 5 && trkType == T_STRUCTURE) { + if ( !GetArgs( line, "dL00sdpfq", + &index, &layer, scale, &visible, &orig, &angle, &title ) ) + return; + } else { + if ( !GetArgs( line, paramVersion<9?"dLll0sdpYfq":"dLll0sdpffq", + &index, &layer, &options, &position, scale, &visible, &orig, &elev, &angle, &title ) ) + return; + } + if (paramVersion >=3 && paramVersion <= 5 && trkType == T_STRUCTURE) + strcpy( scale, curScaleName ); + DYNARR_RESET( trkEndPt_t, tempEndPts_da ); + pathCnt = 0; + ReadSegs(); + path = pathPtr; + if ( tempEndPts_da.cnt > 0 && pathCnt <= 1 ) { + pathCnt = 10; + path = (PATHPTR_T)"Normal\01\0\0"; + } + if (paramVersion<6 && strlen( title ) > 2) { + cp = strchr( title, '\t' ); + if (cp != NULL) { + cp = strchr( cp, '\t' ); + } + if (cp == NULL) { + UpdateTitleMark( title, LookupScale(scale) ); + } + } + trk = NewCompound( trkType, index, orig, angle, title, 0, NULL, pathCnt, (char *)path, tempSegs_da.cnt, &tempSegs(0) ); + SetEndPts( trk, 0 ); + SetTrkVisible(trk, visible); + SetTrkScale(trk, LookupScale( scale )); + SetTrkLayer(trk, layer); + SetTrkWidth(trk, (int)(options&3)); + xx = GetTrkExtraData(trk); + xx->handlaid = (int)((options&0x08)!=0); + xx->flipped = (int)((options&0x10)!=0); + xx->ungrouped = (int)((options&0x20)!=0); + xx->split = (int)((options&0x40)!=0); + xx->descriptionOff = descriptionOff; + if ( ( options & 0x80 ) != 0 ) + SetTrkBits( trk, TB_HIDEDESC ); +#ifdef LATER + trk = NewTrack( index, trkType, 0, sizeof (*xx) + 1 ); + SetEndPts( trk, 0 ); + xx = GetTrkExtraData(trk); + SetTrkVisible(trk, visible); + SetTrkScale(trk, LookupScale( scale )); + SetTrkLayer(trk, layer); + SetTrkWidth(trk, (int)(options&3)); + xx->orig = orig; + xx->angle = angle; + xx->customInfo = NULL; + xx->handlaid = (int)((options>>3)&0x01); + xx->flipped = (int)((options>>4)&0x01); + xx->segCnt = tempSegs_da.cnt; + xx->segs = MyMalloc( (tempSegs_da.cnt)*sizeof xx->segs[0] ); + if (paramVersion<6 && strlen( title ) > 2) { + cp = strchr( title, '\t' ); + if (cp != NULL) { + cp = strchr( cp, '\t' ); + } + if (cp == NULL) { + UpdateTitleMark(title, GetTrkScale(trk)); + } + } + xx->title = title; + if ( GetTrkEndPtCnt(trk) > 0 && pathCnt <= 1 ) { + xx->pathLen = 10; + xx->paths = xx->pathCurr = (PATHPTR_T)Malloc( xx->pathLen ); + memcpy( xx->paths, "Normal\01\0\0", xx->pathLen ); + } else { + xx->pathLen = pathCnt; + if (pathCnt > 0) { + xx->paths = xx->pathCurr = (PATHPTR_T)Malloc( pathCnt ); + memcpy( xpaths(xx), pathPtr, pathCnt ); + } else { + xx->paths = xx->pathCurr = NULL; + } + } + xx->segCnt = tempSegs_da.cnt; + memcpy( xx->segs, tempSegs_da.ptr, tempSegs_da.cnt * sizeof *xx->segs ); + + ComputeCompoundBoundingBox( trk ); + SetDescriptionOrig( trk ); + xx->descriptionOff = descriptionOff; +#endif + + if (tempSpecial[0] != '\0') { + if (strncmp( tempSpecial, ADJUSTABLE, strlen(ADJUSTABLE) ) == 0) { + xx->special = TOadjustable; + GetArgs( tempSpecial+strlen(ADJUSTABLE), "ff", + &xx->u.adjustable.minD, &xx->u.adjustable.maxD ); + + } else if (strncmp( tempSpecial, PIER, strlen(PIER) ) == 0) { + xx->special = TOpier; + GetArgs( tempSpecial+strlen(PIER), "fq", + &xx->u.pier.height, &xx->u.pier.name ); + + } else { + InputError("Unknown special case", TRUE); + } + } + if (pathCnt > 0) { + path = xx->pathCurr; + while ( position-- ) { + path += strlen((char *)path)+1; + while ( path[0] || path[1] ) + path++; + path += 2; + if ( *path == 0 ) + path = xx->paths; + } + } + xx->pathCurr = path; + +} + +void MoveCompound( + track_p trk, + coOrd orig ) +{ + struct extraData *xx = GetTrkExtraData(trk); + xx->orig.x += orig.x; + xx->orig.y += orig.y; + ComputeCompoundBoundingBox( trk ); +} + + +void RotateCompound( + track_p trk, + coOrd orig, + ANGLE_T angle ) +{ + struct extraData *xx = GetTrkExtraData(trk); + Rotate( &xx->orig, orig, angle ); + xx->angle = NormalizeAngle( xx->angle + angle ); + Rotate( &xx->descriptionOff, zero, angle ); + ComputeCompoundBoundingBox( trk ); +} + + +void RescaleCompound( + track_p trk, + FLOAT_T ratio ) +{ + struct extraData *xx = GetTrkExtraData(trk); + xx->orig.x *= ratio; + xx->orig.y *= ratio; + xx->descriptionOff.x *= ratio; + xx->descriptionOff.y *= ratio; + xx->segs = (trkSeg_p)memdup( xx->segs, xx->segCnt * sizeof xx->segs[0] ); + CloneFilledDraw( xx->segCnt, xx->segs, TRUE ); + RescaleSegs( xx->segCnt, xx->segs, ratio, ratio, ratio ); +} + + +void FlipCompound( + track_p trk, + coOrd orig, + ANGLE_T angle ) +{ + struct extraData *xx = GetTrkExtraData(trk); + EPINX_T ep, epCnt; + char * mP, *nP, *pP; + int mL, nL, pL; + char *type, *mfg, *descL, *partL, *descR, *partR, *cp; + wIndex_t inx; + turnoutInfo_t *to, *toBest; + coOrd endPos[4]; + ANGLE_T endAngle[4]; + DIST_T d2, d1, d0; + ANGLE_T a2, a1; +#define SMALLVALUE (0.001) + + FlipPoint( &xx->orig, orig, angle ); + xx->angle = NormalizeAngle( 2*angle - xx->angle + 180.0 ); + xx->segs = memdup( xx->segs, xx->segCnt * sizeof xx->segs[0] ); + FlipSegs( xx->segCnt, xx->segs, zero, angle ); + xx->descriptionOrig.y = - xx->descriptionOrig.y; + ComputeCompoundBoundingBox( trk ); + epCnt = GetTrkEndPtCnt( trk ); + if ( epCnt >= 1 && epCnt <= 2 ) + return; + ParseCompoundTitle( xtitle(xx), &mP, &mL, &nP, &nL, &pP, &pL ); + to = FindCompound( epCnt==0?FIND_STRUCT:FIND_TURNOUT, GetScaleName(GetTrkScale(trk)), xx->title ); + if ( epCnt!=0 && to && to->customInfo ) { + if ( GetArgs( to->customInfo, "qc", &type, &cp ) ) { + if ( strcmp( type, "Regular Turnout" ) == 0 || + strcmp( type, "Curved Turnout" ) == 0 ) { + if ( GetArgs( cp, "qqqqq", &mfg, &descL, &partL, &descR, &partR ) && + mP && strcmp( mP, mfg ) == 0 && nP && pP ) { + if ( strcmp( nP, descL ) == 0 && strcmp( pP, partL ) == 0 ) { + sprintf( message, "%s\t%s\t%s", mfg, descR, partR ); + xx->title = strdup( message ); + return; + } + if ( strcmp( nP, descR ) == 0 && strcmp( pP, partR ) == 0 ) { + sprintf( message, "%s\t%s\t%s", mfg, descL, partL ); + xx->title = strdup( message ); + return; + } + } + } + } + } + if ( epCnt == 3 || epCnt == 4 ) { + for ( ep=0; ep<epCnt; ep++ ) { + endPos[ep] = GetTrkEndPos( trk, ep ); + endAngle[ep] = NormalizeAngle( GetTrkEndAngle( trk, ep ) - xx->angle ); + Rotate( &endPos[ep], xx->orig, -xx->angle ); + endPos[ep].x -= xx->orig.x; + endPos[ep].y -= xx->orig.y; + } + if ( epCnt == 3 ) { + /* Wye? */ + if ( fabs(endPos[1].x-endPos[2].x) < SMALLVALUE && + fabs(endPos[1].y+endPos[2].y) < SMALLVALUE ) + return; + } else { + /* Crossing */ + if ( fabs( (endPos[1].x-endPos[3].x) - (endPos[2].x-endPos[0].x ) ) < SMALLVALUE && + fabs( (endPos[2].y+endPos[3].y) ) < SMALLVALUE && + fabs( (endPos[0].y-endPos[1].y) ) < SMALLVALUE && + NormalizeAngle( (endAngle[2]-endAngle[3]-180+0.05) ) < 0.10 ) + return; + /* 3 way */ + if ( fabs( (endPos[1].x-endPos[2].x) ) < SMALLVALUE && + fabs( (endPos[1].y+endPos[2].y) ) < SMALLVALUE && + fabs( (endPos[0].y-endPos[3].y) ) < SMALLVALUE && + NormalizeAngle( (endAngle[1]+endAngle[2]-180+0.05) ) < 0.10 ) + return; + } + toBest = NULL; + d0 = 0.0; + for (inx=0; inx<turnoutInfo_da.cnt; inx++) { + to = turnoutInfo(inx); + if ( IsParamValid(to->paramFileIndex) && + to->segCnt > 0 && + to->scaleInx == GetTrkScale(trk) && + to->segCnt != 0 && + to->endCnt == epCnt ) { + d1 = 0; + a1 = 0; + for ( ep=0; ep<epCnt; ep++ ) { + d2 = FindDistance( endPos[ep], to->endPt[ep].pos ); + if ( d2 > SMALLVALUE ) + break; + if ( d2 > d1 ) + d1 = d2; + a2 = NormalizeAngle( endAngle[ep] - to->endPt[ep].angle + 0.05 ); + if ( a2 > 0.1 ) + break; + if ( a2 > a1 ) + a1 = a2; + } + if ( ep<epCnt ) + continue; + if ( toBest == NULL || d1 < d0 ) + toBest = to; + } + } + if ( toBest ) { + if ( strcmp( xx->title, toBest->title ) != 0 ) + xx->title = MyStrdup( toBest->title ); + return; + } + } + xx->flipped = !xx->flipped; +} + + +typedef struct { + long count; + char * type; + char * name; + FLOAT_T price; + } enumCompound_t; +static dynArr_t enumCompound_da; +#define EnumCompound(N) DYNARR_N( enumCompound_t,enumCompound_da,N) + +BOOL_T EnumerateCompound( track_p trk ) +{ + struct extraData *xx; + INT_T inx, inx2; + int cmp; + long listLabelsOption = listLabels; + + if ( trk != NULL ) { + xx = GetTrkExtraData(trk); + if ( xx->flipped ) + listLabelsOption |= LABEL_FLIPPED; +#ifdef LATER + if ( xx->ungrouped ) + listLabelsOption |= LABEL_UNGROUPED; + if ( xx->split ) + listLabelsOption |= LABEL_SPLIT; +#endif + FormatCompoundTitle( listLabelsOption, xtitle(xx) ); + if (message[0] == '\0') + return TRUE; + for (inx = 0; inx < enumCompound_da.cnt; inx++ ) { + cmp = strcmp( EnumCompound(inx).name, message ); + if ( cmp == 0 ) { + EnumCompound(inx).count++; + return TRUE; + } else if ( cmp > 0 ) { + break; + } + } + DYNARR_APPEND( enumCompound_t, enumCompound_da, 10 ); + for ( inx2 = enumCompound_da.cnt-1; inx2 > inx; inx2-- ) + EnumCompound(inx2) = EnumCompound(inx2-1); + EnumCompound(inx).name = MyStrdup( message ); + if (strlen(message) > (size_t)enumerateMaxDescLen) + enumerateMaxDescLen = strlen(message); + EnumCompound(inx).type = GetTrkTypeName( trk ); + EnumCompound(inx).count = 1; + FormatCompoundTitle( LABEL_MANUF|LABEL_DESCR|LABEL_PARTNO, xtitle(xx) ); + wPrefGetFloat( "price list", message, &(EnumCompound(inx).price), 0.0 ); + } else { + char * type; + for ( type="TS"; *type; type++ ) { + for (inx = 0; inx < enumCompound_da.cnt; inx++ ) { + if (EnumCompound(inx).type[0] == *type) { + EnumerateList( EnumCompound(inx).count, + EnumCompound(inx).price, + EnumCompound(inx).name ); + } + } + } + DYNARR_RESET( enumCompound_t, enumCompound_da ); + } + return TRUE; +} + diff --git a/app/bin/compound.h b/app/bin/compound.h new file mode 100644 index 0000000..a0de926 --- /dev/null +++ b/app/bin/compound.h @@ -0,0 +1,170 @@ +/* $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/compound.h,v 1.1 2005-12-07 15:47:08 rc-flyer 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 COMPOUND_H +#define COMPOUND_H + +typedef enum { TOnormal, TOadjustable, TOpierInfo, TOpier, TOcarDesc, TOlast } TOspecial_e; + +typedef struct { + char * name; + FLOAT_T height; + } pierInfo_t; +typedef union { + struct { + FLOAT_T minD, maxD; + } adjustable; + struct { + int cnt; + pierInfo_t * info; + } pierInfo; + struct { + FLOAT_T height; + char * name; + } pier; + } turnoutInfo_u; + +typedef struct turnoutInfo_t{ + SCALEINX_T scaleInx; + char * title; + coOrd orig; + coOrd size; + wIndex_t segCnt; + trkSeg_p segs; + wIndex_t endCnt; + trkEndPt_t * endPt; + wIndex_t pathLen; + PATHPTR_T paths; + int paramFileIndex; + char * customInfo; + DIST_T barScale; + TOspecial_e special; + turnoutInfo_u u; + char * contentsLabel; + } turnoutInfo_t; + + +#define xpaths(X) \ + (X->paths) +#define xtitle(X) \ + (X->title) + +#ifndef PRIVATE_EXTRADATA +struct extraData { + coOrd orig; + ANGLE_T angle; + BOOL_T handlaid; + BOOL_T flipped; + BOOL_T ungrouped; + BOOL_T split; + coOrd descriptionOrig; + coOrd descriptionOff; + coOrd descriptionSize; + char * title; + char * customInfo; + TOspecial_e special; + turnoutInfo_u u; + PATHPTR_T paths; + wIndex_t pathLen; + PATHPTR_T pathCurr; + wIndex_t segCnt; + trkSeg_t * segs; + }; +#endif + +extern TRKTYP_T T_TURNOUT; +extern TRKTYP_T T_STRUCTURE; +extern DIST_T curBarScale; +extern dynArr_t turnoutInfo_da; +extern dynArr_t structureInfo_da; +extern dynArr_t carDescInfo_da; +#define turnoutInfo(N) DYNARR_N( turnoutInfo_t *, turnoutInfo_da, N ) +#define structureInfo(N) DYNARR_N( turnoutInfo_t *, structureInfo_da, N ) +extern turnoutInfo_t * curTurnout; +extern turnoutInfo_t * curStructure; + + +#define ADJUSTABLE "adjustable" +#define PIER "pier" + +/* compound.c */ +#define FIND_TURNOUT (1<<11) +#define FIND_STRUCT (1<<12) +void FormatCompoundTitle( long, char *); +BOOL_T WriteCompoundPathsEndPtsSegs( FILE *, PATHPTR_T, wIndex_t, trkSeg_p, EPINX_T, trkEndPt_t *); +void ParseCompoundTitle( char *, char **, int *, char **, int *, char **, int * ); +void FormatCompoundTitle( long, char *); +void ComputeCompoundBoundingBox( track_p); +turnoutInfo_t * FindCompound( long, char *, char * ); +char * CompoundGetTitle( turnoutInfo_t * ); +void CompoundListLoadData( wList_p, turnoutInfo_t *, long ); +void CompoundClearDemoDefns( void ); +void SetDescriptionOrig( track_p ); +void DrawCompoundDescription( track_p, drawCmd_p, wDrawColor ); +DIST_T DistanceCompound( track_p, coOrd * ); +void DescribeCompound( track_p, char *, CSIZE_T ); +void DeleteCompound( track_p ); +track_p NewCompound( TRKTYP_T, TRKINX_T, coOrd, ANGLE_T, char *, EPINX_T, trkEndPt_t *, int, char *, wIndex_t, trkSeg_p ); +BOOL_T WriteCompound( track_p, FILE * ); +void ReadCompound( char *, TRKTYP_T ); +void MoveCompound( track_p, coOrd ); +void RotateCompound( track_p, coOrd, ANGLE_T ); +void RescaleCompound( track_p, FLOAT_T ); +void FlipCompound( track_p, coOrd, ANGLE_T ); +BOOL_T EnumerateCompound( track_p ); + +/* cgroup.c */ +void UngroupCompound( track_p ); +void DoUngroup( void ); +void DoGroup( void ); + +/* dcmpnd.c */ +void UpdateTitleMark( char *, SCALEINX_T ); +void DoUpdateTitles( void ); +BOOL_T RefreshCompound( track_p, BOOL_T ); + +/* cturnout.c */ +EPINX_T TurnoutPickEndPt( coOrd p, track_p ); +void GetSegInxEP( signed char, int *, EPINX_T * ); +wIndex_t CheckPaths( wIndex_t, trkSeg_p, PATHPTR_T ); +turnoutInfo_t * CreateNewTurnout( char *, char *, wIndex_t, trkSeg_p, wIndex_t, PATHPTR_T, EPINX_T, trkEndPt_t *, wBool_t ); +turnoutInfo_t * TurnoutAdd( long, SCALEINX_T, wList_p, coOrd *, EPINX_T ); +STATUS_T CmdTurnoutAction( wAction_t, coOrd ); +BOOL_T ConnectAdjustableTracks( track_p trk1, EPINX_T ep1, track_p trk2, EPINX_T ep2 ); +track_p NewHandLaidTurnout( coOrd, ANGLE_T, coOrd, ANGLE_T, coOrd, ANGLE_T, ANGLE_T ); +void NextTurnoutPosition( track_p trk ); + +/* ctodesgn.c */ +void EditCustomTurnout( turnoutInfo_t *, turnoutInfo_t * ); +long ComputeTurnoutRoadbedSide( trkSeg_p, int, int, ANGLE_T, DIST_T ); + +/* cstruct.c */ +turnoutInfo_t * CreateNewStructure( char *, char *, wIndex_t, trkSeg_p, BOOL_T ); +turnoutInfo_t * StructAdd( long, SCALEINX_T, wList_p, coOrd * ); +STATUS_T CmdStructureAction( wAction_t, coOrd ); +BOOL_T StructLoadCarDescList( wList_p ); + +/* cstrdsgn.c */ +void EditCustomStructure( turnoutInfo_t * ); + +STATUS_T CmdCarDescAction( wAction_t, coOrd ); +BOOL_T CarCustomSave( FILE * ); + +#endif diff --git a/app/bin/cparalle.c b/app/bin/cparalle.c new file mode 100644 index 0000000..28e3513 --- /dev/null +++ b/app/bin/cparalle.c @@ -0,0 +1,186 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cparalle.c,v 1.5 2009-05-25 18:11:03 m_fischer Exp $ + * + * PARALLEL + * + */ + +/* 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 "track.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "i18n.h" + +static struct { + track_p Trk; + coOrd orig; + } Dpa; + +static DIST_T parSeparation = 1.0; + +static paramFloatRange_t r_0o1_100 = { 0.1, 100.0, 100 }; +static paramData_t parSepPLs[] = { +#define parSepPD (parSepPLs[0]) + { PD_FLOAT, &parSeparation, "separation", PDO_DIM|PDO_NOPREF|PDO_NOPREF, &r_0o1_100, N_("Separation") } }; +static paramGroup_t parSepPG = { "parallel", 0, parSepPLs, sizeof parSepPLs/sizeof parSepPLs[0] }; + + +static STATUS_T CmdParallel( wAction_t action, coOrd pos ) +{ + + DIST_T d; + track_p t=NULL; + coOrd p; + static coOrd p0, p1; + ANGLE_T a; + track_p t0, t1; + EPINX_T ep0=-1, ep1=-1; + wControl_p controls[2]; + char * labels[1]; + + switch (action) { + + case C_START: + if (parSepPD.control==NULL) { + ParamCreateControls( &parSepPG, NULL ); + } + sprintf( message, "parallel-separation-%s", curScaleName ); + parSeparation = ceil(13.0*12.0/curScaleRatio); + wPrefGetFloat( "misc", message, &parSeparation, parSeparation ); + ParamLoadControls( &parSepPG ); + ParamGroupRecord( &parSepPG ); + controls[0] = parSepPD.control; + controls[1] = NULL; + labels[0] = N_("Separation"); + InfoSubstituteControls( controls, labels ); + /*InfoMessage( "Select track" );*/ + return C_CONTINUE; + + case C_DOWN: + if ( parSeparation <= 0.0 ) { + ErrorMessage( MSG_PARALLEL_SEP_GTR_0 ); + return C_ERROR; + } + controls[0] = parSepPD.control; + controls[1] = NULL; + labels[0] = N_("Separation"); + InfoSubstituteControls( controls, labels ); + ParamLoadData( &parSepPG ); + Dpa.orig = pos; + Dpa.Trk = OnTrack( &Dpa.orig, TRUE, TRUE ); + if (!Dpa.Trk) { + return C_CONTINUE; + } + if ( !QueryTrack( Dpa.Trk, Q_CAN_PARALLEL ) ) { + Dpa.Trk = NULL; + return C_CONTINUE; + } + /* in case query has changed things (eg joint) */ + /* + * this seems to cause problems so I commented it out + * until further investigation shows the necessity + */ + //Dpa.Trk = OnTrack( &Dpa.orig, TRUE, TRUE ); + tempSegs_da.cnt = 0; + + case C_MOVE: + if (Dpa.Trk == NULL) return C_CONTINUE; + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + if ( !MakeParallelTrack( Dpa.Trk, pos, parSeparation, NULL, &p0, &p1 ) ) { + Dpa.Trk = NULL; + return C_CONTINUE; + } + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + return C_CONTINUE; + + case C_UP: + if (Dpa.Trk == NULL) return C_CONTINUE; + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + p = p0; + if ((t0=OnTrack( &p, FALSE, TRUE )) != NULL) { + ep0 = PickEndPoint( p, t0 ); + if ( GetTrkEndTrk(t0,ep0) != NULL ) { + t0 = NULL; + } else { + p = GetTrkEndPos( t0, ep0 ); + d = FindDistance( p, p0 ); + if ( d > connectDistance ) + t0 = NULL; + } + } + p = p1; + if ((t1=OnTrack( &p, FALSE, TRUE )) != NULL) { + ep1 = PickEndPoint( p, t1 ); + if ( GetTrkEndTrk(t1,ep1) != NULL ) { + t1 = NULL; + } else { + p = GetTrkEndPos( t1, ep1 ); + d = FindDistance( p, p1 ); + if ( d > connectDistance ) + t1 = NULL; + } + } + UndoStart( _("Create Parallel Track"), "newParallel" ); + if ( !MakeParallelTrack( Dpa.Trk, pos, parSeparation, &t, NULL, NULL ) ) { + return C_TERMINATE; + } + CopyAttributes( Dpa.Trk, t ); + if ( t0 ) { + a = NormalizeAngle( GetTrkEndAngle( t0, ep0 ) - GetTrkEndAngle( t, 0 ) + (180.0+connectAngle/2.0) ); + if (a < connectAngle) { + DrawEndPt( &mainD, t0, ep0, wDrawColorWhite ); + ConnectTracks( t0, ep0, t, 0 ); + DrawEndPt( &mainD, t0, ep0, wDrawColorBlack ); + } + } + if ( t1 ) { + a = NormalizeAngle( GetTrkEndAngle( t1, ep1 ) - GetTrkEndAngle( t, 1 ) + (180.0+connectAngle/2.0) ); + if (a < connectAngle) { + DrawEndPt( &mainD, t1, ep1, wDrawColorWhite ); + ConnectTracks( t1, ep1, t, 1 ); + DrawEndPt( &mainD, t1, ep1, wDrawColorBlack ); + } + } + DrawNewTrack( t ); + UndoEnd(); + InfoSubstituteControls( NULL, NULL ); + sprintf( message, "parallel-separation-%s", curScaleName ); + wPrefSetFloat( "misc", message, parSeparation ); + return C_TERMINATE; + + case C_REDRAW: + return C_CONTINUE; + + case C_CANCEL: + InfoSubstituteControls( NULL, NULL ); + return C_TERMINATE; + + } + return C_CONTINUE; +} + + +#include "bitmaps/parallel.xpm" + +EXPORT void InitCmdParallel( wMenu_p menu ) +{ + AddMenuButton( menu, CmdParallel, "cmdParallel", _("Parallel"), wIconCreatePixMap(parallel_xpm), LEVEL0_50, IC_STICKY|IC_POPUP, ACCL_PARALLEL, NULL ); + ParamRegister( &parSepPG ); +} diff --git a/app/bin/cprint.c b/app/bin/cprint.c new file mode 100644 index 0000000..d89d1e2 --- /dev/null +++ b/app/bin/cprint.c @@ -0,0 +1,1301 @@ +/** \file cprint.c + * Printing functions. + * + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cprint.c,v 1.6 2009-08-16 13:26:41 m_fischer Exp $ + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2005 Dave Bullis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <sys/types.h> +#include <time.h> +#include <string.h> +#include <ctype.h> +#include "track.h" +#include "i18n.h" + + +#define PRINT_GAUDY (0) +#define PRINT_PLAIN (1) +#define PRINT_BARE (2) +#define PORTRAIT (0) +#define LANDSCAPE (1) + +#define PRINTOPTION_SNAP (1<<0) + +typedef struct { + int x0, x1, y0, y1; + char * bm; + int memsize; + coOrd orig; + coOrd size; + ANGLE_T angle; + } bitmap_t; +static bitmap_t bm, bm0; +#define BITMAP( BM, X, Y ) \ + (BM).bm[ (X)-(BM).x0 + ((Y)-(BM).y0) * ((BM).x1-(BM).x0) ] + +struct { + coOrd size; + coOrd orig; + ANGLE_T angle; + } currPrintGrid, newPrintGrid; + + +/* + * GUI VARS + */ + + +static long printGaudy = 1; +static long printRegistrationMarks = 1; +static long printPhysSize = FALSE; +static long printFormat = PORTRAIT; +static long printOrder = 0; +static long printGrid = 0; +static long printRuler = 0; +static long printRoadbed = 0; +static DIST_T printRoadbedWidth = 0.0; +static BOOL_T printRotate = FALSE; +static BOOL_T rotateCW = FALSE; + +static double printScale = 16; +static long iPrintScale = 16; +static coOrd maxPageSize; +static coOrd realPageSize; + +static wWin_p printWin; + +static wMenu_p printGridPopupM; + +static wIndex_t pageCount = 0; + +static int log_print = 0; + +static void PrintSnapShot( void ); +static void DoResetGrid( void ); +static void DoPrintSetup( void ); +static void PrintClear( void ); +static void PrintMaxPageSize( void ); + +static char * printFormatLabels[] = { N_("Portrait"), N_("Landscape"), NULL }; +static char * printOrderLabels[] = { N_("Normal"), N_("Reverse"), NULL }; +static char * printGaudyLabels[] = { N_("Engineering Data"), NULL }; +static char * printRegistrationMarksLabels[] = { N_("Print Registration Marks"), NULL }; +static char * printPhysSizeLabels[] = { N_("Ignore Page Margins"), NULL }; +static char * printGridLabels[] = { N_("Print Snap Grid"), NULL }; +static char * printRulerLabels[] = { N_("Print Rulers"), NULL }; +static char * printRoadbedLabels[] = { N_("Print Roadbed Outline"), NULL }; +static paramIntegerRange_t rminScale_999 = { 1, 999, 0, PDO_NORANGECHECK_HIGH }; +static paramFloatRange_t r0_ = { 0, 0, 0, PDO_NORANGECHECK_HIGH }; +static paramFloatRange_t r1_ = { 1, 0, 0, PDO_NORANGECHECK_HIGH }; +static paramFloatRange_t r_10_99999 = { -10, 99999, 0, PDO_NORANGECHECK_HIGH }; +static paramFloatRange_t r0_360 = { 0, 360 }; + +static paramData_t printPLs[] = { +/*0*/ { PD_LONG, &iPrintScale, "scale", 0, &rminScale_999, N_("Print Scale"), 0, (void*)1 }, +/*1*/ { PD_FLOAT, &newPrintGrid.size.x, "pagew", PDO_DIM|PDO_SMALLDIM|PDO_NORECORD|PDO_NOPREF, &r1_, N_("Page Width"), 0, (void*)2 }, +/*2*/ { PD_BUTTON, (void*)PrintMaxPageSize, "max", PDO_DLGHORZ, NULL, N_("Max") }, +/*3*/ { PD_FLOAT, &newPrintGrid.size.y, "pageh", PDO_DIM|PDO_SMALLDIM|PDO_NORECORD|PDO_NOPREF, &r1_, N_("Height"), 0, (void*)2 }, +/*4*/ { PD_BUTTON, (void*)PrintSnapShot, "snapshot", PDO_DLGHORZ, NULL, N_("Snap Shot") }, +/*5*/ { PD_RADIO, &printFormat, "format", 0, printFormatLabels, N_("Page Format"), BC_HORZ|BC_NOBORDER, (void*)1 }, +/*6*/ { PD_RADIO, &printOrder, "order", PDO_DLGBOXEND, printOrderLabels, N_("Print Order"), BC_HORZ|BC_NOBORDER }, + +/*7*/ { PD_TOGGLE, &printGaudy, "style", PDO_DLGNOLABELALIGN, printGaudyLabels, NULL, BC_HORZ|BC_NOBORDER, (void*)1 }, +/*8*/ { PD_TOGGLE, &printPhysSize, "physsize", PDO_DLGNOLABELALIGN, printPhysSizeLabels, NULL, BC_HORZ|BC_NOBORDER, (void*)1 }, +#define I_REGMARKS (9) +/*9*/ { PD_TOGGLE, &printRegistrationMarks, "registrationMarks", PDO_DLGNOLABELALIGN, printRegistrationMarksLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_GRID (10) +/*10*/ { PD_TOGGLE, &printGrid, "grid", PDO_DLGNOLABELALIGN, printGridLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_RULER (11) +/*11*/ { PD_TOGGLE, &printRuler, "ruler", PDO_DLGNOLABELALIGN, printRulerLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_ROADBED (12) +/*12*/{ PD_TOGGLE, &printRoadbed, "roadbed", PDO_DLGNOLABELALIGN, printRoadbedLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_ROADBEDWIDTH (13) +/*13*/{ PD_FLOAT, &printRoadbedWidth, "roadbedWidth", PDO_DIM|PDO_DLGBOXEND, &r0_, N_("Width") }, +/*14*/{ PD_FLOAT, &newPrintGrid.orig.x, "origx", PDO_DIM|PDO_DLGRESETMARGIN, &r_10_99999, N_("Origin: X"), 0, (void*)2 }, +/*15*/ { PD_FLOAT, &newPrintGrid.orig.y, "origy", PDO_DIM, &r_10_99999, N_("Y"), 0, (void*)2 }, +/*16*/ { PD_BUTTON, (void*)DoResetGrid, "reset", PDO_DLGHORZ, NULL, N_("Reset") }, +/*17*/ { PD_FLOAT, &newPrintGrid.angle, "origa", PDO_ANGLE|PDO_DLGBOXEND, &r0_360, N_("Angle"), 0, (void*)2 }, +/*18*/ { PD_BUTTON, (void*)DoPrintSetup, "setup", PDO_DLGCMDBUTTON, NULL, N_("Setup") }, +/*19*/ { PD_BUTTON, (void*)PrintClear, "clear", 0, NULL, N_("Clear") }, +#define I_PAGECNT (20) +/*20*/ { PD_MESSAGE, N_("0 pages"), NULL, 0, (void*)80 }, +/*21*/ { PD_MESSAGE, N_("selected"), NULL, 0, (void*)80 } }; + +static paramGroup_t printPG = { "print", PGO_PREFMISCGROUP, printPLs, sizeof printPLs/sizeof printPLs[0] }; + + +/***************************************************************************** + * + * TEMP DRAW + * + */ + + +static void ChangeDim( void ) +{ + int x, y, x0, x1, y0, y1; + coOrd p0; + int size; + bitmap_t tmpBm; + BOOL_T selected; + + MapGrid( zero, mapD.size, 0.0, currPrintGrid.orig, currPrintGrid.angle, currPrintGrid.size.x, currPrintGrid.size.y, + &x0, &x1, &y0, &y1 ); +#ifdef LATER + d0 = sqrt( mapD.size.x * mapD.size.x + mapD.size.y * mapD.size.y ); + + Translate( &p1, currPrintGrid.orig, currPrintGrid.angle, d0 ); + p0 = currPrintGrid.orig; + ClipLine( &p0, &p1, zero, 0.0, mapD.size ); + d1 = FindDistance( currPrintGrid.orig, p1 ); + y1 = (int)ceil(d1/currPrintGrid.size.y); + + Translate( &p1, currPrintGrid.orig, currPrintGrid.angle+180, d0 ); + p0 = currPrintGrid.orig; + ClipLine( &p0, &p1, zero, 0.0, mapD.size ); + d1 = FindDistance( currPrintGrid.orig, p1 ); + y0 = -(int)floor(d1/currPrintGrid.size.y); + + Translate( &p1, currPrintGrid.orig, currPrintGrid.angle+90, d0 ); + p0 = currPrintGrid.orig; + ClipLine( &p0, &p1, zero, 0.0, mapD.size ); + d1 = FindDistance( currPrintGrid.orig, p1 ); + x1 = (int)ceil(d1/currPrintGrid.size.x); + + Translate( &p1, currPrintGrid.orig, currPrintGrid.angle+270, d0 ); + p0 = currPrintGrid.orig; + ClipLine( &p0, &p1, zero, 0.0, mapD.size ); + d1 = FindDistance( currPrintGrid.orig, p1 ); + x0 = -(int)floor(d1/currPrintGrid.size.x); +#endif + + if ( x0==bm.x0 && x1==bm.x1 && y0==bm.y0 && y1==bm.y1 ) + return; + size = (x1-x0) * (y1-y0); + if (size > bm0.memsize) { + bm0.bm = MyRealloc( bm0.bm, size ); + bm0.memsize = size; + } + bm0.x0 = x0; bm0.x1 = x1; bm0.y0 = y0; bm0.y1 = y1; + memset( bm0.bm, 0, bm0.memsize ); + pageCount = 0; + if (bm.bm) { + for ( x=bm.x0; x<bm.x1; x++ ) { + for ( y=bm.y0; y<bm.y1; y++ ) { + selected = BITMAP( bm, x, y ); + if (selected) { + p0.x = bm.orig.x + x * bm.size.x + bm.size.x/2.0; + p0.y = bm.orig.y + y * bm.size.y + bm.size.y/2.0; + Rotate( &p0, bm.orig, bm.angle ); + p0.x -= currPrintGrid.orig.x; + p0.y -= currPrintGrid.orig.y; + Rotate( &p0, zero, -currPrintGrid.angle ); + x0 = (int)floor(p0.x/currPrintGrid.size.x); + y0 = (int)floor(p0.y/currPrintGrid.size.y); + if ( x0>=bm0.x0 && x0<bm0.x1 && y0>=bm0.y0 && y0<bm0.y1 ) { + if ( BITMAP( bm0, x0, y0 ) == FALSE ) { + pageCount++; + BITMAP( bm0, x0, y0 ) = TRUE; + } + } + } + } + } + } + tmpBm = bm0; + bm0 = bm; + bm = tmpBm; + bm.orig = currPrintGrid.orig; + bm.size = currPrintGrid.size; + bm.angle = currPrintGrid.angle; + sprintf( message, _("%d pages"), pageCount ); + ParamLoadMessage( &printPG, I_PAGECNT, message ); + ParamDialogOkActive( &printPG, pageCount!=0 ); +} + + +static void MarkPage( + wIndex_t x, + wIndex_t y ) +/* + * Hilite a area + */ +{ + coOrd p[4]; + +LOG1( log_print, ( "MarkPage( %d, %d )\n", x, y) ) + if ( x<bm.x0 || x>=bm.x1 || y<bm.y0 || y>=bm.y1) { + ErrorMessage( MSG_OUT_OF_BOUNDS ); + return; + } + p[0].x = p[3].x = currPrintGrid.orig.x + x * currPrintGrid.size.x; + p[0].y = p[1].y = currPrintGrid.orig.y + y * currPrintGrid.size.y; + p[2].x = p[1].x = p[0].x + currPrintGrid.size.x; + p[2].y = p[3].y = p[0].y + currPrintGrid.size.y; + Rotate( &p[0], currPrintGrid.orig, currPrintGrid.angle ); + Rotate( &p[1], currPrintGrid.orig, currPrintGrid.angle ); + Rotate( &p[2], currPrintGrid.orig, currPrintGrid.angle ); + Rotate( &p[3], currPrintGrid.orig, currPrintGrid.angle ); +LOG( log_print, 2, ( "MP(%d,%d) [%0.3f %0.3f] x [%0.3f %0.3f]\n", x, y, p[0].x, p[0].y, p[2].x, p[2].y ) ) + DrawHilightPolygon( &mainD, p, 4 ); +} + + +static void SelectPage( coOrd pos ) +{ + int x, y; + BOOL_T selected; + /*PrintUpdate();*/ + pos.x -= currPrintGrid.orig.x; + pos.y -= currPrintGrid.orig.y; + Rotate( &pos, zero, -currPrintGrid.angle ); + x = (int)floor(pos.x/currPrintGrid.size.x); + y = (int)floor(pos.y/currPrintGrid.size.y); + if ( x<bm.x0 || x>=bm.x1 || y<bm.y0 || y>=bm.y1) + return; + selected = BITMAP( bm, x, y ); + pageCount += (selected?-1:1); + BITMAP( bm, x, y ) = !selected; + MarkPage( x, y ); + sprintf( message, _("%d pages"), pageCount ); + ParamLoadMessage( &printPG, I_PAGECNT, message ); + ParamDialogOkActive( &printPG, pageCount!=0 ); +} + + +static void DrawPrintGrid( void ) +/* + * Draw a grid using currPrintGrid.orig, currPrintGrid.angle, currPrintGrid.size. + * Drawing it twice erases the grid. + * Also hilite any marked pages. + */ +{ + wIndex_t x, y; + + DrawGrid( &tempD, &mapD.size, currPrintGrid.size.x, currPrintGrid.size.y, 0, 0, currPrintGrid.orig, currPrintGrid.angle, wDrawColorBlack, TRUE ); + + for (y=bm.y0; y<bm.y1; y++) + for (x=bm.x0; x<bm.x1; x++) + if (BITMAP(bm,x,y)) { + MarkPage( x, y ); + } +} + +/***************************************************************************** + * + * PRINTING FUNCTIONS + * + */ + + +static drawCmd_t print_d = { + NULL, + &printDrawFuncs, + DC_PRINT, + 16.0, + 0.0, + {0.0, 0.0}, {1.0, 1.0}, + Pix2CoOrd, CoOrd2Pix }; + +static drawCmd_t page_d = { + NULL, + &printDrawFuncs, + DC_PRINT, + 1.0, + 0.0, + {0.0, 0.0}, {1.0, 1.0}, + Pix2CoOrd, CoOrd2Pix }; + + +/** + * Print the basic layout for a trackplan. This includes the frame and some + * information like room size, print scale etc.. + * + * \param roomSize IN size of the layout + */ + +static void PrintGaudyBox( + coOrd roomSize ) +{ + coOrd p00, p01, p10, p11; + struct tm *tm; + time_t clock; + char dat[STR_SIZE]; + wFont_p fp; + DIST_T pageW, pageH; + DIST_T smiggin; + coOrd textsize; + + /*GetTitle();*/ + time(&clock); + tm = localtime(&clock); + strftime( dat, STR_SIZE, "%x", tm ); + + smiggin = wDrawGetDPI( print_d.d ); + if (smiggin>4.0) + smiggin = 4.0/smiggin; + pageW = currPrintGrid.size.x/print_d.scale; + pageH = currPrintGrid.size.y/print_d.scale; + /* Draw some lines */ + p00.x = p01.x = 0.0; + p00.y = p10.y = 0.0; + p10.x = p11.x = pageW-smiggin; + p01.y = p11.y = pageH+1.0-smiggin; + + DrawLine( &page_d, p00, p10, 0, wDrawColorBlack ); + DrawLine( &page_d, p10, p11, 0, wDrawColorBlack ); + DrawLine( &page_d, p11, p01, 0, wDrawColorBlack ); + DrawLine( &page_d, p01, p00, 0, wDrawColorBlack ); + + p00.y = p10.y = 1.0; + DrawLine( &page_d, p00, p10, 0, wDrawColorBlack ); + p00.y = p10.y = 0.5; + DrawLine( &page_d, p00, p10, 0, wDrawColorBlack ); + p00.y = 0.5; + p01.y = 1.0; + p00.x = 0.05; p00.y = 0.5+0.05; + fp = wStandardFont( F_TIMES, TRUE, TRUE ); + DrawString( &page_d, p00, 0.0, sProdName, fp, 30.0, wDrawColorBlack ); + + p00.y = 0.5; p01.y = 1.0; + p00.x = p01.x = (157.0/72.0)+0.1; + DrawLine( &page_d, p00, p01, 0, wDrawColorBlack ); + p00.x = p01.x = pageW-((157.0/72.0)+0.1); + DrawLine( &page_d, p00, p01, 0, wDrawColorBlack ); + + fp = wStandardFont( F_TIMES, FALSE, FALSE ); + p00.x = pageW-((157.0/72.0)+0.05); p00.y = 0.5+0.25+0.05; + DrawString( &page_d, p00, 0.0, dat, fp, 16.0, wDrawColorBlack ); + p00.y = 0.5+0.05; + + DrawTextSize( &mainD, Title1, fp, 16.0, FALSE, &textsize ); + p00.x = (pageW/2.0)-(textsize.x/2.0); + p00.y = 0.75+0.05; + DrawString( &page_d, p00, 0.0, Title1, fp, 16.0, wDrawColorBlack ); + DrawTextSize( &mainD, Title2, fp, 16.0, FALSE, &textsize ); + p00.x = (pageW/2.0)-(textsize.x/2.0); + p00.y = 0.50+0.05; + DrawString( &page_d, p00, 0.0, Title2, fp, 16.0, wDrawColorBlack ); + + sprintf( dat, _("PrintScale 1:%ld Room %s x %s Model Scale %s File %s"), + (long)printScale, + FormatDistance( roomSize.x ), + FormatDistance( roomSize.y ), + curScaleName, curFileName ); + p00.x = 0.05; p00.y = 0.25+0.05; + DrawString( &page_d, p00, 0.0, dat, fp, 16.0, wDrawColorBlack ); +} + + +static void PrintPlainBox( + wPos_t x, + wPos_t y, + coOrd *corners ) +{ + coOrd p00, p01, p10, p11; + char tmp[30]; + wFont_p fp; + DIST_T pageW, pageH; + DIST_T smiggin; + + smiggin = wDrawGetDPI( print_d.d ); + if (smiggin>4.0) + smiggin = 4.0/smiggin; + + pageW = currPrintGrid.size.x/print_d.scale; + pageH = currPrintGrid.size.y/print_d.scale; + + p00.x = p01.x = 0.0; + p00.y = p10.y = 0.0; + p10.x = p11.x = pageW-smiggin; + p01.y = p11.y = pageH-smiggin; + DrawLine( &page_d, p00, p10, 0, wDrawColorBlack ); + DrawLine( &page_d, p10, p11, 0, wDrawColorBlack ); + DrawLine( &page_d, p11, p01, 0, wDrawColorBlack ); + DrawLine( &page_d, p01, p00, 0, wDrawColorBlack ); + + fp = wStandardFont( F_HELV, FALSE, FALSE ); + sprintf( tmp, "[%d,%d]", x, y ); + p00.x = pageW/2.0 - 20.0/72.0; + p00.y = pageH - 10.0/72.0; + DrawString( &page_d, p00, 0.0, tmp, fp, 4.0, wDrawColorBlack ); + + sprintf( tmp, "[%0.2f,%0.2f]", corners[0].x, corners[0].y ); + p00.x = 4.0/72.0; + p00.y = 4.0/72.0; + DrawString( &page_d, p00, 0.0, tmp, fp, 4.0, wDrawColorBlack ); + + sprintf( tmp, "[%0.2f,%0.2f]", corners[1].x, corners[1].y ); + p00.x = pageW - 40.0/72.0; + p00.y = 4.0/72.0; + DrawString( &page_d, p00, 0.0, tmp, fp, 4.0, wDrawColorBlack ); + + sprintf( tmp, "[%0.2f,%0.2f]", corners[2].x, corners[2].y ); + p00.x = pageW - 40.0/72.0; + p00.y = pageH - 10.0/72.0; + DrawString( &page_d, p00, 0.0, tmp, fp, 4.0, wDrawColorBlack ); + + sprintf( tmp, "[%0.2f,%0.2f]", corners[3].x, corners[3].y ); + p00.x = 4.0/72.0; + p00.y = pageH - 10.0/72.0; + DrawString( &page_d, p00, 0.0, tmp, fp, 4.0, wDrawColorBlack ); + +} + +/***************************************************************************** + * + * BUTTON HANDLERS + * + */ + + +static void PrintEnableControls( void ) +{ + if (printScale <= 1) { + ParamLoadControl( &printPG, I_REGMARKS ); + ParamControlActive( &printPG, I_REGMARKS, TRUE ); + } else { + ParamLoadControl( &printPG, I_REGMARKS ); + printRegistrationMarks = 0; + ParamControlActive( &printPG, I_REGMARKS, FALSE ); + } + if (printScale <= (twoRailScale*2+1)/2.0) { + ParamLoadControl( &printPG, I_ROADBED ); + ParamControlActive( &printPG, I_ROADBED, TRUE ); + ParamControlActive( &printPG, I_ROADBEDWIDTH, TRUE ); + } else { + printRoadbed = 0; + ParamLoadControl( &printPG, I_ROADBED ); + ParamControlActive( &printPG, I_ROADBED, FALSE ); + ParamControlActive( &printPG, I_ROADBEDWIDTH, FALSE ); + } +} + + +#ifdef LATER +static void PrintSetOrient( void ) +/* + * Called when print landscape/portrait toggled + */ +{ + DrawPrintGrid(); + ParamLoadData( &printPG ); + currPrintGrid = newPrintGrid; + ChangeDim(); + DrawPrintGrid(); +} +#endif + + +static void PrintUpdate( int inx0 ) +/* + * Called when print page size (x or y) is changed. + * Checks for valid values + */ +{ + int inx; + + DrawPrintGrid(); + ParamLoadData( &printPG ); + + if (newPrintGrid.size.x > maxPageSize.x+0.01 || + newPrintGrid.size.y > maxPageSize.y+0.01) { + NoticeMessage( MSG_PRINT_MAX_SIZE, _("Ok"), NULL, + FormatSmallDistance(maxPageSize.x), FormatSmallDistance(maxPageSize.y) ); + } + if (newPrintGrid.size.x > maxPageSize.x) { + newPrintGrid.size.x = maxPageSize.x; + ParamLoadControl( &printPG, 1 ); + } + if (newPrintGrid.size.y > maxPageSize.y) { + newPrintGrid.size.y = maxPageSize.y; + ParamLoadControl( &printPG, 3 ); + } + currPrintGrid = newPrintGrid; + for ( inx = 0; inx < sizeof printPLs/sizeof printPLs[0]; inx++ ) { + if ( inx != inx0 && printPLs[inx].context == (void*)2 ) + ParamLoadControl( &printPG, inx ); + } + ChangeDim(); + DrawPrintGrid(); +} + + +static void SetPageSize( BOOL_T doScale ) +{ + WDOUBLE_T temp, x, y; + if (printPhysSize) + wPrintGetPhysSize( &x, &y ); + else + wPrintGetPageSize( &x, &y ); + maxPageSize.x = x; + maxPageSize.y = y; + realPageSize = maxPageSize; + if ( (printFormat == PORTRAIT) == (maxPageSize.x > maxPageSize.y) ) { + temp = maxPageSize.x; + maxPageSize.x = maxPageSize.y; + maxPageSize.y = temp; + printRotate = TRUE; + } else { + printRotate = FALSE; + } + if (doScale) { + if (printGaudy) + maxPageSize.y -= 1.0; + maxPageSize.x *= printScale; + maxPageSize.y *= printScale; + } +} + + +static void PrintMaxPageSize( void ) +/* + * Called when print:maxPageSize button is clicked. + * Set print page size to maximum + * (depending on paper size, scale and orientation) + */ +{ + DrawPrintGrid(); + SetPageSize( TRUE ); + currPrintGrid.size = maxPageSize; + newPrintGrid = currPrintGrid; + ParamLoadControls( &printPG ); + ChangeDim(); + DrawPrintGrid(); + wShow( printWin); +} + + +static void DoPrintScale( void ) +/* + * Called whenever print scale or orientation changes. + */ +{ + printScale = iPrintScale; + PrintMaxPageSize(); + PrintEnableControls(); +} + + +static void DoPrintSetup( void ) +{ + wPrintSetup( (wPrintSetupCallBack_p)DoPrintScale ); +} + + +static void PrintClear( void ) +/* + * Called when print:clear button is clicked. + * Flip the status of all printable pages + * (Thus making them non-print) + */ +{ + wIndex_t x, y; + for (y=bm.y0; y<bm.y1; y++) + for (x=bm.x0; x<bm.x1; x++) + if (BITMAP(bm,x,y)) { + BITMAP(bm,x,y) = 0; + MarkPage( x, y ); + } + pageCount = 0; + ParamLoadMessage( &printPG, I_PAGECNT, _("0 pages") ); + ParamDialogOkActive( &printPG, FALSE ); +} + + +static void PrintSnapShot( void ) +/* + * Called when print:SnapShot button is clicked. + * Set scale and orientation so the whole layout is printed on one page. + */ +{ + coOrd size; + ANGLE_T scaleX, scaleY; + long scaleH, scaleV; + int i; + coOrd pageSize; + POS_T t; + + PrintClear(); + DrawPrintGrid(); + SetPageSize( FALSE ); + pageSize = realPageSize; + if (pageSize.x > pageSize.y) { + t = pageSize.x; + pageSize.x = pageSize.y; + pageSize.y = t; + } + size = mapD.size; + + scaleH = 1; + for (i=0;i<3;i++) { + size = mapD.size; + size.x += 0.75*scaleH; + size.y += 0.75*scaleH; + if (printGaudy) + size.y += 1.0*scaleH; + scaleX = size.x/pageSize.x; + scaleY = size.y/pageSize.y; + scaleH = (long)ceil(max( scaleX, scaleY )); + } + + scaleV = 1; + for (i=0;i<3;i++) { + size = mapD.size; + size.x += 0.75*scaleV; + size.y += 0.75*scaleV; + if (printGaudy) + size.y += 1.0*scaleV; + scaleX = size.x/pageSize.y; + scaleY = size.y/pageSize.x; + scaleV = (long)ceil(max( scaleX, scaleY )); + } + + if ( scaleH <= scaleV ) { + printScale = scaleH; + printFormat = PORTRAIT; + } else { + printScale = scaleV; + printFormat = LANDSCAPE; + } + + SetPageSize( TRUE ); +/* + if (printFormat == LANDSCAPE) { + currPrintGrid.orig.x = -0.5*printScale; + currPrintGrid.orig.y = maxPageSize.x-0.5*printScale; + currPrintGrid.angle = 90.0; + } else {*/ + currPrintGrid.orig.x = -0.5*printScale; + currPrintGrid.orig.y = -0.5*printScale; + currPrintGrid.angle = 0.0; +/* }*/ + currPrintGrid.size = maxPageSize; + newPrintGrid = currPrintGrid; + iPrintScale = (long)printScale; + ParamLoadControls( &printPG ); + ParamGroupRecord( &printPG ); + ChangeDim(); + pageCount = 1; + BITMAP(bm,0,0) = TRUE; + DrawPrintGrid(); + ParamLoadMessage( &printPG, I_PAGECNT, _("1 page") ); + ParamDialogOkActive( &printPG, TRUE ); + PrintEnableControls(); + wShow( printWin ); +} + + +static void DrawRegistrationMarks( drawCmd_p d ) +{ + long x, y, delta, divisor; + coOrd p0, p1, qq, q0, q1; + POS_T len; + char msg[10]; + wFont_p fp; + wFontSize_t fs; + fp = wStandardFont( F_TIMES, FALSE, FALSE ); + if ( units==UNITS_METRIC ) { + delta = 10; + divisor = 100; + } else { + delta = 3; + divisor = 12; + } + for ( x=delta; (POS_T)x<PutDim(mapD.size.x); x+=delta ) { + qq.x = p0.x = p1.x = (POS_T)GetDim(x); + p0.y = 0.0; + p1.y = mapD.size.y; + if (!ClipLine( &p0, &p1, d->orig, d->angle, d->size )) + continue; + for ( y=(long)(ceil(PutDim(p0.y)/delta)*delta); (POS_T)y<PutDim(p1.y); y+=delta ) { + qq.y = (POS_T)GetDim(y); + q0.x = q1.x = qq.x; + if ( x%divisor == 0 && y%divisor == 0 ) { + len = 0.25; + fs = 12.0; + } else { + len = 0.125; + fs = 8.0; + } + q0.y = qq.y-len; + q1.y = qq.y+len; + DrawLine( d, q0, q1, 0, wDrawColorBlack ); + q0.y = q1.y = qq.y; + q0.x = qq.x-len; + q1.x = qq.x+len; + DrawLine( d, q0, q1, 0, wDrawColorBlack ); + q0.x = qq.x + len/4;; + q0.y = qq.y + len/4;; + if (units == UNITS_METRIC) + sprintf( msg, "%0.1fm", (DOUBLE_T)x/100.0 ); + else + sprintf( msg, "%ld\' %ld\"", x/12, x%12 ); + DrawString( d, q0, 0.0, msg, fp, fs, wDrawColorBlack ); + q0.y = qq.y - len*3/4; + if (units == UNITS_METRIC) + sprintf( msg, "%0.1fm", (DOUBLE_T)y/100.0 ); + else + sprintf( msg, "%ld\' %ld\"", y/12, y%12 ); + DrawString( d, q0, 0.0, msg, fp, fs, wDrawColorBlack ); + } + } +} + + +static BOOL_T PrintPage( + int x, + int y ) +{ + coOrd orig, p[4], minP, maxP; + int i; + coOrd clipOrig, clipSize; + wFont_p fp; + coOrd roomSize; + + if (BITMAP(bm,x,y)) { + orig.x = currPrintGrid.orig.x + x*currPrintGrid.size.x; + orig.y = currPrintGrid.orig.y + y*currPrintGrid.size.y; + Rotate( &orig, currPrintGrid.orig, currPrintGrid.angle ); + p[0] = p[1] = p[2] = p[3] = orig; + p[1].x = p[2].x = orig.x + currPrintGrid.size.x; + p[2].y = p[3].y = orig.y + currPrintGrid.size.y + + ( printGaudy ? printScale : 0.0 ); + Rotate( &p[0], orig, currPrintGrid.angle ); + Rotate( &p[1], orig, currPrintGrid.angle ); + Rotate( &p[2], orig, currPrintGrid.angle ); + Rotate( &p[3], orig, currPrintGrid.angle ); + minP = maxP = p[0]; + for (i=1; i<4; i++) { + if (maxP.x < p[i].x) maxP.x = p[i].x; + if (maxP.y < p[i].y) maxP.y = p[i].y; + if (minP.x > p[i].x) minP.x = p[i].x; + if (minP.y > p[i].y) minP.y = p[i].y; + } + maxP.x -= minP.x; + maxP.y -= minP.y; + print_d.d = page_d.d = wPrintPageStart(); + if (page_d.d == NULL) + return FALSE; + print_d.dpi = page_d.dpi = wDrawGetDPI( print_d.d ); + print_d.angle = currPrintGrid.angle; + print_d.orig = orig; + print_d.size = /*maxP*/ currPrintGrid.size; + page_d.orig = zero; + page_d.angle = 0.0; + if ( printGaudy ) { + Translate( &print_d.orig, orig, currPrintGrid.angle+180.0, printScale ); + print_d.size.y += printScale; + } + if (printRotate) { + rotateCW = (printFormat != PORTRAIT); + if (rotateCW) { + page_d.orig.x = realPageSize.y; + page_d.orig.y = 0.0; + page_d.angle = -90.0; + print_d.angle += -90.0; + Translate( &print_d.orig, print_d.orig, currPrintGrid.angle+90, maxPageSize.x ); + } else { + page_d.orig.x = 0.0; + page_d.orig.y = realPageSize.x; + page_d.angle = 90.0; + print_d.angle += 90.0; + Translate( &print_d.orig, print_d.orig, currPrintGrid.angle, + maxPageSize.y+(printGaudy?printScale:0) ); + } + page_d.size.x = print_d.size.y/printScale; + page_d.size.y = print_d.size.x/printScale; + print_d.size.x = currPrintGrid.size.y; + print_d.size.y = currPrintGrid.size.x; + } else { + page_d.size.x = print_d.size.x/printScale; + page_d.size.y = print_d.size.y/printScale; + } + wSetCursor( wCursorWait ); + print_d.scale = printScale; + if (print_d.d == NULL) + AbortProg( "wPrintPageStart" ); + clipOrig.x = clipOrig.y = 0; + clipSize.x = maxPageSize.x/printScale; + clipSize.y = maxPageSize.y/printScale; + GetRoomSize( &roomSize ); + if (printGaudy) { + PrintGaudyBox( roomSize ); + if ((!printRotate) || rotateCW) { + clipOrig.y = 1.0; + } + if (printRotate && rotateCW) { + print_d.size.x += printScale; + } + } else if (printRegistrationMarks) + PrintPlainBox( x, y, p ); + if (printRotate) { + wPrintClip( (wPos_t)(clipOrig.y*print_d.dpi), (wPos_t)(clipOrig.x*print_d.dpi), + (wPos_t)(clipSize.y*print_d.dpi), (wPos_t)(clipSize.x*print_d.dpi) ); + } else { + wPrintClip( (wPos_t)(clipOrig.x*print_d.dpi), (wPos_t)(clipOrig.y*print_d.dpi), + (wPos_t)(clipSize.x*print_d.dpi), (wPos_t)(clipSize.y*print_d.dpi) ); + } + p[0].x = p[3].x = 0.0; + p[1].x = p[2].x = roomSize.x; + p[0].y = p[1].y = 0.0; + p[2].y = p[3].y = roomSize.y; + fp = wStandardFont( F_TIMES, FALSE, FALSE ); + DrawRuler( &print_d, p[0], p[1], 0.0, TRUE, FALSE, wDrawColorBlack ); + DrawRuler( &print_d, p[0], p[3], 0.0, TRUE, TRUE, wDrawColorBlack ); + DrawRuler( &print_d, p[1], p[2], 0.0, FALSE, FALSE, wDrawColorBlack ); + DrawRuler( &print_d, p[3], p[2], 0.0, FALSE, TRUE, wDrawColorBlack ); + if ( printRuler && currPrintGrid.angle == 0 ) { + if ( !printRotate ) { + p[2] = p[3] = print_d.orig; + p[3].x += print_d.size.x; + p[3].y += print_d.size.y; + } else if ( rotateCW ) { + p[2].x = print_d.orig.x - print_d.size.y; + p[2].y = print_d.orig.y; + p[3].x = print_d.orig.x; + p[3].y = print_d.orig.y + print_d.size.x; + } else { + p[2].x = print_d.orig.x; + p[2].y = print_d.orig.y - print_d.size.x; + p[3].x = print_d.orig.x + print_d.size.y; + p[3].y = print_d.orig.y; + } + if ( p[2].x > 0 ) + minP.x = p[2].x + 0.4 * print_d.scale; + else + minP.x = 0.0; + if ( p[3].x < roomSize.x ) + maxP.x = p[3].x - 0.2 * print_d.scale; + else + maxP.x = roomSize.x; + if ( p[2].y > 0 ) + minP.y = p[2].y + 0.4 * print_d.scale; + else + minP.y = 0.0; + if ( p[3].y < roomSize.y ) + maxP.y = p[3].y - 0.2 * print_d.scale; + else + maxP.y = roomSize.y; + p[0].y = 0.0; + p[1].y = maxP.y - minP.y; + if ( p[2].x > 0 ) { + p[0].x = p[1].x = p[2].x + 0.4 * print_d.scale; + DrawRuler( &print_d, p[0], p[1], minP.y, TRUE, TRUE, wDrawColorBlack ); + } + if ( p[3].x < roomSize.x ) { + p[0].x = p[1].x = p[3].x - 0.2 * print_d.scale; + DrawRuler( &print_d, p[0], p[1], minP.y, FALSE, FALSE, wDrawColorBlack ); + } + p[0].x = 0; + p[1].x = maxP.x - minP.x; + if ( p[2].y > 0 ) { + p[0].y = p[1].y = p[2].y + 0.4 * print_d.scale; + DrawRuler( &print_d, p[0], p[1], minP.x, TRUE, FALSE, wDrawColorBlack ); + } + if ( p[3].y < roomSize.y ) { + p[0].y = p[1].y = p[3].y - 0.2 * print_d.scale; + DrawRuler( &print_d, p[0], p[1], minP.x, FALSE, TRUE, wDrawColorBlack ); + } + } + if (printGrid) + DrawSnapGrid( &print_d, mapD.size, FALSE ); + roadbedWidth = printRoadbed?printRoadbedWidth:0.0; + DrawTracks( &print_d, print_d.scale, minP, maxP ); + if (printRegistrationMarks && printScale == 1) + DrawRegistrationMarks( &print_d ); + if ( !wPrintPageEnd( print_d.d ) ) + return FALSE; + /*BITMAP(bm,x,y) = 0;*/ + MarkPage( x, y ); + } + return TRUE; +} + + +static void DoPrintPrint( void * junk ) +/* + * Called when print:print button is clicked. + * Print all the printable pages and mark them + * non-print. + */ +{ + wIndex_t x, y; + int copy, copies; + long noDecoration; + + if (pageCount == 0) { + NoticeMessage( MSG_PRINT_NO_PAGES, _("Ok"), NULL ); + return; + } + + wPrefGetInteger( "print", "nodecoration", &noDecoration, 0 ); + + print_d.CoOrd2Pix = page_d.CoOrd2Pix = mainD.CoOrd2Pix; + wSetCursor( wCursorWait ); + if (!wPrintDocStart( Title1, pageCount, &copies )) { + wSetCursor( wCursorNormal ); + return; + } + if (copies <= 0) + copies = 1; + for ( copy=1; copy<=copies; copy++) { + if ( printOrder == 0 ) { + for (x=bm.x0; x<bm.x1; x++) + for (y=bm.y1-1; y>=bm.y0; y--) + if (!PrintPage( x, y )) goto quitPrinting; + } else { + for (y=bm.y0; y<bm.y1; y++) + for (x=bm.x0; x<bm.x1; x++) + if (!PrintPage( x, y )) goto quitPrinting; + } + for (y=bm.y0; y<bm.y1; y++) + for (x=bm.x0; x<bm.x1; x++) + if (BITMAP(bm,x,y)) { + if (copy < copies) + MarkPage( x, y ); + else + BITMAP(bm,x,y) = 0; + } + } + +quitPrinting: + wPrintDocEnd(); + wSetCursor( wCursorNormal ); + Reset(); /* undraws grid, resets pagecount, etc */ +} + + +static void DoResetGrid( void ) +{ + DrawPrintGrid(); + currPrintGrid.orig = zero; + currPrintGrid.angle = 0.0; + ChangeDim(); + newPrintGrid = currPrintGrid; + ParamLoadControls( &printPG ); + DrawPrintGrid(); +} + + +static void PrintGridRotate( void * pangle ) +{ + ANGLE_T angle = (ANGLE_T)(long)pangle; + DrawPrintGrid(); + currPrintGrid.orig = cmdMenuPos; + currPrintGrid.angle += angle; + newPrintGrid = currPrintGrid; + ParamLoadControls( &printPG ); + ChangeDim(); + DrawPrintGrid(); +} + +/***************************************************************************** + * + * PAGE PRINT COMMAND + * + */ + +static void PrintChange( long changes ) +{ + if ( (changes&(CHANGE_MAP|CHANGE_UNITS|CHANGE_GRID))==0 || printWin==NULL || !wWinIsVisible(printWin) ) + return; + newPrintGrid = currPrintGrid; + if (!GridIsVisible()) + printGrid = 0; + ParamLoadControls( &printPG ); + ParamControlActive( &printPG, I_GRID, GridIsVisible() ); + PrintEnableControls(); +} + + +static void PrintDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + if ( inx < 0 ) return; + if ( pg->paramPtr[inx].context == (void*)1 ) + DoPrintScale(); + else if ( pg->paramPtr[inx].context == (void*)2 ) + PrintUpdate( inx ); + ParamControlActive( &printPG, I_RULER, currPrintGrid.angle == 0 ); +} + +static STATUS_T CmdPrint( + wAction_t action, + coOrd pos ) +/* + * Print command: + * + * 3 Sub-states: + * Select - grid coordinates are computed and the selected page is marked. + * Move - grid base (currPrintGrid.orig) is moved. + * Rotate - grid base and angle is rotated about selected point. + */ +{ + STATUS_T rc = C_CONTINUE; + static BOOL_T downShift; + + switch (action) { + + case C_START: + if (!wPrintInit()) + return C_TERMINATE; + printScale = iPrintScale; + if (printWin == NULL) { + rminScale_999.low = 1; + if (printScale < rminScale_999.low) + printScale = rminScale_999.low; + print_d.scale = printScale; + printWin = ParamCreateDialog( &printPG, MakeWindowTitle(_("Print")), _("Print"), DoPrintPrint, (paramActionCancelProc)Reset, TRUE, NULL, 0, PrintDlgUpdate ); + } + wShow( printWin ); + SetPageSize( TRUE ); + if (currPrintGrid.size.x == 0.0) { + currPrintGrid.size.x = maxPageSize.x; + currPrintGrid.size.y = maxPageSize.y; + } + if (currPrintGrid.size.x >= maxPageSize.x) + currPrintGrid.size.x = maxPageSize.x; + if (currPrintGrid.size.y >= maxPageSize.y) + currPrintGrid.size.y = maxPageSize.y; + newPrintGrid = currPrintGrid; + ParamLoadControls( &printPG ); + DrawPrintGrid(); + pageCount = 0; +LOG( log_print, 2, ( "Page size = %0.3f %0.3f\n", currPrintGrid.size.x, currPrintGrid.size.y ) ) + PrintChange( CHANGE_MAP|CHANGE_UNITS ); + ParamGroupRecord( &printPG ); + ParamLoadMessage( &printPG, I_PAGECNT, "0 pages" ); + ParamDialogOkActive( &printPG, FALSE ); + ChangeDim(); + InfoMessage( _("Select pages to print, or drag to move print grid") ); + downShift = FALSE; + ParamControlActive( &printPG, I_RULER, currPrintGrid.angle == 0 ); + return C_CONTINUE; + + case C_DOWN: + downShift = FALSE; + if (MyGetKeyState()&WKEY_SHIFT) { + newPrintGrid = currPrintGrid; + rc = GridAction( C_DOWN, pos, &newPrintGrid.orig, &newPrintGrid.angle ); + downShift = TRUE; + } + return C_CONTINUE; + + case C_MOVE: + if (downShift) { + rc = GridAction( action, pos, &newPrintGrid.orig, &newPrintGrid.angle ); + ParamLoadControls( &printPG ); + } + return C_CONTINUE; + + case C_UP: + if (downShift) { + rc = GridAction( action, pos, &newPrintGrid.orig, &newPrintGrid.angle ); + ParamLoadControls( &printPG ); + DrawPrintGrid(); + currPrintGrid = newPrintGrid; + ChangeDim(); + DrawPrintGrid(); + downShift = FALSE; + } + return C_CONTINUE; + + case C_LCLICK: + SelectPage( pos ); + return C_CONTINUE; + + case C_RDOWN: + downShift = FALSE; + if (MyGetKeyState()&WKEY_SHIFT) { + newPrintGrid = currPrintGrid; + rc = GridAction( action, pos, &newPrintGrid.orig, &newPrintGrid.angle ); + downShift = TRUE; + } + return rc; + + case C_RMOVE: + if (downShift) { + rc = GridAction( action, pos, &newPrintGrid.orig, &newPrintGrid.angle ); + ParamLoadControls( &printPG ); + } + return rc; + + case C_RUP: + if (downShift) { + rc = GridAction( action, pos, &newPrintGrid.orig, &newPrintGrid.angle ); + ParamLoadControls( &printPG ); + DrawPrintGrid(); + currPrintGrid = newPrintGrid; + ChangeDim(); + DrawPrintGrid(); + downShift = FALSE; + ParamControlActive( &printPG, I_RULER, currPrintGrid.angle == 0 ); + } + return rc; + + case C_REDRAW: + DrawPrintGrid(); + return C_TERMINATE; + + case C_CANCEL: + if (printWin == NULL) + return C_TERMINATE; + PrintClear(); + DrawPrintGrid(); + wHide( printWin ); + return C_TERMINATE; + + case C_OK: + DoPrintPrint( NULL ); + return C_TERMINATE; + + case C_CMDMENU: + wMenuPopupShow( printGridPopupM ); + return C_CONTINUE; + + default: + return C_CONTINUE; + } +} + +EXPORT wIndex_t InitCmdPrint( wMenu_p menu ) +{ + ParamRegister( &printPG ); + currPrintGrid = newPrintGrid; + log_print = LogFindIndex( "print" ); + RegisterChangeNotification( PrintChange ); + printGridPopupM = MenuRegister( "Print Grid Rotate" ); + AddRotateMenu( printGridPopupM, PrintGridRotate ); + return InitCommand( menu, CmdPrint, N_("Print..."), NULL, LEVEL0, IC_LCLICK|IC_POPUP2|IC_CMDMENU, ACCL_PRINT ); +} + +/***************************************************************************** + * + * TEST + * + */ +#ifdef TEST + +wDrawable_t printD, mainD; + +void wDrawHilight( void * d, coOrd orig, coOrd size ) +{ + lprintf( "wDrawHilight (%0.3f %0.3f) (%0.3f %0.3f)\n", orig.x, orig.y, size.x, size.y ); +} +void PrintPage( void * d, wIndex_t mode , wIndex_t x, wIndex_t y ) +{ + lprintf( "printPage %dx%d at (%0.3f %0.3f)\n", x, y, orig.x, orig.y ); +} +void PrintStart( wDrawable_t *d, wIndex_t mode ) +{ +} +void PrintEnd( wDrawable *d ) +{ +} +void wPrintGetPageSize( int style, int format, int scale ) +{ + printD.size.x = 11.5-(48.0/72.0); + printD.size.y = 8.0-(48.0/72.0); +} + +void DumpMap( char * f, ANGLE_T a, ANGLE_T b ) +{ + wIndex_t x, y; + lprintf( f, a, b ); + for (y=bm.y1-1; y>=bm.y1; y--) { + for (x=bm.x0; x<bm.x1; x++) + if (BITMAP(bm,x,y)) { + lprintf( "X"); + } else { + lprintf( " "); + } + lprintf( "\n"); + } +} + +#define C_PRINT (C_UP+1) +#define C_CANCEL (C_UP+2) +#define C_SCALE (C_UP+3) + +struct { + wAction_t cmd; + coOrd pos; +} cmds[] = { + { C_START, 0, 0 }, + { C_DOWN, 20.5, 12.4 }, + { C_MOVE, 20.5, 12.5 }, + { C_MOVE, 20.5, 12.3 }, + { C_MOVE, 39.3, 69.4 }, + { C_MOVE, 39.4, 4.5 }, + { C_MOVE, 2.4, 4.5 }, + { C_MOVE, 2.4, 50.3 }, + { C_UP, 0, 0 }, + { C_DOWN, 20.5, 12.4 }, + { C_UP, 0, 0 }, + { C_DOWN, 32.5, 4.4 }, + { C_UP, 0, 0 }, + { C_PRINT, 0, 0, }, + { C_START, 0, 0, }, + { C_DOWN, 45.3, 43.5 }, + { C_CANCEL, 0, 0 } + }; + +main( INT_T argc, char * argv[] ) +{ + INT_T i; + mapD.size.x = 4*12; + mapD.size.y = 3*12; + printD.scale = 1.0; + for (i=0; i<(sizeof cmds)/(sizeof cmds[0]); i++) { + switch (cmds[i].cmd) { + case C_START: + CmdPrint( cmds[i].cmd ); + DumpMap( "Start\n", 0, 0 ); + break; + case C_DOWN: + CmdPrint( cmds[i].cmd, cmds[i].pos ); + DumpMap( "Down (%0.3f %0.3f)\n", cmds[i].pos.x, cmds[i].pos.y ); + break; + case C_MOVE: + CmdPrint( cmds[i].cmd, cmds[i].pos ); + DumpMap( "Move (%0.3f %0.3f)\n", cmds[i].pos.x, cmds[i].pos.y ); + break; + case C_UP: + CmdPrint( cmds[i].cmd, cmds[i].pos ); + DumpMap( "Up\n", 0, 0 ); + break; + case C_PRINT: + DoPrintPrint( NULL ); + DumpMap( "Print\n", 0, 0 ); + break; + case C_CANCEL: + ClearPrint(); + DumpMap( "Cancel\n", 0, 0 ); + break; + case C_SCALE: + printD.scale = cmds[i].x; + break; + } + } +} +#endif diff --git a/app/bin/cprofile.c b/app/bin/cprofile.c new file mode 100644 index 0000000..245bb58 --- /dev/null +++ b/app/bin/cprofile.c @@ -0,0 +1,1357 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cprofile.c,v 1.4 2008-03-06 19:35:06 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 "track.h" +#include "cselect.h" +#include <math.h> +#include "shrtpath.h" +#include "i18n.h" + + +/* + + PROFILE COMMAND TEST CASE +(use 0testprof.xtc - 6 tracks connected 0:0:1 1:0:1 2:0:1 3:0:1 4:0:1 5:0:1 6:0:1) + + PreCond Action PostCond + +/ empty -> creating single pt +A1 - - 10 10 - +A2 - - 20 20 11 +A3 - - 11 11 20 + +/ single pt -> delete +B1 10 - 10 - - +B2 20 11 20 - - +B3 20 11 11 - - + +/ single pt at EOT - extend +C1 10 - 11 10 11 {1} +C2 10 - 20 10 11 {1} +C3 10 - 41 10 41 {1234} +C4 10 - 50 10 41 {1234} + +/ single pt at mid track - extend +D1 31 40 11 31 20 {32} +D2 31 40 20 31 20 {32} +D3 31 40 51 40 51 {45} +D4 31 40 61 40 61 {456} +D5 31 40 10 31 10 {321} + +/ length=2, delete end +E1 30 41 30 40 41 {4} +E2 30 41 21 40 41 {4} +E3 30 41 41 30 31 {3} +E4 30 41 50 30 31 {3} + +/ length=1, delete end +F1 30 31 30 31 - +F2 30 31 21 31 - +F3 30 31 31 30 - +F4 30 31 40 30 - + +/ length=1, extend +G1 30 31 11 20 31 {23} +G2 30 31 10 10 31 {123} +G3 30 31 51 30 51 {345} +G4 30 31 60 30 51 {345} +G5 30 31 61 30 61 {3456} + +/ length=2, extend +H1 30 41 11 20 41 {234} +H2 30 41 10 10 41 {1234} +H3 30 41 51 30 51 {345} +H4 30 41 60 30 51 {345} +H5 30 41 61 30 61 {3456} +*/ + +/***************************************************************************** + * + * PROFILE WINDOW + * + */ + +static wDrawColor profileColorDefinedProfile; +static wDrawColor profileColorUndefinedProfile; +static wDrawColor profileColorFill; +static wFontSize_t screenProfileFontSize = 12; +static wFontSize_t printProfileFontSize = 6; +static BOOL_T printVert = TRUE; +static wMenu_p profilePopupM; +static track_p profilePopupTrk; +static EPINX_T profilePopupEp; +static wMenuToggle_p profilePopupToggles[3]; + +static int log_profile = 0; + +#define LABELH (labelH*fontSize/screenProfileFontSize) +#define PBB(FS) (2.0*(labelH*(FS)/screenProfileFontSize+3.0/mainD.dpi)) +#define PBT (10.0/mainD.dpi) +#define PBR (30.0/mainD.dpi) +#define PBL (20.0/mainD.dpi) +static FLOAT_T labelH; + + +track_p pathStartTrk; +EPINX_T pathStartEp; +track_p pathEndTrk; +EPINX_T pathEndEp; + +#define PASSERT( F, X, R ) if ( ! (X) ) { ErrorMessage( MSG_PASSERT, F, __LINE__, #X ); return R; } +#define NOP + +typedef struct { + track_p trk; + EPINX_T ep; + DIST_T elev; + DIST_T dist; + BOOL_T defined; /* from prev PE to current */ + } profElem_t, *profElem_p; +static dynArr_t profElem_da; +#define profElem(N) DYNARR_N( profElem_t, profElem_da, N ) + +typedef struct { + DIST_T dist; + char * name; + } station_t, *station_p; +static dynArr_t station_da; +#define station(N) DYNARR_N( station_t, station_da, N ) + + +struct { + DIST_T totalD, minE; + int minC, maxC, incrC; + DIST_T scaleX, scaleY; + } prof; +static void DrawProfile( drawCmd_p D, wFontSize_t fontSize, BOOL_T printVert ) +{ + coOrd pl, pt, pb; + int inx; + DIST_T grade; + wFont_p fp; + static dynArr_t points_da; +#define points(N) DYNARR_N( coOrd, points_da, N ) + wDrawWidth lw; + station_p ps; + coOrd textsize; + + lw = (wDrawWidth)(D->dpi*2.0/mainD.dpi); + fp = wStandardFont( F_HELV, FALSE, FALSE ); + DYNARR_RESET( coOrd, points_da ); + + pb.x = pt.x = 0; + pb.y = prof.minE; pt.y = GetDim(prof.maxC); + DrawLine( D, pb, pt, 0, snapGridColor ); + pb.x = pt.x = prof.totalD; + DrawLine( D, pb, pt, 0, snapGridColor ); + pb.x = 0; + pt.x = prof.totalD; + for (inx=prof.minC; inx<=prof.maxC; inx+=prof.incrC) { + pt.y = pb.y = GetDim(inx); + DrawLine( D, pb, pt, 0, snapGridColor ); + pl.x = -(PBL-3.0/mainD.dpi)/prof.scaleX*D->scale; + pl.y = pb.y-LABELH/2/prof.scaleY*D->scale; + sprintf( message, "%d", inx ); + DrawString( D, pl, 0.0, message, fp, fontSize*D->scale, borderColor ); + } + if ( profElem_da.cnt <= 0 ) + return; + + for (inx=0; inx<profElem_da.cnt; inx++ ) { + pt.y = profElem(inx).elev; + pt.x = profElem(inx).dist; + DYNARR_APPEND( coOrd, points_da, 10 ); + points(points_da.cnt-1) = pt; + } + pb.y = pt.y = prof.minE; + if ( points_da.cnt > 1 ) { + DYNARR_APPEND( coOrd, points_da, 10 ); + pt.x = prof.totalD; + points(points_da.cnt-1) = pt; + DYNARR_APPEND( coOrd, points_da, 10 ); + pb.x = 0; + points(points_da.cnt-1) = pb; + DrawFillPoly( D, points_da.cnt, &points(0), profileColorFill ); + DrawLine( D, pb, pt, lw, borderColor ); + } + + pt.y = prof.minE-(2*LABELH+3.0/mainD.dpi)/prof.scaleY*D->scale; + for (inx=0; inx<station_da.cnt; inx++ ) { + ps = &station(inx); + DrawTextSize( &mainD, ps->name, fp, fontSize, FALSE, &textsize ); + pt.x = ps->dist - textsize.x/2.0/prof.scaleX*D->scale; + if (pt.x < -PBR) + pt.x = -(PBR-3/mainD.dpi)/prof.scaleX*D->scale; + else if (pt.x+textsize.x > prof.totalD) + pt.x = prof.totalD-(textsize.x-3/mainD.dpi)/prof.scaleX*D->scale; + DrawString( D, pt, 0.0, ps->name, fp, fontSize*D->scale, borderColor ); + } + + pb.x = 0.0; pb.y = prof.minE; + pt = points(0); + DrawLine( D, pb, pt, lw, borderColor ); + sprintf( message, "%0.1f", PutDim(profElem(0).elev) ); + if (printVert) { + pl.x = pt.x + LABELH/2.0/prof.scaleX*D->scale; + pl.y = pt.y + 2.0/mainD.dpi/prof.scaleY*D->scale; + DrawString( D, pl, 270.0, message, fp, fontSize*D->scale, borderColor ); + } else { + pl.x = pt.x+2.0/mainD.dpi/prof.scaleX*D->scale; + pl.y = pt.y; + if (profElem_da.cnt>1 && profElem(0).elev < profElem(1).elev ) + pl.y -= LABELH/prof.scaleY*D->scale; + DrawString( D, pl, 0.0, message, fp, fontSize*D->scale, borderColor ); + } + pl = pt; + + for (inx=1; inx<profElem_da.cnt; inx++ ) { + pt.y = profElem(inx).elev; + pb.x = pt.x = profElem(inx).dist; + pt = points(inx); + pb.x = pt.x; + DrawLine( D, pl, pt, lw, (profElem(inx).defined?profileColorDefinedProfile:profileColorUndefinedProfile) ); + DrawLine( D, pb, pt, lw, borderColor ); + if (profElem(inx).dist > 0.1) { + grade = fabs(profElem(inx).elev-profElem(inx-1).elev)/ + (profElem(inx).dist-profElem(inx-1).dist); + sprintf( message, "%0.1f%%", grade*100.0 ); + DrawTextSize( &mainD, message, fp, fontSize, FALSE, &textsize ); + pl.x = (points(inx).x+points(inx-1).x)/2.0; + pl.y = (points(inx).y+points(inx-1).y)/2.0; + if (printVert) { + pl.x += (LABELH/2)/prof.scaleX*D->scale; + pl.y += ((LABELH/2)*grade/prof.scaleX + 2.0/mainD.dpi/prof.scaleY)*D->scale; + DrawString( D, pl, 270.0, message, fp, fontSize*D->scale, borderColor ); + } else { + pl.x -= (textsize.x/2)/prof.scaleX*D->scale; + pl.y += (textsize.x/2)*grade/prof.scaleX*D->scale; + DrawString( D, pl, 0.0, message, fp, fontSize*D->scale, borderColor ); + } + } + if (units==UNITS_ENGLISH) { + if (prof.totalD > 240) + sprintf( message, "%d'", ((int)floor(profElem(inx).dist)+6)/12 ); + else + sprintf( message, "%d'%d\"", ((int)floor(profElem(inx).dist+0.5))/12, ((int)floor(profElem(inx).dist+0.5))%12 ); + } else { + if (PutDim(prof.totalD) > 10000) + sprintf( message, "%0.0fm", (PutDim(profElem(inx).dist)+50)/100.0 ); + else if (PutDim(prof.totalD) > 100) + sprintf( message, "%0.1fm", (PutDim(profElem(inx).dist)+5)/100.0 ); + else + sprintf( message, "%0.2fm", (PutDim(profElem(inx).dist)+0.5)/100.0 ); + } + DrawTextSize( &mainD, message, fp, fontSize, FALSE, &textsize ); + pl.x = pb.x-(textsize.x/2)/prof.scaleX*D->scale; + pl.y = prof.minE-(LABELH+3.0/mainD.dpi)/prof.scaleY*D->scale; + DrawString( D, pl, 0.0, message, fp, fontSize*D->scale, borderColor ); + sprintf( message, "%0.1f", PutDim(profElem(inx).elev) ); + if (printVert) { + pl.x = pt.x + LABELH/2.0/prof.scaleX*D->scale; + pl.y = pt.y + 2.0/mainD.dpi/prof.scaleY*D->scale; + DrawString( D, pl, 270.0, message, fp, fontSize*D->scale, borderColor ); + } else { + pl.x = pt.x + 2.0/mainD.dpi/prof.scaleX*D->scale; + pl.y = pt.y; + if ( inx != profElem_da.cnt-1 && profElem(inx).elev < profElem(inx+1).elev ) + pl.y -= LABELH/prof.scaleY*D->scale; + DrawString( D, pl, 0.0, message, fp, fontSize*D->scale, borderColor ); + } + pl = pt; + } +} + + + +static void ProfilePix2CoOrd( drawCmd_p, wPos_t, wPos_t, coOrd * ); +static void ProfileCoOrd2Pix( drawCmd_p, coOrd, wPos_t*, wPos_t* ); +static drawCmd_t screenProfileD = { + NULL, + &screenDrawFuncs, + DC_NOCLIP, + 1.0, + 0.0, + {0.0,0.0}, {0.0,0.0}, + ProfilePix2CoOrd, ProfileCoOrd2Pix }; + +static void ProfilePix2CoOrd( + drawCmd_p d, + wPos_t xx, + wPos_t yy, + coOrd * pos ) +{ + pos->x = (xx/d->dpi+d->orig.x)/prof.scaleX; + pos->y = (yy/d->dpi+d->orig.y)/prof.scaleY+prof.minE; +} + +static void ProfileCoOrd2Pix( + drawCmd_p d, + coOrd pos, + wPos_t *xx, + wPos_t *yy ) +{ + wPos_t x, y; + x = (wPos_t)((((pos.x*prof.scaleX)/d->scale-d->orig.x)*d->dpi+0.5)); + y = (wPos_t)(((((pos.y-prof.minE)*prof.scaleY)/d->scale-d->orig.y)*d->dpi+0.5)); + if ( d->angle == 0 ) { + *xx = x; + *yy = y; + } else if ( d->angle == -90.0 ) { + /* L->P */ + *xx = y; + *yy = -x; + } else { + /* P->L */ + *xx = -y; + *yy = x; + } +} + + +static void RedrawProfileW( void ) +{ + wPos_t ww, hh; + coOrd size; + int inx, divC; + DIST_T maxE, rngE; + profElem_t *p; + wFont_p fp; + POS_T w; + coOrd textsize; + + wDrawClear( screenProfileD.d ); + wDrawGetSize( screenProfileD.d, &ww, &hh ); + screenProfileD.size.x = (ww)/screenProfileD.dpi; + screenProfileD.size.y = (hh)/screenProfileD.dpi; + screenProfileD.orig.x = -PBL; + screenProfileD.orig.y = -PBB(screenProfileFontSize); + + /* Calculate usable dimension of canvas */ + size = screenProfileD.size; + size.x -= (PBL); + size.y -= (PBB(screenProfileFontSize)); +#ifdef WINDOWS + if (printVert) { + size.x -= PBR/4.0; + size.y -= PBT; + } else +#endif + { + size.x -= PBR; + size.y -= PBT; + } + if ( size.x < 0.1 || size.y < 0.1 ) + return; + + /* Calculate range of data values */ + if (profElem_da.cnt<=0) { + prof.totalD = 0.0; + prof.minE = 0.0; + maxE = 1.0; + } else { + maxE = prof.minE = profElem(0).elev; + prof.totalD = profElem(profElem_da.cnt-1).dist; + for (inx=1; inx<profElem_da.cnt; inx++ ) { + p = &profElem(inx); + if (p->elev<prof.minE) + prof.minE = p->elev; + if (p->elev>maxE) + maxE = p->elev; + } + } + + /* Calculate number of grid lines */ + prof.minC = (int)floor(PutDim(prof.minE)); + prof.maxC = (int)ceil(PutDim(maxE)); + if ( prof.maxC-prof.minC <= 0 ) + prof.maxC = prof.minC+1; + divC = (int)floor(size.y/labelH); + if ( divC < 1 ) + divC = 1; + prof.incrC = (prof.maxC-prof.minC+divC-1)/divC; + if ( prof.incrC < 1 ) + prof.incrC = 1; + prof.maxC = prof.minC + (prof.maxC-prof.minC+prof.incrC-1)/prof.incrC * prof.incrC; + + /* Reset bounds based on intergal values */ + prof.minE = GetDim(prof.minC); + rngE = GetDim(prof.maxC) - prof.minE; + if (rngE < 1.0) + rngE = 1.0; + + /* Compute vert scale */ + prof.scaleY = size.y/rngE; + sprintf( message, "%0.2f", maxE ); + fp = wStandardFont( F_HELV, FALSE, FALSE ); + DrawTextSize( &mainD, message, fp, screenProfileFontSize, FALSE, &textsize ); + w = textsize.x; + w -= PBT; + w += 4.0/screenProfileD.dpi; + w -= (GetDim(prof.maxC)-maxE)*prof.scaleY; + if (w > 0) { + size.y -= w; + prof.scaleY = size.y/rngE; + } + + /* Compute horz scale */ + if (prof.totalD <= 0.1) { + prof.totalD = size.x; + } + prof.scaleX = size.x/prof.totalD; + +#ifdef LATER + D->size.x /= prof.scaleX; + D->size.x -= D->orig.x; + D->size.y /= prof.scaleY; + D->size.y -= D->orig.y; + D->size.y += prof.minE; +#endif + + DrawProfile( &screenProfileD, screenProfileFontSize, +#ifdef WINDOWS + printVert +#else + FALSE +#endif + ); +} + + +static drawCmd_t printProfileD = { + NULL, + &printDrawFuncs, + DC_PRINT|DC_NOCLIP, + 1.0, + 0.0, + {0.0,0.0}, {1.0,1.0}, + ProfilePix2CoOrd, ProfileCoOrd2Pix }; +static void DoProfilePrint( void * junk ) +{ + coOrd size, p[4]; + int copies; + WDOUBLE_T w, h, screenRatio, printRatio, titleH; + wFont_p fp; + coOrd screenSize; + coOrd textsize; + + if (!wPrintDocStart( _("Profile"), 1, &copies )) + return; + printProfileD.d = wPrintPageStart(); + if (printProfileD.d == NULL) + return; + printProfileD.dpi = wDrawGetDPI( printProfileD.d ); + wPrintGetPageSize( &w, &h ); + printProfileD.orig.x = -PBL; + printProfileD.orig.y = -PBB(printProfileFontSize); + printProfileD.angle = 0.0; + screenRatio = screenProfileD.size.y/screenProfileD.size.x; + screenSize.x = prof.totalD*prof.scaleX; + screenSize.y = GetDim(prof.maxC-prof.minC)*prof.scaleY; + screenRatio = screenSize.y/screenSize.x; + printProfileD.size.x = w; + printProfileD.size.y = h; + sprintf( message, _("%s Profile: %s"), sProdName, Title1 ); + fp = wStandardFont( F_TIMES, FALSE, FALSE ); + DrawTextSize( &mainD, message, fp, 24, FALSE, &textsize ); + titleH = textsize.y + 6.0/mainD.dpi; + if (screenRatio < 1.0 && w < h ) { + /* Landscape -> Portrait */ + printProfileD.angle = -90.0; + printProfileD.orig.x += h; + size.x = h; + size.y = w; + } else if (screenRatio > 1.0 && w > h ) { + /* Portrait -> Landscape */ + printProfileD.angle = 90.0; + printProfileD.orig.y += w; + size.x = h; + size.y = w; + } else { + size.x = w; + size.y = h; + } + size.y -= titleH+(printVert?PBT*2:PBT)+PBB(printProfileFontSize); + size.x -= 4.0/mainD.dpi+PBL+(printVert?PBR/4.0:PBR); + printRatio = size.y/size.x; + if (printRatio < screenRatio) { + printProfileD.scale = screenSize.y/size.y; + size.x = screenSize.x/printProfileD.scale; + } else { + printProfileD.scale = screenSize.x/size.x; + printProfileD.orig.y -= size.y; + size.y = screenSize.y/printProfileD.scale; + printProfileD.orig.y += size.y; + } +#define PRINT_ABS2PAGEX(X) (((X)*printProfileD.scale)/prof.scaleX) +#define PRINT_ABS2PAGEY(Y) (((Y)*printProfileD.scale)/prof.scaleY+prof.minE) + p[0].y = PRINT_ABS2PAGEY(size.y+(printVert?PBT*2:PBT)+0.05); + p[0].x = PRINT_ABS2PAGEX((size.x-textsize.x)/2.0); + if ( p[0].x < 0 ) + p[0].x = 0; + DrawString( &printProfileD, p[0], 0, message, fp, 24*printProfileD.scale, borderColor ); + p[0].x = p[3].x = PRINT_ABS2PAGEX((-PBL)+2.0/mainD.dpi); + p[0].y = p[1].y = PRINT_ABS2PAGEY(-PBB(printProfileFontSize)); + p[1].x = p[2].x = PRINT_ABS2PAGEX(size.x+(printVert?PBR/4.0:PBR)); + p[2].y = p[3].y = PRINT_ABS2PAGEY(size.y+(printVert?PBT*2:PBT)); + DrawLine( &printProfileD, p[0], p[1], 0, drawColorBlack ); + DrawLine( &printProfileD, p[1], p[2], 0, drawColorBlack ); + DrawLine( &printProfileD, p[2], p[3], 0, drawColorBlack ); + DrawLine( &printProfileD, p[3], p[0], 0, drawColorBlack ); + + DrawProfile( &printProfileD, printProfileFontSize, printVert ); + wPrintPageEnd( printProfileD.d ); + wPrintDocEnd(); +} + + + +/************************************************************************** + * + * Window Handlers + * + **************************************************************************/ + +static wWin_p profileW; + + +static BOOL_T profileUndo = FALSE; +static void DoProfileDone( void * ); +static void DoProfileClear( void * ); +static void DoProfilePrint( void * ); +static void DoProfileChangeMode( void * ); +static void SelProfileW( wIndex_t, coOrd ); + +static paramDrawData_t profileDrawData = { 300, 150, (wDrawRedrawCallBack_p)RedrawProfileW, SelProfileW, &screenProfileD }; +static paramData_t profilePLs[] = { + { PD_DRAW, NULL, "canvas", PDO_DLGRESIZE, &profileDrawData }, +#define I_PROFILEMSG (1) + { PD_MESSAGE, NULL, NULL, PDO_DLGIGNOREX, (void*)300 }, + { PD_BUTTON, (void*)DoProfileClear, "clear", PDO_DLGCMDBUTTON, NULL, N_("Clear") }, + { PD_BUTTON, (void*)DoProfilePrint, "print", 0, NULL, N_("Print") } }; +static paramGroup_t profilePG = { "profile", 0, profilePLs, sizeof profilePLs/sizeof profilePLs[0] }; + + +static void ProfileTempDraw( int inx, DIST_T elev ) +{ + coOrd p0, p1; +#ifdef LATER + p0.x = profElem(inx).dist*prof.scaleX; + p0.y = (elev-prof.minE)*prof.scaleY; + screenProfileD.funcs = &tempDrawFuncs; + if (inx > 0) { + p1.x = profElem(inx-1).dist*prof.scaleX; + p1.y = (profElem(inx-1).elev-prof.minE)*prof.scaleY; + DrawLine( &screenProfileD, p0, p1, 2, borderColor ); + } + if (inx < profElem_da.cnt-1) { + p1.x = profElem(inx+1).dist*prof.scaleX; + p1.y = (profElem(inx+1).elev-prof.minE)*prof.scaleY; + DrawLine( &screenProfileD, p0, p1, 2, borderColor ); + } + screenProfileD.funcs = &screenDrawFuncs; +#endif + p0.x = profElem(inx).dist; + p0.y = elev; + screenProfileD.funcs = &tempDrawFuncs; + if (inx > 0) { + p1.x = profElem(inx-1).dist; + p1.y = profElem(inx-1).elev; + DrawLine( &screenProfileD, p0, p1, 2, borderColor ); + } + if (inx < profElem_da.cnt-1) { + p1.x = profElem(inx+1).dist; + p1.y = profElem(inx+1).elev; + DrawLine( &screenProfileD, p0, p1, 2, borderColor ); + } + screenProfileD.funcs = &screenDrawFuncs; +} + + +static void SelProfileW( + wIndex_t action, + coOrd pos ) +{ + DIST_T dist; + static DIST_T oldElev; + static int inx; + DIST_T elev; + + if (profElem_da.cnt <= 0) + return; + + dist = pos.x; + elev = pos.y; + +#ifdef LATER + if (recordF) + RecordMouse( "PROFILEMOUSE", action, dist, elev ); +#endif + + switch (action&0xFF) { + case C_DOWN: + for (inx=0; inx<profElem_da.cnt; inx++) { + if (dist <= profElem(inx).dist) { + if (inx!=0 && profElem(inx).dist-dist > dist-profElem(inx-1).dist) + inx--; + break; + } + } + if (inx >= profElem_da.cnt) + inx = profElem_da.cnt-1; + sprintf(message, _("Elev = %0.1f"), PutDim(elev) ); + ParamLoadMessage( &profilePG, I_PROFILEMSG, message ); + oldElev = elev; + ProfileTempDraw( inx, elev ); + break; + case C_MOVE: + if ( inx < 0 ) + break; + ProfileTempDraw( inx, oldElev ); + if (profElem_da.cnt == 1 ) { + sprintf(message, _("Elev = %0.1f"), PutDim(elev) ); + } else if (inx == 0) { + sprintf( message, _("Elev=%0.2f %0.1f%%"), + PutDim(elev), + fabs( profElem(inx+1).elev-elev ) / (profElem(inx+1).dist-profElem(inx).dist) * 100.0 ); + } else if (inx == profElem_da.cnt-1) { + sprintf( message, _("%0.1f%% Elev = %0.2f"), + fabs( profElem(inx-1).elev-elev ) / (profElem(inx).dist-profElem(inx-1).dist) * 100.0, + PutDim(elev) ); + } else { + sprintf( message, _("%0.1f%% Elev = %0.2f %0.1f%%"), + fabs( profElem(inx-1).elev-elev ) / (profElem(inx).dist-profElem(inx-1).dist) * 100.0, + PutDim(elev), + fabs( profElem(inx+1).elev-elev ) / (profElem(inx+1).dist-profElem(inx).dist) * 100.0 ); + } + ParamLoadMessage( &profilePG, I_PROFILEMSG, message ); + oldElev = elev; + ProfileTempDraw( inx, oldElev ); + break; + case C_UP: + if (profileUndo == FALSE) { + UndoStart( _("Profile Command"), "Profile - set elevation" ); + profileUndo = TRUE; + } + if (profElem(inx).trk) { + UpdateTrkEndElev( profElem(inx).trk, profElem(inx).ep, ELEV_DEF|ELEV_VISIBLE, oldElev, NULL ); + } + profElem(inx).elev = oldElev; + RedrawProfileW(); + ParamLoadMessage( &profilePG, I_PROFILEMSG, _("Drag to change Elevation") ); + inx = -1; + break; + default: + break; + } +} + + +#ifdef LATER +static BOOL_T ProfilePlayback( char * line ) +{ + int action; + wPos_t x, y; + coOrd pos; + + if ( !GetArgs( line, "dp", &action, &pos ) ) { + return FALSE; + } else { + x = (wPos_t)(((pos.x*prof.scaleX)-screenProfileD.orig.x)*screenProfileD.dpi+0.5); + y = (wPos_t)((((pos.y-prof.minE)*prof.scaleY)-screenProfileD.orig.y)*screenProfileD.dpi+0.5); + PlaybackMouse( selProfileW, &screenProfileD, (wAction_t)action, x, y, drawColorBlack ); + } + return TRUE; +} +#endif + + + +static void HilightProfileElevations( BOOL_T show ) +{ + /*if ( profElem_da.cnt <= 0 ) {*/ + HilightElevations( show ); + /*} else { + }*/ +} + + +static void DoProfileDone( void * junk ) +{ +#ifdef LATER + HilightProfileElevations( FALSE ); + wHide( profileW ); + ClrAllTrkBits( TB_PROFILEPATH ); + MainRedraw(); +#endif + Reset(); +} + + +static void DoProfileClear( void * junk ) +{ + profElem_da.cnt = 0; + station_da.cnt = 0; + if (ClrAllTrkBits( TB_PROFILEPATH )) + MainRedraw(); + pathStartTrk = pathEndTrk = NULL; + RedrawProfileW(); +} + + +static void DoProfileChangeMode( void * junk ) +{ + if (profElem_da.cnt<=0) { + InfoMessage( _("Select a Defined Elevation to start Profile") ); + } else { + InfoMessage( _("Select a Defined Elevation to extend Profile") ); + } +} + +/************************************************************************** + * + * Find Shortest Path + * + **************************************************************************/ + +static BOOL_T PathListEmpty( void ) +{ + return pathStartTrk == NULL; +} + +static BOOL_T PathListSingle( void ) +{ + return pathStartTrk != NULL && + ( pathEndTrk == NULL || + ( GetTrkEndTrk(pathEndTrk,pathEndEp) == pathStartTrk && + GetTrkEndTrk(pathStartTrk,pathStartEp) == pathEndTrk ) ); +} + + +static int profileShortestPathMatch; +static DIST_T profileShortestPathDist; + +static int ProfileShortestPathFunc( + SPTF_CMD cmd, + track_p trk, + EPINX_T ep, + EPINX_T ep0, + DIST_T dist, + void * data ) +{ + track_p trkN; + EPINX_T epN; + int rc0=0; + int pathMatch; + + switch (cmd) { + case SPTC_TERMINATE: + rc0 = 1; + break; + + case SPTC_MATCH: + if ( EndPtIsIgnoredElev(trk,ep) ) + break; + if ( PathListSingle() ) { + if ( trk == pathStartTrk && ep == pathStartEp ) { + pathMatch = 2; + } else if ( trk == pathEndTrk && ep == pathEndEp ) { + pathMatch = 3; + } else { + break; + } + } else if ( ( trkN = GetTrkEndTrk(trk,ep) ) == NULL ) { + break; + } else { + epN = GetEndPtConnectedToMe( trkN, trk ); + if ( trkN == pathStartTrk && epN == pathStartEp ) { + pathMatch = 1; + } else if ( trkN == pathEndTrk && epN == pathEndEp ) { + pathMatch = 2; + } else if ( trkN == pathStartTrk && trkN == pathEndTrk ) { + pathMatch = 2; + } else if ( trkN == pathStartTrk ) { + pathMatch = 1; + } else if ( trkN == pathEndTrk ) { + pathMatch = 2; + } else { + break; + } + } + if ( profileShortestPathMatch < 0 || profileShortestPathDist > dist ) { +LOG( log_shortPath, 4, ( " Match=%d", pathMatch ) ) + profileShortestPathMatch = pathMatch; + profileShortestPathDist = dist; + } + rc0 = 1; + break; + + case SPTC_MATCHANY: + rc0 = -1; + break; + + case SPTC_IGNNXTTRK: + if ( EndPtIsIgnoredElev(trk,ep) ) + rc0 = 1; + else if ( (GetTrkBits(trk)&TB_PROFILEPATH)!=0 ) + rc0 = 1; + else if ( (!EndPtIsDefinedElev(trk,ep)) && GetTrkEndTrk(trk,ep)==NULL ) + rc0 = 1; + else + rc0 = 0; + break; + + case SPTC_ADD_TRK: +if (log_shortPath<=0||logTable(log_shortPath).level<4) LOG( log_profile, 4, ( " ADD_TRK T%d:%d", GetTrkIndex(trk), ep ) ) + SetTrkBits( trk, TB_PROFILEPATH ); + DrawTrack( trk, &mainD, profilePathColor ); + rc0 = 0; + break; + + case SPTC_VALID: + rc0 = 1; + break; + + default: + break; + } + return rc0; +} + +static int FindProfileShortestPath( + track_p trkN, + EPINX_T epN ) +{ +LOG( log_profile, 4, ( "Searching from T%d:%d to T%d:%d or T%d:%d\n", + GetTrkIndex(trkN), epN, + pathStartTrk?GetTrkIndex(pathStartTrk):-1, pathStartTrk?pathStartEp:-1, + pathEndTrk?GetTrkIndex(pathEndTrk):-1, pathEndTrk?pathEndEp:-1 ) ) + profileShortestPathMatch = -1; + return FindShortestPath( trkN, epN, TRUE, ProfileShortestPathFunc, NULL ); +} + + +/************************************************************************** + * + * Main Window Handler + * + **************************************************************************/ + + +#define ONPATH_NOT (1<<0) +#define ONPATH_END (1<<1) +#define ONPATH_MID (1<<2) +#define ONPATH_BRANCH (1<<3) +static int OnPath( track_p trk, EPINX_T ep ) +{ + track_p trk0; + if ( GetTrkBits(trk)&TB_PROFILEPATH ) { + trk0 = GetTrkEndTrk( profilePopupTrk, profilePopupEp ); + if ( trk0 && (GetTrkBits(trk0)&TB_PROFILEPATH) ) { + return ONPATH_MID; + } + if ( ( trk == pathStartTrk && ep == pathStartEp ) || + ( trk == pathStartTrk && ep == pathStartEp ) ) { + return ONPATH_END; + } + return ONPATH_BRANCH; + } + return ONPATH_NOT; +} + + +static BOOL_T PathListCheck( void ) +{ + track_p trk; + if (PathListEmpty() || PathListSingle()) + return TRUE; + if (!(GetTrkBits(pathStartTrk)&TB_PROFILEPATH)) { + ErrorMessage( MSG_PST_NOT_ON_PATH ); + return FALSE; + } + if (!(GetTrkBits(pathEndTrk)&TB_PROFILEPATH)) { + ErrorMessage( MSG_PET_NOT_ON_PATH ); + return FALSE; + } + trk = GetTrkEndTrk(pathStartTrk,pathStartEp); + if (trk && (GetTrkBits(trk)&TB_PROFILEPATH)) { + ErrorMessage( MSG_INV_PST_ON_PATH ); + return FALSE; + } + trk = GetTrkEndTrk(pathEndTrk,pathEndEp); + if (trk && (GetTrkBits(trk)&TB_PROFILEPATH)) { + ErrorMessage( MSG_INV_PET_ON_PATH ); + return FALSE; + } + return TRUE; +} + + +static void RemoveTracksFromPath( + track_p *Rtrk, + EPINX_T *Rep, + track_p trkEnd, + EPINX_T epEnd ) +{ + EPINX_T ep2; + track_p trk = *Rtrk, trkN; + EPINX_T ep = *Rep; + + PASSERT( "removeTracksFromPath", trk, NOP ); + PASSERT( "removeTracksFromPath", !PathListSingle(), NOP ); + while (1) { + DrawTrack( trk, &mainD, drawColorWhite ); + ClrTrkBits( trk, TB_PROFILEPATH ); + DrawTrack( trk, &mainD, drawColorBlack ); + + if (trk == trkEnd) { + pathStartTrk = trkEnd; + pathStartEp = epEnd; + pathEndTrk = GetTrkEndTrk(pathStartTrk,pathStartEp); + if (pathEndTrk) + pathEndEp = GetEndPtConnectedToMe(pathEndTrk,pathStartTrk); + return; + } + + ep2 = GetNextTrkOnPath( trk, ep ); + PASSERT( "removeTracksFromPath", ep2 >= 0,NOP ); + trkN = GetTrkEndTrk(trk,ep2); + PASSERT( "removeTracksFromPath", trkN != NULL, NOP ); + ep = GetEndPtConnectedToMe(trkN,trk); + trk = trkN; + if (EndPtIsDefinedElev(trk,ep)) { + *Rtrk = trk; + *Rep = ep; + return; + } + } +} + + +static void ChkElev( track_p trk, EPINX_T ep, EPINX_T ep2, DIST_T dist, BOOL_T * defined ) +{ + profElem_p p; + station_p s; + EPINX_T epDefElev = -1, ep1; + int mode; + BOOL_T undefined; + + mode = GetTrkEndElevMode( trk, ep ); + if (mode == ELEV_DEF) { + epDefElev = ep; + } else if (mode == ELEV_STATION) { + DYNARR_APPEND( station_t, station_da, 10 ); + s = &station(station_da.cnt-1); + s->dist = dist; + s->name = GetTrkEndElevStation(trk,ep); + } + undefined = FALSE; + if (epDefElev<0) { + if ( (trk == pathStartTrk && ep == pathStartEp) || + (trk == pathEndTrk && ep == pathEndEp) ) { + epDefElev = ep; + } + } + if (epDefElev<0) { + if (ep == ep2 || + GetTrkEndElevMode(trk,ep2) != ELEV_DEF ) + for ( ep1=0; ep1<GetTrkEndPtCnt(trk); ep1++ ) { + if ( ep1==ep || ep1==ep2 ) + continue; + if (EndPtIsDefinedElev(trk,ep1)) { + epDefElev = ep1; + dist -= GetTrkLength( trk, ep, ep1 ); + break; + } + if (GetTrkEndTrk(trk,ep1)) { + if (!EndPtIsIgnoredElev(trk,ep1)) + undefined = TRUE; + } + } + } + + if (epDefElev>=0) { + DYNARR_APPEND( profElem_t, profElem_da, 10 ); + p = &profElem(profElem_da.cnt-1); + p->trk = trk; + p->ep = epDefElev; + p->dist = dist; + if (GetTrkEndElevMode(trk,epDefElev) == ELEV_DEF) + p->elev = GetTrkEndElevHeight(trk,epDefElev); + else + ComputeElev( trk, epDefElev, TRUE, &p->elev, NULL ); + p->defined = *defined; + *defined = TRUE; + } else if (undefined) { + *defined = FALSE; + } +} + + +static void ComputeProfElem( void ) +{ + track_p trk = pathStartTrk, trkN; + EPINX_T ep = pathStartEp, ep2; + BOOL_T go; + DIST_T dist; + BOOL_T defined; + + profElem_da.cnt = 0; + station_da.cnt = 0; + dist = 0; + defined = TRUE; + if (PathListEmpty()) + return; + ChkElev( trk, ep, ep, dist, &defined ); + if (PathListSingle()) + return; + go = TRUE; + while ( go ) { + if (trk == pathEndTrk) { + go = FALSE; + ep2 = pathEndEp; + } else { + ep2 = GetNextTrkOnPath( trk, ep ); + PASSERT( "computeProfElem", ep2 >= 0, NOP ); + } + dist += GetTrkLength( trk, ep, ep2 ); + ChkElev( trk, ep2, ep, dist, &defined ); + if (!go) + break; + trkN = GetTrkEndTrk(trk,ep2); + ep = GetEndPtConnectedToMe(trkN,trk); + trk = trkN; + } +} + + +static void DumpProfElems( void ) +{ + track_p trk, trkN; + EPINX_T ep, ep2; + BOOL_T go; + + trk = pathStartTrk; + ep = pathStartEp; + + if (pathStartTrk==NULL) lprintf( "s--:- e--:-" ); + else if (pathEndTrk == NULL) lprintf( "sT%d:%d e--:-", GetTrkIndex(pathStartTrk), pathStartEp ); + else lprintf( "sT%d:%d eT%d:%d", GetTrkIndex(pathStartTrk), pathStartEp, GetTrkIndex(pathEndTrk), pathEndEp ); + lprintf( " { " ); + go = TRUE; + if (!PathListSingle()) + while ( trk ) { + if (trk==pathEndTrk) { + ep2 = pathEndEp; + go = FALSE; + } else { + ep2 = GetNextTrkOnPath( trk, ep ); + PASSERT( "computeProfElem", ep2 >= 0, NOP ); + } + lprintf( "T%d:%d:%d ", GetTrkIndex(trk), ep, ep2 ); + if (!go) + break; + trkN = GetTrkEndTrk(trk,ep2); + ep = GetEndPtConnectedToMe(trkN,trk); + trk = trkN; + } + lprintf( "}" ); +} + + +static void ProfileSelect( track_p trkN, EPINX_T epN ) +{ + track_p trkP; + EPINX_T epP=-1; + int rc; + +if (log_profile>=1) { + DumpProfElems(); + lprintf( " @ T%d:%d ", GetTrkIndex(trkN), epN ); + if (log_profile>=2) lprintf("\n"); + } + +#ifdef LATER + if (!EndPtIsDefinedElev(trkN, epN)) { + ErrorMessage( MSG_EP_NOT_DEP ); + return; + } +#endif + + trkP = GetTrkEndTrk( trkN, epN ); + if (trkP) + epP = GetEndPtConnectedToMe( trkP, trkN ); + + if (!PathListCheck()) + return; + + HilightProfileElevations( FALSE ); + + if ( PathListEmpty() ) { + pathStartTrk = trkN; + pathStartEp = epN; + pathEndTrk = trkP; + pathEndEp = epP; +LOG( log_profile, 2, ("Adding first element\n") ) + + } else if ( PathListSingle() && + ( ( trkN == pathStartTrk && epN == pathStartEp ) || + ( trkP && trkP == pathStartTrk && epP == pathStartEp ) ) ) { + pathStartTrk = pathEndTrk = NULL; +LOG( log_profile, 2, ("Clearing list\n") ) + + } else if ( (trkN == pathStartTrk && epN == pathStartEp ) || + (trkP && trkP == pathStartTrk && epP == pathStartEp) ) { + RemoveTracksFromPath( &pathStartTrk, &pathStartEp, pathEndTrk, pathEndEp ); +LOG( log_profile, 2, ("Removing first element\n") ) + + } else if ( (trkN == pathEndTrk && epN == pathEndEp) || + (trkP && trkP == pathEndTrk && epP == pathEndEp) ) { + RemoveTracksFromPath( &pathEndTrk, &pathEndEp, pathStartTrk, pathStartEp ); +LOG( log_profile, 2, ("Removing last element\n") ) + + } else if ( (GetTrkBits(trkN)&TB_PROFILEPATH) || (trkP && (GetTrkBits(trkP)&TB_PROFILEPATH)) ) { + ErrorMessage( MSG_EP_ON_PATH ); + HilightProfileElevations( TRUE ); + return; + + } else if ( ( rc = FindProfileShortestPath( trkN, epN ) ) > 0 ) { + if (!(GetTrkBits(trkN)&TB_PROFILEPATH)) { + PASSERT( "profileSelect", trkP != NULL, NOP ); + trkN = trkP; + epN = epP; +LOG( log_profile, 2, ("Invert selected EP\n") ) + } + + switch (profileShortestPathMatch) { + case 1: + /* extend Start */ + pathStartTrk = trkN; + pathStartEp = epN; +LOG( log_profile, 2, ( "Prepending Path\n" ) ) + break; + case 2: + /* extend End */ + pathEndTrk = trkN; + pathEndEp = epN; +LOG( log_profile, 2, ( "Appending Path\n" ) ) + break; + case 3: + /* need to flip */ + pathStartTrk = pathEndTrk; + pathStartEp = pathEndEp; + pathEndTrk = trkN; + pathEndEp = epN; +LOG( log_profile, 2, ( "Flip/Appending Path\n" ) ) + break; + default: + AbortProg( "findPaths:1" ); + } + + } else { + ErrorMessage( MSG_NO_PATH_TO_EP ); + HilightProfileElevations( TRUE ); + return; + } + + HilightProfileElevations( TRUE ); + ComputeProfElem(); + RedrawProfileW(); + DoProfileChangeMode( NULL ); +if (log_profile>=1) { + lprintf( " = " ); + DumpProfElems(); + lprintf( "\n" ); + } + PathListCheck(); +} + + + +static void ProfileSubCommand( wBool_t set, void* pcmd ) +{ + long cmd = (long)pcmd; + int mode; + coOrd pos = oldMarker; + DIST_T elev; + DIST_T radius; + + if ((profilePopupTrk = OnTrack( &pos, TRUE, TRUE )) == NULL || + (profilePopupEp = PickEndPoint( pos, profilePopupTrk )) < 0) + return; + if (profileUndo==0) { + profileUndo = TRUE; + UndoStart(_("Profile Command"), "Profile"); + } + radius = 0.05*mainD.scale; + if ( radius < trackGauge/2.0 ) + radius = trackGauge/2.0; + pos = GetTrkEndPos( profilePopupTrk, profilePopupEp ); + mode = GetTrkEndElevMode( profilePopupTrk, profilePopupEp ); + if ( (mode&ELEV_MASK)==ELEV_DEF || (mode&ELEV_MASK)==ELEV_IGNORE ) + DrawFillCircle( &tempD, pos, radius, + ((mode&ELEV_MASK)==ELEV_DEF?elevColorDefined:elevColorIgnore)); + if ( (mode&ELEV_MASK)==ELEV_DEF ) + + DrawEndPt2( &mainD, profilePopupTrk, profilePopupEp, drawColorWhite ); + elev = 0.0; + switch (cmd) { + case 0: + /* define */ + ComputeElev( profilePopupTrk, profilePopupEp, TRUE, &elev, NULL ); + mode = ELEV_DEF|ELEV_VISIBLE; + break; + case 1: + /* ignore */ + mode = ELEV_IGNORE|ELEV_VISIBLE; + break; + case 2: + default: + /* none */ + mode = ELEV_NONE; + break; + } + UpdateTrkEndElev( profilePopupTrk, profilePopupEp, mode, elev, NULL ); + if ( (mode&ELEV_MASK)==ELEV_DEF || (mode&ELEV_MASK)==ELEV_IGNORE ) + DrawFillCircle( &tempD, pos, radius, + ((mode&ELEV_MASK)==ELEV_DEF?elevColorDefined:elevColorIgnore)); + ComputeProfElem(); + RedrawProfileW(); +} + + +static STATUS_T CmdProfile( wAction_t action, coOrd pos ) +{ + track_p trk0; + EPINX_T ep0; + coOrd textsize; + + switch (action) { + case C_START: + if ( profileW == NULL ) { + profileColorDefinedProfile = drawColorBlue; + profileColorUndefinedProfile = drawColorRed; + profileColorFill = drawColorAqua; + DrawTextSize( &mainD, "999", wStandardFont( F_HELV, FALSE, FALSE ), screenProfileFontSize, FALSE, &textsize ); + labelH = textsize.y; + profileW = ParamCreateDialog( &profilePG, MakeWindowTitle(_("Profile")), _("Done"), DoProfileDone, (paramActionCancelProc)Reset, TRUE, NULL, F_RESIZE, NULL ); + } + ParamLoadControls( &profilePG ); + ParamGroupRecord( &profilePG ); + wShow( profileW ); + ParamLoadMessage( &profilePG, I_PROFILEMSG, _("Drag to change Elevation") ); + HilightProfileElevations( TRUE ); + profElem_da.cnt = 0; + station_da.cnt = 0; + RedrawProfileW(); + if ( ClrAllTrkBits( TB_PROFILEPATH ) ) + MainRedraw(); + pathStartTrk = NULL; + SetAllTrackSelect( FALSE ); + profileUndo = FALSE; + InfoMessage( _("Select a Defined Elevation to start profile") ); + return C_CONTINUE; + case C_LCLICK: + InfoMessage( "" ); + if ((trk0 = OnTrack( &pos, TRUE, TRUE )) != NULL) { + ep0 = PickEndPoint( pos, trk0 ); + if ( ep0 >= 0 ) { + ProfileSelect( trk0, ep0 ); + } + } + return C_CONTINUE; + case C_CMDMENU: + if ((profilePopupTrk = OnTrack( &pos, TRUE, TRUE )) != NULL ) { + profilePopupEp = PickEndPoint( pos, profilePopupTrk ); + if (profilePopupEp >= 0) { + int mode; + mode = GetTrkEndElevMode( profilePopupTrk, profilePopupEp ); + if (mode != ELEV_DEF && mode != ELEV_IGNORE && mode != ELEV_NONE ) { + ErrorMessage( MSG_CHANGE_ELEV_MODE ); + } else { + wMenuToggleEnable( profilePopupToggles[1], TRUE ); + if ( OnPath( profilePopupTrk, profilePopupEp ) & (ONPATH_END|ONPATH_MID) ) + wMenuToggleEnable( profilePopupToggles[1], FALSE ); + wMenuToggleSet( profilePopupToggles[0], mode == ELEV_DEF ); + wMenuToggleSet( profilePopupToggles[1], mode == ELEV_IGNORE ); + wMenuToggleSet( profilePopupToggles[2], mode == ELEV_NONE ); + wMenuPopupShow( profilePopupM ); + } + } + } +#ifdef LATER + InfoMessage( "" ); + if ((trk0 = OnTrack( &pos, TRUE, TRUE )) == NULL) + return C_CONTINUE; + ep0 = PickEndPoint( pos, trk0 ); + if (ep0 < 0) + return C_CONTINUE; + if (profileMode == 0) { + ; + } else { + ProfileIgnore( trk0, ep0 ); + } + DoProfileChangeMode( NULL ); +#endif + return C_CONTINUE; + case C_OK: + DoProfileDone(NULL); + return C_TERMINATE; + case C_CANCEL: + wHide(profileW); + HilightProfileElevations( FALSE ); + if (ClrAllTrkBits(TB_PROFILEPATH)) + MainRedraw(); + return C_TERMINATE; + case C_REDRAW: + if ( wWinIsVisible(profileW) ) { + HilightProfileElevations( wWinIsVisible(profileW) ); + /*RedrawProfileW();*/ + } + return C_CONTINUE; + } + return C_CONTINUE; +} + + +static void ProfileChange( long changes ) +{ + if ( (changes & CHANGE_UNITS) && screenProfileD.d ) + RedrawProfileW(); +} + + +#include "bitmaps/profile.xpm" + +EXPORT void InitCmdProfile( wMenu_p menu ) +{ + log_profile = LogFindIndex( "profile" ); + ParamRegister( &profilePG ); +#ifdef LATER + AddPlaybackProc( "PROFILEMOUSE", (playbackProc_p)profilePlayback, NULL ); +#endif + AddMenuButton( menu, CmdProfile, "cmdProfile", _("Profile"), wIconCreatePixMap(profile_xpm), LEVEL0_50, IC_LCLICK|IC_CMDMENU|IC_POPUP2, ACCL_PROFILE, NULL ); + profilePopupM = MenuRegister( "Profile Mode" ); + profilePopupToggles[0] = wMenuToggleCreate( profilePopupM, "", _("Define"), 0, FALSE, ProfileSubCommand, (void*)0 ); + profilePopupToggles[1] = wMenuToggleCreate( profilePopupM, "", _("Ignore"), 0, FALSE, ProfileSubCommand, (void*)1 ); + profilePopupToggles[2] = wMenuToggleCreate( profilePopupM, "", _("None"), 0, FALSE, ProfileSubCommand, (void*)2 ); + RegisterChangeNotification( ProfileChange ); +} diff --git a/app/bin/cpull.c b/app/bin/cpull.c new file mode 100644 index 0000000..a10f426 --- /dev/null +++ b/app/bin/cpull.c @@ -0,0 +1,662 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cpull.c,v 1.4 2008-03-06 19:35:06 m_fischer Exp $ + * + * Pull and Tighten commands + * + */ + +/* 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 <math.h> +#include "track.h" +#include "cselect.h" +#include "compound.h" +#include "i18n.h" + +/* + * pull track endpoint together + */ + +int debugPull = 0; + +ANGLE_T maxA = 15.0; +DIST_T maxD = 3.0; +ANGLE_T littleA = 1.0; +DIST_T littleD = 0.1; + +static double factorX=10, factorY=100, factorA=0.2; +typedef struct { + double X, Y, A, T; + } cost_t; +static cost_t sumCosts; +static cost_t maxCosts; +static int maxCostsInx; +typedef struct { + coOrd p[2]; + ANGLE_T a[2]; + ANGLE_T angle; + DIST_T dist; + track_p trk; + EPINX_T ep[2]; + cost_t costs[2]; + double contrib; + coOrd pp; + } section_t, *section_p; +static dynArr_t section_da; +#define section(N) DYNARR_N( section_t, section_da, N ) +static double contribL, contribR; + + +typedef enum { freeEnd, connectedEnd, loopEnd } ending_e; + +/* + * Utilities + */ +static ending_e GetConnectedTracks( + track_p trk, + EPINX_T ep, + track_p endTrk, + EPINX_T endEp ) +{ + track_p trk1; + EPINX_T ep1, ep2; + section_p sp; + while (1) { + if (trk == endTrk) { + ep2 = endEp; + trk1 = NULL; + } else { + ep2 = GetNextTrk( trk, ep, &trk1, &ep1, 0 ); + if (trk1 == NULL) + return freeEnd; + } + if ( ep2 >= 0 ) { + int inx; + for (inx=0;inx<section_da.cnt;inx++) { + if ( section(inx).trk == trk ) { + AbortProg("GetConnectedTracks(T%d already selected)", GetTrkIndex(trk)); + } + } + } + DYNARR_APPEND( section_t, section_da, 10 ); + sp = §ion(section_da.cnt-1); + sp->trk = trk; + sp->ep[0] = ep; + sp->ep[1] = ep2; + sp->p[0] = GetTrkEndPos(trk,ep); + sp->costs[0].X = sp->costs[0].Y = sp->costs[0].A = sp->costs[0].T = + sp->costs[1].X = sp->costs[1].Y = sp->costs[1].A = sp->costs[1].T =0.0; + sp->a[0] = GetTrkEndAngle(trk,ep); + sp->a[1] = 0; + if (ep2 < 0) + return connectedEnd; + sp->p[1] = GetTrkEndPos(trk,ep2); + sp->dist = FindDistance( GetTrkEndPos(trk,ep), GetTrkEndPos(trk,ep2) ); + sp->angle = NormalizeAngle( GetTrkEndAngle(trk,ep2)-GetTrkEndAngle(trk,ep) ); + sp->a[1] = GetTrkEndAngle(trk,ep2); + if (trk == endTrk) + return loopEnd; + trk = trk1; + ep = ep1; + } +} + +/* + * Simple move to connect + */ +static void MoveConnectedTracks( + track_p trk1, + EPINX_T ep1, + coOrd pos, + ANGLE_T angle ) +{ + EPINX_T ep, ep2; + track_p trk; + coOrd p; + ANGLE_T a; + + while (1) { + p = GetTrkEndPos( trk1, ep1 ); + p.x = pos.x - p.x; + p.y = pos.y - p.y; + a = angle - GetTrkEndAngle( trk1, ep1 ); + UndoModify( trk1 ); + UndrawNewTrack( trk1 ); + MoveTrack( trk1, p ); + RotateTrack( trk1, pos, a ); + DrawNewTrack( trk1 ); + ep2 = GetNextTrk( trk1, ep1, &trk, &ep, 0 ); + if (trk==NULL) + return; + if (ep2 < 0) + AbortProg("MoveConnectedTracks(T%d rooted)", GetTrkIndex(trk1)); + angle = NormalizeAngle(GetTrkEndAngle( trk1, ep2 )+180.0); + pos = GetTrkEndPos( trk1, ep2 ); + trk1 = trk; + ep1 = ep; + } +} + + +/* + * Helpers for complex case + */ +static void ReverseSectionList( + int start, int end ) +{ + int up, down; + section_t tmpUp, tmpDown; + EPINX_T tmpEp; + coOrd tmpPos; + ANGLE_T tmpA; + for (down=start,up=end-1; down<=up; down++,up-- ) { + tmpUp = section(up); + tmpEp=tmpUp.ep[0]; tmpUp.ep[0]=tmpUp.ep[1]; tmpUp.ep[1]=tmpEp; + tmpPos=tmpUp.p[0]; tmpUp.p[0]=tmpUp.p[1]; tmpUp.p[1]=tmpPos; + tmpA=tmpUp.a[0]; tmpUp.a[0]=tmpUp.a[1]; tmpUp.a[1]=tmpA; + tmpUp.angle = NormalizeAngle( 360.0-tmpUp.angle ); + tmpDown = section(down); + tmpEp=tmpDown.ep[0]; tmpDown.ep[0]=tmpDown.ep[1]; tmpDown.ep[1]=tmpEp; + tmpPos=tmpDown.p[0]; tmpDown.p[0]=tmpDown.p[1]; tmpDown.p[1]=tmpPos; + tmpA=tmpDown.a[0]; tmpDown.a[0]=tmpDown.a[1]; tmpDown.a[1]=tmpA; + tmpDown.angle = NormalizeAngle( 360.0-tmpDown.angle ); + section(up) = tmpDown; + section(down) = tmpUp; + } +} + + +/* + * Evaluators + */ + + +#define ANGLE_FAULT (1<<0) +#define DIST_FAULT (1<<1) + +static int CheckConnections( void ) +{ + section_p sp; + int rc; + int inx; + DIST_T dist; + ANGLE_T angle; + rc = 0; + for (inx = 1; inx<section_da.cnt; inx++) { + sp = §ion(inx); + dist = FindDistance( sp[0].p[0], sp[-1].p[1] ); + angle = NormalizeAngle( sp[0].a[0] - sp[-1].a[1] + 180.0 + connectAngle/2 ); + if (dist > connectDistance) + rc |= DIST_FAULT; + if (angle > connectAngle) + rc |= ANGLE_FAULT; + } + return rc; +} + + +static void ComputeCost( + coOrd p, + ANGLE_T a, + section_p sp ) +{ + ANGLE_T da; + coOrd pp; + da = NormalizeAngle( sp->a[0]+180.0-a ); + if (da>180) + da = 360.0-a; + sp->costs[0].A = da*factorA; + pp = sp->p[0]; + Rotate( &pp, p, -a ); + pp.x -= p.x; + pp.y -= p.y; + sp->costs[0].X = fabs(pp.y*factorX); + sp->costs[0].Y = fabs(pp.x*factorY); + if ( pp.x < -0.010 ) + sp->costs[0].X *= 100; + sp->costs[0].T = sp->costs[0].X+sp->costs[0].Y; +} + + +static void ComputeCosts( void ) +{ + int inx; + section_p sp; + maxCosts.A = maxCosts.X = maxCosts.Y = maxCosts.T = 0.0; + sumCosts.A = sumCosts.X = sumCosts.Y = sumCosts.T = 0.0; + maxCostsInx = -1; + for (inx=1; inx<section_da.cnt; inx++) { + sp = §ion(inx); + ComputeCost( sp[-1].p[1], sp[-1].a[1], sp ); +if (debugPull) { +/*printf("%2d: X=%0.3f Y=%0.3f A=%0.3f T=%0.3f\n", inx, sp->costs[0].X, sp->costs[0].Y, sp->costs[0].A, sp->costs[0].T );*/ +} + sumCosts.A += sp->costs[0].A; + sumCosts.X += sp->costs[0].X; + sumCosts.Y += sp->costs[0].Y; + sumCosts.T += sp->costs[0].T; + if ( sp->costs[0].T > maxCosts.T ) { + maxCosts.A = sp->costs[0].A; + maxCosts.X = sp->costs[0].X; + maxCosts.Y = sp->costs[0].Y; + maxCosts.T = sp->costs[0].T; + maxCostsInx = inx; + } + } +} + + +static double ComputeContrib( + DIST_T dist, + ANGLE_T angle, + int start, + int end, + EPINX_T ep ) +{ + int inx; + section_p sp; + ANGLE_T a; + double contrib = 0.0; + for (inx=start; inx<=end; inx++ ) { + sp = §ion(inx); + a = NormalizeAngle(angle - sp->a[ep] + 180.0); + sp->contrib = (a>270.0||a<90.0)?fabs(cos(a)):0.0; + contrib += sp->contrib; + } + return contrib; +} + + +static void ComputeContribs( coOrd *rp1 ) +{ + section_p sp = §ion(maxCostsInx); + double aveX=sumCosts.X/section_da.cnt, aveY=sumCosts.Y/section_da.cnt; + ANGLE_T angle; + DIST_T dist; + coOrd p0=sp[0].p[0], p1=sp[-1].p[1]; + + Rotate( &p1, p0, -sp[0].a[0] ); + p1.x -= p0.x; + p1.y -= p0.y; + if (sp->costs[0].X > 0.000001 && sp->costs[0].X > aveX) + p1.y *= 1-aveX/sp->costs[0].X; + else + p1.y = 0.0; + if (sp->costs[0].Y > 0.000001 && sp->costs[0].Y > aveY) + p1.x *= 1-aveY/sp->costs[0].Y; + else + p1.x = 0.0; + Rotate( &p1, zero, sp[0].a[0] ); + dist = FindDistance( zero, p1 ); + angle = FindAngle( zero, p1 ); + contribL = ComputeContrib( dist, NormalizeAngle(angle+180.0), 1, maxCostsInx-1, 0 ); + contribR = ComputeContrib( dist, angle, maxCostsInx, section_da.cnt-2, 1 ); + + if (debugPull) { + printf( "Minx=%d D=%0.3f A=%0.3f X=%0.3f Y=%0.3f L=%0.3f R=%0.3f\n", + maxCostsInx, dist, angle, p1.x, p1.y, contribL, contribR ); + sp = §ion(0); + printf( " 0[%d] [%0.3f %0.3f] [%0.3f %0.3f]\n", + GetTrkIndex(sp->trk), + sp[0].p[0].x, sp[0].p[0].y, sp[0].p[1].x, sp[0].p[1].y ); + } + *rp1 = p1; +} + + +/* + * Shufflers + */ +static void AdjustSection( + section_p sp, + coOrd amount ) +{ + sp->p[0].x += amount.x; + sp->p[0].y += amount.y; + sp->p[1].x += amount.x; + sp->p[1].y += amount.y; +} + + +static void AdjustSections( coOrd p1 ) +/* adjust end point to lower the costs of this joint to average + */ +{ + double contrib; + section_p sp; + int inx; + + contrib = 0.0; + for ( inx=1; inx<maxCostsInx; inx++ ) { + sp = §ion(inx); + contrib += sp->contrib; + sp->pp.x = -(p1.x*contrib/(contribL+contribR)); + sp->pp.y = -(p1.y*contrib/(contribL+contribR)); + AdjustSection( sp, sp->pp ); + } + contrib = 0.0; + for ( inx=section_da.cnt-1; inx>=maxCostsInx; inx-- ) { + sp = §ion(inx); + contrib += sp->contrib; + sp->pp.x = p1.x*contrib/(contribL+contribR); + sp->pp.y = p1.y*contrib/(contribL+contribR); + AdjustSection( sp, sp->pp ); + } +} + + +static void DumpSections( void ) +{ + section_p sp; + int inx; + DIST_T dist; + for (inx = 1; inx<section_da.cnt; inx++) { + sp = §ion(inx); + dist = FindDistance( sp[0].p[0], sp[-1].p[1] ); + printf( "%2d[%d] X%0.3f Y%0.3f A%0.3f T%0.3f C%0.3f x%0.3f y%0.3f [%0.3f %0.3f] [%0.3f %0.3f] dd%0.3f da%0.3f\n", + inx, GetTrkIndex(sp->trk), sp->costs[0].X, sp->costs[0].Y, sp->costs[0].A, sp->costs[0].T, + sp->contrib, sp->pp.x, sp->pp.y, + sp[0].p[0].x, sp[0].p[0].y, sp[0].p[1].x, sp[0].p[1].y, + dist, + (dist>0.001)?NormalizeAngle( FindAngle( sp[0].p[0], sp[-1].p[1] ) - sp[0].a[0] ):0.0 ); + } + printf("== X%0.3f Y%0.3f A%0.3f T%0.3f\n", sumCosts.X, sumCosts.Y, sumCosts.A, sumCosts.T ); +} + + +/* + * Controller + */ +static int iterCnt = 5; +static int MinimizeCosts( void ) +{ + int inx; + int rc = 0; + coOrd p1; + if (section_da.cnt <= 0) + return FALSE; + for (inx=0; inx<iterCnt; inx++) { + rc = CheckConnections(); + ComputeCosts(); + if (maxCostsInx<0) + return TRUE; + ComputeContribs( &p1 ); + if (contribR+contribL <= 0.001) + return rc; + if (maxCosts.T*1.1 < sumCosts.T/(contribR+contribL) && rc) + /* our work is done */ + return rc; + AdjustSections( p1 ); + if (debugPull) + DumpSections(); + } + return rc; +} + + +/* + * Doit + */ +static void MoveSectionTracks( void ) +{ + int inx, cnt; + section_p sp; + coOrd amount, oldPos; + cnt = 0; + for (inx=1; inx<section_da.cnt-1; inx++) { + sp = §ion(inx); + oldPos = GetTrkEndPos( sp->trk, sp->ep[0] ); + amount.x = sp->p[0].x-oldPos.x; + amount.y = sp->p[0].y-oldPos.y; +if (debugPull) { +printf("%2d: X%0.3f Y%0.3f\n", inx, amount.x, amount.y ); +} + if (fabs(amount.x)>0.001 || fabs(amount.y)>0.001) { + UndrawNewTrack( sp->trk ); + UndoModify( sp->trk ); + MoveTrack( sp->trk, amount ); + DrawNewTrack( sp->trk ); + cnt++; + } + } + InfoMessage( _("%d tracks moved"), cnt ); +} + + +static void PullTracks( + track_p trk1, + EPINX_T ep1, + track_p trk2, + EPINX_T ep2 ) +{ + ending_e e1, e2; + DIST_T d; + ANGLE_T a; + coOrd p1, p2; + ANGLE_T a1, a2; + coOrd p; + int cnt1, cnt2; + int rc; + + if (ConnectAbuttingTracks( trk1, ep1, trk2, ep2 )) + return; + + if (ConnectAdjustableTracks( trk1, ep1, trk2, ep2 )) + return; + + p1 = GetTrkEndPos( trk1, ep1 ); + p2 = GetTrkEndPos( trk2, ep2 ); + a1 = GetTrkEndAngle( trk1, ep1 ); + a2 = GetTrkEndAngle( trk2, ep2 ); + d = FindDistance( p1, p2 ); + a = NormalizeAngle( a1 - a2 + 180 + maxA/2.0 ); + if ( d > maxD || a > maxA ) { + ErrorMessage( MSG_TOO_FAR_APART_DIVERGE ); + return; + } + UndoStart( _("Pull Tracks"), "PullTracks(T%d[%d] T%d[%d] D%0.3f A%0.3F )", GetTrkIndex(trk1), ep1, GetTrkIndex(trk2), ep2, d, a ); + + DYNARR_RESET( section_t, section_da ); + e1 = e2 = GetConnectedTracks( trk1, ep1, trk2, ep2 ); + cnt1 = section_da.cnt; + if ( e1 != loopEnd ) { + e2 = GetConnectedTracks( trk2, ep2, trk1, ep1 ); + } + cnt2 = section_da.cnt - cnt1; + if ( e1 == freeEnd && e2 == freeEnd ) { + p.x = (p1.x+p2.x)/2.0; + p.y = (p1.y+p2.y)/2.0; + a = NormalizeAngle( (a1-(a2+180.0)) ); + if ( a < 180.0 ) + a = NormalizeAngle(a1 + a/2.0); + else + a = NormalizeAngle(a1 - (360-a)/2.0); + MoveConnectedTracks( trk1, ep1, p, a ); + MoveConnectedTracks( trk2, ep2, p, a+180.0 ); + } else if ( e1 == freeEnd ) { + MoveConnectedTracks( trk1, ep1, p2, a2+180.0 ); + } else if ( e2 == freeEnd ) { + MoveConnectedTracks( trk2, ep2, p1, a1+180.0 ); + } else { + if ( e1 == loopEnd ) { + if (section_da.cnt <= 3) { + NoticeMessage( MSG_PULL_FEW_SECTIONS, _("Ok"), NULL ); + return; + } + cnt1 = section_da.cnt/2; + ReverseSectionList( cnt1+1, (int)section_da.cnt ); + ReverseSectionList( 0, cnt1+1 ); + DYNARR_APPEND( section_t, section_da, 10 ); + section(section_da.cnt-1) = section(0); + } else { + ReverseSectionList( 0, cnt1 ); + } + if ((rc=MinimizeCosts())==0) { + MoveSectionTracks(); + } else { + if (rc == DIST_FAULT) { + NoticeMessage( MSG_PULL_ERROR_1, _("Ok"), NULL ); + } else if (rc == ANGLE_FAULT) { + NoticeMessage( MSG_PULL_ERROR_2, _("Ok"), NULL ); + } else { + NoticeMessage( MSG_PULL_ERROR_3, _("Ok"), NULL ); + } + return; + } + } + UndoModify( trk1 ); + UndoModify( trk2 ); + DrawEndPt( &mainD, trk1, ep1, wDrawColorWhite ); + DrawEndPt( &mainD, trk2, ep2, wDrawColorWhite ); + ConnectTracks( trk1, ep1, trk2, ep2 ); + DrawEndPt( &mainD, trk1, ep1, wDrawColorBlack ); + DrawEndPt( &mainD, trk2, ep2, wDrawColorBlack ); +} + + + +/* + * Tighten tracks + */ + +static void TightenTracks( + track_p trk, + EPINX_T ep ) +{ + track_p trk1; + EPINX_T ep1, ep2; + coOrd p0, p1; + ANGLE_T a0, a1; + int cnt; + UndoStart(_("Tighten Tracks"), "TightenTracks(T%d[%d])", GetTrkIndex(trk), ep ); + while ( (ep2=GetNextTrk(trk,ep,&trk1,&ep1,0)) >= 0 && trk1 != NULL ) { + trk = trk1; + ep = ep1; + } + trk1 = GetTrkEndTrk( trk, ep ); + if (trk1 == NULL) + return; + ep1 = GetEndPtConnectedToMe( trk1, trk ); + cnt = 0; + while(1) { + p0 = GetTrkEndPos( trk, ep ); + a0 = NormalizeAngle( GetTrkEndAngle( trk, ep ) + 180.0 ); + p1 = GetTrkEndPos( trk1, ep1 ); + a1 = GetTrkEndAngle( trk1, ep1 ); + p1.x = p0.x - p1.x; + p1.y = p0.y - p1.y; + a1 = NormalizeAngle( a0-a1 ); +if (debugPull) { +printf("T%d [%0.3f %0.3f %0.3f]\n", GetTrkIndex(trk1), p1.x, p1.y, a1 ); +} + if ( FindDistance( zero, p1 ) > 0.001 || ( a1 > 0.05 && a1 < 365.95 ) ) { + UndrawNewTrack( trk1 ); + UndoModify( trk1 ); + MoveTrack( trk1, p1 ); + RotateTrack( trk1, p1, a1 ); + DrawNewTrack( trk1 ); + cnt++; + } + trk = trk1; + ep = GetNextTrk( trk, ep1, &trk1, &ep1, 0 ); + if (trk1 == NULL) + break; + if (ep<0) + AbortProg( "tightenTracks: can't happen" ); + } + InfoMessage( _("%d tracks moved"), cnt ); +} + + +static STATUS_T CmdPull( + wAction_t action, + coOrd pos ) +{ + + static track_p trk1; + static EPINX_T ep1; + track_p trk2; + EPINX_T ep2; + + switch (action) { + + case C_START: + InfoMessage( _("Select first End-Point to connect") ); + trk1 = NULL; + return C_CONTINUE; + + case C_LCLICK: + if ( (MyGetKeyState() & WKEY_SHIFT) == 0 ) { + if (trk1 == NULL) { + if ((trk1 = OnTrack( &pos, TRUE, FALSE )) != NULL) { + if ((ep1 = PickUnconnectedEndPoint( pos, trk1 )) < 0) { + trk1 = NULL; + } else { + InfoMessage( _("Select second End-Point to connect") ); + } + } + } else { + if ((trk2 = OnTrack( &pos, TRUE, FALSE )) != NULL) { + if ((ep2 = PickUnconnectedEndPoint( pos, trk2 )) >= 0 ) { + PullTracks( trk1, ep1, trk2, ep2 ); + trk1 = NULL; + inError = TRUE; + return C_TERMINATE; + } + } + } + } else { + trk1 = OnTrack( &pos, TRUE, FALSE ); + if (trk1 == NULL) + return C_CONTINUE; + ep1 = PickUnconnectedEndPoint( pos, trk1 ); + if ( ep1 < 0 ) + return C_CONTINUE; + TightenTracks( trk1, ep1 ); + trk1 = NULL; + inError = TRUE; + return C_TERMINATE; + } + return C_CONTINUE; + + case C_REDRAW: + return C_CONTINUE; + + case C_CANCEL: + return C_TERMINATE; + + case C_OK: + return C_TERMINATE; + + case C_CONFIRM: + return C_CONTINUE; + + default: + return C_CONTINUE; + } +} + + + +#include "bitmaps/pull.xpm" + +void InitCmdPull( wMenu_p menu ) +{ + AddMenuButton( menu, CmdPull, "cmdConnect", _("Connect Sectional Tracks"), wIconCreatePixMap(pull_xpm), LEVEL0_50, IC_STICKY|IC_LCLICK|IC_POPUP2, ACCL_CONNECT, NULL ); +} diff --git a/app/bin/cruler.c b/app/bin/cruler.c new file mode 100644 index 0000000..6566e93 --- /dev/null +++ b/app/bin/cruler.c @@ -0,0 +1,147 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cruler.c,v 1.4 2008-03-06 19:35:06 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 "track.h" +#include "i18n.h" + +/***************************************************************************** + * + * RULER + * + */ + + + + +#define DR_OFF (0) +#define DR_ON (1) + +static struct { + STATE_T state; + coOrd pos0; + coOrd pos1; + int modifyingEnd; + } Dr = { DR_OFF, { 0,0 }, { 0,0 } }; + +void RulerRedraw( BOOL_T demo ) +{ + if (Dr.state == DR_ON) + DrawRuler( &tempD, Dr.pos0, Dr.pos1, 0.0, TRUE, TRUE, wDrawColorBlack ); + if (demo) + Dr.state = DR_OFF; +} + +static STATUS_T CmdRuler( wAction_t action, coOrd pos ) +{ + switch (action) { + + case C_START: + switch (Dr.state) { + case DR_OFF: + DrawRuler( &tempD, Dr.pos0, Dr.pos1, 0.0, TRUE, TRUE, wDrawColorBlack ); + Dr.state = DR_ON; + InfoMessage( "%s", FormatDistance( FindDistance( Dr.pos0, Dr.pos1 ) ) ); + break; + case DR_ON: + DrawRuler( &tempD, Dr.pos0, Dr.pos1, 0.0, TRUE, TRUE, wDrawColorBlack ); + Dr.state = DR_OFF; + break; + } + MainRedraw(); + return C_CONTINUE; + + case C_DOWN: + if (Dr.state == DR_ON) { + DrawRuler( &tempD, Dr.pos0, Dr.pos1, 0.0, TRUE, TRUE, wDrawColorBlack ); + } + Dr.pos0 = Dr.pos1 = pos; + Dr.state = DR_ON; + DrawRuler( &tempD, Dr.pos0, Dr.pos1, 0.0, TRUE, TRUE, wDrawColorBlack ); + InfoMessage( "0.0" ); + MainRedraw(); + return C_CONTINUE; + + case C_MOVE: + DrawRuler( &tempD, Dr.pos0, Dr.pos1, 0.0, TRUE, TRUE, wDrawColorBlack ); + Dr.pos1 = pos; + DrawRuler( &tempD, Dr.pos0, Dr.pos1, 0.0, TRUE, TRUE, wDrawColorBlack ); + InfoMessage( "%s", FormatDistance( FindDistance( Dr.pos0, Dr.pos1 ) ) ); + MainRedraw(); + return C_CONTINUE; + + case C_UP: + inError = TRUE; + MainRedraw(); + return C_TERMINATE; + + case C_REDRAW: + return C_CONTINUE; + + case C_CANCEL: + return C_TERMINATE; + + } + return C_CONTINUE; +} + + +STATUS_T ModifyRuler( + wAction_t action, + coOrd pos ) +{ + switch (action&0xFF) { + case C_DOWN: + Dr.modifyingEnd = -1; + if ( Dr.state != DR_ON ) + return C_ERROR; + if ( FindDistance( pos, Dr.pos0 ) < mainD.scale*0.25 ) { + Dr.modifyingEnd = 0; + } else if ( FindDistance( pos, Dr.pos1 ) < mainD.scale*0.25 ) { + Dr.modifyingEnd = 1; + } else { + return C_ERROR; + } + case C_MOVE: + DrawRuler( &tempD, Dr.pos0, Dr.pos1, 0.0, TRUE, TRUE, wDrawColorBlack ); + if ( Dr.modifyingEnd == 0 ) { + Dr.pos0 = pos; + } else { + Dr.pos1 = pos; + } + DrawRuler( &tempD, Dr.pos0, Dr.pos1, 0.0, TRUE, TRUE, wDrawColorBlack ); + InfoMessage( "%s", FormatDistance( FindDistance( Dr.pos0, Dr.pos1 ) ) ); + MainRedraw(); + return C_CONTINUE; + case C_UP: + return C_CONTINUE; + default: + return C_ERROR; + } +} + + +#include "bitmaps/ruler.xpm" + +void InitCmdRuler( wMenu_p menu ) +{ + AddMenuButton( menu, CmdRuler, "cmdRuler", _("Ruler"), wIconCreatePixMap(ruler_xpm), LEVEL0, IC_STICKY|IC_NORESTART, ACCL_RULER, NULL ); +} diff --git a/app/bin/cselect.c b/app/bin/cselect.c new file mode 100644 index 0000000..1bafd45 --- /dev/null +++ b/app/bin/cselect.c @@ -0,0 +1,1918 @@ +/** \file cselect.c + * Handle selecting / unselecting track and basic operations on the selection + * + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cselect.c,v 1.11 2008-09-05 08:08:15 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 "track.h" +/*#include "trackx.h"*/ +#include "ccurve.h" +#define PRIVATE_EXTRADATA +#include "compound.h" + +#include "bitmaps/bmendpt.xbm" +#include "bitmaps/bma0.xbm" +#include "bitmaps/bma45.xbm" +#include "bitmaps/bma90.xbm" +#include "bitmaps/bma135.xbm" +#include "i18n.h" + + +#define SETMOVEMODE "MOVEMODE" + +EXPORT wIndex_t selectCmdInx; +EXPORT wIndex_t moveCmdInx; +EXPORT wIndex_t rotateCmdInx; + +#define MAXMOVEMODE (3) +static long moveMode = MAXMOVEMODE; +static BOOL_T enableMoveDraw = TRUE; +static BOOL_T move0B; +struct extraData { char junk[2000]; }; + +static wDrawBitMap_p endpt_bm; +static wDrawBitMap_p angle_bm[4]; + + long quickMove = 0; + BOOL_T importMove = 0; + int incrementalDrawLimit = 20; + +static dynArr_t tlist_da; +#define Tlist(N) DYNARR_N( track_p, tlist_da, N ) +#define TlistAppend( T ) \ + { DYNARR_APPEND( track_p, tlist_da, 10 );\ + Tlist(tlist_da.cnt-1) = T; } +static track_p *tlist2 = NULL; + +static wMenu_p selectPopup1M; +static wMenu_p selectPopup2M; + +static void DrawSelectedTracksD( drawCmd_p d, wDrawColor color ); + +/***************************************************************************** + * + * SELECT TRACKS + * + */ + +EXPORT long selectedTrackCount = 0; /**< number of currently selected track components */ + +static void SelectedTrackCountChange( void ) +{ + static long oldCount = 0; + if (selectedTrackCount != oldCount) { + if (oldCount == 0) { + /* going non-0 */ + EnableCommands(); + } else if (selectedTrackCount == 0) { + /* going 0 */ + EnableCommands(); + } + oldCount = selectedTrackCount; + } +} + + +static void DrawTrackAndEndPts( + track_p trk, + wDrawColor color ) +{ + EPINX_T ep, ep2; + track_p trk2; + + DrawTrack( trk, &mainD, color ); + for (ep=0;ep<GetTrkEndPtCnt(trk);ep++) { + if ((trk2=GetTrkEndTrk(trk,ep)) != NULL) { + ASSERT( !IsTrackDeleted(trk) ); + ep2 = GetEndPtConnectedToMe( trk2, trk ); + DrawEndPt( &mainD, trk2, ep2, + (color==wDrawColorBlack && GetTrkSelected(trk2))? + selectedColor:color ); + } + } +} + + +EXPORT void SetAllTrackSelect( BOOL_T select ) +{ + track_p trk; + BOOL_T doRedraw = FALSE; + + if (select || selectedTrackCount > incrementalDrawLimit) { + doRedraw = TRUE; + } else { + wDrawDelayUpdate( mainD.d, TRUE ); + } + selectedTrackCount = 0; + trk = NULL; + while ( TrackIterate( &trk ) ) { + if ((!select) || GetLayerVisible( GetTrkLayer( trk ))) { + if (select) + selectedTrackCount++; + if ((GetTrkSelected(trk)!=0) != select) { + if (!doRedraw) + DrawTrackAndEndPts( trk, wDrawColorWhite ); + if (select) + SetTrkBits( trk, TB_SELECTED ); + else + ClrTrkBits( trk, TB_SELECTED ); + if (!doRedraw) + DrawTrackAndEndPts( trk, wDrawColorBlack ); + } + } + } + SelectedTrackCountChange(); + if (doRedraw) { + MainRedraw(); + } else { + wDrawDelayUpdate( mainD.d, FALSE ); + } +} + +/* Invert selected state of all visible objects. + * + * \param none + * \return none + */ + +EXPORT void InvertTrackSelect( void *ptr ) +{ + track_p trk; + + trk = NULL; + while ( TrackIterate( &trk ) ) { + if (GetLayerVisible( GetTrkLayer( trk ))) { + if (GetTrkSelected(trk)) + { + ClrTrkBits( trk, TB_SELECTED ); + selectedTrackCount--; + } + else + SetTrkBits( trk, TB_SELECTED ); + selectedTrackCount++; + } + } + + SelectedTrackCountChange(); + MainRedraw(); +} + +/* Select orphaned (ie single) track pieces. + * + * \param none + * \return none + */ + +EXPORT void OrphanedTrackSelect( void *ptr ) +{ + track_p trk; + EPINX_T ep; + int cnt ; + + trk = NULL; + + while( TrackIterate( &trk ) ) { + cnt = 0; + if( GetLayerVisible( GetTrkLayer( trk ))) { + for( ep = 0; ep < GetTrkEndPtCnt( trk ); ep++ ) { + if( GetTrkEndTrk( trk, ep ) ) + cnt++; + } + + if( !cnt && GetTrkEndPtCnt( trk )) { + SetTrkBits( trk, TB_SELECTED ); + DrawTrackAndEndPts( trk, wDrawColorBlack ); + selectedTrackCount++; + } + } + } + SelectedTrackCountChange(); + MainRedraw(); +} + + +static void SelectOneTrack( + track_p trk, + wBool_t selected ) +{ + DrawTrackAndEndPts( trk, wDrawColorWhite ); + if (selected) { + SetTrkBits( trk, TB_SELECTED ); + selectedTrackCount++; + } else { + ClrTrkBits( trk, TB_SELECTED ); + selectedTrackCount--; + } + SelectedTrackCountChange(); + DrawTrackAndEndPts( trk, wDrawColorBlack ); +} + + +static void SelectConnectedTracks( + track_p trk ) +{ + track_p trk1; + int inx; + EPINX_T ep; + tlist_da.cnt = 0; + TlistAppend( trk ); + InfoCount( 0 ); + wDrawDelayUpdate( mainD.d, FALSE ); + for (inx=0; inx<tlist_da.cnt; inx++) { + if ( inx > 0 && selectedTrackCount == 0 ) + return; + trk = Tlist(inx); + if (inx!=0 && + GetTrkSelected(trk)) + continue; + for (ep=0; ep<GetTrkEndPtCnt(trk); ep++) { + trk1 = GetTrkEndTrk( trk, ep ); + if (trk1 && (!GetTrkSelected(trk1)) && GetLayerVisible( GetTrkLayer( trk1 )) ) { + TlistAppend( trk1 ) + } + } + if (!GetTrkSelected(trk)) { + SelectOneTrack( trk, TRUE ); + InfoCount( inx+1 ); + } + SetTrkBits(trk, TB_SELECTED); + } + wDrawDelayUpdate( mainD.d, TRUE ); + wFlush(); + InfoCount( trackCount ); +} + + + +typedef BOOL_T (*doSelectedTrackCallBack_t)(track_p, BOOL_T); +static void DoSelectedTracks( doSelectedTrackCallBack_t doit ) +{ + track_p trk; + trk = NULL; + while ( TrackIterate( &trk ) ) { + if (GetTrkSelected(trk)) { + if ( !doit( trk, TRUE ) ) { + break; + } + } + } +} + + +static BOOL_T SelectedTracksAreFrozen( void ) +{ + track_p trk; + trk = NULL; + while ( TrackIterate( &trk ) ) { + if ( GetTrkSelected(trk) ) { + if ( GetLayerFrozen( GetTrkLayer( trk ) ) ) { + ErrorMessage( MSG_SEL_TRK_FROZEN ); + return TRUE; + } + } + } + return FALSE; +} + + +EXPORT void SelectTrackWidth( void* width ) +{ + track_p trk; + if (SelectedTracksAreFrozen()) + return; + if (selectedTrackCount<=0) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return; + } + UndoStart( _("Change Track Width"), "trackwidth" ); + trk = NULL; + wDrawDelayUpdate( mainD.d, TRUE ); + while ( TrackIterate( &trk ) ) { + if (GetTrkSelected(trk)) { + DrawTrackAndEndPts( trk, wDrawColorWhite ); + UndoModify( trk ); + SetTrkWidth( trk, (int)(long)width ); + DrawTrackAndEndPts( trk, wDrawColorBlack ); + } + } + wDrawDelayUpdate( mainD.d, FALSE ); + UndoEnd(); +} + + +EXPORT void SelectDelete( void ) +{ + if (SelectedTracksAreFrozen()) + return; + if (selectedTrackCount>0) { + UndoStart( _("Delete Tracks"), "delete" ); + wDrawDelayUpdate( mainD.d, TRUE ); + wDrawDelayUpdate( mapD.d, TRUE ); + DoSelectedTracks( DeleteTrack ); + wDrawDelayUpdate( mainD.d, FALSE ); + wDrawDelayUpdate( mapD.d, FALSE ); + selectedTrackCount = 0; + SelectedTrackCountChange(); + UndoEnd(); + } else { + ErrorMessage( MSG_NO_SELECTED_TRK ); + } +} + + +BOOL_T flipHiddenDoSelectRecount; +static BOOL_T FlipHidden( track_p trk, BOOL_T junk ) +{ + EPINX_T i; + track_p trk2; + + DrawTrackAndEndPts( trk, wDrawColorWhite ); + /*UndrawNewTrack( trk ); + for (i=0; i<GetTrkEndPtCnt(trk); i++) + if ((trk2=GetTrkEndTrk(trk,i)) != NULL) { + UndrawNewTrack( trk2 ); + }*/ + UndoModify( trk ); + if ( drawTunnel == 0 ) + flipHiddenDoSelectRecount = TRUE; + if (GetTrkVisible(trk)) { + ClrTrkBits( trk, TB_VISIBLE|(drawTunnel==0?TB_SELECTED:0) ); + } else { + SetTrkBits( trk, TB_VISIBLE ); + } + /*DrawNewTrack( trk );*/ + DrawTrackAndEndPts( trk, wDrawColorBlack ); + for (i=0; i<GetTrkEndPtCnt(trk); i++) + if ((trk2=GetTrkEndTrk(trk,i)) != NULL) { + UndoModify( trk2 ); + /*DrawNewTrack( trk2 );*/ + } + return TRUE; +} + + +EXPORT void SelectTunnel( void ) +{ + if (SelectedTracksAreFrozen()) + return; + if (selectedTrackCount>0) { + flipHiddenDoSelectRecount = FALSE; + UndoStart( _("Hide Tracks (Tunnel)"), "tunnel" ); + wDrawDelayUpdate( mainD.d, TRUE ); + DoSelectedTracks( FlipHidden ); + wDrawDelayUpdate( mainD.d, FALSE ); + UndoEnd(); + } else { + ErrorMessage( MSG_NO_SELECTED_TRK ); + } + if ( flipHiddenDoSelectRecount ) + SelectRecount(); +} + + +EXPORT void SelectRecount( void ) +{ + track_p trk; + selectedTrackCount = 0; + trk = NULL; + while ( TrackIterate( &trk ) ) { + if (GetTrkSelected(trk)) { + selectedTrackCount++; + } + } + SelectedTrackCountChange(); +} + + +static BOOL_T SetLayer( track_p trk, BOOL_T junk ) +{ + UndoModify( trk ); + SetTrkLayer( trk, curLayer ); + return TRUE; +} + +EXPORT void MoveSelectedTracksToCurrentLayer( void ) +{ + if (SelectedTracksAreFrozen()) + return; + if (selectedTrackCount>0) { + UndoStart( _("Move To Current Layer"), "changeLayer" ); + DoSelectedTracks( SetLayer ); + UndoEnd(); + } else { + ErrorMessage( MSG_NO_SELECTED_TRK ); + } +} + +EXPORT void SelectCurrentLayer( void ) +{ + track_p trk; + trk = NULL; + while ( TrackIterate( &trk ) ) { + if ((!GetTrkSelected(trk)) && GetTrkLayer(trk) == curLayer ) { + SelectOneTrack( trk, TRUE ); + } + } +} + + +static BOOL_T ClearElevation( track_p trk, BOOL_T junk ) +{ + EPINX_T ep; + for ( ep=0; ep<GetTrkEndPtCnt(trk); ep++ ) { + if (!EndPtIsIgnoredElev(trk,ep)) { + DrawEndPt2( &mainD, trk, ep, wDrawColorWhite ); + SetTrkEndElev( trk, ep, ELEV_NONE, 0.0, NULL ); + ClrTrkElev( trk ); + DrawEndPt2( &mainD, trk, ep, wDrawColorBlack ); + } + } + return TRUE; +} + +EXPORT void ClearElevations( void ) +{ + if (SelectedTracksAreFrozen()) + return; + if (selectedTrackCount>0) { + UndoStart( _("Clear Elevations"), "clear elevations" ); + DoSelectedTracks( ClearElevation ); + UpdateAllElevations(); + UndoEnd(); + } else { + ErrorMessage( MSG_NO_SELECTED_TRK ); + } +} + + +static DIST_T elevDelta; +static BOOL_T AddElevation( track_p trk, BOOL_T junk ) +{ + track_p trk1; + EPINX_T ep, ep1; + int mode; + DIST_T elev; + + for ( ep=0; ep<GetTrkEndPtCnt(trk); ep++ ) { + if ((trk1=GetTrkEndTrk(trk,ep))) { + ep1 = GetEndPtConnectedToMe( trk1, trk ); + if (ep1 >= 0) { + if (GetTrkSelected(trk1) && GetTrkIndex(trk1)<GetTrkIndex(trk)) + continue; + } + } + if (EndPtIsDefinedElev(trk,ep)) { + DrawEndPt2( &mainD, trk, ep, wDrawColorWhite ); + mode = GetTrkEndElevUnmaskedMode(trk,ep); + elev = GetTrkEndElevHeight(trk,ep); + SetTrkEndElev( trk, ep, mode, elev+elevDelta, NULL ); + ClrTrkElev( trk ); + DrawEndPt2( &mainD, trk, ep, wDrawColorBlack ); + } + } + return TRUE; +} + +EXPORT void AddElevations( DIST_T delta ) +{ + if (SelectedTracksAreFrozen()) + return; + if (selectedTrackCount>0) { + elevDelta = delta; + UndoStart( _("Add Elevations"), "add elevations" ); + DoSelectedTracks( AddElevation ); + UndoEnd(); + } else { + ErrorMessage( MSG_NO_SELECTED_TRK ); + } + UpdateAllElevations(); +} + + +EXPORT void DoRefreshCompound( void ) +{ + if (SelectedTracksAreFrozen()) + return; + if (selectedTrackCount>0) { + UndoStart( _("Refresh Compound"), "refresh compound" ); + DoSelectedTracks( RefreshCompound ); + RefreshCompound( NULL, FALSE ); + UndoEnd(); + MainRedraw(); + } else { + ErrorMessage( MSG_NO_SELECTED_TRK ); + } +} + + +static drawCmd_t tempSegsD = { + NULL, &tempSegDrawFuncs, DC_GROUP, 1, 0.0, {0.0, 0.0}, {0.0, 0.0}, Pix2CoOrd, CoOrd2Pix }; +EXPORT void WriteSelectedTracksToTempSegs( void ) +{ + track_p trk; + long oldOptions; + DYNARR_RESET( trkSeg_t, tempSegs_da ); + tempSegsD.dpi = mainD.dpi; + oldOptions = tempSegDrawFuncs.options; + tempSegDrawFuncs.options = wDrawOptTemp; + for ( trk=NULL; TrackIterate(&trk); ) { + if ( GetTrkSelected( trk ) ) { + if ( IsTrack( trk ) ) + continue; + ClrTrkBits( trk, TB_SELECTED ); + DrawTrack( trk, &tempSegsD, wDrawColorBlack ); + SetTrkBits( trk, TB_SELECTED ); + } + } + tempSegDrawFuncs.options = oldOptions; +} + +static char rescaleFromScale[20]; +static char rescaleFromGauge[20]; + +static char * rescaleToggleLabels[] = { N_("Scale"), N_("Ratio"), NULL }; +static long rescaleMode; +static wIndex_t rescaleFromScaleInx; +static wIndex_t rescaleFromGaugeInx; +static wIndex_t rescaleToScaleInx; +static wIndex_t rescaleToGaugeInx; +static wIndex_t rescaleToInx; +static long rescaleNoChangeDim = FALSE; +static FLOAT_T rescalePercent; +static char * rescaleChangeDimLabels[] = { N_("Do not resize track"), NULL }; +static paramFloatRange_t r0o001_10000 = { 0.001, 10000.0 }; +static paramData_t rescalePLs[] = { +#define I_RESCALE_MODE (0) + { PD_RADIO, &rescaleMode, "toggle", PDO_NOPREF, &rescaleToggleLabels, N_("Rescale by:"), BC_HORZ|BC_NOBORDER }, +#define I_RESCALE_FROM_SCALE (1) + { PD_STRING, rescaleFromScale, "fromS", PDO_NOPREF, (void *)100, N_("From:") }, +#define I_RESCALE_FROM_GAUGE (2) + { PD_STRING, rescaleFromGauge, "fromG", PDO_NOPREF|PDO_DLGHORZ, (void *)100, " / " }, +#define I_RESCALE_TO_SCALE (3) + { PD_DROPLIST, &rescaleToScaleInx, "toS", PDO_NOPREF|PDO_LISTINDEX, (void *)100, N_("To: ") }, +#define I_RESCALE_TO_GAUGE (4) + { PD_DROPLIST, &rescaleToGaugeInx, "toG", PDO_NOPREF|PDO_LISTINDEX|PDO_DLGHORZ, NULL, " / " }, +#define I_RESCALE_CHANGE (5) + { PD_TOGGLE, &rescaleNoChangeDim, "change-dim", 0, &rescaleChangeDimLabels, "", BC_HORZ|BC_NOBORDER }, +#define I_RESCALE_PERCENT (6) + { PD_FLOAT, &rescalePercent, "ratio", 0, &r0o001_10000, N_("Ratio") }, + { PD_MESSAGE, "%", NULL, PDO_DLGHORZ } }; +static paramGroup_t rescalePG = { "rescale", 0, rescalePLs, sizeof rescalePLs/sizeof rescalePLs[0] }; + + +static long getboundsCount; +static coOrd getboundsLo, getboundsHi; + +static BOOL_T GetboundsDoIt( track_p trk, BOOL_T junk ) +{ + coOrd hi, lo; + + GetBoundingBox( trk, &hi, &lo ); + if ( getboundsCount == 0 ) { + getboundsLo = lo; + getboundsHi = hi; + } else { + if ( lo.x < getboundsLo.x ) getboundsLo.x = lo.x; + if ( lo.y < getboundsLo.y ) getboundsLo.y = lo.y; + if ( hi.x > getboundsHi.x ) getboundsHi.x = hi.x; + if ( hi.y > getboundsHi.y ) getboundsHi.y = hi.y; + } + getboundsCount++; + return TRUE; +} + +static coOrd rescaleShift; +static BOOL_T RescaleDoIt( track_p trk, BOOL_T junk ) +{ + EPINX_T ep, ep1; + track_p trk1; + UndoModify(trk); + if ( rescalePercent != 100.0 ) { + for (ep=0; ep<GetTrkEndPtCnt(trk); ep++) { + if ((trk1 = GetTrkEndTrk(trk,ep)) != NULL && + !GetTrkSelected(trk1)) { + ep1 = GetEndPtConnectedToMe( trk1, trk ); + DisconnectTracks( trk, ep, trk1, ep1 ); + } + } + /* should the track dimensions ie. length or radius be changed as well? */ + if( rescaleNoChangeDim == 0 ) + RescaleTrack( trk, rescalePercent/100.0, rescaleShift ); + } + + if ( rescaleMode==0 ) + SetTrkScale( trk, rescaleToInx ); + getboundsCount++; + return TRUE; +} + + +static void RescaleDlgOk( + void * junk ) +{ + coOrd center, size; + DIST_T d; + FLOAT_T ratio = rescalePercent/100.0; + + UndoStart( _("Rescale Tracks"), "Rescale" ); + getboundsCount = 0; + DoSelectedTracks( GetboundsDoIt ); + center.x = (getboundsLo.x+getboundsHi.x)/2.0; + center.y = (getboundsLo.y+getboundsHi.y)/2.0; + size.x = (getboundsHi.x-getboundsLo.x)/2.0*ratio; + size.y = (getboundsHi.y-getboundsLo.y)/2.0*ratio; + getboundsLo.x = center.x - size.x; + getboundsLo.y = center.y - size.y; + getboundsHi.x = center.x + size.x; + getboundsHi.y = center.y + size.y; + if ( getboundsLo.x < 0 ) { + getboundsHi.x -= getboundsLo.x; + getboundsLo.x = 0; + } else if ( getboundsHi.x > mapD.size.x ) { + d = getboundsHi.x - mapD.size.x; + if ( getboundsLo.x < d ) + d = getboundsLo.x; + getboundsHi.x -= d; + getboundsLo.x -= d; + } + if ( getboundsLo.y < 0 ) { + getboundsHi.y -= getboundsLo.y; + getboundsLo.y = 0; + } else if ( getboundsHi.y > mapD.size.y ) { + d = getboundsHi.y - mapD.size.y; + if ( getboundsLo.y < d ) + d = getboundsLo.y; + getboundsHi.y -= d; + getboundsLo.y -= d; + } + if ( rescaleNoChangeDim == 0 && + (getboundsHi.x > mapD.size.x || + getboundsHi.y > mapD.size.y )) { + NoticeMessage( MSG_RESCALE_TOO_BIG, _("Ok"), NULL, FormatDistance(getboundsHi.x), FormatDistance(getboundsHi.y) ); + } + rescaleShift.x = (getboundsLo.x+getboundsHi.x)/2.0 - center.x*ratio; + rescaleShift.y = (getboundsLo.y+getboundsHi.y)/2.0 - center.y*ratio; + + rescaleToInx = GetScaleInx( rescaleToScaleInx, rescaleToGaugeInx ); + DoSelectedTracks( RescaleDoIt ); + DoRedraw(); + wHide( rescalePG.win ); +} + + +static void RescaleDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + switch (inx) { + case I_RESCALE_MODE: + wControlShow( pg->paramPtr[I_RESCALE_FROM_SCALE].control, rescaleMode==0 ); + wControlActive( pg->paramPtr[I_RESCALE_FROM_SCALE].control, FALSE ); + wControlShow( pg->paramPtr[I_RESCALE_TO_SCALE].control, rescaleMode==0 ); + wControlShow( pg->paramPtr[I_RESCALE_FROM_GAUGE].control, rescaleMode==0 ); + wControlActive( pg->paramPtr[I_RESCALE_FROM_GAUGE].control, FALSE ); + wControlShow( pg->paramPtr[I_RESCALE_TO_GAUGE].control, rescaleMode==0 ); + wControlShow( pg->paramPtr[I_RESCALE_CHANGE].control, rescaleMode==0 ); + wControlActive( pg->paramPtr[I_RESCALE_PERCENT].control, rescaleMode==1 ); + if ( rescaleMode!=0 ) + break; + case I_RESCALE_TO_SCALE: + LoadGaugeList( (wList_p)rescalePLs[I_RESCALE_TO_GAUGE].control, *((int *)valueP) ); + rescaleToGaugeInx = 0; + ParamLoadControl( pg, I_RESCALE_TO_GAUGE ); + ParamLoadControl( pg, I_RESCALE_TO_SCALE ); + rescalePercent = GetScaleDescRatio(rescaleFromScaleInx)/GetScaleDescRatio(rescaleToScaleInx)*100.0; + wControlActive( pg->paramPtr[I_RESCALE_CHANGE].control, (rescaleFromScaleInx != rescaleToScaleInx) ); + ParamLoadControl( pg, I_RESCALE_PERCENT ); + break; + case I_RESCALE_TO_GAUGE: + ParamLoadControl( pg, I_RESCALE_TO_GAUGE ); + break; + case I_RESCALE_FROM_SCALE: + ParamLoadControl( pg, I_RESCALE_FROM_SCALE ); + break; + case I_RESCALE_FROM_GAUGE: + ParamLoadControl( pg, I_RESCALE_FROM_GAUGE ); + break; + case I_RESCALE_CHANGE: + ParamLoadControl( pg, I_RESCALE_CHANGE ); + break; + case -1: + break; + } + ParamDialogOkActive( pg, rescalePercent!=100.0 || rescaleFromGaugeInx != rescaleToGaugeInx ); +} + +/** + * Get the scale gauge information for the selected track pieces. + * FIXME: special cases like tracks pieces with different gauges or scale need to be handled + * + * \param IN trk track element + * \param IN junk + * \return TRUE; + */ + +static BOOL_T SelectedScaleGauge( track_p trk, BOOL_T junk ) +{ + char *scaleName; + SCALEINX_T scale; + SCALEDESCINX_T scaleInx; + GAUGEINX_T gaugeInx; + + scale = GetTrkScale( trk ); + scaleName = GetScaleName( scale ); + if( strcmp( scaleName, "*" )) { + GetScaleGauge( scale, &scaleInx, &gaugeInx ); + strcpy( rescaleFromScale,GetScaleDesc( scaleInx )); + strcpy( rescaleFromGauge, GetGaugeDesc( scaleInx, gaugeInx )); + + rescaleFromScaleInx = scaleInx; + rescaleFromGaugeInx = gaugeInx; + rescaleToScaleInx = scaleInx; + rescaleToGaugeInx = gaugeInx; + } + + return TRUE; +} + +/** + * Bring up the rescale dialog. The dialog for rescaling the selected pieces + * of track is created if necessary and shown. Handling of user input is done via + * RescaleDlgUpdate() + */ + +EXPORT void DoRescale( void ) +{ + if ( rescalePG.win == NULL ) { + ParamCreateDialog( &rescalePG, MakeWindowTitle(_("Rescale")), _("Ok"), RescaleDlgOk, wHide, TRUE, NULL, F_BLOCK, RescaleDlgUpdate ); + LoadScaleList( (wList_p)rescalePLs[I_RESCALE_TO_SCALE].control ); + LoadGaugeList( (wList_p)rescalePLs[I_RESCALE_TO_GAUGE].control, curScaleDescInx ); /* set correct gauge list here */ + rescaleFromScaleInx = curScaleInx; + rescaleToScaleInx = curScaleInx; + rescalePercent = 100.0; + } + + DoSelectedTracks( SelectedScaleGauge ); + + RescaleDlgUpdate( &rescalePG, I_RESCALE_MODE, &rescaleMode ); + RescaleDlgUpdate( &rescalePG, I_RESCALE_CHANGE, &rescaleMode ); + + RescaleDlgUpdate( &rescalePG, I_RESCALE_FROM_GAUGE, rescaleFromGauge ); + RescaleDlgUpdate( &rescalePG, I_RESCALE_FROM_SCALE, rescaleFromScale ); + + RescaleDlgUpdate( &rescalePG, I_RESCALE_TO_SCALE, &rescaleToScaleInx ); + RescaleDlgUpdate( &rescalePG, I_RESCALE_TO_GAUGE, &rescaleToGaugeInx ); + + wShow( rescalePG.win ); +} + + +#define MOVE_NORMAL (0) +#define MOVE_FAST (1) +#define MOVE_QUICK (2) +static char *quickMoveMsgs[] = { + N_("Draw moving track normally"), + N_("Draw moving track simply"), + N_("Draw moving track as end-points") }; +static wMenuToggle_p quickMove1M[3]; +static wMenuToggle_p quickMove2M[3]; + +static void ChangeQuickMove( wBool_t set, void * mode ) +{ + long inx; + quickMove = (long)mode; + InfoMessage( quickMoveMsgs[quickMove] ); + DoChangeNotification( CHANGE_CMDOPT ); + for (inx = 0; inx<3; inx++) { + wMenuToggleSet( quickMove1M[inx], quickMove == inx ); + wMenuToggleSet( quickMove2M[inx], quickMove == inx ); + } +} + +EXPORT void UpdateQuickMove( void * junk ) +{ + long inx; + for (inx = 0; inx<3; inx++) { + wMenuToggleSet( quickMove1M[inx], quickMove == inx ); + wMenuToggleSet( quickMove2M[inx], quickMove == inx ); + } +} + + +static void DrawSelectedTracksD( drawCmd_p d, wDrawColor color ) +{ + wIndex_t inx; + track_p trk; + coOrd lo, hi; + /*wDrawDelayUpdate( d->d, TRUE );*/ + for (inx=0; inx<tlist_da.cnt; inx++) { + trk = Tlist(inx); + if (d != &mapD) { + GetBoundingBox( trk, &hi, &lo ); + if ( OFF_D( d->orig, d->size, lo, hi ) ) + continue; + } + DrawTrack( trk, d, color ); + } + /*wDrawDelayUpdate( d->d, FALSE );*/ +} + +static BOOL_T AddSelectedTrack( + track_p trk, BOOL_T junk ) +{ + DYNARR_APPEND( track_p, tlist_da, 10 ); + DYNARR_LAST( track_p, tlist_da ) = trk; + return TRUE; +} + +static coOrd moveOrig; +static ANGLE_T moveAngle; + +static coOrd moveD_hi, moveD_lo; + +static drawCmd_t moveD = { + NULL, &tempDrawFuncs, DC_SIMPLE, 1, 0.0, {0.0, 0.0}, {0.0, 0.0}, Pix2CoOrd, CoOrd2Pix }; + + + + +/* Draw selected (on-screen) tracks to tempSegs, + and use drawSegs to draw them (moved/rotated) to mainD + Incremently add new tracks as they scroll on-screen. +*/ + + +static int movedCnt; +static void AccumulateTracks( void ) +{ + wIndex_t inx; + track_p trk; + coOrd lo, hi; + + /*wDrawDelayUpdate( moveD.d, TRUE );*/ + if (quickMove == MOVE_FAST) + moveD.options |= DC_QUICK; + for ( inx = 0; inx<tlist_da.cnt; inx++ ) { + trk = tlist2[inx]; + if (trk) { + GetBoundingBox( trk, &hi, &lo ); + if (lo.x <= moveD_hi.x && hi.x >= moveD_lo.x && + lo.y <= moveD_hi.y && hi.y >= moveD_lo.y ) { + if (quickMove != MOVE_QUICK) { +#if defined(WINDOWS) && ! defined(WIN32) + if ( tempSegs_da.cnt+100 > 65500 / sizeof(*(trkSeg_p)NULL) ) { + ErrorMessage( MSG_TOO_MANY_SEL_TRKS ); + + quickMove = MOVE_QUICK; + } else +#endif + DrawTrack( trk, &moveD, wDrawColorBlack ); + } + tlist2[inx] = NULL; + movedCnt++; + } + } + } + moveD.options &= ~DC_QUICK; + InfoCount( movedCnt ); + /*wDrawDelayUpdate( moveD.d, FALSE );*/ +} + + +static void GetMovedTracks( BOOL_T undraw ) +{ + wSetCursor( wCursorWait ); + DYNARR_RESET( track_p, tlist_da ); + DoSelectedTracks( AddSelectedTrack ); + tlist2 = (track_p*)MyRealloc( tlist2, (tlist_da.cnt+1) * sizeof *(track_p*)0 ); + if (tlist_da.ptr) + memcpy( tlist2, tlist_da.ptr, (tlist_da.cnt) * sizeof *(track_p*)0 ); + tlist2[tlist_da.cnt] = NULL; + DYNARR_RESET( trkSeg_p, tempSegs_da ); + moveD = mainD; + moveD.funcs = &tempSegDrawFuncs; + moveD.options = DC_SIMPLE; + tempSegDrawFuncs.options = wDrawOptTemp; + moveOrig = mainD.orig; + movedCnt = 0; + InfoCount(0); + wSetCursor( wCursorNormal ); + moveD_hi = moveD_lo = mainD.orig; + moveD_hi.x += mainD.size.x; + moveD_hi.y += mainD.size.y; + AccumulateTracks(); + if (undraw) { + DrawSelectedTracksD( &mainD, wDrawColorWhite ); + /*DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, + trackGauge, wDrawColorBlack );*/ + } +} + +static void SetMoveD( BOOL_T moveB, coOrd orig, ANGLE_T angle ) +{ + int inx; + + moveOrig.x = orig.x; + moveOrig.y = orig.y; + moveAngle = angle; + if (!moveB) { + Rotate( &orig, zero, angle ); + moveOrig.x -= orig.x; + moveOrig.y -= orig.y; + } + if (moveB) { + moveD_lo.x = mainD.orig.x - orig.x; + moveD_lo.y = mainD.orig.y - orig.y; + moveD_hi = moveD_lo; + moveD_hi.x += mainD.size.x; + moveD_hi.y += mainD.size.y; + } else { + coOrd corner[3]; + corner[2].x = mainD.orig.x; + corner[0].x = corner[1].x = mainD.orig.x + mainD.size.x; + corner[0].y = mainD.orig.y; + corner[1].y = corner[2].y = mainD.orig.y + mainD.size.y; + moveD_hi = mainD.orig; + Rotate( &moveD_hi, orig, -angle ); + moveD_lo = moveD_hi; + for (inx=0;inx<3;inx++) { + Rotate( &corner[inx], orig, -angle ); + if (corner[inx].x < moveD_lo.x) + moveD_lo.x = corner[inx].x; + if (corner[inx].y < moveD_lo.y) + moveD_lo.y = corner[inx].y; + if (corner[inx].x > moveD_hi.x) + moveD_hi.x = corner[inx].x; + if (corner[inx].y > moveD_hi.y) + moveD_hi.y = corner[inx].y; + } + } + AccumulateTracks(); +} + + +static void DrawMovedTracks( void ) +{ + int inx; + track_p trk; + track_p other; + EPINX_T i; + coOrd pos; + wDrawBitMap_p bm; + ANGLE_T a; + int ia; + + if ( quickMove != MOVE_QUICK) { + DrawSegs( &tempD, moveOrig, moveAngle, &tempSegs(0), tempSegs_da.cnt, + 0.0, wDrawColorBlack ); + return; + } + for ( inx=0; inx<tlist_da.cnt; inx++ ) { + trk = Tlist(inx); + if (tlist2[inx] != NULL) + continue; + for (i=GetTrkEndPtCnt(trk)-1; i>=0; i--) { + pos = GetTrkEndPos(trk,i); + if (!move0B) { + Rotate( &pos, zero, moveAngle ); + } + pos.x += moveOrig.x; + pos.y += moveOrig.y; + if ((other=GetTrkEndTrk(trk,i)) == NULL || + !GetTrkSelected(other)) { + bm = endpt_bm; + } else if (other != NULL && GetTrkIndex(trk) < GetTrkIndex(other)) { + a = GetTrkEndAngle(trk,i)+22.5; + if (!move0B) + a += moveAngle; + a = NormalizeAngle( a ); + if (a>=180.0) + a -= 180.0; + ia = (int)(a/45.0); + bm = angle_bm[ia]; + } else { + continue; + } + if ( !OFF_MAIND( pos, pos ) ) + DrawBitMap( &tempD, pos, bm, selectedColor ); + } + } +} + + + +static void MoveTracks( + BOOL_T eraseFirst, + BOOL_T move, + BOOL_T rotate, + coOrd base, + coOrd orig, + ANGLE_T angle ) +{ + track_p trk, trk1; + EPINX_T ep, ep1; + int inx; + + wSetCursor( wCursorWait ); + /*UndoStart( "Move/Rotate Tracks", "move/rotate" );*/ + if (tlist_da.cnt <= incrementalDrawLimit) { + DrawMapBoundingBox( FALSE ); + if (eraseFirst) + DrawSelectedTracksD( &mainD, wDrawColorWhite ); + DrawSelectedTracksD( &mapD, wDrawColorWhite ); + } + for ( inx=0; inx<tlist_da.cnt; inx++ ) { + trk = Tlist(inx); + UndoModify( trk ); + if (move) + MoveTrack( trk, base ); + if (rotate) + RotateTrack( trk, orig, angle ); + for (ep=0; ep<GetTrkEndPtCnt(trk); ep++) { + if ((trk1 = GetTrkEndTrk(trk,ep)) != NULL && + !GetTrkSelected(trk1)) { + ep1 = GetEndPtConnectedToMe( trk1, trk ); + DisconnectTracks( trk, ep, trk1, ep1 ); + DrawEndPt( &mainD, trk1, ep1, wDrawColorBlack ); + } + } + InfoCount( inx ); +#ifdef LATER + if (tlist_da.cnt <= incrementalDrawLimit) + DrawNewTrack( trk ); +#endif + } + if (tlist_da.cnt > incrementalDrawLimit) { + DoRedraw(); + } else { + DrawSelectedTracksD( &mainD, wDrawColorBlack ); + DrawSelectedTracksD( &mapD, wDrawColorBlack ); + DrawMapBoundingBox( TRUE ); + } + wSetCursor( wCursorNormal ); + UndoEnd(); + tempSegDrawFuncs.options = 0; + InfoCount( trackCount ); +} + + +void MoveToJoin( + track_p trk0, + EPINX_T ep0, + track_p trk1, + EPINX_T ep1 ) +{ + coOrd orig; + coOrd base; + ANGLE_T angle; + + UndoStart( _("Move To Join"), "Move To Join" ); + base = GetTrkEndPos(trk0,ep0); + orig = GetTrkEndPos(trk1, ep1 ); + base.x = orig.x - base.x; + base.y = orig.y - base.y; + angle = GetTrkEndAngle(trk1,ep1); + angle -= GetTrkEndAngle(trk0,ep0); + angle += 180.0; + angle = NormalizeAngle( angle ); + GetMovedTracks( FALSE ); + MoveTracks( TRUE, TRUE, TRUE, base, orig, angle ); + UndrawNewTrack( trk0 ); + UndrawNewTrack( trk1 ); + ConnectTracks( trk0, ep0, trk1, ep1 ); + DrawNewTrack( trk0 ); + DrawNewTrack( trk1 ); +} + +static STATUS_T CmdMove( + wAction_t action, + coOrd pos ) +{ + static coOrd base; + static coOrd orig; + static int state; + + switch( action ) { + + case C_START: + if (selectedTrackCount == 0) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return C_TERMINATE; + } + if (SelectedTracksAreFrozen()) { + return C_TERMINATE; + } + InfoMessage( _("Drag to move selected tracks") ); + state = 0; + break; + case C_DOWN: + if (SelectedTracksAreFrozen()) { + return C_TERMINATE; + } + UndoStart( _("Move Tracks"), "move" ); + base = zero; + orig = pos; + GetMovedTracks(quickMove != MOVE_QUICK); + SetMoveD( TRUE, base, 0.0 ); + DrawMovedTracks(); + drawCount = 0; + state = 1; + MainRedraw(); + return C_CONTINUE; + case C_MOVE: + drawEnable = enableMoveDraw; + DrawMovedTracks(); + base.x = pos.x - orig.x; + base.y = pos.y - orig.y; + SnapPos( &base ); + SetMoveD( TRUE, base, 0.0 ); + DrawMovedTracks(); +#ifdef DRAWCOUNT + InfoMessage( " [%s %s] #%ld", FormatDistance(base.x), FormatDistance(base.y), drawCount ); +#else + InfoMessage( " [%s %s]", FormatDistance(base.x), FormatDistance(base.y) ); +#endif + drawEnable = TRUE; + MainRedraw(); + return C_CONTINUE; + case C_UP: + state = 0; + DrawMovedTracks(); + MoveTracks( quickMove==MOVE_QUICK, TRUE, FALSE, base, zero, 0.0 ); + return C_TERMINATE; + + case C_CMDMENU: + wMenuPopupShow( selectPopup1M ); + return C_CONTINUE; + + case C_REDRAW: + /* DO_REDRAW */ + if ( state == 0 ) + break; + DrawSelectedTracksD( &mainD, wDrawColorWhite ); + DrawMovedTracks(); + break; + + default: + break; + } + return C_CONTINUE; +} + + +wMenuPush_p rotateAlignMI; +int rotateAlignState = 0; + +static void RotateAlign( void ) +{ + rotateAlignState = 1; + InfoMessage( _("Click on selected object to align") ); +} + +static STATUS_T CmdRotate( + wAction_t action, + coOrd pos ) +{ + static coOrd base; + static coOrd orig; + static ANGLE_T angle; + static BOOL_T drawnAngle; + static ANGLE_T baseAngle; + static track_p trk; + ANGLE_T angle1; + coOrd pos1; + static int state; + + switch( action ) { + + case C_START: + state = 0; + if (selectedTrackCount == 0) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return C_TERMINATE; + } + if (SelectedTracksAreFrozen()) { + return C_TERMINATE; + } + InfoMessage( _("Drag to rotate selected tracks") ); + wMenuPushEnable( rotateAlignMI, TRUE ); + rotateAlignState = 0; + break; + case C_DOWN: + state = 1; + if (SelectedTracksAreFrozen()) { + return C_TERMINATE; + } + UndoStart( _("Rotate Tracks"), "rotate" ); + if ( rotateAlignState == 0 ) { + drawnAngle = FALSE; + angle = 0; + base = orig = pos; + GetMovedTracks(FALSE); + /*DrawLine( &mainD, base, orig, 0, wDrawColorBlack ); + DrawMovedTracks(FALSE, orig, angle);*/ + } else { + pos1 = pos; + onTrackInSplit = TRUE; + trk = OnTrack( &pos, TRUE, FALSE ); + onTrackInSplit = FALSE; + if ( trk == NULL ) return C_CONTINUE; + angle1 = NormalizeAngle( GetAngleAtPoint( trk, pos, NULL, NULL ) ); + if ( rotateAlignState == 1 ) { + if ( !GetTrkSelected(trk) ) { + NoticeMessage( MSG_1ST_TRACK_MUST_BE_SELECTED, _("Ok"), NULL ); + } else { + base = pos; + baseAngle = angle1; + getboundsCount = 0; + DoSelectedTracks( GetboundsDoIt ); + orig.x = (getboundsLo.x+getboundsHi.x)/2.0; + orig.y = (getboundsLo.y+getboundsHi.y)/2.0; +/*printf( "orig = [%0.3f %0.3f], baseAngle = %0.3f\n", orig.x, orig.y, baseAngle );*/ + } + } else { + if ( GetTrkSelected(trk) ) { + ErrorMessage( MSG_2ND_TRACK_MUST_BE_UNSELECTED ); + angle = 0; + } else { + angle = NormalizeAngle(angle1-baseAngle); + if ( angle > 90 && angle < 270 ) + angle = NormalizeAngle( angle + 180.0 ); + if ( NormalizeAngle( FindAngle( pos, pos1 ) - angle1 ) < 180.0 ) + angle = NormalizeAngle( angle + 180.0 ); +/*printf( "angle 1 = %0.3f\n", angle );*/ + if ( angle1 > 180.0 ) angle1 -= 180.0; + InfoMessage( _("Angle %0.3f"), angle1 ); + } + GetMovedTracks(TRUE); + SetMoveD( FALSE, orig, angle ); + DrawMovedTracks(); + } + } + MainRedraw(); + return C_CONTINUE; + case C_MOVE: + if ( rotateAlignState == 1 ) + return C_CONTINUE; + if ( rotateAlignState == 2 ) { + pos1 = pos; + onTrackInSplit = TRUE; + trk = OnTrack( &pos, TRUE, FALSE ); + onTrackInSplit = FALSE; + if ( trk == NULL ) + return C_CONTINUE; + if ( GetTrkSelected(trk) ) { + ErrorMessage( MSG_2ND_TRACK_MUST_BE_UNSELECTED ); + return C_CONTINUE; + } + DrawMovedTracks(); + angle1 = NormalizeAngle( GetAngleAtPoint( trk, pos, NULL, NULL ) ); + angle = NormalizeAngle(angle1-baseAngle); + if ( angle > 90 && angle < 270 ) + angle = NormalizeAngle( angle + 180.0 ); + if ( NormalizeAngle( FindAngle( pos, pos1 ) - angle1 ) < 180.0 ) + angle = NormalizeAngle( angle + 180.0 ); + if ( angle1 > 180.0 ) angle1 -= 180.0; + InfoMessage( _("Angle %0.3f"), angle1 ); + SetMoveD( FALSE, orig, angle ); +/*printf( "angle 2 = %0.3f\n", angle );*/ + DrawMovedTracks(); + MainRedraw(); + return C_CONTINUE; + } + if ( FindDistance( orig, pos ) > (6.0/75.0)*mainD.scale ) { + drawEnable = enableMoveDraw; + if (drawnAngle) { + DrawLine( &tempD, base, orig, 0, wDrawColorBlack ); + DrawMovedTracks(); + } else if (quickMove != MOVE_QUICK) { + DrawSelectedTracksD( &mainD, wDrawColorWhite ); + } + angle = FindAngle( orig, pos ); + if (!drawnAngle) { + baseAngle = angle; + drawnAngle = TRUE; + } + base = pos; + angle = NormalizeAngle( angle-baseAngle ); + if ( MyGetKeyState()&WKEY_CTRL ) { + angle = NormalizeAngle(floor((angle+7.5)/15.0)*15.0); + Translate( &base, orig, angle+baseAngle, FindDistance(orig,pos) ); + } + DrawLine( &tempD, base, orig, 0, wDrawColorBlack ); + SetMoveD( FALSE, orig, angle ); + DrawMovedTracks(); +#ifdef DRAWCOUNT + InfoMessage( _(" Angle %0.3f #%ld"), angle, drawCount ); +#else + InfoMessage( _(" Angle %0.3f"), angle ); +#endif + wFlush(); + drawEnable = TRUE; + } + MainRedraw(); + return C_CONTINUE; + case C_UP: + state = 0; + if ( rotateAlignState == 1 ) { + if ( trk && GetTrkSelected(trk) ) { + InfoMessage( _("Click on the 2nd Unselected object") ); + rotateAlignState = 2; + } + return C_CONTINUE; + } + if ( rotateAlignState == 2 ) { + DrawMovedTracks(); + MoveTracks( quickMove==MOVE_QUICK, FALSE, TRUE, zero, orig, angle ); + rotateAlignState = 0; + } else if (drawnAngle) { + DrawLine( &tempD, base, orig, 0, wDrawColorBlack ); + DrawMovedTracks(); + MoveTracks( quickMove==MOVE_QUICK, FALSE, TRUE, zero, orig, angle ); + } + MainRedraw(); + return C_TERMINATE; + + case C_CMDMENU: + wMenuPopupShow( selectPopup2M ); + return C_CONTINUE; + + case C_REDRAW: + /* DO_REDRAW */ + if ( state == 0 ) + break; + if ( rotateAlignState != 2 ) + DrawLine( &tempD, base, orig, 0, wDrawColorBlack ); + DrawSelectedTracksD( &mainD, wDrawColorWhite ); + DrawMovedTracks(); + break; + + } + return C_CONTINUE; +} + +static void QuickRotate( void* pangle ) +{ + ANGLE_T angle = (ANGLE_T)(long)pangle; + if ( SelectedTracksAreFrozen() ) + return; + wDrawDelayUpdate( mainD.d, TRUE ); + GetMovedTracks(FALSE); + DrawSelectedTracksD( &mainD, wDrawColorWhite ); + UndoStart( _("Rotate Tracks"), "Rotate Tracks" ); + MoveTracks( quickMove==MOVE_QUICK, FALSE, TRUE, zero, cmdMenuPos, angle ); + wDrawDelayUpdate( mainD.d, FALSE ); +} + + +static wMenu_p moveDescM; +static wMenuToggle_p moveDescMI; +static track_p moveDescTrk; +static void ChangeDescFlag( wBool_t set, void * mode ) +{ + wDrawDelayUpdate( mainD.d, TRUE ); + UndoStart( _("Toggle Label"), "Modedesc( T%d )", GetTrkIndex(moveDescTrk) ); + UndoModify( moveDescTrk ); + UndrawNewTrack( moveDescTrk ); + if ( ( GetTrkBits( moveDescTrk ) & TB_HIDEDESC ) == 0 ) + SetTrkBits( moveDescTrk, TB_HIDEDESC ); + else + ClrTrkBits( moveDescTrk, TB_HIDEDESC ); + DrawNewTrack( moveDescTrk ); + wDrawDelayUpdate( mainD.d, FALSE ); +} + +STATUS_T CmdMoveDescription( + wAction_t action, + coOrd pos ) +{ + static track_p trk; + static EPINX_T ep; + track_p trk1; + EPINX_T ep1; + DIST_T d, dd; + static int mode; + + switch (action) { + case C_START: + if ( labelWhen < 2 || mainD.scale > labelScale || + (labelEnable&(LABELENABLE_TRKDESC|LABELENABLE_LENGTHS|LABELENABLE_ENDPT_ELEV))==0 ) { + ErrorMessage( MSG_DESC_NOT_VISIBLE ); + return C_TERMINATE; + } + InfoMessage( _("Select and drag a description") ); + break; + case C_DOWN: + if ( labelWhen < 2 || mainD.scale > labelScale ) + return C_TERMINATE; + trk = NULL; + dd = 10000; + trk1 = NULL; + while ( TrackIterate( &trk1 ) ) { + if ( !GetLayerVisible(GetTrkLayer(trk1)) ) + continue; + if ( (!GetTrkVisible(trk1)) && drawTunnel==0 ) + continue; + for ( ep1=0; ep1<GetTrkEndPtCnt(trk1); ep1++ ) { + d = EndPtDescriptionDistance( pos, trk1, ep1 ); + if ( d < dd ) { + dd = d; + trk = trk1; + ep = ep1; + mode = 0; + } + } + if ( !QueryTrack( trk1, Q_HAS_DESC ) ) + continue; + if ( ( GetTrkBits( trk1 ) & TB_HIDEDESC ) != 0 ) + continue; + d = CompoundDescriptionDistance( pos, trk1 ); + if ( d < dd ) { + dd = d; + trk = trk1; + ep = -1; + mode = 1; + } + d = CurveDescriptionDistance( pos, trk1 ); + if ( d < dd ) { + dd = d; + trk = trk1; + ep = -1; + mode = 2; + } + } + if (trk != NULL) { + UndoStart( _("Move Label"), "Modedesc( T%d )", GetTrkIndex(trk) ); + UndoModify( trk ); + } + case C_MOVE: + case C_UP: + case C_REDRAW: + if ( labelWhen < 2 || mainD.scale > labelScale ) + return C_TERMINATE; + if (trk != NULL) { + switch (mode) { + case 0: + return EndPtDescriptionMove( trk, ep, action, pos ); + case 1: + return CompoundDescriptionMove( trk, action, pos ); + case 2: + return CurveDescriptionMove( trk, action, pos ); + } + } + + case C_CMDMENU: + moveDescTrk = OnTrack( &pos, TRUE, FALSE ); + if ( moveDescTrk == NULL ) break; + if ( ! QueryTrack( moveDescTrk, Q_HAS_DESC ) ) break; + if ( moveDescM == NULL ) { + moveDescM = MenuRegister( "Move Desc Toggle" ); + moveDescMI = wMenuToggleCreate( moveDescM, "", _("Show Description"), 0, TRUE, ChangeDescFlag, NULL ); + } + wMenuToggleSet( moveDescMI, ( GetTrkBits( moveDescTrk ) & TB_HIDEDESC ) == 0 ); + wMenuPopupShow( moveDescM ); + break; + + default: + ; + } + + return C_CONTINUE; +} + + +static void FlipTracks( + coOrd orig, + ANGLE_T angle ) +{ + track_p trk, trk1; + EPINX_T ep, ep1; + + wSetCursor( wCursorWait ); + /*UndoStart( "Move/Rotate Tracks", "move/rotate" );*/ + if (selectedTrackCount <= incrementalDrawLimit) { + DrawMapBoundingBox( FALSE ); + wDrawDelayUpdate( mainD.d, TRUE ); + wDrawDelayUpdate( mapD.d, TRUE ); + } + for ( trk=NULL; TrackIterate(&trk); ) { + if ( !GetTrkSelected(trk) ) + continue; + UndoModify( trk ); + if (selectedTrackCount <= incrementalDrawLimit) { + DrawTrack( trk, &mainD, wDrawColorWhite ); + DrawTrack( trk, &mapD, wDrawColorWhite ); + } + for (ep=0; ep<GetTrkEndPtCnt(trk); ep++) { + if ((trk1 = GetTrkEndTrk(trk,ep)) != NULL && + !GetTrkSelected(trk1)) { + ep1 = GetEndPtConnectedToMe( trk1, trk ); + DisconnectTracks( trk, ep, trk1, ep1 ); + DrawEndPt( &mainD, trk1, ep1, wDrawColorBlack ); + } + } + FlipTrack( trk, orig, angle ); + if (selectedTrackCount <= incrementalDrawLimit) { + DrawTrack( trk, &mainD, wDrawColorBlack ); + DrawTrack( trk, &mapD, wDrawColorBlack ); + } + } + if (selectedTrackCount > incrementalDrawLimit) { + DoRedraw(); + } else { + wDrawDelayUpdate( mainD.d, FALSE ); + wDrawDelayUpdate( mapD.d, FALSE ); + DrawMapBoundingBox( TRUE ); + } + wSetCursor( wCursorNormal ); + UndoEnd(); + InfoCount( trackCount ); + MainRedraw(); +} + + +static STATUS_T CmdFlip( + wAction_t action, + coOrd pos ) +{ + static coOrd pos0; + static coOrd pos1; + static int state; + + switch( action ) { + + case C_START: + state = 0; + if (selectedTrackCount == 0) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return C_TERMINATE; + } + if (SelectedTracksAreFrozen()) + return C_TERMINATE; + InfoMessage( _("Drag to mark mirror line") ); + break; + case C_DOWN: + state = 1; + if (SelectedTracksAreFrozen()) { + return C_TERMINATE; + } + pos0 = pos1 = pos; + DrawLine( &tempD, pos0, pos1, 0, wDrawColorBlack ); + MainRedraw(); + return C_CONTINUE; + case C_MOVE: + DrawLine( &tempD, pos0, pos1, 0, wDrawColorBlack ); + pos1 = pos; + DrawLine( &tempD, pos0, pos1, 0, wDrawColorBlack ); + InfoMessage( _("Angle %0.2f"), FindAngle( pos0, pos1 ) ); + MainRedraw(); + return C_CONTINUE; + case C_UP: + DrawLine( &tempD, pos0, pos1, 0, wDrawColorBlack ); + UndoStart( _("Flip Tracks"), "flip" ); + FlipTracks( pos0, FindAngle( pos0, pos1 ) ); + state = 0; + MainRedraw(); + return C_TERMINATE; + +#ifdef LATER + case C_CANCEL: +#endif + case C_REDRAW: + if ( state == 0 ) + return C_CONTINUE; + DrawLine( &tempD, pos0, pos1, 0, wDrawColorBlack ); + return C_CONTINUE; + + default: + break; + } + return C_CONTINUE; +} + +static STATUS_T SelectArea( + wAction_t action, + coOrd pos ) +{ + static coOrd pos0; + static int state; + static coOrd base, size, lo, hi; + int cnt; + + track_p trk; + + switch (action) { + + case C_START: + state = 0; + return C_CONTINUE; + + case C_DOWN: + case C_RDOWN: + pos0 = pos; + return C_CONTINUE; + + case C_MOVE: + case C_RMOVE: + if (state == 0) { + state = 1; + } else { + DrawHilight( &mainD, base, size ); + } + base = pos0; + size.x = pos.x - pos0.x; + if (size.x < 0) { + size.x = - size.x; + base.x = pos.x; + } + size.y = pos.y - pos0.y; + if (size.y < 0) { + size.y = - size.y; + base.y = pos.y; + } + DrawHilight( &mainD, base, size ); + return C_CONTINUE; + + case C_UP: + case C_RUP: + if (state == 1) { + state = 0; + DrawHilight( &mainD, base, size ); + cnt = 0; + trk = NULL; + while ( TrackIterate( &trk ) ) { + GetBoundingBox( trk, &hi, &lo ); + if (GetLayerVisible( GetTrkLayer( trk ) ) && + lo.x >= base.x && hi.x <= base.x+size.x && + lo.y >= base.y && hi.y <= base.y+size.y) { + if ( (GetTrkSelected( trk )==0) == (action==C_UP) ) { + cnt++; + } + } + } + trk = NULL; + while ( TrackIterate( &trk ) ) { + GetBoundingBox( trk, &hi, &lo ); + if (GetLayerVisible( GetTrkLayer( trk ) ) && + lo.x >= base.x && hi.x <= base.x+size.x && + lo.y >= base.y && hi.y <= base.y+size.y) { + if ( (GetTrkSelected( trk )==0) == (action==C_UP) ) { + if (cnt > incrementalDrawLimit) { + selectedTrackCount += (action==C_UP?1:-1); + if (action==C_UP) + SetTrkBits( trk, TB_SELECTED ); + else + ClrTrkBits( trk, TB_SELECTED ); + } else { + SelectOneTrack( trk, action==C_UP ); + } + } + } + } + SelectedTrackCountChange(); + if (cnt > incrementalDrawLimit) + MainRedraw(); + } + return C_CONTINUE; + + case C_CANCEL: + if (state == 1) { + DrawHilight( &mainD, base, size ); + state = 0; + } + break; + + case C_REDRAW: + if (state == 0) + break; + DrawHilight( &mainD, base, size ); + break; + + } + return C_CONTINUE; +} + + +static STATUS_T SelectTrack( + coOrd pos ) +{ + track_p trk; + char msg[STR_SIZE]; + + if ((trk = OnTrack( &pos, TRUE, FALSE )) == NULL) { + return C_CONTINUE; + } + DescribeTrack( trk, msg, sizeof msg ); + InfoMessage( msg ); + if (MyGetKeyState() & WKEY_SHIFT) { + SelectConnectedTracks( trk ); + } else { + SelectOneTrack( trk, !GetTrkSelected(trk) ); + } + return C_CONTINUE; +} + + +static STATUS_T CmdSelect( + wAction_t action, + coOrd pos ) +{ + static enum { AREA, MOVE, MOVEDESC, NONE } mode; + static BOOL_T doingMove = TRUE; + STATUS_T rc=C_CONTINUE; + + if ( (action == C_DOWN || action == C_RDOWN) ) { + mode = AREA; + if (MyGetKeyState() & WKEY_SHIFT) { + mode = MOVE; + } else if (MyGetKeyState() & WKEY_CTRL) { + mode = MOVEDESC; + } + } + + switch (action) { + case C_START: + InfoMessage( _("Select tracks") ); +#ifdef LATER + if ((!importMove) && selectedTrackCount > 0) { + SetAllTrackSelect( FALSE ); + } +#endif + importMove = FALSE; + SelectArea( action, pos ); + wMenuPushEnable( rotateAlignMI, FALSE ); + break; + + case C_DOWN: + case C_UP: + case C_MOVE: + case C_RDOWN: + case C_RUP: + case C_RMOVE: + case C_REDRAW: + switch (mode) { + case MOVE: + if (SelectedTracksAreFrozen()) { + rc = C_TERMINATE; + mode = NONE; + } else if (action >= C_DOWN && action <= C_UP) { + rc = CmdMove( action, pos ); + doingMove = TRUE; + } else if (action >= C_RDOWN && action <= C_RUP) { + rc = CmdRotate( action-C_RDOWN+C_DOWN, pos ); + doingMove = FALSE; + } else if (action == C_REDRAW) { + if (doingMove) { + rc = CmdMove( C_REDRAW, pos ); + } else { + rc = CmdRotate( C_REDRAW, pos ); + } + } + break; + case MOVEDESC: + rc = CmdMoveDescription( action, pos ); + break; + case AREA: + rc = SelectArea( action, pos ); + break; + case NONE: + break; + } + if (action == C_UP || action == C_RUP) + mode = AREA; + return rc; + + case wActionMove: + break; + + case C_LCLICK: + switch (mode) { + case MOVE: + case MOVEDESC: + break; + case AREA: + case NONE: + return SelectTrack( pos ); + } + mode = AREA; + break; + + case C_CMDMENU: + if (selectedTrackCount <= 0) { + wMenuPopupShow( selectPopup1M ); + } else { + wMenuPopupShow( selectPopup2M ); + } + return C_CONTINUE; + } + return C_CONTINUE; +} + + +#include "bitmaps/select.xpm" +#include "bitmaps/delete.xpm" +#include "bitmaps/tunnel.xpm" +#include "bitmaps/move.xpm" +#include "bitmaps/rotate.xpm" +#include "bitmaps/flip.xpm" +#include "bitmaps/movedesc.xpm" + + +static void SetMoveMode( char * line ) +{ + long tmp = atol( line ); + moveMode = tmp & 0x0F; + if (moveMode < 0 || moveMode > MAXMOVEMODE) + moveMode = MAXMOVEMODE; + enableMoveDraw = ((tmp&0x10) == 0); +} + + +EXPORT void InitCmdSelect( wMenu_p menu ) +{ + selectCmdInx = AddMenuButton( menu, CmdSelect, "cmdSelect", _("Select"), wIconCreatePixMap(select_xpm), + LEVEL0, IC_CANCEL|IC_POPUP|IC_LCLICK|IC_CMDMENU, ACCL_SELECT, NULL ); + endpt_bm = wDrawBitMapCreate( mainD.d, bmendpt_width, bmendpt_width, 7, 7, bmendpt_bits ); + angle_bm[0] = wDrawBitMapCreate( mainD.d, bma90_width, bma90_width, 7, 7, bma90_bits ); + angle_bm[1] = wDrawBitMapCreate( mainD.d, bma135_width, bma135_width, 7, 7, bma135_bits ); + angle_bm[2] = wDrawBitMapCreate( mainD.d, bma0_width, bma0_width, 7, 7, bma0_bits ); + angle_bm[3] = wDrawBitMapCreate( mainD.d, bma45_width, bma45_width, 7, 7, bma45_bits ); + AddPlaybackProc( SETMOVEMODE, (playbackProc_p)SetMoveMode, NULL ); + wPrefGetInteger( "draw", "movemode", &moveMode, MAXMOVEMODE ); + if (moveMode > MAXMOVEMODE || moveMode < 0) + moveMode = MAXMOVEMODE; + + selectPopup1M = MenuRegister( "Move Draw Mode" ); + quickMove1M[0] = wMenuToggleCreate( selectPopup1M, "", _("Normal"), 0, quickMove==0, ChangeQuickMove, (void *) 0 ); + quickMove1M[1] = wMenuToggleCreate( selectPopup1M, "", _("Simple"), 0, quickMove==1, ChangeQuickMove, (void *) 1 ); + quickMove1M[2] = wMenuToggleCreate( selectPopup1M, "", _("End Points"), 0, quickMove==2, ChangeQuickMove, (void *) 2 ); + selectPopup2M = MenuRegister( "Move Draw Mode " ); + quickMove2M[0] = wMenuToggleCreate( selectPopup2M, "", _("Normal"), 0, quickMove==0, ChangeQuickMove, (void *) 0 ); + quickMove2M[1] = wMenuToggleCreate( selectPopup2M, "", _("Simple"), 0, quickMove==1, ChangeQuickMove, (void *) 1 ); + quickMove2M[2] = wMenuToggleCreate( selectPopup2M, "", _("End Points"), 0, quickMove==2, ChangeQuickMove, (void *) 2 ); + wMenuSeparatorCreate( selectPopup2M ); + AddRotateMenu( selectPopup2M, QuickRotate ); + rotateAlignMI = wMenuPushCreate( selectPopup2M, "", _("Align"), 0, (wMenuCallBack_p)RotateAlign, NULL ); + ParamRegister( &rescalePG ); +} + + +EXPORT void InitCmdDelete( void ) +{ + wIcon_p icon; + icon = wIconCreatePixMap( delete_xpm ); + AddToolbarButton( "cmdDelete", icon, IC_SELECTED, (wButtonCallBack_p)SelectDelete, 0 ); +#ifdef WINDOWS + wAttachAccelKey( wAccelKey_Del, 0, (wAccelKeyCallBack_p)SelectDelete, NULL ); +#endif +} + +EXPORT void InitCmdTunnel( void ) +{ + wIcon_p icon; + icon = wIconCreatePixMap( tunnel_xpm ); + AddToolbarButton( "cmdTunnel", icon, IC_SELECTED|IC_POPUP, (addButtonCallBack_t)SelectTunnel, NULL ); +#ifdef LATER + tunnelCmdInx = AddButton( "cmdTunnel", _("Tunnel"), + (addButtonCallBack_t)SelectTunnel, NULL, IC_SELECTED|IC_POPUP, NULL, LEVEL0_50, ACCL_TUNNEL, + (wControl_p)wButtonCreate(mainW, 0, 0, "cmdTunnel", (char*)bm_p, BO_ICON, 0, (wButtonCallBack_p)SelectTunnel, 0 ) ); +#endif +} + + +EXPORT void InitCmdMoveDescription( wMenu_p menu ) +{ + AddMenuButton( menu, CmdMoveDescription, "cmdMoveLabel", _("Move Description"), wIconCreatePixMap(movedesc_xpm), + LEVEL0, IC_STICKY|IC_POPUP|IC_CMDMENU, ACCL_MOVEDESC, NULL ); +} + + +EXPORT void InitCmdMove( wMenu_p menu ) +{ + moveCmdInx = AddMenuButton( menu, CmdMove, "cmdMove", _("Move"), wIconCreatePixMap(move_xpm), + LEVEL0, IC_STICKY|IC_SELECTED|IC_CMDMENU, ACCL_MOVE, NULL ); + rotateCmdInx = AddMenuButton( menu, CmdRotate, "cmdRotate", _("Rotate"), wIconCreatePixMap(rotate_xpm), + LEVEL0, IC_STICKY|IC_SELECTED|IC_CMDMENU, ACCL_ROTATE, NULL ); + /*flipCmdInx =*/ AddMenuButton( menu, CmdFlip, "cmdFlip", _("Flip"), wIconCreatePixMap(flip_xpm), + LEVEL0, IC_STICKY|IC_SELECTED|IC_CMDMENU, ACCL_FLIP, NULL ); +} diff --git a/app/bin/cselect.h b/app/bin/cselect.h new file mode 100644 index 0000000..890e53b --- /dev/null +++ b/app/bin/cselect.h @@ -0,0 +1,48 @@ +#ifndef CSELECT_H +#define CSELECT_H + +/* 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. + */ + +wIndex_t selectCmdInx; +wIndex_t moveCmdInx; +wIndex_t rotateCmdInx; +long quickMove; +BOOL_T importMove; +extern int incrementalDrawLimit; +extern long selectedTrackCount; + +void InvertTrackSelect( void * ); +void OrphanedTrackSelect( void * ); +void SetAllTrackSelect( BOOL_T ); +void SelectTunnel( void ); +void SelectRecount( void ); +void SelectTrackWidth( void* ); +void SelectDelete( void ); +void MoveToJoin( track_p, EPINX_T, track_p, EPINX_T ); +void MoveSelectedTracksToCurrentLayer( void ); +void SelectCurrentLayer( void ); +void ClearElevations( void ); +void AddElevations( DIST_T ); +void DoRefreshCompound( void ); +void WriteSelectedTracksToTempSegs( void ); +void DoRescale( void ); +STATUS_T CmdMoveDescription( wAction_t, coOrd ); +void UpdateQuickMove( void * ); + +#endif diff --git a/app/bin/csnap.c b/app/bin/csnap.c new file mode 100644 index 0000000..1d16136 --- /dev/null +++ b/app/bin/csnap.c @@ -0,0 +1,820 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/csnap.c,v 1.7 2008-06-03 15:43:58 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 "track.h" +#include "i18n.h" + + +/***************************************************************************** + * + * Draw Snap Grid + * + */ + +EXPORT long minGridSpacing = 3; + +#define CROSSTICK +#ifdef CROSSTICK +#include "bitmaps/cross0.xbm" +static wDrawBitMap_p cross0_bm; +#endif + +#include "bitmaps/bigdot.xbm" +static wDrawBitMap_p bigdot_bm; + +#define DEFAULTGRIDSPACING (1.0) + +EXPORT void MapGrid( + coOrd orig, + coOrd size, + ANGLE_T angle, + coOrd gridOrig, + ANGLE_T gridAngle, + POS_T Xspacing, + POS_T Yspacing, + int * x0, + int * x1, + int * y0, + int * y1 ) +{ + coOrd p[4], hi, lo; + int i; + + p[0] = p[1] = p[2] = p[3] = orig; + p[1].x += size.x; + p[2].x += size.x; + p[2].y += size.y; + p[3].y += size.y; + for (i=1; i<4; i++) { + Rotate( &p[i], orig, angle ); + } + for (i=0; i<4; i++) { + p[i].x -= gridOrig.x; + p[i].y -= gridOrig.y; + Rotate( &p[i], zero, -gridAngle ); + } + hi = lo = p[0]; + for (i=1; i<4; i++) { + if (hi.x < p[i].x) + hi.x = p[i].x; + if (hi.y < p[i].y) + hi.y = p[i].y; + if (lo.x > p[i].x) + lo.x = p[i].x; + if (lo.y > p[i].y) + lo.y = p[i].y; + } + *x0 = (int)floor( lo.x / Xspacing ); + *y0 = (int)floor( lo.y / Yspacing ); + *x1 = (int)ceil( hi.x / Xspacing ); + *y1 = (int)ceil( hi.y / Yspacing ); +} + + +static DIST_T Gdx, Gdy, Ddx, Ddy; +static coOrd GDorig; +static wPos_t lborder, bborder; + +void static DrawGridPoint( + drawCmd_p D, + wDrawColor Color, + coOrd orig, + coOrd * size, + DIST_T dpi, + coOrd p0, + BOOL_T bigdot ) +{ + wPos_t x0, y0; + POS_T x; + x = (p0.x*Gdx + p0.y*Gdy) + orig.x; + p0.y = (p0.y*Gdx - p0.x*Gdy) + orig.y; + p0.x = x; + if (size && + ( p0.x < 0.0 || p0.x > size->x || + p0.y < 0.0 || p0.y > size->y ) ) + return; + p0.x -= D->orig.x; + p0.y -= D->orig.y; + x = (p0.x*Ddx + p0.y*Ddy); + p0.y = (p0.y*Ddx - p0.x*Ddy); + p0.x = x; + if ( p0.x < 0.0 || p0.x > D->size.x || + p0.y < 0.0 || p0.y > D->size.y ) + return; + x0 = (wPos_t)(p0.x*dpi+0.5) + lborder; + y0 = (wPos_t)(p0.y*dpi+0.5) + bborder; + if ( bigdot ) + wDrawBitMap( D->d, bigdot_bm, x0, y0, Color, (wDrawOpts)D->funcs->options ); + else + wDrawPoint( D->d, x0, y0, Color, (wDrawOpts)D->funcs->options ); +} + + +static void DrawGridLine( + drawCmd_p D, + wDrawColor Color, + coOrd orig, + coOrd * size, + DIST_T dpi, + BOOL_T clip, + coOrd p0, + coOrd p1 ) +{ + wPos_t x0, y0, x1, y1; + POS_T x; + x = (p0.x*Gdx + p0.y*Gdy) + orig.x; + p0.y = (p0.y*Gdx - p0.x*Gdy) + orig.y; + p0.x = x; + x = (p1.x*Gdx + p1.y*Gdy) + orig.x; + p1.y = (p1.y*Gdx - p1.x*Gdy) + orig.y; + p1.x = x; + if (size && clip && !ClipLine( &p0, &p1, zero, 0.0, *size )) + return; + p0.x -= D->orig.x; + p0.y -= D->orig.y; + p1.x -= D->orig.x; + p1.y -= D->orig.y; + x = (p0.x*Ddx + p0.y*Ddy); + p0.y = (p0.y*Ddx - p0.x*Ddy); + p0.x = x; + x = (p1.x*Ddx + p1.y*Ddy); + p1.y = (p1.y*Ddx - p1.x*Ddy); + p1.x = x; + if (clip && !ClipLine( &p0, &p1, zero, 0.0, D->size )) + return; + x0 = (wPos_t)(p0.x*dpi+0.5) + lborder; + y0 = (wPos_t)(p0.y*dpi+0.5) + bborder; + x1 = (wPos_t)(p1.x*dpi+0.5) + lborder; + y1 = (wPos_t)(p1.y*dpi+0.5) + bborder; + wDrawLine( D->d, x0, y0, x1, y1, 0, wDrawLineSolid, Color, (wDrawOpts)D->funcs->options ); +} + + +#ifdef WINDOWS +#define WONE (1) +#else +#define WONE (0) +#endif + +EXPORT void DrawGrid( + drawCmd_p D, + coOrd * size, + POS_T hMajSpacing, + POS_T vMajSpacing, + long Hdivision, + long Vdivision, + coOrd Gorig, + ANGLE_T Gangle, + wDrawColor Color, + BOOL_T clip ) +{ + int hMaj, hMajCnt0, hMajCnt1, vMaj, vMajCnt0, vMajCnt1; + coOrd p0, p1; + DIST_T dpi; + int hMin, hMinCnt1, vMin, vMinCnt1; + DIST_T hMinSpacing=0, vMinSpacing=0; + long f; + POS_T hMajSpacing_dpi, vMajSpacing_dpi; + BOOL_T bigdot; + + if (hMajSpacing <= 0 && vMajSpacing <= 0) + return; + +#ifdef CROSSTICK + if (!cross0_bm) + cross0_bm = wDrawBitMapCreate( mainD.d, cross0_width, cross0_height, 2, 2, cross0_bits ); +#endif + if (!bigdot_bm) + bigdot_bm = wDrawBitMapCreate( mainD.d, bigdot_width, bigdot_height, 1, 1, bigdot_bits ); + + wSetCursor( wCursorWait ); + dpi = D->dpi/D->scale; + Gdx = cos(D2R(Gangle)); + Gdy = sin(D2R(Gangle)); + Ddx = cos(D2R(-D->angle)); + Ddy = sin(D2R(-D->angle)); + if (D->options&DC_TICKS) { + lborder = LBORDER; + bborder = BBORDER; + } else { + lborder = bborder = 0; + } + GDorig.x = Gorig.x-D->orig.x; + GDorig.y = Gorig.y-D->orig.y; + hMajSpacing_dpi = hMajSpacing*dpi; + vMajSpacing_dpi = vMajSpacing*dpi; + + MapGrid( D->orig, D->size, D->angle, Gorig, Gangle, + (hMajSpacing>0?hMajSpacing:vMajSpacing), + (vMajSpacing>0?vMajSpacing:hMajSpacing), + &hMajCnt0, &hMajCnt1, &vMajCnt0, &vMajCnt1 ); + + hMinCnt1 = vMinCnt1 = 0; + + if (hMajSpacing_dpi >= minGridSpacing) { + p0.y = vMajCnt0*(vMajSpacing>0?vMajSpacing:hMajSpacing); + p1.y = vMajCnt1*(vMajSpacing>0?vMajSpacing:hMajSpacing); + p0.x = p1.x = hMajCnt0*hMajSpacing; + for ( hMaj=hMajCnt0; hMaj<hMajCnt1; hMaj++ ) { + p0.x += hMajSpacing; + p1.x += hMajSpacing; + DrawGridLine( D, Color, Gorig, size, dpi, clip, p0, p1 ); + } + if ( Hdivision > 0 ) { + hMinSpacing = hMajSpacing/Hdivision; + if (hMinSpacing*dpi > minGridSpacing) + hMinCnt1 = (int)Hdivision; + } + } + + if (vMajSpacing_dpi >= minGridSpacing) { + p0.x = hMajCnt0*(hMajSpacing>0?hMajSpacing:vMajSpacing); + p1.x = hMajCnt1*(hMajSpacing>0?hMajSpacing:vMajSpacing); + p0.y = p1.y = vMajCnt0*vMajSpacing; + for ( vMaj=vMajCnt0; vMaj<vMajCnt1; vMaj++ ) { + p0.y += vMajSpacing; + p1.y += vMajSpacing; + DrawGridLine( D, Color, Gorig, size, dpi, clip, p0, p1 ); + } + if ( Vdivision > 0 ) { + vMinSpacing = vMajSpacing/Vdivision; + if (vMinSpacing*dpi > minGridSpacing) + vMinCnt1 = (int)Vdivision; + } + } + + if (hMinCnt1 <= 0 && vMinCnt1 <= 0) + goto done; + + if (hMajSpacing <= 0) { + hMinCnt1 = vMinCnt1+1; + hMinSpacing = vMinSpacing; + hMajSpacing = vMajSpacing; + } else if (hMajSpacing_dpi < minGridSpacing) { + hMinCnt1 = 1; + hMinSpacing = 0; + f = (long)ceil(minGridSpacing/hMajSpacing); + hMajSpacing *= f; + hMajCnt0 = (int)(hMajCnt0>=0?ceil(hMajCnt0/f):floor(hMajCnt0/f)); + hMajCnt1 = (int)(hMajCnt1>=0?ceil(hMajCnt1/f):floor(hMajCnt1/f)); + } else if (Hdivision <= 0) { + hMinCnt1 = (int)(hMajSpacing/vMinSpacing); + if (hMinCnt1 <= 0) { + goto done; + } + hMinSpacing = hMajSpacing/hMinCnt1; + } else if (hMinSpacing*dpi < minGridSpacing) { + f = (long)ceil(minGridSpacing/hMinSpacing); + hMinCnt1 = (int)(Hdivision/f); + hMinSpacing *= f; + } + + if (vMajSpacing <= 0) { + vMinCnt1 = hMinCnt1+1; + vMinSpacing = hMinSpacing; + vMajSpacing = hMajSpacing; + } else if (vMajSpacing_dpi < minGridSpacing) { + vMinCnt1 = 1; + vMinSpacing = 0; + f = (long)ceil(minGridSpacing/vMajSpacing); + vMajSpacing *= f; + vMajCnt0 = (int)(vMajCnt0>=0?ceil(vMajCnt0/f):floor(vMajCnt0/f)); + vMajCnt1 = (int)(vMajCnt1>=0?ceil(vMajCnt1/f):floor(vMajCnt1/f)); + } else if (Vdivision <= 0) { + vMinCnt1 = (int)(vMajSpacing/hMinSpacing); + if (vMinCnt1 <= 0) { + goto done; + } + vMinSpacing = vMajSpacing/vMinCnt1; + } else if (vMinSpacing*dpi < minGridSpacing) { + f = (long)ceil(minGridSpacing/vMinSpacing); + vMinCnt1 = (int)(Vdivision/f); + vMinSpacing *= f; + } + + bigdot = ( hMinSpacing*dpi > 10 && vMinSpacing*dpi > 10 ); + for ( hMaj=hMajCnt0; hMaj<hMajCnt1; hMaj++ ) { + for ( vMaj=vMajCnt0; vMaj<vMajCnt1; vMaj++ ) { + for ( hMin=1; hMin<hMinCnt1; hMin++ ) { + for ( vMin=1; vMin<vMinCnt1; vMin++ ) { + p0.x = hMaj*hMajSpacing + hMin*hMinSpacing; + p0.y = vMaj*vMajSpacing + vMin*vMinSpacing; + DrawGridPoint( D, Color, Gorig, size, dpi, p0, bigdot ); + } + } + } + } + + +done: + wSetCursor( wCursorNormal ); +} + + + +static void DrawBigCross( coOrd pos, ANGLE_T angle ) +{ + coOrd p0, p1; + DIST_T d; + if (angleSystem!=ANGLE_POLAR) + angle += 90.0; + d = max( mainD.size.x, mainD.size.y ); + Translate( &p0, pos, angle, d ); + Translate( &p1, pos, angle+180, d ); + if (ClipLine( &p0, &p1, mainD.orig, 0.0, mainD.size )) { + DrawLine( &tempD, pos, p0, 0, crossMajorColor ); + DrawLine( &tempD, pos, p1, 0, crossMinorColor ); + } + Translate( &p0, pos, angle+90, d ); + Translate( &p1, pos, angle+270, d ); + if (ClipLine( &p0, &p1, mainD.orig, 0.0, mainD.size )) { + DrawLine( &tempD, p0, p1, 0, crossMinorColor ); + } +} + + +EXPORT STATUS_T GridAction( + wAction_t action, + coOrd pos, + coOrd *orig, + DIST_T *angle ) +{ + + static coOrd pos0, pos1; + static ANGLE_T newAngle, oldAngle; + + switch (action) { + case C_DOWN: + pos1 = pos; + DrawBigCross( pos1, *angle ); + return C_CONTINUE; + + case C_MOVE: + DrawBigCross( pos1, *angle ); + *orig = pos1 = pos; + DrawBigCross( pos1, *angle ); + return C_CONTINUE; + + case C_UP: + DrawBigCross( pos1, *angle ); + *orig = pos1; + return C_CONTINUE; + + case C_RDOWN: + pos0 = pos1 = pos; + oldAngle = newAngle = *angle; + DrawBigCross( pos0, newAngle ); + return C_CONTINUE; + + case C_RMOVE: + if ( FindDistance(pos0, pos) > 0.1*mainD.scale ) { + DrawBigCross( pos0, newAngle ); + pos1 = pos; + newAngle = FindAngle( pos0, pos1 ); + if (angleSystem!=ANGLE_POLAR) + newAngle = newAngle-90.0; + newAngle = NormalizeAngle( floor( newAngle*10.0 ) / 10.0 ); + *angle = newAngle; + DrawBigCross( pos0, newAngle ); + } + return C_CONTINUE; + + case C_RUP: + DrawBigCross( pos0, newAngle ); + Rotate( orig, pos0, newAngle-oldAngle ); + *orig = pos0; + *angle = newAngle; + return C_CONTINUE; + } + return C_CONTINUE; +} + +/***************************************************************************** + * + * Snap Grid Command + * + */ + +EXPORT wDrawColor snapGridColor; + +typedef struct { + DIST_T Spacing; + long Division; + long Enable; + } gridData; +typedef struct { + gridData Horz; + gridData Vert; + coOrd Orig; + ANGLE_T Angle; + long Show; + } gridHVData; + +static gridHVData grid = { { 1.0, 0, 1 }, + { 1.0, 0, 1 } }; + +EXPORT void SnapPos( coOrd * pos ) +{ + coOrd p; + DIST_T spacing; + if ( grid.Vert.Enable == FALSE && grid.Horz.Enable == FALSE ) + return; + p = *pos; + p.x -= grid.Orig.x; + p.y -= grid.Orig.y; + Rotate( &p, zero, -grid.Angle ); + if ( grid.Horz.Enable ) { + if ( grid.Horz.Division > 0 ) + spacing = grid.Horz.Spacing / grid.Horz.Division; + else + spacing = grid.Horz.Spacing; + if (spacing > 0.001) + p.x = floor(p.x/spacing+0.5) * spacing; + } + if ( grid.Vert.Enable ) { + if ( grid.Vert.Division > 0 ) + spacing = grid.Vert.Spacing / grid.Vert.Division; + else + spacing = grid.Vert.Spacing; + if (spacing > 0.001) + p.y = floor(p.y/spacing+0.5) * spacing; + } + REORIGIN1( p, grid.Angle, grid.Orig ); + *pos = p; + InfoPos( p ); +} + + +static void DrawASnapGrid( gridHVData * gridP, drawCmd_p d, coOrd size, BOOL_T drawDivisions ) +{ + if (gridP->Horz.Spacing <= 0.0 && gridP->Vert.Spacing <= 0.0) + return; + if (gridP->Show == FALSE) + return; + DrawGrid( d, &size, + gridP->Horz.Spacing, gridP->Vert.Spacing, + drawDivisions?gridP->Horz.Division:0, + drawDivisions?gridP->Vert.Division:0, + gridP->Orig, gridP->Angle, snapGridColor, TRUE ); +} + + +EXPORT void DrawSnapGrid( drawCmd_p d, coOrd size, BOOL_T drawDivisions ) +{ + DrawASnapGrid( &grid, d, size, drawDivisions ); +} + + +EXPORT BOOL_T GridIsVisible( void ) +{ + return (BOOL_T)grid.Show; +} + +/***************************************************************************** + * + * Snap Grid Dialog + * + */ + +static wWin_p gridW; +static wMenu_p snapGridPopupM; +static wButton_p snapGridEnable_b; +static wButton_p snapGridShow_b; +EXPORT wMenuToggle_p snapGridEnableMI; +EXPORT wMenuToggle_p snapGridShowMI; + +static gridHVData oldGrid; + +#define CHK_HENABLE (1<<0) +#define CHK_VENABLE (1<<1) +#define CHK_SHOW (1<<2) + +static paramFloatRange_t r0_999999 = { 0.0, 999999.0, 60 }; +static paramIntegerRange_t i0_1000 = { 0, 1000, 30 }; +static paramFloatRange_t r_1000_1000 = { -1000.0, 1000.0, 80 }; +static paramFloatRange_t r0_360 = { 0.0, 360.0, 80 }; +static char *gridLabels[] = { "", NULL }; +static paramData_t gridPLs[] = { + { PD_MESSAGE, N_("Horz"), NULL, 0, (void*)60 }, +#define I_HORZSPACING (1) + { PD_FLOAT, &grid.Horz.Spacing, "horzspacing", PDO_DIM, &r0_999999, N_("Spacing") }, +#define I_HORZDIVISION (2) + { PD_LONG, &grid.Horz.Division, "horzdivision", 0, &i0_1000, N_("Divisions") }, +#define I_HORZENABLE (3) +#define gridHorzEnableT ((wChoice_p)gridPLs[I_HORZENABLE].control) + { PD_TOGGLE, &grid.Horz.Enable, "horzenable", 0, gridLabels, N_("Enable"), BC_HORZ|BC_NOBORDER }, + { PD_MESSAGE, N_("Vert"), NULL, PDO_DLGNEWCOLUMN|PDO_DLGWIDE, (void*)60}, +#define I_VERTSPACING (5) + { PD_FLOAT, &grid.Vert.Spacing, "vertspacing", PDO_DIM, &r0_999999, NULL }, +#define I_VERTDIVISION (6) + { PD_LONG, &grid.Vert.Division, "vertdivision", 0, &i0_1000, NULL }, +#define I_VERTENABLE (7) +#define gridVertEnableT ((wChoice_p)gridPLs[I_VERTENABLE].control) + { PD_TOGGLE, &grid.Vert.Enable, "vertenable", 0, gridLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_VALUEX (8) + { PD_FLOAT, &grid.Orig.x, "origx", PDO_DIM|PDO_DLGNEWCOLUMN|PDO_DLGWIDE, &r_1000_1000, N_("X") }, +#define I_VALUEY (9) + { PD_FLOAT, &grid.Orig.y, "origy", PDO_DIM, &r_1000_1000, N_("Y") }, +#define I_VALUEA (10) + { PD_FLOAT, &grid.Angle, "origa", PDO_ANGLE, &r0_360, N_("A") }, +#define I_SHOW (11) +#define gridShowT ((wChoice_p)gridPLs[I_SHOW].control) + { PD_TOGGLE, &grid.Show, "show", PDO_DLGIGNORELABELWIDTH, gridLabels, N_("Show"), BC_HORZ|BC_NOBORDER } }; + +static paramGroup_t gridPG = { "grid", PGO_RECORD, gridPLs, sizeof gridPLs/sizeof gridPLs[0] }; + + +static BOOL_T GridChanged( void ) +{ + return + grid.Horz.Spacing != oldGrid.Horz.Spacing || + grid.Horz.Division != oldGrid.Horz.Division || + grid.Vert.Spacing != oldGrid.Vert.Spacing || + grid.Vert.Division != oldGrid.Vert.Division || + grid.Orig.x != oldGrid.Orig.x || + grid.Orig.y != oldGrid.Orig.y || + grid.Angle != oldGrid.Angle || + grid.Horz.Division != oldGrid.Horz.Division; +} + +static void RedrawGrid( void ) +{ + if (grid.Show != oldGrid.Show || + GridChanged() ) { + wDrawDelayUpdate( tempD.d, TRUE ); + DrawASnapGrid( &oldGrid, &tempD, mapD.size, TRUE ); + DrawASnapGrid( &grid, &tempD, mapD.size, TRUE ); + wDrawDelayUpdate( tempD.d, FALSE ); + } +} + + +static void GridOk( void * junk ) +{ + long changes; + + ParamLoadData( &gridPG ); + if ( ( grid.Horz.Enable && grid.Horz.Spacing <= 0.0) || + ( grid.Vert.Enable && grid.Vert.Spacing <= 0.0) ) { + NoticeMessage( MSG_GRID_ENABLE_SPACE_GTR_0, _("Ok"), NULL ); + return; + } + if ( grid.Horz.Spacing <= 0.0 && + grid.Vert.Spacing <= 0.0 ) + grid.Show = FALSE; + + changes = 0; + if ( GridChanged() ) + changes |= CHANGE_GRID; + if (grid.Show != oldGrid.Show || changes != 0) + changes |= CHANGE_MAIN; + DoChangeNotification( changes ); + oldGrid = grid; + Reset(); +} + + +static void GridButtonUpdate( long mode0 ) +{ + long mode1; + mode1 = 0; + if ( grid.Show && + grid.Horz.Spacing <= 0.0 && + grid.Vert.Spacing <= 0.0 ) { + grid.Show = FALSE; + if ( mode0&CHK_SHOW ) + ErrorMessage( MSG_GRID_SHOW_SPACE_GTR_0 ); + } + if ( grid.Horz.Enable && + grid.Horz.Spacing <= 0.0 ) { + grid.Horz.Enable = FALSE; + if ( mode0&CHK_HENABLE ) + mode1 |= CHK_HENABLE; + } + if ( grid.Vert.Enable && + grid.Vert.Spacing <= 0.0 ) { + grid.Vert.Enable = FALSE; + if ( mode0&CHK_VENABLE ) + mode1 |= CHK_VENABLE; + } + if ( mode1 && + (mode0&(CHK_HENABLE|CHK_VENABLE)) == mode1 ) + ErrorMessage( MSG_GRID_ENABLE_SPACE_GTR_0 ); + if ( gridShowT && + grid.Show != (wToggleGetValue( gridShowT ) != 0) ) + ParamLoadControl( &gridPG, I_SHOW ); + if ( gridHorzEnableT && + grid.Horz.Enable != (wToggleGetValue( gridHorzEnableT ) != 0) ) + ParamLoadControl( &gridPG, I_HORZENABLE ); + if ( gridVertEnableT && + grid.Vert.Enable != (wToggleGetValue( gridVertEnableT ) != 0) ) + ParamLoadControl( &gridPG, I_VERTENABLE ); + if (snapGridEnable_b) + wButtonSetBusy( snapGridEnable_b, grid.Horz.Enable||grid.Vert.Enable ); + if (snapGridShow_b) + wButtonSetBusy( snapGridShow_b, (wBool_t)grid.Show ); + if (snapGridEnableMI) + wMenuToggleSet( snapGridEnableMI, grid.Horz.Enable||grid.Vert.Enable ); + if (snapGridShowMI) + wMenuToggleSet( snapGridShowMI, (wBool_t)grid.Show ); + + if ( mode0&CHK_SHOW ) { + RedrawGrid(); + } + oldGrid = grid; +} + + +static void GridChange( long changes ) +{ + if ( (changes&(CHANGE_GRID|CHANGE_UNITS))==0 ) + return; + GridButtonUpdate( 0 ); + if (gridW==NULL || !wWinIsVisible(gridW)) + return; + ParamLoadControls( &gridPG ); +} + + +static void GridDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + switch ( inx ) { + case I_HORZENABLE: + GridButtonUpdate( CHK_HENABLE ); + break; + case I_VERTENABLE: + GridButtonUpdate( CHK_VENABLE ); + break; + case I_SHOW: + GridButtonUpdate( CHK_SHOW ); + break; + default: + wDrawDelayUpdate( tempD.d, TRUE ); + DrawASnapGrid( &oldGrid, &tempD, mapD.size, TRUE ); + ParamLoadData( &gridPG ); + GridButtonUpdate( 0 ); + DrawASnapGrid( &grid, &tempD, mapD.size, TRUE ); + wDrawDelayUpdate( tempD.d, FALSE ); + } +} + + +static void SnapGridRotate( void * pangle ) +{ + ANGLE_T angle = (ANGLE_T)(long)pangle; + wDrawDelayUpdate( tempD.d, TRUE ); + DrawASnapGrid( &oldGrid, &tempD, mapD.size, TRUE ); + grid.Orig = cmdMenuPos; + grid.Angle += angle; + oldGrid = grid; + DrawASnapGrid( &grid, &tempD, mapD.size, TRUE ); + wDrawDelayUpdate( tempD.d, FALSE ); + ParamLoadControls( &gridPG ); +} + + +EXPORT STATUS_T CmdGrid( + wAction_t action, + coOrd pos ) +{ + STATUS_T rc; +#ifdef TIMEDRAWGRID + unsigned long time0, time1, time2; +#endif + + switch (action) { + + case C_START: + if (gridW == NULL) { + gridW = ParamCreateDialog( &gridPG, MakeWindowTitle(_("Snap Grid")), _("Ok"), GridOk, (paramActionCancelProc)Reset, TRUE, NULL, 0, GridDlgUpdate ); + } + oldGrid = grid; + ParamLoadControls( &gridPG ); + wShow( gridW ); + return C_CONTINUE; + + case C_REDRAW: + return C_TERMINATE; + + case C_CANCEL: + grid = oldGrid; + wHide( gridW ); + MainRedraw(); + return C_TERMINATE; + + case C_OK: + GridOk( NULL ); + return C_TERMINATE; + + case C_CONFIRM: + if (GridChanged() || + grid.Show != oldGrid.Show ) + return C_ERROR; + else + return C_CONTINUE; + + case C_DOWN: + case C_RDOWN: + oldGrid = grid; + rc = GridAction( action, pos, &grid.Orig, &grid.Angle ); + return rc; + case C_MOVE: + case C_RMOVE: + rc = GridAction( action, pos, &grid.Orig, &grid.Angle ); + ParamLoadControls( &gridPG ); + return rc; + case C_UP: + case C_RUP: +#ifdef TIMEDRAWGRID + time0 = wGetTimer(); +#endif +#ifdef TIMEDRAWGRID + time1 = wGetTimer(); +#endif + rc = GridAction( action, pos, &grid.Orig, &grid.Angle ); + ParamLoadControls( &gridPG ); + RedrawGrid(); + oldGrid = grid; +#ifdef TIMEDRAWGRID + time2 = wGetTimer(); + InfoMessage( "undraw %ld, draw %ld", (long)(time1-time0), (long)(time2-time1) ); +#endif + return rc; + + case C_CMDMENU: + wMenuPopupShow( snapGridPopupM ); + break; + } + + return C_CONTINUE; +} + + +/** + * Initialize the user interface for the grid functions. + * + * \param menu IN pulldown to which the grid function will be added + * \return created command button +*/ + +EXPORT wIndex_t InitGrid( wMenu_p menu ) +{ + ParamRegister( &gridPG ); + RegisterChangeNotification( GridChange ); + if ( grid.Horz.Enable && grid.Horz.Spacing <= 0.0 ) + grid.Horz.Enable = FALSE; + if ( grid.Vert.Enable && grid.Vert.Spacing <= 0.0 ) + grid.Vert.Enable = FALSE; + if ( grid.Horz.Spacing <= 0.0 && + grid.Vert.Spacing <= 0.0 ) + grid.Show = FALSE; + snapGridPopupM = MenuRegister( "Snap Grid Rotate" ); + AddRotateMenu( snapGridPopupM, SnapGridRotate ); + GridButtonUpdate( 0 ); + return InitCommand( menu, CmdGrid, N_("Change Grid..."), NULL, LEVEL0, IC_CMDMENU, ACCL_GRIDW ); +} + + +EXPORT void SnapGridEnable( void ) +{ + grid.Vert.Enable = grid.Horz.Enable = !( grid.Vert.Enable || grid.Horz.Enable ); + GridButtonUpdate( (CHK_HENABLE|CHK_VENABLE) ); +} + + +EXPORT void SnapGridShow( void ) +{ + grid.Show = !grid.Show; + GridButtonUpdate( CHK_SHOW ); +} + +#include "bitmaps/snapcurs.xbm" +#include "bitmaps/snapvis.xbm" + +EXPORT void InitSnapGridButtons( void ) +{ + snapGridEnable_b = AddToolbarButton( "cmdGridEnable", wIconCreateBitMap(snapcurs_width, snapcurs_height, snapcurs_bits, wDrawColorBlack), 0, (addButtonCallBack_t)SnapGridEnable, NULL ); + snapGridShow_b = AddToolbarButton( "cmdGridShow", wIconCreateBitMap(snapvis_width, snapvis_height, snapvis_bits, wDrawColorBlack), IC_MODETRAIN_TOO, (addButtonCallBack_t)SnapGridShow, NULL ); +} diff --git a/app/bin/csplit.c b/app/bin/csplit.c new file mode 100644 index 0000000..69642fb --- /dev/null +++ b/app/bin/csplit.c @@ -0,0 +1,155 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/csplit.c,v 1.4 2008-03-06 19:35:06 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 "track.h" +#include "i18n.h" + +/***************************************************************************** + * + * SPLIT + * + */ + + +static wMenu_p splitPopupM[2]; +static wMenuToggle_p splitPopupMI[2][4]; +static track_p splitTrkTrk[2]; +static EPINX_T splitTrkEP[2]; +static BOOL_T splitTrkFlip; + +static void ChangeSplitEPMode( wBool_t set, void * mode ) +{ + long imode = (long)mode; + long option; + int inx0, inx; + + UndoStart( _("Set Block Gaps"), "Set Block Gaps" ); + DrawEndPt( &mainD, splitTrkTrk[0], splitTrkEP[0], wDrawColorWhite ); + DrawEndPt( &mainD, splitTrkTrk[1], splitTrkEP[1], wDrawColorWhite ); + for ( inx0=0; inx0<2; inx0++ ) { + inx = splitTrkFlip?1-inx0:inx0; + UndoModify( splitTrkTrk[inx] ); + option = GetTrkEndOption( splitTrkTrk[inx], splitTrkEP[inx] ); + option &= ~EPOPT_GAPPED; + if ( (imode&1) != 0 ) + option |= EPOPT_GAPPED; + SetTrkEndOption( splitTrkTrk[inx], splitTrkEP[inx], option ); + imode >>= 1; + } + DrawEndPt( &mainD, splitTrkTrk[0], splitTrkEP[0], wDrawColorBlack ); + DrawEndPt( &mainD, splitTrkTrk[1], splitTrkEP[1], wDrawColorBlack ); +} + +static STATUS_T CmdSplitTrack( wAction_t action, coOrd pos ) +{ + track_p trk0, trk1; + EPINX_T ep0; + int oldTrackCount; + int inx, mode, quad; + ANGLE_T angle; + + switch (action) { + case C_START: + InfoMessage( _("Select track to split") ); + case C_DOWN: + case C_MOVE: + return C_CONTINUE; + break; + case C_UP: + onTrackInSplit = TRUE; + trk0 = OnTrack( &pos, TRUE, TRUE ); + if ( trk0 != NULL) { + if (!CheckTrackLayer( trk0 ) ) { + onTrackInSplit = FALSE; + return C_TERMINATE; + } + ep0 = PickEndPoint( pos, trk0 ); + onTrackInSplit = FALSE; + if (ep0 < 0) { + return C_CONTINUE; + } + UndoStart( _("Split Track"), "SplitTrack( T%d[%d] )", GetTrkIndex(trk0), ep0 ); + oldTrackCount = trackCount; + SplitTrack( trk0, pos, ep0, &trk1, FALSE ); + UndoEnd(); + return C_TERMINATE; + } + onTrackInSplit = FALSE; + return C_TERMINATE; + break; + case C_CMDMENU: + splitTrkTrk[0] = OnTrack( &pos, TRUE, TRUE ); + if ( splitTrkTrk[0] == NULL ) + return C_CONTINUE; + if ( splitPopupM[0] == NULL ) { + splitPopupM[0] = MenuRegister( "End Point Mode R-L" ); + splitPopupMI[0][0] = wMenuToggleCreate( splitPopupM[0], "", _("None"), 0, TRUE, ChangeSplitEPMode, (void*)0 ); + splitPopupMI[0][1] = wMenuToggleCreate( splitPopupM[0], "", _("Left"), 0, FALSE, ChangeSplitEPMode, (void*)1 ); + splitPopupMI[0][2] = wMenuToggleCreate( splitPopupM[0], "", _("Right"), 0, FALSE, ChangeSplitEPMode, (void*)2 ); + splitPopupMI[0][3] = wMenuToggleCreate( splitPopupM[0], "", _("Both"), 0, FALSE, ChangeSplitEPMode, (void*)3 ); + splitPopupM[1] = MenuRegister( "End Point Mode T-B" ); + splitPopupMI[1][0] = wMenuToggleCreate( splitPopupM[1], "", _("None"), 0, TRUE, ChangeSplitEPMode, (void*)0 ); + splitPopupMI[1][1] = wMenuToggleCreate( splitPopupM[1], "", _("Top"), 0, FALSE, ChangeSplitEPMode, (void*)1 ); + splitPopupMI[1][2] = wMenuToggleCreate( splitPopupM[1], "", _("Bottom"), 0, FALSE, ChangeSplitEPMode, (void*)2 ); + splitPopupMI[1][3] = wMenuToggleCreate( splitPopupM[1], "", _("Both"), 0, FALSE, ChangeSplitEPMode, (void*)3 ); + } + splitTrkEP[0] = PickEndPoint( pos, splitTrkTrk[0] ); + angle = NormalizeAngle(GetTrkEndAngle( splitTrkTrk[0], splitTrkEP[0] )); + if ( angle <= 45.0 ) + quad = 0; + else if ( angle <= 135.0 ) + quad = 1; + else if ( angle <= 225.0 ) + quad = 2; + else if ( angle <= 315.0 ) + quad = 3; + else + quad = 0; + splitTrkFlip = (quad<2); + if ( (splitTrkTrk[1] = GetTrkEndTrk( splitTrkTrk[0], splitTrkEP[0] ) ) == NULL ) { + ErrorMessage( MSG_BAD_BLOCKGAP ); + return C_CONTINUE; + } + splitTrkEP[1] = GetEndPtConnectedToMe( splitTrkTrk[1], splitTrkTrk[0] ); + mode = 0; + if ( GetTrkEndOption( splitTrkTrk[1-splitTrkFlip], splitTrkEP[1-splitTrkFlip] ) & EPOPT_GAPPED ) + mode |= 2; + if ( GetTrkEndOption( splitTrkTrk[splitTrkFlip], splitTrkEP[splitTrkFlip] ) & EPOPT_GAPPED ) + mode |= 1; + for ( inx=0; inx<4; inx++ ) + wMenuToggleSet( splitPopupMI[quad&1][inx], mode == inx ); + wMenuPopupShow( splitPopupM[quad&1] ); + break; + } + return C_CONTINUE; +} + + + + +#include "bitmaps/splittrk.xpm" + +void InitCmdSplit( wMenu_p menu ) +{ + AddMenuButton( menu, CmdSplitTrack, "cmdSplitTrack", _("Split Track"), wIconCreatePixMap(splittrk_xpm), LEVEL0_50, IC_STICKY|IC_POPUP|IC_CMDMENU, ACCL_SPLIT, NULL ); +} + diff --git a/app/bin/cstraigh.c b/app/bin/cstraigh.c new file mode 100644 index 0000000..6038c9a --- /dev/null +++ b/app/bin/cstraigh.c @@ -0,0 +1,105 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cstraigh.c,v 1.4 2008-03-06 19:35:06 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 "track.h" +#include "cstraigh.h" +#include "i18n.h" + +/******************************************************************************* + * + * STRAIGHT + * + */ + +/* + * STATE INFO + */ +static struct { + coOrd pos0, pos1; + } Dl; + + +static STATUS_T CmdStraight( wAction_t action, coOrd pos ) +{ + track_p t; + DIST_T dist; + + switch (action) { + + case C_START: + InfoMessage( _("Place 1st end point of Straight track") ); + return C_CONTINUE; + + case C_DOWN: + SnapPos( &pos ); + Dl.pos0 = pos; + InfoMessage( _("Drag to place 2nd end point") ); + DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).width = 0; + tempSegs_da.cnt = 0; + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).u.l.pos[0] = pos; + return C_CONTINUE; + + case C_MOVE: + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + SnapPos( &pos ); + InfoMessage( _("Straight Track Length=%s Angle=%0.3f"), + FormatDistance(FindDistance( Dl.pos0, pos )), + PutAngle(FindAngle( Dl.pos0, pos )) ); + tempSegs(0).u.l.pos[1] = pos; + tempSegs_da.cnt = 1; + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + return C_CONTINUE; + + case C_UP: + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + tempSegs_da.cnt = 0; + SnapPos( &pos ); + if ((dist=FindDistance( Dl.pos0, pos )) <= minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, "Straight ", PutDim(fabs(minLength-dist)) ); + return C_TERMINATE; + } + UndoStart( _("Create Straight Track"), "newStraight" ); + t = NewStraightTrack( Dl.pos0, pos ); + UndoEnd(); + DrawNewTrack(t); + return C_TERMINATE; + + case C_REDRAW: + case C_CANCEL: + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + return C_CONTINUE; + + default: + return C_CONTINUE; + } +} + + +#include "bitmaps/straight.xpm" + +void InitCmdStraight( wMenu_p menu ) +{ + AddMenuButton( menu, CmdStraight, "cmdStraight", _("Straight Track"), wIconCreatePixMap(straight_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_STRAIGHT, NULL ); +} diff --git a/app/bin/cstraigh.h b/app/bin/cstraigh.h new file mode 100644 index 0000000..eca7e99 --- /dev/null +++ b/app/bin/cstraigh.h @@ -0,0 +1,25 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cstraigh.h,v 1.1 2005-12-07 15:46:54 rc-flyer 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. + */ + +void AdjustStraightEndPt( track_p t, EPINX_T ep, coOrd pos ); +track_p NewStraightTrack( coOrd p0, coOrd p1 ); +BOOL_T ExtendStraightToJoin( track_p, EPINX_T, track_p, EPINX_T ); diff --git a/app/bin/cstruct.c b/app/bin/cstruct.c new file mode 100644 index 0000000..1f86217 --- /dev/null +++ b/app/bin/cstruct.c @@ -0,0 +1,922 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cstruct.c,v 1.4 2008-03-06 19:35:06 m_fischer Exp $ + * + * T_STRUCTURE + * + */ + +/* 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 <ctype.h> +#include "track.h" +#include "compound.h" +#include "i18n.h" + +#include <stdint.h> + +EXPORT TRKTYP_T T_STRUCTURE = -1; + +#define STRUCTCMD + +EXPORT dynArr_t structureInfo_da; + +typedef struct compoundData extraData; + + +static wIndex_t pierListInx; +EXPORT turnoutInfo_t * curStructure = NULL; +static int log_structure = 0; + +static wMenu_p structPopupM; + +#ifdef STRUCTCMD +static drawCmd_t structureD = { + NULL, + &screenDrawFuncs, + 0, + 1.0, + 0.0, + {0.0,0.0}, {0.0,0.0}, + Pix2CoOrd, CoOrd2Pix }; + +static wIndex_t structureHotBarCmdInx; +static wIndex_t structureInx; +static long hideStructureWindow; +static void RedrawStructure(void); + +static wPos_t structureListWidths[] = { 80, 80, 220 }; +static const char * structureListTitles[] = { N_("Manufacturer"), N_("Part No"), N_("Description") }; +static paramListData_t listData = { 13, 400, 3, structureListWidths, structureListTitles }; +static const char * hideLabels[] = { N_("Hide"), NULL }; +static paramDrawData_t structureDrawData = { 490, 200, (wDrawRedrawCallBack_p)RedrawStructure, NULL, &structureD }; +static paramData_t structurePLs[] = { +#define I_LIST (0) +#define structureListL ((wList_p)structurePLs[I_LIST].control) + { PD_LIST, &structureInx, "list", PDO_NOPREF|PDO_DLGRESIZEW, &listData, NULL, BL_DUP }, +#define I_DRAW (1) + { PD_DRAW, NULL, "canvas", PDO_NOPSHUPD|PDO_DLGUNDERCMDBUTT|PDO_DLGRESIZE, &structureDrawData, NULL, 0 }, +#define I_HIDE (2) + { PD_TOGGLE, &hideStructureWindow, "hide", PDO_DLGCMDBUTTON, /*CAST_AWAY_CONST*/(void*)hideLabels, NULL, BC_NOBORDER }, +#define I_MSGSCALE (3) + { PD_MESSAGE, NULL, NULL, 0, (void*)80 }, +#define I_MSGWIDTH (4) + { PD_MESSAGE, NULL, NULL, 0, (void*)80 }, +#define I_MSGHEIGHT (5) + { PD_MESSAGE, NULL, NULL, 0, (void*)80 } }; +static paramGroup_t structurePG = { "structure", 0, structurePLs, sizeof structurePLs/sizeof structurePLs[0] }; +#endif + + +/**************************************** + * + * STRUCTURE LIST MANAGEMENT + * + */ + + + + +EXPORT turnoutInfo_t * CreateNewStructure( + char * scale, + char * title, + wIndex_t segCnt, + trkSeg_p segData, + BOOL_T updateList ) +{ + turnoutInfo_t * to; +#ifdef REORIGSTRUCT + coOrd orig; +#endif + + if (segCnt == 0) + return NULL; + to = FindCompound( FIND_STRUCT, scale, title ); + if (to == NULL) { + DYNARR_APPEND( turnoutInfo_t *, structureInfo_da, 10 ); + to = (turnoutInfo_t*)MyMalloc( sizeof *to ); + structureInfo(structureInfo_da.cnt-1) = to; + to->title = MyStrdup( title ); + to->scaleInx = LookupScale( scale ); + } + to->segCnt = segCnt; + to->segs = (trkSeg_p)memdup( segData, (sizeof *segData) * segCnt ); + GetSegBounds( zero, 0.0, to->segCnt, to->segs, &to->orig, &to->size ); +#ifdef REORIGSTRUCT + GetSegBounds( zero, 0.0, to->segCnt, to->segs, &orig, &to->size ); + orig.x = - orig.x; + orig.y = - orig.y; + MoveSegs( to->segCnt, to->segs, orig ); + to->orig = zero; +#endif + to->paramFileIndex = curParamFileIndex; + if (curParamFileIndex == PARAM_CUSTOM) + to->contentsLabel = "Custom Structures"; + else + to->contentsLabel = curSubContents; + to->endCnt = 0; + to->pathLen = 0; + to->paths = (PATHPTR_T)""; +#ifdef STRUCTCMD + if (updateList && structureListL != NULL) { + FormatCompoundTitle( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, to->title ); + if (message[0] != '\0') + wListAddValue( structureListL, message, NULL, to ); + } +#endif + + to->barScale = curBarScale>0?curBarScale:-1; + return to; +} + + +static BOOL_T ReadStructureParam( + char * firstLine ) +{ + char scale[10]; + char *title; + turnoutInfo_t * to; + char * cp; +static dynArr_t pierInfo_da; +#define pierInfo(N) DYNARR_N( pierInfo_t, pierInfo_da, N ) + + if ( !GetArgs( firstLine+10, "sq", scale, &title ) ) + return FALSE; + ReadSegs(); + to = CreateNewStructure( scale, title, tempSegs_da.cnt, &tempSegs(0), FALSE ); + if (to == NULL) + return FALSE; + if (tempSpecial[0] != '\0') { + if (strncmp( tempSpecial, PIER, strlen(PIER) ) == 0) { + DYNARR_RESET( pierInfo_t, pierInfo_da ); + to->special = TOpierInfo; + cp = tempSpecial+strlen(PIER); + while (cp) { + DYNARR_APPEND( pierInfo_t, pierInfo_da, 10 ); + GetArgs( cp, "fqc", &pierInfo(pierInfo_da.cnt-1).height, &pierInfo(pierInfo_da.cnt-1).name, &cp ); + } + to->u.pierInfo.cnt = pierInfo_da.cnt; + to->u.pierInfo.info = (pierInfo_t*)MyMalloc( pierInfo_da.cnt * sizeof *(pierInfo_t*)NULL ); + memcpy( to->u.pierInfo.info, &pierInfo(0), pierInfo_da.cnt * sizeof *(pierInfo_t*)NULL ); + } else { + InputError("Unknown special case", TRUE); + } + } + if (tempCustom[0] != '\0') { + to->customInfo = MyStrdup( tempCustom ); + } + MyFree( title ); + return TRUE; +} + + +EXPORT turnoutInfo_t * StructAdd( long mode, SCALEINX_T scale, wList_p list, coOrd * maxDim ) +{ + wIndex_t inx; + turnoutInfo_t * to, *to1=NULL; + for ( inx = 0; inx < structureInfo_da.cnt; inx++ ) { + to = structureInfo(inx); + if ( IsParamValid(to->paramFileIndex) && + to->segCnt > 0 && + CompatibleScale( FALSE, to->scaleInx, scale ) && + to->segCnt != 0 ) { + if (to1 == NULL) + to1 = to; + FormatCompoundTitle( mode, to->title ); + if (message[0] != '\0') { + wListAddValue( list, message, NULL, to ); + if (maxDim) { + if (to->size.x > maxDim->x) + maxDim->x = to->size.x; + if (to->size.y > maxDim->y) + maxDim->y = to->size.y; + } + } + } + } + return to1; +} + + +/**************************************** + * + * GENERIC FUNCTIONS + * + */ + +static void DrawStructure( + track_p t, + drawCmd_p d, + wDrawColor color ) +{ + struct extraData *xx = GetTrkExtraData(t); + coOrd p00, px0, pxy, p0y, orig, size; + + if (d->options&DC_QUICK) { + GetSegBounds( zero, 0.0, xx->segCnt, xx->segs, &orig, &size ); + p00.x = p0y.x = orig.x; + p00.y = px0.y = orig.y; + px0.x = pxy.x = orig.x + size.x; + p0y.y = pxy.y = orig.y + size.y; + REORIGIN1( p00, xx->angle, xx->orig ) + REORIGIN1( px0, xx->angle, xx->orig ) + REORIGIN1( p0y, xx->angle, xx->orig ) + REORIGIN1( pxy, xx->angle, xx->orig ) + DrawLine( d, p00, px0, 0, color ); + DrawLine( d, px0, pxy, 0, color ); + DrawLine( d, pxy, p0y, 0, color ); + DrawLine( d, p0y, p00, 0, color ); + } else { + DrawSegs( d, xx->orig, xx->angle, xx->segs, xx->segCnt, 0.0, color ); + if ( ((d->funcs->options&wDrawOptTemp)==0) && + (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) && + labelScale >= d->scale && + ( GetTrkBits( t ) & TB_HIDEDESC ) == 0 ) { + DrawCompoundDescription( t, d, color ); + } + } +} + + +static void ReadStructure( + char * line ) +{ + ReadCompound( line+10, T_STRUCTURE ); +} + + +static ANGLE_T GetAngleStruct( + track_p trk, + coOrd pos, + EPINX_T * ep0, + EPINX_T * ep1 ) +{ + struct extraData * xx = GetTrkExtraData(trk); + ANGLE_T angle; + + pos.x -= xx->orig.x; + pos.y -= xx->orig.y; + Rotate( &pos, zero, -xx->angle ); + angle = GetAngleSegs( xx->segCnt, xx->segs, pos, NULL ); + if ( ep0 ) *ep0 = -1; + if ( ep1 ) *ep1 = -1; + return NormalizeAngle( angle+xx->angle ); +} + + +static BOOL_T QueryStructure( track_p trk, int query ) +{ + switch ( query ) { + case Q_HAS_DESC: + return TRUE; + default: + return FALSE; + } +} + + +static trackCmd_t structureCmds = { + "STRUCTURE", + DrawStructure, + DistanceCompound, + DescribeCompound, + DeleteCompound, + WriteCompound, + ReadStructure, + MoveCompound, + RotateCompound, + RescaleCompound, + NULL, + GetAngleStruct, + NULL, /* split */ + NULL, /* traverse */ + EnumerateCompound, + NULL, /* redraw */ + NULL, /* trim */ + NULL, /* merge */ + NULL, /* modify */ + NULL, /* getLength */ + NULL, /* getTrkParams */ + NULL, /* moveEndPt */ + QueryStructure, + UngroupCompound, + FlipCompound }; + +static paramData_t pierPLs[] = { + { PD_DROPLIST, &pierListInx, "inx", 0, (void*)50, N_("Pier Number") } }; +static paramGroup_t pierPG = { "structure-pier", 0, pierPLs, sizeof pierPLs/sizeof pierPLs[0] }; +#define pierL ((wList_p)pierPLs[0].control) + +static void ShowPierL( void ) +{ + int inx; + wIndex_t currInx; + wControl_p controls[2]; + char * labels[1]; + + if ( curStructure->special==TOpierInfo && curStructure->u.pierInfo.cnt > 1) { + if (pierL == NULL) { + ParamCreateControls( &pierPG, NULL ); + } + currInx = wListGetIndex( pierL ); + wListClear( pierL ); + for (inx=0;inx<curStructure->u.pierInfo.cnt; inx++) { + wListAddValue( pierL, curStructure->u.pierInfo.info[inx].name, NULL, NULL ); + } + if ( currInx < 0 ) + currInx = 0; + if ( currInx >= curStructure->u.pierInfo.cnt ) + currInx = curStructure->u.pierInfo.cnt-1; + wListSetIndex( pierL, currInx ); + controls[0] = (wControl_p)pierL; + controls[1] = NULL; + labels[0] = N_("Pier Number"); + InfoSubstituteControls( controls, labels ); + } else { + InfoSubstituteControls( NULL, NULL ); + } +} + + +#ifdef STRUCTCMD +/***************************************** + * + * Structure Dialog + * + */ + +static void NewStructure(); +static coOrd maxStructureDim; +static wWin_p structureW; + + +static void RescaleStructure( void ) +{ + DIST_T xscale, yscale; + wPos_t ww, hh; + DIST_T w, h; + wDrawGetSize( structureD.d, &ww, &hh ); + w = ww/structureD.dpi - 0.2; + h = hh/structureD.dpi - 0.2; + if (curStructure) { + xscale = curStructure->size.x/w; + yscale = curStructure->size.y/h; + } else { + xscale = yscale = 0; + } + structureD.scale = ceil(max(xscale,yscale)); + structureD.size.x = (w+0.2)*structureD.scale; + structureD.size.y = (h+0.2)*structureD.scale; + return; +} + + +static void structureChange( long changes ) +{ + static char * lastScaleName = NULL; + if (structureW == NULL) + return; + wListSetIndex( structureListL, 0 ); + if ( (!wWinIsVisible(structureW)) || + ( ((changes&CHANGE_SCALE) == 0 || lastScaleName == curScaleName) && + (changes&CHANGE_PARAMS) == 0 ) ) + return; + lastScaleName = curScaleName; + curStructure = NULL; + wControlShow( (wControl_p)structureListL, FALSE ); + wListClear( structureListL ); + maxStructureDim.x = maxStructureDim.y = 0.0; + if (structureInfo_da.cnt <= 0) + return; + curStructure = StructAdd( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, curScaleInx, structureListL, &maxStructureDim ); + wControlShow( (wControl_p)structureListL, TRUE ); + if (curStructure == NULL) { + wDrawClear( structureD.d ); + return; + } + maxStructureDim.x += 2*trackGauge; + maxStructureDim.y += 2*trackGauge; + /*RescaleStructure();*/ + RedrawStructure(); + return; +} + + + +static void RedrawStructure() +{ + RescaleStructure(); +LOG( log_structure, 2, ( "SelStructure(%s)\n", (curStructure?curStructure->title:"<NULL>") ) ) + wDrawClear( structureD.d ); + if (curStructure == NULL) { + return; + } + structureD.orig.x = -0.10*structureD.scale + curStructure->orig.x; + structureD.orig.y = (curStructure->size.y + curStructure->orig.y) - structureD.size.y + trackGauge; + DrawSegs( &structureD, zero, 0.0, curStructure->segs, curStructure->segCnt, + 0.0, wDrawColorBlack ); + sprintf( message, _("Scale %d:1"), (int)structureD.scale ); + ParamLoadMessage( &structurePG, I_MSGSCALE, message ); + sprintf( message, _("Width %s"), FormatDistance(curStructure->size.x) ); + ParamLoadMessage( &structurePG, I_MSGWIDTH, message ); + sprintf( message, _("Height %s"), FormatDistance(curStructure->size.y) ); + ParamLoadMessage( &structurePG, I_MSGHEIGHT, message ); +} + + +static void StructureDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + turnoutInfo_t * to; + if ( inx != I_LIST ) return; + to = (turnoutInfo_t*)wListGetItemContext( (wList_p)pg->paramPtr[inx].control, (wIndex_t)*(long*)valueP ); + NewStructure(); + curStructure = to; + ShowPierL(); + RedrawStructure(); + ParamDialogOkActive( &structurePG, FALSE ); +} + + +static void DoStructOk( void ) +{ + NewStructure(); + Reset(); +} + +#endif + +/**************************************** + * + * GRAPHICS COMMANDS + * + */ + +/* + * STATE INFO + */ +static struct { + int state; + coOrd pos; + ANGLE_T angle; + } Dst; + +static track_p pierTrk; +static EPINX_T pierEp; + +static ANGLE_T PlaceStructure( + coOrd p0, + coOrd p1, + coOrd origPos, + coOrd * resPos, + ANGLE_T * resAngle ) +{ + coOrd p2 = p1; + if (curStructure->special == TOpierInfo) { + pierTrk = OnTrack( &p1, FALSE, TRUE ); + if (pierTrk != NULL) { + if (GetTrkType(pierTrk) == T_TURNOUT) { + pierEp = PickEndPoint( p1, pierTrk ); + if (pierEp >= 0) { + *resPos = GetTrkEndPos(pierTrk, pierEp); + *resAngle = NormalizeAngle(GetTrkEndAngle(pierTrk, pierEp)-90.0); + return TRUE; + } + } + *resAngle = NormalizeAngle(GetAngleAtPoint( pierTrk, p1, NULL, NULL )+90.0); + if ( NormalizeAngle( FindAngle( p1, p2 ) - *resAngle + 90.0 ) > 180.0 ) + *resAngle = NormalizeAngle( *resAngle + 180.0 ); + *resPos = p1; + return TRUE; + } + } + resPos->x = origPos.x + p1.x - p0.x; + resPos->y = origPos.y + p1.y - p0.y; + return FALSE; +} + + +static void NewStructure( void ) +{ + track_p trk; + struct extraData *xx; + wIndex_t titleLen; + wIndex_t pierInx; + + if (curStructure->segCnt < 1) { + AbortProg( "newStructure: bad cnt" ); + } + if (Dst.state == 0) + return; + if (curStructure->special == TOpierInfo && + curStructure->u.pierInfo.cnt>1 && + wListGetIndex(pierL) == -1) { + return; + } + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + UndoStart( _("Place Structure"), "newStruct" ); + titleLen = strlen( curStructure->title ); + trk = NewCompound( T_STRUCTURE, 0, Dst.pos, Dst.angle, curStructure->title, 0, NULL, 0, "", curStructure->segCnt, curStructure->segs ); + xx = GetTrkExtraData(trk); +#ifdef LATER + trk = NewTrack( 0, T_STRUCTURE, 0, sizeof (*xx) + 1 ); + xx->orig = Dst.pos; + xx->angle = Dst.angle; + xx->segs = MyMalloc( (curStructure->segCnt)*sizeof curStructure->segs[0] ); + + /* + * copy data */ + xx->segCnt = curStructure->segCnt; + memcpy( xx->segs, curStructure->segs, xx->segCnt * sizeof *(trkSeg_p)0 ); + xx->title = curStructure->title; + xx->pathLen = 0; + xx->paths = ""; +#endif + switch(curStructure->special) { + case TOnormal: + xx->special = TOnormal; + break; + case TOpierInfo: + xx->special = TOpier; + if (curStructure->u.pierInfo.cnt>1) { + pierInx = wListGetIndex(pierL); + if (pierInx < 0 || pierInx >= curStructure->u.pierInfo.cnt) + pierInx = 0; + } else { + pierInx = 0; + } + xx->u.pier.height = curStructure->u.pierInfo.info[pierInx].height; + xx->u.pier.name = curStructure->u.pierInfo.info[pierInx].name; + if (pierTrk != NULL && xx->u.pier.height >= 0 ) { + UpdateTrkEndElev( pierTrk, pierEp, ELEV_DEF, xx->u.pier.height, NULL ); + } + break; + default: + AbortProg("bad special"); + } + + SetTrkVisible( trk, TRUE ); +#ifdef LATER + ComputeCompoundBoundingBox( trk ); + + SetDescriptionOrig( trk ); + xx->descriptionOff = zero; + xx->descriptionSize = zero; +#endif + + DrawNewTrack( trk ); + /*DrawStructure( trk, &mainD, wDrawColorBlack, 0 );*/ + + UndoEnd(); + Dst.state = 0; + Dst.angle = 0.0; +} + + +static void StructRotate( void * pangle ) +{ + ANGLE_T angle = (ANGLE_T)(long)pangle; + if (Dst.state == 1) + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + else + Dst.pos = cmdMenuPos; + Rotate( &Dst.pos, cmdMenuPos, angle ); + Dst.angle += angle; + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + Dst.state = 1; +} + + +EXPORT STATUS_T CmdStructureAction( + wAction_t action, + coOrd pos ) +{ + + ANGLE_T angle; + static BOOL_T validAngle; + static ANGLE_T baseAngle; + static coOrd origPos; + static ANGLE_T origAngle; + static coOrd rot0, rot1; + + switch (action & 0xFF) { + + case C_START: + Dst.state = 0; + Dst.angle = 00.0; + ShowPierL(); + return C_CONTINUE; + + case C_DOWN: + if ( curStructure == NULL ) return C_CONTINUE; + ShowPierL(); + if (Dst.state == 1) { + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + } else { + Dst.pos = pos; + } + rot0 = pos; + origPos = Dst.pos; + PlaceStructure( rot0, pos, origPos, &Dst.pos, &Dst.angle ); + Dst.state = 1; + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + InfoMessage( _("Drag to place") ); + return C_CONTINUE; + + case C_MOVE: + if ( curStructure == NULL ) return C_CONTINUE; + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + PlaceStructure( rot0, pos, origPos, &Dst.pos, &Dst.angle ); + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + MainRedraw(); + InfoMessage( "[ %0.3f %0.3f ]", pos.x - origPos.x, pos.y - origPos.y ); + return C_CONTINUE; + + case C_RDOWN: + if ( curStructure == NULL ) return C_CONTINUE; + if (Dst.state == 1) + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + else + Dst.pos = pos; + rot0 = rot1 = pos; + DrawLine( &tempD, rot0, rot1, 0, wDrawColorBlack ); + Dst.state = 1; + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + origPos = Dst.pos; + origAngle = Dst.angle; + InfoMessage( _("Drag to rotate") ); + validAngle = FALSE; + return C_CONTINUE; + + case C_RMOVE: + if ( curStructure == NULL ) return C_CONTINUE; + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + DrawLine( &tempD, rot0, rot1, 0, wDrawColorBlack ); + rot1 = pos; + if ( FindDistance( rot0, rot1 ) > (6.0/75.0)*mainD.scale ) { + angle = FindAngle( rot0, rot1 ); + if (!validAngle) { + baseAngle = angle; + validAngle = TRUE; + } + angle -= baseAngle; + Dst.pos = origPos; + Dst.angle = NormalizeAngle( origAngle + angle ); + Rotate( &Dst.pos, rot0, angle ); + } + InfoMessage( _("Angle = %0.3f"), Dst.angle ); + DrawLine( &tempD, rot0, rot1, 0, wDrawColorBlack ); + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + return C_CONTINUE; + + case C_RUP: + DrawLine( &tempD, rot0, rot1, 0, wDrawColorBlack ); + case C_UP: + MainRedraw(); + return C_CONTINUE; + + case C_CMDMENU: + if ( structPopupM == NULL ) { + structPopupM = MenuRegister( "Structure Rotate" ); + AddRotateMenu( structPopupM, StructRotate ); + } + wMenuPopupShow( structPopupM ); + return C_CONTINUE; + + case C_REDRAW: + if (Dst.state == 1) + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + return C_CONTINUE; + + case C_CANCEL: + if (Dst.state == 1) + DrawSegs( &tempD, Dst.pos, Dst.angle, + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + Dst.state = 0; + InfoSubstituteControls( NULL, NULL ); + HotBarCancel(); + /*wHide( newTurn.reg.win );*/ + return C_TERMINATE; + + case C_TEXT: + if ((action>>8) != ' ') + return C_CONTINUE; + case C_OK: + NewStructure(); + InfoSubstituteControls( NULL, NULL ); + return C_TERMINATE; + + case C_FINISH: + if (Dst.state != 0) + CmdStructureAction( C_OK, pos ); + else + CmdStructureAction( C_CANCEL, pos ); + return C_TERMINATE; + + default: + return C_CONTINUE; + } +} + + +static STATUS_T CmdStructure( + wAction_t action, + coOrd pos ) +{ + + wIndex_t structureIndex; + turnoutInfo_t * structurePtr; + + switch (action & 0xFF) { + + case C_START: + if (structureW == NULL) { + structureW = ParamCreateDialog( &structurePG, MakeWindowTitle(_("Structure")), _("Ok"), (paramActionOkProc)DoStructOk, (paramActionCancelProc)Reset, TRUE, NULL, F_RESIZE, StructureDlgUpdate ); + RegisterChangeNotification( structureChange ); + } + ParamDialogOkActive( &structurePG, FALSE ); + structureIndex = wListGetIndex( structureListL ); + structurePtr = curStructure; + wShow( structureW ); + structureChange( CHANGE_PARAMS ); + if (curStructure == NULL) { + NoticeMessage( MSG_STRUCT_NO_STRUCTS, _("Ok"), NULL ); + return C_TERMINATE; + } + if (structureIndex > 0 && structurePtr) { + curStructure = structurePtr; + wListSetIndex( structureListL, structureIndex ); + RedrawStructure(); + } + InfoMessage( _("Select Structure and then drag to place")); + ParamLoadControls( &structurePG ); + ParamGroupRecord( &structurePG ); + return CmdStructureAction( action, pos ); + + case C_DOWN: + case C_RDOWN: + ParamDialogOkActive( &structurePG, TRUE ); + if (hideStructureWindow) + wHide( structureW ); + case C_MOVE: + case C_RMOVE: + return CmdStructureAction( action, pos ); + + case C_RUP: + case C_UP: + if (hideStructureWindow) + wShow( structureW ); + InfoMessage( _("Left drag to move, right drag to rotate, or press Return or click Ok to finalize") ); + return CmdStructureAction( action, pos ); + return C_CONTINUE; + + case C_CANCEL: + wHide( structureW ); + case C_TEXT: + case C_OK: + case C_FINISH: + case C_CMDMENU: + case C_REDRAW: + return CmdStructureAction( action, pos ); + + default: + return C_CONTINUE; + } +} + + + +static char * CmdStructureHotBarProc( + hotBarProc_e op, + void * data, + drawCmd_p d, + coOrd * origP ) +{ + turnoutInfo_t * to = (turnoutInfo_t*)data; + switch ( op ) { + case HB_SELECT: + CmdStructureAction( C_FINISH, zero ); + curStructure = to; + DoCommandB( (void*)(intptr_t)structureHotBarCmdInx ); + return NULL; + case HB_LISTTITLE: + FormatCompoundTitle( listLabels, to->title ); + if (message[0] == '\0') + FormatCompoundTitle( listLabels|LABEL_DESCR, to->title ); + return message; + case HB_BARTITLE: + FormatCompoundTitle( hotBarLabels<<1, to->title ); + return message; + case HB_FULLTITLE: + return to->title; + case HB_DRAW: + DrawSegs( d, *origP, 0.0, to->segs, to->segCnt, trackGauge, wDrawColorBlack ); + return NULL; + } + return NULL; +} + + +EXPORT void AddHotBarStructures( void ) +{ + wIndex_t inx; + turnoutInfo_t * to; + for ( inx=0; inx < structureInfo_da.cnt; inx ++ ) { + to = structureInfo(inx); + if ( !( IsParamValid(to->paramFileIndex) && + to->segCnt > 0 && + CompatibleScale( FALSE, to->scaleInx, curScaleInx ) ) ) + /*( (strcmp( to->scale, "*" ) == 0 && strcasecmp( curScaleName, "DEMO" ) != 0 ) || + strncasecmp( to->scale, curScaleName, strlen(to->scale) ) == 0 ) ) )*/ + continue; + AddHotBarElement( to->contentsLabel, to->size, to->orig, FALSE, to->barScale, to, CmdStructureHotBarProc ); + } +} + +static STATUS_T CmdStructureHotBar( + wAction_t action, + coOrd pos ) +{ + switch (action & 0xFF) { + + case C_START: + structureChange( CHANGE_PARAMS ); + if (curStructure == NULL) { + NoticeMessage( MSG_STRUCT_NO_STRUCTS, _("Ok"), NULL ); + return C_TERMINATE; + } + FormatCompoundTitle( listLabels|LABEL_DESCR, curStructure->title ); + InfoMessage( _("Place %s and draw into position"), message ); + ParamLoadControls( &structurePG ); + ParamGroupRecord( &structurePG ); + return CmdStructureAction( action, pos ); + + case C_RUP: + case C_UP: + InfoMessage( _("Left drag to move, right drag to rotate, or press Return or click Ok to finalize") ); + return CmdStructureAction( action, pos ); + + case C_TEXT: + if ((action>>8) != ' ') + return C_CONTINUE; + case C_OK: + CmdStructureAction( action, pos ); + return C_CONTINUE; + + case C_CANCEL: + HotBarCancel(); + default: + return CmdStructureAction( action, pos ); + } +} + + +#ifdef STRUCTCMD +#include "bitmaps/struct.xpm" + +EXPORT void InitCmdStruct( wMenu_p menu ) +{ + AddMenuButton( menu, CmdStructure, "cmdStructure", _("Structure"), wIconCreatePixMap(struct_xpm), LEVEL0_50, IC_STICKY|IC_CMDMENU|IC_POPUP2, ACCL_STRUCTURE, NULL ); + structureHotBarCmdInx = AddMenuButton( menu, CmdStructureHotBar, "cmdStructureHotBar", "", NULL, LEVEL0_50, IC_STICKY|IC_CMDMENU|IC_POPUP2, 0, NULL ); + ParamRegister( &structurePG ); +} +#endif + + +EXPORT void InitTrkStruct( void ) +{ + T_STRUCTURE = InitObject( &structureCmds ); + + log_structure = LogFindIndex( "Structure" ); + AddParam( "STRUCTURE ", ReadStructureParam ); + ParamRegister( &pierPG ); +} diff --git a/app/bin/cswitchmotor.c b/app/bin/cswitchmotor.c new file mode 100644 index 0000000..aae5608 --- /dev/null +++ b/app/bin/cswitchmotor.c @@ -0,0 +1,534 @@ +/* + * ------------------------------------------------------------------ + * cswitchmotor.c - Switch Motors + * Created by Robert Heller on Sat Mar 14 10:39:56 2009 + * ------------------------------------------------------------------ + * Modification History: $Log: not supported by cvs2svn $ + * Modification History: Revision 1.5 2009/11/23 19:46:16 rheller + * Modification History: Block and Switchmotor updates + * Modification History: + * Modification History: Revision 1.4 2009/09/16 18:32:24 m_fischer + * Modification History: Remove unused locals + * Modification History: + * Modification History: Revision 1.3 2009/09/05 16:40:53 m_fischer + * Modification History: Make layout control commands a build-time choice + * Modification History: + * Modification History: Revision 1.2 2009/07/08 19:13:58 m_fischer + * Modification History: Make compile under MSVC + * Modification History: + * Modification History: Revision 1.1 2009/07/08 18:40:27 m_fischer + * Modification History: Add switchmotor and block for layout control + * Modification History: + * Modification History: Revision 1.1 2002/07/28 14:03:50 heller + * Modification History: Add it copyright notice headers + * Modification History: + * ------------------------------------------------------------------ + * Contents: + * ------------------------------------------------------------------ + * + * Generic Project + * Copyright (C) 2005 Robert Heller D/B/A Deepwoods Software + * 51 Locke Hill Road + * Wendell, MA 01379-9728 + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#include <ctype.h> +#include "track.h" +#include "compound.h" +#include "i18n.h" + +EXPORT TRKTYP_T T_SWITCHMOTOR = -1; + +#define SWITCHMOTORCMD + +static int log_switchmotor = 0; + +#ifdef SWITCHMOTORCMD +static drawCmd_t switchmotorD = { + NULL, + &screenDrawFuncs, + 0, + 1.0, + 0.0, + {0.0,0.0}, {0.0,0.0}, + Pix2CoOrd, CoOrd2Pix }; + +static char switchmotorName[STR_SHORT_SIZE]; +static char switchmotorNormal[STR_LONG_SIZE]; +static char switchmotorReverse[STR_LONG_SIZE]; +static char switchmotorPointSense[STR_LONG_SIZE]; +static track_p switchmotorTurnout; + +static paramData_t switchmotorPLs[] = { +/*0*/ { PD_STRING, switchmotorName, "name", PDO_NOPREF, (void*)200, N_("Name") }, +/*1*/ { PD_STRING, switchmotorNormal, "normal", PDO_NOPREF, (void*)350, N_("Normal") }, +/*2*/ { PD_STRING, switchmotorReverse, "reverse", PDO_NOPREF, (void*)350, N_("Reverse") }, +/*3*/ { PD_STRING, switchmotorPointSense, "pointSense", PDO_NOPREF, (void*)350, N_("Point Sense") } +}; + +static paramGroup_t switchmotorPG = { "switchmotor", 0, switchmotorPLs, sizeof switchmotorPLs/sizeof switchmotorPLs[0] }; +/* +static dynArr_t switchmotorTrk_da; +#define switchmotorTrk(N) DYNARR_N( track_p , switchmotorTrk_da, N ) +*/ +static wWin_p switchmotorW; +#endif + +typedef struct switchmotorData_t { + char * name; + char * normal; + char * reverse; + char * pointsense; + track_p turnout; +} switchmotorData_t, *switchmotorData_p; + +static switchmotorData_p GetswitchmotorData ( track_p trk ) +{ + return (switchmotorData_p) GetTrkExtraData(trk); +} + +#include "bitmaps/switchmotormark.xbm" +static wDrawBitMap_p switchmotormark_bm = NULL; + +static void DrawSwitchMotor (track_p t, drawCmd_p d, wDrawColor color ) +{ + coOrd p; + switchmotorData_p data_p = GetswitchmotorData(t); + struct extraData *xx = GetTrkExtraData(data_p->turnout); + coOrd orig = xx->orig; + ANGLE_T angle = xx->angle; + + if (switchmotormark_bm == NULL) { + switchmotormark_bm = + wDrawBitMapCreate( mainD.d, + switchmotormark_width, + switchmotormark_height, 16, 16, + switchmotormark_bits); + } + Translate (&p, orig, -angle , 2 ); + Translate (&p, p, 90-angle, 2); + DrawBitMap(d, p, switchmotormark_bm, color); +} + +static struct { + char name[STR_SHORT_SIZE]; + char normal[STR_LONG_SIZE]; + char reverse[STR_LONG_SIZE]; + char pointsense[STR_LONG_SIZE]; + long turnout; +} switchmotorData; + +typedef enum { NM, NOR, REV, PS, TO } switchmotorDesc_e; +static descData_t switchmotorDesc[] = { +/*NM */ { DESC_STRING, N_("Name"), &switchmotorData.name }, +/*NOR*/ { DESC_STRING, N_("Normal"), &switchmotorData.normal }, +/*REV*/ { DESC_STRING, N_("Reverse"), &switchmotorData.reverse }, +/*PS */ { DESC_STRING, N_("Point Sense"), &switchmotorData.pointsense }, +/*TO */ { DESC_LONG, N_("Turnout"), &switchmotorData.turnout }, + { DESC_NULL } }; + +static void UpdateSwitchMotor (track_p trk, int inx, descData_p descUpd, BOOL_T needUndoStart ) +{ + switchmotorData_p xx = GetswitchmotorData(trk); + const char * thename, *thenormal, *thereverse, *thepointsense; + char *newName, *newNormal, *newReverse, *newPointSense; + BOOL_T changed, nChanged, norChanged, revChanged, psChanged; + + LOG( log_switchmotor, 1, ("*** UpdateSwitchMotor(): needUndoStart = %d\n",needUndoStart)) + if ( inx == -1 ) { + nChanged = norChanged = revChanged = psChanged = changed = FALSE; + thename = wStringGetValue( (wString_p)switchmotorDesc[NM].control0 ); + if ( strcmp( thename, xx->name ) != 0 ) { + nChanged = changed = TRUE; + newName = MyStrdup(thename); + } + thenormal = wStringGetValue( (wString_p)switchmotorDesc[NOR].control0 ); + if ( strcmp( thenormal, xx->normal ) != 0 ) { + norChanged = changed = TRUE; + newNormal = MyStrdup(thenormal); + } + thereverse = wStringGetValue( (wString_p)switchmotorDesc[REV].control0 ); + if ( strcmp( thereverse, xx->reverse ) != 0 ) { + revChanged = changed = TRUE; + newReverse = MyStrdup(thereverse); + } + thepointsense = wStringGetValue( (wString_p)switchmotorDesc[PS].control0 ); + if ( strcmp( thepointsense, xx->pointsense ) != 0 ) { + psChanged = changed = TRUE; + newPointSense = MyStrdup(thepointsense); + } + if ( ! changed ) return; + if ( needUndoStart ) + UndoStart( _("Change Switch Motor"), "Change Switch Motor" ); + UndoModify( trk ); + if (nChanged) { + MyFree(xx->name); + xx->name = newName; + } + if (norChanged) { + MyFree(xx->normal); + xx->normal = newNormal; + } + if (revChanged) { + MyFree(xx->reverse); + xx->reverse = newReverse; + } + if (psChanged) { + MyFree(xx->pointsense); + xx->pointsense = newPointSense; + } + return; + } +} + +static DIST_T DistanceSwitchMotor (track_p t, coOrd * p ) +{ + switchmotorData_p xx = GetswitchmotorData(t); + return GetTrkDistance(xx->turnout,*p); +} + +static void DescribeSwitchMotor (track_p trk, char * str, CSIZE_T len ) +{ + switchmotorData_p xx = GetswitchmotorData(trk); + long listLabelsOption = listLabels; + + LOG( log_switchmotor, 1, ("*** DescribeSwitchMotor(): trk is T%d\n",GetTrkIndex(trk))) + FormatCompoundTitle( listLabelsOption, xx->name ); + if (message[0] == '\0') + FormatCompoundTitle( listLabelsOption|LABEL_DESCR, xx->name ); + strcpy( str, _(GetTrkTypeName( trk )) ); + str++; + while (*str) { + *str = tolower(*str); + str++; + } + sprintf( str, _("(%d): Layer=%d %s"), + GetTrkIndex(trk), GetTrkLayer(trk)+1, message ); + strncpy(switchmotorData.name,xx->name,STR_SHORT_SIZE-1); + switchmotorData.name[STR_SHORT_SIZE-1] = '\0'; + strncpy(switchmotorData.normal,xx->normal,STR_LONG_SIZE-1); + switchmotorData.normal[STR_LONG_SIZE-1] = '\0'; + strncpy(switchmotorData.reverse,xx->reverse,STR_LONG_SIZE-1); + switchmotorData.reverse[STR_LONG_SIZE-1] = '\0'; + strncpy(switchmotorData.pointsense,xx->pointsense,STR_LONG_SIZE-1); + switchmotorData.pointsense[STR_LONG_SIZE-1] = '\0'; + switchmotorData.turnout = GetTrkIndex(xx->turnout); + switchmotorDesc[TO].mode = DESC_RO; + switchmotorDesc[NM].mode = + switchmotorDesc[NOR].mode = + switchmotorDesc[REV].mode = + switchmotorDesc[PS].mode = DESC_NOREDRAW; + DoDescribe(_("Switch motor"), trk, switchmotorDesc, UpdateSwitchMotor ); +} + +static switchmotorDebug (track_p trk) +{ + switchmotorData_p xx = GetswitchmotorData(trk); + LOG( log_switchmotor, 1, ("*** switchmotorDebug(): trk = %08x\n",trk)) + LOG( log_switchmotor, 1, ("*** switchmotorDebug(): Index = %d\n",GetTrkIndex(trk))) + LOG( log_switchmotor, 1, ("*** switchmotorDebug(): name = \"%s\"\n",xx->name)) + LOG( log_switchmotor, 1, ("*** switchmotorDebug(): normal = \"%s\"\n",xx->normal)) + LOG( log_switchmotor, 1, ("*** switchmotorDebug(): reverse = \"%s\"\n",xx->reverse)) + LOG( log_switchmotor, 1, ("*** switchmotorDebug(): pointsense = \"%s\"\n",xx->pointsense)) + LOG( log_switchmotor, 1, ("*** switchmotorDebug(): turnout = T%d, %s\n", + GetTrkIndex(xx->turnout), GetTrkTypeName(xx->turnout))) +} + +static void DeleteSwitchMotor ( track_p trk ) +{ + switchmotorData_p xx = GetswitchmotorData(trk); + MyFree(xx->name); xx->name = NULL; + MyFree(xx->normal); xx->normal = NULL; + MyFree(xx->reverse); xx->reverse = NULL; + MyFree(xx->pointsense); xx->pointsense = NULL; +} + +static BOOL_T WriteSwitchMotor ( track_p t, FILE * f ) +{ + BOOL_T rc = TRUE; + switchmotorData_p xx = GetswitchmotorData(t); + + rc &= fprintf(f, "SWITCHMOTOR %d %d \"%s\" \"%s\" \"%s\" \"%s\"\n", + GetTrkIndex(t), GetTrkIndex(xx->turnout), xx->name, + xx->normal, xx->reverse, xx->pointsense)>0; + return rc; +} + +static void ReadSwitchMotor ( char * line ) +{ + TRKINX_T trkindex; + wIndex_t index; + track_p trk; + switchmotorData_p xx; + char *name, *normal, *reverse, *pointsense; + + LOG( log_switchmotor, 1, ("*** ReadSwitchMotor: line is '%s'\n",line)) + if (!GetArgs(line+12,"ddqqqq",&index,&trkindex,&name,&normal,&reverse,&pointsense)) { + return; + } + trk = NewTrack(index, T_SWITCHMOTOR, 0, sizeof(switchmotorData_t)+1); + xx = GetswitchmotorData( trk ); + xx->name = name; + xx->normal = normal; + xx->reverse = reverse; + xx->pointsense = pointsense; + xx->turnout = FindTrack(trkindex); + switchmotorDebug(trk); +} + +static void MoveSwitchMotor (track_p trk, coOrd orig ) {} +static void RotateSwitchMotor (track_p trk, coOrd orig, ANGLE_T angle ) {} +static void RescaleSwitchMotor (track_p trk, FLOAT_T ratio ) {} + + +static trackCmd_t switchmotorCmds = { + "SWITCHMOTOR", + DrawSwitchMotor, + DistanceSwitchMotor, + DescribeSwitchMotor, + DeleteSwitchMotor, + WriteSwitchMotor, + ReadSwitchMotor, + MoveSwitchMotor, + RotateSwitchMotor, + RescaleSwitchMotor, + NULL, /* audit */ + NULL, /* getAngle */ + NULL, /* split */ + NULL, /* traverse */ + NULL, /* enumerate */ + NULL, /* redraw */ + NULL, /* trim */ + NULL, /* merge */ + NULL, /* modify */ + NULL, /* getLength */ + NULL, /* getTrkParams */ + NULL, /* moveEndPt */ + NULL, /* query */ + NULL, /* ungroup */ + NULL, /* flip */ + NULL, /* drawPositionIndicator */ + NULL, /* advancePositionIndicator */ + NULL, /* checkTraverse */ + NULL, /* makeParallel */ + NULL /* drawDesc */ +}; + +#ifdef SWITCHMOTORCMD +static track_p FindSwitchMotor (track_p trk) +{ + track_p a_trk; + switchmotorData_p xx; + + for (a_trk = NULL; TrackIterate( &a_trk ) ;) { + if (GetTrkType(a_trk) == T_SWITCHMOTOR) { + xx = GetswitchmotorData(a_trk); + if (xx->turnout == trk) return a_trk; + } + } + return NULL; +} + +static void SwitchMotorOk ( void * junk ) +{ + switchmotorData_p xx; + track_p trk; + + LOG( log_switchmotor, 1, ("*** SwitchMotorOk()\n")) + ParamUpdate (&switchmotorPG ); + if ( switchmotorName[0]==0 ) { + NoticeMessage( 0, "Switch motor must have a name!", _("Ok")); + return; + } + wDrawDelayUpdate( mainD.d, TRUE ); + UndoStart( _("Create Switch Motor"), "Create Switch Motor" ); + /* Create a switchmotor object */ + trk = NewTrack(0, T_SWITCHMOTOR, 0, sizeof(switchmotorData_t)+1); + xx = GetswitchmotorData( trk ); + xx->name = MyStrdup(switchmotorName); + xx->normal = MyStrdup(switchmotorNormal); + xx->reverse = MyStrdup(switchmotorReverse); + xx->pointsense = MyStrdup(switchmotorPointSense); + xx->turnout = switchmotorTurnout; + switchmotorDebug(trk); + UndoEnd(); + wHide( switchmotorW ); +} + +static void NewSwitchMotorDialog(track_p trk) +{ + LOG( log_switchmotor, 1, ("*** NewSwitchMotorDialog()\n")) + + switchmotorTurnout = trk; + if ( log_switchmotor < 0 ) log_switchmotor = LogFindIndex( "switchmotor" ); + if ( !switchmotorW ) { + ParamRegister( &switchmotorPG ); + switchmotorW = ParamCreateDialog (&switchmotorPG, MakeWindowTitle(_("Create switch motor")), _("Ok"), SwitchMotorOk, wHide, TRUE, NULL, F_BLOCK, NULL ); + switchmotorD.dpi = mainD.dpi; + } + ParamLoadControls( &switchmotorPG ); + wShow( switchmotorW ); +} + +static STATUS_T CmdSwitchMotorCreate( wAction_t action, coOrd pos ) +{ + track_p trk; + + LOG( log_switchmotor, 1, ("*** CmdSwitchMotorCreate(%08x,{%f,%f})\n",action,pos.x,pos.y)) + switch (action & 0xFF) { + case C_START: + InfoMessage( _("Select a turnout") ); + return C_CONTINUE; + case C_DOWN: + if ((trk = OnTrack(&pos, TRUE, TRUE )) == NULL) { + return C_CONTINUE; + } + if (GetTrkType( trk ) != T_TURNOUT) { + ErrorMessage( _("Not a turnout!") ); + return C_CONTINUE; + } + NewSwitchMotorDialog(trk); + return C_CONTINUE; + case C_REDRAW: + return C_CONTINUE; + case C_CANCEL: + return C_TERMINATE; + default: + return C_CONTINUE; + } +} + +extern BOOL_T inDescribeCmd; + +static STATUS_T CmdSwitchMotorEdit( wAction_t action, coOrd pos ) +{ + track_p trk,btrk; + char msg[STR_SIZE]; + + switch (action) { + case C_START: + InfoMessage( _("Select a turnout") ); + inDescribeCmd = TRUE; + return C_CONTINUE; + case C_DOWN: + if ((trk = OnTrack(&pos, TRUE, TRUE )) == NULL) { + return C_CONTINUE; + } + btrk = FindSwitchMotor( trk ); + if ( !btrk ) { + ErrorMessage( _("Not a switch motor!") ); + return C_CONTINUE; + } + DescribeTrack (btrk, msg, sizeof msg ); + InfoMessage( msg ); + return C_CONTINUE; + case C_REDRAW: + return C_CONTINUE; + case C_CANCEL: + inDescribeCmd = FALSE; + return C_TERMINATE; + default: + return C_CONTINUE; + } +} + +static STATUS_T CmdSwitchMotorDelete( wAction_t action, coOrd pos ) +{ + track_p trk,btrk; + switchmotorData_p xx; + + switch (action) { + case C_START: + InfoMessage( _("Select a turnout") ); + return C_CONTINUE; + case C_DOWN: + if ((trk = OnTrack(&pos, TRUE, TRUE )) == NULL) { + return C_CONTINUE; + } + btrk = FindSwitchMotor( trk ); + if ( !btrk ) { + ErrorMessage( _("Not a switch motor!") ); + return C_CONTINUE; + } + /* Confirm Delete SwitchMotor */ + xx = GetswitchmotorData(btrk); + if ( NoticeMessage( _("Really delete switch motor %s?"), _("Yes"), _("No"), xx->name) ) { + UndoStart( _("Delete Switch Motor"), "delete" ); + DeleteTrack (btrk, FALSE); + UndoEnd(); + return C_TERMINATE; + } + return C_CONTINUE; + case C_REDRAW: + return C_CONTINUE; + case C_CANCEL: + return C_TERMINATE; + default: + return C_CONTINUE; + } +} + + + +#define SWITCHMOTOR_CREATE 0 +#define SWITCHMOTOR_EDIT 1 +#define SWITCHMOTOR_DELETE 2 + +static STATUS_T CmdSwitchMotor (wAction_t action, coOrd pos ) +{ + + LOG( log_switchmotor, 1, ("*** CmdSwitchMotor(%08x,{%f,%f})\n",action,pos.x,pos.y)) + + switch ((long)commandContext) { + case SWITCHMOTOR_CREATE: return CmdSwitchMotorCreate(action,pos); + case SWITCHMOTOR_EDIT: return CmdSwitchMotorEdit(action,pos); + case SWITCHMOTOR_DELETE: return CmdSwitchMotorDelete(action,pos); + default: return C_TERMINATE; + } +} + +//#include "bitmaps/switchmotor.xpm" + +#include "bitmaps/switchmnew.xpm" +#include "bitmaps/switchmedit.xpm" +#include "bitmaps/switchmdel.xpm" + +EXPORT void InitCmdSwitchMotor( wMenu_p menu ) +{ + switchmotorName[0] = '\0'; + switchmotorNormal[0] = '\0'; + switchmotorReverse[0] = '\0'; + switchmotorPointSense[0] = '\0'; + ButtonGroupBegin( _("SwitchMotor"), "cmdSwitchMotorSetCmd", _("Switch Motors") ); + AddMenuButton( menu, CmdSwitchMotor, "cmdSwitchMotorCreate", _("Create Switch Motor"), wIconCreatePixMap(switchmnew_xpm), LEVEL0_50, IC_CANCEL|IC_POPUP, ACCL_SWITCHMOTOR1, (void*)SWITCHMOTOR_CREATE ); + AddMenuButton( menu, CmdSwitchMotor, "cmdSwitchMotorEdit", _("Edit Switch Motor"), wIconCreatePixMap(switchmedit_xpm), LEVEL0_50, IC_CANCEL|IC_POPUP, ACCL_SWITCHMOTOR2, (void*)SWITCHMOTOR_EDIT ); + AddMenuButton( menu, CmdSwitchMotor, "cmdSwitchMotorDelete", _("Delete Switch Motor"), wIconCreatePixMap(switchmdel_xpm), LEVEL0_50, IC_CANCEL|IC_POPUP, ACCL_SWITCHMOTOR3, (void*)SWITCHMOTOR_DELETE ); + ButtonGroupEnd(); + ParamRegister( &switchmotorPG ); +} +#endif + + +EXPORT void InitTrkSwitchMotor( void ) +{ + T_SWITCHMOTOR = InitObject ( &switchmotorCmds ); + log_switchmotor = LogFindIndex ( "switchmotor" ); +} + + diff --git a/app/bin/ctext.c b/app/bin/ctext.c new file mode 100644 index 0000000..0779ef5 --- /dev/null +++ b/app/bin/ctext.c @@ -0,0 +1,259 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/ctext.c,v 1.4 2008-03-06 19:35:06 m_fischer Exp $ + * + * TEXT + * + */ + +/* 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 "track.h" +#include "i18n.h" + + +track_p NewText( wIndex_t index, coOrd p, ANGLE_T angle, char * text, CSIZE_T textSize, wDrawColor color ); + +void LoadFontSizeList( wList_p, long ); +void UpdateFontSizeList( long *, wList_p, wIndex_t ); + +static wMenu_p textPopupM; + +/***************************************************************************** + * TEXT COMMAND + */ + +static struct { + STATE_T state; + CSIZE_T len; + coOrd cursPos0, cursPos1; + POS_T cursHeight; + POS_T textLen; + coOrd pos; + ANGLE_T angle; + long size; + wIndex_t fontSizeInx; + char text[STR_SIZE]; + wDrawColor color; + } Dt; + +static paramData_t textPLs[] = { +#define textPD (textPLs[0]) + { PD_DROPLIST, &Dt.fontSizeInx, "fontsize", 0, NULL, N_("Font Size"), BL_EDITABLE }, +#define colorPD (textPLs[1]) + { PD_COLORLIST, &Dt.color, "color", PDO_NORECORD, NULL, N_("Color") } + }; +static paramGroup_t textPG = { "text", 0, textPLs, sizeof textPLs/sizeof textPLs[0] }; + + +static void TextDlgUpdate( + paramGroup_p pg, + int inx, + void * context ) +{ + coOrd size; + + switch (inx) { + case 0: + if ( Dt.state == 1 ) { + DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color ); + DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); + } + UpdateFontSizeList( &Dt.size, (wList_p)textPLs[0].control, Dt.fontSizeInx ); + /*wWinSetBusy( mainW, TRUE );*/ + if ( Dt.state == 1 ) { + DrawTextSize( &mainD, Dt.text, NULL, Dt.size, TRUE, &size ); + Dt.textLen = size.x; + } + DrawTextSize( &mainD, "X", NULL, Dt.size, TRUE, &size ); + Dt.cursHeight = size.y; + /*wWinSetBusy( mainW, FALSE );*/ + if ( Dt.state == 1 ) { + Dt.cursPos0.x = Dt.cursPos1.x = Dt.pos.x+Dt.textLen; + Dt.cursPos1.y = Dt.pos.y+Dt.cursHeight; + DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); + DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color ); + } + MainRedraw(); + break; + } +} + + +static STATUS_T CmdText( wAction_t action, coOrd pos ) +{ + track_p t; + unsigned char c; + wControl_p controls[3]; + char * labels[2]; + coOrd size; + + switch (action & 0xFF) { + case C_START: + /* check if font size was updated by the preferences dialog */ + Dt.size = (CSIZE_T)wSelectedFontSize(); + Dt.state = 0; + Dt.cursPos0 = Dt.cursPos1 = zero; + Dt.len = 0; + Dt.textLen = 0; + Dt.text[0] = '\0'; + if ( !inPlayback ) + wWinSetBusy( mainW, TRUE ); + DrawTextSize( &mainD, "X", NULL, Dt.size, TRUE, &size ); + Dt.cursHeight = size.y; + if ( !inPlayback ) + wWinSetBusy( mainW, FALSE ); + if ( textPD.control==NULL ) { + ParamCreateControls( &textPG, TextDlgUpdate ); + } + LoadFontSizeList( (wList_p)textPD.control, Dt.size ); + ParamGroupRecord( &textPG ); + controls[0] = textPD.control; + controls[1] = colorPD.control; + controls[2] = 0; + labels[0] = N_("Font Size"); + labels[1] = N_("Color"); + InfoSubstituteControls( controls, labels ); + return C_CONTINUE; + break; + case C_DOWN: + if (Dt.state != 0) { + //DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); + //DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color ); + } + Dt.pos = pos; + Dt.cursPos0.y = Dt.cursPos1.y = pos.y; + Dt.cursPos0.x = Dt.cursPos1.x = pos.x + Dt.textLen; + Dt.cursPos1.y += Dt.cursHeight; + DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); + DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color ); + Dt.state = 1; + MainRedraw(); + return C_CONTINUE; + case C_MOVE: + //DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); + //DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color ); + Dt.pos = pos; + Dt.cursPos0.y = Dt.cursPos1.y = pos.y; + Dt.cursPos0.x = Dt.cursPos1.x = pos.x + Dt.textLen; + Dt.cursPos1.y += Dt.cursHeight; + DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, wDrawColorBlack ); + DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color ); + MainRedraw(); + return C_CONTINUE; + case C_UP: + return C_CONTINUE; + case C_TEXT: + if (Dt.state == 0) { + NoticeMessage( MSG_SEL_POS_FIRST, _("Ok"), NULL ); + return C_CONTINUE; + } + DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); + DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color ); + c = (unsigned char)(action >> 8); +/*lprintf("C=%x\n", c);*/ + switch (c) { + case '\b': + case 0xFF: + if (Dt.len > 0) { + Dt.len--; + Dt.text[Dt.len] = '\000'; + } else { + wBeep(); + } + break; + case '\015': + UndoStart( _("Create Text"), "newText - CR" ); + t = NewText( 0, Dt.pos, Dt.angle, Dt.text, (CSIZE_T)Dt.size, Dt.color ); + UndoEnd(); + DrawNewTrack(t); + Dt.state = 0; + InfoSubstituteControls( NULL, NULL ); + return C_TERMINATE; + default: + if (Dt.len < sizeof Dt.text - 1 ) { + Dt.text[Dt.len++] = (char)c; + Dt.text[Dt.len] = '\000'; + } + } + DrawTextSize( &mainD, Dt.text, NULL, Dt.size, TRUE, &size ); + Dt.textLen = size.x; + Dt.cursPos0.x = Dt.cursPos1.x = Dt.pos.x + Dt.textLen; + DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); + DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color ); + return C_CONTINUE; + case C_REDRAW: + if (Dt.state == 1) { + DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); + DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color ); + } + return C_CONTINUE; + case C_CANCEL: + if (Dt.state != 0) { + //DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color ); + //DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); + Dt.state = 0; + } + InfoSubstituteControls( NULL, NULL ); + MainRedraw(); + return C_TERMINATE; + case C_OK: + if (Dt.state != 0) { + DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); + Dt.state = 0; + if (Dt.len) { + UndoStart( _("Create Text"), "newText - OK" ); + t = NewText( 0, Dt.pos, Dt.angle, Dt.text, (CSIZE_T)Dt.size, Dt.color ); + UndoEnd(); + DrawNewTrack(t); + } + } + InfoSubstituteControls( NULL, NULL ); + MainRedraw(); + return C_TERMINATE; + + case C_FINISH: + if (Dt.state != 0 && Dt.len > 0) + CmdText( C_OK, pos ); + else + CmdText( C_CANCEL, pos ); + return C_TERMINATE; + + case C_CMDMENU: + wMenuPopupShow( textPopupM ); + return C_CONTINUE; + } + return C_CONTINUE; +} + + +#include "bitmaps/text.xpm" + +void InitCmdText( wMenu_p menu ) +{ + AddMenuButton( menu, CmdText, "cmdText", _("Text"), wIconCreatePixMap(text_xpm), LEVEL0_50, IC_STICKY|IC_CMDMENU|IC_POPUP2, ACCL_TEXT, NULL ); + textPopupM = MenuRegister( "Text Font" ); + wMenuPushCreate( textPopupM, "", _("Fonts..."), 0, (wMenuCallBack_p)SelectFont, NULL ); + Dt.size = (CSIZE_T)wSelectedFontSize(); + Dt.color = wDrawColorBlack; + ParamRegister( &textPG ); +} + +void InitTrkText( void ) +{ +} diff --git a/app/bin/ctodesgn.c b/app/bin/ctodesgn.c new file mode 100644 index 0000000..e3c1b8e --- /dev/null +++ b/app/bin/ctodesgn.c @@ -0,0 +1,2539 @@ +/* \file ctodesgn.c + * T_TURNOUT Designer + * + */ + +/* 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. + */ + +#ifdef WINDOWS +#include <stdlib.h> +#endif + +#include <stdint.h> + +#include <ctype.h> +#include "track.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "compound.h" +#include "i18n.h" + +#define TURNOUTDESIGNER "CTURNOUT DESIGNER" + + + +/***************************************** + * + * TURNOUT DESIGNER + * + */ + + +#define NTO_REGULAR (1) +#define NTO_CURVED (2) +#define NTO_WYE (3) +#define NTO_3WAY (4) +#define NTO_CROSSING (5) +#define NTO_S_SLIP (6) +#define NTO_D_SLIP (7) +#define NTO_R_CROSSOVER (8) +#define NTO_L_CROSSOVER (9) +#define NTO_D_CROSSOVER (10) +#define NTO_STR_SECTION (11) +#define NTO_CRV_SECTION (12) +#define NTO_BUMPER (13) +#define NTO_TURNTABLE (14) + +#define FLOAT (1) + + +typedef struct { + struct { + wPos_t x, y; + } pos; + int index; + char * winLabel; + char * printLabel; + enum { Dim_e, Frog_e, Angle_e } mode; + } toDesignFloat_t; + +typedef struct { + PATHPTR_T paths; + char * segOrder; + } toDesignSchema_t; + +typedef struct { + int type; + char * label; + int strCnt; + int lineCnt; + wLines_t * lines; + int floatCnt; + toDesignFloat_t * floats; + toDesignSchema_t * paths; + int angleModeCnt; + wLine_p lineC; + } toDesignDesc_t; + +static wWin_p newTurnW; +static FLOAT_T newTurnLen0; +static FLOAT_T newTurnLen1; +static FLOAT_T newTurnOff1; +static FLOAT_T newTurnAngle1; +static FLOAT_T newTurnLen2; +static FLOAT_T newTurnOff2; +static FLOAT_T newTurnAngle2; +static long newTurnAngleMode = 1; +static char newTurnRightDesc[STR_SIZE], newTurnLeftDesc[STR_SIZE]; +static char newTurnRightPartno[STR_SIZE], newTurnLeftPartno[STR_SIZE]; +static char newTurnManufacturer[STR_SIZE]; +static char *newTurnAngleModeLabels[] = { N_("Frog #"), N_("Degrees"), NULL }; +static DIST_T newTurnRoadbedWidth; +static long newTurnRoadbedLineWidth = 0; +static wDrawColor roadbedColor; +static DIST_T newTurnTrackGauge; +static char * newTurnScaleName; +static paramFloatRange_t r0_10000 = { 0, 10000, 80 }; +static paramFloatRange_t r0_360 = { 0, 360, 80 }; +static paramFloatRange_t r0_100 = { 0, 100, 80 }; +static paramIntegerRange_t i0_100 = { 0, 100, 40 }; +static void NewTurnOk( void * ); +static void ShowTurnoutDesigner( void * ); + + +static coOrd points[20]; +static DIST_T radii[10] = { 0.0 }; + +#define POSX(X) ((wPos_t)((X)*newTurnout_d.dpi)) +#define POSY(Y) ((wPos_t)((Y)*newTurnout_d.dpi)) + +static paramData_t turnDesignPLs[] = { +#define I_TOLENGTH (0) +#define I_TO_FIRST_FLOAT (0) + { PD_FLOAT, &newTurnLen1, "len1", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r0_10000, N_("Length") }, + { PD_FLOAT, &newTurnLen2, "len2", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r0_10000, N_("Length") }, + { PD_FLOAT, &newTurnLen0, "len0", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r0_10000, N_("Length") }, +#define I_TOOFFSET (3) + { PD_FLOAT, &newTurnOff1, "off1", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r0_10000, N_("Offset") }, + { PD_FLOAT, &newTurnOff2, "off2", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r0_10000, N_("Offset") }, +#define I_TOANGLE (5) + { PD_FLOAT, &newTurnAngle1, "angle1", PDO_DLGIGNORELABELWIDTH, &r0_360, N_("Angle") }, +#define I_TO_LAST_FLOAT (6) + { PD_FLOAT, &newTurnAngle2, "angle2", PDO_DLGIGNORELABELWIDTH, &r0_360, N_("Angle") }, +#define I_TOMANUF (7) + { PD_STRING, &newTurnManufacturer, "manuf", 0, NULL, N_("Manufacturer") }, +#define I_TOLDESC (8) + { PD_STRING, &newTurnLeftDesc, "desc1", 0, NULL, N_("Left Description") }, + { PD_STRING, &newTurnLeftPartno, "partno1", PDO_DLGHORZ, NULL, N_(" #") }, +#define I_TORDESC (10) + { PD_STRING, &newTurnRightDesc, "desc2", 0, NULL, N_("Right Description") }, + { PD_STRING, &newTurnRightPartno, "partno2", PDO_DLGHORZ, NULL, N_(" #") }, + { PD_FLOAT, &newTurnRoadbedWidth, "roadbedWidth", PDO_DIM, &r0_100, N_("Roadbed Width") }, + { PD_LONG, &newTurnRoadbedLineWidth, "roadbedLineWidth", PDO_DLGHORZ, &i0_100, N_("Line Width") }, + { PD_COLORLIST, &roadbedColor, "color", PDO_DLGHORZ|PDO_DLGBOXEND, NULL, N_("Color") }, + { PD_BUTTON, (void*)NewTurnOk, "done", PDO_DLGCMDBUTTON, NULL, N_("Ok") }, + { PD_BUTTON, (void*)wPrintSetup, "printsetup", 0, NULL, N_("Print Setup") }, +#define I_TOANGMODE (17) + { PD_RADIO, &newTurnAngleMode, "angleMode", 0, newTurnAngleModeLabels } + }; + +#ifndef MKTURNOUT +static paramGroup_t turnDesignPG = { "turnoutNew", 0, turnDesignPLs, sizeof turnDesignPLs/sizeof turnDesignPLs[0] }; + +static turnoutInfo_t * customTurnout1, * customTurnout2; +static BOOL_T includeNontrackSegments; +#endif + +#ifdef MKTURNOUT +int doCustomInfoLine = 1; +int doRoadBed = 0; +char specialLine[256]; +#endif + +static toDesignDesc_t * curDesign; + +/* + * Regular Turnouts + */ + + +static wLines_t RegLines[] = { +#include "toreg.lin" + }; +static toDesignFloat_t RegFloats[] = { +{ { 175, 10 }, I_TOLENGTH+0, N_("Length"), N_("Diverging Length"), Dim_e }, +{ { 400, 28 }, I_TOANGLE+0, N_("Angle"), N_("Diverging Angle"), Frog_e }, +{ { 325, 68 }, I_TOOFFSET+0, N_("Offset"), N_("Diverging Offset"), Dim_e }, +{ { 100, 120 }, I_TOLENGTH+2, N_("Length"), N_("Overall Length"), Dim_e }, + }; +static signed char RegPaths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 2, 0, 0, + 'R', 'e', 'v', 'e', 'r', 's', 'e', 0, 1, 3, 4, 0, 0, 0 }; +static toDesignSchema_t RegSchema = { + RegPaths, + "030" "310" "341" "420" }; +static toDesignDesc_t RegDesc = { + NTO_REGULAR, + N_("Regular Turnout"), + 2, + sizeof RegLines/sizeof RegLines[0], RegLines, + sizeof RegFloats/sizeof RegFloats[0], RegFloats, + &RegSchema, 1 }; + +static wLines_t CrvLines[] = { +#include "tocrv.lin" + }; +static toDesignFloat_t CrvFloats[] = { +{ { 175, 10 }, I_TOLENGTH+0, N_("Length"), N_("Inner Length"), Dim_e }, +{ { 375, 12 }, I_TOANGLE+0, N_("Angle"), N_("Inner Angle"), Frog_e }, +{ { 375, 34 }, I_TOOFFSET+0, N_("Offset"), N_("Inner Offset"), Dim_e }, +{ { 400, 62 }, I_TOANGLE+1, N_("Angle"), N_("Outer Angle"), Frog_e }, +{ { 400, 84 }, I_TOOFFSET+1, N_("Offset"), N_("Outer Offset"), Dim_e }, +{ { 175, 120 }, I_TOLENGTH+1, N_("Length"), N_("Outer Length"), Dim_e } }; +static signed char Crv1Paths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 4, 5, 0, 0, + 'R', 'e', 'v', 'e', 'r', 's', 'e', 0, 1, 2, 3, 0, 0, 0 }; +static toDesignSchema_t Crv1Schema = { + Crv1Paths, + "030" "341" "410" "362" "620" }; +static signed char Crv2Paths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 4, 5, 0, 0, + 'R', 'e', 'v', 'e', 'r', 's', 'e', 0, 1, 6, 2, 3, 0, 0, 0 }; +static toDesignSchema_t Crv2Schema = { + Crv2Paths, + "050" "341" "410" "562" "620" "530" }; +static signed char Crv3Paths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 6, 4, 5, 0, 0, + 'R', 'e', 'v', 'e', 'r', 's', 'e', 0, 1, 2, 3, 0, 0, 0 }; +static toDesignSchema_t Crv3Schema = { + Crv3Paths, + "030" "341" "410" "562" "620" "350" }; + +static toDesignDesc_t CrvDesc = { + NTO_CURVED, + N_("Curved Turnout"), + 2, + sizeof CrvLines/sizeof CrvLines[0], CrvLines, + sizeof CrvFloats/sizeof CrvFloats[0], CrvFloats, + &Crv1Schema, 1 }; + + +static wLines_t WyeLines[] = { +#include "towye.lin" + }; +static toDesignFloat_t WyeFloats[] = { +{ { 175, 10 }, I_TOLENGTH+0, N_("Length"), N_("Left Length"), Dim_e }, +{ { 400, 28 }, I_TOANGLE+0, N_("Angle"), N_("Left Angle"), Frog_e }, +{ { 325, 68 }, I_TOOFFSET+0, N_("Offset"), N_("Left Offset"), Dim_e }, +{ { 325, 115 }, I_TOOFFSET+1, N_("Offset"), N_("Right Offset"), Dim_e }, +{ { 400, 153 }, I_TOANGLE+1, N_("Angle"), N_("Right Angle"), Frog_e }, +{ { 175, 170 }, I_TOLENGTH+1, N_("Length"), N_("Right Length"), Dim_e }, + }; +static signed char Wye1Paths[] = { + 'L', 'e', 'f', 't', 0, 1, 2, 3, 0, 0, + 'R', 'i', 'g', 'h', 't', 0, 1, 4, 5, 0, 0, 0 }; +static toDesignSchema_t Wye1Schema = { + Wye1Paths, + "030" "341" "410" "362" "620" }; +static signed char Wye2Paths[] = { + 'L', 'e', 'f', 't', 0, 1, 2, 3, 4, 0, 0, + 'R', 'i', 'g', 'h', 't', 0, 1, 5, 6, 0, 0, 0 }; +static toDesignSchema_t Wye2Schema = { + Wye2Paths, + "050" "530" "341" "410" "562" "620" }; +static signed char Wye3Paths[] = { + 'L', 'e', 'f', 't', 0, 1, 2, 3, 0, 0, + 'R', 'i', 'g', 'h', 't', 0, 1, 4, 5, 6, 0, 0, 0 }; +static toDesignSchema_t Wye3Schema = { + Wye3Paths, + "030" "341" "410" "350" "562" "620" }; +static toDesignDesc_t WyeDesc = { + NTO_WYE, + N_("Wye Turnout"), + 1, + sizeof WyeLines/sizeof WyeLines[0], WyeLines, + sizeof WyeFloats/sizeof WyeFloats[0], WyeFloats, + NULL, 1 }; + +static wLines_t ThreewayLines[] = { +#include "to3way.lin" + }; +static toDesignFloat_t ThreewayFloats[] = { +{ { 175, 10 }, I_TOLENGTH+0, N_("Length"), N_("Left Length"), Dim_e }, +{ { 400, 28 }, I_TOANGLE+0, N_("Angle"), N_("Left Angle"), Frog_e }, +{ { 325, 68 }, I_TOOFFSET+0, N_("Offset"), N_("Left Offset"), Dim_e }, +{ { 100, 90 }, I_TOLENGTH+2, N_("Length"), N_("Length"), Dim_e }, +{ { 325, 115 }, I_TOOFFSET+1, N_("Offset"), N_("Right Offset"), Dim_e }, +{ { 400, 153 }, I_TOANGLE+1, N_("Angle"), N_("Right Angle"), Frog_e }, +{ { 175, 170 }, I_TOLENGTH+1, N_("Length"), N_("Right Length"), Dim_e }, + }; +static signed char Tri1Paths[] = { + 'L', 'e', 'f', 't', 0, 1, 2, 3, 0, 0, + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 6, 0, 0, + 'R', 'i', 'g', 'h', 't', 0, 1, 4, 5, 0, 0, 0 }; +static toDesignSchema_t Tri1Schema = { + Tri1Paths, + "030" "341" "410" "362" "620" "370" }; +static signed char Tri2Paths[] = { + 'L', 'e', 'f', 't', 0, 1, 2, 3, 4, 0, 0, + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 2, 7, 0, 0, + 'R', 'i', 'g', 'h', 't', 0, 1, 5, 6, 0, 0, 0 }; +static toDesignSchema_t Tri2Schema = { + Tri2Paths, + "050" "530" "341" "410" "562" "620" "370" }; +static signed char Tri3Paths[] = { + 'L', 'e', 'f', 't', 0, 1, 2, 3, 0, 0, + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 4, 7, 0, 0, + 'R', 'i', 'g', 'h', 't', 0, 1, 4, 5, 6, 0, 0, 0 }; +static toDesignSchema_t Tri3Schema = { + Tri3Paths, + "030" "341" "410" "350" "562" "620" "570" }; +static toDesignDesc_t ThreewayDesc = { + NTO_3WAY, + N_("3-way Turnout"), + 1, + sizeof ThreewayLines/sizeof ThreewayLines[0], ThreewayLines, + sizeof ThreewayFloats/sizeof ThreewayFloats[0], ThreewayFloats, + NULL, 1 }; + +static wLines_t CrossingLines[] = { +#include "toxing.lin" + }; +static toDesignFloat_t CrossingFloats[] = { +{ { 329, 30 }, I_TOLENGTH+0, N_("Length"), N_("Length"), Dim_e }, +{ { 370, 90 }, I_TOANGLE+0, N_("Angle"), N_("Angle"), Frog_e }, +{ { 329, 150 }, I_TOLENGTH+1, N_("Length"), N_("Length"), Dim_e } }; +static signed char CrossingPaths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 0, 2, 0, 0, 0 }; +static toDesignSchema_t CrossingSchema = { + CrossingPaths, + "010" "230" }; +static toDesignDesc_t CrossingDesc = { + NTO_CROSSING, + N_("Crossing"), + 1, + sizeof CrossingLines/sizeof CrossingLines[0], CrossingLines, + sizeof CrossingFloats/sizeof CrossingFloats[0], CrossingFloats, + &CrossingSchema, 1 }; + +static wLines_t SingleSlipLines[] = { +#include "tosslip.lin" + }; +static toDesignFloat_t SingleSlipFloats[] = { +{ { 329, 30 }, I_TOLENGTH+0, N_("Length"), N_("Length"), Dim_e }, +{ { 370, 90 }, I_TOANGLE+0, N_("Angle"), N_("Angle"), Frog_e }, +{ { 329, 155 }, I_TOLENGTH+1, N_("Length"), N_("Length"), Dim_e } }; +static signed char SingleSlipPaths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 2, 0, 3, 4, 0, 0, + 'R', 'e', 'v', 'e', 'r', 's', 'e', 0, 1, 5, 4, 0, 0, 0 }; +static toDesignSchema_t SingleSlipSchema = { + SingleSlipPaths, + "040" "410" "250" "530" "451" }; +static toDesignDesc_t SingleSlipDesc = { + NTO_S_SLIP, + N_("Single Slipswitch"), + 1, + sizeof SingleSlipLines/sizeof SingleSlipLines[0], SingleSlipLines, + sizeof SingleSlipFloats/sizeof SingleSlipFloats[0], SingleSlipFloats, + &SingleSlipSchema, 1 }; + +static wLines_t DoubleSlipLines[] = { +#include "todslip.lin" + }; +static toDesignFloat_t DoubleSlipFloats[] = { +{ { 329, 30 }, I_TOLENGTH+0, N_("Length"), N_("Length"), Dim_e }, +{ { 370, 90 }, I_TOANGLE+0, N_("Angle"), N_("Angle"), Frog_e }, +{ { 329, 155 }, I_TOLENGTH+1, N_("Length"), N_("Length"), Dim_e } }; +static signed char DoubleSlipPaths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 2, 3, 0, 4, 5, 6, 0, 0, + 'R', 'e', 'v', 'e', 'r', 's', 'e', 0, 1, 7, 6, 0, 4, 8, 3, 0, 0, 0 }; +static toDesignSchema_t DoubleSlipSchema = { + DoubleSlipPaths, + "040" "460" "610" "270" "750" "530" "451" "762" }; +static toDesignDesc_t DoubleSlipDesc = { + NTO_D_SLIP, + N_("Double Slipswitch"), + 1, + sizeof DoubleSlipLines/sizeof DoubleSlipLines[0], DoubleSlipLines, + sizeof DoubleSlipFloats/sizeof DoubleSlipFloats[0], DoubleSlipFloats, + &DoubleSlipSchema, 1 }; + +static wLines_t RightCrossoverLines[] = { +#include "torcross.lin" + }; +static toDesignFloat_t RightCrossoverFloats[] = { +{ { 200, 10 }, I_TOLENGTH+0, N_("Length"), N_("Length"), Dim_e }, +{ { 90, 85 }, I_TOOFFSET+0, N_("Separation"), N_("Separation"), Dim_e } }; +static signed char RightCrossoverPaths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 2, 0, 3, 4, 0, 0, + 'R', 'e', 'v', 'e', 'r', 's', 'e', 0, 3, 5, 6, 7, 2, 0, 0, 0 }; +static toDesignSchema_t RightCrossoverSchema = { + RightCrossoverPaths, + "060" "610" "280" "830" "892" "970" "761" }; +static toDesignDesc_t RightCrossoverDesc = { + NTO_R_CROSSOVER, + N_("Right Crossover"), + 1, + sizeof RightCrossoverLines/sizeof RightCrossoverLines[0], RightCrossoverLines, + sizeof RightCrossoverFloats/sizeof RightCrossoverFloats[0], RightCrossoverFloats, + &RightCrossoverSchema, 0 }; + +static wLines_t LeftCrossoverLines[] = { +#include "tolcross.lin" + }; +static toDesignFloat_t LeftCrossoverFloats[] = { +{ { 200, 10 }, I_TOLENGTH+0, N_("Length"), N_("Length"), Dim_e }, +{ { 90, 85 }, I_TOOFFSET+0, N_("Separation"), N_("Separation"), Dim_e } }; +static signed char LeftCrossoverPaths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 2, 0, 3, 4, 0, 0, + 'R', 'e', 'v', 'e', 'r', 's', 'e', 0, 1, 5, 6, 7, 4, 0, 0, 0 }; +static toDesignSchema_t LeftCrossoverSchema = { + LeftCrossoverPaths, + "040" "410" "2A0" "A30" "451" "5B0" "BA2" }; +static toDesignDesc_t LeftCrossoverDesc = { + NTO_L_CROSSOVER, + N_("Left Crossover"), + 1, + sizeof LeftCrossoverLines/sizeof LeftCrossoverLines[0], LeftCrossoverLines, + sizeof LeftCrossoverFloats/sizeof LeftCrossoverFloats[0], LeftCrossoverFloats, + &LeftCrossoverSchema, 0 }; + +static wLines_t DoubleCrossoverLines[] = { +#include "todcross.lin" + }; +static toDesignFloat_t DoubleCrossoverFloats[] = { +{ { 200, 10 }, I_TOLENGTH+0, N_("Length"), N_("Length"), Dim_e }, +{ { 90, 85 }, I_TOOFFSET+0, N_("Separation"), N_("Separation"), Dim_e } }; +static signed char DoubleCrossoverPaths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 2, 3, 0, 4, 5, 6, 0, 0, + 'R', 'e', 'v', 'e', 'r', 's', 'e', 0, 1, 7, 8, 9, 6, 0, 4, 10, 11, 12, 3, 0, 0, 0 }; +static toDesignSchema_t DoubleCrossoverSchema = { + DoubleCrossoverPaths, + "040" "460" "610" "280" "8A0" "A30" "451" "5B0" "BA2" "892" "970" "761" }; +static toDesignDesc_t DoubleCrossoverDesc = { + NTO_D_CROSSOVER, + N_("Double Crossover"), + 1, + sizeof DoubleCrossoverLines/sizeof DoubleCrossoverLines[0], DoubleCrossoverLines, + sizeof DoubleCrossoverFloats/sizeof DoubleCrossoverFloats[0], DoubleCrossoverFloats, + &DoubleCrossoverSchema, 0 }; + +static wLines_t StrSectionLines[] = { +#include "tostrsct.lin" + }; +static toDesignFloat_t StrSectionFloats[] = { +{ { 200, 10 }, I_TOLENGTH+0, N_("Length"), N_("Length"), Dim_e } }; +static signed char StrSectionPaths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 0, 0, 0 }; +static toDesignSchema_t StrSectionSchema = { + StrSectionPaths, + "010" }; +static toDesignDesc_t StrSectionDesc = { + NTO_STR_SECTION, + N_("Straight Section"), + 1, + sizeof StrSectionLines/sizeof StrSectionLines[0], StrSectionLines, + sizeof StrSectionFloats/sizeof StrSectionFloats[0], StrSectionFloats, + &StrSectionSchema, 0 }; + +static wLines_t CrvSectionLines[] = { +#include "tocrvsct.lin" + }; +static toDesignFloat_t CrvSectionFloats[] = { +{ { 225, 90 }, I_TOLENGTH+0, N_("Radius"), N_("Radius"), Dim_e }, +{ { 225, 140}, I_TOANGLE+0, N_("Angle (Degrees)"), N_("Angle"), Angle_e } }; +static signed char CrvSectionPaths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 0, 0, 0 }; +static toDesignSchema_t CrvSectionSchema = { + CrvSectionPaths, + "011" }; +static toDesignDesc_t CrvSectionDesc = { + NTO_CRV_SECTION, + N_("Curved Section"), + 1, + sizeof CrvSectionLines/sizeof CrvSectionLines[0], CrvSectionLines, + sizeof CrvSectionFloats/sizeof CrvSectionFloats[0], CrvSectionFloats, + &CrvSectionSchema, 0 }; + +#ifdef LATER +static wLines_t BumperLines[] = { +#include "tostrsct.lin" + }; +static toDesignFloat_t BumperFloats[] = { +{ { 200, 10 }, I_TOLENGTH+0, N_("Length"), N_("Length"), Dim_e } }; +static signed char BumperPaths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 0, 0, 0 }; +static toDesignSchema_t BumperSchema = { + BumperPaths, + "010" }; +static toDesignDesc_t BumperDesc = { + NTO_BUMPER, + N_("Bumper Section"), + 1, + sizeof StrSectionLines/sizeof StrSectionLines[0], StrSectionLines, + sizeof BumperFloats/sizeof BumperFloats[0], BumperFloats, + &BumperSchema, 0 }; + +static wLines_t TurntableLines[] = { +#include "tostrsct.lin" + }; +static toDesignFloat_t TurntableFloats[] = { +{ { 200, 10 }, I_TOOFFSET+0, N_("Offset"), N_("Count"), 0 }, +{ { 200, 10 }, I_TOLENGTH+0, N_("Length"), N_("Radius1"), Dim_e }, +{ { 200, 10 }, I_TOLENGTH+1, N_("Length"), N_("Radius2"), Dim_e } }; +static signed char TurntablePaths[] = { + '1', 0, 1, 0, 0, + '2', 0, 2, 0, 0, + '3', 0, 3, 0, 0, + '4', 0, 4, 0, 0, + '5', 0, 5, 0, 0, + '6', 0, 6, 0, 0, + '7', 0, 7, 0, 0, + '8', 0, 8, 0, 0, + '9', 0, 9, 0, 0, + '1', '0', 0, 10, 0, 0, + '1', '1', 0, 11, 0, 0, + '1', '2', 0, 12, 0, 0, + '1', '3', 0, 13, 0, 0, + '1', '4', 0, 14, 0, 0, + '1', '5', 0, 15, 0, 0, + '1', '6', 0, 16, 0, 0, + '1', '7', 0, 17, 0, 0, + '1', '8', 0, 18, 0, 0, + '1', '9', 0, 19, 0, 0, + '2', '0', 0, 20, 0, 0, + '2', '1', 0, 21, 0, 0, + '2', '2', 0, 22, 0, 0, + '2', '3', 0, 23, 0, 0, + '2', '4', 0, 24, 0, 0, + '2', '5', 0, 25, 0, 0, + '2', '6', 0, 26, 0, 0, + '2', '7', 0, 27, 0, 0, + '2', '8', 0, 28, 0, 0, + '2', '9', 0, 29, 0, 0, + '3', '0', 0, 30, 0, 0, + '3', '1', 0, 31, 0, 0, + '3', '2', 0, 32, 0, 0, + '3', '3', 0, 33, 0, 0, + '3', '4', 0, 34, 0, 0, + '3', '5', 0, 35, 0, 0, + '3', '6', 0, 36, 0, 0, + '3', '7', 0, 37, 0, 0, + '3', '8', 0, 38, 0, 0, + '3', '9', 0, 39, 0, 0, + '4', '0', 0, 40, 0, 0, + '4', '1', 0, 41, 0, 0, + '4', '2', 0, 42, 0, 0, + '4', '3', 0, 43, 0, 0, + '4', '4', 0, 44, 0, 0, + '4', '5', 0, 45, 0, 0, + '4', '6', 0, 46, 0, 0, + '4', '7', 0, 47, 0, 0, + '4', '8', 0, 48, 0, 0, + '4', '9', 0, 49, 0, 0, + '5', '0', 0, 50, 0, 0, + '5', '1', 0, 51, 0, 0, + '5', '2', 0, 52, 0, 0, + '5', '3', 0, 53, 0, 0, + '5', '4', 0, 54, 0, 0, + '5', '5', 0, 55, 0, 0, + '5', '6', 0, 56, 0, 0, + '5', '7', 0, 57, 0, 0, + '5', '8', 0, 58, 0, 0, + '5', '9', 0, 59, 0, 0, + '6', '0', 0, 60, 0, 0, + '6', '1', 0, 61, 0, 0, + '6', '2', 0, 62, 0, 0, + '6', '3', 0, 63, 0, 0, + '6', '4', 0, 64, 0, 0, + '6', '5', 0, 65, 0, 0, + '6', '6', 0, 66, 0, 0, + '6', '7', 0, 67, 0, 0, + '6', '8', 0, 68, 0, 0, + '6', '9', 0, 69, 0, 0, + '7', '0', 0, 70, 0, 0, + '7', '1', 0, 71, 0, 0, + '7', '2', 0, 72, 0, 0, + 0 }; +static toDesignSchema_t TurntableSchema = { + TurntablePaths, + "010" "020" "030" "040" "050" "060" "070" "080" "090" "0A0" "0B0" }; +static toDesignDesc_t TurntableDesc = { + NTO_TURNTABLE, + N_("Turntable Section"), + 1, + sizeof StrSectionLines/sizeof StrSectionLines[0], StrSectionLines, + sizeof TurntableFloats/sizeof TurntableFloats[0], TurntableFloats, + &TurntableSchema, 0 }; +#endif + +#ifndef MKTURNOUT +static toDesignDesc_t * designDescs[] = { + &RegDesc, + &CrvDesc, + &WyeDesc, + &ThreewayDesc, + &CrossingDesc, + &SingleSlipDesc, + &DoubleSlipDesc, + &RightCrossoverDesc, + &LeftCrossoverDesc, + &DoubleCrossoverDesc, + &StrSectionDesc, + &CrvSectionDesc }; +#endif + +/************************************************************************** + * + * Compute Roadbed + * + */ + +int debugComputeRoadbed = 0; +#ifdef LATER +typedef struct { + int start; + unsigned long bits; + unsigned long mask; + int width; + } searchTable_t; +static searchTable_t searchTable[] = { + { 0, 0xFFFF0000, 0xFFFF0000, 32000} , + { 32, 0x0000FFFF, 0x0000FFFF, 32000} , + + { 16, 0x00FFFF00, 0x00FFFF00, 16} , + + { 8, 0x0FF00000, 0x0FF00000, 8} , + { 24, 0x00000FF0, 0x00000FF0, 8} , + + { 4, 0x3C000000, 0x3C000000, 4} , + { 12, 0x003C0000, 0x003C0000, 4} , + { 20, 0x00003C00, 0x00003C00, 4} , + { 28, 0x0000003C, 0x0000003C, 4} , + + { 2, 0x60000000, 0x60000000, 2} , + { 6, 0x06000000, 0x06000000, 2}, + { 10, 0x00600000, 0x00600000, 2}, + { 14, 0x00060000, 0x00060000, 2}, + { 18, 0x00006000, 0x00006000, 2}, + { 22, 0x00000600, 0x00000600, 2}, + { 26, 0x00000060, 0x00000060, 2}, + { 30, 0x00000006, 0x00000006, 2}, + + { 1, 0x40000000, 0x60000000, 1}, + { 3, 0x10000000, 0x30000000, 1}, + { 5, 0x04000000, 0x06000000, 1}, + { 7, 0x01000000, 0x03000000, 1}, + { 9, 0x00400000, 0x00600000, 1}, + { 11, 0x00100000, 0x00300000, 1}, + { 13, 0x00040000, 0x00060000, 1}, + { 15, 0x00010000, 0x00030000, 1}, + { 17, 0x00004000, 0x00006000, 1}, + { 19, 0x00001000, 0x00003000, 1}, + { 21, 0x00000400, 0x00000600, 1}, + { 23, 0x00000100, 0x00000300, 1}, + { 25, 0x00000040, 0x00000060, 1}, + { 27, 0x00000010, 0x00000030, 1}, + { 29, 0x00000004, 0x00000006, 1}, + { 31, 0x00000001, 0x00000003, 1}}; +#endif + + +double LineSegDistance( coOrd p, coOrd p0, coOrd p1 ) +{ + double d, a; + coOrd pp, zero; + zero.x = zero.y = (POS_T)0.0; + d = FindDistance( p0, p1 ); + a = FindAngle( p0, p1 ); + pp.x = p.x-p0.x; + pp.y = p.y-p0.y; + Rotate( &pp, zero, -a ); + if (pp.y < 0.0-EPSILON) { + return FindDistance( p, p0 ); + } else if (pp.y > d+EPSILON ) { + return FindDistance( p, p1 ); + } else { + return pp.x>=0? pp.x : -pp.x; + } +} + + + +double CircleSegDistance( coOrd p, coOrd c, double r, double a0, double a1 ) +{ + double d, d0, d1; + double a,aa; + coOrd p1; + + d = FindDistance( c, p ); + a = FindAngle( c, p ); + aa = NormalizeAngle( a - a0 ); + d -= r; + if ( aa <= a1 ) { + return d>=0 ? d : -d; + } + PointOnCircle( &p1, c, r, a0 ); + d0 = FindDistance( p, p1 ); + PointOnCircle( &p1, c, r, a0+a1 ); + d1 = FindDistance( p, p1 ); + if (d0 < d1) + return d0; + else + return d1; +} + + +BOOL_T HittestTurnoutRoadbed( + trkSeg_p segPtr, + int segCnt, + int segInx, + ANGLE_T side, + int fraction, + DIST_T roadbedWidth ) +{ + ANGLE_T a; + DIST_T d; + int inx; + trkSeg_p sp; + coOrd p0, p1; + DIST_T dd; + int closest; + + sp = &segPtr[segInx]; + if (sp->type == SEG_STRTRK) { + d = FindDistance( sp->u.l.pos[0], sp->u.l.pos[1] ); + a = FindAngle( sp->u.l.pos[0], sp->u.l.pos[1] ); + d *= (fraction*2+1)/64.0; + Translate( &p0, sp->u.l.pos[0], a, d ); + Translate( &p0, p0, a+side, roadbedWidth/2.0 ); + } else { + d = sp->u.c.radius; + if ( d < 0 ) { + d = -d; + fraction = 31-fraction; + } + a = sp->u.c.a0 + sp->u.c.a1*(fraction*2+1)/64.0; + if (side>0) + d += roadbedWidth/2.0; + else + d -= roadbedWidth/2.0; + PointOnCircle( &p0, sp->u.c.center, d, a ); + } + dd = 100000.0; + closest = -1; + for (inx=0; inx<segCnt; inx++) { + sp = &segPtr[inx]; + p1 = p0; + switch( sp->type ) { + case SEG_STRTRK: + d = LineSegDistance( p1, sp->u.l.pos[0], sp->u.l.pos[1] ); + break; + case SEG_CRVTRK: + d = CircleSegDistance( p1, sp->u.c.center, fabs(sp->u.c.radius), sp->u.c.a0, sp->u.c.a1 ); + break; + default: + continue; + } +#ifdef LATER + if (inx==segInx) + d *= .999; +#endif + if ( d < dd ) { + dd = d; + closest = inx; + } + } + if (closest == segInx) + return FALSE; + else + return TRUE; +} + +#ifdef LATER +EXPORT long ComputeTurnoutRoadbedSide( + trkSeg_p segPtr, + int segCnt, + int segInx, + ANGLE_T side, + DIST_T roadbedWidth ) +{ + DIST_T length; + int rbw; + unsigned long res, res1; + searchTable_t * p; + double where; + trkSeg_p sp; + + sp = &segPtr[segInx]; + if (sp->type == SEG_STRTRK) + length = FindDistance( sp->u.l.pos[0], sp->u.l.pos[1] ); + else + length = (fabs(sp->u.c.radius) + (side>0?roadbedWidth/2.0:-roadbedWidth/2.0) ) * 2 * M_PI * sp->u.c.a1 / 360.0; + rbw = (int)(roadbedWidth/length*32/2); +/*printf( "L=%0.3f G=%0.3f [%0.3f %0.3f] RBW=%d\n", length, gapWidth, first, last, rbw );*/ + res = 0xFF0000FF; + for ( p=searchTable; p<&searchTable[sizeof searchTable/sizeof searchTable[0]]; p++) { + if ( (p->width < rbw && res==0xFFFFFFFF) || res==0 ) + break; + res1 = (p->mask & res); + where = p->start*length/32.0; + if (p->width >= rbw || (res1!=p->mask && res1!=0)) { + if (HittestTurnoutRoadbed(segPtr, segCnt, segInx, side, p->start)) { + res &= ~p->bits; +if (debugComputeRoadbed>=1) printf( "res=%08lx *p={%02d %08lx %08lx %02d} res1=%08lx W=%0.3f HIT\n", res, p->start, p->bits, p->mask, p->width, res1, where ); + } else { + res |= p->bits; +if (debugComputeRoadbed>=1) printf( "res=%08lx *p={%02d %08lx %08lx %02d} res1=%08lx W=%0.3f MISS\n", res, p->start, p->bits, p->mask, p->width, res1, where ); + } + } else { +if (debugComputeRoadbed>=2) printf( "res=%08lx *p={%02d %08lx %08lx %02d} res1=%08lx W=%0.3f SKIP\n", res, p->start, p->bits, p->mask, p->width, res1, where ); + } + } +if (debugComputeRoadbed>=1) printf( "res=%08lx\n", res ); + return res; +} +#endif + + +EXPORT long ComputeTurnoutRoadbedSide( + trkSeg_p segPtr, + int segCnt, + int segInx, + ANGLE_T side, + DIST_T roadbedWidth ) +{ + trkSeg_p sp; + DIST_T length; + int bitWidth; + unsigned long res, mask; + int hit0, hit1, inx0, inx1; + int i, j, k, hitx; + + sp = &segPtr[segInx]; + if (sp->type == SEG_STRTRK) + length = FindDistance( sp->u.l.pos[0], sp->u.l.pos[1] ); + else + length = (fabs(sp->u.c.radius) + (side>0?roadbedWidth/2.0:-roadbedWidth/2.0) ) * 2 * M_PI * sp->u.c.a1 / 360.0; + bitWidth = (int)floor(roadbedWidth*32/length); + if ( bitWidth > 31 ) + bitWidth = 31; + else if ( bitWidth <= 0 ) + bitWidth = 2; + res = 0; + mask = (1<<bitWidth)-1; + hit0 = HittestTurnoutRoadbed( segPtr, segCnt, segInx, side, 0, roadbedWidth ); + inx0 = 0; + inx1 = bitWidth; +if ( debugComputeRoadbed>=3 ) printf( "bW=%d HT[0]=%d\n", bitWidth, hit0 ); + while ( 1 ) { + if ( inx1 > 31 ) + inx1 = 31; + hit1 = HittestTurnoutRoadbed( segPtr, segCnt, segInx, side, inx1, roadbedWidth ); +if ( debugComputeRoadbed>=3 ) printf( " HT[%d]=%d\n", inx1, hit1 ); + if ( hit0 != hit1 ) { + i=inx0; + j=inx1; + while ( j-i >= 2 ) { + k = (i+j)/2; + hitx = HittestTurnoutRoadbed( segPtr, segCnt, segInx, side, k, roadbedWidth ); +if ( debugComputeRoadbed>=3 ) printf( " .HT[%d]=%d\n", k, hitx ); + if ( hitx == hit0 ) + i = k; + else + j = k; + } + if ( !hit0 ) { + res |= ((1<<(i-inx0+1))-1)<<inx0; + } else { + res |= ((1<<(inx1-j))-1)<<j; + } + } else if ( !hit1 ) { + res |= mask; + } +if ( debugComputeRoadbed>=3 ) printf( " res=%lx\n", res ); + if ( inx1 >= 31 ) { + if ( !hit1 ) + res |= 0x80000000; + break; + } + mask <<= bitWidth; + inx0 = inx1; + inx1 += bitWidth; + hit0 = hit1; + } +if ( debugComputeRoadbed>=2 ) printf( "S%d %c res=%lx\n", segInx, side>0?'+':'-', res ); + return res; +} + + +static BOOL_T IsNear( coOrd p0, coOrd p1 ) +{ + DIST_T d; + d = FindDistance( p0, p1 ); + return d < 0.05; +} + + +static void AddRoadbedPieces( + int inx, + ANGLE_T side, + int first, + int last ) +{ + DIST_T d0, d1; + ANGLE_T a0, a1; + coOrd p0, p1; + trkSeg_p sp, sq; +#ifdef MKTURNOUT +#define _DPI (76.0) +#else +#define _DPI mainD.dpi +#endif + + if (last<=first) + return; + sp = &tempSegs(inx); + if ( sp->type == SEG_STRTRK ) { + d0 = FindDistance( sp->u.l.pos[0], sp->u.l.pos[1] ); + a0 = FindAngle( sp->u.l.pos[0], sp->u.l.pos[1] ); + d1 = d0*first/32.0; + Translate( &p0, sp->u.l.pos[0], a0, d1 ); + Translate( &p0, p0, a0+side, newTurnRoadbedWidth/2.0 ); + d1 = d0*last/32.0; + Translate( &p1, sp->u.l.pos[0], a0, d1 ); + Translate( &p1, p1, a0+side, newTurnRoadbedWidth/2.0 ); + if ( first==0 || last==32 ) { + for ( sq=&tempSegs(0); sq<&tempSegs(tempSegs_da.cnt); sq++ ) { + if ( sq->type == SEG_STRLIN ) { + a1 = FindAngle( sq->u.l.pos[0], sq->u.l.pos[1] ); + a1 = NormalizeAngle( a1-a0+0.5 ); + if ( first==0 ) { + if ( a1 < 1.0 && IsNear( p0, sq->u.l.pos[1] ) ) { + sq->u.l.pos[1] = p1; + return; + } else if ( a1 > 180.0 && a1 < 181.0 && IsNear( p0, sq->u.l.pos[0] ) ) { + sq->u.l.pos[0] = p1; + return; + } + } + if ( last==32 ) { + if ( a1 < 1.0 && IsNear( p1, sq->u.l.pos[0] ) ) { + sq->u.l.pos[0] = p0; + return; + } else if ( a1 > 180.0 && a1 < 181.0 && IsNear( p1, sq->u.l.pos[1] ) ) { + sq->u.l.pos[1] = p0; + return; + } + } + } + } + } + } + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + sp = &tempSegs(inx); + sq = &tempSegs(tempSegs_da.cnt-1); + sq->width = newTurnRoadbedLineWidth/(_DPI); + sq->color = roadbedColor; + if (sp->type == SEG_STRTRK) { + sq->type = SEG_STRLIN; + sq->u.l.pos[0] = p0; + sq->u.l.pos[1] = p1; + } else { + d0 = sp->u.c.radius; + if ( d0 > 0 ) { + a0 = NormalizeAngle( sp->u.c.a0 + sp->u.c.a1*first/32.0 ); + } else { + d0 = -d0; + a0 = NormalizeAngle( sp->u.c.a0 + sp->u.c.a1*(32-last)/32.0 ); + } + a1 = sp->u.c.a1*(last-first)/32.0; + if (side>0) + d0 += newTurnRoadbedWidth/2.0; + else + d0 -= newTurnRoadbedWidth/2.0; + sq->type = SEG_CRVLIN; + sq->u.c.center = sp->u.c.center; + sq->u.c.radius = d0; + sq->u.c.a0 = a0; + sq->u.c.a1 = a1; + } +} + + +static void AddRoadbedToOneSide( + int trkCnt, + int inx, + ANGLE_T side ) +{ + unsigned long res, res1; + int b0, b1; + + res = ComputeTurnoutRoadbedSide( &tempSegs(0), trkCnt, inx, side, newTurnRoadbedWidth ); + if ( res == 0L ) { + return; + } else if ( res == 0xFFFFFFFF ) { + AddRoadbedPieces( inx, side, 0, 32 ); + } else { + for ( b0=0, res1=0x00000001; res1&&(res1&res); b0++,res1<<=1 ); + for ( b1=32,res1=0x80000000; res1&&(res1&res); b1--,res1>>=1 ); + AddRoadbedPieces( inx, side, 0, b0 ); + AddRoadbedPieces( inx, side, b1, 32 ); + } +} + + +static void AddRoadbed( void ) +{ + int trkCnt, inx; + trkSeg_p sp; + if ( newTurnRoadbedWidth < newTurnTrackGauge ) + return; + trkCnt = tempSegs_da.cnt; + for ( inx=0; inx<trkCnt; inx++ ) { + sp = &tempSegs(inx); + if ( sp->type!=SEG_STRTRK && sp->type!=SEG_CRVTRK ) + continue; + AddRoadbedToOneSide( trkCnt, inx, +90 ); + AddRoadbedToOneSide( trkCnt, inx, -90 ); + } +} + + +/********************************************************************* + * + * Functions + * + */ + +static BOOL_T ComputeCurve( + coOrd *p0, coOrd *p1, DIST_T *radius, + DIST_T len, DIST_T off, ANGLE_T angle ) +{ + coOrd Pf; + coOrd Px, Pc; + DIST_T d; + + Pf.x = len; + Pf.y = off; + p0->x = p0->y = 0.0; + /*lprintf( "Angle = %0.3f\n", angle );*/ + FindIntersection( &Px, *p0, 90.0, Pf, 90.0-angle ); + d = FindDistance( Px, Pf )-newTurnTrackGauge; + if (Px.x < newTurnTrackGauge || d < 0.0) { + NoticeMessage( MSG_TODSGN_NO_CONVERGE, _("Ok"), NULL ); + return FALSE; + } + if (Px.x-newTurnTrackGauge < d) + d = Px.x-newTurnTrackGauge; + *radius = d * cos( D2R(angle/2.0) ) / sin( D2R(angle/2.0) ); + + p0->x = Px.x - *radius * sin( D2R(angle/2.0) ) / cos( D2R(angle/2.0) ); + Translate( &Pc, *p0, 0.0, *radius ); + PointOnCircle( p1, Pc, *radius, 180.0-angle ); + + return TRUE; +} + + + +static toDesignSchema_t * LoadSegs( + toDesignDesc_t * dp, + wBool_t loadPoints, + wIndex_t * pathLenP ) +{ + wIndex_t s; + int i, p, p0, p1; + DIST_T d; +#ifndef MKTURNOUT + wIndex_t pathLen; +#endif + toDesignSchema_t * pp; + char *segOrder; + coOrd pos; + wIndex_t segCnt; + ANGLE_T angle1, angle2; + trkSeg_p segPtr; + + DYNARR_RESET( trkSeg_t, tempSegs_da ); + angle1 = newTurnAngle1; + angle2 = newTurnAngle2; + if ( newTurnAngleMode == 0 && dp->type != NTO_CRV_SECTION ) { + /* convert from Frog Num to degrees */ + if ( angle1 > 0 ) + angle1 = R2D(asin(1.0 / angle1)); + if ( angle2 > 0 ) + angle2 = R2D(asin(1.0 / angle2)); + } + + pp = dp->paths; + if (loadPoints) { + DYNARR_RESET( trkEndPt_t, tempEndPts_da ); + for ( i=0; i<dp->floatCnt; i++ ) + if ( *(FLOAT_T*)(turnDesignPLs[dp->floats[i].index].valueP) == 0.0 ) { + NoticeMessage( MSG_TODSGN_VALUES_GTR_0, _("Ok"), NULL ); + return NULL; + } + + switch (dp->type) { + case NTO_REGULAR: + DYNARR_SET( trkEndPt_t, tempEndPts_da, 3 ); + if ( !ComputeCurve( &points[3], &points[4], &radii[0], + (newTurnLen1), (newTurnOff1), angle1 ) ) + return NULL; + radii[0] = - radii[0]; + points[0].x = points[0].y = points[1].y = 0.0; + points[1].x = (newTurnLen0); + points[2].y = (newTurnOff1); + points[2].x = (newTurnLen1); + tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; + tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0; + tempEndPts(2).pos = points[2]; tempEndPts(2).angle = 90.0-angle1; + break; + + case NTO_CURVED: + DYNARR_SET( trkEndPt_t, tempEndPts_da, 3 ); + if ( !ComputeCurve( &points[3], &points[4], &radii[0], + (newTurnLen1), (newTurnOff1), angle1 ) ) + return NULL; + if ( !ComputeCurve( &points[5], &points[6], &radii[1], + (newTurnLen2), (newTurnOff2), angle2 ) ) + return NULL; + d = points[3].x - points[5].x; + if ( d < -0.10 ) + pp = &Crv3Schema; + else if ( d > 0.10 ) + pp = &Crv2Schema; + else + pp = &Crv1Schema; + radii[0] = - radii[0]; + radii[1] = - radii[1]; + points[0].x = points[0].y = 0.0; + points[1].y = (newTurnOff1); points[1].x = (newTurnLen1); + points[2].y = (newTurnOff2); points[2].x = (newTurnLen2); + tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; + tempEndPts(2).pos = points[1]; tempEndPts(2).angle = 90.0-angle1; + tempEndPts(1).pos = points[2]; tempEndPts(1).angle = 90.0-angle2; + break; + + case NTO_WYE: + case NTO_3WAY: + DYNARR_SET( trkEndPt_t, tempEndPts_da, (dp->type==NTO_3WAY)?4:3 ); + if ( !ComputeCurve( &points[3], &points[4], &radii[0], + (newTurnLen1), (newTurnOff1), angle1 ) ) + return NULL; + if ( !ComputeCurve( &points[5], &points[6], &radii[1], + (newTurnLen2), (newTurnOff2), angle2 ) ) + return NULL; + points[5].y = - points[5].y; + points[6].y = - points[6].y; + radii[0] = - radii[0]; + points[0].x = points[0].y = 0.0; + points[1].y = (newTurnOff1); + points[1].x = (newTurnLen1); + points[2].y = -(newTurnOff2); + points[2].x = (newTurnLen2); + points[7].y = 0; + points[7].x = (newTurnLen0); + d = points[3].x - points[5].x; + if ( d < -0.10 ) { + pp = (dp->type==NTO_3WAY ? &Tri3Schema : &Wye3Schema ); + } else if ( d > 0.10 ) { + pp = (dp->type==NTO_3WAY ? &Tri2Schema : &Wye2Schema ); + } else { + pp = (dp->type==NTO_3WAY ? &Tri1Schema : &Wye1Schema ); + } + tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; + tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0-angle1; + tempEndPts(2).pos = points[2]; tempEndPts(2).angle = 90.0+angle2; + if (dp->type == NTO_3WAY) { + tempEndPts(3).pos = points[7]; tempEndPts(3).angle = 90.0; + } + break; + + case NTO_D_SLIP: + case NTO_S_SLIP: + case NTO_CROSSING: + DYNARR_SET( trkEndPt_t, tempEndPts_da, 4 ); + points[0].x = points[0].y = points[1].y = 0.0; + points[1].x = (newTurnLen1); + pos.y = 0; pos.x = (newTurnLen1)/2.0; + Translate( &points[3], pos, 90.0+angle1, (newTurnLen2)/2.0 ); + points[2].y = - points[3].y; + points[2].x = (newTurnLen1)-points[3].x; + if (dp->type != NTO_CROSSING) { + Translate( &pos, points[3], 90.0+angle1, -newTurnTrackGauge ); + if (!ComputeCurve( &points[4], &points[5], &radii[0], + pos.x, fabs(pos.y), angle1 )) /*???*/ + return NULL; + radii[1] = - radii[0]; + points[5].y = - points[5].y; + points[6].y = 0; points[6].x = (newTurnLen1)-points[4].x; + points[7].y = -points[5].y; + points[7].x = (newTurnLen1)-points[5].x; + } + tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; + tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0; + tempEndPts(2).pos = points[2]; tempEndPts(2).angle = 270.0+angle1; + tempEndPts(3).pos = points[3]; tempEndPts(3).angle = 90.0+angle1; + break; + + case NTO_R_CROSSOVER: + case NTO_L_CROSSOVER: + case NTO_D_CROSSOVER: + DYNARR_SET( trkEndPt_t, tempEndPts_da, 4 ); + d = (newTurnLen1)/2.0 - newTurnTrackGauge; + if (d < 0.0) { + NoticeMessage( MSG_TODSGN_CROSSOVER_TOO_SHORT, _("Ok"), NULL ); + return NULL; + } + angle1 = R2D( atan2( (newTurnOff1), d ) ); + points[0].y = 0.0; points[0].x = 0.0; + points[1].y = 0.0; points[1].x = (newTurnLen1); + points[2].y = (newTurnOff1); points[2].x = 0.0; + points[3].y = (newTurnOff1); points[3].x = (newTurnLen1); + if (!ComputeCurve( &points[4], &points[5], &radii[1], + (newTurnLen1)/2.0, (newTurnOff1)/2.0, angle1 ) ) + return NULL; + radii[0] = - radii[1]; + points[6].y = 0.0; points[6].x = (newTurnLen1)-points[4].x; + points[7].y = points[5].y; points[7].x = (newTurnLen1)-points[5].x; + points[8].y = (newTurnOff1); points[8].x = points[4].x; + points[9].y = (newTurnOff1)-points[5].y; points[9].x = points[5].x; + points[10].y = (newTurnOff1); points[10].x = points[6].x; + points[11].y = points[9].y; points[11].x = points[7].x; + tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; + tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0; + tempEndPts(2).pos = points[2]; tempEndPts(2).angle = 270.0; + tempEndPts(3).pos = points[3]; tempEndPts(3).angle = 90.0; + break; + + case NTO_STR_SECTION: + DYNARR_SET( trkEndPt_t, tempEndPts_da, 2 ); + points[0].y = points[0].x = 0; + points[1].y = 0/*(newTurnOff1)*/; points[1].x = (newTurnLen1); + tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; + tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0; + break; + + case NTO_CRV_SECTION: + DYNARR_SET( trkEndPt_t, tempEndPts_da, 2 ); + points[0].y = points[0].x = 0; + points[1].y = (newTurnLen1) * (1.0 - cos( D2R(angle1) ) ); + points[1].x = (newTurnLen1) * sin( D2R(angle1) ); + radii[0] = -(newTurnLen1); + tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; + tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0-angle1; + break; + + case NTO_BUMPER: + DYNARR_SET( trkEndPt_t, tempEndPts_da, 1 ); + points[0].y = points[0].x = 0; + points[1].y = 0/*(newTurnOff1)*/; points[1].x = (newTurnLen1); + tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; + break; + + default: + ; + } + } else { + switch (dp->type) { + case NTO_CURVED: + d = points[3].x - points[5].x; + if ( d < -0.10 ) + pp = &Crv3Schema; + else if ( d > 0.10 ) + pp = &Crv2Schema; + else + pp = &Crv1Schema; + break; + } + } + + segOrder = pp->segOrder; + segCnt = strlen( segOrder ); + if (segCnt%3 != 0) + AbortProg( dp->label ); + segCnt /= 3; + DYNARR_SET( trkSeg_t, tempSegs_da, segCnt ); + tempSegs_da.cnt = segCnt; + memset( &tempSegs(0), 0, segCnt * sizeof tempSegs(0) ); + for ( s=0; s<segCnt; s++ ) { + segPtr = &tempSegs(s); + segPtr->color = wDrawColorBlack; + if (*segOrder <= '9') + p0 = *segOrder++ - '0'; + else + p0 = *segOrder++ - 'A' + 10; + if (*segOrder <= '9') + p1 = *segOrder++ - '0'; + else + p1 = *segOrder++ - 'A' + 10; + p = *segOrder++ - '0'; + if (p != 0) { + segPtr->type = SEG_CRVTRK; + ComputeCurvedSeg( segPtr, radii[p-1], points[p0], points[p1] ); + } else { + segPtr->type = SEG_STRTRK; + segPtr->u.l.pos[0] = points[p0]; + segPtr->u.l.pos[1] = points[p1]; + } + } + + AddRoadbed(); + +#ifndef MKTURNOUT + if ( (pathLen=CheckPaths( segCnt, &tempSegs(0), pp->paths )) < 0 ) + return NULL; + + if (pathLenP) + *pathLenP = pathLen; +#endif + return pp; +} + + +static void CopyNonTracks( turnoutInfo_t * to ) +{ + trkSeg_p sp0; + for ( sp0=to->segs; sp0<&to->segs[to->segCnt]; sp0++ ) { + if ( sp0->type != SEG_STRTRK && sp0->type != SEG_CRVTRK ) { + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + tempSegs(tempSegs_da.cnt-1) = *sp0; + } + } +} + + +#ifndef MKTURNOUT +static void NewTurnPrint( + void * junk ) +{ + coOrd pos, p0, p1; + WDOUBLE_T px, py; + int i, j, ii, jj, p; + EPINX_T ep; + wFont_p fp; + coOrd orig, size; + toDesignSchema_t * pp; + POS_T tmp; + FLOAT_T tmpR; + static drawCmd_t newTurnout_d = { + NULL, + &printDrawFuncs, + DC_PRINT, + 1.0, + 0.0, + { 0.0, 0.0 }, + { 0.0, 0.0 }, + Pix2CoOrd, CoOrd2Pix }; + + if ((pp=LoadSegs( curDesign, TRUE, NULL )) == NULL) + return; + if (includeNontrackSegments && customTurnout1) + CopyNonTracks( customTurnout1 ); + + GetSegBounds( zero, 0.0, tempSegs_da.cnt, &tempSegs(0), &orig, &size ); + tmp = orig.x; orig.x = orig.y; orig.y = tmp; +#ifdef LATER + size.x = 0.0; size.y = 0.0; + orig.x = 0.0; orig.y = 0.0; + for ( i=0; i<tempSegs_da.cnt; i++ ) { + segPtr = &tempSegs(i); + switch (segPtr->type) { + case SEG_STRLIN: + case SEG_STRTRK: + pos[0] = segPtr->u.l.pos[0]; + pos[1] = segPtr->u.l.pos[1]; + break; + case SEG_CRVTRK: + case SEG_CRVLIN: + PointOnCircle( &pos[0], segPtr->u.c.center, segPtr->u.c.radius, + segPtr->u.c.a0 ); + PointOnCircle( &pos[1], segPtr->u.c.center, segPtr->u.c.radius, + segPtr->u.c.a0+segPtr->u.c.a1 ); + } + for ( ep=0; ep<2; ep++ ) { + if (pos[ep].x < orig.x) + orig.x = pos[ep].x; + if (pos[ep].x > size.x) + size.x = pos[ep].x; + if (pos[ep].y < orig.y) + orig.y = pos[ep].y; + if (pos[ep].y > size.y) + size.y = pos[ep].y; + } + } + + size.x -= orig.x; + size.y -= orig.y; +#endif + + fp = wStandardFont( F_TIMES, FALSE, FALSE ); + wPrintGetPageSize( &px, &py ); + newTurnout_d.size.x = px; + newTurnout_d.size.y = py; + ii = (int)(size.y/newTurnout_d.size.x)+1; + jj = (int)(size.x/newTurnout_d.size.y)+1; + if ( !wPrintDocStart( sTurnoutDesignerW, ii*jj, NULL ) ) + return; +#ifdef LATER + orig.x -= (0.5); + orig.y -= (jj*newTurnout_d.size.y-size.y)/2.0; +#endif + orig.x = - ( size.y + orig.x + newTurnTrackGauge/2.0 + 0.5 ); + orig.y -= (0.5); + for ( i=0, newTurnout_d.orig.x=orig.x; i<ii; + i++, newTurnout_d.orig.x+=newTurnout_d.size.x ) { + for ( j=0, newTurnout_d.orig.y=orig.y; j<jj; + j++, newTurnout_d.orig.y+=newTurnout_d.size.y ) { + newTurnout_d.d = wPrintPageStart(); + newTurnout_d.dpi = wDrawGetDPI(newTurnout_d.d); + + sprintf( message, "%s", sProdName ); + wDrawString( newTurnout_d.d, POSX(3.0), + POSY(6.75), 0.0, message, fp, 40, + wDrawColorBlack, 0 ); + sprintf( message, _("%s Designer"), _(curDesign->label) ); + wDrawString( newTurnout_d.d, POSX(3.0), + POSY(6.25), 0.0, message, fp, 30, + wDrawColorBlack, 0 ); + sprintf( message, "%s %d x %d (of %d x %d)", _("Page"), i+1, j+1, ii, jj ); + wDrawString( newTurnout_d.d, POSX(3.0), + POSY(5.75), 0.0, message, fp, 20, + wDrawColorBlack, 0 ); + + for ( p=0; p<curDesign->floatCnt; p++ ) { + tmpR = *(FLOAT_T*)(turnDesignPLs[curDesign->floats[p].index].valueP); + sprintf( message, "%s: %s", + (curDesign->floats[p].mode!=Frog_e||newTurnAngleMode!=0)?_(curDesign->floats[p].printLabel):_("Frog Number"), + curDesign->floats[p].mode==Dim_e? + FormatDistance(tmpR): + FormatFloat(tmpR) ); + wDrawString( newTurnout_d.d, POSX(3.0), + POSY(5.50-p*0.25), 0.0, + message, fp, 20, wDrawColorBlack, 0 ); + } + if (newTurnLeftDesc[0] || newTurnLeftPartno[0]) { + sprintf( message, "%s %s %s", newTurnManufacturer, newTurnLeftPartno, newTurnLeftDesc ); + wDrawString( newTurnout_d.d, POSX(3.0), + POSY(5.50-curDesign->floatCnt*0.25), 0.0, + message, fp, 20, wDrawColorBlack, 0 ); + } + if (newTurnRightDesc[0] || newTurnRightPartno[0]) { + sprintf( message, "%s %s %s", newTurnManufacturer, newTurnRightPartno, newTurnRightDesc ); + wDrawString( newTurnout_d.d, POSX(3.0), + POSY(5.50-curDesign->floatCnt*0.25-0.25), 0.0, + message, fp, 20, wDrawColorBlack, 0 ); + } + + wDrawLine( newTurnout_d.d, POSX(0), POSY(0), + POSX(newTurnout_d.size.x), POSY(0), 0, wDrawLineSolid, + wDrawColorBlack, 0 ); + wDrawLine( newTurnout_d.d, POSX(newTurnout_d.size.x), POSY(0.0), + POSX(newTurnout_d.size.x), POSY(newTurnout_d.size.y), 0, + wDrawLineSolid, wDrawColorBlack, 0 ); + wDrawLine( newTurnout_d.d, POSX(newTurnout_d.size.x), POSY(newTurnout_d.size.y), + POSX(0.0), POSY(newTurnout_d.size.y), 0, wDrawLineSolid, + wDrawColorBlack, 0 ); + wDrawLine( newTurnout_d.d, POSX(0.0), POSY(newTurnout_d.size.y), + POSX(0.0), POSX(0.0), 0, wDrawLineSolid, wDrawColorBlack, 0 ); + + DrawSegs( &newTurnout_d, zero, 270.0, &tempSegs(0), tempSegs_da.cnt, newTurnTrackGauge, wDrawColorBlack ); + + for ( ep=0; ep<tempEndPts_da.cnt; ep++ ) { + pos.x = -tempEndPts(ep).pos.y; + pos.y = tempEndPts(ep).pos.x; + Translate( &p0, pos, tempEndPts(ep).angle+90+270.0, + newTurnTrackGauge ); + Translate( &p1, pos, tempEndPts(ep).angle+270+270.0, + newTurnTrackGauge ); + DrawLine( &newTurnout_d, p0, p1, 0, wDrawColorBlack ); + Translate( &p0, pos, tempEndPts(ep).angle+270.0, + newTurnout_d.size.y/2.0 ); + DrawStraightTrack( &newTurnout_d, pos, p0, + tempEndPts(ep).angle+270.0, + NULL, newTurnTrackGauge, wDrawColorBlack, 0 ); + } + + if ( !wPrintPageEnd( newTurnout_d.d ) ) + goto quitPrinting; + } + } +quitPrinting: + wPrintDocEnd(); +} +#endif + +static void NewTurnOk( void * context ) +{ + FILE * f; + toDesignSchema_t * pp; + wIndex_t pathLen; + int i; + BOOL_T foundR=FALSE; + char * cp; +#ifndef MKTURNOUT + turnoutInfo_t *to; +#endif + FLOAT_T flt; + wIndex_t segCnt; + char * customInfoP; + char *oldLocale = NULL; + + if ((pp=LoadSegs( curDesign, TRUE, &pathLen )) == NULL) + return; + + if ( (curDesign->strCnt >= 1 && newTurnLeftDesc[0] == 0) || + (curDesign->strCnt >= 2 && newTurnRightDesc[0] == 0) ) { + NoticeMessage( MSG_TODSGN_DESC_NONBLANK, _("Ok"), NULL ); + return; + } + + BuildTrimedTitle( message, "\t", newTurnManufacturer, newTurnLeftDesc, newTurnLeftPartno ); +#ifndef MKTURNOUT + if ( customTurnout1 == NULL && + ( foundR || FindCompound( FIND_TURNOUT, newTurnScaleName, message ) ) ) { + if ( !NoticeMessage( MSG_TODSGN_REPLACE, _("Yes"), _("No") ) ) + return; + } + oldLocale = SaveLocale("C"); +#endif + + f = OpenCustom("a"); + + sprintf( tempCustom, "\"%s\" \"%s\" \"", + curDesign->label, "" ); + cp = tempCustom + strlen(tempCustom); + cp = Strcpytrimed( cp, newTurnManufacturer, TRUE ); + strcpy( cp, "\" \"" ); + cp += 3; + cp = Strcpytrimed( cp, newTurnLeftDesc, TRUE ); + strcpy( cp, "\" \"" ); + cp += 3; + cp = Strcpytrimed( cp, newTurnLeftPartno, TRUE ); + strcpy( cp, "\"" ); + cp += 1; + if (curDesign->type == NTO_REGULAR || curDesign->type == NTO_CURVED) { + strcpy( cp, " \"" ); + cp += 2; + cp = Strcpytrimed( cp, newTurnRightDesc, TRUE ); + strcpy( cp, "\" \"" ); + cp += 3; + cp = Strcpytrimed( cp, newTurnRightPartno, TRUE ); + strcpy( cp, "\"" ); + cp += 1; + } + if ( cp-tempCustom > sizeof tempCustom ) + AbortProg( "Custom line overflow" ); + for ( i=0; i<curDesign->floatCnt; i++ ) { + flt = *(FLOAT_T*)(turnDesignPLs[curDesign->floats[i].index].valueP); + switch( curDesign->floats[i].mode ) { + case Dim_e: + flt = ( flt ); + break; + case Frog_e: + if (newTurnAngleMode == 0 && flt > 0.0) + flt = R2D(asin(1.0/flt)); + break; + case Angle_e: + break; + } + sprintf( cp, " %0.6f", flt ); + cp += strlen(cp); + } + sprintf( cp, " %0.6f %0.6f %ld", newTurnRoadbedWidth, newTurnRoadbedLineWidth/(_DPI), wDrawGetRGB(roadbedColor) ); + customInfoP = MyStrdup( tempCustom ); + strcpy( tempCustom, message ); + + segCnt = tempSegs_da.cnt; +#ifndef MKTURNOUT + if (includeNontrackSegments && customTurnout1) + CopyNonTracks( customTurnout1 ); + if ( customTurnout1 ) + customTurnout1->segCnt = 0; + to = CreateNewTurnout( newTurnScaleName, tempCustom, tempSegs_da.cnt, &tempSegs(0), + pathLen, pp->paths, tempEndPts_da.cnt, &tempEndPts(0), FALSE ); + to->customInfo = customInfoP; +#endif + if (f) { + fprintf( f, "TURNOUT %s \"%s\"\n", newTurnScaleName, PutTitle(tempCustom) ); +#ifdef MKTURNOUT + if (doCustomInfoLine) +#endif + fprintf( f, "\tU %s\n", customInfoP ); + WriteCompoundPathsEndPtsSegs( f, pp->paths, tempSegs_da.cnt, &tempSegs(0), + tempEndPts_da.cnt, &tempEndPts(0) ); + } + + switch (curDesign->type) { + case NTO_REGULAR: + points[2].y = - points[2].y; + points[4].y = - points[4].y; + radii[0] = - radii[0]; + LoadSegs( curDesign, FALSE, &pathLen ); + tempEndPts(2).pos.y = - tempEndPts(2).pos.y; + tempEndPts(2).angle = 180.0 - tempEndPts(2).angle; + BuildTrimedTitle( tempCustom, "\t", newTurnManufacturer, newTurnRightDesc, newTurnRightPartno ); + tempSegs_da.cnt = segCnt; +#ifndef MKTURNOUT + if (includeNontrackSegments && customTurnout2) + CopyNonTracks( customTurnout2 ); + if ( customTurnout2 ) + customTurnout2->segCnt = 0; + to = CreateNewTurnout( newTurnScaleName, tempCustom, tempSegs_da.cnt, &tempSegs(0), + pathLen, pp->paths, tempEndPts_da.cnt, &tempEndPts(0), FALSE ); + to->customInfo = customInfoP; +#endif + if (f) { + fprintf( f, "TURNOUT %s \"%s\"\n", newTurnScaleName, PutTitle(tempCustom) ); +#ifdef MKTURNOUT + if (doCustomInfoLine) +#endif + fprintf( f, "\tU %s\n", customInfoP ); + WriteCompoundPathsEndPtsSegs( f, pp->paths, tempSegs_da.cnt, &tempSegs(0), tempEndPts_da.cnt, &tempEndPts(0) ); + } + break; + case NTO_CURVED: + points[1].y = - points[1].y; + points[2].y = - points[2].y; + points[4].y = - points[4].y; + points[6].y = - points[6].y; + radii[0] = - radii[0]; + radii[1] = - radii[1]; + LoadSegs( curDesign, FALSE, &pathLen ); + tempEndPts(1).pos.y = - tempEndPts(1).pos.y; + tempEndPts(1).angle = 180.0 - tempEndPts(1).angle; + tempEndPts(2).pos.y = - tempEndPts(2).pos.y; + tempEndPts(2).angle = 180.0 - tempEndPts(2).angle; + BuildTrimedTitle( tempCustom, "\t", newTurnManufacturer, newTurnRightDesc, newTurnRightPartno ); + tempSegs_da.cnt = segCnt; +#ifndef MKTURNOUT + if (includeNontrackSegments && customTurnout2) + CopyNonTracks( customTurnout2 ); + if ( customTurnout2 ) + customTurnout2->segCnt = 0; + to = CreateNewTurnout( newTurnScaleName, tempCustom, tempSegs_da.cnt, &tempSegs(0), + pathLen, pp->paths, tempEndPts_da.cnt, &tempEndPts(0), FALSE ); + to->customInfo = customInfoP; +#endif + if (f) { + fprintf( f, "TURNOUT %s \"%s\"\n", newTurnScaleName, PutTitle(tempCustom) ); +#ifdef MKTURNOUT + if (doCustomInfoLine) +#endif + fprintf( f, "\tU %s\n", customInfoP ); + WriteCompoundPathsEndPtsSegs( f, pp->paths, tempSegs_da.cnt, &tempSegs(0), tempEndPts_da.cnt, &tempEndPts(0) ); + } + break; + default: + ; + } + tempCustom[0] = '\0'; + +#ifndef MKTURNOUT + if (f) + fclose(f); + RestoreLocale(oldLocale); + includeNontrackSegments = TRUE; + wHide( newTurnW ); + DoChangeNotification( CHANGE_PARAMS ); + +#endif + +} + + +#ifndef MKTURNOUT +static void NewTurnCancel( wWin_p win ) +{ + wHide( newTurnW ); + includeNontrackSegments = TRUE; +} + + + +static wPos_t turnDesignWidth; +static wPos_t turnDesignHeight; + +static void TurnDesignLayout( + paramData_t * pd, + int index, + wPos_t colX, + wPos_t * w, + wPos_t * h ) +{ + wPos_t inx; + if ( curDesign == NULL ) + return; + if ( index >= I_TO_FIRST_FLOAT && index <= I_TO_LAST_FLOAT ) { + for ( inx=0; inx<curDesign->floatCnt; inx++ ) { + if ( index == curDesign->floats[inx].index ) { + *w = curDesign->floats[inx].pos.x; + *h = curDesign->floats[inx].pos.y; + return; + } + } + AbortProg( "turnDesignLayout: bad index = %d", index ); + } else if ( index == I_TOMANUF ) { + *h = turnDesignHeight + 10; + } +} + + +static void SetupTurnoutDesignerW( toDesignDesc_t * newDesign ) +{ + static wPos_t partnoWidth; + int inx; + wPos_t w, h, ctlH; + + if ( newTurnW == NULL ) { + partnoWidth = wLabelWidth( "999-99999-9999" ); + turnDesignPLs[I_TOLDESC+1].winData = + turnDesignPLs[I_TORDESC+1].winData = + (void*)(intptr_t)partnoWidth; + partnoWidth += wLabelWidth( " # " ); + newTurnW = ParamCreateDialog( &turnDesignPG, _("Turnout Designer"), _("Print"), NewTurnPrint, NewTurnCancel, TRUE, TurnDesignLayout, F_BLOCK, NULL ); + for ( inx=0; inx<(sizeof designDescs/sizeof designDescs[0]); inx++ ) { + designDescs[inx]->lineC = wLineCreate( turnDesignPG.win, NULL, designDescs[inx]->lineCnt, designDescs[inx]->lines ); + wControlShow( (wControl_p)designDescs[inx]->lineC, FALSE ); + } + } + if ( curDesign != newDesign ) { + if ( curDesign ) + wControlShow( (wControl_p)curDesign->lineC, FALSE ); + curDesign = newDesign; + sprintf( message, _("%s %s Designer"), sProdName, _(curDesign->label) ); + wWinSetTitle( newTurnW, message ); + for ( inx=I_TO_FIRST_FLOAT; inx<=I_TO_LAST_FLOAT; inx++ ) { + turnDesignPLs[inx].option |= PDO_DLGIGNORE; + wControlShow( turnDesignPLs[inx].control, FALSE ); + } + for ( inx=0; inx<curDesign->floatCnt; inx++ ) { + turnDesignPLs[curDesign->floats[inx].index].option &= ~PDO_DLGIGNORE; + wControlSetLabel( turnDesignPLs[curDesign->floats[inx].index].control, _(curDesign->floats[inx].winLabel) ); + wControlShow( turnDesignPLs[curDesign->floats[inx].index].control, TRUE ); + } + wControlShow( turnDesignPLs[I_TORDESC+0].control, curDesign->strCnt>1 ); + wControlShow( turnDesignPLs[I_TORDESC+1].control, curDesign->strCnt>1 ); + wControlShow( (wControl_p)curDesign->lineC, TRUE ); + + turnDesignWidth = turnDesignHeight = 0; + for (inx=0;inx<curDesign->lineCnt;inx++) { + if (curDesign->lines[inx].x0 > turnDesignWidth) + turnDesignWidth = curDesign->lines[inx].x0; + if (curDesign->lines[inx].x1 > turnDesignWidth) + turnDesignWidth = curDesign->lines[inx].x1; + if (curDesign->lines[inx].y0 > turnDesignHeight) + turnDesignHeight = curDesign->lines[inx].y0; + if (curDesign->lines[inx].y1 > turnDesignHeight) + turnDesignHeight = curDesign->lines[inx].y1; + } + ctlH = wControlGetHeight( turnDesignPLs[I_TO_FIRST_FLOAT].control ); + for ( inx=0; inx<curDesign->floatCnt; inx++ ) { + w = curDesign->floats[inx].pos.x + 80; + h = curDesign->floats[inx].pos.y + ctlH; + if (turnDesignWidth < w) + turnDesignWidth = w; + if (turnDesignHeight < h) + turnDesignHeight = h; + } + if ( curDesign->strCnt > 1 ) { + w = wLabelWidth( _("Right Description") ); + wControlSetLabel( turnDesignPLs[I_TOLDESC].control, _("Left Description") ); + turnDesignPLs[I_TOLDESC].winLabel = N_("Left Description"); + turnDesignPLs[I_TORDESC+0].option &= ~PDO_DLGIGNORE; + turnDesignPLs[I_TORDESC+1].option &= ~PDO_DLGIGNORE; + } else { + w = wLabelWidth( _("Manufacturer") ); + wControlSetLabel( turnDesignPLs[I_TOLDESC].control, _("Description") ); + turnDesignPLs[I_TOLDESC].winLabel = N_("Description"); + turnDesignPLs[I_TORDESC+0].option |= PDO_DLGIGNORE; + turnDesignPLs[I_TORDESC+1].option |= PDO_DLGIGNORE; + } + if ( curDesign->angleModeCnt > 0 ) { + turnDesignPLs[I_TOANGMODE].option &= ~PDO_DLGIGNORE; + wControlShow( turnDesignPLs[I_TOANGMODE].control, TRUE ); + } else { + turnDesignPLs[I_TOANGMODE].option |= PDO_DLGIGNORE; + wControlShow( turnDesignPLs[I_TOANGMODE].control, FALSE ); + } + + w = turnDesignWidth-w; + wStringSetWidth( (wString_p)turnDesignPLs[I_TOMANUF].control, w ); + w -= partnoWidth; + wStringSetWidth( (wString_p)turnDesignPLs[I_TOLDESC].control, w ); + wStringSetWidth( (wString_p)turnDesignPLs[I_TORDESC].control, w ); + ParamLayoutDialog( &turnDesignPG ); + } +} + + +static void ShowTurnoutDesigner( void * context ) +{ + if (recordF) + fprintf( recordF, TURNOUTDESIGNER " SHOW %s\n", ((toDesignDesc_t*)context)->label ); + newTurnScaleName = curScaleName; + newTurnTrackGauge = trackGauge; + SetupTurnoutDesignerW( (toDesignDesc_t*)context ); + newTurnRightDesc[0] = '\0'; + newTurnRightPartno[0] = '\0'; + newTurnLeftDesc[0] = '\0'; + newTurnLeftPartno[0] = '\0'; + newTurnLen0 = + newTurnOff1 = newTurnLen1 = newTurnAngle1 = + newTurnOff2 = newTurnLen2 = newTurnAngle2 = 0.0; + ParamLoadControls( &turnDesignPG ); + ParamGroupRecord( &turnDesignPG ); + customTurnout1 = NULL; + customTurnout2 = NULL; + wShow( newTurnW ); +} + + +static BOOL_T NotClose( DIST_T d ) +{ + return d < -0.001 || d > 0.001; +} + + +EXPORT void EditCustomTurnout( turnoutInfo_t * to, turnoutInfo_t * to1 ) +{ + int i; + toDesignDesc_t * dp; + char * type, * name, *cp, *mfg, *descL, *partL, *descR, *partR; + wIndex_t pathLen; + long rgb; + trkSeg_p sp0, sp1; + BOOL_T segsDiff; + DIST_T width; + + if ( ! GetArgs( to->customInfo, "qqqqqc", &type, &name, &mfg, &descL, &partL, &cp ) ) + return; + for ( i=0; i<(sizeof designDescs/sizeof designDescs[0]); i++ ) { + dp = designDescs[i]; + if ( strcmp( type, dp->label ) == 0 ) { + break; + } + } + if ( i >= (sizeof designDescs/sizeof designDescs[0]) ) + return; + + SetupTurnoutDesignerW(dp); + newTurnTrackGauge = GetScaleTrackGauge( to->scaleInx ); + newTurnScaleName = GetScaleName( to->scaleInx ); + strcpy( newTurnManufacturer, mfg ); + strcpy( newTurnLeftDesc, descL ); + strcpy( newTurnLeftPartno, partL ); + if (dp->type == NTO_REGULAR || dp->type == NTO_CURVED) { + if ( ! GetArgs( cp, "qqc", &descR, &partR, &cp )) + return; + strcpy( newTurnRightDesc, descR ); + strcpy( newTurnRightPartno, partR ); + } else { + descR = partR = ""; + } + for ( i=0; i<dp->floatCnt; i++ ) { + if ( ! GetArgs( cp, "fc", turnDesignPLs[dp->floats[i].index].valueP, &cp ) ) + return; + switch (dp->floats[i].mode) { + case Dim_e: + /* *dp->floats[i].valueP = PutDim( *dp->floats[i].valueP ); */ + break; + case Frog_e: + if (newTurnAngleMode == 0) { + if ( *(FLOAT_T*)(turnDesignPLs[dp->floats[i].index].valueP) > 0.0 ) + *(FLOAT_T*)(turnDesignPLs[dp->floats[i].index].valueP) = 1.0/sin(D2R(*(FLOAT_T*)(turnDesignPLs[dp->floats[i].index].valueP))); + } + break; + case Angle_e: + break; + } + } + rgb = 0; + if ( cp && GetArgs( cp, "ffl", &newTurnRoadbedWidth, &width, &rgb ) ) { + roadbedColor = wDrawFindColor(rgb); + newTurnRoadbedLineWidth = (long)floor(width*mainD.dpi+0.5); + } else { + newTurnRoadbedWidth = 0; + newTurnRoadbedLineWidth = 0; + roadbedColor = wDrawColorBlack; + } + + customTurnout1 = to; + customTurnout2 = to1; + + segsDiff = FALSE; + if ( to ) { + LoadSegs( dp, TRUE, &pathLen ); + segsDiff = FALSE; + if ( to->segCnt == tempSegs_da.cnt ) { + for ( sp0=to->segs,sp1=&tempSegs(0); (!segsDiff) && sp0<&to->segs[to->segCnt]; sp0++,sp1++ ) { + switch (sp0->type) { + case SEG_STRLIN: + if (sp0->type != sp1->type || + sp0->color != sp1->color || + NotClose(sp0->width-width) || + NotClose(sp0->u.l.pos[0].x-sp1->u.l.pos[0].x) || + NotClose(sp0->u.l.pos[0].y-sp1->u.l.pos[0].y) || + NotClose(sp0->u.l.pos[1].x-sp1->u.l.pos[1].x) || + NotClose(sp0->u.l.pos[1].y-sp1->u.l.pos[1].y) ) + segsDiff = TRUE; + break; + case SEG_CRVLIN: + if (sp0->type != sp1->type || + sp0->color != sp1->color || + NotClose(sp0->width-width) || + NotClose(sp0->u.c.center.x-sp1->u.c.center.x) || + NotClose(sp0->u.c.center.y-sp1->u.c.center.y) || + NotClose(sp0->u.c.radius-sp1->u.c.radius) || + NotClose(sp0->u.c.a0-sp1->u.c.a0) || + NotClose(sp0->u.c.a1-sp1->u.c.a1) ) + segsDiff = TRUE; + break; + case SEG_STRTRK: + case SEG_CRVTRK: + break; + default: + segsDiff = TRUE; + } + } + } else { + for ( sp0=to->segs; (!segsDiff) && sp0<&to->segs[to->segCnt]; sp0++ ) { + if ( sp0->type != SEG_STRTRK && sp0->type != SEG_CRVTRK ) + segsDiff = TRUE; + } + } + } + if ( (!segsDiff) && to1 && (dp->type==NTO_REGULAR||dp->type==NTO_CURVED) ) { + if ( dp->type==NTO_REGULAR ) { + points[2].y = - points[2].y; + points[4].y = - points[4].y; + radii[0] = - radii[0]; + } else { + points[1].y = - points[1].y; + points[2].y = - points[2].y; + points[4].y = - points[4].y; + points[6].y = - points[6].y; + radii[0] = - radii[0]; + radii[1] = - radii[1]; + } + LoadSegs( dp, FALSE, &pathLen ); + if ( dp->type==NTO_REGULAR ) { + points[2].y = - points[2].y; + points[4].y = - points[4].y; + radii[0] = - radii[0]; + } else { + points[1].y = - points[1].y; + points[2].y = - points[2].y; + points[4].y = - points[4].y; + points[6].y = - points[6].y; + radii[0] = - radii[0]; + radii[1] = - radii[1]; + } + segsDiff = FALSE; + if ( to1->segCnt == tempSegs_da.cnt ) { + for ( sp0=to1->segs,sp1=&tempSegs(0); (!segsDiff) && sp0<&to1->segs[to1->segCnt]; sp0++,sp1++ ) { + switch (sp0->type) { + case SEG_STRLIN: + if (sp0->type != sp1->type || + sp0->color != sp1->color || + NotClose(sp0->width-width) || + NotClose(sp0->u.l.pos[0].x-sp1->u.l.pos[0].x) || + NotClose(sp0->u.l.pos[0].y-sp1->u.l.pos[0].y) || + NotClose(sp0->u.l.pos[1].x-sp1->u.l.pos[1].x) || + NotClose(sp0->u.l.pos[1].y-sp1->u.l.pos[1].y) ) + segsDiff = TRUE; + break; + case SEG_CRVLIN: + if (sp0->type != sp1->type || + sp0->color != sp1->color || + NotClose(sp0->width-width) || + NotClose(sp0->u.c.center.x-sp1->u.c.center.x) || + NotClose(sp0->u.c.center.y-sp1->u.c.center.y) || + NotClose(sp0->u.c.radius-sp1->u.c.radius) || + NotClose(sp0->u.c.a0-sp1->u.c.a0) || + NotClose(sp0->u.c.a1-sp1->u.c.a1) ) + segsDiff = TRUE; + break; + case SEG_STRTRK: + case SEG_CRVTRK: + break; + default: + segsDiff = TRUE; + } + } + } else { + for ( sp0=to1->segs; (!segsDiff) && sp0<&to1->segs[to1->segCnt]; sp0++ ) { + if ( sp0->type != SEG_STRTRK && sp0->type != SEG_CRVTRK ) + segsDiff = TRUE; + } + } + } + + includeNontrackSegments = TRUE; + if ( segsDiff ) { + if ( NoticeMessage( MSG_SEGMENTS_DIFFER, _("Yes"), _("No") ) <= 0 ) { + includeNontrackSegments = FALSE; + } + } else { + includeNontrackSegments = FALSE; + } + /*if (recordF) + fprintf( recordF, TURNOUTDESIGNER " SHOW %s\n", dp->label );*/ + ParamLoadControls( &turnDesignPG ); + ParamGroupRecord( &turnDesignPG ); + wShow( newTurnW ); +} + + +EXPORT void InitNewTurn( wMenu_p m ) +{ + int i; + ParamRegister( &turnDesignPG ); + for ( i=0; i<(sizeof designDescs/sizeof designDescs[0]); i++ ) { + wMenuPushCreate( m, NULL, _(designDescs[i]->label), 0, + ShowTurnoutDesigner, (void*)designDescs[i] ); + sprintf( message, "%s SHOW %s", TURNOUTDESIGNER, designDescs[i]->label ); + AddPlaybackProc( message, (playbackProc_p)ShowTurnoutDesigner, designDescs[i] ); + } + roadbedColor = wDrawColorBlack; + includeNontrackSegments = TRUE; +} +#endif + +#ifdef MKTURNOUT + + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> + +char message[1024]; +char * curScaleName; +double trackGauge; +long units = 0; +wDrawColor drawColorBlack; +long roadbedColorRGB = 0; + +EXPORT void AbortProg( + char * msg, + ... ) +{ + static BOOL_T abort2 = FALSE; +// int rc; + va_list ap; + va_start( ap, msg ); + vsprintf( message, msg, ap ); + va_end( ap ); + fprintf( stderr, "%s", message ); + abort(); +} + +void * MyRealloc( void * ptr, long size ) +{ + return realloc( ptr, size ); +} + +EXPORT char * MyStrdup( const char * str ) +{ + char * ret; + ret = (char*)malloc( strlen( str ) + 1 ); + strcpy( ret, str ); + return ret; +} + + +int NoticeMessage( char * msg, char * yes, char * no, ... ) +{ + /*fprintf( stderr, "%s\n", msg );*/ + return 0; +} + +FILE * OpenCustom( char * mode) +{ + return stdout; +} + +void wPrintSetup( wPrintSetupCallBack_p notused ) +{ +} + +EXPORT void ComputeCurvedSeg( + trkSeg_p s, + DIST_T radius, + coOrd p0, + coOrd p1 ) +{ + DIST_T d; + ANGLE_T a, aa, aaa; + s->u.c.radius = radius; + d = FindDistance( p0, p1 )/2.0; + a = FindAngle( p0, p1 ); + if (radius > 0) { + aa = R2D(asin( d/radius )); + aaa = a + (90.0 - aa); + Translate( &s->u.c.center, p0, aaa, radius ); + s->u.c.a0 = NormalizeAngle( aaa + 180.0 ); + s->u.c.a1 = aa*2.0; + } else { + aa = R2D(asin( d/(-radius) )); + aaa = a - (90.0 - aa); + Translate( &s->u.c.center, p0, aaa, -radius ); + s->u.c.a0 = NormalizeAngle( aaa + 180.0 - aa *2.0 ); + s->u.c.a1 = aa*2.0; + } +} + +EXPORT char * Strcpytrimed( char * dst, char * src, BOOL_T double_quotes ) +{ + char * cp; + while (*src && isspace(*src) ) src++; + if (!*src) + return dst; + cp = src+strlen(src)-1; + while ( cp>src && isspace(*cp) ) cp--; + while ( src<=cp ) { + if (*src == '"' && double_quotes) + *dst++ = '"'; + *dst++ = *src++; + } + *dst = '\0'; + return dst; +} + + +EXPORT char * BuildTrimedTitle( char * cp, char * sep, char * mfg, char * desc, char * partno ) +{ + cp = Strcpytrimed( cp, mfg, FALSE ); + strcpy( cp, sep ); + cp += strlen(cp); + cp = Strcpytrimed( cp, desc, FALSE ); + strcpy( cp, sep ); + cp += strlen(cp); + cp = Strcpytrimed( cp, partno, FALSE ); + return cp; +} + + +EXPORT char * PutTitle( char * cp ) +{ + static char title[STR_SIZE]; + char * tp = title; + while (*cp) { + if (*cp == '\"') { + *tp++ = '\"'; + *tp++ = '\"'; + } else { + *tp++ = *cp; + } + cp++; + } + *tp = '\0'; + return title; +} + + +long wDrawGetRGB( + wDrawColor color ) +{ + return roadbedColorRGB; +} + +EXPORT BOOL_T WriteSegs( + FILE * f, + wIndex_t segCnt, + trkSeg_p segs ) +{ + int i, j; + BOOL_T rc = TRUE; + for ( i=0; i<segCnt; i++ ) { + switch ( segs[i].type ) { + case SEG_STRLIN: + case SEG_STRTRK: + rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f %0.6f\n", + segs[i].type, (segs[i].type==SEG_STRTRK?0:roadbedColorRGB), segs[i].width, + segs[i].u.l.pos[0].x, segs[i].u.l.pos[0].y, + segs[i].u.l.pos[1].x, segs[i].u.l.pos[1].y )>0; + break; + case SEG_CRVTRK: + case SEG_CRVLIN: + rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f\n", + segs[i].type, (segs[i].type==SEG_CRVTRK?0:roadbedColorRGB), segs[i].width, + segs[i].u.c.radius, + segs[i].u.c.center.x, segs[i].u.c.center.y, + segs[i].u.c.a0, segs[i].u.c.a1 )>0; + break; + case SEG_FILCRCL: + rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f\n", + segs[i].type, roadbedColorRGB, segs[i].width, + segs[i].u.c.radius, + segs[i].u.c.center.x, segs[i].u.c.center.y )>0; + break; + case SEG_POLY: + case SEG_FILPOLY: + rc &= fprintf( f, "\t%c %ld %0.6f %d\n", + segs[i].type, roadbedColorRGB, segs[i].width, + segs[i].u.p.cnt )>0; + for ( j=0; j<segs[i].u.p.cnt; j++ ) + rc &= fprintf( f, "\t\t%0.6f %0.6f\n", + segs[i].u.p.pts[j].x, segs[i].u.p.pts[j].y )>0; + break; + } + } + rc &= fprintf( f, "\tEND\n" )>0; + return rc; +} + +BOOL_T WriteCompoundPathsEndPtsSegs( + FILE * f, + PATHPTR_T paths, + wIndex_t segCnt, + trkSeg_p segs, + EPINX_T endPtCnt, + trkEndPt_t * endPts ) +{ + int i; + PATHPTR_T pp; + BOOL_T rc = TRUE; + for ( pp=paths; *pp; pp+=2 ) { + rc &= fprintf( f, "\tP \"%s\"", (char*)pp )>0; + for ( pp+=strlen((char*)pp)+1; pp[0]!=0||pp[1]!=0; pp++ ) + rc &= fprintf( f, " %d", *pp )>0; + rc &= fprintf( f, "\n" )>0; + } + for ( i=0; i<endPtCnt; i++ ) + rc &= fprintf( f, "\tE %0.6f %0.6f %0.6f\n", + endPts[i].pos.x, endPts[i].pos.y, endPts[i].angle )>0; +#ifdef MKTURNOUT + if ( specialLine[0] ) + rc &= fprintf( f, "%s\n", specialLine ); +#endif + rc &= WriteSegs( f, segCnt, segs ); + return rc; +} + + +void Usage( int argc, char **argv ) +{ + int inx; + for (inx=1;inx<argc;inx++) + fprintf( stderr, "%s ", argv[inx] ); + fprintf( stderr, +"\nUsage: [-m] [-u] [-r#] [-c#] [-l#]\n" +" <SCL> <MNF> B <DSC> <PNO> <LEN> # Create bumper\n" +" <SCL> <MNF> S <DSC> <PNO> <LEN> # Create straight track\n" +" <SCL> <MNF> J <DSC> <PNO> <LEN1> <LEN2> # Create adjustable track\n" +" <SCL> <MNF> C <DSC> <PNO> <RAD> <ANG> # Create curved track\n" +" <SCL> <MNF> R <LDSC> <LPNO> <RDSC> <RPNO> <LEN2> <ANG> <OFF> <LEN1> # Create Regular Turnout\n" +" <SCL> <MNF> Q <LDSC> <LPNO> <RDSC> <RPNO> <RAD> <ANG> <LEN> # Create Radial Turnout\n" +" <SCL> <MNF> V <LDSC> <LPNO> <RDSC> <RPNO> <LEN1> <ANG1> <OFF1> <LEN2> <ANG2> <OFF2> # Create Curved Turnout\n" +" <SCL> <MNF> W <LDSC> <LPNO> <RDSC> <RPNO> <RAD1> <ANG2> <RAD2> <ANG2> # Create Radial Curved Turnout\n" +" <SCL> <MNF> Y <LDSC> <LPNO> <RDSC> <RPNO> <LENL> <ANGL> <OFFL> <LENR> <ANGR> <OFFR> # Create Wye Turnout\n" +" <SCL> <MNF> 3 <DSC> <PNO> <LEN0> <LENL> <ANGL> <OFFL> <LENR> <ANGR> <OFFR> # Create 3-Way Turnout\n" +" <SCL> <MNF> X <DSC> <PNO> <LEN1> <ANG> <LEN2> # Create Crossing\n" +" <SCL> <MNF> 1 <DSC> <PNO> <LEN1> <ANG> <LEN2> # Create Single Slipswitch\n" +" <SCL> <MNF> 2 <DSC> <PNO> <LEN1> <ANG> <LEN2> # Create Double Slipswitch\n" +" <SCL> <MNF> D <DSC> <PNO> <LEN> <OFF> # Create Double Crossover\n" +" <SCL> <MNF> T <DSC> <PNO> <CNT> <IN-DIAM> <OUT-DIAM> # Create TurnTable\n" +); + exit(1); +} + +struct { + char * scale; + double trackGauge; + } scaleMap[] = { + { "N", 0.3531 }, + { "HO", 0.6486 }, + { "O", 1.1770 }, + { "HOm", 0.472440 }, + { "G", 1.770 } + }; + + + +int main ( int argc, char * argv[] ) +{ +// char * cmd; + double radius, radius2; + int inx, cnt; + double ang, x0, y0, x1, y1; + char **argv0; + int argc0; + + argc0 = argc; + argv0 = argv; + doCustomInfoLine = FALSE; + argv++; + + if (argc < 7) { + Usage(argc0,argv0); + } + + while ( argv[0][0] == '-' ) { + switch (argv[0][1]) { + case 'm': + units = UNITS_METRIC; + break; + case 'u': + doCustomInfoLine = TRUE; + break; + case 'r': + doRoadBed = TRUE; + if (argv[0][2] == '\0') + Usage(argc0,argv0); + newTurnRoadbedWidth = atof(&argv[0][2]); + roadbedColorRGB = 0; + roadbedColor = 0; + newTurnRoadbedLineWidth = 0; + break; + case 'c': + roadbedColorRGB = atol(&argv[0][2]); + break; + case 'l': + newTurnRoadbedLineWidth = atol(&argv[0][2]); + break; + default: + fprintf( stderr, "Unknown option: %s\n", argv[0] ); + } + argv++; + argc--; + } + + newTurnScaleName = curScaleName = *argv++; + trackGauge = 0.0; + for ( inx=0; inx<sizeof scaleMap/sizeof scaleMap[0]; inx++ ) { + if (strcmp( curScaleName, scaleMap[inx].scale ) == 0 ) { + newTurnTrackGauge = trackGauge = scaleMap[inx].trackGauge; + break; + } + } + if (trackGauge == 0.0) { + fprintf( stderr, "Unknown scale: %s\n", curScaleName ); + exit(1); + } + strcpy( newTurnManufacturer, *argv++ ); + specialLine[0] = '\0'; + switch (tolower((*argv++)[0])) { + case 'b': + if (argc != 7) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + newTurnLen1 = GetDim(atof( *argv++ )); + curDesign = &StrSectionDesc; + NewTurnOk( &StrSectionDesc ); + break; + case 's': + if (argc != 7) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + newTurnLen1 = GetDim(atof( *argv++ )); + curDesign = &StrSectionDesc; + NewTurnOk( &StrSectionDesc ); + break; + case 'j': + if (argc != 8) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + newTurnLen1 = GetDim(atof( *argv++ )); + newTurnLen2 = GetDim(atof( *argv++ )); + sprintf( specialLine, "\tX adjustable %0.6f %0.6f", newTurnLen1, newTurnLen2 ); + curDesign = &StrSectionDesc; + NewTurnOk( &StrSectionDesc ); + break; + case 'c': + if (argc != 8) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + newTurnLen1 = GetDim(atof( *argv++ )); + newTurnAngle1 = atof( *argv++ ); + curDesign = &CrvSectionDesc; + NewTurnOk( &CrvSectionDesc ); + break; + case 'r': + if (argc != 12) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + strcpy( newTurnRightDesc, *argv++ ); + strcpy( newTurnRightPartno, *argv++ ); + newTurnLen1 = GetDim(atof( *argv++ )); + newTurnAngle1 = atof( *argv++ ); + newTurnOff1 = GetDim(atof( *argv++ )); + newTurnLen0 = GetDim(atof( *argv++ )); + curDesign = &RegDesc; + NewTurnOk( &RegDesc ); + break; + case 'q': + if (argc != 11) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + strcpy( newTurnRightDesc, *argv++ ); + strcpy( newTurnRightPartno, *argv++ ); + radius = GetDim(atof( *argv++ )); + newTurnAngle1 = atof( *argv++ ); + newTurnLen0 = GetDim(atof( *argv++ )); + newTurnLen1 = radius * sin(D2R(newTurnAngle1)); + newTurnOff1 = radius * (1-cos(D2R(newTurnAngle1))); + curDesign = &RegDesc; + NewTurnOk( &RegDesc ); + break; + case 'v': + if (argc != 14) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + strcpy( newTurnRightDesc, *argv++ ); + strcpy( newTurnRightPartno, *argv++ ); + newTurnLen2 = GetDim(atof( *argv++ )); + newTurnAngle2 = atof( *argv++ ); + newTurnOff2 = GetDim(atof( *argv++ )); + newTurnLen1 = GetDim(atof( *argv++ )); + newTurnAngle1 = atof( *argv++ ); + newTurnOff1 = GetDim(atof( *argv++ )); + curDesign = &CrvDesc; + NewTurnOk( &CrvDesc ); + break; + case 'w': + if (argc != 12) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + strcpy( newTurnRightDesc, *argv++ ); + strcpy( newTurnRightPartno, *argv++ ); + radius = GetDim(atof( *argv++ )); + newTurnAngle1 = atof( *argv++ ); + newTurnLen1 = radius * sin(D2R(newTurnAngle1)); + newTurnOff1 = radius * (1-cos(D2R(newTurnAngle1))); + radius = GetDim(atof( *argv++ )); + newTurnAngle2 = atof( *argv++ ); + newTurnLen2 = radius * sin(D2R(newTurnAngle2)); + newTurnOff2 = radius * (1-cos(D2R(newTurnAngle2))); + curDesign = &CrvDesc; + NewTurnOk( &CrvDesc ); + break; + case 'y': + if (argc != 14) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + strcpy( newTurnRightDesc, *argv++ ); + strcpy( newTurnRightPartno, *argv++ ); + newTurnLen1 = GetDim(atof( *argv++ )); + newTurnAngle1 = atof( *argv++ ); + newTurnOff1 = GetDim(atof( *argv++ )); + newTurnLen2 = GetDim(atof( *argv++ )); + newTurnAngle2 = atof( *argv++ ); + newTurnOff2 = GetDim(atof( *argv++ )); + curDesign = &WyeDesc; + NewTurnOk( &WyeDesc ); + break; + case '3': + if (argc != 13) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + newTurnLen0 = GetDim(atof( *argv++ )); + newTurnLen1 = GetDim(atof( *argv++ )); + newTurnAngle1 = atof( *argv++ ); + newTurnOff1 = GetDim(atof( *argv++ )); + newTurnLen2 = GetDim(atof( *argv++ )); + newTurnAngle2 = atof( *argv++ ); + newTurnOff2 = GetDim(atof( *argv++ )); + curDesign = &ThreewayDesc; + NewTurnOk( &ThreewayDesc ); + break; + case 'x': + if (argc<9) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + newTurnLen1 = GetDim(atof( *argv++ )); + newTurnAngle1 = atof( *argv++ ); + newTurnLen2 = GetDim(atof( *argv++ )); + curDesign = &CrossingDesc; + NewTurnOk( &CrossingDesc ); + break; + case '1': + if (argc<9) Usage(argc0,argv0); + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + newTurnLen1 = GetDim(atof( *argv++ )); + newTurnAngle1 = atof( *argv++ ); + newTurnLen2 = GetDim(atof( *argv++ )); + curDesign = &SingleSlipDesc; + NewTurnOk( &SingleSlipDesc ); + break; + case '2': + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + if (argc<9) Usage(argc0,argv0); + newTurnLen1 = GetDim(atof( *argv++ )); + newTurnAngle1 = atof( *argv++ ); + newTurnLen2 = GetDim(atof( *argv++ )); + curDesign = &DoubleSlipDesc; + NewTurnOk( &DoubleSlipDesc ); + break; + case 'd': + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + if (argc<8) Usage(argc0,argv0); + newTurnLen1 = GetDim(atof( *argv++ )); + newTurnOff1 = GetDim(atof( *argv++ )); + curDesign = &DoubleCrossoverDesc; + NewTurnOk( &DoubleCrossoverDesc ); + break; + case 't': + strcpy( newTurnLeftDesc, *argv++ ); + strcpy( newTurnLeftPartno, *argv++ ); + if (argc<9) Usage(argc0,argv0); + cnt = atoi( *argv++ )/2; + radius = GetDim(atof( *argv++ ))/2.0; + radius2 = GetDim(atof( *argv++ ))/2.0; + BuildTrimedTitle( message, "\t", newTurnManufacturer, newTurnLeftDesc, newTurnLeftPartno ); + fprintf( stdout, "TURNOUT %s \"%s\"\n", curScaleName, PutTitle(message) ); + for (inx=0; inx<cnt; inx++) { + fprintf( stdout, "\tP \"%d\" %d %d %d\n", inx+1, inx*3+1, inx*3+2, inx*3+3 ); + } + for (inx=0; inx<cnt; inx++) { + fprintf( stdout, "\tP \"%d\" %d %d %d\n", inx+1+cnt, -(inx*3+3), -(inx*3+2), -(inx*3+1) ); + } + for (inx=0; inx<cnt; inx++) { + ang = inx*180.0/cnt; + x0 = radius2 * sin(D2R(ang)); + y0 = radius2 * cos(D2R(ang)); + fprintf( stdout, "\tE %0.6f %0.6f %0.6f\n", x0, y0, ang ); + fprintf( stdout, "\tE %0.6f %0.6f %0.6f\n", -x0, -y0, ang+180.0 ); + } + for (inx=0; inx<cnt; inx++) { + ang = inx*180.0/cnt; + x0 = radius2 * sin(D2R(ang)); + y0 = radius2 * cos(D2R(ang)); + x1 = radius * sin(D2R(ang)); + y1 = radius * cos(D2R(ang)); + fprintf( stdout, "\tS 0 0 %0.6f %0.6f %0.6f %0.6f\n", x0, y0, x1, y1 ); + fprintf( stdout, "\tS 16777215 0 %0.6f %0.6f %0.6f %0.6f\n", x1, y1, -x1, -y1 ); + fprintf( stdout, "\tS 0 0 %0.6f %0.6f %0.6f %0.6f\n", -x1, -y1, -x0, -y0 ); + } + fprintf( stdout, "\tA 16711680 0 %0.6f 0.000000 0.000000 0.000000 360.000000\n", radius2 ); + fprintf( stdout, "\tA 16711680 0 %0.6f 0.000000 0.000000 0.000000 360.000000\n", radius ); + fprintf( stdout, "\tEND\n" ); + break; + default: + fprintf( stderr, "Invalid command: %s\n", argv[-1] ); + exit(1); + } + exit(0); +} +#endif diff --git a/app/bin/ctrain.c b/app/bin/ctrain.c new file mode 100644 index 0000000..b78dc9e --- /dev/null +++ b/app/bin/ctrain.c @@ -0,0 +1,2586 @@ +/** \file ctrain.c + * Functions related to running trains + * + */ + +/* 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 WINDOWS +#include <errno.h> +#endif +#include <ctype.h> + +#define PRIVATE_EXTRADATA +#include "track.h" +#include "trackx.h" +#include "ctrain.h" +#include "compound.h" +#include "i18n.h" + +EXPORT long programMode; +EXPORT long maxCouplingSpeed = 100; +EXPORT long hideTrainsInTunnels; + +extern int doDrawTurnoutPosition; +extern void NextTurnoutPosition( track_p ); + +static TRKTYP_T T_CAR = -1; + +typedef enum { ST_NotOnTrack, ST_StopManual, ST_EndOfTrack, ST_OpenTurnout, ST_NoRoom, ST_Crashed } trainStatus_e; + +struct extraData { + traverseTrack_t trvTrk; + long state; + carItem_p item; + double speed; + BOOL_T direction; + BOOL_T autoReverse; + trainStatus_e status; + DIST_T distance; + coOrd couplerPos[2]; + LAYER_T trkLayer; + }; +#define NOTALAYER (127) + +#define CAR_STATE_IGNORED (1L<<17) +#define CAR_STATE_PROCESSED (1L<<18) +#define CAR_STATE_LOCOISMASTER (1L<<19) +#define CAR_STATE_ONHIDENTRACK (1L<<20) + + +#define IsOnTrack( XX ) ((XX)->trvTrk.trk!=NULL) +#define IsIgnored( XX ) (((XX)->state&CAR_STATE_IGNORED)!=0) +#define SetIgnored( XX ) (XX)->state |= CAR_STATE_IGNORED +#define ClrIgnored( XX ) (XX)->state &= ~CAR_STATE_IGNORED +#ifdef LATER +#define IsLocoMaster( XX ) (((XX)->state&CAR_STATE_LOCOISMASTER)!=0) +#define SetLocoMaster( XX ) (XX)->state |= CAR_STATE_LOCOISMASTER +#define ClrLocoMaster( XX ) (XX)->state &= ~CAR_STATE_LOCOISMASTER +#endif +#define IsLocoMaster( XX ) CarItemIsLocoMaster((XX)->item) +#define SetLocoMaster( XX ) CarItemSetLocoMaster((XX)->item,TRUE) +#define ClrLocoMaster( XX ) CarItemSetLocoMaster((XX)->item,FALSE) +#define IsProcessed( XX ) (((XX)->state&CAR_STATE_PROCESSED)!=0) +#define SetProcessed( XX ) (XX)->state |= CAR_STATE_PROCESSED +#define ClrProcessed( XX ) (XX)->state &= ~CAR_STATE_PROCESSED + +static wButton_p newcarB; + +static void ControllerDialogSyncAll( void ); +static STATUS_T CmdTrain( wAction_t, coOrd ); +static wMenu_p trainPopupM; +static wMenuPush_p trainPopupMI[8]; +static track_p followTrain; +static coOrd followCenter; +static BOOL_T trainsTimeoutPending; +static enum { TRAINS_STOP, TRAINS_RUN, TRAINS_IDLE, TRAINS_PAUSE } trainsState; +static wIcon_p stopI, goI; +static void RestartTrains( void ); +static void DrawAllCars( void ); +static void UncoupleCars( track_p, track_p ); +static void TrainTimeEndPause( void ); +static void TrainTimeStartPause( void ); + +static int log_trainMove; +static int log_trainPlayback; + +static void PlaceCar( track_p ); + + +#define WALK_CARS_START( CAR, XX, DIR ) \ + while (1) { \ + (XX) = GetTrkExtraData(CAR);\ + { \ + +#define WALK_CARS_END( CAR, XX, DIR ) \ + } \ + { \ + track_p walk_cars_temp1; \ + if ( (walk_cars_temp1=GetTrkEndTrk(CAR,DIR)) == NULL ) break; \ + (DIR)=(GetTrkEndTrk(walk_cars_temp1,0)==(CAR)?1:0); \ + (CAR)=walk_cars_temp1; \ + } \ + } + + +/* + * Generic Commands + */ + +EXPORT void CarGetPos( + track_p car, + coOrd * posR, + ANGLE_T * angleR ) +{ + struct extraData * xx = GetTrkExtraData( car ); + if ( GetTrkType(car) != T_CAR ) + AbortProg( "getCarPos" ); + *posR = xx->trvTrk.pos; + *angleR = xx->trvTrk.angle; +} + +EXPORT void CarSetVisible( + track_p car ) +{ + struct extraData * xx; + int dir; + dir = 0; + WALK_CARS_START( car, xx, dir ) + if ( GetTrkType(car) != T_CAR ) + AbortProg( "carSetVisible" ); + WALK_CARS_END( car, xx, dir ) + dir = 1-dir; + WALK_CARS_START( car, xx, dir ) { + xx->state &= ~(CAR_STATE_ONHIDENTRACK); + xx->trkLayer = NOTALAYER; + } + WALK_CARS_END( car, xx, dir ) +} + + +static struct { + long index; + coOrd pos; + ANGLE_T angle; + DIST_T length; + DIST_T width; + char desc[STR_SIZE]; + char number[STR_SIZE]; + } carData; +typedef enum { IT, PN, AN, LN, WD, DE, NM } carDesc_e; +static descData_t carDesc[] = { +/*IT*/ { DESC_LONG, N_("Index"), &carData.index }, +/*PN*/ { DESC_POS, N_("Position"), &carData.pos }, +/*AN*/ { DESC_ANGLE, N_("Angle"), &carData.angle }, +/*LN*/ { DESC_DIM, N_("Length"), &carData.length }, +/*WD*/ { DESC_DIM, N_("Width"), &carData.width }, +/*DE*/ { DESC_STRING, N_("Description"), &carData.desc }, +/*NM*/ { DESC_STRING, N_("Rep Marks"), &carData.number }, + { DESC_NULL } }; + +static void UpdateCar( + track_p trk, + int inx, + descData_p descUpd, + BOOL_T needUndoStart ) +{ + BOOL_T titleChanged; + const char * cp; + if ( inx == -1 ) { + titleChanged = FALSE; + cp = wStringGetValue( (wString_p)carDesc[NM].control0 ); + if ( cp && strcmp( carData.number, cp ) != 0 ) { + titleChanged = TRUE; + strcpy( carData.number, cp ); + } + if ( !titleChanged ) + return; + if ( needUndoStart ) + UndoStart( _("Change Track"), "Change Track" ); + UndoModify( trk ); + UndrawNewTrack( trk ); + DrawNewTrack( trk ); + return; + } + UndrawNewTrack( trk ); + switch (inx) { + case NM: + break; + default: + break; + } + DrawNewTrack( trk ); +} + + +static void DescribeCar( + track_p trk, + char * str, + CSIZE_T len ) +{ + struct extraData *xx = GetTrkExtraData(trk); + char * cp; + coOrd size; + + CarItemSize( xx->item, &size ); + carData.length = size.x; + carData.width = size.y; + cp = CarItemDescribe( xx->item, 0, &carData.index ); + strcpy( carData.number, CarItemNumber(xx->item) ); + strncpy( str, cp, len ); + carData.pos = xx->trvTrk.pos; + carData.angle = xx->trvTrk.angle; + cp = CarItemDescribe( xx->item, -1, NULL ); + strncpy( carData.desc, cp, sizeof carData.desc ); + carDesc[IT].mode = + carDesc[PN].mode = + carDesc[AN].mode = + carDesc[LN].mode = + carDesc[WD].mode = DESC_RO; + carDesc[DE].mode = + carDesc[NM].mode = DESC_RO; + DoDescribe( _("Car"), trk, carDesc, UpdateCar ); +} + + +EXPORT void FlipTraverseTrack( + traverseTrack_p trvTrk ) +{ + trvTrk->angle = NormalizeAngle( trvTrk->angle + 180.0 ); + if ( trvTrk->length > 0 ) + trvTrk->dist = trvTrk->length - trvTrk->dist; +} + + +EXPORT BOOL_T TraverseTrack2( + traverseTrack_p trvTrk0, + DIST_T dist0 ) +{ + traverseTrack_t trvTrk = *trvTrk0; + DIST_T dist = dist0; + if ( dist0 < 0 ) { + dist = -dist; + FlipTraverseTrack( &trvTrk ); + } + if ( trvTrk.trk==NULL || + (!TraverseTrack(&trvTrk,&dist)) || + trvTrk.trk==NULL || + dist!=0.0 ) { + Translate( &trvTrk.pos, trvTrk.pos, trvTrk.angle, dist ); + } + if ( dist0 < 0 ) + FlipTraverseTrack( &trvTrk ); + *trvTrk0 = trvTrk; + return TRUE; +} + + + +static BOOL_T drawCarEnable = TRUE; +static BOOL_T noCarDraw = FALSE; + +static void DrawCar( + track_p car, + drawCmd_p d, + wDrawColor color ) +{ + struct extraData * xx = GetTrkExtraData(car); + int dir; + vector_t coupler[2]; + track_p car1; + struct extraData * xx1; + int dir1; + + if ( drawCarEnable == FALSE ) + return; + /*d = &tempD;*/ +/* + if ( !IsVisible(xx) ) + return; +*/ + if ( d == &mapD ) + return; + if ( noCarDraw ) + return; + if ( hideTrainsInTunnels && + ( (((xx->state&CAR_STATE_ONHIDENTRACK)!=0) && drawTunnel==0) || + (xx->trkLayer!=NOTALAYER && !GetLayerVisible(xx->trkLayer)) ) ) + return; + + for ( dir=0; dir<2; dir++ ) { + coupler[dir].pos = xx->couplerPos[dir]; + if ( (car1 = GetTrkEndTrk(car,dir)) ) { + xx1 = GetTrkExtraData(car1); + dir1 = (GetTrkEndTrk(car1,0)==car)?0:1; + coupler[dir].angle = FindAngle( xx->couplerPos[dir], xx1->couplerPos[dir1] ); + } else { + coupler[dir].angle = NormalizeAngle(xx->trvTrk.angle+(dir==0?0.0:180.0)-15.0); + } + } + CarItemDraw( d, xx->item, color, xx->direction, IsLocoMaster(xx), coupler ); +} + + +static DIST_T DistanceCar( + track_p trk, + coOrd * pos ) +{ + struct extraData * xx = GetTrkExtraData(trk); + DIST_T dist; + coOrd pos1; + coOrd size; + + xx = GetTrkExtraData(trk); + if ( IsIgnored(xx) ) + return 10000.0; + + CarItemSize( xx->item, &size ); /* TODO assumes xx->trvTrk.pos is the car center */ + dist = FindDistance( *pos, xx->trvTrk.pos ); + if ( dist < size.x/2.0 ) { + pos1 = *pos; + Rotate( &pos1, xx->trvTrk.pos, -xx->trvTrk.angle ); + pos1.x += -xx->trvTrk.pos.x + size.y/2.0; /* TODO: why not size.x? */ + pos1.y += -xx->trvTrk.pos.y + size.x/2.0; + if ( pos1.x >= 0 && pos1.x <= size.y && + pos1.y >= 0 && pos1.y <= size.x ) + dist = 0; + } + *pos = xx->trvTrk.pos; + return dist; +} + + +static void SetCarBoundingBox( + track_p car ) +{ + struct extraData * xx = GetTrkExtraData(car); + coOrd lo, hi, p[4]; + int inx; + coOrd size; + +/* TODO: should be bounding box of all pieces aligned on track */ + CarItemSize( xx->item, &size ); /* TODO assumes xx->trvTrk.pos is the car center */ + Translate( &p[0], xx->trvTrk.pos, xx->trvTrk.angle, size.x/2.0 ); + Translate( &p[1], p[0], xx->trvTrk.angle+90, size.y/2.0 ); + Translate( &p[0], p[0], xx->trvTrk.angle-90, size.y/2.0 ); + Translate( &p[2], xx->trvTrk.pos, xx->trvTrk.angle+180, size.x/2.0 ); + Translate( &p[3], p[2], xx->trvTrk.angle+90, size.y/2.0 ); + Translate( &p[2], p[2], xx->trvTrk.angle-90, size.y/2.0 ); + lo = hi = p[0]; + for ( inx = 1; inx < 4; inx++ ) { + if ( p[inx].x < lo.x ) + lo.x = p[inx].x; + if ( p[inx].y < lo.y ) + lo.y = p[inx].y; + if ( p[inx].x > hi.x ) + hi.x = p[inx].x; + if ( p[inx].y > hi.y ) + hi.y = p[inx].y; + } + SetBoundingBox( car, hi, lo ); + +} + + +EXPORT track_p NewCar( + wIndex_t index, + carItem_p item, + coOrd pos, + ANGLE_T angle ) +{ + track_p trk; + struct extraData * xx; + + trk = NewTrack( index, T_CAR, 2, sizeof (*xx) ); + /*SetEndPts( trk, 0 );*/ + xx = GetTrkExtraData(trk); + /*SetTrkVisible( trk, IsVisible(xx) );*/ + xx->item = item; + xx->trvTrk.pos = pos; + xx->trvTrk.angle = angle; + xx->state = 0; + SetCarBoundingBox( trk ); + CarItemSetTrack( item, trk ); + PlaceCar( trk ); + return trk; +} + + +static void DeleteCar( + track_p trk ) +{ + struct extraData * xx = GetTrkExtraData(trk); + CarItemSetTrack( xx->item, NULL ); +} + + +static void ReadCar( + char * line ) +{ + CarItemRead( line ); +} + + +static BOOL_T WriteCar( + track_p trk, + FILE * f ) +{ + BOOL_T rc = TRUE; + return rc; +} + + +static void MoveCar( + track_p car, + coOrd pos ) +{ + struct extraData *xx = GetTrkExtraData(car); + xx->trvTrk.pos.x += pos.x; + xx->trvTrk.pos.y += pos.y; + xx->trvTrk.trk = NULL; + PlaceCar( car ); + SetCarBoundingBox(car); +} + + +static void RotateCar( + track_p car, + coOrd pos, + ANGLE_T angle ) +{ + struct extraData *xx = GetTrkExtraData(car); + Rotate( &xx->trvTrk.pos, pos, angle ); + xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle + angle ); + xx->trvTrk.trk = NULL; + PlaceCar( car ); + SetCarBoundingBox( car ); +} + + +static BOOL_T QueryCar( track_p trk, int query ) +{ + switch ( query ) { + case Q_NODRAWENDPT: + return TRUE; + default: + return FALSE; + } +} + + +static trackCmd_t carCmds = { + "CAR ", + DrawCar, /* draw */ + DistanceCar, /* distance */ + DescribeCar, /* describe */ + DeleteCar, /* delete */ + WriteCar, /* write */ + ReadCar, /* read */ + MoveCar, /* move */ + RotateCar, /* rotate */ + NULL, /* rescale */ + NULL, /* audit */ + NULL, /* getAngle */ + NULL, /* split */ + NULL, /* traverse */ + NULL, /* enumerate */ + NULL, /* redraw*/ + NULL, /* trim*/ + NULL, /* merge*/ + NULL, /* modify */ + NULL, /* getLength */ + NULL, /* getParams */ + NULL, /* moveEndPt */ + QueryCar, /* query */ + NULL, /* ungroup */ + NULL, /* flip */ }; + +/* + * + */ + + +static int numTrainDlg; + + +#define SLIDER_WIDTH (20) +#define SLIDER_HEIGHT (200) +#define SLIDER_THICKNESS (10) +#define MAX_SPEED (100.0) + +typedef struct { + wWin_p win; + wIndex_t inx; + track_p train; + long direction; + long followMe; + long autoReverse; + coOrd pos; + char posS[STR_SHORT_SIZE]; + DIST_T speed; + char speedS[10]; + paramGroup_p trainPGp; + } trainControlDlg_t, * trainControlDlg_p; +static trainControlDlg_t * curTrainDlg; + + +static void SpeedRedraw( wDraw_p, void *, wPos_t, wPos_t ); +static void SpeedAction( wAction_t, coOrd ); +static void LocoListChangeEntry( track_p, track_p ); +static void CmdTrainExit( void * ); + +drawCmd_t speedD = { + NULL, + &screenDrawFuncs, + 0, + 1.0, + 0.0, + { 0.0, 0.0 }, + { 0.0, 0.0 }, + Pix2CoOrd, + CoOrd2Pix }; +static paramDrawData_t speedParamData = { SLIDER_WIDTH, SLIDER_HEIGHT, SpeedRedraw, SpeedAction, &speedD }; +#ifndef WINDOWS +static paramListData_t listData = { 3, 120 }; +#endif +static char * trainFollowMeLabels[] = { N_("Follow"), NULL }; +static char * trainAutoReverseLabels[] = { N_("Auto Reverse"), NULL }; +static paramData_t trainPLs[] = { +#define I_LIST (0) +#ifdef WINDOWS +/*0*/ { PD_DROPLIST, NULL, "list", PDO_NOPREF|PDO_NOPSHUPD, (void*)120, NULL, 0 }, +#else +/*0*/ { PD_LIST, NULL, "list", PDO_NOPREF|PDO_NOPSHUPD, &listData, NULL, 0 }, +#endif +#define I_STATUS (1) + { PD_MESSAGE, NULL, NULL, 0, (void*)120 }, +#define I_POS (2) + { PD_MESSAGE, NULL, NULL, 0, (void*)120 }, +#define I_SLIDER (3) + { PD_DRAW, NULL, "speed", PDO_NOPSHUPD|PDO_DLGSETY, &speedParamData }, +#define I_DIST (4) + { PD_STRING, NULL, "distance", PDO_DLGNEWCOLUMN, (void*)(100-SLIDER_WIDTH), NULL, BO_READONLY }, +#define I_ZERO (5) + { PD_BUTTON, NULL, "zeroDistance", PDO_NOPSHUPD|PDO_NOPREF|PDO_DLGHORZ, NULL, NULL, BO_ICON }, +#define I_GOTO (6) + { PD_BUTTON, NULL, "goto", PDO_NOPSHUPD|PDO_NOPREF|PDO_DLGWIDE, NULL, N_("Find") }, +#define I_FOLLOW (7) + { PD_TOGGLE, NULL, "follow", PDO_NOPREF|PDO_DLGWIDE, trainFollowMeLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_AUTORVRS (8) + { PD_TOGGLE, NULL, "autoreverse", PDO_NOPREF, trainAutoReverseLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_DIR (9) + { PD_BUTTON, NULL, "direction", PDO_NOPREF|PDO_DLGWIDE, NULL, N_("Forward"), 0 }, +#define I_STOP (10) + { PD_BUTTON, NULL, "stop", PDO_DLGWIDE, NULL, N_("Stop") }, +#define I_SPEED (11) + { PD_MESSAGE, NULL, NULL, PDO_DLGIGNOREX, (void *)120 } }; + +static paramGroup_t trainPG = { "train", 0, trainPLs, sizeof trainPLs/sizeof trainPLs[0] }; + + +typedef struct { + track_p loco; + BOOL_T running; + } locoList_t; +dynArr_t locoList_da; +#define locoList(N) DYNARR_N( locoList_t, locoList_da, N ) + +static wIndex_t FindLoco( + track_p loco ) +{ + wIndex_t inx; + for ( inx = 0; inx<locoList_da.cnt; inx++ ) { + if ( locoList(inx).loco == loco ) + return inx; + } + return -1; +} + +/** + * Update the speed display when running trains. Draw the slider in the + * correct position and update the odometer. + * + * \param d IN drawing area for slider + * \param d IN the dialog + * \param w, h IN unused? + * \return describe the return value + */ + +static void SpeedRedraw( + wDraw_p d, + void * context, + wPos_t w, + wPos_t h ) +{ + wPos_t y, pts[4][2]; + trainControlDlg_p dlg = (trainControlDlg_p)context; + struct extraData * xx; + wDrawColor drawColor; + + wDrawClear( d ); + if ( dlg == NULL || dlg->train == NULL ) return; + xx = GetTrkExtraData( dlg->train ); + if ( xx->speed > MAX_SPEED ) + xx->speed = MAX_SPEED; + if ( xx->speed < 0 ) + xx->speed = 0; + y = (wPos_t)(xx->speed/MAX_SPEED*((SLIDER_HEIGHT-SLIDER_THICKNESS))+SLIDER_THICKNESS/2); + + drawColor = wDrawFindColor( wRGB( 160, 160, 160) ); + pts[0][1] = pts[1][1] = y-SLIDER_THICKNESS/2; + pts[2][1] = pts[3][1] = y+SLIDER_THICKNESS/2; + pts[0][0] = pts[3][0] = 0; + pts[1][0] = pts[2][0] = SLIDER_WIDTH; + wDrawFilledPolygon( d, pts, 4, drawColor, 0 ); + + drawColor = wDrawFindColor( wRGB( 220, 220, 220) ); + pts[0][1] = pts[1][1] = y+SLIDER_THICKNESS/2; + pts[2][1] = pts[3][1] = y; + pts[0][0] = pts[3][0] = 0; + pts[1][0] = pts[2][0] = SLIDER_WIDTH; + wDrawFilledPolygon( d, pts, 4, drawColor, 0 ); + + wDrawLine( d, 0, y, SLIDER_WIDTH, y, 1, wDrawLineSolid, drawColorRed, 0 ); + wDrawLine( d, 0, y+SLIDER_THICKNESS/2, SLIDER_WIDTH, y+SLIDER_THICKNESS/2, 1, wDrawLineSolid, drawColorBlack, 0 ); + wDrawLine( d, 0, y-SLIDER_THICKNESS/2, SLIDER_WIDTH, y-SLIDER_THICKNESS/2, 1, wDrawLineSolid, drawColorBlack, 0 ); + + sprintf( dlg->speedS, "%3d %s", (int)(units==UNITS_ENGLISH?xx->speed:xx->speed*1.6), (units==UNITS_ENGLISH?"mph":"km/h") ); + ParamLoadMessage( dlg->trainPGp, I_SPEED, dlg->speedS ); + LOG( log_trainPlayback, 3, ( "Speed = %d\n", (int)xx->speed ) ); +} + + +static void SpeedAction( + wAction_t action, + coOrd pos ) +{ + /*trainControlDlg_p dlg = (trainControlDlg_p)wDrawGetContext(d);*/ + trainControlDlg_p dlg = curTrainDlg; + struct extraData * xx; + FLOAT_T speed; + BOOL_T startStop; + if ( dlg == NULL || dlg->train == NULL ) + return; + xx = GetTrkExtraData( dlg->train ); + switch ( action ) { + case C_DOWN: + InfoMessage( "" ); + case C_MOVE: + case C_UP: + TrainTimeEndPause(); + if ( IsOnTrack(xx) ) { + speed = ((FLOAT_T)((pos.y*speedD.dpi)-SLIDER_THICKNESS/2))/(SLIDER_HEIGHT-SLIDER_THICKNESS)*MAX_SPEED; + } else { + speed = 0; + } + if ( speed > MAX_SPEED ) + speed = MAX_SPEED; + if ( speed < 0 ) + speed = 0; + startStop = (xx->speed == 0) != (speed == 0); + xx->speed = speed; + SpeedRedraw( (wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control, dlg, SLIDER_WIDTH, SLIDER_HEIGHT ); + if ( startStop ) { + if ( xx->speed == 0 ) + xx->status = ST_StopManual; + LocoListChangeEntry( dlg->train, dlg->train ); + } + TrainTimeStartPause(); + if ( trainsState == TRAINS_IDLE ) + RestartTrains(); + break; + default: + break; + } +} + + +static void ControllerDialogSync( + trainControlDlg_p dlg ) +{ + struct extraData * xx=NULL; + wIndex_t inx; + BOOL_T dir; + BOOL_T followMe; + BOOL_T autoReverse; + DIST_T speed; + coOrd pos; + char * statusMsg; + long format; + + if ( dlg == NULL ) return; + + inx = wListGetIndex( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control ); + if ( dlg->train ) { + if ( inx >= 0 && inx < locoList_da.cnt && dlg->train && dlg->train != locoList(inx).loco ) { + inx = FindLoco( dlg->train ); + if ( inx >= 0 ) { + wListSetIndex( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control, inx ); + } + } + } else { + wListSetIndex( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control, -1 ); + } + + if ( dlg->train ) { + xx = GetTrkExtraData(dlg->train); + dir = xx->direction==0?0:1; + speed = xx->speed; + pos = xx->trvTrk.pos; + followMe = followTrain == dlg->train; + autoReverse = xx->autoReverse; + if ( xx->trvTrk.trk == NULL ) { + if ( xx->status == ST_Crashed ) + statusMsg = _("Crashed"); + else + statusMsg = _("Not on Track"); + } else if ( xx->speed > 0 ) { + if ( trainsState == TRAINS_STOP ) + statusMsg = _("Trains Paused"); + else + statusMsg = _("Running"); + } else { + switch (xx->status ) { + case ST_EndOfTrack: + statusMsg = _("End of Track"); + break; + case ST_OpenTurnout: + statusMsg = _("Open Turnout"); + break; + case ST_StopManual: + statusMsg = _("Manual Stop"); + break; + case ST_NoRoom: + statusMsg = _("No Room"); + break; + case ST_Crashed: + statusMsg = _("Crashed"); + break; + default: + statusMsg = _("Unknown Status"); + break; + } + } + ParamLoadMessage( dlg->trainPGp, I_STATUS, statusMsg ); + } else { + dir = 0; + followMe = FALSE; + autoReverse = FALSE; + ParamLoadMessage( dlg->trainPGp, I_STATUS, _("No trains") ); + } + if ( dlg->followMe != followMe ) { + dlg->followMe = followMe; + ParamLoadControl( dlg->trainPGp, I_FOLLOW ); + } + if ( dlg->autoReverse != autoReverse ) { + dlg->autoReverse = autoReverse; + ParamLoadControl( dlg->trainPGp, I_AUTORVRS ); + } + if ( dlg->direction != dir ) { + dlg->direction = dir; + wButtonSetLabel( (wButton_p)dlg->trainPGp->paramPtr[I_DIR].control, (dlg->direction?_("Reverse"):_("Forward")) ); + } + if ( dlg->train ) { + if ( dlg->posS[0] == '\0' || + dlg->pos.x != xx->trvTrk.pos.x || + dlg->pos.y != xx->trvTrk.pos.y ) { + dlg->pos = xx->trvTrk.pos; + format = GetDistanceFormat(); + format &= ~DISTFMT_DECS; + sprintf( dlg->posS, "X:%s Y:%s", + FormatDistanceEx( xx->trvTrk.pos.x, format ), + FormatDistanceEx( xx->trvTrk.pos.y, format ) ); + ParamLoadMessage( dlg->trainPGp, I_POS, dlg->posS ); + } + if ( dlg->speed != xx->speed ) { + dlg->speed = xx->speed; + sprintf( dlg->speedS, "%3d", (int)(units==UNITS_ENGLISH?xx->speed:xx->speed*1.6) ); + ParamLoadMessage( dlg->trainPGp, I_SPEED, dlg->speedS ); + SpeedRedraw( (wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control, dlg, SLIDER_WIDTH, SLIDER_HEIGHT ); + } + ParamLoadMessage( dlg->trainPGp, I_DIST, FormatDistance(xx->distance) ); + } else { + if ( dlg->posS[0] != '\0' ) { + dlg->posS[0] = '\0'; + ParamLoadMessage( dlg->trainPGp, I_POS, dlg->posS ); + } + if ( dlg->speed >= 0 ) { + dlg->speed = -1; + dlg->speedS[0] = '\0'; + ParamLoadMessage( dlg->trainPGp, I_SPEED, dlg->speedS ); + wDrawClear( (wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control ); + } + ParamLoadMessage( dlg->trainPGp, I_DIST, "" ); + } +} + + +static void ControllerDialogSyncAll( void ) +{ + if ( curTrainDlg ) + ControllerDialogSync( curTrainDlg ); +} + + +static void LocoListChangeEntry( + track_p oldLoco, + track_p newLoco ) +{ + wIndex_t inx = -1; + struct extraData * xx; + + if ( curTrainDlg == NULL ) + return; + if ( oldLoco && (inx=FindLoco(oldLoco))>=0 ) { + if ( newLoco ) { + xx = GetTrkExtraData(newLoco); + locoList(inx).loco = newLoco; + xx = GetTrkExtraData(newLoco); + locoList(inx).running = IsOnTrack(xx) && xx->speed > 0; + wListSetValues( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx, CarItemNumber(xx->item), locoList(inx).running?goI:stopI, newLoco ); + } else { + wListDelete( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx ); + for ( ; inx<locoList_da.cnt-1; inx++ ) + locoList(inx) = locoList(inx+1); + locoList_da.cnt -= 1; + if ( inx >= locoList_da.cnt ) + inx--; + } + } else if ( newLoco ){ + inx = locoList_da.cnt; + DYNARR_APPEND( locoList_t, locoList_da, 10 ); + locoList(inx).loco = newLoco; + xx = GetTrkExtraData(newLoco); + locoList(inx).running = IsOnTrack(xx) && xx->speed > 0; + wListAddValue( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, CarItemNumber(xx->item), locoList(inx).running?goI:stopI, newLoco ); + } + if ( curTrainDlg->train == oldLoco ) { + if ( newLoco || locoList_da.cnt <= 0 ) { + curTrainDlg->train = newLoco; + } else { + curTrainDlg->train = wListGetItemContext( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx ); + } + } + ControllerDialogSync( curTrainDlg ); +} + + +static void LocoListInit( void ) +{ + track_p train; + struct extraData * xx; + + locoList_da.cnt = 0; + for ( train=NULL; TrackIterate( &train ); ) { + if ( GetTrkType(train) != T_CAR ) continue; + xx = GetTrkExtraData(train); + if ( !CarItemIsLoco(xx->item) ) continue; + if ( !IsLocoMaster(xx) ) continue; + LocoListChangeEntry( NULL, train ); + } +} + + +#ifdef LATER +static void LoadTrainDlgIndex( + trainControlDlg_p dlg ) +{ + track_p car; + struct extraData * xx; + + wListClear( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control ); + for ( car=NULL; TrackIterate( &car ); ) { + if ( GetTrkType(car) != T_CAR ) continue; + xx = GetTrkExtraData(car); + if ( !CarItemIsLoco(xx->item) ) continue; + if ( !IsLocoMaster(xx) ) continue; + wListAddValue( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control, CarItemNumber(xx->item), xx->speed>0?goI:stopI, car ); + } + TrainDialogSetIndex( dlg ); + ControllerDialogSync( curTrainDlg ); +} +#endif + + +static void SetCurTrain( + track_p train ) +{ + curTrainDlg->train = train; + ControllerDialogSync( curTrainDlg ); +} + + +static void StopTrain( + track_p train, + trainStatus_e status ) +{ + struct extraData * xx; + + if ( train == NULL ) + return; + xx = GetTrkExtraData(train); + xx->speed = 0; + xx->status = status; + LocoListChangeEntry( train, train ); +} + + +static void MoveMainWindow( + coOrd pos, + ANGLE_T angle ) +{ + DIST_T dist; + static DIST_T factor = 0.5; + ANGLE_T angle1 = angle, angle2; + if ( angle1 > 180.0) + angle1 = 360.0 - angle1; + if ( angle1 > 90.0) + angle1 = 180.0 - angle1; + angle2 = R2D(atan2(mainD.size.x,mainD.size.y)); + if ( angle1 < angle2 ) + dist = mainD.size.y/2.0/cos(D2R(angle1)); + else + dist = mainD.size.x/2.0/cos(D2R(90.0-angle1)); + dist *= factor; + Translate( &pos, pos, angle, dist ); + DrawMapBoundingBox( FALSE ); + mainCenter = pos; + mainD.orig.x = pos.x-mainD.size.x/2;; + mainD.orig.y = pos.y-mainD.size.y/2;; + MainRedraw(); + DrawMapBoundingBox( TRUE ); +} + + +static void SetTrainDirection( + track_p train ) +{ + struct extraData *xx, *xx0=GetTrkExtraData(train); + int dir, dir0; + track_p car; + + car = train; + for ( dir0 = 0; dir0 < 2; dir0++ ) { + dir = dir0; + WALK_CARS_START( car, xx, dir ) + if ( car != train ) { + if ( CarItemIsLoco(xx->item) ) { + xx->direction = (dir==dir0?xx0->direction:!xx0->direction); + } + } + WALK_CARS_END( car, xx, dir ) + } +} + + +static void ControllerDialogUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + trainControlDlg_p dlg = curTrainDlg; + track_p train; + struct extraData * xx; + + if ( dlg == NULL ) + return; + + TrainTimeEndPause(); + switch (inx) { + case I_LIST: + train = (track_p)wListGetItemContext( (wList_p)pg->paramPtr[inx].control, (wIndex_t)*(long*)valueP ); + if ( train == NULL ) return; + dlg->train = train; + ControllerDialogSync( dlg ); + break; + case I_ZERO: + if ( dlg->train == NULL ) return; + TrainTimeEndPause(); + xx = GetTrkExtraData( dlg->train ); + xx->distance = 0.0; + ParamLoadMessage( dlg->trainPGp, I_DIST, FormatDistance(xx->distance) ); + ParamLoadControl( curTrainDlg->trainPGp, I_DIST ); + TrainTimeStartPause(); + break; + case I_GOTO: + if ( dlg->train == NULL ) return; + TrainTimeEndPause(); + xx = GetTrkExtraData( dlg->train ); + followTrain = NULL; + dlg->followMe = FALSE; + ParamLoadControl( curTrainDlg->trainPGp, I_FOLLOW ); + CarSetVisible( dlg->train ); + MoveMainWindow( xx->trvTrk.pos, xx->trvTrk.angle ); + TrainTimeStartPause(); + break; + case I_FOLLOW: + if ( dlg->train == NULL ) return; + if ( *(long*)valueP ) { + followTrain = dlg->train; + xx = GetTrkExtraData(dlg->train); + if ( OFF_MAIND( xx->trvTrk.pos, xx->trvTrk.pos ) ) { + MoveMainWindow( xx->trvTrk.pos, xx->trvTrk.angle ); + } + followCenter = mainCenter; + } else { + followTrain = NULL; + } + break; + case I_AUTORVRS: + if ( dlg->train == NULL ) return; + xx = GetTrkExtraData(dlg->train); + xx->autoReverse = *(long*)valueP!=0; + break; + case I_DIR: + if ( dlg->train == NULL ) return; + xx = GetTrkExtraData(dlg->train); + dlg->direction = xx->direction = !xx->direction; + wButtonSetLabel( (wButton_p)pg->paramPtr[I_DIR].control, (dlg->direction?_("Reverse"):_("Forward")) ); + SetTrainDirection( dlg->train ); + DrawAllCars(); + break; + case I_STOP: + if ( dlg->train == NULL ) return; + TrainTimeEndPause(); + StopTrain( dlg->train, ST_StopManual ); + TrainTimeStartPause(); + break; + case -1: + /* Close window */ + CmdTrainExit( NULL ); + break; + } + /*ControllerDialogSync( dlg );*/ + TrainTimeStartPause(); +} + + +static trainControlDlg_p CreateTrainControlDlg( void ) +{ + trainControlDlg_p dlg; + char * title; + paramData_p PLp; + dlg = (trainControlDlg_p)MyMalloc( sizeof *dlg ); +#ifdef LATER + PLp = (paramData_p)MyMalloc( sizeof trainPLs ); + memcpy( PLp, trainPLs, sizeof trainPLs ); +#endif + PLp = trainPLs; + dlg->posS[0] = '\0'; + dlg->speedS[0] = '\0'; + PLp[I_LIST].valueP = &dlg->inx; + PLp[I_LIST].context = dlg; + PLp[I_POS].valueP = &dlg->posS; + PLp[I_POS].context = dlg; + /*PLp[I_GOTO].valueP = NULL;*/ + PLp[I_GOTO].context = dlg; + PLp[I_SLIDER].context = dlg; + PLp[I_SPEED].valueP = &dlg->speedS; + PLp[I_SPEED].context = dlg; + PLp[I_DIR].context = dlg; + /*PLp[I_STOP].valueP = NULL;*/ + PLp[I_STOP].context = dlg; + PLp[I_FOLLOW].valueP = &dlg->followMe; + PLp[I_FOLLOW].context = dlg; + PLp[I_AUTORVRS].valueP = &dlg->autoReverse; + PLp[I_AUTORVRS].context = dlg; + title = MyStrdup( _("Train Control XXX") ); + sprintf( title, _("Train Control %d"), ++numTrainDlg ); + dlg->trainPGp = &trainPG; + dlg->win = ParamCreateDialog( dlg->trainPGp, _("Train Control"), NULL, NULL, NULL, FALSE, NULL, 0, ControllerDialogUpdate ); + return dlg; +} + + + +/* + * STATE INFO + */ + +static struct { + STATE_T state; + coOrd pos0; + } Dtrain; + + +EXPORT long trainPause = 200; +static track_p followTrain = NULL; +/*static int suppressTrainRedraw = 0;*/ +static long setTimeD; + + + +#ifdef MEMCHECK +static BOOL_T drawAllCarsDisable; +static void * top1, * top2; +static long drawCounter; +#endif +static void DrawAllCars( void ) +{ + track_p car; + struct extraData * xx; + coOrd size, lo, hi; + BOOL_T drawCarEnable1 = drawCarEnable; +#ifdef MEMCHECK +drawCounter++; +top1 = Sbrk( 0 ); +if ( top1 != top2 ) { + fprintf( stderr, "incr by %ld at %ld\n", (char*)top1-(char*)top2, drawCounter ); + top2 = top1; +} +#endif + drawCarEnable = TRUE; + wDrawDelayUpdate( mainD.d, TRUE ); + wDrawRestoreImage( mainD.d ); + DrawMarkers(); + DrawPositionIndicators(); + for ( car=NULL; TrackIterate(&car); ) { + if ( GetTrkType(car) == T_CAR ) { + xx = GetTrkExtraData(car); + CarItemSize( xx->item, &size ); /* TODO assumes xx->trvTrk.pos is the car center */ + lo.x = xx->trvTrk.pos.x - size.x/2.0; + lo.y = xx->trvTrk.pos.y - size.x/2.0; + hi.x = lo.x + size.x; + hi.y = lo.y + size.x; + if ( !OFF_MAIND( lo, hi ) ) + DrawCar( car, &mainD, wDrawColorBlack ); + } + } + wDrawDelayUpdate( mainD.d, FALSE ); + drawCarEnable = drawCarEnable1; +} + + +static DIST_T GetTrainLength2( + track_p * car0, + BOOL_T * dir ) +{ + DIST_T length = 0, carLength; + struct extraData * xx; + + WALK_CARS_START ( *car0, xx, *dir ) + carLength = CarItemCoupledLength( xx->item ); + if ( length == 0 ) + length = carLength/2.0; /* TODO assumes xx->trvTrk.pos is the car center */ + else + length += carLength; + WALK_CARS_END ( *car0, xx, *dir ) + return length; +} + + +static DIST_T GetTrainLength( + track_p car0, + BOOL_T dir ) +{ + return GetTrainLength2( &car0, &dir ); +} + + +static void PlaceCar( + track_p car ) +{ + struct extraData *xx = GetTrkExtraData(car); + DIST_T dists[2]; + int dir; + + CarItemPlace( xx->item, &xx->trvTrk, dists ); + + for ( dir=0; dir<2; dir++ ) + xx->couplerPos[dir] = CarItemFindCouplerMountPoint( xx->item, xx->trvTrk, dir ); + + car->endPt[0].angle = xx->trvTrk.angle; + Translate( &car->endPt[0].pos, xx->trvTrk.pos, car->endPt[0].angle, dists[0] ); + car->endPt[1].angle = NormalizeAngle( xx->trvTrk.angle + 180.0 ); + Translate( &car->endPt[1].pos, xx->trvTrk.pos, car->endPt[1].angle, dists[1] ); +LOG( log_trainMove, 4, ( "%s @ [%0.3f,%0.3f] A%0.3f\n", CarItemNumber(xx->item), xx->trvTrk.pos.x, xx->trvTrk.pos.y, xx->trvTrk.angle ) ) + SetCarBoundingBox( car ); + xx->state &= ~(CAR_STATE_ONHIDENTRACK); + xx->trkLayer = NOTALAYER; + if ( xx->trvTrk.trk ) { + if ( !GetTrkVisible(xx->trvTrk.trk) ) + xx->state |= CAR_STATE_ONHIDENTRACK; + xx->trkLayer = GetTrkLayer(xx->trvTrk.trk); + } +} + + +static track_p FindCar( + coOrd * pos ) +{ + coOrd pos0, pos1; + track_p trk, trk1; + DIST_T dist1 = 100000, dist; + struct extraData * xx; + + trk1 = NULL; + for ( trk=NULL; TrackIterate(&trk); ) { + if ( GetTrkType(trk) == T_CAR ) { + xx = GetTrkExtraData(trk); + if ( IsIgnored(xx) ) + continue; + pos0 = *pos; + dist = DistanceCar( trk, &pos0 ); + if ( dist < dist1 ) { + dist1 = dist; + trk1 = trk; + pos1 = pos0; + } + } + } + if ( dist1 < 10 ) { + *pos = pos1; + return trk1; + } else { + return NULL; + } +} + + +static track_p FindMasterLoco( + track_p train, + int * dirR ) +{ + track_p car0; + struct extraData *xx0; + int dir, dir0; + + for ( dir = 0; dir<2; dir++ ) { + car0 = train; + dir0 = dir; + WALK_CARS_START( car0, xx0, dir0 ) + if ( CarItemIsLoco(xx0->item) && IsLocoMaster(xx0) ) { + if ( dirR ) *dirR = 1-dir0; + return car0; + } + WALK_CARS_END( car0, xx0, dir0 ) + } + return NULL; +} + + +static track_p PickMasterLoco( + track_p car, + int dir ) +{ + track_p loco=NULL; + struct extraData *xx; + + WALK_CARS_START( car, xx, dir ) + if ( CarItemIsLoco(xx->item) ) { + if ( IsLocoMaster(xx) ) + return car; + if ( loco == NULL ) loco = car; + } + WALK_CARS_END( car, xx, dir ) + if ( loco == NULL ) + return NULL; + xx = GetTrkExtraData(loco); + SetLocoMaster(xx); + xx->speed = 0; + LOG( log_trainMove, 1, ( "%s becomes master\n", CarItemNumber(xx->item) ) ) + return loco; +} + + +static void UncoupleCars( + track_p car1, + track_p car2 ) +{ + struct extraData * xx1, * xx2; + track_p loco, loco1, loco2; + int dir1, dir2; + + xx1 = GetTrkExtraData(car1); + xx2 = GetTrkExtraData(car2); + if ( GetTrkEndTrk(car1,0) == car2 ) { + dir1 = 0; + } else if ( GetTrkEndTrk(car1,1) == car2 ) { + dir1 = 1; + } else { + ErrorMessage( "uncoupleCars - not coupled" ); + return; + } + if ( GetTrkEndTrk(car2,0) == car1 ) { + dir2 = 0; + } else if ( GetTrkEndTrk(car2,1) == car1 ) { + dir2 = 1; + } else { + ErrorMessage( "uncoupleCars - not coupled" ); + return; + } + loco = FindMasterLoco( car1, NULL ); + car1->endPt[dir1].track = NULL; + car2->endPt[dir2].track = NULL; + /*DisconnectTracks( car1, dir1, car2, dir2 );*/ + if ( loco ) { + loco1 = PickMasterLoco( car1, 1-dir1 ); + if ( loco1 != loco ) + LocoListChangeEntry( NULL, loco1 ); + loco2 = PickMasterLoco( car2, 1-dir2 ); + if ( loco2 != loco ) + LocoListChangeEntry( NULL, loco2 ); + } +} + +static void CoupleCars( + track_p car1, + int dir1, + track_p car2, + int dir2 ) +{ + struct extraData * xx1, * xx2; + track_p loco1, loco2; + track_p car; + int dir; + + xx1 = GetTrkExtraData(car1); + xx2 = GetTrkExtraData(car2); + if ( GetTrkEndTrk(car1,dir1) != NULL || GetTrkEndTrk(car2,dir2) != NULL ) { + LOG( log_trainMove, 1, ( "coupleCars - already coupled\n" ) ) + return; + } + car = car1; + dir = 1-dir1; + WALK_CARS_START( car, xx1, dir ) + if ( car == car2 ) { + LOG( log_trainMove, 1, ( "coupleCars - already coupled\n" ) ) + ErrorMessage( "Car coupling loop" ); + return; + } + WALK_CARS_END( car, xx1, dir ) + car = car2; + dir = 1-dir2; + WALK_CARS_START( car, xx2, dir ) + if ( car == car1 ) { + LOG( log_trainMove, 1, ( "coupleCars - already coupled\n" ) ) + ErrorMessage( "Car coupling loop" ); + return; + } + WALK_CARS_END( car, xx1, dir ) + loco1 = FindMasterLoco( car1, NULL ); + loco2 = FindMasterLoco( car2, NULL ); + car1->endPt[dir1].track = car2; + car2->endPt[dir2].track = car1; + /*ConnectTracks( car1, dir1, car2, dir2 );*/ +if ( logTable(log_trainMove).level >= 2 ) { +LogPrintf( "Coupling %s[%d] ", CarItemNumber(xx1->item), dir1 ); +LogPrintf( " and %s[%d]\n", CarItemNumber(xx2->item), dir2 ); +} + if ( ( loco1 != NULL && loco2 != NULL ) ) { + xx1 = GetTrkExtraData( loco1 ); + xx2 = GetTrkExtraData( loco2 ); + if ( xx1->speed == 0 ) { + ClrLocoMaster(xx1); + LOG( log_trainMove, 2, ( "%s loses master\n", CarItemNumber(xx1->item) ) ) + if ( followTrain == loco1 ) + followTrain = loco2; + LocoListChangeEntry( loco1, NULL ); + loco1 = loco2; + } else { + ClrLocoMaster(xx2); + xx1->speed = (xx1->speed + xx2->speed)/2.0; + if ( xx1->speed < 0 ) + xx1->speed = 0; + if ( xx1->speed > 100 ) + xx1->speed = 100; + LOG( log_trainMove, 2, ( "%s loses master\n", CarItemNumber(xx2->item) ) ) + if ( followTrain == loco2 ) + followTrain = loco1; + LocoListChangeEntry( loco2, NULL ); + } + SetTrainDirection( loco1 ); + } +} + + +long crashSpeedDecay=5; +long crashDistFactor=60; +static void PlaceCars( + track_p car0, + int dir0, + long crashSpeed, + BOOL_T crashFlip ) +{ + struct extraData *xx0 = GetTrkExtraData(car0), *xx; + int dir; + traverseTrack_t trvTrk; + DIST_T length, dist, length1; + track_p car_curr; + DIST_T flipflop = 1; + + if ( crashFlip ) + flipflop = -1; + dir = dir0; + trvTrk = xx0->trvTrk; + if ( dir0 ) + FlipTraverseTrack( &trvTrk ); + length = CarItemCoupledLength(xx0->item)/2.0; + car_curr = car0; + ClrIgnored( xx0 ); + WALK_CARS_START ( car_curr, xx, dir ) + if ( car_curr != car0 ) { + ClrIgnored( xx ); + length1 = CarItemCoupledLength(xx->item)/2.0; + dist = length + length1; + crashSpeed = crashSpeed*crashSpeedDecay/10; + if ( crashSpeed > 0 ) + dist -= dist * crashSpeed/crashDistFactor; + TraverseTrack2( &trvTrk, dist ); + xx->trvTrk = trvTrk; + if ( crashSpeed > 0 ) { + xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle + flipflop*crashSpeed ); + xx->trvTrk.trk = NULL; + } + flipflop = -flipflop; + if ( dir != 0 ) + FlipTraverseTrack( &xx->trvTrk ); + PlaceCar( car_curr ); + length = length1; + } + WALK_CARS_END ( car_curr, xx, dir ) +} + + +static void CrashTrain( + track_p car, + int dir, + traverseTrack_p trvTrkP, + long speed, + BOOL_T flip ) +{ + track_p loco; + struct extraData *xx; + + loco = FindMasterLoco(car,NULL); + if ( loco != NULL ) { + StopTrain( loco, ST_Crashed ); + } + xx = GetTrkExtraData(car); + xx->trvTrk = *trvTrkP; + if ( dir ) + FlipTraverseTrack( &xx->trvTrk ); + PlaceCars( car, 1-dir, speed, flip ); + if ( flip ) + speed = - speed; + xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle - speed ); + xx->trvTrk.trk = NULL; + PlaceCar( car ); +} + + +static FLOAT_T couplerConnAngle = 45.0; +static BOOL_T CheckCoupling( + track_p car0, + int dir00, + BOOL_T doCheckCrash ) +{ + track_p car1, loco1; + struct extraData *xx0, *xx1; + coOrd pos1; + DIST_T dist0, distc, dist=100000.0; + int dir0, dir1, dirl; + ANGLE_T angle; + traverseTrack_t trvTrk0, trvTrk1; + long speed, speed0, speed1; + + xx0 = xx1 = GetTrkExtraData(car0); + /* find length of train from loco to start and end */ + dir0 = dir00; + dist0 = GetTrainLength2( &car0, &dir0 ); + + trvTrk0 = xx0->trvTrk; + if ( dir00 ) + FlipTraverseTrack( &trvTrk0 ); + TraverseTrack2( &trvTrk0, dist0 ); + pos1 = trvTrk0.pos; + car1 = FindCar( &pos1 ); + if ( !car1 ) + return TRUE; + xx1 = GetTrkExtraData(car1); + if ( !IsOnTrack(xx1) ) + return TRUE; + /* determine which EP of the found car to couple to */ + angle = NormalizeAngle( trvTrk0.angle-xx1->trvTrk.angle ); + if ( angle > 90 && angle < 270 ) { + dir1 = 0; + angle = NormalizeAngle( angle+180 ); + } else { + dir1 = 1; + } + /* already coupled? */ + if ( GetTrkEndTrk(car1,dir1) != NULL ) + return TRUE; + /* are we close to aligned? */ + if ( angle > couplerConnAngle && angle < 360.0-couplerConnAngle ) + return TRUE; + /* find pos of found car's coupler, and dist btw couplers */ + distc = CarItemCoupledLength(xx1->item); + Translate( &pos1, xx1->trvTrk.pos, xx1->trvTrk.angle+(dir1?180.0:0.0), distc/2.0 ); + dist = FindDistance( trvTrk0.pos, pos1 ); + if ( dist < trackGauge/10 ) + return TRUE; + /* not real close: are we overlapped? */ + angle = FindAngle( trvTrk0.pos, pos1 ); + angle = NormalizeAngle( angle - trvTrk0.angle ); + if ( angle < 90 || angle > 270 ) + return TRUE; + /* are we beyond the end of the found car? */ + if ( dist > distc ) + return TRUE; + /* are we on the same track? */ + trvTrk1 = xx1->trvTrk; + if ( dir1 ) + FlipTraverseTrack( &trvTrk1 ); + TraverseTrack2( &trvTrk1, distc/2.0-dist ); + if ( trvTrk1.trk != trvTrk0.trk ) + return TRUE; + if ( doCheckCrash ) { + speed0 = (long)xx0->speed; + if ( (xx0->direction==0) != (dir00==0) ) + speed0 = - speed0; + loco1 = FindMasterLoco( car1, &dirl ); + xx1 = NULL; + if ( loco1 ) { + xx1 = GetTrkExtraData(loco1); + speed1 = (long)xx1->speed; + if ( car1 == loco1 ) { + dirl = IsAligned( xx1->trvTrk.angle, FindAngle( trvTrk0.pos, xx1->trvTrk.pos ) )?1:0; + } + if ( (xx1->direction==1) != (dirl==1) ) + speed1 = -speed1; + } else { + speed1 = 0; + } + speed = (long)labs( speed0 + speed1 ); + LOG( log_trainMove, 2, ( "coupling speed=%ld\n", speed ) ) + if ( speed > maxCouplingSpeed ) { + CrashTrain( car0, dir0, &trvTrk0, speed, FALSE ); + CrashTrain( car1, dir1, &trvTrk1, speed, TRUE ); + return FALSE; + } + } + if ( dir00 ) + dist = -dist; + TraverseTrack2( &xx0->trvTrk, dist ); + CoupleCars( car0, dir0, car1, dir1 ); +LOG( log_trainMove, 3, ( " -> %0.3f\n", dist ) ) + return TRUE; +} + + +static void PlaceTrain( + track_p car0, + BOOL_T doCheckCrash, + BOOL_T doCheckCoupling ) +{ + track_p car_curr; + struct extraData *xx0, *xx; + int dir0, dir; + + xx0 = GetTrkExtraData(car0); + + LOG( log_trainMove, 2, ( " placeTrain: %s [%0.3f %0.3f] A%0.3f", CarItemNumber(xx0->item), xx0->trvTrk.pos.x, xx0->trvTrk.pos.y, xx0->trvTrk.angle ) ) + + car_curr = car0; + for ( dir0=0; dir0<2; dir0++ ) { + car_curr = car0; + dir = dir0; + xx = xx0; + WALK_CARS_START( car_curr, xx, dir ) + SetIgnored(xx); + WALK_CARS_END( car_curr, xx, dir ); + } + + /* check for coupling to other cars */ + if ( doCheckCoupling ) { + if ( xx0->trvTrk.trk ) + if ( !CheckCoupling( car0, 0, doCheckCrash ) ) + return; + if ( xx0->trvTrk.trk ) + if ( !CheckCoupling( car0, 1, doCheckCrash ) ) + return; + } + + PlaceCar( car0 ); + + for ( dir0=0; dir0<2; dir0++ ) + PlaceCars( car0, dir0, 0, FALSE ); +} + + +static void PlaceTrainInit( + track_p car0, + track_p trk0, + coOrd pos0, + ANGLE_T angle0, + BOOL_T doCheckCoupling ) +{ + struct extraData * xx = GetTrkExtraData(car0); + xx->trvTrk.trk = trk0; + xx->trvTrk.dist = xx->trvTrk.length = -1; + xx->trvTrk.pos = pos0; + xx->trvTrk.angle = angle0; + PlaceTrain( car0, FALSE, doCheckCoupling ); +} + + +static void FlipTrain( + track_p train ) +{ + DIST_T d0, d1; + struct extraData * xx; + + if ( train == NULL ) + return; + d0 = GetTrainLength( train, 0 ); + d1 = GetTrainLength( train, 1 ); + xx = GetTrkExtraData(train); + TraverseTrack2( &xx->trvTrk, d0-d1 ); + FlipTraverseTrack( &xx->trvTrk ); + xx->trvTrk.length = -1; + PlaceTrain( train, FALSE, TRUE ); +} + + +static BOOL_T MoveTrain( + track_p train, + long timeD ) +{ + DIST_T ips, dist0, dist1; + struct extraData *xx, *xx1; + traverseTrack_t trvTrk; + DIST_T length; + track_p car1; + int dir1; + int measured; /* make sure the distance is only measured once per train */ + + if ( train == NULL ) + return FALSE; + xx = GetTrkExtraData(train); + if ( xx->speed <= 0 ) + return FALSE; + + if ( setTimeD ) + timeD = setTimeD; + ips = ((xx->speed*5280.0*12.0)/(60.0*60.0*GetScaleRatio(curScaleInx))); + dist0 = ips * timeD/1000.0; + length = GetTrainLength( train, xx->direction ); + dist1 = length + dist0; + trvTrk = xx->trvTrk; + if ( trvTrk.trk == NULL ) { + return FALSE; + } + LOG( log_trainMove, 1, ( "moveTrain: %s t%ld->%0.3f S%0.3f D%d [%0.3f %0.3f] A%0.3f T%d\n", + CarItemNumber(xx->item), timeD, dist0, xx->speed, xx->direction, xx->trvTrk.pos.x, xx->trvTrk.pos.y, xx->trvTrk.angle, xx->trvTrk.trk?GetTrkIndex(xx->trvTrk.trk):-1 ) ) + if ( xx->direction ) + FlipTraverseTrack( &trvTrk ); + TraverseTrack( &trvTrk, &dist1 ); + if ( dist1 > 0.0 ) { + if ( dist1 > dist0 ) { + /*ErrorMessage( "%s no room: L%0.3f D%0.3f", CarItemNumber(xx->item), length, dist1 );*/ + StopTrain( train, ST_NoRoom ); + return FALSE; + } else { + dist0 -= dist1; + LOG( log_trainMove, 1, ( " %s STOP D%d [%0.3f %0.3f] A%0.3f D%0.3f\n", + CarItemNumber(xx->item), xx->direction, xx->trvTrk.pos.x, xx->trvTrk.pos.y, xx->trvTrk.angle, dist0 ) ) + } + /*ErrorMessage( "%s stopped at End Of Track", CarItemNumber(xx->item) );*/ + if ( xx->autoReverse ) { + xx->direction = !xx->direction; + SetTrainDirection( train ); + } else { + if ( xx->speed > maxCouplingSpeed ) { + car1 = train; + dir1 = xx->direction; + GetTrainLength2( &car1, &dir1 ); + CrashTrain( car1, dir1, &trvTrk, (long)xx->speed, FALSE ); + return TRUE; + } else { + StopTrain( train, trvTrk.trk?ST_OpenTurnout:ST_EndOfTrack ); + } + } + } + trvTrk = xx->trvTrk; + TraverseTrack2( &xx->trvTrk, xx->direction==0?dist0:-dist0 ); + car1 = train; + dir1 = 0; + GetTrainLength2( &car1, &dir1 ); + dir1 = 1-dir1; + + measured = FALSE; + WALK_CARS_START( car1, xx1, dir1 ); + if ( CarItemIsLoco(xx1->item) && !measured ) { + xx->distance += dist0; + measured = TRUE; + } + WALK_CARS_END( car1, xx1, dir1 ); + + if ( train == followTrain ) { + if ( followCenter.x != mainCenter.x || + followCenter.y != mainCenter.y ) { + if ( curTrainDlg->train == followTrain ) { + curTrainDlg->followMe = FALSE; + ParamLoadControl( curTrainDlg->trainPGp, I_FOLLOW ); + } + followTrain = NULL; + } else if ( OFF_MAIND( xx->trvTrk.pos, xx->trvTrk.pos ) ) { + MoveMainWindow( xx->trvTrk.pos, NormalizeAngle(xx->trvTrk.angle+(xx->direction?180.0:0.0)) ); + followCenter = mainCenter; + } + } + PlaceTrain( train, TRUE, TRUE ); + return TRUE; +} + + +static BOOL_T MoveTrains( long timeD ) +{ + BOOL_T trains_moved = FALSE; + track_p train; + struct extraData * xx; + + for ( train=NULL; TrackIterate( &train ); ) { + if ( GetTrkType(train) != T_CAR ) continue; + xx = GetTrkExtraData(train); + if ( !CarItemIsLoco(xx->item) ) continue; + if ( !IsLocoMaster(xx) ) continue; + if ( xx->speed == 0 ) continue; + trains_moved |= MoveTrain( train, timeD ); + } + + ControllerDialogSyncAll(); + + DrawAllCars(); + + return trains_moved; +} + + +static void MoveTrainsLoop( void ) +{ + long time1, timeD; + static long time0 = 0; + + trainsTimeoutPending = FALSE; + if ( trainsState != TRAINS_RUN ) { + time0 = 0; + return; + } + if ( time0 == 0 ) + time0 = wGetTimer(); + time1 = wGetTimer(); + timeD = time1-time0; + time0 = time1; + if ( timeD > 1000 ) + timeD = 1000; + if ( MoveTrains( timeD ) ) { + wAlarm( trainPause, MoveTrainsLoop ); + trainsTimeoutPending = TRUE; + } else { + time0 = 0; + trainsState = TRAINS_IDLE; + TrainTimeEndPause(); + } +} + + +static void RestartTrains( void ) +{ + if ( trainsState != TRAINS_RUN ) + TrainTimeStartPause(); + trainsState = TRAINS_RUN; + if ( !trainsTimeoutPending ) + MoveTrainsLoop(); +} + + +static long trainTime0 = 0; +static long playbackTrainPause = 0; +static drawCmd_t trainMovieD = { + NULL, + &screenDrawFuncs, + 0, + 16.0, + 0, + {0,0}, {1,1}, + Pix2CoOrd, CoOrd2Pix }; +static long trainMovieFrameDelay; +static long trainMovieFrameNext; + +static void TrainTimeEndPause( void ) +{ + if ( recordF ) { + if (trainTime0 != 0 ) { + long delay; + delay = wGetTimer()-trainTime0; + if ( delay > 0 ) + fprintf( recordF, "TRAINPAUSE %ld\n", delay ); + } + trainTime0 = 0; + } +} + +static void TrainTimeStartPause( void ) +{ + if ( trainTime0 == 0 ) + trainTime0 = wGetTimer(); +} + + +static BOOL_T TrainTimeDoPause( char * line ) +{ + BOOL_T drawCarEnable2; + playbackTrainPause = atol( line ); +LOG( log_trainPlayback, 1, ( "DoPause %ld\n", playbackTrainPause ) ); + trainsState = TRAINS_RUN; + if ( trainMovieFrameDelay > 0 ) { + drawCarEnable2 = drawCarEnable; drawCarEnable = TRUE; + TakeSnapshot( &trainMovieD ); + drawCarEnable = drawCarEnable2; +LOG( log_trainPlayback, 1, ( "SNAP 0\n" ) ); + trainMovieFrameNext = trainMovieFrameDelay; + } + /*MoveTrains();*/ + while ( playbackTrainPause > 0 ) { + if ( playbackTrainPause > trainPause ) { + wPause( trainPause ); + MoveTrains( trainPause ); + playbackTrainPause -= trainPause; + if ( trainMovieFrameDelay > 0 ) + trainMovieFrameNext -= trainPause; + } else { + wPause( playbackTrainPause ); + MoveTrains( playbackTrainPause ); + if ( trainMovieFrameDelay > 0 ) + trainMovieFrameNext -= playbackTrainPause; + playbackTrainPause = 0; + } + if ( trainMovieFrameDelay > 0 && + trainMovieFrameNext <= 0 ) { + drawCarEnable2 = drawCarEnable; drawCarEnable = TRUE; + TakeSnapshot( &trainMovieD ); + drawCarEnable = drawCarEnable2; +LOG( log_trainPlayback, 1, ( "SNAP %ld\n", trainMovieFrameNext ) ); + trainMovieFrameNext = trainMovieFrameDelay; + } + } + return TRUE; +} + + +static BOOL_T TrainDoMovie( char * line ) +{ + /* on/off, scale, orig, size */ + long fps; + if ( trainMovieD.dpi == 0 ) + trainMovieD.dpi = mainD.dpi; + if ( !GetArgs( line, "lfpp", &fps, &trainMovieD.scale, &trainMovieD.orig, &trainMovieD.size ) ) + return FALSE; + if ( fps > 0 ) { + trainMovieFrameDelay = 1000/fps; + } else { + trainMovieFrameDelay = 0; + } + trainMovieFrameNext = 0; + return TRUE; +} + +EXPORT void AttachTrains( void ) +{ + track_p car; + track_p loco; + struct extraData * xx; + coOrd pos; + track_p trk; + ANGLE_T angle; + EPINX_T ep0, ep1; + int dir; + + for ( car=NULL; TrackIterate( &car ); ) { + ClrTrkBits( car, TB_CARATTACHED ); + if ( GetTrkType(car) != T_CAR ) + continue; + xx = GetTrkExtraData(car); + ClrProcessed(xx); + } + for ( car=NULL; TrackIterate( &car ); ) { + if ( GetTrkType(car) != T_CAR ) + continue; + xx = GetTrkExtraData(car); + if ( IsProcessed(xx) ) + continue; + loco = FindMasterLoco( car, NULL ); + if ( loco != NULL ) + xx = GetTrkExtraData(loco); + else + loco = car; + pos = xx->trvTrk.pos; + if ( xx->status == ST_Crashed ) + continue; + TRK_ITERATE(trk) { + if ( trk == xx->trvTrk.trk ) + break; + } + if ( trk!=NULL && !QueryTrack( trk, Q_ISTRACK ) ) + trk = NULL; + if ( trk==NULL || GetTrkDistance(trk,pos)>trackGauge*2.0 ) + trk = OnTrack2( &pos, FALSE, TRUE, FALSE ); + if ( trk!=NULL ) { + /*if ( trk == xx->trvTrk.trk ) + continue;*/ + angle = GetAngleAtPoint( trk, pos, &ep0, &ep1 ); + if ( NormalizeAngle( xx->trvTrk.angle-angle+90 ) > 180 ) + angle = NormalizeAngle(angle+180); + PlaceTrainInit( loco, trk, pos, angle, TRUE ); + } else { + PlaceTrainInit( loco, NULL, xx->trvTrk.pos, xx->trvTrk.angle, FALSE ); + } + dir = 0; + WALK_CARS_START( loco, xx, dir ) + WALK_CARS_END( loco, xx, dir ) + dir = 1-dir; + WALK_CARS_START( loco, xx, dir ) + SetProcessed(xx); + if ( xx->trvTrk.trk ) { + SetTrkBits( xx->trvTrk.trk, TB_CARATTACHED ); + xx->status = ST_StopManual; + } else { + xx->status = ST_NotOnTrack; + } + WALK_CARS_END( loco, xx, dir ) + } + for ( car=NULL; TrackIterate( &car ); ) { + if ( GetTrkType(car) != T_CAR ) + continue; + xx = GetTrkExtraData(car); + ClrProcessed(xx); + } +} + + +static void UpdateTrainAttachment( void ) +{ + track_p trk; + struct extraData * xx; + for ( trk=NULL; TrackIterate( &trk ); ) { + ClrTrkBits( trk, TB_CARATTACHED ); + } + for ( trk=NULL; TrackIterate( &trk ); ) { + if ( GetTrkType(trk) == T_CAR ) { + xx = GetTrkExtraData(trk); + if ( xx->trvTrk.trk != NULL ) + SetTrkBits( xx->trvTrk.trk, TB_CARATTACHED ); + } + } +} + + +static BOOL_T TrainOnMovableTrack( + track_p trk, + track_p *trainR ) +{ + track_p train; + struct extraData * xx; + int dir; + + for ( train=NULL; TrackIterate(&train); ) { + if ( GetTrkType(train) != T_CAR ) + continue; + xx = GetTrkExtraData(train); + if ( IsOnTrack(xx) ) { + if ( xx->trvTrk.trk == trk ) + break; + } + } + *trainR = train; + if ( train == NULL ) { + return TRUE; + } + dir = 0; + WALK_CARS_START( train, xx, dir ) + WALK_CARS_END( train, xx, dir ) + dir = 1-dir; + WALK_CARS_START( train, xx, dir ) + if ( xx->trvTrk.trk != trk ) { + ErrorMessage( MSG_CANT_MOVE_UNDER_TRAIN ); + return FALSE; + } + WALK_CARS_END( train, xx, dir ) + train = FindMasterLoco( train, NULL ); + if ( train != NULL ) + *trainR = train; + return TRUE; +} + +/* + * + */ + +#define DO_UNCOUPLE (0) +#define DO_FLIPCAR (1) +#define DO_FLIPTRAIN (2) +#define DO_DELCAR (3) +#define DO_DELTRAIN (4) +#define DO_MUMASTER (5) +#define DO_CHANGEDIR (6) +#define DO_STOP (7) +static track_p trainFuncCar; +static coOrd trainFuncPos; +static wButton_p trainPauseB; + +#ifdef LATER +static char * newCarLabels[3] = { N_("Road"), N_("Number"), NULL }; +#endif + +static STATUS_T CmdTrain( wAction_t action, coOrd pos ) +{ + track_p trk0, trk1; + static track_p currCar; + coOrd pos0, pos1; + static coOrd delta; + ANGLE_T angle1; + EPINX_T ep0, ep1; + int dir; + struct extraData * xx=NULL; + DIST_T dist; + wPos_t w, h; + + switch (action) { + + case C_START: + /*UndoStart( "Trains", "Trains" );*/ + UndoSuspend(); + programMode = MODE_TRAIN; + drawCarEnable = FALSE; + doDrawTurnoutPosition = 1; + DoChangeNotification( CHANGE_PARAMS|CHANGE_TOOLBAR ); + if ( CarAvailableCount() <= 0 ) { + if ( NoticeMessage( MSG_NO_CARS, _("Yes"), _("No") ) > 0 ) { + DoCarDlg(); + DoChangeNotification( CHANGE_PARAMS ); + } + } + EnableCommands(); + if ( curTrainDlg == NULL ) + curTrainDlg = CreateTrainControlDlg(); + curTrainDlg->train = NULL; +#ifdef LATER + if ( trainW == NULL ) + trainW = ParamCreateDialog( MakeWindowTitle(_("Train")), NULL, trainPGp ); + ParamLoadControls( trainPGp ); + wListClear( (wList_p)trainPLs[0].control ); +#endif + wListClear( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control ); + Dtrain.state = 0; + trk0 = NULL; + tempSegs_da.cnt = 0; + DYNARR_SET( trkSeg_t, tempSegs_da, 8 ); + /*MainRedraw();*/ + /*wDrawSaveImage( mainD.d );*/ + /*trainEnable = FALSE;*/ + RestartTrains(); + wButtonSetLabel( trainPauseB, (char*)goI ); + trainTime0 = 0; + AttachTrains(); + DrawAllCars(); + curTrainDlg->train = NULL; + curTrainDlg->speed = -1; + wDrawClear( (wDraw_p)curTrainDlg->trainPGp->paramPtr[I_SLIDER].control ); + LocoListInit(); + ControllerDialogSync( curTrainDlg ); + wShow( curTrainDlg->win ); + wControlShow( (wControl_p)newcarB, (toolbarSet&(1<<BG_HOTBAR)) == 0 ); + currCarItemPtr = NULL; + return C_CONTINUE; + + case C_TEXT: + if ( Dtrain.state == 0 ) + return C_CONTINUE; + else + return C_CONTINUE; + + case C_DOWN: + /*trainEnable = FALSE;*/ + InfoMessage( "" ); + if ( trainsState == TRAINS_RUN ) { + trainsState = TRAINS_PAUSE; + TrainTimeEndPause(); + } + pos0 = pos; + if ( currCarItemPtr != NULL ) { +#ifdef LATER + ParamLoadData( &newCarPG ); +#endif + currCar = NewCar( -1, currCarItemPtr, zero, 0.0 ); + CarItemUpdate( currCarItemPtr ); + HotBarCancel(); + if ( currCar == NULL ) { + LOG1( log_error, ( "Train: currCar became NULL 1\n" ) ) + return C_CONTINUE; + } + xx = GetTrkExtraData(currCar); + dist = CarItemCoupledLength(xx->item)/2.0; + Translate( &pos, xx->trvTrk.pos, xx->trvTrk.angle, dist ); + SetTrkEndPoint( currCar, 0, pos, xx->trvTrk.angle ); + Translate( &pos, xx->trvTrk.pos, xx->trvTrk.angle+180.0, dist ); + SetTrkEndPoint( currCar, 1, pos, NormalizeAngle(xx->trvTrk.angle+180.0) ); + /*xx->state |= (xx->item->options&CAR_DESC_BITS);*/ + ClrLocoMaster(xx); + if ( CarItemIsLoco(xx->item) ) { + SetLocoMaster(xx); + LocoListChangeEntry( NULL, currCar ); + if ( currCar == NULL ) { + LOG1( log_error, ( "Train: currCar became NULL 2\n" ) ) + return C_CONTINUE; + } + } +#ifdef LATER + wPrefSetString( "Car Road Name", xx->ITEM->title, newCarRoad ); + number = strtol( CarItemNumber(xx->item), &cp, 10 ); + if ( cp == NULL || *cp != 0 ) + number = -1; + wPrefSetInteger( "Car Number", xx->ITEM->title, number ); +#endif + if( (trk0 = OnTrack( &pos0, FALSE, TRUE ) ) ) { + xx->trvTrk.angle = GetAngleAtPoint( trk0, pos0, &ep0, &ep1 ); + if ( NormalizeAngle( FindAngle( pos, pos0 ) - xx->trvTrk.angle ) > 180.0 ) + xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle + 180 ); + xx->status = ST_StopManual; + } else { + xx->trvTrk.angle = 90; + } + PlaceTrainInit( currCar, trk0, pos0, xx->trvTrk.angle, (MyGetKeyState()&WKEY_SHIFT) == 0 ); + /*DrawCars( &tempD, currCar, TRUE );*/ + } else { + currCar = FindCar( &pos ); + delta.x = pos.x - pos0.x; + delta.y = pos.y - pos0.y; + if ( logTable(log_trainMove).level >= 1 ) { + if ( currCar ) { + xx = GetTrkExtraData(currCar); + LogPrintf( "selected %s\n", CarItemNumber(xx->item) ); + for ( dir=0; dir<2; dir++ ) { + int dir1 = dir; + track_p car1 = currCar; + struct extraData * xx1 = GetTrkExtraData(car1); + LogPrintf( "dir=%d\n", dir1 ); + WALK_CARS_START( car1, xx1, dir1 ) + LogPrintf( " %s [%0.3f,%d]\n", CarItemNumber(xx1->item), xx1->trvTrk.angle, dir1 ); + WALK_CARS_END( car1, xx1, dir1 ) + } + } + } + } + if ( currCar == NULL ) + return C_CONTINUE; + trk0 = FindMasterLoco( currCar, NULL ); + if ( trk0 ) + SetCurTrain( trk0 ); + DrawAllCars(); + return C_CONTINUE; + + case C_MOVE: + if ( currCar == NULL ) + return C_CONTINUE; + pos.x += delta.x; + pos.y += delta.y; + pos0 = pos; + /*DrawCars( &tempD, currCar, FALSE );*/ + xx = GetTrkExtraData(currCar); + trk0 = OnTrack( &pos0, FALSE, TRUE ); + if ( /*currCarItemPtr != NULL &&*/ trk0 ) { + angle1 = GetAngleAtPoint( trk0, pos0, &ep0, &ep1 ); + if ( currCarItemPtr != NULL ) { + if ( NormalizeAngle( FindAngle( pos, pos0 ) - angle1 ) > 180.0 ) + angle1 = NormalizeAngle( angle1 + 180 ); + } else { + if ( NormalizeAngle( xx->trvTrk.angle - angle1 + 90.0 ) > 180.0 ) + angle1 = NormalizeAngle( angle1 + 180 ); + } + xx->trvTrk.angle = angle1; + } + tempSegs_da.cnt = 1; + PlaceTrainInit( currCar, trk0, pos0, xx->trvTrk.angle, (MyGetKeyState()&WKEY_SHIFT) == 0 ); + ControllerDialogSync( curTrainDlg ); + DrawAllCars(); + return C_CONTINUE; + + + case C_UP: + if ( currCar != NULL ) { + trk0 = FindMasterLoco( currCar, NULL ); + if ( trk0 ) { + xx = GetTrkExtraData( trk0 ); + if ( !IsOnTrack(xx) || xx->speed <= 0 ) + StopTrain( trk0, ST_StopManual ); + } + Dtrain.state = 1; + /*MainRedraw();*/ + ControllerDialogSync( curTrainDlg ); + } + DrawAllCars(); + InfoSubstituteControls( NULL, NULL ); + currCar = trk0 = NULL; + currCarItemPtr = NULL; + /*trainEnable = TRUE;*/ + if ( trainsState == TRAINS_PAUSE ) { + RestartTrains(); + } + return C_CONTINUE; + + case C_LCLICK: + if ( MyGetKeyState() & WKEY_SHIFT ) { + pos0 = pos; + programMode = MODE_DESIGN; + if ( (trk0=OnTrack(&pos,FALSE,TRUE)) && + QueryTrack( trk0, Q_CAN_NEXT_POSITION ) && + TrainOnMovableTrack( trk0, &trk1) ) { + if ( trk1 ) { + xx = GetTrkExtraData(trk1); + pos1 = xx->trvTrk.pos; + angle1 = xx->trvTrk.angle; + } else { + pos1 = pos0; + angle1 = 0; + } + AdvancePositionIndicator( trk0, pos0, &pos1, &angle1 ); + if ( trk1 ) { + xx->trvTrk.pos = pos1; + xx->trvTrk.angle = angle1; + PlaceTrain( trk1, FALSE, TRUE ); + DrawAllCars(); + } + } + programMode = MODE_TRAIN; + trk0 = NULL; + MainRedraw(); //Make sure track is redrawn after switch thrown + } else { + trk0 = FindCar( &pos ); + if ( trk0 == NULL ) + return C_CONTINUE; + trk0 = FindMasterLoco( trk0, NULL ); + if ( trk0 == NULL ) + return C_CONTINUE; + SetCurTrain( trk0 ); + } + return C_CONTINUE; + + case C_RCLICK: + trainFuncPos = pos; + trainFuncCar = FindCar( &pos ); + if ( trainFuncCar == NULL || + GetTrkType(trainFuncCar) != T_CAR ) + return C_CONTINUE; + xx = GetTrkExtraData( trainFuncCar ); + trk0 = FindMasterLoco(trainFuncCar,NULL); + dir = IsAligned( xx->trvTrk.angle, FindAngle(xx->trvTrk.pos,trainFuncPos) ) ? 0 : 1; + wMenuPushEnable( trainPopupMI[DO_UNCOUPLE], GetTrkEndTrk( trainFuncCar, dir )!=NULL ); + wMenuPushEnable( trainPopupMI[DO_MUMASTER], CarItemIsLoco(xx->item) && !IsLocoMaster(xx) ); + if ( trk0 ) xx = GetTrkExtraData(trk0); + wMenuPushEnable( trainPopupMI[DO_CHANGEDIR], trk0!=NULL ); + wMenuPushEnable( trainPopupMI[DO_STOP], trk0!=NULL && xx->speed>0 ); + /*trainEnable = FALSE;*/ +#ifdef LATER + if ( trainsState == TRAINS_RUN ) + trainsState = TRAINS_PAUSE; +#endif + trk0 = FindMasterLoco( trainFuncCar, NULL ); + if ( trk0 ) + SetCurTrain( trk0 ); + if ( !inPlayback ) + wMenuPopupShow( trainPopupM ); + return C_CONTINUE; + + case C_REDRAW: +#ifdef LATER + if (Dtrain.state == 1 && !suppressTrainRedraw) { + mainD.funcs->options = wDrawOptTemp; + mainD.funcs->options = 0; + } +#endif + wDrawSaveImage(mainD.d); + DrawAllCars(); + wWinGetSize( mainW, &w, &h ); + w -= wControlGetPosX( newCarControls[0] ) + 4; + if ( w > 20 ) + wListSetSize( (wList_p)newCarControls[0], w, wControlGetHeight( newCarControls[0] ) ); + return C_CONTINUE; + + case C_CANCEL: + /*trainEnable = FALSE;*/ + trainsState = TRAINS_STOP; + TrainTimeEndPause(); + LOG( log_trainMove, 1, ( "Train Cancel\n" ) ) + Dtrain.state = 0; + doDrawTurnoutPosition = 0; + drawCarEnable = TRUE; + programMode = MODE_DESIGN; + UpdateTrainAttachment(); + UndoResume(); + DoChangeNotification( CHANGE_PARAMS|CHANGE_TOOLBAR ); + if ( curTrainDlg->win ) + wHide( curTrainDlg->win ); + MainRedraw(); + curTrainDlg->train = NULL; + return C_CONTINUE; + + + case C_CONFIRM: + /*trainEnable = FALSE;*/ + if ( trainsState != TRAINS_STOP ) { + trainsState = TRAINS_STOP; + wButtonSetLabel( trainPauseB, (char*)stopI ); + TrainTimeEndPause(); + } + currCar = NULL; + currCarItemPtr = NULL; + HotBarCancel(); + InfoSubstituteControls( NULL, NULL ); + return C_TERMINATE; + + } + + return C_CONTINUE; + +} + + +/* + * + */ + +EXPORT STATUS_T CmdCarDescAction( + wAction_t action, + coOrd pos ) +{ + return CmdTrain( action, pos ); +} + +#include "bitmaps/train.xpm" +#include "bitmaps/exit.xpm" +#include "bitmaps/newcar.xpm" +#include "bitmaps/zero.xpm" +#include "bitmaps/ballgreen.xpm" +#include "bitmaps/ballred.xpm" + + +static void CmdTrainStopGo( void * junk ) +{ + wIcon_p icon; + if ( trainsState == TRAINS_STOP ) { + icon = goI; + RestartTrains(); + } else { + trainsState = TRAINS_STOP; + icon = stopI; + TrainTimeEndPause(); + } + ControllerDialogSync( curTrainDlg ); + wButtonSetLabel( trainPauseB, (char*)icon ); + if ( recordF ) + fprintf( recordF, "TRAINSTOPGO %s\n", trainsState==TRAINS_STOP?"STOP":"GO" ); +} + +static BOOL_T TrainStopGoPlayback( char * line ) +{ + while (*line && isspace(*line) ) line++; + if ( (strcasecmp( line, "STOP" ) == 0) != (trainsState == TRAINS_STOP) ) + CmdTrainStopGo(NULL); + return TRUE; +} + + +static void CmdTrainExit( void * junk ) +{ + Reset(); + InfoSubstituteControls( NULL, NULL ); + MainRedraw(); +} + + +static void TrainFunc( + void * action ) +{ + struct extraData * xx, *xx1; + ANGLE_T angle; + int dir; + track_p loco; + track_p temp0, temp1; + coOrd pos0, pos1; + ANGLE_T angle0, angle1; + EPINX_T ep0=-1, ep1=-1; + + if ( trainFuncCar == NULL ) { + fprintf( stderr, "trainFunc: trainFuncCar==NULL\n" ); + return; + } + + xx = GetTrkExtraData(trainFuncCar); + angle = FindAngle( xx->trvTrk.pos, trainFuncPos ); + angle = NormalizeAngle( angle-xx->trvTrk.angle ); + dir = (angle>90&&angle<270); + + switch ((int)(long)action) { + case DO_UNCOUPLE: + if ( GetTrkEndTrk(trainFuncCar,dir) ) + UncoupleCars( trainFuncCar, GetTrkEndTrk(trainFuncCar,dir) ); + break; + case DO_FLIPCAR: + temp0 = GetTrkEndTrk(trainFuncCar,0); + pos0 = GetTrkEndPos(trainFuncCar,0); + angle0 = GetTrkEndAngle(trainFuncCar,0); + temp1 = GetTrkEndTrk(trainFuncCar,1); + pos1 = GetTrkEndPos(trainFuncCar,1); + angle1 = GetTrkEndAngle(trainFuncCar,1); + if ( temp0 ) { + ep0 = GetEndPtConnectedToMe(temp0,trainFuncCar); + trainFuncCar->endPt[0].track = NULL; + temp0->endPt[ep0].track = NULL; + } + if ( temp1 ) { + ep1 = GetEndPtConnectedToMe(temp1,trainFuncCar); + trainFuncCar->endPt[1].track = NULL; + temp1->endPt[ep1].track = NULL; + } + xx->direction = !xx->direction; + FlipTraverseTrack( &xx->trvTrk ); + SetTrkEndPoint( trainFuncCar, 0, pos1, angle1 ); + SetTrkEndPoint( trainFuncCar, 1, pos0, angle0 ); + if ( temp0 ) { + trainFuncCar->endPt[1].track = temp0; + temp0->endPt[ep0].track = trainFuncCar; + } + if ( temp1 ) { + trainFuncCar->endPt[0].track = temp1; + temp1->endPt[ep1].track = trainFuncCar; + } + ControllerDialogSync( curTrainDlg ); + PlaceCar( trainFuncCar ); + break; + case DO_FLIPTRAIN: + FlipTrain( trainFuncCar ); + /*PlaceTrain( trainFuncCar, xx->trk, xx->trvTrk.pos, xx->trvTrk.angle );*/ + break; + case DO_DELCAR: + for ( dir=0; dir<2; dir++ ) + if ( GetTrkEndTrk(trainFuncCar,dir) ) + UncoupleCars( trainFuncCar, GetTrkEndTrk(trainFuncCar,dir) ); + if ( CarItemIsLoco(xx->item) ) + LocoListChangeEntry( trainFuncCar, NULL ); + trainFuncCar->deleted = TRUE; + /*DeleteTrack( trainFuncCar, FALSE );*/ + CarItemUpdate( xx->item ); + HotBarCancel(); + InfoSubstituteControls( NULL, NULL ); + break; + case DO_DELTRAIN: + dir = 0; + loco = FindMasterLoco( trainFuncCar, NULL ); + WALK_CARS_START( trainFuncCar, xx, dir ) + WALK_CARS_END( trainFuncCar, xx, dir ) + dir = 1-dir; + temp0 = NULL; + WALK_CARS_START( trainFuncCar, xx, dir ) + if ( temp0 ) { + xx1 = GetTrkExtraData(temp0); + temp0->deleted = TRUE; + /*DeleteTrack( temp0, FALSE );*/ + CarItemUpdate( xx1->item ); + } + temp0 = trainFuncCar; + WALK_CARS_END( trainFuncCar, xx, dir ) + if ( temp0 ) { + xx1 = GetTrkExtraData(temp0); + temp0->deleted = TRUE; + /*DeleteTrack( temp0, FALSE );*/ + CarItemUpdate( xx1->item ); + } + if ( loco ) + LocoListChangeEntry( loco, NULL ); + HotBarCancel(); + InfoSubstituteControls( NULL, NULL ); + break; + case DO_MUMASTER: + if ( CarItemIsLoco(xx->item) ) { + loco = FindMasterLoco( trainFuncCar, NULL ); + if ( loco != trainFuncCar ) { + SetLocoMaster(xx); + LOG( log_trainMove, 1, ( "%s gets master\n", CarItemNumber(xx->item) ) ) + if ( loco ) { + xx1 = GetTrkExtraData( loco ); + ClrLocoMaster(xx1); + LOG( log_trainMove, 1, ( "%s looses master\n", CarItemNumber(xx1->item) ) ) + xx->speed = xx1->speed; + xx1->speed = 0; + } + LocoListChangeEntry( loco, trainFuncCar ); + } + } + break; + case DO_CHANGEDIR: + loco = FindMasterLoco( trainFuncCar, NULL ); + if ( loco ) { + xx = GetTrkExtraData(loco); + xx->direction = !xx->direction; + SetTrainDirection(loco); + ControllerDialogSync( curTrainDlg ); + } + break; + case DO_STOP: + loco = FindMasterLoco( trainFuncCar, NULL ); + if ( loco ) { + StopTrain( loco, ST_StopManual ); + ControllerDialogSync( curTrainDlg ); + } + break; + } + MainRedraw(); //Redraw if Train altered + + if ( trainsState == TRAINS_PAUSE ) { + RestartTrains(); + } else { + DrawAllCars(); + } +} + + +EXPORT void InitCmdTrain( wMenu_p menu ) +{ + log_trainMove = LogFindIndex( "trainMove" ); + log_trainPlayback = LogFindIndex( "trainPlayback" ); + trainPLs[I_ZERO].winLabel = (char*)wIconCreatePixMap(zero_xpm); + ParamRegister( &trainPG ); + AddMenuButton( menu, CmdTrain, "cmdTrain", _("Train"), wIconCreatePixMap(train_xpm), LEVEL0_50, IC_POPUP2|IC_LCLICK|IC_RCLICK, 0, NULL ); + stopI = wIconCreatePixMap( ballred ); + goI = wIconCreatePixMap( ballgreen ); + trainPauseB = AddToolbarButton( "cmdTrainPause", stopI, IC_MODETRAIN_ONLY, CmdTrainStopGo, NULL ); + AddToolbarButton( "cmdTrainExit", wIconCreatePixMap(exit_xpm), IC_MODETRAIN_ONLY, CmdTrainExit, NULL ); + newcarB = AddToolbarButton( "cmdTrainNewCar", wIconCreatePixMap(newcar_xpm), IC_MODETRAIN_ONLY, CarItemLoadList, NULL ); + + T_CAR = InitObject( &carCmds ); + +#ifdef LATER + trainPGp = ParamCreateGroup( "trainW", "train", 0, trainPLs, sizeof trainPLs/sizeof trainPLs[0], NULL, 0, _("Ok"), trainOk, wHide ); + ParamRegister( trainPGp ); +#endif + + trainPopupM = MenuRegister( "Train Commands" ); + trainPopupMI[DO_UNCOUPLE] = wMenuPushCreate( trainPopupM, "", _("Uncouple"), 0, TrainFunc, (void*)DO_UNCOUPLE ); + trainPopupMI[DO_FLIPCAR] = wMenuPushCreate( trainPopupM, "", _("Flip Car"), 0, TrainFunc, (void*)DO_FLIPCAR ); + trainPopupMI[DO_FLIPTRAIN] = wMenuPushCreate( trainPopupM, "", _("Flip Train"), 0, TrainFunc, (void*)DO_FLIPTRAIN ); + trainPopupMI[DO_MUMASTER] = wMenuPushCreate( trainPopupM, "", _("MU Master"), 0, TrainFunc, (void*)DO_MUMASTER ); + trainPopupMI[DO_CHANGEDIR] = wMenuPushCreate( trainPopupM, "", _("Change Direction"), 0, TrainFunc, (void*)DO_CHANGEDIR ); + trainPopupMI[DO_STOP] = wMenuPushCreate( trainPopupM, "", _("Stop"), 0, TrainFunc, (void*)DO_STOP ); + wMenuSeparatorCreate( trainPopupM ); + trainPopupMI[DO_DELCAR] = wMenuPushCreate( trainPopupM, "", _("Remove Car"), 0, TrainFunc, (void*)DO_DELCAR ); + trainPopupMI[DO_DELTRAIN] = wMenuPushCreate( trainPopupM, "", _("Remove Train"), 0, TrainFunc, (void*)DO_DELTRAIN ); + +#ifdef LATER + ParamRegister( &newCarPG ); + ParamCreateControls( &newCarPG, NULL ); + newCarControls[0] = newCarPLs[0].control; + newCarControls[1] = newCarPLs[1].control; +#endif + AddPlaybackProc( "TRAINSTOPGO", (playbackProc_p)TrainStopGoPlayback, NULL ); + AddPlaybackProc( "TRAINPAUSE", (playbackProc_p)TrainTimeDoPause, NULL ); + AddPlaybackProc( "TRAINMOVIE", (playbackProc_p)TrainDoMovie, NULL ); +} + diff --git a/app/bin/ctrain.h b/app/bin/ctrain.h new file mode 100644 index 0000000..10f836f --- /dev/null +++ b/app/bin/ctrain.h @@ -0,0 +1,55 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/ctrain.h,v 1.1 2005-12-07 15:46:59 rc-flyer 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. + */ + + +struct carItem_t; +typedef struct carItem_t carItem_t; +typedef carItem_t * carItem_p; +typedef struct { + coOrd pos; + ANGLE_T angle; + } vector_t; + +carItem_p currCarItemPtr; +wControl_p newCarControls[2]; +void DoCarDlg( void ); +BOOL_T CarItemRead( char * ); +track_p NewCar( wIndex_t, carItem_p, coOrd, ANGLE_T ); +void CarGetPos( track_p, coOrd *, ANGLE_T * ); +void CarSetVisible( track_p ); +void CarItemUpdate( carItem_p ); +void CarItemLoadList( void * ); +char * CarItemDescribe( carItem_p, long, long * ); +coOrd CarItemFindCouplerMountPoint( carItem_p, traverseTrack_t, int ); +void CarItemSize( carItem_p, coOrd * ); +char * CarItemNumber( carItem_p ); +DIST_T CarItemCoupledLength( carItem_p ); +BOOL_T CarItemIsLoco( carItem_p ); +BOOL_T CarItemIsLocoMaster( carItem_p ); +void CarItemSetLocoMaster( carItem_p, BOOL_T ); +void CarItemSetTrack( carItem_p, track_p ); +void CarItemPlace( carItem_p, traverseTrack_p, DIST_T * ); +void CarItemDraw( drawCmd_p, carItem_p, wDrawColor, int, BOOL_T, vector_t * ); +int CarAvailableCount( void ); +BOOL_T TraverseTrack2( traverseTrack_p, DIST_T ); +void FlipTraverseTrack( traverseTrack_p ); + diff --git a/app/bin/cturnout.c b/app/bin/cturnout.c new file mode 100644 index 0000000..55b7a4d --- /dev/null +++ b/app/bin/cturnout.c @@ -0,0 +1,2626 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cturnout.c,v 1.8 2009-08-16 13:07:14 m_fischer Exp $ + * + * T_TURNOUT + * + */ + +/* 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 <ctype.h> +#include "track.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "compound.h" +#include "cjoin.h" +#include "i18n.h" + +#include <stdint.h> + +EXPORT TRKTYP_T T_TURNOUT = -1; + +#define TURNOUTCMD + +#define MIN_TURNOUT_SEG_CONNECT_DIST (0.1) + +EXPORT dynArr_t turnoutInfo_da; + +EXPORT turnoutInfo_t * curTurnout = NULL; +EXPORT long curTurnoutEp = 0; + +static int log_turnout = 0; +static int log_traverseTurnout = 0; + +static wMenu_p turnoutPopupM; + +#ifdef TURNOUTCMD +static drawCmd_t turnoutD = { + NULL, + &screenDrawFuncs, + 0, + 1.0, + 0.0, + {0.0,0.0}, {0.0,0.0}, + Pix2CoOrd, CoOrd2Pix }; + +static wIndex_t turnoutHotBarCmdInx; +static wIndex_t turnoutInx; +static long hideTurnoutWindow; +static void RedrawTurnout(void); +static void SelTurnoutEndPt( wIndex_t, coOrd ); + +static wPos_t turnoutListWidths[] = { 80, 80, 220 }; +static const char * turnoutListTitles[] = { N_("Manufacturer"), N_("Part No"), N_("Description") }; +static paramListData_t listData = { 13, 400, 3, turnoutListWidths, turnoutListTitles }; +static const char * hideLabels[] = { N_("Hide"), NULL }; +static paramDrawData_t turnoutDrawData = { 490, 200, (wDrawRedrawCallBack_p)RedrawTurnout, SelTurnoutEndPt, &turnoutD }; +static paramData_t turnoutPLs[] = { +#define I_LIST (0) +#define turnoutListL ((wList_p)turnoutPLs[I_LIST].control) + { PD_LIST, &turnoutInx, "list", PDO_NOPREF|PDO_DLGRESIZEW, &listData, NULL, BL_DUP }, +#define I_DRAW (1) +#define turnoutDrawD ((wDraw_p)turnoutPLs[I_DRAW].control) + { PD_DRAW, NULL, "canvas", PDO_NOPSHUPD|PDO_DLGUNDERCMDBUTT|PDO_DLGRESIZE, &turnoutDrawData, NULL, 0 }, +#define I_NEW (2) +#define turnoutNewM ((wMenu_p)turnoutPLs[I_NEW].control) + { PD_MENU, NULL, "new", PDO_DLGCMDBUTTON, NULL, N_("New") }, +#define I_HIDE (3) +#define turnoutHideT ((wChoice_p)turnoutPLs[I_HIDE].control) + { PD_TOGGLE, &hideTurnoutWindow, "hide", PDO_DLGCMDBUTTON, /*CAST_AWAY_CONST*/(void*)hideLabels, NULL, BC_NOBORDER } }; +static paramGroup_t turnoutPG = { "turnout", 0, turnoutPLs, sizeof turnoutPLs/sizeof turnoutPLs[0] }; +#endif + + + +/**************************************** + * + * TURNOUT LIST MANAGEMENT + * + */ + + +EXPORT turnoutInfo_t * CreateNewTurnout( + char * scale, + char * title, + wIndex_t segCnt, + trkSeg_p segData, + wIndex_t pathLen, + PATHPTR_T paths, + EPINX_T endPtCnt, + trkEndPt_t * endPts, + wBool_t updateList ) +{ + turnoutInfo_t * to; + long changes=0; + + to = FindCompound( FIND_TURNOUT, scale, title ); + if (to == NULL) { + DYNARR_APPEND( turnoutInfo_t *, turnoutInfo_da, 10 ); + to = (turnoutInfo_t*)MyMalloc( sizeof *to ); + turnoutInfo(turnoutInfo_da.cnt-1) = to; + to->title = MyStrdup( title ); + to->scaleInx = LookupScale( scale ); + changes = CHANGE_PARAMS; + } + to->segCnt = segCnt; + to->segs = (trkSeg_p)memdup( segData, (sizeof *segData) * segCnt ); + GetSegBounds( zero, 0.0, segCnt, to->segs, &to->orig, &to->size ); + to->endCnt = endPtCnt; + to->endPt = (trkEndPt_t*)memdup( endPts, (sizeof *endPts) * to->endCnt ); + + to->pathLen = pathLen; + to->paths = (PATHPTR_T)memdup( paths, (sizeof *to->paths) * to->pathLen ); + to->paramFileIndex = curParamFileIndex; + if (curParamFileIndex == PARAM_CUSTOM) + to->contentsLabel = "Custom Turnouts"; + else + to->contentsLabel = curSubContents; +#ifdef TURNOUTCMD + if (updateList && turnoutListL != NULL) { + FormatCompoundTitle( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, title ); + if (message[0] != '\0') + wListAddValue( turnoutListL, message, NULL, to ); + } +#endif + + to->barScale = curBarScale>0?curBarScale:-1; + to->special = TOnormal; + if (updateList && changes) + DoChangeNotification( changes ); + return to; +} + + + +EXPORT wIndex_t CheckPaths( + wIndex_t segCnt, + trkSeg_p segs, + PATHPTR_T paths ) +{ + int pc, ps; + PATHPTR_T pp; + int inx, inx1; + static dynArr_t segMap_da; + int segInx[2], segEp[2]; + int segTrkLast = -1; + trkSeg_t tempSeg; + +#define segMap(N) DYNARR_N( trkSeg_p, segMap_da, N ) + + DYNARR_RESET( trkSeg_p, segMap_da ); + for ( inx=0; inx<segCnt; inx++ ) { + if ( IsSegTrack(&segs[inx]) ) { + if ( segTrkLast != inx-1 ) { + tempSeg = segs[inx]; + segTrkLast++; + for ( inx1=inx; inx1>segTrkLast; inx1-- ) { + segs[inx1] = segs[inx1-1]; + } + segs[segTrkLast] = tempSeg; + } else { + segTrkLast = inx; + } + DYNARR_APPEND( trkSeg_p, segMap_da, 10 ); + segMap(segMap_da.cnt-1) = &segs[inx]; + } + } + + for ( pc=0,pp=paths; *pp; pp+=2,pc++ ) { + for ( ps=0,pp+=strlen((char *)pp)+1; pp[0]!=0 || pp[1]!=0; pp++,ps++ ) { +#ifdef LATER + if (*pp >= '0' && *pp <= '9') + *pp -= '0'; + else if (*pp >= 'A' && *pp <= 'Z') + *pp -= 'A' - 10; + if (*pp < 0 || *pp > segCnt) { + InputError( _("Turnout path[%d:%d] out of bounds: %d"), + FALSE, pc, ps, *pp); + return -1; + } +#endif + + if (pp[0]!=0 && pp[1]!=0 ) { + /* check connectivity */ + DIST_T d; + GetSegInxEP( pp[0], &segInx[0], &segEp[0] ); + GetSegInxEP( pp[1], &segInx[1], &segEp[1] ); + if ( !IsSegTrack( &segs[segInx[0]] ) ) { + InputError( _("Turnout path[%d] %d is not a track segment"), + FALSE, pc, pp[0] ); + return -1; + } + if ( !IsSegTrack( &segs[segInx[1]] ) ) { + InputError( _("Turnout path[%d] %d is not a track segment"), + FALSE, pc, pp[1] ); + return -1; + } + d = FindDistance( + GetSegEndPt( &segs[segInx[0]], 1-segEp[0], FALSE, NULL ), + GetSegEndPt( &segs[segInx[1]], segEp[1], FALSE, NULL ) ); + if (d > MIN_TURNOUT_SEG_CONNECT_DIST) { + InputError( _("Turnout path[%d] %d-%d not connected: %0.3f"), + FALSE, pc, pp[0], pp[1], d ); + return -1; + } + } + + } + } + return pp-paths+1; +} + + +static BOOL_T ReadTurnoutParam( + char * firstLine ) +{ + char scale[10]; + char *title; + turnoutInfo_t * to; + + if ( !GetArgs( firstLine+8, "sq", scale, &title ) ) + return FALSE; + DYNARR_RESET( trkEndPt_t, tempEndPts_da ); + pathCnt = 0; + if (ReadSegs()) { + CheckPaths( tempSegs_da.cnt, &tempSegs(0), pathPtr ); + to = CreateNewTurnout( scale, title, tempSegs_da.cnt, &tempSegs(0), + pathCnt, pathPtr, tempEndPts_da.cnt, &tempEndPts(0), FALSE ); + if (to == NULL) + return FALSE; + if (tempSpecial[0] != '\0') { + if (strncmp( tempSpecial, ADJUSTABLE, strlen(ADJUSTABLE) ) == 0) { + to->special = TOadjustable; + GetArgs( tempSpecial+strlen(ADJUSTABLE), "ff", + &to->u.adjustable.minD, &to->u.adjustable.maxD ); + + } else { + InputError(_("Unknown special case"), TRUE); + } + } + if (tempCustom[0] != '\0') { + to->customInfo = MyStrdup( tempCustom ); + } + } + MyFree( title ); + return TRUE; +} + + +EXPORT turnoutInfo_t * TurnoutAdd( long mode, SCALEINX_T scale, wList_p list, coOrd * maxDim, EPINX_T epCnt ) +{ + wIndex_t inx; + turnoutInfo_t * to, * to1 = NULL; + for ( inx = 0; inx < turnoutInfo_da.cnt; inx++ ) { + to = turnoutInfo(inx); + if ( IsParamValid(to->paramFileIndex) && + to->segCnt > 0 && + CompatibleScale( TRUE, to->scaleInx, scale ) && + /*strcasecmp( to->scale, scaleName ) == 0 && */ + ( epCnt <= 0 || epCnt == to->endCnt ) ) { + if (to1==NULL) + to1 = to; + FormatCompoundTitle( mode, to->title ); + if (message[0] != '\0') { + wListAddValue( list, message, NULL, to ); + if (maxDim) { + if (to->size.x > maxDim->x) + maxDim->x = to->size.x; + if (to->size.y > maxDim->y) + maxDim->y = to->size.y; + } + } + } + } + return to1; +} + +/**************************************** + * + * Adjustable Track Support + * + */ + + +static void ChangeAdjustableEndPt( + track_p trk, + EPINX_T ep, + DIST_T d ) +{ + struct extraData * xx = GetTrkExtraData(trk); + coOrd pos; + trkSeg_p segPtr; + ANGLE_T angle = GetTrkEndAngle( trk, ep ); + Translate( &pos, GetTrkEndPos( trk, 1-ep ), angle, d ); + UndoModify(trk); + SetTrkEndPoint( trk, ep, pos, angle ); + if ( ep == 0 ) + xx->orig = pos; + for ( segPtr=xx->segs; segPtr<&xx->segs[xx->segCnt]; segPtr++ ) { + switch (segPtr->type) { + case SEG_STRLIN: + case SEG_STRTRK: + segPtr->u.l.pos[1].x = d; + break; + default: + ; + } + } + ComputeBoundingBox( trk ); + DrawNewTrack( trk ); +} + + +EXPORT BOOL_T ConnectAdjustableTracks( + track_p trk1, + EPINX_T ep1, + track_p trk2, + EPINX_T ep2 ) +{ + struct extraData * xx1; + struct extraData * xx2; + BOOL_T adj1, adj2; + coOrd p1, p2; + ANGLE_T a, a1, a2; + DIST_T d, maxD, d1, d2; + BOOL_T rc; + coOrd off; + DIST_T beyond; + + xx1 = GetTrkExtraData(trk1); + xx2 = GetTrkExtraData(trk2); + adj1 = adj2 = FALSE; + if (GetTrkType(trk1) == T_TURNOUT && xx1->special == TOadjustable) + adj1 = TRUE; + if (GetTrkType(trk2) == T_TURNOUT && xx2->special == TOadjustable) + adj2 = TRUE; + if (adj1 == FALSE && adj2 == FALSE) + return FALSE; + a1 = GetTrkEndAngle( trk1, ep1 ); + a2 = GetTrkEndAngle( trk2, ep2 ); + a = NormalizeAngle( a1 - a2 + 180.0 + connectAngle/2.0); + if (a>connectAngle) + return FALSE; + UndoStart( _("Connect Adjustable Tracks"), "changeAdjustableEndPt" ); + maxD = 0.0; + if (adj1) { + p1 = GetTrkEndPos( trk1, 1-ep1 ); + Translate( &p1, p1, a1, xx1->u.adjustable.minD ); + maxD += xx1->u.adjustable.maxD-xx1->u.adjustable.minD; + } else { + p1 = GetTrkEndPos( trk1, ep1 ); + } + if (adj2) { + p2 = GetTrkEndPos( trk2, 1-ep2 ); + Translate( &p2, p2, a2, xx2->u.adjustable.minD ); + maxD += xx2->u.adjustable.maxD-xx2->u.adjustable.minD; + } else { + p2 = GetTrkEndPos( trk2, ep2 ); + } + d = FindDistance( p1, p2 ); + rc = TRUE; + if (d > maxD) { + d = maxD; + rc = FALSE; + } + FindPos( &off, &beyond, p1, p2, a1, 10000.0 ); + if (fabs(off.y) > connectDistance) + rc = FALSE; + if (adj1) { + UndrawNewTrack( trk1 ); + d1 = d * (xx1->u.adjustable.maxD-xx1->u.adjustable.minD)/maxD + xx1->u.adjustable.minD; + ChangeAdjustableEndPt( trk1, ep1, d1 ); + } + if (adj2) { + UndrawNewTrack( trk2 ); + d2 = d * (xx2->u.adjustable.maxD-xx2->u.adjustable.minD)/maxD + xx2->u.adjustable.minD; + ChangeAdjustableEndPt( trk2, ep2, d2 ); + } + if (rc) { + DrawEndPt( &mainD, trk1, ep1, wDrawColorWhite ); + DrawEndPt( &mainD, trk2, ep2, wDrawColorWhite ); + ConnectTracks( trk1, ep1, trk2, ep2 ); + DrawEndPt( &mainD, trk1, ep1, wDrawColorBlack ); + DrawEndPt( &mainD, trk2, ep2, wDrawColorBlack ); + } + return rc; +} + +/**************************************** + * + * Draw Turnout Roadbed + * + */ + +int roadbedOnScreen = 0; + + +void DrawTurnoutRoadbedSide( drawCmd_p d, wDrawColor color, coOrd orig, ANGLE_T angle, trkSeg_p sp, ANGLE_T side, int first, int last ) +{ + segProcData_t data; + if (last<=first) + return; + data.drawRoadbedSide.first = first; + data.drawRoadbedSide.last = last; + data.drawRoadbedSide.side = side; + data.drawRoadbedSide.roadbedWidth = roadbedWidth; + data.drawRoadbedSide.rbw = (wDrawWidth)floor(roadbedLineWidth*(d->dpi/d->scale)+0.5); + data.drawRoadbedSide.orig = orig; + data.drawRoadbedSide.angle = angle; + data.drawRoadbedSide.color = color; + data.drawRoadbedSide.d = d; + SegProc( SEGPROC_DRAWROADBEDSIDE, sp, &data ); +} + + +static void ComputeAndDrawTurnoutRoadbedSide( + drawCmd_p d, + wDrawColor color, + coOrd orig, + ANGLE_T angle, + trkSeg_p segPtr, + int segCnt, + int segInx, + ANGLE_T side ) +{ + unsigned long res, res1; + int b0, b1; + res = ComputeTurnoutRoadbedSide( segPtr, segCnt, segInx, side, roadbedWidth ); + if (res == 0L) { + } else if (res == 0xFFFFFFFF) { + DrawTurnoutRoadbedSide( d, color, orig, angle, &segPtr[segInx], side, 0, 32 ); + } else { + for ( b0=0, res1=0x00000001; res1&&(res1&res); b0++,res1<<=1 ); + for ( b1=32,res1=0x80000000; res1&&(res1&res); b1--,res1>>=1 ); + DrawTurnoutRoadbedSide( d, color, orig, angle, &segPtr[segInx], side, 0, b0 ); + DrawTurnoutRoadbedSide( d, color, orig, angle, &segPtr[segInx], side, b1, 32 ); + } +} + + +static void DrawTurnoutRoadbed( + drawCmd_p d, + wDrawColor color, + coOrd orig, + ANGLE_T angle, + trkSeg_p segPtr, + int segCnt ) +{ + int inx, trkCnt=0, segInx=0; + for (inx=0;inx<segCnt;inx++) { + if ( IsSegTrack(&segPtr[inx]) ) { + segInx = inx; + trkCnt++; + if (trkCnt>1) + break; + } + } + if (trkCnt==0) + return; + if (trkCnt == 1) { + DrawTurnoutRoadbedSide( d, color, orig, angle, &segPtr[segInx], +90, 0, 32 ); + DrawTurnoutRoadbedSide( d, color, orig, angle, &segPtr[segInx], -90, 0, 32 ); + } else { + for (inx=0;inx<segCnt;inx++) { + if ( IsSegTrack(&segPtr[inx]) ) { + ComputeAndDrawTurnoutRoadbedSide( d, color, orig, angle, segPtr, segCnt, inx, +90 ); + ComputeAndDrawTurnoutRoadbedSide( d, color, orig, angle, segPtr, segCnt, inx, -90 ); + } + } + } +} + +/**************************************** + * + * HAND LAID TURNOUTS + * + */ + +track_p NewHandLaidTurnout( + coOrd p0, + ANGLE_T a0, + coOrd p1, + ANGLE_T a1, + coOrd p2, + ANGLE_T a2, + ANGLE_T frogA ) +{ + track_p trk; + struct extraData * xx; + trkSeg_t segs[2]; + sprintf( message, "\tHand Laid Turnout, Angle=%0.1f\t", frogA ); + DYNARR_SET( trkEndPt_t, tempEndPts_da, 2 ); + memset( &tempEndPts(0), 0, tempEndPts_da.cnt * sizeof tempEndPts(0) ); + tempEndPts(0).pos = p0; + tempEndPts(0).angle = a0; + tempEndPts(1).pos = p1; + tempEndPts(1).angle = a1; + tempEndPts(2).pos = p2; + tempEndPts(2).angle = a2; + Rotate( &p1, p0, -a0 ); + p1.x -= p0.x; + p1.y -= p0.y; + segs[0].type = SEG_STRTRK; + segs[0].color = wDrawColorBlack; + segs[0].u.l.pos[0] = zero; + segs[0].u.l.pos[1] = p1; + Rotate( &p2, p0, -a0 ); + p2.x -= p0.x; + p2.y -= p0.y; + segs[1].type = SEG_STRTRK; + segs[1].color = wDrawColorBlack; + segs[1].u.l.pos[0] = zero; + segs[1].u.l.pos[1] = p2; + trk = NewCompound( T_TURNOUT, 0, p0, a0, message, 3, &tempEndPts(0), 22, "Normal\0\1\0\0Reverse\0\2\0\0\0", 2, segs ); + xx = GetTrkExtraData(trk); + xx->handlaid = TRUE; + +#ifdef LATER + trk = NewTrack( 0, T_TURNOUT, 3, + sizeof (*xx) + (3-1)*sizeof curTurnout->segs[0] + 1); + xx = GetTrkExtraData(trk); + xx->orig = p0; + xx->angle = a0; + xx->handlaid = TRUE; + xx->descriptionOff = zero; + xx->descriptionSize = zero; + sprintf( message, "\tHand Laid Turnout, Angle=%0.1f\t", frogA ); + xx->title = MyStrdup( message ); + xx->paths = xx->pathCurr = (PATHPTR_T)"Normal\0\1\0\0Reverse\0\2\0\0\0"; + xx->pathLen = 21; + SetTrkEndPoint( trk, 0, p0, a0 ); + SetTrkEndPoint( trk, 1, p1, a1 ); + SetTrkEndPoint( trk, 2, p2, a2 ); + xx->segCnt = 2; + Rotate( &p1, p0, -a0 ); + p1.x -= p0.x; + p1.y -= p0.y; + xx->segs[0].type = SEG_STRTRK; + xx->segs[0].color = wDrawColorBlack; + xx->segs[0].u.l.pos[0] = zero; + xx->segs[0].u.l.pos[1] = p1; + Rotate( &p2, p0, -a0 ); + p2.x -= p0.x; + p2.y -= p0.y; + xx->segs[1].type = SEG_STRTRK; + xx->segs[1].color = wDrawColorBlack; + xx->segs[1].u.l.pos[0] = zero; + xx->segs[1].u.l.pos[1] = p2; + ComputeBoundingBox( trk ); + SetDescriptionOrig( trk ); +#endif + return trk; +} + +/**************************************** + * + * GENERIC FUNCTIONS + * + */ + +static coOrd MapPathPos( + struct extraData * xx, + signed char segInx, + EPINX_T ep ) +{ + trkSeg_p segPtr; + wIndex_t inx; + coOrd pos; + + if ( segInx < 0 ) { + segInx = - segInx; + ep = 1-ep; + } + + for ( inx=0,segPtr=xx->segs; inx<xx->segCnt; inx++,segPtr++ ) { + if ( !IsSegTrack(segPtr) ) continue; + if ( --segInx > 0 ) continue; + pos = GetSegEndPt( segPtr, ep, FALSE, NULL ); + REORIGIN1( pos, xx->angle, xx->orig ); + return pos; + } + fprintf( stderr, "mapPathPos: bad segInx: %d\n", segInx ); + return zero; +} + + +static void DrawTurnout( + track_p trk, + drawCmd_p d, + wDrawColor color ) +{ + struct extraData *xx = GetTrkExtraData(trk); + wIndex_t i; + long widthOptions = 0; + DIST_T scale2rail; + + if (GetTrkWidth(trk) == 2) + widthOptions = DTS_THICK2; + if (GetTrkWidth(trk) == 3) + widthOptions = DTS_THICK3; + scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale; + if ( tieDrawMode!=TIEDRAWMODE_NONE && + d!=&mapD && + (d->options&DC_TIES)!=0 && + d->scale<scale2rail/2 ) + DrawSegsO( d, trk, xx->orig, xx->angle, xx->segs, xx->segCnt, GetTrkGauge(trk), color, widthOptions|DTS_TIES ); + DrawSegsO( d, trk, xx->orig, xx->angle, xx->segs, xx->segCnt, GetTrkGauge(trk), color, widthOptions | DTS_NOCENTER ); // no curve center for turnouts + for (i=0; i<GetTrkEndPtCnt(trk); i++) { + DrawEndPt( d, trk, i, color ); + } + if ( ((d->funcs->options&wDrawOptTemp)==0) && + (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) && + labelScale >= d->scale && + ( GetTrkBits( trk ) & TB_HIDEDESC ) == 0 ) { + DrawCompoundDescription( trk, d, color ); + if (!xx->handlaid) + LabelLengths( d, trk, color ); + } + if ( roadbedWidth > GetTrkGauge(trk) && + ( ((d->options&DC_PRINT) && d->scale <= (twoRailScale*2+1)/2.0) || + (roadbedOnScreen && d->scale <= twoRailScale) ) ) + DrawTurnoutRoadbed( d, color, xx->orig, xx->angle, xx->segs, xx->segCnt ); + +} + + +static void ReadTurnout( + char * line ) +{ + ReadCompound( line+8, T_TURNOUT ); + CheckPaths( tempSegs_da.cnt, &tempSegs(0), pathPtr ); +} + + +static ANGLE_T GetAngleTurnout( + track_p trk, + coOrd pos, + EPINX_T *ep0, + EPINX_T *ep1 ) +{ + struct extraData * xx = GetTrkExtraData(trk); + wIndex_t segCnt, segInx; + ANGLE_T angle; + + if ( ep0 && ep1 ) + *ep0 = *ep1 = PickEndPoint( pos, trk ); + for ( segCnt=0; segCnt<xx->segCnt && IsSegTrack(&xx->segs[segCnt]); segCnt++ ); + pos.x -= xx->orig.x; + pos.y -= xx->orig.y; + Rotate( &pos, zero, -xx->angle ); + angle = GetAngleSegs( segCnt, xx->segs, pos, &segInx ); + return NormalizeAngle( angle+xx->angle ); +} + + +static BOOL_T SplitTurnoutCheckPath( + wIndex_t segInxEnd, + PATHPTR_T pp1, + int dir1, + PATHPTR_T pp2, + int dir2, + trkSeg_p segs, + coOrd epPos ) +{ + wIndex_t segInx1, segInx2; + EPINX_T segEP; + coOrd pos; + DIST_T dist; + + GetSegInxEP( pp2[0], &segInx2, &segEP ); + if ( dir2 < 0 ) segEP = 1-segEP; + pos = GetSegEndPt( &segs[segInx2], segEP, FALSE, NULL ); + dist = FindDistance( pos, epPos ); + if ( dist>connectDistance ) + return TRUE; + while ( pp2[0] ) { + GetSegInxEP( pp1[0], &segInx1, &segEP ); + GetSegInxEP( pp2[0], &segInx2, &segEP ); + if ( segInx1 != segInx2 ) + break; + if ( segInxEnd == segInx2 ) + return TRUE; + pp1 += dir1; + pp2 += dir2; + } + return FALSE; +} + + +static BOOL_T SplitTurnoutCheckEP( + wIndex_t segInx0, + coOrd epPos, + PATHPTR_T pp1, + int dir1, + PATHPTR_T pp, + trkSeg_p segs ) +{ + while ( pp[0] ) { + pp += strlen((char *)pp)+1; + while ( pp[0] ) { + if (!SplitTurnoutCheckPath( segInx0, pp1, dir1, pp, 1, segs, epPos )) + return FALSE; + while ( pp[0] ) + pp++; + if (!SplitTurnoutCheckPath( segInx0, pp1, dir1, pp-1, -1, segs, epPos )) + return FALSE; + pp++; + } + pp++; + } + return TRUE; +} + + +EXPORT EPINX_T TurnoutPickEndPt( + coOrd epPos, + track_p trk ) +{ + struct extraData * xx = GetTrkExtraData(trk); + wIndex_t segCnt, segInx, segInx0; + EPINX_T segEP; + PATHPTR_T cp, cq, pps[2]; + coOrd pos; + DIST_T dist, dists[2]; + int dir; + EPINX_T ep, epCnt, eps[2]; + BOOL_T unique_eps[2]; + + for ( segCnt=0; segCnt<xx->segCnt && IsSegTrack(&xx->segs[segCnt]); segCnt++ ); + DistanceSegs( xx->orig, xx->angle, segCnt, xx->segs, &epPos, &segInx0 ); + Rotate( &epPos, xx->orig, xx->angle ); + epPos.x -= xx->orig.x; + epPos.y -= xx->orig.y; + epCnt = GetTrkEndPtCnt(trk); + cp = xx->paths; + eps[0] = eps[1] = -1; + unique_eps[0] = unique_eps[1] = TRUE; + while ( cp[0] ) { + cp += strlen((char *)cp)+1; + while ( cp[0] ) { + while ( cp[0] ) { + GetSegInxEP( cp[0], &segInx, &segEP ); + if ( segInx == segInx0 ) { + for ( dir=0; dir<2; dir++ ) { + for ( cq=cp; cq[dir?-1:1]; cq += (dir?-1:1) ); + GetSegInxEP( cq[0], &segInx, &segEP ); + if ( dir==0 ) segEP = 1-segEP; + pos = GetSegEndPt( &xx->segs[segInx], segEP, FALSE, NULL ); + dist = FindDistance( pos, epPos ); + if ( eps[dir] < 0 || dist < dists[dir] ) { + dists[dir] = dist; + pos.x += xx->orig.x; + pos.y += xx->orig.y; + Rotate( &pos, xx->orig, xx->angle ); + for ( ep=0; ep<epCnt; ep++ ) { + if ( FindDistance( pos, GetTrkEndPos(trk,ep) ) < connectDistance ) + break; + } + if ( ep<epCnt ) { + if ( eps[dir] >= 0 && eps[dir] != ep ) + unique_eps[dir] = FALSE; + eps[dir] = ep; + dists[dir] = dist; + pps[dir] = cq; + } + } + } + } + cp++; + } + cp++; + } + cp++; + } + + for ( dir=0; dir<2; dir++ ) { + if ( unique_eps[dir] && eps[dir] >= 0 ) { + GetSegInxEP( pps[dir][0], &segInx, &segEP ); + if ( dir == 0 ) segEP = 1-segEP; + epPos = GetSegEndPt( &xx->segs[segInx], segEP, FALSE, NULL ); + if ( ! SplitTurnoutCheckEP( segInx0, epPos, pps[dir], dir?1:-1, xx->paths, xx->segs ) ) + unique_eps[dir] = FALSE; + } + } + + if ( unique_eps[0] == unique_eps[1] ) { + if ( eps[0] >= 0 && eps[1] >= 0 ) + return ( dists[0] < dists[1] ) ? eps[0] : eps[1] ; + } + if ( unique_eps[0] && eps[0] >= 0 ) + return eps[0]; + if ( unique_eps[1] && eps[1] >= 0 ) + return eps[1]; + if ( eps[0] >= 0 && eps[1] >= 0 ) + return ( dists[0] < dists[1] ) ? eps[0] : eps[1] ; + return eps[0] >= 0 ? eps[0] : eps[1] ; +} + + +static PATHPTR_T splitTurnoutPath; +static PATHPTR_T splitTurnoutRoot; +static int splitTurnoutDir; + +static void SplitTurnoutCheckEndPt( + PATHPTR_T path, + int dir, + trkSeg_p segs, + coOrd epPos, + coOrd splitPos ) +{ + PATHPTR_T path0; + wIndex_t segInx; + EPINX_T segEP; + coOrd pos; + DIST_T dist, minDist; + + path0 = path; + GetSegInxEP( path[0], &segInx, &segEP ); + if ( dir < 0 ) segEP = 1-segEP; + pos = GetSegEndPt( &segs[segInx], segEP, FALSE, NULL ); + dist = FindDistance( pos, epPos ); + if ( dist>connectDistance ) + return; + minDist = trackGauge; + while ( path[0] ) { + GetSegInxEP( path[0], &segInx, &segEP ); + if ( dir < 0 ) segEP = 1-segEP; + pos = splitPos; + dist = DistanceSegs( zero, 0.0, 1, &segs[segInx], &pos, NULL ); + if ( dist < minDist ) { + minDist = dist; + splitTurnoutPath = path; + splitTurnoutDir = -dir; + splitTurnoutRoot = path0; + } + path += dir; + } +} + + +static BOOL_T SplitTurnout( + track_p trk, + coOrd pos, + EPINX_T ep, + track_p *leftover, + EPINX_T * ep0, + EPINX_T * ep1 ) +{ + struct extraData * xx = GetTrkExtraData( trk ); + wIndex_t segInx0, segInx, segCnt; + EPINX_T segEP, epCnt, ep2=0, epN; + PATHPTR_T pp, pp1, pp2; + unsigned char c; + char * cp; + int negCnt, posCnt, pathCnt, dir; + segProcData_t segProcDataSplit; + segProcData_t segProcDataNewTrack; + track_p trk2=NULL; + static dynArr_t segIndexMap_da; +#define segIndexMap(N) DYNARR_N( int, segIndexMap_da, N ) + static dynArr_t newPath_da; +#define newPath(N) DYNARR_N( char, newPath_da, N ) + coOrd orig, size, epPos; + ANGLE_T epAngle; + PATHPTR_T path; + int s0, s1; + trkSeg_t newSeg; + + if ( (MyGetKeyState()&WKEY_SHIFT) == 0 ) { + ErrorMessage( MSG_CANT_SPLIT_TRK, _("Turnout") ); + return FALSE; + } + + /* + * 1. Find segment on path that ends at 'ep' + */ + epCnt = GetTrkEndPtCnt(trk); + epPos = GetTrkEndPos( trk, ep ); + for ( segCnt=0; segCnt<xx->segCnt && IsSegTrack(&xx->segs[segCnt]); segCnt++ ); + Rotate( &pos, xx->orig, -xx->angle ); + pos.x -= xx->orig.x; + pos.y -= xx->orig.y; + Rotate( &epPos, xx->orig, -xx->angle ); + epPos.x -= xx->orig.x; + epPos.y -= xx->orig.y; + splitTurnoutPath = NULL; + pp = xx->paths; + while ( pp[0] ) { + pp += strlen((char *)pp)+1; + while ( pp[0] ) { + SplitTurnoutCheckEndPt( pp, 1, xx->segs, epPos, pos ); + if ( splitTurnoutPath != NULL ) + goto foundSeg; + while ( pp[0] ) + pp++; + SplitTurnoutCheckEndPt( pp-1, -1, xx->segs, epPos, pos ); + if ( splitTurnoutPath != NULL ) + goto foundSeg; + pp++; + } + pp++; + } + ErrorMessage( _("splitTurnout: can't find segment") ); + return FALSE; +foundSeg: + + /* + * 2a. Check that all other paths thru found segment are the same + */ + GetSegInxEP( splitTurnoutPath[0], &segInx0, &segEP ); + pp = xx->paths; + pathCnt = 0; + while ( pp[0] ) { + pp += strlen((char *)pp)+1; + while ( pp[0] ) { + while ( pp[0] ) { + GetSegInxEP( pp[0], &segInx, &segEP ); + if ( segInx == segInx0 ) { + pp1 = splitTurnoutPath; + pp2 = pp; + dir = (pp2[0]>0?1:-1) * splitTurnoutDir; + while ( pp1[0] && pp2[0] ) { + if ( splitTurnoutDir * pp1[0] != dir * pp2[0] ) + break; + pp1 += splitTurnoutDir; + pp2 += dir; + } + if ( pp1[0]!='\0' || pp2[0]!='\0' ) { + ErrorMessage( MSG_SPLIT_POS_BTW_MERGEPTS ); + return FALSE; + } + } + pp++; + } + pp++; + } + pp++; + } + + /* + * 2b. Check that all paths from ep pass thru segInx0 + */ + if ( !SplitTurnoutCheckEP( segInx0, epPos, splitTurnoutRoot, -splitTurnoutDir, xx->paths, xx->segs ) ) { + ErrorMessage( MSG_SPLIT_PATH_NOT_UNIQUE ); + return FALSE; + } + + + /* + * 3. Split the found segment. + */ + segProcDataSplit.split.pos = pos; + s0 = (splitTurnoutPath[0] > 0) != (splitTurnoutDir > 0); + s1 = 1-s0; + SegProc( SEGPROC_SPLIT, xx->segs+segInx0, &segProcDataSplit ); + if ( segProcDataSplit.split.length[s1] <= minLength ) { + if ( splitTurnoutPath[splitTurnoutDir] == '\0' ) + return FALSE; + segProcDataSplit.split.length[s0] += segProcDataSplit.split.length[s1]; + segProcDataSplit.split.length[s1] = 0; + segProcDataSplit.split.newSeg[s0] = xx->segs[segInx0]; + epPos = GetSegEndPt( &segProcDataSplit.split.newSeg[s0], s1, FALSE, &epAngle ); + } else if ( segProcDataSplit.split.length[s0] <= minLength ) { + segProcDataSplit.split.length[s1] += segProcDataSplit.split.length[s0]; + segProcDataSplit.split.length[s0] = 0; + segProcDataSplit.split.newSeg[s1] = xx->segs[segInx0]; + epPos = GetSegEndPt( &segProcDataSplit.split.newSeg[s1], s0, FALSE, &epAngle ); + epAngle += 180.0; + } else { + epPos = GetSegEndPt( &segProcDataSplit.split.newSeg[s1], s0, FALSE, &epAngle ); + epAngle += 180.0; + } +#ifdef LATER + if ( segProcDataSplit.split.length[s1] <= minLength && splitTurnoutPath[1] == '\0' ) + return FALSE; +#endif + + /* + * 4. Map the old segments to new + */ + DYNARR_SET( int, segIndexMap_da, xx->segCnt ); + for ( segInx=0; segInx<xx->segCnt; segInx++ ) + segIndexMap(segInx) = segInx+1; + pp = splitTurnoutPath; + if ( segProcDataSplit.split.length[s0] > minLength ) + pp += splitTurnoutDir; + negCnt = 0; + while ( *pp ) { + GetSegInxEP( *pp, &segInx, &segEP ); + segIndexMap(segInx) = - segIndexMap(segInx); + negCnt++; + pp += splitTurnoutDir; + } + for ( segInx=posCnt=0; segInx<xx->segCnt; segInx++ ) { + if ( segIndexMap(segInx) > 0 ) + segIndexMap(segInx) = ++posCnt; + } + DYNARR_SET( trkSeg_t, tempSegs_da, posCnt ); + for ( segInx=posCnt=0; segInx<xx->segCnt; segInx++ ) { + if ( segIndexMap(segInx) > 0 ) { + if ( segInx == segInx0 ) { + tempSegs(segIndexMap(segInx)-1) = segProcDataSplit.split.newSeg[s0]; + } else { + tempSegs(segIndexMap(segInx)-1) = xx->segs[segInx]; + } + } + } + + /* + * 5. Remap paths by removing trailing segments + */ + DYNARR_SET( char, newPath_da, xx->pathLen ); + pp = xx->paths; + pp1 = (PATHPTR_T)&newPath(0); + while ( *pp ) { + strcpy( (char *)pp1, (char *)pp ); + pp += strlen( (char *)pp )+1; + pp1 += strlen( (char *)pp1 )+1; + while ( *pp ) { + while ( *pp ) { + GetSegInxEP( *pp, &segInx, &segEP ); + if ( segIndexMap(segInx) > 0 ) { + c = segIndexMap(segInx); + if ( *pp<0 ) + c = -c; + *pp1++ = c; + } + pp++; + } + *pp1++ = '\0'; + pp++; + } + *pp1++ = '\0'; + pp++; + } + *pp1++ = '\0'; + + /* + * 6. Reorigin segments + */ + GetSegBounds( zero, 0, tempSegs_da.cnt, &tempSegs(0), &orig, &size ); + orig.x = -orig.x; + orig.y = -orig.y; + MoveSegs( tempSegs_da.cnt, &tempSegs(0), orig ); + epPos.x += orig.x; + epPos.y += orig.y; + cp = strchr( xx->title, '\t' ); + if ( cp ) { + if ( strncmp( cp+1, "Split ", 6 ) != 0 ) { + memcpy( message, xx->title, cp-xx->title+1 ); + strcpy( message+(cp-xx->title+1), "Split " ); + strcat( message, cp+1 ); + } else { + strcpy( message, xx->title ); + } + } else { + sprintf( message, "Split %s", xx->title ); + } + + /* + * 7. Convert trailing segments to new tracks + */ + path = splitTurnoutPath; + if ( segProcDataSplit.split.length[s1] < minLength ) + path += splitTurnoutDir; + while ( path[0] ) { + GetSegInxEP( path[0], &segInx, &segEP ); + s0 = (path[0] > 0) != (splitTurnoutDir > 0); + if ( segInx0 != segInx ) { + newSeg = xx->segs[segInx]; + } else { + newSeg = segProcDataSplit.split.newSeg[s1]; + } + MoveSegs( 1, &newSeg, xx->orig ); + RotateSegs( 1, &newSeg, xx->orig, xx->angle ); + SegProc( SEGPROC_NEWTRACK, &newSeg, &segProcDataNewTrack ); + if ( *leftover == NULL ) { + *ep0 = segProcDataNewTrack.newTrack.ep[s0]; + *leftover = trk2 = segProcDataNewTrack.newTrack.trk; + ep2 = 1-*ep0; + } else { + epN = segProcDataNewTrack.newTrack.ep[s0]; + ConnectTracks( trk2, ep2, segProcDataNewTrack.newTrack.trk, epN ); + trk2 = segProcDataNewTrack.newTrack.trk; + ep2 = 1-epN; + } + path += splitTurnoutDir; + } + + /* + * 8. Replace segments, paths, and endPt in original turnout + */ + xx->split = TRUE; + Rotate( &orig, zero, xx->angle ); + xx->orig.x -= orig.x; + xx->orig.y -= orig.y; + xx->segCnt = tempSegs_da.cnt; + xx->segs = (trkSeg_p)memdup( &tempSegs(0), tempSegs_da.cnt * sizeof tempSegs(0) ); + CloneFilledDraw( xx->segCnt, xx->segs, TRUE ); + xx->pathLen = pp1-(PATHPTR_T)&newPath(0); + xx->pathCurr = xx->paths = memdup( &newPath(0), xx->pathLen ); + epAngle = NormalizeAngle( xx->angle+epAngle ); + epPos.x += xx->orig.x; + epPos.y += xx->orig.y; + Rotate( &epPos, xx->orig, xx->angle ); + SetTrkEndPoint( trk, ep, epPos, epAngle ); + ComputeCompoundBoundingBox( trk ); + + return TRUE; +} + + +static BOOL_T CheckTraverseTurnout( + track_p trk, + coOrd pos ) +{ + struct extraData * xx = GetTrkExtraData(trk); + coOrd pos1; +#ifdef LATER + int inx, foundInx = 0; + DIST_T d, foundD; +#endif + DIST_T d; + PATHPTR_T pathCurr; + int segInx; + EPINX_T segEP; + +LOG( log_traverseTurnout, 1, ( "CheckTraverseTurnout( T%d, [%0.3f %0.3f])\n", GetTrkIndex(trk), pos.x, pos.y ) ) + Rotate( &pos, xx->orig, -xx->angle ); + pos.x -= xx->orig.x; + pos.y -= xx->orig.y; +LOG( log_traverseTurnout, 1, ( "After rotation = [%0.3f %0.3f])\n", pos.x, pos.y ) ) + +#ifdef LATER + for ( inx=0; inx<xx->segCnt; inx++ ) { + switch ( xx->segs[inx].type ) { + case SEG_STRTRK: + case SEG_CRVTRK: + pos1 = GetSegEndPt( &xx->segs[inx], 0, FALSE, NULL ); + d = FindDistance( pos, pos1 ); + if ( foundInx == 0 || d < foundD ) { + foundInx = inx+1; + foundD = d; + } + pos1 = GetSegEndPt( &xx->segs[inx], 1, FALSE, NULL ); + d = FindDistance( pos, pos1 ); + if ( foundInx == 0 || d < foundD ) { + foundInx = -(inx+1); + foundD = d; + } + break; + } + } + if ( foundInx == 0 ) + return FALSE; +#endif + for ( pathCurr = xx->pathCurr+strlen((char*)xx->pathCurr)+1; pathCurr[0] || pathCurr[1]; pathCurr++ ) { +LOG( log_traverseTurnout, 1, ( "P[%d] = %d ", pathCurr-xx->paths, pathCurr[0] ) ) + if ( pathCurr[-1] == 0 ) { + GetSegInxEP( pathCurr[0], &segInx, &segEP ); + pos1 = GetSegEndPt( &xx->segs[segInx], segEP, FALSE, NULL ); + d = FindDistance( pos, pos1 ); +LOG( log_traverseTurnout, 1, ( "d=%0.3f\n", d ) ) + if ( d < connectDistance ) + return TRUE; + } + if ( pathCurr[1] == 0 ) { + GetSegInxEP( pathCurr[0], &segInx, &segEP ); + pos1 = GetSegEndPt( &xx->segs[segInx], 1-segEP, FALSE, NULL ); + d = FindDistance( pos, pos1 ); +LOG( log_traverseTurnout, 1, ( "d=%0.3f\n", d ) ) + if ( d < connectDistance ) + return TRUE; + } + } +LOG( log_traverseTurnout, 1, ( " not found\n" ) ) + return FALSE; +} + + +static BOOL_T TraverseTurnout( + traverseTrack_p trvTrk, + DIST_T * distR ) +{ + track_p trk = trvTrk->trk; + struct extraData * xx = GetTrkExtraData(trk); + coOrd pos0, pos1, pos2; + DIST_T d, dist; + PATHPTR_T path, pathCurr; + BOOL_T backwards=FALSE; + trkSeg_p segPtr; + EPINX_T ep, epCnt, ep2; + int segInx; + EPINX_T segEP; + segProcData_t segProcData; + + d = 10000; + pos0 = trvTrk->pos; + Rotate( &pos0, xx->orig, -xx->angle ); + pos0.x -= xx->orig.x; + pos0.y -= xx->orig.y; + dist = *distR; +LOG( log_traverseTurnout, 1, ( "TraverseTurnout( T%d, [%0.3f %0.3f] [%0.3f %0.3f], A%0.3f, D%0.3f\n", GetTrkIndex(trk), trvTrk->pos.x, trvTrk->pos.y, pos0.x, pos0.y, trvTrk->angle, *distR ) ) + pathCurr = 0; + for ( path = xx->pathCurr+strlen((char*)xx->pathCurr)+1; path[0] || path[1]; path++ ) { + if ( path[0] == 0 ) + continue; + GetSegInxEP( path[0], &segInx, &segEP ); + segPtr = xx->segs+segInx; +#ifdef LATER + for ( inx = 0; inx<xx->segCnt; inx++ ) { + segPtr = xx->segs+inx; +#endif + segProcData.distance.pos1 = pos0; + SegProc( SEGPROC_DISTANCE, segPtr, &segProcData ); + if ( segProcData.distance.dd < d ) { + d = segProcData.distance.dd; + pos2 = segProcData.distance.pos1; + pathCurr = path; + } + } + if ( d > 10 || pathCurr == 0 ) { + ErrorMessage( "traverseTurnout: Not near: %0.3f", d ); + return FALSE; + } +LOG( log_traverseTurnout, 1, ( " PC=%d ", pathCurr[0] ) ) + GetSegInxEP( pathCurr[0], &segInx, &segEP ); + segPtr = xx->segs+segInx; +#ifdef LATER + for ( pathCurr = xx->pathCurr+strlen((char*)xx->pathCurr)+1; pathCurr[0] || pathCurr[1]; pathCurr++ ) { + if ( pathCurr[0] == 0 ) + continue; + if ( Abs(pathCurr[0])-1 == currInx ) + break; + } + if ( pathCurr[0] == 0 ) { + fprintf( stderr, "Open turnout [%d]\n", currInx ); + return FALSE; + } + segPtr = xx->segs+currInx; +#endif + segProcData.traverse1.pos = pos2; + segProcData.traverse1.angle = xx->angle-trvTrk->angle; + SegProc( SEGPROC_TRAVERSE1, segPtr, &segProcData ); + dist += segProcData.traverse1.dist; + backwards = segProcData.traverse1.backwards; + if ( segEP ) backwards = !backwards; +LOG( log_traverseTurnout, 2, ( " B%d D%0.3f\n", backwards, dist ) ) + + while ( *pathCurr ) { + GetSegInxEP( pathCurr[0], &segInx, &segEP ); + segPtr = xx->segs+segInx; + segProcData.traverse2.segDir = (backwards?1-segEP:segEP); + segProcData.traverse2.dist = dist; + SegProc( SEGPROC_TRAVERSE2, segPtr, &segProcData ); + if ( segProcData.traverse2.dist <= 0 ) { + *distR = 0; + REORIGIN( trvTrk->pos, segProcData.traverse2.pos, xx->angle, xx->orig ); + trvTrk->angle = NormalizeAngle( xx->angle+segProcData.traverse2.angle ); + return TRUE; + } + dist = segProcData.traverse2.dist; + pathCurr += (backwards?-1:1); +LOG( log_traverseTurnout, 1, ( " D%0.3f\n", dist ) ) + } + + pathCurr += (backwards?1:-1); + pos1 = MapPathPos( xx, pathCurr[0], (backwards?0:1) ); + *distR = dist; + epCnt = GetTrkEndPtCnt(trk); + ep = 0; + dist = FindDistance( pos1, GetTrkEndPos(trk,0) ); + for ( ep2=1; ep2<epCnt; ep2++ ) { + d = FindDistance( pos1, GetTrkEndPos(trk,ep2) ); + if ( d < dist ) { + dist = d; + ep = ep2; + } + } + if ( dist > connectDistance ) { + trk = NULL; + trvTrk->pos = pos1; + } else { + trvTrk->pos = GetTrkEndPos( trk, ep ); + trvTrk->angle = GetTrkEndAngle( trk, ep ); + trk = GetTrkEndTrk( trk, ep ); + } + dist = FindDistance( trvTrk->pos, pos1 ); +LOG( log_traverseTurnout, 1, ( " -> [%0.3f %0.3f] A%0.3f D%0.3f\n", trvTrk->pos.x, trvTrk->pos.y, trvTrk->angle, *distR ) ) + trvTrk->trk = trk; + return TRUE; +} + + +static STATUS_T ModifyTurnout( track_p trk, wAction_t action, coOrd pos ) +{ + struct extraData *xx; + static EPINX_T ep; + DIST_T d; + + xx = GetTrkExtraData(trk); + if ( xx->special == TOadjustable ) { + switch ( action ) { + case C_DOWN: + ep = PickUnconnectedEndPoint( pos, trk ); + if (ep == -1) + return C_ERROR; + UndrawNewTrack( trk ); + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).width = 0; + tempSegs(0).u.l.pos[0] = GetTrkEndPos( trk, 1-ep ); + tempSegs_da.cnt = 1; + InfoMessage( _("Drag to change track length") ); + + case C_MOVE: + d = FindDistance( tempSegs(0).u.l.pos[0], pos ); + if ( d < xx->u.adjustable.minD ) + d = xx->u.adjustable.minD; + else if ( d > xx->u.adjustable.maxD ) + d = xx->u.adjustable.maxD; + Translate( &tempSegs(0).u.l.pos[1], tempSegs(0).u.l.pos[0], GetTrkEndAngle( trk, ep ), d ); + tempSegs_da.cnt = 1; + if (action == C_MOVE) + InfoMessage( _("Length=%s"), FormatDistance( d ) ); + return C_CONTINUE; + + case C_UP: + d = FindDistance( tempSegs(0).u.l.pos[0],tempSegs(0).u.l.pos[1] ); + ChangeAdjustableEndPt( trk, ep, d ); + return C_TERMINATE; + + default: + ; + } + } + return ExtendStraightFromOrig( trk, action, pos ); +} + + +static BOOL_T GetParamsTurnout( int inx, track_p trk, coOrd pos, trackParams_t * params ) +{ + params->type = curveTypeStraight; + params->ep = PickUnconnectedEndPoint( pos, trk ); + if (params->ep == -1) + return FALSE; + params->lineOrig = GetTrkEndPos(trk,params->ep); + params->lineEnd = params->lineOrig; + params->len = 0.0; + params->angle = GetTrkEndAngle(trk,params->ep); + params->arcR = 0.0; + return TRUE; +} + + +static BOOL_T MoveEndPtTurnout( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d0 ) +{ + ANGLE_T angle0; + DIST_T d; + track_p trk1; + + angle0 = GetTrkEndAngle(*trk,*ep); + d = FindDistance( GetTrkEndPos(*trk,*ep), pos); + if (d0 > 0.0) { + d -= d0; + if (d < 0.0) { + ErrorMessage( MSG_MOVED_BEFORE_END_TURNOUT ); + return FALSE; + } + Translate( &pos, pos, angle0+180, d0 ); + } + if (d > minLength) { + trk1 = NewStraightTrack( GetTrkEndPos(*trk,*ep), pos ); + CopyAttributes( *trk, trk1 ); + ConnectTracks( *trk, *ep, trk1, 0 ); + *trk = trk1; + *ep = 1; + DrawNewTrack( *trk ); + } + return TRUE; +} + + +static BOOL_T QueryTurnout( track_p trk, int query ) +{ + switch ( query ) { + case Q_IGNORE_EASEMENT_ON_EXTEND: + case Q_DRAWENDPTV_1: + case Q_CAN_GROUP: + case Q_ISTRACK: + case Q_NOT_PLACE_FROGPOINTS: + case Q_HAS_DESC: + case Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK: + return TRUE; + case Q_CAN_PARALLEL: + if( GetTrkEndPtCnt( trk ) == 2 && fabs( GetTrkEndAngle( trk, 0 ) - GetTrkEndAngle( trk, 1 )) == 180.0 ) + return TRUE; + else + return FALSE; + case Q_CAN_NEXT_POSITION: + return ( GetTrkEndPtCnt(trk) > 2 ); + default: + return FALSE; + } +} + + +EXPORT int doDrawTurnoutPosition = 1; +static wIndex_t drawTurnoutPositionWidth=3; +static void DrawTurnoutPositionIndicator( + track_p trk, + wDrawColor color ) +{ + struct extraData * xx = GetTrkExtraData(trk); + PATHPTR_T path = xx->pathCurr; + coOrd pos0, pos1; + + if ( xx->pathCurr == xx->paths ) { + for ( path=xx->pathCurr+strlen((char *)xx->pathCurr); path[0] || path[1]; path++ ); + if ( path[2] == 0 ) + return; + } + for ( path=xx->pathCurr+strlen((char *)xx->pathCurr); path[0] || path[1]; path++ ) { + if ( path[0] == 0 ) { + pos0 = MapPathPos( xx, path[1], 0 ); + } else if ( path[1] == 0 ) { + pos1 = MapPathPos( xx, path[0], 1 ); + DrawLine( &mainD, pos0, pos1, drawTurnoutPositionWidth, color ); + } + } +} + + +EXPORT void AdvanceTurnoutPositionIndicator( + track_p trk, + coOrd pos, + coOrd *posR, + ANGLE_T *angleR ) +{ + struct extraData * xx = GetTrkExtraData(trk); + PATHPTR_T path; + traverseTrack_t trvtrk; + DIST_T dist; + + if ( GetTrkType(trk) != T_TURNOUT ) + AbortProg( "nextTurnoutPosition" ); + + DrawTurnoutPositionIndicator( trk, wDrawColorWhite ); + path = xx->pathCurr; + path += strlen((char *)path)+1; + while ( path[0] || path[1] ) + path++; + path += 2; + if ( *path == 0 ) + path = xx->paths; + xx->pathCurr = path; + DrawTurnoutPositionIndicator( trk, selectedColor ); + if ( angleR == NULL || posR == NULL ) + return; + trvtrk.trk = trk; + trvtrk.length = 0; + trvtrk.dist = 0; + trvtrk.pos = *posR; + trvtrk.angle = *angleR; + dist = 0; + if ( !TraverseTurnout( &trvtrk, &dist ) ) + return; + if ( NormalizeAngle( trvtrk.angle-*angleR+90.0 ) > 180 ) + trvtrk.angle = NormalizeAngle( trvtrk.angle+180.0 ); + *posR = trvtrk.pos; + *angleR = trvtrk.angle; +} + +/** + * Create a parallel track for a turnout. + * + * + * \param trk IN existing track + * \param pos IN ?? + * \param sep IN distance between existing and new track + * \param newTrk OUT new track piece + * \param p0R OUT starting point of new piece + * \param p1R OUT ending point of new piece + * \return always TRUE + */ + +static BOOL_T MakeParallelTurnout( + track_p trk, + coOrd pos, + DIST_T sep, + track_p * newTrk, + coOrd * p0R, + coOrd * p1R ) +{ + ANGLE_T angle = GetTrkEndAngle(trk,1); + struct extraData *xx, *yy; + coOrd *endPts; + trkEndPt_p endPt; + int i; + int option; + DIST_T d; + + if ( NormalizeAngle( FindAngle( GetTrkEndPos(trk,0), pos ) - GetTrkEndAngle(trk,1) ) < 180.0 ) + angle += 90; + else + angle -= 90; + + /* + * get all endpoints of current piece and translate them for the new piece + */ + endPts = MyMalloc( GetTrkEndPtCnt( trk ) * sizeof( coOrd )); + for( i = 0; i < GetTrkEndPtCnt( trk ); i++) { + Translate( &(endPts[ i ]), GetTrkEndPos( trk, i ), angle, sep ); + } + + /* + * get information about the current piece and copy data + */ + + if( newTrk ) { + endPt = MyMalloc( GetTrkEndPtCnt( trk ) * sizeof( trkEndPt_t )); + endPt[ 0 ].pos = endPts[ 0 ]; + endPt[ 0 ].angle = GetTrkEndAngle( trk, 0 ); + endPt[ 1 ].pos = endPts[ 1 ]; + endPt[ 1 ].angle = GetTrkEndAngle( trk, 1 ); + + yy = GetTrkExtraData(trk); + + *newTrk = NewCompound( T_TURNOUT, 0, endPt[ 0 ].pos, endPt[ 0 ].angle + 90.0, yy->title, 2, endPt, yy->pathLen, (char *)yy->paths, yy->segCnt, yy->segs ); + xx = GetTrkExtraData(*newTrk); + xx->customInfo = yy->customInfo; + + /* if (connection((int)curTurnoutEp).trk) { + CopyAttributes( connection((int)curTurnoutEp).trk, newTrk ); + SetTrkScale( newTrk, curScaleInx ); + } */ + xx->special = yy->special; + xx->u = yy->u; + + SetDescriptionOrig( *newTrk ); + xx->descriptionOff = zero; + xx->descriptionSize = zero; + + SetTrkElev(*newTrk, GetTrkElevMode(trk), GetTrkElev(trk)); + GetTrkEndElev( trk, 0, &option, &d ); + SetTrkEndElev( *newTrk, 0, option, d, NULL ); + GetTrkEndElev( trk, 1, &option, &d ); + SetTrkEndElev( *newTrk, 1, option, d, NULL ); + + MyFree( endPt ); + } else { + /* draw some temporary track while command is in process */ + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).width = 0; + tempSegs_da.cnt = 1; + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).u.l.pos[0] = endPts[ 0 ]; + tempSegs(0).u.l.pos[1] = endPts[ 1 ]; + } + + if ( p0R ) *p0R = endPts[ 0 ]; + if ( p1R ) *p1R = endPts[ 1 ]; + + MyFree( endPts ); + return TRUE; +} + +static trackCmd_t turnoutCmds = { + N_("TURNOUT "), + DrawTurnout, + DistanceCompound, + DescribeCompound, + DeleteCompound, + WriteCompound, + ReadTurnout, + MoveCompound, + RotateCompound, + RescaleCompound, + NULL, + GetAngleTurnout, + SplitTurnout, + TraverseTurnout, + EnumerateCompound, + NULL, /*redraw*/ + NULL, /*trim*/ + NULL, /*merge*/ + ModifyTurnout, + NULL, /* getLength */ + GetParamsTurnout, + MoveEndPtTurnout, + QueryTurnout, + UngroupCompound, + FlipCompound, + DrawTurnoutPositionIndicator, + AdvanceTurnoutPositionIndicator, + CheckTraverseTurnout, + MakeParallelTurnout }; + + +#ifdef TURNOUTCMD +/***************************************** + * + * Turnout Dialog + * + */ + +static coOrd maxTurnoutDim; + +static void AddTurnout( void ); + + +static wWin_p turnoutW; + + +static void RescaleTurnout( void ) +{ + DIST_T xscale, yscale; + wPos_t ww, hh; + DIST_T w, h; + wDrawGetSize( turnoutD.d, &ww, &hh ); + w = ww/turnoutD.dpi; + h = hh/turnoutD.dpi; + xscale = maxTurnoutDim.x/w; + yscale = maxTurnoutDim.y/h; + turnoutD.scale = max(xscale,yscale); + if (turnoutD.scale == 0.0) + turnoutD.scale = 1.0; + turnoutD.size.x = w*turnoutD.scale; + turnoutD.size.y = h*turnoutD.scale; + return; +} + + +static void TurnoutChange( long changes ) +{ + static char * lastScaleName = NULL; + if (turnoutW == NULL) + return; + wListSetIndex( turnoutListL, 0 ); + if ( (!wWinIsVisible(turnoutW)) || + ( ((changes&CHANGE_SCALE) == 0 || lastScaleName == curScaleName) && + (changes&CHANGE_PARAMS) == 0 ) ) + return; + lastScaleName = curScaleName; + curTurnout = NULL; + curTurnoutEp = 0; + wControlShow( (wControl_p)turnoutListL, FALSE ); + wListClear( turnoutListL ); + maxTurnoutDim.x = maxTurnoutDim.y = 0.0; + if (turnoutInfo_da.cnt <= 0) + return; + curTurnout = TurnoutAdd( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, curScaleInx, turnoutListL, &maxTurnoutDim, -1 ); + wListSetIndex( turnoutListL, 0 ); + wControlShow( (wControl_p)turnoutListL, TRUE ); + if (curTurnout == NULL) { + wDrawClear( turnoutD.d ); + return; + } + turnoutD.orig.x = -trackGauge; + turnoutD.orig.y = -trackGauge; + maxTurnoutDim.x += 2*trackGauge; + maxTurnoutDim.y += 2*trackGauge; + /*RescaleTurnout();*/ + RedrawTurnout(); + return; +} + +static void RedrawTurnout() +{ + coOrd p, s; + RescaleTurnout(); +LOG( log_turnout, 2, ( "SelTurnout(%s)\n", (curTurnout?curTurnout->title:"<NULL>") ) ) + + wDrawClear( turnoutD.d ); + if (curTurnout == NULL) { + return; + } + turnoutD.orig.x = curTurnout->orig.x - trackGauge; + turnoutD.orig.y = (curTurnout->size.y + curTurnout->orig.y) - turnoutD.size.y + trackGauge; + DrawSegs( &turnoutD, zero, 0.0, curTurnout->segs, curTurnout->segCnt, + trackGauge, wDrawColorBlack ); + curTurnoutEp = 0; + p.x = curTurnout->endPt[0].pos.x - trackGauge; + p.y = curTurnout->endPt[0].pos.y - trackGauge; + s.x = s.y = trackGauge*2.0 /*+ turnoutD.minSize*/; + DrawHilight( &turnoutD, p, s ); +} + + +static void TurnoutOk( void ) +{ + AddTurnout(); + Reset(); +} + + +static void TurnoutDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + turnoutInfo_t * to; + if ( inx != I_LIST ) return; + to = (turnoutInfo_t*)wListGetItemContext( (wList_p)pg->paramPtr[inx].control, (wIndex_t)*(long*)valueP ); + AddTurnout(); + curTurnout = to; + RedrawTurnout(); +/* ParamDialogOkActive( &turnoutPG, FALSE ); */ +} + + +static wIndex_t TOpickEndPoint( + coOrd p, + turnoutInfo_t *to ) +{ + wIndex_t inx, i; + DIST_T d, dd; + coOrd posI; + + d = FindDistance( p, to->endPt[0].pos ); + inx = 0; + for ( i=1; i<to->endCnt; i++ ) { + posI = to->endPt[i].pos; + if ((dd=FindDistance(p, posI)) < d) { + d = dd; + inx = i; + } + } + return inx; +} + + +static void HilightEndPt( void ) +{ + coOrd p, s; + p.x = curTurnout->endPt[(int)curTurnoutEp].pos.x - trackGauge; + p.y = curTurnout->endPt[(int)curTurnoutEp].pos.y - trackGauge; + s.x = s.y = trackGauge*2.0 /*+ turnoutD.minSize*/; + DrawHilight( &turnoutD, p, s ); +} + + +static void SelTurnoutEndPt( + wIndex_t action, + coOrd pos ) +{ + if (action != C_DOWN) return; + + HilightEndPt(); + curTurnoutEp = TOpickEndPoint( pos, curTurnout ); + HilightEndPt(); +LOG( log_turnout, 3, (" selected (action=%d) %ld\n", action, curTurnoutEp ) ) +} +#endif + +/**************************************** + * + * GRAPHICS COMMANDS + * + */ + +/* + * STATE INFO + */ +static struct { + int state; + coOrd pos; + coOrd place; + track_p trk; + ANGLE_T angle; + coOrd rot0, rot1; + } Dto; + + +typedef struct { + DIST_T off; + ANGLE_T angle; + EPINX_T ep; + } vector_t; + +static void PlaceTurnoutTrial( + track_p *trkR, + coOrd *posR, + ANGLE_T *angle1R, + ANGLE_T *angle2R, + int *connCntR, + DIST_T *maxDR, + vector_t *v ) +{ + coOrd pos = *posR; + ANGLE_T aa; + ANGLE_T angle; + EPINX_T ep0, ep1; + track_p trk, trk1; + coOrd epPos, conPos, posI; + ANGLE_T epAngle; + int i, connCnt = 0; + DIST_T d, maxD = 0; + + if ( (*trkR = trk = OnTrack( &pos, FALSE, TRUE )) != NULL && + !QueryTrack(trk,Q_CANNOT_PLACE_TURNOUT) && + (ep0 = PickEndPoint( pos, trk )) >= 0 && + ! ( GetTrkType(trk) == T_TURNOUT && + (trk1=GetTrkEndTrk(trk,ep0)) && + GetTrkType(trk1) == T_TURNOUT) && + ! GetLayerFrozen(GetTrkLayer(trk)) ) { + epPos = GetTrkEndPos( trk, ep0 ); + d = FindDistance( pos, epPos ); + if (d <= minLength) + pos = epPos; + if ( GetTrkType(trk) == T_TURNOUT ) { + ep0 = ep1 = PickEndPoint( pos, trk ); + angle = GetTrkEndAngle( trk, ep0 ); + } else { + angle = GetAngleAtPoint( trk, pos, &ep0, &ep1 ); + } + angle = NormalizeAngle( angle + 180.0 ); + if ( NormalizeAngle( FindAngle( pos, *posR ) - angle ) < 180.0 && ep0 != ep1 ) + angle = NormalizeAngle( angle + 180 ); + *angle2R = angle; + epPos = curTurnout->endPt[(int)curTurnoutEp].pos; + *angle1R = angle = NormalizeAngle( angle - curTurnout->endPt[(int)curTurnoutEp].angle ); + Rotate( &epPos, zero, angle ); + pos.x -= epPos.x; + pos.y -= epPos.y; + *posR = pos; +LOG( log_turnout, 3, ( "placeTurnout T%d (%0.3f %0.3f) A%0.3f\n", + GetTrkIndex(trk), pos.x, pos.y, angle ) ) + /*InfoMessage( "Turnout(%d): Angle=%0.3f", GetTrkIndex(trk), angle );*/ + + for (i=0;i<curTurnout->endCnt;i++) { + posI = curTurnout->endPt[i].pos; + epPos = AddCoOrd( pos, posI, angle ); + epAngle = NormalizeAngle( curTurnout->endPt[i].angle + angle ); + conPos = epPos; + if ((trk = OnTrack(&conPos, FALSE, TRUE)) != NULL && + !GetLayerFrozen(GetTrkLayer(trk))) { + v->off = FindDistance( epPos, conPos ); + v->angle = FindAngle( epPos, conPos ); + if ( GetTrkType(trk) == T_TURNOUT ) { + ep0 = ep1 = PickEndPoint( conPos, trk ); + aa = GetTrkEndAngle( trk, ep0 ); + } else { + aa = GetAngleAtPoint( trk, conPos, &ep0, &ep1 ); + } + v->ep = i; + aa = NormalizeAngle( aa - epAngle + connectAngle/2.0 ); + if ( IsClose(v->off) && + ( aa<connectAngle || ( aa>180.0 && aa<180.0+connectAngle ) ) && + ! ( GetTrkType(trk) == T_TURNOUT && + (trk1=GetTrkEndTrk(trk,ep0)) && + GetTrkType(trk1) == T_TURNOUT ) ) { + if (v->off > maxD) + maxD = v->off; + connCnt++; + v++; + } + } + } + } + *connCntR = connCnt; + *maxDR = maxD; +} + + +static void PlaceTurnout( + coOrd pos ) +{ + coOrd p, pos1, pos2; + track_p trk1, trk2; + ANGLE_T a, a1, a2, a3; + int i, connCnt1, connCnt2; + DIST_T d, maxD1, maxD2, sina; + vector_t *V, * maxV; + + static dynArr_t vector_da; +#define vector(N) DYNARR_N( vector_t, vector_da, N ) + + pos1 = Dto.place = Dto.pos = pos; + if (curTurnoutEp >= (long)curTurnout->endCnt) + curTurnoutEp = 0; + DYNARR_SET( vector_t, vector_da, curTurnout->endCnt ); + PlaceTurnoutTrial( &trk1, &pos1, &a1, &a2, &connCnt1, &maxD1, &vector(0) ); + if (connCnt1 > 0) { + Dto.pos = pos1; + Dto.trk = trk1; + Dto.angle = a1; + if ( (MyGetKeyState()&WKEY_SHIFT)==0 && connCnt1 > 1 && maxD1 >= 0.001 ) { + maxV = &vector(0); + for ( i=1; i<connCnt1; i++ ) { + V = &vector(i); + if ( V->off > maxV->off ) { + maxV = V; + } + } + a3 = NormalizeAngle( Dto.angle + curTurnout->endPt[maxV->ep].angle ); + a = NormalizeAngle( a2 - a3 ); + sina = sin(D2R(a)); + if (fabs(sina) > 0.01) { + d = maxV->off/sina; + if (NormalizeAngle( maxV->angle - a3) > 180) + d = -d; + Translate( &pos2, pos, a2, d ); + PlaceTurnoutTrial( &trk2, &pos2, &a2, &a, &connCnt2, &maxD2, &vector(0) ); + if ( connCnt2 >= connCnt1 && maxD2 < maxD1 ) { + Dto.pos = pos2; + Dto.trk = trk2; + Dto.angle = a2; + maxD1 = maxD2; + connCnt1 = connCnt2; + } + } + } + } + if ( connCnt1 > 0 ) { + FormatCompoundTitle( listLabels, curTurnout->title ); + InfoMessage( _("%d connections, max distance %0.3f (%s)"), + connCnt1, PutDim(maxD1), message ); + } else { + Dto.trk = NULL; + FormatCompoundTitle( listLabels, curTurnout->title ); + InfoMessage( _("0 connections (%s)"), message ); + p = curTurnout->endPt[(int)curTurnoutEp].pos; + Rotate( &p, zero, Dto.angle ); + Dto.pos.x = pos.x - p.x; + Dto.pos.y = pos.y - p.y; + } +} + +static void AddTurnout( void ) +{ + track_p newTrk; + track_p trk, trk1; + struct extraData *xx; + coOrd epPos; + DIST_T d; + ANGLE_T a, aa; + EPINX_T ep0, ep1, epx, epy; + wIndex_t i,j; + wIndex_t titleLen; + typedef struct { + track_p trk; + EPINX_T ep; + } junk_t; + static dynArr_t connection_da; + static dynArr_t leftover_da; +#define connection(N) DYNARR_N( junk_t, connection_da, N ) +#define leftover(N) DYNARR_N( junk_t, leftover_da, N ) + BOOL_T visible; + BOOL_T noConnections; + coOrd p0, p1; + + if (Dto.state == 0) + return; + + if (curTurnout->segCnt < 1 || curTurnout->endCnt < 1) { + AbortProg( "addTurnout: bad cnt" ); + } + + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlack ); + UndoStart( _("Place New Turnout"), "addTurnout" ); + titleLen = strlen( curTurnout->title ); +#ifdef LATER + newTrk = NewTrack( 0, T_TURNOUT, curTurnout->endCnt, sizeof (*xx) + 1 ); + xx = GetTrkExtraData(newTrk); + xx->orig = Dto.pos; + xx->angle = Dto.angle; + xx->customInfo = curTurnout->customInfo; + xx->segs = MyMalloc( (curTurnout->segCnt)*sizeof curTurnout->segs[0] ); +#endif + + DYNARR_SET( trkEndPt_t, tempEndPts_da, curTurnout->endCnt ); + DYNARR_SET( junk_t, connection_da, curTurnout->endCnt ); + DYNARR_SET( junk_t, leftover_da, curTurnout->endCnt ); + + for (i=0; i<curTurnout->endCnt; i++ ) { + coOrd posI; + posI = curTurnout->endPt[i].pos; + tempEndPts(i).pos = AddCoOrd( Dto.pos, posI, Dto.angle ); + tempEndPts(i).angle = NormalizeAngle( curTurnout->endPt[i].angle + Dto.angle ); + } + + AuditTracks( "addTurnout begin" ); + + for (i=0;i<curTurnout->endCnt;i++) { + AuditTracks( "addTurnout [%d]", i ); + connection(i).trk = leftover(i).trk = NULL; + /* connect each endPt ... */ + epPos = tempEndPts(i).pos; + if ((trk = OnTrack(&epPos, FALSE, TRUE)) != NULL && + (!GetLayerFrozen(GetTrkLayer(trk))) && + (!QueryTrack(trk,Q_CANNOT_PLACE_TURNOUT)) ) { +LOG( log_turnout, 1, ( "ep[%d] on T%d @(%0.3f %0.3f)\n", + i, GetTrkIndex(trk), epPos.x, epPos.y ) ) + d = FindDistance( tempEndPts(i).pos, epPos ); + if ( GetTrkType(trk) == T_TURNOUT ) { + ep0 = ep1 = PickEndPoint( epPos, trk ); + a = GetTrkEndAngle( trk, ep0 ); + } else { + a = GetAngleAtPoint( trk, epPos, &ep0, &ep1 ); + } + aa = NormalizeAngle( a - tempEndPts(i).angle + connectAngle/2.0 ); + if ( IsClose(d) && + ( (ep0!=ep1 && aa<connectAngle) || + ( aa>180.0 && aa<180.0+connectAngle ) ) && + ! ( GetTrkType(trk) == T_TURNOUT && + (trk1=GetTrkEndTrk(trk,ep0)) && + GetTrkType(trk1) == T_TURNOUT ) ) { + /* ... if they are close to a track and line up */ + if (aa<connectAngle) { + epx = ep1; + epy = ep0; + } else { + epx = ep0; + epy = ep1; + } +LOG( log_turnout, 1, ( " Attach! epx=%d\n", epx ) ) + if ( epx != epy && + (d=FindDistance(GetTrkEndPos(trk,epy), epPos)) < minLength && + (trk1=GetTrkEndTrk(trk,epy)) != NULL ) { + epx = GetEndPtConnectedToMe( trk1, trk ); + trk = trk1; + } + /* split the track at the intersection point */ + AuditTracks( "addTurnout [%d] before splitTrack", i ); + if (SplitTrack( trk, epPos, epx, &leftover(i).trk, TRUE )) { + AuditTracks( "addTurnout [%d], after splitTrack", i ); + /* remember so we can fix up connection later */ + connection(i).trk = trk; + connection(i).ep = epx; + if (leftover(i).trk != NULL) { + leftover(i).ep = PickEndPoint( epPos, leftover(i).trk ); + /* did we already split this track? */ + for (j=0;j<i;j++) { + if ( leftover(j).trk == leftover(i).trk ) { + leftover(i).trk = NULL; + break; + } + if ( leftover(j).trk == connection(i).trk ) { + /* yes. Remove the leftover piece */ +LOG( log_turnout, 1, ( " deleting leftover T%d\n", + GetTrkIndex(leftover(i).trk) ) ) + leftover(j).trk = NULL; + AuditTracks( "addTurnout [%d] before delete", i ); + DeleteTrack( leftover(i).trk, FALSE ); + AuditTracks( "addTurnout [%d] before delete", i ); + leftover(i).trk = NULL; + break; + } + } + } + } + } + } + } + + AuditTracks( "addTurnout after loop" ); + + /* + * copy data */ + newTrk = NewCompound( T_TURNOUT, 0, Dto.pos, Dto.angle, curTurnout->title, tempEndPts_da.cnt, &tempEndPts(0), curTurnout->pathLen, (char *)curTurnout->paths, curTurnout->segCnt, curTurnout->segs ); + xx = GetTrkExtraData(newTrk); + xx->customInfo = curTurnout->customInfo; + if (connection((int)curTurnoutEp).trk) { + CopyAttributes( connection((int)curTurnoutEp).trk, newTrk ); + SetTrkScale( newTrk, curScaleInx ); + } + xx->special = curTurnout->special; + xx->u = curTurnout->u; +#ifdef LATER + xx->segCnt = curTurnout->segCnt; + memcpy( xx->segs, curTurnout->segs, xx->segCnt * sizeof *(trkSeg_p)0 ); + xx->title = curTurnout->title; + xx->paths = xx->pathCurr = curTurnout->paths; + xx->pathLen = curTurnout->pathLen; +#endif + + /* Make the connections */ +#ifdef LATER + for (i=0; i<curTurnout->endCnt; i++) + SetTrkEndPoint( newTrk, i, tempEndPts(i).pos, tempEndPts(i).angle ); +#endif + visible = FALSE; + noConnections = TRUE; + AuditTracks( "addTurnout T%d before connection", GetTrkIndex(newTrk) ); + for (i=0;i<curTurnout->endCnt;i++) { + if ( connection(i).trk != NULL ) { + p0 = GetTrkEndPos( newTrk, i ); + p1 = GetTrkEndPos( connection(i).trk, connection(i).ep ); + d = FindDistance( p0, p1 ); + if ( d < connectDistance ) { + noConnections = FALSE; + trk1 = connection(i).trk; + ep0 = connection(i).ep; + DrawEndPt( &mainD, trk1, ep0, wDrawColorWhite ); + ConnectTracks( newTrk, i, trk1, ep0 ); + visible |= GetTrkVisible(trk1); + DrawEndPt( &mainD, trk1, ep0, wDrawColorBlack ); + } + } + } + if (noConnections) + visible = TRUE; + SetTrkVisible( newTrk, visible); +#ifdef LATER + SetTrkScale( newTrk, curScaleInx ); + ComputeCompoundBoundingBox( newTrk ); +#endif + + AuditTracks( "addTurnout T%d before dealing with leftovers", GetTrkIndex(newTrk) ); + /* deal with the leftovers */ + for (i=0;i<curTurnout->endCnt;i++) { + if ( (trk=leftover(i).trk) != NULL && !IsTrackDeleted(trk) ) { + /* move endPt beyond the turnout */ + /* it it is short then delete it */ + coOrd off; + DIST_T maxX; + track_p lt = leftover(i).trk; + EPINX_T ep, le = leftover(i).ep; + coOrd pos; + maxX = 0.0; + a = NormalizeAngle( GetTrkEndAngle(lt,le) + 180.0 ); + for (ep=0; ep<curTurnout->endCnt; ep++) { + FindPos( &off, NULL, GetTrkEndPos(newTrk,ep), GetTrkEndPos(lt,le), a, 100000.0 ); + if (off.x > maxX) + maxX = off.x; + } + maxX += trackGauge; + pos = Dto.pos; + AuditTracks( "addTurnout T%d[%d] before trimming L%d[%d]", GetTrkIndex(newTrk), i, GetTrkIndex(lt), le ); + TrimTrack( lt, le, maxX ); + AuditTracks( "addTurnout T%d[%d] after trimming L%d[%d]", GetTrkIndex(newTrk), i, GetTrkIndex(lt), le ); + } + } + + SetDescriptionOrig( newTrk ); + xx->descriptionOff = zero; + xx->descriptionSize = zero; + + DrawNewTrack( newTrk ); + + AuditTracks( "addTurnout T%d returns", GetTrkIndex(newTrk) ); + UndoEnd(); + Dto.state = 0; + Dto.trk = NULL; + Dto.angle = 0.0; +} + + +static void TurnoutRotate( void * pangle ) +{ + ANGLE_T angle = (ANGLE_T)(long)pangle; + if (Dto.state == 1) + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlack ); + else + Dto.pos = cmdMenuPos; + Rotate( &Dto.pos, cmdMenuPos, angle ); + Dto.angle += angle; + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlack ); + Dto.state = 1; +} + +/** + * Process the mouse events for laying track. + * + * \param action IN event type + * \param pos IN mouse position + * \return next state + */ + +EXPORT STATUS_T CmdTurnoutAction( + wAction_t action, + coOrd pos ) +{ + ANGLE_T angle; + static BOOL_T validAngle; + static ANGLE_T baseAngle; + static coOrd origPos; +#ifdef NEWROTATE + static ANGLE_T origAngle; +#endif + switch (action & 0xFF) { + + case C_START: + Dto.state = 0; + Dto.trk = NULL; + Dto.angle = 0.0; + return C_CONTINUE; + + case C_DOWN: + if ( curTurnout == NULL ) return C_CONTINUE; + if (Dto.state == 1) { + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + } + PlaceTurnout( pos ); + Dto.state = 1; + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + return C_CONTINUE; + + case C_MOVE: + if ( curTurnout == NULL ) return C_CONTINUE; + if ( curTurnoutEp >= (long)curTurnout->endCnt ) + curTurnoutEp = 0; + if (Dto.state == 1) { + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + } else { + Dto.state = 1; + } + PlaceTurnout( pos ); + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + return C_CONTINUE; + + case C_UP: + InfoMessage( _("Left drag to move, right drag to rotate, press Space or Return to fix track in place or Esc to cancel") ); + return C_CONTINUE; + + case C_RDOWN: + if ( curTurnout == NULL ) return C_CONTINUE; + if (Dto.state == 1) + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + else + Dto.pos = pos; + Dto.rot0 = Dto.rot1 = pos; + DrawLine( &tempD, Dto.rot0, Dto.rot1, 0, wDrawColorBlack ); + Dto.state = 1; + origPos = Dto.pos; +#ifdef NEWROTATE + origAngle = Dto.angle; +#else + Rotate( &origPos, Dto.rot0, -(Dto.angle + curTurnout->endPt[(int)curTurnoutEp].angle) ); +#endif + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + validAngle = FALSE; + return C_CONTINUE; + + case C_RMOVE: + if ( curTurnout == NULL ) return C_CONTINUE; + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + DrawLine( &tempD, Dto.rot0, Dto.rot1, 0, wDrawColorBlack ); + Dto.rot1 = pos; + if ( FindDistance(Dto.rot0, Dto.rot1) > 0.1*mainD.scale ) { + angle = FindAngle( Dto.rot0, Dto.rot1 ); + if (!validAngle) { + baseAngle = angle/* - Dto.angle*/; + validAngle = TRUE; + } + Dto.pos = origPos; +#ifdef NEWROTATE + angle -= baseAngle; + Dto.angle = NormalizeAngle( origAngle + angle ); +#else + angle += 180.0; + Dto.angle = angle - curTurnout->endPt[(int)curTurnoutEp].angle; +#endif + Rotate( &Dto.pos, Dto.rot0, angle ); + } + FormatCompoundTitle( listLabels, curTurnout->title ); + InfoMessage( _("Angle = %0.3f (%s)"), PutAngle( NormalizeAngle(Dto.angle + 90.0) ), message ); + DrawLine( &tempD, Dto.rot0, Dto.rot1, 0, wDrawColorBlack ); + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + return C_CONTINUE; + + case C_RUP: + if ( curTurnout == NULL ) return C_CONTINUE; + DrawLine( &tempD, Dto.rot0, Dto.rot1, 0, wDrawColorBlack ); + InfoMessage( _("Left drag to move, right drag to rotate, press Space or Return to fix track in place or Esc to cancel") ); + return C_CONTINUE; + + case C_LCLICK: + if ( curTurnout == NULL ) return C_CONTINUE; + if ( MyGetKeyState() & WKEY_SHIFT ) { + if (Dto.state == 1) + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + angle = curTurnout->endPt[(int)curTurnoutEp].angle; + curTurnoutEp++; + if (curTurnoutEp >= (long)curTurnout->endCnt) + curTurnoutEp = 0; + if (Dto.trk == NULL) + Dto.angle = NormalizeAngle( Dto.angle + (angle - curTurnout->endPt[(int)curTurnoutEp].angle ) ); + PlaceTurnout( Dto.place ); + if (Dto.state == 1) + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + } else { + CmdTurnoutAction( C_DOWN, pos ); + CmdTurnoutAction( C_UP, pos ); + } + return C_CONTINUE; + + case C_REDRAW: + if (Dto.state) + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + return C_CONTINUE; + + case C_CANCEL: + if (Dto.state) + DrawSegs( &tempD, Dto.pos, Dto.angle, + curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + Dto.state = 0; + Dto.trk = NULL; + /*wHide( newTurn.reg.win );*/ + return C_TERMINATE; + + case C_TEXT: + if ((action>>8) != ' ') + return C_CONTINUE; + case C_OK: + AddTurnout(); + return C_TERMINATE; + + case C_FINISH: + if (Dto.state != 0 && Dto.trk != NULL) + CmdTurnoutAction( C_OK, pos ); + else + CmdTurnoutAction( C_CANCEL, pos ); + return C_TERMINATE; + + case C_CMDMENU: + if ( turnoutPopupM == NULL ) { + turnoutPopupM = MenuRegister( "Turnout Rotate" ); + AddRotateMenu( turnoutPopupM, TurnoutRotate ); + } + wMenuPopupShow( turnoutPopupM ); + return C_CONTINUE; + + default: + return C_CONTINUE; + } +} + + +#ifdef TURNOUTCMD +static STATUS_T CmdTurnout( + wAction_t action, + coOrd pos ) +{ + wIndex_t turnoutIndex; + turnoutInfo_t * turnoutPtr; + + switch (action & 0xFF) { + + case C_START: + if (turnoutW == NULL) { +/* turnoutW = ParamCreateDialog( &turnoutPG, MakeWindowTitle("Turnout"), "Ok", , (paramActionCancelProc)Reset, TRUE, NULL, F_RESIZE|F_RECALLSIZE, TurnoutDlgUpdate ); */ + turnoutW = ParamCreateDialog( &turnoutPG, MakeWindowTitle(_("Turnout")), _("Close"), (paramActionOkProc)TurnoutOk, NULL, TRUE, NULL, F_RESIZE|F_RECALLSIZE|PD_F_ALT_CANCELLABEL, TurnoutDlgUpdate ); + InitNewTurn( turnoutNewM ); + } +/* ParamDialogOkActive( &turnoutPG, FALSE ); */ + turnoutIndex = wListGetIndex( turnoutListL ); + turnoutPtr = curTurnout; + wShow( turnoutW ); + TurnoutChange( CHANGE_PARAMS|CHANGE_SCALE ); + if (curTurnout == NULL) { + NoticeMessage2( 0, MSG_TURNOUT_NO_TURNOUT, _("Ok"), NULL ); + return C_TERMINATE; + } + if (turnoutIndex > 0 && turnoutPtr) { + curTurnout = turnoutPtr; + wListSetIndex( turnoutListL, turnoutIndex ); + RedrawTurnout(); + } + InfoMessage( _("Pick turnout and active End Point, then place on the layout")); + ParamLoadControls( &turnoutPG ); + ParamGroupRecord( &turnoutPG ); + return CmdTurnoutAction( action, pos ); + + case C_DOWN: + case C_RDOWN: + ParamDialogOkActive( &turnoutPG, TRUE ); + if (hideTurnoutWindow) + wHide( turnoutW ); + case C_MOVE: + case C_RMOVE: + return CmdTurnoutAction( action, pos ); + + case C_UP: + case C_RUP: + if (hideTurnoutWindow) + wShow( turnoutW ); + InfoMessage( _("Left drag to move, right drag to rotate, press Space or Return to fix track in place or Esc to cancel") ); + return CmdTurnoutAction( action, pos ); + + case C_LCLICK: + HilightEndPt(); + CmdTurnoutAction( action, pos ); + HilightEndPt(); + return C_CONTINUE; + + case C_CANCEL: + wHide( turnoutW ); + return CmdTurnoutAction( action, pos ); + case C_TEXT: + CmdTurnoutAction( action, pos ); + return C_CONTINUE; + case C_OK: + case C_FINISH: + case C_CMDMENU: + case C_REDRAW: + return CmdTurnoutAction( action, pos ); + + default: + return C_CONTINUE; + } +} + +#endif + +/** + * Event procedure for the hotbar. + * + * \param op IN requested function + * \param data IN pointer to info on selected element + * \param d IN + * \param origP IN + * \return + */ + +static char * CmdTurnoutHotBarProc( + hotBarProc_e op, + void * data, + drawCmd_p d, + coOrd * origP ) +{ + turnoutInfo_t * to = (turnoutInfo_t*)data; + switch ( op ) { + case HB_SELECT: /* new element is selected */ + CmdTurnoutAction( C_FINISH, zero ); /* finish current operation */ + curTurnout = to; + DoCommandB( (void*)(intptr_t)turnoutHotBarCmdInx ); /* continue with new turnut / structure */ + return NULL; + case HB_LISTTITLE: + FormatCompoundTitle( listLabels, to->title ); + if (message[0] == '\0') + FormatCompoundTitle( listLabels|LABEL_DESCR, to->title ); + return message; + case HB_BARTITLE: + FormatCompoundTitle( hotBarLabels<<1, to->title ); + return message; + case HB_FULLTITLE: + return to->title; + case HB_DRAW: + DrawSegs( d, *origP, 0.0, to->segs, to->segCnt, trackGauge, wDrawColorBlack ); + return NULL; + } + return NULL; +} + + +EXPORT void AddHotBarTurnouts( void ) +{ + wIndex_t inx; + turnoutInfo_t * to; + for ( inx=0; inx < turnoutInfo_da.cnt; inx ++ ) { + to = turnoutInfo(inx); + if ( !( IsParamValid(to->paramFileIndex) && + to->segCnt > 0 && + CompatibleScale( TRUE, to->scaleInx, curScaleInx ) ) ) + continue; + AddHotBarElement( to->contentsLabel, to->size, to->orig, TRUE, to->barScale, to, CmdTurnoutHotBarProc ); + } +} + +/** + * Handle mouse events for laying track when initiated from hotbar. + * + * \param action IN mouse event type + * \param pos IN mouse position + * \return next state of operation + */ + +static STATUS_T CmdTurnoutHotBar( + wAction_t action, + coOrd pos ) +{ + + switch (action & 0xFF) { + + case C_START: + TurnoutChange( CHANGE_PARAMS|CHANGE_SCALE ); + if (curTurnout == NULL) { + NoticeMessage2( 0, MSG_TURNOUT_NO_TURNOUT, _("Ok"), NULL ); + return C_TERMINATE; + } + FormatCompoundTitle( listLabels|LABEL_DESCR, curTurnout->title ); + InfoMessage( _("Place %s and draw into position"), message ); + ParamLoadControls( &turnoutPG ); + ParamGroupRecord( &turnoutPG ); + return CmdTurnoutAction( action, pos ); + + case C_UP: + case C_RUP: + InfoMessage( _("Left drag to move, right drag to rotate, press Space or Return to fix track in place or Esc to cancel") ); + return CmdTurnoutAction( action, pos ); + + case C_TEXT: + if ((action>>8) != ' ') + return C_CONTINUE; + case C_OK: + CmdTurnoutAction( action, pos ); + return C_CONTINUE; + + case C_CANCEL: + HotBarCancel(); + default: + return CmdTurnoutAction( action, pos ); + } +} + +#ifdef TURNOUTCMD +#include "bitmaps/turnout.xpm" + + +EXPORT void InitCmdTurnout( wMenu_p menu ) +{ + AddMenuButton( menu, CmdTurnout, "cmdTurnout", _("Turnout"), wIconCreatePixMap(turnout_xpm), LEVEL0_50, IC_STICKY|IC_LCLICK|IC_CMDMENU|IC_POPUP2, ACCL_TURNOUT, NULL ); + turnoutHotBarCmdInx = AddMenuButton( menu, CmdTurnoutHotBar, "cmdTurnoutHotBar", "", NULL, LEVEL0_50, IC_STICKY|IC_LCLICK|IC_CMDMENU|IC_POPUP2, 0, NULL ); + RegisterChangeNotification( TurnoutChange ); + ParamRegister( &turnoutPG ); + log_turnout = LogFindIndex( "turnout" ); + log_traverseTurnout = LogFindIndex( "traverseTurnout" ); +} +#endif + +EXPORT void InitTrkTurnout( void ) +{ + T_TURNOUT = InitObject( &turnoutCmds ); + + /*InitDebug( "Turnout", &debugTurnout );*/ + AddParam( N_("TURNOUT "), ReadTurnoutParam ); +} + +#ifdef TEST + +wDrawable_t turnoutD; + +void wListAddValue( wList_p bl, char * val, wIcon_p, void * listData, void * itemData ) +{ +} + +void wListClear( wList_p bl ) +{ +} + +void wDrawSetScale( wDrawable_p d ) +{ + d->scale = 1.0; +} + +void wDrawClear( wDrawable_p d ) +{ +} + +void GetTrkCurveCenter( track_p t, coOrd *pos, DIST_T *radius ) +{ +} + +#ifdef NOTRACK_C + +track_p NewTrack( wIndex_t index, TRKTYP_T type, EPINX_T endCnt, SIZE_T extraSize ) +{ + return NULL; +} + +track_p OnTrack( coOrd *pos ) +{ + return NULL; +} + +void ErrorMessage( char * msg, ... ) +{ + lprintf( "ERROR : %s\n", msg ); +} + +void DeleteTrack( track_p t ) +{ +} + +void ConnectTracks( track_p t0, EPINX_T ep0, track_p t1, EPINX_T ep1 ) +{ +} +#endif + +main( INT_T argc, char * argv[] ) +{ + FILE * f; + char line[STR_SIZE]; + wIndex_t lineCnt = 0; + + /*debugTurnout = 3;*/ + if ((f = fopen("turnout.params", "r" )) == NULL ) { + Perror( "turnout.params" ); + Exit(1); + } + while ( fgets( line, sizeof line, f ) != NULL ) { + lineCnt++; + ReadTurnoutParam( &lineCnt ); + } +} +#endif diff --git a/app/bin/cturntbl.c b/app/bin/cturntbl.c new file mode 100644 index 0000000..31f33ed --- /dev/null +++ b/app/bin/cturntbl.c @@ -0,0 +1,838 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cturntbl.c,v 1.4 2008-03-06 19:35:06 m_fischer Exp $ + * + * TURNTABLE + * + */ + +/* 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 "track.h" +#include "cstraigh.h" +#include "i18n.h" + +static TRKTYP_T T_TURNTABLE = -1; + + +struct extraData { + coOrd pos; + DIST_T radius; + EPINX_T currEp; + BOOL_T reverse; + }; + +static DIST_T turntableDiameter = 1.0; + +EXPORT ANGLE_T turntableAngle = 0.0; + +static paramFloatRange_t r1_100 = { 1.0, 100.0, 100 }; +static paramData_t turntablePLs[] = { +#define turntableDiameterPD (turntablePLs[0]) + { PD_FLOAT, &turntableDiameter, "diameter", PDO_DIM|PDO_NOPREF, &r1_100, N_("Diameter") } }; +static paramGroup_t turntablePG = { "turntable", 0, turntablePLs, sizeof turntablePLs/sizeof turntablePLs[0] }; + + +static BOOL_T ValidateTurntablePosition( + track_p trk ) +{ + struct extraData * xx = GetTrkExtraData(trk); + EPINX_T ep, epCnt = GetTrkEndPtCnt(trk); + + if ( epCnt <= 0 ) + return FALSE; + ep = xx->currEp; + do { + if ( GetTrkEndTrk(trk,ep) ) { + xx->currEp = ep; + return TRUE; + } + ep++; + if ( ep >= epCnt ) + ep = 0; + } while ( ep != xx->currEp ); + return FALSE; +} + + +static void ComputeTurntableBoundingBox( track_p trk ) +{ + struct extraData *xx = GetTrkExtraData(trk); + coOrd hi, lo; + hi.x = xx->pos.x+xx->radius; + lo.x = xx->pos.x-xx->radius; + hi.y = xx->pos.y+xx->radius; + lo.y = xx->pos.y-xx->radius; + SetBoundingBox( trk, hi, lo ); +} + +static track_p NewTurntable( coOrd p, DIST_T r ) +{ + track_p t; + struct extraData *xx; + t = NewTrack( 0, T_TURNTABLE, 0, sizeof *xx ); + xx = GetTrkExtraData(t); + xx->pos = p; + xx->radius = r; + xx->currEp = 0; + xx->reverse = 0; + ComputeTurntableBoundingBox( t ); + return t; +} + +#ifdef LATER +-static void PruneTurntable( track_p trk ) +-{ +- EPINX_T inx0; +- EPINX_T inx1; +- for (inx0=inx1=0; inx0<trk->endCnt; inx0++) { +- if (GetTrkEndTrk(trk,inx0) == NULL) { +- continue; +- } else { +- if (inx0 != inx1) { +- trk->endPt[inx1] = GetTrkEndTrk(trk,inx0); +- } +- inx1++; +- } +- } +- trk->endPt = Realloc( trk->endPt, inx1*sizeof trk->endPt[0] ); +- trk->endCnt = inx1; +-} +#endif + +static ANGLE_T ConstrainTurntableAngle( track_p trk, coOrd pos ) +{ + struct extraData *xx = GetTrkExtraData(trk); + ANGLE_T a, al, ah, aa, aaa; + EPINX_T inx, cnt; + + a = FindAngle( xx->pos, pos ); + cnt = GetTrkEndPtCnt(trk); + if ( cnt == 0 || turntableAngle == 0.0 ) + return a; + ah = 360.0; + al = 360.0; + for ( inx = 0; inx<cnt; inx++ ) { + if (GetTrkEndTrk(trk,inx) == NULL) + continue; + aa = NormalizeAngle( GetTrkEndAngle(trk,inx) - a ); + if (aa < al) + al = aa; + aa = 360 - aa; + if (aa < ah) + ah = aa; + } + if (al+ah>361) + return a; + if ( (al+ah) < turntableAngle*2.0 ) { + ErrorMessage( MSG_NO_ROOM_BTW_TRKS ); + aaa = -1; + } else if ( al <= turntableAngle) + aaa = NormalizeAngle( a - ( turntableAngle - al ) ); + else if ( ah <= turntableAngle) + aaa = NormalizeAngle( a + ( turntableAngle - ah ) ); + else + aaa = a; +#ifdef VERBOSE + Lprintf( "CTA( %0.3f ) [ %0.3f .. %0.3f ] = %0.3f\n", a, ah, al, aaa ); +#endif + return aaa; +} + +static EPINX_T NewTurntableEndPt( track_p trk, ANGLE_T angle ) +{ + struct extraData *xx = GetTrkExtraData(trk); + EPINX_T ep = GetTrkEndPtCnt(trk); + coOrd pos; + SetTrkEndPtCnt( trk, ep+1 ); + PointOnCircle( &pos, xx->pos, xx->radius, angle ); + SetTrkEndPoint( trk, ep, pos, angle ); + return ep; +} + +static void TurntableGetCenter( track_p trk, coOrd * center, DIST_T * radius) +{ + struct extraData *xx = GetTrkExtraData(trk); + *center = xx->pos; + *radius = xx->radius; +} + +static void DrawTurntable( track_p t, drawCmd_p d, wDrawColor color ) +{ + struct extraData *xx = GetTrkExtraData(t); + coOrd p0, p1; + EPINX_T ep; + long widthOptions = DTS_TIES; + + if ( !ValidateTurntablePosition(t) ) { + p0.y = p1.y = xx->pos.y; + p0.x = xx->pos.x-xx->radius; + p1.x = xx->pos.x+xx->radius; + } else { + p0 = GetTrkEndPos( t, xx->currEp ); + Translate( &p1, xx->pos, GetTrkEndAngle(t,xx->currEp)+180.0, xx->radius ); + } + if (color == wDrawColorBlack) + color = normalColor; + DrawArc( d, xx->pos, xx->radius, 0.0, 360.0, 0, 0, color ); + if ( programMode != MODE_DESIGN ) + return; + if ( (d->options&DC_QUICK) == 0 ) { + DrawStraightTrack( d, p0, p1, FindAngle(p0,p1), t, GetTrkGauge(t), color, widthOptions ); + for ( ep=0; ep<GetTrkEndPtCnt(t); ep++ ) { + if (GetTrkEndTrk(t,ep) != NULL ) + DrawEndPt( d, t, ep, color ); + } + } + if ( ((d->funcs->options&wDrawOptTemp)==0) && + (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) && + labelScale >= d->scale ) { + LabelLengths( d, t, color ); + } +} + +static DIST_T DistanceTurntable( track_p trk, coOrd * p ) +{ + struct extraData *xx = GetTrkExtraData(trk); + DIST_T d; + ANGLE_T a; + coOrd pos0, pos1; + + d = FindDistance( xx->pos, *p ) - xx->radius; + if (d < 0.0) + d = 0.0; + if ( programMode == MODE_DESIGN ) { + a = FindAngle( xx->pos, *p ); + Translate( p, xx->pos, a, d+xx->radius ); + } else { + if ( !ValidateTurntablePosition(trk) ) + return 100000.0; + pos0 = GetTrkEndPos(trk,xx->currEp); + Translate( &pos1, xx->pos, GetTrkEndAngle(trk,xx->currEp)+180.0, xx->radius ); + LineDistance( p, pos0, pos1 ); + } + return d; +} + +static struct { + coOrd orig; + DIST_T diameter; + long epCnt; + LAYER_T layerNumber; + } trntblData; +typedef enum { OR, RA, EC, LY } trntblDesc_e; +static descData_t trntblDesc[] = { +/*OR*/ { DESC_POS, N_("Origin: X"), &trntblData.orig }, +/*RA*/ { DESC_DIM, N_("Diameter"), &trntblData.diameter }, +/*EC*/ { DESC_LONG, N_("# EndPt"), &trntblData.epCnt }, +/*LY*/ { DESC_LAYER, N_("Layer"), &trntblData.layerNumber }, + { DESC_NULL } }; + + +static void UpdateTurntable( track_p trk, int inx, descData_p descUpd, BOOL_T final ) +{ + struct extraData *xx = GetTrkExtraData(trk); + + if ( inx == -1 ) + return; + UndrawNewTrack( trk ); + switch ( inx ) { + case OR: + xx->pos = trntblData.orig; + break; + case RA: + if ( trntblData.diameter > 2.0 ) + xx->radius = trntblData.diameter/2.0; + break; + case LY: + SetTrkLayer( trk, trntblData.layerNumber ); + break; + default: + break; + } + ComputeTurntableBoundingBox( trk ); + DrawNewTrack( trk ); +} + + +static void DescribeTurntable( track_p trk, char * str, CSIZE_T len ) +{ + struct extraData *xx = GetTrkExtraData(trk); + sprintf( str, _("Turntable(%d): Layer=%d Center=[%s %s] Diameter=%s #EP=%d"), + GetTrkIndex(trk), GetTrkLayer(trk)+1, + FormatDistance(xx->pos.x), FormatDistance(xx->pos.y), + FormatDistance(xx->radius * 2.0), GetTrkEndPtCnt(trk) ); + + trntblData.orig = xx->pos; + trntblData.diameter = xx->radius*2.0; + trntblData.epCnt = GetTrkEndPtCnt(trk); + trntblData.layerNumber = GetTrkLayer(trk); + + trntblDesc[OR].mode = + trntblDesc[RA].mode = + trntblData.epCnt>0?DESC_RO:0; + trntblDesc[EC].mode = DESC_RO; + trntblDesc[LY].mode = DESC_NOREDRAW; + DoDescribe( _("Turntable"), trk, trntblDesc, UpdateTurntable ); +} + +static void DeleteTurntable( track_p t ) +{ +} + +static BOOL_T WriteTurntable( track_p t, FILE * f ) +{ + struct extraData *xx = GetTrkExtraData(t); + EPINX_T ep; + BOOL_T rc = TRUE; + rc &= fprintf(f, "TURNTABLE %d %d 0 0 0 %s %d %0.6f %0.6f 0 %0.6f %d\n", + GetTrkIndex(t), GetTrkLayer(t), GetTrkScaleName(t), GetTrkVisible(t), + xx->pos.x, xx->pos.y, xx->radius, xx->currEp )>0; + for (ep=0; ep<GetTrkEndPtCnt(t); ep++) + rc &= WriteEndPt( f, t, ep ); + rc &= fprintf(f, "\tEND\n")>0; + return rc; +} + +static void ReadTurntable( char * line ) +{ + track_p trk; + struct extraData *xx; + TRKINX_T index; + BOOL_T visible; + DIST_T r; + coOrd p; + DIST_T elev; + char scale[10]; + wIndex_t layer; + int currEp; + + if ( !GetArgs( line+10, + paramVersion<3?"dXsdpYfX": + paramVersion<9?"dL000sdpYfX": + paramVersion<10?"dL000sdpffX": + "dL000sdpffd", + &index, &layer, scale, &visible, &p, &elev, &r, &currEp )) + return; + trk = NewTrack( index, T_TURNTABLE, 0, sizeof *xx ); + ReadSegs(); + SetEndPts( trk, 0 ); + xx = GetTrkExtraData(trk); + SetTrkVisible(trk, visible); + SetTrkScale(trk, LookupScale( scale ) ); + SetTrkLayer(trk, layer); + xx->pos = p; + xx->radius = r; + xx->currEp = currEp; + xx->reverse = 0; + ComputeTurntableBoundingBox( trk ); +} + +static void MoveTurntable( track_p trk, coOrd orig ) +{ + struct extraData *xx = GetTrkExtraData(trk); + xx->pos.x += orig.x; + xx->pos.y += orig.y; + ComputeTurntableBoundingBox( trk ); +} + +static void RotateTurntable( track_p trk, coOrd orig, ANGLE_T angle ) +{ + struct extraData *xx = GetTrkExtraData(trk); + Rotate( &xx->pos, orig, angle ); + ComputeTurntableBoundingBox( trk ); +} + +static void RescaleTurntable( track_p trk, FLOAT_T ratio ) +{ + struct extraData *xx = GetTrkExtraData(trk); + xx->pos.x *= ratio; + xx->pos.y *= ratio; +} + +static ANGLE_T GetAngleTurntable( track_p trk, coOrd pos, EPINX_T * ep0, EPINX_T * ep1 ) +{ + struct extraData *xx = GetTrkExtraData(trk); + if ( programMode == MODE_DESIGN ) { + return FindAngle( xx->pos, pos ); + } else { + if ( !ValidateTurntablePosition( trk ) ) + return 90.0; + else + return GetTrkEndAngle( trk, xx->currEp ); + } +} + + +static BOOL_T SplitTurntable( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, EPINX_T *ep0, EPINX_T *ep1 ) +{ + if (leftover) + *leftover = NULL; + ErrorMessage( MSG_CANT_SPLIT_TRK, "Turntable" ); + return FALSE; +} + + +static BOOL_T FindTurntableEndPt( + track_p trk, + ANGLE_T *angleR, + EPINX_T *epR, + BOOL_T *reverseR ) +{ + EPINX_T ep, ep0, epCnt=GetTrkEndPtCnt(trk); + ANGLE_T angle=*angleR, angle0, angle1; + for (ep=0,ep0=-1,epCnt=GetTrkEndPtCnt(trk),angle0=370.0; ep<epCnt; ep++) { + if ( (GetTrkEndTrk(trk,ep)) == NULL ) + continue; + angle1 = GetTrkEndAngle(trk,ep); + angle1 = NormalizeAngle(angle1-angle); + if ( angle1 > 180.0 ) + angle1 = 360.0-angle1; + if ( angle1 < angle0 ) { + *epR = ep; + *reverseR = FALSE; + angle0 = angle1; + } +#ifdef LATER + if ( angle1 > 90.0 ) { + angle1 = 180.0-angle1; + if ( angle1 < angle0 ) { + *epR = ep; + *reverseR = TRUE; + angle0 = angle1; + } + } +#endif + } + if ( angle0 < 360.0 ) { + *angleR = angle0; + return TRUE; + } else { + return FALSE; + } +} + + + +static BOOL_T CheckTraverseTurntable( + track_p trk, + coOrd pos ) +{ + struct extraData * xx = GetTrkExtraData(trk); + ANGLE_T angle; + + if ( !ValidateTurntablePosition( trk ) ) + return FALSE; + angle = FindAngle( xx->pos, pos ) - GetTrkEndAngle( trk, xx->currEp )+connectAngle/2.0; + if ( angle <= connectAngle || + ( angle >= 180.0 && angle <= 180+connectAngle ) ) + return TRUE; + return FALSE; +} + + +static BOOL_T TraverseTurntable( + traverseTrack_p trvTrk, + DIST_T * distR ) +{ + track_p trk = trvTrk->trk; + struct extraData * xx = GetTrkExtraData(trk); + coOrd pos0; + DIST_T dist, dist1; + ANGLE_T angle, angle1; + EPINX_T ep; + BOOL_T reverse; + + if ( !ValidateTurntablePosition( trk ) ) + return FALSE; + dist = FindDistance( xx->pos, trvTrk->pos ); + pos0 = GetTrkEndPos( trk, xx->currEp ); + angle = FindAngle( pos0, xx->pos ); + if ( NormalizeAngle( angle-trvTrk->angle+90 ) < 180 ) { + angle1 = angle; + } else { + angle1 = NormalizeAngle( angle+180.0 ); + } + if ( dist > xx->radius*0.9 ) { + angle = NormalizeAngle( angle-trvTrk->angle ); + if ( ( angle < 90.0 && angle > connectAngle ) || + ( angle > 270.0 && angle < 360.0-connectAngle ) ) + return FALSE; + } + trvTrk->angle = angle1; + angle = FindAngle( trvTrk->pos, xx->pos ); + if ( NormalizeAngle( angle-angle1+90.0 ) < 180 ) { + if ( dist > *distR ) { + Translate( &trvTrk->pos, xx->pos, angle1+180.0, dist-*distR ); + *distR = 0; + return TRUE; + } else { + *distR -= dist; + dist = 0.0; + } + } + dist1 = xx->radius-dist; + if ( dist1 > *distR ) { + Translate( &trvTrk->pos, xx->pos, angle1, dist+*distR ); + *distR = 0.0; + return TRUE; + } + Translate( &trvTrk->pos, xx->pos, angle1, xx->radius ); + *distR -= dist1; + if ( FindTurntableEndPt( trk, &angle1, &ep, &reverse ) && angle1 < connectAngle ) { + trk = GetTrkEndTrk(trk,ep); + } else { + trk = NULL; + } + trvTrk->trk = trk; + return TRUE; +} + + +static BOOL_T EnumerateTurntable( track_p trk ) +{ + struct extraData *xx; + static dynArr_t turntables_da; +#define turntables(N) DYNARR_N( FLOAT_T, turntables_da, N ) + int inx; + char tmp[40]; + if ( trk != NULL ) { + xx = GetTrkExtraData(trk); + DYNARR_APPEND( FLOAT_T, turntables_da, 10 ); + turntables(turntables_da.cnt-1) = xx->radius*2.0; + sprintf( tmp, "Turntable, diameter %s", FormatDistance(turntables(turntables_da.cnt-1)) ); + inx = strlen( tmp ); + if ( inx > (int)enumerateMaxDescLen ) + enumerateMaxDescLen = inx; + } else { + for (inx=0; inx<turntables_da.cnt; inx++) { + sprintf( tmp, "Turntable, diameter %s", FormatDistance(turntables(inx)) ); + EnumerateList( 1, 0.0, tmp ); + } + DYNARR_RESET( FLOAT_T, turntables_da ); + } + return TRUE; +} + + +static STATUS_T ModifyTurntable( track_p trk, wAction_t action, coOrd pos ) +{ + static coOrd ttCenter; + static DIST_T ttRadius; + static ANGLE_T angle; + static BOOL_T valid; + + DIST_T r; + EPINX_T ep; + track_p trk1; + + switch ( action ) { + case C_DOWN: + TurntableGetCenter( trk, &ttCenter, &ttRadius ); + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).width = 0; + InfoMessage( _("Drag to create stall track") ); + + case C_MOVE: + valid = FALSE; + if ( (angle = ConstrainTurntableAngle( trk, pos )) < 0.0) { + ; + } else if ((r=FindDistance( ttCenter, pos )) < ttRadius) { + ErrorMessage( MSG_POINT_INSIDE_TURNTABLE ); + } else if ( (r-ttRadius) <= minLength ) { + if (action == C_MOVE) + ErrorMessage( MSG_TRK_TOO_SHORT, "Stall ", PutDim(fabs(minLength-(r-ttRadius))) ); + } else { + Translate( &tempSegs(0).u.l.pos[0], ttCenter, angle, ttRadius ); + Translate( &tempSegs(0).u.l.pos[1], ttCenter, angle, r ); + if (action == C_MOVE) + InfoMessage( _("Straight Track: Length=%s Angle=%0.3f"), + FormatDistance( r-ttRadius ), PutAngle( angle ) ); + tempSegs_da.cnt = 1; + valid = TRUE; + } + return C_CONTINUE; + + case C_UP: + if (!valid) + return C_TERMINATE; + ep = NewTurntableEndPt( trk, angle ); + trk1 = NewStraightTrack( tempSegs(0).u.l.pos[0], tempSegs(0).u.l.pos[1] ); + CopyAttributes( trk, trk1 ); + ConnectTracks( trk, ep, trk1, 0 ); + DrawNewTrack( trk1 ); + return C_TERMINATE; + + default: + ; + } + return C_ERROR; +} + + +static BOOL_T GetParamsTurntable( int inx, track_p trk, coOrd pos, trackParams_t * params ) +{ + coOrd center; + DIST_T radius; + + if (inx == PARAMS_1ST_JOIN) { + ErrorMessage( MSG_JOIN_TURNTABLE ); + return FALSE; + } + params->type = curveTypeStraight; + params->ep = -1; + params->angle = ConstrainTurntableAngle( trk, pos ); + if (params->angle < 0.0) + return FALSE; + TurntableGetCenter( trk, ¢er, &radius ); + PointOnCircle( ¶ms->lineOrig, center, radius, params->angle ); + params->lineEnd = params->lineOrig; + params->len = 0.0; + params->arcR = 0.0; + return TRUE; +} + + +static BOOL_T MoveEndPtTurntable( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d0 ) +{ + coOrd posCen; + DIST_T r; + ANGLE_T angle0; + DIST_T d; + track_p trk1; + + TurntableGetCenter( *trk, &posCen, &r ); + angle0 = FindAngle( posCen, pos ); + d = FindDistance( posCen, pos ); + if (d0 > 0.0) { + d -= d0; + Translate( &pos, pos, angle0+180, d0 ); + } + if (d < r) { + ErrorMessage( MSG_POINT_INSIDE_TURNTABLE ); + return FALSE; + } + *ep = NewTurntableEndPt( *trk, angle0 ); + if ((d-r) > connectDistance) { + trk1 = NewStraightTrack( GetTrkEndPos(*trk,*ep), pos ); + CopyAttributes( *trk, trk1 ); + ConnectTracks( *trk, *ep, trk1, 0 ); + *trk = trk1; + *ep = 1; + DrawNewTrack( *trk ); + } + return TRUE; +} + + +static BOOL_T QueryTurntable( track_p trk, int query ) +{ + switch ( query ) { + case Q_REFRESH_JOIN_PARAMS_ON_MOVE: + case Q_CANNOT_PLACE_TURNOUT: + case Q_DONT_DRAW_ENDPOINT: + case Q_CAN_NEXT_POSITION: + case Q_ISTRACK: + case Q_NOT_PLACE_FROGPOINTS: + case Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK: + return TRUE; + default: + return FALSE; + } +} + + +static void FlipTurntable( + track_p trk, + coOrd orig, + ANGLE_T angle ) +{ + struct extraData * xx = GetTrkExtraData(trk); + FlipPoint( &xx->pos, orig, angle ); + ComputeBoundingBox( trk ); +} + + +static void DrawTurntablePositionIndicator( track_p trk, wDrawColor color ) +{ + struct extraData * xx = GetTrkExtraData(trk); + coOrd pos0, pos1; + ANGLE_T angle; + + if ( !ValidateTurntablePosition(trk) ) + return; + pos0 = GetTrkEndPos(trk,xx->currEp); + angle = FindAngle( xx->pos, pos0 ); + PointOnCircle( &pos1, xx->pos, xx->radius, angle+180.0 ); + DrawLine( &mainD, pos0, pos1, 3, color ); +} + +static void AdvanceTurntablePositionIndicator( + track_p trk, + coOrd pos, + coOrd * posR, + ANGLE_T * angleR ) +{ + struct extraData * xx = GetTrkExtraData(trk); + EPINX_T ep; + ANGLE_T angle0, angle1; + BOOL_T reverse; + + angle1 = FindAngle( xx->pos, pos ); + if ( !FindTurntableEndPt( trk, &angle1, &ep, &reverse ) ) + return; + DrawTurntablePositionIndicator( trk, wDrawColorWhite ); + angle0 = GetTrkEndAngle(trk,xx->currEp); + if ( ep == xx->currEp ) { + Rotate( posR, xx->pos, 180.0 ); + if ( xx->reverse ) { + angle1 = angle0; + xx->reverse = FALSE; + } else { + angle1 = NormalizeAngle( angle0+180.0 ); + xx->reverse = TRUE; + } + } else { + angle1 = GetTrkEndAngle(trk,ep); + Rotate( posR, xx->pos, angle1-angle0 ); + xx->reverse = FALSE; + } + *angleR = angle1; + xx->currEp = ep; + DrawTurntablePositionIndicator( trk, selectedColor ); +} + + +static trackCmd_t turntableCmds = { + "TURNTABLE", + DrawTurntable, + DistanceTurntable, + DescribeTurntable, + DeleteTurntable, + WriteTurntable, + ReadTurntable, + MoveTurntable, + RotateTurntable, + RescaleTurntable, + NULL, /* audit */ + GetAngleTurntable, + SplitTurntable, /* split */ + TraverseTurntable, + EnumerateTurntable, + NULL, /* redraw */ + NULL, /* trim */ + NULL, /* merge */ + ModifyTurntable, + NULL, /* getLength */ + GetParamsTurntable, + MoveEndPtTurntable, + QueryTurntable, + NULL, /* ungroup */ + FlipTurntable, + DrawTurntablePositionIndicator, + AdvanceTurntablePositionIndicator, + CheckTraverseTurntable }; + + +static STATUS_T CmdTurntable( wAction_t action, coOrd pos ) +{ + track_p t; + static coOrd pos0; + wControl_p controls[2]; + char * labels[1]; + + switch (action) { + + case C_START: + if (turntableDiameterPD.control==NULL) + ParamCreateControls( &turntablePG, NULL ); + sprintf( message, "turntable-diameter-%s", curScaleName ); + turntableDiameter = ceil(80.0*12.0/curScaleRatio); + wPrefGetFloat( "misc", message, &turntableDiameter, turntableDiameter ); + ParamLoadControls( &turntablePG ); + ParamGroupRecord( &turntablePG ); + controls[0] = turntableDiameterPD.control; + controls[1] = NULL; + labels[0] = N_("Diameter"); + InfoSubstituteControls( controls, labels ); + /*InfoMessage( "Place Turntable");*/ + return C_CONTINUE; + + case C_DOWN: + SnapPos( &pos ); + if ( turntableDiameter <= 0.0 ) { + ErrorMessage( MSG_TURNTABLE_DIAM_GTR_0 ); + return C_ERROR; + } + controls[0] = turntableDiameterPD.control; + controls[1] = NULL; + labels[0] = N_("Diameter"); + InfoSubstituteControls( controls, labels ); + ParamLoadData( &turntablePG ); + pos0 = pos; + DrawArc( &tempD, pos0, turntableDiameter/2.0, 0.0, 360.0, 0, 0, wDrawColorBlack ); + return C_CONTINUE; + + case C_MOVE: + DrawArc( &tempD, pos0, turntableDiameter/2.0, 0.0, 360.0, 0, 0, wDrawColorBlack ); + SnapPos( &pos ); + pos0 = pos; + DrawArc( &tempD, pos0, turntableDiameter/2.0, 0.0, 360.0, 0, 0, wDrawColorBlack ); + return C_CONTINUE; + + case C_UP: + DrawArc( &tempD, pos0, turntableDiameter/2.0, 0.0, 360.0, 0, 0, wDrawColorBlack ); + SnapPos( &pos ); + UndoStart( _("Create Turntable"), "NewTurntable" ); + t = NewTurntable( pos, turntableDiameter/2.0 ); + UndoEnd(); + DrawNewTrack(t); + InfoSubstituteControls( NULL, NULL ); + sprintf( message, "turntable-diameter-%s", curScaleName ); + wPrefSetFloat( "misc", message, turntableDiameter ); + return C_TERMINATE; + + case C_REDRAW: + DrawArc( &tempD, pos0, turntableDiameter/2.0, 0.0, 360.0, 0, 0, wDrawColorBlack ); + return C_CONTINUE; + + case C_CANCEL: + InfoSubstituteControls( NULL, NULL ); + return C_CONTINUE; + + default: + return C_CONTINUE; + } +} + + +#include "bitmaps/turntbl.xpm" + + +EXPORT void InitCmdTurntable( wMenu_p menu ) +{ + AddMenuButton( menu, CmdTurntable, "cmdTurntable", _("Turntable"), wIconCreatePixMap(turntbl_xpm), LEVEL0_50, IC_STICKY, ACCL_TURNTABLE, NULL ); +} + + +EXPORT void InitTrkTurntable( void ) +{ + T_TURNTABLE = InitObject( &turntableCmds ); + + ParamRegister( &turntablePG ); +} diff --git a/app/bin/cundo.c b/app/bin/cundo.c new file mode 100644 index 0000000..1d17503 --- /dev/null +++ b/app/bin/cundo.c @@ -0,0 +1,883 @@ +/** \file cundo.c + * Undo / redo functions. + */ + +/* 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 <stdlib.h> +#include <time.h> +#include <stdarg.h> +#include <errno.h> +#include "track.h" +#include "trackx.h" +#include "i18n.h" + +/***************************************************************************** + * + * UNDO + * + */ + +static int log_undo = 0; /**< loglevel, can only be set at compile time */ + +#define UNDO_STACK_SIZE (10) + +typedef struct { + wIndex_t modCnt; + wIndex_t newCnt; + wIndex_t delCnt; + wIndex_t trackCount; + track_p newTrks; + long undoStart; + long undoEnd; + long redoStart; + long redoEnd; + BOOL_T needRedo; + track_p * oldTail; + track_p * newTail; + char * label; + } undoStack_t, *undoStack_p; + +static undoStack_t undoStack[UNDO_STACK_SIZE]; +static wIndex_t undoHead = -1; +static BOOL_T undoActive = FALSE; +static int doCount = 0; +static int undoCount = 0; + +static char ModifyOp = 1; +static char DeleteOp = 2; + +static BOOL_T recordUndo = 1; + +#define UASSERT( ARG, VAL ) \ + if (!(ARG)) return UndoFail( #ARG, VAL, __FILE__, __LINE__ ) + +#define INC_UNDO_INX( INX ) {\ + if (++INX >= UNDO_STACK_SIZE) \ + INX = 0; \ + } +#define DEC_UNDO_INX( INX ) {\ + if (--INX < 0) \ + INX = UNDO_STACK_SIZE-1; \ + } + +#define BSTREAM_SIZE (4096) +typedef char streamBlocks_t[BSTREAM_SIZE]; +typedef streamBlocks_t *streamBlocks_p; +typedef struct { + dynArr_t stream_da; + long startBInx; + long end; + long curr; + } stream_t; +typedef stream_t *stream_p; +static stream_t undoStream; +static stream_t redoStream; + +static BOOL_T needAttachTrains = FALSE; + +void UndoResume( void ) +{ + LOG( log_undo, 1, ( "UndoResume()\n" ) ) + undoActive = TRUE; +} + +void UndoSuspend( void ) +{ + LOG( log_undo, 1, ( "UndoSuspend()\n" ) ) + undoActive = FALSE; +} + + +static void DumpStream( FILE * outf, stream_p stream, char * name ) +{ + long binx; + long i, j; + long off; + streamBlocks_p blk; + int zeroCnt; + static char zeros[16] = { 0 }; + fprintf( outf, "Dumping %s\n", name ); + off = stream->startBInx*BSTREAM_SIZE; + zeroCnt = 0; + for ( binx=0; binx<stream->stream_da.cnt; binx++ ) { + blk = DYNARR_N( streamBlocks_p, stream->stream_da, binx ); + for ( i=0; i<BSTREAM_SIZE; i+= 16 ) { + if ( memcmp( &((*blk)[i]), zeros, 16 ) == 0 ) { + zeroCnt++; + } else { + if ( zeroCnt == 2 ) + fprintf( outf, "%6.6lx 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n", off-16 ); + zeroCnt = 0; + } + if ( zeroCnt <= 1 ) { + fprintf( outf, "%6.6lx ", off ); + for ( j=0; j<16; j++ ) { + fprintf( outf, "%2.2x ", (unsigned char)((*blk)[i+j]) ); + } + fprintf( outf, "\n" ); + } else if ( zeroCnt == 3 ) { + fprintf( outf, "%6.6lx .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..\n", off ); + } + off += 16; + } + } + if ( zeroCnt > 2 ) + fprintf( outf, "%6.6lx 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n", off-16 ); +} + +static BOOL_T UndoFail( char * cause, long val, char * fileName, int lineNumber ) +{ + int inx, cnt; + undoStack_p us; + FILE * outf; + time_t clock; + char temp[STR_SIZE]; + NoticeMessage( MSG_UNDO_ASSERT, _("Ok"), NULL, fileName, lineNumber, val, val, cause ); + sprintf( temp, "%s%s%s", workingDir, FILE_SEP_CHAR, sUndoF ); + outf = fopen( temp, "a+" ); + if ( outf == NULL ) { + NoticeMessage( MSG_OPEN_FAIL, _("Ok"), NULL, _("Undo Trace"), temp, strerror(errno) ); + return FALSE; + } + time( &clock ); + fprintf(outf, "\nUndo Assert: %s @ %s:%d (%s)\n", cause, fileName, lineNumber, ctime(&clock) ); + fprintf(outf, "Val = %ld(%lx)\n", val, val ); + fprintf(outf, "to_first=%lx, to_last=%lx\n", (long)to_first, (long)to_last ); + fprintf(outf, "undoHead=%d, doCount=%d, undoCount=%d\n", undoHead, doCount, undoCount ); + if (undoHead >= 0 && undoHead < UNDO_STACK_SIZE) + inx=undoHead; + else + inx = 0; + for (cnt=0; cnt<UNDO_STACK_SIZE; cnt++) { + us = &undoStack[inx]; + fprintf( outf, "US[%d]: M:%d N:%d D:%d TC:%d NT:%lx OT:%lx NT:%lx US:%lx UE:%lx RS:%lx RE:%lx NR:%d\n", + inx, us->modCnt, us->newCnt, us->delCnt, us->trackCount, + (long)us->newTrks, (long)us->oldTail, (long)us->newTail, + us->undoStart, us->undoEnd, us->redoStart, us->redoEnd, us->needRedo ); + INC_UNDO_INX(inx); + } + fprintf( outf, "Undo: SBI:%ld E:%lx C:%lx SC:%d SM:%d\n", + undoStream.startBInx, undoStream.end, undoStream.curr, undoStream.stream_da.cnt, undoStream.stream_da.max ); + fprintf( outf, "Redo: SBI:%ld E:%lx C:%lx SC:%d SM:%d\n", + redoStream.startBInx, redoStream.end, redoStream.curr, redoStream.stream_da.cnt, redoStream.stream_da.max ); + DumpStream( outf, &undoStream, "undoStream" ); + DumpStream( outf, &redoStream, "redoStream" ); + Rdump(outf); + fclose( outf ); + UndoClear(); + UndoStart( "undoFail", "undoFail" ); + return FALSE; +} + + +BOOL_T ReadStream( stream_t * stream, void * ptr, int size ) +{ + long binx, boff, brem; + streamBlocks_p blk; + if ( stream->curr+size > stream->end ) { + UndoFail( "Overrun on stream", (long)(stream->curr+size), __FILE__, __LINE__ ); + return FALSE; + } +LOG( log_undo, 5, ( "ReadStream( , %lx, %d ) %ld %ld %ld\n", (long)ptr, size, stream->startBInx, stream->curr, stream->end ) ) + binx = stream->curr/BSTREAM_SIZE; + boff = stream->curr%BSTREAM_SIZE; + stream->curr += size; + binx -= stream->startBInx; + brem = BSTREAM_SIZE - boff; + while ( brem < size ) { + UASSERT( binx>=0 && binx < stream->stream_da.cnt, binx ); + blk = DYNARR_N( streamBlocks_p, stream->stream_da, binx ); + memcpy( ptr, &(*blk)[boff], (size_t)brem ); + ptr = (char*)ptr + brem; + size -= (int)brem; + binx++; + boff = 0; + brem = BSTREAM_SIZE; + } + if (size) { + UASSERT( binx>=0 && binx < stream->stream_da.cnt, binx ); + blk = DYNARR_N( streamBlocks_p, stream->stream_da, binx ); + memcpy( ptr, &(*blk)[boff], size ); + } + return TRUE; +} + +BOOL_T WriteStream( stream_p stream, void * ptr, int size ) +{ + long binx, boff, brem; + streamBlocks_p blk; +LOG( log_undo, 5, ( "WriteStream( , %lx, %d ) %ld %ld %ld\n", (long)ptr, size, stream->startBInx, stream->curr, stream->end ) ) + if (size == 0) + return TRUE; + binx = stream->end/BSTREAM_SIZE; + boff = stream->end%BSTREAM_SIZE; + stream->end += size; + binx -= stream->startBInx; + brem = BSTREAM_SIZE - boff; + while ( size ) { + if (boff==0) { + UASSERT( binx == stream->stream_da.cnt, binx ); + DYNARR_APPEND( streamBlocks_p, stream->stream_da, 10 ); + blk = (streamBlocks_p)MyMalloc( sizeof *blk ); + DYNARR_N( streamBlocks_p, stream->stream_da, binx ) = blk; + } else { + UASSERT( binx == stream->stream_da.cnt-1, binx ); + blk = DYNARR_N( streamBlocks_p, stream->stream_da, binx ); + } + if (size > brem) { + memcpy( &(*blk)[boff], ptr, (size_t)brem ); + ptr = (char*)ptr + brem; + size -= (size_t)brem; + binx++; + boff = 0; + brem = BSTREAM_SIZE; + } else { + memcpy( &(*blk)[boff], ptr, size ); + break; + } + } + return TRUE; +} + +BOOL_T TrimStream( stream_p stream, long off ) +{ + long binx, cnt, inx; + streamBlocks_p blk; +LOG( log_undo, 3, ( "TrimStream( , %ld )\n", off ) ) + binx = off/BSTREAM_SIZE; + cnt = binx-stream->startBInx; + if (recordUndo) + Rprintf("Trim(%ld) %ld blocks (out of %d)\n", off, cnt, stream->stream_da.cnt); + UASSERT( cnt >= 0 && cnt <= stream->stream_da.cnt, cnt ); + if (cnt == 0) + return TRUE; + for (inx=0; inx<cnt; inx++) { + blk = DYNARR_N( streamBlocks_p, stream->stream_da, inx ); + MyFree( blk ); + } + for (inx=cnt; inx<stream->stream_da.cnt; inx++ ) { + DYNARR_N( streamBlocks_p, stream->stream_da, inx-cnt ) = DYNARR_N( streamBlocks_p, stream->stream_da, inx ); + } + stream->startBInx = binx; + stream->stream_da.cnt -= (wIndex_t)cnt; + UASSERT( stream->stream_da.cnt >= 0, stream->stream_da.cnt ); + return TRUE; +} + + +void ClearStream( stream_p stream ) +{ + long inx; + streamBlocks_p blk; + for (inx=0; inx<stream->stream_da.cnt; inx++) { + blk = DYNARR_N( streamBlocks_p, stream->stream_da, inx ); + MyFree( blk ); + } + stream->stream_da.cnt = 0; + stream->startBInx = stream->end = stream->curr = 0; +} + + +BOOL_T TruncateStream( stream_p stream, long off ) +{ + long binx, boff, cnt, inx; + streamBlocks_p blk; +LOG( log_undo, 3, ( "TruncateStream( , %ld )\n", off ) ) + binx = off/BSTREAM_SIZE; + boff = off%BSTREAM_SIZE; + if (boff!=0) + binx++; + binx -= stream->startBInx; + cnt = stream->stream_da.cnt-binx; + if (recordUndo) + Rprintf("Truncate(%ld) %ld blocks (out of %d)\n", off, cnt, stream->stream_da.cnt); + UASSERT( cnt >= 0 && cnt <= stream->stream_da.cnt, cnt ); + if (cnt == 0) + return TRUE; + for (inx=binx; inx<stream->stream_da.cnt; inx++) { + blk = DYNARR_N( streamBlocks_p, stream->stream_da, inx ); + MyFree( blk ); + } + stream->stream_da.cnt = (wIndex_t)binx; + stream->end = off; + UASSERT( stream->stream_da.cnt >= 0, stream->stream_da.cnt ); + return TRUE; +} + +BOOL_T WriteObject( stream_p stream, char op, track_p trk ) +{ + if (!WriteStream( stream, &op, sizeof op ) || + !WriteStream( stream, &trk, sizeof trk ) || + !WriteStream( stream, trk, sizeof *trk ) || + !WriteStream( stream, trk->endPt, trk->endCnt * sizeof trk->endPt[0] ) || + !WriteStream( stream, trk->extraData, trk->extraSize ) ) { + return FALSE; + } + return TRUE; +} + + +static BOOL_T ReadObject( stream_p stream, BOOL_T needRedo ) +{ + track_p trk; + track_t tempTrk; + char op; + if (!ReadStream( stream, &op, sizeof op )) + return FALSE; + if (!ReadStream( stream, &trk, sizeof trk )) + return FALSE; + if (needRedo) { + if (!WriteObject( &redoStream, op, trk )) + return FALSE; + } + if (!ReadStream( stream, &tempTrk, sizeof tempTrk )) + return FALSE; + if (tempTrk.endCnt != trk->endCnt) + tempTrk.endPt = MyRealloc( trk->endPt, tempTrk.endCnt * sizeof tempTrk.endPt[0] ); + else + tempTrk.endPt = trk->endPt; + if (!ReadStream( stream, tempTrk.endPt, tempTrk.endCnt * sizeof tempTrk.endPt[0] )) + return FALSE; + if (tempTrk.extraSize != trk->extraSize) + tempTrk.extraData = MyRealloc( trk->extraData, tempTrk.extraSize ); + else + tempTrk.extraData = trk->extraData; + if (!ReadStream( stream, tempTrk.extraData, tempTrk.extraSize )) + return FALSE; + if (recordUndo) Rprintf( "Restore T%D(%d) @ %lx\n", trk->index, tempTrk.index, (long)trk ); + tempTrk.index = trk->index; + tempTrk.next = trk->next; + if ( (tempTrk.bits&TB_CARATTACHED) != 0 ) + needAttachTrains = TRUE; + tempTrk.bits &= ~TB_TEMPBITS; + *trk = tempTrk; + if (!trk->deleted) + ClrTrkElev( trk ); + return TRUE; +} + + +static void RedrawInStream( stream_p stream, long start, long end, BOOL_T draw ) +{ + char op; + track_p trk; + track_t tempTrk; + stream->curr = start; + while (stream->curr < end ) { + if (!ReadStream( stream, &op, sizeof op ) || + !ReadStream( stream, &trk, sizeof trk ) || + !ReadStream( stream, &tempTrk, sizeof tempTrk ) ) + return; + stream->curr += tempTrk.extraSize + tempTrk.endCnt*sizeof tempTrk.endPt[0]; + if (!trk->deleted) { + if (draw) + DrawNewTrack( trk ); + else + UndrawNewTrack( trk ); + } + } +} + + +static BOOL_T DeleteInStream( stream_p stream, long start, long end ) +{ + char op; + track_p trk; + track_p *ptrk; + track_t tempTrk; + int delCount = 0; +LOG( log_undo, 3, ( "DeleteInSteam( , %ld, %ld )\n", start, end ) ) + stream->curr = start; + while (stream->curr < end ) { + if (!ReadStream( stream, &op, sizeof op )) + return FALSE; + UASSERT( op == ModifyOp || op == DeleteOp, (long)op ); + if (!ReadStream( stream, &trk, sizeof trk ) || + !ReadStream( stream, &tempTrk, sizeof tempTrk ) ) + return FALSE; + stream->curr += tempTrk.extraSize + tempTrk.endCnt*sizeof tempTrk.endPt[0]; + if (op == DeleteOp) { + if (recordUndo) Rprintf( " Free T%D(%d) @ %lx\n", trk->index, tempTrk.index, (long)trk ); + UASSERT( IsTrackDeleted(trk), (long)trk ); + trk->index = -1; + delCount++; + } + } + if (delCount) { + for (ptrk=&to_first; *ptrk; ) { + if ((*ptrk)->index == -1) { + trk = *ptrk; + UASSERT( IsTrackDeleted(trk), (long)trk ); + *ptrk = trk->next; + FreeTrack(trk); + } else { + ptrk = &(*ptrk)->next; + } + } + to_last = ptrk; + } + return TRUE; +} + + +static BOOL_T SetDeleteOpInStream( stream_p stream, long start, long end, track_p trk0 ) +{ + char op; + track_p trk; + track_t tempTrk; + long binx, boff; + streamBlocks_p blk; + + stream->curr = start; + while (stream->curr < end) { + binx = stream->curr/BSTREAM_SIZE; + binx -= stream->startBInx; + boff = stream->curr%BSTREAM_SIZE; + if (!ReadStream( stream, &op, sizeof op )) + return FALSE; + UASSERT( op == ModifyOp || op == DeleteOp, (long)op ); + if (!ReadStream( stream, &trk, sizeof trk ) ) + return FALSE; + if (trk == trk0) { + UASSERT( op == ModifyOp, (long)op ); + blk = DYNARR_N( streamBlocks_p, stream->stream_da, binx ); + memcpy( &(*blk)[boff], &DeleteOp, sizeof DeleteOp ); + return TRUE; + } + if (!ReadStream( stream, &tempTrk, sizeof tempTrk )) + return FALSE; + stream->curr += tempTrk.extraSize + tempTrk.endCnt*sizeof tempTrk.endPt[0]; + } + UASSERT( "Cannot find undo record to convert to DeleteOp", 0 ); + return FALSE; +} + + +static void SetButtons( BOOL_T undoSetting, BOOL_T redoSetting ) +{ + static BOOL_T undoButtonEnabled = FALSE; + static BOOL_T redoButtonEnabled = FALSE; + int index; + static char undoHelp[STR_SHORT_SIZE]; + static char redoHelp[STR_SHORT_SIZE]; + + if (undoButtonEnabled != undoSetting) { + wControlActive( (wControl_p)undoB, undoSetting ); + undoButtonEnabled = undoSetting; + } + if (redoButtonEnabled != redoSetting) { + wControlActive( (wControl_p)redoB, redoSetting ); + redoButtonEnabled = redoSetting; + } + if (undoSetting) { + sprintf( undoHelp, _("Undo: %s"), undoStack[undoHead].label ); + wControlSetBalloonText( (wControl_p)undoB, undoHelp ); + } else { + wControlSetBalloonText( (wControl_p)undoB, _("Undo last command") ); + } + if (redoSetting) { + index = undoHead; + INC_UNDO_INX(index); + sprintf( redoHelp, _("Redo: %s"), undoStack[index].label ); + wControlSetBalloonText( (wControl_p)redoB, redoHelp ); + } else { + wControlSetBalloonText( (wControl_p)redoB, _("Redo last undo") ); + } +} + + +static track_p * FindParent( track_p trk, int lineNum ) +{ + track_p *ptrk; + ptrk = &to_first; + while ( 1 ) { + if ( *ptrk == trk ) + return ptrk; + if (*ptrk == NULL) + break; + ptrk = &(*ptrk)->next; + } + UndoFail( "Cannot find trk on list", (long)trk, "cundo.c", lineNum ); + return NULL; +} + + +static int undoIgnoreEmpty = 0; +void UndoStart( + char * label, + char * format, + ... ) +{ + static char buff[STR_SIZE]; + va_list ap; + track_p trk, next; + undoStack_p us, us1; + int inx; + int usp; + +LOG( log_undo, 1, ( "UndoStart(%s) [%d] d:%d u:%d us:%ld\n", label, undoHead, doCount, undoCount, undoStream.end ) ) + if (recordUndo) { + va_start( ap, format ); + vsprintf( buff, format, ap ); + va_end( ap ); + Rprintf( "Start(%s)[%d] d:%d u:%d us:%ld\n", buff, undoHead, doCount, undoCount, undoStream.end ); + } + + if ( undoHead >= 0 ) { + us = &undoStack[undoHead]; + if ( us->modCnt == 0 && us->delCnt == 0 && us->newCnt == 0 ) { +#ifndef WINDOWS +#ifdef DEBUG + printf( "undoStart noop: %s - %s\n", us->label?us->label:"<>", label?label:"<>" ); +#endif +#endif + if ( undoIgnoreEmpty ) { + us->label = label; + return; + } + } + } + + INC_UNDO_INX(undoHead); + us = &undoStack[undoHead]; + changed++; + SetWindowTitle(); + if (doCount == UNDO_STACK_SIZE) { + if (recordUndo) Rprintf( " Wrapped N:%d M:%d D:%d\n", us->newCnt, us->modCnt, us->delCnt ); + /* wrapped around stack */ + /* if track saved in undoStream is deleted then really deleted since + we can't get it back */ + if (!DeleteInStream( &undoStream, us->undoStart, us->undoEnd )) + return; + /* strip off unused head of stream */ + if (!TrimStream( &undoStream, us->undoEnd )) + return; + } else if (undoCount != 0) { + if (recordUndo) Rprintf( " Undid N:%d M:%d D:%d\n", us->newCnt, us->modCnt, us->delCnt ); + /* reusing an undid entry */ + /* really delete all new tracks since this point */ + for( inx=0,usp = undoHead; inx<undoCount; inx++ ) { + us1 = &undoStack[usp]; + if (recordUndo) Rprintf(" U[%d] N:%d\n", usp, us1->newCnt ); + for (trk=us1->newTrks; trk; trk=next) { + if (recordUndo) Rprintf( " Free T%d @ %lx\n", trk->index, (long)trk ); + /*ASSERT( IsTrackDeleted(trk) );*/ + next = trk->next; + FreeTrack( trk ); + } + INC_UNDO_INX(usp); + } + /* strip off unused tail of stream */ + if (!TruncateStream( &undoStream, us->undoStart )) + return; + } + us->label = label; + us->modCnt = 0; + us->newCnt = 0; + us->delCnt = 0; + us->undoStart = us->undoEnd = undoStream.end; + ClearStream( &redoStream ); + for ( inx=0; inx<UNDO_STACK_SIZE; inx++ ) { + undoStack[inx].needRedo = TRUE; + undoStack[inx].oldTail = NULL; + undoStack[inx].newTail = NULL; + } + us->newTrks = NULL; + undoStack[undoHead].trackCount = trackCount; + undoCount = 0; + undoActive = TRUE; + for (trk=to_first; trk; trk=trk->next ) { + trk->modified = FALSE; + trk->new = FALSE; + } + if (doCount < UNDO_STACK_SIZE) + doCount++; + SetButtons( TRUE, FALSE ); +} + + +BOOL_T UndoModify( track_p trk ) +{ + undoStack_p us; + + if ( !undoActive ) return TRUE; + if (trk == NULL) return TRUE; + UASSERT(undoCount==0, undoCount); + UASSERT(undoHead >= 0, undoHead); + UASSERT(!IsTrackDeleted(trk), (long)trk); + if (trk->modified || trk->new) + return TRUE; +LOG( log_undo, 2, ( " UndoModify( T%d, E%d, X%ld )\n", trk->index, trk->endCnt, trk->extraSize ) ) + if ( (GetTrkBits(trk)&TB_CARATTACHED)!=0 ) + needAttachTrains = TRUE; + us = &undoStack[undoHead]; + if (recordUndo) + Rprintf( " MOD T%d @ %lx\n", trk->index, (long)trk ); + if (!WriteObject( &undoStream, ModifyOp, trk )) + return FALSE; + us->undoEnd = undoStream.end; + trk->modified = TRUE; + us->modCnt++; + return TRUE; +} + + +BOOL_T UndoDelete( track_p trk ) +{ + undoStack_p us; + if ( !undoActive ) return TRUE; +LOG( log_undo, 2, ( " UndoDelete( T%d, E%d, X%ld )\n", trk->index, trk->endCnt, trk->extraSize ) ) + if ( (GetTrkBits(trk)&TB_CARATTACHED)!=0 ) + needAttachTrains = TRUE; + us = &undoStack[undoHead]; + if (recordUndo) + Rprintf( " DEL T%d @ %lx\n", trk->index, (long)trk ); + UASSERT( !IsTrackDeleted(trk), (long)trk ); + if ( trk->modified ) { + if (!SetDeleteOpInStream( &undoStream, us->undoStart, us->undoEnd, trk )) + return FALSE; + } else if ( !trk->new ) { + if (!WriteObject( &undoStream, DeleteOp, trk )) + return FALSE; + us->undoEnd = undoStream.end; + } else { + track_p * ptrk; + if (us->newTrks == trk) + us->newTrks = trk->next; + if (!(ptrk = FindParent( trk, __LINE__ ))) + return FALSE; + if (trk->next == NULL) { + UASSERT( to_last == &(*ptrk)->next, (long)&(*ptrk)->next ); + to_last = ptrk; + } + *ptrk = trk->next; + FreeTrack( trk ); + us->newCnt--; + return TRUE; + } + trk->deleted = TRUE; + us->delCnt++; + return TRUE; +} + + +BOOL_T UndoNew( track_p trk ) +{ + undoStack_p us; + if (!undoActive) + return TRUE; + +LOG( log_undo, 2, ( " UndoNew( T%d )\n", trk->index ) ) + + if (recordUndo) + Rprintf( " NEW T%d @%lx\n", trk->index, (long)trk ); + UASSERT(undoCount==0, undoCount); + UASSERT(undoHead >= 0, undoHead); + us = &undoStack[undoHead]; + trk->new = TRUE; + if (us->newTrks == NULL) + us->newTrks = trk; + us->newCnt++; + + return TRUE; +} + + +void UndoEnd( void ) +{ + if (recordUndo) Rprintf( "End[%d] d:%d\n", undoHead, doCount ); + /*undoActive = FALSE;*/ + if ( needAttachTrains ) { + AttachTrains(); + needAttachTrains = FALSE; + } + UpdateAllElevations(); +} + + +void UndoClear( void ) +{ + int inx; +LOG( log_undo, 2, ( " UndoClear()\n" ) ) + undoActive = FALSE; + undoHead = -1; + undoCount = 0; + doCount = 0; + ClearStream( &undoStream ); + ClearStream( &redoStream ); + for (inx=0; inx<UNDO_STACK_SIZE; inx++) { + undoStack[inx].undoStart = undoStack[inx].undoEnd = 0; + } + SetButtons( FALSE, FALSE ); +} + + +BOOL_T UndoUndo( void ) +{ + undoStack_p us; + track_p trk; + wIndex_t oldCount; + BOOL_T redrawAll; + + if (doCount <= 0) { + ErrorMessage( MSG_NO_UNDO ); + return FALSE; + } + + ConfirmReset( FALSE ); + wDrawDelayUpdate( mainD.d, TRUE ); + us = &undoStack[undoHead]; +LOG( log_undo, 1, ( " undoUndo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doCount, undoCount, us->newCnt, us->modCnt, us->delCnt ) ) + if (recordUndo) Rprintf( "Undo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doCount, undoCount, us->newCnt, us->modCnt, us->delCnt ); + + //redrawAll = (us->newCnt+us->modCnt) > incrementalDrawLimit; + redrawAll = TRUE; + if (!redrawAll) { + for (trk=us->newTrks; trk; trk=trk->next ) + UndrawNewTrack( trk ); + RedrawInStream( &undoStream, us->undoStart, us->undoEnd, FALSE ); + } + + if (us->needRedo) + us->redoStart = us->redoEnd = redoStream.end; + for (trk=us->newTrks; trk; trk=trk->next ) { + if (recordUndo) Rprintf(" Deleting New Track T%d @ %lx\n", trk->index, (long)trk ); + UASSERT( !IsTrackDeleted(trk), (long)trk ); + trk->deleted = TRUE; + } + if (!(us->oldTail=FindParent(us->newTrks,__LINE__))) + return FALSE; + us->newTail = to_last; + to_last = us->oldTail; + *to_last = NULL; + + needAttachTrains = FALSE; + undoStream.curr = us->undoStart; + while ( undoStream.curr < us->undoEnd ) { + if (!ReadObject( &undoStream, us->needRedo )) + return FALSE; + } + if (us->needRedo) + us->redoEnd = redoStream.end; + us->needRedo = FALSE; + + if ( needAttachTrains ) { + AttachTrains(); + needAttachTrains = FALSE; + } + UpdateAllElevations(); + if (!redrawAll) + RedrawInStream( &undoStream, us->undoStart, us->undoEnd, TRUE ); + else + DoRedraw(); + + oldCount = trackCount; + trackCount = us->trackCount; + us->trackCount = oldCount; + InfoCount( trackCount ); + + doCount--; + undoCount++; + DEC_UNDO_INX( undoHead ); + AuditTracks( "undoUndo" ); + SelectRecount(); + SetButtons( doCount>0, TRUE ); + wBalloonHelpUpdate(); + wDrawDelayUpdate( mainD.d, FALSE ); + return TRUE; +} + + +BOOL_T UndoRedo( void ) +{ + undoStack_p us; + wIndex_t oldCount; + BOOL_T redrawAll; + track_p trk; + + if (undoCount <= 0) { + ErrorMessage( MSG_NO_REDO ); + return FALSE; + } + + ConfirmReset( FALSE ); + wDrawDelayUpdate( mainD.d, TRUE ); + INC_UNDO_INX( undoHead ); + us = &undoStack[undoHead]; +LOG( log_undo, 1, ( " undoRedo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doCount, undoCount, us->newCnt, us->modCnt, us->delCnt ) ) + if (recordUndo) Rprintf( "Redo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doCount, undoCount, us->newCnt, us->modCnt, us->delCnt ); + + //redrawAll = (us->newCnt+us->modCnt) > incrementalDrawLimit; + redrawAll = TRUE; + if (!redrawAll) { + RedrawInStream( &redoStream, us->redoStart, us->redoEnd, FALSE ); + } + + for (trk=us->newTrks; trk; trk=trk->next ) { + if (recordUndo) Rprintf(" Undeleting New Track T%d @ %lx\n", trk->index, (long)trk ); + UASSERT( IsTrackDeleted(trk), (long)trk ); + trk->deleted = FALSE; + } + UASSERT( us->newTail != NULL, (long)us->newTail ); + *to_last = us->newTrks; + to_last = us->newTail; + UASSERT( (*to_last) == NULL, (long)*to_last ); + RenumberTracks(); + + needAttachTrains = FALSE; + redoStream.curr = us->redoStart; + while ( redoStream.curr < us->redoEnd ) { + if (!ReadObject( &redoStream, FALSE )) + return FALSE; + } + + if ( needAttachTrains ) { + AttachTrains(); + needAttachTrains = FALSE; + } + UpdateAllElevations(); + if (!redrawAll) { + for (trk=us->newTrks; trk; trk=trk->next ) + DrawNewTrack( trk ); + RedrawInStream( &redoStream, us->redoStart, us->redoEnd, TRUE ); + } else + DoRedraw(); + + oldCount = trackCount; + trackCount = us->trackCount; + us->trackCount = oldCount; + InfoCount( trackCount ); + + undoCount--; + doCount++; + + AuditTracks( "undoRedo" ); + SelectRecount(); + SetButtons( TRUE, undoCount>0 ); + wBalloonHelpUpdate(); + wDrawDelayUpdate( mainD.d, FALSE ); + return TRUE; +} + + +void InitCmdUndo( void ) +{ + log_undo = LogFindIndex( "undo" ); +} diff --git a/app/bin/cundo.h b/app/bin/cundo.h new file mode 100644 index 0000000..ef767ae --- /dev/null +++ b/app/bin/cundo.h @@ -0,0 +1,32 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cundo.h,v 1.1 2005-12-07 15:46:54 rc-flyer 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. + */ + +int UndoUndo( void ); +int UndoRedo( void ); +void UndoResume( void ); +void UndoSuspend( void ); +void UndoStart( char *, char *, ... ); +BOOL_T UndoModify( track_p ); +BOOL_T UndoDelete( track_p ); +BOOL_T UndoNew( track_p ); +void UndoEnd( void ); +void UndoClear( void ); diff --git a/app/bin/custom.c b/app/bin/custom.c new file mode 100644 index 0000000..61338d6 --- /dev/null +++ b/app/bin/custom.c @@ -0,0 +1,258 @@ +#define RENAME_H +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/custom.c,v 1.14 2010-01-01 13:24:59 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 <stdlib.h> +#include <stdio.h> +#ifndef WINDOWS +#include <unistd.h> +#include <dirent.h> +#endif +#include <math.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#ifdef WINDOWS +#include <io.h> +#include <windows.h> +#else +#include <sys/stat.h> +#endif +#include <stdarg.h> +#include <errno.h> + +#include "track.h" +#include "version.h" +#include "common.h" +#include "misc.h" +#include "fileio.h" +#include "cjoin.h" +#include "i18n.h" + +#define Product "XTrackCAD" +#define product "xtrkcad" +#define PRODUCT "XTRKCAD" +#define Version VERSION +#define KEYCODE "x" +#define PARAMKEY (0) + + +char * sProdName = Product; +char * sProdNameLower = product; +char * sProdNameUpper = PRODUCT; + +char * sEnvExtra = PRODUCT "EXTRA"; + +char * sTurnoutDesignerW = NULL; + +char * sAboutProd = NULL; + +char * sCustomF = product ".cus"; +char * sCheckPointF = product ".ckp"; +char * sCheckPoint1F = product ".ck1"; +char * sClipboardF = product ".clp"; +char * sParamQF = product "." KEYCODE "tq"; +char * sUndoF = product ".und"; +char * sAuditF = product ".aud"; + +char * sSourceFilePattern = NULL; +char * sImportFilePattern = NULL; +char * sDXFFilePattern = NULL; +char * sRecordFilePattern = NULL; +char * sNoteFilePattern = NULL; +char * sLogFilePattern = NULL; +char * sPartsListFilePattern = NULL; + +char * sVersion = Version; +int iParamVersion = PARAMVERSION; +int iMinParamVersion = MINPARAMVERSION; +long lParamKey = PARAMKEY; + +extern char *userLocale; + +EXPORT char * MakeWindowTitle( char * name ) +{ + static char title[STR_SHORT_SIZE]; + sprintf( title, "%s", name ); + return title; +} + +static addButtonCallBack_t easementP; + +void InitCmdEasement( void ) +{ + easementP = EasementInit(); +} +void DoEasementRedir( void ) +{ + if (easementP) + easementP(NULL); +} + +#ifdef STRUCTDESIGNER +static addButtonCallBack_t structDesignerP; +void DoStructDesignerRedir( void ) +{ + if (structDesignerP) + structDesignerP(NULL); +} +#endif + +/** + * Initialize track commands + * + * \return always TRUE + */ + +BOOL_T Initialize( void ) +{ + InitTrkCurve(); + InitTrkStraight(); + InitTrkEase(); + InitTrkTurnout(); + InitTrkTurntable(); + InitTrkStruct(); + InitTrkText(); + InitTrkDraw(); + InitTrkNote(); + +#ifdef XTRKCAD_USE_LAYOUTCONTROL + InitTrkBlock(); + InitTrkSwitchMotor(); +#endif + InitCarDlg(); + + memset( message, 0, sizeof message ); + + return TRUE; +} + +/** + * Initialize siome localized strings for filename patterns etc. + */ + +void InitCustom( void ) +{ + char buf[STR_SHORT_SIZE]; + + /* Initialize some localized strings */ + if (sTurnoutDesignerW == NULL) + { + sprintf(buf, _("%s Turnout Designer"), Product); + sTurnoutDesignerW = strdup(buf); + } + if (sAboutProd == NULL) + { + sprintf(buf, _("%s Version %s"), Product, Version); + sAboutProd = strdup(buf); + } + if (sSourceFilePattern == NULL) + { + sprintf(buf, _("%s Files|*.xtc"), Product); + sSourceFilePattern = strdup(buf); + } + if (sImportFilePattern == NULL) + { + sprintf(buf, _("%s Import Files|*.%sti"), Product, KEYCODE); + sImportFilePattern = strdup(buf); + } + if (sDXFFilePattern == NULL) + { + sDXFFilePattern = strdup(_("Data Exchange Format Files|*.dxf")); + } + if (sRecordFilePattern == NULL) + { + sprintf(buf, _("%s Record Files|*.%str"), Product, KEYCODE); + sRecordFilePattern = strdup(buf); + } + if (sNoteFilePattern == NULL) + { + sprintf(buf, _("%s Note Files|*.not"), Product); + sNoteFilePattern = strdup(buf); + } + if (sLogFilePattern == NULL) + { + sprintf(buf, _("%s Log Files|*.log"), Product); + sLogFilePattern = strdup(buf); + } + if (sPartsListFilePattern == NULL) + { + sprintf(buf, _("%s PartsList Files|*.txt"), Product); + sPartsListFilePattern = strdup(buf); + } +} + + +void CleanupCustom( void ) +{ + /* Free dynamically allocated strings */ + if (sTurnoutDesignerW) + { + free(sTurnoutDesignerW); + sTurnoutDesignerW = NULL; + } + if (sAboutProd) + { + free(sAboutProd); + sAboutProd = NULL; + } + if (sSourceFilePattern) + { + free(sSourceFilePattern); + sSourceFilePattern = NULL; + } + if (sImportFilePattern) + { + free(sImportFilePattern); + sImportFilePattern = NULL; + } + if (sDXFFilePattern) + { + free(sDXFFilePattern); + sDXFFilePattern = NULL; + } + if (sRecordFilePattern) + { + free(sRecordFilePattern); + sRecordFilePattern = NULL; + } + if (sNoteFilePattern) + { + free(sNoteFilePattern); + sNoteFilePattern = NULL; + } + if (sLogFilePattern) + { + free(sLogFilePattern); + sLogFilePattern = NULL; + } + if (sPartsListFilePattern) + { + free(sPartsListFilePattern); + sPartsListFilePattern = NULL; + } + if (userLocale) + { + free(userLocale); + userLocale = NULL; + } +} diff --git a/app/bin/custom.h b/app/bin/custom.h new file mode 100644 index 0000000..3987c16 --- /dev/null +++ b/app/bin/custom.h @@ -0,0 +1,147 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/custom.h,v 1.7 2010-01-01 13:24:59 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 CUSTOM_H +#define CUSTOM_H + +#define ICON_WIDTH (64) +#define ICON_HEIGHT (64) + +#define BG_SELECT (0) +#define BG_ZOOM (1) +#define BG_UNDO (2) +#define BG_EASE (3) +#define BG_TRKCRT (4) +#define BG_TRKMOD (5) +#define BG_TRKGRP (6) +#define BG_MISCCRT (7) +#define BG_RULER (8) +#define BG_LAYER (9) +#define BG_HOTBAR (10) +#define BG_SNAP (11) +#define BG_TRAIN (12) +#define BG_COUNT (13) +#define BG_FILE (14) +#define BG_CONTROL (15) +#define BG_BIGGAP (1<<8) +extern int cmdGroup; + +extern char * sProdName; +extern char * sProdNameLower; +extern char * sProdNameUpper; + +extern char * sEnvExtra; + +extern char * sTurnoutDesignerW; + +extern char * sAboutProd; + +extern char * sCustomF; +extern char * sCheckPointF; +extern char * sCheckPoint1F; +extern char * sClipboardF; +extern char * sParamQF; +extern char * sUndoF; +extern char * sAuditF; + +extern char * sSourceFilePattern; +extern char * sImportFilePattern; +extern char * sDXFFilePattern; +extern char * sRecordFilePattern; +extern char * sNoteFilePattern; +extern char * sLogFilePattern; +extern char * sPartsListFilePattern; + +extern char * sVersion; +extern int iParamVersion; +extern int iMinParamVersion; +extern long lParamKey; + +//extern int bEnablePrices; + +void InitCustom( void ); +void CleanupCustom( void ); + +void InitTrkCurve( void ); +void InitTrkDraw( void ); +void InitTrkEase( void ); +void InitTrkNote( void ); +void InitTrkStraight( void ); +void InitTrkStruct( void ); +void InitTrkTableEdge( void ); +void InitTrkText( void ); +void InitTrkTrack( void ); +void InitTrkTurnout( void ); +void InitTrkTurntable( void ); +void InitTrkBlock( void ); +void InitTrkSwitchMotor( void ); + +void InitCmdCurve( wMenu_p menu ); +void InitCmdHelix( wMenu_p menu ); +void InitCmdDraw( wMenu_p menu ); +void InitCmdElevation( wMenu_p menu ); +void InitCmdJoin( wMenu_p menu ); +void InitCmdProfile( wMenu_p menu ); +void InitCmdPull( wMenu_p menu ); +void InitCmdTighten( void ); +void InitCmdModify( wMenu_p menu ); +void InitCmdMove( wMenu_p menu ); +void InitCmdMoveDescription( wMenu_p menu ); +void InitCmdStraight( wMenu_p menu ); +void InitCmdDescribe( wMenu_p menu ); +void InitCmdSelect( wMenu_p menu ); +void InitCmdDelete( void ); +void InitCmdSplit( wMenu_p menu ); +void InitCmdTunnel( void ); +void InitCmdRuler( wMenu_p menu ); + +void InitCmdParallel( wMenu_p menu ); +wIndex_t InitCmdPrint( wMenu_p menu ); +void InitCmdTableEdge( void ); +void InitCmdText( wMenu_p menu ); +void InitCmdTrain( wMenu_p menu ); +void InitCmdTurnout( wMenu_p menu ); +void InitCmdHandLaidTurnout( wMenu_p menu ); +void InitCmdTurntable( wMenu_p menu ); +void InitCmdNote( wMenu_p menu ); +void InitCmdUndo( void ); +void InitCmdStruct( wMenu_p menu ); +void InitCmdAboveBelow( void ); +void InitCmdEnumerate( void ); +void InitCmdExport( void ); +void InitCmdEasement( void ); + +char * MakeWindowTitle( char * ); +addButtonCallBack_t EasementInit( void ); +addButtonCallBack_t StructDesignerInit( void ); + +void InitLayers( void ); +void InitHotBar( void ); +void InitCarDlg( void ); +BOOL_T Initialize( void ); +void DoEasementRedir( void ); +void DoStructDesignerRedir( void ); +void InitNewTurnRedir( wMenu_p ); +void RedrawAbout( wDraw_p, void *, wPos_t, wPos_t ); +void DoKeycheck( char * ); + +#endif diff --git a/app/bin/dbench.c b/app/bin/dbench.c new file mode 100644 index 0000000..4a32360 --- /dev/null +++ b/app/bin/dbench.c @@ -0,0 +1,455 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dbench.c,v 1.3 2008-03-06 19:35:07 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 "track.h" +#include "i18n.h" + + +/***************************************************************************** + * + * BENCH WORK + * + */ + + +#define B_RECT (0) +#define B_LGRIDER (1) +#define B_TGRIDER (2) + +static char *benchTypeS[] = { "", N_(" L-Girder"), N_(" T-Girder") }; + +#include "bitmaps/bo_edge.xpm" +#include "bitmaps/bo_flat.xpm" +#include "bitmaps/bo_ll.xpm" +#include "bitmaps/bo_lr.xpm" +#include "bitmaps/bo_lld.xpm" +#include "bitmaps/bo_lrd.xpm" +#include "bitmaps/bo_llu.xpm" +#include "bitmaps/bo_lru.xpm" +#include "bitmaps/bo_lli.xpm" +#include "bitmaps/bo_lri.xpm" +#include "bitmaps/bo_t.xpm" +#include "bitmaps/bo_tr.xpm" +#include "bitmaps/bo_tl.xpm" +#include "bitmaps/bo_ti.xpm" + +typedef struct { + char * name; + char ** xpm; + wIcon_p icon; + } orientData_t; +static orientData_t rectOrientD[] = { + { N_("On Edge"), bo_edge_xpm }, + { N_("Flat"), bo_flat_xpm } }; +static orientData_t lgirderOrientD[] = { + { N_("Left"), bo_ll_xpm }, + { N_("Right"), bo_lr_xpm }, + { N_("Left-Down"), bo_lld_xpm }, + { N_("Right-Down"), bo_lrd_xpm }, + { N_("Left-Up"), bo_llu_xpm }, + { N_("Right-Up"), bo_lru_xpm }, + { N_("Left-Inverted"), bo_lli_xpm }, + { N_("Right-Inverted"), bo_lri_xpm } }; +static orientData_t tgirderOrientD[] = { + { N_("Normal"), bo_t_xpm }, + { N_("Right"), bo_tr_xpm }, + { N_("Left"), bo_tl_xpm }, + { N_("Inverted"), bo_ti_xpm } }; + +static struct { + int cnt; + orientData_t *data; + } orientD[] = { {2, rectOrientD}, {8, lgirderOrientD}, {4, tgirderOrientD} }; + + +/* L-N R-N L-D R-D L-U R-U L-I R-I */ +static BOOL_T lgirderFlangeLeft[] = { 1, 0, 0, 1, 1, 0, 0, 1 }; +static BOOL_T lgirderFlangeDashed[] = { 1, 1, 1, 1, 0, 0, 0, 0 }; +static BOOL_T lgirderNarrow[] = { 1, 1, 0, 0, 0, 0, 1, 1 }; + +EXPORT void BenchUpdateOrientationList( + long benchData, + wList_p list ) +{ + long type; + orientData_t *op; + int cnt; + + type = (benchData>>24)&0xff; + wListClear( list ); + op = orientD[type].data; + for (cnt=orientD[type].cnt-1; cnt>=0; cnt--,op++) { +#ifdef WINDOWS + if (op->icon == NULL) + op->icon = wIconCreatePixMap( op->xpm ); + wListAddValue( list, NULL, op->icon, op ); +#else + /* gtk_combo_find is upset if we try to put anything other that a label on a list */ + wListAddValue( list, _(op->name), NULL, op ); +#endif + } + wListSetIndex( list, 0 ); +} + +typedef struct { + long type; + long width; + long height0, height1; + } benchType_t, *benchType_p; +static dynArr_t benchType_da; +#define benchType(N) DYNARR_N( benchType_t, benchType_da, N ) + +static void AddBenchTypes( + long type, + char * key, + char * defvalue ) +{ + benchType_p bt; + char *value, *cp, *cq; + value = CAST_AWAY_CONST wPrefGetString( "misc", key ); + if ( value == NULL ) { + value = defvalue; + wPrefSetString( "misc", key, value ); + } + cp = value; + while ( *cp ) { + DYNARR_APPEND( benchType_t, benchType_da, 10 ); + bt = &benchType(benchType_da.cnt-1); + bt->type = type; + bt->width = strtol( cq=cp, &cp, 10 ); + bt->height0 = strtol( cq=cp, &cp, 10 ); + bt->height1 = strtol( cq=cp, &cp, 10 ); + if ( cp == cq ) { + NoticeMessage( _("Bad BenchType for %s:\n%s"), _("Continue"), NULL, key, value ); + benchType_da.cnt--; + return; + } + } +} + + +EXPORT void BenchLoadLists( wList_p choiceL, wList_p orientL ) +{ + int inx; + long height; + long benchData; + benchType_p bt; + char * cp; + + wListClear( choiceL ); + wListClear( orientL ); + if ( benchType_da.cnt <= 0 ) { + Reset(); + return; + } + for ( inx=0; inx<benchType_da.cnt; inx++ ) { + bt = &benchType(inx); + for (height=bt->height0; height<=bt->height1; height++ ) { + benchData = bt->type<<24 | bt->width<<17 | height<<9; + sprintf( message, "%s", (bt->type==B_LGRIDER?"L-":bt->type==B_TGRIDER?"T-":"") ); + cp = message+strlen(message); + if ( units==UNITS_ENGLISH ) + sprintf( cp, "%ld\"x%ld\"", bt->width, height ); + else + sprintf( cp, "%ldmm x %ldmm", height*25, bt->width*25 ); + wListAddValue( choiceL, message, NULL, (void*)benchData ); + } + } + BenchUpdateOrientationList( benchType(0).type<<24, orientL ); + wListSetIndex( choiceL, 0 ); +} + + +EXPORT long GetBenchData( + long benchData, + long orient ) +{ + return (benchData&0xFFFFFF00)|(orient&0xFF); +} + + +EXPORT wIndex_t GetBenchListIndex( + long benchData ) +{ + wIndex_t inx, cnt; + benchType_p bt; + long type; + long iwidth, iheight; + + iheight = (benchData>>9)&0xff; + iwidth = (benchData>>17)&0x7f; + type = (benchData>>24)&0xff; + + for ( inx=cnt=0; inx<benchType_da.cnt; inx++ ) { + bt = &benchType(inx); + if ( bt->type == type && + bt->width == iwidth ) { + if ( iheight < bt->height0 ) + bt->height0 = iheight; + else if ( iheight > bt->height1 ) + bt->height1 = iheight; + cnt += (wIndex_t)(iheight - bt->height0); + return cnt; + } + cnt += (wIndex_t)(bt->height1 - bt->height0 + 1); + } + DYNARR_APPEND( benchType_t, benchType_da, 10 ); + bt = &benchType(benchType_da.cnt-1); + bt->type = type; + bt->width = iwidth; + bt->height0 = bt->height1 = iheight; + return cnt; +} + + +EXPORT void DrawBench( + drawCmd_p d, + coOrd p0, + coOrd p1, + wDrawColor color1, + wDrawColor color2, + long option, + long benchData ) +{ + long orient; + coOrd pp[4]; + ANGLE_T a; + DIST_T width, thickness=0.75; + long type; + long oldOptions; + long lwidth; + + orient = benchData&0xFF; + type = (benchData>>24)&0xff; + width = BenchGetWidth(benchData); + lwidth = (long)floor( width*d->dpi/d->scale+0.5 ); + + if ( lwidth <= 3 ) { + DrawLine( d, p0, p1, (wDrawWidth)lwidth, color1 ); + } else { + width /= 2.0; + a = FindAngle( p0, p1 ); + Translate( &pp[0], p0, a+90, width ); + Translate( &pp[1], p0, a-90, width ); + Translate( &pp[2], p1, a-90, width ); + Translate( &pp[3], p1, a+90, width ); + DrawFillPoly( d, 4, pp, color1 ); + /* Draw Outline */ + if ( /*color1 != color2 &&*/ + ( ( d->scale < ((d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale) ) || /* big enough scale */ + ( d->funcs == &tempSegDrawFuncs ) ) ) { /* DrawFillPoly didn't draw */ + DrawLine( d, pp[0], pp[1], 0, color2 ); + DrawLine( d, pp[1], pp[2], 0, color2 ); + DrawLine( d, pp[2], pp[3], 0, color2 ); + DrawLine( d, pp[3], pp[0], 0, color2 ); + if ( color1 != color2 && type != B_RECT ) { + oldOptions = d->options; + if ( type == B_LGRIDER || orient == 1 || orient == 2 ) { + if ( type == B_LGRIDER && lgirderFlangeDashed[orient] ) + d->options |= DC_DASH; + if ( (type == B_LGRIDER && lgirderFlangeLeft[orient]) || + (type == B_TGRIDER && orient == 1) ) { + Translate( &pp[0], pp[1], a+90, thickness ); + Translate( &pp[3], pp[2], a+90, thickness ); + } else { + Translate( &pp[0], pp[0], a-90, thickness ); + Translate( &pp[3], pp[3], a-90, thickness ); + } + DrawLine( d, pp[0], pp[3], 0, color2 ); + } else { + Translate( &pp[0], p0, a+90, thickness/2.0 ); + Translate( &pp[1], p0, a-90, thickness/2.0 ); + Translate( &pp[2], p1, a-90, thickness/2.0 ); + Translate( &pp[3], p1, a+90, thickness/2.0 ); + if ( orient == 0 ) + d->options |= DC_DASH; + DrawLine( d, pp[0], pp[3], 0, color2 ); + DrawLine( d, pp[1], pp[2], 0, color2 ); + } + d->options = oldOptions; + } + } + } +} + + +EXPORT addButtonCallBack_t InitBenchDialog( void ) +{ + AddBenchTypes( B_RECT, "benchtype-rect", "1 1 6 2 2 4 2 6 6 2 8 8 4 4 4" ); + AddBenchTypes( B_LGRIDER, "benchtype-lgrider", "2 4 5 3 4 6 4 5 8" ); + AddBenchTypes( B_TGRIDER, "benchtype-tgrider", "2 4 4 3 4 7 4 5 8" ); + return NULL; +} + + +EXPORT void BenchGetDesc( + long benchData, + char * desc ) +{ + long orient; + long type; + long iwidth, iheight; + char name[40]; + + orient = benchData&0xFF; + iheight = (benchData>>9)&0xff; + iwidth = (benchData>>17)&0x7f; + type = (benchData>>24)&0xff; + + if ( units==UNITS_ENGLISH ) + sprintf( name, "%ld\"x%ld\"", iwidth, iheight ); + else + sprintf( name, "%ldmm x %ldmm", iheight*25, iwidth*25 ); + + sprintf( desc, "%s%s %s", + (type==B_LGRIDER?"L - ":type==B_TGRIDER?"T - ":""), + name, + _(orientD[type].data[(int)orient].name) ); +} + +typedef struct { + long type; + long width; + long height; + DIST_T length; + } benchEnum_t, *benchEnum_p; +static dynArr_t benchEnum_da; +#define benchEnum(N) DYNARR_N( benchEnum_t, benchEnum_da, N ) + +static void PrintBenchLine( + char * line, + benchEnum_p bp ) +{ + char name[40]; + if ( units==UNITS_ENGLISH ) + sprintf( name, "%ld\"x%ld\"", bp->width, bp->height ); + else + sprintf( name, "%ldmm x %ldmm", bp->height*25, bp->width*25 ); + sprintf( line, "%s - %s%s", FormatDistance(bp->length), name, benchTypeS[bp->type] ); +} + +EXPORT void CountBench( + long benchData, + DIST_T length ) +{ + int inx; + long orient; + long type; + long iwidth, iheight; + benchEnum_p bp; + + orient = benchData&0xFF; + iheight = (benchData>>9)&0xff; + iwidth = (benchData>>17)&0x7f; + type = (benchData>>24)&0xff; + + for ( inx=0; inx<benchEnum_da.cnt; inx++ ) { + bp = &benchEnum(inx); + if ( bp->type == type && + bp->width == iwidth && + bp->height == iheight ) { + bp->length += length; + goto foundBenchEnum; + } + } + DYNARR_APPEND( benchEnum_t, benchEnum_da, 10 ); + bp = &benchEnum(benchEnum_da.cnt-1); + bp->type = type; + bp->width = iwidth; + bp->height = iheight; + bp->length = length; +foundBenchEnum: + PrintBenchLine( message, bp ); + iwidth = strlen(message); + if ( iwidth > enumerateMaxDescLen) + enumerateMaxDescLen = (int)iwidth; +} + +static int Cmp_benchEnum( + const void *p1, + const void *p2 ) +{ + benchEnum_p bp1 = (benchEnum_p)p1; + benchEnum_p bp2 = (benchEnum_p)p2; + long diff; + if ( ( diff = bp1->type-bp2->type ) != 0 ) return (int)diff; + if ( ( diff = bp1->width-bp2->width ) != 0 ) return (int)diff; + if ( ( diff = bp1->height-bp2->height ) != 0 ) return (int)diff; + return 0; +} + +EXPORT void TotalBench( void ) +{ + int inx; + char title[STR_SIZE]; + benchEnum_p bp; + + qsort( benchEnum_da.ptr, benchEnum_da.cnt, sizeof *bp, Cmp_benchEnum ); + for ( inx=0; inx<benchEnum_da.cnt; inx++ ) { + bp = &benchEnum(inx); + if ( bp->length > 0 ) { + PrintBenchLine( title, bp ); + EnumerateList( 1, 0, title ); + bp->length = 0; + } + } +} + +EXPORT long BenchInputOption( long option ) +{ + return option; +} + + +EXPORT long BenchOutputOption( long benchData ) +{ + return benchData; +} + + +EXPORT DIST_T BenchGetWidth( long benchData ) +{ + long orient; + long type; + long iwidth, iheight; + DIST_T width; + + orient = benchData&0xFF; + iheight = (benchData>>9)&0xff; + iwidth = (benchData>>17)&0x7f; + type = (benchData>>24)&0xff; + + switch (type) { + case B_LGRIDER: + width = lgirderNarrow[orient]?iwidth-0.25:iheight-0.5; + break; + case B_TGRIDER: + width = (orient==0||orient==3)?iwidth-0.25:iheight-0.5; + break; + case B_RECT: + width = (orient==0)?iwidth-0.25:iheight-0.25; + break; + default: + width = 1.0; + } + return width; +} diff --git a/app/bin/dbitmap.c b/app/bin/dbitmap.c new file mode 100644 index 0000000..a1986c0 --- /dev/null +++ b/app/bin/dbitmap.c @@ -0,0 +1,249 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dbitmap.c,v 1.3 2008-02-14 19:49:19 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 "track.h" +#include "i18n.h" + +/***************************************************************************** + * + * Print to Bitmap + * + */ + +static long outputBitMapTogglesV = 3; +static double outputBitMapDensity = 10; + +static struct wFilSel_t * bitmap_fs; +static long bitmap_w, bitmap_h; +static drawCmd_t bitmap_d = { + NULL, + &screenDrawFuncs, + 0, + 16.0, + 0.0, + {0.0, 0.0}, {1.0,1.0}, + Pix2CoOrd, CoOrd2Pix }; + + +static int SaveBitmapFile( + const char * pathName, + const char * fileName, + void * data ) +{ + coOrd p[4]; + FLOAT_T y0, y1; + wFont_p fp, fp_bi; + wFontSize_t fs; + coOrd textsize, textsize1; + + if (pathName == NULL) + return TRUE; + memcpy( curDirName, pathName, fileName-pathName ); + curDirName[fileName-pathName-1] = '\0'; + + bitmap_d.d = wBitMapCreate( (wPos_t)bitmap_w, (wPos_t)bitmap_h, 8 ); + if (bitmap_d.d == (wDraw_p)0) { + NoticeMessage( MSG_WBITMAP_FAILED, _("Ok"), NULL ); + return FALSE; + } + y0 = y1 = 0.0; + p[0].x = p[3].x = 0.0; + p[1].x = p[2].x = mapD.size.x; + p[0].y = p[1].y = 0.0; + p[2].y = p[3].y = mapD.size.y; + if ( (outputBitMapTogglesV&2) ) { + DrawRuler( &bitmap_d, p[0], p[1], 0.0, TRUE, FALSE, wDrawColorBlack ); + DrawRuler( &bitmap_d, p[0], p[3], 0.0, TRUE, TRUE, wDrawColorBlack ); + DrawRuler( &bitmap_d, p[1], p[2], 0.0, FALSE, FALSE, wDrawColorBlack ); + DrawRuler( &bitmap_d, p[3], p[2], 0.0, FALSE, TRUE, wDrawColorBlack ); + y0 = 0.37; + y1 = 0.2; + } + if ( (outputBitMapTogglesV&3) == 1) { + DrawLine( &bitmap_d, p[0], p[1], 2, wDrawColorBlack ); + DrawLine( &bitmap_d, p[0], p[3], 2, wDrawColorBlack ); + DrawLine( &bitmap_d, p[1], p[2], 2, wDrawColorBlack ); + DrawLine( &bitmap_d, p[3], p[2], 2, wDrawColorBlack ); + } + if (outputBitMapTogglesV&1) { + fp = wStandardFont( F_TIMES, FALSE, FALSE ); + fs = 18; + DrawTextSize( &mainD, Title1, fp, fs, FALSE, &textsize ); + p[0].x = (bitmap_d.size.x - (textsize.x*bitmap_d.scale))/2.0 + bitmap_d.orig.x; + p[0].y = mapD.size.y + (y1+0.30)*bitmap_d.scale; + DrawString( &bitmap_d, p[0], 0.0, Title1, fp, fs*bitmap_d.scale, wDrawColorBlack ); + DrawTextSize( &mainD, Title2, fp, fs, FALSE, &textsize ); + p[0].x = (bitmap_d.size.x - (textsize.x*bitmap_d.scale))/2.0 + bitmap_d.orig.x; + p[0].y = mapD.size.y + (y1+0.05)*bitmap_d.scale; + DrawString( &bitmap_d, p[0], 0.0, Title2, fp, fs*bitmap_d.scale, wDrawColorBlack ); + fp_bi = wStandardFont( F_TIMES, TRUE, TRUE ); + DrawTextSize( &mainD, _("Drawn with "), fp, fs, FALSE, &textsize ); + DrawTextSize( &mainD, sProdName, fp_bi, fs, FALSE, &textsize1 ); + p[0].x = (bitmap_d.size.x - ((textsize.x+textsize1.x)*bitmap_d.scale))/2.0 + bitmap_d.orig.x; + p[0].y = -(y0+0.23)*bitmap_d.scale; + DrawString( &bitmap_d, p[0], 0.0, _("Drawn with "), fp, fs*bitmap_d.scale, wDrawColorBlack ); + p[0].x += (textsize.x*bitmap_d.scale); + DrawString( &bitmap_d, p[0], 0.0, sProdName, fp_bi, fs*bitmap_d.scale, wDrawColorBlack ); + } + wDrawClip( bitmap_d.d, + (wPos_t)(-bitmap_d.orig.x/bitmap_d.scale*bitmap_d.dpi), + (wPos_t)(-bitmap_d.orig.y/bitmap_d.scale*bitmap_d.dpi), + (wPos_t)(mapD.size.x/bitmap_d.scale*bitmap_d.dpi), + (wPos_t)(mapD.size.y/bitmap_d.scale*bitmap_d.dpi) ); + wSetCursor( wCursorWait ); + InfoMessage( _("Drawing tracks to BitMap") ); + DrawSnapGrid( &bitmap_d, mapD.size, TRUE ); + if ( (outputBitMapTogglesV&4) ) + bitmap_d.options |= DC_CENTERLINE; + else + bitmap_d.options &= ~DC_CENTERLINE; + DrawTracks( &bitmap_d, bitmap_d.scale, bitmap_d.orig, bitmap_d.size ); + InfoMessage( _("Writing BitMap to file") ); + if ( wBitMapWriteFile( bitmap_d.d, pathName ) == FALSE ) { + NoticeMessage( MSG_WBITMAP_FAILED, _("Ok"), NULL ); + return FALSE; + } + InfoMessage( "" ); + wSetCursor( wCursorNormal ); + wBitMapDelete( bitmap_d.d ); + return TRUE; +} + + + +/******************************************************************************* + * + * Output BitMap Dialog + * + */ + +static wWin_p outputBitMapW; + +static char *bitmapTogglesLabels[] = { N_("Print Titles"), N_("Print Borders"), + N_("Print Centerline"), NULL }; +static paramFloatRange_t r0o1_100 = { 0.1, 100.0, 60 }; + +static paramData_t outputBitMapPLs[] = { +#define I_TOGGLES (0) + { PD_TOGGLE, &outputBitMapTogglesV, "toggles", 0, bitmapTogglesLabels }, +#define I_DENSITY (1) + { PD_FLOAT, &outputBitMapDensity, "density", PDO_DLGRESETMARGIN, &r0o1_100, N_(" dpi") }, +#define I_MSG1 (2) + { PD_MESSAGE, N_("Bitmap : 99999 by 99999 pixels"), NULL, PDO_DLGRESETMARGIN|PDO_DLGUNDERCMDBUTT|PDO_DLGWIDE, (void*)180 }, +#define I_MSG2 (3) + { PD_MESSAGE, N_("Approximate file size: 999.9Mb"), NULL, PDO_DLGUNDERCMDBUTT, (void*)180 } }; + +static paramGroup_t outputBitMapPG = { "outputbitmap", 0, outputBitMapPLs, sizeof outputBitMapPLs/sizeof outputBitMapPLs[0] }; + + +static void OutputBitMapComputeSize( void ) +{ + FLOAT_T Lborder=0.0, Rborder=0.0, Tborder=0.0, Bborder=0.0; + FLOAT_T size; + + ParamLoadData( &outputBitMapPG ); + bitmap_d.dpi = mainD.dpi; + bitmap_d.scale = mainD.dpi/outputBitMapDensity; + + if (outputBitMapTogglesV&2) { + Lborder = 0.37; + Rborder = 0.2; + Tborder = 0.2; + Bborder = 0.37; + } + if (outputBitMapTogglesV&1) { + Tborder += 0.60; + Bborder += 0.28; + } + bitmap_d.orig.x = 0.0-Lborder*bitmap_d.scale; + bitmap_d.size.x = mapD.size.x + (Lborder+Rborder)*bitmap_d.scale; + bitmap_d.orig.y = 0.0-Bborder*bitmap_d.scale; + bitmap_d.size.y = mapD.size.y + (Bborder+Tborder)*bitmap_d.scale; + bitmap_w = (long)(bitmap_d.size.x/bitmap_d.scale*bitmap_d.dpi)/*+1*/; + bitmap_h = (long)(bitmap_d.size.y/bitmap_d.scale*bitmap_d.dpi)/*+1*/; + sprintf( message, _("Bitmap : %ld by %ld pixels"), bitmap_w, bitmap_h ); + ParamLoadMessage( &outputBitMapPG, I_MSG1, message ); + size = bitmap_w * bitmap_h; + if ( size < 1e4 ) + sprintf( message, _("Approximate file size : %0.0f"), size ); + else if ( size < 1e6 ) + sprintf( message, _("Approximate file size : %0.1fKb"), (size+50.0)/1e3 ); + else + sprintf( message, _("Approximate file size : %0.1fMb"), (size+5e4)/1e6 ); + ParamLoadMessage( &outputBitMapPG, I_MSG2, message ); +} + + +static void OutputBitMapOk( void * junk ) +{ + FLOAT_T size; + if (bitmap_w>32000 || bitmap_h>32000) { + NoticeMessage( MSG_BITMAP_TOO_LARGE, _("Ok"), NULL ); + return; + } + size = bitmap_w * bitmap_h; + if (size >= 1000000) { + if (NoticeMessage(MSG_BITMAP_SIZE_WARNING, _("Yes"), _("Cancel") )==0) + return; + } + wHide( outputBitMapW ); + if (bitmap_fs == NULL) + bitmap_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Save Bitmap"), +#ifdef WINDOWS + _("Bitmap files|*.bmp"), +#else + _("Bitmap files|*.xpm"), +#endif + SaveBitmapFile, NULL ); + wFilSelect( bitmap_fs, curDirName ); +} + + + +static void OutputBitMapChange( long changes ) +{ + if ((changes&(CHANGE_UNITS|CHANGE_MAP))==0 || outputBitMapW==NULL) + return; + wControlSetLabel( outputBitMapPLs[I_DENSITY].control, units==UNITS_METRIC?"dpcm":"dpi" ); + ParamLoadControls( &outputBitMapPG ); + OutputBitMapComputeSize(); +} + + +static void DoOutputBitMap( void * junk ) +{ + if (outputBitMapW == NULL) { + outputBitMapW = ParamCreateDialog( &outputBitMapPG, MakeWindowTitle(_("BitMap")), _("Ok"), OutputBitMapOk, wHide, TRUE, NULL, 0, (paramChangeProc)OutputBitMapComputeSize ); + } + ParamLoadControls( &outputBitMapPG ); + ParamGroupRecord( &outputBitMapPG ); + OutputBitMapComputeSize(); + wShow( outputBitMapW ); +} + + +EXPORT addButtonCallBack_t OutputBitMapInit( void ) +{ + ParamRegister( &outputBitMapPG ); + RegisterChangeNotification(OutputBitMapChange); + return &DoOutputBitMap; +} diff --git a/app/bin/dcar.c b/app/bin/dcar.c new file mode 100644 index 0000000..2bbf728 --- /dev/null +++ b/app/bin/dcar.c @@ -0,0 +1,5150 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dcar.c,v 1.6 2008-03-06 19:35:07 m_fischer Exp $ + * + * TRAIN + * + */ + +/* 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 WINDOWS +#include <errno.h> +#endif +#include <ctype.h> + +#include <stdint.h> + +#include "track.h" +#include "ctrain.h" +#include "i18n.h" +#include "fileio.h" + +static int log_carList; +static int log_carInvList; +static int log_carDlgState; +static int log_carDlgList; + +static paramFloatRange_t r0_99999 = { 0, 99999, 80 }; +static paramIntegerRange_t i1_999999999 = { 1, 999999999, 80, PDO_NORANGECHECK_HIGH }; +static paramIntegerRange_t i1_9999 = { 1, 9999, 50 }; +static char * isLocoLabels[] = { "", 0 }; +static char * cplrModeLabels[] = { N_("Truck"), N_("Body"), 0 }; +static BOOL_T carProtoListChanged; +static void CarInvListAdd( carItem_p item ); +static void CarInvListUpdate( carItem_p item ); + +#define T_MANUF (0) +#define T_PROTO (1) +#define T_DESC (2) +#define T_PART (3) +#define T_ROADNAME (4) +#define T_REPMARK (5) +#define T_NUMBER (6) + +typedef struct { + char * name; + long value; + } nameLongMap_t; + + +#define CAR_DESC_COUPLER_MODE_BODY (1L<<0) +#define CAR_DESC_IS_LOCO (1L<<1) +#define CAR_DESC_IS_LOCO_MASTER (1L<<2) +#define CAR_ITEM_HASNOTES (1L<<8) +#define CAR_ITEM_ONLAYOUT (1L<<9) + +#define CAR_DESC_BITS (0x000000FF) +#define CAR_ITEM_BITS (0x0000FF00) + + +typedef struct carProto_t * carProto_p; + +typedef struct { + DIST_T carLength; + DIST_T carWidth; + DIST_T truckCenter; + DIST_T coupledLength; + } carDim_t; +typedef struct { + char * number; + FLOAT_T purchPrice; + FLOAT_T currPrice; + long condition; + long purchDate; + long serviceDate; + char * notes; + } carData_t; + +struct carItem_t { + long index; + SCALEINX_T scaleInx; + char * contentsLabel; + char * title; + carProto_p proto; + DIST_T barScale; + wDrawColor color; + long options; + long type; + carDim_t dim; + carData_t data; + wIndex_t segCnt; + trkSeg_p segPtr; + track_p car; + coOrd pos; + ANGLE_T angle; + }; + + +/* + * Utilities + */ + + + +typedef struct { + char * ptr; + int len; + } tabString_t, *tabString_p; + + +static void TabStringExtract( + char * string, + int count, + tabString_t * tabs ) +{ + int inx; + char * next = string; + + for ( inx=0; inx<count; inx++ ) { + tabs[inx].ptr = string; + if ( next ) + next = strchr( string, '\t' ); + if ( next ) { + tabs[inx].len = next-string; + string = next+1; + } else { + tabs[inx].len = strlen( string ); + string += tabs[inx].len; + } + } + if ( tabs[T_MANUF].len == 0 ) { + tabs[T_MANUF].len = 7; + tabs[T_MANUF].ptr = N_("Unknown"); + } +} + + +static char * TabStringDup( + tabString_t * tab ) +{ + char * ret; + ret = MyMalloc( tab->len+1 ); + memcpy( ret, tab->ptr, tab->len ); + ret[tab->len] = '\0'; + return ret; +} + + +static char * TabStringCpy( + char * dst, + tabString_t * tab ) +{ + memcpy( dst, tab->ptr, tab->len ); + dst[tab->len] = '\0'; + return dst+tab->len; +} + + +static int TabStringCmp( + char * src, + tabString_t * tab ) +{ + int srclen = strlen(src); + int len = srclen; + int rc; + if ( len > tab->len ) + len = tab->len; + rc = strncasecmp( src, tab->ptr, len ); + if ( rc != 0 || srclen == tab->len ) + return rc; + else if ( srclen > tab->len ) + return 1; + else + return -1; +} + + +static long TabGetLong( + tabString_t * tab ) +{ + char old_c; + long val; + if ( tab->len <= 0 ) + return 0; + old_c = tab->ptr[tab->len]; + tab->ptr[tab->len] = '\0'; + val = atol( tab->ptr ); + tab->ptr[tab->len] = old_c; + return val; +} + + +static FLOAT_T TabGetFloat( + tabString_t * tab ) +{ + char old_c; + FLOAT_T val; + if ( tab->len <= 0 ) + return 0.0; + old_c = tab->ptr[tab->len]; + tab->ptr[tab->len] = '\0'; + val = atof( tab->ptr ); + tab->ptr[tab->len] = old_c; + return val; +} + + +static void RotatePts( + int cnt, + coOrd * pts, + coOrd orig, + ANGLE_T angle ) +{ + int inx; + for ( inx=0; inx<cnt; inx++ ) { + Rotate( &pts[inx], orig, angle ); + } +} + + +static void RescalePts( + int cnt, + coOrd * pts, + FLOAT_T scale_x, + FLOAT_T scale_y ) +{ + int inx; + for ( inx=0; inx<cnt; inx++ ) { + pts[inx].x *= scale_x; + pts[inx].y *= scale_y; + } +} + + +static int lookupListIndex; +static void * LookupListElem( + dynArr_t * da, + void * key, + int (*cmpFunc)( void *, void * ), + int elem_size ) +{ + int hi, lo, mid, rc; + lo = 0; + hi = da->cnt-1; + while (lo <= hi ) { + mid = (lo+hi)/2; + rc = cmpFunc( key, DYNARR_N(void*,*da,mid) ); + if ( rc == 0 ) { + lookupListIndex = mid; + return DYNARR_N(void*,*da,mid); + } + if ( rc > 0 ) + lo = mid+1; + else + hi = mid-1; + } + if ( elem_size == 0 ) { + lookupListIndex = -1; + return NULL; + } + DYNARR_APPEND( void*, *da, 10 ); + for ( mid=da->cnt-1; mid>lo; mid-- ) + DYNARR_N(void*,*da,mid) = DYNARR_N(void*,*da,mid-1); + DYNARR_N(void*,*da,lo) = (void*)MyMalloc(elem_size); + memset( DYNARR_N(void*,*da,lo), 0, elem_size ); + lookupListIndex = lo; + return DYNARR_N(void*,*da,lo); +} + +static void RemoveListElem( + dynArr_t * da, + void * elem ) +{ + int inx; + for ( inx=0; inx<da->cnt; inx++ ) + if ( DYNARR_N(void*,*da,inx) == elem ) + break; + if ( inx>=da->cnt ) + AbortProg( "removeListElem" ); + for ( inx++; inx<da->cnt; inx++ ) + DYNARR_N(void*,*da,inx-1) = DYNARR_N(void*,*da,inx); + da->cnt--; +} + +/* + * Draw Car Parts + */ + + +#define BW (8) +#define TW (45) +#define SI (30) +#define SO (37) +static coOrd truckOutline[] = { + { -TW, -SO }, + { TW, -SO }, + { TW, -SI }, + { BW, -SI }, + { BW, SI }, + { TW, SI }, + { TW, SO }, + { -TW, SO }, + { -TW, SI }, + { -BW, SI }, + { -BW, -SI }, + { -TW, -SI } }; +#define WO ((56.6-2)/2) +#define WI ((56.6-12)/2) +#define Wd (36/2) +#define AW (8/2) +static coOrd wheelOutline[] = { + { -Wd, -WO }, + + { -AW, -WO }, + { -AW, -SI }, + { AW, -SI }, + { AW, -WO }, + + { Wd, -WO }, + { Wd, -WI }, + { AW, -WI }, + { AW, WI }, + { Wd, WI }, + { Wd, WO }, + + { AW, WO }, + { AW, SI }, + { -AW, SI }, + { -AW, WO }, + + { -Wd, WO }, + { -Wd, WI }, + { -AW, WI }, + { -AW, -WI }, + + { -Wd, -WI } }; + +static void MovePts( + int cnt, + coOrd * pts, + coOrd orig ) +{ + int inx; + for ( inx=0; inx<cnt; inx++ ) { + pts[inx].x += orig.x; + pts[inx].y += orig.y; + } +} + + +static void CarProtoDrawTruck( + drawCmd_t * d, + DIST_T width, + FLOAT_T ratio, + coOrd pos, + ANGLE_T angle ) +{ + coOrd p[24], pp; + wDrawColor color = wDrawColorBlack; + + memcpy( p, truckOutline, sizeof truckOutline ); + RescalePts( sizeof truckOutline/sizeof truckOutline[0], p, 1.0, width/56.5 ); + RescalePts( sizeof wheelOutline/sizeof wheelOutline[0], p, ratio, ratio ); + RotatePts( sizeof wheelOutline/sizeof wheelOutline[0], p, zero, angle ); + MovePts( sizeof truckOutline/sizeof truckOutline[0], p, pos ); + DrawFillPoly( d, sizeof truckOutline/sizeof truckOutline[0], p, color ); + pp.x = -70/2; + pp.y = 0; + memcpy( p, wheelOutline, sizeof wheelOutline ); + RescalePts( sizeof wheelOutline/sizeof wheelOutline[0], p, 1.0, width/56.5 ); + MovePts( sizeof wheelOutline/sizeof wheelOutline[0], p, pp ); + RescalePts( sizeof wheelOutline/sizeof wheelOutline[0], p, ratio, ratio ); + RotatePts( sizeof wheelOutline/sizeof wheelOutline[0], p, zero, angle ); + MovePts( sizeof wheelOutline/sizeof wheelOutline[0], p, pos ); + DrawFillPoly( d, sizeof wheelOutline/sizeof wheelOutline[0], p, color ); + pp.x = 70/2; + memcpy( p, wheelOutline, sizeof wheelOutline ); + RescalePts( sizeof wheelOutline/sizeof wheelOutline[0], p, 1.0, width/56.5 ); + MovePts( sizeof wheelOutline/sizeof wheelOutline[0], p, pp ); + RescalePts( sizeof wheelOutline/sizeof wheelOutline[0], p, ratio, ratio ); + RotatePts( sizeof wheelOutline/sizeof wheelOutline[0], p, zero, angle ); + MovePts( sizeof wheelOutline/sizeof wheelOutline[0], p, pos ); + DrawFillPoly( d, sizeof wheelOutline/sizeof wheelOutline[0], p, color ); +} + + +static coOrd couplerOutline[] = { + { 0, 2.5 }, + { 0, -2.5 }, + { 0, -2.5 }, + { 3, -7 }, + { 14, -5 }, + { 14, 2 }, + { 12, 2 }, + { 12, -2 }, + { 9, -2 }, + { 9, 3 }, + { 13, 6 }, + { 13, 7 }, + { 6, 7 }, + { 0, 2.5 } }; +static void CarProtoDrawCoupler( + drawCmd_t * d, + DIST_T length, + FLOAT_T ratio, + coOrd pos, + ANGLE_T angle ) +{ + coOrd p[24], pp; + wDrawColor color = wDrawColorBlack; + + length /= ratio; + if ( length < 12.0 ) + return; + memcpy( p, couplerOutline, sizeof couplerOutline ); + p[0].x = p[1].x = -(length-12.0); + pp.x = length-12.0; + pp.y = 0; +/* TODO - if length > 6 then draw Sills */ +#ifdef FUTURE + if ( angle == 270.0 ) { + pos.x -= (length-12.0); + for ( inx=0; inx<sizeof couplerOutline/sizeof couplerOutline[0]; inx++ ) { + p[inx].x = -p[inx].x; + p[inx].y = -p[inx].y; + } + } else { + pos.x += (length-12.0); + } +#endif + MovePts( sizeof couplerOutline/sizeof couplerOutline[0], p, pp ); + RescalePts( sizeof couplerOutline/sizeof couplerOutline[0], p, ratio, ratio ); + RotatePts( sizeof couplerOutline/sizeof couplerOutline[0], p, zero, angle-90.0 ); + MovePts( sizeof couplerOutline/sizeof couplerOutline[0], p, pos ); + DrawFillPoly( d, sizeof couplerOutline/sizeof couplerOutline[0], p, color ); +} + + + +/* + * Car Proto + */ + + +struct carProto_t; +typedef struct carProto_t carProto_t; + +struct carProto_t { + char * contentsLabel; + wIndex_t paramFileIndex; + char * desc; + long options; + long type; + carDim_t dim; + int segCnt; + trkSeg_p segPtr; + coOrd size; + coOrd orig; + }; + +static dynArr_t carProto_da; +#define carProto(N) DYNARR_N( carProto_t*, carProto_da, N ) + +#define N_TYPELISTMAP (7) +static nameLongMap_t typeListMap[N_TYPELISTMAP] = { + { N_("Diesel Loco"), 10101 }, + { N_("Steam Loco"), 10201 }, + { N_("Elect Loco"), 10301 }, + { N_("Freight Car"), 30100 }, + { N_("Psngr Car"), 50100 }, + { N_("M-O-W"), 70100 }, + { N_("Other"), 90100 } }; + +static trkSeg_p carProtoSegPtr; +static int carProtoSegCnt; + + +static coOrd dummyOutlineSegPts[5]; +static trkSeg_t dummyOutlineSegs; +static void CarProtoDlgCreateDummyOutline( + int * segCntP, + trkSeg_p * segPtrP, + BOOL_T isLoco, + DIST_T length, + DIST_T width, + wDrawColor color ) +{ + trkSeg_p segPtr; + coOrd * pts; + DIST_T length2; + + *segCntP = 1; + segPtr = *segPtrP = &dummyOutlineSegs; + + segPtr->type = SEG_FILPOLY; + segPtr->color = color; + segPtr->width = 0; + segPtr->u.p.cnt = isLoco?5:4; + segPtr->u.p.pts = pts = dummyOutlineSegPts; + segPtr->u.p.orig.x = 0; + segPtr->u.p.orig.y = 0; + segPtr->u.p.angle = 0; + length2 = length; + if ( isLoco ) { + pts->x = length; + pts->y = width/2.0; + pts++; + length2 -= width/2.0; + } + pts->x = length2; + pts->y = 0.0; + pts++; + pts->x = 0.0; + pts->y = 0.0; + pts++; + pts->x = 0.0; + pts->y = width; + pts++; + pts->x = length2; + pts->y = width; +} + + +static int CarProtoFindTypeCode( + long code ) +{ + int inx; + for ( inx=0; inx<N_TYPELISTMAP; inx++ ) { + if ( typeListMap[inx].value > code ) { + if ( inx == 0 ) + return N_TYPELISTMAP-1; + else + return inx-1; + } + } + return N_TYPELISTMAP-1; +} + + +static int CmpCarProto( + void * key, + void * elem ) +{ + char * key_val=key; + carProto_p elem_val=elem; + return strcasecmp( key_val, elem_val->desc ); +} + + +static carProto_p CarProtoFind( + char * desc ) +{ + return LookupListElem( &carProto_da, desc, CmpCarProto, 0 ); +} + + +static carProto_p CarProtoLookup( + char * desc, + BOOL_T createMissing, + BOOL_T isLoco, + DIST_T length, + DIST_T width ) +{ + carProto_p proto; + trkSeg_p segPtr; + proto = LookupListElem( &carProto_da, desc, CmpCarProto, createMissing?sizeof *proto:0 ); + if ( proto == NULL ) + return NULL; + if ( proto->desc == NULL ) { + proto->desc = MyStrdup(desc); + proto->contentsLabel = "Car Prototype"; + proto->paramFileIndex = PARAM_LAYOUT; + proto->options = (isLoco?CAR_DESC_IS_LOCO:0); + proto->dim.carLength = length; + proto->dim.carWidth = width; + proto->dim.truckCenter = length - 2.0*59.0; + proto->dim.coupledLength = length + 2.0*16.0; + CarProtoDlgCreateDummyOutline( &proto->segCnt, &segPtr, isLoco, length, width, drawColorBlue ); + proto->segPtr = (trkSeg_p)memdup( segPtr, (sizeof *(trkSeg_p)0) * proto->segCnt ); + CloneFilledDraw( proto->segCnt, proto->segPtr, FALSE ); + GetSegBounds( zero, 0.0, proto->segCnt, proto->segPtr, &proto->orig, &proto->size ); + carProtoListChanged = TRUE; + } + return proto; +} + + +static carProto_p CarProtoNew( + carProto_p proto, + int paramFileIndex, + char * desc, + long options, + long type, + carDim_t * dim, + wIndex_t segCnt, + trkSeg_p segPtr ) +{ + if ( proto == NULL ) { + proto = LookupListElem( &carProto_da, desc, CmpCarProto, sizeof *(carProto_p)0 ); + if ( proto->desc != NULL ) { + if ( proto->paramFileIndex == PARAM_CUSTOM && + paramFileIndex != PARAM_CUSTOM ) + return proto; + } + } + if ( proto->desc != NULL ) { + MyFree( proto->desc ); + } + proto->desc = MyStrdup(desc); + proto->contentsLabel = "Car Prototype"; + proto->paramFileIndex = paramFileIndex; + proto->options = options; + proto->type = type; + proto->dim = *dim; + proto->segCnt = segCnt; + proto->segPtr = (trkSeg_p)memdup( segPtr, (sizeof *(trkSeg_p)0) * proto->segCnt ); + CloneFilledDraw( proto->segCnt, proto->segPtr, FALSE ); + GetSegBounds( zero, 0.0, proto->segCnt, proto->segPtr, &proto->orig, &proto->size ); + carProtoListChanged = TRUE; + return proto; +} + + +static void CarProtoDelete( + carProto_p protoP ) +{ + if ( protoP == NULL ) + return; + RemoveListElem( &carProto_da, protoP ); + if ( protoP->desc ) + MyFree( protoP->desc ); + MyFree( protoP ); +} + + +static BOOL_T CarProtoRead( + char * line ) +{ + char * desc; + long options; + long type; + carDim_t dim; + + if ( !GetArgs( line+9, "qllff00ff", + &desc, &options, &type, &dim.carLength, &dim.carWidth, &dim.truckCenter, &dim.coupledLength ) ) + return FALSE; + if ( !ReadSegs() ) + return FALSE; + CarProtoNew( NULL, curParamFileIndex, desc, options, type, &dim, tempSegs_da.cnt, &tempSegs(0) ); + return TRUE; +} + + +static BOOL_T CarProtoWrite( + FILE * f, + carProto_t * proto ) +{ + BOOL_T rc = TRUE; + char *oldLocale = NULL; + + oldLocale = SaveLocale("C"); + + rc &= fprintf( f, "CARPROTO \"%s\" %ld %ld %0.3f %0.3f 0 0 %0.3f %0.3f\n", + PutTitle(proto->desc), proto->options, proto->type, proto->dim.carLength, proto->dim.carWidth, proto->dim.truckCenter, proto->dim.coupledLength )>0; + rc &= WriteSegs( f, proto->segCnt, proto->segPtr ); + + RestoreLocale(oldLocale); + + return rc; +} + + + +static BOOL_T CarProtoCustomSave( + FILE * f ) +{ + int inx; + carProto_t * proto; + BOOL_T rc = TRUE; + + for ( inx=0; inx<carProto_da.cnt; inx++ ) { + proto = carProto(inx); + if ( proto->paramFileIndex == PARAM_CUSTOM ) + rc &= CarProtoWrite( f, proto ); + } + return rc; +} + + +/* + * Car Desc + */ + +struct carPart_t; +typedef struct carPart_t carPart_t; +typedef carPart_t * carPart_p; +struct carPartParent_t; +typedef struct carPartParent_t carPartParent_t; +typedef carPartParent_t * carPartParent_p; + +typedef struct { + char * name; + int len; + } cmp_key_t; + +typedef struct { + tabString_t manuf; + tabString_t proto; + SCALEINX_T scale; + } cmp_partparent_t; +struct carPartParent_t { + char * manuf; + char * proto; + SCALEINX_T scale; + dynArr_t parts_da; + }; +struct carPart_t { + carPartParent_p parent; + wIndex_t paramFileIndex; + char * title; + long options; + long type; + carDim_t dim; + wDrawColor color; + char * partnoP; + int partnoL; + }; +static dynArr_t carPartParent_da; +#define carPartParent(N) DYNARR_N(carPartParent_p, carPartParent_da, N) +#define carPart(P,N) DYNARR_N(carPart_p, (P)->parts_da, N) +struct roadnameMap_t; +typedef struct roadnameMap_t roadnameMap_t; +typedef roadnameMap_t * roadnameMap_p; +struct roadnameMap_t { + char * roadname; + char * repmark; + }; +static dynArr_t roadnameMap_da; +#define roadnameMap(N) DYNARR_N(roadnameMap_p, roadnameMap_da, N) +static BOOL_T roadnameMapChanged; +static long carPartChangeLevel = 0; + + + +static int Cmp_part( + void * key, + void * elem ) +{ + carPart_p cmp_key=key; + carPart_p part_elem=elem; + int rc; + int len; + + len = min( cmp_key->partnoL, part_elem->partnoL ); + rc = strncasecmp( cmp_key->partnoP, part_elem->partnoP, len+1 ); + if ( rc != 0 ) + return rc; + if ( cmp_key->paramFileIndex == part_elem->paramFileIndex ) + return 0; + if ( cmp_key->paramFileIndex == PARAM_DEMO ) + return -1; + if ( part_elem->paramFileIndex == PARAM_DEMO ) + return 1; + if ( cmp_key->paramFileIndex == PARAM_CUSTOM ) + return -1; + if ( part_elem->paramFileIndex == PARAM_CUSTOM ) + return 1; + if ( cmp_key->paramFileIndex == PARAM_LAYOUT ) + return 1; + if ( part_elem->paramFileIndex == PARAM_LAYOUT ) + return -1; + if ( cmp_key->paramFileIndex > part_elem->paramFileIndex ) + return -1; + else + return 1; +} + + +static int Cmp_partparent( + void * key, + void * elem ) +{ + cmp_partparent_t * cmp_key=key; + carPartParent_p part_elem=elem; + int rc; + + rc = - TabStringCmp( part_elem->manuf, &cmp_key->manuf ); + if ( rc != 0 ) + return rc; + rc = cmp_key->scale - part_elem->scale; + if ( rc != 0 ) + return rc; + rc = - TabStringCmp( part_elem->proto, &cmp_key->proto ); + return rc; +} + + +static int Cmp_roadnameMap( + void * key, + void * elem ) +{ + cmp_key_t * cmp_key=key; + roadnameMap_p roadname_elem=elem; + int rc; + + rc = strncasecmp( cmp_key->name, roadname_elem->roadname, cmp_key->len ); + if ( rc == 0 && roadname_elem->roadname[cmp_key->len] ) + return -1; + return rc; +} + + +static roadnameMap_p LoadRoadnameList( + tabString_p roadnameTab, + tabString_p repmarkTab ) +{ + cmp_key_t cmp_key; + roadnameMap_p roadnameMapP; + + lookupListIndex = -1; + if ( roadnameTab->len<=0 ) + return NULL; + if ( TabStringCmp( "undecorated", roadnameTab ) == 0 ) + return NULL; + + cmp_key.name = roadnameTab->ptr; + cmp_key.len = roadnameTab->len; + roadnameMapP = LookupListElem( &roadnameMap_da, &cmp_key, Cmp_roadnameMap, sizeof *(roadnameMap_p)0 ); + if ( roadnameMapP->roadname == NULL ) { + roadnameMapP->roadname = TabStringDup(roadnameTab); + roadnameMapP->repmark = TabStringDup(repmarkTab); + roadnameMapChanged = TRUE; + } else if ( repmarkTab->len > 0 && + ( roadnameMapP->repmark == NULL || roadnameMapP->repmark[0] == '\0' ) ) { + roadnameMapP->repmark = TabStringDup(repmarkTab); + roadnameMapChanged = TRUE; + } + return roadnameMapP; +} + + +static carPart_p CarPartFind( + char * manufP, + int manufL, + char * partnoP, + int partnoL, + SCALEINX_T scale ) +{ + wIndex_t inx1, inx2; + carPart_p partP; + carPartParent_p parentP; + for ( inx1=0; inx1<carPartParent_da.cnt; inx1++ ) { + parentP = carPartParent(inx1); + if ( manufL == (int)strlen(parentP->manuf) && + strncasecmp( manufP, parentP->manuf, manufL ) == 0 && + scale == parentP->scale ) { + for ( inx2=0; inx2<parentP->parts_da.cnt; inx2++ ) { + partP = carPart( parentP, inx2 ); + if ( partnoL == partP->partnoL && + strncasecmp( partnoP, partP->partnoP, partnoL ) == 0 ) { + return partP; + } + } + } + } + return NULL; +} + + + + +static void CarPartParentDelete( + carPartParent_p parentP ) +{ + RemoveListElem( &carPartParent_da, parentP ); + MyFree( parentP->manuf ); + MyFree( parentP->proto ); + MyFree( parentP ); +} + + +static void CarPartUnlink( + carPart_p partP ) +{ + carPartParent_p parentP = partP->parent; + RemoveListElem( &parentP->parts_da, partP ); + if ( parentP->parts_da.cnt <= 0 ) { + CarPartParentDelete( parentP ); + } +} + + +static carPartParent_p CarPartParentNew( + char * manufP, + int manufL, + char *protoP, + int protoL, + SCALEINX_T scale ) +{ + carPartParent_p parentP; + cmp_partparent_t cmp_key; + cmp_key.manuf.ptr = manufP; + cmp_key.manuf.len = manufL; + cmp_key.proto.ptr = protoP; + cmp_key.proto.len = protoL; + cmp_key.scale = scale; + parentP = (carPartParent_p)LookupListElem( &carPartParent_da, &cmp_key, Cmp_partparent, sizeof * parentP); + if ( parentP->manuf == NULL ) { + parentP->manuf = (char*)MyMalloc( manufL+1 ); + memcpy( parentP->manuf, manufP, manufL ); + parentP->manuf[manufL] = '\0'; + parentP->proto = (char*)MyMalloc( protoL+1 ); + memcpy( parentP->proto, protoP, protoL ); + parentP->proto[protoL] = '\0'; + parentP->scale = scale; + } + return parentP; +} + + +static carPart_p CarPartNew( + carPart_p partP, + int paramFileIndex, + SCALEINX_T scaleInx, + char * title, + long options, + long type, + carDim_t *dim, + wDrawColor color) +{ + carPartParent_p parentP; + carPart_t cmp_key; + tabString_t tabs[7]; + + TabStringExtract( title, 7, tabs ); + if ( TabStringCmp( "Undecorated", &tabs[T_MANUF] ) == 0 || + TabStringCmp( "Custom", &tabs[T_MANUF] ) == 0 || + tabs[T_PART].len == 0 ) + return NULL; + if ( tabs[T_PROTO].len == 0 ) + return NULL; + if ( partP == NULL ) { + partP = CarPartFind( tabs[T_MANUF].ptr, tabs[T_MANUF].len, tabs[T_PART].ptr, tabs[T_PART].len, scaleInx ); + if ( partP != NULL && + partP->paramFileIndex == PARAM_CUSTOM && + paramFileIndex != PARAM_CUSTOM ) + return partP; +LOG( log_carList, 2, ( "new car part: %s (%d) at %d\n", title, paramFileIndex, lookupListIndex ) ) + } + if ( partP != NULL ) { + CarPartUnlink( partP ); + if ( partP->title != NULL ) + MyFree( partP->title ); +LOG( log_carList, 2, ( "upd car part: %s (%d)\n", title, paramFileIndex ) ) + } + LoadRoadnameList( &tabs[T_ROADNAME], &tabs[T_REPMARK] ); + parentP = CarPartParentNew( tabs[T_MANUF].ptr, tabs[T_MANUF].len, tabs[T_PROTO].ptr, tabs[T_PROTO].len, scaleInx ); + cmp_key.title = title; + cmp_key.parent = parentP; + cmp_key.paramFileIndex = paramFileIndex; + cmp_key.options = options; + cmp_key.type = type; + cmp_key.dim = *dim; + cmp_key.color = color; + cmp_key.partnoP = tabs[T_PART].ptr; + cmp_key.partnoL = tabs[T_PART].len; + partP = (carPart_p)LookupListElem( &parentP->parts_da, &cmp_key, Cmp_part, sizeof * partP ); + if ( partP->title != NULL ) + MyFree( partP->title ); + *partP = cmp_key; + sprintf( message, "\t\t%s", tabs[2].ptr ); + partP->title = MyStrdup( message ); + partP->partnoP = partP->title + 2+tabs[2].len+1;; + partP->partnoL = tabs[T_PART].len; + return partP; +} + + +static void CarPartDelete( + carPart_p partP ) +{ + if ( partP == NULL ) + return; + CarPartUnlink( partP ); + if ( partP->title ) + MyFree( partP->title ); + MyFree( partP ); +} + + +static BOOL_T CarPartRead( + char * line ) +{ + char scale[10]; + long options; + long type; + char * title; + carDim_t dim; + long rgb; + + if ( !GetArgs( line+8, "sqllff00ffl", + scale, &title, &options, &type, &dim.carLength, &dim.carWidth, &dim.truckCenter, &dim.coupledLength, &rgb ) ) + return FALSE; + CarPartNew( NULL, curParamFileIndex, LookupScale(scale), title, options, type, &dim, wDrawFindColor(rgb) ); + MyFree( title ); + return TRUE; +} + + +static BOOL_T CarPartWrite( + FILE * f, + carPart_p partP ) +{ + BOOL_T rc = TRUE; + char *oldLocale = NULL; + carPartParent_p parentP=partP->parent; + tabString_t tabs[7]; + + oldLocale = SaveLocale("C"); + + TabStringExtract( partP->title, 7, tabs ); + sprintf( message, "%s\t%s\t%.*s\t%.*s\t%.*s\t%.*s\t%.*s", + parentP->manuf, parentP->proto, + tabs[T_DESC].len, tabs[T_DESC].ptr, + tabs[T_PART].len, tabs[T_PART].ptr, + tabs[T_ROADNAME].len, tabs[T_ROADNAME].ptr, + tabs[T_REPMARK].len, tabs[T_REPMARK].ptr, + tabs[T_NUMBER].len, tabs[T_NUMBER].ptr ); + rc &= fprintf( f, "CARPART %s \"%s\"", GetScaleName(partP->parent->scale), PutTitle(message) )>0; + rc &= fprintf( f, " %ld %ld %0.3f %0.3f 0 0 %0.3f %0.3f %ld\n", + partP->options, partP->type, partP->dim.carLength, partP->dim.carWidth, partP->dim.truckCenter, partP->dim.coupledLength, wDrawGetRGB(partP->color) )>0; + + RestoreLocale(oldLocale); + + return rc; +} + + + +static BOOL_T CarDescCustomSave( + FILE * f ) +{ + int parentX; + carPartParent_p parentP; + int partX; + carPart_p partP; + BOOL_T rc = TRUE; + + for ( parentX=0; parentX<carPartParent_da.cnt; parentX++ ) { + parentP = carPartParent(parentX); + for ( partX=0; partX<parentP->parts_da.cnt; partX++ ) { + partP = carPart(parentP,partX); + if ( partP->paramFileIndex == PARAM_CUSTOM ) + rc &= CarPartWrite(f, partP ); + } + } + return rc; +} + + + +/* + * Car Item + */ + +static dynArr_t carItemInfo_da; +#define carItemInfo(N) DYNARR_N( carItem_t*, carItemInfo_da, N ) + +#define N_CONDLISTMAP (6) +static nameLongMap_t condListMap[N_CONDLISTMAP] = { + { N_("N/A"), 0 }, + { N_("Mint"), 100 }, + { N_("Excellent"), 80 }, + { N_("Good"), 60 }, + { N_("Fair"), 40 }, + { N_("Poor"), 20 } }; + + +static wIndex_t MapCondition( + long conditionValue ) +{ + if ( conditionValue < 10 ) + return 0; + else if ( conditionValue < 30 ) + return 5; + else if ( conditionValue < 50 ) + return 4; + else if ( conditionValue < 70 ) + return 3; + else if ( conditionValue < 90 ) + return 2; + else + return 1; +} + + +static carItem_p CarItemNew( + carItem_p item, + int paramFileIndex, + long itemIndex, + SCALEINX_T scale, + char * title, + long options, + long type, + carDim_t *dim, + wDrawColor color, + FLOAT_T purchPrice, + FLOAT_T currPrice, + long condition, + long purchDate, + long serviceDate ) +{ + carPart_p partP; + tabString_t tabs[7]; + + TabStringExtract( title, 7, tabs ); + if ( paramFileIndex != PARAM_CUSTOM ) { + partP = CarPartFind( tabs[T_MANUF].ptr, tabs[T_MANUF].len, tabs[T_PART].ptr, tabs[T_PART].len, scale ); + if ( partP == NULL ) { + CarPartNew( NULL, PARAM_LAYOUT, scale, title, options, type, dim, color ); + } + } + + if ( item == NULL ) { + DYNARR_APPEND( carItem_t*, carItemInfo_da, 10 ); + item = (carItem_t*)MyMalloc( sizeof * item ); + carItemInfo(carItemInfo_da.cnt-1) = item; + } else { + if ( item->title ) MyFree( item->title ); + if ( item->data.number ) MyFree( item->data.number ); + } + item->index = itemIndex; + item->scaleInx = scale; + item->title = MyStrdup(title); + item->contentsLabel = "Car Item"; + item->barScale = curBarScale>0?curBarScale:(60.0*12.0/curScaleRatio); + item->options = options; + item->type = type; + item->dim = *dim; + item->color = color; + if ( tabs[T_REPMARK].len>0 || tabs[T_NUMBER].len>0 ) { + sprintf( message, "%.*s%s%.*s", tabs[T_REPMARK].len, tabs[T_REPMARK].ptr, (tabs[T_REPMARK].len>0&&tabs[T_NUMBER].len>0)?" ":"", tabs[T_NUMBER].len, tabs[T_NUMBER].ptr ); + } else { + sprintf( message, "#%ld", item->index ); + } + item->data.number = MyStrdup( message ); + item->data.purchPrice = purchPrice; + item->data.currPrice = currPrice; + item->data.condition = condition; + item->data.purchDate = purchDate; + item->data.serviceDate = serviceDate; + item->data.notes = NULL; + item->segCnt = 0; + item->segPtr = NULL; + LoadRoadnameList( &tabs[T_ROADNAME], &tabs[T_REPMARK] ); + return item; +} + + +EXPORT BOOL_T CarItemRead( + char * line ) +{ + long itemIndex; + char scale[10]; + char * title; + long options; + long type; + carDim_t dim; + long rgb; + FLOAT_T purchPrice = 0; + FLOAT_T currPrice = 0; + long condition = 0; + long purchDate = 0; + long serviceDate = 0; + int len, siz; + static dynArr_t buffer_da; + carItem_p item; + char * cp; + wIndex_t layer; + coOrd pos; + ANGLE_T angle; + wIndex_t index; + + if ( !GetArgs( line+4, "lsqll" "ff00ffl" "fflll000000c", + &itemIndex, scale, &title, &options, &type, + &dim.carLength, &dim.carWidth, &dim.truckCenter, &dim.coupledLength, &rgb, + &purchPrice, &currPrice, &condition, &purchDate, &serviceDate, &cp ) ) + return FALSE; + if ( (options&CAR_ITEM_HASNOTES) ) { + DYNARR_SET( char, buffer_da, 0 ); + while ( (line=GetNextLine()) && strncmp( line, " END", 7 ) != 0 ) { + siz = buffer_da.cnt; + len = strlen( line ); + DYNARR_SET( char, buffer_da, siz+len+1 ); + memcpy( &((char*)buffer_da.ptr)[siz], line, len ); + ((char*)buffer_da.ptr)[siz+len] = '\n'; + } + DYNARR_APPEND( char, buffer_da, 1 ); + ((char*)buffer_da.ptr)[buffer_da.cnt-1] = 0; + } + item = CarItemNew( NULL, curParamFileIndex, itemIndex, LookupScale(scale), title, + options&(CAR_DESC_BITS|CAR_ITEM_BITS), type, &dim, wDrawFindColor(rgb), + purchPrice, currPrice, condition, purchDate, serviceDate ); + if ( (options&CAR_ITEM_HASNOTES) ) + item->data.notes = MyStrdup( (char*)buffer_da.ptr ); + MyFree(title); + if ( (options&CAR_ITEM_ONLAYOUT) ) { + if ( !GetArgs( cp, "dLpf", + &index, &layer, &pos, &angle ) ) + return FALSE; + item->car = NewCar( index, item, pos, angle ); + SetTrkLayer( item->car, layer ); + ReadSegs(); + SetEndPts( item->car, 2 ); + ComputeBoundingBox( item->car ); + } + return TRUE; +} + + +static BOOL_T CarItemWrite( + FILE * f, + carItem_t * item, + BOOL_T layout ) +{ + long options = (item->options&CAR_DESC_BITS); + coOrd pos; + ANGLE_T angle; + BOOL_T rc = TRUE; + char *oldLocale = NULL; + + oldLocale = SaveLocale("C"); + + if ( item->data.notes && item->data.notes[0] ) + options |= CAR_ITEM_HASNOTES; + if ( layout && item->car && !IsTrackDeleted(item->car) ) + options |= CAR_ITEM_ONLAYOUT; + rc &= fprintf( f, "CAR %ld %s \"%s\" %ld %ld %0.3f %0.3f 0 0 %0.3f %0.3f %ld %0.3f %0.3f %ld %ld %ld 0 0 0 0 0 0", + item->index, GetScaleName(item->scaleInx), PutTitle(item->title), + options, item->type, + item->dim.carLength, item->dim.carWidth, item->dim.truckCenter, item->dim.coupledLength, wDrawGetRGB(item->color), + item->data.purchPrice, item->data.currPrice, item->data.condition, item->data.purchDate, item->data.serviceDate )>0; + if ( ( options&CAR_ITEM_ONLAYOUT) ) { + CarGetPos( item->car, &pos, &angle ); + rc &= fprintf( f, " %d %d %0.3f %0.3f %0.3f", + GetTrkIndex(item->car), GetTrkLayer(item->car), pos.x, pos.y, angle )>0; + } + rc &= fprintf( f, "\n" )>0; + if ( (options&CAR_ITEM_HASNOTES) ) { + rc &= fprintf( f, "%s\n", item->data.notes )>0; + rc &= fprintf( f, " END\n" )>0; + } + if ( (options&CAR_ITEM_ONLAYOUT) ) { + rc &= WriteEndPt( f, item->car, 0 ); + rc &= WriteEndPt( f, item->car, 1 ); + rc &= fprintf( f, "\tEND\n" )>0; + } + + RestoreLocale(oldLocale); + + return rc; +} + + + +EXPORT carItem_p CarItemFind( + long itemInx ) +{ + if ( itemInx >= 0 && itemInx < carItemInfo_da.cnt ) + return carItemInfo(itemInx); + else + return NULL; +} + + +EXPORT long CarItemFindIndex( + carItem_p item ) +{ + long inx; + for ( inx=0; inx<carItemInfo_da.cnt; inx++ ) + if ( carItemInfo(inx) == item ) + return inx; + AbortProg( "carItemFindIndex" ); + return -1; +} + + +EXPORT void CarItemGetSegs( + carItem_p item ) +{ + coOrd orig; + carProto_p protoP; + tabString_t tabs[7]; + trkSeg_t * segPtr; + DIST_T ratio = GetScaleRatio(item->scaleInx); + + TabStringExtract( item->title, 7, tabs ); + TabStringCpy( message, &tabs[T_PROTO] ); + protoP = CarProtoLookup( message, FALSE, FALSE, 0.0, 0.0 ); + if ( protoP != NULL ) { + item->segCnt = protoP->segCnt; + segPtr = protoP->segPtr; + orig = protoP->orig; + } else { + CarProtoDlgCreateDummyOutline( &item->segCnt, &segPtr, (item->options&CAR_DESC_IS_LOCO)!=0, item->dim.carLength, item->dim.carWidth, item->color ); + orig = zero; + } + item->segPtr = (trkSeg_p)MyMalloc( item->segCnt * sizeof *(segPtr) ); + memcpy( item->segPtr, segPtr, item->segCnt * sizeof *(segPtr) ); + CloneFilledDraw( item->segCnt, item->segPtr, FALSE ); + if ( protoP ) { + orig.x = -orig.x; + orig.y = -orig.y; + MoveSegs( item->segCnt, item->segPtr, orig ); + RescaleSegs( item->segCnt, item->segPtr, item->dim.carLength/protoP->size.x, item->dim.carWidth/protoP->size.y, 1/ratio ); + RecolorSegs( item->segCnt, item->segPtr, item->color ); + } +} + + +EXPORT BOOL_T WriteCars( + FILE * f ) +{ + int inx; + BOOL_T rc = TRUE; + for ( inx=0; inx<carItemInfo_da.cnt; inx++ ) + rc &= CarItemWrite( f, carItemInfo(inx), TRUE ); + return rc; +} + + +EXPORT BOOL_T CarCustomSave( + FILE * f ) +{ + BOOL_T rc = TRUE; + rc &= CarProtoCustomSave( f ); + rc &= CarDescCustomSave( f ); + return rc; +} + + +/* + * Car Item Select + */ + +EXPORT carItem_p currCarItemPtr; +EXPORT long carHotbarModeInx = 1; +static long carHotbarModes[] = { 0x0002, 0x0012, 0x0312, 0x4312, 0x0021, 0x0321, 0x4321 }; +static long carHotbarContents[] = { 0x0005, 0x0002, 0x0012, 0x0012, 0x0001, 0x0021, 0x0021 }; +static long newCarInx; +static paramData_t newCarPLs[] = { + { PD_DROPLIST, &newCarInx, "index", PDO_DLGWIDE, (void*)400, N_("Item") } }; +static paramGroup_t newCarPG = { "train-newcar", 0, newCarPLs, sizeof newCarPLs/sizeof newCarPLs[0] }; +EXPORT wControl_p newCarControls[2]; +static char newCarLabel1[STR_SIZE]; +static char * newCarLabels[2] = { newCarLabel1, NULL }; + +static dynArr_t carItemHotbar_da; +#define carItemHotbar(N) DYNARR_N( carItem_p, carItemHotbar_da, N ) + + +static int Cmp_carHotbar( + const void * ptr1, + const void * ptr2 ) +{ + carItem_p item1 = *(carItem_p*)ptr1; + carItem_p item2 = *(carItem_p*)ptr2; + tabString_t tabs1[7], tabs2[7]; + int rc; + long mode; + + TabStringExtract( item1->title, 7, tabs1 ); + TabStringExtract( item2->title, 7, tabs2 ); + for ( mode=carHotbarModes[carHotbarModeInx],rc=0; mode!=0&&rc==0; mode>>=4 ) { + switch ( mode&0x000F ) { + case 4: + rc = (int)(item1->index-item2->index); + break; + case 1: + rc = strncasecmp( tabs1[T_MANUF].ptr, tabs2[T_MANUF].ptr, max(tabs1[T_MANUF].len,tabs2[T_MANUF].len) ); + break; + case 3: + rc = strncasecmp( tabs1[T_PART].ptr, tabs2[T_PART].ptr, max(tabs1[T_PART].len,tabs2[T_PART].len) ); + break; + case 2: + if ( item1->type < item2->type ) + rc = -1; + else if ( item1->type > item2->type ) + rc = 1; + else + rc = strncasecmp( tabs1[T_PROTO].ptr, tabs2[T_PROTO].ptr, max(tabs1[T_PROTO].len,tabs2[T_PROTO].len) ); + break; + } + } + return rc; +} + + +static void CarItemHotbarUpdate( + paramGroup_p pg, + int inx, + void * data ) +{ + wIndex_t carItemInx; + carItem_p item; + if ( inx == 0 ) { + carItemInx = (wIndex_t)*(long*)data; + if ( carItemInx < 0 ) + return; + carItemInx = (wIndex_t)(long)wListGetItemContext( (wList_p)pg->paramPtr[inx].control, carItemInx ); + item = carItemHotbar(carItemInx); + if ( item != NULL ) + currCarItemPtr = item; + } +} + + +static char * FormatCarTitle( + carItem_p item, + long mode ) +{ + tabString_t tabs[7]; + char * cp; + TabStringExtract( item->title, 7, tabs ); + cp = message; + for ( ; mode!=0; mode>>=4 ) { + switch ( mode&0x000F ) { + case 1: + cp = TabStringCpy( cp, &tabs[T_MANUF] ); + break; + case 2: + cp = TabStringCpy( cp, &tabs[T_PROTO] ); + break; + case 3: + cp = TabStringCpy( cp, &tabs[T_PART] ); + break; + case 4: + sprintf( cp, "%ld ", item->index ); + cp += strlen(cp); + break; + case 5: + strcpy( cp, typeListMap[CarProtoFindTypeCode(item->type)].name ); + cp += strlen(cp); + break; + } + *cp++ = '/'; + } + *--cp = '\0'; + return message; +} + + +EXPORT char * CarItemDescribe( + carItem_p item, + long mode, + long * index ) +{ + tabString_t tabs[7]; + char * cp; + static char desc[STR_LONG_SIZE]; + + TabStringExtract( item->title, 7, tabs ); + cp = desc; + if ( mode != -1 ) { + sprintf( cp, "%ld ", item->index ); + cp = desc+strlen(cp); + } + if ( (mode&0xF)!=1 && ((mode>>4)&0xF)!=1 && ((mode>>8)&0xF)!=1 && ((mode>>12)&0xF)!=1 ) { + cp = TabStringCpy( cp, &tabs[T_MANUF] ); + *cp++ = ' '; + } + if ( (mode&0xF)!=3 && ((mode>>4)&0xF)!=3 && ((mode>>8)&0xF)!=3 && ((mode>>12)&0xF)!=3 ) { + cp = TabStringCpy( cp, &tabs[T_PART] ); + *cp++ = ' '; + } + if ( (mode&0xF)!=2 && ((mode>>4)&0xF)!=2 && ((mode>>8)&0xF)!=2 && ((mode>>12)&0xF)!=2 ) { + cp = TabStringCpy( cp, &tabs[T_PROTO] ); + *cp++ = ' '; + } + if ( tabs[T_DESC].len > 0 ) { + cp = TabStringCpy( cp, &tabs[T_DESC] ); + *cp++ = ' '; + } + if ( mode != -1 ) { + if ( tabs[T_REPMARK].len > 0 ) { + cp = TabStringCpy( cp, &tabs[T_REPMARK] ); + *cp++ = ' '; + } else if ( tabs[T_ROADNAME].len > 0 ) { + cp = TabStringCpy( cp, &tabs[T_ROADNAME] ); + *cp++ = ' '; + } + if ( tabs[T_NUMBER].len > 0 ) { + cp = TabStringCpy( cp, &tabs[T_NUMBER] ); + *cp++ = ' '; + } + } + *--cp = '\0'; + if ( index != NULL ) + *index = item->index; + return desc; +} + + +EXPORT void CarItemLoadList( void * junk ) +{ + wIndex_t inx; + carItem_p item; + char * cp; + wPos_t w, h; + + DYNARR_SET( carItem_t*, carItemHotbar_da, carItemInfo_da.cnt ); + memcpy( carItemHotbar_da.ptr, carItemInfo_da.ptr, carItemInfo_da.cnt * sizeof item ); + wListClear( (wList_p)newCarPLs[0].control ); + for ( inx=0; inx<carItemHotbar_da.cnt; inx++ ) { + item = carItemHotbar(inx); + if ( item->car && !IsTrackDeleted(item->car) ) + continue; + cp = CarItemDescribe( item, 0, NULL ); + wListAddValue( (wList_p)newCarPLs[0].control, cp, NULL, (void*)(intptr_t)inx ); + } + /*wListSetValue( (wList_p)newCarPLs[0].control, "Select a car" );*/ + wListSetIndex( (wList_p)newCarPLs[0].control, 0 ); + strcpy( newCarLabel1, _("Select") ); + ParamLoadControl( &newCarPG, 0 ); + InfoSubstituteControls( newCarControls, newCarLabels ); + wWinGetSize( mainW, &w, &h ); + w -= wControlGetPosX( newCarControls[0] ) + 4; + if ( w > 20 ) + wListSetSize( (wList_p)newCarControls[0], w, wControlGetHeight( newCarControls[0] ) ); +} + + +static char * CarItemHotbarProc( + hotBarProc_e op, + void * data, + drawCmd_p d, + coOrd * origP ) +{ + wIndex_t carItemInx = (wIndex_t)(long)data; + carItem_p item; + wIndex_t inx; + long mode; + char * cp; + wPos_t w, h; + + item = carItemHotbar(carItemInx); + if ( item == NULL ) + return NULL; + switch ( op ) { + case HB_SELECT: + currCarItemPtr = item; + mode = carHotbarModes[carHotbarModeInx]; + if ( (mode&0xF000) == 0 ) { + wListClear( (wList_p)newCarPLs[0].control ); + for ( inx=carItemInx; + inx<carItemHotbar_da.cnt && ( inx==carItemInx || Cmp_carHotbar(&carItemHotbar(carItemInx),&carItemHotbar(inx))==0 ); + inx++ ) { + item = carItemHotbar(inx); + if ( item->car && !IsTrackDeleted(item->car) ) + continue; + cp = CarItemDescribe( item, mode, NULL ); + wListAddValue( (wList_p)newCarPLs[0].control, cp, NULL, (void*)(intptr_t)inx ); + } + /*wListSetValue( (wList_p)newCarPLs[0].control, "Select a car" );*/ + wListSetIndex( (wList_p)newCarPLs[0].control, 0 ); + cp = CarItemHotbarProc( HB_BARTITLE, (void*)(intptr_t)carItemInx, NULL, NULL ); + strncpy( newCarLabel1, cp, sizeof newCarLabel1 ); + ParamLoadControls( &newCarPG ); + ParamGroupRecord( &newCarPG ); + + InfoSubstituteControls( newCarControls, newCarLabels ); + wWinGetSize( mainW, &w, &h ); + w -= wControlGetPosX( newCarControls[0] ) + 4; + if ( w > 20 ) + wListSetSize( (wList_p)newCarControls[0], w, wControlGetHeight( newCarControls[0] ) ); + } else { + InfoSubstituteControls( NULL, NULL ); + cp = CarItemDescribe( item, 0, NULL ); + InfoMessage( cp ); + } + break; + case HB_LISTTITLE: + case HB_BARTITLE: + return FormatCarTitle( item, carHotbarModes[carHotbarModeInx] ); + case HB_FULLTITLE: + return item->title; + case HB_DRAW: + if ( item->segCnt == 0 ) + CarItemGetSegs( item ); + DrawSegs( d, *origP, 0.0, item->segPtr, item->segCnt, trackGauge, wDrawColorBlack ); + return NULL; + } + return NULL; +} + + +EXPORT int CarAvailableCount( void ) +{ + wIndex_t inx; + int cnt = 0; + carItem_t * item; + for ( inx=0; inx < carItemHotbar_da.cnt; inx ++ ) { + item = carItemHotbar(inx); + if ( item->scaleInx != curScaleInx ) + continue; + cnt++; + } + return cnt; +} + + +EXPORT void AddHotBarCarDesc( void ) +{ + wIndex_t inx; + carItem_t * item0, * item1; + coOrd orig; + coOrd size; + + DYNARR_SET( carItem_t*, carItemHotbar_da, carItemInfo_da.cnt ); + memcpy( carItemHotbar_da.ptr, carItemInfo_da.ptr, carItemInfo_da.cnt * sizeof item0 ); + qsort( carItemHotbar_da.ptr, carItemHotbar_da.cnt, sizeof item0, Cmp_carHotbar ); + for ( inx=0,item0=NULL; inx < carItemHotbar_da.cnt; inx ++ ) { + item1 = carItemHotbar(inx); + if ( item1->car && !IsTrackDeleted(item1->car) ) + continue; + if ( item1->scaleInx != curScaleInx ) + continue; + if ( (carHotbarModes[carHotbarModeInx]&0xF000)!=0 || ( item0 == NULL || Cmp_carHotbar( &item0, &item1 ) != 0 ) ) { +#ifdef DESCFIX + orig.x = - item->orig.x; + orig.y = - item->orig.y; +#endif + orig = zero; + size.x = item1->dim.carLength; + size.y = item1->dim.carWidth; + AddHotBarElement( FormatCarTitle( item1, carHotbarContents[carHotbarModeInx] ), size, orig, FALSE, (60.0*12.0/curScaleRatio), (void*)(intptr_t)inx, CarItemHotbarProc ); + } + item0 = item1; + } +} + + +EXPORT coOrd CarItemFindCouplerMountPoint( + carItem_p item, + traverseTrack_t trvTrk, + int dir ) +{ + DIST_T couplerOffset; + coOrd pos; + + if ( dir ) + FlipTraverseTrack( &trvTrk ); + if ( trvTrk.trk == NULL || (item->options&CAR_DESC_COUPLER_MODE_BODY)!=0 ) { + couplerOffset = (item->dim.carLength-(item->dim.coupledLength-item->dim.carLength))/2.0; + Translate( &pos, trvTrk.pos, trvTrk.angle, couplerOffset ); + } else { + TraverseTrack2( &trvTrk, item->dim.truckCenter/2.0 ); + /*Translate( &pos1, trvTrk.pos, trvTrk.angle, item->dim.truckCenter/2.0 );*/ + couplerOffset = item->dim.carLength - (item->dim.truckCenter+item->dim.coupledLength)/2.0; + Translate( &pos, trvTrk.pos, trvTrk.angle, couplerOffset ); + } + return pos; +} + + +EXPORT void CarItemSize( + carItem_p item, + coOrd * size ) +{ + size->x = item->dim.carLength; + size->y = item->dim.carWidth; +} + + +EXPORT char * CarItemNumber( + carItem_p item ) +{ + return item->data.number; +} + + +static DIST_T CarItemTruckCenter( + carItem_p item ) +{ + return item->dim.truckCenter; +} + + +EXPORT DIST_T CarItemCoupledLength( + carItem_p item ) +{ + return item->dim.coupledLength; +} + + +EXPORT BOOL_T CarItemIsLoco( + carItem_p item ) +{ + return (item->options&CAR_DESC_IS_LOCO) == (CAR_DESC_IS_LOCO); +} + + +EXPORT BOOL_T CarItemIsLocoMaster( + carItem_p item ) +{ + return (item->options&(CAR_DESC_IS_LOCO|CAR_DESC_IS_LOCO_MASTER)) == (CAR_DESC_IS_LOCO|CAR_DESC_IS_LOCO_MASTER); +} + + +EXPORT void CarItemSetLocoMaster( + carItem_p item, + BOOL_T locoIsMaster ) +{ + if ( locoIsMaster ) + item->options |= CAR_DESC_IS_LOCO_MASTER; + else + item->options &= ~CAR_DESC_IS_LOCO_MASTER; +} + + +EXPORT void CarItemSetTrack( + carItem_p item, + track_p trk ) +{ + item->car = trk; + if ( trk != NULL ) + SetTrkScale( trk, item->scaleInx ); +} + +static DIST_T CarItemCouplerLength( + carItem_p item, + int dir ) +{ + return item->dim.coupledLength-item->dim.carLength; +} + + +EXPORT void CarItemPlace( + carItem_p item, + traverseTrack_p trvTrk, + DIST_T * dists ) +{ + DIST_T dist; + traverseTrack_t trks[2]; + + dist = CarItemTruckCenter(item)/2.0; + trks[0] = trks[1] = *trvTrk; + TraverseTrack2( &trks[0], dist ); + TraverseTrack2( &trks[1], -dist ); + item->pos.x = (trks[0].pos.x+trks[1].pos.x)/2.0; + item->pos.y = (trks[0].pos.y+trks[1].pos.y)/2.0; + item->angle = FindAngle( trks[1].pos, trks[0].pos ); + dists[0] = dists[1] = CarItemCoupledLength(item)/2.0; +} + + + +static int drawCarTrucks = 0; +EXPORT void CarItemDraw( + drawCmd_p d, + carItem_p item, + wDrawColor color, + int direction, + BOOL_T locoIsMaster, + vector_t *coupler ) +{ + coOrd size, pos, pos2; + DIST_T length; + wFont_p fp; + wDrawWidth width; + trkSeg_t simpleSegs[1]; + coOrd simplePts[4]; + int dir; + DIST_T rad; + static int couplerLineWidth = 3; + DIST_T scale2rail; + + CarItemSize( item, &size ); + if ( d->scale >= ((d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale) ) { + simplePts[0].x = simplePts[3].x = -size.x/2.0; + simplePts[1].x = simplePts[2].x = size.x/2.0; + simplePts[0].y = simplePts[1].y = -size.y/2.0; + simplePts[2].y = simplePts[3].y = size.y/2.0; + simpleSegs[0].type = SEG_FILPOLY; + simpleSegs[0].color = item->color; + simpleSegs[0].width = 0; + simpleSegs[0].u.p.cnt = 4; + simpleSegs[0].u.p.pts = simplePts; + simpleSegs[0].u.p.orig = zero; + simpleSegs[0].u.p.angle = 0.0; + DrawSegs( d, item->pos, item->angle-90.0, simpleSegs, 1, 0.0, color ); + } else { + if ( item->segCnt == 0 ) + CarItemGetSegs( item ); + Translate( &pos, item->pos, item->angle, -size.x/2.0 ); + Translate( &pos, pos, item->angle-90, -size.y/2.0 ); + DrawSegs( d, pos, item->angle-90.0, item->segPtr, item->segCnt, 0.0, color ); + } + + if ( drawCarTrucks ) { + length = item->dim.truckCenter/2.0; + Translate( &pos, item->pos, item->angle, length ); + DrawArc( d, pos, trackGauge/2.0, 0.0, 360.0, FALSE, 0, color ); + Translate( &pos, item->pos, item->angle+180, length ); + DrawArc( d, pos, trackGauge/2.0, 0.0, 360.0, FALSE, 0, color ); + } + + if ( (labelEnable&LABELENABLE_CARS) ) { + fp = wStandardFont( F_HELV, FALSE, FALSE ); + DrawBoxedString( BOX_BACKGROUND, d, item->pos, item->data.number, fp, (wFontSize_t)descriptionFontSize, color, 0.0 ); + } + + /* draw loco head light */ + if ( (item->options&CAR_DESC_IS_LOCO)!=0 ) { + Translate( &pos, item->pos, item->angle+(direction?180.0:0.0), size.x/2.0-trackGauge/2.0 ); + if ( locoIsMaster ) { + DrawFillCircle( d, pos, trackGauge/2.0, (color==wDrawColorBlack?drawColorGold:color) ); + } else { + width = (wDrawWidth)floor( trackGauge/8.0 * d->dpi / d->scale ); + DrawArc( d, pos, trackGauge/2.0, 0.0, 360.0, FALSE, width, (color==wDrawColorBlack?drawColorGold:color) ); + } + } + + /* draw coupler */ + scale2rail = ((d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale); + if ( d->scale >= scale2rail ) + return; + scale2rail /= 2; + rad = trackGauge/8.0; + for ( dir=0; dir<2; dir++ ) { + Translate( &pos, coupler[dir].pos, coupler[dir].angle, CarItemCouplerLength(item,dir) ); + DrawLine( d, coupler[dir].pos, pos, couplerLineWidth, color ); + if ( d->scale < scale2rail ) { + /*DrawFillCircle( d, p0, rad, dir==0?color:selectedColor );*/ + Translate( &pos2, pos, coupler[dir].angle+90.0, trackGauge/3 ); + DrawLine( d, pos2, pos, couplerLineWidth, color ); + } + } +} + + +EXPORT void CarItemUpdate( + carItem_p item ) +{ + DoChangeNotification( CHANGE_SCALE ); +} + +/* + * Car Item/Part Dlg + */ + +static int carDlgChanged; + +static SCALEINX_T carDlgScaleInx; +static carItem_p carDlgUpdateItemPtr; +static carPart_p carDlgUpdatePartPtr; +static carProto_p carDlgUpdateProtoPtr; +static carPart_p carDlgNewPartPtr; +static carProto_p carDlgNewProtoPtr; + +static BOOL_T carDlgFlipToggle; + +static wIndex_t carDlgManufInx; +static char carDlgManufStr[STR_SIZE]; +static wIndex_t carDlgKindInx; +static wIndex_t carDlgProtoInx; +static char carDlgProtoStr[STR_SIZE]; +static wIndex_t carDlgPartnoInx; +static char carDlgPartnoStr[STR_SIZE]; +static char carDlgDescStr[STR_SIZE]; + +static long carDlgDispMode; +static wIndex_t carDlgRoadnameInx; +static char carDlgRoadnameStr[STR_SIZE]; +static char carDlgRepmarkStr[STR_SIZE]; +static char carDlgNumberStr[STR_SIZE]; +static wDrawColor carDlgBodyColor; +static long carDlgIsLoco; +static wIndex_t carDlgTypeInx; + +static carDim_t carDlgDim; +static DIST_T carDlgCouplerLength; +static long carDlgCouplerMount; + +static long carDlgItemIndex = 1; +static FLOAT_T carDlgPurchPrice; +static char carDlgPurchPriceStr[STR_SIZE]; +static FLOAT_T carDlgCurrPrice; +static char carDlgCurrPriceStr[STR_SIZE]; +static wIndex_t carDlgConditionInx; +static long carDlgCondition; +static long carDlgPurchDate; +static char carDlgPurchDateStr[STR_SIZE]; +static long carDlgServiceDate; +static char carDlgServiceDateStr[STR_SIZE]; +static long carDlgQuantity = 1; +static long carDlgMultiNum; + +static char *dispmodeLabels[] = { N_("Information"), N_("Customize"), NULL }; +static drawCmd_t carDlgD = { + NULL, + &screenDrawFuncs, + DC_NOCLIP, + 1.0, + 0.0, + { 0, 0 }, { 0, 0 }, + Pix2CoOrd, CoOrd2Pix }; +static void CarDlgRedraw(void); +static paramDrawData_t carDlgDrawData = { 455, 100, (wDrawRedrawCallBack_p)CarDlgRedraw, NULL, &carDlgD }; +static paramTextData_t notesData = { 440, 100 }; +static char *multinumLabels[] = { N_("Sequential"), N_("Repeated"), NULL }; +static void CarDlgNewProto( void ); +static void CarDlgUpdate( paramGroup_p, int, void * ); +static void CarDlgNewDesc( void ); +static void CarDlgNewProto( void ); + +static paramData_t carDlgPLs[] = { +#define A (0) +#define I_CD_MANUF_LIST (A+0) + { PD_DROPLIST, &carDlgManufInx, "manuf", PDO_NOPREF, (void*)350, N_("Manufacturer"), BL_EDITABLE }, +#define I_CD_PROTOTYPE_STR (A+1) + { PD_STRING, &carDlgProtoStr, "prototype", PDO_NOPREF, (void*)350, N_("Prototype") }, +#define I_CD_PROTOKIND_LIST (A+2) + { PD_DROPLIST, &carDlgKindInx, "protokind-list", PDO_NOPREF, (void*)125, N_("Prototype"), 0 }, +#define I_CD_PROTOTYPE_LIST (A+3) + { PD_DROPLIST, &carDlgProtoInx, "prototype-list", PDO_NOPREF|PDO_DLGHORZ, (void*)(225-3), NULL, 0 }, +#define I_CD_TYPE_LIST (A+4) + { PD_DROPLIST, &carDlgTypeInx, "type", PDO_NOPREF, (void*)350, N_("Type"), 0 }, +#define I_CD_PARTNO_LIST (A+5) + { PD_DROPLIST, &carDlgPartnoInx, "partno-list", PDO_NOPREF, (void*)350, N_("Part"), BL_EDITABLE }, +#define I_CD_PARTNO_STR (A+6) + { PD_STRING, &carDlgPartnoStr, "partno", PDO_NOPREF, (void*)350, N_("Part Number") }, +#define I_CD_ISLOCO (A+7) + { PD_TOGGLE, &carDlgIsLoco, "isLoco", PDO_NOPREF|PDO_DLGWIDE, isLocoLabels, N_("Loco?"), BC_HORZ|BC_NOBORDER }, +#define I_CD_DESC_STR (A+8) + { PD_STRING, &carDlgDescStr, "desc", PDO_NOPREF, (void*)350, N_("Description"), 0 }, +#define I_CD_IMPORT (A+9) + { PD_BUTTON, NULL, "import", 0, 0, N_("Import") }, +#define I_CD_RESET (A+10) + { PD_BUTTON, NULL, "reset", PDO_DLGHORZ, 0, N_("Reset") }, +#define I_CD_FLIP (A+11) + { PD_BUTTON, NULL, "flip", PDO_DLGHORZ|PDO_DLGWIDE|PDO_DLGBOXEND, 0, N_("Flip") }, + +#define I_CD_DISPMODE (A+12) + { PD_RADIO, &carDlgDispMode, "dispmode", PDO_NOPREF|PDO_DLGWIDE, dispmodeLabels, N_("Mode"), BC_HORZ|BC_NOBORDER }, + +#define B (A+13) +#define I_CD_ROADNAME_LIST (B+0) + { PD_DROPLIST, &carDlgRoadnameInx, "road", PDO_NOPREF|PDO_DLGWIDE, (void*)350, N_("Road"), BL_EDITABLE }, +#define I_CD_REPMARK (B+1) + { PD_STRING, carDlgRepmarkStr, "repmark", PDO_NOPREF, (void*)60, N_("Reporting Mark") }, +#define I_CD_NUMBER (B+2) + { PD_STRING, carDlgNumberStr, "number", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ, (void*)80, N_("Number") }, +#define I_CD_BODYCOLOR (B+3) + { PD_COLORLIST, &carDlgBodyColor, "bodyColor", PDO_DLGWIDE|PDO_DLGHORZ, NULL, N_("Color") }, +#define I_CD_CARLENGTH (B+4) + { PD_FLOAT, &carDlgDim.carLength, "carLength", PDO_DIM|PDO_NOPREF|PDO_DLGWIDE, &r0_99999, N_("Car Length") }, +#define I_CD_CARWIDTH (B+5) + { PD_FLOAT, &carDlgDim.carWidth, "carWidth", PDO_DIM|PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ, &r0_99999, N_("Width") }, +#define I_CD_TRKCENTER (B+6) + { PD_FLOAT, &carDlgDim.truckCenter, "trkCenter", PDO_DIM|PDO_NOPREF, &r0_99999, N_("Truck Centers") }, +#define I_CD_CPLRMNT (B+7) + { PD_RADIO, &carDlgCouplerMount, "cplrMount", PDO_NOPREF|PDO_DLGHORZ|PDO_DLGWIDE, cplrModeLabels, N_("Coupler Mount"), BC_HORZ|BC_NOBORDER }, +#define I_CD_CPLDLEN (B+8) + { PD_FLOAT, &carDlgDim.coupledLength, "cpldLen", PDO_DIM|PDO_NOPREF, &r0_99999, N_("Coupled Length") }, +#define I_CD_CPLRLEN (B+9) + { PD_FLOAT, &carDlgCouplerLength, "cplrLen", PDO_DIM|PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ, &r0_99999, N_("Coupler Length") }, +#define I_CD_CANVAS (B+10) + { PD_DRAW, NULL, "canvas", PDO_NOPSHUPD|PDO_DLGWIDE|PDO_DLGNOLABELALIGN|PDO_DLGRESETMARGIN|PDO_DLGBOXEND|PDO_DLGRESIZE, &carDlgDrawData, NULL, 0 }, + +#define C (B+11) +#define I_CD_ITEMINDEX (C+0) + { PD_LONG, &carDlgItemIndex, "index", PDO_NOPREF|PDO_DLGWIDE, &i1_999999999, N_("Index"), 0 }, +#define I_CD_PURPRC (C+1) + { PD_STRING, &carDlgPurchPriceStr, "purchPrice", PDO_NOPREF|PDO_DLGWIDE, (void*)50, N_("Purchase Price"), 0, &carDlgPurchPrice }, +#define I_CD_CURPRC (C+2) + { PD_STRING, &carDlgCurrPriceStr, "currPrice", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ, (void*)50, N_("Current Price"), 0, &carDlgCurrPrice }, +#define I_CD_COND (C+3) + { PD_DROPLIST, &carDlgConditionInx, "condition", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ, (void*)90, N_("Condition") }, +#define I_CD_PURDAT (C+4) + { PD_STRING, &carDlgPurchDateStr, "purchDate", PDO_NOPREF|PDO_DLGWIDE, (void*)80, N_("Purchase Date"), 0, &carDlgPurchDate }, +#define I_CD_SRVDAT (C+5) + { PD_STRING, &carDlgServiceDateStr, "serviceDate", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ, (void*)80, N_("Service Date"), 0, &carDlgServiceDate }, +#define I_CD_QTY (C+6) + { PD_LONG, &carDlgQuantity, "quantity", PDO_NOPREF|PDO_DLGWIDE, &i1_9999, N_("Quantity") }, +#define I_CD_MLTNUM (C+7) + { PD_RADIO, &carDlgMultiNum, "multinum", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ, multinumLabels, N_("Numbers"), BC_HORZ|BC_NOBORDER }, +#define I_CD_NOTES (C+8) + { PD_TEXT, NULL, "notes", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGNOLABELALIGN|PDO_DLGRESETMARGIN, ¬esData, N_("Notes") }, + +#define D (C+9) +#define I_CD_MSG (D+0) + { PD_MESSAGE, NULL, NULL, PDO_DLGNOLABELALIGN|PDO_DLGRESETMARGIN|PDO_DLGBOXEND, (void*)450 }, +#define I_CD_NEW (D+1) + { PD_MENU, NULL, "new-menu", PDO_DLGCMDBUTTON, NULL, N_("New"), 0, (void*)0 }, + { PD_MENUITEM, (void*)CarDlgNewDesc, "new-part-mi", 0, NULL, N_("Car Part"), 0, (void*)0 }, + { PD_MENUITEM, (void*)CarDlgNewProto, "new-proto-mi", 0, NULL, N_("Car Prototype"), 0, (void*)0 }, +#define I_CD_NEWPROTO (D+4) + { PD_BUTTON, (void*)CarDlgNewProto, "new", PDO_DLGCMDBUTTON, NULL, N_("New"), 0, (void*)0 } }; + +static paramGroup_t carDlgPG = { "carpart", 0, carDlgPLs, sizeof carDlgPLs/sizeof carDlgPLs[0] }; + + +static dynArr_t carDlgSegs_da; +#define carDlgSegs(N) DYNARR_N( trkSeg_t, carDlgSegs_da, N ) + + +typedef enum { + T_ItemSel, T_ItemEnter, T_ProtoSel, T_ProtoEnter, T_PartnoSel, T_PartnoEnter } carDlgTransistion_e; +static char *carDlgTransistion_s[] = { + "ItemSel", "ItemEnter", "ProtoSel", "ProtoEnter", "PartnoSel", "PartnoEnter" }; +typedef enum { + S_Error, + S_ItemSel, S_ItemEnter, S_PartnoSel, S_PartnoEnter, S_ProtoSel } carDlgState_e; +static char *carDlgState_s[] = { + "Error", + "ItemSel", "ItemEnter", "PartnoSel", "PartnoEnter", "ProtoSel" }; +typedef enum { + A_Return, + A_SError, + A_Else, + A_SItemSel, + A_SItemEnter, + A_SPartnoSel, + A_SPartnoEnter, + A_SProtoSel, + A_IsCustom, + A_IsNewPart, + A_IsNewProto, + A_LoadDataFromPartList, + A_LoadDimsFromStack, + A_LoadManufListForScale, + A_LoadManufListAll, + A_LoadProtoListForManuf, + A_LoadProtoListAll, + A_LoadPartnoList, + A_LoadLists, + A_LoadDimsFromProtoList, + A_ConvertDimsToProto, + A_Redraw, + A_ClrManuf, + A_ClrPartnoStr, + A_ClrNumberStr, + A_LoadProtoStrFromList, + A_ShowPartnoList, + A_HidePartnoList, + A_PushDims, + A_PopDims, + A_PopTitleAndTypeinx, + A_PopCouplerLength, + A_ShowControls, + A_LoadInfoFromUpdateItem, + A_LoadDataFromUpdatePart, + A_InitProto, + A_RecallCouplerLength, + A_Last + } carDlgAction_e; +static char *carDlgAction_s[] = { + "Return", + "SError", + "Else", + "SItemSel", + "SItemEnter", + "SPartnoSel", + "SPartnoEnter", + "SProtoSel", + "IsCustom", + "IsNewPart", + "IsNewProto", + "LoadDataFromPartList", + "LoadDimsFromStack", + "LoadManufListForScale", + "LoadManufListAll", + "LoadProtoListForManuf", + "LoadProtoListAll", + "LoadPartnoList", + "LoadLists", + "LoadDimsFromProtoList", + "ConvertDimsToProto", + "Redraw", + "ClrManuf", + "ClrPartnoStr", + "ClrNumberStr", + "LoadProtoStrFromList", + "ShowPartnoList", + "HidePartnoList", + "PushDims", + "PopDims", + "PopTitleAndTypeinx", + "PopCouplerLength", + "ShowControls", + "LoadInfoFromUpdateItem", + "LoadDataFromUpdatePart", + "InitProto", + "RecallCouplerLength", + "Last" + }; +static carDlgAction_e stateMachine[7][7][10] = { +/* A_SError */{ {A_SError}, {A_SError}, {A_SError}, {A_SError}, {A_SError}, {A_SError}, {A_SError} }, + +/*A_SItemSel*/{ +/*T_ItemSel*/ { A_LoadProtoListForManuf, A_LoadPartnoList, A_LoadDataFromPartList, A_Redraw }, +/*T_ItemEnter*/ { A_SItemEnter, A_LoadProtoListAll, A_ClrPartnoStr, A_ClrNumberStr, A_LoadDimsFromProtoList, A_Redraw, A_HidePartnoList }, +/*T_ProtoSel*/ { A_LoadPartnoList, A_LoadDataFromPartList, A_Redraw }, +/*T_ProtoEnter*/ { A_SError }, +/*T_PartnoSel*/ { A_LoadDataFromPartList, A_Redraw }, +/*T_PartnoEnter*/{ A_SItemEnter, A_LoadProtoListAll, A_HidePartnoList } }, + +/*A_SItemEnter*/{ +/*T_ItemSel*/ { A_SItemSel, A_LoadProtoListForManuf, A_LoadPartnoList, A_LoadDataFromPartList, A_Redraw, A_ShowPartnoList }, +/*T_ItemEnter*/ { A_Return }, +/*T_ProtoSel*/ { A_LoadDimsFromProtoList, A_Redraw }, +/*T_ProtoEnter*/ { A_SError }, +/*T_PartnoSel*/ { A_SError }, +/*T_PartnoEnter*/{ A_Return } }, + +/*A_SPartnoSel*/{ +/*T_ItemSel*/ { A_SPartnoSel }, +/*T_ItemEnter*/ { A_SPartnoSel }, +/*T_ProtoSel*/ { A_SPartnoSel, A_LoadDimsFromProtoList, A_Redraw }, +/*T_ProtoEnter*/ { A_SError }, +/*T_PartnoSel*/ { A_SError } }, + +/*A_SPartnoEnter*/{ +/*T_ItemSel*/ { A_SPartnoSel }, +/*T_ItemEnter*/ { A_SPartnoEnter }, +/*T_ProtoSel*/ { A_SPartnoEnter, A_LoadDimsFromProtoList, A_Redraw }, +/*T_ProtoEnter*/ { A_SError }, +/*T_PartnoSel*/ { A_SError }, +/*T_PartnoEnter*/{ A_SPartnoEnter } }, + +/*A_SProtoSel*/{ +/*T_ItemSel*/ { A_SError }, +/*T_ItemEnter*/ { A_SError }, +/*T_ProtoSel*/ { A_SError }, +/*T_ProtoEnter*/ { A_SProtoSel }, +/*T_PartnoSel*/ { A_SError }, +/*T_PartnoEnter*/{ A_SError } } }; + +static carDlgAction_e itemNewActions[] = { + A_RecallCouplerLength, + A_LoadLists, + A_IsCustom, 2+3, + A_LoadDimsFromProtoList, A_ClrPartnoStr, A_ClrNumberStr, + A_Else, 1, + A_LoadDataFromPartList, + A_ShowControls, A_Return }; +static carDlgAction_e itemUpdActions[] = { A_LoadInfoFromUpdateItem, /*A_LoadManufListForScale, + A_IsCustom, 5, + A_LoadProtoListAll, A_HidePartnoList, A_SItemEnter, + A_Else, 5, + A_LoadProtoListForManuf, A_LoadPartnoList, A_LoadDataFromPartList, A_ShowPartnoList, A_SItemSel,*/ + A_ShowControls, A_Return }; + +static carDlgAction_e partNewActions[] = { A_RecallCouplerLength, A_LoadManufListAll, A_LoadProtoListAll, A_ClrPartnoStr, A_ClrNumberStr, A_SPartnoSel, A_LoadDimsFromProtoList, A_ShowControls, A_Redraw, A_Return }; +static carDlgAction_e partUpdActions[] = { A_LoadDataFromUpdatePart, A_SPartnoSel, A_ShowControls, A_Return }; + +static carDlgAction_e protoNewActions[] = { A_InitProto, A_SProtoSel, A_ShowControls, A_Return }; +static carDlgAction_e protoUpdActions[] = { A_InitProto, A_SProtoSel, A_ShowControls, A_Return }; + +static carDlgAction_e item2partActions[] = { + A_PushDims, A_LoadManufListAll, A_LoadProtoListAll, + A_IsCustom, 0+1, + A_ClrManuf, + A_SPartnoSel, + A_ShowControls, A_Return }; +static carDlgAction_e part2itemActions[] = { + A_IsNewPart, 2+0, + A_Else, 1, + A_PopTitleAndTypeinx, + A_LoadLists, + A_IsCustom, 2+1, + A_LoadDimsFromProtoList, + A_Else, 1, + A_LoadDataFromPartList, +#ifdef LATER + A_IsNewPart, 2+0, + A_Else, 1, + A_LoadDimsFromStack, +#endif + A_ShowControls, + A_Return }; + +static carDlgAction_e item2protoActions[] = { A_PushDims, A_ConvertDimsToProto, A_SProtoSel, A_ShowControls, A_Return }; +static carDlgAction_e proto2itemActions[] = { + A_IsCustom, 2+2+3, + A_IsNewProto, 2+3, + A_LoadProtoListAll, + A_PopCouplerLength, + A_LoadDimsFromProtoList, + A_Else, 2, + A_LoadDimsFromStack, + A_LoadProtoStrFromList, + A_ShowControls, + A_Return }; + +static carDlgAction_e part2protoActions[] = { A_PushDims, A_ConvertDimsToProto, A_SProtoSel, A_ShowControls, A_Return }; +static carDlgAction_e proto2partActions[] = { + A_IsNewProto, 2+3, + A_LoadProtoListAll, + A_PopCouplerLength, + A_LoadDimsFromProtoList, + A_Else, 2, + A_LoadDimsFromStack, + A_LoadProtoStrFromList, + A_ShowControls, + A_Return }; + + +#define CARDLG_STK_SIZE (2) +int carDlgStkPtr = 0; +struct { + carDim_t dim; + DIST_T couplerLength; + carDlgState_e state; + int changed; + carPart_p partP; + wIndex_t typeInx; + } carDlgStk[CARDLG_STK_SIZE]; + +static carDlgState_e currState = S_Error; +#define S_ITEM (currState==S_ItemSel||currState==S_ItemEnter) +#define S_PART (currState==S_PartnoSel) +#define S_PROTO (currState==S_ProtoSel) + + + +static void CarDlgLoadDimsFromPart( carPart_p partP ) +{ + tabString_t tabs[7]; + + if ( partP == NULL ) return; + carDlgDim = partP->dim; + carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; + sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); + wPrefSetFloat( carDlgPG.nameStr, message, carDlgCouplerLength ); + carDlgIsLoco = (partP->options&CAR_DESC_IS_LOCO)?1:0; + carDlgBodyColor = partP->color; + ParamLoadControl( &carDlgPG, I_CD_CARLENGTH ); + ParamLoadControl( &carDlgPG, I_CD_CARWIDTH ); + ParamLoadControl( &carDlgPG, I_CD_TRKCENTER ); + ParamLoadControl( &carDlgPG, I_CD_CPLDLEN ); + wColorSelectButtonSetColor( (wButton_p)carDlgPLs[I_CD_BODYCOLOR].control, *(wDrawColor*)carDlgPLs[I_CD_BODYCOLOR].valueP ); + TabStringExtract( partP->title, 7, tabs ); +} + + +static void CarDlgLoadDimsFromProto( carProto_p protoP ) +{ + DIST_T ratio = GetScaleRatio(carDlgScaleInx); + carDlgDim.carLength = protoP->dim.carLength/ratio; + carDlgDim.carWidth = protoP->dim.carWidth/ratio; + carDlgDim.truckCenter = protoP->dim.truckCenter/ratio; + carDlgDim.coupledLength = carDlgDim.carLength + carDlgCouplerLength*2; + /*carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0;*/ + carDlgIsLoco = (protoP->options&CAR_DESC_IS_LOCO)?1:0; + ParamLoadControl( &carDlgPG, I_CD_CARLENGTH ); + ParamLoadControl( &carDlgPG, I_CD_CARWIDTH ); + ParamLoadControl( &carDlgPG, I_CD_TRKCENTER ); + ParamLoadControl( &carDlgPG, I_CD_CPLDLEN ); +} + + +static void CarDlgRedraw( void ) +{ + wPos_t w, h; + DIST_T ww, hh; + DIST_T scale_w, scale_h; + coOrd orig, pos, size; + carProto_p protoP; + FLOAT_T ratio; + int segCnt; + trkSeg_p segPtr; + + if ( S_PROTO ) + ratio = 1; + else + ratio = 1/GetScaleRatio(carDlgScaleInx); + wDrawClear( carDlgD.d ); + if ( carDlgDim.carLength <= 0 || carDlgDim.carWidth <= 0 ) + return; + FreeFilledDraw( carDlgSegs_da.cnt, &carDlgSegs(0) ); + if ( !S_PROTO ) { + if ( carDlgProtoInx < 0 || + (protoP = CarProtoLookup( carDlgProtoStr, FALSE, FALSE, 0.0, 0.0 )) == NULL || + protoP->segCnt == 0 ) { + CarProtoDlgCreateDummyOutline( &segCnt, &segPtr, (BOOL_T)carDlgIsLoco, carDlgDim.carLength, carDlgDim.carWidth, carDlgBodyColor ); + } else { + segCnt = protoP->segCnt; + segPtr = protoP->segPtr; + } + } else { + if ( carProtoSegCnt <= 0 ) { + CarProtoDlgCreateDummyOutline( &segCnt, &segPtr, (BOOL_T)carDlgIsLoco, carDlgDim.carLength, carDlgDim.carWidth, drawColorBlue ); + } else { + segCnt = carProtoSegCnt; + segPtr = carProtoSegPtr; + } + } + DYNARR_SET( trkSeg_t, carDlgSegs_da, segCnt ); + memcpy( &carDlgSegs(0), segPtr, segCnt * sizeof *(trkSeg_t*)0 ); + CloneFilledDraw( carDlgSegs_da.cnt, &carDlgSegs(0), TRUE ); + GetSegBounds( zero, 0.0, carDlgSegs_da.cnt, &carDlgSegs(0), &orig, &size ); + scale_w = carDlgDim.carLength/size.x; + scale_h = carDlgDim.carWidth/size.y; + RescaleSegs( carDlgSegs_da.cnt, &carDlgSegs(0), scale_w, scale_h, ratio ); + if ( !S_PROTO ) { + RecolorSegs( carDlgSegs_da.cnt, &carDlgSegs(0), carDlgBodyColor ); + } else { + if ( carDlgFlipToggle ) { + pos.x = carDlgDim.carLength/2.0; + pos.y = carDlgDim.carWidth/2.0; + RotateSegs( carDlgSegs_da.cnt, &carDlgSegs(0), pos, 180.0 ); + } + } + + wDrawGetSize( carDlgD.d, &w, &h ); + ww = w/carDlgD.dpi-1.0; + hh = h/carDlgD.dpi-0.5; + scale_w = carDlgDim.carLength/ww; + scale_h = carDlgDim.carWidth/hh; + if ( scale_w > scale_h ) + carDlgD.scale = scale_w; + else + carDlgD.scale = scale_h; + orig.x = 0.50*carDlgD.scale; + orig.y = 0.25*carDlgD.scale; + DrawSegs( &carDlgD, orig, 0.0, &carDlgSegs(0), carDlgSegs_da.cnt, 0.0, wDrawColorBlack ); + pos.y = orig.y+carDlgDim.carWidth/2.0; + + if ( carDlgDim.truckCenter > 0.0 ) { + pos.x = orig.x+(carDlgDim.carLength-carDlgDim.truckCenter)/2.0; + CarProtoDrawTruck( &carDlgD, trackGauge*curScaleRatio, ratio, pos, 0.0 ); + pos.x = orig.x+(carDlgDim.carLength+carDlgDim.truckCenter)/2.0; + CarProtoDrawTruck( &carDlgD, trackGauge*curScaleRatio, ratio, pos, 0.0 ); + } + if ( carDlgDim.coupledLength > carDlgDim.carLength ) { + pos.x = orig.x; + CarProtoDrawCoupler( &carDlgD, (carDlgDim.coupledLength-carDlgDim.carLength)/2.0, ratio, pos, 270.0 ); + pos.x = orig.x+carDlgDim.carLength; + CarProtoDrawCoupler( &carDlgD, (carDlgDim.coupledLength-carDlgDim.carLength)/2.0, ratio, pos, 90.0 ); + } +} + + + +static void CarDlgLoadRoadnameList( void ) +/* Loads RoadnameList. + * Set carDlgRoadnameInx to entry matching carDlgRoadnameStr (if found) + * Otherwise not set + */ +{ + wIndex_t inx; + roadnameMap_p roadnameMapP; + + if ( !roadnameMapChanged ) return; + wListClear( (wList_p)carDlgPLs[I_CD_ROADNAME_LIST].control ); + wListAddValue( (wList_p)carDlgPLs[I_CD_ROADNAME_LIST].control, _("Undecorated"), NULL, NULL ); + for ( inx=0; inx<roadnameMap_da.cnt; inx++ ) { + roadnameMapP = DYNARR_N(roadnameMap_p, roadnameMap_da, inx); + wListAddValue( (wList_p)carDlgPLs[I_CD_ROADNAME_LIST].control, roadnameMapP->roadname, NULL, roadnameMapP ); + if ( strcasecmp( carDlgRoadnameStr, roadnameMapP->roadname )==0 ) + carDlgRoadnameInx = inx+1; + } + roadnameMapChanged = FALSE; +} + + +static BOOL_T CheckAvail( + carPartParent_p parentP ) +{ + wIndex_t inx; + carPart_p partP; + for ( inx=0; inx<parentP->parts_da.cnt; inx++ ) { + partP = carPart(parentP,inx); + if ( IsParamValid(partP->paramFileIndex) ) + return TRUE; + } + return FALSE; +} + + +static BOOL_T CarDlgLoadManufList( + BOOL_T bLoadAll, + BOOL_T bInclCustomUnknown, + SCALEINX_T scale ) +{ + carPartParent_p manufP, manufP1; + wIndex_t inx, listInx=-1; + BOOL_T found = TRUE; + char * firstName = NULL; + +LOG( log_carDlgList, 3, ( "CarDlgLoadManufList( %s, %s, %d )\n carDlgManufStr=\"%s\"\n", bLoadAll?"TRUE":"FALSE", bInclCustomUnknown?"TRUE":"FALSE", scale, carDlgManufStr ) ) + carDlgManufInx = -1; + manufP1 = NULL; + wListClear( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control ); + for ( inx=0; inx<carPartParent_da.cnt; inx++ ) { + manufP = carPartParent(inx); + if ( manufP1!=NULL && strcasecmp( manufP1->manuf, manufP->manuf ) == 0 ) + continue; + if ( bLoadAll==FALSE && manufP->scale != scale ) + continue; + if ( !CheckAvail(manufP) ) + continue; + listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, manufP->manuf, NULL, (void*)manufP ); + if ( carDlgManufInx < 0 && ( carDlgManufStr[0] == '\0' || strcasecmp( carDlgManufStr, manufP->manuf ) == 0 ) ) { +LOG( log_carDlgList, 4, ( " found manufStr (inx=%d, listInx=%d)\n", inx, listInx ) ) + carDlgManufInx = listInx; + if ( carDlgManufStr[0] == '\0' ) strcpy( carDlgManufStr, manufP->manuf ); + } + if ( firstName == NULL ) + firstName = manufP->manuf; + manufP1 = manufP; + } + if ( bInclCustomUnknown ) { + listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, _("Custom"), NULL, (void*)NULL ); + if ( carDlgManufInx < 0 && ( carDlgManufStr[0] == '\0' || strcasecmp( carDlgManufStr, "Custom" ) == 0 ) ) { +LOG( log_carDlgList, 4, ( " found Cus manufStr (inx=%d, listInx=%d)\n", inx, listInx ) ) + carDlgManufInx = listInx; + if ( carDlgManufStr[0] == '\0' ) strcpy( carDlgManufStr, _("Custom") ); + } + if ( firstName == NULL ) + firstName = "Custom"; + wListAddValue( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, _("Unknown"), NULL, (void*)NULL ); + if ( carDlgManufInx < 0 && ( carDlgManufStr[0] == '\0' || strcasecmp( carDlgManufStr, "Unknown" ) == 0 ) ) { +LOG( log_carDlgList, 4, ( " found Unk manufStr (inx=%d, listInx=%d)\n", inx, listInx ) ) + carDlgManufInx = listInx; + if ( carDlgManufStr[0] == '\0' ) strcpy( carDlgManufStr, _("Unknown") ); + } + } + if ( carDlgManufInx < 0 ) { + found = FALSE; + if ( firstName != NULL ) { +LOG( log_carDlgList, 4, ( " didn't find manufStr, using [0] = %s\n", firstName ) ) + carDlgManufInx = 0; + strcpy( carDlgManufStr, firstName ); + } + } + return found; +} + + +static BOOL_T CarDlgLoadProtoList( + char * manuf, + SCALEINX_T scale, + BOOL_T loadTypeList ) +{ + carPartParent_p parentP; + wIndex_t inx, listInx, inx1; + BOOL_T found; + carProto_p protoP; + carPart_p partP; + char * firstName; + int typeCount[N_TYPELISTMAP]; + int listTypeInx, currTypeInx; + + listTypeInx = -1; + carDlgProtoInx = -1; + firstName = NULL; + + wListClear( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control ); + memset( typeCount, 0, N_TYPELISTMAP * sizeof typeCount[0] ); +LOG( log_carDlgList, 3, ( "CarDlgLoadProtoList( %s, %d, %s )\n carDlgProtoStr=\"%s\", carDlgTypeInx=%d\n", manuf?manuf:"NULL", scale, loadTypeList?"TRUE":"FALSE", carDlgProtoStr, carDlgTypeInx ) ) + if ( manuf==NULL ) { + if ( carProto_da.cnt <= 0 ) return FALSE; + if ( listTypeInx < 0 && carDlgProtoStr[0] && (protoP=CarProtoFind(carDlgProtoStr)) ) + listTypeInx = CarProtoFindTypeCode(protoP->type); + if ( listTypeInx < 0 ) + listTypeInx = CarProtoFindTypeCode(carProto(0)->type); + for ( inx=0; inx<carProto_da.cnt; inx++ ) { + protoP = carProto(inx); + currTypeInx = CarProtoFindTypeCode(protoP->type); + typeCount[currTypeInx]++; + if ( carDlgTypeInx >= 0 && + listTypeInx != carDlgTypeInx && + currTypeInx == carDlgTypeInx ) { +LOG( log_carDlgList, 4, ( " found typeinx, reset list (old=%d)\n", listTypeInx ) ) + wListClear( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control ); + listTypeInx = carDlgTypeInx; + carDlgProtoInx = -1; + firstName = NULL; + } + if ( currTypeInx != listTypeInx ) continue; + listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, protoP->desc, NULL, (void*)protoP ); + if ( carDlgProtoInx < 0 && carDlgProtoStr[0] && strcasecmp( carDlgProtoStr, protoP->desc ) == 0 ) { +LOG( log_carDlgList, 4, ( " found protoStr (inx=%d, listInx=%d)\n", inx, listInx ) ) + carDlgProtoInx = listInx; + if ( carDlgProtoStr[0] == '\0' ) strcpy( carDlgProtoStr, protoP->desc ); + } + if ( firstName == NULL ) + firstName = protoP->desc; + } + } else { + for ( inx=0; inx<carPartParent_da.cnt; inx++ ) { + parentP = carPartParent(inx); + if ( strcasecmp( manuf, parentP->manuf ) != 0 || + scale != parentP->scale ) + continue; + if ( !CheckAvail(parentP) ) + continue; + found = FALSE; + for ( inx1=0; inx1<parentP->parts_da.cnt; inx1++ ) { + partP = carPart( parentP, inx1 ); + currTypeInx = CarProtoFindTypeCode(partP->type); + typeCount[currTypeInx]++; + if ( listTypeInx < 0 ) + listTypeInx = currTypeInx; + if ( carDlgTypeInx >= 0 && + listTypeInx != carDlgTypeInx && + currTypeInx == carDlgTypeInx ) { +LOG( log_carDlgList, 4, ( " found typeinx, reset list (old=%d)\n", listTypeInx ) ) + wListClear( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control ); + listTypeInx = carDlgTypeInx; + carDlgProtoInx = -1; + firstName = NULL; + } + if ( listTypeInx == currTypeInx ) + found = TRUE; + } + if ( !found ) + continue; + listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, parentP->proto, NULL, (void*)parentP ); + if ( carDlgProtoInx < 0 && ( carDlgProtoStr[0] == '\0' || strcasecmp( carDlgProtoStr, parentP->proto ) == 0 ) ) { +LOG( log_carDlgList, 4, ( " found protoStr (inx=%d, listInx=%d)\n", inx, listInx ) ) + carDlgProtoInx = listInx; + if ( carDlgProtoStr[0] == '\0' ) { + strcpy( carDlgProtoStr, parentP->proto ); + } + } + if ( firstName == NULL ) + firstName = parentP->proto; + } + } + + found = TRUE; + if ( carDlgProtoInx < 0 ) { + found = FALSE; + if ( firstName != NULL ) { +LOG( log_carDlgList, 4, ( " didn't find protoStr, using [0] = %s\n", firstName ) ) + carDlgProtoInx = 0; + strcpy( carDlgProtoStr, firstName ); + } + } + wListSetIndex( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); + + if ( loadTypeList ) { +LOG( log_carDlgList, 4, ( " loading typelist\n" ) ) + wListClear( (wList_p)carDlgPLs[I_CD_PROTOKIND_LIST].control ); + for ( currTypeInx=0; currTypeInx<N_TYPELISTMAP; currTypeInx++ ) { + if ( typeCount[currTypeInx] > 0 ) { + listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_PROTOKIND_LIST].control, _(typeListMap[currTypeInx].name), NULL, (void*)(intptr_t)currTypeInx ); + if ( currTypeInx == listTypeInx ) { +LOG( log_carDlgList, 4, ( " current = %d\n", listInx ) ) + carDlgKindInx = listInx; + } + } + } + } + + return found; +} + + +static void ConstructPartDesc( + tabString_t * tabs ) +{ + char * cp; + cp = message; + *cp = '\0'; + if ( tabs[T_PART].len ) { + cp = TabStringCpy( cp, &tabs[T_PART] ); + *cp++ = ' '; + } + if ( tabs[T_DESC].len ) { + cp = TabStringCpy( cp, &tabs[T_DESC] ); + *cp++ = ' '; + } + if ( tabs[T_REPMARK].len ) { + cp = TabStringCpy( cp, &tabs[T_REPMARK] ); + *cp++ = ' '; + } else if ( tabs[T_ROADNAME].len ) { + cp = TabStringCpy( cp, &tabs[T_ROADNAME] ); + *cp++ = ' '; + } else { + strcpy( cp, _("Undecorated ") ); + cp += strlen( cp ); + } + if ( tabs[T_NUMBER].len ) { + cp = TabStringCpy( cp, &tabs[T_NUMBER] ); + *cp++ = ' '; + } + *cp = '\0'; +} + + +static BOOL_T CarDlgLoadPartList( carPartParent_p parentP ) +/* Loads PartList from parentP + * Set carDlgPartnoInx to entry matching carDlgPartnoStr (if set and found) + * Otherwise set carDlgPartnoInx and carDlgPartnoStr to 1st entry on list + * Set carDlgDescStr to found entry + */ +{ + wIndex_t listInx; + wIndex_t inx; + carPart_p partP; + carPart_t lastPart; + tabString_t tabs[7]; + BOOL_T found; + carPart_p selPartP; + + carDlgPartnoInx = -1; + wListClear( (wList_p)carDlgPLs[I_CD_PARTNO_LIST].control ); + if ( parentP==NULL ) { + carDlgPartnoStr[0] = '\0'; + carDlgDescStr[0] = '\0'; + return FALSE; + } + found = FALSE; + selPartP = NULL; + lastPart.title = NULL; + for ( inx=0; inx<parentP->parts_da.cnt; inx++ ) { + partP = carPart(parentP,inx); + TabStringExtract( partP->title, 7, tabs ); + ConstructPartDesc( tabs ); + lastPart.paramFileIndex = partP->paramFileIndex; + if ( message[0] && IsParamValid(partP->paramFileIndex) && + ( lastPart.title == NULL || Cmp_part( &lastPart, partP ) != 0 ) ) { + listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_PARTNO_LIST].control, message, NULL, (void*)partP ); + if ( carDlgPartnoInx<0 && + (carDlgPartnoStr[0]?TabStringCmp( carDlgPartnoStr, &tabs[T_PART] ) == 0:TRUE) ) { + carDlgPartnoInx = listInx; + found = TRUE; + selPartP = partP; + } + if ( selPartP == NULL ) + selPartP = partP; + lastPart = *partP; + } + } + if ( selPartP == NULL ) { + carDlgPartnoStr[0] = '\0'; + carDlgDescStr[0] = '\0'; + } else { + if ( carDlgPartnoInx<0 ) + carDlgPartnoInx = 0; + TabStringExtract( selPartP->title, 7, tabs ); + TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); + TabStringCpy( carDlgDescStr, &tabs[T_DESC] ); + } + return found; +} + + + +static void CarDlgLoadPart( + carPart_p partP ) +{ + tabString_t tabs[7]; + roadnameMap_p roadnameMapP; + CarDlgLoadDimsFromPart( partP ); + carDlgBodyColor = partP->color; + carDlgTypeInx = CarProtoFindTypeCode( partP->type ); + carDlgIsLoco = ((partP->type)&1)!=0; + TabStringExtract( partP->title, 7, tabs ); + TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); + TabStringCpy( carDlgDescStr, &tabs[T_DESC] ); + roadnameMapP = LoadRoadnameList( &tabs[T_ROADNAME], &tabs[T_REPMARK] ); + carDlgRoadnameInx = lookupListIndex+1; + if ( roadnameMapP ) { + TabStringCpy( carDlgRoadnameStr, &tabs[T_ROADNAME] ); + CarDlgLoadRoadnameList(); + TabStringCpy( carDlgRepmarkStr, &tabs[T_REPMARK] ); + } else { + carDlgRoadnameInx = 0; + strcpy( carDlgRoadnameStr, _("Undecorated") ); + carDlgRepmarkStr[0] = '\0'; + } + TabStringCpy( carDlgNumberStr, &tabs[T_NUMBER] ); + carDlgBodyColor = partP->color; +} + + +static BOOL_T CarDlgLoadLists( + BOOL_T isItem, + tabString_t * tabs, + SCALEINX_T scale ) +{ + BOOL_T loadCustomUnknown = isItem; + DIST_T ratio; + carPartParent_p parentP; + static carProto_t protoTmp; + static char protoTmpDesc[STR_SIZE]; + + if ( tabs ) TabStringCpy( carDlgManufStr, &tabs[T_MANUF] ); + if ( strcasecmp( carDlgManufStr, "unknown" ) == 0 || + strcasecmp( carDlgManufStr, "custom" ) == 0 ) { + loadCustomUnknown = TRUE; + /*isItem = FALSE;*/ + } + if ( (!CarDlgLoadManufList( !isItem, loadCustomUnknown, scale )) && tabs ) { + TabStringCpy( carDlgManufStr, &tabs[T_MANUF] ); + carDlgManufInx = wListAddValue( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, carDlgManufStr, NULL, (void*)NULL ); + isItem = FALSE; + } + if ( isItem ) { + parentP = (carPartParent_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, carDlgManufInx ); + if ( parentP ) { + if ( tabs ) TabStringCpy( carDlgProtoStr, &tabs[T_PROTO] ); + if ( CarDlgLoadProtoList( carDlgManufStr, scale, TRUE ) || !tabs ) { + parentP = (carPartParent_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); + if ( parentP ) { + if ( tabs ) TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); + if ( CarDlgLoadPartList( parentP ) || ( (!tabs) && carDlgPartnoInx>=0 ) ) { + return TRUE; + } + } + } + } + } + if ( tabs ) TabStringCpy( carDlgProtoStr, &tabs[T_PROTO] ); + if ( !CarDlgLoadProtoList( NULL, 0, TRUE ) && tabs ) { + /* create dummy proto */ + ratio = GetScaleRatio( scale ); + protoTmp.contentsLabel = "temporary"; + protoTmp.paramFileIndex = PARAM_LAYOUT; + strcpy( protoTmpDesc, carDlgProtoStr ); + protoTmp.desc = protoTmpDesc; + protoTmp.options = (carDlgIsLoco?CAR_DESC_IS_LOCO:0); + protoTmp.type = typeListMap[carDlgTypeInx].value; + protoTmp.dim.carWidth = carDlgDim.carWidth*ratio; + protoTmp.dim.carLength = carDlgDim.carLength*ratio; + protoTmp.dim.coupledLength = carDlgDim.coupledLength*ratio; + protoTmp.dim.truckCenter = carDlgDim.truckCenter*ratio; + CarProtoDlgCreateDummyOutline( &carProtoSegCnt, &carProtoSegPtr, (BOOL_T)carDlgIsLoco, protoTmp.dim.carLength, protoTmp.dim.carWidth, drawColorBlue ); + protoTmp.segCnt = carProtoSegCnt; + protoTmp.segPtr = carProtoSegPtr; + GetSegBounds( zero, 0.0, carProtoSegCnt, carProtoSegPtr, &protoTmp.orig, &protoTmp.size ); + TabStringCpy( carDlgProtoStr, &tabs[T_PROTO] ); + carDlgProtoInx = wListAddValue( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoStr, NULL, &protoTmp );/*??*/ + } + carDlgPartnoInx = -1; + if ( tabs ) { + TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); + TabStringCpy( carDlgDescStr, &tabs[T_DESC] ); + } + return FALSE; +} + + +static void CarDlgShowControls( void ) +{ + + + /*ParamControlActive( &carDlgPG, I_CD_MANUF_LIST, S_ITEM||(S_PART&&carDlgUpdatePartPtr) );*/ + + ParamControlShow( &carDlgPG, I_CD_NEW, S_ITEM ); + ParamControlShow( &carDlgPG, I_CD_NEWPROTO, S_PART ); + + ParamControlShow( &carDlgPG, I_CD_ITEMINDEX, S_ITEM && carDlgDispMode==0 ); + ParamControlShow( &carDlgPG, I_CD_PURPRC, S_ITEM && carDlgDispMode==0 ); + ParamControlShow( &carDlgPG, I_CD_CURPRC, S_ITEM && carDlgDispMode==0 ); + ParamControlShow( &carDlgPG, I_CD_COND, S_ITEM && carDlgDispMode==0 ); + ParamControlShow( &carDlgPG, I_CD_PURDAT, S_ITEM && carDlgDispMode==0 ); + ParamControlShow( &carDlgPG, I_CD_SRVDAT, S_ITEM && carDlgDispMode==0 ); + ParamControlShow( &carDlgPG, I_CD_NOTES, S_ITEM && carDlgDispMode==0 ); + ParamControlShow( &carDlgPG, I_CD_MLTNUM, S_ITEM && carDlgUpdateItemPtr==NULL && carDlgDispMode==0 ); + ParamControlShow( &carDlgPG, I_CD_QTY, S_ITEM && carDlgUpdateItemPtr==NULL && carDlgDispMode==0 ); + + ParamControlShow( &carDlgPG, I_CD_ROADNAME_LIST, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); + ParamControlShow( &carDlgPG, I_CD_REPMARK, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); + ParamControlShow( &carDlgPG, I_CD_NUMBER, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); + ParamControlShow( &carDlgPG, I_CD_BODYCOLOR, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); + ParamControlShow( &carDlgPG, I_CD_CARLENGTH, !( S_ITEM && carDlgDispMode==0 ) ); + ParamControlShow( &carDlgPG, I_CD_CARWIDTH, !( S_ITEM && carDlgDispMode==0 ) ); + ParamControlShow( &carDlgPG, I_CD_TRKCENTER, !( S_ITEM && carDlgDispMode==0 ) ); + ParamControlShow( &carDlgPG, I_CD_CANVAS, !( S_ITEM && carDlgDispMode==0 ) ); + ParamControlShow( &carDlgPG, I_CD_CPLRLEN, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); + ParamControlShow( &carDlgPG, I_CD_CPLDLEN, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); + ParamControlShow( &carDlgPG, I_CD_CPLRMNT, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); + + ParamControlShow( &carDlgPG, I_CD_DISPMODE, S_ITEM ); + + ParamControlShow( &carDlgPG, I_CD_TYPE_LIST, S_PROTO ); + ParamControlShow( &carDlgPG, I_CD_FLIP, S_PROTO ); + ParamControlShow( &carDlgPG, I_CD_DESC_STR, S_PART || (currState==S_ItemEnter) ); + ParamControlShow( &carDlgPG, I_CD_IMPORT, S_PROTO ); + ParamControlShow( &carDlgPG, I_CD_RESET, S_PROTO ); + ParamControlShow( &carDlgPG, I_CD_PARTNO_STR, S_PART || (currState==S_ItemEnter) ); + ParamControlShow( &carDlgPG, I_CD_PARTNO_LIST, (currState==S_ItemSel) ); + ParamControlShow( &carDlgPG, I_CD_ISLOCO, S_PROTO ); + ParamControlShow( &carDlgPG, I_CD_PROTOKIND_LIST, !S_PROTO ); + ParamControlShow( &carDlgPG, I_CD_PROTOTYPE_LIST, !S_PROTO ); + ParamControlShow( &carDlgPG, I_CD_PROTOTYPE_STR, S_PROTO ); + ParamControlShow( &carDlgPG, I_CD_MANUF_LIST, !S_PROTO ); + + /*ParamControlActive( &carDlgPG, I_CD_PROTOTYPE_STR, S_PROTO && carDlgUpdateProtoPtr==NULL );*/ + ParamControlActive( &carDlgPG, I_CD_ITEMINDEX, S_ITEM && carDlgUpdateItemPtr==NULL ); + ParamControlActive( &carDlgPG, I_CD_MLTNUM, S_ITEM && carDlgQuantity>1 ); + ParamControlActive( &carDlgPG, I_CD_IMPORT, selectedTrackCount > 0 ); + + ParamLoadMessage( &carDlgPG, I_CD_MSG, "" ); + + if ( S_ITEM ) { + if ( carDlgUpdateItemPtr == NULL ) { + sprintf( message, _("New %s Scale Car"), GetScaleName( carDlgScaleInx ) ); + wButtonSetLabel( carDlgPG.okB, _("Add") ); + } else { + sprintf( message, _("Update %s Scale Car"), GetScaleName( carDlgScaleInx ) ); + wButtonSetLabel( carDlgPG.okB, _("Update") ); + } + wWinSetTitle( carDlgPG.win, message ); + } else if ( S_PART ) { + if ( carDlgUpdatePartPtr == NULL ) { + sprintf( message, _("New %s Scale Car Part"), GetScaleName( carDlgScaleInx ) ); + wButtonSetLabel( carDlgPG.okB, _("Add") ); + } else { + sprintf( message, _("Update %s Scale Car Part"), GetScaleName( carDlgScaleInx ) ); + wButtonSetLabel( carDlgPG.okB, _("Update") ); + } + wWinSetTitle( carDlgPG.win, message ); + } else if ( S_PROTO ) { + if ( carDlgUpdateProtoPtr == NULL ) { + wWinSetTitle( carDlgPG.win, _("New Prototype") ); + wButtonSetLabel( carDlgPG.okB, _("Add") ); + } else { + wWinSetTitle( carDlgPG.win, _("Update Prototype") ); + wButtonSetLabel( carDlgPG.okB, _("Update") ); + } + } + + ParamLoadControls( &carDlgPG ); + + ParamDialogOkActive( &carDlgPG, S_ITEM ); + CarDlgUpdate( &carDlgPG, -1, NULL ); +} + + + +static void CarDlgDoActions( + carDlgAction_e * actions ) +{ + carPart_p partP; + carPartParent_p parentP; + carProto_p protoP; + wIndex_t inx; + int offset; + DIST_T ratio; + tabString_t tabs[7]; + char * cp; + BOOL_T reload[sizeof carDlgPLs/sizeof carDlgPLs[0]]; +#define RELOAD_DIMS \ + reload[I_CD_CARLENGTH] = reload[I_CD_CARWIDTH] = reload[I_CD_CPLDLEN] = \ + reload[I_CD_TRKCENTER] = reload[I_CD_CPLRLEN] = TRUE +#define RELOAD_PARTDATA \ + RELOAD_DIMS; \ + reload[I_CD_PARTNO_STR] = reload[I_CD_DESC_STR] = \ + reload[I_CD_ROADNAME_LIST] = reload[I_CD_REPMARK] = \ + reload[I_CD_NUMBER] = reload[I_CD_BODYCOLOR] = TRUE +#define RELOAD_LISTS \ + reload[I_CD_MANUF_LIST] = \ + reload[I_CD_PROTOKIND_LIST] = \ + reload[I_CD_PROTOTYPE_LIST] = \ + reload[I_CD_PARTNO_LIST] = TRUE + + memset( reload, 0, sizeof reload ); + while ( 1 ) { +LOG( log_carDlgState, 2, ( "Action = %s\n", carDlgAction_s[*actions] ) ) + switch ( *actions++ ) { + case A_Return: + for ( inx=0; inx<sizeof carDlgPLs/sizeof carDlgPLs[0]; inx++ ) + if ( reload[inx] ) + ParamLoadControl( &carDlgPG, inx ); + return; + case A_SError: + currState = S_Error; + break; + case A_Else: + offset = (int)*actions++; + actions += offset; + break; + case A_SItemSel: + currState = S_ItemSel; + break; + case A_SItemEnter: + currState = S_ItemEnter; + break; + case A_SPartnoSel: + currState = S_PartnoSel; + break; + case A_SPartnoEnter: + currState = S_PartnoEnter; + break; + case A_SProtoSel: + currState = S_ProtoSel; + break; + case A_IsCustom: + offset = (int)*actions++; + if ( currState != S_ItemEnter ) + actions += offset; + break; + case A_IsNewPart: + offset = (int)*actions++; + if (carDlgNewPartPtr==NULL) { + actions += offset; + } else { + TabStringExtract( carDlgNewPartPtr->title, 7, tabs ); + TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); + TabStringCpy( carDlgDescStr, &tabs[T_DESC] ); + reload[I_CD_PARTNO_STR] = reload[I_CD_DESC_STR] = TRUE; + } + break; + case A_IsNewProto: + offset = (int)*actions++; + if (carDlgNewProtoPtr==NULL) { + actions += offset; + } else { + strcpy( carDlgProtoStr, carDlgNewProtoPtr->desc ); + } + break; + case A_LoadDataFromPartList: + partP = (carPart_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PARTNO_LIST].control, carDlgPartnoInx ); + if ( partP != NULL ){ + CarDlgLoadPart(partP); + RELOAD_PARTDATA; + RELOAD_PARTDATA; + } + break; + case A_LoadDimsFromStack: + carDlgDim = carDlgStk[carDlgStkPtr].dim; + carDlgCouplerLength = carDlgStk[carDlgStkPtr].couplerLength; + carDlgTypeInx = carDlgStk[carDlgStkPtr].typeInx; + carDlgIsLoco = (typeListMap[carDlgTypeInx].value&1) != 0; + RELOAD_DIMS; + break; + case A_LoadManufListForScale: + CarDlgLoadManufList( FALSE, TRUE, carDlgScaleInx ); + reload[I_CD_MANUF_LIST] = TRUE; + break; + case A_LoadManufListAll: + CarDlgLoadManufList( TRUE, FALSE, carDlgScaleInx ); + reload[I_CD_MANUF_LIST] = TRUE; + break; + case A_LoadProtoListForManuf: + parentP = (carPartParent_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, carDlgManufInx ); + CarDlgLoadProtoList( parentP->manuf, parentP->scale, TRUE ); + reload[I_CD_PROTOKIND_LIST] = TRUE; + reload[I_CD_PROTOTYPE_LIST] = TRUE; + break; + case A_LoadProtoListAll: + CarDlgLoadProtoList( NULL, 0, TRUE ); + reload[I_CD_PROTOKIND_LIST] = TRUE; + reload[I_CD_PROTOTYPE_LIST] = TRUE; + break; + case A_LoadPartnoList: + parentP = (carPartParent_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); + CarDlgLoadPartList( parentP ); + reload[I_CD_PARTNO_LIST] = TRUE; + break; + case A_LoadLists: + if ( CarDlgLoadLists( TRUE, NULL, carDlgScaleInx ) ) + currState = S_ItemSel; + else + currState = S_ItemEnter; + break; + case A_LoadDimsFromProtoList: + protoP = (carProto_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); + if ( protoP ) { + CarDlgLoadDimsFromProto( protoP ); + carDlgTypeInx = CarProtoFindTypeCode( protoP->type ); + carDlgIsLoco = (protoP->options&CAR_DESC_IS_LOCO)!=0; + } else { + ratio = GetScaleRatio( carDlgScaleInx ); + carDlgDim.carLength = 50*12/ratio; + carDlgDim.carWidth = 10*12/ratio; + carDlgDim.coupledLength = carDlgDim.carLength+carDlgCouplerLength*2; + carDlgDim.truckCenter = carDlgDim.carLength-59.0*2.0/ratio; + carDlgTypeInx = 0; + carDlgIsLoco = (typeListMap[0].value&1); + } + RELOAD_DIMS; + reload[I_CD_TYPE_LIST] = reload[I_CD_ISLOCO] = TRUE; + break; + case A_ConvertDimsToProto: + ratio = GetScaleRatio( carDlgScaleInx ); + carDlgDim.carLength *= ratio; + carDlgDim.carWidth *= ratio; + carDlgCouplerLength = 16.0; + carDlgDim.coupledLength = carDlgDim.carLength + 2 * carDlgCouplerLength; + carDlgDim.truckCenter *= ratio; + RELOAD_DIMS; + break; + case A_Redraw: + CarDlgRedraw(); + break; + case A_ClrManuf: + carDlgManufStr[0] = '\0'; + wListSetValue( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, "" ); + carDlgManufInx = -1; + break; + case A_ClrPartnoStr: + carDlgPartnoStr[0] = '\0'; + carDlgDescStr[0] = '\0'; + reload[I_CD_PARTNO_STR] = reload[I_CD_DESC_STR] = TRUE; + break; + case A_ClrNumberStr: + carDlgNumberStr[0] = '\0'; + reload[I_CD_NUMBER] = TRUE; + break; + case A_LoadProtoStrFromList: + wListGetValues( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoStr, sizeof carDlgProtoStr, NULL, NULL ); +#ifdef LATER + protoP = (carProto_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); + if ( protoP ) { + carDlgTypeInx = CarProtoFindTypeCode( protoP->type ); + carDlgIsLoco = (protoP->options&CAR_DESC_IS_LOCO)!=0; + } +#endif + break; + case A_ShowPartnoList: + reload[I_CD_PARTNO_LIST] = TRUE; + ParamControlShow( &carDlgPG, I_CD_PARTNO_LIST, TRUE ); + ParamControlShow( &carDlgPG, I_CD_DESC_STR, FALSE ); + ParamControlShow( &carDlgPG, I_CD_PARTNO_STR, FALSE ); + break; + case A_HidePartnoList: + reload[I_CD_PARTNO_STR] = reload[I_CD_DESC_STR] = TRUE; + ParamControlShow( &carDlgPG, I_CD_PARTNO_LIST, FALSE ); + ParamControlShow( &carDlgPG, I_CD_DESC_STR, TRUE ); + ParamControlShow( &carDlgPG, I_CD_PARTNO_STR, TRUE ); + break; + case A_PushDims: + if ( carDlgStkPtr >= CARDLG_STK_SIZE ) + AbortProg( "carDlgNewDesc: CARDLG_STK_SIZE" ); + carDlgStk[carDlgStkPtr].dim = carDlgDim; + carDlgStk[carDlgStkPtr].couplerLength = carDlgCouplerLength; + carDlgStk[carDlgStkPtr].state = currState; + carDlgStk[carDlgStkPtr].changed = carDlgChanged; + carDlgStk[carDlgStkPtr].typeInx = carDlgTypeInx; + if ( currState == S_ItemSel && carDlgPartnoInx >= 0 ) + carDlgStk[carDlgStkPtr].partP = (carPart_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PARTNO_LIST].control, carDlgPartnoInx ); + else + carDlgStk[carDlgStkPtr].partP = NULL; + carDlgStkPtr++; + break; + case A_PopDims: + break; + case A_PopTitleAndTypeinx: + if ( carDlgStk[carDlgStkPtr].partP ) { + TabStringExtract( carDlgStk[carDlgStkPtr].partP->title, 7, tabs ); + strcpy( carDlgManufStr, carDlgStk[carDlgStkPtr].partP->parent->manuf ); + strcpy( carDlgProtoStr, carDlgStk[carDlgStkPtr].partP->parent->proto ); + TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); + TabStringCpy( carDlgDescStr, &tabs[T_DESC] ); + } + carDlgTypeInx = carDlgStk[carDlgStkPtr].typeInx; + break; + case A_PopCouplerLength: + carDlgCouplerLength = carDlgStk[carDlgStkPtr].couplerLength; + break; + case A_ShowControls: + CarDlgShowControls(); + break; + case A_LoadInfoFromUpdateItem: + carDlgScaleInx = carDlgUpdateItemPtr->scaleInx; + carDlgItemIndex = carDlgUpdateItemPtr->index; + TabStringExtract( carDlgUpdateItemPtr->title, 7, tabs ); + TabStringCpy( carDlgManufStr, &tabs[T_MANUF] ); + TabStringCpy( carDlgProtoStr, &tabs[T_PROTO] ); + TabStringCpy( carDlgRoadnameStr, &tabs[T_ROADNAME] ); + TabStringCpy( carDlgRepmarkStr, &tabs[T_REPMARK] ); + TabStringCpy( carDlgNumberStr, &tabs[T_NUMBER] ); + carDlgDim = carDlgUpdateItemPtr->dim; + carDlgBodyColor = carDlgUpdateItemPtr->color; + carDlgTypeInx = CarProtoFindTypeCode( carDlgUpdateItemPtr->type ); + carDlgIsLoco = (carDlgUpdateItemPtr->type&1)!=0; + carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; + sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); + wPrefSetFloat( carDlgPG.nameStr, message, carDlgCouplerLength ); + carDlgCouplerMount = (carDlgUpdateItemPtr->options&CAR_DESC_COUPLER_MODE_BODY)!=0; + carDlgIsLoco = (carDlgUpdateItemPtr->options&CAR_DESC_IS_LOCO)!=0; + carDlgPurchPrice = carDlgUpdateItemPtr->data.purchPrice; + sprintf( carDlgPurchPriceStr, "%0.2f", carDlgPurchPrice ); + carDlgCurrPrice = carDlgUpdateItemPtr->data.currPrice; + sprintf( carDlgCurrPriceStr, "%0.2f", carDlgCurrPrice ); + carDlgCondition = carDlgUpdateItemPtr->data.condition; + carDlgConditionInx = MapCondition( carDlgUpdateItemPtr->data.condition ); + carDlgPurchDate = carDlgUpdateItemPtr->data.purchDate; + if ( carDlgPurchDate ) + sprintf( carDlgPurchDateStr, "%ld", carDlgPurchDate ); + else + carDlgPurchDateStr[0] = '\0'; + carDlgServiceDate = carDlgUpdateItemPtr->data.serviceDate; + if ( carDlgServiceDate ) + sprintf( carDlgServiceDateStr, "%ld", carDlgServiceDate ); + else + carDlgServiceDateStr[0] = '\0'; + wTextClear( (wText_p)carDlgPLs[I_CD_NOTES].control ); + if ( carDlgUpdateItemPtr->data.notes ) { + strncpy( message, carDlgUpdateItemPtr->data.notes, sizeof message ); + message[sizeof message - 1] = '\0'; + for ( cp=message; *cp; cp++ ) + if ( *cp == '\n' ) *cp = ' '; + wTextAppend( (wText_p)carDlgPLs[I_CD_NOTES].control, message ); + } + LoadRoadnameList( &tabs[T_ROADNAME], &tabs[T_REPMARK] ); + CarDlgLoadRoadnameList(); + carDlgRoadnameInx = lookupListIndex+1; + memset( reload, 1, sizeof reload ); + + if ( CarDlgLoadLists( TRUE, tabs, carDlgScaleInx ) ) + currState = S_ItemSel; + else + currState = S_ItemEnter; + break; + case A_LoadDataFromUpdatePart: + carDlgScaleInx = carDlgUpdatePartPtr->parent->scale; + TabStringExtract( carDlgUpdatePartPtr->title, 7, tabs ); + tabs[T_MANUF].ptr = carDlgUpdatePartPtr->parent->manuf; + tabs[T_MANUF].len = strlen(carDlgUpdatePartPtr->parent->manuf); + tabs[T_PROTO].ptr = carDlgUpdatePartPtr->parent->proto; + tabs[T_PROTO].len = strlen(carDlgUpdatePartPtr->parent->proto); + CarDlgLoadLists( FALSE, tabs, carDlgScaleInx ); + CarDlgLoadPart( carDlgUpdatePartPtr ); + RELOAD_LISTS; + RELOAD_DIMS; + RELOAD_PARTDATA; + break; + case A_InitProto: + if ( carDlgUpdateProtoPtr==NULL ) { + carDlgProtoStr[0] = 0; + carDlgDim.carLength = 50*12; + carDlgDim.carWidth = 10*12; + carDlgDim.coupledLength = carDlgDim.carLength+16.0*2.0; + carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; + carDlgDim.truckCenter = carDlgDim.carLength-59.0*2.0; + carDlgIsLoco = (typeListMap[carDlgTypeInx].value&1); + } else { + strcpy( carDlgProtoStr , carDlgUpdateProtoPtr->desc ); + carDlgDim = carDlgUpdateProtoPtr->dim; + carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; + carDlgIsLoco = (carDlgUpdateProtoPtr->options&CAR_DESC_IS_LOCO)!=0; + carDlgTypeInx = CarProtoFindTypeCode( carDlgUpdateProtoPtr->type ); + carProtoSegCnt = carDlgUpdateProtoPtr->segCnt; + carProtoSegPtr = carDlgUpdateProtoPtr->segPtr; + currState = S_ProtoSel; + } + RELOAD_DIMS; + break; + case A_RecallCouplerLength: + sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); + carDlgCouplerLength = 16.0/GetScaleRatio(carDlgScaleInx); + wPrefGetFloat( carDlgPG.nameStr, message, &carDlgCouplerLength, carDlgCouplerLength ); + break; + default: + AbortProg( "carDlgDoActions: bad action" ); + break; + } + } +} + + +static void CarDlgDoStateActions( + carDlgAction_e * actions ) +{ + CarDlgDoActions( actions ); +LOG( log_carDlgState, 1, ( " ==> S_%s\n", carDlgState_s[currState] ) ) +} + +static void CarDlgStateMachine( + carDlgTransistion_e transistion ) +{ +LOG( log_carDlgState, 1, ( "S_%s[T_%s]\n", carDlgState_s[currState], carDlgTransistion_s[transistion] ) ) + CarDlgDoStateActions( stateMachine[currState][transistion] ); +} + + +static BOOL_T CheckCarDlgItemIndex( long * index ) +{ + BOOL_T found = TRUE; + BOOL_T updated = FALSE; + + int inx; + carItem_p item; + while ( found ) { + found = FALSE; + for ( inx=0; inx<carItemInfo_da.cnt; inx++ ) { + item = carItemInfo(inx); + if ( item->index == *index ) { + (*index)++; + found = TRUE; + updated = TRUE; + break; + } + } + } + return !updated; +} + + +static void CarDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + BOOL_T redraw = FALSE; + roadnameMap_p roadnameMapP; + char * cp, *cq; + long valL, d, m; + FLOAT_T ratio; + BOOL_T ok; + DIST_T len; + BOOL_T checkTruckCenter = FALSE; + cmp_key_t cmp_key; + coOrd orig, size, size2; + carPartParent_p parentP; + static DIST_T carDlgTruckOffset; + static long carDlgClock; + static long carDlgCarLengthClock; + static long carDlgTruckCenterClock; + static long carDlgCoupledLengthClock; + static long carDlgCouplerLengthClock; + + ratio = (S_PROTO?1.0:GetScaleRatio(carDlgScaleInx)); + +LOG( log_carDlgState, 3, ( "CarDlgUpdate( %d )\n", inx ) ) + + switch ( inx ) { + + case -1: + if ( carDlgDim.truckCenter > 0 && carDlgDim.carLength > carDlgDim.truckCenter ) + carDlgTruckOffset = carDlgDim.carLength - carDlgDim.truckCenter; + else + carDlgTruckOffset = 0; + carDlgCarLengthClock = carDlgCoupledLengthClock = carDlgTruckCenterClock = carDlgCouplerLengthClock = carDlgClock = 0; + redraw = TRUE; + break; + + case I_CD_MANUF_LIST: + carDlgChanged++; + wListGetValues( (wList_p)pg->paramPtr[inx].control, carDlgManufStr, sizeof carDlgManufStr, NULL, NULL ); + if ( carDlgManufInx < 0 || + wListGetItemContext( (wList_p)pg->paramPtr[inx].control, carDlgManufInx ) == NULL ) + CarDlgStateMachine( T_ItemEnter ); +#ifdef LATER + else if ( strcasecmp( carDlgManufStr, "unknown" ) == 0 || + strcasecmp( carDlgManufStr, "custom" ) == 0 ) + CarDlgStateMachine( T_ItemEnter ); +#endif + else + CarDlgStateMachine( T_ItemSel ); + /*ParamControlShow( &carDlgPG, I_CD_MANUF_LIST, TRUE );*/ + break; + + case I_CD_PROTOKIND_LIST: + carDlgChanged++; + carDlgTypeInx = (int)(long)wListGetItemContext( (wList_p)pg->paramPtr[inx].control, carDlgKindInx ); + if ( S_PART || (currState==S_ItemEnter) ) { + CarDlgLoadProtoList( NULL, 0, FALSE ); + } else { + parentP = NULL; + if ( carDlgProtoInx >= 0 ) + parentP = (carPartParent_p)wListGetItemContext( (wList_p)pg->paramPtr[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); + CarDlgLoadProtoList( carDlgManufStr, (parentP?parentP->scale:0), FALSE ); + } + CarDlgStateMachine( T_ProtoSel ); + break; + + case I_CD_PROTOTYPE_LIST: + carDlgChanged++; + wListGetValues( (wList_p)pg->paramPtr[inx].control, carDlgProtoStr, sizeof carDlgProtoStr, NULL, NULL ); + CarDlgStateMachine( T_ProtoSel ); + break; + + case I_CD_PARTNO_LIST: + carDlgChanged++; + wListGetValues( (wList_p)pg->paramPtr[inx].control, carDlgPartnoStr, sizeof carDlgPartnoStr, NULL, NULL ); + if ( carDlgPartnoInx >= 0 ) { + CarDlgStateMachine( T_PartnoSel ); + } else { + CarDlgStateMachine( T_PartnoEnter ); + wControlSetFocus( pg->paramPtr[I_CD_PARTNO_STR].control ); + } + break; + + case I_CD_DISPMODE: + for ( inx=B; inx<C; inx++ ) + ParamControlShow( &carDlgPG, inx, carDlgDispMode==1 ); + for ( inx=C; inx<D; inx++ ) + ParamControlShow( &carDlgPG, inx, carDlgDispMode==0 ); + if ( carDlgDispMode == 0 && carDlgUpdateItemPtr != NULL ) { + ParamControlShow( &carDlgPG, I_CD_QTY, FALSE ); + ParamControlShow( &carDlgPG, I_CD_MLTNUM, FALSE ); + } + redraw = carDlgDispMode==1; + break; + + case I_CD_ROADNAME_LIST: + carDlgChanged++; + roadnameMapP = NULL; + if ( *(long*)valueP == 0 ) { + roadnameMapP = NULL; + carDlgRoadnameStr[0] = '\0'; + } else if ( *(long*)valueP > 0 ) { + roadnameMapP = (roadnameMap_p)wListGetItemContext( (wList_p)pg->paramPtr[I_CD_ROADNAME_LIST].control, (wIndex_t)*(long*)valueP ); + strcpy( carDlgRoadnameStr, roadnameMapP->roadname ); + } else { + wListGetValues( (wList_p)pg->paramPtr[I_CD_ROADNAME_LIST].control, carDlgRoadnameStr, sizeof carDlgRoadnameStr, NULL, NULL ); + cmp_key.name = carDlgRoadnameStr; + cmp_key.len = strlen(carDlgRoadnameStr); + roadnameMapP = LookupListElem( &roadnameMap_da, &cmp_key, Cmp_roadnameMap, 0 ); + } + if ( roadnameMapP ) { + strcpy( carDlgRepmarkStr, roadnameMapP->repmark ); + } else { + carDlgRepmarkStr[0] = '\0'; + } + ParamLoadControl( pg, I_CD_REPMARK ); + break; + + case I_CD_CARLENGTH: + carDlgChanged++; + if ( carDlgDim.carLength == 0.0 ) { + carDlgCarLengthClock = 0; + } else if ( carDlgDim.carLength < 100/ratio ) { + return; + } else if ( carDlgCouplerLength != 0 && ( carDlgDim.coupledLength == 0 || carDlgCouplerLengthClock >= carDlgCoupledLengthClock ) ) { + len = carDlgDim.carLength+carDlgCouplerLength*2.0; + if ( len > 0 ) { + carDlgDim.coupledLength = len; + ParamLoadControl( &carDlgPG, I_CD_CPLDLEN ); + } + carDlgCarLengthClock = ++carDlgClock; + } else if ( carDlgDim.coupledLength != 0 && ( carDlgCouplerLength == 0 || carDlgCoupledLengthClock > carDlgCouplerLengthClock ) ) { + len = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; + if ( len > 0 ) { + carDlgCouplerLength = len; + ParamLoadControl( &carDlgPG, I_CD_CPLRLEN ); + if ( !S_PROTO ) { + sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); + wPrefSetFloat( carDlgPG.nameStr, message, carDlgCouplerLength ); + } + } + carDlgCarLengthClock = ++carDlgClock; + } + checkTruckCenter = TRUE; + redraw = TRUE; + break; + + case I_CD_CPLDLEN: + carDlgChanged++; + if ( carDlgDim.coupledLength == 0 ) { + carDlgCoupledLengthClock = 0; + } else if ( carDlgDim.coupledLength < 100/ratio ) { + return; + } else if ( carDlgDim.carLength != 0 && ( carDlgCouplerLength == 0 || carDlgCarLengthClock > carDlgCouplerLengthClock ) ) { + len = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; + if ( len > 0 ) { + carDlgCouplerLength = len; + ParamLoadControl( &carDlgPG, I_CD_CPLRLEN ); + if ( !S_PROTO ) { + sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); + wPrefSetFloat( carDlgPG.nameStr, message, carDlgCouplerLength ); + } + } + carDlgCoupledLengthClock = ++carDlgClock; + } else if ( carDlgCouplerLength != 0 && ( carDlgDim.carLength == 0 || carDlgCouplerLengthClock >= carDlgCarLengthClock ) ) { + len = carDlgDim.coupledLength-carDlgCouplerLength*2.0; + if ( len > 0 ) { + carDlgDim.carLength = len; + ParamLoadControl( &carDlgPG, I_CD_CARLENGTH ); + checkTruckCenter = TRUE; + } + carDlgCoupledLengthClock = ++carDlgClock; + } + redraw = TRUE; + break; + + case I_CD_CPLRLEN: + carDlgChanged++; + if ( carDlgCouplerLength == 0 ) { + carDlgCouplerLengthClock = 0; + redraw = TRUE; + break; + } else if ( carDlgCouplerLength < 1/ratio ) { + return; + } else if ( carDlgDim.carLength != 0 && ( carDlgDim.coupledLength == 0 || carDlgCarLengthClock >= carDlgCoupledLengthClock ) ) { + len = carDlgDim.carLength+carDlgCouplerLength*2.0; + if ( len > 0 ) { + carDlgDim.coupledLength = carDlgDim.carLength+carDlgCouplerLength*2.0; + ParamLoadControl( &carDlgPG, I_CD_CPLDLEN ); + } + carDlgCouplerLengthClock = ++carDlgClock; + } else if ( carDlgDim.coupledLength != 0 && ( carDlgDim.carLength == 0 || carDlgCoupledLengthClock > carDlgCarLengthClock ) ) { + len = carDlgCouplerLength-carDlgDim.coupledLength*2.0; + if ( len > 0 ) { + carDlgDim.carLength = carDlgCouplerLength-carDlgDim.coupledLength*2.0; + ParamLoadControl( &carDlgPG, I_CD_CARLENGTH ); + checkTruckCenter = TRUE; + } + carDlgCouplerLengthClock = ++carDlgClock; + } + if ( !S_PROTO ) { + sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); + wPrefSetFloat( carDlgPG.nameStr, message, carDlgCouplerLength ); + } + redraw = TRUE; + break; + + case I_CD_CARWIDTH: + carDlgChanged++; + if ( carDlgDim.carLength < 30/ratio ) return; + redraw = TRUE; + break; + + case I_CD_BODYCOLOR: + carDlgChanged++; + RecolorSegs( carDlgSegs_da.cnt, &carDlgSegs(0), carDlgBodyColor ); + redraw = TRUE; + break; + + case I_CD_ISLOCO: + carDlgChanged++; + redraw = TRUE; + break; + + case I_CD_TRKCENTER: + carDlgChanged++; + if ( carDlgDim.truckCenter == 0 ) { + carDlgTruckOffset = 0; + } else if ( carDlgDim.truckCenter < 100/ratio /*&& carDlgDim.carLength == 0.0*/ ) { + return; + } else if ( carDlgDim.carLength > carDlgDim.truckCenter ) { + carDlgTruckOffset = carDlgDim.carLength - carDlgDim.truckCenter; + } else { + carDlgTruckOffset = 0; + } + redraw = TRUE; + break; + + case I_CD_QTY: + wControlActive( carDlgPLs[I_CD_MLTNUM].control, carDlgQuantity>1 ); + break; + + case I_CD_PURPRC: + case I_CD_CURPRC: + carDlgChanged++; + *(FLOAT_T*)(pg->paramPtr[inx].context) = strtod( (char*)pg->paramPtr[inx].valueP, &cp ); + if ( cp==NULL || *cp!='\0' ) + *(FLOAT_T*)(pg->paramPtr[inx].context) = -1; + break; + + case I_CD_COND: + carDlgChanged++; + carDlgCondition = + (carDlgConditionInx==0)?0: + (carDlgConditionInx==1)?100: + (carDlgConditionInx==2)?80: + (carDlgConditionInx==3)?60: + (carDlgConditionInx==4)?40:20; + break; + + case I_CD_PURDAT: + case I_CD_SRVDAT: + carDlgChanged++; + cp = (char*)pg->paramPtr[inx].valueP; + if ( *cp ) { + valL = strtol( cp, &cq, 10 ); + if ( cq==NULL || *cq!='\0' ) { + cp = N_("Enter a 8 digit numeric date"); + } else if ( valL != 0 ) { + if ( strlen(cp) != 8 ) { + cp = N_("Enter a 8 digit date"); + } else if ( valL < 19000101 || valL > 21991231 ) { + cp = N_("Enter a date between 19000101 and 21991231"); + } else { + d = valL % 100; + m = (valL / 100) % 100; + if ( m < 1 || m > 12 ) { + cp = N_("Invalid month"); + } else if ( d < 1 || d > 31 ) { + cp = N_("Invalid day"); + } else { + cp = NULL; + } + } + } + if ( cp ) { + valL = 0; + } + } else { + cp = NULL; + valL = 0; + } + wControlSetBalloon( pg->paramPtr[inx].control, 0, -5, _(cp) ); + *(long*)(pg->paramPtr[inx].context) = valL; + break; + + case I_CD_TYPE_LIST: + carDlgChanged++; + carDlgIsLoco = (typeListMap[carDlgTypeInx].value&1); + ParamLoadControl( &carDlgPG, I_CD_ISLOCO ); + redraw = TRUE; + break; + + case I_CD_IMPORT: + carDlgChanged++; + WriteSelectedTracksToTempSegs(); + carProtoSegCnt = tempSegs_da.cnt; + carProtoSegPtr = (trkSeg_t*)tempSegs_da.ptr; + CloneFilledDraw( carProtoSegCnt, carProtoSegPtr, TRUE ); + GetSegBounds( zero, 0.0, carProtoSegCnt, carProtoSegPtr, &orig, &size ); + if ( size.x <= 0.0 || + size.y <= 0.0 || + size.x < size.y ) { + NoticeMessage( MSG_CARPROTO_BADSEGS, _("Ok"), NULL ); + return; + } + orig.x = -orig.x; + orig.y = -orig.y; + MoveSegs( carProtoSegCnt, carProtoSegPtr, orig ); + size2.x = floor(size.x*curScaleRatio+0.5); + size2.y = floor(size.y*curScaleRatio+0.5); + RescaleSegs( carProtoSegCnt, carProtoSegPtr, size2.x/size.x, size2.y/size.y, curScaleRatio ); + carDlgDim.carLength = size2.x; + carDlgDim.carWidth = size2.y; + carDlgDim.coupledLength = carDlgDim.carLength + 32; + if ( carDlgDim.carLength > 120 ) { + carDlgDim.truckCenter = carDlgDim.carLength - 120; + carDlgTruckOffset = carDlgDim.carLength - carDlgDim.truckCenter; + } else { + carDlgDim.truckCenter = 0; + carDlgTruckOffset = 0; + } + carDlgFlipToggle = FALSE; + ParamLoadControl( &carDlgPG, I_CD_CARLENGTH ); + ParamLoadControl( &carDlgPG, I_CD_CARWIDTH ); + ParamLoadControl( &carDlgPG, I_CD_CPLRLEN ); + ParamLoadControl( &carDlgPG, I_CD_TRKCENTER ); + redraw = TRUE; + break; + + case I_CD_RESET: + carDlgChanged++; + carProtoSegCnt = 0; + redraw = TRUE; + break; + + case I_CD_FLIP: + carDlgChanged++; + carDlgFlipToggle = ! carDlgFlipToggle; + redraw = TRUE; + break; + + } + + if ( checkTruckCenter && carDlgDim.carLength > 0 ) { + if ( carDlgTruckOffset > 0 ) { + carDlgDim.truckCenter = carDlgDim.carLength - carDlgTruckOffset; + } else { + carDlgDim.truckCenter = carDlgDim.carLength * 0.75; + } + ParamLoadControl( &carDlgPG, I_CD_TRKCENTER ); + } + + ok = FALSE; + if ( S_PROTO && carDlgProtoStr[0] == '\0' ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Enter a Prototype name") ); + else if ( S_PART && carDlgManufStr[0] == '\0' ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Select or Enter a Manufacturer") ); + else if ( S_PART && carDlgPartnoStr[0] == '\0' ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Enter a Part Number") ); + else if ( carDlgDim.carLength <= 0 ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Enter the Car Length") ); + else if ( carDlgDim.carWidth <= 0 ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Enter the Car Width") ); + else if ( carDlgDim.truckCenter <= 0 ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Enter the Truck Centers") ); + else if ( carDlgDim.truckCenter >= carDlgDim.carLength ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Truck Centers must be less than Car Length") ); + else if ( (!S_PROTO) && ( carDlgDim.coupledLength <= 0 || carDlgCouplerLength <= 0 ) ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Enter the Coupled Length or Coupler Length") ); + else if ( S_PROTO && carDlgDim.coupledLength <= 0 ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Enter the Coupled Length") ); + else if ( S_ITEM && carDlgItemIndex <= 0 ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Enter a item Index") ); + else if ( S_ITEM && carDlgPurchPrice < 0 ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Purchase Price is not valid") ); + else if ( S_ITEM && carDlgCurrPrice < 0 ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Current Price is not valid") ); + else if ( S_ITEM && carDlgPurchDate < 0 ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Purchase Date is not valid") ); + else if ( S_ITEM && carDlgServiceDate < 0 ) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Service Date is not valid") ); + else if ( S_ITEM && carDlgUpdateItemPtr==NULL && + ( valL = carDlgItemIndex , !CheckCarDlgItemIndex(&carDlgItemIndex) ) ) { + sprintf( message, _("Item Index %ld duplicated an existing item: updated to new value"), valL ); + ParamLoadControl( &carDlgPG, I_CD_ITEMINDEX ); + ParamLoadMessage( &carDlgPG, I_CD_MSG, message ); + ok = TRUE; + } else { + ParamLoadMessage( pg, I_CD_MSG, "" ); + ok = TRUE; + } + + if ( redraw ) + CarDlgRedraw(); + + ParamDialogOkActive( pg, ok ); +} + + + +static void CarDlgNewDesc( void ) +{ + carDlgNewPartPtr = NULL; + carDlgNewProtoPtr = NULL; + carDlgUpdatePartPtr = NULL; + carDlgNumberStr[0] = '\0'; + ParamLoadControl( &carDlgPG, I_CD_NUMBER ); + CarDlgDoStateActions( item2partActions ); + carDlgChanged = 0; +} + + +static void CarDlgNewProto( void ) +{ + carProto_p protoP = CarProtoFind( carDlgProtoStr ); + if ( protoP != NULL ) { + carProtoSegCnt = protoP->segCnt;; + carProtoSegPtr = protoP->segPtr;; + } else { + carProtoSegCnt = 0; + carProtoSegPtr = NULL; + } + carDlgUpdateProtoPtr = NULL; + carDlgNewProtoPtr = NULL; + if ( S_ITEM ) + CarDlgDoStateActions( item2protoActions ); + else + CarDlgDoStateActions( part2protoActions ); + carDlgChanged = 0; +} + + +static void CarDlgClose( wWin_p win ) +{ + carDlgState_e oldState; + + if ( carDlgChanged ) { + if ( !inPlayback ) { + if ( NoticeMessage( MSG_CARDESC_CHANGED, _("Yes"), _("No") ) <= 0 ) + return; + } else { + PlaybackMessage( "Car Desc Changed\n" ); + } + } + if ( carDlgStkPtr > 0 ) { + carDlgStkPtr--; + oldState = currState; + currState = carDlgStk[carDlgStkPtr].state; + carDlgChanged = carDlgStk[carDlgStkPtr].changed; + if ( oldState == S_ProtoSel ) + if ( S_PART ) + CarDlgDoStateActions( proto2partActions ); + else + CarDlgDoStateActions( proto2itemActions ); + else + CarDlgDoStateActions( part2itemActions ); + } else { + wTextClear( (wText_p)carDlgPLs[I_CD_NOTES].control ); + wHide( carDlgPG.win ); + } +} + + +static void CarDlgOk( void * junk ) +{ + long options = 0; + int len; + FILE * f; + long number; + char * cp; + long count; + tabString_t tabs[7]; + char title[STR_LONG_SIZE]; + carItem_p itemP=NULL; + carPart_p partP=NULL; + carProto_p protoP; + BOOL_T reloadRoadnameList = FALSE; + char *oldLocale = NULL; + +LOG( log_carDlgState, 3, ( "CarDlgOk()\n" ) ) + + /*ParamUpdate( &carDlgPG );*/ + if ( carDlgDim.carLength <= 0.0 || + carDlgDim.carWidth <= 0.0 || + carDlgDim.truckCenter <= 0.0 || + carDlgDim.coupledLength <= 0.0 ) { + NoticeMessage( MSG_CARDESC_VALUE_ZERO, _("Ok"), NULL ); + return; + } + if ( carDlgDim.carLength <= carDlgDim.carWidth ) { + NoticeMessage( MSG_CARDESC_BAD_DIM_VALUE, _("Ok"), NULL ); + return; + } + if ( carDlgDim.coupledLength <= carDlgDim.carLength ) { + NoticeMessage( MSG_CARDESC_BAD_COUPLER_LENGTH_VALUE, _("Ok"), NULL ); + return; + } + + if ( S_ITEM && carDlgUpdateItemPtr==NULL && !CheckCarDlgItemIndex(&carDlgItemIndex) ) { + NoticeMessage( MSG_CARITEM_BAD_INDEX, _("Ok"), NULL ); + ParamLoadControl( &carDlgPG, I_CD_ITEMINDEX ); + return; + } + + if ( (!S_PROTO) && carDlgCouplerMount != 0 ) + options |= CAR_DESC_COUPLER_MODE_BODY; + if ( carDlgIsLoco == 1 ) + options |= CAR_DESC_IS_LOCO; + + if ( S_ITEM ) { + len = wTextGetSize( (wText_p)carDlgPLs[I_CD_NOTES].control ); + sprintf( title, "%s\t%s\t%s\t%s\t%s\t%s\t%s", carDlgManufStr, carDlgProtoStr, carDlgDescStr, carDlgPartnoStr, carDlgRoadnameStr, carDlgRepmarkStr, carDlgNumberStr ); + partP = NULL; + if ( ( carDlgManufInx < 0 || carDlgPartnoInx < 0 ) && carDlgPartnoStr[0] ) { + partP = CarPartFind( carDlgManufStr, strlen(carDlgManufStr), carDlgPartnoStr, strlen(carDlgPartnoStr), carDlgScaleInx ); + if ( partP != NULL && + NoticeMessage( MSG_CARPART_DUPNAME, _("Yes"), _("No") ) <= 0 ) + return; + partP = CarPartNew( NULL, PARAM_CUSTOM, carDlgScaleInx, title, options, typeListMap[carDlgTypeInx].value, &carDlgDim, carDlgBodyColor ); + if ( partP != NULL ) { + if ( ( f = OpenCustom("a") ) ) { + oldLocale = SaveLocale("C"); + CarPartWrite( f, partP ); + fclose(f); + RestoreLocale(oldLocale); + } + } + } + if ( carDlgUpdateItemPtr!=NULL ) { + carDlgQuantity = 1; + } + for ( count=0; count<carDlgQuantity; count++ ) { + itemP = CarItemNew( carDlgUpdateItemPtr, + PARAM_CUSTOM, carDlgItemIndex, + carDlgScaleInx, title, options, typeListMap[carDlgTypeInx].value, + &carDlgDim, carDlgBodyColor, + carDlgPurchPrice, carDlgCurrPrice, carDlgCondition, + carDlgPurchDate, carDlgServiceDate ); + if ( carDlgUpdateItemPtr==NULL ) { + wPrefSetInteger( "misc", "last-car-item-index", carDlgItemIndex ); + carDlgItemIndex++; + CheckCarDlgItemIndex(&carDlgItemIndex); + ParamLoadControl( &carDlgPG, I_CD_ITEMINDEX ); + if ( carDlgQuantity>1 && carDlgMultiNum==0 ) { + number = strtol( carDlgNumberStr, &cp, 10 ); + if ( cp && *cp == 0 && number > 0 ) { + sprintf( carDlgNumberStr, "%ld", number+1 ); + sprintf( title, "%s\t%s\t%s\t%s\t%s\t%s\t%s", carDlgManufStr, carDlgProtoStr, carDlgDescStr, carDlgPartnoStr, carDlgRoadnameStr, carDlgRepmarkStr, carDlgNumberStr ); + } + } + } + if ( len > 0 ) { + if ( itemP->data.notes ) + itemP->data.notes = MyRealloc( itemP->data.notes, len+2 ); + else + itemP->data.notes = MyMalloc( len+2 ); + itemP->data.notes = (char*)MyMalloc( len+2 ); + wTextGetText( (wText_p)carDlgPLs[I_CD_NOTES].control, itemP->data.notes, len ); + if ( itemP->data.notes[len-1] != '\n' ) { + itemP->data.notes[len] = '\n'; + itemP->data.notes[len+1] = '\0'; + } else { + itemP->data.notes[len] = '\0'; + } + } else if ( itemP->data.notes ) { + MyFree( itemP->data.notes ); + itemP->data.notes = NULL; + } + } + if ( carDlgUpdateItemPtr==NULL ) + CarInvListAdd( itemP ); + else + CarInvListUpdate( itemP ); + changed++; + SetWindowTitle(); + reloadRoadnameList = TRUE; + if ( carDlgUpdateItemPtr==NULL ) { + if ( carDlgQuantity > 1 ) { + sprintf( message, _("Added %ld new Cars"), carDlgQuantity ); + } else { + strcpy( message, _("Added new Car") ); + } + } else { + strcpy( message, _("Updated Car") ); + } + sprintf( message+strlen(message), "%s: %s %s %s %s %s %s", + (partP?_(" and Part"):""), + carDlgManufStr, carDlgPartnoStr, carDlgProtoStr, carDlgDescStr, + (carDlgRepmarkStr?carDlgRepmarkStr:carDlgRoadnameStr), carDlgNumberStr ); + carDlgQuantity = 1; + ParamLoadControl( &carDlgPG, I_CD_QTY ); + + } else if ( S_PART ) { + if ( strcasecmp( carDlgRoadnameStr, "undecorated" ) == 0 ) { + carDlgRoadnameStr[0] = '\0'; + carDlgRepmarkStr[0] = '\0'; + } + if ( carDlgUpdatePartPtr==NULL ) { + partP = CarPartFind( carDlgManufStr, strlen(carDlgManufStr), carDlgPartnoStr, strlen(carDlgPartnoStr), carDlgScaleInx ); + if ( partP != NULL && + NoticeMessage( MSG_CARPART_DUPNAME, _("Yes"), _("No") ) <= 0 ) + return; + } + sprintf( message, "%s\t%s\t%s\t%s\t%s\t%s\t%s", carDlgManufStr, carDlgProtoStr, carDlgDescStr, carDlgPartnoStr, carDlgRoadnameStr, carDlgRepmarkStr, carDlgNumberStr ); + carDlgNewPartPtr = CarPartNew( carDlgUpdatePartPtr, PARAM_CUSTOM, carDlgScaleInx, message, options, typeListMap[carDlgTypeInx].value, + &carDlgDim, carDlgBodyColor ); + if ( carDlgNewPartPtr != NULL && ( f = OpenCustom("a") ) ) { + oldLocale = SaveLocale("C"); + CarPartWrite( f, carDlgNewPartPtr ); + fclose(f); + RestoreLocale(oldLocale); + } + reloadRoadnameList = TRUE; + sprintf( message, _("%s Part: %s %s %s %s %s %s"), carDlgUpdatePartPtr==NULL?_("Added new"):_("Updated"), carDlgManufStr, carDlgPartnoStr, carDlgProtoStr, carDlgDescStr, carDlgRepmarkStr?carDlgRepmarkStr:carDlgRoadnameStr, carDlgNumberStr ); + + } else if ( S_PROTO ) { + if ( carDlgUpdateProtoPtr==NULL ) { + protoP = CarProtoFind( carDlgProtoStr ); + if ( protoP != NULL && + NoticeMessage( MSG_CARPROTO_DUPNAME, _("Yes"), _("No") ) <= 0 ) + return; + } + carDlgNewProtoPtr = CarProtoNew( carDlgUpdateProtoPtr, PARAM_CUSTOM, carDlgProtoStr, options, typeListMap[carDlgTypeInx].value, &carDlgDim, carDlgSegs_da.cnt, &carDlgSegs(0) ); + if ( (f = OpenCustom("a") ) ) { + oldLocale = SaveLocale("C"); + CarProtoWrite( f, carDlgNewProtoPtr ); + fclose(f); + RestoreLocale(oldLocale); + } + sprintf( message, _("%s Prototype: %s%s."), + carDlgUpdateProtoPtr==NULL?_("Added new"):_("Updated"), carDlgProtoStr, + carDlgUpdateProtoPtr==NULL?_(". Enter new values or press Close"):"" ); + } + + if ( reloadRoadnameList ) { + tabs[0].ptr = carDlgRoadnameStr; + tabs[0].len = strlen(carDlgRoadnameStr); + tabs[1].ptr = carDlgRepmarkStr; + tabs[1].len = strlen(carDlgRepmarkStr); + LoadRoadnameList( &tabs[0], &tabs[1] ); + CarDlgLoadRoadnameList(); + ParamLoadControl( &carDlgPG, I_CD_ROADNAME_LIST ); + } + + ParamLoadMessage( &carDlgPG, I_CD_MSG, message ); + + DoChangeNotification( CHANGE_PARAMS ); + + carDlgChanged = 0; + if ( S_ITEM ) { + if ( carDlgUpdateItemPtr==NULL ) { + if ( partP ) { + TabStringExtract( title, 7, tabs ); + if ( CarDlgLoadLists( TRUE, tabs, curScaleInx ) ) + currState = S_ItemSel; + else + currState = S_ItemEnter; + ParamLoadControl( &carDlgPG, I_CD_MANUF_LIST ); + ParamLoadControl( &carDlgPG, I_CD_PROTOKIND_LIST ); + ParamLoadControl( &carDlgPG, I_CD_PROTOTYPE_LIST ); + ParamLoadControl( &carDlgPG, I_CD_PARTNO_LIST ); + ParamLoadControl( &carDlgPG, I_CD_PARTNO_STR ); + ParamLoadControl( &carDlgPG, I_CD_DESC_STR ); + ParamControlShow( &carDlgPG, I_CD_PARTNO_LIST, carDlgPartnoInx>=0 ); + ParamControlShow( &carDlgPG, I_CD_PARTNO_STR, carDlgPartnoInx<0 ); + ParamControlShow( &carDlgPG, I_CD_DESC_STR, carDlgPartnoInx<0 ); + } else if ( carDlgManufInx == -1 ) { + carDlgManufStr[0] = '\0'; + } + return; + } + } else if ( S_PART ) { + if ( carDlgUpdatePartPtr==NULL ) { + number = strtol( carDlgPartnoStr, &cp, 10 ); + if ( cp && *cp == 0 && number > 0 ) + sprintf( carDlgPartnoStr, "%ld", number+1 ); + else + carDlgPartnoStr[0] = '\0'; + carDlgNumberStr[0] = '\0'; + ParamLoadControl( &carDlgPG, I_CD_PARTNO_STR ); + ParamLoadControl( &carDlgPG, I_CD_NUMBER ); + return; + } + } else if ( S_PROTO ) { + if ( carDlgUpdateProtoPtr==NULL ) { + carDlgProtoStr[0] = '\0'; + ParamLoadControl( &carDlgPG, I_CD_PROTOTYPE_STR ); + return; + } + } + CarDlgClose( carDlgPG.win ); +} + + + +static void CarDlgLayout( + paramData_t * pd, + int inx, + wPos_t currX, + wPos_t *xx, + wPos_t *yy ) +{ + static wPos_t col2pos = 0; + wPos_t y0, y1; + + switch (inx) { + case I_CD_PROTOTYPE_STR: + case I_CD_PARTNO_STR: + case I_CD_ISLOCO: + case I_CD_IMPORT: + case I_CD_TYPE_LIST: + *yy = wControlGetPosY(carDlgPLs[inx-1].control); + break; + case I_CD_NEWPROTO: + *yy = wControlGetPosY(carDlgPLs[I_CD_NEW].control); + break; + case I_CD_CPLRMNT: + case I_CD_CPLRLEN: + case I_CD_CARWIDTH: + if ( col2pos == 0 ) + col2pos = wLabelWidth( _("Coupler Length") )+20; + *xx = wControlBeside(carDlgPLs[inx-1].control) + col2pos; + break; + case I_CD_DESC_STR: + *yy = wControlBelow(carDlgPLs[I_CD_PARTNO_STR].control) + 3; + break; + case I_CD_CPLDLEN: + *yy = wControlBelow(carDlgPLs[I_CD_TRKCENTER].control) + 3; + break; + case I_CD_CANVAS: + *yy = wControlBelow(carDlgPLs[I_CD_CPLDLEN].control)+5; + break; + case C: + *yy = wControlGetPosY(carDlgPLs[B].control); + break; + case I_CD_MSG: + y0 = wControlBelow(carDlgPLs[C-1].control); + y1 = wControlBelow(carDlgPLs[D-1].control); + *yy = ((y0>y1)?y0:y1) + 10; + break; + } +} + + +static void DoCarPartDlg( carDlgAction_e *actions ) +{ + paramData_t * pd; + int inx; + + if ( carDlgPG.win == NULL ) { + ParamCreateDialog( &carDlgPG, MakeWindowTitle(_("New Car Part")), _("Add"), CarDlgOk, CarDlgClose, TRUE, CarDlgLayout, F_BLOCK|PD_F_ALT_CANCELLABEL, CarDlgUpdate ); + + if ( carDlgDim.carWidth==0 ) + carDlgDim.carWidth = 12.0*10.0/curScaleRatio; + + for ( pd=carDlgPG.paramPtr; pd<&carDlgPG.paramPtr[carDlgPG.paramCnt]; pd++ ) { + if ( pd->type == PD_FLOAT && pd->valueP ) { + sprintf( message, "%s-%s", pd->nameStr, curScaleName ); + wPrefGetFloat( carDlgPG.nameStr, message, (FLOAT_T*)pd->valueP, *(FLOAT_T*)pd->valueP ); + } + } + roadnameMapChanged = TRUE; + + for ( inx=0; inx<N_CONDLISTMAP; inx++ ) + wListAddValue( (wList_p)carDlgPLs[I_CD_COND].control, _(condListMap[inx].name), NULL, (void*)condListMap[inx].value ); + + for ( inx=0; inx<N_TYPELISTMAP; inx++ ) + wListAddValue( (wList_p)carDlgPLs[I_CD_TYPE_LIST].control, _(typeListMap[inx].name), NULL, (void*)typeListMap[inx].value ); + + for ( inx=0; inx<N_TYPELISTMAP; inx++ ) + wListAddValue( (wList_p)carDlgPLs[I_CD_PROTOKIND_LIST].control, _(typeListMap[inx].name), NULL, (void*)typeListMap[inx].value ); + + wTextSetReadonly( (wText_p)carDlgPLs[I_CD_NOTES].control, FALSE ); + } + + wPrefGetInteger( "misc", "last-car-item-index", &carDlgItemIndex, 1 ); + CheckCarDlgItemIndex(&carDlgItemIndex); + CarDlgLoadRoadnameList(); + carProtoSegCnt = 0; + carProtoSegPtr = NULL; + carDlgScaleInx = curScaleInx; + carDlgFlipToggle = FALSE; + carDlgChanged = 0; + + CarDlgDoStateActions( actions ); + + /*CarDlgShowControls();*/ + +#ifdef LATER +if ( logTable(log_carList).level >= 1 ) { + int inx; + carPart_p partP; + for ( inx=0; inx<carPart_da.cnt; inx++ ) { + partP = carPart(inx); + LogPrintf( "%d %s %d\n", inx, partP->title, partP->paramFileIndex ); + } +} +#endif + wShow( carDlgPG.win ); +} + + +EXPORT void CarDlgAddProto( void ) +{ + /*carDlgPrototypeStr[0] = 0; */ + carDlgTypeInx = 0; + carDlgUpdateProtoPtr = NULL; + DoCarPartDlg( protoNewActions ); +} + +EXPORT void CarDlgAddDesc( void ) +{ + if ( carProto_da.cnt <= 0 ) { + NoticeMessage( MSG_NO_CARPROTO, _("Ok"), NULL ); + return; + } + carDlgIsLoco = FALSE; + carDlgUpdatePartPtr = NULL; + carDlgNumberStr[0] = '\0'; + ParamLoadControl( &carDlgPG, I_CD_NUMBER ); + DoCarPartDlg( partNewActions ); +} + +/* + * Car Inventory List + */ + +static wIndex_t carInvInx; + +static wIndex_t carInvSort[] = { 0, 1, 2, 3 }; +#define N_SORT (sizeof carInvSort/sizeof carInvSort[0]) + +static void CarInvDlgAdd( void ); +static void CarInvDlgEdit( void ); +static void CarInvDlgDelete( void ); +static void CarInvDlgImportCsv( void ); +static void CarInvDlgExportCsv( void ); +static void CarInvDlgSaveText( void ); +static void CarInvListLoad( void ); + +static wPos_t carInvColumnWidths[] = { + -40, 30, 100, -50, 50, 130, 120, 100, + -50, -50, 60, 55, 55, 40, 200 }; +static const char * carInvColumnTitles[] = { + N_("Index"), N_("Scale"), N_("Manufacturer"), N_("Part No"), N_("Type"), + N_("Description"), N_("Roadname"), N_("Rep Marks"), N_("Purc Price"), + N_("Curr Price"), N_("Condition"), N_("Purc Date"), N_("Srvc Date"), + N_("Locat'n"), N_("Notes") }; +static char * sortOrders[] = { + N_("Index"), N_("Scale"), N_("Manufacturer"), N_("Part No"), N_("Type"), + N_("Description"), N_("Roadname"), N_("RepMarks"), N_("Purch Price"), + N_("Curr Price"), N_("Condition"), N_("Purch Date"), N_("Service Date") }; +#define S_INDEX (0) +#define S_SCALE (1) +#define S_MANUF (2) +#define S_PARTNO (3) +#define S_TYPE (4) +#define S_DESC (5) +#define S_ROADNAME (6) +#define S_REPMARKS (7) +#define S_PURCHPRICE (8) +#define S_CURRPRICE (9) +#define S_CONDITION (10) +#define S_PURCHDATE (11) +#define S_SRVDATE (12) +static paramListData_t carInvListData = { 30, 600, sizeof carInvColumnTitles/sizeof carInvColumnTitles[0], carInvColumnWidths, carInvColumnTitles }; +static paramData_t carInvPLs[] = { +#define I_CI_SORT (0) + { PD_DROPLIST, &carInvSort[0], "sort1", PDO_LISTINDEX|0, (void*)110, N_("Sort By") }, + { PD_DROPLIST, &carInvSort[1], "sort2", PDO_LISTINDEX|PDO_DLGHORZ, (void*)110, "" }, + { PD_DROPLIST, &carInvSort[2], "sort3", PDO_LISTINDEX|PDO_DLGHORZ, (void*)110, "" }, + { PD_DROPLIST, &carInvSort[3], "sort4", PDO_LISTINDEX|PDO_DLGHORZ, (void*)110, "" }, +#define S (4) +#define I_CI_LIST (S+0) + { PD_LIST, &carInvInx, "list", PDO_LISTINDEX|PDO_DLGRESIZE|PDO_DLGNOLABELALIGN|PDO_DLGRESETMARGIN, &carInvListData, NULL, BO_READONLY|BL_MANY }, +#define I_CI_EDIT (S+1) + { PD_BUTTON, (void*)CarInvDlgEdit, "edit", PDO_DLGCMDBUTTON, NULL, N_("Edit") }, +#define I_CI_ADD (S+2) + { PD_BUTTON, (void*)CarInvDlgAdd, "add", 0, NULL, N_("Add"), 0, 0 }, +#define I_CI_DELETE (S+3) + { PD_BUTTON, (void*)CarInvDlgDelete, "delete", PDO_DLGWIDE, NULL, N_("Delete") }, +#define I_CI_IMPORT_CSV (S+4) + { PD_BUTTON, (void*)CarInvDlgImportCsv, "import", PDO_DLGWIDE, NULL, N_("Import") }, +#define I_CI_EXPORT_CSV (S+5) + { PD_BUTTON, (void*)CarInvDlgExportCsv, "export", 0, NULL, N_("Export") }, +#define I_CI_PRINT (S+6) + { PD_BUTTON, (void*)CarInvDlgSaveText, "savetext", 0, NULL, N_("List") } }; +static paramGroup_t carInvPG = { "carinv", 0, carInvPLs, sizeof carInvPLs/sizeof carInvPLs[0] }; + +static carItem_p CarInvDlgFindCurrentItem( void ) +{ + wIndex_t selcnt = wListGetSelectedCount( (wList_p)carInvPLs[I_CI_LIST].control ); + wIndex_t inx, cnt; + + if ( selcnt != 1 ) return NULL; + cnt = wListGetCount( (wList_p)carInvPLs[I_CI_LIST].control ); + for ( inx=0; inx<cnt; inx++ ) + if ( wListGetItemSelected( (wList_p)carInvPLs[I_CI_LIST].control, inx ) ) + break; + if ( inx>=cnt ) return NULL; + return (carItem_p)wListGetItemContext( (wList_p)carInvPLs[I_CI_LIST].control, inx ); +} + + +static void CarInvDlgFind( void * junk ) +{ + carItem_p item = CarInvDlgFindCurrentItem(); + coOrd pos; + ANGLE_T angle; + if ( item == NULL || item->car == NULL || IsTrackDeleted(item->car) ) return; + CarGetPos( item->car, &pos, &angle ); + CarSetVisible( item->car ); + DrawMapBoundingBox( FALSE ); + mainCenter = pos; + mainD.orig.x = pos.x-mainD.size.x/2;; + mainD.orig.y = pos.y-mainD.size.y/2;; + MainRedraw(); + DrawMapBoundingBox( TRUE ); +} + + +static void CarInvDlgAdd( void ) +{ + if ( carProto_da.cnt <= 0 ) { + NoticeMessage( MSG_NO_CARPROTO, _("Ok"), NULL ); + return; + } + carDlgUpdateItemPtr = NULL; + DoCarPartDlg( itemNewActions ); +} + + +static void CarInvDlgEdit( void ) +{ + carDlgUpdateItemPtr = CarInvDlgFindCurrentItem(); + if ( carDlgUpdateItemPtr == NULL ) + return; + DoCarPartDlg( itemUpdActions ); +} + + +static void CarInvDlgDelete( void ) +{ + carItem_p item; + wIndex_t inx, inx1, cnt, selcnt; + + selcnt = wListGetSelectedCount( (wList_p)carInvPLs[I_CI_LIST].control ); + if ( selcnt == 0 ) + return; + if ( NoticeMessage( MSG_CARINV_DELETE_CONFIRM, _("Yes"), _("No"), selcnt ) <= 0 ) + return; + cnt = wListGetCount( (wList_p)carInvPLs[I_CI_LIST].control ); + for ( inx=0; inx<cnt; inx++ ) { + if ( !wListGetItemSelected( (wList_p)carInvPLs[I_CI_LIST].control, inx ) ) + continue; + item = (carItem_p)wListGetItemContext( (wList_p)carInvPLs[I_CI_LIST].control, inx ); + if ( item == NULL ) + continue; + if ( item->car && !IsTrackDeleted(item->car) ) + continue; + wListDelete( (wList_p)carInvPLs[I_CI_LIST].control, inx ); + if ( item->title ) MyFree( item->title ); + if ( item->data.number ) MyFree( item->data.number ); + MyFree( item ); + for ( inx1=inx; inx1<carItemInfo_da.cnt-1; inx1++ ) + carItemInfo(inx1) = carItemInfo(inx1+1); + carItemInfo_da.cnt -= 1; + inx--; + cnt--; + } + changed++; + SetWindowTitle(); + carInvInx = -1; + ParamLoadControl( &carInvPG, I_CI_LIST ); + ParamControlActive( &carInvPG, I_CI_EDIT, FALSE ); + ParamControlActive( &carInvPG, I_CI_DELETE, FALSE ); + ParamControlActive( &carInvPG, I_CI_EXPORT_CSV, carItemInfo_da.cnt > 0 ); + ParamDialogOkActive( &carInvPG, FALSE ); +} + + +static int CarInvSaveText( + const char * pathName, + const char * fileName, + void * data ) +{ + FILE * f; + carItem_p item; + int inx; + int widths[9], width; + tabString_t tabs[7]; + char * cp0, * cp1; + int len; + + if ( pathName == NULL ) + return TRUE; + SetCurDir( pathName, fileName ); + f = fopen( pathName, "w" ); + if ( f == NULL ) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Car Inventory"), fileName, strerror(errno) ); + return FALSE; + } + + memset( widths, 0, sizeof widths ); + for ( inx=0; inx<carItemInfo_da.cnt; inx++ ) { + item = carItemInfo(inx); + TabStringExtract( item->title, 7, tabs ); + sprintf( message, "%ld", item->index ); + width = strlen( message ); + if ( width > widths[0] ) widths[0] = width; + width = strlen(GetScaleName(item->scaleInx)) + 1 + tabs[T_MANUF].len + 1 + tabs[T_PART].len; + if ( width > widths[1] ) widths[1] = width; + if ( tabs[T_PROTO].len > widths[2] ) widths[2] = tabs[T_PROTO].len; + width = tabs[T_REPMARK].len + tabs[T_NUMBER].len; + if ( tabs[T_REPMARK].len > 0 && tabs[T_NUMBER].len > 0 ) + width += 1; + if ( width > widths[3] ) widths[3] = width; + if ( item->data.purchDate > 0 ) widths[4] = 8; + if ( item->data.purchPrice > 0 ) { + sprintf( message, "%0.2f", item->data.purchPrice ); + width = strlen(message); + if ( width > widths[5] ) widths[5] = width; + } + if ( item->data.condition != 0 ) + widths[6] = 5; + if ( item->data.currPrice > 0 ) { + sprintf( message, "%0.2f", item->data.currPrice ); + width = strlen(message); + if ( width > widths[7] ) widths[7] = width; + } + if ( item->data.serviceDate > 0 ) widths[8] = 8; + } + fprintf( f, "%-*.*s %-*.*s %-*.*s %-*.*s", widths[0], widths[0], "#", widths[1], widths[1], "Part", widths[2], widths[2], "Description", widths[3], widths[3], "Rep Mark" ); + if ( widths[4] ) fprintf( f, " %-*.*s", widths[4], widths[4], "PurDate" ); + if ( widths[5] ) fprintf( f, " %-*.*s", widths[5], widths[5], "PurPrice" ); + if ( widths[6] ) fprintf( f, " %-*.*s", widths[6], widths[6], "Cond" ); + if ( widths[7] ) fprintf( f, " %-*.*s", widths[7], widths[7], "CurPrice" ); + if ( widths[8] ) fprintf( f, " %-*.*s", widths[8], widths[8], "SrvDate" ); + fprintf( f, "\n" ); + + for ( inx=0; inx<carItemInfo_da.cnt; inx++ ) { + item = carItemInfo(inx); + TabStringExtract( item->title, 7, tabs ); + sprintf( message, "%ld", item->index ); + fprintf( f, "%.*s", widths[0], message ); + width = tabs[T_MANUF].len + 1 + tabs[T_PART].len; + sprintf( message, "%s %.*s %.*s", GetScaleName(item->scaleInx), tabs[T_MANUF].len, tabs[T_MANUF].ptr, tabs[T_PART].len, tabs[T_PART].ptr ); + fprintf( f, " %-*s", widths[1], message ); + fprintf( f, " %-*.*s", widths[2], tabs[T_PROTO].len, tabs[T_PROTO].ptr ); + width = tabs[T_REPMARK].len + tabs[T_NUMBER].len; + sprintf( message, "%.*s%s%.*s", tabs[T_REPMARK].len, tabs[T_REPMARK].ptr, (tabs[T_REPMARK].len > 0 && tabs[T_NUMBER].len > 0)?" ":"", tabs[T_NUMBER].len, tabs[T_NUMBER].ptr ); + fprintf( f, " %-*s", widths[3], message ); + if ( widths[4] > 0 ) { + if ( item->data.purchDate > 0 ) { + sprintf( message, "%ld", item->data.purchDate ); + fprintf( f, " %*.*s", widths[4], widths[4], message ); + } else { + fprintf( f, " %*s", widths[4], " " ); + } + } + if ( widths[5] > 0 ) { + if ( item->data.purchPrice > 0 ) { + sprintf( message, "%0.2f", item->data.purchPrice ); + fprintf( f, " %*.*s", widths[5], widths[5], message ); + } else { + fprintf( f, " %*s", widths[5], " " ); + } + } + if ( widths[6] > 0 ) { + if ( item->data.condition != 0 ) { + fprintf( f, " %-*.*s", widths[6], widths[6], condListMap[MapCondition(item->data.condition)].name ); + } else { + fprintf( f, " %*s", widths[6], " " ); + } + } + if ( widths[7] > 0 ) { + if ( item->data.purchPrice > 0 ) { + sprintf( message, "%0.2f", item->data.purchPrice ); + fprintf( f, " %*.*s", widths[7], widths[7], message ); + } else { + fprintf( f, " %*s", widths[7], " " ); + } + } + if ( widths[8] > 0 ) { + if ( item->data.serviceDate > 0 ) { + sprintf( message, "%ld", item->data.serviceDate ); + fprintf( f, " %*.*s", widths[8], widths[8], message ); + } else { + fprintf( f, " %*s", widths[8], " " ); + } + } + fprintf( f, "\n" ); + if ( item->data.notes ) { + cp0 = item->data.notes; + while ( 1 ) { + cp1 = strchr( cp0, '\n' ); + if ( cp1 ) { + len = cp1-cp0; + } else { + len = strlen( cp0 ); + if ( len == 0 ) + break; + } + fprintf( f, "%*.*s %*.*s\n", widths[0], widths[0], " ", len, len, cp0 ); + if ( cp1 == NULL ) + break; + cp0 = cp1+1; + } + } + } + fclose( f ); + return TRUE; +} + + +static struct wFilSel_t * carInvSaveText_fs; +static void CarInvDlgSaveText( void ) +{ + if ( carInvSaveText_fs == NULL ) + carInvSaveText_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("List Cars"), + "Text|*.txt", CarInvSaveText, NULL ); + wFilSelect( carInvSaveText_fs, curDirName ); +} + + +static char *carCsvColumnTitles[] = { + "Index", "Scale", "Manufacturer", "Type", "Partno", "Prototype", + "Description", "Roadname", "Repmark", "Number", "Options", "CarLength", + "CarWidth", "CoupledLength", "TruckCenter", "Color", "PurchPrice", + "CurrPrice", "Condition", "PurchDate", "ServiceDate", "Notes" }; +#define M_INDEX (0) +#define M_SCALE (1) +#define M_MANUF (2) +#define M_TYPE (3) +#define M_PARTNO (4) +#define M_PROTO (5) +#define M_DESC (6) +#define M_ROADNAME (7) +#define M_REPMARK (8) +#define M_NUMBER (9) +#define M_OPTIONS (10) +#define M_CARLENGTH (11) +#define M_CARWIDTH (12) +#define M_CPLDLENGTH (13) +#define M_TRKCENTER (14) +#define M_COLOR (15) +#define M_PURCHPRICE (16) +#define M_CURRPRICE (17) +#define M_CONDITION (18) +#define M_PURCHDATE (19) +#define M_SRVDATE (20) +#define M_NOTES (21) + + +static int ParseCsvLine( + char * line, + int max_elem, + tabString_t * tabs, + int * map ) +{ + int elem = 0; + char * cp, * cq, * ptr; + int rc, len; + + cp = line; + for ( cq=cp+strlen(cp)-1; cq>cp&&isspace(*cq); cq-- ); + cq[1] = '\0'; + for ( elem=0; elem<max_elem; elem++ ) { + tabs[elem].ptr = ""; + tabs[elem].len = 0; + } + elem = 0; + while ( *cp && elem < max_elem ) { + while ( *cp == ' ' ) cp++; + if ( *cp == ',' ) { + ptr = ""; + len = 0; + } else if ( *cp == '"' ) { + cp++; + ptr = cq = cp; + while (1) { + while ( *cp!='"' ) { + if ( *cp == '\0' ) { + rc = NoticeMessage( MSG_CARIMP_EOL, _("Continue"), _("Stop"), ptr ); + return (rc<1)?-1:elem; + } + *cq++ = *cp++; + } + cp++; + if ( *cp!='"' ) break; + *cq++ = *cp++; + } + if ( *cp && *cp != ',' ) { + rc = NoticeMessage( MSG_CARIMP_MISSING_COMMA, _("Continue"), _("Stop"), ptr ); + return (rc<1)?-1:elem; + } + len = cq-ptr; + } else { + ptr = cp; + while ( *cp && *cp != ',' ) { cp++; } + len = cp-ptr; + } + if ( map[elem] >= 0 ) { + tabs[map[elem]].ptr = ptr; + tabs[map[elem]].len = len; + } + if ( *cp ) cp++; + elem++; + } + return elem; +} + + +static int CarInvImportCsv( + const char * pathName, + const char * fileName, + void * data ) +{ + FILE * f; + carItem_p item; + tabString_t tabs[40], partTabs[7]; + int map[40]; + int i, j, cnt, numCol, len, rc; + char * cp, * cq; + long type = 0; + char title[STR_LONG_SIZE]; + long index, options, color, condition, purchDate, srvcDate; + carDim_t dim; + FLOAT_T purchPrice, currPrice; + int duplicateIndexError = 0; + SCALEINX_T scale; + carPart_p partP; + int requiredCols; + char *oldLocale = NULL; + + if ( pathName == NULL ) + return TRUE; + SetCurDir( pathName, fileName ); + f = fopen( pathName, "r" ); + if ( f == NULL ) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Import Cars"), fileName, strerror(errno) ); + return FALSE; + } + + oldLocale = SaveLocale("C"); + + if ( fgets( message, sizeof message, f ) == NULL ) { + NoticeMessage( MSG_CARIMP_NO_DATA, _("Continue"), NULL ); + fclose( f ); + RestoreLocale(oldLocale); + return FALSE; + } + for ( j=0; j<40; j++ ) map[j] = j; + numCol = ParseCsvLine( message, 40, tabs, map ); + if ( numCol <= 0 ) { + fclose( f ); + RestoreLocale(oldLocale); + return FALSE; + } + for ( j=0; j<40; j++ ) map[j] = -1; + requiredCols = 0; + for ( i=0; i<numCol; i++ ) { + for ( j=0; j<sizeof carCsvColumnTitles/sizeof carCsvColumnTitles[0]; j++ ) { + if ( TabStringCmp( carCsvColumnTitles[j], &tabs[i] ) == 0 ) { + if ( map[i] >= 0 ) { + NoticeMessage( MSG_CARIMP_DUP_COLUMNS, _("Continue"), NULL, carCsvColumnTitles[j] ); + fclose( f ); + RestoreLocale(oldLocale); + return FALSE; + } + map[i] = j; + /*j = sizeof carCsvColumnTitles/sizeof carCsvColumnTitles[0];*/ + if ( j == M_SCALE || j == M_PROTO || j == M_MANUF || j == M_PARTNO ) + requiredCols++; + } + } + if ( map[i] == -1 ) { + tabs[i].ptr[tabs[i].len] = '\0'; + NoticeMessage( MSG_CARIMP_IGNORED_COLUMN, _("Continue"), NULL, tabs[i].ptr ); + tabs[i].ptr[tabs[i].len] = ','; + } + } + if ( requiredCols != 4 ) { + NoticeMessage( MSG_CARIMP_MISSING_COLUMNS, _("Continue"), NULL ); + fclose( f ); + RestoreLocale(oldLocale); + return FALSE; + } + while ( fgets( message, sizeof message, f ) != NULL ) { + cnt = ParseCsvLine( message, 40, tabs, map ); + if ( cnt > numCol ) cnt = numCol; + tabs[M_SCALE].ptr[tabs[M_SCALE].len] = '\0'; + scale = LookupScale( tabs[M_SCALE].ptr ); + tabs[M_SCALE].ptr[tabs[M_SCALE].len] = ','; + index = TabGetLong( &tabs[M_INDEX] ); + if ( index == 0 ) { + CheckCarDlgItemIndex( &carDlgItemIndex ); + index = carDlgItemIndex; + } else { + carDlgItemIndex = index; + if ( !CheckCarDlgItemIndex(&index) ) { + if ( !duplicateIndexError ) { + NoticeMessage( MSG_CARIMP_DUP_INDEX, _("Ok"), NULL ); + duplicateIndexError++; + } + carDlgItemIndex = index; + } + } +#ifdef OBSOLETE + if ( TabStringCmp( "Unknown", &tabs[M_MANUF] ) != 0 && + TabStringCmp( "Custom", &tabs[M_MANUF] ) != 0 ) { + if ( tabs[M_PARTNO].len == 0 ) { + rc = NoticeMessage( MSG_CARIMP_MISSING_PARTNO, _("Continue"), _("Stop"), tabs[M_MANUF].ptr ); + if ( rc <= 0 ) { + fclose( f ); + RestoreLocale(oldLocale); + return FALSE; + } + continue; + } + } +#endif + dim.carLength = TabGetFloat( &tabs[M_CARLENGTH] ); + dim.carWidth = TabGetFloat( &tabs[M_CARWIDTH] ); + dim.coupledLength = TabGetFloat( &tabs[M_CPLDLENGTH] ); + dim.truckCenter = TabGetFloat( &tabs[M_TRKCENTER] ); + partP = NULL; + if ( tabs[M_MANUF].len > 0 && tabs[M_PARTNO].len > 0 ) + partP = CarPartFind( tabs[M_MANUF].ptr, tabs[M_MANUF].len, tabs[M_PARTNO].ptr, tabs[M_PARTNO].len, scale ); + if ( partP ) { + TabStringExtract( partP->title, 7, partTabs ); + if ( tabs[M_PROTO].len == 0 && partTabs[T_PROTO].len > 0 ) { tabs[M_PROTO].ptr = partTabs[T_PROTO].ptr; tabs[M_PROTO].len = partTabs[T_PROTO].len; } + if ( tabs[M_DESC].len == 0 && partTabs[T_DESC].len > 0 ) { tabs[M_DESC].ptr = partTabs[T_DESC].ptr; tabs[M_DESC].len = partTabs[T_DESC].len; } + if ( tabs[M_ROADNAME].len == 0 && partTabs[T_ROADNAME].len > 0 ) { tabs[M_ROADNAME].ptr = partTabs[T_ROADNAME].ptr; tabs[M_ROADNAME].len = partTabs[T_ROADNAME].len; } + if ( tabs[M_REPMARK].len == 0 && partTabs[T_REPMARK].len > 0 ) { tabs[M_REPMARK].ptr = partTabs[T_REPMARK].ptr; tabs[M_REPMARK].len = partTabs[T_REPMARK].len; } + if ( tabs[M_NUMBER].len == 0 && partTabs[T_NUMBER].len > 0 ) { tabs[M_NUMBER].ptr = partTabs[T_NUMBER].ptr; tabs[M_NUMBER].len = partTabs[T_NUMBER].len; } + if ( dim.carLength <= 0 ) dim.carLength = partP->dim.carLength; + if ( dim.carWidth <= 0 ) dim.carWidth = partP->dim.carWidth; + if ( dim.coupledLength <= 0 ) dim.coupledLength = partP->dim.coupledLength; + if ( dim.truckCenter <= 0 ) dim.truckCenter = partP->dim.truckCenter; + } + cp = TabStringCpy( title, &tabs[M_MANUF] ); + *cp++ = '\t'; + cp = TabStringCpy( cp, &tabs[M_PROTO] ); + *cp++ = '\t'; + cp = TabStringCpy( cp, &tabs[M_DESC] ); + *cp++ = '\t'; + cp = TabStringCpy( cp, &tabs[M_PARTNO] ); + *cp++ = '\t'; + cp = TabStringCpy( cp, &tabs[M_ROADNAME] ); + *cp++ = '\t'; + cp = TabStringCpy( cp, &tabs[M_REPMARK] ); + *cp++ = '\t'; + cp = TabStringCpy( cp, &tabs[M_NUMBER] ); + *cp = '\0'; + options = TabGetLong( &tabs[M_OPTIONS] ); + type = TabGetLong( &tabs[M_TYPE] ); + color = TabGetLong( &tabs[M_COLOR] ); + purchPrice = TabGetFloat( &tabs[M_PURCHPRICE] ); + currPrice = TabGetFloat( &tabs[M_CURRPRICE] ); + condition = TabGetLong( &tabs[M_CONDITION] ); + purchDate = TabGetLong( &tabs[M_PURCHDATE] ); + srvcDate = TabGetLong( &tabs[M_SRVDATE] ); + if ( dim.carLength <= 0 || dim.carWidth <= 0 || dim.coupledLength <= 0 || dim.truckCenter <= 0 ) { + rc = NoticeMessage( MSG_CARIMP_MISSING_DIMS, _("Yes"), _("No"), message ); + if ( rc <= 0 ) { + fclose( f ); + RestoreLocale(oldLocale); + return FALSE; + } + continue; + } + item = CarItemNew( NULL, PARAM_CUSTOM, index, scale, title, options, type, + &dim, wDrawFindColor(color), + purchPrice, currPrice, condition, purchDate, srvcDate ); + if ( tabs[M_NOTES].len > 0 ) { + item->data.notes = cp = MyMalloc( tabs[M_NOTES].len+1 ); + for ( cq=tabs[M_NOTES].ptr,len=tabs[M_NOTES].len; *cq&&len; ) { + if ( strncmp( cq, "<NL>", 4 ) == 0 ) { + *cp++ = '\n'; + cq += 4; + len -= 4; + } else { + *cp++ = *cq++; + len -= 1; + } + } + } + changed++; + SetWindowTitle(); + } + fclose( f ); + RestoreLocale(oldLocale); + CarInvListLoad(); + return TRUE; +} + + + +static struct wFilSel_t * carInvImportCsv_fs; +static void CarInvDlgImportCsv( void ) +{ + if ( carInvImportCsv_fs == NULL ) + carInvImportCsv_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Import Cars"), + _("Comma-Separated-Values|*.csv"), CarInvImportCsv, NULL ); + wFilSelect( carInvImportCsv_fs, curDirName ); +} + + +static void CsvFormatString( + FILE * f, + char * str, + int len, + char * sep ) +{ + while ( str && len>0 && str[len-1]=='\n' ) len--; + if ( *str && len ) { + fputc( '"', f ); + for ( ; *str && len; str++,len-- ) { + if ( !iscntrl( *str ) ) { + if ( *str == '"' ) + fputc( '"', f ); + fputc( *str, f ); + } else if ( *str == '\n' && str[1] && len > 1 ) { + fprintf( f, "<NL>" ); + } + } + fputc( '"', f ); + } + fprintf( f, "%s", sep ); +} + + +static void CsvFormatLong( + FILE * f, + long val, + char * sep ) +{ + if ( val != 0 ) + fprintf( f, "%ld", val ); + fprintf( f, "%s", sep ); +} + + +static void CsvFormatFloat( + FILE * f, + FLOAT_T val, + int digits, + char * sep ) +{ + if ( val != 0.0 ) + fprintf( f, "%0.*f", digits, val ); + fprintf( f, "%s", sep ); +} + + +static int CarInvExportCsv( + const char * pathName, + const char * fileName, + void * data ) +{ + FILE * f; + carItem_p item; + long inx; + tabString_t tabs[7]; + char * sp; + char *oldLocale = NULL; + + if ( pathName == NULL ) + return TRUE; + SetCurDir( pathName, fileName ); + f = fopen( pathName, "w" ); + if ( f == NULL ) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Export Cars"), fileName, strerror(errno) ); + return FALSE; + } + + oldLocale = SaveLocale("C"); + + for ( inx=0; inx<sizeof carCsvColumnTitles/sizeof carCsvColumnTitles[0]; inx++ ) { + CsvFormatString( f, carCsvColumnTitles[inx], strlen(carCsvColumnTitles[inx]), inx<(sizeof carCsvColumnTitles/sizeof carCsvColumnTitles[0])-1?",":"\n" ); + } + for ( inx=0; inx<carItemInfo_da.cnt; inx++ ) { + item = carItemInfo( inx ); + TabStringExtract( item->title, 7, tabs ); + CsvFormatLong( f, item->index, "," ); + sp = GetScaleName(item->scaleInx); + CsvFormatString( f, sp, strlen(sp), "," ); + CsvFormatString( f, tabs[T_MANUF].ptr, tabs[T_MANUF].len, "," ); + CsvFormatLong( f, item->type, "," ); + CsvFormatString( f, tabs[T_PART].ptr, tabs[T_PART].len, "," ); + CsvFormatString( f, tabs[T_PROTO].ptr, tabs[T_PROTO].len, "," ); + CsvFormatString( f, tabs[T_DESC].ptr, tabs[T_DESC].len, "," ); + CsvFormatString( f, tabs[T_ROADNAME].ptr, tabs[T_ROADNAME].len, "," ); + CsvFormatString( f, tabs[T_REPMARK].ptr, tabs[T_REPMARK].len, "," ); + CsvFormatString( f, tabs[T_NUMBER].ptr, tabs[T_NUMBER].len, "," ); + CsvFormatLong( f, item->options, "," ); + CsvFormatFloat( f, item->dim.carLength, 3, "," ); + CsvFormatFloat( f, item->dim.carWidth, 3, "," ); + CsvFormatFloat( f, item->dim.coupledLength, 3, "," ); + CsvFormatFloat( f, item->dim.truckCenter, 3, "," ); + CsvFormatLong( f, wDrawGetRGB(item->color), "," ); + CsvFormatFloat( f, item->data.purchPrice, 2, "," ); + CsvFormatFloat( f, item->data.currPrice, 2, "," ); + CsvFormatLong( f, item->data.condition, "," ); + CsvFormatLong( f, item->data.purchDate, "," ); + CsvFormatLong( f, item->data.serviceDate, "," ); + if ( item->data.notes ) + CsvFormatString( f, item->data.notes, strlen(item->data.notes), "\n" ); + else + CsvFormatString( f, "", strlen(""), "\n" ); + } + fclose( f ); + RestoreLocale(oldLocale); + return TRUE; +} + + +static struct wFilSel_t * carInvExportCsv_fs; +static void CarInvDlgExportCsv( void ) +{ + if ( carItemInfo_da.cnt <= 0 ) + return; + if ( carInvExportCsv_fs == NULL ) + carInvExportCsv_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Export Cars"), + _("Comma-Separated-Values|*.csv"), CarInvExportCsv, NULL ); + wFilSelect( carInvExportCsv_fs, curDirName ); +} + + +static void CarInvLoadItem( + carItem_p item ) +{ +/* "Index", "Scale", "Manufacturer", "Type", "Part No", "Description", "Roadname", "RepMarks", + "Purch Price", "Curr Price", "Condition", "Purch Date", "Service Date", "Location", "Notes" */ + char *condition; + char *location; + char *manuf; + char *road; + char notes[100]; + tabString_t tabs[7]; + + TabStringExtract( item->title, 7, tabs ); + if ( item->data.notes ) { + strncpy( notes, item->data.notes, sizeof notes - 1 ); + notes[sizeof notes - 1] = '\0'; + } else { + notes[0] = '\0'; + } + condition = + (item->data.condition < 10) ? N_("N/A"): + (item->data.condition < 30) ? N_("Poor"): + (item->data.condition < 50) ? N_("Fair"): + (item->data.condition < 70) ? N_("Good"): + (item->data.condition < 90) ? N_("Excellent"): + N_("Mint"); + + if ( item->car && !IsTrackDeleted(item->car) ) + location = N_("Layout"); + else + location = N_("Shelf"); + + manuf = TabStringDup(&tabs[T_MANUF]); + road = TabStringDup(&tabs[T_ROADNAME]); + sprintf( message, "%ld\t%s\t%s\t%.*s\t%s\t%.*s%s%.*s\t%s\t%.*s%s%.*s\t%0.2f\t%0.2f\t%s\t%ld\t%ld\t%s\t%s", + item->index, GetScaleName(item->scaleInx), + _(manuf), + tabs[T_PART].len, tabs[T_PART].ptr, + _(typeListMap[CarProtoFindTypeCode(item->type)].name), + tabs[T_PROTO].len, tabs[T_PROTO].ptr, + (tabs[T_PROTO].len>0 && tabs[T_DESC].len)?"/":"", + tabs[T_DESC].len, tabs[T_DESC].ptr, + _(road), + tabs[T_REPMARK].len, tabs[T_REPMARK].ptr, + (tabs[T_REPMARK].len>0&&tabs[T_NUMBER].len>0)?" ":"", + tabs[T_NUMBER].len, tabs[T_NUMBER].ptr, + item->data.purchPrice, item->data.currPrice, _(condition), item->data.purchDate, item->data.serviceDate, _(location), notes ); + if (manuf) MyFree(manuf); + if (road) MyFree(road); + wListAddValue( (wList_p)carInvPLs[I_CI_LIST].control, message, NULL, item ); +} + + +static int Cmp_carInvItem( + const void * ptr1, + const void * ptr2 ) +{ + carItem_p item1 = *(carItem_p*)ptr1; + carItem_p item2 = *(carItem_p*)ptr2; + tabString_t tabs1[7], tabs2[7]; + int inx; + int rc; + + TabStringExtract( item1->title, 7, tabs1 ); + TabStringExtract( item2->title, 7, tabs2 ); + for ( inx=0,rc=0; inx<N_SORT&&rc==0; inx++ ) { + switch ( carInvSort[inx] ) { + case S_INDEX: + rc = (int)(item1->index-item2->index); + break; + case S_SCALE: + rc = (int)(item1->scaleInx-item2->scaleInx); + case S_MANUF: + rc = strncasecmp( tabs1[T_MANUF].ptr, tabs2[T_MANUF].ptr, max(tabs1[T_MANUF].len,tabs2[T_MANUF].len) ); + break; + case S_TYPE: + rc = (int)(item1->type-item2->type); + break; + case S_PARTNO: + rc = strncasecmp( tabs1[T_PART].ptr, tabs2[T_PART].ptr, max(tabs1[T_PART].len,tabs2[T_PART].len) ); + break; + case S_DESC: + rc = strncasecmp( tabs1[T_PROTO].ptr, tabs2[T_PROTO].ptr, max(tabs1[T_PROTO].len,tabs2[T_PROTO].len) ); + if ( rc != 0 ) + break; + rc = strncasecmp( tabs1[T_DESC].ptr, tabs2[T_DESC].ptr, max(tabs1[T_DESC].len,tabs2[T_DESC].len) ); + break; + case S_ROADNAME: + rc = strncasecmp( tabs1[T_ROADNAME].ptr, tabs2[T_ROADNAME].ptr, max(tabs1[T_ROADNAME].len,tabs2[T_ROADNAME].len) ); + break; + case S_REPMARKS: + rc = strncasecmp( tabs1[T_REPMARK].ptr, tabs2[T_REPMARK].ptr, max(tabs1[T_REPMARK].len,tabs2[T_REPMARK].len) ); + break; + case S_PURCHPRICE: + rc = (int)(item1->data.purchPrice-item2->data.purchPrice); + break; + case S_CURRPRICE: + rc = (int)(item1->data.currPrice-item2->data.currPrice); + break; + case S_CONDITION: + rc = (int)(item1->data.condition-item2->data.condition); + break; + case S_PURCHDATE: + rc = (int)(item1->data.purchDate-item2->data.purchDate); + break; + case S_SRVDATE: + rc = (int)(item1->data.serviceDate-item2->data.serviceDate); + break; + default: + break; + } + } + return rc; +} + +static void CarInvListLoad( void ) +{ + int inx; + carItem_p item; + + qsort( carItemInfo_da.ptr, carItemInfo_da.cnt, sizeof item, Cmp_carInvItem ); + ParamControlShow( &carInvPG, I_CI_LIST, FALSE ); + wListClear( (wList_p)carInvPLs[I_CI_LIST].control ); + for ( inx=0; inx<carItemInfo_da.cnt; inx++ ) { + item = carItemInfo(inx); + CarInvLoadItem( item ); + } + ParamControlShow( &carInvPG, I_CI_LIST, TRUE ); + ParamControlActive( &carInvPG, I_CI_EDIT, FALSE ); + ParamControlActive( &carInvPG, I_CI_DELETE, FALSE ); + ParamControlActive( &carInvPG, I_CI_EXPORT_CSV, carItemInfo_da.cnt > 0 ); + ParamDialogOkActive( &carInvPG, FALSE ); +} + + +static void CarInvDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + carItem_p item = NULL; + wIndex_t cnt, selinx, selcnt; + wBool_t enableDelete; + + if ( inx >= I_CI_SORT && inx < I_CI_SORT+N_SORT ) { + item = CarInvDlgFindCurrentItem(); + CarInvListLoad(); + if ( item ) { + carInvInx = (wIndex_t)CarItemFindIndex( item ); + if ( carInvInx >= 0 ) + ParamLoadControl( &carInvPG, I_CI_LIST ); + } + } else if ( inx == I_CI_LIST ) { + cnt = wListGetCount( (wList_p)carInvPLs[I_CI_LIST].control ); + enableDelete = TRUE; + for ( selinx=selcnt=0; selinx<cnt; selinx++ ) { + if ( wListGetItemSelected( (wList_p)carInvPLs[I_CI_LIST].control, selinx ) ) { + selcnt++; + item = (carItem_p)wListGetItemContext( (wList_p)carInvPLs[I_CI_LIST].control, selinx ); + if ( item && item->car && !IsTrackDeleted( item->car ) ) { + enableDelete = FALSE; + break; + } + } + } + item = CarInvDlgFindCurrentItem(); + ParamDialogOkActive( pg, selcnt==1 && item && item->car && !IsTrackDeleted(item->car) ); + ParamControlActive( &carInvPG, I_CI_EDIT, selcnt==1 && item && (item->car==NULL || IsTrackDeleted(item->car)) ); + ParamControlActive( &carInvPG, I_CI_DELETE, selcnt>0 && enableDelete ); + } +} + + +static void CarInvListAdd( + carItem_p item ) +{ + CarInvListLoad(); + carInvInx = (wIndex_t)CarItemFindIndex( item ); + if ( carInvInx >= 0 ) { + ParamLoadControl( &carInvPG, I_CI_LIST ); + } +} + + +static void CarInvListUpdate( + carItem_p item ) +{ + CarInvListLoad(); + carInvInx = (wIndex_t)CarItemFindIndex( item ); + if ( carInvInx >= 0 ) { + ParamLoadControl( &carInvPG, I_CI_LIST ); + } +} + + +EXPORT void DoCarDlg( void ) +{ + int inx, inx2; + if ( carInvPG.win == NULL ) { + ParamCreateDialog( &carInvPG, MakeWindowTitle(_("Car Inventory")), _("Find"), CarInvDlgFind, wHide, TRUE, NULL, F_BLOCK|F_RESIZE|F_RECALLSIZE|PD_F_ALT_CANCELLABEL, CarInvDlgUpdate ); + for ( inx=I_CI_SORT; inx<I_CI_SORT+N_SORT; inx++ ) { + for ( inx2=0; inx2<sizeof sortOrders/sizeof sortOrders[0]; inx2++ ) { + wListAddValue( (wList_p)carInvPLs[inx].control, _(sortOrders[inx2]), NULL, NULL ); + ParamLoadControl( &carInvPG, inx ); + } + } + ParamDialogOkActive( &carInvPG, FALSE ); + } + CarInvListLoad(); + wShow( carInvPG.win ); +} + + +static void CarDlgChange( long changes ) +{ + if ( (changes&CHANGE_SCALE) ) { + carPartChangeLevel = 0; + carDlgCouplerLength = 0.0; + } +} + + +EXPORT void ClearCars( void ) +{ + int inx; + for ( inx=0; inx<carItemInfo_da.cnt; inx++ ) + MyFree( carItemInfo(inx) ); + carItemInfo_da.cnt = 0; + carItemInfo_da.max = 0; + if ( carItemInfo_da.ptr ) + MyFree( carItemInfo_da.ptr ); + carItemInfo_da.ptr = NULL; +} + + +static struct { + dynArr_t carProto_da; + dynArr_t carPartParent_da; + dynArr_t carItemInfo_da; + } savedCarState; + +EXPORT void SaveCarState( void ) +{ + savedCarState.carProto_da = carProto_da; + savedCarState.carPartParent_da = carPartParent_da; + savedCarState.carItemInfo_da = carItemInfo_da; + carItemInfo_da.cnt = carItemInfo_da.max = 0; + carItemInfo_da.ptr = NULL; +} + + +EXPORT void RestoreCarState( void ) +{ +#ifdef LATER + carProto_da = savedCarState.carProto_da; + carPartParent_da = savedCarState.carPartParent_da; +#endif + carItemInfo_da = savedCarState.carItemInfo_da; +} + + + +EXPORT void InitCarDlg( void ) +{ + log_carList = LogFindIndex( "carList" ); + log_carInvList = LogFindIndex( "carInvList" ); + log_carDlgState = LogFindIndex( "carDlgState" ); + log_carDlgList = LogFindIndex( "carDlgList" ); + carDlgBodyColor = wDrawFindColor( wRGB(255,128,0) ); + ParamRegister( &carDlgPG ); + ParamRegister( &carInvPG ); + RegisterChangeNotification( CarDlgChange ); + AddParam( "CARPROTO ", CarProtoRead ); + AddParam( "CARPART ", CarPartRead ); + ParamRegister( &newCarPG ); + ParamCreateControls( &newCarPG, CarItemHotbarUpdate ); + newCarControls[0] = newCarPLs[0].control; +} + +/***************************************************************************** + * + * Custom Management Support + * + */ + +static int CarPartCustMgmProc( + int cmd, + void * data ) +{ + tabString_t tabs[7]; + int rd_inx; + + carPart_p partP = (carPart_p)data; + switch ( cmd ) { + case CUSTMGM_DO_COPYTO: + return CarPartWrite( customMgmF, partP ); + case CUSTMGM_CAN_EDIT: + return TRUE; + case CUSTMGM_DO_EDIT: + if ( partP == NULL ) + return FALSE; + carDlgUpdatePartPtr = partP; + DoCarPartDlg( partUpdActions ); + return TRUE; + case CUSTMGM_CAN_DELETE: + return TRUE; + case CUSTMGM_DO_DELETE: + CarPartDelete( partP ); + return TRUE; + case CUSTMGM_GET_TITLE: + TabStringExtract( partP->title, 7, tabs ); + rd_inx = T_REPMARK; + if ( tabs[T_REPMARK].len == 0 ) + rd_inx = T_ROADNAME; + sprintf( message, "\t%s\t%s\t%.*s\t%s%s%.*s%s%.*s%s%.*s", + partP->parent->manuf, + GetScaleName(partP->parent->scale), + tabs[T_PART].len, tabs[T_PART].ptr, + partP->parent->proto, + tabs[T_DESC].len?", ":"", tabs[T_DESC].len, tabs[T_DESC].ptr, + tabs[rd_inx].len?", ":"", tabs[rd_inx].len, tabs[rd_inx].ptr, + tabs[T_NUMBER].len?" ":"", tabs[T_NUMBER].len, tabs[T_NUMBER].ptr ); + return TRUE; + } + return FALSE; +} + + +static int CarProtoCustMgmProc( + int cmd, + void * data ) +{ + carProto_p protoP = (carProto_p)data; + switch ( cmd ) { + case CUSTMGM_DO_COPYTO: + return CarProtoWrite( customMgmF, protoP ); + case CUSTMGM_CAN_EDIT: + return TRUE; + case CUSTMGM_DO_EDIT: + if ( protoP == NULL ) + return FALSE; + carDlgUpdateProtoPtr = protoP; + DoCarPartDlg( protoUpdActions ); + return TRUE; + case CUSTMGM_CAN_DELETE: + return TRUE; + case CUSTMGM_DO_DELETE: + CarProtoDelete( protoP ); + return TRUE; + case CUSTMGM_GET_TITLE: + sprintf( message, "\t%s\t\t%s\t%s", _("Prototype"), _(typeListMap[CarProtoFindTypeCode(protoP->type)].name), protoP->desc ); + return TRUE; + } + return FALSE; +} + + +#include "bitmaps/carpart.xpm" +#include "bitmaps/carproto.xpm" + +EXPORT void CarCustMgmLoad( void ) +{ + long parentX, partX, protoX; + carPartParent_p parentP; + carPart_p partP; + carProto_p carProtoP; + static wIcon_p carpartI = NULL; + static wIcon_p carprotoI = NULL; + + if ( carpartI == NULL ) + carpartI = wIconCreatePixMap( carpart_xpm ); + if ( carprotoI == NULL ) + carprotoI = wIconCreatePixMap( carproto_xpm ); + + for ( parentX=0; parentX<carPartParent_da.cnt; parentX++ ) { + parentP = carPartParent(parentX); + for ( partX=0; partX<parentP->parts_da.cnt; partX++ ) { + partP = carPart(parentP,partX); + if ( partP->paramFileIndex != PARAM_CUSTOM ) + continue; + CustMgmLoad( carpartI, CarPartCustMgmProc, (void*)partP ); + } + } + + for ( protoX=0; protoX<carProto_da.cnt; protoX++ ) { + carProtoP = carProto(protoX); + if ( carProtoP->paramFileIndex != PARAM_CUSTOM ) + continue; + if (carProtoP->paramFileIndex == PARAM_CUSTOM) { + CustMgmLoad( carprotoI, CarProtoCustMgmProc, (void*)carProtoP ); + } + } +} diff --git a/app/bin/dcmpnd.c b/app/bin/dcmpnd.c new file mode 100644 index 0000000..2cff06c --- /dev/null +++ b/app/bin/dcmpnd.c @@ -0,0 +1,590 @@ +/* \file dcmpnd.c + * Compound tracks: Turnouts and Structures + * + */ + +/* 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 <ctype.h> +#include "track.h" +#include "compound.h" +#include "shrtpath.h" +#include "i18n.h" + + +/***************************************************************************** + * + * Update Titles + * + */ + +static wWin_p updateTitleW; +typedef enum { updateUnknown, updateTurnout, updateStructure } updateType_e; +static updateType_e updateListType; +static BOOL_T updateWVisible; +static BOOL_T updateWStale; +typedef struct { + updateType_e type; + SCALEINX_T scale; + char * old; + char * new; + } updateTitleElement; +static dynArr_t updateTitles_da; +#define updateTitles(N) DYNARR_N( updateTitleElement, updateTitles_da, N ) + +static void UpdateTitleIgnore( void* junk ); +static wIndex_t updateTitleInx; +static paramData_t updateTitlePLs[] = { + { PD_MESSAGE, "This file contains Turnout and Structure Titles which should be updated." }, + { PD_MESSAGE, "This dialog allows you to change the definitions in this file." }, + { PD_MESSAGE, "To replace the old name, choose a definition from the list." }, + { PD_MESSAGE, "If the required definition is not loaded you can use the Load button" }, + { PD_MESSAGE, "to invoke the Parameter Files dialog to load the required Parameter File." }, + { PD_MESSAGE, "If you choose Cancel then the Titles will not be changed and some" }, + { PD_MESSAGE, "features (Price List and Label selection) may not be fully functional." }, + { PD_MESSAGE, "You can use the List Labels control on the Preferences dialog to" }, + { PD_MESSAGE, "control the format of the list entries" }, +#define I_UPDATESTR (9) + { PD_STRING, NULL, "old", PDO_NOPREF, (void*)400, NULL, BO_READONLY }, +#define I_UPDATELIST (10) +#define updateTitleL ((wList_p)updateTitlePLs[I_UPDATELIST].control) + { PD_DROPLIST, NULL, "sel", PDO_NOPREF, (void*)400 }, + { PD_BUTTON, (void*)UpdateTitleIgnore, "ignore", PDO_DLGCMDBUTTON, NULL, N_("Ignore") }, +#define I_UPDATELOAD (12) + { PD_BUTTON, NULL, "load", 0, NULL, N_("Load") } }; +static paramGroup_t updateTitlePG = { "updatetitle", 0, updateTitlePLs, sizeof updateTitlePLs/sizeof updateTitlePLs[0] }; + + +static void UpdateTitleChange( long changes ) +{ + if ( (changes & (CHANGE_SCALE|CHANGE_PARAMS)) == 0 ) + return; + if (!updateWVisible) { + updateWStale = TRUE; + return; + } + wControlShow( (wControl_p)updateTitleL, FALSE ); + wListClear( updateTitleL ); + if (updateTitles(updateTitleInx).type == updateTurnout) + TurnoutAdd( listLabels, updateTitles(updateTitleInx).scale, updateTitleL, NULL, -1 ); + else + StructAdd( listLabels, updateTitles(updateTitleInx).scale, updateTitleL, NULL ); + wControlShow( (wControl_p)updateTitleL, TRUE ); + updateListType = updateTitles(updateTitleInx).type; +} + + +static void UpdateTitleNext( void ) +{ + wIndex_t inx; + wIndex_t cnt; + track_p trk; + struct extraData *xx; + updateTitleInx++; + if (updateTitleInx >= updateTitles_da.cnt) { + wHide( updateTitleW ); + updateWVisible = FALSE; + InfoMessage( _("Updating definitions, please wait") ); + cnt = 0; + trk = NULL; + while (TrackIterate( &trk ) ) { + InfoCount(cnt++); + if (GetTrkType(trk) == T_TURNOUT || GetTrkType(trk) == T_STRUCTURE) { + xx = GetTrkExtraData(trk); + for (inx=0; inx<updateTitles_da.cnt; inx++) { + if ( updateTitles(inx).old && + strcmp( xx->title, updateTitles(inx).old ) == 0 ) { + xx->title = MyStrdup( updateTitles(inx).new ); + break; + } + } + } + } + DYNARR_RESET( updateTitleElement, updateTitles_da ); + InfoMessage(""); + InfoCount( trackCount ); + changed++; + SetWindowTitle(); + DoChangeNotification( CHANGE_MAIN ); + return; + } + ParamLoadMessage( &updateTitlePG, I_UPDATESTR, updateTitles(updateTitleInx).old ); + if (updateWStale || updateTitles(updateTitleInx).type != updateListType) + UpdateTitleChange( CHANGE_SCALE|CHANGE_PARAMS ); +} + + +static void UpdateTitleUpdate( void* junk ) +{ + void * selP; + turnoutInfo_t * to; + wListGetValues( updateTitleL, NULL, 0, NULL, &selP ); + if (selP != NULL) { + to = (turnoutInfo_t*)selP; + updateTitles(updateTitleInx).new = to->title; + } + UpdateTitleNext(); +} + +static void UpdateTitleIgnore( void* junk ) +{ + updateTitles(updateTitleInx).old = NULL; + UpdateTitleNext(); +} + +static void UpdateTitleCancel( wWin_p junk ) +{ + wHide( updateTitleW ); + DYNARR_RESET( updateTitleElement, updateTitles_da ); + updateWVisible = FALSE; +} + + +void DoUpdateTitles( void ) +{ + if (updateTitles_da.cnt <= 0) + return; + if (updateTitleW == NULL) { + ParamRegister( &updateTitlePG ); + updateTitlePLs[I_UPDATELOAD].valueP = (void*)ParamFilesInit(); + updateTitleW = ParamCreateDialog( &updateTitlePG, MakeWindowTitle(_("Update Title")), _("Update"), UpdateTitleUpdate, UpdateTitleCancel, TRUE, NULL, 0, NULL ); + RegisterChangeNotification( UpdateTitleChange ); + } + updateTitleInx = -1; + wShow( updateTitleW ); + updateWVisible = TRUE; + updateListType = updateUnknown; + UpdateTitleNext(); +} + +EXPORT void UpdateTitleMark( + char * title, + SCALEINX_T scale ) +{ + int inx; + updateTitleElement * ut; + if ( inPlayback ) + return; + for (inx=0; inx<updateTitles_da.cnt; inx++) { + if (strcmp(title,updateTitles(inx).old) == 0) { + return; + } + } + DYNARR_APPEND( updateTitleElement, updateTitles_da, 10 ); + ut = &updateTitles(updateTitles_da.cnt-1); + if ( tempEndPts_da.cnt > 0) + ut->type = updateTurnout; + else + ut->type = updateStructure; + ut->scale = scale; + ut->old = MyStrdup(title); + ut->new = NULL; +} + +/***************************************************************************** + * + * Refresh Compound + * + */ + +static BOOL_T CheckCompoundEndPoint( + track_p trk, + EPINX_T trkEp, + turnoutInfo_t * to, + EPINX_T toEp, + BOOL_T flip ) +{ + + struct extraData *xx = GetTrkExtraData(trk); + coOrd pos; + DIST_T d; + ANGLE_T a, a2; + pos = GetTrkEndPos( trk, trkEp ); + Rotate( &pos, xx->orig, -xx->angle ); + pos.x -= xx->orig.x; + pos.y -= xx->orig.y; + if ( flip ) + pos.y = - pos.y; + d = FindDistance( pos, to->endPt[toEp].pos ); + if ( d > connectDistance ) { + sprintf( message, _("End-Point #%d of the selected and actual turnouts are not close"), toEp ); + return FALSE; + } + a = GetTrkEndAngle( trk, trkEp ); + a2 = to->endPt[toEp].angle; + if ( flip ) + a2 = 180.0 - a2; + a = NormalizeAngle( a - xx->angle - a2 + connectAngle/2.0 ); + if ( a > connectAngle ) { + sprintf( message, _("End-Point #%d of the selected and actual turnouts are not aligned"), toEp ); + return FALSE; + } + return TRUE; +} + + +int refreshCompoundCnt; +static BOOL_T RefreshCompound1( + track_p trk, + turnoutInfo_t * to ) +{ + struct extraData *xx = GetTrkExtraData(trk); + EPINX_T ep, epCnt; + BOOL_T ok; + BOOL_T flip = FALSE; + + epCnt = GetTrkEndPtCnt(trk); + if ( epCnt != to->endCnt ) { + strcpy( message, _("The selected Turnout had a differing number of End-Points") ); + return FALSE; + } + ok = TRUE; + for ( ep=0; ep<epCnt; ep++ ) + if (!CheckCompoundEndPoint( trk, ep, to, ep, FALSE )) { + ok = FALSE; + break; + } + if ( !ok ) { + if ( ep > 0 && epCnt == 2 && + CheckCompoundEndPoint( trk, 1, to, 1, TRUE ) ) { + flip = TRUE; + ok = TRUE; + } else if ( ep > 0 && epCnt == 3 && + CheckCompoundEndPoint( trk, 1, to, 2, FALSE ) && + CheckCompoundEndPoint( trk, 2, to, 1, FALSE ) ) { + ok = TRUE; + } else if ( ep > 0 && epCnt == 4 && + CheckCompoundEndPoint( trk, 1, to, 3, FALSE ) && + CheckCompoundEndPoint( trk, 2, to, 2, FALSE ) && + CheckCompoundEndPoint( trk, 3, to, 1, FALSE ) ) { + ok = TRUE; + } else { + return FALSE; + } + } + UndoModify( trk ); + FreeFilledDraw( xx->segCnt, xx->segs ); + MyFree( xx->segs ); + xx->segCnt = to->segCnt; + xx->segs = (trkSeg_p)MyMalloc( xx->segCnt * sizeof *(trkSeg_p)0 ); + memcpy( xx->segs, to->segs, xx->segCnt * sizeof *(trkSeg_p)0 ); + if ( flip ) + FlipSegs( xx->segCnt, xx->segs, zero, 90.0 ); + ClrTrkBits( trk, TB_SELECTED ); + refreshCompoundCnt++; + CloneFilledDraw( xx->segCnt, xx->segs, FALSE ); + return TRUE; +} + + +typedef struct { + char * name; + turnoutInfo_t * to; + } refreshSpecial_t; +static dynArr_t refreshSpecial_da; +#define refreshSpecial(N) DYNARR_N( refreshSpecial_t, refreshSpecial_da, N ) +static wIndex_t refreshSpecialInx; +static BOOL_T refreshReturnVal; +static void RefreshSkip( void * ); +static paramListData_t refreshSpecialListData = { 30, 600, 0, NULL, NULL }; +static paramData_t refreshSpecialPLs[] = { +#define REFRESH_M1 (0) + { PD_MESSAGE, NULL, NULL, 0/*PDO_DLGRESIZEW*/, (void*)380 }, +#define REFRESH_M2 (1) + { PD_MESSAGE, NULL, NULL, 0/*PDO_DLGRESIZEW*/, (void*)380 }, +#define REFRESH_S (2) + { PD_MESSAGE, NULL, NULL, 0/*PDO_DLGRESIZEW*/, (void*)380 }, +#define REFRESH_L (3) + { PD_LIST, &refreshSpecialInx, "list", PDO_LISTINDEX|PDO_NOPREF|PDO_DLGRESIZE, &refreshSpecialListData, NULL, BO_READONLY }, + { PD_BUTTON, (void*)RefreshSkip, "skip", PDO_DLGCMDBUTTON, NULL, N_("Skip") } }; +static paramGroup_t refreshSpecialPG = { "refreshSpecial", 0, refreshSpecialPLs, sizeof refreshSpecialPLs/sizeof refreshSpecialPLs[0] }; +static void RefreshSpecialOk( + void * junk ) +{ + wHide( refreshSpecialPG.win ); +} +static void RefreshSpecialCancel( + wWin_p win ) +{ + refreshSpecialInx = -1; + refreshReturnVal = FALSE; + wHide( refreshSpecialPG.win ); +} +static void RefreshSkip( + void * junk ) +{ + refreshSpecialInx = -1; + wHide( refreshSpecialPG.win ); +} + +EXPORT BOOL_T RefreshCompound( + track_p trk, + BOOL_T junk ) +{ + TRKTYP_T trkType; + struct extraData *xx; + int inx; + turnoutInfo_t *to; + SCALEINX_T scale; + + if ( trk == NULL ) { + InfoMessage( _("%d Track(s) refreshed"), refreshCompoundCnt ); + refreshCompoundCnt = 0; + for ( inx=0; inx<refreshSpecial_da.cnt; inx++ ) + if ( refreshSpecial(inx).name != NULL && + refreshSpecial(inx).to == NULL ) + refreshSpecial(inx).name = NULL; + return FALSE; + } + trkType = GetTrkType(trk); + xx = GetTrkExtraData(trk); + scale = GetTrkScale(trk); + if ( trkType != T_TURNOUT && trkType != T_STRUCTURE ) { + ClrTrkBits( trk, TB_SELECTED ); + return TRUE; + } + refreshReturnVal = TRUE; + for ( inx=0; inx<refreshSpecial_da.cnt; inx++ ) { + if ( refreshSpecial(inx).name != NULL && + strcasecmp( xx->title, refreshSpecial(inx).name ) == 0 ) { + to = refreshSpecial(inx).to; + if ( to == NULL ) + return TRUE; + if ( IsParamValid(to->paramFileIndex) && + to->segCnt > 0 && + CompatibleScale( GetTrkEndPtCnt(trk)>0, to->scaleInx, scale ) ) { + if ( RefreshCompound1( trk, refreshSpecial(inx).to ) ) { + if ( strcasecmp( xx->title, to->title ) != 0 ) { + MyFree( xx->title ); + xx->title = MyStrdup( to->title ); + } + return TRUE; + } + } + } + } + if ( ( to = FindCompound( FIND_TURNOUT|FIND_STRUCT, NULL, xx->title ) ) != NULL && + RefreshCompound1( trk, to ) ) + return TRUE; + if ( refreshSpecialPG.win == NULL ) { + ParamRegister( &refreshSpecialPG ); + ParamCreateDialog( &refreshSpecialPG, MakeWindowTitle(_("Refresh Turnout/Structure")), _("Ok"), RefreshSpecialOk, RefreshSpecialCancel, TRUE, NULL, F_BLOCK|F_RESIZE|F_RECALLSIZE, NULL ); + } + ParamLoadMessage( &refreshSpecialPG, REFRESH_M1, _("Choose a Turnout/Structure to replace:") ); + ParamLoadMessage( &refreshSpecialPG, REFRESH_M2, "" ); + refreshSpecialInx = -1; + wListClear( (wList_p)refreshSpecialPLs[REFRESH_L].control ); + if ( GetTrkEndPtCnt(trk) > 0 ) + to = TurnoutAdd( listLabels, scale, (wList_p)refreshSpecialPLs[REFRESH_L].control, NULL, GetTrkEndPtCnt(trk) ); + else + to = StructAdd( listLabels, scale, (wList_p)refreshSpecialPLs[REFRESH_L].control, NULL ); + if ( to == NULL ) { + NoticeMessage( MSG_NO_TURNOUTS_AVAILABLE, _("Ok"), NULL, + GetTrkEndPtCnt(trk)>0 ? _("Turnouts") : _("Structures") ); + return FALSE; + } + FormatCompoundTitle( listLabels, xx->title ); + ParamLoadMessage( &refreshSpecialPG, REFRESH_S, message ); + while (1) { + wListSetIndex( (wList_p)refreshSpecialPLs[REFRESH_L].control, -1 ); + wShow( refreshSpecialPG.win ); + if ( refreshSpecialInx < 0 ) { + if ( refreshReturnVal ) { + DYNARR_APPEND( refreshSpecial_t, refreshSpecial_da, 10 ); + refreshSpecial(refreshSpecial_da.cnt-1).to = NULL; + refreshSpecial(refreshSpecial_da.cnt-1).name = MyStrdup( xx->title ); + } + return refreshReturnVal; + } + to = (turnoutInfo_t*)wListGetItemContext( (wList_p)refreshSpecialPLs[REFRESH_L].control, refreshSpecialInx ); + if ( to != NULL && + RefreshCompound1( trk, to ) ) { + DYNARR_APPEND( refreshSpecial_t, refreshSpecial_da, 10 ); + refreshSpecial(refreshSpecial_da.cnt-1).to = to; + refreshSpecial(refreshSpecial_da.cnt-1).name = MyStrdup( xx->title ); + if ( strcasecmp( xx->title, to->title ) != 0 ) { + MyFree( xx->title ); + xx->title = MyStrdup( to->title ); + } + return TRUE; + } + ParamLoadMessage( &refreshSpecialPG, REFRESH_M1, message ); + ParamLoadMessage( &refreshSpecialPG, REFRESH_M2, _("Choose another Turnout/Structure to replace:") ); + } +} + +/***************************************************************************** + * + * Custom Management Support + * + */ + +static char renameManuf[STR_SIZE]; +static char renameDesc[STR_SIZE]; +static char renamePartno[STR_SIZE]; +static turnoutInfo_t * renameTo; + +static paramData_t renamePLs[] = { +/*0*/ { PD_STRING, renameManuf, "manuf", PDO_NOPREF, (void*)350, N_("Manufacturer") }, +/*1*/ { PD_STRING, renameDesc, "desc", PDO_NOPREF, (void*)230, N_("Description") }, +/*2*/ { PD_STRING, renamePartno, "partno", PDO_NOPREF|PDO_DLGHORZ|PDO_DLGIGNORELABELWIDTH, (void*)100, N_("#") } }; +static paramGroup_t renamePG = { "rename", 0, renamePLs, sizeof renamePLs/sizeof renamePLs[0] }; + + +EXPORT BOOL_T CompoundCustomSave( + FILE * f ) +{ + int inx; + turnoutInfo_t * to; + BOOL_T rc = TRUE; + + for ( inx=0; inx<turnoutInfo_da.cnt; inx++ ) { + to = turnoutInfo(inx); + if (to->paramFileIndex == PARAM_CUSTOM && to->segCnt > 0) { + rc &= fprintf( f, "TURNOUT %s \"%s\"\n", GetScaleName(to->scaleInx), PutTitle(to->title) )>0; + if ( to->customInfo ) + rc &= fprintf( f, "\tU %s\n",to->customInfo )>0; + rc &= WriteCompoundPathsEndPtsSegs( f, to->paths, to->segCnt, to->segs, + to->endCnt, to->endPt ); + } + } + for ( inx=0; inx<structureInfo_da.cnt; inx++ ) { + to = structureInfo(inx); + if (to->paramFileIndex == PARAM_CUSTOM && to->segCnt > 0) { + rc &= fprintf( f, "STRUCTURE %s \"%s\"\n", GetScaleName(to->scaleInx), PutTitle(to->title) )>0; + if ( to->customInfo ) + rc &= fprintf( f, "\tU %s\n",to->customInfo )>0; + rc &= WriteSegs( f, to->segCnt, to->segs ); + } + } + return rc; +} + + +static void RenameOk( void * junk ) +{ + sprintf( message, "%s\t%s\t%s", renameManuf, renameDesc, renamePartno ); + if ( renameTo->title ) + MyFree( renameTo->title ); + renameTo->title = MyStrdup( message ); + wHide( renamePG.win ); + DoChangeNotification( CHANGE_PARAMS ); +} + + +static int CompoundCustMgmProc( + int cmd, + void * data ) +{ + turnoutInfo_t * to = (turnoutInfo_t*)data; + turnoutInfo_t * to2=NULL; + int inx; + char * mP, *pP, *nP; + int mL, pL, nL; + BOOL_T rc = TRUE; + + switch ( cmd ) { + case CUSTMGM_DO_COPYTO: + if ( to->segCnt <= 0 ) + return TRUE; + if ( to->endCnt ) { + rc &= fprintf( customMgmF, "TURNOUT %s \"%s\"\n", GetScaleName(to->scaleInx), PutTitle(to->title) )>0; + if ( to->customInfo ) + rc &= fprintf( customMgmF, "\tU %s\n",to->customInfo )>0; + rc &= WriteCompoundPathsEndPtsSegs( customMgmF, to->paths, to->segCnt, to->segs, + to->endCnt, to->endPt ); + } else { + rc &= fprintf( customMgmF, "STRUCTURE %s \"%s\"\n", GetScaleName(to->scaleInx), PutTitle(to->title) )>0; + if ( to->customInfo ) + rc &= fprintf( customMgmF, "\tU %s\n",to->customInfo )>0; + rc &= WriteSegs( customMgmF, to->segCnt, to->segs ); + } + return rc; + case CUSTMGM_CAN_EDIT: + return (to->endCnt != 0 && to->customInfo != NULL); + case CUSTMGM_DO_EDIT: + if ( to->endCnt == 0 || to->customInfo==NULL ) { + renameTo = to; + ParseCompoundTitle( to->title, &mP, &mL, &pP, &pL, &nP, &nL ); + strncpy( renameManuf, mP, mL ); renameManuf[mL] = 0; + strncpy( renameDesc, pP, pL ); renameDesc[pL] = 0; + strncpy( renamePartno, nP, nL ); renamePartno[nL] = 0; + if ( !renamePG.win ) { + ParamRegister( &renamePG ); + ParamCreateDialog( &renamePG, MakeWindowTitle(_("Rename Object")), _("Ok"), RenameOk, wHide, TRUE, NULL, F_BLOCK, NULL ); + } + ParamLoadControls( &renamePG ); + wShow( renamePG.win ); + } else { + for (inx=0; inx<turnoutInfo_da.cnt && to!=turnoutInfo(inx); inx++); + if ( inx > 0 && + turnoutInfo(inx-1)->customInfo && + strcmp( to->customInfo, turnoutInfo(inx-1)->customInfo ) == 0 ) { + to2 = to; + to = turnoutInfo(inx-1); + } else if ( inx < turnoutInfo_da.cnt-1 && + turnoutInfo(inx+1)->customInfo && + strcmp( to->customInfo, turnoutInfo(inx+1)->customInfo ) == 0 ) { + to2 = turnoutInfo(inx+1); + } + EditCustomTurnout( to, to2 ); + } + return TRUE; + case CUSTMGM_CAN_DELETE: + return TRUE; + case CUSTMGM_DO_DELETE: + to->segCnt = 0; + return TRUE; + case CUSTMGM_GET_TITLE: + ParseCompoundTitle( to->title, &mP, &mL, &pP, &pL, &nP, &nL ); + sprintf( message, "\t%.*s\t%s\t%.*s\t%.*s", mL, mP, GetScaleName(to->scaleInx), nL, nP, pL, pP ); + return TRUE; + } + return FALSE; +} + + +#include "bitmaps/turnout.xpm" +#include "bitmaps/struct.xpm" + +EXPORT void CompoundCustMgmLoad( void ) +{ + int inx; + turnoutInfo_t * to; + static wIcon_p turnoutI = NULL; + static wIcon_p structI = NULL; + + if ( turnoutI == NULL ) + turnoutI = wIconCreatePixMap( turnout_xpm ); + if ( structI == NULL ) + structI = wIconCreatePixMap( struct_xpm ); + + for ( inx=0; inx<turnoutInfo_da.cnt; inx++ ) { + to = turnoutInfo(inx); + if (to->paramFileIndex == PARAM_CUSTOM && to->segCnt > 0) { + CustMgmLoad( turnoutI, CompoundCustMgmProc, (void*)to ); + } + } + for ( inx=0; inx<structureInfo_da.cnt; inx++ ) { + to = structureInfo(inx); + if (to->paramFileIndex == PARAM_CUSTOM && to->segCnt > 0) { + CustMgmLoad( structI, CompoundCustMgmProc, (void*)to ); + } + } +} diff --git a/app/bin/dcustmgm.c b/app/bin/dcustmgm.c new file mode 100644 index 0000000..53d1f96 --- /dev/null +++ b/app/bin/dcustmgm.c @@ -0,0 +1,368 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dcustmgm.c,v 1.4 2009-07-30 16:58:42 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 "track.h" +#include <errno.h> +#include "i18n.h" + +#ifdef WINDOWS +#include <io.h> +#define F_OK (0) +#define W_OK (2) +#define access _access +#endif + +/***************************************************************************** + * + * Custom List Management + * + */ + +static void CustomEdit( void * action ); +static void CustomDelete( void * action ); +static void CustomExport( void * action ); +static void CustomDone( void * action ); +static wPos_t customListWidths[] = { 18, 100, 30, 80, 220 }; +static const char * customListTitles[] = { "", N_("Manufacturer"), + N_("Scale"), N_("Part No"), N_("Description") }; +static paramListData_t customListData = { 10, 400, 5, customListWidths, customListTitles }; +static paramData_t customPLs[] = { +#define I_CUSTOMLIST (0) +#define customSelL ((wList_p)customPLs[I_CUSTOMLIST].control) + { PD_LIST, NULL, "inx", PDO_DLGRESETMARGIN|PDO_DLGRESIZE, &customListData, NULL, BL_MANY }, +#define I_CUSTOMEDIT (1) + { PD_BUTTON, (void*)CustomEdit, "edit", PDO_DLGCMDBUTTON, NULL, N_("Edit") }, +#define I_CUSTOMDEL (2) + { PD_BUTTON, (void*)CustomDelete, "delete", 0, NULL, N_("Delete") }, +#define I_CUSTOMCOPYTO (3) + { PD_BUTTON, (void*)CustomExport, "export", 0, NULL, N_("Move To") }, +#define I_CUSTOMNEW (4) + { PD_MENU, NULL, "new", PDO_DLGWIDE, NULL, N_("New") }, + { PD_MENUITEM, (void*)CarDlgAddDesc, "new-part-mi", 0, NULL, N_("Car Part") }, + { PD_MENUITEM, (void*)CarDlgAddProto, "new-proto-mi", 0, NULL, N_("Car Prototype") } + } ; +static paramGroup_t customPG = { "custmgm", 0, customPLs, sizeof customPLs/sizeof customPLs[0] }; + + +typedef struct { + custMgmCallBack_p proc; + void * data; + wIcon_p icon; + } custMgmContext_t, *custMgmContext_p; + + +static void CustomDlgUpdate( + paramGroup_p pg, + int inx, + void *valueP ) +{ + custMgmContext_p context = NULL; + wIndex_t selcnt = wListGetSelectedCount( (wList_p)customPLs[0].control ); + wIndex_t linx, lcnt; + + if ( inx != I_CUSTOMLIST ) return; + if ( selcnt == 1 ) { + lcnt = wListGetCount( (wList_p)pg->paramPtr[inx].control ); + for ( linx=0; + linx<lcnt && wListGetItemSelected( (wList_p)customPLs[0].control, linx ) != TRUE; + linx++ ); + if ( linx < lcnt ) { + context = (custMgmContext_p)wListGetItemContext( (wList_p)pg->paramPtr[inx].control, linx ); + wButtonSetLabel( (wButton_p)customPLs[I_CUSTOMEDIT].control, context->proc( CUSTMGM_CAN_EDIT, context->data )?_("Edit"):_("Rename") ); + ParamControlActive( &customPG, I_CUSTOMEDIT, TRUE ); + } else { + ParamControlActive( &customPG, I_CUSTOMEDIT, FALSE ); + } + } else { + ParamControlActive( &customPG, I_CUSTOMEDIT, FALSE ); + } + ParamControlActive( &customPG, I_CUSTOMDEL, selcnt>0 ); + ParamControlActive( &customPG, I_CUSTOMCOPYTO, selcnt>0 ); +} + + +static void CustomEdit( void * action ) +{ + custMgmContext_p context = NULL; + wIndex_t selcnt = wListGetSelectedCount( (wList_p)customPLs[0].control ); + wIndex_t inx, cnt; + + if ( selcnt != 1 ) + return; + cnt = wListGetCount( (wList_p)customPLs[0].control ); + for ( inx=0; + inx<cnt && wListGetItemSelected( (wList_p)customPLs[0].control, inx ) != TRUE; + inx++ ); + if ( inx >= cnt ) + return; + context = (custMgmContext_p)wListGetItemContext( customSelL, inx ); + if ( context == NULL ) + return; + context->proc( CUSTMGM_DO_EDIT, context->data ); +#ifdef OBSOLETE + context->proc( CUSTMGM_GET_TITLE, context->data ); + wListSetValues( customSelL, inx, message, context->icon, context ); +#endif +} + + +static void CustomDelete( void * action ) +{ + wIndex_t selcnt = wListGetSelectedCount( (wList_p)customPLs[0].control ); + wIndex_t inx, cnt; + custMgmContext_p context = NULL; + + if ( selcnt <= 0 ) + return; + if ( (!NoticeMessage2( 1, MSG_CUSTMGM_DELETE_CONFIRM, _("Yes"), _("No"), selcnt ) ) ) + return; + cnt = wListGetCount( (wList_p)customPLs[0].control ); + for ( inx=0; inx<cnt; inx++ ) { + if ( !wListGetItemSelected( (wList_p)customPLs[0].control, inx ) ) + continue; + context = (custMgmContext_p)wListGetItemContext( customSelL, inx ); + context->proc( CUSTMGM_DO_DELETE, context->data ); + MyFree( context ); + wListDelete( customSelL, inx ); + inx--; + cnt--; + } + DoChangeNotification( CHANGE_PARAMS ); +} + +static struct wFilSel_t * customMgmExport_fs; +EXPORT FILE * customMgmF; +static char custMgmContentsStr[STR_SIZE]; +static BOOL_T custMgmProceed; +static paramData_t custMgmContentsPLs[] = { + { PD_STRING, custMgmContentsStr, "label", 0, (void*)400, N_("Label") } }; +static paramGroup_t custMgmContentsPG = { "contents", 0, custMgmContentsPLs, sizeof custMgmContentsPLs/sizeof custMgmContentsPLs[0] }; + +static void CustMgmContentsOk( void * junk ) +{ + custMgmProceed = TRUE; + wHide( custMgmContentsPG.win ); +} + + +static int CustomDoExport( + const char * pathName, + const char * fileName, + void * data ) +{ + int rc; + wIndex_t selcnt = wListGetSelectedCount( (wList_p)customPLs[0].control ); + wIndex_t inx, cnt; + custMgmContext_p context = NULL; + char *oldLocale = NULL; + + if ( selcnt <= 0 ) + return FALSE; + + SetCurDir( pathName, fileName ); + rc = access( pathName, F_OK ); + if ( rc != -1 ) { + rc = access( pathName, W_OK ); + if ( rc == -1 ) { + NoticeMessage( MSG_CUSTMGM_CANT_WRITE, _("Ok"), NULL, pathName ); + return FALSE; + } + custMgmProceed = TRUE; + } else { + if ( custMgmContentsPG.win == NULL ) { + ParamCreateDialog( &custMgmContentsPG, MakeWindowTitle(_("Contents Label")), _("Ok"), CustMgmContentsOk, wHide, TRUE, NULL, F_BLOCK, NULL ); + } + custMgmProceed = FALSE; + wShow( custMgmContentsPG.win ); + } + if ( !custMgmProceed ) + return FALSE; + customMgmF = fopen( pathName, "a" ); + if ( customMgmF == NULL ) { + NoticeMessage( MSG_CUSTMGM_CANT_WRITE, _("Ok"), NULL, pathName ); + return FALSE; + } + + oldLocale = SaveLocale("C"); + + if ( rc == -1 ) + fprintf( customMgmF, "CONTENTS %s\n", custMgmContentsStr ); + + cnt = wListGetCount( (wList_p)customPLs[0].control ); + for ( inx=0; inx<cnt; inx++ ) { + if ( !wListGetItemSelected( (wList_p)customPLs[0].control, inx ) ) + continue; + context = (custMgmContext_p)wListGetItemContext( customSelL, inx ); + if ( context == NULL ) continue; + if (!context->proc( CUSTMGM_DO_COPYTO, context->data )) { + NoticeMessage( MSG_WRITE_FAILURE, _("Ok"), NULL, strerror(errno), pathName ); + fclose( customMgmF ); + RestoreLocale(oldLocale); + return FALSE; + } + context->proc( CUSTMGM_DO_DELETE, context->data ); + MyFree( context ); + wListDelete( customSelL, inx ); + inx--; + cnt--; + } + fclose( customMgmF ); + RestoreLocale(oldLocale); + LoadParamFile( pathName, fileName, NULL ); + DoChangeNotification( CHANGE_PARAMS ); + return TRUE; +} + + +static void CustomExport( void * junk ) +{ + if ( customMgmExport_fs == NULL ) + customMgmExport_fs = wFilSelCreate( mainW, FS_UPDATE, 0, _("Move To XTP"), + _("Parameter File|*.xtp"), CustomDoExport, NULL ); + wFilSelect( customMgmExport_fs, curDirName ); +} + + +static void CustomDone( void * action ) +{ + char *oldLocale = NULL; + FILE * f = OpenCustom("w"); + + if (f == NULL) { + wHide( customPG.win ); + return; + } + oldLocale = SaveLocale("C"); + CompoundCustomSave(f); + CarCustomSave(f); + fclose(f); + RestoreLocale(oldLocale); + wHide( customPG.win ); +} + + +EXPORT void CustMgmLoad( + wIcon_p icon, + custMgmCallBack_p proc, + void * data ) +{ + custMgmContext_p context; + context = MyMalloc( sizeof *context ); + context->proc = proc; + context->data = data; + context->icon = icon; + context->proc( CUSTMGM_GET_TITLE, context->data ); + wListAddValue( customSelL, message, icon, context ); +} + + +static void LoadCustomMgmList( void ) +{ + wIndex_t curInx, cnt=0; + long tempL; + custMgmContext_p context; + custMgmContext_t curContext; + + curInx = wListGetIndex( customSelL ); + curContext.proc = NULL; + curContext.data = NULL; + curContext.icon = NULL; + if ( curInx >= 0 ) { + context = (custMgmContext_p)wListGetItemContext( customSelL, curInx ); + if ( context != NULL ) + curContext = *context; + } + cnt = wListGetCount( customSelL ); + for ( curInx=0; curInx<cnt; curInx++ ) { + context = (custMgmContext_p)wListGetItemContext( customSelL, curInx ); + if ( context ) + MyFree( context ); + } + curInx = wListGetIndex( customSelL ); + wControlShow( (wControl_p)customSelL, FALSE ); + wListClear( customSelL ); + + CompoundCustMgmLoad(); + CarCustMgmLoad(); + +#ifdef LATER + curInx = 0; + cnt = wListGetCount( customSelL ); + if ( curContext.proc != NULL ) { + for ( curInx=0; curInx<cnt; curInx++ ) { + context = (custMgmContext_p)wListGetItemContext( customSelL, curInx ); + if ( context && + context->proc == curContext.proc && + context->data == curContext.data ) + break; + } + } + if ( curInx >= cnt ) + curInx = (cnt>0?0:-1); + + wListSetIndex( customSelL, curInx ); + tempL = curInx; +#endif + tempL = -1; + CustomDlgUpdate( &customPG, I_CUSTOMLIST, &tempL ); + wControlShow( (wControl_p)customSelL, TRUE ); +} + + +static void CustMgmChange( long changes ) +{ + if (changes) { + if (changed) { + changed = 1; + checkPtMark = 1; + } + } + if ((changes&CHANGE_PARAMS) == 0 || + customPG.win == NULL || !wWinIsVisible(customPG.win) ) + return; + + LoadCustomMgmList(); +} + + +static void DoCustomMgr( void * junk ) +{ + if (customPG.win == NULL) { + ParamCreateDialog( &customPG, MakeWindowTitle(_("Manage custom designed parts")), _("Done"), CustomDone, NULL, TRUE, NULL, F_RESIZE|F_RECALLSIZE|F_BLOCK, CustomDlgUpdate ); + } else { + wListClear( customSelL ); + } + + /*ParamLoadControls( &customPG );*/ + /*ParamGroupRecord( &customPG );*/ + LoadCustomMgmList(); + wShow( customPG.win ); +} + + +EXPORT addButtonCallBack_t CustomMgrInit( void ) +{ + ParamRegister( &customPG ); + ParamRegister( &custMgmContentsPG ); + RegisterChangeNotification( CustMgmChange ); + return &DoCustomMgr; +} diff --git a/app/bin/dease.c b/app/bin/dease.c new file mode 100644 index 0000000..9b07129 --- /dev/null +++ b/app/bin/dease.c @@ -0,0 +1,266 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dease.c,v 1.3 2008-03-06 19:35:08 m_fischer Exp $ + * + * Easement Button Hdlrs + * + */ + +/* 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 "track.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "cjoin.h" +#include "i18n.h" + +static wButton_p easementB; + +static DIST_T easeX = 0.0; + +static DIST_T Rvalues[3]; +static DIST_T Lvalues[3]; + +static DIST_T oldEasementVal; + +static wIcon_p enone_bm; +static wIcon_p esharp_bm; +static wIcon_p egtsharp_bm; +static wIcon_p enormal_bm; +static wIcon_p eltbroad_bm; +static wIcon_p ebroad_bm; +static wIcon_p egtbroad_bm; + +/**************************************** + * + * EASEMENTW + * + */ + +static wWin_p easementW; + +static void EasementSel( long ); +static void SetEasement( DIST_T, void * ); +static void EasementOk( void ); +static void EasementCancel( void ); + +static char *easementChoiceLabels[] = { N_("None"), N_("Sharp"), N_("Normal"), N_("Broad"), NULL }; +static paramFloatRange_t r0o5_2 = { 0.5, 2.0, 60 }; +static paramFloatRange_t r0_100 = { 0.0, 100.0, 60 }; +static paramFloatRange_t r0_10 = { 0.0, 10.0, 60 }; +static long easeM; +static paramData_t easementPLs[] = { +#define I_EASEVAL (0) + { PD_FLOAT, &easementVal, "val", PDO_NOPSHUPD, &r0o5_2, N_("Value") }, + { PD_FLOAT, &easeR, "r", PDO_DIM|PDO_DLGRESETMARGIN, &r0_100, N_("R"), BO_READONLY }, + { PD_FLOAT, &easeX, "x", PDO_DIM|PDO_DLGHORZ, &r0_10, N_("X"), BO_READONLY }, + { PD_FLOAT, &easeL, "l", PDO_DIM|PDO_DLGHORZ, &r0_100, N_("L"), BO_READONLY }, +#define I_EASESEL (4) + { PD_RADIO, &easeM, "radio", PDO_DIM|PDO_NORECORD|PDO_NOPREF|PDO_DLGRESETMARGIN, easementChoiceLabels, NULL, BC_HORZ|BC_NONE } }; +static paramGroup_t easementPG = { "easement", PGO_RECORD, easementPLs, sizeof easementPLs/sizeof easementPLs[0] }; + + +static void SetEasement( + DIST_T val, + void * update ) +/* + * Set transition-curve parameters (R and L). + */ +{ + DIST_T z; + long selVal = -1; + wIcon_p bm; + + if (val == 0.0) { + easeX = easeR = easeL = 0.0; + selVal = 0; + bm = enone_bm; + } else if (val <= 1.0) { + z = 1.0/val - 1.0; + easeR = Rvalues[1] - z * (Rvalues[1] - Rvalues[0]); + easeL = Lvalues[1] - z * (Lvalues[1] - Lvalues[0]); + if (easeR != 0.0) + easeX = easeL*easeL/(24*easeR); + else + easeX = 0.0; + if (val == 1.0) { + selVal = 2; + bm = enormal_bm; + } else if (val == 0.5) { + selVal = 1; + bm = esharp_bm; + } else { + bm = egtsharp_bm; + } + } else { + z = val - 1.0; + easeR = Rvalues[1] + z * (Rvalues[2] - Rvalues[1]); + easeL = Lvalues[1] + z * (Lvalues[2] - Lvalues[1]); + if (easeR != 0.0) + easeX = easeL*easeL/(24*easeR); + else + easeX = 0.0; + if (val == 2.0) { + selVal = 3; + bm = ebroad_bm; + } else if (val < 2.0) { + bm = eltbroad_bm; + } else { + bm = egtbroad_bm; + } + } + + easeR = (floor(easeR*100.0))/100.0; + easementVal = val; + if (easementW && wWinIsVisible(easementW)) { + ParamLoadControls( &easementPG ); + if (update) { + easeM = selVal; + ParamLoadControl( &easementPG, I_EASESEL ); + } + } + /*ParamChange( &easeValPD );*/ + + if (easementB) + wButtonSetLabel( easementB, (char*)bm ); +} + + +static void EasementOk( void ) +{ + ParamLoadData( &easementPG ); + SetEasement( easementVal, (void*)FALSE ); + wHide( easementW ); +} + + +static void EasementCancel( void ) +{ + SetEasement( easementVal = oldEasementVal, (void*)FALSE ); + wHide( easementW ); +} + + +static void EasementSel( + long arg ) +/* + * Handle transition-curve parameter selection. + */ +{ + DIST_T val; + switch (arg) { + case 0: + val = 0; + break; + case 1: + val = 0.5; + break; + case 2: + val = 1.0; + break; + case 3: + val = 2.0; + break; + default: + AbortProg( "easementSel: bad value %ld", arg); + val = 0.0; + break; + } + SetEasement( val, (void*)FALSE ); +} + + +static void EasementDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + switch (inx) { + case I_EASEVAL: + SetEasement( *(FLOAT_T*)valueP, (void*)1 ); + break; + case I_EASESEL: + EasementSel( *(long*)valueP ); + break; + } +} + + +static void LayoutEasementW( + paramData_t * pd, + int inx, + wPos_t colX, + wPos_t * x, + wPos_t * y ) +{ + if ( inx == 2 ) + wControlSetPos( easementPLs[0].control, *x, wControlGetPosY(easementPLs[0].control) ); +} + + +static void DoEasement( void * junk ) +{ + if (easementW == NULL) { + easementW = ParamCreateDialog( &easementPG, MakeWindowTitle(_("Easement")), _("Ok"), (paramActionOkProc)EasementOk, (paramActionCancelProc)EasementCancel, TRUE, LayoutEasementW, 0, EasementDlgUpdate ); + SetEasement( easementVal, (void*)TRUE ); + } + oldEasementVal = easementVal; + wShow( easementW ); + SetEasement( easementVal, (void*)TRUE ); +} + + +static void EasementChange( long changes ) +/* + * Handle change of scale. Load new parameters. + */ +{ + if (changes&(CHANGE_SCALE|CHANGE_UNITS)) { + GetScaleEasementValues( Rvalues, Lvalues ); + SetEasement( easementVal, (void*)TRUE ); + } +} + + +#include "bitmaps/enone.xpm" +#include "bitmaps/esharp.xpm" +#include "bitmaps/egtsharp.xpm" +#include "bitmaps/enormal.xpm" +#include "bitmaps/eltbroad.xpm" +#include "bitmaps/ebroad.xpm" +#include "bitmaps/egtbroad.xpm" + + +EXPORT addButtonCallBack_t EasementInit( void ) +{ + ParamRegister( &easementPG ); + + enone_bm = wIconCreatePixMap( enone_xpm ); + esharp_bm = wIconCreatePixMap( esharp_xpm ); + egtsharp_bm = wIconCreatePixMap( egtsharp_xpm ); + enormal_bm = wIconCreatePixMap( enormal_xpm ); + eltbroad_bm = wIconCreatePixMap( eltbroad_xpm ); + ebroad_bm = wIconCreatePixMap( ebroad_xpm ); + egtbroad_bm = wIconCreatePixMap( egtbroad_xpm ); + easementB = AddToolbarButton( "cmdEasement", enone_bm, 0, (addButtonCallBack_t)DoEasementRedir, NULL ); + + RegisterChangeNotification( EasementChange ); + return &DoEasement; +} + diff --git a/app/bin/denum.c b/app/bin/denum.c new file mode 100644 index 0000000..de5200b --- /dev/null +++ b/app/bin/denum.c @@ -0,0 +1,240 @@ +/** \file denum.c + * Creating and showing the parts list. + */ + +/* 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 <time.h> +#include "track.h" +#include "i18n.h" + +/**************************************************************************** + * + * ENUMERATE + * + */ + + +static wWin_p enumW; + +#define ENUMOP_SAVE (1) +#define ENUMOP_PRINT (5) +#define ENUMOP_CLOSE (6) + +static void DoEnumOp( void * ); +static long enableListPrices; + +static paramTextData_t enumTextData = { 80, 24 }; +static char * priceLabels[] = { N_("Prices"), NULL }; +static paramData_t enumPLs[] = { +#define I_ENUMTEXT (0) +#define enumT ((wText_p)enumPLs[I_ENUMTEXT].control) + { PD_TEXT, NULL, "text", PDO_DLGRESIZE, &enumTextData, NULL, BT_CHARUNITS|BT_FIXEDFONT }, + { PD_BUTTON, (void*)DoEnumOp, "save", PDO_DLGCMDBUTTON, NULL, N_("Save As ..."), 0, (void*)ENUMOP_SAVE }, + { PD_BUTTON, (void*)DoEnumOp, "print", 0, NULL, N_("Print"), 0, (void*)ENUMOP_PRINT }, + { PD_BUTTON, (void*)wPrintSetup, "printsetup", 0, NULL, N_("Print Setup"), 0, NULL }, +#define I_ENUMLISTPRICE (4) + { PD_TOGGLE, &enableListPrices, "list-prices", PDO_DLGRESETMARGIN, priceLabels, NULL, BC_HORZ|BC_NOBORDER } }; +static paramGroup_t enumPG = { "enum", 0, enumPLs, sizeof enumPLs/sizeof enumPLs[0] }; + +static struct wFilSel_t * enumFile_fs; + + +static int count_utf8_chars(char *s) { + int i = 0, j = 0; + while (s[i]) { + if ((s[i] & 0xc0) != 0x80) j++; + i++; + } + return j; +} + +static int DoEnumSave( + const char * pathName, + const char * fileName, + void * data ) +{ + if (pathName == NULL) + return TRUE; + memcpy( curDirName, pathName, fileName-pathName ); + curDirName[fileName-pathName-1] = '\0'; + return wTextSave( enumT, pathName ); +} + + +static void DoEnumOp( + void * data ) +{ + switch( (int)(long)data ) { + case ENUMOP_SAVE: + wFilSelect( enumFile_fs, curDirName ); + break; + case ENUMOP_PRINT: + wTextPrint( enumT ); + break; + case ENUMOP_CLOSE: + wHide( enumW ); + ParamUpdate( &enumPG ); + } +} + + +static void EnumDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + if ( inx != I_ENUMLISTPRICE ) return; + EnumerateTracks(); +} + + +int enumerateMaxDescLen; +static FLOAT_T enumerateTotal; + +void EnumerateList( + long count, + FLOAT_T price, + char * desc ) +{ + char * cp; + int len; + sprintf( message, "%*ld | %s\n", count_utf8_chars(_("Count")), count, desc ); + if (enableListPrices) { + cp = message + strlen( message )-1; + len = enumerateMaxDescLen-strlen(desc); + if (len<0) len = 0; + memset( cp, ' ', len ); + cp += len; + if (price > 0.0) { + sprintf( cp, " | %7.2f |%9.2f\n", price, price*count ); + enumerateTotal += price*count; + } else { + sprintf( cp, " | %-*s |\n", (int) max( 7, count_utf8_chars( _("Each"))), " " ); + } + } + wTextAppend( enumT, message ); +} + +void EnumerateStart(void) +{ + time_t clock; + struct tm *tm; + char * cp; + + if (enumW == NULL) { + ParamRegister( &enumPG ); + enumW = ParamCreateDialog( &enumPG, MakeWindowTitle(_("Parts List")), NULL, NULL, wHide, TRUE, NULL, F_RESIZE, EnumDlgUpdate ); + enumFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Parts List"), sPartsListFilePattern, DoEnumSave, NULL ); + } + + wTextClear( enumT ); + + sprintf( message, _("%s Parts List\n\n"), sProdName); + wTextAppend( enumT, message ); + + message[0] = '\0'; + cp = message; + if ( Title1[0] ) { + strcpy( cp, Title1 ); + cp += strlen(cp); + *cp++ = '\n'; + } + if ( Title2[0] ) { + strcpy( cp, Title2 ); + cp += strlen(cp); + *cp++ = '\n'; + } + if ( cp > message ) { + *cp++ = '\n'; + *cp++ = '\0'; + wTextAppend( enumT, message ); + } + + time(&clock); + tm = localtime(&clock); + strftime( message, STR_LONG_SIZE, "%x\n", tm ); + wTextAppend( enumT, message ); + + enumerateTotal = 0.0; + + if( count_utf8_chars( _("Description")) > enumerateMaxDescLen ) + enumerateMaxDescLen = count_utf8_chars( _("Description" )); + + /* create the table header */ + sprintf( message, "%s | %-*s", _("Count"), enumerateMaxDescLen, _("Description")); + + if( enableListPrices ) + sprintf( message+strlen(message), " | %-*s | %-*s\n", (int) max( 7, count_utf8_chars( _("Each"))), _("Each"), (int) max( 9, count_utf8_chars(_("Extended"))), _("Extended")); + else + strcat( message, "\n" ); + wTextAppend( enumT, message ); + + /* underline the header */ + cp = message; + while( *cp && *cp != '\n' ) + if( *cp == '|' ) + *cp++ = '+'; + else + *cp++ = '-'; + + wTextAppend( enumT, message ); +} +/** + * End of parts list. Print the footer line and the totals if necessary. + * \todo These formatting instructions could be re-written in an easier + * to understand fashion using the possibilities of the printf formatting + * and some string functions. + */ + +void EnumerateEnd(void) +{ + int len; + char * cp; + ScaleLengthEnd(); + + memset( message, '\0', STR_LONG_SIZE ); + memset( message, '-', strlen(_("Count")) + 1 ); + strcpy( message + strlen(_("Count")) + 1, "+"); + cp = message+strlen(message); + memset( cp, '-', enumerateMaxDescLen+2 ); + if (enableListPrices){ + strcpy( cp+enumerateMaxDescLen+2, "+-" ); + memset( cp+enumerateMaxDescLen+4, '-', max( 7, strlen( _("Each")))); + strcat( cp, "-+-"); + memset( message+strlen( message ), '-', max( 9, strlen(_("Extended")))); + *(message + strlen( message )) = '\n'; + } else { + *(cp+enumerateMaxDescLen+2) = '\n'; + *(cp+enumerateMaxDescLen+3) = '\0'; + } + wTextAppend( enumT, message ); + + if (enableListPrices) { + len = strlen( message ) - strlen( _("Total")) - max( 9, strlen(_("Extended"))) - 4 ; + memset ( message, ' ', len ); + cp = message+len; + sprintf( cp, ("%s |%9.2f\n"), _("Total"), enumerateTotal ); + wTextAppend( enumT, message ); + } + wTextSetPosition( enumT, 0 ); + + ParamLoadControls( &enumPG ); + wShow( enumW ); +} diff --git a/app/bin/dlayer.c b/app/bin/dlayer.c new file mode 100644 index 0000000..17d787c --- /dev/null +++ b/app/bin/dlayer.c @@ -0,0 +1,978 @@ +/** \file dlayer.c + * Functions and dialogs for handling layers. + * + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dlayer.c,v 1.9 2009-06-15 19:29:57 m_fischer 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 <assert.h> + +#include "track.h" +#include "i18n.h" + +#include <stdint.h> + + +/***************************************************************************** + * + * LAYERS + * + */ + +#define NUM_BUTTONS (20) +#define LAYERPREF_FROZEN (1) +#define LAYERPREF_ONMAP (2) +#define LAYERPREF_VISIBLE (4) +#define LAYERPREF_SECTION ("Layers") +#define LAYERPREF_NAME "name" +#define LAYERPREF_COLOR "color" +#define LAYERPREF_FLAGS "flags" + +EXPORT LAYER_T curLayer; +EXPORT long layerCount = 10; +static long newLayerCount = 10; +static LAYER_T layerCurrent = NUM_LAYERS; + + +static BOOL_T layoutLayerChanged = FALSE; + +static wIcon_p show_layer_bmps[NUM_BUTTONS]; +/*static wIcon_p hide_layer_bmps[NUM_BUTTONS]; */ +static wButton_p layer_btns[NUM_BUTTONS]; /**< layer buttons on toolbar */ + +/** Layer selector on toolbar */ +static wList_p setLayerL; + +/*static wMessage_p layerNumM;*/ +/** Describe the properties of a layer */ +typedef struct { + char name[STR_SHORT_SIZE]; /**< Layer name */ + wDrawColor color; /**< layer color, is an index into a color table */ + BOOL_T frozen; /**< Frozen flag */ + BOOL_T visible; /**< visible flag */ + BOOL_T onMap; /**< is layer shown map */ + long objCount; /**< number of objects on layer */ + } layer_t; + +static layer_t layers[NUM_LAYERS]; +static layer_t *layers_save = NULL; + + +static int oldColorMap[][3] = { + { 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 */ + }; + +static void DoLayerOp( void * data ); +static void UpdateLayerDlg(void); +/* static void LoadLayerLists(); */ +static void LayerSetCounts(); +static void InitializeLayers( void LayerInitFunc( void ), int newCurrLayer ); +static void LayerPrefSave( void ); +static void LayerPrefLoad( void ); + +EXPORT BOOL_T GetLayerVisible( LAYER_T layer ) +{ + if (layer < 0 || layer >= NUM_LAYERS) + return TRUE; + else + return layers[(int)layer].visible; +} + + +EXPORT BOOL_T GetLayerFrozen( LAYER_T layer ) +{ + if (layer < 0 || layer >= NUM_LAYERS) + return TRUE; + else + return layers[(int)layer].frozen; +} + + +EXPORT BOOL_T GetLayerOnMap( LAYER_T layer ) +{ + if (layer < 0 || layer >= NUM_LAYERS) + return TRUE; + else + return layers[(int)layer].onMap; +} + + +EXPORT char * GetLayerName( LAYER_T layer ) +{ + if (layer < 0 || layer >= NUM_LAYERS) + return NULL; + else + return layers[(int)layer].name; +} + + +EXPORT void NewLayer( void ) +{ +} + + +EXPORT wDrawColor GetLayerColor( LAYER_T layer ) +{ + return layers[(int)layer].color; +} + + +static void FlipLayer( void * arg ) +{ + LAYER_T l = (LAYER_T)(long)arg; + wBool_t visible; + if ( l < 0 || l >= NUM_LAYERS ) + return; + if ( l == curLayer && layers[(int)l].visible) { + wButtonSetBusy( layer_btns[(int)l], layers[(int)l].visible ); + NoticeMessage( MSG_LAYER_HIDE, _("Ok"), NULL ); + return; + } + RedrawLayer( l, FALSE ); + visible = !layers[(int)l].visible; + layers[(int)l].visible = visible; + if (l<NUM_BUTTONS) { + wButtonSetBusy( layer_btns[(int)l], visible != 0 ); + wButtonSetLabel( layer_btns[(int)l], (char *)show_layer_bmps[(int)l]); + } + RedrawLayer( l, TRUE ); +} + +static void SetCurrLayer( wIndex_t inx, const char * name, wIndex_t op, void * listContext, void * arg ) +{ + LAYER_T newLayer = (LAYER_T)(long)inx; + if (layers[(int)newLayer].frozen) { + NoticeMessage( MSG_LAYER_SEL_FROZEN, _("Ok"), NULL ); + wListSetIndex( setLayerL, curLayer ); + return; + } + curLayer = newLayer; + + if ( curLayer < 0 || curLayer >= NUM_LAYERS ) + curLayer = 0; + if ( !layers[(int)curLayer].visible ) + FlipLayer( (void*)(intptr_t)inx ); + if ( recordF ) + fprintf( recordF, "SETCURRLAYER %d\n", inx ); +} + +static void PlaybackCurrLayer( char * line ) +{ + wIndex_t layer; + layer = atoi(line); + wListSetIndex( setLayerL, layer ); + SetCurrLayer( layer, NULL, 0, NULL, NULL ); +} + +/** + * Change the color of a layer. + * + * \param inx IN layer to change + * \param color IN new color + */ + +static void SetLayerColor( int inx, wDrawColor color ) +{ + if ( color != layers[inx].color ) { + if (inx < NUM_BUTTONS) { + wIconSetColor( show_layer_bmps[inx], color ); + wButtonSetLabel( layer_btns[inx], (char*)show_layer_bmps[inx] ); + } + layers[inx].color = color; + layoutLayerChanged = TRUE; + } +} + + +#include "bitmaps/l1.xbm" +#include "bitmaps/l2.xbm" +#include "bitmaps/l3.xbm" +#include "bitmaps/l4.xbm" +#include "bitmaps/l5.xbm" +#include "bitmaps/l6.xbm" +#include "bitmaps/l7.xbm" +#include "bitmaps/l8.xbm" +#include "bitmaps/l9.xbm" +#include "bitmaps/l10.xbm" +#include "bitmaps/l11.xbm" +#include "bitmaps/l12.xbm" +#include "bitmaps/l13.xbm" +#include "bitmaps/l14.xbm" +#include "bitmaps/l15.xbm" +#include "bitmaps/l16.xbm" +#include "bitmaps/l17.xbm" +#include "bitmaps/l18.xbm" +#include "bitmaps/l19.xbm" +#include "bitmaps/l20.xbm" + +static char * show_layer_bits[NUM_BUTTONS] = { l1_bits, l2_bits, l3_bits, l4_bits, l5_bits, l6_bits, l7_bits, l8_bits, l9_bits, l10_bits, + l11_bits, l12_bits, l13_bits, l14_bits, l15_bits, l16_bits, l17_bits, l18_bits, l19_bits, l20_bits }; + +static EXPORT long layerRawColorTab[] = { + wRGB( 0, 0,255), /* blue */ + wRGB( 0, 0,128), /* dk blue */ + wRGB( 0,128, 0), /* dk green */ + wRGB(255,255, 0), /* yellow */ + wRGB( 0,255, 0), /* green */ + wRGB( 0,255,255), /* lt cyan */ + wRGB(128, 0, 0), /* brown */ + wRGB(128, 0,128), /* purple */ + wRGB(128,128, 0), /* green-brown */ + wRGB(255, 0,255)}; /* lt-purple */ +static EXPORT wDrawColor layerColorTab[COUNT(layerRawColorTab)]; + + +static wWin_p layerW; +static char layerName[STR_SHORT_SIZE]; +static wDrawColor layerColor; +static long layerVisible = TRUE; +static long layerFrozen = FALSE; +static long layerOnMap = TRUE; +static void LayerOk( void * ); +static BOOL_T layerRedrawMap = FALSE; + +#define ENUMLAYER_RELOAD (1) +#define ENUMLAYER_SAVE (2) +#define ENUMLAYER_CLEAR (3) + +static char *visibleLabels[] = { "", NULL }; +static char *frozenLabels[] = { "", NULL }; +static char *onMapLabels[] = { "", NULL }; +static paramIntegerRange_t i0_20 = { 0, NUM_BUTTONS }; + +static paramData_t layerPLs[] = { +#define I_LIST (0) + { PD_DROPLIST, NULL, "layer", PDO_LISTINDEX|PDO_DLGNOLABELALIGN, (void*)250 }, +#define I_NAME (1) + { PD_STRING, layerName, "name", PDO_NOPREF, (void*)(250-54), N_("Name") }, +#define I_COLOR (2) + { PD_COLORLIST, &layerColor, "color", PDO_NOPREF, NULL, N_("Color") }, +#define I_VIS (3) + { PD_TOGGLE, &layerVisible, "visible", PDO_NOPREF, visibleLabels, N_("Visible"), BC_HORZ|BC_NOBORDER }, +#define I_FRZ (4) + { PD_TOGGLE, &layerFrozen, "frozen", PDO_NOPREF|PDO_DLGHORZ, frozenLabels, N_("Frozen"), BC_HORZ|BC_NOBORDER }, +#define I_MAP (5) + { PD_TOGGLE, &layerOnMap, "onmap", PDO_NOPREF|PDO_DLGHORZ, onMapLabels, N_("On Map"), BC_HORZ|BC_NOBORDER }, +#define I_COUNT (6) + { PD_STRING, NULL, "object-count", PDO_NOPREF|PDO_DLGBOXEND, (void*)(80), N_("Count"), BO_READONLY }, + { PD_MESSAGE, N_("Personal Preferences"), NULL, PDO_DLGRESETMARGIN, (void *)180 }, + { PD_BUTTON, (void*)DoLayerOp, "reset", PDO_DLGRESETMARGIN, 0, N_("Load"), 0, (void *)ENUMLAYER_RELOAD }, + { PD_BUTTON, (void*)DoLayerOp, "save", PDO_DLGHORZ, 0, N_("Save"), 0, (void *)ENUMLAYER_SAVE }, + { PD_BUTTON, (void*)DoLayerOp, "clear", PDO_DLGHORZ | PDO_DLGBOXEND, 0, N_("Defaults"), 0, (void *)ENUMLAYER_CLEAR }, + { PD_LONG, &newLayerCount, "button-count", PDO_DLGBOXEND|PDO_DLGRESETMARGIN, &i0_20, N_("Number of Layer Buttons") }, +}; + +static paramGroup_t layerPG = { "layer", 0, layerPLs, sizeof layerPLs/sizeof layerPLs[0] }; + +#define layerL ((wList_p)layerPLs[I_LIST].control) + +/** + * Load the layer settings to hard coded system defaults + */ + +void +LayerSystemDefaults( void ) +{ + int inx; + + for ( inx=0;inx<NUM_LAYERS; inx++ ) { + strcpy( layers[inx].name, inx==0?_("Main"):"" ); + layers[inx].visible = TRUE; + layers[inx].frozen = FALSE; + layers[inx].onMap = TRUE; + layers[inx].objCount = 0; + SetLayerColor( inx, layerColorTab[inx%COUNT(layerColorTab)] ); + } +} + +/** + * Load the layer listboxes in Manage Layers and the Toolbar with up-to-date information. + */ + +EXPORT void LoadLayerLists( void ) +{ + int inx; + + /* clear both lists */ + wListClear(setLayerL); + if ( layerL ) + wListClear(layerL); + + /* add all layers to both lists */ + for ( inx=0; inx<NUM_LAYERS; inx++ ) { + + if ( layerL ) { + sprintf( message, "%2d %c %s", inx+1, layers[inx].objCount>0?'+':'-', layers[inx].name ); + wListAddValue( layerL, message, NULL, NULL ); + } + + sprintf( message, "%2d : %s", inx+1, layers[inx].name ); + wListAddValue( setLayerL, message, NULL, NULL ); + } + + /* set current layer to selected */ + wListSetIndex( setLayerL, curLayer ); + if ( layerL ) + wListSetIndex( layerL, curLayer ); +} + +/** + * Handle button presses for the layer dialog. For all button presses in the layer + * dialog, this function is called. The parameter identifies the button pressed and + * the operation is performed. + * + * \param[IN] data identifier for the button prerssed + * \return + */ + +static void DoLayerOp( void * data ) +{ + switch((long)data ) { + + case ENUMLAYER_CLEAR: + InitializeLayers( LayerSystemDefaults, -1 ); + break; + case ENUMLAYER_SAVE: + LayerPrefSave(); + break; + case ENUMLAYER_RELOAD: + LayerPrefLoad(); + break; + } + + UpdateLayerDlg(); + if( layoutLayerChanged ) { + MainProc( mainW, wResize_e, NULL ); + layoutLayerChanged = FALSE; + changed = TRUE; + SetWindowTitle(); + } +} + +/** + * Update all dialogs and dialog elements after changing layers preferences. Once the global array containing + * the settings for the labels has been changed, this function needs to be called to update all the user interface + * elements to the new settings. + */ + +static void +UpdateLayerDlg() +{ + int inx; + + /* update the globals for the layer dialog */ + layerVisible = layers[curLayer].visible; + layerFrozen = layers[curLayer].frozen; + layerOnMap = layers[curLayer].onMap; + layerColor = layers[curLayer].color; + strcpy( layerName, layers[curLayer].name ); + layerCurrent = curLayer; + + /* now re-load the layer list boxes */ + LoadLayerLists(); + + sprintf( message, "%ld", layers[curLayer].objCount ); + ParamLoadMessage( &layerPG, I_COUNT, message ); + + /* force update of the 'manage layers' dialogbox */ + if( layerL ) + ParamLoadControls( &layerPG ); + + /* finally show the layer buttons with ballon text */ + for( inx = 0; inx < NUM_BUTTONS; inx++ ) { + wButtonSetBusy( layer_btns[inx], layers[inx].visible != 0 ); + wControlSetBalloonText( (wControl_p)layer_btns[inx], (layers[inx].name[0] != '\0' ? layers[inx].name :_("Show/Hide Layer") )); + } +} + +/** + * Initialize the layer lists. + * + * \param IN pointer to function that actually initialize tha data structures + * \param IN current layer (0...NUM_LAYERS), (-1) for no change + */ + +static void +InitializeLayers( void LayerInitFunc( void ), int newCurrLayer ) +{ + /* reset the data structures to default valuses */ + LayerInitFunc(); + + /* count the objects on each layer */ + LayerSetCounts(); + + /* Switch the current layer when requested */ + if( newCurrLayer != -1 ) + { + curLayer = newCurrLayer; + } +} + +/** + * Save the customized layer information to preferences. + */ + +static void +LayerPrefSave( void ) +{ + int inx; + int flags; + char buffer[ 80 ]; + char layersSaved[ 3 * NUM_LAYERS ]; /* 0..99 plus separator */ + + /* FIXME: values for layers that are configured to default now should be overwritten in the settings */ + + layersSaved[ 0 ] = '\0'; + + for( inx = 0; inx < NUM_LAYERS; inx++ ) { + /* if a name is set that is not the default value or a color different from the default has been set, + information about the layer needs to be saved */ + if( (layers[inx].name[0] && inx != 0 ) || + layers[inx].frozen || (!layers[inx].onMap) || (!layers[inx].visible) || + layers[inx].color != layerColorTab[inx%COUNT(layerColorTab)]) + { + sprintf( buffer, LAYERPREF_NAME ".%0d", inx ); + wPrefSetString( LAYERPREF_SECTION, buffer, layers[inx].name ); + + sprintf( buffer, LAYERPREF_COLOR ".%0d", inx ); + wPrefSetInteger( LAYERPREF_SECTION, buffer, wDrawGetRGB(layers[inx].color)); + + flags = 0; + if( layers[inx].frozen ) + flags |= LAYERPREF_FROZEN; + if( layers[inx].onMap ) + flags |= LAYERPREF_ONMAP; + if( layers[inx].visible ) + flags |= LAYERPREF_VISIBLE; + + sprintf( buffer, LAYERPREF_FLAGS ".%0d", inx ); + wPrefSetInteger( LAYERPREF_SECTION, buffer, flags ); + + /* extend the list of layers that are set up via the preferences */ + if( layersSaved[ 0 ] ) + strcat( layersSaved, "," ); + + sprintf( layersSaved, "%s%d", layersSaved, inx ); + } + } + + wPrefSetString( LAYERPREF_SECTION, "layers", layersSaved ); +} + + +/** + * Load the settings for all layers from the preferences. + */ + +static void +LayerPrefLoad( void ) +{ + + int inx; + char layersSaved[ 3 * NUM_LAYERS ]; + char layerOption[ 20 ]; + const char *layerValue; + const char *prefString; + long rgb; + int color; + long flags; + + /* reset layer preferences to system default */ + LayerSystemDefaults(); + + prefString = wPrefGetString( LAYERPREF_SECTION, "layers" ); + if( prefString && prefString[ 0 ] ) { + strncpy( layersSaved, prefString, sizeof( layersSaved )); + prefString = strtok( layersSaved, "," ); + while( prefString ) { + inx = atoi( prefString ); + sprintf( layerOption, LAYERPREF_NAME ".%d", inx ); + layerValue = wPrefGetString( LAYERPREF_SECTION, layerOption ); + if( layerValue ) + strcpy( layers[inx].name, layerValue ); + else + *(layers[inx].name) = '\0'; + + /* get and set the color, using the system default color in case color is not available from prefs */ + sprintf( layerOption, LAYERPREF_COLOR ".%d", inx ); + wPrefGetInteger( LAYERPREF_SECTION, layerOption, &rgb, layerColorTab[inx%COUNT(layerColorTab)] ); + color = wDrawFindColor(rgb); + SetLayerColor( inx, color ); + + /* get and set the flags */ + sprintf( layerOption, LAYERPREF_FLAGS ".%d", inx ); + wPrefGetInteger( LAYERPREF_SECTION, layerOption, &flags, LAYERPREF_ONMAP | LAYERPREF_VISIBLE ); + + layers[inx].frozen = ((flags & LAYERPREF_FROZEN) != 0 ); + layers[inx].onMap = ((flags & LAYERPREF_ONMAP) != 0 ); + layers[inx].visible = (( flags & LAYERPREF_VISIBLE ) != 0 ); + + prefString = strtok( NULL, ","); + } + } +} + +/** + * Count the number of elements on a layer. + * NOTE: This function has been implemented but not actually been tested. As it might prove useful in the + * future I left it in place. So you have been warned! + * \param IN layer to count + * \return number of elements + */ +/* +static int LayerCount( int layer ) +{ + track_p trk; + int inx; + int count = 0; + + for( trk = NULL; TrackIterate(&trk); ) { + inx = GetTrkLayer( trk ); + if( inx == layer ) + count++; + } + + return count; +} +*/ + +/** + * Count the number of objects on each layer and store result in layers data structure. + */ + +EXPORT void LayerSetCounts( void ) +{ + int inx; + track_p trk; + for ( inx=0; inx<NUM_LAYERS; inx++ ) + layers[inx].objCount = 0; + for ( trk=NULL; TrackIterate(&trk); ) { + inx = GetTrkLayer(trk); + if ( inx >= 0 && inx < NUM_LAYERS ) + layers[inx].objCount++; + } +} + +/** + * Reset layer options to their default values. The default values are loaded + * from the preferences file. + */ + +EXPORT void +DefaultLayerProperties(void) +{ + InitializeLayers( LayerPrefLoad, 0 ); + + UpdateLayerDlg(); + if( layoutLayerChanged ) { + MainProc( mainW, wResize_e, NULL ); + layoutLayerChanged = FALSE; + } +} + +/** + * Update all UI elements after selecting a layer. + * + */ + +static void LayerUpdate( void ) +{ + BOOL_T redraw; + ParamLoadData( &layerPG ); + if (layerCurrent < 0 || layerCurrent >= NUM_LAYERS) + return; + if (layerCurrent == curLayer && layerFrozen) { + NoticeMessage( MSG_LAYER_FREEZE, _("Ok"), NULL ); + layerFrozen = FALSE; + ParamLoadControl( &layerPG, I_FRZ ); + } + if (layerCurrent == curLayer && !layerVisible) { + NoticeMessage( MSG_LAYER_HIDE, _("Ok"), NULL ); + layerVisible = TRUE; + ParamLoadControl( &layerPG, I_VIS ); + } + + if( strcmp( layers[(int)layerCurrent].name, layerName ) || + layerColor != layers[(int)layerCurrent].color || + layers[(int)layerCurrent].visible != (BOOL_T)layerVisible || + layers[(int)layerCurrent].frozen != (BOOL_T)layerFrozen || + layers[(int)layerCurrent].onMap != (BOOL_T)layerOnMap ) { + + changed = TRUE; + SetWindowTitle(); + } + + if ( layerL ) { + strncpy( layers[(int)layerCurrent].name, layerName, sizeof layers[(int)layerCurrent].name ); + sprintf( message, "%2d %c %s", (int)layerCurrent+1, layers[(int)layerCurrent].objCount>0?'+':'-', layers[(int)layerCurrent].name ); + wListSetValues( layerL, layerCurrent, message, NULL, NULL ); + } + + sprintf( message, "%2d : %s", (int)layerCurrent+1, layers[(int)layerCurrent].name ); + wListSetValues( setLayerL, layerCurrent, message, NULL, NULL ); + if (layerCurrent < NUM_BUTTONS) { + if (strlen(layers[(int)layerCurrent].name)>0) + wControlSetBalloonText( (wControl_p)layer_btns[(int)layerCurrent], layers[(int)layerCurrent].name ); + else + wControlSetBalloonText( (wControl_p)layer_btns[(int)layerCurrent], _("Show/Hide Layer") ); + } + redraw = ( layerColor != layers[(int)layerCurrent].color || + (BOOL_T)layerVisible != layers[(int)layerCurrent].visible ); + if ( (!layerRedrawMap) && redraw) + RedrawLayer( (LAYER_T)layerCurrent, FALSE ); + + SetLayerColor( layerCurrent, layerColor ); + + if (layerCurrent<NUM_BUTTONS && layers[(int)layerCurrent].visible!=(BOOL_T)layerVisible) { + wButtonSetBusy( layer_btns[(int)layerCurrent], layerVisible ); + } + layers[(int)layerCurrent].visible = (BOOL_T)layerVisible; + layers[(int)layerCurrent].frozen = (BOOL_T)layerFrozen; + layers[(int)layerCurrent].onMap = (BOOL_T)layerOnMap; + if ( layerRedrawMap ) + DoRedraw(); + else if (redraw) + RedrawLayer( (LAYER_T)layerCurrent, TRUE ); + layerRedrawMap = FALSE; +} + + +static void LayerSelect( + wIndex_t inx ) +{ + LayerUpdate(); + if (inx < 0 || inx >= NUM_LAYERS) + return; + layerCurrent = (LAYER_T)inx; + strcpy( layerName, layers[inx].name ); + layerVisible = layers[inx].visible; + layerFrozen = layers[inx].frozen; + layerOnMap = layers[inx].onMap; + layerColor = layers[inx].color; + sprintf( message, "%ld", layers[inx].objCount ); + + ParamLoadMessage( &layerPG, I_COUNT, message ); + ParamLoadControls( &layerPG ); +} + +EXPORT void ResetLayers( void ) +{ + int inx; + for ( inx=0;inx<NUM_LAYERS; inx++ ) { + strcpy( layers[inx].name, inx==0?_("Main"):"" ); + layers[inx].visible = TRUE; + layers[inx].frozen = FALSE; + layers[inx].onMap = TRUE; + layers[inx].objCount = 0; + SetLayerColor( inx, layerColorTab[inx%COUNT(layerColorTab)] ); + if ( inx<NUM_BUTTONS ) { + wButtonSetLabel( layer_btns[inx], (char*)show_layer_bmps[inx] ); + } + } + wControlSetBalloonText( (wControl_p)layer_btns[0], _("Main") ); + for ( inx=1; inx<NUM_BUTTONS; inx++ ) { + wControlSetBalloonText( (wControl_p)layer_btns[inx], _("Show/Hide Layer") ); + } + curLayer = 0; + layerVisible = TRUE; + layerFrozen = FALSE; + layerOnMap = TRUE; + layerColor = layers[0].color; + strcpy( layerName, layers[0].name ); + LoadLayerLists(); + + if (layerL) { + ParamLoadControls( &layerPG ); + ParamLoadMessage( &layerPG, I_COUNT, "0" ); + } +} + + +EXPORT void SaveLayers( void ) +{ + layers_save = malloc( NUM_LAYERS * sizeof( layer_t )); + assert( layers_save != NULL ); + + memcpy( layers_save, layers, NUM_LAYERS * sizeof layers[0] ); + ResetLayers(); +} + +EXPORT void RestoreLayers( void ) +{ + int inx; + char * label; + wDrawColor color; + + assert( layers_save != NULL ); + memcpy( layers, layers_save, NUM_LAYERS * sizeof layers[0] ); + free( layers_save ); + + for ( inx=0; inx<NUM_BUTTONS; inx++ ) { + color = layers[inx].color; + layers[inx].color = -1; + SetLayerColor( inx, color ); + if ( layers[inx].name[0] == '\0' ) { + if ( inx == 0 ) { + label = _("Main"); + } else { + label = _("Show/Hide Layer"); + } + } else { + label = layers[inx].name; + } + wControlSetBalloonText( (wControl_p)layer_btns[inx], label ); + } + if (layerL) { + ParamLoadControls( &layerPG ); + ParamLoadMessage( &layerPG, I_COUNT, "0" ); + } + LoadLayerLists(); +} + +/** + * This function is called when the Done button on the layer dialog is pressed. It hides the layer dialog and + * updates the layer information. + * + * \param IN ignored + * + */ + +static void LayerOk( void * junk ) +{ + LayerSelect( layerCurrent ); + + if (newLayerCount != layerCount) { + layoutLayerChanged = TRUE; + if ( newLayerCount > NUM_BUTTONS ) + newLayerCount = NUM_BUTTONS; + layerCount = newLayerCount; + } + if (layoutLayerChanged) + MainProc( mainW, wResize_e, NULL ); + wHide( layerW ); +} + + +static void LayerDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + switch (inx) { + case I_LIST: + LayerSelect( (wIndex_t)*(long*)valueP ); + break; + case I_NAME: + LayerUpdate(); + break; + case I_MAP: + layerRedrawMap = TRUE; + break; + } +} + + +static void DoLayer( void * junk ) +{ + if (layerW == NULL) + layerW = ParamCreateDialog( &layerPG, MakeWindowTitle(_("Layers")), _("Done"), LayerOk, NULL, TRUE, NULL, 0, LayerDlgUpdate ); + + /* set the globals to the values for the current layer */ + UpdateLayerDlg(); + + layerRedrawMap = FALSE; + wShow( layerW ); + + layoutLayerChanged = FALSE; +} + + +EXPORT BOOL_T ReadLayers( char * line ) +{ + char * name; + int inx, visible, frozen, color, onMap; + long rgb; + + /* older files didn't support layers */ + + if (paramVersion < 7) + return TRUE; + + /* set the current layer */ + + if ( strncmp( line, "CURRENT", 7 ) == 0 ) { + curLayer = atoi( line+7 ); + if ( curLayer < 0 ) + curLayer = 0; + + if (layerL) + wListSetIndex( layerL, curLayer ); + if (setLayerL) + wListSetIndex( setLayerL, curLayer ); + + return TRUE; + } + + /* get the properties for a layer from the file and update the layer accordingly */ + + if (!GetArgs( line, "ddddl0000q", &inx, &visible, &frozen, &onMap, &rgb, &name )) + return FALSE; + if (paramVersion < 9) { + if ( rgb >= 0 && (int)rgb < sizeof oldColorMap/sizeof oldColorMap[0] ) + rgb = wRGB( oldColorMap[(int)rgb][0], oldColorMap[(int)rgb][1], oldColorMap[(int)rgb][2] ); + else + rgb = 0; + } + if (inx < 0 || inx >= NUM_LAYERS) + return FALSE; + color = wDrawFindColor(rgb); + SetLayerColor( inx, color ); + strncpy( layers[inx].name, name, sizeof layers[inx].name ); + layers[inx].visible = visible; + layers[inx].frozen = frozen; + layers[inx].onMap = onMap; + layers[inx].color = color; + if (inx<NUM_BUTTONS) { + if (strlen(name) > 0) { + wControlSetBalloonText( (wControl_p)layer_btns[(int)inx], layers[inx].name ); + } + wButtonSetBusy( layer_btns[(int)inx], visible ); + } + return TRUE; +} + + +EXPORT BOOL_T WriteLayers( FILE * f ) +{ + int inx; + BOOL_T rc = TRUE; + for (inx=0; inx<NUM_LAYERS; inx++) + if ((!layers[inx].visible) || layers[inx].frozen || (!layers[inx].onMap) || + layers[inx].color!=layerColorTab[inx%(COUNT(layerColorTab))] || + layers[inx].name[0] ) + rc &= fprintf( f, "LAYERS %d %d %d %d %ld %d %d %d %d \"%s\"\n", inx, layers[inx].visible, layers[inx].frozen, layers[inx].onMap, wDrawGetRGB(layers[inx].color), 0, 0, 0, 0, PutTitle(layers[inx].name) )>0; + rc &= fprintf( f, "LAYERS CURRENT %d\n", curLayer )>0; + return TRUE; +} + + +EXPORT void InitLayers( void ) +{ + int i; + + wPrefGetInteger( PREFSECT, "layer-button-count", &layerCount, layerCount ); + for ( i = 0; i<COUNT(layerRawColorTab); i++ ) + layerColorTab[i] = wDrawFindColor( layerRawColorTab[i] ); + + /* create the bitmaps for the layer buttons */ + for ( i = 0; i<NUM_BUTTONS; i++ ) { + show_layer_bmps[i] = wIconCreateBitMap( l1_width, l1_height, show_layer_bits[i], layerColorTab[i%(COUNT(layerColorTab))] ); + layers[i].color = layerColorTab[i%(COUNT(layerColorTab))]; + } + + /* layer list for toolbar */ + setLayerL = wDropListCreate( mainW, 0, 0, "cmdLayerSet", NULL, 0, 10, 200, NULL, SetCurrLayer, NULL ); + wControlSetBalloonText( (wControl_p)setLayerL, GetBalloonHelpStr("cmdLayerSet") ); + AddToolbarControl( (wControl_p)setLayerL, IC_MODETRAIN_TOO ); + + for ( i = 0; i<NUM_LAYERS; i++ ) { + if (i<NUM_BUTTONS) { + /* create the layer button */ + sprintf( message, "cmdLayerShow%d", i ); + layer_btns[i] = wButtonCreate( mainW, 0, 0, message, + (char*)(show_layer_bmps[i]), + BO_ICON, 0, (wButtonCallBack_p)FlipLayer, (void*)(intptr_t)i ); + + /* add the help text */ + wControlSetBalloonText( (wControl_p)layer_btns[i], _("Show/Hide Layer") ); + + /* put on toolbar */ + AddToolbarControl( (wControl_p)layer_btns[i], IC_MODETRAIN_TOO ); + + /* set state of button */ + wButtonSetBusy( layer_btns[i], 1 ); + } + sprintf( message, "%2d : %s", i+1, (i==0?_("Main"):"") ); + wListAddValue( setLayerL, message, NULL, (void*)(intptr_t)i ); + } + AddPlaybackProc( "SETCURRLAYER", PlaybackCurrLayer, NULL ); + AddPlaybackProc( "LAYERS", (playbackProc_p)ReadLayers, NULL ); +} + + +EXPORT addButtonCallBack_t InitLayersDialog( void ) { + ParamRegister( &layerPG ); + return &DoLayer; +} diff --git a/app/bin/doption.c b/app/bin/doption.c new file mode 100644 index 0000000..9413b5e --- /dev/null +++ b/app/bin/doption.c @@ -0,0 +1,591 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/doption.c,v 1.8 2009-10-15 04:21: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 <ctype.h> +#include "track.h" +#include "ccurve.h" +#include "i18n.h" + +static paramIntegerRange_t i0_64 = { 0, 64 }; +static paramIntegerRange_t i1_64 = { 1, 64 }; +static paramIntegerRange_t i1_100 = { 1, 100 }; +static paramIntegerRange_t i1_256 = { 1, 256 }; +static paramIntegerRange_t i0_10000 = { 0, 10000 }; +static paramIntegerRange_t i1_1000 = { 1, 1000 }; +static paramIntegerRange_t i10_1000 = { 10, 1000 }; +static paramIntegerRange_t i10_100 = { 10, 100 }; +static paramFloatRange_t r0o1_1 = { 0.1, 1 }; +static paramFloatRange_t r1_10 = { 1, 10 }; +static paramFloatRange_t r1_1000 = { 1, 1000 }; +static paramFloatRange_t r1_10000 = { 1, 10000 }; +static paramFloatRange_t r0_90 = { 0, 90 }; +static paramFloatRange_t r0_180 = { 0, 180 }; +static paramFloatRange_t r1_9999999 = { 1, 9999999 }; + +static void UpdatePrefD( void ); + +EXPORT long enableBalloonHelp = 1; + +static long GetChanges( + paramGroup_p pg ) +{ + long changes; + long changed; + int inx; + for ( changed=ParamUpdate(pg),inx=0,changes=0; changed; changed>>=1,inx++ ) { + if ( changed&1 ) + changes |= (long)pg->paramPtr[inx].context; + } + return changes; +} + + +static void OptionDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + int quickMoveOld; + if ( inx < 0 ) return; + if ( pg->paramPtr[inx].valueP == &enableBalloonHelp ) { + wEnableBalloonHelp((wBool_t)*(long*)valueP); + } else if ( pg->paramPtr[inx].valueP == &quickMove ) { + quickMoveOld = (int)quickMove; + quickMove = *(long*)valueP; + UpdateQuickMove(NULL); + quickMove = quickMoveOld; + } else if ( pg->paramPtr[inx].valueP == &units ) { + UpdatePrefD(); + } +} + +static void OptionDlgCancel( + wWin_p win ) +{ + wEnableBalloonHelp( (int)enableBalloonHelp ); + UpdateQuickMove(NULL); + wHide( win ); +} + +/**************************************************************************** + * + * Layout Dialog + * + */ + +static wWin_p layoutW; +static coOrd newSize; + +static paramData_t layoutPLs[] = { + { PD_FLOAT, &newSize.x, "roomsizeX", PDO_NOPREF|PDO_DIM|PDO_NOPSHUPD|PDO_DRAW, &r1_9999999, N_("Room Width"), 0, (void*)(CHANGE_MAIN|CHANGE_MAP) }, + { PD_FLOAT, &newSize.y, "roomsizeY", PDO_NOPREF|PDO_DIM|PDO_NOPSHUPD|PDO_DRAW|PDO_DLGHORZ, &r1_9999999, N_(" Height"), 0, (void*)(CHANGE_MAIN|CHANGE_MAP) }, + { PD_STRING, &Title1, "title1", PDO_NOPSHUPD, NULL, N_("Layout Title") }, + { PD_STRING, &Title2, "title2", PDO_NOPSHUPD, NULL, N_("Subtitle") }, + { PD_DROPLIST, &curScaleDescInx, "scale", PDO_NOPREF|PDO_NOPSHUPD|PDO_NORECORD|PDO_NOUPDACT, (void *)120, N_("Scale"), 0, (void*)(CHANGE_SCALE) }, + { PD_DROPLIST, &curGaugeInx, "gauge", PDO_NOPREF |PDO_NOPSHUPD|PDO_NORECORD|PDO_NOUPDACT|PDO_DLGHORZ, (void *)120, N_(" Gauge"), 0, (void *)(CHANGE_SCALE) }, + { PD_FLOAT, &minTrackRadius, "mintrackradius", PDO_DIM|PDO_NOPSHUPD|PDO_NOPREF, &r1_10000, N_("Min Track Radius"), 0, (void*)(CHANGE_MAIN|CHANGE_LIMITS) }, + { PD_FLOAT, &maxTrackGrade, "maxtrackgrade", PDO_NOPSHUPD|PDO_DLGHORZ, &r0_90 , N_(" Max Track Grade"), 0, (void*)(CHANGE_MAIN) } + }; + + +static paramGroup_t layoutPG = { "layout", PGO_RECORD|PGO_PREFMISC, layoutPLs, sizeof layoutPLs/sizeof layoutPLs[0] }; + +static void LayoutDlgUpdate( paramGroup_p pg, int inx, void * valueP ); + + +static void LayoutOk( void * junk ) +{ + long changes; + char prefString[ 30 ]; + + changes = GetChanges( &layoutPG ); + + /* [mf Nov. 15, 2005] Get the gauge/scale settings */ + if (changes & CHANGE_SCALE) { + SetScaleGauge( curScaleDescInx, curGaugeInx ); + } + /* [mf Nov. 15, 2005] end */ + + if (changes & CHANGE_MAP) { + SetRoomSize( newSize ); + } + + wHide( layoutW ); + DoChangeNotification(changes); + + if( changes & CHANGE_LIMITS ) { + // now set the minimum track radius + sprintf( prefString, "minTrackRadius-%s", curScaleName ); + wPrefSetFloat( "misc", prefString, minTrackRadius ); + } +} + + +static void LayoutChange( long changes ) +{ + if (changes & (CHANGE_SCALE|CHANGE_UNITS)) + if (layoutW != NULL && wWinIsVisible(layoutW) ) + ParamLoadControls( &layoutPG ); +} + + +static void DoLayout( void * junk ) +{ + newSize = mapD.size; + if (layoutW == NULL) { + layoutW = ParamCreateDialog( &layoutPG, MakeWindowTitle(_("Layout Options")), _("Ok"), LayoutOk, wHide, TRUE, NULL, 0, LayoutDlgUpdate ); + LoadScaleList( (wList_p)layoutPLs[4].control ); + } + LoadGaugeList( (wList_p)layoutPLs[5].control, curScaleDescInx ); /* set correct gauge list here */ + ParamLoadControls( &layoutPG ); + wShow( layoutW ); +} + + + +EXPORT addButtonCallBack_t LayoutInit( void ) +{ + ParamRegister( &layoutPG ); + RegisterChangeNotification( LayoutChange ); + return &DoLayout; +} + +/* [mf Nov. 15, 2005] Catch changes done in the LayoutDialog */ +static void +LayoutDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + char prefString[ 100 ]; + char scaleDesc[ 100 ]; + + /* did the scale change ? */ + if( inx == 4 ) { + LoadGaugeList( (wList_p)layoutPLs[5].control, *((int *)valueP) ); + // set the first entry as default, usually the standard gauge for a scale + wListSetIndex( (wList_p)layoutPLs[5].control, 0 ); + + // get the minimum radius + // get the selected scale first + wListGetValues((wList_p)layoutPLs[4].control, scaleDesc, 99, NULL, NULL ); + // split of the name from the scale + strtok( scaleDesc, " " ); + + // now get the minimum track radius + sprintf( prefString, "minTrackRadius-%s", scaleDesc ); + wPrefGetFloat( "misc", prefString, &minTrackRadius, 0.0 ); + + // put the scale's minimum value into the dialog + wStringSetValue( (wString_p)layoutPLs[6].control, FormatDistance( minTrackRadius ) ); + } +} + +/* [mf Nov. 15, 2005] end */ + +/**************************************************************************** + * + * Display Dialog + * + */ + +static wWin_p displayW; + +static char * autoPanLabels[] = { N_("Auto Pan"), NULL }; +static char * drawTunnelLabels[] = { N_("Hide"), N_("Dash"), N_("Normal"), NULL }; +static char * drawEndPtLabels3[] = { N_("None"), N_("Turnouts"), N_("All"), NULL }; +static char * tiedrawLabels[] = { N_("None"), N_("Outline"), N_("Solid"), NULL }; +static char * drawCenterCircle[] = { N_("Off"), N_("On"), NULL }; +static char * labelEnableLabels[] = { N_("Track Descriptions"), N_("Lengths"), N_("EndPt Elevations"), N_("Track Elevations"), N_("Cars"), NULL }; +static char * hotBarLabelsLabels[] = { N_("Part No"), N_("Descr"), NULL }; +static char * listLabelsLabels[] = { N_("Manuf"), N_("Part No"), N_("Descr"), NULL }; +static char * colorLayersLabels[] = { N_("Tracks"), N_("Other"), NULL }; +static char * liveMapLabels[] = { N_("Live Map"), NULL }; +static char * hideTrainsInTunnelsLabels[] = { N_("Hide Trains On Hidden Track"), NULL }; + +extern long trainPause; + +static paramData_t displayPLs[] = { + { PD_TOGGLE, &colorLayers, "color-layers", PDO_NOPSHUPD|PDO_DRAW, colorLayersLabels, N_("Color Layers"), BC_HORZ, (void*)(CHANGE_MAIN) }, + { PD_RADIO, &drawTunnel, "tunnels", PDO_NOPSHUPD|PDO_DRAW, drawTunnelLabels, N_("Draw Tunnel"), BC_HORZ, (void*)(CHANGE_MAIN) }, + { PD_RADIO, &drawEndPtV, "endpt", PDO_NOPSHUPD|PDO_DRAW, drawEndPtLabels3, N_("Draw EndPts"), BC_HORZ, (void*)(CHANGE_MAIN) }, + { PD_RADIO, &tieDrawMode, "tiedraw", PDO_NOPSHUPD|PDO_DRAW, tiedrawLabels, N_("Draw Ties"), BC_HORZ, (void*)(CHANGE_MAIN) }, + { PD_RADIO, ¢erDrawMode, "centerdraw", PDO_NOPSHUPD|PDO_DRAW, drawCenterCircle, N_("Draw Centers"), BC_HORZ, (void*)(CHANGE_MAIN | CHANGE_MAP) }, + { PD_LONG, &twoRailScale, "tworailscale", PDO_NOPSHUPD, &i1_64, N_("Two Rail Scale"), 0, (void*)(CHANGE_MAIN) }, + { PD_LONG, &mapScale, "mapscale", PDO_NOPSHUPD, &i1_256, N_("Map Scale"), 0, (void*)(CHANGE_MAP) }, + { PD_TOGGLE, &liveMap, "livemap", PDO_NOPSHUPD, liveMapLabels, "", BC_HORZ }, + { PD_TOGGLE, &autoPan, "autoPan", PDO_NOPSHUPD, autoPanLabels, "", BC_HORZ }, + { PD_TOGGLE, &labelEnable, "labelenable", PDO_NOPSHUPD, labelEnableLabels, N_("Label Enable"), 0, (void*)(CHANGE_MAIN) }, + { PD_LONG, &labelScale, "labelscale", PDO_NOPSHUPD, &i0_64, N_("Label Scale"), 0, (void*)(CHANGE_MAIN) }, + { PD_LONG, &descriptionFontSize, "description-fontsize", PDO_NOPSHUPD, &i1_1000, N_("Label Font Size"), 0, (void*)(CHANGE_MAIN) }, + { PD_TOGGLE, &hotBarLabels, "hotbarlabels", PDO_NOPSHUPD, hotBarLabelsLabels, N_("Hot Bar Labels"), BC_HORZ, (void*)(CHANGE_TOOLBAR) }, + { PD_TOGGLE, &layoutLabels, "layoutlabels", PDO_NOPSHUPD, listLabelsLabels, N_("Layout Labels"), BC_HORZ, (void*)(CHANGE_MAIN) }, + { PD_TOGGLE, &listLabels, "listlabels", PDO_NOPSHUPD, listLabelsLabels, N_("List Labels"), BC_HORZ, (void*)(CHANGE_PARAMS) }, +/* ATTENTION: update the define below if you add entries above */ +#define I_HOTBARLABELS (15) + { PD_DROPLIST, &carHotbarModeInx, "carhotbarlabels", PDO_NOPSHUPD|PDO_DLGUNDERCMDBUTT|PDO_LISTINDEX, (void*)250, N_("Car Labels"), 0, (void*)CHANGE_SCALE }, + { PD_LONG, &trainPause, "trainpause", PDO_NOPSHUPD, &i10_1000 , N_("Train Update Delay"), 0, 0 }, + { PD_TOGGLE, &hideTrainsInTunnels, "hideTrainsInTunnels", PDO_NOPSHUPD, hideTrainsInTunnelsLabels, "", BC_HORZ } + }; +static paramGroup_t displayPG = { "display", PGO_RECORD|PGO_PREFMISC, displayPLs, sizeof displayPLs/sizeof displayPLs[0] }; + + +static void DisplayOk( void * junk ) +{ + long changes; + changes = GetChanges( &displayPG ); + wHide( displayW ); + DoChangeNotification(changes); +} + + +#ifdef LATER +static void DisplayChange( long changes ) +{ + if (changes & (CHANGE_SCALE|CHANGE_UNITS)) + if (displayW != NULL && wWinIsVisible(displayW) ) + ParamLoadControls( &displayPG ); +} +#endif + + +static void DoDisplay( void * junk ) +{ + if (displayW == NULL) { + displayW = ParamCreateDialog( &displayPG, MakeWindowTitle(_("Display Options")), _("Ok"), DisplayOk, OptionDlgCancel, TRUE, NULL, 0, OptionDlgUpdate ); + wListAddValue( (wList_p)displayPLs[I_HOTBARLABELS].control, _("Proto"), NULL, (void*)0x0002 ); + wListAddValue( (wList_p)displayPLs[I_HOTBARLABELS].control, _("Proto/Manuf"), NULL, (void*)0x0012 ); + wListAddValue( (wList_p)displayPLs[I_HOTBARLABELS].control, _("Proto/Manuf/Part Number"), NULL, (void*)0x0312 ); + wListAddValue( (wList_p)displayPLs[I_HOTBARLABELS].control, _("Proto/Manuf/Partno/Item"), NULL, (void*)0x4312 ); + wListAddValue( (wList_p)displayPLs[I_HOTBARLABELS].control, _("Manuf/Proto"), NULL, (void*)0x0021 ); + wListAddValue( (wList_p)displayPLs[I_HOTBARLABELS].control, _("Manuf/Proto/Part Number"), NULL, (void*)0x0321 ); + wListAddValue( (wList_p)displayPLs[I_HOTBARLABELS].control, _("Manuf/Proto/Partno/Item"), NULL, (void*)0x4321 ); + } + ParamLoadControls( &displayPG ); + wShow( displayW ); +#ifdef LATER + DisplayChange( CHANGE_SCALE ); +#endif +} + + +EXPORT addButtonCallBack_t DisplayInit( void ) +{ + ParamRegister( &displayPG ); + wEnableBalloonHelp( (int)enableBalloonHelp ); +#ifdef LATER + RegisterChangeNotification( DisplayChange ); +#endif + return &DoDisplay; +} + +/**************************************************************************** + * + * Command Options Dialog + * + */ + +static wWin_p cmdoptW; + +static char * moveQlabels[] = { + N_("Normal"), + N_("Simple"), + N_("End-Points"), + NULL }; + +static char * preSelectLabels[] = { N_("Describe"), N_("Select"), NULL }; + +#ifdef HIDESELECTIONWINDOW +static char * hideSelectionWindowLabels[] = { N_("Hide"), NULL }; +#endif +static char * rightClickLabels[] = {N_("Normal: Command List, Shift: Command Options"), N_("Normal: Command Options, Shift: Command List"), NULL }; + +EXPORT paramData_t cmdoptPLs[] = { + { PD_RADIO, &quickMove, "move-quick", PDO_NOPSHUPD, moveQlabels, N_("Draw Moving Tracks"), BC_HORZ }, + { PD_RADIO, &preSelect, "preselect", PDO_NOPSHUPD, preSelectLabels, N_("Default Command"), BC_HORZ }, +#ifdef HIDESELECTIONWINDOW + { PD_TOGGLE, &hideSelectionWindow, PDO_NOPSHUPD, hideSelectionWindowLabels, N_("Hide Selection Window"), BC_HORZ }, +#endif + { PD_RADIO, &rightClickMode, "rightclickmode", PDO_NOPSHUPD, rightClickLabels, N_("Right Click"), 0 } + }; +static paramGroup_t cmdoptPG = { "cmdopt", PGO_RECORD|PGO_PREFMISC, cmdoptPLs, sizeof cmdoptPLs/sizeof cmdoptPLs[0] }; + +EXPORT paramData_p moveQuickPD = &cmdoptPLs[0]; + +static void CmdoptOk( void * junk ) +{ + long changes; + changes = GetChanges( &cmdoptPG ); + wHide( cmdoptW ); + DoChangeNotification(changes); +} + + +static void CmdoptChange( long changes ) +{ + if (changes & CHANGE_CMDOPT) + if (cmdoptW != NULL && wWinIsVisible(cmdoptW) ) + ParamLoadControls( &cmdoptPG ); +} + + +static void DoCmdopt( void * junk ) +{ + if (cmdoptW == NULL) { + cmdoptW = ParamCreateDialog( &cmdoptPG, MakeWindowTitle(_("Command Options")), _("Ok"), CmdoptOk, OptionDlgCancel, TRUE, NULL, 0, OptionDlgUpdate ); + } + ParamLoadControls( &cmdoptPG ); + wShow( cmdoptW ); +} + + +EXPORT addButtonCallBack_t CmdoptInit( void ) +{ + ParamRegister( &cmdoptPG ); + RegisterChangeNotification( CmdoptChange ); + return &DoCmdopt; +} + +/**************************************************************************** + * + * Preferences + * + */ + +static wWin_p prefW; +static long displayUnits; + +static wIndex_t distanceFormatInx; +static char * unitsLabels[] = { N_("English"), N_("Metric"), NULL }; +static char * angleSystemLabels[] = { N_("Polar"), N_("Cartesian"), NULL }; +static char * enableBalloonHelpLabels[] = { N_("Balloon Help"), NULL }; +static char * startOptions[] = { N_("Load Last Layout"), N_("Start New Layout"), NULL }; + +static paramData_t prefPLs[] = { + { PD_RADIO, &angleSystem, "anglesystem", PDO_NOPSHUPD, angleSystemLabels, N_("Angles"), BC_HORZ }, + { PD_RADIO, &units, "units", PDO_NOPSHUPD|PDO_NOUPDACT, unitsLabels, N_("Units"), BC_HORZ, (void*)(CHANGE_MAIN|CHANGE_UNITS) }, +#define I_DSTFMT (2) + { PD_DROPLIST, &distanceFormatInx, "dstfmt", PDO_NOPSHUPD|PDO_LISTINDEX, (void*)150, N_("Length Format"), 0, (void*)(CHANGE_MAIN|CHANGE_UNITS) }, + { PD_FLOAT, &minLength, "minlength", PDO_DIM|PDO_SMALLDIM|PDO_NOPSHUPD, &r0o1_1, N_("Min Track Length") }, + { PD_FLOAT, &connectDistance, "connectdistance", PDO_DIM|PDO_SMALLDIM|PDO_NOPSHUPD, &r0o1_1, N_("Connection Distance"), }, + { PD_FLOAT, &connectAngle, "connectangle", PDO_NOPSHUPD, &r1_10, N_("Connection Angle") }, + { PD_FLOAT, &turntableAngle, "turntable-angle", PDO_NOPSHUPD, &r0_180, N_("Turntable Angle") }, + { PD_LONG, &maxCouplingSpeed, "coupling-speed-max", PDO_NOPSHUPD, &i10_100, N_("Max Coupling Speed"), 0 }, + { PD_TOGGLE, &enableBalloonHelp, "balloonhelp", PDO_NOPSHUPD, enableBalloonHelpLabels, "", BC_HORZ }, + { PD_LONG, &dragPixels, "dragpixels", PDO_NOPSHUPD|PDO_DRAW, &r1_1000, N_("Drag Distance") }, + { PD_LONG, &dragTimeout, "dragtimeout", PDO_NOPSHUPD|PDO_DRAW, &i1_1000, N_("Drag Timeout") }, + { PD_LONG, &minGridSpacing, "mingridspacing", PDO_NOPSHUPD|PDO_DRAW, &i1_100, N_("Min Grid Spacing"), 0, 0 }, + { PD_LONG, &checkPtInterval, "checkpoint", PDO_NOPSHUPD|PDO_FILE, &i0_10000, N_("Check Point") }, + { PD_RADIO, &onStartup, "onstartup", PDO_NOPSHUPD, startOptions, N_("On Program Startup"), 0, NULL } + }; +static paramGroup_t prefPG = { "pref", PGO_RECORD|PGO_PREFMISC, prefPLs, sizeof prefPLs/sizeof prefPLs[0] }; + + +typedef struct { + char * name; + long fmt; + } dstFmts_t; +static dstFmts_t englishDstFmts[] = { + { N_("999.999"), DISTFMT_FMT_NONE|DISTFMT_FRACT_NUM|3 }, + { N_("999.99"), DISTFMT_FMT_NONE|DISTFMT_FRACT_NUM|2 }, + { N_("999.9"), DISTFMT_FMT_NONE|DISTFMT_FRACT_NUM|1 }, + { N_("999 7/8"), DISTFMT_FMT_NONE|DISTFMT_FRACT_FRC|3 }, + { N_("999 63/64"), DISTFMT_FMT_NONE|DISTFMT_FRACT_FRC|6 }, + { N_("999' 11.999\""), DISTFMT_FMT_SHRT|DISTFMT_FRACT_NUM|3 }, + { N_("999' 11.99\""), DISTFMT_FMT_SHRT|DISTFMT_FRACT_NUM|2 }, + { N_("999' 11.9\""), DISTFMT_FMT_SHRT|DISTFMT_FRACT_NUM|1 }, + { N_("999' 11 7/8\""), DISTFMT_FMT_SHRT|DISTFMT_FRACT_FRC|3 }, + { N_("999' 11 63/64\""), DISTFMT_FMT_SHRT|DISTFMT_FRACT_FRC|6 }, + { N_("999ft 11.999in"), DISTFMT_FMT_LONG|DISTFMT_FRACT_NUM|3 }, + { N_("999ft 11.99in"), DISTFMT_FMT_LONG|DISTFMT_FRACT_NUM|2 }, + { N_("999ft 11.9in"), DISTFMT_FMT_LONG|DISTFMT_FRACT_NUM|1 }, + { N_("999ft 11 7/8in"), DISTFMT_FMT_LONG|DISTFMT_FRACT_FRC|3 }, + { N_("999ft 11 63/64in"), DISTFMT_FMT_LONG|DISTFMT_FRACT_FRC|6 }, + { NULL, 0 } }; +static dstFmts_t metricDstFmts[] = { + { N_("999.999"), DISTFMT_FMT_NONE|DISTFMT_FRACT_NUM|3 }, + { N_("999.99"), DISTFMT_FMT_NONE|DISTFMT_FRACT_NUM|2 }, + { N_("999.9"), DISTFMT_FMT_NONE|DISTFMT_FRACT_NUM|1 }, + { N_("999.999mm"), DISTFMT_FMT_MM|DISTFMT_FRACT_NUM|3 }, + { N_("999.99mm"), DISTFMT_FMT_MM|DISTFMT_FRACT_NUM|2 }, + { N_("999.9mm"), DISTFMT_FMT_MM|DISTFMT_FRACT_NUM|1 }, + { N_("999.999cm"), DISTFMT_FMT_CM|DISTFMT_FRACT_NUM|3 }, + { N_("999.99cm"), DISTFMT_FMT_CM|DISTFMT_FRACT_NUM|2 }, + { N_("999.9cm"), DISTFMT_FMT_CM|DISTFMT_FRACT_NUM|1 }, + { N_("999.999m"), DISTFMT_FMT_M|DISTFMT_FRACT_NUM|3 }, + { N_("999.99m"), DISTFMT_FMT_M|DISTFMT_FRACT_NUM|2 }, + { N_("999.9m"), DISTFMT_FMT_M|DISTFMT_FRACT_NUM|1 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 } }; +static dstFmts_t *dstFmts[] = { englishDstFmts, metricDstFmts }; + + + +static void LoadDstFmtList( void ) +{ + int inx; + wListClear( (wList_p)prefPLs[I_DSTFMT].control ); + for ( inx=0; dstFmts[units][inx].name; inx++ ) + wListAddValue( (wList_p)prefPLs[I_DSTFMT].control, _(dstFmts[units][inx].name), NULL, (void*)dstFmts[units][inx].fmt ); +} + + +static void UpdatePrefD( void ) +{ + long newUnits, oldUnits; + int inx; + + if ( prefW==NULL || (!wWinIsVisible(prefW)) || prefPLs[1].control==NULL ) + return; + newUnits = wRadioGetValue( (wChoice_p)prefPLs[1].control ); + if ( newUnits == displayUnits ) + return; + oldUnits = units; + units = newUnits; + for ( inx = 0; inx<sizeof prefPLs/sizeof prefPLs[0]; inx++ ) { + if ( (prefPLs[inx].option&PDO_DIM) ) { + ParamLoadControl( &prefPG, inx ); +#ifdef LATER + val = wFloatGetValue( (wFloat_p)prefPLs[inx].control ); + if ( newUnits == UNITS_METRIC ) + val *= 2.54; + else + val /= 2.54; + wFloatSetValue( (wFloat_p)prefPLs[inx].control, val ); +#endif + } + } + LoadDstFmtList(); + units = oldUnits; + displayUnits = newUnits; +} + + +static void PrefOk( void * junk ) +{ + wBool_t resetValues = FALSE; + long changes; + changes = GetChanges( &prefPG ); + if (connectAngle < 1.0) { + connectAngle = 1.0; + resetValues = TRUE; + } + if (connectDistance < 0.1) { + connectDistance = 0.1; + resetValues = TRUE; + } + if (minLength < 0.1) { + minLength = 0.1; + resetValues = TRUE; + } + if ( resetValues ) { + NoticeMessage2( 0, MSG_CONN_PARAMS_TOO_SMALL, _("Ok"), NULL ) ; + } + wHide( prefW ); + DoChangeNotification(changes); +} + + + +static void DoPref( void * junk ) +{ + if (prefW == NULL) { + prefW = ParamCreateDialog( &prefPG, MakeWindowTitle(_("Preferences")), _("Ok"), PrefOk, wHide, TRUE, NULL, 0, OptionDlgUpdate ); + LoadDstFmtList(); + } + ParamLoadControls( &prefPG ); + displayUnits = units; + wShow( prefW ); +} + + +EXPORT addButtonCallBack_t PrefInit( void ) +{ + ParamRegister( &prefPG ); + if (connectAngle < 1.0) + connectAngle = 1.0; + if (connectDistance < 0.1) + connectDistance = 0.1; + if (minLength < 0.1) + minLength = 0.1; + return &DoPref; +} + + +EXPORT long GetDistanceFormat( void ) +{ + while ( dstFmts[units][distanceFormatInx].name == NULL ) + distanceFormatInx--; + return dstFmts[units][distanceFormatInx].fmt; +} + +/***************************************************************************** + * + * Color + * + */ + +static wWin_p colorW; + +static paramData_t colorPLs[] = { + { PD_COLORLIST, &snapGridColor, "snapgrid", PDO_NOPSHUPD, NULL, N_("Snap Grid"), 0, (void*)(CHANGE_GRID) }, + { PD_COLORLIST, &markerColor, "marker", PDO_NOPSHUPD, NULL, N_("Marker"), 0, (void*)(CHANGE_GRID) }, + { PD_COLORLIST, &borderColor, "border", PDO_NOPSHUPD, NULL, N_("Border"), 0, (void*)(CHANGE_MAIN) }, + { PD_COLORLIST, &crossMajorColor, "crossmajor", PDO_NOPSHUPD, NULL, N_("Primary Axis"), 0, 0 }, + { PD_COLORLIST, &crossMinorColor, "crossminor", PDO_NOPSHUPD, NULL, N_("Secondary Axis"), 0, 0 }, + { PD_COLORLIST, &normalColor, "normal", PDO_NOPSHUPD, NULL, N_("Normal Track"), 0, (void*)(CHANGE_MAIN|CHANGE_PARAMS) }, + { PD_COLORLIST, &selectedColor, "selected", PDO_NOPSHUPD, NULL, N_("Selected Track"), 0, (void*)(CHANGE_MAIN) }, + { PD_COLORLIST, &profilePathColor, "profile", PDO_NOPSHUPD, NULL, N_("Profile Path"), 0, (void*)(CHANGE_MAIN) }, + { PD_COLORLIST, &exceptionColor, "exception", PDO_NOPSHUPD, NULL, N_("Exception Track"), 0, (void*)(CHANGE_MAIN) }, + { PD_COLORLIST, &tieColor, "tie", PDO_NOPSHUPD, NULL, N_("Track Ties"), 0, (void*)(CHANGE_MAIN) } }; +static paramGroup_t colorPG = { "rgbcolor", PGO_RECORD|PGO_PREFGROUP, colorPLs, sizeof colorPLs/sizeof colorPLs[0] }; + + + +static void ColorOk( void * junk ) +{ + long changes; + changes = GetChanges( &colorPG ); + wHide( colorW ); + if ( (changes&CHANGE_GRID) && GridIsVisible() ) + changes |= CHANGE_MAIN; + DoChangeNotification( changes ); +} + + +static void DoColor( void * junk ) +{ + if (colorW == NULL) + colorW = ParamCreateDialog( &colorPG, MakeWindowTitle(_("Color")), _("Ok"), ColorOk, wHide, TRUE, NULL, 0, NULL ); + ParamLoadControls( &colorPG ); + wShow( colorW ); +} + + +EXPORT addButtonCallBack_t ColorInit( void ) +{ + ParamRegister( &colorPG ); + return &DoColor; +} + diff --git a/app/bin/dpricels.c b/app/bin/dpricels.c new file mode 100644 index 0000000..7e17121 --- /dev/null +++ b/app/bin/dpricels.c @@ -0,0 +1,165 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dpricels.c,v 1.2 2008-01-20 23:29:15 mni77 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 "track.h" +#include "compound.h" +#include "i18n.h" + +/***************************************************************************** + * + * Price List Dialog + * + */ + +static wWin_p priceListW; + +static turnoutInfo_t * priceListCurrent; + +static void PriceListOk( void * action ); +static void PriceListUpdate(); +DIST_T priceListCostV; +char priceListEntryV[STR_SIZE]; +DIST_T priceListFlexLengthV; +DIST_T priceListFlexCostV; + +static paramFloatRange_t priceListCostData = { 0.0, 9999.99, 80 }; +static wPos_t priceListColumnWidths[] = { -60, 200 }; +static const char * priceListColumnTitles[] = { N_("Price"), N_("Item") }; +static paramListData_t priceListListData = { 10, 400, 2, priceListColumnWidths, priceListColumnTitles }; +static paramFloatRange_t priceListFlexData = { 0.0, 999.99, 80 }; +static paramData_t priceListPLs[] = { +#define I_PRICELSCOST (0) +#define priceListCostF ((wFloat_p)priceListPLs[I_PRICELSCOST].control) + { PD_FLOAT, &priceListCostV, "cost", PDO_NOPREF|PDO_NOPSHUPD, &priceListCostData }, +#define I_PRICELSENTRY (1) +#define priceListEntryS ((wString_p)priceListPLs[I_PRICELSENTRY].control) + { PD_STRING, &priceListEntryV, "entry", PDO_NOPREF|PDO_NOPSHUPD|PDO_DLGHORZ, (void*)(400-80-3), NULL, BO_READONLY }, +#define I_PRICELSLIST (2) +#define priceListSelL ((wList_p)priceListPLs[I_PRICELSLIST].control) + { PD_LIST, NULL, "inx", PDO_NOPREF|PDO_NOPSHUPD, &priceListListData }, +#define I_PRICELSFLEXLEN (3) + { PD_FLOAT, &priceListFlexLengthV, "flexlen", PDO_NOPREF|PDO_NOPSHUPD|PDO_DIM|PDO_DLGRESETMARGIN, &priceListFlexData, N_("Flex Track") }, + { PD_MESSAGE, N_("costs"), NULL, PDO_DLGHORZ }, +#define I_PRICELSFLEXCOST (6) + { PD_FLOAT, &priceListFlexCostV, "flexcost", PDO_NOPREF|PDO_NOPSHUPD|PDO_DLGHORZ, &priceListFlexData } }; +static paramGroup_t priceListPG = { "pricelist", 0, priceListPLs, sizeof priceListPLs/sizeof priceListPLs[0] }; + + +static void PriceListUpdate() +{ + DIST_T oldPrice; + ParamLoadData( &priceListPG ); + if (priceListCurrent == NULL) + return; + FormatCompoundTitle( LABEL_MANUF|LABEL_DESCR|LABEL_PARTNO, priceListCurrent->title ); + wPrefGetFloat( "price list", message, &oldPrice, 0.0 ); + if (oldPrice == priceListCostV) + return; + wPrefSetFloat( "price list", message, priceListCostV ); + FormatCompoundTitle( listLabels|LABEL_COST, priceListCurrent->title ); + if (message[0] != '\0') + wListSetValues( priceListSelL, wListGetIndex(priceListSelL), message, NULL, priceListCurrent ); +} + + +static void PriceListOk( void * action ) +{ + PriceListUpdate(); + sprintf( message, "price list %s", curScaleName ); + wPrefSetFloat( message, "flex length", priceListFlexLengthV ); + wPrefSetFloat( message, "flex cost", priceListFlexCostV ); + wHide( priceListW ); +} + + +static void PriceListSel( + turnoutInfo_t * to ) +{ + FLOAT_T price; + PriceListUpdate(); + priceListCurrent = to; + if (priceListCurrent == NULL) + return; + FormatCompoundTitle( LABEL_MANUF|LABEL_DESCR|LABEL_PARTNO, priceListCurrent->title ); + wPrefGetFloat( "price list", message, &price, 0.00 ); + priceListCostV = price; + strcpy( priceListEntryV, message ); + ParamLoadControl( &priceListPG, I_PRICELSCOST ); + ParamLoadControl( &priceListPG, I_PRICELSENTRY ); +} + + +static void PriceListChange( long changes ) +{ + turnoutInfo_t * to1, * to2; + if ((changes & (CHANGE_SCALE|CHANGE_PARAMS)) == 0 || + priceListW == NULL || !wWinIsVisible( priceListW ) ) + return; + wListClear( priceListSelL ); + to1 = TurnoutAdd( listLabels|LABEL_COST, curScaleInx, priceListSelL, NULL, -1 ); + to2 = StructAdd( listLabels|LABEL_COST, curScaleInx, priceListSelL, NULL ); + if (to1 == NULL) + to1 = to2; + priceListCurrent = NULL; + if (to1) + PriceListSel( to1 ); + if ((changes & CHANGE_SCALE) == 0) + return; + sprintf( message, "price list %s", curScaleName ); + wPrefGetFloat( message, "flex length", &priceListFlexLengthV, 0.0 ); + wPrefGetFloat( message, "flex cost", &priceListFlexCostV, 0.0 ); + ParamLoadControls( &priceListPG ); +} + + +static void PriceListDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + turnoutInfo_t * to; + switch( inx ) { + case I_PRICELSCOST: + PriceListUpdate(); + break; + case I_PRICELSLIST: + to = (turnoutInfo_t*)wListGetItemContext( (wList_p)pg->paramPtr[inx].control, (wIndex_t)*(long*)valueP ); + PriceListSel( to ); + break; + } +} + + +static void DoPriceList( void * junk ) +{ + if (priceListW == NULL) + priceListW = ParamCreateDialog( &priceListPG, MakeWindowTitle(_("Price List")), _("Done"), PriceListOk, NULL, TRUE, NULL, 0, PriceListDlgUpdate ); + wShow( priceListW ); + PriceListChange( CHANGE_SCALE|CHANGE_PARAMS ); +} + + +EXPORT addButtonCallBack_t PriceListInit( void ) +{ + ParamRegister( &priceListPG ); + return &DoPriceList; +} diff --git a/app/bin/dprmfile.c b/app/bin/dprmfile.c new file mode 100644 index 0000000..5b22a01 --- /dev/null +++ b/app/bin/dprmfile.c @@ -0,0 +1,455 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dprmfile.c,v 1.3 2008-03-10 18:59:53 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 <time.h> +#include "track.h" +#include "i18n.h" + +#include <stdint.h> + +/**************************************************************************** + * + * Param File Management + * + */ + +typedef struct { + char * name; + char * contents; + int deleted; + int deletedShadow; + int valid; + } paramFileInfo_t; +typedef paramFileInfo_t * paramFileInfo_p; +static dynArr_t paramFileInfo_da; +#define paramFileInfo(N) DYNARR_N( paramFileInfo_t, paramFileInfo_da, N ) + +EXPORT int curParamFileIndex = PARAM_DEMO; +static char curParamDir[STR_LONG_SIZE]; +static struct wFilSel_t * paramFile_fs; + + +EXPORT wBool_t IsParamValid( + int fileInx ) +{ + if (fileInx == PARAM_DEMO) + return (curDemo>=0); + else if (fileInx == PARAM_CUSTOM) + return TRUE; + else if (fileInx == PARAM_LAYOUT) + return TRUE; + else if (fileInx >= 0 && fileInx < paramFileInfo_da.cnt) + return (!paramFileInfo(fileInx).deleted) && paramFileInfo(fileInx).valid; + else + return FALSE; +} + + +EXPORT char * GetParamFileName( + int fileInx ) +{ + return paramFileInfo(fileInx).contents; +} + + +static BOOL_T UpdateParamFiles( void ) +{ + char fileName[STR_LONG_SIZE], *fileNameP; + char * contents; + const char * cp; + FILE * updateF; + FILE * paramF; + long updateTime; + long lastTime; + + sprintf( message, "%s%sxtrkcad.upd", libDir, FILE_SEP_CHAR ); + updateF = fopen( message, "r" ); + if ( updateF == NULL ) + return FALSE; + if ( fgets( message, sizeof message, updateF ) == NULL ) { + NoticeMessage( "short file: xtrkcad.upd", _("Ok"), NULL ); + return FALSE; + } + wPrefGetInteger( "file", "updatetime", &lastTime, 0 ); + updateTime = atol( message ); + if ( lastTime >= updateTime ) + return FALSE; + sprintf( fileName, "%s%sparams%s", libDir, FILE_SEP_CHAR, FILE_SEP_CHAR ); + fileNameP = fileName+strlen(fileName); + while ( ( fgets( fileNameP, (fileName+sizeof fileName)-fileNameP, updateF ) ) != NULL ) { + Stripcr( fileNameP ); + InfoMessage( _("Updating %s"), fileNameP ); + paramF = fopen( fileName, "r" ); + if ( paramF == NULL ) { + NoticeMessage( MSG_PRMFIL_OPEN_NEW, _("Ok"), NULL, fileName ); + continue; + } + contents = NULL; + while ( ( fgets(message, sizeof message, paramF) ) != NULL ) { + if (strncmp( message, "CONTENTS", 8 ) == 0) { + Stripcr( message ); + contents = message+9; + break; + } + } + fclose( paramF ); + if (contents == NULL) { + NoticeMessage( MSG_PRMFIL_NO_CONTENTS, _("Ok"), NULL, fileName ); + continue; + } + cp = wPrefGetString( "Parameter File Map", contents ); + wPrefSetString( "Parameter File Map", contents, fileName ); + if (cp!=NULL && *cp!='\0') { + /* been there, done that */ + continue; + } + + DYNARR_APPEND( paramFileInfo_t, paramFileInfo_da, 10 ); + curParamFileIndex = paramFileInfo_da.cnt-1; + paramFileInfo(curParamFileIndex).name = MyStrdup( fileName ); + curContents = curSubContents = NULL; + paramFileInfo(curParamFileIndex).deleted = FALSE; + paramFileInfo(curParamFileIndex).valid = TRUE; + paramFileInfo(curParamFileIndex).deletedShadow = + paramFileInfo(curParamFileIndex).deleted = !ReadParams( 0, NULL, fileName ); + paramFileInfo(curParamFileIndex).contents = curContents; + } + wPrefSetInteger( "file", "updatetime", updateTime ); + return TRUE; +} + + +EXPORT void ReadParamFiles( void ) +{ + int fileNo; + const char *fileName; + const char * contents; + BOOL_T updated = FALSE; + + updated = UpdateParamFiles(); + + for ( fileNo=1; ; fileNo++ ) { + sprintf( message, "File%d", fileNo ); + contents = wPrefGetString( "Parameter File Names", message ); + if (contents==NULL || *contents=='\0') + break; + InfoMessage( "Parameters for %s", contents ); + fileName = wPrefGetString( "Parameter File Map", contents ); + if (fileName==NULL || *fileName=='\0') { + NoticeMessage( MSG_PRMFIL_NO_MAP, _("Ok"), NULL, contents ); + continue; + } + DYNARR_APPEND( paramFileInfo_t, paramFileInfo_da, 10 ); + curParamFileIndex = paramFileInfo_da.cnt-1; + paramFileInfo(curParamFileIndex).name = MyStrdup( fileName ); + curContents = NULL; + paramFileInfo(curParamFileIndex).deleted = FALSE; + paramFileInfo(curParamFileIndex).valid = TRUE; + paramFileInfo(curParamFileIndex).deletedShadow = + paramFileInfo(curParamFileIndex).deleted = !ReadParams( 0, NULL, fileName ); + if (curContents == NULL) + curContents = curSubContents = MyStrdup(contents); + paramFileInfo(curParamFileIndex).contents = curContents; + } + curParamFileIndex = PARAM_CUSTOM; + if (updated) { + RememberParamFiles(); + } +} + + +EXPORT void RememberParamFiles( void ) +{ + int fileInx; + int fileNo; + char * contents, *cp; + + for (fileInx=0, fileNo=1; fileInx<paramFileInfo_da.cnt; fileInx++ ) { + if (paramFileInfo(fileInx).valid && !paramFileInfo(fileInx).deleted) { + sprintf( message, "File%d", fileNo++ ); + contents = paramFileInfo(fileInx).contents; + for ( cp=contents; *cp; cp++ ) { + if ( *cp == '=' || *cp == '\'' || *cp == '"' || *cp == ':' || *cp == '.' ) + *cp = ' '; + } + wPrefSetString( "Parameter File Names", message, contents ); + } + } + sprintf( message, "File%d", fileNo++ ); + wPrefSetString( "Parameter File Names", message, "" ); +} + + + +/**************************************************************************** + * + * Param File Dialog + * + */ + +static wWin_p paramFileW; + +static long paramFileSel = 1; +static wIcon_p mtbox_bm; +static wIcon_p chkbox_bm; + +static void ParamFileAction( void * ); +static void ParamFileBrowse( void * ); + +static paramListData_t paramFileListData = { 10, 370 }; +static char * paramFileLabels[] = { N_("Show File Names"), NULL }; +static paramData_t paramFilePLs[] = { +#define I_PRMFILLIST (0) +#define paramFileL ((wList_p)paramFilePLs[I_PRMFILLIST].control) + { PD_LIST, NULL, "inx", 0, ¶mFileListData, NULL, BL_DUP|BL_SETSTAY }, +#define I_PRMFILTOGGLE (1) + { PD_TOGGLE, ¶mFileSel, "mode", 0, paramFileLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_PRMFILACTION (2) +#define paramFileActionB ((wButton_p)paramFilePLs[I_PRMFILACTION].control) + { PD_BUTTON, (void*)ParamFileAction, "action", PDO_DLGCMDBUTTON, NULL, N_("Unload") }, + { PD_BUTTON, (void*)ParamFileBrowse, "browse", 0, NULL, N_("Browse ...") } }; + +static paramGroup_t paramFilePG = { "prmfile", 0, paramFilePLs, sizeof paramFilePLs/sizeof paramFilePLs[0] }; + + +static void ParamFileLoadList( void ) +{ + int fileInx; + wIndex_t listInx; + wControlShow( (wControl_p)paramFileL, FALSE ); + listInx = wListGetIndex(paramFileL); + wListClear( paramFileL ); + for ( fileInx = 0; fileInx < paramFileInfo_da.cnt; fileInx++ ) { + if (paramFileInfo(fileInx).valid) { + strcpy( message, ((!paramFileSel) && paramFileInfo(fileInx).contents)? + paramFileInfo(fileInx).contents: + paramFileInfo(fileInx).name ); + wListAddValue( paramFileL, message, (paramFileInfo(fileInx).deleted)?mtbox_bm:chkbox_bm, (void*)(intptr_t)fileInx ); + } + } + wListSetIndex( paramFileL, listInx ); + wControlShow( (wControl_p)paramFileL, TRUE ); +} + + +EXPORT int LoadParamFile( + const char * pathName, + const char * fileName, + void * data ) +{ + char * cp; + wIndex_t inx; + wBool_t redrawList; + + if (pathName == NULL) + return TRUE; + memcpy( curParamDir, pathName, fileName-pathName ); + curParamDir[fileName-pathName] = '\0'; + wPrefSetString( "file", "paramdir", curParamDir ); + + redrawList = FALSE; + curContents = curSubContents = NULL; + curParamFileIndex = paramFileInfo_da.cnt; + if ( !ReadParams( 0, NULL, pathName ) ) + return FALSE; + if (curContents == NULL) { + curContents = curSubContents = MyStrdup( fileName ); + for ( cp=curContents; *cp; cp++ ) { + if ( *cp == '=' || *cp == '\'' || *cp == '"' || *cp == ':' || *cp == '.' ) + *cp = ' '; + } + } + + for ( inx=0; inx<paramFileInfo_da.cnt; inx++ ) { + if ( paramFileInfo(inx).valid && + strcmp( paramFileInfo(inx).contents, curContents ) == 0 ) { + paramFileInfo(inx).valid = FALSE; + redrawList = TRUE; + break; + } + } + + DYNARR_APPEND( paramFileInfo_t, paramFileInfo_da, 10 ); + paramFileInfo(curParamFileIndex).name = MyStrdup( pathName ); + paramFileInfo(curParamFileIndex).valid = TRUE; + paramFileInfo(curParamFileIndex).deleted = FALSE; + paramFileInfo(curParamFileIndex).deletedShadow = + paramFileInfo(curParamFileIndex).deleted = FALSE; + paramFileInfo(curParamFileIndex).contents = curContents; + + if ( paramFilePG.win ) { + if ( redrawList ) { + ParamFileLoadList(); + } else { + strcpy( message, ((!paramFileSel) && paramFileInfo(curParamFileIndex).contents)? + paramFileInfo(curParamFileIndex).contents: + paramFileInfo(curParamFileIndex).name ); + wListAddValue( paramFileL, message, chkbox_bm, (void*)(intptr_t)curParamFileIndex ); + wListSetIndex( paramFileL, wListGetCount(paramFileL)-1 ); + } + } + + wPrefSetString( "Parameter File Map", curContents, + paramFileInfo(curParamFileIndex).name ); + curParamFileIndex = PARAM_CUSTOM; + DoChangeNotification( CHANGE_PARAMS ); + return TRUE; +} + + +static void ParamFileBrowse( void * junk ) +{ + wFilSelect( paramFile_fs, curParamDir ); + return; +} + + +static void UpdateParamFileButton( + wIndex_t fileInx ) +{ + if (fileInx < 0 || fileInx >= paramFileInfo_da.cnt) + return; + wButtonSetLabel( paramFileActionB, + paramFileInfo(fileInx).deleted?_("Reload"):_("Unload") ); +} + + +static void ParamFileAction( void * junk ) +{ + wIndex_t listInx; + wIndex_t fileInx; + void * data; + listInx = wListGetValues( paramFileL, NULL, 0, NULL, &data ); + if (listInx<0) + return; + fileInx = (wIndex_t)(long)data; + paramFileInfo(fileInx).deleted = ! paramFileInfo(fileInx).deleted; +#ifndef LATER + strcpy( message, ((!paramFileSel) && paramFileInfo(fileInx).contents)? + paramFileInfo(fileInx).contents: + paramFileInfo(fileInx).name ); + wListSetValues( paramFileL, listInx, message, (paramFileInfo(fileInx).deleted)?mtbox_bm:chkbox_bm, (void*)(intptr_t)fileInx ); +#endif + DoChangeNotification( CHANGE_PARAMS ); + UpdateParamFileButton( fileInx ); +} + + +static void ParamFileOk( void * junk ) +{ + wIndex_t fileInx; + for ( fileInx = 0; fileInx < paramFileInfo_da.cnt; fileInx++ ) + paramFileInfo(fileInx).deletedShadow = paramFileInfo(fileInx).deleted; + wHide( paramFileW ); +} + + +static void ParamFileCancel( wWin_p junk ) +{ + wIndex_t fileInx; + for ( fileInx = 0; fileInx < paramFileInfo_da.cnt; fileInx++ ) + paramFileInfo(fileInx).deleted = paramFileInfo(fileInx).deletedShadow; + wHide( paramFileW ); + DoChangeNotification( CHANGE_PARAMS ); +} + + +static void ParamFilesChange( long changes ) +{ +#ifdef LATER + int fileInx; + wIndex_t listInx; + if ((changes&CHANGE_PARAMS) == 0 || + paramFileW == NULL || !wWinIsVisible(paramFileW) ) + return; + wControlShow( (wControl_p)paramFileL, FALSE ); + listInx = wListGetIndex(paramFileL); + wListClear( paramFileL ); + for ( fileInx = 0; fileInx < paramFileInfo_da.cnt; fileInx++ ) { + if (paramFileInfo(fileInx).valid) { + strcpy( message, ((!paramFileSel) && paramFileInfo(fileInx).contents)? + paramFileInfo(fileInx).contents: + paramFileInfo(fileInx).name ); + wListAddValue( paramFileL, message, (paramFileInfo(fileInx).deleted)?mtbox_bm:chkbox_bm, (void*)fileInx ); + } + } + wListSetIndex( paramFileL, listInx ); + wControlShow( (wControl_p)paramFileL, TRUE ); +#endif +} + + +static void ParamFileDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + switch (inx) { + case I_PRMFILLIST: + UpdateParamFileButton( (wIndex_t)(long)wListGetItemContext(paramFileL,wListGetIndex(paramFileL)) ); + break; + case I_PRMFILTOGGLE: + ParamFileLoadList(); + break; + } +} + + +#include "bitmaps/mtbox.xbm" +#include "bitmaps/chkbox.xbm" +static void DoParamFiles( void * junk ) +{ + wIndex_t listInx; + void * data; + + if (paramFileW == NULL) { + const char * dir; + dir = wPrefGetString( "file", "paramdir" ); + if (dir != NULL) + strcpy( curParamDir, dir ); + else + strcpy( curParamDir, libDir ); + mtbox_bm = wIconCreateBitMap( mtbox_width, mtbox_height, mtbox_bits, drawColorBlack ); + chkbox_bm = wIconCreateBitMap( chkbox_width, chkbox_height, chkbox_bits, drawColorBlack ); + paramFileW = ParamCreateDialog( ¶mFilePG, MakeWindowTitle(_("Parameter Files")), _("Ok"), ParamFileOk, ParamFileCancel, TRUE, NULL, 0, ParamFileDlgUpdate ); + paramFile_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Load Parameters"), _("Parameter files|*.xtp"), LoadParamFile, NULL ); + ParamFileLoadList(); + } + ParamLoadControls( ¶mFilePG ); + ParamGroupRecord( ¶mFilePG ); + if ((listInx = wListGetValues( paramFileL, NULL, 0, NULL, &data ))>=0) + UpdateParamFileButton( (wIndex_t)(long)data ); + ParamFileLoadList(); + wShow( paramFileW ); +} + + +EXPORT addButtonCallBack_t ParamFilesInit( void ) +{ + BOOL_T initted = FALSE; + if (!initted) { + ParamRegister( ¶mFilePG ); + RegisterChangeNotification( ParamFilesChange ); + initted = TRUE; + } + return &DoParamFiles; +} diff --git a/app/bin/draw.c b/app/bin/draw.c new file mode 100644 index 0000000..1987113 --- /dev/null +++ b/app/bin/draw.c @@ -0,0 +1,2446 @@ +/** \file draw.c + * Basic drawing functions. + * + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/draw.c,v 1.17 2009-12-12 17:20:59 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 <stdlib.h> +#include <stdio.h> +#ifdef HAVE_MALLOC_C +#include <malloc.h> +#endif +#include <math.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#include <stdarg.h> +#include <sys/types.h> +#ifndef WINDOWS +#include <unistd.h> +#include <sys/time.h> +#else +#include <sys/timeb.h> +#endif + +#include "track.h" +#include "utility.h" +#include "misc.h" +#include "draw.h" +#include "i18n.h" +#include "fileio.h" + +static void DrawRoomWalls( wBool_t ); +EXPORT void DrawMarkers( void ); +static void ConstraintOrig( coOrd *, coOrd ); + +static int log_pan = 0; +static int log_zoom = 0; +static int log_mouse = 0; + +static wFontSize_t drawMaxTextFontSize = 100; + +/**************************************************************************** + * + * EXPORTED VARIABLES + * + */ + +#define INIT_MAIN_SCALE (8.0) +#define INIT_MAP_SCALE (64.0) +#define MAX_MAIN_SCALE (256.0) +#define MIN_MAIN_SCALE (1.0) + +// static char FAR message[STR_LONG_SIZE]; + +EXPORT wPos_t closePixels = 10; +EXPORT long maxArcSegStraightLen = 100; +EXPORT long drawCount; +EXPORT BOOL_T drawEnable = TRUE; +EXPORT long currRedraw = 0; + +EXPORT wDrawColor drawColorBlack; +EXPORT wDrawColor drawColorWhite; +EXPORT wDrawColor drawColorRed; +EXPORT wDrawColor drawColorBlue; +EXPORT wDrawColor drawColorGreen; +EXPORT wDrawColor drawColorAqua; +EXPORT wDrawColor drawColorPurple; +EXPORT wDrawColor drawColorGold; + +EXPORT DIST_T pixelBins = 80; + +/**************************************************************************** + * + * LOCAL VARIABLES + * + */ + +static wPos_t infoHeight; +EXPORT wWin_p mapW; +EXPORT BOOL_T mapVisible; + +EXPORT wDrawColor markerColor; +EXPORT wDrawColor borderColor; +EXPORT wDrawColor crossMajorColor; +EXPORT wDrawColor crossMinorColor; +EXPORT wDrawColor selectedColor; +EXPORT wDrawColor normalColor; +EXPORT wDrawColor elevColorIgnore; +EXPORT wDrawColor elevColorDefined; +EXPORT wDrawColor profilePathColor; +EXPORT wDrawColor exceptionColor; + +static wFont_p rulerFp; + +static struct { + wMessage_p scale_m; + wMessage_p count_m; + wMessage_p posX_m; + wMessage_p posY_m; + wMessage_p info_m; + wPos_t scale_w; + wPos_t count_w; + wPos_t pos_w; + wPos_t info_w; + wBox_p scale_b; + wBox_p count_b; + wBox_p posX_b; + wBox_p posY_b; + wBox_p info_b; + } infoD; + +EXPORT coOrd oldMarker = { 0.0, 0.0 }; + +EXPORT long dragPixels = 20; +EXPORT long dragTimeout = 500; +EXPORT long autoPan = 0; +EXPORT BOOL_T inError = FALSE; + +typedef enum { mouseNone, mouseLeft, mouseRight, mouseLeftPending } mouseState_e; +static mouseState_e mouseState; +static int mousePositionx, mousePositiony; /**< position of mouse pointer */ + +static int delayUpdate = 1; + +static char xLabel[] = "X : "; +static char yLabel[] = "Y : "; +static char zoomLabel[] = "Zoom : "; + +static struct { + char * name; + double value; + wMenuRadio_p pdRadio; + wMenuRadio_p btRadio; + } zoomList[] = { + { "1:10", 1.0 / 10.0 }, + { "1:5", 1.0 / 5.0 }, + { "1:2", 1.0 / 2.0 }, + { "1:1", 1.0 }, + { "2:1", 2.0 }, + { "3:1", 3.0 }, + { "4:1", 4.0 }, + { "6:1", 6.0 }, + { "8:1", 8.0 }, + { "10:1", 10.0 }, + { "12:1", 12.0 }, + { "16:1", 16.0 }, + { "20:1", 20.0 }, + { "24:1", 24.0 }, + { "28:1", 28.0 }, + { "32:1", 32.0 }, + { "36:1", 36.0 }, + { "40:1", 40.0 }, + { "48:1", 48.0 }, + { "56:1", 56.0 }, + { "64:1", 64.0 }, + { "128:1", 128.0 }, + { "256:1", 256.0 }, +}; + + + +/**************************************************************************** + * + * DRAWING + * + */ + +static void MainCoOrd2Pix( drawCmd_p d, coOrd p, wPos_t * x, wPos_t * y ) +{ + DIST_T t; + if (d->angle != 0.0) + Rotate( &p, d->orig, -d->angle ); + p.x = (p.x - d->orig.x) / d->scale; + p.y = (p.y - d->orig.y) / d->scale; + t = p.x*d->dpi; + if ( t > 0.0 ) + t += 0.5; + else + t -= 0.5; + *x = ((wPos_t)t) + ((d->options&DC_TICKS)?LBORDER:0); + t = p.y*d->dpi; + if ( t > 0.0 ) + t += 0.5; + else + t -= 0.5; + *y = ((wPos_t)t) + ((d->options&DC_TICKS)?BBORDER:0); +} + + +static int Pix2CoOrd_interpolate = 0; + +static void MainPix2CoOrd( + drawCmd_p d, + wPos_t px, + wPos_t py, + coOrd * posR ) +{ + DIST_T x, y; + DIST_T bins = pixelBins; + x = ((((POS_T)((px)-LBORDER))/d->dpi)) * d->scale; + y = ((((POS_T)((py)-BBORDER))/d->dpi)) * d->scale; + x = (long)(x*bins)/bins; + y = (long)(y*bins)/bins; +if (Pix2CoOrd_interpolate) { + DIST_T x1, y1; + x1 = ((((POS_T)((px-1)-LBORDER))/d->dpi)) * d->scale; + y1 = ((((POS_T)((py-1)-BBORDER))/d->dpi)) * d->scale; + x1 = (long)(x1*bins)/bins; + y1 = (long)(y1*bins)/bins; + if (x == x1) { + x += 1/bins/2; + printf ("px=%d x1=%0.6f x=%0.6f\n", px, x1, x ); + } + if (y == y1) + y += 1/bins/2; +} + x += d->orig.x; + y += d->orig.y; + posR->x = x; + posR->y = y; +} + + +static void DDrawLine( + drawCmd_p d, + coOrd p0, + coOrd p1, + wDrawWidth width, + wDrawColor color ) +{ + wPos_t x0, y0, x1, y1; + BOOL_T in0 = FALSE, in1 = FALSE; + coOrd orig, size; + if (d == &mapD && !mapVisible) + return; + if ( (d->options&DC_NOCLIP) == 0 ) { + if (d->angle == 0.0) { + in0 = (p0.x >= d->orig.x && p0.x <= d->orig.x+d->size.x && + p0.y >= d->orig.y && p0.y <= d->orig.y+d->size.y); + in1 = (p1.x >= d->orig.x && p1.x <= d->orig.x+d->size.x && + p1.y >= d->orig.y && p1.y <= d->orig.y+d->size.y); + } + if ( (!in0) || (!in1) ) { + orig = d->orig; + size = d->size; + if (d->options&DC_TICKS) { + orig.x -= LBORDER/d->dpi*d->scale; + orig.y -= BBORDER/d->dpi*d->scale; + size.x += (LBORDER+RBORDER)/d->dpi*d->scale; + size.y += (BBORDER+TBORDER)/d->dpi*d->scale; + } + if (!ClipLine( &p0, &p1, orig, d->angle, size )) + return; + } + } + d->CoOrd2Pix(d,p0,&x0,&y0); + d->CoOrd2Pix(d,p1,&x1,&y1); + drawCount++; + if (drawEnable) { + wDrawLine( d->d, x0, y0, x1, y1, + width, ((d->options&DC_DASH)==0)?wDrawLineSolid:wDrawLineDash, + color, (wDrawOpts)d->funcs->options ); + } +} + + +static void DDrawArc( + drawCmd_p d, + coOrd p, + DIST_T r, + ANGLE_T angle0, + ANGLE_T angle1, + BOOL_T drawCenter, + wDrawWidth width, + wDrawColor color ) +{ + wPos_t x, y; + ANGLE_T da; + coOrd p0, p1; + DIST_T rr; + int i, cnt; + + if (d == &mapD && !mapVisible) + return; + rr = (r / d->scale) * d->dpi + 0.5; + if (rr > wDrawGetMaxRadius(d->d)) { + da = (maxArcSegStraightLen * 180) / (M_PI * rr); + cnt = (int)(angle1/da) + 1; + da = angle1 / cnt; + PointOnCircle( &p0, p, r, angle0 ); + for ( i=1; i<=cnt; i++ ) { + angle0 += da; + PointOnCircle( &p1, p, r, angle0 ); + DrawLine( d, p0, p1, width, color ); + p0 = p1; + } + return; + } + if (d->angle!=0.0 && angle1 < 360.0) + angle0 = NormalizeAngle( angle0-d->angle ); + d->CoOrd2Pix(d,p,&x,&y); + drawCount++; + if (drawEnable) { + wDrawArc( d->d, x, y, (wPos_t)(rr), angle0, angle1, drawCenter, + width, ((d->options&DC_DASH)==0)?wDrawLineSolid:wDrawLineDash, + color, (wDrawOpts)d->funcs->options ); + } +} + + +static void DDrawString( + drawCmd_p d, + coOrd p, + ANGLE_T a, + char * s, + wFont_p fp, + FONTSIZE_T fontSize, + wDrawColor color ) +{ + wPos_t x, y; + if (d == &mapD && !mapVisible) + return; + fontSize /= d->scale; + d->CoOrd2Pix(d,p,&x,&y); + wDrawString( d->d, x, y, d->angle-a, s, fp, fontSize, color, (wDrawOpts)d->funcs->options ); +} + + +static void DDrawFillPoly( + drawCmd_p d, + int cnt, + coOrd * pts, + wDrawColor color ) +{ + typedef wPos_t wPos2[2]; + static dynArr_t wpts_da; + int inx; + wPos_t x, y; + DYNARR_SET( wPos2, wpts_da, cnt * 2 ); +#define wpts(N) DYNARR_N( wPos2, wpts_da, N ) + for ( inx=0; inx<cnt; inx++ ) { + d->CoOrd2Pix( d, pts[inx], &x, &y ); + wpts(inx)[0] = x; + wpts(inx)[1] = y; + } + wDrawFilledPolygon( d->d, &wpts(0), cnt, color, (wDrawOpts)d->funcs->options ); +} + + +static void DDrawFillCircle( + drawCmd_p d, + coOrd p, + DIST_T r, + wDrawColor color ) +{ + wPos_t x, y; + DIST_T rr; + + if (d == &mapD && !mapVisible) + return; + rr = (r / d->scale) * d->dpi + 0.5; + if (rr > wDrawGetMaxRadius(d->d)) { +#ifdef LATER + da = (maxArcSegStraightLen * 180) / (M_PI * rr); + cnt = (int)(angle1/da) + 1; + da = angle1 / cnt; + PointOnCircle( &p0, p, r, angle0 ); + for ( i=1; i<=cnt; i++ ) { + angle0 += da; + PointOnCircle( &p1, p, r, angle0 ); + DrawLine( d, p0, p1, width, color ); + p0 = p1; + } +#endif + return; + } + d->CoOrd2Pix(d,p,&x,&y); + drawCount++; + if (drawEnable) { + wDrawFilledCircle( d->d, x, y, (wPos_t)(rr), + color, (wDrawOpts)d->funcs->options ); + } +} + + +EXPORT void DrawHilight( drawCmd_p d, coOrd p, coOrd s ) +{ + wPos_t x, y, w, h; + if (d == &mapD && !mapVisible) + return; +#ifdef LATER + if (d->options&DC_TEMPSEGS) { + return; + } + if (d->options&DC_PRINT) + return; +#endif + w = (wPos_t)((s.x/d->scale)*d->dpi+0.5); + h = (wPos_t)((s.y/d->scale)*d->dpi+0.5); + d->CoOrd2Pix(d,p,&x,&y); + wDrawFilledRectangle( d->d, x, y, w, h, wDrawColorBlack, wDrawOptTemp ); +} + + +EXPORT void DrawHilightPolygon( drawCmd_p d, coOrd *p, int cnt ) +{ + wPos_t q[4][2]; + int i; +#ifdef LATER + if (d->options&DC_TEMPSEGS) { + return; + } + if (d->options&DC_PRINT) + return; +#endif + ASSERT( cnt <= 4 ); + for (i=0; i<cnt; i++) { + d->CoOrd2Pix(d,p[i],&q[i][0],&q[i][1]); + } + wDrawFilledPolygon( d->d, q, cnt, wDrawColorBlack, wDrawOptTemp ); +} + + +EXPORT void DrawMultiString( + drawCmd_p d, + coOrd pos, + char * text, + wFont_p fp, + wFontSize_t fs, + wDrawColor color, + ANGLE_T a, + coOrd * lo, + coOrd * hi) +{ + char * cp; + POS_T lineH, lineW; + coOrd size, textsize; + POS_T descent; + + DrawTextSize2( &mainD, "Aqjlp", fp, fs, TRUE, &textsize, &descent ); + lineH = textsize.y+descent; + size.x = 0.0; + size.y = 0.0; + while (1) { + cp = message; + while (*text != '\0' && *text != '\n') + *cp++ = *text++; + *cp = '\0'; + DrawTextSize2( &mainD, message, fp, fs, TRUE, &textsize, &descent ); + lineW = textsize.x; + if (lineW>size.x) + size.x = lineW; + DrawString( d, pos, 0.0, message, fp, fs, color ); + pos.y -= lineH; + size.y += lineH; + if (*text) + break; + text++; + } + *lo = pos; + hi->x = pos.x; + hi->y = pos.y+size.y; +} + + +EXPORT void DrawBoxedString( + int style, + drawCmd_p d, + coOrd pos, + char * text, + wFont_p fp, wFontSize_t fs, + wDrawColor color, + ANGLE_T a ) +{ + coOrd size, p[4], p0=pos, p1, p2; + static int bw=5, bh=4, br=2, bb=2; + static double arrowScale = 0.5; + long options = d->options; + POS_T descent; + /*DrawMultiString( d, pos, text, fp, fs, color, a, &lo, &hi );*/ + if ( fs < 2*d->scale ) + return; +#ifndef WINDOWS + if ( ( d->options & DC_PRINT) != 0 ) { + double scale = ((FLOAT_T)fs)/((FLOAT_T)drawMaxTextFontSize)/72.0; + wPos_t w, h, d; + wDrawGetTextSize( &w, &h, &d, mainD.d, text, fp, drawMaxTextFontSize ); + size.x = w*scale; + size.y = h*scale; + descent = d*scale; + } else +#endif + DrawTextSize2( &mainD, text, fp, fs, TRUE, &size, &descent ); +#ifdef WINDOWS + /*h -= 15;*/ +#endif + p0.x -= size.x/2.0; + p0.y -= size.y/2.0; + if (style == BOX_NONE || d == &mapD) { + DrawString( d, p0, 0.0, text, fp, fs, color ); + return; + } + size.x += bw*d->scale/d->dpi; + size.y += bh*d->scale/d->dpi; + size.y += descent; + p[0] = p0; + p[0].x -= br*d->scale/d->dpi; + p[0].y -= bb*d->scale/d->dpi+descent; + p[1].y = p[0].y; + p[2].y = p[3].y = p[0].y + size.y; + p[1].x = p[2].x = p[0].x + size.x; + p[3].x = p[0].x; + d->options &= ~DC_DASH; + switch (style) { + case BOX_ARROW: + Translate( &p1, pos, a, size.x+size.y ); + ClipLine( &pos, &p1, p[0], 0.0, size ); + Translate( &p2, p1, a, size.y*arrowScale ); + DrawLine( d, p1, p2, 0, color ); + Translate( &p1, p2, a+150, size.y*0.7*arrowScale ); + DrawLine( d, p1, p2, 0, color ); + Translate( &p1, p2, a-150, size.y*0.7*arrowScale ); + DrawLine( d, p1, p2, 0, color ); + case BOX_BOX: + DrawLine( d, p[1], p[2], 0, color ); + DrawLine( d, p[2], p[3], 0, color ); + DrawLine( d, p[3], p[0], 0, color ); + case BOX_UNDERLINE: + DrawLine( d, p[0], p[1], 0, color ); + DrawString( d, p0, 0.0, text, fp, fs, color ); + break; + case BOX_INVERT: + DrawFillPoly( d, 4, p, color ); + if ( color != wDrawColorWhite ) + DrawString( d, p0, 0.0, text, fp, fs, wDrawColorWhite ); + break; + case BOX_BACKGROUND: + DrawFillPoly( d, 4, p, wDrawColorWhite ); + DrawString( d, p0, 0.0, text, fp, fs, color ); + break; + } + d->options = options; +} + + +EXPORT void DrawTextSize2( + drawCmd_p dp, + char * text, + wFont_p fp, + wFontSize_t fs, + BOOL_T relative, + coOrd * size, + POS_T * descent ) +{ + wPos_t w, h, d; + FLOAT_T scale = 1.0; + if ( relative ) + fs /= dp->scale; + if ( fs > drawMaxTextFontSize ) { + scale = ((FLOAT_T)fs)/((FLOAT_T)drawMaxTextFontSize); + fs = drawMaxTextFontSize; + } + wDrawGetTextSize( &w, &h, &d, dp->d, text, fp, fs ); + size->x = SCALEX(mainD,w)*scale; + size->y = SCALEY(mainD,h)*scale; + *descent = SCALEY(mainD,d)*scale; + if ( relative ) { + size->x *= dp->scale; + size->y *= dp->scale; + *descent *= dp->scale; + } +/* printf( "DTS2(\"%s\",%0.3f,%d) = (w%d,h%d,d%d) *%0.3f x%0.3f y%0.3f %0.3f\n", text, fs, relative, w, h, d, scale, size->x, size->y, *descent );*/ +} + +EXPORT void DrawTextSize( + drawCmd_p dp, + char * text, + wFont_p fp, + wFontSize_t fs, + BOOL_T relative, + coOrd * size ) +{ + POS_T descent; + DrawTextSize2( dp, text, fp, fs, relative, size, &descent ); +} + + +static void DDrawBitMap( drawCmd_p d, coOrd p, wDrawBitMap_p bm, wDrawColor color) +{ + wPos_t x, y; +#ifdef LATER + if (d->options&DC_TEMPSEGS) { + return; + } + if (d->options&DC_PRINT) + return; +#endif + d->CoOrd2Pix( d, p, &x, &y ); + wDrawBitMap( d->d, bm, x, y, color, (wDrawOpts)d->funcs->options ); +} + + +static void TempSegLine( + drawCmd_p d, + coOrd p0, + coOrd p1, + wDrawWidth width, + wDrawColor color ) +{ + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + tempSegs(tempSegs_da.cnt-1).type = SEG_STRLIN; + tempSegs(tempSegs_da.cnt-1).color = color; + if (d->options&DC_SIMPLE) + tempSegs(tempSegs_da.cnt-1).width = 0; + else + tempSegs(tempSegs_da.cnt-1).width = width*d->scale/d->dpi; + tempSegs(tempSegs_da.cnt-1).u.l.pos[0] = p0; + tempSegs(tempSegs_da.cnt-1).u.l.pos[1] = p1; +} + + +static void TempSegArc( + drawCmd_p d, + coOrd p, + DIST_T r, + ANGLE_T angle0, + ANGLE_T angle1, + BOOL_T drawCenter, + wDrawWidth width, + wDrawColor color ) +{ + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + tempSegs(tempSegs_da.cnt-1).type = SEG_CRVLIN; + tempSegs(tempSegs_da.cnt-1).color = color; + if (d->options&DC_SIMPLE) + tempSegs(tempSegs_da.cnt-1).width = 0; + else + tempSegs(tempSegs_da.cnt-1).width = width*d->scale/d->dpi; + tempSegs(tempSegs_da.cnt-1).u.c.center = p; + tempSegs(tempSegs_da.cnt-1).u.c.radius = r; + tempSegs(tempSegs_da.cnt-1).u.c.a0 = angle0; + tempSegs(tempSegs_da.cnt-1).u.c.a1 = angle1; +} + + +static void TempSegString( + drawCmd_p d, + coOrd p, + ANGLE_T a, + char * s, + wFont_p fp, + FONTSIZE_T fontSize, + wDrawColor color ) +{ + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + tempSegs(tempSegs_da.cnt-1).type = SEG_TEXT; + tempSegs(tempSegs_da.cnt-1).color = color; + tempSegs(tempSegs_da.cnt-1).width = 0; + tempSegs(tempSegs_da.cnt-1).u.t.pos = p; + tempSegs(tempSegs_da.cnt-1).u.t.angle = a; + tempSegs(tempSegs_da.cnt-1).u.t.fontP = fp; + tempSegs(tempSegs_da.cnt-1).u.t.fontSize = fontSize; + tempSegs(tempSegs_da.cnt-1).u.t.string = s; +} + + +static void TempSegFillPoly( + drawCmd_p d, + int cnt, + coOrd * pts, + wDrawColor color ) +{ +#ifdef LATER + pts is not guaranteed to valid + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + tempSegs(tempSegs_da.cnt-1).type = SEG_FILPOLY; + tempSegs(tempSegs_da.cnt-1).color = color; + tempSegs(tempSegs_da.cnt-1).width = 0; + tempSegs(tempSegs_da.cnt-1).u.p.cnt = cnt; + tempSegs(tempSegs_da.cnt-1).u.p.pts = pts; +#endif + return; +} + + +static void TempSegFillCircle( + drawCmd_p d, + coOrd p, + DIST_T r, + wDrawColor color ) +{ + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + tempSegs(tempSegs_da.cnt-1).type = SEG_FILCRCL; + tempSegs(tempSegs_da.cnt-1).color = color; + tempSegs(tempSegs_da.cnt-1).width = 0; + tempSegs(tempSegs_da.cnt-1).u.c.center = p; + tempSegs(tempSegs_da.cnt-1).u.c.radius = r; + tempSegs(tempSegs_da.cnt-1).u.c.a0 = 0.0; + tempSegs(tempSegs_da.cnt-1).u.c.a1 = 360.0; +} + + +static void NoDrawBitMap( drawCmd_p d, coOrd p, wDrawBitMap_p bm, wDrawColor color ) +{ +} + + + +EXPORT drawFuncs_t screenDrawFuncs = { + 0, + DDrawLine, + DDrawArc, + DDrawString, + DDrawBitMap, + DDrawFillPoly, + DDrawFillCircle }; + +EXPORT drawFuncs_t tempDrawFuncs = { + wDrawOptTemp, + DDrawLine, + DDrawArc, + DDrawString, + DDrawBitMap, + DDrawFillPoly, + DDrawFillCircle }; + +EXPORT drawFuncs_t printDrawFuncs = { + 0, + DDrawLine, + DDrawArc, + DDrawString, + NoDrawBitMap, + DDrawFillPoly, + DDrawFillCircle }; + +EXPORT drawFuncs_t tempSegDrawFuncs = { + 0, + TempSegLine, + TempSegArc, + TempSegString, + NoDrawBitMap, + TempSegFillPoly, + TempSegFillCircle }; + +EXPORT drawCmd_t mainD = { + NULL, &screenDrawFuncs, DC_TICKS, INIT_MAIN_SCALE, 0.0, {0.0,0.0}, {0.0,0.0}, MainPix2CoOrd, MainCoOrd2Pix }; + +EXPORT drawCmd_t tempD = { + NULL, &tempDrawFuncs, DC_TICKS|DC_SIMPLE, INIT_MAIN_SCALE, 0.0, {0.0,0.0}, {0.0,0.0}, MainPix2CoOrd, MainCoOrd2Pix }; + +EXPORT drawCmd_t mapD = { + NULL, &screenDrawFuncs, 0, INIT_MAP_SCALE, 0.0, {0.0,0.0}, {96.0,48.0}, Pix2CoOrd, CoOrd2Pix }; + + +/***************************************************************************** + * + * MAIN AND MAP WINDOW DEFINTIONS + * + */ + + +static wPos_t info_yb_offset = 2; +static wPos_t info_ym_offset = 3; +static wPos_t six = 2; +static wPos_t info_xm_offset = 2; +#define NUM_INFOCTL (4) +static wControl_p curInfoControl[NUM_INFOCTL]; +static wPos_t curInfoLabelWidth[NUM_INFOCTL]; + +/** + * Determine the width of a mouse pointer position string ( coordinate plus label ). + * + * \return width of position string + */ +static wPos_t GetInfoPosWidth( void ) +{ + wPos_t labelWidth; + + DIST_T dist; + if ( mapD.size.x > mapD.size.y ) + dist = mapD.size.x; + else + dist = mapD.size.y; + if ( units == UNITS_METRIC ) { + dist *= 2.54; + if ( dist >= 1000 ) + dist = 9999.999*2.54; + else if ( dist >= 100 ) + dist = 999.999*2.54; + else if ( dist >= 10 ) + dist = 99.999*2.54; + } else { + if ( dist >= 100*12 ) + dist = 999.0*12.0+11.0+3.0/4.0-1.0/64.0; + else if ( dist >= 10*12 ) + dist = 99.0*12.0+11.0+3.0/4.0-1.0/64.0; + else if ( dist >= 1*12 ) + dist = 9.0*12.0+11.0+3.0/4.0-1.0/64.0; + } + + labelWidth = (wLabelWidth( xLabel ) > wLabelWidth( yLabel ) ? wLabelWidth( xLabel ):wLabelWidth( yLabel )); + + return wLabelWidth( FormatDistance(dist) ) + labelWidth; +} + +/** + * Initialize the status line at the bottom of the window. + * + */ + +EXPORT void InitInfoBar( void ) +{ + wPos_t width, height, y, yb, ym, x, boxH; + wWinGetSize( mainW, &width, &height ); + infoHeight = 3 + wMessageGetHeight( 0L ) + 3; + y = height - infoHeight; + y -= 19; /* Kludge for MSW */ + infoD.pos_w = GetInfoPosWidth() + 2; + infoD.scale_w = wLabelWidth( "999:1" ) + wLabelWidth( zoomLabel ) + 6; + /* we do not use the count label for the moment */ + infoD.count_w = 0; + infoD.info_w = width - infoD.pos_w*2 - infoD.scale_w - infoD.count_w - 45; + if (infoD.info_w <= 0) { + infoD.info_w = 10; + } + yb = y+info_yb_offset; + ym = y+info_ym_offset; + boxH = infoHeight-5; + x = 0; + infoD.scale_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.scale_w, boxH ); + infoD.scale_m = wMessageCreate( mainW, x+info_xm_offset, ym, "infoBarScale", infoD.scale_w-six, zoomLabel ); + x += infoD.scale_w + 10; + infoD.posX_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.pos_w, boxH ); + infoD.posX_m = wMessageCreate( mainW, x+info_xm_offset, ym, "infoBarPosX", infoD.pos_w-six, xLabel ); + x += infoD.pos_w + 5; + infoD.posY_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.pos_w, boxH ); + infoD.posY_m = wMessageCreate( mainW, x+info_xm_offset, ym, "infoBarPosY", infoD.pos_w-six, yLabel ); + x += infoD.pos_w + 10; + infoD.info_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.info_w, boxH ); + infoD.info_m = wMessageCreate( mainW, x+info_xm_offset, ym, "infoBarStatus", infoD.info_w-six, "" ); +} + + +static void SetInfoBar( void ) +{ + wPos_t width, height, y, yb, ym, x, boxH; + int inx; + static long oldDistanceFormat = -1; + long newDistanceFormat; + wWinGetSize( mainW, &width, &height ); + y = height - infoHeight; + newDistanceFormat = GetDistanceFormat(); + if ( newDistanceFormat != oldDistanceFormat ) { + infoD.pos_w = GetInfoPosWidth() + 2; + wBoxSetSize( infoD.posX_b, infoD.pos_w, infoHeight-5 ); + wMessageSetWidth( infoD.posX_m, infoD.pos_w-six ); + wBoxSetSize( infoD.posY_b, infoD.pos_w, infoHeight-5 ); + wMessageSetWidth( infoD.posY_m, infoD.pos_w-six ); + } + infoD.info_w = width - infoD.pos_w*2 - infoD.scale_w - infoD.count_w - 40 + 4; + if (infoD.info_w <= 0) { + infoD.info_w = 10; + } + yb = y+info_yb_offset; + ym = y+info_ym_offset; + boxH = infoHeight-5; + wWinClear( mainW, 0, y, width, infoHeight ); + x = 0; + wControlSetPos( (wControl_p)infoD.scale_b, x, yb ); + wControlSetPos( (wControl_p)infoD.scale_m, x+info_xm_offset, ym ); + x += infoD.scale_w + 10; + wControlSetPos( (wControl_p)infoD.posX_b, x, yb ); + wControlSetPos( (wControl_p)infoD.posX_m, x+info_xm_offset, ym ); + x += infoD.pos_w + 5; + wControlSetPos( (wControl_p)infoD.posY_b, x, yb ); + wControlSetPos( (wControl_p)infoD.posY_m, x+info_xm_offset, ym ); + x += infoD.pos_w + 10; + wControlSetPos( (wControl_p)infoD.info_b, x, yb ); + wControlSetPos( (wControl_p)infoD.info_m, x+info_xm_offset, ym ); + wBoxSetSize( infoD.info_b, infoD.info_w, boxH ); + wMessageSetWidth( infoD.info_m, infoD.info_w-six ); + if (curInfoControl[0]) { + x = wControlGetPosX( (wControl_p)infoD.info_m ); +#ifndef WINDOWS + yb -= 2; +#endif + for ( inx=0; curInfoControl[inx]; inx++ ) { + x += curInfoLabelWidth[inx]; + wControlSetPos( curInfoControl[inx], x, yb ); + x += wControlGetWidth( curInfoControl[inx] )+3; + wControlShow( curInfoControl[inx], TRUE ); + } + } +} + + +static void InfoScale( void ) +{ + if (mainD.scale >= 1) + sprintf( message, "%s%0.0f:1", zoomLabel, mainD.scale ); + else + sprintf( message, "%s1:%0.0f", zoomLabel, floor(1/mainD.scale+0.5) ); + wMessageSetValue( infoD.scale_m, message ); +} + +EXPORT void InfoCount( wIndex_t count ) +{ +/* + sprintf( message, "%d", count ); + wMessageSetValue( infoD.count_m, message ); +*/ +} + +EXPORT void InfoPos( coOrd pos ) +{ +#ifdef LATER + wPos_t ww, hh; + DIST_T w, h; +#endif + wPos_t x, y; + + sprintf( message, "%s%s", xLabel, FormatDistance(pos.x) ); + wMessageSetValue( infoD.posX_m, message ); + sprintf( message, "%s%s", yLabel, FormatDistance(pos.y) ); + wMessageSetValue( infoD.posY_m, message ); +#ifdef LATER + wDrawGetSize( mainD.d, &ww, &hh ); + w = (DIST_T)(ww/mainD.dpi); + h = (DIST_T)(hh/mainD.dpi); + /*wDrawClip( mainD.d, 0, 0, w, h );*/ +#endif + mainD.CoOrd2Pix(&mainD,oldMarker,&x,&y); + wDrawLine( mainD.d, 0, y, (wPos_t)(LBORDER), y, + 0, wDrawLineSolid, markerColor, wDrawOptTemp ); + wDrawLine( mainD.d, x, 0, x, (wPos_t)(BBORDER), + 0, wDrawLineSolid, markerColor, wDrawOptTemp ); + + mainD.CoOrd2Pix(&mainD,pos,&x,&y); + wDrawLine( mainD.d, 0, y, (wPos_t)(LBORDER), y, + 0, wDrawLineSolid, markerColor, wDrawOptTemp ); + wDrawLine( mainD.d, x, 0, x, (wPos_t)(BBORDER), + 0, wDrawLineSolid, markerColor, wDrawOptTemp ); +#ifdef LATER + /*wDrawClip( mainD.d, LBORDER, BBORDER, + w-(LBORDER+RBORDER), h-(BBORDER+TBORDER) );*/ +#endif + oldMarker = pos; +} + +static wControl_p deferSubstituteControls[NUM_INFOCTL+1]; +static char * deferSubstituteLabels[NUM_INFOCTL]; + +EXPORT void InfoSubstituteControls( + wControl_p * controls, + char ** labels ) +{ + wPos_t x, y; + int inx; + for ( inx=0; inx<NUM_INFOCTL; inx++ ) { + if (curInfoControl[inx]) { + wControlShow( curInfoControl[inx], FALSE ); + curInfoControl[inx] = NULL; + } + curInfoLabelWidth[inx] = 0; + curInfoControl[inx] = NULL; + } + if ( inError && ( controls!=NULL && controls[0]!=NULL) ) { + memcpy( deferSubstituteControls, controls, sizeof deferSubstituteControls ); + memcpy( deferSubstituteLabels, labels, sizeof deferSubstituteLabels ); + } + if ( inError || controls == NULL || controls[0]==NULL ) { + wControlShow( (wControl_p)infoD.info_m, TRUE ); + return; + } + x = wControlGetPosX( (wControl_p)infoD.info_m ); + y = wControlGetPosY( (wControl_p)infoD.info_m ); +#ifndef WINDOWS + y -= 3; +#endif + wMessageSetValue( infoD.info_m, "" ); + wControlShow( (wControl_p)infoD.info_m, FALSE ); + for ( inx=0; controls[inx]; inx++ ) { + curInfoLabelWidth[inx] = wLabelWidth(_(labels[inx])); + x += curInfoLabelWidth[inx]; + wControlSetPos( controls[inx], x, y ); + x += wControlGetWidth( controls[inx] ); + wControlSetLabel( controls[inx], _(labels[inx]) ); + wControlShow( controls[inx], TRUE ); + curInfoControl[inx] = controls[inx]; + x += 3; + } + curInfoControl[inx] = NULL; + deferSubstituteControls[0] = NULL; +} + + +#ifdef LATER +EXPORT void InfoSubstituteControl( + wControl_p control1, + char * label1, + wControl_p control2, + char * label2 ) +{ + wControl_p controls[3]; + wPos_t widths[2]; + + if (control1 == NULL) { + InfoSubstituteControls( NULL, NULL ); + } else { + controls[0] = control1; + controls[1] = control2; + controls[2] = NULL; + widths[0] = wLabelWidth( label1 ); + if (label2) + widths[1] = wLabelWidth( label2 ); + else + widths[1] = 0; + InfoSubstituteControls( controls, widths ); +#ifdef LATER + if (curInfoControl[0]) { + wControlShow( curInfoControl[0], FALSE ); + curInfoControl[0] = NULL; + } + if (curInfoControl[1]) { + wControlShow( curInfoControl[1], FALSE ); + curInfoControl[1] = NULL; + } + wControlShow( (wControl_p)infoD.info_m, TRUE ); + } else { + if (curInfoControl[0]) + wControlShow( curInfoControl[0], FALSE ); + if (curInfoControl[1]) + wControlShow( curInfoControl[1], FALSE ); + x = wControlGetPosX( (wControl_p)infoD.info_m ); + y = wControlGetPosY( (wControl_p)infoD.info_m ); + curInfoLabelWidth[0] = wLabelWidth( label1 ); + x += curInfoLabelWidth[0]; + wControlShow( (wControl_p)infoD.info_m, FALSE ); + wControlSetPos( control1, x, y ); + wControlShow( control1, TRUE ); + curInfoControl[0] = control1; + curInfoControl[1] = NULL; + if (control2 != NULL) { + curInfoLabelWidth[1] = wLabelWidth( label2 ); + x = wControlBeside( curInfoControl[0] ) + 10; + x += curInfoLabelWidth[1]+10; + wControlSetPos( control2, x, y ); + wControlShow( control2, TRUE ); + curInfoControl[1] = control2; + } +#endif + } +} +#endif + + +EXPORT void SetMessage( char * msg ) +{ + wMessageSetValue( infoD.info_m, msg ); +} + + +static void ChangeMapScale( void ) +{ + wPos_t w, h; + wPos_t dw, dh; + FLOAT_T fw, fh; + + wGetDisplaySize( &dw, &dh ); + dw /= 2; + dh /= 2; + fw = ((mapD.size.x/mapD.scale)*mapD.dpi + 0.5)+2; + fh = ((mapD.size.y/mapD.scale)*mapD.dpi + 0.5)+2; + if (fw > dw || fh > dh) { + if (fw/dw > fh/dh) { + mapD.scale = ceil(mapD.size.x*mapD.dpi/dw); + } else { + mapD.scale = ceil(mapD.size.y*mapD.dpi/dh); + } + mapScale = (long)mapD.scale; + fw = ((mapD.size.x/mapD.scale)*mapD.dpi + 0.5)+2; + fh = ((mapD.size.y/mapD.scale)*mapD.dpi + 0.5)+2; + } else if ( fw < 100.0 && fh < 100.0 ) { + if (fw > fh) { + mapD.scale = ceil(mapD.size.x*mapD.dpi/100); + } else { + mapD.scale = ceil(mapD.size.y*mapD.dpi/100); + } + mapScale = (long)mapD.scale; + fw = ((mapD.size.x/mapD.scale)*mapD.dpi + 0.5)+2; + fh = ((mapD.size.y/mapD.scale)*mapD.dpi + 0.5)+2; + } + w = (wPos_t)fw; + h = (wPos_t)fh; + wWinSetSize( mapW, w+DlgSepLeft+DlgSepRight, h+DlgSepTop+DlgSepBottom ); + wDrawSetSize( mapD.d, w, h ); +} + + +EXPORT BOOL_T SetRoomSize( coOrd size ) +{ + if (size.x < 12.0) + size.x = 12.0; + if (size.y < 12.0) + size.y = 12.0; + if ( mapD.size.x == size.x && + mapD.size.y == size.y ) + return TRUE; + mapD.size = size; + if ( mapW == NULL) + return TRUE; + ChangeMapScale(); + ConstraintOrig( &mainD.orig, mainD.size ); + tempD.orig = mainD.orig; + /*MainRedraw();*/ + wPrefSetFloat( "draw", "roomsizeX", mapD.size.x ); + wPrefSetFloat( "draw", "roomsizeY", mapD.size.y ); + return TRUE; +} + + +EXPORT void GetRoomSize( coOrd * froomSize ) +{ + *froomSize = mapD.size; +} + + +static void MapRedraw( void ) +{ + if (inPlaybackQuit) + return; +#ifdef VERBOSE +lprintf("MapRedraw\n"); +#endif + if (!mapVisible) + return; + + if (delayUpdate) + wDrawDelayUpdate( mapD.d, TRUE ); + wSetCursor( wCursorWait ); + wDrawClear( mapD.d ); + DrawTracks( &mapD, mapD.scale, mapD.orig, mapD.size ); + DrawMapBoundingBox( TRUE ); + wSetCursor( wCursorNormal ); + wDrawDelayUpdate( mapD.d, FALSE ); +} + + +static void MapResize( void ) +{ + mapD.scale = mapScale; + ChangeMapScale(); + MapRedraw(); +} + + +#ifdef LATER +static void MapProc( wWin_p win, winProcEvent e, void * data ) +{ + switch( e ) { + case wResize_e: + if (mapD.d == NULL) + return; + DrawMapBoundingBox( FALSE ); + ChangeMapScale(); + break; + case wClose_e: + mapVisible = FALSE; + break; + /*case wRedraw_e: + if (mapD.d == NULL) + break; + MapRedraw(); + break;*/ + default: + break; + } +} +#endif + + +EXPORT void SetMainSize( void ) +{ + wPos_t ww, hh; + DIST_T w, h; + wDrawGetSize( mainD.d, &ww, &hh ); + ww -= LBORDER+RBORDER; + hh -= BBORDER+TBORDER; + w = ww/mainD.dpi; + h = hh/mainD.dpi; + mainD.size.x = w * mainD.scale; + mainD.size.y = h * mainD.scale; + tempD.size = mainD.size; +} + + +EXPORT void MainRedraw( void ) +{ +#ifdef LATER + wPos_t ww, hh; + DIST_T w, h; +#endif + + coOrd orig, size; + DIST_T t1; + if (inPlaybackQuit) + return; +#ifdef VERBOSE +lprintf("mainRedraw\n"); +#endif + + wSetCursor( wCursorWait ); + if (delayUpdate) + wDrawDelayUpdate( mainD.d, TRUE ); +#ifdef LATER + wDrawGetSize( mainD.d, &ww, &hh ); + w = ww/mainD.dpi; + h = hh/mainD.dpi; +#endif + SetMainSize(); +#ifdef LATER + /*wDrawClip( mainD.d, 0, 0, w, h );*/ +#endif + t1 = mainD.dpi/mainD.scale; + if (units == UNITS_ENGLISH) { + t1 /= 2.0; + for ( pixelBins=0.25; pixelBins<t1; pixelBins*=2.0 ); + } else { + pixelBins = 50.8; + if (pixelBins >= t1) + while (1) { + if ( pixelBins <= t1 ) + break; + pixelBins /= 2.0; + if ( pixelBins <= t1 ) + break; + pixelBins /= 2.5; + if ( pixelBins <= t1 ) + break; + pixelBins /= 2.0; + } + } + ConstraintOrig( &mainD.orig, mainD.size ); + tempD.orig = mainD.orig; + wDrawClear( mainD.d ); + currRedraw++; + DrawSnapGrid( &tempD, mapD.size, TRUE ); + DrawRoomWalls( TRUE ); + orig = mainD.orig; + size = mainD.size; + orig.x -= RBORDER/mainD.dpi*mainD.scale; + orig.y -= BBORDER/mainD.dpi*mainD.scale; + size.x += (RBORDER+LBORDER)/mainD.dpi*mainD.scale; + size.y += (BBORDER+TBORDER)/mainD.dpi*mainD.scale; + DrawTracks( &mainD, mainD.scale, orig, size ); + RulerRedraw( FALSE ); + DoCurCommand( C_REDRAW, zero ); + DrawMarkers(); + wSetCursor( wCursorNormal ); + InfoScale(); + wDrawDelayUpdate( mainD.d, FALSE ); +} + + +EXPORT void MainProc( wWin_p win, winProcEvent e, void * data ) +{ + wPos_t width, height; + switch( e ) { + case wResize_e: + if (mainD.d == NULL) + return; + DrawMapBoundingBox( FALSE ); + wWinGetSize( mainW, &width, &height ); + LayoutToolBar(); + height -= (toolbarHeight+infoHeight); + if (height >= 0) { + wDrawSetSize( mainD.d, width, height ); + wControlSetPos( (wControl_p)mainD.d, 0, toolbarHeight ); + SetMainSize(); + ConstraintOrig( &mainD.orig, mainD.size ); + tempD.orig = mainD.orig; + SetInfoBar(); + MainRedraw(); + wPrefSetInteger( "draw", "mainwidth", width ); + wPrefSetInteger( "draw", "mainheight", height ); + } + DrawMapBoundingBox( TRUE ); + break; + case wQuit_e: + if (changed && + NoticeMessage( MSG_SAVE_CHANGES, _("Save"), _("Quit"))) + DoSave(NULL); + + CleanupFiles(); + SaveState(); + CleanupCustom(); + break; + case wClose_e: + /* shutdown the application */ + DoQuit(); + break; + default: + break; + } +} + + +#ifdef WINDOWS +int profRedraw = 0; +void +#ifndef WIN32 +_far _pascal +#endif +ProfStart( void ); +void +#ifndef WIN32 +_far _pascal +#endif +ProfStop( void ); +#endif + +EXPORT void DoRedraw( void ) +{ +#ifdef WINDOWS +#ifndef WIN32 + if (profRedraw) + ProfStart(); +#endif +#endif + MapRedraw(); + MainRedraw(); +#ifdef WINDOWS +#ifndef WIN32 + if (profRedraw) + ProfStop(); +#endif +#endif + + +} + +/***************************************************************************** + * + * RULERS and OTHER DECORATIONS + * + */ + + +static void DrawRoomWalls( wBool_t t ) +{ + coOrd p01, p11, p10; + + if (mainD.d == NULL) + return; +#ifdef LATER + wDrawGetDim( mainD.d, &w, &h ); +#endif + DrawTicks( &mainD, mapD.size ); + + p01.x = p10.y = 0.0; + p11.x = p10.x = mapD.size.x; + p01.y = p11.y = mapD.size.y; + DrawLine( &mainD, p01, p11, 3, t?borderColor:wDrawColorWhite ); + DrawLine( &mainD, p11, p10, 3, t?borderColor:wDrawColorWhite ); +#ifdef LATER + /*wDrawClip( mainD.d, LBORDER, BBORDER, + w-(LBORDER+RBORDER), h-(BBORDER+TBORDER) );*/ +#endif +} + + +EXPORT void DrawMarkers( void ) +{ + wPos_t x, y; + mainD.CoOrd2Pix(&mainD,oldMarker,&x,&y); + wDrawLine( mainD.d, 0, y, (wPos_t)LBORDER, y, + 0, wDrawLineSolid, markerColor, wDrawOptTemp ); + wDrawLine( mainD.d, x, 0, x, (wPos_t)BBORDER, + 0, wDrawLineSolid, markerColor, wDrawOptTemp ); +} + +static DIST_T rulerFontSize = 12.0; + + +EXPORT void DrawRuler( + drawCmd_p d, + coOrd pos0, + coOrd pos1, + DIST_T offset, + int number, + int tickSide, + wDrawColor color ) +{ + coOrd orig = pos0; + wAngle_t a, aa; + DIST_T start, end; + long inch, lastInch; + wPos_t len; + int digit; + char quote; + char message[10]; + coOrd d_orig, d_size; + wFontSize_t fs; + long mm, mm0, mm1, power; + wPos_t x0, y0, x1, y1; + long dxn, dyn; + static int lengths[8] = { + 0, 2, 4, 2, 6, 2, 4, 2 }; + int fraction, incr, firstFraction, lastFraction; + int majorLength; + coOrd p0, p1; + FLOAT_T sin_aa; + + a = FindAngle( pos0, pos1 ); + Translate( &pos0, pos0, a, offset ); + Translate( &pos1, pos1, a, offset ); + aa = NormalizeAngle(a+(tickSide==0?+90:-90)); + if (aa > 90.0 && aa < 270.0) { +#ifdef WINDOWS + dyn = -17; +#else + dyn = -12; +#endif + } else { + dyn = +3; + } + sin_aa = sin(D2R(aa)); + dxn = (long)floor(10.0*sin_aa); + end = FindDistance( pos0, pos1 ); + if (end < 0.1) + return; + d_orig.x = d->orig.x - 0.001; + d_orig.y = d->orig.y - 0.001; + d_size.x = d->size.x + 0.002; + d_size.y = d->size.y + 0.002; + if (!ClipLine( &pos0, &pos1, d_orig, d->angle, d_size )) + return; + + start = FindDistance( orig, pos0 ); + if (offset < 0) + start = -start; + end = FindDistance( orig, pos1 ); + + d->CoOrd2Pix( d, pos0, &x0, &y0 ); + d->CoOrd2Pix( d, pos1, &x1, &y1 ); + wDrawLine( d->d, x0, y0, x1, y1, + 0, wDrawLineSolid, color, (wDrawOpts)d->funcs->options ); + + if (units == UNITS_METRIC) { + mm0 = (int)ceil(start*25.4-0.5); + mm1 = (int)floor(end*25.4+0.5); + len = 2; + if (d->scale <= 1) { + power = 1; + } else if (d->scale <= 8) { + power = 10; + } else if (d->scale <= 32) { + power = 100; + } else { + power = 1000; + } + for ( ; power<=1000; power*=10,len+=3 ) { + if (power == 1000) + len = 10; + for (mm=((mm0+(mm0>0?power-1:0))/power)*power; mm<=mm1; mm+=power) { + if (power==1000 || mm%(power*10) != 0) { + Translate( &p0, orig, a, mm/25.4 ); + Translate( &p1, p0, aa, len*d->scale/mainD.dpi ); + d->CoOrd2Pix( d, p0, &x0, &y0 ); + d->CoOrd2Pix( d, p1, &x1, &y1 ); + wDrawLine( d->d, x0, y0, x1, y1, + 0, wDrawLineSolid, color, (wDrawOpts)d->funcs->options ); + + if (!number) + continue; + if ( (power>=1000) || + (d->scale<=8 && power>=100) || + (d->scale<=1 && power>=10) ) { + if (mm%100 != 0) { + sprintf(message, "%ld", mm/10%10 ); + fs = rulerFontSize*2/3; + p0.x = p1.x+4*dxn/10*d->scale/mainD.dpi; + p0.y = p1.y+dyn*d->scale/mainD.dpi; + } else { + sprintf(message, "%0.1f", mm/1000.0 ); + fs = rulerFontSize; + p0.x = p0.x+((-(LBORDER-2)/2)+((LBORDER-2)/2+2)*sin_aa)*d->scale/mainD.dpi; + p0.y = p1.y+dyn*d->scale/mainD.dpi; + } + d->CoOrd2Pix( d, p0, &x0, &y0 ); + wDrawString( d->d, x0, y0, d->angle, message, rulerFp, + fs, color, (wDrawOpts)d->funcs->options ); + } + } + } + } + } else { + if (d->scale <= 1) + incr = 1; + else if (d->scale <= 2) + incr = 2; + else if (d->scale <= 4) + incr = 4; + else + incr = 8; + lastInch = (int)floor(end); + lastFraction = 7; + inch = (int)ceil(start); + firstFraction = (((int)((inch-start)*8/*+1*/)) / incr) * incr; + if (firstFraction > 0) { + inch--; + firstFraction = 8 - firstFraction; + } + for ( ; inch<=lastInch; inch++){ + if (inch % 12 == 0) { + lengths[0] = 10; + majorLength = 16; + digit = (int)(inch/12); + fs = rulerFontSize; + quote = '\''; + } else if (d->scale <= 8) { + lengths[0] = 8; + majorLength = 13; + digit = (int)(inch%12); + fs = rulerFontSize*(2.0/3.0); + quote = '"'; + } else { + continue; + } + if (inch == lastInch) + lastFraction = (((int)((end - lastInch)*8)) / incr) * incr; + for ( fraction = firstFraction; fraction<=lastFraction; fraction += incr ) { + Translate( &p0, orig, a, inch+fraction/8.0 ); + Translate( &p1, p0, aa, lengths[fraction]*d->scale/72.0 ); + d->CoOrd2Pix( d, p0, &x0, &y0 ); + d->CoOrd2Pix( d, p1, &x1, &y1 ); + wDrawLine( d->d, x0, y0, x1, y1, + 0, wDrawLineSolid, color, + (wDrawOpts)d->funcs->options ); +#ifdef KLUDGEWINDOWS + /* KLUDGE: can't draw invertable strings on windows */ + if ( (opts&DO_TEMP) == 0) +#endif + if ( fraction == 0 && number == TRUE) { + if (inch % 12 == 0 || d->scale <= 2) { + Translate( &p0, p0, aa, majorLength*d->scale/72.0 ); + Translate( &p0, p0, 225, 11*d->scale/72.0 ); + sprintf(message, "%d%c", digit, quote ); + d->CoOrd2Pix( d, p0, &x0, &y0 ); + wDrawString( d->d, x0, y0, d->angle, message, rulerFp, fs, color, (wDrawOpts)d->funcs->options ); + } + } + firstFraction = 0; + } + } + } +} + + +EXPORT void DrawTicks( drawCmd_p d, coOrd size ) +{ + coOrd p0, p1; + DIST_T offset; + + offset = 0.0; + if ( d->orig.x<0.0 ) + offset = d->orig.x; + p0.x = 0.0/*d->orig.x*/; p1.x = size.x; + p0.y = p1.y = /*max(d->orig.y,0.0)*/ d->orig.y; + DrawRuler( d, p0, p1, offset, TRUE, FALSE, borderColor ); + p0.y = p1.y = min(d->orig.y + d->size.y, size.y); + DrawRuler( d, p0, p1, offset, FALSE, TRUE, borderColor ); + offset = 0.0; + if ( d->orig.y<0.0 ) + offset = d->orig.y; + p0.y = 0.0/*d->orig.y*/; p1.y = max(size.y,0.0); + p0.x = p1.x = d->orig.x; + DrawRuler( d, p0, p1, offset, TRUE, TRUE, borderColor ); + p0.x = p1.x = min(d->orig.x + d->size.x, size.x); + DrawRuler( d, p0, p1, offset, FALSE, FALSE, borderColor ); +} + +/***************************************************************************** + * + * ZOOM and PAN + * + */ + +EXPORT coOrd mainCenter; + + +EXPORT void DrawMapBoundingBox( BOOL_T set ) +{ + if (mainD.d == NULL || mapD.d == NULL) + return; + DrawHilight( &mapD, mainD.orig, mainD.size ); +} + + +static void ConstraintOrig( coOrd * orig, coOrd size ) +{ +LOG( log_pan, 2, ( "ConstraintOrig [ %0.3f, %0.3f ] RoomSize(%0.3f %0.3f), WxH=%0.3fx%0.3f", + orig->x, orig->y, mapD.size.x, mapD.size.y, + size.x, size.y ) ) + if (orig->x+size.x > mapD.size.x ) { + orig->x = mapD.size.x-size.x; + orig->x += (units==UNITS_ENGLISH?1.0:(1.0/2.54)); + } + if (orig->x < 0) + orig->x = 0; + if (orig->y+size.y > mapD.size.y ) { + orig->y = mapD.size.y-size.y; + orig->y += (units==UNITS_ENGLISH?1.0:1.0/2.54); + + } + if (orig->y < 0) + orig->y = 0; + if (mainD.scale >= 1.0) { + if (units == UNITS_ENGLISH) { + orig->x = floor(orig->x); + orig->y = floor(orig->y); + } else { + orig->x = floor(orig->x*2.54)/2.54; + orig->y = floor(orig->y*2.54)/2.54; + } + } + orig->x = (long)(orig->x*pixelBins+0.5)/pixelBins; + orig->y = (long)(orig->y*pixelBins+0.5)/pixelBins; +LOG( log_pan, 2, ( " = [ %0.3f %0.3f ]\n", orig->y, orig->y ) ) +} + +/** + * Initialize the menu for setting zoom factors. + * + * \param IN zoomM Menu to which radio button is added + * \param IN zoomSubM Second menu to which radio button is added, ignored if NULL + * + */ + +EXPORT void InitCmdZoom( wMenu_p zoomM, wMenu_p zoomSubM ) +{ + int inx; + + for ( inx=0; inx<sizeof zoomList/sizeof zoomList[0]; inx++ ) { + if( zoomList[ inx ].value >= 1.0 ) { + zoomList[inx].btRadio = wMenuRadioCreate( zoomM, "cmdZoom", zoomList[inx].name, 0, (wMenuCallBack_p)DoZoom, (void *)(&(zoomList[inx].value))); + if( zoomSubM ) + zoomList[inx].pdRadio = wMenuRadioCreate( zoomSubM, "cmdZoom", zoomList[inx].name, 0, (wMenuCallBack_p)DoZoom, (void *)(&(zoomList[inx].value))); + } + } +} + +/** + * Set radio button(s) corresponding to current scale. + * + * \param IN scale current scale + * + */ + +static void SetZoomRadio( DIST_T scale ) +{ + int inx; + long curScale = (long)scale; + + for ( inx=0; inx<sizeof zoomList/sizeof zoomList[0]; inx++ ) { + if( curScale == zoomList[inx].value ) { + + wMenuRadioSetActive( zoomList[inx].btRadio ); + if( zoomList[inx].pdRadio ) + wMenuRadioSetActive( zoomList[inx].pdRadio ); + + /* activate / deactivate zoom buttons when appropriate */ + wControlLinkedActive( (wControl_p)zoomUpB, ( inx != 0 ) ); + wControlLinkedActive( (wControl_p)zoomDownB, ( inx < (sizeof zoomList/sizeof zoomList[0] - 1))); + } + } +} + +/** + * Find current scale + * + * \param IN scale current scale + * \return index in scale table or -1 if error + * + */ + +static int ScaleInx( DIST_T scale ) +{ + int inx; + + for ( inx=0; inx<sizeof zoomList/sizeof zoomList[0]; inx++ ) { + if( scale == zoomList[inx].value ) { + return inx; + } + } + return -1; +} + +/** + * Set up for new drawing scale. After the scale was changed, eg. via zoom button, everything + * is set up for the new scale. + * + * \param scale IN new scale + */ + +static void DoNewScale( DIST_T scale ) +{ + char tmp[20]; + + if (scale > MAX_MAIN_SCALE) + scale = MAX_MAIN_SCALE; + + DrawHilight( &mapD, mainD.orig, mainD.size ); +#ifdef LATER + center.x = mainD.orig.x + mainD.size.x/2.0; + center.y = mainD.orig.y + mainD.size.y/2.0; +#endif + tempD.scale = mainD.scale = scale; + mainD.dpi = wDrawGetDPI( mainD.d ); + if ( mainD.dpi == 75 ) { + mainD.dpi = 72.0; + } else if ( scale > 1.0 && scale <= 12.0 ) { + mainD.dpi = floor( (mainD.dpi + scale/2)/scale) * scale; + } + tempD.dpi = mainD.dpi; + + SetZoomRadio( scale ); + InfoScale(); + SetMainSize(); + mainD.orig.x = mainCenter.x - mainD.size.x/2.0; + mainD.orig.y = mainCenter.y - mainD.size.y/2.0; + ConstraintOrig( &mainD.orig, mainD.size ); + MainRedraw(); + tempD.orig = mainD.orig; +LOG( log_zoom, 1, ( "center = [%0.3f %0.3f]\n", mainCenter.x, mainCenter.y ) ) + /*SetFont(0);*/ + sprintf( tmp, "%0.3f", mainD.scale ); + wPrefSetString( "draw", "zoom", tmp ); + DrawHilight( &mapD, mainD.orig, mainD.size ); + if (recordF) { + fprintf( recordF, "ORIG %0.3f %0.3f %0.3f\n", + mainD.scale, mainD.orig.x, mainD.orig.y ); + } +} + + +/** + * User selected zoom in, via mouse wheel, button or pulldown. + * + * \param mode IN FALSE if zoom button was activated, TRUE if activated via popup or mousewheel + */ + +EXPORT void DoZoomUp( void * mode ) +{ + long newScale; + int i; + + if ( mode != NULL || (MyGetKeyState()&WKEY_SHIFT) == 0 ) { + i = ScaleInx( mainD.scale ); + /* + * Zooming into macro mode happens when we are at scale 1:1. + * To jump into macro mode, the CTRL-key has to be pressed and held. + */ + if( mainD.scale != 1.0 || (mainD.scale == 1.0 && (MyGetKeyState()&WKEY_CTRL))) { + if( i ) + DoNewScale( zoomList[ i - 1 ].value ); + } + } else if ( (MyGetKeyState()&WKEY_CTRL) == 0 ) { + wPrefGetInteger( "misc", "zoomin", &newScale, 4 ); + DoNewScale( newScale ); + } else { + wPrefSetInteger( "misc", "zoomin", (long)mainD.scale ); + InfoMessage( _("Zoom In Program Value %ld:1"), (long)mainD.scale ); + } +} + + +/** + * User selected zoom out, via mouse wheel, button or pulldown. + * + * \param mode IN FALSE if zoom button was activated, TRUE if activated via popup or mousewheel + */ + +EXPORT void DoZoomDown( void * mode) +{ + long newScale; + int i; + + if ( mode != NULL || (MyGetKeyState()&WKEY_SHIFT) == 0 ) { + i = ScaleInx( mainD.scale ); + if( i>= 0 && i < ( sizeof zoomList/sizeof zoomList[0] - 1 )) + DoNewScale( zoomList[ i + 1 ].value ); + + } else if ( (MyGetKeyState()&WKEY_CTRL) == 0 ) { + wPrefGetInteger( "misc", "zoomout", &newScale, 16 ); + DoNewScale( newScale ); + } else { + wPrefSetInteger( "misc", "zoomout", (long)mainD.scale ); + InfoMessage( _("Zoom Out Program Value %ld:1"), (long)mainD.scale ); + } +} + +/** + * Zoom to user selected value. This is the callback function for the + * user-selectable preset zoom values. + * + * \param IN scale current pScale + * + */ + +EXPORT void DoZoom( DIST_T *pScale ) +{ + DIST_T scale = *pScale; + + if( scale != mainD.scale ) + DoNewScale( scale ); +} + + + +EXPORT void Pix2CoOrd( + drawCmd_p d, + wPos_t x, + wPos_t y, + coOrd * pos ) +{ + pos->x = (((DIST_T)x)/d->dpi)*d->scale+d->orig.x; + pos->y = (((DIST_T)y)/d->dpi)*d->scale+d->orig.y; +} + +EXPORT void CoOrd2Pix( + drawCmd_p d, + coOrd pos, + wPos_t * x, + wPos_t * y ) +{ + *x = (wPos_t)((pos.x-d->orig.x)/d->scale*d->dpi); + *y = (wPos_t)((pos.y-d->orig.y)/d->scale*d->dpi); +} + + +static void DoMapPan( wAction_t action, coOrd pos ) +{ + static coOrd mapOrig; + static coOrd oldOrig, newOrig; + static coOrd size; + static DIST_T xscale, yscale; + static enum { noPan, movePan, resizePan } mode = noPan; + wPos_t x, y; + + switch (action & 0xFF) { + + case C_DOWN: + if ( mode == noPan ) + mode = movePan; + else + break; + mapOrig = pos; + size = mainD.size; + newOrig = oldOrig = mainD.orig; +LOG( log_pan, 1, ( "ORIG = [ %0.3f, %0.3f ]\n", mapOrig.x, mapOrig.y ) ) + break; + case C_MOVE: + if ( mode != movePan ) + break; + DrawHilight( &mapD, newOrig, size ); +LOG( log_pan, 2, ( "NEW = [ %0.3f, %0.3f ] \n", pos.x, pos.y ) ) + newOrig.x = oldOrig.x + pos.x-mapOrig.x; + newOrig.y = oldOrig.y + pos.y-mapOrig.y; + ConstraintOrig( &newOrig, mainD.size ); + if (liveMap) { + tempD.orig = mainD.orig = newOrig; + MainRedraw(); + } + DrawHilight( &mapD, newOrig, size ); + break; + case C_UP: + if ( mode != movePan ) + break; + tempD.orig = mainD.orig = newOrig; + mainCenter.x = newOrig.x + mainD.size.x/2.0; + mainCenter.y = newOrig.y + mainD.size.y/2.0; + if (!liveMap) + MainRedraw(); +LOG( log_pan, 1, ( "FINAL = [ %0.3f, %0.3f ]\n", pos.x, pos.y ) ) +#ifdef LATER + if (recordF) { + fprintf( recordF, "ORIG %0.3f %0.3f %0.3f\n", + mainD.scale, mainD.orig.x, mainD.orig.y ); + } +#endif + mode = noPan; + break; + + case C_RDOWN: + if ( mode == noPan ) + mode = resizePan; + else + break; + DrawHilight( &mapD, mainD.orig, mainD.size ); + newOrig = pos; + oldOrig = newOrig; +#ifdef LATER + xscale = INIT_MAP_SCALE; + size.x = mapD.size.x/xscale; + size.y = mapD.size.y/xscale; +#endif + xscale = 1; + size.x = mainD.size.x/mainD.scale; + size.y = mainD.size.y/mainD.scale; + newOrig.x -= size.x/2.0; + newOrig.y -= size.y/2.0; + DrawHilight( &mapD, newOrig, size ); + break; + + case C_RMOVE: + if ( mode != resizePan ) + break; + DrawHilight( &mapD, newOrig, size ); + if (pos.x < 0) + pos.x = 0; + if (pos.x > mapD.size.x) + pos.x = mapD.size.x; + if (pos.y < 0) + pos.y = 0; + if (pos.y > mapD.size.y) + pos.y = mapD.size.y; + size.x = (pos.x - oldOrig.x)*2.0; + size.y = (pos.y - oldOrig.y)*2.0; + if (size.x < 0) { + size.x = - size.x; + } + if (size.y < 0) { + size.y = - size.y; + } + xscale = size.x / (mainD.size.x/mainD.scale); + yscale = size.y / (mainD.size.y/mainD.scale); + if (xscale < yscale) + xscale = yscale; + xscale = ceil( xscale ); + if (xscale < 1) + xscale = 1; + if (xscale > 64) + xscale = 64; + size.x = (mainD.size.x/mainD.scale) * xscale; + size.y = (mainD.size.y/mainD.scale) * xscale; + newOrig = oldOrig; + newOrig.x -= size.x/2.0; + newOrig.y -= size.y/2.0; + DrawHilight( &mapD, newOrig, size ); + break; + + case C_RUP: + if ( mode != resizePan ) + break; + tempD.size = mainD.size = size; + tempD.orig = mainD.orig = newOrig; + mainCenter.x = newOrig.x + mainD.size.x/2.0; + mainCenter.y = newOrig.y + mainD.size.y/2.0; + DoNewScale( xscale ); + mode = noPan; + break; + + case wActionExtKey: + mainD.CoOrd2Pix(&mainD,pos,&x,&y); + switch ((wAccelKey_e)(action>>8)) { +#ifndef WINDOWS + case wAccelKey_Pgdn: + DoZoomUp(NULL); + return; + case wAccelKey_Pgup: + DoZoomDown(NULL); + return; + case wAccelKey_F5: + MainRedraw(); + return; +#endif + case wAccelKey_Right: + DrawHilight( &mapD, mainD.orig, mainD.size ); + mainD.orig.x += mainD.size.x/2; + ConstraintOrig( &mainD.orig, mainD.size ); + mainCenter.x = mainD.orig.x + mainD.size.x/2.0; + mainCenter.y = mainD.orig.y + mainD.size.y/2.0; + MainRedraw(); + DrawHilight( &mapD, mainD.orig, mainD.size ); + break; + case wAccelKey_Left: + DrawHilight( &mapD, mainD.orig, mainD.size ); + mainD.orig.x -= mainD.size.x/2; + ConstraintOrig( &mainD.orig, mainD.size ); + mainCenter.x = mainD.orig.x + mainD.size.x/2.0; + mainCenter.y = mainD.orig.y + mainD.size.y/2.0; + MainRedraw(); + DrawHilight( &mapD, mainD.orig, mainD.size ); + break; + case wAccelKey_Up: + DrawHilight( &mapD, mainD.orig, mainD.size ); + mainD.orig.y += mainD.size.y/2; + ConstraintOrig( &mainD.orig, mainD.size ); + mainCenter.x = mainD.orig.x + mainD.size.x/2.0; + mainCenter.y = mainD.orig.y + mainD.size.y/2.0; + MainRedraw(); + DrawHilight( &mapD, mainD.orig, mainD.size ); + break; + case wAccelKey_Down: + DrawHilight( &mapD, mainD.orig, mainD.size ); + mainD.orig.y -= mainD.size.y/2; + ConstraintOrig( &mainD.orig, mainD.size ); + mainCenter.x = mainD.orig.x + mainD.size.x/2.0; + mainCenter.y = mainD.orig.y + mainD.size.y/2.0; + MainRedraw(); + DrawHilight( &mapD, mainD.orig, mainD.size ); + break; + default: + return; + } + mainD.Pix2CoOrd( &mainD, x, y, &pos ); + InfoPos( pos ); + return; + default: + return; + } +} + + +EXPORT BOOL_T IsClose( + DIST_T d ) +{ + wPos_t pd; + pd = (wPos_t)(d/mainD.scale * mainD.dpi); + return pd <= closePixels; +} + +/***************************************************************************** + * + * MAIN MOUSE HANDLER + * + */ + +static int ignoreMoves = 1; + +EXPORT void ResetMouseState( void ) +{ + mouseState = mouseNone; +} + + +EXPORT void FakeDownMouseState( void ) +{ + mouseState = mouseLeftPending; +} + +/** + * Return the current position of the mouse pointer in drawing coordinates. + * + * \param x OUT pointer x position + * \param y OUT pointer y position + * \return + */ + +void +GetMousePosition( int *x, int *y ) +{ + if( x && y ) { + *x = mousePositionx; + *y = mousePositiony; + } +} + +static void DoMouse( wAction_t action, coOrd pos ) +{ + + BOOL_T rc; + wPos_t x, y; + static BOOL_T ignoreCommands; + + LOG( log_mouse, 2, ( "DoMouse( %d, %0.3f, %0.3f )\n", action, pos.x, pos.y ) ) + + if (recordF) { + RecordMouse( "MOUSE", action, pos.x, pos.y ); + } + + switch (action&0xFF) { + case C_UP: + if (mouseState != mouseLeft) + return; + if (ignoreCommands) { + ignoreCommands = FALSE; + return; + } + mouseState = mouseNone; + break; + case C_RUP: + if (mouseState != mouseRight) + return; + if (ignoreCommands) { + ignoreCommands = FALSE; + return; + } + mouseState = mouseNone; + break; + case C_MOVE: + if (mouseState == mouseLeftPending ) { + action = C_DOWN; + mouseState = mouseLeft; + } + if (mouseState != mouseLeft) + return; + if (ignoreCommands) + return; + break; + case C_RMOVE: + if (mouseState != mouseRight) + return; + if (ignoreCommands) + return; + break; + case C_DOWN: + mouseState = mouseLeft; + break; + case C_RDOWN: + mouseState = mouseRight; + break; + } + + inError = FALSE; + if ( deferSubstituteControls[0] ) + InfoSubstituteControls( deferSubstituteControls, deferSubstituteLabels ); + + switch ( action&0xFF ) { + case C_DOWN: + case C_RDOWN: + tempSegs_da.cnt = 0; + break; + case wActionMove: + InfoPos( pos ); + if ( ignoreMoves ) + return; + break; + case wActionExtKey: + mainD.CoOrd2Pix(&mainD,pos,&x,&y); + switch ((wAccelKey_e)(action>>8)) { + case wAccelKey_Del: + SelectDelete(); + return; +#ifndef WINDOWS + case wAccelKey_Pgdn: + DoZoomUp(NULL); + break; + case wAccelKey_Pgup: + DoZoomDown(NULL); + break; +#endif + case wAccelKey_Right: + DrawHilight( &mapD, mainD.orig, mainD.size ); + mainD.orig.x += mainD.size.x/2; + ConstraintOrig( &mainD.orig, mainD.size ); + mainCenter.x = mainD.orig.x + mainD.size.x/2.0; + mainCenter.y = mainD.orig.y + mainD.size.y/2.0; + MainRedraw(); + DrawHilight( &mapD, mainD.orig, mainD.size ); + break; + case wAccelKey_Left: + DrawHilight( &mapD, mainD.orig, mainD.size ); + mainD.orig.x -= mainD.size.x/2; + ConstraintOrig( &mainD.orig, mainD.size ); + mainCenter.x = mainD.orig.x + mainD.size.x/2.0; + mainCenter.y = mainD.orig.y + mainD.size.y/2.0; + MainRedraw(); + DrawHilight( &mapD, mainD.orig, mainD.size ); + break; + case wAccelKey_Up: + DrawHilight( &mapD, mainD.orig, mainD.size ); + mainD.orig.y += mainD.size.y/2; + ConstraintOrig( &mainD.orig, mainD.size ); + mainCenter.x = mainD.orig.x + mainD.size.x/2.0; + mainCenter.y = mainD.orig.y + mainD.size.y/2.0; + MainRedraw(); + DrawHilight( &mapD, mainD.orig, mainD.size ); + break; + case wAccelKey_Down: + DrawHilight( &mapD, mainD.orig, mainD.size ); + mainD.orig.y -= mainD.size.y/2; + ConstraintOrig( &mainD.orig, mainD.size ); + mainCenter.x = mainD.orig.x + mainD.size.x/2.0; + mainCenter.y = mainD.orig.y + mainD.size.y/2.0; + MainRedraw(); + DrawHilight( &mapD, mainD.orig, mainD.size ); + break; + default: + return; + } + mainD.Pix2CoOrd( &mainD, x, y, &pos ); + InfoPos( pos ); + return; + case C_TEXT: + if ((action>>8) == 0x0D) + action = C_OK; + else if ((action>>8) == 0x1B) { + ConfirmReset( TRUE ); + return; + } + case C_MOVE: + case C_UP: + case C_RMOVE: + case C_RUP: + InfoPos( pos ); + /*DrawTempTrack();*/ + break; + case C_WUP: + DoZoomUp((void *)1L); + break; + case C_WDOWN: + DoZoomDown((void *)1L); + break; + default: + NoticeMessage( MSG_DOMOUSE_BAD_OP, _("Ok"), NULL, action&0xFF ); + break; + } + if (delayUpdate) + wDrawDelayUpdate( mainD.d, TRUE ); + rc = DoCurCommand( action, pos ); + wDrawDelayUpdate( mainD.d, FALSE ); + switch( rc ) { + case C_CONTINUE: + /*DrawTempTrack();*/ + break; + case C_ERROR: + ignoreCommands = TRUE; + inError = TRUE; + Reset(); + LOG( log_mouse, 1, ( "Mouse returns Error\n" ) ) + break; + case C_TERMINATE: + Reset(); + DoCurCommand( C_START, zero ); + break; + case C_INFO: + Reset(); + break; + } +} + + +wPos_t autoPanFactor = 10; +static void DoMousew( wDraw_p d, void * context, wAction_t action, wPos_t x, wPos_t y ) +{ + coOrd pos; + coOrd orig; + wPos_t w, h; + static wPos_t lastX, lastY; + DIST_T minDist; + + if ( autoPan && !inPlayback ) { + wDrawGetSize( mainD.d, &w, &h ); + if ( action == wActionLDown || action == wActionRDown || + (action == wActionLDrag && mouseState == mouseLeftPending ) /*|| + (action == wActionRDrag && mouseState == mouseRightPending ) */ ) { + lastX = x; + lastY = y; + } + if ( action == wActionLDrag || action == wActionRDrag ) { + orig = mainD.orig; + if ( ( x < 10 && x < lastX ) || + ( x > w-10 && x > lastX ) || + ( y < 10 && y < lastY ) || + ( y > h-10 && y > lastY ) ) { + mainD.Pix2CoOrd( &mainD, x, y, &pos ); + orig.x = mainD.orig.x + (pos.x - (mainD.orig.x + mainD.size.x/2.0) )/autoPanFactor; + orig.y = mainD.orig.y + (pos.y - (mainD.orig.y + mainD.size.y/2.0) )/autoPanFactor; + if ( orig.x != mainD.orig.x || orig.y != mainD.orig.y ) { + if ( mainD.scale >= 1 ) { + if ( units == UNITS_ENGLISH ) + minDist = 1.0; + else + minDist = 1.0/2.54; + if ( orig.x != mainD.orig.x ) { + if ( fabs( orig.x-mainD.orig.x ) < minDist ) { + if ( orig.x < mainD.orig.x ) + orig.x -= minDist; + else + orig.x += minDist; + } + } + if ( orig.y != mainD.orig.y ) { + if ( fabs( orig.y-mainD.orig.y ) < minDist ) { + if ( orig.y < mainD.orig.y ) + orig.y -= minDist; + else + orig.y += minDist; + } + } + } + ConstraintOrig( &orig, mainD.size ); + if ( orig.x != mainD.orig.x || orig.y != mainD.orig.y ) { + DrawMapBoundingBox( FALSE ); + mainD.orig = orig; + MainRedraw(); + /*DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );*/ + DrawMapBoundingBox( TRUE ); + wFlush(); + } + } + } + lastX = x; + lastY = y; + } + } + mainD.Pix2CoOrd( &mainD, x, y, &pos ); + mousePositionx = x; + mousePositiony = y; + + DoMouse( action, pos ); +} + +static wBool_t PlaybackMain( char * line ) +{ + int rc; + int action; + coOrd pos; + char *oldLocale = NULL; + + oldLocale = SaveLocale("C"); + rc=sscanf( line, "%d " SCANF_FLOAT_FORMAT SCANF_FLOAT_FORMAT, &action, &pos.x, &pos.y); + RestoreLocale(oldLocale); + + if (rc != 3) { + SyntaxError( "MOUSE", rc, 3 ); + } else { + PlaybackMouse( DoMouse, &mainD, (wAction_t)action, pos, wDrawColorBlack ); + } + return TRUE; +} + +/***************************************************************************** + * + * INITIALIZATION + * + */ + +static paramDrawData_t mapDrawData = { 100, 100, (wDrawRedrawCallBack_p)MapRedraw, DoMapPan, &mapD }; +static paramData_t mapPLs[] = { + { PD_DRAW, NULL, "canvas", 0, &mapDrawData } }; +static paramGroup_t mapPG = { "map", PGO_NODEFAULTPROC, mapPLs, sizeof mapPLs/sizeof mapPLs[0] }; + +static void MapDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + if ( inx == -1 ) { + mapVisible = FALSE; + } +} + + +static void DrawChange( long changes ) +{ + if (changes & CHANGE_MAIN) + MainRedraw(); + if (changes &CHANGE_UNITS) + SetInfoBar(); + if (changes & CHANGE_MAP) + MapResize(); +} + + +EXPORT void DrawInit( int initialZoom ) +{ + wPos_t w, h; + + wWinGetSize( mainW, &w, &h ); + /*LayoutToolBar();*/ + h -= toolbarHeight+infoHeight; + if ( w <= 0 ) w = 1; + if ( h <= 0 ) h = 1; + tempD.d = mainD.d = wDrawCreate( mainW, 0, toolbarHeight, "", BD_TICKS, + w, h, &mainD, + (wDrawRedrawCallBack_p)MainRedraw, DoMousew ); + + if (initialZoom == 0) { + WDOUBLE_T tmpR; + wPrefGetFloat( "draw", "zoom", &tmpR, mainD.scale ); + mainD.scale = tmpR; + } else { + while (initialZoom > 0 && mainD.scale < MAX_MAIN_SCALE) { + mainD.scale *= 2; + initialZoom--; + } + while (initialZoom < 0 && mainD.scale > MIN_MAIN_SCALE) { + mainD.scale /= 2; + initialZoom++; + } + } + tempD.scale = mainD.scale; + mainD.dpi = wDrawGetDPI( mainD.d ); + if ( mainD.dpi == 75 ) { + mainD.dpi = 72.0; + } else if ( mainD.scale > 1.0 && mainD.scale <= 12.0 ) { + mainD.dpi = floor( (mainD.dpi + mainD.scale/2)/mainD.scale) * mainD.scale; + } + tempD.dpi = mainD.dpi; + + SetMainSize(); + mapD.scale = mapScale; + /*w = (wPos_t)((mapD.size.x/mapD.scale)*mainD.dpi + 0.5)+2;*/ + /*h = (wPos_t)((mapD.size.y/mapD.scale)*mainD.dpi + 0.5)+2;*/ + ParamRegister( &mapPG ); + mapW = ParamCreateDialog( &mapPG, MakeWindowTitle(_("Map")), NULL, NULL, NULL, FALSE, NULL, 0, MapDlgUpdate ); + ChangeMapScale(); + + log_pan = LogFindIndex( "pan" ); + log_zoom = LogFindIndex( "zoom" ); + log_mouse = LogFindIndex( "mouse" ); + AddPlaybackProc( "MOUSE ", (playbackProc_p)PlaybackMain, NULL ); + + rulerFp = wStandardFont( F_HELV, FALSE, FALSE ); + + SetZoomRadio( mainD.scale ); + InfoScale(); + SetInfoBar(); + InfoPos( zero ); + RegisterChangeNotification( DrawChange ); +#ifdef LATER + wAttachAccelKey( wAccelKey_Pgup, 0, (wAccelKeyCallBack_p)doZoomUp, NULL ); + wAttachAccelKey( wAccelKey_Pgdn, 0, (wAccelKeyCallBack_p)doZoomDown, NULL ); +#endif +} diff --git a/app/bin/draw.h b/app/bin/draw.h new file mode 100644 index 0000000..6f9f1ea --- /dev/null +++ b/app/bin/draw.h @@ -0,0 +1,208 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/draw.h,v 1.4 2008-10-11 06:03:06 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 DRAW_H +#define DRAW_H + +#define MSG_BASE (1000) +#include "messages.h" + +#define DC_TICKS (1<<1) +#define DC_PRINT (1<<2) +#define DC_NOCLIP (1<<3) +#define DC_QUICK (1<<4) +#define DC_DASH (1<<5) +#define DC_SIMPLE (1<<6) +#define DC_GROUP (1<<7) +#define DC_CENTERLINE (1<<8) +#define DC_SEGTRACK (1<<9) +#define DC_TIES (1<<10) + +typedef struct drawCmd_t * drawCmd_p; + +typedef struct { + long options; + void (*drawLine)( drawCmd_p, coOrd, coOrd, wDrawWidth, wDrawColor ); + void (*drawArc)( drawCmd_p, coOrd, DIST_T, ANGLE_T, ANGLE_T, BOOL_T, wDrawWidth, wDrawColor ); + void (*drawString)( drawCmd_p, coOrd, ANGLE_T, char *, wFont_p, FONTSIZE_T, wDrawColor ); + void (*drawBitMap)( drawCmd_p, coOrd, wDrawBitMap_p, wDrawColor ); + void (*drawFillPoly) (drawCmd_p, int, coOrd *, wDrawColor ); + void (*drawFillCircle) (drawCmd_p, coOrd, DIST_T, wDrawColor ); + } drawFuncs_t; + +typedef void (*drawConvertPix2CoOrd)( drawCmd_p, wPos_t, wPos_t, coOrd * ); +typedef void (*drawConvertCoOrd2Pix)( drawCmd_p, coOrd, wPos_t *, wPos_t * ); +typedef struct drawCmd_t { + wDraw_p d; + drawFuncs_t * funcs; + long options; + DIST_T scale; + ANGLE_T angle; + coOrd orig; + coOrd size; + drawConvertPix2CoOrd Pix2CoOrd; + drawConvertCoOrd2Pix CoOrd2Pix; + FLOAT_T dpi; + } drawCmd_t; + +#define SCALEX(D,X) ((X)/(D).dpi) +#define SCALEY(D,Y) ((Y)/(D).dpi) + +#ifdef WINDOWS +#define LBORDER (33) +#define BBORDER (32) +#else +#define LBORDER (26) +#define BBORDER (27) +#endif +#define RBORDER (9) +#define TBORDER (8) + +#ifdef LATER +#define Pix2CoOrd( D, pos, X, Y ) { \ + pos.x = ((long)(((POS_T)((X)-LBORDER)*pixelBins)/D.dpi))/pixelBins * D.scale + D.orig.x; \ + pos.y = ((long)(((POS_T)((Y)-BBORDER)*pixelBins)/D.dpi))/pixelBins * D.scale + D.orig.y; \ + } +#endif +void Pix2CoOrd( drawCmd_p, wPos_t, wPos_t, coOrd * ); +void CoOrd2Pix( drawCmd_p, coOrd, wPos_t *, wPos_t * ); + +extern BOOL_T inError; +extern DIST_T pixelBins; +extern wWin_p mapW; +extern BOOL_T mapVisible; +extern drawCmd_t mainD; +extern coOrd mainCenter; +extern drawCmd_t mapD; +extern drawCmd_t tempD; +#define RoomSize (mapD.size) +extern coOrd oldMarker; +extern wPos_t closePixels; +#define dragDistance (dragPixels*mainD.scale / mainD.dpi) +extern long dragPixels; +extern long dragTimeout; +extern long autoPan; +extern long minGridSpacing; +extern long drawCount; +extern BOOL_T drawEnable; +extern long currRedraw; + +extern wDrawColor drawColorBlack; +extern wDrawColor drawColorWhite; +extern wDrawColor drawColorRed; +extern wDrawColor drawColorBlue; +extern wDrawColor drawColorGreen; +extern wDrawColor drawColorAqua; +extern wDrawColor drawColorPurple; +extern wDrawColor drawColorGold; +#define wDrawColorBlack drawColorBlack +#define wDrawColorWhite drawColorWhite +#define wDrawColorBlue drawColorBlue + +extern wDrawColor markerColor; +extern wDrawColor borderColor; +extern wDrawColor crossMajorColor; +extern wDrawColor crossMinorColor; +extern wDrawColor snapGridColor; +extern wDrawColor selectedColor; +extern wDrawColor profilePathColor; + +BOOL_T IsClose( DIST_T ); + +drawFuncs_t screenDrawFuncs; +drawFuncs_t tempDrawFuncs; +drawFuncs_t tempSegDrawFuncs; +drawFuncs_t printDrawFuncs; + +#define DrawLine( D, P0, P1, W, C ) (D)->funcs->drawLine( D, P0, P1, W, C ) +#define DrawArc( D, P, R, A0, A1, F, W, C ) (D)->funcs->drawArc( D, P, R, A0, A1, F, W, C ) +#define DrawString( D, P, A, S, FP, FS, C ) (D)->funcs->drawString( D, P, A, S, FP, FS, C ) +#define DrawBitMap( D, P, B, C ) (D)->funcs->drawBitMap( D, P, B, C ) +#define DrawFillPoly( D, N, P, C ) (D)->funcs->drawFillPoly( D, N, P, C ); +#define DrawFillCircle( D, P, R, C ) (D)->funcs->drawFillCircle( D, P, R, C ); + +#define REORIGIN( Q, P, A, O ) { \ + (Q) = (P); \ + REORIGIN1( Q, A, O ) \ + } +#define REORIGIN1( Q, A, O ) { \ + if ( (A) != 0.0 ) \ + Rotate( &(Q), zero, (A) ); \ + (Q).x += (O).x; \ + (Q).y += (O).y; \ + } +#define OFF_D( ORIG, SIZE, LO, HI ) \ + ( (HI).x < (ORIG).x || \ + (LO).x > (ORIG).x+(SIZE).x || \ + (HI).y < (ORIG).y || \ + (LO).y > (ORIG).y+(SIZE).y ) +#define OFF_MAIND( LO, HI ) \ + OFF_D( mainD.orig, mainD.size, LO, HI ) + +void DrawHilight( drawCmd_p, coOrd, coOrd ); +void DrawHilightPolygon( drawCmd_p, coOrd *, int ); +#define BOX_NONE (0) +#define BOX_UNDERLINE (1) +#define BOX_BOX (2) +#define BOX_INVERT (3) +#define BOX_ARROW (4) +#define BOX_BACKGROUND (5) +void DrawBoxedString( int, drawCmd_p, coOrd, char *, wFont_p, wFontSize_t, wDrawColor, ANGLE_T ); +void DrawTextSize2( drawCmd_p, char *, wFont_p, wFontSize_t, BOOL_T, coOrd *, POS_T * ); +void DrawTextSize( drawCmd_p, char *, wFont_p, wFontSize_t, BOOL_T, coOrd * ); +BOOL_T SetRoomSize( coOrd ); +void GetRoomSize( coOrd * ); +void DoRedraw( void ); +void SetMainSize( void ); +void MainRedraw( void ); +void DrawMarkers( void ); +void DrawMapBoundingBox( BOOL_T ); +void DrawTicks( drawCmd_p, coOrd ); +void DrawRuler( drawCmd_p, coOrd, coOrd, DIST_T, int, int, wDrawColor ); +void MainProc( wWin_p, winProcEvent, void * ); +void InitInfoBar( void ); +void DrawInit( int ); +void DoZoomUp( void * ); +void DoZoomDown( void * ); +void DoZoom( DIST_T * ); + +void InitCmdZoom( wMenu_p, wMenu_p ); + +void InfoPos( coOrd ); +void InfoCount( wIndex_t ); +void SetMessage( char * ); + +void InfoSubstituteControls( wControl_p *, char * * ); + +void MapGrid( coOrd, coOrd, ANGLE_T, coOrd, ANGLE_T, POS_T, POS_T, int *, int *, int *, int * ); +void DrawGrid( drawCmd_p, coOrd *, POS_T, POS_T, long, long, coOrd, ANGLE_T, wDrawColor, BOOL_T ); +STATUS_T GridAction( wAction_t, coOrd, coOrd *, DIST_T * ); + +void ResetMouseState( void ); +void FakeDownMouseState( void ); +void GetMousePosition( int *x, int *y ); +void RecordMouse( char *, wAction_t, POS_T, POS_T ); +extern long playbackDelay; +void MovePlaybackCursor( drawCmd_p, wPos_t, wPos_t ); +typedef void (*playbackProc)( wAction_t, coOrd ); +void PlaybackMouse( playbackProc, drawCmd_p, wAction_t, coOrd, wDrawColor ); +#endif diff --git a/app/bin/drawgeom.c b/app/bin/drawgeom.c new file mode 100644 index 0000000..8ef31e8 --- /dev/null +++ b/app/bin/drawgeom.c @@ -0,0 +1,721 @@ + +/* 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 <stdarg.h> +#include "track.h" +#include "ccurve.h" +#include "compound.h" +#include "drawgeom.h" +#include "i18n.h" + +/*EXPORT drawContext_t * drawContext;*/ +static long drawGeomCurveMode; + +#define contextSegs(N) DYNARR_N( trkSeg_t, context->Segs_da, N ) + + + +static dynArr_t points_da; +#define points(N) DYNARR_N( coOrd, points_da, N ) + +static void EndPoly( drawContext_t * context, int cnt ) +{ + trkSeg_p segPtr; + track_p trk; + long oldOptions; + coOrd * pts; + int inx; + + if (context->State==0 || cnt == 0) + return; + + oldOptions = context->D->funcs->options; + context->D->funcs->options |= wDrawOptTemp; + DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + context->D->funcs->options = oldOptions; + + if (IsClose(FindDistance(tempSegs(0).u.l.pos[0], tempSegs(cnt-1).u.l.pos[1] ))) + cnt--; + if ( cnt < 2 ) { + tempSegs_da.cnt = 0; + ErrorMessage( MSG_POLY_SHAPES_3_SIDES ); + return; + } + pts = (coOrd*)MyMalloc( (cnt+1) * sizeof *(coOrd*)NULL ); + for ( inx=0; inx<cnt; inx++ ) + pts[inx] = tempSegs(inx).u.l.pos[0]; + pts[cnt] = tempSegs(cnt-1).u.l.pos[1]; + DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); + segPtr = &tempSegs(0); + segPtr->type = ( context->Op == OP_POLY ? SEG_POLY: SEG_FILPOLY ); + segPtr->u.p.cnt = cnt+1; + segPtr->u.p.pts = pts; + segPtr->u.p.angle = 0.0; + segPtr->u.p.orig = zero; + UndoStart( _("Create Lines"), "newDraw" ); + trk = MakeDrawFromSeg( zero, 0.0, segPtr ); + DrawNewTrack( trk ); + tempSegs_da.cnt = 0; +} + + + +static void DrawGeomOk( void ) +{ + track_p trk; + int inx; + + if (tempSegs_da.cnt <= 0) + return; + UndoStart( _("Create Lines"), "newDraw" ); + for ( inx=0; inx<tempSegs_da.cnt; inx++ ) { + trk = MakeDrawFromSeg( zero, 0.0, &tempSegs(inx) ); + DrawNewTrack( trk ); + } + tempSegs_da.cnt = 0; +} + +/** + * Create and draw a graphics primitive (lines, arc, circle). The complete handling of mouse + * movements and clicks during the editing process is done here. + * + * \param action IN mouse action + * \param pos IN position of mouse pointer + * \param context IN/OUT parameters for drawing op + * \return next command state + */ + +STATUS_T DrawGeomMouse( + wAction_t action, + coOrd pos, + drawContext_t *context ) +{ + static int lastValid = FALSE; + static coOrd pos0, pos0x, pos1, lastPos; + trkSeg_p segPtr; + coOrd *pts; + int inx; + DIST_T width; + static int segCnt; + DIST_T d; + BOOL_T createTrack; + long oldOptions; + + width = context->Width/context->D->dpi; + + switch (action&0xFF) { + + case C_START: + context->State = 0; + context->Changed = FALSE; + segCnt = 0; + DYNARR_RESET( trkSeg_t, tempSegs_da ); + return C_CONTINUE; + + case wActionMove: + return C_CONTINUE; + + case wActionLDown: + context->Started = TRUE; + if ((context->Op == OP_CURVE1 || context->Op == OP_CURVE2 || context->Op == OP_CURVE3 || context->Op == OP_CURVE4) && context->State == 1) { + ; + } else { + if ( (MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_CTRL ) + OnTrack( &pos, FALSE, FALSE ); + pos0 = pos; + pos1 = pos; + } + switch (context->Op) { + case OP_LINE: + case OP_DIMLINE: + case OP_BENCH: + if ( lastValid && ( MyGetKeyState() & WKEY_SHIFT ) ) { + pos = pos0 = lastPos; + } + DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); + switch (context->Op) { + case OP_LINE: tempSegs(0).type = SEG_STRLIN; break; + case OP_DIMLINE: tempSegs(0).type = SEG_DIMLIN; break; + case OP_BENCH: tempSegs(0).type = SEG_BENCH; break; + } + tempSegs(0).color = context->Color; + tempSegs(0).width = width; + tempSegs(0).u.l.pos[0] = tempSegs(0).u.l.pos[1] = pos; + if ( context->Op == OP_BENCH || context->Op == OP_DIMLINE ) { + tempSegs(0).u.l.option = context->benchOption; + } else { + tempSegs(0).u.l.option = 0; + } + tempSegs_da.cnt = 0; + context->message( _("Drag to place next end point") ); + break; + case OP_TBLEDGE: + if ( lastValid && ( MyGetKeyState() & WKEY_SHIFT ) ) { + pos = pos0 = lastPos; + } + OnTableEdgeEndPt( NULL, &pos ); + DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); + tempSegs(0).type = SEG_TBLEDGE; + tempSegs(0).color = context->Color; + tempSegs(0).width = (mainD.scale<=16)?(3/context->D->dpi*context->D->scale):0; + tempSegs(0).u.l.pos[0] = tempSegs(0).u.l.pos[1] = pos; + tempSegs_da.cnt = 0; + context->message( _("Drag to place next end point") ); + break; + case OP_CURVE1: case OP_CURVE2: case OP_CURVE3: case OP_CURVE4: + if (context->State == 0) { + switch ( context->Op ) { + case OP_CURVE1: drawGeomCurveMode = crvCmdFromEP1; break; + case OP_CURVE2: drawGeomCurveMode = crvCmdFromTangent; break; + case OP_CURVE3: drawGeomCurveMode = crvCmdFromCenter; break; + case OP_CURVE4: drawGeomCurveMode = crvCmdFromChord; break; + } + CreateCurve( C_DOWN, pos, FALSE, context->Color, width, drawGeomCurveMode, context->message ); + } else { + tempSegs_da.cnt = segCnt; + } + break; + case OP_CIRCLE1: + case OP_CIRCLE2: + case OP_CIRCLE3: + case OP_FILLCIRCLE1: + case OP_FILLCIRCLE2: + case OP_FILLCIRCLE3: + DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); + tempSegs(0).type = SEG_CRVLIN; + tempSegs(0).color = context->Color; + if ( context->Op >= OP_CIRCLE1 && context->Op <= OP_CIRCLE3 ) + tempSegs(0).width = width; + else + tempSegs(0).width = 0; + tempSegs(0).u.c.a0 = 0; + tempSegs(0).u.c.a1 = 360; + tempSegs(0).u.c.radius = 0; + tempSegs(0).u.c.center = pos; + context->message( _("Drag to set radius") ); + break; + case OP_FILLBOX: + width = 0; + case OP_BOX: + DYNARR_SET( trkSeg_t, tempSegs_da, 4 ); + for ( inx=0; inx<4; inx++ ) { + tempSegs(inx).type = SEG_STRLIN; + tempSegs(inx).color = context->Color; + tempSegs(inx).width = width; + tempSegs(inx).u.l.pos[0] = tempSegs(inx).u.l.pos[1] = pos; + } + tempSegs_da.cnt = 0; + context->message( _("Drag set box size") ); + break; + case OP_POLY: + case OP_FILLPOLY: + tempSegs_da.cnt = segCnt; + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + segPtr = &tempSegs(tempSegs_da.cnt-1); + segPtr->type = SEG_STRLIN; + segPtr->color = context->Color; + segPtr->width = (context->Op==OP_POLY?width:0); + if ( tempSegs_da.cnt == 1 ) { + segPtr->u.l.pos[0] = pos; + } else { + segPtr->u.l.pos[0] = segPtr[-1].u.l.pos[1]; + } + segPtr->u.l.pos[1] = pos; + context->State = 1; + oldOptions = context->D->funcs->options; + context->D->funcs->options |= wDrawOptTemp; + DrawSegs( context->D, zero, 0.0, &tempSegs(tempSegs_da.cnt-1), 1, trackGauge, wDrawColorBlack ); + context->D->funcs->options = oldOptions; + break; + } + return C_CONTINUE; + + case wActionLDrag: + oldOptions = context->D->funcs->options; + context->D->funcs->options |= wDrawOptTemp; + if (context->Op == OP_POLY || context->Op == OP_FILLPOLY) + DrawSegs( context->D, zero, 0.0, &tempSegs(tempSegs_da.cnt-1), 1, trackGauge, wDrawColorBlack ); + else + DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + if ( (MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_CTRL ) + OnTrack( &pos, FALSE, FALSE ); + pos1 = pos; + switch (context->Op) { + case OP_TBLEDGE: + OnTableEdgeEndPt( NULL, &pos1 ); + case OP_LINE: + case OP_DIMLINE: + case OP_BENCH: + tempSegs(0).u.l.pos[1] = pos1; + context->message( _("Length = %s, Angle = %0.2f"), + FormatDistance(FindDistance( pos0, pos1 )), + PutAngle(FindAngle( pos0, pos1 )) ); + tempSegs_da.cnt = 1; + break; + case OP_POLY: + case OP_FILLPOLY: + tempSegs(tempSegs_da.cnt-1).type = SEG_STRLIN; + tempSegs(tempSegs_da.cnt-1).u.l.pos[1] = pos; + context->message( _("Length = %s, Angle = %0.2f"), + FormatDistance(FindDistance( tempSegs(tempSegs_da.cnt-1).u.l.pos[0], pos )), + PutAngle(FindAngle( tempSegs(tempSegs_da.cnt-1).u.l.pos[0], pos )) ); + break; + case OP_CURVE1: case OP_CURVE2: case OP_CURVE3: case OP_CURVE4: + if (context->State == 0) { + pos0x = pos; + CreateCurve( C_MOVE, pos, TRUE, context->Color, width, drawGeomCurveMode, context->message ); + } else { + PlotCurve( drawGeomCurveMode, pos0, pos0x, pos1, &context->ArcData, FALSE ); + tempSegs(0).color = context->Color; + tempSegs(0).width = width; + if (context->ArcData.type == curveTypeStraight) { + tempSegs(0).type = SEG_STRLIN; + tempSegs(0).u.l.pos[0] = pos0; + tempSegs(0).u.l.pos[1] = context->ArcData.pos1; + tempSegs_da.cnt = 1; + context->message( _("Straight Line: Length=%s Angle=%0.3f"), + FormatDistance(FindDistance( pos0, context->ArcData.pos1 )), + PutAngle(FindAngle( pos0, context->ArcData.pos1 )) ); + } else if (context->ArcData.type == curveTypeNone) { + tempSegs_da.cnt = 0; + context->message( _("Back") ); + } else if (context->ArcData.type == curveTypeCurve) { + tempSegs(0).type = SEG_CRVLIN; + tempSegs(0).u.c.center = context->ArcData.curvePos; + tempSegs(0).u.c.radius = context->ArcData.curveRadius; + tempSegs(0).u.c.a0 = context->ArcData.a0; + tempSegs(0).u.c.a1 = context->ArcData.a1; + tempSegs_da.cnt = 1; + d = D2R(context->ArcData.a1); + if (d < 0.0) + d = 2*M_PI+d; + if ( d*context->ArcData.curveRadius > mapD.size.x+mapD.size.y ) { + ErrorMessage( MSG_CURVE_TOO_LARGE ); + tempSegs_da.cnt = 0; + context->ArcData.type = curveTypeNone; + context->D->funcs->options = oldOptions; + return C_CONTINUE; + } + context->message( _("Curved Line: Radius=%s Angle=%0.3f Length=%s"), + FormatDistance(context->ArcData.curveRadius), context->ArcData.a1, + FormatDistance(context->ArcData.curveRadius*d) ); + } + } + break; + case OP_CIRCLE1: + case OP_FILLCIRCLE1: + break; + case OP_CIRCLE2: + case OP_FILLCIRCLE2: + tempSegs(0).u.c.center = pos1; + case OP_CIRCLE3: + case OP_FILLCIRCLE3: + tempSegs(0).u.c.radius = FindDistance( pos0, pos1 ); + context->message( _("Radius = %s"), + FormatDistance(FindDistance( pos0, pos1 )) ); + break; + case OP_BOX: + case OP_FILLBOX: + tempSegs_da.cnt = 4; + tempSegs(0).u.l.pos[1].x = tempSegs(1).u.l.pos[0].x = + tempSegs(1).u.l.pos[1].x = tempSegs(2).u.l.pos[0].x = pos.x; + tempSegs(1).u.l.pos[1].y = tempSegs(2).u.l.pos[0].y = + tempSegs(2).u.l.pos[1].y = tempSegs(3).u.l.pos[0].y = pos.y; + context->message( _("Width = %s, Height = %s"), + FormatDistance(fabs(pos1.x - pos0.x)), FormatDistance(fabs(pos1.y - pos0.y)) ); + break; + } + if (context->Op == OP_POLY || context->Op == OP_FILLPOLY) + DrawSegs( context->D, zero, 0.0, &tempSegs(tempSegs_da.cnt-1), 1, trackGauge, wDrawColorBlack ); + else + DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + context->D->funcs->options = oldOptions; + return C_CONTINUE; + + case wActionLUp: + oldOptions = context->D->funcs->options; + context->D->funcs->options |= wDrawOptTemp; + if (context->Op != OP_POLY && context->Op != OP_FILLPOLY) + DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + lastValid = FALSE; + createTrack = FALSE; + switch ( context->Op ) { + case OP_LINE: + case OP_DIMLINE: + case OP_BENCH: + case OP_TBLEDGE: + lastValid = TRUE; + lastPos = pos1; + break; + case OP_CURVE1: case OP_CURVE2: case OP_CURVE3: case OP_CURVE4: + if (context->State == 0) { + context->State = 1; + context->ArcAngle = FindAngle( pos0, pos1 ); + pos0x = pos1; + CreateCurve( C_UP, pos, TRUE, context->Color, width, drawGeomCurveMode, context->message ); + DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + segCnt = tempSegs_da.cnt; + context->message( _("Drag on Red arrows to adjust curve") ); + context->D->funcs->options = oldOptions; + return C_CONTINUE; + } else { + tempSegs_da.cnt = 0; + if (context->ArcData.type == curveTypeCurve) { + tempSegs_da.cnt = 1; + segPtr = &tempSegs(0); + segPtr->type = SEG_CRVLIN; + segPtr->color = context->Color; + segPtr->width = width; + segPtr->u.c.center = context->ArcData.curvePos; + segPtr->u.c.radius = context->ArcData.curveRadius; + segPtr->u.c.a0 = context->ArcData.a0; + segPtr->u.c.a1 = context->ArcData.a1; + } else if (context->ArcData.type == curveTypeStraight) { + tempSegs_da.cnt = 1; + segPtr = &tempSegs(0); + segPtr->type = SEG_STRLIN; + segPtr->color = context->Color; + segPtr->width = width; + segPtr->u.l.pos[0] = pos0; + segPtr->u.l.pos[1] = pos1; + } else { + tempSegs_da.cnt = 0; + } + context->State = 0; + lastValid = TRUE; + lastPos = pos1; + /*drawContext = context; + DrawGeomOp( (void*)context->Op );*/ + } + break; + case OP_CIRCLE1: + case OP_CIRCLE2: + case OP_CIRCLE3: + case OP_FILLCIRCLE1: + case OP_FILLCIRCLE2: + case OP_FILLCIRCLE3: + if ( context->Op>=OP_FILLCIRCLE1 && context->Op<=OP_FILLCIRCLE3 ) + tempSegs(0).type = SEG_FILCRCL; + /*drawContext = context; + DrawGeomOp( (void*)context->Op );*/ + break; + case OP_BOX: + case OP_FILLBOX: + if ( context->Op == OP_FILLBOX ) { + pts = (coOrd*)MyMalloc( 4 * sizeof *(coOrd*)NULL ); + for ( inx=0; inx<4; inx++ ) + pts[inx] = tempSegs(inx).u.l.pos[0]; + tempSegs(0).type = SEG_FILPOLY; + tempSegs(0).u.p.cnt = 4; + tempSegs(0).u.p.pts = pts; + tempSegs(0).u.p.angle = 0.0; + tempSegs(0).u.p.orig = zero; + tempSegs_da.cnt = 1; + } + /*drawContext = context; + DrawGeomOp( (void*)context->Op );*/ + break; + case OP_POLY: + case OP_FILLPOLY: + segCnt = tempSegs_da.cnt; + context->D->funcs->options = oldOptions; + return C_CONTINUE; + } + context->Started = FALSE; + context->Changed = TRUE; + /*CheckOk();*/ + context->D->funcs->options = oldOptions; + DrawGeomOk(); + return C_TERMINATE; + + case wActionText: + if ( ((action>>8)&0xFF) == 0x0D || + ((action>>8)&0xFF) == ' ' ) { + EndPoly(context, segCnt); + context->State = 0; + } + return C_TERMINATE; + + case C_CANCEL: + oldOptions = context->D->funcs->options; + context->D->funcs->options |= wDrawOptTemp; + DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + context->D->funcs->options = oldOptions; + tempSegs_da.cnt = 0; + context->message( "" ); + context->Changed = FALSE; + lastValid = FALSE; + return C_TERMINATE; + + case C_REDRAW: + oldOptions = context->D->funcs->options; + context->D->funcs->options |= wDrawOptTemp; + DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + context->D->funcs->options = oldOptions; + return C_CONTINUE; + + default: + return C_CONTINUE; + } +} + + +STATUS_T DrawGeomModify( + coOrd orig, + ANGLE_T angle, + wIndex_t segCnt, + trkSeg_p segPtr, + wAction_t action, + coOrd pos, + wBool_t selected) +{ + ANGLE_T a; + coOrd p0, p1, pc; + static wIndex_t segInx; + static EPINX_T segEp; + static ANGLE_T segA1; + static int polyInx; + int inx; + DIST_T d, dd; + coOrd * newPts; + int mergePoints; + + switch ( action ) { + case C_DOWN: + segInx = -1; + DistanceSegs( orig, angle, segCnt, segPtr, &pos, &segInx ); + if (segInx == -1) + return C_ERROR; + tempSegs(0).width = segPtr[segInx].width; + tempSegs(0).color = segPtr[segInx].color; + switch ( segPtr[segInx].type ) { + case SEG_TBLEDGE: + + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + REORIGIN( p0, segPtr[segInx].u.l.pos[0], angle, orig ); + REORIGIN( p1, segPtr[segInx].u.l.pos[1], angle, orig ); + tempSegs(0).type = segPtr[segInx].type; + tempSegs(0).u.l.pos[0] = p0; + tempSegs(0).u.l.pos[1] = p1; + tempSegs(0).u.l.option = segPtr[segInx].u.l.option; + segA1 = FindAngle( p1, p0 ); + break; + case SEG_CRVLIN: + case SEG_FILCRCL: + REORIGIN( pc, segPtr[segInx].u.c.center, angle, orig ) + tempSegs(0).type = segPtr[segInx].type; + tempSegs(0).u.c.center = pc; + tempSegs(0).u.c.radius = segPtr[segInx].u.c.radius; + if (segPtr[segInx].u.c.a1 >= 360.0) { + tempSegs(0).u.c.a0 = 0.0; + tempSegs(0).u.c.a1 = 360.0; + } else { + tempSegs(0).u.c.a0 = NormalizeAngle( segPtr[segInx].u.c.a0+angle ); + tempSegs(0).u.c.a1 = segPtr[segInx].u.c.a1; + segA1 = NormalizeAngle( segPtr[segInx].u.c.a0 + segPtr[segInx].u.c.a1 + angle ); + PointOnCircle( &p0, pc, segPtr[segInx].u.c.radius, segPtr[segInx].u.c.a0+angle ); + PointOnCircle( &p1, pc, segPtr[segInx].u.c.radius, segPtr[segInx].u.c.a0+segPtr[segInx].u.c.a1+angle ); + } + + break; + case SEG_POLY: + case SEG_FILPOLY: + tempSegs(0).type = segPtr[segInx].type; + tempSegs(0).u.p.cnt = segPtr[segInx].u.p.cnt; + tempSegs(0).u.p.angle = 0.0; + tempSegs(0).u.p.orig = zero; + DYNARR_SET( coOrd, points_da, segPtr[segInx].u.p.cnt+1 ); + tempSegs(0).u.p.pts = &points(0); + d = 10000; + polyInx = 0; + for ( inx=0; inx<segPtr[segInx].u.p.cnt; inx++ ) { + REORIGIN( points(inx), segPtr[segInx].u.p.pts[inx], angle, orig ); + } + for ( inx=0; inx<segPtr[segInx].u.p.cnt; inx++ ) { + p0 = pos; + dd = LineDistance( &p0, points( inx==0?segPtr[segInx].u.p.cnt-1:inx-1), points( inx ) ); + if ( d > dd ) { + d = dd; + polyInx = inx; + } + } + inx = (polyInx==0?segPtr[segInx].u.p.cnt-1:polyInx-1); + d = FindDistance( points(inx), pos ); + dd = FindDistance( points(inx), points(polyInx) ); + if ( d < 0.25*dd ) { + polyInx = inx; + } else if ( d > 0.75*dd ) { + ; + } else { + tempSegs(0).u.p.cnt++; + for (inx=points_da.cnt-1; inx>polyInx; inx-- ) { + points(inx) = points(inx-1); + } +/*fprintf( stderr, "Inserting vertix before %d\n", polyInx );*/ + } + points(polyInx) = pos; + p1=p0; + break; + default: + ASSERT( FALSE ); /* CHECKME */ + case SEG_TEXT: + segInx = -1; + return C_ERROR; + } + if ( FindDistance( p0, pos ) < FindDistance( p1, pos ) ) + segEp = 0; + else { + segEp = 1; + switch ( segPtr[segInx].type ) { + case SEG_TBLEDGE: + + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + segA1 = NormalizeAngle( segA1 + 180.0 ); + break; + default: + ; + } + } + tempSegs_da.cnt = 1; + return C_CONTINUE; + case C_MOVE: + if (segInx == -1) + return C_ERROR; + if ( ( MyGetKeyState() & WKEY_SHIFT ) && + (tempSegs(0).type == SEG_STRLIN || tempSegs(0).type == SEG_DIMLIN || tempSegs(0).type == SEG_BENCH || tempSegs(0).type == SEG_TBLEDGE) ) { + d = FindDistance( pos, tempSegs(0).u.l.pos[1-segEp] ); + Translate( &pos, tempSegs(0).u.l.pos[1-segEp], segA1, d ); + } else if ( (MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_CTRL ) { + OnTrack( &pos, FALSE, FALSE ); + } + switch (tempSegs(0).type) { + case SEG_TBLEDGE: + + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + tempSegs(0).u.l.pos[segEp] = pos; + InfoMessage( _("Length = %0.3f Angle = %0.3f"), FindDistance( tempSegs(0).u.l.pos[segEp], tempSegs(0).u.l.pos[1-segEp] ), FindAngle( tempSegs(0).u.l.pos[1-segEp], tempSegs(0).u.l.pos[segEp] ) ); + break; + pos.x -= orig.x; + pos.y -= orig.y; + pos.x -= orig.x; + pos.y -= orig.y; + Rotate( &pos, zero, -angle ); + Rotate( &pos, zero, -angle ); + case SEG_CRVLIN: + case SEG_FILCRCL: + if (tempSegs(0).u.c.a1 >= 360.0) { + tempSegs(0).u.c.radius = FindDistance( tempSegs(0).u.c.center, pos ); + } else { + a = FindAngle( tempSegs(0).u.c.center, pos ); + if (segEp==0) { + tempSegs(0).u.c.a1 = NormalizeAngle(segA1-a); + tempSegs(0).u.c.a0 = a; + } else { + tempSegs(0).u.c.a1 = NormalizeAngle(a-tempSegs(0).u.c.a0); + } + } + break; + case SEG_POLY: + case SEG_FILPOLY: + points(polyInx) = pos; + break; + default: + ; + } + tempSegs_da.cnt = 1; + return C_CONTINUE; + case C_UP: + if (segInx == -1) + return C_CONTINUE; + switch (tempSegs(0).type) { + case SEG_TBLEDGE: + + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + pos = tempSegs(0).u.l.pos[segEp]; + pos.x -= orig.x; + pos.y -= orig.y; + Rotate( &pos, zero, -angle ); + segPtr[segInx].u.l.pos[segEp] = pos; + break; + case SEG_CRVLIN: + case SEG_FILCRCL: + if ( tempSegs(0).u.c.a1 >= 360.0 ) { + segPtr[segInx].u.c.radius = tempSegs(0).u.c.radius; + } else { + a = FindAngle( tempSegs(0).u.c.center, pos ); + a = NormalizeAngle( a-angle ); + segPtr[segInx].u.c.a1 = tempSegs(0).u.c.a1; + if (segEp == 0) { + segPtr[segInx].u.c.a0 = a; + } + } + break; + case SEG_POLY: + case SEG_FILPOLY: + mergePoints = FALSE; + if ( IsClose( FindDistance( pos, points( polyInx==0?tempSegs(0).u.p.cnt-1:polyInx-1 ) ) ) || + IsClose( FindDistance( pos, points( (polyInx==tempSegs(0).u.p.cnt-1)?0:polyInx+1 ) ) ) ) { + mergePoints = TRUE; + if (segPtr[segInx].u.p.cnt <= 3) { + ErrorMessage( MSG_POLY_SHAPES_3_SIDES ); + break; + } + } + + newPts = (coOrd*)MyMalloc( tempSegs(0).u.p.cnt * sizeof *(coOrd*)0 ); + memcpy( newPts, segPtr[segInx].u.p.pts, (segPtr[segInx].u.p.cnt) * sizeof *(coOrd*)0 ); + segPtr[segInx].u.p.pts = newPts; + + if ( tempSegs(0).u.p.cnt > segPtr[segInx].u.p.cnt ) { + ASSERT( tempSegs(0).u.p.cnt == segPtr[segInx].u.p.cnt+1 ); + for (inx=tempSegs(0).u.p.cnt-1; inx>polyInx; inx--) + segPtr[segInx].u.p.pts[inx] = segPtr[segInx].u.p.pts[inx-1]; + segPtr[segInx].u.p.cnt++; + } + + pos = points(polyInx); + if ( mergePoints ) { + for (inx=polyInx+1; inx<points_da.cnt; inx++) + segPtr[segInx].u.p.pts[inx-1] = segPtr[segInx].u.p.pts[inx]; + segPtr[segInx].u.p.cnt--; +/*fprintf( stderr, "Merging with vertix %d\n", polyInx );*/ + break; + } + pos.x -= orig.x; + pos.y -= orig.y; + Rotate( &pos, zero, -angle ); + segPtr[segInx].u.p.pts[polyInx] = pos; + break; + default: + ; + } + return C_TERMINATE; + default: + ; + } + return C_ERROR; +} diff --git a/app/bin/drawgeom.h b/app/bin/drawgeom.h new file mode 100644 index 0000000..377ebaa --- /dev/null +++ b/app/bin/drawgeom.h @@ -0,0 +1,58 @@ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2005 Dave Bullis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define OP_LINE (0) +#define OP_DIMLINE (1) +#define OP_BENCH (2) +#define OP_TBLEDGE (3) +#define OP_CURVE1 (4) +#define OP_CURVE2 (5) +#define OP_CURVE3 (6) +#define OP_CURVE4 (7) +#define OP_CIRCLE1 (8) +#define OP_CIRCLE2 (9) +#define OP_CIRCLE3 (10) +#define OP_BOX (11) +#define OP_POLY (12) +#define OP_FILLCIRCLE1 (13) +#define OP_FILLCIRCLE2 (14) +#define OP_FILLCIRCLE3 (15) +#define OP_FILLBOX (16) +#define OP_FILLPOLY (17) +#define OP_LAST (OP_FILLPOLY) + +typedef struct { + void (*message)( char *, ... ); + void (*Redraw)( void ); + drawCmd_t *D; + long Op; + wDrawColor Color; + long Width; + long benchOption; + int State; + curveData_t ArcData; + ANGLE_T ArcAngle; + int Started; + BOOL_T Changed; + } drawContext_t; + +extern drawContext_t * drawContext; +void DrawGeomOp( void * ); +STATUS_T DrawGeomMouse( wAction_t, coOrd, drawContext_t * ); +STATUS_T DrawGeomModify( coOrd, ANGLE_T, wIndex_t, trkSeg_p, wAction_t, coOrd, wBool_t ); diff --git a/app/bin/elev.c b/app/bin/elev.c new file mode 100644 index 0000000..ec232a4 --- /dev/null +++ b/app/bin/elev.c @@ -0,0 +1,1317 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/elev.c,v 1.1 2005-12-07 15:47:20 rc-flyer 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 "track.h" +#include "shrtpath.h" +#include "ccurve.h" + + +EXPORT long oldElevationEvaluation = 0; +static int log_fillElev = 0; +static int log_dumpElev = 0; +static BOOL_T log_fillElev_initted; +static int checkTrk = 0; +static char * elevPrefix; + +static void SetTrkOnElevPath( track_p trk, int mode, DIST_T elev ); + +typedef struct { + track_p trk; + EPINX_T ep; + DIST_T len; + } elist_t; +static dynArr_t elist_da; +#define elist(N) DYNARR_N( elist_t, elist_da, N ) +#define elistAppend( T, E, L ) \ + { DYNARR_APPEND( elist_t, elist_da, 10 );\ + elist(elist_da.cnt-1).trk = T; elist(elist_da.cnt-1).len = L; elist(elist_da.cnt-1).ep = E; } + +EPINX_T GetNextTrkOnPath( track_p trk, EPINX_T ep ) +{ +/* Get next track on Path: + 1 - there is only 1 connected (not counting ep) + 2 - there are >1 but only 1 on Path + 3 - one of them is PST:PSE or PET:PEE +*/ + EPINX_T ep2; + track_p trkN; + int epCnt = GetTrkEndPtCnt(trk); + + for ( ep2=0; ep2<epCnt; ep2++) { + if ( ep2 == ep ) + continue; + trkN = GetTrkEndTrk(trk,ep2); + if (trkN && (GetTrkBits(trkN)&TB_PROFILEPATH)) + return ep2; + } + return -1; +} + + +EXPORT int FindDefinedElev( + track_p trk, + EPINX_T ep, + int dir, + BOOL_T onpath, + DIST_T * Relev, + DIST_T *Rdist ) +{ + track_p trk0, trk1; + EPINX_T ep0, ep1, ep2; + DIST_T dist=0.0; + + if (dir) { + trk1 = GetTrkEndTrk( trk, ep ); + if (trk1 == NULL) + return FDE_END; + ep = GetEndPtConnectedToMe( trk1, trk ); + trk = trk1; + } + trk0 = trk; + ep0 = ep; + while (1) { + for (ep2=0; ep2<GetTrkEndPtCnt(trk); ep2++) { + if ((trk!=trk0||ep2!=ep0)&& + EndPtIsDefinedElev(trk,ep2) ) { + dist += GetTrkLength( trk, ep, ep2 ); + *Relev = GetTrkEndElevHeight(trk,ep2); + *Rdist = dist; + return FDE_DEF; + } + } + if (onpath) { + ep2 = GetNextTrkOnPath( trk, ep ); + if ( ep2 >= 0 ) { + trk1 = GetTrkEndTrk( trk, ep2 ); + ep1 = GetEndPtConnectedToMe( trk1, trk ); + } else { + trk1 = NULL; + } + } else { + ep2 = GetNextTrk( trk, ep, &trk1, &ep1, GNTignoreIgnore ); + } + if (ep2 < 0) { + if (trk1) { + /* more than one branch */ + return FDE_UDF; + } else { + /* end of the line */ + return FDE_END; + } + } + dist += GetTrkLength( trk, ep, ep2 ); + trk = trk1; + ep = ep1; + if (trk == trk0) + return FDE_UDF; + } +} + + +BOOL_T ComputeElev( + track_p trk, + EPINX_T ep, + BOOL_T onpath, + DIST_T *elevR, + DIST_T *gradeR ) +{ + DIST_T grade; + DIST_T elev0, elev1, dist0, dist1; + BOOL_T rc = FALSE; +if (oldElevationEvaluation) { + int rc0, rc1; + if (GetTrkEndElevMode(trk,ep) == ELEV_DEF) { + if (elevR) + *elevR = GetTrkEndElevHeight(trk,ep); + if (gradeR) + *gradeR = 0.0; + return TRUE; + } + rc0 = FindDefinedElev( trk, ep, 0, onpath, &elev0, &dist0 ); + rc1 = FindDefinedElev( trk, ep, 1, onpath, &elev1, &dist1 ); + if ( rc0 == FDE_DEF && rc1 == FDE_DEF ) { + if (dist0+dist1 > 0.1) + grade = (elev1-elev0)/(dist0+dist1); + else + grade = 0.0; + elev0 += grade*dist0; + rc = TRUE; + } else if ( rc0 == FDE_DEF && rc1 == FDE_END ) { + grade = 0.0; + rc = TRUE; + } else if ( rc1 == FDE_DEF && rc0 == FDE_END ) { + grade = 0.0; + elev0 = elev1; + rc = TRUE; + } else if ( rc0 == FDE_END && rc1 == FDE_END ) { + grade = 0.0; + elev0 = 0.0; + rc = TRUE; + } else { + grade = 0.0; + elev0 = 0.0; + } +} else { + track_p trk1; + EPINX_T ep1; + grade = -1; + rc = TRUE; + if ( EndPtIsDefinedElev(trk,ep) ) { + elev0 = GetTrkEndElevHeight(trk,ep); + rc = FALSE; + } else { + elev0 = GetElevation( trk ); + dist0 = GetTrkLength( trk, ep, -1 ); + trk1 = GetTrkEndTrk( trk, ep ); + if (trk1!=NULL) { + ep1 = GetEndPtConnectedToMe(trk1,trk); + elev1 = GetElevation( trk1 ); + dist1 = GetTrkLength( trk1, ep1, -1 ); + if (dist0+dist1>0.1) { + grade = (elev1-elev0)/(dist0+dist1); + elev0 += grade*dist0; + } else { + elev0 = (elev0+elev1)/2.0; + rc = FALSE; + } + } else { + grade = 0.0; + } + } +} + if ( elevR ) + *elevR = elev0; + if ( gradeR ) + *gradeR = grade; + return rc; +} + + + +/* + * DYNAMIC ELEVATION COMPUTATION + * + * Step 1: Find DefElev's marking the border of the area of interest (or all of them) + */ + +typedef struct { + track_p trk; + EPINX_T ep; + DIST_T elev; + } defelev_t; +static dynArr_t defelev_da; +#define defelev(N) DYNARR_N( defelev_t, defelev_da, N ) + +static void FindDefElev( void ) +{ + track_p trk; + EPINX_T ep, cnt; + defelev_t * dep; + long time0 = wGetTimer(); + + DYNARR_RESET( defelev_t, defelev_da ); + trk = NULL; + while ( TrackIterate( &trk ) ) { + cnt = GetTrkEndPtCnt( trk ); + for ( ep = 0; ep < cnt; ep++ ) { + if ( !EndPtIsDefinedElev( trk, ep ) ) + continue; + DYNARR_APPEND( defelev_t, defelev_da, 10 ); + dep = &defelev( defelev_da.cnt-1 ); + dep->trk = trk; + dep->ep = ep; + dep->elev = GetTrkEndElevHeight(trk,ep); + } + } +LOG( log_fillElev, 1, ( "%s: findDefElev [%d] (%ld)\n", elevPrefix, defelev_da.cnt, wGetTimer()-time0 ) ) +} + + +static int foundNewDefElev; +static void FindAttachedDefElev( track_p trk, BOOL_T remove ) +/* Find all DefElev's attached (directly or not) to this trk + * if 'remove' then clr ELEVPATH bit + * Working: + * elist_da + * Outputs: + * defelev_da - list of DefElev + * foundNewDefElev - found a DefElev not already on ElevPath + */ +{ + int i1; + track_p trk1; + EPINX_T ep, cnt; + defelev_t * dep; + + foundNewDefElev = FALSE; + DYNARR_RESET( elist_t, elist_da ); + elistAppend( trk, 0, 0 ); + SetTrkBits( trk, TB_PROCESSED ); + if (remove) + ClrTrkBits( trk, TB_ELEVPATH ); + for ( i1=0; i1<elist_da.cnt; i1++ ) { + trk = elist(i1).trk; + if (!IsTrack(trk)) + continue; + cnt = GetTrkEndPtCnt(trk); + for ( ep=0; ep<cnt; ep++ ) { + if ( EndPtIsDefinedElev(trk,ep) ) { + DYNARR_APPEND( defelev_t, defelev_da, 10 ); + dep = &defelev( defelev_da.cnt-1 ); + dep->trk = trk; + dep->ep = ep; + dep->elev = GetTrkEndElevHeight(trk,ep); + if ( !(GetTrkBits(trk)&TB_ELEVPATH) ) { + foundNewDefElev = TRUE; + } + } else { + trk1 = GetTrkEndTrk(trk,ep); + if (trk1) { +#ifdef LATER + if ( defElevOnIsland == FALSE && (GetTrkBits(trk1)&TB_ELEVPATH) ) { + /* so far this island is only connected to forks */ + DYNARR_APPEND( fork_t, fork_da, 10 ); + n = &fork(fork_da.cnt-1); + n->trk = trk1; + n->ep = ep; + n->dist = dist; + n->elev = dep->elev; + n->ntrk = dep->trk; + n->nep = dep->ep; + } +#endif + if ( !(GetTrkBits(trk1)&TB_PROCESSED) ) { + elistAppend( trk1, 0, 0 ); + SetTrkBits( trk1, TB_PROCESSED ); + if (remove) + ClrTrkBits( trk1, TB_ELEVPATH ); + } + } + } + } + } +} + +static int FindObsoleteElevs( void ) +{ + track_p trk; + int cnt; + long time0 = wGetTimer(); + + ClrAllTrkBits( TB_PROCESSED ); + DYNARR_RESET( defelev_t, defelev_da ); + cnt = 0; + trk = NULL; + while ( TrackIterate( &trk ) ) { + if ( (!(GetTrkBits(trk)&(TB_ELEVPATH|TB_PROCESSED))) && IsTrack(trk) ) { + cnt++; + FindAttachedDefElev( trk, TRUE ); + } + } +LOG( log_fillElev, 1, ( "%s: findObsoleteElevs [%d] (%ld)\n", elevPrefix, cnt, wGetTimer()-time0 ) ) + return cnt; +} + + + + +/* + * DYNAMIC ELEVATION COMPUTATION + * + * Step 2: Find Forks on Shortest Path between the DefElev's found in Step 1 + */ + +typedef struct { + track_p trk; + EPINX_T ep; + EPINX_T ep2; + DIST_T dist; + DIST_T elev; + } fork_t; +static dynArr_t fork_da; +#define fork(N) DYNARR_N( fork_t, fork_da, N ) + +static int FillElevShortestPathFunc( + SPTF_CMD cmd, + track_p trk, + EPINX_T ep, + EPINX_T ep2, + DIST_T dist, + void * data ) +{ + defelev_t * dep = (defelev_t *)data; + track_p trk1; + EPINX_T ep1, cnt, epRc; + fork_t * n; + + switch ( cmd ) { + case SPTC_MATCH: + /*if ( (GetTrkBits(trk)&TB_PROCESSED) ) + epRc = 0; + else*/ if ( (dep->trk!=trk || dep->ep!=ep) && EndPtIsDefinedElev(trk,ep)) + epRc = 1; + else + epRc = 0; + break; + + case SPTC_MATCHANY: + cnt = GetTrkEndPtCnt( trk ); + epRc = -1; + /*if ( (GetTrkBits(trk)&TB_PROCESSED) ) + break;*/ + for ( ep1=0; ep1<cnt; ep1++ ) { + if ( ep != ep1 && EndPtIsDefinedElev( trk, ep1 ) ) { + epRc = ep1; + break; + } + } + break; + + case SPTC_ADD_TRK: + if (!(GetTrkBits(trk)&TB_PROCESSED)) { + SetTrkBits(trk, TB_PROCESSED); + if ( EndPtIsDefinedElev( trk, ep ) ) { +if (log_shortPath<=0||logTable(log_shortPath).level<4) LOG( log_fillElev, 5, ( " ADD_TRK: T%d:%d D=%0.1f -> DefElev\n", GetTrkIndex(trk), ep, dist ) ) +LOG( log_shortPath, 4, ( "DefElev " ) ) + } else if ( GetNextTrk( trk, ep, &trk1, &ep1, GNTignoreIgnore ) < 0 && trk1 != NULL ) { +if (log_shortPath<=0||logTable(log_shortPath).level<4) LOG( log_fillElev, 4, ( " ADD_TRK: T%d:%d D=%0.3f E=%0.3f -> Fork[%d]\n", GetTrkIndex(trk), ep, dist, dep->elev, fork_da.cnt ) ) +LOG( log_shortPath, 4, ( "E:%0.3f Fork[%d] ", dep->elev, fork_da.cnt ) ) + DYNARR_APPEND( fork_t, fork_da, 10 ); + n = &fork(fork_da.cnt-1); + n->trk = trk; + n->ep = ep; + n->ep2 = ep2; + n->dist = dist; + n->elev = dep->elev; +#ifdef LATER + n->ntrk = dep->trk; + n->nep = dep->ep; +#endif + } else { +LOG( log_shortPath, 4, ( "Normal " ) ) + } + } else { +LOG( log_shortPath, 4, ( "Processed " ) ) + } + return 0; + + case SPTC_TERMINATE: + epRc = 0; + break; + + case SPTC_IGNNXTTRK: + if ( EndPtIsIgnoredElev(trk,ep2) ) { +LOG( log_shortPath, 4, ( "2 Ignore " ) ) + epRc = 1; + } else if ( (!EndPtIsDefinedElev(trk,ep)) && GetTrkEndTrk(trk,ep)==NULL ) { +LOG( log_shortPath, 4, ( "1 Null && !DefElev " ) ) + epRc = 1; + } else { + epRc = 0; + } + break; + + case SPTC_VALID: + epRc = (GetTrkBits(trk)&TB_PROCESSED)==0?1:0; + break; + + default: + epRc = 0; + break; + } + return epRc; +} + +static void FindForks( void ) +/* Find the Shortest Path between all DevElev's (in defelev_da) + * and record all Forks (Turnouts with >2 connections) with distance to and elevation of the DefElev + * Inputs: + * defelev_da - list of DefElev to consider + * Outputs: + * fork_da - list of distances btw Forks and DefElev (plus other info) + */ +{ + int i; + defelev_t * dep; + int rc; + long time0 = wGetTimer(); + + DYNARR_RESET( fork_t, fork_da ); + for ( i=0; i<defelev_da.cnt; i++ ) { + dep = &defelev(i); + + ClrAllTrkBits( TB_PROCESSED ); +LOG( log_fillElev, 3, ( " findForks from T%d:%d\n", GetTrkIndex(dep->trk), dep->ep ) ) + rc = FindShortestPath( dep->trk, dep->ep, FALSE, FillElevShortestPathFunc, dep ); + } + ClrAllTrkBits( TB_PROCESSED ); +LOG( log_fillElev, 1, ( "%s: findForks [%d] (%ld)\n", elevPrefix, fork_da.cnt, wGetTimer()-time0 ) ) +} + + + + +/* + * DYNAMIC ELEVATION COMPUTATION + * + * Step 3: Compute the elevation of each Fork based on the weighted sum of the elevations + * of the DefElev's that it is connected to. + */ + +typedef struct { + DIST_T elev; + DIST_T dist; + } elevdist_t; +static dynArr_t elevdist_da; +#define elevdist(N) DYNARR_N( elevdist_t, elevdist_da, N ); + +static DIST_T ComputeWeightedElev( DIST_T totalDist ) +/* Compute weighted elevation + * Inputs: + * totalDist - total distance of all tracks between DevElev's + * elevdist_da - elev + dist to DefElev from current node + * Outputs: + * elev (returned) + */ +{ + int i2; + DIST_T d2; + elevdist_t * w; + DIST_T e; + + /* Compute inverse weighted Distance (d2) */ + d2 = 0; + for ( i2=0; i2<elevdist_da.cnt; i2++ ) { + w = &elevdist(i2); + if (w->dist < 0.001) { + e = w->elev; +LOG( log_fillElev, 3, ( " computeWeightedElev: close! D%0.3f E%0.3f\n", w->dist, e ) ) + return e; + } + d2 += totalDist/w->dist; + } + + /* Compute weighted elevation (e) */ + e = 0; + for ( i2=0; i2<elevdist_da.cnt; i2++ ) { + w = &elevdist(i2); + e += ((totalDist/w->dist)/d2)*w->elev; + } + + if (log_fillElev >= 4) { + for ( i2=0; i2<elevdist_da.cnt; i2++ ) { + w = &elevdist(i2); + lprintf( " E%0.3f D%0.3f\n", w->elev, w->dist ); + } + } +LOG( log_fillElev, 3, ( " computeWeightedElev: E%0.3f\n", e ) ) + return e; +} + + +static int forkCnt; +static void ComputeForkElev( void ) +/* Compute elevations of all Forks + * Inputs: + * fork_da - fork distance/elev data (overwritten) + * Outputs: + * fork_da - just .trk used for forks + * forkCnt - numer of forks found + */ +{ + int i1, i2; + fork_t * n1, * n2, * n3; + track_p trk, trk1; + EPINX_T ep, cnt; + DIST_T d1, e; + elevdist_t * w; + BOOL_T singlePath; + long time0 = wGetTimer(); + + forkCnt = 0; + for (i1=0; i1<fork_da.cnt; i1++) { + n1 = &fork(i1); + if ((trk=n1->trk)) { + cnt = GetTrkEndPtCnt(n1->trk); + if (cnt<=0) + continue; + + /* collect dist/elev to connected DefElev points */ + d1 = 0; + DYNARR_RESET( elevdist_t, elevdist_da ); + singlePath = TRUE; + for (i2=i1; i2<fork_da.cnt; i2++) { + n2 = &fork(i2); + if (trk == n2->trk) { + DYNARR_APPEND( elevdist_t, elevdist_da, 10 ); + w = &elevdist(elevdist_da.cnt-1); + w->dist = n2->dist; + w->elev = n2->elev; + w->dist += GetTrkLength( n2->trk, n2->ep, -1 ); + n2->trk = NULL; + d1 += w->dist; + if ( ! ( ( n1->ep == n2->ep && n1->ep2 == n2->ep2 ) || + ( n1->ep == n2->ep2 && n1->ep2 == n2->ep ) ) ) + singlePath = FALSE; + } + } + + /* Also check my EPs */ + for (ep=0; ep<cnt; ep++) { + if ( (trk1=GetTrkEndTrk(trk,ep)) ) + SetTrkBits( trk1, TB_PROCESSED ); + if (!EndPtIsDefinedElev(trk,ep)) + continue; + for (i2=i1; i2<fork_da.cnt; i2++) { + n2 = &fork(i2); + if (trk==n2->trk && ep==n2->ep) + break; + } + if (i2 >= fork_da.cnt) { + DYNARR_APPEND( elevdist_t, elevdist_da, 10 ); + w = &elevdist(elevdist_da.cnt-1); + w->elev = GetTrkEndElevHeight(trk,ep); + w->dist = GetTrkLength( trk, ep, -1 ); + d1 += w->dist; + singlePath = FALSE; + } + } + + n3 = &fork(forkCnt); + n3->trk = trk; + if ( singlePath == TRUE ) { + /* only 2 EP are connected to DefElevs, treat other EPs as ignored */ + n3->ep = n1->ep; + n3->ep2 = n1->ep2; + } else { + e = ComputeWeightedElev( d1 ); + SetTrkOnElevPath( trk, ELEV_FORK, e ); + /* 3 or more EPs are to DefElevs */ + n3->ep = -1; +LOG( log_fillElev, 2, ( " 1 T%d E%0.3f\n", GetTrkIndex(trk), e ) ) + } + forkCnt++; + } + } +LOG( log_fillElev, 1, ( "%s: computeForkElev [%d] (%ld)\n", elevPrefix, forkCnt, wGetTimer()-time0 ) ) +} + + + + +/* + * DYNAMIC ELEVATION COMPUTATION + * + * Step 4: Compute the elevation of tracks on the Shortest Path between Forks and DefElev's + */ + +static void RedrawCompGradeElev( track_p trk, EPINX_T ep ) +{ + int mode; + coOrd pos; + track_p trk1; + mode = GetTrkEndElevMode( trk, ep ); + if ( mode == ELEV_COMP || mode == ELEV_GRADE ) { + pos = GetTrkEndPos( trk, ep ); + if (!OFF_MAIND( pos, pos ) ) { + trk1 = GetTrkEndTrk( trk, ep ); + if ( (trk1=GetTrkEndTrk(trk,ep)) && GetTrkIndex(trk1)<GetTrkIndex(trk) ) +{ + ep = GetEndPtConnectedToMe( trk1, trk ); + trk = trk1; + } + DrawEndElev( &mainD, trk, ep, wDrawColorWhite ); + DrawEndElev( &mainD, trk, ep, wDrawColorBlack ); + } + } +} + + +static void PropogateForkElev( + track_p trk1, + EPINX_T ep1, + DIST_T d1, + DIST_T e ) +/* Propogate elev from fork connection + * The track list starting from trk1:ep1 ends at a DefElev or a Fork + * Inputs: + * Working: + * elist_da + * Outputs: + * Sets trk elev + */ +{ + DIST_T d2; + DIST_T e1; + EPINX_T ep2, epN, cnt2; + track_p trkN; + fork_t * n1; + int i2, i3; + + DYNARR_RESET( elist_t, elist_da ); + while (trk1) { + if ( GetTrkIndex(trk1) == checkTrk ) + printf( "found btw forks\n" ); + if ( GetTrkOnElevPath( trk1, &e1 ) ) { + d1 += GetTrkLength( trk1, ep1, -1 ); + goto nextStep; + } + cnt2 = GetTrkEndPtCnt(trk1); + for ( ep2=0; ep2<cnt2; ep2++ ) { + if ( ep2!=ep1 && EndPtIsDefinedElev(trk1,ep2) ) { + e1 = GetTrkEndElevHeight( trk1, ep2 ); + d2 = GetTrkLength( trk1, ep1, ep2 )/2.0; + d1 += d2; + elistAppend( trk1, ep1, d1 ); + d1 += d2; + goto nextStep; + } + } + ep2 = GetNextTrk( trk1, ep1, &trkN, &epN, GNTignoreIgnore ); + if ( ep2<0 ) { + /* is this really a fork? */ + for ( i2=0; i2<forkCnt; i2++ ) { + n1 = &fork(i2); + if ( trk1 == n1->trk && n1->ep >= 0 ) { + /* no: make sure we are on the path */ + if ( n1->ep == ep1 ) + ep2 = n1->ep2; + else if ( n1->ep2 == ep1 ) + ep2 = n1->ep; + else + return; + trkN = GetTrkEndTrk(trk1,ep2); + epN = GetEndPtConnectedToMe( trkN, trk1 ); + break; + } + } + if ( i2 >= forkCnt ) + return; + } + d2 = GetTrkLength( trk1, ep1, ep2 )/2.0; + d1 += d2; + elistAppend( trk1, ep1, d1 ); + d1 += d2; + trk1 = trkN; + ep1 = epN; + } +nextStep: + ASSERT(d1>0.0); + e1 = (e1-e)/d1; + trk1 = NULL; + i3 = elist_da.cnt; + for (i2=0; i2<elist_da.cnt; i2++) { + trk1 = elist(i2).trk; + ep1 = elist(i2).ep; + if ( GetTrkOnElevPath( trk1, &e1 ) ) { + i3=i2; + break; + } + d2 = elist(i2).len; + SetTrkOnElevPath( trk1, ELEV_BRANCH, e+e1*d2 ); +LOG( log_fillElev, 2, ( " 2 T%d E%0.3f\n", GetTrkIndex(trk1), e+e1*d2 ) ) + } + for (i2=0; i2<i3; i2++) { + trk1 = elist(i2).trk; + ep1 = elist(i2).ep; + RedrawCompGradeElev( trk1, ep1 ); + } +} + +static void PropogateForkElevs( void ) +/* For all Forks, For all connections not already processed or not on ElevPath do + * propogate elev along connection + * Inputs: + * fork_da - list of forks + * forkCnt - number of forks + * Working: + * elist_da (by subrtn) + * Outputs: + * Sets trk elev (by subrtn) + */ +{ + int i1; + fork_t * n1; + track_p trk, trk1; + EPINX_T ep, cnt, ep1; + DIST_T d1, e; + long time0 = wGetTimer(); + + /* propogate elevs between forks */ + for ( i1=0; i1<forkCnt; i1++ ) { + n1 = &fork(i1); + if ( n1->ep >= 0 ) + continue; + trk = n1->trk; + GetTrkOnElevPath( trk, &e ); + cnt = GetTrkEndPtCnt(trk); + for (ep=0; ep<cnt; ep++) { + trk1 = GetTrkEndTrk(trk,ep); + if ( trk1 && (GetTrkBits(trk1)&TB_PROCESSED) && !GetTrkOnElevPath(trk1,NULL) ) { + /* should find a fork with a computed elev */ + ep1 = GetEndPtConnectedToMe( trk1, trk ); + if ( EndPtIsDefinedElev(trk,ep) ) { + PropogateForkElev( trk1, ep1, 0, GetTrkEndElevHeight(trk,ep) ); + } else { + d1 = GetTrkLength(trk,ep,-1); + PropogateForkElev( trk1, ep1, d1, e ); + } + } + } + } +LOG( log_fillElev, 1, ( "%s: propogateForkElev (%ld)\n", elevPrefix, wGetTimer()-time0 ) ) +} + +static void PropogateDefElevs( void ) +/* Propogate Elev from DefElev (if not handled already) + * Inputs: + * develev_da - list of DefElev + * Outputs: + * Set trk elev + */ +{ + int i1; + defelev_t * dep; + DIST_T e; + long time0 = wGetTimer(); + + /* propogate elevs between DefElev pts (not handled by propogateForkElevs) */ + for ( i1=0; i1<defelev_da.cnt; i1++ ) { + dep = &defelev(i1); + if (GetTrkOnElevPath( dep->trk, &e )) + /* propogateForkElevs beat us to it */ + continue; + e = GetTrkEndElevHeight( dep->trk, dep->ep ); + PropogateForkElev( dep->trk, dep->ep, 0, e ); + } +LOG( log_fillElev, 1, ( "%s: propogateDefElevs [%d] (%ld)\n", elevPrefix, defelev_da.cnt, wGetTimer()-time0 ) ) +} + + + + +/* + * DYNAMIC ELEVATION COMPUTATION + * + * Step 5: Remaining tracks form either Islands connected to zero or more DefElev, Forks + * or Branches (Pivots). The elevation of these tracks is determined by the distance to + * the Pivots. Tracks which are not connected to Pivots are labeled 'Alone' and have no + * elevation. + */ + +typedef struct { + track_p trk; + coOrd pos; + DIST_T elev; + } pivot_t; +static dynArr_t pivot_da; +#define pivot(N) DYNARR_N(pivot_t, pivot_da, N) + +static void SurveyIsland( + track_p trk, + BOOL_T stopAtElevPath ) +/* Find the tracks in this island and the pivots of this island + * Outputs: + * elist_da - tracks in the island + * pivot_da - pivots connecting island to tracks with some elev + */ +{ + int i1; + track_p trk1; + EPINX_T ep, cnt; + pivot_t * pp; + coOrd hi, lo; + DIST_T elev; + + DYNARR_RESET( elist_t, elist_da ); + DYNARR_RESET( pivot_t, pivot_da ); + ClrAllTrkBits( TB_PROCESSED ); + elistAppend( trk, 0, 0 ); + SetTrkBits( trk, TB_PROCESSED ); + for ( i1=0; i1 < elist_da.cnt; i1++ ) { + trk = elist(i1).trk; + if ( GetTrkIndex(trk) == checkTrk ) + printf( "found in island\n" ); + cnt = GetTrkEndPtCnt(trk); + for ( ep=0; ep<cnt; ep++ ) { + trk1 = GetTrkEndTrk( trk, ep ); + if ( EndPtIsDefinedElev(trk,ep)) { + DYNARR_APPEND( pivot_t, pivot_da, 10 ); + pp = &pivot(pivot_da.cnt-1); + pp->trk = trk; + pp->pos = GetTrkEndPos(trk,ep); + pp->elev = GetTrkEndElevHeight(trk,ep); + } else if ( stopAtElevPath && trk1 && GetTrkOnElevPath(trk1, &elev) ) { + DYNARR_APPEND( pivot_t, pivot_da, 10 ); + pp = &pivot(pivot_da.cnt-1); + pp->trk = trk1; + GetBoundingBox( trk1, &hi, &lo ); + pp->pos.x = (hi.x+lo.x)/2.0; + pp->pos.y = (hi.y+lo.y)/2.0; + pp->elev = elev; + } else if ( trk1 && !(GetTrkBits(trk1)&TB_PROCESSED) ) { + SetTrkBits( trk1, TB_PROCESSED ); + elistAppend( trk1, 0, 0 ); + } + } + } + ClrAllTrkBits( TB_PROCESSED ); +} + +static void ComputeIslandElev( + track_p trk ) +/* Compute elev of tracks connected to 'trk' + * An island is the set of tracks bounded by a DefElev EP or a track already on ElevPath + * Inputs: + * Working: + * elist_da + * Outputs: + * pivot_da - list of tracks on boundary of Island + */ +{ + int i1, i2; + coOrd hi, lo, pos; + pivot_t * pp; + DIST_T elev; + DIST_T totalDist; + elevdist_t * w; + int mode; + EPINX_T ep, epCnt; + + SurveyIsland( trk, TRUE ); + + for ( i1=0; i1 < elist_da.cnt; i1++ ) { + trk = elist(i1).trk; + if ( !IsTrack(trk) ) + continue; + mode = ELEV_ISLAND; + if (pivot_da.cnt == 0) { + elev = 0; + mode = ELEV_ALONE; + } else if (pivot_da.cnt == 1) { + elev = pivot(0).elev; + } else { + if ( !GetCurveMiddle( trk, &pos ) ) { + GetBoundingBox( trk, &hi, &lo ); + pos.x = (hi.x+lo.x)/2.0; + pos.y = (hi.y+lo.y)/2.0; + } + DYNARR_RESET( elevdist_t, elevdist_da ); + totalDist = 0; + for ( i2=0; i2<pivot_da.cnt; i2++ ) { + pp = &pivot(i2); + DYNARR_APPEND( elevdist_t, elevdist_da, 10 ); + w = &elevdist(elevdist_da.cnt-1); + w->elev = pp->elev; + w->dist = FindDistance( pos, pp->pos ); + totalDist += w->dist; + } + elev = ComputeWeightedElev( totalDist ); + } + SetTrkOnElevPath( trk, mode, elev ); +LOG( log_fillElev, 1, ( " 3 T%d E%0.3f\n", GetTrkIndex(trk), elev ) ) + } + + for ( i1=0; i1<elist_da.cnt; i1++ ) { + trk = elist(i1).trk; + epCnt = GetTrkEndPtCnt( trk ); + for ( ep=0; ep<epCnt; ep++ ) { + mode = GetTrkEndElevMode( trk, ep ); + if ( (mode == ELEV_GRADE || mode == ELEV_COMP) ) { + RedrawCompGradeElev( trk, ep ); + } + } + } +} + + +static void FindIslandElevs( void ) +{ + track_p trk; + DIST_T elev; + int islandCnt; + long time0 = wGetTimer(); + + trk = NULL; + islandCnt = 0; + while ( TrackIterate( &trk ) ) { + if ( !GetTrkOnElevPath( trk, &elev ) ) { + if (IsTrack(trk)) { + ComputeIslandElev( trk ); + islandCnt++; + } + } + } +LOG( log_fillElev, 1, ( "%s: findIslandElevs [%d] (%ld)\n", elevPrefix, islandCnt, wGetTimer()-time0 ) ) +} + +/* + * DYNAMIC ELEVATION COMPUTATION + * + * Drivers + * + */ + +EXPORT void RecomputeElevations( void ) +{ + long time0 = wGetTimer(); + elevPrefix = "RECELV"; + if ( !log_fillElev_initted ) { log_fillElev = LogFindIndex( "fillElev" ); log_dumpElev = LogFindIndex( "dumpElev" ); log_fillElev_initted = TRUE; } + ClearElevPath(); + FindDefElev(); + FindForks(); + ComputeForkElev(); + PropogateForkElevs(); + PropogateDefElevs(); + FindIslandElevs(); + MainRedraw(); +LOG( log_fillElev, 1, ( "%s: Total (%ld)\n", elevPrefix, wGetTimer()-time0 ) ) + if ( log_dumpElev > 0 ) { + track_p trk; + DIST_T elev; + for ( trk=NULL; TrackIterate( &trk ); ) { + printf( "T%4.4d = ", GetTrkIndex(trk) ); + if ( GetTrkOnElevPath( trk, &elev ) ) + printf( "%d:%0.2f\n", GetTrkElevMode(trk), elev ); + else + printf( "noelev\n" ); +#ifdef LATER + EPINX_T ep; + int mode; + for ( ep=0; ep<GetTrkEndPtCnt(trk); ep++ ) { + mode = GetTrkEndElevMode( trk, ep ); + ComputeElev( trk, ep, FALSE, &elev, NULL ); + printf( "T%4.4d[%2.2d] = %s:%0.3f\n", + GetTrkIndex(trk), ep, + mode==ELEV_NONE?"None":mode==ELEV_DEF?"Def":mode==ELEV_COMP?"Comp": + mode==ELEV_GRADE?"Grade":mode==ELEV_IGNORE?"Ignore":mode==ELEV_STATION?"Station":"???", + elev ); + } +#endif + } + } +} + + +static BOOL_T needElevUpdate = FALSE; +EXPORT void UpdateAllElevations( void ) +{ + int work; + long time0 = wGetTimer(); + + elevPrefix = "UPDELV"; + if ( !log_fillElev_initted ) { log_fillElev = LogFindIndex( "fillElev" ); log_dumpElev = LogFindIndex( "dumpElev" ); log_fillElev_initted = TRUE; } + if (!needElevUpdate) + return; + work = FindObsoleteElevs(); + if (!work) + return; + FindForks(); + ComputeForkElev(); + PropogateForkElevs(); + PropogateDefElevs(); + FindIslandElevs(); + needElevUpdate = FALSE; +LOG( log_fillElev, 1, ( "%s: Total (%ld)\n", elevPrefix, wGetTimer()-time0 ) ) +} + + +EXPORT DIST_T GetElevation( track_p trk ) +{ + DIST_T elev; + + if ( !IsTrack(trk) ) { + return 0; + } + if ( GetTrkOnElevPath(trk,&elev) ) { + return elev; + } + + elevPrefix = "GETELV"; + if ( !log_fillElev_initted ) { log_fillElev = LogFindIndex( "fillElev" ); log_dumpElev = LogFindIndex( "dumpElev" ); log_fillElev_initted = TRUE; } + ClrAllTrkBits( TB_PROCESSED ); + DYNARR_RESET( defelev_t, defelev_da ); + FindAttachedDefElev( trk, TRUE ); + /* at least one DevElev to be processed */ + FindForks(); + ComputeForkElev(); + PropogateForkElevs(); + PropogateDefElevs(); + if ( GetTrkOnElevPath(trk,&elev) ) + return elev; + + ComputeIslandElev( trk ); + + if ( GetTrkOnElevPath(trk,&elev) ) + return elev; + + printf( "GetElevation(T%d) failed\n", GetTrkIndex(trk) ); + return 0; +} + + +/* + * DYNAMIC ELEVATION COMPUTATION + * + * Utilities + * + */ + + +EXPORT void ClrTrkElev( track_p trk ) +{ + needElevUpdate = TRUE; + DrawTrackElev( trk, &mainD, FALSE ); + ClrTrkBits( trk, TB_ELEVPATH ); +} + + +static void PropogateElevMode( track_p trk, DIST_T elev, int mode ) +{ + int i1; + SurveyIsland( trk, FALSE ); + for ( i1=0; i1<elist_da.cnt; i1++ ) + SetTrkOnElevPath( elist(i1).trk, mode, elev ); +} + + +static BOOL_T CheckForElevAlone( track_p trk ) +{ + int i1; + SurveyIsland( trk, FALSE ); + if ( pivot_da.cnt!=0 ) + return FALSE; + for ( i1=0; i1<elist_da.cnt; i1++ ) + SetTrkOnElevPath( elist(i1).trk, ELEV_ALONE, 0.0 ); + return TRUE; +} + + +EXPORT void SetTrkElevModes( BOOL_T connect, track_p trk0, EPINX_T ep0, track_p trk1, EPINX_T ep1 ) +{ + int mode0, mode1; + DIST_T elev, diff, elev0, elev1; + char * station; + BOOL_T update = TRUE; + + mode0 = GetTrkElevMode( trk0 ); + mode1 = GetTrkElevMode( trk1 ); + if ( mode0 == ELEV_ALONE && mode1 == ELEV_ALONE ) { + update = FALSE;; + } else if ( connect ) { + if ( mode0 == ELEV_ALONE ) { + ComputeElev( trk1, ep1, FALSE, &elev, NULL ); + PropogateElevMode( trk0, elev, ELEV_ISLAND ); + update = FALSE; + } else if ( mode1 == ELEV_ALONE ) { + ComputeElev( trk0, ep0, FALSE, &elev, NULL ); + PropogateElevMode( trk1, elev, ELEV_ISLAND ); + update = FALSE; + } + } else { + if ( mode0 == ELEV_ISLAND ) { + if (CheckForElevAlone( trk0 )) + update = FALSE; + } else if ( mode1 == ELEV_ISLAND ) { + if (CheckForElevAlone( trk1 )) + update = FALSE; + } + } + + if ( connect ) { + mode0 = GetTrkEndElevMode( trk0, ep0 ); + mode1 = GetTrkEndElevMode( trk1, ep1 ); + elev = 0.0; + station = NULL; + if (mode0 == ELEV_DEF && mode1 == ELEV_DEF) { + mode0 = GetTrkEndElevUnmaskedMode( trk0, ep0 ) | GetTrkEndElevUnmaskedMode( trk1, ep1 ); + elev0 = GetTrkEndElevHeight( trk0, ep0 ); + elev1 = GetTrkEndElevHeight( trk1, ep1 ); + elev = (elev0+elev1)/2.0; + diff = fabs( elev0-elev1 ); + if (diff>0.1) + ErrorMessage( MSG_JOIN_DIFFER_ELEV, PutDim(diff) ); + } else if (mode0 == ELEV_DEF) { + mode0 = GetTrkEndElevUnmaskedMode( trk0, ep0 ); + elev = GetTrkEndElevHeight( trk0, ep0 ); + } else if (mode1 == ELEV_DEF) { + mode1 = GetTrkEndElevUnmaskedMode( trk1, ep1 ); + elev = GetTrkEndElevHeight( trk1, ep1 ); + } else if (mode0 == ELEV_STATION) { + station = GetTrkEndElevStation( trk0, ep0 ); + } else if (mode1 == ELEV_STATION) { + station = GetTrkEndElevStation( trk1, ep1 ); + mode0 = mode1; + } else if (mode0 == ELEV_GRADE) { + ; + } else if (mode1 == ELEV_GRADE) { + mode0 = mode1; + } else if (mode0 == ELEV_COMP) { + ; + } else if (mode1 == ELEV_COMP) { + mode0 = mode1; + } else { + ; + } + SetTrkEndElev( trk0, ep0, mode0, elev, station ); + SetTrkEndElev( trk1, ep1, mode0, elev, station ); + } + + if (update) { + needElevUpdate = TRUE; + ClrTrkElev( trk0 ); + ClrTrkElev( trk1 ); + } +} + + +EXPORT void UpdateTrkEndElev( + track_p trk, + EPINX_T ep, + int newMode, + DIST_T newElev, + char * newStation ) +{ + int oldMode; + DIST_T oldElev; + char * oldStation; + BOOL_T changed = TRUE; + track_p trk1; + EPINX_T ep1; + + oldMode = GetTrkEndElevUnmaskedMode( trk, ep ); + if ( (oldMode&ELEV_MASK) == (newMode&ELEV_MASK) ) { + switch ( (oldMode&ELEV_MASK) ) { + case ELEV_DEF: + oldElev = GetTrkEndElevHeight( trk, ep ); + if ( oldElev == newElev ) { + if ( oldMode == newMode ) + return; + changed = FALSE; + } + break; + case ELEV_STATION: + oldStation = GetTrkEndElevStation( trk, ep ); + if ( strcmp( oldStation, newStation ) == 0 ) { + return; + } + break; + default: + return; + } + } else { + changed = TRUE; + if ( (newMode&ELEV_MASK)==ELEV_DEF || (oldMode&ELEV_MASK)==ELEV_DEF || + (newMode&ELEV_MASK)==ELEV_IGNORE || (oldMode&ELEV_MASK)==ELEV_IGNORE ) + changed = TRUE; + } + UndoModify( trk ); + if ( (trk1 = GetTrkEndTrk( trk, ep )) ) { + UndoModify( trk1 ); + } + DrawEndPt2( &mainD, trk, ep, drawColorWhite ); + SetTrkEndElev( trk, ep, newMode, newElev, newStation ); + DrawEndPt2( &mainD, trk, ep, drawColorBlack ); + if ( changed ) { + ClrTrkElev( trk ); + if ( trk1 ) { + ep1 = GetEndPtConnectedToMe( trk1, trk ); + ClrTrkElev( trk1 ); + } + UpdateAllElevations(); + } +} + +static void SetTrkOnElevPath( track_p trk, int mode, DIST_T elev ) +{ + BOOL_T redraw = FALSE; + int oldMode = GetTrkElevMode( trk ); + DIST_T oldElev; + coOrd hi, lo; + + if ( !GetTrkOnElevPath( trk, &oldElev ) ) + oldElev = 0.0; + GetBoundingBox( trk, &hi, &lo ); + if ((labelEnable&LABELENABLE_TRACK_ELEV) && + labelScale >= mainD.scale && + (! OFF_MAIND( lo, hi ) ) && + (GetTrkVisible(trk) || drawTunnel!=0/*DRAW_TUNNEL_NONE*/) && + GetLayerVisible(GetTrkLayer(trk)) ) + redraw = TRUE; + + if ( (GetTrkBits(trk)&TB_ELEVPATH) && (oldElev == elev && oldMode == mode) ) + return; + if ( redraw && (GetTrkBits(trk)&TB_ELEVPATH)) + DrawTrackElev( trk, &mainD, FALSE ); + SetTrkElev( trk, mode, elev ); + if ( redraw ) + DrawTrackElev( trk, &mainD, TRUE ); +} + + +EXPORT void DrawTrackElev( track_cp trk, drawCmd_p d, BOOL_T drawIt ) +{ + coOrd pos; + wFont_p fp; + wDrawColor color=(wDrawColor)0; + DIST_T elev; + coOrd lo, hi; + + if ( (!IsTrack(trk)) || + (!(labelEnable&LABELENABLE_TRACK_ELEV)) || + (d == &mapD) || + (labelScale < d->scale) || + (!GetTrkOnElevPath( trk, &elev )) || + ((GetTrkBits(trk)&TB_ELEVPATH) == 0) || + (d->funcs->options & wDrawOptTemp) != 0 || + (d->options & DC_QUICK) != 0 ) + return; + + if ( !GetCurveMiddle( trk, &pos ) ) { + GetBoundingBox( trk, &hi, &lo ); + pos.x = (hi.x+lo.x)/2.0; + pos.y = (hi.y+lo.y)/2.0; + } + if ( d==&mainD && OFF_MAIND( pos, pos ) ) + return; + + switch ( GetTrkElevMode(trk) ) { + case ELEV_FORK: + color = drawColorBlue; + break; + case ELEV_BRANCH: + color = drawColorPurple; + break; + case ELEV_ISLAND: + color = drawColorGold; + break; + case ELEV_ALONE: + return; + } + if ( !drawIt ) + color = wDrawColorWhite; + sprintf( message, "%s", FormatDistance(elev)); + fp = wStandardFont( F_HELV, FALSE, FALSE ); + DrawBoxedString( BOX_INVERT, d, pos, message, fp, (wFontSize_t)descriptionFontSize, color, 0 ); +} + + diff --git a/app/bin/fileio.c b/app/bin/fileio.c new file mode 100644 index 0000000..dcd8b5c --- /dev/null +++ b/app/bin/fileio.c @@ -0,0 +1,1565 @@ +/** \file fileio.c + * Loading and saving files. Handles trackplans as well as DXF export. + * + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/fileio.c,v 1.18 2009-05-08 15:28:54 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 <stdlib.h> +#include <stdio.h> +#ifndef WINDOWS +#include <unistd.h> +#include <dirent.h> +#include <errno.h> +#endif +#include <math.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#ifdef WINDOWS +#include <io.h> +#include <windows.h> + #if _MSC_VER >=1400 + #define strdup _strdup + #endif +#else +#endif +#include <sys/stat.h> +#include <stdarg.h> +#include <locale.h> + +#include <stdint.h> + +#include "track.h" +#include "version.h" +#include "common.h" +#include "utility.h" +#include "draw.h" +#include "misc.h" +#include "compound.h" +#include "i18n.h" + +/*#define TIME_READTRACKFILE*/ + +EXPORT const char * workingDir; +EXPORT const char * libDir; + +static char * customPath = NULL; +static char * customPathBak = NULL; + +EXPORT char curPathName[STR_LONG_SIZE]; +EXPORT char * curFileName; +EXPORT char curDirName[STR_LONG_SIZE]; + +EXPORT char * clipBoardN; + +EXPORT wBool_t executableOk = FALSE; + +static int log_paramFile; + +EXPORT void SetCurDir( + const char * pathName, + const char * fileName ) +{ + memcpy( curDirName, pathName, fileName-pathName ); + curDirName[fileName-pathName-1] = '\0'; + wPrefSetString( "file", "directory", curDirName ); +} + +#ifdef WINDOWS +#define rename( F1, F2 ) Copyfile( F1, F2 ) + +static int Copyfile( char * fn1, char * fn2 ) +{ + FILE *f1, *f2; + size_t size; + f1 = fopen( fn1, "r" ); + if ( f1 == NULL ) + return 0; + f2 = fopen( fn2, "w" ); + if ( f2 == NULL ) { + fclose( f1 ); + return -1; + } + while ( (size=fread( message, 1, sizeof message, f1 )) > 0 ) + fwrite( message, size, 1, f2 ); + fclose( f1 ); + fclose( f2 ); + return 0; +} +#endif + +/** + * Save the old locale and set to new. + * + * \param newlocale IN the new locale to set + * \return pointer to the old locale + */ + +char * +SaveLocale( char *newLocale ) +{ + char *oldLocale; + char *saveLocale = NULL; + + /* get old locale setting */ + oldLocale = setlocale(LC_ALL, NULL); + + /* allocate memory to save */ + if (oldLocale) + saveLocale = strdup( oldLocale ); + + setlocale(LC_ALL, newLocale ); + + return( saveLocale ); +} + +/** + * Restore a previously saved locale. + * + * \param locale IN return value from earlier call to SaveLocale + */ + +void +RestoreLocale( char * locale ) +{ + if( locale ) { + setlocale( LC_ALL, locale ); + free( locale ); + } +} + + +/**************************************************************************** + * + * PARAM FILE INPUT + * + */ + +EXPORT FILE * paramFile = NULL; +EXPORT char paramFileName[STR_LONG_SIZE]; +EXPORT wIndex_t paramLineNum = 0; +EXPORT char paramLine[STR_LONG_SIZE]; +EXPORT char * curContents; +EXPORT char * curSubContents; +static long paramCheckSum; + +#define PARAM_DEMO (-1) + +typedef struct { + char * name; + readParam_t proc; + } paramProc_t; +static dynArr_t paramProc_da; +#define paramProc(N) DYNARR_N( paramProc_t, paramProc_da, N ) + + +EXPORT void Stripcr( char * line ) +{ + char * cp; + cp = line + strlen(line); + if (cp == line) + return; + cp--; + if (*cp == '\n') + *cp-- = '\0'; + if (cp >= line && *cp == '\r') + *cp = '\0'; +} + +EXPORT void ParamCheckSumLine( char * line ) +{ + long mult=1; + while ( *line ) + paramCheckSum += (((long)(*line++))&0xFF)*(mult++); +} + +EXPORT char * GetNextLine( void ) +{ + if (!paramFile) { + paramLine[0] = '\0'; + return NULL; + } + if (fgets( paramLine, sizeof paramLine, paramFile ) == NULL) { + AbortProg( "Permature EOF on %s", paramFileName ); + } + Stripcr( paramLine ); + ParamCheckSumLine( paramLine ); + paramLineNum++; + return paramLine; +} + + +/** + * Show an error message if problems occur during loading of a param or layout file. + * The user has the choice to cancel the operation or to continue. If operation is + * canceled the open file is closed. + * + * \param IN msg error message + * \param IN showLine set to true if current line should be included in error message + * \param IN ... variable number additional error information + * \return TRUE to continue, FALSE to abort operation + * + */ + +EXPORT int InputError( + char * msg, + BOOL_T showLine, + ... ) +{ + va_list ap; + char * mp = message; + int ret; + + mp += sprintf( message, "INPUT ERROR: %s:%d\n", + paramFileName, paramLineNum ); + va_start( ap, showLine ); + mp += vsprintf( mp, msg, ap ); + va_end( ap ); + if (showLine) { + *mp++ = '\n'; + strcpy( mp, paramLine ); + } + strcat( mp, _("\nDo you want to continue?") ); + if (!(ret = wNoticeEx( NT_ERROR, message, _("Continue"), _("Stop") ))) { + if ( paramFile ) + fclose(paramFile); + paramFile = NULL; + } + return ret; +} + + +EXPORT void SyntaxError( + char * event, + wIndex_t actual, + wIndex_t expected ) +{ + InputError( "%s scan returned %d (expected %d)", + TRUE, event, actual, expected ); +} + +/** + * Parse a line in XTrackCAD's file format + * + * \param line IN line to parse + * \param format IN ??? + * + * \return FALSE in case of parsing error, TRUE on success + */ + +EXPORT BOOL_T GetArgs( + char * line, + char * format, + ... ) +{ + unsigned char * cp, * cq; + int argNo; + long * pl; + int * pi; + FLOAT_T *pf; + coOrd p, *pp; + char * ps; + char ** qp; + va_list ap; + char *oldLocale = NULL; + + oldLocale = SaveLocale("C"); + + cp = line; + va_start( ap, format ); + for (argNo=1;*format;argNo++,format++) { + while (isspace(*cp)) cp++; + if (!*cp && strchr( "XZYzc", *format ) == NULL ) { + RestoreLocale(oldLocale); + InputError( "Arg %d: EOL unexpected", TRUE, argNo ); + return FALSE; + } + switch (*format) { + case '0': + (void)strtol( cp, &cq, 10 ); + if (cp == cq) { + RestoreLocale(oldLocale); + InputError( "Arg %d: expected integer", TRUE, argNo ); + return FALSE; + } + cp = cq; + break; + case 'X': + pi = va_arg( ap, int * ); + *pi = 0; + break; + case 'Z': + pl = va_arg( ap, long * ); + *pl = 0; + break; + case 'Y': + pf = va_arg( ap, FLOAT_T * ); + *pf = 0; + break; + case 'L': + pi = va_arg( ap, int * ); + *pi = (int)strtol( cp, &cq, 10 ); + if (cp == cq) { + RestoreLocale(oldLocale); + InputError( "Arg %d: expected integer", TRUE, argNo ); + return FALSE; + } + cp = cq; + break; + case 'd': + pi = va_arg( ap, int * ); + *pi = (int)strtol( cp, &cq, 10 ); + if (cp == cq) { + RestoreLocale(oldLocale); + InputError( "Arg %d: expected integer", TRUE, argNo ); + return FALSE; + } + cp = cq; + break; + case 'w': + pf = va_arg( ap, FLOAT_T * ); + *pf = (FLOAT_T)strtol( cp, &cq, 10 ); + if (cp == cq) { + RestoreLocale(oldLocale); + InputError( "Arg %d: expected integer", TRUE, argNo ); + return FALSE; + } + if (*cq == '.') + *pf = strtod( cp, &cq ); + else + *pf /= mainD.dpi; + cp = cq; + break; + case 'l': + pl = va_arg( ap, long * ); + *pl = strtol( cp, &cq, 10 ); + if (cp == cq) { + RestoreLocale(oldLocale); + InputError( "Arg %d: expected integer", TRUE, argNo ); + return FALSE; + } + cp = cq; + break; + case 'f': + pf = va_arg( ap, FLOAT_T * ); + *pf = strtod( cp, &cq ); + if (cp == cq) { + RestoreLocale(oldLocale); + InputError( "Arg %d: expected float", TRUE, argNo ); + return FALSE; + } + cp = cq; + break; + case 'z': + pf = va_arg( ap, FLOAT_T * ); +#ifdef LATER + if ( paramVersion >= 9 ) { + *pf = strtod( cp, &cq ); + if (cp == cq) { + RestoreLocale(oldLocale); + InputError( "Arg %d: expected float", TRUE, argNo ); + return FALSE; + } + cp = cq; + } else { + *pf = 0.0; + } +#endif + *pf = 0.0; + break; + case 'p': + pp = va_arg( ap, coOrd * ); + p.x = strtod( cp, &cq ); + if (cp == cq) { + RestoreLocale(oldLocale); + InputError( "Arg %d: expected float", TRUE, argNo ); + return FALSE; + } + cp = cq; + p.y = strtod( cp, &cq ); + if (cp == cq) { + RestoreLocale(oldLocale); + InputError( "Arg %d: expected float", TRUE, argNo ); + return FALSE; + } + cp = cq; + *pp = p; + break; + case 's': + ps = va_arg( ap, char * ); + while (isspace(*cp)) cp++; + while (*cp && !isspace(*cp)) *ps++ = *cp++; + *ps++ = '\0'; + break; + case 'q': + qp = va_arg( ap, char * * ); + if (*cp != '\"') + /* Stupid windows */ + cq = strchr( cp, '\"' ); + else + cq = cp; + if (cq!=NULL) { + cp = cq; + ps = &message[0]; + cp++; + while (*cp) { + if ( (ps-message)>=sizeof message) + AbortProg( "Quoted title argument too long" ); + if (*cp == '\"') { + if (*++cp == '\"') { + *ps++ = '\"'; + } else { + *ps = '\0'; + cp++; + break; + } + } else { + *ps++ = *cp; + } + cp++; + } + *ps = '\0'; + } else { + message[0] = '\0'; + } + *qp = (char*)MyStrdup(message); + break; + case 'c': + qp = va_arg( ap, char * * ); + while (isspace(*cp)) cp++; + if (*cp) + *qp = cp; + else + *qp = NULL; + break; + default: + AbortProg( "getArgs: bad format char" ); + } + } + va_end( ap ); + RestoreLocale(oldLocale); + return TRUE; +} + +EXPORT wBool_t ParseRoomSize( + char * s, + coOrd * roomSizeRet ) +{ + coOrd size; + char *cp; + + size.x = strtod( s, &cp ); + if (cp != s) { + s = cp; + while (isspace(*s)) s++; + if (*s == 'x' || *s == 'X') { + size.y = strtod( ++s, &cp ); + if (cp != s) { +#ifdef LATER + if (units == UNITS_METRIC) { + size.x /= 2.54; + size.y /= 2.54; + } +#endif + *roomSizeRet = size; + return TRUE; + } + } + } + return FALSE; +} + + +EXPORT void AddParam( + char * name, + readParam_t proc ) +{ + DYNARR_APPEND( paramProc_t, paramProc_da, 10 ); + paramProc(paramProc_da.cnt-1).name = name; + paramProc(paramProc_da.cnt-1).proc = proc; +} + + +EXPORT BOOL_T ReadParams( + long key, + const char * dirName, + const char * fileName ) +{ + FILE * oldFile; + char *cp; + wIndex_t oldLineNum; + wIndex_t pc; + long oldCheckSum; + long checkSum=0; + BOOL_T checkSummed; + long paramVersion = -1; + char *oldLocale = NULL; + + if (dirName) { + strcpy( paramFileName, dirName ); + strcat( paramFileName, FILE_SEP_CHAR ); + strcat( paramFileName, fileName ); + } else { + strcpy( paramFileName, fileName ); + } + paramLineNum = 0; + curBarScale = -1; + curContents = strdup( fileName ); + curSubContents = curContents; + +LOG1( log_paramFile, ("ReadParam( %s )\n", fileName ) ) + + oldLocale = SaveLocale("C"); + + paramFile = fopen( paramFileName, "r" ); + if (paramFile == NULL) { + /* Reset the locale settings */ + RestoreLocale( oldLocale ); + + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Parameter"), paramFileName, strerror(errno) ); + + return FALSE; + } + paramCheckSum = key; + paramLineNum = 0; + checkSummed = FALSE; + while ( paramFile && ( fgets(paramLine, 256, paramFile) ) != NULL ) { + paramLineNum++; + Stripcr( paramLine ); + if (strncmp( paramLine, "CHECKSUM ", 9 ) == 0) { + checkSum = atol( paramLine+9 ); + checkSummed = TRUE; + goto nextLine; + } + ParamCheckSumLine( paramLine ); + if (paramLine[0] == '#') { + /* comment */ + } else if (paramLine[0] == 0) { + /* empty paramLine */ + } else if (strncmp( paramLine, "INCLUDE ", 8 ) == 0) { + cp = ¶mLine[8]; + while (*cp && isspace(*cp)) cp++; + if (!*cp) { + InputError( "INCLUDE - no file name", TRUE ); + + /* Close file and reset the locale settings */ + if (paramFile) fclose(paramFile); + RestoreLocale( oldLocale ); + + return FALSE; + } + oldFile = paramFile; + oldLineNum = paramLineNum; + oldCheckSum = paramCheckSum; + ReadParams( key, dirName, cp ); + paramFile = oldFile; + paramLineNum = oldLineNum; + paramCheckSum = oldCheckSum; + if (dirName) { + strcpy( paramFileName, dirName ); + strcat( paramFileName, FILE_SEP_CHAR ); + strcat( paramFileName, fileName ); + } else { + strcpy( paramFileName, fileName ); + } + } else if (strncmp( paramLine, "CONTENTS ", 9) == 0 ) { + curContents = MyStrdup( paramLine+9 ); + curSubContents = curContents; + } else if (strncmp( paramLine, "SUBCONTENTS ", 12) == 0 ) { + curSubContents = MyStrdup( paramLine+12 ); + } else if (strncmp( paramLine, "PARAM ", 6) == 0 ) { + paramVersion = atol( paramLine+6 ); + } else { + for (pc = 0; pc < paramProc_da.cnt; pc++ ) { + if (strncmp( paramLine, paramProc(pc).name, + strlen(paramProc(pc).name)) == 0 ) { + paramProc(pc).proc( paramLine ); + goto nextLine; + } + } + InputError( "Unknown param line", TRUE ); + } + nextLine:; + } + if ( key ) { + if ( !checkSummed || checkSum != paramCheckSum ) { + /* Close file and reset the locale settings */ + if (paramFile) fclose(paramFile); + RestoreLocale( oldLocale ); + + NoticeMessage( MSG_PROG_CORRUPTED, _("Ok"), NULL, paramFileName ); + + return FALSE; + } + } + if (paramFile)fclose( paramFile ); + + RestoreLocale( oldLocale ); + + return TRUE; +} + + +static void ReadCustom( void ) +{ + FILE * f; + customPath = + (char*)MyMalloc( strlen(workingDir) + 1 + strlen(sCustomF) + 1 ); + sprintf( customPath, "%s%s%s", workingDir, FILE_SEP_CHAR, sCustomF ); + customPathBak = MyStrdup( customPath ); + customPathBak[ strlen(customPathBak)-1 ] = '1'; + f = fopen( customPath, "r" ); + if ( f != NULL ) { + fclose( f ); + curParamFileIndex = PARAM_CUSTOM; + ReadParams( 0, workingDir, sCustomF ); + } +} + + +/* + * Open the file and then set the locale to "C". Old locale will be copied to + * oldLocale. After the required file I/O is done, the caller must call + * CloseCustom() with the same locale value that was returned in oldLocale by + * this function. + */ +EXPORT FILE * OpenCustom( char *mode ) +{ + FILE * ret = NULL; + + if (inPlayback) + return NULL; + if ( *mode == 'w' ) + rename( customPath, customPathBak ); + if (customPath) { + ret = fopen( customPath, mode ); + if (ret == NULL) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Custom"), customPath, strerror(errno) ); + } + } + + return ret; +} + + +EXPORT char * PutTitle( char * cp ) +{ + static char title[STR_SIZE]; + char * tp = title; + while (*cp && (tp-title)<=(sizeof title)-3) { + if (*cp == '\"') { + *tp++ = '\"'; + *tp++ = '\"'; + } else { + *tp++ = *cp; + } + cp++; + } + if ( *cp ) + NoticeMessage( _("putTitle: title too long: %s"), _("Ok"), NULL, title ); + *tp = '\0'; + return title; +} + +/** + * Set the title of the main window. After loading a file or changing a design + * this function is called to set the filename and the changed mark in the title + * bar. + */ + +void SetWindowTitle( void ) +{ + if ( changed > 2 || inPlayback ) + return; + sprintf( message, "%s%s - %s(%s)", + (curFileName==NULL||curFileName[0]=='\0')?_("Unnamed Trackplan"):curFileName, + changed>0?"*":"", sProdName, sVersion ); + wWinSetTitle( mainW, message ); +} + +/***************************************************************************** + * + * LOAD / SAVE TRACKS + * + */ + +static struct wFilSel_t * loadFile_fs; +static struct wFilSel_t * saveFile_fs; + +static wWin_p checkPointingW; +static paramData_t checkPointingPLs[] = { + { PD_MESSAGE, N_("Check Pointing") } }; +static paramGroup_t checkPointingPG = { "checkpoint", 0, checkPointingPLs, sizeof checkPointingPLs/sizeof checkPointingPLs[0] }; + +static char * checkPtFileName1; +static char * checkPtFileName2; + +/** Read the layout design. + * + * \param IN pathName filename including directory + * \param IN fileName pointer to filename part in pathName + * \param IN full + * \param IN noSetCurDir if FALSE current diurectory is changed to file location + * \param IN complain if FALSE error messages are supressed + * + * \return FALSE in case of load error + */ + +static BOOL_T ReadTrackFile( + const char * pathName, + const char * fileName, + BOOL_T full, + BOOL_T noSetCurDir, + BOOL_T complain ) +{ + int count; + coOrd roomSize; + long scale; + char * cp; + char *oldLocale = NULL; + int ret = TRUE; + + oldLocale = SaveLocale( "C" ); + + paramFile = fopen( pathName, "r" ); + if (paramFile == NULL) { + /* Reset the locale settings */ + RestoreLocale( oldLocale ); + + if ( complain ) + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, sProdName, pathName, strerror(errno) ); + + return FALSE; + } + + paramLineNum = 0; + strcpy( paramFileName, fileName ); + + InfoMessage("0"); + count = 0; + while ( paramFile && ( fgets(paramLine, sizeof paramLine, paramFile) ) != NULL ) { + count++; + if (count%10 == 0) { + InfoMessage( "%d", count ); + wFlush(); + } + paramLineNum++; + if (strlen(paramLine) == (sizeof paramLine) -1 && + paramLine[(sizeof paramLine)-1] != '\n') { + if( !(ret = InputError( "Line too long", TRUE ))) + break; + } + Stripcr( paramLine ); + if (paramLine[0] == '#' || + paramLine[0] == '\n' || + paramLine[0] == '\0' ) { + /* comment */ + continue; + } + + if (ReadTrack( paramLine )) { + + } else if (strncmp( paramLine, "END", 3 ) == 0) { + break; + } else if (strncmp( paramLine, "VERSION ", 8 ) == 0) { + paramVersion = strtol( paramLine+8, &cp, 10 ); + if (cp) + while (*cp && isspace(*cp)) cp++; + if ( paramVersion > iParamVersion ) { + if (cp && *cp) { + NoticeMessage( MSG_UPGRADE_VERSION1, _("Ok"), NULL, paramVersion, iParamVersion, sProdName, cp ); + } else { + NoticeMessage( MSG_UPGRADE_VERSION2, _("Ok"), NULL, paramVersion, iParamVersion, sProdName ); + } + break; + } + if ( paramVersion < iMinParamVersion ) { + NoticeMessage( MSG_BAD_FILE_VERSION, _("Ok"), NULL, paramVersion, iMinParamVersion, sProdName ); + break; + } + } else if (!full) { + if( !(ret = InputError( "unknown command", TRUE ))) + break; + } else if (strncmp( paramLine, "TITLE1 ", 7 ) == 0) { + strncpy( Title1, ¶mLine[7], TITLEMAXLEN ); + Title1[ TITLEMAXLEN - 1 ] = '\0'; + /*wStringSetValue( title1PD.control, Title1 );*/ + } else if (strncmp( paramLine, "TITLE2 ", 7 ) == 0) { + strncpy( Title2, ¶mLine[7], TITLEMAXLEN ); + Title2[ TITLEMAXLEN - 1 ] = '\0'; + /*wStringSetValue( title2PD.control, Title2 );*/ + } else if (strncmp( paramLine, "ROOMSIZE", 8 ) == 0) { + if ( ParseRoomSize( paramLine+8, &roomSize ) ) { + SetRoomSize( roomSize ); + /*wFloatSetValue( roomSizeXPD.control, PutDim(roomSize.x) );*/ + /*wFloatSetValue( roomSizeYPD.control, PutDim(roomSize.y) );*/ + } else { + if( !(ret = InputError( "ROOMSIZE: bad value", TRUE ))) + break; + } + } else if (strncmp( paramLine, "SCALE ", 6 ) == 0) { + if ( !DoSetScale( paramLine+5 ) ) { + if( !(ret = InputError( "SCALE: bad value", TRUE ))) + break; + } + } else if (strncmp( paramLine, "MAPSCALE ", 9 ) == 0) { + scale = atol( paramLine+9 ); + if (scale > 1) { + mapD.scale = mapScale = scale; + } + } else if (strncmp( paramLine, "LAYERS ", 7 ) == 0) { + ReadLayers( paramLine+7 ); + } else { + if( !(ret = InputError( "unknown command", TRUE ))) + break; + } + } + + if (paramFile) + fclose(paramFile); + + if( ret ) { + if (!noSetCurDir) + SetCurDir( pathName, fileName ); + + if (full) { + strcpy( curPathName, pathName ); + curFileName = &curPathName[fileName-pathName]; + SetWindowTitle(); + } + } + + RestoreLocale( oldLocale ); + + paramFile = NULL; + InfoMessage( "%d", count ); + return ret; +} + + +EXPORT int LoadTracks( + const char * pathName, + const char * fileName, + void * data) +{ +#ifdef TIME_READTRACKFILE + long time0, time1; +#endif + if (pathName == NULL) + return TRUE; + paramVersion = -1; + wSetCursor( wCursorWait ); + Reset(); + ClearTracks(); +/* DefaultLayerProperties(); */ + ResetLayers(); + checkPtMark = changed = 0; + UndoSuspend(); + useCurrentLayer = FALSE; +#ifdef TIME_READTRACKFILE + time0 = wGetTimer(); +#endif + if (ReadTrackFile( pathName, fileName, TRUE, FALSE, TRUE )) { + wMenuListAdd( fileList_ml, 0, fileName, MyStrdup(pathName) ); + ResolveIndex(); +#ifdef TIME_READTRACKFILE + time1 = wGetTimer(); + printf( "time= %ld ms \n", time1-time0 ); +#endif + RecomputeElevations(); + AttachTrains(); + DoChangeNotification( CHANGE_ALL ); + DoUpdateTitles(); + LoadLayerLists(); + } + UndoResume(); + /*DoRedraw();*/ + Reset(); + wSetCursor( wCursorNormal ); + return TRUE; +} + +/** + * Load the layout specified by data. Filename may contain a full + * path. + * \param index IN ignored + * \param label IN ignored + * \param data IN filename + */ + +EXPORT void DoFileList( + int index, + char * label, + void * data ) +{ + char * fileName, * pathName = (char*)data; + fileName = strrchr( pathName, FILE_SEP_CHAR[0] ); + if (fileName == NULL) + fileName = pathName; + else + fileName++; + LoadTracks( pathName, fileName, NULL ); +} + + +static BOOL_T DoSaveTracks( + const char * fileName ) +{ + FILE * f; + time_t clock; + BOOL_T rc = TRUE; + char *oldLocale = NULL; + + oldLocale = SaveLocale( "C" ); + + f = fopen( fileName, "w" ); + if (f==NULL) { + RestoreLocale( oldLocale ); + + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Track"), fileName, strerror(errno) ); + + return FALSE; + } + wSetCursor( wCursorWait ); + time(&clock); + rc &= fprintf(f,"#%s Version: %s, Date: %s\n", sProdName, sVersion, ctime(&clock) )>0; + rc &= fprintf(f, "VERSION %d %s\n", iParamVersion, PARAMVERSIONVERSION )>0; + Stripcr( Title1 ); + Stripcr( Title2 ); + rc &= fprintf(f, "TITLE1 %s\n", Title1 )>0; + rc &= fprintf(f, "TITLE2 %s\n", Title2 )>0; + rc &= fprintf(f, "MAPSCALE %ld\n", (long)mapD.scale )>0; + rc &= fprintf(f, "ROOMSIZE %0.6f x %0.6f\n", mapD.size.x, mapD.size.y )>0; + rc &= fprintf(f, "SCALE %s\n", curScaleName )>0; + rc &= WriteLayers( f ); + rc &= WriteMainNote( f ); + rc &= WriteTracks( f ); + rc &= fprintf(f, "END\n")>0; + if ( !rc ) + NoticeMessage( MSG_WRITE_FAILURE, _("Ok"), NULL, strerror(errno), fileName ); + fclose(f); + + RestoreLocale( oldLocale ); + + checkPtMark = changed; + wSetCursor( wCursorNormal ); + return rc; +} + + +static doSaveCallBack_p doAfterSave; + +static int SaveTracks( + const char * pathName, + const char * fileName, + void * data ) +{ + if (pathName == NULL) + return TRUE; + SetCurDir( pathName, fileName ); + DoSaveTracks( pathName ); + wMenuListAdd( fileList_ml, 0, fileName, MyStrdup(pathName) ); + checkPtMark = changed = 0; + if (curPathName != pathName) + strcpy( curPathName, pathName ); + curFileName = &curPathName[fileName-pathName]; + if (doAfterSave) + doAfterSave(); + doAfterSave = NULL; + return TRUE; +} + + +EXPORT void DoSave( doSaveCallBack_p after ) +{ + doAfterSave = after; + if (curPathName[0] == 0) { + if (saveFile_fs == NULL) + saveFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Save Tracks"), + sSourceFilePattern, SaveTracks, NULL ); + wFilSelect( saveFile_fs, curDirName ); + } else { + SaveTracks( curPathName, curFileName, NULL ); + } + SetWindowTitle(); +} + +EXPORT void DoSaveAs( doSaveCallBack_p after ) +{ + doAfterSave = after; + if (saveFile_fs == NULL) + saveFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Save Tracks"), + sSourceFilePattern, SaveTracks, NULL ); + wFilSelect( saveFile_fs, curDirName ); + SetWindowTitle(); +} + +EXPORT void DoLoad( void ) +{ + loadFile_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Open Tracks"), + sSourceFilePattern, LoadTracks, NULL ); + wFilSelect( loadFile_fs, curDirName ); +} + + +EXPORT void DoCheckPoint( void ) +{ + int rc; + + if (checkPointingW == NULL) { + ParamRegister( &checkPointingPG ); + checkPointingW = ParamCreateDialog( &checkPointingPG, MakeWindowTitle(_("Check Pointing")), NULL, NULL, NULL, FALSE, NULL, F_TOP|F_CENTER, NULL ); + } + rename( checkPtFileName1, checkPtFileName2 ); + wShow( checkPointingW ); + rc = DoSaveTracks( checkPtFileName1 ); + + /* could the check point file be written ok? */ + if( rc ) { + /* yes, delete the backup copy of the checkpoint file */ + remove( checkPtFileName2 ); + } else { + /* no, rename the backup copy back to the checkpoint file name */ + rename( checkPtFileName2, checkPtFileName1 ); + } + wHide( checkPointingW ); +} + +/** + * Remove all temporary files before exiting.When the program terminates + * normally through the exit choice, files that are created temporarily are removed: + * xtrkcad.ckp + * + * \param none + * \return none + * + */ + +EXPORT void CleanupFiles( void ) +{ + if( checkPtFileName1 ) + remove( checkPtFileName1 ); +} + +/** + * Check for existance of checkpoint file. Existance of a checkpoint file means that XTrkCAD was not properly + * terminated. + * + * \param none + * \return TRUE if exists, FALSE otherwise + * + */ + +EXPORT int ExistsCheckpoint( void ) +{ + int len; + char *pattern = sCheckPointF; + char *search; + + struct stat fileStat; + + len = strlen( workingDir ) + 1 + strlen( sCheckPointF ) + 1; + checkPtFileName1 = (char*)MyMalloc(len); + sprintf( checkPtFileName1, "%s%s%s", workingDir, FILE_SEP_CHAR, sCheckPointF ); + checkPtFileName2 = (char*)MyMalloc(len); + sprintf( checkPtFileName2, "%s%s%s", workingDir, FILE_SEP_CHAR, sCheckPoint1F ); + + len = strlen( workingDir ) + 1 + strlen( pattern ) + 1; + search = (char*)MyMalloc(len); + sprintf( search, "%s%s%s", workingDir, FILE_SEP_CHAR, pattern ); + + if( !stat( search, &fileStat ) ) { + MyFree( search ); + return TRUE; + } else { + MyFree( search ); + return FALSE; + } + + +#ifdef LATER + DIR *dir; + + dir = opendir( search ); + MyFree( search ); + + if( dir ) { + closedir( dir ); + return TRUE; + } else { + return FALSE; + } +#endif + +} + +/** + * Load checkpoint file + * + * \return TRUE if exists, FALSE otherwise + * + */ + +EXPORT int LoadCheckpoint( void ) +{ + int len; + char *search; + + paramVersion = -1; + wSetCursor( wCursorWait ); + + len = strlen( workingDir ) + 1 + strlen( sCheckPointF ) + 1; + search = (char*)MyMalloc(len); + sprintf( search, "%s%s%s", workingDir, FILE_SEP_CHAR, sCheckPointF ); + + UndoSuspend(); + + if (ReadTrackFile( search, search + strlen(search) - strlen( sCheckPointF ), TRUE, TRUE, TRUE )) { + ResolveIndex(); + + RecomputeElevations(); + AttachTrains(); + DoChangeNotification( CHANGE_ALL ); + DoUpdateTitles(); + } + + Reset(); + UndoResume(); + + wSetCursor( wCursorNormal ); + + strcpy( curPathName, "" ); + curFileName = curPathName; + SetWindowTitle(); + changed = TRUE; + MyFree( search ); + return TRUE; +} + +/***************************************************************************** + * + * IMPORT / EXPORT + * + */ + +static struct wFilSel_t * exportFile_fs; +static struct wFilSel_t * importFile_fs; +static struct wFilSel_t * exportDXFFile_fs; + + +static int ImportTracks( + const char * pathName, + const char * fileName, + void * data ) +{ + long paramVersionOld = paramVersion; + + if (pathName == NULL) + return TRUE; + paramVersion = -1; + wSetCursor( wCursorWait ); + Reset(); + SetAllTrackSelect( FALSE ); + ImportStart(); + UndoStart( _("Import Tracks"), "importTracks" ); + useCurrentLayer = TRUE; + ReadTrackFile( pathName, fileName, FALSE, FALSE, TRUE ); + ImportEnd(); + /*DoRedraw();*/ + EnableCommands(); + wSetCursor( wCursorNormal ); + paramVersion = paramVersionOld; + importMove = TRUE; + DoCommandB( (void*)(intptr_t)selectCmdInx ); + SelectRecount(); + return TRUE; +} + + +EXPORT void DoImport( void ) +{ + if (importFile_fs == NULL) + importFile_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Import Tracks"), + sImportFilePattern, ImportTracks, NULL ); + + wFilSelect( importFile_fs, curDirName ); +} + + +/** + * Export the selected track pieces + * + * \param pathname IN full path and filename for export file + * \param filename IN pointer to filename part *within* pathname + * \param data IN unused + * \return FALSE on error, TRUE on success + */ + +static int DoExportTracks( + const char * pathName, + const char * fileName, + void * data ) +{ + FILE * f; + time_t clock; + char *oldLocale = NULL; + + if (pathName == NULL) + return TRUE; + SetCurDir( pathName, fileName ); + f = fopen( pathName, "w" ); + if (f==NULL) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Export"), fileName, strerror(errno) ); + return FALSE; + } + + oldLocale = SaveLocale("C"); + + wSetCursor( wCursorWait ); + time(&clock); + fprintf(f,"#%s Version: %s, Date: %s\n", sProdName, sVersion, ctime(&clock) ); + fprintf(f, "VERSION %d %s\n", iParamVersion, PARAMVERSIONVERSION ); + ExportTracks( f ); + fprintf(f, "END\n"); + fclose(f); + + RestoreLocale( oldLocale ); + + Reset(); + wSetCursor( wCursorNormal ); + UpdateAllElevations(); + return TRUE; +} + + +EXPORT void DoExport( void ) +{ + if (selectedTrackCount <= 0) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return; + } + if (exportFile_fs == NULL) + exportFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Export Tracks"), + sImportFilePattern, DoExportTracks, NULL ); + + wFilSelect( exportFile_fs, curDirName ); +} + + +static FILE * dxfF; +static void DxfLine( + drawCmd_p d, + coOrd p0, + coOrd p1, + wDrawWidth width, + wDrawColor color ) +{ + fprintf(dxfF, " 0\nLINE\n" ); + fprintf(dxfF, " 8\n%s%d\n", sProdNameUpper, curTrackLayer+1 ); + fprintf(dxfF, " 10\n%0.6f\n 20\n%0.6f\n 11\n%0.6f\n 21\n%0.6f\n", + p0.x, p0.y, p1.x, p1.y ); + fprintf(dxfF, " 6\n%s\n", (d->options&DC_DASH)?"DASHED":"CONTINUOUS" ); +} + +static void DxfArc( + drawCmd_p d, + coOrd p, + DIST_T r, + ANGLE_T angle0, + ANGLE_T angle1, + BOOL_T drawCenter, + wDrawWidth width, + wDrawColor color ) +{ + angle0 = NormalizeAngle(90.0-(angle0+angle1)); + if (angle1 >= 360.0) { + fprintf(dxfF, " 0\nCIRCLE\n" ); + fprintf(dxfF, " 10\n%0.6f\n 20\n%0.6f\n 40\n%0.6f\n", + p.x, p.y, r ); + } else { + fprintf(dxfF, " 0\nARC\n" ); + fprintf(dxfF, " 10\n%0.6f\n 20\n%0.6f\n 40\n%0.6f\n 50\n%0.6f\n 51\n%0.6f\n", + p.x, p.y, r, angle0, angle0+angle1 ); + } + fprintf(dxfF, " 8\n%s%d\n", sProdNameUpper, curTrackLayer+1 ); + fprintf(dxfF, " 6\n%s\n", (d->options&DC_DASH)?"DASHED":"CONTINUOUS" ); +} + +static void DxfString( + drawCmd_p d, + coOrd p, + ANGLE_T a, + char * s, + wFont_p fp, + FONTSIZE_T fontSize, + wDrawColor color ) +{ + fprintf(dxfF, " 0\nTEXT\n" ); + fprintf(dxfF, " 1\n%s\n", s ); + fprintf(dxfF, " 8\n%s%d\n", sProdNameUpper, curTrackLayer+1 ); + fprintf(dxfF, " 10\n%0.6f\n 20\n%0.6f\n", p.x, p.y ); + fprintf(dxfF, " 40\n%0.6f\n", fontSize/72.0 ); +} + +static void DxfBitMap( + drawCmd_p d, + coOrd p, + wDrawBitMap_p bm, + wDrawColor color ) +{ +} + +static void DxfFillPoly( + drawCmd_p d, + int cnt, + coOrd * pts, + wDrawColor color ) +{ + int inx; + for (inx=1; inx<cnt; inx++) { + DxfLine( d, pts[inx-1], pts[inx], 0, color ); + } + DxfLine( d, pts[cnt-1], pts[0], 0, color ); +} + +static void DxfFillCircle( drawCmd_p d, coOrd center, DIST_T radius, wDrawColor color ) +{ + DxfArc( d, center, radius, 0.0, 360, FALSE, 0, color ); +} + + +static drawFuncs_t dxfDrawFuncs = { + 0, + DxfLine, + DxfArc, + DxfString, + DxfBitMap, + DxfFillPoly, + DxfFillCircle }; + +static drawCmd_t dxfD = { + NULL, &dxfDrawFuncs, 0, 1.0, 0.0, {0.0,0.0}, {0.0,0.0}, Pix2CoOrd, CoOrd2Pix, 100.0 }; + +static int DoExportDXFTracks( + const char * pathName, + const char * fileName, + void * data ) +{ + time_t clock; + char *oldLocale; + + if (pathName == NULL) + return TRUE; + SetCurDir( pathName, fileName ); + dxfF = fopen( pathName, "w" ); + if (dxfF==NULL) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, "DXF", fileName, strerror(errno) ); + return FALSE; + } + + oldLocale = SaveLocale( "C" ); + wSetCursor( wCursorWait ); + time(&clock); + fprintf(dxfF,"\ + 0\nSECTION\n\ + 2\nHEADER\n\ + 9\n$ACADVER\n 1\nAC1009\n\ + 9\n$EXTMIN\n 10\n%0.6f\n 20\n%0.6f\n\ + 9\n$EXTMAX\n 10\n%0.6f\n 20\n%0.6f\n\ + 9\n$TEXTSTYLE\n 7\nSTANDARD\n\ + 0\nENDSEC\n\ + 0\nSECTION\n\ + 2\nTABLES\n\ + 0\nTABLE\n\ + 2\nLTYPE\n\ + 0\nLTYPE\n 2\nCONTINUOUS\n 70\n0\n\ + 3\nSolid line\n\ + 72\n65\n 73\n0\n 40\n0\n\ + 0\nLTYPE\n 2\nDASHED\n 70\n0\n\ + 3\n__ __ __ __ __ __ __ __ __ __ __ __ __ __ __\n\ + 72\n65\n 73\n2\n 40\n0.15\n 49\n0.1\n 49\n-0.05\n\ + 0\nLTYPE\n 2\nDOT\n 70\n0\n\ + 3\n...............................................\n\ + 72\n65\n 73\n2\n 40\n0.1\n 49\n0\n 49\n-0.05\n\ + 0\nENDTAB\n\ + 0\nTABLE\n\ + 2\nLAYER\n\ + 70\n0\n\ + 0\nLAYER\n 2\n%s1\n 6\nCONTINUOUS\n 62\n7\n 70\n0\n\ + 0\nLAYER\n 2\n%s2\n 6\nCONTINUOUS\n 62\n7\n 70\n0\n\ + 0\nLAYER\n 2\n%s3\n 6\nCONTINUOUS\n 62\n7\n 70\n0\n\ + 0\nLAYER\n 2\n%s4\n 6\nCONTINUOUS\n 62\n7\n 70\n0\n\ + 0\nLAYER\n 2\n%s5\n 6\nCONTINUOUS\n 62\n7\n 70\n0\n\ + 0\nLAYER\n 2\n%s6\n 6\nCONTINUOUS\n 62\n7\n 70\n0\n\ + 0\nLAYER\n 2\n%s7\n 6\nCONTINUOUS\n 62\n7\n 70\n0\n\ + 0\nLAYER\n 2\n%s8\n 6\nCONTINUOUS\n 62\n7\n 70\n0\n\ + 0\nLAYER\n 2\n%s9\n 6\nCONTINUOUS\n 62\n7\n 70\n0\n\ + 0\nLAYER\n 2\n%s10\n 6\nCONTINUOUS\n 62\n7\n 70\n0\n\ + 0\nENDTAB\n\ + 0\nENDSEC\n\ + 0\nSECTION\n\ + 2\nENTITIES\n\ +", + 0.0, 0.0, mapD.size.x, mapD.size.y, + sProdNameUpper, sProdNameUpper, sProdNameUpper, sProdNameUpper, sProdNameUpper, + sProdNameUpper, sProdNameUpper, sProdNameUpper, sProdNameUpper, sProdNameUpper ); + DrawSelectedTracks( &dxfD ); + fprintf(dxfF," 0\nENDSEC\n"); + fprintf(dxfF," 0\nEOF\n"); + fclose(dxfF); + RestoreLocale( oldLocale ); + Reset(); + wSetCursor( wCursorNormal ); + return TRUE; +} + + +void DoExportDXF( void ) +{ + if (selectedTrackCount <= 0) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return; + } + if (exportDXFFile_fs == NULL) + exportDXFFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Export to DXF"), + sDXFFilePattern, DoExportDXFTracks, NULL ); + + wFilSelect( exportDXFFile_fs, curDirName ); +} + +EXPORT BOOL_T EditCopy( void ) +{ + FILE * f; + time_t clock; + char *oldLocale = NULL; + + if (selectedTrackCount <= 0) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return FALSE; + } + f = fopen( clipBoardN, "w" ); + if (f == NULL) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Clipboard"), clipBoardN, strerror(errno) ); + return FALSE; + } + + oldLocale = SaveLocale("C"); + + time(&clock); + fprintf(f,"#%s Version: %s, Date: %s\n", sProdName, sVersion, ctime(&clock) ); + fprintf(f, "VERSION %d %s\n", iParamVersion, PARAMVERSIONVERSION ); + ExportTracks(f); + fprintf(f, "END\n"); + RestoreLocale(oldLocale); + fclose(f); + return TRUE; +} + + +EXPORT BOOL_T EditCut( void ) +{ + if (!EditCopy()) + return FALSE; + SelectDelete(); + return TRUE; +} + +/** + * Paste clipboard content. XTrackCAD uses a disk file as clipboard replacement. This file is read and the + * content is inserted. + * + * \return TRUE if success, FALSE on error (file not found) + */ + +EXPORT BOOL_T EditPaste( void ) +{ + BOOL_T rc = TRUE; + char *oldLocale = NULL; + + oldLocale = SaveLocale("C"); + + wSetCursor( wCursorWait ); + Reset(); + SetAllTrackSelect( FALSE ); + ImportStart(); + UndoStart( _("Paste"), "paste" ); + useCurrentLayer = TRUE; + if ( !ReadTrackFile( clipBoardN, sClipboardF, FALSE, TRUE, FALSE ) ) { + NoticeMessage( MSG_CANT_PASTE, _("Continue"), NULL ); + rc = FALSE; + } + ImportEnd(); + /*DoRedraw();*/ + EnableCommands(); + wSetCursor( wCursorNormal ); + importMove = TRUE; + DoCommandB( (void*)(intptr_t)selectCmdInx ); + SelectRecount(); + UpdateAllElevations(); + RestoreLocale(oldLocale); + return rc; +} + +/***************************************************************************** + * + * INITIALIZATION + * + */ + +EXPORT void FileInit( void ) +{ + const char * pref; + + if ( (libDir = wGetAppLibDir()) == NULL ) { + abort(); + } + if ( (workingDir = wGetAppWorkDir()) == NULL ) + AbortProg( "wGetAppWorkDir()" ); + + pref = wPrefGetString( "file", "directory" ); + if (pref != NULL) { + strcpy( curDirName, pref ); + } else { + sprintf( curDirName, "%s%sexamples", libDir, FILE_SEP_CHAR ); + } +} + +EXPORT BOOL_T ParamFileInit( void ) +{ + curParamFileIndex = PARAM_DEMO; + log_paramFile = LogFindIndex( "paramFile" ); + if ( ReadParams( lParamKey, libDir, sParamQF ) == FALSE ) + return FALSE; + + curParamFileIndex = PARAM_CUSTOM; + if (lParamKey == 0) { + ReadParamFiles(); + ReadCustom(); + } + + curPathName[0] = '\0'; + + clipBoardN = (char*)MyMalloc( strlen(workingDir) + 1 + strlen(sClipboardF) + 1 ); + sprintf( clipBoardN, "%s%s%s", workingDir, FILE_SEP_CHAR, sClipboardF ); + return TRUE; + +} diff --git a/app/bin/fileio.h b/app/bin/fileio.h new file mode 100644 index 0000000..b5abc78 --- /dev/null +++ b/app/bin/fileio.h @@ -0,0 +1,123 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/fileio.h,v 1.4 2008-01-15 11:46:03 mni77 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 FILEIO_H +#define FILEIO_H + +FILE * paramFile; +char paramFileName[STR_LONG_SIZE]; +wIndex_t paramLineNum; +char paramLine[STR_LONG_SIZE]; +char * curContents; +char * curSubContents; +#define PARAM_DEMO (-1) + +typedef void (*playbackProc_p)( char * ); +typedef BOOL_T (*readParam_t) ( char * ); + +extern const char * workingDir; +extern const char * libDir; + +extern char curPathName[STR_LONG_SIZE]; +extern char * curFileName; +extern char curDirName[STR_LONG_SIZE]; + +#define PARAM_CUSTOM (-2) +#define PARAM_LAYOUT (-3) +extern int curParamFileIndex; + +extern unsigned long playbackTimer; + +extern wBool_t executableOk; + +extern FILE * recordF; +wBool_t inPlayback; +wBool_t inPlaybackQuit; +wWin_p demoW; +int curDemo; + +wMenuList_p fileList_ml; + +void SetCurDir( const char *, const char * ); + +void Stripcr( char * ); +char * GetNextLine( void ); + +BOOL_T GetArgs( char *, char *, ... ); +BOOL_T ParseRoomSize( char *, coOrd * ); +int InputError( char *, BOOL_T, ... ); +void SyntaxError( char *, wIndex_t, wIndex_t ); + +void AddParam( char *, readParam_t ); + +FILE * OpenCustom( char * ); + +#ifdef WINDOWS +#define fopen( FN, MODE ) wFileOpen( FN, MODE ) +#endif + +void SetWindowTitle( void ); +char * PutTitle( char * cp ); +wBool_t IsParamValid( int ); +char * GetParamFileName( int ); +void RememberParamFiles( void ); +int LoadParamFile( const char *, const char *, void * ); +void ReadParamFiles( void ); +int LoadTracks( const char *, const char *, void * ); +BOOL_T ReadParams( long, const char *, const char * ); + +typedef void (*doSaveCallBack_p)( void ); +void DoSave( doSaveCallBack_p ); +void DoSaveAs( doSaveCallBack_p ); +void DoLoad( void ); +void DoFileList( int, char *, void * ); +void DoCheckPoint( void ); +void CleanupFiles( void ); +int ExistsCheckpoint( void ); +int LoadCheckpoint( void ); +void DoImport( void ); +void DoExport( void ); +void DoExportDXF( void ); +BOOL_T EditCopy( void ); +BOOL_T EditCut( void ); +BOOL_T EditPaste( void ); + + +void DoRecord( void * ); +void AddPlaybackProc( char *, playbackProc_p, void * ); +EXPORT void TakeSnapshot( drawCmd_t * ); +void PlaybackMessage( char * ); +void DoPlayBack( void * ); +int MyGetKeyState( void ); + +int RegLevel( void ); +void ReadKey( void ); +void PopupRegister( void * ); + +void FileInit( void ); +BOOL_T ParamFileInit( void ); +BOOL_T MacroInit( void ); + +char *SaveLocale( char *newLocale ); +void RestoreLocale( char * locale ); + +#endif diff --git a/app/bin/i18n.c b/app/bin/i18n.c new file mode 100644 index 0000000..92c68cf --- /dev/null +++ b/app/bin/i18n.c @@ -0,0 +1,50 @@ +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2007 Mikko Nissinen + * + * 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 "i18n.h" +#include "wlib.h" + +#include <locale.h> +#include <stdio.h> + +/* + * Initialize gettext environment. + */ +void InitGettext( void ) +{ +#ifdef XTRKCAD_USE_GETTEXT + char directory[2048]; + setlocale(LC_ALL, ""); +#ifdef XTRKCAD_CMAKE_BUILD + strcpy(directory, XTRKCAD_INSTALL_PREFIX); + strcat(directory, "/share"); +#else + strcpy(directory, wGetAppLibDir()); +#endif + strcat(directory, "/locale"); + bindtextdomain(XTRKCAD_PACKAGE, directory); + bind_textdomain_codeset(XTRKCAD_PACKAGE, "UTF-8"); + textdomain(XTRKCAD_PACKAGE); + +#ifdef VERBOSE + printf(_("Gettext initialized (PACKAGE=%s, LOCALEDIR=%s, LC_ALL=%s).\n"), + XTRKCAD_PACKAGE, directory, setlocale(LC_ALL, NULL)); +#endif + +#endif /* XTRKCAD_USE_GETTEXT */ +} diff --git a/app/bin/i18n.h b/app/bin/i18n.h new file mode 100644 index 0000000..f70c87d --- /dev/null +++ b/app/bin/i18n.h @@ -0,0 +1,43 @@ +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2007 Mikko Nissinen + * + * 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 "xtrkcad-config.h" + +#ifdef XTRKCAD_USE_GETTEXT +/* Use gettext */ + #ifndef USE_SIMPLE_GETTEXT + #include <libintl.h> + #endif + + #include <string.h> + + #define _(String) ((String && strlen(String) > 0) \ + ? gettext(String) : String) + #define p_(Context, String) ((Context && strlen(Context) > 0) \ + ? pgettext(Context, String) : _(String)) + #define gettext_noop(String) String + #define N_(String) gettext_noop(String) + +#else + /* Don't use gettext */ + #define _(String) String + #define gettext_noop(String) String + #define N_(String) String +#endif /* XTRKCAD_USE_GETTEXT */ + +void InitGettext( void ); diff --git a/app/bin/lprintf.c b/app/bin/lprintf.c new file mode 100644 index 0000000..c0f1c00 --- /dev/null +++ b/app/bin/lprintf.c @@ -0,0 +1,147 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/lprintf.c,v 1.2 2006-05-26 17:31:44 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 <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#ifndef WINDOWS +#include <time.h> +#else +#include <time.h> +#include <sys/timeb.h> +#endif +#include "track.h" + +/**************************************************************************** + * + * LPRINTF + * + */ + + +EXPORT dynArr_t logTable_da; + +static FILE * logFile; +static char * logFileName; +EXPORT time_t logClock = 0; +static BOOL_T logInitted = FALSE; +static long logLineNumber; + +static void LogInit( void ) +{ + int inx; + + if ( logTable_da.cnt != 0 ) + return; + DYNARR_SET( logTable_t, logTable_da, 11 ); + for ( inx=0; inx<=10; inx++ ) { + logTable(inx).name = ""; + logTable(inx).level = inx; + } +} + +EXPORT void LogOpen( char * filename ) +{ + time( &logClock ); + logFileName = filename; + LogInit(); +} + + +static void LogDoOpen( void ) +{ + if ( logFileName == NULL ) { +#ifdef WINDOWS + logFileName = (char*)MyMalloc( strlen(wGetAppWorkDir()) + 1 + strlen("xtclog.txt") + 1); + sprintf( logFileName, "%s%s%s", wGetAppWorkDir(), FILE_SEP_CHAR, "xtclog.txt" ); +#else + logFile = stdout; +#endif + } + + if ( logFileName ) { + logFile = fopen( logFileName, "a" ); + if ( logFile == NULL ) { + NoticeMessage( MSG_OPEN_FAIL, "Continue", NULL, "Log", logFileName, strerror(errno) ); + perror( logFileName ); + return; + } + } + fprintf( logFile, "# %s Version: %s, Date: %s\n", sProdName, sVersion, ctime(&logClock) ); + if ( recordF ) + fprintf( recordF, "# LOG CLOCK %s\n", ctime(&logClock) ); +} + +EXPORT void LogClose( void ) +{ + time_t clock; + if ( logFile ) { + time(&clock); + fprintf( logFile, "LOG END %s\n", ctime(&clock) ); + if ( logFile != stdout ) + fclose( logFile ); + } + logFile = NULL; +} + +EXPORT void LogSet( char * name, int level ) +{ + LogInit(); + DYNARR_APPEND( logTable_t, logTable_da, 10 ); + logTable(logTable_da.cnt-1).name = MyStrdup( name ); + logTable(logTable_da.cnt-1).level = level; +} + +EXPORT int LogFindIndex( char * name ) +{ + int inx; + for ( inx=11; inx<logTable_da.cnt; inx++ ) + if ( strcasecmp( logTable(inx).name, name ) == 0 ) + return inx; + return 0; +} + +EXPORT void LogPrintf( + char * format, + ... ) +{ + va_list ap; + if (!logInitted) { + LogDoOpen(); + logInitted = TRUE; + } + if ( logFile == NULL ) + return; + logLineNumber++; + if ( logLineNumber % 100 == 0 ) { + if ( recordF ) { + fprintf( recordF, "# LOG LINE %ld\n", logLineNumber ); + fprintf( logFile, "LOG LINE %ld\n", logLineNumber ); + } + } + va_start( ap, format ); + vfprintf( logFile, format, ap ); + va_end( ap ); + fflush( logFile ); +} + diff --git a/app/bin/macro.c b/app/bin/macro.c new file mode 100644 index 0000000..88ee928 --- /dev/null +++ b/app/bin/macro.c @@ -0,0 +1,1440 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/macro.c,v 1.7 2009-06-15 19:29:57 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 <stdlib.h> +#include <stdio.h> +#ifndef WINDOWS +#include <unistd.h> +#include <dirent.h> +#include <errno.h> +#endif +#include <math.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#ifdef WINDOWS +#include <io.h> +#include <windows.h> +#else +#include <sys/stat.h> +#endif +#include <stdarg.h> +#ifndef WINDOWS +#include <sys/time.h> +#else +#include <sys/timeb.h> +#endif +#include <locale.h> + +#include <stdint.h> + +#include "track.h" +#include "version.h" +#include "common.h" +#include "utility.h" +#include "draw.h" +#include "misc.h" +#include "compound.h" +#include "i18n.h" + +EXPORT long adjTimer; +static void DemoInitValues( void ); + +extern char *userLocale; + +/***************************************************************************** + * + * RECORD + * + */ + +EXPORT FILE * recordF; +static wWin_p recordW; +struct wFilSel_t * recordFile_fs; +static BOOL_T recordMouseMoves = TRUE; + +static void DoRecordButton( void * context ); +static paramTextData_t recordTextData = { 50, 16 }; +static paramData_t recordPLs[] = { +#define I_RECSTOP (0) +#define recStopB ((wButton_p)recordPLs[I_RECSTOP].control) + { PD_BUTTON, (void*)DoRecordButton, "stop", PDO_NORECORD, NULL, N_("Stop"), 0, (void*)0 }, +#define I_RECMESSAGE (1) +#define recMsgB ((wButton_p)recordPLs[I_RECMESSAGE].control) + { PD_BUTTON, (void*)DoRecordButton, "message", PDO_NORECORD|PDO_DLGHORZ, NULL, N_("Message"), 0, (void*)2 }, +#define I_RECEND (2) +#define recEndB ((wButton_p)recordPLs[I_RECEND].control) + { PD_BUTTON, (void*)DoRecordButton, "end", PDO_NORECORD|PDO_DLGHORZ, NULL, N_("End"), BO_DISABLED, (void*)4 }, +#define I_RECTEXT (3) +#define recordT ((wText_p)recordPLs[I_RECTEXT].control) + { PD_TEXT, NULL, "text", PDO_NORECORD|PDO_DLGRESIZE, &recordTextData, NULL, BT_CHARUNITS|BO_READONLY} }; +static paramGroup_t recordPG = { "record", 0, recordPLs, sizeof recordPLs/sizeof recordPLs[0] }; + + +#ifndef WINDOWS +static struct timeval lastTim = {0,0}; +static void ComputePause( void ) +{ + struct timeval tim; + long secs; + long msecs; + gettimeofday( &tim, NULL ); + secs = tim.tv_sec-lastTim.tv_sec; + if (secs > 10 || secs < 0) + return; + msecs = secs * 1000 + (tim.tv_usec - lastTim.tv_usec)/1000; + if (msecs > 5000) + msecs = 5000; + if (msecs > 1) + fprintf( recordF, "PAUSE %ld\n", msecs ); + lastTim = tim; +} +#else +static struct _timeb lastTim; +static void ComputePause( void ) +{ + struct _timeb tim; + long secs, msecs; + _ftime( &tim ); + secs = (long)(tim.time - lastTim.time); + if (secs > 10 || secs < 0) + return; + msecs = secs * 1000; + if (tim.millitm >= lastTim.millitm) { + msecs += (tim.millitm - lastTim.millitm); + } else { + msecs -= (lastTim.millitm - tim.millitm); + } + if (msecs > 5000) + msecs = 5000; + if (msecs > 1) + fprintf( recordF, "PAUSE %ld\n", msecs ); + lastTim = tim; +} +#endif + + +EXPORT void RecordMouse( char * name, wAction_t action, POS_T px, POS_T py ) +{ + int keyState; + if ( action == C_MOVE || action == C_RMOVE || (action&0xFF) == C_TEXT ) + ComputePause(); + else if ( action == C_DOWN || action == C_RDOWN ) +#ifndef WINDOWS + gettimeofday( &lastTim, NULL ); +#else + _ftime( &lastTim ); +#endif + if (action == wActionMove && !recordMouseMoves) + return; + keyState = wGetKeyState(); + if (keyState) + fprintf( recordF, "KEYSTATE %d\n", keyState ); + fprintf( recordF, "%s %d %0.3f %0.3f\n", name, (int)action, px, py ); + fflush( recordF ); +} + + +static int StartRecord( const char * pathName, const char * fileName, void * context ) +{ + time_t clock; + if (pathName == NULL) + return TRUE; + SetCurDir( pathName, fileName ); + recordF = fopen(pathName, "w"); + if (recordF==NULL) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Recording"), fileName, strerror(errno) ); + return FALSE; + } + time(&clock); + fprintf(recordF, "# %s Version: %s, Date: %s\n", sProdName, sVersion, ctime(&clock) ); + fprintf(recordF, "VERSION %d\n", iParamVersion ); + fprintf(recordF, "ROOMSIZE %0.1f x %0.1f\n", mapD.size.x, mapD.size.y ); + fprintf(recordF, "SCALE %s\n", curScaleName ); + fprintf(recordF, "ORIG %0.3f %0.3f %0.3f\n", mainD.scale, mainD.orig.x, mainD.orig.y ); + if ( logClock != 0 ) + fprintf( recordF, "# LOG CLOCK %s\n", ctime(&logClock) ); + if ( logTable_da.cnt > 11 ) + lprintf( "StartRecord( %s ) @ %s\n", pathName, ctime(&clock) ); + ParamStartRecord(); + WriteTracks( recordF ); + WriteLayers( recordF ); + fprintf( recordF, "REDRAW\n" ); + fflush( recordF ); + wTextClear( recordT ); + wShow( recordW ); + Reset(); + wControlActive( (wControl_p)recEndB, FALSE ); + return TRUE; +} + + +static void DoRecordButton( void * context ) +{ + static wBool_t recordingMessage = FALSE; + char * cp; + int len; + + switch( (int)(long)context ){ + case 0: /* Stop */ + fprintf( recordF, "CLEAR\nMESSAGE\n"); + fprintf( recordF, N_("End of Playback. Hit Step to exit\n")); + fprintf( recordF, "END\nSTEP\n" ); + fclose( recordF ); + recordF = NULL; + wHide( recordW ); + break; + + case 1: /* Step */ + fprintf( recordF, "STEP\n" ); + break; + + case 4: /* End */ + if (recordingMessage) { + len = wTextGetSize( recordT ); + if (len == 0) + break; + cp = (char*)MyMalloc( len+2 ); + wTextGetText( recordT, cp, len ); + if ( cp[len-1] == '\n' ) len--; + cp[len] = '\0'; + fprintf( recordF, "%s\nEND\nSTEP\n", cp ); + MyFree( cp ); + recordingMessage = FALSE; + } + wTextSetReadonly( recordT, TRUE ); + fflush( recordF ); + wControlActive( (wControl_p)recStopB, TRUE ); + wControlActive( (wControl_p)recMsgB, TRUE ); + wControlActive( (wControl_p)recEndB, FALSE ); + wWinSetBusy( mainW, FALSE ); + break; + + case 2: /* Message */ + fprintf( recordF, "MESSAGE\n" ); + wTextSetReadonly( recordT, FALSE ); + wTextClear( recordT ); + recordingMessage = TRUE; + wControlActive( (wControl_p)recStopB, FALSE ); + wControlActive( (wControl_p)recMsgB, FALSE ); + wControlActive( (wControl_p)recEndB, TRUE ); + wWinSetBusy( mainW, TRUE ); + break; + + case 3: /* Pause */ + fprintf( recordF, "BIGPAUSE\n" ); + fflush( recordF ); + break; + + case 5: /* CLEAR */ + fprintf( recordF, "CLEAR\n" ); + fflush( recordF ); + wTextClear( recordT ); + break; + + default: + ; + } +} + + + +EXPORT void DoRecord( void * context ) +{ + if (recordW == NULL) { + char * title = MakeWindowTitle(_("Record")); + recordW = ParamCreateDialog( &recordPG, title, NULL, NULL, NULL, FALSE, NULL, F_RESIZE, NULL ); + recordFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, title, sRecordFilePattern, StartRecord, NULL ); + } + wTextClear( recordT ); + wFilSelect( recordFile_fs, curDirName ); +} + +/***************************************************************************** + * + * PLAYBACK MOUSE + * + */ + +static wDrawBitMap_p playbackBm = NULL; +static wDrawColor playbackColor; +static drawCmd_p playbackD; +static wPos_t playbackX, playbackY; + +#include "bitmaps/arrow0.xbm" +#include "bitmaps/arrow3.xbm" +#include "bitmaps/arrows.xbm" +#include "bitmaps/flash.xbm" + +static wDrawColor rightDragColor; +static wDrawColor leftDragColor; +static wDrawBitMap_p arrow0_bm; +static wDrawBitMap_p arrow3_bm; +static wDrawBitMap_p arrows_bm; +static wDrawBitMap_p flash_bm; + +static long flashTO = 60; +static DIST_T PixelsPerStep = 20; +static long stepTO = 100; +EXPORT unsigned long playbackTimer; + +static wBool_t didPause; +static wBool_t flashTwice = FALSE; + + +#define DRAWALL +static void MacroDrawBitMap( + drawCmd_p d, + wDrawBitMap_p bm, + wPos_t x, + wPos_t y, + wDrawColor color ) +{ + wDrawBitMap( d->d, bm, x, y, color, wDrawOptTemp|wDrawOptNoClip ); + wFlush(); +} + + +static void Flash( drawCmd_p d, wPos_t x, wPos_t y, wDrawColor flashColor ) +{ + if (playbackTimer != 0) + return; + MacroDrawBitMap( d, flash_bm, x, y, flashColor ); + wPause( flashTO ); + MacroDrawBitMap( d, flash_bm, x, y, flashColor ); + wPause( flashTO ); +#ifdef LATER + MacroDrawBitMap( d->d, flash_bm, x, y, flashColor ); + wPause( flashTO ); + MacroDrawBitMap( d->d, flash_bm, x, y, flashColor ); + wPause( flashTO ); +#endif +} + + +EXPORT long playbackDelay = 100; +static long playbackSpeed = 2; + +static void SetPlaybackSpeed( + wIndex_t inx ) +{ + switch (inx) { + case 0: playbackDelay = 500; break; + case 1: playbackDelay = 200; break; + default: + case 2: playbackDelay = 100; break; + case 3: playbackDelay = 50; break; + case 4: playbackDelay = 15; break; + case 5: playbackDelay = 0; break; + } + playbackSpeed = inx; +} + +static void ClearPlaybackCursor( void ) +{ + if (playbackBm != NULL) + MacroDrawBitMap( playbackD, playbackBm, playbackX, playbackY, playbackColor ); + playbackBm = NULL; +} + + +static void MoveCursor( + drawCmd_p d, + playbackProc proc, + wAction_t action, + coOrd pos, + wDrawBitMap_p bm, + wDrawColor color ) +{ + DIST_T dist, dx, dy; + coOrd pos1, dpos; + int i, steps; + wPos_t x, y; + wPos_t xx, yy; + + ClearPlaybackCursor(); + + if (d == NULL) + return; + + pos1 = pos; + d->CoOrd2Pix( d, pos, &x, &y ); + + if (playbackTimer == 0 && playbackD == d && !didPause) { + dx = (DIST_T)(x-playbackX); + dy = (DIST_T)(y-playbackY); + dist = sqrt( dx*dx + dy*dy ); + steps = (int)(dist / PixelsPerStep ) + 1; + dx /= steps; + dy /= steps; + d->Pix2CoOrd( d, playbackX, playbackY, &pos1 ); + dpos.x = (pos.x-pos1.x)/steps; + dpos.y = (pos.y-pos1.y)/steps; + for ( i=1; i<=steps; i++ ) { + xx = playbackX+(wPos_t)(i*dx); + yy = playbackY+(wPos_t)(i*dy); + MacroDrawBitMap( d, bm, xx, yy, color ); + pos1.x += dpos.x; + pos1.y += dpos.y; + if (proc) + proc( action, pos1 ); + else if (d->d == mainD.d) { + InfoPos( pos1 ); + } + wPause( stepTO*playbackDelay/100 ); + MacroDrawBitMap( d, bm, xx, yy, color ); + if (!inPlayback) { + return; + } + } + } + MacroDrawBitMap( playbackD=d, playbackBm=bm, playbackX=x, playbackY=y, playbackColor=color ); +} + + +static void PlaybackCursor( + drawCmd_p d, + playbackProc proc, + wAction_t action, + coOrd pos, + wDrawColor color ) +{ + wDrawBitMap_p bm; + wPos_t x, y; + long time0, time1; + + time0 = wGetTimer(); + ClearPlaybackCursor(); + + d->CoOrd2Pix( d, pos, &x, &y ); + + switch( action ) { + + case wActionMove: + MacroDrawBitMap( playbackD=d, playbackBm=arrow0_bm, playbackX=x, playbackY=y, playbackColor=wDrawColorBlack ); + break; + + case C_DOWN: + MoveCursor( d, proc, wActionMove, pos, arrow0_bm, wDrawColorBlack ); + if (flashTwice) Flash( d, x, y, rightDragColor ); + MacroDrawBitMap( d, arrow0_bm, x, y, wDrawColorBlack ); + MacroDrawBitMap( playbackD=d, playbackBm=((MyGetKeyState()&WKEY_SHIFT)?arrows_bm:arrow3_bm), playbackX=x, playbackY=y, + playbackColor=rightDragColor ); + Flash( d, x, y, rightDragColor ); + break; + + case C_MOVE: + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrows_bm:arrow3_bm); + MoveCursor( d, proc, C_MOVE, pos, bm, rightDragColor ); + playbackD=d; playbackBm=bm; playbackX=x; playbackY=y; playbackColor=rightDragColor; + break; + + case C_UP: + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrows_bm:arrow3_bm); + MoveCursor( d, proc, C_MOVE, pos, bm, rightDragColor ); + /*MacroDrawBitMap( d, bm, x, y, rightDragColor );*/ + if (flashTwice) Flash( d, x, y, rightDragColor ); + MacroDrawBitMap( d, bm, x, y, rightDragColor ); + MacroDrawBitMap( playbackD=d, playbackBm=arrow0_bm, playbackX=x, playbackY=y, playbackColor=wDrawColorBlack ); + Flash( d, x, y, rightDragColor ); + break; + + case C_RDOWN: + MoveCursor( d, proc, wActionMove, pos, arrow0_bm, wDrawColorBlack ); + if (flashTwice) Flash( d, x, y, leftDragColor ); + MacroDrawBitMap( d, arrow0_bm, x, y, wDrawColorBlack ); + MacroDrawBitMap( playbackD=d, playbackBm=((MyGetKeyState()&WKEY_SHIFT)?arrows_bm:arrow3_bm), playbackX=x, playbackY=y, playbackColor=leftDragColor ); + Flash( d, x, y, leftDragColor ); + break; + + case C_RMOVE: + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrows_bm:arrow3_bm); + MoveCursor( d, proc, C_RMOVE, pos, bm, leftDragColor ); + playbackD=d; playbackBm=bm; playbackX=x; playbackY=y; playbackColor=leftDragColor; + break; + + case C_RUP: + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrows_bm:arrow3_bm); + MoveCursor( d, proc, C_RMOVE, pos, bm, leftDragColor ); + if (flashTwice) Flash( d, x, y, leftDragColor ); + MacroDrawBitMap( d, bm, x, y, leftDragColor ); + MacroDrawBitMap( playbackD=d, playbackBm=arrow0_bm, playbackX=x, playbackY=y, playbackColor=wDrawColorBlack ); + Flash( d, x, y, leftDragColor ); + break; + + case C_REDRAW: + MacroDrawBitMap( playbackD, playbackBm, playbackX, playbackY, playbackColor ); + break; + + default: + ; + } + time1 = wGetTimer(); + adjTimer += (time1-time0); +} + + +EXPORT void PlaybackMouse( + playbackProc proc, + drawCmd_p d, + wAction_t action, + coOrd pos, + wDrawColor color ) +{ +#ifdef LATER + if (action == C_DOWN || action == C_RDOWN) { + MoveCursor( d, proc, wActionMove, pos, arrow0_bm, wDrawColorBlack ); + ClearPlaybackCursor(); + } else { + PlaybackCursor( d, proc, action, pos, wDrawColorBlack ); + } +#endif + PlaybackCursor( d, proc, action, pos, wDrawColorBlack ); + if (playbackBm != NULL) + MacroDrawBitMap( playbackD, playbackBm, playbackX, playbackY, playbackColor ); + proc( action, pos ); + if (playbackBm != NULL) + MacroDrawBitMap( playbackD, playbackBm, playbackX, playbackY, playbackColor ); +#ifdef LATER + if (action == C_DOWN || action == C_RDOWN) { + PlaybackCursor( d, proc, action, pos, wDrawColorBlack ); + } +#endif + didPause = FALSE; +} + + +EXPORT void MovePlaybackCursor( + drawCmd_p d, + wPos_t x, + wPos_t y ) +{ + coOrd pos; + d->Pix2CoOrd( d, x, y, &pos ); + d->CoOrd2Pix( d, pos, &x, &y ); + MoveCursor( d, NULL, wActionMove, pos, arrow0_bm, wDrawColorBlack ); + MacroDrawBitMap( d, arrow0_bm, x, y, wDrawColorBlack ); + MacroDrawBitMap( d, arrow3_bm, x, y, rightDragColor ); + Flash( d, x, y, rightDragColor ); + MacroDrawBitMap( d, arrow3_bm, x, y, rightDragColor ); + MacroDrawBitMap( d, arrow0_bm, x, y, wDrawColorBlack ); +} + +/***************************************************************************** + * + * PLAYBACK + * + */ + +EXPORT wBool_t inPlayback; +EXPORT wBool_t inPlaybackQuit; +EXPORT wWin_p demoW; +EXPORT int curDemo = -1; + +typedef struct { + char * title; + char * fileName; + } demoList_t; +static dynArr_t demoList_da; +#define demoList(N) DYNARR_N( demoList_t, demoList_da, N ) +static struct wFilSel_t * playbackFile_fs; + +typedef struct { + char * label; + playbackProc_p proc; + void * data; + } playbackProc_t; +static dynArr_t playbackProc_da; +#define playbackProc(N) DYNARR_N( playbackProc_t, playbackProc_da, N ) + +static coOrd oldRoomSize; +static coOrd oldMainOrig; +static coOrd oldMainSize; +static DIST_T oldMainScale; +static char * oldScaleName; + +static wBool_t pauseDemo = FALSE; +static long bigPause = 2000; +#ifdef LATER +static long MSEC_PER_PIXEL = 6; +#endif +#ifdef DEMOPAUSE +static wButton_p demoPause; +#endif +static BOOL_T playbackNonStop = FALSE; + +static BOOL_T showParamLineNum = FALSE; + +static int playbackKeyState; + +static void DoDemoButton( void * context ); +static paramTextData_t demoTextData = { 50, 16 }; +static paramData_t demoPLs[] = { +#define I_DEMOSTEP (0) +#define demoStep ((wButton_p)demoPLs[I_DEMOSTEP].control) + { PD_BUTTON, (void*)DoDemoButton, "step", PDO_NORECORD, NULL, N_("Step"), BB_DEFAULT, (void*)0 }, +#define I_DEMONEXT (1) +#define demoNext ((wButton_p)demoPLs[I_DEMONEXT].control) + { PD_BUTTON, (void*)DoDemoButton, "next", PDO_NORECORD|PDO_DLGHORZ, NULL, N_("Next"), 0, (void*)1 }, +#define I_DEMOQUIT (2) +#define demoQuit ((wButton_p)demoPLs[I_DEMOQUIT].control) + { PD_BUTTON, (void*)DoDemoButton, "quit", PDO_NORECORD|PDO_DLGHORZ, NULL, N_("Quit"), BB_CANCEL, (void*)3 }, +#define I_DEMOSPEED (3) +#define demoSpeedL ((wList_p)demoPLs[I_DEMOSPEED].control) + { PD_DROPLIST, &playbackSpeed, "speed", PDO_NORECORD|PDO_LISTINDEX|PDO_DLGHORZ, (void*)80, N_("Speed") }, +#define I_DEMOTEXT (4) +#define demoT ((wText_p)demoPLs[I_DEMOTEXT].control) + { PD_TEXT, NULL, "text", PDO_NORECORD|PDO_DLGRESIZE, &demoTextData, NULL, BT_CHARUNITS|BO_READONLY} }; +static paramGroup_t demoPG = { "demo", 0, demoPLs, sizeof demoPLs/sizeof demoPLs[0] }; + +EXPORT int MyGetKeyState( void ) +{ + if (inPlayback) + return playbackKeyState; + else + return wGetKeyState(); +} + + +EXPORT void AddPlaybackProc( char * label, playbackProc_p proc, void * data ) +{ + DYNARR_APPEND( playbackProc_t, playbackProc_da, 10 ); + playbackProc(playbackProc_da.cnt-1).label = MyStrdup(label); + playbackProc(playbackProc_da.cnt-1).proc = proc; + playbackProc(playbackProc_da.cnt-1).data = data; +} + + +static void PlaybackQuit( void ) +{ + long playbackSpeed1 = playbackSpeed; + if (paramFile) + fclose( paramFile ); + paramFile = NULL; + if (!inPlayback) + return; + inPlaybackQuit = TRUE; + ClearPlaybackCursor(); + wPrefReset(); + wHide( demoW ); + wWinSetBusy( mainW, FALSE ); + wWinSetBusy( mapW, FALSE ); + ParamRestoreAll(); + RestoreLayers(); + wEnableBalloonHelp( (int)enableBalloonHelp ); + mainD.scale = oldMainScale; + mainD.size = oldMainSize; + mainD.orig = oldMainOrig; + SetRoomSize( oldRoomSize ); + tempD.orig = mainD.orig; + tempD.size = mainD.size; + tempD.scale = mainD.scale; + ClearTracks(); + checkPtMark = changed = 0; + RestoreTrackState(); + inPlaybackQuit = FALSE; + Reset(); + DoSetScale( oldScaleName ); + DoChangeNotification( CHANGE_ALL ); + CloseDemoWindows(); + inPlayback = FALSE; + curDemo = -1; + wPrefSetInteger( "misc", "playbackspeed", playbackSpeed ); + playbackNonStop = FALSE; + playbackSpeed = playbackSpeed1; + UndoResume(); + wWinBlockEnable( TRUE ); +} + + +static int documentEnable = 0; +static int documentAutoSnapshot = 0; + +static drawCmd_t snapshot_d = { + NULL, + &screenDrawFuncs, + 0, + 16.0, + 0, + {0.0, 0.0}, {1.0, 1.0}, + Pix2CoOrd, CoOrd2Pix }; +static int documentSnapshotNum = 1; +static int documentCopy = 0; +static FILE * documentFile; +static BOOL_T snapshotMouse = FALSE; + +EXPORT void TakeSnapshot( drawCmd_t * d ) +{ + char * cp; + wPos_t ix, iy; + if (d->dpi < 0) + d->dpi = mainD.dpi; + if (d->scale < 0) + d->scale = mainD.scale; + if (d->orig.x < 0 || d->orig.y < 0) + d->orig = mainD.orig; + if (d->size.x < 0 || d->size.y < 0) + d->size = mainD.size; + ix = (wPos_t)(d->dpi*d->size.x/d->scale); + iy = (wPos_t)(d->dpi*d->size.y/d->scale); + d->d = wBitMapCreate( ix, iy, 8 ); + if (d->d == (wDraw_p)0) { + return; + } + DrawTracks( d, d->scale, d->orig, d->size ); + if ( snapshotMouse && playbackBm ) + wDrawBitMap( d->d, playbackBm, playbackX, playbackY, playbackColor, 0 ); + wDrawLine( d->d, 0, 0, ix-1, 0, 0, wDrawLineSolid, wDrawColorBlack, 0 ); + wDrawLine( d->d, ix-1, 0, ix-1, iy-1, 0, wDrawLineSolid, wDrawColorBlack, 0 ); + wDrawLine( d->d, ix-1, iy-1, 0, iy-1, 0, wDrawLineSolid, wDrawColorBlack, 0 ); + wDrawLine( d->d, 0, iy-1, 0, 0, 0, wDrawLineSolid, wDrawColorBlack, 0 ); + strcpy( message, paramFileName ); + cp = message+strlen(message)-4; + sprintf( cp, "-%4.4d.xpm", documentSnapshotNum ); + wBitMapWriteFile( d->d, message ); + wBitMapDelete( d->d ); + documentSnapshotNum++; + if (documentCopy && documentFile) { + cp = strrchr( message, FILE_SEP_CHAR[0] ); + if (cp == 0) + cp = message; + else + cp++; + cp[strlen(cp)-4] = 0; + fprintf( documentFile, "\n?G%s\n", cp ); + } +} + +static void EnableButtons( + BOOL_T enable ) +{ + wButtonSetBusy( demoStep, !enable ); + wButtonSetBusy( demoNext, !enable ); + wControlActive( (wControl_p)demoStep, enable ); + wControlActive( (wControl_p)demoNext, enable ); +#ifdef DEMOPAUSE + wButtonSetBusy( demoPause, enable ); +#endif +} + +EXPORT void PlaybackMessage( + char * line ) +{ + char * cp; + wTextAppend( demoT, _(line) ); + if ( documentCopy && documentFile ) { + if (strncmp(line, "__________", 10) != 0) { + for (cp=line; *cp; cp++) { + switch (*cp) { + case '<': + fprintf( documentFile, "$B" ); + break; + case '>': + fprintf( documentFile, "$" ); + break; + default: + fprintf( documentFile, "%c", *cp ); + } + } + } + } +} + + +static void PlaybackSetup( void ) +{ + SaveTrackState(); + EnableButtons( TRUE ); + SetPlaybackSpeed( (wIndex_t)playbackSpeed ); + wListSetIndex( demoSpeedL, (wIndex_t)playbackSpeed ); + wTextClear( demoT ); + wShow( demoW ); + wFlush(); + RulerRedraw( TRUE ); + wPrefFlush(); + wWinSetBusy( mainW, TRUE ); + wWinSetBusy( mapW, TRUE ); + ParamSaveAll(); + paramLineNum = 0; + oldRoomSize = mapD.size; + oldMainOrig = mainD.orig; + oldMainSize = mainD.size; + oldMainScale = mainD.scale; + oldScaleName = curScaleName; + Reset(); + paramVersion = -1; + playbackColor=wDrawColorBlack; + paramTogglePlaybackHilite = FALSE; + CompoundClearDemoDefns(); + SaveLayers(); +} + + +static void Playback( void ) +{ + POS_T x, y; + POS_T zoom; + wIndex_t inx; + long timeout; + static enum { pauseCmd, mouseCmd, otherCmd } thisCmd, lastCmd; + int len; + static wBool_t demoWinOnTop = FALSE; + coOrd roomSize; + char * cp, * cq; + + useCurrentLayer = FALSE; + inPlayback = TRUE; + EnableButtons( FALSE ); + lastCmd = otherCmd; + playbackTimer = 0; + if (demoWinOnTop) { + wWinTop( mainW ); + demoWinOnTop = FALSE; + } + while (TRUE) { + if ( paramFile == NULL || + fgets(paramLine, STR_LONG_SIZE, paramFile) == NULL ) { + paramTogglePlaybackHilite = FALSE; + ClearPlaybackCursor(); + CloseDemoWindows(); + if (paramFile) { + fclose( paramFile ); + paramFile = NULL; + } + if (documentFile) { + fclose( documentFile ); + documentFile = NULL; + } + Reset(); + if (curDemo < 0 || curDemo >= demoList_da.cnt) + break; + strcpy( paramFileName, demoList(curDemo).fileName ); + paramFile = fopen( paramFileName, "r" ); + if ( paramFile == NULL ) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Demo"), paramFileName, strerror(errno) ); + return; + } + + playbackColor=wDrawColorBlack; + paramLineNum = 0; + wWinSetTitle( demoW, demoList( curDemo ).title ); + curDemo++; + ClearTracks(); + UndoSuspend(); + wWinBlockEnable( FALSE ); + checkPtMark = 0; + RulerRedraw( TRUE ); + DoChangeNotification( CHANGE_ALL ); + CompoundClearDemoDefns(); + if ( fgets(paramLine, STR_LONG_SIZE, paramFile) == NULL ) { + NoticeMessage( MSG_CANT_READ_DEMO, _("Continue"), NULL, sProdName, paramFileName ); + fclose( paramFile ); + paramFile = NULL; + return; + } + } + if (paramLineNum == 0) { + documentSnapshotNum = 1; + if (documentEnable) { + strcpy( message, paramFileName ); + cp = message+strlen(message)-4; + strcpy( cp, ".hlpsrc" ); + documentFile = fopen( message, "w" ); + documentCopy = TRUE; + } + } + thisCmd = otherCmd; + paramLineNum++; + if (showParamLineNum) + InfoCount( paramLineNum ); + Stripcr( paramLine ); + if (paramLine[0] == '#') { + /* comment */ + } else if (paramLine[0] == 0) { + /* empty paramLine */ + } else if (ReadTrack( paramLine ) ) { + } else if (strncmp( paramLine, "STEP", 5 ) == 0) { + paramTogglePlaybackHilite = TRUE; + wWinTop( demoW ); + demoWinOnTop = TRUE; + didPause = FALSE; + EnableButtons( TRUE ); + if (!demoWinOnTop) { + wWinTop( demoW ); + demoWinOnTop = TRUE; + } + if ( documentAutoSnapshot ) { + snapshot_d.dpi=snapshot_d.scale=snapshot_d.orig.x=snapshot_d.orig.y=snapshot_d.size.x=snapshot_d.size.y=-1; + TakeSnapshot(&snapshot_d); + } + if (playbackNonStop) { + wPause( 1000 ); + EnableButtons( FALSE ); + } else { + return; + } + } else if (strncmp( paramLine, "CLEAR", 5 ) == 0) { + wTextClear( demoT ); + } else if (strncmp( paramLine, "MESSAGE", 7 ) == 0) { + didPause = FALSE; + wWinTop( demoW ); + demoWinOnTop = TRUE; + while ( ( fgets( paramLine, STR_LONG_SIZE, paramFile ) ) != NULL ) { + paramLineNum++; + if ( strncmp(paramLine, "END", 3) == 0 ) + break; + if ( strncmp(paramLine, "STEP", 3) == 0 ) { + wWinTop( demoW ); + demoWinOnTop = TRUE; + EnableButtons( TRUE ); + return; + } + PlaybackMessage( paramLine ); + } + } else if (strncmp( paramLine, "ROOMSIZE ", 9 ) == 0) { + if (ParseRoomSize( paramLine+9, &roomSize )) + SetRoomSize( roomSize ); + } else if (strncmp( paramLine, "SCALE ", 6 ) == 0) { + DoSetScale( paramLine+6 ); + } else if (strncmp( paramLine, "REDRAW", 6 ) == 0) { + ResolveIndex(); + RecomputeElevations(); + DoRedraw(); + /*DoChangeNotification( CHANGE_ALL );*/ + if (playbackD != NULL && playbackBm != NULL) + MacroDrawBitMap( playbackD, playbackBm, playbackX, playbackY, wDrawColorBlack ); + } else if (strncmp( paramLine, "COMMAND ", 8 ) == 0) { + paramTogglePlaybackHilite = FALSE; + PlaybackCommand( paramLine, paramLineNum ); + } else if (strncmp( paramLine, "RESET", 5 ) == 0) { + paramTogglePlaybackHilite = TRUE; + Reset(); + } else if (strncmp( paramLine, "VERSION", 7 ) == 0) { + paramVersion = atol( paramLine+8 ); + if ( paramVersion > iParamVersion ) { + NoticeMessage( MSG_PLAYBACK_VERSION_UPGRADE, _("Ok"), NULL, paramVersion, iParamVersion, sProdName ); + break; + } + if ( paramVersion < iMinParamVersion ) { + NoticeMessage( MSG_PLAYBACK_VERSION_DOWNGRADE, _("Ok"), NULL, paramVersion, iMinParamVersion, sProdName ); + break; + } + } else if (strncmp( paramLine, "ORIG ", 5 ) == 0) { + if ( !GetArgs( paramLine+5, "fff", &zoom, &x, &y ) ) + continue; + mainD.scale = zoom; + mainD.orig.x = x; + mainD.orig.y = y; + SetMainSize(); + tempD.orig = mainD.orig; + tempD.size = mainD.size; + tempD.scale = mainD.scale; +#ifdef LATER + ResolveIndex(); + RecomputeElevations(); +#endif + DoRedraw(); + if (playbackD != NULL && playbackBm != NULL) + MacroDrawBitMap( playbackD, playbackBm, playbackX, playbackY, wDrawColorBlack ); +#ifdef LATER + } else if (strncmp( paramLine, "POSITION ", 9 ) == 0) { + if ( !GetArgs( paramLine+9, "ff", &x, &y ) ) + continue; + MovePlaybackCursor( &mainD, x, y ); +#endif + } else if (strncmp( paramLine, "PAUSE ", 6 ) == 0) { + paramTogglePlaybackHilite = TRUE; + didPause = TRUE; +#ifdef DOPAUSE + if (lastCmd == mouseCmd) { + thisCmd = pauseCmd; + } else { + if ( !GetArgs( paramLine+6, "l", &timeout ) ) + continue; +#ifdef LATER + wFlush(); + wAlarm( timeout*playbackDelay/100, playback ); + return; +#else + if (playbackTimer == 0) + wPause( timeout*playbackDelay/100 ); +#endif + } +#endif + if ( !GetArgs( paramLine+6, "l", &timeout ) ) + continue; + if (timeout > 10000) + timeout = 1000; + if (playbackTimer == 0) + wPause( timeout*playbackDelay/100 ); + wFlush(); + if (demoWinOnTop) { + wWinTop( mainW ); + demoWinOnTop = FALSE; + } + } else if (strncmp( paramLine, "BIGPAUSE ", 6 ) == 0) { + paramTogglePlaybackHilite = TRUE; + didPause = FALSE; +#ifdef LATER + wFlush(); + wAlarm( bigPause*playbackDelay/100, playback ); + return; +#else + if (playbackTimer == 0) { + timeout = bigPause*playbackDelay/100; + if (timeout <= dragTimeout) + timeout = dragTimeout+1; + wPause( timeout ); + } +#endif + } else if (strncmp( paramLine, "KEYSTATE ", 9 ) == 0 ) { + playbackKeyState = atoi( paramLine+9 ); + } else if (strncmp( paramLine, "TIMESTART", 9 ) == 0 ) { + playbackTimer = wGetTimer(); + } else if (strncmp( paramLine, "TIMEEND", 7 ) == 0 ) { + if (playbackTimer == 0) { + NoticeMessage( MSG_PLAYBACK_TIMEEND, _("Ok"), NULL ); + } else { + playbackTimer = wGetTimer() - playbackTimer; + sprintf( message, _("Elapsed time %lu\n"), playbackTimer ); + wTextAppend( demoT, message ); + playbackTimer = 0; + } + } else if (strncmp( paramLine, "MEMSTATS", 8 ) == 0 ) { + wTextAppend( demoT, wMemStats() ); + wTextAppend( demoT, "\n" ); + } else if (strncmp( paramLine, "SNAPSHOT", 8 ) == 0 ) { + if ( !documentEnable ) + continue; + snapshot_d.dpi=snapshot_d.scale=snapshot_d.orig.x=snapshot_d.orig.y=snapshot_d.size.x=snapshot_d.size.y=-1; + cp = paramLine+8; + while (*cp && isspace(*cp)) cp++; + if (snapshot_d.dpi = strtod( cp, &cq ), cp == cq) + snapshot_d.dpi = -1; + else if (snapshot_d.scale = strtod( cq, &cp ), cp == cq) + snapshot_d.scale = -1; + else if (snapshot_d.orig.x = strtod( cp, &cq ), cp == cq) + snapshot_d.orig.x = -1; + else if (snapshot_d.orig.y = strtod( cq, &cp ), cp == cq) + snapshot_d.orig.y = -1; + else if (snapshot_d.size.x = strtod( cp, &cq ), cp == cq) + snapshot_d.size.x = -1; + else if (snapshot_d.size.y = strtod( cq, &cp ), cp == cq) + snapshot_d.size.y = -1; + TakeSnapshot(&snapshot_d); + } else if (strncmp( paramLine, "DOCUMENT ON", 11 ) == 0 ) { + documentCopy = documentEnable; + } else if (strncmp( paramLine, "DOCUMENT OFF", 12 ) == 0 ) { + documentCopy = FALSE; + } else if (strncmp( paramLine, "DOCUMENT COPY", 13 ) == 0 ) { + while ( ( fgets( paramLine, STR_LONG_SIZE, paramFile ) ) != NULL ) { + paramLineNum++; + if ( strncmp(paramLine, "END", 3) == 0 ) + break; + if ( documentCopy && documentFile ) + fprintf( documentFile, "%s", paramLine ); + } + } else if ( strncmp( paramLine, "DEMOINIT", 8 ) == 0 ) { + DemoInitValues(); + } else { + if (strncmp( paramLine, "MOUSE ", 6 ) == 0) { +#ifdef LATER + if ( GetArgs( paramLine+6, "dff", &rc, &pos.x, &pos.y) ) { + pos.x = pos.x / mainD.scale - mainD.orig.x; + pos.y = pos.y / mainD.scale - mainD.orig.y; +#ifdef DOPAUSE + if (lastCmd == pauseCmd) { +#endif + d = sqrt( (pos.x-mainPos.x)*(pos.x-mainPos.x) + + (pos.y-mainPos.y)*(pos.y-mainPos.y) ); + d *= mainD.dpi; + timeout = (long)(MSEC_PER_PIXEL * d); + if (timeout > 2) + if (playbackTimer == 0) + wPause( timeout ); +#ifdef DOPAUSE + } +#endif + mainPos = pos; + } +#endif + thisCmd = mouseCmd; + } + if (strncmp( paramLine, "MAP ", 6 ) == 0) { +#ifdef LATER + if ( GetArgs( paramLine+6, "dff", &rc, &pos.x, &pos.y ) ) { + pos.x = pos.x / mapD.scale - mapD.orig.x; + pos.y = pos.y / mapD.scale - mapD.orig.y; +#ifdef DOPAUSE + if (lastCmd == pauseCmd) { +#endif + d = sqrt( (pos.x-mapPos.y)*(pos.x-mapPos.x) + + (pos.y-mapPos.y)*(pos.y-mapPos.y) ); + d *= mapD.dpi; + timeout = (long)(MSEC_PER_PIXEL * d); + if (timeout > 2) + if (playbackTimer == 0) + wPause( timeout ); +#ifdef DOPAUSE + } +#endif + mapPos = pos; + } +#endif + thisCmd = mouseCmd; + } + for ( inx=0; inx<playbackProc_da.cnt; inx++ ) { + len = strlen(playbackProc(inx).label); + if (strncmp( paramLine, playbackProc(inx).label, len ) == 0) { + if (playbackProc(inx).data == NULL) { + while (paramLine[len] == ' ') len++; + playbackProc(inx).proc( paramLine+len ); + } else + playbackProc(inx).proc( (char*)playbackProc(inx).data ); + break; + } + } + if ( thisCmd == mouseCmd ) { + EnableButtons( FALSE ); + playbackKeyState = 0; + } + if (inx == playbackProc_da.cnt) { + NoticeMessage( MSG_PLAYBACK_UNK_CMD, _("Ok"), NULL, paramLineNum, paramLine ); + } + } + lastCmd = thisCmd; + wFlush(); + if (pauseDemo) { + EnableButtons( TRUE ); + pauseDemo = FALSE; + return; + } + } + if (paramFile) { + fclose( paramFile ); + paramFile = NULL; + } + if (documentFile) { + fclose( documentFile ); + documentFile = NULL; + } + PlaybackQuit(); +} + + +static int StartPlayback( const char * pathName, const char * fileName, void * context ) +{ + if (pathName == NULL) + return TRUE; + + SetCurDir( pathName, fileName ); + paramFile = fopen( pathName, "r" ); + if ( paramFile == NULL ) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Playback"), pathName, strerror(errno) ); + return FALSE; + } + + strcpy( paramFileName, pathName ); + + PlaybackSetup(); + curDemo = -1; + UndoSuspend(); + wWinBlockEnable( FALSE ); + Playback(); + + return TRUE; +} + + +static void DoDemoButton( void * command ) +{ + switch( (int)(long)command ) { + case 0: + /* step */ + playbackNonStop = (wGetKeyState() & WKEY_SHIFT) != 0; + paramHiliteFast = (wGetKeyState() & WKEY_CTRL) != 0; + Playback(); + break; + case 1: + if (curDemo == -1) { + DoSaveAs( NULL ); + } else { + /* next */ + if (paramFile) + fclose(paramFile); + paramFile = NULL; + wTextClear( demoT ); + if ( (wGetKeyState()&WKEY_SHIFT)!=0 ) { + if ( curDemo >= 2 ) + curDemo -= 2; + else + curDemo = 0; + } + Playback(); + } + break; + case 2: + /* pause */ + pauseDemo = TRUE; + break; + case 3: + /* quit */ + PlaybackQuit(); + break; + default: + ; + } +} + + + + +static void DemoDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + if ( inx != I_DEMOSPEED ) return; + SetPlaybackSpeed( (wIndex_t)*(long*)valueP ); +} + + +static void CreateDemoW( void ) +{ + char * title = MakeWindowTitle(_("Demo")); + demoW = ParamCreateDialog( &demoPG, title, NULL, NULL, NULL, FALSE, NULL, F_RESIZE, DemoDlgUpdate ); + + wListAddValue( demoSpeedL, _("Slowest"), NULL, (void*)0 ); + wListAddValue( demoSpeedL, _("Slow"), NULL, (void*)1 ); + wListAddValue( demoSpeedL, _("Normal"), NULL, (void*)2 ); + wListAddValue( demoSpeedL, _("Fast"), NULL, (void*)3 ); + wListAddValue( demoSpeedL, _("Faster"), NULL, (void*)4 ); + wListAddValue( demoSpeedL, _("Fastest"), NULL, (void*)5 ); + wListSetIndex( demoSpeedL, (wIndex_t)playbackSpeed ); + playbackFile_fs = wFilSelCreate( mainW, FS_LOAD, 0, title, sRecordFilePattern, StartPlayback, NULL ); +} + + +EXPORT void DoPlayBack( void * context ) +{ + if (demoW == NULL) + CreateDemoW(); + wButtonSetLabel( demoNext, _("Save") ); + wFilSelect( playbackFile_fs, curDirName ); +} + + + +/***************************************************************************** + * + * DEMO + * + */ + +static char * demoInitParams[] = { + "layout title1 XTrackCAD", + "layout title2 Demo", + "GROUP layout", + "display tunnels 1", + "display endpt 2", + "display labelenable 7", + "display description-fontsize 48", + "display labelscale 8", + "display layoutlabels 6", + "display color-layers 0", + "display tworailscale 16", + "display tiedraw 0", + "pref mingridspacing 5", + "pref balloonhelp 1", + "display hotbarlabels 1", + "display mapscale 64", + "display livemap 0", + "display carhotbarlabels 1", + "display hideTrainsInTunnels 0", + "GROUP display", + "cmdopt move-quick 0", + "pref turntable-angle 7.500", + "cmdopt preselect 1", + "pref coupling-speed-max 100", + "cmdopt rightclickmode 0", + "GROUP cmdopt", + "pref checkpoint 0", + "pref units 0", + "pref dstfmt 1", + "pref anglesystem 0", + "pref minlength 0.100", + "pref connectdistance 0.100", + "pref connectangle 1.000", + "pref dragpixels 20", + "pref dragtimeout 500", + "display autoPan 0", + "display listlabels 7", + "layout mintrackradius 1.000", + "layout maxtrackgrade 5.000", + "display trainpause 300", + "GROUP pref", + "rgbcolor snapgrid 65280", + "rgbcolor marker 16711680", + "rgbcolor border 0", + "rgbcolor crossmajor 16711680", + "rgbcolor crossminor 255", + "rgbcolor normal 0", + "rgbcolor selected 16711680", + "rgbcolor profile 16711935", + "rgbcolor exception 16711808", + "rgbcolor tie 16744448", + "GROUP rgbcolor", + "easement val 0.000", + "easement r 0.000", + "easement x 0.000", + "easement l 0.000", + "GROUP easement", + "grid horzspacing 12.000", + "grid horzdivision 12", + "grid horzenable 0", + "grid vertspacing 12.000", + "grid vertdivision 12", + "grid vertenable 0", + "grid origx 0.000", + "grid origy 0.000", + "grid origa 0.000", + "grid show 0", + "GROUP grid", + "misc toolbarset 65535", + "GROUP misc", + "sticky set 268435383", /* 0xfffffb7 - all but Helix and Turntable */ + "GROUP sticky", + "turnout hide 0", + "layer button-count 10", + NULL }; + +static void DemoInitValues( void ) +{ + int inx; + char **cpp; + static playbackProc_p paramPlaybackProc = NULL; + static coOrd roomSize = { 96.0, 48.0 }; + char scaleName[10]; + if ( paramPlaybackProc == NULL ) { + for ( inx=0; inx<playbackProc_da.cnt; inx++ ) { + if (strncmp( "PARAMETER", playbackProc(inx).label, 9 ) == 0 ) { + paramPlaybackProc = playbackProc(inx).proc; + break; + } + } + } + SetRoomSize( roomSize ); + strcpy( scaleName, "DEMO" ); + DoSetScale( scaleName ); + if ( paramPlaybackProc == NULL ) { + wNoticeEx( NT_INFORMATION, _("Can not find PARAMETER playback proc"), _("Ok"), NULL ); + return; + } + for ( cpp = demoInitParams; *cpp; cpp++ ) + paramPlaybackProc( *cpp ); +} + + +static void DoDemo( void * demoNumber ) +{ + + if (demoW == NULL) + CreateDemoW(); + wButtonSetLabel( demoNext, _("Next") ); + curDemo = (int)(long)demoNumber; + if ( curDemo < 0 || curDemo >= demoList_da.cnt ) { + NoticeMessage( MSG_DEMO_BAD_NUM, _("Ok"), NULL, curDemo ); + return; + } + PlaybackSetup(); + playbackNonStop = (wGetKeyState() & WKEY_SHIFT) != 0; + paramFile = NULL; + Playback(); +} + + +static BOOL_T ReadDemo( + char * line ) +{ + static wMenu_p m; + char * cp; + char *oldLocale = NULL; + + if ( m == NULL ) + m = demoM; + + if ( strncmp( line, "DEMOGROUP ", 10 ) == 0 ) { + if (userLocale) + oldLocale = SaveLocale(userLocale); + m = wMenuMenuCreate( demoM, NULL, _(line+10) ); + if (oldLocale) + RestoreLocale(oldLocale); + } else if ( strncmp( line, "DEMO ", 5 ) == 0 ) { + if (line[5] != '"') + goto error; + cp = line+6; + while (*cp && *cp != '"') cp++; + if ( !*cp ) + goto error; + *cp++ = '\0'; + while (*cp && *cp == ' ') cp++; + if ( strlen(cp)==0 ) + goto error; + DYNARR_APPEND( demoList_t, demoList_da, 10 ); + if (userLocale) + oldLocale = SaveLocale(userLocale); + demoList( demoList_da.cnt-1 ).title = MyStrdup( _(line+6) ); + demoList( demoList_da.cnt-1 ).fileName = + (char*)MyMalloc( strlen(libDir) + 1 + 5 + 1 + strlen(cp) + 1 ); + sprintf( demoList( demoList_da.cnt-1 ).fileName, "%s%s%s%s%s", + libDir, FILE_SEP_CHAR, "demos", FILE_SEP_CHAR, cp ); + wMenuPushCreate( m, NULL, _(line+6), 0, DoDemo, (void*)(intptr_t)(demoList_da.cnt-1) ); + if (oldLocale) + RestoreLocale(oldLocale); + } + return TRUE; +error: + InputError( "Expected 'DEMO \"<Demo Name>\" <File Name>'", TRUE ); + return FALSE; +} + + + +EXPORT BOOL_T MacroInit( void ) +{ + AddParam( "DEMOGROUP ", ReadDemo ); + AddParam( "DEMO ", ReadDemo ); + + recordMouseMoves = ( getenv( "XTRKCADNORECORDMOUSEMOVES" ) == NULL ); + + rightDragColor = drawColorRed; + leftDragColor = drawColorBlue; + + arrow0_bm = wDrawBitMapCreate( mainD.d, arrow0_width, arrow0_height, 12, 12, arrow0_bits ); + arrow3_bm = wDrawBitMapCreate( mainD.d, arrow3_width, arrow3_height, 12, 12, arrow3_bits ); + arrows_bm = wDrawBitMapCreate( mainD.d, arrows_width, arrows_height, 12, 12, arrows_bits ); + flash_bm = wDrawBitMapCreate( mainD.d, flash_width, flash_height, 12, 12, flash_bits ); + + ParamRegister( &recordPG ); + ParamRegister( &demoPG ); + return TRUE; +} diff --git a/app/bin/misc.c b/app/bin/misc.c new file mode 100644 index 0000000..609a210 --- /dev/null +++ b/app/bin/misc.c @@ -0,0 +1,2674 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/misc.c,v 1.49 2010-04-28 04:04:39 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 <stdlib.h> +#include <stdio.h> +#ifndef WINDOWS +#include <unistd.h> +#include <dirent.h> +#endif +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif +#include <math.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#ifdef WINDOWS +#include <io.h> +#include <windows.h> +#include "getopt.h" +#define R_OK (02) +#define access _access +#if _MSC_VER >1300 + #define strdup _strdup +#endif +#else +#include <sys/stat.h> +#endif +#include <stdarg.h> + +#include <stdint.h> + +#include "track.h" +#include "common.h" +#include "utility.h" +#include "draw.h" +#include "misc.h" +#include "cjoin.h" +#include "compound.h" +#include "smalldlg.h" +#include "i18n.h" +#include <locale.h> + +char *userLocale = NULL; + +extern wBalloonHelp_t balloonHelp[]; +#ifdef DEBUG +#define CHECK_BALLOONHELP +/*#define CHECK_UNUSED_BALLOONHELP*/ +#endif +#ifdef CHECK_UNUSED_BALLOONHELP +static void ShowUnusedBalloonHelp(void); +#endif +void DoCarDlg(void); + +/**************************************************************************** + * + EXPORTED VARIABLES + * + */ + +EXPORT int foobar = 0; + +EXPORT int log_error; +static int log_command; + +EXPORT wWin_p mainW; + +EXPORT wIndex_t changed = 0; + +EXPORT char FAR message[STR_LONG_SIZE]; +static char message2[STR_LONG_SIZE]; + +EXPORT REGION_T curRegion = 0; + +EXPORT long paramVersion = -1; + +EXPORT coOrd zero = { 0.0, 0.0 }; + +EXPORT wBool_t extraButtons = FALSE; + +EXPORT long onStartup; /**< controls behaviour after startup: load last layout if zero, else start with blank canvas */ + +EXPORT wButton_p undoB; +EXPORT wButton_p redoB; + +EXPORT wButton_p zoomUpB; +EXPORT wButton_p zoomDownB; + +EXPORT wIndex_t checkPtMark = 0; + +EXPORT wMenu_p demoM; +EXPORT wMenu_p popup1M, popup2M; +EXPORT wMenu_p popup1aM, popup2aM; + + +static wIndex_t curCommand = 0; +EXPORT void * commandContext; +EXPORT wIndex_t cmdGroup; +EXPORT wIndex_t joinCmdInx; +EXPORT wIndex_t modifyCmdInx; +EXPORT long rightClickMode = 0; +EXPORT DIST_T easementVal = 0.0; +EXPORT DIST_T easeR = 0.0; +EXPORT DIST_T easeL = 0.0; +EXPORT coOrd cmdMenuPos; + +EXPORT wPos_t DlgSepLeft = 12; +EXPORT wPos_t DlgSepMid = 18; +EXPORT wPos_t DlgSepRight = 12; +EXPORT wPos_t DlgSepTop = 12; +EXPORT wPos_t DlgSepBottom = 12; +EXPORT wPos_t DlgSepNarrow = 6; +EXPORT wPos_t DlgSepWide = 12; +EXPORT wPos_t DlgSepFrmLeft = 4; +EXPORT wPos_t DlgSepFrmRight = 4; +EXPORT wPos_t DlgSepFrmTop = 4; +EXPORT wPos_t DlgSepFrmBottom = 4; + +static int verbose = 0; + +static wMenuList_p winList_mi; +static BOOL_T inMainW = TRUE; + +static long stickySet; +static long stickyCnt = 0; +static char * stickyLabels[33]; +#define TOOLBARSET_INIT (0xFFFF) +EXPORT long toolbarSet = TOOLBARSET_INIT; +EXPORT wPos_t toolbarHeight = 0; +static wPos_t toolbarWidth = 0; + +static wMenuList_p messageList_ml; +static BOOL_T messageListEmpty = TRUE; +#define MESSAGE_LIST_EMPTY N_("No Messages") + +#define NUM_FILELIST (5) + +extern long curTurnoutEp; +static wIndex_t printCmdInx; +static wIndex_t gridCmdInx; +static paramData_t menuPLs[101] = { + { PD_LONG, &toolbarSet, "toolbarset" }, + { PD_LONG, &curTurnoutEp, "cur-turnout-ep" } }; +static paramGroup_t menuPG = { "misc", PGO_RECORD, menuPLs, 2 }; + +/**************************************************************************** + * + * LOCAL UTILITIES + * + */ + +EXPORT long totalMallocs = 0; +EXPORT long totalMalloced = 0; +EXPORT long totalRealloced = 0; +EXPORT long totalReallocs = 0; +EXPORT long totalFreeed = 0; +EXPORT long totalFrees = 0; + +static unsigned long guard0 = 0xDEADBEEF; +static unsigned long guard1 = 0xAF00BA8A; +static int log_malloc; + +EXPORT void * MyMalloc ( long size ) +{ + void * p; + totalMallocs++; + totalMalloced += size; +#if defined(WINDOWS) && ! defined(WIN32) + if ( size > 65500L ) { + AbortProg( "mallocing > 65500 bytes" ); + } +#endif + p = malloc( (size_t)size + sizeof (size_t) + 2 * sizeof (unsigned long) ); + if (p == NULL) + AbortProg( "No memory" ); + +LOG1( log_malloc, ( "Malloc(%ld) = %lx (%lx-%lx)\n", size, + (long)((char*)p+sizeof (size_t) + sizeof (unsigned long)), + (long)p, + (long)((char*)p+size+sizeof (size_t) + 2 * sizeof(unsigned long)) )); + *(size_t*)p = (size_t)size; + p = (char*)p + sizeof (size_t); + *(unsigned long*)p = guard0; + p = (char*)p + sizeof (unsigned long); + *(unsigned long*)((char*)p+size) = guard1; + memset( p, 0, (size_t)size ); + return p; +} + +EXPORT void * MyRealloc( void * old, long size ) +{ + size_t oldSize; + void * new; + if (old==NULL) + return MyMalloc( size ); + totalReallocs++; + totalRealloced += size; +#if defined(WINDOWS) && ! defined(WIN32) + if ( size > 65500L ) { + AbortProg( "reallocing > 65500 bytes" ); + } +#endif + if ( *(unsigned long*)((char*)old - sizeof (unsigned long)) != guard0 ) { + AbortProg( "Guard0 is hosed" ); + } + oldSize = *(size_t*)((char*)old - sizeof (unsigned long) - sizeof (size_t)); + if ( *(unsigned long*)((char*)old + oldSize) != guard1 ) { + AbortProg( "Guard1 is hosed" ); + } +LOG1( log_malloc, ("Realloc(%lx,%ld) was %d\n", (long)old, size, oldSize ) ) + if ((long)oldSize == size) { + return old; + } + if (size == 0) { + free( (char*)old - sizeof *(long*)0 - sizeof *(size_t*)0 ); + return NULL; + } + new = MyMalloc( size ); + if (new == NULL && size) + AbortProg( "No memory" ); + memcpy( new, old, min((size_t)size, oldSize) ); + MyFree(old); + return new; +} + + +EXPORT void MyFree( void * ptr ) +{ + size_t oldSize; + totalFrees++; + if (ptr) { + if ( *(unsigned long*)((char*)ptr - sizeof (unsigned long)) != guard0 ) { + AbortProg( "Guard0 is hosed" ); + } + oldSize = *(size_t*)((char*)ptr - sizeof (unsigned long) - sizeof (size_t)); + if ( *(unsigned long*)((char*)ptr + oldSize) != guard1 ) { + AbortProg( "Guard1 is hosed" ); + } +LOG1( log_malloc, ("Free %d at %lx (%lx-%lx)\n", oldSize, (long)ptr, + (long)((char*)ptr-sizeof *(size_t*)0-sizeof *(long*)0), + (long)((char*)ptr+oldSize+sizeof *(long*)0)) ) + totalFreeed += oldSize; + free( (char*)ptr - sizeof *(long*)0 - sizeof *(size_t*)0 ); + } +} + + +EXPORT void * memdup( void * src, size_t size ) +{ + void * p; + p = MyMalloc( size ); + if (p == NULL) + AbortProg( "No memory" ); + memcpy( p, src, size ); + return p; +} + + +EXPORT char * MyStrdup( const char * str ) +{ + char * ret; + ret = (char*)MyMalloc( strlen( str ) + 1 ); + strcpy( ret, str ); + return ret; +} + + +EXPORT void AbortProg( + char * msg, + ... ) +{ + static BOOL_T abort2 = FALSE; + int rc; + va_list ap; + va_start( ap, msg ); + vsprintf( message, msg, ap ); + va_end( ap ); + if (abort2) { + wNoticeEx( NT_ERROR, message, _("ABORT"), NULL ); + } else { + strcat( message, _("\nDo you want to save your layout?") ); + rc = wNoticeEx( NT_ERROR, message, _("Ok"), _("ABORT") ); + if (rc) { + DoSaveAs( (doSaveCallBack_p)abort ); + } else { + abort(); + } + } +} + + +EXPORT char * Strcpytrimed( char * dst, char * src, BOOL_T double_quotes ) +{ + char * cp; + while (*src && isspace(*src) ) src++; + if (!*src) + return dst; + cp = src+strlen(src)-1; + while ( cp>src && isspace(*cp) ) cp--; + while ( src<=cp ) { + if (*src == '"' && double_quotes) + *dst++ = '"'; + *dst++ = *src++; + } + *dst = '\0'; + return dst; +} + + +EXPORT char * BuildTrimedTitle( char * cp, char * sep, char * mfg, char * desc, char * partno ) +{ + cp = Strcpytrimed( cp, mfg, FALSE ); + strcpy( cp, sep ); + cp += strlen(cp); + cp = Strcpytrimed( cp, desc, FALSE ); + strcpy( cp, sep ); + cp += strlen(cp); + cp = Strcpytrimed( cp, partno, FALSE ); + return cp; +} + + +static void ShowMessageHelp( int index, const char * label, void * data ) +{ + char msgKey[STR_SIZE], *cp, *msgSrc; + msgSrc = (char*)data; + if (!msgSrc) + return; + cp = strchr( msgSrc, '\t' ); + if (cp==NULL) { + sprintf( msgKey, _("No help for %s"), msgSrc ); + wNoticeEx( NT_INFORMATION, msgKey, _("Ok"), NULL ); + return; + } + memcpy( msgKey, msgSrc, cp-msgSrc ); + msgKey[cp-msgSrc] = 0; + wHelp( msgKey ); +} + + +static char * ParseMessage( + char *msgSrc ) +{ + char *cp1=NULL, *cp2=NULL; + static char shortMsg[STR_SIZE]; + cp1 = strchr( _(msgSrc), '\t' ); + if (cp1) { + cp2 = strchr( cp1+1, '\t' ); + if (cp2) { + cp1++; + memcpy( shortMsg, cp1, cp2-cp1 ); + shortMsg[cp2-cp1] = 0; + cp1 = shortMsg; + cp2++; + } else { + cp1++; + cp2 = cp1; + } + if (messageListEmpty) { + wMenuListDelete( messageList_ml, _(MESSAGE_LIST_EMPTY) ); + messageListEmpty = FALSE; + } + wMenuListAdd( messageList_ml, 0, cp1, _(msgSrc) ); + return cp2; + } else { + return _(msgSrc); + } +} + + +EXPORT void InfoMessage( char * format, ... ) +{ + va_list ap; + va_start( ap, format ); + format = ParseMessage( format ); + vsprintf( message2, format, ap ); + va_end( ap ); + /*InfoSubstituteControl( NULL, NULL );*/ + if (inError) + return; + SetMessage( message2 ); +} + + +EXPORT void ErrorMessage( char * format, ... ) +{ + va_list ap; + va_start( ap, format ); + format = ParseMessage( format ); + vsprintf( message2, format, ap ); + va_end( ap ); + InfoSubstituteControls( NULL, NULL ); + SetMessage( message2 ); + wBeep(); + inError = TRUE; +} + + +EXPORT int NoticeMessage( char * format, char * yes, char * no, ... ) +{ + va_list ap; + va_start( ap, no ); + format = ParseMessage( format ); + vsprintf( message2, format, ap ); + va_end( ap ); + return wNotice( message2, yes, no ); +} + + +EXPORT int NoticeMessage2( int playbackRC, char * format, char * yes, char * no, ... ) +{ + va_list ap; + if ( inPlayback ) + return playbackRC; + va_start( ap, no ); + format = ParseMessage( format ); + vsprintf( message2, format, ap ); + va_end( ap ); + return wNoticeEx( NT_INFORMATION, message2, yes, no ); +} + +/***************************************************************************** + * + * MAIN BUTTON HANDLERS + * + */ + + +EXPORT void Confirm( char * label2, doSaveCallBack_p after ) +{ + int rc; + if (changed) { + rc = wNotice3( + _("Save changes to the layout design before closing?\n\n" + "If you don't save now, your unsaved changes will be discarded."), + _("&Save"), _("&Cancel"), _("&Don't Save") ); + if (rc == 1) { + DoSave( after ); + return; + } else if (rc == 0) { + return; + } + } + after(); + return; +} + +static void ChkLoad( void ) +{ + Confirm(_("Load"), DoLoad); +} + +static void ChkRevert( void ) +{ + int rc; + + if( changed) { + rc = wNoticeEx( NT_WARNING, _("Do you want to return to the last saved state?\n\n" + "Revert will cause all changes done since last save to be lost."), + _("&Revert"), _("&Cancel") ); + if( rc ) { + /* load the file */ + LoadTracks( curPathName, curFileName, NULL ); + } + } +} + + +static char * fileListPathName; +static void AfterFileList( void ) +{ + DoFileList( 0, NULL, fileListPathName ); +} + +static void ChkFileList( int index, const char * label, void * data ) +{ + fileListPathName = (char*)data; + Confirm( _("Load"), AfterFileList ); +} + +/** + * Save information about current files and some settings to preferences file. + */ + +EXPORT void SaveState( void ) +{ + wPos_t width, height; + const char * fileName; + void * pathName; + char file[6]; + int inx; + + wWinGetSize( mainW, &width, &height ); + wPrefSetInteger( "draw", "mainwidth", width ); + wPrefSetInteger( "draw", "mainheight", height ); + RememberParamFiles(); + ParamUpdatePrefs(); + + wPrefSetString( "misc", "lastlayout", curPathName ); + + if ( fileList_ml ) { + strcpy( file, "file" ); + file[5] = 0; + for ( inx=0; inx<NUM_FILELIST; inx++ ) { + fileName = wMenuListGet( fileList_ml, inx, &pathName ); + if (fileName) { + file[4] = '0'+inx; + sprintf( message, "%s", (char*)pathName ); + wPrefSetString( "filelist", file, message ); + } + } + } + wPrefFlush(); + LogClose(); +} + +/* + * Clean up befor quitting + */ +static int quitting; +static void DoQuitAfter( void ) +{ + changed = 0; + SaveState(); + + CleanupFiles(); + + quitting = TRUE; +} +/** + * Process shutdown request. This function is called when the user requests + * to close the application. Before shutting down confirmation is gotten to + * prevent data loss. + */ +void DoQuit( void ) +{ + Confirm(_("Quit"), DoQuitAfter ); + if ( quitting ) { +#ifdef CHECK_UNUSED_BALLOONHELP + ShowUnusedBalloonHelp(); +#endif + LogClose(); + wExit(0); + } +} + +static void DoClearAfter( void ) +{ + ClearTracks(); + + /* set all layers to their default properties and set current layer to 0 */ + DefaultLayerProperties(); + + checkPtMark = 0; + Reset(); + DoChangeNotification( CHANGE_MAIN|CHANGE_MAP ); + EnableCommands(); + curPathName[0] = '\0'; + curFileName = curPathName; + SetWindowTitle(); +} + +static void DoClear( void ) +{ + Confirm(_("Clear"), DoClearAfter); +} + + +static void DoShowWindow( + int index, + const char * name, + void * data ) +{ + if (data == mapW) { + if (mapVisible == FALSE) { + mapVisible = TRUE; + DoChangeNotification( CHANGE_MAP ); + } + mapVisible = TRUE; + } + wWinShow( (wWin_p)data, TRUE ); +} + + +static dynArr_t demoWindows_da; +#define demoWindows(N) DYNARR_N( wWin_p, demoWindows_da, N ) + +EXPORT void wShow( + wWin_p win ) +{ + int inx; + if (inPlayback && win != demoW) { + wWinSetBusy( win, TRUE ); + for ( inx=0; inx<demoWindows_da.cnt; inx++ ) + if ( demoWindows(inx) == win ) + break; + if ( inx >= demoWindows_da.cnt ) { + for ( inx=0; inx<demoWindows_da.cnt; inx++ ) + if ( demoWindows(inx) == NULL ) + break; + if ( inx >= demoWindows_da.cnt ) { + DYNARR_APPEND( wWin_p, demoWindows_da, 10 ); + inx = demoWindows_da.cnt-1; + } + demoWindows(inx) = win; + } + } + if (win != mainW) + wMenuListAdd( winList_mi, -1, wWinGetTitle(win), win ); + wWinShow( win, TRUE ); +} + + +EXPORT void wHide( + wWin_p win ) +{ + int inx; + wWinShow( win, FALSE ); + wWinSetBusy( win, FALSE ); + if ( inMainW && win == aboutW ) + return; + wMenuListDelete( winList_mi, wWinGetTitle(win) ); + if ( inPlayback ) + for ( inx=0; inx<demoWindows_da.cnt; inx++ ) + if ( demoWindows(inx) == win ) + demoWindows(inx) = NULL; +} + + +EXPORT void CloseDemoWindows( void ) +{ + int inx; + for ( inx=0; inx<demoWindows_da.cnt; inx++ ) + if ( demoWindows(inx) != NULL ) + wHide( demoWindows(inx) ); + demoWindows_da.cnt = 0; +} + + +EXPORT void DefaultProc( + wWin_p win, + winProcEvent e, + void * data ) +{ + switch( e ) { + case wClose_e: + wMenuListDelete( winList_mi, wWinGetTitle(win) ); + if (data != NULL) + ConfirmReset( FALSE ); + wWinDoCancel( win ); + break; + default: + break; + } +} + + +static void NextWindow( void ) +{ +} + +EXPORT void SelectFont( void ) +{ + wSelectFont(_("XTrackCAD Font")); +} + +/***************************************************************************** + * + * COMMAND + * + */ + +#define COMMAND_MAX (100) +#define BUTTON_MAX (100) +#define NUM_CMDMENUS (4) + +#ifdef LATER +static struct { + addButtonCallBack_t actionProc; + procCommand_t cmdProc; + char * helpStr; + wControl_p control; + char * labelStr; + int reqLevel; + wBool_t enabled; + wPos_t x, y; + long options; + long stickyMask; + int group; + long acclKey; + wMenuPush_p menu[NUM_CMDMENUS]; + void * context; + } commandList[COMMAND_MAX]; +#endif + +static struct { + wControl_p control; + wBool_t enabled; + wPos_t x, y; + long options; + int group; + wIndex_t cmdInx; + } buttonList[BUTTON_MAX]; +static int buttonCnt = 0; + +static struct { + procCommand_t cmdProc; + char * helpKey; + wIndex_t buttInx; + char * labelStr; + wIcon_p icon; + int reqLevel; + wBool_t enabled; + long options; + long stickyMask; + long acclKey; + wMenuPush_p menu[NUM_CMDMENUS]; + void * context; + } commandList[COMMAND_MAX]; +static int commandCnt = 0; + + +#ifdef CHECK_UNUSED_BALLOONHELP +int * balloonHelpCnts; +#endif + +EXPORT const char * GetBalloonHelpStr( char * helpKey ) +{ + wBalloonHelp_t * bh; +#ifdef CHECK_UNUSED_BALLOONHELP + if ( balloonHelpCnts == NULL ) { + for ( bh=balloonHelp; bh->name; bh++ ); + balloonHelpCnts = (int*)malloc( (sizeof *(int*)0) * (bh-balloonHelp) ); + memset( balloonHelpCnts, 0, (sizeof *(int*)0) * (bh-balloonHelp) ); + } +#endif + for ( bh=balloonHelp; bh->name; bh++ ) { + if ( strcmp( bh->name, helpKey ) == 0 ) { +#ifdef CHECK_UNUSED_BALLOONHELP + balloonHelpCnts[(bh-balloonHelp)]++; +#endif + return _(bh->value); + } + } +#ifdef CHECK_BALLOONHELP +fprintf( stderr, _("No balloon help for %s\n"), helpKey ); +#endif + return _("No Help"); +} + + +#ifdef CHECK_UNUSED_BALLOONHELP +static void ShowUnusedBalloonHelp( void ) +{ + int cnt; + for ( cnt=0; balloonHelp[cnt].name; cnt++ ) + if ( balloonHelpCnts[cnt] == 0 ) + fprintf( stderr, "unused BH %s\n", balloonHelp[cnt].name ); +} +#endif + + +EXPORT void EnableCommands( void ) +{ + int inx, minx; + wBool_t enable; + +LOG( log_command, 5, ( "COMMAND enable S%d M%d\n", selectedTrackCount, programMode ) ) + for ( inx=0; inx<commandCnt; inx++ ) { + if (commandList[inx].buttInx) { + if ( (commandList[inx].options & IC_SELECTED) && + selectedTrackCount <= 0 ) + enable = FALSE; + else if ( (programMode==MODE_TRAIN&&(commandList[inx].options&(IC_MODETRAIN_TOO|IC_MODETRAIN_ONLY))==0) || + (programMode!=MODE_TRAIN&&(commandList[inx].options&IC_MODETRAIN_ONLY)!=0) ) + enable = FALSE; + else + enable = TRUE; + if ( commandList[inx].enabled != enable ) { + if ( commandList[inx].buttInx >= 0 ) + wControlActive( buttonList[commandList[inx].buttInx].control, enable ); + for ( minx=0; minx<NUM_CMDMENUS; minx++ ) + if (commandList[inx].menu[minx]) + wMenuPushEnable( commandList[inx].menu[minx], enable ); + commandList[inx].enabled = enable; + } + } + } + + for ( inx=0; inx<menuPG.paramCnt; inx++ ) { + if ( menuPLs[inx].control == NULL ) + continue; + if ( (menuPLs[inx].option & IC_SELECTED) && + selectedTrackCount <= 0 ) + enable = FALSE; + else if ( (programMode==MODE_TRAIN&&(menuPLs[inx].option&(IC_MODETRAIN_TOO|IC_MODETRAIN_ONLY))==0) || + (programMode!=MODE_TRAIN&&(menuPLs[inx].option&IC_MODETRAIN_ONLY)!=0) ) + enable = FALSE; + else + enable = TRUE; + wMenuPushEnable( (wMenuPush_p)menuPLs[inx].control, enable ); + } + + for ( inx=0; inx<buttonCnt; inx++ ) { + if ( buttonList[inx].cmdInx < 0 && (buttonList[inx].options&IC_SELECTED) ) + wControlActive( buttonList[inx].control, selectedTrackCount>0 ); + } +} + + +EXPORT void Reset( void ) +{ + if (recordF) { + fprintf( recordF, "RESET\n" ); + fflush( recordF ); + } +LOG( log_command, 2, ( "COMMAND CANCEL %s\n", commandList[curCommand].helpKey ) ) + commandList[curCommand].cmdProc( C_CANCEL, zero ); + if ( commandList[curCommand].buttInx>=0 ) + wButtonSetBusy( (wButton_p)buttonList[commandList[curCommand].buttInx].control, FALSE ); + curCommand = (preSelect?selectCmdInx:describeCmdInx); + commandContext = commandList[curCommand].context; + if ( commandList[curCommand].buttInx >= 0 ) + wButtonSetBusy( (wButton_p)buttonList[commandList[curCommand].buttInx].control, TRUE ); + tempSegs_da.cnt = 0; + if (checkPtInterval > 0 && + changed >= checkPtMark+(wIndex_t)checkPtInterval && + !inPlayback ) { + DoCheckPoint(); + checkPtMark = changed; + } + MainRedraw(); + EnableCommands(); + ResetMouseState(); +LOG( log_command, 1, ( "COMMAND RESET %s\n", commandList[curCommand].helpKey ) ) + (void)commandList[curCommand].cmdProc( C_START, zero ); +} + + +static BOOL_T CheckClick( + wAction_t *action, + coOrd *pos, + BOOL_T checkLeft, + BOOL_T checkRight ) +{ + static long time0; + static coOrd pos0; + long time1; + long timeDelta; + DIST_T distDelta; + + switch (*action) { + case C_DOWN: + if (!checkLeft) + return TRUE; + time0 = wGetTimer() - adjTimer; + pos0 = *pos; + return FALSE; + case C_MOVE: + if (!checkLeft) + return TRUE; + if (time0 != 0) { + time1 = wGetTimer() - adjTimer; + timeDelta = time1 - time0; + distDelta = FindDistance( *pos, pos0 ); + if ( timeDelta > dragTimeout || + distDelta > dragDistance ) { + time0 = 0; + *pos = pos0; + *action = C_DOWN; + } else { + return FALSE; + } + } + break; + case C_UP: + if (!checkLeft) + return TRUE; + if (time0 != 0) { + time1 = wGetTimer() - adjTimer; + timeDelta = time1 - time0; + distDelta = FindDistance( *pos, pos0 ); + time0 = 0; + *action = C_LCLICK; + } + break; + case C_RDOWN: + if (!checkRight) + return TRUE; + time0 = wGetTimer() - adjTimer; + pos0 = *pos; + return FALSE; + case C_RMOVE: + if (!checkRight) + return TRUE; + if (time0 != 0) { + time1 = wGetTimer() - adjTimer; + timeDelta = time1 - time0; + distDelta = FindDistance( *pos, pos0 ); + if ( timeDelta > dragTimeout || + distDelta > dragDistance ) { + time0 = 0; + *pos = pos0; + *action = C_RDOWN; + } else { + return FALSE; + } + } + break; + case C_RUP: + if (!checkRight) + return TRUE; + if (time0 != 0) { + time0 = 0; + *action = C_RCLICK; + } + break; + } + return TRUE; +} + + +EXPORT wBool_t DoCurCommand( wAction_t action, coOrd pos ) +{ + wAction_t rc; + int mode; + + if ( action == wActionMove && (commandList[curCommand].options & IC_WANT_MOVE) == 0 ) + return C_CONTINUE; + + if ( !CheckClick( &action, &pos, + (int)(commandList[curCommand].options & IC_LCLICK), TRUE ) ) + return C_CONTINUE; + + if ( action == C_RCLICK && (commandList[curCommand].options&IC_RCLICK)==0 ) { + if ( !inPlayback ) { + mode = MyGetKeyState(); + if ( ( mode & (~WKEY_SHIFT) ) != 0 ) { + wBeep(); + return C_CONTINUE; + } + if ( ((mode&WKEY_SHIFT) == 0) == (rightClickMode==0) ) { + if ( selectedTrackCount > 0 ) { + if (commandList[curCommand].options & IC_CMDMENU) { + } + wMenuPopupShow( popup2M ); + } else { + wMenuPopupShow( popup1M ); + } + return C_CONTINUE; + } else if ( (commandList[curCommand].options & IC_CMDMENU) ) { + cmdMenuPos = pos; + action = C_CMDMENU; + } else { + wBeep(); + return C_CONTINUE; + } + } else { + return C_CONTINUE; + } + } + +LOG( log_command, 2, ( "COMMAND MOUSE %s %d @ %0.3f %0.3f\n", commandList[curCommand].helpKey, (int)action, pos.x, pos.y ) ) + rc = commandList[curCommand].cmdProc( action, pos ); +LOG( log_command, 4, ( " COMMAND returns %d\n", rc ) ) + if ( (rc == C_TERMINATE || rc == C_INFO) && + (commandList[curCommand].options & IC_STICKY) && + (commandList[curCommand].stickyMask & stickySet) ) { + tempSegs_da.cnt = 0; + UpdateAllElevations(); + MainRedraw(); + if (commandList[curCommand].options & IC_NORESTART) { + return C_CONTINUE; + } +LOG( log_command, 1, ( "COMMAND START %s\n", commandList[curCommand].helpKey ) ) + rc = commandList[curCommand].cmdProc( C_START, pos ); +LOG( log_command, 4, ( " COMMAND returns %d\n", rc ) ) + switch( rc ) { + case C_CONTINUE: + break; + case C_ERROR: + Reset(); +#ifdef VERBOSE + lprintf( "Start returns Error"); +#endif + break; + case C_TERMINATE: + InfoMessage( "" ); + case C_INFO: + Reset(); + break; + } + } + return rc; +} + + +EXPORT void ConfirmReset( BOOL_T retry ) +{ + wAction_t rc; + if (curCommand != describeCmdInx && curCommand != selectCmdInx ) { +LOG( log_command, 3, ( "COMMAND CONFIRM %s\n", commandList[curCommand].helpKey ) ) + rc = commandList[curCommand].cmdProc( C_CONFIRM, zero ); +LOG( log_command, 4, ( " COMMAND returns %d\n", rc ) ) + if ( rc == C_ERROR ) { + if (retry) + rc = wNotice3( + _("Cancelling the current command will undo the changes\n" + "you are currently making. Do you want to update?"), + _("Yes"), _("No"), _("Cancel") ); + else + rc = wNoticeEx( NT_WARNING, + _("Cancelling the current command will undo the changes\n" + "you are currently making. Do you want to update?"), + _("Yes"), _("No") ); + if (rc == 1) { +LOG( log_command, 3, ( "COMMAND OK %s\n", commandList[curCommand].helpKey ) ) + commandList[curCommand].cmdProc( C_OK, zero ); + return; + } else if (rc == -1) { + return; + } + } else if ( rc == C_TERMINATE ) { + return; + } + } + Reset(); + if (retry) { + /* because user pressed esc */ + SetAllTrackSelect( FALSE ); + } +LOG( log_command, 1, ( "COMMAND RESET %s\n", commandList[curCommand].helpKey ) ) + commandList[curCommand].cmdProc( C_START, zero ); +} + + +EXPORT void ResetIfNotSticky( void ) +{ + if ( (commandList[curCommand].options & IC_STICKY) == 0 || + (commandList[curCommand].stickyMask & stickySet) == 0 ) + Reset(); +} + + +EXPORT void DoCommandB( + void * data ) +{ + wIndex_t inx = (wIndex_t)(long)data; + STATUS_T rc; + static coOrd pos = {0,0}; + static int inDoCommandB = FALSE; + wIndex_t buttInx; + + if (inDoCommandB) + return; + inDoCommandB = TRUE; + + if (inx < 0 || inx >= commandCnt) { + ASSERT( FALSE ); + inDoCommandB = FALSE; + return; + } + + if ( (!inPlayback) && (!commandList[inx].enabled) ) { + ErrorMessage( MSG_COMMAND_DISABLED ); + inx = describeCmdInx; + } + + InfoMessage( "" ); + if (curCommand != selectCmdInx ) { +LOG( log_command, 3, ( "COMMAND FINISH %s\n", commandList[curCommand].helpKey ) ) + rc = commandList[curCommand].cmdProc( C_FINISH, zero ); +LOG( log_command, 3, ( "COMMAND CONFIRM %s\n", commandList[curCommand].helpKey ) ) + rc = commandList[curCommand].cmdProc( C_CONFIRM, zero ); +LOG( log_command, 4, ( " COMMAND returns %d\n", rc ) ) + if ( rc == C_ERROR ) { + rc = wNotice3( + _("Cancelling the current command will undo the changes\n" + "you are currently making. Do you want to update?"), + _("Yes"), _("No"), _("Cancel") ); + if (rc == 1) + commandList[curCommand].cmdProc( C_OK, zero ); + else if (rc == -1) { + inDoCommandB = FALSE; + return; + } + } +LOG( log_command, 3, ( "COMMAND CANCEL %s\n", commandList[curCommand].helpKey ) ) + commandList[curCommand].cmdProc( C_CANCEL, pos ); + tempSegs_da.cnt = 0; + } + if (commandList[curCommand].buttInx>=0) + wButtonSetBusy( (wButton_p)buttonList[commandList[curCommand].buttInx].control, FALSE ); + + if (recordF) { + fprintf( recordF, "COMMAND %s\n", commandList[inx].helpKey+3 ); + fflush( recordF ); + } + + curCommand = inx; + commandContext = commandList[curCommand].context; + if ( (buttInx=commandList[curCommand].buttInx) >= 0 ) { + if ( buttonList[buttInx].cmdInx != curCommand ) { + wButtonSetLabel( (wButton_p)buttonList[buttInx].control, (char*)commandList[curCommand].icon ); + wControlSetHelp( buttonList[buttInx].control, GetBalloonHelpStr(commandList[curCommand].helpKey) ); + wControlSetContext( buttonList[buttInx].control, (void*)(intptr_t)curCommand ); + buttonList[buttInx].cmdInx = curCommand; + } + wButtonSetBusy( (wButton_p)buttonList[commandList[curCommand].buttInx].control, TRUE ); + } +LOG( log_command, 1, ( "COMMAND START %s\n", commandList[curCommand].helpKey ) ) + rc = commandList[curCommand].cmdProc( C_START, pos ); +LOG( log_command, 4, ( " COMMAND returns %d\n", rc ) ) + switch( rc ) { + case C_CONTINUE: + break; + case C_ERROR: + Reset(); +#ifdef VERBOSE + lprintf( "Start returns Error"); +#endif + break; + case C_TERMINATE: + case C_INFO: + if (rc == C_TERMINATE) + InfoMessage( "" ); + Reset(); + break; + } + inDoCommandB = FALSE; +} + + +static void DoCommandBIndirect( void * cmdInxP ) +{ + wIndex_t cmdInx; + cmdInx = *(wIndex_t*)cmdInxP; + DoCommandB( (void*)(intptr_t)cmdInx ); +} + + +EXPORT void LayoutSetPos( + wIndex_t inx ) +{ + wPos_t w, h; + static wPos_t toolbarRowHeight = 0; + static wPos_t width; + static int lastGroup; + static wPos_t gap; + static int layerButtCnt; + int currGroup; + + if ( inx == 0 ) { + lastGroup = 0; + wWinGetSize( mainW, &width, &h ); + gap = 5; + toolbarWidth = width+5; + layerButtCnt = 0; + toolbarHeight = 0; + } + + if (buttonList[inx].control) { + if ( toolbarRowHeight <= 0 ) + toolbarRowHeight = wControlGetHeight( buttonList[inx].control ); + + currGroup = buttonList[inx].group & ~BG_BIGGAP; + if ( currGroup != lastGroup && (buttonList[inx].group&BG_BIGGAP) ) { + gap = 15; + } + if ((toolbarSet & (1<<currGroup)) && + (programMode!=MODE_TRAIN||(buttonList[inx].options&(IC_MODETRAIN_TOO|IC_MODETRAIN_ONLY))) && + (programMode==MODE_TRAIN||(buttonList[inx].options&IC_MODETRAIN_ONLY)==0) && + ((buttonList[inx].group&~BG_BIGGAP) != BG_LAYER || + layerButtCnt++ <= layerCount) ) { + if (currGroup != lastGroup) { + toolbarWidth += gap; + lastGroup = currGroup; + gap = 5; + } + w = wControlGetWidth( buttonList[inx].control ); + h = wControlGetHeight( buttonList[inx].control ); + if ( inx<buttonCnt-1 && (buttonList[inx+1].options&IC_ABUT) ) + w += wControlGetWidth( buttonList[inx+1].control ); + if (toolbarWidth+w>width) { + toolbarWidth = 0; + toolbarHeight += h + 5; + } + wControlSetPos( buttonList[inx].control, toolbarWidth, toolbarHeight-(h+5) ); + buttonList[inx].x = toolbarWidth; + buttonList[inx].y = toolbarHeight-(h+5); + toolbarWidth += wControlGetWidth( buttonList[inx].control ); + wControlShow( buttonList[inx].control, TRUE ); + } else { + wControlShow( buttonList[inx].control, FALSE ); + } + } +} + + +EXPORT void LayoutToolBar( void ) +{ + int inx; + + for (inx = 0; inx<buttonCnt; inx++) { + LayoutSetPos( inx ); + } + if (toolbarSet&(1<<BG_HOTBAR)) { + LayoutHotBar(); + } else { + HideHotBar(); + } +} + + +static void ToolbarChange( long changes ) +{ + if ( (changes&CHANGE_TOOLBAR) ) { + /*if ( !(changes&CHANGE_MAIN) )*/ + MainProc( mainW, wResize_e, NULL ); + /*else + LayoutToolBar();*/ + } +} + +/*************************************************************************** + * + * + * + */ + + +EXPORT BOOL_T CommandEnabled( + wIndex_t cmdInx ) +{ + return commandList[cmdInx].enabled; +} + + +static wIndex_t AddCommand( + procCommand_t cmdProc, + char * helpKey, + char * nameStr, + wIcon_p icon, + int reqLevel, + long options, + long acclKey, + void * context ) +{ + if (commandCnt >= COMMAND_MAX-1) { + AbortProg("addCommand: too many commands" ); + } + commandList[commandCnt].labelStr = MyStrdup(nameStr); + commandList[commandCnt].helpKey = MyStrdup(helpKey); + commandList[commandCnt].cmdProc = cmdProc; + commandList[commandCnt].icon = icon; + commandList[commandCnt].reqLevel = reqLevel; + commandList[commandCnt].enabled = TRUE; + commandList[commandCnt].options = options; + commandList[commandCnt].acclKey = acclKey; + commandList[commandCnt].context = context; + commandList[commandCnt].buttInx = -1; + commandList[commandCnt].menu[0] = NULL; + commandList[commandCnt].menu[1] = NULL; + commandList[commandCnt].menu[2] = NULL; + commandList[commandCnt].menu[3] = NULL; + commandCnt++; + return commandCnt-1; +} + +EXPORT void AddToolbarControl( + wControl_p control, + long options ) +{ + if (buttonCnt >= COMMAND_MAX-1) { + AbortProg("addToolbarControl: too many buttons" ); + } + buttonList[buttonCnt].enabled = TRUE; + buttonList[buttonCnt].options = options; + buttonList[buttonCnt].group = cmdGroup; + buttonList[buttonCnt].x = 0; + buttonList[buttonCnt].y = 0; + buttonList[buttonCnt].control = control; + buttonList[buttonCnt].cmdInx = -1; + LayoutSetPos( buttonCnt ); + buttonCnt++; +} + + +EXPORT wButton_p AddToolbarButton( + char * helpStr, + wIcon_p icon, + long options, + wButtonCallBack_p action, + void * context ) +{ + wButton_p bb; + wIndex_t inx; + + GetBalloonHelpStr(helpStr); + if ( context == NULL ) { + for ( inx=0; inx<menuPG.paramCnt; inx++ ) { + if ( action != DoCommandB && menuPLs[inx].valueP == (void*)action ) { + context = &menuPLs[inx]; + action = ParamMenuPush; + menuPLs[inx].context = (void*)(intptr_t)buttonCnt; + menuPLs[inx].option |= IC_PLAYBACK_PUSH; + break; + } + } + } + bb = wButtonCreate( mainW, 0, 0, helpStr, (char*)icon, + BO_ICON/*|((options&IC_CANCEL)?BB_CANCEL:0)*/, 0, + action, context ); + AddToolbarControl( (wControl_p)bb, options ); + return bb; +} + + +EXPORT void PlaybackButtonMouse( + wIndex_t buttInx ) +{ + wPos_t cmdX, cmdY; + + if ( buttInx < 0 || buttInx >= buttonCnt ) return; + if ( buttonList[buttInx].control == NULL ) return; + cmdX = buttonList[buttInx].x+17; + cmdY = toolbarHeight - (buttonList[buttInx].y+17) + + (wPos_t)(mainD.size.y/mainD.scale*mainD.dpi) + 30; + MovePlaybackCursor( &mainD, cmdX, cmdY ); + if ( playbackTimer == 0 ) { + wButtonSetBusy( (wButton_p)buttonList[buttInx].control, TRUE ); + wFlush(); + wPause( 500 ); + wButtonSetBusy( (wButton_p)buttonList[buttInx].control, FALSE ); + wFlush(); + } +} + + +#include "bitmaps/openbutt.xpm" +static char * buttonGroupMenuTitle; +static char * buttonGroupHelpKey; +static char * buttonGroupStickyLabel; +static wMenu_p buttonGroupPopupM; + +EXPORT void ButtonGroupBegin( + char * menuTitle, + char * helpKey, + char * stickyLabel ) +{ + buttonGroupMenuTitle = menuTitle; + buttonGroupHelpKey = helpKey; + buttonGroupStickyLabel = stickyLabel; + buttonGroupPopupM = NULL; +} + +EXPORT void ButtonGroupEnd( void ) +{ + buttonGroupMenuTitle = NULL; + buttonGroupHelpKey = NULL; + buttonGroupPopupM = NULL; +} + + +#ifdef LATER +EXPORT wIndex_t AddCommandControl( + procCommand_t command, + char * helpKey, + char * nameStr, + wControl_p control, + int reqLevel, + long options, + long acclKey, + void * context ) +{ + wIndex_t buttInx = -1; + wIndex_t cmdInx; + BOOL_T newButtonGroup = FALSE; + wMenu_p tm, p1m, p2m; + static wIcon_p openbuttIcon = NULL; + static wMenu_p commandsSubmenu; + static wMenu_p popup1Submenu; + static wMenu_p popup2Submenu; + + AddToolbarControl( control, options ); + + buttonList[buttInx].cmdInx = commandCnt; + cmdInx = AddCommand( command, helpKey, nameStr, NULL, reqLevel, options, acclKey, context ); + commandList[cmdInx].buttInx = buttInx; + if (nameStr[0] == '\0') + return cmdInx; + if (commandList[cmdInx].options&IC_STICKY) { + if ( buttonGroupPopupM==NULL || newButtonGroup ) { + if ( stickyCnt > 32 ) + AbortProg( "stickyCnt>32" ); + stickyCnt++; + } + if ( buttonGroupPopupM==NULL) { + stickyLabels[stickyCnt-1] = nameStr; + } else { + stickyLabels[stickyCnt-1] = buttonGroupStickyLabel; + } + stickyLabels[stickyCnt] = NULL; + commandList[cmdInx].stickyMask = 1L<<(stickyCnt-1); + } + if ( buttonGroupPopupM ) { + commandList[cmdInx].menu[0] = + wMenuPushCreate( buttonGroupPopupM, helpKey, GetBalloonHelpStr(helpKey), 0, DoCommandB, (void*)cmdInx ); + tm = commandsSubmenu; + p1m = popup1Submenu; + p2m = popup2Submenu; + } else { + tm = commandsM; + p1m = (options&IC_POPUP2)?popup1aM:popup1M; + p2m = (options&IC_POPUP2)?popup2aM:popup2M; + } + commandList[cmdInx].menu[1] = + wMenuPushCreate( tm, helpKey, nameStr, acclKey, DoCommandB, (void*)cmdInx ); + if ( (options & (IC_POPUP|IC_POPUP2)) ) { + if ( !(options & IC_SELECTED) ) { + commandList[cmdInx].menu[2] = + wMenuPushCreate( p1m, helpKey, nameStr, 0, DoCommandB, (void*)cmdInx ); + } + commandList[cmdInx].menu[3] = + wMenuPushCreate( p2m, helpKey, nameStr, 0, DoCommandB, (void*)cmdInx ); + } + + return cmdInx; +} +#endif + + +EXPORT wIndex_t AddMenuButton( + wMenu_p menu, + procCommand_t command, + char * helpKey, + char * nameStr, + wIcon_p icon, + int reqLevel, + long options, + long acclKey, + void * context ) +{ + wIndex_t buttInx = -1; + wIndex_t cmdInx; + BOOL_T newButtonGroup = FALSE; + wMenu_p tm, p1m, p2m; + static wIcon_p openbuttIcon = NULL; + static wMenu_p commandsSubmenu; + static wMenu_p popup1Submenu; + static wMenu_p popup2Submenu; + + if ( icon ) { + if ( buttonGroupPopupM!=NULL ) { + buttInx = buttonCnt-2; + } else { + buttInx = buttonCnt; + AddToolbarButton( helpKey, icon, options, (wButtonCallBack_p)DoCommandB, (void*)(intptr_t)commandCnt ); + buttonList[buttInx].cmdInx = commandCnt; + } + if ( buttonGroupMenuTitle!=NULL && buttonGroupPopupM==NULL ) { + if ( openbuttIcon == NULL ) + openbuttIcon = wIconCreatePixMap(openbutt_xpm); + buttonGroupPopupM = wMenuPopupCreate( mainW, buttonGroupMenuTitle ); + AddToolbarButton( buttonGroupHelpKey, openbuttIcon, IC_ABUT, (wButtonCallBack_p)wMenuPopupShow, (void*)buttonGroupPopupM ); + newButtonGroup = TRUE; + commandsSubmenu = wMenuMenuCreate( menu, "", buttonGroupMenuTitle ); + popup1Submenu = wMenuMenuCreate( ((options&IC_POPUP2)?popup1aM:popup1M), "", buttonGroupMenuTitle ); + popup2Submenu = wMenuMenuCreate( ((options&IC_POPUP2)?popup2aM:popup2M), "", buttonGroupMenuTitle ); + } + } + cmdInx = AddCommand( command, helpKey, nameStr, icon, reqLevel, options, acclKey, context ); + commandList[cmdInx].buttInx = buttInx; + if (nameStr[0] == '\0') + return cmdInx; + if (commandList[cmdInx].options&IC_STICKY) { + if ( buttonGroupPopupM==NULL || newButtonGroup ) { + if ( stickyCnt > 32 ) + AbortProg( "stickyCnt>32" ); + stickyCnt++; + } + if ( buttonGroupPopupM==NULL) { + stickyLabels[stickyCnt-1] = nameStr; + } else { + stickyLabels[stickyCnt-1] = buttonGroupStickyLabel; + } + stickyLabels[stickyCnt] = NULL; + commandList[cmdInx].stickyMask = 1L<<(stickyCnt-1); + } + if ( buttonGroupPopupM ) { + commandList[cmdInx].menu[0] = + wMenuPushCreate( buttonGroupPopupM, helpKey, GetBalloonHelpStr(helpKey), 0, DoCommandB, (void*)(intptr_t)cmdInx ); + tm = commandsSubmenu; + p1m = popup1Submenu; + p2m = popup2Submenu; + } else { + tm = menu; + p1m = (options&IC_POPUP2)?popup1aM:popup1M; + p2m = (options&IC_POPUP2)?popup2aM:popup2M; + } + commandList[cmdInx].menu[1] = + wMenuPushCreate( tm, helpKey, nameStr, acclKey, DoCommandB, (void*)(intptr_t)cmdInx ); + if ( (options & (IC_POPUP|IC_POPUP2)) ) { + if ( !(options & IC_SELECTED) ) { + commandList[cmdInx].menu[2] = + wMenuPushCreate( p1m, helpKey, nameStr, 0, DoCommandB, (void*)(intptr_t)cmdInx ); + } + commandList[cmdInx].menu[3] = + wMenuPushCreate( p2m, helpKey, nameStr, 0, DoCommandB, (void*)(intptr_t)cmdInx ); + } + + return cmdInx; +} + + +EXPORT wIndex_t InitCommand( + wMenu_p menu, + procCommand_t command, + char * nameStr, + char * bits, + int reqLevel, + long options, + long acclKey ) +{ + char helpKey[STR_SHORT_SIZE]; + wIcon_p icon = NULL; + if (bits) + icon = wIconCreateBitMap( 16, 16, bits, wDrawColorBlack ); + strcpy( helpKey, "cmd" ); + strcat( helpKey, nameStr ); + return AddMenuButton( menu, command, helpKey, _(nameStr), icon, reqLevel, options, acclKey, NULL ); +} + +/*--------------------------------------------------------------------*/ + +EXPORT void PlaybackCommand( + char * line, + wIndex_t lineNum ) +{ + wIndex_t inx; + wIndex_t buttInx; + int len1, len2; + len1 = strlen(line+8); + for (inx=0;inx<commandCnt;inx++) { + len2 = strlen(commandList[inx].helpKey+3); + if (len1 == len2 && strncmp( line+8, commandList[inx].helpKey+3, len2 ) == 0) { + break; + } + } + if (inx >= commandCnt) { + fprintf(stderr, "Unknown playback COMMAND command %d : %s\n", + lineNum, line ); + } else { + wPos_t cmdX, cmdY; + if ((buttInx=commandList[inx].buttInx)>=0) { + cmdX = buttonList[buttInx].x+17; + cmdY = toolbarHeight - (buttonList[buttInx].y+17) + + (wPos_t)(mainD.size.y/mainD.scale*mainD.dpi) + 30; + MovePlaybackCursor( &mainD, cmdX, cmdY ); + } + if (strcmp( line+8, "Undo") == 0) { + if (buttInx>0 && playbackTimer == 0) { + wButtonSetBusy( (wButton_p)buttonList[buttInx].control, TRUE ); + wFlush(); + wPause( 500 ); + wButtonSetBusy( (wButton_p)buttonList[buttInx].control, FALSE ); + wFlush(); + } + UndoUndo(); + } else if (strcmp( line+8, "Redo") == 0) { + if (buttInx>=0 && playbackTimer == 0) { + wButtonSetBusy( (wButton_p)buttonList[buttInx].control, TRUE ); + wFlush(); + wPause( 500 ); + wButtonSetBusy( (wButton_p)buttonList[buttInx].control, FALSE ); + wFlush(); + } + UndoRedo(); + } else { + if ( buttInx>=0 && + playbackTimer == 0 ) { + wButtonSetBusy( (wButton_p)buttonList[buttInx].control, TRUE ); + wFlush(); + wPause( 500 ); + wButtonSetBusy( (wButton_p)buttonList[buttInx].control, FALSE ); + wFlush(); + } + DoCommandB( (void*)(intptr_t)inx ); + } + } +} + + +/*--------------------------------------------------------------------*/ +typedef struct { + char * label; + wMenu_p menu; + } menuTrace_t, *menuTrace_p; +static dynArr_t menuTrace_da; +#define menuTrace(N) DYNARR_N( menuTrace_t, menuTrace_da, N ) + + +static void DoMenuTrace( + wMenu_p menu, + const char * label, + void * data ) +{ + /*printf( "MENUTRACE: %s/%s\n", (char*)data, label );*/ + if (recordF) { + fprintf( recordF, "MOUSE 1 %0.3f %0.3f\n", oldMarker.x, oldMarker.y ); + fprintf( recordF, "MENU %0.3f %0.3f \"%s\" \"%s\"\n", oldMarker.x, oldMarker.y, (char*)data, label ); + } +} + + +EXPORT wMenu_p MenuRegister( char * label ) +{ + wMenu_p m; + menuTrace_p mt; + m = wMenuPopupCreate( mainW, label ); + DYNARR_APPEND( menuTrace_t, menuTrace_da, 10 ); + mt = &menuTrace( menuTrace_da.cnt-1 ); + mt->label = strdup(label); + mt->menu = m; + wMenuSetTraceCallBack( m, DoMenuTrace, mt->label ); + return m; +} + + +void MenuPlayback( char * line ) +{ + char * menuName, * itemName; + coOrd pos; + wPos_t x, y; + menuTrace_p mt; + + if (!GetArgs( line, "pqq", &pos, &menuName, &itemName )) + return; + for ( mt=&menuTrace(0); mt<&menuTrace(menuTrace_da.cnt); mt++ ) { + if ( strcmp( mt->label, menuName ) == 0 ) { + mainD.CoOrd2Pix( &mainD, pos, &x, &y ); + MovePlaybackCursor( &mainD, x, y ); + oldMarker = cmdMenuPos = pos; + wMenuAction( mt->menu, _(itemName) ); + return; + } + } + AbortProg( "menuPlayback: %s not found", menuName ); +} +/*--------------------------------------------------------------------*/ + + +static wWin_p stickyW; + +static void StickyOk( void * ); +static paramData_t stickyPLs[] = { + { PD_TOGGLE, &stickySet, "set", 0, stickyLabels } }; +static paramGroup_t stickyPG = { "sticky", PGO_RECORD, stickyPLs, sizeof stickyPLs/sizeof stickyPLs[0] }; + + +static void StickyOk( void * junk ) +{ + wHide( stickyW ); +} + +static void DoSticky( void ) +{ + if ( !stickyW ) + stickyW = ParamCreateDialog( &stickyPG, MakeWindowTitle(_("Sticky Commands")), _("Ok"), StickyOk, NULL, TRUE, NULL, 0, NULL ); + ParamLoadControls( &stickyPG ); + wShow( stickyW ); +} +/*--------------------------------------------------------------------*/ + +/* + * These array control the choices available in the Toolbar setup. + * For each choice, the text is given and the respective mask is + * specified in the following array. + * Note: text and choices must be given in the same order. + */ +static char *AllToolbarLabels[] = { + N_("File Buttons"), + N_("Zoom Buttons"), + N_("Undo Buttons"), + N_("Easement Button"), + N_("SnapGrid Buttons"), + N_("Create Track Buttons"), +#ifdef XTRKCAD_USE_LAYOUTCONTROL + N_("Layout Control Elements"), +#endif + N_("Modify Track Buttons"), + N_("Describe/Select"), + N_("Track Group Buttons"), + N_("Train Group Buttons"), + N_("Create Misc Buttons"), + N_("Ruler Button"), + N_("Layer Buttons"), + N_("Hot Bar"), + NULL }; +static long AllToolbarMasks[] = { + 1<<BG_FILE, + 1<<BG_ZOOM, + 1<<BG_UNDO, + 1<<BG_EASE, + 1<<BG_SNAP, + 1<<BG_TRKCRT, +#ifdef XTRKCAD_USE_LAYOUTCONTROL + 1<<BG_CONTROL, +#endif + 1<<BG_TRKMOD, + 1<<BG_SELECT, + 1<<BG_TRKGRP, + 1<<BG_TRAIN, + 1<<BG_MISCCRT, + 1<<BG_RULER, + 1<<BG_LAYER, + 1<<BG_HOTBAR}; + +static void ToolbarAction( wBool_t set, void * data ) +{ + long mask = (long)data; + if (set) + toolbarSet |= mask; + else + toolbarSet &= ~mask; + wPrefSetInteger( "misc", "toolbarset", toolbarSet ); + MainProc( mainW, wResize_e, NULL ); + if (recordF) + fprintf( recordF, "PARAMETER %s %s %ld", "misc", "toolbarset", toolbarSet ); +} + +/** + * Create the Toolbar configuration submenu. Based on two arrays of descriptions and + * masks, the toolbar submenu is created dynamically. + * + * \param toolbarM IN menu to which the toogles will be added + */ + +static void CreateToolbarM( wMenu_p toolbarM ) +{ + int inx, cnt; + long *masks; + char **labels; + wBool_t set; + + cnt = sizeof(AllToolbarMasks)/sizeof(AllToolbarMasks[0]); + masks = AllToolbarMasks; + labels = AllToolbarLabels; + for (inx=0; inx<cnt; inx++,masks++,labels++) { + set = ( toolbarSet & *masks ) != 0; + wMenuToggleCreate( toolbarM, "toolbarM", _(*labels), 0, set, ToolbarAction, (void*)*masks ); + } +} + +/*--------------------------------------------------------------------*/ + +static wWin_p addElevW; +#define addElevF (wFloat_p)addElevPD.control +EXPORT DIST_T addElevValueV; +static void DoAddElev( void * ); + +static paramFloatRange_t rn1000_1000 = { -1000.0, 1000.0 }; +static paramData_t addElevPLs[] = { + { PD_FLOAT, &addElevValueV, "value", PDO_DIM, &rn1000_1000, NULL, 0 } }; +static paramGroup_t addElevPG = { "addElev", 0, addElevPLs, sizeof addElevPLs/sizeof addElevPLs[0] }; + + +static void DoAddElev( void * junk ) +{ + ParamLoadData( &addElevPG ); + AddElevations( addElevValueV ); + wHide( addElevW ); +} + + +static void ShowAddElevations( void ) +{ + if ( selectedTrackCount <= 0 ) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return; + } + if (addElevW == NULL) + addElevW = ParamCreateDialog( &addElevPG, MakeWindowTitle(_("Change Elevations")), _("Change"), DoAddElev, wHide, FALSE, NULL, 0, NULL ); + wShow( addElevW ); +} + +/*--------------------------------------------------------------------*/ + +static wWin_p rotateW; +static long rotateValue; +static rotateDialogCallBack_t rotateDialogCallBack; + +static void RotateEnterOk( void * ); +static paramIntegerRange_t rn360_360 = { -360, 360, 80 }; +static paramData_t rotatePLs[] = { + { PD_LONG, &rotateValue, "rotate", PDO_ANGLE, &rn360_360, N_("Angle:") } }; +static paramGroup_t rotatePG = { "rotate", 0, rotatePLs, sizeof rotatePLs/sizeof rotatePLs[0] }; + + +EXPORT void StartRotateDialog( rotateDialogCallBack_t func ) +{ + if ( rotateW == NULL ) + rotateW = ParamCreateDialog( &rotatePG, MakeWindowTitle(_("Rotate")), _("Ok"), RotateEnterOk, wHide, FALSE, NULL, 0, NULL ); + ParamLoadControls( &rotatePG ); + rotateDialogCallBack = func; + wShow( rotateW ); +} + + +static void RotateEnterOk( void * junk ) +{ + ParamLoadData( &rotatePG ); + if (angleSystem==ANGLE_POLAR) + rotateDialogCallBack( (void*)rotateValue ); + else + rotateDialogCallBack( (void*)-rotateValue ); + wHide( rotateW ); +} + + +static void RotateDialogInit( void ) +{ + ParamRegister( &rotatePG ); +} + + +EXPORT void AddRotateMenu( + wMenu_p m, + rotateDialogCallBack_t func ) +{ + wMenuPushCreate( m, "", _("180 "), 0, func, (void*)180 ); + wMenuPushCreate( m, "", _("90 CW"), 0, func, (void*)(long)(90) ); + wMenuPushCreate( m, "", _("45 CW"), 0, func, (void*)(long)(45) ); + wMenuPushCreate( m, "", _("30 CW"), 0, func, (void*)(long)(30) ); + wMenuPushCreate( m, "", _("15 CW"), 0, func, (void*)(long)(15) ); + wMenuPushCreate( m, "", _("15 CCW"), 0, func, (void*)(long)(360-15) ); + wMenuPushCreate( m, "", _("30 CCW"), 0, func, (void*)(long)(360-30) ); + wMenuPushCreate( m, "", _("45 CCW"), 0, func, (void*)(long)(360-45) ); + wMenuPushCreate( m, "", _("90 CCW"), 0, func, (void*)(long)(360-90) ); + wMenuPushCreate( m, "", _("Enter Angle ..."), 0, (wMenuCallBack_p)StartRotateDialog, (void*)func ); +} + +/***************************************************************************** + * + * INITIALIZATON + * + */ + + +static wWin_p debugW; + +static int debugCnt = 0; +static paramIntegerRange_t r0_100 = { 0, 100, 80 }; +static void DebugOk( void * junk ); +static paramData_t debugPLs[20]; +static paramGroup_t debugPG = { "debug", 0, debugPLs, 0 }; + +static void DebugOk( void * junk ) +{ + wHide( debugW ); +} + +static void CreateDebugW( void ) +{ + debugPG.paramCnt = debugCnt; + ParamRegister( &debugPG ); + debugW = ParamCreateDialog( &debugPG, MakeWindowTitle(_("Debug")), _("Ok"), DebugOk, NULL, FALSE, NULL, 0, NULL ); +} + + +EXPORT void InitDebug( + char * label, + long * valueP ) +{ + if ( debugCnt >= sizeof debugPLs/sizeof debugPLs[0] ) + AbortProg( "Too many debug flags" ); + memset( &debugPLs[debugCnt], 0, sizeof debugPLs[debugCnt] ); + debugPLs[debugCnt].type = PD_LONG; + debugPLs[debugCnt].valueP = valueP; + debugPLs[debugCnt].nameStr = label; + debugPLs[debugCnt].winData = &r0_100; + debugPLs[debugCnt].winLabel = label; + debugCnt++; +} + + +void RecomputeElevations( void ); + +static void MiscMenuItemCreate( + wMenu_p m1, + wMenu_p m2, + char * name, + char * label, + long acclKey, + void * func, + long option, + void * context ) +{ + wMenuPush_p mp; + mp = wMenuPushCreate( m1, name, label, acclKey, ParamMenuPush, &menuPLs[menuPG.paramCnt] ); + if ( m2 ) + wMenuPushCreate( m2, name, label, acclKey, ParamMenuPush, &menuPLs[menuPG.paramCnt] ); + menuPLs[menuPG.paramCnt].control = (wControl_p)mp; + menuPLs[menuPG.paramCnt].type = PD_MENUITEM; + menuPLs[menuPG.paramCnt].valueP = func; + menuPLs[menuPG.paramCnt].nameStr = name; + menuPLs[menuPG.paramCnt].option = option; + menuPLs[menuPG.paramCnt].context = context; + + if ( name ) GetBalloonHelpStr( name ); + menuPG.paramCnt++; +} + + +static char * accelKeyNames[] = { + "Del", + "Ins", + "Home", + "End", + "Pgup", + "Pgdn", + "Up", + "Down", + "Right", + "Left", + "Back", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12" }; + +static void SetAccelKey( + char * prefName, + wAccelKey_e key, + int mode, + wAccelKeyCallBack_p func, + void * context ) +{ + int mode1 = 0; + int inx; + const char * prefValue = wPrefGetString( "accelKey", prefName ); + if ( prefValue != NULL ) { + while ( prefValue[1] == '-' ) { + switch ( prefValue[0] ) { + case 'S': mode1 |= WKEY_SHIFT; break; + case 'C': mode1 |= WKEY_CTRL; break; + case 'A': mode1 |= WKEY_ALT; break; + default: + ; + } + prefValue += 2; + } + for ( inx=0; inx<sizeof accelKeyNames/sizeof accelKeyNames[0]; inx++ ) { + if ( strcmp( prefValue, accelKeyNames[inx] ) == 0 ) { + key = inx+1; + mode = mode1; + break; + } + } + } + wAttachAccelKey( key, mode, func, context ); +} + +#include "bitmaps/zoomin.xpm" +#include "bitmaps/zoom.xpm" +#include "bitmaps/zoomout.xpm" +#include "bitmaps/edit-undo.xpm" +#include "bitmaps/edit-redo.xpm" +#include "bitmaps/partlist.xpm" +#include "bitmaps/export.xpm" +#include "bitmaps/import.xpm" +#include "bitmaps/document-new.xpm" +#include "bitmaps/document-save.xpm" +#include "bitmaps/document-open.xpm" +#include "bitmaps/document-print.xpm" + +static void CreateMenus( void ) +{ + wMenu_p fileM, editM, viewM, optionM, windowM, macroM, helpM, toolbarM, messageListM, manageM, addM, changeM, drawM; + wMenu_p zoomM, zoomSubM; +// wIcon_p bm_p; + + wMenuPush_p zoomInM, zoomOutM; + + fileM = wMenuBarAdd( mainW, "menuFile", _("&File") ); + editM = wMenuBarAdd( mainW, "menuEdit", _("&Edit") ); + viewM = wMenuBarAdd( mainW, "menuView", _("&View") ); + addM = wMenuBarAdd( mainW, "menuAdd", _("&Add") ); + changeM = wMenuBarAdd( mainW, "menuChange", _("&Change") ); + drawM = wMenuBarAdd( mainW, "menuDraw", _("&Draw") ); + manageM = wMenuBarAdd( mainW, "menuManage", _("&Manage") ); + optionM = wMenuBarAdd( mainW, "menuOption", _("&Options") ); + macroM = wMenuBarAdd( mainW, "menuMacro", _("&Macro") ); + windowM = wMenuBarAdd( mainW, "menuWindow", _("&Window") ); + helpM = wMenuBarAdd( mainW, "menuHelp", _("&Help") ); + + /* + * POPUP MENUS + */ + + popup1M = wMenuPopupCreate( mainW, _("Commands") ); + popup2M = wMenuPopupCreate( mainW, _("Commands") ); + MiscMenuItemCreate( popup1M, popup2M, "cmdUndo", _("Undo"), 0, (void*)(wMenuCallBack_p)UndoUndo, 0, (void *)0 ); + MiscMenuItemCreate( popup1M, popup2M, "cmdRedo", _("Redo"), 0, (void*)(wMenuCallBack_p)UndoRedo, 0, (void *)0 ); + wMenuPushCreate( popup1M, "cmdZoomIn", _("Zoom In"), 0, (wMenuCallBack_p)DoZoomUp, (void*)1 ); + wMenuPushCreate( popup2M, "cmdZoomIn", _("Zoom In"), 0, (wMenuCallBack_p)DoZoomUp, (void*)1 ); + wMenuPushCreate( popup1M, "cmdZoomOut", _("Zoom Out"), 0, (wMenuCallBack_p)DoZoomDown, (void*)1 ); + wMenuPushCreate( popup2M, "cmdZoomOut", _("Zoom Out"), 0, (wMenuCallBack_p)DoZoomDown, (void*)1 ); + MiscMenuItemCreate( popup1M, popup2M, "cmdGridEnable", _("SnapGrid Enable"), 0, (void*)(wMenuCallBack_p)SnapGridEnable, 0, (void *)0 ); + MiscMenuItemCreate( popup1M, popup2M, "cmdGridShow", _("SnapGrid Show"), 0, (void*)(wMenuCallBack_p)SnapGridShow, 0, (void *)0 ); + wMenuSeparatorCreate( popup1M ); + wMenuSeparatorCreate( popup2M ); + MiscMenuItemCreate( popup2M, NULL, "cmdCopy", _("Copy"), 0, (void*)(wMenuCallBack_p)EditCopy, 0, (void *)0 ); + MiscMenuItemCreate( popup1M, popup2M, "cmdPaste", _("Paste"), 0, (void*)(wMenuCallBack_p)EditPaste, 0, (void *)0 ); + MiscMenuItemCreate( popup2M, NULL, "cmdDeselectAll", _("Deselect All"), 0, (void*)(wMenuCallBack_p)SetAllTrackSelect, 0, (void *)0 ); + wMenuPushCreate( popup2M, "cmdMove", _("Move"), 0, (wMenuCallBack_p)DoCommandBIndirect, &moveCmdInx ); + wMenuPushCreate( popup2M, "cmdRotate", _("Rotate"), 0, (wMenuCallBack_p)DoCommandBIndirect, &rotateCmdInx ); + MiscMenuItemCreate( popup2M, NULL, "cmdTunnel", _("Tunnel"), 0, (void*)(wMenuCallBack_p)SelectTunnel, 0, (void *)0 ); + wMenuSeparatorCreate( popup1M ); + wMenuSeparatorCreate( popup2M ); + MiscMenuItemCreate( popup2M, NULL, "cmdDelete", _("Delete"), 0, (void*)(wMenuCallBack_p)SelectDelete, 0, (void *)0 ); + wMenuSeparatorCreate( popup2M ); + popup1aM = wMenuMenuCreate( popup1M, "", _("More") ); + popup2aM = wMenuMenuCreate( popup2M, "", _("More") ); + + cmdGroup = BG_FILE; + AddToolbarButton( "menuFile-clear", wIconCreatePixMap(document_new), IC_MODETRAIN_TOO, (addButtonCallBack_t)DoClear, NULL ); + AddToolbarButton( "menuFile-load", wIconCreatePixMap(document_open), IC_MODETRAIN_TOO, (addButtonCallBack_t)ChkLoad, NULL ); + AddToolbarButton( "menuFile-save", wIconCreatePixMap(document_save), IC_MODETRAIN_TOO, (addButtonCallBack_t)DoSave, NULL ); +// AddToolbarButton( "menuFile-print", wIconCreatePixMap(document_print_xpm), IC_MODETRAIN_TOO, (addButtonCallBack_t)DoPrint, NULL ); + + cmdGroup = BG_ZOOM; + zoomUpB = AddToolbarButton( "cmdZoomIn", wIconCreatePixMap(zoomin_xpm), IC_MODETRAIN_TOO, + (addButtonCallBack_t)DoZoomUp, NULL ); + + zoomM = wMenuPopupCreate( mainW, "" ); + AddToolbarButton( "cmdZoom", wIconCreatePixMap(zoom_xpm), IC_MODETRAIN_TOO, (wButtonCallBack_p)wMenuPopupShow, zoomM ); + + zoomDownB = AddToolbarButton( "cmdZoomOut", wIconCreatePixMap(zoomout_xpm), IC_MODETRAIN_TOO, + (addButtonCallBack_t)DoZoomDown, NULL ); + + cmdGroup = BG_UNDO; + undoB = AddToolbarButton( "cmdUndo", wIconCreatePixMap(edit_undo), 0, (addButtonCallBack_t)UndoUndo, NULL ); + redoB = AddToolbarButton( "cmdRedo", wIconCreatePixMap(edit_redo), 0, (addButtonCallBack_t)UndoRedo, NULL ); + + wControlActive( (wControl_p)undoB, FALSE ); + wControlActive( (wControl_p)redoB, FALSE ); + + + /* + * FILE MENU + */ + MiscMenuItemCreate( fileM, NULL, "menuFile-clear", _("&New"), ACCL_NEW, (void*)(wMenuCallBack_p)DoClear, 0, (void *)0 ); + wMenuPushCreate( fileM, "menuFile-load", _("&Open ..."), ACCL_OPEN, (wMenuCallBack_p)ChkLoad, NULL ); + wMenuSeparatorCreate( fileM ); + + wMenuPushCreate( fileM, "menuFile-save", _("&Save"), ACCL_SAVE, (wMenuCallBack_p)DoSave, NULL ); + wMenuPushCreate( fileM, "menuFile-saveAs", _("Save &As ..."), ACCL_SAVEAS, (wMenuCallBack_p)DoSaveAs, NULL ); + wMenuPushCreate( fileM, "menuFile-revert", _("Revert"), ACCL_REVERT, (wMenuCallBack_p)ChkRevert, NULL ); + wMenuSeparatorCreate( fileM ); + MiscMenuItemCreate( fileM, NULL, "printSetup", _("P&rint Setup ..."), ACCL_PRINTSETUP, (void*)(wMenuCallBack_p)wPrintSetup, 0, (void *)0 ); + printCmdInx = InitCmdPrint( fileM ); + wMenuSeparatorCreate( fileM ); + MiscMenuItemCreate( fileM, NULL, "cmdImport", _("&Import"), ACCL_IMPORT, (void*)(wMenuCallBack_p)DoImport, 0, (void *)0 ); + MiscMenuItemCreate( fileM, NULL, "cmdOutputbitmap", _("Export to &Bitmap"), ACCL_PRINTBM, (void*)(wMenuCallBack_p)OutputBitMapInit(), 0, (void *)0 ); + MiscMenuItemCreate( fileM, NULL, "cmdExport", _("E&xport"), ACCL_EXPORT, (void*)(wMenuCallBack_p)DoExport, IC_SELECTED, (void *)0 ); + MiscMenuItemCreate( fileM, NULL, "cmdExportDXF", _("Export D&XF"), ACCL_EXPORTDXF, (void*)(wMenuCallBack_p)DoExportDXF, IC_SELECTED, (void *)0 ); + wMenuSeparatorCreate( fileM ); + + MiscMenuItemCreate( fileM, NULL, "cmdPrmfile", _("Parameter &Files ..."), ACCL_PARAMFILES, (void*)ParamFilesInit(), 0, (void *)0 ); + MiscMenuItemCreate( fileM, NULL, "cmdFileNote", _("No&tes ..."), ACCL_NOTES, (void*)(wMenuCallBack_p)DoNote, 0, (void *)0 ); + + wMenuSeparatorCreate( fileM ); + fileList_ml = wMenuListCreate( fileM, "menuFileList", NUM_FILELIST, ChkFileList ); + wMenuSeparatorCreate( fileM ); + wMenuPushCreate( fileM, "menuFile-quit", _("E&xit"), 0, + (wMenuCallBack_p)DoQuit, NULL ); + + /* + * EDIT MENU + */ + MiscMenuItemCreate( editM, NULL, "cmdUndo", _("&Undo"), ACCL_UNDO, (void*)(wMenuCallBack_p)UndoUndo, 0, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdRedo", _("R&edo"), ACCL_REDO, (void*)(wMenuCallBack_p)UndoRedo, 0, (void *)0 ); + wMenuSeparatorCreate( editM ); + MiscMenuItemCreate( editM, NULL, "cmdCut", _("Cu&t"), ACCL_CUT, (void*)(wMenuCallBack_p)EditCut, IC_SELECTED, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdCopy", _("&Copy"), ACCL_COPY, (void*)(wMenuCallBack_p)EditCopy, IC_SELECTED, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdPaste", _("&Paste"), ACCL_PASTE, (void*)(wMenuCallBack_p)EditPaste, 0, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdDelete", _("De&lete"), ACCL_DELETE, (void*)(wMenuCallBack_p)SelectDelete, IC_SELECTED, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdMoveToCurrentLayer", _("Move To Current Layer"), ACCL_MOVCURLAYER, (void*)(wMenuCallBack_p)MoveSelectedTracksToCurrentLayer, IC_SELECTED, (void *)0 ); + + + wMenuSeparatorCreate( editM ); + menuPLs[menuPG.paramCnt].context = (void*)1; + MiscMenuItemCreate( editM, NULL, "cmdSelectAll", _("Select &All"), ACCL_SELECTALL, (void*)(wMenuCallBack_p)SetAllTrackSelect, 0, (void *)1 ); + MiscMenuItemCreate( editM, NULL, "cmdSelectCurrentLayer", _("Select Current Layer"), ACCL_SETCURLAYER, (void*)(wMenuCallBack_p)SelectCurrentLayer, 0, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdDeselectAll", _("&Deselect All"), ACCL_DESELECTALL, (void*)(wMenuCallBack_p)SetAllTrackSelect, 0, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdSelectInvert", _("&Invert Selection"), 0L, (void*)(wMenuCallBack_p)InvertTrackSelect, 0, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdSelectOrphaned", _("Select Stranded Track"), 0L, (void*)(wMenuCallBack_p)OrphanedTrackSelect, 0, (void *)0 ); + wMenuSeparatorCreate( editM ); + MiscMenuItemCreate( editM, NULL, "cmdTunnel", _("Tu&nnel"), ACCL_TUNNEL, (void*)(wMenuCallBack_p)SelectTunnel, IC_SELECTED, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdAbove", _("A&bove"), ACCL_ABOVE, (void*)(wMenuCallBack_p)SelectAbove, IC_SELECTED, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdBelow", _("Belo&w"), ACCL_BELOW, (void*)(wMenuCallBack_p)SelectBelow, IC_SELECTED, (void *)0 ); + + wMenuSeparatorCreate( editM ); + MiscMenuItemCreate( editM, NULL, "cmdWidth0", _("Thin Tracks"), ACCL_THIN, (void*)(wMenuCallBack_p)SelectTrackWidth, IC_SELECTED, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdWidth2", _("Medium Tracks"), ACCL_MEDIUM, (void*)(wMenuCallBack_p)SelectTrackWidth, IC_SELECTED, (void *)2 ); + MiscMenuItemCreate( editM, NULL, "cmdWidth3", _("Thick Tracks"), ACCL_THICK, (void*)(wMenuCallBack_p)SelectTrackWidth, IC_SELECTED, (void *)3 ); + + /* + * VIEW MENU + */ + zoomInM = wMenuPushCreate( viewM, "menuEdit-zoomIn", _("Zoom &In"), ACCL_ZOOMIN, (wMenuCallBack_p)DoZoomUp, (void*)1 ); + zoomSubM = wMenuMenuCreate( viewM, "menuEdit-zoomTo", _("&Zoom") ); + zoomOutM = wMenuPushCreate( viewM, "menuEdit-zoomOut", _("Zoom &Out"), ACCL_ZOOMOUT, (wMenuCallBack_p)DoZoomDown, (void*)1 ); + wMenuSeparatorCreate( viewM ); + + InitCmdZoom( zoomM, zoomSubM ); + + /* these menu choices and toolbar buttons are synonymous and should be treated as such */ + wControlLinkedSet( (wControl_p)zoomInM, (wControl_p)zoomUpB ); + wControlLinkedSet( (wControl_p)zoomOutM, (wControl_p)zoomDownB ); + + wMenuPushCreate( viewM, "menuEdit-redraw", _("&Redraw"), ACCL_REDRAW, (wMenuCallBack_p)MainRedraw, NULL ); + wMenuPushCreate( viewM, "menuEdit-redraw", _("Redraw All"), ACCL_REDRAWALL, (wMenuCallBack_p)DoRedraw, NULL ); + wMenuSeparatorCreate( viewM ); + + snapGridEnableMI = wMenuToggleCreate( viewM, "cmdGridEnable", _("Enable SnapGrid"), ACCL_SNAPENABLE, + 0, (wMenuToggleCallBack_p)SnapGridEnable, NULL ); + snapGridShowMI = wMenuToggleCreate( viewM, "cmdGridShow", _("Show SnapGrid"), ACCL_SNAPSHOW, + FALSE, (wMenuToggleCallBack_p)SnapGridShow, NULL ); + gridCmdInx = InitGrid( viewM ); + wMenuSeparatorCreate( viewM ); + + toolbarM = wMenuMenuCreate( viewM, "toolbarM", _("&Tool Bar") ); + CreateToolbarM( toolbarM ); + + cmdGroup = BG_EASE; + InitCmdEasement(); + + cmdGroup = BG_SNAP; + InitSnapGridButtons(); + + /* + * ADD MENU + */ + + cmdGroup = BG_TRKCRT|BG_BIGGAP; + InitCmdStraight( addM ); + InitCmdCurve( addM ); + InitCmdParallel( addM ); + InitCmdTurnout( addM ); + InitCmdHandLaidTurnout( addM ); + InitCmdStruct( addM ); + InitCmdHelix( addM ); + InitCmdTurntable( addM ); + +#ifdef XTRKCAD_USE_LAYOUTCONTROL + cmdGroup = BG_CONTROL; + InitCmdBlock( addM ); + InitCmdSwitchMotor( addM ); +#endif + + /* + * CHANGE MENU + */ + cmdGroup = BG_SELECT; + InitCmdDescribe( changeM ); + InitCmdSelect( changeM ); + wMenuSeparatorCreate( changeM ); + + cmdGroup = BG_TRKGRP; + InitCmdMove( changeM ); + InitCmdDelete(); + InitCmdTunnel(); + InitCmdAboveBelow(); + + cmdGroup = BG_TRKMOD; + if (extraButtons) + MiscMenuItemCreate( changeM, NULL, "loosen", _("&Loosen Tracks"), ACCL_LOOSEN, (void*)(wMenuCallBack_p)LoosenTracks, IC_SELECTED, (void *)0 ); + + InitCmdModify( changeM ); + InitCmdJoin( changeM ); + InitCmdPull( changeM ); + InitCmdSplit( changeM ); + InitCmdMoveDescription( changeM ); + wMenuSeparatorCreate( changeM ); + + MiscMenuItemCreate( changeM, NULL, "cmdAddElevations", _("Raise/Lower Elevations"), ACCL_CHGELEV, (void*)(wMenuCallBack_p)ShowAddElevations, IC_SELECTED, (void *)0 ); + InitCmdElevation( changeM ); + InitCmdProfile( changeM ); + + MiscMenuItemCreate( changeM, NULL, "cmdClearElevations", _("Clear Elevations"), ACCL_CLRELEV, (void*)(wMenuCallBack_p)ClearElevations, IC_SELECTED, (void *)0 ); + MiscMenuItemCreate( changeM, NULL, "cmdElevation", _("Recompute Elevations"), 0, (void*)(wMenuCallBack_p)RecomputeElevations, 0, (void *)0 ); + ParamRegister( &addElevPG ); + + wMenuSeparatorCreate( changeM ); + MiscMenuItemCreate( changeM, NULL, "cmdRescale", _("Change Scale"), 0, (void*)(wMenuCallBack_p)DoRescale, IC_SELECTED, (void *)0 ); + + /* + * DRAW MENU + */ + cmdGroup = BG_MISCCRT; + InitCmdDraw( drawM ); + InitCmdText( drawM ); + InitCmdNote( drawM ); + + cmdGroup = BG_RULER; + InitCmdRuler( drawM ); + + + /* + * OPTION MENU + */ + MiscMenuItemCreate( optionM, NULL, "cmdLayout", _("L&ayout ..."), ACCL_LAYOUTW, (void*)LayoutInit(), IC_MODETRAIN_TOO, (void *)0 ); + MiscMenuItemCreate( optionM, NULL, "cmdDisplay", _("&Display ..."), ACCL_DISPLAYW, (void*)DisplayInit(), IC_MODETRAIN_TOO, (void *)0 ); + MiscMenuItemCreate( optionM, NULL, "cmdCmdopt", _("Co&mmand ..."), ACCL_CMDOPTW, (void*)CmdoptInit(), IC_MODETRAIN_TOO, (void *)0 ); + MiscMenuItemCreate( optionM, NULL, "cmdEasement", _("&Easements ..."), ACCL_EASEW, (void*)(wMenuCallBack_p)DoEasementRedir, IC_MODETRAIN_TOO, (void *)0 ); + MiscMenuItemCreate( optionM, NULL, "fontSelW", _("&Fonts ..."), ACCL_FONTW, (void*)(wMenuCallBack_p)SelectFont, IC_MODETRAIN_TOO, (void *)0 ); + MiscMenuItemCreate( optionM, NULL, "cmdSticky", _("Stic&ky ..."), ACCL_STICKY, (void*)(wMenuCallBack_p)DoSticky, IC_MODETRAIN_TOO, (void *)0 ); + if (extraButtons) { + menuPLs[menuPG.paramCnt].context = debugW; + MiscMenuItemCreate( optionM, NULL, "cmdDebug", _("&Debug ..."), 0, (void*)(wMenuCallBack_p)wShow, IC_MODETRAIN_TOO, (void *)0 ); + } + MiscMenuItemCreate( optionM, NULL, "cmdPref", _("&Preferences ..."), ACCL_PREFERENCES, (void*)PrefInit(), IC_MODETRAIN_TOO, (void *)0 ); + MiscMenuItemCreate( optionM, NULL, "cmdColor", _("&Colors ..."), ACCL_COLORW, (void*)ColorInit(), IC_MODETRAIN_TOO, (void *)0 ); + + /* + * MACRO MENU + */ + wMenuPushCreate( macroM, "cmdRecord", _("&Record ..."), ACCL_RECORD, DoRecord, NULL ); + wMenuPushCreate( macroM, "cmdDemo", _("&Play Back ..."), ACCL_PLAYBACK, DoPlayBack, NULL ); + + + /* + * WINDOW MENU + */ + wMenuPushCreate( windowM, "menuWindow", _("Main window"), 0, (wMenuCallBack_p)wShow, mainW ); + winList_mi = wMenuListCreate( windowM, "menuWindow", -1, DoShowWindow ); + + /* + * HELP MENU + */ + + /* main help window */ + wMenuAddHelp( helpM ); + + /* help on recent messages */ + wMenuSeparatorCreate( helpM ); + messageListM = wMenuMenuCreate( helpM, "menuHelpRecentMessages", _("Recent Messages") ); + messageList_ml = wMenuListCreate( messageListM, "messageListM", 10, ShowMessageHelp ); + wMenuListAdd( messageList_ml, 0, _(MESSAGE_LIST_EMPTY), NULL ); + + /* tip of the day */ + wMenuSeparatorCreate( helpM ); + wMenuPushCreate( helpM, "cmdTip", _("Tip of the Day..."), 0, (wMenuCallBack_p)ShowTip, (void *)(SHOWTIP_FORCESHOW | SHOWTIP_NEXTTIP)); + demoM = wMenuMenuCreate( helpM, "cmdDemo", _("&Demos") ); + + /* about window */ + wMenuSeparatorCreate( helpM ); + wMenuPushCreate( helpM, "about", _("About"), 0, (wMenuCallBack_p)CreateAboutW, NULL ); + + /* + * MANAGE MENU + */ + + cmdGroup = BG_TRAIN|BG_BIGGAP; + InitCmdTrain( manageM ); + wMenuSeparatorCreate( manageM ); + + InitNewTurn( wMenuMenuCreate( manageM, "cmdTurnoutNew", _("Tur&nout Designer...") ) ); + + MiscMenuItemCreate( manageM, NULL, "cmdGroup", _("&Group"), ACCL_GROUP, (void*)(wMenuCallBack_p)DoGroup, IC_SELECTED, (void *)0 ); + MiscMenuItemCreate( manageM, NULL, "cmdUngroup", _("&Ungroup"), ACCL_UNGROUP, (void*)(wMenuCallBack_p)DoUngroup, IC_SELECTED, (void *)0 ); + + MiscMenuItemCreate( manageM, NULL, "cmdCustmgm", _("Custom defined parts..."), ACCL_CUSTMGM, (void*)CustomMgrInit(), 0, (void *)0 ); + MiscMenuItemCreate( manageM, NULL, "cmdRefreshCompound", _("Update Turnouts and Structures"), 0, (void*)(wMenuCallBack_p)DoRefreshCompound, 0, (void *)0 ); + + MiscMenuItemCreate( manageM, NULL, "cmdCarInventory", _("Car Inventory"), ACCL_CARINV, (void*)(wMenuCallBack_p)DoCarDlg, IC_MODETRAIN_TOO, (void *)0 ); + + wMenuSeparatorCreate( manageM ); + + MiscMenuItemCreate( manageM, NULL, "cmdLayer", _("Layers ..."), ACCL_LAYERS, (void*)InitLayersDialog(), 0, (void *)0 ); + wMenuSeparatorCreate( manageM ); + + MiscMenuItemCreate( manageM, NULL, "cmdEnumerate", _("Parts &List ..."), ACCL_PARTSLIST, (void*)(wMenuCallBack_p)EnumerateTracks, 0, (void *)0 ); + MiscMenuItemCreate( manageM, NULL, "cmdPricelist", _("Price List..."), ACCL_PRICELIST, (void*)PriceListInit(), 0, (void *)0 ); + + cmdGroup = BG_LAYER|BG_BIGGAP; + InitLayers(); + + cmdGroup = BG_HOTBAR; + InitHotBar(); + +#ifdef LATER +#ifdef WINDOWS + wAttachAccelKey( wAccelKey_Pgdn, 0, (wAccelKeyCallBack_p)DoZoomUp, (void*)1 ); + wAttachAccelKey( wAccelKey_Pgup, 0, (wAccelKeyCallBack_p)DoZoomDown, (void*)1 ); + wAttachAccelKey( wAccelKey_F5, 0, (wAccelKeyCallBack_p)MainRedraw, (void*)1 ); +#endif + wAttachAccelKey( wAccelKey_Ins, WKEY_CTRL, (wAccelKeyCallBack_p)EditCopy, 0 ); + wAttachAccelKey( wAccelKey_Ins, WKEY_SHIFT, (wAccelKeyCallBack_p)EditPaste, 0 ); + wAttachAccelKey( wAccelKey_Back, WKEY_SHIFT, (wAccelKeyCallBack_p)UndoUndo, 0 ); + wAttachAccelKey( wAccelKey_Del, WKEY_SHIFT, (wAccelKeyCallBack_p)EditCut, 0 ); + wAttachAccelKey( wAccelKey_F6, 0, (wAccelKeyCallBack_p)NextWindow, 0 ); +#endif + SetAccelKey( "zoomUp", wAccelKey_Pgdn, 0, (wAccelKeyCallBack_p)DoZoomUp, (void*)1 ); + SetAccelKey( "zoomDown", wAccelKey_Pgup, 0, (wAccelKeyCallBack_p)DoZoomDown, (void*)1 ); + SetAccelKey( "redraw", wAccelKey_F5, 0, (wAccelKeyCallBack_p)MainRedraw, (void*)1 ); + SetAccelKey( "delete", wAccelKey_Del, 0, (wAccelKeyCallBack_p)SelectDelete, (void*)1 ); + SetAccelKey( "copy", wAccelKey_Ins, WKEY_CTRL, (wAccelKeyCallBack_p)EditCopy, 0 ); + SetAccelKey( "paste", wAccelKey_Ins, WKEY_SHIFT, (wAccelKeyCallBack_p)EditPaste, 0 ); + SetAccelKey( "undo", wAccelKey_Back, WKEY_SHIFT, (wAccelKeyCallBack_p)UndoUndo, 0 ); + SetAccelKey( "cut", wAccelKey_Del, WKEY_SHIFT, (wAccelKeyCallBack_p)EditCut, 0 ); + SetAccelKey( "nextWindow", wAccelKey_F6, 0, (wAccelKeyCallBack_p)NextWindow, 0 ); + + InitBenchDialog(); +} + + +static void LoadFileList( void ) +{ + char file[6]; + int inx; + const char * cp; + const char *fileName, *pathName; + strcpy( file, "fileX" ); + for (inx=NUM_FILELIST-1; inx>=0; inx--) { + file[4] = '0'+inx; + cp = wPrefGetString( "filelist", file ); + if (!cp) + continue; + pathName = MyStrdup(cp); + fileName = strrchr( pathName, FILE_SEP_CHAR[0] ); + if (fileName) + wMenuListAdd( fileList_ml, 0, fileName+1, pathName ); + } +} + +EXPORT void InitCmdEnumerate( void ) +{ + AddToolbarButton( "cmdEnumerate", wIconCreatePixMap(partlist_xpm), IC_SELECTED|IC_ACCLKEY, (addButtonCallBack_t)EnumerateTracks, NULL ); +} + + +EXPORT void InitCmdExport( void ) +{ + AddToolbarButton( "cmdExport", wIconCreatePixMap(export_xpm), IC_SELECTED|IC_ACCLKEY, (addButtonCallBack_t)DoExport, NULL ); + AddToolbarButton( "cmdImport", wIconCreatePixMap(import_xpm), IC_ACCLKEY, (addButtonCallBack_t)DoImport, NULL ); +} + +/* Give user the option to continue work after crash. This function gives the user + * the option to load the checkpoint file to continue working after a crash. + * + * \param none + * \return none + * + */ + +static void OfferCheckpoint( void ) +{ + int ret; + + /* sProdName */ + ret = wNoticeEx( NT_INFORMATION, + _("Program was not terminated properly. Do you want to resume working on the previous trackplan?"), + _("Resume"), _("Ignore") ); + if( ret ) { + /* load the checkpoint file */ + LoadCheckpoint(); + } + +} + + +EXPORT wWin_p wMain( + int argc, + char * argv[] ) +{ + int c; + char * logFileName = NULL; + int log_init = 0; + int initialZoom = 0; + char * initialFile = NULL; + const char * pref; + coOrd roomSize; + long oldToolbarMax; + long newToolbarMax; + char *cp; + char *oldLocale = NULL; + char buffer[ STR_SIZE ]; + unsigned int i; + + strcpy( buffer, sProdNameLower ); + + /* Initialize application name */ + wInitAppName(buffer); + + /* Initialize gettext */ + InitGettext(); + + /* Save user locale */ + oldLocale = setlocale(LC_ALL, NULL); + if (oldLocale) + userLocale = strdup( oldLocale ); + + /* + * ARGUMENTS + */ + + opterr = 0; + + while ((c = getopt (argc, argv, "vl:d:c:")) != -1) + switch (c) { + case 'c': /* configuration name */ + /* test for valid filename */ + for( i = 0; i < strlen( optarg ); i++ ) { + if( !isalnum( optarg[ i ]) && optarg[ i ] != '.' ) { + NoticeMessage( MSG_BAD_OPTION, _("Ok"), NULL, optarg ); + exit( 1 ); + } + } + /* append delimiter and argument to configuration name */ + if( strlen( optarg ) < STR_SIZE - strlen( ";" ) - strlen( buffer ) - 1 ){ + strcat( buffer, ";" ); + strcat( buffer, optarg ); + } + else { + NoticeMessage( MSG_BAD_OPTION, _("Ok"), NULL, optarg ); + exit( 1 ); + } + break; + case 'v': /* verbose flag */ + verbose++; + break; + case 'd': /* define loglevel for a group */ + cp = strchr( optarg, '=' ); + if ( cp != NULL ) { + *cp++ = '\0'; + LogSet( optarg, atoi(cp) ); + } else { + LogSet( optarg, 1 ); + } + break; + case 'l': /* define log filename */ + logFileName = strdup(optarg); + break; + case '?': + NoticeMessage( MSG_BAD_OPTION, _("Ok"), NULL, argv[ optind - 1 ] ); + exit( 1 ); + case ':': + NoticeMessage( "Missing parameter for %s", _("Ok"), NULL, argv[ optind - 1 ] ); + exit( 1 ); + break; + default: + abort (); + } + if( optind < argc ) + initialFile = strdup( argv[ optind ] ); + + extraButtons = ( getenv(sEnvExtra) != NULL ); + LogOpen( logFileName ); + log_init = LogFindIndex( "init" ); + log_malloc = LogFindIndex( "malloc" ); + log_error = LogFindIndex( "error" ); + log_command = LogFindIndex( "command" ); + +LOG1( log_init, ( "initCustom\n" ) ) + InitCustom(); + + /* + * MAIN WINDOW + */ +LOG1( log_init, ( "create main window\n" ) ) + strcpy( Title1, sProdName ); + sprintf( message, _("Unnamed Trackplan - %s(%s)"), sProdName, sVersion ); + wSetBalloonHelp( balloonHelp ); + mainW = wWinMainCreate( buffer, 600, 350, "xtrkcadW", message, "main", + F_RESIZE|F_MENUBAR|F_NOTAB|F_RECALLPOS|F_HIDE, + MainProc, NULL ); + if ( mainW == NULL ) + return NULL; + + drawColorBlack = wDrawFindColor( wRGB( 0, 0, 0) ); + drawColorWhite = wDrawFindColor( wRGB(255,255,255) ); + drawColorRed = wDrawFindColor( wRGB(255, 0, 0) ); + drawColorBlue = wDrawFindColor( wRGB( 0, 0,255) ); + drawColorGreen = wDrawFindColor( wRGB( 0,255, 0) ); + drawColorAqua = wDrawFindColor( wRGB( 0,255,255) ); + drawColorPurple = wDrawFindColor( wRGB(255, 0,255) ); + drawColorGold = wDrawFindColor( wRGB(255,215, 0) ); + snapGridColor = drawColorGreen; + markerColor = drawColorRed; + borderColor = drawColorBlack; + crossMajorColor = drawColorRed; + crossMinorColor = drawColorBlue; + selectedColor = drawColorRed; + normalColor = drawColorBlack; + elevColorIgnore = drawColorBlue; + elevColorDefined = drawColorGold; + profilePathColor = drawColorPurple; + exceptionColor = wDrawFindColor( wRGB(255,0,128) ); + tieColor = wDrawFindColor( wRGB(255,128,0) ); + + newToolbarMax = (1<<BG_COUNT)-1; + wPrefGetInteger( "misc", "toolbarset", &toolbarSet, newToolbarMax ); + wPrefGetInteger( "misc", "max-toolbarset", &oldToolbarMax, 0 ); + toolbarSet |= newToolbarMax & ~oldToolbarMax; + wPrefSetInteger( "misc", "max-toolbarset", newToolbarMax ); + wPrefSetInteger( "misc", "toolbarset", toolbarSet ); + +LOG1( log_init, ( "fontInit\n")) + + wInitializeFonts(); + +LOG1( log_init, ( "fileInit\n" ) ) + FileInit(); + + wCreateSplash( sProdName, sVersion ); + + if (!initialFile) { + WDOUBLE_T tmp; +LOG1( log_init, ( "set roomsize\n" ) ) + wPrefGetFloat( "draw", "roomsizeX", &tmp, 96.0 ); + roomSize.x = tmp; + wPrefGetFloat( "draw", "roomsizeY", &tmp, 48.0 ); + roomSize.y = tmp; + SetRoomSize( roomSize ); + } + + /* + * INITIALIZE + */ +LOG1( log_init, ( "initInfoBar\n" ) ) + InitInfoBar(); + wSetSplashInfo( "Misc2 Init..." ); +LOG1( log_init, ( "misc2Init\n" ) ) + Misc2Init(); + + RotateDialogInit(); + + wSetSplashInfo( _("Initializing commands") ); +LOG1( log_init, ( "paramInit\n" ) ) + ParamInit(); +LOG1( log_init, ( "initTrkTrack\n" ) ) + InitTrkTrack(); + + /* + * MENUS + */ + wSetSplashInfo( _("Initializing menus") ); +LOG1( log_init, ( "createMenus\n" ) ) + CreateMenus(); + + + +LOG1( log_init, ( "initialize\n" ) ) + if (!Initialize()) + return NULL; + ParamRegister( &menuPG ); + ParamRegister( &stickyPG ); + + /* initialize the layers */ + DefaultLayerProperties(); +LOG1( log_init, ( "loadFileList\n" ) ) + LoadFileList(); + AddPlaybackProc( "MENU", MenuPlayback, NULL ); + CreateDebugW(); + + /* + * TIDY UP + */ + curCommand = 0; + commandContext = commandList[curCommand].context; + + /* + * READ PARAMETERS + */ + if (toolbarSet&(1<<BG_HOTBAR)) { + LayoutHotBar(); + } else { + HideHotBar(); + } +LOG1( log_init, ( "drawInit\n" ) ) + DrawInit( initialZoom ); + + MacroInit(); + wSetSplashInfo( _("Reading parameter files") ); +LOG1( log_init, ( "paramFileInit\n" ) ) + if (!ParamFileInit()) + return NULL; + + curCommand = describeCmdInx; +LOG1( log_init, ( "Reset\n" ) ) + Reset(); + + /* + * SCALE + */ + + /* Set up the data for scale and gauge description */ + DoSetScaleDesc(); + + pref = wPrefGetString( "misc", "scale" ); + DoSetScale( pref ); + + /* see whether last layout should be reopened on startup */ + wPrefGetInteger( "DialogItem", "pref-onstartup", &onStartup, 0 ); + + /* + * THE END + */ + +LOG1( log_init, ( "the end\n" ) ) + EnableCommands(); +LOG1( log_init, ( "Initialization complete\n" ) ) + wSetSplashInfo( _("Initialization complete") ); + RegisterChangeNotification( ToolbarChange ); + DoChangeNotification( CHANGE_MAIN|CHANGE_MAP ); + + wWinShow( mainW, TRUE ); + mapVisible = TRUE; + wShow( mapW ); + wDestroySplash(); + + /* this has to be called before ShowTip() */ + InitSmallDlg(); + + ShowTip(SHOWTIP_NEXTTIP); + + /* if work is to be resumed and no filename was given on startup, load last layout */ + if( (onStartup == 0) && (!initialFile || !strlen(initialFile))) { + initialFile = (char*)wPrefGetString( "misc", "lastlayout" ); + } + + if (initialFile && strlen(initialFile)) { + DoFileList( 0, NULL, initialFile ); + } + + /* check for existing checkpoint file */ + if (ExistsCheckpoint()) + OfferCheckpoint(); + + inMainW = FALSE; + return mainW; +} + diff --git a/app/bin/misc.h b/app/bin/misc.h new file mode 100644 index 0000000..22e8f5a --- /dev/null +++ b/app/bin/misc.h @@ -0,0 +1,392 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/misc.h,v 1.8 2009-09-05 16:40:53 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 MISC_H +#define MISC_H + +#define EXPORT + +#include "acclkeys.h" + +typedef void (*addButtonCallBack_t)(void*); + +#include "custom.h" + +#ifdef WINDOWS +#define FILE_SEP_CHAR "\\" +/* suppress warning from *.bmp about conversion of int to char */ +#pragma warning( disable : 4305) +#else +#define FILE_SEP_CHAR "/" +#endif + +#define COUNT(A) (sizeof(A)/sizeof(A[0])) + +#define STR_SIZE (256) +#define STR_SHORT_SIZE (80) +#define STR_LONG_SIZE (1024) + +#define CAST_AWAY_CONST (char*) + +#define TITLEMAXLEN (40) + +/* + * Globals + */ + +extern long adjTimer; + +typedef int SCALEINX_T; +typedef int GAUGEINX_T; +typedef int SCALEDESCINX_T; + +extern int log_error; + +extern long toolbarSet; +extern ANGLE_T turntableAngle; +extern long maxCouplingSpeed; +extern long hideSelectionWindow; +extern long labelWhen; +extern long labelScale; +extern long labelEnable; +extern long colorLayers; +extern long carHotbarModeInx; +extern DIST_T minLength; +extern DIST_T connectDistance; +extern ANGLE_T connectAngle; +extern long twoRailScale; +extern long mapScale; +extern char Title1[40]; +extern char Title2[40]; +extern long checkPtInterval; +extern long liveMap; +extern long preSelect; +extern long hideTrainsInTunnels; +extern long listLabels; +extern long layoutLabels; +extern long descriptionFontSize; +extern long units; +extern long onStartup; +extern long angleSystem; +extern SCALEINX_T curScaleInx; +extern GAUGEINX_T curGaugeInx; +extern SCALEDESCINX_T curScaleDescInx; +extern DIST_T trackGauge; +extern DIST_T curScaleRatio; +extern char * curScaleName; +extern int enumerateMaxDescLen; +extern long enableBalloonHelp; +extern long hotBarLabels; +extern long rightClickMode; +extern void * commandContext; +extern coOrd cmdMenuPos; +#define MODE_DESIGN (0) +#define MODE_TRAIN (1) +extern long programMode; +#define DISTFMT_DECS 0x00FF +#define DISTFMT_FMT 0x0300 +#define DISTFMT_FMT_NONE 0x0000 +#define DISTFMT_FMT_SHRT 0x0100 +#define DISTFMT_FMT_LONG 0x0200 +#define DISTFMT_FMT_MM 0x0100 +#define DISTFMT_FMT_CM 0x0200 +#define DISTFMT_FMT_M 0x0300 +#define DISTFMT_FRACT 0x0400 +#define DISTFMT_FRACT_NUM 0x0000 +#define DISTFMT_FRACT_FRC 0x0400 + +#define UNITS_ENGLISH (0) +#define UNITS_METRIC (1) +#define GetDim(X) ((units==UNITS_METRIC)?(X)/2.54:(X)) +#define PutDim(X) ((units==UNITS_METRIC)?(X)*2.54:(X)) +#define ANGLE_POLAR (0) +#define ANGLE_CART (1) +#define GetAngle(X) ((angleSystem==ANGLE_POLAR)?(X):NormalizeAngle(90.0-(X))) +#define PutAngle(X) ((angleSystem==ANGLE_POLAR)?(X):NormalizeAngle(90.0-(X))) +#define LABELENABLE_TRKDESC (1<<0) +#define LABELENABLE_LENGTHS (1<<1) +#define LABELENABLE_ENDPT_ELEV (1<<2) +#define LABELENABLE_TRACK_ELEV (1<<3) +#define LABELENABLE_CARS (1<<4) + +/* + * Command Action + */ +#define C_DOWN wActionLDown +#define C_MOVE wActionLDrag +#define C_UP wActionLUp +#define C_RDOWN wActionRDown +#define C_RMOVE wActionRDrag +#define C_RUP wActionRUp +#define C_TEXT wActionText +#define C_WUP wActionWheelUp +#define C_WDOWN wActionWheelDown +#define C_INIT (wActionLast+1) +#define C_START (wActionLast+2) +#define C_REDRAW (wActionLast+3) +#define C_CANCEL (wActionLast+4) +#define C_OK (wActionLast+5) +#define C_CONFIRM (wActionLast+6) +#define C_LCLICK (wActionLast+7) +#define C_RCLICK (wActionLast+8) +#define C_CMDMENU (wActionLast+9) +#define C_FINISH (wActionLast+10) + +#define C_CONTINUE (100) +#define C_TERMINATE (101) +#define C_INFO (102) +#define C_ERROR (103) + +/* + * Commands + */ +#define LEVEL0 (0) +#define LEVEL0_50 (1) +#define LEVEL1 (2) +#define LEVEL2 (3) + +typedef STATUS_T (*procCommand_t) (wAction_t, coOrd); + +/* + * Windows and buttons + */ +extern wPos_t DlgSepLeft; +extern wPos_t DlgSepMid; +extern wPos_t DlgSepRight; +extern wPos_t DlgSepTop; +extern wPos_t DlgSepBottom; +extern wPos_t DlgSepNarrow; +extern wPos_t DlgSepWide; +extern wPos_t DlgSepFrmLeft; +extern wPos_t DlgSepFrmRight; +extern wPos_t DlgSepFrmTop; +extern wPos_t DlgSepFrmBottom; + +extern wWin_p mainW; +extern wPos_t toolbarHeight; +extern wIndex_t changed; +extern char FAR message[STR_LONG_SIZE]; +extern REGION_T curRegion; +extern long paramVersion; +extern coOrd zero; +extern wBool_t extraButtons; +extern wButton_p undoB; +extern wButton_p redoB; +extern wButton_p zoomUpB; /** ZoomUp button on toolbar */ +extern wButton_p zoomDownB; /** ZoomDown button on toolbar */ +// extern wButton_p easementB; +extern wIndex_t checkPtMark; +extern wMenu_p demoM; +extern wMenu_p popup1M, popup2M; + +#define wControlBelow( B ) (wControlGetPosY((wControl_p)(B))+wControlGetHeight((wControl_p)(B))) +#define wControlBeside( B ) (wControlGetPosX((wControl_p)(B))+wControlGetWidth((wControl_p)(B))) + +typedef void (*rotateDialogCallBack_t) ( void * ); +extern void AddRotateMenu( wMenu_p, rotateDialogCallBack_t ); +extern void StartRotateDialog( rotateDialogCallBack_t ); +/* + * Safe Memory etc + */ +void * MyMalloc( long ); +void * MyRealloc( void *, long ); +void MyFree( void * ); +void * memdup( void *, size_t ); +char * MyStrdup( const char * ); +void AbortProg( char *, ... ); +#define ASSERT( X ) if ( !(X) ) AbortProg( "%s: %s:%d", #X, __FILE__, __LINE__ ) +char * Strcpytrimed( char *, char *, BOOL_T ); +char * BuildTrimedTitle( char *, char *, char *, char *, char * ); +void ErrorMessage( char *, ... ); +void InfoMessage( char *, ... ); +int NoticeMessage( char *, char*, char *, ... ); +int NoticeMessage2( int, char *, char*, char *, ... ); +void DoQuit( void ); + +void wShow( wWin_p ); +void wHide( wWin_p ); +void CloseDemoWindows( void ); +void DefaultProc( wWin_p, winProcEvent, void * ); +void SelectFont(); + +void CheckRoomSize( BOOL_T ); +const char * GetBalloonHelpStr( char* ); +void EnableCommands( void ); +void Reset( void ); +void ResetIfNotSticky( void ); +wBool_t DoCurCommand( wAction_t, coOrd ); +void ConfirmReset( BOOL_T ); +void LayoutToolBar( void ); +#define IC_STICKY (1<<0) +#define IC_CANCEL (1<<1) +#define IC_MENU (1<<2) +#define IC_NORESTART (1<<3) +#define IC_SELECTED (1<<4) +#define IC_POPUP (1<<5) +#define IC_LCLICK (1<<6) +#define IC_RCLICK (1<<7) +#define IC_CMDMENU (1<<8) +#define IC_POPUP2 (1<<9) +#define IC_ABUT (1<<10) +#define IC_ACCLKEY (1<<11) +#define IC_MODETRAIN_TOO (1<<12) +#define IC_MODETRAIN_ONLY (1<<13) +#define IC_WANT_MOVE (1<<14) +#define IC_PLAYBACK_PUSH (1<<15) +wIndex_t InitCommand( wMenu_p, procCommand_t, char *, char *, int, long, long ); +void AddToolbarControl( wControl_p, long ); +BOOL_T CommandEnabled( wIndex_t ); +wButton_p AddToolbarButton( char*, wIcon_p, long, wButtonCallBack_p, void * context ); +wIndex_t AddCommandButton( procCommand_t, char*, char*, wIcon_p, int, long, long, void* ); +wIndex_t AddMenuButton( wMenu_p, procCommand_t, char*, char*, wIcon_p, int, long, long, void* ); +void PlaybackButtonMouse( wIndex_t ); +void ButtonGroupBegin( char *, char *, char * ); +void ButtonGroupEnd( void ); + +void SaveState( void ); + +void PlaybackCommand( char *, wIndex_t ); +wMenu_p MenuRegister( char * label ); +void DoCommandB( void * ); + +extern void EnumerateTracks( void ); +void InitDebug( char *, long * ); + +#define CHANGE_SCALE (1<<0) +#define CHANGE_PARAMS (1<<1) +#define CHANGE_MAIN (1<<2) +#define CHANGE_MAP (1<<4) +#define CHANGE_GRID (1<<5) +#define CHANGE_UNITS (1<<7) +#define CHANGE_TOOLBAR (1<<8) +#define CHANGE_CMDOPT (1<<9) +#define CHANGE_LIMITS (1<<10) +#define CHANGE_ALL (CHANGE_SCALE|CHANGE_PARAMS|CHANGE_MAIN|CHANGE_MAP|CHANGE_UNITS|CHANGE_TOOLBAR|CHANGE_CMDOPT) +typedef void (*changeNotificationCallBack_t)( long ); +void RegisterChangeNotification( changeNotificationCallBack_t ); +void DoChangeNotification( long ); + +#include "param.h" +#include "misc2.h" +#include "fileio.h" + +/* foreign externs */ +extern drawCmd_t mapD; +extern STATUS_T CmdEnumerate( wAction_t, coOrd ); + +wIndex_t modifyCmdInx; +wIndex_t joinCmdInx; +wIndex_t tunnelCmdInx; + +/* ctodesgn.c */ +void InitNewTurn( wMenu_p m ); + +/* cnote.c */ +void ClearNote( void ); + +/* cruler.c */ +void RulerRedraw( BOOL_T ); +STATUS_T ModifyRuler( wAction_t, coOrd ); + +/* dialogs */ +void OutputBitMap( void ); + +wDrawColor snapGridColor; + +addButtonCallBack_t ColorInit( void ); +addButtonCallBack_t PrefInit( void ); +addButtonCallBack_t LayoutInit( void ); +addButtonCallBack_t DisplayInit( void ); +addButtonCallBack_t CmdoptInit( void ); +addButtonCallBack_t OutputBitMapInit( void ); +addButtonCallBack_t CustomMgrInit( void ); +addButtonCallBack_t PriceListInit( void ); +addButtonCallBack_t ParamFilesInit( void ); + +wIndex_t InitGrid( wMenu_p menu ); + +void SnapPos( coOrd * ); +void DrawSnapGrid( drawCmd_p, coOrd, BOOL_T ); +BOOL_T GridIsVisible( void ); +void InitSnapGridButtons( void ); +void SnapGridEnable( void ); +void SnapGridShow( void ); +wMenuToggle_p snapGridEnableMI; +wMenuToggle_p snapGridShowMI; + +void ScaleLengthEnd( void ); +void EnumerateList( long, FLOAT_T, char * ); +void EnumerateStart(void); +void EnumerateEnd(void); + +/* cnote.c */ +void DoNote( void ); +BOOL_T WriteMainNote( FILE * ); + +/* dbench.c */ +long GetBenchData( long, long ); +wIndex_t GetBenchListIndex( long ); +long SetBenchData( char *, wDrawWidth, wDrawColor ); +void DrawBench( drawCmd_p, coOrd, coOrd, wDrawColor, wDrawColor, long, long ); +void BenchUpdateOrientationList( long, wList_p ); +void BenchUpdateChoiceList( wIndex_t, wList_p, wList_p ); +addButtonCallBack_t InitBenchDialog( void ); +void BenchLoadLists( wList_p, wList_p ); +void BenchGetDesc( long, char * ); +void CountBench( long, DIST_T ); +void TotalBench( void ); +long BenchInputOption( long ); +long BenchOutputOption( long ); +DIST_T BenchGetWidth( long ); + +/* dcustmgm.c */ +FILE * customMgmF; +#define CUSTMGM_DO_COPYTO (1) +#define CUSTMGM_CAN_EDIT (2) +#define CUSTMGM_DO_EDIT (3) +#define CUSTMGM_CAN_DELETE (4) +#define CUSTMGM_DO_DELETE (5) +#define CUSTMGM_GET_TITLE (6) + +typedef int (*custMgmCallBack_p)( int, void * ); +void CustMgmLoad( wIcon_p, custMgmCallBack_p, void * ); +void CompoundCustMgmLoad(); +void CarCustMgmLoad(); +BOOL_T CompoundCustomSave(FILE*); +BOOL_T CarCustomSave(FILE*); + +/* doption.c */ +long GetDistanceFormat( void ); + +/* ctrain.c */ +BOOL_T WriteCars( FILE * ); +void ClearCars( void ); +void CarDlgAddProto( void ); +void CarDlgAddDesc( void ); +void AttachTrains( void ); +#endif + +/* cblock.c */ +void InitCmdBlock( wMenu_p menu ); + +/* cswitchmotor.c */ +void InitCmdSwitchMotor( wMenu_p menu ); diff --git a/app/bin/misc2.c b/app/bin/misc2.c new file mode 100644 index 0000000..96e871a --- /dev/null +++ b/app/bin/misc2.c @@ -0,0 +1,693 @@ +/** \file misc2.c + * Management of information about scales and gauges plus rprintf. + */ + +/* 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 <stdlib.h> +#include <stdio.h> +#ifndef WINDOWS +#include <unistd.h> +#include <dirent.h> +#endif +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif +#include <math.h> +#include <ctype.h> +#include <string.h> +#include <stdarg.h> + +#include <stdint.h> + +#include "track.h" +#include "common.h" +#include "utility.h" +#include "draw.h" +#include "misc.h" +#include "cjoin.h" +#include "compound.h" +#include "i18n.h" + +EXPORT long units = 0; +EXPORT long checkPtInterval = 10; + +EXPORT DIST_T curScaleRatio; +EXPORT char * curScaleName; +EXPORT DIST_T trackGauge; +EXPORT char Title1[TITLEMAXLEN] = ""; +EXPORT char Title2[TITLEMAXLEN] = "Title line 2"; +EXPORT long labelScale = 8; +EXPORT long labelEnable = ((1<<0)|LABELENABLE_LENGTHS|LABELENABLE_ENDPT_ELEV|LABELENABLE_CARS); +EXPORT long labelWhen = 2; +EXPORT long colorLayers = 0; +EXPORT long hideSelectionWindow = 0; +EXPORT long angleSystem = 0; +EXPORT DIST_T minLength = 0.1; +EXPORT DIST_T connectDistance = 0.1; +EXPORT ANGLE_T connectAngle = 1.0; +EXPORT long twoRailScale = 16; +EXPORT long mapScale = 64; +EXPORT long liveMap = 0; +EXPORT long preSelect = 0; +EXPORT long listLabels = 7; +EXPORT long layoutLabels = 1; +EXPORT long descriptionFontSize = 72; +EXPORT long enableListPrices = 1; +EXPORT void ScaleLengthEnd(void); +static char minTrackRadiusPrefS[STR_SHORT_SIZE] = "minTrackRadius"; + +/**************************************************************************** + * + * RPRINTF + * + */ + + +#define RBUFF_SIZE (8192) +static char rbuff[RBUFF_SIZE+1]; +static int roff; +static int rbuff_record = 0; + +EXPORT void Rdump( FILE * outf ) +{ + fprintf( outf, "Record Buffer:\n" ); + rbuff[RBUFF_SIZE] = '\0'; + fprintf( outf, "%s", rbuff+roff ); + rbuff[roff] = '\0'; + fprintf( outf, "%s", rbuff ); + memset( rbuff, 0, sizeof rbuff ); + roff = 0; +} + + +EXPORT void Rprintf( + char * format, + ... ) +{ + static char buff[STR_SIZE]; + char * cp; + va_list ap; + va_start( ap, format ); + vsprintf( buff, format, ap ); + va_end( ap ); + if (rbuff_record >= 1) + lprintf( buff ); + for ( cp=buff; *cp; cp++ ) { + rbuff[roff] = *cp; + roff++; + if (roff>=RBUFF_SIZE) + roff=0; + } +} + +/**************************************************************************** + * + * CHANGE NOTIFICATION + * + */ + + +static changeNotificationCallBack_t changeNotificationCallBacks[20]; +static int changeNotificationCallBackCnt = 0; + +EXPORT void RegisterChangeNotification( + changeNotificationCallBack_t action ) +{ + changeNotificationCallBacks[changeNotificationCallBackCnt] = action; + changeNotificationCallBackCnt++; +} + + +EXPORT void DoChangeNotification( long changes ) +{ + int inx; + for (inx=0;inx<changeNotificationCallBackCnt;inx++) + changeNotificationCallBacks[inx](changes); +} + + +/**************************************************************************** + * + * SCALE + * + */ + + +#define SCALE_ANY (-2) +#define SCALE_DEMO (-1) + +typedef struct { + char * scale; + DIST_T ratio; + DIST_T gauge; + DIST_T R[3]; + DIST_T X[3]; + DIST_T L[3]; + wIndex_t index; + DIST_T length; + BOOL_T tieDataValid; + tieData_t tieData; + } scaleInfo_t; +EXPORT typedef scaleInfo_t * scaleInfo_p; +static dynArr_t scaleInfo_da; +#define scaleInfo(N) DYNARR_N( scaleInfo_t, scaleInfo_da, N ) +static tieData_t tieData_demo = { + 96.0/160.0, + 16.0/160.0, + 32.0/160.0 }; + +EXPORT SCALEINX_T curScaleInx = -1; +static scaleInfo_p curScale; +EXPORT long includeSameGaugeTurnouts = FALSE; +static SCALEINX_T demoScaleInx = -1; + + +/** this struct holds a gauge description */ +typedef struct { + char * gauge; /** ptr to textual description eg. 'n3' */ + SCALEINX_T scale; /** index of complete information in scaleInfo_da */ + wIndex_t index; + } gaugeInfo_t; + +EXPORT typedef gaugeInfo_t * gaugeInfo_p; + +EXPORT GAUGEINX_T curGaugeInx = 0; + +/** this struct holds a scale description */ +typedef struct { + char *scaleDesc; /** ptr to textual description eg. 'HO' */ + SCALEINX_T scale; /** index of complete information (standard gauge) in scaleInfo_da */ + wIndex_t index; + dynArr_t gauges_da; /** known gauges to this scale */ + } scaleDesc_t; + +EXPORT typedef scaleDesc_t *scaleDesc_p; +static dynArr_t scaleDesc_da; +#define scaleDesc(N) DYNARR_N( scaleDesc_t, scaleDesc_da, N ) + +EXPORT SCALEDESCINX_T curScaleDescInx; + +/** + * Get the ratio from a scale description. Each member in the list of scale descriptions is + * linked to an entry in the simple linear list of all scales/gauges. From there the ratio is + * fetched and returned. Note that there is no error checking on parameters! + * + * \param IN sdi index into list of scale descriptions + * \return ratio for scale + */ + +EXPORT DIST_T GetScaleDescRatio( SCALEDESCINX_T sdi ) +{ + return GetScaleRatio( scaleDesc(sdi).scale ); +} + +/** + * Get the index into the linear list from a scale description and a gauge. All information about a + * scale/ gauge combination is stored in a linear list. The index in that list for a given scale and the + * gauge is returned by this function. Note that there is no error checking on parameters! + * + * \param IN scaleInx index into list of scale descriptions + * \param IN gaugeInx index into list of gauges available for this scale + * \return index into master list of scale/gauge combinations + */ + +EXPORT SCALEINX_T GetScaleInx( SCALEDESCINX_T scaleInx, GAUGEINX_T gaugeInx ) +{ + scaleDesc_t s; + gaugeInfo_p g; + + s = scaleDesc(scaleInx); + g = &(DYNARR_N(gaugeInfo_t, s.gauges_da, gaugeInx)); + + return g->scale; + +} +EXPORT DIST_T GetScaleTrackGauge( SCALEINX_T si ) +{ + return scaleInfo(si).gauge; +} + +EXPORT DIST_T GetScaleRatio( SCALEINX_T si ) +{ + return scaleInfo(si).ratio; +} + +EXPORT char * GetScaleName( SCALEINX_T si ) +{ + if ( si == -1 ) + return "DEMO"; + if ( si == SCALE_ANY ) + return "*"; + else if ( si < 0 || si >= scaleInfo_da.cnt ) + return "Unknown"; + else + return scaleInfo(si).scale; +} + +EXPORT void GetScaleEasementValues( DIST_T * R, DIST_T * L ) +{ + wIndex_t i; + for (i=0;i<3;i++) { + *R++ = curScale->R[i]; + *L++ = curScale->L[i]; + } +} + + +EXPORT tieData_p GetScaleTieData( SCALEINX_T si ) +{ + scaleInfo_p s; + DIST_T defLength; + + if ( si == -1 ) + return &tieData_demo; + else if ( si < 0 || si >= scaleInfo_da.cnt ) + return &tieData_demo; + s = &scaleInfo(si); + if ( !s->tieDataValid ) { + sprintf( message, "tiedata-%s", s->scale ); + defLength = (96.0-54.0)/s->ratio+s->gauge; + wPrefGetFloat( message, "length", &s->tieData.length, defLength ); + wPrefGetFloat( message, "width", &s->tieData.width, 16.0/s->ratio ); + wPrefGetFloat( message, "spacing", &s->tieData.spacing, 2*s->tieData.width ); + } + return &scaleInfo(si).tieData; +} + +EXPORT char *GetScaleDesc( SCALEDESCINX_T inx ) +{ + return scaleDesc(inx).scaleDesc; +} + +EXPORT char *GetGaugeDesc( SCALEDESCINX_T scaleInx, GAUGEINX_T gaugeInx ) +{ + scaleDesc_t s; + gaugeInfo_p g; + + s = scaleDesc(scaleInx); + g = &(DYNARR_N(gaugeInfo_t, s.gauges_da, gaugeInx)); + + return g->gauge; +} + +EXPORT SCALEINX_T LookupScale( const char * name ) +{ + wIndex_t si; + DIST_T gauge; + if ( strcmp( name, "*" ) == 0 ) + return SCALE_ANY; + for ( si=0; si<scaleInfo_da.cnt; si++ ) { + if (strcmp( scaleInfo(si).scale, name ) == 0) + return si; + } + if ( isdigit(name[0]) ) { + gauge = atof( name ); + for ( si=0; si<scaleInfo_da.cnt; si++ ) { + if (scaleInfo(si).gauge == gauge) + return si; + } + } + NoticeMessage( MSG_BAD_SCALE_NAME, "Ok", NULL, name, sProdNameLower ); + si = scaleInfo_da.cnt; + DYNARR_APPEND( scaleInfo_t, scaleInfo_da, 10 ); + scaleInfo(si) = scaleInfo(0); + scaleInfo(si).scale = MyStrdup( name ); + return si; +} + + +EXPORT BOOL_T CompatibleScale( + BOOL_T isTurnout, + SCALEINX_T scale1, + SCALEINX_T scale2 ) +{ + if ( scale1 == scale2 ) + return TRUE; + if ( scale1 == SCALE_DEMO || scale2 == SCALE_DEMO ) + return FALSE; + if ( scale1 == demoScaleInx || scale2 == demoScaleInx ) + return FALSE; + if ( isTurnout ) { + if ( includeSameGaugeTurnouts && + scaleInfo(scale1).gauge == scaleInfo(scale2).gauge ) + return TRUE; + } else { + if ( scale1 == SCALE_ANY ) + return TRUE; + if ( scaleInfo(scale1).ratio == scaleInfo(scale2).ratio ) + return TRUE; + } + return FALSE; +} + +/** Split the scale and the gauge description for a given combination. Eg HOn3 will be + * split to HO and n3. + * \param scaleInx IN scale/gauge combination + * \param scaleDescInx OUT scale part + * \param gaugeInx OUT gauge part + * \return TRUE + */ + +EXPORT BOOL_T +GetScaleGauge( SCALEINX_T scaleInx, SCALEDESCINX_T *scaleDescInx, GAUGEINX_T *gaugeInx) +{ + int i, j; + char *scaleName = GetScaleName( scaleInx ); + DIST_T scaleRatio = GetScaleRatio( scaleInx ); + dynArr_t gauges_da; + + for( i = 0; i < scaleDesc_da.cnt; i++ ) { + char *t = strchr( scaleDesc(i).scaleDesc, ' ' ); + /* are the first characters (which describe the scale) identical? */ + if( !strncmp( scaleDesc(i).scaleDesc, scaleName, t - scaleDesc(i).scaleDesc )) { + /* if yes, are we talking about the same ratio */ + if( scaleInfo(scaleDesc(i).scale).ratio == scaleRatio ) { + /* yes, we found the right scale descriptor, so now look for the gauge */ + *scaleDescInx = i; + gauges_da = scaleDesc(i).gauges_da; + *gaugeInx = 0; + for( j = 0; j < gauges_da.cnt; j++ ) { + gaugeInfo_p ptr = &(DYNARR_N( gaugeInfo_t, gauges_da, j )); + if( scaleInfo(ptr->scale).gauge == GetScaleTrackGauge( scaleInx )) { + *gaugeInx = j; + break; + } + } + break; + } + } + } + + return TRUE; +} + +/** + * Setup XTrkCad for the newly selected scale/gauge combination. + * + * \param newScaleInx IN the index of the selected scale/gauge combination + */ + +static void SetScale( + SCALEINX_T newScaleInx ) +{ + if (newScaleInx < 0 && newScaleInx >= scaleInfo_da.cnt) { + NoticeMessage( MSG_BAD_SCALE_INDEX, _("Ok"), NULL, (int)newScaleInx ); + return; + } + curScaleInx = (SCALEINX_T)newScaleInx; + curScale = &scaleInfo(curScaleInx); + trackGauge = curScale->gauge; + curScaleRatio = curScale->ratio; + curScaleName = curScale->scale; + + curScaleDescInx = 0; + + GetScaleGauge( curScaleInx, &curScaleDescInx, &curGaugeInx ); + + wPrefSetString( "misc", "scale", curScaleName ); + + // now load the minimum radius for the newly selected scale + sprintf( minTrackRadiusPrefS, "minTrackRadius-%s", curScaleName ); + wPrefGetFloat( "misc", minTrackRadiusPrefS, &minTrackRadius, curScale->R[0] ); +} + +/** + * Check the new scale value and update the program if a valid scale was passed + * + * \param newScale IN the name of the new scale + * \returns TRUE if valid, FALSE otherwise + */ + +EXPORT BOOL_T DoSetScale( + const char * newScale ) +{ + SCALEINX_T scale; + char * cp; + BOOL_T found = FALSE; + + if ( newScale != NULL ) { + cp = CAST_AWAY_CONST newScale+strlen(newScale)-1; + while ( *cp=='\n' || *cp==' ' || *cp=='\t' ) cp--; + cp[1] = '\0'; + while (isspace(*newScale)) newScale++; + for (scale = 0; scale<scaleInfo_da.cnt; scale++) { + if (strcasecmp( scaleInfo(scale).scale, newScale ) == 0) { + curScaleInx = scale; + found = TRUE; + break; + } + } + // was a valid scale given? + if( found ) { + DoChangeNotification( CHANGE_SCALE ); + } + } + + return found; +} + +/** + * Setup the data structures for scale and gauge. XTC reads 'scales' into an dynamic array, + * but doesn't differentiate between scale and gauge. + * This da is split into an dynamic array of scales. Each scale holds a dynamic array of gauges, + * with at least one gauge per scale (ie standard gauge) + * + * For usage in the dialogs, a textual description for each scale or gauge is provided + * + * \return TRUE + */ + +EXPORT BOOL_T DoSetScaleDesc( void ) +{ + SCALEINX_T scaleInx; + SCALEINX_T work; + SCALEDESCINX_T descInx; + scaleDesc_p s = NULL; + gaugeInfo_p g; + char *cp; + DIST_T ratio; + BOOL_T found; + char buf[ 80 ]; + int len; + + for( scaleInx = 0; scaleInx < scaleInfo_da.cnt; scaleInx++ ) { + ratio = DYNARR_N( scaleInfo_t, scaleInfo_da, scaleInx ).ratio; + + /* do we already have a description for this scale? */ + found = 0; + + if( scaleDesc_da.cnt > 0 ) { + for( descInx = 0; descInx < scaleDesc_da.cnt; descInx++ ) { + work = scaleDesc(descInx).scale; + if( scaleInfo(work).ratio == scaleInfo(scaleInx).ratio ) { + if( !strncmp( scaleInfo(work).scale, scaleInfo(scaleInx).scale, strlen(scaleInfo(work).scale))) + found = TRUE; + } + } + } + + + if( !found ) { + /* if no, add as new scale */ + + DYNARR_APPEND( scaleDesc_t, scaleDesc_da, 1 ); + + s = &(scaleDesc( scaleDesc_da.cnt-1 )); + + s->scale = scaleInx; + + sprintf( buf, "%s (1/%.1f)", scaleInfo(scaleInx).scale, scaleInfo(scaleInx).ratio ); + s->scaleDesc = MyStrdup( buf ); + + /* initialize the array with standard gauge */ + + DYNARR_APPEND( gaugeInfo_t, s->gauges_da, 10 ); + + g = &(DYNARR_N( gaugeInfo_t, s->gauges_da, (s->gauges_da).cnt - 1 )); + g->scale = scaleInx; + sprintf( buf, "Standard (%.1fmm)", scaleInfo(scaleInx).gauge*25.4 ); + g->gauge = MyStrdup( buf ); + + } else { + /* if yes, is this a new gauge to the scale? */ + DYNARR_APPEND( gaugeInfo_t, s->gauges_da, 10 ); + g = &(DYNARR_N( gaugeInfo_t, s->gauges_da, (s->gauges_da).cnt - 1 )); + g->scale = scaleInx; + cp = strchr( s->scaleDesc, ' ' ); + if( cp ) + len = cp - s->scaleDesc; + else + len = strlen(s->scaleDesc); + sprintf( buf, "%s (%.1fmm)", scaleInfo(scaleInx).scale+len, scaleInfo(scaleInx).gauge*25.4 ); + g->gauge = MyStrdup( buf ); + } + } + + return( TRUE ); +} + +void +SetScaleGauge( SCALEDESCINX_T scaleDesc, GAUGEINX_T gauge ) +{ + dynArr_t gauges_da; + + gauges_da = (scaleDesc(scaleDesc)).gauges_da; + curScaleInx = ((gaugeInfo_p)gauges_da.ptr)[ gauge ].scale; +} + +static BOOL_T AddScale( + char * line ) +{ + wIndex_t i; + BOOL_T rc; + DIST_T R[3], X[3], L[3]; + DIST_T ratio, gauge; + char scale[40]; + scaleInfo_p s; + + if ( (rc=sscanf( line, "SCALE %[^,]," SCANF_FLOAT_FORMAT "," SCANF_FLOAT_FORMAT "", + scale, &ratio, &gauge )) != 3) { + SyntaxError( "SCALE", rc, 3 ); + return FALSE; + } + for (i=0;i<3;i++) { + line = GetNextLine(); + if ( (rc=sscanf( line, "" SCANF_FLOAT_FORMAT "," SCANF_FLOAT_FORMAT "," SCANF_FLOAT_FORMAT "", + &R[i], &X[i], &L[i] )) != 3 ) { + SyntaxError( "SCALE easement", rc, 3 ); + return FALSE; + } + } + + DYNARR_APPEND( scaleInfo_t, scaleInfo_da, 10 ); + s = &scaleInfo(scaleInfo_da.cnt-1); + s->scale = MyStrdup( scale ); + s->ratio = ratio; + s->gauge = gauge; + s->index = -1; + for (i=0; i<3; i++) { + s->R[i] = R[i]/ratio; + s->X[i] = X[i]/ratio; + s->L[i] = L[i]/ratio; + } + s->tieDataValid = FALSE; + if ( strcmp( scale, "DEMO" ) == 0 ) + demoScaleInx = scaleInfo_da.cnt-1; + return TRUE; +} + + +EXPORT void ScaleLengthIncrement( + SCALEINX_T scale, + DIST_T length ) +{ + char * cp; + int len; + if (scaleInfo(scale).length == 0.0) { + if (units == UNITS_METRIC) + cp = "999.99m SCALE Flex Track"; + else + cp = "999' 11\" SCALE Flex Track"; + len = strlen( cp )+1; + if (len > enumerateMaxDescLen) + enumerateMaxDescLen = len; + } + scaleInfo(scale).length += length; +} + +EXPORT void ScaleLengthEnd( void ) +{ + wIndex_t si; + int count; + DIST_T length; + char tmp[STR_SIZE]; + FLOAT_T flexLen; + long flexUnit; + FLOAT_T flexCost; + for (si=0; si<scaleInfo_da.cnt; si++) { + sprintf( tmp, "price list %s", scaleInfo(si).scale ); + wPrefGetFloat( tmp, "flex length", &flexLen, 0.0 ); + wPrefGetInteger( tmp, "flex unit", &flexUnit, 0 ); + wPrefGetFloat( tmp, "flex cost", &flexCost, 0.0 ); + tmp[0] = '\0'; + if ((length=scaleInfo(si).length) != 0) { + sprintf( tmp, "%s %s Flex Track", FormatDistance(length), scaleInfo(si).scale ); + for (count = strlen(tmp); count<enumerateMaxDescLen; count++) + tmp[count] = ' '; + tmp[enumerateMaxDescLen] = '\0'; + count = 0; + if (flexLen > 0.0) { + count = (int)ceil( length / (flexLen/(flexUnit?2.54:1.00))); + } + EnumerateList( count, flexCost, tmp ); + } + scaleInfo(si).length = 0; + } +} + + + +EXPORT void LoadScaleList( wList_p scaleList ) +{ + wIndex_t inx; + for (inx=0; inx<scaleDesc_da.cnt-(extraButtons?0:1); inx++) { + scaleDesc(inx).index = + wListAddValue( scaleList, scaleDesc(inx).scaleDesc, NULL, (void*)(intptr_t)inx ); + } +} + +EXPORT void LoadGaugeList( wList_p gaugeList, SCALEDESCINX_T scale ) +{ + wIndex_t inx; + scaleDesc_t s; + gaugeInfo_p g; + dynArr_t *gauges_da_p; + + s = scaleDesc(scale); + gauges_da_p = &(s.gauges_da); + g = gauges_da_p->ptr; + g = s.gauges_da.ptr; + + wListClear( gaugeList ); /* remove old list in case */ + for (inx=0; inx<gauges_da_p->cnt; inx++) { + (g[inx]).index = wListAddValue( gaugeList, (g[inx]).gauge, NULL, (void*)(intptr_t)(g[inx]).scale ); + } +} + +static void ScaleChange( long changes ) +{ + if (changes & CHANGE_SCALE) { + SetScale( curScaleInx ); + } +} + +/***************************************************************************** + * + * + * + */ + +EXPORT void Misc2Init( void ) +{ + AddParam( "SCALE ", AddScale ); + wPrefGetInteger( "draw", "label-when", &labelWhen, labelWhen ); + RegisterChangeNotification( ScaleChange ); + wPrefGetInteger( "misc", "include same gauge turnouts", &includeSameGaugeTurnouts, 1 ); +} diff --git a/app/bin/misc2.h b/app/bin/misc2.h new file mode 100644 index 0000000..ba05394 --- /dev/null +++ b/app/bin/misc2.h @@ -0,0 +1,110 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/misc2.h,v 1.7 2008-01-04 02:12:33 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. + */ + +#ifndef MISC2_H +#define MISC2_H + +#ifdef WINDOWS +#include <time.h> +#endif + +#define LABEL_MANUF (1<<0) +#define LABEL_PARTNO (1<<1) +#define LABEL_DESCR (1<<2) +#define LABEL_COST (1<<7) +#define LABEL_FLIPPED (1<<8) +#define LABEL_TABBED (1<<9) +#define LABEL_UNGROUPED (1<<10) +#define LABEL_SPLIT (1<<11) + +typedef struct { + char * name; + int level; + } logTable_t; +extern dynArr_t logTable_da; +#define logTable(N) DYNARR_N( logTable_t, logTable_da, N ) +time_t logClock; +void LogOpen( char * ); +void LogClose( void ); +void LogSet( char *, int ); +int LogFindIndex( char * ); +void LogPrintf( char *, ... ); +#define LOG( DBINX, DBLVL, DBMSG ) \ + if ( DBINX > 0 && logTable( DBINX ).level >= DBLVL ) { \ + LogPrintf DBMSG ; \ + } +#define LOG1( DBINX, DBMSG ) LOG( DBINX, 1, DBMSG ) +#define LOGNAME( DBNAME, DBMSG ) LOG( LogFindIndex( DBNAME ), DBMSG ) + +#define lprintf LogPrintf +void Rdump( FILE * ); +void Rprintf( char *, ... ); + +typedef struct { + DIST_T length; + DIST_T width; + DIST_T spacing; + } tieData_t, *tieData_p; +DIST_T GetScaleTrackGauge( SCALEINX_T ); +DIST_T GetScaleRatio( SCALEINX_T ); +DIST_T GetScaleDescRatio( SCALEDESCINX_T sdi ); +char * GetScaleName( SCALEINX_T ); +SCALEINX_T GetScaleInx( SCALEDESCINX_T scaleInx, GAUGEINX_T gaugeInx ); + +char *GetScaleDesc( SCALEDESCINX_T inx ); +char *GetGaugeDesc( SCALEDESCINX_T scaleInx, GAUGEINX_T gaugeInx ); +void GetScaleEasementValues( DIST_T *, DIST_T * ); +tieData_p GetScaleTieData( SCALEINX_T ); +SCALEINX_T LookupScale( const char * ); +BOOL_T GetScaleGauge( SCALEINX_T scaleInx, SCALEDESCINX_T *scaleDescInx, GAUGEINX_T *gaugeInx); + +BOOL_T DoSetScale( const char * ); + +void SetScaleGauge( SCALEDESCINX_T, GAUGEINX_T ); +void ScaleLengthIncrement( SCALEINX_T, DIST_T ); +void LoadScaleList( wList_p ); +void LoadGaugeList( wList_p, SCALEDESCINX_T ); +BOOL_T CompatibleScale( BOOL_T, SCALEINX_T, SCALEINX_T ); +BOOL_T DoSetScaleDesc( void ); +typedef int LAYER_T; +LAYER_T curLayer; +long layerCount; +wDrawColor GetLayerColor( LAYER_T ); +BOOL_T GetLayerVisible( LAYER_T ); +BOOL_T GetLayerFrozen( LAYER_T ); +BOOL_T GetLayerOnMap( LAYER_T ); +char * GetLayerName( LAYER_T ); +BOOL_T ReadLayers( char * ); +BOOL_T WriteLayers( FILE * ); + +/* dlayers.c */ +void UpdateLayerLists( void ); +void DefaultLayerProperties(void); +void ResetLayers( void ); +void SaveLayers( void ); +void RestoreLayers( void ); +void LoadLayerLists( void ); +addButtonCallBack_t InitLayersDialog( void ); + +void Misc2Init( void ); + +#endif diff --git a/app/bin/param.c b/app/bin/param.c new file mode 100644 index 0000000..c58a4fa --- /dev/null +++ b/app/bin/param.c @@ -0,0 +1,2699 @@ +/** \file param.c + * Handle all the dialog box creation stuff. + */ + +/* 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 <stdlib.h> +#include <stdio.h> +#ifndef WINDOWS +#include <unistd.h> +#include <dirent.h> +#endif +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif +#include <math.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#ifdef WINDOWS +#include <io.h> +#include <windows.h> +#define R_OK (02) +#define access _access +#else +#include <sys/stat.h> +#include <errno.h> +#endif +#include <stdarg.h> +#include <locale.h> +#include <wlib.h> +#include "track.h" +#include "common.h" +#include "utility.h" +#include "misc.h" +#include "compound.h" +#include "i18n.h" + + +/* Bogus reg vars */ +EXPORT int paramLevel = 1; +EXPORT int paramLen; +EXPORT unsigned long paramKey; +EXPORT char paramId[100]; +EXPORT BOOL_T paramTogglePlaybackHilite; + +EXPORT char *PREFSECT = "DialogItem"; + +static int paramCheckErrorCount = 0; +static BOOL_T paramCheckShowErrors = FALSE; + +static int log_hotspot; +static int hotspotOffsetX = 5; +static int hotspotOffsetY = 19; + +static int log_paramLayout; + + +#ifdef LATER +/***************************************************************************** + * + * Colors + * + */ + +typedef struct { + long rgb; + char * name; + wDrawColor color; + } colorTab_t[]; + +static colorTab_t colorTab = { + { wRGB( 0, 0, 0), N_("Black") }, + + { wRGB( 0, 0,128), N_("Dark Blue") }, + { wRGB( 70,130,180), N_("Steel Blue") }, + { wRGB( 65,105,225), N_("Royal Blue") }, + { wRGB( 0, 0,255), N_("Blue") }, + { wRGB( 0,191,255), N_("Deep Sky Blue") }, + { wRGB(125,206,250), N_("Light Sky Blue") }, + { wRGB(176,224,230), N_("Powder Blue") }, + + { wRGB( 0,128,128), N_("Dark Aqua") }, + { wRGB(127,255,212), N_("Aquamarine") }, + { wRGB( 0,255,255), N_("Aqua") }, + + { wRGB( 0,128, 0), N_("Dark Green") }, + { wRGB( 34,139, 34), N_("Forest Green") }, + { wRGB( 50,205, 50), N_("Lime Green") }, + { wRGB( 0,255, 0), N_("Green") }, + { wRGB(124,252, 0), N_("Lawn Green") }, + { wRGB(152,251,152), N_("Pale Green") }, + + { wRGB(128,128, 0), N_("Dark Yellow") }, + { wRGB(255,127, 80), N_("Coral") }, + { wRGB(255,165, 0), N_("Orange") }, + { wRGB(255,215, 0), N_("Gold") }, + { wRGB(255,255, 0), N_("Yellow") }, + + { wRGB(139, 69, 19), N_("Saddle Brown") }, + { wRGB(165, 42, 42), N_("Brown") }, + { wRGB(210,105, 30), N_("Chocolate") }, + { wRGB(188,143,143), N_("Rosy Brown") }, + { wRGB(210,180,140), N_("Tan") }, + { wRGB(245,245,220), N_("Beige") }, + + + { wRGB(128, 0, 0), N_("Dark Red") }, + { wRGB(255, 99, 71), N_("Tomato") }, + { wRGB(255, 0, 0), N_("Red") }, + { wRGB(255,105,180), N_("Hot Pink") }, + { wRGB(255,192,203), N_("Pink") }, + + { wRGB(128, 0,128), N_("Dark Purple") }, + { wRGB(176, 48, 96), N_("Maroon") }, + { wRGB(160, 32,240), N_("Purple2") }, + { wRGB(255, 0,255), N_("Purple") }, + { wRGB(238,130,238), N_("Violet") }, + + { wRGB( 64, 64, 64), N_("Dark Gray") }, + { wRGB(128,128,128), N_("Gray") }, + { wRGB(192,192,192), N_("Light Gray") } }; +static wIcon_p colorTabBitMaps[ sizeof colorTab/sizeof colorTab[0] ]; +#include "bitmaps/square10.xbm" + +static BOOL_T colorTabInitted = FALSE; + +static void InitColorTab( void ) +{ + wIndex_t inx; + for ( inx=0; inx<COUNT(colorTab); inx++ ) + colorTab[inx].color = wDrawFindColor( colorTab[inx].rgb ); + colorTabInitted = TRUE; +} + + +static wIndex_t ColorTabLookup( wDrawColor color ) +{ + wIndex_t inx; + if (!colorTabInitted) + InitColorTab(); + for (inx = 0; inx < sizeof colorTab/sizeof colorTab[0]; inx++ ) + if (colorTab[inx].color == color) + return inx; + return 0; +} +#endif + +/***************************************************************************** + * + * + * + */ + +static char * getNumberError; +static char decodeErrorStr[STR_SHORT_SIZE]; + +static int GetDigitStr( char ** cpp, long * numP, int * lenP ) +{ + char *cp=*cpp, *cq; + int len; + *numP = 0; + if ( cp == NULL ) { + getNumberError = N_("Unexpected End Of String"); + return FALSE; + } + while ( isspace(*cp) ) cp++; + *numP = strtol( cp, &cq, 10 ); + if ( cp==cq ) { + *cpp = cp; + getNumberError = N_("Expected digit"); + return FALSE; + } + len = cq-cp; + if ( lenP ) + *lenP = len; + if ( len > 9 ) { + getNumberError = N_("Overflow"); + return FALSE; + } + while ( isspace(*cq) ) cq++; + *cpp = cq; + return TRUE; +} + +static int GetNumberStr( char ** cpp, FLOAT_T * numP, BOOL_T * hasFract ) +{ + long n0=0, f1, f2; + int l1; + char * cp = NULL; + struct lconv *lc; + + while ( isspace(**cpp) ) (*cpp)++; + + /* Find out the decimal separator of the current locale */ + lc = localeconv(); + + if ( **cpp != lc->decimal_point[0] + && !GetDigitStr( cpp, &n0, NULL ) ) + return FALSE; + if ( **cpp == lc->decimal_point[0] ) { + (*cpp)++; + if ( !isdigit(**cpp) ) { + *hasFract = FALSE; + *numP = (FLOAT_T)n0; + return TRUE; + } + if ( !GetDigitStr( cpp, &f1, &l1 ) ) return FALSE; + for ( f2=1; l1>0; l1-- ) f2 *= 10; + *numP = ((FLOAT_T)n0)+((FLOAT_T)f1)/((FLOAT_T)f2); + *hasFract = TRUE; + return TRUE; /* 999.999 */ + } + if ( isdigit( **cpp ) ) { + cp = *cpp; + if ( !GetDigitStr( cpp, &f1, NULL ) ) return FALSE; + } else { + f1 = n0; + n0 = 0; + } + if ( **cpp == '/' ) { + (*cpp)++; + if ( !GetDigitStr( cpp, &f2, &l1 ) ) return FALSE; + if ( f2 == 0 ) { + (*cpp) -= l1; + getNumberError = N_("Divide by 0"); + return FALSE; /* div by 0 */ + } + *numP = ((FLOAT_T)n0)+((FLOAT_T)f1)/((FLOAT_T)f2); + *hasFract = TRUE; + } else { + if ( cp != NULL ) { + *cpp = cp; + getNumberError = N_("Expected /"); + return FALSE; /* 999 999 ?? */ + } else { + *hasFract = FALSE; + *numP = f1; + } + } + return TRUE; +} +extern wIndex_t distanceFormatInx; // distanceFormatInx + +static BOOL_T GetDistance( char ** cpp, FLOAT_T * distP ) +{ + FLOAT_T n1, n2; + BOOL_T neg = FALSE; + BOOL_T hasFract; + BOOL_T expectInch = FALSE; + long distanceFormat; + + while ( isspace(**cpp) ) (*cpp)++; + if ( (*cpp)[0] == '\0' ) { + *distP = 0.0; + return TRUE; + } + if ( (*cpp)[0] == '-' ) { + neg = TRUE; + (*cpp)++; + } + if ( !GetNumberStr( cpp, &n1, &hasFract ) ) return FALSE; + + distanceFormat = GetDistanceFormat(); + + + if ( (*cpp)[0] == '\0' ) { /* EOL */ + if ( units==UNITS_METRIC ) + { + n1 = n1/2.54; + if ((distanceFormat & DISTFMT_FMT) == DISTFMT_FMT_MM) + n1 /= 10; + if ((distanceFormat & DISTFMT_FMT) == DISTFMT_FMT_M) + n1 *= 100; + } else { + if (((distanceFormat & DISTFMT_FMT) == DISTFMT_FMT_SHRT) || ((distanceFormat & DISTFMT_FMT) == DISTFMT_FMT_LONG)) + n1 *= 12; + } + if ( neg ) + n1 = -n1; + *distP = n1; + return TRUE; + } + if ( (*cpp)[0] == '\'' ) { + n1 *= 12.0; + (*cpp) += 1; + expectInch = !hasFract; + } else if ( tolower((*cpp)[0]) == 'f' && tolower((*cpp)[1]) == 't' ) { + n1 *= 12.0; + (*cpp) += 2; + expectInch = !hasFract; + } else if ( tolower((*cpp)[0]) == 'c' && tolower((*cpp)[1]) == 'm' ) { + n1 /= 2.54; + (*cpp) += 2; + } else if ( tolower((*cpp)[0]) == 'm' && tolower((*cpp)[1]) == 'm' ) { + n1 /= 25.4; + (*cpp) += 2; + } else if ( tolower((*cpp)[0]) == 'm' ) { + n1 *= 100.0/2.54; + (*cpp) += 1; + } else if ( (*cpp)[0] == '"' ) { + (*cpp) += 1; + } else if ( tolower((*cpp)[0]) == 'i' && tolower((*cpp)[1]) == 'n' ) { + (*cpp) += 2; + } else { + getNumberError = N_("Invalid Units Indicator"); + return FALSE; + } + while ( isspace(**cpp) ) (*cpp)++; + if ( expectInch && isdigit( **cpp ) ) { + if ( !GetNumberStr( cpp, &n2, &hasFract ) ) return FALSE; + n1 += n2; + if ( (*cpp)[0] == '"' ) + (*cpp) += 1; + else if ( tolower((*cpp)[0]) == 'i' && tolower((*cpp)[1]) == 'n' ) + (*cpp) += 2; + while ( isspace(**cpp) ) (*cpp)++; + } + if ( **cpp ) { + getNumberError = N_("Expected End Of String"); + return FALSE; + } + if ( neg ) + n1 = -n1; + *distP = n1; + return TRUE; +} + + +EXPORT FLOAT_T DecodeFloat( + wString_p strCtrl, + BOOL_T * validP ) +{ + FLOAT_T valF; + const char *cp0, *cp1; + char *cp2; + cp0 = cp1 = wStringGetValue( strCtrl ); + while (isspace(*cp1)) cp1++; + if ( *cp1 ) { + valF = strtod( cp1, &cp2 ); + if ( *cp2 != 0 ) { + /*wStringSetHilight( strCtrl, cp2-cp0, -1 );*/ + sprintf( decodeErrorStr, _("Invalid Number") ); + *validP = FALSE; + return 0.0; + } + *validP = TRUE; + return valF; + } else { + *validP = TRUE; + return 0.0; + } +} + + +EXPORT FLOAT_T DecodeDistance( + wString_p strCtrl, + BOOL_T * validP ) +{ + FLOAT_T valF; + char *cp0, *cp1, *cpN, c1; + + cp0 = cp1 = cpN = CAST_AWAY_CONST wStringGetValue( strCtrl ); + cpN += strlen(cpN)-1; + while (cpN > cp1 && isspace(*cpN) ) cpN--; + c1 = *cpN; + switch ( c1 ) { + case '=': + case 's': + case 'S': + case 'p': + case 'P': + *cpN = '\0'; + break; + default: + cpN = NULL; + } + *validP = ( GetDistance( &cp1, &valF ) ); + if ( cpN ) + *cpN = c1; + if ( *validP ) { +/*fprintf( stderr, "gd=%0.6f\n", valF );*/ + if ( c1 == 's' || c1 == 'S' ) + valF *= curScaleRatio; + else if ( c1 == 'p' || c1 == 'P' ) + valF /= curScaleRatio; + if ( cpN ) + wStringSetValue( strCtrl, FormatDistance( valF ) ); + } else { +/*fprintf( stderr, "Gd( @%s ) error=%s\n", cp1, getNumberError );*/ + sprintf( decodeErrorStr, "%s @ %s", _(getNumberError), *cp1?cp1:_("End Of String") ); + /*wStringSetHilight( strCtrl, cp1-cp0, -1 ); */ + valF = 0.0; + } + return valF; +} + + +#define N_STRING (10) +static char formatStrings[N_STRING][40]; +static int formatStringInx; + +EXPORT char * FormatLong( + long valL ) +{ + if ( ++formatStringInx >= N_STRING ) + formatStringInx = 0; + sprintf( formatStrings[formatStringInx], "%ld", valL ); + return formatStrings[formatStringInx]; +} + + +EXPORT char * FormatFloat( + FLOAT_T valF ) +{ + if ( ++formatStringInx >= N_STRING ) + formatStringInx = 0; + sprintf( formatStrings[formatStringInx], "%0.3f", valF ); + return formatStrings[formatStringInx]; +} + + +static void FormatFraction( + char ** cpp, + BOOL_T printZero, + int digits, + BOOL_T rational, + FLOAT_T valF, + char * unitFmt ) +{ + char * cp = *cpp; + long integ; + long f1, f2; + char * space = ""; + + if ( !rational ) { + sprintf( cp, "%0.*f", digits, valF ); + cp += strlen(cp); + } else { + integ = (long)floor(valF); + valF -= (FLOAT_T)integ; + for ( f2=1; digits>0; digits--,f2*=2 ); + f1 = (long)floor( (valF*(FLOAT_T)f2) + 0.5 ); + if ( f1 >= f2 ) { + f1 -= f2; + integ++; + } + if ( integ != 0 || !printZero ) { + sprintf( cp, "%ld", integ ); + cp += strlen(cp); + printZero = FALSE; + space = " "; + } + if ( f2 > 1 && f1 != 0 ) { + while ( (f1&1) == 0 ) { f1 /= 2; f2 /= 2; } + sprintf( cp, "%s%ld/%ld", space, f1, f2 ); + cp += strlen(cp); + } else if ( printZero ) { + *cp++ = '0'; + *cp = '\0'; + } + } + if ( cp != *cpp ) { + strcpy( cp, unitFmt ); + cp += strlen(cp); + *cpp = cp; + } +} + + +EXPORT char * FormatDistanceEx( + FLOAT_T valF, + long distanceFormat ) +{ + char * cp; + int digits; + long feet; + char * metricInd; + + if ( ++formatStringInx >= N_STRING ) + formatStringInx = 0; + cp = formatStrings[formatStringInx]; + digits = (int)(distanceFormat&DISTFMT_DECS); + valF = PutDim(valF); + if ( valF < 0 ) { + *cp++ = '-'; + valF = -valF; + } + if ( (distanceFormat&DISTFMT_FMT) == DISTFMT_FMT_NONE ) { + FormatFraction( &cp, FALSE, digits, (distanceFormat&DISTFMT_FRACT) == DISTFMT_FRACT_FRC, valF, "" ); + return formatStrings[formatStringInx]; + } else if ( units == UNITS_ENGLISH ) { + feet = (long)(floor)(valF/12.0); + valF -= feet*12.0; + if ( feet != 0 ) { + sprintf( cp, "%ld%s", feet, (distanceFormat&DISTFMT_FMT)==DISTFMT_FMT_SHRT?"' ":"ft " ); + cp += strlen(cp); + } + if ( feet==0 || valF != 0 ) { + FormatFraction( &cp, feet==0, digits, (distanceFormat&DISTFMT_FRACT) == DISTFMT_FRACT_FRC, valF, + (distanceFormat&DISTFMT_FMT)==DISTFMT_FMT_SHRT?"\"":"in" ); + } + } else { + if ( (distanceFormat&DISTFMT_FMT)==DISTFMT_FMT_M ) { + valF = valF/100.0; + metricInd = "m"; + } else if ( (distanceFormat&DISTFMT_FMT)==DISTFMT_FMT_MM ) { + valF = valF*10.0; + metricInd = "mm"; + } else { + metricInd = "cm"; + } + FormatFraction( &cp, FALSE, digits, (distanceFormat&DISTFMT_FRACT) == DISTFMT_FRACT_FRC, valF, metricInd ); + } + return formatStrings[formatStringInx]; +} + + +EXPORT char * FormatDistance( + FLOAT_T valF ) +{ + return FormatDistanceEx( valF, GetDistanceFormat() ); +} + +EXPORT char * FormatSmallDistance( + FLOAT_T valF ) +{ + long format = GetDistanceFormat(); + format &= ~(DISTFMT_FRACT_FRC|DISTFMT_DECS); + format |= 3; + return FormatDistanceEx( valF, format ); +} + +/***************************************************************************** + * + * + * + */ + +EXPORT void ParamControlActive( + paramGroup_p pg, + int inx, + BOOL_T active ) +{ + paramData_p p = &pg->paramPtr[inx]; + if ( p->control ) + wControlActive( p->control, active ); +} + + +EXPORT void ParamLoadMessage( + paramGroup_p pg, + int inx, + char * message ) +{ + paramData_p p = &pg->paramPtr[inx]; + if ( p->control ) { + if ( p->type == PD_MESSAGE ) + wMessageSetValue( (wMessage_p)p->control, message ); + else if ( p->type == PD_STRING ) + wStringSetValue( (wString_p)p->control, message ); + else + AbortProg( "paramLoadMessage: not a PD_MESSAGE or PD_STRING" ); + } +} + + +EXPORT void ParamLoadControl( + paramGroup_p pg, + int inx ) +{ + paramData_p p = &pg->paramPtr[inx]; + FLOAT_T tmpR; + char * valS; + + if ( (p->option&PDO_DLGIGNORE) != 0 ) + return; + if (p->control == NULL || p->valueP == NULL) + return; + switch ( p->type ) { + case PD_LONG: + wStringSetValue( (wString_p)p->control, FormatLong( *(long*)p->valueP ) ); + p->oldD.l = *(long*)p->valueP; + break; + case PD_RADIO: + wRadioSetValue( (wChoice_p)p->control, *(long*)p->valueP ); + p->oldD.l = *(long*)p->valueP; + break; + case PD_TOGGLE: + wToggleSetValue( (wChoice_p)p->control, *(long*)p->valueP ); + p->oldD.l = *(long*)p->valueP; + break; + case PD_LIST: + case PD_DROPLIST: + case PD_COMBOLIST: + wListSetIndex( (wList_p)p->control, *(wIndex_t*)p->valueP ); + p->oldD.l = *(wIndex_t*)p->valueP; + break; + case PD_COLORLIST: +#ifdef LATER + inx = ColorTabLookup( *(wDrawColor*)p->valueP ); + wListSetIndex( (wList_p)p->control, inx ); +#endif + wColorSelectButtonSetColor( (wButton_p)p->control, *(wDrawColor*)p->valueP ); + p->oldD.dc = *(wDrawColor*)p->valueP; + break; + case PD_FLOAT: + tmpR = *(FLOAT_T*)p->valueP; + if (p->option&PDO_DIM) { + if (p->option&PDO_SMALLDIM) + valS = FormatSmallDistance( tmpR ); + else + valS = FormatDistance( tmpR ); + } else { + if (p->option&PDO_ANGLE) + tmpR = NormalizeAngle( (angleSystem==ANGLE_POLAR)?tmpR:-tmpR ); + valS = FormatFloat( tmpR ); + } + wStringSetValue( (wString_p)p->control, valS ); + p->oldD.f = tmpR; + break; + case PD_STRING: + wStringSetValue( (wString_p)p->control, (char*)p->valueP ); + if (p->oldD.s) + MyFree( p->oldD.s ); + p->oldD.s = MyStrdup( (char*)p->valueP ); + break; + case PD_MESSAGE: + wMessageSetValue( (wMessage_p)p->control, _((char*)p->valueP) ); + break; + case PD_TEXT: + wTextClear( (wText_p)p->control ); + wTextAppend( (wText_p)p->control, (char*)p->valueP ); + break; + case PD_BUTTON: + case PD_DRAW: + case PD_MENU: + case PD_MENUITEM: + break; + } +} + + +/** Load all the controls in a parameter group. +* \param IN pointer to parameter group to be loaded +*/ +EXPORT void ParamLoadControls( + paramGroup_p pg ) +{ + int inx; + for ( inx=0; inx<pg->paramCnt; inx++ ) + ParamLoadControl( pg, inx ); +} + + +EXPORT long ParamUpdate( + paramGroup_p pg ) +{ + long longV; + FLOAT_T floatV; + const char * stringV; + wDrawColor dc; + long change = 0; + int inx; + paramData_p p; + BOOL_T valid; + + for ( p=pg->paramPtr,inx=0; p<&pg->paramPtr[pg->paramCnt]; p++,inx++ ) { + if ( (p->option&PDO_DLGIGNORE) != 0 ) + continue; + if ( p->control == NULL ) + continue; + switch ( p->type ) { + case PD_LONG: + stringV = wStringGetValue( (wString_p)p->control ); + longV = atol( stringV ); + if (longV != p->oldD.l) { + p->oldD.l = longV; + if ( /*(p->option&PDO_NOUPDUPD)==0 &&*/ p->valueP) + *(long*)p->valueP = longV; + if ( (p->option&PDO_NOUPDACT)==0 && pg->changeProc) + pg->changeProc( pg, inx, &longV ); + change |= (1L<<inx); + } + break; + case PD_RADIO: + longV = wRadioGetValue( (wChoice_p)p->control ); + if (longV != p->oldD.l) { + p->oldD.l = longV; + if ( /*(p->option&PDO_NOUPDUPD)==0 &&*/ p->valueP) + *(long*)p->valueP = longV; + if ( (p->option&PDO_NOUPDACT)==0 && pg->changeProc) + pg->changeProc( pg, inx, &longV ); + change |= (1L<<inx); + } + break; + case PD_TOGGLE: + longV = wToggleGetValue( (wChoice_p)p->control ); + if (longV != p->oldD.l) { + p->oldD.l = longV; + if ( /*(p->option&PDO_NOUPDUPD)==0 &&*/ p->valueP) + *(long*)p->valueP = longV; + if ( (p->option&PDO_NOUPDACT)==0 && pg->changeProc) + pg->changeProc( pg, inx, &longV ); + change |= (1L<<inx); + } + break; + case PD_LIST: + case PD_DROPLIST: + case PD_COMBOLIST: + longV = wListGetIndex( (wList_p)p->control ); + if (longV != p->oldD.l) { + p->oldD.l = longV; + if ( /*(p->option&PDO_NOUPDUPD)==0 &&*/ p->valueP) + *(wIndex_t*)p->valueP = (wIndex_t)longV; + if ( (p->option&PDO_NOUPDACT)==0 && pg->changeProc) + pg->changeProc( pg, inx, &longV ); + change |= (1L<<inx); + } + break; + case PD_COLORLIST: + dc = wColorSelectButtonGetColor( (wButton_p)p->control ); +#ifdef LATER + inx = wListGetIndex( (wList_p)p->control ); + dc = colorTab[inx].color; +#endif + if (dc != p->oldD.dc) { + p->oldD.dc = dc; + if ( /*(p->option&PDO_NOUPDUPD)==0 &&*/ p->valueP) + *(wDrawColor*)p->valueP = dc; + if ( (p->option&PDO_NOUPDACT)==0 && pg->changeProc) { + pg->changeProc( pg, inx, &longV ); /* COLORNOP */ + } + change |= (1L<<inx); + } + break; + case PD_FLOAT: + if (p->option & PDO_DIM) { + floatV = DecodeDistance( (wString_p)p->control, &valid ); + } else { + floatV = DecodeFloat( (wString_p)p->control, &valid ); + if (valid && (p->option & PDO_ANGLE) ) + floatV = NormalizeAngle( (angleSystem==ANGLE_POLAR)?floatV:-floatV ); + } + if ( !valid ) + break; + if (floatV != p->oldD.f) { + p->oldD.f = floatV; + if ( /*(p->option&PDO_NOUPDUPD)==0 &&*/ p->valueP) + *(FLOAT_T*)p->valueP = floatV; + if ( (p->option&PDO_NOUPDACT)==0 && pg->changeProc) + pg->changeProc( pg, inx, &floatV ); + change |= (1L<<inx); + } + break; + case PD_STRING: + stringV = wStringGetValue( (wString_p)p->control ); + if ( strcmp( stringV, p->oldD.s ) != 0 ) { + if (p->oldD.s) + MyFree( p->oldD.s ); + p->oldD.s = MyStrdup( stringV ); + if ( /*(p->option&PDO_NOUPDUPD)==0 &&*/ p->valueP) + strcpy( (char*)p->valueP, stringV ); + if ( (p->option&PDO_NOUPDACT)==0 && pg->changeProc) + pg->changeProc( pg, inx, CAST_AWAY_CONST stringV ); + change |= (1L<<inx); + } + break; + case PD_MESSAGE: + case PD_BUTTON: + case PD_DRAW: + case PD_TEXT: + case PD_MENU: + case PD_MENUITEM: + break; + } + } +#ifdef PGPROC + if (pg->proc) + pg->proc( PGACT_UPDATE, change ); +#endif + return change; +} + + +EXPORT void ParamLoadData( + paramGroup_p pg ) +{ + FLOAT_T floatV; + const char * stringV; + paramData_p p; + BOOL_T valid; + + for ( p=pg->paramPtr; p<&pg->paramPtr[pg->paramCnt]; p++ ) { + if ( (p->option&PDO_DLGIGNORE) != 0 ) + continue; + if ( p->control == NULL || p->valueP == NULL) + continue; + switch ( p->type ) { + case PD_LONG: + stringV = wStringGetValue( (wString_p)p->control ); + *(long*)p->valueP = atol( stringV ); + break; + case PD_RADIO: + *(long*)p->valueP = wRadioGetValue( (wChoice_p)p->control ); + break; + case PD_TOGGLE: + *(long*)p->valueP = wToggleGetValue( (wChoice_p)p->control ); + break; + case PD_LIST: + case PD_DROPLIST: + case PD_COMBOLIST: + *(wIndex_t*)p->valueP = wListGetIndex( (wList_p)p->control ); + break; + case PD_COLORLIST: + *(wDrawColor*)p->valueP = wColorSelectButtonGetColor( (wButton_p)p->control ); +#ifdef LATER + inx = wListGetIndex( (wList_p)p->control ); + *(wDrawColor*)p->valueP = colorTab[inx].color; +#endif + break; + case PD_FLOAT: + if (p->option & PDO_DIM) { + floatV = DecodeDistance( (wString_p)p->control, &valid ); + } else { + floatV = DecodeFloat( (wString_p)p->control, &valid ); + if (valid && (p->option & PDO_ANGLE) ) + floatV = NormalizeAngle( (angleSystem==ANGLE_POLAR)?floatV:-floatV ); + } + if ( valid ) + *(FLOAT_T*)p->valueP = floatV; + break; + case PD_STRING: + stringV = wStringGetValue( (wString_p)p->control ); + strcpy( (char*)p->valueP, stringV ); + break; + case PD_MESSAGE: + case PD_BUTTON: + case PD_DRAW: + case PD_TEXT: + case PD_MENU: + case PD_MENUITEM: + break; + } + } +} + + +static long ParamIntRestore( + paramGroup_p pg, + int class ) +{ + long change = 0; + int inx; + paramData_p p; + FLOAT_T valR; + char * valS; + paramOldData_t * oldP; + + for ( p=pg->paramPtr,inx=0; p<&pg->paramPtr[pg->paramCnt]; p++,inx++ ) { + oldP = (class==0)?&p->oldD:&p->demoD; + if ( (p->option&PDO_DLGIGNORE) != 0 ) + continue; + if (p->valueP == NULL) + continue; + switch ( p->type ) { + case PD_LONG: + if ( *(long*)p->valueP != oldP->l ) { + /*if ((p->option&PDO_NORSTUPD)==0)*/ + *(long*)p->valueP = oldP->l; + if (p->control) { + wStringSetValue( (wString_p)p->control, FormatLong( oldP->l ) ); + } + change |= (1L<<inx); + } + break; + case PD_RADIO: + if ( *(long*)p->valueP != oldP->l ) { + /*if ((p->option&PDO_NORSTUPD)==0)*/ + *(long*)p->valueP = oldP->l; + if (p->control) + wRadioSetValue( (wChoice_p)p->control, oldP->l ); + change |= (1L<<inx); + } + break; + case PD_TOGGLE: + if ( *(long*)p->valueP != oldP->l ) { + /*if ((p->option&PDO_NORSTUPD)==0)*/ + *(long*)p->valueP = oldP->l; + if (p->control) + wToggleSetValue( (wChoice_p)p->control, oldP->l ); + change |= (1L<<inx); + } + break; + case PD_LIST: + case PD_DROPLIST: + case PD_COMBOLIST: + if ( *(wIndex_t*)p->valueP != (wIndex_t)oldP->l ) { + /*if ((p->option&PDO_NORSTUPD)==0)*/ + *(wIndex_t*)p->valueP = (wIndex_t)oldP->l; + if (p->control) + wListSetIndex( (wList_p)p->control, (wIndex_t)oldP->l ); + change |= (1L<<inx); + } + break; + case PD_COLORLIST: + if ( *(wDrawColor*)p->valueP != oldP->dc ) { + /*if ((p->option&PDO_NORSTUPD)==0)*/ + *(wDrawColor*)p->valueP = oldP->dc; + if (p->control) + wColorSelectButtonSetColor( (wButton_p)p->control, oldP->dc ); /* COLORNOP */ + change |= (1L<<inx); + } + break; + case PD_FLOAT: + if ( *(FLOAT_T*)p->valueP != oldP->f ) { + /*if ((p->option&PDO_NORSTUPD)==0)*/ + *(FLOAT_T*)p->valueP = oldP->f; + if (p->control) { + valR = oldP->f; + if (p->option & PDO_DIM) { + if (p->option & PDO_SMALLDIM) + valS = FormatSmallDistance( valR ); + else + valS = FormatDistance( valR ); + } else { + if (p->option & PDO_ANGLE) + valR = NormalizeAngle( (angleSystem==ANGLE_POLAR)?valR:-valR ); + valS = FormatFloat( valR ); + } + wStringSetValue( (wString_p)p->control, valS ); + } + change |= (1L<<inx); + } + break; + case PD_STRING: + if ( oldP->s && strcmp((char*)p->valueP,oldP->s) != 0 ) { + /*if ((p->option&PDO_NORSTUPD)==0)*/ + strcpy( (char*)p->valueP, oldP->s ); + if (p->control) + wStringSetValue( (wString_p)p->control, oldP->s ); + change |= (1L<<inx); + } + break; + case PD_MESSAGE: + case PD_BUTTON: + case PD_DRAW: + case PD_TEXT: + case PD_MENU: + case PD_MENUITEM: + break; + } + } +#ifdef PGPROC + if (pg->proc) + pg->proc( PGACT_RESTORE, change ); +#endif + return change; +} + + +static void ParamIntSave( + paramGroup_p pg, + int class ) +{ + paramData_p p; + paramOldData_t * oldP; + + for ( p=pg->paramPtr; p<&pg->paramPtr[pg->paramCnt]; p++ ) { + oldP = (class==0)?&p->oldD:&p->demoD; + if (p->valueP) { + switch (p->type) { + case PD_LONG: + case PD_RADIO: + case PD_TOGGLE: + oldP->l = *(long*)p->valueP; + break; + case PD_LIST: + case PD_DROPLIST: + case PD_COMBOLIST: + oldP->l = *(wIndex_t*)p->valueP; + break; + case PD_COLORLIST: + oldP->dc = *(wDrawColor*)p->valueP; + break; + case PD_FLOAT: + oldP->f = *(FLOAT_T*)p->valueP; + break; + case PD_STRING: + if (oldP->s) + MyFree(oldP->s); + oldP->s = MyStrdup( (char*)p->valueP ); + break; + case PD_MESSAGE: + case PD_BUTTON: + case PD_DRAW: + case PD_TEXT: + case PD_MENU: + case PD_MENUITEM: + break; + } + } + } +} + +#ifdef LATER +static void ParamSave( paramGroup_p pg ) +{ + ParamIntSave( pg, 0 ); +} + +static long ParamRestore( paramGroup_p pg ) +{ + return ParamIntRestore( pg, 0 ); +} +#endif + +/**************************************************************************** + * + * + * + */ + +static dynArr_t paramGroups_da; +#define paramGroups(N) DYNARR_N( paramGroup_p, paramGroups_da, N ) + + + +EXPORT void ParamRegister( paramGroup_p pg ) +{ + paramData_p p; + const char * cp; + WDOUBLE_T tmpR; + long valL; + long rgb; + char prefName1[STR_SHORT_SIZE]; + const char *prefSect2, *prefName2; + + DYNARR_APPEND( paramGroup_p, paramGroups_da, 10 ); + paramGroups(paramGroups_da.cnt-1) = pg; + for ( p=pg->paramPtr; p<&pg->paramPtr[pg->paramCnt]; p++ ) { + p->group = pg; + if ( p->nameStr == NULL ) + continue; + sprintf( prefName1, "%s-%s", pg->nameStr, p->nameStr ); + if ( p->type != PD_MENUITEM ) { + (void)GetBalloonHelpStr( prefName1 ); + } + if (p->valueP == NULL || (p->option&PDO_NOPREF) != 0) + continue; + prefSect2 = PREFSECT; + prefName2 = prefName1; + if ( (p->option&PDO_MISC) ) { + prefSect2 = "misc"; + prefName2 = p->nameStr; + } else if ( (p->option&PDO_DRAW) ) { + prefSect2 = "draw"; + prefName2 = p->nameStr; + } else if ( (p->option&PDO_FILE) ) { + prefSect2 = "file"; + prefName2 = p->nameStr; + } else if ( (pg->options&PGO_PREFGROUP) ) { + prefSect2 = pg->nameStr; + prefName2 = p->nameStr; + } else if ( (pg->options&PGO_PREFMISC) ) { + prefSect2 = "misc"; + prefName2 = p->nameStr; + } else if ( (pg->options&PGO_PREFMISCGROUP) ) { + prefSect2 = "misc"; + } else if ( (pg->options&PGO_PREFDRAWGROUP) ) { + prefSect2 = "draw"; + } + cp = strchr( p->nameStr, '\t' ); + if ( cp ) { + /* *cp++ = 0; */ + prefSect2 = cp; + cp = strchr( cp, '\t' ); + if ( cp ) { + /* *cp++ = 0; */ + prefName2 = cp; + } + } + switch (p->type) { + case PD_LONG: + case PD_RADIO: + case PD_TOGGLE: + if ( !wPrefGetInteger( PREFSECT, prefName1, p->valueP, *(long*)p->valueP )) + wPrefGetInteger( prefSect2, prefName2, p->valueP, *(long*)p->valueP ); + break; + case PD_LIST: + case PD_DROPLIST: + case PD_COMBOLIST: + if ( (p->option&PDO_LISTINDEX) ) { + if (!wPrefGetInteger( PREFSECT, prefName1, &valL, *(wIndex_t*)p->valueP )) + wPrefGetInteger( prefSect2, prefName2, &valL, *(wIndex_t*)p->valueP ); + if ( p->control ) + wListSetIndex( (wList_p)p->control, (wIndex_t)valL ); + *(wIndex_t*)p->valueP = (wIndex_t)valL; + } else { + if (!p->control) + break; + cp = wPrefGetString( PREFSECT, prefName1 ); + if ( !cp ) + cp = wPrefGetString( prefSect2, prefName2 ); + if ( !cp ) + break; + *(wIndex_t*)p->valueP = wListFindValue( (wList_p)p->control, cp ); + } + break; + case PD_COLORLIST: + rgb = wDrawGetRGB( *(wDrawColor*)p->valueP ); + if (!wPrefGetInteger( PREFSECT, prefName1, &rgb, rgb )) + wPrefGetInteger( prefSect2, prefName2, &rgb, rgb ); + *(wDrawColor*)p->valueP = wDrawFindColor( rgb ); + break; + case PD_FLOAT: + if (!wPrefGetFloat( PREFSECT, prefName1, &tmpR, *(FLOAT_T*)p->valueP )) + wPrefGetFloat( prefSect2, prefName2, &tmpR, *(FLOAT_T*)p->valueP ); + *(FLOAT_T*)p->valueP = tmpR; + break; + case PD_STRING: + cp = wPrefGetString( PREFSECT, prefName1 ); + if (!cp) + wPrefGetString( prefSect2, prefName2 ); + if (cp) + strcpy( p->valueP, cp ); + else + ((char*)p->valueP)[0] = '\0'; + break; + case PD_MESSAGE: + case PD_BUTTON: + case PD_DRAW: + case PD_TEXT: + case PD_MENU: + case PD_MENUITEM: + case PD_BITMAP: + break; + } + } +} + + + + +EXPORT void ParamUpdatePrefs( void ) +{ + int inx; + paramGroup_p pg; + paramData_p p; + long rgb; + char prefName[STR_SHORT_SIZE]; + int len; + int col; + char * cp; + static wPos_t * colWidths; + static int maxColCnt = 0; + paramListData_t * listDataP; + + for ( inx=0; inx<paramGroups_da.cnt; inx++ ) { + pg = paramGroups(inx); + for ( p=pg->paramPtr; p<&pg->paramPtr[pg->paramCnt]; p++ ) { + if (p->valueP == NULL || p->nameStr == NULL || (p->option&PDO_NOPREF)!=0 ) + continue; + if ( (p->option&PDO_DLGIGNORE) != 0 ) + continue; + sprintf( prefName, "%s-%s", pg->nameStr, p->nameStr ); + switch ( p->type ) { + case PD_LONG: + case PD_RADIO: + case PD_TOGGLE: + wPrefSetInteger( PREFSECT, prefName, *(long*)p->valueP ); + break; + case PD_LIST: + listDataP = (paramListData_t*)p->winData; + if ( p->control && listDataP->colCnt > 0 ) { + if ( maxColCnt < listDataP->colCnt ) { + if ( maxColCnt == 0 ) + colWidths = (wPos_t*)MyMalloc( listDataP->colCnt * sizeof * colWidths ); + else + colWidths = (wPos_t*)MyRealloc( colWidths, listDataP->colCnt * sizeof * colWidths ); + maxColCnt = listDataP->colCnt; + } + len = wListGetColumnWidths( (wList_p)p->control, listDataP->colCnt, colWidths ); + cp = message; + for ( col=0; col<len; col++ ) { + sprintf( cp, "%d ", colWidths[col] ); + cp += strlen(cp); + } + *cp = '\0'; + len = strlen( prefName ); + strcpy( prefName+len, "-columnwidths" ); + wPrefSetString( PREFSECT, prefName, message ); + prefName[len] = '\0'; + } + case PD_DROPLIST: + case PD_COMBOLIST: + if ( (p->option&PDO_LISTINDEX) ) { + wPrefSetInteger( PREFSECT, prefName, *(wIndex_t*)p->valueP ); + } else { + if (p->control) { + wListGetValues( (wList_p)p->control, message, sizeof message, NULL, NULL ); + wPrefSetString( PREFSECT, prefName, message ); + } + } + break; + case PD_COLORLIST: + rgb = wDrawGetRGB( *(wDrawColor*)p->valueP ); + wPrefSetInteger( PREFSECT, prefName, rgb ); + break; + case PD_FLOAT: + wPrefSetFloat( PREFSECT, prefName, *(FLOAT_T*)p->valueP ); + break; + case PD_STRING: + wPrefSetString( PREFSECT, prefName, (char*)p->valueP ); + break; + case PD_MESSAGE: + case PD_BUTTON: + case PD_DRAW: + case PD_TEXT: + case PD_MENU: + case PD_MENUITEM: + case PD_BITMAP: + break; + } + } + } +} + +EXPORT void ParamGroupRecord( + paramGroup_p pg ) +{ + paramData_p p; + long rgb; + + if (recordF == NULL) + return; + for ( p=pg->paramPtr; p<&pg->paramPtr[pg->paramCnt]; p++ ) { + if ( (p->option&PDO_NORECORD) != 0 || p->valueP == NULL || p->nameStr == NULL ) + continue; + if ( (p->option&PDO_DLGIGNORE) != 0 ) + continue; + switch ( p->type ) { + case PD_LONG: + case PD_RADIO: + case PD_TOGGLE: + fprintf( recordF, "PARAMETER %s %s %ld\n", pg->nameStr, p->nameStr, *(long*)p->valueP ); + break; + case PD_LIST: + case PD_DROPLIST: + case PD_COMBOLIST: + if (p->control) + wListGetValues( (wList_p)p->control, message, sizeof message, NULL, NULL ); + else + message[0] = '\0'; + fprintf( recordF, "PARAMETER %s %s %d %s\n", pg->nameStr, p->nameStr, *(wIndex_t*)p->valueP, message ); + break; + case PD_COLORLIST: + rgb = wDrawGetRGB( *(wDrawColor*)p->valueP ); + fprintf( recordF, "PARAMETER %s %s %ld\n", + pg->nameStr, p->nameStr, rgb ); + break; + case PD_FLOAT: + fprintf( recordF, "PARAMETER %s %s %0.3f\n", pg->nameStr, p->nameStr, *(FLOAT_T*)p->valueP ); + break; + case PD_STRING: + fprintf( recordF, "PARAMETER %s %s %s\n", pg->nameStr, p->nameStr, (char*)p->valueP ); + break; + case PD_MESSAGE: + case PD_BUTTON: + case PD_DRAW: + case PD_TEXT: + case PD_MENU: + case PD_MENUITEM: + break; + } + } + if (pg->nameStr) + fprintf( recordF, "PARAMETER GROUP %s\n", pg->nameStr ); + fflush( recordF ); +} + + +EXPORT void ParamStartRecord( void ) +{ + int inx; + paramGroup_p pg; + + if (recordF == NULL) + return; + for ( inx=0; inx<paramGroups_da.cnt; inx++ ) { + pg = paramGroups(inx); + if (pg->options&PGO_RECORD) { + ParamGroupRecord( pg ); + } + } +} + + +EXPORT void ParamRestoreAll( void ) +{ + int inx; + paramGroup_p pg; + + for ( inx=0; inx<paramGroups_da.cnt; inx++ ) { + pg = paramGroups(inx); + ParamIntRestore( pg, 1 ); + } + if ( paramCheckErrorCount > 0 ) { + NoticeMessage( "PARAMCHECK: %d errors", "Ok", NULL, paramCheckErrorCount ); + } +} + + +EXPORT void ParamSaveAll( void ) +{ + int inx; + + for ( inx=0; inx<paramGroups_da.cnt; inx++ ) { + ParamIntSave( paramGroups(inx), 1 ); + paramGroups(inx)->action = 0; + } + paramCheckErrorCount = 0; +} + + +static void ParamButtonPush( void * dp ) +{ + paramData_p p = (paramData_p)dp; + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) { + fprintf( recordF, "PARAMETER %s %s\n", p->group->nameStr, p->nameStr ); + fflush( recordF ); + } + if ( (p->option&PDO_NOPSHACT)==0 ) { + if ( p->valueP ) + ((wButtonCallBack_p)(p->valueP))( p->context ); + else if ( p->group->changeProc) + p->group->changeProc( p->group, p-p->group->paramPtr, NULL); + } +} + + +static void ParamChoicePush( long valL, void * dp ) +{ + paramData_p p = (paramData_p)dp; + + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) { + fprintf( recordF, "PARAMETER %s %s %ld\n", p->group->nameStr, p->nameStr, valL ); + fflush( recordF ); + } + if ( (p->option&PDO_NOPSHUPD)==0 && p->valueP) + *((long*)(p->valueP)) = valL; + if ( (p->option&PDO_NOPSHACT)==0 && p->group->changeProc) + p->group->changeProc( p->group, p-p->group->paramPtr, &valL); +} + + +static void ParamIntegerPush( const char * val, void * dp ) +{ + paramData_p p = (paramData_p)dp; + long valL; + char * cp; + paramIntegerRange_t * irangeP; + + while ( isspace(*val)) val++; + valL = strtol( val, &cp, 10 ); + + wControlSetBalloon( p->control, 0, -5, NULL ); + if ( val == cp ) { + wControlSetBalloon( p->control, 0, -5, _("Invalid Number") ); + return; + } + irangeP = (paramIntegerRange_t*)p->winData; + if ( ( (irangeP->rangechecks&PDO_NORANGECHECK_HIGH) == 0 && valL > irangeP->high ) || + ( (irangeP->rangechecks&PDO_NORANGECHECK_LOW) == 0 && valL < irangeP->low ) ) { + if ( (irangeP->rangechecks&(PDO_NORANGECHECK_HIGH|PDO_NORANGECHECK_LOW)) == PDO_NORANGECHECK_HIGH ) + sprintf( message, _("Enter a value > %ld"), irangeP->low ); + else if ( (irangeP->rangechecks&(PDO_NORANGECHECK_HIGH|PDO_NORANGECHECK_LOW)) == PDO_NORANGECHECK_LOW ) + sprintf( message, _("Enter a value < %ld"), irangeP->high ); + else + sprintf( message, _("Enter a value between %ld and %ld"), irangeP->low, irangeP->high ); + wControlSetBalloon( p->control, 0, -5, message ); + return; + } + wControlSetBalloon( p->control, 0, -5, NULL ); + + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) { + fprintf( recordF, "PARAMETER %s %s %ld\n", p->group->nameStr, p->nameStr, valL ); + fflush( recordF ); + } + if ( (p->option&PDO_NOPSHUPD)==0 && p->valueP) + *((long*)(p->valueP)) = valL; + if ( (p->option&PDO_NOPSHACT)==0 && p->group->changeProc) + p->group->changeProc( p->group, p-p->group->paramPtr, &valL); +} + +/** + * Checks the entered value in a float field. Accepts data entered in the different + * formats for dimensions. Compares the value against limits if specified in that + * entry field description. + * + * \param val IN the vale to check + * \param dp IN the field description + */ + +static void ParamFloatPush( const char * val, void * dp ) +{ + paramData_p p = (paramData_p)dp; + FLOAT_T valF; + BOOL_T valid; + paramFloatRange_t * frangeP; + + if (p->option & PDO_DIM) { + valF = DecodeDistance( (wString_p)p->control, &valid ); + } else { + valF = DecodeFloat( (wString_p)p->control, &valid ); + if (p->option & PDO_ANGLE) + valF = NormalizeAngle( (angleSystem==ANGLE_POLAR)?valF:-valF ); + } + wControlSetBalloon( p->control, 0, -5, NULL ); + if ( !valid ) { + wControlSetBalloon( p->control, 0, -5, decodeErrorStr ); + return; + } + frangeP = (paramFloatRange_t*)p->winData; + if ( ( (frangeP->rangechecks&PDO_NORANGECHECK_HIGH) == 0 && valF > frangeP->high ) || + ( (frangeP->rangechecks&PDO_NORANGECHECK_LOW) == 0 && valF < frangeP->low ) ) { + if ( (frangeP->rangechecks&(PDO_NORANGECHECK_HIGH|PDO_NORANGECHECK_LOW)) == PDO_NORANGECHECK_HIGH ) + sprintf( message, _("Enter a value > %s"), + (p->option&PDO_DIM)?FormatDistance(frangeP->low):FormatFloat(frangeP->low) ); + else if ( (frangeP->rangechecks&(PDO_NORANGECHECK_HIGH|PDO_NORANGECHECK_LOW)) == PDO_NORANGECHECK_LOW ) + sprintf( message, _("Enter a value < %s"), + (p->option&PDO_DIM)?FormatDistance(frangeP->high):FormatFloat(frangeP->high) ); + else + sprintf( message, _("Enter a value between %s and %s"), + (p->option&PDO_DIM)?FormatDistance(frangeP->low):FormatFloat(frangeP->low), + (p->option&PDO_DIM)?FormatDistance(frangeP->high):FormatFloat(frangeP->high) ); + wControlSetBalloon( p->control, 0, -5, message ); + return; + } + wControlSetBalloon( p->control, 0, -5, NULL ); + + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) { + fprintf( recordF, "PARAMETER %s %s %0.6f\n", p->group->nameStr, p->nameStr, valF ); + fflush( recordF ); + } + if ( (p->option&PDO_NOPSHUPD)==0 && p->valueP) + *((FLOAT_T*)(p->valueP)) = valF; + if ( (p->option&PDO_NOPSHACT)==0 && p->group->changeProc && strlen( val )) + p->group->changeProc( p->group, p-p->group->paramPtr, &valF ); +} + + +static void ParamStringPush( const char * val, void * dp ) +{ + paramData_p p = (paramData_p)dp; + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) { + fprintf( recordF, "PARAMETER %s %s %s\n", p->group->nameStr, p->nameStr, val ); + fflush( recordF ); + } + if ( (p->option&PDO_NOPSHUPD)==0 && p->valueP) + strcpy( (char*)p->valueP, val ); + if ( (p->option&PDO_NOPSHACT)==0 && p->group->changeProc) + p->group->changeProc( p->group, p-p->group->paramPtr, CAST_AWAY_CONST val ); +} + + +static void ParamListPush( wIndex_t inx, const char * val, wIndex_t op, void * dp, void * itemContext ) +{ + paramData_p p = (paramData_p)dp; + long valL; + + switch (p->type) { + case PD_LIST: + case PD_DROPLIST: + case PD_COMBOLIST: + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) { + fprintf( recordF, "PARAMETER %s %s %d %s\n", p->group->nameStr, p->nameStr, inx, val ); + fflush( recordF ); + } + if ( (p->option&PDO_NOPSHUPD)==0 && p->valueP) + *(wIndex_t*)(p->valueP) = inx; + if ( (p->option&PDO_NOPSHACT)==0 && p->group->changeProc ) { + valL = inx; + p->group->changeProc( p->group, p-p->group->paramPtr, &valL ); + } + break; +#ifdef LATER + case PD_COLORLIST: + dc = colorTab[inx].color; + rgb = wDrawGetRGB( dc ); + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) { + fprintf( recordF, "PARAMETER %s %s %ld\n", + p->group->nameStr, p->nameStr, rgb ); + fflush( recordF ); + } + if ( (p->option&PDO_NOPSHUPD)==0 && p->valueP) + *(wDrawColor*)(p->valueP) = dc; + if ( (p->option&PDO_NOPSHACT)==0 && p->group->changeProc ) { + ; /* COLOR NOP */ + } + break; +#endif + default: + ; + } +} + + +EXPORT void ParamMenuPush( void * dp ) +{ + paramData_p p = (paramData_p)dp; + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) { + fprintf( recordF, "PARAMETER %s %s\n", p->group->nameStr, p->nameStr ); + fflush( recordF ); + } + if ( (p->option&PDO_NOPSHACT)==0 && p->valueP ) + ((wMenuCallBack_p)(p->valueP))( p->context ); +} + + +static void ParamColorSelectPush( void * dp, wDrawColor dc ) +{ + paramData_p p = (paramData_p)dp; + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) { + fprintf( recordF, "PARAMETER %s %s %ld\n", p->group->nameStr, p->nameStr, wDrawGetRGB(dc) ); + fflush( recordF ); + } + if ( (p->option&PDO_NOPSHUPD)==0 && p->valueP) + *(wDrawColor*)(p->valueP) = dc; + if ( (p->option&PDO_NOPSHACT)==0 && p->group->changeProc ) + p->group->changeProc( p->group, p-p->group->paramPtr, &dc ); +} + + +static void ParamDrawRedraw( wDraw_p d, void * dp, wPos_t w, wPos_t h ) +{ + paramData_p p = (paramData_p)dp; + paramDrawData_t * ddp = (paramDrawData_t*)p->winData; + if ( ddp->redraw ) + ddp->redraw( d, p->context, w, h ); +} + + +static void ParamDrawAction( wDraw_p d, void * dp, wAction_t a, wPos_t w, wPos_t h ) +{ + paramData_p p = (paramData_p)dp; + paramDrawData_t * ddp = (paramDrawData_t*)p->winData; + coOrd pos; + ddp->d->Pix2CoOrd( ddp->d, w, h, &pos ); + if ( recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) { + fprintf( recordF, "PARAMETER %s %s %d %0.3f %0.3f\n", p->group->nameStr, p->nameStr, a, pos.x, pos.y ); + fflush( recordF ); + } + if ( (p->option&PDO_NOPSHACT)== 0 && ddp->action ) + ddp->action( a, pos ); +} + + +static void ParamButtonOk( + paramGroup_p group ) +{ + if ( recordF && group->nameStr ) + fprintf( recordF, "PARAMETER %s %s\n", group->nameStr, "ok" ); { + fflush( recordF ); + } + if ( group->okProc ) + group->okProc( group->okProc==(paramActionOkProc)wHide?((void*)group->win):group ); +} + + +static void ParamButtonCancel( + paramGroup_p group ) +{ + if ( recordF && group->nameStr ) { + fprintf( recordF, "PARAMETER %s %s\n", group->nameStr, "cancel" ); + fflush( recordF ); + } + if ( group->cancelProc ) + group->cancelProc( group->win ); +} + + +#ifdef LATER +EXPORT void ParamChange( paramData_p p ) +{ + FLOAT_T tmpR; + + if (p->valueP==NULL) + return; + + switch (p->type) { + case PD_LONG: + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) + fprintf( recordF, "PARAMETER %s %s %ld\n", p->group->nameStr, p->nameStr, *(long*)p->valueP ); +#ifdef LATER + if ( p->control && (p->option&PDO_NOCONTUPD) == 0 ) { + wStringSetValue( (wString_p)p->control, FormatLong( *(long*)p->valueP ) ); + } +#endif + break; + case PD_RADIO: + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) + fprintf( recordF, "PARAMETER %s %s %ld\n", p->group->nameStr, p->nameStr, *(long*)p->valueP ); +#ifdef LATER + if ( p->control && (p->option&PDO_NOCONTUPD) == 0 ) + wRadioSetValue( (wChoice_p)p->control, *(long*)p->valueP ); +#endif + break; + case PD_TOGGLE: + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) + fprintf( recordF, "PARAMETER %s %s %ld\n", p->group->nameStr, p->nameStr, *(long*)p->valueP ); +#ifdef LATER + if ( p->control && (p->option&PDO_NOCONTUPD) == 0 ) + wToggleSetValue( (wChoice_p)p->control, *(long*)p->valueP ); +#endif + break; + case PD_LIST: + case PD_DROPLIST: + case PD_COMBOLIST: + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) { + fprintf( recordF, "PARAMETER %s %s %d %s\n", p->group->nameStr, p->nameStr, *(wIndex_t*)p->valueP, ??? ); + } +#ifdef LATER + if ( p->control && (p->option&PDO_NOCONTUPD) == 0 ) + wListSetIndex( (wList_p)p->control, *(wIndex_t*)p->valueP ); +#endif + break; + case PD_COLORLIST: + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) + fprintf( recordF, "PARAMETER %s %s %ld\n", p->group->nameStr, p->nameStr, rgb ); +#ifdef LATER + if ( p->control && (p->option&PDO_NOCONTUPD) == 0 ) + wColorSelectButtonSetColor( (wButton_p)p->control, wDrawFindRGB(rgb) ); +#endif + break; + case PD_FLOAT: + tmpR = *(FLOAT_T*)p->valueP; + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) + fprintf( recordF, "PARAMETER %s %s %0.6f\n", p->group->nameStr, p->nameStr, tmpR ); +#ifdef LATER + if ( p->control && (p->option&PDO_NOCONTUPD) == 0 ) { + if (p->option&PDO_DIM) +#endif + if (p->option&PDO_ANGLE) + tmpR = NormalizeAngle( (angleSystem==ANGLE_POLAR)?tmpR:-tmpR ); + wStringSetValue( (wString_p)p->control, tmpR ); + } + break; + case PD_STRING: + if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) + fprintf( recordF, "PARAMETER %s %s %s\n", p->group->nameStr, p->nameStr, (char*)p->valueP ); +#ifdef LATER + if ( p->control && (p->option&PDO_NOCONTUPD) == 0 ) + wStringSetValue( (wString_p)p->control, (char*)p->valueP ); +#endif + break; + case PD_MESSAGE: + case PD_BUTTON: + case PD_DRAW: + case PD_TEXT: + case PD_MENU: + case PD_MENUITEM: + break; + } +} +#endif + + +EXPORT int paramHiliteFast = FALSE; +EXPORT void ParamHilite( + wWin_p win, + wControl_p control, + BOOL_T hilite ) +{ + if ( win != NULL && wWinIsVisible(win) == FALSE ) return; + if ( control == NULL ) return; + if ( !paramTogglePlaybackHilite ) return; + if ( hilite ) { + wControlHilite( control, TRUE ); + wFlush(); + if ( !paramHiliteFast ) + wPause(500); + } else { + if ( !paramHiliteFast ) + wPause(500); + wControlHilite( control, FALSE ); + wFlush(); + } +} + + +static void ParamPlayback( char * line ) +{ + paramGroup_p pg; + paramData_p p; + long valL; + FLOAT_T valF, valF1; + int len, len1, len2; + wIndex_t inx; + void * listContext, * itemContext; + long rgb; + wDrawColor dc; + wButton_p button; + paramDrawData_t * ddp; + wAction_t a; + coOrd pos; + char * valS; + char *oldLocale = NULL; + + if ( strncmp( line, "GROUP ", 6 ) == 0 ) { +#ifdef PGPROC + for ( inx=0; inx<paramGroups_da.cnt; inx++ ) { + pg = paramGroups(inx); + if ( pg->name && strncmp( line+6, pg->name, strlen( pg->name ) ) == 0 ) { + if ( pg->proc ) { + pg->proc( PGACT_PARAM, pg->action ); + } + pg->action = 0; + } + } +#endif + return; + } + + for ( inx=0; inx<paramGroups_da.cnt; inx++ ) { + pg = paramGroups(inx); + if ( pg->nameStr == NULL ) + continue; + len1 = strlen( pg->nameStr ); + if ( strncmp( pg->nameStr, line, len1 ) != 0 || + line[len1] != ' ' ) + continue; + for ( p=pg->paramPtr,inx=0; inx<pg->paramCnt; p++,inx++ ) { + if ( p->nameStr == NULL ) + continue; + len2 = strlen( p->nameStr ); + if ( strncmp(p->nameStr, line+len1+1, len2) != 0 || + (line[len1+1+len2] != ' ' && line[len1+1+len2] != '\0') ) + continue; + len = len1 + 1 + len2 + 1; + if ( p->type != PD_DRAW && p->type != PD_MESSAGE && p->type != PD_MENU && p->type != PD_MENUITEM ) + ParamHilite( p->group->win, p->control, TRUE ); + switch (p->type) { + case PD_BUTTON: + if (p->valueP) + ((wButtonCallBack_p)(p->valueP))( p->context ); + if (playbackTimer == 0 && p->control) { + wButtonSetBusy( (wButton_p)p->control, TRUE ); + wFlush(); + wPause( 500 ); + wButtonSetBusy( (wButton_p)p->control, FALSE ); + wFlush(); + } + break; + case PD_LONG: + valL = atol( line+len ); + if (p->valueP) + *(long*)p->valueP = valL; + if (p->control) { + wStringSetValue( (wString_p)p->control, FormatLong( valL ) ); + wFlush(); + } + if (pg->changeProc) + pg->changeProc( pg, inx, &valL ); + break; + case PD_RADIO: + valL = atol( line+len ); + if (p->valueP) + *(long*)p->valueP = valL; + if (p->control) { + wRadioSetValue( (wChoice_p)p->control, valL ); + wFlush(); + } + if (pg->changeProc) + pg->changeProc( pg, inx, &valL ); + break; + case PD_TOGGLE: + valL = atol( line+len ); + if (p->valueP) + *(long*)p->valueP = valL; + if (p->control) { + wToggleSetValue( (wChoice_p)p->control, valL ); + wFlush(); + } + if (pg->changeProc) + pg->changeProc( pg, inx, &valL ); + break; + case PD_LIST: + case PD_DROPLIST: + case PD_COMBOLIST: + line += len; + valL = strtol( line, &valS, 10 ); + if ( valS ) + valS++; + else + valS = ""; + if ( p->control != NULL ) { + if ( (p->option&PDO_LISTINDEX) == 0 ) { + if ( valL < 0 ) { + wListSetValue( (wList_p)p->control, valS ); + } else { + valL = wListFindValue( (wList_p)p->control, valS ); + if (valL < 0) { + NoticeMessage( MSG_PLAYBACK_LISTENTRY, _("Ok"), NULL, line ); + break; + } + wListSetIndex( (wList_p)p->control, (wIndex_t)valL ); + } + } else { + wListSetIndex( (wList_p)p->control, (wIndex_t)valL ); + } + wFlush(); + wListGetValues( (wList_p)p->control, message, sizeof message, &listContext, &itemContext ); + } else if ( (p->option&PDO_LISTINDEX) == 0 ) { + break; + } + if (p->valueP) + *(wIndex_t*)p->valueP = (wIndex_t)valL; + if (pg->changeProc) { + pg->changeProc( pg, inx, &valL ); + } + break; + case PD_COLORLIST: + line += len; + rgb = atol( line ); + dc = wDrawFindColor( rgb ); + if ( p->control) + wColorSelectButtonSetColor( (wButton_p)p->control, dc ); +#ifdef LATER + valL = ColorTabLookup( dc ); + if (p->control) { + wListSetIndex( (wList_p)p->control, (wIndex_t)valL ); + wFlush(); + } +#endif + if (p->valueP) + *(wDrawColor*)p->valueP = dc; + if (pg->changeProc) { + /* COLORNOP */ + pg->changeProc( pg, inx, &valL ); + } + break; + case PD_FLOAT: + oldLocale = SaveLocale("C"); + valF = valF1 = atof( line+len ); + RestoreLocale(oldLocale); + if (p->valueP) + *(FLOAT_T*)p->valueP = valF; + if (p->option&PDO_DIM) { + if ( p->option&PDO_SMALLDIM ) + valS = FormatSmallDistance( valF ); + else + valS = FormatDistance( valF ); + } else { + if (p->option&PDO_ANGLE) + valF1 = NormalizeAngle( (angleSystem==ANGLE_POLAR)?valF1:-valF1 ); + valS = FormatFloat( valF ); + } + if (p->control) { + wStringSetValue( (wString_p)p->control, valS ); + wFlush(); + } + if (pg->changeProc) + pg->changeProc( pg, inx, &valF ); + break; + case PD_STRING: + line += len; + while ( *line == ' ' ) line++; + Stripcr( line ); + if (p->valueP) + strcpy( (char*)p->valueP, line ); + if (p->control) { + wStringSetValue( (wString_p)p->control, line ); + wFlush(); + } + if (pg->changeProc) + pg->changeProc( pg, inx, line ); + break; + case PD_DRAW: + ddp = (paramDrawData_t*)p->winData; + if ( ddp->action == NULL ) + break; + a = (wAction_t)strtol( line+len, &line, 10 ); + pos.x = strtod( line, &line ); + pos.y = strtod( line, NULL ); + PlaybackMouse( ddp->action, ddp->d, a, pos, drawColorBlack ); + break; + case PD_MESSAGE: + case PD_TEXT: + case PD_MENU: + break; + case PD_MENUITEM: + if (p->valueP) { + if ( (p->option&IC_PLAYBACK_PUSH) != 0 ) + PlaybackButtonMouse( (wIndex_t)(long)p->context ); + ((wButtonCallBack_p)(p->valueP))( p->context ); + } + break; + } + if ( p->type != PD_DRAW && p->type != PD_MESSAGE && p->type != PD_MENU && p->type != PD_MENUITEM ) + ParamHilite( p->group->win, p->control, FALSE ); +#ifdef HUH + pg->action |= p->change; +#endif + return; + } + button = NULL; + if ( strcmp("ok", line+len1+1) == 0 ) { + ParamHilite( pg->win, (wControl_p)pg->okB, TRUE ); + if ( pg->okProc ) + pg->okProc( pg ); + button = pg->okB; + } else if ( strcmp("cancel", line+len1+1) == 0 ) { + ParamHilite( pg->win, (wControl_p)pg->cancelB, TRUE ); + if ( pg->cancelProc ) + pg->cancelProc( pg->win ); + button = pg->cancelB; + } + if ( playbackTimer == 0 && button ) { + wButtonSetBusy( button, TRUE ); + wFlush(); + wPause( 500 ); + wButtonSetBusy( button, FALSE ); + wFlush(); + } + ParamHilite( pg->win, (wControl_p)button, FALSE ); + if ( !button ) + NoticeMessage( "Unknown PARAM: %s", _("Ok"), NULL, line ); + return; + } + NoticeMessage( "Unknown PARAM: %s", _("Ok"), NULL, line ); +} + + +static void ParamCheck( char * line ) +{ + paramGroup_p pg; + paramData_p p; + long valL; + FLOAT_T valF, diffF; + int len, len1, len2; + wIndex_t inx; + void * listContext, * itemContext; + char * valS; + char * expVal=NULL, * actVal=NULL; + char expNum[20], actNum[20]; + BOOL_T hasError = FALSE; + FILE * f; + + for ( inx=0; inx<paramGroups_da.cnt; inx++ ) { + pg = paramGroups(inx); + if ( pg->nameStr == NULL ) + continue; + len1 = strlen( pg->nameStr ); + if ( strncmp( pg->nameStr, line, len1 ) != 0 || + line[len1] != ' ' ) + continue; + for ( p=pg->paramPtr,inx=0; inx<pg->paramCnt; p++,inx++ ) { + if ( p->nameStr == NULL ) + continue; + len2 = strlen( p->nameStr ); + if ( strncmp(p->nameStr, line+len1+1, len2) != 0 || + (line[len1+1+len2] != ' ' && line[len1+1+len2] != '\0') ) + continue; + if ( p->valueP == NULL ) + return; + len = len1 + 1 + len2 + 1; + switch (p->type) { + case PD_BUTTON: + break; + case PD_LONG: + case PD_RADIO: + case PD_TOGGLE: + valL = atol( line+len ); + if ( *(long*)p->valueP != valL ) { + sprintf( expNum, "%ld", valL ); + sprintf( actNum, "%ld", *(long*)p->valueP ); + expVal = expNum; + actVal = actNum; + hasError = TRUE; + } + break; + case PD_LIST: + case PD_DROPLIST: + case PD_COMBOLIST: + line += len; + if ( p->control == NULL ) + break; + valL = strtol( line, &valS, 10 ); + if ( valS ) { + if ( valS[0] == ' ' ) + valS++; + } else { + valS = ""; + } + if ( (p->option&PDO_LISTINDEX) != 0 ) { + if ( *(long*)p->valueP != valL ) { + sprintf( expNum, "%ld", valL ); + sprintf( actNum, "%d", *(wIndex_t*)p->valueP ); + expVal = expNum; + actVal = actNum; + hasError = TRUE; + } + } else { + wListGetValues( (wList_p)p->control, message, sizeof message, &listContext, &itemContext ); + if ( strcasecmp( message, valS ) != 0 ) { + expVal = valS; + actVal = message; + hasError = TRUE; + } + } + break; + case PD_COLORLIST: + break; + case PD_FLOAT: + valF = atof( line+len ); + diffF = fabs( *(FLOAT_T*)p->valueP - valF ); + if ( diffF > 0.001 ) { + sprintf( expNum, "%0.3f", valF ); + sprintf( actNum, "%0.3f", *(FLOAT_T*)p->valueP ); + expVal = expNum; + actVal = actNum; + hasError = TRUE; + } + break; + case PD_STRING: + line += len; + while ( *line == ' ' ) line++; + valS = CAST_AWAY_CONST wStringGetValue( (wString_p)p->control ); + if ( strcasecmp( line, (char*)p->valueP ) != 0 ) { + expVal = line; + actVal = (char*)p->valueP; + hasError = TRUE; + } + break; + case PD_DRAW: + case PD_MESSAGE: + case PD_TEXT: + case PD_MENU: + case PD_MENUITEM: + break; + } + if ( hasError ) { + f = fopen( "error.log", "a" ); + if ( f==NULL ) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, "PARAMCHECK LOG", "error.log", strerror(errno) ); + } else { + fprintf( f, "CHECK: %s:%d: %s-%s: exp: %s, act=%s\n", + paramFileName, paramLineNum, pg->nameStr, p->nameStr, expVal, actVal ); + fclose( f ); + } + if ( paramCheckShowErrors ) + NoticeMessage( "CHECK: %d: %s-%s: exp: %s, act=%s", _("Ok"), NULL, paramLineNum, pg->nameStr, p->nameStr, expVal, actVal ); + paramCheckErrorCount++; + } + return; + } + } + NoticeMessage( "Unknown PARAMCHECK: %s", _("Ok"), NULL, line ); +} + +/* + * + */ + + + +static void ParamCreateControl( + paramData_p pd, + char * helpStr, + wPos_t xx, + wPos_t yy ) +{ + paramFloatRange_t * floatRangeP; + paramIntegerRange_t * integerRangeP; + paramDrawData_t * drawDataP; + paramTextData_t * textDataP; + paramListData_t * listDataP; + wIcon_p iconP; + wDrawColor color = wDrawColorBlack; + + wWin_p win; + wPos_t w; + wPos_t colWidth; + static wPos_t *colWidths; + static wBool_t *colRightJust; + static wBool_t maxColCnt = 0; + int col; + const char *cp; + char *cq; + static wMenu_p menu = NULL; + + if ( ( win = pd->group->win ) == NULL ) + win = mainW; + + + switch (pd->type) { + case PD_FLOAT: + floatRangeP = pd->winData; + w = floatRangeP->width?floatRangeP->width:100; + pd->control = (wControl_p)wStringCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption, w, NULL, 0, ParamFloatPush, pd ); + break; + case PD_LONG: + integerRangeP = pd->winData; + w = integerRangeP->width?integerRangeP->width:100; + pd->control = (wControl_p)wStringCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption, w, NULL, 0, ParamIntegerPush, pd ); + break; + case PD_STRING: + w = pd->winData?(wPos_t)(long)pd->winData:(wPos_t)250; + pd->control = (wControl_p)wStringCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption, w, (pd->option&PDO_NOPSHUPD)?NULL:pd->valueP, 0, ParamStringPush, pd ); + break; + case PD_RADIO: + pd->control = (wControl_p)wRadioCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption, pd->winData, NULL, ParamChoicePush, pd ); + break; + case PD_TOGGLE: + pd->control = (wControl_p)wToggleCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption, pd->winData, NULL, ParamChoicePush, pd ); + break; + case PD_LIST: + listDataP = (paramListData_t*)pd->winData; + if ( listDataP->colCnt > 1 ) { + if ( maxColCnt < listDataP->colCnt ) { + if ( maxColCnt == 0 ) { + colWidths = (wPos_t*)MyMalloc( listDataP->colCnt * sizeof *colWidths ); + colRightJust = (wBool_t*)MyMalloc( listDataP->colCnt * sizeof *colRightJust ); + } else { + colWidths = (wPos_t*)MyRealloc( colWidths, listDataP->colCnt * sizeof *colWidths ); + colRightJust = (wBool_t*)MyRealloc( colRightJust, listDataP->colCnt * sizeof *colRightJust ); + } + maxColCnt = listDataP->colCnt; + } + for ( col=0; col<listDataP->colCnt; col++ ) { + colRightJust[col] = listDataP->colWidths[col]<0; + colWidths[col] = abs(listDataP->colWidths[col]); + } + sprintf( message, "%s-%s-%s", pd->group->nameStr, pd->nameStr, "columnwidths" ); + cp = wPrefGetString( PREFSECT, message ); + if ( cp != NULL ) { + for ( col=0; col<listDataP->colCnt; col++ ) { + colWidth = (wPos_t)strtol( cp, &cq, 10 ); + if ( cp == cq ) + break; + colWidths[col] = colWidth; + cp = cq; + } + } + } + pd->control = (wControl_p)wListCreate( win, xx, yy, helpStr, _(pd->winLabel), + pd->winOption, listDataP->number, listDataP->width, listDataP->colCnt, + (listDataP->colCnt>1?colWidths:NULL), + (listDataP->colCnt>1?colRightJust:NULL), + listDataP->colTitles, NULL, ParamListPush, pd ); + listDataP->height = wControlGetHeight( pd->control ); + break; + case PD_DROPLIST: + w = pd->winData?(wPos_t)(long)pd->winData:(wPos_t)100; + pd->control = (wControl_p)wDropListCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption, 10, w, NULL, ParamListPush, pd ); + break; + case PD_COMBOLIST: + listDataP = (paramListData_t*)pd->winData; + pd->control = (wControl_p)wComboListCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption, listDataP->number, listDataP->width, NULL, ParamListPush, pd ); + listDataP->height = wControlGetHeight( pd->control ); + break; + case PD_COLORLIST: + pd->control = (wControl_p)wColorSelectButtonCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption, 0, &color, ParamColorSelectPush, pd ); + break; + case PD_MESSAGE: + if ( pd->winData != 0 ) + w = (wPos_t)(long)pd->winData; + else if (pd->valueP) + w = wLabelWidth( _(pd->valueP) ); + else + w = 150; + pd->control = (wControl_p)wMessageCreateEx( win, xx, yy, _(pd->winLabel), w, pd->valueP?_(pd->valueP):" ", pd->winOption ); + break; + case PD_BUTTON: + pd->control = (wControl_p)wButtonCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption, 0, ParamButtonPush, pd ); + break; + case PD_MENU: + menu = wMenuCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption ); + pd->control = (wControl_p)menu; + break; + case PD_MENUITEM: + pd->control = (wControl_p)wMenuPushCreate( menu, helpStr, _(pd->winLabel), 0, ParamMenuPush, pd ); + break; + case PD_DRAW: + drawDataP = pd->winData; + pd->control = (wControl_p)wDrawCreate( win, xx, yy, helpStr, pd->winOption, drawDataP->width, drawDataP->height, pd, ParamDrawRedraw, ParamDrawAction ); + if ( drawDataP->d ) { + drawDataP->d->d = (wDraw_p)pd->control; + drawDataP->d->dpi = wDrawGetDPI( drawDataP->d->d ); + } + break; + case PD_TEXT: + textDataP = pd->winData; + pd->control = (wControl_p)wTextCreate( win, xx, yy, helpStr, NULL, pd->winOption, textDataP->width, textDataP->height ); + if ( (pd->winOption&BO_READONLY) == 0 ) + wTextSetReadonly( (wText_p)pd->control, FALSE ); + break; + case PD_BITMAP: + iconP = pd->winData; + pd->control = (wControl_p)wBitmapCreate( win, xx, yy, pd->winOption, iconP ); + break; + default: + AbortProg( "paramCreatePG" ); + } + +} + + +static void ParamPositionControl( + paramData_p pd, + char * helpStr, + wPos_t xx, + wPos_t yy ) +{ + paramDrawData_t * drawDataP; + paramTextData_t * textDataP; + paramListData_t * listDataP; + wPos_t winW, winH, ctlW, ctlH; + + if ( pd->type != PD_MENUITEM ) + wControlSetPos( pd->control, xx, yy ); + if ( pd->option&PDO_DLGRESIZE ) { + wWinGetSize( pd->group->win, &winW, &winH ); + switch (pd->type) { + case PD_LIST: + case PD_COMBOLIST: + case PD_DROPLIST: + if ( pd->type == PD_DROPLIST ) { + ctlW = pd->winData?(wPos_t)(long)pd->winData:(wPos_t)100; + ctlH = wControlGetHeight( pd->control ); + } else { + listDataP = (paramListData_t*)pd->winData; + ctlW = listDataP->width; + ctlH = listDataP->height; + } + if ( (pd->option&PDO_DLGRESIZE) == 0 ) + break; + if ( (pd->option&PDO_DLGRESIZEW) != 0 ) + ctlW = winW - (pd->group->origW-ctlW); + if ( (pd->option&PDO_DLGRESIZEH) != 0 ) + ctlH = winH - (pd->group->origH-ctlH); + wListSetSize( (wList_p)pd->control, ctlW, ctlH ); + break; + case PD_DRAW: + drawDataP = pd->winData; + if ( (pd->option&PDO_DLGRESIZEW) ) + ctlW = winW - (pd->group->origW-drawDataP->width); + else + ctlW = wControlGetWidth( pd->control ); + if ( (pd->option&PDO_DLGRESIZEH) ) + ctlH = winH - (pd->group->origH-drawDataP->height); + else + ctlH = wControlGetHeight( pd->control ); + wDrawSetSize( (wDraw_p)pd->control, ctlW, ctlH ); + if ( drawDataP->redraw ) + drawDataP->redraw( (wDraw_p)pd->control, pd->context, ctlW, ctlH ); + break; + case PD_TEXT: + textDataP = pd->winData; + ctlW = textDataP->width; + ctlH = textDataP->height; + if ( (pd->winOption&BT_CHARUNITS) ) + wTextComputeSize( (wText_p)pd->control, ctlW, ctlH, &ctlW, &ctlH ); + if ( (pd->option&PDO_DLGRESIZEW) ) + ctlW = winW - (pd->group->origW-ctlW); + else + ctlW = wControlGetWidth( pd->control ); + if ( (pd->option&PDO_DLGRESIZEH) ) + ctlH = winH - (pd->group->origH-ctlH); + else + ctlH = wControlGetHeight( pd->control ); + wTextSetSize( (wText_p)pd->control, ctlW, ctlH ); + break; + case PD_STRING: + ctlW = pd->winData?(wPos_t)(long)pd->winData:(wPos_t)250; + if ( (pd->option&PDO_DLGRESIZEW) ) { + ctlW = winW - (pd->group->origW-ctlW); + wStringSetWidth( (wString_p)pd->control, ctlW ); + } + break; + case PD_MESSAGE: + ctlW = pd->winData?(wPos_t)(long)pd->winData:(wPos_t)150; + if ( (pd->option&PDO_DLGRESIZEW) ) { + ctlW = winW - (pd->group->origW-ctlW); + wMessageSetWidth( (wMessage_p)pd->control, ctlW ); + } + break; + default: + AbortProg( "paramPositionControl" ); + } + } +} + + +typedef void (*layoutControlsProc)(paramData_p, char *, wPos_t, wPos_t ); +static void LayoutControls( + paramGroup_p group, + layoutControlsProc proc, + wPos_t * retW, + wPos_t * retH ) +{ + struct { + struct { wPos_t x, y; } orig, term; + } controlK, columnK, windowK; + wPos_t controlSize_x; + wPos_t controlSize_y; + paramData_p pd; + wPos_t w; + BOOL_T hasBox; + wPos_t boxTop; + wPos_t boxPos[10]; + int boxCnt = 0; + int box; + int inx; + wPos_t labelW[100]; + int lastLabelPos, currLabelPos; + char helpStr[STR_SHORT_SIZE], * helpStrP; + BOOL_T inCmdButtons = FALSE; + wButton_p lastB = NULL; + BOOL_T areCmdButtons = FALSE; + + strcpy( helpStr, group->nameStr ); + helpStrP = helpStr+strlen(helpStr); + *helpStrP++ = '-'; + *helpStrP = 0; + controlK.orig.x = 0; + hasBox = FALSE; + memset( boxPos, 0, sizeof boxPos ); + memset( labelW, 0, sizeof labelW ); + lastLabelPos = 0; + currLabelPos = 0; + for ( pd=group->paramPtr; pd<&group->paramPtr[group->paramCnt]; pd++,currLabelPos++ ) { + if ( (pd->option&PDO_DLGIGNORE) != 0 ) + continue; + if ( (pd->option&PDO_DLGBOXEND) ) + hasBox = TRUE; + if ( (pd->option&(PDO_DLGRESETMARGIN|PDO_DLGNEWCOLUMN|PDO_DLGCMDBUTTON)) ) { + for ( inx=lastLabelPos; inx<currLabelPos; inx++ ) + labelW[inx] = controlK.orig.x; + controlK.orig.x = 0; + lastLabelPos = currLabelPos; + } + if ( pd->winLabel && (pd->option&(PDO_DLGIGNORELABELWIDTH|PDO_DLGHORZ))==0 && + pd->type!=PD_BUTTON && + pd->type!=PD_MENU && + pd->type!=PD_MENUITEM) { + w = wLabelWidth( _(pd->winLabel) ); + if ( w > controlK.orig.x ) + controlK.orig.x = w; + } + } + for ( inx=lastLabelPos; inx<group->paramCnt; inx++ ) + labelW[inx] = controlK.orig.x; + for ( inx=0; inx<group->paramCnt; inx++ ) + if ( (group->paramPtr[inx].option&PDO_DLGNOLABELALIGN) != 0 ) + labelW[inx] = 0; + + LOG( log_paramLayout, 1, ("Layout %s B?=%s\n", group->nameStr, hasBox?"T":"F" ) ) + + windowK.orig.x = DlgSepLeft + (hasBox?DlgSepFrmLeft:0); + windowK.orig.y = DlgSepTop + (hasBox?DlgSepFrmTop:0); + windowK.term = windowK.orig; + controlK = columnK = windowK; + controlK.orig.x += labelW[0]; + + for ( pd = group->paramPtr,inx=0; pd<&group->paramPtr[group->paramCnt]; pd++,inx++ ) { + LOG( log_paramLayout, 1, ("%2d: Col %dx%d..%dx%d Ctl %dx%d..%dx%d\n", inx, + columnK.orig.x, columnK.orig.y, columnK.term.x, columnK.term.y, + controlK.orig.x, controlK.orig.y, controlK.term.x, controlK.term.y ) ) + if ( (pd->option&PDO_DLGIGNORE) != 0 ) + goto SkipControl; + if ( pd->type == PD_MENUITEM ) { + proc( pd, helpStr, 0, 0 ); + continue; + } + /* + * Set control orig + */ + if ( (pd->option&PDO_DLGNEWCOLUMN) ) { + columnK.orig.x = columnK.term.x; + columnK.orig.x += ((pd->option&PDO_DLGWIDE)?10:DlgSepNarrow); + columnK.term.y = columnK.orig.y; + controlK.orig.x = columnK.orig.x + labelW[inx]; + controlK.orig.y = columnK.orig.y; + } else if ( (pd->option&PDO_DLGHORZ) ) { + controlK.orig.x = controlK.term.x; + if ( (pd->option&PDO_DLGWIDE) ) + controlK.orig.x += 10; + else if ( (pd->option&PDO_DLGNARROW)== 0) + controlK.orig.x += 3; + if ( pd->winLabel && ( pd->type!=PD_BUTTON ) ) + controlK.orig.x += wLabelWidth( _(pd->winLabel) ); + } else if ( inx != 0 ) { + controlK.orig.x = columnK.orig.x + labelW[inx]; + controlK.orig.y = controlK.term.y; + if ( (pd->option&PDO_DLGWIDE) ) + controlK.orig.y += 10; + else if ( (pd->option&PDO_DLGNARROW)== 0) + controlK.orig.y += 3; + } + if ( (pd->option&PDO_DLGSETY) ) { + columnK.term.x = controlK.orig.x; + columnK.orig.y = controlK.orig.y; + } + /* + * Custom layout and create/postion control + */ + if (group->layoutProc) + group->layoutProc( pd, inx, columnK.orig.x+labelW[inx], &controlK.orig.x, &controlK.orig.y ); + if ( pd->nameStr ) + strcpy( helpStrP, pd->nameStr ); + proc( pd, helpStr, controlK.orig.x, controlK.orig.y ); + /* + * Set control term + */ + controlSize_x = wControlGetWidth( pd->control ); + controlSize_y = wControlGetHeight( pd->control ); + controlK.term.x = controlK.orig.x+controlSize_x; + if ( (pd->option&PDO_DLGHORZ)==0 || + controlK.term.y < controlK.orig.y+controlSize_y ) + controlK.term.y = controlK.orig.y+controlSize_y; + if ( retW && pd->nameStr ) { + char * cp; + strcpy( message, pd->nameStr ); + for ( cp=message; *cp; cp++ ) if ( *cp == '-' ) *cp = '_'; + LOG( log_hotspot, 1, ( "popup %d %d %d %d _%s_%s\n", + controlK.orig.x+hotspotOffsetX, controlK.orig.y+hotspotOffsetY, + controlSize_x, controlSize_y, + group->nameStr, message ) ) + } + /* + * Set column term + */ + if ( (pd->option&PDO_DLGIGNOREX) == 0 ) { + if ( (pd->option&PDO_DLGUNDERCMDBUTT) == 0 ) { + if ( columnK.term.x < controlK.term.x ) + columnK.term.x = controlK.term.x; + } else { + if ( columnK.term.x < controlK.term.x-90 ) + columnK.term.x = controlK.term.x-90; + } + } + if ( columnK.term.y < controlK.term.y ) + columnK.term.y = controlK.term.y; + if ( hasBox ) + if ( boxPos[boxCnt] < columnK.term.y+2 ) + boxPos[boxCnt] = columnK.term.y+2; + if ( (pd->option&PDO_DLGBOXEND) ) { + columnK.term.y += 8; + boxCnt++; + controlK.term.y = columnK.term.y; + } + /* + * Set window term + */ + if ( windowK.term.x < columnK.term.x ) + windowK.term.x = columnK.term.x; + if ( windowK.term.y < columnK.term.y ) + windowK.term.y = columnK.term.y; + if ( (pd[1].option&PDO_DLGCMDBUTTON) ) + areCmdButtons = TRUE; +SkipControl: + if ( (!inCmdButtons) && + (pd==&group->paramPtr[group->paramCnt-1] || (pd[1].option&PDO_DLGCMDBUTTON)) ) { + columnK.orig.x = columnK.term.x + DlgSepMid; + if ( boxCnt ) { + boxTop = DlgSepTop; + if ( group->boxs == NULL ) { + group->boxs = (wBox_p*)MyMalloc( boxCnt * sizeof *(wBox_p*)0 ); + for ( box=0; box<boxCnt; box++ ) { + group->boxs[box] = wBoxCreate( group->win, DlgSepLeft, boxTop, NULL, wBoxBelow, columnK.term.x, boxPos[box]-boxTop ); + boxTop = boxPos[box] + 4; + } + } else { + for ( box=0; box<boxCnt; box++ ) { + wControlSetPos( (wControl_p)group->boxs[box], DlgSepLeft, boxTop ); + wBoxSetSize( group->boxs[box], columnK.term.x, boxPos[box]-boxTop ); + boxTop = boxPos[box] + 4; + } + } + columnK.orig.x += DlgSepFrmRight; + } + columnK.orig.y = columnK.term.y = DlgSepTop; + controlK = columnK; + if ( group->okB ) { + wControlSetPos( (wControl_p)(lastB=group->okB), columnK.orig.x, columnK.orig.y ); + controlK.term.y += wControlGetHeight((wControl_p)group->okB); + columnK.term.y = controlK.term.y + 3; + } + inCmdButtons = TRUE; + } + LOG( log_paramLayout, 1, (" Col %dx%d..%dx%d Ctl %dx%d..%dx%d\n", + columnK.orig.x, columnK.orig.y, columnK.term.x, columnK.term.y, + controlK.orig.x, controlK.orig.y, controlK.term.x, controlK.term.y ) ) + if ( windowK.term.x < columnK.term.x ) + windowK.term.x = columnK.term.x; + if ( windowK.term.y < columnK.term.y ) + windowK.term.y = columnK.term.y; + } + if ( group->cancelB ) { + if ( areCmdButtons ) + columnK.term.y += 10; + else if ( group->okB ) + columnK.term.y += 3; + wControlSetPos( (wControl_p)(lastB=group->cancelB), columnK.orig.x, columnK.term.y ); + columnK.term.y += wControlGetHeight((wControl_p)group->cancelB); + } + if ( group->helpB ) { + columnK.term.y += 10; + wControlSetPos( (wControl_p)(lastB=group->helpB), columnK.orig.x, columnK.term.y ); + columnK.term.y += wControlGetHeight((wControl_p)group->helpB); + } + if ( lastB ) { + controlK.term.x = controlK.orig.x + wControlGetWidth((wControl_p)lastB); + if ( columnK.term.x < controlK.term.x ) + columnK.term.x = controlK.term.x; + } + if ( windowK.term.x < columnK.term.x ) + windowK.term.x = columnK.term.x; + if ( windowK.term.y < columnK.term.y ) + windowK.term.y = columnK.term.y; + + if ( retW ) + *retW = windowK.term.x; + if ( retH ) + *retH = windowK.term.y; +} + + +static void ParamDlgProc( + wWin_p win, + winProcEvent e, + void * data ) +{ + paramGroup_p pg = (paramGroup_p)data; + switch (e) { + case wClose_e: + if ( pg->changeProc ) + pg->changeProc( pg, -1, NULL ); + if ( (pg->options&PGO_NODEFAULTPROC) == 0 ) + DefaultProc( win, wClose_e, data ); + break; + case wResize_e: + LayoutControls( pg, ParamPositionControl, NULL, NULL ); + break; + default: + break; + } +} + + + +EXPORT wWin_p ParamCreateDialog( + paramGroup_p group, + char * title, + char * okLabel, + paramActionOkProc okProc, + paramActionCancelProc cancelProc, + BOOL_T needHelpButton, + paramLayoutProc layoutProc, + long winOption, + paramChangeProc changeProc ) +{ + char helpStr[STR_SHORT_SIZE]; + wPos_t w0, h0; + wButton_p lastB = NULL; + char * cancelLabel = (winOption&PD_F_ALT_CANCELLABEL?_("Close"):_("Cancel")); + + winOption &= ~PD_F_ALT_CANCELLABEL; + group->okProc = okProc; + group->cancelProc = cancelProc; + group->layoutProc = layoutProc; + group->changeProc = changeProc; + if ( (winOption&F_CENTER) == 0 ) + winOption |= F_RECALLPOS; + if ( (winOption&F_RESIZE) != 0 ) + winOption |= F_RECALLSIZE; + + sprintf( helpStr, "cmd%s", group->nameStr ); + helpStr[3] = toupper(helpStr[3]); + + group->win = wWinPopupCreate( mainW, DlgSepRight, DlgSepFrmBottom, helpStr, title, group->nameStr, F_AUTOSIZE|winOption, ParamDlgProc, group ); + + if ( okLabel && okProc ) { + sprintf( helpStr, "%s-ok", group->nameStr ); + lastB = group->okB = wButtonCreate( group->win, 0, 0, helpStr, okLabel, BB_DEFAULT, 0, (wButtonCallBack_p)ParamButtonOk, group ); + } + if ( group->cancelProc ) { + lastB = group->cancelB = wButtonCreate( group->win, 0, 0, NULL, cancelLabel, BB_CANCEL, 0, (wButtonCallBack_p)ParamButtonCancel, group ); + } + if ( needHelpButton ) { + sprintf( helpStr, "cmd%s", group->nameStr ); + helpStr[3] = toupper(helpStr[3]); + lastB = group->helpB = wButtonCreate( group->win, 0, 0, NULL, _("Help"), BB_HELP, 0, (wButtonCallBack_p)wHelp, MyStrdup(helpStr) ); + } + + LOG( log_hotspot, 1, ( "mkshg ${PNG2DIR}/%s.png ${SHGDIR}/%s.shg << EOF\n", group->nameStr, group->nameStr ) ) + LayoutControls( group, ParamCreateControl, &group->origW, &group->origH ); + if ( group->okB ) + LOG( log_hotspot, 1, ( "popup %d %d %d %d _%s_%s\n", + wControlGetPosX((wControl_p)(group->okB))+hotspotOffsetX, + wControlGetPosY((wControl_p)(group->okB))+hotspotOffsetY, + wControlGetWidth((wControl_p)(group->okB)), + wControlGetHeight((wControl_p)(group->okB)), + group->nameStr, "ok" ) ) + LOG( log_hotspot, 1, ( "EOF\n" ) ) + + group->origW += DlgSepRight; + group->origH += DlgSepBottom; + wWinGetSize( group->win, &w0, &h0 ); + if ( (winOption&F_RESIZE) ) { + if ( group->origW != w0 || + group->origH != h0 ) { + LayoutControls( group, ParamPositionControl, NULL, NULL ); + } + } else if ( group->origW > w0 || group->origH > h0 ) { + if ( group->origW > w0 ) + w0 = group->origW; + if ( group->origH > h0 ) + h0 = group->origH; + wWinSetSize( group->win, w0, h0 ); + } + + return group->win; +} + + +/** Resize dialog window for the contained fields. +* \param IN OUT Prameter Group +* +*/ +EXPORT void ParamLayoutDialog( + paramGroup_p pg ) +{ + wPos_t w, h; + LayoutControls( pg, ParamPositionControl, &w, &h ); + w += DlgSepRight; + h += DlgSepBottom; + if ( w != pg->origW || h != pg->origH ) { + wWinSetSize( pg->win, w, h ); + pg->origW = w; + pg->origH = h; + } +} + + +EXPORT void ParamDialogOkActive( + paramGroup_p pg, + int active ) +{ + if ( pg->okB ) + wControlActive( (wControl_p)pg->okB, active ); +} + + +EXPORT void ParamCreateControls( + paramGroup_p pg, + paramChangeProc changeProc ) +{ + paramData_p pd; + char helpStr[STR_SHORT_SIZE], * helpStrP; + strcpy( helpStr, pg->nameStr ); + helpStrP = helpStr+strlen(helpStr); + *helpStrP++ = '-'; + for ( pd=pg->paramPtr; pd<&pg->paramPtr[pg->paramCnt]; pd++ ) { + pd->group = pg; + strcpy( helpStrP, pd->nameStr ); + ParamCreateControl( pd, helpStr, 0, 0 ); + if ( pd->type != PD_MENUITEM && pd->control ) + wControlShow( pd->control, FALSE ); + } + pg->changeProc = changeProc; +} + + +EXPORT void ParamInit( void ) +{ + AddPlaybackProc( "PARAMETER", ParamPlayback, NULL ); + AddPlaybackProc( "PARAMCHECK", ParamCheck, NULL ); + log_hotspot = LogFindIndex( "hotspot" ); + log_paramLayout = LogFindIndex( "paramlayout" ); +} diff --git a/app/bin/param.h b/app/bin/param.h new file mode 100644 index 0000000..02d259c --- /dev/null +++ b/app/bin/param.h @@ -0,0 +1,231 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/param.h,v 1.6 2009-09-21 18:24:33 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 PARAM_H +#define PARAM_H + +typedef struct turnoutInfo_t * turnoutInfo_p; + +typedef enum { + PD_LONG, + PD_FLOAT, + PD_RADIO, + PD_TOGGLE, + PD_STRING, + PD_LIST, + PD_DROPLIST, + PD_COMBOLIST, + PD_BUTTON, + PD_COLORLIST, + PD_MESSAGE, /* static text */ + PD_DRAW, + PD_TEXT, + PD_MENU, + PD_MENUITEM, + PD_BITMAP + } parameterType; + +#define PDO_DIM (1L<<0) +#define PDO_ANGLE (1L<<1) +#define PDO_NORECORD (1L<<2) +#define PDO_NOPSHACT (1L<<3) +#define PDO_NOPSHUPD (1L<<4) +#define PDO_NOPREF (1L<<5) +#define PDO_NOUPDACT (1L<<6) +#define PDO_MISC (1L<<7) +#define PDO_DRAW (1L<<8) +#define PDO_FILE (1L<<9) + +#define PDO_SMALLDIM (1L<<12) + +#define PDO_DLGSTARTBTNS (1L<<13) +#define PDO_DLGWIDE (1L<<14) +#define PDO_DLGNARROW (1L<<15) +#define PDO_DLGBOXEND (1L<<16) /**< draw recessed frame around the controls */ +#define PDO_DLGRESETMARGIN (1L<<17) /**< position control on the left ?*/ +#define PDO_DLGIGNORELABELWIDTH (1L<<18) +#define PDO_DLGHORZ (1L<<20) /**< arrange on same line as previous element */ +#define PDO_DLGNEWCOLUMN (1L<<21) +#define PDO_DLGNOLABELALIGN (1L<<22) +#define PDO_LISTINDEX (1L<<23) +#define PDO_DLGSETY (1L<<24) +#define PDO_DLGIGNOREX (1L<<25) +#define PDO_DLGUNDERCMDBUTT (1L<<26) +#define PDO_DLGCMDBUTTON (1L<<27) /**< arrange button on the right with the default buttons */ +#define PDO_DLGIGNORE (1L<<28) + +#define PDO_DLGRESIZEW (1L<<29) +#define PDO_DLGRESIZEH (1L<<30) +#define PDO_DLGRESIZE (PDO_DLGRESIZEW|PDO_DLGRESIZEH) + +#define PDO_NOACT (PDO_NOPSHACT|PDO_NOUPDACT) +#define PDO_NOUPD (PDO_NORSTUPD|PDO_NOPSHUPD|PDO_NOUPDUPD) + +typedef struct paramGroup_t *paramGroup_p; + +#define PDO_NORANGECHECK_LOW (1<<0) +#define PDO_NORANGECHECK_HIGH (1<<1) +typedef struct { + long low; + long high; + wPos_t width; + int rangechecks; + } paramIntegerRange_t; +typedef struct { + FLOAT_T low; + FLOAT_T high; + wPos_t width; + int rangechecks; + } paramFloatRange_t; +typedef struct { + wPos_t width; + wPos_t height; + wDrawRedrawCallBack_p redraw; + playbackProc action; + drawCmd_p d; + } paramDrawData_t; +typedef struct { + wIndex_t number; + wPos_t width; + int colCnt; + wPos_t * colWidths; + const char * * colTitles; + wPos_t height; + } paramListData_t; +typedef struct { + wPos_t width; + wPos_t height; + } paramTextData_t; + +typedef union { + long l; + FLOAT_T f; + char * s; + turnoutInfo_p p; + wDrawColor dc; + } paramOldData_t; +typedef struct { + parameterType type; + void * valueP; + char * nameStr; + long option; + void * winData; + char * winLabel; + long winOption; + void * context; + wControl_p control; + paramGroup_p group; + paramOldData_t oldD, demoD; + } paramData_t, *paramData_p; + + +typedef void (*paramGroupProc_t) ( long, long ); +#define PGACT_OK (1) +#define PGACT_PARAM (2) +#define PGACT_UPDATE (3) +#define PGACT_RESTORE (4) + +#define PGO_RECORD (1<<1) +#define PGO_NODEFAULTPROC (1<<2) +#define PGO_PREFGROUP (1<<8) +#define PGO_PREFMISCGROUP (1<<8) +#define PGO_PREFDRAWGROUP (1<<9) +#define PGO_PREFMISC (1<<10) + +typedef void (*paramLayoutProc)( paramData_t *, int, wPos_t, wPos_t *, wPos_t * ); +typedef void (*paramActionOkProc)( void * ); +typedef void (*paramActionCancelProc)( wWin_p ); +typedef void (*paramChangeProc)( paramGroup_p, int, void * ); + +typedef struct paramGroup_t { + char * nameStr; + long options; + paramData_p paramPtr; + int paramCnt; + paramActionOkProc okProc; + paramActionCancelProc cancelProc; + paramLayoutProc layoutProc; + long winOption; + paramChangeProc changeProc; + long action; + paramGroupProc_t proc; + wWin_p win; + wButton_p okB; + wButton_p cancelB; + wButton_p helpB; + wPos_t origW; + wPos_t origH; + wBox_p * boxs; + } paramGroup_t; + +wIndex_t ColorTabLookup( wDrawColor ); + +extern char * PREFSECT; +// extern char decodeErrorStr[STR_SHORT_SIZE]; +FLOAT_T DecodeFloat( wString_p, BOOL_T * ); +FLOAT_T DecodeDistance( wString_p, BOOL_T * ); +char * FormatLong( long ); +char * FormatFloat( FLOAT_T ); +char * FormatDistance( FLOAT_T ); +char * FormatSmallDistance( FLOAT_T ); +char * FormatDistanceEx( FLOAT_T, long ); + + +void ParamLoadControls( paramGroup_p ); +void ParamLoadControl( paramGroup_p, int ); +void ParamControlActive( paramGroup_p, int, BOOL_T ); +void ParamLoadMessage( paramGroup_p, int, char * ); +void ParamLoadData( paramGroup_p ); +long ParamUpdate( paramGroup_p ); +void ParamRegister( paramGroup_p ); +void ParamGroupRecord( paramGroup_p ); +void ParamUpdatePrefs( void ); +void ParamStartRecord( void ); +void ParamRestoreAll( void ); +void ParamSaveAll( void ); + +void ParamMenuPush( void * ); +int paramHiliteFast; +void ParamHilite( wWin_p, wControl_p, BOOL_T ); + +void ParamInit( void ); + +extern int paramLevel; +extern int paramLen; +extern unsigned long paramKey; +extern BOOL_T paramTogglePlaybackHilite; + +#define ParamMenuPushCreate( PD, M, HS, NS, AK, FUNC ) \ + wMenuPushCreate( M, HS, NS, AK, paramMenuPush, &PD ); \ + (PD).valueP = FUNC; \ + if ( HS ) GetBalloonHelpStr(HS); + +#define PD_F_ALT_CANCELLABEL (1L<<30) +wWin_p ParamCreateDialog( paramGroup_p, char *, char *, paramActionOkProc, paramActionCancelProc, BOOL_T, paramLayoutProc, long, paramChangeProc ); +void ParamCreateControls( paramGroup_p, paramChangeProc ); +void ParamLayoutDialog( paramGroup_p ); + +void ParamDialogOkActive( paramGroup_p, int ); + +#define ParamControlShow( PG, INX, SHOW ) \ + wControlShow( ((PG)->paramPtr)[INX].control, SHOW ) +#endif diff --git a/app/bin/shrtpath.c b/app/bin/shrtpath.c new file mode 100644 index 0000000..fa48408 --- /dev/null +++ b/app/bin/shrtpath.c @@ -0,0 +1,330 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/shrtpath.c,v 1.1 2005-12-07 15:46:54 rc-flyer 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 "track.h" +#include "shrtpath.h" + +EXPORT int log_shortPath; +static int log_shortPathInitted; + +/************************************************************************** + * + * Dijkstra's shortest path + * + **************************************************************************/ + +typedef enum { Unknown, Working, Final } pathState_e; +typedef struct { + pathState_e state; + DIST_T dist; /* Distance from root to entry */ + track_p contTrk; /* continuation */ + EPINX_T contEP; + int inxBack; /* Previous node on shortest path */ + int inxTracks; /* List of tracks along this path */ + int numTracks; + } pathNode_t, *pathNode_p; + +static dynArr_t pathNode_da; +#define pathNode(N) DYNARR_N( pathNode_t, pathNode_da, N ) +typedef struct { + track_p trk; + EPINX_T ep1, ep2; + DIST_T dist; + } trackep_t, *trackep_p; +static dynArr_t trackep_da; +#define trackep(N) DYNARR_N( trackep_t, trackep_da, N ) + +static track_p shortPathTrk0, shortPathTrk1; +static EPINX_T shortPathEP0, shortPathEP1; + + +static int DoShortPathFunc( shortestPathFunc_p func, char * title, SPTF_CMD cmd, track_p trk, EPINX_T ep1, EPINX_T ep2, DIST_T dist, void * data ) +{ + int rc; +LOG( log_shortPath, 4, ( " %s: T%d:%d.%d D:%0.3f ", title, trk?GetTrkIndex(trk):-1, ep1, ep2, dist ) ) + rc = func( cmd, trk, ep1, ep2, dist, data ); +LOG( log_shortPath, 4, ( "-> %d\n", rc ) ) + return rc; +} + +static void DumpPaths( int pinx ) +{ + pathNode_p pPath; + trackep_p pTrackep; + int tinx; + + lprintf(" Current = %d\n", pinx ); + for (pinx=0; pinx<pathNode_da.cnt; pinx++) { + pPath = &pathNode(pinx); + lprintf( " %3d: S%c T%d:%d D%0.3f T%d:%d", + pinx, + pPath->state==Unknown?'U':pPath->state==Working?'W':pPath->state==Final?'F':'?', + (pPath->contTrk?GetTrkIndex(pPath->contTrk):-1), + pPath->contEP, + pPath->dist, + pPath->inxTracks, + pPath->numTracks ); + if (pPath->inxBack>=0) { + lprintf(" B%d", pPath->inxBack ); + } + lprintf("\n "); + for (tinx=0; tinx<pPath->numTracks; tinx++) { + pTrackep = &trackep(pPath->inxTracks+tinx); + lprintf( " T%d:%d-%d=%0.1f", GetTrkIndex(pTrackep->trk), pTrackep->ep1, pTrackep->ep2, pTrackep->dist ); + } + lprintf("\n"); + } +} + + +static void AddTracksToPath( + int inxCurr, + shortestPathFunc_p func, + void * data ) +{ + pathNode_p pPath; + int tinx; + trackep_p pTrackep; + + while (inxCurr>=0) { + pPath = &pathNode(inxCurr); + for (tinx=pPath->numTracks-1;tinx>=0;tinx--) { + pTrackep = &trackep(pPath->inxTracks+tinx); + DoShortPathFunc( func, "ADDTRK", SPTC_ADD_TRK, pTrackep->trk, pTrackep->ep1, pTrackep->ep2, pTrackep->dist, data ); + } + inxCurr = pPath->inxBack; + } +} + + +static void AddTrackToNode( + track_p trk, + EPINX_T ep1, + EPINX_T ep2, + DIST_T dist) +{ + DYNARR_APPEND( trackep_t, trackep_da, 10 ); + trackep(trackep_da.cnt-1).trk = trk; + trackep(trackep_da.cnt-1).ep1 = ep1; + trackep(trackep_da.cnt-1).ep2 = ep2; + trackep(trackep_da.cnt-1).dist = dist; +} + + +static BOOL_T AddPath( + int inxCurr, + track_p trk0, + EPINX_T ep1, + EPINX_T ep2, + DIST_T dist, + shortestPathFunc_p func, + void * data ) +{ + EPINX_T epN; + track_p trk=trk0, trkN; + EPINX_T epCnt; + pathNode_p pNode; + int startTrack; + char * msg=NULL; + +LOG( log_shortPath, 2, ( " AddPath( T%d:%d.%d D=%0.3f B%d ) -> \n", GetTrkIndex(trk), ep1, ep2, dist, inxCurr ) ) + startTrack = trackep_da.cnt; + while (1) { + if ( ep2>=0 ) { + AddTrackToNode( trk, ep1, ep2, dist ); + dist += GetTrkLength( trk, ep1, -1 ) + GetTrkLength( trk, ep2, -1 ); + if ( DoShortPathFunc( func, "MATCH", SPTC_MATCH, trk, ep2, ep1, dist, data ) ) { + trk = NULL; + ep1 = -1; + msg = ""; + goto makeNode; + } + trkN = GetTrkEndTrk(trk,ep2); + if ( trkN == NULL ) { + /* dead end */ + msg = "dead end"; + goto skipNode; + } + if ( DoShortPathFunc( func, "IGNORE", SPTC_IGNNXTTRK, trk, ep2, ep1, dist, data ) ) { + msg = "ignore end"; + goto skipNode; + } + ep1 = GetEndPtConnectedToMe( trkN, trk ); + trk = trkN; + if ( (trk==shortPathTrk0 && ep1==shortPathEP0) || (trk==shortPathTrk1 && ep1==shortPathEP1) ) { + msg = "wrap around"; + goto skipNode; + } + } + epCnt = GetTrkEndPtCnt(trk); + if ( epCnt < 2 ) { + msg = "bumper track"; + goto skipNode; + } + if ( epCnt > 2 ) { + if ( (epN=DoShortPathFunc( func, "MATCHANY", SPTC_MATCHANY, trk, ep1, -1, dist, data )) >= 0 ) { + /* special match */ + /*dist += GetTrkLength( trk, ep1, epN );*/ + AddTrackToNode( trk, ep1, epN, dist ); + trk = NULL; + ep1 = -1; + msg = "ANY"; + } + goto makeNode; + } + ep2 = 1-ep1; + } + +makeNode: +if ( trk ) { +LOG( log_shortPath, 2, ( " -> FORK: [%d] T%d:%d", pathNode_da.cnt, GetTrkIndex(trk), ep1 ) ) +} else { +LOG( log_shortPath, 2, ( " -> MATCH%s: [%d]", msg, pathNode_da.cnt ) ) +} +LOG( log_shortPath, 2, ( " t%d D=%0.3f\n", startTrack, dist ) ) + + DYNARR_APPEND( pathNode_t, pathNode_da, 10 ); + pNode = &pathNode(pathNode_da.cnt-1); + pNode->state = Working; + pNode->dist = dist; + pNode->contTrk = trk; + pNode->contEP = ep1; + pNode->inxBack = inxCurr; + pNode->inxTracks = startTrack; + pNode->numTracks = trackep_da.cnt-startTrack; + if ( trk ) + SetTrkBits( trk, TB_SHRTPATH ); + return TRUE; + +skipNode: +LOG( log_shortPath, 2, ( " -> FAIL: %s @ T%d:%d.%d\n", msg, GetTrkIndex(trk), ep1, ep2 ) ) + trackep_da.cnt = startTrack; + return FALSE; +} + + + +int FindShortestPath( + track_p trkN, + EPINX_T epN, + BOOL_T bidirectional, + shortestPathFunc_p func, + void * data ) +{ + int inxCurr = 0; + pathNode_p pCurr; + pathNode_p pNext; + int pinx=0; + DIST_T minDist; + int count; + int rc = 0; + EPINX_T ep2, epCnt, ep3; + static dynArr_t ep_da; + #define ep(N) DYNARR_N( pathNode_p, ep_da, N ) + + DYNARR_RESET( pathNode_t, pathNode_da ); + DYNARR_RESET( trackep_t, trackep_da ); + count = 0; + + if ( !log_shortPathInitted ) { + log_shortPath = LogFindIndex( "shortPath" ); + log_shortPathInitted = TRUE; + } + +LOG( log_shortPath, 1, ( "FindShortestPath( T%d:%d, %s, ... )\n", GetTrkIndex(trkN), epN, bidirectional?"bidir":"unidir" ) ) + ClrAllTrkBits( TB_SHRTPATH ); + /* Note: trkN:epN is not tested for MATCH */ + shortPathTrk0 = trkN; + shortPathEP0 = epN; + shortPathTrk1 = GetTrkEndTrk( trkN, epN ); + if ( shortPathTrk1 != NULL ) + shortPathEP1 = GetEndPtConnectedToMe( shortPathTrk1, shortPathTrk0 ); + AddPath( -1, shortPathTrk0, shortPathEP0, -1, 0.0, func, data ); + if ( bidirectional && shortPathTrk1 != NULL ) + AddPath( -1, shortPathTrk1, shortPathEP1, -1, 0.0, func, data ); + + while (1) { + InfoMessage( "%d", ++count ); + + /* select next final node */ + minDist = 0.0; + inxCurr = -1; + for (pinx=0; pinx<pathNode_da.cnt; pinx++) { + pNext = &pathNode(pinx); + if (pNext->state == Working && + (inxCurr < 0 || pNext->dist < minDist) ) { + minDist = pathNode(pinx).dist; + inxCurr = pinx; + } + } + if ( inxCurr < 0 ) + break; +if (log_shortPath>=4) DumpPaths(inxCurr); + pCurr = &pathNode(inxCurr); + pCurr->state = Final; + if ( pCurr->contTrk == NULL ) { + if ( !DoShortPathFunc( func, "VALID", SPTC_VALID, trackep(pCurr->inxTracks+pCurr->numTracks-1).trk, trackep(pCurr->inxTracks+pCurr->numTracks-1).ep2, -1, 0.0, data ) ) + continue; + AddTracksToPath( inxCurr, func, data ); + rc++; + if ( DoShortPathFunc( func, "TERMINATE", SPTC_TERMINATE, trackep(pCurr->inxTracks+pCurr->numTracks-1).trk, trackep(pCurr->inxTracks+pCurr->numTracks-1).ep2, -1, 0.0, data ) ) + break; + } else { + epCnt = GetTrkEndPtCnt(pCurr->contTrk); + DYNARR_SET( pathNode_p, ep_da, epCnt ); + memset( ep_da.ptr, 0, epCnt * sizeof pNext ); + if ( (GetTrkBits(pCurr->contTrk) & TB_SHRTPATH) ) { + for ( pinx=0; pinx<pathNode_da.cnt; pinx++ ) { + pNext = &pathNode(pinx); + if ( pNext->contTrk == pCurr->contTrk ) { + ep(pNext->contEP) = pNext; + } + } + } + for ( ep2=0; ep2<epCnt; ep2++ ) { + pCurr = &pathNode(inxCurr); + + /* don't point back at myself */ + if ( pCurr->contEP == ep2 ) continue; + /* no route to ep */ + if ( DoShortPathFunc( func, "IGNORE", SPTC_IGNNXTTRK, pCurr->contTrk, pCurr->contEP, ep2, pCurr->dist, data ) ) continue; + /* somebody got here first */ + if ( ep(ep2) ) continue; + /* there is already a path out via ep2 */ + for ( ep3=0; ep3<epCnt; ep3++ ) { + if ( ep3==pCurr->contEP || ep3==ep2 ) continue; + if ( ep(ep3) == NULL ) continue; + if ( DoShortPathFunc( func, "IGNORE", SPTC_IGNNXTTRK, pCurr->contTrk, ep2, ep3, pCurr->dist, data ) ) continue; + if ( ep(ep3)->state == Final ) break; + } + if ( ep3 < epCnt ) continue; + AddPath( inxCurr, pCurr->contTrk, pCurr->contEP, ep2, pCurr->dist, func, data ); + } + } + } + +if (log_shortPath>=1) DumpPaths(inxCurr); + ClrAllTrkBits( TB_SHRTPATH ); + return rc; +} + + diff --git a/app/bin/shrtpath.h b/app/bin/shrtpath.h new file mode 100644 index 0000000..a8236e6 --- /dev/null +++ b/app/bin/shrtpath.h @@ -0,0 +1,33 @@ +/* $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/shrtpath.h,v 1.1 2005-12-07 15:46:54 rc-flyer 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. + */ + +typedef enum { + SPTC_MATCH, /* trk:ep is end of path? */ + SPTC_MATCHANY, /* any EP matches? */ + SPTC_IGNNXTTRK, /* don't traverse via trk:ep? */ + SPTC_ADD_TRK, /* trk:ep is next on current path */ + SPTC_TERMINATE, /* stop processing after current path? */ + SPTC_VALID /* trk:ep is still valid? */ + } SPTF_CMD; + +typedef int (*shortestPathFunc_p)( SPTF_CMD cmd, track_p, EPINX_T, EPINX_T, DIST_T, void * ); +int FindShortestPath( track_p, EPINX_T, BOOL_T, shortestPathFunc_p, void * ); + +extern int log_shortPath; diff --git a/app/bin/smalldlg.c b/app/bin/smalldlg.c new file mode 100644 index 0000000..e4213a5 --- /dev/null +++ b/app/bin/smalldlg.c @@ -0,0 +1,245 @@ +/** \file smalldlg.c + * Several simple and smaller dialogs. + * + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/smalldlg.c,v 1.6 2009-09-21 18:24:33 m_fischer Exp $ + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2005 Dave Bullis + * 2007 Martin Fischer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif +#include <ctype.h> +#include <string.h> +#include <stdlib.h> + +#ifndef WINDOWS +#include <unistd.h> +#include <dirent.h> +#endif +#ifdef WINDOWS +#include <io.h> +#include <windows.h> +#if _MSC_VER >1300 + #define strdup _strdup +#endif +#else +#include <sys/stat.h> +#endif + +#include "wlib.h" +#include "common.h" +#include "draw.h" +#include "misc.h" +#include "custom.h" +#include "param.h" + +#include "smalldlg.h" +#include "i18n.h" + +wWin_p aboutW; +static wWin_p tipW; /**< window handle for tip dialog */ + +static long showTipAtStart = 1; /**< flag for visibility */ + +static dynArr_t tips_da; /**< dynamic array for all tips */ +#define tips(N) DYNARR_N( char *, tips_da, N ) + +static char * tipLabels[] = { N_("Show tips at start"), NULL }; +static paramTextData_t tipTextData = { 40, 10 }; + +static paramData_t tipPLs[] = { +#define I_TIPTEXT (1) +#define tipT ((wText_p)tipPLs[I_TIPTEXT].control) + { PD_MESSAGE, N_("Did you know..."), NULL, 0, NULL, NULL, BM_LARGE }, + { PD_TEXT, NULL, "text", 0, &tipTextData, NULL, BO_READONLY|BT_CHARUNITS }, + { PD_BUTTON, (void*)ShowTip, "prev", PDO_DLGRESETMARGIN, NULL, N_("Previous Tip"), 0L, (void *)(SHOWTIP_FORCESHOW | SHOWTIP_PREVTIP) }, + { PD_BUTTON, (void*)ShowTip, "next", PDO_DLGHORZ, NULL, N_("Next Tip"), 0L, (void *)(SHOWTIP_FORCESHOW | SHOWTIP_NEXTTIP) }, + { PD_TOGGLE, &showTipAtStart, "showatstart", PDO_DLGCMDBUTTON, tipLabels, NULL, BC_NOBORDER }}; + +static paramGroup_t tipPG = { "tip", 0, tipPLs, sizeof tipPLs/sizeof tipPLs[0] }; + +/** + * Create and initialize the tip of the day window. The dialog box is created and the list of tips is loaded + * into memory. + */ + +static void CreateTipW( void ) +{ + FILE * tipF; + char buff[4096]; + char * cp; + + tipW = ParamCreateDialog( &tipPG, MakeWindowTitle(_("Tip of the Day")), _("Ok"), (paramActionOkProc)wHide, NULL, FALSE, NULL, F_CENTER, NULL ); + + /* open the tip file */ + sprintf( buff, "%s%s%s.tip", libDir, FILE_SEP_CHAR, sProdNameLower ); + tipF = fopen( buff, "r" ); + + /* if tip file could not be opened, the only tip is an error message for the situation */ + if (tipF == NULL) { + DYNARR_APPEND( char *, tips_da, 1 ); + tips(0) = N_("No tips are available"); +/* TODO: enable buttons only if tips are available + wControlActive( prev, FALSE ); + wControlActive( next, FALSE ); */ + } else { + /* read all the tips from the file */ + while (fgets( buff, sizeof buff, tipF )) { + + /* lines starting with hash sign are ignored (comments) */ + if (buff[0] == '#') + continue; + + /* remove CRs and LFs at end of line */ + cp = buff+strlen(buff)-1; + if (*cp=='\n') cp--; + if (*cp=='\r') cp--; + + /* get next line if the line was empty */ + if (cp < buff) + continue; + + cp[1] = 0; + + /* if line ended with a continuation sign, get the rest */ + while (*cp=='\\') { + /* put LF at end */ + *cp++ = '\n'; + + /* read a line */ + if (!fgets( cp, (sizeof buff) - (cp-buff), tipF )) { + return; + } + + /* lines starting with hash sign are ignored (comments) */ + if (*cp=='#') + continue; + + /* remove CRs and LFs at end of line */ + cp += strlen(cp)-1; + if (*cp=='\n') cp--; + if (*cp=='\r') cp--; + cp[1] = 0; + } + + /* allocate memory for the tip and store pointer in dynamic array */ + DYNARR_APPEND( char *, tips_da, 10 ); + tips(tips_da.cnt-1) = strdup( buff ); + } + } +} + +/** + * Show tip of the day. As far as necessary, the dialog is created. The index of + * the last tip shown is retrieved from the preferences and the next tip is + * selected. At the end, the index of the shown tip is saved into the preferences. + * + * \param IN flags see definitions in smalldlg.h for possible values + * + */ + +void ShowTip( long flags ) +{ + long tipNum; + + if (showTipAtStart || (flags & SHOWTIP_FORCESHOW)) + { + if (tipW == NULL) { + CreateTipW(); + } + ParamLoadControls( &tipPG ); + wTextClear( tipT ); + wPrefGetInteger( "misc", "tip-number", &tipNum, 0 ); + + if( flags & SHOWTIP_PREVTIP ) { + if(tipNum == 0 ) + tipNum = tips_da.cnt - 1; + else + tipNum--; + } else { + if (tipNum >= tips_da.cnt - 1) + tipNum = 0; + else + tipNum++; + } + + wTextAppend( tipT, _(tips(tipNum)) ); + + wPrefSetInteger( "misc", "tip-number", tipNum ); + wShow( tipW ); + } +} + +/*--------------------------------------------------------------------*/ + +#include "bitmaps/xtc.xpm" + +static paramTextData_t aboutTextData = { 70, 10 }; + +#define DESCRIPTION N_("XTrackCAD is a CAD (computer-aided design) program for designing model railroad layouts.") +static paramData_t aboutPLs[] = { +#define I_ABOUTDRAW (0) + { PD_BITMAP, NULL, "about", PDO_NOPSHUPD, NULL, NULL, 0 }, +#define I_ABOUTVERSION (1) + { PD_MESSAGE, NULL, NULL, PDO_DLGNEWCOLUMN, NULL, NULL, BM_LARGE }, +#define I_COPYRIGHT (2) +#define COPYRIGHT_T ((wText_p)aboutPLs[I_COPYRIGHT].control) + { PD_TEXT, NULL, NULL, PDO_DLGRESIZE, &aboutTextData, NULL, BT_CHARUNITS } +}; +static paramGroup_t aboutPG = { "about", 0, aboutPLs, sizeof aboutPLs/sizeof aboutPLs[0] }; + +/** + * Create and show the About window. + */ + +void CreateAboutW( void *ptr ) +{ + char *copyright = sAboutProd; + + if( !aboutW ) { + aboutPLs[I_ABOUTDRAW].winData = wIconCreatePixMap( xtc_xpm ); + ParamRegister( &aboutPG ); + aboutW = ParamCreateDialog( &aboutPG, MakeWindowTitle(_("About")), _("Ok"), (paramActionOkProc)wHide, NULL, FALSE, NULL, F_TOP|F_CENTER, NULL ); + ParamLoadMessage( &aboutPG, I_ABOUTVERSION, sAboutProd ); + wTextAppend( COPYRIGHT_T, DESCRIPTION ); + wTextAppend( COPYRIGHT_T, "\n\nXTrackCAD is Copyright 2003 by Sillub Technology and 2007 by Martin Fischer and Bob Blackwell." ); + wTextAppend( COPYRIGHT_T, "\n\nIcons by: Tango Desktop Project (http://tango.freedesktop.org)"); + wTextAppend( COPYRIGHT_T, "\n\nContributions by: Robert Heller, Mikko Nissinen, Timothy M. Shead, Daniel Luis Spagnol" ); + wTextAppend( COPYRIGHT_T, "\n\nParameter Files by: Ralph Boyd, Dwayne Ward" ); + wTextAppend( COPYRIGHT_T, "\n\nuthash Copyright notice:" ); + wTextAppend( COPYRIGHT_T, "\nCopyright (c) 2005-2015, Troy D. Hanson http://troydhanson.github.com/uthash/"); + wTextAppend( COPYRIGHT_T, "\nAll rights reserved."); + } + + wShow( aboutW ); +} + +/*--------------------------------------------------------------------*/ + +/** + * Initialize the functions for small dialogs. + */ + +void InitSmallDlg( void ) +{ + ParamRegister( &tipPG ); +} diff --git a/app/bin/smalldlg.h b/app/bin/smalldlg.h new file mode 100644 index 0000000..2bcb3bc --- /dev/null +++ b/app/bin/smalldlg.h @@ -0,0 +1,38 @@ +/** \file smalldlg.h + * Definitions and declarations for the small dialog box functions. + * + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/smalldlg.h,v 1.2 2009-09-21 18:24:33 m_fischer Exp $ + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) + * + * 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 SMALLDLG_H +#define SMALLDLG_H + +#define SHOWTIP_NEXTTIP (0L) +#define SHOWTIP_PREVTIP (1L) +#define SHOWTIP_FORCESHOW (2L) + +extern wWin_p aboutW; + +void InitSmallDlg( void ); +void ShowTip( long flags ); +void CreateAboutW( void *ptr ); + +#endif diff --git a/app/bin/tcurve.c b/app/bin/tcurve.c new file mode 100644 index 0000000..7e9fc90 --- /dev/null +++ b/app/bin/tcurve.c @@ -0,0 +1,1587 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/tcurve.c,v 1.3 2009-06-15 19:29:57 m_fischer Exp $ + * + * CURVE + * + */ + +/* 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 "track.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "cjoin.h" +#include "i18n.h" + +static TRKTYP_T T_CURVE = -1; + +struct extraData { + coOrd pos; + DIST_T radius; + BOOL_T circle; + long helixTurns; + coOrd descriptionOff; + }; +#define xpos extraData->pos +#define xradius extraData->radius +#define xcircle extraData->circle + +static int log_curve = 0; + +static DIST_T GetLengthCurve( track_p ); + +/**************************************** + * + * UTILITIES + * + */ + +static void GetCurveAngles( ANGLE_T *a0, ANGLE_T *a1, track_p trk ) +{ + struct extraData *xx = GetTrkExtraData(trk); + assert( trk != NULL ); + if (xx->circle != TRUE) { + *a0 = NormalizeAngle( GetTrkEndAngle(trk,0) + 90 ); + *a1 = NormalizeAngle( + GetTrkEndAngle(trk,1) - GetTrkEndAngle(trk,0) + 180 ); + } else { + *a0 = 0.0; + *a1 = 360.0; + } +LOG( log_curve, 4, ( "getCurveAngles: = %0.3f %0.3f\n", *a0, *a1 ) ) +} + +static void SetCurveAngles( track_p p, ANGLE_T a0, ANGLE_T a1, struct extraData * xx ) +{ + coOrd pos0, pos1; + xx->circle = (a0 == 0.0 && a1 == 0.0); + PointOnCircle( &pos0, xx->pos, xx->radius, a0 ); + PointOnCircle( &pos1, xx->pos, xx->radius, a0+a1 ); + SetTrkEndPoint( p, 0, pos0, NormalizeAngle(a0-90.0) ); + SetTrkEndPoint( p, 1, pos1, NormalizeAngle(a0+a1+90.0) ); +} + +static void ComputeCurveBoundingBox( track_p trk, struct extraData * xx ) +{ + coOrd p = xx->pos; + DIST_T r = xx->radius; + ANGLE_T a0, a1, aa; + POS_T x0, x1, y0, y1; + coOrd hi, lo; + + GetCurveAngles( &a0, &a1, trk ); + if ( xx->helixTurns > 0 ) { + a0 = 0.0; + a1 = 360.0; + } + aa = a0+a1; + x0 = r * sin(D2R(a0)); + x1 = r * sin(D2R(aa)); + y0 = r * cos(D2R(a0)); + y1 = r * cos(D2R(aa)); + hi.y = p.y + ((aa>=360.0) ? (r) : max(y0,y1)); + lo.y = p.y + (((a0>180.0?aa-180.0:aa+180.0)>=360.0) ? (-r) : min(y0,y1)); + hi.x = p.x + (((a0> 90.0?aa- 90.0:aa+270.0)>=360.0) ? (r) : max(x0,x1)); + lo.x = p.x + (((a0>270.0?aa-270.0:aa+ 90.0)>=360.0) ? (-r) : min(x0,x1)); + SetBoundingBox( trk, hi, lo ); +} + +static void AdjustCurveEndPt( track_p t, EPINX_T inx, ANGLE_T a ) +{ + struct extraData *xx = GetTrkExtraData(t); + coOrd pos; + ANGLE_T aa; + if (GetTrkType(t) != T_CURVE) { + AbortProg( "AdjustCurveEndPt( %d, %d ) not on CURVE %d", + GetTrkIndex(t), inx, GetTrkType(t) ); + return; + } + UndoModify( t ); +LOG( log_curve, 1, ( "adjustCurveEndPt T%d[%d] a=%0.3f\n", GetTrkIndex(t), inx, a ) ) + aa = a = NormalizeAngle(a); + a += inx==0?90.0:-90.0; + (void)PointOnCircle( &pos, xx->pos, xx->radius, a ); + SetTrkEndPoint( t, inx, pos, aa ); + if (xx->circle) { + (void)PointOnCircle( &pos, xx->pos, xx->radius, aa ); + SetTrkEndPoint( t, 1-inx, pos, a ); + xx->circle = 0; + } +LOG( log_curve, 1, ( " E0:[%0.3f %0.3f] A%0.3f, E1:[%0.3f %0.3f] A%0.3f\n", + GetTrkEndPosXY(t,0), GetTrkEndAngle(t,0), + GetTrkEndPosXY(t,1), GetTrkEndAngle(t,1) ) ) + ComputeCurveBoundingBox( t, xx ); + CheckTrackLength( t ); +} + +static void GetTrkCurveCenter( track_p t, coOrd *p, DIST_T *r ) +{ + struct extraData *xx = GetTrkExtraData(t); + *p = xx->pos; + *r = xx->radius; +} + +BOOL_T IsCurveCircle( track_p t ) +{ + struct extraData *xx; + if ( GetTrkType(t) != T_CURVE ) + return FALSE; + xx = GetTrkExtraData(t); + return xx->circle || xx->helixTurns>0; +} + + +BOOL_T GetCurveMiddle( track_p trk, coOrd * pos ) +{ + struct extraData *xx; + ANGLE_T a0, a1; + if ( GetTrkType(trk) != T_CURVE ) + return FALSE; + xx = GetTrkExtraData(trk); + if (xx->circle || xx->helixTurns>0) { + PointOnCircle( pos, xx->pos, xx->radius, 0 ); + } else { + GetCurveAngles( &a0, &a1, trk ); + PointOnCircle( pos, xx->pos, xx->radius, a0+a1/2 ); + } + return TRUE; +} + +DIST_T CurveDescriptionDistance( + coOrd pos, + track_p trk ) +{ + struct extraData *xx = GetTrkExtraData(trk); + coOrd p1; + FLOAT_T ratio; + ANGLE_T a, a0, a1; + + if ( GetTrkType( trk ) != T_CURVE || ( GetTrkBits( trk ) & TB_HIDEDESC ) != 0 ) + return 100000; + if ( xx->helixTurns > 0 ) { + p1.x = xx->pos.x + xx->descriptionOff.x; + p1.y = xx->pos.y + xx->descriptionOff.y; + } else { + GetCurveAngles( &a0, &a1, trk ); + ratio = ( xx->descriptionOff.x + 1.0 ) / 2.0; + a = a0 + ratio * a1; + ratio = ( xx->descriptionOff.y + 1.0 ) / 2.0; + Translate( &p1, xx->pos, a, xx->radius * ratio ); + } + return FindDistance( p1, pos ); +} + + +static void DrawCurveDescription( + track_p trk, + drawCmd_p d, + wDrawColor color ) +{ + struct extraData *xx = GetTrkExtraData(trk); + wFont_p fp; + coOrd pos, p0, p1; + DIST_T elev0, elev1, dist, grade=0, sep=0; + BOOL_T elevValid; + ANGLE_T a, a0, a1; + FLOAT_T ratio; + + if (layoutLabels == 0) + return; + if ((labelEnable&LABELENABLE_TRKDESC)==0) + return; + + if ( xx->helixTurns > 0 ) { + pos = xx->pos; + pos.x += xx->descriptionOff.x; + pos.y += xx->descriptionOff.y; + dist = GetLengthCurve( trk ); + elevValid = FALSE; + if ( (!xx->circle) && + ComputeElev( trk, 0, FALSE, &elev0, NULL ) && + ComputeElev( trk, 1, FALSE, &elev1, NULL ) ) { + if( elev0 == elev1 ) + elevValid = FALSE; + else { + elevValid = TRUE; + grade = fabs((elev1-elev0)/dist); + sep = grade*(xx->radius*M_PI*2.0); + } + } + fp = wStandardFont( F_TIMES, FALSE, FALSE ); + if (elevValid) + sprintf( message, _("Helix: turns=%ld length=%s grade=%0.1f%% sep=%s"), + xx->helixTurns, + FormatDistance(dist), + grade*100.0, + FormatDistance(sep) ); + else + sprintf( message, _("Helix: turns=%ld length=%s"), + xx->helixTurns, + FormatDistance(dist) ); + DrawBoxedString( BOX_BOX, d, pos, message, fp, (wFontSize_t)descriptionFontSize, color, 0.0 ); + } else { + dist = trackGauge/2.0; + DrawArc( d, xx->pos, dist, 0.0, 360.0, FALSE, 0, color ); + Translate( &p0, xx->pos, 90.0, dist ); + Translate( &p1, xx->pos, 270.0, dist ); + DrawLine( d, p0, p1, 0, color ); + Translate( &p0, xx->pos, 0.0, dist ); + Translate( &p1, xx->pos, 180.0, dist ); + DrawLine( d, p0, p1, 0, color ); + GetCurveAngles( &a0, &a1, trk ); + ratio = ( xx->descriptionOff.x + 1.0 ) / 2.0; + a = a0 + ratio * a1; + PointOnCircle( &p0, xx->pos, xx->radius, a ); + sprintf( message, "R %s", FormatDistance( xx->radius ) ); + ratio = ( xx->descriptionOff.y + 1.0 ) / 2.0; + DrawDimLine( d, xx->pos, p0, message, (wFontSize_t)descriptionFontSize, ratio, 0, color, 0x11 ); + } +} + + +STATUS_T CurveDescriptionMove( + track_p trk, + wAction_t action, + coOrd pos ) +{ + struct extraData *xx = GetTrkExtraData(trk); + static coOrd p0; + wDrawColor color; + ANGLE_T a, a0, a1; + DIST_T d; + + switch (action) { + case C_DOWN: + case C_MOVE: + case C_UP: + color = GetTrkColor( trk, &mainD ); + DrawCurveDescription( trk, &tempD, color ); + if ( xx->helixTurns > 0 ) { + if (action != C_DOWN) + DrawLine( &tempD, xx->pos, p0, 0, wDrawColorBlack ); + xx->descriptionOff.x = (pos.x-xx->pos.x); + xx->descriptionOff.y = (pos.y-xx->pos.y); + p0 = pos; + if (action != C_UP) + DrawLine( &tempD, xx->pos, p0, 0, wDrawColorBlack ); + } else { + GetCurveAngles( &a0, &a1, trk ); + if ( a1 < 1 ) a1 = 1.0; + a = FindAngle( xx->pos, pos ); + if ( ! IsCurveCircle( trk ) ) { + a = NormalizeAngle( a - a0 ); + if ( a > a1 ) { + if ( a < a1 + ( 360.0 - a1 ) / 2 ) { + a = a1; + } else { + a = 0.0; + } + } + } + xx->descriptionOff.x = ( a / a1 ) * 2.0 - 1.0; + d = FindDistance( xx->pos, pos ) / xx->radius; + if ( d > 0.9 ) + d = 0.9; + if ( d < 0.1 ) + d = 0.1; + xx->descriptionOff.y = d * 2.0 - 1.0; + } + DrawCurveDescription( trk, &tempD, color ); + MainRedraw(); + return action==C_UP?C_TERMINATE:C_CONTINUE; + + case C_REDRAW: + if ( xx->helixTurns > 0 ) { + DrawLine( &tempD, xx->pos, p0, 0, wDrawColorBlack ); + } + break; + + } + return C_CONTINUE; +} + +/**************************************** + * + * GENERIC FUNCTIONS + * + */ + +static struct { + coOrd endPt[2]; + FLOAT_T elev[2]; + FLOAT_T length; + coOrd center; + DIST_T radius; + long turns; + DIST_T separation; + ANGLE_T angle0; + ANGLE_T angle1; + ANGLE_T angle; + FLOAT_T grade; + descPivot_t pivot; + LAYER_T layerNumber; + } crvData; +typedef enum { E0, Z0, E1, Z1, CE, RA, TU, SE, LN, AL, A1, A2, GR, PV, LY } crvDesc_e; +static descData_t crvDesc[] = { +/*E0*/ { DESC_POS, N_("End Pt 1: X"), &crvData.endPt[0] }, +/*Z0*/ { DESC_DIM, N_("Z"), &crvData.elev[0] }, +/*E1*/ { DESC_POS, N_("End Pt 2: X"), &crvData.endPt[1] }, +/*Z1*/ { DESC_DIM, N_("Z"), &crvData.elev[1] }, +/*CE*/ { DESC_POS, N_("Center: X"), &crvData.center }, +/*RA*/ { DESC_DIM, N_("Radius"), &crvData.radius }, +/*TU*/ { DESC_LONG, N_("Turns"), &crvData.turns }, +/*SE*/ { DESC_DIM, N_("Separation"), &crvData.separation }, +/*LN*/ { DESC_DIM, N_("Length"), &crvData.length }, +/*AL*/ { DESC_FLOAT, N_("Angular Length"), &crvData.angle }, +/*A1*/ { DESC_ANGLE, N_("CCW Angle"), &crvData.angle0 }, +/*A2*/ { DESC_ANGLE, N_("CW Angle"), &crvData.angle1 }, +/*GR*/ { DESC_FLOAT, N_("Grade"), &crvData.grade }, +/*PV*/ { DESC_PIVOT, N_("Pivot"), &crvData.pivot }, +/*LY*/ { DESC_LAYER, N_("Layer"), &crvData.layerNumber }, + { DESC_NULL } }; + +static void UpdateCurve( track_p trk, int inx, descData_p descUpd, BOOL_T final ) +{ + struct extraData *xx = GetTrkExtraData(trk); + BOOL_T updateEndPts; + ANGLE_T a0, a1; + EPINX_T ep; + struct extraData xx0; + FLOAT_T turns; + + if ( inx == -1 ) + return; + xx0 = *xx; + updateEndPts = FALSE; + GetCurveAngles( &a0, &a1, trk ); + switch ( inx ) { + case CE: + xx0.pos = crvData.center; + updateEndPts = TRUE; + break; + case RA: + if ( crvData.radius <= 0 ) { + ErrorMessage( MSG_RADIUS_GTR_0 ); + crvData.radius = xx0.radius; + crvDesc[RA].mode |= DESC_CHANGE; + } else { + if ( crvData.pivot == DESC_PIVOT_FIRST || GetTrkEndTrk(trk,0) ) { + Translate( &xx0.pos, xx0.pos, a0, xx0.radius-crvData.radius ); + } else if ( crvData.pivot == DESC_PIVOT_SECOND || GetTrkEndTrk(trk,1) ) { + Translate( &xx0.pos, xx0.pos, a0+a1, xx0.radius-crvData.radius ); + } else { + Translate( &xx0.pos, xx0.pos, a0+a1/2.0, xx0.radius-crvData.radius ); + } + crvDesc[CE].mode |= DESC_CHANGE; + xx0.radius = crvData.radius; + crvDesc[LN].mode |= DESC_CHANGE; + updateEndPts = TRUE; + } + break; + case TU: + if ( crvData.turns <= 0 ) { + ErrorMessage( MSG_HELIX_TURNS_GTR_0 ); + crvData.turns = xx0.helixTurns; + crvDesc[TU].mode |= DESC_CHANGE; + } else { + xx0.helixTurns = crvData.turns; + crvDesc[LN].mode |= DESC_CHANGE; + updateEndPts = TRUE; + crvDesc[SE].mode |= DESC_CHANGE; + crvDesc[GR].mode |= DESC_CHANGE; + } + break; + case AL: + if ( crvData.angle <= 0.0 || crvData.angle >= 360.0 ) { + ErrorMessage( MSG_CURVE_OUT_OF_RANGE ); + crvData.angle = a1; + crvDesc[AL].mode |= DESC_CHANGE; + } else { + if ( crvData.pivot == DESC_PIVOT_FIRST || GetTrkEndTrk(trk,0) ) { + a1 = crvData.angle; + crvData.angle1 = NormalizeAngle( a0+a1 ); + crvDesc[A2].mode |= DESC_CHANGE; + } else if ( crvData.pivot == DESC_PIVOT_SECOND || GetTrkEndTrk(trk,1) ) { + a0 = NormalizeAngle( a0+a1-crvData.angle ); + a1 = crvData.angle; + crvData.angle0 = NormalizeAngle( a0 ); + crvDesc[A1].mode |= DESC_CHANGE; + } else { + a0 = NormalizeAngle( a0+a1/2.0-crvData.angle/2.0); + a1 = crvData.angle; + crvData.angle0 = NormalizeAngle( a0 ); + crvData.angle1 = NormalizeAngle( a0+a1 ); + crvDesc[A1].mode |= DESC_CHANGE; + crvDesc[A2].mode |= DESC_CHANGE; + } + crvDesc[LN].mode |= DESC_CHANGE; + updateEndPts = TRUE; + } + break; + case A1: + a0 = crvData.angle0 = NormalizeAngle( crvData.angle0 ); + a1 = NormalizeAngle( crvData.angle1-crvData.angle0 ); + if ( a1 <= 0.0 ) { + ErrorMessage( MSG_CURVE_OUT_OF_RANGE ); + } else { + updateEndPts = TRUE; + crvData.angle = a1; + crvDesc[AL].mode |= DESC_CHANGE; + crvDesc[LN].mode |= DESC_CHANGE; + } + break; + case A2: + a1 = NormalizeAngle( crvData.angle1-crvData.angle0 ); + if ( a1 <= 0.0 ) { + ErrorMessage( MSG_CURVE_OUT_OF_RANGE ); + } else { + updateEndPts = TRUE; + crvData.angle = a1; + crvDesc[AL].mode |= DESC_CHANGE; + crvDesc[LN].mode |= DESC_CHANGE; + } + break; + case Z0: + case Z1: + ep = (inx==Z0?0:1); + UpdateTrkEndElev( trk, ep, GetTrkEndElevUnmaskedMode(trk,ep), crvData.elev[ep], NULL ); + ComputeElev( trk, 1-ep, FALSE, &crvData.elev[1-ep], NULL ); + if ( crvData.length > minLength ) + crvData.grade = fabs( (crvData.elev[0]-crvData.elev[1])/crvData.length )*100.0; + else + crvData.grade = 0.0; + crvDesc[GR].mode |= DESC_CHANGE; + crvDesc[inx==Z0?Z1:Z0].mode |= DESC_CHANGE; + if ( xx->helixTurns > 0 ) { + turns = crvData.length/(2*M_PI*crvData.radius); + crvData.separation = fabs(crvData.elev[0]-crvData.elev[1])/turns; + crvDesc[SE].mode |= DESC_CHANGE; + } + return; + case LY: + SetTrkLayer( trk, crvData.layerNumber); + break; + default: + AbortProg( "updateCurve: Bad inx %d", inx ); + } + UndrawNewTrack( trk ); + *xx = xx0; + if (updateEndPts) { + if ( GetTrkEndTrk(trk,0) == NULL ) { + (void)PointOnCircle( &crvData.endPt[0], xx0.pos, xx0.radius, a0 ); + SetTrkEndPoint( trk, 0, crvData.endPt[0], NormalizeAngle( a0-90.0 ) ); + crvDesc[E0].mode |= DESC_CHANGE; + } + if ( GetTrkEndTrk(trk,1) == NULL ) { + (void)PointOnCircle( &crvData.endPt[1], xx0.pos, xx0.radius, a0+a1 ); + SetTrkEndPoint( trk, 1, crvData.endPt[1], NormalizeAngle( a0+a1+90.0 ) ); + crvDesc[E1].mode |= DESC_CHANGE; + } + } + crvData.length = GetLengthCurve( trk ); + + if ( crvDesc[SE].mode&DESC_CHANGE ) { + DrawCurveDescription( trk, &mainD, wDrawColorWhite ); + DrawCurveDescription( trk, &mainD, wDrawColorBlack ); + turns = crvData.length/(2*M_PI*crvData.radius); + crvData.separation = fabs(crvData.elev[0]-crvData.elev[1])/turns; + if ( crvData.length > minLength ) + crvData.grade = fabs( (crvData.elev[0]-crvData.elev[1])/crvData.length )*100.0; + else + crvData.grade = 0.0; + crvDesc[GR].mode |= DESC_CHANGE; + } + + ComputeCurveBoundingBox( trk, xx ); + DrawNewTrack( trk ); +} + +static void DescribeCurve( track_p trk, char * str, CSIZE_T len ) +{ + struct extraData *xx = GetTrkExtraData(trk); + ANGLE_T a0, a1; + DIST_T d; + int fix0, fix1; + FLOAT_T turns; + + GetCurveAngles( &a0, &a1, trk ); + d = xx->radius * 2.0 * M_PI * a1 / 360.0; + if (xx->helixTurns > 0) { + d += (xx->helixTurns-(xx->circle?1:0)) * xx->radius * 2.0 * M_PI; + sprintf( str, _("Helix Track(%d): Layer=%d Radius=%s Turns=%ld Length=%s Center=[%s,%s] EP=[%0.3f,%0.3f A%0.3f] [%0.3f,%0.3f A%0.3f]"), + GetTrkIndex(trk), + GetTrkLayer(trk)+1, + FormatDistance(xx->radius), + xx->helixTurns, + FormatDistance(d), + FormatDistance(xx->pos.x), FormatDistance(xx->pos.y), + GetTrkEndPosXY(trk,0), GetTrkEndAngle(trk,0), + GetTrkEndPosXY(trk,1), GetTrkEndAngle(trk,1) ); + } else { + sprintf( str, _("Curved Track(%d): Layer=%d Radius=%s Length=%s Center=[%s,%s] EP=[%0.3f,%0.3f A%0.3f] [%0.3f,%0.3f A%0.3f]"), + GetTrkIndex(trk), + GetTrkLayer(trk)+1, + FormatDistance(xx->radius), + FormatDistance(d), + FormatDistance(xx->pos.x), FormatDistance(xx->pos.y), + GetTrkEndPosXY(trk,0), GetTrkEndAngle(trk,0), + GetTrkEndPosXY(trk,1), GetTrkEndAngle(trk,1) ); + } + + fix0 = GetTrkEndTrk(trk,0)!=NULL; + fix1 = GetTrkEndTrk(trk,1)!=NULL; + + crvData.endPt[0] = GetTrkEndPos(trk,0); + crvData.endPt[1] = GetTrkEndPos(trk,1); + crvData.length = GetLengthCurve(trk); + crvData.center = xx->pos; + crvData.radius = xx->radius; + crvData.turns = xx->helixTurns; + crvData.angle0 = NormalizeAngle( a0 ); + crvData.angle1 = NormalizeAngle( a0+a1); + crvData.angle = a1; + crvData.layerNumber = GetTrkLayer(trk); + if ( !xx->circle ) { + ComputeElev( trk, 0, FALSE, &crvData.elev[0], NULL ); + ComputeElev( trk, 1, FALSE, &crvData.elev[1], NULL ); + } else { + crvData.elev[0] = crvData.elev[1] = 0; + } + ComputeElev( trk, 0, FALSE, &crvData.elev[0], NULL ); + ComputeElev( trk, 1, FALSE, &crvData.elev[1], NULL ); + if ( crvData.length > minLength ) + crvData.grade = fabs( (crvData.elev[0]-crvData.elev[1])/crvData.length )*100.0; + else + crvData.grade = 0.0; + if ( xx->helixTurns > 0 ) { + turns = crvData.length/(2*M_PI*crvData.radius); + crvData.separation = fabs(crvData.elev[0]-crvData.elev[1])/turns; + crvDesc[SE].mode |= DESC_CHANGE; + } + + crvDesc[E0].mode = + crvDesc[E1].mode = + crvDesc[LN].mode = + DESC_RO; + crvDesc[Z0].mode = (EndPtIsDefinedElev(trk,0)?0:DESC_RO)|DESC_NOREDRAW; + crvDesc[Z1].mode = (EndPtIsDefinedElev(trk,1)?0:DESC_RO)|DESC_NOREDRAW; + crvDesc[GR].mode = DESC_RO; + crvDesc[CE].mode = (fix0|fix1)?DESC_RO:0; + crvDesc[RA].mode = + crvDesc[AL].mode = + (fix0&fix1)?DESC_RO:0; + crvDesc[TU].mode = DESC_NOREDRAW; + crvDesc[A1].mode = fix0?DESC_RO:0; + crvDesc[A2].mode = fix1?DESC_RO:0; + crvDesc[PV].mode = (fix0|fix1)?DESC_IGNORE:0; + crvDesc[LY].mode = DESC_NOREDRAW; + crvData.pivot = (fix0&fix1)?DESC_PIVOT_NONE: + fix0?DESC_PIVOT_FIRST: + fix1?DESC_PIVOT_SECOND: + DESC_PIVOT_MID; + + crvDesc[SE].mode |= DESC_IGNORE; + if ( xx->circle ) { + crvDesc[E0].mode |= DESC_IGNORE; + crvDesc[Z0].mode |= DESC_IGNORE; + crvDesc[E1].mode |= DESC_IGNORE; + crvDesc[Z1].mode |= DESC_IGNORE; + crvDesc[AL].mode |= DESC_IGNORE; + crvDesc[A1].mode |= DESC_IGNORE; + crvDesc[A2].mode |= DESC_IGNORE; + crvDesc[PV].mode |= DESC_IGNORE; + } + + if ( xx->helixTurns ) { + if ( !xx->circle ) + crvDesc[SE].mode = DESC_RO; + DoDescribe( _("Helix Track"), trk, crvDesc, UpdateCurve ); + } else if ( xx->circle ) { + crvDesc[TU].mode |= DESC_IGNORE; + DoDescribe( _("Circle Track"), trk, crvDesc, UpdateCurve ); + } else { + crvDesc[TU].mode |= DESC_IGNORE; + DoDescribe( _("Curved Track"), trk, crvDesc, UpdateCurve ); + } +} + +static DIST_T DistanceCurve( track_p t, coOrd * p ) +{ + struct extraData *xx = GetTrkExtraData(t); + ANGLE_T a0, a1; + DIST_T d; + GetCurveAngles( &a0, &a1, t ); + if ( xx->helixTurns > 0 ) { + a0 = 0.0; + a1 = 360.0; + } + d = CircleDistance( p, xx->pos, xx->radius, a0, a1 ); + return d; +} + +static void DrawCurve( track_p t, drawCmd_p d, wDrawColor color ) +{ + struct extraData *xx = GetTrkExtraData(t); + ANGLE_T a0, a1; + track_p tt = t; + long widthOptions = DTS_LEFT|DTS_RIGHT|DTS_TIES; + + if (GetTrkWidth(t) == 2) + widthOptions |= DTS_THICK2; + if (GetTrkWidth(t) == 3) + widthOptions |= DTS_THICK3; + GetCurveAngles( &a0, &a1, t ); + if (xx->circle) { + tt = NULL; + } + if (xx->helixTurns > 0) { + a0 = 0.0; + a1 = 360.0; + } + if ( ((d->funcs->options&wDrawOptTemp)==0) && + (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) && + labelScale >= d->scale && + ( GetTrkBits( t ) & TB_HIDEDESC ) == 0 ) { + DrawCurveDescription( t, d, color ); + } + DrawCurvedTrack( d, xx->pos, xx->radius, a0, a1, + GetTrkEndPos(t,0), GetTrkEndPos(t,1), + t, GetTrkGauge(t), color, widthOptions ); + if ( (d->funcs->options & wDrawOptTemp) == 0 && + (d->options&DC_QUICK) == 0 && + (!IsCurveCircle(t)) ) { + DrawEndPt( d, t, 0, color ); + DrawEndPt( d, t, 1, color ); + } +} + +static void DeleteCurve( track_p t ) +{ +} + +static BOOL_T WriteCurve( track_p t, FILE * f ) +{ + struct extraData *xx = GetTrkExtraData(t); + long options; + BOOL_T rc = TRUE; + options = GetTrkWidth(t) & 0x0F; + if ( ( ( GetTrkBits(t) & TB_HIDEDESC ) != 0 ) == ( xx->helixTurns > 0 ) ) + options |= 0x80; + rc &= fprintf(f, "CURVE %d %d %ld 0 0 %s %d %0.6f %0.6f 0 %0.6f %ld %0.6f %0.6f\n", + GetTrkIndex(t), GetTrkLayer(t), (long)options, + GetTrkScaleName(t), GetTrkVisible(t), xx->pos.x, xx->pos.y, xx->radius, + xx->helixTurns, xx->descriptionOff.x, xx->descriptionOff.y )>0; + rc &= WriteEndPt( f, t, 0 ); + rc &= WriteEndPt( f, t, 1 ); + rc &= fprintf(f, "\tEND\n" )>0; + return rc; +} + +static void ReadCurve( char * line ) +{ + struct extraData *xx; + track_p t; + wIndex_t index; + BOOL_T visible; + DIST_T r; + coOrd p; + DIST_T elev; + char scale[10]; + wIndex_t layer; + long options; + char * cp = NULL; + + if (!GetArgs( line+6, paramVersion<3?"dXZsdpYfc":paramVersion<9?"dLl00sdpYfc":"dLl00sdpffc", + &index, &layer, &options, scale, &visible, &p, &elev, &r, &cp ) ) { + return; + } + t = NewTrack( index, T_CURVE, 0, sizeof *xx ); + xx = GetTrkExtraData(t); + SetTrkVisible(t, visible); + SetTrkScale(t, LookupScale(scale)); + SetTrkLayer(t, layer ); + SetTrkWidth(t, (int)(options&3)); + xx->pos = p; + xx->radius = r; + xx->helixTurns = 0; + xx->descriptionOff.x = xx->descriptionOff.y = 0.0; + if (cp) { + GetArgs( cp, "lp", &xx->helixTurns, &xx->descriptionOff ); + } + if ( ( ( options & 0x80 ) != 0 ) == ( xx->helixTurns > 0 ) ) + SetTrkBits(t,TB_HIDEDESC); + ReadSegs(); + SetEndPts(t,2); + if (GetTrkEndAngle( t, 0 ) == 270.0 && + GetTrkEndAngle( t, 1 ) == 90.0 ) + xx->circle = TRUE; + ComputeCurveBoundingBox( t, xx ); +} + +static void MoveCurve( track_p trk, coOrd orig ) +{ + struct extraData *xx = GetTrkExtraData(trk); + xx->pos.x += orig.x; + xx->pos.y += orig.y; + ComputeCurveBoundingBox( trk, xx ); +} + +static void RotateCurve( track_p trk, coOrd orig, ANGLE_T angle ) +{ + struct extraData *xx = GetTrkExtraData(trk); + Rotate( &xx->pos, orig, angle ); + ComputeCurveBoundingBox( trk, xx ); +} + +static void RescaleCurve( track_p trk, FLOAT_T ratio ) +{ + struct extraData *xx = GetTrkExtraData(trk); + xx->pos.x *= ratio; + xx->pos.y *= ratio; + xx->radius *= ratio; +} + +static ANGLE_T GetAngleCurve( track_p trk, coOrd pos, EPINX_T *ep0, EPINX_T *ep1 ) +{ + coOrd center; + DIST_T radius; + if ( ep0 ) *ep0 = 0; + if ( ep1 ) *ep1 = 1; + GetTrkCurveCenter( trk, ¢er, &radius ); + return FindAngle( center, pos ) - 90.0; +} + +static BOOL_T SplitCurve( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, EPINX_T * ep0, EPINX_T * ep1 ) +{ + struct extraData *xx = GetTrkExtraData(trk); + ANGLE_T a, a0, a1; + track_p trk1; + + if ( xx->helixTurns > 0 ) { + ErrorMessage( MSG_CANT_SPLIT_TRK, _("Helix") ); + return FALSE; + } + a = FindAngle( xx->pos, pos ); + GetCurveAngles( &a0, &a1, trk ); + if (xx->circle) { + a0 = a; + a1 = 359; + SetCurveAngles( trk, a0, a1, xx ); + *leftover = NULL; + return TRUE; + } + if (ep == 0) + a1 = NormalizeAngle(a-a0); + else { + a1 = NormalizeAngle(a0+a1-a); + a0 = a; + } + trk1 = NewCurvedTrack( xx->pos, xx->radius, a0, a1, 0 ); + AdjustCurveEndPt( trk, ep, a+(ep==0?-90.0:90.0) ); + *leftover = trk1; + *ep0 = 1-ep; + *ep1 = ep; + + return TRUE; +} + +static BOOL_T TraverseCurve( traverseTrack_p trvTrk, DIST_T * distR ) +{ + track_p trk = trvTrk->trk; + struct extraData *xx = GetTrkExtraData(trk); + ANGLE_T a, a0, a1, a2, a3; + DIST_T arcDist; + DIST_T circum; + DIST_T dist; + long turns; + if ( xx->circle ) + return FALSE; + circum = 2*M_PI*xx->radius; + GetCurveAngles( &a0, &a1, trk ); + a2 = FindAngle( xx->pos, trvTrk->pos ); + a = NormalizeAngle( (a2-90.0) - trvTrk->angle ); + if ( xx->helixTurns <= 0 ) { + if ( NormalizeAngle(a2-a0) > a1 ) { + if ( NormalizeAngle( a2-(a0+a1/2.0+180.0 ) ) < 180.0 ) + a2 = a0; + else + a2 = NormalizeAngle(a0+a1); + } + } + if ( a>270 || a<90 ) + arcDist = NormalizeAngle(a2-a0)/360.0*circum; + else + arcDist = NormalizeAngle(a0+a1-a2)/360.0*circum; + if ( xx->helixTurns > 0 ) { + turns = xx->helixTurns; + if ( NormalizeAngle(a2-a0) > a1 ) + turns -= 1; + dist = (a1/360.0+xx->helixTurns)*circum; + if ( trvTrk->length < 0 ) { + trvTrk->length = dist; + trvTrk->dist = a1/360.0*circum - arcDist; + while ( trvTrk->dist < 0 ) { + if ( trvTrk->dist > -0.1 ) + trvTrk->dist = 0.0; + else + trvTrk->dist += circum; + } + } else { + if ( trvTrk->length != dist ) { + printf( "traverseCurve: trvTrk->length(%0.3f) != Dist(%0.3f)\n", trvTrk->length, dist ); + trvTrk->length = dist; + } + if ( trvTrk->length < trvTrk->dist ) { + printf( "traverseCurve: trvTrk->length(%0.3f) < trvTrk->dist(%0.3f)\n", trvTrk->length, trvTrk->dist ); + trvTrk->dist = trvTrk->length; + } + a3 = trvTrk->dist/circum*360.0; + if ( a>270 || a<90 ) + a3 = (a0+a1-a3); + else + a3 = (a0+a3); + a3 = NormalizeAngle(a3); + if ( NormalizeAngle(a2-a3+1.0) > 2.0 ) + printf( "traverseCurve: A2(%0.3f) != A3(%0.3f)\n", a2, a3 ); + turns = (int)((trvTrk->length-trvTrk->dist)/circum); + } + arcDist += turns * circum; + } + if ( a>270 || a<90 ) { + /* CCW */ + if ( arcDist < *distR ) { + PointOnCircle( &trvTrk->pos, xx->pos, xx->radius, a0 ); + *distR -= arcDist; + trvTrk->angle = NormalizeAngle( a0-90.0 ); + trk = GetTrkEndTrk( trk, 0 ); + } else { + trvTrk->dist += *distR; + a2 -= *distR/circum*360.0; + PointOnCircle( &trvTrk->pos, xx->pos, xx->radius, a2 ); + *distR = 0; + trvTrk->angle = NormalizeAngle( a2-90.0 ); + } + } else { + /* CW */ + if ( arcDist < *distR ) { + PointOnCircle( &trvTrk->pos, xx->pos, xx->radius, a0+a1 ); + *distR -= arcDist; + trvTrk->angle = NormalizeAngle( a0+a1+90.0 ); + trk = GetTrkEndTrk( trk, 1 ); + } else { + trvTrk->dist += *distR; + a2 += *distR/circum*360.0; + PointOnCircle( &trvTrk->pos, xx->pos, xx->radius, a2 ); + *distR = 0; + trvTrk->angle = NormalizeAngle( a2+90.0 ); + } + } + trvTrk->trk = trk; + return TRUE; +} + + +static BOOL_T EnumerateCurve( track_p trk ) +{ + struct extraData *xx; + ANGLE_T a0, a1; + DIST_T d; + if (trk != NULL) { + xx = GetTrkExtraData(trk); + GetCurveAngles( &a0, &a1, trk ); + d = xx->radius * 2.0 * M_PI * a1 / 360.0; + if (xx->helixTurns > 0) + d += (xx->helixTurns-(xx->circle?1:0)) * xx->radius * 2.0 * M_PI; + ScaleLengthIncrement( GetTrkScale(trk), d ); + } + return TRUE; +} + +static BOOL_T TrimCurve( track_p trk, EPINX_T ep, DIST_T dist ) +{ + DIST_T d; + DIST_T radius; + ANGLE_T a, aa; + ANGLE_T a0, a1; + coOrd pos, center; + struct extraData *xx = GetTrkExtraData(trk); + if (xx->helixTurns>0) { + ErrorMessage( MSG_CANT_TRIM_HELIX ); + return FALSE; + } + a = NormalizeAngle( GetTrkEndAngle(trk,ep) + 180.0 ); + Translate( &pos, GetTrkEndPos(trk,ep), a, dist ); + GetTrkCurveCenter( trk, ¢er, &radius ); + GetCurveAngles( &a0, &a1, trk ); + a = FindAngle( center, pos ); + aa = NormalizeAngle(a - a0); + d = radius * aa * 2.0*M_PI/360.0; + if ( aa <= a1 && d > minLength ) { + UndrawNewTrack( trk ); + AdjustCurveEndPt( trk, ep, a+(ep==0?-90.0:90.0) ); + DrawNewTrack( trk ); + } else + DeleteTrack( trk, TRUE ); + return TRUE; +} + +static BOOL_T MergeCurve( + track_p trk0, + EPINX_T ep0, + track_p trk1, + EPINX_T ep1 ) +{ + struct extraData *xx0 = GetTrkExtraData(trk0); + struct extraData *xx1 = GetTrkExtraData(trk1); + ANGLE_T a00, a01, a10, a11; + DIST_T d; + track_p trk2; + EPINX_T ep2=-1; + coOrd pos; + + if (ep0 == ep1) + return FALSE; + if ( IsCurveCircle(trk0) || + IsCurveCircle(trk1) ) + return FALSE; + if ( xx0->helixTurns > 0 || + xx1->helixTurns > 0 ) + return FALSE; + d = FindDistance( xx0->pos, xx1->pos ); + d += fabs( xx0->radius - xx1->radius ); + if ( d > connectDistance ) + return FALSE; + + GetCurveAngles( &a00, &a01, trk0 ); + GetCurveAngles( &a10, &a11, trk1 ); + + UndoStart( _("Merge Curves"), "MergeCurve( T%d[%d] T%d[%d] )", GetTrkIndex(trk0), ep0, GetTrkIndex(trk1), ep1 ); + UndoModify( trk0 ); + UndrawNewTrack( trk0 ); + trk2 = GetTrkEndTrk( trk1, 1-ep1 ); + if (trk2) { + ep2 = GetEndPtConnectedToMe( trk2, trk1 ); + DisconnectTracks( trk1, 1-ep1, trk2, ep2 ); + } + if (ep0 == 0) { + (void)PointOnCircle( &pos, xx0->pos, xx0->radius, a10 ); + a10 = NormalizeAngle( a10-90.0 ); + SetTrkEndPoint( trk0, ep0, pos, a10 ); + } else { + (void)PointOnCircle( &pos, xx0->pos, xx0->radius, a10+a11 ); + a10 = NormalizeAngle( a10+a11+90.0 ); + SetTrkEndPoint( trk0, ep0, pos, a10 ); + } + DeleteTrack( trk1, FALSE ); + if (trk2) { + ConnectTracks( trk0, ep0, trk2, ep2 ); + } + DrawNewTrack( trk0 ); + ComputeCurveBoundingBox( trk0, GetTrkExtraData(trk0) ); + return TRUE; +} + + +static STATUS_T ModifyCurve( track_p trk, wAction_t action, coOrd pos ) +{ + static BOOL_T arcTangent; + static ANGLE_T arcA0, arcA1; + static EPINX_T ep; + static coOrd arcPos; + static DIST_T arcRadius; + static coOrd tangentOrig; + static coOrd tangentEnd; + static ANGLE_T angle; + static easementData_t jointD; + static BOOL_T valid; + + ANGLE_T a, aa1, aa2; + DIST_T r, d; + track_p trk1; + struct extraData *xx = GetTrkExtraData(trk); + + switch ( action ) { + + case C_DOWN: + arcTangent = FALSE; + GetCurveAngles( &arcA0, &arcA1, trk ); + if ( arcA0 == 0.0 && arcA1 == 360.0 ) + return C_ERROR; + if ( xx->helixTurns > 0 ) { + return C_ERROR; + } + ep = PickUnconnectedEndPoint( pos, trk ); + if ( ep == -1 ) + return C_ERROR; + GetTrkCurveCenter( trk, &arcPos, &arcRadius ); + UndrawNewTrack( trk ); + tempSegs(0).type = SEG_CRVTRK; + tempSegs(0).width = 0; + tempSegs(0).u.c.center = arcPos; + tempSegs(0).u.c.radius = arcRadius; + tempSegs(0).u.c.a0 = arcA0; + tempSegs(0).u.c.a1 = arcA1; + tempSegs_da.cnt = 1; + InfoMessage( _("Drag to change angle or create tangent") ); + case C_MOVE: + if (xx->helixTurns>0) + return C_CONTINUE; + valid = FALSE; + a = FindAngle( arcPos, pos ); + r = FindDistance( arcPos, pos ); + if ( r > arcRadius*(arcTangent?1.0:1.10) ) { + arcTangent = TRUE; + if ( easeR > 0.0 && arcRadius < easeR ) { + ErrorMessage( MSG_RADIUS_LSS_EASE_MIN, + FormatDistance( arcRadius ), FormatDistance( easeR ) ); + return C_CONTINUE; + } + aa1 = 90.0-R2D( asin( arcRadius/r ) ); + aa2 = NormalizeAngle( a + (ep==0?aa1:-aa1) ); + PointOnCircle( &tangentOrig, arcPos, arcRadius, aa2 ); + if (ComputeJoint( ep==0?-arcRadius:+arcRadius, 0, &jointD ) == E_ERROR) + return C_CONTINUE; + tangentEnd = pos; + if (jointD.x != 0.0) { + Translate( &tangentOrig, tangentOrig, aa2, jointD.x ); + Translate( &tangentEnd, tangentEnd, aa2, jointD.x ); + } + if (ep == 0) { + tempSegs(0).u.c.a0 = aa2; + tempSegs(0).u.c.a1 = NormalizeAngle( arcA0+arcA1-aa2 ); + } else { + tempSegs(0).u.c.a1 = NormalizeAngle(aa2-arcA0); + } + d = arcRadius * tempSegs(0).u.c.a1 * 2.0*M_PI/360.0; + d -= jointD.d0; + if ( d <= minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, _("Curved "), PutDim(fabs(minLength-d)) ); + return C_CONTINUE; + } + d = FindDistance( tangentOrig, tangentEnd ); + d -= jointD.d1; + if ( d <= minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, _("Tangent "), PutDim(fabs(minLength-d)) ); + return C_CONTINUE; + } + tempSegs(1).type = SEG_STRTRK; + tempSegs(1).width = 0; + tempSegs(1).u.l.pos[0] = tangentOrig; + tempSegs(1).u.l.pos[1] = tangentEnd; + tempSegs_da.cnt = 2; + if (action == C_MOVE) + InfoMessage( _("Tangent track: Length %s Angle %0.3f"), + FormatDistance( d ), + PutAngle( FindAngle( tangentOrig, tangentEnd ) ) ); + } else { + arcTangent = FALSE; + angle = NormalizeAngle( a + + ((ep==0)?-90:90)); + PointOnCircle( &pos, arcPos, arcRadius, a ); + if (ep != 0) { + tempSegs(0).u.c.a0 = NormalizeAngle( GetTrkEndAngle(trk,0)+90.0 ); + tempSegs(0).u.c.a1 = NormalizeAngle( a-tempSegs(0).u.c.a0 ); + } else { + tempSegs(0).u.c.a0 = a; + tempSegs(0).u.c.a1 = NormalizeAngle( (GetTrkEndAngle(trk,1)-90.0) - a ); + } + d = arcRadius*tempSegs(0).u.c.a1*2.0*M_PI/360.0; + if ( d <= minLength ) { + ErrorMessage( MSG_TRK_TOO_SHORT, _("Curved "), PutDim( fabs(minLength-d) ) ); + return C_CONTINUE; + } + tempSegs_da.cnt = 1; + if (action == C_MOVE) + InfoMessage( _("Curved: Radius=%s Length=%s Angle=%0.3f"), + FormatDistance( arcRadius ), FormatDistance( d ), + tempSegs(0).u.c.a1 ); + } + valid = TRUE; + return C_CONTINUE; + case C_UP: + if (xx->helixTurns>0) + return C_CONTINUE; + if (valid) { + if (arcTangent) { + trk1 = NewStraightTrack( tangentOrig, tangentEnd ); + CopyAttributes( trk, trk1 ); + /*UndrawNewTrack( trk );*/ + AdjustCurveEndPt( trk, ep, angle ); + JoinTracks( trk, ep, tangentOrig, + trk1, 0, tangentOrig, &jointD ); + DrawNewTrack( trk1 ); + } else { + AdjustCurveEndPt( trk, ep, angle ); + } + } + DrawNewTrack( trk ); + return C_TERMINATE; + default: + ; + } + return C_ERROR; +} + + +static DIST_T GetLengthCurve( track_p trk ) +{ + DIST_T dist, rad; + ANGLE_T a0, a1; + coOrd cen; + struct extraData *xx = GetTrkExtraData(trk); + + GetTrkCurveCenter( trk, &cen, &rad ); + if (xx->circle) + a1 = 360.0; + else + GetCurveAngles( &a0, &a1, trk ); + dist = (rad+GetTrkGauge(trk)/2.0)*a1*2.0*M_PI/360.0; + if (xx->helixTurns>0) + dist += (xx->helixTurns-(xx->circle?1:0)) * xx->radius * 2.0 * M_PI; + return dist; +} + + +static BOOL_T GetParamsCurve( int inx, track_p trk, coOrd pos, trackParams_t * params ) +{ + struct extraData *xx = GetTrkExtraData(trk); + params->type = curveTypeCurve; + GetTrkCurveCenter( trk, ¶ms->arcP, ¶ms->arcR); + GetCurveAngles( ¶ms->arcA0, ¶ms->arcA1, trk ); + if ( easeR > 0.0 && params->arcR < easeR ) { + ErrorMessage( MSG_RADIUS_LSS_EASE_MIN, + FormatDistance( params->arcR ), FormatDistance( easeR ) ); + return FALSE; + } + if ( inx == PARAMS_EXTEND && ( IsCurveCircle(trk) || xx->helixTurns > 0 ) ) { + ErrorMessage( MSG_CANT_EXTEND_HELIX ); + return FALSE; + } + params->len = params->arcR * params->arcA1 *2.0*M_PI/360.0; + if (xx->helixTurns > 0) + params->len += (xx->helixTurns-(xx->circle?1:0)) * xx->radius * 2.0 * M_PI; + params->helixTurns = xx->helixTurns; + if ( inx == PARAMS_PARALLEL ) { + params->ep = 0; + if (xx->helixTurns > 0) { + params->arcA0 = 0.0; + params->arcA1 = 360.0; + } + } else { + if ( IsCurveCircle( trk ) ) + params->ep = PickArcEndPt( params->arcP, /*Dj.inp[0].*/pos, pos ); + else + params->ep = PickUnconnectedEndPoint( pos, trk ); + if (params->ep == -1) + return FALSE; + } + return TRUE; +} + + +static BOOL_T MoveEndPtCurve( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d0 ) +{ + coOrd posCen; + DIST_T r; + ANGLE_T angle0; + ANGLE_T aa; + + GetTrkCurveCenter( *trk, &posCen, &r ); + angle0 = FindAngle( posCen, pos ); + aa = R2D( d0/r ); + if ( *ep==0 ) + angle0 += aa - 90.0; + else + angle0 -= aa - 90.0; + AdjustCurveEndPt( *trk, *ep, angle0 ); + return TRUE; +} + + +static BOOL_T QueryCurve( track_p trk, int query ) +{ + struct extraData * xx = GetTrkExtraData(trk); + switch ( query ) { + case Q_CAN_PARALLEL: + case Q_CAN_MODIFYRADIUS: + case Q_CAN_GROUP: + case Q_FLIP_ENDPTS: + case Q_ISTRACK: + case Q_HAS_DESC: + return TRUE; + case Q_EXCEPTION: + return xx->radius < minTrackRadius; + case Q_NOT_PLACE_FROGPOINTS: + return IsCurveCircle( trk ); + default: + return FALSE; + } +} + + +static void FlipCurve( + track_p trk, + coOrd orig, + ANGLE_T angle ) +{ + struct extraData * xx = GetTrkExtraData(trk); + FlipPoint( &xx->pos, orig, angle ); + ComputeCurveBoundingBox( trk, xx ); +} + + +static BOOL_T MakeParallelCurve( + track_p trk, + coOrd pos, + DIST_T sep, + track_p * newTrkR, + coOrd * p0R, + coOrd * p1R ) +{ + struct extraData * xx = GetTrkExtraData(trk); + struct extraData * xx1; + DIST_T rad; + ANGLE_T a0, a1; + + rad = FindDistance( pos, xx->pos ); + if ( rad > xx->radius ) + rad = xx->radius + sep; + else + rad = xx->radius - sep; + GetCurveAngles( &a0, &a1, trk ); + if ( newTrkR ) { + *newTrkR = NewCurvedTrack( xx->pos, rad, a0, a1, 0 ); + xx1 = GetTrkExtraData(*newTrkR); + xx1->helixTurns = xx->helixTurns; + xx1->circle = xx->circle; + ComputeCurveBoundingBox( *newTrkR, xx1 ); + } else { + if ( xx->helixTurns > 0) { + a0 = 0; + a1 = 360.0; + } + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).width = 0; + tempSegs_da.cnt = 1; + tempSegs(0).type = SEG_CRVTRK; + tempSegs(0).u.c.center = xx->pos; + tempSegs(0).u.c.radius = rad; + tempSegs(0).u.c.a0 = a0; + tempSegs(0).u.c.a1 = a1; + } + if ( p0R ) PointOnCircle( p0R, xx->pos, rad, a0 ); + if ( p1R ) PointOnCircle( p1R, xx->pos, rad, a0+a1 ); + return TRUE; +} + + +static trackCmd_t curveCmds = { + "CURVE", + DrawCurve, + DistanceCurve, + DescribeCurve, + DeleteCurve, + WriteCurve, + ReadCurve, + MoveCurve, + RotateCurve, + RescaleCurve, + NULL, + GetAngleCurve, + SplitCurve, + TraverseCurve, + EnumerateCurve, + NULL, /* redraw */ + TrimCurve, + MergeCurve, + ModifyCurve, + GetLengthCurve, + GetParamsCurve, + MoveEndPtCurve, + QueryCurve, + NULL, /* ungroup */ + FlipCurve, + NULL, + NULL, + NULL, + MakeParallelCurve }; + + +EXPORT void CurveSegProc( + segProc_e cmd, + trkSeg_p segPtr, + segProcData_p data ) +{ + ANGLE_T a0, a1, a2; + DIST_T d, circum, d0; + coOrd p0; + wIndex_t s0, s1; + + switch (cmd) { + + case SEGPROC_TRAVERSE1: + a1 = FindAngle( segPtr->u.c.center, data->traverse1.pos ); + a1 += (segPtr->u.c.radius>0?90.0:-90.0); + a2 = NormalizeAngle( data->traverse1.angle+a1 ); + data->traverse1.backwards = (a2 < 270 && a2 > 90 ); + a2 = FindAngle( segPtr->u.c.center, data->traverse1.pos ); + if ( data->traverse1.backwards == (segPtr->u.c.radius<0) ) { + a2 = NormalizeAngle( a2-segPtr->u.c.a0 ); + } else { + a2 = NormalizeAngle( segPtr->u.c.a0+segPtr->u.c.a1-a2 ); + } + data->traverse1.dist = a2/360.0*2*M_PI*fabs(segPtr->u.c.radius); + break; + + case SEGPROC_TRAVERSE2: + circum = 2*M_PI*segPtr->u.c.radius; + if ( circum < 0 ) + circum = - circum; + d = segPtr->u.c.a1/360.0*circum; + if ( d > data->traverse2.dist ) { + a2 = (data->traverse2.dist)/circum*360.0; + if ( data->traverse2.segDir == (segPtr->u.c.radius<0) ) { + a2 = NormalizeAngle( segPtr->u.c.a0+a2 ); + a1 = a2+90; + } else { + a2 = NormalizeAngle( segPtr->u.c.a0+segPtr->u.c.a1-a2 ); + a1 = a2-90; + } + PointOnCircle( &data->traverse2.pos, segPtr->u.c.center, fabs(segPtr->u.c.radius), a2 ); + data->traverse2.dist = 0; + data->traverse2.angle = a1; + } else { + data->traverse2.dist -= d; + } + break; + + case SEGPROC_DRAWROADBEDSIDE: + REORIGIN( p0, segPtr->u.c.center, data->drawRoadbedSide.angle, data->drawRoadbedSide.orig ); + d0 = segPtr->u.c.radius; + if ( d0 > 0 ) { + a0 = NormalizeAngle( segPtr->u.c.a0 + segPtr->u.c.a1*data->drawRoadbedSide.first/32.0 + data->drawRoadbedSide.angle ); + } else { + d0 = -d0; + a0 = NormalizeAngle( segPtr->u.c.a0 + segPtr->u.c.a1*(32-data->drawRoadbedSide.last)/32.0 + data->drawRoadbedSide.angle ); + } + a1 = segPtr->u.c.a1*(data->drawRoadbedSide.last-data->drawRoadbedSide.first)/32.0; + if (data->drawRoadbedSide.side>0) + d0 += data->drawRoadbedSide.roadbedWidth/2.0; + else + d0 -= data->drawRoadbedSide.roadbedWidth/2.0; + DrawArc( data->drawRoadbedSide.d, p0, d0, a0, a1, FALSE, data->drawRoadbedSide.rbw, data->drawRoadbedSide.color ); + break; + + case SEGPROC_DISTANCE: + data->distance.dd = CircleDistance( &data->distance.pos1, segPtr->u.c.center, fabs(segPtr->u.c.radius), segPtr->u.c.a0, segPtr->u.c.a1 ); + break; + + case SEGPROC_FLIP: + segPtr->u.c.radius = - segPtr->u.c.radius; + break; + + case SEGPROC_NEWTRACK: + data->newTrack.trk = NewCurvedTrack( segPtr->u.c.center, fabs(segPtr->u.c.radius), segPtr->u.c.a0, segPtr->u.c.a1, 0 ); + data->newTrack.ep[0] = (segPtr->u.c.radius>0?0:1); + data->newTrack.ep[1] = 1-data->newTrack.ep[0]; + break; + + case SEGPROC_LENGTH: + data->length.length = fabs(segPtr->u.c.radius) * segPtr->u.c.a1 * (2.0*M_PI/360.0); + break; + + case SEGPROC_SPLIT: + d = segPtr->u.c.a1/360.0 * 2*M_PI * fabs(segPtr->u.c.radius); + a2 = FindAngle( segPtr->u.c.center, data->split.pos ); + a2 = NormalizeAngle( a2 - segPtr->u.c.a0 ); + if ( a2 > segPtr->u.c.a1 ) { + if ( a2-segPtr->u.c.a1 < (360-segPtr->u.c.a1)/2.0 ) + a2 = segPtr->u.c.a1; + else + a2 = 0.0; + } + s0 = 0; + if ( segPtr->u.c.radius<0 ) + s0 = 1-s0; + s1 = 1-s0; + data->split.length[s0] = a2/360.0 * 2*M_PI * fabs(segPtr->u.c.radius); + data->split.length[s1] = d-data->split.length[s0]; + data->split.newSeg[0] = *segPtr; + data->split.newSeg[1] = *segPtr; + data->split.newSeg[s0].u.c.a1 = a2; + data->split.newSeg[s1].u.c.a0 = NormalizeAngle( data->split.newSeg[s1].u.c.a0 + a2 ); + data->split.newSeg[s1].u.c.a1 -= a2; + break; + + case SEGPROC_GETANGLE: + data->getAngle.angle = NormalizeAngle( FindAngle( data->getAngle.pos, segPtr->u.c.center ) + 90 ); + break; + } +} + + +/**************************************** + * + * GRAPHICS COMMANDS + * + */ + + + +EXPORT void PlotCurve( + long mode, + coOrd pos0, + coOrd pos1, + coOrd pos2, + curveData_t * curveData, + BOOL_T constrain ) +{ + DIST_T d0, d2, r; + ANGLE_T angle, a0, a1, a2; + coOrd posx; + + switch ( mode ) { + case crvCmdFromEP1: + angle = FindAngle( pos0, pos1 ); + d0 = FindDistance( pos0, pos2 )/2.0; + a0 = FindAngle( pos0, pos2 ); + a1 = NormalizeAngle( a0 - angle ); +LOG( log_curve, 3, ( "P1 = [%0.3f %0.3f] D=%0.3f A0=%0.3f A1=%0.3f\n", pos2.x, pos2.y, d0, a0, a1 ) ) + if (fabs(d0*sin(D2R(a1))) < (4.0/75.0)*mainD.scale) { +LOG( log_curve, 3, ( "Straight: %0.3f < %0.3f\n", d0*sin(D2R(a1)), (4.0/75.0)*mainD.scale ) ) + curveData->pos1.x = pos0.x + d0*2.0*sin(D2R(angle)); + curveData->pos1.y = pos0.y + d0*2.0*cos(D2R(angle)); + curveData->type = curveTypeStraight; + } else if (a1 >= 179.0 && a1 <= 181.0) { + curveData->type = curveTypeNone; + } else { + if (a1<180.0) { + a2 = NormalizeAngle( angle + 90.0 ); + if (constrain) + curveData->curveRadius = ConstrainR( d0/sin(D2R(a1)) ); + else + curveData->curveRadius = d0/sin(D2R(a1)); + } else { + a1 -= 360.0; + a2 = NormalizeAngle( angle - 90.0 ); + if (constrain) + curveData->curveRadius = ConstrainR( d0/sin(D2R(-a1)) ); + else + curveData->curveRadius = d0/sin(D2R(-a1)); + } + if (curveData->curveRadius > 1000) { + LOG( log_curve, 3, ( "Straight %0.3f > 1000\n", curveData->curveRadius ) ) + curveData->pos1.x = pos0.x + d0*2.0*sin(D2R(angle)); + curveData->pos1.y = pos0.y + d0*2.0*cos(D2R(angle)); + curveData->type = curveTypeStraight; + } else { + curveData->curvePos.x = pos0.x + curveData->curveRadius*sin(D2R(a2)); + curveData->curvePos.y = pos0.y + curveData->curveRadius*cos(D2R(a2)); + LOG( log_curve, 3, ( "Center = [%0.3f %0.3f] A1=%0.3f A2=%0.3f R=%0.3f\n", + curveData->curvePos.x, curveData->curvePos.y, a1, a2, curveData->curveRadius ) ) + if (a1 > 0.0) { + curveData->a0 = NormalizeAngle( a2-180 ); + curveData->a1 = a1 * 2.0; + } else { + curveData->a1 = (-a1) * 2.0; + curveData->a0 = NormalizeAngle( a2-180-curveData->a1 ); + } + curveData->type = curveTypeCurve; + } + } + break; + case crvCmdFromTangent: + case crvCmdFromCenter: + if ( mode == crvCmdFromCenter ) { + curveData->curvePos = pos0; + curveData->pos1 = pos1; + } else { + curveData->curvePos = pos1; + curveData->pos1 = pos0; + } + curveData->curveRadius = FindDistance( pos0, pos1 ); + a0 = FindAngle( curveData->curvePos, curveData->pos1 ); + a1 = FindAngle( curveData->curvePos, pos2 ); + if ( NormalizeAngle(a1-a0) < 180 ) { + curveData->a0 = a0; + curveData->a1 = NormalizeAngle(a1-a0); + } else { + curveData->a0 = a1; + curveData->a1 = NormalizeAngle(a0-a1); + } + curveData->type = curveTypeCurve; + break; + case crvCmdFromChord: + curveData->pos1 = pos1; + curveData->type = curveTypeStraight; + a0 = FindAngle( pos1, pos0 ); + d0 = FindDistance( pos0, pos1 )/2.0; + Rotate( &pos2, pos1, -a0 ); + pos2.x -= pos1.x; + if ( fabs(pos2.x) < 0.01 ) + break; + d2 = sqrt( d0*d0 + pos2.x*pos2.x )/2.0; + r = d2*d2*2.0/pos2.x; + if ( r > 1000.0 ) + break; + posx.x = (pos1.x+pos0.x)/2.0; + posx.y = (pos1.y+pos0.y)/2.0; + a0 -= 90.0; + LOG( log_curve, 3, ( "CHORD: [%0.3f %0.3f] [%0.3f %0.3f] [%0.3f %0.3f] A0=%0.3f D0=%0.3f D2=%0.3f R=%0.3f\n", pos0.x, pos0.y, pos1.x, pos1.y, pos2.x, pos2.y, a0, d0, d2, r ) ) + Translate( &curveData->curvePos, posx, a0, r-pos2.x ); + curveData->curveRadius = fabs(r); + a0 = FindAngle( curveData->curvePos, pos0 ); + a1 = FindAngle( curveData->curvePos, pos1 ); + if ( r > 0 ) { + curveData->a0 = a0; + curveData->a1 = NormalizeAngle(a1-a0); + } else { + curveData->a0 = a1; + curveData->a1 = NormalizeAngle(a0-a1); + } + curveData->type = curveTypeCurve; + break; + } +} + +EXPORT track_p NewCurvedTrack( coOrd pos, DIST_T r, ANGLE_T a0, ANGLE_T a1, long helixTurns ) +{ + struct extraData *xx; + track_p p; + p = NewTrack( 0, T_CURVE, 2, sizeof *xx ); + xx = GetTrkExtraData(p); + xx->pos = pos; + xx->radius = r; + xx->helixTurns = helixTurns; + if ( helixTurns <= 0 ) + SetTrkBits( p, TB_HIDEDESC ); + SetCurveAngles( p, a0, a1, xx ); +LOG( log_curve, 1, ( "NewCurvedTrack( %0.3f, %0.3f, %0.3f ) = %d\n", pos.x, pos.y, r, GetTrkIndex(p) ) ) + ComputeCurveBoundingBox( p, xx ); + CheckTrackLength( p ); + return p; +} + + + +EXPORT void InitTrkCurve( void ) +{ + T_CURVE = InitObject( &curveCmds ); + log_curve = LogFindIndex( "curve" ); +} diff --git a/app/bin/tease.c b/app/bin/tease.c new file mode 100644 index 0000000..3667fe1 --- /dev/null +++ b/app/bin/tease.c @@ -0,0 +1,1950 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/tease.c,v 1.2 2008-01-20 23:29:15 mni77 Exp $ + * + * TRANSISTION-CURVES (JOINTS) + * + */ + +/* 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. + */ + +/* + +Transistion-curves (aka joints or spirals) connect curves with different +radius (including straights which have infinite radius, indicated by radius=0). +The curve is described by 2 control parameters: R and L. +L is the length along the tangent of the curve and +R is the radius of an arc at the end of the curve. +At any point (l) along the tangent the arc at that point has radius +r=(R*L)/l. +The transition curve offset (x) is the closest distance between the arc +and the tangent. +The center of any arc is at (l/2, r+x). +See 'ComputeJointPos()' for details on this. + +Warning crude ascii graphics! + +a aa + aaa aaa * + aaaa aaaa * + aaaaa aaaaa *** + aaaaaaa aaaaaa **** + aaaaaaa aaaaaaa ***** + aaaaaaaaaaaaaaaaaaaa ****** + ^ ******* + x ******** + *******v* +0*****************************TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT + L/2 L + +'R' and 'L' are curve control parameters. +'0' is curve origin. +'**..TT' is tangent line. +'a' is arc with radius 'R'. +'*' is the transisition curve. +'x' is transisition curve offset. + +For a better representation of this, build 'testjoin' and +do 'testjoin psplot 10 10 40 1 | lpr -Ppostscript' +*/ + + +#include "track.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "cjoin.h" +#include "i18n.h" + +static TRKTYP_T T_EASEMENT = -1; + +static ANGLE_T JOINT_ANGLE_INCR = 2.0; + +struct extraData { + DIST_T l0, l1; /* curve start and end parameter */ + DIST_T R, L; /* curve control parameters */ + BOOL_T flip; /* T: endPt[1] - is l0 */ + BOOL_T negate; /* T: curves to the left */ + BOOL_T Scurve; /* T: is an S-curve */ + coOrd pos; /* Pos of origin */ + ANGLE_T angle; /* Angle of curve tangent */ + }; + +#define xl0 extraData->l0 +#define xl1 extraData->l1 +#define xR extraData->R +#define xL extraData->L +#define xflip extraData->flip +#define xnegate extraData->negate +#define xScurve extraData->Scurve +#define xpos extraData->pos +#define xangle extraData->angle + +#define EASE_MIN_X (0.01) + +static int log_ease; +static int log_traverseJoint; + + +static DIST_T FindL( + DIST_T r, + DIST_T R, + DIST_T L ) +/* + * Given a radius (r) return control value (l). + * This function is it's inverse! + */ +{ + return (r==0.0)?L:(R*L)/r; +} + + +static void GetLandD( + DIST_T *RL, + DIST_T *RD, + coOrd q, + coOrd p, + ANGLE_T a, + DIST_T R, + DIST_T L, + BOOL_T negate, + BOOL_T Scurve ) +{ + DIST_T l, d, x; + + q.x -= p.x; + q.y -= p.y; + Rotate( &q, zero, -a ); + l = q.y; + x = (l*l*l)/(6*R*L); + if (!negate) { + d = q.x - x; + } else { + d = q.x + x; + } + if (RL) + *RL = l; + if (RD) + *RD = d; +} + + +int OLDEASE = 0; +static void ComputeJoinPos( + DIST_T l, + DIST_T R, + DIST_T L, + DIST_T *RR, + ANGLE_T *RA, + coOrd *P, + coOrd *PC ) +/* + * Compute position along transition-curve. + * Also compute angle and position of tangent circle's center. + */ +{ + ANGLE_T a; + DIST_T r; + coOrd pp, pc; + if (l==0.0) + r = 100000.0; + else + r = (R*L)/l; + pp.y = l; + pc.y = l/2.0; + a = asin( l/2.0 / r ); +if (OLDEASE){ + pc.x = l*l / (24*r) + r; + pp.x = pc.x - r*cos(a); +}else{ + pp.x = (l*l*l)/(6*R*L); + pc.x = pp.x + r*cos(a); +} +/*lprintf( "A %0.3f %0.3f %0.3f [%0.3f %0.3f]\n", a, aa, aaa, q.x, q.y );*/ + if (P) + *P = pp; + if (PC) + *PC = pc; + if (RR) + *RR = r; + if (RA) + *RA = R2D(a); +} + +static DIST_T JoinD( + DIST_T l, + DIST_T R, + DIST_T L ) +/* + * Compute distance from transition-curve origin to specified point. + * Distance is approximately equal to length of arc from origin + * to specified point with radius = 2.0 * radius at point. + * This is a very good approximation (< 0.1%). + */ +{ + DIST_T rr1, d1; + ANGLE_T a1; + coOrd p0; + DIST_T sign = 1; + if ( l < 0 ) { + sign = -1; + l = -l; + } + ComputeJoinPos( l, R, L, &rr1, NULL, &p0, NULL ); + rr1 *= 2.0; + a1=asin(sqrt(p0.x*p0.x + p0.y*p0.y)/2.0/rr1); + d1 = rr1 * a1 * 2.0; + return d1*sign; +} + + +static DIST_T GetLfromD( + DIST_T D, + DIST_T R, + DIST_T L ) +{ + DIST_T deltaD, d, l, deltaL; + l = L/2.0; + deltaL = L/4.0; + while ( deltaL>0.0001 ) { + d = JoinD(l,R,L); + if ( d < D ) { + deltaD = D-d; + } else { + deltaD = d-D; + } + if ( deltaD < 0.000001 ) + return l; + if ( d < D ) + l += deltaL; + else + l -= deltaL; + deltaL /= 2.0; + } +/*printf( "GetLfromD( %0.3f %0.3f %0.3f ) = %0.3f\n", D, R, L, l );*/ + return l; +} + + +#ifdef LATER +static void JoinDistance( + DIST_T r, + DIST_T R, + DIST_T X, + DIST_T L, + DIST_T *dr, + DIST_T *xr, + DIST_T *lr ) +{ + DIST_T l, d, rr; + coOrd p, pc; + if (r == 0.0) { + *dr = 0.0; + *lr = *xr = 0.0; + return; + } + l = FindL( r, R, L ); + d = JoinD( l, R, L ); + ComputeJoinPos( l, R, L, NULL, NULL, &p, NULL ); +LOG( log_ease, 2, ( "joinDistance r=%0.3f rr=%0.3f\n", r, rr ) ) + *xr = pc.x - rr; + *dr = d; + *lr = pc.y; +} +#endif + +EXPORT STATUS_T ComputeJoint( + DIST_T r0, + DIST_T r1, + easementData_t * e ) +/* + * Compute joint data given radius of the 2 curves being joined. + * Radius is =0 for straight tracks and <0 for left-handed curves. + * S-curves are handled by treating them as 2 transition-curves joined + * origin to origin. + */ +{ + DIST_T t, l0, l1, d0, d1, rr0, rr1, xx; + ANGLE_T a, a0, a1; + coOrd rp0, rpc0, rp1, rpc1; + +LOG( log_ease, 4, ( "ComputeJoint( %0.3f, %0.3f )\n", r0, r1 ) ) + + if (easementVal <= 0.1) { + e->d0 = e->d1 = e->x = 0.0; + return E_NOTREQ; + } + if (r0 != 0.0 && fabs(r0) < easeR) { + ErrorMessage( MSG_RADIUS_LSS_EASE_MIN, + FormatDistance(fabs(r0)), FormatDistance(easeR) ); + e->d0 = e->d1 = e->x = 0.0; + return E_ERROR; + } + if (r1 != 0.0 && fabs(r1) < easeR) { + ErrorMessage( MSG_RADIUS_LSS_EASE_MIN, FormatDistance(fabs(r1)), FormatDistance(easeR) ); + e->d0 = e->d1 = e->x = 0.0; + return E_ERROR; + } + if (r0 == 0.0 && r1 == 0.0) { + /* ASSERT( FALSE ); CHECKME */ + e->d0 = e->d1 = e->x = 0.0; + return E_NOTREQ; + } + e->r0 = r0; + e->r1 = r1; + e->Scurve = FALSE; + if ( ! ( (r0 >= 0 && r1 >= 0) || (r0 <= 0 && r1 <= 0) ) ) { + /* S-curve */ + e->Scurve = TRUE; + e->flip = FALSE; + e->negate = (r0 > 0.0); + l0 = FindL( fabs(r0), easeR, easeL ); + ComputeJoinPos( l0, easeR, easeL, &rr0, NULL, &rp0, &rpc0 ); + l1 = FindL( fabs(r1), easeR, easeL ); + ComputeJoinPos( l1, easeR, easeL, &rr1, NULL, &rp1, &rpc1 ); + rp1.x = - rp1.x; + rp1.y = - rp1.y; + rpc1.x = - rpc1.x; + rpc1.y = - rpc1.y; + xx = FindDistance(rpc0, rpc1) - rr0 - rr1; + a0 = NormalizeAngle( FindAngle(rpc0, rp0) - FindAngle(rpc0, rpc1) ); + a1 = NormalizeAngle( FindAngle(rpc1, rp1) - FindAngle(rpc1, rpc0) ); + d0 = fabs( rr0 * D2R(a0) ); + d1 = fabs( rr1 * D2R(a1) ); + } else { + /* ! S-curve */ + e->negate = ( (r0==0.0||r1==0.0)? r0>r1 : r0<r1 ); + r0 = fabs(r0); + r1 = fabs(r1); + e->flip = FALSE; + if ( r1 == 0 || (r0 != 0 && r1 > r0 ) ) { + e->flip = TRUE; + t=r0; r0=r1; r1=t; + } + if (r0 == 0) { + if (r1 == 0) { + xx = l0 = l1 = d0 = d1 = 0.0; + } else { + l0 = 0.0; + l1 = FindL( r1, easeR, easeL ); + ComputeJoinPos( l1, easeR, easeL, &rr1, NULL, &rp1, &rpc1 ); + d0 = rpc1.y; + a1 = FindAngle(rpc1, rp1) - 270.0; + d1 = rr1 * D2R(a1); + xx = rpc1.x - rr1; + } + } else { + l0 = FindL( r0, easeR, easeL ); + ComputeJoinPos( l0, easeR, easeL, &rr0, NULL, &rp0, &rpc0 ); + l1 = FindL( r1, easeR, easeL ); + ComputeJoinPos( l1, easeR, easeL, &rr1, NULL, &rp1, &rpc1 ); + a = FindAngle( rpc0, rpc1 ); + a0 = a - FindAngle(rpc0, rp0);/*???*/ + a1 = FindAngle(rpc1, rp1) - a; + xx = rr0 - ( rr1 + FindDistance(rpc0, rpc1) ); + d0 = rr0 * D2R(a0); + d1 = rr1 * D2R(a1); + } + } +LOG( log_ease, 2, ( "CJoint(%0.3f %0.3f) l0=%0.3f d0=%0.3f l1=%0.3f d1=%0.3f x=%0.3f S%d F%d N%d\n", + e->r0, e->r1, l0, d0, l1, d1, xx, e->Scurve, e->flip, e->negate ) ) + if (xx < EASE_MIN_X || d0+d1<=minLength) { + e->d0 = e->d1 = e->x = 0.0; + return E_NOTREQ; + } else { + if (!e->flip) { + e->d0 = d0; + e->d1 = d1; + e->l0 = l0; + e->l1 = l1; + } else { + e->d0 = d1; + e->d1 = d0; + e->l0 = l1; + e->l1 = l0; + } + e->x = xx; + return E_REQ; + } +} + +static track_p NewJoint( + coOrd pos0, + ANGLE_T angle0, + coOrd pos1, + ANGLE_T angle1, + DIST_T trackGauge, + DIST_T R, + DIST_T L, + easementData_t * e ) +/* + * Allocate a joint track segment. + * Allocate a track, save relevant data from (*e), + * and compute origin and angle of transition-curve. + * Position is determined relative to endPoints. + */ +{ + track_p trk; + struct extraData *xx; + coOrd p, p0, p1, q0, q1; + static coOrd qZero = { 0.0, 0.0 }; + ANGLE_T az0, a01, b, b01, b1, d, d1; + trk = NewTrack( 0, T_EASEMENT, 2, sizeof *xx ); + SetTrkScale( trk, curScaleInx ); + xx = GetTrkExtraData( trk ); + SetTrkEndPoint( trk, 0, pos0, NormalizeAngle(angle0+180.0) ); + SetTrkEndPoint( trk, 1, pos1, NormalizeAngle(angle1+180.0) ); + xx->R = R; + xx->L = L; + xx->flip = e->flip; + xx->negate = e->negate; + xx->Scurve = e->Scurve; + if (!e->flip) { + xx->l0 = e->l0; + xx->l1 = e->l1; + p0 = pos0; + p1 = pos1; + } else { + xx->l0 = e->l1; + xx->l1 = e->l0; + p0 = pos1; + p1 = pos0; + } + ComputeJoinPos( xx->l0, R, L, NULL, NULL, &q0, NULL ); + ComputeJoinPos( xx->l1, R, L, NULL, NULL, &q1, NULL ); + if (e->negate) { + q0.x = -q0.x; + q1.x = -q1.x; + } + b01 = FindAngle( p0, p1 ); + if (!e->Scurve) { + az0 = FindAngle( qZero, q0 ); + a01 = FindAngle( q0, q1 ); + b1 = NormalizeAngle( b01 - (a01+az0) ); + b = NormalizeAngle( b01 - a01 ); + } else { + q1.x = -q1.x; + q1.y = -q1.y; + az0 = FindAngle( qZero, q0 ); + a01 = FindAngle( q0, q1 ); + b = NormalizeAngle( b01 - a01 ); + } + /*a = NormalizeAngle(a0+a1-90.0);*/ + p = q0; + Rotate( &p, qZero, b ); + xx->pos.x = p0.x - p.x; + xx->pos.y = p0.y - p.y; + xx->angle = b; + ComputeBoundingBox( trk ); + d = FindDistance( p0, p1 ); + d1 = FindDistance( q0, q1 ); +LOG( log_ease, 1, ( "NewJoint( [%0.3f %0.3f] A%0.3f, [%0.3f %0.3f] A%0.3f\n B01=%0.3f AZ0=%0.3f A01=%0.3f B=%0.3f D0=%0.3f D1=%0.3f\n", + pos0.x, pos0.y, angle0, pos1.x, pos1.y, angle1, + b01, az0, a01, b, d, d1 ) ) + CheckTrackLength( trk ); + return trk; +} + +/**************************************** + * + * GENERIC FUNCTIONS + * + */ + +static DIST_T GetLengthJoint( track_p trk ) +{ + struct extraData *xx; + DIST_T d0, d1; + xx = GetTrkExtraData(trk); + d0 = JoinD( xx->l0, xx->R, xx->L ); + d1 = JoinD( xx->l1, xx->R, xx->L ); + if (xx->Scurve) + return d0+d1; + else + return fabs( d0-d1 ); +} + + +static struct { + coOrd endPt[2]; + DIST_T elev[2]; + FLOAT_T length; + coOrd orig; + ANGLE_T angle; + DIST_T r; + DIST_T l; + DIST_T l0; + DIST_T l1; + FLOAT_T grade; + descPivot_t pivot; + LAYER_T layerNumber; + } jointData; +typedef enum { E0, Z0, E1, Z1, OR, AL, RR, LL, L0, L1, GR, PV, LY } jointDesc_e; +static descData_t jointDesc[] = { +/*E0*/ { DESC_POS, N_("End Pt 1: X"), &jointData.endPt[0] }, +/*Z0*/ { DESC_DIM, N_("Z"), &jointData.elev[0] }, +/*E1*/ { DESC_POS, N_("End Pt 2: X"), &jointData.endPt[1] }, +/*Z1*/ { DESC_DIM, N_("Z"), &jointData.elev[1] }, +/*OR*/ { DESC_POS, N_("Origin: X"), &jointData.orig }, +/*AL*/ { DESC_ANGLE, N_("Angle"), &jointData.angle }, +/*RR*/ { DESC_DIM, N_("R"), &jointData.r }, +/*LL*/ { DESC_DIM, N_("L"), &jointData.l }, +/*L0*/ { DESC_DIM, N_("l0"), &jointData.l0 }, +/*L1*/ { DESC_DIM, N_("l1"), &jointData.l1 }, +/*GR*/ { DESC_FLOAT, N_("Grade"), &jointData.grade }, +/*PV*/ { DESC_PIVOT, N_("Pivot"), &jointData.pivot }, +/*LY*/ { DESC_LAYER, N_("Layer"), &jointData.layerNumber }, + { DESC_NULL } }; + +static void UpdateJoint( track_p trk, int inx, descData_p descUpd, BOOL_T final ) +{ + EPINX_T ep; + switch (inx) { + case Z0: + case Z1: + ep = (inx==Z0?0:1); + UpdateTrkEndElev( trk, ep, GetTrkEndElevUnmaskedMode(trk,ep), jointData.elev[ep], NULL ); + ComputeElev( trk, 1-ep, FALSE, &jointData.elev[1-ep], NULL ); + if ( jointData.length > minLength ) + jointData.grade = fabs( (jointData.elev[0]-jointData.elev[1])/jointData.length )*100.0; + else + jointData.grade = 0.0; + jointDesc[GR].mode |= DESC_CHANGE; + jointDesc[inx==Z0?Z1:Z0].mode |= DESC_CHANGE; + return; + case LY: + SetTrkLayer( trk, jointData.layerNumber ); + break; + default: + return; + } +} + + +static void DescribeJoint( + track_p trk, + char * str, + CSIZE_T len ) +/* + * Print some interesting info about the track. + */ +{ + struct extraData *xx = GetTrkExtraData(trk); + int fix0, fix1; + + sprintf( str, _("Joint Track(%d): Layer=%d Length=%0.3f EP=[%0.3f,%0.3f A%0.3f] [%0.3f,%0.3f A%0.3f]"), GetTrkIndex(trk), + GetTrkLayer(trk)+1, + GetLengthJoint( trk ), + GetTrkEndPosXY(trk,0), GetTrkEndAngle(trk,0), + GetTrkEndPosXY(trk,1), GetTrkEndAngle(trk,1) ); + + fix0 = GetTrkEndTrk(trk,0)!=NULL; + fix1 = GetTrkEndTrk(trk,1)!=NULL; + + jointData.endPt[0] = GetTrkEndPos(trk,0); + jointData.endPt[1] = GetTrkEndPos(trk,1); + jointData.length = GetLengthJoint(trk); + jointData.orig = xx->pos; + jointData.angle = xx->angle; + jointData.r = xx->R; + jointData.l = xx->L; + jointData.l0 = xx->l0; + jointData.l1 = xx->l1; + jointData.layerNumber = GetTrkLayer(trk); + ComputeElev( trk, 0, FALSE, &jointData.elev[0], NULL ); + ComputeElev( trk, 1, FALSE, &jointData.elev[1], NULL ); + if ( jointData.length > minLength ) + jointData.grade = fabs( (jointData.elev[0]-jointData.elev[1])/jointData.length )*100.0; + else + jointData.grade = 0.0; + + jointDesc[E0].mode = + jointDesc[E1].mode = + jointDesc[OR].mode = + jointDesc[AL].mode = + jointDesc[RR].mode = + jointDesc[LL].mode = + jointDesc[L0].mode = + jointDesc[L1].mode = + DESC_RO; + jointDesc[Z0].mode = (EndPtIsDefinedElev(trk,0)?0:DESC_RO)|DESC_NOREDRAW; + jointDesc[Z1].mode = (EndPtIsDefinedElev(trk,1)?0:DESC_RO)|DESC_NOREDRAW; + jointDesc[GR].mode = DESC_RO; + jointDesc[PV].mode = (fix0|fix1)?DESC_IGNORE:0; + jointDesc[LY].mode = DESC_NOREDRAW; + jointData.pivot = (fix0&fix1)?DESC_PIVOT_NONE: + fix0?DESC_PIVOT_FIRST: + fix1?DESC_PIVOT_SECOND: + DESC_PIVOT_MID; + + DoDescribe( _("Easement Track"), trk, jointDesc, UpdateJoint ); +} + +static void GetJointPos( + coOrd * RP, + ANGLE_T * RA, + DIST_T l, + DIST_T R, + DIST_T L, + coOrd P, + ANGLE_T A, + BOOL_T N ) +/* + * Compute position of point on transition-curve. + */ +{ + coOrd p1; + static coOrd pZero = {0.0,0.0}; + ComputeJoinPos( l, R, L, NULL, RA, &p1, NULL ); + if (N) + p1.x = -p1.x; + Rotate( &p1, pZero, A ); + if (RP) { + RP->x = P.x + p1.x; + RP->y = P.y + p1.y; + } + if (RA) + *RA = NormalizeAngle( A + (N?-*RA:*RA) ); +} + + +EXPORT DIST_T JointDistance( + coOrd * q, + coOrd pos, + ANGLE_T angle, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + BOOL_T negate, + BOOL_T Scurve ) +{ + DIST_T d, l; + coOrd p0 = *q; + GetLandD( &l, &d, p0, pos, angle, R, L, negate, Scurve ); + if (Scurve) { + if ( l < -l1 ) + l = -l1; + else if ( l > l0 ) + l = l0; + } else { + if ( l < l0 ) + l = l0; + else if ( l > l1 ) + l = l1; + } + GetJointPos( q, NULL, l, R, L, pos, angle, negate ); + d = FindDistance( p0, *q ); + return d; +} + + +static DIST_T DistanceJoint( + track_p trk, + coOrd * p ) +/* + * Determine how close (p) is to (t). + */ +{ + struct extraData * xx = GetTrkExtraData(trk); + return JointDistance( p, xx->pos, xx->angle, xx->l0, xx->l1, xx->R, xx->L, xx->negate, xx->Scurve ); +} + + +#ifdef LATER +static void DrawJointSegment1( + drawCmd_p d, + wIndex_t cnt, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + coOrd P, + ANGLE_T A, + BOOL_T N, + track_p trk, + DIST_T trackGauge, + wDrawColor color ) +/* + * Draw a transition-curve from (l0) to (l1), + * at angle (A) from origin (P). + */ +{ + DIST_T l, lincr; + wIndex_t i; + coOrd p0, p1; + long widthOptions = DTS_RIGHT|DTS_LEFT|DTS_TIES; + + if (GetTrkWidth(trk) == 2) + widthOptions |= DTS_THICK2; + if (GetTrkWidth(trk) == 3) + widthOptions |= DTS_THICK3; + + l = l0; + lincr = (l1-l0)/cnt; + GetJointPos( &p0, NULL, l0, R, L, P, A, N ); + for (i=1; i<=cnt; i++) { + l += lincr; + GetJointPos( &p1, NULL, l, R, L, P, A, N ); + DrawStraightTrack( d, p0, p1, + FindAngle( p1, p0 ), trk, trackGauge, color, widthOptions ); + p0 = p1; + } +} +#endif + +static void DrawJointSegment( + drawCmd_p d, + wIndex_t cnt, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + coOrd P, + ANGLE_T A, + BOOL_T N, + DIST_T trackGauge, + wDrawColor color, + long widthOptions, + track_p trk ) +/* + * Draw a transition-curve from (l0) to (l1), + * at angle (A) from origin (P). + */ +{ + DIST_T ll; + wIndex_t i; + coOrd p0, p1; + ANGLE_T a0, a1; + int cnt1; + + ComputeJoinPos( l0, R, L, NULL, &a0, NULL, NULL ); + ComputeJoinPos( l1, R, L, NULL, &a1, NULL, NULL ); + a1 = a1-a0; + if ( (d->options&DC_QUICK) ) { + cnt1 = 1; + } else { + cnt1 = (int)floor(a1/JOINT_ANGLE_INCR) + 1; + a1 /= cnt1; + } + + widthOptions |= DTS_RIGHT|DTS_LEFT|DTS_TIES; + GetJointPos( &p0, NULL, l0, R, L, P, A, N ); + for (i=1; i<=cnt1; i++) { + a0 += a1; + ll = sqrt( sin(D2R(a0)) * 2 * R * L ); + GetJointPos( &p1, NULL, ll, R, L, P, A, N ); + DrawStraightTrack( d, p0, p1, FindAngle( p1, p0 ), trk, trackGauge, + color, widthOptions ); + p0 = p1; + } +} + + +EXPORT coOrd GetJointSegEndPos( + coOrd pos, + ANGLE_T angle, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + BOOL_T negate, + BOOL_T flip, + BOOL_T Scurve, + EPINX_T ep, + ANGLE_T * angleR ) +{ + coOrd p1; + DIST_T ll; + if ( flip ) ep = 1-ep; + ll = (ep==0?l0:l1); + if ( Scurve ) { + if ( ep==1 ) + angle += 180; + } + GetJointPos( &p1, &angle, ll, R, L, pos, angle, negate ); + if ( angleR ) { + if ( (!Scurve) && ep==0 ) + angle = NormalizeAngle(angle+180); + *angleR = angle; + } + return p1; +} + + +EXPORT void DrawJointTrack( + drawCmd_p d, + coOrd pos, + ANGLE_T angle, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + BOOL_T negate, + BOOL_T flip, + BOOL_T Scurve, + track_p trk, + EPINX_T ep0, + EPINX_T ep1, + DIST_T trackGauge, + wDrawColor color, + long options ) +{ + wIndex_t cnt; + DIST_T len; + trkSeg_p segPtr; + + if ( (d->options&DC_SEGTRACK) ) { + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + segPtr = &tempSegs(tempSegs_da.cnt-1); + segPtr->type = SEG_JNTTRK; + segPtr->width = 0; + segPtr->color = wDrawColorBlack; + segPtr->u.j.pos = pos; + segPtr->u.j.angle = angle; + segPtr->u.j.l0 = l0; + segPtr->u.j.l1 = l1; + segPtr->u.j.R = R; + segPtr->u.j.L = L; + segPtr->u.j.negate = negate; + segPtr->u.j.flip = flip; + segPtr->u.j.Scurve = Scurve; + return; + } +LOG( log_ease, 4, ( "DJT( (X%0.3f Y%0.3f A%0.3f) \n", pos.x, pos.y, angle ) ) +#ifdef LATER + scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale; + + if (options&DTS_THICK2) + width = 2; + if (options&DTS_THICK3) + width = 3; +#ifdef WINDOWS + width *= (wDrawWidth)(d->dpi/mainD.dpi); +#else + if (d->options&DC_PRINT) + width *= 300/75; +#endif +#endif + if (color == wDrawColorBlack) + color = normalColor; + if (!Scurve) { + /* print segments about 0.20" long */ + len = (l0-l1)/(0.20*d->scale); + cnt = (int)ceil(fabs(len)); + if (cnt == 0 || (d->options&DC_QUICK)) cnt = 1; + DrawJointSegment( d, cnt, l0, l1, R, L, pos, + angle, negate, trackGauge, color, options, trk ); + } else { + /* print segments about 0.20" long */ + cnt = (int)ceil((l0)/(0.20*d->scale)); + if (cnt == 0 || (d->options&DC_QUICK)) cnt = 1; + DrawJointSegment( d, cnt, 0, l0, R, L, pos, + angle, negate, trackGauge, color, options, trk ); + cnt = (int)ceil((l1)/(0.20*d->scale)); + if (cnt == 0 || (d->options&DC_QUICK)) cnt = 1; + DrawJointSegment( d, cnt, 0, l1, R, L, pos, + angle+180, negate, trackGauge, color, options, trk ); + } + if ( (d->funcs->options & wDrawOptTemp) == 0 && (d->options&DC_QUICK) == 0 ) { + DrawEndPt( d, trk, ep0, color ); + DrawEndPt( d, trk, ep1, color ); + } +} + + +static void DrawJoint( + track_p trk, + drawCmd_p d, + wDrawColor color ) +/* + * Draw a transition-curve. + */ +{ + struct extraData * xx = GetTrkExtraData(trk); + long widthOptions = 0; + + if (GetTrkWidth(trk) == 2) + widthOptions = DTS_THICK2; + if (GetTrkWidth(trk) == 3) + widthOptions = DTS_THICK3; + DrawJointTrack( d, xx->pos, xx->angle, xx->l0, xx->l1, xx->R, xx->L, xx->negate, xx->flip, xx->Scurve, trk, 0, 1, GetTrkGauge(trk), color, widthOptions ); +} + + +static void DeleteJoint( + track_p t ) +/* Delete track - nothing to do */ +{ +} + +static BOOL_T WriteJoint( + track_p t, + FILE * f ) +/* + * Write track data to a file (f). + */ +{ + struct extraData * xx = GetTrkExtraData(t); + BOOL_T rc = TRUE; + rc &= fprintf(f, "JOINT %d %d %ld 0 0 %s %d %0.6f %0.6f %0.6f %0.6f %d %d %d %0.6f %0.6f 0 %0.6f\n", + GetTrkIndex(t), GetTrkLayer(t), (long)GetTrkWidth(t), + GetTrkScaleName(t), GetTrkVisible(t), xx->l0, xx->l1, xx->R, xx->L, + xx->flip, xx->negate, xx->Scurve, xx->pos.x, xx->pos.y, xx->angle )>0; + rc &= WriteEndPt( f, t, 0 ); + rc &= WriteEndPt( f, t, 1 ); + rc &= fprintf(f, "\tEND\n" )>0; + return rc; +} + +static void ReadJoint( + char * line ) +/* + * Read track data from a file (f). + */ +{ + track_p trk; + TRKINX_T index; + BOOL_T visible; + struct extraData e, *xx; + char scale[10]; + wIndex_t layer; + long options; + DIST_T elev; + + if ( !GetArgs( line+6, paramVersion<3?"dXZsdffffdddpYf":paramVersion<9?"dLl00sdffffdddpYf":"dLl00sdffffdddpff", + &index, &layer, &options, scale, &visible, &e.l0, &e.l1, &e.R, &e.L, + &e.flip, &e.negate, &e.Scurve, &e.pos, &elev, &e.angle) ) + return; + trk = NewTrack( index, T_EASEMENT, 0, sizeof e ); + xx = GetTrkExtraData(trk); + SetTrkVisible(trk, visible); + SetTrkScale(trk, LookupScale(scale)); + SetTrkLayer(trk, layer); + SetTrkWidth(trk, (int)(options&3)); + *xx = e; + ReadSegs(); + SetEndPts( trk, 2 ); + ComputeBoundingBox( trk ); +} + +static void MoveJoint( + track_p trk, + coOrd orig ) +/* + * Move a track. + */ +{ + struct extraData * xx = GetTrkExtraData(trk); + xx->pos.x += orig.x; + xx->pos.y += orig.y; + ComputeBoundingBox( trk ); +} + +static void RotateJoint( + track_p trk, + coOrd orig, + ANGLE_T angle ) +/* + * Rotate a track. + */ +{ + struct extraData * xx = GetTrkExtraData(trk); + Rotate( &xx->pos, orig, angle ); + xx->angle = NormalizeAngle( xx->angle+angle ); + ComputeBoundingBox( trk ); +} + + +static void RescaleJoint( track_p trk, FLOAT_T ratio ) +{ + struct extraData *xx = GetTrkExtraData(trk); + xx->pos.x *= ratio; + xx->pos.y *= ratio; + xx->R *= ratio; + xx->L *= ratio; + xx->l0 *= ratio; + xx->l1 *= ratio; +} + + +static ANGLE_T GetAngleJoint( track_p trk, coOrd pos, EPINX_T * ep0, EPINX_T * ep1 ) +{ + struct extraData * xx = GetTrkExtraData(trk); + DIST_T l; + ANGLE_T a; + if ( ep0 && ep1 ) { + if (xx->flip) { + *ep0 = 1; + *ep1 = 0; + } else { + *ep0 = 0; + *ep1 = 1; + } + } + GetLandD( &l, NULL, pos, xx->pos, xx->angle, xx->R, xx->L, xx->negate, xx->Scurve ); + if (small(l)) { + a = xx->angle; + } else { +/* if (xx->Scurve && NormalizeAngle(FindAngle(xx->pos,pos)-xx->angle+90.0) > 180.0)*/ + if (xx->Scurve && l < 0.0) { + GetJointPos( NULL, &a, -l, xx->R, xx->L, xx->pos, xx->angle+180.0, xx->negate ); + a = NormalizeAngle( a-180.0 ); + } else { + GetJointPos( NULL, &a, l, xx->R, xx->L, xx->pos, xx->angle, xx->negate ); + } + } + return NormalizeAngle(a+180.0); +} + + +static void SplitJointA( + coOrd * posR, + EPINX_T ep, + struct extraData * xx, + struct extraData * xx1, + ANGLE_T * aR ) +{ + struct extraData * xx0; + BOOL_T flip; + DIST_T l; + + *xx1 = *xx; + if ( (ep==1) == (!xx->flip) ) { + xx0 = xx; + flip = FALSE; + } else { + xx0 = xx1; + xx1 = xx; + flip = TRUE; + } + GetLandD( &l, NULL, *posR, xx->pos, xx->angle, xx->R, xx->L, xx->negate, xx->Scurve ); + + if (!xx->Scurve) { + if (l < xx->l0 || l > xx->l1) { + NoticeMessage2( 0, "splitJoint: ! %0.3f <= %0.3f <= %0.3f", _("Ok"), NULL, xx->l0, l, xx->l1 ); + if ( l < xx->l0 ) l = xx->l0; + else if ( l > xx->l1 ) l = xx->l1; + } + GetJointPos( posR, aR, l, xx->R, xx->L, xx->pos, xx->angle, xx->negate ); + xx0->l1 = xx1->l0 = l; + } else if (small(l)){ + xx0->Scurve = xx1->Scurve = 0; + xx0->l1 = xx0->l0; + xx0->flip = !xx0->flip; + xx1->angle = NormalizeAngle(xx1->angle+180.0); + xx0->l0 = xx1->l0 = 0; + *posR = xx->pos; + *aR = xx1->angle; + } else { + GetJointPos( posR, aR, l, xx->R, xx->L, xx->pos, xx->angle, xx->negate ); + if (l > 0) { + xx0->Scurve = 0; + xx0->l1 = xx0->l0; + xx0->flip = !xx0->flip; + xx0->l0 = l; + xx1->l0 = l; + } else { + xx1->Scurve = 0; + xx1->l0 = -l; + xx1->angle = NormalizeAngle( xx1->angle+180.0 ); + xx0->l1 = -l; + } + *aR = NormalizeAngle( *aR+180.0 ); + } + if (flip) + *aR = NormalizeAngle( *aR + 180.0 ); +} + + +static BOOL_T SplitJoint( track_p trk, coOrd pos, EPINX_T ep, track_p * leftover, EPINX_T *ep0, EPINX_T *ep1 ) +{ + struct extraData *xx, *xx1; + track_p trk1; + ANGLE_T a; + + xx = GetTrkExtraData(trk); + trk1 = NewTrack( 0, T_EASEMENT, 2, sizeof *xx ); + xx1 = GetTrkExtraData(trk1); + *xx1 = *xx; + SetTrkEndPoint( trk1, ep, GetTrkEndPos(trk,ep), GetTrkEndAngle(trk,ep) ); + *leftover = trk1; + *ep0 = 1-ep; + *ep1 = ep; + SplitJointA( &pos, ep, xx, xx1, &a ); + SetTrkEndPoint( trk, ep, pos, a ); + SetTrkEndPoint( trk1, 1-ep, pos, NormalizeAngle(a+180.0) ); + + ComputeBoundingBox( trk ); + ComputeBoundingBox( trk1 ); + return TRUE; +} + + +static BOOL_T TraverseJoint( + coOrd * posR, + ANGLE_T *angleR, + DIST_T *distR, + coOrd pos, + ANGLE_T angle, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + BOOL_T negate, + BOOL_T flip, + BOOL_T Scurve ) +{ + + DIST_T l, lx, d, dx, ll0, ll1, d0, d1; + BOOL_T from_tangent, flip_angle; + + GetLandD( &l, &d, *posR, pos, angle, R, L, negate, Scurve ); + +LOG( log_traverseJoint, 2, ( "TJ: [%0.3f %0.3f] D%0.3f l0:%0.3f l1:%0.3f [%0.3f %0.3f] A%0.3f N%d F%d S%d = l:%0.3f ", + posR->x, posR->y, *distR, l0, l1, pos.x, pos.y, angle, negate, flip, Scurve, l ) ) + + if ( (!Scurve) ) { + if ( l < l0 ) l = l0; + else if ( l > l1 ) l = l1; + } else { + if ( l > l0 ) l = l0; + else if ( l < -l1 ) l = -l1; + } + + lx = l; + from_tangent = !flip; + flip_angle = from_tangent; + if ( !Scurve ) { + ll0 = l0; + ll1 = l1; + } else if ( l > 0 ) { + ll1 = l0; + ll0 = 0; + } else { + ll0 = 0; + ll1 = l1; + lx = -l; + from_tangent = !from_tangent; + } + dx = JoinD( lx, R, L ); + d0 = JoinD( ll0, R, L ); + d1 = JoinD( ll1, R, L ); + if ( from_tangent ) + d = d1 - dx; + else + d = dx - d0; + if ( *distR < d ) { + if ( from_tangent ) { + d = dx + *distR; + } else { + d = dx - *distR; + } + lx = GetLfromD( d, R, L ); + if ( l < 0 ) + lx = - lx; + /* compute posR and angleR */ + GetJointPos( posR, angleR, lx, R, L, pos, angle, negate ); + if ( ! flip_angle ) + *angleR = NormalizeAngle( *angleR + 180.0 ); + *distR = 0; + goto doreturn; + } + *distR -= d; + if ( Scurve && (!from_tangent) ) { + /* skip over midpoint */ + if ( l > 0 ) + d = JoinD( l1, R, L ); + else + d = JoinD( l0, R, L ); + if ( *distR < d ) { + lx = GetLfromD( *distR, R, L ); + if ( l > 0 ) + lx = - lx; + GetJointPos( posR, angleR, lx, R, L, pos, angle, negate ); + if ( ! flip_angle ) + *angleR = NormalizeAngle( *angleR + 180.0 ); + *distR = 0; + goto doreturn; + } + *distR -= d; + } +doreturn: +LOG( log_traverseJoint, 2, ( " [%0.3f %0.3f] A%0.3f D%0.3f\n", posR->x, posR->y, *angleR, *distR ) ) + return TRUE; +} + + +static BOOL_T TraverseJointTrack( + traverseTrack_p trvTrk, + DIST_T * distR ) +{ + track_p trk = trvTrk->trk; + struct extraData * xx = GetTrkExtraData(trk); + BOOL_T rc; + EPINX_T ep; + ANGLE_T angle; + BOOL_T flip; + + angle = NormalizeAngle( xx->angle-trvTrk->angle ); + flip = ( angle < 270 && angle > 90 ); + rc = TraverseJoint( &trvTrk->pos, &trvTrk->angle, distR, xx->pos, xx->angle, xx->l0, xx->l1, xx->R, xx->L, xx->negate, flip, xx->Scurve ); + if ( *distR > 0 ) { + ep = (flip?0:1); + if ( xx->flip ) + ep = 1-ep; + if ( xx->Scurve ) + ep = 1-ep; + trvTrk->pos = GetTrkEndPos( trk, ep ); + trvTrk->angle = GetTrkEndAngle( trk, ep ); + trvTrk->trk = GetTrkEndTrk( trk, ep ); + } + return rc; +} + + +static BOOL_T EnumerateJoint( track_p trk ) +{ + if (trk != NULL) { + ScaleLengthIncrement( GetTrkScale(trk), GetLengthJoint(trk) ); + } + return TRUE; +} + +static BOOL_T TrimJoint( track_p trk, EPINX_T ep, DIST_T maxX ) +{ + DeleteTrack( trk, FALSE ); + return TRUE; +} + + +static BOOL_T MergeJoint( + track_p trk0, + EPINX_T ep0, + track_p trk1, + EPINX_T ep1 ) +{ + track_p trk2; + EPINX_T ep2=-1; + coOrd pos; + ANGLE_T a; + struct extraData *xx0 = GetTrkExtraData(trk0); + struct extraData *xx1 = GetTrkExtraData(trk1); + + if ( ep0 == ep1 ) + return FALSE; + if ( xx0->R != xx1->R || + xx0->L != xx1->L || + xx0->flip != xx1->flip || + xx0->negate != xx1->negate || + xx0->angle != xx1->angle || + xx0->Scurve || + xx1->Scurve || + FindDistance( xx0->pos, xx1->pos ) > connectDistance ) + return FALSE; + + UndoStart( _("Merge Easements"), "MergeJoint( T%d[%d] T%d[%d] )", GetTrkIndex(trk0), ep0, GetTrkIndex(trk1), ep1 ); + UndoModify( trk0 ); + UndrawNewTrack( trk0 ); + trk2 = GetTrkEndTrk( trk1, 1-ep1 ); + if (trk2) { + ep2 = GetEndPtConnectedToMe( trk2, trk1 ); + DisconnectTracks( trk1, 1-ep1, trk2, ep2 ); + } + + if (ep0 == 0) { + xx0->l0 = xx1->l0; + } else { + xx0->l1 = xx1->l1; + } + + pos = GetTrkEndPos( trk1, 1-ep1 ); + a = GetTrkEndAngle( trk1, 1-ep1 ); + SetTrkEndPoint( trk0, ep0, pos, a ); + ComputeBoundingBox( trk0 ); + + DeleteTrack( trk1, TRUE ); + if (trk2) { + ConnectTracks( trk0, ep0, trk2, ep2 ); + } + DrawNewTrack( trk0 ); + return TRUE; +} + + +static BOOL_T GetParamsJoint( int inx, track_p trk, coOrd pos, trackParams_t * params ) +{ + params->type = curveTypeStraight; + params->ep = PickUnconnectedEndPoint( pos, trk ); + if (params->ep == -1) + return FALSE; + params->lineOrig = GetTrkEndPos(trk,params->ep); + params->lineEnd = params->lineOrig; + params->angle = GetTrkEndAngle(trk,params->ep); + params->len = 0.0; + params->arcR = 0.0; + return TRUE; +} + + +static BOOL_T MoveEndPtJoint( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d ) +{ + return FALSE; +} + + +static BOOL_T QueryJoint( track_p trk, int query ) +{ + struct extraData * xx = GetTrkExtraData(trk); + track_p trk1; + + switch ( query ) { + case Q_CANNOT_BE_ON_END: + case Q_IGNORE_EASEMENT_ON_EXTEND: + case Q_ISTRACK: + return TRUE; + case Q_CAN_PARALLEL: + if ( xx->Scurve ) { + if ( FindDistance( xx->pos, GetTrkEndPos(trk,0) ) <= minLength || + FindDistance( xx->pos, GetTrkEndPos(trk,1) ) <= minLength ) + return FALSE; + UndoStart( _("Split Easement Curve"), "queryJoint T%d Scurve", GetTrkIndex(trk) ); + SplitTrack( trk, xx->pos, 0, &trk1, FALSE ); + } + return TRUE; + default: + return FALSE; + } +} + + +static void FlipJoint( + track_p trk, + coOrd orig, + ANGLE_T angle ) +{ + struct extraData * xx = GetTrkExtraData(trk); + FlipPoint( &xx->pos, orig, angle ); + xx->angle = NormalizeAngle( 2*angle - xx->angle ); + xx->negate = !xx->negate; + ComputeBoundingBox( trk ); +} + + +static BOOL_T MakeParallelJoint( + track_p trk, + coOrd pos, + DIST_T sep, + track_p * newTrkR, + coOrd * p0R, + coOrd * p1R ) +{ + struct extraData * xx = GetTrkExtraData(trk), *xx1; + ANGLE_T angle, A; + coOrd p0, p1, P, q1, r1; + DIST_T d, d0; + DIST_T R, L, l0, l1, len, dl; + int cnt, inx; + + if ( xx->Scurve ) + return FALSE; + GetLandD( NULL, &d, pos, xx->pos, xx->angle, xx->R, xx->L, xx->negate, FALSE ); + angle = 90.0; + if ( (d < 0) == xx->negate ) + sep = -sep; + if ( xx->negate ) + angle = -90.0; + if ( xx->flip ) + angle = -angle; + p0 = GetTrkEndPos(trk,0); + p1 = GetTrkEndPos(trk,1); + d0 = FindDistance( p0, p1 ); + Translate( &p0, p0, GetTrkEndAngle(trk,0)+angle, sep ); + Translate( &p1, p1, GetTrkEndAngle(trk,1)-angle, sep ); + d = FindDistance( p0, p1 ); + angle = R2D(asin(xx->L/2/xx->R)); + A = xx->angle; + R = xx->R + sep*sin(D2R(angle)); + + dl = JoinD( xx->l1, xx->R, xx->L ) - JoinD( xx->l0, xx->R, xx->L ); +/*printf( "D = %0.3f %0.3f\n", d, dl );*/ + d /= d0; + R = xx->R * d; + L = xx->L * d; + l0 = xx->l0 * d; + l1 = xx->l1 * d; +/*printf( " R=%0.3f, L=%0.3f, l0=%0.3f, l1=%0.3f\n", R, L, l0, l1 );*/ + Translate( &P, xx->pos, xx->angle+(xx->negate?90:-90), sep ); + ComputeJoinPos( l1, R, L, NULL, NULL, &q1, NULL ); + r1 = (xx->flip?p0:p1); + r1.x -= P.x; + r1.y -= P.y; + Rotate( &r1, zero, -A ); + if ( xx->negate ) + r1.x = -r1.x; + if ( r1.x > 0 && q1.x > 0 ) { +/*printf( " %0.3f %0.3f, R=%0.3f ", q1.x, r1.x, R );*/ + R *= q1.x/r1.x; +/*printf( " %0.3f\n", R );*/ + } + + if ( newTrkR ) { + *newTrkR = NewTrack( 0, T_EASEMENT, 2, sizeof *xx ); + xx1 = GetTrkExtraData( *newTrkR ); + *xx1 = *xx; + xx1->angle = A; + xx1->R = R; + xx1->L = L; + xx1->l0 = l0; + xx1->l1 = l1; + xx1->pos = P; + SetTrkEndPoint( *newTrkR, 0, p0, GetTrkEndAngle(trk,0) ); + SetTrkEndPoint( *newTrkR, 1, p1, GetTrkEndAngle(trk,1) ); + ComputeBoundingBox( *newTrkR ); + } else { + /* print segments about 0.20" long */ + dl = fabs(l0-l1); + len = dl/(0.20*mainD.scale); + cnt = (int)ceil(len); + if (cnt == 0 || (mainD.options&DC_QUICK)) cnt = 1; + dl /= cnt; + DYNARR_SET( trkSeg_t, tempSegs_da, cnt ); + for ( inx=0; inx<cnt; inx++ ) { + tempSegs(inx).color = wDrawColorBlack; + tempSegs(inx).width = 0; + tempSegs(inx).type = SEG_STRTRK; + if ( inx == 0 ) { + GetJointPos( &tempSegs(inx).u.l.pos[0], NULL, l0, R, L, P, A, xx->negate ); + } else { + tempSegs(inx).u.l.pos[0] = tempSegs(inx-1).u.l.pos[1]; + } + l0 += dl; + GetJointPos( &tempSegs(inx).u.l.pos[1], NULL, l0, R, L, P, A, xx->negate ); + } + tempSegs_da.cnt = cnt; + } + if ( p0R ) *p0R = p0; + if ( p1R ) *p1R = p1; + return TRUE; +} + + +static trackCmd_t easementCmds = { + "JOINT", + DrawJoint, + DistanceJoint, + DescribeJoint, + DeleteJoint, + WriteJoint, + ReadJoint, + MoveJoint, + RotateJoint, + RescaleJoint, + NULL, /* audit */ + GetAngleJoint, + SplitJoint, + TraverseJointTrack, + EnumerateJoint, + NULL, /* redraw */ + TrimJoint, + MergeJoint, + ExtendStraightFromOrig, + GetLengthJoint, + GetParamsJoint, + MoveEndPtJoint, + QueryJoint, + NULL, /* ungroup */ + FlipJoint, + NULL, + NULL, + NULL, + MakeParallelJoint }; + + +EXPORT void JointSegProc( + segProc_e cmd, + trkSeg_p segPtr, + segProcData_p data ) +{ + DIST_T l; + ANGLE_T a; + BOOL_T flip; + struct extraData * xx, xxx[2]; + coOrd p; + int inx; + EPINX_T ep0; + + switch (cmd) { + + case SEGPROC_TRAVERSE1: + GetLandD( &l, NULL, data->traverse1.pos, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.Scurve ); + if (small(l)) { + a = segPtr->u.j.angle; + } else { + if (segPtr->u.j.Scurve && l < 0.0) { + GetJointPos( NULL, &a, -l, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.pos, segPtr->u.j.angle+180.0, segPtr->u.j.negate ); + a = NormalizeAngle( a-180.0 ); + } else { + GetJointPos( NULL, &a, l, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.negate ); + } + } + a = NormalizeAngle( data->traverse1.angle+a ); + data->traverse1.backwards = (a < 270 && a > 90 ); + if ( !segPtr->u.j.Scurve ) { + if ( data->traverse1.backwards==0 ) + data->traverse1.dist = JoinD( l, segPtr->u.j.R, segPtr->u.j.L ) - JoinD( segPtr->u.j.l0, segPtr->u.j.R, segPtr->u.j.L ); + else + data->traverse1.dist = JoinD( segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L ) - JoinD( l, segPtr->u.j.R, segPtr->u.j.L ); + } else { + data->traverse1.backwards = !data->traverse1.backwards; + if ( data->traverse1.backwards==0 ) + data->traverse1.dist = JoinD( segPtr->u.j.l0, segPtr->u.j.R, segPtr->u.j.L ) - JoinD( l, segPtr->u.j.R, segPtr->u.j.L ); + else + data->traverse1.dist = JoinD( segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L ) + JoinD( l, segPtr->u.j.R, segPtr->u.j.L ); + } + if ( segPtr->u.j.flip ) + data->traverse1.backwards = !data->traverse1.backwards; +LOG( log_traverseJoint, 1, ( "TJ0: ?[%0.3f %0.3f] A=%0.3f l=%0.3f J[%0.3f %0.3f] A=%0.3f l0=%0.3f l1=%0.3f R=%0.3f L=%0.3f N:%d F:%d S:%d = a=%0.3f D=%0.3f B=%d\n", + data->traverse1.pos.x, data->traverse1.pos.y, data->traverse1.angle, + l, + segPtr->u.j.pos.x, segPtr->u.j.pos.y, segPtr->u.j.angle, + segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, + segPtr->u.j.negate, segPtr->u.j.flip, segPtr->u.j.Scurve, + a, data->traverse1.dist, data->traverse1.backwards ) ); + break; + + case SEGPROC_TRAVERSE2: + flip = segPtr->u.j.flip; + if (data->traverse2.segDir!=0) + flip = !flip; + if (segPtr->u.j.Scurve) + flip = !flip; + data->traverse2.pos = GetSegEndPt( segPtr, data->traverse2.segDir, FALSE, NULL ); + TraverseJoint( &data->traverse2.pos, &data->traverse2.angle, &data->traverse2.dist, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, flip, segPtr->u.j.Scurve ); + break; + + case SEGPROC_DRAWROADBEDSIDE: + /* TODO: JointSegProc( SEGPROC_DRAWROADBEDSIDE, ... */ + break; + + case SEGPROC_DISTANCE: + data->distance.dd = JointDistance( &data->distance.pos1, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.Scurve ); + break; + + case SEGPROC_FLIP: + segPtr->u.j.flip = !segPtr->u.j.flip; + break; + + case SEGPROC_NEWTRACK: + data->newTrack.trk = NewTrack( 0, T_EASEMENT, 2, sizeof *xx ); + xx = GetTrkExtraData(data->newTrack.trk); + xx->pos = segPtr->u.j.pos; + xx->angle = segPtr->u.j.angle; + xx->l0 = segPtr->u.j.l0; + xx->l1 = segPtr->u.j.l1; + xx->R = segPtr->u.j.R; + xx->L = segPtr->u.j.L; + xx->negate = segPtr->u.j.negate; + xx->flip = segPtr->u.j.flip; + xx->Scurve = segPtr->u.j.Scurve; + ep0 = 0; + if ( xx->flip ) + ep0 = 1-ep0; + if ( xx->Scurve ) + ep0 = 1-ep0; + GetJointPos( &p, &a, xx->l0, xx->R, xx->L, xx->pos, xx->angle, xx->negate ); + if ( !xx->Scurve ) + a = NormalizeAngle(a+180.0); + SetTrkEndPoint( data->newTrack.trk, ep0, p, a ); + a = xx->angle; + if ( xx->Scurve ) + a = NormalizeAngle(a+180.0); + GetJointPos( &p, &a, xx->l1, xx->R, xx->L, xx->pos, a, xx->negate ); + if ( xx->Scurve ) + a = NormalizeAngle(a+180.0); + SetTrkEndPoint( data->newTrack.trk, 1-ep0, p, a ); + ComputeBoundingBox( data->newTrack.trk ); + data->newTrack.ep[0] = 0; + data->newTrack.ep[1] = 1; + break; + + case SEGPROC_LENGTH: + if ( !segPtr->u.j.Scurve ) + data->length.length = JoinD( segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L ) - JoinD( segPtr->u.j.l0, segPtr->u.j.R, segPtr->u.j.L ); + else + data->length.length = JoinD( segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L ) + JoinD( segPtr->u.j.l0, segPtr->u.j.R, segPtr->u.j.L ); + break; + + case SEGPROC_SPLIT: + xxx[0].pos = segPtr->u.j.pos; + xxx[0].angle = segPtr->u.j.angle; + xxx[0].l0 = segPtr->u.j.l0; + xxx[0].l1 = segPtr->u.j.l1; + xxx[0].R = segPtr->u.j.R; + xxx[0].L = segPtr->u.j.L; + xxx[0].negate = segPtr->u.j.negate; + xxx[0].flip = segPtr->u.j.flip; + xxx[0].Scurve = segPtr->u.j.Scurve; + SplitJointA( &data->split.pos, 0, &xxx[0], &xxx[1], &a ); + for ( inx=0; inx<2; inx++ ) { + xx = &xxx[(!segPtr->u.j.flip)?1-inx:inx]; + data->split.newSeg[inx] = *segPtr; + data->split.newSeg[inx].u.j.pos = xx->pos; + data->split.newSeg[inx].u.j.angle = xx->angle; + data->split.newSeg[inx].u.j.l0 = xx->l0; + data->split.newSeg[inx].u.j.l1 = xx->l1; + data->split.newSeg[inx].u.j.R = xx->R; + data->split.newSeg[inx].u.j.L = xx->L; + data->split.newSeg[inx].u.j.negate = xx->negate; + data->split.newSeg[inx].u.j.flip = xx->flip; + data->split.newSeg[inx].u.j.Scurve = xx->Scurve; + if ( !xx->Scurve ) + data->split.length[inx] = JoinD( xx->l1, xx->R, xx->L ) - JoinD( xx->l0, xx->R, xx->L ); + else + data->split.length[inx] = JoinD( xx->l1, xx->R, xx->L ) + JoinD( xx->l0, xx->R, xx->L ); + } + break; + + case SEGPROC_GETANGLE: + GetLandD( &l, NULL, data->getAngle.pos, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.Scurve ); + if (small(l)) { + a = segPtr->u.j.angle; + } else { + if (segPtr->u.j.Scurve && l < 0.0) { + GetJointPos( NULL, &a, -l, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.pos, segPtr->u.j.angle+180.0, segPtr->u.j.negate ); + a = NormalizeAngle( a-180.0 ); + } else { + GetJointPos( NULL, &a, l, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.negate ); + } + } + data->getAngle.angle = a; + break; + } +} + + + +#ifndef TEST +BOOL_T JoinTracks( + track_p trk0, + EPINX_T ep0, + coOrd pos0, + track_p trk1, + EPINX_T ep1, + coOrd pos1, + easementData_t * e ) +/* + * Join 2 tracks with joint described in (e). + * (pos0) and (pos1) are points that would be connected if there was no + * transition-curve. + * If there is then: + * (pos0) and (pos1) have been moved (x) apart. + * Adjust the endPoints by moving (pos0) and (pos1) by (e->d0) and (e->d1) + * along the track. + * Connect the tracks. + */ +{ + track_p joint; + +LOG( log_ease, 1, ( "join T%d[%d] @[%0.3f %0.3f], T%d[%d] @[%0.3f %0.3f]\n", + GetTrkIndex(trk0), ep0, pos0.x, pos0.y, GetTrkIndex(trk1), ep1, pos1.x, pos1.y ) ) + + if ( GetTrkType(trk0) == T_EASEMENT ) { + DIST_T d; + ANGLE_T aa; + d = FindDistance( GetTrkEndPos(trk0,ep0), GetTrkEndPos(trk1,ep1) ); + aa = NormalizeAngle( GetTrkEndAngle(trk0,ep0) - GetTrkEndAngle(trk1,ep1) + 180.0 + connectAngle/2.0 ); + if ( d <= connectDistance && aa <= connectAngle ) { + ConnectTracks( trk0, ep0, trk1, ep1 ); + } + return TRUE; + } + + /* Move the endPoint for (trk0) */ + if (!MoveEndPt( &trk0, &ep0, pos0, e->d0 )) + return FALSE; + + /* Move the endPoint for (trk1) */ + if (!MoveEndPt( &trk1, &ep1, pos1, e->d1 )) + return FALSE; + +LOG( log_ease, 1, ( " EASE R%0.3f..%0.3f L%0.3f..%0.3f\n", + e->r0, e->r1, e->d0, e->d1 ) ) + + /* Connect the tracks */ + if (e->x == 0.0) { + /* No transition-curve */ + ConnectTracks( trk0, ep0, trk1, ep1 ); + } else { + /* Connect with transition-curve */ + joint = NewJoint( GetTrkEndPos(trk0,ep0), GetTrkEndAngle(trk0,ep0), + GetTrkEndPos(trk1,ep1), GetTrkEndAngle(trk1,ep1), + GetTrkGauge(trk0), easeR, easeL, e ); + CopyAttributes( trk0, joint ); + ConnectTracks( trk1, ep1, joint, 1 ); + ConnectTracks( trk0, ep0, joint, 0 ); + DrawNewTrack( joint ); + } + return TRUE; +} + + +EXPORT void UndoJoint( + track_p trk, + EPINX_T ep, + track_p trk1, + EPINX_T ep1 ) +{ + struct extraData * xx; + DIST_T d; + + if ( GetTrkType(trk1) != T_EASEMENT ) + return; + xx = GetTrkExtraData(trk1); + if ( ep1 == 0 ) + d = xx->L/2.0 - xx->l0; + else + d = xx->l1 - xx->L/2.0; + if ( d < 0.01 ) + return; + UndrawNewTrack( trk ); + MoveEndPt( &trk, &ep, GetTrkEndPos(trk,ep), -d ); + DrawNewTrack( trk ); +} +#endif + +/***************************************************************************** + * + * INITIALIZATION + * + */ + + + +void InitTrkEase( void ) +{ + T_EASEMENT = InitObject( &easementCmds ); + log_ease = LogFindIndex( "ease" ); + log_traverseJoint = LogFindIndex( "traverseJoint" ); +} + + +/***************************************************************************** + * + * TEST + * + */ + +#ifdef TEST + + +void ErrorMessage( char * msg, ... ) +{ + lprintf( "%s\n", msg ); +} + +void InfoMessage( char * msg, ... ) +{ + lprintf( "%s\n", msg ); +} + +scaleInfo_p curScale; + +track_p NewTrack( TRKINX_T a, TRKTYP_T b, EPINX_T c, TRKTYP_T d ) +{ + return NULL; +} + +void DrawStraightTrack( drawCmd_p a, coOrd b, coOrd c, ANGLE_T d, + DIST_T trackGauge, wDrawColor color, int opts ) +{ +} + +void DrawNewTrack( track_p t ) +{ +} + +static DIST_T JoinDalt( + DIST_T x, + DIST_T R, + DIST_T L ) +/* + * Alternative distance computation, integrate over the curve. + */ +{ +#define DCNT (1000) + DIST_T d; + wIndex_t i; + coOrd p0, p1; + d = 0.0; + p0.x = p0.y = 0.0; + for ( i=1;i<=DCNT; i++) { + ComputeJoinPos( x*((DIST_T)i)/((DIST_T)DCNT), R, L, NULL, NULL, &p1, NULL ); + d += FindDistance( p0, p1 ); + p0 = p1; + } + return d; +} + + +test_plot( INT_T argc, char * argv[] ) +{ + DIST_T l, X, L, rr, ra, d, d1, R; + coOrd p, pc, p1; + INT_T i, C; + if (argc != 4) { + lprintf("%s R L C\n", argv[0]); + Exit(1); + } + argv++; + R = atof( *argv++ ); + L = atof( *argv++ ); + C = atol( *argv++ ); + X = L*L/(24*R); + lprintf("R=%0.3f X=%0.3f L=%0.3f\n", R, X, L ); + + for (i=0;i<=C;i++) { + l = L*((DIST_T)i)/((DIST_T)C); + d = JoinD( l, R, L ); + d1 = JoinDalt( l, R, L ); + ComputeJoinPos( l, R, L, &rr, &ra, &p, &pc ); + lprintf("d: [%0.3f %0.3f] [%0.3f %03f] R=%0.3f A=%0.3f D=%0.3f D1=%0.3f X=%0.4f\n", + i, p.x, p.y, pc.x, pc.y, rr, ra, d, d1, pc.x-rr ); + } +} + +test_psplot( INT_T argc, char * argv[] ) +{ + DIST_T l, L, rr, ra, d, d1, R, S, X; + coOrd p, q, pc, p1; + INT_T i, C; + if (argc != 5) { + lprintf("%s R L C S\n", argv[0]); + Exit(1); + } + argv++; + easeR = R = atof( *argv++ ); + easeL = L = atof( *argv++ ); + C = atol( *argv++ ); + S = atof( *argv++ ); + X = L*L/(24*R); + +lprintf("%%! kvjfv\nsave\n0 setlinewidth\n"); +lprintf("/Times-BoldItalic findfont 16 scalefont setfont\n"); +lprintf("36 36 moveto (R=%0.3f X=%0.3f L=%0.3f S=%0.3f) show\n", easeR, X, L, S ); +/*lprintf("24 768 translate -90 rotate\n");*/ +lprintf("gsave\n72 72 translate\n"); +lprintf("%0.3f %0.3f scale\n", 72.0/S, 72.0/S ); +lprintf("%0.3f %0.3f moveto %0.3f %0.3f lineto stroke\n", 0.0, 0.0, L, 0.0 ); +lprintf("%0.3f %0.3f %0.3f 270.0 90.0 arc stroke\n", L/2.0, easeR+X, easeR ); +lprintf("%0.3f %0.3f %0.3f 0.0 360.0 arc stroke\n", 0.0, 0.0, 0.25 ); + q.x = q.y = 0.0; + for (i=0;i<=C;i++) { + l = L*((DIST_T)i)/((DIST_T)C); + ComputeJoinPos( l, R, L, &rr, &ra, &p, &pc ); +lprintf("%0.3f %0.3f moveto %0.3f %0.3f lineto stroke\n", q.x, q.y, p.x, p.y ); + q = p; + } +lprintf("%0.3f %0.3f %0.3f 0.0 360.0 arc stroke\n", p.x, p.y, 0.25 ); +lprintf("grestore\nrestore\nshowpage\n%%Trailer\n%%Pages: 1\n"); +} + +void Test_compute( INT_T argc, char * argv[] ) +{ + DIST_T r0, r1, x, l0, l1, R, X, d; + coOrd q0, q1, qc0, qc1; + easementData_t e; + if (argc != 5) { + lprintf("compute R0 R1 R L\n"); + Exit(1); + } + /*debugEase = 5;*/ + argv++; + r0 = atof( *argv++); + r1 = atof( *argv++); + easementVal = 1.0; + easeR = atof( *argv++); + easeL = atof( *argv++); + ComputeJoint( r0, r1, &e ); + ComputeJoinPos( e.l0, easeR, easeL, NULL, NULL, &q0, &qc0 ); + ComputeJoinPos( e.l1, easeR, easeL, NULL, NULL, &q1, &qc1 ); + if (e.Scurve) { + q1.x = - q1.x; q1.y = - q1.y; + qc1.x = - qc1.x; qc1.y = - qc1.y; + } + d = FindDistance( q0, q1 ); + lprintf("ENDPT [%0.3f %0.3f] [%0.3f %0.3f]\n", q0.x, q0.y, q1.x, q1.y ); + lprintf("CENTER [%0.3f %0.3f] [%0.3f %0.3f]\n", qc0.x, qc0.y, qc1.x, qc1.y ); + lprintf("ComputeJoint( %0.3f %0.3f) { %0.3f %0.3f %0.3f } D0=%0.5f D1=%0.5f, D=%0.3f\n", + r0, r1, easeR, easeL, e.x, e.d0, e.d1, d ); +} + +void Test_findL( INT_T argc, char * argv[] ) +{ + DIST_T l, r, R, L; + if (argc != 5) { + lprintf("findL r R L\n"); + Exit(1); + } + /*debugEase = 5;*/ + argv++; + r = atof( *argv++ ); + R = atof( *argv++ ); + L = atof( *argv++ ); + l = FindL( r, R, L ); + lprintf("FindL( %0.3f %0.3f %0.3f ) = %0.3f\n", r, R, L, l ); +} + + +main( INT_T argc, char * argv[] ) +{ +INT_T flagX = 0; +INT_T flagV = 0; + if (argc<1) { + lprintf("plot|compute\n"); + Exit(1); + } + argv++; argc--; + while (argv[0][0] == '-') { + switch (argv[0][1]) { + case 'x': + flagX++; + argc--;argv++; + break; + case 'v': + flagV++; + argc--;argv++; + break; + default: + lprintf("Huh: %s\n", *argv ); + argc--;argv++; + break; + } + } + if (strcmp(argv[0],"plot")==0) { + Test_plot( argc, argv ); + } else if (strcmp(argv[0],"psplot")==0) { + Test_psplot( argc, argv ); + } else if (strcmp(argv[0],"compute")==0) { + Test_compute( argc, argv ); + } else if (strcmp(argv[0],"findL")==0) { + Test_findL( argc, argv ); + } else { + lprintf("unknown cmd %s\n", argv[0] ); + } +} +#endif diff --git a/app/bin/to3way.src b/app/bin/to3way.src new file mode 100644 index 0000000..1a65e8f --- /dev/null +++ b/app/bin/to3way.src @@ -0,0 +1,24 @@ +STRAIGHT, 25, 100, 325, 100, +STRAIGHT, 175, 100, 300, 50, +STRAIGHT, 175, 100, 300, 150, +LINE, 294, 35, 306, 65, +LINE, 294, 165, 306, 135, +ARROW, 25, 100, 50, 100, +ARROW, 325, 100, 150, 100, +ARROW, 25, 20, 125, 20, +ARROW, 300, 20, 225, 20, +ARROW, 25, 180, 125, 180, +ARROW, 300, 180, 225, 180, +LINE, 25, 10, 25, 190, +LINE, 300, 50, 300, 10, +LINE, 300, 50, 425, 50, +LINE, 300, 150, 300, 190, +LINE, 300, 150, 425, 150, +LINE, 325, 85, 325, 115, +LINE, 325, 100, 425, 100, +ARROW, 350, 100, 350, 85, +ARROW, 350, 50, 350, 65, +ARROW, 350, 100, 350, 115, +ARROW, 350, 150, 350, 135, +LINE, 300, 50, 425, 10, +LINE, 300, 150, 425, 190, diff --git a/app/bin/tocrv.src b/app/bin/tocrv.src new file mode 100644 index 0000000..882354f --- /dev/null +++ b/app/bin/tocrv.src @@ -0,0 +1,18 @@ +CURVE, 25, 100, 325, 80, 1700, +CURVE, 25, 100, 300, 40, 600, +LINE, 25, 10, 25, 140, +ARROW, 25, 20, 125, 20, +ARROW, 300, 20, 225, 20, +LINE, 300, 40, 300, 10, +LINE, 300, 40, 400, 40, +ARROW, 25, 130, 125, 130, +ARROW, 325, 130, 225, 130, +LINE, 325, 80, 325, 130, +LINE, 325, 80, 425, 80, +LINE, 25, 100, 425, 100, +ARROW, 315, 40, 315, 60, +ARROW, 315, 100, 315, 60, +ARROW, 340, 80, 340, 90, +ARROW, 340, 100, 340, 90, +LINE, 300, 40, 368, 10, +LINE, 325, 80, 400, 67, diff --git a/app/bin/tocrvsct.src b/app/bin/tocrvsct.src new file mode 100644 index 0000000..650bece --- /dev/null +++ b/app/bin/tocrvsct.src @@ -0,0 +1,3 @@ +CURVE, 350, 190, 350, 10, 400, +ARROW, 25, 100, 175, 100, +ARROW, 360, 100, 275, 100, diff --git a/app/bin/todcross.src b/app/bin/todcross.src new file mode 100644 index 0000000..ce2e8f9 --- /dev/null +++ b/app/bin/todcross.src @@ -0,0 +1,14 @@ +STRAIGHT, 10, 50, 410, 50, +STRAIGHT, 10, 150, 410, 150, +LINE, 10, 10, 10, 65, +LINE, 410, 10, 410, 65, +LINE, 10, 135, 10, 165, +LINE, 410, 135, 410, 165, +ARROW, 10, 20, 100, 20, +ARROW, 410, 20, 300, 20, +LINE, 10, 50, 50, 50, +LINE, 10, 150, 50, 150, +ARROW, 25, 50, 25, 85, +ARROW, 25, 150, 25, 115, +STRAIGHT, 60, 50, 360, 150, +STRAIGHT, 60, 150, 360, 50, diff --git a/app/bin/todslip.src b/app/bin/todslip.src new file mode 100644 index 0000000..9dd3439 --- /dev/null +++ b/app/bin/todslip.src @@ -0,0 +1,12 @@ +STRAIGHT, 25, 150, 425, 50, +STRAIGHT, 25, 50, 425, 150, +LINE, 429, 66, 417, 18, +LINE, 429, 134, 417, 182, +LINE, 17, 82, 29, 34, +LINE, 17, 118, 29, 166, +ARROW, 419, 25, 379, 35, +ARROW, 19, 125, 319, 50, +ARROW, 419, 175, 379, 165, +ARROW, 19, 75, 319, 150, +CURVE, 365, 135, 85, 135, 600, +CURVE, 85, 65, 365, 65, 700, diff --git a/app/bin/tolcross.src b/app/bin/tolcross.src new file mode 100644 index 0000000..70fa7e5 --- /dev/null +++ b/app/bin/tolcross.src @@ -0,0 +1,13 @@ +STRAIGHT, 10, 50, 410, 50, +STRAIGHT, 10, 150, 410, 150, +LINE, 10, 10, 10, 65, +LINE, 410, 10, 410, 65, +LINE, 10, 135, 10, 165, +LINE, 410, 135, 410, 165, +ARROW, 10, 20, 100, 20, +ARROW, 410, 20, 300, 20, +LINE, 10, 50, 50, 50, +LINE, 10, 150, 50, 150, +ARROW, 25, 50, 25, 85, +ARROW, 25, 150, 25, 115, +STRAIGHT, 60, 150, 360, 50, diff --git a/app/bin/torcross.src b/app/bin/torcross.src new file mode 100644 index 0000000..f587380 --- /dev/null +++ b/app/bin/torcross.src @@ -0,0 +1,13 @@ +STRAIGHT, 10, 50, 410, 50, +STRAIGHT, 10, 150, 410, 150, +LINE, 10, 10, 10, 65, +LINE, 410, 10, 410, 65, +LINE, 10, 135, 10, 165, +LINE, 410, 135, 410, 165, +ARROW, 10, 20, 100, 20, +ARROW, 410, 20, 300, 20, +LINE, 10, 50, 50, 50, +LINE, 10, 150, 50, 150, +ARROW, 25, 50, 25, 85, +ARROW, 25, 150, 25, 115, +STRAIGHT, 60, 50, 360, 150, diff --git a/app/bin/toreg.src b/app/bin/toreg.src new file mode 100644 index 0000000..c15790c --- /dev/null +++ b/app/bin/toreg.src @@ -0,0 +1,15 @@ +STRAIGHT, 25, 100, 325, 100, +STRAIGHT, 175, 100, 300, 50, +LINE, 294, 35, 306, 65, +ARROW, 25, 130, 50, 130, +ARROW, 325, 130, 150, 130, +ARROW, 25, 20, 125, 20, +ARROW, 300, 20, 225, 20, +LINE, 25, 10, 25, 140, +LINE, 300, 50, 300, 10, +LINE, 300, 50, 425, 50, +LINE, 325, 85, 325, 140, +LINE, 25, 100, 425, 100, +ARROW, 350, 100, 350, 85, +ARROW, 350, 50, 350, 65, +LINE, 300, 50, 425, 10, diff --git a/app/bin/tosslip.src b/app/bin/tosslip.src new file mode 100644 index 0000000..5ba94a9 --- /dev/null +++ b/app/bin/tosslip.src @@ -0,0 +1,11 @@ +STRAIGHT, 25, 150, 425, 50, +STRAIGHT, 25, 50, 425, 150, +LINE, 429, 66, 417, 18, +LINE, 429, 134, 417, 182, +LINE, 17, 82, 29, 34, +LINE, 17, 118, 29, 166, +ARROW, 419, 25, 379, 35, +ARROW, 19, 125, 319, 50, +ARROW, 419, 175, 379, 165, +ARROW, 19, 75, 319, 150, +CURVE, 365, 135, 85, 135, 500, diff --git a/app/bin/tostrsct.src b/app/bin/tostrsct.src new file mode 100644 index 0000000..94781ed --- /dev/null +++ b/app/bin/tostrsct.src @@ -0,0 +1,5 @@ +STRAIGHT, 10, 50, 410, 50, +LINE, 10, 10, 10, 65, +LINE, 410, 10, 410, 65, +ARROW, 10, 20, 100, 20, +ARROW, 410, 20, 300, 20, diff --git a/app/bin/towye.src b/app/bin/towye.src new file mode 100644 index 0000000..e43d400 --- /dev/null +++ b/app/bin/towye.src @@ -0,0 +1,21 @@ +STRAIGHT, 25, 100, 175, 100, +STRAIGHT, 175, 100, 300, 50, +STRAIGHT, 175, 100, 300, 150, +LINE, 294, 35, 306, 65, +LINE, 294, 165, 306, 135, +ARROW, 25, 20, 125, 20, +ARROW, 300, 20, 225, 20, +ARROW, 25, 180, 125, 180, +ARROW, 300, 180, 225, 180, +LINE, 25, 10, 25, 190, +LINE, 300, 50, 300, 10, +LINE, 300, 50, 425, 50, +LINE, 300, 150, 300, 190, +LINE, 300, 150, 425, 150, +LINE, 25, 100, 425, 100, +ARROW, 350, 100, 350, 85, +ARROW, 350, 50, 350, 65, +ARROW, 350, 100, 350, 115, +ARROW, 350, 150, 350, 135, +LINE, 300, 50, 425, 10, +LINE, 300, 150, 425, 190, diff --git a/app/bin/toxing.src b/app/bin/toxing.src new file mode 100644 index 0000000..a725756 --- /dev/null +++ b/app/bin/toxing.src @@ -0,0 +1,10 @@ +STRAIGHT, 25, 150, 425, 50, +STRAIGHT, 25, 50, 425, 150, +LINE, 429, 66, 417, 18, +LINE, 429, 134, 417, 182, +LINE, 17, 82, 29, 34, +LINE, 17, 118, 29, 166, +ARROW, 419, 25, 379, 35, +ARROW, 19, 125, 319, 50, +ARROW, 419, 175, 379, 165, +ARROW, 19, 75, 319, 150, diff --git a/app/bin/track.c b/app/bin/track.c new file mode 100644 index 0000000..30ea186 --- /dev/null +++ b/app/bin/track.c @@ -0,0 +1,2932 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/track.c,v 1.7 2009-07-05 15:11:02 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 <time.h> +#include <ctype.h> +#include <stdarg.h> +#include <math.h> +#include "track.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "cjoin.h" +#include "compound.h" +#include "i18n.h" +#include "draw.h" + +#ifndef TRACKDEP +#ifndef FASTTRACK +#include "trackx.h" +#endif +#endif + +#ifndef WINDOWS +#include <errno.h> +#else +// starting from Visual Studio 2015 round is in the runtime library, fake otherwise +#if ( _MSC_VER < 1900 ) +#define round(x) floor((x)+0.5) +#endif +#endif + +static int log_track = 0; +static int log_endPt = 0; +static int log_readTracks = 0; + +/***************************************************************************** + * + * VARIABLES + * + */ + +#define DRAW_TUNNEL_NONE (0) + +#define CLOSETOTHEEDGE (10) /**< minimum distance between paste position and edge of window */ + +EXPORT wIndex_t trackCount; + +EXPORT long drawEndPtV = 2; + +EXPORT long centerDrawMode = FALSE; /**< flag to control drawing of circle centers */ + +static BOOL_T exportingTracks = FALSE; + +EXPORT signed char * pathPtr; +EXPORT int pathCnt = 0; +EXPORT int pathMax = 0; + +static dynArr_t trackCmds_da; +#define trackCmds(N) DYNARR_N( trackCmd_t*, trackCmds_da, N ) + +EXPORT BOOL_T useCurrentLayer = FALSE; + +EXPORT LAYER_T curTrackLayer; + +EXPORT coOrd descriptionOff; + +EXPORT DIST_T roadbedWidth = 0.0; +EXPORT DIST_T roadbedLineWidth = 3.0/75.0; + +EXPORT DIST_T minTrackRadius; +EXPORT DIST_T maxTrackGrade = 5.0; + +static int suspendElevUpdates = FALSE; + +static track_p * importTrack; + +EXPORT BOOL_T onTrackInSplit; + +static BOOL_T inDrawTracks; + +#ifndef TRACKDEP + +/***************************************************************************** + * + * + * + */ + + +EXPORT void DescribeTrack( track_cp trk, char * str, CSIZE_T len ) +{ + trackCmds( GetTrkType(trk) )->describe ( trk, str, len ); + /*epCnt = GetTrkEndPtCnt(trk); + if (debugTrack >= 2) + for (ep=0; epCnt; ep++) + PrintEndPt( logFile, trk, ep );???*/ +} + + +EXPORT DIST_T GetTrkDistance( track_cp trk, coOrd pos ) +{ + return trackCmds( GetTrkType(trk) )->distance( trk, &pos ); +} + +/** + * Check whether the track passed as parameter is close to an existing piece. Track + * pieces that aren't visible (in a tunnel or on an invisble layer) can be ignored, + * depending on flag. If there is a track closeby, the passed track is moved to that + * position. This implements the snap feature. + * + * \param fp IN/OUT the old and the new position + * \param complain IN show error message if there is no other piece of track + * \param track IN + * \param ignoreHidden IN decide whether hidden track is ignored or not + * \return NULL if there is no track, pointer to track otherwise + */ + +EXPORT track_p OnTrack2( coOrd * fp, BOOL_T complain, BOOL_T track, BOOL_T ignoreHidden ) +{ + track_p trk; + DIST_T distance, closestDistance = 1000000; + track_p closestTrack = NULL; + coOrd p, closestPos, q0, q1; + + q0 = q1 = * fp; + q0.x -= 1.0; + q1.x += 1.0; + q0.y -= 1.0; + q1.y += 1.0; + TRK_ITERATE( trk ) { + if ( track && !IsTrack(trk) ) + continue; + if (trk->hi.x < q0.x || + trk->lo.x > q1.x || + trk->hi.y < q0.y || + trk->lo.y > q1.y ) + continue; + if ( ignoreHidden ) { + if ( (!GetTrkVisible(trk)) && drawTunnel == DRAW_TUNNEL_NONE) + continue; + if ( !GetLayerVisible( GetTrkLayer( trk ) ) ) + continue; + } + p = *fp; + distance = trackCmds( GetTrkType(trk) )->distance( trk, &p ); + if (distance < closestDistance) { + closestDistance = distance; + closestTrack = trk; + closestPos = p; + } + } + if (closestTrack && (closestDistance <= mainD.scale*0.25 || closestDistance <= trackGauge*2.0) ) { + *fp = closestPos; + return closestTrack; + } + if (complain) { + ErrorMessage( MSG_PT_IS_NOT_TRK, FormatDistance(fp->x), FormatDistance(fp->y) ); + } + return NULL; +} + +/** + * Check whether the track passed as parameter is close to an existing piece. Track + * pieces that aren't visible (in a tunnel or on an invisble layer) are ignored, + * This function is basically a wrapper function to OnTrack2(). + */ + + +EXPORT track_p OnTrack( coOrd * fp, BOOL_T complain, BOOL_T track ) +{ + return OnTrack2( fp, complain, track, TRUE ); +} + + +EXPORT BOOL_T CheckTrackLayer( track_p trk ) +{ + if (GetLayerFrozen( GetTrkLayer( trk ) ) ) { + ErrorMessage( MSG_CANT_MODIFY_FROZEN_TRK ); + return FALSE; + } else { + return TRUE; + } +} + +/****************************************************************************** + * + * PARTS LIST + * + */ + + +EXPORT void EnumerateTracks( void ) +{ + track_p trk; + TRKINX_T inx; + + enumerateMaxDescLen = strlen("Description"); + + TRK_ITERATE( trk ) { + /* + * process track piece if none are selected (list all) or if it is one of the + * selected pieces (list only selected ) + */ + if ((!selectedTrackCount || GetTrkSelected(trk)) && trackCmds(trk->type)->enumerate != NULL) + trackCmds(trk->type)->enumerate( trk ); + } + + EnumerateStart(); + + for (inx=1; inx<trackCmds_da.cnt; inx++) + if (trackCmds(inx)->enumerate != NULL) + trackCmds(inx)->enumerate( NULL ); + + EnumerateEnd(); + Reset(); +} + +/***************************************************************************** + * + * NOTRACK + * + */ + +static void AbortNoTrack( void ) +{ + AbortProg( "No Track Op called" ); +} + +static trackCmd_t notrackCmds = { + "NOTRACK", + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack, + (void*)AbortNoTrack }; + +EXPORT TRKTYP_T InitObject( trackCmd_t * cmds ) +{ + DYNARR_APPEND( trackCmd_t*, trackCmds_da, 10 ); + trackCmds(trackCmds_da.cnt-1) = cmds; + return trackCmds_da.cnt-1; +} + + +EXPORT TRKTYP_T T_NOTRACK = -1; + +EXPORT void InitTrkTrack( void ) +{ + T_NOTRACK = InitObject( ¬rackCmds ); + log_track = LogFindIndex( "track" ); + log_endPt = LogFindIndex( "endPt" ); + log_readTracks = LogFindIndex( "readTracks" ); +} + +/***************************************************************************** + * + * TRACK FIELD ACCESS + * + */ + + +#ifndef FASTTRACK + +EXPORT TRKINX_T GetTrkIndex( track_p trk ) +{ + return trk->index; +} + +EXPORT TRKTYP_T GetTrkType( track_p trk ) +{ + ASSERT( trk->type != T_NOTRACK && !IsTrackDeleted(trk) ); + return trk->type; +} + +EXPORT SCALEINX_T GetTrkScale( track_p trk ) +{ + return (SCALEINX_T)trk->scale; +} + +EXPORT void SetTrkScale( track_p trk, SCALEINX_T si ) +{ + trk->scale = (char)si; +} + +EXPORT LAYER_T GetTrkLayer( track_p trk ) +{ + return trk->layer; +} + +EXPORT void SetBoundingBox( track_p trk, coOrd hi, coOrd lo ) +{ + trk->hi.x = (float)hi.x; + trk->hi.y = (float)hi.y; + trk->lo.x = (float)lo.x; + trk->lo.y = (float)lo.y; +} + + +EXPORT void GetBoundingBox( track_p trk, coOrd *hi, coOrd *lo ) +{ + hi->x = (POS_T)trk->hi.x; + hi->y = (POS_T)trk->hi.y; + lo->x = (POS_T)trk->lo.x; + lo->y = (POS_T)trk->lo.y; +} + +EXPORT EPINX_T GetTrkEndPtCnt( track_cp trk ) +{ + return trk->endCnt; +} + +EXPORT struct extraData * GetTrkExtraData( track_cp trk ) +{ + return trk->extraData; +} + +EXPORT void SetTrkEndPoint( track_p trk, EPINX_T ep, coOrd pos, ANGLE_T angle ) +{ + if (trk->endPt[ep].track != NULL) { + AbortProg( "setTrkEndPoint: endPt is connected" ); + } + trk->endPt[ep].pos = pos; + trk->endPt[ep].angle = angle; +} + +EXPORT coOrd GetTrkEndPos( track_p trk, EPINX_T e ) +{ + return trk->endPt[e].pos; +} + +EXPORT ANGLE_T GetTrkEndAngle( track_p trk, EPINX_T e ) +{ + return trk->endPt[e].angle; +} + +EXPORT track_p GetTrkEndTrk( track_p trk, EPINX_T e ) +{ + return trk->endPt[e].track; +} + +EXPORT long GetTrkEndOption( track_p trk, EPINX_T e ) +{ + return trk->endPt[e].option; +} + +EXPORT long SetTrkEndOption( track_p trk, EPINX_T e, long option ) +{ + return trk->endPt[e].option = option; +} + +EXPORT int GetTrkWidth( track_p trk ) +{ + return (int)trk->width; +} + +EXPORT void SetTrkWidth( track_p trk, int width ) +{ + trk->width = (unsigned int)width; +} + +EXPORT int GetTrkBits( track_p trk ) +{ + return trk->bits; +} + +EXPORT int SetTrkBits( track_p trk, int bits ) +{ + int oldBits = trk->bits; + trk->bits |= bits; + return oldBits; +} + +EXPORT int ClrTrkBits( track_p trk, int bits ) +{ + int oldBits = trk->bits; + trk->bits &= ~bits; + return oldBits; +} + +EXPORT BOOL_T IsTrackDeleted( track_p trk ) +{ + return trk->deleted; +} +#endif + +EXPORT void SetTrkEndElev( track_p trk, EPINX_T ep, int option, DIST_T height, char * station ) +{ + track_p trk1; + EPINX_T ep1; + trk->endPt[ep].elev.option = option; + if (EndPtIsDefinedElev(trk,ep)) { + trk->endPt[ep].elev.u.height = height; + } else if (EndPtIsStationElev(trk,ep)) { + if (station == NULL) + station = ""; + trk->endPt[ep].elev.u.name = MyStrdup(station); + } + if ( (trk1=GetTrkEndTrk(trk, ep)) != NULL ) { + ep1 = GetEndPtConnectedToMe( trk1, trk ); + if (ep1 >= 0) { + trk1->endPt[ep1].elev.option = option; + trk1->endPt[ep1].elev.u.height = height; + if (EndPtIsDefinedElev(trk1,ep1)) + trk1->endPt[ep1].elev.u.height = height; + else if (EndPtIsStationElev(trk,ep)) + trk1->endPt[ep1].elev.u.name = MyStrdup(station); + } + } +} + + +EXPORT void GetTrkEndElev( track_p trk, EPINX_T e, int *option, DIST_T *height ) +{ + *option = trk->endPt[e].elev.option; + *height = trk->endPt[e].elev.u.height; +} + + +EXPORT int GetTrkEndElevUnmaskedMode( track_p trk, EPINX_T e ) +{ + return trk->endPt[e].elev.option; +} + + +EXPORT int GetTrkEndElevMode( track_p trk, EPINX_T e ) +{ + return trk->endPt[e].elev.option&ELEV_MASK; +} + + +EXPORT DIST_T GetTrkEndElevHeight( track_p trk, EPINX_T e ) +{ + ASSERT( EndPtIsDefinedElev(trk,e) ); + return trk->endPt[e].elev.u.height; +} + + +EXPORT char * GetTrkEndElevStation( track_p trk, EPINX_T e ) +{ + ASSERT( EndPtIsStationElev(trk,e) ); + if ( trk->endPt[e].elev.u.name == NULL ) + return ""; + else + return trk->endPt[e].elev.u.name; +} + + +EXPORT void SetTrkEndPtCnt( track_p trk, EPINX_T cnt ) +{ + EPINX_T oldCnt = trk->endCnt; + trk->endCnt = cnt; + if ((trk->endPt = MyRealloc( trk->endPt, trk->endCnt * sizeof trk->endPt[0] )) == NULL) { + AbortProg("setTrkEndPtCnt: No memory" ); + } + if (oldCnt < cnt) + memset( &trk->endPt[oldCnt], 0, (cnt-oldCnt) * sizeof *trk->endPt ); +} + + +EXPORT void SetTrkLayer( track_p trk, int layer ) +{ + if (useCurrentLayer) + trk->layer = (LAYER_T)curLayer; + else + trk->layer = (LAYER_T)layer; +} + + + +EXPORT int ClrAllTrkBits( int bits ) +{ + track_p trk; + int cnt; + cnt = 0; + TRK_ITERATE( trk ) { + if (trk->bits&bits) + cnt++; + trk->bits &= ~bits; + } + return cnt; +} + + + +EXPORT void SetTrkElev( track_p trk, int mode, DIST_T elev ) +{ + SetTrkBits( trk, TB_ELEVPATH ); + trk->elev = elev; + trk->elevMode = mode; +} + + +EXPORT int GetTrkElevMode( track_p trk ) +{ + return trk->elevMode; +} + +EXPORT DIST_T GetTrkElev( track_p trk ) +{ + return trk->elev; +} + + +EXPORT void ClearElevPath( void ) +{ + track_p trk; + TRK_ITERATE( trk ) { + ClrTrkBits( trk, TB_ELEVPATH ); + trk->elev = 0.0; + } +} + + +EXPORT BOOL_T GetTrkOnElevPath( track_p trk, DIST_T * elev ) +{ + if (trk->bits&TB_ELEVPATH) { + if ( elev ) *elev = trk->elev; + return TRUE; + } else { + return FALSE; + } +} + + +EXPORT void CopyAttributes( track_p src, track_p dst ) +{ + SetTrkScale( dst, GetTrkScale( src ) ); + dst->bits = (dst->bits&TB_HIDEDESC) | (src->bits&~TB_HIDEDESC); + SetTrkWidth( dst, GetTrkWidth( src ) ); + dst->layer = GetTrkLayer( src ); +} + +/***************************************************************************** + * + * ENDPOINTS + * + */ + + +EXPORT BOOL_T WriteEndPt( FILE * f, track_cp trk, EPINX_T ep ) +{ + trkEndPt_p endPt = &trk->endPt[ep]; + BOOL_T rc = TRUE; + long option; + + assert ( endPt != NULL ); + if (endPt->track == NULL || + ( exportingTracks && !GetTrkSelected(endPt->track) ) ) { + rc &= fprintf( f, "\tE " )>0; + } else { + rc &= fprintf( f, "\tT %d ", endPt->track->index )>0; + } + rc &= fprintf( f, "%0.6f %0.6f %0.6f", endPt->pos.x, endPt->pos.y, endPt->angle )>0; + option = (endPt->option<<8) | (endPt->elev.option&0xFF); + if ( option != 0 ) { + rc &= fprintf( f, " %ld %0.6f %0.6f", option, endPt->elev.doff.x, endPt->elev.doff.y )>0; + if ( (endPt->elev.option&ELEV_MASK) != ELEV_NONE ) { + switch ( endPt->elev.option&ELEV_MASK ) { + case ELEV_DEF: + rc &= fprintf( f, " %0.6f", endPt->elev.u.height )>0; + break; + case ELEV_STATION: + rc &= fprintf( f, " \"%s\"", PutTitle( endPt->elev.u.name ) )>0; + break; + default: + ; + } + } + } + rc &= fprintf( f, "\n" )>0; + return rc; +} + + +EXPORT EPINX_T PickEndPoint( coOrd p, track_cp trk ) +{ + EPINX_T inx, i; + DIST_T d, dd; + coOrd pos; + if (trk->endCnt <= 0) + return -1; + if ( onTrackInSplit && trk->endCnt > 2 ) + return TurnoutPickEndPt( p, trk ); + d = FindDistance( p, trk->endPt[0].pos ); + inx = 0; + for ( i=1; i<trk->endCnt; i++ ) { + pos = trk->endPt[i].pos; + dd=FindDistance(p, pos); + if (dd < d) { + d = dd; + inx = i; + } + } + return inx; +} + + +EXPORT EPINX_T PickUnconnectedEndPoint( coOrd p, track_cp trk ) +{ + EPINX_T inx, i; + DIST_T d=0, dd; + coOrd pos; + inx = -1; + + for ( i=0; i<trk->endCnt; i++ ) { + if (trk->endPt[i].track == NULL) { + pos = trk->endPt[i].pos; + dd=FindDistance(p, pos); + if (inx == -1 || dd <= d) { + d = dd; + inx = i; + } + } + } + + if (inx == -1) + ErrorMessage( MSG_NO_UNCONN_EP ); + return inx; +} + + +EXPORT EPINX_T GetEndPtConnectedToMe( track_p trk, track_p me ) +{ + EPINX_T ep; + for (ep=0; ep<trk->endCnt; ep++) + if (trk->endPt[ep].track == me) + return ep; + return -1; +} + + +EXPORT void SetEndPts( track_p trk, EPINX_T cnt ) +{ + EPINX_T inx; + +LOG1( log_readTracks, ( "SetEndPts( T%d, %d )\n", trk->index, cnt ) ) + if (cnt > 0 && tempEndPts_da.cnt != cnt) { + InputError( "Incorrect number of End Points for track, read %d, expected %d.\n", FALSE, tempEndPts_da.cnt, cnt ); + return; + } + if (tempEndPts_da.cnt) { + trk->endPt = (trkEndPt_p)MyMalloc( tempEndPts_da.cnt * sizeof *trk->endPt ); + } else { + trk->endPt = NULL; + } + for ( inx=0; inx<tempEndPts_da.cnt; inx++ ) { + trk->endPt[inx].index = tempEndPts(inx).index; + trk->endPt[inx].pos = tempEndPts(inx).pos; + trk->endPt[inx].angle = tempEndPts(inx).angle; + trk->endPt[inx].elev = tempEndPts(inx).elev; + trk->endPt[inx].option = tempEndPts(inx).option; + } + trk->endCnt = tempEndPts_da.cnt; +} + + +EXPORT void MoveTrack( track_p trk, coOrd orig ) +{ + EPINX_T ep; + for (ep=0; ep<trk->endCnt; ep++) { + trk->endPt[ep].pos.x += orig.x; + trk->endPt[ep].pos.y += orig.y; + } + trackCmds( trk->type )->move( trk, orig ); +} + + +EXPORT void RotateTrack( track_p trk, coOrd orig, ANGLE_T angle ) +{ + EPINX_T ep; + for (ep=0; ep<trk->endCnt; ep++) { + Rotate( &trk->endPt[ep].pos, orig, angle ); + trk->endPt[ep].angle = NormalizeAngle( trk->endPt[ep].angle + angle ); + } + trackCmds( trk->type )->rotate( trk, orig, angle ); +} + + +EXPORT void RescaleTrack( track_p trk, FLOAT_T ratio, coOrd shift ) +{ + EPINX_T ep; + if ( trackCmds( trk->type )->rotate == NULL ) + return; + for (ep=0; ep<trk->endCnt; ep++) { + trk->endPt[ep].pos.x *= ratio; + trk->endPt[ep].pos.y *= ratio; + } + trackCmds( trk->type )->rescale( trk, ratio ); + MoveTrack( trk, shift ); +} + + +EXPORT void FlipPoint( + coOrd * pos, + coOrd orig, + ANGLE_T angle ) +{ + Rotate( pos, orig, -angle ); + pos->x = 2*orig.x - pos->x; + Rotate( pos, orig, angle ); +} + + +EXPORT void FlipTrack( + track_p trk, + coOrd orig, + ANGLE_T angle ) +{ + EPINX_T ep; + trkEndPt_t endPt; + + for ( ep=0; ep<trk->endCnt; ep++ ) { + FlipPoint( &trk->endPt[ep].pos, orig, angle ); + trk->endPt[ep].angle = NormalizeAngle( 2*angle - trk->endPt[ep].angle ); + } + if ( trackCmds(trk->type)->flip ) + trackCmds(trk->type)->flip( trk, orig, angle ); + if ( QueryTrack( trk, Q_FLIP_ENDPTS ) ) { + endPt = trk->endPt[0]; + trk->endPt[0] = trk->endPt[1]; + trk->endPt[1] = endPt; + } +} + + +EXPORT EPINX_T GetNextTrk( + track_p trk1, + EPINX_T ep1, + track_p *Rtrk, + EPINX_T *Rep, + int mode ) +{ + EPINX_T ep, epCnt = GetTrkEndPtCnt(trk1), epRet=-1; + track_p trk; + + *Rtrk = NULL; + *Rep = 0; + for (ep=0; ep<epCnt; ep++) { + if (ep==ep1) + continue; + trk = GetTrkEndTrk( trk1, ep ); + if (trk==NULL) { +#ifdef LATER + if (isElev) + epRet = ep; +#endif + continue; + } + if ( (mode&GNTignoreIgnore) && + ((trk1->endPt[ep].elev.option&ELEV_MASK)==ELEV_IGNORE)) + continue; + if (*Rtrk != NULL) + return -1; + *Rtrk = trk; + *Rep = GetEndPtConnectedToMe( trk, trk1 ); + epRet = ep; + } + return epRet; +} + +EXPORT BOOL_T MakeParallelTrack( + track_p trk, + coOrd pos, + DIST_T dist, + track_p * newTrkR, + coOrd * p0R, + coOrd * p1R ) +{ + if ( trackCmds(trk->type)->makeParallel ) + return trackCmds(trk->type)->makeParallel( trk, pos, dist, newTrkR, p0R, p1R ); + return FALSE; +} + + +/***************************************************************************** + * + * LIST MANAGEMENT + * + */ + + + +EXPORT track_p to_first = NULL; + +EXPORT TRKINX_T max_index = 0; +EXPORT track_p * to_last = &to_first; + +static struct { + track_p first; + track_p *last; + wIndex_t count; + wIndex_t changed; + TRKINX_T max_index; + } savedTrackState; + + +EXPORT void RenumberTracks( void ) +{ + track_p trk; + max_index = 0; + for (trk=to_first; trk!=NULL; trk=trk->next) { + trk->index = ++max_index; + } +} + + +EXPORT track_p NewTrack( TRKINX_T index, TRKTYP_T type, EPINX_T endCnt, CSIZE_T extraSize ) +{ + track_p trk; + EPINX_T ep; + trk = (track_p ) MyMalloc( sizeof *trk ); + *to_last = trk; + to_last = &trk->next; + trk->next = NULL; + if (index<=0) { + index = ++max_index; + } else if (max_index < index) { + max_index = index; + } +LOG( log_track, 1, ( "NewTrack( T%d, t%d, E%d, X%ld)\n", index, type, endCnt, extraSize ) ) + trk->index = index; + trk->type = type; + trk->layer = curLayer; + trk->scale = (char)curScaleInx; + trk->bits = TB_VISIBLE; + trk->elevMode = ELEV_ALONE; + trk->elev = 0; + trk->endCnt = endCnt; + trk->hi.x = trk->hi.y = trk->lo.x = trk->lo.y = (float)0.0; + if (endCnt) { + trk->endPt = (trkEndPt_p)MyMalloc( endCnt * sizeof *trk->endPt ); + for ( ep = 0; ep < endCnt; ep++ ) + trk->endPt[ep].index = -1; + } else + trk->endPt = NULL; + if (extraSize) { + trk->extraData = MyMalloc( extraSize ); + } else + trk->extraData = NULL; + trk->extraSize = extraSize; + UndoNew( trk ); + trackCount++; + InfoCount( trackCount ); + return trk; +} + + +EXPORT void FreeTrack( track_p trk ) +{ + trackCmds(trk->type)->delete( trk ); + if (trk->endPt) + MyFree(trk->endPt); + if (trk->extraData) + MyFree(trk->extraData); + MyFree(trk); +} + + +EXPORT void ClearTracks( void ) +{ + track_p curr, next; + UndoClear(); + ClearNote(); + for (curr = to_first; curr; curr=next) { + next = curr->next; + FreeTrack( curr ); + } + to_first = NULL; + to_last = &to_first; + max_index = 0; + changed = 0; + trackCount = 0; + ClearCars(); + InfoCount( trackCount ); +} + + +EXPORT track_p FindTrack( TRKINX_T index ) +{ + track_p trk; + TRK_ITERATE(trk) { + if (trk->index == index) return trk; + } + return NULL; +} + + +EXPORT void ResolveIndex( void ) +{ + track_p trk; + EPINX_T ep; + TRK_ITERATE(trk) + for (ep=0; ep<trk->endCnt; ep++) + if (trk->endPt[ep].index >= 0) { + trk->endPt[ep].track = FindTrack( trk->endPt[ep].index ); + if (trk->endPt[ep].track == NULL) { + NoticeMessage( MSG_RESOLV_INDEX_BAD_TRK, _("Continue"), NULL, trk->index, ep, trk->endPt[ep].index ); + } + } + AuditTracks( "readTracks" ); +} + + +EXPORT BOOL_T DeleteTrack( track_p trk, BOOL_T all ) +{ + EPINX_T i, ep2; + track_p trk2; +LOG( log_track, 4, ( "DeleteTrack(T%d)\n", GetTrkIndex(trk) ) ) + if (all) { + if (!QueryTrack(trk,Q_CANNOT_BE_ON_END)) { + for (i=0;i<trk->endCnt;i++) { + if ((trk2=trk->endPt[i].track) != NULL) { + if (QueryTrack(trk2,Q_CANNOT_BE_ON_END)) { + DeleteTrack( trk2, FALSE ); + } + } + } + } + } + UndrawNewTrack( trk ); + for (i=0;i<trk->endCnt;i++) { + if ((trk2=trk->endPt[i].track) != NULL) { + ep2 = GetEndPtConnectedToMe( trk2, trk ); + /*UndrawNewTrack( trk2 );*/ + DrawEndPt( &mainD, trk2, ep2, wDrawColorWhite ); + DisconnectTracks( trk2, ep2, trk, i ); + /*DrawNewTrack( trk2 );*/ + if (!QueryTrack(trk2,Q_DONT_DRAW_ENDPOINT)) + DrawEndPt( &mainD, trk2, ep2, wDrawColorBlack ); + if ( QueryTrack(trk,Q_CANNOT_BE_ON_END) ) + UndoJoint( trk2, ep2, trk, i ); + ClrTrkElev( trk2 ); + } + } + UndoDelete( trk ); + MainRedraw(); + trackCount--; + AuditTracks( "deleteTrack T%d", trk->index); + InfoCount( trackCount ); + return TRUE; +} + +EXPORT void SaveTrackState( void ) +{ + savedTrackState.first = to_first; + savedTrackState.last = to_last; + savedTrackState.count = trackCount; + savedTrackState.changed = changed; + savedTrackState.max_index = max_index; + to_first = NULL; + to_last = &to_first; + trackCount = 0; + changed = 0; + max_index = 0; + SaveCarState(); + InfoCount( trackCount ); +} + +EXPORT void RestoreTrackState( void ) +{ + to_first = savedTrackState.first; + to_last = savedTrackState.last; + trackCount = savedTrackState.count; + changed = savedTrackState.changed; + max_index = savedTrackState.max_index; + RestoreCarState(); + InfoCount( trackCount ); +} + + +BOOL_T TrackIterate( track_p * trk ) +{ + track_p trk1; + if (!*trk) + trk1 = to_first; + else + trk1 = (*trk)->next; + while (trk1 && IsTrackDeleted(trk1)) + trk1 = trk1->next; + *trk = trk1; + return trk1 != NULL; +} + +/***************************************************************************** + * + * ABOVE / BELOW + * + */ + +static void ExciseSelectedTracks( track_p * pxtrk, track_p * pltrk ) +{ + track_p trk, *ptrk; + for (ptrk=&to_first; *ptrk!=NULL; ) { + trk = *ptrk; + if (IsTrackDeleted(trk) || !GetTrkSelected(trk)) { + ptrk = &(*ptrk)->next; + continue; + } + UndoModify( *ptrk ); + UndoModify( trk ); + *ptrk = trk->next; + *pltrk = *pxtrk = trk; + pxtrk = &trk->next; + trk->next = NULL; + } + to_last = ptrk; +} + + +EXPORT void SelectAbove( void ) +{ + track_p xtrk, ltrk; + if (selectedTrackCount<=0) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return; + } + UndoStart( _("Move Objects Above"), "above" ); + xtrk = NULL; + ExciseSelectedTracks( &xtrk, <rk ); + if (xtrk) { + *to_last = xtrk; + to_last = <rk->next; + } + UndoEnd(); + DrawSelectedTracks( &mainD ); +} + + +EXPORT void SelectBelow( void ) +{ + track_p xtrk, ltrk, trk; + coOrd lo, hi, lowest, highest; + if (selectedTrackCount<=0) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return; + } + UndoStart( _("Mode Objects Below"), "below" ); + xtrk = NULL; + ExciseSelectedTracks( &xtrk, <rk ); + if (xtrk) { + for ( trk=xtrk; trk; trk=trk->next ) { + if (trk==xtrk) { + GetBoundingBox( trk, &highest, &lowest ); + } else { + GetBoundingBox( trk, &hi, &lo ); + if (highest.x < hi.x) + highest.x = hi.x; + if (highest.y < hi.y) + highest.y = hi.y; + if (lowest.x > lo.x) + lowest.x = lo.x; + if (lowest.y > lo.y) + lowest.y = lo.y; + } + ClrTrkBits( trk, TB_SELECTED ); + } + ltrk->next = to_first; + to_first = xtrk; + highest.x -= lowest.x; + highest.y -= lowest.y; + DrawTracks( &mainD, 0.0, lowest, highest ); + } + UndoEnd(); +} + + +#include "bitmaps/above.xpm" +#include "bitmaps/below.xpm" + +EXPORT void InitCmdAboveBelow( void ) +{ + wIcon_p bm_p; + bm_p = wIconCreatePixMap( above_xpm ); + AddToolbarButton( "cmdAbove", bm_p, IC_SELECTED, (addButtonCallBack_t)SelectAbove, NULL ); + bm_p = wIconCreatePixMap( below_xpm ); + AddToolbarButton( "cmdBelow", bm_p, IC_SELECTED, (addButtonCallBack_t)SelectBelow, NULL ); +} + +/***************************************************************************** + * + * INPUT / OUTPUT + * + */ + + +static int bsearchRead = 0; +static trackCmd_t **sortedCmds = NULL; +static int CompareCmds( const void * a, const void * b ) +{ + return strcmp( (*(trackCmd_t**)a)->name, (*(trackCmd_t**)b)->name ); +} + +EXPORT BOOL_T ReadTrack( char * line ) +{ + TRKINX_T inx, lo, hi; + int cmp; +if (bsearchRead) { + if (sortedCmds == NULL) { + sortedCmds = (trackCmd_t**)MyMalloc( (trackCmds_da.cnt-1) * sizeof *(trackCmd_t*)0 ); + for (inx=1; inx<trackCmds_da.cnt; inx++) + sortedCmds[inx-1] = trackCmds(inx); + qsort( sortedCmds, trackCmds_da.cnt-1, sizeof *(trackCmd_t**)0, CompareCmds ); + } + + lo = 0; + hi = trackCmds_da.cnt-2; + do { + inx = (lo+hi)/2; + cmp = strncmp( line, sortedCmds[inx]->name, strlen(sortedCmds[inx]->name) ); + if (cmp == 0) { + sortedCmds[inx]->read(line); + return TRUE; + } else if (cmp < 0) { + hi = inx-1; + } else { + lo = inx+1; + } + } while ( lo <= hi ); +} else { + for (inx=1; inx<trackCmds_da.cnt; inx++) { + if (strncmp( line, trackCmds(inx)->name, strlen(trackCmds(inx)->name) ) == 0 ) { + trackCmds(inx)->read( line ); + return TRUE; + } + } +} + if (strncmp( paramLine, "TABLEEDGE ", 10 ) == 0) + return ReadTableEdge( paramLine+10 ); + if (strncmp( paramLine, "TEXT ", 5 ) == 0) + return ReadText( paramLine+5 ); + return FALSE; +} + + +EXPORT BOOL_T WriteTracks( FILE * f ) +{ + track_p trk; + BOOL_T rc = TRUE; + RenumberTracks(); + TRK_ITERATE( trk ) { + rc &= trackCmds(GetTrkType(trk))->write( trk, f ); + } + rc &= WriteCars( f ); + return rc; +} + + + +EXPORT void ImportStart( void ) +{ + importTrack = to_last; +} + + +EXPORT void ImportEnd( void ) +{ + track_p to_firstOld; + wIndex_t trackCountOld; + track_p trk; + coOrd pos; + wPos_t x, y; + wPos_t ww, hh; + double ymax = 0.0; + + // get the current mouse position + GetMousePosition( &x, &y ); + mainD.Pix2CoOrd( &mainD, x, y, &pos ); + + // get the size of the drawing area + wDrawGetSize( mainD.d, &ww, &hh ); + + // in case the pointer is close to the edge or above the drawing area + // recalculate the destination position so the pasted part remains visible + if( abs( y - hh ) < CLOSETOTHEEDGE ) { + for ( trk=*importTrack; trk; trk=trk->next ) { + if (!IsTrackDeleted(trk) && trk->hi.y > ymax ) { + ymax = trk->hi.y; + } + } + pos.y -= ymax; + } + + to_firstOld = to_first; + to_first = *importTrack; + trackCountOld = trackCount; + ResolveIndex(); + to_first = to_firstOld; + RenumberTracks(); + DrawMapBoundingBox( FALSE ); + + // move the imported track into place + for ( trk=*importTrack; trk; trk=trk->next ) if (!IsTrackDeleted(trk)) { + MoveTrack( trk, pos );// mainD.orig ); + trk->bits |= TB_SELECTED; + DrawTrack( trk, &mainD, wDrawColorBlack ); + } + DrawMapBoundingBox( TRUE ); + importTrack = NULL; + trackCount = trackCountOld; + InfoCount( trackCount ); +} + + +EXPORT BOOL_T ExportTracks( FILE * f ) +{ + track_p trk; + coOrd xlat, orig; + + exportingTracks = TRUE; + orig = mapD.size; + max_index = 0; + TRK_ITERATE(trk) { + if ( GetTrkSelected(trk) ) { + if (trk->lo.x < orig.x) + orig.x = trk->lo.x; + if (trk->lo.y < orig.y) + orig.y = trk->lo.y; + trk->index = ++max_index; + } + } + orig.x -= trackGauge; + orig.y -= trackGauge; + xlat.x = - orig.x; + xlat.y = - orig.y; + TRK_ITERATE( trk ) { + if ( GetTrkSelected(trk) ) { + MoveTrack( trk, xlat ); + trackCmds(GetTrkType(trk))->write( trk, f ); + MoveTrack( trk, orig ); + } + } + RenumberTracks(); + exportingTracks = FALSE; + return TRUE; +} + +/******************************************************************************* + * + * AUDIT + * + */ + + +#define SET_BIT( set, bit ) set[bit>>3] |= (1<<(bit&7)) +#define BIT_SET( set, bit ) (set[bit>>3] & (1<<(bit&7))) + +static FILE * auditFile = NULL; +static BOOL_T auditStop = TRUE; +static int auditCount = 0; +static int auditIgnore = FALSE; + +static void AuditDebug( void ) +{ +} + +static void AuditPrint( char * msg ) +{ + time_t clock; + if (auditFile == NULL) { + sprintf( message, "%s%s%s", workingDir, FILE_SEP_CHAR, sAuditF ); + auditFile = fopen( message, "a+" ); + if (auditFile == NULL) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Audit"), message, strerror(errno) ); + auditIgnore = TRUE; + return; + } + time(&clock); + fprintf(auditFile,"\n#==== TRACK AUDIT FAILED\n#==== %s", ctime(&clock) ); + fprintf(auditFile,"#==== %s\n\n", msg ); + auditCount = 0; + auditIgnore = FALSE; + } + fprintf(auditFile, "# " ); + fprintf(auditFile, "%s", msg ); + if (auditIgnore) + return; + NoticeMessage( MSG_AUDIT_PRINT_MSG, _("Ok"), NULL, msg ); + if (++auditCount>10) { + if (NoticeMessage( MSG_AUDIT_PRINT_IGNORE, _("Yes"), _("No") ) ) + auditIgnore = TRUE; + auditCount = 0; + } +} + + +EXPORT void CheckTrackLength( track_cp trk ) +{ + DIST_T dist; + + if (trackCmds(trk->type)->getLength) { + dist = trackCmds(trk->type)->getLength( trk ); + } else { + ErrorMessage( MSG_CTL_UNK_TYPE, trk->type ); + return; + } + + if ( dist < minLength ) { + ErrorMessage( MSG_CTL_SHORT_TRK, dist ); + } +} + + +EXPORT void AuditTracks( char * event, ... ) +{ + va_list ap; + static char used[4096]; + wIndex_t i,j; + track_p trk, tn; + BOOL_T (*auditCmd)( track_p, char * ); + char msg[STR_SIZE], *msgp; + + va_start( ap, event ); + vsprintf( msg, event, ap ); + va_end( ap ); + msgp = msg+strlen(msg); + *msgp++ = '\n'; + + trackCount = 0; + for (i=0;i<sizeof used;i++) { + used[i] = 0; + } + if (*to_last) { + sprintf( msgp, "*to_last is not NULL (%lx)", (long)*to_last ); + AuditPrint( msg ); + } + TRK_ITERATE( trk ) { + trackCount++; + if (trk->type == T_NOTRACK) { + sprintf( msgp, "T%d: type is NOTRACK", trk->index ); + AuditPrint( msg ); + continue; + } + if (trk->index > max_index) { + sprintf( msgp, "T%d: index bigger than max %d\n", trk->index, max_index ); + AuditPrint( msg ); + } + if ((auditCmd = trackCmds( trk->type )->audit) != NULL) { + if (!auditCmd( trk, msgp )) + AuditPrint( msg ); + } + if (trk->index < 8*sizeof used) { + if (BIT_SET(used,trk->index)) { + sprintf( msgp, "T%d: index used again\n", trk->index ); + AuditPrint( msg ); + } + SET_BIT(used, trk->index); + } + for (i=0; i<trk->endCnt; i++) { + if ( (tn = trk->endPt[i].track) != NULL ) { + if (IsTrackDeleted(trk)) { + sprintf( msgp, "T%d[%d]: T%d is deleted\n", trk->index, i, tn->index ); + AuditPrint( msg ); + trk->endPt[i].track = NULL; + } else { + for (j=0;j<tn->endCnt;j++) + if (tn->endPt[j].track == trk) + goto nextEndPt; + sprintf( msgp, "T%d[%d]: T%d doesn\'t point back\n", trk->index, i, tn->index ); + AuditPrint( msg ); + trk->endPt[i].track = NULL; + } + } +nextEndPt:; + } + if (!trk->next) { + if (to_last != &trk->next) { + sprintf( msgp, "last track (T%d @ %lx) is not to_last (%lx)\n", + trk->index, (long)trk, (long)to_last ); + AuditPrint( msg ); + } + } + } + InfoCount( trackCount ); + if (auditFile != NULL) { + if (auditStop) + if (NoticeMessage( MSG_AUDIT_WRITE_FILE, _("Yes"), _("No"))) { + fprintf( auditFile, "# before undo\n" ); + WriteTracks(auditFile); + Rdump( auditFile ); + if (strcmp("undoUndo",event)==0) { + fprintf( auditFile, "# failure in undo\n" ); + } else if (UndoUndo()) { + fprintf( auditFile, "# after undo\n" ); + WriteTracks(auditFile); + Rdump( auditFile ); + } else { + fprintf( auditFile, "# undo stack is empty\n" ); + } + } + if (NoticeMessage( MSG_AUDIT_ABORT, _("Yes"), _("No"))) { + AuditDebug(); + exit(1); + } + fclose(auditFile); + auditFile = NULL; + } +} + + +EXPORT void ComputeRectBoundingBox( track_p trk, coOrd p0, coOrd p1 ) +{ + trk->lo.x = (float)min(p0.x, p1.x); + trk->lo.y = (float)min(p0.y, p1.y); + trk->hi.x = (float)max(p0.x, p1.x); + trk->hi.y = (float)max(p0.y, p1.y); +} + + +EXPORT void ComputeBoundingBox( track_p trk ) +{ + EPINX_T i; + + if (trk->endCnt <= 0) + AbortProg("computeBoundingBox - endCnt<=0"); + + trk->hi.x = trk->lo.x = (float)trk->endPt[0].pos.x; + trk->hi.y = trk->lo.y = (float)trk->endPt[0].pos.y; + for ( i=1; i<trk->endCnt; i++ ) { + if (trk->endPt[i].pos.x > trk->hi.x) + trk->hi.x = (float)trk->endPt[i].pos.x; + if (trk->endPt[i].pos.y > trk->hi.y) + trk->hi.y = (float)trk->endPt[i].pos.y; + if (trk->endPt[i].pos.x < trk->lo.x) + trk->lo.x = (float)trk->endPt[i].pos.x; + if (trk->endPt[i].pos.y < trk->lo.y) + trk->lo.y = (float)trk->endPt[i].pos.y; + } +} + + + +EXPORT DIST_T EndPtDescriptionDistance( + coOrd pos, + track_p trk, + EPINX_T ep ) +{ + elev_t *e; + coOrd pos1; + track_p trk1; + e = &trk->endPt[ep].elev; + if ((e->option&ELEV_MASK)==ELEV_NONE || + (e->option&ELEV_VISIBLE)==0 ) + return 100000; + if ((trk1=GetTrkEndTrk(trk,ep)) && GetTrkIndex(trk1)<GetTrkIndex(trk)) + return 100000; + /*REORIGIN( pos1, e->doff, GetTrkEndPos(trk,ep), GetTrkEndAngle(trk,ep) );*/ + pos1 = GetTrkEndPos(trk,ep); + pos1.x += e->doff.x; + pos1.y += e->doff.y; + return FindDistance( pos1, pos ); +} + + +EXPORT STATUS_T EndPtDescriptionMove( + track_p trk, + EPINX_T ep, + wAction_t action, + coOrd pos ) +{ + static coOrd p0, p1; + elev_t *e, *e1; + wDrawColor color; + track_p trk1; + + e = &trk->endPt[ep].elev; + switch (action) { + case C_DOWN: + p0 = GetTrkEndPos(trk,ep); + /*REORIGIN( p0, e->doff, GetTrkEndPos(trk,ep), GetTrkEndAngle(trk,ep) );*/ + + case C_MOVE: + case C_UP: + if (action != C_DOWN) + DrawLine( &tempD, p0, p1, 0, wDrawColorBlack ); + color = GetTrkColor( trk, &mainD ); + DrawEndElev( &tempD, trk, ep, color ); + p1 = pos; + e->doff.x = (pos.x-p0.x); + e->doff.y = (pos.y-p0.y); + if ((trk1=GetTrkEndTrk(trk,ep))) { + e1 = &trk1->endPt[GetEndPtConnectedToMe(trk1,trk)].elev; + e1->doff = e->doff; + } + DrawEndElev( &tempD, trk, ep, color ); + if (action != C_UP) + DrawLine( &tempD, p0, p1, 0, wDrawColorBlack ); + MainRedraw(); + return action==C_UP?C_TERMINATE:C_CONTINUE; + + case C_REDRAW: + DrawLine( &tempD, p0, p1, 0, wDrawColorBlack ); + break; + } + return C_CONTINUE; +} + + +/***************************************************************************** + * + * TRACK SPLICING ETC + * + */ + + +static DIST_T distanceEpsilon = 0.0; +static ANGLE_T angleEpsilon = 0.0; + +EXPORT void LoosenTracks( void ) +{ + track_p trk, trk1; + EPINX_T ep0, ep1; + ANGLE_T angle0, angle1; + coOrd pos0, pos1; + DIST_T d; + ANGLE_T a; + int count; + + count = 0; + TRK_ITERATE(trk) { + for (ep0=0; ep0<trk->endCnt; ep0++) { + trk1 = GetTrkEndTrk( trk, ep0 ); + if (trk1 == NULL) + continue; + ASSERT( !IsTrackDeleted(trk1) ); + ep1 = GetEndPtConnectedToMe( trk1, trk ); + if (ep1 < 0) + continue; + pos0 = GetTrkEndPos( trk, ep0 ); + pos1 = GetTrkEndPos( trk1, ep1 ); + angle0 = GetTrkEndAngle( trk, ep0 ); + angle1 = GetTrkEndAngle( trk1, ep1 ); + d = FindDistance( pos0, pos1 ); + a = NormalizeAngle( 180+angle0-angle1+angleEpsilon ); + if (d > distanceEpsilon || a > angleEpsilon*2.0) { + DisconnectTracks( trk, ep0, trk1, ep1 ); + count++; + InfoMessage( _("%d Track(s) loosened"), count ); + } + } + } + if (count) + MainRedraw(); + else + InfoMessage(_("No tracks loosened")); +} + +EXPORT void ConnectTracks( track_p trk0, EPINX_T inx0, track_p trk1, EPINX_T inx1 ) +{ + DIST_T d; + ANGLE_T a; + coOrd pos0, pos1; + + if ( !IsTrack(trk0) ) { + NoticeMessage( _("Connecting a non-track(%d) to (%d)"), _("Continue"), NULL, GetTrkIndex(trk0), GetTrkIndex(trk1) ); + return; + } + if ( !IsTrack(trk1) ) { + NoticeMessage( _("Connecting a non-track(%d) to (%d)"), _("Continue"), NULL, GetTrkIndex(trk1), GetTrkIndex(trk0) ); + return; + } + pos0 = trk0->endPt[inx0].pos; + pos1 = trk1->endPt[inx1].pos; +LOG( log_track, 3, ( "ConnectTracks( T%d[%d] @ [%0.3f, %0.3f] = T%d[%d] @ [%0.3f %0.3f]\n", trk0->index, inx0, pos0.x, pos0.y, trk1->index, inx1, pos1.x, pos1.y ) ) + d = FindDistance( pos0, pos1 ); + a = NormalizeAngle( trk0->endPt[inx0].angle - + trk1->endPt[inx1].angle + (180.0+connectAngle/2.0) ); + if (d > connectDistance || a > connectAngle || logTable(log_endPt).level>=1) { +#ifndef WINDOWS + LogPrintf( "connectTracks: T%d[%d] T%d[%d] d=%0.3f a=%0.3f\n %d ", + trk0->index, inx0, trk1->index, inx1, d, a, trk0->index ); + /*PrintEndPt( logFile, trk0, 0 ); + PrintEndPt( logFile, trk0, 1 );???*/ + LogPrintf( "\n %d ", trk1->index ); + /*PrintEndPt( logFile, trk1, 0 ); + PrintEndPt( logFile, trk1, 1 );???*/ + LogPrintf("\n"); +#endif + NoticeMessage( MSG_CONNECT_TRK, _("Continue"), NULL, trk0->index, inx0, trk1->index, inx1, d, a ); + } + UndoModify( trk0 ); + UndoModify( trk1 ); + if (!suspendElevUpdates) + SetTrkElevModes( TRUE, trk0, inx0, trk1, inx1 ); + trk0->endPt[inx0].track = trk1; + trk1->endPt[inx1].track = trk0; + AuditTracks( "connectTracks T%d[%d], T%d[%d]", trk0->index, inx0, trk1->index, inx1 ); +} + + +EXPORT void DisconnectTracks( track_p trk1, EPINX_T ep1, track_p trk2, EPINX_T ep2 ) +{ + if (trk1->endPt[ep1].track != trk2 || + trk2->endPt[ep2].track != trk1 ) + AbortProg("disconnectTracks: tracks not connected" ); + UndoModify( trk1 ); + UndoModify( trk2 ); + trk1->endPt[ep1].track = NULL; + trk2->endPt[ep2].track = NULL; + if (!suspendElevUpdates) + SetTrkElevModes( FALSE, trk1, ep1, trk2, ep2 ); +} + + +EXPORT BOOL_T ConnectAbuttingTracks( + track_p trk0, + EPINX_T ep0, + track_p trk1, + EPINX_T ep1 ) +{ + DIST_T d; + ANGLE_T a; + d = FindDistance( GetTrkEndPos(trk0,ep0), + GetTrkEndPos(trk1,ep1 ) ); + a = NormalizeAngle( GetTrkEndAngle(trk0,ep0) - + GetTrkEndAngle(trk1,ep1) + + (180.0+connectAngle/2.0) ); + if ( a < connectAngle && + d < connectDistance ) { + UndoStart( _("Join Abutting Tracks"), "ConnectAbuttingTracks( T%d[%d] T%d[%d] )", GetTrkIndex(trk0), ep0, GetTrkIndex(trk1), ep1 ); + DrawEndPt( &mainD, trk0, ep0, wDrawColorWhite ); + DrawEndPt( &mainD, trk1, ep1, wDrawColorWhite ); + ConnectTracks( trk0, ep0, + trk1, ep1 ); + DrawEndPt( &mainD, trk0, ep0, wDrawColorBlack ); + DrawEndPt( &mainD, trk1, ep1, wDrawColorBlack ); + UndoEnd(); + return TRUE; + } + return FALSE; +} + + +EXPORT ANGLE_T GetAngleAtPoint( track_p trk, coOrd pos, EPINX_T *ep0, EPINX_T *ep1 ) +{ + ANGLE_T (*getAngleCmd)( track_p, coOrd, EPINX_T *, EPINX_T * ); + + if ((getAngleCmd = trackCmds(trk->type)->getAngle) != NULL) + return getAngleCmd( trk, pos, ep0, ep1 ); + else { + NoticeMessage( MSG_GAAP_BAD_TYPE, _("Continue"), NULL, trk->type, trk->index ); + return 0; + } +} + + +EXPORT BOOL_T SplitTrack( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, BOOL_T disconnect ) +{ + DIST_T d; + track_p trk0, trk2, trkl; + EPINX_T epl, ep0, ep1, ep2=-1, epCnt; + BOOL_T rc; + BOOL_T (*splitCmd)( track_p, coOrd, EPINX_T, track_p *, EPINX_T *, EPINX_T * ); + coOrd pos0; + + trk0 = trk; + epl = ep; + epCnt = GetTrkEndPtCnt(trk); + *leftover = NULL; +LOG( log_track, 2, ( "SplitTrack( T%d[%d], (%0.3f %0.3f)\n", trk->index, ep, pos.x, pos.y ) ) + + if ((splitCmd = trackCmds(trk->type)->split) == NULL) { + ErrorMessage( MSG_CANT_SPLIT_TRK, trackCmds(trk->type)->name ); + return FALSE; + } + UndrawNewTrack( trk ); + UndoModify( trk ); + pos0 = trk->endPt[ep].pos; + if ((d = FindDistance( pos0, pos )) <= minLength) { + /* easy: just disconnect */ + if ((trk2=trk->endPt[ep].track) != NULL) { + UndrawNewTrack( trk2 ); + ep2 = GetEndPtConnectedToMe( trk2, trk ); + if (ep2 < 0) + return FALSE; + DisconnectTracks( trk, ep, trk2, ep2 ); + LOG( log_track, 2, ( " at endPt with T%d[%d]\n", trk2->index, ep2 ) ) + DrawNewTrack( trk2 ); + } else { + LOG( log_track, 2, ( " at endPt (no connection)\n") ) + } + *leftover = trk2; + DrawNewTrack( trk ); + +#ifdef LATER + } else if ( IsTurnout(trk) ) { + ErrorMessage( MSG_CANT_SPLIT_TRK, _("Turnout") ); + return FALSE; +#endif + + } else if ( epCnt == 2 && + (d = FindDistance( trk->endPt[1-ep].pos, pos )) <= minLength) { + /* easy: just disconnect */ + if ((trk2=trk->endPt[1-ep].track) != NULL) { + UndrawNewTrack( trk2 ); + ep2 = GetEndPtConnectedToMe( trk2, trk ); + if (ep2 < 0) + return FALSE; + DisconnectTracks( trk, 1-ep, trk2, ep2 ); + LOG( log_track, 2, ( " at endPt with T%d[%d]\n", trk2->index, ep2 ) ) + DrawNewTrack( trk2 ); +#ifdef LATER + *trk = trk2; + *ep = ep1; + *leftover = trk; +#endif + } else { +#ifdef LATER + *trk = NULL; +#endif + LOG( log_track, 2, ( " at endPt (no connection)\n") ) + } + DrawNewTrack( trk ); + + } else { + /* TODO circle's don't have ep's */ + trk2 = GetTrkEndTrk( trk, ep ); + if ( !disconnect ) + suspendElevUpdates = TRUE; + if (trk2 != NULL) { + ep2 = GetEndPtConnectedToMe( trk2, trk ); + DisconnectTracks( trk, ep, trk2, ep2 ); + } + rc = splitCmd( trk, pos, ep, leftover, &epl, &ep1 ); + if (!rc) { + if ( trk2 != NULL ) + ConnectTracks( trk, ep, trk2, ep2 ); + suspendElevUpdates = FALSE; + DrawNewTrack( trk ); + return FALSE; + } + ClrTrkElev( trk ); + if (*leftover) { + trkl = *leftover; + ep0 = epl; + if ( !disconnect ) + ConnectTracks( trk, ep, trkl, ep0 ); + ep0 = 1-ep0; + while ( 1 ) { + CopyAttributes( trk, trkl ); + ClrTrkElev( trkl ); + trk0 = GetTrkEndTrk(trkl,ep0); + if ( trk0 == NULL ) + break; + ep0 = 1-GetEndPtConnectedToMe(trk0,trkl); + trkl = trk0; + } + if (trk2) + ConnectTracks( trkl, ep0, trk2, ep2 ); + LOG( log_track, 2, ( " midTrack (leftover = T%d)\n", (trkl)->index ) ) + } + suspendElevUpdates = FALSE; + DrawNewTrack( trk ); + if (*leftover) { + trkl = *leftover; + ep0 = 1-epl; + while ( 1 ) { + DrawNewTrack( trkl ); + trk0 = GetTrkEndTrk(trkl,ep0); + if ( trk0 == NULL || trk0 == trk2 ) + break; + ep0 = 1-GetEndPtConnectedToMe(trk0,trkl); + trkl = trk0; + } + } + } + return TRUE; +} + + +EXPORT BOOL_T TraverseTrack( + traverseTrack_p trvTrk, + DIST_T * distR ) +{ + track_p oldTrk; + EPINX_T ep; + + while ( *distR > 0.0 && trvTrk->trk ) { + if ( trackCmds((trvTrk->trk)->type)->traverse == NULL ) + return FALSE; + oldTrk = trvTrk->trk; + if ( !trackCmds((trvTrk->trk)->type)->traverse( trvTrk, distR ) ) + return FALSE; + if ( *distR <= 0.0 ) + return TRUE; + if ( !trvTrk->trk ) + return FALSE; + ep = GetEndPtConnectedToMe( trvTrk->trk, oldTrk ); + if ( ep != -1 ) { + trvTrk->pos = GetTrkEndPos( trvTrk->trk, ep ); + trvTrk->angle = NormalizeAngle( GetTrkEndAngle( trvTrk->trk, ep ) + 180.0 ); + } + if ( trackCmds((trvTrk->trk)->type)->checkTraverse && + !trackCmds((trvTrk->trk)->type)->checkTraverse( trvTrk->trk, trvTrk->pos ) ) + return FALSE; + trvTrk->length = -1; + trvTrk->dist = 0.0; + } + return TRUE; +} + + +EXPORT BOOL_T RemoveTrack( track_p * trk, EPINX_T * ep, DIST_T *dist ) +{ + DIST_T dist1; + track_p trk1; + EPINX_T ep1=-1; + while ( *dist > 0.0 ) { + if (trackCmds((*trk)->type)->getLength == NULL) + return FALSE; + if (GetTrkEndPtCnt(*trk) != 2) + return FALSE; + dist1 = trackCmds((*trk)->type)->getLength(*trk); + if ( dist1 > *dist ) + break; + *dist -= dist1; + trk1 = GetTrkEndTrk( *trk, 1-*ep ); + if (trk1) + ep1 = GetEndPtConnectedToMe( trk1, *trk ); + DeleteTrack( *trk, FALSE ); + if (!trk1) + return FALSE; + *trk = trk1; + *ep = ep1; + } + dist1 = *dist; + *dist = 0.0; + return TrimTrack( *trk, *ep, dist1 ); +} + + +EXPORT BOOL_T TrimTrack( track_p trk, EPINX_T ep, DIST_T dist ) +{ + if (trackCmds(trk->type)->trim) + return trackCmds(trk->type)->trim( trk, ep, dist ); + else + return FALSE; +} + + +EXPORT BOOL_T MergeTracks( track_p trk0, EPINX_T ep0, track_p trk1, EPINX_T ep1 ) +{ + if (trk0->type == trk1->type && + trackCmds(trk0->type)->merge) + return trackCmds(trk0->type)->merge( trk0, ep0, trk1, ep1 ); + else + return FALSE; +} + + +EXPORT STATUS_T ExtendStraightFromOrig( track_p trk, wAction_t action, coOrd pos ) +{ + static EPINX_T ep; + static BOOL_T valid; + DIST_T d; + track_p trk1; + + switch ( action ) { + case C_DOWN: + ep = PickUnconnectedEndPoint( pos, trk ); + if ( ep == -1 ) + return C_ERROR; + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).width = 0; + tempSegs(0).u.l.pos[0] = GetTrkEndPos( trk, ep ); + InfoMessage( _("Drag to change track length") ); + + case C_MOVE: + d = FindDistance( tempSegs(0).u.l.pos[0], pos ); + valid = TRUE; + if ( d <= minLength ) { + if (action == C_MOVE) + ErrorMessage( MSG_TRK_TOO_SHORT, _("Connecting "), PutDim(fabs(minLength-d)) ); + valid = FALSE; + return C_CONTINUE; + } + Translate( &tempSegs(0).u.l.pos[1], tempSegs(0).u.l.pos[0], GetTrkEndAngle( trk, ep ), d ); + tempSegs_da.cnt = 1; + if (action == C_MOVE) + InfoMessage( _("Straight: Length=%s Angle=%0.3f"), + FormatDistance( d ), PutAngle( GetTrkEndAngle( trk, ep ) ) ); + return C_CONTINUE; + + case C_UP: + if (!valid) + return C_TERMINATE; + UndrawNewTrack( trk ); + trk1 = NewStraightTrack( tempSegs(0).u.l.pos[0], tempSegs(0).u.l.pos[1] ); + CopyAttributes( trk, trk1 ); + ConnectTracks( trk, ep, trk1, 0 ); + DrawNewTrack( trk ); + DrawNewTrack( trk1 ); + return C_TERMINATE; + + default: + ; + } + return C_ERROR; +} + + +EXPORT STATUS_T ModifyTrack( track_p trk, wAction_t action, coOrd pos ) +{ + if ( trackCmds(trk->type)->modify ) { + ClrTrkElev( trk ); + return trackCmds(trk->type)->modify( trk, action, pos ); + } else { + return C_TERMINATE; + } +} + + +EXPORT BOOL_T GetTrackParams( int inx, track_p trk, coOrd pos, trackParams_t * params ) +{ + if ( trackCmds(trk->type)->getTrackParams ) { + return trackCmds(trk->type)->getTrackParams( inx, trk, pos, params ); + } else { + ASSERT( FALSE ); /* CHECKME */ +#ifdef LATER + switch ( inx ) { + case PARAMS_1ST_JOIN: + case PARAMS_2ND_JOIN: + ErrorMessage( MSG_JOIN_TRK, (inx==PARAMS_1ST_JOIN?_("First"):_("Second")) ); + break; + case PARAMS_EXTEND: + ErrorMessage( MSG_CANT_EXTEND ); + break; + case PARAMS_PARALLEL: + ErrorMessage( MSG_INV_TRK_PARALLEL ); + break; + default: + ErrorMessage( MSG_INVALID_TRK ); + } +#endif + return FALSE; + } +} + + +EXPORT BOOL_T MoveEndPt( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d ) +{ + if ( trackCmds((*trk)->type)->moveEndPt ) { + return trackCmds((*trk)->type)->moveEndPt( trk, ep, pos, d ); + } else { + ErrorMessage( MSG_MEP_INV_TRK, GetTrkType(*trk) ); + return FALSE; + } +} + + +EXPORT BOOL_T QueryTrack( track_p trk, int query ) +{ + if ( trackCmds(trk->type)->query ) { + return trackCmds(trk->type)->query( trk, query ); + } else { + return FALSE; + } +} + + +EXPORT BOOL_T IsTrack( track_p trk ) +{ + return ( trk && QueryTrack( trk, Q_ISTRACK ) ); +} + + +EXPORT void UngroupTrack( track_p trk ) +{ + if ( trackCmds(trk->type)->ungroup ) { + trackCmds(trk->type)->ungroup( trk ); + } +} + + +EXPORT char * GetTrkTypeName( track_p trk ) +{ + return trackCmds(trk->type)->name; +} + + +EXPORT DIST_T GetFlexLength( track_p trk0, EPINX_T ep, coOrd * pos ) +{ + track_p trk = trk0, trk1; + EPINX_T ep1; + DIST_T d, dd; + + d = 0.0; + while(1) { + trk1 = GetTrkEndTrk( trk, ep ); + if (trk1 == NULL) + break; + if (trk1 == trk0) + break; + ep1 = GetEndPtConnectedToMe( trk1, trk ); + if (ep1 < 0 || ep1 > 1) + break; + if (trackCmds(trk1->type)->getLength == NULL) + break; + dd = trackCmds(trk1->type)->getLength(trk1); + if (dd <= 0.0) + break; + d += dd; + trk = trk1; + ep = 1-ep1; + if (d>1000000.0) + break; + } + *pos = GetTrkEndPos( trk, ep ); + return d; +} + + +EXPORT DIST_T GetTrkLength( track_p trk, EPINX_T ep0, EPINX_T ep1 ) +{ + coOrd pos0, pos1; + DIST_T d; + if (ep0 == ep1) + return 0.0; + else if (trackCmds(trk->type)->getLength != NULL) { + d = trackCmds(trk->type)->getLength(trk); + if (ep1==-1) + d /= 2.0; + return d; + } else { + pos0 = GetTrkEndPos(trk,ep0); + if (ep1==-1) { + pos1.x = (trk->hi.x+trk->lo.x)/2.0; + pos1.y = (trk->hi.y+trk->lo.y)/2.0; + } else { + pos1 = GetTrkEndPos(trk,ep1); + } + pos1.x -= pos0.x; + pos1.y -= pos0.y; + Rotate( &pos1, zero, -GetTrkEndAngle(trk,ep0) ); + return fabs(pos1.y); + } +} +#endif +/*#define DRAW_TUNNEL_NONE (0)*/ +#define DRAW_TUNNEL_DASH (1) +#define DRAW_TUNNEL_SOLID (2) +EXPORT long drawTunnel = DRAW_TUNNEL_DASH; + +/****************************************************************************** + * + * SIMPLE DRAWING + * + */ + +EXPORT long tieDrawMode = TIEDRAWMODE_SOLID; +EXPORT wDrawColor tieColor; + +EXPORT void DrawTie( + drawCmd_p d, + coOrd pos, + ANGLE_T angle, + DIST_T length, + DIST_T width, + wDrawColor color, + BOOL_T solid ) +{ + coOrd p[4], lo, hi; + + length /= 2; + width /= 2; + lo = hi = pos; + lo.x -= length; + lo.y -= length; + hi.x += length; + hi.y += length; + angle += 90; + Translate( &p[0], pos, angle, length ); + Translate( &p[1], p[0], angle+90, width ); + Translate( &p[0], p[0], angle-90, width ); + Translate( &p[2], pos, angle+180, length ); + Translate( &p[3], p[2], angle-90, width ); + Translate( &p[2], p[2], angle+90, width ); +#ifdef LATER + lo = hi = p[0]; + for ( i=1; i<4; i++ ) { + if ( p[i].x < lo.x ) lo.x = p[i].x; + if ( p[i].y < lo.y ) lo.y = p[i].y; + if ( p[i].x > hi.x ) hi.x = p[i].x; + if ( p[i].y > hi.y ) hi.y = p[i].y; + } +#endif + if ( d == &mainD ) { + lo.x -= RBORDER/mainD.dpi*mainD.scale; + lo.y -= TBORDER/mainD.dpi*mainD.scale; + hi.x += LBORDER/mainD.dpi*mainD.scale; + hi.y += BBORDER/mainD.dpi*mainD.scale; + if ( OFF_D( d->orig, d->size, lo, hi ) ) + return; + } + if ( solid ) { + DrawFillPoly( d, 4, p, color ); + } else { + DrawLine( d, p[0], p[1], 0, color ); + DrawLine( d, p[1], p[2], 0, color ); + DrawLine( d, p[2], p[3], 0, color ); + DrawLine( d, p[3], p[0], 0, color ); + } +} + + +EXPORT void DrawCurvedTies( + drawCmd_p d, + track_p trk, + coOrd p, + DIST_T r, + ANGLE_T a0, + ANGLE_T a1, + wDrawColor color ) +{ + tieData_p td = GetScaleTieData(GetTrkScale(trk)); + DIST_T len; + ANGLE_T ang, dang; + coOrd pos; + int cnt; + + if ( (d->funcs->options&wDrawOptTemp) != 0 ) + return; + if ( trk == NULL ) + return; + if ( (!GetTrkVisible(trk)) && drawTunnel!=DRAW_TUNNEL_SOLID ) + return; + if (color == wDrawColorBlack) + color = tieColor; + len = 2*M_PI*r*a1/360.0; + cnt = (int)(len/td->spacing); + if ( len-td->spacing*cnt-td->width > (td->spacing-td->width)/2 ) + cnt++; + if ( cnt != 0 ) { + dang = a1/cnt; + for ( ang=a0+dang/2; cnt; cnt--,ang+=dang ) { + PointOnCircle( &pos, p, r, ang ); + DrawTie( d, pos, ang+90, td->length, td->width, color, tieDrawMode==TIEDRAWMODE_SOLID ); + } + } +} + + +EXPORT void DrawCurvedTrack( + drawCmd_p d, + coOrd p, + DIST_T r, + ANGLE_T a0, + ANGLE_T a1, + coOrd p0, + coOrd p1, + track_p trk, + DIST_T trackGauge, + wDrawColor color, + long options ) +{ + DIST_T scale2rail; + wDrawWidth width=0; + trkSeg_p segPtr; + + if ( (d->options&DC_SEGTRACK) ) { + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + segPtr = &tempSegs(tempSegs_da.cnt-1); + segPtr->type = SEG_CRVTRK; + segPtr->width = 0; + segPtr->color = wDrawColorBlack; + segPtr->u.c.center = p; + segPtr->u.c.a0 = a0; + segPtr->u.c.a1 = a1; + segPtr->u.c.radius = r; + return; + } + + scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale; + if (options&DTS_THICK2) + width = 2; + if (options&DTS_THICK3) + width = 3; +#ifdef WINDOWS + width *= (wDrawWidth)(d->dpi/mainD.dpi); +#else + if (d->options&DC_PRINT) + width *= 300/75; +#endif + +LOG( log_track, 4, ( "DST( (%0.3f %0.3f) R%0.3f A%0.3f..%0.3f)\n", + p.x, p.y, r, a0, a1 ) ) + if ( (options&DTS_TIES) != 0 && trk && + tieDrawMode!=TIEDRAWMODE_NONE && + d!=&mapD && + (d->options&DC_TIES)!=0 && + d->scale<scale2rail/2 ) + DrawCurvedTies( d, trk, p, r, a0, a1, color ); + if (color == wDrawColorBlack) + color = normalColor; + if ( d->scale >= scale2rail ) { + DrawArc( d, p, r, a0, a1, ((d->scale<32) && centerDrawMode && !(options&DTS_NOCENTER)) ? 1 : 0, width, color ); + } else if (d->options & DC_QUICK) { + DrawArc( d, p, r, a0, a1, ((d->scale<32) && centerDrawMode && !(options&DTS_NOCENTER)) ? 1 : 0, 0, color ); + } else { + if ( (d->scale <= 1 && (d->options&DC_SIMPLE)==0) || (d->options&DC_CENTERLINE)!=0 ) { + long options = d->options; + d->options |= DC_DASH; + DrawArc( d, p, r, a0, a1, 0, 0, color ); + d->options = options; + } + DrawArc( d, p, r+trackGauge/2.0, a0, a1, 0, width, color ); + DrawArc( d, p, r-trackGauge/2.0, a0, a1, (centerDrawMode && !(options&DTS_NOCENTER) ? 1: 0), width, color ); + if ( (d->options&DC_PRINT) && roadbedWidth > trackGauge && d->scale <= scale2rail/2 ) { + wDrawWidth rbw = (wDrawWidth)floor(roadbedLineWidth*(d->dpi/d->scale)+0.5); + if ( options&DTS_RIGHT ) { + DrawArc( d, p, r+roadbedWidth/2.0, a0, a1, 0, rbw, color ); + } + if ( options&DTS_LEFT ) { + DrawArc( d, p, r-roadbedWidth/2.0, a0, a1, 0, rbw, color ); + } + } + } +} + + +EXPORT void DrawStraightTies( + drawCmd_p d, + track_p trk, + coOrd p0, + coOrd p1, + wDrawColor color ) +{ + tieData_p td = GetScaleTieData(GetTrkScale(trk)); + DIST_T tieOff0=0.0, tieOff1=0.0; + DIST_T len, dlen; + coOrd pos; + int cnt; + ANGLE_T angle; + + if ( (d->funcs->options&wDrawOptTemp) != 0 ) + return; + if ( trk == NULL ) + return; + if ( (!GetTrkVisible(trk)) && drawTunnel!=DRAW_TUNNEL_SOLID ) + return; + if ( color == wDrawColorBlack ) + color = tieColor; + td = GetScaleTieData( GetTrkScale(trk) ); + len = FindDistance( p0, p1 ); + len -= tieOff0+tieOff1; + angle = FindAngle( p0, p1 ); + cnt = (int)(len/td->spacing); + if ( len-td->spacing*cnt-td->width > (td->spacing-td->width)/2 ) + cnt++; + if ( cnt != 0 ) { + dlen = len/cnt; + for ( len=dlen/2; cnt; cnt--,len+=dlen ) { + Translate( &pos, p0, angle, len ); + DrawTie( d, pos, angle, td->length, td->width, color, tieDrawMode==TIEDRAWMODE_SOLID ); + } + } +} + + +EXPORT void DrawStraightTrack( + drawCmd_p d, + coOrd p0, + coOrd p1, + ANGLE_T angle, + track_p trk, + DIST_T trackGauge, + wDrawColor color, + long options ) +{ + coOrd pp0, pp1; + DIST_T scale2rail; + wDrawWidth width=0; + trkSeg_p segPtr; + + if ( (d->options&DC_SEGTRACK) ) { + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + segPtr = &tempSegs(tempSegs_da.cnt-1); + segPtr->type = SEG_STRTRK; + segPtr->width = 0; + segPtr->color = wDrawColorBlack; + segPtr->u.l.pos[0] = p0; + segPtr->u.l.pos[1] = p1; + segPtr->u.l.angle = angle; + segPtr->u.l.option = 0; + return; + } + + scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale; + + if (options&DTS_THICK2) + width = 2; + if (options&DTS_THICK3) + width = 3; +#ifdef WINDOWS + width *= (wDrawWidth)(d->dpi/mainD.dpi); +#else + if (d->options&DC_PRINT) + width *= 300/75; +#endif +LOG( log_track, 4, ( "DST( (%0.3f %0.3f) .. (%0.3f..%0.3f)\n", + p0.x, p0.y, p1.x, p1.y ) ) + if ( (options&DTS_TIES) != 0 && trk && + tieDrawMode!=TIEDRAWMODE_NONE && + d!=&mapD && + (d->options&DC_TIES)!=0 && + d->scale<scale2rail/2 ) + DrawStraightTies( d, trk, p0, p1, color ); + if (color == wDrawColorBlack) + color = normalColor; + if ( d->scale >= scale2rail ) { + DrawLine( d, p0, p1, width, color ); + } else if (d->options&DC_QUICK) { + DrawLine( d, p0, p1, 0, color ); + } else { + if ( (d->scale <= 1 && (d->options&DC_SIMPLE)==0) || (d->options&DC_CENTERLINE)!=0 ) { + long options = d->options; + d->options |= DC_DASH; + DrawLine( d, p0, p1, 0, color ); + d->options = options; + } + Translate( &pp0, p0, angle+90, trackGauge/2.0 ); + Translate( &pp1, p1, angle+90, trackGauge/2.0 ); + DrawLine( d, pp0, pp1, width, color ); + Translate( &pp0, p0, angle-90, trackGauge/2.0 ); + Translate( &pp1, p1, angle-90, trackGauge/2.0 ); + DrawLine( d, pp0, pp1, width, color ); + if ( (d->options&DC_PRINT) && roadbedWidth > trackGauge && d->scale <= scale2rail/2.0) { + wDrawWidth rbw = (wDrawWidth)floor(roadbedLineWidth*(d->dpi/d->scale)+0.5); + if ( options&DTS_RIGHT ) { + Translate( &pp0, p0, angle+90, roadbedWidth/2.0 ); + Translate( &pp1, p1, angle+90, roadbedWidth/2.0 ); + DrawLine( d, pp0, pp1, rbw, color ); + } + if ( options&DTS_LEFT ) { + Translate( &pp0, p0, angle-90, roadbedWidth/2.0 ); + Translate( &pp1, p1, angle-90, roadbedWidth/2.0 ); + DrawLine( d, pp0, pp1, rbw, color ); + } + } + } +} + + +EXPORT wDrawColor GetTrkColor( track_p trk, drawCmd_p d ) +{ + DIST_T len, elev0, elev1; + ANGLE_T grade = 0.0; + + if ( IsTrack( trk ) && GetTrkEndPtCnt(trk) == 2 ) { + len = GetTrkLength( trk, 0, 1 ); + if (len>0.1) { + ComputeElev( trk, 0, FALSE, &elev0, NULL ); + ComputeElev( trk, 1, FALSE, &elev1, NULL ); + grade = fabs( (elev1-elev0)/len )*100.0; + } + } + if ( (d->options&(DC_GROUP)) == 0 ) { + if ( grade > maxTrackGrade ) + return exceptionColor; + if ( QueryTrack( trk, Q_EXCEPTION ) ) + return exceptionColor; + } + if ( (d->options&(DC_PRINT|DC_GROUP)) == 0 ) { + if (GetTrkBits(trk)&TB_PROFILEPATH) + return profilePathColor; + if ((d->options&DC_PRINT)==0 && GetTrkSelected(trk)) + return selectedColor; + } + if ( (d->options&(DC_GROUP)) == 0 ) { + if ( (IsTrack(trk)?(colorLayers&1):(colorLayers&2)) ) + return GetLayerColor((LAYER_T)curTrackLayer); + } + return wDrawColorBlack; +} + + +EXPORT void DrawTrack( track_cp trk, drawCmd_p d, wDrawColor color ) +{ + DIST_T scale2rail; + TRKTYP_T trkTyp; + + trkTyp = GetTrkType(trk); + curTrackLayer = GetTrkLayer(trk); + if (d != &mapD ) { + if ( (!GetTrkVisible(trk)) ) { + if ( drawTunnel==DRAW_TUNNEL_NONE ) + return; + if ( drawTunnel==DRAW_TUNNEL_DASH ) + d->options |= DC_DASH; + } + if (color == wDrawColorBlack) { + color = GetTrkColor( trk, d ); + } + } + if (d == &mapD && !GetLayerOnMap(curTrackLayer)) + return; + if ( (IsTrack(trk)?(colorLayers&1):(colorLayers&2)) && + d != &mapD && color == wDrawColorBlack ) + color = GetLayerColor((LAYER_T)curTrackLayer); + scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale; + if ( (!inDrawTracks) && + tieDrawMode!=TIEDRAWMODE_NONE && + d != &mapD && + d->scale<scale2rail/2 && + QueryTrack(trk, Q_ISTRACK) && + (GetTrkVisible(trk) || drawTunnel==DRAW_TUNNEL_SOLID) ) { + d->options |= DC_TIES; + } + trackCmds(trkTyp)->draw( trk, d, color ); + if ( (!inDrawTracks) ) { + d->options &= ~DC_TIES; + } + d->options &= ~DC_DASH; + + DrawTrackElev( trk, d, color!=wDrawColorWhite ); +} + + +static void DrawATrack( track_cp trk, wDrawColor color ) +{ + DrawMapBoundingBox( FALSE ); + DrawTrack( trk, &mapD, color ); + DrawTrack( trk, &mainD, color ); + DrawMapBoundingBox( TRUE ); +} + + +EXPORT void DrawNewTrack( track_cp t ) +{ + DrawATrack( t, wDrawColorBlack ); +} + +EXPORT void UndrawNewTrack( track_cp t ) +{ + DrawATrack( t, wDrawColorWhite ); +} + +EXPORT int doDrawPositionIndicator = 1; +EXPORT void DrawPositionIndicators( void ) +{ + track_p trk; + coOrd hi, lo; + if ( !doDrawPositionIndicator ) + return; + TRK_ITERATE( trk ) { + if ( trackCmds(trk->type)->drawPositionIndicator ) { + if ( drawTunnel==DRAW_TUNNEL_NONE && (!GetTrkVisible(trk)) ) + continue; + GetBoundingBox( trk, &hi, &lo ); + if ( OFF_MAIND( lo, hi ) ) + continue; + if (!GetLayerVisible( GetTrkLayer(trk) ) ) + continue; + trackCmds(trk->type)->drawPositionIndicator( trk, selectedColor ); + } + } +} + + +EXPORT void AdvancePositionIndicator( + track_p trk, + coOrd pos, + coOrd * posR, + ANGLE_T * angleR ) +{ + if ( trackCmds(trk->type)->advancePositionIndicator ) + trackCmds(trk->type)->advancePositionIndicator( trk, pos, posR, angleR ); +} +/***************************************************************************** + * + * BASIC DRAWING + * + */ + +static void DrawUnconnectedEndPt( drawCmd_p d, coOrd p, ANGLE_T a, DIST_T trackGauge, wDrawColor color ) +{ + coOrd p0, p1; + Translate( &p0, p, a, trackGauge ); + Translate( &p1, p, a-180.0, trackGauge ); + DrawLine( d, p0, p1, 0, color ); + if (d->scale < 8) { + Translate( &p, p, a+90.0, 0.2 ); + Translate( &p0, p, a, trackGauge ); + Translate( &p1, p, a-180.0, trackGauge ); + DrawLine( d, p0, p1, 0, color ); + } +} + + +EXPORT void DrawEndElev( drawCmd_p d, track_p trk, EPINX_T ep, wDrawColor color ) +{ + coOrd pp; + wFont_p fp; + elev_t * elev; + track_p trk1; + DIST_T elev0, grade; + ANGLE_T a=0; + int style = BOX_BOX; + BOOL_T gradeOk = TRUE; + char *elevStr; + + if ((labelEnable&LABELENABLE_ENDPT_ELEV)==0) + return; + elev = &trk->endPt[ep].elev; /* TRACKDEP */ + if ( (elev->option&ELEV_MASK)==ELEV_NONE || + (elev->option&ELEV_VISIBLE)==0 ) + return; + if ( (trk1=GetTrkEndTrk(trk,ep)) && GetTrkIndex(trk1)<GetTrkIndex(trk) ) + return; + + fp = wStandardFont( F_HELV, FALSE, FALSE ); + pp = GetTrkEndPos( trk, ep ); + switch ((elev->option&ELEV_MASK)) { + case ELEV_COMP: + case ELEV_GRADE: + if ( color == wDrawColorWhite ) { + elev0 = grade = elev->u.height; + } else if ( !ComputeElev( trk, ep, FALSE, &elev0, &grade ) ) { + elev0 = grade = 0; + gradeOk = FALSE; + } + if ((elev->option&ELEV_MASK)==ELEV_COMP) { + elevStr = FormatDistance(elev0); + elev->u.height = elev0; + } else if (gradeOk) { + sprintf( message, "%0.1f%%", fabs(grade*100.0) ); + elevStr = message; + a = GetTrkEndAngle( trk, ep ); + style = BOX_ARROW; + if (grade <= -0.001) + a = NormalizeAngle( a+180.0 ); + else if ( grade < 0.001 ) + style = BOX_BOX; + elev->u.height = grade; + } else { + elevStr = "????%%"; + } + break; + case ELEV_DEF: + elevStr = FormatDistance( elev->u.height); + break; + case ELEV_STATION: + elevStr = elev->u.name; + break; + default: + return; + } + pp.x += elev->doff.x; + pp.y += elev->doff.y; + DrawBoxedString( style, d, pp, elevStr, fp, (wFontSize_t)descriptionFontSize, color, a ); +} + +/** + * Draw track endpoints. The correct track endpoint (connected, unconnected etc.) + * is drawn to the track. In case the endpoint is on the transition into a + * tunnel, a tunnel portal is drawn. + * + * \param d IN drawing functions to use (depends on print, draw to screen etc.) + * \param trk IN track for which endpoints are drawn + * \param ep IN index of endpoint to draw + * \param color IN color to use + */ + +EXPORT void DrawEndPt( + drawCmd_p d, + track_p trk, + EPINX_T ep, + wDrawColor color ) +{ + coOrd p, pp; + ANGLE_T a; + track_p trk1; + coOrd p0, p1, p2; + BOOL_T sepBoundary; + DIST_T trackGauge; + wDrawWidth width; + wDrawWidth width2; + + // line width for the tunnel portal, make sure it is rounded correctly + width2 = (wDrawWidth)round((2.0 * d->dpi)/75.0); + if (d->funcs->options&wDrawOptTemp) + return; + if ( trk && QueryTrack( trk, Q_NODRAWENDPT ) ) + return; + + if (trk == NULL || ep < 0) + return; + + if (color == wDrawColorBlack) + color = normalColor; + + if (labelScale >= d->scale) + DrawEndElev( d, trk, ep, color ); + + if ( d->scale >= ((d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale) ) + return; + + trk1 = GetTrkEndTrk(trk,ep); + pp = p = GetTrkEndPos( trk, ep ); + a = GetTrkEndAngle( trk, ep ) + 90.0; + + trackGauge = GetTrkGauge(trk); + if (trk1 == NULL) { + DrawUnconnectedEndPt( d, p, a, trackGauge, color ); + return; + } + + sepBoundary = FALSE; + if ((d->options&DC_PRINT)==0 && importTrack == NULL && GetTrkSelected(trk) && (!GetTrkSelected(trk1))) { + DIST_T len; + len = trackGauge*2.0; + if (len < 0.10*d->scale) + len = 0.10*d->scale; + Translate( &p0, p, a+45, len ); + Translate( &p1, p, a+225, len ); + DrawLine( &tempD, p0, p1, 0, selectedColor ); + Translate( &p0, p, a-45, len ); + Translate( &p1, p, a-225, len ); + DrawLine( &tempD, p0, p1, 0, selectedColor ); + sepBoundary = TRUE; + } else if ((d->options&DC_PRINT)==0 && importTrack == NULL && (!GetTrkSelected(trk)) && GetTrkSelected(trk1)) { + sepBoundary = TRUE; + } + + // is the endpoint a transition into a tunnel? + if (GetTrkVisible(trk) && (!GetTrkVisible(trk1))) { + // yes, draw tunnel portal + Translate( &p0, p, a, trackGauge ); + Translate( &p1, p, a+180, trackGauge ); + DrawLine( d, p0, p1, width2, color ); + Translate( &p2, p0, a+45, trackGauge/2.0 ); + DrawLine( d, p0, p2, width2, color ); + Translate( &p2, p1, a+135, trackGauge/2.0 ); + DrawLine( d, p1, p2, width2, color ); + if ( d == &mainD ) { + width = (wDrawWidth)ceil(trackGauge*d->dpi/2.0/d->scale); + if ( width > 1 ) { + if ( (GetTrkEndOption(trk,ep)&EPOPT_GAPPED) != 0 ) { + Translate( &p0, p, a, trackGauge ); + DrawLine( d, p0, p, width, color ); + } + trk1 = GetTrkEndTrk(trk,ep); + if ( trk1 ) { + ep = GetEndPtConnectedToMe( trk1, trk ); + if ( (GetTrkEndOption(trk1,ep)&EPOPT_GAPPED) != 0 ) { + Translate( &p0, p, a+180.0, trackGauge ); + DrawLine( d, p0, p, width, color ); + } + } + } + } + } else if ((!GetTrkVisible(trk)) && GetTrkVisible(trk1)) { + ; + } else if ( GetLayerVisible( GetTrkLayer( trk ) ) && !GetLayerVisible( GetTrkLayer( trk1 ) ) ) { + a -= 90.0; + Translate( &p, p, a, trackGauge/2.0 ); + Translate( &p0, p, a-135.0, trackGauge*2.0 ); + DrawLine( d, p0, p, width2, color ); + Translate( &p0, p, a+135.0, trackGauge*2.0 ); + DrawLine( d, p0, p, width2, color ); + } else if ( !GetLayerVisible( GetTrkLayer( trk ) ) && GetLayerVisible( GetTrkLayer( trk1 ) ) ) { + ; + } else if ( sepBoundary ) { + ; + } else if ( (drawEndPtV == 1 && (QueryTrack(trk,Q_DRAWENDPTV_1) || QueryTrack(trk1,Q_DRAWENDPTV_1)) ) || + (drawEndPtV == 2) ) { + Translate( &p0, p, a, trackGauge ); + width = 0; + if ( d != &mapD && d != &tempD && (GetTrkEndOption(trk,ep)&EPOPT_GAPPED) != 0 ) + width = (wDrawWidth)ceil(trackGauge*d->dpi/2.0/d->scale); + DrawLine( d, p0, p, width, color ); + } else { + ; + } +} + + +EXPORT void DrawEndPt2( + drawCmd_p d, + track_p trk, + EPINX_T ep, + wDrawColor color ) +{ + track_p trk1; + EPINX_T ep1; + DrawEndPt( d, trk, ep, color ); + trk1 = GetTrkEndTrk( trk, ep ); + if (trk1) { + ep1 = GetEndPtConnectedToMe( trk1, trk ); + if (ep1>=0) + DrawEndPt( d, trk1, ep1, color ); + } +} + +EXPORT void DrawTracks( drawCmd_p d, DIST_T scale, coOrd orig, coOrd size ) +{ + track_cp trk; + TRKINX_T inx; + wIndex_t count; + coOrd lo, hi; + BOOL_T doSelectRecount = FALSE; + + inDrawTracks = TRUE; + count = 0; + InfoCount( 0 ); + count = 0; + d->options |= DC_TIES; + TRK_ITERATE( trk ) { + if ( (d->options&DC_PRINT) != 0 && + wPrintQuit() ) { + inDrawTracks = FALSE; + return; + } + if ( GetTrkSelected(trk) && + ( (!GetLayerVisible(GetTrkLayer(trk))) || + (drawTunnel==0 && !GetTrkVisible(trk)) ) ) { + ClrTrkBits( trk, TB_SELECTED ); + doSelectRecount = TRUE; + } + GetBoundingBox( trk, &hi, &lo ); + if ( OFF_D( orig, size, lo, hi ) || + (d != &mapD && !GetLayerVisible( GetTrkLayer(trk) ) ) || + (d == &mapD && !GetLayerOnMap( GetTrkLayer(trk) ) ) ) + continue; + DrawTrack( trk, d, wDrawColorBlack ); + count++; + if (count%10 == 0) + InfoCount( count ); + } + d->options &= ~DC_TIES; + + if (d == &mainD) { + for (inx=1; inx<trackCmds_da.cnt; inx++) + if (trackCmds(inx)->redraw != NULL) + trackCmds(inx)->redraw(); + } + InfoCount( trackCount ); + inDrawTracks = FALSE; + if ( doSelectRecount ) + SelectRecount(); +} + + +EXPORT void RedrawLayer( LAYER_T l, BOOL_T draw ) +{ + MainRedraw(); +#ifdef LATER + track_cp trk; + track_cp trk1; + EPINX_T ep; + wIndex_t count; + coOrd hi, lo; + + count = 0; + InfoCount( 0 ); + TRK_ITERATE( trk ) { + if (GetTrkLayer(trk) != l) + continue; + GetBoundingBox( trk, &hi, &lo ); + if ( !OFF_MAIND( lo, hi ) ) { + if ( GetLayerVisible( l ) ) { + DrawTrack( trk, &mainD, draw?wDrawColorBlack:wDrawColorWhite ); + } + for (ep=0; ep<GetTrkEndPtCnt(trk); ep++) { + trk1 = GetTrkEndTrk( trk, ep ); + if ( trk1 && GetTrkLayer(trk1) != l && GetLayerVisible(GetTrkLayer(trk1)) ) { + DrawEndPt( &mainD, trk1, GetEndPtConnectedToMe( trk1, trk ), + draw?wDrawColorBlack:wDrawColorWhite ); + } + } + count++; + if (count%10 == 0) + InfoCount( count ); + } + if (draw) + ClrTrkBits( trk, TB_SELECTED ); + } + InfoCount( trackCount ); + SelectRecount(); +#endif +} + + +EXPORT void DrawSelectedTracks( drawCmd_p d ) +{ + track_cp trk; + wIndex_t count; + + count = 0; + InfoCount( 0 ); + + TRK_ITERATE( trk ) { + if ( GetTrkSelected( trk ) ) { + DrawTrack( trk, d, wDrawColorBlack ); + count++; + if (count%10 == 0) + InfoCount( count ); + } + } + InfoCount( trackCount ); + SelectRecount(); +} + + +EXPORT void HilightElevations( BOOL_T hilight ) +{ + static long lastRedraw = -1; + static BOOL_T lastHilight = FALSE; + track_p trk, trk1; + EPINX_T ep; + int mode; + DIST_T elev; + coOrd pos; + DIST_T radius; + + if (currRedraw > lastRedraw) { + lastRedraw = currRedraw; + lastHilight = FALSE; + } + if (lastHilight == hilight) + return; + radius = 0.05*mainD.scale; + if ( radius < trackGauge/2.0 ) + radius = trackGauge/2.0; + TRK_ITERATE( trk ) { + for (ep=0;ep<GetTrkEndPtCnt(trk);ep++) { + GetTrkEndElev( trk, ep, &mode, &elev ); /* TRACKDEP */ + if ((mode&ELEV_MASK)==ELEV_DEF || (mode&ELEV_MASK)==ELEV_IGNORE) { + if ((trk1=GetTrkEndTrk(trk,ep)) != NULL && + GetTrkIndex(trk1) < GetTrkIndex(trk)) + continue; + if (drawTunnel == DRAW_TUNNEL_NONE && (!GetTrkVisible(trk)) && (trk1==NULL||!GetTrkVisible(trk1)) ) + continue; + if ((!GetLayerVisible(GetTrkLayer(trk))) && + (trk1==NULL||!GetLayerVisible(GetTrkLayer(trk1)))) + continue; + pos = GetTrkEndPos(trk,ep); + if ( !OFF_MAIND( pos, pos ) ) { + DrawFillCircle( &tempD, pos, radius, + ((mode&ELEV_MASK)==ELEV_DEF?elevColorDefined:elevColorIgnore) ); + } + } + } + } + lastHilight = hilight; +} + + +EXPORT void HilightSelectedEndPt( BOOL_T show, track_p trk, EPINX_T ep ) +{ + static BOOL_T lastShow = FALSE; + static long lastRedraw = -1; + coOrd pos; + if (trk == NULL) + return; + if (currRedraw > lastRedraw) { + lastRedraw = currRedraw; + lastShow = FALSE; + } + if (lastShow != show) { + pos = GetTrkEndPos( trk, ep ); + DrawFillCircle( &tempD, pos, 0.10*mainD.scale, selectedColor ); + lastShow = show; + } +} + + +EXPORT void LabelLengths( drawCmd_p d, track_p trk, wDrawColor color ) +{ + wFont_p fp; + wFontSize_t fs; + EPINX_T i; + coOrd p0, p1; + DIST_T dist; + char * msg; + coOrd textsize; + + if ((labelEnable&LABELENABLE_LENGTHS)==0) + return; + fp = wStandardFont( F_HELV, FALSE, FALSE ); + fs = (float)descriptionFontSize/d->scale; + for (i=0; i<GetTrkEndPtCnt(trk); i++) { + p0 = GetTrkEndPos( trk, i ); + dist = GetFlexLength( trk, i, &p1 ); + if (dist < 0.1) + continue; + if (dist < 3.0) { + p0.x = (p0.x+p1.x)/2.0; + p0.y = (p0.y+p1.y)/2.0; + } else { + Translate( &p0, p0, GetTrkEndAngle(trk,i), 0.25*d->scale ); + } + msg = FormatDistance(dist); + DrawTextSize( &mainD, msg, fp, fs, TRUE, &textsize ); + p0.x -= textsize.x/2.0; + p0.y -= textsize.y/2.0; + DrawString( d, p0, 0.0, msg, fp, fs*d->scale, color ); + } +} diff --git a/app/bin/track.h b/app/bin/track.h new file mode 100644 index 0000000..81f5e4c --- /dev/null +++ b/app/bin/track.h @@ -0,0 +1,654 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/track.h,v 1.3 2009-05-25 18:11:03 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 TRACK_H +#define TRACK_H + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#ifndef WINDOWS +#include <unistd.h> +#endif +#include <math.h> +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif +#include <string.h> + +#include "wlib.h" +#include "common.h" +#include "utility.h" +#include "draw.h" +#include "misc.h" + + + + +extern TRKTYP_T T_NOTRACK; + +struct track_t ; +typedef struct track_t * track_p; +typedef struct track_t * track_cp; +extern track_p tempTrack; +extern wIndex_t trackCount; +extern long drawTunnel; +extern long drawEndPtV; +extern long centerDrawMode; +extern wDrawColor selectedColor; +extern wDrawColor normalColor; +extern BOOL_T useCurrentLayer; +extern LAYER_T curTrackLayer; +extern coOrd descriptionOff; +extern DIST_T roadbedWidth; +extern DIST_T roadbedLineWidth; +extern long drawElevations; +extern wDrawColor elevColorIgnore; +extern wDrawColor elevColorDefined; +extern DIST_T minTrackRadius; +extern DIST_T maxTrackGrade; +extern wDrawColor exceptionColor; +#define TIEDRAWMODE_NONE (0) +#define TIEDRAWMODE_OUTLINE (1) +#define TIEDRAWMODE_SOLID (2) +extern long tieDrawMode; +extern wDrawColor tieColor; + + +extern TRKINX_T max_index; + +typedef signed char * PATHPTR_T; +extern PATHPTR_T pathPtr; +extern int pathCnt; +extern int pathMax; + +extern BOOL_T onTrackInSplit; + +typedef enum { curveTypeNone, curveTypeCurve, curveTypeStraight } curveType_e; + +#define PARAMS_1ST_JOIN (0) +#define PARAMS_2ND_JOIN (1) +#define PARAMS_EXTEND (2) +#define PARAMS_PARALLEL (3) + +typedef struct { + curveType_e type; + EPINX_T ep; + DIST_T len; + ANGLE_T angle; + coOrd lineOrig; + coOrd lineEnd; + coOrd arcP; + DIST_T arcR; + ANGLE_T arcA0, arcA1; + long helixTurns; + } trackParams_t; + +#define Q_CANNOT_BE_ON_END (1) +#define Q_IGNORE_EASEMENT_ON_EXTEND (2) +#define Q_REFRESH_JOIN_PARAMS_ON_MOVE (3) +#define Q_CANNOT_PLACE_TURNOUT (4) +#define Q_DONT_DRAW_ENDPOINT (5) +#define Q_DRAWENDPTV_1 (6) +#define Q_CAN_PARALLEL (7) +#define Q_CAN_MODIFYRADIUS (8) +#define Q_EXCEPTION (9) +#define Q_CAN_GROUP (10) +#define Q_FLIP_ENDPTS (11) +#define Q_CAN_NEXT_POSITION (12) +#define Q_NODRAWENDPT (13) +#define Q_ISTRACK (14) +#define Q_NOT_PLACE_FROGPOINTS (15) +#define Q_HAS_DESC (16) +#define Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK (17) + +typedef struct { + track_p trk; + DIST_T length; + DIST_T dist; + coOrd pos; + ANGLE_T angle; + } traverseTrack_t, *traverseTrack_p; + + +typedef struct { + char * name; + void (*draw)( track_p, drawCmd_p, wDrawColor ); + DIST_T (*distance)( track_p, coOrd * ); + void (*describe)( track_p, char * line, CSIZE_T len ); + void (*delete)( track_p ); + BOOL_T (*write)( track_p, FILE * ); + void (*read)( char * ); + void (*move)( track_p, coOrd ); + void (*rotate)( track_p, coOrd, ANGLE_T ); + void (*rescale)( track_p, FLOAT_T ); + BOOL_T (*audit)( track_p, char * ); + ANGLE_T (*getAngle)( track_p, coOrd, EPINX_T *, EPINX_T * ); + BOOL_T (*split)( track_p, coOrd, EPINX_T, track_p *, EPINX_T *, EPINX_T * ); + BOOL_T (*traverse)( traverseTrack_p, DIST_T * ); + BOOL_T (*enumerate)( track_p ); + void (*redraw)( void ); + BOOL_T (*trim)( track_p, EPINX_T, DIST_T ); + BOOL_T (*merge)( track_p, EPINX_T, track_p, EPINX_T ); + STATUS_T (*modify)( track_p, wAction_t, coOrd ); + DIST_T (*getLength)( track_p ); + BOOL_T (*getTrackParams)( int, track_p, coOrd pos, trackParams_t * ); + BOOL_T (*moveEndPt)( track_p *, EPINX_T *, coOrd, DIST_T ); + BOOL_T (*query)( track_p, int ); + void (*ungroup)( track_p ); + void (*flip)( track_p, coOrd, ANGLE_T ); + void (*drawPositionIndicator)( track_p, wDrawColor ); + void (*advancePositionIndicator)( track_p, coOrd, coOrd *, ANGLE_T * ); + BOOL_T (*checkTraverse)( track_p, coOrd ); + BOOL_T (*makeParallel)( track_p, coOrd, DIST_T, track_p *, coOrd *, coOrd * ); + void (*drawDesc)( track_p, drawCmd_p, wDrawColor ); + } trackCmd_t; + + +#define NOELEV (-10000.0) +typedef enum { ELEV_NONE, ELEV_DEF, ELEV_COMP, ELEV_GRADE, ELEV_IGNORE, ELEV_STATION } elevMode_e; +#define ELEV_MASK 0x07 +#define ELEV_VISIBLE 0x08 +typedef struct { + int option; + coOrd doff; + union { + DIST_T height; + char * name; + } u; + } elev_t; +#define EPOPT_GAPPED (1L<<0) +typedef struct { + coOrd pos; + ANGLE_T angle; + TRKINX_T index; + track_p track; + elev_t elev; + long option; + } trkEndPt_t, * trkEndPt_p; + +dynArr_t tempEndPts_da; +#define tempEndPts(N) DYNARR_N( trkEndPt_t, tempEndPts_da, N ) + + +typedef struct { + char type; + wDrawColor color; + DIST_T width; + union { + struct { + coOrd pos[2]; + ANGLE_T angle; + long option; + } l; + struct { + coOrd center; + ANGLE_T a0, a1; + DIST_T radius; + } c; + struct { + coOrd pos; + ANGLE_T angle; + DIST_T R, L; + DIST_T l0, l1; + unsigned int flip:1; + unsigned int negate:1; + unsigned int Scurve:1; + } j; + struct { + coOrd pos; + ANGLE_T angle; + wFont_p fontP; + FONTSIZE_T fontSize; + char * string; + } t; + struct { + int cnt; + coOrd * pts; + coOrd orig; + ANGLE_T angle; + } p; + } u; + } trkSeg_t, * trkSeg_p; + +#define SEG_STRTRK ('S') +#define SEG_CRVTRK ('C') +#define SEG_STRLIN ('L') +#define SEG_CRVLIN ('A') +#define SEG_JNTTRK ('J') +#define SEG_FILCRCL ('G') +#define SEG_POLY ('Y') +#define SEG_FILPOLY ('F') +#define SEG_TEXT ('Z') +#define SEG_UNCEP ('E') +#define SEG_CONEP ('T') +#define SEG_PATH ('P') +#define SEG_SPEC ('X') +#define SEG_CUST ('U') +#define SEG_DOFF ('D') +#define SEG_BENCH ('B') +#define SEG_DIMLIN ('M') +#define SEG_TBLEDGE ('Q') + +#define IsSegTrack( S ) ( (S)->type == SEG_STRTRK || (S)->type == SEG_CRVTRK || (S)->type == SEG_JNTTRK ) + +dynArr_t tempSegs_da; +#define tempSegs(N) DYNARR_N( trkSeg_t, tempSegs_da, N ) + +char tempSpecial[4096]; +char tempCustom[4096]; + +void ComputeCurvedSeg( + trkSeg_p s, + DIST_T radius, + coOrd p0, + coOrd p1 ); + +coOrd GetSegEndPt( + trkSeg_p segPtr, + EPINX_T ep, + BOOL_T bounds, + ANGLE_T * ); + +void GetTextBounds( coOrd, ANGLE_T, char *, FONTSIZE_T, coOrd *, coOrd * ); +void GetSegBounds( coOrd, ANGLE_T, wIndex_t, trkSeg_p, coOrd *, coOrd * ); +void MoveSegs( wIndex_t, trkSeg_p, coOrd ); +void RotateSegs( wIndex_t, trkSeg_p, coOrd, ANGLE_T ); +void FlipSegs( wIndex_t, trkSeg_p, coOrd, ANGLE_T ); +void RescaleSegs( wIndex_t, trkSeg_p, DIST_T, DIST_T, DIST_T ); +void CloneFilledDraw( wIndex_t, trkSeg_p, BOOL_T ); +void FreeFilledDraw( wIndex_t, trkSeg_p ); +DIST_T DistanceSegs( coOrd, ANGLE_T, wIndex_t, trkSeg_p, coOrd *, wIndex_t * ); +void DrawDimLine( drawCmd_p, coOrd, coOrd, char *, wFontSize_t, FLOAT_T, wDrawWidth, wDrawColor, long ); +void DrawSegs( + drawCmd_p d, + coOrd orig, + ANGLE_T angle, + trkSeg_p segPtr, + wIndex_t segCnt, + DIST_T trackGauge, + wDrawColor color ); +void DrawSegsO( + drawCmd_p d, + track_p trk, + coOrd orig, + ANGLE_T angle, + trkSeg_p segPtr, + wIndex_t segCnt, + DIST_T trackGauge, + wDrawColor color, + long options ); +ANGLE_T GetAngleSegs( wIndex_t, trkSeg_p, coOrd, wIndex_t * ); +void RecolorSegs( wIndex_t, trkSeg_p, wDrawColor ); + +BOOL_T ReadSegs( void ); +BOOL_T WriteSegs( FILE * f, wIndex_t segCnt, trkSeg_p segs ); +typedef union { + struct { + coOrd pos; /* IN */ + ANGLE_T angle; + DIST_T dist; /* OUT */ + BOOL_T backwards; + } traverse1; + struct { + EPINX_T segDir; /* IN */ + DIST_T dist; /* IN/OUT */ + coOrd pos; /* OUT */ + ANGLE_T angle; + } traverse2; + struct { + int first, last; /* IN */ + ANGLE_T side; + DIST_T roadbedWidth; + wDrawWidth rbw; + coOrd orig; + ANGLE_T angle; + wDrawColor color; + drawCmd_p d; + } drawRoadbedSide; + struct { + coOrd pos1; /* IN/OUT */ + DIST_T dd; /* OUT */ + } distance; + struct { + track_p trk; /* OUT */ + EPINX_T ep[2]; + } newTrack; + struct { + DIST_T length; + } length; + struct { + coOrd pos; /* IN */ + DIST_T length[2]; /* OUT */ + trkSeg_t newSeg[2]; + } split; + struct { + coOrd pos; /* IN */ + ANGLE_T angle; /* OUT */ + } getAngle; + } segProcData_t, *segProcData_p; +typedef enum { + SEGPROC_TRAVERSE1, + SEGPROC_TRAVERSE2, + SEGPROC_DRAWROADBEDSIDE, + SEGPROC_DISTANCE, + SEGPROC_FLIP, + SEGPROC_NEWTRACK, + SEGPROC_LENGTH, + SEGPROC_SPLIT, + SEGPROC_GETANGLE + } segProc_e; +void SegProc( segProc_e, trkSeg_p, segProcData_p ); +void StraightSegProc( segProc_e, trkSeg_p, segProcData_p ); +void CurveSegProc( segProc_e, trkSeg_p, segProcData_p ); +void JointSegProc( segProc_e, trkSeg_p, segProcData_p ); + + + +/* debug.c */ +void SetDebug( char * ); + +#define TB_SELECTED (1<<0) +#define TB_VISIBLE (1<<1) +#define TB_PROFILEPATH (1<<2) +#define TB_ELEVPATH (1<<3) +#define TB_PROCESSED (1<<4) +#define TB_SHRTPATH (1<<5) +#define TB_HIDEDESC (1<<6) +#define TB_CARATTACHED (1<<7) +#define TB_TEMPBITS (TB_PROFILEPATH|TB_PROCESSED) + +/* track.c */ +#ifdef FASTTRACK +#include "trackx.h" +#define GetTrkIndex( T ) ((T)->index) +#define GetTrkType( T ) ((T)->type) +#define GetTrkScale( T ) ((T)->scale) +#define SetTrkScale( T, S ) (T)->scale = ((char)(S)) +/*#define GetTrkSelected( T ) ((T)->bits&TB_SELECTED)*/ +/*#define GetTrkVisible( T ) ((T)->bits&TB_VISIBLE)*/ +/*#define SetTrkVisible( T, V ) ((V) ? (T)->bits |= TB_VISIBLE : (T)->bits &= !TB_VISIBLE)*/ +#define GetTrkLayer( T ) ((T)->layer) +#define SetBoundingBox( T, HI, LO ) \ + (T)->hi.x = (float)(HI).x; (T)->hi.y = (float)(HI).y; (T)->lo.x = (float)(LO).x; (T)->lo.x; (T)->lo.x = (float)(LO).y = (float)(LO).y +#define GetBoundingBox( T, HI, LO ) \ + (HI)->x = (POS_T)(T)->hi.x; (HI)->y = (POS_T)(T)->hi.y; (LO)->x = (POS_T)(T)->lo.x; (LO)->y = (POS_T)(T)->lo.y; +#define GetTrkEndPtCnt( T ) ((T)->endCnt) +#define SetTrkEndPoint( T, I, PP, AA ) \ + Assert((T)->endPt[I].track); \ + (T)->endPt[I].pos = PP; \ + (T)->endPt[I].angle = AA +#define GetTrkEndTrk( T, I ) ((T)->endPt[I].track) +#define GetTrkEndPos( T, I ) ((T)->endPt[I].pos) +#define GetTrkEndPosXY( T, I ) PutDim((T)->endPt[I].pos.x), PutDim((T)->endPt[I].pos.y) +#define GetTrkEndAngle( T, I ) ((T)->endPt[I].angle) +#define GetTrkEndOption( T, I ) ((T)->endPt[I].option) +#define SetTrkEndOption( T, I, O ) ((T)->endPt[I].option=O) +#define GetTrkExtraData( T ) ((T)->extraData) +#define GetTrkWidth( T ) (int)((T)->width) +#define SetTrkWidth( T, W ) (T)->width = (unsigned int)(W) +#define GetTrkBits(T) ((T)->bits) +#define SetTrkBits(T,V) ((T)->bits|=(V)) +#define ClrTrkBits(T,V) ((T)->bits&=~(V)) +#define IsTrackDeleted(T) ((T)->deleted) +#else +TRKINX_T GetTrkIndex( track_p ); +TRKTYP_T GetTrkType( track_p ); +SCALEINX_T GetTrkScale( track_p ); +void SetTrkScale( track_p, SCALEINX_T ); +BOOL_T GetTrkSelected( track_p ); +BOOL_T GetTrkVisible( track_p ); +void SetTrkVisible( track_p, BOOL_T ); +LAYER_T GetTrkLayer( track_p ); +void SetBoundingBox( track_p, coOrd, coOrd ); +void GetBoundingBox( track_p, coOrd*, coOrd* ); +EPINX_T GetTrkEndPtCnt( track_p ); +void SetTrkEndPoint( track_p, EPINX_T, coOrd, ANGLE_T ); +track_p GetTrkEndTrk( track_p, EPINX_T ); +coOrd GetTrkEndPos( track_p, EPINX_T ); +#define GetTrkEndPosXY( trk, ep ) PutDim(GetTrkEndPos(trk,ep).x), PutDim(GetTrkEndPos(trk,ep).y) +ANGLE_T GetTrkEndAngle( track_p, EPINX_T ); +long GetTrkEndOption( track_p, EPINX_T ); +long SetTrkEndOption( track_p, EPINX_T, long ); +struct extraData * GetTrkExtraData( track_p ); +int GetTrkWidth( track_p ); +void SetTrkWidth( track_p, int ); +int GetTrkBits( track_p ); +int SetTrkBits( track_p, int ); +int ClrTrkBits( track_p, int ); +BOOL_T IsTrackDeleted( track_p ); +#endif + +#define GetTrkSelected(T) (GetTrkBits(T)&TB_SELECTED) +#define GetTrkVisible(T) (GetTrkBits(T)&TB_VISIBLE) +#define SetTrkVisible(T,V) ((V)?SetTrkBits(T,TB_VISIBLE):ClrTrkBits(T,TB_VISIBLE)) +int ClrAllTrkBits( int ); + +void GetTrkEndElev( track_p trk, EPINX_T e, int *option, DIST_T *height ); +void SetTrkEndElev( track_p, EPINX_T, int, DIST_T, char * ); +int GetTrkEndElevMode( track_p, EPINX_T ); +int GetTrkEndElevUnmaskedMode( track_p, EPINX_T ); +DIST_T GetTrkEndElevHeight( track_p, EPINX_T ); +char * GetTrkEndElevStation( track_p, EPINX_T ); +#define EndPtIsDefinedElev( T, E ) (GetTrkEndElevMode(T,E)==ELEV_DEF) +#define EndPtIsIgnoredElev( T, E ) (GetTrkEndElevMode(T,E)==ELEV_IGNORE) +#define EndPtIsStationElev( T, E ) (GetTrkEndElevMode(T,E)==ELEV_STATION) +void SetTrkElev( track_p, int, DIST_T ); +int GetTrkElevMode( track_p ); +DIST_T GetTrkElev( track_p trk ); +void ClearElevPath( void ); +BOOL_T GetTrkOnElevPath( track_p, DIST_T * elev ); +void SetTrkLayer( track_p, int ); +BOOL_T CheckTrackLayer( track_p ); +void CopyAttributes( track_p, track_p ); + +#define GetTrkGauge( T ) GetScaleTrackGauge(GetTrkScale(T)) +#define GetTrkScaleName( T ) GetScaleName(GetTrkScale(T)) +void SetTrkEndPtCnt( track_p, EPINX_T ); +BOOL_T WriteEndPt( FILE *, track_cp, EPINX_T ); +EPINX_T PickEndPoint( coOrd, track_cp ); +EPINX_T PickUnconnectedEndPoint( coOrd, track_cp ); + +void AuditTracks( char *, ... ); +void CheckTrackLength( track_cp ); +track_p NewTrack( wIndex_t, TRKTYP_T, EPINX_T, CSIZE_T ); +void DescribeTrack( track_cp, char *, CSIZE_T ); +EPINX_T GetEndPtConnectedToMe( track_p, track_p ); +void SetEndPts( track_p, EPINX_T ); +BOOL_T DeleteTrack( track_p, BOOL_T ); + +void MoveTrack( track_p, coOrd ); +void RotateTrack( track_p, coOrd, ANGLE_T ); +void RescaleTrack( track_p, FLOAT_T, coOrd ); +#define GNTignoreIgnore (1<<0) +#define GNTfirstDefined (1<<1) +#define GNTonPath (1<<2) +EPINX_T GetNextTrk( track_p, EPINX_T, track_p *, EPINX_T *, int ); +EPINX_T GetNextTrkOnPath( track_p, EPINX_T ); +#define FDE_DEF 0 +#define FDE_UDF 1 +#define FDE_END 2 +int FindDefinedElev( track_p, EPINX_T, int, BOOL_T, DIST_T *, DIST_T *); +BOOL_T ComputeElev( track_p, EPINX_T, BOOL_T, DIST_T *, DIST_T * ); + +#define DTS_LEFT (1<<0) +#define DTS_RIGHT (1<<1) +#define DTS_THICK2 (1<<2) +#define DTS_THICK3 (1<<3) +#define DTS_TIES (1<<4) +#define DTS_NOCENTER (1<<5) + +void DrawCurvedTies( drawCmd_p, track_p, coOrd, DIST_T, ANGLE_T, ANGLE_T, wDrawColor ); +void DrawCurvedTrack( drawCmd_p, coOrd, DIST_T, ANGLE_T, ANGLE_T, coOrd, coOrd, track_p, DIST_T, wDrawColor, long ); +void DrawStraightTies( drawCmd_p, track_p, coOrd, coOrd, wDrawColor ); +void DrawStraightTrack( drawCmd_p, coOrd, coOrd, ANGLE_T, track_p, DIST_T, wDrawColor, long ); + +ANGLE_T GetAngleAtPoint( track_p, coOrd, EPINX_T *, EPINX_T * ); +DIST_T GetTrkDistance( track_cp, coOrd ); +track_p OnTrack( coOrd *, INT_T, BOOL_T ); +track_p OnTrack2( coOrd *, INT_T, BOOL_T, BOOL_T ); + +void ComputeRectBoundingBox( track_p, coOrd, coOrd ); +void ComputeBoundingBox( track_p ); +void DrawEndPt( drawCmd_p, track_p, EPINX_T, wDrawColor ); +void DrawEndPt2( drawCmd_p, track_p, EPINX_T, wDrawColor ); +void DrawEndElev( drawCmd_p, track_p, EPINX_T, wDrawColor ); +wDrawColor GetTrkColor( track_p, drawCmd_p ); +void DrawTrack( track_cp, drawCmd_p, wDrawColor ); +void DrawTracks( drawCmd_p, DIST_T, coOrd, coOrd ); +void RedrawLayer( LAYER_T, BOOL_T ); +void DrawNewTrack( track_cp ); +void DrawOneTrack( track_cp, drawCmd_p ); +void UndrawNewTrack( track_cp ); +void DrawSelectedTracks( drawCmd_p ); +void HilightElevations( BOOL_T ); +void HilightSelectedEndPt( BOOL_T, track_p, EPINX_T ); +DIST_T EndPtDescriptionDistance( coOrd, track_p, EPINX_T ); +STATUS_T EndPtDescriptionMove( track_p, EPINX_T, wAction_t, coOrd ); + +track_p FindTrack( TRKINX_T ); +void ResolveIndex( void ); +void RenumberTracks( void ); +BOOL_T ReadTrack( char * ); +BOOL_T WriteTracks( FILE * ); +BOOL_T ExportTracks( FILE * ); +void ImportStart( void ); +void ImportEnd( void ); +void FreeTrack( track_p ); +void ClearTracks( void ); +BOOL_T TrackIterate( track_p * ); + +void LoosenTracks( void ); + +void SaveTrackState( void ); +void RestoreTrackState( void ); +void SaveCarState( void ); +void RestoreCarState( void ); +TRKTYP_T InitObject( trackCmd_t* ); + +void ConnectTracks( track_p, EPINX_T, track_p, EPINX_T ); +BOOL_T ReconnectTrack( track_p, EPINX_T, track_p, EPINX_T ); +void DisconnectTracks( track_p, EPINX_T, track_p, EPINX_T ); +BOOL_T ConnectAbuttingTracks( track_p, EPINX_T, track_p, EPINX_T ); +BOOL_T SplitTrack( track_p, coOrd, EPINX_T, track_p *leftover, BOOL_T ); +BOOL_T TraverseTrack( traverseTrack_p, DIST_T * ); +BOOL_T RemoveTrack( track_p*, EPINX_T*, DIST_T* ); +BOOL_T TrimTrack( track_p, EPINX_T, DIST_T ); +BOOL_T MergeTracks( track_p, EPINX_T, track_p, EPINX_T ); +STATUS_T ExtendStraightFromOrig( track_p, wAction_t, coOrd ); +STATUS_T ModifyTrack( track_p, wAction_t, coOrd ); +BOOL_T GetTrackParams( int, track_p, coOrd, trackParams_t* ); +BOOL_T MoveEndPt( track_p *, EPINX_T *, coOrd, DIST_T ); +BOOL_T QueryTrack( track_p, int ); +void UngroupTrack( track_p ); +BOOL_T IsTrack( track_p ); +char * GetTrkTypeName( track_p ); + +DIST_T GetFlexLength( track_p, EPINX_T, coOrd * ); +void LabelLengths( drawCmd_p, track_p, wDrawColor ); +DIST_T GetTrkLength( track_p, EPINX_T, EPINX_T ); + +void SelectAbove( void ); +void SelectBelow( void ); + +void FlipPoint( coOrd*, coOrd, ANGLE_T ); +void FlipTrack( track_p, coOrd, ANGLE_T ); + +void DrawPositionIndicators( void ); +void AdvancePositionIndicator( track_p, coOrd, coOrd *, ANGLE_T * ); + +BOOL_T MakeParallelTrack( track_p, coOrd, DIST_T, track_p *, coOrd *, coOrd * ); + +#include "cundo.h" +#include "cselect.h" + +/* cmisc.c */ +wIndex_t describeCmdInx; +typedef enum { DESC_NULL, DESC_POS, DESC_FLOAT, DESC_ANGLE, DESC_LONG, DESC_COLOR, DESC_DIM, DESC_PIVOT, DESC_LAYER, DESC_STRING, DESC_TEXT, DESC_LIST, DESC_EDITABLELIST } descType; +#define DESC_RO (1<<0) +#define DESC_IGNORE (1<<1) +#define DESC_NOREDRAW (1<<2) +#define DESC_CHANGE (1<<8) +typedef enum { DESC_PIVOT_FIRST, DESC_PIVOT_MID, DESC_PIVOT_SECOND, DESC_PIVOT_NONE } descPivot_t; +#define DESC_PIVOT_1 +typedef struct { + coOrd pos; + POS_T ang; + } descEndPt_t; +typedef struct { + descType type; + char * label; + void * valueP; + int mode; + wControl_p control0; + wControl_p control1; + wPos_t posy; + } descData_t, * descData_p; +typedef void (*descUpdate_t)( track_p, int, descData_p, BOOL_T ); +void DoDescribe( char *, track_p, descData_p, descUpdate_t ); +void DescribeCancel( void ); +BOOL_T UpdateDescStraight( int, int, int, int, int, descData_p, long ); + + +/* compound.c */ +DIST_T CompoundDescriptionDistance( coOrd, track_p ); +STATUS_T CompoundDescriptionMove( track_p, wAction_t, coOrd ); + +/* elev.c */ +#define ELEV_FORK (3) +#define ELEV_BRANCH (2) +#define ELEV_ISLAND (1) +#define ELEV_ALONE (0) + +long oldElevationEvaluation; +EPINX_T GetNextTrkOnPath( track_p trk, EPINX_T ep ); +int FindDefinedElev( track_p, EPINX_T, int, BOOL_T, DIST_T *, DIST_T * ); +BOOL_T ComputeElev( track_p, EPINX_T, BOOL_T, DIST_T *, DIST_T * ); +void RecomputeElevations( void ); +void UpdateAllElevations( void ); +DIST_T GetElevation( track_p ); +void ClrTrkElev( track_p ); +void SetTrkElevModes( BOOL_T, track_p, EPINX_T, track_p, EPINX_T ); +void UpdateTrkEndElev( track_p, EPINX_T, int, DIST_T, char * ); +void DrawTrackElev( track_p, drawCmd_p, BOOL_T ); + +/* cdraw.c */ +track_p MakeDrawFromSeg( coOrd, ANGLE_T, trkSeg_p ); +BOOL_T OnTableEdgeEndPt( track_p, coOrd * ); +BOOL_T ReadTableEdge( char * ); +BOOL_T ReadText( char * ); + +/* chotbar.c */ +extern DIST_T curBarScale; +void InitHotBar( void ); +void HideHotBar( void ); +void LayoutHotBar( void ); +typedef enum { HB_SELECT, HB_DRAW, HB_LISTTITLE, HB_BARTITLE, HB_FULLTITLE } hotBarProc_e; +typedef char * (*hotBarProc_t)( hotBarProc_e, void *, drawCmd_p, coOrd * ); +void AddHotBarElement( char *, coOrd, coOrd, BOOL_T, DIST_T, void *, hotBarProc_t ); +void HotBarCancel( void ); +void AddHotBarTurnouts( void ); +void AddHotBarStructures( void ); +void AddHotBarCarDesc( void ); + +#endif + diff --git a/app/bin/trackx.h b/app/bin/trackx.h new file mode 100644 index 0000000..6b46140 --- /dev/null +++ b/app/bin/trackx.h @@ -0,0 +1,52 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/trackx.h,v 1.1 2005-12-07 15:47:39 rc-flyer 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 TRACKX_H +#define TRACKX_H + +struct extraData; + +typedef struct track_t { + struct track_t *next; + TRKINX_T index; + TRKTYP_T type; + LAYER_T layer; + signed char scale; + BOOL_T modified:1; + BOOL_T deleted:1; + BOOL_T new:1; + unsigned int width:2; + unsigned int elevMode:2; + unsigned int bits:9; + EPINX_T endCnt; + trkEndPt_p endPt; + struct { float x; float y; } lo, hi; + struct extraData * extraData; + CSIZE_T extraSize; + DIST_T elev; + } track_t; + +extern track_p to_first; +extern track_p * to_last; +#define TRK_ITERATE(TRK) for (TRK=to_first; TRK!=NULL; TRK=TRK->next) if (!(TRK->deleted)) +#endif diff --git a/app/bin/trkseg.c b/app/bin/trkseg.c new file mode 100644 index 0000000..ff3725c --- /dev/null +++ b/app/bin/trkseg.c @@ -0,0 +1,1662 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/trkseg.c,v 1.2 2006-05-30 16:11:55 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 <time.h> +#include <ctype.h> +#include <stdarg.h> +#include "track.h" +#include "cjoin.h" + +/***************************************************************************** + * + * TRACK SEGMENTS + * + */ + +EXPORT void ComputeCurvedSeg( + trkSeg_p s, + DIST_T radius, + coOrd p0, + coOrd p1 ) +{ + DIST_T d; + ANGLE_T a, aa, aaa; + s->u.c.radius = radius; + d = FindDistance( p0, p1 )/2.0; + a = FindAngle( p0, p1 ); + if (radius > 0) { + aa = R2D(asin( d/radius )); + aaa = a + (90.0 - aa); + Translate( &s->u.c.center, p0, aaa, radius ); + s->u.c.a0 = NormalizeAngle( aaa + 180.0 ); + s->u.c.a1 = aa*2.0; + } else { + aa = R2D(asin( d/(-radius) )); + aaa = a - (90.0 - aa); + Translate( &s->u.c.center, p0, aaa, -radius ); + s->u.c.a0 = NormalizeAngle( aaa + 180.0 - aa *2.0 ); + s->u.c.a1 = aa*2.0; + } +} + + +EXPORT coOrd GetSegEndPt( + trkSeg_p segPtr, + EPINX_T ep, + BOOL_T bounds, + ANGLE_T * angleR ) +{ + coOrd pos; + ANGLE_T angle, a, a0, a1; + DIST_T r; + POS_T x0, y0, x1, y1; + + switch (segPtr->type) { + case SEG_STRTRK: + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + pos = segPtr->u.l.pos[ep]; + angle = FindAngle( segPtr->u.l.pos[1-ep], segPtr->u.l.pos[ep] ); + break; + case SEG_CRVLIN: + case SEG_CRVTRK: + a0 = segPtr->u.c.a0; + a1 = segPtr->u.c.a1; + r = fabs( segPtr->u.c.radius ); + a = a0; + if ( (ep==1) == (segPtr->u.c.radius>0) ) { + a += a1; + angle = NormalizeAngle( a+90 ); + } else { + angle = NormalizeAngle( a-90 ); + } + if (bounds) { + x0 = r * sin(D2R(a0)); + x1 = r * sin(D2R(a0+a1)); + y0 = r * cos(D2R(a0)); + y1 = r * cos(D2R(a0+a1)); + if (ep == 0) { + pos.x = segPtr->u.c.center.x + (((a0<=270.0)&&(a0+a1>=270.0)) ? + (-r) : min(x0,x1)); + pos.y = segPtr->u.c.center.y + (((a0<=180.0)&&(a0+a1>=180.0)) ? + (-r) : min(y0,y1)); + } else { + pos.x = segPtr->u.c.center.x + (((a0<= 90.0)&&(a0+a1>= 90.0)) ? + (r) : max(x0,x1)); + pos.y = segPtr->u.c.center.y + ((a0+a1>=360.0) ? + (r) : max(y0,y1)); + } + } else { + PointOnCircle( &pos, segPtr->u.c.center, fabs(segPtr->u.c.radius), a ); + } + break; + case SEG_JNTTRK: + pos = GetJointSegEndPos( segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.flip, segPtr->u.j.Scurve, ep, &angle ); + break; + default: + AbortProg("GetSegCntPt(%c)", segPtr->type ); + } + if ( angleR ) + *angleR = angle; + return pos; +} + +/** + * Caclulate the bounding box for a string. + * + * \param coOrd IN position of text + * \param angle IN text angle + * \param str IN the string + * \param fs IN size of font + * \param loR OUT bottom left corner + * \param hiR OUT top right corner + * \return describe the return value + */ + +EXPORT void GetTextBounds( + coOrd pos, + ANGLE_T angle, + char * str, + FONTSIZE_T fs, + coOrd * loR, + coOrd * hiR ) +{ + coOrd size; + POS_T descent; + coOrd lo, hi; + coOrd p[4]; + int i; + + DrawTextSize2( &mainD, str, NULL, fs, FALSE, &size, &descent ); + + // set up the corners of the rectangle + p[0].x = p[3].x = 0.0; + p[1].x = p[2].x = size.x; + p[0].y = p[1].y = -descent; + p[2].y = p[3].y = size.y; + + lo = hi = zero; + + // rotate each point + for ( i=1; i<4; i++ ) { + Rotate( &p[i], zero, angle ); + if ( p[i].x < lo.x ) lo.x = p[i].x; + if ( p[i].y < lo.y ) lo.y = p[i].y; + if ( p[i].x > hi.x ) hi.x = p[i].x; + if ( p[i].y > hi.y ) hi.y = p[i].y; + } + + // now recaclulate the corners + loR->x = pos.x + lo.x; + loR->y = pos.y + lo.y; + hiR->x = pos.x + hi.x; + hiR->y = pos.y + hi.y; +} + + +static void Get1SegBounds( trkSeg_p segPtr, coOrd xlat, ANGLE_T angle, coOrd *lo, coOrd *hi ) +{ + int inx; + coOrd p0, p1, pc; + ANGLE_T a0, a1; + coOrd width; + DIST_T radius; + + width = zero; + switch ( segPtr->type ) { + case ' ': + return; + case SEG_STRTRK: + case SEG_CRVTRK: + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + case SEG_CRVLIN: + case SEG_JNTTRK: + REORIGIN( p0, GetSegEndPt( segPtr, 0, FALSE, NULL ), angle, xlat ) + REORIGIN( p1, GetSegEndPt( segPtr, 1, FALSE, NULL ), angle, xlat ) + if (p0.x < p1.x) { + lo->x = p0.x; + hi->x = p1.x; + } else { + lo->x = p1.x; + hi->x = p0.x; + } + if (p0.y < p1.y) { + lo->y = p0.y; + hi->y = p1.y; + } else { + lo->y = p1.y; + hi->y = p0.y; + } + if ( segPtr->type == SEG_CRVTRK || + segPtr->type == SEG_CRVLIN ) { + REORIGIN( pc, segPtr->u.c.center, angle, xlat ); + a0 = NormalizeAngle( segPtr->u.c.a0 + angle ); + a1 = segPtr->u.c.a1; + radius = fabs(segPtr->u.c.radius); + if ( a1 >= 360.0 ) { + lo->x = pc.x - radius; + lo->y = pc.y - radius; + hi->x = pc.x + radius; + hi->y = pc.y + radius; + return; + } + if ( a0 + a1 >= 360.0 ) + hi->y = pc.y + radius; + if ( a0 < 90.0 && a0+a1 >= 90.0 ) + hi->x = pc.x + radius; + if ( a0 < 180 && a0+a1 >= 180.0 ) + lo->y = pc.y - radius; + if ( a0 < 270.0 && a0+a1 >= 270.0 ) + lo->x = pc.x - radius; + } + if ( segPtr->type == SEG_STRLIN ) { + width.x = segPtr->width * fabs(cos( D2R( FindAngle(p0, p1) ) ) ) / 2.0; + width.y = segPtr->width * fabs(sin( D2R( FindAngle(p0, p1) ) ) ) / 2.0; + } else if ( segPtr->type == SEG_CRVLIN ) { + /* TODO: be more precise about curved line width */ + width.x = width.y = segPtr->width/2.0; + } else if ( segPtr->type == SEG_BENCH ) { + width.x = BenchGetWidth( segPtr->u.l.option ) * fabs(cos( D2R( FindAngle(p0, p1) ) ) ) / 2.0; + width.y = BenchGetWidth( segPtr->u.l.option ) * fabs(sin( D2R( FindAngle(p0, p1) ) ) ) / 2.0; + } + break; + case SEG_POLY: + /* TODO: be more precise about poly line width */ + width.x = width.y = segPtr->width/2.0; + case SEG_FILPOLY: + for (inx=0; inx<segPtr->u.p.cnt; inx++ ) { + REORIGIN( p0, segPtr->u.p.pts[inx], angle, xlat ) + if (inx==0) { + *lo = *hi = p0; + } else { + if (p0.x < lo->x) + lo->x = p0.x; + if (p0.y < lo->y) + lo->y = p0.y; + if (p0.x > hi->x) + hi->x = p0.x; + if (p0.y > hi->y) + hi->y = p0.y; + } + } + break; + case SEG_FILCRCL: + REORIGIN( p0, segPtr->u.c.center, angle, xlat ) + lo->x = p0.x - segPtr->u.c.radius; + hi->x = p0.x + segPtr->u.c.radius; + lo->y = p0.y - segPtr->u.c.radius; + hi->y = p0.y + segPtr->u.c.radius; + break; + case SEG_TEXT: + REORIGIN( p0, segPtr->u.t.pos, angle, xlat ) + GetTextBounds( p0, angle+segPtr->u.t.angle, segPtr->u.t.string, segPtr->u.t.fontSize, lo, hi ); + break; + default: + ; + } + lo->x -= width.x; + lo->y -= width.y; + hi->x += width.x; + hi->y += width.y; +} + + +EXPORT void GetSegBounds( + coOrd xlat, + ANGLE_T angle, + wIndex_t segCnt, + trkSeg_p segs, + coOrd * orig_ret, + coOrd * size_ret ) +{ + trkSeg_p s; + coOrd lo, hi, tmpLo, tmpHi; + BOOL_T first; + + first = TRUE; + for (s=segs; s<&segs[segCnt]; s++) { + if (s->type == ' ') + continue; + if (first) { + Get1SegBounds( s, xlat, angle, &lo, &hi ); + first = FALSE; + } else { + Get1SegBounds( s, xlat, angle, &tmpLo, &tmpHi ); + if (tmpLo.x < lo.x) + lo.x = tmpLo.x; + if (tmpLo.y < lo.y) + lo.y = tmpLo.y; + if (tmpHi.x > hi.x) + hi.x = tmpHi.x; + if (tmpHi.y > hi.y) + hi.y = tmpHi.y; + } + } + if (first) { + *orig_ret = xlat; + *size_ret = zero; + return; + } + if (lo.x < hi.x) { + orig_ret->x = lo.x; + size_ret->x = hi.x-lo.x; + } else { + orig_ret->x = hi.x; + size_ret->x = lo.x-hi.x; + } + if (lo.y < hi.y) { + orig_ret->y = lo.y; + size_ret->y = hi.y-lo.y; + } else { + orig_ret->y = hi.y; + size_ret->y = lo.y-hi.y; + } +} + + +EXPORT void MoveSegs( + wIndex_t segCnt, + trkSeg_p segs, + coOrd orig ) +{ + trkSeg_p s; + int inx; + + for (s=segs; s<&segs[segCnt]; s++) { + switch (s->type) { + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + case SEG_STRTRK: + s->u.l.pos[0].x += orig.x; + s->u.l.pos[0].y += orig.y; + s->u.l.pos[1].x += orig.x; + s->u.l.pos[1].y += orig.y; + break; + case SEG_CRVLIN: + case SEG_CRVTRK: + case SEG_FILCRCL: + s->u.c.center.x += orig.x; + s->u.c.center.y += orig.y; + break; + case SEG_TEXT: + s->u.t.pos.x += orig.x; + s->u.t.pos.y += orig.y; + break; + case SEG_POLY: + case SEG_FILPOLY: + for (inx=0; inx<s->u.p.cnt; inx++) { + s->u.p.pts[inx].x += orig.x; + s->u.p.pts[inx].y += orig.y; + } + break; + case SEG_JNTTRK: + s->u.j.pos.x += orig.x; + s->u.j.pos.y += orig.y; + break; + } + } +} + + +EXPORT void RotateSegs( + wIndex_t segCnt, + trkSeg_p segs, + coOrd orig, + ANGLE_T angle ) +{ + trkSeg_p s; + int inx; + + for (s=segs; s<&segs[segCnt]; s++) { + switch (s->type) { + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + case SEG_STRTRK: + Rotate( &s->u.l.pos[0], orig, angle ); + Rotate( &s->u.l.pos[1], orig, angle ); + break; + case SEG_CRVLIN: + case SEG_CRVTRK: + case SEG_FILCRCL: + Rotate( &s->u.c.center, orig, angle ); + s->u.c.a0 = NormalizeAngle( s->u.c.a0+angle ); + break; + case SEG_TEXT: + Rotate( &s->u.t.pos, orig, angle ); + s->u.t.angle = NormalizeAngle( s->u.t.angle+angle ); + break; + case SEG_POLY: + case SEG_FILPOLY: + for (inx=0; inx<s->u.p.cnt; inx++) { + Rotate( &s->u.p.pts[inx], orig, angle ); + } + break; + case SEG_JNTTRK: + Rotate( &s->u.j.pos, orig, angle ); + s->u.j.angle = NormalizeAngle( s->u.j.angle+angle ); + break; + } + } +} + + +EXPORT void FlipSegs( + wIndex_t segCnt, + trkSeg_p segs, + coOrd orig, + ANGLE_T angle ) +{ + trkSeg_p s; + int inx; + coOrd * pts; + + for (s=segs; s<&segs[segCnt]; s++) { + switch (s->type) { + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + case SEG_STRTRK: + s->u.l.pos[0].y = -s->u.l.pos[0].y; + s->u.l.pos[1].y = -s->u.l.pos[1].y; + break; + case SEG_CRVTRK: + s->u.c.radius = - s->u.c.radius; + case SEG_CRVLIN: + case SEG_FILCRCL: + s->u.c.center.y = -s->u.c.center.y; + if ( s->u.c.a1 < 360.0 ) + s->u.c.a0 = NormalizeAngle( 180.0 - s->u.c.a0 - s->u.c.a1 ); + break; + case SEG_TEXT: + s->u.t.pos.y = -s->u.t.pos.y; +/* TODO flip text angle */ + break; + case SEG_POLY: + case SEG_FILPOLY: + pts = (coOrd*)MyMalloc( s->u.p.cnt * sizeof *(coOrd*)NULL ); + memcpy( pts, s->u.p.pts, s->u.p.cnt * sizeof *(coOrd*)NULL ); + s->u.p.pts = pts; + for (inx=0; inx<s->u.p.cnt; inx++) { + s->u.p.pts[inx].y = -s->u.p.pts[inx].y; + } + break; + case SEG_JNTTRK: + s->u.j.pos.y = - s->u.j.pos.y; + s->u.j.angle = NormalizeAngle( 180.0 - s->u.j.angle ); + s->u.j.negate = ! s->u.j.negate; + break; + } + } +} + + +EXPORT void RescaleSegs( + wIndex_t segCnt, + trkSeg_p segs, + DIST_T scale_x, + DIST_T scale_y, + DIST_T scale_w ) +{ + trkSeg_p s; + int inx; + + for (s=segs; s<&segs[segCnt]; s++) { + s->width *= scale_w; + switch (s->type) { + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + case SEG_STRTRK: + s->u.l.pos[0].y *= scale_y; + s->u.l.pos[0].x *= scale_x; + s->u.l.pos[1].x *= scale_x; + s->u.l.pos[1].y *= scale_y; + break; + case SEG_CRVTRK: + case SEG_CRVLIN: + case SEG_FILCRCL: + s->u.c.center.x *= scale_x; + s->u.c.center.y *= scale_y; + s->u.c.radius *= scale_w; + break; + case SEG_TEXT: + s->u.t.pos.x *= scale_x; + s->u.t.pos.y *= scale_y; + s->u.t.fontSize *= scale_w; + break; + case SEG_POLY: + case SEG_FILPOLY: + for (inx=0; inx<s->u.p.cnt; inx++) { + s->u.p.pts[inx].x *= scale_x; + s->u.p.pts[inx].y *= scale_y; + } + break; + case SEG_JNTTRK: + s->u.j.pos.x *= scale_x; + s->u.j.pos.y *= scale_y; + s->u.j.R *= scale_w; + s->u.j.L *= scale_w; + s->u.j.l0 *= scale_w; + s->u.j.l1 *= scale_w; + break; + } + } +} + + +EXPORT void CloneFilledDraw( + int segCnt, + trkSeg_p segs, + BOOL_T reorigin ) +{ + coOrd * newPts; + trkSeg_p sp; + wIndex_t inx; + + for ( sp=segs; sp<&segs[segCnt]; sp++ ) { + switch (sp->type) { + case SEG_POLY: + case SEG_FILPOLY: + newPts = (coOrd*)MyMalloc( sp->u.p.cnt * sizeof *(coOrd*)0 ); + if ( reorigin ) { + for ( inx = 0; inx<sp->u.p.cnt; inx++ ) + REORIGIN( newPts[inx], sp->u.p.pts[inx], sp->u.p.angle, sp->u.p.orig ); + sp->u.p.angle = 0; + sp->u.p.orig = zero; + } else { + memcpy( newPts, sp->u.p.pts, sp->u.p.cnt * sizeof *(coOrd*)0 ); + } + sp->u.p.pts = newPts; + break; + case SEG_TEXT: + sp->u.t.string = MyStrdup( sp->u.t.string ); + break; + default: + break; + } + } +} + + +EXPORT void FreeFilledDraw( + int segCnt, + trkSeg_p segs ) +{ + trkSeg_p sp; + + for ( sp=segs; sp<&segs[segCnt]; sp++ ) { + switch (sp->type) { + case SEG_POLY: + case SEG_FILPOLY: + if ( sp->u.p.pts ) + MyFree( sp->u.p.pts ); + sp->u.p.pts = NULL; + break; + case SEG_TEXT: + if ( sp->u.t.string ) + MyFree( sp->u.t.string ); + sp->u.t.string = NULL; + break; + default: + break; + } + } +} + + +EXPORT DIST_T DistanceSegs( + coOrd orig, + ANGLE_T angle, + wIndex_t segCnt, + trkSeg_p segPtr, + coOrd * pos, + wIndex_t * inx_ret ) +{ + DIST_T d, dd = 100000.0, ddd; + coOrd p0, p1, p2, pt, lo, hi; + BOOL_T found = FALSE; + wIndex_t inx, lin; + p0 = *pos; + Rotate( &p0, orig, -angle ); + p0.x -= orig.x; + p0.y -= orig.y; + d = dd; + for ( inx=0; segCnt>0; segPtr++,segCnt--,inx++) { + p1 = p0; + switch (segPtr->type) { + case SEG_STRTRK: + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + dd = LineDistance( &p1, segPtr->u.l.pos[0], segPtr->u.l.pos[1] ); + if ( segPtr->type == SEG_BENCH ) { + if ( dd < BenchGetWidth( segPtr->u.l.option )/2.0 ) + dd = 0.0; + } + break; + case SEG_CRVTRK: + case SEG_CRVLIN: + case SEG_FILCRCL: + dd = CircleDistance( &p1, segPtr->u.c.center, fabs(segPtr->u.c.radius), segPtr->u.c.a0, segPtr->u.c.a1 ); + break; + case SEG_POLY: + case SEG_FILPOLY: + ddd = 100000.0; + for (lin=0;lin<segPtr->u.p.cnt;lin++) { + pt = p0; + if (lin < segPtr->u.p.cnt-1 ) + ddd = LineDistance( &pt, segPtr->u.p.pts[lin], segPtr->u.p.pts[lin+1] ); + else + ddd = LineDistance( &pt, segPtr->u.p.pts[lin], segPtr->u.p.pts[0] ); + if ( ddd < dd ) { + dd = ddd; + p1 = pt; + } + } + break; + case SEG_TEXT: + /*GetTextBounds( segPtr->u.t.pos, angle+segPtr->u.t.angle, segPtr->u.t.string, segPtr->u.t.fontSize, &lo, &hi );*/ + GetTextBounds( zero, 0, segPtr->u.t.string, segPtr->u.t.fontSize, &lo, &hi ); + Rotate( &p0, segPtr->u.t.pos, segPtr->u.t.angle ); + p0.x -= segPtr->u.t.pos.x; + p0.y -= segPtr->u.t.pos.y; + if ( p0.x < hi.x && p0.y < hi.y ) { + DIST_T dx, dy; + hi.x /= 2.0; + hi.y /= 2.0; + p0.x -= hi.x; + p0.y -= hi.y; + dx = fabs(p0.x/hi.x); + dy = fabs(p0.y/hi.y); + if ( dx > dy ) + dd = dx; + else + dd = dy; + dd *= 0.25*mainD.scale; + /*printf( "dx=%0.4f dy=%0.4f dd=%0.3f\n", dx, dy, dd );*/ + } +/* + if ( p0.x >= lo.x && p0.x <= hi.x && + p0.y >= lo.y && p0.y <= hi.y ) { + p1.x = (lo.x+hi.x)/2.0; + p1.y = (lo.y+hi.y)/2.0; + dd = FindDistance( p0, p1 ); + } +*/ + break; + case SEG_JNTTRK: + dd = JointDistance( &p1, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.Scurve ); + break; + default: + dd = 100000.0; + } + if (dd < d) { + d = dd; + p2 = p1; + if (inx_ret) + *inx_ret = inx; + found = TRUE; + } + } + if (found) { + p2.x += orig.x; + p2.y += orig.y; + Rotate( &p2, orig, angle ); + *pos = p2; + } + return d; +} + + +EXPORT ANGLE_T GetAngleSegs( + wIndex_t segCnt, + trkSeg_p segPtr, + coOrd pos, + wIndex_t * segInxR ) +{ + wIndex_t inx; + ANGLE_T angle = 0.0; + coOrd p0; + DIST_T d, dd; + segProcData_t segProcData; + + DistanceSegs( zero, 0.0, segCnt, segPtr, &pos, &inx ); + segPtr += inx; + segProcData.getAngle.pos = pos; + switch ( segPtr->type ) { + case SEG_STRTRK: + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + StraightSegProc( SEGPROC_GETANGLE, segPtr, &segProcData ); + angle = segProcData.getAngle.angle; + break; + case SEG_CRVTRK: + case SEG_CRVLIN: + case SEG_FILCRCL: + CurveSegProc( SEGPROC_GETANGLE, segPtr, &segProcData ); + angle = segProcData.getAngle.angle; + break; + case SEG_JNTTRK: + JointSegProc( SEGPROC_GETANGLE, segPtr, &segProcData ); + angle = segProcData.getAngle.angle; + break; + case SEG_POLY: + case SEG_FILPOLY: + p0 = pos; + dd = LineDistance( &p0, segPtr->u.p.pts[segPtr->u.p.cnt-1], segPtr->u.p.pts[0] ); + angle = FindAngle( segPtr->u.p.pts[segPtr->u.p.cnt-1], segPtr->u.p.pts[0] ); + for ( inx=0; inx<segPtr->u.p.cnt-1; inx++ ) { + p0 = pos; + d = LineDistance( &p0, segPtr->u.p.pts[inx], segPtr->u.p.pts[inx+1] ); + if ( d < dd ) { + dd = d; + angle = FindAngle( segPtr->u.p.pts[inx], segPtr->u.p.pts[inx+1] ); + } + } + break; + case SEG_TEXT: + angle = segPtr->u.t.angle; + break; + default: + AbortProg( "GetAngleSegs(%d)", segPtr->type ); + } + if ( segInxR ) *segInxR = inx; + return angle; +} + +/**************************************************************************** + * + * Color + * + ****************************************************************************/ + +typedef struct { + FLOAT_T h, s, v; + } hsv_t; +static FLOAT_T max_s; +static FLOAT_T max_v; +static dynArr_t hsv_da; +#define hsv(N) DYNARR_N( hsv_t, hsv_da, N ) + +static void Hsv2rgb( + hsv_t hsv, + long *rgb ) +{ + int i; + FLOAT_T f, w, q, t, r=0, g=0, b=0; + + if (hsv.s == 0.0) + hsv.s = 0.000001; + + if (hsv.h == -1.0) + { + r = hsv.v; + g = hsv.v; + b = hsv.v; + } + else + { + if (hsv.h == 360.0) + hsv.h = 0.0; + hsv.h = hsv.h / 60.0; + i = (int) hsv.h; + f = hsv.h - i; + w = hsv.v * (1.0 - hsv.s); + q = hsv.v * (1.0 - (hsv.s * f)); + t = hsv.v * (1.0 - (hsv.s * (1.0 - f))); + + switch (i) + { + case 0: + r = hsv.v; + g = t; + b = w; + break; + case 1: + r = q; + g = hsv.v; + b = w; + break; + case 2: + r = w; + g = hsv.v; + b = t; + break; + case 3: + r = w; + g = q; + b = hsv.v; + break; + case 4: + r = t; + g = w; + b = hsv.v; + break; + case 5: + r = hsv.v; + g = w; + b = q; + break; + } + } + *rgb = wRGB( (int)(r*255), (int)(g*255), (int)(b*255) ); +} + + +static void Rgb2hsv( + long rgb, + hsv_t *hsv ) +{ + FLOAT_T r, g, b; + FLOAT_T max, min, delta; + + r = ((rgb>>16)&0xFF)/255.0; + g = ((rgb>>8)&0xFF)/255.0; + b = ((rgb)&0xFF)/255.0; + + max = r; + if (g > max) + max = g; + if (b > max) + max = b; + + min = r; + if (g < min) + min = g; + if (b < min) + min = b; + + hsv->v = max; + + if (max != 0.0) + hsv->s = (max - min) / max; + else + hsv->s = 0.0; + + if (hsv->s == 0.0) + hsv->h = -1.0; + else + { + delta = max - min; + + if (r == max) + hsv->h = (g - b) / delta; + else if (g == max) + hsv->h = 2.0 + (b - r) / delta; + else if (b == max) + hsv->h = 4.0 + (r - g) / delta; + + hsv->h = hsv->h * 60.0; + + if (hsv->h < 0.0) + hsv->h = hsv->h + 360; + } +} + + +static void Fill_hsv( + wIndex_t segCnt, + trkSeg_p segPtr, + hsv_t * hsvP ) +{ + int inx; + + max_s = 0.0; + max_v = 0.0; + for ( inx=0; inx<segCnt; inx++ ) { + Rgb2hsv( wDrawGetRGB(segPtr[inx].color), &hsvP[inx] ); + if ( hsvP[inx].h >= 0 ) { + if ( max_s < hsvP[inx].s ) + max_s = hsvP[inx].s; + if ( max_v < hsvP[inx].v ) + max_v = hsvP[inx].v; + } + } +} + +EXPORT void RecolorSegs( + wIndex_t cnt, + trkSeg_p segs, + wDrawColor color ) +{ + long rgb; + wIndex_t inx; + hsv_t hsv0; + FLOAT_T h, s, v; + + DYNARR_SET( hsv_t, hsv_da, cnt ); + Fill_hsv( cnt, segs, &hsv(0) ); + rgb = wDrawGetRGB( color ); + Rgb2hsv( rgb, &hsv0 ); + h = hsv0.h; + if ( max_s > 0.25 ) + s = hsv0.s/max_s; + else + s = 1.0; + if ( max_v > 0.25 ) + v = hsv0.v/max_v; + else + v = 1.0; + for ( inx=0; inx<cnt; inx++,segs++ ) { + hsv0 = hsv(inx); + if ( hsv0.h < 0 ) /* ignore black */ + continue; + hsv0.h = h; + hsv0.s *= s; + hsv0.v *= v; + Hsv2rgb( hsv0, &rgb ); + segs->color = wDrawFindColor( rgb ); + } +} + + + +/**************************************************************************** + * + * Input/Output + * + ****************************************************************************/ + + +static void AppendPath( signed char c ) +{ + if (pathPtr == NULL) { + pathMax = 100; + pathPtr = (signed char*)MyMalloc( pathMax ); + } else if (pathCnt >= pathMax) { + pathMax += 100; + pathPtr = (signed char*)MyRealloc( pathPtr, pathMax ); + } + pathPtr[pathCnt++] = c; +} + + +EXPORT BOOL_T ReadSegs( void ) +{ + char *cp, *cpp; + BOOL_T rc=FALSE; + trkSeg_p s; + trkEndPt_p e; + long rgb; + int i; + DIST_T elev0, elev1; + BOOL_T hasElev; + char type; + long option; + + descriptionOff = zero; + tempSpecial[0] = '\0'; + tempCustom[0] = '\0'; + DYNARR_RESET( trkSeg_t, tempSegs_da ); + DYNARR_RESET( trkEndPt_t, tempEndPts_da ); + pathCnt = 0; + while ( (cp = GetNextLine()) != NULL ) { + while (isspace(*cp)) cp++; + hasElev = FALSE; + if ( strncmp( cp, "END", 3 ) == 0 ) { + rc = TRUE; + break; + } + if ( *cp == '\n' || *cp == '#' ) { + continue; + } + type = *cp++; + hasElev = FALSE; + if ( *cp == '3' ) { + cp++; + hasElev = TRUE; + } + switch (type) { + case SEG_STRLIN: + case SEG_TBLEDGE: + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + s = &tempSegs(tempSegs_da.cnt-1); + s->type = type; + if ( !GetArgs( cp, hasElev?"lwpfpf":"lwpYpY", + &rgb, &s->width, &s->u.l.pos[0], &elev0, &s->u.l.pos[1], &elev1 ) ) { + rc = FALSE; + break; + } + s->u.l.option = 0; + s->color = wDrawFindColor( rgb ); + break; + case SEG_DIMLIN: + case SEG_BENCH: + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + s = &tempSegs(tempSegs_da.cnt-1); + s->type = type; + if ( !GetArgs( cp, hasElev?"lwpfpfl":"lwpYpYZ", + &rgb, &s->width, &s->u.l.pos[0], &elev0, &s->u.l.pos[1], &elev1, &option ) ) { + rc = FALSE; + break; + } + if ( type == SEG_DIMLIN ) + s->u.l.option = option; + else + s->u.l.option = BenchInputOption(option); + s->color = wDrawFindColor( rgb ); + break; + case SEG_CRVLIN: + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + s = &tempSegs(tempSegs_da.cnt-1); + s->type = SEG_CRVLIN; + if ( !GetArgs( cp, hasElev?"lwfpfff":"lwfpYff", + &rgb, &s->width, + &s->u.c.radius, + &s->u.c.center, + &elev0, + &s->u.c.a0, &s->u.c.a1 ) ) { + rc = FALSE; + break; + } + s->color = wDrawFindColor( rgb ); + break; + case SEG_STRTRK: + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + s = &tempSegs(tempSegs_da.cnt-1); + s->type = SEG_STRTRK; + if ( !GetArgs( cp, hasElev?"lwpfpf":"lwpYpY", + &rgb, &s->width, + &s->u.l.pos[0], &elev0, + &s->u.l.pos[1], &elev1 ) ) { + rc = FALSE; + break; + } + s->color = wDrawFindColor( rgb ); + break; + case SEG_CRVTRK: + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + s = &tempSegs(tempSegs_da.cnt-1); + s->type = SEG_CRVTRK; + if ( !GetArgs( cp, hasElev?"lwfpfff":"lwfpYff", + &rgb, &s->width, + &s->u.c.radius, + &s->u.c.center, + &elev0, + &s->u.c.a0, &s->u.c.a1 ) ) { + rc = FALSE; + break; + } + s->color = wDrawFindColor( rgb ); + break; + case SEG_JNTTRK: + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + s = &tempSegs(tempSegs_da.cnt-1); + s->type = SEG_JNTTRK; + if ( !GetArgs( cp, hasElev?"lwpffffffl":"lwpYfffffl", + &rgb, &s->width, + &s->u.j.pos, + &elev0, + &s->u.j.angle, + &s->u.j.l0, + &s->u.j.l1, + &s->u.j.R, + &s->u.j.L, + &option ) ) { + rc = FALSE; + break; + } + s->u.j.negate = ( option&1 )!=0; + s->u.j.flip = ( option&2 )!=0; + s->u.j.Scurve = ( option&4 )!=0; + s->color = wDrawFindColor( rgb ); + break; + case SEG_FILCRCL: + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + s = &tempSegs(tempSegs_da.cnt-1); + s->type = SEG_FILCRCL; + if ( !GetArgs( cp, hasElev?"lwfpf":"lwfpY", + &rgb, &s->width, + &s->u.c.radius, + &s->u.c.center, + &elev0 ) ) { + rc = FALSE; + /*??*/break; + } + s->u.c.a0 = 0.0; + s->u.c.a1 = 360.0; + s->color = wDrawFindColor( rgb ); + break; + case SEG_POLY: + case SEG_FILPOLY: + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + s = &tempSegs(tempSegs_da.cnt-1); + s->type = type; + if ( !GetArgs( cp, "lwd", + &rgb, &s->width, + &s->u.p.cnt ) ) { + rc = FALSE; + /*??*/break; + } + s->color = wDrawFindColor( rgb ); + s->u.p.pts = (coOrd*)MyMalloc( s->u.p.cnt * sizeof *(coOrd*)NULL ); + for ( i=0; i<s->u.p.cnt; i++ ) { + cp = GetNextLine(); + if (cp == NULL || !GetArgs( cp, hasElev?"pf":"pY", &s->u.p.pts[i], &elev0 ) ) { + rc = FALSE; + /*??*/break; + } + } + s->u.p.angle = 0.0; + s->u.p.orig = zero; + break; + case SEG_TEXT: + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + s = &tempSegs(tempSegs_da.cnt-1); + s->type = type; + s->u.t.fontP = NULL; + if ( !GetArgs( cp, "lpf0fq", &rgb, &s->u.t.pos, &s->u.t.angle, &s->u.t.fontSize, &s->u.t.string ) ) { + rc = FALSE; + /*??*/break; + } + s->color = wDrawFindColor( rgb ); + break; + case SEG_UNCEP: + case SEG_CONEP: + DYNARR_APPEND( trkEndPt_t, tempEndPts_da, 10 ); + e = &tempEndPts(tempEndPts_da.cnt-1); + if (type == SEG_CONEP) { + if ( !GetArgs( cp, "dc", &e->index, &cp ) ) { + rc = FALSE; + /*??*/break; + } + } else { + e->index = -1; + } + if ( !GetArgs( cp, "pfc", + &e->pos, &e->angle, &cp) ) { + rc = FALSE; + /*??*/break; + } + e->elev.option = 0; + e->elev.u.height = 0.0; + e->elev.doff = zero; + e->option = 0; + if ( cp != NULL ) { + if (paramVersion < 7) { + GetArgs( cp, "dfp", &e->elev.option, &e->elev.u.height, &e->elev.doff, &cp ); + /*??*/break; + } + GetArgs( cp, "lpc", &option, &e->elev.doff, &cp ); + e->option = option >> 8; + e->elev.option = (int)(option&0xFF); + if ( (e->elev.option&ELEV_MASK) != ELEV_NONE ) { + switch (e->elev.option&ELEV_MASK) { + case ELEV_DEF: + GetArgs( cp, "fc", &e->elev.u.height, &cp ); + break; + case ELEV_STATION: + GetArgs( cp, "qc", &e->elev.u.name, &cp ); + /*??*/break; + default: + ; + } + } + } + break; + case SEG_PATH: + while (isspace(*cp)) cp++; + if (*cp == '\"') cp++; + while ( *cp != '\"') AppendPath((signed char)*cp++); + AppendPath(0); + cp++; + while (1) { + i = (int)strtol(cp, &cpp, 10); + if (cp == cpp) + /*??*/break; + cp = cpp; + AppendPath( (signed char)i ); + } + AppendPath( 0 ); + AppendPath( 0 ); + break; + case SEG_SPEC: + strncpy( tempSpecial, cp+1, sizeof tempSpecial - 1 ); + break; + case SEG_CUST: + strncpy( tempCustom, cp+1, sizeof tempCustom - 1 ); + break; + case SEG_DOFF: + if ( !GetArgs( cp, "ff", &descriptionOff.x, &descriptionOff.y ) ) { + rc = FALSE; + /*??*/break; + } + break; + default: + break; + } + } + AppendPath( 0 ); + +#ifdef LATER + if ( logTable(log_readTracks).level >= 4 ) { + for (s=&tempSegs(0); s<&tempSegs(tempSegs_da.cnt); s++) { + switch (s->type) { + case SEG_STRTRK: + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + LogPrintf( "seg[%d] = %c [%0.3f %0.3f] [%0.3f %0.3f]\n", + tempSegs_da.cnt, s->type, + s->u.l.pos[0].x, s->u.l.pos[0].y, + s->u.l.pos[1].x, s->u.l.pos[1].y ); + break; + case SEG_CRVTRK: + case SEG_CRVLIN: + LogPrintf( "seg[%d] = %c R=%0.3f A0=%0.3f A1=%0.3f [%0.3f %0.3f]\n", + tempSegs_da.cnt, s->type, + s->u.c.radius, + s->u.c.center.x, s->u.c.center.y, + s->u.c.a0, s->u.c.a1 ); + break; + case SEG_JNTTRK: + LogPrintf( "seg[%d] = %c\n", + tempSegs_da.cnt, s->type ); + break; + } + } + } +#endif + return rc; +} + + +EXPORT BOOL_T WriteSegs( + FILE * f, + wIndex_t segCnt, + trkSeg_p segs ) +{ + int i, j; + BOOL_T rc = TRUE; + long option; + + for ( i=0; i<segCnt; i++ ) { + switch ( segs[i].type ) { + case SEG_STRTRK: + rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f %0.6f\n", + segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, + segs[i].u.l.pos[0].x, segs[i].u.l.pos[0].y, + segs[i].u.l.pos[1].x, segs[i].u.l.pos[1].y ) > 0; + break; + case SEG_STRLIN: + case SEG_TBLEDGE: + rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f 0 %0.6f %0.6f 0\n", + segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, + segs[i].u.l.pos[0].x, segs[i].u.l.pos[0].y, + segs[i].u.l.pos[1].x, segs[i].u.l.pos[1].y ) > 0; + break; + case SEG_DIMLIN: + rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f 0 %0.6f %0.6f 0 %ld\n", + segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, + segs[i].u.l.pos[0].x, segs[i].u.l.pos[0].y, + segs[i].u.l.pos[1].x, segs[i].u.l.pos[1].y, + segs[i].u.l.option ) > 0; + break; + case SEG_BENCH: + rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f 0 %0.6f %0.6f 0 %ld\n", + segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, + segs[i].u.l.pos[0].x, segs[i].u.l.pos[0].y, + segs[i].u.l.pos[1].x, segs[i].u.l.pos[1].y, + BenchOutputOption(segs[i].u.l.option) ) > 0; + break; + case SEG_CRVTRK: + rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f\n", + segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, + segs[i].u.c.radius, + segs[i].u.c.center.x, segs[i].u.c.center.y, + segs[i].u.c.a0, segs[i].u.c.a1 ) > 0; + break; + case SEG_JNTTRK: + option = (segs[i].u.j.negate?1:0) + (segs[i].u.j.flip?2:0) + (segs[i].u.j.Scurve?4:0); + rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %ld\n", + segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, + segs[i].u.j.pos.x, segs[i].u.j.pos.y, + segs[i].u.j.angle, + segs[i].u.j.l0, + segs[i].u.j.l1, + segs[i].u.j.R, + segs[i].u.j.L, + option )>0; + break; + case SEG_CRVLIN: + rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f %0.6f 0 %0.6f %0.6f\n", + segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, + segs[i].u.c.radius, + segs[i].u.c.center.x, segs[i].u.c.center.y, + segs[i].u.c.a0, segs[i].u.c.a1 ) > 0; + break; + case SEG_FILCRCL: + rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f %0.6f 0\n", + segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, + segs[i].u.c.radius, + segs[i].u.c.center.x, segs[i].u.c.center.y ) > 0; + break; + case SEG_POLY: + case SEG_FILPOLY: + rc &= fprintf( f, "\t%c3 %ld %0.6f %d\n", + segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, + segs[i].u.p.cnt ) > 0; + for ( j=0; j<segs[i].u.p.cnt; j++ ) + rc &= fprintf( f, "\t\t%0.6f %0.6f 0\n", + segs[i].u.p.pts[j].x, segs[i].u.p.pts[j].y ) > 0; + break; + case SEG_TEXT: /* 0pf0fq */ + rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f 0 %0.6f \"%s\"\n", + segs[i].type, wDrawGetRGB(segs[i].color), + segs[i].u.t.pos.x, segs[i].u.t.pos.y, segs[i].u.t.angle, + segs[i].u.t.fontSize, PutTitle(segs[i].u.t.string) ) > 0; + break; + } + } + rc &= fprintf( f, "\tEND\n" )>0; + return rc; +} + + +EXPORT void SegProc( + segProc_e cmd, + trkSeg_p segPtr, + segProcData_p data ) +{ + switch (segPtr->type) { + case SEG_STRTRK: + StraightSegProc( cmd, segPtr, data ); + break; + case SEG_CRVTRK: + CurveSegProc( cmd, segPtr, data ); + break; + case SEG_JNTTRK: + JointSegProc( cmd, segPtr, data ); + break; + default: + AbortProg( "SegProg( %d )", segPtr->type ); + break; + } +} + + +/* + * Draw Segs + */ + +EXPORT void DrawDimLine( + drawCmd_p d, + coOrd p0, + coOrd p1, + char * dimP, + wFontSize_t fs, + FLOAT_T middle, + wDrawWidth width, + wDrawColor color, + long option ) +{ + ANGLE_T a0, a1; + wFont_p fp; + coOrd size, p, pc; + DIST_T dist, dist1, fx, fy; + POS_T x, y; + coOrd textsize; + + if ( middle < 0.0 ) middle = 0.0; + if ( middle > 1.0 ) middle = 1.0; + a0 = FindAngle( p0, p1 ); + dist = fs/144.0; + + if ( ( option & 0x10 ) == 0 ) { + Translate( &p, p0, a0-45, dist ); + DrawLine( d, p0, p, 0, color ); + Translate( &p, p0, a0+45, dist ); + DrawLine( d, p0, p, 0, color ); + } + if ( ( option & 0x20 ) == 0 ) { + Translate( &p, p1, a0-135, dist ); + DrawLine( d, p1, p, 0, color ); + Translate( &p, p1, a0+135, dist ); + DrawLine( d, p1, p, 0, color ); + } + + if ( fs < 2*d->scale ) { + DrawLine( d, p0, p1, 0, color ); + return; + } + fp = wStandardFont( (option&0x01)?F_TIMES:F_HELV, FALSE, FALSE ); + dist = FindDistance( p0, p1 ); + DrawTextSize( &mainD, dimP, fp, fs, TRUE, &textsize ); + size.x = textsize.x/2.0; + size.y = textsize.y/2.0; + dist1 = FindDistance( zero, size ); + if ( dist <= dist1*2 ) { + DrawLine( d, p0, p1, 0, color ); + return; + } + a1 = FindAngle( zero, size ); + p.x = p0.x+(p1.x-p0.x)*middle; + p.y = p0.y+(p1.y-p0.y)*middle; + pc = p; + p.x -= size.x; + p.y -= size.y; + fx = fy = 1; + if (a0>180) { + a0 = a0-180; + fx = fy = -1; + } + if (a0>90) { + a0 = 180-a0; + fy *= -1; + } + if (a0>a1) { + x = size.x; + y = x * tan(D2R(90-a0)); + } else { + y = size.y; + x = y * tan(D2R(a0)); + } + DrawString( d, p, 0.0, dimP, fp, fs, color ); + p = pc; + p.x -= fx*x; + p.y -= fy*y; + DrawLine( d, p0, p, 0, color ); + p = pc; + p.x += fx*x; + p.y += fy*y; + DrawLine( d, p, p1, 0, color ); +} + +EXPORT void DrawSegsO( + drawCmd_p d, + track_p trk, + coOrd orig, + ANGLE_T angle, + trkSeg_p segPtr, + wIndex_t segCnt, + DIST_T trackGauge, + wDrawColor color, + long options ) +{ + wIndex_t i, j; + coOrd p0, p1, c; + ANGLE_T a0; + wDrawColor color1, color2; + DIST_T factor = d->dpi/d->scale; + trkSeg_p tempPtr; + static dynArr_t tempPts_da; +#define tempPts(N) DYNARR_N( coOrd, tempPts_da, N ) + long option; + wFontSize_t fs; + + for (i=0; i<segCnt; i++,segPtr++ ) { + if (color == wDrawColorBlack) { + color1 = segPtr->color; + color2 = wDrawColorBlack; + } else { + color1 = color2 = color; + } + if ( (options&DTS_TIES)!=0 ) { + if ( segPtr->color == wDrawColorWhite ) + continue; + switch (segPtr->type) { + case SEG_STRTRK: + REORIGIN( p0, segPtr->u.l.pos[0], angle, orig ) + REORIGIN( p1, segPtr->u.l.pos[1], angle, orig ) + DrawStraightTies( d, trk, p0, p1, color ); + break; + case SEG_CRVTRK: + a0 = NormalizeAngle(segPtr->u.c.a0 + angle); + REORIGIN( c, segPtr->u.c.center, angle, orig ); + DrawCurvedTies( d, trk, c, fabs(segPtr->u.c.radius), a0, segPtr->u.c.a1, color ); + break; + case SEG_JNTTRK: + REORIGIN( p0, segPtr->u.j.pos, angle, orig ); + DrawJointTrack( d, p0, NormalizeAngle(segPtr->u.j.angle+angle), segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.flip, segPtr->u.j.Scurve, trk, -1, -1, trackGauge, color1, options ); + break; + } + continue; + } + switch (segPtr->type) { + case SEG_STRTRK: + case SEG_CRVTRK: + case SEG_JNTTRK: + case SEG_TEXT: + break; + default: + if (d->options&DC_QUICK) + return; + if ((d->options&DC_SIMPLE) != 0 && + trackGauge != 0.0) + return; + } + switch (segPtr->type) { + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + case SEG_STRTRK: + REORIGIN( p0, segPtr->u.l.pos[0], angle, orig ) + REORIGIN( p1, segPtr->u.l.pos[1], angle, orig ) + switch (segPtr->type) { + case SEG_STRTRK: + if (color1 == wDrawColorBlack) + color1 = normalColor; + if ( segPtr->color == wDrawColorWhite ) + break; + DrawStraightTrack( d, + p0, p1, + FindAngle(p0, p1 ), + NULL, trackGauge, color1, options ); + break; + case SEG_STRLIN: + DrawLine( d, p0, p1, (wDrawWidth)floor(segPtr->width*factor+0.5), color1 ); + break; + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + if ( (d->options&DC_GROUP) || + (segPtr->type == SEG_DIMLIN && d->funcs == &tempSegDrawFuncs) ) { + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + tempPtr = &tempSegs(tempSegs_da.cnt-1); + memcpy( tempPtr, segPtr, sizeof segPtr[0] ); + tempPtr->u.l.pos[0] = p0; + tempPtr->u.l.pos[1] = p1; + } else { + switch ( segPtr->type ) { + case SEG_DIMLIN: + fs = descriptionFontSize*4; + option = segPtr->u.l.option; + fs /= (option==0?8:option==1?4:option==2?2:1); + if ( fs < 2 ) + fs = 2; + DrawDimLine( d, p0, p1, FormatDistance(FindDistance(p0,p1)), fs, 0.5, 0, color, option & 0x00 ); + break; + case SEG_BENCH: + DrawBench( d, p0, p1, color1, color2, options, segPtr->u.l.option ); + break; + case SEG_TBLEDGE: + DrawLine( d, p0, p1, (wDrawWidth)floor(3.0/mainD.dpi*d->dpi+0.5) , color ); + break; + } + } + break; + } + break; + case SEG_CRVLIN: + case SEG_CRVTRK: + a0 = NormalizeAngle(segPtr->u.c.a0 + angle); + REORIGIN( c, segPtr->u.c.center, angle, orig ); + if (segPtr->type == SEG_CRVTRK) { + if (color1 == wDrawColorBlack) + color1 = normalColor; + if ( segPtr->color == wDrawColorWhite ) + break; + p0.x = p0.y = p1.x = p1.y = 0; + DrawCurvedTrack( d, + c, + fabs(segPtr->u.c.radius), + a0, segPtr->u.c.a1, + p0, p1, + NULL, trackGauge, color1, options ); + } else { + DrawArc( d, c, fabs(segPtr->u.c.radius), a0, segPtr->u.c.a1, + FALSE, (wDrawWidth)floor(segPtr->width*factor+0.5), color1 ); + } + break; + case SEG_JNTTRK: + REORIGIN( p0, segPtr->u.j.pos, angle, orig ); + DrawJointTrack( d, p0, NormalizeAngle(segPtr->u.j.angle+angle), segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.flip, segPtr->u.j.Scurve, NULL, -1, -1, trackGauge, color1, options ); + break; + case SEG_TEXT: + REORIGIN( p0, segPtr->u.t.pos, angle, orig ) + DrawString( d, p0, NormalizeAngle(angle+segPtr->u.t.angle), segPtr->u.t.string, segPtr->u.t.fontP, segPtr->u.t.fontSize, color1 ); + break; + case SEG_FILPOLY: + if ( (d->options&DC_GROUP) == 0 && + d->funcs != &tempSegDrawFuncs ) { + /* Note: if we call tempSegDrawFillPoly we get a nasty bug + /+ because we don't make a private copy of p.pts */ + DYNARR_SET( coOrd, tempPts_da, segPtr->u.p.cnt ); + for ( j=0; j<segPtr->u.p.cnt; j++ ) { + REORIGIN( tempPts(j), segPtr->u.p.pts[j], angle, orig ) + } + DrawFillPoly( d, segPtr->u.p.cnt, &tempPts(0), color1 ); + break; + } /* else fall thru */ + case SEG_POLY: + if ( (d->options&DC_GROUP) ) { + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + tempPtr = &tempSegs(tempSegs_da.cnt-1); + memcpy( tempPtr, segPtr, sizeof segPtr[0] ); + tempPtr->u.p.orig = orig; + tempPtr->u.p.angle = angle; + break; + } + REORIGIN( p0, segPtr->u.p.pts[0], angle, orig ) + c = p0; + for (j=1; j<segPtr->u.p.cnt; j++) { + REORIGIN( p1, segPtr->u.p.pts[j], angle, orig ); + DrawLine( d, p0, p1, (wDrawWidth)floor(segPtr->width*factor+0.5), color1 ); + p0 = p1; + } + DrawLine( d, p0, c, (wDrawWidth)floor(segPtr->width*factor+0.5), color1 ); + break; + case SEG_FILCRCL: + REORIGIN( c, segPtr->u.c.center, angle, orig ) + if ( (d->options&DC_GROUP) != 0 || + d->funcs != &tempSegDrawFuncs ) { + DrawFillCircle( d, c, fabs(segPtr->u.c.radius), color1 ); + } else { + DrawArc( d, c, fabs(segPtr->u.c.radius), 0, 360, + FALSE, (wDrawWidth)0, color1 ); + } + break; + } + } +} + + + +EXPORT void DrawSegs( + drawCmd_p d, + coOrd orig, + ANGLE_T angle, + trkSeg_p segPtr, + wIndex_t segCnt, + DIST_T trackGauge, + wDrawColor color ) +{ + DrawSegsO( d, NULL, orig, angle, segPtr, segCnt, trackGauge, color, 0 ); +} + + diff --git a/app/bin/tstraigh.c b/app/bin/tstraigh.c new file mode 100644 index 0000000..0f5f273 --- /dev/null +++ b/app/bin/tstraigh.c @@ -0,0 +1,806 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/tstraigh.c,v 1.2 2008-01-20 23:29:15 mni77 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 "track.h" +#include "cstraigh.h" +#include "i18n.h" + +/******************************************************************************* + * + * STRAIGHT + * + */ + +static TRKTYP_T T_STRAIGHT = -1; + + +/**************************************** + * + * UTILITIES + * + */ + + +void AdjustStraightEndPt( track_p t, EPINX_T inx, coOrd pos ) +{ + if (GetTrkType(t) != T_STRAIGHT) { + AbortProg( "AdjustLIneEndPt( %d, %d ) not on STRAIGHT %d\n", + GetTrkIndex(t), inx, GetTrkType(t) ); + return; + } + UndoModify( t ); +#ifdef VERBOSE +lprintf("adjustStraightEndPt T%d[%d] p=[%0.3f %0.3f]\n", + GetTrkIndex(t), inx, pos.x, pos.y ); +#endif + SetTrkEndPoint( t, inx, pos, GetTrkEndAngle(t,inx)); + ComputeBoundingBox( t ); + CheckTrackLength( t ); +} + +/**************************************** + * + * GENERIC FUNCTIONS + * + */ + +static struct { + coOrd endPt[2]; + DIST_T elev[2]; + FLOAT_T length; + ANGLE_T angle; + FLOAT_T grade; + descPivot_t pivot; + LAYER_T layerNumber; + } strData; +typedef enum { E0, Z0, E1, Z1, LN, AN, GR, PV, LY } strDesc_e; +static descData_t strDesc[] = { +/*E0*/ { DESC_POS, N_("End Pt 1: X"), &strData.endPt[0] }, +/*Z0*/ { DESC_DIM, N_("Z"), &strData.elev[0] }, +/*E1*/ { DESC_POS, N_("End Pt 2: X"), &strData.endPt[1] }, +/*Z1*/ { DESC_DIM, N_("Z"), &strData.elev[1] }, +/*LN*/ { DESC_DIM, N_("Length"), &strData.length }, +/*AN*/ { DESC_ANGLE, N_("Angle"), &strData.angle }, +/*GR*/ { DESC_FLOAT, N_("Grade"), &strData.grade }, +/*PV*/ { DESC_PIVOT, N_("Pivot"), &strData.pivot }, +/*LY*/ { DESC_LAYER, N_("Layer"), &strData.layerNumber }, + { DESC_NULL } }; + + + +EXPORT BOOL_T UpdateDescStraight( + int inx, + int e0, + int e1, + int ln, + int an, + descData_p desc, + long pivot ) +{ + coOrd mid; + if ( inx == e0 || inx == e1 ) { + *(DIST_T*)desc[ln].valueP = FindDistance( *(coOrd*)desc[e0].valueP, *(coOrd*)desc[e1].valueP ); + *(ANGLE_T*)desc[an].valueP = FindAngle( *(coOrd*)desc[e0].valueP, *(coOrd*)desc[e1].valueP ); + if ( inx == e0 ) + desc[e1].mode |= DESC_CHANGE; + else + desc[e0].mode |= DESC_CHANGE; + desc[ln].mode |= DESC_CHANGE; + desc[an].mode |= DESC_CHANGE; + } else if ( inx == ln || inx == an ) { + if ( inx == ln && *(DIST_T*)desc[ln].valueP <= minLength ) { + ErrorMessage( MSG_OBJECT_TOO_SHORT ); + *(DIST_T*)desc[ln].valueP = FindDistance( *(coOrd*)desc[e0].valueP, *(coOrd*)desc[e1].valueP ); + desc[ln].mode |= DESC_CHANGE; + return FALSE; + } + switch (pivot) { + case DESC_PIVOT_FIRST: + Translate( (coOrd*)desc[e1].valueP, *(coOrd*)desc[e0].valueP, *(ANGLE_T*)desc[an].valueP, *(DIST_T*)desc[ln].valueP ); + desc[e1].mode |= DESC_CHANGE; + break; + case DESC_PIVOT_SECOND: + Translate( (coOrd*)desc[e0].valueP, *(coOrd*)desc[e1].valueP, *(ANGLE_T*)desc[an].valueP+180.0, *(DIST_T*)desc[ln].valueP ); + desc[e0].mode |= DESC_CHANGE; + break; + case DESC_PIVOT_MID: + mid.x = (((coOrd*)desc[e0].valueP)->x+((coOrd*)desc[e1].valueP)->x)/2.0; + mid.y = (((coOrd*)desc[e0].valueP)->y+((coOrd*)desc[e1].valueP)->y)/2.0; + Translate( (coOrd*)desc[e0].valueP, mid, *(ANGLE_T*)desc[an].valueP+180.0, *(DIST_T*)desc[ln].valueP/2.0 ); + Translate( (coOrd*)desc[e1].valueP, mid, *(ANGLE_T*)desc[an].valueP, *(DIST_T*)desc[ln].valueP/2.0 ); + desc[e0].mode |= DESC_CHANGE; + desc[e1].mode |= DESC_CHANGE; + break; + default: + break; + } + } else { + return FALSE; + } + return TRUE; +} + + +static void UpdateStraight( track_p trk, int inx, descData_p descUpd, BOOL_T final ) +{ + EPINX_T ep; + switch ( inx ) { + case E0: + case E1: + case LN: + case AN: + if ( ! UpdateDescStraight( inx, E0, E1, LN, AN, strDesc, strData.pivot ) ) + return; + break; + case Z0: + case Z1: + ep = (inx==Z0?0:1); + UpdateTrkEndElev( trk, ep, GetTrkEndElevUnmaskedMode(trk,ep), strData.elev[ep], NULL ); + ComputeElev( trk, 1-ep, FALSE, &strData.elev[1-ep], NULL ); + if ( strData.length > minLength ) + strData.grade = fabs( (strData.elev[0]-strData.elev[1])/strData.length )*100.0; + else + strData.grade = 0.0; + strDesc[GR].mode |= DESC_CHANGE; + strDesc[inx==Z0?Z1:Z0].mode |= DESC_CHANGE; + /*return;*/ + break; +#ifdef LATER + update = UpdateDescStraight( 0, &strDesc[E0], &strDesc[E1], &strDesc[LN], &strDesc[AN], strData.pivot ); + break; + case E1: + update = UpdateDescStraight( 1, &strDesc[E0], &strDesc[E1], &strDesc[LN], &strDesc[AN], strData.pivot ); + break; + case E1: + strData.length = FindDistance( strData.endPt[0], strData.endPt[1] ); + strData.angle = FindAngle( strData.endPt[0], strData.endPt[1] ); + strDesc[1-inx].mode |= DESC_CHANGE; + strDesc[LN].mode |= DESC_CHANGE; + strDesc[AN].mode |= DESC_CHANGE; + break; + case LN: + if ( strData.length < minLength ) { + ErrorMessage( ); + strData.length = FindDistance( strData.endPt[0], strData.endPt[1] ); + strDesc[LN].mode |= DESC_CHANGE; + break; + } + case AN: + switch (strData.pivot) { + case DESC_PIVOT_FIRST: + Translate( &strData.endPt[1], strData.endPt[0], strData.angle, strData.length ); + strDesc[E1].mode |= DESC_CHANGE; + break; + case DESC_PIVOT_SECOND: + Translate( &strData.endPt[0], strData.endPt[1], strData.angle+180.0, strData.length ); + strDesc[E0].mode |= DESC_CHANGE; + break; + case DESC_PIVOT_MID: + mid.x = (strData.endPt[0].x+strData.endPt[1].x)/2.0; + mid.y = (strData.endPt[0].y+strData.endPt[1].y)/2.0; + Translate( &strData.endPt[0], mid, strData.angle+180.0, strData.length/2.0 ); + Translate( &strData.endPt[1], mid, strData.angle, strData.length/2.0 ); + strDesc[E0].mode |= DESC_CHANGE; + strDesc[E1].mode |= DESC_CHANGE; + break; + default: + break; + } + break; +#endif + case LY: + SetTrkLayer( trk, strData.layerNumber); + break; + default: + return; + } + UndrawNewTrack( trk ); + if ( GetTrkEndTrk(trk,0) == NULL ) + SetTrkEndPoint( trk, 0, strData.endPt[0], NormalizeAngle(strData.angle+180.0) ); + if ( GetTrkEndTrk(trk,1) == NULL ) + SetTrkEndPoint( trk, 1, strData.endPt[1], strData.angle ); + ComputeBoundingBox( trk ); + DrawNewTrack( trk ); +} + +static void DescribeStraight( track_p trk, char * str, CSIZE_T len ) +{ + int fix0, fix1; + sprintf( str, _("Straight Track(%d): Layer=%d Length=%s EP=[%0.3f,%0.3f A%0.3f] [%0.3f,%0.3f A%0.3f]"), GetTrkIndex(trk), + GetTrkLayer(trk)+1, + FormatDistance(FindDistance( GetTrkEndPos(trk,0), GetTrkEndPos(trk,1) )), + GetTrkEndPosXY(trk,0), GetTrkEndAngle(trk,0), + GetTrkEndPosXY(trk,1), GetTrkEndAngle(trk,1) ); + fix0 = GetTrkEndTrk(trk,0)!=NULL; + fix1 = GetTrkEndTrk(trk,1)!=NULL; + strData.endPt[0] = GetTrkEndPos(trk,0); + strData.endPt[1] = GetTrkEndPos(trk,1); + ComputeElev( trk, 0, FALSE, &strData.elev[0], NULL ); + ComputeElev( trk, 1, FALSE, &strData.elev[1], NULL ); + strData.length = FindDistance( strData.endPt[0], strData.endPt[1] ); + strData.layerNumber = GetTrkLayer(trk); + if ( strData.length > minLength ) + strData.grade = fabs( (strData.elev[0]-strData.elev[1])/strData.length )*100.0; + else + strData.grade = 0.0; + strData.angle = FindAngle( strData.endPt[0], strData.endPt[1] ); + strDesc[E0].mode = + strDesc[E1].mode = (fix0|fix1)?DESC_RO:0; + strDesc[Z0].mode = (EndPtIsDefinedElev(trk,0)?0:DESC_RO)|DESC_NOREDRAW; + strDesc[Z1].mode = (EndPtIsDefinedElev(trk,1)?0:DESC_RO)|DESC_NOREDRAW; + strDesc[GR].mode = DESC_RO; + strDesc[LN].mode = (fix0&fix1)?DESC_RO:0; + strDesc[AN].mode = (fix0|fix1)?DESC_RO:0; + strDesc[PV].mode = (fix0|fix1)?DESC_IGNORE:0; + strDesc[LY].mode = DESC_NOREDRAW; + strData.pivot = (fix0&fix1)?DESC_PIVOT_NONE: + fix0?DESC_PIVOT_FIRST: + fix1?DESC_PIVOT_SECOND: + DESC_PIVOT_MID; + DoDescribe( _("Straight Track"), trk, strDesc, UpdateStraight ); +} + +static DIST_T DistanceStraight( track_p t, coOrd * p ) +{ + return LineDistance( p, GetTrkEndPos(t,0), GetTrkEndPos(t,1) ); +} + +static void DrawStraight( track_p t, drawCmd_p d, wDrawColor color ) +{ + long widthOptions = DTS_LEFT|DTS_RIGHT|DTS_TIES; + if (GetTrkWidth(t) == 2) + widthOptions |= DTS_THICK2; + if (GetTrkWidth(t) == 3) + widthOptions |= DTS_THICK3; + DrawStraightTrack( d, GetTrkEndPos(t,0), GetTrkEndPos(t,1), + GetTrkEndAngle(t,0), + t, GetTrkGauge(t), color, widthOptions ); + if ( (d->funcs->options & wDrawOptTemp) == 0 && (d->options & DC_QUICK) == 0 ) { + DrawEndPt( d, t, 0, color ); + DrawEndPt( d, t, 1, color ); + } +} + +static void DeleteStraight( track_p t ) +{ +} + +static BOOL_T WriteStraight( track_p t, FILE * f ) +{ + BOOL_T rc = TRUE; + rc &= fprintf(f, "STRAIGHT %d %d %ld 0 0 %s %d\n", + GetTrkIndex(t), GetTrkLayer(t), (long)GetTrkWidth(t), + GetTrkScaleName(t), GetTrkVisible(t) )>0; + rc &= WriteEndPt( f, t, 0 ); + rc &= WriteEndPt( f, t, 1 ); + rc &= fprintf(f, "\tEND\n" )>0; + return rc; +} + +static void ReadStraight( char * line ) +{ + track_p trk; + wIndex_t index; + BOOL_T visible; + char scale[10]; + wIndex_t layer; + long options; + + if ( !GetArgs( line+8, paramVersion<3?"dXZsd":"dLl00sd", &index, &layer, &options, scale, &visible ) ) + return; + trk = NewTrack( index, T_STRAIGHT, 0, 0 ); + SetTrkScale( trk, LookupScale(scale) ); + SetTrkVisible(trk, visible); + SetTrkLayer(trk, layer); + SetTrkWidth( trk, (int)(options&3) ); + ReadSegs(); + SetEndPts( trk, 2 ); + ComputeBoundingBox( trk ); +} + +static void MoveStraight( track_p trk, coOrd orig ) +{ + ComputeBoundingBox( trk ); +} + +static void RotateStraight( track_p trk, coOrd orig, ANGLE_T angle ) +{ + ComputeBoundingBox( trk ); +} + +static void RescaleStraight( track_p trk, FLOAT_T ratio ) +{ +} + +static int AuditStraight( track_p trk, char * msg ) +{ + if (FindDistance( GetTrkEndPos(trk,0), GetTrkEndPos(trk,1) ) < 0.01) { + sprintf( msg, "T%d: short track\n", GetTrkIndex(trk) ); + return FALSE; + } else { + return TRUE; + } +} + + +static ANGLE_T GetAngleStraight( track_p trk, coOrd pos, EPINX_T *ep0, EPINX_T *ep1 ) +{ + if ( ep0 ) *ep0 = 0; + if ( ep1 ) *ep1 = 1; + return GetTrkEndAngle( trk, 0 ); +} + + +static BOOL_T SplitStraight( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, EPINX_T * ep0, EPINX_T * ep1 ) +{ + track_p trk1; + + trk1 = NewStraightTrack( GetTrkEndPos(trk,ep), pos ); + AdjustStraightEndPt( trk, ep, pos ); + *leftover = trk1; + *ep0 = 1; + *ep1 = 0; + return TRUE; +} + + +static BOOL_T TraverseStraight( traverseTrack_p trvTrk, DIST_T * distR ) +{ + coOrd pos[2]; + ANGLE_T angle0, angle; + DIST_T dist; + track_p trk = trvTrk->trk; + EPINX_T ep; + + pos[0] = GetTrkEndPos(trk,0); + pos[1] = GetTrkEndPos(trk,1); + angle0 = FindAngle( pos[0], pos[1] ); + angle = NormalizeAngle( angle0-trvTrk->angle ); + trvTrk->angle = angle0; + if ( angle < 270 && angle > 90 ) { + ep = 0; + trvTrk->angle = NormalizeAngle( trvTrk->angle + 180.0 ); + } else { + ep = 1; + } + dist = FindDistance( trvTrk->pos, pos[ep] ); + if ( dist > *distR ) { + Translate( &trvTrk->pos, pos[ep], NormalizeAngle(trvTrk->angle+180.0), dist-*distR ); + *distR = 0; + } else { + trvTrk->pos = pos[ep]; + *distR -= dist; + trvTrk->trk = GetTrkEndTrk( trk, ep ); + } + return TRUE; +} + + +static BOOL_T EnumerateStraight( track_p trk ) +{ + DIST_T d; + if (trk != NULL) { + d = FindDistance( GetTrkEndPos( trk, 0 ), GetTrkEndPos( trk, 1 ) ); + ScaleLengthIncrement( GetTrkScale(trk), d ); + } + return TRUE; +} + +static BOOL_T TrimStraight( track_p trk, EPINX_T ep, DIST_T dist ) +{ + DIST_T d; + ANGLE_T a; + coOrd p1, pos; + a = NormalizeAngle( GetTrkEndAngle(trk,ep) + 180.0 ); + Translate( &pos, GetTrkEndPos(trk,ep), a, dist ); + p1 = GetTrkEndPos( trk, 1-ep ); + d = FindDistance( pos, p1 ); + if (dist < FindDistance( GetTrkEndPos(trk,0), GetTrkEndPos(trk,1) ) && + d > minLength ) { + UndrawNewTrack( trk ); + AdjustStraightEndPt( trk, ep, pos ); + DrawNewTrack( trk ); + } else + DeleteTrack( trk, FALSE ); + return TRUE; +} + + +BOOL_T ExtendStraightToJoin( + track_p trk0, + EPINX_T ep0, + track_p trk1, + EPINX_T ep1 ) +{ + coOrd off; + ANGLE_T a; + track_p trk0x, trk1x, trk2; + EPINX_T ep0x=-1, ep1x=-1; + coOrd pos0, pos1; + ANGLE_T a0, a1, aa; + + a0 = GetTrkEndAngle( trk0, ep0 ); + a1 = GetTrkEndAngle( trk1, ep1 ); + a = NormalizeAngle( a0 - a1 + 180.0 + connectAngle/2.0 ); + pos0 = GetTrkEndPos( trk0, (GetTrkType(trk0) == T_STRAIGHT)?1-ep0:ep0 ); + off = pos1 = GetTrkEndPos( trk1, (GetTrkType(trk1) == T_STRAIGHT)?1-ep1:ep1 ); + Rotate( &off, pos0, -a0 ); + off.x -= pos0.x; + + if ( a >= connectAngle || + !IsClose( fabs(off.x) ) || + off.y-pos0.y <= connectDistance ) { + return FALSE; + } + + if ( GetTrkType(trk0) != T_STRAIGHT && + GetTrkType(trk1) != T_STRAIGHT ) { + aa = FindAngle( pos0, pos1 ); + aa = NormalizeAngle( aa-a0+connectAngle/2.0); + if (aa > connectAngle) + return FALSE; + } + UndoStart( _("Extending Straight Track"), "ExtendStraightToJoin( T%d[%d] T%d[%d] )", GetTrkIndex(trk0), ep0, GetTrkIndex(trk1), ep1 ); + UndoModify( trk0 ); + UndoModify( trk1 ); + trk2 = trk0x = trk1x = NULL; + if ( GetTrkType(trk0) == T_STRAIGHT ) { + pos0 = GetTrkEndPos( trk0, 1-ep0 ); + trk0x = GetTrkEndTrk( trk0, 1-ep0 ); + if (trk0x) { + ep0x = GetEndPtConnectedToMe( trk0x, trk0 ); + DisconnectTracks( trk0, 1-ep0, trk0x, ep0x ); + } + trk2 = trk0; + UndrawNewTrack( trk2 ); + } else { + trk0x = trk0; + ep0x = ep0; + DrawEndPt( &mainD, trk0, ep0, wDrawColorWhite ); + } + if ( GetTrkType(trk1) == T_STRAIGHT ) { + pos1 = GetTrkEndPos( trk1, 1-ep1 ); + trk1x = GetTrkEndTrk( trk1, 1-ep1 ); + if (trk1x) { + ep1x = GetEndPtConnectedToMe( trk1x, trk1 ); + DisconnectTracks( trk1, 1-ep1, trk1x, ep1x ); + } + if (trk2) { + DeleteTrack( trk1, TRUE ); + } else { + trk2 = trk1; + UndrawNewTrack( trk2 ); + } + } else { + trk1x = trk1; + ep1x = ep1; + DrawEndPt( &mainD, trk1, ep1, wDrawColorWhite ); + } + + if (trk2) { + SetTrkEndPoint( trk2, 0, pos0, NormalizeAngle(a0+180.0) ); + SetTrkEndPoint( trk2, 1, pos1, NormalizeAngle(a1+180.0) ); + ComputeBoundingBox( trk2 ); + } else { + trk2 = NewStraightTrack( pos0, pos1 ); + } + if (trk0x) { + ConnectTracks( trk2, 0, trk0x, ep0x ); + } + if (trk1x) { + ConnectTracks( trk2, 1, trk1x, ep1x ); + } + DrawNewTrack( trk2 ); + + return TRUE; +} + + +static STATUS_T ModifyStraight( track_p trk, wAction_t action, coOrd pos ) +{ + static EPINX_T ep; + static BOOL_T valid; + DIST_T d; + + switch ( action ) { + case C_DOWN: + ep = PickUnconnectedEndPoint( pos, trk ); + if (ep == -1) + return C_ERROR; + UndrawNewTrack( trk ); + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).width = 0; + tempSegs(0).u.l.pos[0] = GetTrkEndPos( trk, 1-ep ); + tempSegs_da.cnt = 1; + InfoMessage( _("Drag to change track length") ); + + case C_MOVE: + d = FindDistance( tempSegs(0).u.l.pos[0], pos ); + valid = TRUE; + if ( d <= minLength ) { + if (action == C_MOVE) + ErrorMessage( MSG_TRK_TOO_SHORT, _("Straight "), PutDim(fabs(minLength-d)) ); + valid = FALSE; + return C_CONTINUE; + } + Translate( &tempSegs(0).u.l.pos[1], tempSegs(0).u.l.pos[0], GetTrkEndAngle( trk, ep ), d ); + tempSegs_da.cnt = 1; + if (action == C_MOVE) + InfoMessage( _("Straight: Length=%s Angle=%0.3f"), + FormatDistance( d ), PutAngle( GetTrkEndAngle( trk, ep ) ) ); + MainRedraw(); + return C_CONTINUE; + + case C_UP: + if (valid) + AdjustStraightEndPt( trk, ep, tempSegs(0).u.l.pos[1] ); + tempSegs_da.cnt = 0; + DrawNewTrack( trk ); + MainRedraw(); + return C_TERMINATE; + + default: + ; + } + return C_ERROR; +} + + +static DIST_T GetLengthStraight( track_p trk ) +{ + return FindDistance( GetTrkEndPos(trk,0), GetTrkEndPos(trk,1) ); +} + + +static BOOL_T GetParamsStraight( int inx, track_p trk, coOrd pos, trackParams_t * params ) +{ + params->type = curveTypeStraight; + if ( inx == PARAMS_PARALLEL ) { + params->ep = 0; + } else { + params->ep = PickUnconnectedEndPoint( pos, trk ); + if (params->ep == -1) + return FALSE; + } + params->lineOrig = GetTrkEndPos(trk,1-params->ep); + params->lineEnd = GetTrkEndPos(trk,params->ep); + params->len = FindDistance( params->lineOrig, params->lineEnd ); + params->angle = GetTrkEndAngle(trk,params->ep); + params->arcR = 0.0; + return TRUE; +} + + +static BOOL_T MoveEndPtStraight( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d0 ) +{ + if ( NormalizeAngle( FindAngle( GetTrkEndPos(*trk,1-*ep), pos ) - + GetTrkEndAngle(*trk,*ep) + 0.5 ) > 1.0 ) { + ErrorMessage( MSG_MOVED_BEYOND_END_TRK ); + return FALSE; + } + Translate( &pos, pos, GetTrkEndAngle(*trk,*ep)+180, d0 ); + AdjustStraightEndPt( *trk, *ep, pos ); + return TRUE; +} + + +static BOOL_T QueryStraight( track_p trk, int query ) +{ + switch ( query ) { + case Q_CAN_PARALLEL: + case Q_CAN_MODIFYRADIUS: + case Q_CAN_GROUP: + case Q_ISTRACK: + return TRUE; + default: + return FALSE; + } +} + + +static void FlipStraight( + track_p trk, + coOrd orig, + ANGLE_T angle ) +{ + ComputeBoundingBox( trk ); +} + + +static BOOL_T MakeParallelStraight( + track_p trk, + coOrd pos, + DIST_T sep, + track_p * newTrkR, + coOrd * p0R, + coOrd * p1R ) +{ + ANGLE_T angle = GetTrkEndAngle(trk,1); + coOrd p0, p1; + if ( NormalizeAngle( FindAngle( GetTrkEndPos(trk,0), pos ) - GetTrkEndAngle(trk,1) ) < 180.0 ) + angle += 90; + else + angle -= 90; + Translate( &p0, GetTrkEndPos(trk,0), angle, sep ); + Translate( &p1, GetTrkEndPos(trk,1), angle, sep ); + if ( newTrkR ) { + *newTrkR = NewStraightTrack( p0, p1 ); + } else { + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).width = 0; + tempSegs_da.cnt = 1; + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).u.l.pos[0] = p0; + tempSegs(0).u.l.pos[1] = p1; + } + if ( p0R ) *p0R = p0; + if ( p1R ) *p1R = p1; + return TRUE; +} + + +static trackCmd_t straightCmds = { + "STRAIGHT", + DrawStraight, + DistanceStraight, + DescribeStraight, + DeleteStraight, + WriteStraight, + ReadStraight, + MoveStraight, + RotateStraight, + RescaleStraight, + AuditStraight, + GetAngleStraight, + SplitStraight, + TraverseStraight, + EnumerateStraight, + NULL, /* redraw */ + TrimStraight, + ExtendStraightToJoin, + ModifyStraight, + GetLengthStraight, + GetParamsStraight, + MoveEndPtStraight, + QueryStraight, + NULL, /* ungroup */ + FlipStraight, + NULL, + NULL, + NULL, + MakeParallelStraight }; + + +EXPORT void StraightSegProc( + segProc_e cmd, + trkSeg_p segPtr, + segProcData_p data ) +{ + ANGLE_T a0, a1, a2; + DIST_T d, d0, d1; + coOrd p0, p1; + + switch( cmd ) { + + case SEGPROC_TRAVERSE1: + a1 = FindAngle( segPtr->u.l.pos[0], segPtr->u.l.pos[1] ); + a2 = NormalizeAngle( data->traverse1.angle+a1 ); + data->traverse1.backwards = (a2 < 270 && a2 > 90 ); + data->traverse1.dist = FindDistance( segPtr->u.l.pos[data->traverse1.backwards?1:0], data->traverse1.pos ); + break; + + case SEGPROC_TRAVERSE2: + d = FindDistance( segPtr->u.l.pos[0], segPtr->u.l.pos[1] ); + if ( d >= data->traverse2.dist ) { + a1 = FindAngle( segPtr->u.l.pos[data->traverse2.segDir], segPtr->u.l.pos[1-data->traverse2.segDir] ); + Translate( &data->traverse2.pos, segPtr->u.l.pos[data->traverse2.segDir], a1, data->traverse2.dist ); + data->traverse2.dist = 0; + data->traverse2.angle = a1; + } else { + data->traverse2.dist -= d; + } + break; + + case SEGPROC_DRAWROADBEDSIDE: + d0 = FindDistance( segPtr->u.l.pos[0], segPtr->u.l.pos[1] ); + a0 = FindAngle( segPtr->u.l.pos[0], segPtr->u.l.pos[1] ); + d1 = d0*data->drawRoadbedSide.first/32.0; + Translate( &p0, segPtr->u.l.pos[0], a0, d1 ); + Translate( &p0, p0, a0+data->drawRoadbedSide.side, data->drawRoadbedSide.roadbedWidth/2.0 ); + d1 = d0*data->drawRoadbedSide.last/32.0; + Translate( &p1, segPtr->u.l.pos[0], a0, d1 ); + Translate( &p1, p1, a0+data->drawRoadbedSide.side, data->drawRoadbedSide.roadbedWidth/2.0 ); + REORIGIN1( p0, data->drawRoadbedSide.angle, data->drawRoadbedSide.orig ); + REORIGIN1( p1, data->drawRoadbedSide.angle, data->drawRoadbedSide.orig ); + DrawLine( data->drawRoadbedSide.d, p0, p1, data->drawRoadbedSide.rbw, data->drawRoadbedSide.color ); + break; + + case SEGPROC_DISTANCE: + data->distance.dd = LineDistance( &data->distance.pos1, segPtr->u.l.pos[0], segPtr->u.l.pos[1] ); + break; + + case SEGPROC_FLIP: + p0 = segPtr->u.l.pos[0]; + segPtr->u.l.pos[0] = segPtr->u.l.pos[1]; + segPtr->u.l.pos[1] = p0; + break; + + case SEGPROC_NEWTRACK: + data->newTrack.trk = NewStraightTrack( segPtr->u.l.pos[0], segPtr->u.l.pos[1] ); + data->newTrack.ep[0] = 0; + data->newTrack.ep[1] = 1; + break; + + case SEGPROC_LENGTH: + data->length.length = FindDistance( segPtr->u.l.pos[0], segPtr->u.l.pos[1] ); + break; + + case SEGPROC_SPLIT: + d = FindDistance( segPtr->u.l.pos[0], segPtr->u.l.pos[1] ); + data->split.length[0] = FindDistance( segPtr->u.l.pos[0], data->split.pos ); + if ( data->split.length[0] <= d ) { + data->split.length[1] = d-data->split.length[0]; + } else { + data->split.length[0] = d; + data->split.length[1] = 0.0; + } + Translate( &p0, segPtr->u.l.pos[0], FindAngle( segPtr->u.l.pos[0], segPtr->u.l.pos[1] ), data->split.length[0] ); + data->split.newSeg[0] = *segPtr; + data->split.newSeg[1] = *segPtr; + data->split.newSeg[0].u.l.pos[1] = data->split.newSeg[1].u.l.pos[0] = p0; + break; + + case SEGPROC_GETANGLE: + data->getAngle.angle = FindAngle( segPtr->u.l.pos[0], segPtr->u.l.pos[1] ); + break; + } +} + + +/**************************************** + * + * GRAPHICS EDITING + * + */ + + +track_p NewStraightTrack( coOrd p0, coOrd p1 ) +{ + track_p t; + ANGLE_T a; + t = NewTrack( 0, T_STRAIGHT, 2, 0 ); + SetTrkScale( t, curScaleInx ); + a = FindAngle( p1, p0 ); + SetTrkEndPoint( t, 0, p0, a ); + SetTrkEndPoint( t, 1, p1, NormalizeAngle( a+180.0 ) ); + ComputeBoundingBox( t ); + CheckTrackLength( t ); + return t; +} + + + + +void InitTrkStraight( void ) +{ + T_STRAIGHT = InitObject( &straightCmds ); +} diff --git a/app/bin/utility.c b/app/bin/utility.c new file mode 100644 index 0000000..9708ac4 --- /dev/null +++ b/app/bin/utility.c @@ -0,0 +1,639 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/utility.c,v 1.2 2009-05-25 18:11:03 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 <stdlib.h> +#include <stdio.h> +#ifndef WINDOWS +#include <unistd.h> +#endif +#include <math.h> +#include "common.h" +#include "utility.h" + +/***************************************************************************** + * + * VARIABLES + * + */ + +double radiusGranularity = 1.0/8.0; +DEBUGF_T debugIntersection = 0; + +#define CLOSE (1.0) + +/***************************************************************************** + * + * UTLITY FUNCTIONS + * + */ + + + +#ifndef min +double max( double a, double b ) +{ + if (a>b) return a; + return b; +} + + + +double min( double a, double b ) +{ + if (a<b) return a; + return b; +} +#endif + + + +double FindDistance( coOrd p0, coOrd p1 ) +{ + double dx = p1.x-p0.x, dy = p1.y-p0.y; + return sqrt( dx*dx + dy*dy ); +} + + + +double NormalizeAngle( double a ) +{ + while (a<0.0) a += 360.0; + while (a>=360.0) a -= 360.0; + if ( a > 360.0-EPSILON ) a = 0.0; + return a; +} + + + +int IsAligned( double a1, double a2 ) +{ + a1 = NormalizeAngle( a1 - a2 + 90.0 ); + return ( a1 < 180 ); +} + + +double D2R( double D ) +{ + D = NormalizeAngle(D); + if (D >= 180.0) D = D - 360.0; + return D * (M_PI*2) / 360.0; +} + + + +double R2D( double R ) +{ + return NormalizeAngle( R * 360.0 / (M_PI*2) ); +} + + + +void Rotate( coOrd *p, coOrd orig, double angle ) +{ + double x=p->x,y=p->y; + x -= orig.x; + y -= orig.y; + p->x = (POS_T)(x * cos(D2R(angle)) + y * sin(D2R(angle))); + p->y = (POS_T)(y * cos(D2R(angle)) - x * sin(D2R(angle))); + p->x += orig.x; + p->y += orig.y; +} + + +/** + * Translate coordinates. + * + * \param res OUT new (translated) position + * \param orig IN old position + * \param a IN angle + * \param d IN distance + */ + +void Translate( coOrd *res, coOrd orig, double a, double d ) +{ + res->x = orig.x + (POS_T)(d * sin( D2R(a)) ); + res->y = orig.y + (POS_T)(d * cos( D2R(a)) ); +} + + + +double FindAngle( coOrd p0, coOrd p1 ) +{ + double dx = p1.x-p0.x, dy = p1.y-p0.y; + if (small(dx)) { + if (dy >=0) return 0.0; + else return 180.0; + } + if (small(dy)) { + if (dx >=0) return 90.0; + else return 270.0; + } + return R2D(atan2( dx,dy )); +} + + + +BOOL_T PointOnCircle( coOrd * resP, coOrd center, double radius, double angle ) +{ + double r; + r = sin(D2R(angle)); + r = radius * r; + resP->x = center.x + (POS_T)(radius * sin(D2R(angle))); + resP->y = center.y + (POS_T)(radius * cos(D2R(angle))); + return 1; +} + + + +double ConstrainR( double r ) +{ + double ret; + ret = r / radiusGranularity; + ret = floor( ret + 0.5 ); + ret = ret * radiusGranularity; + return ret; +} + + + + +void FindPos( coOrd * res, double * beyond, coOrd pos, coOrd orig, double angle, double length ) +{ + double a0, a1; + double d; +#ifdef __linux + static volatile double x; +#else + double x; +#endif + a0 = FindAngle( orig, pos ); + a1 = NormalizeAngle( a0 - angle ); + d = FindDistance( orig, pos ); + x = d * cos( D2R( a1 ) ); + if ( x < 0.0 ) { + res->x = (POS_T)0.0; + } else if (x > length) { + res->x = (POS_T)length; + } else { + res->x = (POS_T)x; + } + if (beyond) *beyond = x - res->x; + res->y = (POS_T)(d * sin( D2R( a1 )) ); +} + + + +/* Find intersection: + Given 2 lines each described by a point and angle (P0,A0) (P1,A1) + there exists a common point PC. + d0x = sin(A0) + d0y = cos(A0) + d1x = sin(A1) + d1y = cos(A1) + Pc.x = P0.x + N0 * d0x + Pc.y = P0.y + N0 * d0y + Pc.x = P1.x + N1 * d1x + Pc.y = P1.y + N1 * d1y + + Combining: +(1) Pc.x = P0.x + N0 * d0x = P1.x + N1 * d1y +(2) Pc.y = P0.y + N0 * d0y = P1.y + N1 * d1y + + Solve Pc.y for N0: + P0.y + N0 * d0y = P1.y + N1 * d1y + N0 * d0y = P1.y + N1 * d1y - P0.y + N0 = (P1.y + N1 * d1y - P0.y) / d0y +(3) N0 = (P1.y - P0.y + N1 * d1y) / d0y + + Solve Pc.x for N1: + P0.x + N0 * d0x = P1.x + N1 * d1x + P0.x + N0 * d0x - P1.x = N1 * d1x + (P0.x + N0 * d0x - P1.x) / d1x = N1 +(4) (P0.x - P1.x + N0 * d0x) / d1x = N1 + + Substitute (3) into (4): + (P0.x - P1.x + [(P1.y - P0.y + N1 * d1y) / d0y ] * d0x) + N1 = ----------------------------------------------------------- + d1x + Regroup: + (P0.x - P1.x + [(P1.y - P0.y)/d0y] * d0x [ N1 * d1y / d0y ] * d0x + N1 = -------------------------------------------- + ------------------------ + d1x d1x + + (P0.x - P1.x + [(P1.y - P0.y)/d0y] * d0x N1 * (d1y * d0x / d0y) + N1 = -------------------------------------------- + ------------------------ + d1x d1x + + (P0.x - P1.x + [(P1.y - P0.y)/d0y] * d0x (d1y * d0x / d0y) + N1 = -------------------------------------------- + N1 * -------------------- + d1x d1x + + (d1y * d0x / d0y) (P0.x - P1.x + [(P1.y - P0.y)/d0y] * d0x + N1 * ( 1 - ----------------- ) = -------------------------------------------- + d1x d1x + + (P0.x - P1.x + [(P1.y - P0.y)/d0y] * d0x + -------------------------------------------- + d1x + N1 = ============================================ + d1y * d0x / d0y + 1 - --------------- + d1x + + (P0.x - P1.x + [(P1.y - P0.y)/d0y] * d0x + -------------------------------------------- + d1x + N1 = ============================================ + d1x - d1y * d0x / d0y + --------------------- + d1x + + d1x cancel + (P0.x - P1.x + [(P1.y - P0.y)/d0y] * d0x + N1 = ============================================ + d1x - d1y * d0x / d0y + + (P0.x - P1.x + [(P1.y - P0.y)/d0y] * d0x + N1 = ============================================ + d1x*d0y - d1y*d0x + ------------------- + d0y + + Bring up d0y: + { ((P0.x - P1.x + [(P1.y - P0.y)/d0y] * d0x } * d0y + N1 = ======================================================= + d1x*d0y - d1y*d0x + + Distribute and cancel: + (P0.x - P1.x) * d0y + (P1.y - P0.y) * d0x + N1 = ============================================= + d1x*d0y - d1y*d0x + + if (d1x*d0y - d1y*d0x) = 0 then lines are parallel +*/ + +BOOL_T FindIntersection( coOrd *Pc, coOrd P0, double A0, coOrd P1, double A1 ) +{ + double dx0, dy0, dx1, dy1, N1; + double d; + +#ifndef WINDOWS + if (debugIntersection >= 3) + printf("FindIntersection( [%0.3f %0.3f] A%0.3f [%0.3f %0.3f] A%0.3f\n", + P0.x, P0.y, A0, P1.x, P1.y, A1 ); +#endif + + dx0 = sin( D2R( A0 ) ); + dy0 = cos( D2R( A0 ) ); + dx1 = sin( D2R( A1 ) ); + dy1 = cos( D2R( A1 ) ); + d = dx1 * dy0 - dx0 * dy1; + if (d < EPSILON && d > -EPSILON) { +#ifndef WINDOWS + if (debugIntersection >=3 ) printf("dx1 * dy0 - dx0 * dy1 = %0.3f\n", d ); +#endif + return FALSE; + } +/* + * (P0.x - P1.x) * d0y + (P1.y - P0.y) * d0x + * N1 = ============================================= + * d1x*d0y - d1y*d0x + */ + N1 = dy0 * (P0.x - P1.x) + dx0 * (P1.y - P0.y ); + N1 = N1 / d; + Pc->x = P1.x + (POS_T)(N1*dx1); + Pc->y = P1.y + (POS_T)(N1*dy1); +#ifndef WINDOWS + if (debugIntersection >=3 ) printf( " [%0.3f,%0.3f]\n", Pc->x, Pc->y ); +#endif + return TRUE; +} + + +EPINX_T PickArcEndPt( coOrd pc, coOrd p0, coOrd p1 ) +{ + double a; + a = NormalizeAngle( FindAngle( pc, p1 ) - FindAngle( pc, p0 ) ); + if (a > 180.0) + return 0; + else + return 1; +} + +EPINX_T PickLineEndPt( coOrd p0, double a0, coOrd p1 ) +{ + double a; + a = NormalizeAngle( FindAngle( p0, p1 ) - a0 ); + if (a < 90.0 || a > 270 ) + return 0; + else + return 1; +} + +double LineDistance( coOrd *p, coOrd p0, coOrd p1 ) +{ + double d, a; + coOrd pp, zero; + zero.x = zero.y = (POS_T)0.0; + d = FindDistance( p0, p1 ); + a = FindAngle( p0, p1 ); + pp.x = p->x-p0.x; + pp.y = p->y-p0.y; + Rotate( &pp, zero, -a ); + if (pp.y < 0.0-EPSILON) { + d = FindDistance( p0, *p ); + *p = p0; + return d; + } else if (pp.y > d+EPSILON ) { + d = FindDistance( p1, *p ); + *p = p1; + return d; + } else { + p->x = p0.x + (POS_T)(pp.y*sin(D2R(a))); + p->y = p0.y + (POS_T)(pp.y*cos(D2R(a))); + return pp.x>=0? pp.x : -pp.x; + } +} + + + +double CircleDistance( coOrd *p, coOrd c, double r, double a0, double a1 ) +{ + double d; + double a,aa; + coOrd pEnd; + d = FindDistance( c, *p ); + a = FindAngle( c, *p ); + aa = NormalizeAngle( a - a0 ); + if (a1 >= 360.0 || aa <= a1) { + d = fabs(d-r); + PointOnCircle( p, c, r, a ); + } else { + if ( aa < a1+(360.0-a1)/2.0 ) { + PointOnCircle( &pEnd, c, r, a0+a1 ); + } else { + PointOnCircle( &pEnd, c, r, a0 ); + } + d = FindDistance( *p, pEnd ); + *p = pEnd; + } + return d; +} + + + +coOrd AddCoOrd( coOrd p0, coOrd p1, double a ) +{ + coOrd res, zero; + zero.x = zero.y = (POS_T)0.0; + Rotate(&p1, zero, a ); + res.x = p0.x + p1.x; + res.y = p0.y + p1.y; + return res; +} + +BOOL_T InRect( coOrd pos, coOrd rect ) +{ + if (pos.x >= 0.0 && pos.x <= rect.x && pos.y >= 0.0 && pos.y <= rect.y) + return 1; + else + return 0; +} + + +static BOOL_T IntersectLine( POS_T *fx0, POS_T *fy0, POS_T x1, POS_T y1, POS_T x, POS_T y ) +{ + POS_T x0=*fx0, y0=*fy0, dx, dy; + BOOL_T rc; +#ifdef TEST + printf(" IntersectLine( P0=[%0.2f %0.2f] P1=[%0.2f %0.2f] X=%0.2f Y=%0.2f\n", + x0, y0, x1, y1, x, y ); +#endif + dx = x1-x0; + dy = y1-y0; + if (dy==0.0) { + if (y0 == y) + rc = TRUE; + else + rc = FALSE; + } else { + x0 += (y-y0) * dx/dy; + if (x0 < -EPSILON || x0 > x) { + rc = FALSE; + } else { + *fx0 = x0; + *fy0 = y; + rc = TRUE; + } + } +#ifdef TEST + if (rc) + printf(" = TRUE [%0.2f %0.2f]\n", *fx0, *fy0 ); + else + printf(" = FALSE\n"); +#endif + return rc; +} + +/* + * intersectBox - find point on box boundary ([0,0],[size]) where + * line from p0 (interior) to p1 (exterior) intersects + */ +static void IntersectBox( coOrd *p1, coOrd p0, coOrd size, int x1, int y1 ) +{ +#ifdef TEST + printf(" IntersectBox( P1=[%0.2f %0.2f] P0=[%0.2f %0.2f] S=[%0.2f %0.2f] X1=%d Y1=%d\n", + p1->x, p1->y, p0.x, p0.y, size.x, size.y, x1, y1 ); +#endif + if ( y1!=0 && + IntersectLine( &p1->x, &p1->y, p0.x, p0.y, size.x, (y1==-1?(POS_T)0.0:size.y) )) + return; + else if ( x1!=0 && + IntersectLine( &p1->y, &p1->x, p0.y, p0.x, size.y, (x1==-1?(POS_T)0.0:size.x) )) + return; +#ifndef WINDOWS + else + fprintf(stderr, "intersectBox bogus\n" ); +#endif +} + +BOOL_T ClipLine( coOrd *fp0, coOrd *fp1, coOrd orig, double angle, coOrd size ) +{ + coOrd p0 = *fp0, p1 = * fp1; + int x0, y0, x1, y1; + +#ifdef TEST + printf("ClipLine( P0=[%0.2f %0.2f] P1=[%0.2f %0.2f] O=[%0.2f %0.2f] A=%0.2f S=[%0.2f %0.2f]\n", + p0.x, p0.y, p1.x, p1.y, orig.x, orig.y, angle, size.x, size.y ); +#endif + + Rotate( &p0, orig, -angle ); + Rotate( &p1, orig, -angle ); + p0.x -= orig.x; p0.y -= orig.y; + p1.x -= orig.x; p1.y -= orig.y; + + /* categorize point as to sector: + -1,1 | 0,1 | 1,1 + ------------+-------------S---------- + -1,0 | 0,0 | 1,0 + ------------O-------------+---------- + -1,-1 | 0,-1 + 1,-1 + */ + if ( p0.x < 0.0-EPSILON ) x0 = -1; + else if ( p0.x > size.x+EPSILON ) x0 = 1; + else x0 = 0; + if ( p0.y < 0.0-EPSILON ) y0 = -1; + else if ( p0.y > size.y+EPSILON ) y0 = 1; + else y0 = 0; + if ( p1.x < 0.0-EPSILON ) x1 = -1; + else if ( p1.x > size.x+EPSILON ) x1 = 1; + else x1 = 0; + if ( p1.y < 0.0-EPSILON ) y1 = -1; + else if ( p1.y > size.y+EPSILON ) y1 = 1; + else y1 = 0; + +#ifdef TEST + printf(" X0=%d Y0=%d X1=%d Y1=%d\n", x0, y0, x1, y1 ); +#endif + + /* simple cases: one or both points within box */ + if ( x0==0 && y0==0 ) { + if ( x1==0 && y1==0 ) { + /* both within box */ + return 1; + } + /* p0 within, p1 without */ + IntersectBox( &p1, p0, size, x1, y1 ); + p1.x += orig.x; p1.y += orig.y; + Rotate( &p1, orig, angle ); + *fp1 = p1; + return 1; + } + + if ( x1==0 && y1==0 ) { + /* p1 within, p0 without */ + IntersectBox( &p0, p1, size, x0, y0 ); + p0.x += orig.x; p0.y += orig.y; + Rotate( &p0, orig, angle ); + *fp0 = p0; + return 1; + } + + /* both points without box and cannot intersect */ + if ( (x0==x1 && y0==y1) || /* within same sector (but not the middle one) */ + (x0!=0 && x0==x1) || /* both right or left */ + (y0!=0 && y0==y1) ) /* both above or below */ + return 0; + +#ifdef TEST + printf(" complex intersection\n"); +#endif + + /* possible intersection */ + if ( y0!=0 && + IntersectLine( &p0.x, &p0.y, p1.x, p1.y, size.x, (y0==-1?(POS_T)0.0:size.y) )) + IntersectBox( &p1, p0, size, x1, y1 ); + else if ( y1!=0 && + IntersectLine( &p1.x, &p1.y, p0.x, p0.y, size.x, (y1==-1?(POS_T)0.0:size.y) )) + IntersectBox( &p0, p1, size, x0, y0 ); + else if ( x0!=0 && + IntersectLine( &p0.y, &p0.x, p1.y, p1.x, size.y, (x0==-1?(POS_T)0.0:size.x) )) + IntersectBox( &p1, p0, size, x1, y1 ); + else if ( x1!=0 && + IntersectLine( &p1.y, &p1.x, p0.y, p0.x, size.y, (x1==-1?(POS_T)0.0:size.x) )) + IntersectBox( &p0, p1, size, x0, y0 ); + else { + return 0; + } + p0.x += orig.x; p0.y += orig.y; + p1.x += orig.x; p1.y += orig.y; + Rotate( &p0, orig, angle ); + Rotate( &p1, orig, angle ); + *fp0 = p0; + *fp1 = p1; + return 1; +} + +#ifdef LATER +BOOL_T ClipArc( double a0, double a1, coOrd pos, double radius, coOrd orig, double angle, double size ) +{ + i = -1; + state = unknown; + if (pos.y + radius < 0.0 || + pos.y - radius > size.y || + pos.x + radius < 0.0 || + pos.x - radius > size.x ) + return 0; + + if (pos.y + radius <= size.y || + pos.y - radius >= 0.0 || + pos.x + radius <= size.x || + pos.x - radius >= 0.0 ) + return 1; + + if (pos.y + radius > size.y) { + a = R2D(acos( (size.y-pos.y) / radius ) )); + if (pos.x + radius*cos(R2D(a)) > size.x) { + state = outside; + } else { + state = inside; + i++; + aa[i].a0 = a; + } + } else { + state = inside; + i++; + aa[i].a0 = 0; + } +} +#endif + +#ifdef TEST +void Test( double p0x, double p0y, double p1x, double p1y, double origx, double origy, double angle, double sizex, double sizey ) +{ + + coOrd p0, p1, orig, size, p0a, p1a; + BOOL_T rc; + p0.x = p0x; p0.y = p0y; p1.x = p1x; p1.y = p1y; + orig.x = origx; orig.y = origy; + size.x = sizex; size.y = sizey; + p0a = p0; p1a = p1; + rc = ClipLine( &p0, &p1, orig, angle, size ); + printf("clipLine=%d P0=[%0.3f %0.3f] P1=[%0.3f %0.3f]\n", rc, + p0.x, p0.y, p1.x, p1.y ); +} + +INT_T Main( INT_T argc, char *argv[] ) +{ + double a[9]; + int i; + if (argc != 10) { + printf("usage: a x0 y0 x1 y1 xo yo a xs ys\n"); + Exit(1); + } + argv++; + for (i=0;i<9;i++) + a[i] = atof( *argv++ ); + + Test( a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8] ); +} +#endif diff --git a/app/bin/utility.h b/app/bin/utility.h new file mode 100644 index 0000000..ccf85e4 --- /dev/null +++ b/app/bin/utility.h @@ -0,0 +1,63 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/utility.h,v 1.1 2005-12-07 15:47:39 rc-flyer 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 UTILITY_H +#define UTILITY_H + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#define EPSILON (0.000001) + +#ifdef small +#undef small +#endif +#define small(r) (r < EPSILON && r > -EPSILON) + +extern DEBUGF_T debugIntersection; + +#ifndef max +double max( double a, double b ); +double min( double a, double b ); +#endif +double FindDistance( coOrd p0, coOrd p1 ); +double NormalizeAngle( double a ); +int IsAligned( double a1, double a2 ); +double D2R( double D ); +double R2D( double R ); +void Rotate( coOrd *p, coOrd orig, double angle ); +void Translate( coOrd *res, coOrd orig, double a, double d ); +double FindAngle( coOrd p0, coOrd p1 ); +int PointOnCircle( coOrd * resP, coOrd center, double radius, double angle ); +double ConstrainR( double r ); +void FindPos( coOrd * res, double * beyond, coOrd pos, coOrd orig, double angle, double length ); +int FindIntersection( coOrd *Pc, coOrd P00, double A0, coOrd P10, double A1 ); +double LineDistance( coOrd *p, coOrd p0, coOrd p1 ); +double CircleDistance( coOrd *p, coOrd c, double r, double a0, double a1 ); +int PickArcEndPt( coOrd, coOrd, coOrd ); +int PickLineEndPt( coOrd, double, coOrd ); +coOrd AddCoOrd( coOrd, coOrd, double ); +int ClipLine( coOrd *, coOrd *, coOrd, double, coOrd ); + +#endif diff --git a/app/bin/version.h b/app/bin/version.h new file mode 100644 index 0000000..3441687 --- /dev/null +++ b/app/bin/version.h @@ -0,0 +1,39 @@ +/* $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/version.h,v 1.9 2008-01-29 04:10:23 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. + */ + +#ifdef XTRKCAD_CMAKE_BUILD + + #include "xtrkcad-config.h" + + #define VERSION XTRKCAD_VERSION + #define PARAMVERSION XTRKCAD_PARAMVERSION + #define PARAMVERSIONVERSION XTRKCAD_PARAMVERSIONVERSION + #define MINPARAMVERSION XTRKCAD_MINPARAMVERSION + +#else + + #define VERSION "4.1.0b1" + #define PARAMVERSION (10) + #define PARAMVERSIONVERSION "3.0.0" + #define MINPARAMVERSION (1) + +#endif + diff --git a/app/bin/xtrackcad.c b/app/bin/xtrackcad.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/bin/xtrackcad.c diff --git a/app/bin/xtrkcad.def b/app/bin/xtrkcad.def new file mode 100644 index 0000000..5d5ee31 --- /dev/null +++ b/app/bin/xtrkcad.def @@ -0,0 +1,11 @@ +NAME XTrkCad
+DESCRIPTION 'xtrkcad'
+EXETYPE WINDOWS
+STUB 'WINSTUB.EXE'
+CODE MOVEABLE DISCARDABLE
+DATA MOVEABLE MULTIPLE
+HEAPSIZE 2048
+STACKSIZE 10240
+EXPORTS
+ MainWndProc @1
+ About @2
diff --git a/app/bin/xtrkcad.ico b/app/bin/xtrkcad.ico Binary files differnew file mode 100644 index 0000000..da224a4 --- /dev/null +++ b/app/bin/xtrkcad.ico diff --git a/app/bin/xtrkcad.rc b/app/bin/xtrkcad.rc new file mode 100644 index 0000000..3fd5784 --- /dev/null +++ b/app/bin/xtrkcad.rc @@ -0,0 +1,4 @@ +#include <windows.h> +#include "mswlib.h" + +0 ICON xtrkcad.ico diff --git a/app/bin/xtrkcad256.ico b/app/bin/xtrkcad256.ico Binary files differnew file mode 100644 index 0000000..de23e4c --- /dev/null +++ b/app/bin/xtrkcad256.ico |