summaryrefslogtreecommitdiff
path: root/app/bin
diff options
context:
space:
mode:
Diffstat (limited to 'app/bin')
-rw-r--r--app/bin/CMakeLists.txt181
-rw-r--r--app/bin/ChangeLog495
-rw-r--r--app/bin/acclkeys.h157
-rw-r--r--app/bin/bdf2xtp.c1231
-rwxr-xr-xapp/bin/bitmaps/SVG/block.svg150
-rwxr-xr-xapp/bin/bitmaps/SVG/blockdel.svg202
-rwxr-xr-xapp/bin/bitmaps/SVG/blockedit.svg239
-rwxr-xr-xapp/bin/bitmaps/SVG/blocknew.svg216
-rw-r--r--app/bin/bitmaps/SVG/switchm.svg116
-rw-r--r--app/bin/bitmaps/SVG/switchmdel.svg191
-rw-r--r--app/bin/bitmaps/SVG/switchmedit.svg214
-rw-r--r--app/bin/bitmaps/SVG/switchmnew.svg172
-rw-r--r--app/bin/bitmaps/SVG/tipofday.svg1176
-rw-r--r--app/bin/bitmaps/above.xpm22
-rw-r--r--app/bin/bitmaps/arrow0.xbm9
-rw-r--r--app/bin/bitmaps/arrow3.xbm9
-rw-r--r--app/bin/bitmaps/arrows.xbm10
-rw-r--r--app/bin/bitmaps/ballgreen.xpm35
-rw-r--r--app/bin/bitmaps/ballred.xpm38
-rw-r--r--app/bin/bitmaps/below.xpm22
-rw-r--r--app/bin/bitmaps/bigdot.xbm5
-rw-r--r--app/bin/bitmaps/blockdel.xpm52
-rw-r--r--app/bin/bitmaps/blockedit.xpm89
-rw-r--r--app/bin/bitmaps/blocknew.xpm90
-rw-r--r--app/bin/bitmaps/bma0.xbm6
-rw-r--r--app/bin/bitmaps/bma135.xbm6
-rw-r--r--app/bin/bitmaps/bma45.xbm6
-rw-r--r--app/bin/bitmaps/bma90.xbm6
-rw-r--r--app/bin/bitmaps/bmendpt.xbm6
-rw-r--r--app/bin/bitmaps/bo_edge.xpm20
-rw-r--r--app/bin/bitmaps/bo_flat.xpm20
-rw-r--r--app/bin/bitmaps/bo_ll.xpm20
-rw-r--r--app/bin/bitmaps/bo_lld.xpm20
-rw-r--r--app/bin/bitmaps/bo_lli.xpm20
-rw-r--r--app/bin/bitmaps/bo_llu.xpm20
-rw-r--r--app/bin/bitmaps/bo_lr.xpm20
-rw-r--r--app/bin/bitmaps/bo_lrd.xpm20
-rw-r--r--app/bin/bitmaps/bo_lri.xpm20
-rw-r--r--app/bin/bitmaps/bo_lru.xpm20
-rw-r--r--app/bin/bitmaps/bo_t.xpm20
-rw-r--r--app/bin/bitmaps/bo_ti.xpm20
-rw-r--r--app/bin/bitmaps/bo_tl.xpm20
-rw-r--r--app/bin/bitmaps/bo_tr.xpm20
-rw-r--r--app/bin/bitmaps/bridge.xbm7
-rw-r--r--app/bin/bitmaps/carpart.xpm22
-rw-r--r--app/bin/bitmaps/carproto.xpm23
-rw-r--r--app/bin/bitmaps/chkbox.xbm7
-rw-r--r--app/bin/bitmaps/circle1.xpm23
-rw-r--r--app/bin/bitmaps/circle2.xpm22
-rw-r--r--app/bin/bitmaps/circle3.xpm22
-rw-r--r--app/bin/bitmaps/cnote.xpm22
-rw-r--r--app/bin/bitmaps/cross0.xbm5
-rw-r--r--app/bin/bitmaps/curve1.xpm23
-rw-r--r--app/bin/bitmaps/curve2.xpm23
-rw-r--r--app/bin/bitmaps/curve3.xpm23
-rw-r--r--app/bin/bitmaps/curve4.xpm23
-rw-r--r--app/bin/bitmaps/dbench.xpm22
-rw-r--r--app/bin/bitmaps/dbox.xpm22
-rw-r--r--app/bin/bitmaps/dcircle1.xpm22
-rw-r--r--app/bin/bitmaps/dcircle2.xpm22
-rw-r--r--app/bin/bitmaps/dcircle3.xpm22
-rw-r--r--app/bin/bitmaps/dcurve1.xpm22
-rw-r--r--app/bin/bitmaps/dcurve2.xpm22
-rw-r--r--app/bin/bitmaps/dcurve3.xpm22
-rw-r--r--app/bin/bitmaps/dcurve4.xpm22
-rw-r--r--app/bin/bitmaps/ddimlin.xpm22
-rw-r--r--app/bin/bitmaps/delete.xpm21
-rw-r--r--app/bin/bitmaps/describe.xpm22
-rw-r--r--app/bin/bitmaps/dfilbox.xpm23
-rw-r--r--app/bin/bitmaps/dfilpoly.xpm23
-rw-r--r--app/bin/bitmaps/dflcrcl1.xpm22
-rw-r--r--app/bin/bitmaps/dflcrcl2.xpm23
-rw-r--r--app/bin/bitmaps/dflcrcl3.xpm23
-rw-r--r--app/bin/bitmaps/dline.xpm22
-rw-r--r--app/bin/bitmaps/document-new.xpm38
-rw-r--r--app/bin/bitmaps/document-open.xpm35
-rw-r--r--app/bin/bitmaps/document-print.xpm24
-rw-r--r--app/bin/bitmaps/document-save.xpm35
-rw-r--r--app/bin/bitmaps/dpoly.xpm22
-rw-r--r--app/bin/bitmaps/dtbledge.xpm23
-rw-r--r--app/bin/bitmaps/ebroad.xpm21
-rw-r--r--app/bin/bitmaps/edit-redo.xpm29
-rw-r--r--app/bin/bitmaps/edit-undo.xpm28
-rw-r--r--app/bin/bitmaps/egtbroad.xpm21
-rw-r--r--app/bin/bitmaps/egtsharp.xpm21
-rw-r--r--app/bin/bitmaps/elev.xpm23
-rw-r--r--app/bin/bitmaps/eltbroad.xpm21
-rw-r--r--app/bin/bitmaps/enone.xpm21
-rw-r--r--app/bin/bitmaps/enormal.xpm21
-rw-r--r--app/bin/bitmaps/esharp.xpm21
-rw-r--r--app/bin/bitmaps/exit.xpm21
-rw-r--r--app/bin/bitmaps/export.xpm21
-rw-r--r--app/bin/bitmaps/extend.xpm23
-rw-r--r--app/bin/bitmaps/flash.xbm9
-rw-r--r--app/bin/bitmaps/flip.xpm23
-rw-r--r--app/bin/bitmaps/go.xpm22
-rw-r--r--app/bin/bitmaps/helix.xpm21
-rw-r--r--app/bin/bitmaps/hndldto.xpm21
-rw-r--r--app/bin/bitmaps/hotbarl.xbm7
-rw-r--r--app/bin/bitmaps/hotbarr.xbm7
-rw-r--r--app/bin/bitmaps/import.xpm21
-rw-r--r--app/bin/bitmaps/join.xpm22
-rw-r--r--app/bin/bitmaps/l1.xbm6
-rw-r--r--app/bin/bitmaps/l10.xbm6
-rw-r--r--app/bin/bitmaps/l11.xbm6
-rw-r--r--app/bin/bitmaps/l12.xbm6
-rw-r--r--app/bin/bitmaps/l13.xbm6
-rw-r--r--app/bin/bitmaps/l14.xbm6
-rw-r--r--app/bin/bitmaps/l15.xbm6
-rw-r--r--app/bin/bitmaps/l16.xbm6
-rw-r--r--app/bin/bitmaps/l17.xbm6
-rw-r--r--app/bin/bitmaps/l18.xbm6
-rw-r--r--app/bin/bitmaps/l19.xbm6
-rw-r--r--app/bin/bitmaps/l2.xbm6
-rw-r--r--app/bin/bitmaps/l20.xbm6
-rw-r--r--app/bin/bitmaps/l3.xbm6
-rw-r--r--app/bin/bitmaps/l4.xbm6
-rw-r--r--app/bin/bitmaps/l5.xbm6
-rw-r--r--app/bin/bitmaps/l6.xbm6
-rw-r--r--app/bin/bitmaps/l7.xbm6
-rw-r--r--app/bin/bitmaps/l8.xbm6
-rw-r--r--app/bin/bitmaps/l9.xbm6
-rw-r--r--app/bin/bitmaps/move.xpm23
-rw-r--r--app/bin/bitmaps/movedesc.xpm23
-rw-r--r--app/bin/bitmaps/mtbox.xbm7
-rw-r--r--app/bin/bitmaps/newcar.xpm23
-rw-r--r--app/bin/bitmaps/note.xbm6
-rw-r--r--app/bin/bitmaps/openbutt.xpm21
-rw-r--r--app/bin/bitmaps/parallel.xpm21
-rw-r--r--app/bin/bitmaps/partlist.xpm21
-rw-r--r--app/bin/bitmaps/profile.xpm24
-rw-r--r--app/bin/bitmaps/pull.xpm23
-rw-r--r--app/bin/bitmaps/rotate.xpm23
-rw-r--r--app/bin/bitmaps/ruler.xpm21
-rw-r--r--app/bin/bitmaps/select.xpm22
-rw-r--r--app/bin/bitmaps/snapcurs.xbm7
-rw-r--r--app/bin/bitmaps/snapvis.xbm7
-rw-r--r--app/bin/bitmaps/splittrk.xpm22
-rw-r--r--app/bin/bitmaps/square10.xbm7
-rw-r--r--app/bin/bitmaps/stop.xpm22
-rw-r--r--app/bin/bitmaps/straight.xpm22
-rw-r--r--app/bin/bitmaps/struct.xpm22
-rw-r--r--app/bin/bitmaps/switchmdel.xpm54
-rw-r--r--app/bin/bitmaps/switchmedit.xpm78
-rw-r--r--app/bin/bitmaps/switchmnew.xpm66
-rw-r--r--app/bin/bitmaps/switchmotormark.xbm6
-rw-r--r--app/bin/bitmaps/text.xpm21
-rw-r--r--app/bin/bitmaps/train.xpm22
-rw-r--r--app/bin/bitmaps/tunnel.xpm21
-rw-r--r--app/bin/bitmaps/turnout.xpm21
-rw-r--r--app/bin/bitmaps/turntbl.xpm21
-rw-r--r--app/bin/bitmaps/xtc.xpm83
-rw-r--r--app/bin/bitmaps/xtc16.xbm7
-rw-r--r--app/bin/bitmaps/xtc64.xbm47
-rw-r--r--app/bin/bitmaps/zero.xpm21
-rw-r--r--app/bin/bitmaps/zoom.xpm24
-rw-r--r--app/bin/bitmaps/zoomin.xpm24
-rw-r--r--app/bin/bitmaps/zoomout.xpm24
-rw-r--r--app/bin/cblock.c658
-rw-r--r--app/bin/ccurve.c735
-rw-r--r--app/bin/ccurve.h48
-rw-r--r--app/bin/cdraw.c1245
-rw-r--r--app/bin/celev.c475
-rw-r--r--app/bin/cgroup.c1598
-rw-r--r--app/bin/chndldto.c369
-rw-r--r--app/bin/chotbar.c485
-rw-r--r--app/bin/cjoin.c901
-rw-r--r--app/bin/cjoin.h44
-rw-r--r--app/bin/cmisc.c451
-rw-r--r--app/bin/cmisc2.c54
-rw-r--r--app/bin/cmodify.c407
-rw-r--r--app/bin/cnote.c409
-rw-r--r--app/bin/cnvdsgn.c147
-rw-r--r--app/bin/common.h124
-rw-r--r--app/bin/compound.c1265
-rw-r--r--app/bin/compound.h170
-rw-r--r--app/bin/cparalle.c186
-rw-r--r--app/bin/cprint.c1301
-rw-r--r--app/bin/cprofile.c1357
-rw-r--r--app/bin/cpull.c662
-rw-r--r--app/bin/cruler.c147
-rw-r--r--app/bin/cselect.c1918
-rw-r--r--app/bin/cselect.h48
-rw-r--r--app/bin/csnap.c820
-rw-r--r--app/bin/csplit.c155
-rw-r--r--app/bin/cstraigh.c105
-rw-r--r--app/bin/cstraigh.h25
-rw-r--r--app/bin/cstruct.c922
-rw-r--r--app/bin/cswitchmotor.c534
-rw-r--r--app/bin/ctext.c259
-rw-r--r--app/bin/ctodesgn.c2539
-rw-r--r--app/bin/ctrain.c2586
-rw-r--r--app/bin/ctrain.h55
-rw-r--r--app/bin/cturnout.c2626
-rw-r--r--app/bin/cturntbl.c838
-rw-r--r--app/bin/cundo.c883
-rw-r--r--app/bin/cundo.h32
-rw-r--r--app/bin/custom.c258
-rw-r--r--app/bin/custom.h147
-rw-r--r--app/bin/dbench.c455
-rw-r--r--app/bin/dbitmap.c249
-rw-r--r--app/bin/dcar.c5150
-rw-r--r--app/bin/dcmpnd.c590
-rw-r--r--app/bin/dcustmgm.c368
-rw-r--r--app/bin/dease.c266
-rw-r--r--app/bin/denum.c240
-rw-r--r--app/bin/dlayer.c978
-rw-r--r--app/bin/doption.c591
-rw-r--r--app/bin/dpricels.c165
-rw-r--r--app/bin/dprmfile.c455
-rw-r--r--app/bin/draw.c2446
-rw-r--r--app/bin/draw.h208
-rw-r--r--app/bin/drawgeom.c721
-rw-r--r--app/bin/drawgeom.h58
-rw-r--r--app/bin/elev.c1317
-rw-r--r--app/bin/fileio.c1565
-rw-r--r--app/bin/fileio.h123
-rw-r--r--app/bin/i18n.c50
-rw-r--r--app/bin/i18n.h43
-rw-r--r--app/bin/lprintf.c147
-rw-r--r--app/bin/macro.c1440
-rw-r--r--app/bin/misc.c2674
-rw-r--r--app/bin/misc.h392
-rw-r--r--app/bin/misc2.c693
-rw-r--r--app/bin/misc2.h110
-rw-r--r--app/bin/param.c2699
-rw-r--r--app/bin/param.h231
-rw-r--r--app/bin/shrtpath.c330
-rw-r--r--app/bin/shrtpath.h33
-rw-r--r--app/bin/smalldlg.c245
-rw-r--r--app/bin/smalldlg.h38
-rw-r--r--app/bin/tcurve.c1587
-rw-r--r--app/bin/tease.c1950
-rw-r--r--app/bin/to3way.src24
-rw-r--r--app/bin/tocrv.src18
-rw-r--r--app/bin/tocrvsct.src3
-rw-r--r--app/bin/todcross.src14
-rw-r--r--app/bin/todslip.src12
-rw-r--r--app/bin/tolcross.src13
-rw-r--r--app/bin/torcross.src13
-rw-r--r--app/bin/toreg.src15
-rw-r--r--app/bin/tosslip.src11
-rw-r--r--app/bin/tostrsct.src5
-rw-r--r--app/bin/towye.src21
-rw-r--r--app/bin/toxing.src10
-rw-r--r--app/bin/track.c2932
-rw-r--r--app/bin/track.h654
-rw-r--r--app/bin/trackx.h52
-rw-r--r--app/bin/trkseg.c1662
-rw-r--r--app/bin/tstraigh.c806
-rw-r--r--app/bin/utility.c639
-rw-r--r--app/bin/utility.h63
-rw-r--r--app/bin/version.h39
-rw-r--r--app/bin/xtrackcad.c0
-rw-r--r--app/bin/xtrkcad.def11
-rw-r--r--app/bin/xtrkcad.icobin0 -> 22486 bytes
-rw-r--r--app/bin/xtrkcad.rc4
-rw-r--r--app/bin/xtrkcad256.icobin0 -> 32953 bytes
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, &center, &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, &noteTextData } };
+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( &notePG, 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"), &noteData.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( &notePG );
+ 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( &noteCmds );
+}
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 = &section(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 = &section(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 = &section(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 = &section(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 = &section(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 = &section(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 = &section(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 = &section(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 = &section(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 = &section(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, &center, &radius );
+ PointOnCircle( &params->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, &notesData, 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, &centerDrawMode, "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, &paramFileListData, NULL, BL_DUP|BL_SETSTAY },
+#define I_PRMFILTOGGLE (1)
+ { PD_TOGGLE, &paramFileSel, "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( &paramFilePG, 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( &paramFilePG );
+ ParamGroupRecord( &paramFilePG );
+ 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( &paramFilePG );
+ 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 = &paramLine[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, &paramLine[7], TITLEMAXLEN );
+ Title1[ TITLEMAXLEN - 1 ] = '\0';
+ /*wStringSetValue( title1PD.control, Title1 );*/
+ } else if (strncmp( paramLine, "TITLE2 ", 7 ) == 0) {
+ strncpy( Title2, &paramLine[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, &center, &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, &center, &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, &params->arcP, &params->arcR);
+ GetCurveAngles( &params->arcA0, &params->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( &notrackCmds );
+ 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, &ltrk );
+ if (xtrk) {
+ *to_last = xtrk;
+ to_last = &ltrk->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, &ltrk );
+ 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
new file mode 100644
index 0000000..da224a4
--- /dev/null
+++ b/app/bin/xtrkcad.ico
Binary files differ
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
new file mode 100644
index 0000000..de23e4c
--- /dev/null
+++ b/app/bin/xtrkcad256.ico
Binary files differ