diff options
author | JΓΆrg Frings-FΓΌrst <debian@jff-webhosting.net> | 2020-08-24 21:26:53 +0200 |
---|---|---|
committer | JΓΆrg Frings-FΓΌrst <debian@jff-webhosting.net> | 2020-08-24 21:26:53 +0200 |
commit | df247efec654e512242e4f4f1b0212034f9e01fe (patch) | |
tree | 25c02e16957f3aa613af30c140fd8e8a3d52fda6 /app/bin | |
parent | d0b6a8a4ec298024f14f704f9e40a6f9d324ccf3 (diff) | |
parent | a5ade52caa489cf0a713e0f02b764000d203140e (diff) |
Merge branch 'release/debian/1%5.2.0Beta2.1-1' into masterdebian/1%5.2.0Beta2.1-1
Diffstat (limited to 'app/bin')
191 files changed, 36965 insertions, 9289 deletions
diff --git a/app/bin/CMakeLists.txt b/app/bin/CMakeLists.txt index 74b1bc8..2dea1bc 100644 --- a/app/bin/CMakeLists.txt +++ b/app/bin/CMakeLists.txt @@ -1,5 +1,6 @@ +include( CheckSymbolExists ) + 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) @@ -8,7 +9,7 @@ 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 + COMMAND cnvdsgn < ${CMAKE_CURRENT_SOURCE_DIR}/${lin_name}.src > ${CMAKE_CURRENT_BINARY_DIR}/${lin_name}.lin ) ENDMACRO(GENERATE_LIN) @@ -24,6 +25,9 @@ GENERATE_LIN(tosslip) GENERATE_LIN(tostrsct) GENERATE_LIN(towye) GENERATE_LIN(toxing) +GENERATE_LIN(tocornu) +GENERATE_LIN(tocornuwye) +GENERATE_LIN(tocornu3way) SET(LIN_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/to3way.lin @@ -38,19 +42,21 @@ SET(LIN_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/tostrsct.lin ${CMAKE_CURRENT_BINARY_DIR}/towye.lin ${CMAKE_CURRENT_BINARY_DIR}/toxing.lin + ${CMAKE_CURRENT_BINARY_DIR}/tocornu.lin + ${CMAKE_CURRENT_BINARY_DIR}/tocornuwye.lin + ${CMAKE_CURRENT_BINARY_DIR}/tocornu3way.lin ) -GET_TARGET_PROPERTY(genhelp_EXE genhelp LOCATION) - 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 + DEPENDS genhelp ${help_SOURCE_DIR}/genhelp.json + COMMAND genhelp ${GENHELP_OPTS} ${help_SOURCE_DIR}/genhelp.json ${CMAKE_CURRENT_BINARY_DIR}/bllnhlp.c ) SET(SOURCES ${LIN_SOURCES} appdefaults.c + archive.c bllnhlp.c cbezier.c cblock.c @@ -95,6 +101,7 @@ SET(SOURCES dcontmgm.c dease.c denum.c + directory.c dlayer.c doption.c dpricels.c @@ -104,33 +111,72 @@ SET(SOURCES dxfformat.c dxfoutput.c elev.c + file2uri.c + file2uri.h fileio.c + filenoteui.c i18n.c layout.c + linknoteui.c lprintf.c macro.c + manifest.c misc2.c param.c + paramfile.c + paramfilelist.c + paramfilesearch_ui.c + partcatalog.c paths.c + shortentext.c shrtpath.c smalldlg.c + stringxtc.c tbezier.c tcornu.c tcurve.c tease.c + textnoteui.c track.c + trknote.c trkseg.c tstraigh.c utility.c + validator.c + cJSON.c + archive.h + directory.h + manifest.h + validator.h ) +# add UTF-8 conversion utilities on Windows +if(WIN32) + set( SOURCES + ${SOURCES} + utf8convert.c + include/utf8convert.h + ) +endif(WIN32) + +set (SOURCES + ${SOURCES} + include/dirent.h + include/paramfile.h + include/paramfilelist.h +) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES(${XTrkCAD_BINARY_DIR}) INCLUDE_DIRECTORIES(${help_BINARY_DIR}) INCLUDE_DIRECTORIES(${wlib_SOURCE_DIR}/include) +include_directories(${FREEIMAGE_INCLUDE_PATH}) +INCLUDE_DIRECTORIES(${LIBZIP_INCLUDE_DIR_ZIP}) +INCLUDE_DIRECTORIES(${CJSON_INCLUDE}) LINK_DIRECTORIES(${GTK_LIBRARY_DIRS}) LINK_DIRECTORIES(${GTK_WEBKIT_LIBRARY_DIRS}) +LINK_DIRECTORIES(${LIBZIP_LIBZIP_LIBRARY}) ADD_LIBRARY(xtrkcad-lib ${SOURCES}) @@ -145,18 +191,22 @@ TARGET_LINK_LIBRARIES(xtrkcad xtrkcad-lib) TARGET_LINK_LIBRARIES(xtrkcad xtrkcad-wlib) TARGET_LINK_LIBRARIES(xtrkcad xtrkcad-cornu) TARGET_LINK_LIBRARIES(xtrkcad dynstring) +target_link_libraries(xtrkcad ${LIBZIP_LIBRARY} ${LIBZIP_LIBRARIES}) ADD_EXECUTABLE(mkturnout ${LIN_SOURCES} ctodesgn.c utility.c + ) SET_TARGET_PROPERTIES(mkturnout PROPERTIES COMPILE_FLAGS -DMKTURNOUT) +TARGET_LINK_LIBRARIES(mkturnout xtrkcad-cornu) + 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) @@ -164,25 +214,57 @@ IF(NOT WIN32) endif(INTL_LIBRARY) ELSE(NOT WIN32) TARGET_LINK_LIBRARIES(mkturnout xtrkcad-wlib) -ENDIF(NOT WIN32) + + # copy dlls into the build dir for easier debugging + add_custom_command( + TARGET xtrkcad POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${FREEIMAGE_SHAREDLIB} + ${CMAKE_CURRENT_BINARY_DIR} + ) + + add_custom_command( + TARGET xtrkcad POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${LIBZIP_SHAREDLIB} + ${CMAKE_CURRENT_BINARY_DIR} + ) + + add_custom_command( + TARGET xtrkcad POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${ZLIB_SHAREDLIB} + ${CMAKE_CURRENT_BINARY_DIR} + ) + + # add dll to install package + install(FILES + ${LIBZIP_SHAREDLIB} + DESTINATION ${XTRKCAD_BIN_INSTALL_DIR} + ) + install(FILES + ${ZLIB_SHAREDLIB} + DESTINATION ${XTRKCAD_BIN_INSTALL_DIR} + ) +ENDIF(NOT WIN32) # for testing only, should be IF(APPLE) ... IF(APPLE) - ADD_EXECUTABLE( helphelper helphelper.c ) + ADD_EXECUTABLE( helphelper helphelper.c ) FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation) - FIND_LIBRARY(CARBON_LIBRARY Carbon) + FIND_LIBRARY(CARBON_LIBRARY Carbon) TARGET_lINK_LIBRARIES(helphelper ${COREFOUNDATION_LIBRARY} ${CARBON_LIBRARY}) INSTALL( TARGETS helphelper RUNTIME DESTINATION ${XTRKCAD_BIN_INSTALL_DIR} ) ENDIF(APPLE) - + INSTALL( TARGETS xtrkcad RUNTIME DESTINATION ${XTRKCAD_BIN_INSTALL_DIR} ) - -if(XTRKCAD_TESTING AND CMOCKA_FOUND) + +if(XTRKCAD_TESTING AND CMOCKA_FOUND) add_subdirectory( unittest ) -endif() +endif() diff --git a/app/bin/acclkeys.h b/app/bin/acclkeys.h index 1cbdf00..4dd80fc 100644 --- a/app/bin/acclkeys.h +++ b/app/bin/acclkeys.h @@ -40,6 +40,9 @@ #define ACCL_CIRCLE2 (WCTL+'9') #define ACCL_CIRCLE3 (WCTL+'0') #define ACCL_BEZIER (0) +#define ACCL_CORNU (0) +#define ACCL_CONVERTTO (0) +#define ACCL_CONVERTFR (0) #define ACCL_TURNOUT (WCTL+'t') #define ACCL_TURNTABLE (WCTL+WSHIFT+'n') #define ACCL_PARALLEL (WCTL+WSHIFT+'p') @@ -56,6 +59,8 @@ #define ACCL_PROFILE (WCTL+WSHIFT+'f') #define ACCL_DELETE (WCTL+'d') #define ACCL_TUNNEL (WCTL+WSHIFT+'t') +#define ACCL_BRIDGE (0) +#define ACCL_TIES (0) #define ACCL_HNDLDTO (WCTL+WSHIFT+'i') #define ACCL_TEXT (WCTL+WSHIFT+'x') #define ACCL_DRAWLINE (WCTL+WSHIFT+'1') @@ -75,8 +80,10 @@ #define ACCL_DRAWBEZLINE (0) #define ACCL_DRAWBOX (WCTL+WSHIFT+'[') #define ACCL_DRAWFILLBOX (WALT+WCTL+'[') -#define ACCL_DRAWPOLYLINE (WCTL+WSHIFT+'2') +#define ACCL_DRAWPOLYLINE (0) #define ACCL_DRAWPOLYGON (WALT+WCTL+'2') +#define ACCL_DRAWPOLY (0) +#define ACCL_DRAWFILLPOLYGON (WCTL+WSHIFT+'2') #define ACCL_NOTE (WALT+WCTL+'n') #define ACCL_STRUCTURE (WCTL+WSHIFT+'c') #define ACCL_ABOVE (WCTL+WSHIFT+'b') @@ -104,6 +111,7 @@ #define ACCL_COPY (WCTL+'c') #define ACCL_CUT (WCTL+'x') #define ACCL_PASTE (WCTL+'v') +#define ACCL_CLONE (0) #define ACCL_SELECTALL (WCTL+WSHIFT+'a') #define ACCL_DESELECTALL (0) #define ACCL_THIN (WCTL+'1') @@ -111,6 +119,7 @@ #define ACCL_THICK (WCTL+'3') #define ACCL_EXPORT (WALT+WCTL+'x') #define ACCL_IMPORT (WALT+WCTL+'i') +#define ACCL_IMPORT_MOD (0) #define ACCL_EXPORTDXF (0) #define ACCL_LOOSEN (WCTL+WSHIFT+'k') #define ACCL_GROUP (WCTL+WSHIFT+'g') @@ -149,6 +158,7 @@ #define ACCL_PLAYBACK (WALT+WCTL+'b') #define ACCL_BRIDGE (0) +#define ACCL_TIES (0) /* Blocks */ #define ACCL_BLOCK1 (0) diff --git a/app/bin/appdefaults.c b/app/bin/appdefaults.c index a2dd885..55a2201 100644 --- a/app/bin/appdefaults.c +++ b/app/bin/appdefaults.c @@ -82,7 +82,10 @@ static char *GetParamPrototype(struct appDefault *ptrDefault, */ struct appDefault xtcDefaults[] = { - { "DialogItem.cmdopt-preselect", 0, INTEGERCONSTANT,{ .intValue = 1 } }, /**< default command is select */ + { "DialogItem.cmdopt-preselect", 0, INTEGERCONSTANT,{ .intValue = 1 } }, /**< default command is select */ + { "DialogItem.cmdopt-rightclickmode", 0, INTEGERCONSTANT,{ .intValue = 1 } }, /**< swap default to context */ + { "DialogItem.cmdopt-selectmode", 0, INTEGERCONSTANT,{ .intValue = 0 } }, /**< 'Only' mode */ + { "DialogItem.cmdopt-selectzero", 0, INTEGERCONSTANT,{ .intValue = 1 } }, /**< 'On' mode */ { "DialogItem.grid-horzenable", 0, INTEGERCONSTANT, { .intValue = 0 }}, { "DialogItem.grid-vertenable", 0, INTEGERCONSTANT,{ .intValue = 0 } }, { "DialogItem.pref-dstfmt", 0, INTEGERFUNCTION,{ .intFunction = GetLocalDistanceFormat } }, /**< number format for distances */ @@ -227,6 +230,14 @@ InitializeRegionCode(void) } /** + * Use Metric measures everywhere except United States and Canada\ + */ +static bool UseMetric() +{ + return ( strcmp( regionCode, "US" ) != 0 && + strcmp( regionCode, "CA" ) != 0 ); +} +/** * For the US the classical 4x8 sheet is used as default size. in the metric world 1,25x2,0m is used. */ @@ -234,11 +245,11 @@ static double GetLocalRoomSize(struct appDefault *ptrDefault, void *data) { if (!strcmp(ptrDefault->defaultKey, "draw.roomsizeY")) { - return (strcmp(regionCode, "US") ? 125.0/2.54 : 48); + return (UseMetric() ? 125.0/2.54 : 48); } if (!strcmp(ptrDefault->defaultKey, "draw.roomsizeX")) { - return (strcmp(regionCode, "US") ? 200.0 / 2.54 : 96); + return (UseMetric() ? 200.0 / 2.54 : 96); } return (0.0); // should never get here @@ -255,21 +266,21 @@ GetLocalPopularScale(struct appDefault *ptrDefault, void *data) } /** - * The measurement system is english for the US and metric elsewhere + * The measurement system is english for the US and Canada and metric elsewhere */ static int GetLocalMeasureSystem(struct appDefault *ptrDefault, void *data) { - return (strcmp(regionCode, "US") ? 1 : 0); + return (UseMetric() ? 1 : 0); } /** -* The distance format is 999.9 cm for metric and ?? for english +* The distance format is 999.9 cm for metric and 999.99 for english */ static int GetLocalDistanceFormat(struct appDefault *ptrDefault, void *data) { - return (strcmp(regionCode, "US") ? 8 : 5); + return (UseMetric() ? 8 : 4); } /** diff --git a/app/bin/archive.c b/app/bin/archive.c new file mode 100644 index 0000000..4e82bd3 --- /dev/null +++ b/app/bin/archive.c @@ -0,0 +1,454 @@ +/** \file archive.c + * ARCHIVE PROCESSING + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2018 Adam Richards and 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 <errno.h> +#include <fcntl.h> + +#include <string.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <zip.h> + +#ifdef WINDOWS + #include "include/dirent.h" + #include <direct.h> + #include <io.h> + #include <process.h> + #define unlink(a) _unlink((a)) + #define rmdir(a) _rmdir((a)) + #define open(name, flag, mode) _open((name), (flag), (mode)) + #define write(file, buffer, count) _write((file),(buffer), (count)) + #define close(file) _close((file)) + #define getpid() _getpid() +#else + #include <dirent.h> + #include <unistd.h> +#endif + +#include <wlib.h> +#include "archive.h" +#include "directory.h" +#include "dynstring.h" +#include "i18n.h" +#include "messages.h" +#include "misc.h" +#include "misc2.h" +#include "paths.h" +#include "include/utf8convert.h" + +int log_zip = 0; + +//char * +//NativeToUtf8(const char *nativeString) +//{ +// +//#ifdef WINDOWS +// +// int cnt = 2 * (strlen(nativeString) + 1); +// char *tempBuffer = MyMalloc( cnt ); +// char *destBuffer = MyMalloc( cnt ); +// +// //// find the +// //cnt = MultiByteToWideChar(CP_ACP, +// // 0, +// // nativeString, +// // -1, +// // tempBuffer, +// // 0); +// +// //tempBuffer = realloc(tempBuffer, cnt * 2 + 4); +// +// // convert to wide character (UTF16) +// MultiByteToWideChar(CP_ACP, +// 0, +// nativeString, +// -1, +// (LPWSTR)tempBuffer, +// cnt); +// +// // convert from wide char to UTF-8 +// WideCharToMultiByte(CP_UTF8, +// 0, +// (LPCWCH)tempBuffer, +// -1, +// (LPSTR)destBuffer, +// cnt, +// NULL, +// NULL); +// +// MyFree(tempBuffer); +//#else +// char * destBuffer = MyStrdup(nativeString); +//#endif +// +// return(destBuffer); +//} + +/** + * Create the full path for temporary directories used in zip archive operations + * + * \param archive operation + * \return pointer to full path, must be free'd by caller + */ + +char * +GetZipDirectoryName(enum ArchiveOps op) +{ + char *opDesc; + char *directory; + DynString zipDirectory; + + DynStringMalloc(&zipDirectory, 0); + + switch (op) { + case ARCHIVE_READ: + opDesc = "in"; + break; + case ARCHIVE_WRITE: + opDesc = "out"; + break; + default: + opDesc = "err"; + break; + } + + DynStringPrintf(&zipDirectory, + "%s" FILE_SEP_CHAR "zip_%s.%d", + workingDir, + opDesc, + getpid()); + + directory = strdup(DynStringToCStr(&zipDirectory)); + DynStringFree(&zipDirectory); + return (directory); +} + +/***************************************************************************** + * Add directory to archive + * + * \param IN zip The open zip archive handle + * \param IN dir_path The path to add + * \param IN prefix The prefix in the archive + * + * \returns TRUE if OK + */ + +BOOL_T AddDirectoryToArchive( + struct zip * za, + const char * dir_path, + const char * prefix) +{ + + char *full_path; + char *arch_path; + DIR *dir; + const char * buf; + struct stat stat_path, stat_entry; + struct dirent *entry; + + zip_source_t * zt; + + // stat for the path + stat(dir_path, &stat_path); + + // if path does not exists or is not dir - exit with status -1 + if (S_ISDIR(stat_path.st_mode) == 0) { + NoticeMessage(MSG_NOT_DIR_FAIL, + _("Continue"), NULL, dir_path); + return FALSE; + } + + // if not possible to read the directory for this user + if ((dir = opendir(dir_path)) == NULL) { + NoticeMessage(MSG_OPEN_DIR_FAIL, + _("Continue"), NULL, dir_path); + return FALSE; + } + + // iteration through entries in the directory + while ((entry = readdir(dir)) != NULL) { + // skip entries "." and ".." + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { + continue; + } + + // determinate a full path of an entry + MakeFullpath(&full_path, dir_path, entry->d_name, NULL); + + // stat for the entry + stat(full_path, &stat_entry); + + if (prefix && prefix[0]) { + MakeFullpath(&arch_path, prefix, entry->d_name, NULL); + } else { + MakeFullpath(&arch_path, entry->d_name, NULL); + } + + // recursively add a nested directory + if (S_ISDIR(stat_entry.st_mode) != 0) { + if (zip_dir_add(za, arch_path, 0) < 0) { + zip_error_t *ziperr = zip_get_error(za); + buf = zip_error_strerror(ziperr); + NoticeMessage(MSG_ZIP_DIR_ADD_FAIL, + _("Continue"), NULL, arch_path, buf); +#if DEBUG + printf("Added Directory %s \n", arch_path); +#endif + } + + if (AddDirectoryToArchive(za, full_path, arch_path) != TRUE) { + free(full_path); + free(arch_path); + return FALSE; + } + free(arch_path); + continue; + } else { + char *archPathUtf8 = MyStrdup(arch_path); + char *fullPathUtf8 = MyStrdup(full_path); +#ifdef WINDOWS + archPathUtf8 = Convert2UTF8(archPathUtf8); + fullPathUtf8 = Convert2UTF8(fullPathUtf8); + ConvertPathForward(archPathUtf8); +#endif // WINDOWS + zt = zip_source_file(za, fullPathUtf8, 0, -1); + if (zip_file_add(za, archPathUtf8, zt, ZIP_FL_ENC_UTF_8) == -1) { + zip_error_t *ziperr = zip_get_error(za); + buf = zip_error_strerror(ziperr); + NoticeMessage(MSG_ZIP_FILE_ADD_FAIL, _("Continue"), NULL, full_path, arch_path, + buf); + free(full_path); + free(arch_path); + MyFree(fullPathUtf8); + MyFree(archPathUtf8); + return FALSE; + } + MyFree(fullPathUtf8); + MyFree(archPathUtf8); +#if DEBUG + printf("Added File %s", full_path); +#endif + } + free(arch_path); + free(full_path); + } + + closedir(dir); + return TRUE; +} + +/*********************************************************************** + * Create Archive + * + * \param IN dir_path The place to create the archive + * \param IN fileName The name of the archive + * + * \return TRUE if ok + */ + +BOOL_T CreateArchive( + const char * dir_path, + const char * fileName) +{ + struct zip *za; + int err; + char buf[100]; + + char * archive = MyStrdup(fileName); // Because of const char + char * archive_name = FindFilename(archive); + char * archive_path; + char * archiveUtf8; + + MakeFullpath(&archive_path, workingDir, archive_name, NULL); + + archiveUtf8 = MyStrdup(archive_path); +#ifdef WINDOWS + archiveUtf8 = Convert2UTF8(archiveUtf8); +#endif // WINDOWS + + MyFree(archive); + + if ((za = zip_open(archiveUtf8, ZIP_CREATE, &err)) == NULL) { + zip_error_to_str(buf, sizeof(buf), err, errno); + NoticeMessage(MSG_ZIP_CREATE_FAIL, _("Continue"), NULL, archiveUtf8, buf); + MyFree(archiveUtf8); + return FALSE; + } +#if DEBUG + printf("====================== \n"); + printf("Started Archive %s", archive_path); +#endif + + AddDirectoryToArchive(za, dir_path, ""); + + if (zip_close(za) == -1) { + zip_error_to_str(buf, sizeof(buf), err, errno); + NoticeMessage(MSG_ZIP_CLOSE_FAIL, _("Continue"), NULL, archiveUtf8, buf); + free(archive_path); + MyFree(archiveUtf8); + return FALSE; + } + + unlink(fileName); //Delete Old + if (rename(archive_path, fileName) == -1) { //Move zip into place + NoticeMessage(MSG_ZIP_RENAME_FAIL, _("Continue"), NULL, archiveUtf8, fileName, + strerror(errno)); + free(archive_path); + MyFree(archiveUtf8); + return FALSE; + } + free(archive_path); + MyFree(archiveUtf8); + +#if DEBUG + printf("Moved Archive to %s", fileName); + printf("====================== \n"); +#endif + return TRUE; +} + +/************************************************************************** + * Unpack_Archive_for + * + * \param IN pathName the name of the archive + * \param IN fileName just the filename and extension of the layout + * \param IN tempDir The directory to use to unpack into + * + * \returns TRUE if all worked + */ +BOOL_T UnpackArchiveFor( + const char * pathName, /*Full name of archive*/ + const char * fileName, /*Layout name and extension */ + const char * tempDir, /*Directory to unpack into */ + BOOL_T file_only) +{ + char *dirName; + struct zip *za; + struct zip_file *zf; + struct zip_stat sb; + char buf[100]; + int err; + int i; + int64_t len; + FILE *fd; + long long sum; + + char *destBuffer = MyStrdup(pathName); +#ifdef WINDOWS + destBuffer = Convert2UTF8(destBuffer); +#endif // WINDOWS + + + if ((za = zip_open(destBuffer, 0, &err)) == NULL) { + zip_error_to_str(buf, sizeof(buf), err, errno); + NoticeMessage(MSG_ZIP_OPEN_FAIL, _("Continue"), NULL, pathName, buf); + fprintf(stderr, "xtrkcad: can't open xtrkcad zip archive `%s': %s \n", + pathName, buf); + + MyFree(destBuffer); + return FALSE; + } + + for (i = 0; i < zip_get_num_entries(za, 0); i++) { + if (zip_stat_index(za, i, 0, &sb) == 0) { + len = strlen(sb.name); + +#if DEBUG + printf("==================\n"); + printf("Name: [%s], ", sb.name); + printf("Size: [%llu], ", sb.size); + printf("mtime: [%u]\n", (unsigned int)sb.mtime); + printf("mtime: [%u]\n", (unsigned int)sb.mtime); +#endif + + LOG(log_zip, 1, ("================= \n")) + LOG(log_zip, 1, ("Zip-Name [%s] \n", sb.name)) + LOG(log_zip, 1, ("Zip-Size [%llu] \n", sb.size)) + LOG(log_zip, 1, ("Zip-mtime [%u] \n", (unsigned int)sb.mtime)) + + if (sb.name[len - 1] == '/' && !file_only) { + MakeFullpath(&dirName, tempDir, &sb.name[0], NULL); + if (SafeCreateDir(dirName) != TRUE) { + free(dirName); + return FALSE; + } + free(dirName); + } else { + zf = zip_fopen_index(za, i, 0); + if (!zf) { + NoticeMessage(MSG_ZIP_INDEX_FAIL, _("Continue"), NULL); + fprintf(stderr, "xtrkcad zip archive open index error \n"); + return FALSE; + } + + if (file_only) { + if (strncmp(sb.name, fileName, strlen(fileName)) != 0) { + continue; /* Ignore any other files than the one we asked for */ + } + } + MakeFullpath(&dirName, tempDir, &sb.name[0], NULL); +#ifdef WINDOWS + ConvertUTF8ToSystem(dirName); +#endif // WINDOWS + fd = fopen(dirName, "wb"); + if (!fd) { + NoticeMessage(MSG_ZIP_FILE_OPEN_FAIL, _("Continue"), NULL, dirName, + strerror(errno)); + free(dirName); + return FALSE; + } + + sum = 0; + while (sum != sb.size) { + len = zip_fread(zf, buf, 100); + if (len < 0) { + NoticeMessage(MSG_ZIP_READ_FAIL, _("Continue"), NULL, dirName, &sb.name[0]); + free(dirName); + fclose(fd); + return FALSE; + } + fwrite(buf, 1, (unsigned int)len, fd); + sum += len; + } + fclose(fd); + free(dirName); + zip_fclose(zf); + } + } else { + LOG(log_zip, 1, ("Zip-Unknown File[%s] Line[%d] \n", __FILE__, __LINE__)) +#if DEBUG + printf("File[%s] Line[%d]\n", __FILE__, __LINE__); +#endif + } + } + + MyFree(destBuffer); + + if (zip_close(za) == -1) { + NoticeMessage(MSG_ZIP_CLOSE_FAIL, _("Continue"), NULL, dirName, &sb.name[0]); + return FALSE; + } + return TRUE; +} + diff --git a/app/bin/archive.h b/app/bin/archive.h new file mode 100644 index 0000000..cfbb642 --- /dev/null +++ b/app/bin/archive.h @@ -0,0 +1,15 @@ +#ifndef HAVE_ARCHIVE_H +#define HAVE_ARCHIVE_H +#include <zip.h> +#include "common.h" + +enum ArchiveOps { ARCHIVE_READ, ARCHIVE_WRITE }; // has to be contiguous, see CleanupFiles()! + +extern int log_zip; +extern const char *workingDir; + +char *GetZipDirectoryName(enum ArchiveOps op); +BOOL_T AddDirectoryToArchive(struct zip * za, const char * dir_path, const char * prefix); +BOOL_T CreateArchive(const char * dir_path, const char * fileName); +BOOL_T UnpackArchiveFor(const char * pathName, const char * fileName, const char * tempDir, BOOL_T file_only); +#endif diff --git a/app/bin/bdf2xtp.c b/app/bin/bdf2xtp.c index 76fb31a..c979aa3 100644 --- a/app/bin/bdf2xtp.c +++ b/app/bin/bdf2xtp.c @@ -9,6 +9,7 @@ #include <math.h> #ifndef _MSDOS #include <unistd.h> +#include "fileio.h" #else #define M_PI 3.14159265358979323846 #define strncasecmp strnicmp @@ -563,7 +564,7 @@ void generateTurnout( void ) 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" ); + fprintf( fout, "\t%s\n", END_SEGS ); } @@ -875,7 +876,7 @@ void process( tokenDesc_t * tp, arg_t *args ) break; } } - fprintf( fout, "\tEND\n" ); + fprintf( fout, "\t%s\n", END_SEGS ); break; case ACT_TRANSFERTABLE: @@ -928,7 +929,7 @@ void process( tokenDesc_t * tp, arg_t *args ) } offset += length2; } - fprintf( fout, "\tEND\n"); + fprintf( fout, "\t%s\n", END_SEGS); break; case ACT_ENDTRANSFERTABLE: @@ -956,7 +957,7 @@ void process( tokenDesc_t * tp, arg_t *args ) break; } } - fprintf( fout, "\tEND\n" ); + fprintf( fout, "\t%s\n", END_SEGS ); break; case ACT_FILL_POINT: diff --git a/app/bin/bitmaps/SVG/star.svg b/app/bin/bitmaps/SVG/star.svg new file mode 100644 index 0000000..13f0914 --- /dev/null +++ b/app/bin/bitmaps/SVG/star.svg @@ -0,0 +1,124 @@ +<?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: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="64" + height="64" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45.1" + version="1.0" + inkscape:export-filename="C:\Users\mf\Desktop\star.png" + inkscape:export-xdpi="22.5" + inkscape:export-ydpi="22.5" + sodipodi:docbase="C:\Users\mf\Documents\XTrackCAD\src\work\app\bin\bitmaps\SVG" + sodipodi:docname="star.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs4"> + <linearGradient + id="linearGradient3159"> + <stop + style="stop-color:#808080;stop-opacity:1;" + offset="0" + id="stop3161" /> + <stop + id="stop3167" + offset="1" + style="stop-color:#404040;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient3153"> + <stop + id="stop3155" + offset="0" + style="stop-color:#ffff00;stop-opacity:1;" /> + <stop + id="stop3157" + offset="1" + style="stop-color:#ffff00;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient3136"> + <stop + style="stop-color:#ffff00;stop-opacity:1;" + offset="0" + id="stop3138" /> + <stop + style="stop-color:#ffff00;stop-opacity:0;" + offset="1" + id="stop3140" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3159" + id="radialGradient3165" + cx="37.931442" + cy="34.408134" + fx="37.931442" + fy="34.408134" + r="35.324765" + gradientTransform="matrix(1,0,0,0.956027,0,1.7894602)" + gradientUnits="userSpaceOnUse" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="2.8" + inkscape:cx="61.305799" + inkscape:cy="40.157191" + inkscape:document-units="px" + inkscape:current-layer="layer1" + width="64px" + height="64px" + inkscape:window-width="765" + inkscape:window-height="575" + inkscape:window-x="785" + inkscape:window-y="236" /> + <metadata + id="metadata7"> + <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 + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <path + sodipodi:type="star" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.27731241;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1.0;fill:url(#radialGradient3165)" + id="path2160" + sodipodi:sides="5" + sodipodi:cx="43.214287" + sodipodi:cy="44" + sodipodi:r1="35.801678" + sodipodi:r2="14.088029" + sodipodi:arg1="-2.8166875" + sodipodi:arg2="-2.1460669" + inkscape:flatsided="false" + inkscape:rounded="0" + inkscape:randomized="0" + d="M 9.2857168,32.571428 L 35.549528,32.179508 L 43.599,8.2003894 L 52.087702,33.057648 L 77.380623,33.303196 L 56.363118,49.057747 L 63.94553,73.188622 L 42.467296,58.068211 L 21.860564,72.736365 L 29.60379,47.636886 L 9.2857168,32.571428 z " + transform="matrix(0.793397,0,0,0.7725297,-2.475811,0.6853635)" /> + </g> +</svg> diff --git a/app/bin/bitmaps/XCF/bluedot.xcf b/app/bin/bitmaps/XCF/bluedot.xcf Binary files differnew file mode 100644 index 0000000..30d323e --- /dev/null +++ b/app/bin/bitmaps/XCF/bluedot.xcf diff --git a/app/bin/bitmaps/XCF/greendot.xcf b/app/bin/bitmaps/XCF/greendot.xcf Binary files differnew file mode 100644 index 0000000..f59311a --- /dev/null +++ b/app/bin/bitmaps/XCF/greendot.xcf diff --git a/app/bin/bitmaps/XCF/greydot.xcf b/app/bin/bitmaps/XCF/greydot.xcf Binary files differnew file mode 100644 index 0000000..7e795a5 --- /dev/null +++ b/app/bin/bitmaps/XCF/greydot.xcf diff --git a/app/bin/bitmaps/XCF/reddot.xcf b/app/bin/bitmaps/XCF/reddot.xcf Binary files differnew file mode 100644 index 0000000..449581f --- /dev/null +++ b/app/bin/bitmaps/XCF/reddot.xcf diff --git a/app/bin/bitmaps/XCF/yellowdot.xcf b/app/bin/bitmaps/XCF/yellowdot.xcf Binary files differnew file mode 100644 index 0000000..9395645 --- /dev/null +++ b/app/bin/bitmaps/XCF/yellowdot.xcf diff --git a/app/bin/bitmaps/arrow0.xbm b/app/bin/bitmaps/arrow0.xbm index 60fb2aa..f07a9e4 100644 --- a/app/bin/bitmaps/arrow0.xbm +++ b/app/bin/bitmaps/arrow0.xbm @@ -1,9 +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}; +static unsigned 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, 0x18, 0x00, 0x00, 0x24, 0x00, +0x00, 0x42, 0x00, 0x00, 0x81, 0x00, 0x80, 0x00, 0x01, 0xc0, 0xe7, 0x03, +0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, +0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x3c, 0x00}; diff --git a/app/bin/bitmaps/arrow0_ctl.xbm b/app/bin/bitmaps/arrow0_ctl.xbm new file mode 100644 index 0000000..3b535c4 --- /dev/null +++ b/app/bin/bitmaps/arrow0_ctl.xbm @@ -0,0 +1,9 @@ +#define arrow0_ctl_width 24 +#define arrow0_ctl_height 24 +static unsigned char arrow0_ctl_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, 0x18, 0x00, 0x00, 0x3c, 0x00, +0x00, 0x7e, 0x00, 0x00, 0xff, 0x00, 0x80, 0xff, 0x01, 0xc0, 0xff, 0x03, +0x00, 0x24, 0x00, 0x00, 0x24, 0x06, 0x00, 0x24, 0x09, 0x00, 0x24, 0x01, +0x00, 0x24, 0x01, 0x00, 0x24, 0x09, 0x00, 0x24, 0x06, 0x00, 0x3c, 0x00};
\ No newline at end of file diff --git a/app/bin/bitmaps/arrow0_shift.xbm b/app/bin/bitmaps/arrow0_shift.xbm new file mode 100644 index 0000000..683f7e3 --- /dev/null +++ b/app/bin/bitmaps/arrow0_shift.xbm @@ -0,0 +1,9 @@ +#define arrow0_shift_width 24 +#define arrow0_shift_height 24 +static unsigned char arrow0_shift_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, 0x18, 0x00, 0x00, 0x24, 0x00, 0x00, 0x5a, 0x00, 0x00, 0xa5, 0x00, +0x80, 0x42, 0x01, 0x40, 0x81, 0x02, 0xa0, 0x00, 0x05, 0xd0, 0xe7, 0x0b, +0x10, 0x24, 0x08, 0xe0, 0xa5, 0x67, 0x00, 0xa5, 0x90, 0x00, 0xa5, 0x10, +0x00, 0xa5, 0x60, 0x00, 0xa5, 0x80, 0x00, 0xa5, 0x90, 0x00, 0xbd, 0x60};
\ No newline at end of file diff --git a/app/bin/bitmaps/arrow3.xbm b/app/bin/bitmaps/arrow3.xbm index 5f85bc0..aeac91f 100644 --- a/app/bin/bitmaps/arrow3.xbm +++ b/app/bin/bitmaps/arrow3.xbm @@ -1,6 +1,6 @@ #define arrow3_width 24 #define arrow3_height 24 -static char arrow3_bits[] = { +static unsigned 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, diff --git a/app/bin/bitmaps/arrow3_ctl.xbm b/app/bin/bitmaps/arrow3_ctl.xbm new file mode 100644 index 0000000..e87279a --- /dev/null +++ b/app/bin/bitmaps/arrow3_ctl.xbm @@ -0,0 +1,9 @@ +#define arrow3_ctl_width 24 +#define arrow3_ctl_height 24 +static unsigned char arrow3_ctl_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, 0xc4, 0x01, 0x00, 0xe4, 0x03, +0xc0, 0xd4, 0x07, 0x20, 0x8d, 0x0f, 0x20, 0x04, 0x1f, 0x20, 0x00, 0x3e, +0x20, 0x00, 0x7c, 0x20, 0x01, 0xf8, 0xc0, 0x00, 0x70, 0x00, 0x00, 0x20 };
\ No newline at end of file diff --git a/app/bin/bitmaps/arrow3_shift.xbm b/app/bin/bitmaps/arrow3_shift.xbm new file mode 100644 index 0000000..d2ee571 --- /dev/null +++ b/app/bin/bitmaps/arrow3_shift.xbm @@ -0,0 +1,9 @@ +#define arrow3_shift_width 24 +#define arrow3_shift_height 24 +static unsigned char arrow3_shift_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, 0x0f, 0x00, 0x01, 0x10, 0x00, 0xfd, 0x17, 0x00, 0xfd, 0x0b, +0x00, 0xfd, 0x05, 0x00, 0xfd, 0x02, 0x00, 0xfd, 0x05, 0x00, 0xfd, 0x0b, +0x30, 0xdd, 0x17, 0x48, 0xad, 0x2f, 0x08, 0x55, 0x5f, 0x30, 0x89, 0xbe, +0x40, 0x06, 0x7d, 0x48, 0x00, 0xfa, 0x30, 0x00, 0x74, 0x00, 0x00, 0xa8};
\ No newline at end of file diff --git a/app/bin/bitmaps/arrowr3.xbm b/app/bin/bitmaps/arrowr3.xbm new file mode 100644 index 0000000..e63a39b --- /dev/null +++ b/app/bin/bitmaps/arrowr3.xbm @@ -0,0 +1,9 @@ +#define arrowr3_width 24 +#define arrowr3_height 24 +static unsigned char arrowr3_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, 0xe0, 0x3f, 0x00, 0xc0, 0x3f, 0x00, +0x80, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x80, 0x3f, 0x00, 0xc0, 0x3f, 0x00, +0xe0, 0x3b, 0x00, 0xf0, 0x31, 0x00, 0xf8, 0x20, 0x00, 0x7c, 0x00, 0x00, +0x3e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x04, 0x00, 0x00};
\ No newline at end of file diff --git a/app/bin/bitmaps/arrowr3_ctl.xbm b/app/bin/bitmaps/arrowr3_ctl.xbm new file mode 100644 index 0000000..f7bd770 --- /dev/null +++ b/app/bin/bitmaps/arrowr3_ctl.xbm @@ -0,0 +1,9 @@ +#define arrowr3_ctl_width 24 +#define arrowr3_ctl_height 24 +static unsigned char arrowr3_ctl_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, 0xe0, 0x3f, 0x00, 0x40, 0x20, 0x00, +0x80, 0x20, 0x00, 0x00, 0x21, 0x00, 0x80, 0x23, 0x00, 0xc0, 0x27, 0x00, +0xe0, 0x2b, 0x00, 0xf0, 0x31, 0x06, 0xf8, 0x20, 0x09, 0x7c, 0x00, 0x01, +0x3e, 0x00, 0x01, 0x1f, 0x00, 0x09, 0x0e, 0x00, 0x06, 0x04, 0x00, 0x00};
\ No newline at end of file diff --git a/app/bin/bitmaps/arrowr3_shift.xbm b/app/bin/bitmaps/arrowr3_shift.xbm new file mode 100644 index 0000000..1b10ea9 --- /dev/null +++ b/app/bin/bitmaps/arrowr3_shift.xbm @@ -0,0 +1,9 @@ +#define arrowr3_shift_width 24 +#define arrowr3_shift_height 24 +static unsigned char arrowr3_shift_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, +0xf0, 0xff, 0x00, 0x08, 0x80, 0x00, 0xe8, 0xbf, 0x00, 0xd0, 0xbf, 0x00, +0xa0, 0xbf, 0x00, 0x40, 0xbf, 0x00, 0xa0, 0xbf, 0x00, 0xd0, 0xbf, 0x00, +0xe8, 0xbb, 0x00, 0xf4, 0xb5, 0x0c, 0xfa, 0xaa, 0x12, 0x7d, 0x91, 0x02, +0xbe, 0x60, 0x0c, 0x5f, 0x00, 0x10, 0x2e, 0x00, 0x12, 0x15, 0x00, 0x0c};
\ No newline at end of file diff --git a/app/bin/bitmaps/arrows.xbm b/app/bin/bitmaps/arrows.xbm index 494b8de..7ac3113 100644 --- a/app/bin/bitmaps/arrows.xbm +++ b/app/bin/bitmaps/arrows.xbm @@ -1,7 +1,7 @@ #define arrows_width 24 #define arrows_height 24 // static unsigned char arrows_bits[] = { -static char arrows_bits[] = { +static unsigned 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, diff --git a/app/bin/bitmaps/background.xpm b/app/bin/bitmaps/background.xpm new file mode 100644 index 0000000..4859734 --- /dev/null +++ b/app/bin/bitmaps/background.xpm @@ -0,0 +1,155 @@ +/* XPM */
+static char *background[] = {
+/* columns rows colors chars-per-pixel */
+"16 16 133 2 ",
+" c #0B2B2B",
+". c #183A3A",
+"X c #789757",
+"o c #799358",
+"O c #7E9068",
+"+ c #86A068",
+"@ c #9BA77A",
+"# c #99B073",
+"$ c #9DB777",
+"% c #98B67E",
+"& c #A6BD7F",
+"* c #A4BE7E",
+"= c #77AAA8",
+"- c #5FA7D7",
+"; c #58A1DB",
+": c #59A2DB",
+"> c #5AA3DA",
+", c #5BA4DB",
+"< c #5CA4D9",
+"1 c #5DA5DB",
+"2 c #66ACD3",
+"3 c #62A9D5",
+"4 c #69AED1",
+"5 c #60A9DB",
+"6 c #63ACDB",
+"7 c #64ACDA",
+"8 c #66AFDB",
+"9 c #62ACDE",
+"0 c #67B0DC",
+"q c #67B0DE",
+"w c #68B1DB",
+"e c #6BB4DB",
+"r c #6EB7DB",
+"t c #6FBADF",
+"y c #71B9DB",
+"u c #71BADB",
+"i c #74BEDE",
+"p c #6FBAE0",
+"a c #6EB9E3",
+"s c #73BEE3",
+"d c #74BEE0",
+"f c #72BEE4",
+"g c #70BCE7",
+"h c #6BBFFF",
+"j c #7CC9E9",
+"k c #7CCAEC",
+"l c #6EC2FF",
+"z c #6FC4FF",
+"x c #73C8FF",
+"c c #74C9FF",
+"v c #78CDFF",
+"b c #79CEFF",
+"n c #7DD2FF",
+"m c #7ED3FF",
+"M c #809786",
+"N c #84AC98",
+"B c #93A492",
+"V c #A1AD8E",
+"C c #A3B587",
+"Z c #A7B787",
+"A c #ABB584",
+"S c #A2B389",
+"D c #A3B988",
+"F c #A8BA94",
+"G c #ACBD97",
+"H c #B5BC90",
+"J c #8EA8A8",
+"K c #97BEAB",
+"L c #8BADB7",
+"P c #80B2B8",
+"I c #91B5B6",
+"U c #AFBFA9",
+"Y c #A4C182",
+"T c #B5C987",
+"R c #B2C088",
+"E c #BAD08E",
+"W c #AFC491",
+"Q c #B6CB97",
+"! c #BED190",
+"~ c #AFCEAA",
+"^ c #BBCAA0",
+"/ c #BECFA1",
+"( c #BDCEA7",
+") c #B5C7A9",
+"_ c #BFD0AE",
+"` c #BFD1AF",
+"' c #A8CDBF",
+"] c #C2CC95",
+"[ c #C7D798",
+"{ c #C3D19D",
+"} c #C6D898",
+"| c #CDD8A6",
+" . c #D0DDA6",
+".. c #C4DCB6",
+"X. c #D3E0A2",
+"o. c #D0E4B9",
+"O. c #DBE5B9",
+"+. c #D9E5BD",
+"@. c #E2ECB3",
+"#. c #E4EDB0",
+"$. c #87B8C5",
+"%. c #96CFDB",
+"&. c #9CD1D5",
+"*. c #98D1DF",
+"=. c #A4CDC4",
+"-. c #ADD7D0",
+";. c #B0D1D5",
+":. c #82D6FF",
+">. c #83D7FF",
+",. c #83D8FF",
+"<. c #8CDEFF",
+"1. c #95DEFC",
+"2. c #99E1F6",
+"3. c #A8E4ED",
+"4. c #B6E6E2",
+"5. c #BDEAE6",
+"6. c #ADE7F8",
+"7. c #E2EBCA",
+"8. c #E2F3F3",
+"9. c #E4F4F4",
+"0. c #E6F5F5",
+"q. c #E9F6F6",
+"w. c #EBF7F7",
+"e. c #E9F9F9",
+"r. c #EEF8F8",
+"t. c #F1F9F9",
+"y. c #F4FBFB",
+"u. c #F7FCFC",
+"i. c #F9FDFD",
+"p. c #FCFEFE",
+"a. c #FEFFFF",
+"s. c #FFFFFF",
+"d. c None",
+/* pixels */
+"d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.",
+"d.s.s.s.s.s.s.s.s.s.s.s.s.s.d.d.",
+"d.s.; ; , < 5 6 7 w e r r s.d.d.",
+"d.s.; l l 9 x v a m ,.1.k s.d.d.",
+"d.i.< z x q b m j ,.3.5.-.i.d.d.",
+"d.u.- 7 q g p s %.*.%.=.{ u.d.d.",
+"d.y.3 b m t :.5.| @.o...K u.d.d.",
+"d.t.2 m :.i 2.#._ .} ! = t.d.d.",
+"d.w.4 u i k ' ] / W * $ = w.d.d.",
+"d.q.$.<.6.;.7.+.^ ~ D % N q.d.d.",
+"d.w.H O. .R T * D _ ) F I q.d.d.",
+"d.q.A [ ! # Q ( F F ) V L 0.d.d.",
+"d.0.@ Z C S + X o O B M J 0.d.d.",
+". e.8.8.8.8.8.8.8.8.8.8.8.e.. d.",
+"d. d.d.",
+"d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d."
+};
diff --git a/app/bin/bitmaps/bluedot.xpm b/app/bin/bitmaps/bluedot.xpm new file mode 100644 index 0000000..5c1df4c --- /dev/null +++ b/app/bin/bitmaps/bluedot.xpm @@ -0,0 +1,26 @@ +/* XPM */
+static char * bluedot[] = {
+"16 16 7 1",
+" c None",
+". c #000000",
+"+ c #3465A4",
+"@ c #3565A4",
+"# c #4465A1",
+"$ c #5A649B",
+"% c #4C79BA",
+" ",
+" ",
+" .... ",
+" ..#@@#.. ",
+" .$%%%+++$. ",
+" .%%%%%+++. ",
+" .#%%%%%+++#. ",
+" .@%%%%%+++@. ",
+" .@+%%%++++@. ",
+" .#++++++++#. ",
+" .++++++++. ",
+" .$++++++$. ",
+" ..#@@#.. ",
+" .... ",
+" ",
+" "};
diff --git a/app/bin/bitmaps/bma0.xbm b/app/bin/bitmaps/bma0.xbm index e0a2815..6986b0f 100644 --- a/app/bin/bitmaps/bma0.xbm +++ b/app/bin/bitmaps/bma0.xbm @@ -1,6 +1,6 @@ #define bma0_width 16 #define bma0_height 16 -static char bma0_bits[] = { +static unsigned 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 index e0c5f4a..5a3ffcb 100644 --- a/app/bin/bitmaps/bma135.xbm +++ b/app/bin/bitmaps/bma135.xbm @@ -1,6 +1,6 @@ #define bma135_width 16 #define bma135_height 16 -static char bma135_bits[] = { +static unsigned 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 index c4717b4..6a943f0 100644 --- a/app/bin/bitmaps/bma45.xbm +++ b/app/bin/bitmaps/bma45.xbm @@ -1,6 +1,6 @@ #define bma45_width 16 #define bma45_height 16 -static char bma45_bits[] = { +static unsigned 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 index cf03ee3..007a8d5 100644 --- a/app/bin/bitmaps/bma90.xbm +++ b/app/bin/bitmaps/bma90.xbm @@ -1,6 +1,6 @@ #define bma90_width 16 #define bma90_height 16 -static char bma90_bits[] = { +static unsigned 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 index 1bea7b7..7572d17 100644 --- a/app/bin/bitmaps/bmendpt.xbm +++ b/app/bin/bitmaps/bmendpt.xbm @@ -1,6 +1,6 @@ #define bmendpt_width 16 #define bmendpt_height 16 -static char bmendpt_bits[] = { +static unsigned 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/bridge.xpm b/app/bin/bitmaps/bridge.xpm new file mode 100644 index 0000000..446f055 --- /dev/null +++ b/app/bin/bitmaps/bridge.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * bridge_xpm[] = { +"16 16 3 1", +" c None", +". c #00FFFF", +"+ c #000000", +".. ..", +" .. .. ", +" ............ ", +" .......... ", +"+ + + + ", +"+++++++++++++++ ", +"+ + + + ", +"+ + + + ", +"+ + + + ", +"+++++++++++++++ ", +"+ + + + ", +" .......... ", +" ............ ", +" .. .. ", +".. ..", +" "}; diff --git a/app/bin/bitmaps/clip.xbm b/app/bin/bitmaps/clip.xbm new file mode 100644 index 0000000..6bffd55 --- /dev/null +++ b/app/bin/bitmaps/clip.xbm @@ -0,0 +1,6 @@ +#define clip_width 16 +#define clip_height 16 +static unsigned char clip_bits[] = { + 0xff, 0x03, 0x01, 0x06, 0x01, 0x0a, 0x01, 0x12, 0x01, 0x22, 0x01, 0x7e, + 0x01, 0x40, 0xf9, 0x5f, 0x05, 0x60, 0x13, 0x60, 0xf3, 0x7f, 0x05, 0x60, + 0xf9, 0x5f, 0x01, 0x40, 0x01, 0x40, 0xff, 0x7f }; diff --git a/app/bin/bitmaps/convertfr.xpm b/app/bin/bitmaps/convertfr.xpm new file mode 100644 index 0000000..7f141c8 --- /dev/null +++ b/app/bin/bitmaps/convertfr.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * convertfr_xpm[] = { +"16 16 4 1", +" c None", +"! c #000000000000", +"# c #FFFF00000000", +"$ c #808080000000", +" !!! !!!! ", +" !! !!! !!", +" !! !! !! ", +" !!!! # !!! ", +" # ", +" ## # ## ", +" ####### ", +" ### ", +" # ", +" ", +" !!!!!! ", +" !! ", +" !!!! ", +" !! ", +" !! ", +" "};
\ No newline at end of file diff --git a/app/bin/bitmaps/convertto.xpm b/app/bin/bitmaps/convertto.xpm new file mode 100644 index 0000000..f0fead2 --- /dev/null +++ b/app/bin/bitmaps/convertto.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * convertto_xpm[] = { +"16 16 4 1", +" c None", +"! c #000000000000", +"# c #FFFF00000000", +"$ c #808080000000", +" !!! !!!! ", +" !! !!! !!", +" !! !! !! ", +" !!!! # !!! ", +" ### ", +" ####### ", +" ## # ## ", +" # ", +" # ", +" ", +" !!!!!! ", +" !! ", +" !!!! ", +" !! ", +" !! ", +" "};
\ No newline at end of file diff --git a/app/bin/bitmaps/cornu.xpm b/app/bin/bitmaps/cornu.xpm new file mode 100644 index 0000000..bd3a2ed --- /dev/null +++ b/app/bin/bitmaps/cornu.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * cornu_xpm[] = { +"16 16 4 1", +" c None", +"! c #000000000000", +"# c #FFFF00000000", +"$ c #808080000000", +" !!$!!!! ", +" !! $ !! ", +" !! $!!! $$$", +" $! !!$ $$ !!", +" !! $! ! !!", +"!! !! !! !! ", +"! !! $###$ ", +"$$$$ # # ", +"! !! ### ", +"! !! ", +"! !$ ", +"!!$$! ", +"!$ !! ", +"$! $!!!$!!$###", +" !! $ $ # #", +" $!!!!!$!!$###"}; diff --git a/app/bin/bitmaps/cross0.xbm b/app/bin/bitmaps/cross0.xbm index 373d897..8f2e35d 100644 --- a/app/bin/bitmaps/cross0.xbm +++ b/app/bin/bitmaps/cross0.xbm @@ -1,5 +1,5 @@ #define cross0_width 8 #define cross0_height 8 //static unsigned char cross0_bits[] = { -static char cross0_bits[] = { +static unsigned char cross0_bits[] = { 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00, 0x00, 0x00}; diff --git a/app/bin/bitmaps/delete.xpm b/app/bin/bitmaps/delete.xpm index 1e88b80..63b875a 100644 --- a/app/bin/bitmaps/delete.xpm +++ b/app/bin/bitmaps/delete.xpm @@ -1,21 +1,22 @@ /* XPM */ static char * delete_xpm[] = { -"16 16 2 1", -". c None", -" c #000000000000", -" ............ .", -" ......... ..", -".. ...... ...", -"... .... ....", -".. .. .. .. ..", -" .. . ", -".. ... ... ..", -".. .... .... ..", -".. ... ... ..", -" . . . ", -".. . .... . ..", -"... ...... ...", -".. ........ ..", -". ........... .", -" .............", -". ............. "}; +"16 16 3 1", +" c None", +". c #FF0000", +"+ c #000000", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" + .. .. + ", +"++++++....++++++", +" + +..+ + ", +" + +..+ + ", +" + .... + ", +"+++++..++..+++++", +" + .. .. + ", +" .. .. ", +" .. .. ", +" .. .. ", +".. ..", +" "}; diff --git a/app/bin/bitmaps/document-export.xpm b/app/bin/bitmaps/document-export.xpm new file mode 100644 index 0000000..8d632de --- /dev/null +++ b/app/bin/bitmaps/document-export.xpm @@ -0,0 +1,90 @@ +/* XPM */
+static char * export_xpm[] = {
+"16 16 71 1",
+" c None",
+". c #406C98",
+"+ c #DDF0FB",
+"@ c #D4E9F7",
+"# c #D1E7F5",
+"$ c #CEE4F4",
+"% c #CCE2F3",
+"& c #CCE1F2",
+"* c #A7C2DC",
+"= c #D6EBF7",
+"- c #C5DFEF",
+"; c #C1DBED",
+"> c #BCD6EA",
+", c #B6D0E8",
+"' c #B3CDE6",
+") c #B2CCE5",
+"! c #9AB6D2",
+"~ c #E5F6FF",
+"{ c #D7ECF8",
+"] c #C7E1EF",
+"^ c #85A4C2",
+"/ c #D7EDF8",
+"( c #C8E2F0",
+"_ c #D8EEF8",
+": c #CAE4F1",
+"< c #D9EEF9",
+"[ c #CBE5F2",
+"} c #8FAFCF",
+"| c #83A5C7",
+"1 c #DAEFF9",
+"2 c #CDE7F2",
+"3 c #81A3C5",
+"4 c #105293",
+"5 c #DAF0F9",
+"6 c #CEE8F3",
+"7 c #C0DAEB",
+"8 c #AFCCE0",
+"9 c #9AB9D4",
+"0 c #88A9C9",
+"a c #7FA1C3",
+"b c #CCE1F0",
+"c c #155493",
+"d c #DBF0FA",
+"e c #D0EAF4",
+"f c #CFE8F3",
+"g c #D4EAF6",
+"h c #E1F3FD",
+"i c #195793",
+"j c #DBF1FA",
+"k c #D1EBF4",
+"l c #C1DCEB",
+"m c #AFCBE0",
+"n c #98B7D1",
+"o c #84A4C4",
+"p c #799BBD",
+"q c #CBE0EF",
+"r c #1E5993",
+"s c #DCF1FA",
+"t c #D2ECF5",
+"u c #7597B9",
+"v c #275D94",
+"w c #DCF2FB",
+"x c #D3EDF5",
+"y c #84A4C0",
+"z c #6B8DAF",
+"A c #3D71A5",
+"B c #DDF2FB",
+"C c #D4EEF6",
+"D c #D8EDF8",
+"E c #E1F4FD",
+"F c #DFF2FC",
+" ......... ",
+" .+@#$%&&*. ",
+" .=-;>,')!~. ",
+" .{]-;>,'^^^. ",
+" ./(]-;>,')&. ",
+" ._:(]-;>,'&. ",
+" .<[:(]-;>}| ",
+" .12[:(]-;3~4 ",
+" .562[7890ab~c ",
+" .de62fg1h~~~~i",
+" .jke6lmnopq~r ",
+" .stke62[:u~v ",
+" .wxtke62[yzA ",
+" .BCxtke62[D. ",
+" .EBwsjd51<F. ",
+" ............ "};
diff --git a/app/bin/bitmaps/document-exportdxf.xpm b/app/bin/bitmaps/document-exportdxf.xpm new file mode 100644 index 0000000..fc4071b --- /dev/null +++ b/app/bin/bitmaps/document-exportdxf.xpm @@ -0,0 +1,84 @@ +/* XPM */
+static char * export_dxf_xpm[] = {
+"16 16 65 1",
+" c None",
+". c #406C98",
+"+ c #DDF0FB",
+"@ c #D4E9F7",
+"# c #11CC22",
+"$ c #CEE4F4",
+"% c #CCE2F3",
+"& c #CCE1F2",
+"* c #A7C2DC",
+"= c #D6EBF7",
+"- c #C5DFEF",
+"; c #BCD6EA",
+"> c #B6D0E8",
+", c #B3CDE6",
+"' c #9AB6D2",
+") c #E5F6FF",
+"! c #D7ECF8",
+"~ c #C7E1EF",
+"{ c #C1DBED",
+"] c #85A4C2",
+"^ c #D8EEF8",
+"/ c #CAE4F1",
+"( c #D9EEF9",
+"_ c #CBE5F2",
+": c #C8E2F0",
+"< c #8FAFCF",
+"[ c #83A5C7",
+"} c #DAEFF9",
+"| c #CDE7F2",
+"1 c #81A3C5",
+"2 c #105293",
+"3 c #AFCCE0",
+"4 c #9AB9D4",
+"5 c #88A9C9",
+"6 c #7FA1C3",
+"7 c #CCE1F0",
+"8 c #155493",
+"9 c #DBF0FA",
+"0 c #D0EAF4",
+"a c #CFE8F3",
+"b c #D4EAF6",
+"c c #E1F3FD",
+"d c #195793",
+"e c #DBF1FA",
+"f c #D1EBF4",
+"g c #CEE8F3",
+"h c #C1DCEB",
+"i c #AFCBE0",
+"j c #98B7D1",
+"k c #84A4C4",
+"l c #799BBD",
+"m c #CBE0EF",
+"n c #1E5993",
+"o c #DCF1FA",
+"p c #D2ECF5",
+"q c #7597B9",
+"r c #275D94",
+"s c #84A4C0",
+"t c #6B8DAF",
+"u c #3D71A5",
+"v c #DDF2FB",
+"w c #D4EEF6",
+"x c #D8EDF8",
+"y c #E1F4FD",
+"z c #DFF2FC",
+" ......... ",
+" .+@#$%&#*. ",
+" .=-#;>,#'). ",
+" .!~#{;>#]]]. ",
+" .##########. ",
+" .^/#~-{#>,&. ",
+" .(_#:~-#;<[ ",
+" .}|#/:~#{1)2 ",
+" .#####34567)8 ",
+" .90#|ab}c))))d",
+" .ef#ghijklm)n ",
+" .op#0g|#/q)r ",
+" .########stu ",
+" .vw#pf0#|_x. ",
+" .yv#oe9#}(z. ",
+" ............ "};
diff --git a/app/bin/bitmaps/document-import.xpm b/app/bin/bitmaps/document-import.xpm new file mode 100644 index 0000000..28dc3c8 --- /dev/null +++ b/app/bin/bitmaps/document-import.xpm @@ -0,0 +1,92 @@ +/* XPM */
+static char * import_xpm[] = {
+"16 16 73 1",
+" c None",
+". c #406C98",
+"+ c #DDF0FB",
+"@ c #D4E9F7",
+"# c #D1E7F5",
+"$ c #CEE4F4",
+"% c #CCE2F3",
+"& c #CCE1F2",
+"* c #A7C2DC",
+"= c #D6EBF7",
+"- c #C5DFEF",
+"; c #C1DBED",
+"> c #BCD6EA",
+", c #B6D0E8",
+"' c #B3CDE6",
+") c #B2CCE5",
+"! c #9AB6D2",
+"~ c #E5F6FF",
+"{ c #D7ECF8",
+"] c #C7E1EF",
+"^ c #85A4C2",
+"/ c #D7EDF8",
+"( c #C8E2F0",
+"_ c #D8EEF8",
+": c #94B4D1",
+"< c #86A7C9",
+"[ c #AECBE1",
+"} c #D9EEF9",
+"| c #81A3C5",
+"1 c #E4F6FF",
+"2 c #93B2D0",
+"3 c #B6D2E6",
+"4 c #155493",
+"5 c #7FA1C3",
+"6 c #CBE1F0",
+"7 c #DEF2FC",
+"8 c #A5C3DA",
+"9 c #BED9EA",
+"0 c #195793",
+"a c #DFF2FC",
+"b c #CFE7F4",
+"c c #1E5993",
+"d c #799BBD",
+"e c #CAE0EF",
+"f c #A4C2D9",
+"g c #C0DBEB",
+"h c #D4EAF7",
+"i c #DBF1FA",
+"j c #7597B9",
+"k c #8BABC7",
+"l c #B8D4E6",
+"m c #CBE5F2",
+"n c #CAE4F1",
+"o c #D5EBF7",
+"p c #DCF1FA",
+"q c #86A6C1",
+"r c #6F91B2",
+"s c #ACC9DC",
+"t c #CEE8F3",
+"u c #CDE7F2",
+"v c #D6ECF7",
+"w c #DCF2FB",
+"x c #D3EDF5",
+"y c #D2ECF5",
+"z c #D1EBF4",
+"A c #D0EAF4",
+"B c #DDF2FB",
+"C c #D4EEF6",
+"D c #D8EDF8",
+"E c #E1F4FD",
+"F c #DBF0FA",
+"G c #DAF0F9",
+"H c #DAEFF9",
+" ......... ",
+" .+@#$%&&* ",
+" .=-;>,')!~ ",
+" .{]-;>,'^^^. ",
+" ./(]-;>,')&. ",
+" ._:<[-;>,'&. ",
+" .}|123-;>,%. ",
+" 44556789-;>$. ",
+"0~~~~1a/b]-;#. ",
+" ccddeafg(]-h. ",
+" .ij1klmn(]o. ",
+" .pqrstumn(v. ",
+" .wxyzAtumn{. ",
+" .BCxyzAtumD. ",
+" .EBwpiFGH}a. ",
+" ............ "};
diff --git a/app/bin/bitmaps/document-importmod.xpm b/app/bin/bitmaps/document-importmod.xpm new file mode 100644 index 0000000..d0efd02 --- /dev/null +++ b/app/bin/bitmaps/document-importmod.xpm @@ -0,0 +1,71 @@ +/* XPM */ +static char *importmod_xpm[] = { +/* columns rows colors chars-per-pixel */ +"16 16 49 1 ", +" c #27795F", +". c #305173", +"X c #1A6878", +"o c #07900F", +"O c #0E8E14", +"+ c #0B9C17", +"@ c #10971E", +"# c #0BA619", +"$ c #10A51D", +"% c #0DB61C", +"& c #159E22", +"* c #209E2D", +"= c #18A727", +"- c #16B627", +"; c #27AB35", +": c #30AF3B", +"> c #2AB437", +", c #31B63E", +"< c #36BD46", +"1 c #3CC44C", +"2 c #40C94E", +"3 c #44CB54", +"4 c #54DB64", +"5 c #195793", +"6 c #3B6D8D", +"7 c #39798B", +"8 c #3F6E9C", +"9 c #377C97", +"0 c #3D77A6", +"q c #3F7FBC", +"w c #37878B", +"e c #38938E", +"r c #378399", +"t c #3983A7", +"y c #6F91B2", +"u c #789ABC", +"i c #7FA1C3", +"p c #86A6C4", +"a c #96B4D1", +"s c #A7C4DB", +"d c #AECBE1", +"f c #B9D5E7", +"g c #C0DBEB", +"h c #C9E1EF", +"j c #CDE4F2", +"k c #D7EDF8", +"l c #DFF2FC", +"z c #E5F6FF", +"x c None", +/* pixels */ +"xxqqqqqqqqqxxxxx", +"xxt3111;<>sqxxxx", +"xxq1<<:O;<a;qxxx", +"xxq<;;&x=2pppqxx", +"xxq===$o+;>#xxxx", +"xxq=aid=$=,1>txx", +"xxq=ilaf->44>txx", +"x5qiijlsg-31=0xx", +"5zzzzzzkjh1,#rxx", +"x5Xuuhlsf1111exx", +"xxw<uzpf211;:exx", +"xx9;pys1<<;$+9xx", +"xx0@&@=<;;=%#9xx", +"xx8;<;;===$##7xx", +"xx8*<<==+@++#7xx", +"xx.677776667w xx" +}; diff --git a/app/bin/bitmaps/dpolyline.xpm b/app/bin/bitmaps/dpolyline.xpm new file mode 100644 index 0000000..7f01bda --- /dev/null +++ b/app/bin/bitmaps/dpolyline.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * dpolyline_xpm[] = { +"16 16 3 1", +"X c None", +" c #FFFF00000000", +". c #000000000000", +"XXXXXXXXXXXXXXXX", +"XXXXXXX .... XXX", +"XXXXXXXXXX..XXXX", +"XXXXXXXXX.XXXXXX", +"XXXXXXX..XXXXXXX", +"XXXXXX.XXXXXXXXX", +"XXXX..XXXXXXXXXX", +"XXX ...XXXXXXXXX", +"XXXXXXX......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/export.xpm b/app/bin/bitmaps/export.xpm deleted file mode 100644 index f6bc7d3..0000000 --- a/app/bin/bitmaps/export.xpm +++ /dev/null @@ -1,21 +0,0 @@ -/* XPM */ -static char * export_xpm[] = { -"16 16 2 1", -" c None", -". c #000000000000", -" .........", -" . .", -" . . .", -" . . .", -"...... . .", -" .. .", -" .. .", -"...... . .", -" . . .. .", -" . .. . ..", -" . .. ", -" ", -" . . ... .", -" . . .", -" . . . . .", -" "}; diff --git a/app/bin/bitmaps/flash.xbm b/app/bin/bitmaps/flash.xbm index 677978d..d135b2f 100644 --- a/app/bin/bitmaps/flash.xbm +++ b/app/bin/bitmaps/flash.xbm @@ -1,6 +1,6 @@ #define flash_width 24 #define flash_height 24 -static char flash_bits[] = { +static unsigned 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, diff --git a/app/bin/bitmaps/greendot.xpm b/app/bin/bitmaps/greendot.xpm new file mode 100644 index 0000000..8c44035 --- /dev/null +++ b/app/bin/bitmaps/greendot.xpm @@ -0,0 +1,23 @@ +/* XPM */
+static char * greendot[] = {
+"16 16 4 1",
+" c None",
+". c #000000",
+"+ c #4E9A06",
+"@ c #59A51A",
+" ",
+" ",
+" .... ",
+" ..++++.. ",
+" .+@@@++++. ",
+" .@@@@@+++. ",
+" .+@@@@@++++. ",
+" .+@@@@@++++. ",
+" .++@@@+++++. ",
+" .++++++++++. ",
+" .++++++++. ",
+" .++++++++. ",
+" ..++++.. ",
+" .... ",
+" ",
+" "};
diff --git a/app/bin/bitmaps/greenstar.xpm b/app/bin/bitmaps/greenstar.xpm new file mode 100644 index 0000000..b83a4b9 --- /dev/null +++ b/app/bin/bitmaps/greenstar.xpm @@ -0,0 +1,69 @@ +/* XPM */
+static char * greenstar[] = {
+"16 16 50 1",
+" c None",
+". c #264706",
+"+ c #274906",
+"@ c #346408",
+"# c #386C09",
+"$ c #478A0D",
+"% c #46880C",
+"& c #224007",
+"* c #131D0A",
+"= c #234207",
+"- c #274B06",
+"; c #2D5306",
+"> c #305A07",
+", c #3A700A",
+"' c #4D960E",
+") c #4A900D",
+"! c #356508",
+"~ c #264806",
+"{ c #203B07",
+"] c #1C3508",
+"^ c #203C07",
+"/ c #42800C",
+"( c #478B0D",
+"_ c #529E0F",
+": c #4C940E",
+"< c #46890D",
+"[ c #417E0B",
+"} c #3C730A",
+"| c #1B3208",
+"1 c #346208",
+"2 c #4B920E",
+"3 c #498F0D",
+"4 c #45850C",
+"5 c #407C0B",
+"6 c #315B07",
+"7 c #336108",
+"8 c #44840C",
+"9 c #2E5507",
+"0 c #3F7A0B",
+"a c #3F790B",
+"b c #3C740A",
+"c c #2B5106",
+"d c #1F3808",
+"e c #386B09",
+"f c #234107",
+"g c #2A4F06",
+"h c #182C09",
+"i c #2E5707",
+"j c #1A3008",
+"k c #172B09",
+" ",
+" ",
+" ",
+" .+ ",
+" @# ",
+" $%& ",
+" *=-;>,')!~={] ",
+" ^#/('_:<[}@| ",
+" 1%)23456 ",
+" 74%8[9 ",
+" ,05abc ",
+" deef;e1 ",
+" &gh di| ",
+" j k ",
+" ",
+" "};
diff --git a/app/bin/bitmaps/greydot.xpm b/app/bin/bitmaps/greydot.xpm new file mode 100644 index 0000000..771a096 --- /dev/null +++ b/app/bin/bitmaps/greydot.xpm @@ -0,0 +1,25 @@ +/* XPM */
+static char * greydot[] = {
+"16 16 6 1",
+" c None",
+". c #000000",
+"+ c #30312F",
+"@ c #959792",
+"# c #888A85",
+"$ c #A2A49F",
+" ",
+" ",
+" .... ",
+" .+@@@@+. ",
+" .#$$$@@@#. ",
+" +$$$$$@@@+ ",
+" .@$$$$$@@@@. ",
+" .@$$$$$@@@@. ",
+" .@@$$$@@@@@. ",
+" .@@@@@@@@@@. ",
+" +@@@@@@@@+ ",
+" .#@@@@@@#. ",
+" .+@@@@+. ",
+" .... ",
+" ",
+" "};
diff --git a/app/bin/bitmaps/greystar.xpm b/app/bin/bitmaps/greystar.xpm new file mode 100644 index 0000000..e7c5300 --- /dev/null +++ b/app/bin/bitmaps/greystar.xpm @@ -0,0 +1,69 @@ +/* XPM */
+static char * greystar[] = {
+"16 16 50 1",
+" c None",
+". c #5A5B57",
+"+ c #5B5D59",
+"@ c #6D6F6A",
+"# c #73756F",
+"$ c #878984",
+"% c #868882",
+"& c #555652",
+"* c #3D3F3C",
+"= c #565854",
+"- c #5C5E59",
+"; c #62645F",
+"> c #676964",
+", c #757772",
+"' c #8F918C",
+") c #8B8D88",
+"! c #6E706A",
+"~ c #5A5C58",
+"{ c #51534F",
+"] c #4D4F4B",
+"^ c #525450",
+"/ c #81837D",
+"( c #888A84",
+"_ c #959792",
+": c #8E908A",
+"< c #868983",
+"[ c #7F817B",
+"} c #787A74",
+"| c #4C4D49",
+"1 c #6C6E69",
+"2 c #8D8F8A",
+"3 c #8B8D87",
+"4 c #848680",
+"5 c #7D807A",
+"6 c #686A65",
+"7 c #6B6D68",
+"8 c #83857F",
+"9 c #646561",
+"0 c #7D7F79",
+"a c #7C7E78",
+"b c #787B75",
+"c c #60625D",
+"d c #50514D",
+"e c #72746E",
+"f c #565753",
+"g c #5F615D",
+"h c #474945",
+"i c #646661",
+"j c #4A4B48",
+"k c #474844",
+" ",
+" ",
+" ",
+" .+ ",
+" @# ",
+" $%& ",
+" *=-;>,')!~={] ",
+" ^#/('_:<[}@| ",
+" 1%)23456 ",
+" 74%8[9 ",
+" ,05abc ",
+" deef;e1 ",
+" &gh di| ",
+" j k ",
+" ",
+" "};
diff --git a/app/bin/bitmaps/import.xpm b/app/bin/bitmaps/import.xpm deleted file mode 100644 index f048333..0000000 --- a/app/bin/bitmaps/import.xpm +++ /dev/null @@ -1,21 +0,0 @@ -/* XPM */ -static char * import_xpm[] = { -"16 16 2 1", -" c None", -". c #FFFFFFFFFFFF", -" .......", -" ....... .......", -" ....... ... ...", -" ....... .... ..", -" ....... . .", -" ....... ...... ", -" ....... ...... ", -" ....... . .", -" . .... .... ..", -" .. .. ... ...", -" .... .........", -"................", -".. . . . .....", -"... ... .. .....", -" . . .. .. .....", -"................"}; diff --git a/app/bin/bitmaps/joinline.xpm b/app/bin/bitmaps/joinline.xpm new file mode 100644 index 0000000..06e22f8 --- /dev/null +++ b/app/bin/bitmaps/joinline.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * joinline_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ", +" ", +" ", +" .", +" ..", +" ...", +" ... ", +" ... ", +" XXX ", +" X X ", +" X X X ", +".....X X ", +".....XXXX ", +"..... X ", +" X ", +" "};
\ No newline at end of file diff --git a/app/bin/bitmaps/link.xbm b/app/bin/bitmaps/link.xbm new file mode 100644 index 0000000..199256e --- /dev/null +++ b/app/bin/bitmaps/link.xbm @@ -0,0 +1,6 @@ +#define link_width 16 +#define link_height 16 +static unsigned char link_bits[] = { + 0xff, 0x03, 0x01, 0x06, 0x01, 0x0a, 0x01, 0x12, 0x01, 0x22, 0x01, 0x7e, + 0x01, 0x40, 0x01, 0x40, 0x3d, 0x5e, 0x43, 0x61, 0xf3, 0x67, 0x43, 0x61, + 0x3d, 0x5e, 0x01, 0x40, 0x01, 0x40, 0xff, 0x7f }; diff --git a/app/bin/bitmaps/magnet.xpm b/app/bin/bitmaps/magnet.xpm new file mode 100644 index 0000000..99a31db --- /dev/null +++ b/app/bin/bitmaps/magnet.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * magnet_xpm[] = { +"16 16 3 1", +" c #FF0000", +". c None", +"X c #FFFF00", +"................", +"........ ......", +"....... ....", +"...... ...", +"..... ..", +"..... . .", +".... ... .", +"... ... .", +"..XXX ... ..", +".XXXXX... ..", +"..XXXX.. ...", +"...XX..XX ....", +"......XXXXX ....", +"......XXXXX.....", +"........XX......", +"................"}; diff --git a/app/bin/bitmaps/magnifier.xpm b/app/bin/bitmaps/magnifier.xpm new file mode 100644 index 0000000..69a3faa --- /dev/null +++ b/app/bin/bitmaps/magnifier.xpm @@ -0,0 +1,89 @@ +/* XPM */
+static char * magnifier_xpm[] = {
+"16 16 70 1",
+" c None",
+". c #545454",
+"+ c #555555",
+"@ c #515151",
+"# c #5E6063",
+"$ c #94A3B1",
+"% c #C5D5E6",
+"& c #DFEAF4",
+"* c #D9E3ED",
+"= c #A2ACB6",
+"- c #4D4D4D",
+"; c #5A5D5F",
+"> c #AEC1D5",
+", c #C4D8EB",
+"' c #E2ECF6",
+") c #E4EDF6",
+"! c #B8C8D9",
+"~ c #5B5D60",
+"{ c #494949",
+"] c #919FAD",
+"^ c #BBD2E8",
+"/ c #D5E3F1",
+"( c #D6E4F2",
+"_ c #97A4B0",
+": c #434343",
+"< c #B6CBE0",
+"[ c #C1D3E4",
+"} c #3E3E3E",
+"| c #BED4E9",
+"1 c #C8DBED",
+"2 c #383838",
+"3 c #BCCFE1",
+"4 c #CAD8E7",
+"5 c #313131",
+"6 c #939DA8",
+"7 c #BFD5EA",
+"8 c #DFE9F5",
+"9 c #9EA6AD",
+"0 c #373737",
+"a c #444647",
+"b c #C4D1DE",
+"c c #D9E6F3",
+"d c #E6EFF7",
+"e c #D3D9DF",
+"f c #3B3C3D",
+"g c #262626",
+"h c #3B3B3B",
+"i c #3C3C3C",
+"j c #ADADAF",
+"k c #28292B",
+"l c #91979D",
+"m c #E3E8EE",
+"n c #EDF3F9",
+"o c #E5EAEF",
+"p c #9EA0A3",
+"q c #282829",
+"r c #464647",
+"s c #B8B8BC",
+"t c #151516",
+"u c #141414",
+"v c #3D3D3D",
+"w c #515153",
+"x c #C4C4CC",
+"y c #212122",
+"z c #606064",
+"A c #D1D1DD",
+"B c #2E2E30",
+"C c #DADAEA",
+"D c #3F3F43",
+"E c #151515",
+" .+++++. ",
+" @#$%&*=#@ ",
+" -;>,'))'!~-",
+" {]^/))))(_{",
+" :<^^^^^^^[:",
+" }|^^)))))1}",
+" 23^))))))42",
+" 5678))))'95",
+" 0abc)))defg",
+" hijklmnopq ",
+" irst uuuuu ",
+" vwxy ",
+" }zAB ",
+" 0CD ",
+" Eu ",
+" "};
diff --git a/app/bin/bitmaps/note.xbm b/app/bin/bitmaps/note.xbm index 7ca281a..3d2cac9 100644 --- a/app/bin/bitmaps/note.xbm +++ b/app/bin/bitmaps/note.xbm @@ -1,6 +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}; +static unsigned char note_bits[] = { + 0xff, 0x03, 0x01, 0x06, 0x01, 0x0a, 0x01, 0x12, 0x01, 0x22, 0x01, 0x7e, + 0x01, 0x40, 0xbd, 0x43, 0x01, 0x40, 0x01, 0x40, 0xfd, 0x5e, 0x01, 0x40, + 0x01, 0x40, 0xbd, 0x4f, 0x01, 0x40, 0xff, 0x7f }; diff --git a/app/bin/bitmaps/pan.xpm b/app/bin/bitmaps/pan.xpm index 8782714..9575ec5 100644 --- a/app/bin/bitmaps/pan.xpm +++ b/app/bin/bitmaps/pan.xpm @@ -14,7 +14,7 @@ static char * pan_xpm[] = { " XXXXXXXXXXXXXX ", " XXXXXXXXXXXXXX ", " XX XX XX ", -" XX ", +" XX ", " XX XX XX ", " XXXXXX ", " XXXX ", diff --git a/app/bin/bitmaps/parallel-line.xpm b/app/bin/bitmaps/parallel-line.xpm new file mode 100644 index 0000000..4ac471d --- /dev/null +++ b/app/bin/bitmaps/parallel-line.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * parallel_line_xpm[] = { +"16 16 3 1", +" c None", +". c #000000000000", +"X c #FFFF00000000", +" ", +" ", +" ", +"................", +" X ", +" X X ", +" X X ", +" ", +" ", +" . . . ", +"................", +" . . . ", +" . . . ", +" . . . ", +"................", +" . . . "}; diff --git a/app/bin/bitmaps/parallel.xpm b/app/bin/bitmaps/parallel.xpm index 3fe5591..eb816dc 100644 --- a/app/bin/bitmaps/parallel.xpm +++ b/app/bin/bitmaps/parallel.xpm @@ -1,15 +1,16 @@ /* XPM */ static char * parallel_xpm[] = { -"16 16 2 1", +"16 16 3 1", " c None", ". c #000000000000", -" ", -" ", -" ", +"X c #FFFF00000000", +"................", +" . . . ", +" . . . ", "................", -" . ", -" . . ", -" . . ", +" . X . ", +" X X ", +" X X ", " ", " ", " . . . ", diff --git a/app/bin/bitmaps/reddot.xpm b/app/bin/bitmaps/reddot.xpm new file mode 100644 index 0000000..14529bf --- /dev/null +++ b/app/bin/bitmaps/reddot.xpm @@ -0,0 +1,26 @@ +/* XPM */
+static char * reddot[] = {
+"16 16 7 1",
+" c None",
+". c #000000",
+"+ c #CC0000",
+"@ c #CA1F1E",
+"# c #E62E16",
+"$ c #C04E4B",
+"% c #B25F5B",
+" ",
+" ",
+" .... ",
+" ..$@@$.. ",
+" .%###+++%. ",
+" .#####+++. ",
+" .$#####+++$. ",
+" .@#####+++@. ",
+" .@+###++++@. ",
+" .$++++++++$. ",
+" .++++++++. ",
+" .%++++++%. ",
+" ..$@@$.. ",
+" .... ",
+" ",
+" "};
diff --git a/app/bin/bitmaps/redstar.xpm b/app/bin/bitmaps/redstar.xpm new file mode 100644 index 0000000..b9f51f9 --- /dev/null +++ b/app/bin/bitmaps/redstar.xpm @@ -0,0 +1,67 @@ +/* XPM */
+static char * redstar[] = {
+"16 16 48 1",
+" c None",
+". c #950800",
+"+ c #980800",
+"@ c #B50A00",
+"# c #BF0A00",
+"$ c #E00C00",
+"% c #DE0C00",
+"& c #8D0800",
+"* c #670600",
+"= c #900800",
+"- c #990800",
+"; c #A30900",
+"> c #AC0900",
+", c #C30B00",
+"' c #ED0D00",
+") c #E70C00",
+"! c #B70A00",
+"~ c #960800",
+"{ c #870700",
+"] c #810700",
+"^ c #890700",
+"/ c #D60C00",
+"( c #E20C00",
+"_ c #F70D00",
+": c #EC0D00",
+"< c #D30B00",
+"[ c #C70B00",
+"} c #7E0700",
+"| c #B40A00",
+"1 c #EA0D00",
+"2 c #DB0C00",
+"3 c #D10B00",
+"4 c #AD0900",
+"5 c #B20A00",
+"6 c #D90C00",
+"7 c #A50900",
+"8 c #D00B00",
+"9 c #CE0B00",
+"0 c #C90B00",
+"a c #A00900",
+"b c #850700",
+"c c #BE0A00",
+"d c #8E0800",
+"e c #9E0900",
+"f c #770600",
+"g c #A70900",
+"h c #7B0700",
+"i c #760600",
+" ",
+" ",
+" ",
+" .+ ",
+" @# ",
+" $%& ",
+" *=-;>,')!~={] ",
+" ^#/('_:$<[@} ",
+" |%)1)234 ",
+" 52%6<7 ",
+" ,8390a ",
+" bccd;c| ",
+" &ef bg} ",
+" h i ",
+" ",
+" "};
diff --git a/app/bin/bitmaps/sticky-note-chain.xpm b/app/bin/bitmaps/sticky-note-chain.xpm new file mode 100644 index 0000000..aa9445a --- /dev/null +++ b/app/bin/bitmaps/sticky-note-chain.xpm @@ -0,0 +1,84 @@ +/* XPM */
+static char * sticky_note_chain_bits[] = {
+"16 16 65 1",
+" c None",
+". c #CCB301",
+"+ c #CAB101",
+"@ c #FBEF9C",
+"# c #F9EB8F",
+"$ c #F8EA8D",
+"% c #F8E98A",
+"& c #F6E785",
+"* c #F3E37C",
+"= c #F0E074",
+"- c #EEDD6F",
+"; c #D5C44D",
+"> c #C8AF01",
+", c #FBED95",
+"' c #F7E67E",
+") c #F6E57C",
+"! c #F5E47B",
+"~ c #F4E379",
+"{ c #F1E075",
+"] c #ECDB70",
+"^ c #E8D76A",
+"/ c #E6D567",
+"( c #CDBC45",
+"_ c #FFF6BB",
+": c #C6AD01",
+"< c #FCEB84",
+"[ c #B7A73A",
+"} c #BFAE37",
+"| c #AC9401",
+"1 c #C3AA01",
+"2 c #FBED97",
+"3 c #EEDD7B",
+"4 c #9D96F6",
+"5 c #E1D26F",
+"6 c #C0A701",
+"7 c #006E6E",
+"8 c #00FFFF",
+"9 c #BCA401",
+"0 c #AFA358",
+"a c #ADA054",
+"b c #B9A101",
+"c c #B59E01",
+"d c #FEF19E",
+"e c #EDDD7C",
+"f c #E5D575",
+"g c #E5D571",
+"h c #B29A01",
+"i c #FFF2A1",
+"j c #FEED87",
+"k c #FDEC86",
+"l c #FDEC85",
+"m c #FBEA82",
+"n c #FAE981",
+"o c #F8E77F",
+"p c #A89100",
+"q c #FFF4AF",
+"r c #FFF1A0",
+"s c #FDF09C",
+"t c #FDEF9B",
+"u c #FCEE99",
+"v c #FAEC92",
+"w c #F9EA90",
+"x c #FAEC96",
+"y c #9D8600",
+"z c #9C8500",
+" ",
+" ",
+" ........... ",
+" +@#$%&*=--;+ ",
+" >,')!~{]^/(_> ",
+" :<<<<<<<<<[}}| ",
+" 1234445444''-1 ",
+" 647887478874-6 ",
+" 94800878a084=9 ",
+" b47887478874*b ",
+" cde444f444g{&c ",
+" hijkl<mno')!%h ",
+" pqirdstu2,vwxp ",
+" yzzzzzzzzzzzzy ",
+" ",
+" "};
diff --git a/app/bin/bitmaps/sticky-note-clip.xpm b/app/bin/bitmaps/sticky-note-clip.xpm new file mode 100644 index 0000000..c74c64d --- /dev/null +++ b/app/bin/bitmaps/sticky-note-clip.xpm @@ -0,0 +1,99 @@ +/* XPM */
+static char * sticky_note_clip_bits[] = {
+"16 16 80 1",
+" c None",
+". c #CCB301",
+"+ c #CAB101",
+"@ c #FBEF9C",
+"# c #F9EB8F",
+"$ c #F8EA8D",
+"% c #F8E98A",
+"& c #F7E992",
+"* c #EDE1A3",
+"= c #555753",
+"- c #DFCF67",
+"; c #D5C44D",
+"> c #C8AF01",
+", c #FBED95",
+"' c #F7E67E",
+") c #F6E57C",
+"! c #F6E68A",
+"~ c #ECE0A0",
+"{ c #BABDB6",
+"] c #EFE3A7",
+"^ c #CDBC45",
+"/ c #FFF6BB",
+"( c #C6AD01",
+"_ c #FBED97",
+": c #F8E77F",
+"< c #F8E88D",
+"[ c #EDE0A0",
+"} c #F5E68F",
+"| c #F0E39B",
+"1 c #888A85",
+"2 c #BFAE37",
+"3 c #AC9401",
+"4 c #C3AA01",
+"5 c #FCEE99",
+"6 c #F7E78B",
+"7 c #EBDE9C",
+"8 c #F8EBA2",
+"9 c #ECDB70",
+"0 c #E8D76A",
+"a c #E6D567",
+"b c #EEDD6F",
+"c c #C0A701",
+"d c #FDF0A5",
+"e c #DCD695",
+"f c #EAE29B",
+"g c #E8E098",
+"h c #DACB69",
+"i c #BCA401",
+"j c #FDF3B6",
+"k c #B9A101",
+"l c #FEF4B7",
+"m c #E0D986",
+"n c #F5E47B",
+"o c #F4E379",
+"p c #F1E075",
+"q c #F3E37C",
+"r c #B59E01",
+"s c #FFF3B4",
+"t c #DECF6F",
+"u c #F4E47D",
+"v c #F5E47C",
+"w c #F6E785",
+"x c #B29A01",
+"y c #FFF2A3",
+"z c #FBEDA4",
+"A c #F0E07C",
+"B c #F3E27C",
+"C c #F5E47D",
+"D c #A89100",
+"E c #FFF4AF",
+"F c #FFF2A1",
+"G c #FFF1A0",
+"H c #FEF19E",
+"I c #FDF09C",
+"J c #FDEF9B",
+"K c #FAEC92",
+"L c #F9EA90",
+"M c #FAEC96",
+"N c #9D8600",
+"O c #9C8500",
+" ",
+" ",
+" ........... ",
+" +@#$%&*==-;+ ",
+" >,')!~={]=^/> ",
+" (_:<[=}=|1{223 ",
+" 4567=8=8=90ab4 ",
+" cde=f=g=}h90bc ",
+" ij1818=8={)))i ",
+" kl=m=1f={nopqk ",
+" rs1t)8={uvnowr ",
+" xyz1=1{ABC)n%x ",
+" DEFGHIJ5_,KLMD ",
+" NOOOOOOOOOOOON ",
+" ",
+" "};
diff --git a/app/bin/bitmaps/sticky-note-text.xpm b/app/bin/bitmaps/sticky-note-text.xpm new file mode 100644 index 0000000..119f39b --- /dev/null +++ b/app/bin/bitmaps/sticky-note-text.xpm @@ -0,0 +1,86 @@ +/* XPM */
+static char * sticky_note_text_bits[] = {
+"16 16 67 1",
+" c None",
+". c #CCB301",
+"+ c #CAB101",
+"@ c #FBEF9C",
+"# c #F9EB8F",
+"$ c #F8EA8D",
+"% c #F8E98A",
+"& c #F6E785",
+"* c #F3E37C",
+"= c #F0E074",
+"- c #EEDD6F",
+"; c #D5C44D",
+"> c #C8AF01",
+", c #FBED95",
+"' c #F7E67E",
+") c #F6E57C",
+"! c #F5E47B",
+"~ c #F4E379",
+"{ c #F1E075",
+"] c #ECDB70",
+"^ c #E8D76A",
+"/ c #E6D567",
+"( c #CDBC45",
+"_ c #FFF6BB",
+": c #C6AD01",
+"< c #FBED97",
+"[ c #F8E77F",
+"} c #BFAE37",
+"| c #AC9401",
+"1 c #C3AA01",
+"2 c #FCEE99",
+"3 c #7E7E7E",
+"4 c #979797",
+"5 c #C0A701",
+"6 c #FDEF9B",
+"7 c #E5DC75",
+"8 c #E4DB73",
+"9 c #E4DA70",
+"0 c #E3D86D",
+"a c #E2D769",
+"b c #EBDC70",
+"c c #BCA401",
+"d c #FDF09D",
+"e c #B9A101",
+"f c #FEF19E",
+"g c #E5DD77",
+"h c #EDDF75",
+"i c #B59E01",
+"j c #FFF1A0",
+"k c #DED273",
+"l c #E1D078",
+"m c #B29A01",
+"n c #FFF2A1",
+"o c #FEED87",
+"p c #FDEC86",
+"q c #FDEC85",
+"r c #FCEB84",
+"s c #FBEA82",
+"t c #FAE981",
+"u c #A89100",
+"v c #FFF4AF",
+"w c #FDF09C",
+"x c #FAEC92",
+"y c #F9EA90",
+"z c #FAEC96",
+"A c #9D8600",
+"B c #9C8500",
+" ",
+" ",
+" ........... ",
+" +@#$%&*=--;+ ",
+" >,')!~{]^/(_> ",
+" :<[')!~{]^}}}| ",
+" 12334344{]^/-1 ",
+" 567890ab~{]^-5 ",
+" cd3334333443=c ",
+" efgg789h)!~{*e ",
+" ij3343kl')!~&i ",
+" mnopqrst[')!%m ",
+" uvnjfw62<,xyzu ",
+" ABBBBBBBBBBBBA ",
+" ",
+" "};
diff --git a/app/bin/bitmaps/tunnel.xpm b/app/bin/bitmaps/tunnel.xpm index 79aed20..449080e 100644 --- a/app/bin/bitmaps/tunnel.xpm +++ b/app/bin/bitmaps/tunnel.xpm @@ -1,19 +1,20 @@ /* XPM */ static char * tunnel_xpm[] = { -"16 16 2 1", +"16 16 3 1", " c None", -". c #000000000000", +". c #00FFFF", +"+ c #000000", " .. ", " .. ", " .. ", " .. ", -". . .. ", -".......... . . ", -". . .. ", -". . .. ", -". . .. ", -".......... . . ", -". . .. ", +"+ + .. ", +"++++++++.. + + ", +"+ + .. ", +"+ + .. ", +"+ + .. ", +"++++++++.. + + ", +"+ + .. ", " .. ", " .. ", " .. ", diff --git a/app/bin/bitmaps/yellowdot.xpm b/app/bin/bitmaps/yellowdot.xpm new file mode 100644 index 0000000..da0dddf --- /dev/null +++ b/app/bin/bitmaps/yellowdot.xpm @@ -0,0 +1,27 @@ +/* XPM */
+static char * yellowdot[] = {
+"16 16 8 1",
+" c None",
+". c #000000",
+"+ c #B69A19",
+"@ c #E0C504",
+"# c #EBD200",
+"$ c #CFB410",
+"% c #EFD947",
+"& c #EDD400",
+" ",
+" ",
+" .... ",
+" .+@##@+. ",
+" .$%%%&&&$. ",
+" +%%%%%&&&+ ",
+" .@%%%%%&&&@. ",
+" .#%%%%%&&&#. ",
+" .#&%%%&&&&#. ",
+" .@&&&&&&&&@. ",
+" +&&&&&&&&+ ",
+" .$&&&&&&$. ",
+" .+@##@+. ",
+" .... ",
+" ",
+" "};
diff --git a/app/bin/bitmaps/yellowstar.xpm b/app/bin/bitmaps/yellowstar.xpm new file mode 100644 index 0000000..637ad9c --- /dev/null +++ b/app/bin/bitmaps/yellowstar.xpm @@ -0,0 +1,67 @@ +/* XPM */
+static char * yellowstar[] = {
+"16 16 48 1",
+" c None",
+". c #AB9600",
+"+ c #AE9900",
+"@ c #D0B600",
+"# c #DBC000",
+"$ c #FFDF02",
+"% c #FFDF00",
+"& c #A28D00",
+"* c #766700",
+"= c #A59000",
+"- c #B09A00",
+"; c #BBA400",
+"> c #C5AC00",
+", c #DFC300",
+"' c #FFE111",
+") c #FFE00A",
+"! c #D2B800",
+"~ c #AC9700",
+"{ c #9B8800",
+"] c #948100",
+"^ c #9D8A00",
+"/ c #F6D700",
+"( c #FFE004",
+"_ c #FFE31D",
+": c #FFE10F",
+"< c #F2D400",
+"[ c #E5C800",
+"} c #917F00",
+"| c #CEB500",
+"1 c #FFE10E",
+"2 c #FCDC00",
+"3 c #F0D200",
+"4 c #C7AE00",
+"5 c #CCB300",
+"6 c #FADB00",
+"7 c #BEA600",
+"8 c #EED100",
+"9 c #ECCF00",
+"0 c #E6CA00",
+"a c #B8A100",
+"b c #988500",
+"c c #DABE00",
+"d c #A38F00",
+"e c #B69F00",
+"f c #897800",
+"g c #BFA700",
+"h c #8D7B00",
+"i c #877600",
+" ",
+" ",
+" ",
+" .+ ",
+" @# ",
+" $%& ",
+" *=-;>,')!~={] ",
+" ^#/('_:$<[@} ",
+" |%)1)234 ",
+" 52%6<7 ",
+" ,8390a ",
+" bccd;c| ",
+" &ef bg} ",
+" h i ",
+" ",
+" "};
diff --git a/app/bin/cJSON.c b/app/bin/cJSON.c new file mode 100755 index 0000000..1733811 --- /dev/null +++ b/app/bin/cJSON.c @@ -0,0 +1,2932 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include <string.h> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <limits.h> +#include <ctype.h> + +#ifdef ENABLE_LOCALES +#include <locale.h> +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { + if (!cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 8) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dillimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occured */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* faile to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return false; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return; + } + + add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return; + } + + add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + add_item_to_array(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + unsigned char *into = (unsigned char*)json; + + if (json == NULL) + { + return; + } + + while (*json) + { + if (*json == ' ') + { + json++; + } + else if (*json == '\t') + { + /* Whitespace characters. */ + json++; + } + else if (*json == '\r') + { + json++; + } + else if (*json=='\n') + { + json++; + } + else if ((*json == '/') && (json[1] == '/')) + { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) + { + json++; + } + } + else if ((*json == '/') && (json[1] == '*')) + { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) + { + json++; + } + json += 2; + } + else if (*json == '\"') + { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char)*json++; + while (*json && (*json != '\"')) + { + if (*json == '\\') + { + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + else + { + /* All other characters. */ + *into++ = (unsigned char)*json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/app/bin/cJSON.h b/app/bin/cJSON.h new file mode 100755 index 0000000..8d45390 --- /dev/null +++ b/app/bin/cJSON.h @@ -0,0 +1,285 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 8 + +#include <stddef.h> + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check if the item is a string and return its valuestring */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/arrray that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + + +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/bin/cbezier.c b/app/bin/cbezier.c index b91a81e..7f90a27 100644 --- a/app/bin/cbezier.c +++ b/app/bin/cbezier.c @@ -104,7 +104,8 @@ static struct { DIST_T trackGauge; } Da; - +static dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) /** * Draw a ControlArm. @@ -233,17 +234,16 @@ double BezErrorLine(coOrd pos[4], coOrd start_point, coOrd end_point, double sta /* * Add element to DYNARR pointed to by caller from segment handed in */ -void addSegBezier(dynArr_t * const array_p, trkSeg_p seg) { +void addSegBezier(dynArr_t * array_p, trkSeg_p seg) { trkSeg_p s; - DYNARR_APPEND(trkSeg_t, * array_p, 1); //Adds 1 to cnt - s = &DYNARR_N(trkSeg_t,* array_p,array_p->cnt-1); + DYNARR_APPEND(trkSeg_t,* array_p, 1); //Adds 1 to cnt + s = &DYNARR_N(trkSeg_t,*array_p,(array_p->cnt)-1); s->type = seg->type; s->color = seg->color; s->width = seg->width; s->bezSegs.cnt = 0; - if (s->bezSegs.ptr) MyFree(s->bezSegs.ptr); s->bezSegs.ptr=NULL; s->bezSegs.max = 0; if ((s->type == SEG_BEZLIN || s->type == SEG_BEZTRK) && seg->bezSegs.cnt) { @@ -394,15 +394,15 @@ EXPORT BOOL_T ConvertToArcs (coOrd pos[4], dynArr_t * segs, BOOL_T track, wDrawC if (arc.curveData.type == curveTypeStraight) { double error = BezErrorLine(pos,start_point,end_point, t_s, t_e); - curr_good = (error <= errorThreshold/2); - arc.curveData.a0 = FindAngle(start_point,end_point); - arc.curveData.a1 = FindAngle(end_point,start_point); + curr_good = (error <= errorThreshold/4); + //arc.curveData.a0 = FindAngle(start_point,end_point); + //arc.curveData.a1 = FindAngle(end_point,start_point); } else if (arc.curveData.type == curveTypeNone) { return FALSE; //Something wrong } else { double error = BezError(pos, arc.curveData.curvePos, start_point, t_s, t_e); - curr_good = (error <= errorThreshold/2); + curr_good = (error <= errorThreshold/4); }; done = prev_good && !curr_good; //Was better than this last time? @@ -449,7 +449,7 @@ EXPORT BOOL_T ConvertToArcs (coOrd pos[4], dynArr_t * segs, BOOL_T track, wDrawC curveSeg.width = track?0:width; if ( prev_arc.curveData.type == curveTypeCurve ) { if (track) - curveSeg.color = (fabs(prev_arc.curveData.curveRadius)<(GetLayoutMinTrackRadius()-EPSILON))?wDrawColorRed:wDrawColorBlack; + curveSeg.color = (fabs(prev_arc.curveData.curveRadius)<(GetLayoutMinTrackRadius()-EPSILON))?exceptionColor:normalColor; else curveSeg.color = color; curveSeg.type = track?SEG_CRVTRK:SEG_CRVLIN; @@ -466,7 +466,7 @@ EXPORT BOOL_T ConvertToArcs (coOrd pos[4], dynArr_t * segs, BOOL_T track, wDrawC curveSeg.color = wDrawColorBlack; else curveSeg.color = color; - curveSeg.u.l.angle = prev_arc.curveData.a1; + curveSeg.u.l.angle = FindAngle(prev_arc.pos0,prev_arc.pos1); curveSeg.u.l.pos[0] = prev_arc.pos0; curveSeg.u.l.pos[1] = prev_arc.pos1; curveSeg.u.l.option = 0; @@ -483,7 +483,7 @@ EXPORT BOOL_T ConvertToArcs (coOrd pos[4], dynArr_t * segs, BOOL_T track, wDrawC * */ -EXPORT void DrawBezCurve(trkSeg_p control_arm1, +static void DrawBezCurve(trkSeg_p control_arm1, int cp1Segs_cnt, trkSeg_p control_arm2, int cp2Segs_cnt, @@ -491,28 +491,24 @@ EXPORT void DrawBezCurve(trkSeg_p control_arm1, int crvSegs_cnt, wDrawColor color ) { - long oldDrawOptions = tempD.funcs->options; - tempD.funcs->options = wDrawOptTemp; - long oldOptions = tempD.options; - tempD.options = DC_TICKS; - tempD.orig = mainD.orig; - tempD.angle = mainD.angle; if (crvSegs_cnt && curveSegs) DrawSegs( &tempD, zero, 0.0, curveSegs, crvSegs_cnt, Da.trackGauge, color ); if (cp1Segs_cnt && control_arm1) DrawSegs( &tempD, zero, 0.0, control_arm1, cp1Segs_cnt, Da.trackGauge, drawColorBlack ); if (cp2Segs_cnt && control_arm2) DrawSegs( &tempD, zero, 0.0, control_arm2, cp2Segs_cnt, Da.trackGauge, drawColorBlack ); - tempD.funcs->options = oldDrawOptions; - tempD.options = oldOptions; } /* + * Undraw the temp Bezier + */ + +/* * If Track, make it red if the radius is below minimum */ void DrawTempBezier(BOOL_T track) { - if (track) DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt, (trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da_cnt,Da.minRadius<(GetLayoutMinTrackRadius()-EPSILON)?drawColorRed:drawColorBlack); + if (track) DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt, (trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da_cnt,fabs(Da.minRadius)<(GetLayoutMinTrackRadius()-EPSILON)?exceptionColor:normalColor); else DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt, (trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da_cnt,drawColorBlack); //Add Second Arm } @@ -542,6 +538,19 @@ void CreateBothControlArms(int selectPoint, BOOL_T track) { } } +void CreateMoveAnchor(coOrd pos,BOOL_T fill) { + double d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int inx = anchors_da.cnt-1; + anchors(inx).type = fill?SEG_FILCRCL:SEG_CRVLIN; + anchors(inx).u.c.a0 = 0.0; + anchors(inx).u.c.a1 = 360.0; + anchors(inx).width = 0; + anchors(inx).color = wDrawColorBlue; + anchors(inx).u.c.radius = d/4; + anchors(inx).u.c.center = pos; +} + /* * AdjustBezCurve * @@ -590,14 +599,24 @@ EXPORT STATUS_T AdjustBezCurve( InfoMessage( _("Select End-Point - Ctrl unlocks end-point") ); else InfoMessage( _("Select End-Point") ); - DrawTempBezier(Da.track); return C_CONTINUE; + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + if (Da.state != PICK_POINT) return C_CONTINUE; + if (Da.state != PICK_POINT) return C_CONTINUE; + for (int i=0;i<4;i++) { + if (i==0 && Da.trk[0]) continue; + if (i==3 && Da.trk[1]) continue; //ignore locked points + d = FindDistance(Da.pos[i],pos); + if (IsClose(d)) CreateMoveAnchor(Da.pos[i],TRUE); + } + break; + case C_DOWN: if (Da.state != PICK_POINT) return C_CONTINUE; dd = 10000.0; Da.selectPoint = -1; - DrawTempBezier(Da.track); //wipe out for (int i=0;i<4;i++) { d = FindDistance(Da.pos[i],pos); if (d < dd) { @@ -609,19 +628,19 @@ EXPORT STATUS_T AdjustBezCurve( } if (!IsClose(dd) ) Da.selectPoint = -1; + DYNARR_RESET(trkSeg_t,anchors_da); if (Da.selectPoint == -1) { InfoMessage( _("Not close enough to any valid, selectable point, reselect") ); - DrawTempBezier(Da.track); return C_CONTINUE; } else { pos = Da.pos[Da.selectPoint]; + CreateMoveAnchor(pos,TRUE); Da.state = POINT_PICKED; InfoMessage( _("Drag point %d to new location and release it"),Da.selectPoint+1 ); } CreateBothControlArms(Da.selectPoint, track); if (ConvertToArcs(Da.pos, &Da.crvSegs_da, track, color,Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; Da.minRadius = BezierMinRadius(Da.pos, Da.crvSegs_da); - DrawTempBezier(Da.track); return C_CONTINUE; case C_MOVE: @@ -629,8 +648,8 @@ EXPORT STATUS_T AdjustBezCurve( InfoMessage(_("Pick any circle to adjust it - Enter to confirm, ESC to abort")); return C_CONTINUE; } + DYNARR_RESET(trkSeg_t,anchors_da); //If locked, reset pos to be on line from other track - DrawTempBezier(Da.track); //wipe out if (Da.selectPoint == 1 || Da.selectPoint == 2) { //CPs int controlArm = Da.selectPoint-1; //Snap to direction of track if (Da.trk[controlArm]) { @@ -642,6 +661,7 @@ EXPORT STATUS_T AdjustBezCurve( } // Dont Snap control points } else SnapPos(&pos); Da.pos[Da.selectPoint] = pos; + CreateMoveAnchor(pos,TRUE); CreateBothControlArms(Da.selectPoint, track); if (ConvertToArcs(Da.pos,&Da.crvSegs_da,track, color, Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; Da.minRadius = BezierMinRadius(Da.pos,Da.crvSegs_da); @@ -666,7 +686,6 @@ EXPORT STATUS_T AdjustBezCurve( InfoMessage( _("Bezier %s : Min Radius=%s Length=%s"),track?"Track":"Line", FormatDistance(Da.minRadius>=100000?0:Da.minRadius), FormatDistance(BezierLength(Da.pos,Da.crvSegs_da))); - DrawTempBezier(Da.track); return C_CONTINUE; case C_UP: @@ -674,11 +693,8 @@ EXPORT STATUS_T AdjustBezCurve( //Take last pos and decide if it should be snapped to a track because SHIFT is held (pos0 and pos3) ep = 0; BOOL_T found = FALSE; - - DrawTempBezier(Da.track); //wipe out - + DYNARR_RESET(trkSeg_t,anchors_da); p = pos; - if (track && (Da.selectPoint == 0 || Da.selectPoint == 3)) { //EPs if ((MyGetKeyState() & WKEY_SHIFT) != 0) { //Snap Track if ((t = OnTrackIgnore(&p, FALSE, TRUE, Da.selectTrack)) != NULL) { //Snap to endPoint @@ -701,6 +717,7 @@ EXPORT STATUS_T AdjustBezCurve( angle2 = NormalizeAngle(FindAngle(pos, pos0)-angle1); Translate(&Da.pos[Da.selectPoint==0?1:2], Da.pos[Da.selectPoint==0?0:3], angle1, FindDistance(Da.pos[Da.selectPoint==0?1:2],pos)*cos(D2R(angle2))); } + Da.selectPoint = -1; CreateBothControlArms(Da.selectPoint,track); if (ConvertToArcs(Da.pos,&Da.crvSegs_da,track,color,Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; @@ -722,7 +739,6 @@ EXPORT STATUS_T AdjustBezCurve( InfoMessage(_("Pick any circle to adjust it - Enter to confirm, ESC to abort")); } else InfoMessage(_("Pick any circle to adjust it - Enter to confirm, ESC to abort")); - DrawTempBezier(Da.track); Da.state = PICK_POINT; return C_CONTINUE; @@ -751,7 +767,6 @@ EXPORT STATUS_T AdjustBezCurve( } } Da.minRadius = BezierMinRadius(Da.pos,Da.crvSegs_da); - DrawTempBezier(Da.track); UndoStart( _("Create Bezier"), "newBezier - CR" ); if (Da.track) { t = NewBezierTrack( Da.pos, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt); @@ -761,26 +776,30 @@ EXPORT STATUS_T AdjustBezCurve( else t = NewBezierLine(Da.pos, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt,color,width); UndoEnd(); if (Da.crvSegs_da.ptr) MyFree(Da.crvSegs_da.ptr); + DYNARR_RESET(trkSeg_t,anchors_da); Da.crvSegs_da.ptr = NULL; Da.crvSegs_da.cnt = 0; Da.crvSegs_da.max = 0; DrawNewTrack(t); Da.state = NONE; - MainRedraw(); - MapRedraw(); return C_TERMINATE; } return C_CONTINUE; case C_REDRAW: - DrawTempBezier(Da.track); + if (Da.state != NONE) + DrawTempBezier(Da.track); + if (anchors_da.cnt>0) + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); return C_CONTINUE; default: return C_CONTINUE; } + return C_CONTINUE; + } @@ -824,14 +843,16 @@ STATUS_T CmdBezModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG) Da.selectPoint = -1; Da.selectTrack = NULL; - if (IsTrack(trk)) Da.track = TRUE; + if (IsTrack(trk)) { + Da.track = TRUE; + Da.trk[0] = GetTrkEndTrk( trk, 0 ); + if (Da.trk[0]) Da.ep[0] = GetEndPtConnectedToMe(Da.trk[0],trk); + Da.trk[1] = GetTrkEndTrk( trk, 1 ); + if (Da.trk[1]) Da.ep[1] = GetEndPtConnectedToMe(Da.trk[1],trk); + } else Da.track = FALSE; Da.selectTrack = trk; - Da.trk[0] = GetTrkEndTrk( trk, 0 ); - if (Da.trk[0]) Da.ep[0] = GetEndPtConnectedToMe(Da.trk[0],trk); - Da.trk[1] = GetTrkEndTrk( trk, 1 ); - if (Da.trk[1]) Da.ep[1] = GetEndPtConnectedToMe(Da.trk[1],trk); for (int i=0;i<4;i++) Da.pos[i] = xx->bezierData.pos[i]; //Copy parms from old trk InfoMessage(_("%s picked - now select a Point"),track?"Track":"Line"); @@ -839,8 +860,12 @@ STATUS_T CmdBezModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG) DrawTrack(Da.selectTrack,&mainD,wDrawColorWhite); //Wipe out real track, draw replacement return AdjustBezCurve(C_START, pos, Da.track, xx->bezierData.segsColor, xx->bezierData.segsWidth, InfoMessage); + case wActionMove: + if (Da.state == NONE) return C_CONTINUE; + return AdjustBezCurve(wActionMove, pos, Da.track, xx->bezierData.segsColor, xx->bezierData.segsWidth, InfoMessage); case C_DOWN: if (Da.state == TRACK_SELECTED) return C_CONTINUE; //Ignore until first up + UndrawNewTrack( Da.selectTrack ); return AdjustBezCurve(C_DOWN, pos, Da.track, xx->bezierData.segsColor, xx->bezierData.segsWidth, InfoMessage); @@ -867,9 +892,10 @@ STATUS_T CmdBezModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG) UndoStart( _("Modify Bezier"), "newBezier - CR" ); if (Da.track) t = NewBezierTrack( Da.pos, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt); else t = NewBezierLine(Da.pos, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt,xx->bezierData.segsColor,xx->bezierData.segsWidth); - + if (Da.track) CopyAttributes( trk, t ); + Da.state = NONE; //Must do before Delete for redraw DeleteTrack(trk, TRUE); if (Da.track) { @@ -879,16 +905,14 @@ STATUS_T CmdBezModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG) } } } + DrawNewTrack( t ); UndoEnd(); - InfoMessage(_("Modify Bezier Complete - select another")); - Da.state = NONE; + InfoMessage(_("Modify Bezier Complete")); return C_TERMINATE; case C_CANCEL: InfoMessage(_("Modify Bezier Cancelled")); Da.state = NONE; - MainRedraw(); - MapRedraw(); return C_TERMINATE; case C_REDRAW: @@ -919,6 +943,23 @@ DIST_T BezierLength(coOrd pos[4],dynArr_t segs) { return dd; } +DIST_T BezierOffsetLength(dynArr_t segs, double offset) { + DIST_T dd = 0.0; + if (segs.cnt == 0 ) return dd; + for (int i = 0;i<segs.cnt;i++) { + trkSeg_t t = DYNARR_N(trkSeg_t, segs, i); + if (t.type == SEG_CRVTRK || t.type == SEG_CRVLIN) { + dd += fabs((t.u.c.radius+(t.u.c.radius>0?offset:-offset))*D2R(t.u.c.a1)); + } else if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) { + dd +=BezierOffsetLength(t.bezSegs,offset); + } else if (t.type == SEG_STRLIN || t.type == SEG_STRTRK ) { + dd += FindDistance(t.u.l.pos[0],t.u.l.pos[1]); + } + } + return dd; +} + + DIST_T BezierMinRadius(coOrd pos[4],dynArr_t segs) { DIST_T r = 100000.0, rr; if (segs.cnt == 0 ) return r; @@ -934,6 +975,20 @@ DIST_T BezierMinRadius(coOrd pos[4],dynArr_t segs) { return r; } +static void CreateEndAnchor(coOrd p, wBool_t lock) { + DIST_T d = tempD.scale*0.15; + + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = lock?SEG_FILCRCL:SEG_CRVLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.c.center = p; + anchors(i).u.c.radius = d/2; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).width = 0; +} + /* * Create a Bezier Curve (Track or Line) * Sequence is @@ -955,7 +1010,6 @@ STATUS_T CmdBezCurve( wAction_t action, coOrd pos ) cmd = action>>8; } else cmd = (long)commandContext; - Da.color = lineColor; Da.width = (double)lineWidth/mainD.dpi; Da.trackGauge = trackGauge; @@ -965,6 +1019,10 @@ STATUS_T CmdBezCurve( wAction_t action, coOrd pos ) case C_START: Da.track = (cmd == bezCmdModifyTrack || cmd == bezCmdCreateTrack)?TRUE:FALSE; + if (Da.track ) + Da.color = wDrawColorBlack; + else + Da.color = lineColor; Da.state = POS_1; Da. selectPoint = -1; @@ -977,44 +1035,43 @@ STATUS_T CmdBezCurve( wAction_t action, coOrd pos ) DYNARR_RESET(trkSeg_t,Da.crvSegs_da); Da.cp1Segs_da_cnt = 0; Da.cp2Segs_da_cnt = 0; - InfoMessage( _("Place 1st end point of Bezier + Shift -> snap to %s end"), Da.track?"Unconnected Track":"Line" ); + InfoMessage( _("Place 1st endpoint of Bezier - snap to %s"), Da.track?"unconnected Track":"line" ); return C_CONTINUE; case C_DOWN: + DYNARR_RESET(trkSeg_t,anchors_da); if ( Da.state == POS_1 || Da.state == POS_2) { //Set the first or third point coOrd p = pos; BOOL_T found = FALSE; int end = Da.state==POS_1?0:1; EPINX_T ep; if (Da.track) { - if ((MyGetKeyState() & WKEY_SHIFT) != 0) { //Snap Track + if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == 0) { //Snap Track if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) { ep = PickUnconnectedEndPointSilent(p, t); if (ep != -1) { - Da.trk[end] = t; - Da.ep[end] = ep; - pos = GetTrkEndPos(t, ep); - found = TRUE; + if (GetTrkGauge(t) != GetScaleTrackGauge(GetLayoutCurScale())) { + wBeep(); + InfoMessage(_("Track is different gauge")); + ep = -1; + t = NULL; + } else { + Da.trk[end] = t; + Da.ep[end] = ep; + pos = GetTrkEndPos(t, ep); + found = TRUE; + } } } - if (!found) { - wBeep(); - InfoMessage(_("Shift used, but no Unconnected Track End there")); - return C_CONTINUE; - } } } else { //Snap Bez Line to Lines - if ((MyGetKeyState() & WKEY_SHIFT) != 0) { + if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == 0) { if ((t = OnTrack(&p,FALSE, FALSE)) != NULL) { if (GetClosestEndPt(t,&p)) { pos = p; found = TRUE; } - } else { - wBeep(); - InfoMessage(_("Shift used, but no Line End there")); - return C_CONTINUE; } } } @@ -1024,37 +1081,58 @@ STATUS_T CmdBezCurve( wAction_t action, coOrd pos ) Da.pos[1] = pos; Da.state = CONTROL_ARM_1; //Draw the first control arm Da.selectPoint = 1; - InfoMessage( _("Drag end of first Control Arm") ); + InfoMessage( _("Drag end of first control arm") ); Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track,TRUE,Da.trk[1]!=NULL,1,wDrawColorBlack); - DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack); - } else { + } else { Da.pos[3] = pos; //2nd End Point Da.pos[2] = pos; //2nd Ctl Point Da.state = POINT_PICKED; // Drag out the second control arm Da.selectPoint = 2; - InfoMessage( _("Drag end of second Control Arm") ); - DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack); //Wipe out initial Arm + InfoMessage( _("Drag end of second control arm") ); Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track,FALSE,Da.trk[0]!=NULL,-1,wDrawColorBlack); Da.cp2Segs_da_cnt = createControlArm(Da.cp2Segs_da, Da.pos[3], Da.pos[2], Da.track,TRUE,Da.trk[1]!=NULL,1,wDrawColorBlack); if (ConvertToArcs(Da.pos,&Da.crvSegs_da,Da.track,Da.color,Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; - DrawTempBezier(Da.track); } return C_CONTINUE; } else { return AdjustBezCurve( action&0xFF, pos, Da.track, Da.color, Da.width, InfoMessage ); } return C_CONTINUE; + + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + if ( Da.state != POS_1 && Da.state != POS_2) return C_CONTINUE; + if (Da.track) { + if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == 0) { + if ((t = OnTrack(&pos, FALSE, TRUE)) != NULL) { + EPINX_T ep = PickUnconnectedEndPointSilent(pos, t); + if (ep != -1) { + if (GetTrkGauge(t) == GetScaleTrackGauge(GetLayoutCurScale())) { + pos = GetTrkEndPos(t, ep); + CreateEndAnchor(pos,FALSE); + } + } + } + } + } else { + if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == 0) { + if ((t = OnTrack(&pos,FALSE, FALSE)) != NULL) { + CreateEndAnchor(pos,TRUE); + } + } + } + if (anchors_da.cnt) + return C_CONTINUE; case C_MOVE: if (Da.state == POS_1) { - InfoMessage( _("Place 1st end point of Bezier + Shift -> snap to %s end"), Da.track?"Unconnected Track":"Line" ); + InfoMessage( _("Place 1st endpoint of Bezier - snap to %s"), Da.track?"unconnected track":"line" ); return C_CONTINUE; } if (Da.state == POS_2) { - InfoMessage( _("Select other end of Bezier, +Shift -> snap to %s end"), Da.track?"Unconnected Track":"Line" ); + InfoMessage( _("Select other end of Bezier - snap to %s end"), Da.track?"unconnected track":"line" ); } if (Da.state == CONTROL_ARM_1 ) { - DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack); if (Da.trk[0]) { EPINX_T ep = 0; ANGLE_T angle1,angle2; @@ -1066,7 +1144,6 @@ STATUS_T CmdBezCurve( wAction_t action, coOrd pos ) } // Don't Snap control points Da.pos[1] = pos; Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track, TRUE, Da.trk[0]!=NULL, 1, wDrawColorBlack); - DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack); } else { return AdjustBezCurve( action&0xFF, pos, Da.track, Da.color, Da.width, InfoMessage ); } @@ -1074,7 +1151,6 @@ STATUS_T CmdBezCurve( wAction_t action, coOrd pos ) case C_UP: if (Da.state == CONTROL_ARM_1) { - DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack); if (Da.trk[0]) { EPINX_T ep = Da.ep[0]; ANGLE_T angle1,angle2; @@ -1091,9 +1167,8 @@ STATUS_T CmdBezCurve( wAction_t action, coOrd pos ) return C_CONTINUE; } Da.state = POS_2; - InfoMessage( _("Select other end of Bezier, +Shift -> snap to %s end"), Da.track?"Unconnected Track":"Line" ); + InfoMessage( _("Select other end of Bezier - snap to %s end"), Da.track?"Unconnected Track":"Line" ); Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track, FALSE, Da.trk[0]!=NULL, -1, wDrawColorBlack); - DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack); return C_CONTINUE; } else { return AdjustBezCurve( action&0xFF, pos, Da.track, Da.color, Da.width, InfoMessage ); @@ -1108,14 +1183,14 @@ STATUS_T CmdBezCurve( wAction_t action, coOrd pos ) case C_REDRAW: if ( Da.state != NONE ) { - DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt,(trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da.cnt, Da.color); } + if (anchors_da.cnt) + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); return C_CONTINUE; case C_CANCEL: if (Da.state != NONE) { - DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt,(trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da.cnt, Da.color); Da.cp1Segs_da_cnt = 0; Da.cp2Segs_da_cnt = 0; Da.crvSegs_da_cnt = 0; @@ -1139,13 +1214,13 @@ STATUS_T CmdBezCurve( wAction_t action, coOrd pos ) } void UpdateParms(wDrawColor color,long width) { - DrawTempBezier(Da.track); Da.color = lineColor; Da.width = (double)lineWidth/mainD.dpi; if (Da.crvSegs_da.cnt) { ConvertToArcs(Da.pos,&Da.crvSegs_da,Da.track,Da.color,Da.width); } DrawTempBezier(Da.track); + } diff --git a/app/bin/cbezier.h b/app/bin/cbezier.h index 8d0dde8..49b818f 100644 --- a/app/bin/cbezier.h +++ b/app/bin/cbezier.h @@ -25,7 +25,7 @@ #include "utility.h" -dynArr_t tempEndPts_da; +extern dynArr_t tempEndPts_da; #define BezSegs(N) DYNARR_N( trkEndPt_t, tempEndPts_da, N ) #define bezCmdNone (0) @@ -42,12 +42,15 @@ STATUS_T CmdBezCurve( wAction_t, coOrd); STATUS_T CmdBezModify(track_p, wAction_t, coOrd, DIST_T); STATUS_T CreateBezier( wAction_t, coOrd, BOOL_T, wDrawColor, DIST_T, long, bezMessageProc ); -DIST_T BezierDescriptionDistance( coOrd, track_p ); +DIST_T BezierDescriptionDistance( coOrd, track_p, coOrd *, BOOL_T, BOOL_T * ); STATUS_T BezierDescriptionMove( track_p, wAction_t, coOrd ); BOOL_T GetBezierMiddle( track_p, coOrd * ); BOOL_T ConvertToArcs (coOrd[4], dynArr_t *, BOOL_T, wDrawColor, DIST_T); track_p NewBezierTrack(coOrd[4], trkSeg_t *, int); double BezierLength(coOrd[4], dynArr_t); +double BezierOffsetLength(dynArr_t,double offset); double BezierMinRadius(coOrd[4],dynArr_t); void UpdateParms(wDrawColor color,long width); +void addSegBezier(dynArr_t * array_p, trkSeg_p seg); + diff --git a/app/bin/cblock.c b/app/bin/cblock.c index 4c4895c..b395306 100644 --- a/app/bin/cblock.c +++ b/app/bin/cblock.c @@ -61,6 +61,10 @@ #include "trackx.h" #include "utility.h" +#ifdef WINDOWS +#include "include/utf8convert.h" +#endif // WINDOWS + EXPORT TRKTYP_T T_BLOCK = -1; static int log_block = 0; @@ -74,8 +78,8 @@ 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 NoDrawFillPoly( drawCmd_p d, int cnt, coOrd * pts, int * types, + wDrawColor color, wDrawWidth width, int fill, int open) {} static void NoDrawFillCircle( drawCmd_p d, coOrd p, DIST_T r, wDrawColor color ) {} @@ -100,6 +104,8 @@ static drawCmd_t blockD = { static char blockName[STR_SHORT_SIZE]; static char blockScript[STR_LONG_SIZE]; static long blockElementCount; +static track_p first_block; +static track_p last_block; static paramData_t blockPLs[] = { /*0*/ { PD_STRING, blockName, "name", PDO_NOPREF | PDO_STRINGLIMITLENGTH, (void*)200, N_("Name"), 0, 0, sizeof( blockName )}, @@ -116,7 +122,7 @@ static track_p blockEditTrack; static paramData_t blockEditPLs[] = { /*0*/ { PD_STRING, blockEditName, "name", PDO_NOPREF | PDO_STRINGLIMITLENGTH, (void*)200, N_("Name"), 0, 0, sizeof(blockEditName)}, /*1*/ { PD_STRING, blockEditScript, "script", PDO_NOPREF | PDO_STRINGLIMITLENGTH, (void*)350, N_("Script"), 0, 0, sizeof(blockEditScript)}, -/*2*/ { PD_STRING, blockEditSegs, "segments", PDO_NOPREF, (void*)350, N_("Segments"), BO_READONLY }, +/*2*/ { PD_STRING, blockEditSegs, "segments", PDO_NOPREF, (void*)350, N_("Segments"), BO_READONLY }, }; static paramGroup_t blockEditPG = { "block", 0, blockEditPLs, sizeof blockEditPLs/sizeof blockEditPLs[0] }; static wWin_p blockEditW; @@ -129,12 +135,14 @@ typedef struct btrackinfo_t { static dynArr_t blockTrk_da; #define blockTrk(N) DYNARR_N( btrackinfo_t , blockTrk_da, N ) +#define tracklist(N) (&(xx->trackList))[N] typedef struct blockData_t { char * name; char * script; BOOL_T IsHilite; + track_p next_block; wIndex_t numTracks; btrackinfo_t trackList; } blockData_t, *blockData_p; @@ -221,10 +229,11 @@ static DIST_T DistanceBlock (track_p t, coOrd * p ) DIST_T closest, current; int iTrk = 1; coOrd pos = *p; - closest = GetTrkDistance ((&(xx->trackList))[0].t, &pos); + closest = 99999.0; coOrd best_pos = pos; - for (; iTrk < xx->numTracks; iTrk++) { + for (iTrk = 0; iTrk < xx->numTracks; iTrk++) { pos = *p; + if ((&(xx->trackList))[iTrk].t == NULL) continue; current = GetTrkDistance ((&(xx->trackList))[iTrk].t, &pos); if (current < closest) { closest = current; @@ -252,22 +261,25 @@ static void DescribeBlock (track_p trk, char * str, CSIZE_T len ) *str = tolower((unsigned char)*str); str++; } - sprintf( str, _("(%d): Layer=%d %s"), + sprintf( str, _("(%d): Layer=%u %s"), GetTrkIndex(trk), GetTrkLayer(trk)+1, message ); blockData.name[0] = '\0'; strncat(blockData.name,xx->name,STR_SHORT_SIZE-1); blockData.script[0] = '\0'; strncat(blockData.script,xx->script,STR_LONG_SIZE-1); blockData.length = 0; - if (xx->numTracks > 0) { - blockData.endPt[0] = GetTrkEndPos((&(xx->trackList))[0].t,0); - } + BOOL_T first = TRUE; for (tcount = 0; tcount < xx->numTracks; tcount++) { - if ((&(xx->trackList))[tcount].t == NULL) continue; - blockData.length += GetTrkLength((&(xx->trackList))[tcount].t,0,1); - lastTrk = (&(xx->trackList))[tcount].t; + if ((&(xx->trackList))[tcount].t == NULL) continue; + if (first) { + blockData.endPt[0] = GetTrkEndPos((&(xx->trackList))[tcount].t,0); + first = FALSE; + } + blockData.endPt[1] = GetTrkEndPos((&(xx->trackList))[tcount].t,1); + blockData.length += GetTrkLength((&(xx->trackList))[tcount].t,0,1); + tcount++; + break; } - if (lastTrk != NULL) blockData.endPt[1] = GetTrkEndPos(lastTrk,1); blockDesc[E0].mode = blockDesc[E1].mode = blockDesc[LN].mode = DESC_RO; @@ -347,13 +359,31 @@ static BOOL_T blockCheckContigiousPath() static void DeleteBlock ( track_p t ) { - LOG( log_block, 1, ("*** DeleteBlock(%p)\n",t)) - blockData_p xx = GetblockData(t); - LOG( log_block, 1, ("*** DeleteBlock(): index is %d\n",GetTrkIndex(t))) - LOG( log_block, 1, ("*** DeleteBlock(): xx = %p, xx->name = %p, xx->script = %p\n", + track_p trk1; + blockData_p xx1; + + LOG( log_block, 1, ("*** DeleteBlock(%p)\n",t)) + blockData_p xx = GetblockData(t); + LOG( log_block, 1, ("*** DeleteBlock(): index is %d\n",GetTrkIndex(t))) + LOG( log_block, 1, ("*** DeleteBlock(): xx = %p, xx->name = %p, xx->script = %p\n", xx,xx->name,xx->script)) MyFree(xx->name); xx->name = NULL; MyFree(xx->script); xx->script = NULL; + + if (first_block == t) + first_block = xx->next_block; + trk1 = first_block; + while(trk1) { + xx1 = GetblockData (trk1); + if (xx1->next_block == t) { + xx1->next_block = xx->next_block; + break; + } + trk1 = xx1->next_block; + } + if (t == last_block) + last_block = trk1; + } static BOOL_T WriteBlock ( track_p t, FILE * f ) @@ -361,25 +391,31 @@ static BOOL_T WriteBlock ( track_p t, FILE * f ) BOOL_T rc = TRUE; wIndex_t iTrack; blockData_p xx = GetblockData(t); + char *blockName = MyStrdup(xx->name); + +#ifdef WINDOWS + blockName = Convert2UTF8(blockName); +#endif // WINDOWS rc &= fprintf(f, "BLOCK %d \"%s\" \"%s\"\n", - GetTrkIndex(t), xx->name, xx->script)>0; + GetTrkIndex(t), blockName, xx->script)>0; for (iTrack = 0; iTrack < xx->numTracks && rc; iTrack++) { if ((&(xx->trackList))[iTrack].t == NULL) continue; rc &= fprintf(f, "\tTRK %d\n", GetTrkIndex((&(xx->trackList))[iTrack].t))>0; } - rc &= fprintf( f, "\tEND\n" )>0; + rc &= fprintf( f, "\t%s\n", END_BLOCK )>0; + MyFree(blockName); return rc; } -static void ReadBlock ( char * line ) +static BOOL_T ReadBlock ( char * line ) { TRKINX_T trkindex; wIndex_t index; - track_p trk; + track_p trk, trk1; char * cp = NULL; - blockData_p xx; + blockData_p xx,xx1; wIndex_t iTrack; EPINX_T ep; trkEndPt_p endPtP; @@ -387,19 +423,24 @@ static void ReadBlock ( char * line ) LOG( log_block, 1, ("*** ReadBlock: line is '%s'\n",line)) if (!GetArgs(line+6,"dqq",&index,&name,&script)) { - return; + return FALSE; } + +#ifdef WINDOWS + ConvertUTF8ToSystem(name); +#endif // WINDOWS + DYNARR_RESET( btrackinfo_p , blockTrk_da ); while ( (cp = GetNextLine()) != NULL ) { - while (isspace((unsigned char)*cp)) cp++; - if ( strncmp( cp, "END", 3 ) == 0 ) { + if ( IsEND( END_BLOCK ) ) { break; } + while (isspace((unsigned char)*cp)) cp++; if ( *cp == '\n' || *cp == '#' ) { continue; } if ( strncmp( cp, "TRK", 3 ) == 0 ) { - if (!GetArgs(cp+4,"d",&trkindex)) return; + if (!GetArgs(cp+4,"d",&trkindex)) return FALSE; /*trk = FindTrack(trkindex);*/ DYNARR_APPEND( btrackinfo_p *, blockTrk_da, 10 ); blockTrk(blockTrk_da.cnt-1).i = trkindex; @@ -411,18 +452,28 @@ static void ReadBlock ( char * line ) endPtP = &tempEndPts(ep); SetTrkEndPoint( trk, ep, endPtP->pos, endPtP->angle ); } - xx = GetblockData( trk ); - LOG( log_block, 1, ("*** ReadBlock(): trk = %p (%d), xx = %p\n",trk,GetTrkIndex(trk),xx)) - LOG( log_block, 1, ("*** ReadBlock(): name = %p, script = %p\n",name,script)) - xx->name = name; - xx->script = script; - xx->IsHilite = FALSE; + xx = GetblockData( trk ); + LOG( log_block, 1, ("*** ReadBlock(): trk = %p (%d), xx = %p\n",trk,GetTrkIndex(trk),xx)) + LOG( log_block, 1, ("*** ReadBlock(): name = %p, script = %p\n",name,script)) + xx->name = name; + xx->script = script; + xx->IsHilite = FALSE; xx->numTracks = blockTrk_da.cnt; + trk1 = last_block; + if (!trk1) first_block = trk; + else { + xx1 = GetblockData(trk1); + xx1->next_block = trk; + } + xx->next_block = NULL; + last_block = trk; for (iTrack = 0; iTrack < blockTrk_da.cnt; iTrack++) { - LOG( log_block, 1, ("*** ReadBlock(): copying track T%d\n",GetTrkIndex(blockTrk(iTrack).t))) - memcpy((void*)&((&(xx->trackList))[iTrack]),(void*)&(blockTrk(iTrack)),sizeof(btrackinfo_t)); + LOG( log_block, 1, ("*** ReadBlock(): copying track T%d\n",blockTrk(iTrack).i)) + tracklist(iTrack).i = blockTrk(iTrack).i; + tracklist(iTrack).t = NULL; // Not resolved yet!! // } blockDebug(trk); + return TRUE; } EXPORT void ResolveBlockTrack ( track_p trk ) @@ -435,12 +486,12 @@ EXPORT void ResolveBlockTrack ( track_p trk ) LOG( log_block, 1, ("*** ResolveBlockTrack(%d)\n",GetTrkIndex(trk))) xx = GetblockData(trk); for (iTrack = 0; iTrack < xx->numTracks; iTrack++) { - t_trk = FindTrack((&(xx->trackList))[iTrack].i); + t_trk = FindTrack(tracklist(iTrack).i); if (t_trk == NULL) { - NoticeMessage( _("resolveBlockTrack: T%d[%d]: T%d doesn't exist"), _("Continue"), NULL, GetTrkIndex(trk), iTrack, (&(xx->trackList))[iTrack].i ); + NoticeMessage( _("resolveBlockTrack: T%d[%d]: T%d doesn't exist"), _("Continue"), NULL, GetTrkIndex(trk), iTrack, tracklist(iTrack).i,t_trk ); } - (&(xx->trackList))[iTrack].t = t_trk; - LOG( log_block, 1, ("*** ResolveBlockTrack(): %d (%d): %p\n",iTrack,(&(xx->trackList))[iTrack].i,t_trk)) + tracklist(iTrack).t = t_trk; + LOG( log_block, 1, ("*** ResolveBlockTrack(): %d (%d): %p\n",iTrack,tracklist(iTrack).i,t_trk)) } } @@ -494,17 +545,24 @@ static BOOL_T TrackInBlock (track_p trk, track_p blk) { 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; + blockData_p xx; + if (!first_block) return NULL; + a_trk = first_block; + while (a_trk) { + if (!IsTrackDeleted(a_trk)) { + if (GetTrkType(a_trk) == T_BLOCK && + TrackInBlock(trk,a_trk)) return a_trk; + } + xx = GetblockData(a_trk); + a_trk = xx->next_block; } return NULL; } static void BlockOk ( void * junk ) { - blockData_p xx; - track_p trk; + blockData_p xx,xx1; + track_p trk,trk1; wIndex_t iTrack; EPINX_T ep; trkEndPt_p endPtP; @@ -525,10 +583,11 @@ static void BlockOk ( void * junk ) while ( TrackIterate( &trk ) ) { if ( GetTrkSelected( trk ) ) { if ( IsTrack(trk) ) { - DYNARR_APPEND( btrackinfo_p *, blockTrk_da, 10 ); + DYNARR_APPEND( btrackinfo_t, blockTrk_da, 10 ); + blockTrk(blockTrk_da.cnt - 1).t = trk; + blockTrk(blockTrk_da.cnt - 1).i = GetTrkIndex(trk); LOG( log_block, 1, ("*** BlockOk(): adding track T%d\n",GetTrkIndex(trk))) - blockTrk(blockTrk_da.cnt-1).t = trk; - blockTrk(blockTrk_da.cnt-1).i = GetTrkIndex(trk); + } } } @@ -556,15 +615,26 @@ static void BlockOk ( void * junk ) SetTrkEndPoint( trk, ep, endPtP->pos, endPtP->angle ); } - xx = GetblockData( trk ); - LOG(log_block, 1, ("*** BlockOk(): trk = %p (%d), xx = %p\n", trk, GetTrkIndex(trk), xx)) + xx = GetblockData( trk ); + LOG(log_block, 1, ("*** BlockOk(): trk = %p (%d), xx = %p\n", trk, GetTrkIndex(trk), xx)) xx->name = MyStrdup(blockName); xx->script = MyStrdup(blockScript); - xx->IsHilite = FALSE; + xx->IsHilite = FALSE; xx->numTracks = blockTrk_da.cnt; + trk1 = last_block; + if (!trk1) { + first_block = trk; + } + else { + xx1 = GetblockData(trk1); + xx1->next_block = trk; + } + xx->next_block = NULL; + last_block = trk; for (iTrack = 0; iTrack < blockTrk_da.cnt; iTrack++) { - LOG( log_block, 1, ("*** BlockOk(): copying track T%d\n",GetTrkIndex(blockTrk(iTrack).t))) - memcpy((void*)&(&(xx->trackList))[iTrack],(void*)&blockTrk(iTrack),sizeof(btrackinfo_t)); + LOG( log_block, 1, ("*** BlockOk(): copying track T%d\n",tracklist(iTrack).i)) + tracklist(iTrack).i = blockTrk(iTrack).i; + tracklist(iTrack).t = blockTrk(iTrack).t; } blockDebug(trk); UndoEnd(); @@ -710,13 +780,17 @@ static STATUS_T CmdBlock (wAction_t action, coOrd pos ) } #endif -EXPORT void CheckDeleteBlock (track_p t) +void CheckDeleteBlock(track_p t) { track_p blk; blockData_p xx; - + if (!IsTrack(t)) { + return; + } blk = FindBlock(t); - if (blk == NULL) return; + if (blk == NULL) { + return; + } xx = GetblockData(blk); NoticeMessage(_("Deleting block %s"),_("Ok"),NULL,xx->name); DeleteTrack(blk,FALSE); @@ -789,7 +863,7 @@ static void DrawBlockTrackHilite( void ) w = (wPos_t)((blkhiliteSize.x/mainD.scale)*mainD.dpi+0.5); h = (wPos_t)((blkhiliteSize.y/mainD.scale)*mainD.dpi+0.5); mainD.CoOrd2Pix(&mainD,blkhiliteOrig,&x,&y); - wDrawFilledRectangle( mainD.d, x, y, w, h, blkhiliteColor, wDrawOptTemp ); + wDrawFilledRectangle( mainD.d, x, y, w, h, blkhiliteColor, wDrawOptTemp|wDrawOptTransparent ); } diff --git a/app/bin/ccontrol.c b/app/bin/ccontrol.c index fb02fdf..793acc3 100644 --- a/app/bin/ccontrol.c +++ b/app/bin/ccontrol.c @@ -58,6 +58,9 @@ static const char rcsid[] = "@(#) : $Id$"; #include "param.h" #include "track.h" #include "trackx.h" +#ifdef WINDOWS +#include "include/utf8convert.h" +#endif // WINDOWS #include "utility.h" #include "messages.h" @@ -276,7 +279,7 @@ static void DescribeControl (track_p trk, char * str, CSIZE_T len ) *str = tolower((unsigned char)*str); str++; } - sprintf( str, _("(%d [%s]): Layer=%d, at %0.3f,%0.3f"), + sprintf( str, _("(%d [%s]): Layer=%u, at %0.3f,%0.3f"), GetTrkIndex(trk), xx->name,GetTrkLayer(trk)+1, xx->orig.x, xx->orig.y); strncpy(controlProperties.name,xx->name,STR_SHORT_SIZE-1); @@ -305,14 +308,22 @@ static BOOL_T WriteControl ( track_p t, FILE * f ) { BOOL_T rc = TRUE; controlData_p xx = GetcontrolData(t); - rc &= fprintf(f, "CONTROL %d %d %s %d %0.6f %0.6f \"%s\" \"%s\" \"%s\"\n", + char *controlName = MyStrdup(xx->name); + +#ifdef WINDOWS + controlName = Convert2UTF8(controlName); +#endif // WINDOWS + + rc &= fprintf(f, "CONTROL %d %u %s %d %0.6f %0.6f \"%s\" \"%s\" \"%s\"\n", GetTrkIndex(t), GetTrkLayer(t), GetTrkScaleName(t), - GetTrkVisible(t), xx->orig.x, xx->orig.y, xx->name, + GetTrkVisible(t), xx->orig.x, xx->orig.y, controlName, xx->onscript, xx->offscript)>0; + + MyFree(controlName); return rc; } -static void ReadControl ( char * line ) +static BOOL_T ReadControl ( char * line ) { wIndex_t index; /*TRKINX_T trkindex;*/ @@ -326,8 +337,13 @@ static void ReadControl ( char * line ) wIndex_t layer; controlData_p xx; if (!GetArgs(line+7,"dLsdpqqq",&index,&layer,scale, &visible, &orig,&name,&onscript,&offscript)) { - return; + return FALSE; } + +#ifdef WINDOWS + ConvertUTF8ToSystem(name); +#endif // WINDOWS + trk = NewTrack(index, T_CONTROL, 0, sizeof(controlData_t)); SetTrkVisible(trk, visible); SetTrkScale(trk, LookupScale( scale )); @@ -338,6 +354,7 @@ static void ReadControl ( char * line ) xx->onscript = onscript; xx->offscript = offscript; ComputeControlBoundingBox(trk); + return TRUE; } static void MoveControl (track_p trk, coOrd orig ) @@ -502,24 +519,29 @@ static void CreateNewControl (coOrd orig) static STATUS_T CmdControl ( wAction_t action, coOrd pos ) { - + static coOrd control_pos; + static BOOL_T create; switch (action) { case C_START: InfoMessage(_("Place control")); + create = FALSE; return C_CONTINUE; case C_DOWN: + create = TRUE; + /* no break */ case C_MOVE: SnapPos(&pos); - DDrawControl( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack ); + control_pos = pos; return C_CONTINUE; case C_UP: SnapPos(&pos); - DDrawControl( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack ); CreateNewControl(pos); return C_TERMINATE; case C_REDRAW: + if (create) + DDrawControl( &tempD, control_pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack ); + return C_CONTINUE; case C_CANCEL: - DDrawControl( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack ); return C_CONTINUE; default: return C_CONTINUE; @@ -537,7 +559,7 @@ static void DrawControlTrackHilite( void ) w = (wPos_t)((ctlhiliteSize.x/mainD.scale)*mainD.dpi+0.5); h = (wPos_t)((ctlhiliteSize.y/mainD.scale)*mainD.dpi+0.5); mainD.CoOrd2Pix(&mainD,ctlhiliteOrig,&x,&y); - wDrawFilledRectangle( mainD.d, x, y, w, h, ctlhiliteColor, wDrawOptTemp ); + wDrawFilledRectangle( mainD.d, x, y, w, h, ctlhiliteColor, wDrawOptTemp|wDrawOptTransparent ); } static int ControlMgmProc ( int cmd, void * data ) diff --git a/app/bin/ccornu.c b/app/bin/ccornu.c index 9bdb0d0..fd51755 100644 --- a/app/bin/ccornu.c +++ b/app/bin/ccornu.c @@ -1,3 +1,7 @@ + + + + /** \file ccornu.c * Cornu Command. Draw or modify a Cornu Easement Track. */ @@ -71,6 +75,7 @@ #include "ccurve.h" #include "ccornu.h" #include "tcornu.h" +#include "tbezier.h" #include "cstraigh.h" #include "drawgeom.h" #include "cjoin.h" @@ -83,12 +88,24 @@ #include "cundo.h" #include "messages.h" #include "cselect.h" +#include "fileio.h" + +#include <stdint.h> extern drawCmd_t tempD; extern TRKTYP_T T_BEZIER; extern TRKTYP_T T_CORNU; - +typedef struct { + coOrd end_center; + coOrd end_curve; + DIST_T mid_disp; + BOOL_T end_valid; + BOOL_T angle_selected; + BOOL_T radius_selected; + BOOL_T last_selected; + ANGLE_T arc_angle; +} endHandle; /* * STATE INFO @@ -101,11 +118,18 @@ enum Cornu_States { NONE, POINT_PICKED, TRACK_SELECTED }; +typedef enum {CORNU_MODIFY, CORNU_CREATE} cornuCmdType_e; + + static struct { enum Cornu_States state; coOrd pos[2]; - int selectPoint; - wDrawColor color; + int number_of_points; + int selectEndPoint; + int selectMidPoint; + int selectEndHandle; + int prevSelected; + int prevEndPoint; DIST_T width; track_p trk[2]; EPINX_T ep[2]; @@ -119,9 +143,9 @@ static struct { BOOL_T extend[2]; trkSeg_t extendSeg[2]; - trkSeg_t ep1Segs[2]; + trkSeg_t ep1Segs[11]; int ep1Segs_da_cnt; - trkSeg_t ep2Segs[2]; + trkSeg_t ep2Segs[11]; int ep2Segs_da_cnt; dynArr_t crvSegs_da; int crvSegs_da_cnt; @@ -132,9 +156,143 @@ static struct { BOOL_T circleorHelix[2]; DIST_T trackGauge; + int cmdType; + + dynArr_t midSegs; + + dynArr_t mid_points; + dynArr_t tracks; + BOOL_T ends[2]; + + endHandle endHandle[2]; + bezctx * bezc; + + cornuCmdType_e commandType; + } Da; +static trkSeg_p curCornu; +static wIndex_t cornuHotBarCmdInx; + +static struct { + trkSeg_t st; + trkSeg_t back; + trkSeg_t txt; + int count; +} hotB; + + +static char * CmdCornuHotBarProc( + hotBarProc_e op, + void * data, + drawCmd_p d, + coOrd * origP ) +{ + trkSeg_p trkseg = &hotB.st; + switch ( op ) { + case HB_SELECT: + CmdCornu( C_CANCEL, zero ); + curCornu = trkseg; + DoCommandB( (void*)(intptr_t)cornuHotBarCmdInx ); + return NULL; + case HB_LISTTITLE: + sprintf(message,_("%s FlexTrack"),GetScaleName(GetLayoutCurScale())); + return message; + case HB_BARTITLE: + sprintf(message,_("%s FlexTrack"),GetScaleName(GetLayoutCurScale())); + return message; + case HB_FULLTITLE: + sprintf(message,_("%s FlexTrack"),GetScaleName(GetLayoutCurScale())); + return message; + case HB_DRAW: + DrawSegs( d, *origP, 0.0, trkseg, hotB.count, trackGauge, wDrawColorBlack ); + return NULL; + } + return NULL; +} + +static pts_t pts[4]; + + +EXPORT void AddHotBarCornu( void ) +{ + hotB.st.type = SEG_STRTRK; + hotB.st.color = wDrawColorBlack; + hotB.st.u.l.pos[0] = zero; + DIST_T ratio = 75.0/curScaleRatio; + Translate(&hotB.st.u.l.pos[1],zero,45.0,15.0*ratio); + hotB.st.u.l.angle = 45.0; + + pts[0].pt_type = wPolyLineStraight; + pts[0].pt.x = 1.0*ratio; + pts[0].pt.y = 5.0*ratio; + pts[1].pt_type = wPolyLineStraight; + pts[1].pt.x = 1.0*ratio; + pts[1].pt.y = 8.0*ratio; + pts[2].pt_type = wPolyLineStraight; + pts[2].pt.x = 13.0*ratio; + pts[2].pt.y = 8.0*ratio; + pts[3].pt_type = wPolyLineStraight; + pts[3].pt.x = 13.0*ratio; + pts[3].pt.y = 5.0*ratio; + + hotB.back.type = SEG_FILPOLY; + hotB.back.color = wDrawColorWhite; + hotB.back.u.p.orig.x = 0.0; + hotB.back.u.p.orig.y = 0.0; + hotB.back.u.p.cnt = 4; + hotB.back.u.p.angle = 0.0; + hotB.back.u.p.polyType = RECTANGLE; + hotB.back.u.p.pts = &pts[0]; + + hotB.txt.type = SEG_TEXT; + hotB.txt.color = wDrawColorBlack; + hotB.txt.u.t.pos.x = 1.0*ratio; + hotB.txt.u.t.pos.y = 5.0*ratio; + hotB.txt.u.t.boxed = TRUE; + hotB.txt.u.t.string = MyStrdup(_(" FLEX ")); + hotB.txt.u.t.fontP = NULL; + hotB.txt.u.t.fontSize = 160.0*ratio; + hotB.txt.u.t.angle = 0.0; + + char * label = MyMalloc(256); + sprintf(label,_("%s FlexTrack"),GetScaleName(GetLayoutCurScale())); + coOrd end; + end = hotB.st.u.l.pos[1]; + //end.x = 21.25; + //end.y = 21.25; + hotB.count = 3; + //hotB.st.u.l.pos[1] = end; + AddHotBarElement( label, end, zero, TRUE, TRUE, curBarScale>0?curBarScale:-1, &hotB, CmdCornuHotBarProc ); +} + +int createMidPoint(dynArr_t * ap, + coOrd pos0, //end on curve + BOOL_T point_selected, + BOOL_T point_selectable, + BOOL_T track_modifyable + ) +{ + DIST_T d, w; + d = tempD.scale*0.25; + w = tempD.scale/tempD.dpi; /*double width*/ + + DYNARR_APPEND(trkSeg_t,*ap,1); + + trkSeg_p sp = &DYNARR_LAST(trkSeg_t,*ap); + + sp->u.c.center = pos0; + sp->u.c.a0 = 0.0; + sp->u.c.a1 = 360.0; + sp->u.c.radius = d/2; + sp->type = point_selected?SEG_FILCRCL:SEG_CRVLIN; + sp->width = w; + sp->color = drawColorBlack; + + return 1; + +} /** @@ -146,35 +304,208 @@ int createEndPoint( coOrd pos0, //end on curve BOOL_T point_selected, BOOL_T point_selectable, - BOOL_T track_modifyable + BOOL_T track_modifyable, + BOOL_T track_present, + ANGLE_T angle, + DIST_T radius, + coOrd centert, + endHandle * endHandle ) { DIST_T d, w; + int num =0; d = tempD.scale*0.25; w = tempD.scale/tempD.dpi; /*double width*/ + num = 1; + if (point_selectable) { + sp[1].u.c.center = pos0; + sp[1].u.c.a0 = 0.0; + sp[1].u.c.a1 = 360.0; + sp[1].u.c.radius = d/2; + sp[1].type = SEG_CRVLIN; + sp[1].width = w; + sp[1].color = point_selected?drawColorBlue:drawColorRed; + num = 2; + } sp[0].u.c.center = pos0; sp[0].u.c.a0 = 0.0; sp[0].u.c.a1 = 360.0; sp[0].width = w; sp[0].u.c.radius = d/4; - sp[0].color = (point_selected>=0)?drawColorRed:drawColorBlack; + sp[0].color = point_selected?drawColorBlue:drawColorRed; if (track_modifyable) sp[0].type = SEG_CRVLIN; else sp[0].type = SEG_FILCRCL; - if (point_selectable) { - sp[1].u.c.center = pos0; - sp[1].u.c.a0 = 0.0; - sp[1].u.c.a1 = 360.0; - sp[1].u.c.radius = d/2; - sp[1].type = SEG_CRVLIN; - sp[1].width = w; - sp[1].color = drawColorRed; - return 2; + if (!track_present && endHandle ) { + endHandle->end_center = zero; + endHandle->end_curve = zero; + endHandle->end_valid = TRUE; + endHandle->mid_disp = 0.0; + DIST_T end_length = 20*trackGauge; + Translate(&endHandle->end_curve,pos0,angle,end_length); + Translate(&endHandle->end_center,pos0,angle,end_length/2); + if (radius>0.0) { + ANGLE_T a1 = R2D(end_length/radius); + if (DifferenceBetweenAngles(angle,FindAngle(centert,pos0))>0.0) { + a1 = -a1; + } + PointOnCircle( &endHandle->end_curve, centert,radius,NormalizeAngle(FindAngle(centert,pos0)+a1)); + PointOnCircle( &endHandle->end_center,centert,radius,NormalizeAngle(FindAngle(centert,pos0)+(a1/2.0))); + coOrd cm; + cm = endHandle->end_center; + ANGLE_T a = FindAngle(endHandle->end_curve,pos0); + Rotate(&cm,endHandle->end_curve,-a ); + endHandle->mid_disp = cm.x-endHandle->end_curve.x; + curveData_t curveData; + PlotCurve(crvCmdFromCenter,pos0,endHandle->end_center, endHandle->end_curve, &curveData, FALSE); + if (curveData.type == curveTypeStraight) { + coOrd pos_line[2]; + Translate(&pos_line[0],pos0,FindAngle(pos0,endHandle->end_curve)+90,trackGauge/2); + Translate(&pos_line[1],endHandle->end_curve,FindAngle(pos0,endHandle->end_curve)+90,trackGauge/2); + sp[num].type = SEG_STRLIN; + sp[num].width = w; + sp[num].u.l.pos[0] = pos_line[0]; + sp[num].u.l.pos[1] = pos_line[1]; + sp[num].color = (endHandle->last_selected||endHandle->radius_selected)?drawColorBlue:drawColorRed; + num++; + Translate(&pos_line[0],pos0,FindAngle(pos0,endHandle->end_curve)-90,trackGauge/2); + Translate(&pos_line[1],endHandle->end_curve,FindAngle(pos0,endHandle->end_curve)-90,trackGauge/2); + sp[num].type = SEG_STRLIN; + sp[num].width = w; + sp[num].u.l.pos[0] = pos_line[0]; + sp[num].u.l.pos[1] = pos_line[1]; + sp[num].color = (endHandle->last_selected||endHandle->radius_selected)?drawColorBlue:drawColorRed; + num++; + pos_line[0]= pos0; + Translate(&pos_line[1],pos0,-FindAngle(pos0,endHandle->end_curve),end_length); + sp[num].type = SEG_STRLIN; + sp[num].width = w; + sp[num].u.l.pos[0] = pos_line[0]; + sp[num].u.l.pos[1] = pos_line[1]; + sp[num].color = drawColorRed; + num++; + } else { + DIST_T pos_rad; + pos_rad = radius+trackGauge/2; + sp[num].type = SEG_CRVLIN; + sp[num].width = w; + sp[num].u.c.center = centert; + sp[num].u.c.radius = pos_rad; + ANGLE_T an0 = FindAngle(centert,pos0); + ANGLE_T an1 = FindAngle(centert,endHandle->end_curve); + if (DifferenceBetweenAngles(an0,an1)>0) { + sp[num].u.c.a1 = DifferenceBetweenAngles(an0,an1); + sp[num].u.c.a0 = an0; + } else { + sp[num].u.c.a1 = -DifferenceBetweenAngles(an0,an1); + sp[num].u.c.a0 = an1; + } + endHandle->arc_angle = sp[num].u.c.a1; + sp[num].color = (endHandle->last_selected||endHandle->radius_selected)?drawColorBlue:drawColorRed; + num++; + pos_rad = radius-trackGauge/2; + sp[num].type = SEG_CRVLIN; + sp[num].width = w; + sp[num].u.c.center = centert; + sp[num].u.c.radius = pos_rad; + sp[num].u.c.a1 = sp[num-1].u.c.a1; + sp[num].u.c.a0 = sp[num-1].u.c.a0; + sp[num].color = (endHandle->last_selected||endHandle->radius_selected)?drawColorBlue:drawColorRed; + num++; + } + } else { + coOrd pos_line[2]; + Translate(&pos_line[0],pos0,FindAngle(pos0,endHandle->end_curve)+90,trackGauge/2); + Translate(&pos_line[1],endHandle->end_curve,FindAngle(pos0,endHandle->end_curve)+90,trackGauge/2); + sp[num].type = SEG_STRLIN; + sp[num].width = w; + sp[num].u.l.pos[0] = pos_line[0]; + sp[num].u.l.pos[1] = pos_line[1]; + sp[num].color = (endHandle->last_selected||endHandle->radius_selected)?drawColorBlue:drawColorRed; + num++; + Translate(&pos_line[0],pos0,FindAngle(pos0,endHandle->end_curve)-90,trackGauge/2); + Translate(&pos_line[1],endHandle->end_curve,FindAngle(pos0,endHandle->end_curve)-90,trackGauge/2); + sp[num].type = SEG_STRLIN; + sp[num].width = w; + sp[num].u.l.pos[0] = pos_line[0]; + sp[num].u.l.pos[1] = pos_line[1]; + sp[num].color = (endHandle->last_selected||endHandle->radius_selected)?drawColorBlue:drawColorRed; + num++; + + } + coOrd pos_line[2]; + pos_line[0]= pos0; + Translate(&pos_line[1],pos0,angle+180,end_length); + sp[num].type = SEG_STRLIN; + sp[num].width = w; + sp[num].u.l.pos[0] = pos_line[0]; + sp[num].u.l.pos[1] = pos_line[1]; + sp[num].color = drawColorRed; + num++; + sp[num].type = SEG_CRVLIN; + sp[num].u.c.center = endHandle->end_curve; + sp[num].u.c.a0 = 0.0; + sp[num].u.c.a1 = 360.0; + sp[num].width = w; + sp[num].u.c.radius = d/4; + sp[num].color = endHandle->angle_selected?drawColorBlue:drawColorRed; + num++; + if (radius<=0.0) + DrawArrowHeads(&sp[num],endHandle->end_center,angle+90.0,TRUE,endHandle->radius_selected?drawColorBlue:drawColorRed); + else + DrawArrowHeads(&sp[num],endHandle->end_center,FindAngle(centert,endHandle->end_center),TRUE,endHandle->radius_selected?drawColorBlue:drawColorRed); + num=num+5; + } else if (endHandle) { + endHandle->end_valid=FALSE; } - return 1; + return num; +} + +static dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) + +static void CreateCornuEndAnchor(coOrd p, wBool_t lock) { + DIST_T d = tempD.scale*0.15; + + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = lock?SEG_FILCRCL:SEG_CRVLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.c.center = p; + anchors(i).u.c.radius = d/2; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).width = 0; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_CRVLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.c.center = p; + anchors(i).u.c.radius = d; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).width = 0; + +} + +static void CreateCornuExtendAnchor(coOrd p, ANGLE_T a, wBool_t selected) { + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),p,a,FALSE,wDrawColorBlue); } +static void CreateCornuAnchor(coOrd p, wBool_t open) { + DIST_T d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = open?SEG_CRVLIN:SEG_FILCRCL; + anchors(i).color = wDrawColorBlue; + anchors(i).u.c.center = p; + anchors(i).u.c.radius = d/2; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).width = 0; +} /* * Add element to DYNARR pointed to by caller from segment handed in @@ -182,7 +513,6 @@ int createEndPoint( void addSegCornu(dynArr_t * const array_p, trkSeg_p seg) { trkSeg_p s; - DYNARR_APPEND(trkSeg_t, * array_p, 10); //Adds 1 to cnt s = &DYNARR_N(trkSeg_t,* array_p,array_p->cnt-1); s->type = seg->type; @@ -206,20 +536,105 @@ void addSegCornu(dynArr_t * const array_p, trkSeg_p seg) { s->u = seg->u; } } -EXPORT void SetKnots(spiro_cp knots[6], coOrd posk[6]) { - for (int i = 0; i < 6; i++) { +EXPORT void SetKnots(spiro_cp knots[], coOrd posk[], char type[], int count) { + for (int i = 0; i < count; i++) { knots[i].x = posk[i].x; knots[i].y = posk[i].y; + knots[i].ty = type[i]; + } +} + +typedef struct { + coOrd pos; + char ty; +} points_t; + +// Take in extra points within Cornu +// G2 (position only k1'' = k2'' = 0); Also Cornu <-> Cornu +// G4 (position only - splitable for Cornu - a G4 point) k1''= k2'' + +BOOL_T CallCornuM(dynArr_t extra_points, BOOL_T end[2], coOrd pos[2], cornuParm_t * cp, dynArr_t * array_p, BOOL_T spots) { + array_p->cnt = 0; + //Create LH knots + //Find remote end point of track, create start knot + int ends[2]; + ends[0] = (end[0]?2:0); ends[1] = (end[0]?3:1)+extra_points.cnt; + spiro_cp * knots; + coOrd * posk; + char * type; + posk = MyMalloc((6+extra_points.cnt)*sizeof(coOrd)); + knots = MyMalloc((6+extra_points.cnt)*sizeof(spiro_cp)); + type = MyMalloc((6+extra_points.cnt)*sizeof(char)); + BOOL_T back; + ANGLE_T angle1; + + if (Da.bezc) free(Da.bezc); + + Da.bezc = new_bezctx_xtrkcad(array_p,ends,spots,tempD.scale*0.15/4); + + coOrd pos0 = pos[0]; + + if (end[0]) { + type[0] = SPIRO_OPEN_CONTOUR; + type[1] = SPIRO_G2; + type[2] = SPIRO_RIGHT; + if (cp->radius[0] == 0.0) { + Translate(&posk[0],pos0,cp->angle[0],10); + Translate(&posk[1],pos0,cp->angle[0],5); + } else { + angle1 = FindAngle(cp->center[0],pos[0]); + if (NormalizeAngle(angle1 - cp->angle[0])<180) back = TRUE; + else back = FALSE; + posk[0] = pos[0]; + Rotate(&posk[0],cp->center[0],(back)?-10:10); + posk[1] = pos[0]; + Rotate(&posk[1],cp->center[0],(back)?-5:5); + } + posk[2] = pos[0]; + } else { + type[0] = SPIRO_OPEN_CONTOUR; + posk[0] = pos[0]; + } + + for (int i=0;i<extra_points.cnt;i++) { + posk[(end[0]?3:1)+i] = DYNARR_N(coOrd,extra_points,i); + type[(end[0]?3:1)+i] = SPIRO_G4; + } + + posk[(end[0]?3:1)+extra_points.cnt] = pos[1]; + coOrd pos1 = pos[1]; + + if (end[1]) { + type[(end[0]?3:1)+extra_points.cnt] = SPIRO_LEFT; + type[(end[0]?3:1)+extra_points.cnt+1] = SPIRO_G2; + type[(end[0]?3:1)+extra_points.cnt+2] = SPIRO_END_OPEN_CONTOUR; + if (cp->radius[1] == 0.0) { + Translate(&posk[(end[0]?3:1)+extra_points.cnt+1],pos1,cp->angle[1],5); + Translate(&posk[(end[0]?3:1)+extra_points.cnt+2],pos1,cp->angle[1],10); + } else { + angle1 = FindAngle(cp->center[1],pos[1]); + if (NormalizeAngle(angle1 - cp->angle[1])>180) back = TRUE; + else back = FALSE; + posk[(end[0]?3:1)+extra_points.cnt+1] = pos[1]; + Rotate(&posk[(end[0]?3:1)+extra_points.cnt+1],cp->center[1],(back)?5:-5); + posk[(end[0]?3:1)+extra_points.cnt+2] = pos[1]; + Rotate(&posk[(end[0]?3:1)+extra_points.cnt+2],cp->center[1],(back)?10:-10); + } + } else { + type[(end[0]?3:1)+extra_points.cnt] = SPIRO_END_OPEN_CONTOUR; } - knots[0].ty = SPIRO_OPEN_CONTOUR; - knots[1].ty = SPIRO_G2; - knots[2].ty = SPIRO_RIGHT; - knots[3].ty = SPIRO_LEFT; - knots[4].ty = SPIRO_G2; - knots[5].ty = SPIRO_END_OPEN_CONTOUR; + SetKnots(knots, posk, type, ((end[0]?3:1)+(end[1]?3:1)+extra_points.cnt)); + TaggedSpiroCPsToBezier(knots,Da.bezc); + MyFree(posk); + MyFree(knots); + MyFree(type); + if (!bezctx_xtrkcad_close(Da.bezc)) { + return FALSE; + } + return TRUE; } -BOOL_T CallCornu0(coOrd pos[2], coOrd center[2], ANGLE_T angle[2], DIST_T radius[2], dynArr_t * array_p, BOOL_T spots) { +EXPORT BOOL_T CallCornu0(coOrd pos[2], coOrd center[2], ANGLE_T angle[2], DIST_T radius[2], dynArr_t * array_p, BOOL_T spots) { array_p->cnt = 0; //Create LH knots //Find remote end point of track, create start knot @@ -227,14 +642,17 @@ BOOL_T CallCornu0(coOrd pos[2], coOrd center[2], ANGLE_T angle[2], DIST_T radius ends[0] = 2; ends[1] = 3; spiro_cp knots[6]; coOrd posk[6]; + char type[6]; BOOL_T back; ANGLE_T angle1; if (Da.bezc) free(Da.bezc); - Da.bezc = new_bezctx_xtrkcad(array_p,ends,spots); + Da.bezc = new_bezctx_xtrkcad(array_p,ends,spots,tempD.scale*0.15/4); coOrd pos0 = pos[0]; + type[0] = SPIRO_OPEN_CONTOUR; + if (radius[0] == 0.0) { Translate(&posk[0],pos0,angle[0],10); @@ -248,9 +666,12 @@ BOOL_T CallCornu0(coOrd pos[2], coOrd center[2], ANGLE_T angle[2], DIST_T radius posk[1] = pos[0]; Rotate(&posk[1],center[0],(back)?-5:5); } + type[1] = SPIRO_G2; posk[2] = pos[0]; + type[2] = SPIRO_RIGHT; posk[3] = pos[1]; + type[3] = SPIRO_LEFT; coOrd pos1 = pos[1]; @@ -266,7 +687,10 @@ BOOL_T CallCornu0(coOrd pos[2], coOrd center[2], ANGLE_T angle[2], DIST_T radius posk[5] = pos[1]; Rotate(&posk[5],center[1],(back)?10:-10); } - SetKnots(knots,posk); + type[4] = SPIRO_G2; + type[5] = SPIRO_END_OPEN_CONTOUR; + + SetKnots(knots, posk, type, 6); TaggedSpiroCPsToBezier(knots,Da.bezc); if (!bezctx_xtrkcad_close(Da.bezc)) { return FALSE; @@ -290,19 +714,19 @@ BOOL_T CallCornu(coOrd pos[2], track_p trk[2], EPINX_T ep[2], dynArr_t * array_p if (Da.circleorHelix[i]) { //Helix/Circle only cp->radius[i] = params.arcR; cp->center[i] = params.arcP; - cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0)); + if (ep && ep[i]>=0) cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0)); } else if (params.type == curveTypeStraight) { cp->angle[i] = NormalizeAngle(angle+180); //Because end always backwards cp->radius[i] = 0.0; } else if ((params.type == curveTypeCornu || params.type == curveTypeBezier) && params.arcR == 0.0 ) { cp->radius[i] = 0.0; - cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0)); //Use point not end + if (ep && ep[i]>=0) cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0)); //Use point not end } else if (params.type == curveTypeCurve) { - cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0)); + if (ep && ep[i]>=0) cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0)); cp->radius[i] = params.arcR; cp->center[i] = params.arcP; } else if ((params.type == curveTypeCornu || params.type == curveTypeBezier) && params.arcR != 0.0 ){ - cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0)); + if (ep && ep[i]>=0) cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0)); cp->radius[i] = params.arcR; cp->center[i] = params.arcP; } else { @@ -317,6 +741,7 @@ BOOL_T CallCornu(coOrd pos[2], track_p trk[2], EPINX_T ep[2], dynArr_t * array_p } + /* * Draw Cornu while editing it. It consists of up to five elements - the ends, the curve and one or two End Points. * @@ -333,30 +758,26 @@ EXPORT void DrawCornuCurve( trkSeg_p second_trk, trkSeg_p extend1_trk, trkSeg_p extend2_trk, + trkSeg_p mids, + int midSegs_cnt, wDrawColor color ) { - long oldDrawOptions = tempD.funcs->options; - tempD.funcs->options = wDrawOptTemp; - long oldOptions = tempD.options; - tempD.options = DC_TICKS; - tempD.orig = mainD.orig; - tempD.angle = mainD.angle; if (first_trk) DrawSegs( &tempD, zero, 0.0, first_trk, 1, Da.trackGauge, drawColorBlack ); - if (crvSegs_cnt && curveSegs) + if (crvSegs_cnt>0 && curveSegs) DrawSegs( &tempD, zero, 0.0, curveSegs, crvSegs_cnt, Da.trackGauge, color ); if (second_trk) DrawSegs( &tempD, zero, 0.0, second_trk, 1, Da.trackGauge, drawColorBlack ); - if (ep1Segs_cnt && point1) + if (ep1Segs_cnt>0 && point1) DrawSegs( &tempD, zero, 0.0, point1, ep1Segs_cnt, Da.trackGauge, drawColorBlack ); - if (ep2Segs_cnt && point2) + if (ep2Segs_cnt>0 && point2) DrawSegs( &tempD, zero, 0.0, point2, ep2Segs_cnt, Da.trackGauge, drawColorBlack ); + if (midSegs_cnt>0 && mids) + DrawSegs( &tempD, zero, 0.0, mids, midSegs_cnt, Da.trackGauge, drawColorBlack ); if (extend1_trk) DrawSegs( &tempD, zero, 0.0, extend1_trk, 1, Da.trackGauge, drawColorBlack); if (extend2_trk) DrawSegs( &tempD, zero, 0.0, extend2_trk, 1, Da.trackGauge, drawColorBlack); - tempD.funcs->options = oldDrawOptions; - tempD.options = oldOptions; } @@ -373,35 +794,53 @@ void DrawTempCornu() { &Da.trk2Seg, Da.extend[0]?&Da.extendSeg[0]:NULL, Da.extend[1]?&Da.extendSeg[1]:NULL, - Da.minRadius<(GetLayoutMinTrackRadius()-EPSILON)?drawColorRed:drawColorBlack); + (trkSeg_t *)Da.midSegs.ptr,Da.midSegs.cnt, + fabs(Da.minRadius)<(GetLayoutMinTrackRadius()-EPSILON)?exceptionColor:normalColor); } -void CreateBothEnds(int selectPoint) { +void CreateBothEnds(int selectEndPoint, int selectMidPoint, int selectEndHandle, int lastSelected ) { BOOL_T selectable[2],modifyable[2]; selectable[0] = !Da.trk[0] || ( Da.trk[0] && !QueryTrack(Da.trk[0],Q_IS_CORNU) && !QueryTrack(Da.trk[0],Q_CAN_MODIFY_CONTROL_POINTS)); modifyable[0] = !Da.trk[0] || ( Da.trk[0] && QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY)); selectable[1] = !Da.trk[1] || ( - Da.trk[1] && !QueryTrack(Da.trk[1],Q_IS_CORNU) && !QueryTrack(Da.trk[0],Q_CAN_MODIFY_CONTROL_POINTS)); + Da.trk[1] && !QueryTrack(Da.trk[1],Q_IS_CORNU) && !QueryTrack(Da.trk[1],Q_CAN_MODIFY_CONTROL_POINTS)); modifyable[1] = !Da.trk[1] || ( Da.trk[1] && QueryTrack(Da.trk[1],Q_CORNU_CAN_MODIFY)); - if (selectPoint == -1) { - Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],FALSE,selectable[0],modifyable[0]); - Da.ep2Segs_da_cnt = createEndPoint(Da.ep2Segs, Da.pos[1],FALSE,selectable[1],modifyable[1]); - } else if (selectPoint == 0 || selectPoint == 1) { - Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],selectPoint == 0,selectable[0],modifyable[0]); - Da.ep2Segs_da_cnt = createEndPoint(Da.ep2Segs, Da.pos[1],selectPoint == 1,selectable[1],modifyable[1]); + + Da.endHandle[0].angle_selected = (selectEndHandle==1)?TRUE:FALSE; + Da.endHandle[0].radius_selected = (selectEndHandle==0)?TRUE:FALSE; + Da.endHandle[1].angle_selected = (selectEndHandle==3)?TRUE:FALSE; + Da.endHandle[1].radius_selected = (selectEndHandle==2)?TRUE:FALSE; + Da.endHandle[0].last_selected = lastSelected==0?TRUE:FALSE; + Da.endHandle[1].last_selected = lastSelected==1?TRUE:FALSE; + if (selectEndPoint == -1) { + Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],FALSE,selectable[0],modifyable[0],Da.trk[0]!=NULL,Da.angle[0],Da.radius[0],Da.center[0],Da.extend[0]?NULL:&Da.endHandle[0]); + Da.ep2Segs_da_cnt = createEndPoint(Da.ep2Segs, Da.pos[1],FALSE,selectable[1],modifyable[1],Da.trk[1]!=NULL,Da.angle[1],Da.radius[1],Da.center[1],Da.extend[1]?NULL:&Da.endHandle[1]); } else { - Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],FALSE,selectable[0],modifyable[0]); - Da.ep2Segs_da_cnt = createEndPoint(Da.ep2Segs, Da.pos[1],FALSE,selectable[1],modifyable[1]); + Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],selectEndPoint == 0,selectable[0],modifyable[0],Da.trk[0]!=NULL,Da.angle[0],Da.radius[0],Da.center[0],Da.extend[0]?NULL:&Da.endHandle[0]); + Da.ep2Segs_da_cnt = createEndPoint(Da.ep2Segs, Da.pos[1],selectEndPoint == 1,selectable[1],modifyable[1],Da.trk[1]!=NULL,Da.angle[1],Da.radius[1],Da.center[1],Da.extend[1]?NULL:&Da.endHandle[1]); } + Da.endHandle[0].end_valid = !Da.extend[0]; + Da.endHandle[1].end_valid = !Da.extend[1]; + DYNARR_RESET(trkSeg_t,Da.midSegs); + for (int i=0;i<Da.mid_points.cnt;i++) { + createMidPoint(&Da.midSegs, DYNARR_N(coOrd,Da.mid_points,i),selectMidPoint == i,TRUE, TRUE ); + } + if (Da.radius[0] >=0.0) Da.ends[0] = TRUE; + else Da.ends[0] = FALSE; + if (Da.radius[1] >=0.0) Da.ends[1] = TRUE; + else Da.ends[1] = FALSE; } -BOOL_T GetConnectedTrackParms(track_p t, const coOrd pos, int end, EPINX_T track_end) { +BOOL_T GetConnectedTrackParms(track_p t, const coOrd pos, int end, EPINX_T track_end, wBool_t extend) { trackParams_t trackParams; - if (!GetTrackParams(PARAMS_CORNU, t, pos, &trackParams)) return FALSE; + coOrd pos1; + if ((track_end>=0) && extend) pos1 = GetTrkEndPos(t,track_end); + else pos1 = pos; + if (!GetTrackParams(PARAMS_CORNU, t, pos1, &trackParams)) return FALSE; Da.radius[end] = 0.0; Da.center[end] = zero; Da.circleorHelix[end] = FALSE; @@ -415,7 +854,8 @@ BOOL_T GetConnectedTrackParms(track_p t, const coOrd pos, int end, EPINX_T track Da.circleorHelix[end] = TRUE; Da.angle[end] = trackParams.track_angle; //For Now } else { - Da.angle[end] = NormalizeAngle(trackParams.track_angle + (track_end?180:0)); + Da.angle[end] = NormalizeAngle(GetTrkEndAngle(t,track_end)+180); + //Da.angle[end] = NormalizeAngle(trackParams.track_angle + (track_end?180:0)); } } else if (trackParams.type == curveTypeBezier) { Da.angle[end] = NormalizeAngle(trackParams.track_angle+(track_end?180:0)); @@ -430,12 +870,12 @@ BOOL_T GetConnectedTrackParms(track_p t, const coOrd pos, int end, EPINX_T track } } else if (trackParams.type == curveTypeCornu) { int ep = trackParams.ep; - Da.angle[end] = NormalizeAngle(trackParams.cornuAngle[ep]+(track_end?180:0)); + Da.angle[end] = NormalizeAngle(trackParams.cornuAngle[ep]+180); Da.radius[end] = trackParams.cornuRadius[ep]; Da.pos[end] = trackParams.cornuEnd[ep]; Da.center[end] = trackParams.cornuCenter[ep]; } else if (trackParams.type == curveTypeStraight) { - if (Da.ep[end]>=0) + if (trackParams.ep>=0) Da.angle[end] = NormalizeAngle(GetTrkEndAngle(t,track_end)+180); //Ignore params.angle because it gives from nearest end else { Da.angle[end] = NormalizeAngle(trackParams.angle+180); //Turntable @@ -483,11 +923,106 @@ void SetUpCornuParms(cornuParm_t * cp) { cp->radius[1] = Da.radius[1]; } +track_p CreateCornuFromPoints(coOrd pos[2],BOOL_T track_end[2]) { + coOrd center[2]; + DIST_T radius[2]; + ANGLE_T angle[2]; + BOOL_T back, neg; + cornuParm_t new; + int inx,subinx; + coOrd pos_temp[2]; + + for (int i=0;i<2;i++) { + pos_temp[i] = pos[i]; + + if (!track_end[i] || (Da.radius[i]==-1.0)) { + + angle[i] = GetAngleSegs(Da.crvSegs_da.cnt,(trkSeg_t *)(Da.crvSegs_da.ptr),&pos_temp[i],&inx,NULL,&back,&subinx,&neg); + + trkSeg_p segPtr = &DYNARR_N(trkSeg_t, Da.crvSegs_da, inx); + + if (segPtr->type == SEG_BEZTRK) + segPtr = &DYNARR_N(trkSeg_t, segPtr->bezSegs, subinx); + + if (i==0) { + if (neg==back) angle[i] = NormalizeAngle(angle[i]+180); + } else { + if (!(neg==back)) angle[i] = NormalizeAngle(angle[i]+180); + } + + if (segPtr->type == SEG_STRTRK) { + radius[i] = 0.0; + center[i] = zero; + } else if (segPtr->type == SEG_CRVTRK) { + center[i] = segPtr->u.c.center; + radius[i] = fabs(segPtr->u.c.radius); + } + } else { + pos[i] = Da.pos[i]; + radius[i] = Da.radius[i]; + center[i] = Da.center[i]; + angle[i] = Da.angle[i]; + neg = FALSE; + back = FALSE; + } + } + new.pos[0] = pos[0]; + new.pos[1] = pos[1]; + new.angle[0] = angle[0]; + new.angle[1] = angle[1]; + new.center[0] = center[0]; + new.center[1] = center[1]; + new.radius[0] = radius[0]; + new.radius[1] = radius[1]; + + track_p trk1 = NewCornuTrack(new.pos,new.center,new.angle,new.radius,NULL,0); + if (trk1==NULL) { + wBeep(); + InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"), + new.pos[0].x,new.pos[0].y, + new.pos[1].x,new.pos[1].y, + new.center[0].x,new.center[0].y, + new.center[1].x,new.center[1].y, + new.angle[0],new.angle[1], + FormatDistance(new.radius[0]),FormatDistance(new.radius[1])); + UndoEnd(); + return NULL; + } + return trk1; +} + struct extraData { cornuData_t cornuData; }; +ANGLE_T GetOpenAngle(coOrd pos[2],ANGLE_T angle[2],int moved) { + ANGLE_T a = FindAngle(pos[1-moved],pos[moved]); + ANGLE_T diff = (180+a)-angle[1-moved]; //Difference between input and line + return a+diff; //Change to line plus this at the other end +} + +static struct { + ANGLE_T angle; + DIST_T radius; + +} cornuModCmdContext; + +static BOOL_T infoSubst = FALSE; + +static paramFloatRange_t r10000_10000 = {-10000, 10000}; +static paramFloatRange_t r0_360 = { 0, 360, 80 }; + +static paramData_t cornuModPLs[] = { + +#define cornuModEndAnglePD (cornuModPLs[0]) +#define cornuModEndAngle 0 + { PD_FLOAT, &cornuModCmdContext.angle, "End Angle", PDO_NORECORD|BO_ENTER, &r0_360, N_("End Angle") }, +#define cornuModEndRadiusPD (cornuModPLs[1]) +#define cornuModEndRadius 1 + { PD_FLOAT, &cornuModCmdContext.radius, "End Radius", PDO_DIM|PDO_NORECORD|BO_ENTER, &r10000_10000, N_("End Radius") }, +}; +static paramGroup_t cornuModPG = { "cornuMod", 0, cornuModPLs, sizeof cornuModPLs/sizeof cornuModPLs[0] }; /* * AdjustCornuCurve @@ -509,221 +1044,535 @@ EXPORT STATUS_T AdjustCornuCurve( track_p t; DIST_T d; ANGLE_T a, a2; - DIST_T dd; EPINX_T ep; cornuParm_t cp; + wControl_p controls[5]; //Always needs a NULL last entry + char * labels[4]; + + Da.cmdType = (long)commandContext; + if (Da.state != PICK_POINT && Da.state != POINT_PICKED && Da.state != TRACK_SELECTED) return C_CONTINUE; switch ( action & 0xFF) { case C_START: - Da.selectPoint = -1; - Da.extend[0] = FALSE; - Da.extend[1] = FALSE; - CreateBothEnds(Da.selectPoint); - Da.crvSegs_da.cnt = 0; - SetUpCornuParms(&cp); - if (CallCornu(Da.pos,Da.trk,Da.ep,&Da.crvSegs_da,&cp)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; - else Da.crvSegs_da_cnt = 0; - Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da); - InfoMessage( _("Select End-Point") ); - DrawTempCornu(); - return C_CONTINUE; + DYNARR_RESET(trkSeg_t,anchors_da); + infoSubst = FALSE; + Da.selectEndPoint = -1; + Da.selectMidPoint = -1; + Da.selectEndHandle = -1; + Da.prevSelected = -1; + Da.prevEndPoint = -1; + Da.extend[0] = FALSE; + Da.extend[1] = FALSE; + CreateBothEnds(Da.selectEndPoint, Da.selectMidPoint,Da.selectEndHandle,Da.prevSelected); + Da.crvSegs_da.cnt = 0; + SetUpCornuParms(&cp); + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + else Da.crvSegs_da_cnt = 0; + Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da); + InfoMessage( _("Select Point, or Add Point") ); + TempRedraw(); // AdjustCornuCurve C_START + return C_CONTINUE; + + case C_UPDATE: + if (Da.state != PICK_POINT && Da.prevSelected>-1) return C_CONTINUE; + int sel = Da.prevSelected; + if (Da.trk[sel]) return C_CONTINUE; //Track Here - should never happen + Da.radius[sel] = fabs(cornuModCmdContext.radius); + Da.angle[sel] = cornuModCmdContext.angle; + if (cornuModCmdContext.radius!=0) + Translate(&Da.center[sel],Da.pos[sel],Da.angle[sel]+90,cornuModCmdContext.radius); + CreateBothEnds(Da.prevSelected,-1,-1,Da.prevSelected); + SetUpCornuParms(&cp); + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + else Da.crvSegs_da_cnt = 0; + Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da); + return C_CONTINUE; + break; + + case wActionMove: + if (Da.state == NONE || Da.state == PICK_POINT) { + DYNARR_RESET(trkSeg_t,anchors_da); + for(int i=0;i<2;i++) { + if (IsClose(FindDistance(pos,Da.pos[i]))) { + if (((MyGetKeyState() & WKEY_SHIFT) != 0) && Da.selectTrack) { + CreateCornuExtendAnchor(Da.pos[i], Da.angle[i], FALSE); + return C_CONTINUE; + } else { + CreateCornuAnchor(Da.pos[i], FALSE); + return C_CONTINUE; + } + } + } + CreateBothEnds(Da.selectEndPoint,Da.selectMidPoint,Da.selectEndHandle,Da.prevSelected); + Da.selectEndPoint = -1; + for (int i=0;i<Da.mid_points.cnt;i++) { + d = FindDistance(DYNARR_N(coOrd,Da.mid_points,i),pos); + if (IsClose(d)) { + CreateCornuAnchor(DYNARR_N(coOrd,Da.mid_points,i),FALSE); + return C_CONTINUE; + } + } + for (int i=0;i<2;i++) { + if (Da.endHandle[i].end_valid == FALSE) continue; + d = FindDistance(Da.endHandle[i].end_center,pos); + if (IsClose(d)) { + CreateCornuAnchor(Da.endHandle[i].end_center, FALSE); + return C_CONTINUE; + } + } + for (int i=0;i<2;i++) { + if (Da.endHandle[i].end_valid == FALSE) continue; + d = FindDistance(Da.endHandle[i].end_curve,pos); + if (IsClose(d)) { + CreateCornuAnchor(Da.endHandle[i].end_curve, FALSE); + return C_CONTINUE; + } + } + coOrd temp_pos = pos; + if (IsClose(DistanceSegs(zero,0.0,Da.crvSegs_da.cnt,(trkSeg_p)Da.crvSegs_da.ptr,&temp_pos,NULL))) { + CreateCornuAnchor(temp_pos, TRUE); + } + } + return C_CONTINUE; case C_DOWN: + DYNARR_RESET(trkSeg_t,anchors_da); if (Da.state != PICK_POINT) return C_CONTINUE; - dd = 10000.0; - Da.selectPoint = -1; + Da.selectEndPoint = -1; + Da.selectMidPoint = -1; + Da.selectEndHandle = -1; + Da.prevSelected = -1; + if (infoSubst) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } for (int i=0;i<2;i++) { d = FindDistance(Da.pos[i],pos); - if (d < dd) { - dd = d; - Da.selectPoint = i; + if (IsClose(d)) { + Da.selectEndPoint = i; + CreateCornuAnchor(Da.pos[i],FALSE); + break; } } - if (!IsClose(dd) ) Da.selectPoint = -1; - if (Da.selectPoint == -1) { + if (Da.selectEndPoint == -1) { + for (int i=0;i<Da.mid_points.cnt;i++) { + d = FindDistance(DYNARR_N(coOrd,Da.mid_points,i),pos); + if (IsClose(d)) { + Da.selectMidPoint = i; + Da.selectEndPoint = -1; + CreateCornuAnchor(DYNARR_N(coOrd,Da.mid_points,i),FALSE); + break; + } + } + if (Da.selectMidPoint == -1 ) { + for (int i=0;i<2;i++) { + if (Da.endHandle[i].end_valid == FALSE) continue; + d = FindDistance(Da.endHandle[i].end_center,pos); + if (IsClose(d)) { + Da.selectEndHandle = i*2; + Da.selectEndPoint = -1; + Da.selectMidPoint = -1; + CreateCornuAnchor(Da.endHandle[i].end_center,i); + break; + } + } + if (Da.selectEndHandle == -1) { + for (int i=0;i<2;i++) { + if (Da.endHandle[i].end_valid == FALSE) continue; + d = FindDistance(Da.endHandle[i].end_curve,pos); + if (IsClose(d)) { + Da.selectEndHandle = 1+i*2; + Da.selectEndPoint = -1; + Da.selectMidPoint = -1; + CreateCornuAnchor(Da.endHandle[i].end_curve,i); + break; + } + } + } + } + } else { //We picked an end point + if (!Da.trk[Da.selectEndPoint] && ((MyGetKeyState() & WKEY_SHIFT) != 0) && Da.selectTrack) { //With Shift no track -> Extend + Da.extend[Da.selectEndPoint] = TRUE; //Adding to end Point + DYNARR_RESET(trkSeg_t,anchors_da); + CreateCornuExtendAnchor(Da.pos[Da.selectEndPoint], Da.angle[Da.selectEndPoint], FALSE); + } + } + if (Da.selectMidPoint ==-1 && Da.selectEndPoint ==-1 && Da.selectEndHandle ==-1) { + coOrd temp_pos = pos; + wIndex_t index; + if (IsClose(DistanceSegs(zero,0.0,Da.crvSegs_da.cnt,(trkSeg_p)Da.crvSegs_da.ptr,&temp_pos,&index))) { + //Add Point between two other points + //Find closest two points along Track + int closest = -1; + wIndex_t pIndex, nIndex; + temp_pos = Da.pos[0]; + DistanceSegs(zero,0.0,Da.crvSegs_da.cnt,(trkSeg_p)Da.crvSegs_da.ptr,&temp_pos,&pIndex); + if (Da.mid_points.cnt>0) { + for (int i=0;i<Da.mid_points.cnt;i++) { + temp_pos = DYNARR_N(coOrd ,Da.mid_points,i); + DistanceSegs(zero,0.0,Da.crvSegs_da.cnt,(trkSeg_p)Da.crvSegs_da.ptr,&temp_pos,&nIndex); + if (((pIndex<=index) && (nIndex>=index))) { + closest = i; + break; + } + pIndex = nIndex; + } + temp_pos = Da.pos[1]; + DistanceSegs(zero,0.0,Da.crvSegs_da.cnt,(trkSeg_p)Da.crvSegs_da.ptr,&temp_pos,&nIndex); + if (index == nIndex) closest = Da.mid_points.cnt; + if (closest == -1) + closest = Da.mid_points.cnt; + } else closest = 0; + DYNARR_APPEND(coOrd,Da.mid_points,1); + for (int i=Da.mid_points.cnt-1;i>closest;i--) { + DYNARR_N(coOrd,Da.mid_points,i) = DYNARR_N(coOrd ,Da.mid_points,i-1); + } + DYNARR_N(coOrd,Da.mid_points,closest) = pos; + Da.selectMidPoint = closest; + Da.number_of_points++; + CreateCornuAnchor(pos,FALSE); + InfoMessage("Pin Point Added"); + } else { + wBeep(); + InfoMessage("Add Point Is Not on Track"); + return C_CONTINUE; + } + } + if (Da.selectEndPoint == -1 && Da.selectMidPoint == -1 && Da.selectEndHandle ==-1) { wBeep(); - InfoMessage( _("Not close enough to end point, reselect") ); + InfoMessage( _("Not close enough to track or point, reselect") ); return C_CONTINUE; } else { - pos = Da.pos[Da.selectPoint]; + if (Da.selectEndPoint >=0 ) { + pos = Da.pos[Da.selectEndPoint]; + if (Da.extend[Da.selectEndPoint]) + InfoMessage( _("Drag out end of Cornu")); + else if (Da.trk[Da.selectEndPoint]) { + InfoMessage( _("Drag along end of track")); + } else + InfoMessage( _("Drag to move")); + } else if (Da.selectMidPoint >=0 ) { + pos = DYNARR_N(coOrd,Da.mid_points,Da.selectMidPoint); + InfoMessage( _("Drag point to new location, Delete to remove")); + } else { + if (Da.selectEndHandle%2 == 0) { + pos = Da.endHandle[Da.selectEndHandle/2].end_center; + InfoMessage( _("Drag to change end radius")); + } else { + pos = Da.endHandle[Da.selectEndHandle/2].end_curve; + InfoMessage( _("Drag to change end angle")); + } + } Da.state = POINT_PICKED; - InfoMessage( _("Drag point %d to new location and release it"),Da.selectPoint+1 ); } - DrawTempCornu(); //wipe out - CreateBothEnds(Da.selectPoint); + CreateBothEnds(Da.selectEndPoint,Da.selectMidPoint,Da.selectEndHandle,Da.prevSelected); SetUpCornuParms(&cp); - if (CallCornu(Da.pos, Da.trk,Da.ep, &Da.crvSegs_da, &cp)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; else Da.crvSegs_da_cnt = 0; Da.minRadius = CornuMinRadius(Da.pos, Da.crvSegs_da); - DrawTempCornu(); return C_CONTINUE; case C_MOVE: + DYNARR_RESET(trkSeg_t,anchors_da); if (Da.state != POINT_PICKED) { - InfoMessage(_("Pick any circle to adjust it by dragging - Enter to accept, Esc to cancel")); + InfoMessage(_("Pick any circle to adjust or add - Enter to accept, Esc to cancel")); return C_CONTINUE; } - //If locked, reset pos to be on line from other track - int sel = Da.selectPoint; - coOrd pos2 = pos; - BOOL_T inside = FALSE; - if (Da.trk[sel]) { //There is a track - if (OnTrack(&pos,FALSE,TRUE) == Da.trk[sel]) { //And the pos is on it - inside = TRUE; - if (!QueryTrack(Da.trk[Da.selectPoint],Q_CORNU_CAN_MODIFY)) { //Turnouts - InfoMessage(_("Track can't be split")); - if (Da.ep[sel]>=0) //Ignore if turntable - pos = GetTrkEndPos(Da.trk[sel],Da.ep[sel]); + if (Da.selectEndPoint >= 0) { + //If locked, reset pos to be on line from other track + int sel = Da.selectEndPoint; + coOrd pos2 = pos; + BOOL_T inside = FALSE; + if (Da.trk[sel]) { //Track + if (OnTrack(&pos,FALSE,TRUE) == Da.trk[sel]) { //And the pos is on it + inside = TRUE; + if (!QueryTrack(Da.trk[Da.selectEndPoint],Q_CORNU_CAN_MODIFY)) { //Turnouts + InfoMessage(_("Track can't be split")); + inside = FALSE; + if (Da.ep[sel]>=0) { //If not turntable + Da.pos[sel] = pos = GetTrkEndPos(Da.trk[sel],Da.ep[sel]); + } else { + if (QueryTrack(Da.trk[sel],Q_CAN_ADD_ENDPOINTS)){ //Turntable + trackParams_t tp; + if (!GetTrackParams(PARAMS_CORNU, Da.trk[sel], pos, &tp)) return C_CONTINUE; + ANGLE_T a = tp.angle; + Translate(&pos,tp.ttcenter,a,tp.ttradius); + Da.angle[sel] = NormalizeAngle(a+180); + SetUpCornuParms(&cp); + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + else Da.crvSegs_da_cnt = 0; + } else return C_CONTINUE; + } + } + } else { + pos = pos2; //Put Back to original position as outside track } - } else { - pos = pos2; //Put Back to original position as outside track - } - // Stop the user extending right through the other track - if (Da.ep[sel]>=0 && QueryTrack(Da.trk[sel],Q_CORNU_CAN_MODIFY)) { //For non-turnouts - if ((!QueryTrack(Da.trk[sel],Q_CAN_ADD_ENDPOINTS)) // But Not Helix or Circle - && (!QueryTrack(Da.trk[sel],Q_HAS_VARIABLE_ENDPOINTS))) { // Not a Turntable - DIST_T ab = FindDistance(GetTrkEndPos(Da.trk[sel],Da.ep[sel]),GetTrkEndPos(Da.trk[sel],1-Da.ep[sel])); - DIST_T ac = FindDistance(GetTrkEndPos(Da.trk[sel],Da.ep[sel]),pos); - DIST_T cb = FindDistance(GetTrkEndPos(Da.trk[sel],1-Da.ep[sel]), pos); - if (cb<minLength) { - InfoMessage(_("Too close to other end of selected Track")); - return C_CONTINUE; + if (!inside) { + if (Da.ep[sel]>=0) { //Track defined end point + ANGLE_T diff = NormalizeAngle(GetTrkEndAngle(Da.trk[sel],Da.ep[sel])-FindAngle(GetTrkEndPos(Da.trk[sel],Da.ep[sel]),pos)); + if (diff>90.0 && diff<270.0) { //The point is not on track but outside cone of end angle+/-90 + Da.pos[sel] = pos = GetTrkEndPos(Da.trk[sel],Da.ep[sel]); + CreateBothEnds(Da.selectEndPoint,Da.selectMidPoint,Da.selectEndHandle,Da.prevSelected); + return C_CONTINUE; + } + } else { //Not an end point + if (QueryTrack(Da.trk[sel],Q_CAN_ADD_ENDPOINTS)){ //Turntable + trackParams_t tp; + if (!GetTrackParams(PARAMS_CORNU, Da.trk[sel], pos, &tp)) return C_CONTINUE; + ANGLE_T a = tp.angle; + coOrd edge; + Translate(&edge,tp.ttcenter,a,tp.ttradius); + ANGLE_T da = DifferenceBetweenAngles(FindAngle(edge,pos),a); + DIST_T d = fabs(FindDistance(edge,pos)*cos(R2D(da))); + Translate(&pos,edge,a,d); + Da.angle[sel] = NormalizeAngle(a+180); + Da.pos[sel] = pos; + CreateBothEnds(Da.selectEndPoint,Da.selectMidPoint,Da.selectEndHandle,Da.prevSelected); + Da.extendSeg[sel].type = SEG_STRTRK; + Da.extendSeg[sel].width = 0; + Da.extendSeg[sel].color = wDrawColorBlack; + Da.extendSeg[sel].u.l.pos[1-sel] = pos; + Da.extendSeg[sel].u.l.pos[sel] = edge; + Da.extend[sel] = TRUE; + SetUpCornuParms(&cp); + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + else Da.crvSegs_da_cnt = 0; + return C_CONTINUE; //Stop moving end point + } else return C_CONTINUE; + } } - if ((ac>=cb) && (ac>=ab)) { //Closer to far end and as long as the track - pos = GetTrkEndPos(Da.trk[sel],1-Da.ep[sel]); //Make other end of track + // Stop the user extending right through the other track + if (Da.ep[sel]>=0 && QueryTrack(Da.trk[sel],Q_CORNU_CAN_MODIFY)) { //For non-turnouts + if ((!QueryTrack(Da.trk[sel],Q_CAN_ADD_ENDPOINTS)) // Not Turntable - may not be needed + && (!QueryTrack(Da.trk[sel],Q_HAS_VARIABLE_ENDPOINTS))) { // Not Helix or a Circle + DIST_T ab = FindDistance(GetTrkEndPos(Da.trk[sel],Da.ep[sel]),GetTrkEndPos(Da.trk[sel],1-Da.ep[sel])); + DIST_T ac = FindDistance(GetTrkEndPos(Da.trk[sel],Da.ep[sel]),pos); + DIST_T cb = FindDistance(GetTrkEndPos(Da.trk[sel],1-Da.ep[sel]),pos); + if (cb<minLength) { + InfoMessage(_("Too close to other end of selected Track")); + return C_CONTINUE; + } + if ((ac>=cb) && (ac>=ab)) { //Closer to far end and as long as the track + pos = GetTrkEndPos(Da.trk[sel],1-Da.ep[sel]); //Make other end of track + } + } + } else if (Da.ep[sel]>=0 && inside) { //Has a point and inside track + InfoMessage(_("Can't move end inside a turnout")); //Turnouts are stuck to end-point + Da.pos[sel] = pos = GetTrkEndPos(Da.trk[sel],Da.ep[sel]); + CreateBothEnds(Da.selectEndPoint,Da.selectMidPoint,Da.selectEndHandle,Da.prevSelected); + return C_CONTINUE; } } - } - } - DrawTempCornu(); //wipe out old - Da.extend[sel] = FALSE; - if(!Da.trk[sel]) { //Cornu with no ends - struct extraData *xx = GetTrkExtraData(Da.selectTrack); - Da.pos[sel] = xx->cornuData.pos[sel]; //Re-Copy parms from old trk - Da.radius[sel] = xx->cornuData.r[sel]; - Da.angle[sel] = xx->cornuData.a[sel]; - Da.center[sel] = xx->cornuData.c[sel]; - if (Da.radius[sel] == 0) { //Straight - Da.extendSeg[sel].type = SEG_STRTRK; - Da.extendSeg[sel].width = 0; - Da.extendSeg[sel].color = wDrawColorBlack; - Da.extendSeg[sel].u.l.pos[1-sel] = Da.pos[sel]; - d = FindDistance( Da.extendSeg[sel].u.l.pos[1-sel], pos ); - a = NormalizeAngle(Da.angle[sel]-FindAngle(pos,Da.pos[sel])); - if (cos(D2R(a))<=0) { - Translate( &Da.extendSeg[sel].u.l.pos[sel], - Da.extendSeg[sel].u.l.pos[1-sel], - Da.angle[sel], - d * cos(D2R(a))); - pos = Da.extendSeg[sel].u.l.pos[1-sel]; - Da.extend[sel] = TRUE; - } - } else { //Curve - Da.extendSeg[sel].type = SEG_CRVTRK; - Da.extendSeg[sel].width = 0; - Da.extendSeg[sel].color = wDrawColorBlack; - Da.extendSeg[sel].u.c.center = Da.center[sel]; - Da.extendSeg[sel].u.c.radius = Da.radius[sel]; - a = FindAngle( Da.center[sel], pos ); - PointOnCircle( &pos, Da.center[sel], Da.radius[sel], a ); - a2 = FindAngle(Da.center[sel],Da.pos[sel]); - if (((Da.angle[sel] < 180) && (a2>90 && a2<270)) || - ((Da.angle[sel] > 180) && (a2<90 || a2>270))) { - Da.extendSeg[sel].u.c.a0 = a; - Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a2-a); + if(!Da.trk[sel]) { //Cornu with no end + if (((MyGetKeyState() & WKEY_SHIFT) != 0) && Da.selectTrack) { //Extend end locked + SetUpCornuParms(&cp); + CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,FALSE); + struct extraData *xx = GetTrkExtraData(Da.selectTrack); + if (Da.radius[sel] == 0) { //Straight + Da.extendSeg[sel].type = SEG_STRTRK; + Da.extendSeg[sel].width = 0; + Da.extendSeg[sel].color = wDrawColorBlack; + Da.extendSeg[sel].u.l.pos[1-sel] = Da.pos[sel]; + d = FindDistance( Da.extendSeg[sel].u.l.pos[1-sel], pos ); + a = NormalizeAngle(Da.angle[sel]-FindAngle(pos,Da.pos[sel])); + if (cos(D2R(a))<=0) { + Translate( &Da.extendSeg[sel].u.l.pos[sel], + Da.extendSeg[sel].u.l.pos[1-sel], + Da.angle[sel], - d * cos(D2R(a))); + pos = Da.extendSeg[sel].u.l.pos[sel]; + Da.extend[sel] = TRUE; + } else Da.extend[sel] = FALSE; + } else { //Curve + Da.extendSeg[sel].type = SEG_CRVTRK; + Da.extendSeg[sel].width = 0; + Da.extendSeg[sel].color = wDrawColorBlack; + Da.extendSeg[sel].u.c.center = Da.center[sel]; + Da.extendSeg[sel].u.c.radius = Da.radius[sel]; + a = FindAngle( Da.center[sel], pos ); + PointOnCircle( &pos, Da.extendSeg[sel].u.c.center, Da.radius[sel], a ); + a2 = FindAngle(Da.extendSeg[sel].u.c.center,Da.pos[sel]); + if (((Da.angle[sel] < 180) && (a2>90 && a2<270)) || + ((Da.angle[sel] > 180) && (a2<90 || a2>270))) { + Da.extendSeg[sel].u.c.a0 = a; + Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a2-a); + } else { + Da.extendSeg[sel].u.c.a0 = a2; + Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a-a2); + } + if (Da.extendSeg[sel].u.c.a1 == 0 || Da.extendSeg[sel].u.c.a1 >180 ) + Da.extend[sel] = FALSE; + else + Da.extend[sel] = TRUE; + } } else { - Da.extendSeg[sel].u.c.a0 = a2; - Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a-a2); - } - if (Da.extendSeg[sel].u.c.a1 == 0 || Da.extendSeg[sel].u.c.a1 >180 ) Da.extend[sel] = FALSE; - else - Da.extend[sel] = TRUE; - } - if (Da.extend[sel] == FALSE) { // Not extending - so trim along our own Cornu - GetCornuParmsNear(Da.selectTrack, sel, &pos, &Da.center[sel], &Da.angle[sel], &Da.radius[sel] ); - Da.pos[sel] = pos; - } - } else { //Cornu with ends - if (inside) Da.pos[sel] = pos; - if (!GetConnectedTrackParms(Da.trk[sel],pos,sel,Da.ep[sel])) { - DrawTempCornu(); - wBeep(); - return C_CONTINUE; //Stop drawing - } - CorrectHelixAngles(); - if (!inside) { //Extend the track - if (Da.trackType[sel] == curveTypeStraight) { //Extend with a straight - Da.extendSeg[sel].type = SEG_STRTRK; - Da.extendSeg[sel].width = 0; - Da.extendSeg[sel].color = wDrawColorBlack; - if (Da.ep[sel]>=0) { - Da.extendSeg[sel].u.l.pos[0] = GetTrkEndPos( Da.trk[sel], Da.ep[sel] ); - a = NormalizeAngle(Da.angle[sel]-FindAngle(pos,GetTrkEndPos(Da.trk[sel],Da.ep[sel]))); - } else { //Turntable when unconnected - Da.extendSeg[sel].u.l.pos[0] = Da.pos[sel]; - a = NormalizeAngle(Da.angle[sel]-FindAngle(pos,Da.pos[sel])); + coOrd offset; //Just move end (no shift) + offset.x = pos.x-Da.pos[sel].x; + offset.y = pos.y-Da.pos[sel].y; + Da.pos[sel] = pos; + if (Da.radius[sel] >0.0) { + Da.center[sel].x += offset.x; + Da.center[sel].y += offset.y; } - // Remove any extend in opposite direction for Turntable/Turnouts - if ((QueryTrack(Da.trk[sel],Q_CAN_ADD_ENDPOINTS) && Da.ep[sel]>=0) - && (!QueryTrack(Da.trk[sel],Q_CORNU_CAN_MODIFY)) - && (a>90 && a<270)) { - Da.extend[sel] = FALSE; //Turntable with point and extension is other side of well - Da.pos[sel] = GetTrkEndPos(Da.trk[sel],Da.ep[sel]); - } else { - Da.extend[sel] = TRUE; - d = FindDistance( Da.extendSeg[sel].u.l.pos[0], pos ); - Translate( &Da.extendSeg[sel].u.l.pos[1], - Da.extendSeg[sel].u.l.pos[0], - Da.angle[sel], -d * cos(D2R(a))); - Da.pos[sel] = pos = Da.extendSeg[sel].u.l.pos[1]; + if (Da.selectTrack) { //We have track + if (!Da.trk[sel] && ((t = OnTrackIgnore(&pos,FALSE,TRUE,Da.selectTrack))!= NULL) ) { + if ((ep = PickUnconnectedEndPointSilent(pos,t))>=0) { + pos = GetTrkEndPos(t,ep); + if (IsClose(FindDistance(pos,pos)/2)) { + CreateCornuEndAnchor(pos,FALSE); + } + } + } + } else { //Not yet a track + coOrd p = pos; + Da.angle[sel] = GetOpenAngle(Da.pos,Da.angle,sel); + if ((t = OnTrack(&p,FALSE,TRUE)) !=NULL ) { + if ((ep = PickUnconnectedEndPointSilent(pos,t))>=0) { + p = GetTrkEndPos(t,ep); + if (IsClose(FindDistance(p,pos)/2)) { + CreateCornuEndAnchor(p,FALSE); + } + } + } } - } else if (Da.trackType[sel] == curveTypeCurve) { //Extend with temp curve - Da.extendSeg[sel].type = SEG_CRVTRK; - Da.extendSeg[sel].width = 0; - Da.extendSeg[sel].color = wDrawColorBlack; - Da.extendSeg[sel].u.c.center = Da.center[sel]; - Da.extendSeg[sel].u.c.radius = Da.radius[sel]; - a = FindAngle( Da.center[sel], pos ); - PointOnCircle( &pos, Da.center[sel], Da.radius[sel], a ); - a2 = FindAngle(Da.center[sel],GetTrkEndPos(Da.trk[sel],Da.ep[sel])); - if ((Da.angle[sel] < 180 && (a2>90 && a2 <270)) || - (Da.angle[sel] > 180 && (a2<90 || a2 >270))) { - Da.extendSeg[sel].u.c.a0 = a2; - Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a-a2); - } else { - Da.extendSeg[sel].u.c.a0 = a; - Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a2-a); + } + } else { //Cornu with ends + if (inside) Da.pos[sel] = pos; + if (!GetConnectedTrackParms(Da.trk[sel],pos,sel,Da.ep[sel],inside?FALSE:TRUE)) { + wBeep(); + return C_CONTINUE; //Stop drawing + } + CorrectHelixAngles(); + if (!inside) { //Extend the track + if (Da.trackType[sel] == curveTypeStraight) { //Extend with a straight + Da.extendSeg[sel].type = SEG_STRTRK; + Da.extendSeg[sel].width = 0; + Da.extendSeg[sel].color = wDrawColorBlack; + if (Da.ep[sel]>=0) { + Da.extendSeg[sel].u.l.pos[0] = GetTrkEndPos( Da.trk[sel], Da.ep[sel] ); + a = NormalizeAngle(Da.angle[sel]-FindAngle(pos,GetTrkEndPos(Da.trk[sel],Da.ep[sel]))); + } else { //Turntable when unconnected + Da.extendSeg[sel].u.l.pos[0] = Da.pos[sel]; + a = NormalizeAngle(Da.angle[sel]-FindAngle(pos,Da.pos[sel])); + } + // Remove any extend in opposite direction for Turntable/Turnouts + if ((QueryTrack(Da.trk[sel],Q_CAN_ADD_ENDPOINTS) && Da.ep[sel]>=0) + && (!QueryTrack(Da.trk[sel],Q_CORNU_CAN_MODIFY)) + && (a>90 && a<270)) { + Da.extend[sel] = FALSE; //Turntable with point and extension is other side of well + Da.pos[sel] = GetTrkEndPos(Da.trk[sel],Da.ep[sel]); + } else { + Da.extend[sel] = TRUE; + d = FindDistance( Da.extendSeg[sel].u.l.pos[0], pos ); + Translate( &Da.extendSeg[sel].u.l.pos[1], + Da.extendSeg[sel].u.l.pos[0], + Da.angle[sel], -d * cos(D2R(a))); + Da.pos[sel] = pos = Da.extendSeg[sel].u.l.pos[1]; + } + } else if (Da.trackType[sel] == curveTypeCurve) { //Extend with temp curve + Da.extendSeg[sel].type = SEG_CRVTRK; + Da.extendSeg[sel].width = 0; + Da.extendSeg[sel].color = wDrawColorBlack; + Da.extendSeg[sel].u.c.center = Da.center[sel]; + Da.extendSeg[sel].u.c.radius = Da.radius[sel]; + a = FindAngle( Da.center[sel], pos ); + PointOnCircle( &pos, Da.center[sel], Da.radius[sel], a ); + a2 = FindAngle(Da.center[sel],GetTrkEndPos(Da.trk[sel],Da.ep[sel])); + if ((Da.angle[sel] < 180 && (a2>90 && a2 <270)) || + (Da.angle[sel] > 180 && (a2<90 || a2 >270))) { + Da.extendSeg[sel].u.c.a0 = a2; + Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a-a2); + } else { + Da.extendSeg[sel].u.c.a0 = a; + Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a2-a); + } + if (Da.extendSeg[sel].u.c.a1 == 0.0 || Da.extendSeg[sel].u.c.a1 >180 + || (Da.extendSeg[sel].u.c.a0 >= Da.arcA0[sel] && Da.extendSeg[sel].u.c.a0 < Da.arcA0[sel]+Da.arcA1[sel] + && Da.extendSeg[sel].u.c.a0 + Da.extendSeg[sel].u.c.a1 <= Da.arcA0[sel] + Da.arcA1[sel]) + ) { + Da.extend[sel] = FALSE; + Da.pos[sel] = pos; + } else { + Da.extend[sel] = TRUE; + Da.pos[sel] = pos; + } + + } else { //Bezier and Cornu that we are joining TO can't extend + wBeep(); + InfoMessage(_("Can't extend connected Bezier or Cornu")); + pos = GetTrkEndPos(Da.trk[sel],Da.ep[sel]); + return C_CONTINUE; } - if (Da.extendSeg[sel].u.c.a1 == 0.0 || Da.extendSeg[sel].u.c.a1 >180 - || (Da.extendSeg[sel].u.c.a0 >= Da.arcA0[sel] && Da.extendSeg[sel].u.c.a0 < Da.arcA0[sel]+Da.arcA1[sel] - && Da.extendSeg[sel].u.c.a0 + Da.extendSeg[sel].u.c.a1 <= Da.arcA0[sel] + Da.arcA1[sel]) - ) { - Da.extend[sel] = FALSE; - Da.pos[sel] = pos; - } else { - Da.extend[sel] = TRUE; - Da.pos[sel] = pos; + } else Da.pos[sel] = pos; + } + } else if (Da.selectMidPoint >=0){ + DYNARR_N(coOrd,Da.mid_points,Da.selectMidPoint) = pos; + } else if (Da.selectEndHandle >=0) { //Cornu has no end, so has handles + int end = Da.selectEndHandle/2; + if (Da.selectEndHandle%2 == 0) { //Radius + coOrd p0 = Da.pos[end]; //Start + coOrd p1 = Da.endHandle[end].end_curve; //End + ANGLE_T a0 = FindAngle( p1, p0 ); + DIST_T d0 = FindDistance( p0, p1 )/2.0; //Distance to Middle of Chord + coOrd pos2 = pos; //New pos + Rotate( &pos2, p1, -a0 ); + pos2.x -= p1.x; //Deflection at right angles to Chord + DIST_T r = 1000.0; + if ( fabs(pos2.x) >= 0.01 ) { //Not zero + double d2 = sqrt( d0*d0 + pos2.x*pos2.x )/2.0; + r = d2*d2*2.0/pos2.x; + if ( fabs(r) > 1000.0 ) { //Limit Radius + r = 0.0; + //r = ((r > 0) ? 1 : -1 ) *1000.0; } - - } else { //Bezier and Cornu that we are joining TO can't extend - DrawTempCornu(); //put back - wBeep(); - InfoMessage(_("Can't extend connected Bezier or Cornu")); - pos = GetTrkEndPos(Da.trk[sel],Da.ep[sel]); - return C_CONTINUE; + } else { + r = 0.0; + //r = ((r > 0) ? 1 : -1 ) *1000.0; } + coOrd posx; //Middle of chord + posx.x = (p1.x-p0.x)/2.0 + p0.x; + posx.y = (p1.y-p0.y)/2.0 + p0.y; + a0 -= 90.0; + if (r<0) { //Negative radius means other side + coOrd pt = p0; + p0 = p1; + p1 = pt; + a0 += 180.0; + } + coOrd pc; + if (r == 0.0) { + Da.center[end] = zero; + Da.radius[end] = 0.0; + Da.angle[end] = FindAngle(Da.pos[end],Da.endHandle[end].end_curve); + } else { + Translate( &pc, posx, a0, fabs(r) - fabs(pos2.x) ); //Move Radius less Deflection to get to center + Da.center[end] = pc; + if (DifferenceBetweenAngles(FindAngle(Da.center[end],Da.pos[end]),FindAngle(Da.center[end],Da.endHandle[end].end_curve))>0.0) + Da.angle[end] = NormalizeAngle(FindAngle(Da.center[end],Da.pos[end])+90.0); + else + Da.angle[end] = NormalizeAngle(FindAngle(Da.center[end],Da.pos[end])-90.0); + Da.radius[end] = fabs(r); + } + } else { + Da.angle[end] = FindAngle(Da.pos[end],pos); + Da.radius[end] = 0.0; + Translate(&Da.center[end],Da.pos[end],NormalizeAngle(Da.angle[end]+90.0),Da.radius[end]); } } - - CreateBothEnds(Da.selectPoint); + CreateBothEnds(Da.selectEndPoint,Da.selectMidPoint,Da.selectEndHandle,Da.prevSelected); SetUpCornuParms(&cp); //In case we want to use these because the ends are not on the track - - if (CallCornu(Da.pos, Da.trk, Da.ep, &Da.crvSegs_da,&cp)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; else Da.crvSegs_da_cnt = 0; + for (int i=0;i<2;i++) { + if (Da.trk[i] || Da.ends[i]) continue; + coOrd p = Da.pos[i]; + Da.angle[i] = NormalizeAngle((i?0:180)+GetAngleSegs( Da.crvSegs_da_cnt, Da.crvSegs_da.ptr, &p, NULL, NULL, NULL, NULL, NULL)); + Da.radius[i] = 0.0; + } Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da); DIST_T rin = Da.radius[0]; InfoMessage( _("Cornu : Min Radius=%s MaxRateofCurveChange/Scale=%s Length=%s Winding Arc=%s"), @@ -731,25 +1580,121 @@ EXPORT STATUS_T AdjustCornuCurve( FormatFloat(CornuMaxRateofChangeofCurvature(Da.pos,Da.crvSegs_da,&rin)*GetScaleRatio(GetLayoutCurScale())), FormatDistance(CornuLength(Da.pos,Da.crvSegs_da)), FormatDistance(CornuTotalWindingArc(Da.pos,Da.crvSegs_da))); - DrawTempCornu(); return C_CONTINUE; case C_UP: - if (Da.state != POINT_PICKED) return C_CONTINUE; + DYNARR_RESET(trkSeg_t,anchors_da); + if (Da.state != POINT_PICKED) { + Da.state = PICK_POINT; + return C_CONTINUE; + } ep = 0; - DrawTempCornu(); //wipe out - Da.selectPoint = -1; - CreateBothEnds(Da.selectPoint); + if (Da.selectMidPoint!=-1) Da.prevSelected = Da.selectMidPoint; + else if (Da.selectEndPoint!=-1) { + if (!Da.trk[Da.selectEndPoint] && + (t=OnTrack(&pos,FALSE,TRUE)) != NULL && t != Da.selectTrack ) { + EPINX_T ep = PickUnconnectedEndPoint(pos,t); + if (ep>=0) { + if (QueryTrack(t,Q_HAS_VARIABLE_ENDPOINTS)) { //Circle/Helix find if there is an open slot and where + if ((GetTrkEndTrk(t,0) != NULL) && (GetTrkEndTrk(t,1) != NULL)) { + InfoMessage(_("Helix Already Connected")); + return C_CONTINUE; + } + ep = -1; //Not a real ep yet + } else ep = PickUnconnectedEndPointSilent(pos, t); //EP + if (ep>=0 && QueryTrack(t,Q_CAN_ADD_ENDPOINTS)) { + ep=-1; //Don't attach to Turntable + trackParams_t tp; + if (!GetTrackParams(PARAMS_CORNU, t, pos, &tp)) return C_CONTINUE; + ANGLE_T a = tp.angle; + Translate(&pos,tp.ttcenter,a,tp.ttradius); + } + if ( ep==-1 && (!QueryTrack(t,Q_CAN_ADD_ENDPOINTS) && !QueryTrack(t,Q_HAS_VARIABLE_ENDPOINTS))) { //No endpoints and not Turntable or Helix/Circle + wBeep(); + InfoMessage(_("No Valid end point on that track")); + return C_CONTINUE; + } + if (GetTrkScale(t) != (char)GetLayoutCurScale()) { + wBeep(); + InfoMessage(_("Track is different scale")); + return C_CONTINUE; + } + } + if (ep>=0 && t) { //Real end point, real track + Da.trk[Da.selectEndPoint] = t; + Da.ep[Da.selectEndPoint] = ep; // Note: -1 for Turntable or Circle + pos = GetTrkEndPos(t,ep); + Da.pos[Da.selectEndPoint] = pos; + if (!GetConnectedTrackParms(t,pos,Da.selectEndPoint,ep,FALSE)) return C_CONTINUE; + } + } else { + cornuModCmdContext.angle = NormalizeAngle(Da.angle[Da.selectEndPoint]); + cornuModCmdContext.radius = Da.radius[Da.selectEndPoint]; + if (DifferenceBetweenAngles(FindAngle(Da.center[Da.selectEndPoint],Da.pos[Da.selectEndPoint]),Da.angle[Da.selectEndPoint])<0.0) + cornuModCmdContext.radius = -cornuModCmdContext.radius; + controls[0] = cornuModEndRadiusPD.control; + controls[1] = cornuModEndAnglePD.control; + controls[2] = NULL; + labels[0] = N_("End Radius"); + labels[1] = N_("End Angle"); + ParamLoadControls( &cornuModPG ); + InfoSubstituteControls( controls, labels ); + cornuModEndRadiusPD.option &= ~PDO_NORECORD; + cornuModEndAnglePD.option &= ~PDO_NORECORD; + infoSubst = TRUE; + Da.prevSelected = Da.selectEndPoint; + Da.selectEndHandle = -1; + } + } else if (Da.selectEndHandle!=-1) { + Da.prevSelected = Da.selectEndHandle>2?1:0; + cornuModCmdContext.angle = NormalizeAngle(Da.angle[Da.prevSelected]); + cornuModCmdContext.radius = Da.radius[Da.prevSelected]; + if (DifferenceBetweenAngles(FindAngle(Da.center[Da.prevSelected],Da.pos[Da.prevSelected]),Da.angle[Da.prevSelected])<0.0) + cornuModCmdContext.radius = -cornuModCmdContext.radius; + controls[0] = cornuModEndRadiusPD.control; + controls[1] = cornuModEndAnglePD.control; + controls[2] = NULL; + labels[0] = N_("End Radius"); + labels[1] = N_("End Angle"); + ParamLoadControls( &cornuModPG ); + InfoSubstituteControls( controls, labels ); + cornuModEndRadiusPD.option &= ~PDO_NORECORD; + cornuModEndAnglePD.option &= ~PDO_NORECORD; + infoSubst = TRUE; + Da.selectEndHandle = -1; + } + Da.selectEndPoint = -1; Da.selectMidPoint = -1; + CreateBothEnds(Da.selectEndPoint,Da.selectMidPoint,Da.selectEndHandle,Da.prevSelected); SetUpCornuParms(&cp); - if (CallCornu(Da.pos,Da.trk,Da.ep,&Da.crvSegs_da,&cp)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; else Da.crvSegs_da_cnt = 0; Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da); - InfoMessage(_("Pick on point to adjust it along track - Enter to confirm, ESC to abort")); - DrawTempCornu(); + InfoMessage(_("Pick on point to adjust it along track - Delete to remove, Enter to confirm, ESC to abort")); Da.state = PICK_POINT; return C_CONTINUE; + case C_TEXT: + DYNARR_RESET(trkSeg_t,anchors_da); + //Delete or backspace deletes last selected index + if (action>>8 == 127 || action>>8 == 8) { + if ((Da.state == PICK_POINT) && Da.prevSelected !=-1) { + for (int i=Da.prevSelected;i<Da.mid_points.cnt;i++) { + DYNARR_N(coOrd,Da.mid_points,i) = DYNARR_N(coOrd,Da.mid_points,i+1); + } + Da.mid_points.cnt--; + } + Da.prevSelected = -1; + CreateBothEnds(Da.selectEndPoint,Da.selectMidPoint,Da.selectEndHandle,Da.prevSelected); + cornuParm_t cp; + SetUpCornuParms(&cp); + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + else Da.crvSegs_da_cnt = 0; + return C_CONTINUE; + } + return C_CONTINUE; + case C_OK: //C_OK is not called by Modify. + DYNARR_RESET(trkSeg_t,anchors_da); if ( Da.state == PICK_POINT ) { Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da); if (CornuTotalWindingArc(Da.pos,Da.crvSegs_da)>4*360) { @@ -762,63 +1707,109 @@ EXPORT STATUS_T AdjustCornuCurve( return C_CONTINUE; } for (int i=0;i<2;i++) { - if (!(QueryTrack(Da.trk[i],Q_CAN_ADD_ENDPOINTS))) { // Not Turntable + if (Da.trk[i] && !(QueryTrack(Da.trk[i],Q_CAN_ADD_ENDPOINTS))) { // Not Turntable if (FindDistance(Da.pos[i],GetTrkEndPos(Da.trk[i],1-Da.ep[i])) < minLength) { wBeep(); - InfoMessage(_("Cornu end %d too close to other end of connect track - reposition it"),i+1); + InfoMessage(_("Cornu point %d too close to other end of connect track - reposition it"),i+1); return C_CONTINUE; } } } - - DrawTempCornu(); - UndoStart( _("Create Cornu"),"newCornu curve"); - t = NewCornuTrack( Da.pos, Da.center, Da.angle, Da.radius,(trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt); - if (t==NULL) { - wBeep(); - InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"), - Da.pos[0].x,Da.pos[0].y, - Da.pos[1].x,Da.pos[1].y, - Da.center[0].x,Da.center[0].y, - Da.center[1].x,Da.center[1].y, - Da.angle[0],Da.angle[1], - FormatDistance(Da.radius[0]),FormatDistance(Da.radius[1])); - return C_TERMINATE; + UndoStart( _("Create Cornu"),"newCornu curves"); + BOOL_T end_point[2]; + end_point[0] = TRUE; + end_point[1] = FALSE; + coOrd sub_pos[2]; + sub_pos[0] = Da.pos[0]; + if (Da.radius[0] == -1) end_point[0] = FALSE; + track_p first_trk= NULL,trk1=NULL,trk2 = NULL; + + for (int i=0;i<Da.mid_points.cnt;i++) { + sub_pos[1] = DYNARR_N(coOrd,Da.mid_points,i); + if ((trk1 = CreateCornuFromPoints(sub_pos,end_point))== NULL) return C_TERMINATE; + if (Da.trk[0]) { + CopyAttributes( Da.trk[0], trk1 ); + } else if (Da.trk[1]) { + CopyAttributes( Da.trk[1], trk1 ); + } else { + SetTrkScale( trk1, GetLayoutCurScale() ); + SetTrkBits( trk1, TB_HIDEDESC ); + } + DrawNewTrack(trk1); + if (first_trk == NULL) first_trk = trk1; + if (trk2) ConnectTracks(trk1,0,trk2,1); + trk2 = trk1; + end_point[0] = FALSE; + sub_pos[0] = DYNARR_N(coOrd,Da.mid_points,i); } - - CopyAttributes( Da.trk[0], t ); + sub_pos[1] = Da.pos[1]; + end_point[1] = TRUE; + if (Da.radius[1] == -1) end_point[1] = FALSE; + if ((trk1 = CreateCornuFromPoints(sub_pos,end_point)) == NULL) return C_TERMINATE; + if (Da.trk[0]) { + CopyAttributes( Da.trk[0], trk1 ); + } else if (Da.trk[1]){ + CopyAttributes( Da.trk[1], trk1 ); + } else { + SetTrkScale( trk1, GetLayoutCurScale() ); + SetTrkBits( trk1, TB_HIDEDESC ); + } + if (trk2) ConnectTracks(trk1,0,trk2,1); + if (first_trk == NULL) first_trk = trk1; + //t = NewCornuTrack( Da.pos, Da.center, Da.angle, Da.radius,(trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt); for (int i=0;i<2;i++) { - UndoModify(Da.trk[i]); - MoveEndPt(&Da.trk[i],&Da.ep[i],Da.pos[i],0); - if ((GetTrkType(Da.trk[i])==T_BEZIER) || (GetTrkType(Da.trk[i])==T_CORNU)) { //Bezier split position not precise, so readjust Cornu - GetConnectedTrackParms(Da.trk[i],GetTrkEndPos(Da.trk[i],Da.ep[i]),i,Da.ep[i]); + if (Da.trk[i]) { + UndoModify(Da.trk[i]); + MoveEndPt(&Da.trk[i],&Da.ep[i],Da.pos[i],0); + //End position not precise, so readjust Cornu + GetConnectedTrackParms(Da.trk[i],GetTrkEndPos(Da.trk[i],Da.ep[i]),i,Da.ep[i],FALSE); ANGLE_T endAngle = NormalizeAngle(GetTrkEndAngle(Da.trk[i],Da.ep[i])+180); - SetCornuEndPt(t,i,GetTrkEndPos(Da.trk[i],Da.ep[i]),Da.center[i],endAngle,Da.radius[i]); + SetCornuEndPt(i==0?first_trk:trk1,i,GetTrkEndPos(Da.trk[i],Da.ep[i]),Da.center[i],endAngle,Da.radius[i]); + if (Da.ep[i]>=0) + ConnectTracks(Da.trk[i],Da.ep[i],i==0?first_trk:trk1,i); } - if (Da.ep[i]>=0) - ConnectTracks(Da.trk[i],Da.ep[i],t,i); } UndoEnd(); - DrawNewTrack(t); + if (infoSubst) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } Da.state = NONE; - MainRedraw(); - MapRedraw(); return C_TERMINATE; } return C_CONTINUE; case C_REDRAW: + if (Da.state == NONE) return C_CONTINUE; DrawTempCornu(); + if (anchors_da.cnt) { + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + } return C_CONTINUE; + case C_CANCEL: + case C_FINISH: + break; default: return C_CONTINUE; } + return C_CONTINUE; } +static void cornuModDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + AdjustCornuCurve(C_UPDATE, zero, InfoMessage); + ParamLoadControl(&cornuModPG,cornuModEndRadius); // Make sure Radius updated + ParamLoadControl(&cornuModPG,cornuModEndAngle); //Relative Angle as well + TempRedraw(); + +} /** * CmdCornuModify @@ -835,11 +1826,12 @@ EXPORT STATUS_T AdjustCornuCurve( * */ STATUS_T CmdCornuModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG ) { - track_p t; struct extraData *xx = GetTrkExtraData(trk); Da.trackGauge = trackG; + Da.commandType = CORNU_MODIFY; + switch (action&0xFF) { case C_START: Da.state = NONE; @@ -847,42 +1839,90 @@ STATUS_T CmdCornuModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG Da.ep1Segs_da_cnt = 0; Da.ep2Segs_da_cnt = 0; Da.crvSegs_da_cnt = 0; + Da.midSegs.cnt = 0; Da.extend[0] = FALSE; Da.extend[1] = FALSE; - Da.selectPoint = -1; + Da.selectEndPoint = -1; Da.selectTrack = NULL; + DYNARR_RESET(coOrd,Da.mid_points); + DYNARR_RESET(track_p,Da.tracks); Da.selectTrack = trk; + DYNARR_APPEND(track_p,Da.tracks,1); + DYNARR_LAST(track_p,Da.tracks) = trk; Da.trk[0] = GetTrkEndTrk( trk, 0 ); + track_p prior = trk; if (Da.trk[0]) Da.ep[0] = GetEndPtConnectedToMe(Da.trk[0],trk); - Da.trk[1] = GetTrkEndTrk( trk, 1 ); - if (Da.trk[1]) Da.ep[1] = GetEndPtConnectedToMe(Da.trk[1],trk); + EPINX_T ep0 = 0; + //Move down the LHS adding tracks until no more Cornu + while (Da.trk[0] && QueryTrack(Da.trk[0],Q_IS_CORNU)) { + prior = Da.trk[0]; + ep0 = 1-Da.ep[0]; + DYNARR_APPEND(track_p,Da.tracks,1); + DYNARR_LAST(track_p,Da.tracks) = prior; + DYNARR_APPEND(coOrd,Da.mid_points,1); + for (int i=Da.mid_points.cnt-1;i>0;i--) { + DYNARR_N(coOrd,Da.mid_points,i) = DYNARR_N(coOrd,Da.mid_points,i-1); + } + DYNARR_N(coOrd,Da.mid_points,0) = GetTrkEndPos(prior,1-ep0); + Da.trk[0] = GetTrkEndTrk( prior, ep0 ); + if (Da.trk[0]) Da.ep[0] = GetEndPtConnectedToMe(Da.trk[0],prior); + else Da.ep[0] = -1; + } + if (prior) { + struct extraData *xx0 = GetTrkExtraData(prior); + Da.pos[0] = xx0->cornuData.pos[ep0]; //Copy parms from FIRST CORNU trk + Da.radius[0] = xx0->cornuData.r[ep0]; + Da.angle[0] = xx0->cornuData.a[ep0]; + Da.center[0] = xx0->cornuData.c[ep0]; + } - for (int i=0;i<2;i++) { - Da.pos[i] = xx->cornuData.pos[i]; //Copy parms from old trk - Da.radius[i] = xx->cornuData.r[i]; - Da.angle[i] = xx->cornuData.a[i]; - Da.center[i] = xx->cornuData.c[i]; - } + //Move to RHS + Da.trk[1] = GetTrkEndTrk( trk, 1 ); + track_p next = trk; + EPINX_T ep1 = 1; + if (Da.trk[1]) Da.ep[1] = GetEndPtConnectedToMe(Da.trk[1],trk); + //Move down RHS adding tracks until no more Cornu + while (Da.trk[1] && QueryTrack(Da.trk[1],Q_IS_CORNU)) { + next = Da.trk[1]; + ep1 = 1-Da.ep[1]; + DYNARR_APPEND(track_p,Da.tracks,1); + DYNARR_LAST(track_p,Da.tracks) = next; + DYNARR_APPEND(coOrd,Da.mid_points,1); + DYNARR_LAST(coOrd,Da.mid_points) = GetTrkEndPos(next,1-ep1); + Da.trk[1] = GetTrkEndTrk( next, ep1 ); + if (Da.trk[1]) Da.ep[1] = GetEndPtConnectedToMe(Da.trk[1],next); + } - if ((Da.trk[0] && (!QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY) && !QueryTrack(Da.trk[0],Q_CAN_EXTEND))) && - (Da.trk[1] && (!QueryTrack(Da.trk[1],Q_CORNU_CAN_MODIFY) && !QueryTrack(Da.trk[1],Q_CAN_EXTEND)))) { - wBeep(); - ErrorMessage("Both Ends of this Cornu are UnAdjustable"); - return C_TERMINATE; - } + if (next) { + struct extraData *xx1 = GetTrkExtraData(next); + Da.pos[1] = xx1->cornuData.pos[ep1]; //Copy parms from LAST CORNU trk + Da.radius[1] = xx1->cornuData.r[ep1]; + Da.angle[1] = xx1->cornuData.a[ep1]; + Da.center[1] = xx1->cornuData.c[ep1]; + } - InfoMessage(_("Track picked - now select a Point")); + InfoMessage(_("Now Select or Add (+Shift) a Point")); Da.state = TRACK_SELECTED; - DrawTrack(Da.selectTrack,&mainD,wDrawColorWhite); //Wipe out real track, draw replacement + for (int i=0;i<Da.tracks.cnt;i++) { + DrawTrack(DYNARR_N(track_p,Da.tracks,i),&mainD,wDrawColorWhite); //Wipe out real tracks, draw replacement + } return AdjustCornuCurve(C_START, pos, InfoMessage); + case wActionMove: + return AdjustCornuCurve(wActionMove, pos, InfoMessage); + break; + case C_DOWN: if (Da.state == TRACK_SELECTED) return C_CONTINUE; //Ignore until first up return AdjustCornuCurve(C_DOWN, pos, InfoMessage); + case C_LCLICK: + if (Da.state == TRACK_SELECTED) return C_CONTINUE; //Ignore until first up + AdjustCornuCurve(C_DOWN, pos, InfoMessage); + return AdjustCornuCurve(C_UP, pos, InfoMessage); case C_MOVE: if (Da.state == TRACK_SELECTED) return C_CONTINUE; //Ignore until first up and down @@ -895,10 +1935,19 @@ STATUS_T CmdCornuModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG return AdjustCornuCurve(C_UP, pos, InfoMessage); //Run Adjust case C_TEXT: - if ((action>>8) != 32) + //Delete or backspace deletes last selected index + if (action>>8 == 127 || action>>8 == 8) { + return AdjustCornuCurve(action, pos, InfoMessage); + } + //Space bar or enter means done + if ( (action>>8 != ' ') && (action>>8 != 13) ) return C_CONTINUE; /* no break */ case C_OK: + if (infoSubst) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } if (Da.state != PICK_POINT) { //Too early - abandon InfoMessage(_("No changes made")); Da.state = NONE; @@ -928,23 +1977,59 @@ STATUS_T CmdCornuModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG } else { Da.trk[i] = NewCurvedTrack(Da.extendSeg[i].u.c.center,fabs(Da.extendSeg[i].u.c.radius), Da.extendSeg[i].u.c.a0,Da.extendSeg[i].u.c.a1,FALSE); - - if (Da.angle[i]>180) - Da.ep[i] = (Da.extendSeg[i].u.c.a0>90 && Da.extendSeg[i].u.c.a0<270)?0:1; - else - Da.ep[i] = (Da.extendSeg[i].u.c.a0>90 && Da.extendSeg[i].u.c.a0<270)?1:0; + if (FindDistance(GetTrkEndPos(Da.trk[i],0),Da.pos[i])<=connectDistance) { + Da.ep[i] = 0; + } else Da.ep[i] = 1; } if (!Da.trk[i]) { wBeep(); InfoMessage(_("Cornu Extension Create Failed for end %d"),i); + Da.state = NONE; return C_TERMINATE; } } } - - t = NewCornuTrack( Da.pos, Da.center, Da.angle, Da.radius, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt); - if (t==NULL) { + BOOL_T end_point[2]; + end_point[0] = TRUE; + end_point[1] = FALSE; + coOrd sub_pos[2]; + sub_pos[0] = Da.pos[0]; + + track_p first_trk= NULL,trk1=NULL,trk2 = NULL; + for (int i=0;i<Da.mid_points.cnt;i++) { + sub_pos[1] = DYNARR_N(coOrd,Da.mid_points,i); + if ((trk1 = CreateCornuFromPoints(sub_pos, end_point))== NULL) { + wBeep(); + InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"), + Da.pos[0].x,Da.pos[0].y, + Da.pos[1].x,Da.pos[1].y, + Da.center[0].x,Da.center[0].y, + Da.center[1].x,Da.center[1].y, + Da.angle[0],Da.angle[1], + FormatDistance(Da.radius[0]),FormatDistance(Da.radius[1])); + UndoUndo(); + Da.state = NONE; + return C_TERMINATE; + } + if (Da.trk[0]) { + CopyAttributes( Da.trk[0], trk1 ); + } else if (Da.trk[1]) { + CopyAttributes( Da.trk[1], trk1 ); + } else { + SetTrkScale( trk1, GetLayoutCurScale() ); + SetTrkBits( trk1, TB_HIDEDESC ); + } + DrawNewTrack(trk1); + if (first_trk == NULL) first_trk = trk1; + if (trk2) ConnectTracks(trk1,0,trk2,1); + trk2 = trk1; + end_point[0] = FALSE; + sub_pos[0] = DYNARR_N(coOrd,Da.mid_points,i); + } + sub_pos[1] = Da.pos[1]; + end_point[1] = TRUE; + if ((trk1 = CreateCornuFromPoints(sub_pos,end_point)) == NULL) { wBeep(); InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"), Da.pos[0].x,Da.pos[0].y, @@ -954,15 +2039,26 @@ STATUS_T CmdCornuModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG Da.angle[0],Da.angle[1], FormatDistance(Da.radius[0]),FormatDistance(Da.radius[1])); UndoUndo(); - MainRedraw(); - MapRedraw(); - //DYNARR_FREE(trkSeg_t,Da.crvSegs_da); + Da.state = NONE; return C_TERMINATE; } + DrawNewTrack(trk1); + if (Da.trk[0]) { + CopyAttributes( Da.trk[0], trk1 ); + } else if (Da.trk[1]) { + CopyAttributes( Da.trk[1], trk1 ); + } else { + SetTrkScale( trk1, GetLayoutCurScale() ); + SetTrkBits( trk1, TB_HIDEDESC ); + } + if (trk2) ConnectTracks(trk1,0,trk2,1); + if (first_trk == NULL) first_trk = trk1; - CopyAttributes( trk, t ); + Da.state = NONE; //Must do before Delete - DeleteTrack(trk, TRUE); + for (int i=0;i<Da.tracks.cnt;i++) { + DeleteTrack(DYNARR_N(track_p,Da.tracks,i), TRUE); + } if (Da.trk[0]) UndoModify(Da.trk[0]); if (Da.trk[1]) UndoModify(Da.trk[1]); @@ -970,13 +2066,12 @@ STATUS_T CmdCornuModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG for (int i=0;i<2;i++) { //Attach new track if (Da.trk[i] && Da.ep[i] != -1) { //Like the old track if (MoveEndPt(&Da.trk[i],&Da.ep[i],Da.pos[i],0)) { - if (GetTrkType(Da.trk[i])==T_BEZIER) { //Bezier split position not precise, so readjust Cornu - GetConnectedTrackParms(Da.trk[i],GetTrkEndPos(Da.trk[i],Da.ep[i]),i,Da.ep[i]); - ANGLE_T endAngle = NormalizeAngle(GetTrkEndAngle(Da.trk[i],Da.ep[i])+180); - SetCornuEndPt(t,i,GetTrkEndPos(Da.trk[i],Da.ep[i]),Da.center[i],endAngle,Da.radius[i]); - } + //Bezier split position not precise, so readjust Cornu + GetConnectedTrackParms(Da.trk[i],GetTrkEndPos(Da.trk[i],Da.ep[i]),i,Da.ep[i],FALSE); + ANGLE_T endAngle = NormalizeAngle(GetTrkEndAngle(Da.trk[i],Da.ep[i])+180); + SetCornuEndPt(i==0?first_trk:trk1,i,GetTrkEndPos(Da.trk[i],Da.ep[i]),Da.center[i],endAngle,Da.radius[i]); if (Da.ep[i]>= 0) - ConnectTracks(t,i,Da.trk[i],Da.ep[i]); + ConnectTracks(i==0?first_trk:trk1,i,Da.trk[i],Da.ep[i]); } else { UndoUndo(); wBeep(); @@ -986,18 +2081,18 @@ STATUS_T CmdCornuModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG } } UndoEnd(); - MainRedraw(); - MapRedraw(); Da.state = NONE; - //DYNARR_FREE(trkSeg_t,Da.crvSegs_da); + //DYNARR_FREE(trkSeg_t,Da.crvSegs_da) return C_TERMINATE; case C_CANCEL: InfoMessage(_("Modify Cornu Cancelled")); Da.state = NONE; + if (infoSubst) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } //DYNARR_FREE(trkSeg_t,Da.crvSegs_da); - MainRedraw(); - MapRedraw(); return C_TERMINATE; case C_REDRAW: @@ -1028,6 +2123,22 @@ DIST_T CornuLength(coOrd pos[4],dynArr_t segs) { return dd; } +DIST_T CornuOffsetLength(dynArr_t segs, double offset) { + DIST_T dd = 0.0; + if (segs.cnt == 0 ) return dd; + for (int i = 0;i<segs.cnt;i++) { + trkSeg_t t = DYNARR_N(trkSeg_t, segs, i); + if (t.type == SEG_CRVTRK || t.type == SEG_CRVLIN) { + dd += fabs((t.u.c.radius+(t.u.c.radius>0?offset:-offset))*D2R(t.u.c.a1)); + } else if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) { + dd +=CornuOffsetLength(t.bezSegs,offset); + } else if (t.type == SEG_STRLIN || t.type == SEG_STRTRK ) { + dd += FindDistance(t.u.l.pos[0],t.u.l.pos[1]); + } + } + return dd; +} + DIST_T CornuMinRadius(coOrd pos[4],dynArr_t segs) { DIST_T r = 100000.0, rr; if (segs.cnt == 0 ) return r; @@ -1081,6 +2192,9 @@ DIST_T CornuMaxRateofChangeofCurvature(coOrd pos[4], dynArr_t segs, DIST_T * las return r_max; } + + + /* * Create a Cornu Curve Track * Sequence is @@ -1091,167 +2205,359 @@ DIST_T CornuMaxRateofChangeofCurvature(coOrd pos[4], dynArr_t segs, DIST_T * las */ STATUS_T CmdCornu( wAction_t action, coOrd pos ) { - track_p t; + track_p t = NULL; cornuParm_t cp; - Da.color = lineColor; + Da.commandType = CORNU_CREATE; + Da.width = (double)lineWidth/mainD.dpi; Da.trackGauge = trackGauge; + Da.selectTrack = NULL; switch (action&0xFF) { case C_START: + Da.cmdType = (long)commandContext; Da.state = NONE; - Da. selectPoint = -1; + Da.selectEndPoint = -1; + Da.selectMidPoint = -1; + Da.endHandle[0].end_valid = FALSE; + Da.endHandle[1].end_valid = FALSE; for (int i=0;i<2;i++) { + Da.ends[i] = FALSE; Da.pos[i] = zero; + Da.angle[i] = 0.0; + Da.radius[i] = -1.0; } Da.trk[0] = Da.trk[1] = NULL; //tempD.orig = mainD.orig; - DYNARR_RESET(trkSeg_t,Da.crvSegs_da); Da.ep1Segs_da_cnt = 0; Da.ep2Segs_da_cnt = 0; + Da.crvSegs_da_cnt = 0; + Da.midSegs.cnt = 0; + DYNARR_RESET(coOrd,Da.mid_points); + DYNARR_RESET(track_p,Da.tracks); + DYNARR_RESET(trkSeg_t,anchors_da); Da.extend[0] = FALSE; Da.extend[1] = FALSE; - if (selectedTrackCount==0) - InfoMessage( _("Left click - join with Cornu track") ); - else - InfoMessage( _("Left click - join with Cornu track, Shift Left click - move to join") ); + if (Da.cmdType == cornuCmdCreateTrack) + InfoMessage( _("Left click - Start Cornu track") ); + else if (Da.cmdType == cornuCmdHotBar) { + InfoMessage( _("Left click - Place Flextrack") ); + } else { + if (selectedTrackCount==0) + InfoMessage( _("Left click - join with Cornu track") ); + else + InfoMessage( _("Left click - join with Cornu track, Shift Left click - move to join") ); + } return C_CONTINUE; case C_DOWN: + DYNARR_RESET(trkSeg_t,anchors_da); if ( Da.state == NONE || Da.state == LOC_2) { //Set the first or second point coOrd p = pos; - int end = Da.state==NONE?0:1; - EPINX_T ep; - if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) { - if (QueryTrack(t,Q_HAS_VARIABLE_ENDPOINTS)) { //Circle/Helix find if there is an open slot and where - if ((GetTrkEndTrk(t,0) != NULL) && (GetTrkEndTrk(t,1) != NULL)) { + t = NULL; + int end = 0; + if (Da.state != NONE) end=1; + EPINX_T ep = -1; + //Lock to endpoint if one is available and under pointer + if ((t = OnTrack(&p, FALSE, TRUE)) != NULL && t != Da.selectTrack) { + if (QueryTrack(t,Q_HAS_VARIABLE_ENDPOINTS)) { //Circle/Helix find if there is an open slot and where + if ((GetTrkEndTrk(t,0) != NULL) && (GetTrkEndTrk(t,1) != NULL)) { + wBeep(); InfoMessage(_("Helix Already Connected")); - return C_CONTINUE; + t= NULL; } ep = -1; //Not a real ep yet - } else ep = PickUnconnectedEndPointSilent(p, t); - if (ep>=0 && QueryTrack(t,Q_CAN_ADD_ENDPOINTS)) ep=-1; //Ignore Turntable Unconnected - else if (ep==-1 && (!QueryTrack(t,Q_CAN_ADD_ENDPOINTS) && !QueryTrack(t,Q_HAS_VARIABLE_ENDPOINTS))) { //No endpoints and not Turntable or Helix/Circle - wBeep(); - InfoMessage(_("No Unconnected end point on that track")); - return C_CONTINUE; + } else if (QueryTrack(t,Q_CAN_ADD_ENDPOINTS)) { + ep=-1; //Don't attach to existing Turntable ep + trackParams_t tp; + if (!GetTrackParams(PARAMS_CORNU, t, pos, &tp)) return C_CONTINUE; + ANGLE_T a = tp.angle; + Translate(&pos,tp.ttcenter,a,tp.ttradius); + p = pos; //Fix to wall of turntable initially + } else ep = PickUnconnectedEndPointSilent(p, t); //EP + if ( t && ep==-1 && (!QueryTrack(t,Q_CAN_ADD_ENDPOINTS) && !QueryTrack(t,Q_HAS_VARIABLE_ENDPOINTS))) { //No endpoints and not Turntable or Helix/Circle + wBeep(); + InfoMessage(_("No valid open endpoint on that track")); + t = NULL; + } + if (t && GetTrkGauge(t) != GetScaleTrackGauge(GetLayoutCurScale())) { + wBeep(); + InfoMessage(_("Track is different gauge")); + t = NULL; } + } + if (ep>=0 && t) { //Real end point, real track Da.trk[end] = t; Da.ep[end] = ep; // Note: -1 for Turntable or Circle - if (ep ==-1) pos = p; - else pos = GetTrkEndPos(t,ep); + pos = GetTrkEndPos(t,ep); Da.pos[end] = pos; - InfoMessage( _("Place 2nd end point of Cornu track on track with an unconnected end-point") ); - } else { + Da.angle[end] = GetTrkEndAngle(t,ep); + } else if (t == NULL) { //end not on Track, OK for CreateCornu -> empty end point + pos = p; //Reset to initial + SnapPos( &pos ); + if (Da.cmdType == cornuCmdCreateTrack || Da.cmdType == cornuCmdHotBar) { + Da.trk[end] = NULL; + Da.pos[end] = pos; + Da.radius[end] = -1.0; //No End Rad for open + if (Da.state == NONE ) { + Da.state = POS_1; + Da.angle[0] = 270.0; + Da.radius[0] = 0.0; + Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0], FALSE,TRUE,TRUE,FALSE,Da.angle[0],Da.radius[0],zero,&Da.endHandle[0]); + Da.ep2Segs_da_cnt = 0; + InfoMessage( _("Drag arm in the direction of track") ); + return C_CONTINUE; + } + Da.state = POS_2; //Now this is second end and it is open + Da.selectEndPoint = 1; + Da.mid_points.cnt=0; + Da.angle[1] = GetOpenAngle(Da.pos,Da.angle,1); + Da.radius[1] = 0.0; + CreateBothEnds(1,-1,-1,-1); + SetUpCornuParms(&cp); + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) + Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + InfoMessage( _("Drag arm in the direction of track") ); + return C_CONTINUE; + } wBeep(); - InfoMessage(_("No Unconnected Track End there")); + InfoMessage(_("No Unconnected Track End there")); //Not creating a Cornu - Join can't be open return C_CONTINUE; + } else { + Da.pos[end] = p; + //Either a real end or a track but no end } if (Da.state == NONE) { - if (!GetConnectedTrackParms(t, pos, 0, Da.ep[0])) { + if (!GetConnectedTrackParms(t, pos, 0, Da.ep[0],FALSE)) { //Must get parms Da.trk[0] = NULL; + Da.ep[0] = -1; + wBeep(); + InfoMessage(_("No Valid Track End there")); return C_CONTINUE; } + if (ep<0) { + Da.trk[0] = t; + Da.pos[0] = p; + Da.ep[0] = ep; + } Da.state = POS_1; - Da.selectPoint = 0; - Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0], FALSE, !QueryTrack(Da.trk[0],Q_IS_CORNU),QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY)); - DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack); - InfoMessage( _("Move 1st end point of Cornu track along track 1") ); - } else { - if ( Da.trk[0] == t) { + Da.selectEndPoint = 0; //Select first end point + Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0], FALSE, !QueryTrack(Da.trk[0],Q_IS_CORNU),QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY), + Da.trk[0]!=NULL,Da.angle[0],Da.radius[0],Da.center[0],NULL); + InfoMessage( _("Locked - Move 1st end point of Cornu track along track 1") ); + return C_CONTINUE; + } else { //Second Point + if (Da.trk[0] == t) { ErrorMessage( MSG_JOIN_CORNU_SAME ); Da.trk[1] = NULL; + Da.ep[1] = -1; return C_CONTINUE; } - if (!GetConnectedTrackParms(t, pos, 1, Da.ep[1])) { + if (!GetConnectedTrackParms(t, pos, 1, Da.ep[1],FALSE)) { Da.trk[1] = NULL; //Turntable Fail + wBeep(); + InfoMessage(_("No Valid Track End there")); return C_CONTINUE; } + if (ep<0) { + Da.trk[1] = t; + Da.pos[1] = p; + Da.ep[1] = -1; + Da.radius[1] = 0.0; + } CorrectHelixAngles(); - Da.selectPoint = 1; + Da.selectEndPoint = 1; //Select second end point Da.state = POINT_PICKED; - DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack); //Wipe out initial Arm - CreateBothEnds(1); - if (CallCornu(Da.pos,Da.trk,Da.ep,&Da.crvSegs_da, &cp)) - Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; - DrawTempCornu(); - InfoMessage( _("Move 2nd end point of Cornu track along track 2") ); + InfoMessage( _("Locked - Move 2nd end point of Cornu track along track 2") ); } + CreateBothEnds(1,-1,-1,-1); + SetUpCornuParms(&cp); + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) + Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; return C_CONTINUE; - } else { + } else { //This is after both ends exist return AdjustCornuCurve( action&0xFF, pos, InfoMessage ); } return C_CONTINUE; + + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + if (Da.state != NONE && Da.state != LOC_2) return C_CONTINUE; + if (Da.trk[0] && Da.trk[1]) return C_CONTINUE; + EPINX_T ep = -1; + t = NULL; + if (((MyGetKeyState() & WKEY_ALT) == 0) == magneticSnap) { + //Lock to endpoint if one is available and under pointer + if ((t = OnTrack(&pos, FALSE, TRUE)) != NULL && t != Da.selectTrack) { + if (QueryTrack(t,Q_HAS_VARIABLE_ENDPOINTS)) { //Circle/Helix find if there is an open slot and where + if ((GetTrkEndTrk(t,0) != NULL) && (GetTrkEndTrk(t,1) != NULL)) { + return C_CONTINUE; + } + ep = -1; //Not a real ep yet + } else ep = PickUnconnectedEndPointSilent(pos, t); //EP + if (ep>=0 && QueryTrack(t,Q_CAN_ADD_ENDPOINTS)) ep=-1; //Don't attach to Turntable + if ( ep==-1 && (!QueryTrack(t,Q_CAN_ADD_ENDPOINTS) && !QueryTrack(t,Q_HAS_VARIABLE_ENDPOINTS))) { //No endpoints and not Turntable or Helix/Circle + return C_CONTINUE; + } + if (GetTrkGauge(t) != GetScaleTrackGauge(GetLayoutCurScale())) { + return C_CONTINUE; + } + if (Da.state != NONE && t==Da.trk[0]) return C_CONTINUE; + } + } + if (ep>=0 && t) { + pos = GetTrkEndPos(t,ep); + CreateCornuEndAnchor(pos,TRUE); + } else if (t) { + trackParams_t tp; //Turntable or extendable track + if (!GetTrackParams(PARAMS_CORNU, t, pos, &tp)) return C_CONTINUE; + if (QueryTrack(t,Q_CAN_ADD_ENDPOINTS)) { + if (!GetTrackParams(PARAMS_CORNU, t, pos, &tp)) return C_CONTINUE; + ANGLE_T a = tp.angle; + Translate(&pos,tp.ttcenter,a,tp.ttradius); + CreateCornuEndAnchor(pos,TRUE); + } else CreateCornuEndAnchor(pos,TRUE); + } + + return C_CONTINUE; case C_MOVE: - if (Da.state == NONE) { - InfoMessage("Place 1st end point of Cornu track on unconnected end-point"); + if (Da.state == NONE) { //First point not created + if (Da.cmdType == cornuCmdCreateTrack || Da.cmdType == cornuCmdHotBar) { + InfoMessage("Place 1st end point of Cornu track"); + } else + InfoMessage("Place 1st end point of Cornu track on unconnected end-point"); return C_CONTINUE; } - if (Da.state == POS_1) { + if (Da.state == POS_1) { //First point has been created + if ((Da.cmdType == cornuCmdCreateTrack || Da.cmdType == cornuCmdHotBar) && !Da.trk[0]) { //OK for CreateCornu -> No track selected + if (IsClose(FindDistance(pos,Da.pos[0]))) return C_CONTINUE; + Da.selectEndPoint = 0; + Da.angle[0] = NormalizeAngle(FindAngle(Da.pos[0],pos)+180); + Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],TRUE,TRUE,TRUE,FALSE,Da.angle[0],0.0,zero,&Da.endHandle[0]); + Da.radius[1] = -1.0; /*No end*/ + return C_CONTINUE; + } EPINX_T ep = 0; BOOL_T found = FALSE; int end = Da.state==POS_1?0:1; if(!QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY) && !QueryTrack(Da.trk[0],Q_CAN_ADD_ENDPOINTS)) { - InfoMessage(_("Can't Split - Locked to End Point")); + InfoMessage(_("Track can't be split - so locked to endpoint")); return C_CONTINUE; } if (Da.trk[0] != OnTrack(&pos, FALSE, TRUE)) { wBeep(); InfoMessage(_("Point not on track 1")); Da.state = POS_1; - Da.selectPoint = 1; + Da.selectEndPoint = 0; return C_CONTINUE; } t = Da.trk[0]; - DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack); - if (!GetConnectedTrackParms(t, pos, ep, Da.ep[ep])) { + if (!GetConnectedTrackParms(t, pos, ep, Da.ep[ep],FALSE)) { Da.state = POS_1; - Da.selectPoint = 1; + Da.selectEndPoint = 0; return C_CONTINUE; } Da.pos[ep] = pos; - Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],TRUE,!QueryTrack(Da.trk[0],Q_IS_CORNU),QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY)); - DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack); - } else { + Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs,Da.pos[0],TRUE,!QueryTrack(Da.trk[0],Q_IS_CORNU),QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY), + Da.trk[0]!=NULL,Da.angle[0],Da.radius[0],Da.center[0],NULL); + } else if (Da.state == POS_2 && + (Da.cmdType == cornuCmdCreateTrack || Da.cmdType == cornuCmdHotBar) && !Da.trk[1]) { //OK for CreateCornu -> No track selected + if (IsClose(FindDistance(pos,Da.pos[1]))) return C_CONTINUE; + Da.selectEndPoint = 1; + Da.angle[1] = NormalizeAngle(FindAngle(Da.pos[1],pos)+180); + Da.radius[1] = 0.0; /*No end*/ + Da.ep1Segs_da_cnt = createEndPoint(Da.ep2Segs,Da.pos[1],TRUE,TRUE,TRUE,FALSE,Da.angle[1],0.0,zero,&Da.endHandle[1]); + SetUpCornuParms(&cp); + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + else Da.crvSegs_da_cnt = 0; + CreateBothEnds(-1,-1,-1,-1); + Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da); + return C_CONTINUE; + } else { //Second Point Has Been Created and aligned return AdjustCornuCurve( action&0xFF, pos, InfoMessage ); } return C_CONTINUE; case C_UP: - if (Da.state == POS_1 && Da.trk[0]) { - DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack); + if (Da.state == POS_1 && (Da.cmdType == cornuCmdCreateTrack || Da.cmdType == cornuCmdHotBar || Da.trk[0])) { Da.state = LOC_2; + Da.selectEndPoint = -1; + if (Da.cmdType == cornuCmdCreateTrack || Da.cmdType == cornuCmdHotBar) { + if (Da.cmdType == cornuCmdCreateTrack) + InfoMessage( _("Pick other end of Cornu") ); + else + InfoMessage( _("Select flextrack ends or anchors and drag, Enter to approve, Esc to Cancel") ); + Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],FALSE,TRUE,TRUE,FALSE,0.0,0.0,zero,NULL); + return C_CONTINUE; + } InfoMessage( _("Put other end of Cornu on a track with an unconnected end point") ); - Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0], FALSE,!QueryTrack(Da.trk[0],Q_IS_CORNU),QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY)); - DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack); + if (Da.trk[0]) + Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0], FALSE,!QueryTrack(Da.trk[0],Q_IS_CORNU),QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY), + Da.trk[0]!=NULL,Da.angle[0],Da.radius[0],Da.center[0],NULL); + else + Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0], FALSE,TRUE,TRUE,FALSE, Da.angle[0],Da.radius[0],Da.center[0],&Da.endHandle[0]); + return C_CONTINUE; + } else if (Da.state == POS_2 && (Da.cmdType == cornuCmdCreateTrack || Da.cmdType == cornuCmdHotBar || Da.trk[1] )){ + Da.state = PICK_POINT; + Da.selectEndPoint = -1; + Da.prevEndPoint = 1; + Da.prevSelected = -1; + SetUpCornuParms(&cp); + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + else Da.crvSegs_da_cnt = 0; + CreateBothEnds(-1,-1,-1,-1); + Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da); return C_CONTINUE; } else { return AdjustCornuCurve( action&0xFF, pos, InfoMessage ); } + return C_CONTINUE; + break; case C_TEXT: - if (Da.state != PICK_POINT || (action>>8) != 32) //Space is same as Enter. + if (Da.state != PICK_POINT) return C_CONTINUE; + if ((action>>8 == 127) || (action>>8 == 8)) // + return AdjustCornuCurve(action, pos, InfoMessage); + if (!(action>>8 == 32 )) //Space is same as Enter. return C_CONTINUE; /* no break */ case C_OK: if (Da.state != PICK_POINT) return C_CONTINUE; - return AdjustCornuCurve( C_OK, pos, InfoMessage); + STATUS_T rc = AdjustCornuCurve( C_OK, pos, InfoMessage); + if (rc == C_TERMINATE) { + Da.state = NONE; + Da.ep1Segs_da_cnt = 0; + Da.ep2Segs_da_cnt = 0; + Da.crvSegs_da_cnt = 0; + for (int i=0;i<2;i++) { + Da.radius[i] = 0.0; + Da.angle[i] = 0.0; + Da.center[i] = zero; + Da.trk[i] = NULL; + Da.ep[i] = -1; + Da.pos[i] = zero; + Da.endHandle[i].end_valid = FALSE; + } + } + return rc; case C_REDRAW: if ( Da.state != NONE ) { - DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,Da.ep2Segs,Da.ep2Segs_da_cnt,(trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da.cnt, NULL, - Da.extend[0]?&Da.extendSeg[0]:NULL,Da.extend[1]?&Da.extendSeg[1]:NULL,Da.color); + DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,Da.ep2Segs,Da.ep2Segs_da_cnt,(trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da_cnt, NULL, + Da.extend[0]?&Da.extendSeg[0]:NULL,Da.extend[1]?&Da.extendSeg[1]:NULL,(trkSeg_t *)Da.midSegs.ptr,Da.midSegs.cnt,wDrawColorBlack); } + if (anchors_da.cnt) + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + if (MyGetKeyState()&WKEY_SHIFT) DrawHighlightBoxes(FALSE,FALSE,NULL); + return C_CONTINUE; case C_CANCEL: if (Da.state != NONE) { - DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,Da.ep2Segs,Da.ep2Segs_da_cnt,(trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da.cnt, NULL, - Da.extend[0]?&Da.extendSeg[0]:NULL,Da.extend[1]?&Da.extendSeg[1]:NULL,Da.color); Da.ep1Segs_da_cnt = 0; Da.ep2Segs_da_cnt = 0; Da.crvSegs_da_cnt = 0; @@ -1262,21 +2568,486 @@ STATUS_T CmdCornu( wAction_t action, coOrd pos ) Da.trk[i] = NULL; Da.ep[i] = -1; Da.pos[i] = zero; + Da.endHandle[i].end_valid = FALSE; } //DYNARR_FREE(trkSeg_t,Da.crvSegs_da); } Da.state = NONE; + if (infoSubst) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } return C_CONTINUE; default: return C_CONTINUE; } + return C_CONTINUE; +} +BOOL_T GetTracksFromCornuTrack(track_p trk, track_p newTracks[2]) { + track_p trk_old = NULL; + newTracks[0] = NULL, newTracks[1] = NULL; + struct extraData * xx = GetTrkExtraData(trk); + if (!IsTrack(trk)) return FALSE; + for (int i=0; i<xx->cornuData.arcSegs.cnt;i++) { + track_p bezTrack[2]; + bezTrack[0] = NULL, bezTrack[1] = NULL; + trkSeg_p seg = &DYNARR_N(trkSeg_t,xx->cornuData.arcSegs,i); + if (seg->type == SEG_BEZTRK) { + DYNARR_RESET(trkSeg_t,seg->bezSegs); + FixUpBezierSeg(seg->u.b.pos,seg,TRUE); + GetTracksFromBezierSegment(seg, bezTrack, trk); + if (newTracks[0] == NULL) newTracks[0] = bezTrack[0]; + newTracks[1] = bezTrack[1]; + if (trk_old) { + for (int i=0;i<2;i++) { + if (GetTrkEndTrk(trk_old,i)==NULL) { + coOrd pos = GetTrkEndPos(trk_old,i); + EPINX_T ep_n = PickUnconnectedEndPoint(pos,bezTrack[0]); + if ((connectDistance >= FindDistance(GetTrkEndPos(trk_old,i),GetTrkEndPos(bezTrack[0],ep_n))) && + (connectAngle >= fabs(DifferenceBetweenAngles(GetTrkEndAngle(trk_old,i),GetTrkEndAngle(bezTrack[0],ep_n)+180))) ) { + ConnectTracks(trk_old,i,bezTrack[0],ep_n); + break; + } + } + } + } + trk_old = newTracks[1]; + } else { + track_p new_trk; + if (seg->type == SEG_CRVTRK) + new_trk = NewCurvedTrack(seg->u.c.center,seg->u.c.radius,seg->u.c.a0,seg->u.c.a1,0); + else if (seg->type == SEG_STRTRK) + new_trk = NewStraightTrack(seg->u.l.pos[0],seg->u.l.pos[1]); + if (newTracks[0] == NULL) newTracks[0] = new_trk; + CopyAttributes( trk, new_trk ); + newTracks[1] = new_trk; + if (trk_old) { + for (int i=0;i<2;i++) { + if (GetTrkEndTrk(trk_old,i)==NULL) { + coOrd pos = GetTrkEndPos(trk_old,i); + EPINX_T ep_n = PickUnconnectedEndPoint(pos,new_trk); + if ((connectDistance >= FindDistance(GetTrkEndPos(trk_old,i),GetTrkEndPos(new_trk,ep_n))) && + (connectAngle >= fabs(DifferenceBetweenAngles(GetTrkEndAngle(trk_old,i),GetTrkEndAngle(new_trk,ep_n)+180)))) { + ConnectTracks(trk_old,i,new_trk,ep_n); + break; + } + } + } + } + trk_old = new_trk; + } + } + return TRUE; + +} + +static STATUS_T cmdCornuCreate( + wAction_t action, + coOrd pos ) { + static int createState = 0; + int rc = 0; + + switch(action&0xFF) { + + case C_DOWN: + return CmdCornu(C_DOWN,pos); + case C_UP: + rc = CmdCornu(C_UP,pos); + return rc; + case C_FINISH: + if (createState != 0 ) { + createState = 0; + CmdCornu( C_OK, pos ); + } else + CmdCornu( C_CANCEL, pos ); + Da.prevSelected = -1; + Da.selectEndHandle = -1; + Da.selectEndPoint = -1; + Da.selectMidPoint = -1; + Da.selectTrack = NULL; + Da.trk[0] = NULL; Da.trk[1] = NULL; + Da.radius[0] = Da.radius[1] = -1.0; + Da.angle[0] = Da.angle[1] = 0.0; + Da.ends[0] = Da.ends[1] = FALSE; + Da.endHandle[0].end_valid = Da.endHandle[1].end_valid = FALSE; + return C_TERMINATE; + case C_TEXT: + if ((action>>8) != ' ' && (action>>8) != 32) + return CmdCornu(action,pos); + /*no break*/ + case C_OK: + CmdCornu(C_OK,pos); + MainRedraw(); + return C_CONTINUE; + case C_CANCEL: + HotBarCancel(); + CmdCornu(C_CANCEL, pos); + createState = 0; + rc = C_TERMINATE; + /* no break */ + case C_START: + createState = 0; + commandContext = (void *)cornuCmdHotBar; + rc = CmdCornu(C_START, pos); + Da.prevSelected = -1; + Da.selectEndHandle = -1; + Da.selectEndPoint = -1; + Da.selectMidPoint = -1; + Da.selectTrack = NULL; + Da.trk[0] = NULL; Da.trk[1] = NULL; + Da.radius[0] = Da.radius[1] = -1.0; + Da.angle[0] = Da.angle[1] = 0.0; + Da.ends[0] = Da.ends[1] = FALSE; + Da.endHandle[0].end_valid = Da.endHandle[1].end_valid = FALSE; + return rc; + default: + return CmdCornu(action,pos); + } + return C_CONTINUE; } +static STATUS_T CmdConvertTo( + wAction_t action, + coOrd pos ) +{ + static track_p trk; + cornuParm_t cp; + switch (action) { + + case wActionMove: + if ((trk = OnTrack(&pos,FALSE,TRUE)) == NULL) return C_CONTINUE; + if (!QueryTrack(trk, Q_CORNU_CAN_MODIFY) && //Not Fixed Track/Turnout/Turntable + !QueryTrack(trk, Q_IGNORE_EASEMENT_ON_EXTEND )) + trk = NULL; + return C_CONTINUE; + + case C_LCLICK: + if ((trk = OnTrack(&pos,FALSE,TRUE))!=NULL) { + SetTrkBits(trk,TB_SELECTED); + selectedTrackCount = 1; + } else { + wBeep(); + InfoMessage( _("Not on a Track") ); + return C_CONTINUE; + } + trk = NULL; + + /* no break */ + case C_START: + if (selectedTrackCount==0) { + InfoMessage( _("Select a Track To Convert") ); + return C_CONTINUE; + } + else if (selectedTrackCount>1) { + if (NoticeMessage(_("Convert all Selected Tracks to Cornu Tracks?"), _("Yes"), _("No"))<=0) { + SetAllTrackSelect(FALSE); + return C_TERMINATE; + } + } + UndoStart( _("Convert Cornu"),"newCornu curves"); + trk = NULL; + int converted=0, not_convertable = 0, created=0, deleted=0; + DYNARR_RESET(track_p,Da.tracks); + while ( TrackIterate( &trk ) ) { + if (!GetTrkSelected( trk )) continue; //Only selected + if (!QueryTrack(trk, Q_CORNU_CAN_MODIFY) && //Not Fixed Track/Turnout/Turntable + !QueryTrack( trk, Q_IGNORE_EASEMENT_ON_EXTEND )) { //But Yes to Easement + not_convertable++; + continue; + } + converted++; + DYNARR_RESET(trkSeg_t,Da.crvSegs_da); + Da.ep1Segs_da_cnt = 0; + Da.ep2Segs_da_cnt = 0; + Da.midSegs.cnt = 0; + Da.extend[0] = FALSE; + Da.extend[1] = FALSE; + Da.selectEndPoint = -1; + Da.selectTrack = NULL; + DYNARR_RESET(coOrd,Da.mid_points); + ClrTrkBits( trk, TB_SELECTED ); //Done with this one + Da.selectTrack = trk; + DYNARR_APPEND(track_p,Da.tracks,1); + DYNARR_LAST(track_p,Da.tracks) = trk; + Da.trk[0] = GetTrkEndTrk( trk, 0 ); + track_p prior = trk; + if (Da.trk[0]) Da.ep[0] = GetEndPtConnectedToMe(Da.trk[0],trk); + else Da.ep[0] = -1; + EPINX_T ep0 = 0; + //Move down the LHS adding tracks until no more Selected or not modifyable + while (Da.trk[0] && GetTrkSelected( Da.trk[0]) && IsTrack(Da.trk[0]) && (QueryTrack(Da.trk[0], Q_CORNU_CAN_MODIFY) || QueryTrack(Da.trk[0], Q_IS_CORNU)) ) { + prior = Da.trk[0]; + ep0 = 1-Da.ep[0]; + ClrTrkBits( Da.trk[0], TB_SELECTED ); //Done with this one + if (selectedTrackCount>0) selectedTrackCount--; + DYNARR_APPEND(track_p,Da.tracks,1); + DYNARR_LAST(track_p,Da.tracks) = prior; + DYNARR_APPEND(coOrd,Da.mid_points,1); + for (int i=Da.mid_points.cnt-1;i>1;i--) { + DYNARR_N(coOrd,Da.mid_points,i) = DYNARR_N(coOrd,Da.mid_points,i-1); + } + DYNARR_N(coOrd,Da.mid_points,0) = GetTrkEndPos(prior,1-ep0); + Da.trk[0] = GetTrkEndTrk( prior, ep0 ); + if (Da.trk[0]) Da.ep[0] = GetEndPtConnectedToMe(Da.trk[0],prior); + else Da.ep[0] = -1; + converted++; + } + Da.radius[0] = -1.0; //Initialize with no end + Da.ends[0] = FALSE; + Da.center[0] = zero; + Da.pos[0] = GetTrkEndPos(prior,ep0); + if (Da.trk[0] && Da.ep[0]>=0) { + GetConnectedTrackParms(Da.trk[0],GetTrkEndPos(Da.trk[0],Da.ep[0]),0,Da.ep[0],FALSE); + } + + //Move to RHS + + Da.trk[1] = GetTrkEndTrk( trk, 1 ); + track_p next = trk; + EPINX_T ep1 = 1; + if (Da.trk[1]) Da.ep[1] = GetEndPtConnectedToMe(Da.trk[1],trk); + else Da.ep[1] = -1; + //Move down RHS adding tracks until no more Selected or not modifyable + while (Da.trk[1] && GetTrkSelected( Da.trk[1]) && (QueryTrack(Da.trk[1], Q_CORNU_CAN_MODIFY) || QueryTrack(Da.trk[1], Q_IS_CORNU))) { + next = Da.trk[1]; + ep1 = 1-Da.ep[1]; + if (selectedTrackCount>0) selectedTrackCount--; + ClrTrkBits( Da.trk[1], TB_SELECTED ); //Done with this one + DYNARR_APPEND(track_p,Da.tracks,1); + DYNARR_LAST(track_p,Da.tracks) = next; + DYNARR_APPEND(coOrd,Da.mid_points,1); + DYNARR_LAST(coOrd,Da.mid_points) = GetTrkEndPos(next,1-ep1); + Da.trk[1] = GetTrkEndTrk( next, ep1 ); + if (Da.trk[1]) Da.ep[1] = GetEndPtConnectedToMe(Da.trk[1],next); + converted++; + } + Da.radius[1] = -1.0; //Initialize with no end + Da.ends[1] = FALSE; + Da.center[1] = zero; + Da.pos[1] = GetTrkEndPos(next,ep1); + if (Da.trk[1] && Da.ep[1]>=0) { + GetConnectedTrackParms(Da.trk[1],GetTrkEndPos(Da.trk[1],Da.ep[1]),1,Da.ep[1],FALSE); + } + SetUpCornuParms(&cp); + if (CallCornuM(Da.mid_points,Da.ends,Da.pos,&cp,&Da.crvSegs_da,TRUE)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt; + else continue; //Checks that a solution can be found + + // Do the deed - Create a replacement Cornu + + BOOL_T end_point[2]; + end_point[0] = TRUE; + end_point[1] = FALSE; + coOrd sub_pos[2]; + sub_pos[0] = Da.pos[0]; + if (Da.radius[0] == -1) end_point[0] = FALSE; + track_p first_trk= NULL,trk1=NULL,trk2 = NULL; + + for (int i=0;i<Da.mid_points.cnt;i++) { + sub_pos[1] = DYNARR_N(coOrd,Da.mid_points,i); + if ((trk1 = CreateCornuFromPoints(sub_pos,end_point))== NULL) continue; + if (Da.trk[0]) { + CopyAttributes( Da.trk[0], trk1 ); + } else if (Da.trk[1]) { + CopyAttributes( Da.trk[1], trk1 ); + } else { + SetTrkScale( trk1, GetLayoutCurScale() ); + SetTrkBits( trk1, TB_HIDEDESC ); + } + DrawNewTrack(trk1); + if (first_trk == NULL) first_trk = trk1; + if (trk2) ConnectTracks(trk1,0,trk2,1); + trk2 = trk1; + end_point[0] = FALSE; + sub_pos[0] = DYNARR_N(coOrd,Da.mid_points,i); + } + sub_pos[1] = Da.pos[1]; + end_point[1] = TRUE; + if (Da.radius[1] == -1) end_point[1] = FALSE; + if ((trk1 = CreateCornuFromPoints(sub_pos,end_point)) == NULL) continue; + created++; + DrawNewTrack(trk1); + if (Da.trk[0]) { + CopyAttributes( Da.trk[0], trk1 ); + } else if (Da.trk[1]){ + CopyAttributes( Da.trk[1], trk1 ); + } else { + SetTrkScale( trk1, GetLayoutCurScale() ); + SetTrkBits( trk1, TB_HIDEDESC ); + } + if (trk2) ConnectTracks(trk1,0,trk2,1); + if (first_trk == NULL) first_trk = trk1; + + for (int i=0;i<2;i++) { + if (Da.ep[i]>=0 && Da.trk[i]) { + track_p trk_old = GetTrkEndTrk(Da.trk[i],Da.ep[i]); + EPINX_T old_ep = GetEndPtConnectedToMe(trk_old,Da.trk[i]); + DisconnectTracks(Da.trk[i],Da.ep[i],trk_old,old_ep); + if (Da.ep[i]>=0 && Da.trk[i]) + ConnectTracks(Da.trk[i],Da.ep[i],i==0?first_trk:trk1,i); + } + } + + } //Find next track + SetAllTrackSelect(FALSE); + //Get rid of old tracks + for (int i = 0; i<Da.tracks.cnt;i++) { + DeleteTrack(DYNARR_N(track_p,Da.tracks,i),FALSE); + deleted++; + } + + UndoEnd(); //Stop accumulating + NoticeMessage(_("Tracks Counts: %d converted %d unconvertible %d created %d deleted"),_("OK"),NULL,converted,not_convertable,created,deleted); + + return C_TERMINATE; + + case C_REDRAW: + if (trk) { + DrawTrack(trk,&tempD,wDrawColorPreviewSelected); + } + 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; + } +} +static STATUS_T CmdConvertFrom( + wAction_t action, + coOrd pos ) +{ + static track_p trk; + track_p trk1,trk2; + switch (action) { + + case wActionMove: + if ((trk = OnTrack(&pos,FALSE,TRUE)) == NULL) return C_CONTINUE; + if ((!(GetTrkType(trk) == T_CORNU)) || + (!(GetTrkType(trk) == T_BEZIER))) + trk = NULL; + return C_CONTINUE; + + case C_LCLICK: + if ((trk = OnTrack(&pos,FALSE,TRUE))!=NULL) { + SetTrkBits(trk,TB_SELECTED); + selectedTrackCount = 1; + trk = NULL; + } else { + wBeep(); + InfoMessage( _("Not on a Track") ); + trk = NULL; + return C_CONTINUE; + } + /* no break */ + case C_START: + if (selectedTrackCount==0) { + InfoMessage( _("Select a Cornu or Bezier Track To Convert to Fixed") ); + return C_CONTINUE; + } + else if (selectedTrackCount>1) { + if (NoticeMessage(_("Convert all Selected Tracks to Fixed Tracks?"), _("Yes"), _("No"))<=0) { + SetAllTrackSelect(FALSE); + return C_TERMINATE; + } + } + dynArr_t trackSegs_da; + DYNARR_RESET(trkSeg_t,trackSegs_da); + trk1 = NULL; + trk2 = NULL; + trk = NULL; + UndoStart( _("Convert Bezier and Cornu"),"Try to convert all selected tracks"); + track_p tracks[2]; + DYNARR_RESET(track_p,Da.tracks); + int converted=0, not_convertable = 0, created=0, deleted=0; + while ( TrackIterate( &trk1 ) ) { + if ( GetTrkSelected( trk1 ) && IsTrack( trk1 ) ) { + //Only Cornu or Bezier + tracks[0] = NULL, tracks[1] = NULL; + if (selectedTrackCount>0) selectedTrackCount--; + ClrTrkBits( trk1, TB_SELECTED ); //Done with this one + if (GetTrkType(trk1) == T_CORNU) { + GetTracksFromCornuTrack(trk1,tracks); + DYNARR_APPEND(track_p,Da.tracks,1); + DYNARR_LAST(track_p,Da.tracks) = trk1; + converted++; + } else if (GetTrkType(trk1) == T_BEZIER) { + GetTracksFromBezierTrack(trk1,tracks); + DYNARR_APPEND(track_p,Da.tracks,1); + DYNARR_LAST(track_p,Da.tracks) = trk1; + converted++; + } else { + not_convertable++; + continue; + } + for (int i=0;i<2;i++) { + track_p trk2 = GetTrkEndTrk(trk1,i); + if (trk2) { + EPINX_T ep1 = GetEndPtConnectedToMe( trk2, trk1 ); + DisconnectTracks(trk2,ep1,trk1,i); + pos = GetTrkEndPos(trk2,ep1); + for (int j=0;j<2;j++) { + EPINX_T ep2 = PickUnconnectedEndPointSilent( pos, tracks[j] ); + coOrd ep_pos; + if (ep2<0) continue; + ep_pos = GetTrkEndPos(tracks[j],ep2); + if (connectDistance>=FindDistance(pos,ep_pos) && + connectAngle >= fabs(DifferenceBetweenAngles(GetTrkEndAngle(tracks[j],ep2),GetTrkEndAngle(trk2,ep1)+180))) { + ConnectTracks(trk2,ep1,tracks[j],ep2); + break; + } + } + } + } + } + } + SetAllTrackSelect(FALSE); + for (int i = 0; i<Da.tracks.cnt;i++) { + DeleteTrack(DYNARR_N(track_p,Da.tracks,i),FALSE); + deleted++; + } + UndoEnd(); + NoticeMessage(_("Tracks Counts: %d converted %d unconvertible %d deleted"),_("OK"),NULL,converted,not_convertable,deleted); + return C_TERMINATE; + + case C_REDRAW: + if (trk) { + DrawTrack(trk,&tempD,wDrawColorPreviewSelected); + } + 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/convertto.xpm" +#include "bitmaps/convertfr.xpm" EXPORT void InitCmdCornu( wMenu_p menu ) { - + ButtonGroupBegin( _("Convert"), "cmdConvertSetCmd", _("Convert") ); + AddMenuButton( menu, CmdConvertTo, "cmdConvertTo", _("Convert To Cornu"), wIconCreatePixMap(convertto_xpm), LEVEL0_50, IC_STICKY|IC_LCLICK|IC_POPUP3|IC_WANT_MOVE,ACCL_CONVERTTO, NULL ); + AddMenuButton( menu, CmdConvertFrom, "cmdConvertFrom", _("Convert From Cornu"), wIconCreatePixMap(convertfr_xpm), LEVEL0_50, IC_STICKY|IC_LCLICK|IC_POPUP3|IC_WANT_MOVE,ACCL_CONVERTFR, NULL ); + cornuHotBarCmdInx = AddMenuButton(menu, cmdCornuCreate, "cmdCornuCreate", "", NULL, LEVEL0_50, IC_STICKY|IC_POPUP3|IC_WANT_MOVE, 0, NULL); + ButtonGroupEnd(); + ParamCreateControls( &cornuModPG, cornuModDlgUpdate) ; } diff --git a/app/bin/ccornu.h b/app/bin/ccornu.h index b279cb4..2bd1f49 100644 --- a/app/bin/ccornu.h +++ b/app/bin/ccornu.h @@ -11,13 +11,24 @@ typedef void (*cornuMessageProc)( char *, ... ); +#define cornuCmdNone (0) +#define cornuJoinTrack (1) +#define cornuCmdCreateTrack (2) +#define cornuCmdHotBar (3) + #endif /* APP_BIN_CCORNU_H_ */ STATUS_T CmdCornu( wAction_t action, coOrd pos ); +BOOL_T CallCornu0(coOrd pos[2], coOrd center[2], ANGLE_T angle[2], DIST_T radius[2], dynArr_t * array_p, BOOL_T spots); DIST_T CornuMinRadius(coOrd pos[4],dynArr_t segs); DIST_T CornuMaxRateofChangeofCurvature(coOrd pos[4],dynArr_t segs,DIST_T * last_c); DIST_T CornuLength(coOrd pos[4],dynArr_t segs); +DIST_T CornuOffsetLength(dynArr_t segs, double offset); DIST_T CornuTotalWindingArc(coOrd pos[4],dynArr_t segs); STATUS_T CmdCornuModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG); + +void InitCmdCornu( wMenu_p menu ); + +void AddHotBarCornu( void ); diff --git a/app/bin/ccurve.c b/app/bin/ccurve.c index 58bb5c1..e119610 100644 --- a/app/bin/ccurve.c +++ b/app/bin/ccurve.c @@ -39,57 +39,102 @@ #include "utility.h" #include "wlib.h" #include "cbezier.h" +#include "ccornu.h" +#include "layout.h" /* * STATE INFO */ +typedef enum createState_e {NOCURVE,FIRSTEND_DEF,SECONDEND_DEF,CENTER_DEF} createState_e; + static struct { STATE_T state; + createState_e create_state; coOrd pos0; coOrd pos1; curveData_t curveData; track_p trk; EPINX_T ep; BOOL_T down; + BOOL_T lock0; + coOrd middle; + coOrd end0; + coOrd end1; } Da; static long curveMode; +static dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) +#define array_anchor(N) DYNARR_N(trkSeg_t,*anchor_array,N) + -EXPORT void DrawArrowHeads( +EXPORT int 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 ); - } + 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 ); + } else { + sp[3].u.l.pos[0] = p1; + sp[3].u.l.pos[1] = p1; + sp[4].u.l.pos[0] = p1; + sp[4].u.l.pos[1] = p1; + } + return 5; } +EXPORT int DrawArrowHeadsArray( + dynArr_t *anchor_array, + coOrd pos, + ANGLE_T angle, + BOOL_T bidirectional, + wDrawColor color ) +{ + int i = (*anchor_array).cnt; + DYNARR_SET(trkSeg_t,*anchor_array,i+5) + return DrawArrowHeads(&DYNARR_N(trkSeg_t,*anchor_array,i),pos,angle,bidirectional,color); + +} + +static void CreateEndAnchor(coOrd p, dynArr_t * anchor_array, wBool_t lock) { + DIST_T d = tempD.scale*0.15; + + DYNARR_APPEND(trkSeg_t,*anchor_array,1); + int i = (*anchor_array).cnt-1; + array_anchor(i).type = lock?SEG_FILCRCL:SEG_CRVLIN; + array_anchor(i).color = wDrawColorBlue; + array_anchor(i).u.c.center = p; + array_anchor(i).u.c.radius = d/2; + array_anchor(i).u.c.a0 = 0.0; + array_anchor(i).u.c.a1 = 360.0; + array_anchor(i).width = 0; +} @@ -100,6 +145,7 @@ EXPORT STATUS_T CreateCurve( wDrawColor color, DIST_T width, long mode, + dynArr_t * anchor_array, curveMessageProc message ) { track_p t; @@ -110,23 +156,28 @@ EXPORT STATUS_T CreateCurve( switch ( action ) { case C_START: - DYNARR_SET( trkSeg_t, tempSegs_da, 8 ); + DYNARR_RESET(trkSeg_t,*anchor_array); + DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); + Da.create_state = NOCURVE; + tempSegs_da.cnt = 0; Da.down = FALSE; //Not got a valid start yet + Da.pos0 = zero; + Da.pos1 = zero; switch ( curveMode ) { case crvCmdFromEP1: if (track) - message(_("Drag from End-Point in direction of curve - Shift locks to track open end-point") ); + message(_("Drag from endpoint in direction of curve - lock to track open endpoint") ); else - message (_("Drag from End-Point in direction of curve") ); + message (_("Drag from endpoint in direction of curve") ); break; case crvCmdFromTangent: if (track) - message(_("Drag from End-Point to Center - Shift locks to track open end-point") ); + message(_("Drag from endpoint to center - lock to track open endpoint") ); else - message(_("Drag from End-Point to Center") ); + message(_("Drag from endpoint to center") ); break; case crvCmdFromCenter: - message(_("Drag from Center to End-Point") ); + message(_("Drag from center to endpoint") ); break; case crvCmdFromChord: message(_("Drag from one to other end of chord") ); @@ -134,6 +185,7 @@ EXPORT STATUS_T CreateCurve( } return C_CONTINUE; case C_DOWN: + DYNARR_RESET(trkSeg_t, *anchor_array); for ( inx=0; inx<8; inx++ ) { tempSegs(inx).color = wDrawColorBlack; tempSegs(inx).width = 0; @@ -141,150 +193,212 @@ EXPORT STATUS_T CreateCurve( tempSegs_da.cnt = 0; p = pos; BOOL_T found = FALSE; - Da.trk = NULL; - if ((mode == crvCmdFromEP1 || mode == crvCmdFromTangent) && track && (MyGetKeyState() & WKEY_SHIFT) != 0) { - if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) { - EPINX_T ep = PickUnconnectedEndPointSilent(p, t); - if (ep != -1) { - Da.trk = t; - Da.ep = ep; - pos = GetTrkEndPos(t, ep); - found = TRUE; - } else { - Da.pos0=pos; - message(_("No unconnected end-point on track - Try again or release Shift and click")); - return C_CONTINUE; - } - } else { - Da.pos0=pos; - message(_("Not on a track - Try again or release Shift and click")); - return C_CONTINUE; + Da.trk = NULL; + if (track) { + if ((mode == crvCmdFromEP1 || mode == crvCmdFromTangent || (mode == crvCmdFromChord)) && + ((MyGetKeyState() & WKEY_ALT) == 0 ) == magneticSnap) { + if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) { + EPINX_T ep = PickUnconnectedEndPointSilent(p, t); + if (ep != -1) { + if (GetTrkScale(t) != (char)GetLayoutCurScale()) { + wBeep(); + InfoMessage(_("Track is different gauge")); + return C_CONTINUE; + } + Da.trk = t; + Da.ep = ep; + pos = GetTrkEndPos(t, ep); + found = TRUE; + } + } } - Da.down = TRUE; - } + } else { + if ((t = OnTrack(&p, FALSE, FALSE)) != NULL) { + if (!IsTrack(t)) { + pos = p; + found = TRUE; + } + } + } Da.down = TRUE; if (!found) SnapPos( &pos ); - pos0 = pos; - Da.pos0 = pos; + Da.lock0 = found; + + if (Da.create_state == NOCURVE) + Da.pos0 = pos; + else + Da.pos1 = pos; + + tempSegs_da.cnt = 1; switch (mode) { case crvCmdFromEP1: tempSegs(0).type = (track?SEG_STRTRK:SEG_STRLIN); tempSegs(0).color = color; tempSegs(0).width = width; - if (Da.trk) message(_("End Locked: Drag out curve start")); + Da.create_state = FIRSTEND_DEF; + Da.end0 = pos; + CreateEndAnchor(pos,anchor_array,found); + if (Da.trk && !(MyGetKeyState() & WKEY_SHIFT)) message(_("End locked: Drag out curve start")); + else if (Da.trk) message(_("End Position locked: Drag out curve start with Shift")); else message(_("Drag along curve start") ); break; case crvCmdFromTangent: + Da.create_state = FIRSTEND_DEF; + tempSegs(0).type = SEG_STRLIN; + tempSegs(0).color = color; + Da.create_state = CENTER_DEF; + CreateEndAnchor(pos,anchor_array,found); + if (Da.trk && !(MyGetKeyState() & WKEY_SHIFT)) message(_("End locked: Drag out curve center")); + else if (Da.trk) message(_("End Position locked: Drag out curve start with Shift")); + else message(_("Drag out curve center") ); + break; 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; - if (Da.trk && mode==crvCmdFromTangent) message(_("End Locked: Drag out to center")); - else - message( mode==crvCmdFromTangent?_("Drag from End-Point to Center"):_("Drag from Center to End-Point") ); + tempSegs(0).color = color; + Da.create_state = CENTER_DEF; + CreateEndAnchor(pos,anchor_array,FALSE); + message(_("Drag out from center to endpoint")); 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") ); + CreateEndAnchor(pos,anchor_array,FALSE); + Da.create_state = FIRSTEND_DEF; + if (Da.trk && !(MyGetKeyState() & WKEY_SHIFT)) + message( _("End locked: Drag to other end of chord") ); + else if (Da.trk) message(_("End Position locked: Drag out curve start with Shift")); + else + message( _("Drag to other end of chord") ); break; } - tempSegs(0).u.l.pos[0] = pos; + tempSegs(0).u.l.pos[0] = tempSegs(0).u.l.pos[1] = pos; return C_CONTINUE; case C_MOVE: + DYNARR_RESET(trkSeg_t,*anchor_array); + DYNARR_APPEND(trkSeg_t,*anchor_array,1); if (!Da.down) return C_CONTINUE; - if (Da.trk) { + if (Da.trk && !(MyGetKeyState() & WKEY_SHIFT)) { //Shift inhibits direction lock angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk, Da.ep)); - angle2 = NormalizeAngle(FindAngle(pos, pos0)-angle1); - if (mode ==crvCmdFromEP1) { + angle2 = NormalizeAngle(FindAngle(pos, Da.pos0)-angle1); + if (mode ==crvCmdFromEP1 ) { if (angle2 > 90.0 && angle2 < 270.0) - Translate( &pos, pos0, angle1, -FindDistance( pos0, pos )*cos(D2R(angle2)) ); - else pos = pos0; - } else { - DIST_T dp = -FindDistance(pos0, pos)*sin(D2R(angle2)); - if (angle2 > 180.0) - Translate( &pos, pos0, angle1+90.0, dp ); + Translate( &pos, Da.pos0, angle1, -FindDistance( Da.pos0, pos )*cos(D2R(angle2)) ); + else pos = Da.pos0; + } else if ( mode == crvCmdFromChord ) { + DIST_T dp = -FindDistance(Da.pos0, pos)*sin(D2R(angle2)); + if (DifferenceBetweenAngles(FindAngle(Da.pos0,pos),angle1)>0) + Translate( &pos, Da.pos0, angle1+90, dp ); + else + Translate( &pos, Da.pos0, angle1-90, -dp ); + } else if (mode == crvCmdFromCenter) { + DIST_T dp = -FindDistance(Da.pos0, pos)*sin(D2R(angle2)); + if (angle2 > 90 && angle2 < 270.0) + Translate( &pos, Da.pos0, angle1+90.0, dp ); else - Translate( &pos, pos0, angle1-90.0, dp ); + Translate( &pos, Da.pos0, angle1-90.0, dp ); + } else if (mode == crvCmdFromTangent) { + DIST_T dp = FindDistance(Da.pos0, pos)*sin(D2R(angle2)); + Translate( &pos, Da.pos0, angle1-90.0, dp ); } } else SnapPos(&pos); - tempSegs(0).u.l.pos[1] = pos; - d = FindDistance( pos0, pos ); - a = FindAngle( pos0, pos ); + tempSegs_da.cnt =1; + if (Da.trk && mode == crvCmdFromChord) { + tempSegs(0).type = SEG_CRVTRK; + tempSegs(0).u.c.center.x = (pos.x+Da.pos0.x)/2.0; + tempSegs(0).u.c.center.y = (pos.y+Da.pos0.y)/2.0; + tempSegs(0).u.c.radius = FindDistance(pos,Da.pos0)/2; + ANGLE_T a0 = FindAngle(tempSegs(0).u.c.center,Da.pos0); + ANGLE_T a1 = FindAngle(tempSegs(0).u.c.center,pos); + if (NormalizeAngle(a0+90-GetTrkEndAngle(Da.trk,Da.ep))<90) { + tempSegs(0).u.c.a0 = a0; + } else { + tempSegs(0).u.c.a0 = a1; + } + tempSegs(0).u.c.a1 = 180.0; + } else tempSegs(0).u.l.pos[1] = pos; + Da.pos1 = pos; + + d = FindDistance( Da.pos0, Da.pos1 ); + a = FindAngle( Da.pos0, Da.pos1 ); switch ( mode ) { case crvCmdFromEP1: if (Da.trk) message( _("Start Locked: Drag out curve start - Angle=%0.3f"), PutAngle(a)); else message( _("Drag out curve start - Angle=%0.3f"), PutAngle(a) ); + CreateEndAnchor(Da.pos0,anchor_array,Da.lock0); + DrawArrowHeadsArray( anchor_array, pos, FindAngle(Da.pos0,Da.pos1)+90, TRUE, wDrawColorBlue ); tempSegs_da.cnt = 1; break; case crvCmdFromTangent: - if (Da.trk) message( _("Tangent Locked: Drag out center - Radius=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) ); + if (Da.trk) message( _("Tangent locked: Drag out center - Radius=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) ); else message( _("Drag out center - 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; + CreateEndAnchor(Da.pos1,anchor_array,TRUE); + DrawArrowHeadsArray( anchor_array, Da.pos0, FindAngle(Da.pos0,Da.pos1)+90, TRUE, wDrawColorBlue ); + tempSegs_da.cnt = 1; 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; + message( _("Drag to Edge: Radius=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) ); + CreateEndAnchor(Da.pos0,anchor_array,Da.lock0); + DrawArrowHeadsArray( anchor_array, Da.pos1, FindAngle(Da.pos1,Da.pos0)+90, TRUE, wDrawColorBlue ); + tempSegs_da.cnt = 1; 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; + if (Da.trk) message( _("Start locked: Drag out chord length=%s angle=%0.3f"), FormatDistance(d), PutAngle(a) ); + else message( _("Drag out chord length=%s angle=%0.3f"), FormatDistance(d), PutAngle(a) ); + Da.middle.x = (Da.pos1.x+Da.pos0.x)/2.0; + Da.middle.y = (Da.pos1.y+Da.pos0.y)/2.0; + if (track && Da.trk) { + ANGLE_T ea = GetTrkEndAngle(Da.trk,Da.ep); + Translate(&Da.middle,Da.middle,ea,FindDistance(Da.middle,Da.pos0)); } + CreateEndAnchor(Da.pos0,anchor_array,TRUE); + CreateEndAnchor(Da.pos1,anchor_array,FALSE); + if (!track || !Da.trk) + DrawArrowHeadsArray( anchor_array, Da.middle, FindAngle(Da.pos0,Da.pos1)+90, TRUE, wDrawColorBlue ); break; } return C_CONTINUE; case C_UP: + /* Note - no anchor reset - assumes run after Down/Move */ if (!Da.down) return C_CONTINUE; if (Da.trk) { angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk, Da.ep)); - angle2 = NormalizeAngle(FindAngle(pos, pos0)-angle1); + angle2 = NormalizeAngle(FindAngle(pos, Da.pos0)-angle1); if (mode == crvCmdFromEP1) { if (angle2 > 90.0 && angle2 < 270.0) { - Translate( &pos, pos0, angle1, -FindDistance( pos0, pos )*cos(D2R(angle2)) ); + Translate( &pos, Da.pos0, angle1, -FindDistance( Da.pos0, pos )*cos(D2R(angle2)) ); Da.pos1 = pos; } else { ErrorMessage( MSG_TRK_TOO_SHORT, "Curved ", PutDim(0.0) ); return C_TERMINATE; } + } else if (mode == crvCmdFromTangent) { + DIST_T dp = FindDistance(Da.pos0, pos)*sin(D2R(angle2)); + Translate( &pos, Da.pos0, angle1-90.0, dp ); + Da.pos1 = pos; } else { - DIST_T dp = -FindDistance(pos0, pos)*sin(D2R(angle2)); + DIST_T dp = -FindDistance(Da.pos0, pos)*sin(D2R(angle2)); if (angle2 > 180.0) - Translate( &pos, pos0, angle1+90.0, dp ); + Translate( &pos, Da.pos0, angle1+90.0, dp ); else - Translate( &pos, pos0, angle1-90.0, dp ); + Translate( &pos, Da.pos0, angle1-90.0, dp ); Da.pos1 = pos; } + if (FindDistance(Da.pos0,Da.pos1)<minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, "Curved ", PutDim(FindDistance(Da.pos0,Da.pos1)) ); + return C_TERMINATE; + } } 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; + case crvCmdFromChord: + for (int i=0;i<(*anchor_array).cnt;i++) { + DYNARR_N(trkSeg_t,*anchor_array,i).color = drawColorRed; + } + break; } message( _("Drag on Red arrows to adjust curve") ); return C_CONTINUE; @@ -296,6 +410,7 @@ EXPORT STATUS_T CreateCurve( } + static STATUS_T CmdCurve( wAction_t action, coOrd pos ) { track_p t; @@ -310,38 +425,71 @@ static STATUS_T CmdCurve( wAction_t action, coOrd pos ) Da.state = -1; Da.pos0 = pos; tempSegs_da.cnt = 0; + segCnt = 0; STATUS_T rcode; - 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; + DYNARR_RESET(trkSeg_t,anchors_da); + return CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, &anchors_da, InfoMessage ); case C_DOWN: - if ( Da.state == -1 ) { - //SnapPos( &pos ); - Da.pos0 = pos; + if (Da.state == -1) { + BOOL_T found = FALSE; + if (curveMode != crvCmdFromCenter ) { + if (((MyGetKeyState() & WKEY_ALT)==0) == magneticSnap) { + if ((t = OnTrack(&pos,FALSE,TRUE))!=NULL) { + EPINX_T ep = PickUnconnectedEndPointSilent(pos, t); + if (ep != -1) { + if (GetTrkGauge(t) != GetScaleTrackGauge(GetLayoutCurScale())) { + wBeep(); + InfoMessage(_("Track is different gauge")); + return C_CONTINUE; + } + pos = GetTrkEndPos(t, ep); + found = TRUE; + } + } + } + } + if (!found) SnapPos( &pos ); + Da.pos0 = Da.pos1 = pos; Da.state = 0; - rcode = CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); + rcode = CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, &anchors_da, InfoMessage ); + segCnt = tempSegs_da.cnt ; if (!Da.down) Da.state = -1; return rcode; //Da.pos0 = pos; - } else { - tempSegs_da.cnt = segCnt; - return C_CONTINUE; } + //This is where the user could adjust - if we allow that? + tempSegs_da.cnt = segCnt; + return C_CONTINUE; + + + case wActionMove: + if ((Da.state<0) && (curveMode != crvCmdFromCenter)) { + DYNARR_RESET(trkSeg_t,anchors_da); + if (((MyGetKeyState() & WKEY_ALT)==0) == magneticSnap) { + if ((t=OnTrack(&pos,FALSE,TRUE))!= NULL) { + if (GetTrkGauge(t) == GetScaleTrackGauge(GetLayoutCurScale())) { + EPINX_T ep = PickUnconnectedEndPointSilent(pos, t); + if (ep != -1) { + pos = GetTrkEndPos(t, ep); + CreateEndAnchor(pos,&anchors_da,FALSE); + } + } + } + } + } + return C_CONTINUE; case C_MOVE: if (Da.state<0) return C_CONTINUE; - mainD.funcs->options = wDrawOptTemp; - DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); if ( Da.state == 0 ) { Da.pos1 = pos; - rc = CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); + rc = CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, &anchors_da, InfoMessage ); + segCnt = tempSegs_da.cnt ; } else { + DYNARR_RESET(trkSeg_t,anchors_da); // SnapPos( &pos ); + tempSegs_da.cnt = segCnt; if (Da.trk) PlotCurve( curveMode, Da.pos0, Da.pos1, pos, &Da.curveData, FALSE ); else PlotCurve( curveMode, Da.pos0, Da.pos1, pos, &Da.curveData, TRUE ); if (Da.curveData.type == curveTypeStraight) { @@ -349,11 +497,14 @@ static STATUS_T CmdCurve( wAction_t action, coOrd pos ) tempSegs(0).u.l.pos[0] = Da.pos0; tempSegs(0).u.l.pos[1] = Da.curveData.pos1; tempSegs_da.cnt = 1; + segCnt = 1; InfoMessage( _("Straight Track: Length=%s Angle=%0.3f"), FormatDistance(FindDistance( Da.pos0, Da.curveData.pos1 )), PutAngle(FindAngle( Da.pos0, Da.curveData.pos1 )) ); + DrawArrowHeadsArray(&anchors_da,Da.curveData.pos1,FindAngle(Da.pos0, Da.curveData.pos1)+90,TRUE,wDrawColorRed); } else if (Da.curveData.type == curveTypeNone) { tempSegs_da.cnt = 0; + segCnt = 0; InfoMessage( _("Back") ); } else if (Da.curveData.type == curveTypeCurve) { tempSegs(0).type = SEG_CRVTRK; @@ -362,6 +513,7 @@ static STATUS_T CmdCurve( wAction_t action, coOrd pos ) tempSegs(0).u.c.a0 = Da.curveData.a0; tempSegs(0).u.c.a1 = Da.curveData.a1; tempSegs_da.cnt = 1; + segCnt = 1; d = D2R(Da.curveData.a1); if (d < 0.0) d = 2*M_PI+d; @@ -375,80 +527,101 @@ static STATUS_T CmdCurve( wAction_t action, coOrd pos ) InfoMessage( _("Curved Track: Radius=%s Angle=%0.3f Length=%s"), FormatDistance(Da.curveData.curveRadius), Da.curveData.a1, FormatDistance(Da.curveData.curveRadius*d) ); + coOrd pos1; + Translate(&pos1,Da.curveData.curvePos,Da.curveData.a0+Da.curveData.a1,Da.curveData.curveRadius); + if (curveMode == crvCmdFromEP1 || curveMode == crvCmdFromChord) + DrawArrowHeadsArray(&anchors_da,pos,FindAngle(Da.curveData.curvePos,pos),TRUE,wDrawColorRed); + else if (curveMode == crvCmdFromTangent || curveMode == crvCmdFromCenter) { + CreateEndAnchor(Da.curveData.pos2,&anchors_da,FALSE); + DrawArrowHeadsArray(&anchors_da,Da.curveData.pos2,FindAngle(Da.curveData.curvePos,Da.curveData.pos2)+90,TRUE,wDrawColorRed); + } + CreateEndAnchor(Da.curveData.curvePos,&anchors_da,TRUE); } } - DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); mainD.funcs->options = 0; return rc; - - + case C_TEXT: + if ( Da.state == 0 ) + return CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, &anchors_da, InfoMessage ); + /*no break*/ case C_UP: if (Da.state<0) return C_CONTINUE; - mainD.funcs->options = wDrawOptTemp; - DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); - if (Da.state == 0) { + if (Da.state == 0 && ((curveMode != crvCmdFromChord) || (curveMode == crvCmdFromChord && !Da.trk))) { SnapPos( &pos ); Da.pos1 = pos; + if ((d = FindDistance(Da.pos0,Da.pos1))<minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, "Curved ", PutDim(fabs(minLength-d)) ); + return C_TERMINATE; + } Da.state = 1; - CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); - DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, &anchors_da, InfoMessage ); + tempSegs_da.cnt = 1; 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 ); - if (Da.trk) { - EPINX_T ep = PickUnconnectedEndPoint(Da.pos0, t); - if (ep != -1) ConnectTracks(Da.trk, Da.ep, t, ep); - } - 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 ); - if (Da.trk) { - EPINX_T ep = PickUnconnectedEndPoint(Da.pos0, t); - if (ep != -1) ConnectTracks(Da.trk, Da.ep, t, ep); - } - UndoEnd(); - } else { - return C_ERROR; + } else if ((curveMode == crvCmdFromChord && Da.state == 0 && Da.trk)) { + pos = Da.middle; + if ((d = FindDistance(Da.pos0,Da.pos1))<minLength) { + ErrorMessage( MSG_TRK_TOO_SHORT, "Curved ", PutDim(fabs(minLength-d)) ); + return C_TERMINATE; + } + PlotCurve( curveMode, Da.pos0, Da.pos1, Da.middle, &Da.curveData, TRUE ); + } + mainD.funcs->options = 0; + tempSegs_da.cnt = 0; + segCnt = 0; + Da.state = -1; + DYNARR_RESET(trkSeg_t,anchors_da); // No More anchors for this one + 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 ); + if (Da.trk && !(MyGetKeyState() & WKEY_SHIFT)) { + EPINX_T ep = PickUnconnectedEndPoint(Da.pos0, t); + if (ep != -1) ConnectTracks(Da.trk, Da.ep, t, ep); + } + 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 ); + if (Da.trk && !(MyGetKeyState() & WKEY_SHIFT)) { + EPINX_T ep = PickUnconnectedEndPoint(Da.pos0, t); + if (ep != -1) ConnectTracks(Da.trk, Da.ep, t, ep); } - DrawNewTrack( t ); - return C_TERMINATE; + 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 ); + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); mainD.funcs->options = 0; } + if (anchors_da.cnt) + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); 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.trk = NULL; } + DYNARR_RESET(trkSeg_t,anchors_da); + DYNARR_RESET(trkSeg_t,tempSegs_da); Da.state = -1; + segCnt = 0; return C_CONTINUE; } @@ -581,7 +754,6 @@ static void ComputeHelix( static void HelixCancel( wWin_p win ) { wHide( helixW ); - Reset(); } @@ -610,30 +782,30 @@ static STATUS_T CmdCircleCommon( wAction_t action, coOrd pos, BOOL_T helix ) 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 ); + 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 ) { + ParamLoadControls(&circleRadiusPG); + ParamGroupRecord(&circleRadiusPG); + switch (circleMode) { case circleCmdFixedRadius: controls[0] = circleRadiusPLs[0].control; controls[1] = NULL; labels[0] = N_("Circle Radius"); - InfoSubstituteControls( controls, labels ); + InfoSubstituteControls(controls, labels); break; case circleCmdFromTangent: - InfoSubstituteControls( NULL, NULL ); - InfoMessage( _("Click on Circle Edge") ); + InfoSubstituteControls(NULL, NULL); + InfoMessage(_("Click on Circle Edge")); break; case circleCmdFromCenter: - InfoSubstituteControls( NULL, NULL ); - InfoMessage( _("Click on Circle Center") ); + InfoSubstituteControls(NULL, NULL); + InfoMessage(_("Click on Circle Center")); break; } } @@ -641,98 +813,95 @@ static STATUS_T CmdCircleCommon( wAction_t action, coOrd pos, BOOL_T helix ) return C_CONTINUE; case C_DOWN: - DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); + DYNARR_SET(trkSeg_t, tempSegs_da, 1); tempSegs_da.cnt = 0; if (helix) { if (helixRadius <= 0.0) { - ErrorMessage( MSG_RADIUS_GTR_0 ); + ErrorMessage(MSG_RADIUS_GTR_0); return C_ERROR; } if (helixTurns <= 0) { - ErrorMessage( MSG_HELIX_TURNS_GTR_0 ); + ErrorMessage(MSG_HELIX_TURNS_GTR_0); return C_ERROR; } - ParamLoadData( &helixPG ); + ParamLoadData(&helixPG); } else { - ParamLoadData( &circleRadiusPG ); - switch( circleMode ) { + ParamLoadData(&circleRadiusPG); + switch (circleMode) { case circleCmdFixedRadius: if (circleRadius <= 0.0) { - ErrorMessage( MSG_RADIUS_GTR_0 ); + ErrorMessage(MSG_RADIUS_GTR_0); return C_ERROR; } break; case circleCmdFromTangent: - InfoSubstituteControls( NULL, NULL ); - InfoMessage( _("Drag to Center") ); + InfoSubstituteControls(NULL, NULL); + InfoMessage(_("Drag to Center")); break; case circleCmdFromCenter: - InfoSubstituteControls( NULL, NULL ); - InfoMessage( _("Drag to Edge") ); + InfoSubstituteControls(NULL, NULL); + InfoMessage(_("Drag to Edge")); break; } } - SnapPos( &pos ); + 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 ); + SnapPos(&pos); tempSegs(0).u.c.center = pos; - if ( !helix ) { - switch ( circleMode ) { + 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) ); + 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) ); + 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.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 (helixRadius > mapD.size.x && helixRadius > mapD.size.y) { - ErrorMessage( MSG_RADIUS_TOO_BIG ); - return C_ERROR; - } - if (circleRadius > mapD.size.x && circleRadius > mapD.size.y) { - ErrorMessage( MSG_RADIUS_TOO_BIG ); - return C_ERROR; - } - if ( helix ) { + if (helix) { + if (helixRadius > mapD.size.x || helixRadius > mapD.size.y) { + ErrorMessage(MSG_RADIUS_TOO_BIG); + return C_ERROR; + } if (helixRadius > 10000) { - ErrorMessage( MSG_RADIUS_GTR_10000 ); + ErrorMessage(MSG_RADIUS_GTR_10000); return C_ERROR; } - UndoStart( _("Create Helix Track"), "newHelix" ); - t = NewCurvedTrack( tempSegs(0).u.c.center, helixRadius, 0.0, 0.0, helixTurns ); + 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 ); + if (circleRadius > mapD.size.x || circleRadius > mapD.size.y) { + ErrorMessage(MSG_RADIUS_TOO_BIG); + return C_ERROR; + } + if (circleRadius <= 0) { + ErrorMessage(MSG_RADIUS_GTR_0); return C_ERROR; } if ((circleRadius > 100000) || (helixRadius > 10000)) { - ErrorMessage( MSG_RADIUS_GTR_10000 ); + ErrorMessage(MSG_RADIUS_GTR_10000); return C_ERROR; } - UndoStart( _("Create Circle Track"), "newCircle" ); - t = NewCurvedTrack( tempSegs(0).u.c.center, circleRadius, 0.0, 0.0, 0 ); + UndoStart(_("Create Circle Track"), "newCircle"); + t = NewCurvedTrack(tempSegs(0).u.c.center, circleRadius, 0.0, 0.0, 0); } UndoEnd(); DrawNewTrack(t); @@ -779,19 +948,21 @@ static STATUS_T CmdHelix( wAction_t action, coOrd pos ) #include "bitmaps/curve3.xpm" #include "bitmaps/curve4.xpm" #include "bitmaps/bezier.xpm" +#include "bitmaps/cornu.xpm" #include "bitmaps/circle1.xpm" #include "bitmaps/circle2.xpm" #include "bitmaps/circle3.xpm" EXPORT void InitCmdCurve( wMenu_p menu ) { + AddMenuButton( menu, CmdCornu, "cmdCornu", _("Cornu Curve"), wIconCreatePixMap(cornu_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2|IC_WANT_MOVE, ACCL_CORNU, (void*)cornuCmdCreateTrack); 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 ); - AddMenuButton( menu, CmdBezCurve, "cmdBezier", _("Bezier Curve"), wIconCreatePixMap(bezier_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_BEZIER, (void*)bezCmdCreateTrack ); + AddMenuButton( menu, CmdCurve, "cmdCurveEndPt", _("Curve from End-Pt"), wIconCreatePixMap( curve1_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2|IC_WANT_MOVE, ACCL_CURVE1, (void*)0 ); + AddMenuButton( menu, CmdCurve, "cmdCurveTangent", _("Curve from Tangent"), wIconCreatePixMap( curve2_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2|IC_WANT_MOVE, ACCL_CURVE2, (void*)1 ); + AddMenuButton( menu, CmdCurve, "cmdCurveCenter", _("Curve from Center"), wIconCreatePixMap( curve3_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2|IC_WANT_MOVE, ACCL_CURVE3, (void*)2 ); + AddMenuButton( menu, CmdCurve, "cmdCurveChord", _("Curve from Chord"), wIconCreatePixMap( curve4_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2|IC_WANT_MOVE, ACCL_CURVE4, (void*)3 ); + AddMenuButton( menu, CmdBezCurve, "cmdBezier", _("Bezier Curve"), wIconCreatePixMap(bezier_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2|IC_WANT_MOVE, ACCL_BEZIER, (void*)bezCmdCreateTrack ); ButtonGroupEnd(); ButtonGroupBegin( _("Circle Track"), "cmdCurveSetCmd", _("Circle Tracks") ); @@ -816,7 +987,7 @@ EXPORT void InitCmdCurve( wMenu_p menu ) void InitCmdHelix(wMenu_p menu) { AddMenuButton(menu, CmdHelix, "cmdHelix", _("Helix"), NULL, LEVEL0_50, - IC_STICKY|IC_POPUP2, ACCL_HELIX, NULL); + IC_STICKY|IC_INITNOTSTICKY|IC_POPUP2, ACCL_HELIX, NULL); ParamRegister(&helixPG); RegisterChangeNotification(ChangeHelixW); } diff --git a/app/bin/ccurve.h b/app/bin/ccurve.h index c9d1c8c..0c00c46 100644 --- a/app/bin/ccurve.h +++ b/app/bin/ccurve.h @@ -32,6 +32,7 @@ typedef struct { curveType_e type; coOrd curvePos; coOrd pos1; + coOrd pos2; DIST_T curveRadius; ANGLE_T a0, a1; BOOL_T negative; @@ -48,13 +49,14 @@ typedef struct { #define circleCmdFromCenter (2) typedef void (*curveMessageProc)( char *, ... ); -STATUS_T CreateCurve( wAction_t, coOrd, BOOL_T, wDrawColor, DIST_T, long, curveMessageProc ); +STATUS_T CreateCurve( wAction_t, coOrd, BOOL_T, wDrawColor, DIST_T, long, dynArr_t *,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 ); +DIST_T CurveDescriptionDistance( coOrd, track_p, coOrd *, BOOL_T, BOOL_T * ); STATUS_T CurveDescriptionMove( track_p, wAction_t, coOrd ); BOOL_T GetCurveMiddle( track_p , coOrd * ); -void DrawArrowHeads(trkSeg_p sp, coOrd pos, ANGLE_T angle, BOOL_T bidirectional, wDrawColor color ); +int DrawArrowHeads(trkSeg_p sp, coOrd pos, ANGLE_T angle, BOOL_T bidirectional, wDrawColor color ); +int DrawArrowHeadsArray(dynArr_t *anchor_array,coOrd pos,ANGLE_T angle,BOOL_T bidirectional,wDrawColor color ); #endif // !HAVE_CCURVE_H diff --git a/app/bin/cdraw.c b/app/bin/cdraw.c index 9bddcaf..6bb4c4a 100644 --- a/app/bin/cdraw.c +++ b/app/bin/cdraw.c @@ -23,6 +23,7 @@ #include <math.h> #include <stdint.h> #include <string.h> +#include "wlib.h" #include "ccurve.h" #include "cbezier.h" @@ -37,7 +38,31 @@ extern TRKTYP_T T_BZRLIN; -extern void wSetSelectedFontSize(int size); +static wMenu_p drawModDelMI; +static wMenu_p drawModLinMI; +static wMenuPush_p drawModDel; +static wMenuPush_p drawModSmooth; +static wMenuPush_p drawModVertex; +static wMenuPush_p drawModRound; +static wMenuPush_p drawModriginMode; +static wMenuPush_p drawModPointsMode; +static wMenuPush_p drawModOrigin; +static wMenuPush_p drawModLast; +static wMenuPush_p drawModCenter; +static wMenuPush_p drawModClose; +static wMenuPush_p drawModOpen; +static wMenuPush_p drawModFill; +static wMenuPush_p drawModEmpty; +static wMenuPush_p drawModSolid; +static wMenuPush_p drawModDot; +static wMenuPush_p drawModDash; +static wMenuPush_p drawModDashDot; +static wMenuPush_p drawModDashDotDot; +static wMenuPush_p drawModCenterDot; +static wMenuPush_p drawModPhantom; + + +extern void wSetSelectedFontSize(wFontSize_t size); static long fontSizeList[] = { 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 24, 28, 32, 36, @@ -111,7 +136,7 @@ EXPORT void UpdateFontSizeList( *fontSizeR = fontSize; /* inform gtkfont dialog from change */ - wSetSelectedFontSize((int)fontSize); + wSetSelectedFontSize((wFontSize_t)fontSize); /*LoadFontSizeList( list, *fontSizeR );*/ } else { sprintf( message, "%ld", *fontSizeR ); @@ -128,9 +153,11 @@ EXPORT void UpdateFontSizeList( * */ + struct extraData { coOrd orig; ANGLE_T angle; + drawLineType_e lineType; wIndex_t segCnt; trkSeg_t segs[1]; }; @@ -167,12 +194,13 @@ static track_p MakeDrawFromSeg1( xx->orig = pos; xx->angle = angle; xx->segCnt = 1; + xx->lineType = DRAWLINESOLID; memcpy( xx->segs, sp, sizeof *(trkSeg_p)0 ); if (xx->segs[0].type == SEG_POLY || - xx->segs[0].type == SEG_FILPOLY) { - xx->segs[0].u.p.pts = (coOrd*)MyMalloc( (sp->u.p.cnt) * sizeof (coOrd) ); - memcpy(xx->segs[0].u.p.pts, sp->u.p.pts, sp->u.p.cnt * sizeof (coOrd) ); + xx->segs[0].type == SEG_FILPOLY ) { + xx->segs[0].u.p.pts = (pts_t*)MyMalloc( (sp->u.p.cnt) * sizeof (pts_t) ); + memcpy(xx->segs[0].u.p.pts, sp->u.p.pts, sp->u.p.cnt * sizeof (pts_t) ); } if (xx->segs[0].type == SEG_TEXT) { xx->segs[0].u.t.string = MyStrdup(sp->u.t.string); @@ -189,6 +217,254 @@ EXPORT track_p MakeDrawFromSeg( return MakeDrawFromSeg1( 0, pos, angle, sp ); } +int SliceCuts(ANGLE_T a, DIST_T radius) { + double Error = 0.05; + double Error_angle = acos(1-(Error/fabs(radius))); + if (Error_angle <0.0001) return 0; + return (int)(floor(D2R(a)/(2*Error_angle))); +} + +/* Only straight, curved and PolyLine */ +EXPORT track_p MakePolyLineFromSegs( + coOrd pos, + ANGLE_T angle, + dynArr_t * segsArr) +{ + struct extraData * xx; + track_p trk; + trk = NewTrack( 0, T_DRAW, 0, sizeof *xx ); + xx = GetTrkExtraData( trk ); + xx->orig = pos; + xx->angle = angle; + xx->lineType = DRAWLINESOLID; + xx->segCnt = 1; + xx->segs[0].type = SEG_POLY; + xx->segs[0].width = 0; + xx->segs[0].u.p.polyType = POLYLINE; + xx->segs[0].color = wDrawColorBlack; + coOrd last; + BOOL_T first = TRUE; + int cnt = 0; + for (int i=0;i<segsArr->cnt;i++) { + trkSeg_p sp = &DYNARR_N(trkSeg_t,*segsArr,i); + if (sp->type == SEG_BEZLIN || sp->type == SEG_BEZTRK ) { + for (int j=0;j<sp->bezSegs.cnt;j++) { + trkSeg_p spb = &DYNARR_N(trkSeg_t,sp->bezSegs,j); + if (spb->type == SEG_STRLIN || spb->type == SEG_STRTRK) { + if (!first && IsClose(FindDistance(spb->u.l.pos[0], last))) + cnt++; + else + cnt=cnt+2; + last = spb->u.l.pos[1]; + first = FALSE; + } + else if (spb->type == SEG_CRVLIN || spb->type == SEG_CRVTRK) { + coOrd this; + if (spb->u.c.radius >= 0.0) + Translate(&this, spb->u.c.center, spb->u.c.a0, fabs(spb->u.c.radius)); + else + Translate(&this, spb->u.c.center, spb->u.c.a0+spb->u.c.a1, fabs(spb->u.c.radius)); + if (first || !IsClose(FindDistance(this, last))) { + cnt++; //Add first point + } + cnt += 1 + SliceCuts(spb->u.c.a1,spb->u.c.radius); + if (spb->u.c.radius >= 0.0) + Translate(&last, spb->u.c.center, spb->u.c.a0+spb->u.c.a1, fabs(spb->u.c.radius)); + else + Translate(&last, spb->u.c.center, spb->u.c.a0, fabs(spb->u.c.radius)); + first = FALSE; + } + } + } + else if (sp->type == SEG_STRLIN || sp->type == SEG_STRTRK) { + if (!first && IsClose(FindDistance(sp->u.l.pos[0], last))) + cnt++; + else + cnt=cnt+2; + last = sp->u.l.pos[1]; + first = FALSE; + } + else if (sp->type == SEG_CRVLIN || sp->type == SEG_CRVTRK) { + coOrd this; + if (sp->u.c.radius >= 0.0) + Translate(&this, sp->u.c.center, sp->u.c.a0, fabs(sp->u.c.radius)); + else + Translate(&this, sp->u.c.center, sp->u.c.a0+sp->u.c.a1, fabs(sp->u.c.radius)); + if (first || !IsClose(FindDistance(this, last))) { + cnt++; //Add first point + } + cnt += 1+ SliceCuts(sp->u.c.a1,sp->u.c.radius); + if (sp->u.c.radius >= 0.0) + Translate(&last, sp->u.c.center, sp->u.c.a0+sp->u.c.a1, fabs(sp->u.c.radius)); + else + Translate(&last, sp->u.c.center, sp->u.c.a0, fabs(sp->u.c.radius)); + first = FALSE; + } + else if (sp->type == SEG_POLY) { + if (!first && IsClose(FindDistance(sp->u.p.pts[0].pt, last))) + cnt = cnt + sp->u.p.cnt-1; + else + cnt = cnt + sp->u.p.cnt; + last = sp->u.p.pts[sp->u.p.cnt-1].pt; + first = FALSE; + } + } + xx->segs[0].u.p.cnt = cnt; + xx->segs[0].u.p.pts = (pts_t*)MyMalloc( (cnt) * sizeof (pts_t) ); + first = TRUE; + int j =0; + for (int i=0;i<segsArr->cnt;i++) { + trkSeg_p sp = &DYNARR_N(trkSeg_t,*segsArr,i); + if (sp->type == SEG_BEZLIN || sp->type == SEG_BEZTRK ) { + for (int l=0;l<sp->bezSegs.cnt;l++) { + trkSeg_p spb = &DYNARR_N(trkSeg_t,sp->bezSegs,l); + if (spb->type == SEG_STRLIN || spb->type == SEG_STRTRK) { + if (first || !IsClose(FindDistance(spb->u.l.pos[0], last))) { + xx->segs[0].u.p.pts[j].pt = spb->u.l.pos[0]; + xx->segs[0].u.p.pts[j].pt_type = wPolyLineStraight; + j++; + } + xx->segs[0].u.p.pts[j].pt = spb->u.l.pos[1]; + xx->segs[0].u.p.pts[j].pt_type = wPolyLineStraight; + last = xx->segs[0].u.p.pts[j].pt; + j ++; + first = FALSE; + } + if (spb->type == SEG_CRVLIN || spb->type == SEG_CRVTRK) { + coOrd this; + if (spb->u.c.radius>=0.0) + Translate(&this, spb->u.c.center, spb->u.c.a0, fabs(spb->u.c.radius)); + else + Translate(&this, spb->u.c.center, spb->u.c.a0+spb->u.c.a1, fabs(spb->u.c.radius)); + if (first || !IsClose(FindDistance(this, last))) { + xx->segs[0].u.p.pts[j].pt= this; + xx->segs[0].u.p.pts[j].pt_type = wPolyLineStraight; + j++; + } + int slices = SliceCuts(spb->u.c.a1,spb->u.c.radius); + for (int k=1; k<slices;k++) { + if (spb->u.c.radius>=0.0) + Translate(&xx->segs[0].u.p.pts[j].pt, spb->u.c.center, spb->u.c.a0+(k*(spb->u.c.a1/(slices))), fabs(spb->u.c.radius)); + else + Translate(&xx->segs[0].u.p.pts[j].pt, spb->u.c.center, spb->u.c.a0+((slices-k)*(spb->u.c.a1/(slices))), fabs(spb->u.c.radius)); + xx->segs[0].u.p.pts[j].pt_type = wPolyLineSmooth; + j++; + } + if (spb->u.c.radius>=0.0) + Translate(&xx->segs[0].u.p.pts[j].pt, spb->u.c.center, spb->u.c.a0+spb->u.c.a1, fabs(spb->u.c.radius)); + else + Translate(&xx->segs[0].u.p.pts[j].pt, spb->u.c.center, spb->u.c.a0, fabs(spb->u.c.radius)); + + xx->segs[0].u.p.pts[j].pt_type = wPolyLineStraight; + last = xx->segs[0].u.p.pts[j].pt; + j++; + first = FALSE; + } + } + } + if (sp->type == SEG_STRLIN || sp->type == SEG_STRTRK) { + if (first || !IsClose(FindDistance(sp->u.l.pos[0], last))) { + xx->segs[0].u.p.pts[j].pt = sp->u.l.pos[0]; + xx->segs[0].u.p.pts[j].pt_type = wPolyLineStraight; + j++; + } + xx->segs[0].u.p.pts[j].pt = last = sp->u.l.pos[1]; + xx->segs[0].u.p.pts[j].pt_type = wPolyLineStraight; + last = xx->segs[0].u.p.pts[j].pt; + j++; + first = FALSE; + } + if (sp->type == SEG_CRVLIN || sp->type == SEG_CRVTRK) { + coOrd this; + if (sp->u.c.radius>0) + Translate(&this, sp->u.c.center, sp->u.c.a0, fabs(sp->u.c.radius)); + else + Translate(&this, sp->u.c.center, sp->u.c.a0+sp->u.c.a1, fabs(sp->u.c.radius)); + if (first || !IsClose(FindDistance(this, last))) { + xx->segs[0].u.p.pts[j].pt= this; + xx->segs[0].u.p.pts[j].pt_type = wPolyLineStraight; + j++; + } + int slices = SliceCuts(sp->u.c.a1,sp->u.c.radius); + + for (int k=1; k<slices;k++) { + if (sp->u.c.radius>0) + Translate(&xx->segs[0].u.p.pts[j].pt, sp->u.c.center, sp->u.c.a0+(k*(sp->u.c.a1/(slices))), fabs(sp->u.c.radius)); + else + Translate(&xx->segs[0].u.p.pts[j].pt, sp->u.c.center, sp->u.c.a0+((slices-k)*(sp->u.c.a1/(slices))), fabs(sp->u.c.radius)); + xx->segs[0].u.p.pts[j].pt_type = wPolyLineSmooth; + j++; + } + if (sp->u.c.radius>0) + Translate(&xx->segs[0].u.p.pts[j].pt, sp->u.c.center, sp->u.c.a0+sp->u.c.a1, fabs(sp->u.c.radius)); + else + Translate(&xx->segs[0].u.p.pts[j].pt, sp->u.c.center, sp->u.c.a0, fabs(sp->u.c.radius)); + + xx->segs[0].u.p.pts[j].pt_type = wPolyLineStraight; + last = xx->segs[0].u.p.pts[j].pt; + j++; + first = FALSE; + } + if (sp->type == SEG_POLY) { + if (first || !IsClose(FindDistance(sp->u.p.pts[0].pt, last))) { + xx->segs[0].u.p.pts[j] = sp->u.p.pts[0]; + j++; + } + memcpy(&xx->segs[0].u.p.pts[j],&sp->u.p.pts[1], (sp->u.p.cnt-1) * sizeof (pts_t)); + last = xx->segs[0].u.p.pts[sp->u.p.cnt-1].pt; + j +=sp->u.p.cnt-1; + first = FALSE; + } + ASSERT(j<=cnt); + + } + xx->segs[0].u.p.cnt = j; + + if (IsClose(FindDistance(xx->segs[0].u.p.pts[0].pt,xx->segs[0].u.p.pts[xx->segs[0].u.p.cnt-1].pt))) { + xx->segs[0].u.p.polyType = FREEFORM; + xx->segs[0].u.p.cnt = xx->segs[0].u.p.cnt-1; + } + + ComputeDrawBoundingBox( trk ); + return trk; +} + + +static dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) + +void static CreateOriginAnchor(coOrd origin, wBool_t trans_selected) { + double d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,2); + int i = anchors_da.cnt-1; + coOrd p0,p1; + Translate(&p0,origin,0,d*4); + Translate(&p1,origin,0,-d*4); + anchors(i).type = SEG_STRLIN; + anchors(i).u.l.pos[0] = p0; + anchors(i).u.l.pos[1] = p1; + anchors(i).color = wDrawColorBlue; + anchors(i).width = 0; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + Translate(&p0,origin,90,d*4); + Translate(&p1,origin,90,-d*4); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).u.l.pos[0] = p0; + anchors(i).u.l.pos[1] = p1; + anchors(i).color = wDrawColorBlue; + anchors(i).width = 0; +} + +EXPORT void DrawOriginAnchor(track_p trk) { + if (!trk || GetTrkType(trk) != T_DRAW) return; + struct extraData * xx = GetTrkExtraData(trk); + if ((xx->orig.x != 0.0) || (xx->orig.y !=0.0) ) { + DYNARR_RESET(trkSeg_t,anchors_da); + CreateOriginAnchor(xx->orig,FALSE); + DrawSegs(&tempD, zero, 0.0, anchors_da.ptr, anchors_da.cnt, trackGauge, wDrawColorBlue); + } +} @@ -204,15 +480,27 @@ static DIST_T DistanceDraw( track_p t, coOrd * p ) static struct { - coOrd endPt[2]; + coOrd endPt[4]; + coOrd origin; + coOrd oldOrigin; + coOrd oldE0; + coOrd oldE1; FLOAT_T length; + FLOAT_T height; + FLOAT_T width; coOrd center; DIST_T radius; ANGLE_T angle0; ANGLE_T angle1; ANGLE_T angle; + ANGLE_T rotate_angle; + ANGLE_T oldAngle; long pointCount; long lineWidth; + BOOL_T boxed; + BOOL_T filled; + BOOL_T open; + BOOL_T lock_origin; wDrawColor color; wIndex_t benchChoice; wIndex_t benchOrient; @@ -221,21 +509,31 @@ static struct { wIndex_t fontSizeInx; char text[STR_LONG_SIZE]; unsigned int layer; - char polyType[STR_SIZE]; + wIndex_t lineType; } drawData; -typedef enum { E0, E1, CE, RA, LN, AL, A1, A2, VC, LW, CO, BE, OR, DS, TP, TA, TS, TX, PV, LY, PT } drawDesc_e; +typedef enum { E0, E1, PP, CE, AL, A1, A2, RD, LN, HT, WT, LK, OI, RA, VC, LW, LT, CO, FL, OP, BX, BE, OR, DS, TP, TA, TS, TX, PV, LY } drawDesc_e; static descData_t drawDesc[] = { /*E0*/ { DESC_POS, N_("End Pt 1: X,Y"), &drawData.endPt[0] }, /*E1*/ { DESC_POS, N_("End Pt 2: X,Y"), &drawData.endPt[1] }, +/*PP*/ { DESC_POS, N_("First Point: X,Y"), &drawData.endPt[0] }, /*CE*/ { DESC_POS, N_("Center: X,Y"), &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 }, +/*RD*/ { DESC_DIM, N_("Radius"), &drawData.radius }, +/*LN*/ { DESC_DIM, N_("Length"), &drawData.length }, +/*HT*/ { DESC_DIM, N_("Height"), &drawData.height }, +/*WT*/ { DESC_DIM, N_("Width"), &drawData.width }, +/*LK*/ { DESC_BOXED, N_("Keep Origin Relative"), &drawData.lock_origin}, +/*OI*/ { DESC_POS, N_("Rot Origin: X,Y"), &drawData.origin }, +/*RA*/ { DESC_FLOAT, N_("Rotate Angle"), &drawData.angle }, /*VC*/ { DESC_LONG, N_("Point Count"), &drawData.pointCount }, /*LW*/ { DESC_LONG, N_("Line Width"), &drawData.lineWidth }, +/*LT*/ { DESC_LIST, N_("Line Type"), &drawData.lineType }, /*CO*/ { DESC_COLOR, N_("Color"), &drawData.color }, +/*FL*/ { DESC_BOXED, N_("Filled"), &drawData.filled }, +/*OP*/ { DESC_BOXED, N_("Open End"), &drawData.open }, +/*BX*/ { DESC_BOXED, N_("Boxed"), &drawData.boxed }, /*BE*/ { DESC_LIST, N_("Lumber"), &drawData.benchChoice }, /*OR*/ { DESC_LIST, N_("Orientation"), &drawData.benchOrient }, /*DS*/ { DESC_LIST, N_("Size"), &drawData.dimenSize }, @@ -245,9 +543,8 @@ static descData_t drawDesc[] = { /*TX*/ { DESC_TEXT, N_("Text"), &drawData.text }, /*PV*/ { DESC_PIVOT, N_("Pivot"), &drawData.pivot }, /*LY*/ { DESC_LAYER, N_("Layer"), &drawData.layer }, -/*PT*/ { DESC_STRING, N_("Type"), &drawData.polyType }, { DESC_NULL } }; -int drawSegInx; +static int drawSegInx; #define UNREORIGIN( Q, P, A, O ) { \ (Q) = (P); \ @@ -257,6 +554,23 @@ int drawSegInx; Rotate( &(Q), zero, -(A) ); \ } +/* + * Notes - + * + * In V5.1, Origin was always {0,0} and Angle 0.0 after editing a Draw object. + * This did not allow for the use of the objects in other contexts (such as Signal Arms). + * + * In V5.2 - + * + * OI - Origin will be adjusted if it is locked to remain relative to the end point - this equally applies when moving the object points. + * If not locked, the object points will be set relative to the new origin value, + * so that the object remains at the same place as the user specifies. + * If the edit starts with origin {0,0}, it will be set unlocked, otherwise set locked. + * + * AL- Angle will be set to 0.0 when the object is modified. The points of the objects will be rotated so that + * rotated and adjusted so they don't need rotation to lie where the user left them. + * + */ static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final ) { struct extraData *xx = GetTrkExtraData(trk); @@ -271,9 +585,9 @@ static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final ) if (segPtr->type != SEG_TEXT) return; else inx = TX; //Always look at TextField for SEG_TEXT on "Done" } - MainRedraw(); - MapRedraw(); UndrawNewTrack( trk ); + coOrd pt; + coOrd off; switch ( inx ) { case LW: segPtr->width = drawData.lineWidth/mainD.dpi; @@ -284,37 +598,251 @@ static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final ) 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 ); + coOrd off; + off.x = drawData.endPt[0].x - drawData.oldE0.x; + off.y = drawData.endPt[0].y - drawData.oldE0.y; + if (drawData.lock_origin) { + xx->orig.x +=off.x; + xx->orig.y +=off.y; + drawDesc[OI].mode |= DESC_CHANGE; + } else { + switch(segPtr->type) { //E0 does not alter length - translates + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + UNREORIGIN( segPtr->u.l.pos[0], drawData.endPt[0], xx->angle, xx->orig ); + drawData.endPt[1].x = off.x+drawData.endPt[1].x; + drawData.endPt[1].y = off.y+drawData.endPt[1].y; + UNREORIGIN( segPtr->u.l.pos[1], drawData.endPt[1], xx->angle, xx->orig ); + drawDesc[E1].mode |= DESC_CHANGE; + break; + case SEG_CRVLIN: + case SEG_FILCRCL: + UNREORIGIN( segPtr->u.c.center, drawData.endPt[0], xx->angle, xx->orig ); + break; + case SEG_TEXT: + UNREORIGIN( segPtr->u.t.pos, drawData.endPt[0], xx->angle, xx->orig ); + break; + case SEG_POLY: + case SEG_FILPOLY: + break; //Note not used by POLYGONS + default:; + } + } + } else { //E1 - alters length + off.x = drawData.endPt[1].x - drawData.oldE1.x; + off.y = drawData.endPt[1].y - drawData.oldE1.y; + drawDesc[E1].mode |= DESC_CHANGE; + if (drawData.lock_origin) { + xx->orig.x +=off.x; + xx->orig.y +=off.y; + drawDesc[OI].mode |= DESC_CHANGE; + } 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; + case OI: + off.x = drawData.origin.x - drawData.oldOrigin.x; + off.y = drawData.origin.y - drawData.oldOrigin.y; + xx->orig = drawData.origin; + if (!drawData.lock_origin) { + switch(segPtr->type) { + case SEG_POLY: + case SEG_FILPOLY: + for (int i=0;i<segPtr->u.p.cnt;i++) { + REORIGIN( pt, segPtr->u.p.pts[i].pt, xx->angle, drawData.oldOrigin); + UNREORIGIN( segPtr->u.p.pts[i].pt, pt, xx->angle, xx->orig ); + } break; + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + for (int i=0;i<2;i++) { + UNREORIGIN( segPtr->u.l.pos[i], drawData.endPt[i], xx->angle, xx->orig ); + } + break; + case SEG_CRVLIN: + case SEG_FILCRCL: + UNREORIGIN( segPtr->u.c.center, drawData.center, xx->angle, xx->orig ); + break; + case SEG_TEXT: + UNREORIGIN( segPtr->u.t.pos, drawData.endPt[0], xx->angle, xx->orig ); + break; + default:; } } else { - if ( drawData.length <= minLength ) { - ErrorMessage( MSG_OBJECT_TOO_SHORT ); - if ( segPtr->type != SEG_CRVLIN ) { - drawData.length = FindDistance( drawData.endPt[0], drawData.endPt[1] ); + drawData.endPt[0].x += off.x; + drawData.endPt[0].y += off.y; + switch(segPtr->type) { + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + drawDesc[E0].mode |= DESC_CHANGE; + UNREORIGIN( segPtr->u.l.pos[0], drawData.endPt[0], xx->angle, xx->orig ); + drawData.endPt[1].x = off.x+drawData.endPt[1].x; + drawData.endPt[1].y = off.y+drawData.endPt[1].y; + UNREORIGIN( segPtr->u.l.pos[1], drawData.endPt[1], xx->angle, xx->orig ); + drawDesc[E1].mode |= DESC_CHANGE; + break; + case SEG_CRVLIN: + case SEG_FILCRCL: + UNREORIGIN( segPtr->u.c.center, drawData.endPt[0], xx->angle, xx->orig ); + drawDesc[E0].mode |= DESC_CHANGE; + break; + case SEG_TEXT: + UNREORIGIN( segPtr->u.t.pos, drawData.endPt[0], xx->angle, xx->orig ); + drawDesc[E0].mode |= DESC_CHANGE; + break; + case SEG_POLY: + case SEG_FILPOLY: + for (int i=0;i<segPtr->u.p.cnt;i++) { + REORIGIN( pt, segPtr->u.p.pts[i].pt, xx->angle, drawData.oldOrigin); + pt.x += off.x; + pt.y += off.y; + UNREORIGIN( segPtr->u.p.pts[i].pt, pt, xx->angle, xx->orig ); + } + drawDesc[PP].mode |= DESC_CHANGE; + break; + default:; + } + } + break; + case HT: + case WT: + if ((segPtr->type == SEG_POLY) || (segPtr->type == SEG_FILPOLY)) { + if (segPtr->u.p.polyType == RECTANGLE) { + if (inx == HT) { + ANGLE_T angle = NormalizeAngle(FindAngle(drawData.endPt[0],drawData.endPt[3])); + Translate( &drawData.endPt[3], drawData.endPt[0], angle, drawData.height); + UNREORIGIN( segPtr->u.p.pts[3].pt, drawData.endPt[3], xx->angle, xx->orig ); + Translate( &drawData.endPt[2], drawData.endPt[1], angle, drawData.height); + UNREORIGIN( segPtr->u.p.pts[2].pt, drawData.endPt[2], xx->angle, xx->orig ); } else { - drawData.length = fabs(segPtr->u.c.radius)*2*M_PI*segPtr->u.c.a1/360.0; + ANGLE_T angle = NormalizeAngle(FindAngle(drawData.endPt[0],drawData.endPt[1]));; + Translate( &drawData.endPt[1], drawData.endPt[0], angle, drawData.width); + UNREORIGIN( segPtr->u.p.pts[1].pt, drawData.endPt[1], xx->angle, xx->orig ); + Translate( &drawData.endPt[2], drawData.endPt[3], angle, drawData.width); + UNREORIGIN( segPtr->u.p.pts[2].pt, drawData.endPt[2], xx->angle, xx->orig ); } - drawDesc[LN].mode |= DESC_CHANGE; + drawDesc[E0].mode |= DESC_CHANGE; + } + } + break; + case RA:; + ANGLE_T angle = NormalizeAngle(drawData.rotate_angle); + switch(segPtr->type) { + case SEG_POLY: + case SEG_FILPOLY: + for (int i=0;i<segPtr->u.p.cnt;i++) { + REORIGIN(pt,segPtr->u.p.pts[i].pt, angle, xx->orig); + if (i == 0) drawData.endPt[0] = pt; + UNREORIGIN(segPtr->u.p.pts[i].pt, pt, 0.0, xx->orig); + } + drawDesc[PP].mode |= DESC_CHANGE; break; + case SEG_CRVLIN:; + coOrd end0, end1; + Translate(&end0,segPtr->u.c.center,segPtr->u.c.a0,segPtr->u.c.radius); + Translate(&end1,segPtr->u.c.center,segPtr->u.c.a0+segPtr->u.c.a1,segPtr->u.c.radius); + REORIGIN(end0, end0, angle, xx->orig ); + REORIGIN(end1, end1, angle, xx->orig ); + REORIGIN( drawData.center,segPtr->u.c.center, angle, xx->orig ); + drawData.angle0 = FindAngle( drawData.center, end0); + drawData.angle1 = FindAngle( drawData.center, end1); + drawDesc[CE].mode |= DESC_CHANGE; + drawDesc[A1].mode |= DESC_CHANGE; + drawDesc[A2].mode |= DESC_CHANGE; + /*no break*/ + case SEG_FILCRCL: + REORIGIN( drawData.center,segPtr->u.c.center, angle, xx->orig ); + UNREORIGIN( segPtr->u.c.center, drawData.center, 0.0, xx->orig); //Remove angle + drawDesc[CE].mode |= DESC_CHANGE; + break; + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + for (int i=0;i<2;i++) { + REORIGIN( drawData.endPt[i], segPtr->u.l.pos[i], angle, xx->orig ); + UNREORIGIN(segPtr->u.l.pos[i], drawData.endPt[i], 0.0, xx->orig ); + } + drawDesc[E0].mode |= DESC_CHANGE; + drawDesc[E1].mode |= DESC_CHANGE; + break; + case SEG_TEXT: + + break; + default:; + } + xx->angle = drawData.rotate_angle = 0.0; + drawDesc[RA].mode |= DESC_CHANGE; + break; + case AL:; + angle = NormalizeAngle(drawData.angle); + switch(segPtr->type) { + case SEG_POLY: + case SEG_FILPOLY: + break; //Doesn't Use + case SEG_CRVLIN: + switch ( drawData.pivot ) { + case DESC_PIVOT_FIRST: + segPtr->u.c.a1 = drawData.angle; + drawData.angle1 = NormalizeAngle( drawData.angle0+segPtr->u.c.a1 ); + drawDesc[A2].mode |= DESC_CHANGE; + break; + case DESC_PIVOT_SECOND: + segPtr->u.c.a0 = NormalizeAngle( segPtr->u.c.a1+segPtr->u.c.a0-drawData.angle); + 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 DESC_PIVOT_MID: + 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; + default: + break; + } + break; + case SEG_FILCRCL: + break; //Doesn't Use + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + Translate(&drawData.endPt[1],drawData.endPt[0],angle,drawData.length); + UNREORIGIN(segPtr->u.l.pos[1], drawData.endPt[1], xx->angle, xx->orig ); + drawDesc[E1].mode |= DESC_CHANGE; + break; + case SEG_TEXT: + break; //Doesnt Use + default:; + } + break; + case LN: + 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 = fabs(segPtr->u.c.radius)*2*M_PI*segPtr->u.c.a1/360.0; } + drawDesc[LN].mode |= DESC_CHANGE; + break; } - if ( segPtr->type != SEG_CRVLIN ) { + if ( segPtr->type != SEG_CRVLIN ) { switch ( drawData.pivot ) { case DESC_PIVOT_FIRST: Translate( &drawData.endPt[1], drawData.endPt[0], drawData.angle, drawData.length ); @@ -340,6 +868,7 @@ static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final ) break; } } else { + if ( drawData.angle < 0.0 || drawData.angle >= 360.0 ) { ErrorMessage( MSG_CURVE_OUT_OF_RANGE ); drawData.angle = segPtr->u.c.a1; @@ -357,13 +886,44 @@ static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final ) case CE: UNREORIGIN( segPtr->u.c.center, drawData.center, xx->angle, xx->orig ); break; - case RA: + case RD: + if ( drawData.pivot == DESC_PIVOT_FIRST ) { + Translate( &segPtr->u.c.center, segPtr->u.c.center, segPtr->u.c.a0, segPtr->u.c.radius-drawData.radius ); + } else if ( drawData.pivot == DESC_PIVOT_SECOND ) { + Translate( &segPtr->u.c.center, segPtr->u.c.center, segPtr->u.c.a0+segPtr->u.c.a1, segPtr->u.c.radius-drawData.radius ); + } else { + Translate( &segPtr->u.c.center, segPtr->u.c.center, (segPtr->u.c.a0+segPtr->u.c.a1)/2.0, segPtr->u.c.radius-drawData.radius ); + } + drawDesc[CE].mode |= DESC_CHANGE; segPtr->u.c.radius = drawData.radius; + drawDesc[LN].mode |= DESC_CHANGE; break; case A1: - segPtr->u.c.a0 = NormalizeAngle( drawData.angle0-xx->angle ); - drawData.angle1 = NormalizeAngle( drawData.angle0+drawData.angle ); - drawDesc[A2].mode |= DESC_CHANGE; + switch ( drawData.pivot ) { + case DESC_PIVOT_FIRST: + segPtr->u.c.a1 = drawData.angle; + drawData.angle1 = NormalizeAngle( drawData.angle0+segPtr->u.c.a1 ); + drawDesc[A2].mode |= DESC_CHANGE; + break; + case DESC_PIVOT_SECOND: + segPtr->u.c.a0 = NormalizeAngle( segPtr->u.c.a1+segPtr->u.c.a0-drawData.angle); + 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 DESC_PIVOT_MID: + 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; + default: + break; + } break; case A2: segPtr->u.c.a0 = NormalizeAngle( drawData.angle1-segPtr->u.c.a1-xx->angle ); @@ -387,6 +947,28 @@ static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final ) case TP: UNREORIGIN( segPtr->u.t.pos, drawData.endPt[0], xx->angle, xx->orig ); break; + case PP: + off.x = drawData.endPt[0].x - drawData.oldE0.x; + off.y = drawData.endPt[0].y - drawData.oldE0.y; + if (drawData.lock_origin) { + xx->orig.x +=off.x; + xx->orig.y +=off.y; + drawData.origin = xx->orig; + drawDesc[OI].mode |= DESC_CHANGE; + drawDesc[E0].mode |= DESC_CHANGE; + break; + } else { + for (int i=0;i<segPtr->u.p.cnt;i++) { + REORIGIN( pt, segPtr->u.p.pts[i].pt, xx->angle, xx->orig ); + pt.x += off.x; + pt.y += off.y; + if (i<5) drawData.endPt[i] = pt; + UNREORIGIN( segPtr->u.p.pts[i].pt, pt, 0.0, xx->orig ); + } + xx->angle = 0.0; + drawDesc[AL].mode |= DESC_CHANGE; + } + break; case TA: //segPtr->u.t.angle = NormalizeAngle( drawData.angle ); xx->angle = NormalizeAngle( drawData.angle ); @@ -396,6 +978,39 @@ static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final ) UpdateFontSizeList( &fontSize, (wList_p)drawDesc[TS].control0, drawData.fontSizeInx ); segPtr->u.t.fontSize = fontSize; break; + case FL: + if (segPtr->type == SEG_POLY && drawData.open) { + drawData.filled = FALSE; + drawDesc[FL].mode |= DESC_CHANGE; + break; + } + if(drawData.filled) { + if (segPtr->type == SEG_POLY) segPtr->type = SEG_FILPOLY; + if (segPtr->type == SEG_CRVLIN) segPtr->type = SEG_FILCRCL; + } else { + if (segPtr->type == SEG_FILPOLY) segPtr->type = SEG_POLY; + if (segPtr->type == SEG_FILCRCL) { + segPtr->type = SEG_CRVLIN; + segPtr->u.c.a0 = 0.0; + segPtr->u.c.a1 = 360.0; + } + } + break; + case OP: + if (drawData.filled || (segPtr->type != SEG_POLY)) { + drawData.open = FALSE; + drawDesc[OP].mode |= DESC_CHANGE; + break; + } + if (drawData.open) { + if (segPtr->type == SEG_POLY && segPtr->u.p.polyType == FREEFORM) segPtr->u.p.polyType = POLYLINE; + } else { + if (segPtr->type == SEG_POLY && segPtr->u.p.polyType == POLYLINE) segPtr->u.p.polyType = FREEFORM; + } + break; + case BX: + segPtr->u.t.boxed = drawData.boxed; + break; case TX: if ( wTextGetModified((wText_p)drawDesc[TX].control0 )) { int len = wTextGetSize((wText_p)drawDesc[TX].control0); @@ -409,14 +1024,25 @@ static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final ) case LY: SetTrkLayer( trk, drawData.layer); break; + case LK: + break; + case LT: + xx->lineType = drawData.lineType; + break; default: AbortProg( "bad op" ); } + drawData.oldE0 = drawData.endPt[0]; + drawData.oldE1 = drawData.endPt[1]; + drawData.oldAngle = drawData.angle; + drawData.oldOrigin = drawData.origin; ComputeDrawBoundingBox( trk ); DrawNewTrack( trk ); - DoCurCommand( C_REDRAW, zero ); + TempRedraw(); // UpdateDraw } +extern BOOL_T inDescribeCmd; + static void DescribeDraw( track_p trk, char * str, CSIZE_T len ) { struct extraData *xx = GetTrkExtraData(trk); @@ -443,8 +1069,17 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len ) drawDesc[LY].mode = DESC_NOREDRAW; drawDesc[BE].mode = drawDesc[OR].mode = + drawDesc[LT].mode = drawDesc[DS].mode = DESC_IGNORE; drawData.pivot = DESC_PIVOT_MID; + + if ((xx->orig.x == 0.0) && (xx->orig.y == 0.0)) drawData.lock_origin = FALSE; + else drawData.lock_origin = TRUE; + + drawData.rotate_angle = xx->angle; + + drawDesc[LK].mode = 0; + switch ( segPtr->type ) { case SEG_STRLIN: case SEG_DIMLIN: @@ -454,24 +1089,35 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len ) 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] ); + drawData.origin = xx->orig; drawDesc[LN].mode = drawDesc[AL].mode = drawDesc[PV].mode = 0; drawDesc[E0].mode = + drawDesc[OI].mode = 0; drawDesc[E1].mode = 0; + drawDesc[RA].mode = 0; switch (segPtr->type) { case SEG_STRLIN: title = _("Straight Line"); + drawDesc[LT].mode = 0; + drawData.lineType = (wIndex_t)xx->lineType; break; case SEG_DIMLIN: title = _("Dimension Line"); - drawDesc[CO].mode = DESC_IGNORE; - drawDesc[LW].mode = DESC_IGNORE; + drawDesc[CO].mode = + drawDesc[LW].mode = + drawDesc[LK].mode = + drawDesc[OI].mode = + drawDesc[RA].mode = DESC_IGNORE; drawData.dimenSize = (wIndex_t)segPtr->u.l.option; drawDesc[DS].mode = 0; break; case SEG_BENCH: title = _("Lumber"); + drawDesc[LK].mode = + drawDesc[OI].mode = + drawDesc[RA].mode = drawDesc[LW].mode = DESC_IGNORE; drawDesc[BE].mode = drawDesc[OR].mode = 0; @@ -480,6 +1126,9 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len ) break; case SEG_TBLEDGE: title = _("Table Edge"); + drawDesc[LK].mode = + drawDesc[OI].mode = + drawDesc[RA].mode = DESC_IGNORE; drawDesc[CO].mode = DESC_IGNORE; drawDesc[LW].mode = DESC_IGNORE; break; @@ -488,10 +1137,17 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len ) case SEG_CRVLIN: REORIGIN( drawData.center, segPtr->u.c.center, xx->angle, xx->orig ); drawData.radius = fabs(segPtr->u.c.radius); + drawData.origin = xx->orig; + drawDesc[OI].mode = 0; + drawDesc[RA].mode = drawDesc[CE].mode = - drawDesc[RA].mode = 0; + drawDesc[RD].mode = 0; + drawDesc[LT].mode = 0; + drawData.lineType = (wIndex_t)xx->lineType; if ( segPtr->u.c.a1 >= 360.0 ) { title = _("Circle"); + drawDesc[FL].mode = 0; + drawData.filled = FALSE; } else { drawData.angle = segPtr->u.c.a1; drawData.angle0 = NormalizeAngle( segPtr->u.c.a0+xx->angle ); @@ -499,64 +1155,127 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len ) drawDesc[AL].mode = drawDesc[A1].mode = drawDesc[A2].mode = 0; + drawDesc[PV].mode = 0; title = _("Curved Line"); } break; case SEG_FILCRCL: REORIGIN( drawData.center, segPtr->u.c.center, xx->angle, xx->orig ); drawData.radius = fabs(segPtr->u.c.radius); + drawData.origin = xx->orig; + drawDesc[OI].mode = + drawDesc[RA].mode = + drawDesc[FL].mode = 0; + drawData.filled = TRUE; drawDesc[CE].mode = - drawDesc[RA].mode = 0; + drawDesc[RD].mode = 0; + drawDesc[PV].mode = 0; + drawDesc[OI].mode = 0; drawDesc[LW].mode = DESC_IGNORE; title = _("Filled Circle"); break; case SEG_POLY: + REORIGIN(drawData.endPt[0],segPtr->u.p.pts[0].pt, xx->angle, xx->orig); + drawDesc[PP].mode = 0; drawData.pointCount = segPtr->u.p.cnt; drawDesc[VC].mode = DESC_RO; - drawDesc[PT].mode = DESC_RO; + drawData.filled = FALSE; + drawDesc[FL].mode = 0; + drawData.angle = 0.0; + drawDesc[RA].mode = 0; + drawData.origin = xx->orig; + drawDesc[OI].mode = 0; + drawData.open=FALSE; + drawDesc[OP].mode = 0; + drawDesc[LT].mode = 0; + drawData.lineType = (wIndex_t)xx->lineType; switch (segPtr->u.p.polyType) { case RECTANGLE: - polyType = _("Rectangle"); + title = _("Rectangle"); + drawDesc[OP].mode = DESC_IGNORE; + drawDesc[VC].mode = DESC_IGNORE; + drawData.width = FindDistance(segPtr->u.p.pts[0].pt, segPtr->u.p.pts[1].pt); + drawDesc[WT].mode = 0; + drawData.height = FindDistance(segPtr->u.p.pts[0].pt, segPtr->u.p.pts[3].pt); + drawDesc[HT].mode = 0; + for(int i=0;i<4;i++) { + REORIGIN( drawData.endPt[i], segPtr->u.p.pts[i].pt, xx->angle, xx->orig ); + } + drawDesc[E0].mode = DESC_IGNORE; + drawData.origin = xx->orig; + break; + case POLYLINE: + title = _("Polyline"); + drawData.open=TRUE; break; default: - polyType = _("Freeform"); + title = _("Polygon"); } - strncpy( drawData.polyType, polyType, sizeof drawData.polyType ); - title = _("Polygonal Line"); break; case SEG_FILPOLY: + REORIGIN(drawData.endPt[0],segPtr->u.p.pts[0].pt, xx->angle, xx->orig); + drawDesc[PP].mode = 0; drawData.pointCount = segPtr->u.p.cnt; drawDesc[VC].mode = DESC_RO; + drawData.filled = TRUE; + drawDesc[FL].mode = 0; drawDesc[LW].mode = DESC_IGNORE; - drawDesc[PT].mode = DESC_RO; + drawData.angle = xx->angle; + drawDesc[RA].mode = 0; + drawData.origin = xx->orig; + drawDesc[OI].mode = DESC_RO; + drawData.open = FALSE; switch (segPtr->u.p.polyType) { case RECTANGLE: - polyType =_("Rectangle"); + title =_("Filled Rectangle"); + drawDesc[VC].mode = DESC_IGNORE; + drawData.width = FindDistance(segPtr->u.p.pts[0].pt, segPtr->u.p.pts[1].pt); + drawDesc[WT].mode = 0; + drawData.height = FindDistance(segPtr->u.p.pts[0].pt, segPtr->u.p.pts[3].pt); + drawDesc[HT].mode = 0; + for(int i=0;i<4;i++) { + REORIGIN( drawData.endPt[i], segPtr->u.p.pts[i].pt, xx->angle, xx->orig ); + } + drawDesc[E0].mode = DESC_IGNORE; + drawData.origin = xx->orig; break; default: - polyType = _("Freeform"); + title = _("Filled Polygon"); } - strncpy( drawData.polyType, polyType, sizeof drawData.polyType ); - title = _("Polygon"); break; case SEG_TEXT: REORIGIN( drawData.endPt[0], segPtr->u.t.pos, xx->angle, xx->orig ); drawData.angle = NormalizeAngle( xx->angle ); strncpy( drawData.text, segPtr->u.t.string, sizeof drawData.text ); drawData.text[sizeof drawData.text-1] ='\0'; + drawData.boxed = segPtr->u.t.boxed; + drawData.origin = xx->orig; + drawDesc[E0].mode = drawDesc[TP].mode = drawDesc[TS].mode = drawDesc[TX].mode = - drawDesc[TA].mode = + drawDesc[TA].mode = + drawDesc[BX].mode = + drawDesc[RA].mode = + drawDesc[OI].mode = 0; 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 ); + snprintf( str, len, _("%s(%d) Layer=%d"), title, GetTrkIndex(trk), GetTrkLayer(trk)+1 ); + + if (!inDescribeCmd) return; + + drawData.oldE0 = drawData.endPt[0]; + drawData.oldE1 = drawData.endPt[1]; + drawData.oldAngle = drawData.angle; + drawData.oldOrigin = drawData.origin; + + DoDescribe( title, trk, drawDesc, UpdateDraw ); if ( segPtr->type==SEG_BENCH && drawDesc[BE].control0!=NULL && drawDesc[OR].control0!=NULL) { @@ -565,6 +1284,17 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len ) 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_STRLIN || segPtr->type==SEG_CRVLIN || segPtr->type==SEG_POLY) && drawDesc[LT].control0!=NULL) { + wListClear( (wList_p)drawDesc[LT].control0 ); + wListAddValue( (wList_p)drawDesc[LT].control0, _("Solid"), NULL, (void*)0 ); + wListAddValue( (wList_p)drawDesc[LT].control0, _("Dash"), NULL, (void*)1 ); + wListAddValue( (wList_p)drawDesc[LT].control0, _("Dot"), NULL, (void*)2 ); + wListAddValue( (wList_p)drawDesc[LT].control0, _("DashDot"), NULL, (void*)3 ); + wListAddValue( (wList_p)drawDesc[LT].control0, _("DashDotDot"), NULL, (void*)4 ); + wListAddValue( (wList_p)drawDesc[LT].control0, _("CenterDot"), NULL, (void*)5 ); + wListAddValue( (wList_p)drawDesc[LT].control0, _("PhantomDot"), NULL, (void*)6 ); + wListSetIndex( (wList_p)drawDesc[LT].control0, drawData.lineType ); + } 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 ); @@ -582,8 +1312,17 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len ) static void DrawDraw( track_p t, drawCmd_p d, wDrawColor color ) { struct extraData * xx = GetTrkExtraData(t); - if ( (d->funcs->options&DC_QUICK) == 0 ) - DrawSegs( d, xx->orig, xx->angle, xx->segs, xx->segCnt, 0.0, color ); + unsigned long NotSolid = ~(DC_NOTSOLIDLINE); + d->options &= NotSolid; + if (xx->lineType == DRAWLINESOLID) {} + else if (xx->lineType == DRAWLINEDASH) d->options |= DC_DASH; + else if (xx->lineType == DRAWLINEDOT) d->options |= DC_DOT; + else if (xx->lineType == DRAWLINEDASHDOT) d->options |= DC_DASHDOT; + else if (xx->lineType == DRAWLINEDASHDOTDOT) d->options |= DC_DASHDOTDOT; + else if (xx->lineType == DRAWLINECENTER) d->options |= DC_CENTER; + else if (xx->lineType == DRAWLINEPHANTOM) d->options |= DC_PHANTOM; + DrawSegs( d, xx->orig, xx->angle, xx->segs, xx->segCnt, 0.0, color ); + d->options = d->options&~(DC_NOTSOLIDLINE); } @@ -594,6 +1333,7 @@ static void DeleteDraw( track_p t ) if (xx->segs[0].type == SEG_POLY || xx->segs[0].type == SEG_FILPOLY) { MyFree(xx->segs[0].u.p.pts); + xx->segs[0].u.p.pts = NULL; } } @@ -602,14 +1342,15 @@ 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), + rc &= fprintf(f, "DRAW %d %d %d 0 0 %0.6f %0.6f 0 %0.6f\n", GetTrkIndex(t), GetTrkLayer(t), + xx->lineType, xx->orig.x, xx->orig.y, xx->angle )>0; rc &= WriteSegs( f, xx->segCnt, xx->segs ); return rc; } -static void ReadDraw( char * header ) +static BOOL_T ReadDraw( char * header ) { track_p trk; wIndex_t index; @@ -617,12 +1358,14 @@ static void ReadDraw( char * header ) DIST_T elev; ANGLE_T angle; wIndex_t layer; + int lineType; struct extraData * xx; - if ( !GetArgs( header+5, paramVersion<3?"dXpYf":paramVersion<9?"dL000pYf":"dL000pff", - &index, &layer, &orig, &elev, &angle ) ) - return; - ReadSegs(); + if ( !GetArgs( header+5, paramVersion<3?"dXXpYf":paramVersion<9?"dLX00pYf":"dLd00pff", + &index, &layer, &lineType, &orig, &elev, &angle ) ) + return FALSE; + if ( !ReadSegs() ) + return FALSE; if (tempSegs_da.cnt == 1) { trk = MakeDrawFromSeg1( index, orig, angle, &tempSegs(0) ); SetTrkLayer( trk, layer ); @@ -633,9 +1376,11 @@ static void ReadDraw( char * header ) xx->orig = orig; xx->angle = angle; xx->segCnt = tempSegs_da.cnt; + xx->lineType = lineType; memcpy( xx->segs, tempSegs_da.ptr, tempSegs_da.cnt * sizeof *(trkSeg_p)0 ); ComputeDrawBoundingBox( trk ); } + return TRUE; } @@ -665,22 +1410,283 @@ static void RescaleDraw( track_p trk, FLOAT_T ratio ) RescaleSegs( xx->segCnt, xx->segs, ratio, ratio, ratio ); } +static void DoConvertFill(void) { + +} + +static drawModContext_t drawModCmdContext = { + InfoMessage, + DoRedraw, + &mainD}; + + +static BOOL_T infoSubst = FALSE; + +static paramIntegerRange_t i0_100 = { 0, 100, 25 }; +static paramFloatRange_t r1_10000 = { 1, 10000 }; +static paramFloatRange_t r0_10000 = { 0, 10000 }; +static paramFloatRange_t r10000_10000 = {-10000, 10000}; +static paramFloatRange_t r360_360 = { -360, 360, 80 }; +static paramFloatRange_t r0_360 = { 0, 360, 80 }; +static paramData_t drawModPLs[] = { + +#define drawModLengthPD (drawModPLs[0]) + { PD_FLOAT, &drawModCmdContext.length, "Length", PDO_DIM|PDO_NORECORD|BO_ENTER, &r0_10000, N_("Length") }, +#define drawModAnglePD (drawModPLs[1]) + { PD_FLOAT, &drawModCmdContext.abs_angle, "Angle", PDO_NORECORD|BO_ENTER, &r360_360, N_("Angle") }, +#define drawModRelAnglePD (drawModPLs[2]) +#define drawModRelAngle 2 + { PD_FLOAT, &drawModCmdContext.rel_angle, "Rel Angle", PDO_NORECORD|BO_ENTER, &r360_360, N_("Relative Angle") }, +#define drawModWidthPD (drawModPLs[3]) + { PD_FLOAT, &drawModCmdContext.width, "Width", PDO_DIM|PDO_NORECORD|BO_ENTER, &r0_10000, N_("Width") }, +#define drawModHeightPD (drawModPLs[4]) + { PD_FLOAT, &drawModCmdContext.height, "Height", PDO_DIM|PDO_NORECORD|BO_ENTER, &r0_10000, N_("Height") }, +#define drawModRadiusPD (drawModPLs[5]) +#define drawModRadius 5 + { PD_FLOAT, &drawModCmdContext.radius, "Radius", PDO_DIM|PDO_NORECORD|BO_ENTER, &r10000_10000, N_("Radius") }, +#define drawModArcAnglePD (drawModPLs[6]) + { PD_FLOAT, &drawModCmdContext.arc_angle, "ArcAngle", PDO_NORECORD|BO_ENTER, &r360_360, N_("Arc Angle") }, +#define drawModRotAnglePD (drawModPLs[7) + { PD_FLOAT, &drawModCmdContext.rot_angle, "Rot Angle", PDO_NORECORD|BO_ENTER, &r0_360, N_("Rotate Angle") }, +#define drawModRotCenterXPD (drawModPLs[8]) +#define drawModRotCenterInx 8 + { PD_FLOAT, &drawModCmdContext.rot_center.x, "Rot Center X,Y", PDO_NORECORD|BO_ENTER, &r0_10000, N_("Rot Center X") }, +#define drawModRotCenterYPD (drawModPLs[9]) + { PD_FLOAT, &drawModCmdContext.rot_center.y, " ", PDO_NORECORD|BO_ENTER, &r0_10000, N_("Rot Center Y") }, + +}; +static paramGroup_t drawModPG = { "drawMod", 0, drawModPLs, sizeof drawModPLs/sizeof drawModPLs[0] }; + +static void DrawModDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP ) +{ + DrawGeomModify(C_UPDATE,zero,&drawModCmdContext); + ParamLoadControl(&drawModPG,drawModRotCenterInx-1); //Make sure the angle is updated in case center moved + ParamLoadControl(&drawModPG,drawModRadius); // Make sure Radius updated + ParamLoadControl(&drawModPG,drawModRelAngle); //Relative Angle as well + MainRedraw(); + +} static STATUS_T ModifyDraw( track_p trk, wAction_t action, coOrd pos ) { struct extraData * xx = GetTrkExtraData(trk); - STATUS_T rc; + STATUS_T rc = C_CONTINUE; - if (action == C_DOWN) { - //UndrawNewTrack( trk ); - } - if ( action == C_MOVE ) + wControl_p controls[5]; //Always needs a NULL last entry + char * labels[4]; + + drawModCmdContext.trk = trk; + drawModCmdContext.orig = xx->orig; + drawModCmdContext.angle = xx->angle; + drawModCmdContext.segCnt = xx->segCnt; + drawModCmdContext.segPtr = xx->segs; + drawModCmdContext.selected = GetTrkSelected(trk); + + + switch(action&0xFF) { //Remove Text value + case C_START: + drawModCmdContext.type = xx->segs[0].type; + switch(drawModCmdContext.type) { + case SEG_POLY: + case SEG_FILPOLY: + drawModCmdContext.filled = (drawModCmdContext.type==SEG_FILPOLY)?TRUE:FALSE; + drawModCmdContext.subtype = xx->segs[0].u.p.polyType; + drawModCmdContext.open = (drawModCmdContext.subtype==POLYLINE)?TRUE:FALSE; + break; + case SEG_TEXT: + InfoMessage("Text can only be modified in Describe Mode"); + wBeep(); + return C_ERROR; + default: + break; + + } + drawModCmdContext.rot_moved = FALSE; + drawModCmdContext.rotate_state = FALSE; + + infoSubst = FALSE; + rc = DrawGeomModify( C_START, pos, &drawModCmdContext ); + break; + case C_DOWN: + rc = DrawGeomModify( C_DOWN, pos, &drawModCmdContext ); + if ( infoSubst ) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } + break; + case C_LDOUBLE: + rc = DrawGeomModify( C_LDOUBLE, pos, &drawModCmdContext ); + if ( infoSubst ) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } + break; + case wActionMove: + rc = DrawGeomModify( action, pos, &drawModCmdContext ); + break; + case C_REDRAW: + rc = DrawGeomModify( action, pos, &drawModCmdContext ); + break; + case C_MOVE: + ignoredDraw = trk; + rc = DrawGeomModify( action, pos, &drawModCmdContext ); + ignoredDraw = NULL; + break; + case C_UP: + ignoredDraw = trk; + rc = DrawGeomModify( action, pos, &drawModCmdContext ); + ignoredDraw = NULL; + ComputeDrawBoundingBox( trk ); + if (drawModCmdContext.state == MOD_AFTER_PT) { + switch(drawModCmdContext.type) { + case SEG_POLY: + case SEG_FILPOLY: + if (xx->segs[0].u.p.polyType != RECTANGLE) { + if (drawModCmdContext.prev_inx >= 0) { + controls[0] = drawModLengthPD.control; + controls[1] = drawModRelAnglePD.control; + controls[2] = NULL; + labels[0] = N_("Seg Lth"); + labels[1] = N_("Rel Ang"); + ParamLoadControls( &drawModPG ); + InfoSubstituteControls( controls, labels ); + drawModLengthPD.option &= ~PDO_NORECORD; + drawModRelAnglePD.option &= ~PDO_NORECORD; + infoSubst = TRUE; + } + } else { + controls[0] = drawModWidthPD.control; + controls[1] = drawModHeightPD.control; + controls[2] = NULL; + labels[0] = N_("Width"); + labels[1] = N_("Height"); + ParamLoadControls( &drawModPG ); + InfoSubstituteControls( controls, labels ); + drawModWidthPD.option &= ~PDO_NORECORD; + drawModHeightPD.option &= ~PDO_NORECORD; + infoSubst = TRUE; + } + break; + case SEG_STRLIN: + case SEG_BENCH: + case SEG_DIMLIN: + case SEG_TBLEDGE: + controls[0] = drawModLengthPD.control; + controls[1] = drawModAnglePD.control; + controls[2] = NULL; + labels[0] = N_("Length"); + labels[1] = N_("Angle"); + ParamLoadControls( &drawModPG ); + InfoSubstituteControls( controls, labels ); + drawModLengthPD.option &= ~PDO_NORECORD; + drawModAnglePD.option &= ~PDO_NORECORD; + infoSubst = TRUE; + break; + case SEG_CRVLIN: + case SEG_FILCRCL: + controls[0] = drawModRadiusPD.control; + controls[1] = NULL; + labels[0] = N_("Radius"); + if ((drawModCmdContext.type == SEG_CRVLIN) && xx->segs[0].u.c.a1>0.0 && xx->segs[0].u.c.a1 <360.0) { + controls[1] = drawModArcAnglePD.control; + controls[2] = NULL; + labels[1] = N_("Arc Angle"); + } + ParamLoadControls( &drawModPG ); + InfoSubstituteControls( controls, labels ); + drawModArcAnglePD.option &= ~PDO_NORECORD; + if (drawModCmdContext.type == SEG_CRVLIN) + drawModArcAnglePD.option &= ~PDO_NORECORD; + infoSubst = TRUE; + break; + default: + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + break; + } + } else { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } + break; + case C_CMDMENU: + menuPos = pos; + wMenuPopupShow( drawModDelMI ); + wMenuPushEnable( drawModPointsMode,drawModCmdContext.rotate_state); + wMenuPushEnable( drawModriginMode,!drawModCmdContext.rotate_state); + wMenuPushEnable( drawModRound, FALSE); + wMenuPushEnable( drawModVertex, FALSE); + wMenuPushEnable( drawModSmooth, FALSE); + wMenuPushEnable( drawModDel, FALSE); + wMenuPushEnable( drawModFill, FALSE); + wMenuPushEnable( drawModEmpty, FALSE); + wMenuPushEnable( drawModClose, FALSE); + wMenuPushEnable( drawModOpen, FALSE); + wMenuPushEnable( drawModSolid, TRUE); + wMenuPushEnable( drawModDot, TRUE); + wMenuPushEnable( drawModDash, TRUE); + wMenuPushEnable( drawModDashDot, TRUE); + wMenuPushEnable( drawModDashDotDot, TRUE); + wMenuPushEnable( drawModCenterDot, TRUE); + wMenuPushEnable( drawModPhantom, TRUE); + if (!drawModCmdContext.rotate_state && (drawModCmdContext.type == SEG_POLY || drawModCmdContext.type == SEG_FILPOLY)) { + wMenuPushEnable( drawModDel,drawModCmdContext.prev_inx>=0); + if ((!drawModCmdContext.open && drawModCmdContext.prev_inx>=0) || + ((drawModCmdContext.prev_inx>0) && (drawModCmdContext.prev_inx<drawModCmdContext.max_inx))) { + wMenuPushEnable( drawModRound,TRUE); + wMenuPushEnable( drawModVertex, TRUE); + wMenuPushEnable( drawModSmooth, TRUE); + } + wMenuPushEnable( drawModFill, (!drawModCmdContext.open) && (!drawModCmdContext.filled)); + wMenuPushEnable( drawModEmpty, (!drawModCmdContext.open) && (drawModCmdContext.filled)); + wMenuPushEnable( drawModClose, drawModCmdContext.open); + wMenuPushEnable( drawModOpen, !drawModCmdContext.open); + } + wMenuPushEnable( drawModOrigin,drawModCmdContext.rotate_state); + wMenuPushEnable( drawModLast,drawModCmdContext.rotate_state && (drawModCmdContext.prev_inx>=0)); + wMenuPushEnable( drawModCenter,drawModCmdContext.rotate_state); + break; + case C_TEXT: + ignoredDraw = trk ; + rc = DrawGeomModify( action, pos, &drawModCmdContext ); + if ( infoSubst ) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } + ignoredDraw = NULL; + if (rc == C_CONTINUE) break; + /* no break*/ + case C_FINISH: ignoredDraw = trk; - rc = DrawGeomModify( xx->orig, xx->angle, xx->segCnt, xx->segs, action, pos, GetTrkSelected(trk) ); - ignoredDraw = NULL; - if (action == C_UP) { + rc = DrawGeomModify( C_FINISH, pos, &drawModCmdContext ); + xx->angle = drawModCmdContext.angle; + xx->orig = drawModCmdContext.orig; + ignoredDraw = NULL; ComputeDrawBoundingBox( trk ); - DrawNewTrack( trk ); + DYNARR_RESET(trkSeg_t,tempSegs_da); + if ( infoSubst ) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } + break; + case C_CANCEL: + case C_CONFIRM: + case C_TERMINATE: + rc = DrawGeomModify( action, pos, &drawModCmdContext ); + drawModCmdContext.state = MOD_NONE; + DYNARR_RESET(trkSeg_t,tempSegs_da); + if ( infoSubst ) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } + break; + + default: + + break; } return rc; } @@ -768,7 +1774,7 @@ static BOOL_T StoreDraw( if (xx->segs[0].type == SEG_POLY || xx->segs[0].type == SEG_FILPOLY) { *data = xx->segs[0].u.p.pts; - *len = xx->segs[0].u.p.cnt* sizeof (coOrd); + *len = xx->segs[0].u.p.cnt* sizeof (pts_t); return TRUE; } return FALSE; @@ -789,6 +1795,275 @@ static BOOL_T ReplayDraw( return FALSE; } +static BOOL_T QueryDraw( track_p trk, int query ) +{ + struct extraData * xx = GetTrkExtraData(trk); + switch(query) { + case Q_IS_DRAW: + return TRUE; + case Q_IS_POLY: + if ((xx->segs[0].type == SEG_POLY) || (xx->segs[0].type == SEG_FILPOLY) ) { + return TRUE; + } + else + return FALSE; + case Q_IS_TEXT: + if (xx->segs[0].type== SEG_TEXT) return TRUE; + else return FALSE; + case Q_GET_NODES: + return TRUE; + case Q_CAN_PARALLEL: + if ((xx->segs[0].type == SEG_STRLIN) || (xx->segs[0].type == SEG_CRVLIN || + ((xx->segs[0].type == SEG_POLY) && (xx->segs[0].u.p.polyType == POLYLINE)) + )) return TRUE; + else return FALSE; + default: + return FALSE; + } +} + +static wBool_t CompareDraw( track_cp trk1, track_cp trk2 ) +{ + struct extraData *xx1 = GetTrkExtraData( trk1 ); + struct extraData *xx2 = GetTrkExtraData( trk2 ); + char * cp = message + strlen(message); + REGRESS_CHECK_POS( "Orig", xx1, xx2, orig ) + REGRESS_CHECK_ANGLE( "Angle", xx1, xx2, angle ) + REGRESS_CHECK_INT( "LineType", xx1, xx2, lineType ) + return CompareSegs( xx1->segs, xx1->segCnt, xx2->segs, xx2->segCnt ); +} + +static BOOL_T GetParamsDraw( int inx, track_p trk, coOrd pos, trackParams_t * params ) { + + struct extraData * xx = GetTrkExtraData(trk); + if (inx != PARAMS_NODES ) return FALSE; + DYNARR_RESET(coOrd,params->nodes); + BOOL_T back = FALSE; + coOrd start,end; + switch (xx->segs[0].type) { + case SEG_POLY: + if (xx->segs[0].u.p.polyType != POLYLINE) return FALSE; + REORIGIN(start,xx->segs[0].u.p.pts[0].pt,xx->angle,xx->orig); + REORIGIN(end,xx->segs[0].u.p.pts[xx->segs[0].u.p.cnt-1].pt,xx->angle,xx->orig); + if (FindDistance(pos,start)>FindDistance(pos,end)) back = TRUE; + for (int i=0;i<xx->segs[0].u.p.cnt;i++) { + DYNARR_APPEND(coOrd,params->nodes,xx->segs[0].u.p.cnt); + if (back) + DYNARR_LAST(coOrd,params->nodes) = xx->segs[0].u.p.pts[xx->segs[0].u.p.cnt-1-i].pt; + else + DYNARR_LAST(coOrd,params->nodes) = xx->segs[0].u.p.pts[i].pt; + REORIGIN(DYNARR_LAST(coOrd,params->nodes),DYNARR_LAST(coOrd,params->nodes),xx->angle,xx->orig); + } + params->lineOrig = DYNARR_N(coOrd,params->nodes,0); + params->lineEnd = DYNARR_LAST(coOrd,params->nodes); + return TRUE; + + case SEG_STRLIN:; + REORIGIN(start,xx->segs[0].u.l.pos[0],xx->angle,xx->orig); + REORIGIN(end,xx->segs[0].u.l.pos[1],xx->angle,xx->orig); + if (FindDistance(pos,start)>FindDistance(pos,end)) back = TRUE; + for (int i=0;i<2;i++) { + DYNARR_APPEND(coOrd,params->nodes,2); + REORIGIN(DYNARR_LAST(coOrd,params->nodes),xx->segs[0].u.l.pos[back?1-i:i],xx->angle,xx->orig); + } + params->lineOrig = DYNARR_N(coOrd,params->nodes,0); + params->lineEnd = DYNARR_LAST(coOrd,params->nodes); + return TRUE; + + case SEG_CRVLIN:; + Translate(&start,xx->segs[0].u.c.center,xx->segs[0].u.c.a0,fabs(xx->segs[0].u.c.radius)); + REORIGIN(start,start,xx->angle,xx->orig); + Translate(&end,xx->segs[0].u.c.center,xx->segs[0].u.c.a0+xx->segs[0].u.c.a1,fabs(xx->segs[0].u.c.radius)); + REORIGIN(end,end,xx->angle,xx->orig); + if (FindDistance(start,pos) > FindDistance(end,pos)) back = TRUE; + if (fabs(xx->segs[0].u.c.radius) > 0.5) { + double min_angle = R2D(2*acos(1.0-(0.1/fabs(xx->segs[0].u.c.radius)))); //Error max is 0.1" + int number = (int) ceil(xx->segs[0].u.c.a1/min_angle); + double arc_size = xx->segs[0].u.c.a1/number; + for (int i=0;i<=number;i++) { + DYNARR_APPEND(coOrd,params->nodes,number); + if (back) + Translate(&DYNARR_LAST(coOrd,params->nodes),xx->segs[0].u.c.center,xx->segs[0].u.c.a0+xx->segs[0].u.c.a1-(i*arc_size),fabs(xx->segs[0].u.c.radius)); + else + Translate(&DYNARR_LAST(coOrd,params->nodes),xx->segs[0].u.c.center,xx->segs[0].u.c.a0+(i*arc_size),fabs(xx->segs[0].u.c.radius)); + REORIGIN(DYNARR_LAST(coOrd,params->nodes),DYNARR_LAST(coOrd,params->nodes),xx->angle,xx->orig); + } + } else { + DYNARR_APPEND(coOrd,params->nodes,2); + REORIGIN(DYNARR_LAST(coOrd,params->nodes),back?end:start,xx->angle,xx->orig); + DYNARR_APPEND(coOrd,params->nodes,2); + REORIGIN(DYNARR_LAST(coOrd,params->nodes),back?start:end,xx->angle,xx->orig); + } + params->lineOrig = DYNARR_N(coOrd,params->nodes,0); + params->lineEnd = DYNARR_LAST(coOrd,params->nodes); + return TRUE; + + case SEG_BEZLIN: + REORIGIN(start,xx->segs[0].u.b.pos[0],xx->angle,xx->orig); + REORIGIN(end,xx->segs[0].u.b.pos[3],xx->angle,xx->orig); + if (FindDistance(pos,start) < FindDistance(pos,end)) + params->ep = 0; + else params->ep = 1; + BOOL_T back = FALSE; + coOrd curr_pos = params->bezierPoints[params->ep*3]; + BOOL_T first = TRUE; + for (int i = 0; i<xx->segs[0].bezSegs.cnt;i++) { + trkSeg_p segPtr = &DYNARR_N(trkSeg_t,xx->segs[0].bezSegs,params->ep?xx->segs[0].bezSegs.cnt-1-i:i); + if (segPtr->type == SEG_STRLIN) { + back = FindDistance(segPtr->u.l.pos[0],curr_pos)>FindDistance(segPtr->u.l.pos[1],curr_pos); + if (first) { + first = FALSE; + DYNARR_APPEND(coOrd,params->nodes,2); + REORIGIN(DYNARR_LAST(coOrd,params->nodes),segPtr->u.l.pos[back],xx->angle,xx->orig); + } + DYNARR_APPEND(coOrd,params->nodes,2); + REORIGIN(DYNARR_LAST(coOrd,params->nodes),segPtr->u.l.pos[1-back],xx->angle,xx->orig); + curr_pos = DYNARR_LAST(coOrd,params->nodes); + } else { + coOrd start,end; + Translate(&start,segPtr->u.c.center,segPtr->u.c.a0,segPtr->u.c.radius); + Translate(&end,segPtr->u.c.center,segPtr->u.c.a0+segPtr->u.c.a1,segPtr->u.c.radius); + back = FindDistance(start,curr_pos)>FindDistance(end,curr_pos); + if (fabs(segPtr->u.c.radius) > 0.2) { + double min_angle = 360*acos(1.0-(0.1/fabs(segPtr->u.c.radius)))/M_PI; //Error max is 0.1" + int number = (int)ceil(segPtr->u.c.a1/min_angle); + double arc_size = segPtr->u.c.a1/number; + for (int j=1-first;j<number;j++) { + DYNARR_APPEND(coOrd,params->nodes,number-first); + if (back == params->ep) + Translate(&DYNARR_LAST(coOrd,params->nodes),segPtr->u.c.center,segPtr->u.c.a0+(j*arc_size),fabs(segPtr->u.c.radius) ); + else + Translate(&DYNARR_LAST(coOrd,params->nodes),segPtr->u.c.center,segPtr->u.c.a0+segPtr->u.c.a1-(j*arc_size),fabs(segPtr->u.c.radius) ); + REORIGIN(DYNARR_LAST(coOrd,params->nodes),DYNARR_LAST(coOrd,params->nodes),xx->angle,xx->orig); + } + first = FALSE; + } else { + if (first) { + first = FALSE; + DYNARR_APPEND(coOrd,params->nodes,2); + REORIGIN(DYNARR_LAST(coOrd,params->nodes),start,xx->angle,xx->orig); + } + DYNARR_APPEND(coOrd,params->nodes,1); + REORIGIN(DYNARR_LAST(coOrd,params->nodes),end,xx->angle,xx->orig); + first = FALSE; + } + curr_pos = DYNARR_LAST(coOrd,params->nodes); + } + } + params->lineOrig = DYNARR_N(coOrd,params->nodes,0); + params->lineEnd = DYNARR_LAST(coOrd,params->nodes); + return TRUE; + + default: + return FALSE; + } + return FALSE; + + +} + +static BOOL_T MakeParallelDraw( + track_p trk, + coOrd pos, + DIST_T sep, + DIST_T factor, + track_p * newTrkR, + coOrd * p0R, + coOrd * p1R, + BOOL_T track) +{ + if (track) return FALSE; + struct extraData * xx = GetTrkExtraData(trk); + + ANGLE_T angle; + DIST_T rad; + coOrd p0,p1; + + switch (xx->segs[0].type) { + case SEG_STRLIN: + angle = FindAngle(xx->segs[0].u.l.pos[0],xx->segs[0].u.l.pos[1]); + if ( NormalizeAngle( FindAngle( xx->segs[0].u.l.pos[0], pos ) - angle ) < 180.0 ) + angle += 90; + else + angle -= 90; + Translate(&p0,xx->segs[0].u.l.pos[0], angle, sep); + Translate(&p1,xx->segs[0].u.l.pos[1], angle, sep); + tempSegs(0).color = xx->segs[0].color; + tempSegs(0).width = xx->segs[0].width; + tempSegs_da.cnt = 1; + tempSegs(0).type = SEG_STRLIN; + tempSegs(0).u.l.pos[0] = p0; + tempSegs(0).u.l.pos[1] = p1; + if (newTrkR) { + *newTrkR = MakeDrawFromSeg( zero, 0.0, &tempSegs(0) ); + struct extraData * yy = GetTrkExtraData(*newTrkR); + yy->lineType = xx->lineType; + } + + if ( p0R ) *p0R = p0; + if ( p1R ) *p1R = p1; + return TRUE; + break; + case SEG_CRVLIN: + rad = FindDistance( pos, xx->segs[0].u.c.center ); + if ( rad > xx->segs[0].u.c.radius ) + rad = xx->segs[0].u.c.radius + sep; + else + rad = xx->segs[0].u.c.radius - sep; + tempSegs(0).color = xx->segs[0].color; + tempSegs(0).width = xx->segs[0].width; + tempSegs_da.cnt = 1; + tempSegs(0).type = SEG_CRVLIN; + tempSegs(0).u.c.center = xx->segs[0].u.c.center; + tempSegs(0).u.c.radius = rad; + tempSegs(0).u.c.a0 = xx->segs[0].u.c.a0; + tempSegs(0).u.c.a1 = xx->segs[0].u.c.a1; + if (newTrkR) { + *newTrkR = MakeDrawFromSeg( zero, 0.0, &tempSegs(0) ); + struct extraData * yy = GetTrkExtraData(*newTrkR); + yy->lineType = xx->lineType; + } + if ( p0R ) PointOnCircle( p0R, xx->segs[0].u.c.center, rad, xx->segs[0].u.c.a0 ); + if ( p1R ) PointOnCircle( p1R, xx->segs[0].u.c.center, rad, xx->segs[0].u.c.a0+xx->segs[0].u.c.a1 ); + return TRUE; + break; + case SEG_POLY: + if (xx->segs[0].u.p.polyType != POLYLINE) return FALSE; + int inx2; + coOrd p = pos; + angle = GetAngleSegs(1,&xx->segs[0],&p,NULL,NULL,NULL,&inx2,NULL); + if ( NormalizeAngle( FindAngle( p, pos ) - angle ) < 180.0 ) { + sep = sep*1.0; + angle += 90; + } else { + angle -= 90; + sep = sep*1.0; + } + tempSegs(0).color = xx->segs[0].color; + tempSegs(0).width = xx->segs[0].width; + tempSegs_da.cnt = 1; + tempSegs(0).type = SEG_POLY; + tempSegs(0).u.p.polyType = POLYLINE; + tempSegs(0).u.p.pts = memdup( xx->segs[0].u.p.pts, xx->segs[0].u.p.cnt*sizeof (pts_t) ); + tempSegs(0).u.p.cnt = xx->segs[0].u.p.cnt; + for (int i=0;i<xx->segs[0].u.p.cnt;i++) { + Translate(&tempSegs(0).u.p.pts[i].pt,tempSegs(0).u.p.pts[i].pt,angle,sep); + } + if (newTrkR) { + *newTrkR = MakeDrawFromSeg( zero, 0.0, &tempSegs(0) ); + struct extraData * yy = GetTrkExtraData(*newTrkR); + yy->lineType = xx->lineType; + if (tempSegs(0).u.p.pts) MyFree(tempSegs(0).u.p.pts); + } + if (p0R) *p0R = tempSegs(0).u.p.pts[0].pt; + if (p1R) *p1R = tempSegs(0).u.p.pts[tempSegs(0).u.p.cnt-1].pt; + return TRUE; + break; + default: + return FALSE; + } + return FALSE; +} static trackCmd_t drawCmds = { "DRAW", @@ -811,19 +2086,21 @@ static trackCmd_t drawCmds = { NULL, /* merge */ ModifyDraw, NULL, /* getLength */ - NULL, /* getTrackParams */ + GetParamsDraw, /* getTrackParams */ NULL, /* moveEndPt */ - NULL, /* query */ + QueryDraw, /* query */ UngroupDraw, FlipDraw, NULL, NULL, NULL, - NULL, /*Parallel*/ + MakeParallelDraw, /*Parallel*/ NULL, NULL, /*MakeSegs*/ ReplayDraw, - StoreDraw + StoreDraw, + NULL, + CompareDraw }; EXPORT BOOL_T OnTableEdgeEndPt( track_p trk, coOrd * pos ) @@ -918,19 +2195,12 @@ EXPORT BOOL_T GetClosestEndPt( track_p trk, coOrd * pos) } -static void DrawRedraw(void); static drawContext_t drawCmdContext = { InfoMessage, - DrawRedraw, + DoRedraw, &mainD, OP_LINE }; -static void DrawRedraw( void ) -{ - MainRedraw(); - MapRedraw(); -} - static wIndex_t benchChoice; static wIndex_t benchOrient; static wIndex_t dimArrowSize; @@ -939,10 +2209,10 @@ long lineWidth = 0; static wDrawColor benchColor; -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 drawLineWidthPD (drawPLs[0]) + { PD_LONG, &drawCmdContext.line_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]) @@ -960,7 +2230,19 @@ static paramData_t drawPLs[] = { { 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") } }; + { PD_DROPLIST, &dimArrowSize, "arrowsize", PDO_NORECORD|PDO_LISTINDEX, (void*)80, N_("Size") }, +#define drawLengthPD (drawPLs[6]) + { PD_FLOAT, &drawCmdContext.length, "Length", PDO_DIM|PDO_NORECORD|BO_ENTER, &r0_10000, N_("Length") }, +#define drawWidthPD (drawPLs[7]) + { PD_FLOAT, &drawCmdContext.width, "BoxWidth", PDO_DIM|PDO_NORECORD|BO_ENTER, &r0_10000, N_("Width") }, +#define drawAnglePD (drawPLs[8]) +#define drawAngleInx 8 + { PD_FLOAT, &drawCmdContext.angle, "Angle", PDO_NORECORD|BO_ENTER, &r360_360, N_("Angle") }, +#define drawRadiusPD (drawPLs[9]) + { PD_FLOAT, &drawCmdContext.radius, "Radius", PDO_DIM|PDO_NORECORD|BO_ENTER, &r0_10000, N_("Radius") }, +#define drawLineTypePD (drawPLs[10]) + { PD_DROPLIST, &drawCmdContext.lineType, "Type", PDO_DIM|PDO_NORECORD|BO_ENTER, (void*)0, N_("Line Type") }, +}; static paramGroup_t drawPG = { "draw", 0, drawPLs, sizeof drawPLs/sizeof drawPLs[0] }; static char * objectName[] = { @@ -976,21 +2258,22 @@ static char * objectName[] = { N_("Circle"), N_("Circle"), N_("Box"), - N_("Polyline"), + N_("Polygon"), N_("Filled Circle"), N_("Filled Circle"), N_("Filled Circle"), N_("Filled Box"), - N_("Polygon"), + N_("Filled Polygon"), N_("Bezier Line"), + N_("Polyline"), NULL}; static STATUS_T CmdDraw( wAction_t action, coOrd pos ) { static BOOL_T infoSubst = FALSE; - wControl_p controls[4]; - char * labels[3]; + wControl_p controls[5]; //Always needs a NULL last entry + char * labels[4]; static char labelName[40]; wAction_t act2 = (action&0xFF) | (bezCmdCreateLine<<8); @@ -1000,7 +2283,7 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos ) case C_START: ParamLoadControls( &drawPG ); /*drawContext = &drawCmdContext;*/ - drawWidthPD.option |= PDO_NORECORD; + drawLineWidthPD.option |= PDO_NORECORD; drawColorPD.option |= PDO_NORECORD; drawBenchColorPD.option |= PDO_NORECORD; drawBenchChoicePD.option |= PDO_NORECORD; @@ -1021,19 +2304,29 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos ) case OP_CURVE4: case OP_CIRCLE2: case OP_CIRCLE3: + case OP_BEZLIN: case OP_BOX: case OP_POLY: - case OP_BEZLIN: - controls[0] = drawWidthPD.control; + case OP_POLYLINE: + controls[0] = drawLineWidthPD.control; controls[1] = drawColorPD.control; + controls[2] = drawLineTypePD.control; controls[2] = NULL; sprintf( labelName, _("%s Line Width"), _(objectName[drawCmdContext.Op]) ); labels[0] = labelName; labels[1] = N_("Color"); + labels[2] = N_("Type"); + if ( wListGetCount( (wList_p)drawLineTypePD.control ) == 0 ) { + wListAddValue( (wList_p)drawLineTypePD.control, _("Solid"), NULL, NULL ); + wListAddValue( (wList_p)drawLineTypePD.control, _("Dot"), NULL, NULL ); + wListAddValue( (wList_p)drawLineTypePD.control, _("Dash"), NULL, NULL ); + wListAddValue( (wList_p)drawLineTypePD.control, _("Dash-Dot"), NULL, NULL ); + wListAddValue( (wList_p)drawLineTypePD.control, _("Dash-Dot-Dot"), NULL, NULL ); + } InfoSubstituteControls( controls, labels ); - drawWidthPD.option &= ~PDO_NORECORD; + drawLineWidthPD.option &= ~PDO_NORECORD; drawColorPD.option &= ~PDO_NORECORD; - lineWidth = drawCmdContext.Width; + drawLineTypePD.option &= ~PDO_NORECORD; break; case OP_FILLCIRCLE2: case OP_FILLCIRCLE3: @@ -1065,6 +2358,7 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos ) drawBenchColorPD.option &= ~PDO_NORECORD; drawBenchChoicePD.option &= ~PDO_NORECORD; drawBenchOrientPD.option &= ~PDO_NORECORD; + drawLengthPD.option &= ~PDO_NORECORD; break; case OP_DIMLINE: controls[0] = drawDimArrowSizePD.control; @@ -1081,9 +2375,7 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos ) 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 ); @@ -1091,8 +2383,7 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos ) } ParamGroupRecord( &drawPG ); if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos); - DrawGeomMouse( C_START, pos, &drawCmdContext ); - + DrawGeomMouse( C_START, pos, &drawCmdContext); return C_CONTINUE; case wActionLDown: @@ -1106,7 +2397,10 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos ) drawCmdContext.Color = benchColor; } else if ( drawCmdContext.Op == OP_DIMLINE ) { + drawCmdContext.Color = wDrawColorBlack; drawCmdContext.benchOption = dimArrowSize; + } else if ( drawCmdContext.Op == OP_TBLEDGE ) { + drawCmdContext.Color = wDrawColorBlack; } else { drawCmdContext.Color = lineColor; } @@ -1114,39 +2408,132 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos ) InfoSubstituteControls( NULL, NULL ); infoSubst = FALSE; } + /* no break */ case wActionLDrag: ParamLoadData( &drawPG ); + /* no break */ case wActionMove: - case wActionLUp: case wActionRDown: case wActionRDrag: - case wActionRUp: - case wActionText: - case C_CMDMENU: if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos); - if (!((MyGetKeyState() & WKEY_SHIFT) != 0)) { + if (!((MyGetKeyState() & WKEY_ALT) != magneticSnap)) { SnapPos( &pos ); } - return DrawGeomMouse( action, pos, &drawCmdContext ); + return DrawGeomMouse( action, pos, &drawCmdContext); + case wActionLUp: + case wActionRUp: + if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos); + //if (!((MyGetKeyState() & WKEY_SHIFT) != 0)) { + // SnapPos( &pos ); Remove Snap at end of action - it will have been imposed in Geom if needed + //} + int rc = DrawGeomMouse( action, pos, &drawCmdContext); + // Put up text entry boxes ready for updates if the result was continue + if (rc == C_CONTINUE) { + switch( drawCmdContext.Op ) { + case OP_CIRCLE1: + case OP_CIRCLE2: + case OP_CIRCLE3: + case OP_FILLCIRCLE1: + case OP_FILLCIRCLE2: + case OP_FILLCIRCLE3: + controls[0] = drawRadiusPD.control; + controls[1] = NULL; + labels[0] = N_("Radius"); + ParamLoadControls( &drawPG ); + InfoSubstituteControls( controls, labels ); + drawRadiusPD.option &= ~PDO_NORECORD; + infoSubst = TRUE; + break; + case OP_CURVE1: + case OP_CURVE2: + case OP_CURVE3: + case OP_CURVE4: + if (drawCmdContext.ArcData.type == curveTypeCurve) { + controls[0] = drawRadiusPD.control; + controls[1] = drawAnglePD.control; + controls[2] = NULL; + labels[0] = N_("Radius"); + labels[1] = N_("Arc Angle"); + } else { + controls[0] = drawLengthPD.control; + controls[1] = drawAnglePD.control; + controls[2] = NULL; + labels[0] = N_("Length"); + labels[1] = N_("Angle"); + } + ParamLoadControls( &drawPG ); + InfoSubstituteControls( controls, labels ); + drawLengthPD.option &= ~PDO_NORECORD; + drawRadiusPD.option &= ~PDO_NORECORD; + drawAnglePD.option &= ~PDO_NORECORD; + infoSubst = TRUE; + break; + case OP_LINE: + case OP_BENCH: + case OP_TBLEDGE: + case OP_POLY: + case OP_FILLPOLY: + case OP_POLYLINE: + controls[0] = drawLengthPD.control; + controls[1] = drawAnglePD.control; + controls[2] = NULL; + labels[0] = N_("Seg Length"); + if (drawCmdContext.Op == OP_LINE || drawCmdContext.Op == OP_BENCH || drawCmdContext.Op == OP_TBLEDGE) + labels[1] = N_("Angle"); + else if (drawCmdContext.index > 0 ) + labels[1] = N_("Rel Angle"); + else + labels[1] = N_("Angle"); + ParamLoadControls( &drawPG ); + InfoSubstituteControls( controls, labels ); + drawLengthPD.option &= ~PDO_NORECORD; + drawAnglePD.option &= ~PDO_NORECORD; + infoSubst = TRUE; + break; + case OP_BOX: + case OP_FILLBOX: + controls[0] = drawLengthPD.control; + controls[1] = drawWidthPD.control; + controls[2] = NULL; + labels[0] = N_("Length"); + labels[1] = N_("Width"); + ParamLoadControls( &drawPG ); + InfoSubstituteControls( controls, labels ); + drawLengthPD.option &= ~PDO_NORECORD; + drawWidthPD.option &= ~PDO_NORECORD; + infoSubst = TRUE; + break; + default: + break; + } + } + return rc; case C_CANCEL: InfoSubstituteControls( NULL, NULL ); if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos); - return DrawGeomMouse( action, pos, &drawCmdContext ); - + return DrawGeomMouse( action, pos, &drawCmdContext); + case C_TEXT: + if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(action, pos); + return DrawGeomMouse( action, pos, &drawCmdContext); case C_OK: if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos); - return DrawGeomMouse( (0x0D<<8|wActionText), pos, &drawCmdContext ); + return DrawGeomMouse( (0x0D<<8|wActionText), pos, &drawCmdContext); + /*DrawOk( NULL );*/ case C_FINISH: if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos); - return DrawGeomMouse( (0x0D<<8|wActionText), pos, &drawCmdContext ); + return DrawGeomMouse( (0x0D<<8|wActionText), pos, &drawCmdContext); /*DrawOk( NULL );*/ case C_REDRAW: if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos); - return DrawGeomMouse( action, pos, &drawCmdContext ); + return DrawGeomMouse( action, pos, &drawCmdContext); + + case C_CMDMENU: + if (drawCmdContext.Op == OP_BEZLIN) return C_CONTINUE; + return DrawGeomMouse( action, pos, &drawCmdContext); default: return C_CONTINUE; @@ -1172,6 +2559,7 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos ) #include "bitmaps/dpoly.xpm" #include "bitmaps/dfilpoly.xpm" #include "bitmaps/dbezier.xpm" +#include "bitmaps/dpolyline.xpm" typedef struct { char **xpm; @@ -1195,16 +2583,18 @@ static drawData_t dcurveCmds[] = { { dbezier_xpm, OP_BEZLIN, N_("Bezier Curve"), N_("Draw Bezier"), "cmdDrawBezierCurve", ACCL_DRAWBEZLINE } }; 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 }, + { dcircle3_xpm, OP_CIRCLE3, N_("Circle Tangent"), N_("Draw Circle from Tangent"), "cmdDrawCircleTangent", ACCL_DRAWCIRCLE2 }, + { dcircle2_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 } }; + { dflcrcl3_xpm, OP_FILLCIRCLE3, N_("Circle Filled Tangent"), N_("Draw Filled Circle from Tangent"), "cmdDrawFilledCircleTangent", ACCL_DRAWFILLCIRCLE2 }, + { dflcrcl2_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 } }; + { dpoly_xpm, OP_POLY, N_("Polygon"), N_("Draw Polygon"), "cmdDrawPolygon", ACCL_DRAWPOLY }, + { dfilpoly_xpm, OP_FILLPOLY, N_("Filled Polygon"), N_("Draw Filled Polygon"), "cmdDrawFilledPolygon", ACCL_DRAWFILLPOLYGON }, + { dpolyline_xpm, OP_POLYLINE, N_("PolyLine"), N_("Draw PolyLine"), "cmdDrawPolyline", ACCL_DRAWPOLYLINE }, +}; typedef struct { char * helpKey; @@ -1223,7 +2613,7 @@ static drawStuff_t drawStuff[4] = { { "cmdDrawLineSetCmd", N_("Straight Objects"), N_("Draw Straight Objects"), 4, dlineCmds }, { "cmdDrawCurveSetCmd", N_("Curved Lines"), N_("Draw Curved Lines"), 5, dcurveCmds }, { "cmdDrawCircleSetCmd", N_("Circle Lines"), N_("Draw Circles"), 4, dcircleCmds }, - { "cmdDrawShapeSetCmd", N_("Shapes"), N_("Draw Shapes"), 4, dshapeCmds} }; + { "cmdDrawShapeSetCmd", N_("Shapes"), N_("Draw Shapes"), 5, dshapeCmds} }; static void ChangeDraw( long changes ) @@ -1247,13 +2637,58 @@ static void DrawDlgUpdate( int inx, void * valueP ) { - if (drawCmdContext.Op == OP_BEZLIN) { - if ( (inx == 0 && pg->paramPtr[inx].valueP == &drawCmdContext.Width) || - (inx == 1 && pg->paramPtr[inx].valueP == &lineColor)) - { - lineWidth = drawCmdContext.Width; - UpdateParms(lineColor, lineWidth); - } + if (inx==3) { + if (drawCmdContext.Op == OP_BEZLIN) { + if ( (inx == 0 && pg->paramPtr[inx].valueP == &drawCmdContext.line_Width) || + (inx == 1 && pg->paramPtr[inx].valueP == &lineColor)) + { + lineWidth = drawCmdContext.line_Width; + UpdateParms(lineColor, lineWidth); + } + } + } + if (inx >=6 ) { + if (drawCmdContext.Op == OP_CIRCLE1 || + drawCmdContext.Op == OP_FILLCIRCLE1 || + drawCmdContext.Op == OP_CIRCLE2 || + drawCmdContext.Op == OP_FILLCIRCLE2 || + drawCmdContext.Op == OP_CIRCLE3 || + drawCmdContext.Op == OP_FILLCIRCLE3) { + coOrd pos = zero; + DrawGeomMouse(C_UPDATE,pos,&drawCmdContext); + } + if (drawCmdContext.Op == OP_CURVE1 || + drawCmdContext.Op == OP_CURVE2 || + drawCmdContext.Op == OP_CURVE3 || + drawCmdContext.Op == OP_CURVE4 ) { + coOrd pos = zero; + DrawGeomMouse(C_UPDATE,pos,&drawCmdContext); + } + if (drawCmdContext.Op == OP_LINE || + drawCmdContext.Op == OP_BENCH|| + drawCmdContext.Op == OP_TBLEDGE) { + coOrd pos = zero; + DrawGeomMouse(C_UPDATE,pos,&drawCmdContext); + } + + if (drawCmdContext.Op == OP_BOX || + drawCmdContext.Op == OP_FILLBOX ){ + coOrd pos = zero; + DrawGeomMouse(C_UPDATE,pos,&drawCmdContext); + } + + if (drawCmdContext.Op == OP_POLY || + drawCmdContext.Op == OP_FILLPOLY || + drawCmdContext.Op == OP_POLYLINE) { + coOrd pos = zero; + DrawGeomMouse(C_UPDATE,pos,&drawCmdContext); + } + ParamLoadControl(&drawPG,drawAngleInx); //Force Angle change out + //if (pg->paramPtr[inx].enter_pressed) { + // coOrd pos = zero; + // DrawGeomMouse((0x0D<<8)|(C_TEXT&0xFF),pos,&drawCmdContext); + // CmdDraw(C_START,pos); + //} } if ( inx >= 0 && pg->paramPtr[inx].valueP == &benchChoice ) @@ -1272,13 +2707,15 @@ EXPORT void InitCmdDraw( wMenu_p menu ) benchColor = wDrawFindColor( wRGB(255,192,0) ); ParamCreateControls( &drawPG, DrawDlgUpdate ); + ParamCreateControls( &drawModPG, DrawModDlgUpdate) ; + 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 ); + AddMenuButton( menu, CmdDraw, ddp->helpKey, _(ddp->cmdName), icon, LEVEL0_50, IC_STICKY|IC_POPUP2|IC_WANT_MOVE, ddp->acclKey, (void *)(intptr_t)ddp->OP ); } ButtonGroupEnd(); } @@ -1327,7 +2764,8 @@ EXPORT track_p NewText( ANGLE_T angle, char * text, CSIZE_T textSize, - wDrawColor color ) + wDrawColor color, + BOOL_T boxed) { trkSeg_t tempSeg; track_p trk; @@ -1339,6 +2777,7 @@ EXPORT track_p NewText( tempSeg.u.t.fontP = NULL; tempSeg.u.t.fontSize = textSize; tempSeg.u.t.string = MyStrdup( text ); + tempSeg.u.t.boxed = boxed; trk = MakeDrawFromSeg1( index, pos, angle, &tempSeg ); return trk; } @@ -1364,20 +2803,127 @@ EXPORT BOOL_T ReadText( char * line ) return FALSE; } - char * old = text; - text = ConvertFromEscapedText(text); - MyFree(old); - - trk = NewText( index, pos, angle, text, textSize, color ); + trk = NewText( index, pos, angle, text, textSize, color, FALSE ); SetTrkLayer( trk, layer ); MyFree(text); return TRUE; } +void MenuMode(int mode) { + if ( infoSubst ) { + InfoSubstituteControls( NULL, NULL ); + infoSubst = FALSE; + } + if (mode == 1) { + DrawGeomOriginMove(C_START,zero,&drawModCmdContext); + InfoMessage("Origin Mode"); + } else { + DrawGeomModify(C_START,zero,&drawModCmdContext); + InfoMessage("Points Mode"); + } +} + +void MenuEnter(int key) { + int action; + action = C_TEXT; + action |= key<<8; + if (drawModCmdContext.rotate_state) + DrawGeomOriginMove(action,zero,&drawModCmdContext); + else + DrawGeomModify(action,zero,&drawModCmdContext); +} + +void MenuLine(int key) { + struct extraData * xx = GetTrkExtraData(drawModCmdContext.trk); + if ( drawModCmdContext.type==SEG_STRLIN || drawModCmdContext.type==SEG_CRVLIN || drawModCmdContext.type==SEG_POLY ) { + switch(key) { + case '0': + xx->lineType = DRAWLINESOLID; + break; + case '1': + xx->lineType = DRAWLINEDASH; + break; + case '2': + xx->lineType = DRAWLINEDOT; + break; + case '3': + xx->lineType = DRAWLINEDASHDOT; + break; + case '4': + xx->lineType = DRAWLINEDASHDOTDOT; + break; + case '5': + xx->lineType = DRAWLINECENTER; + break; + case '6': + xx->lineType = DRAWLINEPHANTOM; + break; + } + MainRedraw(); // MenuLine + } +} + +EXPORT void SetLineType( track_p trk, int width ) { + if (QueryTrack(trk, Q_IS_DRAW)) { + struct extraData * xx = GetTrkExtraData(trk); + if ( xx->segs[0].type==SEG_STRLIN || xx->segs[0].type==SEG_CRVLIN || xx->segs[0].type==SEG_POLY) { + switch(width) { + case 0: + xx->lineType = DRAWLINESOLID; + break; + case 1: + xx->lineType = DRAWLINEDASH; + break; + case 2: + xx->lineType = DRAWLINEDOT; + break; + case 3: + xx->lineType = DRAWLINEDASHDOT; + break; + case 4: + xx->lineType = DRAWLINEDASHDOTDOT; + break; + case 5: + xx->lineType = DRAWLINECENTER; + break; + case 6: + xx->lineType = DRAWLINEPHANTOM; + break; + } + } + } +} EXPORT void InitTrkDraw( void ) { T_DRAW = InitObject( &drawCmds ); AddParam( "TABLEEDGE", ReadTableEdge ); AddParam( "TEXT", ReadText ); + + drawModDelMI = MenuRegister( "Modify Draw Edit Menu" ); + drawModClose = wMenuPushCreate( drawModDelMI, "", _("Close Polygon - 'g'"), 0, (wMenuCallBack_p)MenuEnter, (void*) 'g'); + drawModOpen = wMenuPushCreate( drawModDelMI, "", _("Make PolyLine - 'l'"), 0, (wMenuCallBack_p)MenuEnter, (void*) 'l'); + drawModFill = wMenuPushCreate( drawModDelMI, "", _("Fill Polygon - 'f'"), 0, (wMenuCallBack_p)MenuEnter, (void*) 'f'); + drawModEmpty = wMenuPushCreate( drawModDelMI, "", _("Empty Polygon - 'u'"), 0, (wMenuCallBack_p)MenuEnter, (void*) 'u'); + wMenuSeparatorCreate( drawModDelMI ); + drawModPointsMode = wMenuPushCreate( drawModDelMI, "", _("Points Mode - 'p'"), 0, (wMenuCallBack_p)MenuMode, (void*) 0 ); + drawModDel = wMenuPushCreate( drawModDelMI, "", _("Delete Selected Point - 'Del'"), 0, (wMenuCallBack_p)MenuEnter, (void*) 127 ); + drawModVertex = wMenuPushCreate( drawModDelMI, "", _("Vertex Point - 'v'"), 0, (wMenuCallBack_p)MenuEnter, (void*) 'v' ); + drawModRound = wMenuPushCreate( drawModDelMI, "", _("Round Corner - 'r'"), 0, (wMenuCallBack_p)MenuEnter, (void*) 'r' ); + drawModSmooth = wMenuPushCreate( drawModDelMI, "", _("Smooth Corner - 's'"), 0, (wMenuCallBack_p)MenuEnter, (void*) 's' ); + wMenuSeparatorCreate( drawModDelMI ); + drawModLinMI = wMenuMenuCreate( drawModDelMI, "", _("LineType...") ); + drawModSolid = wMenuPushCreate( drawModLinMI, "", _("Solid Line"), 0, (wMenuCallBack_p)MenuLine, (void*) '0' ); + drawModDot = wMenuPushCreate( drawModLinMI, "", _("Dashed Line"), 0, (wMenuCallBack_p)MenuLine, (void*) '1' ); + drawModDash = wMenuPushCreate( drawModLinMI, "", _("Dotted Line"), 0, (wMenuCallBack_p)MenuLine, (void*) '2' ); + drawModDashDot = wMenuPushCreate( drawModLinMI, "", _("Dash-Dot Line"), 0, (wMenuCallBack_p)MenuLine, (void*) '3' ); + drawModDashDotDot = wMenuPushCreate( drawModLinMI, "", _("Dash-Dot-Dot Line"), 0, (wMenuCallBack_p)MenuLine, (void*) '4' ); + drawModCenterDot = wMenuPushCreate( drawModLinMI, "", _("Center-Dot Line"), 0, (wMenuCallBack_p)MenuLine, (void*) '5' ); + drawModPhantom = wMenuPushCreate( drawModLinMI, "", _("Phantom-Dot Line"), 0, (wMenuCallBack_p)MenuLine, (void*) '6' ); + wMenuSeparatorCreate( drawModDelMI ); + drawModriginMode = wMenuPushCreate( drawModDelMI, "", _("Origin Mode - 'o'"), 0, (wMenuCallBack_p)MenuMode, (void*) 1 ); + drawModOrigin = wMenuPushCreate( drawModDelMI, "", _("Reset Origin - '0'"), 0, (wMenuCallBack_p)MenuEnter, (void*) '0' ); + drawModLast = wMenuPushCreate( drawModDelMI, "", _("Origin to Selected - 'l'"), 0, (wMenuCallBack_p)MenuEnter, (void*) 'l' ); + drawModCenter = wMenuPushCreate( drawModDelMI, "", _("Origin to Middle - 'm'"), 0, (wMenuCallBack_p)MenuEnter, (void*) 'm'); + } diff --git a/app/bin/celev.c b/app/bin/celev.c index 5a63a3a..1da4b22 100644 --- a/app/bin/celev.c +++ b/app/bin/celev.c @@ -29,6 +29,8 @@ #include "i18n.h" #include "param.h" #include "track.h" +#include "ccurve.h" +#include "utility.h" static wWin_p elevW; @@ -58,6 +60,71 @@ static paramData_t elevationPLs[] = { { PD_STRING, elevStationV, "station", PDO_DLGUNDERCMDBUTT|PDO_STRINGLIMITLENGTH, (void*)200, NULL, 0, 0, sizeof(elevStationV)} }; static paramGroup_t elevationPG = { "elev", 0, elevationPLs, sizeof elevationPLs/sizeof elevationPLs[0] }; +static dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) + +static void CreateSquareAnchor(coOrd p) { + DIST_T d = tempD.scale*0.25; + int i = anchors_da.cnt; + DYNARR_SET(trkSeg_t,anchors_da,i+4); + for (int j =0; j<4;j++) { + anchors(i+j).type = SEG_STRLIN; + anchors(i+j).color = wDrawColorBlue; + anchors(i+j).width = 0; + } + anchors(i).u.l.pos[0].x = anchors(i+2).u.l.pos[1].x = + anchors(i+3).u.l.pos[0].x = anchors(i+3).u.l.pos[1].x = p.x-d/2; + + anchors(i).u.l.pos[0].y = anchors(i).u.l.pos[1].y = + anchors(i+1).u.l.pos[0].y = anchors(i+3).u.l.pos[1].y = p.y-d/2; + + anchors(i).u.l.pos[1].x = + anchors(i+1).u.l.pos[0].x = anchors(i+1).u.l.pos[1].x = + anchors(i+2).u.l.pos[0].x = p.x+d/2; + + anchors(i+1).u.l.pos[1].y = + anchors(i+2).u.l.pos[0].y = anchors(i+2).u.l.pos[1].y = + anchors(i+3).u.l.pos[0].y = p.y+d/2; +} + +static void CreateEndAnchor(coOrd p, wBool_t lock) { + DIST_T d = tempD.scale*0.15; + + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = lock?SEG_FILCRCL:SEG_CRVLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.c.center = p; + anchors(i).u.c.radius = d/2; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).width = 0; +} + +static void CreateSplitAnchor(coOrd pos, track_p t) { + DIST_T d = tempD.scale*0.1; + DIST_T w = tempD.scale/tempD.dpi*4; + int i; + ANGLE_T a = NormalizeAngle(GetAngleAtPoint(t,pos,NULL,NULL)+90.0); + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).color = wDrawColorBlue; + Translate(&anchors(i).u.l.pos[0],pos,a,GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[1],pos,a,-GetTrkGauge(t)); + anchors(i).width = w; + +} + + +void static CreateMoveAnchor(coOrd pos) { + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),pos,0,TRUE,wDrawColorBlue); + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),pos,90,TRUE,wDrawColorBlue); + DYNARR_APPEND(trkSeg_t,anchors_da,1); + CreateSquareAnchor(pos); +} static void LayoutElevW( paramData_t * pd, @@ -97,59 +164,16 @@ static int GetElevMode( void ) } -#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 ) { @@ -161,15 +185,6 @@ static void DoElevUpdate( paramGroup_p pg, int inx, void * valueP ) 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 ); @@ -204,27 +219,14 @@ static void DoElevUpdate( paramGroup_p pg, int inx, void * valueP ) 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)); + TempRedraw(); // DoElevUpdate } static void DoElevDone( void * arg ) { DoElevUpdate( NULL, 1, NULL ); - HilightElevations( FALSE ); - HilightSelectedEndPt( FALSE, elevTrk, elevEp ); elevTrk = NULL; Reset(); } @@ -250,7 +252,6 @@ static void ElevSelect( track_p trk, EPINX_T ep ) elevOldValue = 0.0; elevHeightV = 0.0; elevStationV[0] = 0; - HilightSelectedEndPt(FALSE, elevTrk, elevEp); elevTrk = trk; elevEp = ep; mode = GetTrkEndElevUnmaskedMode( trk, ep ); @@ -294,89 +295,26 @@ static void ElevSelect( track_p trk, EPINX_T ep ) } 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, "" );*/ - } + gradeOk = ComputeElev( trk, ep, FALSE, &elevX, &grade, TRUE ); 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":"\"") ); + sprintf( message, "%0.2f%s", round(PutDim( elevX )*100.0)/100.0, (units==UNITS_METRIC?"cm":"\"") ); ParamLoadMessage( &elevationPG, I_COMPUTED, message ); if (gradeOk) { - sprintf( message, "%0.1f%%", fabs(grade*100) ); + sprintf( message, "%0.1f%%", fabs(round(grade*1000.0)/10.0) ); } 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 ); + sprintf( message, "%0.1f%%", fabs(round((elev-elevX)/dist)*1000.0)/10.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 ); + sprintf( message+strlen(message), " - %0.1f%%", fabs(round((elev-elevX)/dist)*1000.0)/10.0 ); else sprintf( message+strlen(message), " - %s", _("Undefined") ); } @@ -390,7 +328,41 @@ if (oldElevationEvaluation) { ParamLoadControl( &elevationPG, I_HEIGHT ); } } - HilightSelectedEndPt(TRUE, elevTrk, elevEp); + wShow(elevW); +} + +static BOOL_T GetPointElev(track_p trk, coOrd pos, DIST_T * height) { + DIST_T len, len1, elev0, elev1, dist0, dist1; + if ( IsTrack( trk ) && GetTrkEndPtCnt(trk) == 2 ) { + dist0 = FindDistance(pos,GetTrkEndPos(trk,0)); + dist1 = FindDistance(pos,GetTrkEndPos(trk,1)); + if (EndPtIsDefinedElev(trk,0)) + elev0 = GetTrkEndElevHeight(trk,0); + else { + if (!GetTrkEndElevCachedHeight(trk,0,&elev0,&len)) { + if (GetTrkLength( trk, 0, 1 )<0.1) return FALSE; + ComputeElev( trk, 0, FALSE, &elev0, NULL, TRUE ); + } + } + if (EndPtIsDefinedElev(trk,1)) + elev1 = GetTrkEndElevHeight(trk,1); + else { + if (!GetTrkEndElevCachedHeight(trk,1,&elev1,&len1)) { + if (GetTrkLength( trk, 0, 1 )<0.1) return FALSE; + ComputeElev( trk, 0, FALSE, &elev0, NULL, TRUE ); + } + } + if (dist1+dist0 < 0.1) { + *height = elev0; + return TRUE; + } + *height = ((elev1-elev0)*(dist0/(dist0+dist1)))+elev0; + return TRUE; + } else if (GetTrkEndPtCnt(trk) == 1 && GetTrkEndElevCachedHeight(trk,0,&elev0,&len)) { + *height = elev0; + return TRUE; + } + return FALSE; } @@ -403,59 +375,129 @@ static STATUS_T CmdElevation( wAction_t action, coOrd pos ) switch (action) { case C_START: if ( elevW == NULL ) - elevW = ParamCreateDialog( &elevationPG, MakeWindowTitle(_("Elevation")), _("Done"), DoElevDone, NULL, TRUE, LayoutElevW, 0, DoElevUpdate ); + elevW = ParamCreateDialog( &elevationPG, MakeWindowTitle(_("Elevation")), _("Done"), DoElevDone, wHide, TRUE, LayoutElevW, 0, DoElevUpdate ); elevModeV = 0; elevHeightV = 0.0; elevStationV[0] = 0; ParamLoadControls( &elevationPG ); ParamGroupRecord( &elevationPG ); - wShow( elevW ); + //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 ); + InfoMessage( _("Click on end, +Shift to split, +Ctrl to move description") ); elevTrk = NULL; elevUndo = FALSE; + CmdMoveDescription( action, pos ); + TempRedraw(); // CmdElevation C_START 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) { + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + if (MyGetKeyState()&WKEY_CTRL) { + commandContext = (void*) 1; //Just end points + CmdMoveDescription( action, pos ); 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 )) + BOOL_T xing = FALSE; + coOrd p0 = pos, p2=pos; + if ((trk0 = OnTrack2(&p0,FALSE, TRUE, FALSE, NULL)) != NULL) { + EPINX_T ep0 = 0, ep1 = 1; + DIST_T elev0, elev1; + if (GetTrkEndPtCnt(trk0) == 2) { + if (!GetPointElev(trk0,p0,&elev0)) { + InfoMessage( _("Move to end or track crossing +Shift to split") ); + return C_CONTINUE; + } + } else { + InfoMessage( _("Move to end or track crossing") ); return C_CONTINUE; - ElevSelect( trk0, ep0 ); - UndoEnd(); - elevUndo = FALSE; - } else { - ep0 = PickEndPoint( pos, trk0 ); - ElevSelect( trk0, ep0 ); + } + if ((trk1 = OnTrack2(&p2,FALSE, TRUE, FALSE, trk0)) != NULL) { + if (IsClose(FindDistance(p0,p2))) { + if (GetEndPtConnectedToMe(trk0,trk1) == -1) { //Not simply connected to each other!!! + if (GetTrkEndPtCnt(trk1) == 2) { + if (GetPointElev(trk1,p2,&elev1)) { + if (MyGetKeyState()&WKEY_SHIFT) { + InfoMessage (_("Crossing - First %0.3f, Second %0.3f, Clearance %0.3f - Click to Split"), PutDim(elev0), PutDim(elev1), PutDim(fabs(elev0-elev1))); + } else + InfoMessage (_("Crossing - First %0.3f, Second %0.3f, Clearance %0.3f"), PutDim(elev0), PutDim(elev1), PutDim(fabs(elev0-elev1))); + } + CreateSquareAnchor(p2); + return C_CONTINUE; + } + } + } + } + if ((ep0 = PickEndPoint( p0, trk0 )) != -1) { + if (IsClose(FindDistance(GetTrkEndPos(trk0,ep0),pos))) { + CreateEndAnchor(GetTrkEndPos(trk0,ep0),FALSE); + InfoMessage (_("Track End elevation %0.3f"), PutDim(elev0)); + } else if ((MyGetKeyState()&WKEY_SHIFT) && QueryTrack(trk0,Q_MODIFY_CAN_SPLIT) + && !(QueryTrack(trk0,Q_IS_TURNOUT))) { + InfoMessage( _("Click to split here - elevation %0.3f"), PutDim(elev0)); + CreateSplitAnchor(p0,trk0); + } else { + InfoMessage( _("Track Point elevation %0.3f"), PutDim(elev0)); + CreateEndAnchor(p0,TRUE); + } + } else InfoMessage( _("Click on end, +Shift to split, +Ctrl to move description") ); + } else + InfoMessage( _("Click on end, +Shift to split, +Ctrl to move description") ); + return C_CONTINUE; + case C_DOWN: + case C_MOVE: + case C_UP: + if (MyGetKeyState()&WKEY_CTRL) { + commandContext = (void*) 1; //Just end points + CmdMoveDescription( action, pos ); + DYNARR_RESET(trkSeg_t,anchors_da); + elevTrk = NULL; return C_CONTINUE; } + /*no break*/ + case C_LCLICK: + ; + p0= pos; + if ((trk0 = OnTrack( &p0, TRUE, TRUE )) == NULL) { + wHide(elevW); + elevTrk = NULL; + InfoMessage( _("Click on end, +Shift to split, +Ctrl to move description") ); + } else { + ep0 = PickEndPoint( p0, trk0 ); + if (IsClose(FindDistance(GetTrkEndPos(trk0,ep0),pos))) { + InfoMessage( _("Point selected!") ); + ElevSelect( trk0, ep0 ); + } else if ( (MyGetKeyState()&WKEY_SHIFT) ) { + UndoStart( _("Split track"), "SplitTrack( T%d[%d] )", GetTrkIndex(trk0), ep0 ); + oldTrackCount = trackCount; + if (!QueryTrack(trk0,Q_IS_TURNOUT) && + !SplitTrack( trk0, p0, ep0, &trk1, FALSE )) + return C_CONTINUE; + InfoMessage( _("Track split!") ); + ElevSelect( trk0, ep0 ); + UndoEnd(); + elevUndo = FALSE; + } + } + DYNARR_RESET(trkSeg_t,anchors_da); return C_CONTINUE; case C_OK: DoElevDone(NULL); + InfoMessage( "" ); return C_TERMINATE; case C_CANCEL: - HilightElevations( FALSE ); - HilightSelectedEndPt( FALSE, elevTrk, elevEp ); elevTrk = NULL; wHide( elevW ); + InfoMessage( "" ); return C_TERMINATE; case C_REDRAW: DoElevHilight( NULL ); HilightSelectedEndPt( TRUE, elevTrk, elevEp ); + if (anchors_da.cnt) + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + CmdMoveDescription( action, pos ); return C_CONTINUE; } return C_CONTINUE; @@ -469,6 +511,6 @@ static STATUS_T CmdElevation( wAction_t action, coOrd pos ) 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 ); + AddMenuButton( menu, CmdElevation, "cmdElevation", _("Elevation"), wIconCreatePixMap(elev_xpm), LEVEL0_50, IC_POPUP|IC_LCLICK|IC_RCLICK|IC_WANT_MOVE, ACCL_ELEVATION, NULL ); } diff --git a/app/bin/cgroup.c b/app/bin/cgroup.c index 0094564..1183e76 100644 --- a/app/bin/cgroup.c +++ b/app/bin/cgroup.c @@ -472,11 +472,12 @@ LOG( log_group, 1, ( " EP%d = [%0.3f %0.3f] A%0.3f T%d.%d\n", ep, epp->pos.x, ep 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) ); + trk1 = NewCompound( T_TURNOUT, 0, orig, xx->angle, xx->title, tempEndPts_da.cnt-epCnt1, &tempEndPts(epCnt1), NULL, pathPtr_da.cnt, &pathPtr(0), tempSegs_da.cnt, &tempSegs(0) ); xx1 = GetTrkExtraData(trk1); xx1->ungrouped = TRUE; SetTrkVisible( trk1, TRUE ); + SetTrkNoTies( trk1, FALSE ); SetTrkBits( trk1, TB_SELECTED ); for ( segInx=0; segInx<segCnt; segInx++ ) { if ( refCount(segInx) == inx ) { @@ -608,19 +609,25 @@ EXPORT void DoUngroup( void ) static drawCmd_t groupD = { - NULL, &tempSegDrawFuncs, DC_GROUP, 1, 0.0, {0.0, 0.0}, {0.0, 0.0}, Pix2CoOrd, CoOrd2Pix }; + NULL, &tempSegDrawFuncs, DC_SEGTRACK, 1, 0.0, {0.0, 0.0}, {0.0, 0.0}, Pix2CoOrd, CoOrd2Pix }; static long groupSegCnt; static long groupReplace; +static double groupOriginX; +static double groupOriginY; char * groupReplaceLabels[] = { N_("Replace with new group?"), NULL }; static wWin_p groupW; static paramIntegerRange_t r0_999999 = { 0, 999999 }; +static paramFloatRange_t r_1000_1000 = { -1000.0, 1000.0, 80 }; static paramData_t groupPLs[] = { /*0*/ { PD_STRING, groupManuf, "manuf", PDO_NOPREF | PDO_STRINGLIMITLENGTH, (void*)350, N_("Manufacturer"), 0, 0, sizeof(groupManuf)}, /*1*/ { PD_STRING, groupDesc, "desc", PDO_NOPREF | PDO_STRINGLIMITLENGTH, (void*)230, N_("Description"), 0, 0, sizeof(groupDesc)}, /*2*/ { PD_STRING, groupPartno, "partno", PDO_NOPREF|PDO_DLGHORZ|PDO_DLGIGNORELABELWIDTH|PDO_STRINGLIMITLENGTH, (void*)100, N_("#"), 0, 0, sizeof(groupPartno)}, /*3*/ { PD_LONG, &groupSegCnt, "segcnt", PDO_NOPREF, &r0_999999, N_("# Segments"), BO_READONLY }, -/*4*/ { PD_TOGGLE, &groupReplace, "replace", 0, groupReplaceLabels, "", BC_HORZ|BC_NOBORDER } }; +#define I_GROUP_ORIGIN_OFFSET 4 /* Need to change if add above */ +/*4*/ { PD_FLOAT, &groupOriginX, "orig", PDO_DIM, &r_1000_1000, N_("Offset X,Y:")}, +/*5*/ { PD_FLOAT, &groupOriginY, "origy",PDO_DIM | PDO_DLGHORZ, &r_1000_1000, ""}, +/*6*/ { PD_TOGGLE, &groupReplace, "replace", 0, groupReplaceLabels, "", BC_HORZ|BC_NOBORDER } }; static paramGroup_t groupPG = { "group", 0, groupPLs, sizeof groupPLs/sizeof groupPLs[0] }; @@ -652,18 +659,14 @@ static dynArr_t pathElem_da; 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 ); -} - +/* + * Find sub-path that connects the 2 EPs for the given track + * + * \param trk IN Track + * \param ep1, ep2 IN EndPt index + * \param BOOL_T *flip OUT whether path is flipped + * \return sub-path that connects the 2 EPs + */ static char * FindPathBtwEP( track_p trk, EPINX_T ep1, @@ -671,12 +674,11 @@ static char * FindPathBtwEP( BOOL_T * flip ) { struct extraData * xx = GetTrkExtraData( trk ); - char * cp, *cp0; - int epN; - coOrd pos1, pos2; - int segInx; - EPINX_T segEP; + char * cp; + coOrd trkPos[2]; + + LOG( log_group, 3, (" FindPathBtwEP: T%d .%d .%d = ", trk?GetTrkIndex(trk):-1, ep1, ep2 )); if ( GetTrkType(trk) != T_TURNOUT ) { if ( ep1+ep2 != 1 ) AbortProg( "findPathBtwEP" ); @@ -685,40 +687,63 @@ static char * FindPathBtwEP( cp = CreateSegPathList(trk); // Make path LOG( log_group, 2, ( " Group: Cornu path:%s \n", cp ) ) } else cp = "\1\0\0"; //One segment (but could be a Bezier) + LOG( log_group, 3, (" Flip:%s Path= Seg=%d-\n", *flip?"T":"F", *cp ) ); return cp; } 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; + trkPos[0] = GetTrkEndPos(trk,ep1); + Rotate( &trkPos[0], xx->orig, -xx->angle ); + trkPos[0].x -= xx->orig.x; + trkPos[0].y -= xx->orig.y; + trkPos[1] = GetTrkEndPos(trk,ep2); + Rotate( &trkPos[1], xx->orig, -xx->angle ); + trkPos[1].x -= xx->orig.x; + trkPos[1].y -= xx->orig.y; + DIST_T dist = 1000.0; + char * path = NULL; + char * pName = "Not Found"; while ( cp[0] ) { - cp += strlen(cp)+1; //Ignore Path Name + char * pName1 = cp; // Save path name + cp += strlen(cp)+1; while ( cp[0] ) { - cp0 = cp; - epN = -1; + int segInx; + int segEP; + coOrd segPos[2]; + // Check if this sub-path endpts match the requested endpts + char * path1 = cp; + + // get the seg indices for the start and end GetSegInxEP( cp[0], &segInx, &segEP ); - if ( CheckTurnoutEndPoint( &xx->segs[segInx], pos1, segEP ) ) - epN = 1; - else if ( CheckTurnoutEndPoint( &xx->segs[segInx], pos2, segEP ) ) - epN = 0; + segPos[0] = GetSegEndPt( &xx->segs[segInx], segEP, FALSE, NULL ); 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; // If its reversed, set up to be flipped or noted - return cp0; //Found path between EPs + GetSegInxEP( cp[-1], &segInx, &segEP ); + segPos[1] = GetSegEndPt( &xx->segs[segInx], 1-segEP, FALSE, NULL ); + + // Find the closest seg end + for ( int inx = 0; inx<2; inx++ ) { + // Check 1st end + DIST_T dist1 = FindDistance( trkPos[0], segPos[inx] ); + if ( dist1 < connectDistance && dist1 < dist ) { + // Closest so far, Check 2nd end + DIST_T dist2 = FindDistance( trkPos[1], segPos[1-inx] ); + if ( dist2 > dist1 ) + // 2nd end is further away + dist1 = dist2; + if ( dist1 < connectDistance && dist1 < dist ) { + // both ends are closest + dist = dist1; + path = path1; + pName = pName1; + *flip = (inx==1); + } } } cp++; } cp++; } - return NULL; +LOG( log_group, 3, (" %s: %d..%d Flip:%s\n", pName, path?path[0]:-1, path?path[strlen(path)-1]:-1, *flip?"T":"F" ) ); + return path; } @@ -754,7 +779,7 @@ static int GroupShortestPathFunc( 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 ) ) + LOG( log_group, 4, ( " Add 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++ ) { @@ -793,7 +818,7 @@ if (log_shortPath<=0||logTable(log_shortPath).level<4) LOG( log_group, 2, ( " } } if ( ep1<0 || ep2<0 ) { -LOG( log_group, 2, ( " Remove: ep not found\n" ) ) +LOG( log_group, 4, ( " Remove: ep not found\n" ) ) pathElem_da.cnt = pathElemStart; return 0; } @@ -801,7 +826,7 @@ LOG( log_group, 2, ( " Remove: ep not found\n" ) ) 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 ) ) +LOG( log_group, 4, ( " Remove: duplicate path P%d\n", inx ) ) pathElem_da.cnt = pathElemStart; return 0; } @@ -814,7 +839,7 @@ LOG( log_group, 2, ( " Remove: duplicate path P%d\n", inx ) ) pp->ep1 = ep1; pp->ep2 = ep2; pathElemStart = pathElem_da.cnt; -LOG( log_group, 2, ( " Keep\n" ) ) +LOG( log_group, 4, ( " Keep\n" ) ) return 0; case SPTC_IGNNXTTRK: @@ -917,6 +942,87 @@ static BOOL_T CheckForBumper( return TRUE; } +typedef struct { + int inx; + wBool_t track; +} segInMap_t; +static dynArr_t segInMap_da; +#define segInMap(N) DYNARR_N( segInMap_t, segInMap_da, N) + +void AddToSegMap(int inx,wBool_t track) { + DYNARR_APPEND(segInMap_t,segInMap_da,10); + DYNARR_LAST(segInMap_t,segInMap_da).inx = inx; + DYNARR_LAST(segInMap_t,segInMap_da).track = track; +} + +void AddSegsToSegMap(int start, int end, wBool_t track) { + for (int i = start; i<= end; i++) { + AddToSegMap(i,track); + } +} + +static dynArr_t trackSegs_da; +#define trackSegs(N) DYNARR_N( trkSeg_t, trackSegs_da, N ) + + +trkSeg_p GetSegFromSegMap(int index) { + if (DYNARR_N( segInMap_t, segInMap_da, index).track) { + return &DYNARR_N(trkSeg_t,trackSegs_da,DYNARR_N( segInMap_t, segInMap_da, index).inx); + } else + return &DYNARR_N(trkSeg_t,tempSegs_da,DYNARR_N( segInMap_t, segInMap_da, index).inx); +} + +static dynArr_t outputSegs_da; +#define outputSegs(N) DYNARR_N( trkSeg_t, outputSegs_da, N) + +static void LogSeg( + trkSeg_p segP ) +{ + if ( segP == NULL ) { + LogPrintf( "<NULL>\n" ); + return; + } + LogPrintf( "%c: ", segP->type ); + switch ( segP->type ) { + case SEG_STRTRK: + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + LogPrintf( "[ %0.3f %0.3f ] [ %0.3f %0.3f ]\n", + segP->u.l.pos[0].x, segP->u.l.pos[0].y, + segP->u.l.pos[1].x, segP->u.l.pos[1].y ); + break; + case SEG_CRVLIN: + case SEG_CRVTRK: + LogPrintf( "R:%0.3f [ %0.3f %0.3f } A0:%0.3f A1:%0.3f\n", + segP->u.c.radius, + segP->u.c.center.x, segP->u.c.center.y, + segP->u.c.a0, segP->u.c.a1 ); + break; + default: + LogPrintf( "%c:\n", segP->type ); + } +} +/* + * GroupOk: create a TURNOUT or STRUCTURE from the selected objects + * 1 - Add selected tracks to groupTrk[] + * - Add each group trk's segments to trackSeg[] or tempSegs[] + * - Add all segs to segInMap[] + * - if no track segments goto step 9 + * 2 - Collect boundary endPts and sort them in tempEndPts[] + * 3 - Find shortest path between all endPts (if it exists) + * - For each track we add to the shortest path tree + * capture the sub-path elements (FindPathBtwEP) in pathElem[] + * 4 - Flip tracks so sub-path elements match up + * 5 - Create conflict map + * 6 - Flip paths to minimize the number of flipped segments + * 7 - Build the path ('P') string + * 8 - Build segment list, adjust endPts in tempEndPts[] + * 9 - create new TURNOUT/STRUCTURE definition + * 10 - write defn to xtrkcad.cus + * 11 - optionally replace grouped tracks with new defn + */ static void GroupOk( void * junk ) { @@ -925,7 +1031,6 @@ static void GroupOk( void * junk ) int inx; EPINX_T ep, epCnt, epN; coOrd orig, size; - long oldOptions; FILE * f = NULL; BOOL_T rc = TRUE; track_p trk, trk1; @@ -937,17 +1042,7 @@ static void GroupOk( void * junk ) 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; @@ -965,9 +1060,6 @@ static void GroupOk( void * junk ) 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 ); @@ -976,6 +1068,8 @@ static void GroupOk( void * junk ) DYNARR_RESET( trkEndPt_t, tempEndPts_da ); DYNARR_RESET( char, pathPtr_da ); + DYNARR_RESET( segInMap_t, segInMap_da); + ParamUpdate( &groupPG ); if ( groupManuf[0]==0 || groupDesc[0]==0 || groupPartno[0]==0 ) { NoticeMessage2( 0, MSG_GROUP_NONBLANK, _("Ok"), NULL ); @@ -991,9 +1085,10 @@ static void GroupOk( void * junk ) wDrawDelayUpdate( mainD.d, TRUE ); /* - * Collect tracks + * 1: Collect tracks */ trk = NULL; + int InInx = -1; while ( TrackIterate( &trk ) ) { if ( GetTrkSelected( trk ) ) { if ( IsTrack(trk) ) { @@ -1008,41 +1103,82 @@ static void GroupOk( void * junk ) if ( IsSegTrack(segPtr) ) { DYNARR_APPEND( trkSeg_t, trackSegs_da, 10 ); trackSegs(trackSegs_da.cnt-1) = *segPtr; + + AddToSegMap(trackSegs_da.cnt-1,TRUE); /* Single Track Seg - Note no Cornu*/ + RotateSegs( 1, &trackSegs(trackSegs_da.cnt-1), zero, xx->angle ); MoveSegs( 1, &trackSegs(trackSegs_da.cnt-1), xx->orig ); + } else { + int start = tempSegs_da.cnt; DrawSegs( &groupD, xx->orig, xx->angle, segPtr, 1, trackGauge, wDrawColorBlack ); + + AddSegsToSegMap(start,tempSegs_da.cnt-1,FALSE); /* Multiple Non-Track Segs */ } } } else if (GetTrkType(trk) == T_BEZIER || GetTrkType(trk) == T_BZRLIN ) { DYNARR_APPEND(trkSeg_t, trackSegs_da, 10); segPtr = &trackSegs(trackSegs_da.cnt-1); + GetBezierSegmentFromTrack(trk,segPtr); + + AddToSegMap(trackSegs_da.cnt-1,TRUE); // Add Single Bezier Track + } else if (GetTrkType(trk) == T_CORNU) { - GetBezierSegmentsFromCornu(trk,&trackSegs_da); //Only give back Bezier - cant be undone + + int start = trackSegs_da.cnt; + + GetBezierSegmentsFromCornu(trk,&trackSegs_da,TRUE); //Only give back Bezier - cant be undone + + AddSegsToSegMap(start,trackSegs_da.cnt-1,TRUE); /* Add Multiple Track Segs */ + } 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 ); + + AddToSegMap(trackSegs_da.cnt-1,TRUE); // Add One Track + 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 { + int start = tempSegs_da.cnt; + DrawTrack( trk, &groupD, wDrawColorBlack ); + + AddSegsToSegMap(start,tempSegs_da.cnt-1,FALSE); /* Multiple Non-Track Segs */ } } } +if ( log_group >= 1 && logTable(log_group).level >= 4 ) { + LogPrintf( "Track Segs:\n"); + for ( int inx = 0; inx < trackSegs_da.cnt; inx++ ) { + LogPrintf( " %d: ", inx+1 ); + LogSeg( &trackSegs(inx) ); + } + LogPrintf( "Other Segs:\n"); + for ( int inx = 0; inx < tempSegs_da.cnt; inx++ ) { + LogPrintf( " %d: ", inx+1 ); + LogSeg( &tempSegs(inx) ); + } +} +if ( log_group >= 1 && logTable(log_group).level >= 3 ) { + LogPrintf( "Combined Segs:\n" ); + for ( int inx = 0; inx<segInMap_da.cnt; inx++ ) { + LogPrintf( "%d: %s X%d - ", inx+1, segInMap(inx).track?"Track":"Other", segInMap(inx).inx ); + LogSeg( GetSegFromSegMap( inx ) ); + } +} if ( groupTrk_da.cnt>0 ) { if ( groupTrk_da.cnt > 128 ) { @@ -1084,6 +1220,17 @@ static void GroupOk( void * junk ) } } } +if ( log_group >= 1 && logTable(log_group).level >= 4 ) { + LogPrintf( "EndPts:\n" ); + for ( int inx=0; inx<tempEndPts_da.cnt; inx++ ) { + endPtP = &tempEndPts(inx); + LogPrintf( " [ %0.3f %0.3f ] A:%0.3f, T:%d.%d\n", + endPtP->pos.x, endPtP->pos.y, endPtP->angle, endPtP->track?GetTrkIndex(endPtP->track):-1, endPtP->index ); + } +} + /* + * 2: Collect EndPts + */ if ( tempEndPts_da.cnt <= 0 ) { NoticeMessage( _("No endpts"), _("Ok"), NULL ); wDrawDelayUpdate( mainD.d, FALSE ); @@ -1140,10 +1287,7 @@ static void GroupOk( void * junk ) 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); @@ -1151,9 +1295,17 @@ static void GroupOk( void * junk ) tempEndPts(tempEndPts_da.cnt-ep) = tempEndPt; } } +if ( log_group >= 1 && logTable(log_group).level >= 3 ) { + LogPrintf( "Sorted EndPts:\n" ); + for ( int inx=0; inx<tempEndPts_da.cnt; inx++ ) { + endPtP = &tempEndPts(inx); + LogPrintf( " [ %0.3f %0.3f ] A:%0.3f, T:%d.%d\n", + endPtP->pos.x, endPtP->pos.y, endPtP->angle, endPtP->track?GetTrkIndex(endPtP->track):-1, endPtP->index ); + } +} /* - * Find shortest Paths + * 3: Find shortest Paths */ for ( inx=0; inx<groupTrk_da.cnt; inx++ ) { trk = groupTrk(inx).trk; @@ -1162,13 +1314,37 @@ static void GroupOk( void * junk ) trk1 = GetTrkEndTrk(trk,ep); if ( trk1 == NULL || !GetTrkSelected(trk1) ) { /* boundary EP */ + LOG( log_group, 3, ("FindShortPath: T%d.%d\n", GetTrkIndex(trk), ep ) ); rc = FindShortestPath( trk, ep, FALSE, GroupShortestPathFunc, NULL ); } } } - +if ( log_group >= 1 && logTable(log_group).level >= 3 ) { + LogPrintf( "Shortest path:\n Group Tracks\n" ); + for ( int inx=0; inx<groupTrk_da.cnt; inx++ ) { + groupTrk_p gtp = &groupTrk(inx); + LogPrintf( " %d: T%d S%d-%d\n", inx, GetTrkIndex( gtp->trk ), gtp->segStart+1, gtp->segEnd+1 ); + } + LogPrintf( " Path Elem\n" ); + for ( int inx=0; inx<pathElem_da.cnt; inx++ ) { + ppp = &pathElem(inx); + LogPrintf( " %d: GTx: %d, EP: %d %d, F:%s, P:", + inx, ppp->groupInx, ppp->ep1, ppp->ep2, ppp->flip?"T":"F" ); + for ( PATHPTR_T cp = ppp->path; cp[0] || cp[1]; cp++ ) { + LogPrintf( " %d", *cp ); + } + LogPrintf( " 0\n" ); + } + LogPrintf( " Path\n" ); + for ( int inx=0; inx<path_da.cnt; inx++ ) { + path_p pp = &path(inx); + LogPrintf( " %d: PE: %d-%d, EP: %d-%d, Conf: %d, InGrp: %s, Done: %s\n", + inx, pp->pathElemStart, pp->pathElemEnd, pp->ep1, pp->ep2, + pp->conflicts, pp->inGroup?"T":"F", pp->done?"T":"F" ); + } +} /* - * Flip paths so they align + * 4: Flip paths so they align */ if ( path_da.cnt == 0 ) { NoticeMessage( _("No paths"), _("Ok"), NULL ); @@ -1226,11 +1402,11 @@ LOG( log_group, 1, ( "P%d aligns flipped with P%d\n", pinx, pinx2 ) ); path(inx).done = TRUE; } } -if ( log_group >= 1 && logTable(log_group).level > log_group ) { +if ( log_group >= 1 && logTable(log_group).level >= 1 ) { 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 ); + 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 ); @@ -1239,62 +1415,9 @@ if ( log_group >= 1 && logTable(log_group).level > log_group ) { } } -#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 + * 5: 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) ); @@ -1353,42 +1476,18 @@ if ( log_group >= 1 && logTable(log_group).level > log_group ) { } } -if ( log_group >= 1 && logTable(log_group).level > log_group ) { +if ( log_group >= 1 && logTable(log_group).level >= 3 ) { 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( " %d: %d", ginx, 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 + * 6: 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) ); @@ -1414,15 +1513,17 @@ if ( log_group >= 1 && logTable(log_group).level > log_group ) { /* * Flip each segment that is used as flipped more than not */ +LOG( log_group, 3, ( "Flipping Segments:" ) ); 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 ); +LOG( log_group, 3, ( " %d", pinx ) ); } } +LOG( log_group, 3, ( "\n" ) ); /* - * Output Path lists + * 7: Output Path lists */ for ( pinx=0; pinx<groupCnt; pinx++ ) { sprintf( message, "P%d", pinx ); @@ -1431,8 +1532,10 @@ LOG( log_group, 1, ( "Flipping Segment %d\n", pinx+1 ) ); memcpy( &pathPtr(inx), message, pathPtr_da.cnt-inx ); for ( ginx=0; groupMap(pinx,ginx) >= 0; ginx++ ) { pp = &path(groupMap(pinx,ginx)); + LOG( log_group, 3, (" Group Map(%d, %d): elem %d-%d, EP %d %d, Conflicts %d, inGrp %d, Done: %s\n", pinx, ginx, pp->pathElemStart, pp->pathElemEnd, pp->ep1, pp->ep2, pp->conflicts, pp->inGroup, pp->done?"T":"F" ) ); for ( pinx2=pp->pathElemEnd; pinx2>=pp->pathElemStart; pinx2-- ) { ppp = &pathElem( pinx2 ); + LOG( log_group, 3, (" PE %d: GI %d, EP %d %d, Flip %d =", pinx2, ppp->groupInx, ppp->ep1, ppp->ep2, ppp->flip )); groupP = &groupTrk( ppp->groupInx ); path = ppp->path; flip = ppp->flip; @@ -1453,7 +1556,9 @@ LOG( log_group, 1, ( "Flipping Segment %d\n", pinx+1 ) ); if ( flip1 ) pathChar = - pathChar; pathPtr(pathPtr_da.cnt-1) = pathChar; path += (flip?-1:1); + LOG( log_group, 3, (" %d", pathChar ) ); } + LOG( log_group, 3, ("\n") ); } DYNARR_APPEND( char, pathPtr_da, 10 ); pathPtr(pathPtr_da.cnt-1) = 0; @@ -1468,43 +1573,49 @@ LOG( log_group, 1, ( "Flipping Segment %d\n", pinx+1 ) ); groupSimpleTurnout: /* - * Copy and Reorigin Segments + * 8: Copy and Reorigin Segments - Start by putting them out in the original order */ - 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 ); + + + DYNARR_RESET(trkSeg_t, outputSegs_da); + for (int i=0; i<segInMap_da.cnt;i++) { + DYNARR_APPEND(trkSeg_t,outputSegs_da,10); + trkSeg_p from_p = GetSegFromSegMap(i); + trkSeg_p to_p = &DYNARR_LAST(trkSeg_t, outputSegs_da); + memcpy((void *)to_p,(void *)from_p,sizeof( trkSeg_t)); } - GetSegBounds( zero, 0, trackSegs_da.cnt, &trackSegs(0), &orig, &size ); + CloneFilledDraw( outputSegs_da.cnt, outputSegs_da.ptr, FALSE ); + + GetSegBounds( zero, 0, outputSegs_da.cnt, &outputSegs(0), &orig, &size ); orig.x = - tempEndPts(0).pos.x; orig.y = - tempEndPts(0).pos.y; - MoveSegs( trackSegs_da.cnt, &trackSegs(0), orig ); + MoveSegs( outputSegs_da.cnt, &outputSegs(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 + * 9: Final: create new definition + */ + + CheckPaths( outputSegs_da.cnt, &outputSegs(0), path ); + + to = CreateNewTurnout( curScaleName, groupTitle, outputSegs_da.cnt, &outputSegs(0), pathLen, path, tempEndPts_da.cnt, &tempEndPts(0), NULL, TRUE ); + + /* + * 10: Write defn to xtrkcad.cus */ - 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) ); + rc &= WriteCompoundPathsEndPtsSegs( f, path, outputSegs_da.cnt, &outputSegs(0), tempEndPts_da.cnt, &tempEndPts(0) ); } if ( groupReplace ) { + /* + * 11: Replace defn + */ UndoStart( _("Group Tracks"), "group" ); orig.x = - orig.x; orig.y = - orig.y; @@ -1528,8 +1639,7 @@ groupSimpleTurnout: 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 ); + trk = NewCompound( T_TURNOUT, 0, orig, 0.0, to->title, tempEndPts_da.cnt, &tempEndPts(0), NULL, pathLen, (char *)path, outputSegs_da.cnt, &outputSegs(0) ); SetTrkVisible( trk, TRUE ); for ( ep=0; ep<tempEndPts_da.cnt; ep++ ) { @@ -1544,18 +1654,15 @@ groupSimpleTurnout: } 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; + + orig.x = - orig.x-groupOriginX; //Include orig offset + orig.y = - orig.y-groupOriginY; 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 ) { @@ -1570,7 +1677,7 @@ groupSimpleTurnout: } 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) ); + trk = NewCompound( T_STRUCTURE, 0, orig, 0.0, groupTitle, 0, NULL, NULL, 0, "", tempSegs_da.cnt, &tempSegs(0) ); SetTrkVisible( trk, TRUE ); DrawNewTrack( trk ); EnableCommands(); @@ -1594,10 +1701,14 @@ EXPORT void DoGroup( void ) xx = NULL; groupSegCnt = 0; groupCompoundCount = 0; + groupOriginX = 0.0; + groupOriginY = 0.0; + BOOL_T isTurnout = FALSE; while ( TrackIterate( &trk ) ) { if ( GetTrkSelected( trk ) ) { trkType = GetTrkType(trk); + if ( IsTrack(trk) ) isTurnout = TRUE; if ( trkType == T_TURNOUT || trkType == T_STRUCTURE ) { xx = GetTrkExtraData(trk); groupSegCnt += xx->segCnt; @@ -1618,6 +1729,18 @@ EXPORT void DoGroup( void ) groupW = ParamCreateDialog( &groupPG, MakeWindowTitle(_("Group Objects")), _("Ok"), GroupOk, wHide, TRUE, NULL, F_BLOCK, NULL ); groupD.dpi = mainD.dpi; } + if (isTurnout) { + groupPLs[4].option |= PDO_DLGIGNORE; + wControlShow( groupPLs[4].control, FALSE ); + groupPLs[5].option |= PDO_DLGIGNORE; + wControlShow( groupPLs[5].control, FALSE ); + } else { + groupPLs[4].option &= ~PDO_DLGIGNORE; + wControlShow( groupPLs[4].control, TRUE ); + groupPLs[5].option &= ~PDO_DLGIGNORE; + wControlShow( groupPLs[5].control, TRUE ); + } + ParamLoadControls( &groupPG ); wShow( groupW ); } diff --git a/app/bin/chndldto.c b/app/bin/chndldto.c index fa88398..a0f2d6b 100644 --- a/app/bin/chndldto.c +++ b/app/bin/chndldto.c @@ -91,7 +91,6 @@ static STATUS_T CmdHandLaidTurnout( wAction_t action, coOrd pos ) 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; @@ -102,7 +101,6 @@ static STATUS_T CmdHandLaidTurnout( wAction_t action, coOrd pos ) 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 ); @@ -141,10 +139,8 @@ static STATUS_T CmdHandLaidTurnout( wAction_t action, coOrd pos ) 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) @@ -273,7 +269,6 @@ PTRACE(( " a2=%0.1f rA1=%0.1f\n", angle2, reverseA1 )) 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 ); @@ -334,7 +329,6 @@ PTRACE(( " a2=%0.1f rA1=%0.1f\n", angle2, reverseA1 )) 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; @@ -348,12 +342,6 @@ PTRACE(( " a2=%0.1f rA1=%0.1f\n", angle2, reverseA1 )) 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; } @@ -367,5 +355,5 @@ PTRACE(( " a2=%0.1f rA1=%0.1f\n", angle2, reverseA1 )) EXPORT void InitCmdHandLaidTurnout( wMenu_p menu ) { - AddMenuButton( menu, CmdHandLaidTurnout, "cmdHandLaidTurnout", _("HandLaidTurnout"), wIconCreatePixMap(hndldto_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_HNDLDTO, NULL ); + AddMenuButton( menu, CmdHandLaidTurnout, "cmdHandLaidTurnout", _("HandLaidTurnout"), wIconCreatePixMap(hndldto_xpm), LEVEL0_50, IC_STICKY|IC_INITNOTSTICKY|IC_POPUP2, ACCL_HNDLDTO, NULL ); } diff --git a/app/bin/chotbar.c b/app/bin/chotbar.c index 31a19ad..0b9a327 100644 --- a/app/bin/chotbar.c +++ b/app/bin/chotbar.c @@ -27,6 +27,7 @@ #include "compound.h" #include "fileio.h" #include "messages.h" +#include "ccornu.h" #include "track.h" EXPORT DIST_T curBarScale = -1; @@ -57,7 +58,7 @@ typedef struct { DIST_T labelW; coOrd size; coOrd orig; - BOOL_T isTrack; + BOOL_T isFixed; void * context; hotBarProc_t proc; DIST_T barScale; @@ -72,12 +73,15 @@ static int hotBarCurrEnds[2] = { -1, -1 }; #define hotBarCurrEnd (hotBarCurrEnds[programMode]) static DIST_T hotBarWidth = 0.0; -static void HotBarHighlight( int inx ) +static void HotBarHighlight( int inx, DIST_T fixed_x ) { wPos_t x0; - if ( inx >= hotBarCurrStart && inx < hotBarCurrEnd ) { - x0 = (wPos_t)((hotBarMap(inx).x-hotBarMap((int)hotBarCurrStart).x)*hotBarD.dpi); - wDrawFilledRectangle( hotBarD.d, x0, 0, (wPos_t)(hotBarMap(inx).w*hotBarD.dpi-2), hotBarHeight, wDrawColorBlack, wDrawOptTemp ); + if ( inx == 0 && hotBarMap_da.cnt>0 && hotBarMap(0).isFixed) { + x0 = (wPos_t)0; + wDrawFilledRectangle( hotBarD.d, x0, 0, (wPos_t)(hotBarMap(0).w*hotBarD.dpi-2), hotBarHeight, wDrawColorBlack, wDrawOptTransparent ); + } else if ( inx >= hotBarCurrStart && inx < hotBarCurrEnd ) { + x0 = (wPos_t)((hotBarMap(inx).x-hotBarMap((int)hotBarCurrStart).x + (inx>0?fixed_x:0))*hotBarD.dpi); + wDrawFilledRectangle( hotBarD.d, x0, 0, (wPos_t)(hotBarMap(inx).w*hotBarD.dpi-2), hotBarHeight, wDrawColorBlack, wDrawOptTransparent ); } } @@ -105,11 +109,38 @@ static void RedrawHotBar( wDraw_p dd, void * data, wPos_t w, wPos_t h ) if ( hotBarLabels && !hotBarFp ) hotBarFp = wStandardFont( F_HELV, FALSE, FALSE ); wPos_t textSize = wMessageGetHeight(0L); + DIST_T fixed_x = 0.0; + if (hotBarCurrStart>0 && hotBarMap_da.cnt>0 && hotBarMap(0).isFixed) { //Do fixed element first - Cornu + tbm = &hotBarMap(0); + barScale = tbm->barScale; + x = 0.0; + orig.y = hh/2.0*barScale - tbm->size.y/2.0 - tbm->orig.y; + if ( hotBarLabels ) { + orig.y += textSize/hotBarD.dpi*barScale; + if ( tbm->labelW > tbm->objectW ) { + fixed_x = tbm->labelW; + x += (tbm->labelW-tbm->objectW)/2; + } else fixed_x = tbm->objectW; + } else fixed_x = tbm->objectW; + x *= barScale; + orig.x = x; + hotBarD.scale = barScale; + hotBarD.size.x = barWidth*barScale; + hotBarD.size.y = barHeight*barScale; + tbm->proc( HB_DRAW, tbm->context, &hotBarD, &orig ); + if ( hotBarLabels ) { + hotBarD.scale = 1.0; + orig.x = 0.0; + orig.y = 2.0/hotBarD.dpi; //Draw Label under icon + DrawString( &hotBarD, orig, 0.0, tbm->proc( HB_BARTITLE, tbm->context, NULL, NULL ), hotBarFp, hotBarFs, drawColorBlack ); + } + + } for ( inx=hotBarCurrStart; inx < hotBarMap_da.cnt; inx++ ) { tbm = &hotBarMap(inx); barScale = tbm->barScale; - x = tbm->x - hotBarMap(hotBarCurrStart).x; - if ( x + tbm->w > barWidth ) { + x = tbm->x - hotBarMap(hotBarCurrStart).x + fixed_x; //Add space for fixed at start + if ( x + tbm->w + fixed_x > barWidth ) { break; } orig.y = hh/2.0*barScale - tbm->size.y/2.0 - tbm->orig.y; @@ -120,6 +151,7 @@ static void RedrawHotBar( wDraw_p dd, void * data, wPos_t w, wPos_t h ) } } x *= barScale; + x -= tbm->orig.x; orig.x = x; hotBarD.scale = barScale; hotBarD.size.x = barWidth*barScale; @@ -127,14 +159,15 @@ static void RedrawHotBar( wDraw_p dd, void * data, wPos_t w, wPos_t h ) tbm->proc( HB_DRAW, tbm->context, &hotBarD, &orig ); if ( hotBarLabels ) { hotBarD.scale = 1.0; - orig.x = tbm->x - hotBarMap(hotBarCurrStart).x; + orig.x = tbm->x - hotBarMap(hotBarCurrStart).x + fixed_x; orig.y = 2.0/hotBarD.dpi; //Draw Label under icon DrawString( &hotBarD, orig, 0.0, tbm->proc( HB_BARTITLE, tbm->context, NULL, NULL ), hotBarFp, hotBarFs, drawColorBlack ); } } hotBarCurrEnd = inx; - if (hotBarCurrSelect >= hotBarCurrStart && hotBarCurrSelect < hotBarCurrEnd ) - HotBarHighlight( hotBarCurrSelect ); + if ((hotBarCurrSelect==0 && hotBarMap_da.cnt>0 && hotBarMap(0).isFixed) || + ((hotBarCurrSelect >= hotBarCurrStart) && (hotBarCurrSelect < hotBarCurrEnd)) ) + HotBarHighlight( hotBarCurrSelect, fixed_x ); /* else hotBarCurrSelect = -1;*/ wControlActive( (wControl_p)hotBarRightB, hotBarCurrEnd < hotBarMap_da.cnt ); @@ -223,17 +256,33 @@ static void SelectHotBar( wDraw_p d, void * context, wAction_t action, wPos_t w, wMenuPopupShow( hotbarPopupM ); return; } - x = w/hotBarD.dpi + hotBarMap(hotBarCurrStart).x; - for ( inx=hotBarCurrStart; inx<hotBarCurrEnd; inx++ ) { - if ((x >= hotBarMap(inx).x) && //leave spaces between buttons - (x <= hotBarMap(inx).x + hotBarMap(inx).w )) { - break; + inx = -1; + x = hotBarMap(0).x; + DIST_T fixed_x = 0.0; + if (hotBarCurrStart>0 && hotBarMap_da.cnt>0 && hotBarMap(0).isFixed) { + fixed_x = hotBarMap(0).w; + x = w/hotBarD.dpi + hotBarMap(0).x; + if ( (x>= hotBarMap(0).x) && + (x <=hotBarMap(0).w )) inx = 0; //Match on fixed + } + if (inx<0){ //NoMatch + x = w/hotBarD.dpi + hotBarMap(hotBarCurrStart).x; + for ( inx=hotBarCurrStart; inx<hotBarCurrEnd; inx++ ) { + if ((x >= hotBarMap(inx).x + fixed_x) && //leave spaces between buttons + (x <= hotBarMap(inx).x + hotBarMap(inx).w + fixed_x )) { + break; + } } } + if (inx >= hotBarCurrEnd) return; tbm = &hotBarMap(inx); - px = (wPos_t)((tbm->x-hotBarMap(hotBarCurrStart).x)*hotBarD.dpi); + if (inx==0) { + px = (wPos_t)((tbm->x-hotBarMap(0).x)*hotBarD.dpi); + } else { + px = (wPos_t)(((tbm->x-hotBarMap(hotBarCurrStart).x)+fixed_x)*hotBarD.dpi); + } px += (wPos_t)(tbm->w*hotBarD.dpi/2); titleP = tbm->proc( HB_LISTTITLE, tbm->context, NULL, NULL ); px -= wLabelWidth( titleP ) / 2; @@ -243,12 +292,14 @@ static void SelectHotBar( wDraw_p d, void * context, wAction_t action, wPos_t w, pos.x = mainD.size.x+mainD.orig.x; pos.y = mainD.size.y+mainD.orig.y; if ( hotBarCurrSelect >= 0 ) { - HotBarHighlight( hotBarCurrSelect ); + //HotBarHighlight( hotBarCurrSelect ); hotBarCurrSelect = -1; + RedrawHotBar(hotBarD.d, NULL, 0, 0 ); } + tbm->proc( HB_SELECT, tbm->context, NULL, NULL ); hotBarCurrSelect = inx; - HotBarHighlight( hotBarCurrSelect ); + HotBarHighlight( hotBarCurrSelect, fixed_x ); if (recordF) { fprintf( recordF, "HOTBARSELECT %s\n", tbm->proc( HB_FULLTITLE, tbm->context, NULL, NULL ) ); } @@ -296,8 +347,9 @@ static void SelectHotBar( wDraw_p d, void * context, wAction_t action, wPos_t w, EXPORT void HotBarCancel( void ) { if ( hotBarCurrSelect >= 0 ) - HotBarHighlight( hotBarCurrSelect ); + //HotBarHighlight( hotBarCurrSelect ); hotBarCurrSelect = -1; + RedrawHotBar(hotBarD.d, NULL, 0, 0 ); } @@ -306,18 +358,23 @@ static BOOL_T HotBarSelectPlayback( char * line ) int inx; hotBarMap_t * tbm; while (*line && isspace((unsigned char)*line) ) line++; + DIST_T fixed_x = 0; for ( inx=0; inx<hotBarMap_da.cnt; inx++ ) { tbm = &hotBarMap(inx); + if (inx == 0 && hotBarMap_da.cnt>0 && hotBarMap(0).isFixed) { + fixed_x = hotBarMap(0).w; + } if ( strcmp( tbm->proc( HB_FULLTITLE, tbm->context, NULL, NULL ), line) == 0) { if ( hotBarCurrSelect >= 0 ) { - HotBarHighlight( hotBarCurrSelect ); + //HotBarHighlight( hotBarCurrSelect ); + RedrawHotBar(hotBarD.d, NULL, 0, 0 ); } hotBarCurrSelect = inx; if ( hotBarCurrSelect < hotBarCurrStart || hotBarCurrSelect > hotBarCurrEnd ) { hotBarCurrStart = hotBarCurrSelect; RedrawHotBar( hotBarD.d, NULL, 0, 0 ); } - HotBarHighlight( hotBarCurrSelect ); + HotBarHighlight( hotBarCurrSelect, fixed_x ); hotBarMap(inx).proc( HB_SELECT, hotBarMap(inx).context, NULL, NULL ); FakeDownMouseState(); return TRUE; @@ -347,6 +404,7 @@ EXPORT void AddHotBarElement( coOrd size, coOrd orig, BOOL_T isTrack, + BOOL_T isFixed, DIST_T barScale, void * context, hotBarProc_t proc_p ) @@ -360,10 +418,13 @@ EXPORT void AddHotBarElement( } if (barScale <= 0) { - if (isTrack) + if (!isTrack) + barScale = size.y/((double)hotBarDrawHeight/hotBarD.dpi); + else if (isTrack) { barScale = (trackGauge>0.1)?trackGauge*24:10; - else - barScale = size.y/((double)hotBarDrawHeight/hotBarD.dpi-0.07); + if (size.y >= size.x) + barScale = size.y/((double)hotBarDrawHeight/hotBarD.dpi); + } } DYNARR_APPEND( hotBarMap_t, hotBarMap_da, 10 ); tbm = &hotBarMap(hotBarMap_da.cnt-1); @@ -376,6 +437,7 @@ EXPORT void AddHotBarElement( tbm->orig = orig; tbm->proc = proc_p; tbm->barScale = barScale; + tbm->isFixed = isFixed; tbm->w = tbm->objectW = size.x/barScale + 5.0/hotBarD.dpi; tbm->labelW = 0; tbm->x = hotBarWidth; @@ -409,6 +471,8 @@ static void ChangeHotBar( long changes ) DYNARR_RESET( hotBarMap_t, hotBarMap_da ); curContentsLabel[0] = '\0'; if ( programMode == MODE_DESIGN ) { + if (showFlexTrack) + AddHotBarCornu(); AddHotBarTurnouts(); AddHotBarStructures(); } else { @@ -446,6 +510,13 @@ EXPORT void LayoutHotBar( void * redraw ) wWinGetSize( mainW, &winWidth, &winHeight ); hotBarHeight = hotBarDrawHeight; + double scaleicon; + wPrefGetFloat(PREFSECTION, LARGEICON, &scaleicon, 1.0); + if (scaleicon<1.0) scaleicon=1.0; + if (scaleicon>2.0) scaleicon=2.0; + if (scaleicon>1.0) { + hotBarHeight = hotBarHeight*scaleicon; + } if ( hotBarLabels) { hotBarHeight += wMessageGetHeight(0L); } diff --git a/app/bin/cjoin.c b/app/bin/cjoin.c index 8cfa3d4..71f4dae 100644 --- a/app/bin/cjoin.c +++ b/app/bin/cjoin.c @@ -1,5 +1,5 @@ /* - * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cjoin.c,v 1.4 2008-03-06 19:35:05 m_fischer Exp $ + * $Header: /home/dave/Source/xtrkcad_5_1_2a/app/bin/RCS/cjoin.c,v 1.3 2019/07/24 15:11:51 dave Exp $ * * JOINS * @@ -39,7 +39,7 @@ #include "cselect.h" #include "fileio.h" - +static BOOL_T debug = 0; static int log_join = 0; typedef struct { curveType_e type; @@ -53,25 +53,18 @@ typedef struct { static struct { STATE_T state; int joinMoveState; + BOOL_T cornuMode; 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]; + dynArr_t anchors; } Dj; @@ -169,7 +162,8 @@ LOG( log_join, 2, (" = CURVE @ Pc=[%0.3f %0.3f] R=%0.3f A0=%0.3f A1=%0.3f Fli d = D2R(res->arcA1); if (d < 0.0) d = 2*M_PI + d; - InfoMessage( _("Curved Track: Radius=%s Length=%s"), + if (!debug) + InfoMessage( _("Curved Track: Radius=%s Length=%s"), FormatDistance(res->arcR), FormatDistance(res->arcR*d) ); return TRUE; @@ -213,7 +207,7 @@ LOG( log_join, 3, (" p1=[%0.3f %0.3f] aa=%0.3f a=%0.3f\n", /* 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"), + if (!debug) InfoMessage( _("Straight Track: Length=%s Angle=%0.3f"), FormatDistance(FindDistance( pt, pos1 )), PutAngle(FindAngle( pt, pos1 )) ); res->type = curveTypeStraight; res->pos[0]=pt; @@ -255,7 +249,7 @@ LOG( log_join, 3, (" A0=%0.3f A1=%0.3f R=%0.3f\n", res->arcA0, res->arcA1, d = D2R(res->arcA1); if (d < 0.0) d = 2*M_PI + d; - InfoMessage( _("Curved Track: Radius=%s Length=%s Angle=%0.3f"), + if (!debug) InfoMessage( _("Curved Track: Radius=%s Length=%s Angle=%0.3f"), FormatDistance(res->arcR), FormatDistance(res->arcR*d), PutAngle(res->arcA1) ); res->type = curveTypeCurve; } @@ -302,11 +296,11 @@ static STATUS_T AdjustJoint( 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); + a0 = FindAngle( Dj.inp[0].params.arcP, Dj.jRes.pos[0] ); 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 ) ) +LOG( log_join, 2, (" Move P0 X%0.3f A%0.3f P1 X%0.3f A%0.3f SC%d FL%d\n", + Dj.jointD[0].x, a0, Dj.jointD[1].x, a1, + Dj.jointD[0].Scurve, Dj.jointD[0].flip ) ) } else { pc = Dj.inp[0].params.arcP; } @@ -368,7 +362,8 @@ LOG( log_join, 2, (" Move P0 X%0.3f A%0.3f P1 X%0.3f A%0.3f\n", } d -= l; if ( d <= minLength ) { - InfoMessage( _("Connecting track is too short by %0.3f"), PutDim(fabs(minLength-d)) ); + if (!debug) + InfoMessage( _("Connecting track is too short by %0.3f"), PutDim(fabs(minLength-d)) ); return FALSE; } @@ -415,7 +410,6 @@ static STATUS_T DoMoveToJoin( coOrd pos ) _("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) ) { @@ -423,7 +417,6 @@ static STATUS_T DoMoveToJoin( coOrd pos ) ? _("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 @@ -432,6 +425,372 @@ static STATUS_T DoMoveToJoin( coOrd pos ) return C_TERMINATE; } +typedef enum {NO_LINE,FIRST_END,HAVE_LINE,HAVE_SECOND_LINE} LineState_t; + +static struct { + LineState_t line_state; + int joinMoveState; + track_p curr_line; + struct { + TRKTYP_T realType; + track_p line; + coOrd pos; + coOrd end; + int cnt; + } inp[2]; + joinRes_t jRes; + coOrd inp_pos[2]; + dynArr_t anchors_da; + trackParams_t params; + dynArr_t newLine; + } Dl; + +#define anchors(N) DYNARR_N(trkSeg_t,Dl.anchors_da,N) + +void AddAnchorEnd(coOrd p) { + DIST_T d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,Dl.anchors_da,1); + trkSeg_p a = &DYNARR_LAST(trkSeg_t,Dl.anchors_da); + a->type = SEG_CRVLIN; + a->width = 0; + a->u.c.a0 = 0.0; + a->u.c.a1 = 360.0; + a->u.c.center = p; + a->u.c.radius = d/2; + a->color = wDrawColorPowderedBlue; +} + + +static STATUS_T CmdJoinLine( + wAction_t action, + coOrd pos ) +/* + * Join 2 lines. + */ +{ + + switch (action&0xFF) { + case C_START: + InfoMessage( _("Left click - Select first draw object end") ); + Dl.line_state = NO_LINE; + Dl.joinMoveState = 0; + tempSegs_da.cnt = 0; + DYNARR_RESET(trkSeg_t,Dl.newLine); + Dl.curr_line = NULL; + return C_CONTINUE; + case wActionMove: + DYNARR_RESET(trkSeg_t,Dl.anchors_da); + Dl.curr_line = NULL; + coOrd pos1= pos; + Dl.curr_line = OnTrack( &pos1, FALSE, FALSE ); + if (!Dl.curr_line) return C_CONTINUE; + if (IsTrack(Dl.curr_line)) { + Dl.curr_line = NULL; + return C_CONTINUE; + } + if (!QueryTrack(Dl.curr_line,Q_GET_NODES)) { + Dl.curr_line = NULL; + return C_CONTINUE; + } + if (!GetTrackParams(PARAMS_NODES,Dl.curr_line,pos,&Dl.params)) { + Dl.curr_line = NULL; + return C_CONTINUE; + } + if ( (Dl.line_state != NO_LINE) && + (Dl.inp[0].line == Dl.curr_line) && + (IsClose(FindDistance(Dl.inp[0].pos,Dl.params.lineOrig)) ) ) { + Dl.curr_line = NULL; + } else { + AddAnchorEnd(Dl.params.lineOrig); + } + break; + case C_DOWN: + DYNARR_RESET(trkSeg_t,Dl.anchors_da); + Dl.curr_line = NULL; + if (Dl.line_state == NO_LINE) { + Dl.curr_line = OnTrack( &pos, FALSE, FALSE); + if (!Dl.curr_line || IsTrack(Dl.curr_line)) { + InfoMessage( _("Not a line - Try again") ); + return C_CONTINUE; + } + if (!QueryTrack(Dl.curr_line,Q_GET_NODES)) return C_CONTINUE; + if (!GetTrackParams(PARAMS_NODES,Dl.curr_line,pos,&Dl.params)) return C_CONTINUE; + Dl.line_state = HAVE_LINE; + Dl.inp[0].line = Dl.curr_line; + Dl.inp[0].pos = Dl.params.lineOrig; + Dl.inp[0].end = Dl.params.lineEnd; + DYNARR_SET(trkSeg_t,Dl.newLine,1); + + DYNARR_LAST(trkSeg_t,Dl.newLine).type = SEG_POLY; + DYNARR_LAST(trkSeg_t,Dl.newLine).color = wDrawColorBlack; + DYNARR_LAST(trkSeg_t,Dl.newLine).width = 0; + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.polyType = POLYLINE; + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.pts = MyMalloc(sizeof(pts_t)*Dl.params.nodes.cnt); + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.cnt = Dl.params.nodes.cnt; + //Copy in reverse as we want this point to be last + for (int i=0;i<Dl.params.nodes.cnt;i++) { + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.pts[i].pt = DYNARR_N(coOrd,Dl.params.nodes,Dl.params.nodes.cnt-1-i); + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.pts[i].pt_type = wPolyLineStraight; + } + InfoMessage( _("Left click - Select second object end") ); + } else { + Dl.curr_line = OnTrack( &pos, FALSE, FALSE ); + if (!Dl.curr_line || IsTrack(Dl.curr_line)) { + InfoMessage( _("Not a line - Try again") ); + return C_CONTINUE; + } + if (!QueryTrack(Dl.curr_line,Q_GET_NODES)) return C_CONTINUE; + if (!GetTrackParams(PARAMS_NODES,Dl.curr_line,pos,&Dl.params)) return C_CONTINUE; + if (Dl.curr_line == Dl.inp[0].line) { + if ((Dl.params.lineOrig.x == Dl.inp[0].pos.x) && + (Dl.params.lineOrig.y == Dl.inp[0].pos.y)) { + InfoMessage( _("Same draw object and same endpoint - Try again") ); + return C_CONTINUE; + } + } + Dl.line_state = HAVE_SECOND_LINE; + Dl.inp[1].line = Dl.curr_line; + Dl.inp[1].pos = Dl.params.lineOrig; + Dl.inp[1].end = Dl.params.lineEnd; + int old_cnt = DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.cnt; + BOOL_T join_near = FALSE; + if (Dl.inp[1].line == Dl.inp[0].line) { + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.pts = MyRealloc(DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.pts,sizeof(pts_t)*(old_cnt+1)); + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.pts[old_cnt] = DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.pts[0]; // Joined up Polygon + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.cnt += 1; + } else { + if (IsClose(FindDistance(Dl.inp[0].pos,Dl.inp[1].pos))) + join_near = TRUE; + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.pts = MyRealloc(DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.pts,sizeof(pts_t)*(old_cnt+Dl.params.nodes.cnt-join_near)); + //Copy forwards as this point is first + for (int i=join_near;i<Dl.params.nodes.cnt;i++) { + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.pts[i-join_near+old_cnt].pt = DYNARR_N(coOrd,Dl.params.nodes,i); + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.pts[i-join_near+old_cnt].pt_type = wPolyLineStraight; + } + DYNARR_LAST(trkSeg_t,Dl.newLine).u.p.cnt += Dl.params.nodes.cnt-join_near; + } + } + UndrawNewTrack(Dl.curr_line); + Dl.curr_line = NULL; + break; + case C_MOVE: + break; + case C_UP: + if (Dl.line_state != HAVE_SECOND_LINE) return C_CONTINUE; + Dl.line_state = NO_LINE; + UndoStart(_("Create PolyLine"), "newPolyLine"); + track_p newTrack = MakePolyLineFromSegs( zero, 0.0, &Dl.newLine ); + DeleteTrack(Dl.inp[0].line,FALSE); + if (Dl.inp[0].line != Dl.inp[1].line) + DeleteTrack(Dl.inp[1].line,FALSE); + UndoEnd(); + DrawNewTrack(newTrack); + CleanSegs(&Dl.newLine); + Dl.curr_line = NULL; + return C_TERMINATE; + break; + case C_CANCEL: + CleanSegs(&Dl.newLine); + Dl.line_state = NO_LINE; + Dl.curr_line = NULL; + break; + case C_REDRAW: + if (Dl.line_state != NO_LINE) DrawSegs(&tempD,zero,0.0,((trkSeg_t*)Dl.newLine.ptr), Dl.newLine.cnt, trackGauge, wDrawColorPreviewSelected); + if (Dl.curr_line) DrawTrack(Dl.curr_line,&tempD,wDrawColorPreviewSelected); + if (Dl.anchors_da.cnt>0) + DrawSegs( &tempD, zero, 0.0, &anchors(0), Dl.anchors_da.cnt, trackGauge, wDrawColorPreviewSelected ); + break; + case C_TEXT: + case C_OK: + default:; + + } + + + return C_CONTINUE; + +} + +void AnchorTempLine(coOrd p0, coOrd p1) { + DYNARR_APPEND(trkSeg_t,Dj.anchors,1); + trkSeg_p p = &DYNARR_LAST(trkSeg_t,Dj.anchors); + p->type = SEG_STRLIN; + p->color = wDrawColorBlue; + p->width = 0.0; + p->u.l.pos[0] = p0; + p->u.l.pos[1] = p1; +} + +void AnchorTempCircle(coOrd center,DIST_T radius, ANGLE_T a0, ANGLE_T a1) { + DYNARR_APPEND(trkSeg_t,Dj.anchors,1); + trkSeg_p p = &DYNARR_LAST(trkSeg_t,Dj.anchors); + p->type = SEG_CRVLIN; + p->color = wDrawColorBlue; + p->width = 0.0; + p->u.c.a0 =a0; + p->u.c.a1 = a1; + p->u.c.radius = radius; + p->u.c.center = center; +} + +void AnchorPoint(coOrd center) { + DYNARR_APPEND(trkSeg_t,Dj.anchors,1); + trkSeg_p p = &DYNARR_LAST(trkSeg_t,Dj.anchors); + p->type = SEG_CRVLIN; + p->color = wDrawColorAqua; + p->width = 0.0; + p->u.c.a0 =0.0; + p->u.c.a1 = 360.0; + p->u.c.radius = mainD.scale/4; + p->u.c.center = center; +} + +static DIST_T desired_radius = 0.0; + +static paramFloatRange_t r_0_10000 = { 0.0, 100000.0 }; +static paramData_t joinPLs[] = { +#define joinRadPD (joinPLs[0]) +#define joinRadI 0 + { PD_FLOAT, &desired_radius, "radius", PDO_DIM, &r_0_10000, N_("Desired Radius") } +}; +static paramGroup_t joinPG = { "join-fixed", 0, joinPLs, sizeof joinPLs/sizeof joinPLs[0] }; + + + +BOOL_T AdjustPosToRadius(coOrd *pos, DIST_T desired_radius, ANGLE_T an0, ANGLE_T an1) { + coOrd point1,point2; + switch ( Dj.inp[1].params.type ) { + case curveTypeCurve: + if (Dj.inp[0].params.type == curveTypeStraight) { + coOrd newP, newP1; + //Offset curve by desired_radius + DIST_T newR1; + newR1 = Dj.inp[1].params.arcR + desired_radius*((fabs(an1-Dj.inp[1].params.arcA0)<1.0)?1:-1); + if (newR1<=0.0) { + if (debug) InfoMessage("Zero Radius C1"); + return FALSE; + } + //Offset line by desired_radius + Translate(&newP,Dj.inp[0].params.lineEnd,an0,desired_radius); + Translate(&newP1,Dj.inp[0].params.lineOrig,an0,desired_radius); + if (debug) + AnchorTempLine(newP,newP1); + //Intersect - this is the joining curve center + if (debug) + AnchorTempCircle(Dj.inp[1].params.arcP,newR1,Dj.inp[1].params.arcA0,Dj.inp[1].params.arcA1); + if (!FindArcAndLineIntersections(&point1,&point2,Dj.inp[1].params.arcP,newR1,newP,newP1)) + return FALSE; + } else if (Dj.inp[0].params.type == curveTypeCurve) { + //Offset curve by desired_radius + DIST_T newR0; + newR0 = Dj.inp[0].params.arcR + desired_radius*((fabs(an0-Dj.inp[0].params.arcA0)<1.0)?1:-1); + if (newR0<=0.0) { + if (debug) InfoMessage("Zero Radius C0"); + return FALSE; + } + //Offset curve by desired_radius + if (debug) + AnchorTempCircle(Dj.inp[0].params.arcP,newR0,Dj.inp[0].params.arcA0,Dj.inp[0].params.arcA1); + DIST_T newR1; + newR1 = Dj.inp[1].params.arcR + desired_radius*((fabs(an1-Dj.inp[1].params.arcA0)<1.0)?1:-1); + if (newR1<=0.0) { + if (debug) InfoMessage("Zero Radius C1"); + return FALSE; + } + //Intersect - this is the joining curve center + if (debug) + AnchorTempCircle(Dj.inp[1].params.arcP,newR1,Dj.inp[1].params.arcA0,Dj.inp[1].params.arcA1); + if (!FindArcIntersections(&point1,&point2,Dj.inp[0].params.arcP,newR0,Dj.inp[1].params.arcP,newR1)) + return FALSE; + } + if (debug) { + AnchorPoint(point1); + AnchorPoint(point2); + } + break; + case curveTypeStraight: + if (Dj.inp[0].params.type == curveTypeStraight) { + coOrd newI,newP0,newP01, newP1, newP11; + //Offset line1 by desired_radius + Translate(&newP0,Dj.inp[0].params.lineEnd,an0,desired_radius); + Translate(&newP01,Dj.inp[0].params.lineOrig,an0,desired_radius); + if (debug) + AnchorTempLine(newP0,newP01); + //Offset line2 by desired_radius + Translate(&newP1,Dj.inp[1].params.lineEnd,an1,desired_radius); + Translate(&newP11,Dj.inp[1].params.lineOrig,an1,desired_radius); + if (debug) + AnchorTempLine(newP1,newP11); + if (!FindIntersection(&newI,newP0,Dj.inp[0].params.angle,newP1,Dj.inp[1].params.angle)) + return FALSE; + point1 = point2 = newI; + } else if (Dj.inp[0].params.type == curveTypeCurve) { + coOrd newP, newP1; + //Offset curve by desired_radius + DIST_T newR0; + newR0 = Dj.inp[0].params.arcR + desired_radius*((fabs(an0-Dj.inp[0].params.arcA0)<1.0)?1:-1); + if (newR0<=0.0) { + if (debug) InfoMessage("Zero Radius C0"); + return FALSE; + } + if (debug) + AnchorTempCircle(Dj.inp[0].params.arcP,newR0,Dj.inp[0].params.arcA0,Dj.inp[0].params.arcA1); + //Offset line by desired_radius + Translate(&newP,Dj.inp[1].params.lineEnd,an1,desired_radius); + Translate(&newP1,Dj.inp[1].params.lineOrig,an1,desired_radius); + if (debug) + AnchorTempLine(newP,newP1); + //Intersect - this is the joining curve center + if (!FindArcAndLineIntersections(&point1,&point2,Dj.inp[0].params.arcP,newR0,newP,newP1)) + return FALSE; + } + if (debug) { + AnchorPoint(point1); + AnchorPoint(point2); + } + break; + default: + return FALSE; + } + if (FindDistance(*pos,point1)<=FindDistance(*pos,point2)) { + if (Dj.inp[1].params.type == curveTypeCurve) { + ANGLE_T a = FindAngle(Dj.inp[1].params.arcP,point1); + Translate(pos,Dj.inp[1].params.arcP,a,Dj.inp[1].params.arcR); + } else { + Translate(pos,point1,NormalizeAngle(an1+180),desired_radius); + } + } else { + if (Dj.inp[1].params.type == curveTypeCurve) { + ANGLE_T a = FindAngle(Dj.inp[1].params.arcP,point2); + Translate(pos,Dj.inp[1].params.arcP,a,Dj.inp[1].params.arcR); + } else + Translate(pos,point2,NormalizeAngle(an1+180),desired_radius); + } + + return TRUE; + +} + +void AddAnchorJoin(coOrd pos, ANGLE_T angle) { + DIST_T d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,Dj.anchors,1); + trkSeg_p a = &DYNARR_LAST(trkSeg_t,Dj.anchors); + a->type = SEG_CRVLIN; + a->width = 0; + a->u.c.a0 = 0.0; + a->u.c.a1 = 360.0; + a->u.c.center = pos; + a->u.c.radius = d/2; + a->color = wDrawColorBlue; + DYNARR_SET(trkSeg_t,Dj.anchors,Dj.anchors.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,Dj.anchors,Dj.anchors.cnt-5),pos,angle,FALSE,wDrawColorBlue); + +} + +static BOOL_T infoSubst = FALSE; +static track_p anchor_trk; +static coOrd anchor_pos; +static ANGLE_T anchor_angle = 0.0; static STATUS_T CmdJoin( wAction_t action, @@ -451,26 +810,63 @@ static STATUS_T CmdJoin( ANGLE_T a, a1; DIST_T eR[2]; BOOL_T ok; + wControl_p controls[2]; + char * labels[1]; switch (action&0xFF) { case C_START: + if (joinPLs[0].control==NULL) { + ParamCreateControls(&joinPG, NULL); + } if (selectedTrackCount==0) InfoMessage( _("Left click - join with track") ); else InfoMessage( _("Left click - join with track, Shift Left click - move to join") ); + DYNARR_RESET(trkSeg_t,Dj.anchors); Dj.state = 0; Dj.joinMoveState = 0; + Dj.cornuMode = FALSE; /*ParamGroupRecord( &easementPG );*/ - if (easementVal < 0) + infoSubst = FALSE; + anchor_trk = NULL; + if (easementVal < 0.0) return CmdCornu(action, pos); return C_CONTINUE; + + case wActionMove: + anchor_trk = NULL; + DYNARR_RESET(rkSeg_t,Dj.anchors); + if ((easementVal < 0) && Dj.joinMoveState == 0 ) + return CmdCornu(action, pos); + if ( Dj.state >= 2) return C_CONTINUE; + if ( (trk = OnTrack( &pos, FALSE, TRUE )) == NULL) + return C_CONTINUE; + if (!CheckTrackLayer( trk ) ) + return C_CONTINUE; + if ((Dj.state > 0) && (trk == Dj.inp[0].trk)) + return C_CONTINUE; + trackParams_t moveParams; + if (!GetTrackParams( PARAMS_1ST_JOIN, trk, pos, &moveParams )) + return C_CONTINUE; + ep = PickUnconnectedEndPointSilent(pos,trk); + if (ep <0) return C_CONTINUE; + if (IsClose(FindDistance(GetTrkEndPos(trk,ep),pos))) + anchor_angle = GetTrkEndAngle(trk,ep); + else + anchor_angle = FindAngle(pos,GetTrkEndPos(trk,ep)); + anchor_trk = trk; + anchor_pos = pos; + AddAnchorJoin(pos,anchor_angle); + break; case C_DOWN: - if ( (Dj.state == 0 && (MyGetKeyState() & WKEY_SHIFT) != 0) || Dj.joinMoveState != 0 ) + if ( !Dj.cornuMode && ((Dj.state == 0 && (MyGetKeyState() & WKEY_SHIFT) != 0) || Dj.joinMoveState != 0) ) return DoMoveToJoin( pos ); - if (easementVal < 0.0) + if (easementVal < 0.0 && Dj.joinMoveState == 0) { + Dj.cornuMode = TRUE; return CmdCornu(action, pos); + } DYNARR_SET( trkSeg_t, tempSegs_da, 3 ); tempSegs(0).color = drawColorBlack; @@ -495,10 +891,18 @@ LOG( log_join, 1, ("JOIN: 1st track %d @[%0.3f %0.3f]\n", 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 ); + wPrefGetFloat("misc", "desired_radius", &desired_radius, desired_radius); + controls[0] = joinRadPD.control; + controls[1] = NULL; + labels[0] = N_("Desired Radius"); + InfoSubstituteControls(controls, labels); + infoSubst = TRUE; + joinRadPD.option |= PDO_NORECORD; + ParamLoadControls(&joinPG); + ParamGroupRecord(&joinPG); return C_CONTINUE; } else { - if ( (Dj.inp[1].trk = OnTrack( &pos, TRUE, TRUE )) == NULL) + if ( (Dj.inp[1].trk = OnTrack( &pos, FALSE, TRUE )) == NULL) return C_CONTINUE; if (!CheckTrackLayer( Dj.inp[1].trk ) ) return C_CONTINUE; @@ -509,6 +913,10 @@ LOG( log_join, 1, ("JOIN: 1st track %d @[%0.3f %0.3f]\n", ErrorMessage( MSG_JOIN_SAME ); return C_CONTINUE; } + if (infoSubst) + InfoSubstituteControls(NULL, NULL); + infoSubst = FALSE; + 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 ); @@ -519,21 +927,30 @@ 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; + BOOL_T only_merge = FALSE; + if ( (Dj.inp[0].params.ep >=0) && + (GetTrkEndTrk(Dj.inp[0].trk,Dj.inp[0].params.ep) != NULL)) { + if (GetTrkEndTrk(Dj.inp[0].trk,Dj.inp[0].params.ep) != Dj.inp[1].trk) { + only_merge = TRUE; + 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; + if ( (Dj.inp[1].params.ep >= 0) && + (GetTrkEndTrk(Dj.inp[1].trk,Dj.inp[1].params.ep) != NULL)) { + if (GetTrkEndTrk(Dj.inp[1].trk,Dj.inp[1].params.ep) != Dj.inp[0].trk) { + only_merge = TRUE; + 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 ) ) + Dj.inp[1].trk, Dj.inp[1].params.ep ) ) { + rc = C_TERMINATE; + } else if (only_merge) { rc = C_TERMINATE; - else if ( Dj.inp[0].params.ep >= 0 && Dj.inp[1].params.ep >= 0 ) { + } 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, @@ -544,7 +961,6 @@ LOG( log_join, 1, ("P1=[%0.3f %0.3f]\n", pos.x, pos.y ) ) 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 ) || @@ -553,30 +969,103 @@ LOG( log_join, 1, ("P1=[%0.3f %0.3f]\n", pos.x, pos.y ) ) 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; + /* no break */ case C_MOVE: - if (easementVal < 0) + if (easementVal < 0 && Dj.cornuMode) return CmdCornu(action, pos); 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 ); + DYNARR_RESET(trkSeg_t,Dj.anchors); + + + //Fix Pos onto the line of the second track + if (Dj.inp[1].params.type == curveTypeStraight) { + ANGLE_T a = NormalizeAngle(FindAngle(Dj.inp[1].params.lineOrig,pos)-Dj.inp[1].params.angle); + DIST_T d = FindDistance(Dj.inp[1].params.lineOrig,pos); + Translate(&pos,Dj.inp[1].params.lineOrig,Dj.inp[1].params.angle,d*cos(D2R(a))); + } else { + ANGLE_T a = FindAngle(Dj.inp[1].params.arcP,pos); + Translate(&pos,Dj.inp[1].params.arcP,a,Dj.inp[1].params.arcR); + } + + if ((desired_radius != 0.0) && + ((Dj.inp[0].params.type == curveTypeStraight) || (Dj.inp[0].params.type == curveTypeCurve)) && + ((Dj.inp[1].params.type == curveTypeStraight) || (Dj.inp[1].params.type == curveTypeCurve)) && + Dj.jRes.type==curveTypeCurve + ) { + ANGLE_T na0=0.0,na1=0.0; + coOrd end0, end1; + ANGLE_T a0,a1; + end0 = GetTrkEndPos(Dj.inp[0].trk,Dj.inp[0].params.ep); + end1 = GetTrkEndPos(Dj.inp[1].trk,Dj.inp[1].params.ep); + if (Dj.inp[0].params.type == curveTypeStraight) { + a0 = DifferenceBetweenAngles(Dj.inp[0].params.angle,FindAngle(Dj.jRes.pos[0], pos)); + na0 = NormalizeAngle( Dj.inp[0].params.angle + + ((a0>0.0)?90.0:-90.0)); + } else { + na0 = Dj.inp[0].params.arcA0; + if (FindDistance(Dj.inp[0].params.arcP,pos)<Dj.inp[0].params.arcR) + na0 = NormalizeAngle(na0+180.0); + } + //Now Second Line offset + if (Dj.inp[1].params.type == curveTypeStraight) { + a1 = DifferenceBetweenAngles(Dj.inp[1].params.angle,FindAngle(pos, Dj.jRes.pos[0])); + na1 = NormalizeAngle( Dj.inp[1].params.angle + + ((a1>0.0)?90.0:-90.0)); + } else { + na1 = Dj.inp[1].params.arcA0; + if (FindDistance(Dj.inp[1].params.arcP,Dj.jRes.pos[0])<Dj.inp[1].params.arcR) + na1 = NormalizeAngle(na1+180.0); + } + coOrd pos1 = pos; + if (AdjustPosToRadius(&pos1,desired_radius+(Dj.jointD[0].x), na0, na1)) { + if (Dj.inp[1].params.type == curveTypeStraight) { + FindPos( &off, &beyond, pos1, Dj.inp[1].params.lineOrig, Dj.inp[1].params.angle, + FindDistance(Dj.inp[1].params.lineOrig,Dj.inp[1].params.lineEnd) ); + } else if (Dj.inp[1].params.type == curveTypeCurve) { + ANGLE_T a = FindAngle(Dj.inp[1].params.arcP,pos1); + if ((a>Dj.inp[1].params.arcA0+Dj.inp[1].params.arcA1) || (a< Dj.inp[1].params.arcA0)) { + beyond = 1.0; + } + } + //Suppress result unless on track and close to user position (when on track) + if (beyond>-0.01 && IsClose(FindDistance(pos,pos1))) { + pos = pos1; + DYNARR_APPEND(trkSeg_t,Dj.anchors,1); + trkSeg_p p = &DYNARR_LAST(trkSeg_t,Dj.anchors); + p->type= SEG_CRVLIN; + p->width = 0; + p->color = wDrawColorBlue; + p->u.c.center = pos; + p->u.c.a1= 360.0; + p->u.c.a0 = 0.0; + p->u.c.radius = tempD.scale*0.25/2; + } + } + + } + + 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: @@ -798,16 +1287,14 @@ errorReturn: 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) { - if (easementVal<0) - return CmdCornu(action, pos); + if (easementVal<0 && Dj.cornuMode) + return CmdCornu(action, pos); else return C_CONTINUE; } @@ -815,12 +1302,10 @@ errorReturn: 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; } @@ -845,7 +1330,10 @@ errorReturn: UndrawNewTrack( Dj.inp[1].trk ); ep = Dj.jRes.flip?1:0; Dj.state = 0; + DYNARR_RESET(trkSeg_t,Dj.anchors); rc = C_TERMINATE; + if (easementVal == 0.0) + wPrefSetFloat("misc", "desired_radius", desired_radius); 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], @@ -856,24 +1344,34 @@ errorReturn: DrawNewTrack( Dj.inp[0].trk ); DrawNewTrack( Dj.inp[1].trk ); DrawNewTrack( trk ); + if (infoSubst) + InfoSubstituteControls(NULL, NULL); + infoSubst = FALSE; return rc; case C_CANCEL: - case C_REDRAW: - + if (infoSubst) + InfoSubstituteControls(NULL, NULL); + infoSubst = FALSE; + break; + case C_REDRAW: if ( Dj.joinMoveState == 1 || Dj.state == 1 ) { DrawFillCircle( &tempD, Dj.inp[0].pos, 0.10*mainD.scale, selectedColor ); - } else if (easementVal<0 ) - return CmdCornu(action,pos); - + } else if (easementVal<0 && Dj.joinMoveState == 0) + return CmdCornu(action,pos); + if (Dj.anchors.cnt) + DrawSegs(&tempD, zero, 0.0, &(((trkSeg_t *)Dj.anchors.ptr)[0]), Dj.anchors.cnt,trackGauge,wDrawColorBlack); DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); break; case C_TEXT: case C_OK: - if (easementVal<0 ) - return CmdCornu(action,pos); + if (easementVal<0 && Dj.cornuMode) + return CmdCornu(action,pos); + if (infoSubst) + InfoSubstituteControls(NULL, NULL); + infoSubst = FALSE; } @@ -889,10 +1387,14 @@ errorReturn: */ #include "bitmaps/join.xpm" +#include "bitmaps/joinline.xpm" void InitCmdJoin( wMenu_p menu ) { - joinCmdInx = AddMenuButton( menu, CmdJoin, "cmdJoin", _("Join"), wIconCreatePixMap(join_xpm), LEVEL0_50, IC_STICKY|IC_POPUP, ACCL_JOIN, NULL ); + ButtonGroupBegin( _("Join"), "cmdJoinSetCmd", _("Join") ); + joinCmdInx = AddMenuButton( menu, CmdJoin, "cmdJoinTrack", _("Join Track"), wIconCreatePixMap(join_xpm), LEVEL0_50, IC_STICKY|IC_POPUP|IC_WANT_MOVE, ACCL_JOIN, NULL ); + AddMenuButton( menu, CmdJoinLine, "cmdJoinLine", _("Join Lines"), wIconCreatePixMap(joinline_xpm), LEVEL0_50, IC_STICKY|IC_POPUP|IC_WANT_MOVE, ACCL_JOIN, NULL ); + ButtonGroupEnd(); log_join = LogFindIndex( "join" ); } diff --git a/app/bin/cmisc.c b/app/bin/cmisc.c index a353530..b41ae42 100644 --- a/app/bin/cmisc.c +++ b/app/bin/cmisc.c @@ -23,6 +23,7 @@ #include <stdint.h> #include "common.h" +#include "utility.h" #include "cundo.h" #include "i18n.h" #include "messages.h" @@ -32,6 +33,10 @@ EXPORT wIndex_t describeCmdInx; EXPORT BOOL_T inDescribeCmd; +extern wIndex_t selectCmdInx; +extern wIndex_t joinCmdInx; +extern wIndex_t modifyCmdInx; + static track_p descTrk; static descData_p descData; static descUpdate_t descUpdateFunc; @@ -43,13 +48,16 @@ static BOOL_T descNeedDrawHilite; static wPos_t describeW_posy; static wPos_t describeCmdButtonEnd; +static wMenu_p descPopupM; + static unsigned int editableLayerList[NUM_LAYERS]; /**< list of non-frozen layers */ static int * layerValue; /**pointer to current Layer (int *) */ 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 char * pivotLabels[] = { N_("First"), N_("Middle"), N_("End"), NULL }; +static char * boxLabels[] = { "", NULL }; static paramData_t describePLs[] = { #define I_FLOAT_0 (0) { PD_FLOAT, NULL, "F1", 0, &rdata }, @@ -114,13 +122,15 @@ static paramData_t describePLs[] = { #define I_LAYER_N I_LAYER_0+1 #define I_COLOR_0 I_LAYER_N - { PD_COLORLIST, NULL, "C1", 0, NULL, N_("Color") }, + { PD_COLORLIST, NULL, "C1", PDO_NOPREF, NULL, N_("Color"), BC_HORZ|BC_NOBORDER }, #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 + { PD_DROPLIST, NULL, "L3", 0, (void*)150, NULL, 0 }, + { PD_DROPLIST, NULL, "L4", 0, (void*)150, NULL, 0 }, +#define I_LIST_N I_LIST_0+4 #define I_EDITLIST_0 I_LIST_N { PD_DROPLIST, NULL, "LE1", 0, (void*)150, NULL, BL_EDITABLE }, @@ -131,8 +141,15 @@ static paramData_t describePLs[] = { #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 } + { PD_RADIO, NULL, "P1", 0, pivotLabels, N_("Pivot"), BC_HORZ|BC_NOBORDER, 0 }, #define I_PIVOT_N I_PIVOT_0+1 + +#define I_TOGGLE_0 I_PIVOT_N + { PD_TOGGLE, NULL, "boxed1", PDO_NOPREF|PDO_DLGHORZ, boxLabels, N_("Boxed"), BC_HORZ|BC_NOBORDER }, + { PD_TOGGLE, NULL, "boxed2", PDO_NOPREF|PDO_DLGHORZ, boxLabels, N_("Boxed"), BC_HORZ|BC_NOBORDER }, + { PD_TOGGLE, NULL, "boxed3", PDO_NOPREF|PDO_DLGHORZ, boxLabels, N_("Boxed"), BC_HORZ|BC_NOBORDER }, + { PD_TOGGLE, NULL, "boxed4", PDO_NOPREF|PDO_DLGHORZ, boxLabels, N_("Boxed"), BC_HORZ|BC_NOBORDER }, +#define I_TOGGLE_N I_TOGGLE_0+4 }; static paramGroup_t describePG = { "describe", 0, describePLs, sizeof describePLs/sizeof describePLs[0] }; @@ -149,7 +166,7 @@ CreateEditableLayersList() int i = 0; int j = 0; - while (i <= NUM_LAYERS) { + while (i < NUM_LAYERS) { if (!GetLayerFrozen(i)) { editableLayerList[j++] = i; } @@ -179,7 +196,7 @@ SearchEditableLayerList(unsigned int layer) return (-1); } -static void DrawDescHilite(void) +static void DrawDescHilite(BOOL_T selected) { wPos_t x, y, w, h; @@ -194,7 +211,7 @@ static void DrawDescHilite(void) 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); + wDrawFilledRectangle(tempD.d, x, y, w, h, selected?descColor:wDrawColorBlue, wDrawOptTemp|wDrawOptTransparent); } @@ -221,10 +238,6 @@ static void DescribeUpdate( return; } - if ((ddp->mode&DESC_NOREDRAW) == 0) { - DrawDescHilite(); - } - if (!descUndoStarted) { UndoStart(_("Change Track"), "Change Track"); descUndoStarted = TRUE; @@ -252,7 +265,6 @@ static void DescribeUpdate( descOrig.y -= descBorder; descSize.x -= descOrig.x-descBorder; descSize.y -= descOrig.y-descBorder; - DrawDescHilite(); } for (inx = 0; inx < sizeof describePLs/sizeof describePLs[0]; inx++) { @@ -288,9 +300,6 @@ static void DescOk(void * junk) { wHide(describePG.win); - if (descTrk) { - DrawDescHilite(); - } if (layerValue && *layerValue>=0) { SetTrkLayer(descTrk, editableLayerList[*layerValue]); //int found that is really in the parm controls. } @@ -304,7 +313,7 @@ static void DescOk(void * junk) } descNeedDrawHilite = FALSE; - Reset(); + Reset(); // DescOk } @@ -326,10 +335,22 @@ static struct { /*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 } + /*EDITABLELIST*/{ PD_DROPLIST, 0, I_EDITLIST_0, I_EDITLIST_N }, + /*BOXED*/ { PD_TOGGLE, 0, I_TOGGLE_0, I_TOGGLE_N }, }; -static wControl_p AllocateButt(descData_p ddp, void * valueP, char * label, +/** + * An unused param element is selected from the list of pre-defined param elements and initialized + * for an element specific param. + * + * \param ddp Element specific param + * \param valueP the value pointer used by the element + * \param label the label assigned by the element + * \param sep ? + * \return the selected widget + */ + +static wControl_p AssignParamToDescribeDialog(descData_p ddp, void * valueP, char * label, wPos_t sep) { int inx; @@ -364,7 +385,7 @@ static wControl_p AllocateButt(descData_p ddp, void * valueP, char * label, } } - AbortProg("allocateButt: can't find %d", ddp->type); + AbortProg("AssignParamToDescribeDialog: can't find %d", ddp->type); return NULL; } @@ -465,13 +486,13 @@ void DoDescribe(char * title, track_p trk, descData_p data, descUpdate_t update) label = _(ddp->label); ddp->posy = describeW_posy; - ddp->control0 = AllocateButt(ddp, ddp->valueP, label, + ddp->control0 = AssignParamToDescribeDialog(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, + ddp->control1 = AssignParamToDescribeDialog(ddp, &((coOrd*)(ddp->valueP))->y, NULL, 0); @@ -523,9 +544,10 @@ EXPORT void DescribeCancel(void) { if (describePG.win && wWinIsVisible(describePG.win)) { if (descTrk) { - descUpdateFunc(descTrk, -1, descData, TRUE); - descTrk = NULL; - DrawDescHilite(); + if (!IsTrackDeleted(descTrk)) + descUpdateFunc(descTrk, -1, descData, TRUE); + descTrk = NULL; + } wHide(describePG.win); @@ -540,21 +562,27 @@ EXPORT void DescribeCancel(void) } -static STATUS_T CmdDescribe(wAction_t action, coOrd pos) +EXPORT STATUS_T CmdDescribe(wAction_t action, coOrd pos) { - track_p trk; + static track_p trk; char msg[STR_SIZE]; switch (action) { case C_START: InfoMessage(_("Select track to describe")); + wSetCursor(mainD.d,wCursorQuestion); descUndoStarted = FALSE; + trk = NULL; return C_CONTINUE; + case wActionMove: + trk = OnTrack(&pos, FALSE, 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; } @@ -572,10 +600,10 @@ static STATUS_T CmdDescribe(wAction_t action, coOrd pos) descSize.x -= descOrig.x-descBorder; descSize.y -= descOrig.y-descBorder; descNeedDrawHilite = TRUE; - DrawDescHilite(); DescribeTrack(trk, msg, 255); inDescribeCmd = FALSE; InfoMessage(msg); + trk = NULL; } else { InfoMessage(""); } @@ -583,17 +611,31 @@ static STATUS_T CmdDescribe(wAction_t action, coOrd pos) return C_CONTINUE; case C_REDRAW: + if (describePG.win && wWinIsVisible(describePG.win) && descTrk) { - DrawDescHilite(); + DrawDescHilite(TRUE); + if (descTrk && QueryTrack(descTrk, Q_IS_DRAW)) { + DrawOriginAnchor(descTrk); + } + } else if (trk){ + DrawTrack(trk,&tempD,wDrawColorPreviewSelected); } + break; case C_CANCEL: DescribeCancel(); + wSetCursor(mainD.d,defaultCursor); return C_CONTINUE; + + case C_CMDMENU: + menuPos = pos; + if (!trk) wMenuPopupShow(descPopupM); + return C_CONTINUE; } + return C_CONTINUE; } @@ -601,11 +643,23 @@ static STATUS_T CmdDescribe(wAction_t action, coOrd pos) #include "bitmaps/describe.xpm" +extern wIndex_t selectCmdInx; +extern wIndex_t modifyCmdInx; +extern wIndex_t panCmdInx; + void InitCmdDescribe(wMenu_p menu) { describeCmdInx = AddMenuButton(menu, CmdDescribe, "cmdDescribe", _("Properties"), wIconCreatePixMap(describe_xpm), - LEVEL0, IC_CANCEL|IC_POPUP, ACCL_DESCRIBE, NULL); + LEVEL0, IC_CANCEL|IC_POPUP|IC_WANT_MOVE|IC_CMDMENU, ACCL_DESCRIBE, NULL); RegisterChangeNotification(DescChange); ParamRegister(&describePG); } +void InitCmdDescribe2(wMenu_p menu) +{ + descPopupM = MenuRegister( "Describe Context Menu" ); + wMenuPushCreate(descPopupM, "cmdSelectMode", GetBalloonHelpStr("cmdSelectMode"), 0, DoCommandB, (void*) (intptr_t) selectCmdInx); + wMenuPushCreate(descPopupM, "cmdModifyMode", GetBalloonHelpStr("cmdModifyMode"), 0, DoCommandB, (void*) (intptr_t) modifyCmdInx); + wMenuPushCreate(descPopupM, "cmdPanMode", GetBalloonHelpStr("cmdPanMode"), 0, DoCommandB, (void*) (intptr_t) panCmdInx); + +} diff --git a/app/bin/cmodify.c b/app/bin/cmodify.c index 11f4c06..8f82012 100644 --- a/app/bin/cmodify.c +++ b/app/bin/cmodify.c @@ -34,6 +34,9 @@ #include "param.h" #include "track.h" #include "utility.h" +#include "drawgeom.h" +#include "common.h" +#include "layout.h" static struct { track_p Trk; @@ -47,10 +50,65 @@ static struct { BOOL_T first; } Dex; +static wMenu_p modPopupM; + +extern wIndex_t selectCmdInx; +extern wIndex_t joinCmdInx; +extern wIndex_t describeCmdInx; + +static dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) static int log_modify; static BOOL_T modifyBezierMode; static BOOL_T modifyCornuMode; +static BOOL_T modifyDrawMode; +static BOOL_T modifyRulerMode; +static BOOL_T modifyExtendMode; + + +static void CreateEndAnchor(coOrd p, wBool_t lock) { + DIST_T d = tempD.scale*0.15; + + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = lock?SEG_FILCRCL:SEG_CRVLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.c.center = p; + anchors(i).u.c.radius = d/2; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).width = 0; +} + +static void CreateCornuAnchor(coOrd p, wBool_t lock) { + DIST_T d = tempD.scale*0.15; + + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = lock?SEG_FILCRCL:SEG_CRVLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.c.center = p; + anchors(i).u.c.radius = d/2; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).width = 0; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_CRVLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.c.center = p; + anchors(i).u.c.radius = d; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).width = 0; +} + + +static void CreateRadiusAnchor(coOrd p, ANGLE_T a, BOOL_T bi) { + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),p,a,bi,wDrawColorBlue); +} /* * Call cbezier.c CmdBezModify to alter Bezier Track and Lines. @@ -66,6 +124,7 @@ static STATUS_T ModifyBezier(wAction_t action, coOrd pos) { case C_UP: case C_OK: case C_TEXT: + case wActionMove: trackGauge = (IsTrack(Dex.Trk)?GetTrkGauge(Dex.Trk):0.0); rc = CmdBezModify(Dex.Trk, action, pos, trackGauge); break; @@ -89,12 +148,14 @@ static STATUS_T ModifyCornu(wAction_t action, coOrd pos) { STATUS_T rc = C_CONTINUE; if (Dex.Trk == NULL) return C_ERROR; //No track picked yet! switch (action&0xFF) { + case C_LCLICK: case C_START: case C_DOWN: case C_MOVE: case C_UP: case C_OK: case C_TEXT: + case wActionMove: trackGauge = (IsTrack(Dex.Trk)?GetTrkGauge(Dex.Trk):0.0); rc = CmdCornuModify(Dex.Trk, action, pos, trackGauge); break; @@ -110,7 +171,61 @@ static STATUS_T ModifyCornu(wAction_t action, coOrd pos) { return rc; } -static STATUS_T CmdModify( +/* + * Picking a DRAW will allow point modifications until terminated with "Enter" + */ +static STATUS_T ModifyDraw(wAction_t action, coOrd pos) { + STATUS_T rc = C_CONTINUE; + if (Dex.Trk == NULL) return C_ERROR; //No item picked yet! + switch (action&0xFF) { + case C_START: + case C_DOWN: + case C_MOVE: + case C_UP: + rc = ModifyTrack( Dex.Trk, action, pos ); + break; + case wActionMove: + rc = ModifyTrack( Dex.Trk, action, pos ); + break; + case C_TEXT: + //Delete or '0' - continues + if ((action>>8 !=32) && (action >>8 !=13)) + return ModifyTrack( Dex.Trk, action, pos ); + //Enter/Space does not + if ((action>>8 !=32) && (action>>8 != 13)) return C_CONTINUE; + /*no break*/ + case C_OK: + UndoStart( _("Modify Track"), "Modify( T%d[%d] )", GetTrkIndex(Dex.Trk), Dex.params.ep ); + UndoModify( Dex.Trk ); + rc = ModifyTrack( Dex.Trk, C_TEXT | (13<<8), pos ); + if (rc != C_CONTINUE) modifyDrawMode = FALSE; + UndoEnd(); + break; + case C_CANCEL: + case C_FINISH: + case C_CONFIRM: + case C_TERMINATE: + rc = ModifyTrack( Dex.Trk, action, pos ); + Dex.Trk = NULL; + modifyDrawMode = FALSE; + tempSegs_da.cnt = 0; + rc = C_CONTINUE; + break; + case C_REDRAW: + rc = ModifyTrack( Dex.Trk, action, pos ); + break; + case C_CMDMENU: + menuPos = pos; + rc = ModifyTrack( Dex.Trk, action, pos ); + break; + default: + break; + } + return rc; +} + + +STATUS_T CmdModify( wAction_t action, coOrd pos ) /* @@ -128,7 +243,7 @@ static STATUS_T CmdModify( EPINX_T inx; curveType_e curveType; static BOOL_T changeTrackMode; - static BOOL_T modifyRulerMode; + STATUS_T rc; static DIST_T trackGauge; @@ -143,7 +258,8 @@ static STATUS_T CmdModify( switch (action&0xFF) { case C_START: - InfoMessage( _("Select track to modify") ); + DYNARR_RESET(trkSeg_t,anchors_da); + InfoMessage( _("Select a track to modify, Left-Click change length, Right-Click to add flextrack") ); Dex.Trk = NULL; tempSegs_da.cnt = 0; /*ChangeParameter( &easementPD );*/ @@ -151,21 +267,28 @@ static STATUS_T CmdModify( changeTrackMode = modifyRulerMode = FALSE; modifyBezierMode = FALSE; modifyCornuMode = FALSE; + modifyDrawMode = FALSE; + modifyExtendMode = FALSE; return C_CONTINUE; case C_DOWN: + DYNARR_RESET(trkSeg_t,anchors_da); if (modifyBezierMode) return ModifyBezier(C_DOWN, pos); if (modifyCornuMode) return ModifyCornu(C_DOWN, pos); + if (modifyDrawMode) + return ModifyDraw(C_DOWN, pos); + /*no break*/ + case C_LDOUBLE: 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 ); + //Dex.Trk = trk; if (Dex.Trk == NULL) { if ( ModifyRuler( C_DOWN, pos ) == C_CONTINUE ) modifyRulerMode = TRUE; @@ -173,6 +296,7 @@ static STATUS_T CmdModify( } if (!CheckTrackLayer( Dex.Trk ) ) { Dex.Trk = NULL; + return C_CONTINUE; } trackGauge = (IsTrack(Dex.Trk)?GetTrkGauge(Dex.Trk):0.0); @@ -185,21 +309,38 @@ static STATUS_T CmdModify( } return C_CONTINUE; //That's it } - if (QueryTrack( Dex.Trk, Q_IS_CORNU )) { //Bezier + if (QueryTrack( Dex.Trk, Q_IS_CORNU )) { //Cornu modifyCornuMode = TRUE; if (ModifyCornu(C_START, pos) != C_CONTINUE) { //Call Start with track - modifyCornuMode = FALSE; //Function rejected Bezier + modifyCornuMode = FALSE; //Function rejected Cornu Dex.Trk =NULL; tempSegs_da.cnt = 0; + } return C_CONTINUE; //That's it } - if ( (MyGetKeyState()&WKEY_SHIFT) && + if (QueryTrack( Dex.Trk, Q_IS_DRAW )) { + modifyDrawMode = TRUE; + if (ModifyDraw(C_START, pos) != C_CONTINUE) { + modifyDrawMode = FALSE; + Dex.Trk = NULL; + tempSegs_da.cnt = 0; + } + return C_CONTINUE; + } + + if ((action&0xFF) == C_LDOUBLE) return C_ERROR; + + if ((MyGetKeyState()&WKEY_CTRL)) goto extendTrack; + + + + if ( (MyGetKeyState()&WKEY_SHIFT) && //Free to change radius QueryTrack( Dex.Trk, Q_CAN_MODIFYRADIUS )&& - (inx=PickUnconnectedEndPoint(pos,Dex.Trk)) >= 0 ) { + ((inx=PickUnconnectedEndPoint(pos,Dex.Trk)) >= 0 )) { trk = Dex.Trk; - while ( (trk1=GetTrkEndTrk(trk,1-inx)) && + while ( (trk1=GetTrkEndTrk(trk,1-inx)) && //Means next track to mine even if can be end... QueryTrack(trk1, Q_CANNOT_BE_ON_END) ) { inx = GetEndPtConnectedToMe( trk1, trk ); trk = trk1; @@ -208,7 +349,8 @@ static STATUS_T CmdModify( UndoStart( _("Change Track"), "Change( T%d[%d] )", GetTrkIndex(Dex.Trk), Dex.params.ep ); inx = GetEndPtConnectedToMe( trk1, trk ); Dex.Trk = NULL; - DeleteTrack(trk, TRUE); + UndrawNewTrack( trk ); + DeleteTrack(trk, TRUE); //Get rid of original track if ( !GetTrkEndTrk( trk1, inx ) ) { Dex.Trk = trk1; Dex.pos00 = GetTrkEndPos( Dex.Trk, inx ); @@ -218,16 +360,70 @@ static STATUS_T CmdModify( } ErrorMessage( MSG_CANNOT_CHANGE ); } + ModifyTrack(Dex.Trk, C_START, pos); //Basically trim via Modify... 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(); - MapRedraw(); return rc; + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + if (modifyCornuMode) return ModifyCornu(wActionMove,pos); + if (modifyDrawMode) return ModifyDraw(wActionMove,pos); + if (modifyBezierMode) return ModifyBezier(wActionMove, pos); + track_p t; + if (((t=OnTrack(&pos,FALSE,TRUE))!= NULL) && CheckTrackLayerSilent( t )) { + EPINX_T ep = PickUnconnectedEndPointSilent(pos, t); + if (QueryTrack( t, Q_IS_CORNU )) { + CreateCornuAnchor(pos,FALSE); + } else if ( QueryTrack( t, Q_CAN_MODIFY_CONTROL_POINTS )) { + CreateRadiusAnchor(pos,NormalizeAngle(GetAngleAtPoint(t,pos,NULL,NULL)+90.0),TRUE); + CreateEndAnchor(pos,FALSE); + } else if (QueryTrack(t,Q_CAN_ADD_ENDPOINTS)){ //Turntable + trackParams_t tp; + if (!GetTrackParams(PARAMS_CORNU, t, pos, &tp)) return C_CONTINUE; + ANGLE_T a = tp.angle; + Translate(&pos,tp.ttcenter,a,tp.ttradius); + CreateRadiusAnchor(pos,a,FALSE); + } else if (QueryTrack(t,Q_CAN_EXTEND)) { + if (ep != -1) { + if (MyGetKeyState()&WKEY_CTRL) { + pos = GetTrkEndPos(t,ep); + CreateEndAnchor(pos,FALSE); + CreateRadiusAnchor(pos,GetTrkEndAngle(t,ep),FALSE); + CreateRadiusAnchor(pos,GetTrkEndAngle(t,ep)+90,TRUE); + } else { + CreateEndAnchor(pos,FALSE); + if ((MyGetKeyState()&WKEY_SHIFT) && //Shift Down + QueryTrack( t, Q_CAN_MODIFYRADIUS ) && // Straight or Curve + ((inx=PickUnconnectedEndPointSilent(pos,t)) >= 0 )) { //Which has an open end + if (GetTrkEndTrk(t,1-inx)) // Has to have a track on other end + CreateRadiusAnchor(pos,NormalizeAngle(GetAngleAtPoint(t,pos,NULL,NULL)+90.0),TRUE); + } + CreateRadiusAnchor(pos,GetAngleAtPoint(t,pos,NULL,NULL),TRUE); + } + } + } else if (ep>=0){ //Turnout + pos = GetTrkEndPos(t, ep); + CreateEndAnchor(pos,TRUE); + if ( (MyGetKeyState()&WKEY_CTRL)) { + CreateRadiusAnchor(pos,NormalizeAngle(GetTrkEndAngle(t,ep)),FALSE); + CreateRadiusAnchor(pos,GetTrkEndAngle(t,ep)+90,TRUE); + CreateEndAnchor(pos,TRUE); + } else { + CreateRadiusAnchor(pos,NormalizeAngle(GetTrkEndAngle(t,ep)),FALSE); + CreateEndAnchor(pos,TRUE); + } + } + } else if (((t=OnTrack(&pos,FALSE,FALSE))!= NULL) + && (!(GetLayerFrozen(GetTrkLayer(t)) && GetLayerModule(GetTrkLayer(t)))) + && (QueryTrack(t, Q_IS_DRAW ) && !QueryTrack(t, Q_IS_TEXT)) ) { + CreateEndAnchor(pos,FALSE); + } + return C_CONTINUE; + case C_MOVE: if ( modifyRulerMode ) return ModifyRuler( C_MOVE, pos ); @@ -237,21 +433,22 @@ static STATUS_T CmdModify( return ModifyBezier(C_MOVE, pos); if ( modifyCornuMode ) return ModifyCornu(C_MOVE, pos); - DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + if ( modifyDrawMode) + return ModifyDraw(C_MOVE, pos); + if (modifyExtendMode && (MyGetKeyState()&WKEY_CTRL)) + goto extendTrackMove; 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(); - MapRedraw(); return rc; - case C_UP: + DYNARR_RESET(trkSeg_t,anchors_da); if (Dex.Trk == NULL) return C_CONTINUE; if ( modifyRulerMode ) @@ -260,58 +457,71 @@ static STATUS_T CmdModify( return ModifyBezier( C_UP, pos); if (modifyCornuMode) return ModifyCornu(C_UP, pos); - DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + if (modifyDrawMode) + return ModifyDraw(C_UP, pos); + if ((MyGetKeyState()&WKEY_CTRL)) goto extendTrackUp; + 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(); - MapRedraw(); return rc; - case C_RDOWN: + case C_RDOWN: //This is same as context menu.... +extendTrack: + DYNARR_RESET(trkSeg_t,anchors_da); changeTrackMode = TRUE; + modifyExtendMode = TRUE; modifyRulerMode = FALSE; modifyBezierMode = FALSE; modifyCornuMode = 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) { + modifyDrawMode = FALSE; + Dex.first = FALSE; + if (((action&0xFF) == C_RDOWN) || ((action&0xFF)== C_DOWN)) { + Dex.Trk = OnTrack( &pos, TRUE, TRUE ); + if (Dex.Trk) { + if (!CheckTrackLayer( Dex.Trk ) ) { Dex.Trk = NULL; return C_CONTINUE; - break; } - if (Dex.params.ep == 0) { - Dex.params.arcR = -Dex.params.arcR; + 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 add flex track") ); + } else { + return C_ERROR; } - 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 { + InfoMessage ( _("No track to extend")); return C_ERROR; } + Dex.first = TRUE; + } else if (!Dex.Trk) { + InfoMessage ( _("No track selected")); + return C_ERROR; } - Dex.first = TRUE; - MainRedraw(); - MapRedraw(); /* no break */ case C_RMOVE: - DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); +extendTrackMove: + DYNARR_RESET(trkSeg_t,anchors_da); tempSegs_da.cnt = 0; Dex.valid = FALSE; if (Dex.Trk == NULL) return C_CONTINUE; @@ -320,7 +530,8 @@ LOG( log_modify, 1, ("extend endPt[%d] = [%0.3f %0.3f] A%0.3f\n", return C_CONTINUE; Dex.first = FALSE; Dex.pos01 = Dex.pos00; - if (Dex.params.type == curveTypeCornu) { //Restrict Cornu drag out to match end + + if (Dex.params.type == curveTypeCornu) { //Always Restrict Cornu drag out to match end ANGLE_T angle2 = NormalizeAngle(FindAngle(pos, Dex.pos00)-Dex.angle); if (angle2 > 90.0 && angle2 < 270.0) { if (Dex.params.cornuRadius[Dex.params.ep] == 0) { @@ -346,11 +557,6 @@ LOG( log_modify, 1, ("extend endPt[%d] = [%0.3f %0.3f] A%0.3f\n", 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; } @@ -376,44 +582,55 @@ LOG( log_modify, 2, ("A=%0.3f X=%0.3f\n", a0, Dex.jointD.x ) ) 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 */ + /* Ignore easements when extending turnouts or turntables */ 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; + } else { /* Easement code */ + if (easementVal<0.0) { //Cornu Join - need to estimate a "good" easement length + d = Dex.curveData.curveRadius * Dex.curveData.a1 * 2.0*M_PI/360.0; + Dex.jointD.d0 = Dex.jointD.d1 =0.75*72*12/GetTrkScale(Dex.Trk); //Easement 1.5 cars long to start + if (Dex.jointD.d0>(GetTrkLength(Dex.Trk,0,1)/2)) + Dex.jointD.d0 = GetTrkLength(Dex.Trk,0,1)/2; + if (Dex.jointD.d1>d/2) + Dex.jointD.d1 = d/2; + Dex.jointD.negate = DifferenceBetweenAngles(Dex.angle,FindAngle(Dex.pos00,pos))<0.0; + Dex.jointD.x = 2*trackGauge; //Signal an easement present to JoinTracks + } else { + 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 (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.jointD.d1; + 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 ) ) } - 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; @@ -421,9 +638,9 @@ LOG( log_modify, 2, ("A=%0.3f X=%0.3f\n", a0, Dex.jointD.x ) ) 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; + double da = D2R(Dex.curveData.a1); + if (da < 0.0) + da = 2*M_PI + da; a = NormalizeAngle( Dex.angle - FindAngle( Dex.pos00, Dex.curveData.curvePos ) ); if ( a < 180.0 ) a = NormalizeAngle( Dex.curveData.a0-90 ); @@ -433,17 +650,15 @@ LOG( log_modify, 2, ("A=%0.3f X=%0.3f\n", a0, Dex.jointD.x ) ) if (action != C_RDOWN) InfoMessage( _("Curve Track: Radius=%s Length=%s Angle=%0.3f"), FormatDistance( Dex.curveData.curveRadius ), - FormatDistance( Dex.curveData.curveRadius * d), + FormatDistance( Dex.curveData.curveRadius * da), Dex.curveData.a1 ); } - DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); - MainRedraw(); - MapRedraw(); return C_CONTINUE; case C_RUP: +extendTrackUp: changeTrackMode = FALSE; - DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + modifyExtendMode = FALSE; tempSegs_da.cnt = 0; if (Dex.Trk == NULL) return C_CONTINUE; if (!Dex.valid) @@ -453,22 +668,24 @@ LOG( log_modify, 2, ("A=%0.3f X=%0.3f\n", a0, Dex.jointD.x ) ) 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(); - MapRedraw(); - return C_TERMINATE; + if (QueryTrack(Dex.Trk,Q_CAN_EXTEND)) //Check it isn't a turnout end.... + if ( Dex.params.type == curveTypeStraight && + FindDistance(Dex.pos01, Dex.curveData.pos1) > 0 ) { + UndoModify( Dex.Trk ); + AdjustStraightEndPt( Dex.Trk, Dex.params.ep, Dex.curveData.pos1 ); + UndoEnd(); + DrawNewTrack(Dex.Trk ); + return C_TERMINATE; } + if (FindDistance(Dex.pos01, Dex.curveData.pos1) == 0) return C_ERROR; +LOG( log_modify, 1, ("L = %0.3f, P0 = %0.3f, P1 = %0.3f\n", + Dex.params.len, Dex.pos01, Dex.curveData.pos1 ) ) 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 ) ) +LOG( log_modify, 1, ("R = %0.3f, A0 = %0.3f, A1 = %0.3f\n", + Dex.curveData.curveRadius, 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 ); @@ -478,39 +695,86 @@ LOG( log_modify, 1, ("A0 = %0.3f, A1 = %0.3f\n", } 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 ); + if (Dex.jointD.d1 == 0) { + DrawEndPt( &mainD, Dex.Trk, Dex.params.ep, wDrawColorWhite ); + ConnectTracks(Dex.Trk, Dex.params.ep, trk, inx); + DrawEndPt( &mainD, Dex.Trk, Dex.params.ep, wDrawColorBlack ); + } else { + UndrawNewTrack( Dex.Trk ); + JoinTracks( Dex.Trk, Dex.params.ep, Dex.pos00, trk, inx, Dex.pos01, &Dex.jointD ); + DrawNewTrack( Dex.Trk ); + } UndoEnd(); + tempSegs_da.cnt = 0; DrawNewTrack( trk ); - DrawNewTrack( Dex.Trk ); - Dex.Trk = NULL; - MainRedraw(); - MapRedraw(); return C_TERMINATE; case C_REDRAW: if (modifyBezierMode) return ModifyBezier(C_REDRAW, pos); if (modifyCornuMode) return ModifyCornu(C_REDRAW, pos); - if ( (!changeTrackMode) && Dex.Trk && !QueryTrack( Dex.Trk, Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK ) ) - UndrawNewTrack( Dex.Trk ); + if (modifyDrawMode) return ModifyDraw(C_REDRAW, pos); DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + if (anchors_da.cnt) + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + return C_CONTINUE; case C_TEXT: + if ((action>>8) == 'c') { + panCenter = pos; + LOG( log_pan, 2, ( "PanCenter:Mod-%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)0); + return C_CONTINUE; + } + if ((action>>8) == 'e') { + DoZoomExtents(0); + } + if ((action>>8) == '0' || (action>>8 == 'o')) { + PanMenuEnter('o'); + } if ( !Dex.Trk ) return C_CONTINUE; if (modifyBezierMode) return ModifyBezier(action, pos); if (modifyCornuMode) return ModifyCornu(action, pos); + if (modifyDrawMode) + return ModifyDraw(action, pos); + return ModifyTrack( Dex.Trk, action, pos ); + + case C_CMDMENU: + if ( !Dex.Trk ) { + menuPos = pos; + wMenuPopupShow(modPopupM); + return C_CONTINUE; + } + if (modifyBezierMode) + return ModifyBezier(action, pos); + if (modifyCornuMode) + return ModifyCornu(action, pos); + if (modifyDrawMode) + return ModifyDraw(action, pos); return ModifyTrack( Dex.Trk, action, pos ); + case C_LCLICK: + if ( modifyDrawMode) { + rc = ModifyDraw(C_DOWN, pos); + if (rc == C_CONTINUE) + return ModifyDraw(C_UP, pos); + } + if (modifyCornuMode) + return ModifyCornu(action, pos); + /*no break*/ default: if (modifyBezierMode) return ModifyBezier(action, pos); if (modifyCornuMode) return ModifyCornu(action, pos); + if (modifyDrawMode) return ModifyDraw(action, pos); + if (Dex.Trk) + return ModifyTrack( Dex.Trk, action, pos ); return C_CONTINUE; } + return C_CONTINUE; } @@ -521,9 +785,21 @@ LOG( log_modify, 1, ("A0 = %0.3f, A1 = %0.3f\n", */ #include "bitmaps/extend.xpm" +extern wIndex_t panCmdInx; +extern wIndex_t selectCmdInx; +extern wIndex_t describeCmdInx; + void InitCmdModify( wMenu_p menu ) { - modifyCmdInx = AddMenuButton( menu, CmdModify, "cmdModify", _("Modify"), wIconCreatePixMap(extend_xpm), LEVEL0_50, IC_STICKY|IC_POPUP, ACCL_MODIFY, NULL ); + modifyCmdInx = AddMenuButton( menu, CmdModify, "cmdModify", _("Modify"), wIconCreatePixMap(extend_xpm), LEVEL0_50, IC_STICKY|IC_POPUP|IC_WANT_MOVE|IC_CMDMENU, ACCL_MODIFY, NULL ); log_modify = LogFindIndex( "modify" ); + modPopupM = MenuRegister( "Modify Context Menu" ); + wMenuPushCreate(modPopupM, "cmdSelectMode", GetBalloonHelpStr("cmdSelectMode"), 0, DoCommandB, (void*) (intptr_t) selectCmdInx); + wMenuPushCreate(modPopupM, "cmdDescribeMode", GetBalloonHelpStr("cmdDescribeMode"), 0, DoCommandB, (void*) (intptr_t) describeCmdInx); + wMenuPushCreate(modPopupM, "cmdPanMode", GetBalloonHelpStr("cmdPanMode"), 0, DoCommandB, (void*) (intptr_t) panCmdInx); + wMenuSeparatorCreate(modPopupM); + wMenuPushCreate(modPopupM, "", _("Zoom In"), 0,(wMenuCallBack_p) DoZoomUp, (void*) 1); + wMenuPushCreate(modPopupM, "", _("Zoom Out"), 0, (wMenuCallBack_p) DoZoomDown, (void*) 1); + wMenuPushCreate(modPopupM, "", _("Pan center - 'c'"), 0, (wMenuCallBack_p) PanHere, (void*) 3); } diff --git a/app/bin/cnote.c b/app/bin/cnote.c index 3cbd28d..0a015f1 100644 --- a/app/bin/cnote.c +++ b/app/bin/cnote.c @@ -1,5 +1,5 @@ /** \file cnote.c - * NOTE + * Main layout note */ /* XTrkCad - Model Railroad CAD @@ -21,26 +21,13 @@ */ #include <string.h> -#include "cundo.h" #include "custom.h" +#include "dynstring.h" #include "fileio.h" #include "i18n.h" +#include "misc.h" #include "param.h" -#include "track.h" -#include "utility.h" - -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" +#include "include/utf8convert.h" static char * mainText = NULL; static wWin_p noteW; @@ -54,18 +41,6 @@ static paramData_t notePLs[] = { 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) { @@ -82,12 +57,6 @@ static void NoteOk(void * junk) 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); @@ -98,7 +67,7 @@ void DoNote(void) { if (noteW == NULL) { noteW = ParamCreateDialog(¬ePG, MakeWindowTitle(_("Note")), _("Ok"), NoteOk, - NULL, FALSE, NULL, F_RESIZE, NULL); + wHide, FALSE, NULL, F_NOTTRANSIENT|F_RESIZE, NULL); } wTextClear(noteT); @@ -109,365 +78,66 @@ void DoNote(void) } - -/***************************************************************************** - * NOTE OBJECT - */ - -static void DrawNote(track_p t, drawCmd_p d, wDrawColor color) -{ - struct extraData *xx = GetTrkExtraData(t); - coOrd p[4]; - - if (d->scale >= 16) { - return; - } - - if ((d->funcs->options & wDrawOptTemp) == 0) { - DrawBitMap(d, xx->pos, note_bm, color); - } else { - DIST_T dist; - 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; - unsigned int layer; -} noteData; -typedef enum { OR, LY, TX } noteDesc_e; -static descData_t noteDesc[] = { - /*OR*/ { DESC_POS, N_("Position"), ¬eData.pos }, - /*LY*/ { DESC_LAYER, N_("Layer"), ¬eData.layer }, - /*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); - - switch (inx) { - case OR: - xx->pos = noteData.pos; - SetBoundingBox(trk, xx->pos, xx->pos); - MainRedraw(); - break; - - case LY: - SetTrkLayer(trk, noteData.layer); - MainRedraw(); - break; - - case -1: - if (wTextGetModified((wText_p)noteDesc[TX].control0)) { - int len; - - 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'; - } - MainRedraw(); - 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_NOREDRAW; - 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) +BOOL_T WriteMainNote(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; + char *noteText = mainText; + + if (noteText && *noteText) { +#ifdef WINDOWS + char *out = NULL; + if (RequiresConvToUTF8(mainText)) { + unsigned cnt = strlen(mainText) * 2 + 1; + out = MyMalloc(cnt); + wSystemToUTF8(mainText, out, cnt); + noteText = out; + } +#endif // WINDOWS + + + char * sText = ConvertToEscapedText( noteText ); + rc &= fprintf(f, "NOTE MAIN 0 0 0 0 0 \"%s\"\n", sText )>0; + MyFree( sText ); + +#ifdef WINDOWS + if (out) { + MyFree(out); + } +#endif // WINDOWS } - - 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; } +/** + * Read the layout main note + * + * \param line complete NOTE statement + */ -static void ReadNote(char * line) +BOOL_T ReadMainNote(char *line) { - coOrd pos; - DIST_T elev; - CSIZE_T size; - char * cp; - struct extraData *xx; - 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; - } + long size; + char * sNote = NULL; - if (mainText) { - MyFree(mainText); - } - - mainText = (char*)MyMalloc(size+2); - cp = mainText; - } else { - track_p t; - - 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) { - int len; - 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'; + if (!GetArgs(line + 9, + paramVersion < 3 ? "l" : + paramVersion < 12 ? "0000l": + "0000lq", &size, &sNote)) { + return FALSE; } - *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 %lu\n", strlen(mainText))>0; - rc &= fprintf(f, "%s", mainText)>0; - rc &= fprintf(f, " END\n")>0; + if (mainText) { + MyFree(mainText); } - return rc; + if ( paramVersion < 12 ) + mainText = ReadMultilineText(); + else + mainText = sNote; + return TRUE; } -/***************************************************************************** - * NOTE COMMAND - */ - - - -static STATUS_T CmdNote(wAction_t action, coOrd pos) -{ - static coOrd oldPos; - track_p trk; - struct extraData * xx; - const char* tmpPtrText; - static int state_on = FALSE; - - switch (action) { - case C_START: - InfoMessage(_("Place a note on the layout")); - return C_CONTINUE; - - case C_DOWN: - state_on = TRUE; - oldPos = pos; - MainRedraw(); - return C_CONTINUE; - - case C_MOVE: - oldPos = pos; - MainRedraw(); - return C_CONTINUE; - break; - - case C_UP: - UndoStart(_("New Note"), "New Note"); - state_on = FALSE; - MainRedraw(); - 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: - if (state_on) 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) +void InitCmdNote() { ParamRegister(¬ePG); - AddMenuButton(menu, CmdNote, "cmdNote", _("Note"), wIconCreatePixMap(cnote_xpm), - LEVEL0_50, IC_POPUP2, ACCL_NOTE, NULL); -} - -void InitTrkNote(void) -{ - note_bm = wDrawBitMapCreate(mainD.d, note_width, note_width, 8, 8, note_bits); - T_NOTE = InitObject(¬eCmds); } diff --git a/app/bin/common.h b/app/bin/common.h index 255e8d7..2db961f 100644 --- a/app/bin/common.h +++ b/app/bin/common.h @@ -24,6 +24,7 @@ #define COMMON_H #include <stdlib.h> +#include <stdint.h> #ifndef TRUE #define TRUE (1) @@ -46,6 +47,11 @@ typedef struct { POS_T x,y; } coOrd; +typedef struct { + coOrd pt; + int pt_type; +} pts_t; + typedef int INT_T; typedef int BOOL_T; @@ -61,6 +67,11 @@ typedef int TRKINX_T; typedef long DEBUGF_T; typedef int REGION_T; +enum paramFileState { PARAMFILE_UNLOADED = 0, PARAMFILE_NOTUSABLE, PARAMFILE_COMPATIBLE, PARAMFILE_FIT, PARAMFILE_MAXSTATE }; + +#define SCALE_ANY (-2) +#define SCALE_DEMO (-1) + typedef struct { int cnt; int max; @@ -108,6 +119,17 @@ typedef struct { } \ (DA).max = 0; \ (DA).cnt = 0; } +#define DYNARR_REMOVE(T,DA,I) \ + { \ + { if ((DA).cnt-1 > I) { \ + for (int i=I;i<(DA).cnt-1;i++) { \ + (((T*)(DA).ptr)[i])= (((T*)(DA).ptr)[i+1]); \ + } \ + } \ + } \ + if ((DA.cnt)>=I) (DA).cnt--; \ + } + #ifdef WINDOWS #define M_PI 3.14159 diff --git a/app/bin/compound.c b/app/bin/compound.c index d1a16f5..627d2ef 100644 --- a/app/bin/compound.c +++ b/app/bin/compound.c @@ -38,6 +38,7 @@ #include "track.h" #include "utility.h" #include "messages.h" +#include "include/paramfile.h" /***************************************************************************** * @@ -45,6 +46,25 @@ * */ +//Convert the internal path segment into the external one - which is based on the index count of only the track segments + +char ConvertPathSegToExternal(char signed pp, int segCnt,trkSeg_p segs) { + + char signed new_pp; + int old_inx; + EPINX_T old_EP; + GetSegInxEP(pp,&old_inx,&old_EP); + int j = old_inx; + for (int i=0;i<old_inx;i++) { + if ( !IsSegTrack(&segs[i]) ) { + j--; + } + } + SetSegInxEP(&new_pp,j,old_EP); + return new_pp; + +} + BOOL_T WriteCompoundPathsEndPtsSegs( FILE * f, PATHPTR_T paths, @@ -55,11 +75,12 @@ BOOL_T WriteCompoundPathsEndPtsSegs( { 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; + for ( pp+=strlen((char *)pp)+1; pp[0]!=0 || pp[1]!=0; pp++ ) + rc &= fprintf( f, " %d", ConvertPathSegToExternal(pp[0],segCnt,segs) )>0; rc &= fprintf( f, "\n" )>0; } for ( i=0; i<endPtCnt; i++ ) @@ -315,7 +336,7 @@ void DrawCompoundDescription( return; if ((labelEnable&LABELENABLE_TRKDESC)==0) return; - if ( (d->options&DC_GROUP) ) + if ( (d->options&DC_SIMPLE) ) return; if ( xx->special == TOpier ) { desc = xx->u.pier.name; @@ -340,18 +361,25 @@ void DrawCompoundDescription( DIST_T CompoundDescriptionDistance( coOrd pos, - track_p trk ) + track_p trk, + coOrd * dpos, + BOOL_T show_hidden, + BOOL_T * hidden) { struct extraData *xx = GetTrkExtraData(trk); coOrd p1; if (GetTrkType(trk) != T_TURNOUT && GetTrkType(trk) != T_STRUCTURE) return 100000; - if ( (GetTrkBits( trk ) & TB_HIDEDESC) != 0 ) + if ( ((GetTrkBits( trk ) & TB_HIDEDESC) != 0 ) && !show_hidden) return 100000; p1 = xx->descriptionOrig; + coOrd offset = xx->descriptionOff; + if ( (GetTrkBits( trk ) & TB_HIDEDESC) != 0 ) offset = zero; Rotate( &p1, zero, xx->angle ); - p1.x += xx->orig.x + xx->descriptionOff.x; - p1.y += xx->orig.y + xx->descriptionOff.y; + p1.x += xx->orig.x + offset.x; + p1.y += xx->orig.y + offset.y; + if (hidden) *hidden = (GetTrkBits( trk ) & TB_HIDEDESC); + *dpos = p1; return FindDistance( p1, pos ); } @@ -370,6 +398,7 @@ STATUS_T CompoundDescriptionMove( case C_DOWN: editMode = TRUE; REORIGIN( p0, xx->descriptionOrig, xx->angle, xx->orig ) + DrawCompoundDescription( trk, &mainD, wDrawColorWhite ); case C_MOVE: case C_UP: @@ -383,13 +412,15 @@ STATUS_T CompoundDescriptionMove( if (action == C_UP) { editMode = FALSE; } - MainRedraw(); - MapRedraw(); + if ( action == C_UP ) { + DrawCompoundDescription( trk, &mainD, color ); + } return action==C_UP?C_TERMINATE:C_CONTINUE; break; case C_REDRAW: if (editMode) { - DrawLine( &tempD, p0, p1, 0, wDrawColorBlack ); + DrawCompoundDescription( trk, &tempD, wDrawColorBlue ); + DrawLine( &tempD, p0, p1, 0, wDrawColorBlue ); } } @@ -405,6 +436,18 @@ STATUS_T CompoundDescriptionMove( * */ +EXPORT void SetSegInxEP( + signed char * segChar, + int segInx, + EPINX_T segEP ) +{ + if (segEP == 1) { + * segChar = -(segInx+1); + } else { + * segChar = (segInx+1); + } + +} EXPORT void GetSegInxEP( signed char segChar, @@ -482,6 +525,7 @@ static struct { FLOAT_T elev[4]; coOrd orig; ANGLE_T angle; + descPivot_t pivot; char manuf[STR_SIZE]; char name[STR_SIZE]; char partno[STR_SIZE]; @@ -490,9 +534,10 @@ static struct { long pathCnt; FLOAT_T grade; DIST_T length; + drawLineType_e linetype; unsigned int layerNumber; } compoundData; -typedef enum { E0, A0, C0, R0, Z0, E1, A1, C1, R1, Z1, E2, A2, C2, R2, Z2, E3, A3, C3, R3, Z3, GR, OR, AN, MN, NM, PN, EC, SC, LY } compoundDesc_e; +typedef enum { E0, A0, C0, R0, Z0, E1, A1, C1, R1, Z1, E2, A2, C2, R2, Z2, E3, A3, C3, R3, Z3, GR, OR, AN, PV, MN, NM, PN, LT, SC, LY } compoundDesc_e; static descData_t compoundDesc[] = { /*E0*/ { DESC_POS, N_("End Pt 1: X,Y"), &compoundData.endPt[0] }, /*A0*/ { DESC_ANGLE, N_("Angle"), &compoundData.endAngle[0] }, @@ -517,10 +562,11 @@ static descData_t compoundDesc[] = { /*GR*/ { DESC_FLOAT, N_("Grade"), &compoundData.grade }, /*OR*/ { DESC_POS, N_("Origin: X,Y"), &compoundData.orig }, /*AN*/ { DESC_ANGLE, N_("Angle"), &compoundData.angle }, +/*PV*/ { DESC_PIVOT, N_("Pivot"), &compoundData.pivot }, /*MN*/ { DESC_STRING, N_("Manufacturer"), &compoundData.manuf, sizeof(compoundData.manuf)}, /*NM*/ { DESC_STRING, N_("Name"), &compoundData.name, sizeof(compoundData.name) }, /*PN*/ { DESC_STRING, N_("Part No"), &compoundData.partno, sizeof(compoundData.partno)}, -/*EC*/ { DESC_LONG, N_("# End Pts"), &compoundData.epCnt }, +/*LT*/ { DESC_LIST, N_("LineType"), &compoundData.linetype }, /*SC*/ { DESC_LONG, N_("# Segments"), &compoundData.segCnt }, /*LY*/ { DESC_LAYER, N_("Layer"), &compoundData.layerNumber }, { DESC_NULL } }; @@ -539,7 +585,11 @@ static void UpdateCompound( track_p trk, int inx, descData_p descUpd, BOOL_T nee BOOL_T titleChanged, flipped, ungrouped, split; char * newTitle; - if ( inx == -1 ) { + switch ( inx ) { + case -1: + case MN: + case NM: + case PN: titleChanged = FALSE; ParseCompoundTitle( xtitle(xx), &mP, &mL, &nP, &nL, &pP, &pL ); if (mP == NULL) mP = ""; @@ -616,7 +666,7 @@ static void UpdateCompound( track_p trk, int inx, descData_p descUpd, BOOL_T nee GetBoundingBox( trk, &hi, &lo ); if ( labelScale >= mainD.scale && !OFF_MAIND( lo, hi ) ) { - DrawCompoundDescription( trk, &tempD, GetTrkColor(trk,&tempD) ); + DrawCompoundDescription( trk, &mainD, wDrawColorWhite ); } /*sprintf( message, "%s\t%s\t%s", manufS, nameS, partnoS );*/ if (xx->title) MyFree(xx->title); @@ -626,12 +676,13 @@ static void UpdateCompound( track_p trk, int inx, descData_p descUpd, BOOL_T nee xx->split = split; if ( labelScale >= mainD.scale && !OFF_MAIND( lo, hi ) ) { - DrawCompoundDescription( trk, &tempD, GetTrkColor(trk,&tempD) ); + DrawCompoundDescription( trk, &mainD, GetTrkColor(trk,&tempD) ); } return; } UndrawNewTrack( trk ); + coOrd orig; switch ( inx ) { case OR: pos.x = compoundData.orig.x - xx->orig.x; @@ -643,17 +694,31 @@ static void UpdateCompound( track_p trk, int inx, descData_p descUpd, BOOL_T nee case A1: case A2: case A3: - if (inx==E3) ep=3; - else if (inx==E2) ep=2; - else if (inx==E1) ep=1; + if (inx==A3) ep=3; + else if (inx==A2) ep=2; + else if (inx==A1) ep=1; else ep=0; - RotateTrack( trk, xx->orig, NormalizeAngle( compoundData.endAngle[ep]-xx->angle ) ); + RotateTrack( trk, GetTrkEndPos(trk,ep), NormalizeAngle( compoundData.endAngle[ep]-GetTrkEndAngle(trk,ep) ) ); ComputeCompoundBoundingBox( trk ); - compoundData.angle = xx->angle; - compoundDesc[AN].mode |= DESC_CHANGE; break; case AN: - RotateTrack( trk, xx->orig, NormalizeAngle( compoundData.angle-xx->angle ) ); + orig = xx->orig; + GetBoundingBox(trk,&hi,&lo); + switch (compoundData.pivot) { + case DESC_PIVOT_MID: + orig.x = (hi.x-lo.x)/2+lo.x; + orig.y = (hi.y-lo.y)/2+lo.y; + break; + case DESC_PIVOT_SECOND: + orig.x = (hi.x-lo.x)/2+lo.x; + orig.y = (hi.y-lo.y)/2+lo.y; + orig.x = (orig.x - xx->orig.x)*2+xx->orig.x; + orig.y = (orig.y - xx->orig.y)*2+xx->orig.y; + break; + default: + break; + } + RotateTrack( trk, orig, NormalizeAngle( compoundData.angle-xx->angle ) ); ComputeCompoundBoundingBox( trk ); break; case E0: @@ -680,7 +745,7 @@ static void UpdateCompound( track_p trk, int inx, descData_p descUpd, BOOL_T nee break; for (int i=0;i<compoundData.epCnt;i++) { if (i==ep) continue; - ComputeElev( trk, i, FALSE, &compoundData.elev[i], NULL ); + ComputeElev( trk, i, FALSE, &compoundData.elev[i], NULL, TRUE ); } if ( compoundData.length > minLength ) compoundData.grade = fabs( (compoundData.elev[0]-compoundData.elev[1])/compoundData.length )*100.0; @@ -719,6 +784,13 @@ static void UpdateCompound( track_p trk, int inx, descData_p descUpd, BOOL_T nee compoundDesc[i*(E1-E0)+C0].mode |= DESC_CHANGE; } } + compoundData.orig = xx->orig; + compoundDesc[OR].mode |= DESC_CHANGE; + compoundData.angle = xx->angle; + compoundDesc[AN].mode |= DESC_CHANGE; + break; + case LT: + xx->lineType = compoundData.linetype; break; default: break; @@ -839,9 +911,10 @@ void DescribeCompound( compoundDesc[MN].mode = compoundDesc[NM].mode = compoundDesc[PN].mode = 0 /*DESC_NOREDRAW*/; - compoundDesc[EC].mode = compoundDesc[SC].mode = DESC_RO; compoundDesc[LY].mode = DESC_NOREDRAW; + compoundDesc[PV].mode = 0; + compoundData.pivot = DESC_PIVOT_FIRST; if (compoundData.epCnt >0) { for (int i=0;(i<compoundData.epCnt)&&(i<MAX_DESCRIBE_ENDS);i++) { compoundDesc[A0+(E1-E0)*i].mode = (int)mode; @@ -859,18 +932,37 @@ void DescribeCompound( compoundDesc[C0+(E1-E0)*i].mode = DESC_IGNORE; compoundDesc[R0+(E1-E0)*i].mode = DESC_IGNORE; } - ComputeElev( trk, i, FALSE, &compoundData.elev[i], NULL ); + ComputeElev( trk, i, FALSE, &compoundData.elev[i], NULL, FALSE ); compoundDesc[Z0+(E1-E0)*i].mode = (EndPtIsDefinedElev(trk,i)?0:DESC_RO)|DESC_NOREDRAW; } compoundDesc[GR].mode = DESC_RO; } + if ( compoundData.epCnt == 2 ) + compoundData.length = GetTrkLength( trk, 0, 1 ); if ( compoundData.length > minLength && compoundData.epCnt > 1) compoundData.grade = fabs( (compoundData.elev[0]-compoundData.elev[1])/compoundData.length )*100.0; else compoundData.grade = 0.0; + if (GetTrkEndPtCnt(trk) == 0) { + compoundDesc[LT].mode = 0; + } else + compoundDesc[LT].mode = DESC_IGNORE; + DoDescribe(trackType, trk, compoundDesc, UpdateCompound); + if ( compoundDesc[LT].control0!=NULL) { + wListClear( (wList_p)compoundDesc[LT].control0 ); + wListAddValue( (wList_p)compoundDesc[LT].control0, _("Solid"), NULL, (void*)0 ); + wListAddValue( (wList_p)compoundDesc[LT].control0, _("Dash"), NULL, (void*)1 ); + wListAddValue( (wList_p)compoundDesc[LT].control0, _("Dot"), NULL, (void*)2 ); + wListAddValue( (wList_p)compoundDesc[LT].control0, _("DashDot"), NULL, (void*)3 ); + wListAddValue( (wList_p)compoundDesc[LT].control0, _("DashDotDot"), NULL, (void*)4 ); + wListAddValue( (wList_p)compoundDesc[LT].control0, _("CenterDot"), NULL, (void*)5 ); + wListAddValue( (wList_p)compoundDesc[LT].control0, _("PhantomDot"), NULL, (void*)6 ); + wListSetIndex( (wList_p)compoundDesc[LT].control0, compoundData.linetype ); + } + } @@ -880,6 +972,7 @@ void DeleteCompound( struct extraData *xx = GetTrkExtraData(t); FreeFilledDraw( xx->segCnt, xx->segs ); MyFree( xx->segs ); + xx->segs = NULL; } @@ -891,6 +984,7 @@ BOOL_T WriteCompound( EPINX_T ep, epCnt; long options; long position = 0; + drawLineType_e lineType = 0; PATHPTR_T path; BOOL_T rc = TRUE; @@ -918,10 +1012,11 @@ BOOL_T WriteCompound( position++; } } - rc &= fprintf(f, "%s %d %d %ld %ld 0 %s %d %0.6f %0.6f 0 %0.6f \"%s\"\n", + lineType = xx->lineType; + rc &= fprintf(f, "%s %d %d %ld %ld %d %s %d %0.6f %0.6f 0 %0.6f \"%s\"\n", GetTrkTypeName(t), - GetTrkIndex(t), GetTrkLayer(t), options, position, - GetTrkScaleName(t), GetTrkVisible(t), + GetTrkIndex(t), GetTrkLayer(t), options, position, lineType, + GetTrkScaleName(t), GetTrkVisible(t)|(GetTrkNoTies(t)?1<<2:0)|(GetTrkBridge(t)?1<<3:0), xx->orig.x, xx->orig.y, xx->angle, PutTitle(xtitle(xx)) )>0; for (ep=0; ep<epCnt; ep++ ) @@ -933,6 +1028,8 @@ BOOL_T WriteCompound( break; case TOpier: rc &= fprintf( f, "\tX %s %0.6f \"%s\"\n", PIER, xx->u.pier.height, xx->u.pier.name )>0; + break; + default: ; } @@ -950,6 +1047,34 @@ BOOL_T WriteCompound( * */ +EXPORT void SetCompoundLineType( track_p trk, int width ) { + struct extraData * xx = GetTrkExtraData(trk); + switch(width) { + case 0: + xx->lineType = DRAWLINESOLID; + break; + case 1: + xx->lineType = DRAWLINEDASH; + break; + case 2: + xx->lineType = DRAWLINEDOT; + break; + case 3: + xx->lineType = DRAWLINEDASHDOT; + break; + case 4: + xx->lineType = DRAWLINEDASHDOTDOT; + break; + case 5: + xx->lineType = DRAWLINECENTER; + break; + case 6: + xx->lineType = DRAWLINEPHANTOM; + break; + } +} + + EXPORT track_p NewCompound( TRKTYP_T trkType, @@ -959,6 +1084,7 @@ EXPORT track_p NewCompound( char * title, EPINX_T epCnt, trkEndPt_t * epp, + DIST_T * radii, int pathLen, char * paths, wIndex_t segCnt, @@ -994,13 +1120,23 @@ EXPORT track_p NewCompound( FixUpBezierSegs(xx->segs,xx->segCnt); ComputeCompoundBoundingBox( trk ); SetDescriptionOrig( trk ); - for ( ep=0; ep<epCnt; ep++ ) +// if (radii) { +// xx->special = TOcurved; +// xx->u.curved.radii.max = 0; +// xx->u.curved.radii.cnt = 0; +// DYNARR_SET(DIST_T,xx->u.curved.radii,epCnt); +// } + for ( ep=0; ep<epCnt; ep++ ) { SetTrkEndPoint( trk, ep, epp[ep].pos, epp[ep].angle ); +// if (radii) { +// DYNARR_N(DIST_T,xx->u.curved.radii,ep) = radii[ep]; +// } + } return trk; } -void ReadCompound( +BOOL_T ReadCompound( char * line, TRKTYP_T trkType ) { @@ -1017,26 +1153,28 @@ void ReadCompound( char *cp; long options = 0; long position = 0; + long lineType = 0; PATHPTR_T path=NULL; if (paramVersion<3) { if ( !GetArgs( line, "dXsdpfq", &index, &layer, scale, &visible, &orig, &angle, &title ) ) - return; + return FALSE; } else if (paramVersion <= 5 && trkType == T_STRUCTURE) { if ( !GetArgs( line, "dL00sdpfq", &index, &layer, scale, &visible, &orig, &angle, &title ) ) - return; + return FALSE; } else { - if ( !GetArgs( line, paramVersion<9?"dLll0sdpYfq":"dLll0sdpffq", - &index, &layer, &options, &position, scale, &visible, &orig, &elev, &angle, &title ) ) - return; + if ( !GetArgs( line, paramVersion<9?"dLlldsdpYfq":"dLlldsdpffq", + &index, &layer, &options, &position, &lineType, scale, &visible, &orig, &elev, &angle, &title ) ) + return FALSE; } if (paramVersion >=3 && paramVersion <= 5 && trkType == T_STRUCTURE) strcpy( scale, curScaleName ); DYNARR_RESET( trkEndPt_t, tempEndPts_da ); pathCnt = 0; - ReadSegs(); + if ( !ReadSegs() ) + return FALSE; path = pathPtr; if ( tempEndPts_da.cnt > 0 && pathCnt <= 1 ) { pathCnt = 10; @@ -1051,9 +1189,17 @@ void ReadCompound( UpdateTitleMark( title, LookupScale(scale) ); } } - trk = NewCompound( trkType, index, orig, angle, title, 0, NULL, pathCnt, (char *)path, tempSegs_da.cnt, &tempSegs(0) ); + trk = NewCompound( trkType, index, orig, angle, title, 0, NULL, NULL, pathCnt, (char *)path, tempSegs_da.cnt, &tempSegs(0) ); SetEndPts( trk, 0 ); - SetTrkVisible(trk, visible); + if ( paramVersion < 3 ) { + SetTrkVisible(trk, visible!=0); + SetTrkNoTies(trk, FALSE); + SetTrkBridge(trk, FALSE); + } else { + SetTrkVisible(trk, visible&2); + SetTrkNoTies(trk, visible&4); + SetTrkBridge(trk, visible&8); + } SetTrkScale(trk, LookupScale( scale )); SetTrkLayer(trk, layer); SetTrkWidth(trk, (int)(options&3)); @@ -1062,68 +1208,27 @@ void ReadCompound( xx->flipped = (int)((options&0x10)!=0); xx->ungrouped = (int)((options&0x20)!=0); xx->split = (int)((options&0x40)!=0); + xx->lineType = lineType; 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 ); + if ( !GetArgs( tempSpecial+strlen(ADJUSTABLE), "ff", + &xx->u.adjustable.minD, &xx->u.adjustable.maxD ) ) + return FALSE; } else if (strncmp( tempSpecial, PIER, strlen(PIER) ) == 0) { xx->special = TOpier; - GetArgs( tempSpecial+strlen(PIER), "fq", - &xx->u.pier.height, &xx->u.pier.name ); + if ( !GetArgs( tempSpecial+strlen(PIER), "fq", + &xx->u.pier.height, &xx->u.pier.name ) ) + return FALSE; } else { InputError("Unknown special case", TRUE); + return FALSE; } } if (pathCnt > 0) { @@ -1138,7 +1243,7 @@ void ReadCompound( } } xx->pathCurr = path; - + return TRUE; } void MoveCompound( @@ -1217,12 +1322,12 @@ void FlipCompound( 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 ); + xx->title = MyStrdup( 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 ); + xx->title = MyStrdup( message ); return; } } diff --git a/app/bin/compound.h b/app/bin/compound.h index 4845f78..b4c63ca 100644 --- a/app/bin/compound.h +++ b/app/bin/compound.h @@ -26,7 +26,7 @@ #include "common.h" #include "track.h" -typedef enum { TOnormal, TOadjustable, TOpierInfo, TOpier, TOcarDesc, TOlast } TOspecial_e; +typedef enum { TOnormal, TOadjustable, TOpierInfo, TOpier, TOcarDesc, TOlast, TOcurved } TOspecial_e; typedef struct { char * name; @@ -44,6 +44,9 @@ typedef union { FLOAT_T height; char * name; } pier; + struct { + dynArr_t radii; + } curved; } turnoutInfo_u; typedef struct turnoutInfo_t{ @@ -91,6 +94,8 @@ struct extraData { PATHPTR_T pathCurr; wIndex_t segCnt; trkSeg_t * segs; + DIST_T * radii; + drawLineType_e lineType; }; #endif @@ -111,6 +116,7 @@ extern turnoutInfo_t * curStructure; #define ADJUSTABLE "adjustable" #define PIER "pier" +#define CURVED "curvedends" /* compound.c */ #define FIND_TURNOUT (1<<11) @@ -129,14 +135,15 @@ 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 ); +track_p NewCompound( TRKTYP_T, TRKINX_T, coOrd, ANGLE_T, char *, EPINX_T, trkEndPt_t *, DIST_T *, int, char *, wIndex_t, trkSeg_p ); BOOL_T WriteCompound( track_p, FILE * ); -void ReadCompound( char *, TRKTYP_T ); +BOOL_T 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 ); +void SetCompoundLineType( track_p trk, int width ); /* cgroup.c */ void UngroupCompound( track_p ); @@ -147,27 +154,34 @@ void DoGroup( void ); void UpdateTitleMark( char *, SCALEINX_T ); void DoUpdateTitles( void ); BOOL_T RefreshCompound( track_p, BOOL_T ); +wIndex_t FindListItemByContext( wList_p, void *); + /* cturnout.c */ EPINX_T TurnoutPickEndPt( coOrd p, track_p ); +BOOL_T SplitTurnoutCheck(track_p,coOrd,EPINX_T ep,track_p *,EPINX_T *,EPINX_T *,BOOL_T check, coOrd *, ANGLE_T *); void GetSegInxEP( signed char, int *, EPINX_T * ); +void SetSegInxEP( 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 * CreateNewTurnout( char *, char *, wIndex_t, trkSeg_p, wIndex_t, PATHPTR_T, EPINX_T, trkEndPt_t *, DIST_T *, wBool_t ); +void DeleteTurnoutParams(int fileInx); 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 ); - +enum paramFileState GetTrackCompatibility(int paramFileIndex, SCALEINX_T scaleIndex); /* 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 ); +enum paramFileState GetStructureCompatibility(int paramFileIndex, SCALEINX_T scaleIndex); turnoutInfo_t * StructAdd( long, SCALEINX_T, wList_p, coOrd * ); STATUS_T CmdStructureAction( wAction_t, coOrd ); BOOL_T StructLoadCarDescList( wList_p ); +void DeleteStructures(int fileIndex); /* cstrdsgn.c */ void EditCustomStructure( turnoutInfo_t * ); diff --git a/app/bin/cparalle.c b/app/bin/cparalle.c index 8e70408..27276b1 100644 --- a/app/bin/cparalle.c +++ b/app/bin/cparalle.c @@ -30,162 +30,274 @@ #include "param.h" #include "track.h" #include "utility.h" +#include "layout.h" static struct { track_p Trk; coOrd orig; + track_p anchor_Trk; } Dpa; static DIST_T parSeparation = 1.0; +static double parSepFactor = 0.0; +static long parType = 0; -static paramFloatRange_t r_0o1_100 = { 0.1, 100.0, 100 }; +enum PAR_TYPE_E { PAR_TRACK, PAR_LINE }; + +static paramFloatRange_t r_0o1_100 = { 0.0, 100.0, 100 }; +static paramFloatRange_t r_0_10 = { 0.0, 10.0 }; static paramData_t parSepPLs[] = { #define parSepPD (parSepPLs[0]) - { PD_FLOAT, &parSeparation, "separation", PDO_DIM|PDO_NOPREF|PDO_NOPREF, &r_0o1_100, N_("Separation") } }; +#define parSepI 0 + { PD_FLOAT, &parSeparation, "separation", PDO_DIM, &r_0o1_100, N_("Separation") }, +#define parFactorPD (parSepPLs[1]) +#define parFactorI 1 + { PD_FLOAT, &parSepFactor, "factor", 0, &r_0_10, N_("Radius Factor") } +}; static paramGroup_t parSepPG = { "parallel", 0, parSepPLs, sizeof parSepPLs/sizeof parSepPLs[0] }; -static STATUS_T CmdParallel( wAction_t action, coOrd pos ) +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; - InfoMessage(_(" Track doesn't support parallel")); - 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 ) + 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[4]; + char * labels[3]; + static DIST_T parRFactor; + + parType = (long)commandContext; + + switch (action&0xFF) { + + case C_START: + if (parSepPLs[0].control==NULL) { + ParamCreateControls(&parSepPG, NULL); + } + if (parType == PAR_TRACK) { + sprintf(message, "parallel-separation-%s", curScaleName); + parSeparation = ceil(13.0*12.0/curScaleRatio); + } else { + sprintf(message, "parallel-line-separation-%s", curScaleName); + parSeparation = 5.0*12.0/curScaleRatio; + } + wPrefGetFloat("misc", message, &parSeparation, parSeparation); + ParamLoadControls(&parSepPG); + ParamGroupRecord(&parSepPG); + parSepPD.option |= PDO_NORECORD; + parFactorPD.option |= PDO_NORECORD; + controls[0] = parSepPD.control; + if (parType == PAR_TRACK) + controls[1] = parFactorPD.control; + else + controls[1] = NULL; + controls[2] = NULL; + labels[0] = N_("Separation"); + labels[1] = N_("Radius Factor"); + InfoSubstituteControls(controls, labels); + parSepPD.option &= ~PDO_NORECORD; + parFactorPD.option &= ~PDO_NORECORD; + Dpa.anchor_Trk = NULL; + tempSegs_da.cnt = 0; + return C_CONTINUE; + + case wActionMove: + tempSegs_da.cnt = 0; + Dpa.anchor_Trk = NULL; + if (parType == PAR_TRACK) + Dpa.anchor_Trk = OnTrack(&pos, FALSE, TRUE); + else + Dpa.anchor_Trk = OnTrack(&pos, FALSE, FALSE); + + if (!Dpa.anchor_Trk) { + return C_CONTINUE; + } + if (Dpa.anchor_Trk && !CheckTrackLayerSilent(Dpa.anchor_Trk)) { + Dpa.anchor_Trk = NULL; + return C_CONTINUE; + } + if (!QueryTrack(Dpa.anchor_Trk, Q_CAN_PARALLEL)) { + Dpa.anchor_Trk = NULL; + return C_CONTINUE; + } + break; + case C_DOWN: + Dpa.anchor_Trk = NULL; + tempSegs_da.cnt = 0; + if (parSeparation < 0.0) { + ErrorMessage(MSG_PARALLEL_SEP_GTR_0); + return C_ERROR; + } + + controls[0] = parSepPD.control; + controls[1] = parFactorPD.control; + controls[2] = NULL; + labels[0] = N_("Separation"); + labels[1] = N_("Radius factor"); + InfoSubstituteControls(controls, labels); + ParamLoadData(&parSepPG); + Dpa.orig = pos; + if (parType == PAR_TRACK) + Dpa.Trk = OnTrack(&pos, FALSE, TRUE); + else + Dpa.Trk = OnTrack(&pos, FALSE, FALSE); //Also lines for line + if (!Dpa.Trk) { + return C_CONTINUE; + } + if (!QueryTrack(Dpa.Trk, Q_CAN_PARALLEL)) { + Dpa.Trk = NULL; + InfoMessage(_(" Track/Line doesn't support parallel")); + wBeep(); + return C_CONTINUE; + } + + parRFactor = (2864.0*(double)parSepFactor)/curScaleRatio; + + if ((parType == PAR_TRACK) && (parSeparation == 0.0)) { + DIST_T orig_gauge = GetTrkGauge(Dpa.Trk); + DIST_T new_gauge = GetScaleTrackGauge(GetLayoutCurScale()); + if (orig_gauge == new_gauge) { + ErrorMessage(MSG_PARALLEL_SEP_GTR_0); + return C_ERROR; + } + parSeparation = fabs(orig_gauge/2-new_gauge/2); + parRFactor = 0.0; + } else if (parType != PAR_TRACK) + parRFactor = 0.0; + /* 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; + /* no break */ + + case C_MOVE: + if (Dpa.Trk == NULL) { + return C_CONTINUE; + } + tempSegs_da.cnt = 0; + if (!MakeParallelTrack(Dpa.Trk, pos, parSeparation, parRFactor, NULL, &p0, &p1, + parType == PAR_TRACK)) { + Dpa.Trk = NULL; + return C_CONTINUE; + } + return C_CONTINUE; + + case C_UP: + Dpa.anchor_Trk = NULL; + if (Dpa.Trk == NULL) { + return C_CONTINUE; + } + t0=t1=NULL; + if (parType == PAR_TRACK) { + p = p0; + tempSegs_da.cnt = 0; + 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 ) + 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 ); + } + UndoStart(_("Create Parallel Track"), "newParallel"); + if (!MakeParallelTrack(Dpa.Trk, pos, parSeparation, parRFactor, &t, NULL, NULL, + parType == PAR_TRACK)) { + tempSegs_da.cnt = 0; + return C_TERMINATE; + } + if (parType == PAR_TRACK) { + if (GetTrkGauge(Dpa.Trk)> parSeparation) { + SetTrkNoTies(t, TRUE); + } + //CopyAttributes( Dpa.Trk, t ); Don't force scale or track width or Layer + SetTrkBits(t,(GetTrkBits(t)&TB_HIDEDESC) | (GetTrkBits(Dpa.Trk)&~TB_HIDEDESC)); + + 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 ); + 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; + } + DrawNewTrack(t); + UndoEnd(); + InfoSubstituteControls(NULL, NULL); + if (parType == PAR_TRACK) + sprintf(message, "parallel-separation-%s", curScaleName); + else + sprintf(message, "parallel-line-separation-%s", curScaleName); + wPrefSetFloat("misc", message, parSeparation); + tempSegs_da.cnt = 0; + return C_TERMINATE; + + case C_REDRAW: + if (Dpa.anchor_Trk) { + DrawTrack(Dpa.anchor_Trk,&tempD, + wDrawColorPreviewSelected); //Special color means THICK3 as well + } + if (tempSegs_da.cnt>0) { + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, + wDrawColorBlack ); + } + return C_CONTINUE; + + case C_CANCEL: + Dpa.anchor_Trk = NULL; + tempSegs_da.cnt = 0; + InfoSubstituteControls(NULL, NULL); + return C_TERMINATE; + + } + return C_CONTINUE; } #include "bitmaps/parallel.xpm" +#include "bitmaps/parallel-line.xpm" EXPORT void InitCmdParallel( wMenu_p menu ) { - AddMenuButton( menu, CmdParallel, "cmdParallel", _("Parallel"), wIconCreatePixMap(parallel_xpm), LEVEL0_50, IC_STICKY|IC_POPUP, ACCL_PARALLEL, NULL ); + ButtonGroupBegin( _("Parallel"), "cmdParallelSetCmd", _("Parallel") ); + AddMenuButton( menu, CmdParallel, "cmdParallelTrack", _("Parallel Track"), wIconCreatePixMap(parallel_xpm), LEVEL0_50, IC_STICKY|IC_POPUP|IC_WANT_MOVE, ACCL_PARALLEL, (void*)0 ); + AddMenuButton( menu, CmdParallel, "cmdParallelLine", _("Parallel Line"), wIconCreatePixMap(parallel_line_xpm), LEVEL0_50, IC_STICKY|IC_POPUP|IC_WANT_MOVE, ACCL_PARALLEL, (void*)1 ); + ButtonGroupEnd(); ParamRegister( &parSepPG ); } diff --git a/app/bin/cprint.c b/app/bin/cprint.c index 88a9151..066e649 100644 --- a/app/bin/cprint.c +++ b/app/bin/cprint.c @@ -25,8 +25,10 @@ #include <string.h> #include <ctype.h> #include <math.h> +#include <stdbool.h> #include "custom.h" +#include "dynstring.h" #include "fileio.h" #include "i18n.h" #include "layout.h" @@ -69,6 +71,7 @@ struct { static long printGaudy = 1; static long printRegistrationMarks = 1; +static long printPageNumbers = 1; static long printPhysSize = FALSE; static long printFormat = PORTRAIT; static long printOrder = 0; @@ -85,7 +88,8 @@ static long iPrintScale = 16; static coOrd maxPageSize; static coOrd realPageSize; -static wWin_p printWin; +static wWin_p printWin = NULL; +static wWin_p printMarginWin = NULL; static wMenu_p printGridPopupM; @@ -98,16 +102,21 @@ static void DoResetGrid( void ); static void DoPrintSetup( void ); static void PrintClear( void ); static void PrintMaxPageSize( void ); +static void SelectAllPages(void); +static void DoPrintMargin(void); +static bool PrintPageNumber( wPos_t x, wPos_t y, DIST_T width, DIST_T height ); +static bool PrintNextPageNumbers(int x, int y, DIST_T pageW, DIST_T pageH); 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 * printRegistrationMarksLabels[] = { N_("Registration Marks (in 1:1 scale only)"), NULL }; +static char * printPageNumberLabels[] = { N_("Page Numbers"), 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 char * printCenterLineLabels[] = { N_("Print Centerline below Scale 1:1"), NULL }; +static char * printGridLabels[] = { N_("Snap Grid"), NULL }; +static char * printRulerLabels[] = { N_("Rulers"), NULL }; +static char * printRoadbedLabels[] = { N_("Roadbed Outline"), NULL }; +static char * printCenterLineLabels[] = { N_("Centerline below Scale 1:1"), 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 }; @@ -122,34 +131,41 @@ static paramData_t printPLs[] = { /*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 }, +/*7*/ { PD_MESSAGE, N_("Print "), NULL, PDO_DLGRESETMARGIN| PDO_DLGNOLABELALIGN, (void*)0 }, +/*8*/ { PD_TOGGLE, &printGaudy, "style", PDO_DLGNOLABELALIGN, printGaudyLabels, 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_CENTERLINE (12) -/*12*/ { PD_TOGGLE, &printCenterLine, "centerLine", PDO_DLGNOLABELALIGN, printCenterLineLabels, NULL, BC_HORZ|BC_NOBORDER }, -#define I_ROADBED (13) -/*13*/{ PD_TOGGLE, &printRoadbed, "roadbed", PDO_DLGNOLABELALIGN, printRoadbedLabels, NULL, BC_HORZ|BC_NOBORDER }, -#define I_ROADBEDWIDTH (14) -/*14*/{ PD_FLOAT, &printRoadbedWidth, "roadbedWidth", PDO_DIM|PDO_DLGBOXEND, &r0_, N_("Width") }, -/*15*/{ PD_FLOAT, &newPrintGrid.orig.x, "origx", PDO_DIM|PDO_DLGRESETMARGIN, &r_10_99999, N_("Origin: X"), 0, (void*)2 }, -/*16*/ { PD_FLOAT, &newPrintGrid.orig.y, "origy", PDO_DIM, &r_10_99999, N_("Y"), 0, (void*)2 }, -/*17*/ { PD_BUTTON, (void*)DoResetGrid, "reset", PDO_DLGHORZ, NULL, N_("Reset") }, -/*18*/ { PD_FLOAT, &newPrintGrid.angle, "origa", PDO_ANGLE|PDO_DLGBOXEND, &r0_360, N_("Angle"), 0, (void*)2 }, -/*19*/ { PD_BUTTON, (void*)DoPrintSetup, "setup", PDO_DLGCMDBUTTON, NULL, N_("Setup") }, -/*20*/ { PD_BUTTON, (void*)PrintClear, "clear", 0, NULL, N_("Clear") }, -#define I_PAGECNT (21) -/*21*/ { PD_MESSAGE, N_("0 pages"), NULL, 0, (void*)80 }, -/*22*/ { PD_MESSAGE, N_("selected"), NULL, 0, (void*)80 } +#define I_PAGENUMBERS (10) +/*10*/ { PD_TOGGLE, &printPageNumbers, "pageNumbers", PDO_DLGNOLABELALIGN, printPageNumberLabels, NULL, BC_HORZ | BC_NOBORDER }, +#define I_GRID (11) +/*11*/ { PD_TOGGLE, &printGrid, "grid", PDO_DLGNOLABELALIGN, printGridLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_RULER (12) +/*12*/ { PD_TOGGLE, &printRuler, "ruler", PDO_DLGNOLABELALIGN, printRulerLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_CENTERLINE (13) +/*13*/ { PD_TOGGLE, &printCenterLine, "centerLine", PDO_DLGNOLABELALIGN, printCenterLineLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_ROADBED (14) +/*14*/{ PD_TOGGLE, &printRoadbed, "roadbed", PDO_DLGNOLABELALIGN, printRoadbedLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_ROADBEDWIDTH (15) +/*15*/{ PD_FLOAT, &printRoadbedWidth, "roadbedWidth", PDO_DIM , &r0_, N_(" Width") }, +/*16*/ { PD_TOGGLE, &printPhysSize, "physsize", PDO_DLGNOLABELALIGN, printPhysSizeLabels, NULL, BC_HORZ | BC_NOBORDER, (void*)1 }, +/*17*/ { PD_BUTTON, (void*)DoPrintMargin, "margin", PDO_DLGHORZ|PDO_DLGBOXEND, NULL, N_("Margins") }, +/*18*/{ PD_FLOAT, &newPrintGrid.orig.x, "origx", PDO_DIM|PDO_DLGRESETMARGIN, &r_10_99999, N_("Origin: X"), 0, (void*)2 }, +/*19*/ { PD_FLOAT, &newPrintGrid.orig.y, "origy", PDO_DIM, &r_10_99999, N_("Y"), 0, (void*)2 }, +/*20*/ { PD_BUTTON, (void*)DoResetGrid, "reset", PDO_DLGHORZ, NULL, N_("Reset") }, +/*21*/ { PD_FLOAT, &newPrintGrid.angle, "origa", PDO_ANGLE|PDO_DLGBOXEND, &r0_360, N_("Angle"), 0, (void*)2 }, +/*22*/ { PD_BUTTON, (void*)DoPrintSetup, "setup", PDO_DLGCMDBUTTON, NULL, N_("Setup") }, +/*23*/ { PD_BUTTON, (void*)SelectAllPages, "selall", 0, NULL, N_("Select All") }, +/*24*/ { PD_BUTTON, (void*)PrintClear, "clear", 0, NULL, N_("Clear") }, +#define I_PAGECNT (25) +/*25*/ { PD_MESSAGE, N_("0 pages"), NULL, 0, (void*)80 }, +/*26*/ { PD_MESSAGE, N_("selected"), NULL, 0, (void*)80 } }; static paramGroup_t printPG = { "print", PGO_PREFMISCGROUP, printPLs, sizeof printPLs/sizeof printPLs[0] }; +static struct { + double top, right, bottom, left; +} printMargin = { 0.0, 0.0, 0.0, 0.0 }; /***************************************************************************** * @@ -157,6 +173,23 @@ static paramGroup_t printPG = { "print", PGO_PREFMISCGROUP, printPLs, sizeof pri * */ +/** + * Update the dialog with the current number of selected pages. + * + */ + +static void +UpdatePageCount() +{ + DynString msg; + DynStringMalloc(&msg, 0); + + DynStringPrintf(&msg, (pageCount == 1?_("%d page"):_("%d pages")), pageCount); + ParamLoadMessage(&printPG, I_PAGECNT, DynStringToCStr(&msg)); + ParamDialogOkActive(&printPG, pageCount != 0); + + DynStringFree(&msg); +} static void ChangeDim( void ) { @@ -208,9 +241,7 @@ static void ChangeDim( void ) 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 ); + UpdatePageCount(); } @@ -237,7 +268,7 @@ LOG1( log_print, ( "MarkPage( %d, %d )\n", x, y) ) 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 ); + DrawHilightPolygon( &tempD, p, 4 ); } @@ -256,10 +287,7 @@ static void SelectPage( coOrd pos ) 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 ); + UpdatePageCount(); } @@ -351,11 +379,11 @@ static void PrintGaudyBox( 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.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 ); + DrawString( &page_d, p00, 0.0, sProdName, fp, 22.0, wDrawColorBlack ); p00.y = 0.5; p01.y = 1.0; p00.x = p01.x = (157.0/72.0)+0.1; @@ -365,17 +393,17 @@ static void PrintGaudyBox( 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 ); + DrawString( &page_d, p00, 0.0, dat, fp, 14.0, wDrawColorBlack ); p00.y = 0.5+0.05; - DrawTextSize( &mainD, GetLayoutTitle(), fp, 16.0, FALSE, &textsize ); + DrawTextSize( &mainD, GetLayoutTitle(), fp, 14.0, FALSE, &textsize ); p00.x = (pageW/2.0)-(textsize.x/2.0); p00.y = 0.75+0.05; - DrawString( &page_d, p00, 0.0, GetLayoutTitle(), fp, 16.0, wDrawColorBlack ); - DrawTextSize( &mainD, GetLayoutSubtitle(), fp, 16.0, FALSE, &textsize ); + DrawString( &page_d, p00, 0.0, GetLayoutTitle(), fp, 14.0, wDrawColorBlack ); + DrawTextSize( &mainD, GetLayoutSubtitle(), fp, 14.0, FALSE, &textsize ); p00.x = (pageW/2.0)-(textsize.x/2.0); p00.y = 0.50+0.05; - DrawString( &page_d, p00, 0.0, GetLayoutSubtitle(), fp, 16.0, wDrawColorBlack ); + DrawString( &page_d, p00, 0.0, GetLayoutSubtitle(), fp, 12.0, wDrawColorBlack ); sprintf( dat, _("PrintScale 1:%ld Room %s x %s Model Scale %s File %s"), (long)printScale, @@ -383,7 +411,7 @@ static void PrintGaudyBox( FormatDistance( roomSize.y ), curScaleName, GetLayoutFilename() ); p00.x = 0.05; p00.y = 0.25+0.05; - DrawString( &page_d, p00, 0.0, dat, fp, 16.0, wDrawColorBlack ); + DrawString( &page_d, p00, 0.0, dat, fp, 14.0, wDrawColorBlack ); } @@ -414,12 +442,7 @@ static void PrintPlainBox( 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 ); - + fp = wStandardFont(F_HELV, FALSE, FALSE); sprintf( tmp, "[%0.2f,%0.2f]", corners[0].x, corners[0].y ); p00.x = 4.0/72.0; p00.y = 4.0/72.0; @@ -482,7 +505,6 @@ static void PrintUpdate( int inx0 ) { int inx; - DrawPrintGrid(); ParamLoadData( &printPG ); if (newPrintGrid.size.x > maxPageSize.x+0.01 || @@ -504,17 +526,17 @@ static void PrintUpdate( int inx0 ) 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 ); + wPrintGetPageSize( &x, &y ); + if (!printPhysSize) { + x -= (printMargin.left+printMargin.right); + y -= (printMargin.top+printMargin.bottom); + } maxPageSize.x = x; maxPageSize.y = y; realPageSize = maxPageSize; @@ -534,6 +556,22 @@ static void SetPageSize( BOOL_T doScale ) } } +/** + * Select all pages for printing. + * + */ + +static void SelectAllPages(void) +{ + for (int y = bm.y0; y < bm.y1; y++) { + for (int x = bm.x0; x < bm.x1; x++) { + BITMAP(bm, x, y) = TRUE; + } + } + pageCount = (bm.x1 - bm.x0) * (bm.y1 - bm.y0); + UpdatePageCount(); + TempRedraw(); // SelectAllPages +} static void PrintMaxPageSize( void ) /* @@ -542,13 +580,12 @@ static void PrintMaxPageSize( void ) * (depending on paper size, scale and orientation) */ { - DrawPrintGrid(); SetPageSize( TRUE ); currPrintGrid.size = maxPageSize; newPrintGrid = currPrintGrid; ParamLoadControls( &printPG ); ChangeDim(); - DrawPrintGrid(); + TempRedraw(); // PrintMaxSize wShow( printWin); } @@ -563,10 +600,152 @@ static void DoPrintScale( void ) PrintEnableControls(); } +/* + * Printer Margins + */ + +static void PrintMarginReset(); + +static paramFloatRange_t r0_1 = { 0.0, 1.0, 50 }; +static paramData_t printMarginPLs[] = { +#define I_PM_FIRST (0) + { PD_FLOAT, &printMargin.top, "marginT", PDO_DIM|PDO_NOPREF, &r0_1, NULL, 0, NULL }, + { PD_FLOAT, &printMargin.right, "marginR", PDO_DIM|PDO_NOPREF, &r0_1, NULL, 0, NULL }, + { PD_FLOAT, &printMargin.bottom, "marginB", PDO_DIM|PDO_NOPREF, &r0_1, NULL, 0, NULL }, + { PD_FLOAT, &printMargin.left, "marginL", PDO_DIM|PDO_NOPREF, &r0_1, NULL, 0, NULL }, +#define I_PM_COUNT (4) +#define I_PM_MESSAGE (4) + { PD_MESSAGE, NULL, NULL, 0, NULL }, +#define I_PM_RESET (5) + { PD_BUTTON, (void*) PrintMarginReset, "marginReset", PDO_DLGCMDBUTTON, NULL, N_("Reset") } }; +static paramGroup_t printMarginPG = { "printMargin", PGO_PREFMISCGROUP|PGO_NODEFAULTPROC, printMarginPLs, sizeof printMarginPLs/sizeof printMarginPLs[0] }; + +static wLines_t aPmLines[] = { + { 1, 25, 11, 94, 11 }, + { 1, 94, 11, 94, 111 }, + { 1, 94, 111, 25, 111 }, + { 1, 25, 111, 25, 11 }}; +static int pmxoff=14; +static int pmyoff=5; + +static void PrintMarginLayout( + paramData_t * pd, + int index, + wPos_t colX, + wPos_t * w, + wPos_t * h ) +{ + if ( index < I_PM_FIRST || index > (I_PM_MESSAGE) ) + return; + if ( index == I_PM_MESSAGE ) { + *h = wControlGetPosY( printMarginPLs[I_PM_FIRST+2].control ) + wControlGetHeight( printMarginPLs[I_PM_FIRST+2].control ); + return; + } + wPos_t x0, y0; + x0 = (aPmLines[index-I_PM_FIRST].x0+aPmLines[index-I_PM_FIRST].x1)/2; + y0 = (aPmLines[index-I_PM_FIRST].y0+aPmLines[index-I_PM_FIRST].y1)/2; + x0 -= pmxoff; + y0 -= pmyoff; +// y0 += wControlGetPosY( printMarginPLs[0].control ) + wControlGetHeight( printMarginPLs[0].control ); +// x0 -= wControlGetWidth( printMarginPLs[index-I_PM_FIRST].control )/2; +// y0 -= wControlGetHeight( printMarginPLs[index-I_PM_FIRST].control )/2; + *w = x0; + *h = y0; +} + + +static const char * sPrinterName = NULL; + +static BOOL_T SetMargins() +{ + double top, right, bottom, left; + wPrintGetMargins( &top, &right, &bottom, &left ); + sprintf( message, "%s-marginT", sPrinterName ); + wPrefGetFloat( "printer", message, &printMargin.top, top ); + sprintf( message, "%s-marginR", sPrinterName ); + wPrefGetFloat( "printer", message, &printMargin.right, right ); + sprintf( message, "%s-marginB", sPrinterName ); + wPrefGetFloat( "printer", message, &printMargin.bottom, bottom ); + sprintf( message, "%s-marginL", sPrinterName ); + wPrefGetFloat( "printer", message, &printMargin.left, left ); + ParamLoadControls( &printMarginPG ); + return + fabs( top - printMargin.top ) >= 0.001 || + fabs( right - printMargin.right ) >= 0.001 || + fabs( bottom - printMargin.bottom ) >= 0.001 || + fabs( left - printMargin.left ) >= 0.001; +} + + +static void DoPrintMarginOk( void * context ) +{ + wHide( printMarginWin ); + sprintf( message, "%s-marginT", sPrinterName ); + wPrefSetFloat( "printer", message, printMargin.top ); + sprintf( message, "%s-marginR", sPrinterName ); + wPrefSetFloat( "printer", message, printMargin.right ); + sprintf( message, "%s-marginB", sPrinterName ); + wPrefSetFloat( "printer", message, printMargin.bottom ); + sprintf( message, "%s-marginL", sPrinterName ); + wPrefSetFloat( "printer", message, printMargin.left ); + SetPageSize( TRUE ); + for ( int inx = 0; inx < sizeof printPLs/sizeof printPLs[0]; inx++ ) { + if ( printPLs[inx].context == (void*)2 ) + ParamLoadControl( &printPG, inx ); + } + DoPrintScale(); +} + +static void PrintMarginDlgUpdate( paramGroup_p pg, int index, void * context ) +{ + wControlActive( printMarginPLs[I_PM_RESET].control, TRUE ); +} + +static void PrintMarginReset() +{ + wPrintGetMargins( &printMargin.top, &printMargin.right, &printMargin.bottom, &printMargin.left ); + ParamLoadControls( &printMarginPG ); + wControlActive( printMarginPLs[I_PM_RESET].control, FALSE ); +} + +static void DoPrintMargin( void ) +{ + sPrinterName = wPrintGetName(); + while ( *sPrinterName == '\0' ) { + int rc = NoticeMessage( MSG_NO_PRINTER_SELECTED, _("Ok"), _("Cancel") ); + if ( rc <= 0 ) + return; + DoPrintSetup(); + } + if ( printMarginWin == NULL ) { + wPos_t x=10, y=10; + printMarginWin = ParamCreateDialog( &printMarginPG, MakeWindowTitle(_("Print Margins")), _("Ok"), DoPrintMarginOk, NULL, TRUE, PrintMarginLayout, F_BLOCK, PrintMarginDlgUpdate ); + if ( printMarginWin == NULL ) + return; + for ( int i=0; i<sizeof aPmLines / sizeof aPmLines[0]; i++ ) { + aPmLines[i].x0 += x; + aPmLines[i].x1 += x; + aPmLines[i].y0 += y; + aPmLines[i].y1 += y; + } + wLineCreate( printMarginWin, NULL, sizeof aPmLines / sizeof aPmLines[0], aPmLines ); + } + wMessageSetValue( (wMessage_p)printMarginPLs[I_PM_MESSAGE].control, sPrinterName ); + // Enable Reset button if we've changed anything + wControlActive( printMarginPLs[I_PM_RESET].control, SetMargins() ); + wShow( printMarginWin ); + +} + +/* + * Misc buttons + */ static void DoPrintSetup( void ) { wPrintSetup( (wPrintSetupCallBack_p)DoPrintScale ); + sPrinterName = wPrintGetName(); + SetPageSize( TRUE ); } @@ -582,11 +761,10 @@ static void PrintClear( void ) 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 ); + UpdatePageCount(); + TempRedraw(); // PrintClear } @@ -604,7 +782,6 @@ static void PrintSnapShot( void ) POS_T t; PrintClear(); - DrawPrintGrid(); SetPageSize( FALSE ); pageSize = realPageSize; if (pageSize.x > pageSize.y) { @@ -665,11 +842,10 @@ static void PrintSnapShot( void ) ChangeDim(); pageCount = 1; BITMAP(bm,0,0) = TRUE; - DrawPrintGrid(); - ParamLoadMessage( &printPG, I_PAGECNT, _("1 page") ); - ParamDialogOkActive( &printPG, TRUE ); + UpdatePageCount(); PrintEnableControls(); wShow( printWin ); + TempRedraw(); // PrintSnapShot } @@ -729,20 +905,167 @@ static void DrawRegistrationMarks( drawCmd_p d ) } } +/** + * Format the page coordinates. Also handle cases where the coordinates are + * out of range. + * + * \param x x position + * \param y y position + * \return TRUE + */ + +static char * +FormatPageNumber(int x, int y) +{ + DynString formatted; + char *result; + + DynStringMalloc(&formatted, 16); + if (x > 0 && x <= bm.x1 && y > 0 && y <= bm.y1) { + DynStringPrintf(&formatted, "(%d/%d)", x, y); + } else { + DynStringCatCStr(&formatted, "(-/-)"); + } + + result = strdup(DynStringToCStr(&formatted)); + DynStringFree(&formatted); + + return (result); +} + +/** + * Print the page number in the center of the page + * + * \param x page index x-direction + * \param y page index y-direction + * \param width page width + * \param height page height + * \return TRUE + */ + +static bool +PrintPageNumber(wPos_t x, wPos_t y, DIST_T width, DIST_T height) +{ + coOrd printPosition; + coOrd textSize; + + char *positionText; + wFont_p fp = wStandardFont(F_HELV, TRUE, FALSE); + wFontSize_t fs = 64.0; + + positionText = FormatPageNumber(x + 1, y + 1); + + // even though we're printing into page_d, mainD must be used here + DrawTextSize(&mainD, positionText, fp, fs, TRUE, &textSize); + + if (printFormat == PORTRAIT) { + printPosition.x = (width - textSize.x) / 2; + printPosition.y = (height - textSize.y) / 2; + } else { + printPosition.x = (height - textSize.x) / 2; + printPosition.y = (width - textSize.y) / 2; + } + + page_d.funcs->options |= wDrawOutlineFont; + DrawString(&page_d, printPosition, 0.0, positionText, fp, fs, + wDrawColorGray(70)); + page_d.funcs->options &= ~wDrawOutlineFont; + + free(positionText); + + return (TRUE); +} + +/** + * Print the page number of an adjoining page at a specified position + * + * \param x page index x-direction + * \param y page index y-direction + * \param position page position + */ + +void +PrintNextPageNumberAt(int x, int y, coOrd position) +{ + char *pageNumber; + wFont_p fp = wStandardFont(F_HELV, FALSE, FALSE); + wFontSize_t fs = 8.0; + + pageNumber = FormatPageNumber(x, y); + DrawString(&page_d, position, 0.0, pageNumber, fp, fs, wDrawColorBlack); + free(pageNumber); +} + +/** + * Print the page numbers of all four adjoining pages (left, right, above, below) + * + * \param x page index of current page x + * \param y page index of current page y + * \param pageW width of page + * \param pageH height of page + * + * \return TRUE + */ + +static bool +PrintNextPageNumbers(int x, int y, DIST_T pageW, DIST_T pageH) +{ + coOrd p00; + + // above + if (printFormat == PORTRAIT) { + p00.x = pageW / 2.0 - 20.0 / 72.0; + p00.y = pageH - 10.0 / 72.0; + } else { + p00.x = pageH / 2.0 - 20.0 / 72.0; + p00.y = pageW - 10.0 / 72.0; + } + PrintNextPageNumberAt(x + 1, y + 2, p00); + + // below + if (printFormat == PORTRAIT) { + p00.y = 10.0 / 72.0; + } else { + p00.y = 10.0 / 72.0; + } + PrintNextPageNumberAt(x + 1, y, p00); + + // right + if (printFormat == PORTRAIT) { + p00.y = pageH / 2 + 10.0 / 72.0; + p00.x = pageW - 20.0 / 72.0; + } else { + p00.y = pageW / 2 + 10.0 / 72.0; + p00.x = pageH - 20.0 / 72.0; + } + PrintNextPageNumberAt(x + 2, y + 1, p00); + + // left + if (printFormat == PORTRAIT) { + p00.x = 10.0 / 72.0; + } else { + p00.x = 10.0 / 72.0; + } + PrintNextPageNumberAt(x, y + 1, p00); + return (TRUE); +} static BOOL_T PrintPage( int x, int y ) { - coOrd orig, p[4], minP, maxP; + coOrd orig, p[4], psave[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; + if (printPhysSize) { + orig.x += printMargin.left; + orig.y += printMargin.bottom; + } 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; @@ -774,6 +1097,9 @@ static BOOL_T PrintPage( Translate( &print_d.orig, orig, currPrintGrid.angle+180.0, printScale ); print_d.size.y += printScale; } + for (int i=0;i<4;i++) { + psave[i] = p[i]; + } if (printRotate) { rotateCW = (printFormat != PORTRAIT); if (rotateCW) { @@ -798,7 +1124,7 @@ static BOOL_T PrintPage( page_d.size.x = print_d.size.x/printScale; page_d.size.y = print_d.size.y/printScale; } - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, wCursorWait ); print_d.scale = printScale; if (print_d.d == NULL) AbortProg( "wPrintPageStart" ); @@ -814,8 +1140,7 @@ static BOOL_T PrintPage( 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) ); @@ -827,7 +1152,7 @@ static BOOL_T PrintPage( 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 ); @@ -885,6 +1210,7 @@ static BOOL_T PrintPage( 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; @@ -892,10 +1218,15 @@ static BOOL_T PrintPage( DrawTracks( &print_d, print_d.scale, minP, maxP ); if (printRegistrationMarks && printScale == 1) DrawRegistrationMarks( &print_d ); + if (printRegistrationMarks) + PrintPlainBox( x, y, psave ); + + if (printPageNumbers) { + PrintPageNumber(x, y, page_d.size.x, page_d.size.y); + PrintNextPageNumbers(x, y, page_d.size.x, page_d.size.y); + } if ( !wPrintPageEnd( print_d.d ) ) return FALSE; - /*BITMAP(bm,x,y) = 0;*/ - MarkPage( x, y ); } return TRUE; } @@ -920,9 +1251,9 @@ static void DoPrintPrint( void * junk ) wPrefGetInteger( "print", "nodecoration", &noDecoration, 0 ); print_d.CoOrd2Pix = page_d.CoOrd2Pix = mainD.CoOrd2Pix; - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, wCursorWait ); if (!wPrintDocStart(GetLayoutTitle(), pageCount, &copies )) { - wSetCursor( wCursorNormal ); + wSetCursor( mainD.d, defaultCursor ); return; } if (copies <= 0) @@ -940,42 +1271,38 @@ static void DoPrintPrint( void * junk ) 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 + if (copy >= copies) BITMAP(bm,x,y) = 0; } } quitPrinting: wPrintDocEnd(); - wSetCursor( wCursorNormal ); + wSetCursor( mainD.d, defaultCursor ); Reset(); /* undraws grid, resets pagecount, etc */ } static void DoResetGrid( void ) { - DrawPrintGrid(); currPrintGrid.orig = zero; currPrintGrid.angle = 0.0; ChangeDim(); newPrintGrid = currPrintGrid; ParamLoadControls( &printPG ); - DrawPrintGrid(); + TempRedraw(); // DoResetGrid } static void PrintGridRotate( void * pangle ) { ANGLE_T angle = (ANGLE_T)(long)pangle; - DrawPrintGrid(); currPrintGrid.orig = cmdMenuPos; - currPrintGrid.angle += angle; + currPrintGrid.angle += angle/1000; newPrintGrid = currPrintGrid; ParamLoadControls( &printPG ); ChangeDim(); - DrawPrintGrid(); + TempRedraw(); // PrintGridRotate } /***************************************************************************** @@ -1008,6 +1335,7 @@ static void PrintDlgUpdate( else if ( pg->paramPtr[inx].context == (void*)2 ) PrintUpdate( inx ); ParamControlActive( &printPG, I_RULER, currPrintGrid.angle == 0 ); + TempRedraw(); // PrintDlgUpdate } static STATUS_T CmdPrint( @@ -1038,6 +1366,8 @@ static STATUS_T CmdPrint( print_d.scale = printScale; printWin = ParamCreateDialog( &printPG, MakeWindowTitle(_("Print")), _("Print"), DoPrintPrint, (paramActionCancelProc)Reset, TRUE, NULL, 0, PrintDlgUpdate ); } + sPrinterName = wPrintGetName(); + SetMargins(); wShow( printWin ); SetPageSize( TRUE ); if (currPrintGrid.size.x == 0.0) { @@ -1050,17 +1380,15 @@ static STATUS_T CmdPrint( currPrintGrid.size.y = maxPageSize.y; newPrintGrid = currPrintGrid; ParamLoadControls( &printPG ); - DrawPrintGrid(); pageCount = 0; + UpdatePageCount(); 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 ); + TempRedraw(); // CmdPrint C_START return C_CONTINUE; case C_DOWN: @@ -1083,10 +1411,8 @@ LOG( log_print, 2, ( "Page size = %0.3f %0.3f\n", currPrintGrid.size.x, currPrin if (downShift) { rc = GridAction( action, pos, &newPrintGrid.orig, &newPrintGrid.angle ); ParamLoadControls( &printPG ); - DrawPrintGrid(); currPrintGrid = newPrintGrid; ChangeDim(); - DrawPrintGrid(); downShift = FALSE; } return C_CONTINUE; @@ -1115,10 +1441,8 @@ LOG( log_print, 2, ( "Page size = %0.3f %0.3f\n", currPrintGrid.size.x, currPrin 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 ); } @@ -1126,13 +1450,13 @@ LOG( log_print, 2, ( "Page size = %0.3f %0.3f\n", currPrintGrid.size.x, currPrin case C_REDRAW: DrawPrintGrid(); + rc = GridAction( action, pos, &newPrintGrid.orig, &newPrintGrid.angle ); return C_TERMINATE; case C_CANCEL: if (printWin == NULL) return C_TERMINATE; PrintClear(); - DrawPrintGrid(); wHide( printWin ); return C_TERMINATE; @@ -1141,6 +1465,7 @@ LOG( log_print, 2, ( "Page size = %0.3f %0.3f\n", currPrintGrid.size.x, currPrin return C_TERMINATE; case C_CMDMENU: + menuPos = pos; wMenuPopupShow( printGridPopupM ); return C_CONTINUE; @@ -1157,7 +1482,8 @@ EXPORT wIndex_t InitCmdPrint( wMenu_p menu ) 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 ); + ParamRegister( &printMarginPG ); + return InitCommand( menu, CmdPrint, N_("Print..."), NULL, LEVEL0, IC_LCLICK|IC_POPUP3|IC_CMDMENU, ACCL_PRINT ); } /***************************************************************************** diff --git a/app/bin/cprofile.c b/app/bin/cprofile.c index 49c3289..4f375ed 100644 --- a/app/bin/cprofile.c +++ b/app/bin/cprofile.c @@ -21,6 +21,7 @@ */ #include <math.h> +#include <stdbool.h> #include "custom.h" #include "cselect.h" @@ -105,19 +106,21 @@ static BOOL_T printVert = TRUE; static wMenu_p profilePopupM; static track_p profilePopupTrk; static EPINX_T profilePopupEp; -static wMenuToggle_p profilePopupToggles[3]; +static wMenuToggle_p profilePopupToggles[3]; -static int log_profile = 0; +static int log_profile = 0; #define LABELH (labelH*fontSize/screenProfileFontSize) +#define LABELW (labelW*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) +#define PBR(FS) (1.0*(labelW*(FS)/screenProfileFontSize+3.0/mainD.dpi)) +#define PBL(FS) (1.0*(labelW*(FS)/screenProfileFontSize+3.0/mainD.dpi)) static FLOAT_T labelH; +static FLOAT_T labelW; -track_p pathStartTrk; +track_p pathStartTrk; EPINX_T pathStartEp; track_p pathEndTrk; EPINX_T pathEndEp; @@ -126,330 +129,467 @@ EPINX_T pathEndEp; #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 ) + 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; +static profElem_p copyOfprofElem; + +#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; + 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 ) + DIST_T totalD, minE; + int minC, maxC, incrC; + DIST_T scaleX, scaleY; +} prof; + + +/** + * Creates a copy of profile elements + */ + +static void +CreateCopyProfileElements() { - 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 ); - } + if (copyOfprofElem) { + MyFree(copyOfprofElem); + } + + copyOfprofElem = MyMalloc(profElem_da.cnt * sizeof(profElem_t)); + if (!copyOfprofElem) { + AbortProg("Couldn't allocate memory for profile copy\n"); + } + for (int i = 0; i < profElem_da.cnt; i++) { + copyOfprofElem[i] = profElem(i); + } +} - 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 ); - } +/** + * Destroys the copy of profile elements + */ - 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 ); +static void +DestroyCopyOfProfileElements() +{ + if (copyOfprofElem) { + MyFree(copyOfprofElem); + copyOfprofElem = NULL; + } +} + + +/** + * Draw profile + * + * \param D The drawCmd_p to use. + * \param fontSize Size of the font. + * \param printVert print vertical. + */ + +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*1.0/mainD.dpi); + fp = wStandardFont(F_HELV, FALSE, FALSE); + DYNARR_RESET(pts_t, 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; + + // Draw horizontal grid and y scale + for (inx=prof.minC; inx<=prof.maxC; inx+=prof.incrC) { + coOrd textsize; + // grid line + pt.y = pb.y = GetDim(inx); + DrawLine(D, pb, pt, 0, snapGridColor); + // scale + sprintf(message, "%d", inx); + DrawTextSize(&mainD, message, wStandardFont(F_HELV, FALSE, FALSE), + screenProfileFontSize, FALSE, &textsize); + pl.x = ((-3.0/mainD.dpi) - textsize.y*0.5 - textsize.x) / prof.scaleX*D->scale; + pl.y = pb.y-LABELH/2/prof.scaleY*D->scale; + + DrawString(D, pl, 0.0, message, fp, fontSize*D->scale, borderColor); + } + + // show the measurement units + sprintf(message, "%s", units == UNITS_ENGLISH ? "in." : "cm"); + DrawTextSize(&mainD, message, wStandardFont(F_HELV, FALSE, FALSE), + screenProfileFontSize, FALSE, &textsize); + pl.x = ((-3.0 / mainD.dpi) - textsize.y*0.5 - textsize.x) / + prof.scaleX*D->scale; + pl.y += LABELH * 1.5 / prof.scaleY*D->scale; + 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(pts_t, 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(pts_t, points_da, 10); + pb.x = 0; + points(points_da.cnt-1) = pb; + DrawPoly(D, points_da.cnt, points_da.ptr, NULL, profileColorFill, 1, 1, 0); + } + + 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(screenProfileFontSize)) { + pt.x = -(PBR(screenProfileFontSize)-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; + + // mark the starting point for the profile + pt = points(0); + DrawLine(D, pb, pt, lw, snapGridColor); + DrawArc(D, pt, 0.05, 0, 360, TRUE, 2, wDrawColorGrey40); + if (units==UNITS_ENGLISH) { + sprintf(message, "%0.1f", PutDim(profElem(0).elev)+0.05); + } else { + sprintf(message, "%0.1f", PutDim(profElem(0).elev)+0.05); + } + 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 + GetDim(prof.incrC) / 16;; + DrawString(D, pl, 270.0, message, fp, fontSize*D->scale, borderColor); + } else { + pl.x = pt.x+2.0/mainD.dpi/prof.scaleX*D->scale + GetDim(prof.incrC) / 16;; + 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); + + // draw line to x-axis for intermediate elevation points + if (inx != profElem_da.cnt - 1) { + unsigned long oldOptions = D->options; + + D->options = D->options | DC_DOT; + DrawLine(D, pb, pt, lw, borderColor); + D->options = oldOptions; } - pl = pt; - } + + // draw grade line + DrawLine(D, pl, pt, lw*2, (profElem(inx).defined ? profileColorDefinedProfile : + profileColorUndefinedProfile)); + // draw the markers + DrawArc(D, pt, 0.05, 0, 360, TRUE, 2, wDrawColorGrey40); + + 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%%", round(grade*1000.0)/10.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, "%0.1f'", (round((profElem(inx).dist/12.0)*10.0)/10.0)); + } else { + sprintf(message, "%d'%0.1f\"", (int)floor((profElem(inx).dist)/12.0), + round(fmod(profElem(inx).dist,12.0)*10.0)/10.0); + } + } else { + if (PutDim(prof.totalD) > 10000) { + sprintf(message, "%0.1fm", (round(PutDim(profElem(inx).dist)/10.0)/10.0)); + } else if (PutDim(prof.totalD) > 100) { + sprintf(message, "%0.2fm", (round(PutDim(profElem(inx).dist))/100.0)); + } else { + sprintf(message, "%0.1fcm", round(PutDim(profElem(inx).dist)+0.5)); + } + } + 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", round(PutDim(profElem(inx).elev)*100.0)/100.0); + 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+GetDim(prof.incrC) / 16; + DrawString(D, pl, 270.0, message, fp, fontSize*D->scale, borderColor); + } else { + pl.x = pt.x + 2.0/mainD.dpi/prof.scaleX*D->scale + GetDim(prof.incrC) / 16; + 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 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 }; + 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 ) + 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; + 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 ) + 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; - } + 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; + } } +/** + * Redraw profile window + */ -static void RedrawProfileW( void ) +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)); + wPos_t ww, hh; + coOrd size; + int divC; + DIST_T maxE, rngE; + profElem_t *p; + wFont_p fp; + POS_T w; + coOrd textsize; + char *pTestString; + + wDrawDelayUpdate(screenProfileD.d, TRUE); + wDrawClear(screenProfileD.d); + + // get the size of the window area in pixels and convert to inches + wDrawGetSize(screenProfileD.d, &ww, &hh); + screenProfileD.size.x = (ww)/screenProfileD.dpi; + screenProfileD.size.y = (hh)/screenProfileD.dpi; + + // calculate positions for labels??? + fp = wStandardFont(F_HELV, FALSE, FALSE); + screenProfileD.orig.x = -PBL(screenProfileFontSize); + screenProfileD.orig.y = -PBB(screenProfileFontSize); + + /* Calculate usable dimension of canvas in inches */ + size = screenProfileD.size; + size.x -= (PBL(screenProfileFontSize)); + size.y -= (PBB(screenProfileFontSize)); + + /* make sure there is enough space to show the rightmost coordinate value*/ + if (units == UNITS_ENGLISH) { + if (prof.totalD > 240.0) { + pTestString = "9999'"; + } else { + pTestString = "999'11\""; + } + } else { + if (PutDim(prof.totalD) > 10000.0) { + pTestString = "999m"; + } else { + if (PutDim(prof.totalD) > 100.0) { + pTestString = "99.9m"; + } else { + pTestString = "9.99m"; + } + } + } + DrawTextSize(&mainD, pTestString, fp, screenProfileFontSize, FALSE, &textsize); + size.x -= textsize.x / 2; + size.y -= textsize.y * 1.5 ; + + + // now we have the size of the profile area #ifdef WINDOWS - if (printVert) { - size.x -= PBR/4.0; - size.y -= PBT; - } else + if (printVert) { + size.x -= PBR(screenProfileFontSize)/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, + { + size.x -= PBR(screenProfileFontSize); + size.y -= PBT; + } + + if (size.x < 0.1 || size.y < 0.1) { + wDrawDelayUpdate(screenProfileD.d, FALSE); + 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 (int 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); + + 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; + + DrawProfile(&screenProfileD, screenProfileFontSize, #ifdef WINDOWS - printVert + printVert #else - FALSE + FALSE #endif - ); + ); + wDrawDelayUpdate(screenProfileD.d, FALSE); } + static drawCmd_t printProfileD = { - NULL, - &printDrawFuncs, - DC_PRINT|DC_NOCLIP, - 1.0, - 0.0, - {0.0,0.0}, {1.0,1.0}, - ProfilePix2CoOrd, ProfileCoOrd2Pix }; + NULL, + &printDrawFuncs, + DC_PRINT | DC_NOCLIP, + 1.0, + 0.0, + {0.0,0.0}, {1.0,1.0}, + ProfilePix2CoOrd, ProfileCoOrd2Pix +}; /** * This is the print function for the track height profile. The paper @@ -457,94 +597,95 @@ static drawCmd_t printProfileD = { * Eg. is the windows is wider than high, the printout will be in * landscape. * \todo Rework the layout of the printout - * This function is (at least for me) hard to comprehend with all the - * fiddling around with the ccordinates. Also the filled area is a - * waste of toner or ink. * * \param junk IN * \return */ -static void DoProfilePrint( void * junk ) +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, GetLayoutTitle() ); - 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; - } + 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(printProfileFontSize); + 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, GetLayoutTitle()); + 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(printProfileFontSize)+(printVert?PBR( + printProfileFontSize)/4.0:PBR(printProfileFontSize)); + 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(); + 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(printProfileFontSize))+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( + printProfileFontSize)/4.0:PBR(printProfileFontSize))); + 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 @@ -553,201 +694,218 @@ static void DoProfilePrint( void * junk ) 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 void DoProfileChange(void *junk); +static void DoProfileReset(void *junk); +static void DoProfileDone(void *); +static void DoProfileClear(void *); +static void DoProfilePrint(void *); +static void DoProfileChangeMode(void *); +static void SelProfileW(wIndex_t, coOrd); +static void CloseProfileWindow(paramGroup_p pg, int event, void *data); static paramDrawData_t profileDrawData = { 300, 150, (wDrawRedrawCallBack_p)RedrawProfileW, SelProfileW, &screenProfileD }; static paramData_t profilePLs[] = { - { PD_DRAW, NULL, "canvas", PDO_DLGRESIZE, &profileDrawData }, + { 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") } }; + { PD_MESSAGE, NULL, NULL, PDO_DLGIGNOREX, (void*)300 }, +#define I_CHANGEBUTTON 2 + { PD_BUTTON, (void*)DoProfileChange, "change", PDO_DLGCMDBUTTON, NULL, N_("Change") }, +#define I_RESETBUTTON 3 + { PD_BUTTON, (void*)DoProfileReset, "reset", PDO_DLGCMDBUTTON, NULL, N_("Reset") }, +#define I_CLEARBUTTON 4 + { PD_BUTTON, (void*)DoProfileClear, "clear", PDO_DLGCMDBUTTON, NULL, N_("Clear") }, +#define I_PRINTBUTTON 5 + { PD_BUTTON, (void*)DoProfilePrint, "print", 0, NULL, N_("Print") } +}; static paramGroup_t profilePG = { "profile", 0, profilePLs, sizeof profilePLs/sizeof profilePLs[0] }; +#define CHANGEBUTTON ((wButton_p)profilePLs[I_CHANGEBUTTON].control) +#define RESETBUTTON ((wButton_p)profilePLs[I_RESETBUTTON].control) +#define CLEARBUTTON ((wButton_p)profilePLs[I_CLEARBUTTON].control) +#define PRINTBUTTON ((wButton_p)profilePLs[I_PRINTBUTTON].control) -static void ProfileTempDraw( int inx, DIST_T elev ) +static void SelProfileW( + wIndex_t action, + coOrd pos) { - 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; + 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; + + 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"), round(PutDim(elev)*10.0)/10.0); + ParamLoadMessage(&profilePG, I_PROFILEMSG, message); + oldElev = elev; + RedrawProfileW(); + break; + case C_MOVE: + if (inx < 0) { + break; + } + if (profElem_da.cnt == 1) { + sprintf(message, _("Elev = %0.1f"), round(PutDim(elev)*10.0)/10.0); + } else if (inx == 0) { + sprintf(message, _("Elev=%0.2f %0.1f%%"), + round(PutDim(elev)*100.0)/100.0, + round(fabs(((profElem(inx+1).elev-elev) / (profElem(inx+1).dist-profElem( + inx).dist)) * 1000.0))/10.0); + } else if (inx == profElem_da.cnt-1) { + sprintf(message, _("%0.1f%% Elev = %0.2f"), + round(fabs(((profElem(inx-1).elev-elev) / (profElem(inx).dist-profElem( + inx-1).dist)) * 1000.0))/10.0, + round(PutDim(elev)*100.0)/100.0); + } else { + sprintf(message, _("%0.1f%% Elev = %0.2f %0.1f%%"), + round(fabs(((profElem(inx-1).elev-elev) / (profElem(inx).dist-profElem( + inx-1).dist)) * 1000.0))/10.0, + round(PutDim(elev)*100.0)/100.0, + round(fabs((profElem(inx+1).elev-elev) / (profElem(inx+1).dist-profElem( + inx).dist)) * 1000.0)/10.0); + } + ParamLoadMessage(&profilePG, I_PROFILEMSG, message); + oldElev = elev; + profElem(inx).elev = oldElev; + RedrawProfileW(); + wPause(500l); + 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; + } } - -static void SelProfileW( - wIndex_t action, - coOrd pos ) +static void HilightProfileElevations(BOOL_T show) { - DIST_T dist; - static DIST_T oldElev; - static int inx; - DIST_T elev; - - if (profElem_da.cnt <= 0) - return; + /*if ( profElem_da.cnt <= 0 ) {*/ + HilightElevations(show); + /*} else { + }*/ +} - dist = pos.x; - elev = pos.y; +/** + * + * + * \param pg The page. + * \param event The event. + * \param [in,out] data If non-null, the data. + */ +void +CloseProfileWindow(paramGroup_p pg, int event, void *data) +{ + Reset(); + return; +} -#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; +/** + * Undo the changes made in the profile window to the layout. + */ + +static void +ResetChanges() +{ + if (copyOfprofElem) { + for (int i = 0; i < profElem_da.cnt; i++) { + profElem(i) = copyOfprofElem[i]; + if (profElem(i).trk) { + UpdateTrkEndElev(profElem(i).trk, profElem(i).ep, ELEV_DEF | ELEV_VISIBLE, + copyOfprofElem[i].elev, NULL); } } - 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; } } +/** + * Executes the profile reset operation. All elevations are copied from the + * backup, the main drawing area and the profile window are updated + * + * \param [in,out] junk + */ -#ifdef LATER -static BOOL_T ProfilePlayback( char * line ) +static void +DoProfileReset(void *junk) { - 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 ); + if (profileUndo == 0) { + profileUndo = TRUE; + UndoStart(_("Profile Command"), "Profile"); } - return TRUE; + ResetChanges(); + RedrawProfileW(); + TempRedraw(); } -#endif - +/** + * Confirm the changes made in the profile window + * + * \param [in,out] junk If non-null, the junk. + */ -static void HilightProfileElevations( BOOL_T show ) +static void +DoProfileChange(void *junk) { - /*if ( profElem_da.cnt <= 0 ) {*/ - HilightElevations( show ); - /*} else { - }*/ + DestroyCopyOfProfileElements(); + TempRedraw(); } -static void DoProfileDone( void * junk ) +static void DoProfileDone(void * junk) { -#ifdef LATER - HilightProfileElevations( FALSE ); - wHide( profileW ); - ClrAllTrkBits( TB_PROFILEPATH ); - MainRedraw(); - MapRedraw(); -#endif - Reset(); + Reset(); } -static void DoProfileClear( void * junk ) +static void DoProfileClear(void * junk) { - profElem_da.cnt = 0; - station_da.cnt = 0; - if (ClrAllTrkBits( TB_PROFILEPATH )) { - MainRedraw(); - MapRedraw(); - } - pathStartTrk = pathEndTrk = NULL; - RedrawProfileW(); + ResetChanges(); + profElem_da.cnt = 0; + station_da.cnt = 0; + ClrAllTrkBitsRedraw(TB_PROFILEPATH, TRUE); + pathStartTrk = pathEndTrk = NULL; + RedrawProfileW(); } -static void DoProfileChangeMode( void * junk ) +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") ); - } + if (profElem_da.cnt<=0) { + InfoMessage(_("Select a Defined Elevation to start Profile")); + } else { + InfoMessage(_("Select a Defined Elevation to extend Profile")); + } } /************************************************************************** @@ -756,17 +914,17 @@ static void DoProfileChangeMode( void * junk ) * **************************************************************************/ -static BOOL_T PathListEmpty( void ) +static BOOL_T PathListEmpty(void) { - return pathStartTrk == NULL; + return pathStartTrk == NULL; } -static BOOL_T PathListSingle( void ) +static BOOL_T PathListSingle(void) { - return pathStartTrk != NULL && - ( pathEndTrk == NULL || - ( GetTrkEndTrk(pathEndTrk,pathEndEp) == pathStartTrk && - GetTrkEndTrk(pathStartTrk,pathStartEp) == pathEndTrk ) ); + return pathStartTrk != NULL && + (pathEndTrk == NULL || + (GetTrkEndTrk(pathEndTrk,pathEndEp) == pathStartTrk && + GetTrkEndTrk(pathStartTrk,pathStartEp) == pathEndTrk)); } @@ -774,102 +932,106 @@ 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 ) + 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; + int rc0=0; + int pathMatch; + + switch (cmd) { + track_p trkN; + 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 { + EPINX_T epN; + 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 ) + 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 ); + 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); } @@ -884,502 +1046,494 @@ LOG( log_profile, 4, ( "Searching from T%d:%d to T%d:%d or T%d:%d\n", #define ONPATH_END (1<<1) #define ONPATH_MID (1<<2) #define ONPATH_BRANCH (1<<3) -static int OnPath( track_p trk, EPINX_T ep ) +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; + if (GetTrkBits(trk)&TB_PROFILEPATH) { + track_p trk0; + trk0 = GetTrkEndTrk(profilePopupTrk, profilePopupEp); + if (trk0 && (GetTrkBits(trk0)&TB_PROFILEPATH)) { + return ONPATH_MID; + } + if (trk == pathStartTrk && ep == pathStartEp) { + return ONPATH_END; + } + return ONPATH_BRANCH; + } + return ONPATH_NOT; } -static BOOL_T PathListCheck( void ) +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; + 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 ) + 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; - } - } + track_p trk = *Rtrk, trkN; + EPINX_T ep = *Rep; + + PASSERT("removeTracksFromPath", trk, NOP); + PASSERT("removeTracksFromPath", !PathListSingle(), NOP); + while (1) { + EPINX_T ep2; + 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 ) +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; - } + profElem_p p; + station_p s; + EPINX_T epDefElev = -1; + 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 (EPINX_T 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, TRUE); + } + p->defined = *defined; + *defined = TRUE; + } else if (undefined) { + *defined = FALSE; + } } -static void ComputeProfElem( void ) +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; - } + 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 ) +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( "}" ); + track_p trk, trkN; + EPINX_T ep; + 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) { + EPINX_T ep2; + 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 ) +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(); + 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"); + } + } + + trkP = GetTrkEndTrk(trkN, epN); + if (trkP) { + epP = GetEndPtConnectedToMe(trkP, trkN); + } + + if (!PathListCheck()) { + return; + } + + 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); + 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); + return; + } + + DestroyCopyOfProfileElements(); + ComputeProfElem(); + CreateCopyProfileElements(); + + RedrawProfileW(); + DoProfileChangeMode(NULL); + if (log_profile>=1) { + lprintf(" = "); + DumpProfElems(); + lprintf("\n"); + } + PathListCheck(); } -static void ProfileSubCommand( wBool_t set, void* pcmd ) +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(); + 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); + + elev = 0.0; + switch (cmd) { + case 0: + /* define */ + ComputeElev(profilePopupTrk, profilePopupEp, TRUE, &elev, NULL, TRUE); + 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); + ComputeProfElem(); + RedrawProfileW(); + TempRedraw(); // ProfileSubCommand } -static STATUS_T CmdProfile( wAction_t action, coOrd pos ) +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(); - MapRedraw(); - } - 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(); - MapRedraw(); - } - return C_TERMINATE; - case C_REDRAW: - if ( wWinIsVisible(profileW) ) { - HilightProfileElevations( wWinIsVisible(profileW) ); - /*RedrawProfileW();*/ - } - return C_CONTINUE; - } - return C_CONTINUE; + track_p trk0; + coOrd textsize; + + switch (action) { + case C_START: + if (profileW == NULL) { + profileColorDefinedProfile = drawColorBlue; + profileColorUndefinedProfile = drawColorRed; + profileColorFill = drawColorGrey80; + DrawTextSize(&mainD, "999.9", wStandardFont(F_HELV, FALSE, FALSE), + screenProfileFontSize, FALSE, &textsize); + labelH = textsize.y; + labelW = textsize.x; + profileW = ParamCreateDialog(&profilePG, MakeWindowTitle(_("Profile")), NULL, + NULL, wHide, TRUE, NULL, F_RESIZE, CloseProfileWindow); + } + ParamLoadControls(&profilePG); + ParamGroupRecord(&profilePG); + wShow(profileW); + ParamLoadMessage(&profilePG, I_PROFILEMSG, _("Drag to change Elevation")); + profElem_da.cnt = 0; + station_da.cnt = 0; + RedrawProfileW(); + ClrAllTrkBitsRedraw(TB_PROFILEPATH, TRUE); + pathStartTrk = NULL; + SetAllTrackSelect(FALSE); + profileUndo = FALSE; + InfoMessage(_("Select a Defined Elevation to start profile")); + TempRedraw(); // CmdProfile C_START + return C_CONTINUE; + case C_LCLICK: + InfoMessage(""); + if ((trk0 = OnTrack(&pos, TRUE, TRUE)) != NULL) { + EPINX_T ep0; + 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); + menuPos = pos; + wMenuPopupShow(profilePopupM); + } + } + } + return C_CONTINUE; + case C_OK: + DoProfileDone(NULL); + return C_TERMINATE; + case C_CANCEL: + wHide(profileW); + ClrAllTrkBitsRedraw(TB_PROFILEPATH, TRUE); + return C_TERMINATE; + case C_REDRAW: + if (wWinIsVisible(profileW)) { + HilightProfileElevations(wWinIsVisible(profileW)); + } + return C_CONTINUE; + } + return C_CONTINUE; } -static void ProfileChange( long changes ) +static void ProfileChange(long changes) { - if ( (changes & CHANGE_UNITS) && screenProfileD.d ) - RedrawProfileW(); + if ((changes & CHANGE_UNITS) && screenProfileD.d) { + RedrawProfileW(); + } } - #include "bitmaps/profile.xpm" -EXPORT void InitCmdProfile( wMenu_p menu ) +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 ); + log_profile = LogFindIndex("profile"); + ParamRegister(&profilePG); + + AddMenuButton(menu, CmdProfile, "cmdProfile", _("Profile"), + wIconCreatePixMap(profile_xpm), LEVEL0_50, IC_LCLICK|IC_CMDMENU|IC_POPUP3, + 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 index d7f7c80..7f27864 100644 --- a/app/bin/cpull.c +++ b/app/bin/cpull.c @@ -60,6 +60,9 @@ static dynArr_t section_da; #define section(N) DYNARR_N( section_t, section_da, N ) static double contribL, contribR; +static dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) + typedef enum { freeEnd, connectedEnd, loopEnd } ending_e; @@ -452,12 +455,15 @@ static void PullTracks( int cnt1, cnt2; int rc; - if (QueryTrack(trk1,Q_CAN_ADD_ENDPOINTS) || QueryTrack(trk2,Q_CAN_ADD_ENDPOINTS)) { + if (QueryTrack(trk1,Q_CAN_ADD_ENDPOINTS)) { ConnectTurntableTracks(trk1, ep1, trk2, ep2 ); return; + } else if (QueryTrack(trk2,Q_CAN_ADD_ENDPOINTS)) { + ConnectTurntableTracks(trk2, ep2, trk1, ep1 ); + return; } - if (ep1<0 || ep1<0 ) return; + if (ep1<0 || ep2<0 ) return; if (ConnectAbuttingTracks( trk1, ep1, trk2, ep2 )) return; @@ -589,57 +595,202 @@ printf("T%d [%0.3f %0.3f %0.3f]\n", GetTrkIndex(trk1), p1.x, p1.y, a1 ); InfoMessage( _("%d tracks moved"), cnt ); } +static void CreateConnectAnchor(EPINX_T ep, track_p t, BOOL_T shift) { + coOrd pos = GetTrkEndPos(t,ep); + DIST_T d = tempD.scale*0.15; + DIST_T w = tempD.scale/tempD.dpi*4; + ANGLE_T a = GetTrkEndAngle(t,ep); + int i; + if (!shift) { + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.l.pos[0] = pos; + Translate(&anchors(i).u.l.pos[1],pos,a+90,-GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[1],anchors(i).u.l.pos[1],a,-d); + anchors(i).width = w; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.l.pos[0] = pos; + Translate(&anchors(i).u.l.pos[1],pos,a+90,GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[1],anchors(i).u.l.pos[1],a,-d); + anchors(i).width = w; + } else { + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).color = wDrawColorBlue; + Translate(&anchors(i).u.l.pos[0],pos,a+90,GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[0],anchors(i).u.l.pos[0],a,d); + Translate(&anchors(i).u.l.pos[1],pos,a+90,-GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[1],anchors(i).u.l.pos[1],a,-d); + anchors(i).width = w; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).color = wDrawColorBlue; + Translate(&anchors(i).u.l.pos[0],pos,a+90,GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[0],anchors(i).u.l.pos[0],a,-d); + Translate(&anchors(i).u.l.pos[1],pos,a+90,-GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[1],anchors(i).u.l.pos[1],a,d); + anchors(i).width = w; + } +} + +STATUS_T ConnectMultiple() { + int countTracksR0 =0,countTracksR1 =0, possibleEndPoints =0; + if (selectedTrackCount==0) { + ErrorMessage(_("Connect Multiple Tracks - Select multiple tracks to join first")); + return C_CONTINUE; + } + if (NoticeMessage(_("Try to Connect all Selected Tracks?"), _("Yes"), _("No"))<=0) return C_CONTINUE; + track_p trk1 = NULL; + track_p trk2 = NULL; + EPINX_T ep1,ep2; + ANGLE_T a; + DIST_T d; + UndoStart( _("ReConnect"),"Try to reconnect all selected tracks"); + for (int i=0;i<2;i++) { // Try twice - in case later joins help earlier ones and to try close ones first + while ( TrackIterate( &trk1 ) ) { + BOOL_T found = FALSE; + if ( GetTrkSelected( trk1 ) ) { + for (ep1=0; ep1<GetTrkEndPtCnt(trk1); ep1++) { + if (!GetTrkEndTrk( trk1, ep1 )) { + trk2 = NULL; + while (!found && TrackIterate(&trk2) ) { + if (trk1 == trk2) continue; + for (ep2=0; ep2<GetTrkEndPtCnt(trk2); ep2++) { + if (GetTrkEndTrk( trk2, ep2 )) continue; + d = FindDistance(GetTrkEndPos(trk1,ep1),GetTrkEndPos(trk2,ep2)); + a = NormalizeAngle( 180+GetTrkEndAngle( trk1, ep1 ) - GetTrkEndAngle( trk2, ep2 )+(connectAngle/2.0)); + // Take two passes. In round one favor closer connections. In round two try anything. + if ( (i==0 && (d < connectDistance) && (a < connectAngle)) || + (i>0 && (d<3.0 && a<7.5))) { // Match PullTracks criteria in round 2 + PullTracks(trk1,ep1,trk2,ep2); + if (GetTrkEndTrk( trk2, ep2 )) { + found = TRUE; + if (i==0) + countTracksR0++; + else + countTracksR1++; + break; //Stop looking + } else if (i==1) possibleEndPoints++; + } + } + } + if (found) break; //Next EndPoint + } + } + } + } + } + UndoEnd(); + NoticeMessage(_("Round 1 %d and Round 2 %d tracks connected, %d close pairs of end Points were not connected"), _("Ok"), NULL, countTracksR0, countTracksR1, possibleEndPoints); + return C_TERMINATE; +} + +static wMenu_p pullPopupM; static STATUS_T CmdPull( wAction_t action, coOrd pos ) { - static track_p trk1; - static EPINX_T ep1; + static track_p trk1, t1, t2; + static BOOL_T t_turn1, t_turn2; + static EPINX_T ep1, t_ep1, t_ep2; track_p trk2; EPINX_T ep2; static BOOL_T turntable; int countTracksR0 = 0, countTracksR1 = 0, possibleEndPoints = 0; BOOL_T found = FALSE; - ANGLE_T a; - DIST_T d; - switch (action) { + switch (action&0xFF) { case C_START: if (selectedTrackCount==0) - InfoMessage( _("Select first end-point to connect") ); + InfoMessage( _("Select first endpoint or turntable to connect, +Shift to tighten") ); else - InfoMessage( _("Select first end-point to connect, or Right-Click for connecting selected tracks") ); + InfoMessage( _("Select first endpoint to connect, or Right-Click for connecting selected tracks (not turntable)") ); trk1 = NULL; turntable = FALSE; + t1 = t2 = NULL; + t_turn1 = t_turn2 = FALSE; return C_CONTINUE; + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + if ((MyGetKeyState() & WKEY_SHIFT) == 0 ) { + if (trk1 == NULL) { + if ((t1= OnTrack( &pos, FALSE, TRUE )) != NULL) { + if ((t_ep1 = PickUnconnectedEndPointSilent( pos, t1 )) < 0) { + if (QueryTrack(t1, Q_CAN_ADD_ENDPOINTS)) { + DrawTrack(t1,&mainD,wDrawColorBlue); + t_turn1 = TRUE; + } else t1 = NULL; + } + if (t1 && t_ep1 >=0) + CreateConnectAnchor(t_ep1,t1,FALSE); + } + } else { + if (t1 != NULL) { + if (t_turn1) DrawTrack(t1,&mainD,wDrawColorBlue); + else CreateConnectAnchor(t_ep1,t1,FALSE); + } + if ((t2= OnTrackIgnore( &pos, FALSE, TRUE, t1 )) != NULL) { + if ((t_ep2 = PickUnconnectedEndPointSilent( pos, t2 )) < 0) { + if (QueryTrack(t2, Q_CAN_ADD_ENDPOINTS)) { + DrawTrack(t2,&mainD,wDrawColorBlue); + t_turn2 = TRUE; + } else t2 = NULL; + } + if (t2 && t_ep2 >=0) + CreateConnectAnchor(t_ep2,t2,FALSE); + } + + } + } else { //Shift, tighten + t1 = OnTrack( &pos, FALSE, TRUE ); + if (t1 == NULL) + return C_CONTINUE; + t_ep1 = PickUnconnectedEndPointSilent( pos, t1 ); + if ( t_ep1 < 0 ) + return C_CONTINUE; + CreateConnectAnchor(t_ep1,t1,TRUE); + } + break; + case C_LCLICK: - if ( (MyGetKeyState() & WKEY_SHIFT) == 0 ) { + if ( (MyGetKeyState() & WKEY_SHIFT) == 0 ) { //No shift - try and join if (trk1 == NULL) { - if ((trk1 = OnTrack( &pos, TRUE, FALSE )) != NULL) { + if ((trk1 = OnTrack( &pos, TRUE, TRUE )) != NULL) { if ((ep1 = PickUnconnectedEndPoint( pos, trk1 )) < 0) { if (QueryTrack(trk1, Q_CAN_ADD_ENDPOINTS)) { turntable = TRUE; ep1 = -1; } else trk1 = NULL; } else { - InfoMessage( _("Select second end-point to connect") ); + InfoMessage( _("Select second endpoint or turntable to connect") ); } } } else { - if ((trk2 = OnTrack( &pos, TRUE, FALSE )) != NULL) { + if ((trk2 = OnTrackIgnore( &pos, TRUE, TRUE, trk1 )) != NULL) { + if (trk2 == trk1) { + InfoMessage( _("Same Track! - please select another") ); + return C_CONTINUE; + } if ((ep2 = PickUnconnectedEndPoint( pos, trk2 )) >= 0 ) { PullTracks( trk1, ep1, trk2, ep2 ); trk1 = NULL; inError = TRUE; return C_TERMINATE; } - if (!turntable && QueryTrack(trk2, Q_CAN_ADD_ENDPOINTS)) { + if (!turntable && QueryTrack(trk2, Q_CAN_ADD_ENDPOINTS)) { /*Second end a turntable */ ep2 = -1; turntable = TRUE; PullTracks( trk2, ep2, trk1, ep1); @@ -651,7 +802,7 @@ static STATUS_T CmdPull( } } } else { - trk1 = OnTrack( &pos, TRUE, FALSE ); + trk1 = OnTrack( &pos, TRUE, TRUE ); if (trk1 == NULL) return C_CONTINUE; ep1 = PickUnconnectedEndPoint( pos, trk1 ); @@ -664,56 +815,23 @@ static STATUS_T CmdPull( } return C_CONTINUE; - case C_RCLICK: - if (selectedTrackCount==0) { - ErrorMessage(_("Connect Multiple Tracks - Select multiple tracks to join first")); - return C_CONTINUE; - } - if (NoticeMessage(_("Try to Connect all Selected Tracks?"), _("Yes"), _("No"))<=0) return C_CONTINUE; - trk1 = NULL; - trk2 = NULL; - UndoStart( _("ReConnect"),"Try to reconnect all selected tracks"); - for (int i=0;i<2;i++) { // Try twice - in case later joins help earlier ones and to try close ones first - while ( TrackIterate( &trk1 ) ) { - found = FALSE; - if ( GetTrkSelected( trk1 ) ) { - for (ep1=0; ep1<GetTrkEndPtCnt(trk1); ep1++) { - if (!GetTrkEndTrk( trk1, ep1 )) { - trk2 = NULL; - while (!found && TrackIterate(&trk2) ) { - if (trk1 == trk2) continue; - for (ep2=0; ep2<GetTrkEndPtCnt(trk2); ep2++) { - if (GetTrkEndTrk( trk2, ep2 )) continue; - d = FindDistance(GetTrkEndPos(trk1,ep1),GetTrkEndPos(trk2,ep2)); - a = NormalizeAngle( 180+GetTrkEndAngle( trk1, ep1 ) - GetTrkEndAngle( trk2, ep2 )+(connectAngle/2.0)); - // Take two passes. In round one favor closer connections. In round two try anything. - if ( (i==0 && (d < connectDistance) && (a < connectAngle)) || - (i>0 && (d<3.0 && a<7.5))) { // Match PullTracks criteria in round 2 - PullTracks(trk1,ep1,trk2,ep2); - if (GetTrkEndTrk( trk2, ep2 )) { - found = TRUE; - if (i==0) - countTracksR0++; - else - countTracksR1++; - break; //Stop looking - } else if (i==1) possibleEndPoints++; - } - } - } - if (found) break; //Next EndPoint - } - } - } - } - } - UndoEnd(); - NoticeMessage(_("Round 1 %d and Round 2 %d tracks connected, %d close pairs of end Points were not connected"), _("Ok"), NULL, countTracksR0, countTracksR1, possibleEndPoints); - return C_TERMINATE; - case C_REDRAW: + if (anchors_da.cnt) + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + if (t1 && t_turn1) + DrawTrack(t1,&tempD,wDrawColorBlue); + if (t2 && t_turn2) + DrawTrack(t2,&tempD,wDrawColorBlue); return C_CONTINUE; + case C_TEXT: + if (action>>8 == 'S') { + wBool_t rc = ConnectMultiple(); + MainRedraw(); // CmdPull: ConnectMultiple + return rc; + } + break; + case C_CANCEL: return C_TERMINATE; @@ -723,16 +841,35 @@ static STATUS_T CmdPull( case C_CONFIRM: return C_CONTINUE; + case C_CMDMENU: + menuPos = pos; + wMenuPopupShow( pullPopupM ); + return C_CONTINUE; + break; + + default: return C_CONTINUE; } + return C_CONTINUE; } #include "bitmaps/pull.xpm" +wMenuPush_p pullConnectMultiple; + +void pullMenuEnter(int key) { + int action; + action = C_TEXT; + action |= key<<8; + CmdPull(action,zero); +} + void InitCmdPull( wMenu_p menu ) { - AddMenuButton( menu, CmdPull, "cmdConnect", _("Connect Two Tracks"), wIconCreatePixMap(pull_xpm), LEVEL0_50, IC_STICKY|IC_LCLICK|IC_POPUP2|IC_RCLICK, ACCL_CONNECT, NULL ); + AddMenuButton( menu, CmdPull, "cmdConnect", _("Connect Two Tracks"), wIconCreatePixMap(pull_xpm), LEVEL0_50, IC_STICKY|IC_INITNOTSTICKY|IC_LCLICK|IC_POPUP3|IC_CMDMENU|IC_WANT_MOVE, ACCL_CONNECT, NULL ); + pullPopupM = MenuRegister( "Connect Options" ); + pullConnectMultiple = wMenuPushCreate( pullPopupM, "", _("Connect All Selected - 'S'"), 0, (wMenuCallBack_p)pullMenuEnter, (void*) 'S'); } diff --git a/app/bin/cruler.c b/app/bin/cruler.c index b1addc6..d3f2926 100644 --- a/app/bin/cruler.c +++ b/app/bin/cruler.c @@ -62,43 +62,34 @@ static STATUS_T CmdRuler( wAction_t action, coOrd pos ) 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: + if (Dr.state == DR_ON) { + DrawRuler( &tempD, Dr.pos0, Dr.pos1, 0.0, TRUE, TRUE, wDrawColorBlack ); + } return C_CONTINUE; case C_CANCEL: @@ -126,21 +117,22 @@ STATUS_T ModifyRuler( 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; + case C_REDRAW: + DrawRuler( &tempD, Dr.pos0, Dr.pos1, 0.0, TRUE, TRUE, wDrawColorBlack ); + break; default: return C_ERROR; } + return C_CONTINUE; } diff --git a/app/bin/cselect.c b/app/bin/cselect.c index 8ff68c0..4e4e8eb 100644 --- a/app/bin/cselect.c +++ b/app/bin/cselect.c @@ -23,9 +23,11 @@ #include <math.h> #include <string.h> +#include "draw.h" #include "ccurve.h" #include "tcornu.h" #include "tbezier.h" +#include "track.h" #define PRIVATE_EXTRADATA #include "compound.h" #include "cselect.h" @@ -38,6 +40,10 @@ #include "param.h" #include "track.h" #include "utility.h" +#include "draw.h" +#include "misc.h" +#include "trackx.h" + #include "bitmaps/bmendpt.xbm" #include "bitmaps/bma0.xbm" @@ -59,10 +65,12 @@ struct extraData { char junk[2000]; }; static wDrawBitMap_p endpt_bm; static wDrawBitMap_p angle_bm[4]; +track_p IsInsideABox(coOrd pos); - long quickMove = 0; - BOOL_T importMove = 0; - int incrementalDrawLimit = 20; +static track_p moveDescTrk; +static coOrd moveDescPos; + +int incrementalDrawLimit = 20; static int microCount = 0; static dynArr_t tlist_da; @@ -71,12 +79,169 @@ static dynArr_t tlist_da; #define TlistAppend( T ) \ { DYNARR_APPEND( track_p, tlist_da, 10 );\ Tlist(tlist_da.cnt-1) = T; } -static track_p *tlist2 = NULL; + +BOOL_T TListSearch(track_p T) { + for (int i=0;i<tlist_da.cnt-1;i++) { \ + if (Tlist(i) == T) return TRUE; + } + return FALSE; +} static wMenu_p selectPopup1M; +static wMenu_p selectPopup1CM; static wMenu_p selectPopup2M; +static wMenu_p selectPopup2CM; +static wMenu_p selectPopup2RM; +static wMenu_p selectPopup2TM; +static wMenu_p selectPopup2TYM; +static wMenuPush_p menuPushModify; +static wMenuPush_p rotateAlignMI; +static wMenuPush_p descriptionMI; +static wMenuPush_p hideMI; +static wMenuPush_p bridgeMI; +static wMenuPush_p tiesMI; + + +static BOOL_T doingAlign = FALSE; +static enum { AREA, MOVE } mode; +static void SelectOneTrack( + track_p trk, + wBool_t selected ); static void DrawSelectedTracksD( drawCmd_p d, wDrawColor color ); + +static dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) + +void CreateArrowAnchor(coOrd pos,ANGLE_T a,DIST_T len) { + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).width = 0; + anchors(i).u.l.pos[0] = pos; + Translate(&anchors(i).u.l.pos[1],pos,NormalizeAngle(a+135),len); + anchors(i).color = wDrawColorBlue; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).width = 0; + anchors(i).u.l.pos[0] = pos; + Translate(&anchors(i).u.l.pos[1],pos,NormalizeAngle(a-135),len); + anchors(i).color = wDrawColorBlue; +} + +void static CreateRotateAnchor(coOrd pos) { + DIST_T d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = SEG_CRVLIN; + anchors(i).width = d/8; + anchors(i).u.c.center = pos; + anchors(i).u.c.a0 = 180.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).u.c.radius = d*2; + anchors(i).color = wDrawColorAqua; + coOrd head; //Arrows + for (int j=0;j<3;j++) { + Translate(&head,pos,j*120,d*2); + CreateArrowAnchor(head,NormalizeAngle((j*120)+90),d); + } +} + +void static CreateModifyAnchor(coOrd pos) { + DIST_T d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = SEG_FILCRCL; + anchors(i).width = 0; + anchors(i).u.c.center = pos; + anchors(i).u.c.a0 = 180.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).u.c.radius = d/2; + anchors(i).color = wDrawColorPowderedBlue; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_CRVLIN; + anchors(i).width = 0; + anchors(i).u.c.center = pos; + anchors(i).u.c.a0 = 180.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).u.c.radius = d; + anchors(i).color = wDrawColorPowderedBlue; + +} + +void CreateDescribeAnchor(coOrd pos) { + DIST_T d = tempD.scale*0.15; + for (int j=0;j<2;j++) { + pos.x += j*d*3/4; + pos.y += j*d/2; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = SEG_CRVLIN; + anchors(i).width = d/4; + anchors(i).u.c.center = pos; + anchors(i).u.c.a0 = 270.0; + anchors(i).u.c.a1 = 270.0; + anchors(i).u.c.radius = d*3/4; + anchors(i).color = wDrawColorPowderedBlue; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).width = d/4; + Translate(&anchors(i).u.l.pos[0],pos,180.0,d*3/4); + Translate(&anchors(i).u.l.pos[1],pos,180.0,d*1.5); + anchors(i).color = wDrawColorPowderedBlue; + } +} + +void CreateActivateAnchor(coOrd pos) { + DIST_T d = tempD.scale*0.15; + coOrd c = pos; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = SEG_CRVLIN; + anchors(i).width = 0; + c.x -= d*3/4; + anchors(i).u.c.center = c; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).u.c.radius = d; + anchors(i).color = wDrawColorPowderedBlue; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + c.x += d*1.5; + anchors(i).type = SEG_CRVLIN; + anchors(i).width = 0; + anchors(i).u.c.center = pos; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).u.c.radius = d; + anchors(i).color = wDrawColorPowderedBlue; +} + +void static CreateMoveAnchor(coOrd pos) { + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),pos,0,TRUE,wDrawColorBlue); + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),pos,90,TRUE,wDrawColorBlue); +} + +void CreateEndAnchor(coOrd p, wBool_t lock) { + DIST_T d = tempD.scale*0.15; + + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = lock?SEG_FILCRCL:SEG_CRVLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.c.center = p; + anchors(i).u.c.radius = d/2; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).width = 0; +} + + /***************************************************************************** * @@ -122,6 +287,88 @@ static void DrawTrackAndEndPts( } + +static void RedrawSelectedTracksBoundary() +{ +/* Truth table: 4 cases for a track trk, connected to trk1 + * SELREDRAW + * trk, trk1: F, F - No changes, nothing to draw + * T, F - trk changes but trk1 didn't, flip drawing of select boundary marker + * F, T - trk didn't change but trk1 did, handle redrawing when we get to 2nd track + * T, T - both changed, but we don't need to redraw anything + * unfortunately we will do a redundant redraw when we get to the 2nd track + */ +// if (importTrack != NULL) +// return; + track_p trk; + TRK_ITERATE( trk ) { + if ( GetTrkBits(trk) & TB_SELREDRAW ) { + // This track has changed + for ( EPINX_T ep = 0; ep < GetTrkEndPtCnt(trk); ep++ ) { + track_p trk1 = GetTrkEndTrk( trk, ep ); + if ( trk1 == NULL ) + continue; + +// if ( GetTrkIndex( trk ) < GetTrkIndex( trk1 ) +// continue; + if ( ( GetTrkBits(trk1) & TB_SELREDRAW ) == 0 ) { + // Connected track hasn't changed + wDrawColor color = selectedColor; + if ( ( GetTrkBits(trk) & TB_SELECTED ) == ( GetTrkBits(trk1) & TB_SELECTED ) ) { + // There was a select boundary here, but not now. + // Undraw old X + color = wDrawColorWhite; + } + DIST_T len; + coOrd p = GetTrkEndPos( trk, ep ); + ANGLE_T a = GetTrkEndAngle( trk, ep ); + coOrd p0, p1, p2; + len = GetTrkGauge(trk)*2.0; + if (len < 0.10*mainD.scale) + len = 0.10*mainD.scale; + Translate( &p1, p, a+45, len ); + Translate( &p2, p, a+225, len ); + DrawLine( &mainD, p1, p2, 2, color ); + Translate( &p1, p, a-45, len ); + Translate( &p2, p, a-225, len ); + DrawLine( &mainD, p1, p2, 2, color ); + if ( color == wDrawColorWhite ) { + // Fill in holes by undraw cross + DIST_T len2 = sqrt( GetTrkGauge(trk)*GetTrkGauge(trk)/2.0 ); + DIST_T len3 = 0.1*mainD.scale; + color = GetTrkColor( trk, &mainD ); + if ( mainD.scale < twoRailScale ) { + Translate( &p0, p, a-225, len2 ); + Translate( &p1, p0, a, len3 ); + Translate( &p2, p0, a+180, len3 ); + DrawLine( &mainD, p1, p2, GetTrkWidth(trk), color ); + Translate( &p0, p, a+225, len2 ); + Translate( &p1, p0, a, len3 ); + Translate( &p2, p0, a+180, len3 ); + DrawLine( &mainD, p1, p2, GetTrkWidth(trk), color ); + color = GetTrkColor( trk1, &mainD ); + Translate( &p0, p, a-45, len2 ); + Translate( &p1, p0, a, len3 ); + Translate( &p2, p0, a+180, len3 ); + DrawLine( &mainD, p1, p2, GetTrkWidth(trk1), color ); + Translate( &p0, p, a+45, len2 ); + Translate( &p1, p0, a, len3 ); + Translate( &p2, p0, a+180, len3 ); + DrawLine( &mainD, p1, p2, GetTrkWidth(trk1), color ); + } else { + Translate( &p1, p, a, len3 ); + Translate( &p2, p, a+180, len3 ); + DrawLine( &mainD, p1, p2, GetTrkWidth(trk), color ); + } + } + } + } + ClrTrkBits( trk, TB_SELREDRAW ); + } + } +} + + EXPORT void SetAllTrackSelect( BOOL_T select ) { track_p trk; @@ -139,27 +386,26 @@ EXPORT void SetAllTrackSelect( BOOL_T select ) 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) + SetTrkBits( trk, TB_SELREDRAW ); DrawTrackAndEndPts( trk, wDrawColorBlack ); } } } SelectedTrackCountChange(); if (doRedraw) { - MainRedraw(); - MapRedraw(); + MainRedraw(); // SetAllTrackSelect } else { + RedrawSelectedTracksBoundary(); wDrawDelayUpdate( mainD.d, FALSE ); } } -/* Invert selected state of all visible objects. +/* Invert selected state of all visible non-module objects. * * \param none * \return none @@ -171,21 +417,15 @@ EXPORT void InvertTrackSelect( void *ptr ) trk = NULL; while ( TrackIterate( &trk ) ) { - if (GetLayerVisible( GetTrkLayer( trk ))) { - if (GetTrkSelected(trk)) - { - ClrTrkBits( trk, TB_SELECTED ); - selectedTrackCount--; - } - else - SetTrkBits( trk, TB_SELECTED ); - selectedTrackCount++; + if (GetLayerVisible( GetTrkLayer( trk )) && + !GetLayerModule(GetTrkLayer( trk ))) { + SelectOneTrack( trk, GetTrkSelected(trk)==0 ); } } + RedrawSelectedTracksBoundary(); SelectedTrackCountChange(); - MainRedraw(); - MapRedraw(); + MainRedraw(); // InvertTrackSelect } /* Select orphaned (ie single) track pieces. @@ -204,7 +444,7 @@ EXPORT void OrphanedTrackSelect( void *ptr ) while( TrackIterate( &trk ) ) { cnt = 0; - if( GetLayerVisible( GetTrkLayer( trk ))) { + if( GetLayerVisible( GetTrkLayer( trk ) && !GetLayerModule(GetTrkLayer(trk)))) { for( ep = 0; ep < GetTrkEndPtCnt( trk ); ep++ ) { if( GetTrkEndTrk( trk, ep ) ) cnt++; @@ -217,17 +457,21 @@ EXPORT void OrphanedTrackSelect( void *ptr ) } } } + RedrawSelectedTracksBoundary(); SelectedTrackCountChange(); - MainRedraw(); - MapRedraw(); + MainRedraw(); // OrphanTrackSelect } - static void SelectOneTrack( track_p trk, wBool_t selected ) { - DrawTrackAndEndPts( trk, wDrawColorWhite ); + BOOL_T bRedraw = (GetTrkSelected(trk) != 0) != selected; + if ( !bRedraw ) { + ClrTrkBits( trk, TB_SELREDRAW ); + return; + } + SetTrkBits( trk, TB_SELREDRAW ); if (selected) { SetTrkBits( trk, TB_SELECTED ); selectedTrackCount++; @@ -240,8 +484,27 @@ static void SelectOneTrack( } +static void HighlightSelectedTracks( + track_p trk_ignore, BOOL_T box, BOOL_T invert ) +{ + track_p trk = NULL; + if ( selectedTrackCount == 0 ) + return; + while ( TrackIterate( &trk ) ) { + if (trk == trk_ignore) continue; + if(GetTrkSelected(trk)) { + if (!GetLayerVisible( GetTrkLayer( trk ))) continue; + if (invert) + DrawTrack(trk,&tempD,wDrawColorPreviewUnselected); + else + DrawTrack(trk,&tempD,wDrawColorPreviewSelected ); + } + } + +} + static void SelectConnectedTracks( - track_p trk ) + track_p trk, BOOL_T display_only ) { track_p trk1; int inx; @@ -249,32 +512,65 @@ static void SelectConnectedTracks( tlist_da.cnt = 0; TlistAppend( trk ); InfoCount( 0 ); - wDrawDelayUpdate( mainD.d, FALSE ); + if (!display_only) wDrawDelayUpdate( mainD.d, FALSE ); for (inx=0; inx<tlist_da.cnt; inx++) { - if ( inx > 0 && selectedTrackCount == 0 ) + if ( inx > 0 && (selectedTrackCount == 0) && !display_only ) return; trk = Tlist(inx); if (inx!=0 && - GetTrkSelected(trk)) + GetTrkSelected(trk)) { + if (display_only) + DrawTrack(trk,&tempD,wDrawColorPreviewSelected ); continue; + } else if (GetTrkSelected(trk)) { + if (display_only) + DrawTrack(trk,&tempD,wDrawColorPreviewUnselected); + continue; + } for (ep=0; ep<GetTrkEndPtCnt(trk); ep++) { trk1 = GetTrkEndTrk( trk, ep ); - if (trk1 && (!GetTrkSelected(trk1)) && GetLayerVisible( GetTrkLayer( trk1 )) ) { - TlistAppend( trk1 ) + if (trk1 && !TListSearch(trk1) && GetLayerVisible( GetTrkLayer( trk1 ))) { + if (GetTrkSelected(trk1)) { + if (display_only) DrawTrack(trk1,&tempD,wDrawColorPreviewSelected ); + } else TlistAppend( trk1 ); } } - if (!GetTrkSelected(trk)) { - SelectOneTrack( trk, TRUE ); - InfoCount( inx+1 ); + if (display_only) DrawTrack(trk,&tempD,wDrawColorPreviewSelected ); + else if (!GetTrkSelected(trk)) { + if (GetLayerModule(GetTrkLayer(trk))) { + continue; + } else { + SelectOneTrack( trk, TRUE ); + InfoCount( inx+1 ); + } } - SetTrkBits(trk, TB_SELECTED); } - wDrawDelayUpdate( mainD.d, TRUE ); - wFlush(); - InfoCount( trackCount ); + if (!display_only) { + RedrawSelectedTracksBoundary(); + wDrawDelayUpdate( mainD.d, TRUE ); + wFlush(); + InfoCount( trackCount ); + } } +typedef void (*doModuleTrackCallBack_t)(track_p, BOOL_T); +static int DoModuleTracks( int moduleLayer, doModuleTrackCallBack_t doit, BOOL_T val) +{ + track_p trk; + trk = NULL; + int cnt = 0; + while ( TrackIterate( &trk ) ) { + if (GetTrkLayer(trk) == moduleLayer) { + doit( trk, val ); + cnt++; + } + } + return cnt; +} +static void DrawSingleTrack(track_p trk, BOOL_T bit) { + DrawTrack(trk,&tempD,bit?wDrawColorPreviewSelected:wDrawColorPreviewUnselected); +} typedef BOOL_T (*doSelectedTrackCallBack_t)(track_p, BOOL_T); static void DoSelectedTracks( doSelectedTrackCallBack_t doit ) @@ -331,9 +627,41 @@ EXPORT void SelectTrackWidth( void* width ) UndoEnd(); } +EXPORT void SelectLineType( void* width ) +{ + track_p trk; + if (SelectedTracksAreFrozen()) + return; + if (selectedTrackCount<=0) { + ErrorMessage( MSG_NO_SELECTED_TRK ); + return; + } + UndoStart( _("Change Line Type"), "linetype" ); + trk = NULL; + wDrawDelayUpdate( mainD.d, TRUE ); + while ( TrackIterate( &trk ) ) { + if (GetTrkSelected(trk)) { + UndoModify( trk ); + if (QueryTrack(trk, Q_CAN_MODIFY_CONTROL_POINTS)) + SetBezierLineType(trk, (int) (long) width); + else if (QueryTrack(trk, Q_IS_DRAW)) + SetLineType( trk, (int)(long)width ); + else if (QueryTrack(trk, Q_IS_STRUCTURE)) { + SetCompoundLineType(trk, (int)(long)width); + } + } + } + wDrawDelayUpdate( mainD.d, FALSE ); + UndoEnd(); +} + +static BOOL_T doingDouble; EXPORT void SelectDelete( void ) { + if (GetCurrentCommand() != selectCmdInx) return; + if (doingDouble) return; + if (SelectedTracksAreFrozen()) return; if (selectedTrackCount>0) { @@ -341,6 +669,7 @@ EXPORT void SelectDelete( void ) wDrawDelayUpdate( mainD.d, TRUE ); wDrawDelayUpdate( mapD.d, TRUE ); DoSelectedTracks( DeleteTrack ); + DoRedraw(); // SelectDelete wDrawDelayUpdate( mainD.d, FALSE ); wDrawDelayUpdate( mapD.d, FALSE ); selectedTrackCount = 0; @@ -368,8 +697,10 @@ static BOOL_T FlipHidden( track_p trk, BOOL_T junk ) if ( drawTunnel == 0 ) flipHiddenDoSelectRecount = TRUE; if (GetTrkVisible(trk)) { - ClrTrkBits( trk, TB_VISIBLE|(drawTunnel==0?TB_SELECTED:0) ); - } else { + ClrTrkBits( trk, TB_VISIBLE|(drawTunnel==0?(TB_SELECTED|TB_SELREDRAW):0) ); + ClrTrkBits (trk, TB_BRIDGE); + ClrTrkBits (trk, TB_NOTIES); +; } else { SetTrkBits( trk, TB_VISIBLE ); } /*DrawNewTrack( trk );*/ @@ -382,6 +713,29 @@ static BOOL_T FlipHidden( track_p trk, BOOL_T junk ) return TRUE; } +static BOOL_T FlipBridge( track_p trk, BOOL_T junk ) +{ + UndoModify( trk ); + if (GetTrkBridge(trk)) { + ClrTrkBits( trk, TB_BRIDGE ); + } else { + SetTrkBits( trk, TB_BRIDGE ); + SetTrkBits( trk, TB_VISIBLE); + } + return TRUE; +} + +static BOOL_T FlipTies( track_p trk, BOOL_T junk ) +{ + UndoModify( trk ); + if (GetTrkNoTies(trk)) { + ClrTrkBits( trk, TB_NOTIES ); + } else { + SetTrkBits( trk, TB_NOTIES ); + SetTrkBits( trk, TB_VISIBLE ); + } + return TRUE; +} EXPORT void SelectTunnel( void ) { @@ -401,6 +755,39 @@ EXPORT void SelectTunnel( void ) SelectRecount(); } +EXPORT void SelectBridge( void ) +{ + if (SelectedTracksAreFrozen()) + return; + if (selectedTrackCount>0) { + flipHiddenDoSelectRecount = FALSE; + UndoStart( _("Bridge Tracks "), "bridge" ); + wDrawDelayUpdate( mainD.d, TRUE ); + DoSelectedTracks( FlipBridge ); + wDrawDelayUpdate( mainD.d, FALSE ); + UndoEnd(); + } else { + ErrorMessage( MSG_NO_SELECTED_TRK ); + } + MainRedraw(); // SelectBridge +} + +EXPORT void SelectTies( void ) +{ + if (SelectedTracksAreFrozen()) + return; + if (selectedTrackCount>0) { + flipHiddenDoSelectRecount = FALSE; + UndoStart( _("Ties Tracks "), "noties" ); + wDrawDelayUpdate( mainD.d, TRUE ); + DoSelectedTracks( FlipTies ); + wDrawDelayUpdate( mainD.d, FALSE ); + UndoEnd(); + } else { + ErrorMessage( MSG_NO_SELECTED_TRK ); + } + MainRedraw(); // SelectTies +} void SelectRecount( void ) { @@ -445,6 +832,7 @@ EXPORT void SelectCurrentLayer( void ) SelectOneTrack( trk, TRUE ); } } + RedrawSelectedTracksBoundary(); } @@ -530,8 +918,7 @@ EXPORT void DoRefreshCompound( void ) DoSelectedTracks( RefreshCompound ); RefreshCompound( NULL, FALSE ); UndoEnd(); - MainRedraw(); - MapRedraw(); + MainRedraw(); // DoRefreshCompound } else { ErrorMessage( MSG_NO_SELECTED_TRK ); } @@ -539,15 +926,12 @@ EXPORT void DoRefreshCompound( void ) static drawCmd_t tempSegsD = { - NULL, &tempSegDrawFuncs, DC_GROUP, 1, 0.0, {0.0, 0.0}, {0.0, 0.0}, Pix2CoOrd, CoOrd2Pix }; + NULL, &tempSegDrawFuncs, 0, 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 ) ) @@ -557,7 +941,6 @@ EXPORT void WriteSelectedTracksToTempSegs( void ) SetTrkBits( trk, TB_SELECTED ); } } - tempSegDrawFuncs.options = oldOptions; } static char rescaleFromScale[20]; @@ -619,6 +1002,7 @@ static BOOL_T RescaleDoIt( track_p trk, BOOL_T junk ) { EPINX_T ep, ep1; track_p trk1; + UndrawNewTrack( trk ); UndoModify(trk); if ( rescalePercent != 100.0 ) { for (ep=0; ep<GetTrkEndPtCnt(trk); ep++) { @@ -636,6 +1020,7 @@ static BOOL_T RescaleDoIt( track_p trk, BOOL_T junk ) if ( rescaleMode==0 ) SetTrkScale( trk, rescaleToInx ); getboundsCount++; + DrawNewTrack( trk ); return TRUE; } @@ -688,7 +1073,6 @@ static void RescaleDlgOk( rescaleToInx = GetScaleInx( rescaleToScaleInx, rescaleToGaugeInx ); DoSelectedTracks( RescaleDoIt ); - DoRedraw(); wHide( rescalePG.win ); } @@ -799,38 +1183,6 @@ EXPORT void DoRescale( void ) 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 ) @@ -846,7 +1198,11 @@ static void DrawSelectedTracksD( drawCmd_p d, wDrawColor color ) if ( OFF_D( d->orig, d->size, lo, hi ) ) continue; } + if (color != wDrawColorWhite) + ClrTrkBits(trk, TB_UNDRAWN); DrawTrack( trk, d, color ); + if (color == wDrawColorWhite) + SetTrkBits( trk, TB_UNDRAWN ); } /*wDrawDelayUpdate( d->d, FALSE );*/ } @@ -865,7 +1221,7 @@ 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 }; + NULL, &tempSegDrawFuncs, DC_SIMPLE, 1, 0.0, {0.0, 0.0}, {0.0, 0.0}, Pix2CoOrd, CoOrd2Pix }; @@ -884,61 +1240,56 @@ static void AccumulateTracks( void ) 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++; + movedCnt =0; + for ( inx = 0; inx<tlist_da.cnt; inx++ ) { + trk = Tlist(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 (!QueryTrack(trk,Q_IS_CORNU)) + DrawTrack( trk, &moveD, wDrawColorBlack ); } + movedCnt++; } - } - moveD.options &= ~DC_QUICK; + } InfoCount( movedCnt ); /*wDrawDelayUpdate( moveD.d, FALSE );*/ } +static void AddEndCornus() { + for (int i=0;i<tlist_da.cnt;i++) { + track_p trk = DYNARR_N(track_p,tlist_da,i); + track_p tc; + for (int j=GetTrkEndPtCnt(trk)-1;j>=0;j--) { + tc = GetTrkEndTrk(trk,j); + if (tc && !GetTrkSelected(tc) && QueryTrack(tc,Q_IS_CORNU) && !QueryTrack(trk,Q_IS_CORNU)) { //On end and cornu + SelectOneTrack( tc, TRUE ); + DYNARR_APPEND(track_p,tlist_da,1); //Add to selected list + DYNARR_LAST(track_p,tlist_da) = tc; + } + } + } +} + static void GetMovedTracks( BOOL_T undraw ) { - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, 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; + AddEndCornus(); //Include Cornus that are attached at ends of selected 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 ); + wSetCursor( mainD.d, defaultCursor ); 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 );*/ } } @@ -989,48 +1340,50 @@ 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; - } + dynArr_t cornu_segs; + + DrawSegs( &tempD, moveOrig, moveAngle, &tempSegs(0), tempSegs_da.cnt, + 0.0, wDrawColorBlack ); + 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 (QueryTrack(trk,Q_IS_CORNU)) { + DYNARR_RESET(trkSeg_t,cornu_segs); + coOrd pos[2]; + DIST_T radius[2]; + ANGLE_T angle[2]; + coOrd center[2]; + trackParams_t trackParams; + if (GetTrackParams(PARAMS_CORNU, trk, zero, &trackParams)) { + for (int i=0;i<2;i++) { + pos[i] = trackParams.cornuEnd[i]; + center[i] = trackParams.cornuCenter[i]; + angle[i] = trackParams.cornuAngle[i]; + radius[i] = trackParams.cornuRadius[i]; + if (!GetTrkEndTrk(trk,i) || + (GetTrkEndTrk(trk,i) && GetTrkSelected(GetTrkEndTrk(trk,i)))) { + if (!move0B) { + Rotate( &pos[i], zero, moveAngle ); + Rotate( ¢er[i],zero, moveAngle ); + angle[i] = NormalizeAngle(angle[i]+moveAngle); + } + pos[i].x += moveOrig.x; + pos[i].y += moveOrig.y; + center[i].x +=moveOrig.x; + center[i].y +=moveOrig.y; + } + } + CallCornu0(&pos[0],¢er[0],&angle[0],&radius[0],&cornu_segs, FALSE); + trkSeg_p cornu_p = &DYNARR_N(trkSeg_t,cornu_segs,0); + + DrawSegsO(&tempD, trk, zero, 0.0, cornu_p,cornu_segs.cnt, + GetTrkGauge(trk), wDrawColorBlack, DTS_LEFT|DTS_RIGHT ); } - if ( !OFF_MAIND( pos, pos ) ) - DrawBitMap( &tempD, pos, bm, selectedColor ); + } + } + return; } @@ -1041,7 +1394,8 @@ static void MoveTracks( BOOL_T rotate, coOrd base, coOrd orig, - ANGLE_T angle ) + ANGLE_T angle, + BOOL_T undo) { track_p trk, trk1; EPINX_T ep, ep1; @@ -1051,48 +1405,47 @@ static void MoveTracks( DIST_T endRadius; coOrd endCenter; - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, wCursorWait ); /*UndoStart( "Move/Rotate Tracks", "move/rotate" );*/ if (tlist_da.cnt <= incrementalDrawLimit) { - DrawMapBoundingBox( FALSE ); - if (eraseFirst) + if (eraseFirst) { DrawSelectedTracksD( &mainD, wDrawColorWhite ); - DrawSelectedTracksD( &mapD, 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 ); - if (QueryTrack(trk1,Q_IS_CORNU)) { //Cornu at end stays connected - GetTrackParams(PARAMS_CORNU,trk,GetTrkEndPos(trk,ep),&trackParms); - if (trackParms.type == curveTypeStraight) { - endRadius = 0; - endCenter = zero; - } else { - endRadius = trackParms.arcR; - endCenter = trackParms.arcP; - } - DrawTrack(trk1,&mainD,wDrawColorWhite); - DrawTrack(trk1,&mapD,wDrawColorWhite); - endAngle = NormalizeAngle(GetTrkEndAngle(trk,ep)+180); - if (SetCornuEndPt(trk1,ep1,GetTrkEndPos(trk,ep),endCenter,endAngle,endRadius)) { - ConnectTracks(trk,ep,trk1,ep1); - DrawTrack(trk1,&mainD,wDrawColorBlack); - DrawTrack(trk1,&mapD,wDrawColorBlack); - } else { - DeleteTrack(trk1,TRUE); - ErrorMessage(_("Cornu too tight - it was deleted")); - } - } else { - if (QueryTrack(trk,Q_IS_CORNU)) { //I am a Cornu myself! + BOOL_T fixed_end; + fixed_end = FALSE; + if (QueryTrack(trk, Q_IS_CORNU)) { + for (int i=0;i<2;i++) { + track_p te; + if ((te = GetTrkEndTrk(trk,i)) && !GetTrkSelected(te)) { + fixed_end = TRUE; + } + } + } + + if (!fixed_end) { + 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 ); + } + } + } else { + if (QueryTrack(trk, Q_IS_CORNU)) { //Cornu will be at the end of selected set + for (int i=0;i<2;i++) { + if ((trk1 = GetTrkEndTrk(trk,i)) && GetTrkSelected(trk1)) { + ep1 = GetEndPtConnectedToMe( trk1, trk ); + DisconnectTracks(trk,i,trk1,ep1); GetTrackParams(PARAMS_CORNU,trk1,GetTrkEndPos(trk1,ep1),&trackParms); if (trackParms.type == curveTypeStraight) { endRadius = 0; @@ -1102,39 +1455,55 @@ static void MoveTracks( endCenter = trackParms.arcP; } DrawTrack(trk,&mainD,wDrawColorWhite); - DrawTrack(trk1,&mapD,wDrawColorWhite); + DrawTrack(trk,&mapD,wDrawColorWhite); endAngle = NormalizeAngle(GetTrkEndAngle(trk1,ep1)+180); - if (SetCornuEndPt(trk,ep,GetTrkEndPos(trk1,ep1),endCenter,endAngle,endRadius)) { - ConnectTracks(trk,ep,trk1,ep1); + if (SetCornuEndPt(trk,i,GetTrkEndPos(trk1,ep1),endCenter,endAngle,endRadius)) { + ConnectTracks(trk,i,trk1,ep1); DrawTrack(trk,&mainD,wDrawColorBlack); DrawTrack(trk,&mapD,wDrawColorBlack); } else { - ErrorMessage(_("Cornu selected too tight after move - it was left alone")); - DrawTrack(trk,&mainD,wDrawColorBlack); - DrawTrack(trk,&mapD,wDrawColorBlack); + DeleteTrack(trk,TRUE); + ErrorMessage(_("Cornu too tight - it was deleted")); + DoRedraw(); // MoveTracks: Cornu/delete + return; + } + } else if (!trk1) { //No end track + DrawTrack(trk,&mainD,wDrawColorWhite); + DrawTrack(trk,&mapD,wDrawColorWhite); + GetTrackParams(PARAMS_CORNU,trk,GetTrkEndPos(trk,i),&trackParms); + if (move) { + coOrd end_pos, end_center; + end_pos = trackParms.cornuEnd[i]; + end_pos.x += base.x; + end_pos.y += base.y; + end_center = trackParms.cornuCenter[i]; + end_center.x += base.x; + end_center.y += base.y; + SetCornuEndPt(trk,i,end_pos,end_center,trackParms.cornuAngle[i],trackParms.cornuRadius[i]); + } + if (rotate) { + coOrd end_pos, end_center; + ANGLE_T end_angle; + end_pos = trackParms.cornuEnd[i]; + end_center = trackParms.cornuCenter[i]; + Rotate(&end_pos, orig, angle); + Rotate(&end_center, orig, angle); + end_angle = NormalizeAngle( trackParms.cornuAngle[i] + angle ); + SetCornuEndPt(trk,i,end_pos,end_center,end_angle,trackParms.cornuRadius[i]); } + DrawTrack(trk,&mainD,wDrawColorBlack); + DrawTrack(trk,&mapD,wDrawColorBlack); } } - 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; + ClrAllTrkBits(TB_UNDRAWN); + DoRedraw(); + wSetCursor( mainD.d, defaultCursor ); + if (undo) UndoEnd(); InfoCount( trackCount ); } @@ -1159,7 +1528,7 @@ void MoveToJoin( angle += 180.0; angle = NormalizeAngle( angle ); GetMovedTracks( FALSE ); - MoveTracks( TRUE, TRUE, TRUE, base, orig, angle ); + MoveTracks( TRUE, TRUE, TRUE, base, orig, angle, TRUE ); UndrawNewTrack( trk0 ); UndrawNewTrack( trk1 ); ConnectTracks( trk0, ep0, trk1, ep1 ); @@ -1176,6 +1545,145 @@ void FreeTempStrings() { } } } + + +wBool_t FindEndIntersection(coOrd base, coOrd orig, ANGLE_T angle, track_p * t1, EPINX_T * ep1, track_p * t2, EPINX_T * ep2) { + *ep1 = -1; + *ep2 = -1; + *t1 = NULL; + *t2 = NULL; + for ( int inx=0; inx<tlist_da.cnt; inx++ ) { //All selected + track_p ts = Tlist(inx); + for (int i=0; i<GetTrkEndPtCnt(ts); i++) { //All EndPoints + track_p ct; + if ((ct = GetTrkEndTrk(ts,i))!=NULL) { + if (GetTrkSelected(ct) || QueryTrack(ts,Q_IS_CORNU)) continue; // Another selected track or Cornu - ignore + } + + coOrd pos1 = GetTrkEndPos(ts,i); + if (angle != 0.0) + Rotate(&pos1,orig,angle); + else { + pos1.x +=base.x; + pos1.y +=base.y; + } + coOrd pos2; + pos2 = pos1; + track_p tt; + if ((tt=OnTrackIgnore(&pos2,FALSE,TRUE,ts))!=NULL) { + if (GetTrkGauge(ts) != GetTrkGauge(tt)) continue; //Ignore if different gauges + if (!GetTrkSelected(tt)) { //Ignore if new track is selected + EPINX_T epp = PickUnconnectedEndPointSilent(pos2, tt); + if (epp>=0) { + DIST_T d = FindDistance(pos1,GetTrkEndPos(tt,epp)); + if (IsClose(d)) { + *ep1 = epp; + *ep2 = i; + *t1 = tt; + *t2 = ts; + return TRUE; + } + } else { + epp = PickEndPoint(pos2,tt); //Any close end point (even joined) + if (epp>=0) { + ct = GetTrkEndTrk(tt,epp); + if (ct && GetTrkSelected(ct)) { //Point is junction to selected track - so will be broken + DIST_T d = FindDistance(pos1,GetTrkEndPos(tt,epp)); + if (IsClose(d)) { + *ep1 = epp; + *ep2 = i; + *t1 = tt; + *t2 = ts; + return TRUE; + } + } + } + } + } + } + } + } + return FALSE; +} + +void DrawHighlightLayer(int layer) { + track_p ts = NULL; + BOOL_T initial = TRUE; + coOrd layer_hi = zero,layer_lo = zero; + while ( TrackIterate( &ts ) ) { + if ( !GetLayerVisible( GetTrkLayer( ts))) continue; + if (!GetTrkSelected(ts)) continue; + if (GetTrkLayer(ts) != layer) continue; + coOrd hi,lo; + GetBoundingBox(ts, &hi, &lo); + if (initial) { + layer_hi = hi; + layer_lo = lo; + initial = FALSE; + } else { + if (layer_hi.x < hi.x ) layer_hi.x = hi.x; + if (layer_hi.y < hi.y ) layer_hi.y = hi.y; + if (layer_lo.x > lo.x ) layer_lo.x = lo.x; + if (layer_lo.y > lo.y ) layer_lo.y = lo.y; + } + } + wPos_t margin = (wPos_t)(10.5*mainD.scale/mainD.dpi); + layer_hi.x +=margin; + layer_hi.y +=margin; + layer_lo.x -=margin; + layer_lo.y -=margin; + + wPos_t rect[4][2]; + int type[4]; + coOrd top_left, bot_right; + top_left.x = layer_lo.x; top_left.y = layer_hi.y; + bot_right.x = layer_hi.x; bot_right.y = layer_lo.y; + type[0] = type[1] = type[2] = type[3] = 0; + mainD.CoOrd2Pix(&mainD,layer_lo,&rect[0][0],&rect[0][1]); + mainD.CoOrd2Pix(&mainD,top_left,&rect[1][0],&rect[1][1]); + mainD.CoOrd2Pix(&mainD,layer_hi,&rect[2][0],&rect[2][1]); + mainD.CoOrd2Pix(&mainD,bot_right,&rect[3][0],&rect[3][1]); + wDrawPolygon(tempD.d,rect,(wPolyLine_e *)type,4,wDrawColorPowderedBlue,0,wDrawLineDash,wDrawOptTemp,0,0); +} + +void SetUpMenu2(coOrd pos, track_p trk) { + wMenuPushEnable( menuPushModify,FALSE); + wMenuPushEnable(descriptionMI,FALSE); + wMenuPushEnable( rotateAlignMI, FALSE ); + wMenuPushEnable( hideMI, FALSE ); + wMenuPushEnable( bridgeMI, FALSE ); + wMenuPushEnable( tiesMI, FALSE ); + if ((trk) && + QueryTrack(trk,Q_CAN_ADD_ENDPOINTS)) { //Turntable snap to center if within 1/4 radius + trackParams_t trackParams; + if (GetTrackParams(PARAMS_CORNU, trk, pos, &trackParams)) { + DIST_T dist = FindDistance(pos, trackParams.ttcenter); + if (dist < trackParams.ttradius/4) { + cmdMenuPos = trackParams.ttcenter; + } + } + } + if (trk && !QueryTrack( trk, Q_IS_DRAW )) { + wMenuPushEnable( hideMI, TRUE ); + wMenuPushEnable( bridgeMI, TRUE ); + wMenuPushEnable( tiesMI, TRUE ); + } + if (trk) { + wMenuPushEnable( menuPushModify, + (QueryTrack( trk, Q_CAN_MODIFY_CONTROL_POINTS ) || + QueryTrack( trk, Q_IS_CORNU ) || + (QueryTrack( trk, Q_IS_DRAW ) && !QueryTrack( trk, Q_IS_TEXT )) || + QueryTrack( trk, Q_IS_ACTIVATEABLE))); + } + if ((trk)) { + wMenuPushEnable(descriptionMI, QueryTrack( trk, Q_HAS_DESC )); + moveDescTrk = trk; + moveDescPos = pos; + } + if (selectedTrackCount>0) + wMenuPushEnable( rotateAlignMI, TRUE ); +} + static STATUS_T CmdMove( wAction_t action, @@ -1185,9 +1693,16 @@ static STATUS_T CmdMove( static coOrd orig; static int state; - switch( action&0xFF) { + static EPINX_T ep1; + static EPINX_T ep2; + static track_p t1; + static track_p t2; + static BOOL_T doingMove; + + switch( action & 0xFF) { case C_START: + DYNARR_RESET(trkSeg_t,anchors_da); if (selectedTrackCount == 0) { ErrorMessage( MSG_NO_SELECTED_TRK ); return C_TERMINATE; @@ -1197,63 +1712,123 @@ static STATUS_T CmdMove( } InfoMessage( _("Drag to move selected tracks - Shift+Ctrl+Arrow micro-steps the move") ); state = 0; + ep1 = -1; + ep2 = -1; + doingMove = FALSE; + break; + + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + CreateMoveAnchor(pos); break; case C_DOWN: + DYNARR_RESET(trkSeg_t,anchors_da); + if (doingMove) { + doingMove = FALSE; + UndoEnd(); + } + if (SelectedTracksAreFrozen()) { return C_TERMINATE; } UndoStart( _("Move Tracks"), "move" ); base = zero; orig = pos; - GetMovedTracks(quickMove != MOVE_QUICK); + + GetMovedTracks(TRUE); SetMoveD( TRUE, base, 0.0 ); - //DrawMovedTracks(); drawCount = 0; state = 1; - MainRedraw(); - MapRedraw(); return C_CONTINUE; case C_MOVE: + DYNARR_RESET(trkSeg_t,anchors_da); + ep1=-1; + ep2=-1; drawEnable = enableMoveDraw; - //DrawMovedTracks(); base.x = pos.x - orig.x; base.y = pos.y - orig.y; SnapPos( &base ); SetMoveD( TRUE, base, 0.0 ); - //DrawMovedTracks(); + if (((MyGetKeyState()&(WKEY_ALT)) == 0) == magneticSnap) { // ALT + if (FindEndIntersection(base,zero,0.0,&t1,&ep1,&t2,&ep2)) { + coOrd pos2 = GetTrkEndPos(t2,ep2); + pos2.x +=base.x; + pos2.y +=base.y; + CreateEndAnchor(pos2,FALSE); + CreateEndAnchor(GetTrkEndPos(t1,ep1),TRUE); + } + } #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(); - MapRedraw(); return C_CONTINUE; case C_UP: + DYNARR_RESET(trkSeg_t,anchors_da); state = 0; - //DrawMovedTracks(); FreeTempStrings(); - MoveTracks( quickMove==MOVE_QUICK, TRUE, FALSE, base, zero, 0.0 ); + if (t1 && ep1>=0 && t2 && ep2>=0) { + MoveToJoin(t2,ep2,t1,ep1); + } else { + MoveTracks( FALSE, TRUE, FALSE, base, zero, 0.0, TRUE ); + } + ep1 = -1; + ep2 = -1; + tlist_da.cnt = 0; return C_TERMINATE; case C_CMDMENU: - wMenuPopupShow( selectPopup1M ); + if (doingMove) UndoEnd(); + doingMove = FALSE; + base = pos; + track_p trk = OnTrack(&pos, FALSE, FALSE); //Note pollutes pos if turntable + if ((trk) && + QueryTrack(trk,Q_CAN_ADD_ENDPOINTS)) { //Turntable snap to center if within 1/4 radius + trackParams_t trackParams; + if (GetTrackParams(PARAMS_CORNU, trk, pos, &trackParams)) { + DIST_T dist = FindDistance(base, trackParams.ttcenter); + if (dist < trackParams.ttradius/4) { + cmdMenuPos = trackParams.ttcenter; + } + } + } + moveDescPos = pos; + moveDescTrk = trk; + SetUpMenu2(pos,trk); + menuPos = pos; + wMenuPopupShow( selectPopup2M ); return C_CONTINUE; + case C_TEXT: + if ((action>>8) == 'c') { + panCenter = pos; + LOG( log_pan, 2, ( "PanCenter:Sel-%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)0); + } + if ((action>>8) == 'e') { + DoZoomExtents(0); + } + if ((action>>8) == '0' || (action>>8 == 'o')) { + PanMenuEnter('o'); + } + break; case C_REDRAW: /* DO_REDRAW */ + if (anchors_da.cnt) + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); if ( state == 0 ) break; - DrawSelectedTracksD( &mainD, wDrawColorWhite ); DrawMovedTracks(); + break; case wActionExtKey: if (state) return C_CONTINUE; if (SelectedTracksAreFrozen()) return C_TERMINATE; if ((MyGetKeyState() & - (WKEY_SHIFT | WKEY_CTRL)) == (WKEY_SHIFT | WKEY_CTRL)) { + (WKEY_SHIFT | WKEY_CTRL)) == (WKEY_SHIFT | WKEY_CTRL)) { //Both base = zero; DIST_T w = tempD.scale/tempD.dpi; switch((wAccelKey_e) action>>8) { @@ -1275,21 +1850,35 @@ static STATUS_T CmdMove( } drawEnable = enableMoveDraw; - GetMovedTracks(quickMove!=MOVE_QUICK); - UndoStart( _("Move Tracks"), "move" ); + GetMovedTracks(TRUE); + if (!doingMove) UndoStart( _("Move Tracks"), "move" ); + doingMove = TRUE; SetMoveD( TRUE, base, 0.0 ); - DrawSelectedTracksD( &mainD, wDrawColorWhite ); - MoveTracks( quickMove==MOVE_QUICK, TRUE, FALSE, base, zero, 0.0 ); + MoveTracks( FALSE, TRUE, FALSE, base, zero, 0.0, FALSE ); ++microCount; if (microCount>5) { microCount = 0; - MainRedraw(); - MapRedraw(); + MainRedraw(); // Micro step move } return C_CONTINUE; } break; + case C_FINISH: + if (doingMove) { + doingMove = FALSE; + UndoEnd(); + } + tlist_da.cnt = 0; + break; + case C_CONFIRM: + case C_CANCEL: + if (doingMove) { + doingMove = FALSE; + UndoUndo(); + } + tlist_da.cnt = 0; + break; default: break; } @@ -1297,13 +1886,18 @@ static STATUS_T CmdMove( } -wMenuPush_p rotateAlignMI; -int rotateAlignState = 0; -static void RotateAlign( void ) +static int rotateAlignState = 0; + +static void RotateAlign( BOOL_T align ) { - rotateAlignState = 1; - InfoMessage( _("Click on selected object to align") ); + rotateAlignState = 0; + if (align) { + rotateAlignState = 1; + doingAlign = TRUE; + mode = MOVE; + InfoMessage( _("Align: Click on a selected object to be aligned") ); + } } static STATUS_T CmdRotate( @@ -1311,18 +1905,27 @@ static STATUS_T CmdRotate( coOrd pos ) { static coOrd base; + static coOrd orig_base; static coOrd orig; static ANGLE_T angle; static BOOL_T drawnAngle; static ANGLE_T baseAngle; + static BOOL_T clockwise; + static BOOL_T direction_set; static track_p trk; ANGLE_T angle1; coOrd pos1; static int state; + static EPINX_T ep1; + static EPINX_T ep2; + static track_p t1; + static track_p t2; + switch( action ) { case C_START: + DYNARR_RESET(trkSeg_t,anchors_da); state = 0; if (selectedTrackCount == 0) { ErrorMessage( MSG_NO_SELECTED_TRK ); @@ -1334,8 +1937,15 @@ static STATUS_T CmdRotate( InfoMessage( _("Drag to rotate selected tracks, Shift+RightClick for QuickRotate Menu") ); wMenuPushEnable( rotateAlignMI, TRUE ); rotateAlignState = 0; + ep1 = -1; + ep2 = -1; + break; + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + CreateRotateAnchor(pos); break; case C_DOWN: + DYNARR_RESET(trkSeg_t,anchors_da); state = 1; if (SelectedTracksAreFrozen()) { return C_TERMINATE; @@ -1357,12 +1967,15 @@ static STATUS_T CmdRotate( } } } - GetMovedTracks(FALSE); + CreateRotateAnchor(orig); + GetMovedTracks(TRUE); SetMoveD( FALSE, base, angle ); + /*DrawLine( &mainD, base, orig, 0, wDrawColorBlack ); DrawMovedTracks(FALSE, orig, angle);*/ } else { pos1 = pos; + drawnAngle = FALSE; onTrackInSplit = TRUE; trk = OnTrack( &pos, TRUE, FALSE ); onTrackInSplit = FALSE; @@ -1396,13 +2009,13 @@ static STATUS_T CmdRotate( } GetMovedTracks(TRUE); SetMoveD( FALSE, orig, angle ); - //DrawMovedTracks(); } } - MainRedraw(); - MapRedraw(); return C_CONTINUE; case C_MOVE: + DYNARR_RESET(trkSeg_t,anchors_da); + ep1=-1; + ep2=-1; if ( rotateAlignState == 1 ) return C_CONTINUE; if ( rotateAlignState == 2 ) { @@ -1416,7 +2029,6 @@ static STATUS_T CmdRotate( 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 ) @@ -1427,65 +2039,95 @@ static STATUS_T CmdRotate( InfoMessage( _("Angle %0.3f"), angle1 ); SetMoveD( FALSE, orig, angle ); /*printf( "angle 2 = %0.3f\n", angle );*/ - //DrawMovedTracks(); - MainRedraw(); - MapRedraw(); 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_T diff_angle = 0.0; + base = pos; + drawEnable = enableMoveDraw; + if ( FindDistance( orig, pos ) > (20.0/75.0)*mainD.scale ) { + ANGLE_T old_angle = angle; angle = FindAngle( orig, pos ); if (!drawnAngle) { baseAngle = angle; drawnAngle = TRUE; + direction_set = FALSE; + } else { + if (!direction_set) { + if (DifferenceBetweenAngles(baseAngle,angle)>=0) clockwise = TRUE; + else clockwise = FALSE; + direction_set = TRUE; + } else { + if (clockwise) { + if (DifferenceBetweenAngles(baseAngle,angle)<0 && fabs(DifferenceBetweenAngles(baseAngle, old_angle))<5) + clockwise = FALSE; + } else { + if (DifferenceBetweenAngles(baseAngle,angle)>=0 && fabs(DifferenceBetweenAngles(baseAngle,old_angle))<5) + clockwise = 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) ); + orig_base = base = pos; + //angle = NormalizeAngle( angle-baseAngle ); + diff_angle = DifferenceBetweenAngles(baseAngle,angle); + if ( (MyGetKeyState() & (WKEY_CTRL|WKEY_SHIFT)) == (WKEY_CTRL|WKEY_SHIFT) ) { //Both Shift+Ctrl + if (clockwise) { + if (diff_angle<0) diff_angle+=360; + } else { + if (diff_angle>0) diff_angle-=360; + } + diff_angle = floor((diff_angle+7.5)/15.0)*15.0; + angle = baseAngle+diff_angle; } - DrawLine( &tempD, base, orig, 0, wDrawColorBlack ); - SetMoveD( FALSE, orig, angle ); - //DrawMovedTracks(); + Translate( &base, orig, angle, FindDistance(orig,pos) ); //Line one + Translate( &orig_base,orig, baseAngle, FindDistance(orig,pos)<=(60.0/75.00*mainD.scale)?FindDistance(orig,pos):60.0/75.00*mainD.scale ); //Line two + SetMoveD( FALSE, orig, NormalizeAngle( angle-baseAngle ) ); + if (((MyGetKeyState()&(WKEY_ALT)) == WKEY_ALT) != magneticSnap) { //Just Shift + if (FindEndIntersection(zero,orig,NormalizeAngle( angle-baseAngle ),&t1,&ep1,&t2,&ep2)) { + coOrd pos2 = GetTrkEndPos(t2,ep2); + coOrd pos1 = GetTrkEndPos(t1,ep1); + Rotate(&pos2,orig,NormalizeAngle( angle-baseAngle )); + CreateEndAnchor(pos2,FALSE); + CreateEndAnchor(pos1,TRUE); + } + } + #ifdef DRAWCOUNT - InfoMessage( _(" Angle %0.3f #%ld"), angle, drawCount ); + InfoMessage( _("Angle %0.3f #%ld"), fabs(diff_angle), drawCount ); #else - InfoMessage( _(" Angle %0.3f"), angle ); + InfoMessage( _("Angle %0.3f %s"), fabs(diff_angle), clockwise?"Clockwise":"Counter-Clockwise" ); #endif wFlush(); drawEnable = TRUE; - } - MainRedraw(); - MapRedraw(); + } else + InfoMessage( _("Origin Set. Drag away to set start angle")); + return C_CONTINUE; + case C_UP: + DYNARR_RESET(trkSeg_t,anchors_da); state = 0; - if ( rotateAlignState == 1 ) { - if ( trk && GetTrkSelected(trk) ) { - InfoMessage( _("Click on the 2nd Unselected object") ); - rotateAlignState = 2; - } - return C_CONTINUE; - } - FreeTempStrings(); - if ( rotateAlignState == 2 ) { - //DrawMovedTracks(); - MoveTracks( quickMove==MOVE_QUICK, FALSE, TRUE, zero, orig, angle ); + if (t1 && ep1>=0 && t2 && ep2>=0) { + MoveToJoin(t2,ep2,t1,ep1); + CleanSegs(&tempSegs_da); rotateAlignState = 0; - } else if (drawnAngle) { - DrawLine( &tempD, base, orig, 0, wDrawColorBlack ); - //DrawMovedTracks(); - MoveTracks( quickMove==MOVE_QUICK, FALSE, TRUE, zero, orig, angle ); + } else { + if ( rotateAlignState == 1 ) { + if ( trk && GetTrkSelected(trk) ) { + InfoMessage( _("Align: Click on the 2nd unselected object") ); + rotateAlignState = 2; + } + return C_CONTINUE; + } + CleanSegs(&tempSegs_da); + if ( rotateAlignState == 2 ) { + MoveTracks( FALSE, FALSE, TRUE, zero, orig, angle, TRUE ); + rotateAlignState = 0; + } else if (drawnAngle) { + MoveTracks( FALSE, FALSE, TRUE, zero, orig, NormalizeAngle( angle-baseAngle ), TRUE ); + } } - MainRedraw(); - MapRedraw(); + UndoEnd(); + tlist_da.cnt = 0; return C_TERMINATE; case C_CMDMENU: @@ -1501,16 +2143,65 @@ static STATUS_T CmdRotate( } } } + moveDescPos = pos; + moveDescTrk = trk; + SetUpMenu2(pos,trk); + menuPos = pos; wMenuPopupShow( selectPopup2M ); return C_CONTINUE; + case C_TEXT: + if ((action>>8) == 'd') { + panCenter = pos; + LOG( log_pan, 2, ( "PanCenter:Sel-%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)0); + } + if ((action>>8) == 'e') { + DoZoomExtents(0); + } + if ((action>>8) == '0' || (action>>8 == 'o')) { + PanMenuEnter('o'); + } + break; case C_REDRAW: + if (anchors_da.cnt) + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); /* DO_REDRAW */ if ( state == 0 ) break; - if ( rotateAlignState != 2 ) - DrawLine( &tempD, base, orig, 0, wDrawColorBlack ); - DrawSelectedTracksD( &mainD, wDrawColorWhite ); + if ( rotateAlignState != 2 ) { + DIST_T width = mainD.scale*0.5; + DrawLine( &tempD, base, orig, 0, wDrawColorBlue ); + if (drawnAngle) { + DrawLine( &tempD, orig_base, orig, (wDrawWidth)width, wDrawColorBlue ); + ANGLE_T a = DifferenceBetweenAngles(FindAngle(orig, orig_base),FindAngle(orig, base)); + + DIST_T dist = FindDistance(orig,base); + if (dist>(60.0/75.0)*mainD.scale) dist = (60.0/75.0)*mainD.scale; + + if (direction_set) { + if (clockwise) { + if (a<0) a = a + 360; + DrawArc( &tempD, orig, dist/2, FindAngle(orig,orig_base), a, FALSE, 0, wDrawColorBlue); + } else { + if (a>0) a = a - 360; + DrawArc( &tempD, orig, dist/2, FindAngle(orig,base), fabs(a), FALSE, 0, wDrawColorBlue); + } + DIST_T d; + d = mainD.scale*0.25; + ANGLE_T arrow_a = NormalizeAngle(FindAngle(orig,orig_base)+a/2); + coOrd arr1,arr2,arr3; + Translate(&arr2,orig,arrow_a,dist/2); + if (clockwise) arrow_a +=90; + else arrow_a -=90; + Translate(&arr1,arr2,arrow_a+135,d/2); + Translate(&arr3,arr2,arrow_a-135,d/2); + DrawLine( &tempD, arr1, arr2, 0, wDrawColorBlue ); + DrawLine( &tempD, arr2, arr3, 0, wDrawColorBlue ); + } + } + + } DrawMovedTracks(); break; @@ -1524,12 +2215,9 @@ static void QuickMove( void* pos) { return; wDrawDelayUpdate( mainD.d, TRUE ); GetMovedTracks(FALSE); - DrawSelectedTracksD( &mainD, wDrawColorWhite ); UndoStart( _("Move Tracks"), "Move Tracks" ); - MoveTracks( quickMove==MOVE_QUICK, TRUE, FALSE, move_pos, zero, 0.0 ); + MoveTracks( TRUE, TRUE, FALSE, move_pos, zero, 0.0, TRUE ); wDrawDelayUpdate( mainD.d, FALSE ); - MainRedraw(); - MapRedraw(); } static void QuickRotate( void* pangle ) @@ -1539,18 +2227,16 @@ static void QuickRotate( void* pangle ) return; wDrawDelayUpdate( mainD.d, TRUE ); GetMovedTracks(FALSE); - DrawSelectedTracksD( &mainD, wDrawColorWhite ); + //DrawSelectedTracksD( &mainD, wDrawColorWhite ); UndoStart( _("Rotate Tracks"), "Rotate Tracks" ); - MoveTracks( quickMove==MOVE_QUICK, FALSE, TRUE, zero, cmdMenuPos, angle ); + MoveTracks( TRUE, FALSE, TRUE, zero, cmdMenuPos, (double)angle/1000, TRUE); wDrawDelayUpdate( mainD.d, FALSE ); - MainRedraw(); - MapRedraw(); } static wMenu_p moveDescM; static wMenuToggle_p moveDescMI; -static track_p moveDescTrk; + static void ChangeDescFlag( wBool_t set, void * mode ) { wDrawDelayUpdate( mainD.d, TRUE ); @@ -1565,113 +2251,228 @@ static void ChangeDescFlag( wBool_t set, void * mode ) 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; +track_p FindTrackDescription(coOrd pos, EPINX_T * ep_o, int * mode_o, BOOL_T show_hidden, BOOL_T * hidden_o) { + track_p trk = NULL; + DIST_T d, dd = 10000; + track_p trk1 = NULL; + EPINX_T ep1=-1, ep=-1; + BOOL_T hidden_t, hidden; + coOrd dpos = pos; + coOrd cpos; + int mode = -1; 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 ( (labelEnable&LABELENABLE_ENDPT_ELEV)!=0 && *mode_o <= 0) { + for ( ep1=0; ep1<GetTrkEndPtCnt(trk1); ep1++ ) { + d = EndPtDescriptionDistance( pos, trk1, ep1, &dpos, FALSE, NULL ); //No hidden + if ( d < dd ) { + dd = d; + trk = trk1; + ep = ep1; + mode = 0; + hidden = FALSE; + cpos= dpos; + } } } - if ( !QueryTrack( trk1, Q_HAS_DESC ) ) + if ( !QueryTrack( trk1, Q_HAS_DESC ) && (mode <0 || mode > 0) ) continue; - if ( ( GetTrkBits( trk1 ) & TB_HIDEDESC ) != 0 ) + if ((labelEnable&LABELENABLE_TRKDESC)==0) continue; - d = CompoundDescriptionDistance( pos, trk1 ); + if ( ( GetTrkBits( trk1 ) & TB_HIDEDESC ) != 0 ) { + if ( !show_hidden ) continue; + } + d = CompoundDescriptionDistance( pos, trk1, &dpos, show_hidden, &hidden_t ); if ( d < dd ) { dd = d; trk = trk1; ep = -1; mode = 1; + hidden = hidden_t; + cpos = dpos; } - d = CurveDescriptionDistance( pos, trk1 ); + d = CurveDescriptionDistance( pos, trk1, &dpos, show_hidden, &hidden_t ); if ( d < dd ) { dd = d; trk = trk1; ep = -1; mode = 2; + hidden = hidden_t; + cpos = dpos; } - d = CornuDescriptionDistance( pos, trk1 ); + d = CornuDescriptionDistance( pos, trk1, &dpos, show_hidden, &hidden_t ); if ( d < dd ) { dd = d; trk = trk1; ep = -1; mode = 3; + hidden = hidden_t; + cpos = dpos; } - d = BezierDescriptionDistance( pos, trk1 ); + d = BezierDescriptionDistance( pos, trk1, &dpos, show_hidden, &hidden_t ); if ( d < dd ) { dd = d; trk = trk1; ep = -1; mode = 4; + hidden = hidden_t; + cpos = dpos; } } - if (trk != NULL) { - UndoStart( _("Move Label"), "Modedesc( T%d )", GetTrkIndex(trk) ); - UndoModify( trk ); + if ((trk != NULL && (trk == OnTrack(&pos, FALSE, FALSE))) || + IsClose(d) || IsClose(FindDistance( pos, cpos) )) { //Only when close to a label or the track - not anywhere on layout! + if (ep_o) *ep_o = ep; + if (mode_o) *mode_o = mode; + if (hidden_o) *hidden_o = hidden; + return trk; + } + else return NULL; +} + +static long moveDescMode; + +STATUS_T CmdMoveDescription( + wAction_t action, + coOrd pos ) +{ + static track_p trk; + static EPINX_T ep; + static BOOL_T hidden; + static int mode; + BOOL_T bChanged; + + moveDescMode = (long)commandContext; //Context 0 = everything, 1 means elevations, 2 means descriptions + + bChanged = FALSE; + switch (action&0xFF) { + case C_START: + moveDescTrk = NULL; + moveDescPos = zero; + trk = NULL; + hidden = FALSE; + mode = -1; + if ( labelWhen < 2 || mainD.scale > labelScale || + (labelEnable&(LABELENABLE_TRKDESC|LABELENABLE_ENDPT_ELEV))==0 ) { + ErrorMessage( MSG_DESC_NOT_VISIBLE ); + return C_TERMINATE; + } + InfoMessage( _("Select and drag a description") ); + break; + case C_TEXT: + if (!moveDescTrk) return C_CONTINUE; + bChanged = FALSE; + if (action>>8 == 's') { + if ( ( GetTrkBits( moveDescTrk ) & TB_HIDEDESC) != 0 ) + bChanged = TRUE; + ClrTrkBits( moveDescTrk, TB_HIDEDESC ); + } else if (action>>8 == 'h') { + if ( ( GetTrkBits( moveDescTrk ) & TB_HIDEDESC) == 0 ) + bChanged = TRUE; + SetTrkBits( moveDescTrk, TB_HIDEDESC ); + } + if ( bChanged ) { + // We should push the draw/undraw of the description down + // but there is no clear way to do that + MainRedraw(); // CmdMoveDescription + } + /*no break*/ + case wActionMove: + if ( labelWhen < 2 || mainD.scale > labelScale ) return C_CONTINUE; + mode = moveDescMode-1; // -1 means everything, 0 means elevations only, 1 means descriptions only + if ((trk=FindTrackDescription(pos,&ep,&mode,TRUE,&hidden))!=NULL) { + if (mode==0) { + InfoMessage(_("Elevation description")); + } else { + if (hidden) { + InfoMessage(_("Hidden description - 's' to Show")); + moveDescTrk = trk; + moveDescPos = pos; + } else { + InfoMessage(_("Shown description - 'h' to Hide")); + moveDescTrk = trk; + moveDescPos = pos; + } + } + return C_CONTINUE; + } + InfoMessage( _("Select and drag a description") ); + break; + case C_DOWN: + if (( labelWhen < 2 || mainD.scale > labelScale ) || + (labelEnable&(LABELENABLE_TRKDESC|LABELENABLE_ENDPT_ELEV))==0 ) { + ErrorMessage( MSG_DESC_NOT_VISIBLE ); + return C_TERMINATE; + } + mode = moveDescMode-1; + trk = FindTrackDescription(pos,&ep,&mode,TRUE,&hidden); + if (trk == NULL ) + return C_CONTINUE; + if (hidden) { + ClrTrkBits( trk, TB_HIDEDESC ); + InfoMessage(_("Hidden Label - Drag to reveal")); + } else { + InfoMessage(_("Drag label")); } + UndoStart( _("Move Label"), "Modedesc( T%d )", GetTrkIndex(trk) ); + UndoModify( trk ); /* no break */ 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 3: - return CornuDescriptionMove( trk, action, pos ); - case 4: - return BezierDescriptionMove( trk, action, pos ); + if ( trk == NULL ) + return C_CONTINUE; + STATUS_T status = C_ERROR; + if ( action == C_REDRAW ) { + if (mode==0) { + DrawEndPt2( &tempD, trk, ep, wDrawColorBlue ); + } else { + if (hidden) { + DrawTrack( trk,&tempD,wDrawColorAqua); + } else { + DrawTrack( trk,&tempD,wDrawColorBlue); + } } } + 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 3: + return CornuDescriptionMove( trk, action, pos ); + case 4: + return BezierDescriptionMove( trk, action, pos ); + } + hidden = FALSE; + if ( action == C_UP ) { + trk = NULL; + InfoMessage(_("To Hide, use Context Menu")); + } break; + case C_CMDMENU: - moveDescTrk = OnTrack( &pos, TRUE, FALSE ); + if (trk == NULL) { + moveDescTrk = OnTrack( &pos, TRUE, FALSE ); + moveDescPos = pos; + } else { + moveDescTrk = trk; + moveDescPos = pos; + } 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 ); + moveDescMI = wMenuToggleCreate( moveDescM, "", _("Show/Hide Description"), 0, TRUE, ChangeDescFlag, NULL ); } wMenuToggleSet( moveDescMI, ( GetTrkBits( moveDescTrk ) & TB_HIDEDESC ) == 0 ); + menuPos = pos; wMenuPopupShow( moveDescM ); break; @@ -1690,10 +2491,9 @@ static void FlipTracks( track_p trk, trk1; EPINX_T ep, ep1; - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, wCursorWait ); /*UndoStart( "Move/Rotate Tracks", "move/rotate" );*/ if (selectedTrackCount <= incrementalDrawLimit) { - DrawMapBoundingBox( FALSE ); wDrawDelayUpdate( mainD.d, TRUE ); wDrawDelayUpdate( mapD.d, TRUE ); } @@ -1724,12 +2524,10 @@ static void FlipTracks( } else { wDrawDelayUpdate( mainD.d, FALSE ); wDrawDelayUpdate( mapD.d, FALSE ); - DrawMapBoundingBox( TRUE ); } - wSetCursor( wCursorNormal ); + wSetCursor( mainD.d, defaultCursor ); UndoEnd(); InfoCount( trackCount ); - MainRedraw(); } @@ -1759,25 +2557,15 @@ static STATUS_T CmdFlip( return C_TERMINATE; } pos0 = pos1 = pos; - DrawLine( &tempD, pos0, pos1, 0, wDrawColorBlack ); - MainRedraw(); - MapRedraw(); 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(); - MapRedraw(); return C_CONTINUE; case C_UP: - DrawLine( &tempD, pos0, pos1, 0, wDrawColorBlack ); UndoStart( _("Flip Tracks"), "flip" ); FlipTracks( pos0, FindAngle( pos0, pos1 ) ); state = 0; - MainRedraw(); - MapRedraw(); return C_TERMINATE; #ifdef LATER @@ -1795,13 +2583,15 @@ static STATUS_T CmdFlip( return C_CONTINUE; } -static STATUS_T SelectArea( +static BOOL_T SelectArea( wAction_t action, coOrd pos ) { static coOrd pos0; static int state; static coOrd base, size, lo, hi; + static BOOL_T add; + static BOOL_T subtract; int cnt; track_p trk; @@ -1810,19 +2600,21 @@ static STATUS_T SelectArea( case C_START: state = 0; - return C_CONTINUE; + add = FALSE; + subtract = FALSE; + return FALSE; case C_DOWN: case C_RDOWN: pos0 = pos; - return C_CONTINUE; + add = (action == C_DOWN); + subtract = (action == C_RDOWN); + return TRUE; case C_MOVE: case C_RMOVE: if (state == 0) { state = 1; - } else { - DrawHilight( &mainD, base, size ); } base = pos0; size.x = pos.x - pos0.x; @@ -1835,24 +2627,24 @@ static STATUS_T SelectArea( size.y = - size.y; base.y = pos.y; } - DrawHilight( &mainD, base, size ); - return C_CONTINUE; + return TRUE; case C_UP: case C_RUP: if (state == 1) { state = 0; - DrawHilight( &mainD, base, size ); + add = (action == C_UP); + subtract = (action == C_RUP); cnt = 0; trk = NULL; + if (add && (selectMode == 0)) SetAllTrackSelect( FALSE ); //Remove all tracks first 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++; - } + if ( (GetTrkSelected( trk )==0) == (action==C_UP) ) + cnt++; } } trk = NULL; @@ -1862,41 +2654,85 @@ static STATUS_T SelectArea( 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) { + if (GetLayerModule(GetTrkLayer(trk))) { + if (add) + DoModuleTracks(GetTrkLayer(trk),SelectOneTrack,TRUE); + else + DoModuleTracks(GetTrkLayer(trk),SelectOneTrack,FALSE); + } else if (cnt > incrementalDrawLimit) { selectedTrackCount += (action==C_UP?1:-1); - if (action==C_UP) + if (add) SetTrkBits( trk, TB_SELECTED ); else ClrTrkBits( trk, TB_SELECTED ); } else { - SelectOneTrack( trk, action==C_UP ); + SelectOneTrack( trk, add ); } } } } + add = FALSE; + subtract = FALSE; + if (cnt > incrementalDrawLimit) { + MainRedraw(); // SelectArea C_UP + } else { + RedrawSelectedTracksBoundary(); + } SelectedTrackCountChange(); - if (cnt > incrementalDrawLimit) - MainRedraw(); } - return C_CONTINUE; + return FALSE; case C_CANCEL: - if (state == 1) { - DrawHilight( &mainD, base, size ); - state = 0; - } + state = 0; + add = FALSE; + subtract = FALSE; break; case C_REDRAW: if (state == 0) break; - DrawHilight( &mainD, base, size ); + //Draw to-be selected tracks versus not. + trk = NULL; + if (selectMode == 0 && add) HighlightSelectedTracks(NULL, TRUE, TRUE); + 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 (GetLayerModule(GetTrkLayer(trk))) { + if (add) + DoModuleTracks(GetTrkLayer(trk),DrawSingleTrack,TRUE); + else if (subtract) + DoModuleTracks(GetTrkLayer(trk),DrawSingleTrack,FALSE); + } else { + if (add) { + if (selectMode == 0 && add) + DrawTrack(trk,&tempD,wDrawColorPreviewSelected); + if (!GetTrkSelected(trk)) + DrawTrack(trk,&tempD,wDrawColorPreviewSelected); + } + else if (subtract) { + if (GetTrkSelected(trk)) + DrawTrack(trk,&tempD,wDrawColorPreviewUnselected); + } + } + } + } + if (add || subtract) { + DrawHilight( &tempD, base, size, add ); + return TRUE; + } break; } - return C_CONTINUE; + return FALSE; } +extern BOOL_T inDescribeCmd; +extern wIndex_t modifyCmdInx; +extern wIndex_t describeCmdInx; +extern wIndex_t panCmdInx; +extern wIndex_t trainCmdInx; static STATUS_T SelectTrack( coOrd pos ) @@ -1904,124 +2740,515 @@ static STATUS_T SelectTrack( track_p trk; char msg[STR_SIZE]; - if ((trk = OnTrack( &pos, TRUE, FALSE )) == NULL) { - return C_CONTINUE; + if (((trk = OnTrack( &pos, FALSE, FALSE )) == NULL) && selectZero) { //If option set and !ctrl or unset and ctrl + SetAllTrackSelect( FALSE ); //Unselect all + return C_CONTINUE; } + if (trk == NULL) return C_CONTINUE; + inDescribeCmd = FALSE; DescribeTrack( trk, msg, sizeof msg ); InfoMessage( msg ); - if (MyGetKeyState() & WKEY_SHIFT) { - SelectConnectedTracks( trk ); + if (GetLayerModule(GetTrkLayer(trk))) { + if (((MyGetKeyState() & WKEY_CTRL) && (selectMode==0)) || (!(MyGetKeyState() & WKEY_CTRL) && (selectMode==1)) ) { + DoModuleTracks(GetTrkLayer(trk),SelectOneTrack,!GetTrkSelected(trk)); + } else { + SetAllTrackSelect( FALSE ); //Just this Track if selectMode = 0 and !CTRL or selectMode = 1 and CTRL + DoModuleTracks(GetTrkLayer(trk),SelectOneTrack,TRUE); + } + RedrawSelectedTracksBoundary(); + return C_CONTINUE; + } + if (MyGetKeyState() & WKEY_SHIFT) { //All track up to + SelectConnectedTracks( trk, FALSE ); + } else if ((MyGetKeyState() & WKEY_CTRL) && (selectMode==0)) { + SelectOneTrack( trk, !GetTrkSelected(trk) ); + } else if (!(MyGetKeyState() & WKEY_CTRL) && (selectMode==1)) { + SelectOneTrack( trk, !GetTrkSelected(trk) ); } else { + SetAllTrackSelect( FALSE ); //Just this Track SelectOneTrack( trk, !GetTrkSelected(trk) ); } + RedrawSelectedTracksBoundary(); + return C_CONTINUE; } +static STATUS_T Activate( coOrd pos) { + track_p trk; + if ((trk = OnTrack( &pos, TRUE, FALSE )) == NULL) { + return C_CONTINUE; + } + if (GetLayerModule(GetTrkLayer(trk))) { + return C_CONTINUE; + } + if (QueryTrack(trk,Q_IS_ACTIVATEABLE)) ActivateTrack(trk); + + return C_CONTINUE; + +} + +track_p IsInsideABox(coOrd pos) { + track_p ts = NULL; + while ( TrackIterate( &ts ) ) { + if (!GetLayerVisible( GetTrkLayer( ts))) continue; + if (!GetTrkSelected(ts)) continue; + coOrd hi,lo; + GetBoundingBox(ts, &hi, &lo); + double boundary = mainD.scale*5/mainD.dpi; + if ((pos.x>=lo.x-boundary && pos.x<=hi.x+boundary) && (pos.y>=lo.y-boundary && pos.y<=hi.y+boundary)) { + return ts; + } + } + return NULL; +} + +void DrawHighlightBoxes(BOOL_T highlight_selected, BOOL_T select, track_p not_this) { + track_p ts = NULL; + coOrd origin,max; + BOOL_T first = TRUE; + while ( TrackIterate( &ts ) ) { + if ( !GetLayerVisible( GetTrkLayer( ts))) continue; + if (!GetTrkSelected(ts)) continue; + if (GetLayerModule(GetTrkLayer(ts))) { + DrawHighlightLayer(GetTrkLayer(ts)); + } + coOrd hi,lo; + if (highlight_selected && (ts != not_this)) DrawTrack(ts,&tempD,select?wDrawColorPreviewSelected:wDrawColorPreviewUnselected ); + GetBoundingBox(ts, &hi, &lo); + if (first) { + origin = lo; + max = hi; + first = FALSE; + } else { + if (lo.x <origin.x) origin.x = lo.x; + if (lo.y <origin.y) origin.y = lo.y; + if (hi.x >max.x) max.x = hi.x; + if (hi.y >max.y) max.y = hi.y; + } + } + if (!first) { + coOrd size; + size.x = max.x-origin.x; + size.y = max.y-origin.y; + wPos_t w,h; + w = (wPos_t)((size.x/mainD.scale)*mainD.dpi+0.5+10); + h = (wPos_t)((size.y/mainD.scale)*mainD.dpi+0.5+10); + wPos_t x, y; + tempD.CoOrd2Pix(&tempD,origin,&x,&y); + wDrawFilledRectangle(tempD.d, x-5, y-5, w, h, wDrawColorPowderedBlue, wDrawOptTemp|wDrawOptTransparent); + } + +} + +static STATUS_T CallModify(wAction_t action, + coOrd pos ) { + int rc = CmdModify(action,pos); + if (rc != C_CONTINUE) + doingDouble = FALSE; + return rc; +} + +static STATUS_T CallDescribe(wAction_t action, coOrd pos) { + int rc = CmdDescribe(action, pos); + return rc; +} + +static void CallPushDescribe(void * func) { + if (moveDescTrk) { + CallDescribe(C_START, moveDescPos); + CallDescribe(C_DOWN, moveDescPos); + CallDescribe(C_UP, moveDescPos); + } + return; +} + +static STATUS_T CmdSelect(wAction_t,coOrd); + +static void CallPushModify(void * func) { + if (moveDescTrk) { + CmdSelect(C_LDOUBLE, moveDescPos); + } + return; +} 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; + static BOOL_T doingMove; + static BOOL_T doingRotate; + + + + STATUS_T rc=C_CONTINUE; + static track_p trk = NULL; + typedef enum {NOSHOW,SHOWMOVE,SHOWROTATE,SHOWMODIFY,SHOWACTIVATE} showType; + static showType showMode; + + mode = AREA; + if (doingAlign || doingRotate || doingMove ) + mode = MOVE; + else { + if ( (action == C_DOWN) || (action == C_RDOWN) || ((action&0xFF) == wActionExtKey) ) { + mode = AREA; + if ( ((action&0xFF) == wActionExtKey) || ( //Moves don't need to be in a box + ( MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) && IsInsideABox(pos)) ) //But cursors do + { + mode = MOVE; + } } } - switch (action) { + + switch (action&0xFF) { case C_START: - InfoMessage( _("Select tracks") ); -#ifdef LATER - if ((!importMove) && selectedTrackCount > 0) { - SetAllTrackSelect( FALSE ); - } -#endif - importMove = FALSE; + InfoMessage( _("Select track") ); + doingMove = FALSE; + doingRotate = FALSE; + doingAlign = FALSE; + doingDouble = FALSE; + showMode = NOSHOW; SelectArea( action, pos ); wMenuPushEnable( rotateAlignMI, FALSE ); + wSetCursor(mainD.d,defaultCursor); + mode = AREA; + trk = NULL; + break; + + case wActionModKey: + case wActionMove: + if (doingDouble) { + return CallModify(action,pos); + } + showMode = NOSHOW; + DYNARR_RESET(trkSeg_t,anchors_da); + coOrd p = pos; + trk = OnTrack( &p, FALSE, FALSE ); + track_p ht; + if ((selectedTrackCount==0) && (trk == NULL)) return C_CONTINUE; + if (trk && !CheckTrackLayerSilent( trk ) ) { + if (GetLayerFrozen(GetTrkLayer(trk)) ) { + trk = NULL; + InfoMessage(_("Track is in Frozen Layer")); + return C_CONTINUE; + } + } + if (selectedTrackCount>0) { + if ((ht = IsInsideABox(pos)) != NULL) { + if ((MyGetKeyState()&WKEY_SHIFT)) { + CreateMoveAnchor(pos); + showMode = SHOWMOVE; + } else if ((MyGetKeyState()&WKEY_CTRL)) { + CreateRotateAnchor(pos); + showMode = SHOWROTATE; + } else if (!GetLayerModule(GetTrkLayer(ht))) { + if (QueryTrack( ht, Q_CAN_MODIFY_CONTROL_POINTS ) || + QueryTrack( ht, Q_IS_CORNU ) || + (QueryTrack( ht, Q_IS_DRAW ) && !QueryTrack( ht, Q_IS_TEXT))) { + CreateModifyAnchor(pos); + showMode = SHOWMODIFY; + } else { + if (QueryTrack(ht,Q_IS_ACTIVATEABLE)) + CreateActivateAnchor(pos); + showMode = SHOWACTIVATE; + } + } + } + } break; case C_DOWN: - case C_UP: - case C_MOVE: case C_RDOWN: - case C_RUP: - case C_RMOVE: - case C_REDRAW: + if (doingDouble) { + return CallModify(action,pos); + } + DYNARR_RESET(trkSeg_t,anchors_da); switch (mode) { + rc = C_CONTINUE; case MOVE: - if (SelectedTracksAreFrozen()) { + if (SelectedTracksAreFrozen() || (selectedTrackCount==0)) { 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 ); - } + } else if ((MyGetKeyState()&(WKEY_CTRL|WKEY_SHIFT))==WKEY_CTRL) { + doingRotate = TRUE; + doingMove = FALSE; + RotateAlign( FALSE ); + rc = CmdRotate( action, pos ); + } else if ((MyGetKeyState()&(WKEY_SHIFT|WKEY_CTRL))==WKEY_SHIFT) { + doingMove = TRUE; + doingRotate = FALSE; + rc = CmdMove( action, pos ); } break; - case MOVEDESC: - rc = CmdMoveDescription( action, pos ); + case AREA: + doingMove = FALSE; + doingRotate = FALSE; + SelectArea( action, pos ); + break; + default: ; + } + trk = NULL; + return rc; + break; + case wActionExtKey: + case C_RMOVE: + case C_MOVE: + if (doingDouble) { + return CallModify(action,pos); + } + if ((action&0xFF) == wActionExtKey && ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL)) == (WKEY_SHIFT|WKEY_CTRL))) { //Both + arrow + doingMove = TRUE; + mode = MOVE; + } + DYNARR_RESET(trkSeg_t,anchors_da); + switch (mode) { + case MOVE: + if (SelectedTracksAreFrozen() || (selectedTrackCount==0)) { + rc = C_TERMINATE; + tlist_da.cnt = 0; + doingMove = FALSE; + doingRotate = FALSE; + } else if (doingRotate == TRUE) { + RotateAlign( FALSE ); + rc = CmdRotate( action, pos ); + } else if (doingMove == TRUE) { + rc = CmdMove( action, pos ); + } break; case AREA: - rc = SelectArea( action, pos ); + doingMove = FALSE; + doingRotate = FALSE; + SelectArea( action, pos ); + break; + default: ; + } + if ((action&0xFF) == wActionExtKey && ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL)) == (WKEY_SHIFT|WKEY_CTRL))) //Both + doingMove = FALSE; + return rc; + break; + case C_RUP: + case C_UP: + if (doingDouble) { + return CallModify(action,pos); + } + DYNARR_RESET(trkSeg_t,anchors_da); + switch (mode) { + case MOVE: + if (SelectedTracksAreFrozen() || (selectedTrackCount==0)) { + rc = C_TERMINATE; + doingMove = FALSE; + doingRotate = FALSE; + } else if (doingRotate == TRUE) { + RotateAlign( FALSE ); + rc = CmdRotate( action, pos ); + } else if (doingMove == TRUE) { + rc = CmdMove( action, pos ); + } break; - case NONE: + case AREA: + doingMove = FALSE; + doingRotate = FALSE; + SelectArea( action, pos ); + rc = C_CONTINUE; break; + default: ; } - if (action == C_UP || action == C_RUP) - mode = AREA; + doingMove = FALSE; + doingRotate = FALSE; + mode = AREA; return rc; - - case wActionMove: break; + case C_REDRAW: + if (doingDouble) { + return CallModify(action,pos); + } + if (doingMove) { + rc = CmdMove( C_REDRAW, pos ); + } else if (doingRotate) { + rc = CmdRotate( C_REDRAW, pos ); + } + + //Once doing a move or a rotate, make an early exit + if (doingMove || doingRotate) { + if (anchors_da.cnt) { + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + } + return C_CONTINUE; + } + BOOL_T AreaSelect = FALSE; + // Draw the selected area, no-op if none selected + if (mode==AREA) { + AreaSelect = SelectArea( action, pos ); + if (AreaSelect) return C_CONTINUE; + } + + // Highlight a whole Module's worth of tracks if we are hovering over one + if (trk && GetLayerModule(GetTrkLayer(trk))) { + if ( (selectMode == 1) && ((MyGetKeyState() & (WKEY_CTRL|WKEY_SHIFT)) != WKEY_CTRL) ) + DoModuleTracks(GetTrkLayer(trk),DrawSingleTrack,!GetTrkSelected(trk)); //Toggle + else + DoModuleTracks(GetTrkLayer(trk),DrawSingleTrack,TRUE); + DrawHighlightLayer(GetTrkLayer(trk)); + } + + //Draw all existing highlight boxes only + DrawHighlightBoxes(FALSE, FALSE, trk); + + // If not on a track, show all tracks as going to be de-selected if selectZero on + if (!trk && selectZero ) { + HighlightSelectedTracks(NULL, TRUE, TRUE); + //Handle the SHIFT+ which means SelectAllConnected case + } else if ( trk && !IsTrackDeleted(trk)) { + if ((MyGetKeyState() & WKEY_SHIFT) ) + SelectConnectedTracks(trk, TRUE); //Highlight all connected + //Normal case - handle track we are hovering over + else { + //Select=Add + if (selectMode == 1) { + if ((MyGetKeyState() & (WKEY_CTRL|WKEY_SHIFT)) == WKEY_CTRL) { + //Only Highlight if adding + if (!GetTrkSelected(trk)) + DrawTrack(trk,&tempD,wDrawColorPreviewSelected); + } else { + if (GetTrkSelected(trk)) + DrawTrack(trk,&tempD,wDrawColorPreviewUnselected); //Toggle + else + DrawTrack(trk,&tempD,wDrawColorPreviewSelected); + } + //Select=Only + } else { + if ((MyGetKeyState() & (WKEY_CTRL|WKEY_SHIFT)) == WKEY_CTRL) { + if (GetTrkSelected(trk)) + DrawTrack(trk,&tempD,wDrawColorPreviewUnselected); //Toggle + else + DrawTrack(trk,&tempD,wDrawColorPreviewSelected); + } else { + //Only Highlight if adding + if (!GetTrkSelected(trk)) + DrawTrack(trk,&tempD,wDrawColorPreviewSelected ); + } + } + } + // Now Highlight the rest of the tracks or Module + if (GetLayerModule(GetTrkLayer(trk))) { + if (selectMode == 1 && ((MyGetKeyState() & (WKEY_CTRL|WKEY_SHIFT)) != WKEY_CTRL) ) + DoModuleTracks(GetTrkLayer(trk),DrawSingleTrack,!GetTrkSelected(trk)); //Toggle + else + DoModuleTracks(GetTrkLayer(trk),DrawSingleTrack,TRUE); + DrawHighlightLayer(GetTrkLayer(trk)); + } else { + //Select=Add + if (selectMode == 1) { + if (((MyGetKeyState() & (WKEY_CTRL|WKEY_SHIFT)) == WKEY_CTRL)) + HighlightSelectedTracks(trk, TRUE, TRUE); + //else + // HighlightSelectedTracks(trk, TRUE, FALSE); Highlight all selected + //Select=Only + } else { + if (((MyGetKeyState() & (WKEY_CTRL|WKEY_SHIFT)) != WKEY_CTRL)) + HighlightSelectedTracks(trk, TRUE, TRUE); + //else + // HighlightSelectedTracks(trk, TRUE, TRUE); Highlight all selected + } + } + } + //Finally add the anchors for any actions or snaps + if (anchors_da.cnt) { + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + } + + return rc; + case C_LCLICK: + if (doingDouble) { + return CallModify(action,pos); + } switch (mode) { case MOVE: - case MOVEDESC: - break; case AREA: - case NONE: - return SelectTrack( pos ); + if (doingAlign) { + rc = CmdRotate (C_DOWN, pos); + rc = CmdRotate (C_UP, pos); + } else + rc = SelectTrack( pos ); + doingRotate = FALSE; + doingMove = FALSE; + return rc; } mode = AREA; break; + case C_LDOUBLE: + if (doingDouble) { + return C_CONTINUE; + } + switch (mode) { + case AREA: + if ((ht = OnTrack(&pos,FALSE,FALSE))!=NULL) { + if (QueryTrack( ht, Q_CAN_MODIFY_CONTROL_POINTS ) || + QueryTrack( ht, Q_IS_CORNU ) || + (QueryTrack( ht, Q_IS_DRAW ) && !QueryTrack( ht, Q_IS_TEXT ))) { + doingDouble = TRUE; + CallModify(C_START,pos); + if (doingDouble == FALSE) return C_CONTINUE; + CallModify(C_LDOUBLE,pos); + } else if (QueryTrack( ht, Q_IS_ACTIVATEABLE)){ + return Activate(pos); + } + } + break; + case MOVE: + default: + break; + } + break; + case C_CMDMENU: + if (doingDouble) { + return CallModify(action,pos); + } + menuPos = pos; if (selectedTrackCount <= 0) { wMenuPopupShow( selectPopup1M ); } else { - coOrd base = pos; track_p trk = OnTrack(&pos, FALSE, FALSE); //Note pollutes pos if turntable - if ((trk) && - QueryTrack(trk,Q_CAN_ADD_ENDPOINTS)) { //Turntable snap to center if within 1/4 radius - trackParams_t trackParams; - if (GetTrackParams(PARAMS_CORNU, trk, pos, &trackParams)) { - DIST_T dist = FindDistance(base, trackParams.ttcenter); - if (dist < trackParams.ttradius/4) { - cmdMenuPos = trackParams.ttcenter; - } - } - } + SetUpMenu2(pos,trk); wMenuPopupShow( selectPopup2M ); } return C_CONTINUE; + case C_TEXT: + if (doingDouble) { + return CallModify(action,pos); + } + if ((action>>8) == 'c') { + panCenter = pos; + LOG( log_pan, 2, ( "PanCenter:Sel-%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)0); + } + if ((action>>8) == 'e') { + DoZoomExtents(0); + } + if ((action>>8) == '0' || (action>>8 == 'o')) { + PanMenuEnter('o'); + } + if ((action>>8) == '?') { + if((moveDescTrk = OnTrack(&pos,FALSE,FALSE)) != NULL) + moveDescPos = pos; + CallPushDescribe((void*)0); + wSetCursor(mainD.d,defaultCursor); + moveDescTrk = NULL; + } + break; + case C_FINISH: + if (doingMove) UndoEnd(); + doingDouble = FALSE; + break; + default: + if (doingDouble) return CallModify(action, pos); } + return C_CONTINUE; } @@ -2029,6 +3256,7 @@ static STATUS_T CmdSelect( #include "bitmaps/select.xpm" #include "bitmaps/delete.xpm" #include "bitmaps/tunnel.xpm" +#include "bitmaps/bridge.xpm" #include "bitmaps/move.xpm" #include "bitmaps/rotate.xpm" #include "bitmaps/flip.xpm" @@ -2044,11 +3272,27 @@ static void SetMoveMode( char * line ) enableMoveDraw = ((tmp&0x10) == 0); } +static void moveDescription( void ) { + if (!moveDescTrk) return; + int hidden = GetTrkBits( moveDescTrk) &TB_HIDEDESC ; + if (hidden) + ClrTrkBits( moveDescTrk, TB_HIDEDESC ); + else + SetTrkBits( moveDescTrk, TB_HIDEDESC ); +} + 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 ); + LEVEL0, IC_CANCEL|IC_POPUP|IC_LCLICK|IC_CMDMENU|IC_WANT_MOVE|IC_WANT_MODKEYS, ACCL_SELECT, NULL ); +} + +extern wIndex_t trainCmdInx; + +EXPORT void InitCmdSelect2( wMenu_p menu ) { + + 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 ); @@ -2058,24 +3302,76 @@ EXPORT void InitCmdSelect( wMenu_p menu ) 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 ); + selectPopup1M = MenuRegister( "Select Mode Menu" ); + wMenuPushCreate(selectPopup1M, "", _("Undo"), 0,(wMenuCallBack_p) UndoUndo, (void *) 0); + wMenuPushCreate(selectPopup1M, "", _("Redo"), 0,(wMenuCallBack_p) UndoRedo, (void *) 0); + wMenuSeparatorCreate( selectPopup1M ); + wMenuPushCreate(selectPopup1M, "cmdDescribeMode", GetBalloonHelpStr("cmdModifyMode"), 0, DoCommandB, (void*) (intptr_t) modifyCmdInx); + wMenuPushCreate(selectPopup1M, "cmdPanMode", GetBalloonHelpStr("cmdPanMode"), 0, DoCommandB, (void*) (intptr_t) panCmdInx); + wMenuPushCreate(selectPopup1M, "cmdTrainMode", GetBalloonHelpStr("cmdTrainMode"), 0, DoCommandB, (void*) (intptr_t) trainCmdInx); + wMenuSeparatorCreate( selectPopup1M ); + wMenuPushCreate(selectPopup1M, "", _("Zoom In"), 0,(wMenuCallBack_p) DoZoomUp, (void*) 1); + wMenuPushCreate( selectPopup1M, "", _("Zoom to extents - 'e'"), 0, (wMenuCallBack_p)DoZoomExtents, (void*) 0); + wMenu_p zoomPop1 = wMenuMenuCreate(selectPopup1M, "", _("&Zoom")); + InitCmdZoom(NULL, NULL, zoomPop1, NULL); + wMenuPushCreate(selectPopup1M, "", _("Zoom Out"), 0, (wMenuCallBack_p) DoZoomDown, (void*) 1); + wMenuPushCreate(selectPopup1M, "", _("Pan to Origin - 'o'/'0'"), 0, (wMenuCallBack_p) PanMenuEnter, (void*) 'o'); + wMenuPushCreate(selectPopup1M, "", _("Pan Center Here - 'c'"), 0, (wMenuCallBack_p) PanHere, (void*) 3); + wMenuSeparatorCreate( selectPopup1M ); + wMenuPushCreate(selectPopup1M, "", _("Select All"), 0,(wMenuCallBack_p) SetAllTrackSelect, (void *) 1); + wMenuPushCreate(selectPopup1M, "",_("Select Current Layer"), 0,(wMenuCallBack_p) SelectCurrentLayer, (void *) 0); + wMenuSeparatorCreate( selectPopup1M ); + + selectPopup2M = MenuRegister( "Track Selected Menu " ); + wMenuPushCreate(selectPopup2M, "", _("Undo"), 0,(wMenuCallBack_p) UndoUndo, (void *) 0); + wMenuPushCreate(selectPopup2M, "", _("Redo"), 0,(wMenuCallBack_p) UndoRedo, (void *) 0); + wMenuSeparatorCreate( selectPopup2M ); + wMenuPushCreate(selectPopup2M, "", _("Zoom In"), 0,(wMenuCallBack_p) DoZoomUp, (void*) 1); + wMenuPushCreate(selectPopup2M, "", _("Zoom Out"), 0, (wMenuCallBack_p) DoZoomDown, (void*) 1); + wMenuPushCreate(selectPopup2M, "", _("Pan Center Here - 'c'"), 0, (wMenuCallBack_p) PanHere, (void*) 3); wMenuSeparatorCreate( selectPopup2M ); + wMenuPushCreate(selectPopup2M, "", _("Deselect All"), 0, (wMenuCallBack_p) SetAllTrackSelect, (void *) 0); + wMenuSeparatorCreate( selectPopup2M ); + wMenuPushCreate(selectPopup2M, "", _("Properties -'?'"), 0,(wMenuCallBack_p) CallPushDescribe, (void*)0); + menuPushModify = wMenuPushCreate(selectPopup2M, "", _("Modify/Activate Track"), 0,(wMenuCallBack_p) CallPushModify, (void*)0); + wMenuSeparatorCreate( selectPopup2M ); + wMenuPushCreate(selectPopup2M, "", _("Cut"), 0,(wMenuCallBack_p) EditCut, (void *) 0); + wMenuPushCreate(selectPopup2M, "", _("Copy"), 0,(wMenuCallBack_p) EditCopy, (void *) 0); + wMenuPushCreate(selectPopup2M, "", _("Paste"), 0, (wMenuCallBack_p) EditPaste, (void *) 0); + wMenuPushCreate(selectPopup2M, "", _("Clone"), 0, (wMenuCallBack_p) EditClone, (void *) 0); AddMoveMenu( selectPopup2M, QuickMove); + selectPopup2RM = wMenuMenuCreate(selectPopup2M, "", _("Rotate...")); + AddRotateMenu( selectPopup2RM, QuickRotate ); + rotateAlignMI = wMenuPushCreate( selectPopup2RM, "", _("Align"), 0, (wMenuCallBack_p)RotateAlign, (void* ) 1 ); + wMenuSeparatorCreate( selectPopup2M ); + descriptionMI = wMenuPushCreate(selectPopup2M, "cmdMoveLabel", _("Show/Hide Description"), 0, (wMenuCallBack_p)moveDescription, (void*) 0); + wMenuSeparatorCreate( selectPopup2M ); + hideMI = wMenuPushCreate(selectPopup2M, "", _("Hide/NoHide"), 0,(wMenuCallBack_p) SelectTunnel, (void *) 0); + bridgeMI = wMenuPushCreate(selectPopup2M, "", _("Bridge/NoBridge"), 0,(wMenuCallBack_p) SelectBridge, (void *) 0); + tiesMI = wMenuPushCreate(selectPopup2M, "", _("NoTies/Ties"), 0,(wMenuCallBack_p) SelectTies, (void *) 0); + selectPopup2TM = wMenuMenuCreate(selectPopup2M, "", _("Thickness...")); + wMenuPushCreate( selectPopup2TM, "", _("Thin Tracks"), 0, (void*)(wMenuCallBack_p)SelectTrackWidth, (void *)0 ); + wMenuPushCreate( selectPopup2TM, "", _("Medium Tracks"), 0, (void*)(wMenuCallBack_p)SelectTrackWidth, (void *)2 ); + wMenuPushCreate( selectPopup2TM, "", _("Thick Tracks"), 0, (void*)(wMenuCallBack_p)SelectTrackWidth, (void *)3 ); + selectPopup2TYM = wMenuMenuCreate( selectPopup2M, "", _("LineType...") ); + wMenuPushCreate( selectPopup2TYM, "", _("Solid Line"), 0, (wMenuCallBack_p)SelectLineType, (void*)0 ); + wMenuPushCreate( selectPopup2TYM, "", _("Dashed Line"), 0, (wMenuCallBack_p)SelectLineType, (void*)1 ); + wMenuPushCreate( selectPopup2TYM, "", _("Dotted Line"), 0, (wMenuCallBack_p)SelectLineType, (void*)2 ); + wMenuPushCreate( selectPopup2TYM, "", _("Dash-Dotted Line"), 0, (wMenuCallBack_p)SelectLineType, (void*)3 ); + wMenuPushCreate( selectPopup2TYM, "", _("Dash-Dot-Dotted Line"), 0, (wMenuCallBack_p)SelectLineType, (void*)4 ); + wMenuSeparatorCreate( selectPopup2M ); + wMenuPushCreate(selectPopup2M, "", _("Move To Front"), 0,(wMenuCallBack_p) SelectAbove,(void *) 0); + wMenuPushCreate(selectPopup2M, "", _("Move To Back"), 0,(wMenuCallBack_p) SelectBelow, (void *) 0); wMenuSeparatorCreate( selectPopup2M ); - AddRotateMenu( selectPopup2M, QuickRotate ); - rotateAlignMI = wMenuPushCreate( selectPopup2M, "", _("Align"), 0, (wMenuCallBack_p)RotateAlign, NULL ); + wMenuPushCreate(selectPopup2M, "", _("Group"), 0,(wMenuCallBack_p) DoGroup, (void *) 0); + wMenuPushCreate(selectPopup2M, "", _("UnGroup"), 0,(wMenuCallBack_p) DoUngroup, (void *) 0); + wMenuSeparatorCreate( selectPopup2M ); + ParamRegister( &rescalePG ); } + EXPORT void InitCmdDelete( void ) { wIcon_p icon; @@ -2091,27 +3387,29 @@ 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 InitCmdBridge( void) +{ + wIcon_p icon; + icon = wIconCreatePixMap( bridge_xpm ); + AddToolbarButton( "cmdBridge", icon, IC_SELECTED|IC_POPUP, (addButtonCallBack_t)SelectBridge, NULL ); } 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 ); + LEVEL0, IC_STICKY|IC_POPUP3|IC_CMDMENU|IC_WANT_MOVE, ACCL_MOVEDESC, (void*) 0 ); } 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 ); + LEVEL0, IC_STICKY|IC_SELECTED|IC_CMDMENU|IC_WANT_MOVE, ACCL_MOVE, NULL ); rotateCmdInx = AddMenuButton( menu, CmdRotate, "cmdRotate", _("Rotate"), wIconCreatePixMap(rotate_xpm), - LEVEL0, IC_STICKY|IC_SELECTED|IC_CMDMENU, ACCL_ROTATE, NULL ); + LEVEL0, IC_STICKY|IC_SELECTED|IC_CMDMENU|IC_WANT_MOVE, 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 index c02cc1c..a9913bf 100644 --- a/app/bin/cselect.h +++ b/app/bin/cselect.h @@ -25,11 +25,9 @@ #include "common.h" #include "track.h" -wIndex_t selectCmdInx; -wIndex_t moveCmdInx; -wIndex_t rotateCmdInx; -long quickMove; -BOOL_T importMove; +extern wIndex_t selectCmdInx; +extern wIndex_t moveCmdInx; +extern wIndex_t rotateCmdInx; extern int incrementalDrawLimit; extern long selectedTrackCount; @@ -37,6 +35,8 @@ void InvertTrackSelect( void * ); void OrphanedTrackSelect( void * ); void SetAllTrackSelect( BOOL_T ); void SelectTunnel( void ); +void SelectBridge( void ); +void SelectTies( void ); void SelectRecount( void ); void SelectTrackWidth( void* ); void SelectDelete( void ); @@ -49,6 +49,6 @@ void DoRefreshCompound( void ); void WriteSelectedTracksToTempSegs( void ); void DoRescale( void ); STATUS_T CmdMoveDescription( wAction_t, coOrd ); -void UpdateQuickMove( void * ); +void DrawHighlightBoxes(BOOL_T, BOOL_T,track_p); #endif diff --git a/app/bin/csensor.c b/app/bin/csensor.c index 871b8d6..4f395c2 100644 --- a/app/bin/csensor.c +++ b/app/bin/csensor.c @@ -58,6 +58,9 @@ static const char rcsid[] = "@(#) : $Id$"; #include "param.h" #include "track.h" #include "trackx.h" +#ifdef WINDOWS +#include "include/utf8convert.h" +#endif // WINDOWS #include "utility.h" #include "messages.h" @@ -246,7 +249,7 @@ static void DescribeSensor (track_p trk, char * str, CSIZE_T len ) *str = tolower((unsigned char)*str); str++; } - sprintf( str, _("(%d [%s]): Layer=%d, at %0.3f,%0.3f"), + sprintf( str, _("(%d [%s]): Layer=%u, at %0.3f,%0.3f"), GetTrkIndex(trk), xx->name,GetTrkLayer(trk)+1, xx->orig.x, xx->orig.y); strncpy(sensorProperties.name,xx->name,STR_SHORT_SIZE-1); @@ -270,14 +273,22 @@ static BOOL_T WriteSensor ( track_p t, FILE * f ) { BOOL_T rc = TRUE; sensorData_p xx = GetsensorData(t); - rc &= fprintf(f, "SENSOR %d %d %s %d %0.6f %0.6f \"%s\" \"%s\"\n", + char *sensorName = MyStrdup(xx->name); + +#ifdef WINDOWS + sensorName = Convert2UTF8(sensorName); +#endif // WINDOWS + + rc &= fprintf(f, "SENSOR %d %u %s %d %0.6f %0.6f \"%s\" \"%s\"\n", GetTrkIndex(t), GetTrkLayer(t), GetTrkScaleName(t), - GetTrkVisible(t), xx->orig.x, xx->orig.y, xx->name, + GetTrkVisible(t), xx->orig.x, xx->orig.y, sensorName, xx->script)>0; + + MyFree(sensorName); return rc; } -static void ReadSensor ( char * line ) +static BOOL_T ReadSensor ( char * line ) { wIndex_t index; /*TRKINX_T trkindex;*/ @@ -291,8 +302,13 @@ static void ReadSensor ( char * line ) wIndex_t layer; sensorData_p xx; if (!GetArgs(line+7,"dLsdpqq",&index,&layer,scale, &visible, &orig,&name,&script)) { - return; + return FALSE; } + +#ifdef WINDOWS + ConvertUTF8ToSystem(name); +#endif // WINDOWS + trk = NewTrack(index, T_SENSOR, 0, sizeof(sensorData_t)); SetTrkVisible(trk, visible); SetTrkScale(trk, LookupScale( scale )); @@ -302,6 +318,7 @@ static void ReadSensor ( char * line ) xx->orig = orig; xx->script = script; ComputeSensorBoundingBox(trk); + return TRUE; } static void MoveSensor (track_p trk, coOrd orig ) @@ -457,23 +474,30 @@ static void CreateNewSensor (coOrd orig) static STATUS_T CmdSensor ( wAction_t action, coOrd pos ) { + static coOrd sensor_pos; + static BOOL_T create; switch (action) { case C_START: InfoMessage(_("Place sensor")); + create = FALSE; return C_CONTINUE; case C_DOWN: + create = TRUE; + /* no break */ case C_MOVE: SnapPos(&pos); - DDrawSensor( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack ); + sensor_pos = pos; return C_CONTINUE; case C_UP: SnapPos(&pos); - DDrawSensor( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack ); CreateNewSensor(pos); return C_TERMINATE; case C_REDRAW: + if (create) + DDrawSensor( &tempD, sensor_pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack ); + return C_CONTINUE; case C_CANCEL: - DDrawSensor( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack ); + create = FALSE; return C_CONTINUE; default: return C_CONTINUE; @@ -491,7 +515,7 @@ static void DrawSensorTrackHilite( void ) w = (wPos_t)((ctlhiliteSize.x/mainD.scale)*mainD.dpi+0.5); h = (wPos_t)((ctlhiliteSize.y/mainD.scale)*mainD.dpi+0.5); mainD.CoOrd2Pix(&mainD,ctlhiliteOrig,&x,&y); - wDrawFilledRectangle( mainD.d, x, y, w, h, ctlhiliteColor, wDrawOptTemp ); + wDrawFilledRectangle( tempD.d, x, y, w, h, ctlhiliteColor, wDrawOptTemp|wDrawOptTransparent ); } static int SensorMgmProc ( int cmd, void * data ) diff --git a/app/bin/csignal.c b/app/bin/csignal.c index fb9bee8..0fc09e6 100644 --- a/app/bin/csignal.c +++ b/app/bin/csignal.c @@ -59,6 +59,9 @@ static const char rcsid[] = "@(#) : $Id$"; #include "param.h" #include "track.h" #include "trackx.h" +#ifdef WINDOWS +#include "include/utf8convert.h" +#endif // WINDOWS #include "utility.h" #include "messages.h" @@ -308,7 +311,7 @@ static void DescribeSignal (track_p trk, char * str, CSIZE_T len ) *str = tolower((unsigned char)*str); str++; } - sprintf( str, _("(%d [%s]): Layer=%d, %d heads at %0.3f,%0.3f A%0.3f"), + sprintf( str, _("(%d [%s]): Layer=%u, %d heads at %0.3f,%0.3f A%0.3f"), GetTrkIndex(trk), xx->name,GetTrkLayer(trk)+1, xx->numHeads, xx->orig.x, xx->orig.y,xx->angle ); @@ -338,20 +341,28 @@ static BOOL_T WriteSignal ( track_p t, FILE * f ) BOOL_T rc = TRUE; wIndex_t ia; signalData_p xx = GetsignalData(t); - rc &= fprintf(f, "SIGNAL %d %d %s %d %0.6f %0.6f %0.6f %d \"%s\"\n", + char *signalName = MyStrdup(xx->name); + +#ifdef WINDOWS + signalName = Convert2UTF8(signalName); +#endif // WINDOWS + + rc &= fprintf(f, "SIGNAL %d %u %s %d %0.6f %0.6f %0.6f %d \"%s\"\n", GetTrkIndex(t), GetTrkLayer(t), GetTrkScaleName(t), GetTrkVisible(t), xx->orig.x, xx->orig.y, xx->angle, - xx->numHeads, xx->name)>0; + xx->numHeads, signalName)>0; for (ia = 0; ia < xx->numAspects; ia++) { rc &= fprintf(f, "\tASPECT \"%s\" \"%s\"\n", (&(xx->aspectList))[ia].aspectName, (&(xx->aspectList))[ia].aspectScript)>0; } - rc &= fprintf( f, "\tEND\n" )>0; + rc &= fprintf( f, "\t%s\n",END_SIGNAL )>0; + + MyFree(signalName); return rc; } -static void ReadSignal ( char * line ) +static BOOL_T ReadSignal ( char * line ) { /*TRKINX_T trkindex;*/ wIndex_t index; @@ -369,19 +380,24 @@ static void ReadSignal ( char * line ) signalData_p xx; if (!GetArgs(line+6,"dLsdpfdq",&index,&layer,scale, &visible, &orig, &angle, &numHeads,&name)) { - return; + return FALSE; } + +#ifdef WINDOWS + ConvertUTF8ToSystem(name); +#endif // WINDOWS + DYNARR_RESET( signalAspect_p, signalAspect_da ); while ( (cp = GetNextLine()) != NULL ) { - while (isspace((unsigned char)*cp)) cp++; - if ( strncmp( cp, "END", 3 ) == 0 ) { + if ( IsEND( END_SIGNAL) ) { break; } + while (isspace((unsigned char)*cp)) cp++; if ( *cp == '\n' || *cp == '#' ) { continue; } if ( strncmp( cp, "ASPECT", 6 ) == 0 ) { - if (!GetArgs(cp+4,"qq",&aspname,&aspscript)) return; + if (!GetArgs(cp+4,"qq",&aspname,&aspscript)) return FALSE; DYNARR_APPEND( signalAspect_p *, signalAspect_da, 10 ); signalAspect(signalAspect_da.cnt-1).aspectName = aspname; signalAspect(signalAspect_da.cnt-1).aspectScript = aspscript; @@ -402,6 +418,7 @@ static void ReadSignal ( char * line ) (&(xx->aspectList))[ia].aspectScript = signalAspect(ia).aspectScript; } ComputeSignalBoundingBox(trk); + return TRUE; } static void MoveSignal (track_p trk, coOrd orig ) @@ -772,20 +789,21 @@ static ANGLE_T orient; static STATUS_T CmdSignal ( wAction_t action, coOrd pos ) { - + static BOOL_T create; switch (action) { case C_START: InfoMessage(_("Place base of signal")); + create = FALSE; return C_CONTINUE; case C_DOWN: SnapPos(&pos); pos0 = pos; + create = TRUE; InfoMessage(_("Drag to orient signal")); return C_CONTINUE; case C_MOVE: SnapPos(&pos); orient = FindAngle(pos0,pos); - DDrawSignal( &tempD, pos0, orient, 1, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack ); return C_CONTINUE; case C_UP: SnapPos(&pos); @@ -793,8 +811,11 @@ static STATUS_T CmdSignal ( wAction_t action, coOrd pos ) CreateNewSignal(pos0,orient); return C_TERMINATE; case C_REDRAW: + if (create) + DDrawSignal( &tempD, pos0, orient, 1, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack ); + return C_CONTINUE; case C_CANCEL: - DDrawSignal( &tempD, pos0, orient, 1, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack ); + create = FALSE; return C_CONTINUE; default: return C_CONTINUE; @@ -812,7 +833,7 @@ static void DrawSignalTrackHilite( void ) w = (wPos_t)((sighiliteSize.x/mainD.scale)*mainD.dpi+0.5); h = (wPos_t)((sighiliteSize.y/mainD.scale)*mainD.dpi+0.5); mainD.CoOrd2Pix(&mainD,sighiliteOrig,&x,&y); - wDrawFilledRectangle( mainD.d, x, y, w, h, sighiliteColor, wDrawOptTemp ); + wDrawFilledRectangle( tempD.d, x, y, w, h, sighiliteColor, wDrawOptTemp|wDrawOptTransparent ); } static int SignalMgmProc ( int cmd, void * data ) diff --git a/app/bin/csnap.c b/app/bin/csnap.c index 4c4d948..eb58bc4 100644 --- a/app/bin/csnap.c +++ b/app/bin/csnap.c @@ -209,7 +209,7 @@ EXPORT void DrawGrid( cross0_bm = wDrawBitMapCreate( mainD.d, cross0_width, cross0_height, 2, 2, cross0_bits ); #endif - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, wCursorWait ); dpi = D->dpi/D->scale; Gdx = cos(D2R(Gangle)); Gdy = sin(D2R(Gangle)); @@ -329,7 +329,7 @@ EXPORT void DrawGrid( done: - wSetCursor( wCursorNormal ); + wSetCursor( mainD.d, defaultCursor ); } @@ -368,45 +368,41 @@ EXPORT STATUS_T GridAction( 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; + + case C_REDRAW: + DrawBigCross( *orig, *angle ); + break; } return C_CONTINUE; } @@ -564,8 +560,7 @@ 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 ); + MainRedraw(); // RedrawGrid wDrawDelayUpdate( tempD.d, FALSE ); } } @@ -674,12 +669,9 @@ static void GridDlgUpdate( 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 ); + MainRedraw(); // GridDlgUpdate } } @@ -688,9 +680,8 @@ 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; + grid.Angle += angle/1000; oldGrid = grid; DrawASnapGrid( &grid, &tempD, mapD.size, TRUE ); wDrawDelayUpdate( tempD.d, FALSE ); @@ -719,12 +710,12 @@ EXPORT STATUS_T CmdGrid( return C_CONTINUE; case C_REDRAW: - return C_TERMINATE; + DrawBigCross( grid.Orig, grid.Angle ); + return C_CONTINUE; case C_CANCEL: grid = oldGrid; wHide( gridW ); - MainRedraw(); return C_TERMINATE; case C_OK: @@ -767,6 +758,7 @@ EXPORT STATUS_T CmdGrid( return rc; case C_CMDMENU: + menuPos = pos; wMenuPopupShow( snapGridPopupM ); break; } diff --git a/app/bin/csplit.c b/app/bin/csplit.c index 6cfdcc8..c2b516a 100644 --- a/app/bin/csplit.c +++ b/app/bin/csplit.c @@ -21,16 +21,20 @@ */ #include "cundo.h" +#include "compound.h" #include "i18n.h" #include "messages.h" #include "track.h" #include "utility.h" +#include "fileio.h" 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 dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) static void ChangeSplitEPMode( wBool_t set, void * mode ) { @@ -55,6 +59,45 @@ static void ChangeSplitEPMode( wBool_t set, void * mode ) DrawEndPt( &mainD, splitTrkTrk[1], splitTrkEP[1], wDrawColorBlack ); } +static void CreateSplitAnchorAngle(coOrd pos, track_p t, BOOL_T end, ANGLE_T a) { + DIST_T d = tempD.scale*0.1; + DIST_T w = tempD.scale/tempD.dpi*4; + int i; + if (!end) { + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).color = wDrawColorBlue; + Translate(&anchors(i).u.l.pos[0],pos,a,GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[1],pos,a,-GetTrkGauge(t)); + anchors(i).width = w; + } else { + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).color = wDrawColorBlue; + Translate(&anchors(i).u.l.pos[0],pos,a,GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[0],anchors(i).u.l.pos[0],a+90,d); + Translate(&anchors(i).u.l.pos[1],pos,a,-GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[1],anchors(i).u.l.pos[1],a+90,-d); + anchors(i).width = w; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).color = wDrawColorBlue; + Translate(&anchors(i).u.l.pos[0],pos,a,GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[0],anchors(i).u.l.pos[0],a+90,-d); + Translate(&anchors(i).u.l.pos[1],pos,a,-GetTrkGauge(t)); + Translate(&anchors(i).u.l.pos[1],anchors(i).u.l.pos[1],a+90,d); + anchors(i).width = w; + } +} + +static void CreateSplitAnchor(coOrd pos, track_p t, BOOL_T end) { + ANGLE_T a = NormalizeAngle(GetAngleAtPoint(t,pos,NULL,NULL)+90.0); + CreateSplitAnchorAngle(pos,t,end,a); +} + static STATUS_T CmdSplitTrack( wAction_t action, coOrd pos ) { track_p trk0, trk1; @@ -66,6 +109,7 @@ static STATUS_T CmdSplitTrack( wAction_t action, coOrd pos ) switch (action) { case C_START: InfoMessage( _("Select track to split") ); + DYNARR_RESET(trkSeg_t,anchors_da); /* no break */ case C_DOWN: case C_MOVE: @@ -79,12 +123,16 @@ static STATUS_T CmdSplitTrack( wAction_t action, coOrd pos ) onTrackInSplit = FALSE; return C_TERMINATE; } - if (!QueryTrack(trk0,Q_MODIFY_CAN_SPLIT)) { - onTrackInSplit = FALSE; - InfoMessage(_("Can't Split that Track")); - return C_CONTINUE; - } ep0 = PickEndPoint( pos, trk0 ); + if (IsClose(FindDistance(GetTrkEndPos(trk0,ep0),pos)) && (GetTrkEndTrk(trk0,ep0)!=NULL)) { + pos = GetTrkEndPos(trk0,ep0); + } else { + if (!QueryTrack(trk0,Q_MODIFY_CAN_SPLIT)) { + onTrackInSplit = FALSE; + InfoMessage(_("Can't Split that Track")); + return C_CONTINUE; + } + } onTrackInSplit = FALSE; if (ep0 < 0) { return C_CONTINUE; @@ -139,9 +187,38 @@ static STATUS_T CmdSplitTrack( wAction_t action, coOrd pos ) mode |= 1; for ( inx=0; inx<4; inx++ ) wMenuToggleSet( splitPopupMI[quad&1][inx], mode == inx ); + menuPos = pos; wMenuPopupShow( splitPopupM[quad&1] ); break; + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + onTrackInSplit = TRUE; + if ((trk0 = OnTrack( &pos, FALSE, TRUE ))!=NULL && CheckTrackLayerSilent( trk0 )) { + ep0 = PickEndPoint( pos, trk0 ); + if (IsClose(FindDistance(GetTrkEndPos(trk0,ep0),pos)) && (GetTrkEndTrk(trk0,ep0)!=NULL)) { + CreateSplitAnchor(GetTrkEndPos(trk0,ep0),trk0,TRUE); + } else if (QueryTrack(trk0,Q_IS_TURNOUT)) { + if ((MyGetKeyState()&WKEY_SHIFT) != 0 ) { + if (SplitTurnoutCheck(trk0,pos,ep0,NULL,NULL,NULL,TRUE,&pos,&angle)) { + angle = NormalizeAngle(angle+90); + CreateSplitAnchorAngle(pos,trk0,FALSE,angle); + } + } else { + CreateSplitAnchor(GetTrkEndPos(trk0,ep0),trk0,TRUE); + } + break; + } else if (QueryTrack(trk0,Q_MODIFY_CAN_SPLIT)) { + CreateSplitAnchor(pos,trk0,FALSE); + } + } + onTrackInSplit = FALSE; + break; + case C_REDRAW: + if (anchors_da.cnt) + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + break; } + return C_CONTINUE; } @@ -152,6 +229,6 @@ static STATUS_T CmdSplitTrack( wAction_t action, coOrd pos ) 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 ); + AddMenuButton( menu, CmdSplitTrack, "cmdSplitTrack", _("Split Track"), wIconCreatePixMap(splittrk_xpm), LEVEL0_50, IC_STICKY|IC_POPUP|IC_CMDMENU|IC_WANT_MOVE, ACCL_SPLIT, NULL ); } diff --git a/app/bin/cstraigh.c b/app/bin/cstraigh.c index 7be25ee..464f16e 100644 --- a/app/bin/cstraigh.c +++ b/app/bin/cstraigh.c @@ -29,6 +29,7 @@ #include "param.h" #include "track.h" #include "utility.h" +#include "layout.h" /* * STATE INFO @@ -40,6 +41,23 @@ static struct { BOOL_T down; } Dl; +static dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) + +static void CreateEndAnchor(coOrd p, wBool_t lock) { + DIST_T d = tempD.scale*0.15; + + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = lock?SEG_FILCRCL:SEG_CRVLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.c.center = p; + anchors(i).u.c.radius = d/2; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).width = 0; +} + static STATUS_T CmdStraight( wAction_t action, coOrd pos ) { @@ -47,40 +65,37 @@ static STATUS_T CmdStraight( wAction_t action, coOrd pos ) DIST_T dist; coOrd p; - switch (action) { + switch (action&0xFF) { case C_START: + DYNARR_RESET(trkSeg_t,anchors_da); Dl.pos0=pos; Dl.pos1=pos; Dl.trk = NULL; Dl.ep=-1; Dl.down = FALSE; - InfoMessage( _("Place 1st end point of straight track + Shift -> snap to unconnected endpoint") ); + InfoMessage( _("Place 1st endpoint of straight track, snap to unconnected endpoint") ); return C_CONTINUE; case C_DOWN: + DYNARR_RESET(trkSeg_t,anchors_da); p = pos; BOOL_T found = FALSE; Dl.trk = NULL; - if ((MyGetKeyState() & WKEY_SHIFT) != 0) { + if (((MyGetKeyState() & WKEY_ALT) == 0) == magneticSnap) { if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) { EPINX_T ep = PickUnconnectedEndPointSilent(p, t); if (ep != -1) { + if (GetTrkGauge(t) != GetScaleTrackGauge(GetLayoutCurScale())) { + wBeep(); + InfoMessage(_("Track is different gauge")); + return C_CONTINUE; + } Dl.trk = t; Dl.ep = ep; pos = GetTrkEndPos(t, ep); found = TRUE; - } else { - InfoMessage(_("No unconnected end-point on track - Try again or release Shift and click")); - Dl.pos0=pos; - Dl.pos1=pos; - return C_CONTINUE; } - } else { - InfoMessage(_("Not on a track - Try again or release Shift and click")); - Dl.pos0=pos; - Dl.pos1=pos; - return C_CONTINUE; } } Dl.down = TRUE; @@ -96,10 +111,25 @@ static STATUS_T CmdStraight( wAction_t action, coOrd pos ) return C_CONTINUE; case C_MOVE: - if (!Dl.down) return C_CONTINUE; - DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + if (!Dl.down) { + if (((MyGetKeyState() & WKEY_ALT) == 0) == magneticSnap) { + p = pos; + if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) { + if (GetTrkGauge(t) == GetScaleTrackGauge(GetLayoutCurScale())) { + EPINX_T ep = PickUnconnectedEndPointSilent(pos, t); + if (ep != -1) { + if (GetTrkGauge(t) == GetScaleTrackGauge(GetLayoutCurScale())) + CreateEndAnchor(GetTrkEndPos(t,ep),FALSE); + } + } + } + } + return C_CONTINUE; + } ANGLE_T angle, angle2; - if (Dl.trk) { + if (Dl.trk && !(MyGetKeyState() & WKEY_SHIFT)) { angle = NormalizeAngle(GetTrkEndAngle( Dl.trk, Dl.ep)); angle2 = NormalizeAngle(FindAngle(pos, Dl.pos0)-angle); if (angle2 > 90.0 && angle2 < 270.0) @@ -112,14 +142,13 @@ static STATUS_T CmdStraight( wAction_t action, coOrd 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: + DYNARR_RESET(trkSeg_t,anchors_da); if (!Dl.down) return C_CONTINUE; - DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); tempSegs_da.cnt = 0; - if (Dl.trk) { + if (Dl.trk && !(MyGetKeyState() & WKEY_SHIFT)) { angle = NormalizeAngle(GetTrkEndAngle( Dl.trk, Dl.ep)); angle2 = NormalizeAngle(FindAngle(pos, Dl.pos0)-angle); if (angle2 > 90.0 && angle2 < 270.0) @@ -132,7 +161,7 @@ static STATUS_T CmdStraight( wAction_t action, coOrd pos ) } UndoStart( _("Create Straight Track"), "newStraight" ); t = NewStraightTrack( Dl.pos0, pos ); - if (Dl.trk) { + if (Dl.trk && !(MyGetKeyState() & WKEY_SHIFT)) { ConnectTracks(Dl.trk, Dl.ep, t, 0); } UndoEnd(); @@ -140,8 +169,12 @@ static STATUS_T CmdStraight( wAction_t action, coOrd pos ) return C_TERMINATE; case C_REDRAW: + if (anchors_da.cnt) + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + if (Dl.down) + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + return C_CONTINUE; case C_CANCEL: - DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); Dl.down = FALSE; return C_CONTINUE; @@ -155,5 +188,5 @@ static STATUS_T CmdStraight( wAction_t action, coOrd pos ) void InitCmdStraight( wMenu_p menu ) { - AddMenuButton( menu, CmdStraight, "cmdStraight", _("Straight Track"), wIconCreatePixMap(straight_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_STRAIGHT, NULL ); + AddMenuButton( menu, CmdStraight, "cmdStraight", _("Straight Track"), wIconCreatePixMap(straight_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2|IC_WANT_MOVE, ACCL_STRAIGHT, NULL ); } diff --git a/app/bin/cstruct.c b/app/bin/cstruct.c index 6ad240c..6907e2c 100644 --- a/app/bin/cstruct.c +++ b/app/bin/cstruct.c @@ -33,13 +33,13 @@ #include "layout.h" #include "messages.h" #include "param.h" +#include "include/paramfile.h" #include "track.h" #include "utility.h" +#include "ccurve.h" EXPORT TRKTYP_T T_STRUCTURE = -1; -#define STRUCTCMD - EXPORT dynArr_t structureInfo_da; typedef struct compoundData extraData; @@ -51,7 +51,6 @@ static int log_structure = 0; static wMenu_p structPopupM; -#ifdef STRUCTCMD static drawCmd_t structureD = { NULL, &screenDrawFuncs, @@ -86,7 +85,7 @@ static paramData_t structurePLs[] = { #define I_MSGHEIGHT (5) { PD_MESSAGE, NULL, NULL, 0, (void*)80 } }; static paramGroup_t structurePG = { "structure", 0, structurePLs, sizeof structurePLs/sizeof structurePLs[0] }; -#endif + /**************************************** @@ -133,24 +132,103 @@ EXPORT turnoutInfo_t * CreateNewStructure( #endif to->paramFileIndex = curParamFileIndex; if (curParamFileIndex == PARAM_CUSTOM) - to->contentsLabel = "Custom Structures"; + to->contentsLabel = MyStrdup("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; } +/** + * Delete a structure definition from memory. + * \TODO Find a better way to handle Custom Structures (see CreateNewStructure) + * + * \param [IN] structure the structure to be deleted + */ + +BOOL_T +StructureDelete(void *structure) +{ + turnoutInfo_t * to = (turnoutInfo_t *)structure; + MyFree(to->title); + MyFree(to->segs); + + MyFree(to); + return(TRUE); +} + +/** + * Delete all structure definitions that came from a specific parameter + * file. Due to the way the definitions are loaded from file it is safe to + * assume that they form a contiguous block in the array. + * + * \param fileIndex parameter file. + */ + +void +DeleteStructures(int fileIndex) +{ + int inx = 0; + int startInx = -1; + int cnt = 0; + + // go to the start of the block + while (inx < structureInfo_da.cnt && + structureInfo(inx)->paramFileIndex != fileIndex) { + startInx = inx++; + } + + // delete them + for (; inx < structureInfo_da.cnt && + structureInfo(inx)->paramFileIndex == fileIndex; inx++) { + turnoutInfo_t * to = structureInfo(inx); + if (to->paramFileIndex == fileIndex) { + StructureDelete(to); + cnt++; + } + } + + // copy down the rest of the list to fill the gap + startInx++; + while (inx < structureInfo_da.cnt) { + structureInfo(startInx++) = structureInfo(inx++); + } + + // and reduce the actual number + structureInfo_da.cnt -= cnt; +} + +enum paramFileState +GetStructureCompatibility(int paramFileIndex, SCALEINX_T scaleIndex) +{ + int i; + enum paramFileState ret = PARAMFILE_NOTUSABLE; + DIST_T ratio = GetScaleRatio(scaleIndex); + + if (!IsParamValid(paramFileIndex)) { + return(PARAMFILE_UNLOADED); + } + + for (i = 0; i < structureInfo_da.cnt; i++) { + turnoutInfo_t *to = structureInfo(i); + if (to->paramFileIndex == paramFileIndex) { + if (GetScaleRatio(to->scaleInx) == ratio || to->scaleInx == SCALE_ANY) { + ret = PARAMFILE_FIT; + break; + } + } + } + return(ret); +} static BOOL_T ReadStructureParam( char * firstLine ) @@ -164,7 +242,8 @@ static dynArr_t pierInfo_da; if ( !GetArgs( firstLine+10, "sq", scale, &title ) ) return FALSE; - ReadSegs(); + if ( !ReadSegs() ) + return FALSE; to = CreateNewStructure( scale, title, tempSegs_da.cnt, &tempSegs(0), FALSE ); if (to == NULL) return FALSE; @@ -175,7 +254,8 @@ static dynArr_t pierInfo_da; 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 ); + if ( !GetArgs( cp, "fqc", &pierInfo(pierInfo_da.cnt-1).height, &pierInfo(pierInfo_da.cnt-1).name, &cp ) ) + return FALSE; } to->u.pierInfo.cnt = pierInfo_da.cnt; to->u.pierInfo.info = (pierInfo_t*)MyMalloc( pierInfo_da.cnt * sizeof *(pierInfo_t*)NULL ); @@ -196,6 +276,7 @@ EXPORT turnoutInfo_t * StructAdd( long mode, SCALEINX_T scale, wList_p list, coO { wIndex_t inx; turnoutInfo_t * to, *to1=NULL; + structureInx = 0; for ( inx = 0; inx < structureInfo_da.cnt; inx++ ) { to = structureInfo(inx); if ( IsParamValid(to->paramFileIndex) && @@ -204,6 +285,10 @@ EXPORT turnoutInfo_t * StructAdd( long mode, SCALEINX_T scale, wList_p list, coO to->segCnt != 0 ) { if (to1 == NULL) to1 = to; + if ( to == curStructure ) { + to1 = to; + structureInx = wListGetCount( list ); + } FormatCompoundTitle( mode, to->title ); if (message[0] != '\0') { wListAddValue( list, message, NULL, to ); @@ -232,38 +317,45 @@ static void DrawStructure( 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 ); - } + + d->options &= ~DC_NOTSOLIDLINE; + switch(xx->lineType) { + case DRAWLINESOLID: + break; + case DRAWLINEDASH: + d->options |= DC_DASH; + break; + case DRAWLINEDOT: + d->options |= DC_DOT; + break; + case DRAWLINEDASHDOT: + d->options |= DC_DASHDOT; + break; + case DRAWLINEDASHDOTDOT: + d->options |= DC_DASHDOTDOT; + break; + case DRAWLINECENTER: + d->options |= DC_CENTER; + break; + case DRAWLINEPHANTOM: + d->options |= DC_CENTER; + break; + } + DrawSegs( d, xx->orig, xx->angle, xx->segs, xx->segCnt, 0.0, color ); + d->options &= ~DC_NOTSOLIDLINE; + if ( ((d->options & DC_SIMPLE)==0) && + (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) && + labelScale >= d->scale && + ( GetTrkBits( t ) & TB_HIDEDESC ) == 0 ) { + DrawCompoundDescription( t, d, color ); } } -static void ReadStructure( +static BOOL_T ReadStructure( char * line ) { - ReadCompound( line+10, T_STRUCTURE ); + return ReadCompound( line+10, T_STRUCTURE ); } @@ -291,12 +383,31 @@ static BOOL_T QueryStructure( track_p trk, int query ) switch ( query ) { case Q_HAS_DESC: return TRUE; + case Q_IS_STRUCTURE: + return TRUE; default: return FALSE; } } +static wBool_t CompareStruct( track_cp trk1, track_cp trk2 ) +{ + struct extraData *xx1 = GetTrkExtraData( trk1 ); + struct extraData *xx2 = GetTrkExtraData( trk2 ); + char * cp = message + strlen(message); + REGRESS_CHECK_POS( "Orig", xx1, xx2, orig ) + REGRESS_CHECK_ANGLE( "Angle", xx1, xx2, angle ) + REGRESS_CHECK_INT( "Flipped", xx1, xx2, flipped ) + REGRESS_CHECK_INT( "Ungrouped", xx1, xx2, ungrouped ) + REGRESS_CHECK_INT( "Split", xx1, xx2, split ) + /* desc orig is not stable + REGRESS_CHECK_POS( "DescOrig", xx1, xx2, descriptionOrig ) */ + REGRESS_CHECK_POS( "DescOff", xx1, xx2, descriptionOff ) + REGRESS_CHECK_POS( "DescSize", xx1, xx2, descriptionSize ) + return CompareSegs( xx1->segs, xx1->segCnt, xx1->segs, xx1->segCnt ); +} + static trackCmd_t structureCmds = { "STRUCTURE", DrawStructure, @@ -322,7 +433,17 @@ static trackCmd_t structureCmds = { NULL, /* moveEndPt */ QueryStructure, UngroupCompound, - FlipCompound }; + FlipCompound, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + CompareStruct }; static paramData_t pierPLs[] = { { PD_DROPLIST, &pierListInx, "inx", 0, (void*)50, N_("Pier Number") } }; @@ -360,7 +481,6 @@ static void ShowPierL( void ) } -#ifdef STRUCTCMD /***************************************** * * Structure Dialog @@ -404,13 +524,14 @@ static void structureChange( long changes ) (changes&CHANGE_PARAMS) == 0 ) ) return; lastScaleName = curScaleName; - curStructure = NULL; + //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, GetLayoutCurScale(), structureListL, &maxStructureDim ); + wListSetIndex( structureListL, structureInx ); wControlShow( (wControl_p)structureListL, TRUE ); if (curStructure == NULL) { wDrawClear( structureD.d ); @@ -468,7 +589,7 @@ static void DoStructOk( void ) Reset(); } -#endif + /**************************************** * @@ -488,6 +609,51 @@ static struct { static track_p pierTrk; static EPINX_T pierEp; +static dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) + +void static CreateArrowAnchor(coOrd pos,ANGLE_T a,DIST_T len) { + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).width = 0; + anchors(i).u.l.pos[0] = pos; + Translate(&anchors(i).u.l.pos[1],pos,NormalizeAngle(a+135),len); + anchors(i).color = wDrawColorBlue; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).width = 0; + anchors(i).u.l.pos[0] = pos; + Translate(&anchors(i).u.l.pos[1],pos,NormalizeAngle(a-135),len); + anchors(i).color = wDrawColorBlue; +} + +void static CreateRotateAnchor(coOrd pos) { + DIST_T d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = SEG_CRVLIN; + anchors(i).width = 0.5; + anchors(i).u.c.center = pos; + anchors(i).u.c.a0 = 180.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).u.c.radius = d*2; + anchors(i).color = wDrawColorAqua; + coOrd head; //Arrows + for (int j=0;j<3;j++) { + Translate(&head,pos,j*120,d*2); + CreateArrowAnchor(head,NormalizeAngle((j*120)+90),d); + } +} + +void static CreateMoveAnchor(coOrd pos) { + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),pos,0,TRUE,wDrawColorBlue); + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),pos,90,TRUE,wDrawColorBlue); +} + static ANGLE_T PlaceStructure( coOrd p0, coOrd p1, @@ -537,11 +703,9 @@ static void NewStructure( void ) 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 ); + trk = NewCompound( T_STRUCTURE, 0, Dst.pos, Dst.angle, curStructure->title, 0, NULL, NULL, 0, "", curStructure->segCnt, curStructure->segs ); xx = GetTrkExtraData(trk); #ifdef LATER trk = NewTrack( 0, T_STRUCTURE, 0, sizeof (*xx) + 1 ); @@ -581,6 +745,8 @@ static void NewStructure( void ) } SetTrkVisible( trk, TRUE ); + SetTrkNoTies( trk, FALSE); + SetTrkBridge( trk, FALSE); #ifdef LATER ComputeCompoundBoundingBox( trk ); @@ -600,17 +766,14 @@ static void NewStructure( void ) static void StructRotate( void * pangle ) { + if (Dst.state == 0) + return; 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; + angle /= 1000.0; + 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; + TempRedraw(); // StructRotate } @@ -629,64 +792,62 @@ EXPORT STATUS_T CmdStructureAction( switch (action & 0xFF) { case C_START: + DYNARR_RESET(trkSeg_t,anchors_da); Dst.state = 0; Dst.angle = 00.0; ShowPierL(); + InfoMessage(_("Left-Drag to place, Ctrl+Left-Drag or Right-Drag to Rotate, Space or Enter to accept, Esc to Cancel")); return C_CONTINUE; + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + if (Dst.state && (MyGetKeyState()&WKEY_CTRL)) { + CreateRotateAnchor(pos); + } else { + CreateMoveAnchor(pos); + } + return C_CONTINUE; + break; + case C_DOWN: + DYNARR_RESET(trkSeg_t,anchors_da); 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; - } + 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") ); + CreateMoveAnchor(pos); return C_CONTINUE; case C_MOVE: + DYNARR_RESET(trkSeg_t,anchors_da); 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(); - MapRedraw(); + CreateMoveAnchor(pos); InfoMessage( "[ %0.3f %0.3f ]", pos.x - origPos.x, pos.y - origPos.y ); return C_CONTINUE; case C_RDOWN: + DYNARR_RESET(trkSeg_t,anchors_da); 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; + if (Dst.state == 0) { + Dst.pos = pos; // If first, use pos, otherwise use current + } 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 ); + CreateRotateAnchor(pos); origPos = Dst.pos; origAngle = Dst.angle; InfoMessage( _("Drag to rotate") ); + Dst.state = 2; validAngle = FALSE; return C_CONTINUE; case C_RMOVE: + DYNARR_RESET(trkSeg_t,anchors_da); 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 ); @@ -700,36 +861,37 @@ EXPORT STATUS_T CmdStructureAction( 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 ); + Dst.state = 2; + CreateRotateAnchor(rot0); return C_CONTINUE; case C_RUP: - DrawLine( &tempD, rot0, rot1, 0, wDrawColorBlack ); case C_UP: - MainRedraw(); - MapRedraw(); + DYNARR_RESET(trkSeg_t,anchors_da); + CreateMoveAnchor(pos); + Dst.state = 1; + InfoMessage(_("Left-Drag to place, Ctrl+Left-Drag or Right-Drag to Rotate, Space or Enter to accept, Esc to Cancel")); return C_CONTINUE; case C_CMDMENU: - if ( structPopupM == NULL ) { - structPopupM = MenuRegister( "Structure Rotate" ); - AddRotateMenu( structPopupM, StructRotate ); - } + DYNARR_RESET(trkSeg_t,anchors_da); + menuPos = pos; wMenuPopupShow( structPopupM ); return C_CONTINUE; case C_REDRAW: - if (Dst.state == 1) + if (Dst.state) DrawSegs( &tempD, Dst.pos, Dst.angle, - curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack ); + curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlue ); + if (anchors_da.cnt>0) { + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + } + if (Dst.state == 2) + DrawLine( &tempD, rot0, rot1, 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 ); + DYNARR_RESET(trkSeg_t,anchors_da); Dst.state = 0; InfoSubstituteControls( NULL, NULL ); HotBarCancel(); @@ -739,12 +901,15 @@ EXPORT STATUS_T CmdStructureAction( case C_TEXT: if ((action>>8) != ' ') return C_CONTINUE; + /*no break*/ case C_OK: + DYNARR_RESET(trkSeg_t,anchors_da); NewStructure(); InfoSubstituteControls( NULL, NULL ); return C_TERMINATE; case C_FINISH: + DYNARR_RESET(trkSeg_t,anchors_da); if (Dst.state != 0) CmdStructureAction( C_OK, pos ); else @@ -791,17 +956,37 @@ static STATUS_T CmdStructure( ParamGroupRecord( &structurePG ); return CmdStructureAction( action, pos ); + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + if (Dst.state && (MyGetKeyState()&WKEY_CTRL)) { + CreateRotateAnchor(pos); + } else { + CreateMoveAnchor(pos); + } + return C_CONTINUE; + break; case C_DOWN: + if (MyGetKeyState()&WKEY_CTRL) { + return CmdStructureAction( C_RDOWN, pos ); + } + /* no break*/ case C_RDOWN: ParamDialogOkActive( &structurePG, TRUE ); if (hideStructureWindow) wHide( structureW ); + return CmdStructureAction( action, pos ); case C_MOVE: + if (MyGetKeyState()&WKEY_CTRL) { + return CmdStructureAction( C_RMOVE, pos ); + } + /*no break*/ case C_RMOVE: return CmdStructureAction( action, pos ); - case C_RUP: case C_UP: + if (MyGetKeyState()&WKEY_CTRL) { + return CmdStructureAction( C_RUP, pos ); + } if (hideStructureWindow) wShow( structureW ); InfoMessage( _("Left drag to move, right drag to rotate, or press Return or click Ok to finalize") ); @@ -810,11 +995,12 @@ static STATUS_T CmdStructure( case C_CANCEL: wHide( structureW ); + /*no break*/ + case C_REDRAW: case C_TEXT: case C_OK: case C_FINISH: case C_CMDMENU: - case C_REDRAW: return CmdStructureAction( action, pos ); default: @@ -848,6 +1034,8 @@ static char * CmdStructureHotBarProc( case HB_FULLTITLE: return to->title; case HB_DRAW: + origP->x -= to->orig.x; + origP->y -= to->orig.y; DrawSegs( d, *origP, 0.0, to->segs, to->segCnt, trackGauge, wDrawColorBlack ); return NULL; } @@ -867,7 +1055,7 @@ EXPORT void AddHotBarStructures( void ) /*( (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 ); + AddHotBarElement( to->contentsLabel, to->size, to->orig, FALSE, FALSE, to->barScale, to, CmdStructureHotBarProc ); } } @@ -885,47 +1073,72 @@ static STATUS_T CmdStructureHotBar( } FormatCompoundTitle( listLabels|LABEL_DESCR, curStructure->title ); InfoMessage( _("Place %s and draw into position"), message ); - ParamLoadControls( &structurePG ); - ParamGroupRecord( &structurePG ); + wIndex_t listIndex = FindListItemByContext( structureListL, curStructure ); + if ( listIndex > 0 ) + structureInx = listIndex; + //ParamLoadControls( &structurePG ); + //ParamGroupRecord( &structurePG ); + return CmdStructureAction( action, pos ); + + case wActionMove: + return CmdStructureAction( action, pos ); + + case C_RDOWN: + case C_DOWN: + if (MyGetKeyState()&WKEY_CTRL) { + return CmdStructureAction( C_RDOWN, pos ); + } + return CmdStructureAction( action, pos ); + + case C_RMOVE: + case C_MOVE: + if (MyGetKeyState()&WKEY_CTRL) { + return CmdStructureAction( C_RMOVE, pos ); + } 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") ); + if (MyGetKeyState()&WKEY_CTRL) { + return CmdStructureAction( C_RUP, pos ); + } + InfoMessage( _("Left-Drag to place, Ctrl+Left-Drag or Right-Drag to Rotate, Space or Enter to accept, Esc to Cancel") ); return CmdStructureAction( action, pos ); case C_TEXT: if ((action>>8) != ' ') return C_CONTINUE; + /*no break*/ case C_OK: CmdStructureAction( action, pos ); return C_CONTINUE; case C_CANCEL: HotBarCancel(); + /* no break*/ 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 ); + AddMenuButton( menu, CmdStructure, "cmdStructure", _("Structure"), wIconCreatePixMap(struct_xpm), LEVEL0_50, IC_WANT_MOVE|IC_STICKY|IC_CMDMENU|IC_POPUP2, ACCL_STRUCTURE, NULL ); + structureHotBarCmdInx = AddMenuButton( menu, CmdStructureHotBar, "cmdStructureHotBar", "", NULL, LEVEL0_50, IC_WANT_MOVE|IC_STICKY|IC_CMDMENU|IC_POPUP2, 0, NULL ); ParamRegister( &structurePG ); + if ( structPopupM == NULL ) { + structPopupM = MenuRegister( "Structure Rotate" ); + AddRotateMenu( structPopupM, StructRotate ); + } } -#endif - EXPORT void InitTrkStruct( void ) { T_STRUCTURE = InitObject( &structureCmds ); log_structure = LogFindIndex( "Structure" ); - AddParam( "STRUCTURE ", ReadStructureParam ); + AddParam( "STRUCTURE ", ReadStructureParam); ParamRegister( &pierPG ); } diff --git a/app/bin/cswitchmotor.c b/app/bin/cswitchmotor.c index 09e6709..a8e1c54 100644 --- a/app/bin/cswitchmotor.c +++ b/app/bin/cswitchmotor.c @@ -60,6 +60,9 @@ #include "param.h" #include "track.h" #include "trackx.h" +#ifdef WINDOWS +#include "include/utf8convert.h" +#endif // WINDOWS #include "utility.h" #include "messages.h" @@ -83,6 +86,9 @@ static char switchmotorReverse[STR_LONG_SIZE]; static char switchmotorPointSense[STR_LONG_SIZE]; static track_p switchmotorTurnout; +static track_p last_motor; +static track_p first_motor; + static paramData_t switchmotorPLs[] = { /*0*/ { PD_STRING, switchmotorName, "name", PDO_NOPREF|PDO_STRINGLIMITLENGTH, (void*)200, N_("Name"), 0, 0, sizeof(switchmotorName)}, /*1*/ { PD_STRING, switchmotorNormal, "normal", PDO_NOPREF|PDO_STRINGLIMITLENGTH, (void*)350, N_("Normal"), 0, 0, sizeof(switchmotorNormal)}, @@ -126,6 +132,7 @@ typedef struct switchmotorData_t { BOOL_T IsHilite; TRKINX_T turnindx; track_p turnout; + track_p next_motor; } switchmotorData_t, *switchmotorData_p; static switchmotorData_p GetswitchmotorData ( track_p trk ) @@ -201,7 +208,7 @@ static void DrawSwitchMotor (track_p t, drawCmd_p d, wDrawColor color ) Translate (&p[iPoint], orig, x_angle, switchmotorPoly_Pix[iPoint].x * switchmotorPoly_SF / scaleRatio ); Translate (&p[iPoint], p[iPoint], y_angle, (10+switchmotorPoly_Pix[iPoint].y) * switchmotorPoly_SF / scaleRatio ); } - DrawFillPoly(d, switchmotorPoly_CNT, p, wDrawColorBlack); + DrawPoly(d, switchmotorPoly_CNT, p, NULL, color, 0, 1, 0); } static struct { @@ -308,7 +315,13 @@ static DIST_T DistanceSwitchMotor (track_p t, coOrd * p ) { switchmotorData_p xx = GetswitchmotorData(t); if (xx->turnout == NULL) return 0; - return GetTrkDistance(xx->turnout,p); + coOrd center,hi,lo; + GetBoundingBox(t,&hi,&lo); + center.x = (hi.x+lo.x)/2; + center.y = (hi.y+lo.y)/2; + DIST_T d = FindDistance(center,*p); + *p = center; + return d; } static void DescribeSwitchMotor (track_p trk, char * str, CSIZE_T len ) @@ -326,7 +339,7 @@ static void DescribeSwitchMotor (track_p trk, char * str, CSIZE_T len ) *str = tolower((unsigned char)*str); str++; } - sprintf( str, _("(%d): Layer=%d %s"), + sprintf( str, _("(%d): Layer=%u %s"), GetTrkIndex(trk), GetTrkLayer(trk)+1, message ); strncpy(switchmotorData.name,xx->name,STR_SHORT_SIZE-1); switchmotorData.name[STR_SHORT_SIZE-1] = '\0'; @@ -364,52 +377,89 @@ static void switchmotorDebug (track_p trk) static void DeleteSwitchMotor ( track_p trk ) { - LOG( log_switchmotor, 1,("*** DeleteSwitchMotor(%p)\n",trk)) - LOG( log_switchmotor, 1,("*** DeleteSwitchMotor(): index is %d\n",GetTrkIndex(trk))) - switchmotorData_p xx = GetswitchmotorData(trk); - LOG( log_switchmotor, 1,("*** DeleteSwitchMotor(): xx = %p, xx->name = %p, xx->normal = %p, xx->reverse = %p, xx->pointsense = %p\n", + + track_p trk1; + switchmotorData_p xx1; + + LOG( log_switchmotor, 1,("*** DeleteSwitchMotor(%p)\n",trk)) + LOG( log_switchmotor, 1,("*** DeleteSwitchMotor(): index is %d\n",GetTrkIndex(trk))) + switchmotorData_p xx = GetswitchmotorData(trk); + LOG( log_switchmotor, 1,("*** DeleteSwitchMotor(): xx = %p, xx->name = %p, xx->normal = %p, xx->reverse = %p, xx->pointsense = %p\n", xx,xx->name,xx->normal,xx->reverse,xx->pointsense)) MyFree(xx->name); xx->name = NULL; MyFree(xx->normal); xx->normal = NULL; MyFree(xx->reverse); xx->reverse = NULL; MyFree(xx->pointsense); xx->pointsense = NULL; + if (first_motor == trk) + first_motor = xx->next_motor; + trk1 = first_motor; + while(trk1) { + xx1 = GetswitchmotorData (trk1); + if (xx1->next_motor == trk) { + xx1->next_motor = xx->next_motor; + break; + } + trk1 = xx1->next_motor; + } + if (trk == last_motor) + last_motor = trk1; } static BOOL_T WriteSwitchMotor ( track_p t, FILE * f ) { BOOL_T rc = TRUE; switchmotorData_p xx = GetswitchmotorData(t); + char *switchMotorName = MyStrdup(xx->name); - if (xx->turnout == NULL) return FALSE; +#ifdef WINDOWS + switchMotorName = Convert2UTF8(switchMotorName); +#endif // WINDOWS + + if (xx->turnout == NULL) + return FALSE; rc &= fprintf(f, "SWITCHMOTOR %d %d \"%s\" \"%s\" \"%s\" \"%s\"\n", - GetTrkIndex(t), GetTrkIndex(xx->turnout), xx->name, + GetTrkIndex(t), GetTrkIndex(xx->turnout), switchMotorName, xx->normal, xx->reverse, xx->pointsense)>0; + + MyFree(switchMotorName); return rc; } -static void ReadSwitchMotor ( char * line ) +static BOOL_T ReadSwitchMotor ( char * line ) { TRKINX_T trkindex; wIndex_t index; - track_p trk; - switchmotorData_p xx; + track_p trk,last_trk; + switchmotorData_p xx,xx1; 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; + return FALSE; } +#ifdef WINDOWS + ConvertUTF8ToSystem(name); +#endif // WINDOWS 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->turnindx = trkindex; + xx->turnindx = trkindex; + if (last_motor) { + last_trk = last_motor; + xx1 = GetswitchmotorData(last_trk); + xx1->next_motor = trk; + } else first_motor = trk; + xx->next_motor = NULL; + last_motor = trk; + LOG( log_switchmotor, 1,("*** ReadSwitchMotor(): trk = %p (%d), xx = %p\n",trk,GetTrkIndex(trk),xx)) LOG( log_switchmotor, 1,("*** ReadSwitchMotor(): name = %p, normal = %p, reverse = %p, pointsense = %p\n", name,normal,reverse,pointsense)) switchmotorDebug(trk); + return TRUE; } EXPORT void ResolveSwitchmotorTurnout ( track_p trk ) @@ -472,19 +522,21 @@ 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); + a_trk = first_motor; + while (a_trk) { + xx = GetswitchmotorData(a_trk); + if (!IsTrackDeleted(a_trk)) { if (xx->turnout == trk) return a_trk; } + a_trk = xx->next_motor; } return NULL; } static void SwitchMotorOk ( void * junk ) { - switchmotorData_p xx; - track_p trk; + switchmotorData_p xx,xx1; + track_p trk,trk1; LOG( log_switchmotor, 1, ("*** SwitchMotorOk()\n")) ParamUpdate (&switchmotorPG ); @@ -502,12 +554,19 @@ static void SwitchMotorOk ( void * junk ) xx->reverse = MyStrdup(switchmotorReverse); xx->pointsense = MyStrdup(switchmotorPointSense); xx->turnout = switchmotorTurnout; - LOG( log_switchmotor, 1,("*** SwitchMotorOk(): trk = %p (%d), xx = %p\n",trk,GetTrkIndex(trk),xx)) + trk1 = last_motor; + if (trk1) { + xx1 = GetswitchmotorData( trk1 ); + xx1->next_motor = trk; + } else first_motor = trk; + xx->next_motor = NULL; + last_motor = trk; + LOG( log_switchmotor, 1,("*** SwitchMotorOk(): trk = %p (%d), xx = %p\n",trk,GetTrkIndex(trk),xx)) switchmotorDebug(trk); UndoEnd(); - wHide( switchmotorW ); - ComputeSwitchMotorBoundingBox(trk); - DrawNewTrack(trk); + wHide( switchmotorW ); + ComputeSwitchMotorBoundingBox(trk); + DrawNewTrack(trk); } static void NewSwitchMotorDialog(track_p trk) @@ -704,7 +763,7 @@ static void DrawSWMotorTrackHilite( void ) w = (wPos_t)((swmhiliteSize.x/mainD.scale)*mainD.dpi+0.5); h = (wPos_t)((swmhiliteSize.y/mainD.scale)*mainD.dpi+0.5); mainD.CoOrd2Pix(&mainD,swmhiliteOrig,&x,&y); - wDrawFilledRectangle( mainD.d, x, y, w, h, swmhiliteColor, wDrawOptTemp ); + wDrawFilledRectangle( mainD.d, x, y, w, h, swmhiliteColor, wDrawOptTemp|wDrawOptTransparent ); } static int SwitchmotorMgmProc ( int cmd, void * data ) @@ -805,6 +864,7 @@ EXPORT void CheckDeleteSwitchmotor(track_p t) { track_p sm; switchmotorData_p xx; + if (GetTrkType( t ) != T_TURNOUT) return; // SMs only on turnouts while ((sm = FindSwitchMotor( t ))) { //Cope with multiple motors for one Turnout! xx = GetswitchmotorData (sm); diff --git a/app/bin/ctext.c b/app/bin/ctext.c index ca0c7c7..c292d1c 100644 --- a/app/bin/ctext.c +++ b/app/bin/ctext.c @@ -30,7 +30,7 @@ #include "draw.h" #include "misc.h" -track_p NewText( wIndex_t index, coOrd p, ANGLE_T angle, char * text, CSIZE_T textSize, wDrawColor color ); +track_p NewText( wIndex_t index, coOrd p, ANGLE_T angle, char * text, CSIZE_T textSize, wDrawColor color, BOOL_T boxed ); void LoadFontSizeList( wList_p, long ); void UpdateFontSizeList( long *, wList_p, wIndex_t ); @@ -57,13 +57,17 @@ static struct { wIndex_t fontSizeInx; char text[STR_LONG_SIZE]; wDrawColor color; + BOOL_T boxed; } Dt; +static char * boxLabels[] = { "", NULL }; static paramData_t textPLs[] = { #define textPD (textPLs[0]) - { PD_DROPLIST, &Dt.fontSizeInx, "fontsize", 0, NULL, N_("Font Size"), BL_EDITABLE }, + { 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") } + { PD_COLORLIST, &Dt.color, "Color", PDO_NORECORD, NULL, N_("Color") }, +#define boxPD (textPLs[2]) + { PD_TOGGLE, &Dt.boxed, "Boxed", 0, boxLabels, N_("Boxed"), 0 } }; static paramGroup_t textPG = { "text", 0, textPLs, sizeof textPLs/sizeof textPLs[0] }; @@ -83,29 +87,21 @@ static void TextDlgUpdate( switch (inx) { case 0: case 1: - if ( Dt.state == SHOW_TEXT) { - DrawMultiString( &tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0, NULL, NULL ); - DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); - } + case 2: UpdateFontSizeList( &Dt.size, (wList_p)textPLs[0].control, Dt.fontSizeInx ); - /*wWinSetBusy( mainW, TRUE );*/ if ( Dt.state == SHOW_TEXT) { DrawMultiLineTextSize( &mainD, Dt.text, NULL, Dt.size, TRUE, &size, &lastline); Dt.textLen = size.x; Dt.lastLineLen = lastline.x; Dt.lastLineOffset = lastline.y; } + wSetSelectedFontSize((wFontSize_t)Dt.size); //Update for next time DrawTextSize( &mainD, "Aquilp", NULL, Dt.size, TRUE, &size ); Dt.cursHeight = size.y; - /*wWinSetBusy( mainW, FALSE );*/ if ( Dt.state == SHOW_TEXT) { Dt.cursPos0.x = Dt.cursPos1.x = Dt.pos.x+Dt.lastLineLen; Dt.cursPos1.y = Dt.pos.y+Dt.cursHeight+Dt.lastLineOffset; - DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); - DrawMultiString( &tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0, NULL, NULL ); } - MainRedraw(); - MapRedraw(); break; } } @@ -115,8 +111,8 @@ static STATUS_T CmdText( wAction_t action, coOrd pos ) { track_p t; unsigned char c; - wControl_p controls[3]; - char * labels[2]; + wControl_p controls[4]; + char * labels[3]; coOrd size, lastline; switch (action & 0xFF) { @@ -141,23 +137,21 @@ static STATUS_T CmdText( wAction_t action, coOrd pos ) ParamLoadControls(&textPG); ParamGroupRecord( &textPG ); - if (!inPlayback) - wWinSetBusy(mainW, TRUE); DrawTextSize(&mainD, "Aquilp", NULL, Dt.size, TRUE, &size); Dt.cursHeight = size.y; - if (!inPlayback) - wWinSetBusy(mainW, FALSE); controls[0] = textPD.control; controls[1] = colorPD.control; - controls[2] = 0; + controls[2] = boxPD.control; + controls[3] = 0; labels[0] = N_("Font Size"); labels[1] = N_("Color"); + labels[2] = N_("Boxed"); InfoSubstituteControls( controls, labels ); return C_CONTINUE; break; case C_DOWN: - if (Dt.state != 0) { + if (Dt.state != POSITION_TEXT) { } Dt.pos = pos; Dt.cursPos0.y = Dt.cursPos1.y = pos.y + Dt.lastLineOffset; @@ -165,21 +159,13 @@ static STATUS_T CmdText( wAction_t action, coOrd pos ) DrawTextSize(&mainD, "Aquilp", NULL, Dt.size, TRUE, &size); //In case fontsize change Dt.cursHeight = size.y; Dt.cursPos1.y += Dt.cursHeight; - DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); - DrawMultiString(&tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0.0, NULL, NULL ); Dt.state = SHOW_TEXT; - MainRedraw(); - MapRedraw(); return C_CONTINUE; case C_MOVE: Dt.pos = pos; Dt.cursPos0.y = Dt.cursPos1.y = pos.y + Dt.lastLineOffset; Dt.cursPos0.x = Dt.cursPos1.x = pos.x + Dt.lastLineLen; Dt.cursPos1.y += Dt.cursHeight; - DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, wDrawColorBlack ); - DrawMultiString(&tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0.0, NULL, NULL ); - MainRedraw(); - MapRedraw(); return C_CONTINUE; case C_UP: return C_CONTINUE; @@ -188,8 +174,7 @@ static STATUS_T CmdText( wAction_t action, coOrd pos ) NoticeMessage( MSG_SEL_POS_FIRST, _("Ok"), NULL ); return C_CONTINUE; } - DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); - DrawMultiString(&tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0.0, NULL, NULL ); + c = (unsigned char)(action >> 8); switch (c) { case '\b': @@ -209,7 +194,7 @@ static STATUS_T CmdText( wAction_t action, coOrd pos ) break; case '\015': UndoStart( _("Create Text"), "newText - CR" ); - t = NewText( 0, Dt.pos, Dt.angle, Dt.text, (CSIZE_T)Dt.size, Dt.color ); + t = NewText( 0, Dt.pos, Dt.angle, Dt.text, (CSIZE_T)Dt.size, Dt.color, Dt.boxed ); UndoEnd(); DrawNewTrack(t); Dt.state = POSITION_TEXT; @@ -227,42 +212,33 @@ static STATUS_T CmdText( wAction_t action, coOrd pos ) Dt.lastLineOffset = lastline.y; Dt.cursPos0.x = Dt.cursPos1.x = Dt.pos.x + Dt.lastLineLen; Dt.cursPos0.y = Dt.cursPos1.y = Dt.pos.y + Dt.lastLineOffset; - DrawTextSize(&mainD, "Aquilp", NULL, Dt.size, TRUE, &size); //In case fontsize change + POS_T descent, ascent; + DrawTextSize2(&mainD, "Aquilp", NULL, Dt.size, TRUE, &size, &descent, &ascent); //In case fontsize change Dt.cursHeight = size.y; + Dt.cursPos0.y -=descent; Dt.cursPos1.y +=Dt.cursHeight; - MainRedraw(); - MapRedraw(); - //DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); - //DrawMultiString(&tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0.0, NULL, NULL ); return C_CONTINUE; case C_REDRAW: - if (Dt.state == 1) { - DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); - DrawMultiString(&tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0.0, NULL, NULL ); - } + DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); + DrawMultiString(&tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0.0, NULL, NULL, Dt.boxed ); return C_CONTINUE; case C_CANCEL: if (Dt.state != POSITION_TEXT) { Dt.state = POSITION_TEXT; } InfoSubstituteControls( NULL, NULL ); - MainRedraw(); - MapRedraw(); return C_TERMINATE; case C_OK: if (Dt.state != POSITION_TEXT) { - DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color ); Dt.state = POSITION_TEXT; if (Dt.len) { UndoStart( _("Create Text"), "newText - OK" ); - t = NewText( 0, Dt.pos, Dt.angle, Dt.text, (CSIZE_T)Dt.size, Dt.color ); + t = NewText( 0, Dt.pos, Dt.angle, Dt.text, (CSIZE_T)Dt.size, Dt.color, Dt.boxed ); UndoEnd(); DrawNewTrack(t); } } InfoSubstituteControls( NULL, NULL ); - MainRedraw(); - MapRedraw(); return C_TERMINATE; case C_FINISH: @@ -273,6 +249,7 @@ static STATUS_T CmdText( wAction_t action, coOrd pos ) return C_TERMINATE; case C_CMDMENU: + menuPos = pos; wMenuPopupShow( textPopupM ); return C_CONTINUE; } diff --git a/app/bin/ctodesgn.c b/app/bin/ctodesgn.c index 27acbb6..dc118a1 100644 --- a/app/bin/ctodesgn.c +++ b/app/bin/ctodesgn.c @@ -41,9 +41,17 @@ #include "param.h" #include "track.h" #include "utility.h" +#include "ccornu.h" +#include "cbezier.h" +#include "misc.h" + +dynArr_t tempSegs_da; +dynArr_t tempEndPts_da; +char tempCustom[4096]; #define TURNOUTDESIGNER "CTURNOUT DESIGNER" +dynArr_t tempSegs_da; /***************************************** @@ -67,6 +75,9 @@ #define NTO_CRV_SECTION (12) #define NTO_BUMPER (13) #define NTO_TURNTABLE (14) +#define NTO_CORNU (15) +#define NTO_CORNUWYE (16) +#define NTO_CORNU3WAY (17) #define FLOAT (1) @@ -78,7 +89,7 @@ typedef struct { int index; char * winLabel; char * printLabel; - enum { Dim_e, Frog_e, Angle_e } mode; + enum { Dim_e, Frog_e, Angle_e, Rad_e } mode; } toDesignFloat_t; typedef struct { @@ -97,27 +108,48 @@ typedef struct { toDesignSchema_t * paths; int angleModeCnt; wLine_p lineC; + wBool_t slipmode; } toDesignDesc_t; static wWin_p newTurnW; + +static FLOAT_T newTurnRad0; +static FLOAT_T newTurnAngle0; static FLOAT_T newTurnLen0; +static FLOAT_T newTurnOff0; + +static FLOAT_T newTurnRad1; +static FLOAT_T newTurnAngle1; static FLOAT_T newTurnLen1; static FLOAT_T newTurnOff1; -static FLOAT_T newTurnAngle1; + +static FLOAT_T newTurnRad2; +static FLOAT_T newTurnAngle2; static FLOAT_T newTurnLen2; static FLOAT_T newTurnOff2; -static FLOAT_T newTurnAngle2; + +static FLOAT_T newTurnRad3; +static FLOAT_T newTurnAngle3; +static FLOAT_T newTurnOff3; +static FLOAT_T newTurnLen3; + +static FLOAT_T newTurnToeL; +static FLOAT_T newTurnToeR; + static long newTurnAngleMode = 1; +static long newTurnSlipMode = 0; 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 char *newTurnSlipModeLabels[] = { N_("Dual Path"), N_("Quad Path"), 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 r_10000_10000 = {-10000, 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 }; @@ -126,7 +158,13 @@ static void ShowTurnoutDesigner( void * ); static coOrd points[20]; -static DIST_T radii[10] = { 0.0 }; +static coOrd end_points[20]; +static coOrd end_centers[20]; +static double end_arcs[20]; +static double end_angles[20]; +static DIST_T radii[10]; +static double angles[10]; + #define POSX(X) ((wPos_t)((X)*newTurnout_d.dpi)) #define POSY(Y) ((wPos_t)((Y)*newTurnout_d.dpi)) @@ -134,22 +172,35 @@ static DIST_T radii[10] = { 0.0 }; static paramData_t turnDesignPLs[] = { #define I_TOLENGTH (0) #define I_TO_FIRST_FLOAT (0) + { PD_FLOAT, &newTurnLen0, "len0", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r0_10000, N_("Length") }, { 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, &newTurnLen3, "len3", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r0_10000, N_("Length") }, +#define I_TOOFFSET (4) + { PD_FLOAT, &newTurnOff0, "off0", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r_10000_10000, N_("Offset") }, + { PD_FLOAT, &newTurnOff1, "off1", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r_10000_10000, N_("Offset") }, + { PD_FLOAT, &newTurnOff2, "off2", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r_10000_10000, N_("Offset") }, + { PD_FLOAT, &newTurnOff3, "off3", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r_10000_10000, N_("Offset") }, +#define I_TORAD (8) + { PD_FLOAT, &newTurnRad0, "rad0", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r_10000_10000, N_("Radius") }, + { PD_FLOAT, &newTurnRad1, "rad1", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r_10000_10000, N_("Radius") }, + { PD_FLOAT, &newTurnRad2, "rad2", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r_10000_10000, N_("Radius") }, + { PD_FLOAT, &newTurnRad3, "rad3", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r_10000_10000, N_("Radius") }, +#define I_TOTOELENGTH (12) + { PD_FLOAT, &newTurnToeL, "toeL", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r0_10000, N_("Length") }, + { PD_FLOAT, &newTurnToeR, "toeR", PDO_DIM|PDO_DLGIGNORELABELWIDTH, &r0_10000, N_("Length") }, +#define I_TOANGLE (14) + { PD_FLOAT, &newTurnAngle0, "angle0", PDO_DLGIGNORELABELWIDTH, &r0_360, N_("Angle") }, { 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) +#define I_TO_LAST_FLOAT (17) + { PD_FLOAT, &newTurnAngle3, "angle3", PDO_DLGIGNORELABELWIDTH, &r0_360, N_("Angle") }, +#define I_TOMANUF (18) { PD_STRING, &newTurnManufacturer, "manuf", PDO_STRINGLIMITLENGTH, NULL, N_("Manufacturer"), 0, 0, sizeof(newTurnManufacturer)}, -#define I_TOLDESC (8) +#define I_TOLDESC (19) { PD_STRING, &newTurnLeftDesc, "desc1", PDO_STRINGLIMITLENGTH, NULL, N_("Left Description"), 0, 0, sizeof(newTurnLeftDesc)}, { PD_STRING, &newTurnLeftPartno, "partno1", PDO_DLGHORZ | PDO_STRINGLIMITLENGTH, NULL, N_(" #"), 0, 0, sizeof(newTurnLeftPartno)}, -#define I_TORDESC (10) +#define I_TORDESC (21) { PD_STRING, &newTurnRightDesc, "desc2", PDO_STRINGLIMITLENGTH, NULL, N_("Right Description"),0, 0, sizeof(newTurnRightDesc)}, { PD_STRING, &newTurnRightPartno, "partno2", PDO_DLGHORZ | PDO_STRINGLIMITLENGTH, NULL, N_(" #"),0, 0, sizeof(newTurnRightPartno)}, { PD_FLOAT, &newTurnRoadbedWidth, "roadbedWidth", PDO_DIM, &r0_100, N_("Roadbed Width") }, @@ -157,11 +208,13 @@ static paramData_t turnDesignPLs[] = { { 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 } +#define I_TOANGMODE (28) + { PD_RADIO, &newTurnAngleMode, "angleMode", 0, newTurnAngleModeLabels }, +#define I_TOSLIPMODE (29) + { PD_RADIO, &newTurnSlipMode, "slipMode", 0, newTurnSlipModeLabels } }; - #ifndef MKTURNOUT + static paramGroup_t turnDesignPG = { "turnoutNew", 0, turnDesignPLs, sizeof turnDesignPLs/sizeof turnDesignPLs[0] }; static turnoutInfo_t * customTurnout1, * customTurnout2; @@ -188,7 +241,7 @@ 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 }, +{ { 100, 120 }, I_TOLENGTH+1, N_("Length"), N_("Overall Length"), Dim_e }, }; static signed char RegPaths[] = { 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 2, 0, 0, @@ -241,6 +294,34 @@ static toDesignDesc_t CrvDesc = { sizeof CrvFloats/sizeof CrvFloats[0], CrvFloats, &Crv1Schema, 1 }; +static wLines_t CornuLines[] = { +#include "tocornu.lin" + }; +static toDesignFloat_t CornuFloats[] = { +{ { 175, 10 }, I_TOLENGTH+0, N_("Length"), N_("Inner Length"), Dim_e }, +{ { 375, 0 }, I_TOANGLE+0, N_("Angle"), N_("Inner Angle"), Frog_e }, +{ { 375, 22 }, I_TOOFFSET+0, N_("Offset"), N_("Inner Offset"), Dim_e }, +{ { 375, 44 }, I_TORAD+0, N_("Radius"), N_("Inner Radius"), 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 }, +{ { 400, 106 }, I_TORAD+1, N_("Radius"), N_("Outer Radius"), Dim_e }, +{ { 175, 120 }, I_TOLENGTH+1, N_("Length"), N_("Outer Length"), Dim_e }, +{ { 50, 90 }, I_TORAD+2, N_("Radius"), N_("Toe Radius"), Dim_e }, +{ { 50, 40 }, I_TOTOELENGTH+0, N_("Length"), N_("Toe Length"), Dim_e } }; +static signed char CornuPaths[] = { + 'N', 'o', 'r', 'm', 'a', 'l', 0, 1, 4, 0, 0, 0, + 'R', 'e', 'v', 'e', 'r', 's', 'e', 0, 1, 2, 0, 0, 0, 0 }; +static toDesignSchema_t CornuSchema = { + CornuPaths, + "033" "343" "413" }; + +static toDesignDesc_t CornuDesc = { + NTO_CORNU, + N_("Cornu Curved Turnout"), + 2, + sizeof CornuLines/sizeof CornuLines[0], CornuLines, + sizeof CornuFloats/sizeof CornuFloats[0], CornuFloats, + &CornuSchema, 1 }; static wLines_t WyeLines[] = { #include "towye.lin" @@ -279,6 +360,35 @@ static toDesignDesc_t WyeDesc = { sizeof WyeFloats/sizeof WyeFloats[0], WyeFloats, NULL, 1 }; +static wLines_t CornuWyeLines[] = { +#include "tocornuwye.lin" + }; +static toDesignFloat_t CornuWyeFloats[] = { +{ { 175, 10 }, I_TOLENGTH+0, N_("Length"), N_("Left Length"), Dim_e }, +{ { 400, 28 }, I_TOANGLE+0, N_("Angle"), N_("Left Angle"), Frog_e }, +{ { 400, 48 }, I_TOOFFSET+0, N_("Offset"), N_("Left Offset"), Dim_e }, +{ { 400, 68 }, I_TORAD+0, N_("Radius"), N_("Left Radius"), Dim_e }, +{ { 400, 108 }, I_TORAD+1, N_("Radius"), N_("Right Radius"), Dim_e }, +{ { 400, 128 }, I_TOOFFSET+1, N_("Offset"), N_("Right Offset"), Dim_e }, +{ { 400, 148 }, I_TOANGLE+1, N_("Angle"), N_("Right Angle"), Frog_e }, +{ { 175, 170 }, I_TOLENGTH+1, N_("Length"), N_("Right Length"), Dim_e }, +{ { 80, 48 }, I_TOTOELENGTH+0, N_("Length"), N_("Toe Length"), Dim_e }, +{ { 80, 28 }, I_TORAD+2, N_("Radius"), N_("Toe Radius"), Dim_e }, + }; +static signed char CornuWyePaths[] = { + 'L', 'e', 'f', 't', 0, 1, 2, 3, 0, 0, + 'R', 'i', 'g', 'h', 't', 0, 1, 4, 5, 0, 0, 0 }; /* Not Used */ +static toDesignSchema_t CornuWyeSchema = { + CornuWyePaths, + "030" "341" "410" "362" "620" }; /* Not Used */ +static toDesignDesc_t CornuWyeDesc = { + NTO_CORNUWYE, + N_("Cornu Wye Turnout"), + 1, + sizeof CornuWyeLines/sizeof CornuWyeLines[0], CornuWyeLines, + sizeof CornuWyeFloats/sizeof CornuWyeFloats[0], CornuWyeFloats, + NULL, 1 }; + static wLines_t ThreewayLines[] = { #include "to3way.lin" }; @@ -320,6 +430,41 @@ static toDesignDesc_t ThreewayDesc = { sizeof ThreewayFloats/sizeof ThreewayFloats[0], ThreewayFloats, NULL, 1 }; +static wLines_t CornuThreewayLines[] = { +#include "tocornu3way.lin" + }; +static toDesignFloat_t CornuThreewayFloats[] = { +{ { 175, 10 }, I_TOLENGTH+0, N_("Length"), N_("Left Length"), Dim_e }, +{ { 380, 10 }, I_TOANGLE+0, N_("Angle"), N_("Left Angle"), Frog_e }, +{ { 380, 50 }, I_TOOFFSET+0, N_("Offset"), N_("Left Offset"), Dim_e }, +{ { 380, 30 }, I_TORAD+0, N_("Radius"), N_("Left Radius"), Dim_e }, +{ { 130, 90 }, I_TOLENGTH+3, N_("Length"), N_("Center Length"), Dim_e }, +{ { 400, 70 }, I_TOANGLE+3, N_("Angle"), N_("Center Angle"), Dim_e }, +{ { 400, 90}, I_TOOFFSET+3, N_("Offset"), N_("Center Offset"), Dim_e }, +{ { 400, 110 }, I_TORAD+3, N_("Radius"), N_("Center Radius"), Dim_e }, +{ { 420, 150 }, I_TORAD+1, N_("Radius"), N_("Right Radius"), Dim_e }, +{ { 420, 130 }, I_TOOFFSET+1, N_("Offset"), N_("Right Offset"), Dim_e }, +{ { 420, 170 }, I_TOANGLE+1, N_("Angle"), N_("Right Angle"), Frog_e }, +{ { 175, 170 }, I_TOLENGTH+1, N_("Length"), N_("Right Length"), Dim_e }, +{ { 45, 50 }, I_TOTOELENGTH+0, N_("Length"), N_("Toe Length Left"), Dim_e }, +{ { 55, 140 }, I_TOTOELENGTH+1, N_("Length"), N_("Toe Length Right"), Dim_e }, +{ { 40, 105 }, I_TORAD+2, N_("Radius"), N_("Toe Radius"), Dim_e }, + }; +static signed char CornuTriPaths[] = { + '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 CornuTriSchema = { + CornuTriPaths, + "030" "341" "410" "362" "620" "370" }; +static toDesignDesc_t CornuThreewayDesc = { + NTO_CORNU3WAY, + N_("Cornu 3-way Turnout"), + 1, + sizeof CornuThreewayLines/sizeof CornuThreewayLines[0], CornuThreewayLines, + sizeof CornuThreewayFloats/sizeof CornuThreewayFloats[0], CornuThreewayFloats, + NULL, 1 }; + static wLines_t CrossingLines[] = { #include "toxing.lin" }; @@ -371,9 +516,17 @@ static toDesignFloat_t DoubleSlipFloats[] = { 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 signed char DoubleSlipPaths2[] = { + 'C', 'r', 'o', 's', 's', '1', 0, 1, 2, 3, 0, 0, + 'C', 'r', 'o', 's', 's', '2', 0, 4, 5, 6, 0, 0, + 'S', 'l', 'i', 'p', '1', 0, 1, 7, 6, 0, 0, + 'S', 'l', 'i', 'p', '2', 0, 4, 8, 3, 0, 0, 0 }; static toDesignSchema_t DoubleSlipSchema = { DoubleSlipPaths, "040" "460" "610" "270" "750" "530" "451" "762" }; +static toDesignSchema_t DoubleSlipSchema2 = { + DoubleSlipPaths2, + "040" "460" "610" "270" "750" "530" "451" "762" }; static toDesignDesc_t DoubleSlipDesc = { NTO_D_SLIP, N_("Double Slipswitch"), @@ -595,8 +748,11 @@ static toDesignDesc_t TurntableDesc = { static toDesignDesc_t * designDescs[] = { &RegDesc, &CrvDesc, + &CornuDesc, &WyeDesc, + &CornuWyeDesc, &ThreewayDesc, + &CornuThreewayDesc, &CrossingDesc, &SingleSlipDesc, &DoubleSlipDesc, @@ -1059,6 +1215,73 @@ static BOOL_T ComputeCurve( return TRUE; } +#ifndef MKTURNOUT +/* For Bezier Segs we need to duplicate the subSegs Array as well */ +void AppendSegs(dynArr_t * target, dynArr_t * source) { + +#define sourceSegs(N) DYNARR_N( trkSeg_t, *source, N ) +#define targetSegs(N) DYNARR_N( trkSeg_t, *target, N ) + + trkSeg_p src; + + for (int i=0;i<source->cnt; i++) { + src = &sourceSegs(i); + addSegBezier(target, src); + } +} + +/* Bezier Segs will have subSegs Array - free it before resetting the array */ +void ClearSegs(dynArr_t * target) { + for (int i=0;i<(*target).cnt;i++) { + if (targetSegs(i).type == SEG_BEZTRK) + if (targetSegs(i).bezSegs.ptr) MyFree(targetSegs(i).bezSegs.ptr); + targetSegs(i).bezSegs.ptr = NULL; + targetSegs(i).bezSegs.cnt = 0; + targetSegs(i).bezSegs.max = 0; + } + DYNARR_RESET( trkSeg_t, *target ); +} + +BOOL_T CallCornuNoBez(coOrd pos[2], coOrd center[2], ANGLE_T angle[2], DIST_T radius[2], dynArr_t * array_p) { + + dynArr_t temp_array; + DYNARR_RESET(trkSeg_t,temp_array); + temp_array.ptr=0; + temp_array.max=0; + + + wBool_t rc = CallCornu0(pos,center,angle,radius, &temp_array, FALSE); + + if (!rc) return FALSE; + + for (int i=0;i<temp_array.cnt;i++) { + trkSeg_p from_seg = &DYNARR_N(trkSeg_t,temp_array,i); + if ((from_seg->type == SEG_BEZTRK) || (from_seg->type == SEG_BEZLIN)) { + for (int j=0;j<from_seg->bezSegs.cnt;j++) { + trkSeg_p sub_seg = &DYNARR_N(trkSeg_t,from_seg->bezSegs,j); + DYNARR_APPEND(trkSeg_t,*array_p,5); + trkSeg_p to_seg = &DYNARR_N(trkSeg_t,*array_p,(*array_p).cnt-1); + to_seg->u = sub_seg->u; + to_seg->type = sub_seg->type; + to_seg->color = wDrawColorBlack; + to_seg->width = sub_seg->width; + } + } else { + DYNARR_APPEND(trkSeg_t,*array_p,5); + trkSeg_p to_seg = &DYNARR_N(trkSeg_t,*array_p,(*array_p).cnt-1); + to_seg->u = from_seg->u; + to_seg->type = from_seg->type; + to_seg->color = wDrawColorBlack; + to_seg->width = from_seg->width; + } + } + + ClearSegs(&temp_array); + + return TRUE; +} + +#endif static toDesignSchema_t * LoadSegs( @@ -1076,52 +1299,72 @@ static toDesignSchema_t * LoadSegs( char *segOrder; coOrd pos; wIndex_t segCnt; - ANGLE_T angle1, angle2; + ANGLE_T angle0, angle1, angle2, angle3; trkSeg_p segPtr; +#ifndef MKTURNOUT + struct { + coOrd pos[10]; + coOrd center[10]; + DIST_T radius[10]; + DIST_T angle[10]; + } cornuData; +#endif DYNARR_RESET( trkSeg_t, tempSegs_da ); + angle0 = newTurnAngle0; angle1 = newTurnAngle1; angle2 = newTurnAngle2; + angle3 = newTurnAngle3; + + if ( newTurnAngleMode == 0 && dp->type != NTO_CRV_SECTION ) { /* convert from Frog Num to degrees */ + if ( angle0 > 0 ) + angle0 = R2D(asin(1.0 / angle0)); if ( angle1 > 0 ) angle1 = R2D(asin(1.0 / angle1)); if ( angle2 > 0 ) angle2 = R2D(asin(1.0 / angle2)); + if ( angle3 > 0 ) + angle3 = R2D(asin(1.0 / angle3)); } 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; + if ( *(FLOAT_T*)(turnDesignPLs[dp->floats[i].index].valueP) == 0.0 ) + if (dp->type != NTO_CORNU && + dp->type != NTO_CORNUWYE && + dp->type != NTO_CORNU3WAY + ) { + 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 ) ) + (newTurnLen0), fabs(newTurnOff0), angle0 ) ) 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); + points[1].x = (newTurnLen1); + points[2].y = fabs(newTurnOff0); + points[2].x = (newTurnLen0); 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; + tempEndPts(2).pos = points[2]; tempEndPts(2).angle = 90.0-angle0; break; case NTO_CURVED: DYNARR_SET( trkEndPt_t, tempEndPts_da, 3 ); if ( !ComputeCurve( &points[3], &points[4], &radii[0], - (newTurnLen1), (newTurnOff1), angle1 ) ) + (newTurnLen0), fabs(newTurnOff0), angle0 ) ) return NULL; if ( !ComputeCurve( &points[5], &points[6], &radii[1], - (newTurnLen2), (newTurnOff2), angle2 ) ) + (newTurnLen1), fabs(newTurnOff1), angle1 ) ) return NULL; d = points[3].x - points[5].x; if ( d < -0.10 ) @@ -1133,32 +1376,51 @@ static toDesignSchema_t * LoadSegs( 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); + points[1].y = fabs(newTurnOff0); points[1].x = (newTurnLen0); + points[2].y = fabs(newTurnOff1); points[2].x = (newTurnLen1); 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; + tempEndPts(2).pos = points[1]; tempEndPts(2).angle = 90.0-angle0; + tempEndPts(1).pos = points[2]; tempEndPts(1).angle = 90.0-angle1; break; +#ifndef MKTURNOUT + case NTO_CORNU: + + radii[0] = fabs(newTurnRad2); /*Toe*/ + radii[1] = fabs(newTurnRad0); /*Inner*/ + radii[2] = fabs(newTurnRad1); /*Outer*/ + angles[0] = 0.0; /*Base*/ + angles[1] = newTurnAngle0; /*Inner*/ + angles[2] = newTurnAngle1; /*Outer*/ + pp = &CornuSchema; + points[0].x = points[0].y = 0.0; + points[1].y = (newTurnOff0); points[1].x = (newTurnLen0); /*Inner*/ + points[2].y = (newTurnOff1); points[2].x = (newTurnLen1); /*Outer*/ + + tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; + tempEndPts(2).pos = points[1]; tempEndPts(2).angle = 90.0-angles[1]; + tempEndPts(1).pos = points[2]; tempEndPts(1).angle = 90.0-angles[2]; + break; +#endif 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 ) ) + (newTurnLen0), fabs(newTurnOff0), angle0 ) ) return NULL; if ( !ComputeCurve( &points[5], &points[6], &radii[1], - (newTurnLen2), (newTurnOff2), angle2 ) ) + (newTurnLen1), fabs(newTurnOff1), angle1 ) ) 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[1].y = fabs(newTurnOff0); + points[1].x = (newTurnLen0); + points[2].y = -fabs(newTurnOff1); + points[2].x = (newTurnLen1); points[7].y = 0; - points[7].x = (newTurnLen0); + points[7].x = (newTurnLen2); d = points[3].x - points[5].x; if ( d < -0.10 ) { pp = (dp->type==NTO_3WAY ? &Tri3Schema : &Wye3Schema ); @@ -1168,63 +1430,644 @@ static toDesignSchema_t * LoadSegs( 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; + tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0-angle0; + tempEndPts(2).pos = points[2]; tempEndPts(2).angle = 90.0+angle1; if (dp->type == NTO_3WAY) { tempEndPts(3).pos = points[7]; tempEndPts(3).angle = 90.0; } break; +#ifndef MKTURNOUT + case NTO_CORNUWYE: + case NTO_CORNU3WAY: + DYNARR_SET( trkEndPt_t, tempEndPts_da, (dp->type==NTO_CORNU3WAY)?4:3 ); + + /* + * Construct Wye and 3 Way Turnouts with Cornu curves + * 1. Establish where the joint(s) (Toes) are by using the main curve + * 2. Rebuild the segments into a single set using those points + * 3. Build Path statements to suit the segments + * --------------------------------------------------------------------------------------------- + * 7 CornuData. Cheat Sheet - segment array parts + * =============+ Note - 6-7 is at Toe2 and 8-9 at Toe1 if RH comes before LH + * // Toe 2 - Toe2 and Toe1 are the same (no 2-3) if co-incident + * 0 6+ 3 4 5 - Toe2, 2-3 and 4-5 all only exist for 3WAY not WYE + * +=====+ +=====+ +==================+ - If zero radius at 0, curve starts at Toe 1 + * 1 2 8+ + * Toe 1 \\ 9 + * =========+ + * + * --------------------------------------------------------------------------------------------- + */ + + radii[0] = (newTurnRad2); /*Base*/ + radii[1] = (newTurnRad0); /*Left*/ + radii[2] = (newTurnRad1); /*Right*/ + radii[3] = (newTurnRad3); /*Center*/ + angles[0] = 0.0; /*Base*/ + angles[1] = newTurnAngle0; /*Left*/ + angles[2] = newTurnAngle1; /*Right*/ + angles[3] = newTurnAngle3; /*Center*/ + points[0].x = points[0].y = 0.0; /*Base*/ + points[1].y = (newTurnOff0); /* Left */ + points[1].x = (newTurnLen0); + points[2].y = -(newTurnOff1); /* Right */ + points[2].x = (newTurnLen1); + if (dp->type==NTO_CORNU3WAY) { + points[3].y = (newTurnOff3); /* Center */ + points[3].x = (newTurnLen3); + } + + pp = (dp->type==NTO_CORNU3WAY ? &CornuTriSchema : &CornuWyeSchema ); + + tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; + + if (newTurnRad0<0.0) { + tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0+angles[1]; + } else { + tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0-angles[1]; + } + if (newTurnRad1<0.0) { + tempEndPts(2).pos = points[2]; tempEndPts(2).angle = 90.0-angles[2]; + } else { + tempEndPts(2).pos = points[2]; tempEndPts(2).angle = 90.0+angles[2]; + } + if (dp->type == NTO_CORNU3WAY) { + if (newTurnRad3<0.0) { + tempEndPts(3).pos = points[3]; tempEndPts(3).angle = 90.0+angles[3]; + } else { + tempEndPts(3).pos = points[3]; tempEndPts(3).angle = 90.0-angles[3]; + } + } + + DIST_T end_length = minLength/2; + + for (int i=0;i<((dp->type==NTO_CORNU3WAY)?4:3);i++) { + if (radii[i] == 0.0) { + Translate(&end_points[i], points[i], 90-angles[i]+(i==0?0:180), end_length); + end_angles[i] = angles[i]; + } else { + if (((i==0) && radii[0]>0.0) || ((i==1 || i==3) && radii[i]>0.0)|| ((i==2) && radii[i]<0.0)) + Translate(&end_centers[i], points[i], -angles[i], fabs(radii[i])); + else + Translate(&end_centers[i], points[i], angles[i], fabs(radii[i])); + end_arcs[i] = (radii[i]>=0?1:-1)*R2D(end_length/fabs(radii[i])); + end_points[i] = points[i]; + Rotate(&end_points[i],end_centers[i],((i==0||i==3)?-1:1)*end_arcs[i]); + end_angles[i] = angles[i]-((i==0||i==3)?-1:1)*end_arcs[i]; + } +LogPrintf( "ctoDes0-%d: EP(%f,%f) NEP(%f,%f) EA(%f) NEA(%f) R(%f) ARC(%f) EC(%f,%f) \n", + i+1,points[i].x,points[i].y,end_points[i].x,end_points[i].y,angles[i],end_angles[i],radii[i],end_arcs[i], + end_centers[i].x,end_centers[i].y); + } + + wBool_t LH_main = TRUE, LH_first = TRUE; + + cornuData.pos[0] = end_points[0]; /*Start*/ + if (dp->type == NTO_CORNU3WAY) { + if (newTurnToeR < newTurnToeL) LH_first = FALSE; + cornuData.pos[1] = end_points[3]; /*Center for First Time */ + cornuData.pos[5] = end_points[3]; /*Center for last time*/ + } else if (newTurnRad1>=0.0) { + cornuData.pos[1] = end_points[1]; /*Left is dominant curve */ + newTurnToeR = newTurnToeL; + } else { + cornuData.pos[1] = end_points[2]; /*Right is dominant */ + newTurnToeR = newTurnToeL; + LH_main = FALSE; + } + + cornuData.pos[7] = end_points[1]; /*Left*/ + cornuData.pos[9] = end_points[2]; /*Right*/ + if (dp->type == NTO_CORNU3WAY) { + cornuData.pos[5] = end_points[3]; /*Center */ + } + + if (radii[0] == 0.0) /* Base */ + cornuData.center[0] = zero; + else { + cornuData.center[0].x = end_points[0].x; + cornuData.center[0].y = end_points[0].y + radii[0]; + } + if (radii[1] == 0.0) /* Left */ + cornuData.center[7] = zero; + else if (radii[1] >0.0) + Translate(&cornuData.center[7], cornuData.pos[7], -end_angles[1], radii[1]); + else + Translate(&cornuData.center[7], cornuData.pos[7], 180.0+end_angles[1], radii[1]); + + if (radii[2] == 0.0) /* Right */ + cornuData.center[9] = zero; + else if (radii[2] >0.0) + Translate(&cornuData.center[9], cornuData.pos[9], 180.0+end_angles[2], radii[2]); + else + Translate(&cornuData.center[9], cornuData.pos[9], -end_angles[2], radii[2]); + + if (dp->type == NTO_CORNU3WAY) { + if (radii[3] == 0.0) /* Center */ + cornuData.center[5] = zero; + else if (radii[3] >0.0) + Translate(&cornuData.center[5], cornuData.pos[5], -end_angles[3], radii[3]); + else + Translate(&cornuData.center[5], cornuData.pos[5], 180.0+end_angles[3], radii[3]); + } + + /* Set up for calculation of Toe(s) */ + + if (dp->type == NTO_CORNU3WAY) { + cornuData.center[1] = cornuData.center[5]; /*For Toe1 calc always use center */ + cornuData.center[3] = cornuData.center[5]; /*For Toe2 calc always use center*/ + } else if (LH_main) { + cornuData.center[1] = cornuData.center[7]; /* Dominant Curve Left */ + } else + cornuData.center[1] = cornuData.center[9]; /* Right */ + + cornuData.angle[0] = 270.0; /*Always*/ + if (dp->type == NTO_CORNU3WAY) { + cornuData.angle[1] = 90.0-end_angles[3]; + cornuData.angle[3] = 90.0-end_angles[3]; + cornuData.angle[5] = 90.0-end_angles[3]; /* Only used for 3way */ + } else if (LH_main) { + cornuData.angle[1] = 90.0-end_angles[1]; + } else { + cornuData.angle[1] = 90.0+end_angles[2]; + } + cornuData.angle[7] = 90.0-end_angles[1]; /*Left*/ + cornuData.angle[9] = 90.0+end_angles[2]; /*Right*/ + + cornuData.radius[0] = fabs(radii[0]); + if (dp->type == NTO_CORNU3WAY) { + cornuData.radius[1] = fabs(radii[3]); + cornuData.radius[3] = fabs(radii[3]); + cornuData.radius[5] = fabs(radii[3]); + } else if (LH_main) { + cornuData.radius[1] = fabs(radii[1]); + } else { + cornuData.radius[1] = fabs(radii[2]); + } + cornuData.radius[7] = fabs(radii[1]); /*Left*/ + cornuData.radius[9] = fabs(radii[2]); /*Right*/ + + /* Ready to find Toe points */ + + DYNARR_RESET( trkSeg_t, tempSegs_da ); + trkSeg_t * temp_p; + temp_p = &tempSegs(0); + + + DIST_T radius; + coOrd center; + ANGLE_T angle; + int inx,subSeg; + wBool_t back, neg; + + CallCornu0(&cornuData.pos[0],&cornuData.center[0],&cornuData.angle[0],&cornuData.radius[0],&tempSegs_da, FALSE); + + /* Override if a "Y" has zero radius at base to be a straight until the Toe + * We set the start of the curve to be at the Toe position */ + if (cornuData.radius[0] == 0.0) { + pos.x = end_points[0].x+(LH_first?newTurnToeL:newTurnToeR); + pos.y = 0.0; + angle = 90.0; + radius = 0.0; + center = zero; + + } else { + + /*Find Toe 1 from curve */ + + /*Get ToeAngle/Radius/Center for first toe */ + pos.x = end_points[0].x+(LH_first?newTurnToeL:newTurnToeR); + pos.y = end_points[0].y; /* This will be close to but not on the curve */ + angle = GetAngleSegs(tempSegs_da.cnt,(trkSeg_t *)(tempSegs_da.ptr),&pos,&inx,NULL,&back,&subSeg,&neg); + segPtr = &DYNARR_N(trkSeg_t, tempSegs_da, inx); + + if (segPtr->type == SEG_BEZTRK) { + segPtr = &DYNARR_N(trkSeg_t,segPtr->bezSegs,subSeg); + } + + if (segPtr->type == SEG_STRTRK) { + radius = 0.0; + center = zero; + } else if (segPtr->type == SEG_CRVTRK) { + center = segPtr->u.c.center; + radius = fabs(segPtr->u.c.radius); + } + } + + /* Set up 2-3 even if we don't use it */ + cornuData.pos[1] = pos; + cornuData.center[1] = center; + cornuData.angle[1] = angle; + cornuData.radius[1] = radius; + + cornuData.pos[2] = pos; + cornuData.center[2] = center; + cornuData.angle[2] = NormalizeAngle(180.0+angle); + cornuData.radius[2] = radius; + + if ((dp->type == NTO_CORNU3WAY) && (newTurnToeR!=newTurnToeL)) { + if (LH_first) { + cornuData.pos[6] = pos; + cornuData.center[6] = center; + cornuData.angle[6] = NormalizeAngle(180.0+angle); + cornuData.radius[6] = radius; + } else { + cornuData.pos[8] = pos; + cornuData.center[8] = center; + cornuData.angle[8] = NormalizeAngle(180.0+angle); + cornuData.radius[8] = radius; + } + } else { /* Just one toe */ + cornuData.pos[8] = pos; + cornuData.center[8] = center; + cornuData.angle[8] = NormalizeAngle(180.0+angle); + cornuData.radius[8] = radius; + + cornuData.pos[6] = pos; + cornuData.center[6] = center; + cornuData.angle[6] = NormalizeAngle(180.0+angle); + cornuData.radius[6] = radius; + } + + if (dp->type == NTO_CORNU3WAY) { + if (newTurnToeR!=newTurnToeL) { + /* Second Toe */ + pos.x = end_points[0].x+(LH_first?newTurnToeR:newTurnToeL); + pos.y = end_points[0].y; /* This will be close to but not on the curve */ + angle = GetAngleSegs(tempSegs_da.cnt,(trkSeg_t *)(tempSegs_da.ptr),&pos,&inx,NULL,&back,&subSeg,&neg); + segPtr = &DYNARR_N(trkSeg_t, tempSegs_da, inx); + + if (segPtr->type == SEG_BEZTRK) { + segPtr = &DYNARR_N(trkSeg_t,segPtr->bezSegs,subSeg); + } + + if (segPtr->type == SEG_STRTRK) { + radius = 0.0; + center = zero; + } else if (segPtr->type == SEG_CRVTRK) { + center = segPtr->u.c.center; + radius = fabs(segPtr->u.c.radius); + } + cornuData.pos[3] = pos; + cornuData.center[3] = center; + cornuData.angle[3] = angle; + cornuData.radius[3] = radius; + + cornuData.pos[4] = pos; + cornuData.center[4] = center; + cornuData.angle[4] = NormalizeAngle(180.0+angle); + cornuData.radius[4] = radius; + + if (LH_first) { + cornuData.pos[8] = pos; + cornuData.center[8] = center; + cornuData.angle[8] = NormalizeAngle(180.0+angle); + cornuData.radius[8] = radius; + + cornuData.pos[4] = pos; + cornuData.center[4] = center; + cornuData.angle[4] = NormalizeAngle(180.0+angle); + cornuData.radius[4] = radius; + } else { + cornuData.pos[6] = pos; + cornuData.center[6] = center; + cornuData.angle[6] = NormalizeAngle(180.0+angle); + cornuData.radius[6] = radius; + + cornuData.pos[4] = pos; + cornuData.center[4] = center; + cornuData.angle[4] = NormalizeAngle(180.0+angle); + cornuData.radius[4] = radius; + } + } else { //Set next center start to same place + cornuData.pos[4] = pos; + cornuData.center[4] = center; + cornuData.angle[4] = NormalizeAngle(180.0+angle); + cornuData.radius[4] = radius; + } + } + + static dynArr_t cornuSegs_da; + + ClearSegs(&tempSegs_da); + ClearSegs(&cornuSegs_da); + + int Toe1Seg = 0 , Toe2Seg = 0, CenterEndSeg = 0, LeftEndSeg = 0, RightEndSeg = 0; + + /* Override if at zero radius at base don't compute end */ + if (cornuData.radius[0] == 0.0) { + DYNARR_APPEND(trkSeg_t,tempSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,tempSegs_da); + temp_p->type = SEG_STRTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.l.pos[0] = zero; + temp_p->u.l.pos[1] = cornuData.pos[0]; +LogPrintf( "ctoDes1: P0(%f,%f) P1(%f,%f) \n", + temp_p->u.l.pos[0].x,temp_p->u.l.pos[0].y,temp_p->u.l.pos[1].x,temp_p->u.l.pos[1].y ); + } else { + DYNARR_APPEND(trkSeg_t,tempSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,tempSegs_da); + temp_p->type = SEG_CRVTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.c.radius = fabs(radii[0]);; + if (radii[0]>0.0) + temp_p->u.c.a0 = FindAngle(end_centers[0],end_points[0]); + else + temp_p->u.c.a0 = FindAngle(end_centers[0],points[0]); + temp_p->u.c.a1 = fabs(end_arcs[0]); + temp_p->u.c.center = end_centers[0]; + coOrd rp0,rp1; + Translate(&rp0,temp_p->u.c.center,temp_p->u.c.a0,temp_p->u.c.radius); + Translate(&rp1,temp_p->u.c.center,temp_p->u.c.a0+temp_p->u.c.a1,temp_p->u.c.radius); +LogPrintf( "ctoDes1: R(%f) A0(%f) A1(%f) C(%f,%f) P(%f,%f), EP(%f,%f) RP0(%f,%f) RP1(%f,%f)\n", + temp_p->u.c.radius,temp_p->u.c.a0,temp_p->u.c.a1,temp_p->u.c.center.x,temp_p->u.c.center.y, + points[0].x,points[0].y,end_points[0].x,end_points[0].y, + rp0.x,rp0.y,rp1.x,rp1.y); + } + + if ((cornuData.pos[0].x != cornuData.pos[1].x) || + (cornuData.pos[0].y != cornuData.pos[1].y) ) + CallCornuNoBez(&cornuData.pos[0],&cornuData.center[0],&cornuData.angle[0],&cornuData.radius[0],&tempSegs_da); + Toe1Seg = tempSegs_da.cnt; + + if (dp->type == NTO_CORNU3WAY) { + if (newTurnToeR!=newTurnToeL) { + /* Toe1 to Toe2 in tempSegs array */ + if ((cornuData.pos[2].x != cornuData.pos[3].x) || + (cornuData.pos[2].y != cornuData.pos[3].y) ) + CallCornuNoBez(&cornuData.pos[2],&cornuData.center[2],&cornuData.angle[2],&cornuData.radius[2],&cornuSegs_da); + + Toe2Seg = cornuSegs_da.cnt+Toe1Seg; + /* Add to second cornu to tempSegs array */ + AppendSegs(&tempSegs_da,&cornuSegs_da); + /* Get ready to reuse cornuSegs array*/ + ClearSegs(&cornuSegs_da); + } else { + Toe2Seg = Toe1Seg; //No Toe2 + } + /* Toe2 to Center in cornuSegs array */ + CallCornuNoBez(&cornuData.pos[4],&cornuData.center[4],&cornuData.angle[4],&cornuData.radius[4],&cornuSegs_da); + + if (cornuData.radius[5] == 0.0) { + DYNARR_APPEND(trkSeg_t,cornuSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,cornuSegs_da); + temp_p->type = SEG_STRTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.l.pos[0] = cornuData.pos[5]; + temp_p->u.l.pos[1] = end_points[3]; + LogPrintf( "ctoDes2: P0(%f,%f) P1(%f,%f) \n", + temp_p->u.l.pos[0].x,temp_p->u.l.pos[0].y,temp_p->u.l.pos[1].x,temp_p->u.l.pos[1].y ); + } else { + DYNARR_APPEND(trkSeg_t,cornuSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,cornuSegs_da); + temp_p->type = SEG_CRVTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.c.radius = fabs(radii[3]); + if (radii[3]>0) + temp_p->u.c.a0 = FindAngle(end_centers[3],points[3]); + else + temp_p->u.c.a0 = FindAngle(end_centers[3],end_points[3]); + temp_p->u.c.a1 = fabs(end_arcs[3]); + temp_p->u.c.center = end_centers[3]; + coOrd rp0,rp1; + Translate(&rp0,temp_p->u.c.center,temp_p->u.c.a0,temp_p->u.c.radius); + Translate(&rp1,temp_p->u.c.center,temp_p->u.c.a0+temp_p->u.c.a1,temp_p->u.c.radius); + LogPrintf( "ctoDes2: R(%f) A0(%f) A1(%f) C(%f,%f) P(%f,%f) EP(%f,%f) RP0(%f,%f) RP1(%f,%f)\n", + temp_p->u.c.radius,temp_p->u.c.a0,temp_p->u.c.a1,temp_p->u.c.center.x,temp_p->u.c.center.y, + points[3].x,points[3].y,end_points[3].x,end_points[3].y, + rp0.x,rp0.y,rp1.x,rp1.y); + } + + CenterEndSeg = cornuSegs_da.cnt+Toe2Seg; + /* Add to second cornu to tempSegs array */ + AppendSegs(&tempSegs_da,&cornuSegs_da); + /* Get ready to reuse cornuSegs array*/ + ClearSegs(&cornuSegs_da); + } else { + CenterEndSeg = Toe2Seg = Toe1Seg; //No Toe2, No Center + } + + /* Left in cornuSegs array*/ + CallCornuNoBez(&cornuData.pos[6],&cornuData.center[6],&cornuData.angle[6],&cornuData.radius[6],&cornuSegs_da); + + if (cornuData.radius[7] == 0.0) { + DYNARR_APPEND(trkSeg_t,cornuSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,cornuSegs_da); + temp_p->type = SEG_STRTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.l.pos[0] = cornuData.pos[7]; + temp_p->u.l.pos[1] = end_points[1]; +LogPrintf( "ctoDes2: P0(%f,%f) P1(%f,%f) \n", + temp_p->u.l.pos[0].x,temp_p->u.l.pos[0].y,temp_p->u.l.pos[1].x,temp_p->u.l.pos[1].y ); + } else { + DYNARR_APPEND(trkSeg_t,cornuSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,cornuSegs_da); + temp_p->type = SEG_CRVTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.c.radius = fabs(radii[1]); + if (radii[1]>0) + temp_p->u.c.a0 = FindAngle(end_centers[1],points[1]); + else + temp_p->u.c.a0 = FindAngle(end_centers[1],end_points[1]); + temp_p->u.c.a1 = fabs(end_arcs[1]); + temp_p->u.c.center = end_centers[1]; + coOrd rp0,rp1; + Translate(&rp0,temp_p->u.c.center,temp_p->u.c.a0,temp_p->u.c.radius); + Translate(&rp1,temp_p->u.c.center,temp_p->u.c.a0+temp_p->u.c.a1,temp_p->u.c.radius); +LogPrintf( "ctoDes2: R(%f) A0(%f) A1(%f) C(%f,%f) P(%f,%f) EP(%f,%f) RP0(%f,%f) RP1(%f,%f)\n", + temp_p->u.c.radius,temp_p->u.c.a0,temp_p->u.c.a1,temp_p->u.c.center.x,temp_p->u.c.center.y, + points[1].x,points[1].y,end_points[1].x,end_points[1].y, + rp0.x,rp0.y,rp1.x,rp1.y); + } + + LeftEndSeg = cornuSegs_da.cnt+CenterEndSeg; + + /* Add to second cornu to tempSegs array */ + AppendSegs(&tempSegs_da,&cornuSegs_da); + /* Get ready to reuse cornuSegs array*/ + ClearSegs(&cornuSegs_da); + + /* Right in cornuSegs array*/ + CallCornuNoBez(&cornuData.pos[8],&cornuData.center[8],&cornuData.angle[8],&cornuData.radius[8],&cornuSegs_da); + + if (cornuData.radius[9] == 0.0) { + DYNARR_APPEND(trkSeg_t,cornuSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,cornuSegs_da); + temp_p->type = SEG_STRTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.l.pos[0] = cornuData.pos[9]; + temp_p->u.l.pos[1] = end_points[2]; +LogPrintf( "ctoDes2: P0(%f,%f) P1(%f,%f) \n", + temp_p->u.l.pos[0].x,temp_p->u.l.pos[0].y,temp_p->u.l.pos[1].x,temp_p->u.l.pos[1].y ); + } else { + DYNARR_APPEND(trkSeg_t,cornuSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,cornuSegs_da); + temp_p->type = SEG_CRVTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.c.radius = fabs(radii[2]); + if (radii[2]<0) + temp_p->u.c.a0 = FindAngle(end_centers[2],points[2]); + else + temp_p->u.c.a0 = FindAngle(end_centers[2],end_points[2]); + temp_p->u.c.a1 = fabs(end_arcs[2]); + temp_p->u.c.center = end_centers[2]; + coOrd rp0,rp1; + Translate(&rp0,temp_p->u.c.center,temp_p->u.c.a0,temp_p->u.c.radius); + Translate(&rp1,temp_p->u.c.center,temp_p->u.c.a0+temp_p->u.c.a1,temp_p->u.c.radius); +LogPrintf( "ctoDes2: R(%f) A0(%f) A1(%f) C(%f,%f) P(%f,%f) EP(%f,%f) RP0(%f,%f) RP1(%f,%f)\n", + temp_p->u.c.radius,temp_p->u.c.a0,temp_p->u.c.a1,temp_p->u.c.center.x,temp_p->u.c.center.y, + points[2].x,points[2].y,end_points[2].x,end_points[2].y, + rp0.x,rp0.y,rp1.x,rp1.y); + } + + RightEndSeg = cornuSegs_da.cnt+LeftEndSeg; + + /*Add Third Part to tempSegs Array */ + AppendSegs(&tempSegs_da,&cornuSegs_da); + /* Safety - clear out cornu Array */ + ClearSegs(&cornuSegs_da); + + if (tempSegs_da.cnt >128 ) { + NoticeMessage( MSG_TODSGN_CORNU_TOO_COMPLEX, _("Ok"), NULL ); + return NULL; + } + + /* Generate Paths */ + + static char pathChar[512]; + if (dp->type == NTO_CORNU3WAY) { + strcpy(pathChar,"Normal"); /* Also resets array */ + pathLen = strlen(pathChar)+1; + for (uint8_t i=0;i<CenterEndSeg;i++) { + pathChar[pathLen] = i+1; + pathLen++; + } + pathChar[pathLen] = 0; + pathLen++; + pathChar[pathLen] = 0; + pathLen++; + sprintf(&pathChar[pathLen],"%s","Left"); + pathLen += strlen(&pathChar[pathLen])+1; + } else { + strcpy(pathChar,"Left"); + pathLen = strlen(pathChar)+1; + } + for (uint8_t i=0;i<Toe1Seg;i++) { + pathChar[pathLen] = i+1; + pathLen++; + } + if ((dp->type == NTO_CORNU3WAY) && !LH_first && (newTurnToeR != newTurnToeL)) { + for (uint8_t i=Toe1Seg;i<Toe2Seg;i++) { + pathChar[pathLen] = i+1; + pathLen++; + } + } + + for (uint8_t i=CenterEndSeg;i<LeftEndSeg;i++) { + pathChar[pathLen] = i+1; + pathLen++; + } + + pathChar[pathLen] = 0; + pathLen++; + pathChar[pathLen] = 0; + pathLen++; + + sprintf(&pathChar[pathLen],"%s","Right"); + pathLen += strlen(&pathChar[pathLen])+1; + + for (uint8_t i=0;i<Toe1Seg;i++) { + pathChar[pathLen] = i+1; + pathLen++; + } + if ((dp->type == NTO_CORNU3WAY) && LH_first && (newTurnToeR != newTurnToeL)) { + for (uint8_t i=Toe1Seg;i<Toe2Seg;i++) { + pathChar[pathLen] = i+1; + pathLen++; + } + } + for (uint8_t i=LeftEndSeg;i<RightEndSeg;i++) { + pathChar[pathLen] = i+1; + pathLen++; + } + pathChar[pathLen] = 0; + pathLen++; + pathChar[pathLen] = 0; + pathLen++; + pathChar[pathLen] = 0; + pathLen++; + + pp->paths = (signed char *)pathChar; + segCnt = tempSegs_da.cnt; + + break; +#endif case NTO_D_SLIP: case NTO_S_SLIP: case NTO_CROSSING: + if (dp->type == NTO_D_SLIP) { + if (newTurnSlipMode == 1) + pp = &DoubleSlipSchema2; + else + pp = &DoubleSlipSchema; + } 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[1].x = (newTurnLen0); + pos.y = 0; pos.x = (newTurnLen0)/2.0; + coOrd cpos = pos; + Translate( &points[3], pos, 90.0+angle0, (newTurnLen1)/2.0 ); points[2].y = - points[3].y; - points[2].x = (newTurnLen1)-points[3].x; + points[2].x = pos.x-(points[3].x-pos.x); if (dp->type != NTO_CROSSING) { - Translate( &pos, points[3], 90.0+angle1, -newTurnTrackGauge ); + Translate( &pos, points[3], 90.0+angle0, -newTurnTrackGauge ); if (!ComputeCurve( &points[4], &points[5], &radii[0], - pos.x, fabs(pos.y), angle1 )) /*???*/ + pos.x, fabs(pos.y), angle0 )) /*???*/ return NULL; radii[1] = - radii[0]; points[5].y = - points[5].y; - points[6].y = 0; points[6].x = (newTurnLen1)-points[4].x; + points[6].y = 0; points[6].x = cpos.x-(points[4].x-cpos.x); points[7].y = -points[5].y; - points[7].x = (newTurnLen1)-points[5].x; + points[7].x = cpos.x-(points[5].x-cpos.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; + tempEndPts(2).pos = points[2]; tempEndPts(2).angle = 270.0+angle0; + tempEndPts(3).pos = points[3]; tempEndPts(3).angle = 90.0+angle0; 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; + d = (newTurnLen0)/2.0 - newTurnTrackGauge; if (d < 0.0) { NoticeMessage( MSG_TODSGN_CROSSOVER_TOO_SHORT, _("Ok"), NULL ); return NULL; } - angle1 = R2D( atan2( (newTurnOff1), d ) ); + angle0 = R2D( atan2( fabs(newTurnOff0), 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); + points[1].y = 0.0; points[1].x = (newTurnLen0); + points[2].y = fabs(newTurnOff0); points[2].x = 0.0; + points[3].y = fabs(newTurnOff0); points[3].x = (newTurnLen0); if (!ComputeCurve( &points[4], &points[5], &radii[1], - (newTurnLen1)/2.0, (newTurnOff1)/2.0, angle1 ) ) + (newTurnLen0)/2.0, fabs(newTurnOff0)/2.0, angle0 ) ) 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[6].y = 0.0; points[6].x = (newTurnLen0)-points[4].x; + points[7].y = points[5].y; points[7].x = (newTurnLen0)-points[5].x; + points[8].y = fabs(newTurnOff0); points[8].x = points[4].x; + points[9].y = fabs(newTurnOff0)-points[5].y; points[9].x = points[5].x; + points[10].y = fabs(newTurnOff0); 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; @@ -1235,7 +2078,7 @@ static toDesignSchema_t * LoadSegs( 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); + points[1].y = 0/*(newTurnOff1)*/; points[1].x = (newTurnLen0); tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0; break; @@ -1243,17 +2086,17 @@ static toDesignSchema_t * LoadSegs( 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); + points[1].y = (newTurnLen0) * (1.0 - cos( D2R(angle0) ) ); + points[1].x = (newTurnLen0) * sin( D2R(angle0) ); + radii[0] = -(newTurnLen0); tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; - tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0-angle1; + tempEndPts(1).pos = points[1]; tempEndPts(1).angle = 90.0-angle0; 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); + points[1].y = 0/*(newTurnOff1)*/; points[1].x = (newTurnLen0); tempEndPts(0).pos = points[0]; tempEndPts(0).angle = 270.0; break; @@ -1274,33 +2117,312 @@ static toDesignSchema_t * LoadSegs( } } - 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]; +#ifndef MKTURNOUT + if(dp->type == NTO_CORNU) { + DYNARR_SET( trkEndPt_t, tempEndPts_da, 3 ); + + DIST_T end_length = minLength/2; + + // Adjust end_points to impose small fixed end segments + + for (int i=0;i<3;i++) { + if (radii[i] == 0.0) { + Translate(&end_points[i], points[i], 90-angles[i]+(i==0?0:180), end_length); + end_angles[i] = angles[i]; + } else { + Translate(&end_centers[i], points[i], -angles[i], radii[i]); + end_arcs[i] = (radii[i]>=0?1:-1)*R2D(end_length/fabs(radii[i])); + end_points[i] = points[i]; + Rotate(&end_points[i],end_centers[i],(i>0?1:-1)*end_arcs[i]); + end_angles[i] = angles[i]-(i>0?1:-1)*end_arcs[i]; + } +LogPrintf( "ctoDes0-%d: EP(%f,%f) NEP(%f,%f) EA(%f) NEA(%f) R(%f) ARC(%f) EC(%f,%f) \n", + i+1,points[i].x,points[i].y,end_points[i].x,end_points[i].y,angles[i],end_angles[i],radii[i],end_arcs[i], + end_centers[i].x,end_centers[i].y); + } + + + cornuData.pos[0] = end_points[0]; /*Start*/ + cornuData.pos[1] = end_points[2]; /*Outer*/ + cornuData.pos[3] = end_points[2]; /*Outer for second time*/ + cornuData.pos[5] = end_points[1]; /*Inner*/ + + + if (radii[0] == 0.0) /* Toe */ + cornuData.center[0] = zero; + else { + cornuData.center[0].x = end_points[0].x; + cornuData.center[0].y = end_points[0].y + radii[0]; + } + if (radii[1] == 0.0) /* Inner */ + cornuData.center[5] = zero; + else + Translate(&cornuData.center[5], cornuData.pos[5], -end_angles[1], radii[1]); + + if (radii[2] == 0.0) /* Outer */ + cornuData.center[1] = zero; + else + Translate(&cornuData.center[1], cornuData.pos[1], -end_angles[2], radii[2]); + cornuData.center[3] = cornuData.center[1]; + + cornuData.angle[0] = 270.0; + cornuData.angle[1] = 90.0-end_angles[2]; + cornuData.angle[3] = 90.0-end_angles[2]; + cornuData.angle[5] = 90.0-end_angles[1]; /*Inner*/ + + cornuData.radius[0] = fabs(radii[0]); + cornuData.radius[1] = fabs(radii[2]); + cornuData.radius[3] = fabs(radii[2]); + cornuData.radius[5] = fabs(radii[1]); /*Inner*/ + + DYNARR_RESET( trkSeg_t, tempSegs_da ); + trkSeg_t * temp_p, * cornu_p; + temp_p = &tempSegs(0); + + /*Map out the full outer curve */ + + CallCornu0(&cornuData.pos[0],&cornuData.center[0],&cornuData.angle[0],&cornuData.radius[0],&tempSegs_da, FALSE); + + /*Get ToeAngle/Radius/Center */ + int inx,subSeg; + wBool_t back, neg; + DIST_T radius; + coOrd center; + pos.x = end_points[0].x+newTurnToeL; + pos.y = end_points[0].y; /* This will be close to but not on the curve */ + ANGLE_T angle = GetAngleSegs(tempSegs_da.cnt,(trkSeg_t *)(tempSegs_da.ptr),&pos,&inx,NULL,&back,&subSeg,&neg); + segPtr = &DYNARR_N(trkSeg_t, tempSegs_da, inx); + + if (segPtr->type == SEG_BEZTRK) { + segPtr = &DYNARR_N(trkSeg_t,segPtr->bezSegs,subSeg); + } + + if (segPtr->type == SEG_STRTRK) { + radius = 0.0; + center = zero; + } else if (segPtr->type == SEG_CRVTRK) { + center = segPtr->u.c.center; + radius = fabs(segPtr->u.c.radius); + } + cornuData.pos[1] = pos; + cornuData.center[1] = center; + cornuData.angle[1] = angle; + cornuData.radius[1] = radius; + cornuData.pos[2] = pos; + cornuData.center[2] = center; + cornuData.angle[2] = NormalizeAngle(180.0+angle); + cornuData.radius[2] = radius; + cornuData.pos[4] = pos; + cornuData.center[4] = center; + cornuData.angle[4] = NormalizeAngle(180.0+angle); + cornuData.radius[4] = radius; + + static dynArr_t cornuSegs_da; + + ClearSegs(&tempSegs_da); + ClearSegs(&cornuSegs_da); + + /* Override if at zero radius at base don't compute end */ + if (cornuData.radius[0] == 0.0) { + DYNARR_APPEND(trkSeg_t,tempSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,tempSegs_da); + temp_p->type = SEG_STRTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.l.pos[0] = zero; + temp_p->u.l.pos[1] = cornuData.pos[1]; +LogPrintf( "ctoDes1: P0(%f,%f) P1(%f,%f) \n", + temp_p->u.l.pos[0].x,temp_p->u.l.pos[0].y,temp_p->u.l.pos[1].x,temp_p->u.l.pos[1].y ); + } else { + DYNARR_APPEND(trkSeg_t,tempSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,tempSegs_da); + temp_p->type = SEG_CRVTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.c.radius = fabs(radii[0]);; + if (radii[0]>0.0) + temp_p->u.c.a0 = FindAngle(end_centers[0],end_points[0]); + else + temp_p->u.c.a0 = FindAngle(end_centers[0],points[0]); + temp_p->u.c.a1 = fabs(end_arcs[0]); + temp_p->u.c.center = end_centers[0]; + coOrd rp0,rp1; + Translate(&rp0,temp_p->u.c.center,temp_p->u.c.a0,temp_p->u.c.radius); + Translate(&rp1,temp_p->u.c.center,temp_p->u.c.a0+temp_p->u.c.a1,temp_p->u.c.radius); +LogPrintf( "ctoDes1: R(%f) A0(%f) A1(%f) C(%f,%f) P(%f,%f), EP(%f,%f) RP0(%f,%f) RP1(%f,%f)\n", + temp_p->u.c.radius,temp_p->u.c.a0,temp_p->u.c.a1,temp_p->u.c.center.x,temp_p->u.c.center.y, + points[0].x,points[0].y,end_points[0].x,end_points[0].y, + rp0.x,rp0.y,rp1.x,rp1.y); + + /* Base to Toe in tempSegs array */ + CallCornuNoBez(&cornuData.pos[0],&cornuData.center[0],& cornuData.angle[0],&cornuData.radius[0],&tempSegs_da); + } + + int ToeSeg = tempSegs_da.cnt; + + /* Toe to Outer in cornuSegs array */ + CallCornuNoBez(&cornuData.pos[2],&cornuData.center[2],&cornuData.angle[2],&cornuData.radius[2],&cornuSegs_da); + + cornu_p = (trkSeg_p)cornuSegs_da.ptr; + + if (cornuData.radius[3] == 0.0) { + DYNARR_APPEND(trkSeg_t,cornuSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,cornuSegs_da); + temp_p->type = SEG_STRTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.l.pos[0] = cornuData.pos[3]; + temp_p->u.l.pos[1] = end_points[2]; +LogPrintf( "ctoDes2: P0(%f,%f) P1(%f,%f) \n", + temp_p->u.l.pos[0].x,temp_p->u.l.pos[0].y,temp_p->u.l.pos[1].x,temp_p->u.l.pos[1].y ); + } else { + DYNARR_APPEND(trkSeg_t,cornuSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,cornuSegs_da); + temp_p->type = SEG_CRVTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.c.radius = fabs(radii[2]); + if (radii[2]>0) + temp_p->u.c.a0 = FindAngle(end_centers[2],points[2]); + else + temp_p->u.c.a0 = FindAngle(end_centers[2],end_points[2]); + temp_p->u.c.a1 = fabs(end_arcs[2]); + temp_p->u.c.center = end_centers[2]; + coOrd rp0,rp1; + Translate(&rp0,temp_p->u.c.center,temp_p->u.c.a0,temp_p->u.c.radius); + Translate(&rp1,temp_p->u.c.center,temp_p->u.c.a0+temp_p->u.c.a1,temp_p->u.c.radius); +LogPrintf( "ctoDes2: R(%f) A0(%f) A1(%f) C(%f,%f) P(%f,%f) EP(%f,%f) RP0(%f,%f) RP1(%f,%f)\n", + temp_p->u.c.radius,temp_p->u.c.a0,temp_p->u.c.a1,temp_p->u.c.center.x,temp_p->u.c.center.y, + points[2].x,points[2].y,end_points[2].x,end_points[2].y, + rp0.x,rp0.y,rp1.x,rp1.y); + } + + int OuterEndSeg = cornuSegs_da.cnt + ToeSeg; + + /* Add to second cornu to tempSegs array */ + AppendSegs(&tempSegs_da,&cornuSegs_da); + + /* Get ready to reuse cornuSegs array*/ + ClearSegs(&cornuSegs_da); + + /* Toe to Inner in cornuSegs array*/ + CallCornuNoBez(&cornuData.pos[4],&cornuData.center[4],&cornuData.angle[4],&cornuData.radius[4],&cornuSegs_da); + + if (cornuData.radius[5] == 0.0) { + DYNARR_APPEND(trkSeg_t,cornuSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,cornuSegs_da); + temp_p->type = SEG_STRTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.l.pos[0] = cornuData.pos[5]; + temp_p->u.l.pos[1] = points[1]; +LogPrintf( "ctoDes3: P0(%f,%f) P1(%f,%f) \n", + temp_p->u.l.pos[0].x,temp_p->u.l.pos[0].y,temp_p->u.l.pos[1].x,temp_p->u.l.pos[1].y ); + } else { + DYNARR_APPEND(trkSeg_t,cornuSegs_da,1); + temp_p = &DYNARR_LAST(trkSeg_t,cornuSegs_da); + temp_p->type = SEG_CRVTRK; + temp_p->color = wDrawColorBlack; + temp_p->width = 0.0; + temp_p->u.c.radius = fabs(radii[1]); + if (radii[1]>0) + temp_p->u.c.a0 = FindAngle(end_centers[1],points[1]); + else + temp_p->u.c.a0 = FindAngle(end_centers[1],end_points[1]); + temp_p->u.c.a1 = fabs(end_arcs[1]); + temp_p->u.c.center = end_centers[1]; + coOrd rp0,rp1; + Translate(&rp0,temp_p->u.c.center,temp_p->u.c.a0,temp_p->u.c.radius); + Translate(&rp1,temp_p->u.c.center,temp_p->u.c.a0+temp_p->u.c.a1,temp_p->u.c.radius); +LogPrintf( "ctoDes3: R(%f) A0(%f) A1(%f) C(%f,%f) P(%f,%f) EP(%f,%f) RP0(%f,%f) RP1(%f,%f)\n", + temp_p->u.c.radius,temp_p->u.c.a0,temp_p->u.c.a1,temp_p->u.c.center.x,temp_p->u.c.center.y, + points[1].x,points[1].y,end_points[1].x,end_points[1].y, + rp0.x,rp0.y,rp1.x,rp1.y); + } + + int InnerEndSeg = cornuSegs_da.cnt + OuterEndSeg; + + /*Add Third Part to tempSegs Array */ + AppendSegs(&tempSegs_da,&cornuSegs_da); + + + /* Safety - clear out cornu Array */ + ClearSegs(&cornuSegs_da); + + if (tempSegs_da.cnt >128 ) { + NoticeMessage( MSG_TODSGN_CORNU_TOO_COMPLEX, _("Ok"), NULL ); + return NULL; + } + + static char pathChar[512]; + strcpy(pathChar,"Normal"); /* Also resets array */ + + pathLen = strlen(pathChar)+1; + + for (uint8_t i=0;i<OuterEndSeg;i++) { + pathChar[pathLen] = i+1; + pathLen++; + } + pathChar[pathLen] = 0; + pathLen++; + pathChar[pathLen] = 0; + pathLen++; + + sprintf(&pathChar[pathLen],"%s","Reverse"); + + pathLen += strlen(&pathChar[pathLen])+1; + for (uint8_t i=0;i<ToeSeg;i++) { + pathChar[pathLen] = i+1; + pathLen++; + } + for (uint8_t i=OuterEndSeg;i<InnerEndSeg;i++) { + pathChar[pathLen] = i+1; + pathLen++; + } + pathChar[pathLen] = 0; + pathLen++; + pathChar[pathLen] = 0; + pathLen++; + pathChar[pathLen] = 0; + pathLen++; + + pp->paths = (signed char *)pathChar; + segCnt = tempSegs_da.cnt; + } +#endif + + if (!( (dp->type== NTO_CORNU) || (dp->type == NTO_CORNUWYE) || (dp->type == NTO_CORNU3WAY))) { + 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 == 3) { + /* cornu */ + } else 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]; + + } } } @@ -1321,7 +2443,7 @@ 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 ) { + if ( sp0->type != SEG_STRTRK && sp0->type != SEG_CRVTRK && sp0->type != SEG_BEZTRK ) { DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); tempSegs(tempSegs_da.cnt-1) = *sp0; } @@ -1477,7 +2599,7 @@ static void NewTurnPrint( newTurnout_d.size.y/2.0 ); DrawStraightTrack( &newTurnout_d, pos, p0, tempEndPts(ep).angle+270.0, - NULL, newTurnTrackGauge, wDrawColorBlack, 0 ); + NULL, wDrawColorBlack, 0 ); } if ( !wPrintPageEnd( newTurnout_d.d ) ) @@ -1538,7 +2660,7 @@ static void NewTurnOk( void * context ) cp = Strcpytrimed( cp, newTurnLeftPartno, TRUE ); strcpy( cp, "\"" ); cp += 1; - if (curDesign->type == NTO_REGULAR || curDesign->type == NTO_CURVED) { + if (curDesign->type == NTO_REGULAR || curDesign->type == NTO_CURVED || curDesign->type == NTO_CORNU ) { strcpy( cp, " \"" ); cp += 2; cp = Strcpytrimed( cp, newTurnRightDesc, TRUE ); @@ -1553,15 +2675,17 @@ static void NewTurnOk( void * context ) 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; + 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; + case Rad_e: + break; } sprintf( cp, " %0.6f", flt ); cp += strlen(cp); @@ -1576,8 +2700,17 @@ static void NewTurnOk( void * context ) CopyNonTracks( customTurnout1 ); if ( customTurnout1 ) customTurnout1->segCnt = 0; + + DIST_T * radii_ends = NULL; + + if ((curDesign->type == NTO_CORNU) || + (curDesign->type == NTO_CORNUWYE) || + (curDesign->type == NTO_CORNU3WAY)) { + radii_ends = &radii[0]; + } + to = CreateNewTurnout( newTurnScaleName, tempCustom, tempSegs_da.cnt, &tempSegs(0), - pathLen, pp->paths, tempEndPts_da.cnt, &tempEndPts(0), FALSE ); + pathLen, pp->paths, tempEndPts_da.cnt, &tempEndPts(0), radii, FALSE ); to->customInfo = customInfoP; #endif if (f) { @@ -1593,6 +2726,7 @@ static void NewTurnOk( void * context ) switch (curDesign->type) { case NTO_REGULAR: points[2].y = - points[2].y; + points[3].y = - points[3].y; points[4].y = - points[4].y; radii[0] = - radii[0]; LoadSegs( curDesign, FALSE, &pathLen ); @@ -1606,7 +2740,7 @@ static void NewTurnOk( void * context ) if ( customTurnout2 ) customTurnout2->segCnt = 0; to = CreateNewTurnout( newTurnScaleName, tempCustom, tempSegs_da.cnt, &tempSegs(0), - pathLen, pp->paths, tempEndPts_da.cnt, &tempEndPts(0), FALSE ); + pathLen, pp->paths, tempEndPts_da.cnt, &tempEndPts(0), NULL, FALSE ); to->customInfo = customInfoP; #endif if (f) { @@ -1619,26 +2753,41 @@ static void NewTurnOk( void * context ) } break; case NTO_CURVED: - points[1].y = - points[1].y; + case NTO_CORNU: points[2].y = - points[2].y; + points[1].y = - points[1].y; + points[3].y = - points[3].y; points[4].y = - points[4].y; + points[5].y = - points[5].y; points[6].y = - points[6].y; - radii[0] = - radii[0]; - radii[1] = - radii[1]; + radii[0] = -radii[0]; + radii[1] = -radii[1]; + radii[2] = -radii[2]; + radii[3] = -radii[3]; + radii[4] = -radii[4]; + radii[5] = -radii[5]; + radii[6] = -radii[6]; + angles[0] = -angles[0]; + angles[1] = -angles[1]; + angles[2] = -angles[2]; + angles[3] = -angles[3]; + angles[4] = -angles[4]; + angles[5] = -angles[5]; + angles[6] = -angles[6]; 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; + //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 ); + pathLen, pp->paths, tempEndPts_da.cnt, &tempEndPts(0), NULL, FALSE ); to->customInfo = customInfoP; #endif if (f) { @@ -1782,6 +2931,13 @@ static void SetupTurnoutDesignerW( toDesignDesc_t * newDesign ) turnDesignPLs[I_TOANGMODE].option |= PDO_DLGIGNORE; wControlShow( turnDesignPLs[I_TOANGMODE].control, FALSE ); } + if (curDesign->type == NTO_D_SLIP) { + turnDesignPLs[I_TOSLIPMODE].option &= ~PDO_DLGIGNORE; + wControlShow( turnDesignPLs[I_TOSLIPMODE].control, TRUE ); + } else { + turnDesignPLs[I_TOSLIPMODE].option |= PDO_DLGIGNORE; + wControlShow( turnDesignPLs[I_TOSLIPMODE].control, FALSE ); + } w = turnDesignWidth-w; wStringSetWidth( (wString_p)turnDesignPLs[I_TOMANUF].control, w ); @@ -1795,18 +2951,25 @@ static void SetupTurnoutDesignerW( toDesignDesc_t * newDesign ) static void ShowTurnoutDesigner( void * context ) { + wBool_t sameTurnout = FALSE; if (recordF) fprintf( recordF, TURNOUTDESIGNER " SHOW %s\n", ((toDesignDesc_t*)context)->label ); newTurnScaleName = curScaleName; newTurnTrackGauge = trackGauge; + if (context && (curDesign == context)) + sameTurnout = TRUE; 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; + if (!sameTurnout) { /* Clear Values unless same as last time */ + newTurnRightDesc[0] = '\0'; + newTurnRightPartno[0] = '\0'; + newTurnLeftDesc[0] = '\0'; + newTurnLeftPartno[0] = '\0'; + newTurnOff0 = newTurnLen0 = newTurnAngle0 = newTurnRad0 = + newTurnOff1 = newTurnLen1 = newTurnAngle1 = newTurnRad1 = + newTurnOff2 = newTurnLen2 = newTurnAngle2 = newTurnRad2 = + newTurnOff3 = newTurnLen3 = newTurnAngle3 = newTurnRad3 = + newTurnToeL = newTurnToeR = 0.0; + } ParamLoadControls( &turnDesignPG ); ParamGroupRecord( &turnDesignPG ); customTurnout1 = NULL; @@ -1849,7 +3012,7 @@ EXPORT void EditCustomTurnout( turnoutInfo_t * to, turnoutInfo_t * to1 ) strcpy( newTurnManufacturer, mfg ); strcpy( newTurnLeftDesc, descL ); strcpy( newTurnLeftPartno, partL ); - if (dp->type == NTO_REGULAR || dp->type == NTO_CURVED) { + if (dp->type == NTO_REGULAR || dp->type == NTO_CURVED || dp->type == NTO_CORNU) { if ( ! GetArgs( cp, "qqc", &descR, &partR, &cp )) return; strcpy( newTurnRightDesc, descR ); @@ -1872,6 +3035,8 @@ EXPORT void EditCustomTurnout( turnoutInfo_t * to, turnoutInfo_t * to1 ) break; case Angle_e: break; + case Rad_e: + break; } } rgb = 0; @@ -1917,6 +3082,7 @@ EXPORT void EditCustomTurnout( turnoutInfo_t * to, turnoutInfo_t * to1 ) break; case SEG_STRTRK: case SEG_CRVTRK: + case SEG_BEZTRK: break; default: segsDiff = TRUE; @@ -1924,34 +3090,42 @@ EXPORT void EditCustomTurnout( turnoutInfo_t * to, turnoutInfo_t * to1 ) } } else { for ( sp0=to->segs; (!segsDiff) && sp0<&to->segs[to->segCnt]; sp0++ ) { - if ( sp0->type != SEG_STRTRK && sp0->type != SEG_CRVTRK ) + if ( sp0->type != SEG_STRTRK && sp0->type != SEG_CRVTRK && sp0->type != SEG_BEZTRK) segsDiff = TRUE; } } } - if ( (!segsDiff) && to1 && (dp->type==NTO_REGULAR||dp->type==NTO_CURVED) ) { + if ( (!segsDiff) && to1 && (dp->type==NTO_REGULAR||dp->type==NTO_CURVED||dp->type == NTO_CORNU) ) { if ( dp->type==NTO_REGULAR ) { points[2].y = - points[2].y; - points[4].y = - points[4].y; radii[0] = - radii[0]; - } else { + } else if (dp->type == 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]; + } else { + points[2].y = - points[2].y; + points[1].y = - points[1].y; + angles[1] = -angles[1]; + angles[2] = -angles[2]; 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 { + } else if (dp->type == 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]; + } else { + points[2].y = - points[2].y; + points[1].y = - points[1].y; + angles[1] = -angles[1]; + angles[2] = -angles[2]; radii[0] = - radii[0]; radii[1] = - radii[1]; } @@ -1982,6 +3156,7 @@ EXPORT void EditCustomTurnout( turnoutInfo_t * to, turnoutInfo_t * to1 ) break; case SEG_STRTRK: case SEG_CRVTRK: + case SEG_BEZTRK: break; default: segsDiff = TRUE; @@ -1989,7 +3164,7 @@ EXPORT void EditCustomTurnout( turnoutInfo_t * to, turnoutInfo_t * to1 ) } } else { for ( sp0=to1->segs; (!segsDiff) && sp0<&to1->segs[to1->segCnt]; sp0++ ) { - if ( sp0->type != SEG_STRTRK && sp0->type != SEG_CRVTRK ) + if ( sp0->type != SEG_STRTRK && sp0->type != SEG_CRVTRK && sp0->type != SEG_BEZTRK) segsDiff = TRUE; } } @@ -2033,7 +3208,7 @@ EXPORT void InitNewTurn( wMenu_p m ) #include <stdio.h> #include <stdarg.h> -char message[1024]; +char message[STR_HUGE_SIZE]; char * curScaleName; double trackGauge; long units = 0; @@ -2201,11 +3376,11 @@ EXPORT BOOL_T WriteSegs( 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; + segs[i].u.p.pts[j].pt.x, segs[i].u.p.pts[j].pt.y )>0; break; } } - rc &= fprintf( f, "\tEND\n" )>0; + rc &= fprintf( f, "\t%s\n", END_SEGS )>0; return rc; } @@ -2344,7 +3519,7 @@ int main ( int argc, char * argv[] ) if (argc != 7) Usage(argc0,argv0); strcpy( newTurnLeftDesc, *argv++ ); strcpy( newTurnLeftPartno, *argv++ ); - newTurnLen1 = GetDim(atof( *argv++ )); + newTurnLen0 = GetDim(atof( *argv++ )); curDesign = &StrSectionDesc; NewTurnOk( &StrSectionDesc ); break; @@ -2352,7 +3527,7 @@ int main ( int argc, char * argv[] ) if (argc != 7) Usage(argc0,argv0); strcpy( newTurnLeftDesc, *argv++ ); strcpy( newTurnLeftPartno, *argv++ ); - newTurnLen1 = GetDim(atof( *argv++ )); + newTurnLen0 = GetDim(atof( *argv++ )); curDesign = &StrSectionDesc; NewTurnOk( &StrSectionDesc ); break; @@ -2360,9 +3535,9 @@ int main ( int argc, char * argv[] ) if (argc != 8) Usage(argc0,argv0); strcpy( newTurnLeftDesc, *argv++ ); strcpy( newTurnLeftPartno, *argv++ ); + newTurnLen0 = GetDim(atof( *argv++ )); newTurnLen1 = GetDim(atof( *argv++ )); - newTurnLen2 = GetDim(atof( *argv++ )); - sprintf( specialLine, "\tX adjustable %0.6f %0.6f", newTurnLen1, newTurnLen2 ); + sprintf( specialLine, "\tX adjustable %0.6f %0.6f", newTurnLen0, newTurnLen1 ); curDesign = &StrSectionDesc; NewTurnOk( &StrSectionDesc ); break; @@ -2370,8 +3545,8 @@ int main ( int argc, char * argv[] ) if (argc != 8) Usage(argc0,argv0); strcpy( newTurnLeftDesc, *argv++ ); strcpy( newTurnLeftPartno, *argv++ ); - newTurnLen1 = GetDim(atof( *argv++ )); - newTurnAngle1 = atof( *argv++ ); + newTurnLen0 = GetDim(atof( *argv++ )); + newTurnAngle0 = atof( *argv++ ); curDesign = &CrvSectionDesc; NewTurnOk( &CrvSectionDesc ); break; @@ -2381,10 +3556,10 @@ int main ( int argc, char * 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++ )); + newTurnAngle0 = atof( *argv++ ); + newTurnOff0 = GetDim(atof( *argv++ )); + newTurnLen1 = GetDim(atof( *argv++ )); curDesign = &RegDesc; NewTurnOk( &RegDesc ); break; @@ -2395,9 +3570,9 @@ int main ( int argc, char * argv[] ) strcpy( newTurnRightDesc, *argv++ ); strcpy( newTurnRightPartno, *argv++ ); radius = GetDim(atof( *argv++ )); - newTurnAngle1 = atof( *argv++ ); + newTurnAngle0 = atof( *argv++ ); newTurnLen0 = GetDim(atof( *argv++ )); - newTurnLen1 = radius * sin(D2R(newTurnAngle1)); + newTurnLen1 = radius * sin(D2R(newTurnAngle0)); newTurnOff1 = radius * (1-cos(D2R(newTurnAngle1))); curDesign = &RegDesc; NewTurnOk( &RegDesc ); @@ -2408,12 +3583,12 @@ int main ( int argc, char * 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++ )); + newTurnLen0 = GetDim(atof( *argv++ )); + newTurnAngle0 = atof( *argv++ ); + newTurnOff0 = GetDim(atof( *argv++ )); curDesign = &CrvDesc; NewTurnOk( &CrvDesc ); break; @@ -2424,13 +3599,13 @@ int main ( int argc, char * argv[] ) strcpy( newTurnRightDesc, *argv++ ); strcpy( newTurnRightPartno, *argv++ ); radius = GetDim(atof( *argv++ )); + newTurnAngle0 = atof( *argv++ ); + newTurnLen0 = radius * sin(D2R(newTurnAngle0)); + newTurnOff0 = radius * (1-cos(D2R(newTurnAngle0))); + 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; @@ -2440,12 +3615,12 @@ int main ( int argc, char * argv[] ) strcpy( newTurnLeftPartno, *argv++ ); strcpy( newTurnRightDesc, *argv++ ); strcpy( newTurnRightPartno, *argv++ ); + newTurnLen0 = GetDim(atof( *argv++ )); + newTurnAngle0 = atof( *argv++ ); + newTurnOff0 = 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 = &WyeDesc; NewTurnOk( &WyeDesc ); break; @@ -2453,13 +3628,13 @@ int main ( int argc, char * argv[] ) if (argc != 13) Usage(argc0,argv0); strcpy( newTurnLeftDesc, *argv++ ); strcpy( newTurnLeftPartno, *argv++ ); + newTurnLen2 = GetDim(atof( *argv++ )); newTurnLen0 = GetDim(atof( *argv++ )); + newTurnAngle0 = atof( *argv++ ); + newTurnOff0 = 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; @@ -2467,9 +3642,9 @@ int main ( int argc, char * argv[] ) if (argc<9) Usage(argc0,argv0); strcpy( newTurnLeftDesc, *argv++ ); strcpy( newTurnLeftPartno, *argv++ ); + newTurnLen0 = GetDim(atof( *argv++ )); + newTurnAngle0 = atof( *argv++ ); newTurnLen1 = GetDim(atof( *argv++ )); - newTurnAngle1 = atof( *argv++ ); - newTurnLen2 = GetDim(atof( *argv++ )); curDesign = &CrossingDesc; NewTurnOk( &CrossingDesc ); break; @@ -2477,9 +3652,9 @@ int main ( int argc, char * argv[] ) if (argc<9) Usage(argc0,argv0); strcpy( newTurnLeftDesc, *argv++ ); strcpy( newTurnLeftPartno, *argv++ ); + newTurnLen0 = GetDim(atof( *argv++ )); + newTurnAngle0 = atof( *argv++ ); newTurnLen1 = GetDim(atof( *argv++ )); - newTurnAngle1 = atof( *argv++ ); - newTurnLen2 = GetDim(atof( *argv++ )); curDesign = &SingleSlipDesc; NewTurnOk( &SingleSlipDesc ); break; @@ -2487,9 +3662,9 @@ int main ( int argc, char * argv[] ) strcpy( newTurnLeftDesc, *argv++ ); strcpy( newTurnLeftPartno, *argv++ ); if (argc<9) Usage(argc0,argv0); + newTurnLen0 = GetDim(atof( *argv++ )); + newTurnAngle0 = atof( *argv++ ); newTurnLen1 = GetDim(atof( *argv++ )); - newTurnAngle1 = atof( *argv++ ); - newTurnLen2 = GetDim(atof( *argv++ )); curDesign = &DoubleSlipDesc; NewTurnOk( &DoubleSlipDesc ); break; @@ -2497,8 +3672,8 @@ int main ( int argc, char * argv[] ) strcpy( newTurnLeftDesc, *argv++ ); strcpy( newTurnLeftPartno, *argv++ ); if (argc<8) Usage(argc0,argv0); - newTurnLen1 = GetDim(atof( *argv++ )); - newTurnOff1 = GetDim(atof( *argv++ )); + newTurnLen0 = GetDim(atof( *argv++ )); + newTurnOff0 = GetDim(atof( *argv++ )); curDesign = &DoubleCrossoverDesc; NewTurnOk( &DoubleCrossoverDesc ); break; @@ -2531,12 +3706,12 @@ int main ( int argc, char * argv[] ) 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, -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" ); + fprintf( stdout, "\t%s\n", END_SEGS ); break; default: fprintf( stderr, "Invalid command: %s\n", argv[-1] ); diff --git a/app/bin/ctrain.c b/app/bin/ctrain.c index fe41a39..b15cb91 100644 --- a/app/bin/ctrain.c +++ b/app/bin/ctrain.c @@ -58,6 +58,7 @@ struct extraData { long state; carItem_p item; double speed; + BOOL_T pencils; BOOL_T direction; BOOL_T autoReverse; trainStatus_e status; @@ -92,7 +93,7 @@ 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 wMenuPush_p trainPopupMI[10]; static track_p followTrain; static coOrd followCenter; static BOOL_T trainsTimeoutPending; @@ -311,6 +312,24 @@ BOOL_T TraverseTrack2( return TRUE; } +/*************** + * When a track is deleted, cross check that the Traverse Track reference is removed. + */ +EXPORT void CheckCarTraverse(track_p track) { + + track_p car; + for (car=NULL; TrackIterate(&car);) { + if (GetTrkType(car) == T_CAR) { + struct extraData * xx = GetTrkExtraData(car); + if (xx->trvTrk.trk == track) { + xx->trvTrk.trk=NULL; + xx->status = ST_NotOnTrack; + } + } + } + +} + static BOOL_T drawCarEnable = TRUE; @@ -358,7 +377,9 @@ static void DrawCar( } } - CarItemDraw(d, xx->item, color, xx->direction, IsLocoMaster(xx), coupler); + + + CarItemDraw(d, xx->item, color, xx->direction, IsLocoMaster(xx), coupler, xx->pencils, xx->trvTrk.trk); } @@ -467,10 +488,10 @@ static void DeleteCar( } -static void ReadCar( +static BOOL_T ReadCar( char * line) { - CarItemRead(line); + return CarItemRead(line); } @@ -521,6 +542,29 @@ static BOOL_T QueryCar(track_p trk, int query) } } +static BOOL_T StoreCar( + track_p car, + void **data, + long * len) { + + struct extraData *xx = GetTrkExtraData(car); + return StoreCarItem(xx->item,data,len); + +} + +static BOOL_T ReplayCar (track_p car, void *data,long len) { + + struct extraData *xx = GetTrkExtraData(car); + return ReplayCarItem(xx->item,data,len); + +} + + +static wBool_t CompareCar( track_cp trk1, track_cp trk2 ) +{ + return TRUE; +} + static trackCmd_t carCmds = { "CAR ", @@ -548,6 +592,16 @@ static trackCmd_t carCmds = { QueryCar, /* query */ NULL, /* ungroup */ NULL, /* flip */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ReplayCar, + StoreCar, + NULL, /*activate*/ + CompareCar }; /* @@ -699,13 +753,13 @@ static void SpeedRedraw( 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); + wDrawPolygon(d, pts, NULL, 4, drawColor, 0, 0, 0, 1, 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); + wDrawPolygon(d, pts, NULL, 4, drawColor, 0, 0, 0, 1, 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); @@ -1068,13 +1122,11 @@ static void MoveMainWindow( 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(); - MapRedraw(); - //DrawMapBoundingBox(TRUE); + panCenter = pos; + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + MainLayout( TRUE, TRUE ); // MoveTrainWindow } @@ -1197,7 +1249,7 @@ static void ControllerDialogUpdate( wButtonSetLabel((wButton_p)pg->paramPtr[I_DIR].control, (dlg->direction?_("Reverse"):_("Forward"))); SetTrainDirection(dlg->train); - DrawAllCars(); + TempRedraw(); // ctrain: change direction break; case I_STOP: @@ -1277,7 +1329,6 @@ static void DrawAllCars(void) drawCarEnable = TRUE; wDrawDelayUpdate(mainD.d, TRUE); wDrawRestoreImage(mainD.d); - DrawMarkers(); DrawPositionIndicators(); for (car=NULL; TrackIterate(&car);) { @@ -1291,7 +1342,7 @@ static void DrawAllCars(void) hi.y = lo.y + size.x; if (!OFF_MAIND(lo, hi)) { - DrawCar(car, &mainD, wDrawColorBlack); + DrawCar(car, &tempD, wDrawColorBlack); } } } @@ -1334,12 +1385,9 @@ static void PlaceCar( { 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); - } + CarItemFindCouplerMountPoint(xx->item, xx->trvTrk, xx->couplerPos); car->endPt[0].angle = xx->trvTrk.angle; Translate(&car->endPt[0].pos, xx->trvTrk.pos, car->endPt[0].angle, dists[0]); @@ -1787,6 +1835,9 @@ static BOOL_T CheckCoupling( /* Move second train back along track half a car length */ TraverseTrack2(&trvTrk1, distc/2.0-dist); + if ( trvTrk0.trk == NULL || trvTrk1.trk == NULL ) + // fell off the end of track + return FALSE; /* If tracks are not the same - dont couple */ if (trvTrk1.trk != trvTrk0.trk) { @@ -2076,7 +2127,7 @@ static BOOL_T MoveTrains(long timeD) } ControllerDialogSyncAll(); - DrawAllCars(); + TempRedraw(); // MoveTrains return trains_moved; } @@ -2427,6 +2478,8 @@ static BOOL_T TrainOnMovableTrack( #define DO_MUMASTER (5) #define DO_CHANGEDIR (6) #define DO_STOP (7) +#define DO_PENCILS_ON (8) +#define DO_PENCILS_OFF (9) static track_p trainFuncCar; static coOrd trainFuncPos; static wButton_p trainPauseB; @@ -2470,12 +2523,11 @@ static STATUS_T CmdTrain(wAction_t action, coOrd pos) Dtrain.state = 0; trk0 = NULL; tempSegs_da.cnt = 0; - DYNARR_SET(trkSeg_t, tempSegs_da, 8); + DYNARR_SET(trkSeg_t, tempSegs_da, 8); RestartTrains(); wButtonSetLabel(trainPauseB, (char*)goI); trainTime0 = 0; AttachTrains(); - DrawAllCars(); curTrainDlg->train = NULL; curTrainDlg->speed = -1; wDrawClear((wDraw_p)curTrainDlg->trainPGp->paramPtr[I_SLIDER].control); @@ -2484,6 +2536,7 @@ static STATUS_T CmdTrain(wAction_t action, coOrd pos) wShow(curTrainDlg->win); wControlShow((wControl_p)newcarB, (toolbarSet&(1<<BG_HOTBAR)) == 0); currCarItemPtr = NULL; + TempRedraw(); // CmdTrain C_START return C_CONTINUE; case C_TEXT: @@ -2516,6 +2569,7 @@ static STATUS_T CmdTrain(wAction_t action, coOrd pos) } xx = GetTrkExtraData(currCar); + xx->pencils = FALSE; dist = CarItemCoupledLength(xx->item)/2.0; Translate(&pos, xx->trvTrk.pos, xx->trvTrk.angle, dist); SetTrkEndPoint(currCar, 0, pos, xx->trvTrk.angle); @@ -2578,7 +2632,6 @@ static STATUS_T CmdTrain(wAction_t action, coOrd pos) SetCurTrain(trk0); } - DrawAllCars(); return C_CONTINUE; case C_MOVE: @@ -2589,7 +2642,6 @@ static STATUS_T CmdTrain(wAction_t action, coOrd pos) pos.x += delta.x; pos.y += delta.y; pos0 = pos; - /*DrawCars( &tempD, currCar, FALSE );*/ xx = GetTrkExtraData(currCar); trk0 = OnTrack(&pos0, FALSE, TRUE); @@ -2613,7 +2665,6 @@ static STATUS_T CmdTrain(wAction_t action, coOrd pos) PlaceTrainInit(currCar, trk0, pos0, xx->trvTrk.angle, (MyGetKeyState()&WKEY_SHIFT) == 0); ControllerDialogSync(curTrainDlg); - DrawAllCars(); return C_CONTINUE; case C_UP: @@ -2629,11 +2680,9 @@ static STATUS_T CmdTrain(wAction_t action, coOrd pos) } Dtrain.state = 1; - /*MainRedraw();*/ ControllerDialogSync(curTrainDlg); } - DrawAllCars(); InfoSubstituteControls(NULL, NULL); currCar = trk0 = NULL; currCarItemPtr = NULL; @@ -2668,14 +2717,12 @@ static STATUS_T CmdTrain(wAction_t action, coOrd pos) 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 - MapRedraw(); + MainRedraw(); //CmdTrain: Make sure track is redrawn after switch thrown } else { trk0 = FindCar(&pos); @@ -2704,6 +2751,14 @@ static STATUS_T CmdTrain(wAction_t action, coOrd pos) } xx = GetTrkExtraData(trainFuncCar); + if (xx->pencils) { + wMenuPushEnable(trainPopupMI[DO_PENCILS_OFF], TRUE); + wMenuPushEnable(trainPopupMI[DO_PENCILS_ON], FALSE); + } else { + wMenuPushEnable(trainPopupMI[DO_PENCILS_OFF], FALSE); + wMenuPushEnable(trainPopupMI[DO_PENCILS_ON], TRUE); + } + trk0 = FindMasterLoco(trainFuncCar,NULL); dir = IsAligned(xx->trvTrk.angle, FindAngle(xx->trvTrk.pos, trainFuncPos)) ? 0 : 1; @@ -2761,8 +2816,7 @@ static STATUS_T CmdTrain(wAction_t action, coOrd pos) wHide(curTrainDlg->win); } - MainRedraw(); - MapRedraw(); + MainRedraw(); // CmdTrain: Exit curTrainDlg->train = NULL; return C_CONTINUE; @@ -2844,8 +2898,6 @@ static void CmdTrainExit(void * junk) { Reset(); InfoSubstituteControls(NULL, NULL); - MainRedraw(); - MapRedraw(); } @@ -2879,6 +2931,14 @@ static void TrainFunc( break; + case DO_PENCILS_ON: + xx->pencils = TRUE; + break; + + case DO_PENCILS_OFF: + xx->pencils = FALSE; + break; + case DO_FLIPCAR: temp0 = GetTrkEndTrk(trainFuncCar,0); pos0 = GetTrkEndPos(trainFuncCar,0); @@ -3019,16 +3079,15 @@ static void TrainFunc( break; } - MainRedraw(); //Redraw if Train altered - MapRedraw(); + MainRedraw(); //TrainFunc: Redraw if Train altered if (trainsState == TRAINS_PAUSE) { RestartTrains(); } else { - DrawAllCars(); } } +EXPORT wIndex_t trainCmdInx; void InitCmdTrain(wMenu_p menu) { @@ -3036,8 +3095,8 @@ void InitCmdTrain(wMenu_p menu) 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, + trainCmdInx = AddMenuButton(menu, CmdTrain, "cmdTrain", _("Train"), + wIconCreatePixMap(train_xpm), LEVEL0_50, IC_POPUP3|IC_LCLICK|IC_RCLICK, 0, NULL); stopI = wIconCreatePixMap(ballred); goI = wIconCreatePixMap(ballgreen); @@ -3053,6 +3112,10 @@ void InitCmdTrain(wMenu_p menu) TrainFunc, (void*)DO_UNCOUPLE); trainPopupMI[DO_FLIPCAR] = wMenuPushCreate(trainPopupM, "", _("Flip Car"), 0, TrainFunc, (void*)DO_FLIPCAR); + trainPopupMI[DO_PENCILS_ON] = wMenuPushCreate(trainPopupM, "", _("Clearance Lines On"), 0, + TrainFunc, (void*)DO_PENCILS_ON); + trainPopupMI[DO_PENCILS_OFF] = wMenuPushCreate(trainPopupM, "", _("Clearance Lines Off"), 0, + TrainFunc, (void*)DO_PENCILS_OFF); trainPopupMI[DO_FLIPTRAIN] = wMenuPushCreate(trainPopupM, "", _("Flip Train"), 0, TrainFunc, (void*)DO_FLIPTRAIN); trainPopupMI[DO_MUMASTER] = wMenuPushCreate(trainPopupM, "", _("MU Master"), diff --git a/app/bin/ctrain.h b/app/bin/ctrain.h index daa083c..858860b 100644 --- a/app/bin/ctrain.h +++ b/app/bin/ctrain.h @@ -24,8 +24,11 @@ #define HAVE_CTRAIN_H #include "common.h" +#include "include/paramfile.h" #include "track.h" +extern wIndex_t trainCmdInx; + struct carItem_t; typedef struct carItem_t carItem_t; typedef carItem_t * carItem_p; @@ -34,8 +37,8 @@ typedef struct { ANGLE_T angle; } vector_t; -carItem_p currCarItemPtr; -wControl_p newCarControls[2]; +extern carItem_p currCarItemPtr; +extern wControl_p newCarControls[2]; void DoCarDlg( void ); BOOL_T CarItemRead( char * ); track_p NewCar( wIndex_t, carItem_p, coOrd, ANGLE_T ); @@ -44,7 +47,7 @@ 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 CarItemFindCouplerMountPoint( carItem_p, traverseTrack_t, coOrd[2] ); void CarItemSize( carItem_p, coOrd * ); char * CarItemNumber( carItem_p ); DIST_T CarItemCoupledLength( carItem_p ); @@ -53,9 +56,16 @@ 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 * ); +void CarItemDraw( drawCmd_p, carItem_p, wDrawColor, int, BOOL_T, vector_t *, BOOL_T, track_p ); +BOOL_T StoreCarItem (carItem_p item, void **data,long *len); +BOOL_T ReplayCarItem(carItem_p item, void *data,long len); +enum paramFileState GetCarPartCompatibility(int paramFileIndex, SCALEINX_T scaleIndex); +enum paramFileState GetCarProtoCompatibility(int paramFileIndex, SCALEINX_T scaleIndex); int CarAvailableCount( void ); BOOL_T TraverseTrack2( traverseTrack_p, DIST_T ); void FlipTraverseTrack( traverseTrack_p ); +void CheckCarTraverse( track_p trk); +void DeleteCarProto(int fileIndex); +void DeleteCarPart(int fileIndex); -#endif // !HAVE_CTRAIN_H
\ No newline at end of file +#endif // !HAVE_CTRAIN_H diff --git a/app/bin/cturnout.c b/app/bin/cturnout.c index eace782..150f381 100644 --- a/app/bin/cturnout.c +++ b/app/bin/cturnout.c @@ -27,6 +27,7 @@ #include "ccurve.h" #include "tbezier.h" +#include "tcornu.h" #include "cjoin.h" #include "compound.h" #include "cstraigh.h" @@ -37,7 +38,9 @@ #include "layout.h" #include "messages.h" #include "param.h" +#include "include/paramfile.h" #include "track.h" +#include "trackx.h" #include "utility.h" EXPORT TRKTYP_T T_TURNOUT = -1; @@ -50,9 +53,12 @@ EXPORT dynArr_t turnoutInfo_da; EXPORT turnoutInfo_t * curTurnout = NULL; EXPORT long curTurnoutEp = 0; +static int curTurnoutInx = -1; static int log_turnout = 0; static int log_traverseTurnout = 0; +static int log_suppressCheckPaths = 0; +static int log_splitturnout = 0; static wMenu_p turnoutPopupM; @@ -71,6 +77,7 @@ static wIndex_t turnoutInx; static long hideTurnoutWindow; static void RedrawTurnout(void); static void SelTurnoutEndPt( wIndex_t, coOrd ); +static void HilightEndPt( void ); static wPos_t turnoutListWidths[] = { 80, 80, 220 }; static const char * turnoutListTitles[] = { N_("Manufacturer"), N_("Part No"), N_("Description") }; @@ -111,6 +118,7 @@ EXPORT turnoutInfo_t * CreateNewTurnout( PATHPTR_T paths, EPINX_T endPtCnt, trkEndPt_t * endPts, + DIST_T * radii, wBool_t updateList ) { turnoutInfo_t * to; @@ -126,7 +134,14 @@ EXPORT turnoutInfo_t * CreateNewTurnout( changes = CHANGE_PARAMS; } to->segCnt = segCnt; - to->segs = (trkSeg_p)memdup( segData, (sizeof *segData) * segCnt ); + trkSeg_p seg_p; + to->segs = (trkSeg_p)memdup( segData, (sizeof (*segData) * segCnt )); + seg_p = to->segs; + for (int i=0;i<segCnt;i++) { + seg_p[i].bezSegs.ptr = NULL; + seg_p[i].bezSegs.cnt = 0; + seg_p[i].bezSegs.max = 0; + } CopyPoly(to->segs,segCnt); FixUpBezierSegs(to->segs,to->segCnt); GetSegBounds( zero, 0.0, segCnt, to->segs, &to->orig, &to->size ); @@ -137,7 +152,7 @@ EXPORT turnoutInfo_t * CreateNewTurnout( to->paths = (PATHPTR_T)memdup( paths, (sizeof *to->paths) * to->pathLen ); to->paramFileIndex = curParamFileIndex; if (curParamFileIndex == PARAM_CUSTOM) - to->contentsLabel = "Custom Turnouts"; + to->contentsLabel = MyStrdup("Custom Turnouts"); else to->contentsLabel = curSubContents; #ifdef TURNOUTCMD @@ -150,11 +165,137 @@ EXPORT turnoutInfo_t * CreateNewTurnout( to->barScale = curBarScale>0?curBarScale:-1; to->special = TOnormal; + if (radii) { + to->special = TOcurved; + DYNARR_SET(DIST_T,to->u.curved.radii,to->endCnt); + for (int i=0;i<to->endCnt;i++) { + DYNARR_N(DIST_T,to->u.curved.radii,i) = radii[i]; + } + } if (updateList && changes) DoChangeNotification( changes ); return to; } +/** + * Delete a turnout parameter from the list and free the related memory + * + * \param [IN] to turnout definition to be deleted + */ + +BOOL_T +DeleteTurnout(void *toInfo) +{ + turnoutInfo_t * to = (turnoutInfo_t *)toInfo; + MyFree(to->title); + MyFree(to->segs); + MyFree(to->endPt); + MyFree(to->paths); + if (to->special) { + DYNARR_FREE(DIST_T, to->u.curved.radii); + } + + MyFree(to); + return(TRUE); +} + +/** + * Delete all turnout definitions that came from a specific parameter file. + * Due to the way the definitions are loaded from file it is safe to + * assume that they form a contiguous block in the array. + * + * \param [IN] fileIndex parameter file + */ + +void +DeleteTurnoutParams(int fileIndex) +{ + int inx=0; + int startInx = -1; + int cnt = 0; + + // go to the start of the block + while (inx < turnoutInfo_da.cnt && + turnoutInfo(inx)->paramFileIndex != fileIndex) { + startInx = inx++; + } + + // delete them + for (; inx < turnoutInfo_da.cnt && + turnoutInfo(inx)->paramFileIndex == fileIndex; inx++) { + turnoutInfo_t * to = turnoutInfo(inx); + if (to->paramFileIndex == fileIndex) { + DeleteTurnout(to); + cnt++; + } + } + + // copy down the rest of the list to fill the gap + startInx++; + while (inx < turnoutInfo_da.cnt) { + turnoutInfo(startInx++) = turnoutInfo(inx++); + } + + // and reduce the actual number + turnoutInfo_da.cnt -= cnt; +} + +/** + * Check to find out to what extent the contents of the parameter file can be used with + * the current layout scale / gauge. + * + * If parameter scale == layout and parameter gauge == layout we have an exact fit. + * If parameter gauge == layout we have compatible track. + * OO scale is special cased. If the layout is in OO scale track in HO is considered + * an exact fit in spite of scale differences. + * + * \param paramFileIndex + * \param scaleIndex + * \return + */ + +enum paramFileState +GetTrackCompatibility(int paramFileIndex, SCALEINX_T scaleIndex) +{ + int i; + enum paramFileState ret = PARAMFILE_NOTUSABLE; + DIST_T gauge = GetScaleTrackGauge(scaleIndex); + + if (!IsParamValid(paramFileIndex)) { + return(PARAMFILE_UNLOADED); + } + + // loop over all parameter entries or until a exact fit is found + for (i = 0; i < turnoutInfo_da.cnt && ret < PARAMFILE_FIT; i++) { + turnoutInfo_t *to = turnoutInfo( i ); + if (to->paramFileIndex == paramFileIndex ) { + if (to->scaleInx == scaleIndex ) { + ret = PARAMFILE_FIT; + break; + } else { + if (GetScaleTrackGauge(to->scaleInx) == gauge && + ret < PARAMFILE_COMPATIBLE) { + ret = PARAMFILE_COMPATIBLE; + // handle special cases + // if layout is OO scale, HO scale track is considered exact + char *layoutScaleName = GetScaleName(scaleIndex); + char *paramScaleName = GetScaleName(to->scaleInx); + if (!strcmp(layoutScaleName, "OO") && + !strcmp(paramScaleName, "HO")) { + ret = PARAMFILE_FIT; + } + //if layout is in Japanese or British N scale, N scale is exact + if ((!strcmp(layoutScaleName, "N(UK)") || + !strcmp(layoutScaleName, "N(JP)")) && + !strcmp(paramScaleName, "N")) { + ret = PARAMFILE_FIT; + } + } + } + } + } + return(ret); +} EXPORT wIndex_t CheckPaths( @@ -162,31 +303,59 @@ EXPORT wIndex_t CheckPaths( trkSeg_p segs, PATHPTR_T paths ) { + if ((segCnt == 0) || !segs) return -1; int pc, ps; PATHPTR_T pp = 0; - int inx, inx1; + int inx; 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 ) + // Check that each track segment is on at least one path + int suppressCheckPaths = log_suppressCheckPaths > 0 ? logTable(log_suppressCheckPaths).level : 0; + if ( suppressCheckPaths == 0 ) { + char trkSegInx = 0; + for ( int inx = 0; inx<segCnt; inx++ ) { + if ( IsSegTrack( &segs[inx] ) ) { + trkSegInx++; + PATHPTR_T cp = paths; + while ( *cp ) { + // path is: 'N' 'A' 'M' 'E' 0 1 2 0 3 4 0 0 + // skip name + for ( ; *cp; cp++ ); + cp++; + // check each path component + for ( ; cp[0] || cp[1]; cp++ ) + if ( abs(*cp) == trkSegInx ) + break; + if ( *cp ) // we broke early + break; + cp += 2;; // Skip 2nd 0 + } + if ( !*cp ) { // we looked and didn't find + InputError( "Track segment %d not on Path", FALSE, inx+1 ); + return -1;; + } + } + } + } - DYNARR_RESET( trkSeg_p, segMap_da ); +typedef struct { + trkSeg_p seg; + int indx; +} segMap_t, * segMap_p; + +#define segMap(N) DYNARR_N( segMap_t, segMap_da, N ) + segMap_p sg; + DYNARR_RESET( segMap_t, segMap_da ); + // Don't reshuffle segs, but build an offset map instead just of the tracks + // Use the map to set up the paths to point at the correct segs in the Turnout 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]; + DYNARR_APPEND( segMap_t, segMap_da, 10 ); + sg = &DYNARR_LAST(segMap_t,segMap_da); + sg->seg = &segs[inx]; + sg->indx = inx; } } @@ -203,8 +372,27 @@ EXPORT wIndex_t CheckPaths( return -1; } #endif - + //Rewrite the Path to point to the nth Track seg using the Map + int old_inx; + EPINX_T old_EP; + if (pp[0]!=0 && ps==0) { // First or only one + GetSegInxEP( pp[0], &old_inx, &old_EP ); + if (old_inx<0 || old_inx>= segMap_da.cnt) { + InputError( _("Turnout path[%d] %d is not a valid track segment"), + FALSE, pc, ps ); + return -1; + } + SetSegInxEP( &pp[0], DYNARR_N(segMap_t,segMap_da,old_inx).indx, old_EP); + } if (pp[0]!=0 && pp[1]!=0 ) { + //Rewrite the Path to point to the nth Track seg using the Map + GetSegInxEP( pp[1], &old_inx, &old_EP ); + if (old_inx<0 || old_inx>= segMap_da.cnt) { + InputError( _("Turnout path[%d] %d is not a valid track segment"), + FALSE, pc, ps ); + return -1; + } + SetSegInxEP( &pp[1], DYNARR_N(segMap_t,segMap_da,old_inx).indx, old_EP); /* check connectivity */ DIST_T d; GetSegInxEP( pp[0], &segInx[0], &segEp[0] ); @@ -219,12 +407,12 @@ EXPORT wIndex_t CheckPaths( 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 ) ); + coOrd p0 = GetSegEndPt( &segs[segInx[0]], 1-segEp[0], FALSE, NULL ); + coOrd p1 = GetSegEndPt( &segs[segInx[1]], segEp[1], FALSE, NULL ); + d = FindDistance(p0,p1); if (d > MIN_TURNOUT_SEG_CONNECT_DIST) { - InputError( _("Turnout path[%d] %d-%d not connected: %0.3f"), - FALSE, pc, pp[0], pp[1], d ); + InputError( _("Turnout path[%d] %d-%d not connected: %0.3f P0(%f,%f) P1(%f,%f)"), + FALSE, pc, pp[0], pp[1], d, p0.x, p0.y, p1.x, p1.y ); return -1; } } @@ -246,27 +434,28 @@ static BOOL_T ReadTurnoutParam( 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) + if ( !ReadSegs() ) + return FALSE; + CheckPaths( tempSegs_da.cnt, &tempSegs(0), pathPtr ); + to = CreateNewTurnout( scale, title, tempSegs_da.cnt, &tempSegs(0), + pathCnt, pathPtr, tempEndPts_da.cnt, &tempEndPts(0), NULL, FALSE ); + MyFree( title ); + if (to == NULL) + return FALSE; + if (tempSpecial[0] != '\0') { + if (strncmp( tempSpecial, ADJUSTABLE, strlen(ADJUSTABLE) ) == 0) { + to->special = TOadjustable; + if ( !GetArgs( tempSpecial+strlen(ADJUSTABLE), "ff", + &to->u.adjustable.minD, &to->u.adjustable.maxD ) ) + return FALSE; + } else { + InputError(_("Unknown special case"), TRUE); 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 ); + if (tempCustom[0] != '\0') { + to->customInfo = MyStrdup( tempCustom ); + } return TRUE; } @@ -275,6 +464,7 @@ EXPORT turnoutInfo_t * TurnoutAdd( long mode, SCALEINX_T scale, wList_p list, co { wIndex_t inx; turnoutInfo_t * to, * to1 = NULL; + turnoutInx = 0; for ( inx = 0; inx < turnoutInfo_da.cnt; inx++ ) { to = turnoutInfo(inx); if ( IsParamValid(to->paramFileIndex) && @@ -284,6 +474,10 @@ EXPORT turnoutInfo_t * TurnoutAdd( long mode, SCALEINX_T scale, wList_p list, co ( epCnt <= 0 || epCnt == to->endCnt ) ) { if (to1==NULL) to1 = to; + if ( to == curTurnout ) { + to1 = to; + turnoutInx = wListGetCount( list ); + } FormatCompoundTitle( mode, to->title ); if (message[0] != '\0') { wListAddValue( list, message, NULL, to ); @@ -535,44 +729,10 @@ track_p NewHandLaidTurnout( 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 ); + trk = NewCompound( T_TURNOUT, 0, p0, a0, message, 3, &tempEndPts(0), NULL, 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; } @@ -588,7 +748,6 @@ static coOrd MapPathPos( EPINX_T ep ) { trkSeg_p segPtr; - wIndex_t inx; coOrd pos; if ( segInx < 0 ) { @@ -596,15 +755,15 @@ static coOrd MapPathPos( 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; + segPtr=xx->segs+(segInx-1); + if (!IsSegTrack(segPtr)) { + fprintf( stderr, "mapPathPos: bad segInx: %d\n", segInx ); + return zero; } - fprintf( stderr, "mapPathPos: bad segInx: %d\n", segInx ); - return zero; + pos = GetSegEndPt( segPtr, ep, FALSE, NULL ); + REORIGIN1( pos, xx->angle, xx->orig ); + return pos; + } @@ -618,21 +777,16 @@ static void DrawTurnout( long widthOptions = 0; DIST_T scale2rail; - if (GetTrkWidth(trk) == 2) - widthOptions = DTS_THICK2; - if (GetTrkWidth(trk) == 3) - widthOptions = DTS_THICK3; + widthOptions = DTS_LEFT|DTS_RIGHT; + 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) && + if ( (d->options & DC_SIMPLE) == 0 && (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) && labelScale >= d->scale && ( GetTrkBits( trk ) & TB_HIDEDESC ) == 0 ) { @@ -648,11 +802,12 @@ static void DrawTurnout( } -static void ReadTurnout( +static BOOL_T ReadTurnout( char * line ) { - ReadCompound( line+8, T_TURNOUT ); - CheckPaths( tempSegs_da.cnt, &tempSegs(0), pathPtr ); + if ( !ReadCompound( line+8, T_TURNOUT ) ) + return FALSE; + return TRUE; } @@ -668,12 +823,27 @@ static ANGLE_T GetAngleTurnout( 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, NULL, NULL, NULL, NULL ); - return NormalizeAngle( angle+xx->angle ); + coOrd pos0=pos; + double dd = 10000.0; + int found = -1; + //Cope with tracks not being first + for (segCnt =0; segCnt<xx->segCnt ; segCnt++ ) { + if (IsSegTrack(&xx->segs[segCnt])) { + double d = DistanceSegs( xx->orig, xx->angle, 1, &xx->segs[segCnt], &pos0, NULL ); + if (d<dd) { + dd = d; + found = segCnt; + } + } + pos0 = pos; + } + if (found>=0) { + pos.x -= xx->orig.x; + pos.y -= xx->orig.y; + Rotate( &pos, zero, -xx->angle ); + angle = GetAngleSegs( 1, &xx->segs[found], &pos, &segInx, NULL, NULL, NULL, NULL ); + return NormalizeAngle( angle+xx->angle ); + } else return 0.0; } @@ -843,6 +1013,7 @@ static void SplitTurnoutCheckEndPt( if ( dir < 0 ) segEP = 1-segEP; pos = GetSegEndPt( &segs[segInx], segEP, FALSE, NULL ); dist = FindDistance( pos, epPos ); + LOG( log_splitturnout, 1, ( " SPTChkEp P%d DIR:%d SegInx:%d SegEP:%d POS[%0.3f %0.3f] DIST:%0.3f\n", *path, dir, segInx, segEP, pos.x, pos.y, dist ) ); if ( dist>connectDistance ) return; minDist = trackGauge; @@ -851,6 +1022,7 @@ static void SplitTurnoutCheckEndPt( if ( dir < 0 ) segEP = 1-segEP; pos = splitPos; dist = DistanceSegs( zero, 0.0, 1, &segs[segInx], &pos, NULL ); + LOG( log_splitturnout, 1, ( " - P:%d SegInx:%d SegEP:%d DIST:%0.3f\n", path[0], segInx, segEP, dist ) ); if ( dist < minDist ) { minDist = dist; splitTurnoutPath = path; @@ -861,15 +1033,17 @@ static void SplitTurnoutCheckEndPt( } } - -static BOOL_T SplitTurnout( - track_p trk, - coOrd pos, - EPINX_T ep, - track_p *leftover, - EPINX_T * ep0, - EPINX_T * ep1 ) -{ +EXPORT BOOL_T SplitTurnoutCheck( + track_p trk, + coOrd pos, + EPINX_T ep, + track_p *leftover, + EPINX_T * ep0, + EPINX_T * ep1, + BOOL_T check, + coOrd * outPos, + ANGLE_T * outAngle ) + { struct extraData * xx = GetTrkExtraData( trk ); wIndex_t segInx0, segInx, segCnt; EPINX_T segEP, epCnt, ep2=0, epN; @@ -891,7 +1065,8 @@ static BOOL_T SplitTurnout( trkSeg_t newSeg; if ( (MyGetKeyState()&WKEY_SHIFT) == 0 ) { - ErrorMessage( MSG_CANT_SPLIT_TRK, _("Turnout") ); + if (!check) + ErrorMessage( MSG_CANT_SPLIT_TRK, _("Turnout") ); return FALSE; } @@ -909,6 +1084,7 @@ static BOOL_T SplitTurnout( epPos.y -= xx->orig.y; splitTurnoutPath = NULL; pp = xx->paths; + LOG( log_splitturnout, 1, ( "SplitTurnoutCheck T%d POS[%0.3f %0.3f] EP:%d CHK:%d EPPOS[%0.3f %0.3f]\n", trk?trk->index:0, pos.x, pos.y, ep, check, epPos.x, epPos.y ) ); while ( pp[0] ) { pp += strlen((char *)pp)+1; while ( pp[0] ) { @@ -924,7 +1100,8 @@ static BOOL_T SplitTurnout( } pp++; } - ErrorMessage( _("splitTurnout: can't find segment") ); + if (!check) + ErrorMessage( _("splitTurnout: can't find segment") ); return FALSE; foundSeg: @@ -932,6 +1109,7 @@ foundSeg: * 2a. Check that all other paths thru found segment are the same */ GetSegInxEP( splitTurnoutPath[0], &segInx0, &segEP ); + LOG( log_splitturnout, 1, (" Found Seg: %d SEG:%d EP:%d\n", *splitTurnoutPath, segInx0, segEP ) ); pp = xx->paths; pathCnt = 0; while ( pp[0] ) { @@ -950,7 +1128,8 @@ foundSeg: pp2 += dir; } if ( pp1[0]!='\0' || pp2[0]!='\0' ) { - ErrorMessage( MSG_SPLIT_POS_BTW_MERGEPTS ); + if (!check) + ErrorMessage( MSG_SPLIT_POS_BTW_MERGEPTS ); return FALSE; } } @@ -965,10 +1144,21 @@ foundSeg: * 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 ); + if (!check) + ErrorMessage( MSG_SPLIT_PATH_NOT_UNIQUE ); return FALSE; } + if (check) { + segProcDataSplit.getAngle.pos = pos; + SegProc( SEGPROC_GETANGLE, xx->segs+segInx0, &segProcDataSplit ); + *outAngle = NormalizeAngle(segProcDataSplit.getAngle.angle+xx->angle); + *outPos = segProcDataSplit.getAngle.pos; + (*outPos).x += xx->orig.x; + (*outPos).y += xx->orig.y; + Rotate( outPos, xx->orig, xx->angle ); + return TRUE; + } /* * 3. Split the found segment. @@ -994,10 +1184,6 @@ foundSeg: 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 @@ -1027,6 +1213,7 @@ foundSeg: } else { tempSegs(segIndexMap(segInx)-1) = xx->segs[segInx]; } + posCnt++; } } @@ -1084,6 +1271,7 @@ foundSeg: /* * 7. Convert trailing segments to new tracks */ + int trks = 0; path = splitTurnoutPath; if ( segProcDataSplit.split.length[s1] < minLength ) path += splitTurnoutDir; @@ -1108,6 +1296,7 @@ foundSeg: trk2 = segProcDataNewTrack.newTrack.trk; ep2 = 1-epN; } + ++trks; path += splitTurnoutDir; } @@ -1133,6 +1322,16 @@ foundSeg: return TRUE; } +static BOOL_T SplitTurnout( + track_p trk, + coOrd pos, + EPINX_T ep, + track_p *leftover, + EPINX_T * ep0, + EPINX_T * ep1 ) +{ + return SplitTurnoutCheck(trk,pos,ep,leftover,ep0,ep1,FALSE,NULL,NULL); +} static BOOL_T CheckTraverseTurnout( track_p trk, @@ -1332,11 +1531,16 @@ static STATUS_T ModifyTurnout( track_p trk, wAction_t action, coOrd pos ) { struct extraData *xx; static EPINX_T ep; + static wBool_t curved; DIST_T d; xx = GetTrkExtraData(trk); if ( xx->special == TOadjustable ) { switch ( action ) { + case C_START: + ep = -1; + curved = FALSE; + return C_CONTINUE; case C_DOWN: ep = PickUnconnectedEndPoint( pos, trk ); if (ep == -1) @@ -1347,7 +1551,7 @@ static STATUS_T ModifyTurnout( track_p trk, wAction_t action, coOrd pos ) tempSegs(0).u.l.pos[0] = GetTrkEndPos( trk, 1-ep ); tempSegs_da.cnt = 1; InfoMessage( _("Drag to change track length") ); - + return C_CONTINUE; case C_MOVE: d = FindDistance( tempSegs(0).u.l.pos[0], pos ); if ( d < xx->u.adjustable.minD ) @@ -1359,28 +1563,148 @@ static STATUS_T ModifyTurnout( track_p trk, wAction_t action, coOrd pos ) 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 C_CONTINUE; } } - return ExtendStraightFromOrig( trk, action, pos ); + + return ExtendTrackFromOrig(trk, action, pos); } static BOOL_T GetParamsTurnout( int inx, track_p trk, coOrd pos, trackParams_t * params ) { - - - params->type = curveTypeStraight; //TODO should check if last segment is actually straight - if (inx == PARAMS_CORNU || inx == PARAMS_BEZIER) { + struct extraData *xx; + xx = GetTrkExtraData(trk); + params->type = curveTypeStraight; + if (inx == PARAMS_TURNOUT) { + params->len = 0.0; + int epCnt = GetTrkEndPtCnt(trk); + if (epCnt < 3) { + double d = 10000.0; + params->centroid = zero; + //calculate path length from endPt (either to end or to other end) + segProcData_t segProcData; + trkSeg_p seg; + int segInx; + int segEP; + trkSeg_p segPtr; + PATHPTR_T path,pathCurr; + //Find starting seg on path (nearest to end Pt) + 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; + segProcData.distance.pos1 = pos; + SegProc( SEGPROC_DISTANCE, segPtr, &segProcData ); + if ( segProcData.distance.dd < d ) { + d = segProcData.distance.dd; + pathCurr = path; + } + } + GetSegInxEP( pathCurr[0], &segInx, &segEP ); + seg = xx->segs+segInx; + d = 0.0; + //Loop through segs on path from endPt adding + while (pathCurr[0]) { + GetSegInxEP( pathCurr[0], &segInx, &segEP ); + seg = xx->segs+segInx; + SegProc(SEGPROC_LENGTH, seg, &segProcData ); + d += segProcData.length.length; + pathCurr += segEP?1:-1; + } + params->len = d; + } else { + double x, y; + x = 0; y = 0; + for (int i=0;i<epCnt; i++) { + coOrd cpos = GetTrkEndPos(trk,i); + x += cpos.x; + y += cpos.y; + } + params->centroid.x = x/epCnt; + params->centroid.y = y/epCnt; + params->len = FindDistance(params->centroid,pos)*2; //Times two because it will be halved by track.c + } + return TRUE; + } + if ((inx == PARAMS_CORNU) || (inx == PARAMS_EXTEND)) { + params->type = curveTypeStraight; params->arcR = 0.0; params->arcP = zero; + params->ep = PickEndPoint(pos, trk); + params->circleOrHelix = FALSE; + if (params->ep>=0) { + params->angle = GetTrkEndAngle(trk,params->ep); + params->track_angle = params->angle + params->ep?0:180; + } else { + params->angle = params-> track_angle = 0; + return FALSE; + } + /* Use end radii if we have them */ + //if (xx->special == TOcurved) { + // params->type = curveTypeCurve; + // params->arcR = fabs(DYNARR_N(DIST_T,xx->u.curved.radii,params->ep)); + // if (params->arcR != 0.0) + // Translate(¶ms->arcP,pos,params->track_angle-90.0,params->arcR); + // else + // params->type = curveTypeStraight; + // return TRUE; + //} + /* Find the path we are closest to */ + PATHPTR_T pathCurr = 0; + int segInx, subSegInx; + trkSeg_p segPtr; + double d = 10000; + struct extraData * xx = GetTrkExtraData(trk); + /* Get parms from that seg */ + wBool_t back,negative; + coOrd segPos = pos; + Rotate(&segPos,xx->orig,-xx->angle); + segPos.x -= xx->orig.x; + segPos.y -= xx->orig.y; + + params->track_angle = GetAngleSegs( //Find correct subSegment + xx->segCnt,xx->segs, + &segPos, &segInx, &d , &back, &subSegInx, &negative ); + if (segInx ==- 1) return FALSE; + segPtr = xx->segs+segInx; + switch (segPtr->type) { + case SEG_BEZTRK: + if ( negative != back ) params->track_angle = NormalizeAngle(params->track_angle+180); //Bezier is in reverse + segPtr = xx->segs + segInx; + trkSeg_p subSegPtr = (trkSeg_p)segPtr->bezSegs.ptr+subSegInx; + if (subSegPtr->type == SEG_CRVTRK) { + params->type = curveTypeCurve; + params->arcR = fabs(subSegPtr->u.c.radius); + params->arcP = subSegPtr->u.c.center; + params->arcP.x += xx->orig.x; + params->arcP.y += xx->orig.y; + Rotate(¶ms->arcP,xx->orig,xx->angle); + params->arcA0 = subSegPtr->u.c.a0; + params->arcA1 = subSegPtr->u.c.a1; + } + return TRUE; + break; + case SEG_CRVTRK: + params->type = curveTypeCurve; + params->arcR = fabs(segPtr->u.c.radius); + params->arcP = segPtr->u.c.center; + params->arcP.x += xx->orig.x; + params->arcP.y += xx->orig.y; + Rotate(¶ms->arcP,xx->orig,xx->angle); + params->arcA0 = segPtr->u.c.a0; + params->arcA1 = segPtr->u.c.a1; + return TRUE; + break; + } + params->arcR = 0.0; + params->arcP = zero; params->ep = PickEndPoint(pos,trk); //Nearest if (params->ep>=0) { params->angle = GetTrkEndAngle(trk,params->ep); @@ -1391,7 +1715,10 @@ static BOOL_T GetParamsTurnout( int inx, track_p trk, coOrd pos, trackParams_t * } return TRUE; } - params->ep = PickUnconnectedEndPointSilent( pos, trk ); + if ((inx == PARAMS_1ST_JOIN) || (inx == PARAMS_2ND_JOIN)) + params->ep = PickEndPoint(pos, trk); + else + params->ep = PickUnconnectedEndPointSilent( pos, trk ); if (params->ep == -1) return FALSE; params->lineOrig = GetTrkEndPos(trk,params->ep); @@ -1441,15 +1768,11 @@ static BOOL_T QueryTurnout( track_p trk, int query ) case Q_NOT_PLACE_FROGPOINTS: case Q_HAS_DESC: case Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK: - case Q_CAN_EXTEND: return TRUE; case Q_MODIFY_CAN_SPLIT: - if (GetTrkEndPtCnt(trk) <= 2) { // allow splitting of simple track und buffers - return TRUE ; - } - else { - return FALSE; - } + return TRUE; + case Q_IS_TURNOUT: + return TRUE; case Q_CAN_PARALLEL: if( GetTrkEndPtCnt( trk ) == 2 && fabs( GetTrkEndAngle( trk, 0 ) - GetTrkEndAngle( trk, 1 )) == 180.0 ) return TRUE; @@ -1485,7 +1808,7 @@ static void DrawTurnoutPositionIndicator( pos0 = MapPathPos( xx, path[1], 0 ); } else if ( path[1] == 0 ) { pos1 = MapPathPos( xx, path[0], 1 ); - DrawLine( &mainD, pos0, pos1, drawTurnoutPositionWidth, color ); + DrawLine( &tempD, pos0, pos1, drawTurnoutPositionWidth, color ); } } } @@ -1505,7 +1828,6 @@ EXPORT void AdvanceTurnoutPositionIndicator( if ( GetTrkType(trk) != T_TURNOUT ) AbortProg( "nextTurnoutPosition" ); - DrawTurnoutPositionIndicator( trk, wDrawColorWhite ); path = xx->pathCurr; path += strlen((char *)path)+1; while ( path[0] || path[1] ) @@ -1514,7 +1836,6 @@ EXPORT void AdvanceTurnoutPositionIndicator( if ( *path == 0 ) path = xx->paths; xx->pathCurr = path; - DrawTurnoutPositionIndicator( trk, selectedColor ); if ( angleR == NULL || posR == NULL ) return; trvtrk.trk = trk; @@ -1548,9 +1869,11 @@ static BOOL_T MakeParallelTurnout( track_p trk, coOrd pos, DIST_T sep, + DIST_T factor, track_p * newTrk, coOrd * p0R, - coOrd * p1R ) + coOrd * p1R, + BOOL_T track) { ANGLE_T angle = GetTrkEndAngle(trk,1); struct extraData *xx, *yy; @@ -1578,42 +1901,61 @@ static BOOL_T MakeParallelTurnout( */ 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 ); + if (track) { + 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); + + DIST_T * radii = NULL; + if (yy->special == TOcurved) { + radii = MyMalloc(GetTrkEndPtCnt(trk) * sizeof(DIST_T)); + for (int i=0;i<GetTrkEndPtCnt( trk );i++) { + radii[i] = DYNARR_N(DIST_T,yy->u.curved.radii,i); + } + } + + *newTrk = NewCompound( T_TURNOUT, 0, endPt[ 0 ].pos, endPt[ 0 ].angle + 90.0, yy->title, 2, endPt, radii, 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 { + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).width = 0; + tempSegs_da.cnt = 1; + tempSegs(0).type = track?SEG_STRTRK:SEG_STRLIN; + tempSegs(0).u.l.pos[0] = endPts[ 0 ]; + tempSegs(0).u.l.pos[1] = endPts[ 1 ]; + *newTrk = MakeDrawFromSeg( zero, 0.0, &tempSegs(0) ); + } } 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).type = track?SEG_STRTRK:SEG_STRLIN; tempSegs(0).u.l.pos[0] = endPts[ 0 ]; tempSegs(0).u.l.pos[1] = endPts[ 1 ]; } @@ -1625,8 +1967,26 @@ static BOOL_T MakeParallelTurnout( return TRUE; } +static wBool_t CompareTurnout( track_cp trk1, track_cp trk2 ) +{ + struct extraData *xx1 = GetTrkExtraData( trk1 ); + struct extraData *xx2 = GetTrkExtraData( trk2 ); + char * cp = message + strlen(message); + REGRESS_CHECK_POS( "Orig", xx1, xx2, orig ) + REGRESS_CHECK_ANGLE( "Angle", xx1, xx2, angle ) + REGRESS_CHECK_INT( "Handlaid", xx1, xx2, handlaid ) + REGRESS_CHECK_INT( "Flipped", xx1, xx2, flipped ) + REGRESS_CHECK_INT( "Ungrouped", xx1, xx2, ungrouped ) + REGRESS_CHECK_INT( "Split", xx1, xx2, split ) + /* desc orig is not stable + REGRESS_CHECK_POS( "DescOrig", xx1, xx2, descriptionOrig ) */ + REGRESS_CHECK_POS( "DescOff", xx1, xx2, descriptionOff ) + REGRESS_CHECK_POS( "DescSize", xx1, xx2, descriptionSize ) + return CompareSegs( xx1->segs, xx1->segCnt, xx1->segs, xx1->segCnt ); +} + static trackCmd_t turnoutCmds = { - N_("TURNOUT "), + "TURNOUT ", DrawTurnout, DistanceCompound, DescribeCompound, @@ -1654,7 +2014,13 @@ static trackCmd_t turnoutCmds = { DrawTurnoutPositionIndicator, AdvanceTurnoutPositionIndicator, CheckTraverseTurnout, - MakeParallelTurnout }; + MakeParallelTurnout, + NULL, + NULL, + NULL, + NULL, + NULL, + CompareTurnout }; #ifdef TURNOUTCMD @@ -1702,7 +2068,7 @@ static void TurnoutChange( long changes ) (changes&CHANGE_PARAMS) == 0 ) ) return; lastScaleName = curScaleName; - curTurnout = NULL; + //curTurnout = NULL; curTurnoutEp = 0; wControlShow( (wControl_p)turnoutListL, FALSE ); wListClear( turnoutListL ); @@ -1710,7 +2076,7 @@ static void TurnoutChange( long changes ) if (turnoutInfo_da.cnt <= 0) return; curTurnout = TurnoutAdd( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, GetLayoutCurScale(), turnoutListL, &maxTurnoutDim, -1 ); - wListSetIndex( turnoutListL, 0 ); + wListSetIndex( turnoutListL, turnoutInx ); wControlShow( (wControl_p)turnoutListL, TRUE ); if (curTurnout == NULL) { wDrawClear( turnoutD.d ); @@ -1727,7 +2093,6 @@ static void TurnoutChange( long changes ) static void RedrawTurnout() { - coOrd p, s; RescaleTurnout(); LOG( log_turnout, 2, ( "SelTurnout(%s)\n", (curTurnout?curTurnout->title:"<NULL>") ) ) @@ -1740,10 +2105,7 @@ LOG( log_turnout, 2, ( "SelTurnout(%s)\n", (curTurnout?curTurnout->title:"<NULL> 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 ); + HilightEndPt(); } @@ -1796,7 +2158,9 @@ static void HilightEndPt( void ) 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 ); + wDrawSetTempMode( turnoutD.d, TRUE ); + DrawHilight( &turnoutD, p, s, FALSE ); + wDrawSetTempMode( turnoutD.d, FALSE ); } @@ -1806,7 +2170,6 @@ static void SelTurnoutEndPt( { if (action != C_DOWN) return; - HilightEndPt(); curTurnoutEp = TOpickEndPoint( pos, curTurnout ); HilightEndPt(); LOG( log_turnout, 3, (" selected (action=%d) %ld\n", action, curTurnoutEp ) ) @@ -1831,13 +2194,27 @@ static struct { coOrd rot0, rot1; } Dto; +static dynArr_t vector_da; +#define vector(N) DYNARR_N( vector_t, vector_da, N ) typedef struct { DIST_T off; ANGLE_T angle; EPINX_T ep; + track_p trk; } vector_t; +/* + * PlaceTurnoutTrial + * + * OUT Track - the Track that the Turnout is closest to + * IN/OUT Pos - Position of the Turnout end + * OUT Angle1 - The angle on the Track at the position + * OUT Angle2 - The angle of the Turnout (can be reversed from Angle 2 if the point is to the left) + * OUT Count - The number of connections + * OUT Max - The maximum distance between the ends and the connection points + * OUT Vector - An array of end points positions and offsets + */ static void PlaceTurnoutTrial( track_p *trkR, coOrd *posR, @@ -1856,23 +2233,30 @@ static void PlaceTurnoutTrial( ANGLE_T epAngle; int i, connCnt = 0; DIST_T d, maxD = 0; + coOrd testP = pos; - if ( (*trkR = trk = OnTrack( &pos, FALSE, TRUE )) != NULL && + if (*trkR && (GetTrkDistance(*trkR,&testP)<trackGauge)) { //Have Track, stick with it unless outside bounds + trk = *trkR; + pos = testP; + } else *trkR = trk = OnTrack( &pos, FALSE, TRUE ); + if ( (trk) != 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)) ) { + ! GetLayerFrozen(GetTrkLayer(trk)) && + ! GetLayerModule(GetTrkLayer(trk))) { epPos = GetTrkEndPos( trk, ep0 ); d = FindDistance( pos, epPos ); if (d <= minLength) pos = epPos; - if ( GetTrkType(trk) == T_TURNOUT ) { + if ( GetTrkType(trk) == T_TURNOUT ) { //Only on the end ep0 = ep1 = PickEndPoint( pos, trk ); angle = GetTrkEndAngle( trk, ep0 ); } else { angle = GetAngleAtPoint( trk, pos, &ep0, &ep1 ); + if (ep0==1) angle = NormalizeAngle(angle+180); //Reverse if curve backwards } angle = NormalizeAngle( angle + 180.0 ); if ( NormalizeAngle( FindAngle( pos, *posR ) - angle ) < 180.0 && ep0 != ep1 ) @@ -1883,18 +2267,21 @@ static void PlaceTurnoutTrial( Rotate( &epPos, zero, angle ); pos.x -= epPos.x; pos.y -= epPos.y; - *posR = pos; + *posR = pos; //The place the Turnout end sits 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 );*/ - + track_p ctrk = NULL; + int ccnt = 0; + DIST_T clarge = 100000; 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))) { + !GetLayerFrozen(GetTrkLayer(trk)) && + !GetLayerModule(GetTrkLayer(trk))) { v->off = FindDistance( epPos, conPos ); v->angle = FindAngle( epPos, conPos ); if ( GetTrkType(trk) == T_TURNOUT ) { @@ -1902,14 +2289,28 @@ LOG( log_turnout, 3, ( "placeTurnout T%d (%0.3f %0.3f) A%0.3f\n", aa = GetTrkEndAngle( trk, ep0 ); } else { aa = GetAngleAtPoint( trk, conPos, &ep0, &ep1 ); + if (ep0) //Backwards - so reverse + aa = NormalizeAngle(aa+180); } 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 && + aa = fabs(DifferenceBetweenAngles( aa, epAngle )); + if (QueryTrack(trk,Q_IS_CORNU) ) { //Make sure only two conns to each Cornu + int k=0; + v->trk = trk; + for (int j=0; j<i;j++) { + if (vector(j).trk == trk) k++; + } + if (k<2) { //Already two conns to this track + connCnt++; + if (v->off > maxD) + maxD = v->off; + v++; + + } + } else if (( IsClose(v->off) && (aa<connectAngle || aa>180-connectAngle) && + !( GetTrkType(trk) == T_TURNOUT && (trk1=GetTrkEndTrk(trk,ep0)) && - GetTrkType(trk1) == T_TURNOUT ) ) { + GetTrkType(trk1) == T_TURNOUT )) ) { if (v->off > maxD) maxD = v->off; connCnt++; @@ -1927,7 +2328,7 @@ LOG( log_turnout, 3, ( "placeTurnout T%d (%0.3f %0.3f) A%0.3f\n", static void PlaceTurnout( - coOrd pos ) + coOrd pos, track_p trk ) { coOrd p, pos1, pos2; track_p trk1, trk2; @@ -1936,21 +2337,24 @@ static void PlaceTurnout( 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; +LOG( log_turnout, 1, ( "Place Turnout @ %0.3fx%0.3f\n", Dto.pos.x, Dto.pos.y ) ); if (curTurnoutEp >= (long)curTurnout->endCnt) curTurnoutEp = 0; DYNARR_SET( vector_t, vector_da, curTurnout->endCnt ); + if (trk) trk1 = trk; + else trk1 = NULL; 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 ) { + Dto.pos = pos1; //First track pos +LOG( log_turnout, 1, ( " trial 1 @ %0.3fx%0.3f\n", Dto.pos.x, Dto.pos.y ) ); + Dto.trk = trk1; //Track + Dto.angle = a1; //Angle of track to put down + if ( ((MyGetKeyState()&WKEY_SHIFT)==0) && (connCnt1 > 1) && (maxD1 >= 0.001) ) { //Adjust if not Shift maxV = &vector(0); - for ( i=1; i<connCnt1; i++ ) { + for ( i=1; i<connCnt1; i++ ) { //Ignore first point V = &vector(i); if ( V->off > maxV->off ) { maxV = V; @@ -1964,9 +2368,11 @@ static void PlaceTurnout( if (NormalizeAngle( maxV->angle - a3) > 180) d = -d; Translate( &pos2, pos, a2, d ); + trk2 = trk1; PlaceTurnoutTrial( &trk2, &pos2, &a2, &a, &connCnt2, &maxD2, &vector(0) ); if ( connCnt2 >= connCnt1 && maxD2 < maxD1 ) { Dto.pos = pos2; +LOG( log_turnout, 1, ( " trial 2 @ %0.3fx%0.3f\n", Dto.pos.x, Dto.pos.y ) ); Dto.trk = trk2; Dto.angle = a2; maxD1 = maxD2; @@ -1987,6 +2393,7 @@ static void PlaceTurnout( Rotate( &p, zero, Dto.angle ); Dto.pos.x = pos.x - p.x; Dto.pos.y = pos.y - p.y; +LOG( log_turnout, 1, ( " final @ %0.3fx%0.3f\n", Dto.pos.x, Dto.pos.y ) ); } } @@ -2010,6 +2417,7 @@ static void AddTurnout( void ) #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 no_ties; BOOL_T noConnections; coOrd p0, p1; @@ -2020,8 +2428,6 @@ static void AddTurnout( void ) 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 ); @@ -2041,13 +2447,29 @@ static void AddTurnout( void ) for (i=0;i<curTurnout->endCnt;i++) { AuditTracks( "addTurnout [%d]", i ); connection(i).trk = leftover(i).trk = NULL; + connection(i).ep = -1; + leftover(i).ep = -1; /* connect each endPt ... */ epPos = tempEndPts(i).pos; - if ((trk = OnTrack(&epPos, FALSE, TRUE)) != NULL && + if ((trk = OnTrack(&epPos, FALSE, TRUE)) != NULL && //Adjust epPos onto existing track (!GetLayerFrozen(GetTrkLayer(trk))) && + (!GetLayerModule(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 ) ) + DIST_T dd = 10000.0; + int nearest = -1; + for (int j=0;j<curTurnout->endCnt;j++) { + if (j<i && (connection(j).trk == trk)) { + nearest = -1; + goto nextEnd; //Track already chosen in use + } + if (dd>FindDistance(epPos,tempEndPts(j).pos)) { + dd = FindDistance(epPos,tempEndPts(j).pos); + nearest = j; + } + } + if (nearest != i) continue; //Not this one d = FindDistance( tempEndPts(i).pos, epPos ); if ( GetTrkType(trk) == T_TURNOUT ) { ep0 = ep1 = PickEndPoint( epPos, trk ); @@ -2055,15 +2477,14 @@ LOG( log_turnout, 1, ( "ep[%d] on T%d @(%0.3f %0.3f)\n", } 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) { + aa = fabs(DifferenceBetweenAngles( a , tempEndPts(i).angle)); + if ((QueryTrack(trk,Q_IS_CORNU) && (d<trackGauge*2)) || + (( IsClose(d) && ( ((ep0!=ep1) && (aa<=connectAngle)) || ((aa<=connectAngle) || (aa>180-connectAngle)) ) && + ! ( GetTrkType(trk) == T_TURNOUT && + (trk1=GetTrkEndTrk(trk,ep0)) && + GetTrkType(trk1) == T_TURNOUT )) ) ) { + /* ... if they are close enough to a track and line up */ + if (aa<90) { epx = ep1; epy = ep0; } else { @@ -2071,9 +2492,9 @@ LOG( log_turnout, 1, ( "ep[%d] on T%d @(%0.3f %0.3f)\n", 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 ) { + if ( (epx != epy) && + ((d=FindDistance(GetTrkEndPos(trk,epy), epPos)) < minLength) && + ((trk1=GetTrkEndTrk(trk,epy)) != NULL) ) { epx = GetEndPtConnectedToMe( trk1, trk ); trk = trk1; } @@ -2085,22 +2506,21 @@ LOG( log_turnout, 1, ( " Attach! epx=%d\n", epx ) ) connection(i).trk = trk; connection(i).ep = epx; if (leftover(i).trk != NULL) { - leftover(i).ep = PickEndPoint( epPos, leftover(i).trk ); + leftover(i).ep = 1-epx; /* 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 */ + if ( connection(i).trk == leftover(j).trk ) { + /* yes. Remove the latest 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 ); + UndrawNewTrack( leftover(i).trk ); DeleteTrack( leftover(i).trk, FALSE ); AuditTracks( "addTurnout [%d] before delete", i ); leftover(i).trk = NULL; + leftover(i).ep = -1; + leftover(j).trk = NULL; //Forget this leftover + leftover(j).ep = -1; break; } } @@ -2108,13 +2528,15 @@ LOG( log_turnout, 1, ( " deleting leftover T%d\n", } } } +nextEnd:; } 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 ); + + newTrk = NewCompound( T_TURNOUT, 0, Dto.pos, Dto.angle, curTurnout->title, tempEndPts_da.cnt, &tempEndPts(0), NULL, curTurnout->pathLen, (char *)curTurnout->paths, curTurnout->segCnt, curTurnout->segs ); xx = GetTrkExtraData(newTrk); xx->customInfo = curTurnout->customInfo; if (connection((int)curTurnoutEp).trk) { @@ -2122,28 +2544,50 @@ LOG( log_turnout, 1, ( " deleting leftover T%d\n", SetTrkScale( newTrk, GetLayoutCurScale()); } xx->special = curTurnout->special; + if (xx->special == TOcurved) { + DYNARR_SET(DIST_T,xx->u.curved.radii,curTurnout->endCnt); + for (int i=0;i<curTurnout->endCnt;i++) { + DYNARR_N(DIST_T,xx->u.curved.radii,i) = DYNARR_N(DIST_T,curTurnout->u.curved.radii,i); + } + } xx->u = curTurnout->u; /* Make the connections */ visible = FALSE; + no_ties = FALSE; noConnections = TRUE; AuditTracks( "addTurnout T%d before connection", GetTrkIndex(newTrk) ); for (i=0;i<curTurnout->endCnt;i++) { if ( connection(i).trk != NULL ) { + if (GetTrkEndTrk(connection(i).trk,connection(i).ep)) continue; p0 = GetTrkEndPos( newTrk, i ); p1 = GetTrkEndPos( connection(i).trk, connection(i).ep ); ANGLE_T a0 = GetTrkEndAngle( newTrk, i); ANGLE_T a1 = GetTrkEndAngle( connection(i).trk, connection(i).ep ); - ANGLE_T a = NormalizeAngle(a1-a0+180); + ANGLE_T a = fabs(DifferenceBetweenAngles(a0+180,a1)); d = FindDistance( p0, p1 ); - if ( d < connectDistance ) { + if (QueryTrack(connection(i).trk,Q_IS_CORNU)) { + ANGLE_T a = DifferenceBetweenAngles(FindAngle(p0,p1),a0); + if (IsClose(d) || fabs(a)<=90.0) { + trk1 = connection(i).trk; + ep0 = connection(i).ep; + if (GetTrkEndTrk(trk1,ep0)) continue; + DrawEndPt( &mainD, trk1, ep0, wDrawColorWhite ); + trackParams_t params; + GetTrackParams( PARAMS_EXTEND, newTrk, GetTrkEndPos(newTrk,i), ¶ms); + SetCornuEndPt(trk1, ep0, GetTrkEndPos(newTrk,i), params.arcP, NormalizeAngle(params.angle+180.0), params.arcR); + ConnectTracks(newTrk,i,trk1,ep0); + DrawEndPt( &mainD, trk1, ep0, wDrawColorBlack ); + } + } else if ( d < connectDistance && (a<=connectAngle)) { noConnections = FALSE; trk1 = connection(i).trk; ep0 = connection(i).ep; DrawEndPt( &mainD, trk1, ep0, wDrawColorWhite ); ConnectTracks( newTrk, i, trk1, ep0 ); visible |= GetTrkVisible(trk1); + no_ties |= GetTrkNoTies(trk1); DrawEndPt( &mainD, trk1, ep0, wDrawColorBlack ); } } @@ -2151,10 +2595,8 @@ LOG( log_turnout, 1, ( " deleting leftover T%d\n", if (noConnections) visible = TRUE; SetTrkVisible( newTrk, visible); -#ifdef LATER - SetTrkScale( newTrk, curScaleInx ); - ComputeCompoundBoundingBox( newTrk ); -#endif + SetTrkNoTies(newTrk, no_ties); + SetTrkBridge(newTrk, FALSE); AuditTracks( "addTurnout T%d before dealing with leftovers", GetTrkIndex(newTrk) ); /* deal with the leftovers */ @@ -2165,20 +2607,59 @@ LOG( log_turnout, 1, ( " deleting leftover T%d\n", coOrd off; DIST_T maxX; track_p lt = leftover(i).trk; - EPINX_T ep, le = leftover(i).ep; - coOrd pos; + if (QueryTrack(lt,Q_IS_CORNU)) { + UndrawNewTrack(lt); + DeleteTrack(lt,TRUE); + leftover(i).trk = NULL; + continue; + } + EPINX_T ep, le = leftover(i).ep, nearest_ep =-1; + coOrd pos, nearest_pos = zero; + ANGLE_T nearest_angle = 0.0; + DIST_T nearest_radius = 0.0; + coOrd nearest_center = zero; + trackParams_t params; maxX = 0.0; + DIST_T dd = 10000.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 ); + pos = GetTrkEndPos(newTrk,ep); + DIST_T d = GetTrkDistance(lt, &pos); + if ((d<dd) && ( d<trackGauge/2)) { + ANGLE_T a = GetTrkEndAngle( lt, le ); + ANGLE_T a2 = fabs(DifferenceBetweenAngles(GetTrkEndAngle(newTrk,ep),a+180)); + if (GetTrkEndTrk(newTrk,ep)==NULL) { //Not if joined already + if (a2<90 && QueryTrack(lt,Q_IS_CORNU)) { //And Cornu in the right direction + GetTrackParams( PARAMS_EXTEND, newTrk, GetTrkEndPos(newTrk,ep), ¶ms); + nearest_pos = GetTrkEndPos(newTrk,ep); + nearest_angle = NormalizeAngle(params.angle+180.0); + nearest_radius = params.arcR; + nearest_center = params.arcP; + nearest_ep = ep; + } + dd = d; + } + } if (off.x > maxX) maxX = off.x; } maxX += trackGauge; pos = Dto.pos; + if (QueryTrack(lt,Q_IS_CORNU)) { + if (nearest_ep >=0) { + SetCornuEndPt(lt, le, nearest_pos, nearest_center, nearest_angle, nearest_radius); + ConnectTracks(newTrk,nearest_ep,lt,le); + } else { + UndrawNewTrack(lt); + DeleteTrack(lt,TRUE); + } + } else { AuditTracks( "addTurnout T%d[%d] before trimming L%d[%d]", GetTrkIndex(newTrk), i, GetTrkIndex(lt), le ); - TrimTrack( lt, le, maxX ); + wBool_t rc = TrimTrack( lt, le, maxX, nearest_pos, nearest_angle, nearest_radius, nearest_center ); AuditTracks( "addTurnout T%d[%d] after trimming L%d[%d]", GetTrkIndex(newTrk), i, GetTrkIndex(lt), le ); + + } } } @@ -2198,17 +2679,59 @@ LOG( log_turnout, 1, ( " deleting leftover T%d\n", static void TurnoutRotate( void * pangle ) { + if (Dto.state == 0) + return; 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; + angle /= 1000.0; + 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; + TempRedraw(); // TurnoutRotate +} + +static dynArr_t anchors_da; +#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N) + +void static CreateArrowAnchor(coOrd pos,ANGLE_T a,DIST_T len) { + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).width = 0; + anchors(i).u.l.pos[0] = pos; + Translate(&anchors(i).u.l.pos[1],pos,NormalizeAngle(a+135),len); + anchors(i).color = wDrawColorBlue; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).width = 0; + anchors(i).u.l.pos[0] = pos; + Translate(&anchors(i).u.l.pos[1],pos,NormalizeAngle(a-135),len); + anchors(i).color = wDrawColorBlue; +} + +void static CreateRotateAnchor(coOrd pos) { + DIST_T d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = SEG_CRVLIN; + anchors(i).width = 0.5; + anchors(i).u.c.center = pos; + anchors(i).u.c.a0 = 180.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).u.c.radius = d*2; + anchors(i).color = wDrawColorAqua; + coOrd head; //Arrows + for (int j=0;j<3;j++) { + Translate(&head,pos,j*120,d*2); + CreateArrowAnchor(head,NormalizeAngle((j*120)+90),d); + } +} + +void static CreateMoveAnchor(coOrd pos) { + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),pos,0,TRUE,wDrawColorBlue); + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),pos,90,TRUE,wDrawColorBlue); } /** @@ -2230,71 +2753,71 @@ EXPORT STATUS_T CmdTurnoutAction( #ifdef NEWROTATE static ANGLE_T origAngle; #endif + switch (action & 0xFF) { case C_START: Dto.state = 0; Dto.trk = NULL; Dto.angle = 0.0; + DYNARR_RESET(trkSeg_t,anchors_da); return C_CONTINUE; + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + if (Dto.state && (MyGetKeyState()&WKEY_CTRL)) { + CreateRotateAnchor(pos); + } else { + CreateMoveAnchor(pos); + } + return C_CONTINUE; + break; case C_DOWN: + DYNARR_RESET(trkSeg_t,anchors_da); if ( curTurnout == NULL ) return C_CONTINUE; - if (Dto.state == 1) { - DrawSegs( &tempD, Dto.pos, Dto.angle, - curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); - } - PlaceTurnout( pos ); + PlaceTurnout( pos, NULL ); Dto.state = 1; - DrawSegs( &tempD, Dto.pos, Dto.angle, - curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + CreateMoveAnchor(pos); return C_CONTINUE; case C_MOVE: + DYNARR_RESET(trkSeg_t,anchors_da); 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 ); + Dto.state = 1; + PlaceTurnout( pos, Dto.trk ); + CreateMoveAnchor(pos); 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") ); + DYNARR_RESET(trkSeg_t,anchors_da); + CreateMoveAnchor(pos); + InfoMessage( _("Left-Drag to place, Ctrl+Left-Drag or Right-Drag to Rotate, Space or Enter to accept, Esc to Cancel") ); return C_CONTINUE; case C_RDOWN: + DYNARR_RESET(trkSeg_t,anchors_da); 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; + if (Dto.state == 0) { + Dto.pos = pos; // If first, use pos, otherwise use current +LOG( log_turnout, 1, ( "RDOWN @ %0.3fx%0.3f\n", Dto.pos.x, Dto.pos.y ) ); + } Dto.rot0 = Dto.rot1 = pos; - DrawLine( &tempD, Dto.rot0, Dto.rot1, 0, wDrawColorBlack ); - Dto.state = 1; + CreateRotateAnchor(pos); + Dto.state = 2; 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: + DYNARR_RESET(trkSeg_t,anchors_da); 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 ); @@ -2303,6 +2826,7 @@ EXPORT STATUS_T CmdTurnoutAction( validAngle = TRUE; } Dto.pos = origPos; +LOG( log_turnout, 1, ( "RMOVE pre @ %0.3fx%0.3f\n", Dto.pos.x, Dto.pos.y ) ); #ifdef NEWROTATE angle -= baseAngle; Dto.angle = NormalizeAngle( origAngle + angle ); @@ -2311,36 +2835,33 @@ EXPORT STATUS_T CmdTurnoutAction( Dto.angle = angle - curTurnout->endPt[(int)curTurnoutEp].angle; #endif Rotate( &Dto.pos, Dto.rot0, angle ); +LOG( log_turnout, 1, ( "RMOVE post @ %0.3fx%0.3f\n", Dto.pos.x, Dto.pos.y ) ); } 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 ); + Dto.state = 2; + CreateRotateAnchor(Dto.rot0); return C_CONTINUE; case C_RUP: + DYNARR_RESET(trkSeg_t,anchors_da); 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") ); + Dto.state = 1; + CreateMoveAnchor(pos); + InfoMessage( _("Left-Drag to place, Ctrl+Left-Drag or Right-Drag to Rotate, Space or Enter to accept, Esc to Cancel") ); return C_CONTINUE; case C_LCLICK: + DYNARR_RESET(trkSeg_t,anchors_da); 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 ); + PlaceTurnout( Dto.place, Dto.trk ); } else { CmdTurnoutAction( C_DOWN, pos ); CmdTurnoutAction( C_UP, pos ); @@ -2348,15 +2869,19 @@ EXPORT STATUS_T CmdTurnoutAction( return C_CONTINUE; case C_REDRAW: - if (Dto.state) + if (Dto.state) { DrawSegs( &tempD, Dto.pos, Dto.angle, curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + } + if (anchors_da.cnt>0) { + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + } + if (Dto.state == 2) + DrawLine( &tempD, Dto.rot0, Dto.rot1, 0, wDrawColorBlack ); return C_CONTINUE; case C_CANCEL: - if (Dto.state) - DrawSegs( &tempD, Dto.pos, Dto.angle, - curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue ); + DYNARR_RESET(trkSeg_t,anchors_da); Dto.state = 0; Dto.trk = NULL; /*wHide( newTurn.reg.win );*/ @@ -2365,11 +2890,16 @@ EXPORT STATUS_T CmdTurnoutAction( case C_TEXT: if ((action>>8) != ' ') return C_CONTINUE; + /*no break*/ case C_OK: + DYNARR_RESET(trkSeg_t,anchors_da); AddTurnout(); + Dto.state=0; + Dto.trk = NULL; return C_TERMINATE; case C_FINISH: + DYNARR_RESET(trkSeg_t,anchors_da); if (Dto.state != 0 && Dto.trk != NULL) CmdTurnoutAction( C_OK, pos ); else @@ -2377,10 +2907,7 @@ EXPORT STATUS_T CmdTurnoutAction( return C_TERMINATE; case C_CMDMENU: - if ( turnoutPopupM == NULL ) { - turnoutPopupM = MenuRegister( "Turnout Rotate" ); - AddRotateMenu( turnoutPopupM, TurnoutRotate ); - } + menuPos = pos; wMenuPopupShow( turnoutPopupM ); return C_CONTINUE; @@ -2403,7 +2930,7 @@ static STATUS_T CmdTurnout( 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 ); + turnoutW = ParamCreateDialog( &turnoutPG, MakeWindowTitle(_("Turnout")), _("Close"), (paramActionOkProc)TurnoutOk, wHide, TRUE, NULL, F_RESIZE|F_RECALLSIZE|PD_F_ALT_CANCELLABEL, TurnoutDlgUpdate ); InitNewTurn( turnoutNewM ); } /* ParamDialogOkActive( &turnoutPG, FALSE ); */ @@ -2425,12 +2952,21 @@ static STATUS_T CmdTurnout( ParamGroupRecord( &turnoutPG ); return CmdTurnoutAction( action, pos ); + case wActionMove: + return CmdTurnoutAction( action, pos ); + case C_DOWN: case C_RDOWN: ParamDialogOkActive( &turnoutPG, TRUE ); if (hideTurnoutWindow) wHide( turnoutW ); + if (((action&0xFF) == C_DOWN) && (MyGetKeyState()&WKEY_CTRL)) + return CmdTurnoutAction(C_RDOWN, pos); //Convert CTRL into Right + /*no break*/ case C_MOVE: + if (MyGetKeyState()&WKEY_CTRL) + return CmdTurnoutAction (C_RMOVE, pos); + /*no break*/ case C_RMOVE: return CmdTurnoutAction( action, pos ); @@ -2438,11 +2974,13 @@ static STATUS_T CmdTurnout( 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") ); + + InfoMessage( _("Left-Drag to place, Ctrl+Left-Drag or Right-Drag to Rotate, Space or Enter to accept, Esc to Cancel") ); + if (((action&0xFF) == C_UP) && (MyGetKeyState()&WKEY_CTRL)) + return CmdTurnoutAction (C_RUP, pos); return CmdTurnoutAction( action, pos ); case C_LCLICK: - HilightEndPt(); CmdTurnoutAction( action, pos ); HilightEndPt(); return C_CONTINUE; @@ -2487,7 +3025,7 @@ static char * CmdTurnoutHotBarProc( 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 */ + DoCommandB( (void*)(intptr_t)turnoutHotBarCmdInx ); /* continue with new turnout / structure */ return NULL; case HB_LISTTITLE: FormatCompoundTitle( listLabels, to->title ); @@ -2517,7 +3055,7 @@ EXPORT void AddHotBarTurnouts( void ) to->segCnt > 0 && CompatibleScale( TRUE, to->scaleInx, GetLayoutCurScale()) ) ) continue; - AddHotBarElement( to->contentsLabel, to->size, to->orig, TRUE, to->barScale, to, CmdTurnoutHotBarProc ); + AddHotBarElement( to->contentsLabel, to->size, to->orig, TRUE, FALSE, to->barScale, to, CmdTurnoutHotBarProc ); } } @@ -2537,31 +3075,61 @@ static STATUS_T CmdTurnoutHotBar( switch (action & 0xFF) { case C_START: - TurnoutChange( CHANGE_PARAMS|CHANGE_SCALE ); + //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 ); + wIndex_t listIndex = FindListItemByContext( turnoutListL, curTurnout ); + if ( listIndex > 0 ) + turnoutInx = listIndex; + ParamLoadControls( &turnoutPG ); + ParamGroupRecord( &turnoutPG ); + return CmdTurnoutAction( action, pos ); + + case wActionMove: + return CmdTurnoutAction( action, pos ); + + case C_DOWN: + if (MyGetKeyState()&WKEY_CTRL) { + return CmdTurnoutAction( C_RDOWN, pos ); + } + /*no break*/ + case C_RDOWN: + return CmdTurnoutAction( action, pos ); + + case C_MOVE: + if (MyGetKeyState()&WKEY_CTRL) { + return CmdTurnoutAction( C_RMOVE, pos ); + } + /*no break*/ + case C_RMOVE: return CmdTurnoutAction( action, pos ); case C_UP: + if (MyGetKeyState()&WKEY_CTRL) { + return CmdTurnoutAction( C_RUP, pos ); + } + /*no break*/ 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") ); + InfoMessage( _("Left-Drag to place, Ctrl+Left-Drag or Right-Drag to Rotate, Space or Enter to accept, Esc to Cancel") ); return CmdTurnoutAction( action, pos ); + case C_REDRAW: + return CmdTurnoutAction( action, pos ); case C_TEXT: if ((action>>8) != ' ') return C_CONTINUE; + /* no break*/ case C_OK: CmdTurnoutAction( action, pos ); return C_CONTINUE; case C_CANCEL: HotBarCancel(); + /*no break*/ default: return CmdTurnoutAction( action, pos ); } @@ -2573,12 +3141,18 @@ static STATUS_T CmdTurnoutHotBar( 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 ); + AddMenuButton( menu, CmdTurnout, "cmdTurnout", _("Predefined Track"), wIconCreatePixMap(turnout_xpm), LEVEL0_50, IC_WANT_MOVE|IC_STICKY|IC_LCLICK|IC_CMDMENU|IC_POPUP2, ACCL_TURNOUT, NULL ); + turnoutHotBarCmdInx = AddMenuButton( menu, CmdTurnoutHotBar, "cmdTurnoutHotBar", "", NULL, LEVEL0_50, IC_WANT_MOVE|IC_STICKY|IC_LCLICK|IC_CMDMENU|IC_POPUP2, 0, NULL ); RegisterChangeNotification( TurnoutChange ); ParamRegister( &turnoutPG ); log_turnout = LogFindIndex( "turnout" ); log_traverseTurnout = LogFindIndex( "traverseTurnout" ); + log_suppressCheckPaths = LogFindIndex( "suppresscheckpaths" ); + log_splitturnout = LogFindIndex( "splitturnout" ); + if ( turnoutPopupM == NULL ) { + turnoutPopupM = MenuRegister( "Turnout Rotate" ); + AddRotateMenu( turnoutPopupM, TurnoutRotate ); + } } #endif @@ -2587,7 +3161,7 @@ EXPORT void InitTrkTurnout( void ) T_TURNOUT = InitObject( &turnoutCmds ); /*InitDebug( "Turnout", &debugTurnout );*/ - AddParam( N_("TURNOUT "), ReadTurnoutParam ); + AddParam( "TURNOUT ", ReadTurnoutParam); } #ifdef TEST diff --git a/app/bin/cturntbl.c b/app/bin/cturntbl.c index 9264572..f15aeff 100644 --- a/app/bin/cturntbl.c +++ b/app/bin/cturntbl.c @@ -162,9 +162,19 @@ static ANGLE_T ConstrainTurntableAngle( track_p trk, coOrd pos ) static EPINX_T NewTurntableEndPt( track_p trk, ANGLE_T angle ) { struct extraData *xx = GetTrkExtraData(trk); - EPINX_T ep = GetTrkEndPtCnt(trk); + EPINX_T ep = -1; + /* Reuse an old empty ep if it exists */ + for (int i =0;i< GetTrkEndPtCnt(trk)-1;i++) { + if (GetTrkEndTrk(trk,i) == NULL) { + ep = i; + break; + } + } + if (ep == -1) { + ep = GetTrkEndPtCnt(trk); + SetTrkEndPtCnt( trk, ep+1 ); + } coOrd pos; - SetTrkEndPtCnt( trk, ep+1 ); PointOnCircle( &pos, xx->pos, xx->radius, angle ); SetTrkEndPoint( trk, ep, pos, angle ); return ep; @@ -182,7 +192,8 @@ 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; + long widthOptions = DTS_LEFT|DTS_RIGHT; + if ( !ValidateTurntablePosition(t) ) { p0.y = p1.y = xx->pos.y; @@ -194,17 +205,13 @@ static void DrawTurntable( track_p t, drawCmd_p d, wDrawColor color ) } 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 ); - } + DrawArc( d, xx->pos, xx->radius, 0.0, 360.0, 0, (color == wDrawColorPreviewSelected || color == wDrawColorPreviewUnselected)?3:0, color ); + DrawStraightTrack( d, p0, p1, FindAngle(p0,p1), 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) && + if ( ((d->options&DC_SIMPLE)==0) && (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) && labelScale >= d->scale ) { LabelLengths( d, t, color ); @@ -218,9 +225,7 @@ static DIST_T DistanceTurntable( track_p trk, coOrd * p ) ANGLE_T a; coOrd pos0, pos1; - d = FindDistance( xx->pos, *p ) - xx->radius; - if (d < 0.0) - d = 0.0; + d = FindDistance( xx->pos, *p ) - xx->radius; //OK to be negative if ( programMode == MODE_DESIGN ) { a = FindAngle( xx->pos, *p ); Translate( p, xx->pos, a, d+xx->radius ); @@ -310,11 +315,11 @@ static BOOL_T WriteTurntable( track_p t, FILE * f ) 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; + rc &= fprintf(f, "\t%s\n", END_SEGS)>0; return rc; } -static void ReadTurntable( char * line ) +static BOOL_T ReadTurntable( char * line ) { track_p trk; struct extraData *xx; @@ -333,12 +338,17 @@ static void ReadTurntable( char * line ) paramVersion<10?"dL000sdpffX": "dL000sdpffd", &index, &layer, scale, &visible, &p, &elev, &r, &currEp )) - return; + return FALSE; + if ( !ReadSegs() ) + return FALSE; trk = NewTrack( index, T_TURNTABLE, 0, sizeof *xx ); - ReadSegs(); SetEndPts( trk, 0 ); xx = GetTrkExtraData(trk); - SetTrkVisible(trk, visible); + if ( paramVersion < 3 ) { + SetTrkVisible(trk, visible!=0); + } else { + SetTrkVisible(trk, visible&2); + } SetTrkScale(trk, LookupScale( scale ) ); SetTrkLayer(trk, layer); xx->pos = p; @@ -346,6 +356,7 @@ static void ReadTurntable( char * line ) xx->currEp = currEp; xx->reverse = 0; ComputeTurntableBoundingBox( trk ); + return TRUE; } static void MoveTurntable( track_p trk, coOrd orig ) @@ -590,22 +601,32 @@ static STATUS_T ModifyTurntable( track_p trk, wAction_t action, coOrd pos ) } EXPORT BOOL_T ConnectTurntableTracks( - track_p trk1, - EPINX_T ep1, + track_p trk1, /*The turntable */ + EPINX_T ep1, /*Ignored */ track_p trk2, EPINX_T ep2 ) { coOrd center, pos; DIST_T radius; + DIST_T dist; + if (!QueryTrack(trk1,Q_CAN_ADD_ENDPOINTS)) return FALSE; TurntableGetCenter( trk1, ¢er, &radius ); pos = GetTrkEndPos(trk2,ep2); ANGLE_T angle = FindAngle(center, GetTrkEndPos(trk2,ep2)); - if (NormalizeAngle(GetTrkEndAngle(trk2,ep2) + 180 - angle) < connectAngle) { - if (FindDistance(center,pos)-radius < connectDistance) { + if (fabs(DifferenceBetweenAngles(GetTrkEndAngle(trk2,ep2),angle+180)) <= connectAngle) { + dist = FindDistance(center,pos)-radius; + if (dist < connectDistance) { + UndoStart( _("Connect Turntable Tracks"), "TurnTracks(T%d[%d] T%d[%d] D%0.3f A%0.3F )", + GetTrkIndex(trk1), ep1, GetTrkIndex(trk2), ep2, dist, angle ); + UndoModify(trk1); EPINX_T ep = NewTurntableEndPt(trk1,angle); - ConnectTracks( trk1, ep, trk2, ep2 ); + if (ConnectTracks( trk1, ep, trk2, ep2 )) { + UndoUndo(); + return FALSE; + } return TRUE; } } + ErrorMessage( MSG_TOO_FAR_APART_DIVERGE ); return FALSE; } @@ -688,13 +709,12 @@ 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_NODRAWENDPT: case Q_CAN_NEXT_POSITION: case Q_ISTRACK: case Q_NOT_PLACE_FROGPOINTS: case Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK: case Q_CAN_ADD_ENDPOINTS: - case Q_CAN_EXTEND: return TRUE; case Q_MODIFY_CAN_SPLIT: case Q_CORNU_CAN_MODIFY: @@ -727,7 +747,19 @@ static void DrawTurntablePositionIndicator( track_p trk, wDrawColor color ) 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 ); + DrawLine( &tempD, pos0, pos1, 3, color ); +} + +static wBool_t CompareTurntable( track_cp trk1, track_cp trk2 ) +{ + struct extraData *xx1 = GetTrkExtraData( trk1 ); + struct extraData *xx2 = GetTrkExtraData( trk2 ); + char * cp = message + strlen(message); + REGRESS_CHECK_POS( "Pos", xx1, xx2, pos ) + REGRESS_CHECK_DIST( "Radius", xx1, xx2, radius ) + REGRESS_CHECK_INT( "CurrEp", xx1, xx2, currEp ) + REGRESS_CHECK_INT( "Reverse", xx1, xx2, reverse ) + return TRUE; } static void AdvanceTurntablePositionIndicator( @@ -744,7 +776,6 @@ static void AdvanceTurntablePositionIndicator( 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 ); @@ -762,7 +793,6 @@ static void AdvanceTurntablePositionIndicator( } *angleR = angle1; xx->currEp = ep; - DrawTurntablePositionIndicator( trk, selectedColor ); } @@ -794,13 +824,21 @@ static trackCmd_t turntableCmds = { FlipTurntable, DrawTurntablePositionIndicator, AdvanceTurntablePositionIndicator, - CheckTraverseTurntable }; + CheckTraverseTurntable, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + CompareTurntable }; static STATUS_T CmdTurntable( wAction_t action, coOrd pos ) { track_p t; static coOrd pos0; + static int state = 0; wControl_p controls[2]; char * labels[1]; @@ -819,6 +857,7 @@ static STATUS_T CmdTurntable( wAction_t action, coOrd pos ) labels[0] = N_("Diameter"); InfoSubstituteControls( controls, labels ); /*InfoMessage( "Place Turntable");*/ + state = 0; return C_CONTINUE; case C_DOWN: @@ -833,18 +872,15 @@ static STATUS_T CmdTurntable( wAction_t action, coOrd pos ) InfoSubstituteControls( controls, labels ); ParamLoadData( &turntablePG ); pos0 = pos; - DrawArc( &tempD, pos0, turntableDiameter/2.0, 0.0, 360.0, 0, 0, wDrawColorBlack ); + state = 1; 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 ); @@ -853,10 +889,13 @@ static STATUS_T CmdTurntable( wAction_t action, coOrd pos ) InfoSubstituteControls( NULL, NULL ); sprintf( message, "turntable-diameter-%s", curScaleName ); wPrefSetFloat( "misc", message, turntableDiameter ); + state = 0; return C_TERMINATE; case C_REDRAW: - DrawArc( &tempD, pos0, turntableDiameter/2.0, 0.0, 360.0, 0, 0, wDrawColorBlack ); + if ( state > 0 ) { + DrawArc( &tempD, pos0, turntableDiameter/2.0, 0.0, 360.0, 0, 0, wDrawColorBlack ); + } return C_CONTINUE; case C_CANCEL: @@ -874,7 +913,7 @@ static STATUS_T CmdTurntable( wAction_t action, coOrd pos ) EXPORT void InitCmdTurntable( wMenu_p menu ) { - AddMenuButton( menu, CmdTurntable, "cmdTurntable", _("Turntable"), wIconCreatePixMap(turntbl_xpm), LEVEL0_50, IC_STICKY, ACCL_TURNTABLE, NULL ); + AddMenuButton( menu, CmdTurntable, "cmdTurntable", _("Custom Turntable"), wIconCreatePixMap(turntbl_xpm), LEVEL0_50, IC_STICKY|IC_INITNOTSTICKY, ACCL_TURNTABLE, NULL ); } diff --git a/app/bin/custom.c b/app/bin/custom.c index 618a8ac..68a996b 100644 --- a/app/bin/custom.c +++ b/app/bin/custom.c @@ -53,7 +53,6 @@ #define product "xtrkcad" #define PRODUCT "XTRKCAD" #define Version VERSION -#define KEYCODE "x" #define PARAMKEY (0) @@ -68,15 +67,18 @@ char * sTurnoutDesignerW = NULL; char * sAboutProd = NULL; char * sCustomF = product ".cus"; -char * sCheckPointF = product ".ckp"; -char * sCheckPoint1F = product ".ck1"; +char * sCheckPointF = product Version ".ckp"; +char * sCheckPoint1F = product Version ".ck1"; + char * sClipboardF = product ".clp"; -char * sParamQF = product "." KEYCODE "tq"; +char * sParamQF = product ".xtq"; char * sUndoF = product ".und"; char * sAuditF = product ".aud"; char * sTipF = product ".tip"; char * sSourceFilePattern = NULL; +char * sSaveFilePattern = NULL; +char * sImageFilePattern = NULL; char * sImportFilePattern = NULL; char * sDXFFilePattern = NULL; char * sRecordFilePattern = NULL; @@ -137,13 +139,14 @@ BOOL_T Initialize( void ) InitTrkStruct(); InitTrkText(); InitTrkDraw(); - InitTrkNote(); + InitTrkBlock(); InitTrkSwitchMotor(); InitTrkSignal(); InitTrkControl(); InitTrkSensor(); InitCarDlg(); + InitCmdNote(); memset( message, 0, sizeof message ); @@ -156,7 +159,7 @@ BOOL_T Initialize( void ) void InitCustom( void ) { - char buf[STR_SHORT_SIZE]; + char *buf = malloc(1024); /* Initialize some localized strings */ if (sTurnoutDesignerW == NULL) @@ -171,38 +174,60 @@ void InitCustom( void ) } if (sSourceFilePattern == NULL) { - sprintf(buf, _("%s Files|*.xtc"), Product); + sprintf(buf, _("All %s Files (*.xtc,*.xtce)|*.xtc;*.xtce|" + "%s Trackplan (*.xtc)|*.xtc|" + "%s Extended Trackplan (*.xtce)|*.xtce|" + "All Files (*)|*"), + Product, + Product, + Product ); sSourceFilePattern = strdup(buf); } + if (sSaveFilePattern == NULL) + { + sprintf(buf, _("%s Trackplan (*.xtc)|*.xtc|" + "%s Extended Trackplan (*.xtce)|*.xtce|" + "All Files (*)|*"), + Product, + Product ); + sSaveFilePattern = strdup(buf); + } + if (sImageFilePattern == NULL) + { + sprintf(buf,_("All Files (*)|*")); + sImageFilePattern = strdup(buf); + } if (sImportFilePattern == NULL) { - sprintf(buf, _("%s Import Files|*.%sti"), Product, KEYCODE); + sprintf(buf, _("%s Import Files (*.xti)|*.xti"), Product ); sImportFilePattern = strdup(buf); } if (sDXFFilePattern == NULL) { - sDXFFilePattern = strdup(_("Data Exchange Format Files|*.dxf")); + sDXFFilePattern = strdup(_("Data Exchange Format Files (*.dxf)|*.dxf")); } if (sRecordFilePattern == NULL) { - sprintf(buf, _("%s Record Files|*.%str"), Product, KEYCODE); + sprintf(buf, _("%s Record Files (*.xtr)|*.xtr"), Product); sRecordFilePattern = strdup(buf); } if (sNoteFilePattern == NULL) { - sprintf(buf, _("%s Note Files|*.not"), Product); + sprintf(buf, _("%s Note Files (*.not)|*.not"), Product); sNoteFilePattern = strdup(buf); } if (sLogFilePattern == NULL) { - sprintf(buf, _("%s Log Files|*.log"), Product); + sprintf(buf, _("%s Log Files (*.log)|*.log"), Product); sLogFilePattern = strdup(buf); } if (sPartsListFilePattern == NULL) { - sprintf(buf, _("%s PartsList Files|*.txt"), Product); + sprintf(buf, _("%s PartsList Files (*.txt)|*.txt"), Product); sPartsListFilePattern = strdup(buf); } + + free(buf); } diff --git a/app/bin/custom.h b/app/bin/custom.h index a4d335a..1c4f7b6 100644 --- a/app/bin/custom.h +++ b/app/bin/custom.h @@ -45,6 +45,7 @@ #define BG_COUNT (13) #define BG_FILE (14) #define BG_CONTROL (15) +#define BG_EXPORTIMPORT (16) #define BG_BIGGAP (1<<8) extern int cmdGroup; @@ -67,6 +68,8 @@ extern char * sUndoF; extern char * sAuditF; extern char * sSourceFilePattern; +extern char * sSaveFilePattern; +extern char * sImageFilePattern; extern char * sImportFilePattern; extern char * sDXFFilePattern; extern char * sRecordFilePattern; @@ -89,7 +92,7 @@ void InitTrkBezier( void ); void InitTrkDraw( void ); void InitTrkEase( void ); void InitTrkCornu( void ); -void InitTrkNote( void ); +void InitTrkNote(wMenu_p menu); void InitTrkStraight( void ); void InitTrkStruct( void ); void InitTrkText( void ); @@ -103,6 +106,7 @@ void InitTrkControl ( void ); void InitTrkSensor ( void ); void InitCmdCurve( wMenu_p menu ); +void InitCmdCornu( wMenu_p menu); void InitCmdHelix( wMenu_p menu ); void InitCmdDraw( wMenu_p menu ); void InitCmdElevation( wMenu_p menu ); @@ -114,11 +118,15 @@ void InitCmdMove( wMenu_p menu ); void InitCmdMoveDescription( wMenu_p menu ); void InitCmdStraight( wMenu_p menu ); void InitCmdDescribe( wMenu_p menu ); +void InitCmdDescribe2( wMenu_p menu ); void InitCmdSelect( wMenu_p menu ); -void InitCmdPan( wMenu_p menu); +void InitCmdSelect2( wMenu_p menu ); +void InitCmdPan( wMenu_p menu ); +void InitCmdPan2( wMenu_p menu ); void InitCmdDelete( void ); void InitCmdSplit( wMenu_p menu ); void InitCmdTunnel( void ); +void InitCmdBridge( void ); void InitCmdRuler( wMenu_p menu ); void InitCmdParallel( wMenu_p menu ); @@ -128,7 +136,7 @@ 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 InitCmdNote(); void InitCmdUndo( void ); void InitCmdStruct( wMenu_p menu ); void InitCmdAboveBelow( void ); diff --git a/app/bin/dbench.c b/app/bin/dbench.c index 7e44713..c8d944f 100644 --- a/app/bin/dbench.c +++ b/app/bin/dbench.c @@ -257,7 +257,7 @@ EXPORT void DrawBench( 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 ); + DrawPoly( d, 4, pp, NULL, color1, 0, 1, 0); /* Draw Outline */ if ( /*color1 != color2 &&*/ ( ( d->scale < ((d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale) ) || /* big enough scale */ diff --git a/app/bin/dbitmap.c b/app/bin/dbitmap.c index 340bad1..c45c7d0 100644 --- a/app/bin/dbitmap.c +++ b/app/bin/dbitmap.c @@ -112,7 +112,7 @@ static int SaveBitmapFile( (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 ); + wSetCursor( mainD.d, wCursorWait ); InfoMessage( _("Drawing tracks to BitMap") ); DrawSnapGrid( &bitmap_d, mapD.size, TRUE ); if ( (outputBitMapTogglesV&4) ) @@ -126,7 +126,7 @@ static int SaveBitmapFile( return FALSE; } InfoMessage( "" ); - wSetCursor( wCursorNormal ); + wSetCursor( mainD.d, defaultCursor ); wBitMapDelete( bitmap_d.d ); return TRUE; } @@ -211,11 +211,11 @@ static void OutputBitMapOk( void * junk ) 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 +//#ifdef WINDOWS +// _("Bitmap files (*.bmp)|*.bmp"), +//#else + _("Bitmap files (*.png)|*.png"), +//#endif SaveBitmapFile, NULL ); wFilSelect( bitmap_fs, GetCurrentPath( BITMAPPATHKEY )); } diff --git a/app/bin/dcar.c b/app/bin/dcar.c index 2e1a790..e067e2b 100644 --- a/app/bin/dcar.c +++ b/app/bin/dcar.c @@ -47,6 +47,7 @@ static int log_carDlgState; static int log_carDlgList; static paramFloatRange_t r0_99999 = { 0, 99999, 80 }; +static paramFloatRange_t r9999_9999 = {-99999, 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 }; @@ -85,6 +86,7 @@ typedef struct { DIST_T carLength; DIST_T carWidth; DIST_T truckCenter; + DIST_T truckCenterOffset; DIST_T coupledLength; } carDim_t; typedef struct { @@ -381,10 +383,10 @@ static void CarProtoDrawTruck( 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 ); + RescalePts( sizeof truckOutline/sizeof truckOutline[0], p, ratio, ratio ); + RotatePts( sizeof truckOutline/sizeof truckOutline[0], p, zero, angle ); MovePts( sizeof truckOutline/sizeof truckOutline[0], p, pos ); - DrawFillPoly( d, sizeof truckOutline/sizeof truckOutline[0], p, color ); + DrawPoly( d, sizeof truckOutline/sizeof truckOutline[0], p, NULL, color, 0, 1, 0); pp.x = -70/2; pp.y = 0; memcpy( p, wheelOutline, sizeof wheelOutline ); @@ -393,7 +395,7 @@ static void CarProtoDrawTruck( 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 ); + DrawPoly( d, sizeof wheelOutline/sizeof wheelOutline[0], p, NULL, color, 0, 1, 0); pp.x = 70/2; memcpy( p, wheelOutline, sizeof wheelOutline ); RescalePts( sizeof wheelOutline/sizeof wheelOutline[0], p, 1.0, width/56.5 ); @@ -401,7 +403,7 @@ static void CarProtoDrawTruck( 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 ); + DrawPoly( d, sizeof wheelOutline/sizeof wheelOutline[0], p, NULL, color, 0, 1, 0 ); } @@ -438,22 +440,11 @@ static void CarProtoDrawCoupler( 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 ); + DrawPoly( d, sizeof couplerOutline/sizeof couplerOutline[0], p, NULL, color, 0, 1 ,0 ); } @@ -496,7 +487,7 @@ static trkSeg_p carProtoSegPtr; static int carProtoSegCnt; -static coOrd dummyOutlineSegPts[5]; +static pts_t dummyOutlineSegPts[5]; static trkSeg_t dummyOutlineSegs; static void CarProtoDlgCreateDummyOutline( int * segCntP, @@ -507,7 +498,7 @@ static void CarProtoDlgCreateDummyOutline( wDrawColor color ) { trkSeg_p segPtr; - coOrd * pts; + pts_t * pts; DIST_T length2; *segCntP = 1; @@ -523,22 +514,27 @@ static void CarProtoDlgCreateDummyOutline( segPtr->u.p.angle = 0; length2 = length; if ( isLoco ) { - pts->x = length; - pts->y = width/2.0; + pts->pt.x = length; + pts->pt.y = width/2.0; + pts->pt_type = 0; pts++; length2 -= width/2.0; } - pts->x = length2; - pts->y = 0.0; + pts->pt.x = length2; + pts->pt.y = 0.0; + pts->pt_type = 0; pts++; - pts->x = 0.0; - pts->y = 0.0; + pts->pt.x = 0.0; + pts->pt.y = 0.0; + pts->pt_type = 0; pts++; - pts->x = 0.0; - pts->y = width; + pts->pt.x = 0.0; + pts->pt.y = width; + pts->pt_type = 0; pts++; - pts->x = length2; - pts->y = width; + pts->pt.x = length2; + pts->pt.y = width; + pts->pt_type = 0; } @@ -605,6 +601,26 @@ static carProto_p CarProtoLookup( return proto; } +enum paramFileState +GetCarProtoCompatibility(int paramFileIndex, SCALEINX_T scaleIndex) +{ + int i; + enum paramFileState ret = PARAMFILE_NOTUSABLE; + DIST_T ratio = GetScaleRatio(scaleIndex); + + if (!IsParamValid(paramFileIndex)) { + return(PARAMFILE_UNLOADED); + } + + for (i = 0; i < carProto_da.cnt; i++) { + carProto_t *carProto = carProto(i); + if (carProto->paramFileIndex == paramFileIndex) { + ret = PARAMFILE_FIT; + break; + } + } + return(ret); +} static carProto_p CarProtoNew( carProto_p proto, @@ -617,7 +633,7 @@ static carProto_p CarProtoNew( trkSeg_p segPtr ) { if ( proto == NULL ) { - proto = LookupListElem( &carProto_da, desc, CmpCarProto, sizeof *(carProto_p)0 ); + proto = LookupListElem( &carProto_da, desc, CmpCarProto, sizeof *proto ); if ( proto->desc != NULL ) { if ( proto->paramFileIndex == PARAM_CUSTOM && paramFileIndex != PARAM_CUSTOM ) @@ -656,6 +672,45 @@ static void CarProtoDelete( } +/** +* Delete all car prototype definitions that came from a specific parameter file. +* Due to the way the definitions are loaded from file it is safe to +* assume that they form a contiguous block in the array. +* +* \param [IN] fileIndex parameter file +*/ + +void +DeleteCarProto(int fileIndex) +{ + int inx = 0; + int startInx = -1; + int cnt = 0; + + // go to the start of the block + while (inx < carProto_da.cnt && carProto(inx)->paramFileIndex != fileIndex) { + startInx = inx++; + } + + // delete them + for (; inx < carProto_da.cnt && carProto(inx)->paramFileIndex == fileIndex; inx++) { + carProto_t * cp = carProto(inx); + if (cp->paramFileIndex == fileIndex) { + CarProtoDelete(cp); + cnt++; + } + } + + // copy down the rest of the list to fill the gap + startInx++; + while (inx < carProto_da.cnt) { + carProto(startInx++) = carProto(inx++); + } + + // and reduce the actual number + carProto_da.cnt -= cnt; +} + static BOOL_T CarProtoRead( char * line ) { @@ -663,10 +718,12 @@ static BOOL_T CarProtoRead( long options; long type; carDim_t dim; + long longCenterOffset; - if ( !GetArgs( line+9, "qllff00ff", - &desc, &options, &type, &dim.carLength, &dim.carWidth, &dim.truckCenter, &dim.coupledLength ) ) + if ( !GetArgs( line+9, "qllff0lff", + &desc, &options, &type, &dim.carLength, &dim.carWidth, &longCenterOffset, &dim.truckCenter, &dim.coupledLength ) ) return FALSE; + dim.truckCenterOffset = longCenterOffset/1000.0; if ( !ReadSegs() ) return FALSE; CarProtoNew( NULL, curParamFileIndex, desc, options, type, &dim, tempSegs_da.cnt, &tempSegs(0) ); @@ -685,8 +742,10 @@ static BOOL_T CarProtoWrite( 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; + long longCenterOffset = (long)(proto->dim.truckCenterOffset*1000); + + rc &= fprintf( f, "CARPROTO \"%s\" %ld %ld %0.3f %0.3f 0 %ld %0.3f %0.3f\n", + PutTitle(proto->desc), proto->options, proto->type, proto->dim.carLength, proto->dim.carWidth, longCenterOffset, proto->dim.truckCenter, proto->dim.coupledLength )>0; rc &= WriteSegs( f, proto->segCnt, proto->segPtr ); RestoreLocale(oldLocale); @@ -850,7 +909,7 @@ static roadnameMap_p LoadRoadnameList( cmp_key.name = roadnameTab->ptr; cmp_key.len = roadnameTab->len; - roadnameMapP = LookupListElem( &roadnameMap_da, &cmp_key, Cmp_roadnameMap, sizeof *(roadnameMap_p)0 ); + roadnameMapP = LookupListElem( &roadnameMap_da, &cmp_key, Cmp_roadnameMap, sizeof roadnameMapP ); if ( roadnameMapP->roadname == NULL ) { roadnameMapP->roadname = TabStringDup(roadnameTab); roadnameMapP->repmark = TabStringDup(repmarkTab); @@ -957,29 +1016,29 @@ static carPart_p CarPartNew( 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 ) + 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 ) + 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 ) + 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 ) ) + 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 ) ) + 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 ); + 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; @@ -989,13 +1048,13 @@ LOG( log_carList, 2, ( "upd car part: %s (%d)\n", title, paramFileIndex ) ) 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 = (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;; + 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; } @@ -1012,6 +1071,32 @@ static void CarPartDelete( MyFree( partP ); } +/** +* Delete all car part definitions that came from a specific parameter file. +* CarParts are stored in DYNARR for the specific car model. These DYNARRs +* are linked from CarPartParents, again DYNARRs. Thes parents are created +* from part definition and only contain manufacturer and type information. +* +* \param [IN] fileIndex parameter file +*/ + +void +DeleteCarPart(int fileIndex) +{ + int inxParent = 0; + int inx; + + while (inxParent < carPartParent_da.cnt) { + inx = 0; + while (inx < carPartParent(inxParent)->parts_da.cnt) { + carPart_p part = carPart(carPartParent(inxParent), inx++); + if (part->paramFileIndex == fileIndex) { + CarPartDelete(part); + } + } + inxParent++; + } +} static BOOL_T CarPartRead( char * line ) @@ -1022,10 +1107,12 @@ static BOOL_T CarPartRead( char * title; carDim_t dim; long rgb; + long longCenterOffset; - if ( !GetArgs( line+8, "sqllff00ffl", - scale, &title, &options, &type, &dim.carLength, &dim.carWidth, &dim.truckCenter, &dim.coupledLength, &rgb ) ) + if ( !GetArgs( line+8, "sqllff0lffl", + scale, &title, &options, &type, &dim.carLength, &dim.carWidth, &longCenterOffset, &dim.truckCenter, &dim.coupledLength, &rgb ) ) return FALSE; + dim.truckCenterOffset = longCenterOffset/1000.0; CarPartNew( NULL, curParamFileIndex, LookupScale(scale), title, options, type, &dim, wDrawFindColor(rgb) ); MyFree( title ); return TRUE; @@ -1181,6 +1268,42 @@ static carItem_p CarItemNew( return item; } +/** + * Check the whether the parameter file has CARPARTS that are compatible + * with the current state. For CARPARTS only the exactly identical scale + * is accepted as compatible + * + * \param paramFileIndex IN the parameter file + * \param scaleIndex IN the scale to check against + * \return the compatibility state of the the + */ +enum paramFileState +GetCarPartCompatibility(int paramFileIndex, SCALEINX_T scaleIndex) +{ + int i; + enum paramFileState ret = PARAMFILE_NOTUSABLE; + DIST_T ratio = GetScaleRatio(scaleIndex); + + if (!IsParamValid(paramFileIndex)) { + return(PARAMFILE_UNLOADED); + } + + for (i = 0; i < carPartParent_da.cnt && ret != PARAMFILE_FIT; i++) { + carPartParent_t *carPartParent = carPartParent( i ); + + if(GetScaleRatio(carPartParent->scale) == ratio ){ + for(int j = 0; j < carPartParent->parts_da.cnt; j++ ){ + carPart_t *carPart = carPart( carPartParent, j ); + + if (carPart->paramFileIndex == paramFileIndex) { + ret = PARAMFILE_FIT; + break; + } + } + } + } + return(ret); +} EXPORT BOOL_T CarItemRead( char * line ) @@ -1197,45 +1320,43 @@ EXPORT BOOL_T CarItemRead( 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; + long longCenterOffset; + char * sNote = NULL; - if ( !GetArgs( line+4, "lsqll" "ff00ffl" "fflll000000c", + if ( !GetArgs( line+4, "lsqll" "ff0lffl" "fflll000000c", &itemIndex, scale, &title, &options, &type, - &dim.carLength, &dim.carWidth, &dim.truckCenter, &dim.coupledLength, &rgb, + &dim.carLength, &dim.carWidth, &longCenterOffset, &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'; + dim.truckCenterOffset = longCenterOffset/1000.0; + if ( paramVersion < 12 ) { + if ( (options&CAR_ITEM_HASNOTES) ) { + sNote = ReadMultilineText(); } - DYNARR_APPEND( char, buffer_da, 1 ); - ((char*)buffer_da.ptr)[buffer_da.cnt-1] = 0; + } else { + if ( !GetArgs( cp, "qc", &sNote, &cp ) ) + return FALSE; } 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 ); + item->data.notes = sNote; MyFree(title); if ( (options&CAR_ITEM_ONLAYOUT) ) { if ( !GetArgs( cp, "dLpf", &index, &layer, &pos, &angle ) ) return FALSE; + if ( !ReadSegs() ) + return FALSE; item->car = NewCar( index, item, pos, angle ); SetTrkLayer( item->car, layer ); - ReadSegs(); SetEndPts( item->car, 2 ); ComputeBoundingBox( item->car ); } @@ -1253,6 +1374,7 @@ static BOOL_T CarItemWrite( ANGLE_T angle; BOOL_T rc = TRUE; char *oldLocale = NULL; + long longCenterOffset = (long)(item->dim.truckCenterOffset*1000); oldLocale = SaveLocale("C"); @@ -1260,25 +1382,27 @@ static BOOL_T CarItemWrite( 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", + rc &= fprintf( f, "CAR %ld %s \"%s\" %ld %ld %0.3f %0.3f 0 %ld %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->dim.carLength, item->dim.carWidth, longCenterOffset, 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_HASNOTES) ) { + char * sEscapedNote = ConvertToEscapedText( item->data.notes ); + rc &= fprintf( f, " \"%s\"", sEscapedNote )>0; + MyFree( sEscapedNote ); + } else { + rc &= fprintf( f, " \"\"" ) > 0; + } if ( ( options&CAR_ITEM_ONLAYOUT) ) { CarGetPos( item->car, &pos, &angle ); - rc &= fprintf( f, " %d %u %0.3f %0.3f %0.3f", + rc &= fprintf( f, " %d %u %0.3f %0.3f %0.3f\n", 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; + rc &= fprintf( f, "\t%s\n", END_SEGS )>0; + } else { + rc &= fprintf( f, "\n" )>0; } RestoreLocale(oldLocale); @@ -1658,33 +1782,76 @@ EXPORT void AddHotBarCarDesc( void ) 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 ); + AddHotBarElement( FormatCarTitle( item1, carHotbarContents[carHotbarModeInx] ), size, orig, FALSE, FALSE, (60.0*12.0/curScaleRatio), (void*)(intptr_t)inx, CarItemHotbarProc ); } item0 = item1; } } -EXPORT coOrd CarItemFindCouplerMountPoint( +EXPORT void CarItemFindCouplerMountPoint( carItem_p item, - traverseTrack_t trvTrk, - int dir ) + traverseTrack_t trvTrk0, + coOrd pos[2] ) { - DIST_T couplerOffset; - coOrd pos; + // We assume the coupler pivot is 'couplerLength' before the end of the car + DIST_T couplerLength = (item->dim.coupledLength - item->dim.carLength) / 2.0; + if ( IsClose(item->dim.truckCenter) ) { + // Single truck/bogie + DIST_T d = item->dim.carLength/2.0 - couplerLength; + Translate( &pos[0], trvTrk0.pos, trvTrk0.angle, d + item->dim.truckCenterOffset ); + FlipTraverseTrack( &trvTrk0 ); + Translate( &pos[1], trvTrk0.pos, trvTrk0.angle, d - item->dim.truckCenterOffset ); + return; + } + // Find the pos of the 2 trucks + // Note this is a slight simplification, we should use the car center, not the on-track position + traverseTrack_t trvTrk1 = trvTrk0; + TraverseTrack2( &trvTrk0, item->dim.truckCenter/2.0 + item->dim.truckCenterOffset ); + FlipTraverseTrack( & trvTrk1 ); + TraverseTrack2( &trvTrk1, item->dim.truckCenter/2.0 - item->dim.truckCenterOffset ); + + // Get the angle to translate from the truck + ANGLE_T angle[2]; + if ( trvTrk0.trk == NULL || (item->options&CAR_DESC_COUPLER_MODE_BODY)!=0 ) { + // Body mount couplers + // Angle is same as the car + angle[0] = FindAngle( trvTrk1.pos, trvTrk0.pos ); + angle[1] = NormalizeAngle( angle[0]+180.0 ); + } else { + // Truck mounted couplers + // Angle is same as the trucks + angle[0] = trvTrk0.angle; + angle[1] = trvTrk1.angle; + } - if ( dir ) - FlipTraverseTrack( &trvTrk ); + // Get the distance to translate + DIST_T d[2]; + d[0] = item->dim.carLength/2.0 - couplerLength - ( item->dim.truckCenter/2.0 + item->dim.truckCenterOffset ); + d[1] = item->dim.carLength/2.0 - couplerLength - ( item->dim.truckCenter/2.0 - item->dim.truckCenterOffset ); + + // And translate + Translate( &pos[0], trvTrk0.pos, angle[0], d[0] ); + Translate( &pos[1], trvTrk1.pos, angle[1], d[1] ); + +#ifdef LATER if ( trvTrk.trk == NULL || (item->options&CAR_DESC_COUPLER_MODE_BODY)!=0 ) { - couplerOffset = (item->dim.carLength-(item->dim.coupledLength-item->dim.carLength))/2.0; + couplerOffset = item->dim.coupledLength/2.0; Translate( &pos, trvTrk.pos, trvTrk.angle, couplerOffset ); } else { - TraverseTrack2( &trvTrk, item->dim.truckCenter/2.0 ); + if (dir) + TraverseTrack2( &trvTrk, item->dim.truckCenter/2.0-item->dim.truckCenterOffset ); + else + TraverseTrack2( &trvTrk, item->dim.truckCenter/2.0+item->dim.truckCenterOffset ); /*Translate( &pos1, trvTrk.pos, trvTrk.angle, item->dim.truckCenter/2.0 );*/ - couplerOffset = item->dim.carLength - (item->dim.truckCenter+item->dim.coupledLength)/2.0; + couplerOffset = (item->dim.coupledLength-item->dim.truckCenter)/2.0; + if (dir) + couplerOffset = couplerOffset + item->dim.truckCenterOffset; + else + couplerOffset = couplerOffset - item->dim.truckCenterOffset; Translate( &pos, trvTrk.pos, trvTrk.angle, couplerOffset ); } - return pos; +#endif } @@ -1710,6 +1877,11 @@ static DIST_T CarItemTruckCenter( return item->dim.truckCenter; } +static DIST_T CarItemTruckOffset( + carItem_p item ) { + return item->dim.truckCenterOffset; +} + EXPORT DIST_T CarItemCoupledLength( carItem_p item ) @@ -1759,6 +1931,22 @@ static DIST_T CarItemCouplerLength( return item->dim.coupledLength-item->dim.carLength; } +EXPORT BOOL_T StoreCarItem (carItem_p item, void **data,long *len) { + + *data = item; + *len = sizeof (carItem_t); + return TRUE; + +} + +EXPORT BOOL_T ReplayCarItem(carItem_p item, void *data,long len) { + + + item->pos = ((carItem_t *)data)->pos; + item->angle = ((carItem_t *)data)->angle; + return TRUE; + +} EXPORT void CarItemPlace( carItem_p item, @@ -1766,18 +1954,37 @@ EXPORT void CarItemPlace( DIST_T * dists ) { DIST_T dist; + DIST_T offset; traverseTrack_t trks[2]; dist = CarItemTruckCenter(item)/2.0; + offset = CarItemTruckOffset(item); //Offset is the amount the truck centers are displaced trks[0] = trks[1] = *trvTrk; - TraverseTrack2( &trks[0], dist ); - TraverseTrack2( &trks[1], -dist ); + TraverseTrack2( &trks[0], dist+offset ); + TraverseTrack2( &trks[1], -dist+offset ); + item->angle = FindAngle( trks[1].pos, trks[0].pos ); 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 ); + Translate(&item->pos,item->pos,item->angle, -offset); // Put truck center back along line by offset dists[0] = dists[1] = CarItemCoupledLength(item)/2.0; } +static dynArr_t clearance; + +static void ClearClearancePoints(void) { + //DYNARR_RESET(trkSeg_t,clearance); +} + +static void CreateClearancePoint(coOrd pos, int position) { + //DYNARR_APPEND(trkSeg_t,clearance,1); + +} + +static void DrawClearancePoints(void) { + //for (int i=0;i<clearance.cnt;i++) { + //DrawSegs(); + //} +} static int drawCarTrucks = 0; @@ -1787,14 +1994,16 @@ EXPORT void CarItemDraw( wDrawColor color, int direction, BOOL_T locoIsMaster, - vector_t *coupler ) + vector_t *coupler, + BOOL_T pencils, + track_p traverse) { coOrd size, pos, pos2; DIST_T length; wFont_p fp; wDrawWidth width; trkSeg_t simpleSegs[1]; - coOrd simplePts[4]; + pts_t simplePts[4]; int dir; DIST_T rad; static int couplerLineWidth = 3; @@ -1802,10 +2011,14 @@ EXPORT void CarItemDraw( 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; + simplePts[0].pt.x = simplePts[3].pt.x = -size.x/2.0; + simplePts[1].pt.x = simplePts[2].pt.x = size.x/2.0; + simplePts[0].pt.y = simplePts[1].pt.y = -size.y/2.0; + simplePts[2].pt.y = simplePts[3].pt.y = size.y/2.0; + simplePts[0].pt_type = 0; + simplePts[1].pt_type = 0; + simplePts[2].pt_type = 0; + simplePts[3].pt_type = 0; simpleSegs[0].type = SEG_FILPOLY; simpleSegs[0].color = item->color; simpleSegs[0].width = 0; @@ -1822,11 +2035,47 @@ EXPORT void CarItemDraw( DrawSegs( d, pos, item->angle-90.0, item->segPtr, item->segCnt, 0.0, color ); } + if (pencils) { + ClearClearancePoints(); + coOrd posm1,posm2; + Translate( &posm1, item->pos, item->angle-90, -size.y/2.0 ); + Translate( &posm2, item->pos, item->angle+90, -size.y/2.0 ); + coOrd posm1a = posm1; + coOrd posm2a = posm2; + if (GetTrkDistance(traverse, &posm1a)>GetTrkDistance(traverse, &posm2a)) + CreateClearancePoint(posm1,1); + else + CreateClearancePoint(posm2,2); + + coOrd pose1,pose2; + Translate( &pose1, item->pos, item->angle, size.x/2.0 ); + Translate( &pose1, pose1, item->angle-90, -size.y/2.0 ); + Translate( &pose2, pose1, item->angle+90, -size.y ); + + traverseTrack_t traverseTrk; + traverseTrk.trk = traverse; + traverseTrk.pos = item->pos; + traverseTrk.angle = item->angle; + TraverseTrack2(&traverseTrk,size.x/2.0); + coOrd pose1a = pose1; + coOrd pose2a = pose2; + if (GetTrkDistance(traverseTrk.trk, &pose1a)>GetTrkDistance(traverseTrk.trk, &pose2a)) + CreateClearancePoint(pose1,3); + else + CreateClearancePoint(pose2,4); + + + DrawClearancePoints(); + + } + if ( drawCarTrucks ) { + length = item->dim.truckCenter/2.0; - Translate( &pos, item->pos, item->angle, length ); + double offset = CarItemTruckOffset(item); + Translate( &pos, item->pos, item->angle, length+(direction?offset:-offset) ); DrawArc( d, pos, trackGauge/2.0, 0.0, 360.0, FALSE, 0, color ); - Translate( &pos, item->pos, item->angle+180, length ); + Translate( &pos, item->pos, item->angle+180, length+(direction?-offset:offset) ); DrawArc( d, pos, trackGauge/2.0, 0.0, 360.0, FALSE, 0, color ); } @@ -1925,7 +2174,7 @@ static char *dispmodeLabels[] = { N_("Information"), N_("Customize"), NULL }; static drawCmd_t carDlgD = { NULL, &screenDrawFuncs, - DC_NOCLIP, + 0, 1.0, 0.0, { 0, 0 }, { 0, 0 }, @@ -1984,16 +2233,18 @@ static paramData_t carDlgPLs[] = { { 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) +#define I_CD_TRKOFFSET (B+7) + { PD_FLOAT, &carDlgDim.truckCenterOffset, "trkCenterOffset", PDO_DIM|PDO_NOPREF|PDO_DLGHORZ|PDO_DLGWIDE, &r9999_9999, N_("Center Offset") }, +#define I_CD_CPLRMNT (B+8) + { PD_RADIO, &carDlgCouplerMount, "cplrMount", PDO_NOPREF, cplrModeLabels, N_("Coupler Mount"), BC_HORZ|BC_NOBORDER }, +#define I_CD_CPLDLEN (B+9) { 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) +#define I_CD_CPLRLEN (B+10) + { PD_FLOAT, &carDlgCouplerLength, "cplrLen", PDO_DIM|PDO_NOPREF|PDO_DLGHORZ, &r0_99999, N_("Coupler Length") }, +#define I_CD_CANVAS (B+11) { PD_DRAW, NULL, "canvas", PDO_NOPSHUPD|PDO_DLGWIDE|PDO_DLGNOLABELALIGN|PDO_DLGRESETMARGIN|PDO_DLGBOXEND|PDO_DLGRESIZE, &carDlgDrawData, NULL, 0 }, -#define C (B+11) +#define C (B+12) #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) @@ -2276,6 +2527,7 @@ static void CarDlgLoadDimsFromProto( carProto_p protoP ) carDlgDim.carLength = protoP->dim.carLength/ratio; carDlgDim.carWidth = protoP->dim.carWidth/ratio; carDlgDim.truckCenter = protoP->dim.truckCenter/ratio; + carDlgDim.truckCenterOffset = protoP->dim.truckCenterOffset/ratio; carDlgDim.coupledLength = carDlgDim.carLength + carDlgCouplerLength*2; /*carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0;*/ carDlgIsLoco = (protoP->options&CAR_DESC_IS_LOCO)?1:0; @@ -2354,9 +2606,9 @@ static void CarDlgRedraw( void ) pos.y = orig.y+carDlgDim.carWidth/2.0; if ( carDlgDim.truckCenter > 0.0 ) { - pos.x = orig.x+(carDlgDim.carLength-carDlgDim.truckCenter)/2.0; + pos.x = orig.x+(carDlgDim.carLength-carDlgDim.truckCenter)/2.0-carDlgDim.truckCenterOffset; CarProtoDrawTruck( &carDlgD, trackGauge*curScaleRatio, ratio, pos, 0.0 ); - pos.x = orig.x+(carDlgDim.carLength+carDlgDim.truckCenter)/2.0; + pos.x = orig.x+(carDlgDim.carLength+carDlgDim.truckCenter)/2.0-carDlgDim.truckCenterOffset; CarProtoDrawTruck( &carDlgD, trackGauge*curScaleRatio, ratio, pos, 0.0 ); } if ( carDlgDim.coupledLength > carDlgDim.carLength ) { @@ -2756,6 +3008,7 @@ static BOOL_T CarDlgLoadLists( protoTmp.dim.carLength = carDlgDim.carLength*ratio; protoTmp.dim.coupledLength = carDlgDim.coupledLength*ratio; protoTmp.dim.truckCenter = carDlgDim.truckCenter*ratio; + protoTmp.dim.truckCenterOffset = carDlgDim.truckCenterOffset*ratio; CarProtoDlgCreateDummyOutline( &carProtoSegCnt, &carProtoSegPtr, (BOOL_T)carDlgIsLoco, protoTmp.dim.carLength, protoTmp.dim.carWidth, drawColorBlue ); protoTmp.segCnt = carProtoSegCnt; protoTmp.segPtr = carProtoSegPtr; @@ -2798,6 +3051,7 @@ static void CarDlgShowControls( void ) 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_TRKOFFSET, !( 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 ) ); @@ -2875,7 +3129,7 @@ static void CarDlgDoActions( 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 + reload[I_CD_TRKCENTER] = reload[I_CD_TRKOFFSET] = reload[I_CD_CPLRLEN] = TRUE #define RELOAD_PARTDATA \ RELOAD_DIMS; \ reload[I_CD_PARTNO_STR] = reload[I_CD_DESC_STR] = \ @@ -2999,6 +3253,7 @@ LOG( log_carDlgState, 2, ( "Action = %s\n", carDlgAction_s[*actions] ) ) 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); } @@ -3012,6 +3267,7 @@ LOG( log_carDlgState, 2, ( "Action = %s\n", carDlgAction_s[*actions] ) ) carDlgCouplerLength = 16.0; carDlgDim.coupledLength = carDlgDim.carLength + 2 * carDlgCouplerLength; carDlgDim.truckCenter *= ratio; + carDlgDim.truckCenterOffset *= ratio; RELOAD_DIMS; break; case A_Redraw: @@ -3158,6 +3414,7 @@ LOG( log_carDlgState, 2, ( "Action = %s\n", carDlgAction_s[*actions] ) ) carDlgDim.coupledLength = carDlgDim.carLength+16.0*2.0; carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; carDlgDim.truckCenter = carDlgDim.carLength-59.0*2.0; + carDlgDim.truckCenterOffset = 0; carDlgIsLoco = (typeListMap[carDlgTypeInx].value&1); } else { strcpy( carDlgProtoStr , carDlgUpdateProtoPtr->desc ); @@ -3238,7 +3495,8 @@ static void CarDlgUpdate( cmp_key_t cmp_key; coOrd orig, size, size2; carPartParent_p parentP; - static DIST_T carDlgTruckOffset; + static DIST_T carDlgTruckOffsetL; + static DIST_T carDlgTruckOffsetR; static long carDlgClock; static long carDlgCarLengthClock; static long carDlgTruckCenterClock; @@ -3252,10 +3510,15 @@ 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; + if ( carDlgDim.truckCenter > 0 && carDlgDim.carLength > carDlgDim.truckCenter ) { + carDlgTruckOffsetL = (carDlgDim.carLength - carDlgDim.truckCenter)/2 - carDlgDim.truckCenterOffset; + carDlgTruckOffsetR = (carDlgDim.carLength - carDlgDim.truckCenter)/2 + carDlgDim.truckCenterOffset; + } + else { + carDlgTruckOffsetL = 0; + carDlgTruckOffsetR = 0; + } + carDlgCarLengthClock = carDlgCoupledLengthClock = carDlgTruckCenterClock = carDlgCouplerLengthClock = carDlgClock = 0; redraw = TRUE; break; @@ -3448,16 +3711,36 @@ LOG( log_carDlgState, 3, ( "CarDlgUpdate( %d )\n", inx ) ) redraw = TRUE; break; + case I_CD_TRKOFFSET: + carDlgChanged++; + if ( carDlgDim.truckCenterOffset == 0 ) { + carDlgTruckOffsetL = carDlgDim.truckCenter/2; + carDlgTruckOffsetR = carDlgTruckOffsetL; + } else if (carDlgDim.carLength - carDlgDim.truckCenter > 2*fabs(carDlgDim.truckCenterOffset)) { + carDlgTruckOffsetL = carDlgDim.truckCenter/2 - carDlgDim.truckCenterOffset; + carDlgTruckOffsetR = carDlgDim.truckCenter/2 + carDlgDim.truckCenterOffset; + } else { + carDlgTruckOffsetL = 0; + carDlgTruckOffsetR = 0; + } + redraw = TRUE; + break; + case I_CD_TRKCENTER: carDlgChanged++; if ( carDlgDim.truckCenter == 0 ) { - carDlgTruckOffset = 0; + carDlgTruckOffsetL = 0; + carDlgTruckOffsetR = 0; } else if ( carDlgDim.truckCenter < 100/ratio /*&& carDlgDim.carLength == 0.0*/ ) { + carDlgTruckOffsetL = 0; + carDlgTruckOffsetR = 0; return; - } else if ( carDlgDim.carLength > carDlgDim.truckCenter ) { - carDlgTruckOffset = carDlgDim.carLength - carDlgDim.truckCenter; + } else if ( carDlgDim.carLength - carDlgDim.truckCenter > 2*fabs(carDlgDim.truckCenterOffset) ) { + carDlgTruckOffsetL = carDlgDim.truckCenter/2-carDlgDim.truckCenterOffset; + carDlgTruckOffsetR = carDlgDim.truckCenter/2+carDlgDim.truckCenterOffset; } else { - carDlgTruckOffset = 0; + carDlgTruckOffsetL = 0; + carDlgTruckOffsetR = 0; } redraw = TRUE; break; @@ -3551,16 +3834,19 @@ LOG( log_carDlgState, 3, ( "CarDlgUpdate( %d )\n", inx ) ) carDlgDim.coupledLength = carDlgDim.carLength + 32; if ( carDlgDim.carLength > 120 ) { carDlgDim.truckCenter = carDlgDim.carLength - 120; - carDlgTruckOffset = carDlgDim.carLength - carDlgDim.truckCenter; + carDlgTruckOffsetL = (carDlgDim.carLength - carDlgDim.truckCenter)/2; + carDlgTruckOffsetR = (carDlgDim.carLength - carDlgDim.truckCenter)/2; } else { carDlgDim.truckCenter = 0; - carDlgTruckOffset = 0; + carDlgTruckOffsetL = 0; + carDlgTruckOffsetR = 0; } carDlgFlipToggle = FALSE; ParamLoadControl( &carDlgPG, I_CD_CARLENGTH ); ParamLoadControl( &carDlgPG, I_CD_CARWIDTH ); ParamLoadControl( &carDlgPG, I_CD_CPLRLEN ); ParamLoadControl( &carDlgPG, I_CD_TRKCENTER ); + ParamLoadControl( &carDlgPG, I_CD_TRKOFFSET ); redraw = TRUE; break; @@ -3579,12 +3865,15 @@ LOG( log_carDlgState, 3, ( "CarDlgUpdate( %d )\n", inx ) ) } if ( checkTruckCenter && carDlgDim.carLength > 0 ) { - if ( carDlgTruckOffset > 0 ) { - carDlgDim.truckCenter = carDlgDim.carLength - carDlgTruckOffset; + if ( carDlgTruckOffsetL > 0 || carDlgTruckOffsetR > 0 ) { + carDlgDim.truckCenter = carDlgTruckOffsetL + carDlgTruckOffsetR; + carDlgDim.truckCenterOffset = (carDlgTruckOffsetR - carDlgTruckOffsetL)/2; } else { carDlgDim.truckCenter = carDlgDim.carLength * 0.75; + carDlgDim.truckCenterOffset = 0; } ParamLoadControl( &carDlgPG, I_CD_TRKCENTER ); + ParamLoadControl( &carDlgPG, I_CD_TRKOFFSET ); } ok = FALSE; @@ -3600,8 +3889,12 @@ LOG( log_carDlgState, 3, ( "CarDlgUpdate( %d )\n", inx ) ) 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.truckCenterOffset < 0) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Truck Center Offset must be greater than 0 or 0") ); else if ( carDlgDim.truckCenter >= carDlgDim.carLength ) ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Truck Centers must be less than Car Length") ); + else if ( 2*carDlgDim.truckCenterOffset > carDlgDim.carLength - carDlgDim.truckCenter) + ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Truck Center Offset plus 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 ) @@ -3720,6 +4013,7 @@ LOG( log_carDlgState, 3, ( "CarDlgOk()\n" ) ) if ( carDlgDim.carLength <= 0.0 || carDlgDim.carWidth <= 0.0 || carDlgDim.truckCenter <= 0.0 || + carDlgDim.truckCenterOffset < 0.0 || carDlgDim.coupledLength <= 0.0 ) { NoticeMessage( MSG_CARDESC_VALUE_ZERO, _("Ok"), NULL ); return; @@ -3951,7 +4245,6 @@ static void CarDlgLayout( 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 ) @@ -3961,8 +4254,11 @@ static void CarDlgLayout( case I_CD_DESC_STR: *yy = wControlBelow(carDlgPLs[I_CD_PARTNO_STR].control) + 3; break; + case I_CD_CPLRMNT: + *yy = wControlBelow(carDlgPLs[I_CD_TRKOFFSET].control) + 3; + break; case I_CD_CPLDLEN: - *yy = wControlBelow(carDlgPLs[I_CD_TRKCENTER].control) + 3; + *yy = wControlBelow(carDlgPLs[I_CD_CPLRMNT].control) + 3; break; case I_CD_CANVAS: *yy = wControlBelow(carDlgPLs[I_CD_CPLDLEN].control)+5; @@ -3985,7 +4281,7 @@ static void DoCarPartDlg( carDlgAction_e *actions ) int inx; if ( carDlgPG.win == NULL ) { - ParamCreateDialog( &carDlgPG, MakeWindowTitle(_("New Car Part")), _("Add"), CarDlgOk, CarDlgClose, TRUE, CarDlgLayout, F_BLOCK|PD_F_ALT_CANCELLABEL, CarDlgUpdate ); + ParamCreateDialog( &carDlgPG, MakeWindowTitle(_("New Car Part")), _("Add"), CarDlgOk, CarDlgClose, TRUE, CarDlgLayout, F_BLOCK|F_RESIZE|F_RECALLSIZE|PD_F_ALT_CANCELLABEL, CarDlgUpdate ); if ( carDlgDim.carWidth==0 ) carDlgDim.carWidth = 12.0*10.0/curScaleRatio; @@ -4021,18 +4317,6 @@ static void DoCarPartDlg( carDlgAction_e *actions ) 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 ); } @@ -4147,13 +4431,9 @@ static void CarInvDlgFind( void * junk ) 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(); - MapRedraw(); - //DrawMapBoundingBox( TRUE ); + panCenter = pos; + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere( (void*)0 ); // CarInvDlgFind } @@ -4357,7 +4637,7 @@ static void CarInvDlgSaveText( void ) { if ( carInvSaveText_fs == NULL ) carInvSaveText_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("List Cars"), - "Text|*.txt", CarInvSaveText, NULL ); + "Text (*.txt)|*.txt", CarInvSaveText, NULL ); wFilSelect( carInvSaveText_fs, GetCurrentPath(CARSPATHKEY)); } @@ -4365,7 +4645,7 @@ static void CarInvDlgSaveText( void ) static char *carCsvColumnTitles[] = { "Index", "Scale", "Manufacturer", "Type", "Partno", "Prototype", "Description", "Roadname", "Repmark", "Number", "Options", "CarLength", - "CarWidth", "CoupledLength", "TruckCenter", "Color", "PurchPrice", + "CarWidth", "CoupledLength", "TruckOffset", "TruckCenter", "Color", "PurchPrice", "CurrPrice", "Condition", "PurchDate", "ServiceDate", "Notes" }; #define M_INDEX (0) #define M_SCALE (1) @@ -4381,14 +4661,16 @@ static char *carCsvColumnTitles[] = { #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) +#define M_TRKOFFSET (14) +#define M_TRKCENTER (15) +#define M_COLOR (16) +#define M_PURCHPRICE (17) +#define M_CURRPRICE (18) +#define M_CONDITION (19) +#define M_PURCHDATE (20) +#define M_SRVDATE (21) +#define M_NOTES (22) + static int ParseCsvLine( @@ -4564,6 +4846,7 @@ static int CarInvImportCsv( dim.carWidth = TabGetFloat( &tabs[M_CARWIDTH] ); dim.coupledLength = TabGetFloat( &tabs[M_CPLDLENGTH] ); dim.truckCenter = TabGetFloat( &tabs[M_TRKCENTER] ); + dim.truckCenterOffset = TabGetFloat( &tabs[M_TRKOFFSET] ); 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 ); @@ -4578,6 +4861,7 @@ static int CarInvImportCsv( 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; + if ( dim.truckCenterOffset < 0 ) dim.truckCenterOffset = partP->dim.truckCenterOffset; } cp = TabStringCpy( title, &tabs[M_MANUF] ); *cp++ = '\t'; @@ -4642,7 +4926,7 @@ static void CarInvDlgImportCsv( void ) { if ( carInvImportCsv_fs == NULL ) carInvImportCsv_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Import Cars"), - _("Comma-Separated-Values|*.csv"), CarInvImportCsv, NULL ); + _("Comma-Separated-Values (*.csv)|*.csv"), CarInvImportCsv, NULL ); wFilSelect( carInvImportCsv_fs, GetCurrentPath(CARSPATHKEY)); } @@ -4738,6 +5022,7 @@ static int CarInvExportCsv( CsvFormatLong( f, item->options, "," ); CsvFormatFloat( f, item->dim.carLength, 3, "," ); CsvFormatFloat( f, item->dim.carWidth, 3, "," ); + CsvFormatFloat( f, item->dim.truckCenterOffset, 3, ","); CsvFormatFloat( f, item->dim.coupledLength, 3, "," ); CsvFormatFloat( f, item->dim.truckCenter, 3, "," ); CsvFormatLong( f, wDrawGetRGB(item->color), "," ); @@ -4764,7 +5049,7 @@ static void CarInvDlgExportCsv( void ) return; if ( carInvExportCsv_fs == NULL ) carInvExportCsv_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Export Cars"), - _("Comma-Separated-Values|*.csv"), CarInvExportCsv, NULL ); + _("Comma-Separated-Values (*.csv)|*.csv"), CarInvExportCsv, NULL ); wFilSelect( carInvExportCsv_fs, GetCurrentPath(CARSPATHKEY)); } @@ -5042,7 +5327,7 @@ EXPORT void InitCarDlg( void ) ParamRegister( &carInvPG ); RegisterChangeNotification( CarDlgChange ); AddParam( "CARPROTO ", CarProtoRead ); - AddParam( "CARPART ", CarPartRead ); + AddParam( "CARPART ", CarPartRead); ParamRegister( &newCarPG ); ParamCreateControls( &newCarPG, CarItemHotbarUpdate ); newCarControls[0] = newCarPLs[0].control; diff --git a/app/bin/dcmpnd.c b/app/bin/dcmpnd.c index 13f7c56..93e73ac 100644 --- a/app/bin/dcmpnd.c +++ b/app/bin/dcmpnd.c @@ -30,6 +30,7 @@ #include "i18n.h" #include "messages.h" #include "param.h" +#include "include/paramfile.h" #include "shrtpath.h" #include "track.h" #include "utility.h" @@ -290,6 +291,11 @@ static BOOL_T RefreshCompound1( 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 ); + MyFree( xx->paths); + xx->paths = (signed char*)MyMalloc( to->pathLen * sizeof *xx->paths ); + memcpy( xx->paths, to->paths, to->pathLen * sizeof *xx->paths ); + xx->pathLen = to->pathLen; + xx->pathCurr = xx->paths; if ( flip ) FlipSegs( xx->segCnt, xx->segs, zero, 90.0 ); ClrTrkBits( trk, TB_SELECTED ); @@ -594,3 +600,30 @@ EXPORT void CompoundCustMgmLoad( void ) } } } + +/***************************************************************************** + * + * Utitlies + * + */ + +wIndex_t FindListItemByContext( + wList_p listP, + void * context ) +{ + if ( listP == NULL ) + return -1; + if ( context == NULL ) + return -1; + for ( wIndex_t inx = 0; inx < wListGetCount( listP ); ++inx ) { + void * itemContext = wListGetItemContext( listP, inx ); + if ( itemContext != NULL ) { + if ( itemContext == context ) { + return inx; + } + } + } + return -1; +} + + diff --git a/app/bin/dcontmgm.c b/app/bin/dcontmgm.c index e9e929f..19abefa 100644 --- a/app/bin/dcontmgm.c +++ b/app/bin/dcontmgm.c @@ -281,8 +281,7 @@ static void ContMgmChange( long changes ) { if (changes) { if (changed) { - changed = 1; - checkPtMark = 1; + changed = checkPtMark = 1; } } if ((changes&CHANGE_PARAMS) == 0 || @@ -297,7 +296,7 @@ static void ContMgmChange( long changes ) static void DoControlMgr( void * junk ) { if (controlPG.win == NULL) { - ParamCreateDialog( &controlPG, MakeWindowTitle(_("Manage Layout Control Elements")), _("Done"), ControlDone, NULL, TRUE, NULL, F_RESIZE|F_RECALLSIZE|F_BLOCK, ControlDlgUpdate ); + ParamCreateDialog( &controlPG, MakeWindowTitle(_("Manage Layout Control Elements")), _("Done"), ControlDone, wHide, TRUE, NULL, F_RESIZE|F_RECALLSIZE|F_BLOCK, ControlDlgUpdate ); } else { wListClear( controlSelL ); } diff --git a/app/bin/dcustmgm.c b/app/bin/dcustmgm.c index 8455958..39bd085 100644 --- a/app/bin/dcustmgm.c +++ b/app/bin/dcustmgm.c @@ -41,11 +41,20 @@ #include "paths.h" #include "track.h" #include "wlib.h" +#include "include/paramfilelist.h" +#ifdef WINDOWS +#include "include/utf8convert.h" +#endif static void CustomEdit( void * action ); static void CustomDelete( void * action ); static void CustomExport( void * action ); static void CustomDone( void * action ); +static void CustomNewCar( void * action ); + +static const char * customTypes[] = { "Car Part", "Car Prototype", NULL }; +static wIndex_t selectedType; + static wPos_t customListWidths[] = { 18, 100, 30, 80, 220 }; static const char * customListTitles[] = { "", N_("Manufacturer"), N_("Scale"), N_("Part No"), N_("Description") }; @@ -53,17 +62,17 @@ static paramListData_t customListData = { 10, 400, 5, customListWidths, customLi 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_LIST, NULL, "inx", PDO_DLGRESETMARGIN|PDO_DLGRESIZE|PDO_DLGBOXEND, &customListData, NULL, BL_MANY }, +#define I_CUSTOMNEWTYPE (1) + { PD_DROPLIST, &selectedType, "newtype", PDO_DLGRESETMARGIN | PDO_LISTINDEX, (void*)150, N_("Create a new ") }, +#define I_CUSTOMNEW (2) + { PD_BUTTON, (void *)CustomNewCar, "newcar", PDO_DLGHORZ| PDO_DLGBOXEND, NULL, N_("Go") }, +#define I_CUSTOMEDIT (3) { PD_BUTTON, (void*)CustomEdit, "edit", PDO_DLGCMDBUTTON, NULL, N_("Edit") }, -#define I_CUSTOMDEL (2) +#define I_CUSTOMDEL (4) { PD_BUTTON, (void*)CustomDelete, "delete", 0, NULL, N_("Delete") }, -#define I_CUSTOMCOPYTO (3) +#define I_CUSTOMCOPYTO (5) { 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] }; @@ -84,6 +93,10 @@ static void CustomDlgUpdate( wIndex_t selcnt = wListGetSelectedCount( (wList_p)customPLs[0].control ); wIndex_t linx, lcnt; + if ( inx == I_CUSTOMNEW ) { + lcnt = wListGetCount( (wList_p)pg->paramPtr[I_CUSTOMNEWTYPE].control ); + } + if ( inx != I_CUSTOMLIST ) return; if ( selcnt == 1 ) { lcnt = wListGetCount( (wList_p)pg->paramPtr[inx].control ); @@ -129,6 +142,20 @@ static void CustomEdit( void * action ) #endif } +static void CustomNewCar( void * action ) +{ + + switch(selectedType) { + case 1: // car prototype + CarDlgAddProto(); + break; + case 0: // car part + CarDlgAddDesc(); + break; + default: + break; + } +} static void CustomDelete( void * action ) { @@ -212,8 +239,17 @@ static int CustomDoExport( oldLocale = SaveLocale("C"); - if ( rc == -1 ) - fprintf( customMgmF, "CONTENTS %s\n", custMgmContentsStr ); + if (rc == -1) + { + #ifdef WINDOWS + char *contents = MyStrdup(custMgmContentsStr); + contents = Convert2UTF8(contents); + fprintf(customMgmF, "CONTENTS %s\n", contents); + MyFree(contents); + #else + fprintf(customMgmF, "CONTENTS %s\n", custMgmContentsStr); + #endif // WINDOWS + } cnt = wListGetCount( (wList_p)customPLs[0].control ); for ( inx=0; inx<cnt; inx++ ) { @@ -245,7 +281,7 @@ static void CustomExport( void * junk ) { if ( customMgmExport_fs == NULL ) customMgmExport_fs = wFilSelCreate( mainW, FS_UPDATE, 0, _("Move To XTP"), - _("Parameter File|*.xtp"), CustomDoExport, NULL ); + _("Parameter File (*.xtp)|*.xtp"), CustomDoExport, NULL ); wFilSelect( customMgmExport_fs, GetCurrentPath(CUSTOMPATHKEY)); } @@ -340,8 +376,7 @@ static void CustMgmChange( long changes ) { if (changes) { if (changed) { - changed = 1; - checkPtMark = 1; + changed = checkPtMark = 1; } } if ((changes&CHANGE_PARAMS) == 0 || @@ -354,8 +389,16 @@ static void CustMgmChange( long changes ) static void DoCustomMgr( void * junk ) { + int i = 0; + if (customPG.win == NULL) { - ParamCreateDialog( &customPG, MakeWindowTitle(_("Manage custom designed parts")), _("Done"), CustomDone, NULL, TRUE, NULL, F_RESIZE|F_RECALLSIZE|F_BLOCK, CustomDlgUpdate ); + ParamCreateDialog( &customPG, MakeWindowTitle(_("Manage custom designed parts")), _("Done"), CustomDone, wHide, TRUE, NULL, F_RESIZE|F_RECALLSIZE|F_BLOCK, CustomDlgUpdate ); + while(customTypes[ i ] != NULL) { + wListAddValue( ((wList_p)customPLs[I_CUSTOMNEWTYPE].control), customTypes[ i++ ], NULL, NULL ); + } + selectedType = 0; + wListSetIndex( ((wList_p)customPLs[I_CUSTOMNEWTYPE].control), selectedType); + } else { wListClear( customSelL ); } @@ -369,7 +412,7 @@ static void DoCustomMgr( void * junk ) EXPORT addButtonCallBack_t CustomMgrInit( void ) { - ParamRegister( &customPG ); + ParamRegister( &customPG ); ParamRegister( &custMgmContentsPG ); RegisterChangeNotification( CustMgmChange ); return &DoCustomMgr; diff --git a/app/bin/dease.c b/app/bin/dease.c index 7841857..d01f0df 100644 --- a/app/bin/dease.c +++ b/app/bin/dease.c @@ -21,12 +21,14 @@ */ #include <math.h> +#include <string.h> #include "ccurve.h" #include "cjoin.h" #include "cstraigh.h" #include "custom.h" #include "i18n.h" +#include "fileio.h" #include "param.h" #include "track.h" diff --git a/app/bin/directory.c b/app/bin/directory.c new file mode 100644 index 0000000..265485b --- /dev/null +++ b/app/bin/directory.c @@ -0,0 +1,162 @@ +/** \file directory.c + * Directory Management + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2018 Adam Richards and 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 <errno.h> +#include <string.h> + +#ifdef WINDOWS + #include "include/dirent.h" + #include <direct.h> + #define unlink(a) _unlink((a)) + #define rmdir(a) _rmdir((a)) +#else + #include <dirent.h> + #include <unistd.h> + #include <sys/stat.h> + #include <sys/types.h> +#endif + +#include <wlib.h> +#include "directory.h" +#include "dynstring.h" +#include "i18n.h" +#include "messages.h" +#include "misc.h" + +/***************************************************************************** + * Safe Create Dir + * \param IN dir The directory path to create + * + * \return TRUE if ok + * + */ + +BOOL_T SafeCreateDir(const char *dir) +{ + int err; + +#ifdef WINDOWS + err = _mkdir(dir); +#else + err = mkdir(dir, 0755); +#endif + if (err < 0) { + if (errno != EEXIST) { + NoticeMessage(MSG_DIR_CREATE_FAIL, + _("Continue"), NULL, dir, strerror(errno)); + perror(dir); + return FALSE; + } + } + return TRUE; +} + +/************************************************ + * DeleteDirectory empties and removes a directory recursively + * + * \param IN dir_path The Directory to empty and remove + * + * \return TRUE if ok + * + */ +BOOL_T DeleteDirectory(const char *dir_path) +{ + size_t path_len; + char *full_path = NULL; + DIR *dir; + struct stat stat_path, stat_entry; + struct dirent *entry; + DynString path; + + // stat for the path + int resp = stat(dir_path, &stat_path); + + if (resp != 0 && errno == ENOENT) { + return TRUE; //Does not Exist + } + + // if path is not dir - exit + if (!(S_ISDIR(stat_path.st_mode))) { + NoticeMessage(MSG_NOT_DIR_FAIL, + _("Continue"), NULL, dir_path); + return FALSE; + } + + // if not possible to read the directory for this user + if ((dir = opendir(dir_path)) == NULL) { + NoticeMessage(MSG_DIR_OPEN_FAIL, + _("Continue"), NULL, dir_path); + return FALSE; + } + + // the length of the path + path_len = strlen(dir_path) + 1; + DynStringMalloc(&path, path_len + 16); //guessing the total path length + + // iteration through entries in the directory + while ((entry = readdir(dir)) != NULL) { + + // skip entries "." and ".." + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { + continue; + } + + // determinate a full path of an entry + DynStringReset(&path); + DynStringCatCStrs(&path, dir_path, FILE_SEP_CHAR, entry->d_name, NULL); + full_path = DynStringToCStr(&path); + // stat for the entry + stat(full_path, &stat_entry); + + // recursively remove a nested directory + if (S_ISDIR(stat_entry.st_mode) != 0) { + DeleteDirectory(full_path); + continue; + } + + // remove a file object + if (unlink(full_path)) { + NoticeMessage(MSG_UNLINK_FAIL, _("Continue"), NULL, full_path); + DynStringFree(&path); + closedir(dir); + return FALSE; + } else { +#if DEBUG + printf("Removed a file: %s \n", full_path); +#endif + } + } + + closedir(dir); + DynStringFree(&path); + + // remove the devastated directory and close the object of it + if (rmdir(dir_path)) { + NoticeMessage(MSG_RMDIR_FAIL, _("Continue"), NULL, dir_path); + return FALSE; + } else { +#if DEBUG + printf("Removed a directory: %s \n", dir_path); +#endif + } + return TRUE; +} diff --git a/app/bin/directory.h b/app/bin/directory.h new file mode 100644 index 0000000..2ddfffd --- /dev/null +++ b/app/bin/directory.h @@ -0,0 +1,6 @@ +#ifndef HAVE_DIRECTORY_H
+ #define HAVE_DIRECTORY_H
+ #include "common.h"
+ BOOL_T SafeCreateDir(const char *dir);
+ BOOL_T DeleteDirectory(const char *dir_path);
+#endif
\ No newline at end of file diff --git a/app/bin/dlayer.c b/app/bin/dlayer.c index 70f613f..352dbe1 100644 --- a/app/bin/dlayer.c +++ b/app/bin/dlayer.c @@ -29,6 +29,7 @@ #include "dynstring.h" #include "fileio.h" #include "i18n.h" +#include "layout.h" #include "messages.h" #include "param.h" #include "track.h" @@ -43,6 +44,7 @@ #define LAYERPREF_FROZEN (1) #define LAYERPREF_ONMAP (2) #define LAYERPREF_VISIBLE (4) +#define LAYERPREF_MODULE (8) #define LAYERPREF_SECTION ("Layers") #define LAYERPREF_NAME "name" #define LAYERPREF_COLOR "color" @@ -66,9 +68,11 @@ static wList_p setLayerL; typedef struct { char name[STR_SHORT_SIZE]; /**< Layer name */ wDrawColor color; /**< layer color, is an index into a color table */ + BOOL_T useColor; /**< Use Layer color */ BOOL_T frozen; /**< Frozen flag */ BOOL_T visible; /**< visible flag */ BOOL_T onMap; /**< is layer shown map */ + BOOL_T module; /**< is layer a module (all or nothing) */ long objCount; /**< number of objects on layer */ } layer_t; @@ -176,6 +180,22 @@ BOOL_T GetLayerOnMap(unsigned int layer) } } +BOOL_T GetLayerModule(unsigned int layer) +{ + if (!IsLayerValid(layer)) { + return TRUE; + } else { + return layers[layer].module; + } +} + +void SetLayerModule(unsigned int layer, BOOL_T module) +{ + if (IsLayerValid(layer)) { + layers[layer].module = module; + } +} + char * GetLayerName(unsigned int layer) { @@ -186,11 +206,26 @@ char * GetLayerName(unsigned int layer) } } +void SetLayerName(unsigned int layer, char* name) { + if (IsLayerValid(layer)) { + strcpy(layers[layer].name,name); + } +} + +BOOL_T GetLayerUseColor(unsigned int layer) { + return layers[layer].useColor; +} + wDrawColor GetLayerColor(unsigned int layer) { return layers[layer].color; } +static void RedrawLayer( unsigned int l, BOOL_T draw ) +{ + DoRedraw(); // RedrawLayer +} + static void FlipLayer(unsigned int layer) { @@ -218,7 +253,7 @@ static void FlipLayer(unsigned int layer) RedrawLayer(layer, TRUE); } -static void SetCurrLayer(wIndex_t inx, const char * name, wIndex_t op, +void SetCurrLayer(wIndex_t inx, const char * name, wIndex_t op, void * listContext, void * arg) { unsigned int newLayer = (unsigned int)inx; @@ -422,9 +457,11 @@ static wDrawColor layerColorTab[COUNT(layerRawColorTab)]; static wWin_p layerW; static char layerName[STR_SHORT_SIZE]; static wDrawColor layerColor; +static long layerUseColor = TRUE; static long layerVisible = TRUE; static long layerFrozen = FALSE; static long layerOnMap = TRUE; +static long layerModule = FALSE; static void LayerOk(void *); static BOOL_T layerRedrawMap = FALSE; @@ -435,6 +472,8 @@ static BOOL_T layerRedrawMap = FALSE; static char *visibleLabels[] = { "", NULL }; static char *frozenLabels[] = { "", NULL }; static char *onMapLabels[] = { "", NULL }; +static char *moduleLabels[] = { "", NULL }; +static char *layerColorLabels[] = { "", NULL }; static paramIntegerRange_t i0_20 = { 0, NUM_BUTTONS }; static paramData_t layerPLs[] = { @@ -444,13 +483,17 @@ static paramData_t layerPLs[] = { { PD_STRING, layerName, "name", PDO_NOPREF|PDO_STRINGLIMITLENGTH, (void*)(250-54), N_("Name"), 0, 0, sizeof(layerName) }, #define I_COLOR (2) { PD_COLORLIST, &layerColor, "color", PDO_NOPREF, NULL, N_("Color") }, -#define I_VIS (3) +#define I_USE_COLOR (3) + { PD_TOGGLE, &layerUseColor, "layercolor", PDO_NOPREF|PDO_DLGHORZ, layerColorLabels, N_("Use Color"), BC_HORZ|BC_NOBORDER }, +#define I_VIS (4) { PD_TOGGLE, &layerVisible, "visible", PDO_NOPREF, visibleLabels, N_("Visible"), BC_HORZ|BC_NOBORDER }, -#define I_FRZ (4) +#define I_FRZ (5) { PD_TOGGLE, &layerFrozen, "frozen", PDO_NOPREF|PDO_DLGHORZ, frozenLabels, N_("Frozen"), BC_HORZ|BC_NOBORDER }, -#define I_MAP (5) +#define I_MAP (6) { PD_TOGGLE, &layerOnMap, "onmap", PDO_NOPREF|PDO_DLGHORZ, onMapLabels, N_("On Map"), BC_HORZ|BC_NOBORDER }, -#define I_COUNT (6) +#define I_MOD (7) + { PD_TOGGLE, &layerModule, "module", PDO_NOPREF|PDO_DLGHORZ, moduleLabels, N_("Module"), BC_HORZ|BC_NOBORDER }, +#define I_COUNT (8) { 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 }, @@ -477,6 +520,7 @@ LayerSystemDefaults(void) layers[inx].visible = TRUE; layers[inx].frozen = FALSE; layers[inx].onMap = TRUE; + layers[inx].module = FALSE; layers[inx].objCount = 0; SetLayerColor(inx, layerColorTab[inx%COUNT(layerColorTab)]); } @@ -547,7 +591,7 @@ static void DoLayerOp(void * data) if (layoutLayerChanged) { MainProc(mainW, wResize_e, NULL, NULL); layoutLayerChanged = FALSE; - changed = TRUE; + changed++; SetWindowTitle(); } } @@ -566,7 +610,9 @@ UpdateLayerDlg() layerVisible = layers[curLayer].visible; layerFrozen = layers[curLayer].frozen; layerOnMap = layers[curLayer].onMap; + layerModule = layers[curLayer].module; layerColor = layers[curLayer].color; + layerUseColor = layers[curLayer].useColor; strcpy(layerName, layers[curLayer].name); layerCurrent = curLayer; /* now re-load the layer list boxes */ @@ -588,6 +634,28 @@ UpdateLayerDlg() } /** + * Fill a layer dropbox with the current layer settings + * + * \param listLayers the dropbox + * \return + */ + +void +FillLayerList( wList_p listLayers) +{ + wListClear(listLayers); // Rebuild list on each invovation + + for (int inx = 0; inx < NUM_LAYERS; inx++) { + char *layerFormattedName; + layerFormattedName = FormatLayerName(inx); + wListAddValue((wList_p)listLayers, layerFormattedName, NULL, (void*)(long)inx); + free(layerFormattedName); + } + + /* set current layer to selected */ + wListSetIndex(listLayers, curLayer); +} +/** * Initialize the layer lists. * * \param IN pointer to function that actually initialize tha data structures @@ -646,6 +714,10 @@ LayerPrefSave(void) flags |= LAYERPREF_VISIBLE; } + if (layers[inx].module) { + flags |= LAYERPREF_MODULE; + } + sprintf(buffer, LAYERPREF_FLAGS ".%0u", inx); wPrefSetInteger(LAYERPREF_SECTION, buffer, flags); @@ -710,6 +782,7 @@ LayerPrefLoad(void) layers[inx].frozen = ((flags & LAYERPREF_FROZEN) != 0); layers[inx].onMap = ((flags & LAYERPREF_ONMAP) != 0); layers[inx].visible = ((flags & LAYERPREF_VISIBLE) != 0); + layers[inx].module = ((flags & LAYERPREF_MODULE) !=0); prefString = strtok(NULL, ","); } } @@ -761,6 +834,15 @@ void LayerSetCounts(void) } } +int FindUnusedLayer(unsigned int start) { + int inx; + for (inx=start; inx<NUM_LAYERS; inx++) { + if (layers[inx].objCount == 0 && !layers[inx].frozen) return inx; + } + ErrorMessage( MSG_NO_EMPTY_LAYER ); + return -1; +} + /** * Reset layer options to their default values. The default values are loaded * from the preferences file. @@ -805,12 +887,20 @@ static void LayerUpdate(void) ParamLoadControl(&layerPG, I_VIS); } + if (layerCurrent == curLayer && layerModule) { + NoticeMessage(MSG_LAYER_MODULE, _("Ok"), NULL); + layerModule = FALSE; + ParamLoadControl(&layerPG, I_MOD); + } + if (strcmp(layers[(int)layerCurrent].name, layerName) || layerColor != layers[(int)layerCurrent].color || + layers[(int)layerCurrent].useColor != (BOOL_T)layerUseColor || layers[(int)layerCurrent].visible != (BOOL_T)layerVisible || layers[(int)layerCurrent].frozen != (BOOL_T)layerFrozen || - layers[(int)layerCurrent].onMap != (BOOL_T)layerOnMap) { - changed = TRUE; + layers[(int)layerCurrent].onMap != (BOOL_T)layerOnMap || + layers[(int)layerCurrent].module != (BOOL_T)layerModule) { + changed++; SetWindowTitle(); } @@ -837,6 +927,7 @@ static void LayerUpdate(void) } redraw = (layerColor != layers[(int)layerCurrent].color || + layers[(int)layerCurrent].useColor != (BOOL_T)layerUseColor || (BOOL_T)layerVisible != layers[(int)layerCurrent].visible); if ((!layerRedrawMap) && redraw) { @@ -850,9 +941,11 @@ static void LayerUpdate(void) wButtonSetBusy(layer_btns[(int)layerCurrent], layerVisible); } + layers[(int)layerCurrent].useColor = (BOOL_T)layerUseColor; layers[(int)layerCurrent].visible = (BOOL_T)layerVisible; layers[(int)layerCurrent].frozen = (BOOL_T)layerFrozen; layers[(int)layerCurrent].onMap = (BOOL_T)layerOnMap; + layers[(int)layerCurrent].module = (BOOL_T)layerModule; if (layerRedrawMap) { DoRedraw(); @@ -878,7 +971,9 @@ static void LayerSelect( layerVisible = layers[inx].visible; layerFrozen = layers[inx].frozen; layerOnMap = layers[inx].onMap; + layerModule = layers[inx].module; layerColor = layers[inx].color; + layerUseColor = layers[inx].useColor; sprintf(message, "%ld", layers[inx].objCount); ParamLoadMessage(&layerPG, I_COUNT, message); ParamLoadControls(&layerPG); @@ -893,6 +988,7 @@ void ResetLayers(void) layers[inx].visible = TRUE; layers[inx].frozen = FALSE; layers[inx].onMap = TRUE; + layers[inx].module = FALSE; layers[inx].objCount = 0; SetLayerColor(inx, layerColorTab[inx%COUNT(layerColorTab)]); @@ -911,7 +1007,9 @@ void ResetLayers(void) layerVisible = TRUE; layerFrozen = FALSE; layerOnMap = TRUE; + layerModule = FALSE; layerColor = layers[0].color; + layerUseColor = TRUE; strcpy(layerName, layers[0].name); LoadLayerLists(); @@ -1024,7 +1122,7 @@ static void DoLayer(void * junk) { if (layerW == NULL) { layerW = ParamCreateDialog(&layerPG, MakeWindowTitle(_("Layers")), _("Done"), - LayerOk, NULL, TRUE, NULL, 0, LayerDlgUpdate); + LayerOk, wHide, TRUE, NULL, 0, LayerDlgUpdate); } /* set the globals to the values for the current layer */ @@ -1038,7 +1136,7 @@ static void DoLayer(void * junk) BOOL_T ReadLayers(char * line) { char * name; - int inx, visible, frozen, color, onMap; + int inx, visible, frozen, color, onMap, module, dontUseColor, ColorFlags; unsigned long rgb; /* older files didn't support layers */ @@ -1069,7 +1167,7 @@ BOOL_T ReadLayers(char * line) /* get the properties for a layer from the file and update the layer accordingly */ - if (!GetArgs(line, "ddddu0000q", &inx, &visible, &frozen, &onMap, &rgb, + if (!GetArgs(line, "dddduddd0q", &inx, &visible, &frozen, &onMap, &rgb, &module, &dontUseColor, &ColorFlags, &name)) { return FALSE; } @@ -1093,7 +1191,12 @@ BOOL_T ReadLayers(char * line) layers[inx].visible = visible; layers[inx].frozen = frozen; layers[inx].onMap = onMap; + layers[inx].module = module; layers[inx].color = color; + layers[inx].useColor = !dontUseColor; + + colorTrack = ColorFlags&1; //Make sure globals are set + colorDraw = ColorFlags&2; if (inx<NUM_BUTTONS) { if (strlen(name) > 0) { @@ -1117,12 +1220,13 @@ BOOL_T ReadLayers(char * line) * \return TRUE if configured, FALSE if not */ -bool +BOOL_T IsLayerConfigured(unsigned int layerNumber) { return (!layers[layerNumber].visible || layers[layerNumber].frozen || !layers[layerNumber].onMap || + layers[layerNumber].module || layers[layerNumber].color != layerColorTab[layerNumber % (COUNT(layerColorTab))] || layers[layerNumber].name[0] || @@ -1140,6 +1244,11 @@ BOOL_T WriteLayers(FILE * f) { unsigned int inx; + int ColorFlags = 0; + + if (colorTrack) ColorFlags |= 1; + if (colorDraw) ColorFlags |= 2; + for (inx = 0; inx < NUM_LAYERS; inx++) { if (IsLayerConfigured(inx)) { fprintf(f, "LAYERS %u %d %d %d %ld %d %d %d %d \"%s\"\n", @@ -1148,7 +1257,9 @@ BOOL_T WriteLayers(FILE * f) layers[inx].frozen, layers[inx].onMap, wDrawGetRGB(layers[inx].color), - 0, 0, 0, 0, + layers[inx].module, + layers[inx].useColor?0:1, + ColorFlags, 0, PutTitle(layers[inx].name)); } } @@ -1157,6 +1268,7 @@ BOOL_T WriteLayers(FILE * f) return TRUE; } +#include "bitmaps/background.xpm" void InitLayers(void) { @@ -1173,6 +1285,7 @@ void InitLayers(void) show_layer_bmps[i] = wIconCreateBitMap(l1_width, l1_height, show_layer_bits[i], layerColorTab[i%(COUNT(layerColorTab))]); layers[i].color = layerColorTab[i%(COUNT(layerColorTab))]; + layers[i].useColor = TRUE; } /* layer list for toolbar */ @@ -1180,6 +1293,10 @@ void InitLayers(void) SetCurrLayer, NULL); wControlSetBalloonText((wControl_p)setLayerL, GetBalloonHelpStr("cmdLayerSet")); AddToolbarControl((wControl_p)setLayerL, IC_MODETRAIN_TOO); + + backgroundB = AddToolbarButton("cmdBackgroundShow", wIconCreatePixMap(background), 0, + (addButtonCallBack_t)BackgroundToggleShow, NULL); + wControlActive((wControl_p)backgroundB, FALSE); for (i = 0; i<NUM_LAYERS; i++) { char *layerName; diff --git a/app/bin/doption.c b/app/bin/doption.c index ae36d21..3b9ed02 100644 --- a/app/bin/doption.c +++ b/app/bin/doption.c @@ -35,6 +35,7 @@ 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 i0_99 = { 0, 99}; static paramIntegerRange_t i1_1000 = { 1, 1000 }; static paramIntegerRange_t i10_1000 = { 10, 1000 }; static paramIntegerRange_t i10_100 = { 10, 100 }; @@ -45,11 +46,15 @@ static paramFloatRange_t r0_180 = { 0, 180 }; static void UpdatePrefD( void ); static void UpdateMeasureFmt(void); +static void UpdateAutoSaveInterval(long); +static void UpdateChkPtInterval(long); static wIndex_t distanceFormatInx; EXPORT long enableBalloonHelp = 1; +EXPORT long showFlexTrack = 1; + long GetChanges( paramGroup_p pg ) { long changes; @@ -62,21 +67,18 @@ long GetChanges( paramGroup_p pg ) return changes; } +static paramGroup_t prefPG; + + 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(); @@ -84,6 +86,28 @@ static void OptionDlgUpdate( if (pg->paramPtr[inx].valueP == &distanceFormatInx) { UpdateMeasureFmt(); } + if (pg->paramPtr[inx].valueP == &showFlexTrack) { + DoChangeNotification(CHANGE_PARAMS|CHANGE_TOOLBAR); + } + if (pg->paramPtr[inx].valueP == &checkPtInterval) { + checkPtInterval = *(long *)valueP; + if (checkPtInterval == 0 ) { + wControlSetBalloon( pg->paramPtr[inx].control, 0, -5, _("Turning off AutoSave") ); + UpdateAutoSaveInterval(0); + } else { + wControlSetBalloon( pg->paramPtr[inx].control, 0, -5, NULL ); + } + } + if (pg->paramPtr[inx].valueP == &autosaveChkPoints) { + autosaveChkPoints = *(long *)valueP; + if (checkPtInterval == 0 && autosaveChkPoints>0 ) { + wControlSetBalloon( pg->paramPtr[inx].control, 0, -5, _("Turning on CheckPointing") ); + UpdateChkPtInterval(10); + } else { + wControlSetBalloon( pg->paramPtr[inx].control, 0, -5, NULL ); + } + + } } } @@ -91,7 +115,6 @@ static void OptionDlgCancel( wWin_p win ) { wEnableBalloonHelp( (int)enableBalloonHelp ); - UpdateQuickMove(NULL); wHide( win ); } @@ -113,15 +136,19 @@ 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 * colorTrackLabels[] = { N_("Object"), N_("Layer"), NULL }; +static char * colorDrawLabels[] = { N_("Object"), N_("Layer"), NULL }; static char * liveMapLabels[] = { N_("Live Map"), NULL }; static char * hideTrainsInTunnelsLabels[] = { N_("Hide Trains On Hidden Track"), NULL }; -static char * zoomCornerLabels[] = {N_("Zoom keeps lower corner in view"), NULL}; +static char * constrainMainLabels[] = {N_("Constrain Drawing Area to Room boundaries"), 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, &colorTrack, "color-track", PDO_NOPSHUPD|PDO_DRAW, colorTrackLabels, N_("Color Track"), BC_HORZ, (void*)(CHANGE_MAIN) }, + { PD_RADIO, &colorDraw, "color-draw", PDO_NOPSHUPD|PDO_DRAW, colorDrawLabels, N_("Color Draw"), 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, &drawUnconnectedEndPt, "unconnected-endpt", PDO_NOPSHUPD|PDO_DRAW, drawEndPtUnconnectedSize, N_("Draw Unconnected EndPts"), BC_HORZ, (void*)(CHANGE_MAIN) }, @@ -129,7 +156,7 @@ static paramData_t displayPLs[] = { { PD_RADIO, ¢erDrawMode, "centerdraw", PDO_NOPSHUPD|PDO_DRAW, drawCenterCircle, N_("Draw Centers"), BC_HORZ, (void*)(CHANGE_MAIN | CHANGE_MAP) }, { PD_LONG, &twoRailScale, "tworailscale", PDO_NOPSHUPD, &i1_64, N_("Two Rail Scale"), 0, (void*)(CHANGE_MAIN) }, { PD_LONG, &mapScale, "mapscale", PDO_NOPSHUPD, &i1_256, N_("Map Scale"), 0, (void*)(CHANGE_MAP) }, - { PD_TOGGLE, &zoomCorner, "zoom-corner", PDO_NOPSHUPD, zoomCornerLabels, "", BC_HORZ }, + { PD_TOGGLE, &constrainMain, "constrainmain", PDO_NOPSHUPD, constrainMainLabels, "", BC_HORZ }, { 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) }, @@ -139,7 +166,7 @@ static paramData_t displayPLs[] = { { 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 (17) +#define I_HOTBARLABELS (18) { 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 } @@ -178,6 +205,7 @@ static void DoDisplay( void * junk ) 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 @@ -204,13 +232,9 @@ EXPORT addButtonCallBack_t DisplayInit( void ) static wWin_p cmdoptW; -static char * moveQlabels[] = { - N_("Normal"), - N_("Simple"), - N_("End-Points"), - NULL }; - static char * preSelectLabels[] = { N_("Properties"), N_("Select"), NULL }; +static char * selectLabels[] = { N_("Single item selected, +Ctrl Add to selection"), N_("Add to selection, +Ctrl Single item selected"), NULL }; +static char * selectZeroLabels[] = { N_("Deselect all on select nothing"), NULL }; #ifdef HIDESELECTIONWINDOW static char * hideSelectionWindowLabels[] = { N_("Hide"), NULL }; @@ -218,17 +242,16 @@ static char * hideSelectionWindowLabels[] = { N_("Hide"), NULL }; 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 } + { PD_RADIO, &rightClickMode, "rightclickmode", PDO_NOPSHUPD, rightClickLabels, N_("Right Click"), 0 }, + { PD_RADIO, &selectMode, "selectmode", PDO_NOPSHUPD, selectLabels, N_("Select Mode"), 0}, + { PD_TOGGLE, &selectZero, "selectzero", PDO_NOPSHUPD, selectZeroLabels, "", 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; @@ -275,6 +298,7 @@ static long displayUnits; 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 * enableFlexTrackLabels[] = { N_("Show FlexTrack in HotBar"), NULL }; static char * startOptions[] = { N_("Load Last Layout"), N_("Start New Layout"), NULL }; static paramData_t prefPLs[] = { @@ -288,10 +312,14 @@ static paramData_t prefPLs[] = { { 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_TOGGLE, &showFlexTrack, "showflextrack", PDO_NOPSHUPD, enableFlexTrackLabels, "", BC_HORZ}, { PD_LONG, &dragPixels, "dragpixels", PDO_NOPSHUPD|PDO_DRAW, &i1_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") }, +#define I_CHKPT (13) + { PD_LONG, &checkPtInterval, "checkpoint", PDO_NOPSHUPD|PDO_FILE, &i0_10000, N_("Check Point Frequency") }, +#define I_AUTOSAVE (14) + { PD_LONG, &autosaveChkPoints, "autosave", PDO_NOPSHUPD|PDO_FILE, &i0_99, N_("Autosave Checkpoint Frequency") }, { 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] }; @@ -302,6 +330,7 @@ typedef struct { long fmt; } dstFmts_t; static dstFmts_t englishDstFmts[] = { + { N_("999.999"), DISTFMT_FMT_NONE|DISTFMT_FRACT_NUM|3 }, { N_("999.999999"), DISTFMT_FMT_NONE|DISTFMT_FRACT_NUM|6 }, { N_("999.99999"), DISTFMT_FMT_NONE|DISTFMT_FRACT_NUM|5 }, { N_("999.9999"), DISTFMT_FMT_NONE|DISTFMT_FRACT_NUM|4 }, @@ -339,10 +368,23 @@ static dstFmts_t metricDstFmts[] = { { NULL, 0 }, { NULL, 0 }, { NULL, 0 }, - { NULL, 0 }, { NULL, 0 } }; static dstFmts_t *dstFmts[] = { englishDstFmts, metricDstFmts }; +void UpdateAutoSaveInterval(long value) +{ + autosaveChkPoints = value; + ParamLoadControl(&prefPG, I_AUTOSAVE); + ParamLoadControl(&prefPG, I_CHKPT); +} + +void UpdateChkPtInterval(long value) +{ + checkPtInterval = value; + ParamLoadControl(&prefPG, I_AUTOSAVE); + ParamLoadControl(&prefPG, I_CHKPT); +} + /** * Load the selection list for number formats with the appropriate list of variants. */ diff --git a/app/bin/dpricels.c b/app/bin/dpricels.c index 87df88b..9d04d6d 100644 --- a/app/bin/dpricels.c +++ b/app/bin/dpricels.c @@ -151,7 +151,7 @@ static void PriceListDlgUpdate( static void DoPriceList( void * junk ) { if (priceListW == NULL) - priceListW = ParamCreateDialog( &priceListPG, MakeWindowTitle(_("Price List")), _("Done"), PriceListOk, NULL, TRUE, NULL, 0, PriceListDlgUpdate ); + priceListW = ParamCreateDialog( &priceListPG, MakeWindowTitle(_("Price List")), _("Done"), PriceListOk, wHide, TRUE, NULL, F_RESIZE, PriceListDlgUpdate ); wShow( priceListW ); PriceListChange( CHANGE_SCALE|CHANGE_PARAMS ); } diff --git a/app/bin/dprmfile.c b/app/bin/dprmfile.c index 24250e7..3bb249e 100644 --- a/app/bin/dprmfile.c +++ b/app/bin/dprmfile.c @@ -1,5 +1,5 @@ /** \file dprmfile.c - * Param File Management + * Param File Dialog */ /* XTrkCad - Model Railroad CAD @@ -21,535 +21,426 @@ */ #include <assert.h> +#include <stdbool.h> #include <stdint.h> #include <string.h> -#include <time.h> #include "custom.h" +#include "dynstring.h" #include "fileio.h" #include "i18n.h" #include "messages.h" #include "param.h" +#include "include/paramfile.h" +#include "include/paramfilelist.h" #include "paths.h" #include "track.h" -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; - - MakeFullpath(&fileNameP, libDir, "xtrkcad.upd", NULL); - updateF = fopen( fileNameP, "r" ); - free(fileNameP); - 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; - - while ( ( fgets( fileName, STR_LONG_SIZE, updateF ) ) != NULL ) { - Stripcr( fileName ); - InfoMessage( _("Updating %s"), fileName ); - MakeFullpath(&fileNameP, libDir, "params", fileName, NULL); - paramF = fopen( fileNameP, "r" ); - if ( paramF == NULL ) { - NoticeMessage( MSG_PRMFIL_OPEN_NEW, _("Ok"), NULL, fileNameP ); - free(fileNameP); - 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, fileNameP ); - free(fileNameP); - continue; - } - cp = wPrefGetString( "Parameter File Map", contents ); - wPrefSetString( "Parameter File Map", contents, fileNameP ); - if (cp!=NULL && *cp!='\0') { - /* been there, done that */ - free(fileNameP); - continue; - } - - DYNARR_APPEND( paramFileInfo_t, paramFileInfo_da, 10 ); - curParamFileIndex = paramFileInfo_da.cnt-1; - paramFileInfo(curParamFileIndex).name = MyStrdup( fileNameP ); - curContents = curSubContents = NULL; - paramFileInfo(curParamFileIndex).deleted = FALSE; - paramFileInfo(curParamFileIndex).valid = TRUE; - paramFileInfo(curParamFileIndex).deletedShadow = - paramFileInfo(curParamFileIndex).deleted = !ReadParams( 0, NULL, fileNameP ); - paramFileInfo(curParamFileIndex).contents = curContents; - - free(fileNameP); - } - 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(); - } -} +#include "bitmaps/greendot.xpm" +#include "bitmaps/greydot.xpm" +#include "bitmaps/yellowdot.xpm" +#include "bitmaps/reddot.xpm" +#include "bitmaps/greenstar.xpm" +#include "bitmaps/greystar.xpm" +#include "bitmaps/yellowstar.xpm" +#include "bitmaps/redstar.xpm" +#define FAVORITE_PARAM 1 +#define STANDARD_PARAM 0 -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 ); - wPrefSetString("Parameter File Map", contents, paramFileInfo(fileInx).name); - } - } - sprintf( message, "File%d", fileNo++ ); - wPrefSetString( "Parameter File Names", message, "" ); -} +#define PARAMBUTTON_UNLOAD "Unload" +#define PARAMBUTTON_REFRESH "Reload" +#define PARAMFILE_UNLOAD (0) +#define PARAMFILE_REFRESH (1) - -/**************************************************************************** - * - * Param File Dialog - * - */ +static wIcon_p indicatorIcons[ 2 ][PARAMFILE_MAXSTATE]; static wWin_p paramFileW; static long paramFileSel = 0; -static wIcon_p mtbox_bm; -static wIcon_p chkbox_bm; -static void ParamFileAction( void * ); -static void ParamFileBrowse( void * ); -static void ParamFileSelectAll( void * ); +static void ParamFileFavorite(void * favorite); +static void ParamRefreshSelectedFiles(void * action); +static void ParamUnloadSelectedFiles(void *); +static void ParamFileBrowse(void *); +static void ParamFileSelectAll(void *); -static paramListData_t paramFileListData = { 10, 370 }; +static paramListData_t paramFileListData = { 15, 370 }; static char * paramFileLabels[] = { N_("Show File Names"), NULL }; static paramData_t paramFilePLs[] = { #define I_PRMFILLIST (0) #define paramFileL ((wList_p)paramFilePLs[I_PRMFILLIST].control) - { PD_LIST, NULL, "inx", 0, ¶mFileListData, NULL, BL_DUP|BL_SETSTAY|BL_MANY }, + { PD_LIST, NULL, "inx", PDO_NOPREF | PDO_DLGRESIZE, ¶mFileListData, NULL, BL_DUP|BL_SETSTAY|BL_MANY }, #define I_PRMFILTOGGLE (1) - { PD_TOGGLE, ¶mFileSel, "mode", 0, paramFileLabels, NULL, BC_HORZ|BC_NOBORDER }, - { PD_BUTTON, (void *)ParamFileSelectAll, "selectall", PDO_DLGCMDBUTTON, NULL, N_("Select all") }, -#define I_PRMFILACTION (3) -#define paramFileActionB ((wButton_p)paramFilePLs[I_PRMFILACTION].control) - { PD_BUTTON, (void*)ParamFileAction, "action", PDO_DLGCMDBUTTON, NULL, N_("Unload"), 0L, FALSE }, - { PD_BUTTON, (void*)ParamFileBrowse, "browse", 0, NULL, N_("Browse ...") } }; + { PD_TOGGLE, ¶mFileSel, "mode", 0, paramFileLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_MESSAGE (2) + { PD_MESSAGE, "", NULL, 0, (void *)370 }, + { PD_BUTTON, (void *)ParamFileSelectAll, "selectall", PDO_DLGCMDBUTTON, NULL, N_("Select all") }, +#define I_PRMFILEFAVORITE (4) + { PD_BUTTON, (void *)ParamFileFavorite, "favorite", PDO_DLGCMDBUTTON, (void *)TRUE, N_("Favorite")}, + { PD_BUTTON, (void*)ParamUnloadSelectedFiles, "unload", PDO_DLGCMDBUTTON, NULL, N_(PARAMBUTTON_UNLOAD), 0L, FALSE }, + { PD_BUTTON, (void*)ParamRefreshSelectedFiles, "refresh", PDO_DLGCMDBUTTON, NULL, N_(PARAMBUTTON_REFRESH), 0L, FALSE }, + { PD_BUTTON, (void*)DoSearchParams, "find", 0, NULL, N_("Search Library") }, + { PD_BUTTON, (void*)ParamFileBrowse, "browse", 0, NULL, N_("Browse ...") }, + + +}; static paramGroup_t paramFilePG = { "prmfile", 0, paramFilePLs, sizeof paramFilePLs/sizeof paramFilePLs[0] }; +#define MESSAGETEXT ((wMessage_p)paramFilePLs[I_MESSAGE].control) -static void ParamFileLoadList( void ) +static dynArr_t *sortFiles; + +/** Comparison function per C runtime conventions. Elements are ordered by compatibility + * state first and name of contents second. + * + * \param index1 IN first element + * \param index2 IN second element + * \return + */ + +int +CompareParameterFiles(const void *index1, const void *index2) { - 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 ); + paramFileInfo_t paramFile1 = DYNARR_N(paramFileInfo_t, (*sortFiles), *(int*)index1); + paramFileInfo_t paramFile2 = DYNARR_N(paramFileInfo_t, (*sortFiles), *(int*)index2); + + if (paramFile2.trackState != paramFile1.trackState) { + return (paramFile2.trackState - paramFile1.trackState); + } else { + return (strcmp(paramFile1.contents, paramFile2.contents)); + } } /** - * Load the selected parameter files. This is a callback executed when the file selection dialog - * is closed. - * Steps: - * - the parameters are read from file - * - check is performed to see whether the content is already present, if yes the previously - * loaded content is invalidated - * - loaded parameter file is added to list of parameter files - * - if a parameter file dialog exists the list is updated. It is either rewritten in - * in case of an invalidated file or the new file is appended - * - the settings are updated - * These steps are repeated for every file in list + * Create a sorted list of indexes into the parameter file array. That way, the elements + * in the array will not be moved. Instead the list is used for the order in which the + * list box is populated. * - * \param files IN the number of filenames in the fileName array - * \param fileName IN an array of fully qualified filenames - * \param data IN ignored - * \return TRUE on success, FALSE on error + * \param cnt IN number of parameter files + * \param files IN parameter file array + * \param list OUT the ordered list */ -EXPORT int LoadParamFile( - int files, - char ** fileName, - void * data ) +void +SortParamFileList(size_t cnt, dynArr_t *files, int *list) { - wIndex_t inx; - int i = 0; - - wBool_t redrawList = FALSE; - - assert( fileName != NULL ); - assert( files > 0); - - for( i=0; i < files; i++ ) - { - curContents = curSubContents = NULL; - curParamFileIndex = paramFileInfo_da.cnt; - if ( !ReadParams( 0, NULL, fileName[ i ] ) ) - return FALSE; - - assert( curContents != NULL ); - // in case the contents is already presented, make invalid - 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; - } - } + for (size_t i = 0; i < cnt; i++) { + list[i] = i; + } - DYNARR_APPEND( paramFileInfo_t, paramFileInfo_da, 10 ); - paramFileInfo(curParamFileIndex).name = MyStrdup( fileName[ i ] ); - paramFileInfo(curParamFileIndex).valid = TRUE; - 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 ); - } - } + sortFiles = files; - wPrefSetString( "Parameter File Map", curContents, - paramFileInfo(curParamFileIndex).name ); - } - curParamFileIndex = PARAM_CUSTOM; - DoChangeNotification( CHANGE_PARAMS ); - return TRUE; + qsort((void *)list, (size_t)cnt, sizeof(int), CompareParameterFiles); +} + + +/** + * Reload the listbox showing the current parameter files + */ +void ParamFileListLoad(int paramFileCnt, dynArr_t *paramFiles) +{ + DynString description; + DynStringMalloc(&description, STR_SHORT_SIZE); + int *sortedIndex = MyMalloc(sizeof(int)*paramFileCnt); + int log_params = LogFindIndex("params"); + + SortParamFileList(paramFileCnt, paramFiles, sortedIndex); + + wControlShow((wControl_p)paramFileL, FALSE); + wListClear(paramFileL); + + for (int i = 0; i < paramFileCnt; i++) { + paramFileInfo_t paramFileInfo = DYNARR_N(paramFileInfo_t, (*paramFiles), + sortedIndex[ i ]); + if (paramFileInfo.valid) { + DynStringClear(&description); + DynStringCatCStr(&description, + ((!paramFileSel) && paramFileInfo.contents) ? + paramFileInfo.contents : + paramFileInfo.name); + + wListAddValue(paramFileL, + DynStringToCStr(&description), + indicatorIcons[ paramFileInfo.favorite ][paramFileInfo.trackState], + (void*)(intptr_t)sortedIndex[i]); + + LOG1(log_params, ("ParamFileListLoad: = %s: %d\n", paramFileInfo.contents, paramFileInfo.trackState)) + } + } + wControlShow((wControl_p)paramFileL, TRUE); + DynStringFree(&description); + MyFree(sortedIndex); } -static void ParamFileBrowse( void * junk ) +static void ParamFileBrowse(void * junk) { - wFilSelect( paramFile_fs, curParamDir ); - return; + wMessageSetValue(MESSAGETEXT, ""); + wFilSelect(paramFile_fs, GetParamFileDir()); + return; } /** - * Update the action button. If at least one selected file is unloaded, the action button - * is set to 'Reload'. If all selected files are loaded, the button will be set to 'Unload'. + * Update the action buttons. + * + * If at least one selected file is not a favorite, the favorite button is set to 'SetFavorite' * - * \param varname1 IN this is a variable - * \return */ -static void UpdateParamFileButton( - wIndex_t fileInx ) +static void UpdateParamFileButton(void) { - wIndex_t selcnt = wListGetSelectedCount( paramFileL ); - wIndex_t inx, cnt; - - // set the default - wButtonSetLabel( paramFileActionB, _("Unload")); - paramFilePLs[ I_PRMFILACTION ].context = FALSE; - - //nothing selected -> leave - if( selcnt <= 0 ) - return; - - // get the number of items in list - cnt = wListGetCount( paramFileL ); - - // walk through the whole list box - for ( inx=0; inx<cnt; inx++ ) - { - if ( wListGetItemSelected( (wList_p)paramFileL, inx )) - { - // if item is selected, get status - fileInx = (intptr_t)wListGetItemContext( paramFileL, inx ); - - if (fileInx < 0 || fileInx >= paramFileInfo_da.cnt) - return; - if( paramFileInfo(fileInx).deleted ) { - // if selected file was unloaded, set button to reload and finish loop - wButtonSetLabel( paramFileActionB, _("Reload")); - paramFilePLs[ I_PRMFILACTION ].context = (void *)TRUE; - break; - } - } - } + wIndex_t selcnt = wListGetSelectedCount(paramFileL); + wIndex_t inx, cnt; + wIndex_t fileInx; + + //nothing selected -> leave + if (selcnt <= 0) { + return; + } + + // set the default + paramFilePLs[I_PRMFILEFAVORITE].context = FALSE; + + // get the number of items in list + cnt = wListGetCount(paramFileL); + + // walk through the whole list box + for (inx=0; inx<cnt; inx++) { + if (wListGetItemSelected((wList_p)paramFileL, inx)) { + // if item is selected, get status + fileInx = (intptr_t)wListGetItemContext(paramFileL, inx); + + if (fileInx < 0 || fileInx >= GetParamFileCount()) { + return; + } + if (!IsParamFileFavorite(fileInx)) { + paramFilePLs[I_PRMFILEFAVORITE].context = (void *)TRUE; + } + } + } } +/** + * Set the property for a parameter file in memory + * + * \param newState IN new value for property + */ + +void +UpdateParamFileProperties( bool newState) +{ + wIndex_t inx, cnt; + wIndex_t fileInx; + + // get the number of items in list + cnt = wListGetCount(paramFileL); + + // walk through the whole list box + for (inx = 0; inx < cnt; inx++) { + if (wListGetItemSelected((wList_p)paramFileL, inx)) { + fileInx = (intptr_t)wListGetItemContext(paramFileL, inx); + SetParamFileFavorite(fileInx, newState); + } + } + DoChangeNotification(CHANGE_PARAMS); +} /** - * Unload selected files. + * Mark selected files as favorite * - * \param action IN FALSE = unload, TRUE = reload parameter files + * \param favorite IN FALSE = remove, TRUE = set favorite * \return */ -static void ParamFileAction( void * action ) +static void ParamFileFavorite(void * setFavorite) +{ + wIndex_t selcnt = wListGetSelectedCount(paramFileL); + wMessageSetValue(MESSAGETEXT, ""); + if (selcnt) { + UpdateParamFileProperties(setFavorite?TRUE:FALSE); + } +} + +/** + * Parameter change selected files + * + * \param paramFileChange The parameter file change. + */ + +static void +ParamChangeSelectedFiles(unsigned paramFileChange) { - wIndex_t selcnt = wListGetSelectedCount( paramFileL ); wIndex_t inx, cnt; wIndex_t fileInx; - unsigned newDeletedState; - - if( action ) - newDeletedState = FALSE; - else - newDeletedState = TRUE; - - //nothing selected -> leave - if( selcnt <= 0 ) - return; // get the number of items in list - cnt = wListGetCount( paramFileL ); - - // walk through the whole list box - for ( inx=0; inx<cnt; inx++ ) - { - if ( wListGetItemSelected( (wList_p)paramFileL, inx ) ) - { - fileInx = (intptr_t)wListGetItemContext( paramFileL, inx ); - - // set the desired state - paramFileInfo(fileInx).deleted = newDeletedState; - - strcpy( message, ((!paramFileSel) && paramFileInfo(fileInx).contents)? - paramFileInfo(fileInx).contents: - paramFileInfo(fileInx).name ); - wListSetValues( paramFileL, inx, message, (paramFileInfo(fileInx).deleted)?mtbox_bm:chkbox_bm, (void*)(intptr_t)fileInx ); + cnt = wListGetCount(paramFileL); + + for (inx = 0; inx < cnt; inx++) { + if (wListGetItemSelected((wList_p)paramFileL, inx)) { + fileInx = (intptr_t)wListGetItemContext(paramFileL, inx); + + switch (paramFileChange) { + case PARAMFILE_UNLOAD: + if (IsParamFileFavorite(fileInx)) { + SetParamFileDeleted(fileInx, TRUE); + } else { + UnloadParamFile(fileInx); + } + break; + case PARAMFILE_REFRESH: + if (IsParamFileFavorite(fileInx) && IsParamFileDeleted(fileInx)) { + SetParamFileDeleted(fileInx, FALSE); + } else { + ReloadParamFile(fileInx); + } + break; + default: + AbortProg("Invalid change type %d in ParamChangeSelectedFiles", paramFileChange); + } } } - DoChangeNotification( CHANGE_PARAMS ); - UpdateParamFileButton( fileInx ); + ParamFileListLoad(paramFileInfo_da.cnt, ¶mFileInfo_da); + DoChangeNotification(CHANGE_PARAMS); } /** - * Select all files in the list and set action button + * Refresh selected files. * - * \param junk IN ignored - * \return + * \param action IN FALSE = unload, TRUE = reload parameter files + * \return */ -static void ParamFileSelectAll( void *junk ) +static void ParamRefreshSelectedFiles(void * action) { - wListSelectAll( paramFileL ); - UpdateParamFileButton( 0 ); + wIndex_t selcnt = wListGetSelectedCount(paramFileL); + + //nothing selected -> leave + if (selcnt) { + DynString reloadMessage; + ParamChangeSelectedFiles(PARAMFILE_REFRESH); + + DynStringMalloc(&reloadMessage, 16); + if (selcnt > 1) { + DynStringPrintf(&reloadMessage, _("%d parameter files reloaded."), selcnt); + } else { + DynStringCatCStr(&reloadMessage, _("One parameter file reloaded.")); + } + wMessageSetValue(MESSAGETEXT, DynStringToCStr(&reloadMessage)); + DynStringFree(&reloadMessage); + } else { + wBeep(); + } } -static void ParamFileOk( void * junk ) +static void ParamUnloadSelectedFiles(void * action) { - wIndex_t fileInx; - for ( fileInx = 0; fileInx < paramFileInfo_da.cnt; fileInx++ ) - paramFileInfo(fileInx).deletedShadow = paramFileInfo(fileInx).deleted; - wHide( paramFileW ); + wIndex_t selcnt = wListGetSelectedCount(paramFileL); + wMessageSetValue(MESSAGETEXT, ""); + //nothing selected -> leave + if (selcnt) { + ParamChangeSelectedFiles(PARAMFILE_UNLOAD); + } else { + wBeep(); + } } -static void ParamFileCancel( wWin_p junk ) +/** + * Select all files in the list and set action button + * + * \param junk IN ignored + * \return + */ + +static void ParamFileSelectAll(void *junk) { - wIndex_t fileInx; - for ( fileInx = 0; fileInx < paramFileInfo_da.cnt; fileInx++ ) - paramFileInfo(fileInx).deleted = paramFileInfo(fileInx).deletedShadow; - wHide( paramFileW ); - DoChangeNotification( CHANGE_PARAMS ); + wMessageSetValue(MESSAGETEXT, ""); + wListSelectAll(paramFileL); + UpdateParamFileButton(); } - -static void ParamFilesChange( long changes ) +static void ParamFileOk(void * junk) { -#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 + SearchUiOk(junk); + + DoChangeNotification(CHANGE_PARAMS); + wHide(paramFileW); } static void ParamFileDlgUpdate( - paramGroup_p pg, - int inx, - void * valueP ) + 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; - } + switch (inx) { + case I_PRMFILLIST: + UpdateParamFileButton(); + break; + case I_PRMFILTOGGLE: + DoChangeNotification(CHANGE_PARAMS); + break; + } } -#include "bitmaps/mtbox.xbm" -#include "bitmaps/chkbox.xbm" -static void DoParamFiles( void * junk ) +void ParamFilesChange(long changes) { - wIndex_t listInx; - void * data; - - if (paramFileW == NULL) { - const char * dir; - dir = wPrefGetString( "file", "paramdir" ); - if (dir != NULL) - strcpy( curParamDir, dir ); - else { - // in case there is no preference setting, use the installation's param directory as default - char *str; - MakeFullpath(&str, libDir, PARAM_SUBDIR, NULL); - strcpy( curParamDir, str ); - free(str); - } - mtbox_bm = wIconCreateBitMap( mtbox_width, mtbox_height, mtbox_bits, drawColorBlack ); - chkbox_bm = wIconCreateBitMap( chkbox_width, chkbox_height, chkbox_bits, drawColorBlack ); - paramFileW = ParamCreateDialog( ¶mFilePG, MakeWindowTitle(_("Parameter Files")), _("Ok"), ParamFileOk, ParamFileCancel, TRUE, NULL, 0, ParamFileDlgUpdate ); - paramFile_fs = wFilSelCreate( mainW, FS_LOAD, FS_MULTIPLEFILES, _("Load Parameters"), _("Parameter files|*.xtp"), LoadParamFile, NULL ); - ParamFileLoadList(); - } - ParamLoadControls( ¶mFilePG ); - ParamGroupRecord( ¶mFilePG ); - if ((listInx = wListGetValues( paramFileL, NULL, 0, NULL, &data ))>=0) - UpdateParamFileButton( (wIndex_t)(long)data ); - ParamFileLoadList(); - wShow( paramFileW ); + if (changes & CHANGE_PARAMS || changes & CHANGE_SCALE) { + UpdateParamFileList(); + if (paramFileW) { + ParamFileListLoad(paramFileInfo_da.cnt, ¶mFileInfo_da); + } + } } +/** + * Create and open the parameter file dialog. + * + * \param junk + */ -EXPORT addButtonCallBack_t ParamFilesInit( void ) +void DoParamFiles(void * junk) { - BOOL_T initted = FALSE; - if (!initted) { - ParamRegister( ¶mFilePG ); - RegisterChangeNotification( ParamFilesChange ); - initted = TRUE; - } - return &DoParamFiles; + void * data; + + if (paramFileW == NULL) { + indicatorIcons[ STANDARD_PARAM ][ PARAMFILE_UNLOADED ] = wIconCreatePixMap( + greydot); + indicatorIcons[ STANDARD_PARAM ][ PARAMFILE_NOTUSABLE ] = wIconCreatePixMap( + reddot); + indicatorIcons[ STANDARD_PARAM ][ PARAMFILE_COMPATIBLE ] = wIconCreatePixMap( + yellowdot); + indicatorIcons[ STANDARD_PARAM ][ PARAMFILE_FIT] = wIconCreatePixMap(greendot); + indicatorIcons[ FAVORITE_PARAM ][ PARAMFILE_UNLOADED ] = wIconCreatePixMap( + greystar); + indicatorIcons[ FAVORITE_PARAM ][ PARAMFILE_NOTUSABLE ] = wIconCreatePixMap( + redstar); + indicatorIcons[ FAVORITE_PARAM ][ PARAMFILE_COMPATIBLE ] = wIconCreatePixMap( + yellowstar); + indicatorIcons[ FAVORITE_PARAM ][ PARAMFILE_FIT ] = wIconCreatePixMap( + greenstar); + + ParamRegister(¶mFilePG); + + paramFileW = ParamCreateDialog(¶mFilePG, + MakeWindowTitle(_("Parameter Files")), _("Ok"), ParamFileOk, NULL, + TRUE, NULL, F_RESIZE | F_RECALLSIZE, ParamFileDlgUpdate); + paramFile_fs = wFilSelCreate(mainW, FS_LOAD, FS_MULTIPLEFILES, + _("Load Parameters"), _("Parameter files (*.xtp)|*.xtp"), LoadParamFile, NULL); + } + ParamLoadControls(¶mFilePG); + ParamGroupRecord(¶mFilePG); + if ((wListGetValues(paramFileL, NULL, 0, NULL, &data))>=0) { + UpdateParamFileButton(); + } + + wShow(paramFileW); } diff --git a/app/bin/draw.c b/app/bin/draw.c index 1a0a74f..343ae3f 100644 --- a/app/bin/draw.c +++ b/app/bin/draw.c @@ -50,18 +50,32 @@ #include "param.h" #include "track.h" #include "utility.h" +#include "layout.h" static void DrawRoomWalls( wBool_t ); -EXPORT void DrawMarkers( void ); -static void ConstraintOrig( coOrd *, coOrd ); +static void DrawMarkers( void ); +static void ConstraintOrig( coOrd *, coOrd, int, int ); +static void DoMouse( wAction_t action, coOrd pos ); +static void DDrawPoly( + drawCmd_p d, + int cnt, + coOrd * pts, + int * types, + wDrawColor color, + wDrawWidth width, + int fill, + int open ); +static void DrawMapBoundingBox( BOOL_T set ); +static void DrawTicks( drawCmd_p d, coOrd size ); -static int log_pan = 0; +EXPORT int log_pan = 0; static int log_zoom = 0; static int log_mouse = 0; +static int log_redraw = 0; -static wFontSize_t drawMaxTextFontSize = 100; +static BOOL_T hideBox = FALSE; -extern long zoomCorner; +static wFontSize_t drawMaxTextFontSize = 100; /**************************************************************************** * @@ -69,12 +83,6 @@ extern long zoomCorner; * */ -#define INIT_MAIN_SCALE (8.0) -#define INIT_MAP_SCALE (64.0) -#define MAX_MAIN_SCALE (256.0) -#define MIN_MAIN_SCALE (1.0) -#define MIN_MAIN_MACRO (0.10) - // static char FAR message[STR_LONG_SIZE]; EXPORT wPos_t closePixels = 10; @@ -83,14 +91,31 @@ EXPORT long drawCount; EXPORT BOOL_T drawEnable = TRUE; EXPORT long currRedraw = 0; +EXPORT coOrd panCenter; +EXPORT coOrd menuPos; + EXPORT wDrawColor drawColorBlack; EXPORT wDrawColor drawColorWhite; EXPORT wDrawColor drawColorRed; EXPORT wDrawColor drawColorBlue; EXPORT wDrawColor drawColorGreen; EXPORT wDrawColor drawColorAqua; +EXPORT wDrawColor drawColorPreviewSelected; +EXPORT wDrawColor drawColorPreviewUnselected; +EXPORT wDrawColor drawColorPowderedBlue; EXPORT wDrawColor drawColorPurple; EXPORT wDrawColor drawColorGold; +EXPORT wDrawColor drawColorGrey10; +EXPORT wDrawColor drawColorGrey20; +EXPORT wDrawColor drawColorGrey30; +EXPORT wDrawColor drawColorGrey40; +EXPORT wDrawColor drawColorGrey50; +EXPORT wDrawColor drawColorGrey60; +EXPORT wDrawColor drawColorGrey70; +EXPORT wDrawColor drawColorGrey80; +EXPORT wDrawColor drawColorGrey90; + + EXPORT DIST_T pixelBins = 80; @@ -104,6 +129,7 @@ static wPos_t infoHeight; static wPos_t textHeight; EXPORT wWin_p mapW; EXPORT BOOL_T mapVisible; +EXPORT BOOL_T magneticSnap; EXPORT wDrawColor markerColor; EXPORT wDrawColor borderColor; @@ -148,15 +174,17 @@ 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 char xLabel[] = "X: "; +static char yLabel[] = "Y: "; +static char zoomLabel[] = "Zoom: "; static struct { char * name; double value; wMenuRadio_p pdRadio; wMenuRadio_p btRadio; + wMenuRadio_p ctxRadio1; + wMenuRadio_p panRadio; } zoomList[] = { { "1:10", 1.0 / 10.0 }, { "1:9", 1.0 / 9.0 }, @@ -293,9 +321,26 @@ static void DDrawLine( d->CoOrd2Pix(d,p0,&x0,&y0); d->CoOrd2Pix(d,p1,&x1,&y1); drawCount++; + wDrawLineType_e lineOpt = wDrawLineSolid; + unsigned long NotSolid = DC_NOTSOLIDLINE; + unsigned long opt = d->options&NotSolid; + if (opt == DC_DASH) + lineOpt = wDrawLineDash; + else if(opt == DC_DOT) + lineOpt = wDrawLineDot; + else if(opt == DC_DASHDOT) + lineOpt = wDrawLineDashDot; + else if (opt == DC_DASHDOTDOT) + lineOpt = wDrawLineDashDotDot; + else if(opt == DC_CENTER) + lineOpt = wDrawLineCenter; + else if (opt == DC_PHANTOM) + lineOpt = wDrawLinePhantom; + if (drawEnable) { wDrawLine( d->d, x0, y0, x1, y1, - width, ((d->options&DC_DASH)==0)?wDrawLineSolid:wDrawLineDash, + width, + lineOpt, color, (wDrawOpts)d->funcs->options ); } } @@ -311,37 +356,78 @@ static void DDrawArc( 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 ); - } + 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; + coOrd min,max; + min = d->orig; + max.x = min.x + d->size.x; + max.y = min.y + d->size.y; + PointOnCircle(&p0, p, r, angle0); + for (i=1; i<=cnt; i++) { + angle0 += da; + PointOnCircle(&p1, p, r, angle0); + if (d->angle == 0.0 && + ((p0.x >= min.x && + p0.x <= max.x && + p0.y >= min.y && + p0.y <= max.y) || + (p1.x >= min.x && + p1.x <= max.x && + p1.y >= min.y && + p1.y <= max.y))) { + DrawLine(d, p0, p1, width, color); + } else { + coOrd clip0 = p0, clip1 = p1; + if (ClipLine(&clip0, &clip1, d->orig, d->angle, d->size)) { + DrawLine(d, clip0, clip1, 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++; + wDrawLineType_e lineOpt = wDrawLineSolid; + unsigned long NotSolid = DC_NOTSOLIDLINE; + unsigned long opt = d->options&NotSolid; + if (opt == DC_DASH) + lineOpt = wDrawLineDash; + else if(opt == DC_DOT) + lineOpt = wDrawLineDot; + else if(opt == DC_DASHDOT) + lineOpt = wDrawLineDashDot; + else if (opt == DC_DASHDOTDOT) + lineOpt = wDrawLineDashDotDot; + else if(opt == DC_CENTER) + lineOpt = wDrawLineCenter; + else if (opt == DC_PHANTOM) + lineOpt = wDrawLinePhantom; + if (drawEnable) + { + wDrawArc(d->d, x, y, (wPos_t)(rr), angle0, angle1, drawCenter, + width, lineOpt, + color, (wDrawOpts)d->funcs->options); + } } @@ -357,30 +443,76 @@ static void DDrawString( 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 ); + if ( color == wDrawColorWhite ) { + wPos_t width, height, descent, ascent; + coOrd pos[4], size; + double scale = 1.0; + wDrawGetTextSize( &width, &height, &descent, &ascent, d->d, s, fp, fontSize ); + pos[0] = p; + size.x = SCALEX(mainD,width)*scale; + size.y = SCALEY(mainD,height)*scale; + pos[1].x = p.x+size.x; + pos[1].y = p.y; + pos[2].x = p.x+size.x; + pos[2].y = p.y+size.y; + pos[3].x = p.x; + pos[3].y = p.y+size.y; + Rotate( &pos[1], pos[0], a ); + Rotate( &pos[2], pos[0], a ); + Rotate( &pos[3], pos[0], a ); + DDrawPoly( d, 4, pos, NULL, color, 0, 1, 0 ); + } else { + fontSize /= d->scale; + wDrawString( d->d, x, y, d->angle-a, s, fp, fontSize, color, (wDrawOpts)d->funcs->options ); + } } -static void DDrawFillPoly( +static void DDrawPoly( drawCmd_p d, int cnt, coOrd * pts, - wDrawColor color ) + int * types, + wDrawColor color, + wDrawWidth width, + int fill, + int open ) { typedef wPos_t wPos2[2]; static dynArr_t wpts_da; + static dynArr_t wpts_type_da; int inx; wPos_t x, y; DYNARR_SET( wPos2, wpts_da, cnt * 2 ); + DYNARR_SET( int, wpts_type_da, cnt); #define wpts(N) DYNARR_N( wPos2, wpts_da, N ) +#define wtype(N) DYNARR_N( wPolyLine_e, wpts_type_da, N ) for ( inx=0; inx<cnt; inx++ ) { d->CoOrd2Pix( d, pts[inx], &x, &y ); wpts(inx)[0] = x; wpts(inx)[1] = y; + if (!types) + wtype(inx) = 0; + else + wtype(inx) = (wPolyLine_e)types[inx]; } - wDrawFilledPolygon( d->d, &wpts(0), cnt, color, (wDrawOpts)d->funcs->options ); + wDrawLineType_e lineOpt = wDrawLineSolid; + unsigned long NotSolid = DC_NOTSOLIDLINE; + unsigned long opt = d->options&NotSolid; + if (opt == DC_DASH) + lineOpt = wDrawLineDash; + else if(opt == DC_DOT) + lineOpt = wDrawLineDot; + else if(opt == DC_DASHDOT) + lineOpt = wDrawLineDashDot; + else if (opt == DC_DASHDOTDOT) + lineOpt = wDrawLineDashDotDot; + else if(opt == DC_CENTER) + lineOpt = wDrawLineCenter; + else if (opt == DC_PHANTOM) + lineOpt = wDrawLinePhantom; + wDrawPolygon( d->d, &wpts(0), &wtype(0), cnt, color, width, lineOpt, (wDrawOpts)d->funcs->options, fill, open ); } @@ -420,24 +552,20 @@ static void DDrawFillCircle( } -EXPORT void DrawHilight( drawCmd_p d, coOrd p, coOrd s ) +EXPORT void DrawHilight( drawCmd_p d, coOrd p, coOrd s, BOOL_T add ) { 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 ); -} + if ( add ) + wDrawFilledRectangle( d->d, x, y, w, h, drawColorPowderedBlue, wDrawOptTemp|wDrawOptTransparent ); + else + wDrawFilledRectangle( d->d, x, y, w, h, selectedColor, wDrawOptTemp|wDrawOptTransparent ); +} EXPORT void DrawHilightPolygon( drawCmd_p d, coOrd *p, int cnt ) { @@ -454,7 +582,10 @@ EXPORT void DrawHilightPolygon( drawCmd_p d, coOrd *p, int cnt ) for (i=0; i<cnt; i++) { d->CoOrd2Pix(d,p[i],&q[i][0],&q[i][1]); } - wDrawFilledPolygon( d->d, q, cnt, wDrawColorBlack, wDrawOptTemp ); + static wDrawColor color = 0; + if ( color == 0 ) + color = wDrawColorGray( 70 ); + wDrawPolygon( d->d, q, NULL, cnt, color, 0, 0, wDrawOptTemp|wDrawOptTransparent, 1, 0 ); } @@ -467,13 +598,14 @@ EXPORT void DrawMultiString( wDrawColor color, ANGLE_T a, coOrd * lo, - coOrd * hi) + coOrd * hi, + BOOL_T boxed) { char * cp; char * cp1; POS_T lineH, lineW; coOrd size, textsize, posl, orig; - POS_T descent; + POS_T descent, ascent; char *line; if (!text || !*text) { @@ -481,9 +613,9 @@ EXPORT void DrawMultiString( } line = malloc(strlen(text) + 1); - DrawTextSize2( &mainD, "Aqjlp", fp, fs, TRUE, &textsize, &descent); - POS_T ascent = textsize.y-descent; - lineH = ascent+descent*1.5; + DrawTextSize2( &mainD, "Aqjlp", fp, fs, TRUE, &textsize, &descent, &ascent); + //POS_T ascent = textsize.y-descent; + lineH = (ascent+descent)*1.0; size.x = 0.0; size.y = 0.0; orig.x = pos.x; @@ -494,7 +626,7 @@ EXPORT void DrawMultiString( while (*text != '\0' && *text != '\n') *cp++ = *text++; *cp = '\0'; - DrawTextSize2( &mainD, cp1, fp, fs, TRUE, &textsize, &descent); + DrawTextSize2( &mainD, cp1, fp, fs, TRUE, &textsize, &descent, &ascent); lineW = textsize.x; if (lineW>size.x) size.x = lineW; @@ -515,7 +647,25 @@ EXPORT void DrawMultiString( } if (hi) { hi->x = posl.x+size.x; - hi->y = posl.y+ascent; + hi->y = orig.y+ascent; + } + if (boxed && (d != &mapD)) { + int bw=2, bh=2, br=1, bb=1; + size.x += bw*d->scale/d->dpi; + size.y = fabs(orig.y-posl.y)+bh*d->scale/d->dpi; + size.y += descent+ascent; + coOrd p[4]; + p[0] = orig; p[0].x -= (bw-br)*d->scale/d->dpi; p[0].y += (bh-bb)*d->scale/d->dpi+ascent; + p[1] = p[0]; p[1].x += size.x; + p[2] = p[1]; p[2].y -= size.y; + p[3] = p[2]; p[3].x = p[0].x; + for (int i=0;i<4;i++) { + Rotate( &p[i], orig, a); + } + 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 ); } free(line); @@ -532,24 +682,25 @@ EXPORT void DrawBoxedString( ANGLE_T a ) { coOrd size, p[4], p0=pos, p1, p2; - static int bw=5, bh=4, br=2, bb=2; + static int bw=2, bh=2, br=1, bb=1; static double arrowScale = 0.5; - long options = d->options; - POS_T descent; + unsigned long options = d->options; + POS_T descent, ascent; /*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 ); + wPos_t w, h, d, a; + wDrawGetTextSize( &w, &h, &d, &a, mainD.d, text, fp, drawMaxTextFontSize ); size.x = w*scale; size.y = h*scale; descent = d*scale; + ascent = a*scale; } else #endif - DrawTextSize2( &mainD, text, fp, fs, TRUE, &size, &descent ); + DrawTextSize2( &mainD, text, fp, fs, TRUE, &size, &descent, &ascent ); #ifdef WINDOWS /*h -= 15;*/ #endif @@ -561,10 +712,9 @@ EXPORT void DrawBoxedString( } 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[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; @@ -589,12 +739,12 @@ EXPORT void DrawBoxedString( DrawString( d, p0, 0.0, text, fp, fs, color ); break; case BOX_INVERT: - DrawFillPoly( d, 4, p, color ); + DrawPoly( d, 4, p, NULL, color, 0, 1, 0); if ( color != wDrawColorWhite ) - DrawString( d, p0, 0.0, text, fp, fs, wDrawColorWhite ); + DrawString( d, p0, 0.0, text, fp, fs, wDrawColorGray( 94 ) ); break; case BOX_BACKGROUND: - DrawFillPoly( d, 4, p, wDrawColorWhite ); + DrawPoly( d, 4, p, NULL, wDrawColorWhite, 0, 1, 0 ); DrawString( d, p0, 0.0, text, fp, fs, color ); break; } @@ -609,9 +759,10 @@ EXPORT void DrawTextSize2( wFontSize_t fs, BOOL_T relative, coOrd * size, - POS_T * descent ) + POS_T * descent, + POS_T * ascent) { - wPos_t w, h, d; + wPos_t w, h, d, a; FLOAT_T scale = 1.0; if ( relative ) fs /= dp->scale; @@ -619,14 +770,16 @@ EXPORT void DrawTextSize2( scale = ((FLOAT_T)fs)/((FLOAT_T)drawMaxTextFontSize); fs = drawMaxTextFontSize; } - wDrawGetTextSize( &w, &h, &d, dp->d, text, fp, fs ); + wDrawGetTextSize( &w, &h, &d, &a, dp->d, text, fp, fs ); size->x = SCALEX(mainD,w)*scale; size->y = SCALEY(mainD,h)*scale; *descent = SCALEY(mainD,d)*scale; + *ascent = SCALEY(mainD,a)*scale; if ( relative ) { size->x *= dp->scale; size->y *= dp->scale; *descent *= dp->scale; + *ascent *=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 );*/ } @@ -639,8 +792,8 @@ EXPORT void DrawTextSize( BOOL_T relative, coOrd * size ) { - POS_T descent; - DrawTextSize2( dp, text, fp, fs, relative, size, &descent ); + POS_T descent, ascent; + DrawTextSize2( dp, text, fp, fs, relative, size, &descent, &ascent ); } EXPORT void DrawMultiLineTextSize( @@ -652,15 +805,14 @@ EXPORT void DrawMultiLineTextSize( coOrd * size, coOrd * lastline ) { - POS_T descent, lineW, lineH; + POS_T descent, ascent, lineW, lineH; coOrd textsize, blocksize; char *cp; char *line = malloc(strlen(text) + 1); - DrawTextSize2( &mainD, "Aqlip", fp, fs, TRUE, &textsize, &descent); - POS_T ascent = textsize.y-descent; - lineH = ascent+descent*1.5; + DrawTextSize2( &mainD, "Aqlip", fp, fs, TRUE, &textsize, &descent, &ascent); + lineH = (ascent+descent)*1.0; blocksize.x = 0; blocksize.y = 0; lastline->x = 0; @@ -671,17 +823,20 @@ EXPORT void DrawMultiLineTextSize( *cp++ = *text++; *cp = '\0'; blocksize.y += lineH; - DrawTextSize2( &mainD, line, fp, fs, TRUE, &textsize, &descent); + DrawTextSize2( &mainD, line, fp, fs, TRUE, &textsize, &descent, &ascent); lineW = textsize.x; if (lineW>blocksize.x) blocksize.x = lineW; lastline->x = textsize.x; if (*text =='\n') { + blocksize.y += lineH; lastline->y -= lineH; lastline->x = 0; } - if (*text == '\0') + if (*text == '\0') { + blocksize.y += textsize.y; break; + } text++; } size->x = blocksize.x; @@ -760,6 +915,7 @@ static void TempSegString( 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).u.t.boxed = FALSE; 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; @@ -769,22 +925,33 @@ static void TempSegString( } -static void TempSegFillPoly( +static void TempSegPoly( drawCmd_p d, int cnt, coOrd * pts, - wDrawColor color ) + int * types, + wDrawColor color, + wDrawWidth width, + int fill, + int open ) { -#ifdef LATER - pts is not guaranteed to valid - DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); - tempSegs(tempSegs_da.cnt-1).type = SEG_FILPOLY; + DYNARR_APPEND( trkSeg_t, tempSegs_da, 1); + tempSegs(tempSegs_da.cnt-1).type = fill?SEG_FILPOLY:SEG_POLY; tempSegs(tempSegs_da.cnt-1).color = color; - tempSegs(tempSegs_da.cnt-1).width = 0; + 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.p.polyType = open?POLYLINE:FREEFORM; tempSegs(tempSegs_da.cnt-1).u.p.cnt = cnt; - tempSegs(tempSegs_da.cnt-1).u.p.pts = pts; -#endif - return; + tempSegs(tempSegs_da.cnt-1).u.p.orig = zero; + tempSegs(tempSegs_da.cnt-1).u.p.angle = 0.0; + tempSegs(tempSegs_da.cnt-1).u.p.pts = (pts_t *)MyMalloc(cnt*sizeof(pts_t)); + for (int i=0;i<=cnt-1;i++) { + tempSegs(tempSegs_da.cnt-1).u.p.pts[i].pt = pts[i]; + tempSegs(tempSegs_da.cnt-1).u.p.pts[i].pt_type = (d->options&DC_SIMPLE)==0?types[i]:wPolyLineStraight; + } + } @@ -817,7 +984,7 @@ EXPORT drawFuncs_t screenDrawFuncs = { DDrawArc, DDrawString, DDrawBitMap, - DDrawFillPoly, + DDrawPoly, DDrawFillCircle }; EXPORT drawFuncs_t tempDrawFuncs = { @@ -826,7 +993,7 @@ EXPORT drawFuncs_t tempDrawFuncs = { DDrawArc, DDrawString, DDrawBitMap, - DDrawFillPoly, + DDrawPoly, DDrawFillCircle }; EXPORT drawFuncs_t printDrawFuncs = { @@ -835,7 +1002,7 @@ EXPORT drawFuncs_t printDrawFuncs = { DDrawArc, DDrawString, NoDrawBitMap, - DDrawFillPoly, + DDrawPoly, DDrawFillCircle }; EXPORT drawFuncs_t tempSegDrawFuncs = { @@ -844,17 +1011,17 @@ EXPORT drawFuncs_t tempSegDrawFuncs = { TempSegArc, TempSegString, NoDrawBitMap, - TempSegFillPoly, + TempSegPoly, 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 }; + NULL, &tempDrawFuncs, DC_TICKS, 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 }; + NULL, &screenDrawFuncs, DC_SIMPLE, INIT_MAP_SCALE, 0.0, {0.0,0.0}, {96.0,48.0}, Pix2CoOrd, CoOrd2Pix }; /***************************************************************************** @@ -919,7 +1086,7 @@ EXPORT void InitInfoBar( void ) { wPos_t width, height, y, yb, ym, x, boxH; wWinGetSize( mainW, &width, &height ); - infoHeight = 3 + wStatusGetHeight( COMBOBOX ) + 3; + infoHeight = 2 + wStatusGetHeight( COMBOBOX ) + 2 ; textHeight = wStatusGetHeight(0L); y = height - max(infoHeight,textHeight)-10; @@ -927,8 +1094,8 @@ EXPORT void InitInfoBar( void ) y -= 19; /* Kludge for MSW */ #endif - infoD.pos_w = GetInfoPosWidth() + 2; - infoD.scale_w = wStatusGetWidth( "999:1" ) + wStatusGetWidth( zoomLabel ) + 6; + infoD.pos_w = GetInfoPosWidth(); + infoD.scale_w = wStatusGetWidth( "999:1" ) + wStatusGetWidth( zoomLabel ); /* we do not use the count label for the moment */ infoD.count_w = 0; infoD.info_w = width - 20 - infoD.pos_w*2 - infoD.scale_w - infoD.count_w - 45; // Allow Window to resize down @@ -941,13 +1108,13 @@ EXPORT void InitInfoBar( void ) x = 2; infoD.scale_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.scale_w, boxH ); infoD.scale_m = wStatusCreate( mainW, x+info_xm_offset, ym, "infoBarScale", infoD.scale_w-six, zoomLabel); - x += infoD.scale_w + 10; + x += infoD.scale_w + 2; infoD.posX_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.pos_w, boxH ); infoD.posX_m = wStatusCreate( mainW, x+info_xm_offset, ym, "infoBarPosX", infoD.pos_w-six, xLabel ); - x += infoD.pos_w + 5; + x += infoD.pos_w + 2; infoD.posY_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.pos_w, boxH ); infoD.posY_m = wStatusCreate( mainW, x+info_xm_offset, ym, "infoBarPosY", infoD.pos_w-six, yLabel ); - x += infoD.pos_w + 10; + x += infoD.pos_w + 2; messageOrControlX = x+info_xm_offset; //Remember Position messageOrControlY = ym; infoD.info_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.info_w, boxH ); @@ -966,10 +1133,10 @@ static void SetInfoBar( void ) y = height - max(infoHeight,textHeight)-10; newDistanceFormat = GetDistanceFormat(); if ( newDistanceFormat != oldDistanceFormat ) { - infoD.pos_w = GetInfoPosWidth() + 2; - wBoxSetSize( infoD.posX_b, infoD.pos_w, infoHeight-5 ); + infoD.pos_w = GetInfoPosWidth(); + wBoxSetSize( infoD.posX_b, infoD.pos_w, infoHeight-3 ); wStatusSetWidth( infoD.posX_m, infoD.pos_w-six ); - wBoxSetSize( infoD.posY_b, infoD.pos_w, infoHeight-5 ); + wBoxSetSize( infoD.posY_b, infoD.pos_w, infoHeight-3 ); wStatusSetWidth( infoD.posY_m, infoD.pos_w-six ); } infoD.info_w = width - 20 - infoD.pos_w*2 - infoD.scale_w - infoD.count_w - 40 + 4; @@ -1028,13 +1195,12 @@ EXPORT void InfoCount( wIndex_t count ) EXPORT void InfoPos( coOrd pos ) { - DrawMarkers(); sprintf( message, "%s%s", xLabel, FormatDistance(pos.x) ); wStatusSetValue( infoD.posX_m, message ); sprintf( message, "%s%s", yLabel, FormatDistance(pos.y) ); wStatusSetValue( infoD.posY_m, message ); + oldMarker = pos; - DrawMarkers(); } static wControl_p deferSubstituteControls[NUM_INFOCTL+1]; @@ -1071,7 +1237,11 @@ EXPORT void InfoSubstituteControls( for ( inx=0; controls[inx]; inx++ ) { curInfoLabelWidth[inx] = wLabelWidth(_(labels[inx])); x += curInfoLabelWidth[inx]; - int y_this = y + (textHeight/2) - (wControlGetHeight( controls[inx] )/2); +#ifdef WINDOWS + int y_this = y + (infoHeight/2) - (textHeight / 2 ); +#else + int y_this = y + (infoHeight / 2) - (wControlGetHeight(controls[inx]) / 2) - 2; +#endif wControlSetPos( controls[inx], x, y_this ); x += wControlGetWidth( controls[inx] ); wControlSetLabel( controls[inx], _(labels[inx]) ); @@ -1090,39 +1260,23 @@ EXPORT void SetMessage( char * msg ) } -static void ChangeMapScale( void ) +static void ChangeMapScale( BOOL_T reset ) { 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; - } + + 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 ); + if (reset) { + wGetDisplaySize( &dw, &dh ); + wSetGeometry(mapW, 50, dw, 50, dh, -1, -1, mapD.size.x/mapD.size.y); + wWinSetSize( mapW, w+DlgSepLeft+DlgSepRight, h+DlgSepTop+DlgSepBottom); + } wDrawSetSize( mapD.d, w, h, NULL ); } @@ -1137,12 +1291,10 @@ EXPORT BOOL_T SetRoomSize( coOrd size ) mapD.size.y == size.y ) return TRUE; mapD.size = size; + SetLayoutRoomSize(size); if ( mapW == NULL) return TRUE; - ChangeMapScale(); - ConstraintOrig( &mainD.orig, mainD.size ); - tempD.orig = mainD.orig; - /*MainRedraw();*/ + ChangeMapScale(TRUE); wPrefSetFloat( "draw", "roomsizeX", mapD.size.x ); wPrefSetFloat( "draw", "roomsizeY", mapD.size.y ); return TRUE; @@ -1155,23 +1307,21 @@ EXPORT void GetRoomSize( coOrd * froomSize ) } -EXPORT void MapRedraw( void ) +static void MapRedraw() { if (inPlaybackQuit) return; -#ifdef VERBOSE -lprintf("MapRedraw\n"); -#endif + static int cMR = 0; + LOG( log_redraw, 2, ( "MapRedraw: %d\n", cMR++ ) ); if (!mapVisible) return; - if (delayUpdate) wDrawDelayUpdate( mapD.d, TRUE ); - wSetCursor( wCursorWait ); + //wSetCursor( mapD.d, wCursorWait ); wDrawClear( mapD.d ); DrawTracks( &mapD, mapD.scale, mapD.orig, mapD.size ); DrawMapBoundingBox( TRUE ); - wSetCursor( wCursorNormal ); + //wSetCursor( mapD.d, defaultCursor ); wDrawDelayUpdate( mapD.d, FALSE ); } @@ -1179,7 +1329,7 @@ lprintf("MapRedraw\n"); static void MapResize( void ) { mapD.scale = mapScale; - ChangeMapScale(); + ChangeMapScale(TRUE); MapRedraw(); } @@ -1222,25 +1372,107 @@ EXPORT void SetMainSize( void ) tempD.size = mainD.size; } +// Hack to switch between TempRedraw and MainRedraw +extern wBool_t wDrawDoTempDraw; +/* Update temp_surface after executing a command + */ +EXPORT void TempRedraw( void ) { + + static int cTR = 0; + LOG( log_redraw, 2, ( "TempRedraw: %d\n", cTR++ ) ); +if (wDrawDoTempDraw == FALSE) { + // Remove this after windows supports GTK + MainRedraw(); // TempRedraw - windows +} else { + wDrawDelayUpdate( tempD.d, TRUE ); + wDrawSetTempMode( tempD.d, TRUE ); + DrawMarkers(); + DoCurCommand( C_REDRAW, zero ); + RulerRedraw( FALSE ); + RedrawPlaybackCursor(); //If in playback + wDrawSetTempMode( tempD.d, FALSE ); + wDrawDelayUpdate( tempD.d, FALSE ); +} +} + +/* +* Redraw contents on main window +*/ EXPORT void MainRedraw( void ) { + coOrd orig, size; + + static int cMR = 0; + LOG( log_redraw, 1, ( "MainRedraw: %d\n", cMR++ ) ); + if (delayUpdate) + wDrawDelayUpdate( mainD.d, TRUE ); + + wDrawClear( mainD.d ); + + //mainD.d->option = 0; + //mainD.options = 0; + mainD.funcs->options = 0; //Force MainD back from Temp + + orig = mainD.orig; + size = mainD.size; + orig.x -= LBORDER/mainD.dpi*mainD.scale; + orig.y -= BBORDER/mainD.dpi*mainD.scale; + wPos_t back_x,back_y; + coOrd back_pos = GetLayoutBackGroundPos(); + back_x = (wPos_t)((back_pos.x-orig.x)/mainD.scale*mainD.dpi); + back_y = (wPos_t)((back_pos.y-orig.y)/mainD.scale*mainD.dpi); + wPos_t back_width = (wPos_t)(GetLayoutBackGroundSize()/mainD.scale*mainD.dpi); + + DrawRoomWalls( TRUE ); + if (GetLayoutBackGroundScreen() < 100.0 && GetLayoutBackGroundVisible()) { + wDrawShowBackground( mainD.d, back_x, back_y, back_width, GetLayoutBackGroundAngle(), GetLayoutBackGroundScreen()); + } + 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 ); + + DrawRoomWalls( FALSE ); + currRedraw++; + DrawSnapGrid( &mainD, mapD.size, TRUE ); + + //wSetCursor( mainD.d, defaultCursor ); + InfoScale(); + // The remainder is from TempRedraw + wDrawSetTempMode( tempD.d, TRUE ); + DrawMarkers(); + DoCurCommand( C_REDRAW, zero ); + RulerRedraw( FALSE ); + RedrawPlaybackCursor(); //If in playback + wDrawSetTempMode( tempD.d, FALSE ); + wDrawDelayUpdate( mainD.d, FALSE ); +} + +/* +* Layout main window in response to Pan/Zoom +* +* \param bRedraw Redraw mainD +* \param bNoBorder Don't allow mainD.orig to go negative +*/ +EXPORT void MainLayout( + wBool_t bRedraw, + wBool_t bNoBorder ) +{ #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 + static int cML = 0; + LOG( log_redraw, 1, ( "MainLayout: %d\n", cML++ ) ); - wSetCursor( wCursorWait ); - if (delayUpdate) - wDrawDelayUpdate( mainD.d, TRUE ); #ifdef LATER wDrawGetSize( mainD.d, &ww, &hh ); w = ww/mainD.dpi; @@ -1269,25 +1501,32 @@ lprintf("mainRedraw\n"); pixelBins /= 2.0; } } - ConstraintOrig( &mainD.orig, mainD.size ); + ConstraintOrig( &mainD.orig, mainD.size, bNoBorder, FALSE ); 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 ); + tempD.size = mainD.size; + mainCenter.x = mainD.orig.x + mainD.size.x/2.0; + mainCenter.y = mainD.orig.y + mainD.size.y/2.0; + DrawMapBoundingBox( TRUE ); + + if ( bRedraw ) + MainRedraw(); + + if ( bRedraw && wDrawDoTempDraw ) { // Temporary until mswlib supports TempDraw + wAction_t action = wActionMove; + coOrd pos; + if ( mouseState == mouseLeft ) + action = wActionLDrag; + if ( mouseState == mouseRight ) + action = wActionRDrag; + mainD.Pix2CoOrd( &mainD, mousePositionx, mousePositiony, &pos ); + // Prevent recursion if curCommand calls MainLayout when action if a Move or Drag + // ie CmdPan + static int iRecursion = 0; + iRecursion++; + if ( iRecursion == 1 ) + DoMouse( action, pos ); + iRecursion--; + } } /** @@ -1305,7 +1544,6 @@ void MainProc( wWin_p win, winProcEvent e, void * refresh, void * data ) case wResize_e: if (mainD.d == NULL) return; - if (refresh) DrawMapBoundingBox( FALSE ); wWinGetSize( mainW, &width, &height ); LayoutToolBar(refresh); height -= (toolbarHeight+max(infoHeight,textHeight)+10); @@ -1313,13 +1551,11 @@ void MainProc( wWin_p win, winProcEvent e, void * refresh, void * data ) wDrawSetSize( mainD.d, width-20, height, refresh ); wControlSetPos( (wControl_p)mainD.d, 0, toolbarHeight ); SetMainSize(); - ConstraintOrig( &mainD.orig, mainD.size ); - tempD.orig = mainD.orig; SetInfoBar(); - if (!refresh) { - MainRedraw(); - MapRedraw(); - } else DrawMapBoundingBox( TRUE ); + panCenter.x = mainD.orig.x + mainD.size.x/2.0; + panCenter.y = mainD.orig.y + mainD.size.y/2.0; + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + MainLayout( !refresh, TRUE ); // MainProc: wResize_e event wPrefSetInteger( "draw", "mainwidth", width ); wPrefSetInteger( "draw", "mainheight", height ); } else DrawMapBoundingBox( TRUE ); @@ -1363,7 +1599,7 @@ EXPORT void DoRedraw( void ) #endif #endif MapRedraw(); - MainRedraw(); + MainRedraw(); // DoRedraw #ifdef WINDOWS #ifndef WIN32 if (profRedraw) @@ -1381,31 +1617,54 @@ EXPORT void DoRedraw( void ) */ -static void DrawRoomWalls( wBool_t t ) +static void DrawRoomWalls( wBool_t drawBackground ) { - coOrd p01, p11, p10; + coOrd p00, p01, p11, p10; + int p0,p1,p2,p3; if (mainD.d == NULL) return; - DrawTicks( &mainD, mapD.size ); + if (drawBackground) { + mainD.CoOrd2Pix(&mainD,mainD.orig,&p0,&p1); + coOrd end; + end.x = mainD.orig.x + mainD.size.x; + end.y = mainD.orig.y + mainD.size.y; + mainD.CoOrd2Pix(&mainD,end,&p2,&p3); + p2 -= p0; + p3 -= p1; + wDrawFilledRectangle( mainD.d, p0, p1, p2, p3, drawColorGrey80, 0 ); + + mainD.CoOrd2Pix(&mainD,zero,&p0,&p1); + mainD.CoOrd2Pix(&mainD,mapD.size,&p2,&p3); + p2 -= p0; + p3 -= p1; + wDrawFilledRectangle( mainD.d, p0, p1, p2, p3, drawColorWhite, 0 ); + + } else { + + 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 ); + p00.x = 0.0; p00.y = 0.0; + 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, borderColor ); + DrawLine( &mainD, p11, p10, 3, borderColor ); + DrawLine( &mainD, p00, p01, 3, borderColor ); + DrawLine( &mainD, p00, p10, 3, borderColor ); + } } -EXPORT void DrawMarkers( void ) +static void DrawMarkers( void ) { wPos_t x, y; mainD.CoOrd2Pix(&mainD,oldMarker,&x,&y); - wDrawLine( mainD.d, 0, y, (wPos_t)LBORDER, y, + wDrawLine( tempD.d, 0, y, (wPos_t)LBORDER, y, 0, wDrawLineSolid, markerColor, wDrawOptTemp ); - wDrawLine( mainD.d, x, 0, x, (wPos_t)BBORDER, + wDrawLine( tempD.d, x, 0, x, (wPos_t)BBORDER, 0, wDrawLineSolid, markerColor, wDrawOptTemp ); } @@ -1433,9 +1692,9 @@ EXPORT void DrawRuler( 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 }; + + static double lengths[16] = { + 0, 2.0, 4.0, 2.0, 6.0, 2.0, 4.0, 2.0, 8.0, 2.0, 4.0, 2.0, 6.0, 2.0, 4.0, 2.0 }; int fraction, incr, firstFraction, lastFraction; int majorLength; coOrd p0, p1; @@ -1445,17 +1704,8 @@ EXPORT void DrawRuler( 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; @@ -1479,7 +1729,7 @@ EXPORT void DrawRuler( if (units == UNITS_METRIC) { mm0 = (int)ceil(start*25.4-0.5); mm1 = (int)floor(end*25.4+0.5); - len = 2; + len = 5; if (d->scale <= 1) { power = 1; } else if (d->scale <= 8) { @@ -1501,7 +1751,7 @@ EXPORT void DrawRuler( wDrawLine( d->d, x0, y0, x1, y1, 0, wDrawLineSolid, color, (wDrawOpts)d->funcs->options ); - if (!number) + if (!number || (d->scale>40 && mm != 0.0)) continue; if ( (power>=1000) || (d->scale<=8 && power>=100) || @@ -1509,15 +1759,21 @@ EXPORT void DrawRuler( 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; + Translate( &p0, p0, aa, (fs/2.0+len)*d->scale/mainD.dpi ); + Translate( &p0, p0, 225, fs*d->scale/mainD.dpi ); + //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; + Translate( &p0, p0, aa, (fs/2.0+len)*d->scale/mainD.dpi ); + Translate( &p0, p0, 225, 1.5*fs*d->scale/mainD.dpi ); + //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 ); + if (x0<0) x0 = 0; + if (y0<0) y0 = 0; wDrawString( d->d, x0, y0, d->angle, message, rulerFp, fs, color, (wDrawOpts)d->funcs->options ); } @@ -1526,41 +1782,50 @@ EXPORT void DrawRuler( } } else { if (d->scale <= 1) - incr = 1; - else if (d->scale <= 2) - incr = 2; - else if (d->scale <= 4) - incr = 4; + incr = 1; //16ths + else if (d->scale <= 3) + incr = 2; //8ths + else if (d->scale <= 5) + incr = 4; //4ths + else if (d->scale <= 7) + incr = 8; //1/2ths + else if (d->scale <= 48) + incr = 32; else - incr = 8; + incr = 16; //Inches lastInch = (int)floor(end); - lastFraction = 7; + lastFraction = 16; inch = (int)ceil(start); - firstFraction = (((int)((inch-start)*8/*+1*/)) / incr) * incr; + firstFraction = (((int)((inch-start)*16/*+1*/)) / incr) * incr; if (firstFraction > 0) { inch--; - firstFraction = 8 - firstFraction; + firstFraction = 16 - firstFraction; } for ( ; inch<=lastInch; inch++){ if (inch % 12 == 0) { - lengths[0] = 10; + lengths[0] = 12; majorLength = 16; digit = (int)(inch/12); fs = rulerFontSize; quote = '\''; } else if (d->scale <= 8) { - lengths[0] = 8; - majorLength = 13; + lengths[0] = 12; + majorLength = 16; digit = (int)(inch%12); fs = rulerFontSize*(2.0/3.0); quote = '"'; + } else if (d->scale <= 16){ + lengths[0] = 10; + majorLength = 12; + digit = (int)(inch%12); + fs = rulerFontSize*(1.0/2.0); } else { continue; } if (inch == lastInch) - lastFraction = (((int)((end - lastInch)*8)) / incr) * incr; + lastFraction = (((int)((end - lastInch)*16)) / incr) * incr; for ( fraction = firstFraction; fraction<=lastFraction; fraction += incr ) { - Translate( &p0, orig, a, inch+fraction/8.0 ); + Translate( &p0, orig, a, inch+fraction/16.0 ); Translate( &p1, p0, aa, lengths[fraction]*d->scale/72.0 ); d->CoOrd2Pix( d, p0, &x0, &y0 ); d->CoOrd2Pix( d, p1, &x1, &y1 ); @@ -1571,13 +1836,17 @@ EXPORT void DrawRuler( /* 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 ); + if (fraction == 0) { + if ( (number == TRUE && d->scale<40) || (digit==0)) { + if (inch % 12 == 0 || d->scale <= 2) { + Translate( &p0, p0, aa, majorLength*d->scale/mainD.dpi ); + Translate( &p0, p0, 225, fs*d->scale/mainD.dpi ); + sprintf(message, "%d%c", digit, quote ); + d->CoOrd2Pix( d, p0, &x0, &y0 ); + if (x0<0) x0 = 0; + if (y0<0) y0 = 0; + wDrawString( d->d, x0, y0, d->angle, message, rulerFp, fs, color, (wDrawOpts)d->funcs->options ); + } } } firstFraction = 0; @@ -1587,28 +1856,49 @@ EXPORT void DrawRuler( } -EXPORT void DrawTicks( drawCmd_p d, coOrd size ) +static 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; + double blank_zone = 40*d->scale/72.0; + + if ( d->orig.x<0.0-blank_zone ) { + p0.y = 0.0; p1.y = mapD.size.y; + p0.x = p1.x = 0.0; + DrawRuler( d, p0, p1, offset, FALSE, TRUE, borderColor ); + } + if (d->orig.x+d->size.x>mapD.size.x+blank_zone) { + p0.y = 0.0; p1.y = mapD.size.y; + p0.x = p1.x = mapD.size.x; + DrawRuler( d, p0, p1, offset, FALSE, FALSE, borderColor ); + } + p0.x = 0.0; p1.x = d->size.x; + offset = d->orig.x; + p0.y = p1.y = d->orig.y; DrawRuler( d, p0, p1, offset, TRUE, FALSE, borderColor ); - p0.y = p1.y = min(d->orig.y + d->size.y, size.y); + p0.y = p1.y = d->size.y+d->orig.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); + if ( d->orig.y<0.0-blank_zone) { + p0.x = 0.0; p1.x = mapD.size.x; + p0.y = p1.y = 0.0; + DrawRuler( d, p0, p1, offset, FALSE, FALSE, borderColor ); + } + if (d->orig.y+d->size.y>mapD.size.y+blank_zone) { + p0.x = 0.0; p1.x = mapD.size.x; + p0.y = p1.y = mapD.size.y; + DrawRuler( d, p0, p1, offset, FALSE, TRUE, borderColor ); + } + p0.y = 0.0; p1.y = d->size.y; + offset = d->orig.y; 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); + p0.x = p1.x = d->size.x+d->orig.x; DrawRuler( d, p0, p1, offset, FALSE, FALSE, borderColor ); } @@ -1621,54 +1911,74 @@ EXPORT void DrawTicks( drawCmd_p d, coOrd size ) EXPORT coOrd mainCenter; -EXPORT void DrawMapBoundingBox( BOOL_T set ) +static void DrawMapBoundingBox( BOOL_T set ) { if (mainD.d == NULL || mapD.d == NULL) return; - DrawHilight( &mapD, mainD.orig, mainD.size ); + wDrawSetTempMode( mapD.d, TRUE ); + DrawHilight( &mapD, mainD.orig, mainD.size, TRUE ); + wDrawSetTempMode( mapD.d, FALSE ); } -static void ConstraintOrig( coOrd * orig, coOrd size ) +static void ConstraintOrig( coOrd * orig, coOrd size, wBool_t bNoBorder, wBool_t round ) { 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)); + coOrd bound = zero; + + if ( !bNoBorder ) { + bound.x = size.x/2; + bound.y = size.y/2; } - 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->x > 0.0) { + if ((orig->x+size.x) > (mapD.size.x+bound.x)) { + orig->x = mapD.size.x-size.x+bound.x; + //orig->x += (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*4)/4; //>1:1 = 1/4 inch - orig->y = floor(orig->y*4)/4; - } else { - orig->x = floor(orig->x*2.54*2)/(2.54*2); //>1:1 = 0.5 cm - orig->y = floor(orig->y*2.54*2)/(2.54*2); + if (orig->x < (0-bound.x)) + orig->x = 0-bound.x; + + if (orig->y > 0.0) { + if ((orig->y+size.y) > (mapD.size.y+bound.y) ) { + orig->y = mapD.size.y-size.y+bound.y; + //orig->y += (units==UNITS_ENGLISH?1.0:1.0/2.54); + } - } else { - if (units == UNITS_ENGLISH) { - orig->x = floor(orig->x*64)/64; //<1:1 = 1/64 inch - orig->y = floor(orig->y*64)/64; + } + + if (orig->y < (0-bound.y)) + orig->y = 0-bound.y; + + if (round) { + if (mainD.scale >= 1.0) { + if (units == UNITS_ENGLISH) { + orig->x = floor(orig->x*4)/4; //>1:1 = 1/4 inch + orig->y = floor(orig->y*4)/4; + } else { + orig->x = floor(orig->x*2.54*2)/(2.54*2); //>1:1 = 0.5 cm + orig->y = floor(orig->y*2.54*2)/(2.54*2); + } } else { - orig->x = floor(orig->x*25.4*2)/(25.4*2); //>1:1 = 0.5 mm - orig->y = floor(orig->y*25.4*2)/(25.4*2); + if (units == UNITS_ENGLISH) { + orig->x = floor(orig->x*64)/64; //<1:1 = 1/64 inch + orig->y = floor(orig->y*64)/64; + } else { + orig->x = floor(orig->x*25.4*2)/(25.4*2); //>1:1 = 0.5 mm + orig->y = floor(orig->y*25.4*2)/(25.4*2); + } } } - 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 ) ) + //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 ) ) } /** @@ -1676,18 +1986,29 @@ LOG( log_pan, 2, ( " = [ %0.3f %0.3f ]\n", orig->y, orig->y ) ) * * \param IN zoomM Menu to which radio button is added * \param IN zoomSubM Second menu to which radio button is added, ignored if NULL + * \param IN ctxMenu1 + * \param IN ctxMenu2 * */ -EXPORT void InitCmdZoom( wMenu_p zoomM, wMenu_p zoomSubM ) +EXPORT void InitCmdZoom( wMenu_p zoomM, wMenu_p zoomSubM, wMenu_p ctxMenu1, wMenu_p panMenu ) { 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( (zoomList[ inx ].value >= 1.0 && zoomList[ inx ].value<=10 ) || + (ceil(log2(zoomList[ inx ].value)) == floor(log2(zoomList[ inx ].value)))) + { + if (zoomM) + 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))); + if (panMenu) + zoomList[inx].panRadio = wMenuRadioCreate( panMenu, "cmdZoom", zoomList[inx].name, 0, (wMenuCallBack_p)DoZoom, (void *)(&(zoomList[inx].value))); + } + if ((zoomList[inx].value >=1.0 && zoomList[inx].value <= 10.0) || (ceil(log2(zoomList[ inx ].value)) == floor(log2(zoomList[ inx ].value)))) { + if (ctxMenu1) + zoomList[inx].ctxRadio1 = wMenuRadioCreate( ctxMenu1, "cmdZoom", zoomList[inx].name, 0, (wMenuCallBack_p)DoZoom, (void *)(&(zoomList[inx].value))); } } } @@ -1706,11 +2027,14 @@ static void SetZoomRadio( DIST_T scale ) for ( inx=0; inx<sizeof zoomList/sizeof zoomList[0]; inx++ ) { if( curScale == zoomList[inx].value ) { - - wMenuRadioSetActive( zoomList[inx].btRadio ); + if (zoomList[inx].btRadio) + wMenuRadioSetActive( zoomList[inx].btRadio ); if( zoomList[inx].pdRadio ) wMenuRadioSetActive( zoomList[inx].pdRadio ); - + if (zoomList[inx].ctxRadio1) + wMenuRadioSetActive( zoomList[inx].ctxRadio1 ); + if (zoomList[inx].panRadio) + wMenuRadioSetActive( zoomList[inx].panRadio ); /* activate / deactivate zoom buttons when appropriate */ wControlLinkedActive( (wControl_p)zoomUpB, ( inx != 0 ) ); wControlLinkedActive( (wControl_p)zoomDownB, ( inx < (sizeof zoomList/sizeof zoomList[0] - 1))); @@ -1768,7 +2092,6 @@ static void DoNewScale( DIST_T scale ) if (scale > MAX_MAIN_SCALE) scale = MAX_MAIN_SCALE; - DrawHilight( &mapD, mainD.orig, mainD.size ); tempD.scale = mainD.scale = scale; mainD.dpi = wDrawGetDPI( mainD.d ); if ( mainD.dpi == 75 ) { @@ -1781,21 +2104,10 @@ static void DoNewScale( DIST_T scale ) SetZoomRadio( scale ); InfoScale(); SetMainSize(); - if (zoomCorner) { - mainCenter.x = mainD.orig.x + mainD.size.x/2.0; - mainCenter.y = mainD.orig.y + mainD.size.y/2.0; - } else { - 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; + PanHere( (void*)1 ); 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 ); @@ -1814,6 +2126,7 @@ EXPORT void DoZoomUp( void * mode ) long newScale; int i; + LOG( log_zoom, 2, ( "DoZoomUp KS:%x\n", MyGetKeyState() ) ); if ( mode != NULL || (MyGetKeyState()&WKEY_SHIFT) == 0) { i = ScaleInx( mainD.scale ); if (i < 0) i = NearestScaleInx(mainD.scale, TRUE); @@ -1826,12 +2139,12 @@ EXPORT void DoZoomUp( void * mode ) if (mainD.scale <=1.0) InfoMessage(_("Macro Zoom Mode")); else - InfoMessage(_("Use Shift+PageDwn to jump to preset Zoom In")); + InfoMessage(""); DoNewScale( zoomList[ i - 1 ].value ); - } else InfoMessage("Min Macro Zoom"); + } else InfoMessage("Minimum Macro Zoom"); } else { - InfoMessage(_("Scale 1:1 - Use Ctrl+PageDwn to go to Macro Zoom Mode")); + InfoMessage(_("Scale 1:1 - Use Ctrl+ to go to Macro Zoom Mode")); } } else if ( (MyGetKeyState()&WKEY_CTRL) == 0 ) { wPrefGetInteger( "misc", "zoomin", &newScale, 4 ); @@ -1843,6 +2156,22 @@ EXPORT void DoZoomUp( void * mode ) } } +EXPORT void DoZoomExtents( void * mode) { + + DIST_T scale_x, scale_y; + scale_x = mapD.size.x/(mainD.size.x/mainD.scale); + scale_y = mapD.size.y/(mainD.size.y/mainD.scale); + if (scale_x<scale_y) + scale_x = scale_y; + scale_x = ceil(scale_x); + if (scale_x < 1) scale_x = 1; + if (scale_x > MAX_MAIN_SCALE) scale_x = MAX_MAIN_SCALE; + mainD.orig = zero; + DoNewScale(scale_x); + MainLayout(TRUE,TRUE); + +} + /** * User selected zoom out, via mouse wheel, button or pulldown. @@ -1855,11 +2184,12 @@ EXPORT void DoZoomDown( void * mode) long newScale; int i; + LOG( log_zoom, 2, ( "DoZoomDown KS:%x\n", MyGetKeyState() ) ); if ( mode != NULL || (MyGetKeyState()&WKEY_SHIFT) == 0 ) { i = ScaleInx( mainD.scale ); if (i < 0) i = NearestScaleInx(mainD.scale, TRUE); if( i>= 0 && i < ( sizeof zoomList/sizeof zoomList[0] - 1 )) { - InfoMessage(_("Use Shift+PageUp to jump to preset Zoom Out")); + InfoMessage(""); DoNewScale( zoomList[ i + 1 ].value ); } else InfoMessage(_("At Maximum Zoom Out")); @@ -1913,11 +2243,102 @@ EXPORT void CoOrd2Pix( *y = (wPos_t)((pos.y-d->orig.y)/d->scale*d->dpi); } +/* +* Position mainD based on panCenter +* \param mode control effect of constrainMain and CTRL key +* +* mode: +* - 0: constrain if constrainMain==1 or CTRL is pressed +* - 1: same as 0, but ignore CTRL +* - 2: same as 0, plus liveMap +* - 3: Take position from menuPos +*/ +EXPORT void PanHere(void * mode) { + if ( 3 == (long)mode) { + panCenter = menuPos; + LOG( log_pan, 2, ( "MCenter:Mod-%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + } + mainD.orig.x = panCenter.x - mainD.size.x/2.0; + mainD.orig.y = panCenter.y - mainD.size.y/2.0; + wBool_t bNoBorder = (constrainMain != 0); + if ( 1 != (long)mode ) + if ( (MyGetKeyState()&WKEY_CTRL)!= 0 ) + bNoBorder = !bNoBorder; + wBool_t bLiveMap = TRUE; + if ( 2 == (long)mode ) + bLiveMap = liveMap; + + MainLayout( bLiveMap, bNoBorder ); // PanHere +} + + +static void DoPanKeyAction( wAction_t action ) +{ + 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: + panCenter.x = mainD.orig.x + mainD.size.x/2; + panCenter.y = mainD.orig.y + mainD.size.y/2; + if ((MyGetKeyState() & WKEY_SHIFT) != 0) + panCenter.x += 0.25*mainD.scale; //~1cm in 1::1, 1ft in 30:1, 1mm in 10:1 + else + panCenter.x += mainD.size.x/2; + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)0); + break; + + case wAccelKey_Left: + panCenter.x = mainD.orig.x + mainD.size.x/2; + panCenter.y = mainD.orig.y + mainD.size.y/2; + if ((MyGetKeyState() & WKEY_SHIFT) != 0) + panCenter.x -= 0.25*mainD.scale; + else + panCenter.x -= mainD.size.x/2; + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)0); + break; + + case wAccelKey_Up: + panCenter.x = mainD.orig.x + mainD.size.x/2; + panCenter.y = mainD.orig.y + mainD.size.y/2; + if ((MyGetKeyState() & WKEY_SHIFT) != 0) + panCenter.y += 0.25*mainD.scale; + else + panCenter.y += mainD.size.y/2; + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)0); + break; + + case wAccelKey_Down: + panCenter.x = mainD.orig.x + mainD.size.x/2; + panCenter.y = mainD.orig.y + mainD.size.y/2; + if ((MyGetKeyState() & WKEY_SHIFT) != 0) + panCenter.y -= 0.25*mainD.scale; + else + panCenter.y -= mainD.size.y/2; + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)0); + break; + + default: + return; + } +} + 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; @@ -1930,34 +2351,21 @@ static void DoMapPan( wAction_t action, coOrd pos ) 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 ); +// mainD.orig.x = pos.x - mainD.size.x/2.0; +// mainD.orig.y = pos.y - mainD.size.y/2.0; + panCenter = pos; +LOG( log_pan, 1, ( "%s = [ %0.3f, %0.3f ]\n", action == C_DOWN? "START":"MOVE", mainD.orig.x, mainD.orig.y ) ) + PanHere( (void*)2 ); 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 ) ) + panCenter = pos; + PanHere( (void*)0 ); +LOG( log_pan, 1, ( "FINAL = [ %0.3f, %0.3f ]\n", mainD.orig.x, mainD.orig.y ) ) mode = noPan; break; @@ -1966,21 +2374,17 @@ LOG( log_pan, 1, ( "FINAL = [ %0.3f, %0.3f ]\n", pos.x, pos.y ) ) mode = resizePan; else break; - DrawHilight( &mapD, mainD.orig, mainD.size ); - newOrig = pos; - oldOrig = newOrig; - xscale = 1; - size.x = mainD.size.x/mainD.scale; + mapOrig = pos; + size.x = mainD.size.x/mainD.scale; //How big screen? size.y = mainD.size.y/mainD.scale; - newOrig.x -= size.x/2.0; - newOrig.y -= size.y/2.0; - DrawHilight( &mapD, newOrig, size ); + xscale = mainD.scale; //start at current + panCenter = pos; +LOG( log_pan, 1, ( "START %0.3fx%0.3f %0.3f+%0.3f\n", mapOrig.x, mapOrig.y, size.x, size.y ) ) 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) @@ -1989,103 +2393,42 @@ LOG( log_pan, 1, ( "FINAL = [ %0.3f, %0.3f ]\n", pos.x, pos.y ) ) 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); + + xscale = fabs((pos.x-mapOrig.x)*2.0/size.x); + yscale = fabs((pos.y-mapOrig.y)*2.0/size.y); if (xscale < yscale) xscale = yscale; xscale = ceil( xscale ); - if (xscale < 1) - xscale = 1; + + if (xscale < 0.01) + xscale = 0.01; 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; + + mainD.size.x = size.x * xscale; + mainD.size.y = size.y * xscale; + mainD.orig.x = mapOrig.x - mainD.size.x / 2.0; + mainD.orig.y = mapOrig.y - mainD.size.y / 2.0; + tempD.scale = mainD.scale = xscale; + PanHere( (void*)2 ); +LOG( log_pan, 1, ( "MOVE SCL:%0.3f %0.3fx%0.3f %0.3f+%0.3f\n", xscale, mainD.orig.x, mainD.orig.y, mainD.size.x, mainD.size.y ) ) + InfoScale(); + 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(); - MapRedraw(); - 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(); - MapRedraw(); - //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(); - MapRedraw(); - //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(); - MapRedraw(); - //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(); - MapRedraw(); - //DrawHilight( &mapD, mainD.orig, mainD.size ); - break; - default: - return; - } - mainD.Pix2CoOrd( &mainD, x, y, &pos ); - InfoPos( pos ); - return; + case wActionExtKey: + mainD.CoOrd2Pix(&mainD,pos,&x,&y); + DoPanKeyAction( action ); + mainD.Pix2CoOrd( &mainD, x, y, &pos ); + InfoPos( pos ); + return; + default: return; } @@ -2106,7 +2449,7 @@ EXPORT BOOL_T IsClose( * */ -static int ignoreMoves = 1; +static int ignoreMoves = 0; EXPORT void ResetMouseState( void ) { @@ -2136,6 +2479,31 @@ GetMousePosition( int *x, int *y ) } } +coOrd minIncrementSizes() { + double x,y; + if (mainD.scale >= 1.0) { + if (units == UNITS_ENGLISH) { + x = 0.25; //>1:1 = 1/4 inch + y = 0.25; + } else { + x = 1/(2.54*2); //>1:1 = 0.5 cm + y = 1/(2.54*2); + } + } else { + if (units == UNITS_ENGLISH) { + x = 1/64; //<1:1 = 1/64 inch + y = 1/64; + } else { + x = 1/(25.4*2); //>1:1 = 0.5 mm + y = 1/(25.4*2); + } + } + coOrd ret; + ret.x = x*1.5; + ret.y = y*1.5; + return ret; +} + static void DoMouse( wAction_t action, coOrd pos ) { @@ -2149,6 +2517,8 @@ static void DoMouse( wAction_t action, coOrd pos ) RecordMouse( "MOUSE", action, pos.x, pos.y ); } + + switch (action&0xFF) { case C_UP: if (mouseState != mouseLeft) @@ -2196,6 +2566,12 @@ static void DoMouse( wAction_t action, coOrd pos ) if ( deferSubstituteControls[0] ) InfoSubstituteControls( deferSubstituteControls, deferSubstituteLabels ); +// panCenter.y = mainD.orig.y + mainD.size.y/2; +// panCenter.x = mainD.orig.x + mainD.size.x/2; +//printf( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ); + + coOrd min = minIncrementSizes(); + switch ( action&0xFF ) { case C_DOWN: case C_RDOWN: @@ -2209,80 +2585,17 @@ static void DoMouse( wAction_t action, coOrd pos ) case wActionExtKey: mainD.CoOrd2Pix(&mainD,pos,&x,&y); if ((MyGetKeyState() & - (WKEY_SHIFT | WKEY_CTRL)) == (WKEY_SHIFT | WKEY_CTRL)) break; //Allow SHIFT+CTRL for Move + (WKEY_SHIFT | WKEY_CTRL)) == (WKEY_SHIFT | WKEY_CTRL) && + ( action>>8 == wAccelKey_Up || + action>>8 == wAccelKey_Down || + action>>8 == wAccelKey_Left || + action>>8 == wAccelKey_Right )) + break; //Allow SHIFT+CTRL for Move if (((action>>8)&0xFF) == wAccelKey_LineFeed) { action = C_TEXT+((int)(0x0A<<8)); break; } - 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 ); - if ((MyGetKeyState() & WKEY_SHIFT) != 0) - mainD.orig.x += 0.25*mainD.scale; //~1cm in 1::1, 1ft in 30:1, 1mm in 10:1 - else - 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(); - MapRedraw(); - //DrawHilight( &mapD, mainD.orig, mainD.size ); - break; - case wAccelKey_Left: - //DrawHilight( &mapD, mainD.orig, mainD.size ); - if ((MyGetKeyState() & WKEY_SHIFT) != 0) - mainD.orig.x -= 0.25*mainD.scale; - else - 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(); - MapRedraw(); - //DrawHilight( &mapD, mainD.orig, mainD.size ); - break; - case wAccelKey_Up: - //DrawHilight( &mapD, mainD.orig, mainD.size ); - if ((MyGetKeyState() & WKEY_SHIFT) != 0) - mainD.orig.y += 0.25*mainD.scale; - else - 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(); - MapRedraw(); - //DrawHilight( &mapD, mainD.orig, mainD.size ); - break; - case wAccelKey_Down: - //DrawHilight( &mapD, mainD.orig, mainD.size ); - if ((MyGetKeyState() & WKEY_SHIFT) != 0) - mainD.orig.y -= 0.25*mainD.scale; - else - 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(); - MapRedraw(); - //DrawHilight( &mapD, mainD.orig, mainD.size ); - break; - default: - return; - } - mainD.Pix2CoOrd( &mainD, x, y, &pos ); - InfoPos( pos ); + DoPanKeyAction( action ); return; case C_TEXT: if ((action>>8) == 0x0D) { @@ -2291,10 +2604,12 @@ static void DoMouse( wAction_t action, coOrd pos ) ConfirmReset( TRUE ); return; } + case C_MODKEY: case C_MOVE: case C_UP: case C_RMOVE: case C_RUP: + case C_LDOUBLE: InfoPos( pos ); /*DrawTempTrack();*/ break; @@ -2304,6 +2619,26 @@ static void DoMouse( wAction_t action, coOrd pos ) case C_WDOWN: DoZoomDown((void *)1L); break; + case C_SCROLLUP: + panCenter.y = panCenter.y + ((mainD.size.y/20>min.y)?mainD.size.y/20:min.y); + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)1); + break; + case C_SCROLLDOWN: + panCenter.y = panCenter.y - ((mainD.size.y/20>min.y)?mainD.size.y/20:min.y); + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)1); + break; + case C_SCROLLLEFT: + panCenter.x = panCenter.x - ((mainD.size.x/20>min.x)?mainD.size.x/20:min.x); + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)1); + break; + case C_SCROLLRIGHT: + panCenter.x = panCenter.x + ((mainD.size.x/20>min.x)?mainD.size.x/20:min.x); + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere((void*)1); + break; default: NoticeMessage( MSG_DOMOUSE_BAD_OP, _("Ok"), NULL, action&0xFF ); break; @@ -2382,16 +2717,13 @@ static void DoMousew( wDraw_p d, void * context, wAction_t action, wPos_t x, wPo } } } - ConstraintOrig( &orig, mainD.size ); - if ( orig.x != mainD.orig.x || orig.y != mainD.orig.y ) { - //DrawMapBoundingBox( FALSE ); - mainD.orig = orig; - MainRedraw(); - MapRedraw(); - /*DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );*/ - //DrawMapBoundingBox( TRUE ); - wFlush(); - } + mainD.orig = orig; + panCenter.x = mainD.orig.x + mainD.size.x/2.0; + panCenter.y = mainD.orig.y + mainD.size.y/2.0; + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + MainLayout( TRUE, TRUE ); // DoMouseW: autopan + tempD.orig = mainD.orig; + wFlush(); } } lastX = x; @@ -2399,8 +2731,21 @@ static void DoMousew( wDraw_p d, void * context, wAction_t action, wPos_t x, wPo } } mainD.Pix2CoOrd( &mainD, x, y, &pos ); - mousePositionx = x; - mousePositiony = y; + switch (action & 0xFF) { + case wActionMove: + case wActionLDown: + case wActionLDrag: + case wActionLUp: + case wActionRDown: + case wActionRDrag: + case wActionRUp: + case wActionLDownDouble: + mousePositionx = x; + mousePositiony = y; + break; + default: + break; + } DoMouse( action, pos ); } @@ -2413,6 +2758,7 @@ static wBool_t PlaybackMain( char * line ) char *oldLocale = NULL; oldLocale = SaveLocale("C"); + rc=sscanf( line, "%d " SCANF_FLOAT_FORMAT SCANF_FLOAT_FORMAT, &action, &pos.x, &pos.y); RestoreLocale(oldLocale); @@ -2423,6 +2769,27 @@ static wBool_t PlaybackMain( char * line ) } return TRUE; } + +static wBool_t PlaybackKey( char * line ) +{ + int rc; + int action = C_TEXT; + coOrd pos; + char *oldLocale = NULL; + int c; + + oldLocale = SaveLocale("C"); + rc=sscanf( line, "%d " SCANF_FLOAT_FORMAT SCANF_FLOAT_FORMAT, &c, &pos.x, &pos.y ); + RestoreLocale(oldLocale); + + if (rc != 3) { + SyntaxError( "MOUSE", rc, 3 ); + } else { + action = action||c<<8; + PlaybackMouse( DoMouse, &mainD, (wAction_t)action, pos, wDrawColorBlack ); + } + return TRUE; +} /***************************************************************************** * @@ -2440,8 +2807,40 @@ static void MapDlgUpdate( int inx, void * valueP ) { - if ( inx == -1 ) { - MapWindowShow( FALSE ); + int width,height; + switch(inx) { + case wResize_e: + if (mapD.d == NULL) + return; + wWinGetSize( mapW, &width, &height ); + if (height >= 100) { + wControlSetPos( (wControl_p)mapD.d, 0, 0 ); + double scaleX = (mapD.size.x/((width-DlgSepLeft-DlgSepRight-10)/mapD.dpi)); + double scaleY = (mapD.size.y/((height-DlgSepTop-DlgSepBottom-10)/mapD.dpi)); + double scale; + + if (scaleX<scaleY) scale = scaleX; + else scale = scaleY; + + if (scale > 256.0) scale = 256.0; + if (scale < 0.01) scale = 0.01; + + mapScale = (long)scale; + + mapD.scale = mapScale; + ChangeMapScale(FALSE); + + if (mapVisible) { + MapRedraw(); + } + wPrefSetInteger( "draw", "mapscale", (long)mapD.scale ); + } + break; + case -1: + MapWindowShow( FALSE ); + break; + default: + break; } } @@ -2449,8 +2848,7 @@ static void MapDlgUpdate( static void DrawChange( long changes ) { if (changes & CHANGE_MAIN) { - MainRedraw(); - MapRedraw(); + MainLayout( TRUE, FALSE ); // DrawChange: CHANGE_MAIN } if (changes &CHANGE_UNITS) SetInfoBar(); @@ -2459,6 +2857,13 @@ static void DrawChange( long changes ) } +static void MainLayoutCB( + wDraw_p bd, void * pContex, wPos_t px, wPos_t py ) +{ + MainLayout( TRUE, FALSE ); +} + + EXPORT void DrawInit( int initialZoom ) { wPos_t w, h; @@ -2469,9 +2874,9 @@ EXPORT void DrawInit( int initialZoom ) h = h - (toolbarHeight+max(textHeight,infoHeight)+10); if ( w <= 0 ) w = 1; if ( h <= 0 ) h = 1; - tempD.d = mainD.d = wDrawCreate( mainW, 0, toolbarHeight, "", BD_TICKS, + tempD.d = mainD.d = wDrawCreate( mainW, 0, toolbarHeight, "", BD_TICKS|BD_MODKEYS, w, h, &mainD, - (wDrawRedrawCallBack_p)MainRedraw, DoMousew ); + (wDrawRedrawCallBack_p)MainLayoutCB, DoMousew ); if (initialZoom == 0) { WDOUBLE_T tmpR; @@ -2497,17 +2902,21 @@ EXPORT void DrawInit( int initialZoom ) tempD.dpi = mainD.dpi; SetMainSize(); + panCenter.x = mainD.size.x/2 +mainD.orig.x; + panCenter.y = mainD.size.y/2 +mainD.orig.y; 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(); + mapW = ParamCreateDialog( &mapPG, MakeWindowTitle(_("Map")), NULL, NULL, NULL, FALSE, NULL, F_RESIZE, MapDlgUpdate ); + ChangeMapScale(TRUE); log_pan = LogFindIndex( "pan" ); log_zoom = LogFindIndex( "zoom" ); log_mouse = LogFindIndex( "mouse" ); + log_redraw = LogFindIndex( "redraw" ); AddPlaybackProc( "MOUSE ", (playbackProc_p)PlaybackMain, NULL ); + AddPlaybackProc( "KEY ", (playbackProc_p)PlaybackKey, NULL ); rulerFp = wStandardFont( F_HELV, FALSE, FALSE ); @@ -2524,6 +2933,8 @@ EXPORT void DrawInit( int initialZoom ) #include "bitmaps/pan.xpm" +EXPORT static wMenu_p panPopupM; + static STATUS_T CmdPan( wAction_t action, coOrd pos ) @@ -2544,19 +2955,23 @@ static STATUS_T CmdPan( switch (action&0xFF) { case C_START: start_pos = zero; - panmode = NONE; InfoMessage(_("Left Drag to Pan, Right Drag to Zoom, 0 to set Origin to 0,0, 1-9 to Zoom#, e to set to Extent")); + panmode = NONE; + InfoMessage(_("Left-Drag to pan, Ctrl+Left-Drag to zoom, 0 to set origin to zero, 1-9 to zoom#, e to set to extents")); + wSetCursor(mainD.d,wCursorSizeAll); break; case C_DOWN: - panmode = PAN; - start_pos = pos; - InfoMessage(_("Pan Mode - drag point to new position")); - break; - case C_RDOWN: - panmode = ZOOM; - start_pos = pos; - base = pos; - size = zero; - InfoMessage(_("Zoom Mode - drag Area to Zoom")); + if ((MyGetKeyState()&WKEY_CTRL) == 0) { + panmode = PAN; + start_pos = pos; + InfoMessage(_("Pan Mode - drag point to new position")); + break; + } else { + panmode = ZOOM; + start_pos = pos; + base = pos; + size = zero; + InfoMessage(_("Zoom Mode - drag area to zoom")); + } break; case C_MOVE: if (panmode == PAN) { @@ -2575,20 +2990,18 @@ static STATUS_T CmdPan( } } if ((fabs(pos.x-start_pos.x) > min_inc) || (fabs(pos.y-start_pos.y) > min_inc)) { - DrawMapBoundingBox( TRUE ); + coOrd oldOrig = mainD.orig; mainD.orig.x -= (pos.x - start_pos.x); mainD.orig.y -= (pos.y - start_pos.y); - ConstraintOrig( &mainD.orig, mainD.size ); - tempD.orig = mainD.orig; - mainCenter.x = mainD.orig.x + mainD.size.x/2.0; - mainCenter.y = mainD.orig.y + mainD.size.y/2.0; - DrawMapBoundingBox( TRUE ); + if ((MyGetKeyState()&WKEY_SHIFT) != 0) + ConstraintOrig(&mainD.orig,mainD.size,TRUE,FALSE); + if ((oldOrig.x == mainD.orig.x) && (oldOrig.y == mainD.orig.y)) + InfoMessage(_("Can't move any further in that direction")); + else + InfoMessage(_("Left click to pan, right click to zoom, 'o' for origin, 'e' for extents")); } } - MainRedraw(); - break; - case C_RMOVE: - if (panmode == ZOOM) { + else if (panmode == ZOOM) { base = start_pos; size.x = pos.x - base.x; if (size.x < 0) { @@ -2601,90 +3014,125 @@ static STATUS_T CmdPan( base.y = pos.y; } } - MainRedraw(); + panCenter.x = mainD.orig.x + mainD.size.x/2.0; + panCenter.y = mainD.orig.y + mainD.size.y/2.0; + PanHere( (void*)0 ); break; - case C_RUP: + case C_UP: + if (panmode == ZOOM) { + scale_x = size.x/mainD.size.x*mainD.scale; + scale_y = size.y/mainD.size.y*mainD.scale; - scale_x = size.x/mainD.size.x*mainD.scale; - scale_y = size.y/mainD.size.y*mainD.scale; + DIST_T oldScale = mainD.scale; - if (scale_x<scale_y) - scale_x = scale_y; - if (scale_x>1) scale_x = ceil( scale_x ); - else scale_x = 1/(ceil(1/scale_x)); + if (scale_x<scale_y) + scale_x = scale_y; + if (scale_x>1) scale_x = ceil( scale_x ); + else scale_x = 1/(ceil(1/scale_x)); - if (scale_x > MAX_MAIN_SCALE) scale_x = MAX_MAIN_SCALE; - if (scale_x < MIN_MAIN_MACRO) scale_x = MIN_MAIN_MACRO; + if (scale_x > MAX_MAIN_SCALE) scale_x = MAX_MAIN_SCALE; + if (scale_x < MIN_MAIN_MACRO) scale_x = MIN_MAIN_MACRO; - mainCenter.x = base.x + size.x/2.0; //Put center for scale in center of square - mainCenter.y = base.y + size.y/2.0; - mainD.orig.x = base.x; - mainD.orig.y = base.y; + mainD.orig.x = base.x; + mainD.orig.y = base.y; - panmode = NONE; - DoNewScale(scale_x); - MapRedraw(); - break; - case C_UP: - panmode = NONE; + panmode = NONE; + InfoMessage(_("Left Drag to Pan, +CTRL to Zoom, 0 to set Origin to 0,0, 1-9 to Zoom#, e to set to Extent")); + DoNewScale(scale_x); + break; + } else if (panmode == PAN) { + panCenter.x = mainD.orig.x + mainD.size.x/2.0; + panCenter.y = mainD.orig.y + mainD.size.y/2.0; + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + panmode = NONE; + } break; case C_REDRAW: if (panmode == ZOOM) { if (base.x && base.y && size.x && size.y) - DrawHilight( &mainD, base, size ); + DrawHilight( &tempD, base, size, TRUE ); } break; case C_CANCEL: base = zero; + wSetCursor(mainD.d,defaultCursor); return C_TERMINATE; case C_TEXT: panmode = NONE; - if ((action>>8) == 0x65) { //"e" - scale_x = mapD.size.x/(mainD.size.x/mainD.scale); - scale_y = mapD.size.y/(mainD.size.y/mainD.scale); - if (scale_x<scale_y) - scale_x = scale_y; - scale_x = ceil(scale_x); - if (scale_x < 1) scale_x = 1; - if (scale_x > MAX_MAIN_SCALE) scale_x = MAX_MAIN_SCALE; - mainD.orig = zero; - mainCenter.x = mainD.orig.x + mapD.size.x/2.0; - mainCenter.y = mainD.orig.y + mapD.size.y/2.0; - DoNewScale(scale_x); - 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; - MapRedraw(); - MainRedraw(); - } - if ((action>>8) == 0x30) { //"0" + + if ((action>>8) == 'e') { //"e" + DoZoomExtents(0); + } else if (((action>>8) == '0') || ((action>>8) == 'o')) { //"0" or "o" mainD.orig = zero; - 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; - MapRedraw(); - MainRedraw(); - } - if ((action>>8) >= 0x31 && (action>>8) <= 0x39) { //"1" to "9" + panCenter.x = mainD.size.x/2.0; + panCenter.y = mainD.size.y/2.0; + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + MainLayout( TRUE, TRUE ); // CmdPan C_TEXT '0' 'o' + } else if ((action>>8) >= '1' && (action>>8) <= '9') { //"1" to "9" scale_x = (action>>8)&0x0F; DoNewScale(scale_x); - MapRedraw(); - MainRedraw(); + } else if ((action>>8) == 'c') { // "c" + panCenter = pos; + LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); + PanHere( (void*)0 ); // CmdPan C_TEXT 'c' } + if ((action>>8) == 0x0D) { + wSetCursor(mainD.d,defaultCursor); return C_TERMINATE; - } - else if ((action>>8) == 0x1B) { + } else if ((action>>8) == 0x1B) { + wSetCursor(mainD.d,defaultCursor); return C_TERMINATE; } break; + case C_CMDMENU: + menuPos = pos; + wMenuPopupShow( panPopupM ); + return C_CONTINUE; + break; } return C_CONTINUE; } +static wMenuPush_p zoomExtents,panOrig,panHere; +static wMenuPush_p zoomLvl1,zoomLvl2,zoomLvl3,zoomLvl4,zoomLvl5,zoomLvl6,zoomLvl7,zoomLvl8,zoomLvl9; + +EXPORT void PanMenuEnter(int key) { + int action; + action = C_TEXT; + action |= key<<8; + CmdPan(action,zero); +} + +extern wIndex_t selectCmdInx; +extern wIndex_t describeCmdInx; +extern wIndex_t joinCmdInx; +extern wIndex_t modifyCmdInx; EXPORT void InitCmdPan( wMenu_p menu ) { panCmdInx = AddMenuButton( menu, CmdPan, "cmdPan", _("Pan/Zoom"), wIconCreatePixMap(pan_xpm), - LEVEL0, IC_CANCEL|IC_POPUP|IC_LCLICK|IC_RCLICK|IC_CMDMENU, ACCL_PAN, NULL ); + LEVEL0, IC_CANCEL|IC_POPUP|IC_LCLICK|IC_CMDMENU, ACCL_PAN, NULL ); +} +EXPORT void InitCmdPan2( wMenu_p menu ) +{ + panPopupM = MenuRegister( "Pan Options" ); + wMenuPushCreate(panPopupM, "cmdSelectMode", GetBalloonHelpStr("cmdSelectMode"), 0, DoCommandB, (void*) (intptr_t) selectCmdInx); + wMenuPushCreate(panPopupM, "cmdDescribeMode", GetBalloonHelpStr("cmdDescribeMode"), 0, DoCommandB, (void*) (intptr_t) describeCmdInx); + wMenuPushCreate(panPopupM, "cmdModifyMode", GetBalloonHelpStr("cmdModifyMode"), 0, DoCommandB, (void*) (intptr_t) modifyCmdInx); + wMenuSeparatorCreate(panPopupM); + zoomExtents = wMenuPushCreate( panPopupM, "", _("Zoom to extents - 'e'"), 0, (wMenuCallBack_p)PanMenuEnter, (void*) 'e'); + zoomLvl1 = wMenuPushCreate( panPopupM, "", _("Zoom to 1:1 - '1'"), 0, (wMenuCallBack_p)PanMenuEnter, (void*) '1'); + zoomLvl2 = wMenuPushCreate( panPopupM, "", _("Zoom to 1:2 - '2'"), 0, (wMenuCallBack_p)PanMenuEnter, (void*) '2'); + zoomLvl3 = wMenuPushCreate( panPopupM, "", _("Zoom to 1:3 - '3'"), 0, (wMenuCallBack_p)PanMenuEnter, (void*) '3'); + zoomLvl4 = wMenuPushCreate( panPopupM, "", _("Zoom to 1:4 - '4'"), 0, (wMenuCallBack_p)PanMenuEnter, (void*) '4'); + zoomLvl5 = wMenuPushCreate( panPopupM, "", _("Zoom to 1:5 - '5'"), 0, (wMenuCallBack_p)PanMenuEnter, (void*) '5'); + zoomLvl6 = wMenuPushCreate( panPopupM, "", _("Zoom to 1:6 - '6'"), 0, (wMenuCallBack_p)PanMenuEnter, (void*) '6'); + zoomLvl7 = wMenuPushCreate( panPopupM, "", _("Zoom to 1:7 - '7'"), 0, (wMenuCallBack_p)PanMenuEnter, (void*) '7'); + zoomLvl8 = wMenuPushCreate( panPopupM, "", _("Zoom to 1:8 - '8'"), 0, (wMenuCallBack_p)PanMenuEnter, (void*) '8'); + zoomLvl9 = wMenuPushCreate( panPopupM, "", _("Zoom to 1:9 - '9'"), 0, (wMenuCallBack_p)PanMenuEnter, (void*) '9'); + panOrig = wMenuPushCreate( panPopupM, "", _("Pan to Origin - 'o'/'0'"), 0, (wMenuCallBack_p)PanMenuEnter, (void*) 'o'); + wMenu_p zoomPanM = wMenuMenuCreate(panPopupM, "", _("&Zoom")); + InitCmdZoom(NULL, NULL, NULL, zoomPanM); + panHere = wMenuPushCreate( panPopupM, "", _("Pan center here - 'c'"), 0, (wMenuCallBack_p)PanHere, (void*) 3); } diff --git a/app/bin/draw.h b/app/bin/draw.h index 66a1571..dc01695 100644 --- a/app/bin/draw.h +++ b/app/bin/draw.h @@ -26,70 +26,89 @@ #include "common.h" #include "wlib.h" -#define DC_TICKS (1<<1) +// drawCmd_t.options +// +// SIMPLE: draw simplified objects. +// No endpts, descriptions, wide lines and arcs, ties, centerlines, special color +// Draw simple lines for: wide lines and arcs, dimlines, benchwork, tableedge, filled poly andcircle +#define DC_SIMPLE (1<<0) +// SEGTRACK: draw tracks as segments (SEG_*TRK) instead of lines and arcs +#define DC_SEGTRACK (1<<1) +// PRINT: we're printing #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) +// CENTERLINE: draw centerlines (for bitmaps) +#define DC_CENTERLINE (1<<4) +// TICKS: draw rulers on edges +#define DC_TICKS (1<<5) +// Line styles +#define DC_THICK (1<<7) +#define DC_DASH (1<<12) +#define DC_DOT (1<<13) +#define DC_DASHDOT (1<<14) +#define DC_DASHDOTDOT (1<<15) +#define DC_CENTER (1<<16) +#define DC_PHANTOM (1<<17) + +#define DC_NOTSOLIDLINE (DC_DASH|DC_DOT|DC_DASHDOT|DC_DASHDOTDOT|DC_CENTER|DC_PHANTOM) + +#define INIT_MAIN_SCALE (8.0) +#define INIT_MAP_SCALE (64.0) +#define MAX_MAIN_SCALE (256.0) +#define MIN_MAIN_SCALE (1.0) +#define MIN_MAIN_MACRO (0.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 * ); + 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 (*drawPoly)(drawCmd_p, int, coOrd *, int *, wDrawColor, wDrawWidth, int, + int); + 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; + wDraw_p d; + drawFuncs_t * funcs; + unsigned 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) + #define LBORDER (33) + #define BBORDER (32) #else -#define LBORDER (26) -#define BBORDER (27) + #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 * ); +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 BOOL_T magneticSnap; extern drawCmd_t mainD; extern coOrd mainCenter; extern drawCmd_t mapD; @@ -106,6 +125,11 @@ extern long drawCount; extern BOOL_T drawEnable; extern long currRedraw; +extern coOrd panCenter; +extern coOrd menuPos; + +extern int log_pan; + extern wDrawColor drawColorBlack; extern wDrawColor drawColorWhite; @@ -113,12 +137,39 @@ extern wDrawColor drawColorRed; extern wDrawColor drawColorBlue; extern wDrawColor drawColorGreen; extern wDrawColor drawColorAqua; +extern wDrawColor drawColorPowderedBlue; extern wDrawColor drawColorPurple; extern wDrawColor drawColorGold; +extern wDrawColor drawColorGrey10; +extern wDrawColor drawColorGrey20; +extern wDrawColor drawColorGrey30; +extern wDrawColor drawColorGrey40; +extern wDrawColor drawColorGrey50; +extern wDrawColor drawColorGrey60; +extern wDrawColor drawColorGrey70; +extern wDrawColor drawColorGrey80; +extern wDrawColor drawColorGrey90; +// Special colors +extern wDrawColor drawColorPreviewSelected; +extern wDrawColor drawColorPreviewUnselected; + #define wDrawColorBlack drawColorBlack #define wDrawColorWhite drawColorWhite #define wDrawColorBlue drawColorBlue +#define wDrawColorPowderedBlue drawColorPowderedBlue +#define wDrawColorAqua drawColorAqua #define wDrawColorRed drawColorRed +#define wDrawColorGrey10 drawColorGrey10 +#define wDrawColorGrey20 drawColorGrey20 +#define wDrawColorGrey30 drawColorGrey30 +#define wDrawColorGrey40 drawColorGrey40 +#define wDrawColorGrey50 drawColorGrey50 +#define wDrawColorGrey60 drawColorGrey60 +#define wDrawColorGrey70 drawColorGrey70 +#define wDrawColorGrey80 drawColorGrey80 +#define wDrawColorGrey90 drawColorGrey90 +#define wDrawColorPreviewSelected drawColorPreviewSelected +#define wDrawColorPreviewUnselected drawColorPreviewUnselected extern wDrawColor markerColor; extern wDrawColor borderColor; @@ -128,88 +179,97 @@ extern wDrawColor snapGridColor; extern wDrawColor selectedColor; extern wDrawColor profilePathColor; -BOOL_T IsClose( DIST_T ); +BOOL_T IsClose(DIST_T); -drawFuncs_t screenDrawFuncs; -drawFuncs_t tempDrawFuncs; -drawFuncs_t tempSegDrawFuncs; -drawFuncs_t printDrawFuncs; +extern drawFuncs_t screenDrawFuncs; +extern drawFuncs_t tempDrawFuncs; +extern drawFuncs_t tempSegDrawFuncs; +extern 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 DrawPoly( D, N, P, T, C, W, F, O ) (D)->funcs->drawPoly( D, N, P, T, C, W, F, O ); #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 ) \ - } + (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; \ - } + 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 ) + ( (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 ) + OFF_D( mainD.orig, mainD.size, LO, HI ) -void DrawHilight( drawCmd_p, coOrd, coOrd ); -void DrawHilightPolygon( drawCmd_p, coOrd *, int ); +void DrawHilight(drawCmd_p, coOrd, coOrd, BOOL_T add); +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 DrawMultiLineTextSize(drawCmd_p dp, char * text, wFont_p fp, wFontSize_t fs, BOOL_T relative, coOrd * size, coOrd * lastline ); -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 * ); -void DrawMultiString(drawCmd_p d, coOrd pos, char * text, wFont_p fp, wFontSize_t fs, wDrawColor color, ANGLE_T a, coOrd * lo, coOrd * hi); -BOOL_T SetRoomSize( coOrd ); -void GetRoomSize( coOrd * ); -void DoRedraw( void ); -void SetMainSize( void ); -void MainRedraw( void ); -void MapRedraw( 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 * ); -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 DrawBoxedString(int, drawCmd_p, coOrd, char *, wFont_p, wFontSize_t, + wDrawColor, ANGLE_T); +void DrawMultiLineTextSize(drawCmd_p dp, char * text, wFont_p fp, + wFontSize_t fs, BOOL_T relative, coOrd * size, coOrd * lastline); +void DrawTextSize2(drawCmd_p, char *, wFont_p, wFontSize_t, BOOL_T, coOrd *, + POS_T *, POS_T *); +void DrawTextSize(drawCmd_p, char *, wFont_p, wFontSize_t, BOOL_T, coOrd *); +void DrawMultiString(drawCmd_p d, coOrd pos, char * text, wFont_p fp, + wFontSize_t fs, wDrawColor color, ANGLE_T a, coOrd * lo, coOrd * hi, + BOOL_T boxed); +BOOL_T SetRoomSize(coOrd); +void GetRoomSize(coOrd *); +void DoRedraw(void); +void SetMainSize(void); +void MainRedraw(void); +void MainLayout(wBool_t, wBool_t); +void TempRedraw(void); +void DrawRuler(drawCmd_p, coOrd, coOrd, DIST_T, int, int, wDrawColor); +void MainProc(wWin_p, winProcEvent, void *, void *); +void InitInfoBar(void); +void DrawInit(int); +void DoZoomUp(void *); +void DoZoomDown(void *); +void DoZoomExtents( void *); +void DoZoom(DIST_T *); +void PanHere(void *); +void PanMenuEnter(int); + +void InitCmdZoom(wMenu_p, wMenu_p, wMenu_p, wMenu_p); + +void InfoPos(coOrd); +void InfoCount(wIndex_t); +void SetMessage(char *); wIndex_t panCmdInx; -void InfoSubstituteControls( wControl_p *, 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 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 ); +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 ); +void MovePlaybackCursor(drawCmd_p, wPos_t, wPos_t, wBool_t, wControl_p); +typedef void (*playbackProc)(wAction_t, coOrd); +void PlaybackMouse(playbackProc, drawCmd_p, wAction_t, coOrd, wDrawColor); +void RedrawPlaybackCursor(); #endif diff --git a/app/bin/drawgeom.c b/app/bin/drawgeom.c index 2342599..d23031f 100644 --- a/app/bin/drawgeom.c +++ b/app/bin/drawgeom.c @@ -37,46 +37,42 @@ 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 dynArr_t anchors_da; +static dynArr_t select_da; + +#define points(N) DYNARR_N( pts_t, points_da, N ) +#define point_selected(N) DYNARR_N( wBool_t, select_da, N) +#define anchors(N) DYNARR_N( trkSeg_t, anchors_da, N) -static void EndPoly( drawContext_t * context, int cnt ) +static void EndPoly( drawContext_t * context, int cnt, wBool_t open) { trkSeg_p segPtr; track_p trk; - long oldOptions; - coOrd * pts; + pts_t * 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 ) { + if ( cnt < 3 ) { tempSegs_da.cnt = 0; ErrorMessage( MSG_POLY_SHAPES_3_SIDES ); return; } - pts = (coOrd*)MyMalloc( (cnt+1) * sizeof (coOrd) ); - for ( inx=0; inx<cnt; inx++ ) - pts[inx] = tempSegs(inx).u.l.pos[0]; - pts[cnt] = tempSegs(cnt-1).u.l.pos[1]; + pts = (pts_t*)MyMalloc( (cnt) * sizeof (pts_t) ); + for ( inx=0; inx<cnt; inx++ ) { + pts[inx].pt = tempSegs(inx).u.l.pos[0]; + pts[inx].pt_type = wPolyLineStraight; + } 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->type = ( (context->Op == OP_POLY || context->Op == OP_POLYLINE )? SEG_POLY:SEG_FILPOLY ); + segPtr->u.p.cnt = cnt; segPtr->u.p.pts = pts; segPtr->u.p.angle = 0.0; segPtr->u.p.orig = zero; - segPtr->u.p.polyType = FREEFORM; + segPtr->u.p.polyType = open?POLYLINE:FREEFORM; UndoStart( _("Create Lines"), "newDraw" ); trk = MakeDrawFromSeg( zero, 0.0, segPtr ); DrawNewTrack( trk ); @@ -100,6 +96,80 @@ static void DrawGeomOk( void ) tempSegs_da.cnt = 0; } +static void CreateEndAnchor(coOrd p, wBool_t lock) { + DIST_T d = tempD.scale*0.15; + + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = lock?SEG_FILCRCL:SEG_CRVLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.c.center = p; + anchors(i).u.c.radius = d/2; + anchors(i).u.c.a0 = 0.0; + anchors(i).u.c.a1 = 360.0; + anchors(i).width = 0; +} + +static void CreateLineAnchor(coOrd p, coOrd p0) { + DIST_T d = tempD.scale*0.15; + coOrd p1; + ANGLE_T a = FindAngle(p0,p); + Translate(&p1,p,a,d*5); + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).color = wDrawColorBlue; + anchors(i).u.l.pos[0] = p; + anchors(i).u.l.pos[1] = p0; + anchors(i).width = 0; +} + +static void CreateSquareAnchor(coOrd p) { + DIST_T d = tempD.scale*0.15; + int i = anchors_da.cnt; + DYNARR_SET(trkSeg_t,anchors_da,i+4); + for (int j =0; j<4;j++) { + anchors(i+j).type = SEG_STRLIN; + anchors(i+j).color = wDrawColorBlue; + anchors(i+j).width = 0; + } + anchors(i).u.l.pos[0].x = anchors(i+2).u.l.pos[1].x = + anchors(i+3).u.l.pos[0].x = anchors(i+3).u.l.pos[1].x = p.x-d/2; + + anchors(i).u.l.pos[0].y = anchors(i).u.l.pos[1].y = + anchors(i+1).u.l.pos[0].y = anchors(i+3).u.l.pos[1].y = p.y-d/2; + + anchors(i).u.l.pos[1].x = + anchors(i+1).u.l.pos[0].x = anchors(i+1).u.l.pos[1].x = + anchors(i+2).u.l.pos[0].x = p.x+d/2; + + anchors(i+1).u.l.pos[1].y = + anchors(i+2).u.l.pos[0].y = anchors(i+2).u.l.pos[1].y = + anchors(i+3).u.l.pos[0].y = p.y+d/2; +} + +BOOL_T FindTempNear(drawContext_t *context, coOrd *p) { + if (context->State == 2) { + if (context->Op >= OP_CURVE1 && context->Op <= OP_CURVE4) { + if (context->ArcData.type == curveTypeCurve) { + ANGLE_T a = FindAngle(context->ArcData.curvePos,*p); + if (IsClose(FindDistance(context->ArcData.curvePos,*p)-context->ArcData.curveRadius) && + (a>=context->ArcData.a0) && (a<=context->ArcData.a0+context->ArcData.a1)) { + Translate(p,context->ArcData.curvePos,a,context->ArcData.curveRadius); + return TRUE; + } + } else { + if (IsClose(LineDistance(p,tempSegs(0).u.l.pos[0],tempSegs(0).u.l.pos[1]))) + return TRUE; + } + } else if ( context->Op >=OP_LINE && context->Op <= OP_BENCH) { + if (IsClose(LineDistance(p,tempSegs(0).u.l.pos[0],tempSegs(0).u.l.pos[1]))) + return TRUE; + } + } + return FALSE; +} + /** * Create and draw a graphics primitive (lines, arc, circle). The complete handling of mouse * movements and clicks during the editing process is done here. @@ -108,63 +178,195 @@ static void DrawGeomOk( void ) * \param pos IN position of mouse pointer * \param context IN/OUT parameters for drawing op * \return next command state + * + * Note - Poly supports both clicking points and/or dragging sides. Close is space or enter. + * Note - This routine will be recalled to C_UPDATE the last action if the State is 2 and the user updates the dialog + * */ STATUS_T DrawGeomMouse( wAction_t action, coOrd pos, - drawContext_t *context ) + drawContext_t *context) { static int lastValid = FALSE; + static wBool_t lock; static coOrd pos0, pos0x, pos1, lastPos; trkSeg_p segPtr; - coOrd *pts; + pts_t *pts; int inx; DIST_T width; static int segCnt; DIST_T d; + ANGLE_T a1,a2; + static ANGLE_T line_angle; BOOL_T createTrack; - long oldOptions; - width = context->Width/context->D->dpi; + width = context->line_Width/context->D->dpi; switch (action&0xFF) { + case C_UPDATE: + if (context->State == 0 ) return C_TERMINATE; + if (context->Op != OP_POLY && context->Op != OP_FILLPOLY && context->Op != OP_POLYLINE && context->State == 1) return C_TERMINATE; + switch (context->Op) { + case OP_CIRCLE1: + case OP_CIRCLE2: + case OP_CIRCLE3: + case OP_FILLCIRCLE1: + case OP_FILLCIRCLE2: + case OP_FILLCIRCLE3: + tempSegs(0).u.c.radius = context->radius; + break; + case OP_CURVE1: + case OP_CURVE2: + case OP_CURVE3: + case OP_CURVE4: + if (context->ArcData.type == curveTypeCurve) { + if (tempSegs(0).u.c.radius != context->radius) { + coOrd end; + Translate(&end,context->ArcData.curvePos,context->ArcData.a0,context->ArcData.curveRadius); + tempSegs(0).u.c.radius = context->radius; + Translate(&tempSegs(0).u.c.center,end,context->ArcData.a0+180,context->radius); + context->ArcData.curvePos = tempSegs(0).u.c.center; + context->ArcData.curveRadius = tempSegs(0).u.c.radius; + } + tempSegs(0).u.c.a1 = context->angle; + context->ArcData.a1 = tempSegs(0).u.c.a1; + Translate(&context->ArcData.pos1,context->ArcData.curvePos,context->ArcData.a0,context->ArcData.curveRadius); + Translate(&context->ArcData.pos2,context->ArcData.curvePos,context->ArcData.a0+context->ArcData.a1,context->ArcData.curveRadius); + } else + Translate(&tempSegs(0).u.l.pos[1],tempSegs(0).u.l.pos[0],context->angle,context->length); + break; + case OP_LINE: + case OP_BENCH: + case OP_TBLEDGE: + a1 = FindAngle(pos0,pos1); + Translate(&tempSegs(0).u.l.pos[1],tempSegs(0).u.l.pos[0],context->angle,context->length); + lastPos = pos1 = tempSegs(0).u.l.pos[1]; + tempSegs_da.cnt = 1; + context->angle = NormalizeAngle(context->angle); + break; + case OP_BOX: + case OP_FILLBOX: + pts = tempSegs(0).u.p.pts; + a1 = FindAngle(pts[0].pt,pts[1].pt); + Translate(&pts[1].pt,pts[0].pt,a1,context->length); + a2 = FindAngle(pts[0].pt,pts[3].pt); + Translate(&pts[2].pt,pts[1].pt,a2,context->width); + Translate(&pts[3].pt,pts[0].pt,a2,context->width); + tempSegs_da.cnt = 1; + break; + case OP_POLY: + case OP_FILLPOLY: + case OP_POLYLINE: + tempSegs_da.cnt = segCnt; + if (segCnt>1) { + ANGLE_T an = FindAngle(tempSegs(segCnt-2).u.l.pos[0],tempSegs(segCnt-2).u.l.pos[1]); + an = an+context->angle; + Translate(&tempSegs(segCnt-1).u.l.pos[1],tempSegs(segCnt-1).u.l.pos[0],an,context->length); + } else { + Translate(&tempSegs(0).u.l.pos[1],tempSegs(0).u.l.pos[0],context->angle,context->length); + } + pos1 = lastPos = tempSegs(segCnt-1).u.l.pos[1]; + context->angle = fabs(context->angle); + if (context->angle >180) context->angle = context->angle - 180.0; + break; + default: + break; + } + MainRedraw(); + anchors_da.cnt = 0; + return C_CONTINUE; + case C_START: context->State = 0; context->Changed = FALSE; segCnt = 0; + CleanSegs(&tempSegs_da); DYNARR_RESET( trkSeg_t, tempSegs_da ); + DYNARR_RESET( trkSeg_t, anchors_da ); + lock = FALSE; + if (!magneticSnap) + InfoMessage(_("+Shift to lock to nearby objects")); + else + InfoMessage(_("+Shift to not lock to nearby objects")); return C_CONTINUE; case wActionMove: - return C_CONTINUE; - - case wActionLDown: - context->Started = TRUE; - if (context->State == 0) { //First Down only - switch (context->Op) { //Snap pos to nearest line end point if this is end and just shift is depressed for lines and some curves - case OP_LINE: + if (context->State == 0 || context->State ==2 ) { + DYNARR_RESET( trkSeg_t, anchors_da ); + switch (context->Op) { //Snap pos to nearest line if this is end and just shift is depressed for lines and some curves case OP_CURVE1: - if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_SHIFT ) { + case OP_CURVE2: + case OP_CURVE3: + case OP_CURVE4: + case OP_LINE: + case OP_DIMLINE: + case OP_BENCH: + case OP_POLY: + case OP_FILLPOLY: + case OP_POLYLINE: + if (((MyGetKeyState() & WKEY_ALT) == 0) == magneticSnap ) { coOrd p = pos; track_p t; - if ((t=OnTrack(&p,FALSE,FALSE))) { - if (GetClosestEndPt(t,&p)) { - pos = p; + if ((t=OnTrack(&p,FALSE,FALSE))!=NULL) { + if (context->Op == OP_DIMLINE ) { + CreateEndAnchor(p,FALSE); + } else if (!IsTrack(t)) CreateEndAnchor(p,FALSE); + } else { + p = pos; + if (FindTempNear(context,&p)) { + CreateEndAnchor(p,FALSE); } } - }; + } break; default: ; } } + return C_CONTINUE; + + case wActionLDown: + DYNARR_RESET( trkSeg_t, anchors_da ); + if (context->State == 2) { + tempSegs_da.cnt = segCnt; + if ((context->Op == OP_POLY || context->Op == OP_FILLPOLY || context->Op == OP_POLYLINE)) { + EndPoly(context, segCnt, context->Op==OP_POLYLINE); + } else { + DrawGeomOk(); + } + segCnt = 0; + anchors_da.cnt = 0; + context->State = 0; + } + context->Started = TRUE; + line_angle = 90.0; + if ((context->Op == OP_CURVE1 && context->State != 2) || + (context->Op == OP_CURVE2 && context->State == 0) || + (context->Op == OP_CURVE3 && context->State != 0) || + (context->Op == OP_CURVE4 && context->State != 2) || + (context->Op == OP_LINE) || (context->Op == OP_DIMLINE) || + (context->Op == OP_BENCH) ) { + BOOL_T found = FALSE; + if (((MyGetKeyState() & WKEY_ALT) ==0) == magneticSnap ) { + coOrd p = pos; + track_p t; + if ((t=OnTrack(&p,FALSE,FALSE))!=NULL) { + if (!IsTrack(t)) { + EPINX_T ep1,ep2; + line_angle = GetAngleAtPoint(t,pos,&ep1,&ep2); + pos = p; + found = TRUE; + } + } + } + if (!found) SnapPos( &pos ); + } 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 ) // Control snaps to nearest track (not necessarily the end) - OnTrack( &pos, FALSE, FALSE ); pos0 = pos; pos1 = pos; } @@ -173,9 +375,6 @@ STATUS_T DrawGeomMouse( case OP_LINE: case OP_DIMLINE: case OP_BENCH: - if ( lastValid && ( MyGetKeyState() & WKEY_CTRL ) ) { - pos = pos0 = lastPos; - } DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); switch (context->Op) { case OP_LINE: tempSegs(0).type = SEG_STRLIN; break; @@ -191,12 +390,9 @@ STATUS_T DrawGeomMouse( tempSegs(0).u.l.option = 0; } tempSegs_da.cnt = 0; - context->message( _("Drag to place next end point") ); + context->message( _("Drag to next point, +Shift to lock to object, +Ctrl to lock to 90deg") ); break; case OP_TBLEDGE: - if ( lastValid && ( MyGetKeyState() & WKEY_CTRL ) ) { - pos = pos0 = lastPos; - } OnTableEdgeEndPt( NULL, &pos ); DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); tempSegs(0).type = SEG_TBLEDGE; @@ -214,9 +410,8 @@ STATUS_T DrawGeomMouse( 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; + CreateCurve( C_START, pos, FALSE, context->Color, width, drawGeomCurveMode, &anchors_da, context->message ); + CreateCurve( C_DOWN, pos, FALSE, context->Color, width, drawGeomCurveMode, &anchors_da, context->message ); } break; case OP_CIRCLE1: @@ -240,6 +435,7 @@ STATUS_T DrawGeomMouse( break; case OP_FILLBOX: width = 0; + /* no break */ case OP_BOX: DYNARR_SET( trkSeg_t, tempSegs_da, 4 ); for ( inx=0; inx<4; inx++ ) { @@ -248,75 +444,172 @@ STATUS_T DrawGeomMouse( 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: + case OP_POLYLINE: tempSegs_da.cnt = segCnt; - DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + wBool_t first_spot = FALSE; + if (segCnt == 1 && tempSegs(0).type == SEG_CRVLIN) { + coOrd start; + start = tempSegs(0).u.c.center; + tempSegs(0).type = SEG_STRLIN; + tempSegs(0).u.l.pos[0] = start; + first_spot=TRUE; + } else { + 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]; + //End if over start + if ( segCnt>2 && IsClose(FindDistance(tempSegs(0).u.l.pos[0], pos ))) { + segPtr->u.l.pos[0] = tempSegs(segCnt-1).u.l.pos[1]; + segPtr->u.l.pos[1] = tempSegs(0).u.l.pos[0]; + EndPoly(context, tempSegs_da.cnt, context->Op==OP_POLYLINE); + DYNARR_RESET(pts_t, points_da); + DYNARR_RESET(trkSeg_t,tempSegs_da); + context->State = 0; + segCnt = 0; + return C_TERMINATE; + } + if (!first_spot) { + 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; + segCnt = tempSegs_da.cnt; + context->message(_("+Shift - lock to close object, +Ctrl - lock to 90 deg")); 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 ); + DYNARR_RESET(trkSeg_t, anchors_da ); + if ((context->Op == OP_CURVE1 && context->State == 1) || + (context->Op == OP_CURVE2 && context->State == 0) || + (context->Op == OP_CURVE4 && context->State != 2) || + (context->Op == OP_LINE) || + (context->Op == OP_BENCH) ) { + if (( (MyGetKeyState() & WKEY_ALT)==0) == magneticSnap) { + if (OnTrack( &pos, FALSE, FALSE )!=NULL) + CreateEndAnchor(pos,TRUE); + } + } else if (context->Op == OP_DIMLINE) { + if (OnTrack( &pos, FALSE, FALSE )!=NULL) CreateEndAnchor(pos,TRUE); + } + pos1 = pos; + switch (context->Op) { case OP_TBLEDGE: OnTableEdgeEndPt( NULL, &pos1 ); + /* no break */ case OP_LINE: case OP_DIMLINE: case OP_BENCH: + if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_CTRL ) { + //Snap to Right-Angle from previous or from 0 + DIST_T l = FindDistance(pos0, pos); + ANGLE_T angle2 = NormalizeAngle(FindAngle(pos0, pos)-line_angle); + int quad = (int)((angle2 + 45.0) / 90.0); + if (tempSegs_da.cnt != 1 && (quad == 2)) { + pos1 = pos0; + } else if (quad == 1 || quad == 3) { + if (tempSegs_da.cnt != 1) + l = fabs(l*cos(D2R(((quad==1)?line_angle+90.0:line_angle-90.0)-FindAngle(pos,pos0)))); + Translate( &pos1, pos0, NormalizeAngle(quad==1?line_angle+90.0:line_angle-90.0), l ); + } else { + if (tempSegs_da.cnt != 1) + l = fabs(l*cos(D2R(((quad==0||quad==4)?line_angle:line_angle+180.0)-FindAngle(pos,pos0)))); + Translate( &pos1, pos0, NormalizeAngle((quad==0||quad==4)?line_angle:line_angle+180.0), l ); + } + CreateLineAnchor(pos1,pos0); + } 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; + if (anchors_da.cnt == 0) CreateEndAnchor(pos, FALSE); break; case OP_POLY: case OP_FILLPOLY: + case OP_POLYLINE: + if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_CTRL ) { + coOrd last_point = zero; + ANGLE_T last_angle, initial_angle; + if (tempSegs_da.cnt == 1) { + last_angle = 90.0; + last_point = tempSegs(0).u.l.pos[0]; + initial_angle = 90.0; + } else { + last_point = tempSegs(tempSegs_da.cnt-2).u.l.pos[1]; + last_angle = FindAngle(tempSegs(tempSegs_da.cnt-2).u.l.pos[0],tempSegs(tempSegs_da.cnt-2).u.l.pos[1]); + initial_angle = FindAngle(tempSegs(0).u.l.pos[0],tempSegs(0).u.l.pos[1]); + } + //Snap to Right-Angle from previous or from 0 + DIST_T l = FindDistance(tempSegs(tempSegs_da.cnt-1).u.l.pos[0], pos); + ANGLE_T angle2 = NormalizeAngle(FindAngle(tempSegs(tempSegs_da.cnt-1).u.l.pos[0], pos)-last_angle); + int quad = (int)((angle2+45.0)/90.0); + if (tempSegs_da.cnt != 1 && (quad == 2)) { + pos = tempSegs(tempSegs_da.cnt-1).u.l.pos[0]; + } else if (quad == 1 || quad == 3) { + if (tempSegs_da.cnt != 1) + l = fabs(l*cos(D2R(((quad==1)?last_angle+90.0:last_angle-90.0)-FindAngle(pos,last_point)))); + Translate( &pos, tempSegs(tempSegs_da.cnt-1).u.l.pos[0], NormalizeAngle(quad==1?last_angle+90.0:last_angle-90.0), l ); + } else { + if (tempSegs_da.cnt != 1) + l = fabs(l*cos(D2R(((quad==0||quad==4)?last_angle:last_angle+180.0)-FindAngle(pos,last_point)))); + Translate( &pos, tempSegs(tempSegs_da.cnt-1).u.l.pos[0], NormalizeAngle((quad==0||quad==4)?last_angle:last_angle+180.0), l ); + } + CreateEndAnchor(pos,TRUE); + if (FindDistance(pos,last_point)>0.0) CreateLineAnchor(pos,last_point); + } + //If there is any point on this line that will give a 90 degree return to the first point, show it + if (tempSegs_da.cnt > 1) { + coOrd intersect; + ANGLE_T an_this = FindAngle(tempSegs(tempSegs_da.cnt-2).u.l.pos[1],pos); + if (FindIntersection(&intersect,tempSegs(0).u.l.pos[0],an_this+90.0,tempSegs(tempSegs_da.cnt-2).u.l.pos[1],an_this)) { + ANGLE_T an_inter = FindAngle(tempSegs(tempSegs_da.cnt-2).u.l.pos[1],intersect); + if (fabs(DifferenceBetweenAngles(an_inter,an_this))<90.0) { + CreateSquareAnchor(intersect); + d = FindDistance(intersect,pos); + if (IsClose(d)) { + pos = intersect; + } + } + } + } 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 )) ); + segCnt = tempSegs_da.cnt; break; case OP_CURVE1: case OP_CURVE2: case OP_CURVE3: case OP_CURVE4: if (context->State == 0) { - CreateCurve( C_MOVE, pos, FALSE, context->Color, width, drawGeomCurveMode, context->message ); pos0x = pos; + CreateCurve( C_MOVE, pos, FALSE, context->Color, width, drawGeomCurveMode, &anchors_da, context->message ); } else { PlotCurve( drawGeomCurveMode, pos0, pos0x, pos1, &context->ArcData, FALSE ); tempSegs(0).color = context->Color; tempSegs(0).width = width; + DYNARR_SET(trkSeg_t,tempSegs_da,1); 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; + CreateEndAnchor(pos0, FALSE); + CreateEndAnchor(context->ArcData.pos1, FALSE); context->message( _("Straight Line: Length=%s Angle=%0.3f"), FormatDistance(FindDistance( pos0, context->ArcData.pos1 )), PutAngle(FindAngle( pos0, context->ArcData.pos1 )) ); @@ -337,14 +630,21 @@ STATUS_T DrawGeomMouse( 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) ); + if (context->Op == OP_CURVE1 || context->Op == OP_CURVE4 ) + DrawArrowHeadsArray(&anchors_da,pos,FindAngle(context->ArcData.curvePos,pos),TRUE,wDrawColorRed); + else if (context->Op == OP_CURVE2 || context->Op == OP_CURVE3 ) { + CreateEndAnchor(context->ArcData.pos2,FALSE); + DrawArrowHeadsArray(&anchors_da,context->ArcData.pos2,FindAngle(context->ArcData.curvePos,context->ArcData.pos2)+90,TRUE,wDrawColorRed); + } + CreateEndAnchor(context->ArcData.curvePos,TRUE); } } + if (anchors_da.cnt == 0) CreateEndAnchor(pos, FALSE); break; case OP_CIRCLE1: case OP_FILLCIRCLE1: @@ -352,6 +652,7 @@ STATUS_T DrawGeomMouse( case OP_CIRCLE2: case OP_FILLCIRCLE2: tempSegs(0).u.c.center = pos1; + /* no break */ case OP_CIRCLE3: case OP_FILLCIRCLE3: tempSegs(0).u.c.radius = FindDistance( pos0, pos1 ); @@ -369,58 +670,48 @@ STATUS_T DrawGeomMouse( 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; - if (context->Op == OP_DIMLINE) MainRedraw(); //Wipe Out Text 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; - if ((context->State == 0 && (context->Op == OP_LINE )) || //first point release for line, - (context->State == 1 && context->Op == OP_CURVE1)) { //second point for curve from end - switch (context->Op) { //Snap pos to nearest line end point if this is on a line and just shift is depressed for lines and some curves - case OP_CURVE1: - case OP_LINE: - if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_SHIFT ) { - coOrd p = pos1; - track_p t; - if ((t=OnTrack(&p,FALSE,FALSE))) { - if (GetClosestEndPt(t,&p)) { - pos1 = p; - if (context->Op == OP_LINE) { - tempSegs(0).u.l.pos[1] = p; - } else { - PlotCurve( drawGeomCurveMode, pos0, pos0x, pos1, &context->ArcData, FALSE ); - 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; - } else if (context->ArcData.type == curveTypeNone) { - tempSegs_da.cnt = 0; - } 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; - } - } - } + if ((context->Op == OP_CURVE1 && context->State == 1) || + (context->Op == OP_CURVE2 && context->State == 0) || + (context->Op == OP_CURVE3 && context->State != 0) || + (context->Op == OP_CURVE4 && context->State != 2) || + (context->Op == OP_LINE) || (context->Op == OP_DIMLINE) || + (context->Op == OP_BENCH) ) { + if (((MyGetKeyState() & WKEY_ALT)==0) == magneticSnap ) { + coOrd p = pos1; + track_p t; + if ((t=OnTrack(&p,FALSE,FALSE))) { + pos1 = p; + if (context->Op == OP_LINE || context->Op == OP_DIMLINE || context->Op == OP_BENCH) { + tempSegs(0).u.l.pos[1] = p; + } else { + PlotCurve( drawGeomCurveMode, pos0, pos0x, pos1, &context->ArcData, FALSE ); + if (context->ArcData.type == curveTypeStraight) { + DYNARR_RESET(trkSeg_t,tempSegs_da); + DYNARR_APPEND(trkSeg_t,tempSegs_da,1); + 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; + } else if (context->ArcData.type == curveTypeNone) { + DYNARR_RESET(trkSeg_t,tempSegs_da); + } else if (context->ArcData.type == curveTypeCurve) { + DYNARR_RESET(trkSeg_t,tempSegs_da); + DYNARR_APPEND(trkSeg_t,tempSegs_da,1); + 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; } - }; - break; - default: - ; + } + + } } } switch ( context->Op ) { @@ -430,22 +721,22 @@ STATUS_T DrawGeomMouse( case OP_TBLEDGE: lastValid = TRUE; lastPos = pos1; + context->length = FindDistance(pos1,pos0); + context->angle = FindAngle(pos0,pos1); + context->State = 2; + segCnt = tempSegs_da.cnt; 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, FALSE, context->Color, width, drawGeomCurveMode, context->message ); - DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); - segCnt = tempSegs_da.cnt; + CreateCurve( C_UP, pos, FALSE, context->Color, width, drawGeomCurveMode, &anchors_da, context->message ); context->message( _("Drag on Red arrows to adjust curve") ); - context->D->funcs->options = oldOptions; return C_CONTINUE; } else { - tempSegs_da.cnt = 0; + DYNARR_SET(trkSeg_t,tempSegs_da,1); if (context->ArcData.type == curveTypeCurve) { - tempSegs_da.cnt = 1; segPtr = &tempSegs(0); segPtr->type = SEG_CRVLIN; segPtr->color = context->Color; @@ -454,20 +745,22 @@ STATUS_T DrawGeomMouse( segPtr->u.c.radius = context->ArcData.curveRadius; segPtr->u.c.a0 = context->ArcData.a0; segPtr->u.c.a1 = context->ArcData.a1; + context->radius = context->ArcData.curveRadius; + context->angle = 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->radius = 0; + context->length = FindDistance(pos0,pos1); + context->angle = FindAngle(pos0,pos1); } - context->State = 0; lastValid = TRUE; lastPos = pos1; + context->State = 2; /*drawContext = context; DrawGeomOp( (void*)context->Op );*/ } @@ -480,14 +773,17 @@ STATUS_T DrawGeomMouse( case OP_FILLCIRCLE3: if ( context->Op>=OP_FILLCIRCLE1 && context->Op<=OP_FILLCIRCLE3 ) tempSegs(0).type = SEG_FILCRCL; - /*drawContext = context; - DrawGeomOp( (void*)context->Op );*/ + tempSegs_da.cnt = 1; + context->State = 2; + context->radius = tempSegs(0).u.c.radius; break; case OP_BOX: case OP_FILLBOX: - pts = (coOrd*)MyMalloc( 4 * sizeof (coOrd) ); - for ( inx=0; inx<4; inx++ ) - pts[inx] = tempSegs(inx).u.l.pos[0]; + pts = (pts_t*)MyMalloc( 4 * sizeof (pts_t) ); + for ( inx=0; inx<4; inx++ ) { + pts[inx].pt = tempSegs(inx).u.l.pos[0]; + pts[inx].pt_type = wPolyLineStraight; + } tempSegs(0).type = (context->Op == OP_FILLBOX)?SEG_FILPOLY:SEG_POLY; tempSegs(0).u.p.cnt = 4; tempSegs(0).u.p.pts = pts; @@ -497,188 +793,1259 @@ STATUS_T DrawGeomMouse( tempSegs_da.cnt = 1; /*drawContext = context; DrawGeomOp( (void*)context->Op );*/ + context->length = FindDistance(pts[0].pt,pts[1].pt); + context->width = FindDistance(pts[3].pt,pts[0].pt); + context->State = 2; + segCnt = tempSegs_da.cnt; break; case OP_POLY: case OP_FILLPOLY: + case OP_POLYLINE: + tempSegs_da.cnt = segCnt; + anchors_da.cnt=0; + //End if close to start + if ( segCnt>2 && IsClose(FindDistance(tempSegs(0).u.l.pos[0], pos))) { + EndPoly(context, tempSegs_da.cnt, context->Op==OP_POLYLINE); + DYNARR_RESET(pts_t, points_da); + CleanSegs(&tempSegs_da); + context->State = 0; + segCnt = 0; + return C_TERMINATE; + } + //If too short, remove last segment + if (IsClose(FindDistance(tempSegs(segCnt-1).u.l.pos[0],pos))) { + if (tempSegs_da.cnt>1) { + --tempSegs_da.cnt; + segCnt = tempSegs_da.cnt; + wBeep(); + } else { //First spot only + tempSegs(0).color = wDrawColorRed; + tempSegs(0).type = SEG_CRVLIN; + tempSegs(0).u.c.a1 = 360; + tempSegs(0).u.c.radius = tempD.scale*0.15/2; + tempSegs(0).u.c.center = pos; + segCnt = tempSegs_da.cnt; + } + return C_CONTINUE; + } + int text_inx = tempSegs_da.cnt-1; + //tempSegs(tempSegs_da.cnt-1).u.l.pos[1] = pos; + context->length = FindDistance(tempSegs(text_inx).u.l.pos[0],tempSegs(text_inx).u.l.pos[1]); + if (text_inx>1) { + ANGLE_T an = FindAngle(tempSegs(text_inx-1).u.l.pos[0],tempSegs(text_inx-1).u.l.pos[1]); + context->angle = NormalizeAngle(FindAngle(tempSegs(text_inx).u.l.pos[0],tempSegs(text_inx).u.l.pos[1])-an); + } else + context->angle = FindAngle(tempSegs(1).u.l.pos[0],tempSegs(1).u.l.pos[1]); + context->State = 1; + context->index = text_inx; segCnt = tempSegs_da.cnt; - context->D->funcs->options = oldOptions; return C_CONTINUE; } context->Started = FALSE; - context->Changed = TRUE; + context->Changed = TRUE; //Update screen shown /*CheckOk();*/ - context->D->funcs->options = oldOptions; + if (context->State == 2 && IsCurCommandSticky()) { + segCnt = tempSegs_da.cnt; + return C_CONTINUE; + } DrawGeomOk(); + context->State = 0; + context->Changed = FALSE; + context->message(""); return C_TERMINATE; case wActionText: - + DYNARR_RESET(trkSeg_t, anchors_da ); if ( ((action>>8)&0xFF) == 0x0D || ((action>>8)&0xFF) == ' ' ) { - EndPoly(context, segCnt); - context->State = 0; + if ((context->Op == OP_POLY) || (context->Op == OP_FILLPOLY) || (context->Op == OP_POLYLINE)) { + tempSegs_da.cnt = segCnt; + //If last segment wasn't just a point, add another starting on its end + if (!IsClose(FindDistance(tempSegs(segCnt-1).u.l.pos[0],tempSegs(segCnt-1).u.l.pos[1]))) { + DYNARR_APPEND(trkSeg_t,tempSegs_da,1); + segPtr = &tempSegs(tempSegs_da.cnt-1); + segPtr->type = SEG_STRLIN; + segPtr->u.l.pos[0] = segPtr[-1].u.l.pos[1]; + segPtr->u.l.pos[1] = tempSegs(0).u.l.pos[0]; + } + EndPoly(context, tempSegs_da.cnt, context->Op == OP_POLYLINE); + DYNARR_RESET(pts_t, points_da); + DYNARR_RESET(trkSeg_t,tempSegs_da); + } else { + if (context->State == 2) + tempSegs_da.cnt = segCnt; + DrawGeomOk(); + } } + context->State = 0; + segCnt = 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; + if (context->Changed) { //If the update values were shown + if (context->State == 2) { + tempSegs_da.cnt = segCnt; + DrawGeomOk(); + } + } + DYNARR_RESET(trkSeg_t, anchors_da ); tempSegs_da.cnt = 0; context->message( "" ); context->Changed = FALSE; + context->State = 0; + segCnt = 0; 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; + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); + if (anchors_da.cnt > 0) { + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, 0.0, wDrawColorBlack ); + } return C_CONTINUE; + case C_CMDMENU: + + return C_CONTINUE; + default: return C_CONTINUE; } } +static int polyInx; +static int lineInx; +static int curveInx; + +typedef enum {POLY_NONE, POLY_SELECTED, POLYPOINT_SELECTED} PolyState_e; +static PolyState_e polyState = POLY_NONE; +static coOrd rotate_origin; +static ANGLE_T rotate_angle; +static dynArr_t origin_da; + + +void static CreateCircleAnchor(wBool_t selected,coOrd center, DIST_T rad, ANGLE_T angle) { + DYNARR_RESET(trkSeg_t,anchors_da); + double d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,2); + anchors(0).type = (selected)?SEG_FILCRCL:SEG_CRVLIN; + anchors(0).u.c.a1 = 360.0; + anchors(0).color = wDrawColorBlue; + anchors(0).u.c.radius = d/2; + PointOnCircle(&anchors(0).u.c.center,center,rad,angle); +} +void static CreateLineAnchors(int index, coOrd p0, coOrd p1) { + DYNARR_RESET(trkSeg_t,anchors_da); + double d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,2); + anchors(0).type = (index ==0)?SEG_FILCRCL:SEG_CRVLIN; + anchors(0).u.c.a1 = 360.0; + anchors(0).color = wDrawColorBlue; + anchors(0).u.c.radius = d/2; + anchors(0).u.c.center = p0; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + anchors(1).type = (index ==1)?SEG_FILCRCL:SEG_CRVLIN; + anchors(1).u.c.a1 = 360.0; + anchors(1).color = wDrawColorBlue; + anchors(1).u.c.radius = d/2; + anchors(1).u.c.center = p1; +} +void static CreateBoxAnchors(int index, pts_t pt[4]) { + DYNARR_RESET(trkSeg_t,anchors_da); + double d = tempD.scale*0.15; + ANGLE_T a = FindAngle(pt[0].pt,pt[1].pt); + ANGLE_T diag = FindAngle(pt[0].pt,pt[2].pt); + for (int i=0;i<4;i++) { + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),pt[i].pt,(diag>a?45.0:-45.0)+a+(90.0*(i)),TRUE,i==index?wDrawColorRed:wDrawColorBlue); + } + coOrd pp; + for (int i=0;i<4;i++) { + pp.x = (i==3?((((pt[0].pt.x - pt[i].pt.x)/2))+pt[i].pt.x):((pt[i+1].pt.x - pt[i].pt.x)/2)+pt[i].pt.x); + pp.y = (i==3?((((pt[0].pt.y - pt[i].pt.y)/2))+pt[i].pt.y):((pt[i+1].pt.y - pt[i].pt.y)/2)+pt[i].pt.y); + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),pp,90.0*(i-1)+a,TRUE,i==index+5?wDrawColorRed:wDrawColorBlue); + } +} + +void static CreateOriginAnchor(coOrd origin, wBool_t trans_selected) { + double d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,2); + int i = anchors_da.cnt-1; + coOrd p0,p1; + Translate(&p0,origin,0,d*4); + Translate(&p1,origin,0,-d*4); + anchors(i).type = SEG_STRLIN; + anchors(i).u.l.pos[0] = p0; + anchors(i).u.l.pos[1] = p1; + anchors(i).color = wDrawColorBlue; + anchors(i).width = 0; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + Translate(&p0,origin,90,d*4); + Translate(&p1,origin,90,-d*4); + i = anchors_da.cnt-1; + anchors(i).type = SEG_STRLIN; + anchors(i).u.l.pos[0] = p0; + anchors(i).u.l.pos[1] = p1; + anchors(i).color = wDrawColorBlue; + anchors(i).width = 0; +} + +void static CreateCurveAnchors(int index, coOrd pm, coOrd pc, coOrd p0, coOrd p1) { + DYNARR_RESET(trkSeg_t,anchors_da); + double d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,9); + anchors(0).type = (index ==0)?SEG_FILCRCL:SEG_CRVLIN; + anchors(0).u.c.a1 = 360.0; + anchors(0).color = wDrawColorBlue; + anchors(0).u.c.radius = d/2; + anchors(0).u.c.center = p0; + anchors(0).width = 0; + DYNARR_APPEND(trkSeg_t,anchors_da,8); + anchors(1).type = (index ==1)?SEG_FILCRCL:SEG_CRVLIN; + anchors(1).u.c.a1 = 360.0; + anchors(1).color = wDrawColorBlue; + anchors(1).u.c.radius = d/2; + anchors(1).u.c.center = p1; + anchors(1).width = 0; + DYNARR_SET(trkSeg_t,anchors_da,anchors_da.cnt+5); + DrawArrowHeads(&DYNARR_N(trkSeg_t,anchors_da,anchors_da.cnt-5),pm,FindAngle(pm,pc),TRUE,index==2?wDrawColorAqua:wDrawColorBlue); +} + +void static CreatePolyAnchors(int index) { + DYNARR_RESET(trkSeg_t,anchors_da); + double d = tempD.scale*0.15; + for ( int inx=0; inx<points_da.cnt; inx++ ) { + DYNARR_APPEND(trkSeg_t,anchors_da,3); + + anchors(inx).type = point_selected(inx)?SEG_FILCRCL:SEG_CRVLIN; + anchors(inx).u.c.a0 = 0.0; + anchors(inx).u.c.a1 = 360.0; + anchors(inx).width = 0; + anchors(inx).color = wDrawColorBlue; + anchors(inx).u.c.radius = d/2; + anchors(inx).u.c.center = points(inx).pt; + } + if (index>=0) { + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int inx = anchors_da.cnt-1; + anchors(inx).type = SEG_STRLIN; + anchors(inx).u.l.pos[0] = points(index==0?points_da.cnt-1:index-1).pt; + anchors(inx).u.l.pos[1] = points(index).pt; + anchors(inx).color = wDrawColorBlue; + anchors(inx).width = 0; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + inx = anchors_da.cnt-1; + int index0 = index==0?points_da.cnt-1:index-1; + ANGLE_T an0 = FindAngle(points(index0).pt, points(index).pt); + ANGLE_T an1 = FindAngle(points(index0==0?points_da.cnt-1:index0-1).pt, points(index0).pt); + anchors(inx).type = SEG_CRVLIN; + if (DifferenceBetweenAngles(an0,an1)<=0) { + anchors(inx).u.c.a1 = DifferenceBetweenAngles(an0,an1)-180; + anchors(inx).u.c.a0 = an0; + } else { + anchors(inx).u.c.a1 = 180-DifferenceBetweenAngles(an0,an1); + anchors(inx).u.c.a0 = NormalizeAngle(180+an1); + } + anchors(inx).color = wDrawColorBlue; + anchors(inx).u.c.radius = d; + anchors(inx).u.c.center = points(index0).pt; + } +} + +void CreateMovingAnchor(coOrd pos,BOOL_T fill) { + double d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int inx = anchors_da.cnt-1; + anchors(inx).type = fill?SEG_FILCRCL:SEG_CRVLIN; + anchors(inx).u.c.a0 = 0.0; + anchors(inx).u.c.a1 = 360.0; + anchors(inx).width = 0; + anchors(inx).color = wDrawColorBlue; + anchors(inx).u.c.radius = d/4; + anchors(inx).u.c.center = pos; +} + +/* + * Modify Polygons. Polygons have a variable number of nodes. + * + * Each point has an anchor and selecting the node allows it to be moved + * Selecting a point between nodes adds a node ready for dragging + * The last selected node can be deleted + * + */ +STATUS_T DrawGeomPolyModify( + wAction_t action, + coOrd pos, + drawModContext_t *context) { + double d; + static int selected_count; + static int segInx; + static int prev_inx; + static wDrawColor save_color; + static wBool_t drawnAngle; + static double currentAngle; + static double baseAngle; + static BOOL_T lock; + + switch ( action&0xFF ) { + case C_START: + lock = FALSE; + DistanceSegs( context->orig, context->angle, context->segCnt, context->segPtr, &pos, &segInx ); + if (segInx == -1) + return C_ERROR; + if (context->type != SEG_POLY && context->type != SEG_FILPOLY) + return C_ERROR; + prev_inx = -1; + polyState = POLY_SELECTED; + polyInx = -1; + //Copy points + DYNARR_RESET( pts_t, points_da); + DYNARR_RESET( wBool_t, select_da); + for (int inx=0;inx<context->segPtr->u.p.cnt;inx++) { + DYNARR_APPEND(pts_t, points_da,3); + DYNARR_APPEND(wBool_t,select_da,3); + REORIGIN( points(inx).pt, context->segPtr[segInx].u.p.pts[inx].pt, context->angle, context->orig ); + points(inx).pt_type = context->segPtr[segInx].u.p.pts[inx].pt_type; + point_selected(inx) = FALSE; + } + context->prev_inx = -1; + context->max_inx = points_da.cnt-1; + selected_count=0; + rotate_origin = context->orig; + rotate_angle = context->angle; + context->p0 = points(0).pt; + context->p1 = points(1).pt; + //Show points + tempSegs_da.cnt = 1; + tempSegs(0).width = context->segPtr->width; + save_color = context->segPtr->color; + tempSegs(0).color = wDrawColorRed; + tempSegs(0).type = context->type; + tempSegs(0).u.p.cnt = context->segPtr[segInx].u.p.cnt; + tempSegs(0).u.p.angle = 0.0; + tempSegs(0).u.p.orig = zero; + tempSegs(0).u.p.polyType = context->segPtr[segInx].u.p.polyType; + tempSegs(0).u.p.pts = &points(0); + CreatePolyAnchors( -1); + InfoMessage(_("Select points or use context menu")); + ClrAllTrkBitsRedraw( TB_UNDRAWN, TRUE ); + UndrawNewTrack( context->trk ); + return C_CONTINUE; + case wActionMove: + DYNARR_RESET(trkSeg_t,anchors_da); + CreatePolyAnchors(context->prev_inx); + for (int i = 0; i<points_da.cnt; i++) { + if (IsClose(FindDistance(pos,points(i).pt))) { + CreateMovingAnchor(points(i).pt,TRUE); + return C_CONTINUE; + } + } + int pInx=0; + coOrd pm0,pm1; + DIST_T dm = 10000.0; + for ( int inx=0; inx<points_da.cnt; inx++ ) { + pm0 = pos; + DIST_T ddm = LineDistance( &pm0, points( inx==0?points_da.cnt-1:inx-1).pt, points(inx).pt ); + if ( dm > ddm ) { + dm = ddm; + pm1 = pm0; + pInx = inx; + } + } + if (!IsClose(dm)) return C_CONTINUE; + int inxm = pInx==0?points_da.cnt-1:pInx-1; + dm = FindDistance( points(inxm).pt, pm1 ); + DIST_T ddm = FindDistance( points(inxm).pt, points(pInx).pt ); + if ( (dm > 0.25*ddm) && (dm < 0.75*ddm)) { + CreateMovingAnchor(pm1,FALSE); + } else { + if (dm < FindDistance( points(pInx).pt, pm1 )) + CreateMovingAnchor(points(inxm).pt,TRUE); + else + CreateMovingAnchor(points(pInx).pt,TRUE); + } + return C_CONTINUE; + break; + case C_DOWN: + d = 10000.0; + polyInx = -1; + coOrd p0; + double dd; + int inx; + if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) != WKEY_SHIFT) { + if (selected_count <2 ) { + //Wipe out selection(s) if we don't have multiple already (i,e. move with >1 selected) + for (int i=0;i<points_da.cnt;i++) { + point_selected(i) = FALSE; + } + selected_count = 0; + } else { + for (int i=0;i<points_da.cnt;i++) { + if (IsClose(FindDistance(pos,points(i).pt)) && point_selected(i)==TRUE) { + point_selected(i) = FALSE; + selected_count--; + } + } + } + } + //Select Point (polyInx) + for ( int inx=0; inx<points_da.cnt; inx++ ) { + p0 = pos; + dd = LineDistance( &p0, points( inx==0?points_da.cnt-1:inx-1).pt, points(inx).pt ); + if ( d > dd ) { + d = dd; + polyInx = inx; + } + } + if (!IsClose(d)) { //Not on/near object - de-select all points + for (int i=0;i<points_da.cnt;i++) { + point_selected(i) = FALSE; + } + polyInx = -1; + selected_count = 0; + CreatePolyAnchors( -1); + context->prev_inx = -1; + return C_CONTINUE; //Not close to any line + } + polyState = POLYPOINT_SELECTED; + inx = polyInx==0?points_da.cnt-1:polyInx-1; + //Find if the point is to be added + d = FindDistance( points(inx).pt, pos ); + dd = FindDistance( points(inx).pt, points(polyInx).pt ); + if ( d < 0.25*dd ) { + polyInx = inx; + } else if ( d > 0.75*dd ) { + ; + } else { + if (selected_count == 0) { //Only add a new point if no points are already selected! + DYNARR_APPEND(wBool_t,select_da,1); + for (int i=0;i<select_da.cnt;i++) { + if (i == polyInx) point_selected(i) = TRUE; + else point_selected(i) = FALSE; + } + selected_count=1; + DYNARR_APPEND(pts_t,points_da,1); + tempSegs(0).u.p.pts = &points(0); + for (inx=points_da.cnt-1; inx>polyInx; inx-- ) { + points(inx) = points(inx-1); + } + points(polyInx).pt_type = wPolyLineStraight; + tempSegs(0).u.p.cnt = points_da.cnt; + context->max_inx = points_da.cnt-1; + } + } + //If already selected (multiple points), not using shift (to add) select, and on object move to first point + if (selected_count>0 && ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) != WKEY_SHIFT)) { + for (int i=0; i<points_da.cnt;i++) { + if (IsClose(FindDistance(pos,points(i).pt))) { + + point_selected(i) = FALSE; + + } + if (point_selected(i) == TRUE) { + polyInx = i; + } + } + } + pos = points(polyInx).pt; //Move to point + if (point_selected(polyInx)) { //Already selected + } else { + point_selected(polyInx) = TRUE; + ++selected_count; + } + //Work out before and after point + int first_inx = -1; + if (selected_count >0 && selected_count < points_da.cnt-2) { + for (int i=0; i<points_da.cnt;i++) { + if (point_selected(i)) { + first_inx = i; + break; + } + } + } + int last_inx = -1, next_inx = -1; + ANGLE_T an1, an0; + if (first_inx >=0) { + if (first_inx == 0) { + last_inx = points_da.cnt-1; + } else { + last_inx = first_inx-1; + } + if (first_inx == points_da.cnt-1) { + next_inx = 0; + } else { + next_inx = first_inx+1; + } + context->length = FindDistance(points(last_inx).pt,points(first_inx).pt); + an1 = FindAngle(points(last_inx).pt,points(first_inx).pt); + an0 = FindAngle(points(last_inx==0?(points_da.cnt-1):(last_inx-1)).pt,points(last_inx).pt); + if (DifferenceBetweenAngles(an0,an1)<=0) { + context->rel_angle = 180+DifferenceBetweenAngles(an0,an1); + } else { + context->rel_angle = 180-DifferenceBetweenAngles(an0,an1); + } + } else { + + } + context->prev_inx = first_inx; + context->p0 = points(0).pt; + context->p1 = points(1).pt; + //Show three anchors only + CreatePolyAnchors(first_inx); + return C_CONTINUE; + case C_LDOUBLE: + return C_CONTINUE; + case C_MOVE: + tempSegs_da.cnt = 1; + if (polyState != POLYPOINT_SELECTED) { + return C_CONTINUE; + } + //Moving with Point Selected + if (polyInx<0) return C_ERROR; + first_inx = -1; + if (selected_count >0 && selected_count < points_da.cnt-2) { + for (int i=0; i<points_da.cnt;i++) { + if (point_selected(i)) { + first_inx = i; + break; + } + } + } + last_inx = -1; + next_inx = -1; + coOrd intersect; + wBool_t show_intersect = FALSE; + if (first_inx >=0) { + if (first_inx == 0) { + last_inx = points_da.cnt-1; + } else { + last_inx = first_inx-1; + } + if (first_inx == points_da.cnt-1) { + next_inx = 0; + } else { + next_inx = first_inx+1; + } + //Lock to 90 degrees first/last point + if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_SHIFT ) { + ANGLE_T last_angle,next_angle; + coOrd last_point,next_point; + if (first_inx == 0) { + last_point = points(points_da.cnt-1).pt; + last_angle = FindAngle(points(points_da.cnt-2).pt,last_point); + } else if (first_inx == 1) { + last_point = points(0).pt; + last_angle = FindAngle(points(points_da.cnt-1).pt,last_point); + } else { + last_point = points(first_inx-1).pt; + last_angle = FindAngle(points(first_inx-2).pt,last_point); + } + if (first_inx == points_da.cnt-1) { + next_point = points(0).pt; + next_angle = FindAngle(next_point,points(1).pt); + } else if (first_inx == points_da.cnt-2){ + next_point = points(points_da.cnt-1).pt; + next_angle = FindAngle(next_point,points(0).pt); + } else { + next_point = points(first_inx+1).pt; + next_angle = FindAngle(next_point,points(first_inx+2).pt); + } + coOrd diff; + diff.x = pos.x - points(polyInx).pt.x; + diff.y = pos.y - points(polyInx).pt.y; + coOrd pos_lock = points(first_inx).pt; + pos_lock.x += diff.x; + pos_lock.y += diff.y; + //Snap to Right-Angle from previous or from 0 + DIST_T l = FindDistance(last_point, pos_lock); + ANGLE_T angle2 = NormalizeAngle(FindAngle(last_point, pos_lock)-last_angle); + int quad = (int)((angle2+45.0)/90.0); + if (tempSegs_da.cnt != 1 && (quad == 2)) { + pos_lock = last_point; + } else if (quad == 1 || quad == 3) { + l = fabs(l*cos(D2R(((quad==1)?last_angle+90.0:last_angle-90.0)-FindAngle(pos_lock,last_point)))); + Translate( &pos_lock, last_point, NormalizeAngle((quad==1)?last_angle+90.0:last_angle-90.0), l ); + } else { + l = fabs(l*cos(D2R(((quad==0||quad==4)?last_angle:last_angle+180.0)-FindAngle(pos_lock,last_point)))); + Translate( &pos_lock, last_point, NormalizeAngle((quad==0||quad==4)?last_angle:last_angle+180.0), l ); + } + diff.x = pos_lock.x - points(first_inx).pt.x; + diff.y = pos_lock.y - points(first_inx).pt.y; + pos.x = points(polyInx).pt.x+diff.x; + pos.y = points(polyInx).pt.y+diff.y; + if (selected_count<2) { + if (FindIntersection(&intersect,last_point,last_angle+90.0,next_point,last_angle+180.0)) { + show_intersect = TRUE; + } + } + d = FindDistance(intersect,pos_lock); + if (IsClose(d)) { + pos = intersect; + } + InfoMessage( _("Length = %s, Last angle = %0.2f"), + FormatDistance(FindDistance(pos_lock,last_point)), + PutAngle(FindAngle(pos_lock,last_point))); + + } + } + context->prev_inx = first_inx; + coOrd diff; + diff.x = pos.x - points(polyInx).pt.x; + diff.y = pos.y - points(polyInx).pt.y; + //points(polyInx) = pos; + for (int i=0;i<points_da.cnt;i++) { + if (point_selected(i)) { + points(i).pt.x = points(i).pt.x + diff.x; + points(i).pt.y = points(i).pt.y + diff.y; + } + } + if (first_inx>=0) { + context->length = FindDistance(points(first_inx).pt,points(last_inx).pt); + an1 = FindAngle(points(last_inx).pt,points(first_inx).pt); + an0 = FindAngle(points(first_inx==0?(points_da.cnt-1):(first_inx-1)).pt,points(first_inx).pt); + context->rel_angle = NormalizeAngle(180-(an1-an0)); + } + CreatePolyAnchors(first_inx); + if (show_intersect) + CreateSquareAnchor(intersect); + context->p0 = points(0).pt; + context->p1 = points(1).pt; + return C_CONTINUE; + case C_UP: + context->prev_inx = -1; + if (segInx == -1 || polyState != POLYPOINT_SELECTED) + return C_CONTINUE; //Didn't get a point selected/added + polyState = POLY_SELECTED; //Return to base state + anchors_da.cnt = 0; + CreatePolyAnchors(polyInx); //Show last selection + prev_inx = polyInx; + for (int i=0;i<points_da.cnt;i++) { + if (point_selected(i)) { + first_inx = i; + break; + } + } + last_inx = first_inx==0?(points_da.cnt-1):(first_inx-1); + if (first_inx>=0) { + context->length = FindDistance(points(first_inx).pt,points(last_inx).pt); + an1 = FindAngle(points(last_inx).pt,points(first_inx).pt); + an0 = FindAngle(points(last_inx==0?(points_da.cnt-1):(last_inx-1)).pt,points(last_inx).pt); + if (DifferenceBetweenAngles(an0,an1)<=0) { + context->rel_angle = 180+DifferenceBetweenAngles(an0,an1); + } else { + context->rel_angle = 180-DifferenceBetweenAngles(an0,an1); + } + } + context->prev_inx = first_inx; + context->p0 = points(0).pt; + context->p1 = points(1).pt; + polyInx = -1; + return C_CONTINUE; + case C_UPDATE: + if (context->prev_inx>=0) { + int last_index = context->prev_inx==0?(points_da.cnt-1):(context->prev_inx-1); + an0 = FindAngle(points(last_index==0?(points_da.cnt-1):(last_index-1)).pt,points(last_index).pt); + an1 = FindAngle(points(last_index).pt,points(context->prev_inx).pt); + if (DifferenceBetweenAngles(an0,an1)<=0) { + an1 = NormalizeAngle(an0-(180-context->rel_angle)); + } else { + an1 = NormalizeAngle((180-context->rel_angle)+an0); + } + Translate(&points(prev_inx).pt,points(last_index).pt,an1,context->length); + } + context->rel_angle = fabs(context->rel_angle); + if (context->rel_angle >180) context->rel_angle = context->rel_angle - 180.0; + CreatePolyAnchors(prev_inx); + context->p0 = points(0).pt; + context->p1 = points(1).pt; + break; + case C_TEXT: + if (action>>8 == 'o') { //"o" -> origin mode + MenuMode(1); + InfoMessage("Move Origin Mode: Place Origin, p for Points, Enter or Esc"); + return C_CONTINUE; + } + if (((prev_inx>=0 && tempSegs(0).u.p.polyType != POLYLINE) || (prev_inx>=1 && prev_inx<=points_da.cnt-2)) && + ((action>>8 == 's') || (action>>8 == 'v') || (action>>8 == 'r'))) { + switch(action>>8) { + case 's': + points(context->prev_inx).pt_type = wPolyLineSmooth; + break; + case 'v': + points(context->prev_inx).pt_type = wPolyLineStraight; + break; + case 'r': + points(context->prev_inx).pt_type = wPolyLineRound; + break; + default: + return C_CONTINUE; + } + } + if ((action>>8 == 'g') && (tempSegs(0).type == SEG_POLY) && (tempSegs(0).u.p.polyType == POLYLINE) ) { + tempSegs(0).u.p.polyType = FREEFORM; + context->subtype=FREEFORM; + context->open = FALSE; + CreatePolyAnchors( -1); + return C_CONTINUE; + } + if ((action>>8 == 'l') && (tempSegs(0).type == SEG_POLY) && (tempSegs(0).u.p.polyType == FREEFORM)) { + tempSegs(0).u.p.polyType = POLYLINE; + context->subtype=POLYLINE; + context->open = TRUE; + CreatePolyAnchors( -1); + return C_CONTINUE; + } + if ((action>>8 == 'f') && (tempSegs(0).type == SEG_POLY) && (tempSegs(0).u.p.polyType != POLYLINE )) { + tempSegs(0).type = SEG_FILPOLY; + context->type = SEG_FILPOLY; + context->filled = TRUE; + CreatePolyAnchors( -1); + return C_CONTINUE; + } + if ((action>>8 == 'u') && (tempSegs(0).type == SEG_FILPOLY) ) { + tempSegs(0).type = SEG_POLY; + context->type = SEG_POLY; + context->filled = FALSE; + CreatePolyAnchors( -1); + return C_CONTINUE; + } + //Delete or backspace deletes last selected index + if (action>>8 == 127 || action>>8 == 8) { + if (polyState == POLY_SELECTED && prev_inx >=0) { + if (selected_count >1) { + ErrorMessage( MSG_POLY_MULTIPLE_SELECTED ); + return C_CONTINUE; + } + if (points_da.cnt <= 3) { + ErrorMessage( MSG_POLY_SHAPES_3_SIDES ); + return C_CONTINUE; + } + for (int i=0;i<points_da.cnt;i++) { + point_selected(i) = FALSE; + if (i>=prev_inx && i<points_da.cnt-1) + points(i) = points(i+1); + } + points_da.cnt--; + select_da.cnt--; + selected_count=0; + tempSegs(0).u.p.cnt = points_da.cnt; + context->max_inx = points_da.cnt-1; + } + prev_inx = -1; + context->prev_inx = -1; + polyInx = -1; + polyState = POLY_SELECTED; + CreatePolyAnchors( -1); + InfoMessage(_("Point Deleted")); + return C_CONTINUE; + } + if (action>>8 != 32 && action>>8 != 13) return C_CONTINUE; + /* no break */ + case C_FINISH: + //copy changes back into track + if (polyState != POLY_SELECTED) return C_TERMINATE; + pts_t * oldPts = context->segPtr[segInx].u.p.pts; + void * newPts = (pts_t*)MyMalloc( points_da.cnt * sizeof (pts_t) ); + context->segPtr[segInx].u.p.pts = newPts; + context->segPtr->u.p.cnt = points_da.cnt; + context->orig = rotate_origin; + context->angle = rotate_angle; + for (int i=0; i<points_da.cnt; i++) { + pos = points(i).pt; + pos.x -= context->orig.x; + pos.y -= context->orig.y; + Rotate( &pos, zero, -context->angle ); + context->segPtr[segInx].u.p.pts[i].pt = pos; + context->segPtr[segInx].u.p.pts[i].pt_type = points(i).pt_type; + } + MyFree(oldPts); + oldPts = NULL; + polyState = POLY_NONE; + DYNARR_RESET(trkSeg_t,anchors_da); + DYNARR_RESET(trkSeg_t,tempSegs_da); + DrawNewTrack( context->trk ); + return C_TERMINATE; + case C_REDRAW: + if (polyState == POLY_NONE) return C_CONTINUE; + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt,trackGauge, wDrawColorBlack); + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + break; + default: + ; + } + return C_CONTINUE; +} + +void BuildCircleContext(drawModContext_t * context,int segInx) { + context->radius = fabs(context->segPtr[segInx].u.c.radius); + REORIGIN( context->pc, context->segPtr[segInx].u.c.center, context->angle, context->orig ); + PointOnCircle( &context->p0, context->segPtr[segInx].u.c.center, fabs(context->segPtr[segInx].u.c.radius), context->segPtr[segInx].u.c.a0 ); + REORIGIN( context->p0, context->p0, context->angle, context->orig ); + PointOnCircle( &context->p1, context->segPtr[segInx].u.c.center, fabs(context->segPtr[segInx].u.c.radius), context->segPtr[segInx].u.c.a0 + context->segPtr[segInx].u.c.a1); + REORIGIN( context->p1, context->p1, context->angle, context->orig ); + if (context->segPtr[segInx].u.c.a1<360) { + context->arc_angle = context->segPtr[segInx].u.c.a1; + PointOnCircle( &context->pm, context->segPtr[segInx].u.c.center, fabs(context->segPtr[segInx].u.c.radius), context->segPtr[segInx].u.c.a0 + (context->segPtr[segInx].u.c.a1/2)); + REORIGIN( context->pm, context->pm, context->angle, context->orig ); + coOrd cm; + cm = context->pm; + ANGLE_T a = FindAngle(context->p1,context->p0); + Rotate(&cm,context->p1,-a ); + context->disp = cm.x-context->p1.x; + } else { + context->pm = context->p0; + context->disp = 0; + context->arc_angle = 360.0; + } +} + +void CreateSelectedAnchor(coOrd pos) { + double d = tempD.scale*0.15; + DYNARR_APPEND(trkSeg_t,anchors_da,1); + int inx = anchors_da.cnt-1; + anchors(inx).type = SEG_FILCRCL; + anchors(inx).u.c.a0 = 0.0; + anchors(inx).u.c.a1 = 360.0; + anchors(inx).color = wDrawColorBlue; + anchors(inx).u.c.radius = d/2; + anchors(inx).u.c.center = pos; +} + +/* + * Rotate Object Dialogs. + * + * Each Draw object has a rotation origin which all the points are offset from. + * Formerly this has been set to the origin, but it doesn't have to be. By setting + * to a point on the shape this allows assembly by aligning parts to a common point on a base object. + * The angle is always set to zero when the Modify finishes but can be altered via Describe. + * + * First locate the origin, and then place a rotation arm which is (optionally) rotated about the origin. + * Also supports whole object translate using translate anchor. + **/ + +STATUS_T DrawGeomOriginMove( + wAction_t action, + coOrd pos, + drawModContext_t * context) { + + switch ( action&0xFF ) { + case C_START: + context->state = MOD_ORIGIN; + context->rotate_state = TRUE; + context->rot_moved = TRUE; + DYNARR_RESET(trkSeg_t,anchors_da); + CreateOriginAnchor(context->rot_center,FALSE); + if ((tempSegs(0).type == SEG_POLY || tempSegs(0).type == SEG_FILPOLY) && (context->prev_inx>=0)) { + CreateSelectedAnchor(points(context->prev_inx).pt); + } + InfoMessage("Move Origin Mode: Place Origin, 0-4, c or l, Enter or Esc"); + return C_CONTINUE; + break; + case wActionMove: + CreateOriginAnchor(context->rot_center, TRUE); + if ((tempSegs(0).type == SEG_POLY || tempSegs(0).type == SEG_FILPOLY) && (context->prev_inx>=0)) { + CreateSelectedAnchor(points(context->prev_inx).pt); + } + return C_CONTINUE; + break; + case C_DOWN: + if (context->state == MOD_ORIGIN || context->state == MOD_AFTER_ORIG) { + context->state = MOD_ORIGIN; + DYNARR_RESET(trkSeg_t,anchors_da); + if (IsClose(FindDistance(pos,context->rot_center))) { + pos = context->rot_center; + } else { + context->rot_center = pos; + } + CreateOriginAnchor(context->rot_center, TRUE); + if ((tempSegs(0).type == SEG_POLY || tempSegs(0).type == SEG_FILPOLY) && (context->prev_inx>=0)) { + CreateSelectedAnchor(points(context->prev_inx).pt); + } + } + return C_CONTINUE; + break; + case C_MOVE: + if (context->state == MOD_ORIGIN || context->state == MOD_AFTER_ORIG){ + context->rot_center = pos; + DYNARR_RESET(trkSeg_t,anchors_da); + CreateOriginAnchor(context->rot_center, TRUE); + if ((tempSegs(0).type == SEG_POLY || tempSegs(0).type == SEG_FILPOLY) && (context->prev_inx>=0)) { + CreateSelectedAnchor(points(context->prev_inx).pt); + } + } + return C_CONTINUE; + break; + case C_UP: + DYNARR_RESET(trkSeg_t,anchors_da); + if (context->state == MOD_ORIGIN) { + context->state = MOD_AFTER_ORIG; + } + CreateOriginAnchor(context->rot_center,FALSE); + if ((tempSegs(0).type == SEG_POLY || tempSegs(0).type == SEG_FILPOLY) && (context->prev_inx>=0)) { + CreateSelectedAnchor(points(context->prev_inx).pt); + } + return C_CONTINUE; + break; + case C_UPDATE: + DYNARR_RESET(trkSeg_t,anchors_da); + if (context->state == MOD_AFTER_ORIG) { + if (context->rot_center.x != context->rel_center.x && + context->rot_center.y != context->rel_center.y ) { + context->rel_center = context->rot_center; + CreateOriginAnchor(context->rot_center, FALSE); + if ((tempSegs(0).type == SEG_POLY || tempSegs(0).type == SEG_FILPOLY) && (context->prev_inx>=0)) { + CreateSelectedAnchor(points(context->prev_inx).pt); + } + } + } + return C_CONTINUE; + break; + case C_TEXT: + if ((context->state == MOD_ORIGIN || context->state == MOD_AFTER_ORIG) && + ((action>>8 >= '0' && action>>8 <= '1') || action>>8 == 'l' || action>>8 == 'm' || action>>8 == 'p')) { + // 0,1,2,3,4 -> reset rot center + if (action>>8 == '0') { + context->rot_center = zero; + } else if (action>>8 == '1') { + context->rot_center = context->p0; + } else if (action>>8 == '2') { + context->rot_center = context->p1; + } else if (tempSegs(0).type == SEG_POLY || tempSegs(0).type == SEG_FILPOLY) { + if (action>>8 == '3' || action>>8 == '4') { + context->rot_center = action>>8 == '3'?points(2).pt:points(3).pt; + } else if (action>>8 == 'l') { //"l" - last selected + if (context->prev_inx !=-1) { + context->rot_center = points(context->prev_inx).pt; + } + } else if (action>>8 == 'm') { + context->rot_center = FindCentroid(points_da.cnt,&points(0)); + } + } + if (action>>8 == 'p') { //"p" - points mode + MenuMode(0); + return C_CONTINUE; + } + context->rel_center = context->rot_center; + context->rot_angle = 0; + DYNARR_RESET(trkSeg_t,anchors_da); + context->state = MOD_AFTER_ORIG; + CreateOriginAnchor(context->rot_center, FALSE); + if ((tempSegs(0).type == SEG_POLY || tempSegs(0).type == SEG_FILPOLY) && (context->prev_inx>=0)) { + CreateSelectedAnchor(points(context->prev_inx).pt); + } + return C_CONTINUE; + } + break; + case C_FINISH: + context->rotate_state = FALSE; + context->state = MOD_STARTED; + return C_CONTINUE; + break; + default: + break; + } + return C_CONTINUE; + + +} + +/* + * Base Modify function for Draw objects. + */ STATUS_T DrawGeomModify( - coOrd orig, - ANGLE_T angle, - wIndex_t segCnt, - trkSeg_p segPtr, wAction_t action, coOrd pos, - wBool_t selected) + drawModContext_t * context) { ANGLE_T a; - coOrd p0, p1, pc; + coOrd p0, p1, pc, pm; static coOrd start_pos; static wIndex_t segInx; static EPINX_T segEp; static ANGLE_T segA1; - static int polyInx, inx_other, inx_line, inx_origin; + static int inx_other, inx_line, inx_origin; static BOOL_T corner_mode; + static BOOL_T polyMode; + static ANGLE_T original_angle; int inx, inx1, inx2; DIST_T d, d1, d2, dd; coOrd * newPts = NULL; - int mergePoints; tempSegs_da.cnt = 1; - switch ( action ) { - case C_DOWN: + switch ( action&0xFF ) { + case C_START: + if (!context->rotate_state && !context->rot_moved) { + context->rot_center.x = context->orig.x; + context->rot_center.y = context->orig.y; + } + context->state = MOD_STARTED; + context->rotate_state = FALSE; + context->last_inx=-1; + lineInx = -1; + curveInx = -1; segInx = -1; - corner_mode = FALSE; - DistanceSegs( orig, angle, segCnt, segPtr, &pos, &segInx ); + polyMode = FALSE; + DistanceSegs( context->orig, context->angle, context->segCnt, context->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 ) { + context->type = context->segPtr[segInx].type; + switch(context->type) { + case SEG_TBLEDGE: + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + REORIGIN( p0, context->segPtr[segInx].u.l.pos[0], context->angle, context->orig ); + REORIGIN( p1, context->segPtr[segInx].u.l.pos[1], context->angle, context->orig ); + tempSegs(0).type = SEG_STRLIN; + tempSegs(0).color = wDrawColorRed; + tempSegs(0).u.l.pos[0] = p0; + tempSegs(0).u.l.pos[1] = p1; + tempSegs(0).width = 0; + tempSegs_da.cnt = 1; + context->p0 = p0; + context->p1 = p1; + CreateLineAnchors(-1,p0,p1); + break; + case SEG_CRVLIN: + case SEG_FILCRCL: + BuildCircleContext(context,segInx); + tempSegs(0).type = SEG_CRVLIN; + tempSegs(0).color = wDrawColorRed; + tempSegs(0).u.c.center = context->pc; + tempSegs(0).u.c.radius = context->radius; + tempSegs(0).u.c.a0 = context->segPtr[segInx].u.c.a0; + tempSegs(0).u.c.a1 = context->segPtr[segInx].u.c.a1; + tempSegs(0).width = 0; + tempSegs_da.cnt = 1; + if (tempSegs(0).u.c.a1<360.0) { + CreateCurveAnchors(-1,context->pm,context->pc,context->p0,context->p1); + } else + CreateCircleAnchor(FALSE,tempSegs(0).u.c.center,tempSegs(0).u.c.radius,FindAngle(tempSegs(0).u.c.center,pos)); + context->last_inx = 2; + break; + case SEG_POLY: + case SEG_FILPOLY: + if (context->segPtr[segInx].u.p.polyType !=RECTANGLE) { + polyMode = TRUE; + return DrawGeomPolyModify(action,pos,context); + } else { + tempSegs(0).type = SEG_POLY; + tempSegs(0).color = wDrawColorRed; + DYNARR_RESET( pts_t, points_da); + for (int inx=0;inx<context->segPtr->u.p.cnt;inx++) { + DYNARR_APPEND(pts_t, points_da,3); + REORIGIN( points(inx).pt, context->segPtr[segInx].u.p.pts[inx].pt, context->angle, context->orig ); + } + tempSegs(0).u.p.pts = &points(0); + tempSegs(0).u.p.cnt = points_da.cnt; + tempSegs(0).width = 0; + tempSegs_da.cnt = 1; + context->p0 = points(0).pt; + CreateBoxAnchors(-1,&context->segPtr[segInx].u.p.pts[0]); + context->p0 = points(0).pt; + context->p1 = points(1).pt; + } + break; + case SEG_TEXT: + InfoMessage("Text can only be modified with Describe"); + wBeep(); + return C_ERROR; + default: + ; + } + InfoMessage("Points Mode - Select and drag Anchor Point"); + return C_CONTINUE; + break; + case wActionMove: + if (context->rotate_state) return DrawGeomOriginMove(action,pos,context); + if (polyMode) return DrawGeomPolyModify(action,pos,context); + DYNARR_RESET(trkSeg_t,anchors_da); + switch( context->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; + DYNARR_RESET(trkSeg_t,anchors_da); + CreateLineAnchors(lineInx,context->p0,context->p1); + dd = FindDistance( context->p0, pos ); + if ( IsClose(dd)) { + CreateMovingAnchor(context->p0,TRUE); + } else { + dd = FindDistance( context->p1, pos ); + if ( IsClose(dd)) { + CreateMovingAnchor(context->p1,TRUE); + } + } + break; + case SEG_CRVLIN: + case SEG_FILCRCL: + DYNARR_RESET(trkSeg_t,anchors_da); + if (tempSegs(0).u.c.a1 < 360.0) + CreateCurveAnchors(curveInx,context->pm,context->pc,context->p0,context->p1); + dd = FindDistance( context->p0, pos ); + if ( IsClose(dd)) { + CreateMovingAnchor(context->p0,TRUE); + } else { + dd = FindDistance( context->p1, pos ); + if ( IsClose(dd)) { + CreateMovingAnchor(context->p1,TRUE); + } else { + dd = FindDistance( context->pm, pos ); + if ( IsClose(dd)) { + CreateMovingAnchor(context->pm,TRUE); + } + } + } + break; + case SEG_POLY: + case SEG_FILPOLY:; + CreateBoxAnchors(-1,&points(0)); + break; + default:; + } + return C_CONTINUE; + break; + case C_DOWN: + if (context->rotate_state) return DrawGeomOriginMove(action,pos,context); + if (polyMode) return DrawGeomPolyModify(action,pos,context); + corner_mode = FALSE; + segInx = 0; + context->state = MOD_STARTED; + tempSegs(0).width = context->segPtr[segInx].width; + tempSegs(0).color = context->segPtr[segInx].color; + switch ( context->type ) { + case SEG_TBLEDGE: + if ( MyGetKeyState() & WKEY_CTRL ) + OnTableEdgeEndPt( NULL, &pos ); + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + p0 = context->p0; + p1 = context->p1; + lineInx = -1; + dd = FindDistance( p0, pos ); + if ( IsClose(dd)) { + lineInx = 0; + } else { + dd = FindDistance( p1, pos ); + if ( IsClose(dd)) { + lineInx = 1; + } + } + if (lineInx < 0 ) { + InfoMessage( _("Not close to end of line")); + } else { + InfoMessage("End selected, drag to reposition"); + context->state = MOD_SELECTED_PT; + } + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).width = 0; + tempSegs(0).type = context->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; + tempSegs(0).u.l.option = context->segPtr[segInx].u.l.option; segA1 = FindAngle( p1, p0 ); + tempSegs_da.cnt = 1; + CreateLineAnchors(lineInx,p0,p1); 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 = fabs(segPtr[segInx].u.c.radius); - if (segPtr[segInx].u.c.a1 >= 360.0) { + curveInx = -1; + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).width = 0; + tempSegs(0).type = context->type; + tempSegs(0).u.c.center = context->pc; + tempSegs(0).u.c.radius = context->radius; + if (context->segPtr[segInx].u.c.a1 >= 360.0) { tempSegs(0).u.c.a0 = 0.0; tempSegs(0).u.c.a1 = 360.0; + InfoMessage("Drag to Change Radius"); + CreateCircleAnchor(TRUE,tempSegs(0).u.c.center,tempSegs(0).u.c.radius,FindAngle(tempSegs(0).u.c.center,pos)); } 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, fabs(segPtr[segInx].u.c.radius), segPtr[segInx].u.c.a0+angle ); - PointOnCircle( &p1, pc, fabs(segPtr[segInx].u.c.radius), segPtr[segInx].u.c.a0+segPtr[segInx].u.c.a1+angle ); + p0 = context->p0; + p1 = context->p1; + pm = context->pm; + pc = context->pc; + tempSegs(0).u.c.a0 = FindAngle(context->pc,context->p0); + tempSegs(0).u.c.a1 = DifferenceBetweenAngles(FindAngle(context->pc,context->p0),FindAngle(context->pc,context->p1)); + tempSegs(0).u.c.center = context->pc; + tempSegs(0).u.c.radius = context->radius; + curveInx = -1; + dd = FindDistance( p0, pos ); + if ( IsClose(dd)) { + curveInx = 0; + } else { + dd = FindDistance( p1, pos ); + if ( IsClose(dd)) { + curveInx = 1; + } else { + dd = FindDistance( pm, pos ); + if ( IsClose(dd)) { + curveInx = 2; + } + } + } + if (curveInx < 0) { + InfoMessage( _("Not close to ends or middle of mine, reselect")); + return C_CONTINUE; + } else { + if (curveInx <2 ) + InfoMessage("Drag to move end, +Ctrl to lock to other objects"); + else + InfoMessage("Drag to change radius"); + } + tempSegs_da.cnt = 1; + if (tempSegs(0).u.c.a1 < 360.0) + CreateCurveAnchors(curveInx,pm,pc,p0,p1); } - + context->state = MOD_SELECTED_PT; 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; - tempSegs(0).u.p.polyType = segPtr[segInx].u.p.polyType; - 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++ ) { + for ( inx=0; inx<4; inx++ ) { + if (IsClose(FindDistance(pos,points(inx).pt))) { + corner_mode = TRUE; + polyInx = inx; + break; + } p0 = pos; - dd = LineDistance( &p0, points( inx==0?segPtr[segInx].u.p.cnt-1:inx-1), points( inx ) ); + dd = LineDistance( &p0, points( inx==0?3:inx-1).pt, points( inx ).pt ); if ( d > dd ) { d = dd; - polyInx = inx; + inx_line = inx; } } - if (segPtr[segInx].u.p.polyType == RECTANGLE) { - d1 = FindDistance( points(polyInx), pos ); - d2 = FindDistance( points(polyInx==0?segPtr[segInx].u.p.cnt-1:polyInx-1), pos ); + if (!corner_mode) { + d1 = FindDistance( points(inx_line).pt, pos ); + d2 = FindDistance( points(inx_line==0?3:inx_line-1).pt, pos ); if (d2<d1) { - inx_line = polyInx; - polyInx = polyInx==0?segPtr[segInx].u.p.cnt-1:polyInx-1; + polyInx = inx_line==0?3:inx_line-1; } else { - inx_line = polyInx==0?segPtr[segInx].u.p.cnt-1:polyInx-1; + polyInx = inx_line; } - //polyInx is closest point - inx1 = (polyInx==0?3:polyInx-1); //Prev point - inx2 = (polyInx==3?0:polyInx+1); //Next Point - inx_origin = (inx2==3?0:inx2+1); //Opposite point - if ( IsClose(d2) || IsClose(d1) ) { - corner_mode = TRUE; - pos = points(polyInx); - start_pos = pos; - DrawArrowHeads( &tempSegs(1), pos, FindAngle(points(polyInx),points(inx_origin)), TRUE, wDrawColorRed ); - tempSegs_da.cnt = 6; - InfoMessage( _("Drag to Move Corner Point")); - } else { - corner_mode = FALSE; - start_pos = pos; - pos.x = (points(polyInx).x + points(inx_line).x)/2; - pos.y = (points(polyInx).y + points(inx_line).y)/2; - DrawArrowHeads( &tempSegs(1), pos, FindAngle(points(polyInx),pos)+90, TRUE, wDrawColorRed ); - tempSegs_da.cnt = 6; - InfoMessage( _("Drag to Move Edge ")); - } - return C_CONTINUE; + } + inx1 = (polyInx==0?3:polyInx-1); //Prev point + inx2 = (polyInx==3?0:polyInx+1); //Next Point + inx_origin = (inx2==3?0:inx2+1); //Opposite point + if ( corner_mode ) { + start_pos = pos; + pos = points(inx).pt; + DYNARR_SET(trkSeg_t,anchors_da,5); + DrawArrowHeads( &anchors(0), pos, FindAngle(points(polyInx).pt,points(inx_origin).pt), TRUE, wDrawColorRed ); + InfoMessage( _("Drag to Move Corner Point")); } else { - 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; + start_pos = pos; + pos.x = (points(inx_line).pt.x + points(inx_line==0?3:inx_line-1).pt.x)/2; + pos.y = (points(inx_line).pt.y + points(inx_line==0?3:inx_line-1).pt.y)/2; + DYNARR_SET(trkSeg_t,anchors_da,5); + DrawArrowHeads( &anchors(0), pos, FindAngle(points(polyInx).pt,pos)+90, TRUE, wDrawColorRed ); + InfoMessage( _("Drag to Move Edge ")); } - p1=p0; - break; + context->state = MOD_SELECTED_PT; + return C_CONTINUE; case SEG_TEXT: segInx = -1; return C_ERROR; @@ -690,7 +2057,7 @@ STATUS_T DrawGeomModify( segEp = 0; else { segEp = 1; - switch ( segPtr[segInx].type ) { + switch ( context->type ) { case SEG_TBLEDGE: case SEG_STRLIN: case SEG_DIMLIN: @@ -701,176 +2068,454 @@ STATUS_T DrawGeomModify( ; } } + UndrawNewTrack( context->trk ); 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 ); + if (context->rotate_state) return DrawGeomOriginMove(action,pos,context); + + if (polyMode) return DrawGeomPolyModify(action,pos,context); + if (context->state != MOD_SELECTED_PT) return C_CONTINUE; + switch (tempSegs(0).type) { + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + if ( (MyGetKeyState() & WKEY_SHIFT) != 0) { + d = FindDistance( pos, tempSegs(0).u.l.pos[1-lineInx] ); + Translate( &pos, tempSegs(0).u.l.pos[1-lineInx], segA1, d ); + } else if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_SHIFT ) { + OnTrack( &pos, FALSE, FALSE ); + CreateEndAnchor(pos,TRUE); + } + break; + default: + break; } int prior_pnt, next_pnt, orig_pnt; ANGLE_T prior_angle, next_angle, line_angle; tempSegs_da.cnt = 1; switch (tempSegs(0).type) { case SEG_TBLEDGE: - + if ( MyGetKeyState() & WKEY_CTRL ) + OnTableEdgeEndPt( NULL, &pos ); + /* no break */ 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] ) ); + if (lineInx<0 || lineInx>1) return C_CONTINUE; + tempSegs(0).u.l.pos[lineInx] = pos; + InfoMessage( _("Length = %0.3f Angle = %0.3f"), FindDistance( tempSegs(0).u.l.pos[lineInx], tempSegs(0).u.l.pos[1-lineInx] ), FindAngle( tempSegs(0).u.l.pos[1-lineInx], tempSegs(0).u.l.pos[lineInx] ) ); + tempSegs_da.cnt = 1; + context->p0 = tempSegs(0).u.l.pos[0]; + context->p1 = tempSegs(0).u.l.pos[1]; + p0 = context->p0; + p1 = context->p1; + CreateLineAnchors(lineInx, p0, p1); break; 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 ); + tempSegs(0).u.c.radius = FindDistance( context->pc, pos ); + CreateCircleAnchor(TRUE,tempSegs(0).u.c.center,tempSegs(0).u.c.radius,FindAngle(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; + if (context->state != MOD_SELECTED_PT) return C_CONTINUE; + if (curveInx < 0 || curveInx > 2) return C_CONTINUE; + p0 = context->p0; + p1 = context->p1; + pc = context->pc; + pm = context->pm; + if ( (MyGetKeyState() & WKEY_SHIFT) != 0) { + //Preserve Radius, Change swept angle + a = FindAngle( pc, pos ); + if (curveInx==0) { + tempSegs(0).u.c.a1 = NormalizeAngle(FindAngle(pc,p1)-a); + tempSegs(0).u.c.a0 = a; + } else if (curveInx ==1) { + tempSegs(0).u.c.a1 = NormalizeAngle(a-tempSegs(0).u.c.a0); + } + PointOnCircle(&p0,pc,context->radius,tempSegs(0).u.c.a0); + PointOnCircle(&p1,pc,context->radius,tempSegs(0).u.c.a0+tempSegs(0).u.c.a1); + context->p0 = p0; + context->p1 = p1; + PointOnCircle(&pm,pc,context->radius,tempSegs(0).u.c.a0+(tempSegs(0).u.c.a1/2)); + context->pm = pm; } else { - tempSegs(0).u.c.a1 = NormalizeAngle(a-tempSegs(0).u.c.a0); + if (curveInx == 0 || curveInx == 1) { + p0 = context->p0; + p1 = context->p1; + /* Preserve Curve "Deflection" */ + d = context->disp; // Original Deflection + if (curveInx == 0) { + context->p0 = p0 = pos; + } else { + context->p1 = p1 = pos; + } + coOrd posx; //Middle of chord + posx.x = (p1.x-p0.x)/2.0+p0.x; + posx.y = (p1.y-p0.y)/2.0+p0.y; //Middle of chord + ANGLE_T a0 = FindAngle( p1, p0 ); + DIST_T d0 = FindDistance( p0, p1 )/2.0; + DIST_T r = 1000.0; + if ( fabs(d) >= 0.01 ) { + d2 = sqrt( d0*d0 + d*d )/2.0; + r = d2*d2*2.0/d; + if ( fabs(r) > 1000.0 ) + r = ((r > 0)? 1 : -1) *1000.0; + } else { + r = ((r > 0) ? 1 : -1 ) *1000.0; + } + a0 -= 90.0; + if (r<0) { + coOrd pt = p0; + p0 = p1; + p1 = pt; + a0 += 180.0; + } + context->radius = tempSegs(0).u.c.radius = fabs(r); + Translate( &pc, posx, a0, fabs(r)-fabs(d) ); + context->pc = tempSegs(0).u.c.center = pc; + tempSegs(0).u.c.a0 = FindAngle(pc,p0); + context->arc_angle = tempSegs(0).u.c.a1 = NormalizeAngle(FindAngle(pc,p1)-FindAngle(pc,p0)); + PointOnCircle(&pm,pc,context->radius,tempSegs(0).u.c.a0+(tempSegs(0).u.c.a1/2.0)); + context->pm = pm; + } else { + //Change radius using chord + p0 = context->p0; + p1 = context->p1; + pm = context->pm; + ANGLE_T a0 = FindAngle( p1, p0 ); + DIST_T d0 = FindDistance( p0, p1 )/2.0; + coOrd pos2 = pos; + Rotate( &pos2, p1, -a0 ); + pos2.x -= p1.x; + DIST_T r = 1000.0; + if ( fabs(pos2.x) >= 0.01 ) { + d2 = sqrt( d0*d0 + pos2.x*pos2.x )/2.0; + r = d2*d2*2.0/pos2.x; + if ( fabs(r) > 1000.0 ) + r = ((r > 0) ? 1 : -1 ) *1000.0; + } else { + r = ((r > 0) ? 1 : -1 ) *1000.0; + } + coOrd posx; //Middle of chord + posx.x = (p1.x-p0.x)/2.0 + p0.x; + posx.y = (p1.y-p0.y)/2.0 + p0.y; + a0 -= 90.0; + if (r<0) { + coOrd pt = p0; + p0 = p1; + p1 = pt; + a0 += 180.0; + } + Translate( &pc, posx, a0, fabs(r) - fabs(pos2.x) ); + context->pc = tempSegs(0).u.c.center = pc; + context->radius = tempSegs(0).u.c.radius = fabs(r); + a0 = FindAngle( pc, p0 ); + ANGLE_T a1 = FindAngle( pc, p1 ); + tempSegs(0).u.c.a0 = a0; + tempSegs(0).u.c.a1 = NormalizeAngle(a1-a0); + PointOnCircle(&pm,pc,context->radius,a0+(NormalizeAngle(a1-a0)/2)); + context->p0 = p0; + context->p1 = p1; + context->pm = pm; + context->disp = pos2.x; + } } + if (tempSegs(0).u.c.a1 < 360.0) + CreateCurveAnchors(curveInx,pm,pc,p0,p1); } break; case SEG_POLY: case SEG_FILPOLY: - switch (tempSegs(0).u.p.polyType) { - case RECTANGLE: - if (!corner_mode) { - /* Constrain movement to be perpendicular */ - d = FindDistance(start_pos, pos); - line_angle = NormalizeAngle(FindAngle(points(inx_line),points(polyInx))-90); - a = FindAngle(pos,start_pos); - Translate( &pos, start_pos, line_angle, - d*cos(D2R(line_angle-a))); - } - d = FindDistance(start_pos,pos); - a = FindAngle(start_pos, pos); - start_pos = pos; - prior_pnt = (polyInx == 0)?3:polyInx-1; - next_pnt = (polyInx == 3)?0:polyInx+1; - orig_pnt = (prior_pnt == 0)?3:prior_pnt-1; - Translate( &points(polyInx), points(polyInx), a, d); - d = FindDistance(points(orig_pnt),points(polyInx)); - a = FindAngle(points(orig_pnt),points(polyInx)); - prior_angle = FindAngle(points(orig_pnt),points(prior_pnt)); - Translate( &points(prior_pnt), points(orig_pnt), prior_angle, d*cos(D2R(prior_angle-a))); - next_angle = FindAngle(points(orig_pnt),points(next_pnt)); - Translate( &points(next_pnt), points(orig_pnt), next_angle, d*cos(D2R(next_angle-a))); - if (!corner_mode) { - pos.x = (points(polyInx).x + points(inx_line).x)/2; - pos.y = (points(polyInx).y + points(inx_line).y)/2; - DrawArrowHeads( &tempSegs(1), pos, FindAngle(points(polyInx),points(inx_line))+90, TRUE, wDrawColorRed ); - tempSegs_da.cnt = 6; - InfoMessage( _("Drag to Move Edge")); - } else { - pos = points(polyInx); - DrawArrowHeads( &tempSegs(1), pos, FindAngle(points(polyInx),points(inx_origin)), TRUE, wDrawColorRed ); - tempSegs_da.cnt = 6; - InfoMessage( _("Drag to Move Corner Point")); - } - break; - default: - points(polyInx) = pos; + if (!corner_mode) { + /* Constrain movement to be perpendicular */ + d = FindDistance(start_pos, pos); + line_angle = NormalizeAngle(FindAngle(points(inx_line).pt,points(inx_line==3?0:inx_line+1).pt)); + a = FindAngle(pos,start_pos); + Translate( &pos, start_pos, line_angle, - d*cos(D2R(line_angle-a))); } - + d = FindDistance(start_pos,pos); + a = FindAngle(start_pos, pos); + start_pos = pos; + prior_pnt = (polyInx == 0)?3:polyInx-1; + next_pnt = (polyInx == 3)?0:polyInx+1; + orig_pnt = (prior_pnt == 0)?3:prior_pnt-1; + Translate( &points(polyInx).pt, points(polyInx).pt, a, d); + d = FindDistance(points(orig_pnt).pt,points(polyInx).pt); + a = FindAngle(points(orig_pnt).pt,points(polyInx).pt); + prior_angle = FindAngle(points(orig_pnt).pt,points(prior_pnt).pt); + Translate( &points(prior_pnt).pt, points(orig_pnt).pt, prior_angle, d*cos(D2R(prior_angle-a))); + next_angle = FindAngle(points(orig_pnt).pt,points(next_pnt).pt); + Translate( &points(next_pnt).pt, points(orig_pnt).pt, next_angle, d*cos(D2R(next_angle-a))); + if (!corner_mode) { + pos.x = (points(inx_line).pt.x + points(inx_line==0?3:inx_line-1).pt.x)/2; + pos.y = (points(inx_line).pt.y + points(inx_line==0?3:inx_line-1).pt.y)/2; + DYNARR_SET(trkSeg_t,anchors_da,5); + DrawArrowHeads( &anchors(0), pos, FindAngle(points(inx_line).pt,points(inx_line==0?3:inx_line-1).pt)+90, TRUE, wDrawColorRed ); + InfoMessage( _("Drag to Move Edge")); + } else { + pos = points(polyInx).pt; + DYNARR_SET(trkSeg_t,anchors_da,5); + DrawArrowHeads( &anchors(0), pos, FindAngle(points(polyInx).pt,points(inx_origin).pt), TRUE, wDrawColorRed ); + InfoMessage( _("Drag to Move Corner Point")); + } + context->p0 = points(0).pt; + context->p1 = points(1).pt; break; default: ; } - return C_CONTINUE; case C_UP: + + if (context->rotate_state) return DrawGeomOriginMove(action, pos, context); + + if (polyMode) { + int rc; + rc = DrawGeomPolyModify(action,pos,context); + if (context->prev_inx != -1) + context->state = MOD_AFTER_PT; + return rc; + } + 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; + p0 = context->p0; + p1 = context->p1; + context->abs_angle = FindAngle(p0,p1); + context->length = FindDistance(p0,p1); + CreateLineAnchors(lineInx,p0,p1); + context->last_inx = lineInx; break; case SEG_CRVLIN: case SEG_FILCRCL: - if ( tempSegs(0).u.c.a1 >= 360.0 ) { - segPtr[segInx].u.c.radius = fabs(tempSegs(0).u.c.radius); + if ( (tempSegs(0).type == SEG_FILCRCL) || (tempSegs(0).u.c.a1 == 360.0 || tempSegs(0).u.c.a1 == 0.0) ) { + context->radius = fabs(tempSegs(0).u.c.radius); + context->arc_angle = 360.0; + CreateCircleAnchor(FALSE,tempSegs(0).u.c.center,tempSegs(0).u.c.radius,FindAngle(tempSegs(0).u.c.center,pos)); } 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; - } + p0 = context->p0; + p1 = context->p1; + pc = context->pc; + pm = context->pm; + context->radius = fabs(tempSegs(0).u.c.radius); + context->arc_angle = tempSegs(0).u.c.a1; + CreateCurveAnchors(curveInx,pm,pc,p0,p1); + a = FindAngle(p1,p0); + Rotate(&pm,p1,-a); + context->disp = pm.x-p1.x; } + context->last_inx = curveInx; break; case SEG_POLY: case SEG_FILPOLY: - switch(tempSegs(0).u.p.polyType) { - case RECTANGLE: - for (int i=0;i<4;i++) { - pos = points(i); - pos.x -= orig.x; - pos.y -= orig.y; - Rotate( &pos, zero, -angle ); - segPtr[segInx].u.p.pts[i] = pos; - } - break; - default: - 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 ); + CreateBoxAnchors(-1,tempSegs(0).u.p.pts); + context->width = FindDistance(tempSegs(0).u.p.pts[0].pt,tempSegs(0).u.p.pts[1].pt); + context->height = FindDistance(tempSegs(0).u.p.pts[1].pt,tempSegs(0).u.p.pts[2].pt); + context->last_inx = polyInx; + if (corner_mode) context->last_inx +=5; + break; + default: + ; + } + context->state = MOD_AFTER_PT; + curveInx = -1; + lineInx = -1; + polyInx = -1; + InfoMessage("Enter/Space to Accept, ESC to Reject"); + return C_CONTINUE; + case C_UPDATE: + if (context->rotate_state) return DrawGeomOriginMove(action, pos, context); + + if (polyMode) return DrawGeomPolyModify(action,pos,context); + if (context->state == MOD_AFTER_PT) { + switch (tempSegs(0).type) { + case SEG_TBLEDGE: + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + context->abs_angle = NormalizeAngle(context->abs_angle); + Translate(&tempSegs(0).u.l.pos[1],tempSegs(0).u.l.pos[0],context->abs_angle,context->length); + CreateLineAnchors(context->last_inx,tempSegs(0).u.l.pos[0],tempSegs(0).u.l.pos[1]); + context->p0 = tempSegs(0).u.l.pos[0]; + context->p1 = tempSegs(0).u.l.pos[1]; + break; + case SEG_CRVLIN: + case SEG_FILCRCL: + if (tempSegs(0).u.c.a1 == 360.0 || tempSegs(0).u.c.a1 == 0.0) { + tempSegs(0).u.c.a1 = 360.0; + tempSegs(0).u.c.radius = context->radius; + Translate(&p0,tempSegs(0).u.c.center,tempSegs(0).u.c.a0,tempSegs(0).u.c.radius); + context->p0 = p0; + context->p1 = p0; + context->pm = p0; + context->pc = tempSegs(0).u.c.center; break; } - } + if (context->radius < 0) { //swap ends + context->radius = fabs(context->radius); + p1 = context->p0; + p0 = context->p1; + a = FindAngle(context->pc,context->pm); + Translate(&pm,context->pm,a+180,2*context->disp); + Translate(&pc,pm,a,context->radius); + context->pm = pm; + context->pc = pc; + context->p0 = p0; + context->p1 = p1; + } else { + pm = context->pm; + pc = context->pc; + p0 = context->p0; + p1 = context->p1; + } + if (context->last_inx == 0) { + p1 = context->p1; + pc = context->pc; + a = FindAngle(p1,pc); + Translate(&pc,p1,a,context->radius); + context->pc = pc; + PointOnCircle( &p0, context->pc, context->radius, NormalizeAngle(a+180-context->arc_angle) ); + context->p0 = p0; + PointOnCircle( &pm, context->pc, context->radius, NormalizeAngle(a+180-(context->arc_angle/2))); + context->pm = pm; + } else if (context->last_inx == 1) { + p0 = context->p0; + pc = context->pc; + a = FindAngle(p0,pc); + Translate(&pc,p0,a,context->radius); + context->pc = pc; + PointOnCircle( &p1, context->pc, context->radius, NormalizeAngle(a+180+context->arc_angle) ); + context->p1 = p1; + PointOnCircle( &pm, context->pc, context->radius, NormalizeAngle(a+180+(context->arc_angle/2))); + context->pm = pm; + } else { // Middle if neither + a = FindAngle(context->pm,context->pc); + Translate(&pc,context->pm,a,context->radius); + context->pc = pc; + PointOnCircle( &p1, context->pc, context->radius, NormalizeAngle(FindAngle(context->pc,context->pm)+(context->arc_angle/2)) ); + PointOnCircle( &p0, context->pc, context->radius, NormalizeAngle(FindAngle(context->pc,context->pm)-(context->arc_angle/2)) ); + context->p1 = p1; + context->p0 = p0; + } + a = FindAngle(p1,p0); + Rotate(&pm,p1,-a); + context->disp = pm.x-p1.x; + tempSegs(0).u.c.center = context->pc; + tempSegs(0).u.c.a0 = FindAngle(context->pc,context->p0); + tempSegs(0).u.c.a1 = NormalizeAngle(FindAngle(context->pc,context->p1)-tempSegs(0).u.c.a0); + if (tempSegs(0).u.c.a1 == 0.0) tempSegs(0).u.c.a1 = 360.0; + tempSegs(0).u.c.radius = context->radius; + CreateCurveAnchors(context->last_inx,context->pm,context->pc,context->p0,context->p1); + break; + case SEG_POLY: + case SEG_FILPOLY: + a = NormalizeAngle(FindAngle(points(0).pt,points(3).pt)); + Translate( &points(3).pt, points(0).pt, a, context->height); + Translate( &points(2).pt, points(1).pt, a, context->height); + a = NormalizeAngle(FindAngle(points(0).pt,points(1).pt));; + Translate( &points(1).pt, points(0).pt, a, context->width); + Translate( &points(2).pt, points(3).pt, a, context->width); + CreateBoxAnchors(context->last_inx,&points(0)); + break; + default: + break; + } + } + break; + case C_TEXT: + if (context->rotate_state) DrawGeomOriginMove(action, pos, context); - coOrd * oldPts = segPtr[segInx].u.p.pts; - newPts = (coOrd*)MyMalloc( tempSegs(0).u.p.cnt * sizeof (coOrd) ); - int size = segPtr[segInx].u.p.cnt; - memcpy( newPts, segPtr[segInx].u.p.pts, (size) * sizeof (coOrd) ); - segPtr[segInx].u.p.pts = newPts; - MyFree(oldPts); + if (polyMode) return DrawGeomPolyModify(action,pos,context); + if (action>>8 == 'o') { + MenuMode(1); + } - 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++; + if (action>>8 != 32 && action>>8 != 13) return C_CONTINUE; + /* no break */ + case C_FINISH: + if (polyMode) { + DrawGeomPolyModify(action,pos,context); + context->segPtr[segInx].type = context->type; + context->segPtr[segInx].u.p.polyType = context->subtype; + return C_TERMINATE; + } + //copy changes back into track + context->orig.x = context->rot_center.x; + context->orig.y = context->rot_center.y; + context->rot_moved = FALSE; + context->angle = 0.0; + switch (tempSegs(0).type) { + case SEG_TBLEDGE: + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + for (int i=0;i<2;i++) { + pos = i==0?context->p0:context->p1; + pos.x -= context->rot_center.x; + pos.y -= context->rot_center.y; + context->segPtr[segInx].u.l.pos[i] = pos; } - - pos = points(polyInx); - if ( mergePoints ) { - for (inx=polyInx+1; inx<segPtr[segInx].u.p.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; + break; + case SEG_CRVLIN: + case SEG_FILCRCL: + pc = context->pc; + pc.x -= context->rot_center.x; + pc.y -= context->rot_center.y; + context->segPtr[segInx].u.c.center = pc; + p0 = context->p0; + p0.x -= context->rot_center.x; + p0.y -= context->rot_center.y; + p1 = context->p1; + p1.x -= context->rot_center.x; + p1.y -= context->rot_center.y; + context->segPtr[segInx].u.c.a0 = FindAngle(pc,p0); + context->segPtr[segInx].u.c.a1 = NormalizeAngle(FindAngle(pc,p1)-FindAngle(pc,p0)); + if (context->segPtr[segInx].u.c.a1 == 0) context->segPtr[segInx].u.c.a1 = 360.0; + context->segPtr[segInx].u.c.radius = context->radius; + break; + case SEG_POLY: + case SEG_FILPOLY: + for (int i=0;i<4;i++) { + pos = points(i).pt; + pos.x -= context->rot_center.x; + pos.y -= context->rot_center.y; + context->segPtr[segInx].u.p.pts[i].pt = pos; } - pos.x -= orig.x; - pos.y -= orig.y; - Rotate( &pos, zero, -angle ); - segPtr[segInx].u.p.pts[polyInx] = pos; - } - break; - default: - ; + break; + default: + break; } + context->state = MOD_NONE; + context->rotate_state = FALSE; + context->last_inx = -1; + DYNARR_RESET(trkSeg_t,anchors_da); + DYNARR_RESET(trkSeg_t,tempSegs_da); + DrawNewTrack( context->trk ); return C_TERMINATE; + case C_REDRAW: + if (polyMode) return DrawGeomPolyModify(action,pos,context); + if (context->state == MOD_NONE) return C_CONTINUE; + DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack); + DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack ); + break; + case C_CANCEL: + case C_CONFIRM: + case C_TERMINATE: + context->state = MOD_NONE; + context->rotate_state = FALSE; + context->rot_moved = FALSE; + DYNARR_RESET(trkSeg_t,anchors_da); + DYNARR_RESET(trkSeg_t,tempSegs_da); + break; default: ; } - return C_ERROR; + return C_CONTINUE; } diff --git a/app/bin/drawgeom.h b/app/bin/drawgeom.h index d9f54f8..45814d7 100644 --- a/app/bin/drawgeom.h +++ b/app/bin/drawgeom.h @@ -47,7 +47,8 @@ #define OP_FILLBOX (16) #define OP_FILLPOLY (17) #define OP_BEZLIN (18) -#define OP_LAST (OP_BEZLIN) +#define OP_POLYLINE (19) +#define OP_LAST (OP_POLYLINE) typedef struct { void (*message)( char *, ... ); @@ -55,20 +56,74 @@ typedef struct { drawCmd_t *D; long Op; wDrawColor Color; - long Width; + long line_Width; + double width; + ANGLE_T angle; + double length; + double radius; long benchOption; + drawLineType_e lineType; int State; + int index; curveData_t ArcData; ANGLE_T ArcAngle; int Started; BOOL_T Changed; } drawContext_t; +typedef enum {MOD_NONE, MOD_STARTED, MOD_SELECTED_PT, MOD_AFTER_PT, + MOD_ORIGIN, MOD_AFTER_ORIG } ModState_e; + +typedef struct { + void (*message)( char *, ... ); + void (*Redraw)( void ); + drawCmd_t *D; + double length; + ANGLE_T rel_angle; + double radius; + ANGLE_T arc_angle; + int last_inx; + ANGLE_T abs_angle; + double height; + double width; + int prev_inx; + int max_inx; + track_p trk; + char type; + coOrd orig; //Origin Pos + ANGLE_T angle; //Origin Angle + wIndex_t segCnt; + trkSeg_p segPtr; + wBool_t selected; + wBool_t circle; + ModState_e state; + coOrd rel_center; + coOrd rot_center; + wBool_t rot_moved; + coOrd translate_center; + coOrd moved; + coOrd arm; + coOrd new_arm; + ANGLE_T rot_angle; + coOrd p0; + coOrd p1; + coOrd pm; + coOrd pc; + DIST_T disp; + wBool_t rotate_state; + wBool_t open; + wBool_t filled; + PolyType_e subtype; + } drawModContext_t; + +typedef enum {LENGTH_UPDATE, WIDTH_UPDATE} drawUpdateType_e; + extern drawContext_t * drawContext; extern wDrawColor lineColor; extern long lineWidth; 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 ); -#endif //HAVE_DRAWGEOM_H
\ No newline at end of file +STATUS_T DrawGeomMouse( wAction_t, coOrd, drawContext_t *); +STATUS_T DrawGeomModify( wAction_t, coOrd, drawModContext_t * ); +STATUS_T DrawGeomOriginMove(wAction_t, coOrd, drawModContext_t * ); +#endif //HAVE_DRAWGEOM_H diff --git a/app/bin/dxfoutput.c b/app/bin/dxfoutput.c index 69c6df4..214f63c 100644 --- a/app/bin/dxfoutput.c +++ b/app/bin/dxfoutput.c @@ -135,15 +135,19 @@ static void DxfFillPoly( drawCmd_p d, int cnt, coOrd * pts, - wDrawColor color) + int * types, + wDrawColor color, + wDrawWidth width, + int fill, + int open ) { int inx; for (inx=1; inx<cnt; inx++) { - DxfLine(d, pts[inx-1], pts[inx], 0, color); + DxfLine(d, pts[inx-1], pts[inx], width, color); } - - DxfLine(d, pts[cnt-1], pts[0], 0, color); + if (!open) + DxfLine(d, pts[cnt-1], pts[0], width, color); } static void DxfFillCircle(drawCmd_p d, coOrd center, DIST_T radius, @@ -192,7 +196,7 @@ static int DoExportDXFTracks( } oldLocale = SaveLocale("C"); - wSetCursor(wCursorWait); + wSetCursor(mainD.d, wCursorWait); time(&clock); DxfPrologue(&command, 10, 0.0, 0.0, mapD.size.x, mapD.size.y); @@ -208,7 +212,7 @@ static int DoExportDXFTracks( fclose(dxfF); RestoreLocale(oldLocale); Reset(); - wSetCursor(wCursorNormal); + wSetCursor(mainD.d, defaultCursor); return TRUE; } diff --git a/app/bin/elev.c b/app/bin/elev.c index 6b86bc1..a9e5fee 100644 --- a/app/bin/elev.c +++ b/app/bin/elev.c @@ -137,7 +137,8 @@ BOOL_T ComputeElev( EPINX_T ep, BOOL_T onpath, DIST_T *elevR, - DIST_T *gradeR ) + DIST_T *gradeR, + BOOL_T force ) { DIST_T grade; DIST_T elev0, elev1, dist0, dist1; @@ -176,6 +177,7 @@ if (oldElevationEvaluation) { elev0 = 0.0; } } else { + track_p trk1; EPINX_T ep1; grade = -1; @@ -184,30 +186,40 @@ if (oldElevationEvaluation) { elev0 = GetTrkEndElevHeight(trk,ep); rc = FALSE; } else { - elev0 = GetElevation( trk ); - dist0 = GetTrkLength( trk, ep, -1 ); + if (force || (!GetTrkEndElevCachedHeight(trk,ep,&elev0,&dist0))) { + elev0 = GetElevation( trk ); + dist0 = GetTrkLength( trk, ep, -1 ); + } + SetTrkEndElevCachedHeight(trk,ep,elev0,dist0); trk1 = GetTrkEndTrk( trk, ep ); if (trk1!=NULL) { ep1 = GetEndPtConnectedToMe(trk1,trk); - elev1 = GetElevation( trk1 ); - dist1 = GetTrkLength( trk1, ep1, -1 ); + if (force || (!GetTrkEndElevCachedHeight(trk1,ep1,&elev1,&dist1))) { + elev1 = GetElevation( trk1 ); + dist1 = GetTrkLength( trk1, ep1, -1 ); + } + SetTrkEndElevCachedHeight(trk1,ep1,elev1,dist1); if (dist0+dist1>0.1) { grade = (elev1-elev0)/(dist0+dist1); elev0 += grade*dist0; } else { elev0 = (elev0+elev1)/2.0; rc = FALSE; + SetTrkEndElevCachedHeight(trk,ep,elev0,dist0); + SetTrkEndElevCachedHeight(trk1,ep1,elev0,dist1); } } else { grade = 0.0; } + } } - if ( elevR ) - *elevR = elev0; - if ( gradeR ) - *gradeR = grade; - return rc; +if ( elevR ) + *elevR = elev0; +if ( gradeR ) + *gradeR = grade; +return rc; + } @@ -988,7 +1000,7 @@ EXPORT void RecomputeElevations( void ) PropogateForkElevs(); PropogateDefElevs(); FindIslandElevs(); - MainRedraw(); + MainRedraw(); // RecomputeElevations LOG( log_fillElev, 1, ( "%s: Total (%ld)\n", elevPrefix, wGetTimer()-time0 ) ) if ( log_dumpElev > 0 ) { track_p trk; @@ -1087,6 +1099,7 @@ EXPORT void ClrTrkElev( track_p trk ) needElevUpdate = TRUE; DrawTrackElev( trk, &mainD, FALSE ); ClrTrkBits( trk, TB_ELEVPATH ); + } @@ -1124,11 +1137,11 @@ EXPORT void SetTrkElevModes( BOOL_T connect, track_p trk0, EPINX_T ep0, track_p update = FALSE;; } else if ( connect ) { if ( mode0 == ELEV_ALONE ) { - ComputeElev( trk1, ep1, FALSE, &elev, NULL ); + ComputeElev( trk1, ep1, FALSE, &elev, NULL, TRUE ); PropogateElevMode( trk0, elev, ELEV_ISLAND ); update = FALSE; } else if ( mode1 == ELEV_ALONE ) { - ComputeElev( trk0, ep0, FALSE, &elev, NULL ); + ComputeElev( trk0, ep0, FALSE, &elev, NULL, TRUE ); PropogateElevMode( trk1, elev, ELEV_ISLAND ); update = FALSE; } @@ -1287,8 +1300,7 @@ EXPORT void DrawTrackElev( track_cp trk, drawCmd_p d, BOOL_T drawIt ) (labelScale < d->scale) || (!GetTrkOnElevPath( trk, &elev )) || ((GetTrkBits(trk)&TB_ELEVPATH) == 0) || - (d->funcs->options & wDrawOptTemp) != 0 || - (d->options & DC_QUICK) != 0 ) + (d->options & DC_SIMPLE) != 0 ) return; if ( !GetCurveMiddle( trk, &pos ) ) { diff --git a/app/bin/file2uri.c b/app/bin/file2uri.c new file mode 100644 index 0000000..a9d8f4f --- /dev/null +++ b/app/bin/file2uri.c @@ -0,0 +1,83 @@ +/** \file file2uri.c + * Conversion for filename to URI and reverse + */ + + /* XTrackCAD - Model Railroad CAD + * Copyright (C) 2019 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 <string.h> +#include <stdio.h> +#include "wlib.h" + +static char *reservedChars = "?#[]@!$&'()*+,;= "; + +unsigned +File2URI(char *fileName, unsigned resultLen, char *resultBuffer) +{ + char *currentSource = fileName; + char *currentDest; + + currentDest = resultBuffer; + + while (*currentSource && ((unsigned)(currentDest - resultBuffer) < resultLen - 1)) { + if (*currentSource == FILE_SEP_CHAR[ 0 ]) { + *currentDest++ = '/'; + currentSource++; + continue; + } + if (strchr(reservedChars, *currentSource)) + { + sprintf(currentDest, "%%%02x", *currentSource); + currentSource++; + currentDest += 3; + } else { + *currentDest++ = *currentSource++; + } + + } + *currentDest = '\0'; + return(strlen(resultBuffer)); +} + +unsigned +URI2File(char *encodedFileName, unsigned resultLen, char *resultBuffer) +{ + char *currentSource = encodedFileName; + char *currentDest = resultBuffer; + + currentSource = encodedFileName; + while (*currentSource && ((unsigned)(currentDest - resultBuffer) < resultLen - 1)) { + if (*currentSource == '/') { + *currentDest++ = FILE_SEP_CHAR[0]; + currentSource++; + continue; + } + if (*currentSource == '%') { + char hexCode[3]; + memcpy(hexCode, currentSource + 1, 2); + hexCode[2] = '\0'; + sscanf(hexCode, "%x", (unsigned int*)currentDest); + currentDest++; + currentSource += 3; + } else { + *currentDest++ = *currentSource++; + } + } + *currentDest = '\0'; + return(strlen(resultBuffer)); +} diff --git a/app/bin/file2uri.h b/app/bin/file2uri.h new file mode 100644 index 0000000..921976a --- /dev/null +++ b/app/bin/file2uri.h @@ -0,0 +1,27 @@ +/** \file file2uri.h + * Include file for file2uri.c + */ + + /* XTrackCAD - Model Railroad CAD + * Copyright (C) 2019 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. + */
+
+#ifndef HAVE_FILE2URI_H
+unsigned File2URI(char *fileName, unsigned resultLen, char *resultBuffer);
+unsigned URI2File(char *encodedFileName, unsigned resultLen, char *resultBuffer);
+#endif // !HAVE_FILE2URI_H
+
diff --git a/app/bin/fileio.c b/app/bin/fileio.c index 4e2a21d..cb0c8de 100644 --- a/app/bin/fileio.c +++ b/app/bin/fileio.c @@ -33,12 +33,10 @@ #include <time.h> #include <ctype.h> #ifdef WINDOWS -#include <io.h> -#include <windows.h> - //#if _MSC_VER >=1400 - // #define strdup _strdup - //#endif -#else + #include <io.h> + #define W_OK (2) + #define access _access + #include <windows.h> #endif #include <sys/stat.h> #include <stdarg.h> @@ -48,35 +46,52 @@ #include <assert.h> +#include <cJSON.h> + +#include "archive.h" #include "common.h" #include "compound.h" #include "cselect.h" #include "cundo.h" #include "custom.h" +#include "directory.h" #include "draw.h" #include "fileio.h" +#include "fcntl.h" #include "i18n.h" #include "layout.h" +#include "manifest.h" #include "messages.h" #include "misc.h" #include "param.h" +#include "include/paramfile.h" #include "paths.h" #include "track.h" #include "utility.h" #include "version.h" +#include "dynstring.h" + +#ifdef WINDOWS +#include "include/utf8convert.h" +#endif // WINDOWS /*#define TIME_READTRACKFILE*/ +#define COPYBLOCKSIZE 1024 + EXPORT const char * workingDir; EXPORT const char * libDir; -static char * customPath = NULL; -static char * customPathBak = NULL; +EXPORT wMenuList_p fileList_ml; EXPORT char * clipBoardN; +static coOrd paste_offset, cursor_offset; + +EXPORT wBool_t bExample = FALSE; +EXPORT wBool_t bReadOnly = FALSE; + -static int log_paramFile; #ifdef WINDOWS #define rename( F1, F2 ) Copyfile( F1, F2 ) @@ -151,20 +166,13 @@ RestoreLocale( char * locale ) EXPORT FILE * paramFile = NULL; char *paramFileName; EXPORT wIndex_t paramLineNum = 0; -EXPORT char paramLine[STR_LONG_SIZE]; +EXPORT char paramLine[STR_HUGE_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 ) - +dynArr_t paramProc_da; EXPORT void Stripcr( char * line ) { @@ -179,13 +187,6 @@ EXPORT void Stripcr( char * line ) *cp = '\0'; } -EXPORT void ParamCheckSumLine( char * line ) -{ - long mult=1; - while ( *line ) - paramCheckSum += (((long)(*line++))&0xFF)*(mult++); -} - EXPORT char * GetNextLine( void ) { if (!paramFile) { @@ -193,7 +194,12 @@ EXPORT char * GetNextLine( void ) return NULL; } if (fgets( paramLine, sizeof paramLine, paramFile ) == NULL) { - AbortProg( "Permature EOF on %s", paramFileName ); + sprintf( message, "INPUT ERROR: premature EOF on %s", paramFileName ); + wNoticeEx( NT_ERROR, message, _("Ok"), NULL ); + if ( paramFile ) { + fclose( paramFile ); + paramFile = NULL; + } } Stripcr( paramLine ); ParamCheckSumLine( paramLine ); @@ -234,9 +240,14 @@ EXPORT int InputError( } strcat( mp, _("\nDo you want to continue?") ); if (!(ret = wNoticeEx( NT_ERROR, message, _("Continue"), _("Stop") ))) { - if ( paramFile ) + if ( paramFile ) { fclose(paramFile); - paramFile = NULL; + paramFile = NULL; + } + if ( paramFileName ) { + free( paramFileName ); + paramFileName = NULL; + } } return ret; } @@ -251,6 +262,7 @@ EXPORT void SyntaxError( TRUE, event, actual, expected ); } + /** * Parse a line in XTrackCAD's file format * @@ -258,6 +270,24 @@ EXPORT void SyntaxError( * \param format IN ??? * * \return FALSE in case of parsing error, TRUE on success + * In the error case, InputError had been called which may have closed the input file (paramFile) + * + * format chars are: + * 0 - read a number and discard + * X - no read, *pi = 0 + * Y - no read, *pf = 0L + * Z - no read, *pl = 0.0 + * L - *pi = number + * d - *pi = number + * w - *pf = read a width + * u - *pul = number + * l - *pl = number + * f - *pf = number + * z - *pf = 0.0 + * p - *pp = ( number, number ) a coOrd + * s - *ps = string + * q - *ps = quoted string + * c - *qp = position of next non-space char or NULL */ EXPORT BOOL_T GetArgs( @@ -266,7 +296,6 @@ EXPORT BOOL_T GetArgs( ... ) { char * cp, * cq; - int argNo; long * pl; unsigned long *pul; int * pi; @@ -276,25 +305,24 @@ EXPORT BOOL_T GetArgs( char ** qp; va_list ap; char *oldLocale = NULL; + char * sError = NULL; oldLocale = SaveLocale("C"); cp = line; va_start( ap, format ); - for (argNo=1;*format;argNo++,format++) { + for ( ; sError==NULL && *format; format++ ) { while (isspace((unsigned char)*cp)) cp++; if (!*cp && strchr( "XZYzc", *format ) == NULL ) { - RestoreLocale(oldLocale); - InputError( "Arg %d: EOL unexpected", TRUE, argNo ); - return FALSE; + sError = "EOL unexpected"; + break; } switch (*format) { case '0': (void)strtol( cp, &cq, 10 ); if (cp == cq) { - RestoreLocale(oldLocale); - InputError( "Arg %d: expected integer", TRUE, argNo ); - return FALSE; + sError = "%s: expected integer"; + break; } cp = cq; break; @@ -314,9 +342,8 @@ EXPORT BOOL_T GetArgs( 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; + sError = "%s: expected integer"; + break; } cp = cq; break; @@ -324,9 +351,8 @@ EXPORT BOOL_T GetArgs( 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; + sError = "%s: expected integer"; + break; } cp = cq; break; @@ -334,9 +360,8 @@ EXPORT BOOL_T GetArgs( 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; + sError = "%s: expected integer"; + break; } if (*cq == '.') *pf = strtod( cp, &cq ); @@ -348,9 +373,8 @@ EXPORT BOOL_T GetArgs( pul = va_arg( ap, unsigned long * ); *pul = strtoul( cp, &cq, 10 ); if (cp == cq) { - RestoreLocale(oldLocale); - InputError( "Arg %d: expected integer", TRUE, argNo ); - return FALSE; + sError = "%s: expected integer"; + break; } cp = cq; break; @@ -358,9 +382,8 @@ EXPORT BOOL_T GetArgs( pl = va_arg( ap, long * ); *pl = strtol( cp, &cq, 10 ); if (cp == cq) { - RestoreLocale(oldLocale); - InputError( "Arg %d: expected integer", TRUE, argNo ); - return FALSE; + sError = "%s: expected integer"; + break; } cp = cq; break; @@ -368,43 +391,27 @@ EXPORT BOOL_T GetArgs( pf = va_arg( ap, FLOAT_T * ); *pf = strtod( cp, &cq ); if (cp == cq) { - RestoreLocale(oldLocale); - InputError( "Arg %d: expected float", TRUE, argNo ); - return FALSE; + sError = "%s: expected float"; + break; } 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; + sError = "%s: expected float"; + break; } cp = cq; p.y = strtod( cp, &cq ); if (cp == cq) { - RestoreLocale(oldLocale); - InputError( "Arg %d: expected float", TRUE, argNo ); - return FALSE; + sError = "%s: expected float"; + break; } cp = cq; *pp = p; @@ -446,7 +453,10 @@ EXPORT BOOL_T GetArgs( } else { message[0] = '\0'; } - *qp = (char*)MyStrdup(message); +#ifdef WINDOWS + ConvertUTF8ToSystem(message); +#endif + *qp = (char*)ConvertFromEscapedText(message); break; case 'c': qp = va_arg( ap, char * * ); @@ -457,14 +467,76 @@ EXPORT BOOL_T GetArgs( *qp = NULL; break; default: - AbortProg( "getArgs: bad format char" ); + AbortProg( "getArgs: bad format char: %c", *format ); } } va_end( ap ); RestoreLocale(oldLocale); + if ( sError ) { + InputError( sError, TRUE, cp ); + return FALSE; + } + return TRUE; +} + + + +wBool_t IsEND( char * sEnd ) +{ + char * cp; + wBool_t bAllowNakedENDs = paramVersion < 12; + for( cp = paramLine; *cp && (isspace( *cp ) || *cp == '\t'); cp++ ); + if ( strncmp( cp, sEnd, strlen(sEnd) ) == 0 ) + cp += strlen( sEnd ); + else if ( bAllowNakedENDs && strncmp( cp, "END", 3 ) == 0 ) + cp += 3; + else + return FALSE; + for ( ; *cp && isspace( *cp ); cp++ ); + if ( *cp != '\0' ) + return FALSE; return TRUE; } + +/** + * Read the text for a note/car. Lines are read from the input file + * until the END statement is found. + * + * \todo Handle premature end as an error + * + * \return pointer to string, has to be myfree'd by caller + */ + +char * +ReadMultilineText() +{ + char *string; + DynString noteText; + DynStringMalloc(¬eText, 0); + char *line; + + line = GetNextLine(); + + while ( !IsEND("END") ) { + DynStringCatCStr(¬eText, line); + DynStringCatCStr(¬eText, "\n"); + line = GetNextLine(); + } + string = MyStrdup(DynStringToCStr(¬eText)); + string[strlen(string) - 1] = '\0'; + +#ifdef WINDOWS + if (wIsUTF8(string)) { + ConvertUTF8ToSystem(string); + } +#endif // WINDOWS + + DynStringFree(¬eText); + return(string); +} + + EXPORT wBool_t ParseRoomSize( char * s, coOrd * roomSizeRet ) @@ -493,179 +565,37 @@ EXPORT wBool_t ParseRoomSize( return FALSE; } - +/** + * Parameter file parser definitions + * + * \param [IN] name command + * \param [IN] proc function for reading the parameter definition + * \param [IN] delete if not NULL function for freeing the definition + */ EXPORT void AddParam( char * name, - readParam_t proc ) + 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 ) +EXPORT char * PutTitle( char * cp ) { - FILE * oldFile; - char *cp; - wIndex_t oldLineNum; - wIndex_t pc; - long oldCheckSum; - long checkSum=0; - BOOL_T checkSummed; - long paramVersion = -1; - char *oldLocale = NULL; + static char *title; + char * tp; + unsigned cnt = strlen(cp) * 2 + 3; // add 3 for quotes and terminating \0 - if (dirName) { - MakeFullpath(¶mFileName, dirName, fileName, NULL); + if (!title) { + title = MyMalloc(cnt); } else { - MakeFullpath(¶mFileName, fileName, NULL); - } - paramLineNum = 0; - curBarScale = -1; - curContents = strdup( fileName ); - curSubContents = curContents; - -LOG1( log_paramFile, ("ReadParam( %s )\n", fileName ) ) - - oldLocale = SaveLocale("C"); - - paramFile = fopen( paramFileName, "r" ); - if (paramFile == NULL) { - /* Reset the locale settings */ - RestoreLocale( oldLocale ); - - NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Parameter"), paramFileName, strerror(errno) ); - - return FALSE; - } - paramCheckSum = key; - paramLineNum = 0; - checkSummed = FALSE; - while ( paramFile && ( fgets(paramLine, 256, paramFile) ) != NULL ) { - paramLineNum++; - Stripcr( paramLine ); - if (strncmp( paramLine, "CHECKSUM ", 9 ) == 0) { - checkSum = atol( paramLine+9 ); - checkSummed = TRUE; - goto nextLine; - } - ParamCheckSumLine( paramLine ); - if (paramLine[0] == '#') { - /* comment */ - } else if (paramLine[0] == 0) { - /* empty paramLine */ - } else if (strncmp( paramLine, "INCLUDE ", 8 ) == 0) { - cp = ¶mLine[8]; - while (*cp && isspace((unsigned char)*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) { - MakeFullpath(¶mFileName, dirName, fileName, NULL); - } else { - MakeFullpath(¶mFileName, 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:; + title = MyRealloc(title, cnt); } - 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 ); - free(paramFileName); - paramFileName = NULL; - RestoreLocale( oldLocale ); - - return TRUE; -} + tp = title; -static void ReadCustom( void ) -{ - FILE * f; - MakeFullpath(&customPath, workingDir, sCustomF, NULL); - 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) { + while (*cp ) { if (*cp == '\"') { *tp++ = '\"'; *tp++ = '\"'; @@ -677,6 +607,16 @@ EXPORT char * PutTitle( char * cp ) if ( *cp ) NoticeMessage( _("putTitle: title too long: %s"), _("Ok"), NULL, title ); *tp = '\0'; + +#ifdef WINDOWS + if(RequiresConvToUTF8(title)) { + char *out = MyMalloc(cnt); + wSystemToUTF8(title, out, cnt); + strcpy(title, out); + MyFree(out); + } +#endif // WINDOWS + return title; } @@ -694,20 +634,25 @@ void SetWindowTitle( void ) return; filename = GetLayoutFilename(); - sprintf( message, "%s%s - %s(%s)", + sprintf( message, "%s%s%s - %s(%s)", (filename && filename[0])?filename: _("Unnamed Trackplan"), - changed>0?"*":"", sProdName, sVersion ); + bReadOnly?_(" (R/O)"):"", + changed>0?"*":"", + sProdName, sVersion ); wWinSetTitle( mainW, message ); } - + + + /***************************************************************************** * * LOAD / SAVE TRACKS * */ -static struct wFilSel_t * loadFile_fs; -static struct wFilSel_t * saveFile_fs; +static struct wFilSel_t * loadFile_fs = NULL; +static struct wFilSel_t * saveFile_fs = NULL; +static struct wFilSel_t * examplesFile_fs = NULL; static wWin_p checkPointingW; static paramData_t checkPointingPLs[] = { @@ -716,6 +661,7 @@ static paramGroup_t checkPointingPG = { "checkpoint", 0, checkPointingPLs, sizeo static char * checkPtFileName1; static char * checkPtFileName2; +static char * checkPtFileNameBackup; /** Read the layout design. * @@ -760,8 +706,12 @@ static BOOL_T ReadTrackFile( InfoMessage("0"); count = 0; + int skipLines = 0; + BOOL_T skip = FALSE; while ( paramFile && ( fgets(paramLine, sizeof paramLine, paramFile) ) != NULL ) { count++; + BOOL_T old_skip = skip; + skip = FALSE; if (count%10 == 0) { InfoMessage( "%d", count ); wFlush(); @@ -781,8 +731,8 @@ static BOOL_T ReadTrackFile( } if (ReadTrack( paramLine )) { - - } else if (strncmp( paramLine, "END", 3 ) == 0) { + continue; + } else if (IsEND( END_TRK_FILE ) ) { break; } else if (strncmp( paramLine, "VERSION ", 8 ) == 0) { paramVersion = strtol( paramLine+8, &cp, 10 ); @@ -804,8 +754,14 @@ static BOOL_T ReadTrackFile( if( !(ret = InputError( "unknown command", TRUE ))) break; } else if (strncmp( paramLine, "TITLE1 ", 7 ) == 0) { +#ifdef WINDOWS + ConvertUTF8ToSystem(paramLine + 7); +#endif // WINDOWS SetLayoutTitle(paramLine + 7); } else if (strncmp( paramLine, "TITLE2 ", 7 ) == 0) { +#ifdef WINDOWS + ConvertUTF8ToSystem(paramLine + 7); +#endif // WINDOWS SetLayoutSubtitle(paramLine + 7); } else if (strncmp( paramLine, "ROOMSIZE", 8 ) == 0) { if ( ParseRoomSize( paramLine+8, &roomSize ) ) { @@ -829,39 +785,44 @@ static BOOL_T ReadTrackFile( } else if (strncmp( paramLine, "LAYERS ", 7 ) == 0) { ReadLayers( paramLine+7 ); } else { - if( !(ret = InputError( "unknown command", TRUE ))) - break; + if (!old_skip) { + if (InputError(_("Unknown layout file object - skip until next good object?"), TRUE)) { //OK to carry on + /* SKIP until next main line we recognize */ + skip = TRUE; + skipLines++; + continue; + } else { + break; //Close File + } + } else skip = TRUE; + skipLines++; } } - if (paramFile) + if (paramFile) { fclose(paramFile); + paramFile = NULL; + } if( ret ) { if (!noSetCurDir) SetCurrentPath( LAYOUTPATHKEY, fileName ); - - if (full) { -// SetCurrentPath(LAYOUTPATHKEY, pathName); - SetLayoutFullPath(pathName); - //strcpy(curPathName, pathName); - //curFileName = &curPathName[fileName-pathName]; - SetWindowTitle(); - } } + if (skipLines>0) + NoticeMessage( MSG_LAYOUT_LINES_SKIPPED, _("Ok"), NULL, paramFileName, skipLines); + RestoreLocale( oldLocale ); paramFile = NULL; free(paramFileName); - paramFileName = NULL; + paramFileName = NULL; InfoMessage( "%d", count ); return ret; } - -EXPORT int LoadTracks( +int LoadTracks( int cnt, char **fileName, void * data) @@ -869,18 +830,23 @@ EXPORT int LoadTracks( #ifdef TIME_READTRACKFILE long time0, time1; #endif - char *nameOfFile; + char *nameOfFile = NULL; + + char *extOfFile; assert( fileName != NULL ); - assert( cnt == 1 ); + assert( cnt == 1 ); - SetCurrentPath(LAYOUTPATHKEY, fileName[0]); + if ( ! bExample ) + SetCurrentPath(LAYOUTPATHKEY, fileName[0]); + bReadOnly = bExample; paramVersion = -1; - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, wCursorWait ); Reset(); ClearTracks(); ResetLayers(); checkPtMark = changed = 0; + LayoutBackGroundInit(TRUE); //Keep values of background -> will be overriden my archive UndoSuspend(); useCurrentLayer = FALSE; #ifdef TIME_READTRACKFILE @@ -888,8 +854,119 @@ EXPORT int LoadTracks( #endif nameOfFile = FindFilename( fileName[ 0 ] ); - if (ReadTrackFile( fileName[ 0 ], nameOfFile, TRUE, FALSE, TRUE )) { - wMenuListAdd( fileList_ml, 0, nameOfFile, MyStrdup(fileName[0]) ); + /* + * Support zipped filetype + */ + extOfFile = FindFileExtension( nameOfFile); + + BOOL_T zipped = FALSE; + BOOL_T loadXTC = TRUE; + char * full_path = strdup(fileName[0]); + + if (extOfFile && (strcmp(extOfFile, ZIPFILETYPEEXTENSION )==0)) { + + char * zip_input = GetZipDirectoryName(ARCHIVE_READ); + + //If zipped unpack file into temporary input dir (cleared and re-created) + + DeleteDirectory(zip_input); + SafeCreateDir(zip_input); + + if (UnpackArchiveFor(fileName[0], nameOfFile, zip_input, FALSE)) { + + char * manifest_file; + + MakeFullpath(&manifest_file, zip_input, "manifest.json", NULL); + + char * manifest = 0; + long length; + + FILE * f = fopen (manifest_file, "rb"); + + if (f) + { + fseek(f, 0, SEEK_END); + length = ftell(f); + fseek(f, 0, SEEK_SET); + manifest = malloc(length + 1); + if (manifest) { + size_t siz = fread(manifest, 1, length, f); + manifest[length] = '\0'; + } + fclose(f); + } else + { + NoticeMessage(MSG_MANIFEST_OPEN_FAIL, _("Continue"), NULL, manifest_file); + } + free(manifest_file); + + char * arch_file = NULL; + + //Set filename to point to included .xtc file + //Use the name inside manifest (this helps if a user renames the zip) + if (manifest) + { + arch_file = ParseManifest(manifest, zip_input); + free(manifest); + } + + free(full_path); + full_path = NULL; + // If no manifest value use same name as the archive + if (arch_file && arch_file[0]) + { + MakeFullpath(&full_path, zip_input, arch_file, NULL); + } else + { + MakeFullpath(&full_path, zip_input, nameOfFile, NULL); + } + + nameOfFile = FindFilename(full_path); + extOfFile = FindFileExtension(full_path); + if (strcmp(extOfFile, ZIPFILETYPEEXTENSION )==0) + { + for (int i=0; i<4; i++) { + extOfFile[i] = extOfFile[i+1]; + } + } + LOG(log_zip, 1, ("Zip-File %s \n", full_path)) + #if DEBUG + printf("File Path: %s \n", full_path); + #endif + } else { + loadXTC = FALSE; // when unzipping fails, don't attempt loading the trackplan + } + zipped = TRUE; + + free(zip_input); + + + } + + if ( bExample ) + bReadOnly = TRUE; + else if ( access( fileName[0], W_OK ) == -1 ) + bReadOnly = TRUE; + else + bReadOnly = FALSE; + + char *copyOfFileName = MyStrdup(fileName[0]); + + if (loadXTC && ReadTrackFile( full_path, FindFilename( fileName[0]), TRUE, TRUE, TRUE )) { + + nameOfFile = NULL; + extOfFile = NULL; + SetCurrentPath( LAYOUTPATHKEY, copyOfFileName ); + SetLayoutFullPath(copyOfFileName); + SetWindowTitle(); + + if ( ! bExample && (nameOfFile != NULL) ) { + char * copyFile = strdup(fileName[0]); + char * listName = FindFilename(strdup(fileName[0])); //Make sure the list name is new + wMenuListAdd( fileList_ml, 0, listName, copyFile ); + } + + ResolveIndex(); #ifdef TIME_READTRACKFILE time1 = wGetTimer(); @@ -902,9 +979,14 @@ EXPORT int LoadTracks( LoadLayerLists(); LayerSetCounts(); } + + MyFree(copyOfFileName); + free(full_path); + full_path = NULL; + UndoResume(); Reset(); - wSetCursor( wCursorNormal ); + wSetCursor( mainD.d, defaultCursor ); return TRUE; } @@ -913,7 +995,7 @@ EXPORT int LoadTracks( * path. * \param index IN ignored * \param label IN ignored - * \param data IN path and filename + * \param data IN path and filename */ EXPORT void DoFileList( @@ -922,7 +1004,7 @@ EXPORT void DoFileList( void * data ) { char *pathName = (char*)data; - + bExample = FALSE; LoadTracks( 1, &pathName, NULL ); } @@ -945,7 +1027,7 @@ static BOOL_T DoSaveTracks( return FALSE; } - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, 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; @@ -958,34 +1040,156 @@ static BOOL_T DoSaveTracks( rc &= fprintf(f, "SCALE %s\n", curScaleName )>0; rc &= WriteLayers( f ); rc &= WriteMainNote( f ); - rc &= WriteTracks( f ); - rc &= fprintf(f, "END\n")>0; + rc &= WriteTracks( f, TRUE ); + rc &= fprintf(f, "%s\n", END_TRK_FILE)>0; if ( !rc ) NoticeMessage( MSG_WRITE_FAILURE, _("Ok"), NULL, strerror(errno), fileName ); fclose(f); + bReadOnly = FALSE; RestoreLocale( oldLocale ); checkPtMark = changed; - wSetCursor( wCursorNormal ); + wSetCursor( mainD.d, defaultCursor ); return rc; } +/************************************************ + * Copy Dependency - copy file into another directory + * + * \param IN name + * \param IN target_dir + * + * \returns TRUE for success + * + */ +static BOOL_T CopyDependency(char * name, char * target_dir) +{ + char * backname = FindFilename(name); + BOOL_T copied = TRUE; + FILE * source = fopen(name, "rb"); + + if (source != NULL) { + char * target_file; + MakeFullpath(&target_file, target_dir, backname, NULL); + FILE * target = fopen(target_file, "wb"); + + if (target != NULL) { + char *buffer = MyMalloc(COPYBLOCKSIZE); + while (!feof(source)) { + size_t bytes = fread(buffer, 1, sizeof(buffer), source); + if (bytes) { + fwrite(buffer, 1, bytes, target); + } + } + MyFree(buffer); + LOG(log_zip, 1, ("Zip-Include %s into %s \n", name, target_file)) +#if DEBUG + printf("xtrkcad: Included file %s into %s \n", + name, target_file); +#endif + fclose(target); + } else { + NoticeMessage(MSG_COPY_FAIL, _("Continue"), NULL, name, target_file); + copied = FALSE; + } + free(target_file); + fclose(source); + } else { + NoticeMessage(MSG_COPY_OPEN_FAIL, _("Continue"), NULL, name); + copied = FALSE; + } + return copied; +} + static doSaveCallBack_p doAfterSave; + + static int SaveTracks( int cnt, char **fileName, void * data ) { - char *nameOfFile; assert( fileName != NULL ); assert( cnt == 1 ); + char *nameOfFile = FindFilename(fileName[0]); + SetCurrentPath(LAYOUTPATHKEY, fileName[0]); - DoSaveTracks( fileName[ 0 ] ); + + //Support Archive zipped files + + char * extOfFile = FindFileExtension( fileName[0]); + + + if (extOfFile && (strcmp(extOfFile,ZIPFILETYPEEXTENSION)==0)) { + + char * ArchiveName; + + //Set filename to point to be the same as the included .xtc file. + //This is also in the manifest - in case a user renames the archive file. + + char * zip_output = GetZipDirectoryName(ARCHIVE_WRITE); + + DeleteDirectory(zip_output); + SafeCreateDir(zip_output); + + MakeFullpath(&ArchiveName, zip_output, nameOfFile, NULL); + + nameOfFile = FindFilename(ArchiveName); + extOfFile = FindFileExtension(ArchiveName); + + if (extOfFile && strcmp(extOfFile, ZIPFILETYPEEXTENSION)==0) { + // Get rid of the 'e' + extOfFile[3] = '\0'; + } + + char * DependencyDir; + + //The included files are placed (for now) into an includes directory - TODO an array of includes with directories by type + MakeFullpath(&DependencyDir, zip_output, "includes", NULL); + + SafeCreateDir(DependencyDir); + + char * background = GetLayoutBackGroundFullPath(); + + if (background && background[0]) + CopyDependency(background,DependencyDir); + + //The details are stored into the manifest - TODO use arrays for files, locations + char *oldLocale = SaveLocale("C"); + char* json_Manifest = CreateManifest(nameOfFile, background, "includes"); + char * manifest_file; + + MakeFullpath(&manifest_file, zip_output, "manifest.json", NULL); + + FILE *fp = fopen(manifest_file, "wb"); + if (fp != NULL) + { + fputs(json_Manifest, fp); + fclose(fp); + } else { + NoticeMessage( MSG_MANIFEST_FAIL, _("Continue"), NULL, manifest_file ); + } + RestoreLocale(oldLocale); + + free(manifest_file); + free(json_Manifest); + + DoSaveTracks( ArchiveName ); + + if (CreateArchive( zip_output, fileName[0]) != TRUE) { + NoticeMessage( MSG_ARCHIVE_FAIL, _("Continue"), NULL, fileName[0], zip_output ); + } + free(zip_output); + free(ArchiveName); + + } else + + DoSaveTracks( fileName[ 0 ] ); nameOfFile = FindFilename( fileName[ 0 ] ); wMenuListAdd( fileList_ml, 0, nameOfFile, MyStrdup(fileName[ 0 ]) ); @@ -1003,16 +1207,18 @@ static int SaveTracks( EXPORT void DoSave( doSaveCallBack_p after ) { doAfterSave = after; - if (*(GetLayoutFilename()) == '\0') { + if ( bReadOnly || *(GetLayoutFilename()) == '\0') { if (saveFile_fs == NULL) saveFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Save Tracks"), sSourceFilePattern, SaveTracks, NULL ); wFilSelect( saveFile_fs, GetCurrentPath(LAYOUTPATHKEY)); + changed = checkPtMark = 1; } else { - char *temp = GetLayoutFullPath(); + char *temp = GetLayoutFullPath(); SaveTracks( 1, &temp, NULL ); } SetWindowTitle(); + SaveState(); } EXPORT void DoSaveAs( doSaveCallBack_p after ) @@ -1020,46 +1226,93 @@ EXPORT void DoSaveAs( doSaveCallBack_p after ) doAfterSave = after; if (saveFile_fs == NULL) saveFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Save Tracks As"), - sSourceFilePattern, SaveTracks, NULL ); + sSaveFilePattern, SaveTracks, NULL ); wFilSelect( saveFile_fs, GetCurrentPath(LAYOUTPATHKEY)); + changed = checkPtMark = 1; SetWindowTitle(); + SaveState(); } EXPORT void DoLoad( void ) { - loadFile_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Open Tracks"), - sSourceFilePattern, LoadTracks, NULL ); + if (loadFile_fs == NULL) + loadFile_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Open Tracks"), + sSourceFilePattern, LoadTracks, NULL ); + bExample = FALSE; wFilSelect( loadFile_fs, GetCurrentPath(LAYOUTPATHKEY)); + paste_offset = zero; + cursor_offset = zero; + SaveState(); } +EXPORT void DoExamples( void ) +{ + if (examplesFile_fs == NULL) { + static wBool_t bExample = TRUE; + examplesFile_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Example Tracks"), + sSourceFilePattern, LoadTracks, &bExample ); + } + bExample = TRUE; + sprintf( message, "%s" FILE_SEP_CHAR "examples" FILE_SEP_CHAR, libDir ); + wFilSelect( examplesFile_fs, message ); + SaveState(); +} + +static wIndex_t generations_count = 0; +wIndex_t max_generations_count = 10; +static char sCheckPointBF[STR_LONG_SIZE]; + + EXPORT void DoCheckPoint( void ) { int rc; + if (!checkPtFileNameBackup || (changed <= checkPtInterval+1)) { + sprintf(sCheckPointBF,"%s00.bkp",GetLayoutFilename()); + MakeFullpath(&checkPtFileNameBackup, workingDir, sCheckPointBF, NULL); + } + 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 ); + //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 ); + /* yes, archive/delete the backup copy of the checkpoint file */ + if (checkPtFileNameBackup) { + char * spot = strrchr(checkPtFileNameBackup,'.'); + if (spot && spot>checkPtFileNameBackup+3) { + spot[-2]=generations_count/10+'0'; + spot[-1]=generations_count%10+'0'; + } + generations_count++; + if (((autosaveChkPoints == 0) && (generations_count > 5)) || + ((autosaveChkPoints > 0) && (generations_count > autosaveChkPoints)) ) { + generations_count = 0; + } + remove( checkPtFileNameBackup); + rename( checkPtFileName2, checkPtFileNameBackup ); + } else { + remove(checkPtFileName2); + } } else { /* no, rename the backup copy back to the checkpoint file name */ rename( checkPtFileName2, checkPtFileName1 ); } - wHide( checkPointingW ); + + //wHide( checkPointingW ); + wShow( mainW ); } /** - * Remove all temporary files before exiting.When the program terminates - * normally through the exit choice, files that are created temporarily are removed: - * xtrkcad.ckp + * Remove all temporary files before exiting. When the program terminates + * normally through the exit choice, files and directories that were created + * temporarily are removed: xtrkcad.ckp * * \param none * \return none @@ -1068,12 +1321,27 @@ EXPORT void DoCheckPoint( void ) EXPORT void CleanupFiles( void ) { - if( checkPtFileName1 ) + char *tempDir; + + if( checkPtFileName1 ) { + if (checkPtFileNameBackup) { + remove( checkPtFileNameBackup ); + rename( checkPtFileName1, checkPtFileNameBackup ); + } remove( checkPtFileName1 ); + } + + for (int i = ARCHIVE_READ; i <= ARCHIVE_WRITE; ++i) { + tempDir = GetZipDirectoryName(i); + if (tempDir) { + DeleteDirectory(tempDir); + free(tempDir); + } + } } /** - * Check for existance of checkpoint file. Existance of a checkpoint file means that XTrkCAD was not properly + * Check for existence of checkpoint file. Existence of a checkpoint file means that XTrkCAD was not properly * terminated. * * \param none @@ -1098,37 +1366,52 @@ EXPORT int ExistsCheckpoint( void ) /** * Load checkpoint file * + * \param if TRUE reuse old filename + * \param filename returned * \return TRUE if exists, FALSE otherwise * */ -EXPORT int LoadCheckpoint( void ) +EXPORT int LoadCheckpoint( BOOL_T sameName ) { char *search; paramVersion = -1; - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, wCursorWait ); MakeFullpath(&search, workingDir, sCheckPointF, NULL); UndoSuspend(); if (ReadTrackFile( search, search + strlen(search) - strlen( sCheckPointF ), TRUE, TRUE, TRUE )) { ResolveIndex(); + LayoutBackGroundInit(FALSE); //Get Prior BackGround + LayoutBackGroundSave(); //Save Background Values + + if (sameName) { + long iExample; + char * initialFile = (char*)wPrefGetString("misc", "lastlayout"); + wPrefGetInteger("misc", "lastlayoutexample", &iExample, 0); + bExample = (iExample == 1); + if (initialFile && strlen(initialFile)) { + SetCurrentPath( LAYOUTPATHKEY, initialFile ); + SetLayoutFullPath(initialFile); + } + } else SetLayoutFullPath(""); RecomputeElevations(); AttachTrains(); DoChangeNotification( CHANGE_ALL ); DoUpdateTitles(); - } + } else SetLayoutFullPath(""); Reset(); UndoResume(); - wSetCursor( wCursorNormal ); + wSetCursor( mainD.d, defaultCursor ); + - SetLayoutFullPath(""); SetWindowTitle(); - changed = TRUE; + checkPtMark = changed = 1; free( search ); return TRUE; } @@ -1142,6 +1425,15 @@ EXPORT int LoadCheckpoint( void ) static struct wFilSel_t * exportFile_fs; static struct wFilSel_t * importFile_fs; +static int importAsModule; + + + +/******************************************************************************* + * + * Import Layout Dialog + * + */ static int ImportTracks( int cnt, @@ -1156,27 +1448,40 @@ static int ImportTracks( nameOfFile = FindFilename(fileName[ 0 ]); paramVersion = -1; - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, wCursorWait ); Reset(); SetAllTrackSelect( FALSE ); + int saveLayer = curLayer; + int layer; + if (importAsModule) { + layer = FindUnusedLayer(0); + if (layer==-1) return FALSE; + char LayerName[80]; + LayerName[0] = '\0'; + sprintf(LayerName,_("Module - %s"),nameOfFile); + if (layer>=0) SetCurrLayer(layer, NULL, 0, NULL, NULL); + SetLayerName(layer,LayerName); + } ImportStart(); UndoStart( _("Import Tracks"), "importTracks" ); useCurrentLayer = TRUE; ReadTrackFile( fileName[ 0 ], nameOfFile, FALSE, FALSE, TRUE ); - ImportEnd(); + ImportEnd(zero, TRUE, FALSE); + if (importAsModule) SetLayerModule(layer,TRUE); + useCurrentLayer = FALSE; + SetCurrLayer(saveLayer, NULL, 0, NULL, NULL); /*DoRedraw();*/ EnableCommands(); - wSetCursor( wCursorNormal ); + wSetCursor( mainD.d, defaultCursor ); paramVersion = paramVersionOld; - importMove = TRUE; DoCommandB( (void*)(intptr_t)selectCmdInx ); SelectRecount(); return TRUE; } - -EXPORT void DoImport( void ) +EXPORT void DoImport( void * type ) { + importAsModule = (int)(long)type; if (importFile_fs == NULL) importFile_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Import Tracks"), sImportFilePattern, ImportTracks, NULL ); @@ -1215,18 +1520,19 @@ static int DoExportTracks( oldLocale = SaveLocale("C"); - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, 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"); + coOrd offset; + ExportTracks( f , &offset); + fprintf(f, "%s\n", END_TRK_FILE); fclose(f); RestoreLocale( oldLocale ); Reset(); - wSetCursor( wCursorNormal ); + wSetCursor( mainD.d, defaultCursor ); UpdateAllElevations(); return TRUE; } @@ -1246,7 +1552,6 @@ EXPORT void DoExport( void ) } - EXPORT BOOL_T EditCopy( void ) { FILE * f; @@ -1268,10 +1573,11 @@ EXPORT BOOL_T EditCopy( void ) 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"); + ExportTracks(f, &paste_offset); + fprintf(f, "%s\n", END_TRK_FILE ); RestoreLocale(oldLocale); fclose(f); + return TRUE; } @@ -1284,6 +1590,7 @@ EXPORT BOOL_T EditCut( void ) return TRUE; } + /** * Paste clipboard content. XTrackCAD uses a disk file as clipboard replacement. This file is read and the * content is inserted. @@ -1291,16 +1598,24 @@ EXPORT BOOL_T EditCut( void ) * \return TRUE if success, FALSE on error (file not found) */ -EXPORT BOOL_T EditPaste( void ) +BOOL_T EditPastePlace( wBool_t inPlace ) { + BOOL_T rc = TRUE; char *oldLocale = NULL; oldLocale = SaveLocale("C"); - wSetCursor( wCursorWait ); + wSetCursor( mainD.d, wCursorWait ); Reset(); SetAllTrackSelect( FALSE ); + + double offset = 20*mainD.scale/mainD.dpi; + + paste_offset.x += offset; + paste_offset.y += offset; + + ImportStart(); UndoStart( _("Paste"), "paste" ); useCurrentLayer = TRUE; @@ -1308,18 +1623,33 @@ EXPORT BOOL_T EditPaste( void ) NoticeMessage( MSG_CANT_PASTE, _("Continue"), NULL ); rc = FALSE; } - ImportEnd(); + if (inPlace) + ImportEnd(paste_offset, FALSE, TRUE); + else + ImportEnd(zero, FALSE, FALSE); + useCurrentLayer = FALSE; /*DoRedraw();*/ EnableCommands(); - wSetCursor( wCursorNormal ); - importMove = TRUE; + wSetCursor( mainD.d, defaultCursor ); DoCommandB( (void*)(intptr_t)selectCmdInx ); SelectRecount(); UpdateAllElevations(); RestoreLocale(oldLocale); + return rc; } + +EXPORT BOOL_T EditPaste( void) { + return EditPastePlace(FALSE); +} +EXPORT BOOL_T EditClone( void ) { + BOOL_T rc = TRUE; + if (!EditCopy()) return FALSE; + if (!EditPastePlace(TRUE)) return FALSE; + return rc; +} + /***************************************************************************** * * INITIALIZATION @@ -1333,23 +1663,8 @@ EXPORT void FileInit( void ) } if ( (workingDir = wGetAppWorkDir()) == NULL ) AbortProg( "wGetAppWorkDir()" ); -} - -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(); - } SetLayoutFullPath(""); - MakeFullpath(&clipBoardN, workingDir, sClipboardF, NULL); - return TRUE; + MakeFullpath(&clipBoardN, workingDir, sClipboardF, NULL); } diff --git a/app/bin/fileio.h b/app/bin/fileio.h index 4f5aa8d..13761bf 100644 --- a/app/bin/fileio.h +++ b/app/bin/fileio.h @@ -27,20 +27,24 @@ #include "common.h" #include "misc.h" -FILE * paramFile; +extern FILE * paramFile; extern char *paramFileName; -wIndex_t paramLineNum; -char paramLine[STR_LONG_SIZE]; -char * curContents; -char * curSubContents; +extern wIndex_t paramLineNum; +extern char paramLine[STR_HUGE_SIZE]; +extern char * curContents; +extern char * curSubContents; #define PARAM_DEMO (-1) typedef void (*playbackProc_p)( char * ); typedef BOOL_T (*readParam_t) ( char * ); +typedef BOOL_T (*deleteParam_t) (void *param); extern const char * workingDir; extern const char * libDir; +extern wBool_t bReadOnly; +extern wBool_t bExample; + #define PARAM_CUSTOM (-2) #define PARAM_LAYOUT (-3) extern int curParamFileIndex; @@ -50,17 +54,20 @@ extern unsigned long playbackTimer; extern wBool_t executableOk; extern FILE * recordF; -wBool_t inPlayback; -wBool_t inPlaybackQuit; -wWin_p demoW; -int curDemo; +extern wBool_t inPlayback; +extern wBool_t inPlaybackQuit; +extern wWin_p demoW; +extern int curDemo; + +extern wMenuList_p fileList_ml; -wMenuList_p fileList_ml; +#define ZIPFILETYPEEXTENSION "xtce" #define PARAM_SUBDIR "params" #define LAYOUTPATHKEY "layout" #define BITMAPPATHKEY "bitmap" +#define BACKGROUNDPATHKEY "images" #define DXFPATHKEY "dxf" #define PARTLISTPATHKEY "parts" #define CARSPATHKEY "cars" @@ -68,16 +75,32 @@ wMenuList_p fileList_ml; #define IMPORTPATHKEY "import" #define MACROPATHKEY "macro" #define CUSTOMPATHKEY "custom" +#define ARCHIVEPATHKEY "archive" + +typedef struct { + char * name; + readParam_t proc; +} paramProc_t; +dynArr_t paramProc_da; +#define paramProc(N) DYNARR_N( paramProc_t, paramProc_da, N ) void Stripcr( char * ); char * GetNextLine( void ); +#define END_TRK_FILE "END$TRACKS" +#define END_BLOCK "END$BLOCK" +#define END_SIGNAL "END$SIGNAL" +#define END_SEGS "END$SEGS" +#define END_MESSAGE "END$MESSAGE" +wBool_t IsEND( char * sEnd ); + BOOL_T GetArgs( char *, char *, ... ); +char * ReadMultilineText(); BOOL_T ParseRoomSize( char *, coOrd * ); int InputError( char *, BOOL_T, ... ); -void SyntaxError( char *, wIndex_t, wIndex_t ); +void SyntaxError( char *, wIndex_t, wIndex_t ); -void AddParam( char *, readParam_t ); +void AddParam( char *name, readParam_t proc ); FILE * OpenCustom( char * ); @@ -87,29 +110,29 @@ FILE * OpenCustom( char * ); void SetWindowTitle( void ); char * PutTitle( char * cp ); -wBool_t IsParamValid( int ); -char * GetParamFileName( int ); -void RememberParamFiles( void ); -int LoadParamFile( int files, char **fileName, void *data ); -void ReadParamFiles( void ); + +void ParamFileListLoad(int paramFileCnt, dynArr_t *paramFiles); +void DoParamFiles(void * junk); + int LoadTracks( int cnt, char **fileName, void *data ); -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 DoExamples( void ); void DoFileList( int, char *, void * ); void DoCheckPoint( void ); void CleanupFiles( void ); int ExistsCheckpoint( void ); -int LoadCheckpoint( void ); -void DoImport( void ); +int LoadCheckpoint( BOOL_T ); +void DoImport( void * ); void DoExport( void ); void DoExportDXF( void ); BOOL_T EditCopy( void ); BOOL_T EditCut( void ); BOOL_T EditPaste( void ); +BOOL_T EditClone( void ); void DoRecord( void * ); @@ -124,10 +147,13 @@ 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 ); +// Parameter file search +void DoSearchParams(void * junk); + #endif diff --git a/app/bin/filenoteui.c b/app/bin/filenoteui.c new file mode 100644 index 0000000..5ffddd1 --- /dev/null +++ b/app/bin/filenoteui.c @@ -0,0 +1,330 @@ +/** \file filenoteui.c + * View for the file note + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2018 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 <string.h> +#include <stdbool.h> +#ifdef WINDOWS + #include <io.h> + #define access(path,mode) _access(path,mode) + #define F_OK (0) +#else + #include <unistd.h> +#endif +#include "custom.h" +#include "dynstring.h" +#include "file2uri.h" +#include "i18n.h" +#include "misc.h" +#include "note.h" +#include "param.h" +#include "paths.h" +#include "include/stringxtc.h" +#include "track.h" +#include "wlib.h" + +extern BOOL_T inDescribeCmd; + +#define MYMIN(x, y) (((x) < (y)) ? (x) : (y)) + +#define DOCUMENTFILEPATTERN "All Files (*.*)|*.*" +#define DOCUMENTPATHKEY "document" + +static struct extraDataNote noteDataInUI; +static struct wFilSel_t * documentFile_fs; + +static void NoteFileOpenExternal(void * junk); +static void NoteFileBrowse(void * junk); + +static paramFloatRange_t r_1000_1000 = { -1000.0, 1000.0, 80 }; + +// static char *toggleLabels[] = { N_("Copy to archive"), NULL }; +static paramData_t fileEditPLs[] = { +#define I_ORIGX (0) + /*0*/ { PD_FLOAT, ¬eDataInUI.pos.x, "origx", PDO_DIM, &r_1000_1000, N_("Position X") }, +#define I_ORIGY (1) + /*1*/ { PD_FLOAT, ¬eDataInUI.pos.y, "origy", PDO_DIM, &r_1000_1000, N_("Position Y") }, +#define I_LAYER (2) + /*2*/ { PD_DROPLIST, ¬eDataInUI.layer, "layer", 0, (void*)150, "Layer", 0 }, +#define I_TITLE (3) + /*3*/ { PD_STRING, NULL, "title", PDO_NOPREF | PDO_STRINGLIMITLENGTH, (void*)200, N_("Title"), 0, 0, TITLEMAXIMUMLENGTH-1 }, +#define I_PATH (4) + { PD_STRING, NULL, "filename", PDO_NOPSHUPD, (void*)200, N_("Document"), BO_READONLY, (void *)0L }, +#define I_BROWSE (5) + { PD_BUTTON, (void *)NoteFileBrowse, "browse", 0L, NULL, N_("Select...") }, +#define I_OPEN (6) + { PD_BUTTON, (void*)NoteFileOpenExternal, "openfile", PDO_DLGHORZ, NULL, N_("Open...") }, +//#define I_ARCHIVE (7) +// { PD_TOGGLE, ¬eFileData.inArchive, "archive", 0, toggleLabels, NULL }, + +}; + +static paramGroup_t fileEditPG = { "fileEdit", 0, fileEditPLs, sizeof fileEditPLs / sizeof fileEditPLs[0] }; +static wWin_p fileEditW; + +BOOL_T IsFileNote(track_p trk) +{ + struct extraDataNote * xx = (struct extraDataNote *)GetTrkExtraData(trk); + + return(xx->op == OP_NOTEFILE ); +} + +/** Check for the file existance + * + * \param fileName IN file + * \return TRUE if exists, FALSE otherwise + */ +BOOL_T IsFileValid(char *fileName) +{ + if (!strlen(fileName)) { + return(FALSE); + } else { + if (access(fileName, F_OK) == -1) { + return(FALSE); + } + } + + return(TRUE); +} + +/** + * Put the selected filename into the dialog + * + * \param files IN always 1 + * \param fileName IN name of selected file + * \param data IN ignored + * \return always 0 + */ +int LoadDocumentFile( + int files, + char ** fileName, + void * data) +{ + wControlActive(fileEditPLs[I_OPEN].control, TRUE); + ParamDialogOkActive(&fileEditPG, TRUE); + strscpy(noteDataInUI.noteData.fileData.path, *fileName, PATHMAXIMUMLENGTH ); + ParamLoadControl(&fileEditPG, I_PATH); + + return(0); +} + +/** + * Select the file to attach + * + * \param junk unused + */ +static void NoteFileBrowse(void * junk) +{ + documentFile_fs = wFilSelCreate(mainW, FS_LOAD, 0, _("Add Document"), DOCUMENTFILEPATTERN, LoadDocumentFile, NULL); + + wFilSelect(documentFile_fs, GetCurrentPath(DOCUMENTPATHKEY)); + + wControlActive(fileEditPLs[I_OPEN].control, + (strlen(noteDataInUI.noteData.fileData.path) ? TRUE : FALSE)); + + return; +} + +/** + * Open the file using an external program. Before opening the file existance and permissions are checked. + * If access is not allowed the Open Button is disabled + * + * \param fileName IN file + */ + +static void NoteFileOpen(char *fileName) +{ + if (IsFileValid(fileName)) { + wOpenFileExternal(fileName); + } else { + wNoticeEx(NT_ERROR, _("The file doesn't exist or cannot be read!"), _("Cancel"), NULL); + if (fileEditW) { + wControlActive(fileEditPLs[I_OPEN].control, FALSE); + } + } +} + +static void +NoteFileOpenExternal(void * junk) +{ + NoteFileOpen(noteDataInUI.noteData.fileData.path); +} +/** + * Handle the dialog actions + */ +static void +FileDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP) +{ + switch (inx) { + case I_ORIGX: + case I_ORIGY: + UpdateFile(¬eDataInUI, OR_NOTE, FALSE); + break; + case I_LAYER: + UpdateFile(¬eDataInUI, LY_NOTE, FALSE); + break; + case I_PATH: + if (IsFileValid(noteDataInUI.noteData.fileData.path)) { + wControlActive(fileEditPLs[I_OPEN].control, TRUE); + } else { + wControlActive(fileEditPLs[I_OPEN].control, FALSE); + } + break; + default: + break; + } +} + + +/** + * Handle Cancel button: restore old values for layer and position + */ + +static void +FileEditCancel( wWin_p junk) +{ + if (inDescribeCmd) { + UpdateFile(¬eDataInUI, CANCEL_NOTE, FALSE); + } + ResetIfNotSticky(); + wHide(fileEditW); +} +/** + * Handle OK button: make sure the entered filename is syntactically valid, update + * the layout and close the dialog + * + * \param junk + */ + +static void +FileEditOK(void *junk) +{ + UpdateFile(¬eDataInUI, OK_FILE, FALSE); + wHide(fileEditW); + ResetIfNotSticky(); + FileIsChanged(); +} + +/** + * Show the attachment edit dialog. Create if non-existant + * + * \param trk IN track element to edit + * \param windowTitle IN title for the edit dialog window + */ + +void CreateEditFileDialog(track_p trk, char * windowTitle) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + + if (!fileEditW) { + noteDataInUI.noteData.fileData.path = MyMalloc(PATHMAXIMUMLENGTH); + noteDataInUI.noteData.fileData.title = MyMalloc(TITLEMAXIMUMLENGTH); + fileEditPLs[I_TITLE].valueP = noteDataInUI.noteData.fileData.title; + fileEditPLs[I_PATH].valueP = noteDataInUI.noteData.fileData.path; + + ParamRegister(&fileEditPG); + fileEditW = ParamCreateDialog(&fileEditPG, + "", + _("Done"), FileEditOK, + FileEditCancel, TRUE, NULL, + F_BLOCK, + FileDlgUpdate); + } + + wWinSetTitle(fileEditPG.win, MakeWindowTitle(windowTitle)); + + noteDataInUI.pos = xx->pos; + noteDataInUI.layer = xx->layer; + noteDataInUI.trk = trk; + strscpy(noteDataInUI.noteData.fileData.title, xx->noteData.fileData.title, TITLEMAXIMUMLENGTH); + strscpy(noteDataInUI.noteData.fileData.path, xx->noteData.fileData.path, PATHMAXIMUMLENGTH); + FillLayerList((wList_p)fileEditPLs[I_LAYER].control); + ParamLoadControls(&fileEditPG); + wControlActive(fileEditPLs[I_OPEN].control, (IsFileValid(noteDataInUI.noteData.fileData.path)?TRUE:FALSE)); + + wShow(fileEditW); +} + +/** + * Activate note if double clicked + * \param trk the note + */ + +void ActivateFileNote(track_p trk) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + + NoteFileOpen(xx->noteData.fileData.path); +} + +/** + * Describe and enable editing of an existing link note + * + * \param trk the existing, valid note + * \param str the field to put a text version of the note so it will appear on the status line + * \param len the lenght of the field + */ + +void DescribeFileNote(track_p trk, char * str, CSIZE_T len) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + DynString statusLine; + + DynStringMalloc(&statusLine, 80); + + DynStringPrintf(&statusLine, + _("Document(%d) Layer=%d %-.80s [%s]"), + GetTrkIndex(trk), + GetTrkLayer(trk) + 1, + xx->noteData.fileData.title, + xx->noteData.fileData.path); + + strcpy(str, DynStringToCStr(&statusLine)); + DynStringFree(&statusLine); + + if (inDescribeCmd) { + NoteStateSave(trk); + + CreateEditFileDialog(trk, _("Update document")); + } +} + +/** + * Take a new note track element and initialize it. It will be + * initialized with defaults and can then be edited by the user. + * + * \param the newly created trk + */ + +void NewFileNoteUI(track_p trk) +{ + struct extraDataNote * xx = (struct extraDataNote *)GetTrkExtraData(trk); + char *tmpPtrText = _("Describe the file"); + + xx->noteData.fileData.title = MyStrdup(tmpPtrText); + xx->noteData.fileData.path = MyStrdup(""); + + CreateEditFileDialog(trk, + _("Attach document")); +} diff --git a/app/bin/helphelper.c b/app/bin/helphelper.c index 013ff0a..36083b8 100644 --- a/app/bin/helphelper.c +++ b/app/bin/helphelper.c @@ -117,6 +117,7 @@ main( int argc, char **argv ) if( numBytes == sizeof(int)) { printf( "HelpHelper: Expecting %d bytes\n", len ); numBytes2 = read( handleOfPipe, buffer, len + 1 ); + buffer[numBytes2] = '\0'; if (numBytes2 > 0) printf( "HelpHelper: Display help on: %s\n", buffer ); diff --git a/app/bin/include/dirent.h b/app/bin/include/dirent.h new file mode 100644 index 0000000..fdfbe5b --- /dev/null +++ b/app/bin/include/dirent.h @@ -0,0 +1,1254 @@ +/*
+ * Dirent interface for Microsoft Visual Studio
+ *
+ * Copyright (C) 2006-2012 Toni Ronkko
+ * This file is part of dirent. Dirent may be freely distributed
+ * under the MIT license. For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#ifndef DIRENT_H
+#define DIRENT_H
+
+ /*
+ * Include windows.h without Windows Sockets 1.1 to prevent conflicts with
+ * Windows Sockets 2.0.
+ */
+#ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <wchar.h>
+#include <string.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+ /* Indicates that d_type field is available in dirent structure */
+#define _DIRENT_HAVE_D_TYPE
+
+/* Indicates that d_namlen field is available in dirent structure */
+#define _DIRENT_HAVE_D_NAMLEN
+
+/* Entries missing from MSVC 6.0 */
+#if !defined(FILE_ATTRIBUTE_DEVICE)
+# define FILE_ATTRIBUTE_DEVICE 0x40
+#endif
+
+/* File type and permission flags for stat(), general mask */
+#if !defined(S_IFMT)
+# define S_IFMT _S_IFMT
+#endif
+
+/* Directory bit */
+#if !defined(S_IFDIR)
+# define S_IFDIR _S_IFDIR
+#endif
+
+/* Character device bit */
+#if !defined(S_IFCHR)
+# define S_IFCHR _S_IFCHR
+#endif
+
+/* Pipe bit */
+#if !defined(S_IFFIFO)
+# define S_IFFIFO _S_IFFIFO
+#endif
+
+/* Regular file bit */
+#if !defined(S_IFREG)
+# define S_IFREG _S_IFREG
+#endif
+
+/* Read permission */
+#if !defined(S_IREAD)
+# define S_IREAD _S_IREAD
+#endif
+
+/* Write permission */
+#if !defined(S_IWRITE)
+# define S_IWRITE _S_IWRITE
+#endif
+
+/* Execute permission */
+#if !defined(S_IEXEC)
+# define S_IEXEC _S_IEXEC
+#endif
+
+/* Pipe */
+#if !defined(S_IFIFO)
+# define S_IFIFO _S_IFIFO
+#endif
+
+/* Block device */
+#if !defined(S_IFBLK)
+# define S_IFBLK 0
+#endif
+
+/* Link */
+#if !defined(S_IFLNK)
+# define S_IFLNK 0
+#endif
+
+/* Socket */
+#if !defined(S_IFSOCK)
+# define S_IFSOCK 0
+#endif
+
+/* Read user permission */
+#if !defined(S_IRUSR)
+# define S_IRUSR S_IREAD
+#endif
+
+/* Write user permission */
+#if !defined(S_IWUSR)
+# define S_IWUSR S_IWRITE
+#endif
+
+/* Execute user permission */
+#if !defined(S_IXUSR)
+# define S_IXUSR 0
+#endif
+
+/* Read group permission */
+#if !defined(S_IRGRP)
+# define S_IRGRP 0
+#endif
+
+/* Write group permission */
+#if !defined(S_IWGRP)
+# define S_IWGRP 0
+#endif
+
+/* Execute group permission */
+#if !defined(S_IXGRP)
+# define S_IXGRP 0
+#endif
+
+/* Read others permission */
+#if !defined(S_IROTH)
+# define S_IROTH 0
+#endif
+
+/* Write others permission */
+#if !defined(S_IWOTH)
+# define S_IWOTH 0
+#endif
+
+/* Execute others permission */
+#if !defined(S_IXOTH)
+# define S_IXOTH 0
+#endif
+
+/* Maximum length of file name */
+#if !defined(PATH_MAX)
+# define PATH_MAX MAX_PATH
+#endif
+#if !defined(FILENAME_MAX)
+# define FILENAME_MAX MAX_PATH
+#endif
+#if !defined(NAME_MAX)
+# define NAME_MAX FILENAME_MAX
+#endif
+
+/* File type flags for d_type */
+#define DT_UNKNOWN 0
+#define DT_REG S_IFREG
+#define DT_DIR S_IFDIR
+#define DT_FIFO S_IFIFO
+#define DT_SOCK S_IFSOCK
+#define DT_CHR S_IFCHR
+#define DT_BLK S_IFBLK
+#define DT_LNK S_IFLNK
+
+/* Macros for converting between st_mode and d_type */
+#define IFTODT(mode) ((mode) & S_IFMT)
+#define DTTOIF(type) (type)
+
+/*
+ * File type macros. Note that block devices, sockets and links cannot be
+ * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
+ * only defined for compatibility. These macros should always return false
+ * on Windows.
+ */
+#if !defined(S_ISFIFO)
+# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISDIR)
+# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG)
+# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISLNK)
+# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK)
+# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISCHR)
+# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISBLK)
+# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
+#endif
+
+ /* Return the exact length of the file name without zero terminator */
+#define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
+
+/* Return the maximum size of a file name */
+#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1)
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+ /* Wide-character version */
+ struct _wdirent {
+ /* Always zero */
+ long d_ino;
+
+ /* File position within stream */
+ long d_off;
+
+ /* Structure size */
+ unsigned short d_reclen;
+
+ /* Length of name without \0 */
+ size_t d_namlen;
+
+ /* File type */
+ int d_type;
+
+ /* File name */
+ wchar_t d_name[PATH_MAX + 1];
+ };
+ typedef struct _wdirent _wdirent;
+
+ struct _WDIR {
+ /* Current directory entry */
+ struct _wdirent ent;
+
+ /* Private file data */
+ WIN32_FIND_DATAW data;
+
+ /* True if data is valid */
+ int cached;
+
+ /* Win32 search handle */
+ HANDLE handle;
+
+ /* Initial directory name */
+ wchar_t *patt;
+ };
+ typedef struct _WDIR _WDIR;
+
+ /* Multi-byte character version */
+ struct dirent {
+ /* Always zero */
+ long d_ino;
+
+ /* File position within stream */
+ long d_off;
+
+ /* Structure size */
+ unsigned short d_reclen;
+
+ /* Length of name without \0 */
+ size_t d_namlen;
+
+ /* File type */
+ int d_type;
+
+ /* File name */
+ char d_name[PATH_MAX + 1];
+ };
+ typedef struct dirent dirent;
+
+ struct DIR {
+ struct dirent ent;
+ struct _WDIR *wdirp;
+ };
+ typedef struct DIR DIR;
+
+
+ /* Dirent functions */
+ static DIR *opendir(const char *dirname);
+ static _WDIR *_wopendir(const wchar_t *dirname);
+
+ static struct dirent *readdir(DIR *dirp);
+ static struct _wdirent *_wreaddir(_WDIR *dirp);
+
+ static int readdir_r(
+ DIR *dirp, struct dirent *entry, struct dirent **result);
+ static int _wreaddir_r(
+ _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result);
+
+ static int closedir(DIR *dirp);
+ static int _wclosedir(_WDIR *dirp);
+
+ static void rewinddir(DIR* dirp);
+ static void _wrewinddir(_WDIR* dirp);
+
+ static int scandir(const char *dirname, struct dirent ***namelist,
+ int(*filter)(const struct dirent*),
+ int(*compare)(const struct dirent**, const struct dirent**));
+
+ static int alphasort(const struct dirent **a, const struct dirent **b);
+
+ static int versionsort(const struct dirent **a, const struct dirent **b);
+
+
+ /* For compatibility with Symbian */
+#define wdirent _wdirent
+#define WDIR _WDIR
+#define wopendir _wopendir
+#define wreaddir _wreaddir
+#define wclosedir _wclosedir
+#define wrewinddir _wrewinddir
+
+
+/* Internal utility functions */
+ static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp);
+ static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp);
+
+ static int dirent_mbstowcs_s(
+ size_t *pReturnValue,
+ wchar_t *wcstr,
+ size_t sizeInWords,
+ const char *mbstr,
+ size_t count);
+
+ static int dirent_wcstombs_s(
+ size_t *pReturnValue,
+ char *mbstr,
+ size_t sizeInBytes,
+ const wchar_t *wcstr,
+ size_t count);
+
+ static void dirent_set_errno(int error);
+
+
+ /*
+ * Open directory stream DIRNAME for read and return a pointer to the
+ * internal working area that is used to retrieve individual directory
+ * entries.
+ */
+ static _WDIR*
+ _wopendir(
+ const wchar_t *dirname)
+ {
+ _WDIR *dirp = NULL;
+ int error;
+
+ /* Must have directory name */
+ if (dirname == NULL || dirname[0] == '\0') {
+ dirent_set_errno(ENOENT);
+ return NULL;
+ }
+
+ /* Allocate new _WDIR structure */
+ dirp = (_WDIR*)malloc(sizeof(struct _WDIR));
+ if (dirp != NULL) {
+ DWORD n;
+
+ /* Reset _WDIR structure */
+ dirp->handle = INVALID_HANDLE_VALUE;
+ dirp->patt = NULL;
+ dirp->cached = 0;
+
+ /* Compute the length of full path plus zero terminator
+ *
+ * Note that on WinRT there's no way to convert relative paths
+ * into absolute paths, so just assume it is an absolute path.
+ */
+# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
+ n = wcslen(dirname);
+# else
+ n = GetFullPathNameW(dirname, 0, NULL, NULL);
+# endif
+
+ /* Allocate room for absolute directory name and search pattern */
+ dirp->patt = (wchar_t*)malloc(sizeof(wchar_t) * n + 16);
+ if (dirp->patt) {
+
+ /*
+ * Convert relative directory name to an absolute one. This
+ * allows rewinddir() to function correctly even when current
+ * working directory is changed between opendir() and rewinddir().
+ *
+ * Note that on WinRT there's no way to convert relative paths
+ * into absolute paths, so just assume it is an absolute path.
+ */
+# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
+ wcsncpy_s(dirp->patt, n + 1, dirname, n);
+# else
+ n = GetFullPathNameW(dirname, n, dirp->patt, NULL);
+# endif
+ if (n > 0) {
+ wchar_t *p;
+
+ /* Append search pattern \* to the directory name */
+ p = dirp->patt + n;
+ if (dirp->patt < p) {
+ switch (p[-1]) {
+ case '\\':
+ case '/':
+ case ':':
+ /* Directory ends in path separator, e.g. c:\temp\ */
+ /*NOP*/;
+ break;
+
+ default:
+ /* Directory name doesn't end in path separator */
+ *p++ = '\\';
+ }
+ }
+ *p++ = '*';
+ *p = '\0';
+
+ /* Open directory stream and retrieve the first entry */
+ if (dirent_first(dirp)) {
+ /* Directory stream opened successfully */
+ error = 0;
+ }
+ else {
+ /* Cannot retrieve first entry */
+ error = 1;
+ dirent_set_errno(ENOENT);
+ }
+
+ }
+ else {
+ /* Cannot retrieve full path name */
+ dirent_set_errno(ENOENT);
+ error = 1;
+ }
+
+ }
+ else {
+ /* Cannot allocate memory for search pattern */
+ error = 1;
+ }
+
+ }
+ else {
+ /* Cannot allocate _WDIR structure */
+ error = 1;
+ }
+
+ /* Clean up in case of error */
+ if (error && dirp) {
+ _wclosedir(dirp);
+ dirp = NULL;
+ }
+
+ return dirp;
+ }
+
+ /*
+ * Read next directory entry.
+ *
+ * Returns pointer to static directory entry which may be overwritten by
+ * subsequent calls to _wreaddir().
+ */
+ static struct _wdirent*
+ _wreaddir(
+ _WDIR *dirp)
+ {
+ struct _wdirent *entry;
+
+ /*
+ * Read directory entry to buffer. We can safely ignore the return value
+ * as entry will be set to NULL in case of error.
+ */
+ (void)_wreaddir_r(dirp, &dirp->ent, &entry);
+
+ /* Return pointer to statically allocated directory entry */
+ return entry;
+ }
+
+ /*
+ * Read next directory entry.
+ *
+ * Returns zero on success. If end of directory stream is reached, then sets
+ * result to NULL and returns zero.
+ */
+ static int
+ _wreaddir_r(
+ _WDIR *dirp,
+ struct _wdirent *entry,
+ struct _wdirent **result)
+ {
+ WIN32_FIND_DATAW *datap;
+
+ /* Read next directory entry */
+ datap = dirent_next(dirp);
+ if (datap) {
+ size_t n;
+ DWORD attr;
+
+ /*
+ * Copy file name as wide-character string. If the file name is too
+ * long to fit in to the destination buffer, then truncate file name
+ * to PATH_MAX characters and zero-terminate the buffer.
+ */
+ n = 0;
+ while (n < PATH_MAX && datap->cFileName[n] != 0) {
+ entry->d_name[n] = datap->cFileName[n];
+ n++;
+ }
+ entry->d_name[n] = 0;
+
+ /* Length of file name excluding zero terminator */
+ entry->d_namlen = n;
+
+ /* File type */
+ attr = datap->dwFileAttributes;
+ if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
+ entry->d_type = DT_CHR;
+ }
+ else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ entry->d_type = DT_DIR;
+ }
+ else {
+ entry->d_type = DT_REG;
+ }
+
+ /* Reset dummy fields */
+ entry->d_ino = 0;
+ entry->d_off = 0;
+ entry->d_reclen = sizeof(struct _wdirent);
+
+ /* Set result address */
+ *result = entry;
+
+ }
+ else {
+
+ /* Return NULL to indicate end of directory */
+ *result = NULL;
+
+ }
+
+ return /*OK*/0;
+ }
+
+ /*
+ * Close directory stream opened by opendir() function. This invalidates the
+ * DIR structure as well as any directory entry read previously by
+ * _wreaddir().
+ */
+ static int
+ _wclosedir(
+ _WDIR *dirp)
+ {
+ int ok;
+ if (dirp) {
+
+ /* Release search handle */
+ if (dirp->handle != INVALID_HANDLE_VALUE) {
+ FindClose(dirp->handle);
+ dirp->handle = INVALID_HANDLE_VALUE;
+ }
+
+ /* Release search pattern */
+ if (dirp->patt) {
+ free(dirp->patt);
+ dirp->patt = NULL;
+ }
+
+ /* Release directory structure */
+ free(dirp);
+ ok = /*success*/0;
+
+ }
+ else {
+
+ /* Invalid directory stream */
+ dirent_set_errno(EBADF);
+ ok = /*failure*/-1;
+
+ }
+ return ok;
+ }
+
+ /*
+ * Rewind directory stream such that _wreaddir() returns the very first
+ * file name again.
+ */
+ static void
+ _wrewinddir(
+ _WDIR* dirp)
+ {
+ if (dirp) {
+ /* Release existing search handle */
+ if (dirp->handle != INVALID_HANDLE_VALUE) {
+ FindClose(dirp->handle);
+ }
+
+ /* Open new search handle */
+ dirent_first(dirp);
+ }
+ }
+
+ /* Get first directory entry (internal) */
+ static WIN32_FIND_DATAW*
+ dirent_first(
+ _WDIR *dirp)
+ {
+ WIN32_FIND_DATAW *datap;
+
+ /* Open directory and retrieve the first entry */
+ dirp->handle = FindFirstFileExW(
+ dirp->patt, FindExInfoStandard, &dirp->data,
+ FindExSearchNameMatch, NULL, 0);
+ if (dirp->handle != INVALID_HANDLE_VALUE) {
+
+ /* a directory entry is now waiting in memory */
+ datap = &dirp->data;
+ dirp->cached = 1;
+
+ }
+ else {
+
+ /* Failed to re-open directory: no directory entry in memory */
+ dirp->cached = 0;
+ datap = NULL;
+
+ }
+ return datap;
+ }
+
+ /*
+ * Get next directory entry (internal).
+ *
+ * Returns
+ */
+ static WIN32_FIND_DATAW*
+ dirent_next(
+ _WDIR *dirp)
+ {
+ WIN32_FIND_DATAW *p;
+
+ /* Get next directory entry */
+ if (dirp->cached != 0) {
+
+ /* A valid directory entry already in memory */
+ p = &dirp->data;
+ dirp->cached = 0;
+
+ }
+ else if (dirp->handle != INVALID_HANDLE_VALUE) {
+
+ /* Get the next directory entry from stream */
+ if (FindNextFileW(dirp->handle, &dirp->data) != FALSE) {
+ /* Got a file */
+ p = &dirp->data;
+ }
+ else {
+ /* The very last entry has been processed or an error occurred */
+ FindClose(dirp->handle);
+ dirp->handle = INVALID_HANDLE_VALUE;
+ p = NULL;
+ }
+
+ }
+ else {
+
+ /* End of directory stream reached */
+ p = NULL;
+
+ }
+
+ return p;
+ }
+
+ /*
+ * Open directory stream using plain old C-string.
+ */
+ static DIR*
+ opendir(
+ const char *dirname)
+ {
+ struct DIR *dirp;
+ int error;
+
+ /* Must have directory name */
+ if (dirname == NULL || dirname[0] == '\0') {
+ dirent_set_errno(ENOENT);
+ return NULL;
+ }
+
+ /* Allocate memory for DIR structure */
+ dirp = (DIR*)malloc(sizeof(struct DIR));
+ if (dirp) {
+ wchar_t wname[PATH_MAX + 1];
+ size_t n;
+
+ /* Convert directory name to wide-character string */
+ error = dirent_mbstowcs_s(
+ &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1);
+ if (!error) {
+
+ /* Open directory stream using wide-character name */
+ dirp->wdirp = _wopendir(wname);
+ if (dirp->wdirp) {
+ /* Directory stream opened */
+ error = 0;
+ }
+ else {
+ /* Failed to open directory stream */
+ error = 1;
+ }
+
+ }
+ else {
+ /*
+ * Cannot convert file name to wide-character string. This
+ * occurs if the string contains invalid multi-byte sequences or
+ * the output buffer is too small to contain the resulting
+ * string.
+ */
+ error = 1;
+ }
+
+ }
+ else {
+ /* Cannot allocate DIR structure */
+ error = 1;
+ }
+
+ /* Clean up in case of error */
+ if (error && dirp) {
+ free(dirp);
+ dirp = NULL;
+ }
+
+ return dirp;
+ }
+
+ /*
+ * Read next directory entry.
+ */
+ static struct dirent*
+ readdir(
+ DIR *dirp)
+ {
+ struct dirent *entry;
+
+ /*
+ * Read directory entry to buffer. We can safely ignore the return value
+ * as entry will be set to NULL in case of error.
+ */
+ (void)readdir_r(dirp, &dirp->ent, &entry);
+
+ /* Return pointer to statically allocated directory entry */
+ return entry;
+ }
+
+ /*
+ * Read next directory entry into called-allocated buffer.
+ *
+ * Returns zero on success. If the end of directory stream is reached, then
+ * sets result to NULL and returns zero.
+ */
+ static int
+ readdir_r(
+ DIR *dirp,
+ struct dirent *entry,
+ struct dirent **result)
+ {
+ WIN32_FIND_DATAW *datap;
+
+ /* Read next directory entry */
+ datap = dirent_next(dirp->wdirp);
+ if (datap) {
+ size_t n;
+ int error;
+
+ /* Attempt to convert file name to multi-byte string */
+ error = dirent_wcstombs_s(
+ &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1);
+
+ /*
+ * If the file name cannot be represented by a multi-byte string,
+ * then attempt to use old 8+3 file name. This allows traditional
+ * Unix-code to access some file names despite of unicode
+ * characters, although file names may seem unfamiliar to the user.
+ *
+ * Be ware that the code below cannot come up with a short file
+ * name unless the file system provides one. At least
+ * VirtualBox shared folders fail to do this.
+ */
+ if (error && datap->cAlternateFileName[0] != '\0') {
+ error = dirent_wcstombs_s(
+ &n, entry->d_name, PATH_MAX + 1,
+ datap->cAlternateFileName, PATH_MAX + 1);
+ }
+
+ if (!error) {
+ DWORD attr;
+
+ /* Length of file name excluding zero terminator */
+ entry->d_namlen = n - 1;
+
+ /* File attributes */
+ attr = datap->dwFileAttributes;
+ if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
+ entry->d_type = DT_CHR;
+ }
+ else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ entry->d_type = DT_DIR;
+ }
+ else {
+ entry->d_type = DT_REG;
+ }
+
+ /* Reset dummy fields */
+ entry->d_ino = 0;
+ entry->d_off = 0;
+ entry->d_reclen = sizeof(struct dirent);
+
+ }
+ else {
+
+ /*
+ * Cannot convert file name to multi-byte string so construct
+ * an erroneous directory entry and return that. Note that
+ * we cannot return NULL as that would stop the processing
+ * of directory entries completely.
+ */
+ entry->d_name[0] = '?';
+ entry->d_name[1] = '\0';
+ entry->d_namlen = 1;
+ entry->d_type = DT_UNKNOWN;
+ entry->d_ino = 0;
+ entry->d_off = -1;
+ entry->d_reclen = 0;
+
+ }
+
+ /* Return pointer to directory entry */
+ *result = entry;
+
+ }
+ else {
+
+ /* No more directory entries */
+ *result = NULL;
+
+ }
+
+ return /*OK*/0;
+ }
+
+ /*
+ * Close directory stream.
+ */
+ static int
+ closedir(
+ DIR *dirp)
+ {
+ int ok;
+ if (dirp) {
+
+ /* Close wide-character directory stream */
+ ok = _wclosedir(dirp->wdirp);
+ dirp->wdirp = NULL;
+
+ /* Release multi-byte character version */
+ free(dirp);
+
+ }
+ else {
+
+ /* Invalid directory stream */
+ dirent_set_errno(EBADF);
+ ok = /*failure*/-1;
+
+ }
+ return ok;
+ }
+
+ /*
+ * Rewind directory stream to beginning.
+ */
+ static void
+ rewinddir(
+ DIR* dirp)
+ {
+ /* Rewind wide-character string directory stream */
+ _wrewinddir(dirp->wdirp);
+ }
+
+ /*
+ * Scan directory for entries.
+ */
+ static int
+ scandir(
+ const char *dirname,
+ struct dirent ***namelist,
+ int(*filter)(const struct dirent*),
+ int(*compare)(const struct dirent**, const struct dirent**))
+ {
+ struct dirent **files = NULL;
+ size_t size = 0;
+ size_t allocated = 0;
+ const size_t init_size = 1;
+ DIR *dir = NULL;
+ struct dirent *entry;
+ struct dirent *tmp = NULL;
+ size_t i;
+ int result = 0;
+
+ /* Open directory stream */
+ dir = opendir(dirname);
+ if (dir) {
+
+ /* Read directory entries to memory */
+ while (1) {
+
+ /* Enlarge pointer table to make room for another pointer */
+ if (size >= allocated) {
+ void *p;
+ size_t num_entries;
+
+ /* Compute number of entries in the enlarged pointer table */
+ if (size < init_size) {
+ /* Allocate initial pointer table */
+ num_entries = init_size;
+ }
+ else {
+ /* Double the size */
+ num_entries = size * 2;
+ }
+
+ /* Allocate first pointer table or enlarge existing table */
+ p = realloc(files, sizeof(void*) * num_entries);
+ if (p != NULL) {
+ /* Got the memory */
+ files = (dirent**)p;
+ allocated = num_entries;
+ }
+ else {
+ /* Out of memory */
+ result = -1;
+ break;
+ }
+
+ }
+
+ /* Allocate room for temporary directory entry */
+ if (tmp == NULL) {
+ tmp = (struct dirent*) malloc(sizeof(struct dirent));
+ if (tmp == NULL) {
+ /* Cannot allocate temporary directory entry */
+ result = -1;
+ break;
+ }
+ }
+
+ /* Read directory entry to temporary area */
+ if (readdir_r(dir, tmp, &entry) == /*OK*/0) {
+
+ /* Did we get an entry? */
+ if (entry != NULL) {
+ int pass;
+
+ /* Determine whether to include the entry in result */
+ if (filter) {
+ /* Let the filter function decide */
+ pass = filter(tmp);
+ }
+ else {
+ /* No filter function, include everything */
+ pass = 1;
+ }
+
+ if (pass) {
+ /* Store the temporary entry to pointer table */
+ files[size++] = tmp;
+ tmp = NULL;
+
+ /* Keep up with the number of files */
+ result++;
+ }
+
+ }
+ else {
+
+ /*
+ * End of directory stream reached => sort entries and
+ * exit.
+ */
+ qsort(files, size, sizeof(void*),
+ (int(*) (const void*, const void*)) compare);
+ break;
+
+ }
+
+ }
+ else {
+ /* Error reading directory entry */
+ result = /*Error*/ -1;
+ break;
+ }
+
+ }
+
+ }
+ else {
+ /* Cannot open directory */
+ result = /*Error*/ -1;
+ }
+
+ /* Release temporary directory entry */
+ if (tmp) {
+ free(tmp);
+ }
+
+ /* Release allocated memory on error */
+ if (result < 0) {
+ for (i = 0; i < size; i++) {
+ free(files[i]);
+ }
+ free(files);
+ files = NULL;
+ }
+
+ /* Close directory stream */
+ if (dir) {
+ closedir(dir);
+ }
+
+ /* Pass pointer table to caller */
+ if (namelist) {
+ *namelist = files;
+ }
+ return result;
+ }
+
+ /* Alphabetical sorting */
+ static int
+ alphasort(
+ const struct dirent **a, const struct dirent **b)
+ {
+ return strcoll((*a)->d_name, (*b)->d_name);
+ }
+
+ /* Sort versions */
+ static int
+ versionsort(
+ const struct dirent **a, const struct dirent **b)
+ {
+ /* FIXME: implement strverscmp and use that */
+ return alphasort(a, b);
+ }
+
+ /* Convert multi-byte string to wide character string */
+ static int
+ dirent_mbstowcs_s(
+ size_t *pReturnValue,
+ wchar_t *wcstr,
+ size_t sizeInWords,
+ const char *mbstr,
+ size_t count)
+ {
+ int error;
+ int n;
+ size_t len;
+ UINT cp;
+ DWORD flags;
+
+ /* Determine code page for multi-byte string */
+ if (AreFileApisANSI()) {
+ /* Default ANSI code page */
+ cp = GetACP();
+ }
+ else {
+ /* Default OEM code page */
+ cp = GetOEMCP();
+ }
+
+ /*
+ * Determine flags based on the character set. For more information,
+ * please see https://docs.microsoft.com/fi-fi/windows/desktop/api/stringapiset/nf-stringapiset-multibytetowidechar
+ */
+ switch (cp) {
+ case 42:
+ case 50220:
+ case 50221:
+ case 50222:
+ case 50225:
+ case 50227:
+ case 50229:
+ case 57002:
+ case 57003:
+ case 57004:
+ case 57005:
+ case 57006:
+ case 57007:
+ case 57008:
+ case 57009:
+ case 57010:
+ case 57011:
+ case 65000:
+ /* MultiByteToWideChar does not support MB_ERR_INVALID_CHARS */
+ flags = 0;
+ break;
+
+ default:
+ /*
+ * Ask MultiByteToWideChar to return an error if a multi-byte
+ * character cannot be converted to a wide-character.
+ */
+ flags = MB_ERR_INVALID_CHARS;
+ }
+
+ /* Compute the length of input string without zero-terminator */
+ len = 0;
+ while (mbstr[len] != '\0' && len < count) {
+ len++;
+ }
+
+ /* Convert to wide-character string */
+ n = MultiByteToWideChar(
+ /* Source code page */ cp,
+ /* Flags */ flags,
+ /* Pointer to string to convert */ mbstr,
+ /* Size of multi-byte string */ (int)len,
+ /* Pointer to output buffer */ wcstr,
+ /* Size of output buffer */ sizeInWords - 1
+ );
+
+ /* Ensure that output buffer is zero-terminated */
+ wcstr[n] = '\0';
+
+ /* Return length of wide-character string with zero-terminator */
+ *pReturnValue = (size_t)(n + 1);
+
+ /* Return zero if conversion succeeded */
+ if (n > 0) {
+ error = 0;
+ }
+ else {
+ error = 1;
+ }
+ return error;
+ }
+
+ /* Convert wide-character string to multi-byte string */
+ static int
+ dirent_wcstombs_s(
+ size_t *pReturnValue,
+ char *mbstr,
+ size_t sizeInBytes, /* max size of mbstr */
+ const wchar_t *wcstr,
+ size_t count)
+ {
+ int n;
+ int error;
+ UINT cp;
+ size_t len;
+ BOOL flag = 0;
+ LPBOOL pflag;
+
+ /* Determine code page for multi-byte string */
+ if (AreFileApisANSI()) {
+ /* Default ANSI code page */
+ cp = GetACP();
+ }
+ else {
+ /* Default OEM code page */
+ cp = GetOEMCP();
+ }
+
+ /* Compute the length of input string without zero-terminator */
+ len = 0;
+ while (wcstr[len] != '\0' && len < count) {
+ len++;
+ }
+
+ /*
+ * Determine if we can ask WideCharToMultiByte to return information on
+ * broken characters. For more information, please see
+ * https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-widechartomultibyte
+ */
+ switch (cp) {
+ case CP_UTF7:
+ case CP_UTF8:
+ /*
+ * WideCharToMultiByte fails if we request information on default
+ * characters. This is just a nuisance but doesn't affect the
+ * conversion: if Windows is configured to use UTF-8, then the default
+ * character should not be needed anyway.
+ */
+ pflag = NULL;
+ break;
+
+ default:
+ /*
+ * Request that WideCharToMultiByte sets the flag if it uses the
+ * default character.
+ */
+ pflag = &flag;
+ }
+
+ /* Convert wide-character string to multi-byte character string */
+ n = WideCharToMultiByte(
+ /* Target code page */ cp,
+ /* Flags */ 0,
+ /* Pointer to unicode string */ wcstr,
+ /* Length of unicode string */ (int)len,
+ /* Pointer to output buffer */ mbstr,
+ /* Size of output buffer */ sizeInBytes - 1,
+ /* Default character */ NULL,
+ /* Whether default character was used or not */ pflag
+ );
+
+ /* Ensure that output buffer is zero-terminated */
+ mbstr[n] = '\0';
+
+ /* Return length of multi-byte string with zero-terminator */
+ *pReturnValue = (size_t)(n + 1);
+
+ /* Return zero if conversion succeeded without using default characters */
+ if (n > 0 && flag == 0) {
+ error = 0;
+ }
+ else {
+ error = 1;
+ }
+ return error;
+ }
+
+ /* Set errno variable */
+ static void
+ dirent_set_errno(
+ int error)
+ {
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+
+ /* Microsoft Visual Studio 2005 and later */
+ _set_errno(error);
+
+#else
+
+ /* Non-Microsoft compiler or older Microsoft compiler */
+ errno = error;
+
+#endif
+ }
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*DIRENT_H*/#pragma once
diff --git a/app/bin/include/paramfile.h b/app/bin/include/paramfile.h new file mode 100644 index 0000000..b177b15 --- /dev/null +++ b/app/bin/include/paramfile.h @@ -0,0 +1,26 @@ +#ifndef HAVE_PARAMFILE_H + #define HAVE_PARAMFILE_H + #include <stdbool.h> + #include <wlib.h> + #include "common.h" + + extern DIST_T curBarScale; + extern dynArr_t paramProc_da; + extern dynArr_t paramFileInfo_da; + + void ParamCheckSumLine(char * line); + wBool_t IsParamValid(int fileInx); + bool IsParamFileDeleted(int fileInx); + bool IsParamFileFavorite(int fileInx); + void SetParamFileState(int index); + int ReadParamFile(const char *fileName); + int ReloadDeletedParamFile(int fileindex); + void SetParamFileDeleted(int fileInx, bool deleted); + void SetParamFileFavorite(int fileInx, bool favorite); + char * GetParamFileName(int fileInx); + char * GetParamFileContents(int fileInx); + bool ReadParams(long key, const char * dirName, const char * fileName); + + #define CONTENTSCOMMAND "CONTENTS" + char *GetParameterFileContent(char *file); +#endif // !HAVE_PARAMFILE_H diff --git a/app/bin/include/paramfilelist.h b/app/bin/include/paramfilelist.h new file mode 100644 index 0000000..a1c081d --- /dev/null +++ b/app/bin/include/paramfilelist.h @@ -0,0 +1,32 @@ +#ifndef HAVE_PARAMFILELIST_H + #define HAVE_PARAMFILELIST_H + #include <stdbool.h> + #include "include/paramfile.h" + + typedef struct { + char * name; /** < name of parameter file */ + char * contents; + bool deleted; + bool valid; /** < FALSE for dropped file */ + bool favorite; + enum paramFileState trackState; + } paramFileInfo_t; + typedef paramFileInfo_t * paramFileInfo_p; + + #define paramFileInfo(N) DYNARR_N( paramFileInfo_t, paramFileInfo_da, N ) + + char *GetParamFileDir(void); + void SetParamFileDir(char *fullPath); + void LoadParamFileList(void); + BOOL_T ReadDefaultParams(const char * dirName); + void SaveParamFileList(void); + int GetParamFileCount(); + void UpdateParamFileList(void); + void ParamFilesChange(long changes); + int LoadParamFile(int files, char ** fileName, void * data); + BOOL_T ParamFileListInit(void); + + void SearchUiOk(void * junk); + bool ReloadParamFile(wIndex_t index); + bool UnloadParamFile(wIndex_t fileIndex); +#endif // !HAVE_PARAMFILELIST_H diff --git a/app/bin/include/partcatalog.h b/app/bin/include/partcatalog.h new file mode 100644 index 0000000..eec9d1e --- /dev/null +++ b/app/bin/include/partcatalog.h @@ -0,0 +1,70 @@ +/** \file partcatalog.h +* Manage the catalog of track parameter files +*/ +/* XTrkCad - Model Railroad CAD +* Copyright (C) 2019 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. +*/ + +#ifndef HAVE_TRACKCATALOG_H +#define HAVE_TRACKCATALOG_H + +#include <stdbool.h> + +#define MAXFILESPERCONTENT 10 /**< count of files with the same content header */ +#define ESTIMATED_CONTENTS_WORDS 10 /**< average count of words in CONTENTS header */ + +struct sCatalogEntry { + struct sCatalogEntry *next; + unsigned files; /**< current count of files */ + char *fullFileName[MAXFILESPERCONTENT]; /**< fully qualified file name */ + char *contents; /**< content field of parameter file */ + struct sCatalogEntry *indirect; /**< pointer to another catalog entry */ +}; + +typedef struct sCatalogEntry CatalogEntry; + +struct sIndexEntry { + CatalogEntry *value; /**< catalog entry having the key word in contents */ + char *keyWord; /**< keyword */ +}; + +typedef struct sIndexEntry IndexEntry; + +struct sTrackLibrary { + CatalogEntry *catalog; /**< list of files cataloged */ + IndexEntry *index; /**< Index for lookup */ + unsigned wordCount; /**< How many words indexed */ + void * words_array; /**< The array of words */ + unsigned trackTypeCount; /**< */ +}; + +typedef struct sTrackLibrary + TrackLibrary; /**< core data structure for the catalog */ + +CatalogEntry *InitCatalog(void); +TrackLibrary *InitLibrary(void); +TrackLibrary *CreateLibrary(char *directory); +void DeleteLibrary(TrackLibrary *tracklib); +bool GetTrackFiles(TrackLibrary *trackLib, char *directory); +int GetParameterFileInfo(int files, char ** fileName, void * data); +unsigned CreateLibraryIndex(TrackLibrary *trackLib); +unsigned SearchLibrary(TrackLibrary *library, char *searchExpression, CatalogEntry *resultEntries); +unsigned CountCatalogEntries(CatalogEntry *listHeader); +void EmptyCatalog(CatalogEntry *listHeader); +unsigned SearchLibrary(TrackLibrary *library, char *searchExpression, CatalogEntry *resultEntries); +bool FilterKeyword(char *word); +#endif // !HAVE_TRACKCATALOG_H diff --git a/app/bin/include/stringxtc.h b/app/bin/include/stringxtc.h new file mode 100644 index 0000000..cbadd0e --- /dev/null +++ b/app/bin/include/stringxtc.h @@ -0,0 +1,8 @@ +#include <stddef.h>
+
+#ifndef HAVE_STRINGXTC_H
+ #define HAVE_STRINGXTC_H
+ size_t strscpy(char *dest, const char *src, size_t count);
+ char *XtcStrlwr(char *str);
+ int XtcStricmp(const char *a, const char *b);
+#endif
diff --git a/app/bin/include/utf8convert.h b/app/bin/include/utf8convert.h new file mode 100644 index 0000000..6a3e678 --- /dev/null +++ b/app/bin/include/utf8convert.h @@ -0,0 +1,24 @@ +/* XTrackCad - Model Railroad CAD + * Copyright (C) 2020 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. + */ + +#ifndef HAVE_UTF8CONVERT_H +#include <stdbool.h> +char *Convert2UTF8(char *string); +bool RequiresConvToUTF8(char *string); +void ConvertUTF8ToSystem(unsigned char *in); +#endif // HAVE_UTF8CONVERT_H diff --git a/app/bin/layout.c b/app/bin/layout.c index 584e62b..a77cbb2 100644 --- a/app/bin/layout.c +++ b/app/bin/layout.c @@ -31,6 +31,8 @@ #include "paths.h" #include "track.h" #include "wlib.h" +#include "fileio.h" +#include "utility.h" #define MINTRACKRADIUSPREFS "minTrackRadius" @@ -43,6 +45,11 @@ struct sLayoutProps { DIST_T minTrackRadius; DIST_T maxTrackGrade; coOrd roomSize; + DynString backgroundFileName; + coOrd backgroundPos; + ANGLE_T backgroundAngle; + int backgroundScreen; + double backgroundSize; }; struct sDataLayout { @@ -51,8 +58,8 @@ struct sDataLayout { struct sLayoutProps *copyOfLayoutProps; }; -struct sDataLayout thisLayout = { - { "", "", -1, 0, 0, 0.0, 5.0, {0.0, 0.0} }, +static struct sDataLayout thisLayout = { + { "", "", -1, 0, 0, 0.0, 5.0, {0.0, 0.0}, NaS, {0.0, 0.0}, 0.0, 0, 0.0 }, NaS, NULL, }; @@ -60,6 +67,9 @@ struct sDataLayout thisLayout = { static paramFloatRange_t r0_90 = { 0, 90 }; static paramFloatRange_t r1_10000 = { 1, 10000 }; static paramFloatRange_t r1_9999999 = { 1, 9999999 }; +static paramFloatRange_t r360_360 = { -360, 360 }; +static paramFloatRange_t rN_9999999 = { -99999, 99999 }; +static paramIntegerRange_t i0_100 = { 0, 100 }; static void LayoutDlgUpdate(paramGroup_p pg, int inx, void * valueP); @@ -72,14 +82,18 @@ static void LayoutDlgUpdate(paramGroup_p pg, int inx, void * valueP); void SetLayoutFullPath(const char *fileName) { - if (DynStringToCStr(&thisLayout.fullFileName) != fileName) { - if (isnas(&thisLayout.fullFileName)) { - DynStringMalloc(&thisLayout.fullFileName, strlen(fileName) + 1); - } else { - DynStringClear(&thisLayout.fullFileName); - } - - DynStringCatCStr(&thisLayout.fullFileName, fileName); + if (fileName && fileName[0]) { + if (DynStringSize(&thisLayout.fullFileName)) { + if (strcmp(DynStringToCStr(&thisLayout.fullFileName),fileName)==0) { + return; + } + DynStringClear(&thisLayout.fullFileName); + } + DynStringMalloc(&thisLayout.fullFileName, strlen(fileName) + 1); + DynStringCatCStr(&thisLayout.fullFileName, fileName); + } else { + DynStringMalloc(&thisLayout.fullFileName, 2); + DynStringCatCStr(&thisLayout.fullFileName, ""); } } @@ -157,6 +171,41 @@ SetLayoutCurGauge(GAUGEINX_T gauge) thisLayout.props.curGaugeInx = gauge; } +void SetLayoutBackGroundFullPath(const char *fileName) { + if (fileName && fileName[0]) { + if (DynStringSize(&thisLayout.props.backgroundFileName)) { + if (strcmp(DynStringToCStr(&thisLayout.props.backgroundFileName),fileName)==0) { + return; + } + DynStringClear(&thisLayout.props.backgroundFileName); + } + DynStringMalloc(&thisLayout.props.backgroundFileName, strlen(fileName) + 1); + DynStringCatCStr(&thisLayout.props.backgroundFileName, fileName); + } else { + DynStringClear(&thisLayout.props.backgroundFileName); + DynStringCatCStr(&thisLayout.props.backgroundFileName, ""); + } +} + +void SetLayoutBackGroundSize(double size) { + thisLayout.props.backgroundSize = size; +} + +void SetLayoutBackGroundPos(coOrd pos) { + thisLayout.props.backgroundPos = pos; + +} + +void SetLayoutBackGroundAngle(ANGLE_T angle) { + thisLayout.props.backgroundAngle = angle; + +} + +void SetLayoutBackGroundScreen(int screen) { + thisLayout.props.backgroundScreen = screen; + +} + /** * Return the full filename. * @@ -166,7 +215,8 @@ SetLayoutCurGauge(GAUGEINX_T gauge) char * GetLayoutFullPath() { - return (DynStringToCStr(&thisLayout.fullFileName)); + char * s = DynStringToCStr(&thisLayout.fullFileName); + return s; } /** @@ -181,7 +231,7 @@ GetLayoutFilename() char *string = DynStringToCStr(&thisLayout.fullFileName); if (string) { - return (FindFilename(string)); + return FindFilename(string); } else { return (NULL); } @@ -222,14 +272,188 @@ GetLayoutCurScale() { return (thisLayout.props.curScaleInx); } + +char * +GetLayoutBackGroundFullPath() +{ + char * s = DynStringToCStr(&thisLayout.props.backgroundFileName); + return s; +} + +double +GetLayoutBackGroundSize() +{ + if (thisLayout.props.backgroundSize > 0.0) { + return (thisLayout.props.backgroundSize); + } else { + return (thisLayout.props.roomSize.x); + } +} + +coOrd +GetLayoutBackGroundPos() +{ + return (thisLayout.props.backgroundPos); +} + +ANGLE_T +GetLayoutBackGroundAngle() +{ + return (thisLayout.props.backgroundAngle); +} + +int GetLayoutBackGroundScreen() +{ + return (thisLayout.props.backgroundScreen); +} + /**************************************************************************** * * Layout Dialog * */ +static char backgroundFileName[STR_LONG_SIZE]; +#define TEXT_FIELD_LEN 40 static wWin_p layoutW; +/************************************************************************************** +* Show only the end of the background file path including the filename in the Dialog +*/ +void SetName() { + char * name = GetLayoutBackGroundFullPath(); + if (name && name[0]) { //Ignore "" + if (name && (strlen(name)<=TEXT_FIELD_LEN)) { + for (unsigned int i=0; i<=strlen(name);i++) { + backgroundFileName[i] = name[i]; + } + backgroundFileName[strlen(name)] = '\0'; + } else { + for (int i=TEXT_FIELD_LEN;i>=0; i--) { + backgroundFileName[i] = name[strlen(name)-(TEXT_FIELD_LEN-i)]; + } + backgroundFileName[TEXT_FIELD_LEN] = '\0'; //Insurance + } + } else backgroundFileName[0] = '\0'; +} + +static struct wFilSel_t * imageFile_fs; + +static paramData_p layout_p; +static paramGroup_t * layout_pg_p; +static wBool_t file_changed; + +EXPORT BOOL_T haveBackground = FALSE; +BOOL_T backgroundVisible = TRUE; + +char * noname = ""; + +void +BackgroundToggleShow() +{ + backgroundVisible = !backgroundVisible; + wButtonSetBusy(backgroundB, backgroundVisible); + MainRedraw(); +} + +int GetLayoutBackGroundVisible() +{ + return(backgroundVisible); +} + +/***************************************** +* Try to load the background image file +*/ +wBool_t +LoadBackGroundImage(void) +{ + char * error; + char * background = GetLayoutBackGroundFullPath(); + if (wDrawSetBackground( mainD.d, background, &error)==-1) { + NoticeMessage(_("Unable to load Image File - %s"),_("Ok"),NULL,error); + return FALSE; + } + return TRUE; +} + +/******************************************************* +* Callback from File Select for Background Image File +* +* \param files number of files selected (only first file is used) +* \param fileName array of pointers to filenames +* \param data unused +* \return FALSE +*/ +EXPORT int LoadImageFile( + int files, + char ** fileName, + void * data ) +{ + if (files >0) { + SetLayoutBackGroundFullPath( strdup(fileName[0])); + + if (!LoadBackGroundImage()) { + SetLayoutBackGroundFullPath(noname); + backgroundVisible = FALSE; + } + else { + backgroundVisible = TRUE; + SetCurrentPath(BACKGROUNDPATHKEY, fileName[0]); + } + } else { + SetLayoutBackGroundFullPath(noname); + backgroundVisible = FALSE; + } + wControlActive((wControl_p)backgroundB, backgroundVisible); + wButtonSetBusy(backgroundB, backgroundVisible); + + SetName(); + file_changed = TRUE; + ParamLoadControl(layout_pg_p, 8); + return FALSE; +} + +/********************************************************** + * Save the Background Parms - forcing a write + */ +void LayoutBackGroundSave(void) { + char * background = GetLayoutBackGroundFullPath(); + wPrefSetString("layout", "BackgroundPath", background); + wPrefSetFloat("layout", "BackgroundPosX", thisLayout.props.backgroundPos.x); + wPrefSetFloat("layout", "BackgroundPosY", thisLayout.props.backgroundPos.y); + wPrefSetFloat("layout", "BackgroundAngle", thisLayout.props.backgroundAngle); + wPrefSetInteger("layout", "BackgroundScreen", thisLayout.props.backgroundScreen); + wPrefSetFloat("layout", "BackgroundSize", thisLayout.props.backgroundSize); + + wPrefFlush(); +} + +/************************************************************ + * Run File Select for the Background Image File + */ +static void ImageFileBrowse( void * junk ) +{ + imageFile_fs = wFilSelCreate( mainW, FS_LOAD, FS_PICTURES, _("Load Background"), sImageFilePattern, LoadImageFile, NULL ); + + wFilSelect( imageFile_fs, GetCurrentPath( BACKGROUNDPATHKEY ) ); + return; +} + +/************************************************************ + * Remove the background Image File + */ +static void ImageFileClear( void * junk) +{ + char * noname = ""; + SetLayoutBackGroundFullPath(noname); + wDrawSetBackground( mainD.d, NULL, NULL); + SetName(); + wControlActive((wControl_p)backgroundB, FALSE); + file_changed = TRUE; + ParamLoadControl(layout_pg_p, 8); + MainRedraw(); +} + static paramData_t layoutPLs[] = { { PD_FLOAT, &thisLayout.props.roomSize.x, "roomsizeX", PDO_NOPREF | PDO_DIM | PDO_NOPSHUPD | PDO_DRAW, &r1_9999999, N_("Room Width"), 0, (void*)(CHANGE_MAIN | CHANGE_MAP) }, { PD_FLOAT, &thisLayout.props.roomSize.y, "roomsizeY", PDO_NOPREF | PDO_DIM | PDO_NOPSHUPD | PDO_DRAW | PDO_DLGHORZ, &r1_9999999, N_(" Height"), 0, (void*)(CHANGE_MAIN | CHANGE_MAP) }, @@ -241,7 +465,22 @@ static paramData_t layoutPLs[] = { { PD_DROPLIST, &thisLayout.props.curGaugeInx, "gauge", PDO_NOPREF | PDO_NOPSHUPD | PDO_NORECORD | PDO_NOUPDACT | PDO_DLGHORZ, (void *)120, N_(" Gauge"), 0, (void *)(CHANGE_SCALE) }, #define MINRADIUSENTRY (6) { PD_FLOAT, &thisLayout.props.minTrackRadius, "mintrackradius", PDO_DIM | PDO_NOPSHUPD | PDO_NOPREF, &r1_10000, N_("Min Track Radius"), 0, (void*)(CHANGE_MAIN | CHANGE_LIMITS) }, - { PD_FLOAT, &thisLayout.props.maxTrackGrade, "maxtrackgrade", PDO_NOPSHUPD | PDO_DLGHORZ, &r0_90, N_(" Max Track Grade (%)"), 0, (void*)(CHANGE_MAIN) } + { PD_FLOAT, &thisLayout.props.maxTrackGrade, "maxtrackgrade", PDO_NOPSHUPD | PDO_DLGHORZ, &r0_90, N_(" Max Track Grade (%)"), 0, (void*)(CHANGE_MAIN) }, +#define BACKGROUNDFILEENTRY (8) //Note this value used in the file section routines above - if it chnages, they will need to change + { PD_STRING, &backgroundFileName, "backgroundfile", PDO_NOPSHUPD, NULL, N_("Background File Path"), 0, (void *)(CHANGE_BACKGROUND) }, + { PD_BUTTON, (void*)ImageFileBrowse, "browse", PDO_DLGHORZ, NULL, N_("Browse ...") }, + { PD_BUTTON, (void*)ImageFileClear, "clear", PDO_DLGHORZ, NULL, N_("Clear") }, +#define BACKGROUNDPOSX (11) + { PD_FLOAT, &thisLayout.props.backgroundPos.x, "backgroundposX", PDO_DIM | PDO_NOPSHUPD | PDO_DRAW, &rN_9999999, N_("Background PosX,Y"), 0, (void*)(CHANGE_BACKGROUND) }, +#define BACKGROUNDPOSY (12) + { PD_FLOAT, &thisLayout.props.backgroundPos.y, "backgroundposY", PDO_DIM | PDO_NOPSHUPD | PDO_DRAW | PDO_DLGHORZ, &rN_9999999, NULL, 0, (void*)(CHANGE_BACKGROUND) }, +#define BACKGROUNDWIDTH (13) + { PD_FLOAT, &thisLayout.props.backgroundSize, "backgroundWidth", PDO_DIM | PDO_NOPSHUPD | PDO_DRAW, &r1_9999999, N_("Background Size"), 0, (void*)(CHANGE_BACKGROUND) }, +#define BACKGROUNDSCREEN (14) + { PD_LONG, &thisLayout.props.backgroundScreen, "backgroundScreen", PDO_NOPSHUPD | PDO_DRAW, &i0_100, N_("Background Screen %"), 0, (void*)(CHANGE_BACKGROUND) }, +#define BACKGROUNDANGLE (15) + { PD_FLOAT, &thisLayout.props.backgroundAngle, "backgroundAngle", PDO_NOPSHUPD | PDO_DRAW, &r360_360, N_("Background Angle"), 0, (void*)(CHANGE_BACKGROUND) } + }; static paramGroup_t layoutPG = { "layout", PGO_RECORD | PGO_PREFMISC, layoutPLs, sizeof layoutPLs / sizeof layoutPLs[0] }; @@ -278,10 +517,20 @@ static void LayoutOk(void * junk) wPrefSetFloat("misc", prefString, thisLayout.props.minTrackRadius); } + if ((changes & CHANGE_BACKGROUND) || file_changed) { + + LayoutBackGroundSave(); + file_changed = FALSE; + } + free(thisLayout.copyOfLayoutProps); wHide(layoutW); + + MainLayout( TRUE, TRUE ); } + + /** * Discard the changes entered and replace with earlier values * @@ -297,7 +546,7 @@ static void LayoutCancel(struct wWin_t *junk) static void LayoutChange(long changes) { - if (changes & (CHANGE_SCALE | CHANGE_UNITS)) + if (changes & (CHANGE_SCALE | CHANGE_UNITS | CHANGE_BACKGROUND)) if (layoutW != NULL && wWinIsVisible(layoutW)) { ParamLoadControls(&layoutPG); } @@ -305,7 +554,7 @@ static void LayoutChange(long changes) void DoLayout(void * junk) { - thisLayout.props.roomSize = mapD.size; + SetLayoutRoomSize(mapD.size); if (layoutW == NULL) { layoutW = ParamCreateDialog(&layoutPG, MakeWindowTitle(_("Layout Options")), @@ -313,6 +562,8 @@ void DoLayout(void * junk) LoadScaleList((wList_p)layoutPLs[4].control); } + ParamControlActive(&layoutPG, BACKGROUNDFILEENTRY, FALSE); + LoadGaugeList((wList_p)layoutPLs[5].control, thisLayout.props.curScaleDescInx); /* set correct gauge list here */ thisLayout.copyOfLayoutProps = malloc(sizeof(struct sLayoutProps)); @@ -320,7 +571,7 @@ void DoLayout(void * junk) if (!thisLayout.copyOfLayoutProps) { exit(1); } - + SetName(); *(thisLayout.copyOfLayoutProps) = thisLayout.props; ParamLoadControls(&layoutPG); @@ -331,6 +582,8 @@ EXPORT addButtonCallBack_t LayoutInit(void) { ParamRegister(&layoutPG); RegisterChangeNotification(LayoutChange); + layout_p = layoutPLs; + layout_pg_p = &layoutPG; return &DoLayout; } @@ -372,4 +625,83 @@ LayoutDlgUpdate( wStringSetValue((wString_p)layoutPLs[MINRADIUSENTRY].control, FormatDistance(thisLayout.props.minTrackRadius)); } + if (inx == BACKGROUNDPOSX) { + coOrd pos; + pos.x = *(double *)valueP; + pos.y = GetLayoutBackGroundPos().y; + SetLayoutBackGroundPos(pos); + MainRedraw(); + } + if (inx == BACKGROUNDPOSY) { + coOrd pos; + pos.y = *(double *)valueP; + pos.x = GetLayoutBackGroundPos().x; + SetLayoutBackGroundPos(pos); + MainRedraw(); + } + if (inx == BACKGROUNDWIDTH) { + SetLayoutBackGroundSize(*(double *)valueP); + MainRedraw(); + } + if (inx == BACKGROUNDSCREEN) { + SetLayoutBackGroundScreen(*(int *)valueP); + MainRedraw(); + } + if (inx == BACKGROUNDANGLE) { + + ANGLE_T angle = NormalizeAngle(*(double *)valueP); + wStringSetValue((wString_p)layoutPLs[BACKGROUNDANGLE].control,FormatFloat(angle)); + SetLayoutBackGroundAngle(angle); + MainRedraw(); + } + +} +/*************************************************************************************** + * Load Background Options from Saved Parms + ***************************************************************************************/ +void +LayoutBackGroundLoad(void) { + SetLayoutBackGroundFullPath(wPrefGetString("layout", "BackgroundPath")); + + wPrefGetFloat("layout", "BackgroundPosX", &thisLayout.props.backgroundPos.x, 0.0); + wPrefGetFloat("layout", "BackgroundPosY", &thisLayout.props.backgroundPos.y, 0.0); + wPrefGetFloat("layout", "BackgroundAngle", &thisLayout.props.backgroundAngle, 0.0); + long screen_long; + wPrefGetInteger("layout", "BackgroundScreen", &screen_long, 0L); + thisLayout.props.backgroundScreen = screen_long; + wPrefGetFloat("layout", "BackgroundSize", &thisLayout.props.backgroundSize, 0.0); +} + +static wBool_t inited; + +/************************************************************************************** + * Either Clear Background Parms or (if the first time called) Load from Saved Parms + **************************************************************************************/ +void +LayoutBackGroundInit(BOOL_T clear) { + if (clear) { + SetLayoutBackGroundFullPath(noname); + SetLayoutBackGroundPos(zero); + SetLayoutBackGroundAngle(0.0); + SetLayoutBackGroundScreen(0); + SetLayoutBackGroundSize(0.0); + LayoutBackGroundSave(); + } else { //First Time and not "Clear" + inited = TRUE; + LayoutBackGroundLoad(); + } + char * str = GetLayoutBackGroundFullPath(); + if (str && str[0]) { + if (!LoadBackGroundImage()) { //Failed -> Wipe Out + SetLayoutBackGroundFullPath(noname); + SetLayoutBackGroundPos(zero); + SetLayoutBackGroundAngle(0.0); + SetLayoutBackGroundScreen(0); + SetLayoutBackGroundSize(0.0); + LayoutBackGroundSave(); + } + } else { + wDrawSetBackground( mainD.d, NULL, NULL); + } + } diff --git a/app/bin/layout.h b/app/bin/layout.h index 4a581df..fcb5160 100644 --- a/app/bin/layout.h +++ b/app/bin/layout.h @@ -38,6 +38,11 @@ void SetLayoutCurScale(SCALEINX_T scale); void SetLayoutCurScaleDesc(SCALEDESCINX_T desc); void SetLayoutCurGauge(GAUGEINX_T gauge); void SetLayoutScaleGauge(SCALEDESCINX_T desc, GAUGEINX_T gauge); +void SetLayoutBackGroundFullPath(const char *fileName); +void SetLayoutBackGroundSize(double size); +void SetLayoutBackGroundPos(coOrd pos); +void SetLayoutBackGroundAngle(ANGLE_T angle); +void SetLayoutBackGroundScreen(int screen); char *GetLayoutFullPath(void); char *GetLayoutFilename(void); @@ -48,9 +53,18 @@ SCALEINX_T GetLayoutCurScale(void ); SCALEDESCINX_T GetLayoutCurScaleDesc(void); //GAUGEINX_T GetLayoutCurGauge(void); - ANGLE_T GetLayoutMaxTrackGrade(void); SCALEDESCINX_T GetLayoutCurScaleDesc(void); - +char *GetLayoutBackGroundFullPath(void); +double GetLayoutBackGroundSize(void); +coOrd GetLayoutBackGroundPos(void); +ANGLE_T GetLayoutBackGroundAngle(void); +int GetLayoutBackGroundScreen(void); +int GetLayoutBackGroundVisible(void); +void LayoutBackGroundInit(BOOL_T clear); +void LayoutBackGroundLoad(void); +void LayoutBackGroundSave(void); +void BackgroundToggleShow(void); void DoLayout(void * junk); -#endif
\ No newline at end of file +int LoadImageFile(int files,char ** fileName,void * data ); +#endif diff --git a/app/bin/linknoteui.c b/app/bin/linknoteui.c new file mode 100644 index 0000000..daa3ccf --- /dev/null +++ b/app/bin/linknoteui.c @@ -0,0 +1,260 @@ +/** \file linknoteui.c + * View for the text note + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2018 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 <string.h> +#include <stdbool.h> + +#include "custom.h" +#include "dynstring.h" +#include "i18n.h" +#include "misc.h" +#include "note.h" +#include "param.h" +#include "include/stringxtc.h" +#include "track.h" +#include "validator.h" +#include "wlib.h" + +extern BOOL_T inDescribeCmd; + +#define DEFAULTLINKURL "http://www.xtrkcad.org/" +#define DEFAULTLINKTITLE "The XTrackCAD Homepage" + +static struct extraDataNote noteDataInUI; + +static void NoteLinkBrowse(void *junk); +static void NoteLinkOpen(char *url ); + +static paramFloatRange_t r_1000_1000 = { -1000.0, 1000.0, 80 }; +static paramData_t linkEditPLs[] = { +#define I_ORIGX (0) + /*0*/ { PD_FLOAT, ¬eDataInUI.pos.x, "origx", PDO_DIM, &r_1000_1000, N_("Position X") }, +#define I_ORIGY (1) + /*1*/ { PD_FLOAT, ¬eDataInUI.pos.y, "origy", PDO_DIM, &r_1000_1000, N_("Position Y") }, +#define I_LAYER (2) + /*2*/ { PD_DROPLIST, ¬eDataInUI.layer, "layer", 0, (void*)150, "Layer", 0 }, +#define I_TITLE (3) + /*3*/ { PD_STRING, NULL, "title", PDO_NOPREF | PDO_STRINGLIMITLENGTH, (void*)200, N_("Title"), 0, 0, TITLEMAXIMUMLENGTH-1 }, +#define I_URL (4) + /*4*/ { PD_STRING, NULL, "name", PDO_NOPREF | PDO_STRINGLIMITLENGTH, (void*)200, N_("URL"), 0, 0, URLMAXIMUMLENGTH-1 }, +#define I_OPEN (5) + /*5*/{ PD_BUTTON, (void*)NoteLinkBrowse, "openlink", PDO_DLGHORZ, NULL, N_("Open...") }, +}; + +static paramGroup_t linkEditPG = { "linkEdit", 0, linkEditPLs, sizeof linkEditPLs / sizeof linkEditPLs[0] }; +static wWin_p linkEditW; + +BOOL_T +IsLinkNote(track_p trk) +{ + struct extraDataNote * xx = (struct extraDataNote *)GetTrkExtraData(trk); + + return(xx->op == OP_NOTELINK); +} + + +/** + * Callback for Open URL button + * + * \param junk IN ignored + */ +static void NoteLinkBrowse(void *junk) +{ + NoteLinkOpen(noteDataInUI.noteData.linkData.url); +} + +/** + * Open the URL in the external default browser + * + * \param url IN url to open + */ +static void NoteLinkOpen(char *url) +{ + wOpenFileExternal(url); +} + +static void +LinkDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP) +{ + switch (inx) { + case I_URL: + if (strlen(noteDataInUI.noteData.linkData.url) > URLMAXIMUMLENGTH) { + DynString message; + + DynStringMalloc(&message, 80); + DynStringPrintf(&message, _("The entered URL is too long. The maximum allowed length is %d. Please edit the entered value."), URLMAXIMUMLENGTH); + wNoticeEx(NT_ERROR, + DynStringToCStr(&message), + _("Re-edit"), + NULL); + DynStringFree(&message); + } + + if (IsValidURL(noteDataInUI.noteData.linkData.url) && + (strlen(noteDataInUI.noteData.linkData.url) <= URLMAXIMUMLENGTH)) + { + wControlActive(linkEditPLs[I_OPEN].control, TRUE); + ParamDialogOkActive(&linkEditPG, TRUE); + } else { + wControlActive(linkEditPLs[I_OPEN].control, FALSE); + ParamDialogOkActive(&linkEditPG, FALSE); + } + break; + case I_ORIGX: + case I_ORIGY: + UpdateLink(¬eDataInUI, OR_NOTE, FALSE); + break; + case I_LAYER: + UpdateLink(¬eDataInUI, LY_NOTE, FALSE); + break; + default: + break; + } +} + +/** +* Handle Cancel button: restore old values for layer and position +*/ + +static void +LinkEditCancel( wWin_p junk) +{ + if (inDescribeCmd) { + UpdateFile(¬eDataInUI, CANCEL_NOTE, FALSE); + } + ResetIfNotSticky(); + wHide(linkEditW); +} + +/** + * Handle OK button: make sure the entered URL is syntactically valid, update + * the layout and close the dialog + * + * \param junk + */ + +static void +LinkEditOK(void *junk) +{ + UpdateLink(¬eDataInUI, OK_LINK, FALSE); + wHide(linkEditW); + ResetIfNotSticky(); + FileIsChanged(); +} + + +static void +CreateEditLinkDialog(track_p trk, char *title) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + + // create the dialog if necessary + if (!linkEditW) { + noteDataInUI.noteData.linkData.url = MyMalloc(URLMAXIMUMLENGTH); + noteDataInUI.noteData.linkData.title = MyMalloc(TITLEMAXIMUMLENGTH); + linkEditPLs[I_TITLE].valueP = noteDataInUI.noteData.linkData.title; + linkEditPLs[I_URL].valueP = noteDataInUI.noteData.linkData.url; + ParamRegister(&linkEditPG); + linkEditW = ParamCreateDialog(&linkEditPG, + "", + _("Done"), LinkEditOK, + LinkEditCancel, TRUE, NULL, + F_BLOCK, + LinkDlgUpdate); + } + + wWinSetTitle(linkEditPG.win, MakeWindowTitle(title)); + + // initialize the dialog fields + noteDataInUI.pos = xx->pos; + noteDataInUI.layer = xx->layer; + noteDataInUI.trk = trk; + strscpy(noteDataInUI.noteData.linkData.url, xx->noteData.linkData.url,URLMAXIMUMLENGTH ); + strscpy(noteDataInUI.noteData.linkData.title, xx->noteData.linkData.title, TITLEMAXIMUMLENGTH ); + + FillLayerList((wList_p)linkEditPLs[I_LAYER].control); + ParamLoadControls(&linkEditPG); + + // and show the dialog + wShow(linkEditW); +} + +/** + * Activate note if double clicked + * \param trk the note + */ + +void ActivateLinkNote(track_p trk) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + NoteLinkOpen(xx->noteData.linkData.url); +} + + +/** + * Describe and enable editing of an existing link note + * + * \param trk the existing, valid note + * \param str the field to put a text version of the note so it will appear on the status line + * \param len the lenght of the field + */ + +void DescribeLinkNote(track_p trk, char * str, CSIZE_T len) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + DynString statusLine; + + DynStringMalloc(&statusLine, 80); + DynStringPrintf(&statusLine, + "Link: Layer=%d %-.80s (%s)", + GetTrkLayer(trk)+1, + xx->noteData.linkData.title, + xx->noteData.linkData.url); + strcpy(str, DynStringToCStr(&statusLine)); + DynStringFree(&statusLine); + + if (inDescribeCmd) { + NoteStateSave(trk); + + CreateEditLinkDialog(trk, _("Update link")); + } +} + +/** + * Take a new note track element and initialize it. It will be + * initialized with defaults and can then be edited by the user. + * + * \param the newly created trk + */ + +void NewLinkNoteUI(track_p trk) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + + xx->noteData.linkData.url = MyStrdup( DEFAULTLINKURL ); + xx->noteData.linkData.title = MyStrdup( DEFAULTLINKTITLE ); + + CreateEditLinkDialog(trk, _("Create link")); +} diff --git a/app/bin/macro.c b/app/bin/macro.c index 1c711ce..8db996d 100644 --- a/app/bin/macro.c +++ b/app/bin/macro.c @@ -1,4 +1,5 @@ /** \file macro.c + * Macros */ @@ -60,6 +61,7 @@ #include "param.h" #include "paths.h" #include "track.h" +#include "trackx.h" #include "utility.h" #include "version.h" @@ -67,6 +69,9 @@ EXPORT long adjTimer; static void DemoInitValues( void ); extern char *userLocale; +static int log_playbackCursor = 0; + + /***************************************************************************** * @@ -185,7 +190,7 @@ static int StartRecord( int cnt, char ** pathName, void * context ) if ( logTable_da.cnt > 11 ) lprintf( "StartRecord( %s ) @ %s\n", pathName, ctime(&clock) ); ParamStartRecord(); - WriteTracks( recordF ); + WriteTracks( recordF, TRUE ); WriteLayers( recordF ); fprintf( recordF, "REDRAW\n" ); fflush( recordF ); @@ -207,7 +212,7 @@ static void DoRecordButton( void * 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" ); + fprintf( recordF, "%s\nSTEP\n", END_MESSAGE ); fclose( recordF ); recordF = NULL; wHide( recordW ); @@ -226,7 +231,7 @@ static void DoRecordButton( void * context ) wTextGetText( recordT, cp, len ); if ( cp[len-1] == '\n' ) len--; cp[len] = '\0'; - fprintf( recordF, "%s\nEND\nSTEP\n", cp ); + fprintf( recordF, "%s\n%s\nSTEP\n", cp, END_MESSAGE ); MyFree( cp ); recordingMessage = FALSE; } @@ -284,59 +289,101 @@ EXPORT void DoRecord( void * context ) * */ +static drawCmd_p playbackD = NULL; static wDrawBitMap_p playbackBm = NULL; static wDrawColor playbackColor; -static drawCmd_p playbackD; static wPos_t playbackX, playbackY; +static wBool_t bDoFlash = FALSE; +static wDrawColor flashColor; #include "bitmaps/arrow0.xbm" +#include "bitmaps/arrow0_shift.xbm" +#include "bitmaps/arrow0_ctl.xbm" #include "bitmaps/arrow3.xbm" +#include "bitmaps/arrow3_shift.xbm" +#include "bitmaps/arrow3_ctl.xbm" #include "bitmaps/arrows.xbm" +#include "bitmaps/arrowr3.xbm" +#include "bitmaps/arrowr3_shift.xbm" +#include "bitmaps/arrowr3_ctl.xbm" #include "bitmaps/flash.xbm" static wDrawColor rightDragColor; static wDrawColor leftDragColor; static wDrawBitMap_p arrow0_bm; +static wDrawBitMap_p arrow0_shift_bm; +static wDrawBitMap_p arrow0_ctl_bm; static wDrawBitMap_p arrow3_bm; +static wDrawBitMap_p arrow3_shift_bm; +static wDrawBitMap_p arrow3_ctl_bm; static wDrawBitMap_p arrows_bm; +static wDrawBitMap_p arrowr3_bm; +static wDrawBitMap_p arrowr3_shift_bm; +static wDrawBitMap_p arrowr3_ctl_bm; static wDrawBitMap_p flash_bm; -static long flashTO = 60; -static DIST_T PixelsPerStep = 20; +static long flashTO = 120; +static DIST_T PixelsPerStep = 5; static long stepTO = 100; EXPORT unsigned long playbackTimer; static wBool_t didPause; static wBool_t flashTwice = FALSE; +int DBMCount=0; #define DRAWALL +typedef enum { FLASH_PLUS, FLASH_MINUS, REDRAW, CLEAR, DRAW, RESET, ORIG, MOVE_PLYBCK1, MOVE_PLYBCK2, MOVE_PLYBCK3, MOVE_PLYBCK4, QUIT } DrawBitMap_e; + +char * DrawBitMapToString(DrawBitMap_e dbm) { + switch(dbm) { + case FLASH_PLUS: + return "Flsh+"; + case FLASH_MINUS: + return "Flsh-"; + case REDRAW: + return "Redraw"; + case CLEAR: + return "Clr"; + case DRAW: + return "Draw"; + case RESET: + return "RESET"; + case ORIG: + return "ORIG"; + case MOVE_PLYBCK1: + return "MPBC1"; + case MOVE_PLYBCK2: + return "MPBC2"; + case MOVE_PLYBCK3: + return "MPBC3"; + case MOVE_PLYBCK4: + return "MPBC4"; + case QUIT: + return "Quit"; + default: + return ""; + } +} + static void MacroDrawBitMap( - drawCmd_p d, + DrawBitMap_e dbm, wDrawBitMap_p bm, wPos_t x, wPos_t y, wDrawColor color ) { - wDrawBitMap( d->d, bm, x, y, color, wDrawOptTemp|wDrawOptNoClip ); + wDrawBitMap( playbackD->d, bm, x, y, color, wDrawOptTemp|wDrawOptNoClip ); wFlush(); + + LOG( log_playbackCursor, 1, ("%s %d DrawBitMap( %p %p %d %d %d %d )\n", DrawBitMapToString(dbm), DBMCount++, playbackD->d, bm, x, y, color, wDrawOptTemp|wDrawOptNoClip ) ); } -static void Flash( drawCmd_p d, wPos_t x, wPos_t y, wDrawColor flashColor ) +static void Flash( drawCmd_p d, wPos_t x, wPos_t y, wDrawColor color ) { - 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 + bDoFlash = TRUE; + flashColor = color; } @@ -358,13 +405,26 @@ static void SetPlaybackSpeed( playbackSpeed = inx; } -static void ClearPlaybackCursor( void ) -{ - if (playbackBm != NULL) - MacroDrawBitMap( playbackD, playbackBm, playbackX, playbackY, playbackColor ); - playbackBm = NULL; -} +EXPORT void RedrawPlaybackCursor() { + if ( playbackD && playbackBm && inPlayback) { + wBool_t ret; + if ( playbackD->d != mainD.d ) + ret = wDrawSetTempMode( playbackD->d, TRUE ); + if ( bDoFlash && playbackTimer == 0 ) { + MacroDrawBitMap( FLASH_PLUS, flash_bm, playbackX, playbackY, flashColor ); + wPause( flashTO*2 ); + if ( flashTwice ) { + MacroDrawBitMap( FLASH_PLUS, flash_bm, playbackX, playbackY, flashColor ); + wPause( flashTO*2 ); + } + bDoFlash = FALSE; + } + MacroDrawBitMap( DRAW, playbackBm, playbackX, playbackY, playbackColor ); + if ( playbackD->d != mainD.d ) + wDrawSetTempMode( playbackD->d, ret ); + } +} static void MoveCursor( drawCmd_p d, @@ -378,45 +438,56 @@ static void MoveCursor( coOrd pos1, dpos; int i, steps; wPos_t x, y; - wPos_t xx, yy; - - ClearPlaybackCursor(); + wPos_t x0=playbackX; + wPos_t y0=playbackY; 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); + if (playbackTimer == 0 /*&& !didPause*/) { + playbackBm = bm; + playbackColor = color; + dx = (DIST_T)(x-x0); + dy = (DIST_T)(y-y0); dist = sqrt( dx*dx + dy*dy ); steps = (int)(dist / PixelsPerStep ) + 1; dx /= steps; dy /= steps; - d->Pix2CoOrd( d, playbackX, playbackY, &pos1 ); + d->Pix2CoOrd( d, x0, y0, &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 ); + + playbackX = x0+(wPos_t)(i*dx); + playbackY = y0+(wPos_t)(i*dy); + pos1.x += dpos.x; pos1.y += dpos.y; - if (proc) + if ( proc != NULL ) { proc( action, pos1 ); - else if (d->d == mainD.d) { + } else { + TempRedraw(); + } +// DrawPlaybackCursor( d, bm, xx, yy, color ); + if ( d->d == mainD.d ) { InfoPos( pos1 ); + wFlush(); } - wPause( stepTO*playbackDelay/100 ); - MacroDrawBitMap( d, bm, xx, yy, color ); + // Simple mouse moves happen twice as fast + wPause( stepTO*playbackDelay/100/(action==wActionMove?2:1) ); + + if (!inPlayback) { return; } } + } else { + playbackX = x; + playbackY = y; } - MacroDrawBitMap( playbackD=d, playbackBm=bm, playbackX=x, playbackY=y, playbackColor=color ); } @@ -427,76 +498,86 @@ static void PlaybackCursor( coOrd pos, wDrawColor color ) { - wDrawBitMap_p bm; + wDrawBitMap_p bm = playbackBm; + playbackD = d; wPos_t x, y; long time0, time1; time0 = wGetTimer(); - ClearPlaybackCursor(); d->CoOrd2Pix( d, pos, &x, &y ); - switch( action ) { + + + switch( action&0xFF ) { case wActionMove: - MacroDrawBitMap( playbackD=d, playbackBm=arrow0_bm, playbackX=x, playbackY=y, playbackColor=wDrawColorBlack ); + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrow0_shift_bm:(MyGetKeyState()&WKEY_CTRL)?arrow0_ctl_bm:arrow0_bm); //0 is normal, shift, ctrl + MoveCursor( d, proc, wActionMove, pos, bm, 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; + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrow0_shift_bm:(MyGetKeyState()&WKEY_CTRL)?arrow0_ctl_bm:arrow0_bm); + MoveCursor( d, proc, wActionMove, pos, bm, wDrawColorBlack ); //Go to spot + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrow3_shift_bm:(MyGetKeyState()&WKEY_CTRL)?arrow3_ctl_bm:arrow3_bm); + Flash( d, x, y, playbackColor=rightDragColor ); + proc( action, pos ); + /* no break */ case C_MOVE: - bm = ((MyGetKeyState()&WKEY_SHIFT)?arrows_bm:arrow3_bm); + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrow3_shift_bm:(MyGetKeyState()&WKEY_CTRL)?arrow3_ctl_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); + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrow3_shift_bm:(MyGetKeyState()&WKEY_CTRL)?arrow3_ctl_bm:arrow0_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 ); + proc( action, pos ); + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrow0_shift_bm:(MyGetKeyState()&WKEY_CTRL)?arrow0_ctl_bm:arrow0_bm); + MoveCursor( d, NULL, 0, pos, bm, wDrawColorBlack ); 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; + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrow0_shift_bm:(MyGetKeyState()&WKEY_CTRL)?arrow0_ctl_bm:arrow0_bm); + MoveCursor( d, proc, wActionMove, pos, bm, wDrawColorBlack ); //Go to spot + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrowr3_shift_bm:(MyGetKeyState()&WKEY_CTRL)?arrowr3_ctl_bm:arrowr3_bm); + Flash( d, x, y, playbackColor=leftDragColor ); + proc( action, pos ); + /* no break */ case C_RMOVE: - bm = ((MyGetKeyState()&WKEY_SHIFT)?arrows_bm:arrow3_bm); + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrowr3_shift_bm:(MyGetKeyState()&WKEY_CTRL)?arrowr3_ctl_bm:arrowr3_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); + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrowr3_shift_bm:(MyGetKeyState()&WKEY_CTRL)?arrowr3_ctl_bm:arrowr3_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 ); + proc( action, pos ); + bm = ((MyGetKeyState()&WKEY_SHIFT)?arrow0_shift_bm:(MyGetKeyState()&WKEY_CTRL)?arrow0_ctl_bm:arrow0_bm); + MoveCursor( d, NULL, 0, pos, bm, wDrawColorBlack ); break; case C_REDRAW: - MacroDrawBitMap( playbackD, playbackBm, playbackX, playbackY, playbackColor ); + proc( action, pos ); //Send Redraw to functions + playbackD = &tempD; + MacroDrawBitMap( REDRAW, playbackBm, playbackX, playbackY, playbackColor ); + break; + + case C_TEXT: + proc( action, pos); + char c = action>>8; + bm = playbackBm; break; + default: - ; + bm = playbackBm; } + + playbackBm = bm; time1 = wGetTimer(); adjTimer += (time1-time0); } @@ -509,25 +590,7 @@ EXPORT void PlaybackMouse( 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; } @@ -535,17 +598,29 @@ EXPORT void PlaybackMouse( EXPORT void MovePlaybackCursor( drawCmd_p d, wPos_t x, - wPos_t y ) + wPos_t y, wBool_t direct, wControl_p control) { coOrd pos; + playbackD = &tempD; 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 ); + if (!direct) + MoveCursor( d, NULL, wActionMove, pos, arrow0_bm, wDrawColorBlack ); + wBool_t ret = wDrawSetTempMode( d->d, TRUE ); + MacroDrawBitMap( MOVE_PLYBCK1, arrow0_bm, x, y, wDrawColorBlack ); + MacroDrawBitMap( MOVE_PLYBCK2, arrow3_bm, x, y, rightDragColor ); + Flash( d, x, y, rightDragColor ); - MacroDrawBitMap( d, arrow3_bm, x, y, rightDragColor ); - MacroDrawBitMap( d, arrow0_bm, x, y, wDrawColorBlack ); + if (direct) { + wControlHilite(control,TRUE); + } + MacroDrawBitMap( MOVE_PLYBCK3, arrow3_bm, x, y, rightDragColor ); + MacroDrawBitMap( MOVE_PLYBCK4, arrow0_bm, x, y, wDrawColorBlack ); + if (direct) { + wPause(1000); + wControlHilite(control,FALSE); + } + wDrawSetTempMode( d->d, ret ); } /***************************************************************************** @@ -580,12 +655,10 @@ static coOrd oldMainOrig; static coOrd oldMainSize; static DIST_T oldMainScale; static char * oldScaleName; +static int oldMagneticSnap; 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 @@ -639,15 +712,13 @@ static void PlaybackQuit( void ) if (paramFile) fclose( paramFile ); paramFile = NULL; - if (!inPlayback) - return; inPlaybackQuit = TRUE; - ClearPlaybackCursor(); wPrefReset(); wHide( demoW ); wWinSetBusy( mainW, FALSE ); wWinSetBusy( mapW, FALSE ); ParamRestoreAll(); + magneticSnap = oldMagneticSnap; RestoreLayers(); wEnableBalloonHelp( (int)enableBalloonHelp ); mainD.scale = oldMainScale; @@ -657,15 +728,14 @@ static void PlaybackQuit( void ) tempD.orig = mainD.orig; tempD.size = mainD.size; tempD.scale = mainD.scale; + Reset(); ClearTracks(); checkPtMark = changed = 0; RestoreTrackState(); inPlaybackQuit = FALSE; - Reset(); DoSetScale( oldScaleName ); DoChangeNotification( CHANGE_ALL ); CloseDemoWindows(); - inPlayback = FALSE; curDemo = -1; wPrefSetInteger( "misc", "playbackspeed", playbackSpeed ); playbackNonStop = FALSE; @@ -729,6 +799,137 @@ EXPORT void TakeSnapshot( drawCmd_t * d ) } } +/* +* Regression test +*/ +static int log_regression = 0; +wBool_t bWriteEndPtDirectIndex; + +static BOOL_T DoRegression( char * sFileName ) +{ + typedef enum { REGRESSION_NONE, REGRESSION_CHECK, REGRESSION_QUIET, REGRESSION_SAVE } E_REGRESSION; + E_REGRESSION eRegression = REGRESSION_NONE; + long oldParamVersion; + long regressVersion; + FILE * fRegression; + char * sRegressionFile = NULL; + wBool_t bWroteActualTracks; + eRegression = log_regression > 0 ? logTable(log_regression).level : 0; + char * cp; + regressVersion = strtol( paramLine+16, &cp, 10 ); + if (cp == paramLine+16 ) + regressVersion = PARAMVERSION; + LOG( log_regression, 1, ("REGRESSION %s %d %s:%d %s\n", + eRegression==REGRESSION_SAVE?"SAVE":"CHECK", + regressVersion, + sFileName, paramLineNum, + cp ) ); + MakeFullpath( &sRegressionFile, workingDir, "xtrkcad.regress", NULL ); + switch ( eRegression ){ + case REGRESSION_SAVE: + fRegression = fopen( sRegressionFile, "a" ); + if ( fRegression == NULL ) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Regression"), sFileName, strerror(errno) ); + } else { + fprintf( fRegression, "REGRESSION START %d %s\n", + PARAMVERSION, cp ); + fprintf( fRegression, "# %s - %d\n", sFileName, paramLineNum ); + WriteTracks( fRegression, FALSE ); + fprintf( fRegression, "REGRESSION END\n" ); + fclose( fRegression ); + } + while ( fgets(paramLine, STR_LONG_SIZE, paramFile) != NULL ) { + if ( strncmp( paramLine, "REGRESSION END", 14 ) == 0) + break; + } + break; + case REGRESSION_CHECK: + case REGRESSION_QUIET: + oldParamVersion = paramVersion; + paramVersion = regressVersion; + bWroteActualTracks = FALSE; + track_p to_first_save = to_first; + track_p* to_last_save = to_last; + while ( GetNextLine() ) { + if ( paramLine[0] == '#' ) + continue; + // Read Expected track + to_first = NULL; + to_last = &to_first; + paramVersion = regressVersion; + if ( !ReadTrack( paramLine ) ) { + if ( paramFile == NULL ) + return FALSE; + break; + } + if ( to_first == NULL ) { + // Something bad happened + break; + } + track_cp tExpected = to_first; + to_first = to_first_save; + // Find corresponding Actual track + track_cp tActual = FindTrack( GetTrkIndex( tExpected ) ); + strcat( message, "Regression " ); + if ( ! CompareTrack( tActual, tExpected ) ) { + // Actual doesn't match Expected + LOG( log_regression, 1, (" FAIL: %s", message) ); + fRegression = fopen( sRegressionFile, "a" ); + if ( fRegression == NULL ) { + NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Regression"), sRegressionFile, strerror(errno) ); + break; + } + fprintf( fRegression, "REGRESSION FAIL %d\n", + PARAMVERSION ); + fprintf( fRegression, "# %s - %d\n", sFileName, paramLineNum ); + fprintf( fRegression, "# %s", message ); + if ( !bWroteActualTracks ) { + // Print Actual tracks + fprintf( fRegression, "Actual Tracks\n" ); + paramVersion = PARAMVERSION; + WriteTracks( fRegression, FALSE ); + bWroteActualTracks = TRUE; + } + // Print Expected track + to_first = tExpected; + fprintf( fRegression, "Expected Track\n" ); + WriteTracks( fRegression, FALSE ); + fclose( fRegression ); + strcat( message, "Continue test?" ); + if ( eRegression == REGRESSION_CHECK ) { + int rc = wNoticeEx( NT_ERROR, message, _("Stop"), _("Continue") ); + if ( !rc ) { + while ( GetNextLine() && + strncmp( paramLine, "REGRESSION END", 14 ) != 0 ) + ; + break; + } + } + } + // Delete Expected track + to_first = tExpected; + to_last = &to_first; + FreeTrack( tExpected ); + } + to_first = to_first_save; + to_last = to_last_save; + if ( strncmp( paramLine, "REGRESSION END", 14 ) != 0 ) + InputError( "Expected REGRESSION END", TRUE ); + paramVersion = oldParamVersion; + break; + case REGRESSION_NONE: + default: + while ( GetNextLine() ) { + if ( strncmp( paramLine, "REGRESSION END", 14 ) == 0 ) + break; + } + break; + } + free( sRegressionFile ); + + return TRUE; +} + static void EnableButtons( BOOL_T enable ) { @@ -774,7 +975,6 @@ static void PlaybackSetup( void ) wTextClear( demoT ); wShow( demoW ); wFlush(); - RulerRedraw( TRUE ); wPrefFlush(); wWinSetBusy( mainW, TRUE ); wWinSetBusy( mapW, TRUE ); @@ -785,6 +985,8 @@ static void PlaybackSetup( void ) oldMainSize = mainD.size; oldMainScale = mainD.scale; oldScaleName = curScaleName; + playbackX = 0; + playbackY = 0; Reset(); paramVersion = -1; playbackColor=wDrawColorBlack; @@ -816,11 +1018,15 @@ static void Playback( void ) wWinTop( mainW ); demoWinOnTop = FALSE; } + char * oldLocale = NULL; + oldLocale = SaveLocale( "C" ); while (TRUE) { + if ( ! inPlayback ) + // User pressed Quit + break; if ( paramFile == NULL || fgets(paramLine, STR_LONG_SIZE, paramFile) == NULL ) { paramTogglePlaybackHilite = FALSE; - ClearPlaybackCursor(); CloseDemoWindows(); if (paramFile) { fclose( paramFile ); @@ -837,6 +1043,8 @@ static void Playback( void ) paramFile = fopen( demoFileName, "r" ); if ( paramFile == NULL ) { NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Demo"), demoFileName, strerror(errno) ); + RestoreLocale( oldLocale ); + inPlayback = FALSE; return; } @@ -848,13 +1056,14 @@ static void Playback( void ) 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, demoFileName ); fclose( paramFile ); paramFile = NULL; + RestoreLocale( oldLocale ); + inPlayback = FALSE; return; } free(demoFileName); @@ -880,6 +1089,10 @@ static void Playback( void ) } else if (paramLine[0] == 0) { /* empty paramLine */ } else if (ReadTrack( paramLine ) ) { + if ( paramFile == NULL ) { + inPlayback = FALSE; + break; + } } else if (strncmp( paramLine, "STEP", 5 ) == 0) { paramTogglePlaybackHilite = TRUE; wWinTop( demoW ); @@ -898,6 +1111,8 @@ static void Playback( void ) wPause( 1000 ); EnableButtons( FALSE ); } else { + RestoreLocale( oldLocale ); + inPlayback = FALSE; return; } } else if (strncmp( paramLine, "CLEAR", 5 ) == 0) { @@ -908,12 +1123,14 @@ static void Playback( void ) demoWinOnTop = TRUE; while ( ( fgets( paramLine, STR_LONG_SIZE, paramFile ) ) != NULL ) { paramLineNum++; - if ( strncmp(paramLine, "END", 3) == 0 ) + if ( IsEND( END_MESSAGE ) ) break; if ( strncmp(paramLine, "STEP", 3) == 0 ) { wWinTop( demoW ); demoWinOnTop = TRUE; EnableButtons( TRUE ); + RestoreLocale( oldLocale ); + inPlayback = FALSE; return; } PlaybackMessage( paramLine ); @@ -928,14 +1145,13 @@ static void Playback( void ) 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(); + InfoMessage("Esc Key Pressed"); + ConfirmReset(TRUE); } else if (strncmp( paramLine, "VERSION", 7 ) == 0) { paramVersion = atol( paramLine+8 ); if ( paramVersion > iParamVersion ) { @@ -949,7 +1165,18 @@ static void Playback( void ) } else if (strncmp( paramLine, "ORIG ", 5 ) == 0) { if ( !GetArgs( paramLine+5, "fff", &zoom, &x, &y ) ) continue; + if (zoom == 0.0) { + double scale_x = mapD.size.x/(mainD.size.x/mainD.scale); + double scale_y = mapD.size.y/(mainD.size.y/mainD.scale); + if (scale_x<scale_y) + scale_x = scale_y; + scale_x = ceil(scale_x); + if (scale_x < 1) scale_x = 1; + if (scale_x > MAX_MAIN_SCALE) scale_x = MAX_MAIN_SCALE; + zoom = scale_x; + } mainD.scale = zoom; + InfoMessage("Zoom Set to %.0f", zoom); mainD.orig.x = x; mainD.orig.y = y; SetMainSize(); @@ -958,8 +1185,6 @@ static void Playback( void ) tempD.scale = mainD.scale; DoRedraw(); - if (playbackD != NULL && playbackBm != NULL) - MacroDrawBitMap( playbackD, playbackBm, playbackX, playbackY, wDrawColorBlack ); } else if (strncmp( paramLine, "PAUSE ", 6 ) == 0) { paramTogglePlaybackHilite = TRUE; @@ -1028,13 +1253,16 @@ static void Playback( void ) } else if (strncmp( paramLine, "DOCUMENT COPY", 13 ) == 0 ) { while ( ( fgets( paramLine, STR_LONG_SIZE, paramFile ) ) != NULL ) { paramLineNum++; - if ( strncmp(paramLine, "END", 3) == 0 ) + if ( IsEND( END_MESSAGE ) ) break; if ( documentCopy && documentFile ) fprintf( documentFile, "%s", paramLine ); } } else if ( strncmp( paramLine, "DEMOINIT", 8 ) == 0 ) { DemoInitValues(); + } else if ( strncmp( paramLine, "REGRESSION START", 16 ) == 0 ) { + DoRegression( curDemo < 1 ? paramFileName : + demoList(curDemo-1).fileName ); } else { if (strncmp( paramLine, "MOUSE ", 6 ) == 0) { thisCmd = mouseCmd; @@ -1066,6 +1294,8 @@ static void Playback( void ) if (pauseDemo) { EnableButtons( TRUE ); pauseDemo = FALSE; + RestoreLocale( oldLocale ); + inPlayback = FALSE; return; } } @@ -1077,7 +1307,9 @@ static void Playback( void ) fclose( documentFile ); documentFile = NULL; } + inPlayback = FALSE; PlaybackQuit(); + RestoreLocale( oldLocale ); } @@ -1093,13 +1325,15 @@ static int StartPlayback( int cnt, char **pathName, void * context ) return FALSE; } - strcpy( paramFileName, pathName[0] ); + paramFileName = strdup( pathName[0] ); PlaybackSetup(); curDemo = -1; UndoSuspend(); wWinBlockEnable( FALSE ); Playback(); + free( paramFileName ); + paramFileName = NULL; return TRUE; } @@ -1138,7 +1372,13 @@ static void DoDemoButton( void * command ) break; case 3: /* quit */ - PlaybackQuit(); + if ( inPlayback ) { + // We will exit the loop in Playback() after the current command + inPlayback = FALSE; + } else { + // We're waiting for the user to press 'Step' + PlaybackQuit(); + } break; default: ; @@ -1196,11 +1436,10 @@ static char * demoInitParams[] = { "GROUP layout", "display tunnels 1", "display endpt 2", - "display labelenable 7", + "display labelenable 0", "display description-fontsize 48", "display labelscale 8", "display layoutlabels 6", - "display color-layers 0", "display tworailscale 16", "display tiedraw 0", "pref mingridspacing 5", @@ -1211,8 +1450,7 @@ static char * demoInitParams[] = { "display carhotbarlabels 1", "display hideTrainsInTunnels 0", "GROUP display", - "cmdopt move-quick 0", - "pref turntable-angle 7.500", + "pref turntable-angle 15.00", "cmdopt preselect 1", "pref coupling-speed-max 100", "cmdopt rightclickmode 0", @@ -1261,10 +1499,12 @@ static char * demoInitParams[] = { "GROUP grid", "misc toolbarset 65535", "GROUP misc", - "sticky set 268435383", /* 0xfffffb7 - all but Helix and Turntable */ + "sticky set 67108479", /* 0x3fffe7f - all but Helix and Turntable */ "GROUP sticky", "turnout hide 0", "layer button-count 10", + "cmdopt selectmode 0", + "cmdopt selectzero 1", NULL }; static void DemoInitValues( void ) @@ -1291,6 +1531,8 @@ static void DemoInitValues( void ) } for ( cpp = demoInitParams; *cpp; cpp++ ) paramPlaybackProc( *cpp ); + // Have to do this manually + oldMagneticSnap = MagneticSnap( TRUE ); } @@ -1369,11 +1611,22 @@ EXPORT BOOL_T MacroInit( void ) leftDragColor = drawColorBlue; arrow0_bm = wDrawBitMapCreate( mainD.d, arrow0_width, arrow0_height, 12, 12, arrow0_bits ); + arrow0_shift_bm = wDrawBitMapCreate( mainD.d, arrow0_shift_width, arrow0_shift_height, 12, 12, arrow0_shift_bits ); + arrow0_ctl_bm = wDrawBitMapCreate( mainD.d, arrow0_ctl_width, arrow0_ctl_height, 12, 12, arrow0_ctl_bits ); arrow3_bm = wDrawBitMapCreate( mainD.d, arrow3_width, arrow3_height, 12, 12, arrow3_bits ); + arrow3_shift_bm = wDrawBitMapCreate( mainD.d, arrow3_shift_width, arrow3_shift_height, 12, 12, arrow3_shift_bits ); + arrow3_ctl_bm = wDrawBitMapCreate( mainD.d, arrow3_ctl_width, arrow3_ctl_height, 12, 12, arrow3_ctl_bits ); + arrowr3_bm = wDrawBitMapCreate( mainD.d, arrowr3_width, arrowr3_height, 12, 12, arrowr3_bits ); + arrowr3_shift_bm = wDrawBitMapCreate( mainD.d, arrowr3_shift_width, arrowr3_shift_height, 12, 12, arrowr3_shift_bits ); + arrowr3_ctl_bm = wDrawBitMapCreate( mainD.d, arrowr3_ctl_width, arrowr3_ctl_height, 12, 12, arrowr3_ctl_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 ); + + log_playbackCursor = LogFindIndex( "playbackcursor" ); + log_regression = LogFindIndex( "regression" ); + return TRUE; } diff --git a/app/bin/manifest.c b/app/bin/manifest.c new file mode 100644 index 0000000..1652996 --- /dev/null +++ b/app/bin/manifest.c @@ -0,0 +1,176 @@ +/** \file manifest.c + * JSON routines + */ + /* XTrkCad - Model Railroad CAD + * Copyright (C) 2018 Adam Richards and 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 <string.h> + +#include "cJSON.h" +#include "fileio.h" +#include "layout.h" +#include "misc2.h" +#include "paths.h" +#include "include/utf8convert.h" + +extern int log_zip; + + /********************************************************** + * Build JSON Manifest - manifest.json + * There are only two objects in the root - + * - The layout object defines the correct filename for the layout + * - The dependencies object is an arraylist of included elements + * + * Each element has a name, a filename and an arch-path (where in the archive it is located) + * It may have other values - a common one the copy-path is where it was copied from the originators machine (info only) + * + * There is one reserved name - "background" which is for the image file that is used as a layout background + * + *\param IN nameOfLayout - the layout this is a manifest for + *\param IN background - the full filepath to the background image (or NULL) -> TODO this will become an array with a count + *\param IN DependencyDir - the relative path in the archive to the directory in which the included object(s) will be stored + * + *\returns a String containing the JSON object + */ + +char* CreateManifest(char* nameOfLayout, char* background, + char* dependencyDir) +{ + cJSON* manifest = cJSON_CreateObject(); + if (manifest != NULL) { + char *copyOfFileName = MyStrdup(nameOfLayout); + cJSON* a_object = cJSON_CreateObject(); + cJSON_AddItemToObject(manifest, "layout", a_object); +#ifdef WINDOWS + copyOfFileName = Convert2UTF8(copyOfFileName); +#endif // WINDOWS + cJSON_AddStringToObject(a_object, "name", copyOfFileName); + MyFree(copyOfFileName); + + cJSON* dependencies = cJSON_AddArrayToObject(manifest, "dependencies"); + cJSON* b_object = cJSON_CreateObject(); + if (background && background[0]) { + char *backg; + cJSON_AddStringToObject(b_object, "name", "background"); + + backg = MyStrdup(FindFilename(background)); +#ifdef WINDOWS + backg = Convert2UTF8(backg); +#endif + cJSON_AddStringToObject(b_object, "filename", backg); + MyFree(backg); + backg = MyStrdup(background); +#ifdef WINDOWS + backg = Convert2UTF8(backg); + ConvertPathForward(backg); +#endif // WINDOWS + cJSON_AddStringToObject(b_object, "copy-path", backg); + cJSON_AddStringToObject(b_object, "arch-path", dependencyDir); + MyFree(backg); + cJSON_AddNumberToObject(b_object, "size", GetLayoutBackGroundSize()); + cJSON_AddNumberToObject(b_object, "pos-x", GetLayoutBackGroundPos().x); + cJSON_AddNumberToObject(b_object, "pos-y", GetLayoutBackGroundPos().y); + cJSON_AddNumberToObject(b_object, "screen", GetLayoutBackGroundScreen()); + cJSON_AddNumberToObject(b_object, "angle", GetLayoutBackGroundAngle()); + cJSON_AddItemToArray(dependencies, b_object); + } + } + char* json_Manifest = cJSON_Print(manifest); + cJSON_Delete(manifest); + return json_Manifest; +} + +/************************************************************************** + * Pull in a Manifest File and extract values from it + * \param IN manifest - the full path to the mainifest.json file + * \param IN zip_directory - the path to the directory for extracted objects + * + * \returns - the layout filename + */ + +char* ParseManifest(char* manifest, char* zip_directory) +{ + char* background_file[1] = { NULL }; + char* layoutname; + + char *oldLocale = SaveLocale("C"); + cJSON* json_manifest = cJSON_Parse(manifest); + RestoreLocale(oldLocale); + + cJSON* layout = cJSON_GetObjectItemCaseSensitive(json_manifest, "layout"); + cJSON* name = cJSON_GetObjectItemCaseSensitive(layout, "name"); + layoutname = cJSON_GetStringValue(name); +#ifdef WINDOWS + ConvertUTF8ToSystem(layoutname); +#endif // WINDOWS + + LOG(log_zip, 1, ("Zip-Manifest %s \n", layoutname)) +#if DEBUG + fprintf(stderr, "Layout name %s \n", layoutname); +#endif + + cJSON* dependencies = cJSON_GetObjectItemCaseSensitive(json_manifest, + "dependencies"); + cJSON* dependency; + cJSON_ArrayForEach(dependency, dependencies) { + cJSON* name = cJSON_GetObjectItemCaseSensitive(dependency, "name"); + if (strcmp(cJSON_GetStringValue(name), "background") == 0) { + char *file; + char *path; + cJSON* filename = cJSON_GetObjectItemCaseSensitive(dependency, "filename"); + cJSON* archpath = cJSON_GetObjectItemCaseSensitive(dependency, "arch-path"); + file = MyStrdup(cJSON_GetStringValue(filename)); + path = MyStrdup(cJSON_GetStringValue(archpath)); +#ifdef WINDOWS + ConvertUTF8ToSystem(file); + ConvertUTF8ToSystem(path); +#endif + MakeFullpath(&background_file[0], zip_directory, path, + file, NULL); + MyFree(file); + MyFree(path); +#if DEBUG + printf("Link to background image %s \n", background_file[0]); +#endif + LoadImageFile(1, &background_file[0], NULL); + cJSON* size = cJSON_GetObjectItemCaseSensitive(dependency, "size"); + SetLayoutBackGroundSize(size->valuedouble); + cJSON* posx = cJSON_GetObjectItemCaseSensitive(dependency, "pos-x"); + cJSON* posy = cJSON_GetObjectItemCaseSensitive(dependency, "pos-y"); + coOrd pos; + pos.x = posx->valuedouble; + pos.y = posy->valuedouble; + SetLayoutBackGroundPos(pos); + cJSON* screen = cJSON_GetObjectItemCaseSensitive(dependency, "screen"); + SetLayoutBackGroundScreen((int)screen->valuedouble); + cJSON* angle = cJSON_GetObjectItemCaseSensitive(dependency, "angle"); + SetLayoutBackGroundAngle(angle->valuedouble); + LayoutBackGroundSave(); //Force out Values to override saved + } + } + char *str = NULL; + if (background_file[0]) { + free(background_file[0]); + } + if (layoutname) { + str = strdup(layoutname); + } + cJSON_Delete(json_manifest); + return str; +} + diff --git a/app/bin/manifest.h b/app/bin/manifest.h new file mode 100644 index 0000000..8e751f0 --- /dev/null +++ b/app/bin/manifest.h @@ -0,0 +1,6 @@ +#ifndef HAVE_MANIFEST_H +#define HAVE_MANIFEST_H + char* CreateManifest(char* nameOfLayout, char* background, + char* DependencyDir); + char* ParseManifest(char* manifest, char* zip_directory);
+#endif
diff --git a/app/bin/misc.c b/app/bin/misc.c index 827c2db..2ac1e2f 100644 --- a/app/bin/misc.c +++ b/app/bin/misc.c @@ -1,4 +1,4 @@ -/* \file misc.c +/* file misc.c * Main routine and initialization for the application */ @@ -40,7 +40,7 @@ #define R_OK (02) #define access _access #if _MSC_VER >1300 - #define strdup _strdup +#define strdup _strdup #endif #else #include <sys/stat.h> @@ -62,6 +62,7 @@ #include "messages.h" #include "misc.h" #include "param.h" +#include "include/paramfilelist.h" #include "paths.h" #include "smalldlg.h" #include "track.h" @@ -72,6 +73,10 @@ char *userLocale = NULL; extern wBalloonHelp_t balloonHelp[]; + +static wMenuToggle_p mapShowMI; +static wMenuToggle_p magnetsMI; + #ifdef DEBUG #define CHECK_BALLOONHELP /*#define CHECK_UNUSED_BALLOONHELP*/ @@ -83,7 +88,7 @@ void DoCarDlg(void); /**************************************************************************** * - EXPORTED VARIABLES + EXPORTED VARIABLES * */ @@ -96,7 +101,7 @@ EXPORT wWin_p mainW; EXPORT wIndex_t changed = 0; -EXPORT char message[STR_LONG_SIZE]; +EXPORT char message[STR_HUGE_SIZE]; static char message2[STR_LONG_SIZE]; EXPORT REGION_T curRegion = 0; @@ -107,7 +112,7 @@ 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 long onStartup; /**< controls behaviour after startup: load last layout if zero, else start with blank canvas */ EXPORT wButton_p undoB; EXPORT wButton_p redoB; @@ -115,13 +120,15 @@ EXPORT wButton_p redoB; EXPORT wButton_p zoomUpB; EXPORT wButton_p zoomDownB; wButton_p mapShowB; +wButton_p magnetsB; +wButton_p backgroundB; EXPORT wIndex_t checkPtMark = 0; EXPORT wMenu_p demoM; EXPORT wMenu_p popup1M, popup2M; EXPORT wMenu_p popup1aM, popup2aM; - +EXPORT wMenu_p popup1mM, popup2mM; static wIndex_t curCommand = 0; EXPORT void * commandContext; @@ -129,6 +136,8 @@ EXPORT wIndex_t cmdGroup; EXPORT wIndex_t joinCmdInx; EXPORT wIndex_t modifyCmdInx; EXPORT long rightClickMode = 0; +EXPORT long selectMode = 0; +EXPORT long selectZero = 1; EXPORT DIST_T easementVal = 0.0; EXPORT DIST_T easeR = 0.0; EXPORT DIST_T easeL = 0.0; @@ -151,7 +160,7 @@ static int verbose = 0; static wMenuList_p winList_mi; static BOOL_T inMainW = TRUE; -static long stickySet; +static long stickySet = 0; static long stickyCnt = 0; static char * stickyLabels[33]; #define TOOLBARSET_INIT (0xFFFF) @@ -168,10 +177,11 @@ static BOOL_T messageListEmpty = TRUE; 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 paramData_t menuPLs[101] = { { PD_LONG, &toolbarSet, "toolbarset" }, { + PD_LONG, &curTurnoutEp, "cur-turnout-ep" } }; static paramGroup_t menuPG = { "misc", PGO_RECORD, menuPLs, 2 }; + +extern wBool_t wDrawDoTempDraw; /**************************************************************************** * @@ -186,12 +196,11 @@ EXPORT long totalReallocs = 0; EXPORT long totalFreeed = 0; EXPORT long totalFrees = 0; -static unsigned long guard0 = 0xDEADBEEF; -static unsigned long guard1 = 0xAF00BA8A; +static unsigned long guard0 = 0xDEADBEEF; +static unsigned long guard1 = 0xAF00BA8A; static int log_malloc; -EXPORT void * MyMalloc ( long size ) -{ +EXPORT void * MyMalloc(long size) { void * p; totalMallocs++; totalMalloced += size; @@ -200,29 +209,26 @@ EXPORT void * MyMalloc ( long size ) AbortProg( "mallocing > 65500 bytes" ); } #endif - p = malloc( (size_t)size + sizeof (size_t) + 2 * sizeof (unsigned long) ); + 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 ); + 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 ) -{ +EXPORT void * MyRealloc(void * old, long size) { size_t oldSize; void * new; - if (old==NULL) - return MyMalloc( size ); + if (old == NULL) + return MyMalloc(size); totalReallocs++; totalRealloced += size; #if defined(WINDOWS) && ! defined(WIN32) @@ -230,67 +236,61 @@ EXPORT void * MyRealloc( void * old, long size ) AbortProg( "reallocing > 65500 bytes" ); } #endif - if ( *(unsigned long*)((char*)old - sizeof (unsigned long)) != guard0 ) { - AbortProg( "Guard0 is hosed" ); + 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" ); + 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) { + 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 ); + free((char*) old - sizeof *(long*) 0 - sizeof *(size_t*) 0); return NULL; } - new = MyMalloc( size ); + new = MyMalloc(size); if (new == NULL && size) - AbortProg( "No memory" ); - memcpy( new, old, min((size_t)size, oldSize) ); + AbortProg("No memory"); + memcpy(new, old, min((size_t )size, oldSize)); MyFree(old); return new; } - -EXPORT void MyFree( void * ptr ) -{ +EXPORT void MyFree(void * ptr) { size_t oldSize; totalFrees++; if (ptr) { - if ( *(unsigned long*)((char*)ptr - sizeof (unsigned long)) != guard0 ) { - AbortProg( "Guard0 is hosed" ); + 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" ); + 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)) ) + 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 ); + free((char*) ptr - sizeof *(long*) 0 - sizeof *(size_t*) 0); } } - -EXPORT void * memdup( void * src, size_t size ) -{ +EXPORT void * memdup(void * src, size_t size) { void * p; - p = MyMalloc( size ); + p = MyMalloc(size); if (p == NULL) - AbortProg( "No memory" ); - memcpy( p, src, size ); + AbortProg("No memory"); + memcpy(p, src, size); return p; } - -EXPORT char * MyStrdup( const char * str ) -{ +EXPORT char * MyStrdup(const char * str) { char * ret; - ret = (char*)MyMalloc( strlen( str ) + 1 ); - strcpy( ret, str ); + ret = (char*) MyMalloc(strlen(str) + 1); + strcpy(ret, str); return ret; } @@ -304,32 +304,70 @@ EXPORT char * MyStrdup( const char * str ) * */ EXPORT char * ConvertToEscapedText(const char * text) { - int text_i=0; + int text_i = 0; int add = 0; //extra chars for escape - while(text[text_i]) { + while (text[text_i]) { switch (text[text_i]) { - case '\n': add++; break; - case '\t': add++; break; - case '\\': add++; break; - case '\"': add++; break; + case '\n': + add++; + break; + case '\t': + add++; + break; + case '\\': + add++; + break; + case '\"': + add++; + break; } text_i++; } - char * cout = MyMalloc(strlen(text)+1+add); + unsigned cnt = strlen(text) + 1 + add; +#ifdef WINDOWS + cnt *= 2; +#endif + char * cout = MyMalloc(cnt); int cout_i = 0; text_i = 0; - while(text[text_i]) { + while (text[text_i]) { char c = text[text_i]; switch (c) { - case '\n': cout[cout_i] = '\\'; cout_i++; cout[cout_i] = 'n'; cout_i++; break; // Line Feed - case '\t': cout[cout_i] = '\\'; cout_i++; cout[cout_i] = 't'; cout_i++; break; // Tab - case '\\': cout[cout_i] = '\\'; cout_i++; cout[cout_i] = '\\'; cout_i++; break; // BackSlash - case '\"': cout[cout_i] = '\"'; cout_i++; cout[cout_i] = '\"'; cout_i++; break; // Double Quotes - default: cout[cout_i] = c; cout_i++; + case '\n': + cout[cout_i] = '\\'; + cout_i++; + cout[cout_i] = 'n'; + cout_i++; + break; // Line Feed + case '\t': + cout[cout_i] = '\\'; + cout_i++; + cout[cout_i] = 't'; + cout_i++; + break; // Tab + case '\\': + cout[cout_i] = '\\'; + cout_i++; + cout[cout_i] = '\\'; + cout_i++; + break; // BackSlash + case '\"': + cout[cout_i] = '\"'; + cout_i++; + cout[cout_i] = '\"'; + cout_i++; + break; // Double Quotes + default: + cout[cout_i] = c; + cout_i++; } text_i++; } cout[cout_i] = '\0'; +#ifdef WINDOWS + wSystemToUTF8(cout, cout, cnt); +#endif // WINDOWS + return cout; } @@ -340,83 +378,82 @@ EXPORT char * ConvertToEscapedText(const char * text) { * \n = LineFeed 0x0A * \t = Tab 0x09 * \\ = \ The way to still produce backslash - * "" = " Take out quotes included so that other (CSV-like) programs could read the files * */ EXPORT char * ConvertFromEscapedText(const char * text) { - enum { CHARACTER, ESCAPE, QUOTE } state = CHARACTER; - char * cout = MyMalloc(strlen(text)+1); //always equal to or shorter than - int text_i = 0; - int cout_i = 0; - int c; - while (text[text_i]) { - c = text[text_i]; - switch (state) { - case CHARACTER: - if (c == '\\') { - state = ESCAPE; - } else if (c == '\"') { - state = QUOTE; - } else { - cout[cout_i] = c; - cout_i++; - } - break; - - case ESCAPE: - switch (c) { - case '\\': cout[cout_i] = '\\'; cout_i++; break; // "\\" = "\" - case 'n': cout[cout_i] = '\n'; cout_i++; break; // LF - case 't': cout[cout_i] = '\t'; cout_i++; break; // TAB - } - state = CHARACTER; - break; - case QUOTE: - switch(c) { - case '\"': cout[cout_i] = c; cout_i++; break; //One quote = NULL, Two quotes = 1 quote - } - state = CHARACTER; - } - text_i++; - } - cout[cout_i] = '\0'; - return cout; -} + enum { + CHARACTER, ESCAPE + } state = CHARACTER; + char * cout = MyMalloc(strlen(text) + 1); //always equal to or shorter than + int text_i = 0; + int cout_i = 0; + int c; + while (text[text_i]) { + c = text[text_i]; + switch (state) { + case CHARACTER: + if (c == '\\') { + state = ESCAPE; + } else { + cout[cout_i] = c; + cout_i++; + } + break; + case ESCAPE: + switch (c) { + case '\\': + cout[cout_i] = '\\'; + cout_i++; + break; // "\\" = "\" + case 'n': + cout[cout_i] = '\n'; + cout_i++; + break; // LF + case 't': + cout[cout_i] = '\t'; + cout_i++; + break; // TAB + } + state = CHARACTER; + break; + } + text_i++; + } + cout[cout_i] = '\0'; + return cout; +} -EXPORT void AbortProg( - char * msg, - ... ) -{ +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 ); + va_start(ap, msg); + vsprintf(message, msg, ap); + va_end(ap); if (abort2) { - wNoticeEx( NT_ERROR, message, _("ABORT"), NULL ); + wNoticeEx( NT_ERROR, message, _("ABORT"), NULL); } else { - strcat( message, _("\nDo you want to save your layout?") ); - rc = wNoticeEx( NT_ERROR, message, _("Ok"), _("ABORT") ); + strcat(message, _("\nDo you want to save your layout?")); + rc = wNoticeEx( NT_ERROR, message, _("Ok"), _("ABORT")); if (rc) { - DoSaveAs( (doSaveCallBack_p)abort ); + DoSaveAs((doSaveCallBack_p) abort); } else { abort(); } } } - -EXPORT char * Strcpytrimed( char * dst, char * src, BOOL_T double_quotes ) -{ +EXPORT char * Strcpytrimed(char * dst, char * src, BOOL_T double_quotes) { char * cp; - while (*src && isspace((unsigned char)*src) ) src++; + while (*src && isspace((unsigned char) *src)) + src++; if (!*src) return dst; - cp = src+strlen(src)-1; - while ( cp>src && isspace((unsigned char)*cp) ) cp--; - while ( src<=cp ) { + cp = src + strlen(src) - 1; + while (cp > src && isspace((unsigned char) *cp)) + cp--; + while (src <= cp) { if (*src == '"' && double_quotes) *dst++ = '"'; *dst++ = *src++; @@ -425,50 +462,83 @@ EXPORT char * Strcpytrimed( char * dst, char * src, BOOL_T double_quotes ) return dst; } +static char * directory; -EXPORT char * BuildTrimedTitle( char * cp, char * sep, char * mfg, char * desc, char * partno ) -{ - cp = Strcpytrimed( cp, mfg, FALSE ); - strcpy( cp, sep ); +#ifdef WINDOWS +#define F_OK (0) +#endif + +EXPORT wBool_t CheckHelpTopicExists(const char * topic) { + + char * htmlFile; + + // Check the file exits in the distro + + if (!directory) + directory = malloc(BUFSIZ); + + if (directory == NULL) return 0; + + sprintf(directory, "%s/html/", wGetAppLibDir()); + + htmlFile = malloc(strlen(directory)+strlen(topic) + 6); + + sprintf(htmlFile, "%s%s.html", directory, topic); + + if( access( htmlFile, F_OK ) == -1 ) { + + printf("Missing help topic %s\n",topic); + + free(htmlFile); + + return 0; + + } + + free(htmlFile); + + return 1; + +} + +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 = Strcpytrimed(cp, desc, FALSE); + strcpy(cp, sep); cp += strlen(cp); - cp = Strcpytrimed( cp, partno, FALSE ); + cp = Strcpytrimed(cp, partno, FALSE); return cp; } - -static void ShowMessageHelp( int index, const char * label, void * data ) -{ +static void ShowMessageHelp(int index, const char * label, void * data) { char msgKey[STR_SIZE], *cp, *msgSrc; - msgSrc = (char*)data; + 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 ); + 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 ); + memcpy(msgKey, msgSrc, cp - msgSrc); + msgKey[cp - msgSrc] = 0; + wHelp(msgKey); } - -static char * ParseMessage( - char *msgSrc ) -{ - char *cp1=NULL, *cp2=NULL; +static char * ParseMessage(char *msgSrc) { + char *cp1 = NULL, *cp2 = NULL; static char shortMsg[STR_SIZE]; - cp1 = strchr( _(msgSrc), '\t' ); + cp1 = strchr(_(msgSrc), '\t'); if (cp1) { - cp2 = strchr( cp1+1, '\t' ); + cp2 = strchr(cp1 + 1, '\t'); if (cp2) { cp1++; - memcpy( shortMsg, cp1, cp2-cp1 ); - shortMsg[cp2-cp1] = 0; + memcpy(shortMsg, cp1, cp2 - cp1); + shortMsg[cp2 - cp1] = 0; cp1 = shortMsg; cp2++; } else { @@ -476,157 +546,177 @@ static char * ParseMessage( cp2 = cp1; } if (messageListEmpty) { - wMenuListDelete( messageList_ml, _(MESSAGE_LIST_EMPTY) ); + wMenuListDelete(messageList_ml, _(MESSAGE_LIST_EMPTY)); messageListEmpty = FALSE; } - wMenuListAdd( messageList_ml, 0, cp1, _(msgSrc) ); + wMenuListAdd(messageList_ml, 0, cp1, _(msgSrc)); return cp2; } else { return _(msgSrc); } } - -EXPORT void InfoMessage( char * format, ... ) -{ +EXPORT void InfoMessage(char * format, ...) { va_list ap; - va_start( ap, format ); - format = ParseMessage( format ); - vsprintf( message2, format, ap ); - va_end( ap ); + va_start(ap, format); + format = ParseMessage(format); + vsprintf(message2, format, ap); + va_end(ap); /*InfoSubstituteControl( NULL, NULL );*/ if (inError) return; - SetMessage( message2 ); + SetMessage(message2); } - -EXPORT void ErrorMessage( char * format, ... ) -{ +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 ); + 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 NoticeMessage( char * format, char * yes, char * no, ... ) -{ +EXPORT int NoticeMessage2(int playbackRC, 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 ); + 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); } +/** +* Set the file's changed flag and update the window title. +*/ -EXPORT int NoticeMessage2( int playbackRC, char * format, char * yes, char * no, ... ) +void +FileIsChanged(void) { - 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 ); + changed++; + SetWindowTitle(); } - + /***************************************************************************** * * MAIN BUTTON HANDLERS * */ + /** + * Confirm a requested operation in case of possible loss of changes. + * + * \param label2 IN operation to be cancelled, unused at the moment + * \param after IN function to be executed on positive confirmation + * \return true if proceed, false if cancel operation + */ +/** TODO: make sensible messages when requesting confirmation */ -EXPORT void Confirm( char * label2, doSaveCallBack_p after ) +bool +Confirm(char * label2, doSaveCallBack_p after) { - int rc; + int rc = -1; 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; - } + 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")); } - after(); - return; + + switch (rc) { + case -1: /* do not save */ + after(); + break; + case 0: /* cancel operation */ + break; + case 1: /* save */ + LayoutBackGroundInit(FALSE); + LayoutBackGroundSave(); + DoSave(after); + break; + } + return(rc != 0); } -static void ChkLoad( void ) -{ +static void ChkLoad(void) { Confirm(_("Load"), DoLoad); } -static void ChkRevert( void ) +static void ChkExamples( 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 */ - char *filename = GetLayoutFullPath(); - LoadTracks( 1, &filename, NULL ); - } - } + Confirm(_("examples"), DoExamples); } +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 */ + char *filename = GetLayoutFullPath(); + LoadTracks(1, &filename, NULL); + } + } +} static char * fileListPathName; -static void AfterFileList( void ) -{ - DoFileList( 0, NULL, 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 ); +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 ) -{ +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(); + wWinGetSize(mainW, &width, &height); + wPrefSetInteger("draw", "mainwidth", width); + wPrefSetInteger("draw", "mainheight", height); + SaveParamFileList(); ParamUpdatePrefs(); wPrefSetString( "misc", "lastlayout", GetLayoutFullPath()); + wPrefSetInteger( "misc", "lastlayoutexample", bExample ); - if ( fileList_ml ) { - strcpy( file, "file" ); + if (fileList_ml) { + strcpy(file, "file"); file[5] = 0; - for ( inx=0; inx<NUM_FILELIST; inx++ ) { - fileName = wMenuListGet( fileList_ml, inx, &pathName ); + 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 ); + file[4] = '0' + inx; + sprintf(message, "%s", (char* )pathName); + wPrefSetString("filelist", file, message); } } } @@ -637,8 +727,7 @@ EXPORT void SaveState( void ) /* * Clean up before quitting */ -static void DoQuitAfter( void ) -{ +static void DoQuitAfter(void) { changed = 0; SaveState(); @@ -649,34 +738,34 @@ static void DoQuitAfter( void ) * to close the application. Before shutting down confirmation is gotten to * prevent data loss. */ -void DoQuit( void ) -{ - Confirm(_("Quit"), DoQuitAfter ); +void DoQuit(void) { + if (Confirm(_("Quit"), DoQuitAfter)) { + #ifdef CHECK_UNUSED_BALLOONHELP - ShowUnusedBalloonHelp(); + ShowUnusedBalloonHelp(); #endif - LogClose(); - wExit(0); + LogClose(); + wExit(0); + } } -static void DoClearAfter( void ) -{ - +static void DoClearAfter(void) { + + Reset(); ClearTracks(); /* set all layers to their default properties and set current layer to 0 */ - DefaultLayerProperties(); DoLayout(NULL); - checkPtMark = 0; - Reset(); + checkPtMark = changed = 0; DoChangeNotification( CHANGE_MAIN|CHANGE_MAP ); + bReadOnly = TRUE; EnableCommands(); SetLayoutFullPath(""); SetWindowTitle(); + LayoutBackGroundInit(TRUE); } -static void DoClear( void ) -{ +static void DoClear(void) { Confirm(_("Clear"), DoClearAfter); } @@ -684,9 +773,8 @@ static void DoClear( void ) * Toggle visibility state of map window. */ -void MapWindowToggleShow(void) -{ - MapWindowShow(!mapVisible); +void MapWindowToggleShow(void) { + MapWindowShow(!mapVisible); } /** @@ -695,114 +783,114 @@ void MapWindowToggleShow(void) * \param state IN TRUE if visible, FALSE if hidden */ -void MapWindowShow(int state) -{ - mapVisible = state; - wPrefSetInteger("misc", "mapVisible", mapVisible); - wMenuToggleSet(mapShowMI, mapVisible); +void MapWindowShow(int state) { + mapVisible = state; + wPrefSetInteger("misc", "mapVisible", mapVisible); + wMenuToggleSet(mapShowMI, mapVisible); - if (mapVisible) { - DoChangeNotification(CHANGE_MAP); - } + if (mapVisible) { + DoChangeNotification(CHANGE_MAP); + } - wWinShow(mapW, mapVisible); - wButtonSetBusy(mapShowB, (wBool_t)mapVisible); + wWinShow(mapW, mapVisible); + wButtonSetBusy(mapShowB, (wBool_t) mapVisible); } -static void DoShowWindow( - int index, - const char * name, - void * data ) +/** + * Set magnets state + */ +int MagneticSnap(int state) { + int oldState = magneticSnap; + magneticSnap = state; + wPrefSetInteger("misc", "magnets", magneticSnap); + wMenuToggleSet(magnetsMI, magneticSnap); + wButtonSetBusy(magnetsB, (wBool_t) magneticSnap); + return oldState; +} + +/** + * Toggle magnets on/off + */ +void MagneticSnapToggle(void) { + MagneticSnap(!magneticSnap); +} + + +static void DoShowWindow(int index, const char * name, void * data) { if (data == mapW) { if (mapVisible == FALSE) { - MapWindowShow( TRUE ); + MapWindowShow( TRUE); return; } } - wWinShow( (wWin_p)data, 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 ) -{ +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 ) + 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; + 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 ); + wMenuListAdd(winList_mi, -1, wWinGetTitle(win), win); + wWinShow(win, TRUE); } - -EXPORT void wHide( - wWin_p win ) -{ +EXPORT void wHide(wWin_p win) { int inx; - wWinShow( win, FALSE ); - wWinSetBusy( win, FALSE ); - if ( inMainW && win == aboutW ) + 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 ) + 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 ) -{ +EXPORT void CloseDemoWindows(void) { int inx; - for ( inx=0; inx<demoWindows_da.cnt; inx++ ) - if ( demoWindows(inx) != NULL ) - wHide( demoWindows(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 ) { +EXPORT void DefaultProc(wWin_p win, winProcEvent e, void * data) { + switch (e) { case wClose_e: - wMenuListDelete( winList_mi, wWinGetTitle(win) ); + wMenuListDelete(winList_mi, wWinGetTitle(win)); if (data != NULL) - ConfirmReset( FALSE ); - wWinDoCancel( win ); + ConfirmReset( FALSE); + wWinDoCancel(win); break; default: break; } } - -static void NextWindow( void ) -{ +static void NextWindow(void) { } -EXPORT void SelectFont( void ) -{ +EXPORT void SelectFont(void) { wSelectFont(_("XTrackCAD Font")); } @@ -817,38 +905,36 @@ EXPORT void SelectFont( void ) #define NUM_CMDMENUS (4) static struct { - wControl_p control; - wBool_t enabled; - wPos_t x, y; - long options; - int group; - wIndex_t cmdInx; - } buttonList[BUTTON_MAX]; + 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]; + 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 ) -{ +EXPORT const char * GetBalloonHelpStr(char * helpKey) { wBalloonHelp_t * bh; #ifdef CHECK_UNUSED_BALLOONHELP if ( balloonHelpCnts == NULL ) { @@ -857,8 +943,8 @@ EXPORT const char * GetBalloonHelpStr( char * helpKey ) memset( balloonHelpCnts, 0, (sizeof *(int*)0) * (bh-balloonHelp) ); } #endif - for ( bh=balloonHelp; bh->name; bh++ ) { - if ( strcmp( bh->name, helpKey ) == 0 ) { + for (bh = balloonHelp; bh->name; bh++) { + if (strcmp(bh->name, helpKey) == 0) { #ifdef CHECK_UNUSED_BALLOONHELP balloonHelpCnts[(bh-balloonHelp)]++; #endif @@ -866,107 +952,134 @@ EXPORT const char * GetBalloonHelpStr( char * helpKey ) } } #ifdef CHECK_BALLOONHELP -fprintf( stderr, _("No balloon help for %s\n"), helpKey ); + 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 ); + if ( balloonHelpCnts[cnt] == 0 ) + fprintf( stderr, "unused BH %s\n", balloonHelp[cnt].name ); } #endif +EXPORT const char* GetCurCommandName() { + return commandList[curCommand].helpKey; +} -EXPORT void EnableCommands( void ) -{ +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++ ) { + 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 ) + 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) ) + 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].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 ); + wMenuPushEnable(commandList[inx].menu[minx], enable); commandList[inx].enabled = enable; } } } - for ( inx=0; inx<menuPG.paramCnt; inx++ ) { - if ( menuPLs[inx].control == NULL ) + for (inx = 0; inx < menuPG.paramCnt; inx++) { + if (menuPLs[inx].control == NULL) continue; - if ( (menuPLs[inx].option & IC_SELECTED) && - selectedTrackCount <= 0 ) + 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) ) + 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 ); + 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 ); - } + for (inx = 0; inx < buttonCnt; inx++) { + if (buttonList[inx].cmdInx < 0 + && (buttonList[inx].options & IC_SELECTED)) + wControlActive(buttonList[inx].control, selectedTrackCount > 0); + } } +EXPORT wIndex_t GetCurrentCommand() { + return curCommand; +} -EXPORT void Reset( void ) -{ +static wIndex_t autosave_count = 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); + 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); + wSetCursor(mainD.d, preSelect ? defaultCursor : wCursorQuestion); commandContext = commandList[curCommand].context; - if ( commandList[curCommand].buttInx >= 0 ) - wButtonSetBusy( (wButton_p)buttonList[commandList[curCommand].buttInx].control, TRUE ); + 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 ) { + if (checkPtInterval > 0 + && changed >= checkPtMark + (wIndex_t) checkPtInterval + && !inPlayback) { DoCheckPoint(); checkPtMark = changed; + + autosave_count++; + + if ((autosaveChkPoints>0) && (autosave_count>=autosaveChkPoints)) { + DoSave(NULL); + InfoMessage(_("File AutoSaved")); + autosave_count = 0; + } } - MainRedraw(); - MapRedraw(); + + + + ClrAllTrkBits( TB_UNDRAWN ); + DoRedraw(); // Reset EnableCommands(); ResetMouseState(); -LOG( log_command, 1, ( "COMMAND RESET %s\n", commandList[curCommand].helpKey ) ) - (void)commandList[curCommand].cmdProc( C_START, zero ); + 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 BOOL_T CheckClick(wAction_t *action, coOrd *pos, BOOL_T checkLeft, + BOOL_T checkRight) { static long time0; static coOrd pos0; long time1; @@ -974,6 +1087,11 @@ static BOOL_T CheckClick( DIST_T distDelta; switch (*action) { + case C_LDOUBLE: + if (!checkLeft) + return TRUE; + time0 = 0; + break; case C_DOWN: if (!checkLeft) return TRUE; @@ -986,9 +1104,8 @@ static BOOL_T CheckClick( if (time0 != 0) { time1 = wGetTimer() - adjTimer; timeDelta = time1 - time0; - distDelta = FindDistance( *pos, pos0 ); - if ( timeDelta > dragTimeout || - distDelta > dragDistance ) { + distDelta = FindDistance(*pos, pos0); + if (timeDelta > dragTimeout || distDelta > dragDistance) { time0 = 0; *pos = pos0; *action = C_DOWN; @@ -1003,7 +1120,7 @@ static BOOL_T CheckClick( if (time0 != 0) { time1 = wGetTimer() - adjTimer; timeDelta = time1 - time0; - distDelta = FindDistance( *pos, pos0 ); + distDelta = FindDistance(*pos, pos0); time0 = 0; *action = C_LCLICK; } @@ -1020,9 +1137,8 @@ static BOOL_T CheckClick( if (time0 != 0) { time1 = wGetTimer() - adjTimer; timeDelta = time1 - time0; - distDelta = FindDistance( *pos, pos0 ); - if ( timeDelta > dragTimeout || - distDelta > dragDistance ) { + distDelta = FindDistance(*pos, pos0); + if (timeDelta > dragTimeout || distDelta > dragDistance) { time0 = 0; *pos = pos0; *action = C_RDOWN; @@ -1043,66 +1159,90 @@ static BOOL_T CheckClick( return TRUE; } - -EXPORT wBool_t DoCurCommand( wAction_t action, coOrd pos ) -{ +EXPORT wBool_t DoCurCommand(wAction_t action, coOrd pos) { wAction_t rc; int mode; + wBool_t bExit = FALSE; - 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 ) { + if (action == wActionMove) { + if ((commandList[curCommand].options & IC_WANT_MOVE) == 0) { + bExit = TRUE; + } + } else if ((action&0xFF) == wActionModKey) { + if ((commandList[curCommand].options & IC_WANT_MODKEYS) == 0) { + bExit = TRUE; + } + } else if (!CheckClick(&action, &pos, + (int) (commandList[curCommand].options & IC_LCLICK), TRUE)) { + bExit = TRUE; + } else if (action == C_RCLICK + && (commandList[curCommand].options & IC_RCLICK) == 0) { + if (!inPlayback) { mode = MyGetKeyState(); - if ( ( mode & (~WKEY_SHIFT) ) != 0 ) { + if ((mode & (~WKEY_SHIFT)) != 0) { wBeep(); - return C_CONTINUE; - } - if ( ((mode&WKEY_SHIFT) == 0) == (rightClickMode==0) ) { - if ( selectedTrackCount > 0 ) { + bExit = TRUE; + } else if (((mode & WKEY_SHIFT) == 0) == (rightClickMode == 0)) { + if (selectedTrackCount > 0) { if (commandList[curCommand].options & IC_CMDMENU) { } - wMenuPopupShow( popup2M ); + wMenuPopupShow(popup2M); } else { - wMenuPopupShow( popup1M ); + wMenuPopupShow(popup1M); } - return C_CONTINUE; - } else if ( (commandList[curCommand].options & IC_CMDMENU) ) { + bExit = TRUE; + } else if ((commandList[curCommand].options & IC_CMDMENU)) { cmdMenuPos = pos; action = C_CMDMENU; } else { wBeep(); - return C_CONTINUE; + bExit = TRUE; } } else { - return C_CONTINUE; + bExit = TRUE; } } + if ( bExit ) { + TempRedraw(); // DoCurCommand: precommand + 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) ) { + 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 )) + switch ( action & 0xFF ) { + case wActionMove: + case wActionModKey: + case C_DOWN: + case C_MOVE: + case C_UP: + case C_RDOWN: + case C_RMOVE: + case C_RUP: + case C_LCLICK: + case C_RCLICK: + case C_TEXT: + case C_OK: + if (rc== C_TERMINATE) MainRedraw(); + else TempRedraw(); // DoCurCommand: postcommand + break; + default: + break; + } + if ((rc == C_TERMINATE || rc == C_INFO) + && (commandList[curCommand].options & IC_STICKY) + && (commandList[curCommand].stickyMask & stickySet)) { tempSegs_da.cnt = 0; UpdateAllElevations(); - if (action != C_REDRAW) { - MainRedraw(); - MapRedraw(); - } 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 ) { + 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: @@ -1112,7 +1252,7 @@ LOG( log_command, 4, ( " COMMAND returns %d\n", rc ) ) #endif break; case C_TERMINATE: - InfoMessage( "" ); + InfoMessage(""); case C_INFO: Reset(); break; @@ -1121,60 +1261,67 @@ LOG( log_command, 4, ( " COMMAND returns %d\n", rc ) ) return rc; } - -EXPORT void ConfirmReset( BOOL_T retry ) -{ +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 (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") ); + 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") ); + 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 ); + 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 ) { + } else if (rc == C_TERMINATE) { return; } } - Reset(); if (retry) { /* because user pressed esc */ - SetAllTrackSelect( FALSE ); + SetAllTrackSelect( FALSE); } -LOG( log_command, 1, ( "COMMAND RESET %s\n", commandList[curCommand].helpKey ) ) - commandList[curCommand].cmdProc( C_START, zero ); + Reset(); + LOG(log_command, 1, + ( "COMMAND RESET %s\n", commandList[curCommand].helpKey )) + commandList[curCommand].cmdProc( C_START, zero); } +EXPORT BOOL_T IsCurCommandSticky(void) { + if ((commandList[curCommand].options & IC_STICKY) != 0 + && (commandList[curCommand].stickyMask & stickySet) != 0) + return TRUE; + return FALSE; +} -EXPORT void ResetIfNotSticky( void ) -{ - if ( (commandList[curCommand].options & IC_STICKY) == 0 || - (commandList[curCommand].stickyMask & stickySet) == 0 ) +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; +EXPORT void DoCommandB(void * data) { + wIndex_t inx = (wIndex_t) (long) data; STATUS_T rc; - static coOrd pos = {0,0}; + static coOrd pos = { 0, 0 }; static int inDoCommandB = FALSE; wIndex_t buttInx; @@ -1183,62 +1330,74 @@ EXPORT void DoCommandB( inDoCommandB = TRUE; if (inx < 0 || inx >= commandCnt) { - ASSERT( FALSE ); + ASSERT(FALSE); inDoCommandB = FALSE; return; } - if ( (!inPlayback) && (!commandList[inx].enabled) ) { - ErrorMessage( MSG_COMMAND_DISABLED ); + 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 ) { + 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") ); + _("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 ); + 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 ); + 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 (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 ); + 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 ); + 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 ); + 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 ) { + 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 )) + TempRedraw(); // DoCommandB + switch (rc) { case C_CONTINUE: break; case C_ERROR: @@ -1250,26 +1409,21 @@ LOG( log_command, 4, ( " COMMAND returns %d\n", rc ) ) case C_TERMINATE: case C_INFO: if (rc == C_TERMINATE) - InfoMessage( "" ); + InfoMessage(""); Reset(); break; } inDoCommandB = FALSE; } - -static void DoCommandBIndirect( void * cmdInxP ) -{ +static void DoCommandBIndirect(void * cmdInxP) { wIndex_t cmdInx; - cmdInx = *(wIndex_t*)cmdInxP; - DoCommandB( (void*)(intptr_t)cmdInx ); + cmdInx = *(wIndex_t*) cmdInxP; + DoCommandB((void*) (intptr_t) cmdInx); } - -EXPORT void LayoutSetPos( - wIndex_t inx ) -{ - wPos_t w, h; +EXPORT void LayoutSetPos(wIndex_t inx) { + wPos_t w, h, offset; static wPos_t toolbarRowHeight = 0; static wPos_t width; static int lastGroup; @@ -1277,59 +1431,66 @@ EXPORT void LayoutSetPos( static int layerButtCnt; int currGroup; - if ( inx == 0 ) { + if (inx == 0) { lastGroup = 0; - wWinGetSize( mainW, &width, &h ); + wWinGetSize(mainW, &width, &h); gap = 5; - toolbarWidth = width-20+5; + toolbarWidth = width - 20 + 5; layerButtCnt = 0; toolbarHeight = 0; } if (buttonList[inx].control) { - if ( toolbarRowHeight <= 0 ) - toolbarRowHeight = wControlGetHeight( 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 (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-20) { - 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 ); + 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 (h<toolbarRowHeight) { + offset = (h-toolbarRowHeight)/2; + h = toolbarRowHeight; //Uniform + } else offset = 0; + if (inx < buttonCnt - 1 && (buttonList[inx + 1].options & IC_ABUT)) + w += wControlGetWidth(buttonList[inx + 1].control); + if (toolbarWidth + w > width - 20) { + toolbarWidth = 0; + toolbarHeight += h + 5; + } + wControlSetPos(buttonList[inx].control, toolbarWidth, + toolbarHeight - (h + 5 +offset)); + buttonList[inx].x = toolbarWidth; + buttonList[inx].y = toolbarHeight - (h + 5 + offset); + toolbarWidth += wControlGetWidth(buttonList[inx].control); + wControlShow(buttonList[inx].control, TRUE); } else { - wControlShow( buttonList[inx].control, FALSE ); + wControlShow(buttonList[inx].control, FALSE); } } } - EXPORT void LayoutToolBar( void * data ) { int inx; - for (inx = 0; inx<buttonCnt; inx++) { - LayoutSetPos( inx ); + for (inx = 0; inx < buttonCnt; inx++) { + LayoutSetPos(inx); } if (toolbarSet&(1<<BG_HOTBAR)) { LayoutHotBar(data); @@ -1338,14 +1499,12 @@ EXPORT void LayoutToolBar( void * data ) } } - -static void ToolbarChange( long changes ) -{ - if ( (changes&CHANGE_TOOLBAR) ) { +static void ToolbarChange(long changes) { + if ((changes & CHANGE_TOOLBAR)) { /*if ( !(changes&CHANGE_MAIN) )*/ - MainProc( mainW, wResize_e, NULL, NULL ); + MainProc( mainW, wResize_e, NULL, NULL ); /*else - LayoutToolBar();*/ + LayoutToolBar();*/ } } @@ -1355,26 +1514,15 @@ static void ToolbarChange( long changes ) * */ - -EXPORT BOOL_T CommandEnabled( - wIndex_t cmdInx ) -{ +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" ); +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); @@ -1391,15 +1539,12 @@ static wIndex_t AddCommand( commandList[commandCnt].menu[2] = NULL; commandList[commandCnt].menu[3] = NULL; commandCnt++; - return commandCnt-1; + return commandCnt - 1; } -EXPORT void AddToolbarControl( - wControl_p control, - long options ) -{ - if (buttonCnt >= COMMAND_MAX-1) { - AbortProg("addToolbarControl: too many buttons" ); +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; @@ -1408,98 +1553,77 @@ EXPORT void AddToolbarControl( buttonList[buttonCnt].y = 0; buttonList[buttonCnt].control = control; buttonList[buttonCnt].cmdInx = -1; - LayoutSetPos( buttonCnt ); + LayoutSetPos(buttonCnt); buttonCnt++; } - -EXPORT wButton_p AddToolbarButton( - char * helpStr, - wIcon_p icon, - long options, - wButtonCallBack_p action, - void * context ) -{ +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 ) { + 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].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 ); + 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 ) -{ +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 ); + 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,TRUE,buttonList[buttInx].control); + if (playbackTimer == 0) { + wButtonSetBusy((wButton_p) buttonList[buttInx].control, TRUE); wFlush(); - wPause( 500 ); - wButtonSetBusy( (wButton_p)buttonList[buttInx].control, FALSE ); + 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 ) -{ +EXPORT void ButtonGroupBegin(char * menuTitle, char * helpKey, + char * stickyLabel) { buttonGroupMenuTitle = menuTitle; buttonGroupHelpKey = helpKey; buttonGroupStickyLabel = stickyLabel; buttonGroupPopupM = NULL; } -EXPORT void ButtonGroupEnd( void ) -{ +EXPORT void ButtonGroupEnd(void) { buttonGroupMenuTitle = NULL; buttonGroupHelpKey = NULL; buttonGroupPopupM = NULL; } - -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 ) -{ +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; @@ -1509,225 +1633,223 @@ EXPORT wIndex_t AddMenuButton( static wMenu_p popup1Submenu; static wMenu_p popup2Submenu; - if ( icon ) { - if ( buttonGroupPopupM!=NULL ) { - buttInx = buttonCnt-2; + if (icon) { + if (buttonGroupPopupM != NULL) { + buttInx = buttonCnt - 2; } else { buttInx = buttonCnt; - AddToolbarButton( helpKey, icon, options, (wButtonCallBack_p)DoCommandB, (void*)(intptr_t)commandCnt ); + AddToolbarButton(helpKey, icon, options, + (wButtonCallBack_p) DoCommandB, + (void*) (intptr_t) commandCnt); buttonList[buttInx].cmdInx = commandCnt; } - if ( buttonGroupMenuTitle!=NULL && buttonGroupPopupM==NULL ) { - if ( openbuttIcon == NULL ) + 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 ); + 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 ); + commandsSubmenu = wMenuMenuCreate(menu, "", buttonGroupMenuTitle); + if (options & IC_POPUP2) { + popup1Submenu = wMenuMenuCreate(popup1aM, "", buttonGroupMenuTitle); + popup2Submenu = wMenuMenuCreate(popup2aM, "", buttonGroupMenuTitle); + } else if (options & IC_POPUP3) { + popup1Submenu= wMenuMenuCreate(popup1mM, "", buttonGroupMenuTitle); + popup2Submenu = wMenuMenuCreate(popup2mM, "", buttonGroupMenuTitle); + + } else { + popup1Submenu = wMenuMenuCreate(popup1M, "", buttonGroupMenuTitle); + popup2Submenu = wMenuMenuCreate(popup2M, "", buttonGroupMenuTitle); + } } } - cmdInx = AddCommand( command, helpKey, nameStr, icon, reqLevel, options, acclKey, context ); + 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" ); + if (commandList[cmdInx].options & IC_STICKY) { + if (buttonGroupPopupM == NULL || newButtonGroup) { + if (stickyCnt > 32) + AbortProg("stickyCnt>32"); stickyCnt++; } - if ( buttonGroupPopupM==NULL) { - stickyLabels[stickyCnt-1] = nameStr; + if (buttonGroupPopupM == NULL) { + stickyLabels[stickyCnt - 1] = nameStr; } else { - stickyLabels[stickyCnt-1] = buttonGroupStickyLabel; + stickyLabels[stickyCnt - 1] = buttonGroupStickyLabel; } stickyLabels[stickyCnt] = NULL; - commandList[cmdInx].stickyMask = 1L<<(stickyCnt-1); + long stickyMask = 1L<<(stickyCnt-1); + commandList[cmdInx].stickyMask = stickyMask; + if ( ( commandList[cmdInx].options & IC_INITNOTSTICKY ) == 0 ) + stickySet |= stickyMask; } - if ( buttonGroupPopupM ) { - commandList[cmdInx].menu[0] = - wMenuPushCreate( buttonGroupPopupM, helpKey, GetBalloonHelpStr(helpKey), 0, DoCommandB, (void*)(intptr_t)cmdInx ); + 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 ); + p1m = (options & IC_POPUP2) ? popup1aM : (options & IC_POPUP3) ? popup1mM : popup1M; + p2m = (options & IC_POPUP2) ? popup2aM : (options & IC_POPUP3) ? popup2mM : popup2M; + } + commandList[cmdInx].menu[1] = wMenuPushCreate(tm, helpKey, nameStr, acclKey, + DoCommandB, (void*) (intptr_t) cmdInx); + if ((options & (IC_POPUP | IC_POPUP2 | IC_POPUP3))) { + 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 ); + 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 ) -{ +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 ); + 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 ) -{ +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) { + 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 ); + 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 ((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,TRUE,buttonList[buttInx].control); } - if (strcmp( line+8, "Undo") == 0) { - if (buttInx>0 && playbackTimer == 0) { - wButtonSetBusy( (wButton_p)buttonList[buttInx].control, TRUE ); + 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 ); + 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 ); + } 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 ); + wPause(500); + wButtonSetBusy((wButton_p) buttonList[buttInx].control, FALSE); wFlush(); } UndoRedo(); } else { - if ( buttInx>=0 && - playbackTimer == 0 ) { - wButtonSetBusy( (wButton_p)buttonList[buttInx].control, TRUE ); + if (buttInx >= 0 && playbackTimer == 0) { + wButtonSetBusy((wButton_p) buttonList[buttInx].control, TRUE); wFlush(); - wPause( 500 ); - wButtonSetBusy( (wButton_p)buttonList[buttInx].control, FALSE ); + wPause(500); + wButtonSetBusy((wButton_p) buttonList[buttInx].control, FALSE); wFlush(); } - DoCommandB( (void*)(intptr_t)inx ); + DoCommandB((void*) (intptr_t) inx); } } } - /*--------------------------------------------------------------------*/ typedef struct { - char * label; - wMenu_p menu; - } menuTrace_t, *menuTrace_p; + 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 ) -{ +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 ); + 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 ) -{ +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 ); + 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 ); + wMenuSetTraceCallBack(m, DoMenuTrace, mt->label); return m; } - -void MenuPlayback( char * line ) -{ - char * menuName, * itemName; +void MenuPlayback(char * line) { + char * menuName, *itemName; coOrd pos; wPos_t x, y; menuTrace_p mt; - if (!GetArgs( line, "pqq", &pos, &menuName, &itemName )) + 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 ); + 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, FALSE, NULL); oldMarker = cmdMenuPos = pos; - wMenuAction( mt->menu, _(itemName) ); + wMenuAction(mt->menu, _(itemName)); return; } } - AbortProg( "menuPlayback: %s not found", menuName ); + 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 *); +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 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 ); +static void DoSticky(void) { + if (!stickyW) + stickyW = ParamCreateDialog(&stickyPG, + MakeWindowTitle(_("Sticky Commands")), _("Ok"), StickyOk, wHide, + TRUE, NULL, 0, NULL); + ParamLoadControls(&stickyPG); + wShow(stickyW); } /*--------------------------------------------------------------------*/ @@ -1737,43 +1859,21 @@ static void DoSticky( void ) * 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"), - N_("Layout Control Elements"), - N_("Modify Track Buttons"), - N_("Properties/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, - 1<<BG_CONTROL, - 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; +static char *AllToolbarLabels[] = { N_("File Buttons"), N_("Import/Export Buttons"), N_("Zoom Buttons"), N_( + "Undo Buttons"), N_("Easement Button"), N_("SnapGrid Buttons"), N_( + "Create Track Buttons"), N_("Layout Control Elements"), N_( + "Modify Track Buttons"), N_("Properties/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_EXPORTIMPORT, 1 << BG_ZOOM, 1 << BG_UNDO, 1 + << BG_EASE, 1 << BG_SNAP, 1 << BG_TRKCRT, 1 << BG_CONTROL, 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 @@ -1781,7 +1881,8 @@ static void ToolbarAction( wBool_t set, void * data ) wPrefSetInteger( "misc", "toolbarset", toolbarSet ); MainProc( mainW, wResize_e, NULL, NULL ); if (recordF) - fprintf( recordF, "PARAMETER %s %s %ld", "misc", "toolbarset", toolbarSet ); + fprintf(recordF, "PARAMETER %s %s %ld", "misc", "toolbarset", + toolbarSet); } /** @@ -1791,19 +1892,19 @@ static void ToolbarAction( wBool_t set, void * data ) * \param toolbarM IN menu to which the toogles will be added */ -static void CreateToolbarM( wMenu_p toolbarM ) -{ +static void CreateToolbarM(wMenu_p toolbarM) { int inx, cnt; long *masks; char **labels; wBool_t set; - cnt = sizeof(AllToolbarMasks)/sizeof(AllToolbarMasks[0]); + 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 ); + for (inx = 0; inx < cnt; inx++, masks++, labels++) { + set = (toolbarSet & *masks) != 0; + wMenuToggleCreate(toolbarM, "toolbarM", _(*labels), 0, set, + ToolbarAction, (void*) *masks); } } @@ -1812,157 +1913,148 @@ static void CreateToolbarM( wMenu_p toolbarM ) static wWin_p addElevW; #define addElevF (wFloat_p)addElevPD.control EXPORT DIST_T addElevValueV; -static void DoAddElev( void * ); +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 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 DoAddElev(void * junk) { + ParamLoadData(&addElevPG); + AddElevations(addElevValueV); + wHide(addElevW); } - -static void ShowAddElevations( void ) -{ - if ( selectedTrackCount <= 0 ) { - ErrorMessage( MSG_NO_SELECTED_TRK ); +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 ); + addElevW = ParamCreateDialog(&addElevPG, + MakeWindowTitle(_("Change Elevations")), _("Change"), DoAddElev, + wHide, FALSE, NULL, 0, NULL); + wShow(addElevW); } /*--------------------------------------------------------------------*/ static wWin_p rotateW; static wWin_p moveW; -static long rotateValue; +static double rotateValue; static coOrd moveValue; static rotateDialogCallBack_t rotateDialogCallBack; static moveDialogCallBack_t moveDialogCallBack; -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] }; - -static paramFloatRange_t r_1000_1000 = { -1000.0, 1000.0, 80 }; -static void MoveEnterOk( void * ); -static paramData_t movePLs[] = { - { PD_FLOAT, &moveValue.x, "moveX", PDO_DIM, &r_1000_1000, N_("Move X:") }, - { PD_FLOAT, &moveValue.y, "moveY", PDO_DIM, &r_1000_1000, N_("Move Y:") } }; -static paramGroup_t movePG = { "move", 0, movePLs, sizeof movePLs/sizeof movePLs[0] }; - - -EXPORT void StartRotateDialog( rotateDialogCallBack_t func ) -{ - if ( rotateW == NULL ) - rotateW = ParamCreateDialog( &rotatePG, MakeWindowTitle(_("Rotate")), _("Ok"), RotateEnterOk, wHide, FALSE, NULL, 0, NULL ); - ParamLoadControls( &rotatePG ); +static void RotateEnterOk(void *); + +static paramFloatRange_t rn360_360 = { -360.0, 360.0, 80.0 }; +static paramData_t rotatePLs[] = { { PD_FLOAT, &rotateValue, "rotate", PDO_ANGLE, + &rn360_360, N_("Angle:") } }; +static paramGroup_t rotatePG = { "rotate", 0, rotatePLs, sizeof rotatePLs + / sizeof rotatePLs[0] }; + +static paramFloatRange_t r_1000_1000 = { -1000.0, 1000.0, 80 }; +static void MoveEnterOk(void *); +static paramData_t movePLs[] = { { PD_FLOAT, &moveValue.x, "moveX", PDO_DIM, + &r_1000_1000, N_("Move X:") }, { PD_FLOAT, &moveValue.y, "moveY", + PDO_DIM, &r_1000_1000, N_("Move Y:") } }; +static paramGroup_t movePG = { "move", 0, movePLs, sizeof movePLs + / sizeof movePLs[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 ); + wShow(rotateW); } -EXPORT void StartMoveDialog( moveDialogCallBack_t func ) -{ - if ( moveW == NULL ) - moveW = ParamCreateDialog( &movePG, MakeWindowTitle(_("Move")), _("Ok"), MoveEnterOk, wHide, FALSE, NULL, 0, NULL ); - ParamLoadControls( &movePG ); +EXPORT void StartMoveDialog(moveDialogCallBack_t func) { + if (moveW == NULL) + moveW = ParamCreateDialog(&movePG, MakeWindowTitle(_("Move")), _("Ok"), + MoveEnterOk, wHide, FALSE, NULL, 0, NULL); + ParamLoadControls(&movePG); moveDialogCallBack = func; moveValue = zero; - wShow( moveW ); + wShow(moveW); } -static void MoveEnterOk( void * junk ) -{ - ParamLoadData( &movePG ); - moveDialogCallBack( (void*) &moveValue ); - wHide( moveW ); +static void MoveEnterOk(void * junk) { + ParamLoadData(&movePG); + moveDialogCallBack((void*) &moveValue); + wHide(moveW); } -static void RotateEnterOk( void * junk ) -{ - ParamLoadData( &rotatePG ); - if (angleSystem==ANGLE_POLAR) - rotateDialogCallBack( (void*)rotateValue ); +static void RotateEnterOk(void * junk) { + ParamLoadData(&rotatePG); + if (angleSystem == ANGLE_POLAR) + rotateDialogCallBack((void*) (long)(rotateValue*1000)); else - rotateDialogCallBack( (void*)-rotateValue ); - wHide( rotateW ); + rotateDialogCallBack((void*) (long)(-rotateValue*1000)); + wHide(rotateW); } - -static void RotateDialogInit( void ) -{ - ParamRegister( &rotatePG ); +static void RotateDialogInit(void) { + ParamRegister(&rotatePG); } -static void MoveDialogInit (void) -{ - ParamRegister( &movePG ); +static void MoveDialogInit(void) { + ParamRegister(&movePG); } - -EXPORT void AddMoveMenu( - wMenu_p m, - moveDialogCallBack_t func ) { - wMenuPushCreate( m, "", _("Enter Move ..."), 0, (wMenuCallBack_p)StartMoveDialog, (void*)func ); +EXPORT void AddMoveMenu(wMenu_p m, moveDialogCallBack_t func) { + wMenuPushCreate(m, "", _("Enter Move ..."), 0, + (wMenuCallBack_p) StartMoveDialog, (void*) func); } -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 ); +//All values multipled by 100 to support decimal points from PD_FLOAT +EXPORT void AddRotateMenu(wMenu_p m, rotateDialogCallBack_t func) { + wMenuPushCreate(m, "", _("180 "), 0, func, (void*) 180000); + wMenuPushCreate(m, "", _("90 CW"), 0, func, (void*) (long) (90000)); + wMenuPushCreate(m, "", _("45 CW"), 0, func, (void*) (long) (45000)); + wMenuPushCreate(m, "", _("30 CW"), 0, func, (void*) (long) (30000)); + wMenuPushCreate(m, "", _("15 CW"), 0, func, (void*) (long) (15000)); + wMenuPushCreate(m, "", _("15 CCW"), 0, func, (void*) (long) (360000 - 15000)); + wMenuPushCreate(m, "", _("30 CCW"), 0, func, (void*) (long) (360000 - 30000)); + wMenuPushCreate(m, "", _("45 CCW"), 0, func, (void*) (long) (360000 - 45000)); + wMenuPushCreate(m, "", _("90 CCW"), 0, func, (void*) (long) (360000 - 90000)); + 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 void DebugOk(void * junk); static paramData_t debugPLs[30]; static long debug_values[30]; static int debug_index[30]; + static paramGroup_t debugPG = { "debug", 0, debugPLs, 0 }; -static void DebugOk( void * junk ) -{ +static void DebugOk(void * junk) { for (int i = 0; i<debugCnt;i++) { - logTable(debug_index[i]).level = debug_values[i]; + logTable(debug_index[i]).level = debug_values[i]; } - wHide( debugW ); + wHide(debugW); } -static void CreateDebugW( void ) -{ +static void CreateDebugW(void) { debugPG.paramCnt = debugCnt; - ParamRegister( &debugPG ); - debugW = ParamCreateDialog( &debugPG, MakeWindowTitle(_("Debug")), _("Ok"), DebugOk, NULL, FALSE, NULL, 0, NULL ); + ParamRegister(&debugPG); + debugW = ParamCreateDialog(&debugPG, MakeWindowTitle(_("Debug")), _("Ok"), + DebugOk, wHide, FALSE, NULL, 0, NULL); wHide(debugW); } @@ -1995,108 +2087,77 @@ EXPORT void DebugInit(void) { wShow(debugW); } -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] ); + +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); -void RecomputeElevations( void ); - -static void MiscMenuItemCreate( - wMenu_p m1, - wMenu_p m2, - char * name, - char * label, - long acclKey, - void * func, - long option, - void * context ) -{ +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; + 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 ); + 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", "NumpadAdd", "NumpadSub" }; -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", - "NumpadAdd", - "NumpadSub"}; - -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 ); +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" @@ -2105,130 +2166,231 @@ static void SetAccelKey( #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-export.xpm" +#include "bitmaps/document-exportdxf.xpm" +#include "bitmaps/document-import.xpm" +#include "bitmaps/document-importmod.xpm" #include "bitmaps/document-new.xpm" #include "bitmaps/document-save.xpm" #include "bitmaps/document-open.xpm" #include "bitmaps/document-print.xpm" #include "bitmaps/map.xpm" +#include "bitmaps/magnet.xpm" -static void CreateMenus( void ) -{ - wMenu_p fileM, editM, viewM, optionM, windowM, macroM, helpM, toolbarM, messageListM, manageM, addM, changeM, drawM; +static void CreateMenus(void) { + wMenu_p fileM, editM, viewM, optionM, windowM, macroM, helpM, toolbarM, + messageListM, manageM, addM, changeM, drawM; wMenu_p zoomM, zoomSubM; 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") ); + 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 ); - MiscMenuItemCreate( popup1M, popup2M, "cmdMapShow", _("Show/Hide Map"), 0, (void*)(wMenuCallBack_p)MapWindowToggleShow, 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( popup1M, popup2M, "cmdSelectAll", _("Select All"), 0, (void*)(wMenuCallBack_p)SetAllTrackSelect, 0, (void *)1 ); - MiscMenuItemCreate( popup1M, popup2M, "cmdSelectCurrentLayer", _("Select Current Layer"), 0, (void*)(wMenuCallBack_p)SelectCurrentLayer, 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") ); + /* Select Commands */ + /* Select All */ + /* Select All Current */ + + /* Common View Commands Menu */ + /* Zoom In/Out */ + /* Snap Grid Menu */ + /* Show/Hide Map */ + /* Show/Hide Background */ + + /* Selected Commands */ + /*--------------*/ + /* DeSelect All */ + /* Select All */ + /* Select All Current */ + /*--------------*/ + /* Quick Move */ + /* Quick Rotate */ + /* Quick Align */ + /*--------------*/ + /* Move To Current Layer */ + /* Move/Rotate Cmds */ + /* Cut/Paste/Delete */ + /* Group/Un-group Selected */ + /*----------*/ + /* Thick/Thin */ + /* Bridge/Tunnel */ + /* Ties/NoTies */ + /*-----------*/ + /* More Commands */ + + popup1M = wMenuPopupCreate(mainW, _("Context Commands")); + popup2M = wMenuPopupCreate(mainW, _("Shift Context 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); + /* Zoom */ + 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); + /* Display */ + MiscMenuItemCreate(popup1M, popup2M, "cmdGridEnable", _("Enable SnapGrid"), + 0, (void*) (wMenuCallBack_p) SnapGridEnable, 0, (void *) 0); + MiscMenuItemCreate(popup1M, popup2M, "cmdGridShow", _("SnapGrid Show"), 0, + (void*) (wMenuCallBack_p) SnapGridShow, 0, (void *) 0); + MiscMenuItemCreate(popup1M, popup2M, "cmdMagneticSnap", _(" Enable Magnetic Snap"), 0, + (void*) (wMenuCallBack_p) MagneticSnapToggle, 0, (void *) 0); + MiscMenuItemCreate(popup1M, popup2M, "cmdMapShow", _("Show/Hide Map"), 0, + (void*) (wMenuCallBack_p) MapWindowToggleShow, 0, (void *) 0); + MiscMenuItemCreate(popup1M, popup2M, "cmdBackgroundShow", _("Show/Hide Background"), 0, + (void*) (wMenuCallBack_p) BackgroundToggleShow, 0, (void *) 0); + wMenuSeparatorCreate(popup1M); + wMenuSeparatorCreate(popup2M); + /* Copy/Paste */ + MiscMenuItemCreate(popup2M, NULL, "cmdCut", _("Cut"), 0, + (void*) (wMenuCallBack_p) EditCut, 0, (void *) 0); + 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, "cmdClone", _("Clone"), 0, + (void*) (wMenuCallBack_p) EditClone, 0, (void *) 0); + /*Select*/ + MiscMenuItemCreate(popup1M, popup2M, "cmdSelectAll", _("Select All"), 0, + (void*) (wMenuCallBack_p) SetAllTrackSelect, 0, (void *) 1); + MiscMenuItemCreate(popup1M, popup2M, "cmdSelectCurrentLayer", + _("Select Current Layer"), 0, + (void*) (wMenuCallBack_p) SelectCurrentLayer, 0, (void *) 0); + MiscMenuItemCreate(popup2M, NULL, "cmdDeselectAll", _("Deselect All"), 0, + (void*) (wMenuCallBack_p) SetAllTrackSelect, 0, (void *) 0); + /* Modify */ + wMenuPushCreate(popup2M, "cmdMove", _("Move"), 0, + (wMenuCallBack_p) DoCommandBIndirect, &moveCmdInx); + wMenuPushCreate(popup2M, "cmdRotate", _("Rotate"), 0, + (wMenuCallBack_p) DoCommandBIndirect, &rotateCmdInx); + wMenuSeparatorCreate(popup1M); + wMenuSeparatorCreate(popup2M); + MiscMenuItemCreate(popup2M, NULL, "cmdDelete", _("Delete"), 0, + (void*) (wMenuCallBack_p) SelectDelete, 0, (void *) 0); + wMenuSeparatorCreate(popup2M); + popup1aM = wMenuMenuCreate(popup1M, "", _("Add...")); + popup2aM = wMenuMenuCreate(popup2M, "", _("Add...")); + wMenuSeparatorCreate(popup2M); + wMenuSeparatorCreate(popup1M); + popup1mM = wMenuMenuCreate(popup1M, "", _("More...")); + popup2mM = 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-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); + + InitCmdExport(); cmdGroup = BG_ZOOM; - zoomUpB = AddToolbarButton( "cmdZoomIn", wIconCreatePixMap(zoomin_xpm), IC_MODETRAIN_TOO, - (addButtonCallBack_t)DoZoomUp, NULL ); + 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 ); + 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 ); + 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 ); + 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 ); + 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, "cmdImportModule", _("Import &Module"), ACCL_IMPORT_MOD, + (void*) (wMenuCallBack_p) DoImport, 0, (void *) 1); + 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 ); - - + 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, "cmdClone", _("C&lone"), ACCL_CLONE, + (void*) (wMenuCallBack_p) EditClone, 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 ); @@ -2238,8 +2400,10 @@ static void CreateMenus( void ) 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 ); + MiscMenuItemCreate( editM, NULL, "cmdBridge", _("B&ridge"), ACCL_BRIDGE, (void*)(wMenuCallBack_p)SelectBridge, IC_SELECTED, (void *)0); + MiscMenuItemCreate( editM, NULL, "cmdTies", _("Ties/NoTies"), ACCL_TIES, (void*)(wMenuCallBack_p)SelectTies, IC_SELECTED, (void *)0); + MiscMenuItemCreate( editM, NULL, "cmdAbove", _("Move to &Front"), ACCL_ABOVE, (void*)(wMenuCallBack_p)SelectAbove, IC_SELECTED, (void *)0 ); + MiscMenuItemCreate( editM, NULL, "cmdBelow", _("Move to &Back"), 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 ); @@ -2249,199 +2413,273 @@ static void CreateMenus( void ) /* * 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 ); + 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 ); + InitCmdZoom(zoomM, zoomSubM, NULL, NULL); /* 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 ); + 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); + + // visibility toggle for anchors + // get the start value + long anchors_long; + wPrefGetInteger("misc", "anchors", (long *)&anchors_long, 1); + magneticSnap = anchors_long ? TRUE : FALSE; + magnetsMI = wMenuToggleCreate(viewM, "cmdMagneticSnap", _("Enable Magnetic Snap"), + 0, magneticSnap, + (wMenuToggleCallBack_p)MagneticSnapToggle, NULL); // visibility toggle for map window // get the start value long mapVisible_long; - wPrefGetInteger( "misc", "mapVisible", (long *)&mapVisible_long, 1 ); - mapVisible = mapVisible_long?TRUE:FALSE; - mapShowMI = wMenuToggleCreate( viewM, "cmdMapShow", _("Show/Hide Map"), ACCL_MAPSHOW, - mapVisible, (wMenuToggleCallBack_p)MapWindowToggleShow, NULL ); + wPrefGetInteger("misc", "mapVisible", (long *) &mapVisible_long, 1); + mapVisible = mapVisible_long ? TRUE : FALSE; + mapShowMI = wMenuToggleCreate(viewM, "cmdMapShow", _("Show/Hide Map"), + ACCL_MAPSHOW, mapVisible, + (wMenuToggleCallBack_p) MapWindowToggleShow, NULL); - wMenuSeparatorCreate( viewM ); + wMenuSeparatorCreate(viewM); - toolbarM = wMenuMenuCreate( viewM, "toolbarM", _("&Tool Bar") ); - CreateToolbarM( toolbarM ); + toolbarM = wMenuMenuCreate(viewM, "toolbarM", _("&Tool Bar")); + CreateToolbarM(toolbarM); - cmdGroup = BG_EASE; + cmdGroup = BG_EASE; InitCmdEasement(); cmdGroup = BG_SNAP; InitSnapGridButtons(); - mapShowB = AddToolbarButton("cmdMapShow", wIconCreatePixMap(map_xpm), IC_MODETRAIN_TOO, - (addButtonCallBack_t)MapWindowToggleShow, NULL); - wControlLinkedSet((wControl_p)mapShowMI, (wControl_p)mapShowB); - wButtonSetBusy(mapShowB, (wBool_t)mapVisible); + magnetsB = AddToolbarButton("cmdMagneticSnap", wIconCreatePixMap(magnet_xpm), + IC_MODETRAIN_TOO, (addButtonCallBack_t) MagneticSnapToggle, NULL); + wControlLinkedSet((wControl_p) magnetsMI, (wControl_p) magnetsB); + wButtonSetBusy(magnetsB, (wBool_t) magneticSnap); + + mapShowB = AddToolbarButton("cmdMapShow", wIconCreatePixMap(map_xpm), + IC_MODETRAIN_TOO, (addButtonCallBack_t) MapWindowToggleShow, NULL); + wControlLinkedSet((wControl_p) mapShowMI, (wControl_p) mapShowB); + wButtonSetBusy(mapShowB, (wBool_t) mapVisible); /* * ADD MENU */ - cmdGroup = BG_TRKCRT|BG_BIGGAP; - InitCmdStraight( addM ); - InitCmdCurve( addM ); - InitCmdParallel( addM ); - InitCmdTurnout( addM ); - InitCmdHandLaidTurnout( addM ); - InitCmdStruct( addM ); - InitCmdHelix( addM ); - InitCmdTurntable( addM ); + cmdGroup = BG_TRKCRT | BG_BIGGAP; + InitCmdStraight(addM); + InitCmdCurve(addM); + InitCmdParallel(addM); + InitCmdTurnout(addM); + InitCmdHandLaidTurnout(addM); + InitCmdStruct(addM); + InitCmdHelix(addM); + InitCmdTurntable(addM); cmdGroup = BG_CONTROL; - InitCmdBlock( addM ); - InitCmdSwitchMotor( addM ); - InitCmdSignal( addM ); - InitCmdControl( addM ); - InitCmdSensor( addM ); - + ButtonGroupBegin( _("Control Element"), "cmdControlElements", _("Control Element") ); + InitCmdBlock(addM); + InitCmdSwitchMotor(addM); + InitCmdSignal(addM); + InitCmdControl(addM); + InitCmdSensor(addM); + ButtonGroupEnd(); + /* * CHANGE MENU */ cmdGroup = BG_SELECT; - InitCmdDescribe( changeM ); - InitCmdSelect( changeM ); - InitCmdPan( changeM ); - wMenuSeparatorCreate( changeM ); + InitCmdDescribe(changeM); + InitCmdSelect(changeM); + InitCmdPan(changeM); + wMenuSeparatorCreate(changeM); cmdGroup = BG_TRKGRP; - InitCmdMove( changeM ); + InitCmdMove(changeM); InitCmdDelete(); InitCmdTunnel(); + InitCmdBridge(); 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 ); + 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); + + wMenuSeparatorCreate(changeM); + + InitCmdCornu(changeM); /* * DRAW MENU */ cmdGroup = BG_MISCCRT; - InitCmdDraw( drawM ); - InitCmdText( drawM ); - InitCmdNote( drawM ); + InitCmdDraw(drawM); + InitCmdText(drawM); + InitTrkNote(drawM); cmdGroup = BG_RULER; - InitCmdRuler( drawM ); - + 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 ); + 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)DebugInit, IC_MODETRAIN_TOO, (void *)0 ); + MiscMenuItemCreate(optionM, NULL, "cmdDebug", _("&Debug ..."), 0, + (void*) (wMenuCallBack_p) DebugInit, 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 ); + 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 ); - + 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 ); + wMenuPushCreate(windowM, "menuWindow", _("Main window"), 0, + (wMenuCallBack_p) wShow, mainW); + winList_mi = wMenuListCreate(windowM, "menuWindow", -1, DoShowWindow); /* * HELP MENU */ /* main help window */ - wMenuAddHelp( helpM ); + 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 ); + 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") ); + wMenuPushCreate( helpM, "cmdExamples", _("Examples..."), 0, (wMenuCallBack_p)ChkExamples, (void *)0); /* about window */ - wMenuSeparatorCreate( helpM ); - wMenuPushCreate( helpM, "about", _("About"), 0, (wMenuCallBack_p)CreateAboutW, NULL ); + wMenuSeparatorCreate(helpM); + wMenuPushCreate(helpM, "about", _("About"), 0, + (wMenuCallBack_p) CreateAboutW, NULL); /* * MANAGE MENU */ - cmdGroup = BG_TRAIN|BG_BIGGAP; - InitCmdTrain( manageM ); - wMenuSeparatorCreate( manageM ); + cmdGroup = BG_TRAIN | BG_BIGGAP; + InitCmdTrain(manageM); + wMenuSeparatorCreate(manageM); + + InitNewTurn( + wMenuMenuCreate(manageM, "cmdTurnoutNew", + _("Tur&nout Designer..."))); - InitNewTurn( wMenuMenuCreate( manageM, "cmdTurnoutNew", _("Tur&nout Designer...") ) ); + MiscMenuItemCreate(manageM, NULL, "cmdContmgm", + _("Layout &Control Elements"), ACCL_CONTMGM, + (void*) ControlMgrInit(), 0, (void*) 0); + 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, "cmdContmgm", _("Layout &Control Elements"), ACCL_CONTMGM,(void*)ControlMgrInit(),0,(void*) 0); - 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, "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); - MiscMenuItemCreate( manageM, NULL, "cmdCarInventory", _("Car Inventory"), ACCL_CARINV, (void*)(wMenuCallBack_p)DoCarDlg, IC_MODETRAIN_TOO, (void *)0 ); + wMenuSeparatorCreate(manageM); - wMenuSeparatorCreate( manageM ); + MiscMenuItemCreate(manageM, NULL, "cmdLayer", _("Layers ..."), ACCL_LAYERS, + (void*) InitLayersDialog(), 0, (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); - 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; + + InitCmdSelect2(changeM); + InitCmdDescribe2(changeM); + InitCmdPan2(changeM); - cmdGroup = BG_LAYER|BG_BIGGAP; InitLayers(); cmdGroup = BG_HOTBAR; @@ -2459,51 +2697,73 @@ static void CreateMenus( void ) 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 ); - SetAccelKey( "zoomUp", wAccelKey_Numpad_Add, WKEY_CTRL, (wAccelKeyCallBack_p)DoZoomUp, (void*)1 ); - SetAccelKey( "zoomDown", wAccelKey_Numpad_Subtract, WKEY_CTRL, (wAccelKeyCallBack_p)DoZoomDown, (void*)1 ); + 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); + SetAccelKey("zoomUp", wAccelKey_Numpad_Add, WKEY_CTRL, + (wAccelKeyCallBack_p) DoZoomUp, (void*) 1); + SetAccelKey("zoomDown", wAccelKey_Numpad_Subtract, WKEY_CTRL, + (wAccelKeyCallBack_p) DoZoomDown, (void*) 1); + SetAccelKey("help", wAccelKey_F1, WKEY_SHIFT, + (wAccelKeyCallBack_p) wDoAccelHelp, (void*) 1); + SetAccelKey("help-context", wAccelKey_F1, 0, + (wAccelKeyCallBack_p) wDoAccelHelp, (void*) 3); InitBenchDialog(); + wPrefGetInteger( "DialogItem", "sticky-set", &stickySet, stickySet ); } - -static void LoadFileList( void ) -{ +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 ); + 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 = FindFilename((char *)pathName); + fileName = FindFilename((char *) pathName); if (fileName) - wMenuListAdd( fileList_ml, 0, fileName, pathName ); + wMenuListAdd(fileList_ml, 0, fileName, pathName); } } -EXPORT void InitCmdEnumerate( void ) -{ - AddToolbarButton( "cmdEnumerate", wIconCreatePixMap(partlist_xpm), IC_SELECTED|IC_ACCLKEY, (addButtonCallBack_t)EnumerateTracks, NULL ); +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 ); +EXPORT void InitCmdExport(void) { + ButtonGroupBegin( _("Import/Export"), "cmdExportImportSetCmd", _("Import/Export") ); + cmdGroup = BG_EXPORTIMPORT; + AddToolbarButton("cmdExport", wIconCreatePixMap(export_xpm), + IC_SELECTED | IC_ACCLKEY, (addButtonCallBack_t) DoExport, NULL); + AddToolbarButton("cmdExportDXF", wIconCreatePixMap(export_dxf_xpm), IC_SELECTED | IC_ACCLKEY, + (addButtonCallBack_t)DoExportDXF, (void*)1); + AddToolbarButton("cmdImport", wIconCreatePixMap(import_xpm), IC_ACCLKEY, + (addButtonCallBack_t) DoImport, (void*)0); + AddToolbarButton("cmdImportModule", wIconCreatePixMap(importmod_xpm), IC_ACCLKEY, + (addButtonCallBack_t) DoImport, (void*)1); + ButtonGroupEnd(); } /* Give user the option to continue work after crash. This function gives the user @@ -2514,27 +2774,34 @@ EXPORT void InitCmdExport( void ) * */ + static int OfferCheckpoint( void ) { int ret = FALSE; /* sProdName */ - ret = wNoticeEx( NT_INFORMATION, - _("Program was not terminated properly. Do you want to resume working on the previous trackplan?"), - _("Resume"), _("Ignore") ); - if( ret ) { + ret = + wNotice3( + _( + "Program was not terminated properly. Do you want to resume working on the previous trackplan?"), + _("Resume"), _("Resume with New Name"), _("Ignore Checkpoint")); + //ret==1 Same, ret==-1 New, ret==0 Ignore + if (ret == 1) + printf(_("Reload Checkpoint Selected\n")); + else if (ret == -1) + printf(_("Reload Checkpoint With New Name Selected\n")); + else + printf(_("Ignore Checkpoint Selected\n")); + if (ret>=0) { /* load the checkpoint file */ - LoadCheckpoint(); + LoadCheckpoint(ret==1); ret = TRUE; + } - return ret; + return (ret>=0); } - -EXPORT wWin_p wMain( - int argc, - char * argv[] ) -{ +EXPORT wWin_p wMain(int argc, char * argv[]) { int c; int resumeWork; char * logFileName = NULL; @@ -2547,12 +2814,12 @@ EXPORT wWin_p wMain( long newToolbarMax; char *cp; char *oldLocale = NULL; - char buffer[ STR_SIZE ]; + char buffer[STR_SIZE]; unsigned int i; - wPos_t displayWidth; - wPos_t displayHeight; + wPos_t displayWidth; + wPos_t displayHeight; - strcpy( buffer, sProdNameLower ); + strcpy(buffer, sProdNameLower); /* Initialize application name */ wInitAppName(buffer); @@ -2563,7 +2830,7 @@ EXPORT wWin_p wMain( /* Save user locale */ oldLocale = setlocale(LC_ALL, NULL); if (oldLocale) - userLocale = strdup( oldLocale ); + userLocale = strdup(oldLocale); /* * ARGUMENTS @@ -2571,89 +2838,107 @@ EXPORT wWin_p wMain( opterr = 0; - while ((c = getopt (argc, argv, "vl:d:c:")) != -1) - switch (c) { - case 'c': /* configuration name */ + while ((c = getopt(argc, argv, "vl:d:c:m")) != -1) + switch (c) { + case 'c': /* configuration name */ /* test for valid filename */ - for( i = 0; i < strlen( optarg ); i++ ) { - if( !isalnum( (unsigned char)optarg[ i ]) && optarg[ i ] != '.' ) { - NoticeMessage( MSG_BAD_OPTION, _("Ok"), NULL, optarg ); - exit( 1 ); + for (i = 0; i < strlen(optarg); i++) { + if (!isalnum((unsigned char) 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 ); + 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 */ + case 'v': /* verbose flag */ verbose++; - break; - case 'd': /* define loglevel for a group */ - cp = strchr( optarg, '=' ); - if ( cp != NULL ) { + break; + case 'd': /* define loglevel for a group */ + cp = strchr(optarg, '='); + if (cp != NULL) { *cp++ = '\0'; - LogSet( optarg, atoi(cp) ); + LogSet(optarg, atoi(cp)); } else { - LogSet( optarg, 1 ); + LogSet(optarg, 1); } break; - case 'l': /* define log filename */ + case 'l': /* define log filename */ logFileName = strdup(optarg); break; - case '?': - NoticeMessage( MSG_BAD_OPTION, _("Ok"), NULL, argv[ optind - 1 ] ); - exit( 1 ); + case '?': + NoticeMessage(MSG_BAD_OPTION, _("Ok"), NULL, argv[optind - 1]); + exit(1); + case 'm': // temporary: use MainRedraw instead of TempRedraw + wDrawDoTempDraw = FALSE; + break; case ':': - NoticeMessage( "Missing parameter for %s", _("Ok"), NULL, argv[ optind - 1 ] ); - exit( 1 ); + NoticeMessage("Missing parameter for %s", _("Ok"), NULL, + argv[optind - 1]); + exit(1); break; - default: - abort (); - } - if( optind < argc ) - initialFile = strdup( argv[ optind ] ); + 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" ); + 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" ) ) + LOG1(log_init, ( "initCustom\n" )) InitCustom(); /* * MAIN WINDOW */ -LOG1( log_init, ( "create main window\n" ) ) - SetLayoutTitle( sProdName ); - sprintf( message, _("Unnamed Trackplan - %s(%s)"), sProdName, sVersion ); - wSetBalloonHelp( balloonHelp ); - - wGetDisplaySize(&displayWidth, &displayHeight); - mainW = wWinMainCreate( buffer, (displayWidth*2)/3, (displayHeight*2)/3, "xtrkcadW", message, "main", - F_RESIZE|F_MENUBAR|F_NOTAB|F_RECALLPOS|F_HIDE, - MainProc, NULL ); - if ( mainW == NULL ) + LOG1(log_init, ( "create main window\n" )) + SetLayoutTitle(sProdName); + sprintf(message, _("Unnamed Trackplan - %s(%s)"), sProdName, sVersion); + wSetBalloonHelp(balloonHelp); + + wGetDisplaySize(&displayWidth, &displayHeight); + mainW = wWinMainCreate(buffer, (displayWidth * 2) / 3, + (displayHeight * 2) / 3, "xtrkcadW", message, "main", + F_RESIZE | F_MENUBAR | F_NOTAB | F_RECALLPOS | F_RECALLSIZE | F_HIDE, MainProc, + NULL); + if (mainW == NULL) return NULL; InitAppDefaults(); - 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) ); + + // Last component of spectial color must be > 3 + drawColorPreviewSelected = wDrawFindColor( wRGB ( 6, 6, 255) ); //Special Blue + drawColorPreviewUnselected = wDrawFindColor( wRGB( 255, 215, 6)); //Special Yellow + + drawColorPowderedBlue = wDrawFindColor( wRGB(129, 212, 250) ); drawColorPurple = wDrawFindColor( wRGB(255, 0,255) ); drawColorGold = wDrawFindColor( wRGB(255,215, 0) ); + drawColorGrey10 = wDrawFindColor( wRGB(26,26,26) ); + drawColorGrey20 = wDrawFindColor( wRGB(51,51,51) ); + drawColorGrey30 = wDrawFindColor( wRGB(72,72,72) ); + drawColorGrey40 = wDrawFindColor( wRGB(102,102,102) ); + drawColorGrey50 = wDrawFindColor( wRGB(128,128,128) ); + drawColorGrey60 = wDrawFindColor( wRGB(153,153,153) ); + drawColorGrey70 = wDrawFindColor( wRGB(179,179,179) ); + drawColorGrey80 = wDrawFindColor( wRGB(204,204,204) ); + drawColorGrey90 = wDrawFindColor( wRGB(230,230,230) ); snapGridColor = drawColorGreen; markerColor = drawColorRed; borderColor = drawColorBlack; @@ -2664,73 +2949,71 @@ LOG1( log_init, ( "create main window\n" ) ) elevColorIgnore = drawColorBlue; elevColorDefined = drawColorGold; profilePathColor = drawColorPurple; - exceptionColor = wDrawFindColor( wRGB(255,0,128) ); - tieColor = wDrawFindColor( wRGB(255,128,0) ); + exceptionColor = wDrawFindColor(wRGB(255, 89, 0 )); + tieColor = wDrawFindColor(wRGB(153, 89, 68)); - newToolbarMax = (1<<BG_COUNT)-1; - wPrefGetInteger( "misc", "toolbarset", &toolbarSet, newToolbarMax ); - wPrefGetInteger( "misc", "max-toolbarset", &oldToolbarMax, 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 ); + wPrefSetInteger("misc", "max-toolbarset", newToolbarMax); + wPrefSetInteger("misc", "toolbarset", toolbarSet); -LOG1( log_init, ( "fontInit\n")) + LOG1(log_init, ( "fontInit\n")) wInitializeFonts(); -LOG1( log_init, ( "fileInit\n" ) ) + LOG1(log_init, ( "fileInit\n" )) FileInit(); - wCreateSplash( sProdName, sVersion ); + wCreateSplash(sProdName, sVersion); if (!initialFile) { WDOUBLE_T tmp; -LOG1( log_init, ( "set roomsize\n" ) ) - wPrefGetFloat( "draw", "roomsizeX", &tmp, 96.0 ); + LOG1(log_init, ( "set roomsize\n" )) + wPrefGetFloat("draw", "roomsizeX", &tmp, 96.0); roomSize.x = tmp; - wPrefGetFloat( "draw", "roomsizeY", &tmp, 48.0 ); + wPrefGetFloat("draw", "roomsizeY", &tmp, 48.0); roomSize.y = tmp; - SetRoomSize( roomSize ); + SetRoomSize(roomSize); } /* * INITIALIZE */ -LOG1( log_init, ( "initInfoBar\n" ) ) + LOG1(log_init, ( "initInfoBar\n" )) InitInfoBar(); - wSetSplashInfo( "Misc2 Init..." ); -LOG1( log_init, ( "misc2Init\n" ) ) + wSetSplashInfo("Misc2 Init..."); + LOG1(log_init, ( "misc2Init\n" )) Misc2Init(); RotateDialogInit(); MoveDialogInit(); - wSetSplashInfo( _("Initializing commands") ); -LOG1( log_init, ( "paramInit\n" ) ) + wSetSplashInfo(_("Initializing commands")); + LOG1(log_init, ( "paramInit\n" )) ParamInit(); -LOG1( log_init, ( "initTrkTrack\n" ) ) + LOG1(log_init, ( "initTrkTrack\n" )) InitTrkTrack(); /* * MENUS */ - wSetSplashInfo( _("Initializing menus") ); -LOG1( log_init, ( "createMenus\n" ) ) + wSetSplashInfo(_("Initializing menus")); + LOG1(log_init, ( "createMenus\n" )) CreateMenus(); - - -LOG1( log_init, ( "initialize\n" ) ) + LOG1(log_init, ( "initialize\n" )) if (!Initialize()) return NULL; - ParamRegister( &menuPG ); - ParamRegister( &stickyPG ); + ParamRegister(&menuPG); + ParamRegister(&stickyPG); /* initialize the layers */ DefaultLayerProperties(); -LOG1( log_init, ( "loadFileList\n" ) ) + LOG1(log_init, ( "loadFileList\n" )) LoadFileList(); - AddPlaybackProc( "MENU", MenuPlayback, NULL ); + AddPlaybackProc("MENU", MenuPlayback, NULL); //CreateDebugW(); /* @@ -2747,17 +3030,20 @@ LOG1( log_init, ( "loadFileList\n" ) ) } else { HideHotBar(); } -LOG1( log_init, ( "drawInit\n" ) ) - DrawInit( initialZoom ); + LOG1(log_init, ( "drawInit\n" )) + DrawInit(initialZoom); MacroInit(); - wSetSplashInfo( _("Reading parameter files") ); -LOG1( log_init, ( "paramFileInit\n" ) ) - if (!ParamFileInit()) + wSetSplashInfo(_("Reading parameter files")); + LOG1(log_init, ( "paramFileInit\n" )) + + SetParamFileDir(GetCurrentPath(LAYOUTPATHKEY)); //Set default for new parms to be the same as the layout + + if (!ParamFileListInit()) return NULL; curCommand = describeCmdInx; -LOG1( log_init, ( "Reset\n" ) ) + LOG1(log_init, ( "Reset\n" )) Reset(); /* @@ -2767,30 +3053,30 @@ LOG1( log_init, ( "Reset\n" ) ) /* Set up the data for scale and gauge description */ DoSetScaleDesc(); - // get the preferred scale from the configuration file - pref = wPrefGetString( "misc", "scale" ); - if( !pref ) + // get the preferred scale from the configuration file + pref = wPrefGetString("misc", "scale"); + if (!pref) // if preferred scale was not set (eg. during initial run), initialize to a default value pref = DEFAULT_SCALE; - strcpy( buffer, pref ); - DoSetScale( buffer ); + strcpy(buffer, pref); + DoSetScale(buffer); /* see whether last layout should be reopened on startup */ - wPrefGetInteger( "DialogItem", "pref-onstartup", &onStartup, 0 ); + wPrefGetInteger("DialogItem", "pref-onstartup", &onStartup, 0); /* * THE END */ -LOG1( log_init, ( "the end\n" ) ) + LOG1(log_init, ( "the end\n" )) EnableCommands(); -LOG1( log_init, ( "Initialization complete\n" ) ) - wSetSplashInfo( _("Initialization complete") ); - RegisterChangeNotification( ToolbarChange ); - DoChangeNotification( CHANGE_MAIN|CHANGE_MAP ); + LOG1(log_init, ( "Initialization complete\n" )) + wSetSplashInfo(_("Initialization complete")); + RegisterChangeNotification(ToolbarChange); + DoChangeNotification( CHANGE_MAIN | CHANGE_MAP); - wWinShow( mainW, TRUE ); - wWinShow( mapW, mapVisible ); + wWinShow(mainW, TRUE); + wWinShow(mapW, mapVisible); wDestroySplash(); /* this has to be called before ShowTip() */ @@ -2800,20 +3086,28 @@ LOG1( log_init, ( "Initialization complete\n" ) ) /* check for existing checkpoint file */ resumeWork = FALSE; - if (ExistsCheckpoint()) + if (ExistsCheckpoint()) { resumeWork = OfferCheckpoint(); + MainRedraw(); + } if (!resumeWork) { - /* if work is to be resumed and no filename was given on startup, load last layout */ + /* if work is not to be resumed and no filename was given on startup, load last layout */ if ((onStartup == 0) && (!initialFile || !strlen(initialFile))) { + long iExample; initialFile = (char*)wPrefGetString("misc", "lastlayout"); + wPrefGetInteger("misc", "lastlayoutexample", &iExample, 0); + bExample = (iExample == 1); } if (initialFile && strlen(initialFile)) { - DoFileList(0, NULL, initialFile); + DoFileList(0, NULL, initialFile); //Will load Background values, if archive + if (onStartup == 1) + LayoutBackGroundInit(TRUE); //Wipe Out Prior Background + else + LayoutBackGroundInit(FALSE); //Get Prior BackGround } } inMainW = FALSE; return mainW; } - diff --git a/app/bin/misc.h b/app/bin/misc.h index 2fb3359..2be0be2 100644 --- a/app/bin/misc.h +++ b/app/bin/misc.h @@ -44,6 +44,7 @@ typedef void (*addButtonCallBack_t)(void*); #define STR_SIZE (256) #define STR_SHORT_SIZE (80) #define STR_LONG_SIZE (1024) +#define STR_HUGE_SIZE (10240) #define CAST_AWAY_CONST (char*) @@ -68,15 +69,17 @@ extern long hideSelectionWindow; extern long labelWhen; extern long labelScale; extern long labelEnable; -extern long colorLayers; +extern long colorTrack; +extern long colorDraw; extern long carHotbarModeInx; extern DIST_T minLength; extern DIST_T connectDistance; extern ANGLE_T connectAngle; extern long twoRailScale; extern long mapScale; -extern long zoomCorner; +extern long constrainMain; extern long checkPtInterval; +extern long autosaveChkPoints; extern long liveMap; extern long preSelect; extern long hideTrainsInTunnels; @@ -91,8 +94,11 @@ extern DIST_T curScaleRatio; extern char * curScaleName; extern int enumerateMaxDescLen; extern long enableBalloonHelp; +extern long showFlexTrack; extern long hotBarLabels; extern long rightClickMode; +extern long selectMode; +extern long selectZero; extern void * commandContext; extern coOrd cmdMenuPos; #define MODE_DESIGN (0) @@ -136,6 +142,12 @@ extern long programMode; #define C_TEXT wActionText #define C_WUP wActionWheelUp #define C_WDOWN wActionWheelDown +#define C_LDOUBLE wActionLDownDouble +#define C_MODKEY wActionModKey +#define C_SCROLLUP wActionScrollUp +#define C_SCROLLDOWN wActionScrollDown +#define C_SCROLLLEFT wActionScrollLeft +#define C_SCROLLRIGHT wActionScrollRight #define C_INIT (wActionLast+1) #define C_START (wActionLast+2) #define C_REDRAW (wActionLast+3) @@ -146,6 +158,7 @@ extern long programMode; #define C_RCLICK (wActionLast+8) #define C_CMDMENU (wActionLast+9) #define C_FINISH (wActionLast+10) +#define C_UPDATE (wActionLast+11) #define C_CONTINUE (100) #define C_TERMINATE (101) @@ -180,7 +193,7 @@ extern wPos_t DlgSepFrmBottom; extern wWin_p mainW; extern wPos_t toolbarHeight; extern wIndex_t changed; -extern char message[STR_LONG_SIZE]; +extern char message[STR_HUGE_SIZE]; extern REGION_T curRegion; extern long paramVersion; extern coOrd zero; @@ -189,6 +202,7 @@ 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 backgroundB; /** background visibility control */ // extern wButton_p easementB; extern wIndex_t checkPtMark; extern wMenu_p demoM; @@ -221,9 +235,11 @@ int NoticeMessage( char *, char*, char *, ... ); int NoticeMessage2( int, char *, char*, char *, ... ); void DoQuit( void ); +void FileIsChanged(void); char * ConvertFromEscapedText(const char * text); char * ConvertToEscapedText(const char * text); +int MagneticSnap( int state ); void wShow( wWin_p ); void wHide( wWin_p ); void CloseDemoWindows( void ); @@ -232,28 +248,34 @@ void SelectFont(); void CheckRoomSize( BOOL_T ); const char * GetBalloonHelpStr( char* ); +const char * GetCurCommandName( void ); void EnableCommands( void ); void Reset( void ); +wIndex_t GetCurrentCommand(void); +BOOL_T IsCurCommandSticky(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) +#define IC_STICKY (1<<0) +#define IC_INITNOTSTICKY (1<<1) +#define IC_CANCEL (1<<2) +#define IC_MENU (1<<3) +#define IC_NORESTART (1<<4) +#define IC_SELECTED (1<<5) +#define IC_POPUP (1<<6) +#define IC_LCLICK (1<<7) +#define IC_RCLICK (1<<8) +#define IC_CMDMENU (1<<9) +#define IC_POPUP2 (1<<10) +#define IC_ABUT (1<<11) +#define IC_ACCLKEY (1<<12) +#define IC_MODETRAIN_TOO (1<<13) +#define IC_MODETRAIN_ONLY (1<<14) +#define IC_WANT_MOVE (1<<15) +#define IC_PLAYBACK_PUSH (1<<16) +#define IC_WANT_MODKEYS (1<<17) +#define IC_POPUP3 (1<<18) wIndex_t InitCommand( wMenu_p, procCommand_t, char *, char *, int, long, long ); void AddToolbarControl( wControl_p, long ); BOOL_T CommandEnabled( wIndex_t ); @@ -278,22 +300,25 @@ void InitDebug( char *, long * ); #define CHANGE_MAIN (1<<2) #define CHANGE_MAP (1<<4) #define CHANGE_GRID (1<<5) +#define CHANGE_BACKGROUND (1<<6) #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) +#define CHANGE_ALL (CHANGE_SCALE|CHANGE_PARAMS|CHANGE_MAIN|CHANGE_MAP|CHANGE_UNITS|CHANGE_TOOLBAR|CHANGE_CMDOPT|CHANGE_BACKGROUND) typedef void (*changeNotificationCallBack_t)( long ); void RegisterChangeNotification( changeNotificationCallBack_t ); void DoChangeNotification( long ); +wBool_t CheckHelpTopicExists(const char * topic); + /* foreign externs */ extern drawCmd_t mapD; extern STATUS_T CmdEnumerate( wAction_t, coOrd ); -wIndex_t modifyCmdInx; -wIndex_t joinCmdInx; -wIndex_t tunnelCmdInx; +extern wIndex_t modifyCmdInx; +extern wIndex_t joinCmdInx; +extern wIndex_t tunnelCmdInx; /* ctodesgn.c */ void InitNewTurn( wMenu_p m ); @@ -308,7 +333,7 @@ STATUS_T ModifyRuler( wAction_t, coOrd ); /* dialogs */ void OutputBitMap( void ); -wDrawColor snapGridColor; +extern wDrawColor snapGridColor; addButtonCallBack_t ColorInit( void ); addButtonCallBack_t PrefInit( void ); @@ -330,9 +355,8 @@ void InitSnapGridButtons( void ); void SnapGridEnable( void ); void SnapGridShow( void ); void MapWindowShow( int state ); -wMenuToggle_p snapGridEnableMI; -wMenuToggle_p snapGridShowMI; -wMenuToggle_p mapShowMI; +extern wMenuToggle_p snapGridEnableMI; +extern wMenuToggle_p snapGridShowMI; void ScaleLengthEnd( void ); void EnumerateList( long, FLOAT_T, char * ); @@ -343,6 +367,8 @@ void EnumerateEnd(void); void DoNote( void ); BOOL_T WriteMainNote( FILE * ); +BOOL_T ReadMainNote(char * line); + /* dbench.c */ long GetBenchData( long, long ); wIndex_t GetBenchListIndex( long ); @@ -360,7 +386,7 @@ long BenchOutputOption( long ); DIST_T BenchGetWidth( long ); /* dcustmgm.c */ -FILE * customMgmF; +extern FILE * customMgmF; #define CUSTMGM_DO_COPYTO (1) #define CUSTMGM_CAN_EDIT (2) #define CUSTMGM_DO_EDIT (3) @@ -389,6 +415,7 @@ void ContMgmLoad (wIcon_p,contMgmCallBack_p,void *); /* dlayer.c */ void LayerSetCounts(); +int FindUnusedLayer(unsigned int start); void DecrementLayerObjects(unsigned int index); void IncrementLayerObjects(unsigned int index); @@ -418,4 +445,8 @@ void InitCmdControl ( wMenu_p menu ); /* csensor.c */ void SensorMgmLoad ( void ); void InitCmdSensor ( wMenu_p menu ); +/* cmodify.c */ +STATUS_T CmdModify(wAction_t action,coOrd pos ); + + #endif diff --git a/app/bin/misc2.c b/app/bin/misc2.c index 0dbb57d..19226cc 100644 --- a/app/bin/misc2.c +++ b/app/bin/misc2.c @@ -53,15 +53,17 @@ EXPORT long units = 0; /**< measurement units: 0 = English, 1 = metric */ EXPORT long checkPtInterval = 10; +EXPORT long autosaveChkPoints = 2; EXPORT DIST_T curScaleRatio; EXPORT char * curScaleName; EXPORT DIST_T trackGauge; EXPORT long labelScale = 8; -EXPORT long labelEnable = ((1<<0)|LABELENABLE_LENGTHS|LABELENABLE_ENDPT_ELEV|LABELENABLE_CARS); +EXPORT long labelEnable = (LABELENABLE_ENDPT_ELEV|LABELENABLE_CARS); EXPORT long labelWhen = 2; -EXPORT long colorLayers = 0; -EXPORT long zoomCorner = 0; +EXPORT long colorTrack = 0; +EXPORT long colorDraw = 0; +EXPORT long constrainMain = 0; EXPORT long hideSelectionWindow = 0; EXPORT long angleSystem = 0; EXPORT DIST_T minLength = 0.1; @@ -156,10 +158,6 @@ EXPORT void DoChangeNotification( long changes ) * */ - -#define SCALE_ANY (-2) -#define SCALE_DEMO (-1) - typedef struct { char * scale; DIST_T ratio; @@ -467,7 +465,9 @@ SetScale( SCALEINX_T newScaleInx ) SetScaleDescGauge((SCALEINX_T)newScaleInx); - wPrefSetString( "misc", "scale", curScaleName ); + + if (!inPlayback) + wPrefSetString( "misc", "scale", curScaleName ); // now load the minimum radius for the newly selected scale LoadLayoutMinRadiusPref(curScaleName, curScale->R[0]); diff --git a/app/bin/misc2.h b/app/bin/misc2.h index d616ad8..966f75b 100644 --- a/app/bin/misc2.h +++ b/app/bin/misc2.h @@ -42,7 +42,7 @@ typedef struct { } logTable_t; extern dynArr_t logTable_da; #define logTable(N) DYNARR_N( logTable_t, logTable_da, N ) -time_t logClock; +extern time_t logClock; void LogOpen( char * ); void LogClose( void ); void LogSet( char *, int ); @@ -85,13 +85,19 @@ void LoadGaugeList( wList_p, SCALEDESCINX_T ); BOOL_T CompatibleScale( BOOL_T, SCALEINX_T, SCALEINX_T ); BOOL_T DoSetScaleDesc( void ); -unsigned int curLayer; -long layerCount; +extern unsigned int curLayer; +extern long layerCount; +void SetCurrLayer(wIndex_t inx, const char * name, wIndex_t op, + void * listContext, void * arg); wDrawColor GetLayerColor( unsigned int ); +BOOL_T GetLayerUseColor( unsigned int); BOOL_T GetLayerVisible( unsigned int ); BOOL_T GetLayerFrozen( unsigned int ); BOOL_T GetLayerOnMap( unsigned int ); +BOOL_T GetLayerModule( unsigned int ); +void SetLayerModule(unsigned int, BOOL_T); char * GetLayerName( unsigned int ); +void SetLayerName(unsigned int layer, char* name); BOOL_T ReadLayers( char * ); BOOL_T WriteLayers( FILE * ); char * FormatLayerName(unsigned int layerNumber); @@ -103,7 +109,7 @@ void SaveLayers( void ); void RestoreLayers( void ); void LoadLayerLists( void ); addButtonCallBack_t InitLayersDialog( void ); - +void FillLayerList(wList_p layerList); void Misc2Init( void ); #endif diff --git a/app/bin/note.h b/app/bin/note.h new file mode 100644 index 0000000..315823f --- /dev/null +++ b/app/bin/note.h @@ -0,0 +1,110 @@ +/** \file note.h + * Common definitions for notes + */ + + /* XTrkCad - Model Railroad CAD + * Copyright (C) 2018 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. + */ + +#ifndef HAVE_NOTE_H +#define HAVE_NOTE_H +#include <stdbool.h> +#include "track.h" + +#define URLMAXIMUMLENGTH (512) +#define PATHMAXIMUMLENGTH (2048) +#define TITLEMAXIMUMLENGTH (81) + +#define MYMIN(x, y) (((x) < (y)) ? (x) : (y)) + +#define DELIMITER "--|--" + +enum noteCommands { + OP_NOTETEXT, + OP_NOTELINK, + OP_NOTEFILE +}; + +/** hold the data for the note */ +struct extraDataNote { + coOrd pos; /**< position */ + unsigned int layer; + enum noteCommands op; /**< note type */ + track_p trk; /**< track */ + union { + char * text; /**< used for text only note */ + struct { + char *title; + char *url; + } linkData; /**< used for link note */ + struct { + char *path; + char *title; + BOOL_T inArchive; + } fileData; /**< used for file note */ + } noteData; +}; + +//struct noteTextData { +// coOrd pos; +// unsigned int layer; +// char *text; +// track_p trk; +//}; + +struct noteLinkData { + coOrd pos; + unsigned int layer; + char title[TITLEMAXIMUMLENGTH]; + char url[URLMAXIMUMLENGTH]; + track_p trk; +}; + +struct noteFileData { + coOrd pos; + unsigned int layer; + char title[TITLEMAXIMUMLENGTH]; + char path[PATHMAXIMUMLENGTH]; + track_p trk; + BOOL_T inArchive; +}; + +enum { OR_NOTE, LY_NOTE, TX_TEXT, OK_TEXT, TITLE_LINK, TX_LINK, OK_LINK, TITLE_FILE, OK_FILE, CANCEL_NOTE }; + +/* linknoteui.c */ +void NewLinkNoteUI(track_p trk); +BOOL_T IsLinkNote(track_p trk); +void DescribeLinkNote(track_p trk, char * str, CSIZE_T len); +void ActivateLinkNote(track_p trk); + +/* filenozeui.c */ +void NewFileNoteUI(track_p trk); +BOOL_T IsFileNote(track_p trk); +void DescribeFileNote(track_p trk, char * str, CSIZE_T len); +void ActivateFileNote(track_p trk); + +/* textnoteui.c */ +void NewTextNoteUI(track_p trk); +void DescribeTextNote(track_p trk, char * str, CSIZE_T len); + +/* trknote.c */ +void NoteStateSave(track_p trk); + +void UpdateFile(struct extraDataNote *noteUIData, int inx, BOOL_T needUndoStart); +void UpdateText(struct extraDataNote *noteUIData, int inx, BOOL_T needUndoStart); +void UpdateLink(struct extraDataNote *noteUIData, int inx, BOOL_T needUndoStart); +#endif // !HAVE_NOTE_H diff --git a/app/bin/param.c b/app/bin/param.c index 2f54d77..27a8cf9 100644 --- a/app/bin/param.c +++ b/app/bin/param.c @@ -766,63 +766,100 @@ EXPORT long ParamUpdate( } -EXPORT void ParamLoadData( - paramGroup_p pg ) +void ParamLoadData( + paramGroup_p pg) { - FLOAT_T floatV; - const char * stringV; - paramData_p p; - BOOL_T valid; + FLOAT_T floatV; + const char * stringV; + paramData_p p; + BOOL_T valid; + BOOL_T inRange; + + for (p=pg->paramPtr; p<&pg->paramPtr[pg->paramCnt]; p++) { + if ((p->option&PDO_DLGIGNORE) != 0) { + continue; + } - 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 ); - 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: - case PD_BITMAP: - break; - } - } + if (p->control == NULL || p->valueP == NULL) { + continue; + } + + switch (p->type) { + long longV; + + case PD_LONG: + longV = atol(wStringGetValue((wString_p)p->control)); + + if (p->winData) { + inRange = (longV <= ((paramIntegerRange_t *)p->winData)->high) && + (longV >= ((paramIntegerRange_t *)p->winData)->low); + } else { + inRange = TRUE; + } + + if (inRange) { + *(long*)p->valueP = longV; + } + + 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); + 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 (p->winData) { + inRange = (floatV <= ((paramFloatRange_t *)p->winData)->high) && + (floatV >= ((paramFloatRange_t *)p->winData)->low); + } else { + inRange = TRUE; + } + + if (valid && inRange) { + *(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: + case PD_BITMAP: + break; + } + } } @@ -1348,13 +1385,24 @@ static void ParamIntegerPush( const char * val, void * dp ) paramData_p p = (paramData_p)dp; long valL; char * cp; + const char * value; paramIntegerRange_t * irangeP; - while ( isspace((unsigned char)*val)) val++; - valL = strtol( val, &cp, 10 ); + if (strlen(val) == 1 && val[strlen(val)-1] == '\n' && (p->option & BO_ENTER)) { + value = wStringGetValue((wString_p)p->control); + p->enter_pressed = TRUE; + } else { + p->enter_pressed = FALSE; + value = CAST_AWAY_CONST val; + } + + + while ( isspace((unsigned char)*value)) value++; + valL = strtol( value, &cp, 10 ); + //wControlSetBalloon( p->control, 0, -5, NULL ); - if ( val == cp ) { + if ( value == cp ) { wControlSetBalloon( p->control, 0, -5, _("Invalid Number") ); return; } @@ -1396,7 +1444,15 @@ static void ParamFloatPush( const char * val, void * dp ) paramData_p p = (paramData_p)dp; FLOAT_T valF; BOOL_T valid; + const char * value; paramFloatRange_t * frangeP; + if (strlen(val) == 1 && val[strlen(val)-1] == '\n' && (p->option & PDO_ENTER)) { + value = wStringGetValue((wString_p)p->control); + p->enter_pressed = TRUE; + } else { + value = val; + p->enter_pressed = FALSE; + } if (p->option & PDO_DIM) { valF = DecodeDistance( (wString_p)p->control, &valid ); @@ -1434,7 +1490,7 @@ static void ParamFloatPush( const char * val, void * dp ) } if ( (p->option&PDO_NOPSHUPD)==0 && p->valueP) *((FLOAT_T*)(p->valueP)) = valF; - if ( (p->option&PDO_NOPSHACT)==0 && p->group->changeProc && strlen( val )) + if ( (p->option&PDO_NOPSHACT)==0 && p->group->changeProc && strlen( value )) p->group->changeProc( p->group, p-p->group->paramPtr, &valF ); } @@ -1442,14 +1498,23 @@ static void ParamFloatPush( const char * val, void * dp ) static void ParamStringPush( const char * val, void * dp ) { paramData_p p = (paramData_p)dp; + const char * value; 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 (strlen(val) == 1 && val[strlen(val)-1] == '\n' && (p->option & PDO_ENTER)) { + value = wStringGetValue((wString_p)p->control); + p->enter_pressed = TRUE; + } else { + p->enter_pressed = FALSE; + value = CAST_AWAY_CONST val; + } + if ( (p->option&PDO_NOPSHUPD)==0 && p->valueP) - strcpy( (char*)p->valueP, val ); + strcpy( (char*)p->valueP, value ); if ( (p->option&PDO_NOPSHACT)==0 && p->group->changeProc) - p->group->changeProc( p->group, p-p->group->paramPtr, CAST_AWAY_CONST val ); + p->group->changeProc( p->group, p-p->group->paramPtr, CAST_AWAY_CONST value ); } @@ -1495,6 +1560,15 @@ EXPORT void ParamMenuPush( void * dp ) static void ParamColorSelectPush( void * dp, wDrawColor dc ) { paramData_p p = (paramData_p)dp; + long rgb = wDrawGetRGB( dc ); + while ( dc == drawColorPreviewSelected || dc == drawColorPreviewUnselected ) { + // The user picked a special color, tweak it + rgb -= 1; // Make it very close but different + if ( ( rgb & 0xFF ) == 0 ) + // Ran out of room - bail + break; + dc = wDrawFindColor( rgb ); + } 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 ); @@ -1827,13 +1901,19 @@ static void ParamPlayback( char * line ) pg->changeProc( pg, inx, &valF ); break; case PD_STRING: + case PD_TEXT: line += len; while ( *line == ' ' ) line++; Stripcr( line ); if (p->valueP) strcpy( (char*)p->valueP, line ); if (p->control) { - wStringSetValue( (wString_p)p->control, line ); + if (p->type == PD_STRING) { + wStringSetValue((wString_p)p->control, line); + } else { + wTextClear((wText_p)p->control); + wTextAppend((wText_p)p->control, line); + } wFlush(); } if (pg->changeProc) @@ -1849,7 +1929,6 @@ static void ParamPlayback( char * line ) PlaybackMouse( ddp->action, ddp->d, a, pos, drawColorBlack ); break; case PD_MESSAGE: - case PD_TEXT: case PD_MENU: case PD_BITMAP: break; @@ -2429,7 +2508,7 @@ SkipControl: 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 ); + group->boxs[box] = wBoxCreate( group->win, DlgSepLeft, boxTop, NULL, wBoxThickW, columnK.term.x, boxPos[box]-boxTop ); boxTop = boxPos[box] + 4; } } else { @@ -2503,7 +2582,10 @@ static void ParamDlgProc( DefaultProc( win, wClose_e, data ); break; case wResize_e: - LayoutControls( pg, ParamPositionControl, NULL, NULL ); + if (win == mapW) + pg->changeProc(pg, wResize_e, NULL); + else + LayoutControls( pg, ParamPositionControl, NULL, NULL ); break; default: break; @@ -2544,6 +2626,7 @@ wWin_p ParamCreateDialog( group->cancelProc = cancelProc; group->layoutProc = layoutProc; group->changeProc = changeProc; + group->winOption = winOption; if ( (winOption&F_CENTER) == 0 ) winOption |= F_RECALLPOS; if ( (winOption&F_RESIZE) != 0 ) diff --git a/app/bin/param.h b/app/bin/param.h index 243bd1b..414dae3 100644 --- a/app/bin/param.h +++ b/app/bin/param.h @@ -58,6 +58,7 @@ typedef enum { #define PDO_MISC (1L<<7) #define PDO_DRAW (1L<<8) #define PDO_FILE (1L<<9) +#define PDO_ENTER (1L<<10) #define PDO_STRINGLIMITLENGTH (1L<<11) /**< context has maximum length for string */ #define PDO_SMALLDIM (1L<<12) @@ -142,6 +143,7 @@ typedef struct { wControl_p control; paramGroup_p group; paramOldData_t oldD, demoD; + wBool_t enter_pressed; } paramData_t, *paramData_p; diff --git a/app/bin/paramfile.c b/app/bin/paramfile.c new file mode 100644 index 0000000..2dd9ac7 --- /dev/null +++ b/app/bin/paramfile.c @@ -0,0 +1,393 @@ +/** \file paramfile.c + * Handling of parameter files + */ + + /* XTrackkCad - Model Railroad CAD + * Copyright (C) 2019 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 <ctype.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "common.h" +#include "compound.h" +#include "ctrain.h" +#include "custom.h" +#include "fileio.h" +#include "i18n.h" +#include "layout.h" +#include "messages.h" +#include "misc2.h" +#include "paths.h" +#include "include/paramfile.h" +#include "include/paramfilelist.h" +#include "include/utf8convert.h" + +#if _MSC_VER >1300 +#define stricmp( a, b ) _stricmp(a, b ) +#endif + +static long paramCheckSum; + +typedef enum paramFileState(*GetCompatibilityFunction)(int index, + SCALEINX_T scale); + +GetCompatibilityFunction GetCompatibility[] = { + GetTrackCompatibility, + GetStructureCompatibility, + GetCarProtoCompatibility, + GetCarPartCompatibility +}; + +#define COMPATIBILITYCHECKSCOUNT (sizeof(GetCompatibility)/sizeof(GetCompatibility[0])) + +/** + * Check whether parameter file is still loaded + * + * \param fileInx + * \return TRUE if loaded, FALSE otherwise + */ + +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); + } else { + return FALSE; + } +} + +char *GetParamFileDir(void) +{ + return (GetCurrentPath(PARAMETERPATHKEY)); +} + +void +SetParamFileDir(char *fullPath) +{ + SetCurrentPath(PARAMETERPATHKEY, fullPath); +} + +char * GetParamFileName( + int fileInx) +{ + return paramFileInfo(fileInx).name; +} + +char * GetParamFileContents( + int fileInx) +{ + return paramFileInfo(fileInx).contents; +} + +bool IsParamFileDeleted(int inx) +{ + return paramFileInfo(inx).deleted; +} + +bool IsParamFileFavorite(int inx) +{ + return paramFileInfo(inx).favorite; +} + +void SetParamFileDeleted(int fileInx, bool deleted) +{ + paramFileInfo(fileInx).deleted = deleted; +} + +void SetParamFileFavorite(int fileInx, bool favorite) +{ + paramFileInfo(fileInx).favorite = favorite; +} + +void ParamCheckSumLine(char * line) +{ + long mult = 1; + while (*line) { + paramCheckSum += (((long)(*line++)) & 0xFF)*(mult++); + } +} + +/** + * Set the compatibility state of a parameter file + * + * \param index parameter file number in list + * \return + */ + +void SetParamFileState(int index) +{ + enum paramFileState state = PARAMFILE_NOTUSABLE; + enum paramFileState newState; + SCALEINX_T scale = GetLayoutCurScale(); + + for (int i = 0; i < COMPATIBILITYCHECKSCOUNT && state < PARAMFILE_FIT && + state != PARAMFILE_UNLOADED; i++) { + newState = (*GetCompatibility[i])(index, scale); + if (newState > state || newState == PARAMFILE_UNLOADED) { + state = newState; + } + } + + paramFileInfo(index).trackState = state; +} + +/** + * Read a single parameter file and update the parameter file list + * + * \param fileName full path for parameter file + * \return + */ + +int +ReadParamFile(const char *fileName) +{ + DYNARR_APPEND(paramFileInfo_t, paramFileInfo_da, 10); + curParamFileIndex = paramFileInfo_da.cnt - 1; + paramFileInfo(curParamFileIndex).name = MyStrdup(fileName); + paramFileInfo(curParamFileIndex).valid = TRUE; + paramFileInfo(curParamFileIndex).deleted = !ReadParams(0, NULL, fileName); + paramFileInfo(curParamFileIndex).contents = MyStrdup(curContents); + + SetParamFileState(curParamFileIndex); + + return (curParamFileIndex); +} + +/** + * Reload a single parameter file that had been unloaded before. + * + * \param fileindex index of previously created paramFileInfo + * + * \returns + */ + +int +ReloadDeletedParamFile(int fileindex) +{ + curParamFileIndex = fileindex; + paramFileInfo(curParamFileIndex).valid = TRUE; + paramFileInfo(curParamFileIndex).deleted = !ReadParams(0, NULL, paramFileInfo(curParamFileIndex).name); + paramFileInfo(curParamFileIndex).contents = MyStrdup(curContents); + + SetParamFileState(curParamFileIndex); + + return (curParamFileIndex); +} + +/** + * Parameter file reader and interpreter + * + * \param key unused + * \param dirName prefix for parameter file path + * \param fileName name of parameter file + * \return + */ + +bool 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; + paramVersion = -1; + char *oldLocale = NULL; + + if (dirName) { + MakeFullpath(¶mFileName, dirName, fileName, NULL); + } else { + MakeFullpath(¶mFileName, fileName, NULL); + } + paramLineNum = 0; + curBarScale = -1; + curContents = MyStrdup(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; + BOOL_T skip = false; + int skipLines = 0; + while (paramFile && (fgets(paramLine, 256, paramFile)) != NULL) { + paramLineNum++; + Stripcr(paramLine); + if (strncmp(paramLine, "CHECKSUM ", 9) == 0) { + checkSum = atol(paramLine + 9); + checkSummed = TRUE; + goto nextLine; + } + ParamCheckSumLine(paramLine); + if (paramLine[0] == '#') { + /* comment */ + } else if (paramLine[0] == 0) { + /* empty paramLine */ + } else if (strncmp(paramLine, "INCLUDE ", 8) == 0) { + cp = ¶mLine[8]; + while (*cp && isspace((unsigned char)*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; + if (!ReadParams(key, dirName, cp)) { + RestoreLocale(oldLocale); + return FALSE; + } + paramFile = oldFile; + paramLineNum = oldLineNum; + paramCheckSum = oldCheckSum; + if (dirName) { + MakeFullpath(¶mFileName, dirName, fileName, NULL); + } else { + MakeFullpath(¶mFileName, fileName); + } + skip = FALSE; + } else if (strncmp(paramLine, "CONTENTS ", 9) == 0) { +#ifdef WINDOWS + ConvertUTF8ToSystem(paramLine + 9); +#endif + curContents = MyStrdup(paramLine + 9); + curSubContents = curContents; + skip = FALSE; + } else if (strncmp(paramLine, "SUBCONTENTS ", 12) == 0) { +#ifdef WINDOWS + ConvertUTF8ToSystem(paramLine + 12); +#endif // WINDOWS + curSubContents = MyStrdup(paramLine + 12); + skip = FALSE; + } else if (strncmp(paramLine, "PARAM ", 6) == 0) { + paramVersion = strtol(paramLine + 8, &cp, 10); + if (cp) + while (*cp && isspace((unsigned char)*cp)) cp++; + if (paramVersion > iParamVersion) { + if (cp && *cp) { + NoticeMessage(MSG_PARAM_UPGRADE_VERSION1, _("Ok"), NULL, paramVersion, iParamVersion, sProdName, cp); + } else { + NoticeMessage(MSG_PARAM_UPGRADE_VERSION2, _("Ok"), NULL, paramVersion, iParamVersion, sProdName); + } + break; + } + if (paramVersion < iMinParamVersion) { + NoticeMessage(MSG_PARAM_BAD_FILE_VERSION, _("Ok"), NULL, paramVersion, iMinParamVersion, sProdName); + break; + } + } else if (skip && (strncmp(paramLine, " ", 1) == 0)) { + //Always skip to next line starting in LeftHandColumn + skipLines++; + goto nextLine; + } else { + for (pc = 0; pc < paramProc_da.cnt; pc++) { + if (strncmp(paramLine, paramProc(pc).name, + strlen(paramProc(pc).name)) == 0) { + skip = FALSE; //Stop skip so we re-message + paramProc(pc).proc(paramLine); + goto nextLine; + } + } + if (!skip) { + if (InputError(_("Unknown param file line - skip until next good object?"), TRUE)) { //OK to carry on + /* SKIP until next main line we recognize */ + skip = TRUE; + skipLines++; + goto nextLine; + } else { + if (skipLines > 0) + NoticeMessage(MSG_PARAM_LINES_SKIPPED, _("Ok"), NULL, paramFileName, skipLines); + if (paramFile) { + fclose(paramFile); + paramFile = NULL; + } + if (paramFileName) { + free(paramFileName); + paramFileName = NULL; + } + RestoreLocale(oldLocale); + return FALSE; + } + } + skipLines++; + } + 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 (skipLines > 0) + NoticeMessage(MSG_PARAM_LINES_SKIPPED, _("Ok"), NULL, paramFileName, skipLines); + if (paramFile) { + fclose(paramFile); + } + free(paramFileName); + paramFileName = NULL; + RestoreLocale(oldLocale); + + return TRUE; +} diff --git a/app/bin/paramfilelist.c b/app/bin/paramfilelist.c new file mode 100644 index 0000000..199345c --- /dev/null +++ b/app/bin/paramfilelist.c @@ -0,0 +1,496 @@ +/** \file paramfilelist.c + * Handling of the list of parameter files + */ + +/* XTrackCad - Model Railroad CAD + * Copyright (C) 2019 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 <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "common.h" +#include "compound.h" +#include "ctrain.h" +#include "custom.h" +#include "dynstring.h" +#include "fileio.h" +#include "i18n.h" +#include "layout.h" +#include "messages.h" +#include "misc2.h" +#include "paths.h" +#include "include/paramfile.h" +#include "include/paramfilelist.h" + + +dynArr_t paramFileInfo_da; + +int curParamFileIndex = PARAM_DEMO; + +static int log_params; + +static char * customPath; +static char * customPathBak; + +#define FAVORITESECTION "Parameter File Favorites" +#define FAVORITETOTALS "Total" +#define FAVORITEKEY "Favorite%d" +#define FAVORITEDELETED "Deleted%d" + +int GetParamFileCount() +{ + return (paramFileInfo_da.cnt); +} + + + + +/** + * Update the configuration file in case the name of a parameter file has changed. + * The function reads a list of new parameter filenames and gets the contents + * description for each file. If that contents is in use, i.e. is a loaded parameter + * file, the setting in the config file is updated to the new filename. + * First line of update file has the date of the update file. + * * Following lines have filenames, one per line. + * + * \return FALSE if update not possible or not necessary, TRUE if update successful + */ + +static BOOL_T UpdateParamFiles(void) +{ + char fileName[STR_LONG_SIZE], *fileNameP; + const char * cp; + FILE * updateF; + long updateTime; + long lastTime; + + MakeFullpath(&fileNameP, libDir, "xtrkcad.upd", NULL); + updateF = fopen(fileNameP, "r"); + free(fileNameP); + if (updateF == NULL) { + return FALSE; + } + if (fgets(message, sizeof message, updateF) == NULL) { + NoticeMessage("short file: xtrkcad.upd", _("Ok"), NULL); + fclose(updateF); + return FALSE; + } + wPrefGetInteger("file", "updatetime", &lastTime, 0); + updateTime = atol(message); + if (lastTime >= updateTime) { + fclose(updateF); + return FALSE; + } + + while ((fgets(fileName, STR_LONG_SIZE, updateF)) != NULL) { + FILE * paramF; + char * contents; + + Stripcr(fileName); + InfoMessage(_("Updating %s"), fileName); + MakeFullpath(&fileNameP, libDir, "params", fileName, NULL); + paramF = fopen(fileNameP, "r"); + if (paramF == NULL) { + NoticeMessage(MSG_PRMFIL_OPEN_NEW, _("Ok"), NULL, fileNameP); + free(fileNameP); + 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, fileNameP); + free(fileNameP); + continue; + } + cp = wPrefGetString("Parameter File Map", contents); + wPrefSetString("Parameter File Map", contents, fileNameP); + if (cp != NULL && *cp != '\0') { + /* been there, done that */ + free(fileNameP); + continue; + } + + free(fileNameP); + } + fclose(updateF); + wPrefSetInteger("file", "updatetime", updateTime); + return TRUE; +} + +/** + * Read the list of parameter files from configuration and load the files. + * + */ +void LoadParamFileList(void) +{ + int fileNo; + BOOL_T updated = FALSE; + long *favoriteList = NULL; + long favorites; + int nextFavorite = 0; + + updated = UpdateParamFiles(); + + wPrefGetIntegerBasic(FAVORITESECTION, FAVORITETOTALS, &favorites, 0); + if (favorites) { + DynString topic; + favoriteList = MyMalloc(sizeof(long)*favorites); + if (!favoriteList) { + AbortProg("Couldn't allocate memory for favorite list!\n"); + } + + DynStringMalloc(&topic, 16); + for (int i = 0; i < favorites; i++) { + DynStringPrintf(&topic, FAVORITEKEY, i); + wPrefGetIntegerBasic(FAVORITESECTION, DynStringToCStr(&topic), &favoriteList[i], + 0); + } + DynStringFree(&topic); + } + + for (fileNo = 1; ; fileNo++) { + char *fileName; + const char * contents; + enum paramFileState structState = PARAMFILE_UNLOADED; + + + 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; + } + char * share; + + // Rewire to the latest system level + if ((share= strstr(fileName,"/share/xtrkcad/params/"))) { + share += strlen("/share/xtrkcad/params/"); + MakeFullpath(&fileName, wGetAppLibDir(), "params", share, NULL); + wPrefSetString("Parameter File Map", contents, fileName); + } + + ReadParamFile(fileName); + + if (curContents == NULL) { + curContents = curSubContents = MyStrdup(contents); + } + paramFileInfo(curParamFileIndex).contents = curContents; + if (favoriteList && fileNo == favoriteList[nextFavorite]) { + DynString topic; + long deleted; + DynStringMalloc(&topic, 16); + DynStringPrintf(&topic, FAVORITEDELETED, fileNo); + + wPrefGetIntegerBasic(FAVORITESECTION, DynStringToCStr(&topic), &deleted, 0L); + paramFileInfo(curParamFileIndex).favorite = TRUE; + paramFileInfo(curParamFileIndex).deleted = deleted; + if (nextFavorite < favorites - 1) { + nextFavorite++; + } + DynStringFree(&topic); + } + + } + curParamFileIndex = PARAM_CUSTOM; + if (updated) { + SaveParamFileList(); + } + + MyFree(favoriteList); +} + +/** + * Save the currently selected parameter files. Parameter files that have been unloaded + * are not saved. + * + */ + +void SaveParamFileList(void) +{ + int fileInx; + int fileNo; + int favorites; + char * contents, *cp; + + for (fileInx = 0, fileNo = 1, favorites = 0; fileInx < paramFileInfo_da.cnt; + fileInx++) { + if (paramFileInfo(fileInx).valid && (!paramFileInfo(fileInx).deleted || + paramFileInfo(fileInx).favorite)) { + 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); + wPrefSetString("Parameter File Map", contents, paramFileInfo(fileInx).name); + if (paramFileInfo(fileInx).favorite) { + sprintf(message, FAVORITEKEY, favorites); + wPrefSetInteger(FAVORITESECTION, message, fileNo); + sprintf(message, FAVORITEDELETED, fileNo); + wPrefSetInteger(FAVORITESECTION, message, paramFileInfo(fileInx).deleted); + favorites++; + } + fileNo++; + } + } + sprintf(message, "File%d", fileNo); + wPrefSetString("Parameter File Names", message, ""); + wPrefSetInteger(FAVORITESECTION, FAVORITETOTALS, favorites); +} + +void +UpdateParamFileList(void) +{ + for (size_t i = 0; i < (unsigned)paramFileInfo_da.cnt; i++) { + SetParamFileState(i); + } +} + +/** + * Load the selected parameter files. This is a callback executed when the file selection dialog + * is closed. + * Steps: + * - the parameters are read from file + * - check is performed to see whether the content is already present, if yes the previously + * loaded content is invalidated + * - loaded parameter file is added to list of parameter files + * - if a parameter file dialog exists the list is updated. It is either rewritten in + * in case of an invalidated file or the new file is appended + * - the settings are updated + * These steps are repeated for every file in list + * + * \param files IN the number of filenames in the fileName array + * \param fileName IN an array of fully qualified filenames + * \param data IN ignored + * \return TRUE on success, FALSE on error + */ + +int LoadParamFile( + int files, + char ** fileName, + void * data) +{ + wIndex_t inx; + int i = 0; + + assert(fileName != NULL); + assert(files > 0); + + for (i = 0; i < files; i++) { + enum paramFileState structState = PARAMFILE_UNLOADED; + int newIndex; + + curContents = curSubContents = NULL; + + newIndex = ReadParamFile(fileName[i]); + + // in case the contents is already present, make invalid + for (inx = 0; inx < newIndex; inx++) { + if (paramFileInfo(inx).valid && + strcmp(paramFileInfo(inx).contents, curContents) == 0) { + paramFileInfo(inx).valid = FALSE; + break; + } + } + + wPrefSetString("Parameter File Map", curContents, + paramFileInfo(curParamFileIndex).name); + } + //Only set the ParamFileDir if not the system directory + if (!strstr(fileName[i-1],"/share/xtrkcad/params/")) + SetParamFileDir(fileName[i - 1]); + curParamFileIndex = PARAM_CUSTOM; + DoChangeNotification(CHANGE_PARAMS); + return TRUE; +} + +static void ReadCustom(void) +{ + FILE * f; + MakeFullpath(&customPath, workingDir, sCustomF, NULL); + 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. + */ + +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; +} +/** + * Update and open the parameter files dialog box + * + * \param junk + */ + +static void DoParamFileListDialog(void *junk) +{ + DoParamFiles(junk); + ParamFileListLoad(paramFileInfo_da.cnt, ¶mFileInfo_da); + +} + +addButtonCallBack_t ParamFilesInit(void) +{ + RegisterChangeNotification(ParamFilesChange); + return &DoParamFileListDialog; +} + +/** + * Get the initial parameter files. The Xtrkcad.xtq file containing scale and + * demo definitions is read. + * + * \return FALSE on error, TRUE otherwise + */ +BOOL_T ParamFileListInit(void) +{ + log_params = LogFindIndex("params"); + + // get the default definitions + if (ReadParams(lParamKey, libDir, sParamQF) == FALSE) { + return FALSE; + } + + curParamFileIndex = PARAM_CUSTOM; + + if (lParamKey == 0) { + LoadParamFileList(); + ReadCustom(); + } + + return TRUE; + +} + +/** + * Deletes all parameter types described by index + * + * \param index Zero-based index of the. + */ + +static void +DeleteAllParamTypes(int index) +{ + + DeleteTurnoutParams(index); + DeleteCarProto(index); + DeleteCarPart(index); + DeleteStructures(index); +} + + +/** + * Unload parameter file: all parameter definitions from this file are deleted + * from memory. Strings allocated to store the filename and contents + * description are free'd as well. + * In order to keep the overall data structures consistent, the file info + * structure is not removed from the array but flagged as garbage + * + * \param fileIndex Zero-based index of the file. + * + * \returns True if it succeeds, false if it fails. + */ + +bool +UnloadParamFile(wIndex_t fileIndex) +{ + paramFileInfo_p paramFileI = ¶mFileInfo(fileIndex); + + DeleteAllParamTypes(fileIndex); + + MyFree(paramFileI->name); + MyFree(paramFileI->contents); + + paramFileI->valid = FALSE; + + for (int i = 0; i < paramFileInfo_da.cnt; i++) { + LOG1(log_params, ("UnloadParamFiles: = %s: %d\n", paramFileInfo(i).contents, + paramFileInfo(i).trackState)) + } + + return (true); +} + +/** + * Reload parameter file + * + * \param index Zero-based index of the paramFileInfo struct. + * + * \returns True if it succeeds, false if it fails. + */ + +bool +ReloadParamFile(wIndex_t index) +{ + paramFileInfo_p paramFileI = ¶mFileInfo(index); + + DeleteAllParamTypes(index); + MyFree(paramFileI->contents); + + ReloadDeletedParamFile(index); + + return(true); +} diff --git a/app/bin/paramfilesearch_ui.c b/app/bin/paramfilesearch_ui.c new file mode 100644 index 0000000..bf9c47a --- /dev/null +++ b/app/bin/paramfilesearch_ui.c @@ -0,0 +1,426 @@ +/** \file paramfilesearch_ui.c + * Parameter File Search Dialog + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2019 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 <ctype.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> + +#include "custom.h" +#include "dynstring.h" +#include "i18n.h" +#include "messages.h" +#include "param.h" +#include "include/partcatalog.h" +#include "paths.h" +#include "include/paramfilelist.h" +#include "fileio.h" +#include "directory.h" + +#include "bitmaps/magnifier.xpm" + +static CatalogEntry *catalogFileBrowse; /**< current search results */ +static TrackLibrary *trackLibrary; /**< Track Library */ +static CatalogEntry *currentCat; /**< catalog being shown */ + +/* define the search / browse dialog */ + +static struct wFilSel_t *searchUi_fs; /**< searchdialog for parameter files */ + +static void SearchUiBrowse(void *junk); +static void SearchUiDefault(void * junk); +static void SearchUiApply(wWin_p junk); +static void SearchUiSelectAll(void *junk); +static void SearchUiDoSearch(void *junk); + +static long searchUiMode = 0; +static paramListData_t searchUiListData = { 10, 370, 0 }; +#define MAXQUERYLENGTH 250 +static char searchUiQuery[MAXQUERYLENGTH]; +static char * searchUiLabels[] = { N_("Show File Names"), NULL }; + +static paramData_t searchUiPLs[] = { +#define I_QUERYSTRING (0) + { PD_STRING, searchUiQuery, "query", PDO_NOPREF | PDO_STRINGLIMITLENGTH, (void*)(340), "", 0, 0, MAXQUERYLENGTH-1 }, +#define I_SEARCHBUTTON (1) + { PD_BUTTON, (void*)SearchUiDoSearch, "find", PDO_DLGHORZ, 0, NULL, BO_ICON, (void *)NULL }, +#define I_MESSAGE (2) + { PD_MESSAGE, N_("Enter at least one search word"), NULL, PDO_DLGBOXEND, (void *)370 }, +#define I_RESULTLIST (3) + { PD_LIST, NULL, "inx", PDO_NOPREF | PDO_DLGRESIZE, &searchUiListData, NULL, BL_DUP|BL_SETSTAY|BL_MANY }, +#define I_MODETOGGLE (4) + { PD_TOGGLE, &searchUiMode, "mode", PDO_DLGBOXEND, searchUiLabels, NULL, BC_HORZ|BC_NOBORDER }, +#define I_APPLYBUTTON (5) + { PD_BUTTON, (void *)SearchUiApply, "apply", PDO_DLGCMDBUTTON, NULL, N_("Add") }, +#define I_SELECTALLBUTTON (6) + { PD_BUTTON, (void*)SearchUiSelectAll, "selectall", PDO_DLGCMDBUTTON, NULL, N_("Select all") }, + { PD_BUTTON, (void*)SearchUiDefault, "default", 0, NULL, N_("Reload Library") }, +}; + +#define SEARCHBUTTON ((wButton_p)searchUiPLs[I_SEARCHBUTTON].control) +#define RESULTLIST ((wList_p)searchUiPLs[I_RESULTLIST].control) +#define APPLYBUTTON ((wButton_p)searchUiPLs[I_APPLYBUTTON].control) +#define SELECTALLBUTTON ((wButton_p)searchUiPLs[I_SELECTALLBUTTON].control) +#define MESSAGETEXT ((wMessage_p)searchUiPLs[I_MESSAGE].control) +#define QUERYSTRING ((wString_p)searchUiPLs[I_QUERYSTRING].control) + +static paramGroup_t searchUiPG = { "searchgui", 0, searchUiPLs, sizeof searchUiPLs/sizeof searchUiPLs[0] }; +static wWin_p searchUiW; + +#define FILESECTION "file" +#define PARAMDIRECTORY "paramdir" + +/** + * Reload the listbox showing the current catalog + */ + +static +void SearchFileListLoad(CatalogEntry *catalog) +{ + CatalogEntry *currentEntry = catalog->next; + DynString description; + DynStringMalloc(&description, STR_SHORT_SIZE); + + wControlShow((wControl_p)RESULTLIST, FALSE); + wListClear(RESULTLIST); + + while (currentEntry != currentEntry->next) { + for (unsigned int i=0;i<currentEntry->files;i++) { + DynStringClear(&description); + DynStringCatCStr(&description, + ((!searchUiMode) && currentEntry->contents) ? + currentEntry->contents : + currentEntry->fullFileName[i]); + + wListAddValue(RESULTLIST, + DynStringToCStr(&description), + NULL, + // indicatorIcons[paramFileInfo.favorite][paramFileInfo.trackState], + (void*)currentEntry->fullFileName[i]); + } + + currentEntry = currentEntry->next; + } + + wControlShow((wControl_p)RESULTLIST, TRUE); + wControlActive((wControl_p)SELECTALLBUTTON, + wListGetCount(RESULTLIST)); + + DynStringFree(&description); + + currentCat = catalog; +} + +/** + * Find parameter files using the file selector + * + * \param junk + */ + +static void SearchUiBrowse(void * junk) +{ + + //EmptyCatalog(catalogFileBrowse); + + wFilSelect(searchUi_fs, GetParamFileDir()); + + //SearchFileListLoad(catalogFileBrowse); + + return; +} + + +/** + * Reload just the system files into the searchable set + */ + +static void SearchUiDefault(void * junk) +{ + + if (!catalogFileBrowse) + catalogFileBrowse = InitCatalog(); + else { + EmptyCatalog(catalogFileBrowse); + } + + if (trackLibrary) + DeleteLibrary(trackLibrary); + + char * parms_path; + + MakeFullpath(&parms_path, wGetAppLibDir(), "params", NULL); + + trackLibrary = CreateLibrary(parms_path); + + SearchFileListLoad(trackLibrary->catalog); //Start with system files + + free(parms_path); + +} + +/** + * Load the selected items of search results + */ + +void static +SearchUILoadResults(void) +{ + char **fileNames; + int files = wListGetSelectedCount(RESULTLIST); + int found = 0; + + if (files) { + fileNames = malloc(sizeof(char *)*files); + if (!fileNames) { + AbortProg("Couldn't allocate memory for result list: %s (%d)", __FILE__, + __LINE__, NULL); + } + + for (int inx = 0; found < files; inx++) { + if (wListGetItemSelected(RESULTLIST, inx)) { + fileNames[found++] = (char *)wListGetItemContext(RESULTLIST, inx); + } + } + + LoadParamFile(files, fileNames, NULL); + free(fileNames); + SearchUiOk((void *) 0); + } + +} + +/** + * Update the action buttons. + * + * If there is at least one selected file, Apply is enabled + * If there are entries in the list, Select All is enabled + * + * \return + */ + +static void UpdateSearchUiButton(void) +{ + wIndex_t selCnt = wListGetSelectedCount(RESULTLIST); + wIndex_t cnt = wListGetCount(RESULTLIST); + + wControlActive((wControl_p)APPLYBUTTON, selCnt > 0); + wControlActive((wControl_p)SELECTALLBUTTON, cnt > 0); +} + +// Return a pointer to the (shifted) trimmed string +char * StringTrim(char *s) +{ + char *original = s; + size_t len = 0; + + while (isspace((unsigned char) *s)) { + s++; + } + if (*s) { + char *p = s; + while (*p) p++; + while (isspace((unsigned char) *(--p))); + p[1] = '\0'; + len = (size_t) (p - s + 1); + } + + return (s == original) ? s : memmove(original, s, len + 1); +} + +/** + * Perform the search. If successful, the results are loaded into the list + * + * \param ptr INignored + */ + +static void SearchUiDoSearch(void * ptr) +{ + unsigned result; + + char * search; + + search = StringTrim(searchUiQuery); + + if (catalogFileBrowse) { + EmptyCatalog(catalogFileBrowse); + } else + catalogFileBrowse = InitCatalog(); + + result = SearchLibrary(trackLibrary, search, catalogFileBrowse); + + if (result) { + DynString hitsMessage; + DynStringMalloc(&hitsMessage, 16); + DynStringPrintf(&hitsMessage, _("%d parameter files found."), result); + wMessageSetValue(MESSAGETEXT, DynStringToCStr(&hitsMessage)); + DynStringFree(&hitsMessage); + + SearchFileListLoad(catalogFileBrowse); + + } else { + + wListClear(RESULTLIST); + wControlActive((wControl_p)SELECTALLBUTTON, FALSE); + wMessageSetValue(MESSAGETEXT, _("No matches found.")); + } +} + +/** + * Select all files in the list + * + * \param junk IN ignored + * \return + */ + +static void SearchUiSelectAll(void *junk) +{ + wListSelectAll(RESULTLIST); +} + +/** + * Action handler for Done button. Hides the dialog. + * + * \param [in,out] junk ignored. + */ + +void SearchUiOk(void * junk) +{ + if (searchUiW) { + wHide(searchUiW); + } +} + +/** + * Handle the Add button: a list of selected list elements is created and + * passed to the parameter file list. + * + * \param junk IN/OUT ignored + */ + +static void SearchUiApply(wWin_p junk) +{ + SearchUILoadResults(); +} + +/** + * Event handling for the Search dialog. If the 'X' decoration is pressed the + * dialog window is closed. + * + * \param pg IN ignored + * \param inx IN ignored + * \param valueP IN ignored + */ + +static void SearchUiDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP) +{ + switch (inx) { + case I_RESULTLIST: + UpdateSearchUiButton(); + break; + case I_MODETOGGLE: + SearchFileListLoad(currentCat); + break; + case -1: + SearchUiOk(valueP); + break; + } +} + +/** + * Get the system default directory for parameter files. First step is to + * check the configuration file for a user specific setting. If that is not + * found, the diretory is based derived from the installation directory. + * The returned string has to be free'd() when no longer needed. + * + * \return parameter file directory + */ + +static char * +GetParamsPath() +{ + char * params_path; + char *params_pref; + params_pref = wPrefGetString(FILESECTION, PARAMDIRECTORY); + + if (!params_pref) { + MakeFullpath(¶ms_path, wGetAppLibDir(), "params", NULL); + } else { + params_path = strdup(params_pref); + } + return(params_path); +} +/** + * Create and open the search dialog. + * + * \param junk + */ + +void DoSearchParams(void * junk) +{ + if (searchUiW == NULL) { + catalogFileBrowse = InitCatalog(); + + //Make the Find menu bound to the System Library initially + char *paramsDir = GetParamsPath(); + trackLibrary = CreateLibrary(paramsDir); + free(paramsDir); + + searchUiPLs[I_SEARCHBUTTON].winLabel = (char *)wIconCreatePixMap(magnifier_xpm); + + ParamRegister(&searchUiPG); + + searchUiW = ParamCreateDialog(&searchUiPG, + MakeWindowTitle(_("Choose parameter files")), _("Done"), NULL, wHide, + TRUE, NULL, F_RESIZE | F_RECALLSIZE, SearchUiDlgUpdate); + if (trackLibrary) { + SearchFileListLoad(trackLibrary->catalog); //Start with system files + } + wControlActive((wControl_p)APPLYBUTTON, FALSE); + wControlActive((wControl_p)SELECTALLBUTTON, FALSE); + + searchUi_fs = wFilSelCreate(searchUiW, FS_LOAD, FS_MULTIPLEFILES, + _("Load Parameters"), _("Parameter files (*.xtp)|*.xtp"), GetParameterFileInfo, + (void *)catalogFileBrowse); + } + + ParamLoadControls(&searchUiPG); + ParamGroupRecord(&searchUiPG); + + if (!trackLibrary) { + wControlActive((wControl_p)SEARCHBUTTON, FALSE); + wControlActive((wControl_p)QUERYSTRING, FALSE); + wMessageSetValue(MESSAGETEXT, + _("No system parameter files found, search is disabled.")); + } else { + wStringSetValue(QUERYSTRING, ""); + + SearchFileListLoad(trackLibrary->catalog); //Start with system files + + } + wShow(searchUiW); +} + + diff --git a/app/bin/partcatalog.c b/app/bin/partcatalog.c new file mode 100644 index 0000000..a1db09c --- /dev/null +++ b/app/bin/partcatalog.c @@ -0,0 +1,846 @@ +/** \file partcatalog.c +* Manage the catalog of track parameter files +*/ +/* XTrkCad - Model Railroad CAD +* Copyright (C) 2019 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 <ctype.h> +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif +#include <search.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef WINDOWS + #include "include/dirent.h" +#else + #include <dirent.h> +#endif +#include "fileio.h" +#include "misc.h" +#include "include/paramfile.h" +#include "include/partcatalog.h" +#include "paths.h" +#include "include/stringxtc.h" +#include "include/utf8convert.h" + +#if _MSC_VER > 1300 + #define strnicmp _strnicmp + #define strdup _strdup +#endif + +#define PUNCTUATION "+-*/.,&%=#" + + + +/** + * Create and initialize the linked list for the catalog entries + * + * \return pointer to first element + */ + +CatalogEntry * +InitCatalog(void) +{ + CatalogEntry *head; + CatalogEntry *tail; + + /* allocate two pseudo nodes for beginning and end of list */ + head = (CatalogEntry *)malloc(sizeof(CatalogEntry)); + tail = (CatalogEntry *)malloc(sizeof(CatalogEntry)); + + head->next = tail; + tail->next = tail; + + + + return (head); +} + +/** + * Create a new CatalogEntry and add it to the linked list. The newly + * created entry is inserted into the list after the given position + * + * \param entry IN insertion point + * \return pointer to new entry + */ + +static CatalogEntry * +InsertIntoCatalogAfter(CatalogEntry *entry) +{ + CatalogEntry *newEntry = (CatalogEntry *)malloc(sizeof(CatalogEntry)); + newEntry->next = entry->next; + entry->next = newEntry; + newEntry->files = 0; + newEntry->contents = NULL; + + return (newEntry); +} + +/** + * Count the elements in the linked list ignoring dummy elements + * + * \param listHeader IN the linked list + * \return the numberof elements + */ + +unsigned +CountCatalogEntries(CatalogEntry *listHeader) +{ + CatalogEntry *currentEntry = listHeader->next; + unsigned count = 0; + + while (currentEntry != currentEntry->next) { + count++; + currentEntry = currentEntry->next; + } + return (count); +} + +/** + * Empty a catalog. All data nodes including their allocated memory are freed. On + * + * \param listHeader IN the list + */ + +void +EmptyCatalog(CatalogEntry *listHeader) +{ + CatalogEntry *current = listHeader; + + while (current->next != current->next->next) { + CatalogEntry *removedElement; + removedElement = current->next; + current->next = current->next->next; + if (removedElement->contents) { + free(removedElement->contents); + } + for (unsigned int i = 0; i < removedElement->files; i++) { + free(removedElement->fullFileName[i]); + } + free(removedElement); + } +} + +/** + * Find the position in the list and add + * + * \param listHeader IN start of list + * \param contents IN contents to include + * + * \return CatalogEntry if found, NULL otherwise + */ + +static CatalogEntry * +InsertInOrder(CatalogEntry *listHeader, const char *contents) +{ + CatalogEntry *currentEntry = listHeader; + + while (currentEntry->next != currentEntry->next->next) { + CatalogEntry *nextEntry = currentEntry->next; + if (XtcStricmp(nextEntry->contents, contents)>0) { + return InsertIntoCatalogAfter(currentEntry); + } + currentEntry = nextEntry; + } + return InsertIntoCatalogAfter(currentEntry); +} +/** + * Get the existing list element for a content + * + * \param listHeader IN start of list + * \param contents IN contents to search + * \param Do we log error messages or not + * + * \return CatalogEntry if found, NULL otherwise + */ + +static CatalogEntry * +IsExistingContents(CatalogEntry *listHeader, const char *contents, BOOL_T silent) +{ + CatalogEntry *currentEntry = listHeader->next; + + while (currentEntry != currentEntry->next) { + if (!XtcStricmp(currentEntry->contents, contents)) { + if (!silent) + printf("%s already exists in %s\n", contents, currentEntry->fullFileName[0]); + return (currentEntry); + } + currentEntry = currentEntry->next; + } + return (NULL); +} + +/** + * Store information about a parameter file. The filename is added to the array + * of files with identical content. If not already present, in addition + * the content is stored + * + * \param entry existing entry to be updated + * \param path filename to add + * \param contents contents description + */ + +static void +UpdateCatalogEntry(CatalogEntry *entry, char *path, char *contents) +{ + if (!entry->contents) { + entry->contents = strdup(contents); + } + + if (entry->files < MAXFILESPERCONTENT) { + entry->fullFileName[entry->files++] = strdup(path); + } else { + AbortProg("Number of file with same content too large!", NULL); + } +} + +/** + * Create the list for the catalog entries + * + * \return + */ + +static CatalogEntry * +CreateCatalog() +{ + CatalogEntry *catalog = InitCatalog(); + + return (catalog); +} + + +static IndexEntry * +CreateIndexTable(unsigned int capacity) +{ + IndexEntry *index = (IndexEntry *)malloc(capacity * sizeof(IndexEntry)); + + return (index); +} + +/** + * Scan opened directory for the next parameter file + * + * \param dir IN opened directory handle + * \param dirName IN name of directory + * \param fileName OUT fully qualified filename + * + * \return TRUE if file found, FALSE if not + */ + +static bool +GetNextParameterFile(DIR *dir, const char *dirName, char **fileName) +{ + bool done = false; + bool res = false; + + /* + * get all files from the directory + */ + while (!done) { + struct stat fileState; + struct dirent *ent; + + ent = readdir(dir); + + if (ent) { + if (!XtcStricmp(FindFileExtension(ent->d_name), "xtp")) { + /* create full file name and get the state for that file */ + MakeFullpath(fileName, dirName, ent->d_name, NULL); + + if (stat(*fileName, &fileState) == -1) { + fprintf(stderr, "Error getting file state for %s\n", *fileName); + continue; + } + + /* ignore any directories */ + if (!(fileState.st_mode & S_IFDIR)) { + done = true; + res = true; + } + } + } else { + done = true; + res = false; + } + } + return (res); +} + +/** + * Scan a directory for parameter files. For each file found the CONTENTS is + * read and added to the list * + * + * \param insertAfter IN starting point for the list of files + * \param dirName IN directory to be scanned + * + * \return pointer to the last element(?) + */ + +static CatalogEntry * +ScanDirectory(CatalogEntry *catalog, const char *dirName) +{ + DIR *d; + CatalogEntry *newEntry = catalog; + + d = opendir(dirName); + if (d) { + char *fileName = NULL; + + while (GetNextParameterFile(d, dirName, &fileName)) { + CatalogEntry *existingEntry; + char *contents = GetParameterFileContent(fileName); + if ((existingEntry = IsExistingContents(catalog, contents,FALSE))) { + printf("Duplicate CONTENTS record in parameter file %s\n", fileName); + if (strcmp(existingEntry->fullFileName[existingEntry->files-1],fileName)) + UpdateCatalogEntry(existingEntry, fileName, contents); + } else { + newEntry = InsertInOrder(catalog,contents); + UpdateCatalogEntry(newEntry, fileName, contents); + } + free(contents); + free(fileName); + fileName = NULL; + } + closedir(d); + } + + return (newEntry); +} + +/** + * Comparison function for IndexEntries used by qsort() + * + * \param entry1 IN + * \param entry2 IN + * \return per C runtime conventions + */ + +static int +CompareIndex(const void *entry1, const void *entry2) +{ + IndexEntry index1 = *(IndexEntry *)entry1; + IndexEntry index2 = *(IndexEntry *)entry2; + return (strcoll(index1.keyWord, index2.keyWord)); +} + +/*! + * Filter keywords. Current rules: + * - single character string that only consist of a punctuation char + * + * \param word IN keyword + * \return true if any rule applies, false otherwise + */ + +bool +FilterKeyword(char *word) +{ + if (strlen(word) == 1 && strpbrk(word, PUNCTUATION )) { + return(true); + } + return(false); +} + +/** + * Create the keyword index from a list of parameter files + * + * \param catalog IN list of parameter files + * \param index IN index table to be filled + * \param pointer IN/OUT array of words that are indexed + * \param capacityOfIndex IN total maximum of keywords + * \return number of indexed keywords + */ +static unsigned +CreateContentsIndex(CatalogEntry *catalog, IndexEntry *index, void** words_array, + unsigned capacityOfIndex) +{ + CatalogEntry *currentEntry = catalog->next; + unsigned totalMemory = 0; + size_t wordCount = 0; + char *wordList; + char *wordListPtr; + + while (currentEntry != currentEntry->next) { + totalMemory += strlen(currentEntry->contents) + 1; + currentEntry = currentEntry->next; + } + + wordList = malloc((totalMemory + 1) * sizeof(char)); + *words_array = (void*)wordList; + + wordListPtr = wordList; + currentEntry = catalog->next; + + while (currentEntry != currentEntry->next) { + char *word; + char *content = strdup(currentEntry->contents); + + word = strtok(content, " \t\n\r"); + while (word && wordCount < capacityOfIndex) { + strcpy(wordListPtr, word); + + char *p = wordListPtr; + for (; *p; ++p) { + *p = tolower(*p); + } + if (!FilterKeyword(wordListPtr)) { + index[wordCount].value = currentEntry; + index[wordCount].keyWord = wordListPtr; + wordListPtr += strlen(word) + 1; + wordCount++; + if (wordCount >= capacityOfIndex) { + AbortProg("Too many keywords were used!", NULL); + } + } + word = strtok(NULL, " \t\n\r"); + } + free(content); + currentEntry = currentEntry->next; + } + *wordListPtr = '\0'; + + qsort((void*)index, wordCount, sizeof(IndexEntry), CompareIndex); + + return (wordCount); +} + +/** +* A recursive binary search function. It returns location of x in +* given array arr[l..r] is present, otherwise -1 +* Taken from http://www.geeksforgeeks.org/binary-search/ and modified +* +* \param arr IN array to search +* \param l IN starting index +* \param r IN highest index in array +* \param key IN key to search +* \return index if found, -1 otherwise +*/ + +static int SearchInIndex(IndexEntry arr[], int l, int r, char *key) +{ + if (r >= l) { + int mid = l + (r - l) / 2; + int res = XtcStricmp(key, arr[mid].keyWord); + + // If the element is present at the middle itself + if (!res) { + return mid; + } + + // If the array size is 1 + if (r == 0) { + return -1; + } + + // If element is smaller than mid, then it can only be present + // in left subarray + if (res < 0) { + return SearchInIndex(arr, l, mid - 1, key); + } + + // Else the element can only be present in right subarray + return SearchInIndex(arr, mid + 1, r, key); + } + + // We reach here when element is not present in array + return -1; +} + +/** + * Inserts a key in arr[] of given capacity. n is current + * size of arr[]. This function returns n+1 if insertion + * is successful, else n. + * Taken from http ://www.geeksforgeeks.org/search-insert-and-delete-in-a-sorted-array/ and modified + */ + +int InsertSorted(CatalogEntry *arr[], int n, CatalogEntry *key, int capacity) +{ + // Cannot insert more elements if n is already + // more than or equal to capcity + if (n >= capacity) { + return n; + } + + int i; + for (i = n - 1; (i >= 0 && arr[i] > key); i--) { + arr[i + 1] = arr[i]; + } + + arr[i + 1] = key; + + return (n + 1); +} + +/** + * Comparison function for CatalogEntries used by qsort() + * + * \param entry1 IN + * \param entry2 IN + * \return per C runtime conventions + */ + +static int +CompareResults(const void *entry1, const void *entry2) +{ + CatalogEntry * index1 = *(CatalogEntry **)entry1; + CatalogEntry * index2 = *(CatalogEntry **)entry2; + return (strcoll(index1->contents, index2->contents)); +} + +/** + * Search the index for a keyword. The index is assumed to be sorted. So after one entry + * is found, neighboring entries up and down are checked as well. The total result set + * is placed into an array and returned. This array has to be free'd by the caller. + * + * \param index IN index list + * \param length IN number of entries index + * \param search IN search string + * \param resultCount OUT count of found entries + * \return array of found catalog entries, NULL if none found + */ + +static int findAll = 1; + +unsigned int +FindWord(IndexEntry *index, int length, char *search, CatalogEntry ***entries) +{ + CatalogEntry **result; //Array of pointers to Catalog Entries + int found; + int foundElements = 0; + *entries = NULL; + + //Get all the entries back for generic search or if "generic find" + if (findAll || !search || (search[0] == '*') || (search[0] == '\0')) { + result = malloc((length) * sizeof(CatalogEntry *)); + for (int i = 0; i < length; i++) { + result[i] = index[i].value; + } + *entries = result; + return length; + } + + found = SearchInIndex(index, 0, length, search); + + if (found >= 0) { + int lower = found; + int upper = found; + int i; + + while (lower > 0 && !XtcStricmp(index[lower-1].keyWord, search)) { + lower--; + } + + while (upper < length - 1 && !XtcStricmp(index[upper + 1].keyWord, search)) { + upper++; + } + + foundElements = 1 + upper - lower; + + result = malloc((foundElements) * sizeof(CatalogEntry *)); + + for (i = 0; i < foundElements; i++) { + result[i] = index[i+lower].value; + } + + qsort((void*)result, foundElements, sizeof(void *), CompareResults); + + *entries = result; + } + return (foundElements); +} + +/** + * Create and initialize the data structure for the track library + * + * \param trackLibrary OUT the newly allocated track library + * \return TRUE on success + */ + +TrackLibrary * +InitLibrary(void) +{ + TrackLibrary *trackLib = malloc(sizeof(TrackLibrary)); + + if (trackLib) { + trackLib->catalog = CreateCatalog(); + trackLib->index = NULL; + trackLib->wordCount = 0; + trackLib->trackTypeCount = 0; + } + + return (trackLib); +} + +/** + * Scan directory and all parameter files found to the catalog + * + * \param trackLib IN the catalog + * \param directory IN directory to scan + * \return number of files found + */ + +bool +GetTrackFiles(TrackLibrary *trackLib, char *directory) +{ + ScanDirectory(trackLib->catalog, directory); + trackLib->trackTypeCount = CountCatalogEntries(trackLib->catalog); + + return (trackLib->trackTypeCount); +} +/** + * Add a list of parameters files to a catalog. This function is + * called when the user selects files in the file selector. + * + * \param files IN count of files + * \param fileName IN array of filenames + * \param data IN pointer to the catalog + * \return alwqys TRUE + */ + +int GetParameterFileInfo( + int files, + char ** fileName, + void * data) +{ + CatalogEntry *catalog = (CatalogEntry *)data; + + assert(fileName != NULL); + assert(files > 0); + assert(data != NULL); + + for (int i = 0; i < files; i++) { + CatalogEntry *newEntry; + char *contents = GetParameterFileContent(fileName[i]); + + if (!(newEntry = IsExistingContents(catalog, contents,TRUE))) { + newEntry = InsertIntoCatalogAfter(catalog); + } + UpdateCatalogEntry(newEntry, fileName[i], contents); + free(contents); + } + return (TRUE); +} + +/** + * Create the search index from the contents description for the whole catalog. + * A fixed number of words are added to the index. See ESTIMATED_CONTENTS_WORDS + * + * \param trackLib IN the catalog + * \return the number of words indexed + */ + +unsigned +CreateLibraryIndex(TrackLibrary *trackLib) +{ + trackLib->index = CreateIndexTable(trackLib->trackTypeCount * + ESTIMATED_CONTENTS_WORDS); + + trackLib->wordCount = CreateContentsIndex(trackLib->catalog, trackLib->index, + &trackLib->words_array, + ESTIMATED_CONTENTS_WORDS * trackLib->trackTypeCount); + + return (trackLib->wordCount); +} + +void +DeleteLibraryIndex(TrackLibrary *trackLib) +{ + free(trackLib->index); + trackLib->index = NULL; + + free(trackLib->words_array); + + trackLib->wordCount = 0; + +} + + +/** + * Create the library and index of parameter files in a given directory. + * + * \param directory IN directory to scan + * \return NULL if error or empty directory, else library handle + */ + +TrackLibrary * +CreateLibrary(char *directory) +{ + TrackLibrary *library; + + library = InitLibrary(); + if (library) { + if (!GetTrackFiles(library, directory)) { + return (NULL); + } + + CreateLibraryIndex(library); + } + return (library); +} + +void +DeleteLibrary(TrackLibrary* library) +{ + DeleteLibraryIndex(library); + + + free(library); +} + +// Case insensitive comparison +char* stristr( const char* haystack, const char* needle ) +{ + int c = tolower((unsigned char)*needle); + if (c == '\0') + return (char *)haystack; + for (; *haystack; haystack++) { + if (tolower((unsigned char)*haystack) == c) { + for (size_t i = 0;;) { + if (needle[++i] == '\0') + return (char *)haystack; + if (tolower((unsigned char)haystack[i]) != tolower((unsigned char)needle[i])) + break; + } + } + } + return NULL; +} + +/** + * Search the library for a keyword string and return the result list + * + * First the index is searched for the first word and then each "hit" is matched + * to the entire search string + * + * Null, Blank and "*" match all entries + * + * The list is de-duped of repeat of filenames as the same file might appear in + * more than once + * + * \param library IN the library + * \param searchExpression IN keyword to search for + * \param resultEntries IN list header for result list + * \return number of found entries + */ +unsigned +SearchLibrary(TrackLibrary *library, char *searchExpression, + CatalogEntry *resultEntries) +{ + CatalogEntry **entries; + CatalogEntry * newEntry = resultEntries; + unsigned entryCount; + + char * word; + + word = strdup(searchExpression); + + //word = strtok(word," \t"); + + if (library->index == NULL || library->wordCount == 0) { + return (0); + } + entryCount = FindWord(library->index, library->wordCount, word, + &entries); + int count= 0; + if (entryCount) { + unsigned int i = 0; + while (i < entryCount) { + char * match; + //Check if entire String Matches + if (!searchExpression || !word || (word[0] == '*') || (word[0] == '\0') || + (match = stristr(entries[i]->contents,searchExpression))) { + CatalogEntry * existingEntry; + existingEntry = IsExistingContents(resultEntries, entries[i]->contents, TRUE); + //Same FileName already in one of the entries? + BOOL_T found = FALSE; + if (existingEntry) { + for (unsigned int j=0;j<existingEntry->files;j++) { + if (!strcmp(existingEntry->fullFileName[j],entries[i]->fullFileName[entries[i]->files-1])) { + found=TRUE; + break; + } + } + if (found == TRUE ) { + i++; + continue; + } + UpdateCatalogEntry(existingEntry, entries[i]->fullFileName[(entries[i]->files- 1)], + entries[i]->contents); + } else { + newEntry = InsertInOrder(resultEntries,entries[i]->contents); + UpdateCatalogEntry(newEntry, entries[i]->fullFileName[(entries[i]->files- 1)], + entries[i]->contents); + } + count++; + } + i++; + } + } + free(word); + if (entries) + free(entries); //Clean-up after search + return (count); +} + +/** + * Get the contents description from a parameter file. Returned string has to be freed after use. + * + * \param file IN xtpfile + * \return pointer to found contents or NULL if not present + */ + +char * +GetParameterFileContent(char *file) +{ + FILE *fh; + char *result = NULL; + + fh = fopen(file, "rt"); + if (fh) { + bool found = false; + + while (!found) { + char buffer[512]; + if (fgets(buffer, sizeof(buffer), fh)) { + char *ptr = strtok(buffer, " \t"); + if (!XtcStricmp(ptr, CONTENTSCOMMAND)) { + /* if found, store the rest of the line and the filename */ + ptr = ptr+strlen(CONTENTSCOMMAND)+1; + ptr = strtok(ptr, "\r\n"); + result = strdup(ptr); +#ifdef WINDOWS + ConvertUTF8ToSystem(result); +#endif // WINDOWS + found = true; + } + } else { + fprintf(stderr, "Nothing found in %s\n", file); + found = true; + } + } + fclose(fh); + } + return(result); +} diff --git a/app/bin/paths.c b/app/bin/paths.c index cbd9b38..6c6bb10 100644 --- a/app/bin/paths.c +++ b/app/bin/paths.c @@ -69,7 +69,7 @@ FindPath(const char *type) } /** - * Add a path to the table. If it already exists, the value ist updated. + * Add a path to the table. If it already exists, the value list updated. * * \param type IN type of path * \param path IN path @@ -162,6 +162,21 @@ char *GetCurrentPath( } /** + * Convert path to forward slash + * + * \param [in,out] string If non-null, the string. + */ + +void ConvertPathForward(char *string) +{ + char *ptr = string; + while ((ptr = strchr(ptr, '\\')) != NULL) { + ptr[0] = '/'; + ptr++; + } +} + +/** * Find the filename/extension piece in a fully qualified path * * \param path IN the full path @@ -183,8 +198,28 @@ char *FindFilename(char *path) } /** + * Find file extension in a filename + * + * \param path IN full or partial path + * \return pointer to the file extension part, empty string if no extension present + */ + +char *FindFileExtension(char *path) { + char *ext; + ext = strrchr(path, '.'); + + if (ext) { + ext++; + } else { + ext = path + strlen(path); + } + + return ext; +} + +/** * Make a full path definition from directorys and filenames. The individual pieces are -* concatinated. Where necessary a path delimiter is added. A pointer to the resulting +* Concatenated. Where necessary a path delimiter is added. A pointer to the resulting * string is returned. This memory should be free'd when no longer needed. * Windows: to construct an absolute path, a leading backslash has to be included after * the drive delimiter ':' or at the beginning of the first directory name. diff --git a/app/bin/paths.h b/app/bin/paths.h index 1e3c98a..f3f5f23 100644 --- a/app/bin/paths.h +++ b/app/bin/paths.h @@ -27,6 +27,8 @@ void SetCurrentPath( const char * pathType, const char * fileName ); char *GetCurrentPath(const char *pathType); +void ConvertPathForward(char *string); char *FindFilename(char *path); +char *FindFileExtension(char *path); void MakeFullpath(char **str, ...); #endif diff --git a/app/bin/shortentext.c b/app/bin/shortentext.c new file mode 100644 index 0000000..6cd16e6 --- /dev/null +++ b/app/bin/shortentext.c @@ -0,0 +1,92 @@ +/** \file stringutils.c + * Some assorted string handling functions + */ + + /* XTrackCAD - Model Railroad CAD + * Copyright (C) 2019 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 <string.h> +#include "shortentext.h" + +#define WHITESPACES "\n\r\t " +#define ELLIPSIZE "..." + +/** + * Replace all whitespace characters with blanks. Successive occurences are reduced to a single blank. + * + * \param source IN string to convert + * \param dest IN buffer for converted string, minimum size is the size of the source string + */ +void +RemoveFormatChars( char *source, char *dest ) +{ + int lastChar = '\0'; + + while (*source) { + if (strchr(WHITESPACES, *source)) { + if (lastChar != ' ') { + *dest++ = ' '; + lastChar = ' '; + } + } else { + lastChar = *source; + *dest++ = lastChar; + } + source++; + } + if (lastChar != ' ') { + *dest = '\0'; + } else { + *(dest - 1) = '\0'; + } +} + +void +EllipsizeString(char *source, char *dest, size_t length) +{ + size_t position; + char *resultString = (dest ? dest: source); + + + // trivial case: nothing to do if source is shorter and no inplace + if( strlen(source) <= length ) + { + if( dest ) + strcpy(dest, source); + return; + } + + strncpy(resultString, source, length); + resultString[ length ] = '\0'; + + position = length - 1; + while (position) { + if (resultString[position] == ' ' && position <= (length - sizeof(ELLIPSIZE))) { + strcpy(resultString + position, ELLIPSIZE); + break; + } else { + position--; + } + } + + // no blank in string, replace the last n chars + if (!position) { + strcpy(resultString + (strlen(resultString) - sizeof(ELLIPSIZE) + 1), ELLIPSIZE); + } + return; +}
\ No newline at end of file diff --git a/app/bin/shortentext.h b/app/bin/shortentext.h new file mode 100644 index 0000000..1b71a08 --- /dev/null +++ b/app/bin/shortentext.h @@ -0,0 +1,28 @@ +/** \file stringutils.h + * String handling utilities + */ + + /* XTrackCAD - Model Railroad CAD + * Copyright (C) 2019 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. + */ + +#ifndef HAVE_STRINGUTILS_H +#define HAVE_STRINGUTILS_H + void RemoveFormatChars(char *source, char *dest); + void EllipsizeString(char *source, char *dest, size_t length); +#endif // !HAVE_STRINGUTIL_H + diff --git a/app/bin/smalldlg.c b/app/bin/smalldlg.c index 7828912..1fb5965 100644 --- a/app/bin/smalldlg.c +++ b/app/bin/smalldlg.c @@ -36,6 +36,7 @@ #ifdef WINDOWS #include <io.h> #include <windows.h> +#include <FreeImage.h> #else #include <sys/stat.h> #endif @@ -67,7 +68,7 @@ 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_TEXT, NULL, "text", 0, &tipTextData, NULL, BO_READONLY|BT_TOP|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 }}; @@ -86,7 +87,7 @@ static void CreateTipW( void ) char *filename; char * cp; - tipW = ParamCreateDialog( &tipPG, MakeWindowTitle(_("Tip of the Day")), _("Ok"), (paramActionOkProc)wHide, NULL, FALSE, NULL, F_CENTER, NULL ); + tipW = ParamCreateDialog( &tipPG, MakeWindowTitle(_("Tip of the Day")), _("Ok"), (paramActionOkProc)wHide, wHide, FALSE, NULL, F_RESIZE|F_CENTER, NULL ); /* open the tip file */ MakeFullpath(&filename, libDir, sTipF, NULL); @@ -167,7 +168,8 @@ void ShowTip( long flags ) } ParamLoadControls( &tipPG ); wTextClear( tipT ); - wPrefGetInteger( "misc", "tip-number", &tipNum, 0 ); + /* initial value is -1 which gets incremented 0 below */ + wPrefGetInteger( "misc", "tip-number", &tipNum, -1 ); if( flags & SHOWTIP_PREVTIP ) { if(tipNum == 0 ) @@ -202,7 +204,7 @@ static paramData_t aboutPLs[] = { { 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 } + { PD_TEXT, NULL, NULL, PDO_DLGRESIZE, &aboutTextData, NULL, BO_READONLY|BT_TOP|BT_CHARUNITS } }; static paramGroup_t aboutPG = { "about", 0, aboutPLs, sizeof aboutPLs/sizeof aboutPLs[0] }; @@ -217,13 +219,18 @@ void CreateAboutW( void *ptr ) 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 ); + aboutW = ParamCreateDialog( &aboutPG, MakeWindowTitle(_("About")), _("Ok"), (paramActionOkProc)wHide, wHide, 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 2017 by Bob Blackwell, Martin Fischer and Adam Richards." ); wTextAppend( COPYRIGHT_T, "\nIcons by: Tango Desktop Project (http://tango.freedesktop.org)"); + wTextAppend(COPYRIGHT_T, "\nSome icons by Yusuke Kamiyamane. Licensed under a Creative Commons Attribution 3.0 License."); wTextAppend( COPYRIGHT_T, "\nContributions by: Robert Heller, Mikko Nissinen, Timothy M. Shead, Daniel Luis Spagnol" ); wTextAppend( COPYRIGHT_T, "\nParameter Files by: Ralph Boyd, Dwayne Ward" ); +#ifdef WINDOWS + wTextAppend(COPYRIGHT_T, "\n"); + wTextAppend(COPYRIGHT_T, FreeImage_GetCopyrightMessage()); +#endif wTextAppend( COPYRIGHT_T, "\nCornu Algorithm and Implementation by: Raph Levien"); wTextAppend( COPYRIGHT_T, "\nuthash Copyright notice:" ); wTextAppend( COPYRIGHT_T, "\nCopyright (c) 2005-2015, Troy D. Hanson http://troydhanson.github.com/uthash/"); diff --git a/app/bin/stringxtc.c b/app/bin/stringxtc.c new file mode 100644 index 0000000..483f1a6 --- /dev/null +++ b/app/bin/stringxtc.c @@ -0,0 +1,106 @@ +/** \file stringxtc.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+ /*
+ * stupid library routines.
+ */
+
+#include <ctype.h>
+#include <stddef.h>
+#include <errno.h>
+#include "include/stringxtc.h"
+
+/**
+ * strscpy - Copy a C-string into a sized buffer
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @count: Size of destination buffer
+ *
+ * Copy the string, or as much of it as fits, into the dest buffer. The
+ * behavior is undefined if the string buffers overlap. The destination
+ * buffer is always NUL terminated, unless it's zero-sized.
+ *
+ * Preferred to strlcpy() since the API doesn't require reading memory
+ * from the src string beyond the specified "count" bytes, and since
+ * the return value is easier to error-check than strlcpy()'s.
+ * In addition, the implementation is robust to the string changing out
+ * from underneath it, unlike the current strlcpy() implementation.
+ *
+ * Preferred to strncpy() since it always returns a valid string, and
+ * doesn't unnecessarily force the tail of the destination buffer to be
+ * zeroed. If zeroing is desired please use strscpy_pad().
+ *
+ * Return: The number of characters copied (not including the trailing
+ * %NUL) or -E2BIG if the destination buffer wasn't big enough.
+ */
+
+size_t strscpy(char *dest, const char *src, size_t count)
+{
+ long res = 0;
+
+ if (count == 0)
+ return -E2BIG;
+
+ while (count) {
+ char c;
+
+ c = src[res];
+ dest[res] = c;
+ if (!c)
+ return res;
+ res++;
+ count--;
+ }
+
+ /* Hit buffer length without finding a NUL; force NUL-termination. */
+ if (res)
+ dest[res - 1] = '\0';
+
+ return -E2BIG;
+}
+
+/**
+ * Convert a string to lower case
+ * Taken from https://stackoverflow.com/questions/23618316/undefined-reference-to-strlwr
+ *
+ * \param str IN string to convert
+ * \return pointer to converted string
+ */
+
+char *
+XtcStrlwr(char *str)
+{
+ unsigned char *p = (unsigned char *)str;
+
+ while (*p) {
+ *p = tolower((unsigned char)*p);
+ p++;
+ }
+
+ return str;
+}
+
+/**
+ * Compare two strings case insensitive
+ * Taken from https://stackoverflow.com/questions/30733786/c99-remove-stricmp-and-strnicmp
+ *
+ * \param a, b IN strings to compare
+ * \return
+ */
+
+int +XtcStricmp(const char *a, const char *b) +{ + int ca, cb; + do { + ca = (unsigned char) *a++; + cb = (unsigned char) *b++; + ca = tolower(toupper(ca)); + cb = tolower(toupper(cb)); + } while ((ca == cb) && (ca != '\0')); + return ca - cb; +} + + diff --git a/app/bin/tbezier.c b/app/bin/tbezier.c index 80feedb..fc949a2 100644 --- a/app/bin/tbezier.c +++ b/app/bin/tbezier.c @@ -54,7 +54,6 @@ EXPORT TRKTYP_T T_BEZIER = -1; EXPORT TRKTYP_T T_BZRLIN = -1; - struct extraData { BezierData_t bezierData; }; @@ -134,17 +133,25 @@ static void ComputeBezierBoundingBox( track_p trk, struct extraData * xx ) DIST_T BezierDescriptionDistance( coOrd pos, - track_p trk ) + track_p trk, + coOrd * dpos, + BOOL_T show_hidden, + BOOL_T * hidden) { struct extraData *xx = GetTrkExtraData(trk); coOrd p1; - - if ( GetTrkType( trk ) != T_BEZIER || ( GetTrkBits( trk ) & TB_HIDEDESC ) != 0 ) + if (hidden) *hidden = FALSE; + if ( GetTrkType( trk ) != T_BEZIER || ((( GetTrkBits( trk ) & TB_HIDEDESC ) != 0 ) && !show_hidden)) return 100000; - p1.x = xx->bezierData.pos[0].x + ((xx->bezierData.pos[3].x-xx->bezierData.pos[0].x)/2) + xx->bezierData.descriptionOff.x; - p1.y = xx->bezierData.pos[0].y + ((xx->bezierData.pos[3].y-xx->bezierData.pos[0].y)/2) + xx->bezierData.descriptionOff.y; - + coOrd offset = xx->bezierData.descriptionOff; + + if (( GetTrkBits( trk ) & TB_HIDEDESC ) != 0 ) offset = zero; + + p1.x = xx->bezierData.pos[0].x + ((xx->bezierData.pos[3].x-xx->bezierData.pos[0].x)/2) + offset.x; + p1.y = xx->bezierData.pos[0].y + ((xx->bezierData.pos[3].y-xx->bezierData.pos[0].y)/2) + offset.y; + if (hidden) *hidden = (GetTrkBits( trk ) & TB_HIDEDESC); + *dpos = p1; return FindDistance( p1, pos ); } @@ -167,8 +174,8 @@ static void DrawBezierDescription( pos.x += xx->bezierData.descriptionOff.x; pos.y += xx->bezierData.descriptionOff.y; fp = wStandardFont( F_TIMES, FALSE, FALSE ); - sprintf( message, _("Bezier Curve: length=%s min radius=%s"), - FormatDistance(xx->bezierData.length), FormatDistance(xx->bezierData.minCurveRadius)); + sprintf( message, _("Bezier: len=%0.2f min_rad=%0.2f"), + xx->bezierData.length, xx->bezierData.minCurveRadius>10000?0.0:xx->bezierData.minCurveRadius); DrawBoxedString( BOX_BOX, d, pos, message, fp, (wFontSize_t)descriptionFontSize, color, 0.0 ); } @@ -180,30 +187,31 @@ STATUS_T BezierDescriptionMove( { struct extraData *xx = GetTrkExtraData(trk); static coOrd p0,p1; - static BOOL_T editState; - wDrawColor color; + static BOOL_T editState = FALSE; + if (GetTrkType(trk) != T_BEZIER) return C_TERMINATE; p0.x = xx->bezierData.pos[0].x + ((xx->bezierData.pos[3].x - xx->bezierData.pos[0].x)/2); p0.y = xx->bezierData.pos[0].y + ((xx->bezierData.pos[3].y - xx->bezierData.pos[0].y)/2); switch (action) { case C_DOWN: + DrawBezierDescription( trk, &mainD, wDrawColorWhite ); case C_MOVE: case C_UP: editState = TRUE; p1 = pos; - color = GetTrkColor( trk, &mainD ); - DrawLine( &mainD, p0, pos, 0, wDrawColorBlack ); xx->bezierData.descriptionOff.x = pos.x - p0.x; xx->bezierData.descriptionOff.y = pos.y - p0.y; if (action == C_UP) { editState = FALSE; + wDrawColor color = GetTrkColor( trk, &mainD ); + DrawBezierDescription( trk, &mainD, color ); } - MainRedraw(); - MapRedraw(); return action==C_UP?C_TERMINATE:C_CONTINUE; case C_REDRAW: - if (editState) - DrawLine( &mainD, p1, p0, 0, wDrawColorBlack ); + if (editState) { + DrawBezierDescription( trk, &tempD, wDrawColorBlue ); + DrawLine( &tempD, p1, p0, 0, wDrawColorBlue ); + } break; @@ -230,8 +238,9 @@ static struct { dynArr_t segs; long width; wDrawColor color; + long lineType; } bezData; -typedef enum { P0, A0, R0, C0, Z0, CP1, CP2, P1, A1, R1, C1, Z1, RA, LN, GR, LY, WI, CO } crvDesc_e; +typedef enum { P0, A0, R0, C0, Z0, CP1, CP2, P1, A1, R1, C1, Z1, RA, LN, GR, LT, WI, CO, LY} crvDesc_e; static descData_t bezDesc[] = { /*P0*/ { DESC_POS, N_("End Pt 1: X,Y"), &bezData.pos[0] }, /*A0*/ { DESC_ANGLE, N_("End Angle"), &bezData.angle[0] }, @@ -248,9 +257,10 @@ static descData_t bezDesc[] = { /*RA*/ { DESC_DIM, N_("MinRadius"), &bezData.radius }, /*LN*/ { DESC_DIM, N_("Length"), &bezData.length }, /*GR*/ { DESC_FLOAT, N_("Grade"), &bezData.grade }, -/*LY*/ { DESC_LAYER, N_("Layer"), &bezData.layerNumber }, +/*LT*/ { DESC_LIST, N_("Line Type"), &bezData.lineType}, /*WI*/ { DESC_LONG, N_("Line Width"), &bezData.width}, /*CO*/ { DESC_COLOR, N_("Line Color"), &bezData.color}, +/*LY*/ { DESC_LAYER, N_("Layer"), &bezData.layerNumber }, { DESC_NULL } }; static void UpdateBezier( track_p trk, int inx, descData_p descUpd, BOOL_T final ) @@ -305,7 +315,7 @@ static void UpdateBezier( track_p trk, int inx, descData_p descUpd, BOOL_T final case Z1: ep = (inx==Z0?0:1); UpdateTrkEndElev( trk, ep, GetTrkEndElevUnmaskedMode(trk,ep), bezData.elev[ep], NULL ); - ComputeElev( trk, 1-ep, FALSE, &bezData.elev[1-ep], NULL ); + ComputeElev( trk, 1-ep, FALSE, &bezData.elev[1-ep], NULL, TRUE ); if ( bezData.length > minLength ) bezData.grade = fabs( (bezData.elev[0]-bezData.elev[1])/bezData.length )*100.0; else @@ -322,6 +332,9 @@ static void UpdateBezier( track_p trk, int inx, descData_p descUpd, BOOL_T final case CO: xx->bezierData.segsColor = bezData.color; break; + case LT: + xx->bezierData.lineType = bezData.lineType; + break; default: AbortProg( "updateBezier: Bad inx %d", inx ); } @@ -398,8 +411,8 @@ static void DescribeBezier( track_p trk, char * str, CSIZE_T len ) bezData.center[1] = params.arcP; if (GetTrkType(trk) == T_BEZIER) { - ComputeElev( trk, 0, FALSE, &bezData.elev[0], NULL ); - ComputeElev( trk, 1, FALSE, &bezData.elev[1], NULL ); + ComputeElev( trk, 0, FALSE, &bezData.elev[0], NULL, FALSE ); + ComputeElev( trk, 1, FALSE, &bezData.elev[1], NULL, FALSE ); if ( bezData.length > minLength ) bezData.grade = fabs( (bezData.elev[0]-bezData.elev[1])/bezData.length )*100.0; @@ -415,9 +428,13 @@ static void DescribeBezier( track_p trk, char * str, CSIZE_T len ) if (GetTrkType(trk) == T_BEZIER) { bezDesc[Z0].mode = EndPtIsDefinedElev(trk,0)?0:DESC_RO; bezDesc[Z1].mode = EndPtIsDefinedElev(trk,1)?0:DESC_RO; + bezDesc[LT].mode = DESC_IGNORE; } - else + else { bezDesc[Z0].mode = bezDesc[Z1].mode = DESC_IGNORE; + bezDesc[LT].mode = 0; + bezData.lineType = xx->bezierData.lineType; + } bezDesc[A0].mode = DESC_RO; bezDesc[A1].mode = DESC_RO; bezDesc[C0].mode = DESC_RO; @@ -434,11 +451,52 @@ static void DescribeBezier( track_p trk, char * str, CSIZE_T len ) if (GetTrkType(trk) == T_BEZIER) DoDescribe( _("Bezier Track"), trk, bezDesc, UpdateBezier ); - else + else { DoDescribe( _("Bezier Line"), trk, bezDesc, UpdateBezier ); + if (bezDesc[LT].control0!=NULL) { + wListClear( (wList_p)bezDesc[LT].control0 ); + wListAddValue( (wList_p)bezDesc[LT].control0, _("Solid"), NULL, (void*)0 ); + wListAddValue( (wList_p)bezDesc[LT].control0, _("Dash"), NULL, (void*)1 ); + wListAddValue( (wList_p)bezDesc[LT].control0, _("Dot"), NULL, (void*)2 ); + wListAddValue( (wList_p)bezDesc[LT].control0, _("DashDot"), NULL, (void*)3 ); + wListAddValue( (wList_p)bezDesc[LT].control0, _("DashDotDot"), NULL, (void*)4 ); + wListAddValue( (wList_p)bezDesc[LT].control0, _("CenterDot"), NULL, (void*)5); + wListAddValue( (wList_p)bezDesc[LT].control0, _("PhantomDot"), NULL, (void*)6 ); + wListSetIndex( (wList_p)bezDesc[LT].control0, bezData.lineType ); + } + } } +EXPORT void SetBezierLineType( track_p trk, int width ) { + if (GetTrkType(trk) == T_BZRLIN) { + struct extraData * xx = GetTrkExtraData(trk); + switch(width) { + case 0: + xx->bezierData.lineType = DRAWLINESOLID; + break; + case 1: + xx->bezierData.lineType = DRAWLINEDASH; + break; + case 2: + xx->bezierData.lineType = DRAWLINEDOT; + break; + case 3: + xx->bezierData.lineType = DRAWLINEDASHDOT; + break; + case 4: + xx->bezierData.lineType = DRAWLINEDASHDOTDOT; + break; + case 5: + xx->bezierData.lineType = DRAWLINECENTER; + break; + case 6: + xx->bezierData.lineType = DRAWLINEPHANTOM; + break; + } + } +} + static DIST_T DistanceBezier( track_p t, coOrd * p ) { struct extraData *xx = GetTrkExtraData(t); @@ -465,34 +523,30 @@ static void DrawBezier( track_p t, drawCmd_p d, wDrawColor color ) if (GetTrkType(t) == T_BZRLIN) { + unsigned long NotSolid = ~(DC_NOTSOLIDLINE); + d->options &= NotSolid; + if (xx->bezierData.lineType == DRAWLINESOLID) {} + else if (xx->bezierData.lineType == DRAWLINEDASH) d->options |= DC_DASH; + else if (xx->bezierData.lineType == DRAWLINEDOT) d->options |= DC_DOT; + else if (xx->bezierData.lineType == DRAWLINEDASHDOT) d->options |= DC_DASHDOT; + else if (xx->bezierData.lineType == DRAWLINEDASHDOTDOT) d->options |= DC_DASHDOTDOT; + else if (xx->bezierData.lineType == DRAWLINECENTER) d->options |= DC_CENTER; + else if (xx->bezierData.lineType == DRAWLINEPHANTOM) d->options |= DC_PHANTOM; DrawSegsO(d,t,zero,0.0,xx->bezierData.arcSegs.ptr,xx->bezierData.arcSegs.cnt, 0.0, color, 0); + d->options &= NotSolid; return; } - if (GetTrkWidth(t) == 2) - widthOptions |= DTS_THICK2; - if (GetTrkWidth(t) == 3) - widthOptions |= DTS_THICK3; - - - if ( ((d->funcs->options&wDrawOptTemp)==0) && + if ( ((d->options&DC_SIMPLE)==0) && (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) && labelScale >= d->scale && ( GetTrkBits( t ) & TB_HIDEDESC ) == 0 ) { DrawBezierDescription( t, d, color ); } DIST_T 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,t,zero,0.0,xx->bezierData.arcSegs.ptr,xx->bezierData.arcSegs.cnt, GetTrkGauge(t), color, widthOptions|DTS_TIES); DrawSegsO(d,t,zero,0.0,xx->bezierData.arcSegs.ptr,xx->bezierData.arcSegs.cnt, 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 ); - } + DrawEndPt( d, t, 0, color ); + DrawEndPt( d, t, 1, color ); } static void DeleteBezier( track_p t ) @@ -505,6 +559,7 @@ static void DeleteBezier( track_p t ) if (s.bezSegs.ptr) MyFree(s.bezSegs.ptr); s.bezSegs.max = 0; s.bezSegs.cnt = 0; + s.bezSegs.ptr = NULL; } } if (xx->bezierData.arcSegs.ptr && !xx->bezierData.arcSegs.max) @@ -522,13 +577,14 @@ static BOOL_T WriteBezier( track_p t, FILE * f ) BOOL_T track =(GetTrkType(t)==T_BEZIER); options = GetTrkWidth(t) & 0x0F; if ( ( GetTrkBits(t) & TB_HIDEDESC ) == 0 ) options |= 0x80; - rc &= fprintf(f, "%s %d %u %ld %ld %0.6f %s %d %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f 0 %0.6f %0.6f \n", + rc &= fprintf(f, "%s %d %u %ld %ld %0.6f %s %d %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %d %0.6f %0.6f \n", track?"BEZIER":"BZRLIN",GetTrkIndex(t), GetTrkLayer(t), (long)options, wDrawGetRGB(xx->bezierData.segsColor), xx->bezierData.segsWidth, - GetTrkScaleName(t), GetTrkVisible(t), + GetTrkScaleName(t), GetTrkVisible(t)|(GetTrkNoTies(t)?1<<2:0)|(GetTrkBridge(t)?1<<3:0), xx->bezierData.pos[0].x, xx->bezierData.pos[0].y, xx->bezierData.pos[1].x, xx->bezierData.pos[1].y, xx->bezierData.pos[2].x, xx->bezierData.pos[2].y, xx->bezierData.pos[3].x, xx->bezierData.pos[3].y, + xx->bezierData.lineType, xx->bezierData.descriptionOff.x, xx->bezierData.descriptionOff.y )>0; if (track) { rc &= WriteEndPt( f, t, 0 ); @@ -538,7 +594,7 @@ static BOOL_T WriteBezier( track_p t, FILE * f ) return rc; } -static void ReadBezier( char * line ) +static BOOL_T ReadBezier( char * line ) { struct extraData *xx; track_p t; @@ -548,20 +604,23 @@ static void ReadBezier( char * line ) char scale[10]; wIndex_t layer; long options; + int lt; char * cp = NULL; unsigned long rgb; DIST_T width; - if (!GetArgs( line+6, "dLluwsdpppp0p", - &index, &layer, &options, &rgb, &width, scale, &visible, &p0, &c1, &c2, &p1, &dp ) ) { - return; + TRKTYP_T trkTyp = strncmp(line,"BEZIER",6)==0?T_BEZIER:T_BZRLIN; + if (!GetArgs( line+6, "dLluwsdppppdp", + &index, &layer, &options, &rgb, &width, scale, &visible, &p0, &c1, &c2, &p1, <, &dp ) ) { + return FALSE; } - if (strncmp(line,"BEZIER",6)==0) - t = NewTrack( index, T_BEZIER, 0, sizeof *xx ); - else - t = NewTrack( index, T_BZRLIN, 0, sizeof *xx ); + if ( !ReadSegs() ) + return FALSE; + t = NewTrack( index, trkTyp, 0, sizeof *xx ); xx = GetTrkExtraData(t); - SetTrkVisible(t, visible); + SetTrkVisible(t, visible&2); + SetTrkNoTies(t,visible&4); + SetTrkBridge(t,visible&8); SetTrkScale(t, LookupScale(scale)); SetTrkLayer(t, layer ); SetTrkWidth(t, (int)(options&0x0F)); @@ -570,15 +629,16 @@ static void ReadBezier( char * line ) xx->bezierData.pos[1] = c1; xx->bezierData.pos[2] = c2; xx->bezierData.pos[3] = p1; + xx->bezierData.lineType = lt; xx->bezierData.descriptionOff = dp; xx->bezierData.segsWidth = width; xx->bezierData.segsColor = wDrawFindColor( rgb ); - ReadSegs(); FixUpBezier(xx->bezierData.pos,xx,GetTrkType(t) == T_BEZIER); ComputeBezierBoundingBox(t,xx); if (GetTrkType(t) == T_BEZIER) { SetEndPts(t,2); } + return TRUE; } static void MoveBezier( track_p trk, coOrd orig ) @@ -665,16 +725,21 @@ static BOOL_T SplitBezier( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover if (track) { trk1 = NewBezierTrack(ep?newr:newl,NULL,0); + //Move elev data from ep } else trk1 = NewBezierLine(ep?newr:newl,NULL,0, xx->bezierData.segsColor,xx->bezierData.segsWidth); - UndoModify(trk); + DIST_T height; + int opt; + GetTrkEndElev(trk,ep,&opt,&height); + UpdateTrkEndElev( trk1, ep, opt, height, (opt==ELEV_STATION)?GetTrkEndElevStation(trk,ep):NULL ); + UndoModify(trk); for (int i=0;i<4;i++) { xx->bezierData.pos[i] = ep?newl[i]:newr[i]; } FixUpBezier(xx->bezierData.pos,xx,track); ComputeBezierBoundingBox(trk,xx); SetTrkEndPoint( trk, ep, xx->bezierData.pos[ep?3:0], ep?xx->bezierData.a1:xx->bezierData.a0); - + UpdateTrkEndElev( trk, ep, ELEV_NONE, 0, NULL); *leftover = trk1; *ep0 = 1-ep; *ep1 = ep; @@ -831,7 +896,6 @@ static BOOL_T MergeBezier( } DrawNewTrack( trk0 ); - return TRUE; } @@ -842,7 +906,8 @@ static BOOL_T EnumerateBezier( track_p trk ) if (trk != NULL) { DIST_T d; struct extraData *xx = GetTrkExtraData(trk); - d = xx->bezierData.length; + d = max(BezierOffsetLength(xx->bezierData.arcSegs,-GetTrkGauge(trk)/2.0), + BezierOffsetLength(xx->bezierData.arcSegs,GetTrkGauge(trk)/2.0)); ScaleLengthIncrement( GetTrkScale(trk), d ); } return TRUE; @@ -884,20 +949,72 @@ static BOOL_T GetParamsBezier( int inx, track_p trk, coOrd pos, trackParams_t * params->arcA0 = segPtr->u.c.a0; params->arcA1 = segPtr->u.c.a1; } - if ( inx == PARAMS_PARALLEL ) { - params->ep = 0; - } else if (inx == PARAMS_CORNU ){ + if ( inx == PARAMS_NODES ) { + if (GetTrkType(trk) == T_BEZIER) return FALSE; + if (FindDistance(pos,params->bezierPoints[0]) > FindDistance(pos,params->bezierPoints[3])) + params->ep = 1; + else params->ep = 0; + coOrd curr_pos = params->bezierPoints[params->ep*3]; + BOOL_T first = TRUE; + DYNARR_RESET(coOrd,params->nodes); + for (int i = 0; i<xx->bezierData.arcSegs.cnt;i++) { + trkSeg_p segPtr = &DYNARR_N(trkSeg_t,xx->bezierData.arcSegs,params->ep?xx->bezierData.arcSegs.cnt-1-i:i); + if (segPtr->type == SEG_STRLIN) { + BOOL_T eps = FindDistance(segPtr->u.l.pos[0],curr_pos)>FindDistance(segPtr->u.l.pos[1],curr_pos); + if (first) { + first = FALSE; + DYNARR_APPEND(coOrd,params->nodes,1); + DYNARR_LAST(coOrd,params->nodes) = segPtr->u.l.pos[eps]; + } + DYNARR_APPEND(coOrd,params->nodes,1); + DYNARR_LAST(coOrd,params->nodes) = segPtr->u.l.pos[1-eps]; + } else { + coOrd start,end; + Translate(&start,segPtr->u.c.center,segPtr->u.c.a0,fabs(segPtr->u.c.radius)); + Translate(&end,segPtr->u.c.center,segPtr->u.c.a0+segPtr->u.c.a1,fabs(segPtr->u.c.radius)); + BOOL_T back = FindDistance(start,curr_pos)>FindDistance(end,curr_pos); + if (segPtr->u.c.radius > 0.5) { + double min_angle = 360*acos(1.0-(0.1/fabs(segPtr->u.c.radius)))/M_PI; //Error max is 0.1" + double number = ceil(segPtr->u.c.a1/min_angle); + double arc_size = segPtr->u.c.a1/number; + for (int j=1-first;j<=number;j++) { + DYNARR_APPEND(coOrd,params->nodes,1); + if (back == params->ep) + Translate(&DYNARR_LAST(coOrd,params->nodes),segPtr->u.c.center,segPtr->u.c.a0+segPtr->u.c.a1-(j*arc_size),fabs(segPtr->u.c.radius) ); + else + Translate(&DYNARR_LAST(coOrd,params->nodes),segPtr->u.c.center,segPtr->u.c.a0+(j*arc_size),fabs(segPtr->u.c.radius) ); + } + first = FALSE; + } else { + if (first) { + first = FALSE; + DYNARR_APPEND(coOrd,params->nodes,1); + DYNARR_LAST(coOrd,params->nodes) = start; + } + DYNARR_APPEND(coOrd,params->nodes,1); + DYNARR_LAST(coOrd,params->nodes) = end; + + } + } + curr_pos = DYNARR_LAST(coOrd,params->nodes); + } + params->lineOrig = params->bezierPoints[params->ep*3]; + params->lineEnd = params->bezierPoints[(1-params->ep)*3]; + return TRUE; + } else if ((inx == PARAMS_CORNU) || (inx == PARAMS_1ST_JOIN) || (inx == PARAMS_2ND_JOIN)){ params->ep = PickEndPoint( pos, trk); } else { params->ep = PickUnconnectedEndPointSilent( pos, trk); } if (params->ep>=0) params->angle = GetTrkEndAngle(trk, params->ep); + return TRUE; } -static BOOL_T TrimBezier( track_p trk, EPINX_T ep, DIST_T dist ) { +static BOOL_T TrimBezier( track_p trk, EPINX_T ep, DIST_T dist, coOrd endpos, ANGLE_T angle, DIST_T radius, coOrd center ) { + UndrawNewTrack( trk ); DeleteTrack(trk, TRUE); return TRUE; } @@ -916,7 +1033,7 @@ static BOOL_T QueryBezier( track_p trk, int query ) return TRUE; break; case Q_EXCEPTION: - return GetTrkType(trk) == T_BEZIER?xx->bezierData.minCurveRadius < (GetLayoutMinTrackRadius()-EPSILON):FALSE; + return GetTrkType(trk) == T_BEZIER?fabs(xx->bezierData.minCurveRadius) < (GetLayoutMinTrackRadius()-EPSILON):FALSE; break; case Q_CAN_MODIFY_CONTROL_POINTS: return TRUE; @@ -928,11 +1045,13 @@ static BOOL_T QueryBezier( track_p trk, int query ) return GetTrkType(trk) == T_BEZIER?TRUE:FALSE; break; case Q_CAN_PARALLEL: - return (GetTrkType(trk) == T_BEZIER); + return TRUE; break; case Q_MODIFY_CAN_SPLIT: case Q_CORNU_CAN_MODIFY: return (GetTrkType(trk) == T_BEZIER); + case Q_GET_NODES: + return (GetTrkType(trk) == T_BZRLIN); default: return FALSE; } @@ -980,19 +1099,76 @@ BOOL_T GetBezierSegmentFromTrack(track_p trk, trkSeg_p seg_p) { seg_p->bezSegs.cnt = 0; if (seg_p->bezSegs.ptr) MyFree(seg_p->bezSegs.ptr); seg_p->bezSegs.max = 0; + seg_p->bezSegs.ptr = NULL; FixUpBezierSeg(seg_p->u.b.pos,seg_p,seg_p->type == SEG_BEZTRK); return TRUE; } +BOOL_T GetTracksFromBezierSegment(trkSeg_p bezSeg, track_p newTracks[2], track_p trk) { + track_p trk_old = NULL; + newTracks[0] = NULL, newTracks[1] = NULL; + if (bezSeg->type != SEG_BEZTRK) return FALSE; + for (int i=0;i<bezSeg->bezSegs.cnt;i++) { + trkSeg_p seg = &DYNARR_N(trkSeg_t,bezSeg->bezSegs,i); + track_p new_trk; + if (seg->type == SEG_CRVTRK) + new_trk = NewCurvedTrack(seg->u.c.center,fabs(seg->u.c.radius),seg->u.c.a0,seg->u.c.a1,0); + else if (seg->type == SEG_STRTRK) + new_trk = NewStraightTrack(seg->u.l.pos[0],seg->u.l.pos[1]); + if (newTracks[0] == NULL) newTracks[0] = new_trk; + CopyAttributes( trk, new_trk ); + newTracks[1] = new_trk; + if (trk_old) { + for (int i=0;i<2;i++) { + if (GetTrkEndTrk(trk_old,i)==NULL) { + coOrd pos = GetTrkEndPos(trk_old,i); + EPINX_T ep_n = PickUnconnectedEndPoint(pos,new_trk); + if (connectDistance >= FindDistance(GetTrkEndPos(trk_old,i),GetTrkEndPos(new_trk,ep_n))) { + ConnectTracks(trk_old,i,new_trk,ep_n); + break; + } + } + } + } + trk_old = new_trk; + } + return TRUE; +} + +BOOL_T GetTracksFromBezierTrack(track_p trk, track_p newTracks[2]) { + trkSeg_t seg_temp; + struct extraData * xx = GetTrkExtraData(trk); + newTracks[0] = NULL, newTracks[1] = NULL; + + if (!IsTrack(trk)) return FALSE; + seg_temp.type = SEG_BEZTRK; + for (int i=0;i<4;i++) seg_temp.u.b.pos[i] = xx->bezierData.pos[i]; + seg_temp.color = xx->bezierData.segsColor; + seg_temp.bezSegs.cnt = 0; + seg_temp.bezSegs.max = 0; + //if (seg_temp->bezSegs.ptr) MyFree(seg_temp->bezSegs.ptr); + DYNARR_RESET(trkSeg_t,seg_temp.bezSegs); + FixUpBezierSeg(seg_temp.u.b.pos,&seg_temp,TRUE); + GetTracksFromBezierSegment(&seg_temp, newTracks, trk); + MyFree(seg_temp.bezSegs.ptr); + seg_temp.bezSegs.cnt = 0; + seg_temp.bezSegs.max = 0; + seg_temp.bezSegs.ptr = NULL; + return TRUE; + +} + static BOOL_T MakeParallelBezier( track_p trk, coOrd pos, DIST_T sep, + DIST_T factor, track_p * newTrkR, coOrd * p0R, - coOrd * p1R ) + coOrd * p1R, + BOOL_T track) { struct extraData * xx = GetTrkExtraData(trk); coOrd np[4], p; @@ -1011,7 +1187,8 @@ static BOOL_T MakeParallelBezier( for (int i =0; i<4;i++) { np[i] = xx->bezierData.pos[i]; } - + sep = sep+factor/xx->bezierData.minCurveRadius; + // Adjust sep based on radius and factor if ( a2 > 180 ) { Translate(&np[0],np[0],a+90,sep); Translate(&np[1],np[1],a+90,sep); @@ -1025,14 +1202,18 @@ static BOOL_T MakeParallelBezier( } if ( newTrkR ) { - *newTrkR = NewBezierTrack( np, NULL, 0); + if (track) + *newTrkR = NewBezierTrack( np, NULL, 0); + else + *newTrkR = NewBezierLine( np, NULL, 0, wDrawColorBlack, 0); } else { DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); tempSegs(0).color = wDrawColorBlack; tempSegs(0).width = 0; tempSegs_da.cnt = 1; - tempSegs(0).type = SEG_BEZTRK; + tempSegs(0).type = track?SEG_BEZTRK:SEG_BEZLIN; if (tempSegs(0).bezSegs.ptr) MyFree(tempSegs(0).bezSegs.ptr); + tempSegs(0).bezSegs.ptr = 0; tempSegs(0).bezSegs.max = 0; tempSegs(0).bezSegs.cnt = 0; for (int i=0;i<4;i++) tempSegs(0).u.b.pos[i] = np[i]; @@ -1063,14 +1244,40 @@ BOOL_T MoveBezierEndPt ( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d0 ) { track_p trk2; struct extraData *xx; if (SplitTrack(*trk,pos,*ep,&trk2,TRUE)) { - if (trk2) DeleteTrack(trk2,TRUE); + if (trk2) { + UndrawNewTrack( trk2 ); + DeleteTrack(trk2,TRUE); + } + UndrawNewTrack( *trk ); xx = GetTrkExtraData(*trk); SetTrkEndPoint( *trk, *ep, *ep?xx->bezierData.pos[3]:xx->bezierData.pos[0], *ep?xx->bezierData.a1:xx->bezierData.a0 ); + DrawNewTrack( *trk ); return TRUE; } return FALSE; } +static wBool_t CompareBezier( track_cp trk1, track_cp trk2 ) +{ + struct extraData *xx1 = GetTrkExtraData( trk1 ); + struct extraData *xx2 = GetTrkExtraData( trk2 ); + char * cp = message + strlen(message); + REGRESS_CHECK_POS( "Pos[0]", xx1, xx2, bezierData.pos[0] ) + REGRESS_CHECK_POS( "Pos[1]", xx1, xx2, bezierData.pos[1] ) + REGRESS_CHECK_POS( "Pos[2]", xx1, xx2, bezierData.pos[2] ) + REGRESS_CHECK_POS( "Pos[3]", xx1, xx2, bezierData.pos[3] ) + REGRESS_CHECK_DIST( "MinCurveRadius", xx1, xx2, bezierData.minCurveRadius ) + REGRESS_CHECK_ANGLE( "A0", xx1, xx2, bezierData.a0 ) + REGRESS_CHECK_ANGLE( "A1", xx1, xx2, bezierData.a1 ) + // Check arcSegs + REGRESS_CHECK_DIST( "Length", xx1, xx2, bezierData.length ) + REGRESS_CHECK_POS( "DescOff", xx1, xx2, bezierData.descriptionOff ) + REGRESS_CHECK_WIDTH( "SegsWidth", xx1, xx2, bezierData.segsWidth ) + REGRESS_CHECK_COLOR( "SegsColor", xx1, xx2, bezierData.segsColor ) + REGRESS_CHECK_INT( "LineType", xx1, xx2, bezierData.lineType ) + return TRUE; +} + static trackCmd_t bezlinCmds = { "BZRLIN", DrawBezier, @@ -1102,7 +1309,11 @@ static trackCmd_t bezlinCmds = { NULL, NULL, NULL, - RebuildBezier + RebuildBezier, + NULL, + NULL, + NULL, + CompareBezier }; static trackCmd_t bezierCmds = { @@ -1136,7 +1347,11 @@ static trackCmd_t bezierCmds = { NULL, MakeParallelBezier, NULL, - RebuildBezier + RebuildBezier, + NULL, + NULL, + NULL, + CompareBezier }; @@ -1302,8 +1517,38 @@ LOG( log_bezierSegments, 1, ( " BezTr-Exit2 --> SI%d A%0.3f P[%0.3f %0.3f] D% } break; - case SEGPROC_SPLIT: - //TODO Split + case SEGPROC_SPLIT: ; + wIndex_t subinx; + double t; + double dd; + coOrd split_p = data->split.pos; + ANGLE_T angle = GetAngleSegs(segPtr->bezSegs.cnt,(trkSeg_p)segPtr->bezSegs.ptr, &split_p, &inx, &dd, &back, &subinx, NULL); + coOrd current[4]; + + BezierMathDistance(&split_p, segPtr->u.b.pos, 500, &t); //Find t value + + for (int i=0;i<4;i++) { + current[i] = segPtr->u.b.pos[i]; + + } + for (int i=0;i<2;i++) { + data->split.newSeg[i].type = segPtr->type; + data->split.newSeg[i].color = segPtr->color; + data->split.newSeg[i].width = segPtr->width; + data->split.newSeg[i].bezSegs.ptr = NULL; + data->split.newSeg[i].bezSegs.cnt = 0; + data->split.newSeg[i].bezSegs.max = 0; + } + BezierSplit(segPtr->u.b.pos, data->split.newSeg[0].u.b.pos, data->split.newSeg[1].u.b.pos, t); + + FixUpBezierSeg(data->split.newSeg[0].u.b.pos,&data->split.newSeg[0],segPtr->type == SEG_BEZTRK); + FixUpBezierSeg(data->split.newSeg[1].u.b.pos,&data->split.newSeg[1],segPtr->type == SEG_BEZTRK); + + data->split.length[0] = data->split.newSeg[0].u.b.length; + data->split.length[1] = data->split.newSeg[1].u.b.length; + + data->split.pos = split_p; + break; case SEGPROC_GETANGLE: diff --git a/app/bin/tbezier.h b/app/bin/tbezier.h index 1e8b915..823992e 100644 --- a/app/bin/tbezier.h +++ b/app/bin/tbezier.h @@ -32,6 +32,7 @@ typedef struct { coOrd descriptionOff; DIST_T segsWidth; wDrawColor segsColor; + drawLineType_e lineType; } BezierData_t; @@ -51,7 +52,10 @@ void FixUpBezier(coOrd[4], struct extraData*, BOOL_T); void FixUpBezierSeg(coOrd[4], trkSeg_p , BOOL_T); void FixUpBezierSegs(trkSeg_p p,int segCnt); BOOL_T GetBezierSegmentFromTrack(track_p, trkSeg_p); +BOOL_T GetTracksFromBezierTrack(track_p trk, track_p newTracks[2]); +BOOL_T GetTracksFromBezierSegment(trkSeg_p bezSeg, track_p newTracks[2], track_p old); +void SetBezierLineType( track_p trk, int width ); -DIST_T BezierDescriptionDistance(coOrd pos,track_p trk ); +DIST_T BezierDescriptionDistance(coOrd pos,track_p trk, coOrd *, BOOL_T, BOOL_T * ); STATUS_T BezierDescriptionMove(track_p trk,wAction_t action,coOrd pos ); diff --git a/app/bin/tcornu.c b/app/bin/tcornu.c index 74a7a5e..dd09cfa 100644 --- a/app/bin/tcornu.c +++ b/app/bin/tcornu.c @@ -136,8 +136,8 @@ EXPORT char * CreateSegPathList(track_p trk) { char * cp = "\0\0"; if (GetTrkType(trk) != T_CORNU) return cp; struct extraData *xx = GetTrkExtraData(trk); - if (xx->cornuData.cornuPath) free(xx->cornuData.cornuPath); - xx->cornuData.cornuPath = malloc(xx->cornuData.arcSegs.cnt+2); + if (xx->cornuData.cornuPath) MyFree(xx->cornuData.cornuPath); + xx->cornuData.cornuPath = MyMalloc(xx->cornuData.arcSegs.cnt+2); int j= 0; for (int i = 0;i<xx->cornuData.arcSegs.cnt;i++,j++) { xx->cornuData.cornuPath[j] = i+1; @@ -178,17 +178,24 @@ static void ComputeCornuBoundingBox( track_p trk, struct extraData * xx ) DIST_T CornuDescriptionDistance( coOrd pos, - track_p trk ) + track_p trk, + coOrd * dpos, + BOOL_T show_hidden, + BOOL_T * hidden) { struct extraData *xx = GetTrkExtraData(trk); coOrd p1; - - if ( GetTrkType( trk ) != T_CORNU || ( GetTrkBits( trk ) & TB_HIDEDESC ) != 0 ) + if (hidden) *hidden = FALSE; + if ( GetTrkType( trk ) != T_CORNU || ((( GetTrkBits( trk ) & TB_HIDEDESC ) != 0) && !show_hidden) ) return 100000; - p1.x = xx->cornuData.pos[0].x + ((xx->cornuData.pos[1].x-xx->cornuData.pos[0].x)/2) + xx->cornuData.descriptionOff.x; - p1.y = xx->cornuData.pos[0].y + ((xx->cornuData.pos[1].y-xx->cornuData.pos[0].y)/2) + xx->cornuData.descriptionOff.y; - + coOrd offset = xx->cornuData.descriptionOff; + + if (( GetTrkBits( trk ) & TB_HIDEDESC ) != 0) offset = zero; + p1.x = xx->cornuData.pos[0].x + ((xx->cornuData.pos[1].x-xx->cornuData.pos[0].x)/2) + offset.x; + p1.y = xx->cornuData.pos[0].y + ((xx->cornuData.pos[1].y-xx->cornuData.pos[0].y)/2) + offset.y; + if (hidden) *hidden = (GetTrkBits( trk ) & TB_HIDEDESC); + *dpos = p1; return FindDistance( p1, pos ); } @@ -211,8 +218,9 @@ static void DrawCornuDescription( pos.x += xx->cornuData.descriptionOff.x; pos.y += xx->cornuData.descriptionOff.y; fp = wStandardFont( F_TIMES, FALSE, FALSE ); - sprintf( message, _("Cornu Curve: length=%0.3f min radius=%0.3f"), - xx->cornuData.length, xx->cornuData.minCurveRadius); + + sprintf( message, _("Cornu: len=%0.2f min_rad=%0.2f"), + xx->cornuData.length, (xx->cornuData.minCurveRadius>=10000.00)?0.0:xx->cornuData.minCurveRadius); DrawBoxedString( BOX_BOX, d, pos, message, fp, (wFontSize_t)descriptionFontSize, color, 0.0 ); } @@ -225,7 +233,6 @@ STATUS_T CornuDescriptionMove( struct extraData *xx = GetTrkExtraData(trk); static coOrd p0,p1; static BOOL_T editState; - wDrawColor color; if (GetTrkType(trk) != T_CORNU) return C_TERMINATE; @@ -234,24 +241,25 @@ STATUS_T CornuDescriptionMove( switch (action) { case C_DOWN: + DrawCornuDescription( trk, &mainD, wDrawColorWhite ); case C_MOVE: case C_UP: editState = TRUE; p1 = pos; - color = GetTrkColor( trk, &mainD ); xx->cornuData.descriptionOff.x = pos.x - p0.x; xx->cornuData.descriptionOff.y = pos.y - p0.y; - DrawCornuDescription( trk, &mainD, color ); if (action == C_UP) { editState = FALSE; + wDrawColor color = GetTrkColor( trk, &mainD ); + DrawCornuDescription( trk, &mainD, color ); } - MainRedraw(); - MapRedraw(); return action==C_UP?C_TERMINATE:C_CONTINUE; case C_REDRAW: - if (editState) - DrawLine( &mainD, p1, p0, 0, wDrawColorBlack ); + if (editState) { + DrawCornuDescription( trk, &tempD, wDrawColorBlue ); + DrawLine( &tempD, p1, p0, 0, wDrawColorBlue ); + } break; } @@ -365,18 +373,20 @@ static void UpdateCornu( track_p trk, int inx, descData_p descUpd, BOOL_T final case R0: if (GetTrkEndTrk(trk,0)) break; updateEndPts = TRUE; - xx->cornuData.r[0] = cornData.radius[0]; - Translate(&xx->cornuData.c[0],xx->cornuData.pos[0],NormalizeAngle(xx->cornuData.a[0]+90),xx->cornuData.r[0]); + xx->cornuData.r[0] = fabs(cornData.radius[0]); + Translate(&xx->cornuData.c[0],xx->cornuData.pos[0],NormalizeAngle(xx->cornuData.a[0]+90),cornData.radius[0]); cornData.center[0] = xx->cornuData.c[0]; + cornData.radius[0] = fabs(cornData.radius[0]); cornuDesc[R0].mode |= DESC_CHANGE; cornuDesc[C0].mode |= DESC_CHANGE; break; case R1: if (GetTrkEndTrk(trk,1)) break; updateEndPts = TRUE; - xx->cornuData.r[1]= cornData.radius[1]; - Translate(&xx->cornuData.c[1],xx->cornuData.pos[1],NormalizeAngle(xx->cornuData.a[1]-90),xx->cornuData.r[1]); + xx->cornuData.r[1]= fabs(cornData.radius[1]); + Translate(&xx->cornuData.c[1],xx->cornuData.pos[1],NormalizeAngle(xx->cornuData.a[1]-90),cornData.radius[1]); cornData.center[1] = xx->cornuData.c[1]; + cornData.radius[1] = fabs(cornData.radius[1]); cornuDesc[R1].mode |= DESC_CHANGE; cornuDesc[C1].mode |= DESC_CHANGE; break; @@ -384,7 +394,7 @@ static void UpdateCornu( track_p trk, int inx, descData_p descUpd, BOOL_T final case Z1: ep = (inx==Z0?0:1); UpdateTrkEndElev( trk, ep, GetTrkEndElevUnmaskedMode(trk,ep), cornData.elev[ep], NULL ); - ComputeElev( trk, 1-ep, FALSE, &cornData.elev[1-ep], NULL ); + ComputeElev( trk, 1-ep, FALSE, &cornData.elev[1-ep], NULL, TRUE ); if ( cornData.length > minLength ) cornData.grade = fabs( (cornData.elev[0]-cornData.elev[1])/cornData.length )*100.0; else @@ -417,7 +427,7 @@ static void UpdateCornu( track_p trk, int inx, descData_p descUpd, BOOL_T final ts[0] = GetTrkEndTrk(trk,0); ts[1] = GetTrkEndTrk(trk,1); SetUpCornuParmFromTracks(ts,&cp,xx); - CallCornu(xx->cornuData.pos, tracks, NULL, &xx->cornuData.arcSegs, &cp); + CallCornu0(xx->cornuData.pos, xx->cornuData.c, xx->cornuData.a, xx->cornuData.r, &xx->cornuData.arcSegs, FALSE); //FixUpCornu(xx->bezierData.pos, xx, IsTrack(trk)); ComputeCornuBoundingBox(trk, xx); @@ -454,8 +464,8 @@ static void DescribeCornu( track_p trk, char * str, CSIZE_T len ) cornData.radius[0] = xx->cornuData.r[0]; cornData.radius[1] = xx->cornuData.r[1]; if (GetTrkType(trk) == T_CORNU) { - ComputeElev( trk, 0, FALSE, &cornData.elev[0], NULL ); - ComputeElev( trk, 1, FALSE, &cornData.elev[1], NULL ); + ComputeElev( trk, 0, FALSE, &cornData.elev[0], NULL, FALSE ); + ComputeElev( trk, 1, FALSE, &cornData.elev[1], NULL, FALSE ); if ( cornData.length > minLength ) cornData.grade = fabs( (cornData.elev[0]-cornData.elev[1])/cornData.length )*100.0; @@ -517,30 +527,16 @@ static void DrawCornu( track_p t, drawCmd_p d, wDrawColor color ) struct extraData *xx = GetTrkExtraData(t); long widthOptions = DTS_LEFT|DTS_RIGHT; - if (GetTrkWidth(t) == 2) - widthOptions |= DTS_THICK2; - if (GetTrkWidth(t) == 3) - widthOptions |= DTS_THICK3; - - - if ( ((d->funcs->options&wDrawOptTemp)==0) && + if ( ((d->options&DC_SIMPLE)==0) && (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) && labelScale >= d->scale && ( GetTrkBits( t ) & TB_HIDEDESC ) == 0 ) { DrawCornuDescription( t, d, color ); } DIST_T 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,t,zero,0.0,xx->cornuData.arcSegs.ptr,xx->cornuData.arcSegs.cnt, GetTrkGauge(t), color, widthOptions|DTS_TIES); DrawSegsO(d,t,zero,0.0,xx->cornuData.arcSegs.ptr,xx->cornuData.arcSegs.cnt, 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 ); - } + DrawEndPt( d, t, 0, color ); + DrawEndPt( d, t, 1, color ); } void FreeSubSegs(trkSeg_t* s) { @@ -579,7 +575,7 @@ static BOOL_T WriteCornu( track_p t, FILE * f ) if ( ( GetTrkBits(t) & TB_HIDEDESC ) == 0 ) options |= 0x80; rc &= fprintf(f, "%s %d %d %ld 0 0 %s %d %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f \n", "CORNU",GetTrkIndex(t), GetTrkLayer(t), (long)options, - GetTrkScaleName(t), GetTrkVisible(t), + GetTrkScaleName(t), GetTrkVisible(t)|(GetTrkNoTies(t)?1<<2:0)|(GetTrkBridge(t)?1<<3:0), xx->cornuData.pos[0].x, xx->cornuData.pos[0].y, xx->cornuData.a[0], xx->cornuData.r[0], @@ -593,11 +589,10 @@ static BOOL_T WriteCornu( track_p t, FILE * f ) rc &= WriteEndPt( f, t, 1 ); } rc &= WriteSegs( f, xx->cornuData.arcSegs.cnt, xx->cornuData.arcSegs.ptr ); - //rc &= fprintf(f, "\tEND\n" )>0; return rc; } -static void ReadCornu( char * line ) +static BOOL_T ReadCornu( char * line ) { struct extraData *xx; track_p t; @@ -613,12 +608,16 @@ static void ReadCornu( char * line ) if (!GetArgs( line+6, "dLl00sdpffppffp", &index, &layer, &options, scale, &visible, &p0, &a0, &r0, &c0, &p1, &a1, &r1, &c1 ) ) { - return; + return FALSE; } + if ( !ReadSegs() ) + return FALSE; t = NewTrack( index, T_CORNU, 0, sizeof *xx ); xx = GetTrkExtraData(t); - SetTrkVisible(t, visible); + SetTrkVisible(t, visible&2); + SetTrkNoTies(t, visible&4); + SetTrkBridge(t, visible&8); SetTrkScale(t, LookupScale(scale)); SetTrkLayer(t, layer ); SetTrkWidth(t, (int)(options&0x0F)); @@ -632,10 +631,10 @@ static void ReadCornu( char * line ) xx->cornuData.c[1] = c1; xx->cornuData.r[1] = r1; xx->cornuData.descriptionOff.x = xx->cornuData.descriptionOff.y = 0.0; - ReadSegs(); FixUpCornu0(xx->cornuData.pos,xx->cornuData.c,xx->cornuData.a, xx->cornuData.r, xx); ComputeCornuBoundingBox(t,xx); SetEndPts(t,2); + return TRUE; } static void MoveCornu( track_p trk, coOrd orig ) @@ -667,6 +666,9 @@ static void RescaleCornu( track_p trk, FLOAT_T ratio ) for (int i=0;i<2;i++) { xx->cornuData.pos[i].x *= ratio; xx->cornuData.pos[i].y *= ratio; + xx->cornuData.c[i].x *= ratio; + xx->cornuData.c[i].y *= ratio; + xx->cornuData.r[i] *= ratio; } RebuildCornu(trk); @@ -689,13 +691,63 @@ void GetCornuParmsNear(track_p t, int sel, coOrd * pos2, coOrd * center, ANGLE_T coOrd pos = *pos2; double dd = DistanceCornu(t, &pos); //Pos adjusted to be on curve int inx; + *radius = 0.0; + *angle2 = 0.0; + *center = zero; wBool_t back,neg; ANGLE_T angle = GetAngleSegs(xx->cornuData.arcSegs.cnt,(trkSeg_t *)(xx->cornuData.arcSegs.ptr),&pos,&inx,NULL,&back,NULL,&neg); + if (inx == -1) { + return; //Error in GetAngle + } + trkSeg_p segPtr = &DYNARR_N(trkSeg_t, xx->cornuData.arcSegs, inx); - GetAngleSegs(segPtr->bezSegs.cnt,(trkSeg_t *)(segPtr->bezSegs.ptr),&pos,&inx,NULL,&back,NULL,&neg); - segPtr = &DYNARR_N(trkSeg_t, segPtr->bezSegs, inx); + if (segPtr->type == SEG_BEZTRK) { + GetAngleSegs(segPtr->bezSegs.cnt,(trkSeg_t *)(segPtr->bezSegs.ptr),&pos,&inx,NULL,&back,NULL,&neg); + if (inx ==-1) return; + segPtr = &DYNARR_N(trkSeg_t, segPtr->bezSegs, inx); + } + + if (segPtr->type == SEG_STRTRK) { + *radius = 0.0; + *center = zero; + } else if (segPtr->type == SEG_CRVTRK) { + *center = segPtr->u.c.center; + *radius = fabs(segPtr->u.c.radius); + } + if (sel) + angle = NormalizeAngle(angle+(neg==back?0:180)); + else + angle = NormalizeAngle(angle+(neg==back?180:0)); + *angle2 = angle; + *pos2 = pos; +} + +void GetCornuParmsTemp(dynArr_t * array_p, int sel, coOrd * pos2, coOrd * center, ANGLE_T * angle2, DIST_T * radius ) { + + coOrd pos = *pos2; + int inx; + wBool_t back,neg; + *radius = 0.0; + *center = zero; + *angle2 = 0.0; + + ANGLE_T angle = GetAngleSegs(array_p->cnt,(trkSeg_p)array_p->ptr,&pos,&inx,NULL,&back,NULL,&neg); + + if (inx==-1) return; + + trkSeg_p segPtr = &DYNARR_N(trkSeg_t, *array_p, inx); + + if (segPtr->type == SEG_BEZTRK) { + + GetAngleSegs(segPtr->bezSegs.cnt,(trkSeg_t *)(segPtr->bezSegs.ptr),&pos,&inx,NULL,&back,NULL,&neg); + + if (inx ==-1) return; + + segPtr = &DYNARR_N(trkSeg_t, segPtr->bezSegs, inx); + + } if (segPtr->type == SEG_STRTRK) { *radius = 0.0; @@ -734,9 +786,14 @@ static BOOL_T SplitCornu( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, ANGLE_T angle = GetAngleSegs(xx->cornuData.arcSegs.cnt,(trkSeg_t *)(xx->cornuData.arcSegs.ptr),&pos,&inx,NULL,&back,NULL,&neg); + if (inx == -1) return FALSE; + trkSeg_p segPtr = &DYNARR_N(trkSeg_t, xx->cornuData.arcSegs, inx); GetAngleSegs(segPtr->bezSegs.cnt,(trkSeg_t *)(segPtr->bezSegs.ptr),&pos,&inx,NULL,&back,NULL,&neg); + + if (inx == -1) return FALSE; + segPtr = &DYNARR_N(trkSeg_t, segPtr->bezSegs, inx); if (segPtr->type == SEG_STRTRK) { @@ -767,6 +824,7 @@ static BOOL_T SplitCornu( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, } trk1 = NewCornuTrack(new.pos,new.center,new.angle,new.radius,NULL,0); + //Copy elevation details from old ep to new ep 0/1 if (trk1==NULL) { wBeep(); InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"), @@ -779,20 +837,26 @@ static BOOL_T SplitCornu( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, UndoEnd(); return FALSE; } + DIST_T height; + int opt; + GetTrkEndElev(trk,ep,&opt,&height); + UpdateTrkEndElev( trk1, ep, opt, height, (opt==ELEV_STATION)?GetTrkEndElevStation(trk,ep):NULL ); UndoModify(trk); xx->cornuData.pos[ep] = pos; xx->cornuData.a[ep] = NormalizeAngle(new.angle[1-ep]+180); xx->cornuData.r[ep] = new.radius[1-ep]; xx->cornuData.c[ep] = new.center[1-ep]; + //Wipe out old elevation for ep1 RebuildCornu(trk); SetTrkEndPoint(trk, ep, xx->cornuData.pos[ep], xx->cornuData.a[ep]); + UpdateTrkEndElev( trk, ep, ELEV_NONE, 0, NULL); *leftover = trk1; - *ep0 = 1-ep; - *ep1 = ep; + *ep0 = 1-ep; //Which end is for new on pos? + *ep1 = ep; //Which end is for old trk? return TRUE; } @@ -801,8 +865,12 @@ BOOL_T MoveCornuEndPt ( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d0 ) { track_p trk2; if (SplitTrack(*trk,pos,*ep,&trk2,TRUE)) { struct extraData *xx = GetTrkExtraData(*trk); - if (trk2) DeleteTrack(trk2,TRUE); + if (trk2) { + UndrawNewTrack( trk2 ); + DeleteTrack(trk2,TRUE); + } SetTrkEndPoint( *trk, *ep, *ep?xx->cornuData.pos[1]:xx->cornuData.pos[0], *ep?xx->cornuData.a[1]:xx->cornuData.a[0] ); + DrawNewTrack( *trk ); return TRUE; } return FALSE; @@ -923,7 +991,8 @@ static BOOL_T EnumerateCornu( track_p trk ) if (trk != NULL) { struct extraData *xx = GetTrkExtraData(trk); DIST_T d; - d = xx->cornuData.length; + d = max(CornuOffsetLength(xx->cornuData.arcSegs,-GetTrkGauge(trk)/2.0), + CornuOffsetLength(xx->cornuData.arcSegs,GetTrkGauge(trk)/2.0)); ScaleLengthIncrement( GetTrkScale(trk), d ); } return TRUE; @@ -1004,29 +1073,63 @@ static BOOL_T MergeCornu( DrawNewTrack( trk3 ); UndoEnd(); - return TRUE; } -BOOL_T GetBezierSegmentsFromCornu(track_p trk, dynArr_t * segs) { +BOOL_T GetBezierSegmentsFromCornu(track_p trk, dynArr_t * segs, BOOL_T track) { struct extraData * xx = GetTrkExtraData(trk); for (int i=0;i<xx->cornuData.arcSegs.cnt;i++) { - DYNARR_APPEND(trkSeg_t, * segs, 10); + trkSeg_p p = (trkSeg_t *) xx->cornuData.arcSegs.ptr+i; + if (p->type == SEG_BEZTRK) { + if (track) { + DYNARR_APPEND(trkSeg_t, * segs, 10); + trkSeg_p segPtr = &DYNARR_N(trkSeg_t,* segs,segs->cnt-1); + segPtr->type = SEG_BEZTRK; + segPtr->color = wDrawColorBlack; + segPtr->width = 0; + if (segPtr->bezSegs.ptr) MyFree(segPtr->bezSegs.ptr); + segPtr->bezSegs.cnt = 0; + segPtr->bezSegs.max = 0; + segPtr->bezSegs.ptr = NULL; + for (int j=0;j<4;j++) segPtr->u.b.pos[j] = p->u.b.pos[j]; + FixUpBezierSeg(segPtr->u.b.pos,segPtr,TRUE); + } else { + for (int j=0;j<p->bezSegs.cnt;j++) { + trkSeg_p bez_p = &DYNARR_N(trkSeg_t,p->bezSegs,j); + DYNARR_APPEND(trkSeg_t, * segs, 10); + trkSeg_p segPtr = &DYNARR_LAST(trkSeg_t,* segs); + if (bez_p->type == SEG_CRVTRK) segPtr->type = SEG_CRVLIN; + if (bez_p->type == SEG_STRTRK) segPtr->type = SEG_STRLIN; + segPtr->u = bez_p->u; + segPtr->width = bez_p->width; + segPtr->color = bez_p->color; + } + } + } else if (p->type == SEG_STRTRK) { + DYNARR_APPEND(trkSeg_t, * segs, 1); + trkSeg_p segPtr = &DYNARR_N(trkSeg_t,* segs,segs->cnt-1); + segPtr->type = track?SEG_STRTRK:SEG_STRLIN; + segPtr->color = wDrawColorBlack; + segPtr->width = 0; + for (int j=0;j<2;j++) segPtr->u.l.pos[i] = p->u.l.pos[i]; + segPtr->u.l.angle = p->u.l.angle; + segPtr->u.l.option = 0; + } else if (p->type == SEG_CRVTRK) { + DYNARR_APPEND(trkSeg_t, * segs, 1); trkSeg_p segPtr = &DYNARR_N(trkSeg_t,* segs,segs->cnt-1); - segPtr->type = SEG_BEZTRK; + segPtr->type = track?SEG_CRVTRK:SEG_CRVLIN; segPtr->color = wDrawColorBlack; segPtr->width = 0; - if (segPtr->bezSegs.ptr) MyFree(segPtr->bezSegs.ptr); - segPtr->bezSegs.cnt = 0; - segPtr->bezSegs.max = 0; - segPtr->bezSegs.ptr = NULL; - trkSeg_p p = (trkSeg_t *) xx->cornuData.arcSegs.ptr+i; - for (int j=0;j<4;j++) segPtr->u.b.pos[j] = p->u.b.pos[j]; - FixUpBezierSeg(segPtr->u.b.pos,segPtr,TRUE); + segPtr->u.c.a0 = p->u.c.a0; + segPtr->u.c.a1 = p->u.c.a1; + segPtr->u.c.center = p->u.c.center; + segPtr->u.c.radius = p->u.c.radius; + } } return TRUE; } + static DIST_T GetLengthCornu( track_p trk ) { struct extraData *xx = GetTrkExtraData(trk); @@ -1052,6 +1155,7 @@ static BOOL_T GetParamsCornu( int inx, track_p trk, coOrd pos, trackParams_t * p params->track_angle = GetAngleSegs( //Find correct Segment and nearest point in it xx->cornuData.arcSegs.cnt,xx->cornuData.arcSegs.ptr, &pos, &segInx, &d , &back, &segInx2, &negative ); + if (segInx ==-1) return FALSE; trkSeg_p segPtr = &DYNARR_N(trkSeg_t,xx->cornuData.arcSegs,segInx); if (negative != back) params->track_angle = NormalizeAngle(params->track_angle+180); //Cornu is in reverse if (segPtr->type == SEG_STRTRK) { @@ -1079,13 +1183,16 @@ static BOOL_T GetParamsCornu( int inx, track_p trk, coOrd pos, trackParams_t * p params->cornuCenter[i] = xx->cornuData.c[i]; } params->len = xx->cornuData.length; - if ( inx == PARAMS_PARALLEL ) { - params->ep = 0; - } else if (inx == PARAMS_CORNU) { + if ( inx == PARAMS_NODES ) { + return FALSE; + } else if ((inx == PARAMS_CORNU) || (inx == PARAMS_1ST_JOIN) || (inx == PARAMS_2ND_JOIN) ) { params->ep = PickEndPoint( pos, trk); } else { params->ep = PickUnconnectedEndPointSilent( pos, trk ); + } + if (params->ep == -1) return FALSE; + if (params->ep>=0) { params->angle = GetTrkEndAngle(trk,params->ep); } @@ -1108,7 +1215,7 @@ static BOOL_T QueryCornu( track_p trk, int query ) return TRUE; break; case Q_EXCEPTION: - return xx->cornuData.minCurveRadius < (GetLayoutMinTrackRadius()-EPSILON); + return fabs(xx->cornuData.minCurveRadius) < (GetLayoutMinTrackRadius()-EPSILON); break; case Q_IS_CORNU: return TRUE; @@ -1122,13 +1229,12 @@ static BOOL_T QueryCornu( track_p trk, int query ) // case Q_MODIFY_CANT_SPLIT: Remove Split Restriction // case Q_CANNOT_BE_ON_END: Remove Restriction - Can have Cornu with no ends case Q_CANNOT_PLACE_TURNOUT: - return TRUE; + return FALSE; break; case Q_IGNORE_EASEMENT_ON_EXTEND: return TRUE; break; case Q_MODIFY_CAN_SPLIT: - case Q_CAN_EXTEND: return TRUE; default: return FALSE; @@ -1178,8 +1284,9 @@ static ANGLE_T GetAngleCornu( BOOL_T back, neg; int indx; angle = GetAngleSegs( xx->cornuData.arcSegs.cnt, (trkSeg_p)xx->cornuData.arcSegs.ptr, &pos, &indx, NULL, &back, NULL, &neg ); - if ( ep0 ) *ep0 = -1; - if ( ep1 ) *ep1 = -1; + if (!back) angle = NormalizeAngle(angle+180); + if ( ep0 ) *ep0 = neg?1:0; + if ( ep1 ) *ep1 = neg?0:1; return angle; } @@ -1188,14 +1295,17 @@ BOOL_T GetCornuSegmentFromTrack(track_p trk, trkSeg_p seg_p) { return TRUE; } +static dynArr_t cornuSegs_da; static BOOL_T MakeParallelCornu( track_p trk, coOrd pos, DIST_T sep, + DIST_T factor, track_p * newTrkR, coOrd * p0R, - coOrd * p1R ) + coOrd * p1R, + BOOL_T track ) { struct extraData * xx = GetTrkExtraData(trk); coOrd np[4], p, nc[2]; @@ -1216,40 +1326,41 @@ static BOOL_T MakeParallelCornu( BOOL_T above = FALSE; if ( diff_a < 180 ) above = TRUE; //Above track if (xx->cornuData.a[0] <180) above = !above; - Translate(&np[0],xx->cornuData.pos[0],xx->cornuData.a[0]+(above?90:-90),sep); - Translate(&np[1],xx->cornuData.pos[1],xx->cornuData.a[1]+(above?-90:90),sep); + DIST_T sep0 = sep+((xx->cornuData.r[0]!=0.0)?fabs(factor/xx->cornuData.r[0]):0); + DIST_T sep1 = sep+((xx->cornuData.r[1]!=0.0)?fabs(factor/xx->cornuData.r[1]):0); + Translate(&np[0],xx->cornuData.pos[0],xx->cornuData.a[0]+(above?90:-90),sep0); + Translate(&np[1],xx->cornuData.pos[1],xx->cornuData.a[1]+(above?-90:90),sep1); na[0]=xx->cornuData.a[0]; na[1]=xx->cornuData.a[1]; - if (xx->cornuData.r[0]) { + if (xx->cornuData.r[0] != 0.0) { //Find angle between center and end angle of track ANGLE_T ea0 = NormalizeAngle(FindAngle(xx->cornuData.c[0],xx->cornuData.pos[0])-xx->cornuData.a[0]); - DIST_T sep0 = sep; - if (ea0>180) sep0 = -sep; + if (ea0>180) sep0 = -sep0; nr[0]=xx->cornuData.r[0]+(above?sep0:-sep0); //Needs adjustment nc[0]=xx->cornuData.c[0]; } else { - nr[0] = 0; + nr[0] = 0.0; nc[0] = zero; } - if (xx->cornuData.r[1]) { + if (xx->cornuData.r[1] != 0.0) { ANGLE_T ea1 = NormalizeAngle(FindAngle(xx->cornuData.c[1],xx->cornuData.pos[1])-xx->cornuData.a[1]); - DIST_T sep1 = sep; - if (ea1<180) sep1 = -sep; + if (ea1<180) sep1 = -sep1; nr[1]=xx->cornuData.r[1]+(above?sep1:-sep1); //Needs adjustment nc[1]=xx->cornuData.c[1]; } else { - nr[1] = 0; + nr[1] = 0.0; nc[1] = zero; } if ( newTrkR ) { - *newTrkR = NewCornuTrack( np, nc, na, nr, NULL, 0); - if (*newTrkR==NULL) { - wBeep(); - InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"), + if (track) { + *newTrkR = NewCornuTrack( np, nc, na, nr, NULL, 0); + if (*newTrkR==NULL) { + wBeep(); + InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"), np[0].x,np[0].y, np[1].x,np[1].y, nc[0].x,nc[0].y, @@ -1258,16 +1369,75 @@ static BOOL_T MakeParallelCornu( FormatDistance(nr[0]),FormatDistance(nr[1])); return FALSE; } + } else { + tempSegs_da.cnt = 0; + CallCornu0(np,nc,na,nr,&tempSegs_da,FALSE); + *newTrkR = MakePolyLineFromSegs( zero, 0.0, &tempSegs_da ); + } } else { tempSegs_da.cnt = 0; CallCornu0(np,nc,na,nr,&tempSegs_da,FALSE); + if (!track) { + for (int i=0;i<tempSegs_da.cnt;i++) { + trkSeg_p seg = &tempSegs(i); + if (seg->type == SEG_STRTRK) { + seg->type = SEG_STRLIN; + seg->color = wDrawColorBlack; + seg->width = 0; + } + if (seg->type == SEG_CRVTRK) { + seg->type = SEG_CRVLIN; + seg->color = wDrawColorBlack; + seg->width = 0; + } + if (seg->type == SEG_BEZTRK) { + for (int j=0;j<seg->bezSegs.cnt;j++) { + trkSeg_p bseg = &(((trkSeg_t *)seg->bezSegs.ptr)[j]); + if (bseg->type == SEG_STRTRK) { + bseg->type = SEG_STRLIN; + bseg->color = wDrawColorBlack; + bseg->width = 0; + } + if (bseg->type == SEG_CRVTRK) { + bseg->type = SEG_CRVLIN; + bseg->color = wDrawColorBlack; + bseg->width = 0; + } + } + seg->type = SEG_BEZLIN; + seg->color = wDrawColorBlack; + seg->width = 0; + } + } + } } if ( p0R ) *p0R = np[0]; if ( p1R ) *p1R = np[1]; return TRUE; } +static BOOL_T TrimCornu( track_p trk, EPINX_T ep, DIST_T dist, coOrd endpos, ANGLE_T angle, DIST_T radius, coOrd center ) { + UndoModify(trk); + if (dist>0.0 && dist<minLength) { + UndrawNewTrack( trk ); + DeleteTrack(trk, TRUE); + return FALSE; + } else { + struct extraData *xx; + UndrawNewTrack( trk ); + xx = GetTrkExtraData(trk); + xx->cornuData.a[ep] = angle; + xx->cornuData.c[ep] = center; + xx->cornuData.r[ep] = radius; + xx->cornuData.pos[ep] = endpos; + RebuildCornu(trk); + SetTrkEndPoint(trk, ep, xx->cornuData.pos[ep], xx->cornuData.a[ep]); + DrawNewTrack( trk ); + } + return TRUE; +} + /* * When an undo is run, the array of segs is missing - they are not saved to the Undo log. So Undo calls this routine to * ensure @@ -1288,6 +1458,29 @@ EXPORT BOOL_T RebuildCornu (track_p trk) } +static wBool_t CompareCornu( track_cp trk1, track_cp trk2 ) +{ + struct extraData *xx1 = GetTrkExtraData( trk1 ); + struct extraData *xx2 = GetTrkExtraData( trk2 ); + char * cp = message + strlen(message); + REGRESS_CHECK_POS( "Pos[0]", xx1, xx2, cornuData.pos[0] ) + REGRESS_CHECK_POS( "Pos[1]", xx1, xx2, cornuData.pos[1] ) + REGRESS_CHECK_POS( "C[0]", xx1, xx2, cornuData.c[0] ) + REGRESS_CHECK_POS( "C[1]", xx1, xx2, cornuData.c[1] ) + REGRESS_CHECK_ANGLE( "A[0]", xx1, xx2, cornuData.a[0] ) + REGRESS_CHECK_ANGLE( "A[1]", xx1, xx2, cornuData.a[1] ) + REGRESS_CHECK_DIST( "R[0]", xx1, xx2, cornuData.r[0] ) + REGRESS_CHECK_DIST( "R[1]", xx1, xx2, cornuData.r[1] ) + REGRESS_CHECK_DIST( "MinCurveRadius", xx1, xx2, cornuData.minCurveRadius ) + REGRESS_CHECK_DIST( "MaxRateofChange", xx1, xx2, cornuData.maxRateofChange ) + REGRESS_CHECK_DIST( "Length", xx1, xx2, cornuData.length ) + REGRESS_CHECK_ANGLE( "WindingAngle", xx1, xx2, cornuData.windingAngle ) + // CHECK arcSegs + REGRESS_CHECK_POS( "DescOff", xx1, xx2, cornuData.descriptionOff ) + // CHECK cornuPath + return TRUE; +} + static trackCmd_t cornuCmds = { "CORNU", DrawCornu, @@ -1305,7 +1498,7 @@ static trackCmd_t cornuCmds = { TraverseCornu, EnumerateCornu, NULL, /* redraw */ - NULL, /* trim */ + TrimCornu, /* trim */ MergeCornu, NULL, /* modify */ GetLengthCornu, @@ -1319,7 +1512,11 @@ static trackCmd_t cornuCmds = { NULL, MakeParallelCornu, NULL, - RebuildCornu + RebuildCornu, + NULL, + NULL, + NULL, + CompareCornu }; diff --git a/app/bin/tcornu.h b/app/bin/tcornu.h index a987044..5684373 100644 --- a/app/bin/tcornu.h +++ b/app/bin/tcornu.h @@ -37,6 +37,7 @@ typedef struct { DIST_T radius[2]; //0.0 if straight ANGLE_T angle[2]; //Set if straight coOrd center[2]; //Set if radius >0 + dynArr_t mids; //If there are G2 points added } cornuParm_t; @@ -53,13 +54,14 @@ BOOL_T RebuildCornu (track_p trk); DIST_T DistanceCornu( track_p t, coOrd * p ); STATUS_T CornuDescriptionMove(track_p trk,wAction_t action,coOrd pos ); -DIST_T CornuDescriptionDistance(coOrd pos,track_p trk ); +DIST_T CornuDescriptionDistance(coOrd pos,track_p trk, coOrd *, BOOL_T show_hidden, BOOL_T * hidden ); void GetCornuParmsNear(track_p t, int sel, coOrd * pos, coOrd * center, ANGLE_T * angle, DIST_T * radius ); +void GetCornuParmsTemp(dynArr_t *, int sel, coOrd * pos2, coOrd * center, ANGLE_T * angle2, DIST_T * radius ); BOOL_T CallCornu(coOrd[2],track_p[2],EPINX_T[2],dynArr_t *,cornuParm_t *); BOOL_T CallCornu0(coOrd[2], coOrd[2], ANGLE_T[2], DIST_T[2], dynArr_t *,BOOL_T); -BOOL_T GetBezierSegmentsFromCornu(track_p, dynArr_t *); +BOOL_T GetBezierSegmentsFromCornu(track_p, dynArr_t *, BOOL_T); char * CreateSegPathList(track_p trk); diff --git a/app/bin/tcurve.c b/app/bin/tcurve.c index 7233ebf..00d1ef5 100644 --- a/app/bin/tcurve.c +++ b/app/bin/tcurve.c @@ -22,6 +22,7 @@ #include <assert.h> #include <math.h> +#include <string.h> #include "ccurve.h" #include "cjoin.h" @@ -172,25 +173,32 @@ BOOL_T GetCurveMiddle( track_p trk, coOrd * pos ) DIST_T CurveDescriptionDistance( coOrd pos, - track_p trk ) + track_p trk, + coOrd * dpos, + BOOL_T show_hidden, + BOOL_T * hidden) { 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 ) + if (hidden) *hidden = FALSE; + if ( (GetTrkType( trk ) != T_CURVE )|| ((( GetTrkBits( trk ) & TB_HIDEDESC ) != 0) && !show_hidden)) return 100000; + coOrd offset = xx->descriptionOff; + if (( GetTrkBits( trk ) & TB_HIDEDESC ) != 0) offset = zero; if ( xx->helixTurns > 0 ) { - p1.x = xx->pos.x + xx->descriptionOff.x; - p1.y = xx->pos.y + xx->descriptionOff.y; + p1.x = xx->pos.x + offset.x; + p1.y = xx->pos.y + offset.y; } else { GetCurveAngles( &a0, &a1, trk ); - ratio = ( xx->descriptionOff.x + 1.0 ) / 2.0; + ratio = ( offset.x + 1.0 ) / 2.0; a = a0 + ratio * a1; - ratio = ( xx->descriptionOff.y + 1.0 ) / 2.0; + ratio = ( offset.y + 1.0 ) / 2.0; Translate( &p1, xx->pos, a, xx->radius * ratio ); } + if (hidden) *hidden = (GetTrkBits( trk ) & TB_HIDEDESC); + *dpos = p1; return FindDistance( p1, pos ); } @@ -220,8 +228,8 @@ static void DrawCurveDescription( dist = GetLengthCurve( trk ); elevValid = FALSE; if ( (!xx->circle) && - ComputeElev( trk, 0, FALSE, &elev0, NULL ) && - ComputeElev( trk, 1, FALSE, &elev1, NULL ) ) { + ComputeElev( trk, 0, FALSE, &elev0, NULL, FALSE ) && + ComputeElev( trk, 1, FALSE, &elev1, NULL, FALSE ) ) { if( elev0 == elev1 ) elevValid = FALSE; else { @@ -232,15 +240,15 @@ static void DrawCurveDescription( } fp = wStandardFont( F_TIMES, FALSE, FALSE ); if (elevValid) - sprintf( message, _("Helix: turns=%ld length=%s grade=%0.1f%% sep=%s"), + sprintf( message, _("Helix: turns=%ld len=%0.2f grade=%0.1f%% sep=%0.2f"), xx->helixTurns, - FormatDistance(dist), + dist, grade*100.0, - FormatDistance(sep) ); + sep ); else - sprintf( message, _("Helix: turns=%ld length=%s"), + sprintf( message, _("Helix: turns=%ld len=%0.2f"), xx->helixTurns, - FormatDistance(dist) ); + dist ); DrawBoxedString( BOX_BOX, d, pos, message, fp, (wFontSize_t)descriptionFontSize, color, 0.0 ); } else { dist = trackGauge/2.0; @@ -278,6 +286,7 @@ STATUS_T CurveDescriptionMove( switch (action) { case C_DOWN: + DrawCurveDescription( trk, &mainD, wDrawColorWhite ); case C_MOVE: case C_UP: editMode = TRUE; @@ -286,8 +295,6 @@ STATUS_T CurveDescriptionMove( xx->descriptionOff.x = (pos.x-xx->pos.x); xx->descriptionOff.y = (pos.y-xx->pos.y); p1 = pos; - if (action != C_UP) - DrawLine( &tempD, p0, p1, 0, wDrawColorBlack ); } else { p1 = pos; GetCurveAngles( &a0, &a1, trk ); @@ -314,14 +321,16 @@ STATUS_T CurveDescriptionMove( a = a0 + (0.5 * a1); PointOnCircle( &p0, xx->pos, xx->radius/2, a ); } - if (action == C_UP) editMode = FALSE; - MainRedraw(); - MapRedraw(); + if (action == C_UP) { + editMode = FALSE; + DrawCurveDescription( trk, &mainD, wDrawColorBlack ); + } return action==C_UP?C_TERMINATE:C_CONTINUE; case C_REDRAW: if (editMode) { - DrawLine( &tempD, p1, p0, 0, wDrawColorBlack ); + DrawLine( &tempD, p0, p1, 0, wDrawColorBlue ); + DrawCurveDescription( trk, &tempD, wDrawColorBlue ); } break; @@ -478,7 +487,7 @@ static void UpdateCurve( track_p trk, int inx, descData_p descUpd, BOOL_T final 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 ); + ComputeElev( trk, 1-ep, FALSE, &crvData.elev[1-ep], NULL, TRUE ); if ( crvData.length > minLength ) crvData.grade = fabs( (crvData.elev[0]-crvData.elev[1])/crvData.length )*100.0; else @@ -575,13 +584,13 @@ static void DescribeCurve( track_p trk, char * str, CSIZE_T len ) 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 ); + ComputeElev( trk, 0, FALSE, &crvData.elev[0], NULL, FALSE ); + ComputeElev( trk, 1, FALSE, &crvData.elev[1], NULL, FALSE ); } else { crvData.elev[0] = crvData.elev[1] = 0; } - ComputeElev( trk, 0, FALSE, &crvData.elev[0], NULL ); - ComputeElev( trk, 1, FALSE, &crvData.elev[1], NULL ); + ComputeElev( trk, 0, FALSE, &crvData.elev[0], NULL, FALSE ); + ComputeElev( trk, 1, FALSE, &crvData.elev[1], NULL, FALSE ); if ( crvData.length > minLength ) crvData.grade = fabs( (crvData.elev[0]-crvData.elev[1])/crvData.length )*100.0; else @@ -657,12 +666,8 @@ 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; + long widthOptions = DTS_LEFT|DTS_RIGHT; - if (GetTrkWidth(t) == 2) - widthOptions |= DTS_THICK2; - if (GetTrkWidth(t) == 3) - widthOptions |= DTS_THICK3; GetCurveAngles( &a0, &a1, t ); if (xx->circle) { tt = NULL; @@ -671,21 +676,18 @@ static void DrawCurve( track_p t, drawCmd_p d, wDrawColor color ) a0 = 0.0; a1 = 360.0; } - if ( ((d->funcs->options&wDrawOptTemp)==0) && + if ( ((d->options&(DC_SIMPLE|DC_SEGTRACK))==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 ); - } + t, color, widthOptions ); + DrawEndPt( d, t, 0, color ); + DrawEndPt( d, t, 1, color ); } static void DeleteCurve( track_p t ) @@ -702,15 +704,15 @@ static BOOL_T WriteCurve( track_p t, FILE * f ) 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, + GetTrkScaleName(t), GetTrkVisible(t)|(GetTrkNoTies(t)?1<<2:0)|(GetTrkBridge(t)?1<<3:0), 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; + rc &= fprintf(f, "\t%s\n", END_SEGS)>0; return rc; } -static void ReadCurve( char * line ) +static BOOL_T ReadCurve( char * line ) { struct extraData *xx; track_p t; @@ -723,32 +725,45 @@ static void ReadCurve( char * line ) wIndex_t layer; long options; char * cp = NULL; + long helixTurns = 0; + coOrd descriptionOff = { 0.0, 0.0 }; if (!GetArgs( line+6, paramVersion<3?"dXZsdpYfc":paramVersion<9?"dLl00sdpYfc":"dLl00sdpffc", &index, &layer, &options, scale, &visible, &p, &elev, &r, &cp ) ) { - return; + return FALSE; } + if (cp) { + if ( !GetArgs( cp, "lp", &helixTurns, &descriptionOff ) ) + return FALSE; + } + if ( !ReadSegs() ) + return FALSE; t = NewTrack( index, T_CURVE, 0, sizeof *xx ); xx = GetTrkExtraData(t); - SetTrkVisible(t, visible); + xx->helixTurns = helixTurns; + xx->descriptionOff = descriptionOff; + if ( paramVersion < 3 ) { + SetTrkVisible(t, visible!=0); + SetTrkNoTies(t, FALSE); + SetTrkBridge(t, FALSE); + } else { + SetTrkVisible(t, visible&2); + SetTrkNoTies(t, visible&4); + SetTrkBridge(t, visible&8); + } 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 ); + return TRUE; } static void MoveCurve( track_p trk, coOrd orig ) @@ -810,7 +825,12 @@ static BOOL_T SplitCurve( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, a0 = a; } trk1 = NewCurvedTrack( xx->pos, xx->radius, a0, a1, 0 ); + DIST_T height; + int opt; + GetTrkEndElev(trk,ep,&opt,&height); + UpdateTrkEndElev( trk1, 1-ep, opt, height, (opt==ELEV_STATION)?GetTrkEndElevStation(trk,ep):NULL ); AdjustCurveEndPt( trk, ep, a+(ep==0?-90.0:90.0) ); + UpdateTrkEndElev( trk, ep, ELEV_NONE, 0, NULL); *leftover = trk1; *ep0 = 1-ep; *ep1 = ep; @@ -922,15 +942,15 @@ static BOOL_T EnumerateCurve( track_p trk ) if (trk != NULL) { xx = GetTrkExtraData(trk); GetCurveAngles( &a0, &a1, trk ); - d = xx->radius * 2.0 * M_PI * a1 / 360.0; + d = (xx->radius + (GetTrkGauge(trk)/2.0))* 2.0 * M_PI * a1 / 360.0; if (xx->helixTurns > 0) - d += (xx->helixTurns-(xx->circle?1:0)) * xx->radius * 2.0 * M_PI; + d += (xx->helixTurns-(xx->circle?1:0)) * (xx->radius+(GetTrkGauge(trk)/2.0)) * 2.0 * M_PI; ScaleLengthIncrement( GetTrkScale(trk), d ); } return TRUE; } -static BOOL_T TrimCurve( track_p trk, EPINX_T ep, DIST_T dist ) +static BOOL_T TrimCurve( track_p trk, EPINX_T ep, DIST_T dist, coOrd endpos, ANGLE_T angle, DIST_T endradius, coOrd endcenter ) { DIST_T d; DIST_T radius; @@ -980,6 +1000,8 @@ static BOOL_T MergeCurve( if ( xx0->helixTurns > 0 || xx1->helixTurns > 0 ) return FALSE; + if (GetTrkType(trk0) != GetTrkType(trk1)) + return FALSE; d = FindDistance( xx0->pos, xx1->pos ); d += fabs( xx0->radius - xx1->radius ); if ( d > connectDistance ) @@ -991,6 +1013,8 @@ static BOOL_T MergeCurve( UndoStart( _("Merge Curves"), "MergeCurve( T%d[%d] T%d[%d] )", GetTrkIndex(trk0), ep0, GetTrkIndex(trk1), ep1 ); UndoModify( trk0 ); UndrawNewTrack( trk0 ); + if (GetTrkEndTrk(trk0,ep0) == trk1) + DisconnectTracks( trk0, ep0, trk1, ep1); trk2 = GetTrkEndTrk( trk1, 1-ep1 ); if (trk2) { ep2 = GetEndPtConnectedToMe( trk2, trk1 ); @@ -1168,7 +1192,7 @@ static DIST_T GetLengthCurve( track_p trk ) a1 = 360.0; else GetCurveAngles( &a0, &a1, trk ); - dist = (rad+GetTrkGauge(trk)/2.0)*a1*2.0*M_PI/360.0; + dist = rad*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; @@ -1194,32 +1218,25 @@ static BOOL_T GetParamsCurve( int inx, track_p trk, coOrd pos, trackParams_t * p ErrorMessage( MSG_CANT_EXTEND_HELIX ); return FALSE; } + if (inx == PARAMS_NODES) 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; - } + params->circleOrHelix = FALSE; + if ( IsCurveCircle( trk ) ) { + params->ep = PickArcEndPt( params->arcP, /*Dj.inp[0].*/pos, pos ); + params->angle = params->track_angle; + params->circleOrHelix = TRUE; + return TRUE; + } else if ((inx == PARAMS_CORNU) || (inx == PARAMS_1ST_JOIN) || (inx == PARAMS_2ND_JOIN) ) { + params->ep = PickEndPoint(pos, trk); } else { - params->circleOrHelix = FALSE; - if ( IsCurveCircle( trk ) ) { - params->ep = PickArcEndPt( params->arcP, /*Dj.inp[0].*/pos, pos ); - params->angle = params->track_angle; - params->circleOrHelix = TRUE; - return TRUE; - } else if (inx == PARAMS_CORNU ) { - params->ep = PickEndPoint(pos, trk); - } else { - params->ep = PickUnconnectedEndPointSilent( pos, trk ); - } - if (params->ep == -1) - return FALSE; - params->angle = GetTrkEndAngle(trk,params->ep); ; + params->ep = PickUnconnectedEndPointSilent( pos, trk ); } + if (params->ep == -1) + return FALSE; + params->angle = GetTrkEndAngle(trk,params->ep); ; return TRUE; } @@ -1255,10 +1272,11 @@ static BOOL_T QueryCurve( track_p trk, int query ) case Q_HAS_DESC: case Q_CORNU_CAN_MODIFY: case Q_MODIFY_CAN_SPLIT: + case Q_CAN_EXTEND: return TRUE; break; case Q_EXCEPTION: - return xx->radius < GetLayoutMinTrackRadius() - EPSILON; + return fabs(xx->radius) < (GetLayoutMinTrackRadius() - EPSILON); break; case Q_NOT_PLACE_FROGPOINTS: return IsCurveCircle( trk ); @@ -1274,6 +1292,8 @@ static BOOL_T QueryCurve( track_p trk, int query ) if ((xx->helixTurns >0) || xx->circle) return TRUE; return FALSE; break; + case Q_NODRAWENDPT: + return xx->circle; default: return FALSE; } @@ -1295,9 +1315,11 @@ static BOOL_T MakeParallelCurve( track_p trk, coOrd pos, DIST_T sep, + DIST_T factor, track_p * newTrkR, coOrd * p0R, - coOrd * p1R ) + coOrd * p1R, + BOOL_T track) { struct extraData * xx = GetTrkExtraData(trk); struct extraData * xx1; @@ -1305,16 +1327,32 @@ static BOOL_T MakeParallelCurve( ANGLE_T a0, a1; rad = FindDistance( pos, xx->pos ); + sep = sep+factor/xx->radius; 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; + if (track) { + *newTrkR = NewCurvedTrack( xx->pos, rad, a0, a1, 0 ); + xx1 = GetTrkExtraData(*newTrkR); + xx1->helixTurns = xx->helixTurns; + xx1->circle = xx->circle; + } + else { + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).width = 0; + tempSegs_da.cnt = 1; + tempSegs(0).type = SEG_CRVLIN; + 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; + *newTrkR = MakeDrawFromSeg( zero, 0.0, &tempSegs(0) ); + return TRUE; + } + ComputeCurveBoundingBox( *newTrkR, xx1 ); } else { if ( xx->helixTurns > 0) { @@ -1324,7 +1362,7 @@ static BOOL_T MakeParallelCurve( tempSegs(0).color = wDrawColorBlack; tempSegs(0).width = 0; tempSegs_da.cnt = 1; - tempSegs(0).type = SEG_CRVTRK; + tempSegs(0).type = track?SEG_CRVTRK:SEG_CRVLIN; tempSegs(0).u.c.center = xx->pos; tempSegs(0).u.c.radius = rad; tempSegs(0).u.c.a0 = a0; @@ -1336,6 +1374,19 @@ static BOOL_T MakeParallelCurve( } +static wBool_t CompareCurve( track_cp trk1, track_cp trk2 ) +{ + struct extraData * ed1 = GetTrkExtraData( trk1 ); + struct extraData * ed2 = GetTrkExtraData( trk2 ); + char * cp = message+strlen(message); + REGRESS_CHECK_POS( "POS", ed1, ed2, pos ) + REGRESS_CHECK_DIST( "RADIUS", ed1, ed2, radius ) + REGRESS_CHECK_INT( "CIRCLE", ed1, ed2, circle ) + REGRESS_CHECK_INT( "TURNS", ed1, ed2, helixTurns ) + REGRESS_CHECK_POS( "DESCOFF", ed1, ed2, descriptionOff ); + return TRUE; +} + static trackCmd_t curveCmds = { "CURVE", DrawCurve, @@ -1365,7 +1416,13 @@ static trackCmd_t curveCmds = { NULL, NULL, NULL, - MakeParallelCurve }; + MakeParallelCurve, + NULL, + NULL, + NULL, + NULL, + NULL, + CompareCurve }; EXPORT void CurveSegProc( @@ -1524,7 +1581,7 @@ EXPORT void PlotCurve( coOrd pos1, coOrd pos2, curveData_t * curveData, - BOOL_T constrain ) + BOOL_T constrain ) //Make the Radius be in steps of radiusGranularity (1/8) { DIST_T d0, d2, r; ANGLE_T angle, a0, a1, a2; @@ -1532,13 +1589,14 @@ EXPORT void PlotCurve( switch ( mode ) { case crvCmdFromCornu: + /* Already set curveRadius, pos1, and type */ 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) & (mode == crvCmdFromEP1)) { + 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)); @@ -1560,7 +1618,7 @@ LOG( log_curve, 3, ( "Straight: %0.3f < %0.3f\n", d0*sin(D2R(a1)), (4.0/75.0)*ma else curveData->curveRadius = d0/sin(D2R(-a1)); } - if (curveData->curveRadius > 1000 && mode == crvCmdFromEP1) { + 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)); @@ -1579,6 +1637,7 @@ LOG( log_curve, 3, ( "Straight: %0.3f < %0.3f\n", d0*sin(D2R(a1)), (4.0/75.0)*ma curveData->a0 = NormalizeAngle( a2-180-curveData->a1 ); curveData->negative = TRUE; } + Translate(&curveData->pos2,curveData->curvePos,FindAngle(curveData->curvePos,pos2),curveData->curveRadius); curveData->type = curveTypeCurve; } } @@ -1602,6 +1661,7 @@ LOG( log_curve, 3, ( "Straight: %0.3f < %0.3f\n", d0*sin(D2R(a1)), (4.0/75.0)*ma curveData->a0 = a1; curveData->a1 = NormalizeAngle(a0-a1); } + Translate(&curveData->pos2,curveData->curvePos,FindAngle(curveData->curvePos,pos2),curveData->curveRadius); curveData->type = curveTypeCurve; break; case crvCmdFromChord: @@ -1611,7 +1671,7 @@ LOG( log_curve, 3, ( "Straight: %0.3f < %0.3f\n", d0*sin(D2R(a1)), (4.0/75.0)*ma d0 = FindDistance( pos0, pos1 )/2.0; Rotate( &pos2, pos1, -a0 ); pos2.x -= pos1.x; - if ( fabs(pos2.x) < 0.01 ) + if ( fabs(pos2.x) < 0.005 ) break; d2 = sqrt( d0*d0 + pos2.x*pos2.x )/2.0; r = d2*d2*2.0/pos2.x; @@ -1634,6 +1694,7 @@ LOG( log_curve, 3, ( "Straight: %0.3f < %0.3f\n", d0*sin(D2R(a1)), (4.0/75.0)*ma curveData->a1 = NormalizeAngle(a0-a1); curveData->negative = TRUE; } + Translate(&curveData->pos2,curveData->curvePos,FindAngle(curveData->curvePos,pos2),curveData->curveRadius); curveData->type = curveTypeCurve; break; } diff --git a/app/bin/tease.c b/app/bin/tease.c index ad281df..dec0801 100644 --- a/app/bin/tease.c +++ b/app/bin/tease.c @@ -62,6 +62,9 @@ do 'testjoin psplot 10 10 40 1 | lpr -Ppostscript' #include <math.h> +#include "common.h" +#include "track.h" +#include "tcornu.h" #include "ccurve.h" #include "cselect.h" #include "cstraigh.h" @@ -72,7 +75,6 @@ do 'testjoin psplot 10 10 40 1 | lpr -Ppostscript' #include "layout.h" #include "messages.h" #include "param.h" -#include "track.h" #include "utility.h" static TRKTYP_T T_EASEMENT = -1; @@ -483,6 +485,20 @@ static DIST_T GetLengthJoint( track_p trk ) return fabs( d0-d1 ); } +static DIST_T GetFlexLengthJoint( track_p trk ) +{ + struct extraData *xx; + DIST_T d0, d1, d3; + xx = GetTrkExtraData(trk); + d0 = JoinD( xx->l0, xx->R+(GetTrkGauge(trk)/2.0), xx->L ); + d1 = JoinD( xx->l1, xx->R+(GetTrkGauge(trk)/2.0), xx->L ); + d3 = JoinD( xx->l1, xx->R-(GetTrkGauge(trk)/2.0), xx->L ); + if (xx->Scurve) { + return d0+d3; + } else + return fabs( d0-d1 ); +} + static struct { coOrd endPt[2]; @@ -523,7 +539,7 @@ static void UpdateJoint( track_p trk, int inx, descData_p descUpd, BOOL_T final 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 ); + ComputeElev( trk, 1-ep, FALSE, &jointData.elev[1-ep], NULL, TRUE ); if ( jointData.length > minLength ) jointData.grade = fabs( (jointData.elev[0]-jointData.elev[1])/jointData.length )*100.0; else @@ -570,8 +586,8 @@ static void DescribeJoint( 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 ); + ComputeElev( trk, 0, FALSE, &jointData.elev[0], NULL, FALSE ); + ComputeElev( trk, 1, FALSE, &jointData.elev[1], NULL, FALSE ); if ( jointData.length > minLength ) jointData.grade = fabs( (jointData.elev[0]-jointData.elev[1])/jointData.length )*100.0; else @@ -670,48 +686,6 @@ static DIST_T DistanceJoint( } -#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, @@ -740,20 +714,16 @@ static void DrawJointSegment( 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; - } + cnt1 = (int)floor(a1/JOINT_ANGLE_INCR) + 1; + a1 /= cnt1; - widthOptions |= DTS_RIGHT|DTS_LEFT|DTS_TIES; + widthOptions |= DTS_RIGHT|DTS_LEFT; 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, + DrawStraightTrack( d, p0, p1, FindAngle( p1, p0 ), trk, color, widthOptions ); p0 = p1; } @@ -834,10 +804,6 @@ 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 @@ -851,24 +817,22 @@ LOG( log_ease, 4, ( "DJT( (X%0.3f Y%0.3f A%0.3f) \n", pos.x, pos.y, angle ) ) /* 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; + if (cnt == 0) 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; + if (cnt == 0) 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; + if (cnt == 0) 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 ); - } + DrawEndPt( d, trk, ep0, color ); + DrawEndPt( d, trk, ep1, color ); } @@ -883,10 +847,6 @@ static void DrawJoint( 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 ); } @@ -912,11 +872,11 @@ static BOOL_T WriteJoint( 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; + rc &= fprintf(f, "\t%s\n", END_SEGS )>0; return rc; } -static void ReadJoint( +static BOOL_T ReadJoint( char * line ) /* * Read track data from a file (f). @@ -934,17 +894,27 @@ static void ReadJoint( 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; + return FALSE; + if ( !ReadSegs() ) + return FALSE; trk = NewTrack( index, T_EASEMENT, 0, sizeof e ); xx = GetTrkExtraData(trk); - SetTrkVisible(trk, visible); + if ( paramVersion < 3 ) { + SetTrkVisible(trk, visible!=0); + SetTrkNoTies(trk, FALSE); + SetTrkBridge(trk, FALSE); + } else { + SetTrkVisible(trk, visible&2); + SetTrkNoTies(trk, visible&4); + SetTrkBridge(trk, visible&8); + } SetTrkScale(trk, LookupScale(scale)); SetTrkLayer(trk, layer); SetTrkWidth(trk, (int)(options&3)); *xx = e; - ReadSegs(); SetEndPts( trk, 2 ); ComputeBoundingBox( trk ); + return TRUE; } static void MoveJoint( @@ -1225,12 +1195,12 @@ static BOOL_T TraverseJointTrack( static BOOL_T EnumerateJoint( track_p trk ) { if (trk != NULL) { - ScaleLengthIncrement( GetTrkScale(trk), GetLengthJoint(trk) ); + ScaleLengthIncrement( GetTrkScale(trk), GetFlexLengthJoint(trk) ); } return TRUE; } -static BOOL_T TrimJoint( track_p trk, EPINX_T ep, DIST_T maxX ) +static BOOL_T TrimJoint( track_p trk, EPINX_T ep, DIST_T maxX, coOrd endpos, ANGLE_T angle, DIST_T radius, coOrd center ) { DeleteTrack( trk, FALSE ); return TRUE; @@ -1294,13 +1264,16 @@ static BOOL_T MergeJoint( static BOOL_T GetParamsJoint( int inx, track_p trk, coOrd pos, trackParams_t * params ) { params->type = curveTypeStraight; - params->ep = PickUnconnectedEndPointSilent( pos, trk ); + if ((inx == PARAMS_CORNU) || (inx == PARAMS_1ST_JOIN) || (inx == PARAMS_2ND_JOIN))\ + params->ep = PickEndPoint(pos, trk); + else + params->ep = PickUnconnectedEndPointSilent( 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->len = GetLengthJoint(trk); params->arcR = 0.0; return TRUE; } @@ -1354,9 +1327,11 @@ static BOOL_T MakeParallelJoint( track_p trk, coOrd pos, DIST_T sep, + DIST_T factor, track_p * newTrkR, coOrd * p0R, - coOrd * p1R ) + coOrd * p1R, + BOOL_T track) { struct extraData * xx = GetTrkExtraData(trk), *xx1; ANGLE_T angle, A; @@ -1378,13 +1353,13 @@ static BOOL_T MakeParallelJoint( p0 = GetTrkEndPos(trk,0); p1 = GetTrkEndPos(trk,1); d0 = FindDistance( p0, p1 ); + sep = sep+factor/(xx->R); 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; @@ -1408,30 +1383,53 @@ static BOOL_T MakeParallelJoint( } 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 ); + if (track) { + *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 { + dl = fabs(l0-l1); + len = dl/(0.20*mainD.scale); + cnt = (int)ceil(len); + if (cnt == 0) 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 = track?SEG_STRTRK:SEG_STRLIN; + 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 ); + *newTrkR = MakeDrawFromSeg( zero, 0.0, &tempSegs(inx) ); + } + tempSegs_da.cnt = cnt; + } } 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; + if (cnt == 0) 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; + tempSegs(inx).type = track?SEG_STRTRK:SEG_STRLIN; if ( inx == 0 ) { GetJointPos( &tempSegs(inx).u.l.pos[0], NULL, l0, R, L, P, A, xx->negate ); } else { @@ -1447,6 +1445,21 @@ static BOOL_T MakeParallelJoint( return TRUE; } +static wBool_t CompareJoint( track_cp trk1, track_cp trk2 ) +{ + struct extraData *xx1 = GetTrkExtraData( trk1 ); + struct extraData *xx2 = GetTrkExtraData( trk2 ); + char * cp = message + strlen(message); + REGRESS_CHECK_DIST( "L0", xx1, xx2, l0 ); + REGRESS_CHECK_DIST( "L1", xx1, xx2, l1 ); + REGRESS_CHECK_INT( "Flip", xx1, xx2, flip ); + REGRESS_CHECK_INT( "Negate", xx1, xx2, negate ); + REGRESS_CHECK_INT( "Scurve", xx1, xx2, Scurve ); + REGRESS_CHECK_POS( "Pos", xx1, xx2, pos ); + REGRESS_CHECK_ANGLE( "Angle", xx1, xx2, angle ); + return TRUE; +} + static trackCmd_t easementCmds = { "JOINT", @@ -1477,7 +1490,13 @@ static trackCmd_t easementCmds = { NULL, NULL, NULL, - MakeParallelJoint }; + MakeParallelJoint, + NULL, + NULL, + NULL, + NULL, + NULL, + CompareJoint }; EXPORT void JointSegProc( @@ -1703,9 +1722,28 @@ LOG( log_ease, 1, ( " EASE R%0.3f..%0.3f L%0.3f..%0.3f\n", ConnectTracks( trk0, ep0, trk1, ep1 ); } else { /* Connect with transition-curve */ - joint = NewJoint( GetTrkEndPos(trk0,ep0), GetTrkEndAngle(trk0,ep0), + if (easementVal<0.0) { //Cornu Easements + coOrd pos[2]; + pos[0] = GetTrkEndPos(trk0,ep0); + pos[1] = GetTrkEndPos(trk1,ep1); + DIST_T radius[2]; + trackParams_t params0, params1; + GetTrackParams(PARAMS_CORNU,trk0,pos0,¶ms0); + GetTrackParams(PARAMS_CORNU,trk1,pos1,¶ms1); + radius[0] = params0.arcR; + radius[1] = params1.arcR; + coOrd center[2]; + center[0] = params0.arcP; + center[1] = params1.arcP; + ANGLE_T angle[2]; + angle[0] = NormalizeAngle(GetTrkEndAngle(trk0,ep0)+180.0); + angle[1] = NormalizeAngle(GetTrkEndAngle(trk1,ep1)+180.0); + joint = NewCornuTrack(pos,center,angle,radius, NULL, 0); + } else { + 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 ); @@ -1782,7 +1820,7 @@ track_p NewTrack( TRKINX_T a, TRKTYP_T b, EPINX_T c, TRKTYP_T d ) } void DrawStraightTrack( drawCmd_p a, coOrd b, coOrd c, ANGLE_T d, - DIST_T trackGauge, wDrawColor color, int opts ) + track_p trk, wDrawColor color, int opts ) { } diff --git a/app/bin/textnoteui.c b/app/bin/textnoteui.c new file mode 100644 index 0000000..331cfb5 --- /dev/null +++ b/app/bin/textnoteui.c @@ -0,0 +1,243 @@ +/** \file textnoteui.c + * View for the text note + */ + + /* XTrkCad - Model Railroad CAD + * Copyright (C) 2018 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 <string.h> +#include <stdbool.h> + +#include "custom.h" +#include "dynstring.h" +#include "i18n.h" +#include "misc.h" +#include "note.h" +#include "param.h" +#include "shortentext.h" +#include "track.h" +#include "wlib.h" + +static struct extraDataNote noteDataInUI; + +static paramTextData_t noteTextData = { 300, 150 }; +static paramFloatRange_t r_1000_1000 = { -1000.0, 1000.0, 80 }; +static paramData_t textEditPLs[] = { +#define I_ORIGX (0) + /*0*/ { PD_FLOAT, ¬eDataInUI.pos.x, "origx", PDO_DIM, &r_1000_1000, N_("Position X") }, +#define I_ORIGY (1) + /*1*/ { PD_FLOAT, ¬eDataInUI.pos.y, "origy", PDO_DIM, &r_1000_1000, N_("Position Y") }, +#define I_LAYER (2) + /*2*/ { PD_DROPLIST, ¬eDataInUI.layer, "layer", 0, (void*)150, "Layer", 0 }, +#define I_TEXT (3) + /*3*/ { PD_TEXT, NULL, "text", PDO_NOPREF, ¬eTextData, N_("Note") } +}; + +static paramGroup_t textEditPG = { "textEdit", 0, textEditPLs, sizeof textEditPLs / sizeof textEditPLs[0] }; +static wWin_p textEditW; + +#define textEntry ((wText_p)textEditPLs[I_TEXT].control) + +extern BOOL_T inDescribeCmd; + +/** + * Return the current text + * + */ +static void GetNoteTextData() +{ + int len; + + if (noteDataInUI.noteData.text ) { + MyFree(noteDataInUI.noteData.text); + } + len = wTextGetSize(textEntry); + noteDataInUI.noteData.text = (char*)MyMalloc(len + 2); + wTextGetText(textEntry, noteDataInUI.noteData.text, len); + return; +} + +/** + * Check validity of entered text + * + * \return always TRUE for testing + */ +BOOL_T +IsValidText(char * text) +{ + return(TRUE); +} + + +/** + * Callback for text note dialog + * + * \param pg IN unused + * \param inx IN index into dialog template + * \param valueP IN unused + */ +static void +TextDlgUpdate( + paramGroup_p pg, + int inx, + void * valueP) +{ + switch (inx) { + case I_ORIGX: + case I_ORIGY: + UpdateText(¬eDataInUI, OR_NOTE, FALSE); + break; + case I_LAYER: + UpdateText(¬eDataInUI, LY_NOTE, FALSE); + break; + case I_TEXT: + /** TODO: this is never called, why doesn't text update trigger this callback? */ + GetNoteTextData(); + if (IsValidText(noteDataInUI.noteData.text)) { + ParamDialogOkActive(&textEditPG, TRUE); + } else { + ParamDialogOkActive(&textEditPG, FALSE); + } + break; + default: + break; + } +} + +/** +* Handle Cancel button: restore old values for layer and position +*/ + +static void +TextEditCancel( wWin_p junk ) +{ + if (inDescribeCmd) { + UpdateText(¬eDataInUI, CANCEL_NOTE, FALSE); + } + ResetIfNotSticky(); + wHide(textEditW); +} + +/** + * Handle OK button: make sure the entered URL is syntactically valid, update + * the layout and close the dialog + * + * \param junk + */ + +static void +TextEditOK(void *junk) +{ + GetNoteTextData(); + UpdateText(¬eDataInUI, OK_TEXT, FALSE); + wHide(textEditW); + ResetIfNotSticky(); + FileIsChanged(); +} + + + +/** + * Create the edit dialog for text notes. + * + * \param trk IN selected note + * \param title IN dialog title + */ +static void +CreateEditTextNote(track_p trk, char *title) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + + // create the dialog if necessary + if (!textEditW) { + ParamRegister(&textEditPG); + textEditW = ParamCreateDialog(&textEditPG, + "", + _("Done"), TextEditOK, + TextEditCancel, TRUE, NULL, + F_BLOCK, + TextDlgUpdate); + } + + wWinSetTitle(textEditPG.win, MakeWindowTitle(title)); + + // initialize the dialog fields + noteDataInUI.pos = xx->pos; + noteDataInUI.layer = xx->layer; + noteDataInUI.trk = trk; + + wTextClear(textEntry); + wTextAppend(textEntry, xx->noteData.text ); + wTextSetReadonly(textEntry, FALSE); + FillLayerList((wList_p)textEditPLs[I_LAYER].control); + ParamLoadControls(&textEditPG); + + // and show the dialog + wShow(textEditW); +} + +/** + * Show details in statusbar. If running in Describe mode, the describe dialog is opened for editing the note + * + * \param trk IN the selected track (note) + * \param str IN the buffer for the description string + * \param len IN length of string buffer str +*/ + +void DescribeTextNote(track_p trk, char * str, CSIZE_T len) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + char *noteText; + DynString statusLine; + + noteText = MyMalloc(strlen(xx->noteData.text) + 1); + RemoveFormatChars(xx->noteData.text, noteText); + EllipsizeString(noteText, NULL, 80); + DynStringMalloc(&statusLine, 100); + + DynStringPrintf(&statusLine, + _("Note: Layer=%d %-.80s"), + GetTrkLayer(trk)+1, + noteText ); + strcpy(str, DynStringToCStr(&statusLine)); + + DynStringFree(&statusLine); + MyFree(noteText); + + if (inDescribeCmd) { + NoteStateSave(trk); + + CreateEditTextNote(trk, _("Update comment")); + } +} + +/** + * Show the UI for entering new text notes + * + * \param xx Note object data + */ + +void NewTextNoteUI(track_p trk) { + struct extraDataNote * xx = (struct extraDataNote *)GetTrkExtraData(trk); + char *tmpPtrText = _("Replace this text with your note"); + + xx->noteData.text = MyStrdup(tmpPtrText); + + CreateEditTextNote(trk, _("Create Text Note")); +} + diff --git a/app/bin/tocornu.src b/app/bin/tocornu.src new file mode 100644 index 0000000..6184300 --- /dev/null +++ b/app/bin/tocornu.src @@ -0,0 +1,21 @@ +CURVE, 25, 100, 325, 80, 1700, +CURVE, 25, 100, 300, 40, 600, +LINE, 25, 10, 25, 140, +LINE, 75, 70, 75, 120, +ARROW, 25, 70, 50, 70, +ARROW, 75, 70, 50, 70, +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/tocornu3way.src b/app/bin/tocornu3way.src new file mode 100644 index 0000000..3185e49 --- /dev/null +++ b/app/bin/tocornu3way.src @@ -0,0 +1,33 @@ +CURVE, 25, 100, 305, 90, 1200 +CURVE, 45, 100, 290, 50, 500 +CURVE, 65, 100, 280, 145, -600 +LINE, 294, 35, 306, 65, +LINE, 294, 165, 306, 135, +ARROW, 25, 100, 70, 100, +ARROW, 322, 100, 150, 100, +ARROW, 25, 20, 120, 20, +ARROW, 300, 20, 200, 20, +ARROW, 25, 180, 125, 180, +ARROW, 290, 180, 225, 180, +ARROW, 25, 70, 35, 70, +ARROW, 45, 70, 35, 70, +ARROW, 25, 140, 45, 140, +ARROW, 65, 140, 45, 140, +LINE, 45, 70, 45, 100 +LINE, 65, 140, 65, 100 +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, 320, 75, 325, 105, +LINE, 325, 90, 425, 90, +LINE, 325, 90, 425, 70, +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, +LINE, 322, 100, 425, 100, +LINE, 322, 100, 322, 90, diff --git a/app/bin/tocornuwye.src b/app/bin/tocornuwye.src new file mode 100644 index 0000000..82a411c --- /dev/null +++ b/app/bin/tocornuwye.src @@ -0,0 +1,24 @@ +STRAIGHT, 25, 100, 75, 100, +CURVE, 75, 100, 300, 50, 600, +CURVE, 75, 100, 300, 150, -600, +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, +LINE, 75, 70, 75, 120, +ARROW, 25, 70, 50, 70, +ARROW, 75, 70, 50, 70, diff --git a/app/bin/track.c b/app/bin/track.c index 0660d7d..c9ec7db 100644 --- a/app/bin/track.c +++ b/app/bin/track.c @@ -43,6 +43,8 @@ #include "paths.h" #include "track.h" #include "utility.h" +#include "misc.h" +#include "ctrain.h" #ifndef TRACKDEP #ifndef FASTTRACK @@ -59,6 +61,8 @@ #endif #endif +EXPORT char tempSpecial[4096]; + static int log_track = 0; static int log_endPt = 0; static int log_readTracks = 0; @@ -110,6 +114,8 @@ EXPORT BOOL_T onTrackInSplit = FALSE; static BOOL_T inDrawTracks; +static wBool_t bWriteEndPtDirectIndex = FALSE; + #ifndef TRACKDEP /***************************************************************************** @@ -117,10 +123,17 @@ static BOOL_T inDrawTracks; * * */ +EXPORT void ActivateTrack( track_cp trk) { + int inx = GetTrkType(trk); + if (trackCmds( inx )->activate != NULL) + trackCmds( inx )->activate (trk); + +} EXPORT void DescribeTrack( track_cp trk, char * str, CSIZE_T len ) { + trackCmds( GetTrkType(trk) )->describe ( trk, str, len ); /*epCnt = GetTrkEndPtCnt(trk); if (debugTrack >= 2) @@ -131,7 +144,7 @@ EXPORT void DescribeTrack( track_cp trk, char * str, CSIZE_T len ) EXPORT DIST_T GetTrkDistance( track_cp trk, coOrd * pos ) { - return trackCmds( GetTrkType(trk) )->distance( trk, pos ); + return fabs(trackCmds( GetTrkType(trk) )->distance( trk, pos )); } /** @@ -176,13 +189,14 @@ EXPORT track_p OnTrack2( coOrd * fp, BOOL_T complain, BOOL_T track, BOOL_T ignor } p = *fp; distance = trackCmds( GetTrkType(trk) )->distance( trk, &p ); - if (distance < closestDistance) { + if (fabs(distance) <= fabs(closestDistance)) { //Make the last (highest) preferred closestDistance = distance; closestTrack = trk; closestPos = p; } } - if (closestTrack && (closestDistance <= mainD.scale*0.25 || closestDistance <= trackGauge*2.0) ) { + if (closestTrack && closestDistance <0 ) closestDistance = 0.0; //Turntable was closest - inside well + if (closestTrack && ((closestDistance <= mainD.scale*0.25) || (closestDistance <= trackGauge*2.0) )) { *fp = closestPos; return closestTrack; } @@ -214,9 +228,21 @@ EXPORT BOOL_T CheckTrackLayer( track_p trk ) if (GetLayerFrozen( GetTrkLayer( trk ) ) ) { ErrorMessage( MSG_CANT_MODIFY_FROZEN_TRK ); return FALSE; - } else { + } else if (GetLayerModule( GetTrkLayer( trk ) ) ) { + ErrorMessage( MSG_CANT_MODIFY_MODULE_TRK ); + return FALSE; + } else + return TRUE; +} + +EXPORT BOOL_T CheckTrackLayerSilent( track_p trk ) +{ + if (GetLayerFrozen( GetTrkLayer( trk ) ) ) { + return FALSE; + } else if (GetLayerModule( GetTrkLayer( trk ) ) ) { + return FALSE; + } else return TRUE; - } } /****************************************************************************** @@ -249,7 +275,6 @@ EXPORT void EnumerateTracks( void ) trackCmds(inx)->enumerate( NULL ); EnumerateEnd(); - Reset(); } /***************************************************************************** @@ -327,7 +352,9 @@ EXPORT TRKTYP_T GetTrkType( track_p trk ) EXPORT SCALEINX_T GetTrkScale( track_p trk ) { - return (SCALEINX_T)trk->scale; + if ( trk ) + return (SCALEINX_T)trk->scale; + return 0; } EXPORT void SetTrkScale( track_p trk, SCALEINX_T si ) @@ -369,6 +396,7 @@ EXPORT struct extraData * GetTrkExtraData( track_cp trk ) EXPORT void SetTrkEndPoint( track_p trk, EPINX_T ep, coOrd pos, ANGLE_T angle ) { + ASSERT( ep < trk->endCnt ); if (trk->endPt[ep].track != NULL) { AbortProg( "setTrkEndPoint: endPt is connected" ); } @@ -378,29 +406,43 @@ EXPORT void SetTrkEndPoint( track_p trk, EPINX_T ep, coOrd pos, ANGLE_T angle ) EXPORT coOrd GetTrkEndPos( track_p trk, EPINX_T e ) { + ASSERT( e < trk->endCnt ); return trk->endPt[e].pos; } EXPORT ANGLE_T GetTrkEndAngle( track_p trk, EPINX_T e ) { + ASSERT( e < trk->endCnt ); return trk->endPt[e].angle; } EXPORT track_p GetTrkEndTrk( track_p trk, EPINX_T e ) { + ASSERT( e < trk->endCnt ); return trk->endPt[e].track; } EXPORT long GetTrkEndOption( track_p trk, EPINX_T e ) { + ASSERT( e < trk->endCnt ); return trk->endPt[e].option; } EXPORT long SetTrkEndOption( track_p trk, EPINX_T e, long option ) { + ASSERT( e < trk->endCnt ); return trk->endPt[e].option = option; } +EXPORT DIST_T GetTrkGauge( + track_cp trk ) +{ + if (trk) + return GetScaleTrackGauge( GetTrkScale( trk ) ); + else + return trackGauge; +} + EXPORT int GetTrkWidth( track_p trk ) { return (int)trk->width; @@ -441,6 +483,7 @@ EXPORT void SetTrkEndElev( track_p trk, EPINX_T ep, int option, DIST_T height, c track_p trk1; EPINX_T ep1; trk->endPt[ep].elev.option = option; + trk->endPt[ep].elev.cacheSet = FALSE; if (EndPtIsDefinedElev(trk,ep)) { trk->endPt[ep].elev.u.height = height; } else if (EndPtIsStationElev(trk,ep)) { @@ -487,6 +530,23 @@ EXPORT DIST_T GetTrkEndElevHeight( track_p trk, EPINX_T e ) return trk->endPt[e].elev.u.height; } +EXPORT BOOL_T GetTrkEndElevCachedHeight (track_p trk, EPINX_T e, DIST_T * height, DIST_T * length) +{ + if (trk->endPt[e].elev.cacheSet) { + *height = trk->endPt[e].elev.cachedElev; + *length = trk->endPt[e].elev.cachedLength; + return TRUE; + } + return FALSE; +} + +EXPORT void SetTrkEndElevCachedHeight ( track_p trk, EPINX_T e, DIST_T height, DIST_T length) +{ + trk->endPt[e].elev.cachedElev = height; + trk->endPt[e].elev.cachedLength = length; + trk->endPt[e].elev.cacheSet = TRUE; +} + EXPORT char * GetTrkEndElevStation( track_p trk, EPINX_T e ) { @@ -531,13 +591,21 @@ void SetTrkLayer( track_p trk, int layer ) EXPORT int ClrAllTrkBits( int bits ) { + return ClrAllTrkBitsRedraw( bits, FALSE ); +} + + +EXPORT int ClrAllTrkBitsRedraw( int bits, wBool_t bRedraw ) +{ track_p trk; - int cnt; - cnt = 0; + int cnt = 0; TRK_ITERATE( trk ) { - if (trk->bits&bits) + if (trk->bits&bits) { cnt++; - trk->bits &= ~bits; + trk->bits &= ~bits; + if ( bRedraw ) + DrawNewTrack( trk ); + } } return cnt; } @@ -549,6 +617,9 @@ EXPORT void SetTrkElev( track_p trk, int mode, DIST_T elev ) SetTrkBits( trk, TB_ELEVPATH ); trk->elev = elev; trk->elevMode = mode; + for (int i=0;i<trk->endCnt;i++) { + trk->endPt[i].elev.cacheSet = FALSE; + } } @@ -606,7 +677,9 @@ EXPORT BOOL_T WriteEndPt( FILE * f, track_cp trk, EPINX_T ep ) long option; assert ( endPt != NULL ); - if (endPt->track == NULL || + if (bWriteEndPtDirectIndex && endPt->index > 0) { + rc &= fprintf( f, "\tT4 %d ", endPt->index )>0; + } else if (endPt->track == NULL || ( exportingTracks && !GetTrkSelected(endPt->track) ) ) { rc &= fprintf( f, "\tE4 " )>0; } else { @@ -651,7 +724,7 @@ EXPORT EPINX_T PickEndPoint( coOrd p, track_cp trk ) if (trk->endCnt <= 0) return -1; if ( onTrackInSplit && trk->endCnt > 2 ) { - if (GetTrkType(trk) != T_TURNOUT) + if (GetTrkType(trk) == T_TURNOUT) return TurnoutPickEndPt( p, trk ); } d = FindDistance( p, trk->endPt[0].pos ); @@ -875,12 +948,14 @@ EXPORT BOOL_T MakeParallelTrack( track_p trk, coOrd pos, DIST_T dist, + DIST_T factor, track_p * newTrkR, coOrd * p0R, - coOrd * p1R ) + coOrd * p1R, + BOOL_T track) { if ( trackCmds(trk->type)->makeParallel ) - return trackCmds(trk->type)->makeParallel( trk, pos, dist, newTrkR, p0R, p1R ); + return trackCmds(trk->type)->makeParallel( trk, pos, dist, factor, newTrkR, p0R, p1R, track); return FALSE; } @@ -1011,7 +1086,7 @@ EXPORT void ClearTracks( void ) to_first = NULL; to_last = &to_first; max_index = 0; - changed = 0; + changed = checkPtMark = 0; trackCount = 0; ClearCars(); InfoCount( trackCount ); @@ -1033,11 +1108,14 @@ EXPORT void ResolveIndex( void ) track_p trk; EPINX_T ep; TRK_ITERATE(trk) { + LOG (log_track, 1, ( "ResNextTrack( T%d, t%d, E%d, X%ld)\n", trk->index, trk->type, trk->endCnt, trk->extraSize )); 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 ); + int rc = NoticeMessage( MSG_RESOLV_INDEX_BAD_TRK, _("Continue"), _("Quit"), trk->index, ep, trk->endPt[ep].index ); + if ( rc != 1 ) + return; } } ResolveBlockTrack (trk); @@ -1063,29 +1141,21 @@ LOG( log_track, 4, ( "DeleteTrack(T%d)\n", GetTrkIndex(trk) ) ) } } } - 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 ); } } - CheckDeleteSwitchmotor( trk ); - CheckDeleteBlock( trk ); + CheckDeleteSwitchmotor( trk ); + CheckDeleteBlock( trk ); + CheckCarTraverse( trk ); DecrementLayerObjects(trk->layer); trackCount--; AuditTracks( "deleteTrack T%d", trk->index); UndoDelete(trk); /**< Attention: trk is invalidated during that call */ - MainRedraw(); - MapRedraw(); InfoCount( trackCount ); return TRUE; } @@ -1130,6 +1200,98 @@ BOOL_T TrackIterate( track_p * trk ) *trk = trk1; return trk1 != NULL; } + + +wBool_t IsPosClose( coOrd pos1, coOrd pos2 ) { + DIST_T d = FindDistance( pos1, pos2 ); + return d < 0.1; +} + + +wBool_t IsAngleClose( ANGLE_T angle1, ANGLE_T angle2 ) +{ + ANGLE_T angle = NormalizeAngle( angle1 - angle2 ); + if (angle > 180) + angle = 360-angle; + return angle < 0.01; +} + + +wBool_t IsDistClose( DIST_T dist1, DIST_T dist2 ) +{ + DIST_T dist = fabs( dist1 - dist2 ); + return dist < 0.01; +} + +wBool_t IsWidthClose( DIST_T dist1, DIST_T dist2 ) +{ + // width is computed by pixels/dpi + // problem is when widths are computed on platforms with differing dpi + DIST_T dist = fabs( dist1 - dist2 ); + if ( dist < 0.01 ) + return TRUE; +#ifdef WINDOWS + dist1 *= 96.0/72.0; +#else + dist1 *= 72.0/96.0; +#endif + dist = fabs( dist1 - dist2 ); + if ( dist < 0.01 ) + return TRUE; + return FALSE; +} + +wBool_t IsColorClose( wDrawColor color1, wDrawColor color2 ) +{ + long rgb1 = wDrawGetRGB( color1 ); + long rgb2 = wDrawGetRGB( color2 ); + int r1 = (rgb1 >> 16) & 0xff; + int g1 = (rgb1 >> 8) & 0xff; + int b1 = rgb1 & 0xff; + int r2 = (rgb2 >> 16) & 0xff; + int g2 = (rgb2 >> 8) & 0xff; + int b2 = rgb2 & 0xff; + long diff = abs(r1-r2) + abs(g1-g2) + abs(b1-b2); + return (diff < 7); +} + +wBool_t CompareTrack( track_cp trk1, track_cp trk2 ) +{ + wBool_t rc = FALSE; + if ( trk1 == NULL ) { + sprintf( message, "Compare: T%d not found\n", trk2->index ); + return FALSE; + } + sprintf( message, "Compare T:%d - ", GetTrkIndex(trk1) ); + char * cp = message+strlen(message); + REGRESS_CHECK_INT( "Type", trk1, trk2, type ) + REGRESS_CHECK_INT( "Index", trk1, trk2, index ) + REGRESS_CHECK_INT( "Layer", trk1, trk2, layer ) + REGRESS_CHECK_INT( "Scale", trk1, trk2, scale ) + REGRESS_CHECK_INT( "EndPtCnt", trk1, trk2, endCnt ) + char * cq = cp-2; + for ( int inx=0; inx<GetTrkEndPtCnt( trk1 ); inx++ ) { + cp = cq; + sprintf( cp, "EP:%d - ", inx ); + cp += strlen(cp); + REGRESS_CHECK_POS( "Pos", trk1, trk2, endPt[inx].pos ) + REGRESS_CHECK_ANGLE( "Angle", trk1, trk2, endPt[inx].angle ) + int inx1 = trk1->endPt[inx].index; + track_cp trk1x = GetTrkEndTrk( trk1, inx ); + if ( trk1x ) + inx1 = GetTrkIndex( trk1x ); + int inx2 = trk2->endPt[inx].index; + if ( inx1 != inx2 ) { + sprintf( cp, "Index: Actual` %d, Expected %d\n", inx1, inx2 ); + return FALSE; + } + REGRESS_CHECK_INT( "Option", trk1, trk2, endPt[inx].option ) + } + if ( trackCmds( GetTrkType( trk1 ) )->compare == NULL ) + return TRUE; + return trackCmds( GetTrkType( trk1 ) )->compare( trk1, trk2 ); +} + /***************************************************************************** * @@ -1221,9 +1383,9 @@ EXPORT void InitCmdAboveBelow( void ) { wIcon_p bm_p; bm_p = wIconCreatePixMap( above_xpm ); - AddToolbarButton( "cmdAbove", bm_p, IC_SELECTED, (addButtonCallBack_t)SelectAbove, NULL ); + AddToolbarButton( "cmdAbove", bm_p, IC_SELECTED|IC_POPUP, (addButtonCallBack_t)SelectAbove, NULL ); bm_p = wIconCreatePixMap( below_xpm ); - AddToolbarButton( "cmdBelow", bm_p, IC_SELECTED, (addButtonCallBack_t)SelectBelow, NULL ); + AddToolbarButton( "cmdBelow", bm_p, IC_SELECTED|IC_POPUP, (addButtonCallBack_t)SelectBelow, NULL ); } /***************************************************************************** @@ -1244,6 +1406,15 @@ EXPORT BOOL_T ReadTrack( char * line ) { TRKINX_T inx, lo, hi; int cmp; + if (strncmp( paramLine, "TABLEEDGE ", 10 ) == 0) { + ReadTableEdge( paramLine+10 ); + return TRUE; + } + if (strncmp( paramLine, "TEXT ", 5 ) == 0) { + ReadText( paramLine+5 ); + return TRUE; + } + if (bsearchRead) { if (sortedCmds == NULL) { sortedCmds = (trackCmd_t**)MyMalloc( (trackCmds_da.cnt-1) * sizeof *(trackCmd_t*)0 ); @@ -1258,8 +1429,7 @@ if (bsearchRead) { inx = (lo+hi)/2; cmp = strncmp( line, sortedCmds[inx]->name, strlen(sortedCmds[inx]->name) ); if (cmp == 0) { - sortedCmds[inx]->read(line); - return TRUE; + return sortedCmds[inx]->read(line); } else if (cmp < 0) { hi = inx-1; } else { @@ -1269,28 +1439,33 @@ if (bsearchRead) { } 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; + trackCmds(inx)->read( line ); + // Return TRUE means we found the object type and processed it + // Any errors will be handled by the callee's: + // Either skip the definition (ReadSegs) or skip the remainder of the file (InputError) + return TRUE; } } } - if (strncmp( paramLine, "TABLEEDGE ", 10 ) == 0) - return ReadTableEdge( paramLine+10 ); - if (strncmp( paramLine, "TEXT ", 5 ) == 0) - return ReadText( paramLine+5 ); + // Object type not found return FALSE; } -EXPORT BOOL_T WriteTracks( FILE * f ) +EXPORT BOOL_T WriteTracks( FILE * f, wBool_t bFull ) { track_p trk; BOOL_T rc = TRUE; - RenumberTracks(); + if ( bFull ) + RenumberTracks(); + if ( !bFull ) + bWriteEndPtDirectIndex = TRUE; TRK_ITERATE( trk ) { rc &= trackCmds(GetTrkType(trk))->write( trk, f ); } - rc &= WriteCars( f ); + bWriteEndPtDirectIndex = FALSE; + if ( bFull ) + rc &= WriteCars( f ); return rc; } @@ -1302,7 +1477,9 @@ EXPORT void ImportStart( void ) } -EXPORT void ImportEnd( void ) + + +EXPORT void ImportEnd( coOrd offset, wBool_t import, wBool_t inPlace ) { track_p to_firstOld; wIndex_t trackCountOld; @@ -1310,51 +1487,98 @@ EXPORT void ImportEnd( void ) coOrd pos; wPos_t x, y; wPos_t ww, hh; + wBool_t offscreen = FALSE; + + double xmin = 0.0; + double xmax = 0.0; + double ymin = 0.0; 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; - } + coOrd middle_screen; + wPos_t mx,my; + + mx = ww/2; + my = hh/2; + + mainD.Pix2CoOrd( &mainD, mx, my, &middle_screen ); + + + for ( trk=*importTrack; trk; trk=trk->next ) { + if (!IsTrackDeleted(trk)) { + if (trk->hi.y > ymax ) ymax = trk->hi.y; + if (trk->lo.y < ymin ) ymin = trk->lo.y; + if (trk->hi.x > xmax ) xmax = trk->hi.x; + if (trk->lo.x < xmin ) xmin = trk->lo.x; } - pos.y -= ymax; } - + + coOrd size = {xmax-xmin,ymax-ymin}; + + + + if (import) { + offset = zero; + } else if (!inPlace) { + //If cursor is off drawing area - cursor is in middle screen + if ((x<LBORDER) || (x>(ww-RBORDER)) || (y<BBORDER) || (y>(hh-TBORDER))) { + pos.x = middle_screen.x; + pos.y = middle_screen.y; + } + offset.x += pos.x; + offset.y += pos.y; + } + + coOrd middle_object; + + middle_object.x = offset.x + (size.x/2); + middle_object.y = offset.y + (size.y/2); + + wPos_t ox,oy; + mainD.CoOrd2Pix( &mainD, middle_object, &ox, &oy ); + + if ((ox<0) || (ox>ww) || (oy<0) || (oy>hh) ) offscreen = TRUE; + 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 ); + coOrd move; + move.x = offset.x; + move.y = offset.y; + MoveTrack( trk, move );// mainD.orig ); trk->bits |= TB_SELECTED; DrawTrack( trk, &mainD, wDrawColorBlack ); } - DrawMapBoundingBox( TRUE ); importTrack = NULL; trackCount = trackCountOld; InfoCount( trackCount ); + // Pan screen if needed to center of new + if (offscreen) { + panCenter = middle_object; + PanHere((void*)0); + } } - -EXPORT BOOL_T ExportTracks( FILE * f ) +/******* + * Move Selected Tracks to origin zero and write out + *******/ +EXPORT BOOL_T ExportTracks( FILE * f, coOrd * offset) { track_p trk; - coOrd xlat, orig; + coOrd xlat,orig; exportingTracks = TRUE; orig = mapD.size; @@ -1368,8 +1592,9 @@ EXPORT BOOL_T ExportTracks( FILE * f ) trk->index = ++max_index; } } - orig.x -= trackGauge; - orig.y -= trackGauge; + + *offset = orig; + xlat.x = - orig.x; xlat.y = - orig.y; TRK_ITERATE( trk ) { @@ -1527,13 +1752,13 @@ nextEndPt:; if (auditStop) if (NoticeMessage( MSG_AUDIT_WRITE_FILE, _("Yes"), _("No"))) { fprintf( auditFile, "# before undo\n" ); - WriteTracks(auditFile); + WriteTracks(auditFile, TRUE); Rdump( auditFile ); if (strcmp("undoUndo",event)==0) { fprintf( auditFile, "# failure in undo\n" ); } else if (UndoUndo()) { fprintf( auditFile, "# after undo\n" ); - WriteTracks(auditFile); + WriteTracks(auditFile, TRUE); Rdump( auditFile ); } else { fprintf( auditFile, "# undo stack is empty\n" ); @@ -1584,21 +1809,33 @@ EXPORT void ComputeBoundingBox( track_p trk ) EXPORT DIST_T EndPtDescriptionDistance( coOrd pos, track_p trk, - EPINX_T ep ) + EPINX_T ep, + coOrd *dpos, + BOOL_T show_hidden, + BOOL_T * hidden) { elev_t *e; coOrd pos1; track_p trk1; + *dpos = pos; + if (hidden) *hidden = FALSE; e = &trk->endPt[ep].elev; - if ((e->option&ELEV_MASK)==ELEV_NONE || - (e->option&ELEV_VISIBLE)==0 ) + if ((e->option&ELEV_MASK)==ELEV_NONE) + return 100000; + if (((e->option&ELEV_VISIBLE)==0) && !show_hidden) return 100000; if ((trk1=GetTrkEndTrk(trk,ep)) && GetTrkIndex(trk1)<GetTrkIndex(trk)) return 100000; + if ((e->option&ELEV_VISIBLE)==0) { //Hidden - disregard offset + if (hidden) *hidden = TRUE; + return FindDistance( GetTrkEndPos(trk,ep), pos ); + } /*REORIGIN( pos1, e->doff, GetTrkEndPos(trk,ep), GetTrkEndAngle(trk,ep) );*/ pos1 = GetTrkEndPos(trk,ep); pos1.x += e->doff.x; pos1.y += e->doff.y; + *dpos = pos1; + if (hidden) *hidden = !(e->option&ELEV_VISIBLE); return FindDistance( pos1, pos ); } @@ -1610,22 +1847,21 @@ EXPORT STATUS_T EndPtDescriptionMove( coOrd pos ) { static coOrd p0, p1; + static BOOL_T editState = FALSE; 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) );*/ - + p1 = pos; + e->option |= ELEV_VISIBLE; //Make sure we make visible + DrawEndElev( &mainD, trk, ep, wDrawColorWhite ); + /*no break*/ 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 ); + editState = TRUE; p1 = pos; e->doff.x = (pos.x-p0.x); e->doff.y = (pos.y-p0.y); @@ -1633,15 +1869,18 @@ EXPORT STATUS_T EndPtDescriptionMove( 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(); - MapRedraw(); + if ( action == C_UP ) { + editState = FALSE; + wDrawColor color = GetTrkColor( trk, &mainD ); + DrawEndElev( &mainD, trk, ep, color ); + } return action==C_UP?C_TERMINATE:C_CONTINUE; case C_REDRAW: - DrawLine( &tempD, p0, p1, 0, wDrawColorBlack ); + DrawEndElev( &tempD, trk, ep, wDrawColorBlue ); + if ( editState ) { + DrawLine( &tempD, p0, p1, 0, wDrawColorBlue ); + } break; } return C_CONTINUE; @@ -1692,12 +1931,12 @@ EXPORT void LoosenTracks( void ) } } if (count) - MainRedraw(); + MainRedraw(); // LoosenTracks else InfoMessage(_("No tracks loosened")); } -EXPORT void ConnectTracks( track_p trk0, EPINX_T inx0, track_p trk1, EPINX_T inx1 ) +EXPORT int ConnectTracks( track_p trk0, EPINX_T inx0, track_p trk1, EPINX_T inx1 ) { DIST_T d; ANGLE_T a; @@ -1705,31 +1944,23 @@ EXPORT void ConnectTracks( track_p trk0, EPINX_T inx0, track_p trk1, EPINX_T inx if ( !IsTrack(trk0) ) { NoticeMessage( _("Connecting a non-track(%d) to (%d)"), _("Continue"), NULL, GetTrkIndex(trk0), GetTrkIndex(trk1) ); - return; + return -1; } if ( !IsTrack(trk1) ) { NoticeMessage( _("Connecting a non-track(%d) to (%d)"), _("Continue"), NULL, GetTrkIndex(trk1), GetTrkIndex(trk0) ); - return; + return -1; } 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 ); - if (d > connectDistance || (a > connectAngle && a < 360.0 - connectAngle) || (log_endPt>0 && 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 - if (d > connectDistance || (a > connectAngle && a < 360.0 - connectAngle)) - NoticeMessage( MSG_CONNECT_TRK, _("Continue"), NULL, trk0->index, inx0, trk1->index, inx1, d, a ); + a = fabs(DifferenceBetweenAngles( trk0->endPt[inx0].angle, + trk1->endPt[inx1].angle + 180.0 )); + if (d > connectDistance || (a > connectAngle ) ) { + LOG( log_endPt, 1, ( "connectTracks: T%d[%d] T%d[%d] d=%0.3f a=%0.3f\n", + trk0->index, inx0, trk1->index, inx1, d, a ) ); + NoticeMessage( MSG_CONNECT_TRK, _("Continue"), NULL, trk0->index, inx0, trk1->index, inx1, d, a ); + return -1; /* Stop connecting out of alignment tracks! */ } UndoModify( trk0 ); UndoModify( trk1 ); @@ -1738,6 +1969,7 @@ LOG( log_track, 3, ( "ConnectTracks( T%d[%d] @ [%0.3f, %0.3f] = T%d[%d] @ [%0.3f trk0->endPt[inx0].track = trk1; trk1->endPt[inx1].track = trk0; AuditTracks( "connectTracks T%d[%d], T%d[%d]", trk0->index, inx0, trk1->index, inx1 ); + return 0; } @@ -1834,7 +2066,6 @@ LOG( log_track, 2, ( "SplitTrack( T%d[%d], (%0.3f %0.3f)\n", trk->index, ep, pos } else { LOG( log_track, 2, ( " at endPt (no connection)\n") ) } - *leftover = trk2; DrawNewTrack( trk ); @@ -1855,7 +2086,6 @@ LOG( log_track, 2, ( "SplitTrack( T%d[%d], (%0.3f %0.3f)\n", trk->index, ep, pos LOG( log_track, 2, ( " at endPt (no connection)\n") ) } DrawNewTrack( trk ); - } else { /* TODO circle's don't have ep's */ trk2 = GetTrkEndTrk( trk, ep ); @@ -1878,13 +2108,13 @@ LOG( log_track, 2, ( "SplitTrack( T%d[%d], (%0.3f %0.3f)\n", trk->index, ep, pos trkl = *leftover; ep0 = epl; if ( !disconnect ) - ConnectTracks( trk, ep, trkl, ep0 ); - ep0 = 1-ep0; + ConnectTracks( trk, ep, trkl, epl ); + ep0 = 1-epl; while ( 1 ) { CopyAttributes( trk, trkl ); ClrTrkElev( trkl ); trk0 = GetTrkEndTrk(trkl,ep0); - if ( trk0 == NULL ) + if ( trk0 == NULL || trk0->type == T_TURNOUT ) break; ep0 = 1-GetEndPtConnectedToMe(trk0,trkl); trkl = trk0; @@ -1901,7 +2131,7 @@ LOG( log_track, 2, ( "SplitTrack( T%d[%d], (%0.3f %0.3f)\n", trk->index, ep, pos while ( 1 ) { DrawNewTrack( trkl ); trk0 = GetTrkEndTrk(trkl,ep0); - if ( trk0 == NULL || trk0 == trk2 ) + if ( trk0 == NULL || trk0 == trk2 || trk0->type == T_TURNOUT) break; ep0 = 1-GetEndPtConnectedToMe(trk0,trkl); trkl = trk0; @@ -1970,14 +2200,14 @@ EXPORT BOOL_T RemoveTrack( track_p * trk, EPINX_T * ep, DIST_T *dist ) } dist1 = *dist; *dist = 0.0; - return TrimTrack( *trk, *ep, dist1 ); + return TrimTrack( *trk, *ep, dist1, zero, 0.0, 0.0, zero ); } -EXPORT BOOL_T TrimTrack( track_p trk, EPINX_T ep, DIST_T dist ) +EXPORT BOOL_T TrimTrack( track_p trk, EPINX_T ep, DIST_T dist, coOrd pos, ANGLE_T angle, DIST_T radius, coOrd center ) { if (trackCmds(trk->type)->trim) - return trackCmds(trk->type)->trim( trk, ep, dist ); + return trackCmds(trk->type)->trim( trk, ep, dist, pos, angle, radius, center ); else return FALSE; } @@ -1992,6 +2222,130 @@ EXPORT BOOL_T MergeTracks( track_p trk0, EPINX_T ep0, track_p trk1, EPINX_T ep1 return FALSE; } +EXPORT STATUS_T ExtendTrackFromOrig( track_p trk, wAction_t action, coOrd pos ) +{ + static EPINX_T ep; + static coOrd end_pos; + static BOOL_T valid; + DIST_T d; + track_p trk1; + trackParams_t params; + static wBool_t curved; + static ANGLE_T end_angle; + + switch ( action ) { + case C_DOWN: + ep = PickUnconnectedEndPoint( pos, trk ); + if ( ep == -1 ) + return C_ERROR; + pos = GetTrkEndPos(trk,ep); + if (!GetTrackParams(PARAMS_CORNU,trk,pos,¶ms)) return C_ERROR; + end_pos = pos; + if (params.type == curveTypeCurve) { + curved = TRUE; + tempSegs(0).type = SEG_CRVTRK; + tempSegs(0).width = 0; + tempSegs(0).u.c.radius = params.arcR; + tempSegs(0).u.c.center = params.arcP; + tempSegs(0).u.c.a0 = FindAngle(params.arcP,GetTrkEndPos(trk,ep)); + tempSegs(0).u.c.a1 = 0; + } else { + curved = FALSE; + tempSegs(0).type = SEG_STRTRK; + tempSegs(0).width = 0; + tempSegs(0).u.l.pos[0] = tempSegs(0).u.l.pos[1] = GetTrkEndPos( trk, ep ); + } + valid = FALSE; + InfoMessage( _("Drag to change track length") ); + return C_CONTINUE; + /*no break*/ + case C_MOVE: + if (curved) { + //Normalize pos + PointOnCircle( &pos, tempSegs(0).u.c.center, tempSegs(0).u.c.radius, FindAngle(tempSegs(0).u.c.center,pos) ); + ANGLE_T a = FindAngle(tempSegs(0).u.c.center,pos)-FindAngle(tempSegs(0).u.c.center,end_pos); + d = fabs(a)*2*M_PI/360*tempSegs(0).u.c.radius; + if ( d <= minLength ) { + if (action == C_MOVE) + ErrorMessage( MSG_TRK_TOO_SHORT, _("Connecting "), PutDim(fabs(minLength-d)) ); + valid = FALSE; + return C_CONTINUE; + } + //Restrict to outside track + ANGLE_T diff = NormalizeAngle(GetTrkEndAngle(trk, ep)-FindAngle(end_pos,pos)); + if (diff>90.0 && diff<270.0) { + valid = FALSE; + tempSegs(0).u.c.a1 = 0; + tempSegs(0).u.c.a0 = end_angle; + InfoMessage( _("Inside turnout track")); + return C_CONTINUE; + } + end_angle = GetTrkEndAngle( trk, ep ); + a = FindAngle(tempSegs(0).u.c.center, pos ); + PointOnCircle( &pos, tempSegs(0).u.c.center, tempSegs(0).u.c.radius, a ); + ANGLE_T a2 = FindAngle(tempSegs(0).u.c.center,end_pos); + if ((end_angle > 180 && (a2>90 && a2 <270)) || + (end_angle < 180 && (a2<90 || a2 >270))) { + tempSegs(0).u.c.a0 = a2; + tempSegs(0).u.c.a1 = NormalizeAngle(a-a2); + } else { + tempSegs(0).u.c.a0 = a; + tempSegs(0).u.c.a1 = NormalizeAngle(a2-a); + } + tempSegs_da.cnt = 1; + valid = TRUE; + if (action == C_MOVE) + InfoMessage( _("Curve: Length=%s Radius=%0.3f Arc=%0.3f"), + FormatDistance( d ), FormatDistance(tempSegs(0).u.c.radius), PutAngle( fabs(a) ) ); + return C_CONTINUE; + } else { + d = FindDistance( end_pos, 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; + } + ANGLE_T diff = NormalizeAngle(GetTrkEndAngle( trk, ep )-FindAngle(end_pos, pos)); + if (diff>=90.0 && diff<=270.0) { + valid = FALSE; + tempSegs(0).u.c.a1 = 0; + tempSegs(0).u.c.a0 = end_angle; + InfoMessage( _("Inside turnout track")); + 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 ); + EPINX_T jp; + if (curved) { + trk1 = NewCurvedTrack(tempSegs(0).u.c.center, tempSegs(0).u.c.radius, tempSegs(0).u.c.a0, tempSegs(0).u.c.a1, 0); + jp = PickUnconnectedEndPoint(end_pos,trk1); + } else { + trk1 = NewStraightTrack( tempSegs(0).u.l.pos[0], tempSegs(0).u.l.pos[1] ); + jp = 0; + } + CopyAttributes( trk, trk1 ); + ConnectTracks( trk, ep, trk1, jp ); + DrawNewTrack( trk ); + DrawNewTrack( trk1 ); + return C_TERMINATE; + + default: + ; + } + return C_ERROR; +} EXPORT STATUS_T ExtendStraightFromOrig( track_p trk, wAction_t action, coOrd pos ) { @@ -2106,6 +2460,9 @@ EXPORT char * GetTrkTypeName( track_p trk ) return trackCmds(trk->type)->name; } +/* + * Note Misnomer - this function also gets the normal length - enumerate deals with flextrack length + */ EXPORT DIST_T GetFlexLength( track_p trk0, EPINX_T ep, coOrd * pos ) { @@ -2153,9 +2510,33 @@ EXPORT DIST_T GetTrkLength( track_p trk, EPINX_T ep0, EPINX_T ep1 ) } else { pos0 = GetTrkEndPos(trk,ep0); if (ep1==-1) { + // Usual case for asking about distance to center of turnout for grades + if (trk->type==T_TURNOUT) { + trackParams_t trackParamsData; + trackParamsData.ep = ep0; + if (trackCmds(trk->type)->getTrackParams != NULL) { + //Find distance to centroid of end points * 2 or actual length if epCnt < 3 + trackCmds(trk->type)->getTrackParams(PARAMS_TURNOUT,trk,pos0,&trackParamsData); + return trackParamsData.len/2.0; + } + } pos1.x = (trk->hi.x+trk->lo.x)/2.0; pos1.y = (trk->hi.y+trk->lo.y)/2.0; } else { + if (trk->type==T_TURNOUT) { + pos1 = GetTrkEndPos(trk,ep1); + trackParams_t trackParamsData; + trackParamsData.ep = ep0; + if (trackCmds(trk->type)->getTrackParams != NULL) { + //Find distance via centroid of end points or actual length if epCnt < 3 + trackCmds(trk->type)->getTrackParams(PARAMS_TURNOUT,trk,pos0,&trackParamsData); + d = trackParamsData.len/2.0; + trackParamsData.ep = ep1; + trackCmds(trk->type)->getTrackParams(PARAMS_TURNOUT,trk,pos1,&trackParamsData); + d += trackParamsData.len/2.0; + return d; + } + } pos1 = GetTrkEndPos(trk,ep1); } pos1.x -= pos0.x; @@ -2169,6 +2550,8 @@ EXPORT DIST_T GetTrkLength( track_p trk, EPINX_T ep0, EPINX_T ep1 ) #define DRAW_TUNNEL_DASH (1) #define DRAW_TUNNEL_SOLID (2) EXPORT long drawTunnel = DRAW_TUNNEL_DASH; +EXPORT long colorTrack; +EXPORT long colorDraw; /****************************************************************************** * @@ -2179,6 +2562,24 @@ EXPORT long drawTunnel = DRAW_TUNNEL_DASH; EXPORT long tieDrawMode = TIEDRAWMODE_SOLID; EXPORT wDrawColor tieColor; +static wBool_t DoDrawTies( drawCmd_p d, track_cp trk ) +{ + DIST_T scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale; + if ( !trk ) + return FALSE; + if (GetTrkNoTies(trk)) + return FALSE; //No Ties for this Track + if ( tieDrawMode == TIEDRAWMODE_NONE ) + return FALSE; + if ( d == &mapD ) + return FALSE; + if ( d->scale >= scale2rail ) + return FALSE; + if ( !(GetTrkVisible(trk) || drawTunnel==DRAW_TUNNEL_SOLID) ) + return FALSE; + return TRUE; +} + EXPORT void DrawTie( drawCmd_p d, coOrd pos, @@ -2188,7 +2589,9 @@ EXPORT void DrawTie( wDrawColor color, BOOL_T solid ) { - coOrd p[4], lo, hi; + coOrd lo, hi; + coOrd p[4]; + int t[4]; length /= 2; width /= 2; @@ -2198,6 +2601,7 @@ EXPORT void DrawTie( 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 ); @@ -2205,6 +2609,10 @@ EXPORT void DrawTie( Translate( &p[3], p[2], angle-90, width ); Translate( &p[2], p[2], angle+90, width ); + for (int i=0;i<4;i++) { + t[i] = 0; + } + if ( d == &mainD ) { lo.x -= RBORDER/mainD.dpi*mainD.scale; lo.y -= TBORDER/mainD.dpi*mainD.scale; @@ -2214,19 +2622,16 @@ EXPORT void DrawTie( return; } if ( solid ) { - DrawFillPoly( d, 4, p, color ); + DrawPoly( d, 4, p, t, color, 0, 1, 0 ); } 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 ); + DrawPoly( d, 4, p, t, color, 0, 0, 0); } } -EXPORT void DrawCurvedTies( +static void DrawCurvedTies( drawCmd_p d, - track_p trk, + SCALEINX_T scaleInx, coOrd p, DIST_T r, ANGLE_T a0, @@ -2239,27 +2644,27 @@ EXPORT void DrawCurvedTies( coOrd pos; int cnt; - if ( (d->funcs->options&wDrawOptTemp) != 0 ) + if ( (d->options&DC_SIMPLE) != 0 ) return; - if ( trk == NULL ) - return; - - td = GetScaleTieData(GetTrkScale(trk)); - if ( (!GetTrkVisible(trk)) && drawTunnel!=DRAW_TUNNEL_SOLID ) + if ( scaleInx < 0 ) return; + td = GetScaleTieData( scaleInx ); + 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 = (int)floor(len/td->spacing+0.5); + if ( len-td->spacing*cnt-(td->width/2) > (td->spacing-td->width)/2 ) { cnt++; + } if ( cnt != 0 ) { - dang = a1/cnt; + dang = (360.0*(len)/cnt)/(2*M_PI*r); 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 ); } + } } @@ -2273,11 +2678,11 @@ EXPORT void DrawCurvedTrack( coOrd p0, coOrd p1, track_p trk, - DIST_T trackGauge, wDrawColor color, long options ) { DIST_T scale2rail; + DIST_T trackGauge = GetTrkGauge(trk); wDrawWidth width=0; trkSeg_p segPtr; @@ -2295,9 +2700,10 @@ EXPORT void DrawCurvedTrack( } scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale; - if (options&DTS_THICK2) - width = 2; - if (options&DTS_THICK3) + width = trk ? GetTrkWidth( trk ): 0; + if ( d->options&DC_THICK ) + width = 3; + if ( color == wDrawColorPreviewSelected || color == wDrawColorPreviewUnselected ) width = 3; #ifdef WINDOWS width *= (wDrawWidth)(d->dpi/mainD.dpi); @@ -2308,21 +2714,15 @@ EXPORT void DrawCurvedTrack( 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 ( DoDrawTies( d, trk ) ) + DrawCurvedTies( d, GetTrkScale(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 - || (d->scale <= scale2rail/2 && d->options&DC_PRINT && printCenterLines)) { // if printing two rails respect print CenterLine option + || (d->scale <= scale2rail/2 && ((d->options&DC_PRINT) && printCenterLines))) { // if printing two rails respect print CenterLine option long options = d->options; d->options |= DC_DASH; DrawArc( d, p, r, a0, a1, 0, 0, color ); @@ -2340,12 +2740,44 @@ LOG( log_track, 4, ( "DST( (%0.3f %0.3f) R%0.3f A%0.3f..%0.3f)\n", } } } + if (trk && GetTrkBridge( trk ) ) { + + ANGLE_T a2,a3; + coOrd pp0,pp1,pp2,pp3; + + a2 = a0+R2D(trackGauge*1.0/r); + a3 = a1-R2D(trackGauge*2.0/r); + + wDrawWidth width2 = (wDrawWidth)round((2.0 * d->dpi)/75.0); + + DrawArc( d, p, r+(trackGauge*1.5), a2, a3, 0, width2, color ); + + PointOnCircle(&pp0,p,r+(trackGauge*1.5),a2); + PointOnCircle(&pp1,p,r+(trackGauge*1.5),a3+a2); + + Translate( &pp2,pp0, a2-90+45, trackGauge); + DrawLine( d, pp0, pp2, width2, color ); + Translate( &pp3,pp1, a2+a3+90-45, trackGauge); + DrawLine( d, pp1, pp3, width2, color ); + + DrawArc( d, p, r-(trackGauge*1.5), a2, a3, 0, width2, color ); + + PointOnCircle(&pp0,p,r-(trackGauge*1.5),a2); + PointOnCircle(&pp1,p,r-(trackGauge*1.5),a3+a2); + + Translate( &pp2,pp0, a2-90-45, trackGauge); + DrawLine( d, pp0, pp2, width2, color ); + Translate( &pp3,pp1, a2+a3+90+45, trackGauge); + DrawLine( d, pp1, pp3, width2, color ); + + } + } -EXPORT void DrawStraightTies( +static void DrawStraightTies( drawCmd_p d, - track_p trk, + SCALEINX_T scaleInx, coOrd p0, coOrd p1, wDrawColor color ) @@ -2357,25 +2789,24 @@ EXPORT void DrawStraightTies( int cnt; ANGLE_T angle; - if ( (d->funcs->options&wDrawOptTemp) != 0 ) - return; - if ( trk == NULL ) + if ( (d->options&DC_SIMPLE) != 0 ) return; - td = GetScaleTieData(GetTrkScale(trk)); - if ( (!GetTrkVisible(trk)) && drawTunnel!=DRAW_TUNNEL_SOLID ) - return; if ( color == wDrawColorBlack ) color = tieColor; - td = GetScaleTieData( GetTrkScale(trk) ); + if ( scaleInx < 0 ) + return; + td = GetScaleTieData( scaleInx ); 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 = (int)floor(len/td->spacing+0.5); + if ( len-td->spacing*cnt-td->width > (td->spacing-td->width)/2 ) { cnt++; + } if ( cnt != 0 ) { - dlen = len/cnt; + dlen = FindDistance( p0, p1 )/cnt; + double endsize = FindDistance( p0, p1 )-cnt*dlen-td->width; 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 ); @@ -2389,13 +2820,13 @@ EXPORT void DrawStraightTrack( coOrd p0, coOrd p1, ANGLE_T angle, - track_p trk, - DIST_T trackGauge, + track_cp trk, wDrawColor color, long options ) { coOrd pp0, pp1; DIST_T scale2rail; + DIST_T trackGauge = GetTrkGauge(trk); wDrawWidth width=0; trkSeg_p segPtr; @@ -2414,9 +2845,10 @@ EXPORT void DrawStraightTrack( scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale; - if (options&DTS_THICK2) - width = 2; - if (options&DTS_THICK3) + width = trk ? GetTrkWidth( trk ): 0; + if ( d->options&DC_THICK ) + width = 3; + if ( color == wDrawColorPreviewSelected || color == wDrawColorPreviewUnselected ) width = 3; #ifdef WINDOWS width *= (wDrawWidth)(d->dpi/mainD.dpi); @@ -2426,21 +2858,15 @@ EXPORT void DrawStraightTrack( #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 ( DoDrawTies( d, trk ) ) + DrawStraightTies( d, GetTrkScale(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 - || (d->scale <= scale2rail/2 && d->options&DC_PRINT && printCenterLines)) { // if printing two rails respect print CenterLine option + || (d->scale <= scale2rail/2 && ((d->options&DC_PRINT) && printCenterLines))) { // if printing two rails respect print CenterLine option long options = d->options; d->options |= DC_DASH; DrawLine( d, p0, p1, 0, color ); @@ -2466,37 +2892,68 @@ LOG( log_track, 4, ( "DST( (%0.3f %0.3f) .. (%0.3f..%0.3f)\n", } } } + if (trk && GetTrkBridge( trk ) ) { + + coOrd pp2,pp3; + wDrawWidth width2 = (wDrawWidth)round((2.0 * d->dpi)/75.0); + + Translate( &pp0, p0, angle+90, trackGauge*1.5 ); + Translate( &pp1, p1, angle+90, trackGauge*1.5 ); + Translate( &pp0, pp0, angle+180, trackGauge*1.5 ); + Translate( &pp1, pp1, angle, trackGauge*1.5 ); + DrawLine( d, pp0, pp1, width2, color ); + Translate( &pp2,pp0, angle+90-45, trackGauge); + DrawLine( d, pp0, pp2, width2, color ); + Translate( &pp3,pp1, angle+90+45, trackGauge); + DrawLine( d, pp1, pp3, width2, color ); + + Translate( &pp0, p0, angle-90, trackGauge*1.5 ); + Translate( &pp1, p1, angle-90, trackGauge*1.5 ); + Translate( &pp0, pp0, angle+180, trackGauge*1.5 ); + Translate( &pp1, pp1, angle, trackGauge*1.5 ); + DrawLine( d, pp0, pp1, width2, color ); + Translate( &pp2,pp0, angle-90+45, trackGauge); + DrawLine( d, pp0, pp2, width2, color ); + Translate( &pp3,pp1, angle-90-45, trackGauge); + DrawLine( d, pp1, pp3, width2, color ); + + } } EXPORT wDrawColor GetTrkColor( track_p trk, drawCmd_p d ) { - DIST_T len, elev0, elev1; + DIST_T len, len1, 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 (GetTrkEndElevCachedHeight(trk,0,&elev0,&len) && GetTrkEndElevCachedHeight(trk,1,&elev1,&len1)) { + grade = fabs( (elev1-elev0)/(len+len1))*100.0; + } else { + len = GetTrkLength( trk, 0, 1 ); + if (len>0.1) { + ComputeElev( trk, 0, FALSE, &elev0, NULL, FALSE ); + ComputeElev( trk, 1, FALSE, &elev1, NULL, FALSE ); + grade = fabs( (elev1-elev0)/len )*100.0; + } } } - if ( (d->options&(DC_GROUP)) == 0 ) { - if ( grade > GetLayoutMaxTrackGrade()) - return exceptionColor; - if ( QueryTrack( trk, Q_EXCEPTION ) ) - return exceptionColor; - } - if ( (d->options&(DC_PRINT|DC_GROUP)) == 0 ) { + if ( (d->options&(DC_SIMPLE|DC_SEGTRACK)) != 0 ) + return wDrawColorBlack; + if ( grade > GetLayoutMaxTrackGrade()) + return exceptionColor; + if ( QueryTrack( trk, Q_EXCEPTION ) ) + return exceptionColor; + if ( (d->options&(DC_PRINT)) == 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((unsigned int)curTrackLayer); + if ( (IsTrack(trk)?(colorTrack):(colorDraw)) ) { + unsigned int iLayer = GetTrkLayer( trk ); + if (GetLayerUseColor( iLayer ) ) + return GetLayerColor( iLayer ); } return wDrawColorBlack; } @@ -2504,9 +2961,11 @@ EXPORT wDrawColor GetTrkColor( track_p trk, drawCmd_p d ) EXPORT void DrawTrack( track_cp trk, drawCmd_p d, wDrawColor color ) { - DIST_T scale2rail; TRKTYP_T trkTyp; + // Hack for WINDOWS + if ( trk->bits & TB_UNDRAWN ) + return; trkTyp = GetTrkType(trk); curTrackLayer = GetTrkLayer(trk); if (d != &mapD ) { @@ -2519,48 +2978,43 @@ EXPORT void DrawTrack( track_cp trk, drawCmd_p d, wDrawColor color ) if (color == wDrawColorBlack) { color = GetTrkColor( trk, d ); } + if (color == wDrawColorPreviewSelected || color == wDrawColorPreviewUnselected ) { + d->options |= DC_THICK; + } } + if (d == &mapD && !GetLayerOnMap(curTrackLayer)) return; - if ( (IsTrack(trk)?(colorLayers&1):(colorLayers&2)) && + if ( (IsTrack(trk)?(colorTrack):(colorDraw)) && d != &mapD && color == wDrawColorBlack ) - color = GetLayerColor((unsigned int)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; - } + if (GetLayerUseColor((unsigned int)curTrackLayer)) + color = GetLayerColor((unsigned int)curTrackLayer); trackCmds(trkTyp)->draw( trk, d, color ); - if ( (!inDrawTracks) ) { - d->options &= ~DC_TIES; - } d->options &= ~DC_DASH; + d->options &= ~DC_THICK; + 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 ) { + t->bits &= ~TB_UNDRAWN; DrawATrack( t, wDrawColorBlack ); } EXPORT void UndrawNewTrack( track_cp t ) { DrawATrack( t, wDrawColorWhite ); + t->bits |= TB_UNDRAWN; } EXPORT int doDrawPositionIndicator = 1; @@ -2610,7 +3064,7 @@ static void DrawUnconnectedEndPt( drawCmd_p d, coOrd p, ANGLE_T a, DIST_T trackG Translate( &p, p, a+90.0, 0.2 ); Translate( &p0, p, a, trackGauge ); Translate( &p1, p, a-180.0, trackGauge ); - DrawLine( d, p0, p1, (drawUnconnectedEndPt>0)?4:0, (drawUnconnectedEndPt>1)?exceptionColor:color ); + DrawLine( d, p0, p1, (drawUnconnectedEndPt>0)?4:0, (color==wDrawColorWhite)?color:(drawUnconnectedEndPt>1)?exceptionColor:color ); } } @@ -2643,7 +3097,7 @@ EXPORT void DrawEndElev( drawCmd_p d, track_p trk, EPINX_T ep, wDrawColor color case ELEV_GRADE: if ( color == wDrawColorWhite ) { elev0 = grade = elev->u.height; - } else if ( !ComputeElev( trk, ep, FALSE, &elev0, &grade ) ) { + } else if ( !ComputeElev( trk, ep, FALSE, &elev0, &grade, FALSE ) ) { elev0 = grade = 0; gradeOk = FALSE; } @@ -2651,7 +3105,7 @@ EXPORT void DrawEndElev( drawCmd_p d, track_p trk, EPINX_T ep, wDrawColor color elevStr = FormatDistance(elev0); elev->u.height = elev0; } else if (gradeOk) { - sprintf( message, "%0.1f%%", fabs(grade*100.0) ); + sprintf( message, "%0.1f%%", round(fabs(grade*100.0)*10)/10 ); elevStr = message; a = GetTrkEndAngle( trk, ep ); style = BOX_ARROW; @@ -2704,16 +3158,16 @@ EXPORT void DrawEndPt( 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) + if ( (d->options & (DC_SIMPLE|DC_SEGTRACK)) != 0) return; if ( trk && QueryTrack( trk, Q_NODRAWENDPT ) ) return; - if (trk == NULL || ep < 0) return; + // line width for the tunnel portal, make sure it is rounded correctly + width2 = (wDrawWidth)round((2.0 * d->dpi)/75.0); + if (color == wDrawColorBlack) color = normalColor; @@ -2734,17 +3188,20 @@ EXPORT void DrawEndPt( return; sepBoundary = FALSE; - if ((d->options&DC_PRINT)==0 && importTrack == NULL && GetTrkSelected(trk) && (!GetTrkSelected(trk1))) { + if ( inDrawTracks && (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; + long oldOptions = d->options; + d->options &= ~DC_NOTSOLIDLINE; Translate( &p0, p, a+45, len ); Translate( &p1, p, a+225, len ); - DrawLine( &tempD, p0, p1, 0, selectedColor ); + DrawLine( d, p0, p1, 2, selectedColor ); Translate( &p0, p, a-45, len ); Translate( &p1, p, a-225, len ); - DrawLine( &tempD, p0, p1, 0, selectedColor ); + DrawLine( d, p0, p1, 2, selectedColor ); + d->options = oldOptions; sepBoundary = TRUE; } else if ((d->options&DC_PRINT)==0 && importTrack == NULL && (!GetTrkSelected(trk)) && GetTrkSelected(trk1)) { sepBoundary = TRUE; @@ -2831,7 +3288,6 @@ EXPORT void DrawTracks( drawCmd_p d, DIST_T scale, coOrd orig, coOrd size ) inDrawTracks = TRUE; InfoCount( 0 ); - d->options |= DC_TIES; TRK_ITERATE( trk ) { if ( (d->options&DC_PRINT) != 0 && wPrintQuit() ) { @@ -2854,7 +3310,6 @@ EXPORT void DrawTracks( drawCmd_p d, DIST_T scale, coOrd orig, coOrd size ) if (count%10 == 0) InfoCount( count ); } - d->options &= ~DC_TIES; if (d == &mainD) { for (inx=1; inx<trackCmds_da.cnt; inx++) @@ -2868,14 +3323,6 @@ EXPORT void DrawTracks( drawCmd_p d, DIST_T scale, coOrd orig, coOrd size ) } -EXPORT void RedrawLayer( unsigned int l, BOOL_T draw ) -{ - MainRedraw(); - MapRedraw(); - -} - - EXPORT void DrawSelectedTracks( drawCmd_p d ) { track_cp trk; @@ -2899,8 +3346,6 @@ EXPORT void DrawSelectedTracks( drawCmd_p d ) EXPORT void HilightElevations( BOOL_T hilight ) { - static long lastRedraw = -1; - static BOOL_T lastHilight = FALSE; track_p trk, trk1; EPINX_T ep; int mode; @@ -2908,12 +3353,6 @@ EXPORT void HilightElevations( BOOL_T hilight ) 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; @@ -2937,25 +3376,20 @@ EXPORT void HilightElevations( BOOL_T hilight ) } } } - 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) { + if (!trk || (ep==-1)) return; + pos = GetTrkEndPos( trk, ep ); + if ( show == TRUE ) { pos = GetTrkEndPos( trk, ep ); DrawFillCircle( &tempD, pos, 0.10*mainD.scale, selectedColor ); - lastShow = show; + } else { + pos = GetTrkEndPos( trk, ep ); + DrawFillCircle( &tempD, pos, 0.10*mainD.scale, wDrawColorWhite ); } } diff --git a/app/bin/track.h b/app/bin/track.h index 7485730..4e24280 100644 --- a/app/bin/track.h +++ b/app/bin/track.h @@ -23,6 +23,7 @@ #ifndef TRACK_H #define TRACK_H +#include <string.h> #include "common.h" #include "draw.h" #include "misc2.h" @@ -34,6 +35,8 @@ typedef struct track_t * track_p; typedef struct track_t * track_cp; extern track_p tempTrack; extern wIndex_t trackCount; +extern long colorTrack; +extern long colorDraw; extern long drawTunnel; extern long drawEndPtV; extern long drawUnconnectedEndPt; @@ -71,13 +74,16 @@ typedef enum { curveTypeNone, curveTypeCurve, curveTypeStraight, curveTypeBezier #define PARAMS_1ST_JOIN (0) #define PARAMS_2ND_JOIN (1) #define PARAMS_EXTEND (2) -#define PARAMS_PARALLEL (3) +#define PARAMS_NODES (3) #define PARAMS_BEZIER (4) //Not used (yet) #define PARAMS_CORNU (5) //Called to get end characteristics +#define PARAMS_TURNOUT (6) +#define PARAMS_LINE (7) //Called on Lines typedef struct { curveType_e type; //Straight, Curve, Bezier, Cornu EPINX_T ep; //End point that is nearby pos + dynArr_t nodes; //Array of nodes -> PARAMS_PARALLEL only DIST_T len; //Length of track ANGLE_T angle; //Angle at end of track coOrd lineOrig; //Start of straight @@ -95,6 +101,7 @@ typedef struct { coOrd cornuCenter[2]; //Center at Cornu Ends coOrd ttcenter; //Turntable DIST_T ttradius; //Turntable + coOrd centroid; //Turnout } trackParams_t; @@ -102,7 +109,6 @@ typedef struct { #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) @@ -122,6 +128,14 @@ typedef struct { #define Q_CAN_ADD_ENDPOINTS (22) // Is T_TURNTABLE #define Q_HAS_VARIABLE_ENDPOINTS (23) // Is Helix or Circle #define Q_CORNU_CAN_MODIFY (24) // can be modified by CORNU MODIFY +#define Q_ISTRAIN (25) +#define Q_IS_POLY (26) +#define Q_IS_DRAW (27) +#define Q_IS_TEXT (28) +#define Q_IS_ACTIVATEABLE (29) +#define Q_IS_STRUCTURE (30) +#define Q_IS_TURNOUT (31) +#define Q_GET_NODES (32) typedef struct { track_p trk; // IN Current Track OUT Next Track @@ -139,7 +153,7 @@ typedef struct { void (*describe)( track_p, char * line, CSIZE_T len ); void (*delete)( track_p ); BOOL_T (*write)( track_p, FILE * ); - void (*read)( char * ); + BOOL_T (*read)( char * ); void (*move)( track_p, coOrd ); void (*rotate)( track_p, coOrd, ANGLE_T ); void (*rescale)( track_p, FLOAT_T ); @@ -149,7 +163,7 @@ typedef struct { 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 (*trim)( track_p, EPINX_T, DIST_T, coOrd endpos, ANGLE_T angle, DIST_T endradius, coOrd endcenter ); BOOL_T (*merge)( track_p, EPINX_T, track_p, EPINX_T ); STATUS_T (*modify)( track_p, wAction_t, coOrd ); DIST_T (*getLength)( track_p ); @@ -161,11 +175,13 @@ typedef struct { 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 * ); + BOOL_T (*makeParallel)( track_p, coOrd, DIST_T, DIST_T, track_p *, coOrd *, coOrd *, BOOL_T ); void (*drawDesc)( track_p, drawCmd_p, wDrawColor ); BOOL_T (*rebuildSegs)(track_p); BOOL_T (*replayData)(track_p, void *,long ); BOOL_T (*storeData)(track_p, void **,long *); + void (*activate)(track_p); + wBool_t (*compare)( track_cp, track_cp ); } trackCmd_t; @@ -180,6 +196,9 @@ typedef struct { DIST_T height; char * name; } u; + BOOL_T cacheSet; + double cachedElev; + double cachedLength; } elev_t; #define EPOPT_GAPPED (1L<<0) typedef struct { @@ -191,12 +210,13 @@ typedef struct { long option; } trkEndPt_t, * trkEndPt_p; -dynArr_t tempEndPts_da; +extern dynArr_t tempEndPts_da; #define tempEndPts(N) DYNARR_N( trkEndPt_t, tempEndPts_da, N ) -typedef enum { FREEFORM, RECTANGLE +typedef enum { FREEFORM, RECTANGLE, POLYLINE } PolyType_e; + typedef struct { char type; wDrawColor color; @@ -236,11 +256,12 @@ typedef struct { ANGLE_T angle; wFont_p fontP; FONTSIZE_T fontSize; + BOOL_T boxed; char * string; } t; struct { int cnt; - coOrd * pts; + pts_t * pts; coOrd orig; ANGLE_T angle; PolyType_e polyType; @@ -271,11 +292,12 @@ typedef struct { #define IsSegTrack( S ) ( (S)->type == SEG_STRTRK || (S)->type == SEG_CRVTRK || (S)->type == SEG_JNTTRK || (S)->type == SEG_BEZTRK) -dynArr_t tempSegs_da; +extern dynArr_t tempSegs_da; + #define tempSegs(N) DYNARR_N( trkSeg_t, tempSegs_da, N ) -char tempSpecial[4096]; -char tempCustom[4096]; +extern char tempSpecial[4096]; +extern char tempCustom[4096]; void ComputeCurvedSeg( trkSeg_p s, @@ -396,6 +418,7 @@ void JointSegProc( segProc_e, trkSeg_p, segProcData_p ); void BezierSegProc( segProc_e, trkSeg_p, segProcData_p ); //Used in Cornu join void CleanSegs( dynArr_t *); void CopyPoly(trkSeg_p seg_p, wIndex_t segCnt); +wBool_t CompareSegs( trkSeg_p, int, trkSeg_p, int ); /* debug.c */ void SetDebug( char * ); @@ -408,7 +431,12 @@ void SetDebug( char * ); #define TB_SHRTPATH (1<<5) #define TB_HIDEDESC (1<<6) #define TB_CARATTACHED (1<<7) -#define TB_TEMPBITS (TB_PROFILEPATH|TB_PROCESSED) +#define TB_NOTIES (1<<8) +#define TB_BRIDGE (1<<9) +#define TB_SELREDRAW (1<<10) +// Track has been undrawn, don't draw it on Redraw +#define TB_UNDRAWN (1<<11) +#define TB_TEMPBITS (TB_PROFILEPATH|TB_PROCESSED|TB_UNDRAWN) /* track.c */ #ifdef FASTTRACK @@ -473,14 +501,21 @@ BOOL_T IsTrackDeleted( track_p ); #define GetTrkSelected(T) (GetTrkBits(T)&TB_SELECTED) #define GetTrkVisible(T) (GetTrkBits(T)&TB_VISIBLE) +#define GetTrkNoTies(T) (GetTrkBits(T)&TB_NOTIES) +#define GetTrkBridge(T) (GetTrkBits(T)&TB_BRIDGE) #define SetTrkVisible(T,V) ((V)?SetTrkBits(T,TB_VISIBLE):ClrTrkBits(T,TB_VISIBLE)) +#define SetTrkNoTies(T,V) ((V)?SetTrkBits(T,TB_NOTIES):ClrTrkBits(T,TB_NOTIES)) +#define SetTrkBridge(T,V) ((V)?SetTrkBits(T,TB_BRIDGE):ClrTrkBits(T,TB_BRIDGE)) int ClrAllTrkBits( int ); +int ClrAllTrkBitsRedraw( int, wBool_t ); 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 ); +BOOL_T GetTrkEndElevCachedHeight (track_p trk, EPINX_T e, DIST_T *height, DIST_T *length); +void SetTrkEndElevCachedHeight ( track_p trk, EPINX_T e, DIST_T height, DIST_T length); char * GetTrkEndElevStation( track_p, EPINX_T ); #define EndPtIsDefinedElev( T, E ) (GetTrkEndElevMode(T,E)==ELEV_DEF) #define EndPtIsIgnoredElev( T, E ) (GetTrkEndElevMode(T,E)==ELEV_IGNORE) @@ -492,9 +527,10 @@ void ClearElevPath( void ); BOOL_T GetTrkOnElevPath( track_p, DIST_T * elev ); void SetTrkLayer( track_p, int ); BOOL_T CheckTrackLayer( track_p ); +BOOL_T CheckTrackLayerSilent(track_p); void CopyAttributes( track_p, track_p ); -#define GetTrkGauge( T ) GetScaleTrackGauge(GetTrkScale(T)) +DIST_T GetTrkGauge( track_cp ); #define GetTrkScaleName( T ) GetScaleName(GetTrkScale(T)) void SetTrkEndPtCnt( track_p, EPINX_T ); BOOL_T WriteEndPt( FILE *, track_cp, EPINX_T ); @@ -506,11 +542,56 @@ 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 ); +void ActivateTrack( track_cp ); EPINX_T GetEndPtConnectedToMe( track_p, track_p ); EPINX_T GetNearestEndPtConnectedToMe( track_p, track_p, coOrd); void SetEndPts( track_p, EPINX_T ); BOOL_T DeleteTrack( track_p, BOOL_T ); +#define REGRESS_CHECK_POS( TITLE, P1, P2, FIELD ) \ + if ( ! IsPosClose( P1->FIELD, P2->FIELD ) ) { \ + sprintf( cp, TITLE ": Actual [%0.3f %0.3f], Expected [%0.3f %0.3f]\n", \ + P1->FIELD.x, P1->FIELD.y, \ + P2->FIELD.x, P2->FIELD.y ); \ + return FALSE; \ + } +#define REGRESS_CHECK_DIST( TITLE, P1, P2, FIELD ) \ + if ( ! IsDistClose( P1->FIELD, P2->FIELD ) ) { \ + sprintf( cp, TITLE ": Actual %0.3f, Expected %0.3f\n", \ + P1->FIELD, P2->FIELD ); \ + return FALSE; \ + } +#define REGRESS_CHECK_WIDTH( TITLE, P1, P2, FIELD ) \ + if ( ! IsWidthClose( P1->FIELD, P2->FIELD ) ) { \ + sprintf( cp, TITLE ": Actual %0.3f, Expected %0.3f\n", \ + P1->FIELD, P2->FIELD ); \ + return FALSE; \ + } +#define REGRESS_CHECK_ANGLE( TITLE, P1, P2, FIELD ) \ + if ( ! IsAngleClose( P1->FIELD, P2->FIELD ) ) { \ + sprintf( cp, TITLE ": Actual %0.3f , Expected %0.3f\n", \ + P1->FIELD, P2->FIELD ); \ + return FALSE; \ + } +#define REGRESS_CHECK_INT( TITLE, P1, P2, FIELD ) \ + if ( P1->FIELD != P2->FIELD ) { \ + sprintf( cp, TITLE ": Actual %d, Expected %d\n", \ + (int)(P1->FIELD), (int)(P2->FIELD) ); \ + return FALSE; \ + } +#define REGRESS_CHECK_COLOR( TITLE, P1, P2, FIELD ) \ + if ( ! IsColorClose(P1->FIELD, P2->FIELD) ) { \ + sprintf( cp, TITLE ": Actual %6x, Expected %6x\n", \ + (int)wDrawGetRGB(P1->FIELD), (int)wDrawGetRGB(P2->FIELD) ); \ + return FALSE; \ + } +wBool_t IsPosClose( coOrd, coOrd ); +wBool_t IsAngleClose( ANGLE_T, ANGLE_T ); +wBool_t IsDistClose( DIST_T, DIST_T ); +wBool_t IsWidthClose( DIST_T, DIST_T ); +wBool_t IsColorClose( wDrawColor, wDrawColor ); +wBool_t CompareTrack( track_cp, track_cp ); + void MoveTrack( track_p, coOrd ); void RotateTrack( track_p, coOrd, ANGLE_T ); void RescaleTrack( track_p, FLOAT_T, coOrd ); @@ -523,19 +604,18 @@ EPINX_T GetNextTrkOnPath( track_p, EPINX_T ); #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 * ); +BOOL_T ComputeElev( track_p trk, EPINX_T ep, BOOL_T on_path, DIST_T * elev, DIST_T * grade, BOOL_T force); #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) +#define DTS_DOT (1<<7) +#define DTS_DASH (1<<8) +#define DTS_DASHDOT (1<<9) +#define DTS_DASHDOTDOT (1<<10) -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 ); +void DrawCurvedTrack( drawCmd_p, coOrd, DIST_T, ANGLE_T, ANGLE_T, coOrd, coOrd, track_cp, wDrawColor, long ); +void DrawStraightTrack( drawCmd_p, coOrd, coOrd, ANGLE_T, track_cp, wDrawColor, long ); ANGLE_T GetAngleAtPoint( track_p, coOrd, EPINX_T *, EPINX_T * ); DIST_T GetTrkDistance( track_cp, coOrd *); @@ -551,24 +631,23 @@ 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( unsigned int, 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 ); +DIST_T EndPtDescriptionDistance( coOrd, track_p, EPINX_T, coOrd *, BOOL_T show_hidden, BOOL_T * hidden ); 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 * ); +BOOL_T WriteTracks( FILE *, wBool_t ); +BOOL_T ExportTracks( FILE * , coOrd *); void ImportStart( void ); -void ImportEnd( void ); +void ImportEnd( coOrd , wBool_t, wBool_t); void FreeTrack( track_p ); void ClearTracks( void ); BOOL_T TrackIterate( track_p * ); @@ -581,7 +660,7 @@ void SaveCarState( void ); void RestoreCarState( void ); TRKTYP_T InitObject( trackCmd_t* ); -void ConnectTracks( track_p, EPINX_T, track_p, EPINX_T ); +int 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 ); @@ -589,9 +668,10 @@ BOOL_T ConnectTurntableTracks(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 TrimTrack( track_p, EPINX_T, DIST_T, coOrd pos, ANGLE_T angle, DIST_T radius, coOrd center); BOOL_T MergeTracks( track_p, EPINX_T, track_p, EPINX_T ); STATUS_T ExtendStraightFromOrig( track_p, wAction_t, coOrd ); +STATUS_T ExtendTrackFromOrig( 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 ); @@ -616,12 +696,11 @@ 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 * ); - +BOOL_T MakeParallelTrack( track_p, coOrd, DIST_T, DIST_T, track_p *, coOrd *, coOrd * , BOOL_T); /* 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; +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, DESC_BOXED } descType; #define DESC_RO (1<<0) #define DESC_IGNORE (1<<1) #define DESC_NOREDRAW (1<<2) @@ -647,10 +726,11 @@ 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 ); +STATUS_T CmdDescribe(wAction_t,coOrd); /* compound.c */ -DIST_T CompoundDescriptionDistance( coOrd, track_p ); +DIST_T CompoundDescriptionDistance( coOrd, track_p, coOrd *, BOOL_T, BOOL_T * ); STATUS_T CompoundDescriptionMove( track_p, wAction_t, coOrd ); /* elev.c */ @@ -659,10 +739,10 @@ STATUS_T CompoundDescriptionMove( track_p, wAction_t, coOrd ); #define ELEV_ISLAND (1) #define ELEV_ALONE (0) -long oldElevationEvaluation; +extern 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 * ); +BOOL_T ComputeElev( track_p, EPINX_T, BOOL_T, DIST_T *, DIST_T *, BOOL_T ); void RecomputeElevations( void ); void UpdateAllElevations( void ); DIST_T GetElevation( track_p ); @@ -672,11 +752,22 @@ void UpdateTrkEndElev( track_p, EPINX_T, int, DIST_T, char * ); void DrawTrackElev( track_p, drawCmd_p, BOOL_T ); /* cdraw.c */ +typedef enum {DRAWLINESOLID, + DRAWLINEDASH, + DRAWLINEDOT, + DRAWLINEDASHDOT, + DRAWLINEDASHDOTDOT, + DRAWLINECENTER, + DRAWLINEPHANTOM } drawLineType_e; track_p MakeDrawFromSeg( coOrd, ANGLE_T, trkSeg_p ); +track_p MakePolyLineFromSegs( coOrd, ANGLE_T, dynArr_t * ); +void DrawOriginAnchor(track_p); BOOL_T OnTableEdgeEndPt( track_p, coOrd * ); BOOL_T GetClosestEndPt( track_p, coOrd * ); BOOL_T ReadTableEdge( char * ); BOOL_T ReadText( char * ); +void SetLineType( track_p trk, int width ); +void MenuMode(int ); /* chotbar.c */ extern DIST_T curBarScale; @@ -685,7 +776,7 @@ 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 AddHotBarElement( char *, coOrd, coOrd, BOOL_T, BOOL_T, DIST_T, void *, hotBarProc_t ); void HotBarCancel( void ); void AddHotBarTurnouts( void ); void AddHotBarStructures( void ); diff --git a/app/bin/trackx.h b/app/bin/trackx.h index 9f24e7c..50fda1d 100644 --- a/app/bin/trackx.h +++ b/app/bin/trackx.h @@ -40,7 +40,7 @@ typedef struct track_t { BOOL_T new:1; unsigned int width:2; unsigned int elevMode:2; - unsigned int bits:9; + unsigned int bits:12; EPINX_T endCnt; trkEndPt_p endPt; struct { float x; float y; } lo, hi; diff --git a/app/bin/trknote.c b/app/bin/trknote.c new file mode 100644 index 0000000..f27cf2e --- /dev/null +++ b/app/bin/trknote.c @@ -0,0 +1,720 @@ +/** \file trknote.c + * Track notes "postits" + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2005 Dave Bullis, 2018 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 <stdint.h> +#include <string.h> +#include <ctype.h> + +#include "cundo.h" +#include "custom.h" +#include "dynstring.h" +#include "fileio.h" +#include "i18n.h" +#include "misc.h" +#include "note.h" +#include "param.h" +#include "track.h" +#include "include/utf8convert.h" +#include "utility.h" + +extern BOOL_T inDescribeCmd; +extern descData_t noteDesc[]; + +static TRKTYP_T T_NOTE = -1; + +static wDrawBitMap_p note_bm, link_bm, document_bm; + +typedef struct { + char **xpm; + int OP; + char * shortName; + char * cmdName; + char * helpKey; + long acclKey; +} trknoteData_t; + +#include "bitmaps/sticky-note-text.xpm" +#include "bitmaps/sticky-note-chain.xpm" +#include "bitmaps/sticky-note-clip.xpm" + +static trknoteData_t noteTypes[] = { + { sticky_note_text_bits, OP_NOTETEXT, N_("Note"), N_("Comment"), "cmdTextNote", 0L }, + { sticky_note_chain_bits, OP_NOTELINK, N_("Link"), N_("Weblink"), "cmdLinkNote", 0L }, + { sticky_note_clip_bits, OP_NOTEFILE, N_("Document"), N_("Document"), "cmdFileNote", 0L }, +}; + +static long curNoteType; + +static unsigned layerSave; +static coOrd posSave; + +#define NOTETYPESCOUNT (sizeof(noteTypes)/sizeof(trknoteData_t)) + + +/***************************************************************************** + * NOTE OBJECT + */ + +static track_p NewNote(wIndex_t index, coOrd p, enum noteCommands command ) +{ + track_p t; + struct extraDataNote * xx; + t = NewTrack(index, T_NOTE, 0, sizeof *xx); + xx = (struct extraDataNote *)GetTrkExtraData(t); + xx->pos = p; + xx->op = command; + SetBoundingBox(t, p, p); + return t; +} + +/** + * Draw the icon for a note into the drawing area + * + * \param t IN note + * \param d IN drawing environment + * \param color IN color for ico + */ + +static void DrawNote(track_p t, drawCmd_p d, wDrawColor color) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(t); + coOrd p[4]; + + if (d->scale >= 16) { + return; + } + if ((d->options & DC_SIMPLE)) { + //while the icon is moved, draw a square + //because CmdMove draws all selected object into tempSeg and + //tempSegDrawFuncs doesn't have a BitMap drawing func + DIST_T dist; + dist = 0.1*mainD.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); + } else { + // draw a bitmap for static object + wDrawBitMap_p bm; + + if (xx->op == OP_NOTELINK ||(inDescribeCmd && curNoteType == OP_NOTELINK)) { + bm = link_bm; + } else { + if (xx->op == OP_NOTEFILE || (inDescribeCmd && curNoteType == OP_NOTEFILE)) { + bm = document_bm; + } else { + bm = note_bm; + } + } + DrawBitMap(d, xx->pos, bm, color); + } +} + +static DIST_T DistanceNote(track_p t, coOrd * p) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(t); + DIST_T d; + d = FindDistance(*p, xx->pos); + + if (d < 3.0*(mainD.scale/12.0)) { + return d; + } + + return 100000.0; +} + +static void DeleteNote(track_p t) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(t); + + switch (xx->op) { + case OP_NOTETEXT: + if (xx->noteData.text) { + MyFree(xx->noteData.text); + } + break; + case OP_NOTEFILE: + if (xx->noteData.fileData.path) { + MyFree(xx->noteData.fileData.path); + } + if (xx->noteData.fileData.title) { + MyFree(xx->noteData.fileData.title); + } + break; + case OP_NOTELINK: + if (xx->noteData.linkData.title) { + MyFree(xx->noteData.linkData.title); + } + if (xx->noteData.linkData.url) { + MyFree(xx->noteData.linkData.url); + } + break; + default: + break; + } +} + +void +NoteStateSave(track_p trk) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + layerSave = GetTrkLayer(trk); + posSave = xx->pos; +} + +/** +* Handle Cancel button: restore old values for layer and position +*/ + +void +CommonCancelNote(track_p trk) +{ + if (inDescribeCmd) { + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + xx->layer = layerSave; + xx->pos = posSave; + SetBoundingBox(trk, xx->pos, xx->pos); + } +} + +static void +CommonUpdateNote(track_p trk, int inx, struct extraDataNote *noteData ) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + + switch (inx) { + case OR_NOTE: + xx->pos = noteData->pos; + SetBoundingBox(trk, xx->pos, xx->pos); + break; + case LY_NOTE: + SetTrkLayer(trk, noteData->layer); + break; + case CANCEL_NOTE: + CommonCancelNote(trk); + break; + } +} + + +void UpdateFile(struct extraDataNote *noteUIData, int inx, BOOL_T needUndoStart) +{ + track_p trk = noteUIData->trk; + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + + switch (inx) { + case OR_NOTE: + case LY_NOTE: + case CANCEL_NOTE: + CommonUpdateNote(trk, inx, noteUIData); + break; + case OK_FILE: + { + DeleteNote(trk); + xx->noteData.fileData.path = MyStrdup(noteUIData->noteData.fileData.path); + xx->noteData.fileData.title = MyStrdup(noteUIData->noteData.fileData.title); + //result = malloc( maximumSize ); + //resultSize = File2URI(noteFileData->path, maximumSize, result); + //xx->text = (char*)MyMalloc(resultSize + strlen(noteFileData->title) + 2); + //sprintf(xx->text, "%s %s", result, noteFileData->title); + //if (noteFileData->inArchive) { + // CopyFile(noteFileData->path, archiveDirectory); + + //} + //free(result); + } + break; + + default: + break; + } +} + +void UpdateLink(struct extraDataNote *noteUIData, int inx, BOOL_T needUndoStart) +{ + track_p trk = noteUIData->trk; + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + + switch (inx) { + case OR_NOTE: + case LY_NOTE: + case CANCEL_NOTE: + CommonUpdateNote(trk, inx, noteUIData); + break; + + case OK_LINK: + DeleteNote(trk); + xx->noteData.linkData.title = MyStrdup(noteUIData->noteData.linkData.title); + xx->noteData.linkData.url = MyStrdup(noteUIData->noteData.linkData.url); + break; + default: + break; + } +} + +void UpdateText(struct extraDataNote *noteUIData, int inx, BOOL_T needUndoStart) +{ + track_p trk = noteUIData->trk; + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(trk); + + switch (inx) { + case OR_NOTE: + case LY_NOTE: + case CANCEL_NOTE: + CommonUpdateNote(trk, inx, noteUIData); + break; + + case OK_TEXT: + DeleteNote(trk); + xx->noteData.text = MyStrdup(noteUIData->noteData.text); + break; + default: + break; + } + changed++; +} + +/** + * Get the delimited marker for the current note. Markers start and end with + * a delimiter. The marker itself is a single digit number. For plain text notes + * no marker is used for backwards compatibility + * + * \param command IN the note's command code + * \return a pointer to the marker string. + */ + +static char * +GetNoteMarker(enum noteCommands command ) +{ + static char marker[2 * sizeof(DELIMITER) + 3]; + + switch (command) { + case OP_NOTEFILE: + case OP_NOTELINK: + sprintf(marker, DELIMITER "%d" DELIMITER, command); + break; + case OP_NOTETEXT: + default: + *marker = '\0'; + break; + } + return(marker); +} + +/** + * Write the note to file. Handles the complete syntax for a note statement + * + * \param t IN pointer to the note track element + * \param f IN file handle for writing + * \return TRUE for success + */ + +static BOOL_T WriteNote(track_p t, FILE * f) +{ + struct extraDataNote *xx = (struct extraDataNote *)GetTrkExtraData(t); + BOOL_T rc = TRUE; + + rc &= fprintf(f, "NOTE %d %u 0 0 %0.6f %0.6f 0 %d", GetTrkIndex(t), + GetTrkLayer(t), + xx->pos.x, xx->pos.y, xx->op )>0; + + char *s[2] = { NULL, NULL }; + switch (xx->op) { + case OP_NOTETEXT: + s[0]=ConvertToEscapedText( xx->noteData.text ); + break; + case OP_NOTELINK: + s[0]=ConvertToEscapedText( xx->noteData.linkData.url ); + s[1]=ConvertToEscapedText( xx->noteData.linkData.title ); + break; + case OP_NOTEFILE: + s[0]=ConvertToEscapedText( xx->noteData.fileData.path ); + s[1]=ConvertToEscapedText( xx->noteData.fileData.title ); + break; + default: + AbortProg( "WriteNote: %d", xx->op ); + } +#ifdef WINDOWS + for ( int inx = 0; inx < 2; inx++ ) { + if ( RequiresConvToUTF8( s[inx] ) ) { + wSystemToUTF8 ( s[inx], message, sizeof message ); + MyFree( s[inx] ); + s[inx] = MyStrdup( message ); + } + } +#endif + rc &= fprintf( f, " \"%s\"", s[0] )>0; + MyFree(s[0]); + if ( s[1] ) { + rc &= fprintf( f, " \"%s\"", s[1] )>0; + MyFree( s[1] ); + } + rc &= fprintf( f, "\n" )>0; + + return rc; +} + +/** + * Read a track note aka postit + * + * \param line + */ + +static BOOL_T +ReadTrackNote(char *line) +{ + track_p t; + int size; + char * cp; + struct extraDataNote *xx; + wIndex_t index; + wIndex_t layer; + coOrd pos; + DIST_T elev; + char *noteText; + enum noteCommands noteType; + char * sText; + + if (!GetArgs(line + 5, paramVersion < 3 ? "XXpYdc" : paramVersion < 9 ? + "dL00pYdc" : "dL00pfdc", + &index, &layer, &pos, &elev, &size, &cp)) { + return FALSE; + } + + if ( paramVersion >= 12 ) { + noteType = size; + t = NewNote(index, pos, noteType); + SetTrkLayer(t, layer); + + xx = (struct extraDataNote *)GetTrkExtraData(t); + switch (noteType) { + case OP_NOTETEXT: + if ( !GetArgs( cp, "qc", &sText, &cp ) ) + return FALSE; +#ifdef WINDOWS + ConvertUTF8ToSystem( sText ); +#endif + xx->noteData.text = sText; + break; + case OP_NOTELINK: + if ( !GetArgs( cp, "qc", &sText, &cp ) ) + return FALSE; +#ifdef WINDOWS + ConvertUTF8ToSystem( sText ); +#endif + xx->noteData.linkData.url = sText; + if ( !GetArgs( cp, "qc", &sText, &cp ) ) + return FALSE; +#ifdef WINDOWS + ConvertUTF8ToSystem( sText ); +#endif + xx->noteData.linkData.title = sText; + break; + case OP_NOTEFILE: + if ( !GetArgs( cp, "qc", &sText, &cp ) ) + return FALSE; +#ifdef WINDOWS + ConvertUTF8ToSystem( sText ); +#endif + xx->noteData.fileData.path = sText; + if ( !GetArgs( cp, "qc", &sText, &cp ) ) + return FALSE; +#ifdef WINDOWS + ConvertUTF8ToSystem( sText ); +#endif + xx->noteData.fileData.title = sText; + xx->noteData.fileData.inArchive = FALSE; + break; + default: + AbortProg( "ReadNote: %d", noteType ); + } + } else { + noteText = ReadMultilineText(); + + noteType = OP_NOTETEXT; + + if( !strncmp(noteText, DELIMITER, strlen( DELIMITER )) && + !strncmp(noteText + strlen(DELIMITER) + 1, DELIMITER, strlen(DELIMITER)) && + noteText[strlen(DELIMITER)] - '0' > 0 && + noteText[strlen(DELIMITER)] - '0' <= OP_NOTEFILE) + { + noteType = noteText[strlen(DELIMITER)] - '0'; + } + + t = NewNote(index, pos, noteType); + SetTrkLayer(t, layer); + + xx = (struct extraDataNote *)GetTrkExtraData(t); + + switch (noteType) { + case OP_NOTETEXT: + xx->noteData.text = MyStrdup(noteText); + break; + case OP_NOTELINK: + { + char *ptr; + ptr = strtok(noteText, " "); + xx->noteData.linkData.url = MyStrdup(ptr + 2 * strlen(DELIMITER) + 1); + xx->noteData.linkData.title = MyStrdup(noteText + strlen(ptr) + 1); + break; + } + case OP_NOTEFILE: + { + char *ptr; + ptr = strtok(noteText + 2 * strlen(DELIMITER) + 1, "\""); + xx->noteData.fileData.path = MyStrdup(ptr); + xx->noteData.fileData.title = MyStrdup(ptr + strlen(ptr) + 2 ); + xx->noteData.fileData.inArchive = FALSE; + break; + } + + } + MyFree(noteText); + } + return TRUE; +} + +/** + * Handle reading of NOTE + * + * \param line IN complete line with NOTE statement + */ + +static BOOL_T +ReadNote(char * line) +{ + if (strncmp(line, "NOTE MAIN", 9) == 0) { + return ReadMainNote(line); + } else { + return ReadTrackNote(line); + } +} + +static void MoveNote(track_p trk, coOrd orig) +{ + struct extraDataNote * xx = (struct extraDataNote *)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 extraDataNote * xx = (struct extraDataNote *)GetTrkExtraData(trk); + Rotate(&xx->pos, orig, angle); + SetBoundingBox(trk, xx->pos, xx->pos); +} + +static void RescaleNote(track_p trk, FLOAT_T ratio) +{ + struct extraDataNote * xx = (struct extraDataNote *)GetTrkExtraData(trk); + xx->pos.x *= ratio; + xx->pos.y *= ratio; +} + +static void DescribeNote(track_p trk, char * str, CSIZE_T len) +{ + if (IsLinkNote(trk)) { + DescribeLinkNote(trk, str, len); + } + else { + if (IsFileNote(trk)) { + DescribeFileNote(trk, str, len); + } else { + DescribeTextNote(trk, str, len); + } + } +} + +static void ActivateNote(track_p trk) { + if (IsLinkNote(trk) ) { + ActivateLinkNote(trk); + } + if (IsFileNote(trk)) { + ActivateFileNote(trk); + } +} + +static BOOL_T QueryNote( track_p trk, int query ) +{ + switch ( query ) { + case Q_IS_ACTIVATEABLE:; + if (IsFileNote(trk)) return TRUE; + if (IsLinkNote(trk)) return TRUE; + break; + default: + return FALSE; + } + return FALSE; +} + +static wBool_t CompareNote( track_cp trk1, track_cp trk2 ) +{ + struct extraDataNote *xx1 = (struct extraDataNote *)GetTrkExtraData( trk1 ); + struct extraDataNote *xx2 = (struct extraDataNote *)GetTrkExtraData( trk2 ); + char * cp = message + strlen(message); + REGRESS_CHECK_POS( "Pos", xx1, xx2, pos ) + REGRESS_CHECK_INT( "Layer", xx1, xx2, layer ) + REGRESS_CHECK_INT( "Op", xx1, xx2, op ) + return TRUE; +} + +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 */ + NULL, /*trim*/ + NULL, /*merge*/ + NULL, /*modify*/ + NULL, /*getLength*/ + NULL, /*getTrackParams*/ + NULL, /*moveEndPt*/ + QueryNote, /*query*/ + NULL, /*ungroup*/ + NULL, /*flip*/ + NULL, /*drawPositionIndicator*/ + NULL, /*advancePositionIndicator*/ + NULL, /*checkTraverse*/ + NULL, /*makeParallel*/ + NULL, /*drawDesc*/ + NULL, /*rebuildSegs*/ + NULL, /*replayData*/ + NULL, /*storeData*/ + ActivateNote, + CompareNote +}; + +/***************************************************************************** + * NOTE COMMAND + */ + + + +static STATUS_T CmdNote(wAction_t action, coOrd pos) +{ + static coOrd oldPos; + static int state_on = FALSE; + track_p trk; + + switch (action) { + case C_START: + InfoMessage(_("Place a note on the layout")); + curNoteType = (long)commandContext; + return C_CONTINUE; + + case C_DOWN: + state_on = TRUE; + oldPos = pos; + return C_CONTINUE; + + case C_MOVE: + oldPos = pos; + return C_CONTINUE; + + case C_UP: + UndoStart(_("New Note"), "New Note"); + state_on = FALSE; + trk = NewNote(-1, pos, curNoteType ); + inDescribeCmd = TRUE; + DrawNewTrack(trk); + + switch (curNoteType) + { + case OP_NOTETEXT: + NewTextNoteUI(trk); + break; + case OP_NOTELINK: + NewLinkNoteUI(trk); + break; + case OP_NOTEFILE: + NewFileNoteUI(trk); + break; + } + + inDescribeCmd = FALSE; + + return C_CONTINUE; + + case C_REDRAW: + if (state_on) { + switch (curNoteType) { + case OP_NOTETEXT: + DrawBitMap(&tempD, oldPos, note_bm, normalColor); + break; + case OP_NOTELINK: + DrawBitMap(&tempD, oldPos, link_bm, normalColor); + break; + } + } + return C_CONTINUE; + + case C_CANCEL: + DescribeCancel(); + state_on = FALSE; + return C_CONTINUE; + } + + return C_INFO; +} + +#include "bitmaps/note.xbm" +#include "bitmaps/link.xbm" +#include "bitmaps/clip.xbm" +#include "bitmaps/cnote.xpm" + +void InitTrkNote(wMenu_p menu) +{ + note_bm = wDrawBitMapCreate(mainD.d, note_width, note_width, 8, 8, note_bits); + link_bm = wDrawBitMapCreate(mainD.d, note_width, note_width, 8, 8, link_bits); + document_bm = wDrawBitMapCreate(mainD.d, note_width, note_width, 8, 8, clip_bits); + + ButtonGroupBegin(_("Notes"), "cmdNoteCmd", _("Add notes")); + for (int i = 0; i < NOTETYPESCOUNT; i++) { + trknoteData_t *nt; + wIcon_p icon; + + nt = noteTypes + i; + icon = wIconCreatePixMap(nt->xpm); + AddMenuButton(menu, CmdNote, nt->helpKey, _(nt->cmdName), icon, LEVEL0_50, IC_STICKY | IC_POPUP2, nt->acclKey, (void *)(intptr_t)nt->OP); + } + ButtonGroupEnd(); + + T_NOTE = InitObject(¬eCmds); +} diff --git a/app/bin/trkseg.c b/app/bin/trkseg.c index ab4b5a8..3e38933 100644 --- a/app/bin/trkseg.c +++ b/app/bin/trkseg.c @@ -142,8 +142,14 @@ EXPORT coOrd GetSegEndPt( break; case SEG_BEZTRK: case SEG_BEZLIN: - if (ep ==1) pos = segPtr->u.b.pos[3]; //For Bezier, use the End Points of the overall curve - else pos = segPtr->u.b.pos[0]; + if (ep ==1) { + pos = segPtr->u.b.pos[3]; //For Bezier, use the End Points of the overall curve + angle = FindAngle(segPtr->u.b.pos[2],segPtr->u.b.pos[3]); + } else { + pos = segPtr->u.b.pos[0]; + angle = FindAngle(segPtr->u.b.pos[1],segPtr->u.b.pos[0]); + } + break; default: AbortProg("GetSegCntPt(%c)", segPtr->type ); @@ -172,8 +178,8 @@ EXPORT void GetTextBounds( coOrd * hiR ) { - coOrd size; - POS_T descent = 0.0; + coOrd size, size2; + POS_T descent = 0.0, ascent = 0.0; coOrd lo, hi; coOrd p[4]; coOrd lastL; @@ -183,9 +189,9 @@ EXPORT void GetTextBounds( // set up the corners of the rectangle p[0].x = p[3].x = 0.0; p[1].x = p[2].x = size.x; - DrawTextSize2(&mainD, "A", NULL, fs, FALSE, &size, &descent); + DrawTextSize2(&mainD, "A", NULL, fs, FALSE, &size2, &descent, &ascent); p[0].y = p[1].y = lastL.y - descent; - p[2].y = p[3].y = size.y; + p[2].y = p[3].y = size2.y; lo = hi = zero; @@ -239,24 +245,10 @@ static void Get1SegBounds( trkSeg_p segPtr, coOrd xlat, ANGLE_T angle, coOrd *lo 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 ) { + if ( (segPtr->type == SEG_CRVTRK) || + (segPtr->type == SEG_CRVLIN) ) { + /* TODO: be more precise about curved line width */ + width.x = width.y = segPtr->width/2.0; REORIGIN( pc, segPtr->u.c.center, angle, xlat ); a0 = NormalizeAngle( segPtr->u.c.a0 + angle ); a1 = segPtr->u.c.a1; @@ -266,7 +258,7 @@ static void Get1SegBounds( trkSeg_p segPtr, coOrd xlat, ANGLE_T angle, coOrd *lo lo->y = pc.y - radius; hi->x = pc.x + radius; hi->y = pc.y + radius; - return; + break; } if ( a0 + a1 >= 360.0 ) hi->y = pc.y + radius; @@ -277,12 +269,25 @@ static void Get1SegBounds( trkSeg_p segPtr, coOrd xlat, ANGLE_T angle, coOrd *lo if ( a0 < 270.0 && a0+a1 >= 270.0 ) lo->x = pc.x - radius; } + 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_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; @@ -293,7 +298,7 @@ static void Get1SegBounds( trkSeg_p segPtr, coOrd xlat, ANGLE_T angle, coOrd *lo 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 ) + REORIGIN( p0, segPtr->u.p.pts[inx].pt, angle, xlat ) if (inx==0) { *lo = *hi = p0; } else { @@ -431,8 +436,8 @@ EXPORT void MoveSegs( 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; + s->u.p.pts[inx].pt.x += orig.x; + s->u.p.pts[inx].pt.y += orig.y; } break; case SEG_JNTTRK: @@ -484,7 +489,7 @@ EXPORT void RotateSegs( case SEG_POLY: case SEG_FILPOLY: for (inx=0; inx<s->u.p.cnt; inx++) { - Rotate( &s->u.p.pts[inx], orig, angle ); + Rotate( &s->u.p.pts[inx].pt, orig, angle ); } break; case SEG_JNTTRK: @@ -511,7 +516,7 @@ EXPORT void FlipSegs( { trkSeg_p s; int inx; - coOrd * pts; + pts_t * pts; for (s=segs; s<&segs[segCnt]; s++) { switch (s->type) { @@ -537,11 +542,11 @@ EXPORT void FlipSegs( break; case SEG_POLY: case SEG_FILPOLY: - pts = (coOrd*)MyMalloc( s->u.p.cnt * sizeof (coOrd) ); - memcpy( pts, s->u.p.pts, s->u.p.cnt * sizeof (coOrd) ); + pts = (pts_t*)MyMalloc( s->u.p.cnt * sizeof (pts_t) ); + memcpy( pts, s->u.p.pts, s->u.p.cnt * sizeof (pts_t) ); 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; + s->u.p.pts[inx].pt.y = -s->u.p.pts[inx].pt.y; } /* Don't Free - we only just got! ALso can't free other copy as that may be a template */ //MyFree(pts); @@ -602,8 +607,8 @@ EXPORT void RescaleSegs( 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; + s->u.p.pts[inx].pt.x *= scale_x; + s->u.p.pts[inx].pt.y *= scale_y; } break; case SEG_JNTTRK: @@ -638,7 +643,7 @@ EXPORT void CloneFilledDraw( trkSeg_p segs, BOOL_T reorigin ) { - coOrd * newPts; + pts_t * newPts; trkSeg_p sp; wIndex_t inx; @@ -646,21 +651,20 @@ EXPORT void CloneFilledDraw( switch (sp->type) { case SEG_POLY: case SEG_FILPOLY: - newPts = (coOrd*)MyMalloc( sp->u.p.cnt * sizeof (coOrd) ); + newPts = (pts_t*)MyMalloc( sp->u.p.cnt * sizeof (pts_t) ); + memcpy( newPts, sp->u.p.pts, sp->u.p.cnt * sizeof (pts_t) ); 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 ); + REORIGIN( newPts[inx].pt, sp->u.p.pts[inx].pt, 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) ); } //if (sp->u.p.pts) Can't do this a pts could be pointing at static // free(sp->u.p.pts); sp->u.p.pts = newPts; break; case SEG_TEXT: - sp->u.t.string = strdup( sp->u.t.string); + sp->u.t.string = MyStrdup( sp->u.t.string); break; case SEG_BEZTRK: case SEG_BEZLIN: @@ -753,9 +757,9 @@ EXPORT DIST_T DistanceSegs( 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] ); + ddd = LineDistance( &pt, segPtr->u.p.pts[lin].pt, segPtr->u.p.pts[lin+1].pt ); else - ddd = LineDistance( &pt, segPtr->u.p.pts[lin], segPtr->u.p.pts[0] ); + ddd = LineDistance( &pt, segPtr->u.p.pts[lin].pt, segPtr->u.p.pts[0].pt ); if ( ddd < dd ) { dd = ddd; p1 = pt; @@ -846,7 +850,7 @@ EXPORT ANGLE_T GetAngleSegs( { wIndex_t inx; ANGLE_T angle = 0.0; - coOrd p0; + coOrd p0,p1; DIST_T d, dd; segProcData_t segProcData; coOrd pos2 = * pos1; @@ -889,15 +893,17 @@ EXPORT ANGLE_T GetAngleSegs( break; case SEG_POLY: case SEG_FILPOLY: - p0 = pos2; - 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] ); + p0 = p1 = pos2; + dd = LineDistance( &p0, segPtr->u.p.pts[segPtr->u.p.cnt-1].pt, segPtr->u.p.pts[0].pt ); + angle = FindAngle( segPtr->u.p.pts[segPtr->u.p.cnt-1].pt, segPtr->u.p.pts[0].pt ); for ( inx=0; inx<segPtr->u.p.cnt-1; inx++ ) { - p0 = pos2; - d = LineDistance( &p0, segPtr->u.p.pts[inx], segPtr->u.p.pts[inx+1] ); + p0 = p1; + d = LineDistance( &p0, segPtr->u.p.pts[inx].pt, segPtr->u.p.pts[inx+1].pt ); if ( d < dd ) { dd = d; - angle = FindAngle( segPtr->u.p.pts[inx], segPtr->u.p.pts[inx+1] ); + angle = FindAngle( segPtr->u.p.pts[inx].pt, segPtr->u.p.pts[inx+1].pt ); + if (subSegInxR) *subSegInxR = inx; + pos2 = p0; } } break; @@ -1125,14 +1131,14 @@ static void AppendPath( signed char c ) EXPORT BOOL_T ReadSegs( void ) { char *cp, *cpp; - BOOL_T rc=FALSE; + BOOL_T rc=TRUE; trkSeg_p s; trkEndPt_p e; long rgb; int i; DIST_T elev0, elev1; BOOL_T hasElev; - BOOL_T isPolyV2, noVersion; + BOOL_T isPolyV1, isPolyV2; BOOL_T improvedEnds; FLOAT_T ignoreFloat; char type; @@ -1146,14 +1152,13 @@ EXPORT BOOL_T ReadSegs( void ) DYNARR_RESET( trkSeg_t, tempSegs_da ); DYNARR_RESET( trkEndPt_t, tempEndPts_da ); pathCnt = 0; - while ( (cp = GetNextLine()) != NULL ) { + AppendPath(0); // End of all paths + while ( rc && ((cp = GetNextLine()) != NULL) ) { while (isspace(*cp)) cp++; hasElev = FALSE; improvedEnds = FALSE; - if ( strncmp( cp, "END", 3 ) == 0 ) { - rc = TRUE; - subsegs = FALSE; - break; + if ( IsEND( END_SEGS ) ) { + return TRUE; } if ( strncmp(cp, "SUBSEGS", 7) == 0) { subsegs = TRUE; @@ -1163,24 +1168,22 @@ EXPORT BOOL_T ReadSegs( void ) subsegs = FALSE; continue; } - if ( *cp == '\n' || *cp == '#' ) { + if ( *cp == '\0' || *cp == '\n' || *cp == '#' ) { continue; } + if (subsegs) continue; type = *cp++; - hasElev = FALSE; - noVersion = TRUE; - if ( *cp != ' ') - noVersion = FALSE; - if ( *cp == '3' ) { - cp++; - hasElev = TRUE; - } - isPolyV2 = FALSE; - if (*cp == '4') { - cp++; - hasElev = TRUE; - isPolyV2 = TRUE; - improvedEnds = TRUE; + improvedEnds = isPolyV2 = hasElev = isPolyV1 = FALSE; + if ( isdigit( *cp ) ) { + long iVersion = strtol( cp, &cp, 10 ); + if ( iVersion >= 3 ) + hasElev = isPolyV1 = TRUE; + if ( iVersion >= 4 ) + improvedEnds = isPolyV2 = TRUE; + if ( iVersion > 4 ) { + InputError( "Invalid segment version number, maximum is %d", TRUE, 4 ); + break; + } } switch (type) { case SEG_STRLIN: @@ -1341,34 +1344,28 @@ EXPORT BOOL_T ReadSegs( void ) s = &tempSegs(tempSegs_da.cnt-1); s->type = type; s->u.p.polyType = FREEFORM; - if (isPolyV2) { - if ( !GetArgs( cp, "lwdd", - &rgb, &s->width, - &s->u.p.cnt, &s->u.p.polyType) ) { - rc = FALSE; - /*??*/break; - } - } else { - if ( !GetArgs( cp, "lwd", - &rgb, &s->width, - &s->u.p.cnt) ) { - rc = FALSE; - /*??*/break; - } + if ( !GetArgs( cp, + isPolyV2?"lwdd":"lwdX", + &rgb, + &s->width, + &s->u.p.cnt, + &s->u.p.polyType) ) { + rc = FALSE; + /*??*/break; } s->color = wDrawFindColor( rgb ); - s->u.p.pts = (coOrd*)MyMalloc( s->u.p.cnt * sizeof (coOrd) ); + s->u.p.pts = (pts_t*)MyMalloc( s->u.p.cnt * sizeof (pts_t) ); for ( i=0; i<s->u.p.cnt; i++ ) { cp = GetNextLine(); - if (cp == NULL || !GetArgs( cp, "p", &s->u.p.pts[i])) { + if (cp == NULL || + !GetArgs( cp, +// TODO: does elev belong here instead of the seg header? + isPolyV2?"pdY":isPolyV1?"pXf":"pXY", + &s->u.p.pts[i].pt, + &s->u.p.pts[i].pt_type, + &elev0 )) { rc = FALSE; } - if (!noVersion) { - if (cp == NULL || !GetArgs( cp, hasElev?"f":"Y", &elev0 ) ) { - rc = FALSE; - /*??*/break; - } - } } s->u.p.angle = 0.0; s->u.p.orig = zero; @@ -1378,14 +1375,11 @@ EXPORT BOOL_T ReadSegs( void ) s = &tempSegs(tempSegs_da.cnt-1); s->type = type; s->u.t.fontP = NULL; - char * expandedText; - if ( !GetArgs( cp, "lpf0fq", &rgb, &s->u.t.pos, &s->u.t.angle, &s->u.t.fontSize, &expandedText ) ) { + if ( !GetArgs( cp, "lpfdfq", &rgb, &s->u.t.pos, &s->u.t.angle, &s->u.t.boxed, &s->u.t.fontSize, &plain_text ) ) { rc = FALSE; /*??*/break; } - plain_text = ConvertFromEscapedText(expandedText); - s->u.t.string = plain_text; - MyFree(expandedText); + s->u.t.string = MyStrdup(plain_text); s->color = wDrawFindColor( rgb ); break; case SEG_UNCEP: @@ -1455,18 +1449,20 @@ EXPORT BOOL_T ReadSegs( void ) case SEG_PATH: while (isspace(*cp)) cp++; if (*cp == '\"') cp++; - while ( *cp != '\"') AppendPath((signed char)*cp++); - AppendPath(0); + pathCnt--; // Overwrite previous terminator + while ( *cp != '\"') AppendPath((signed char)*cp++); // Name of path + AppendPath(0); // End of name cp++; while (1) { i = (int)strtol(cp, &cpp, 10); if (cp == cpp) /*??*/break; cp = cpp; - AppendPath( (signed char)i ); + AppendPath( (signed char)i ); // Segment # of path component } - AppendPath( 0 ); - AppendPath( 0 ); + AppendPath( 0 ); // End of last path components + AppendPath( 0 ); // End of path + AppendPath( 0 ); // End of all paths break; case SEG_SPEC: strncpy( tempSpecial, cp+1, sizeof tempSpecial - 1 ); @@ -1481,11 +1477,18 @@ EXPORT BOOL_T ReadSegs( void ) } break; default: + InputError( "Unknown segment type: %c", TRUE, type ); + rc = FALSE; break; } } - AppendPath( 0 ); - return rc; +// We've hit an InputError. +// The user may have chosen to not continue (paramFile == NULL) +// Otherwise we have to flush until we see END$SEGS + + while ( GetNextLine() && !IsEND( END_SEGS ) ); // Chomp, Chomp + + return FALSE; } EXPORT BOOL_T WriteSegs( @@ -1559,7 +1562,8 @@ EXPORT BOOL_T WriteSegsEnd( case SEG_BEZTRK: case SEG_BEZLIN: rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f %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].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.pos[2].x, segs[i].u.l.pos[2].y, @@ -1584,24 +1588,26 @@ EXPORT BOOL_T WriteSegsEnd( break; case SEG_POLY: case SEG_FILPOLY: +// TODO: to be consistent, we should add a dummy 0 for elev. See ReadSegs/SEG_POLY rc &= fprintf( f, "\t%c4 %ld %0.6f %d %d \n", segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, segs[i].u.p.cnt, segs[i].u.p.polyType ) > 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; + rc &= fprintf( f, "\t\t%0.6f %0.6f %d\n", + segs[i].u.p.pts[j].pt.x, segs[i].u.p.pts[j].pt.y, segs[i].u.p.pts[j].pt_type ) > 0; break; case SEG_TEXT: /* 0pf0fq */ escaped_text = ConvertToEscapedText(segs[i].u.t.string); - rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f 0 %0.6f \"%s\"\n", + rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %d %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.boxed, segs[i].u.t.fontSize, escaped_text ) > 0; MyFree(escaped_text); break; } } - if (writeEnd) rc &= fprintf( f, "\tEND\n" )>0; + if (writeEnd) rc &= fprintf( f, "\t%s\n", END_SEGS )>0; return rc; } @@ -1664,15 +1670,15 @@ EXPORT void DrawDimLine( if ( ( option & 0x10 ) == 0 ) { Translate( &p, p0, a0-45, dist ); - DrawLine( d, p0, p, 0, color ); + DrawLine( d, p0, p, width, color ); Translate( &p, p0, a0+45, dist ); - DrawLine( d, p0, p, 0, color ); + DrawLine( d, p0, p, width, color ); } if ( ( option & 0x20 ) == 0 ) { Translate( &p, p1, a0-135, dist ); - DrawLine( d, p1, p, 0, color ); + DrawLine( d, p1, p, width, color ); Translate( &p, p1, a0+135, dist ); - DrawLine( d, p1, p, 0, color ); + DrawLine( d, p1, p, width, color ); } if ( fs < 2*d->scale ) { @@ -1686,7 +1692,7 @@ EXPORT void DrawDimLine( size.y = textsize.y/2.0; dist1 = FindDistance( zero, size ); if ( dist <= dist1*2 ) { - DrawLine( d, p0, p1, 0, color ); + DrawLine( d, p0, p1, width, color ); return; } a1 = FindAngle( zero, size ); @@ -1715,11 +1721,11 @@ EXPORT void DrawDimLine( p = pc; p.x -= fx*x; p.y -= fy*y; - DrawLine( d, p0, p, 0, color ); + DrawLine( d, p0, p, width, color ); p = pc; p.x += fx*x; p.y += fy*y; - DrawLine( d, p, p1, 0, color ); + DrawLine( d, p, p1, width, color ); } /* @@ -1747,6 +1753,8 @@ EXPORT void DrawSegsO( long option; wFontSize_t fs; + wBool_t bFill; + for (i=0; i<segCnt; i++,segPtr++ ) { if (color == wDrawColorBlack) { color1 = segPtr->color; @@ -1754,60 +1762,10 @@ EXPORT void DrawSegsO( } 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; - case SEG_BEZTRK: - REORIGIN(p0, segPtr->u.b.pos[0], angle, orig); - REORIGIN(p1, segPtr->u.b.pos[1], angle, orig); - REORIGIN(p2, segPtr->u.b.pos[2], angle, orig); - REORIGIN(p3, segPtr->u.b.pos[3], angle, orig); - tempPtr = segPtr->bezSegs.ptr; - for(int j=0;j<segPtr->bezSegs.cnt;j++,tempPtr++) { //Loop through sub parts (only Trks supported) - if (tempPtr->type == SEG_CRVTRK) { - a0 = NormalizeAngle(tempPtr->u.c.a0 + angle); - REORIGIN( c, tempPtr->u.c.center, angle, orig ); - DrawCurvedTies( d, trk, c, fabs(tempPtr->u.c.radius), a0, tempPtr->u.c.a1, color ); - } - if (tempPtr->type == SEG_STRTRK) { - REORIGIN( p0, tempPtr->u.l.pos[0], angle, orig ) - REORIGIN( p1, tempPtr->u.l.pos[1], angle, orig ) - DrawStraightTies( d, trk, p0, p1, color ); - } - } - break; - } - continue; - } - switch (segPtr->type) { - case SEG_STRTRK: - case SEG_CRVTRK: - case SEG_JNTTRK: - case SEG_BEZTRK: - case SEG_TEXT: - break; - default: - if (d->options&DC_QUICK) - return; - if ((d->options&DC_SIMPLE) != 0 && - trackGauge != 0.0) - return; - } + wDrawWidth thick = 3; +#ifdef WINDOWS + thick *= (wDrawWidth)(d->dpi/mainD.dpi); +#endif switch (segPtr->type) { case SEG_STRLIN: case SEG_DIMLIN: @@ -1824,17 +1782,16 @@ EXPORT void DrawSegsO( break; DrawStraightTrack( d, p0, p1, - FindAngle(p0, p1 ), - NULL, trackGauge, color1, options ); + FindAngle(p1, p0 ), + trk, color1, options ); break; case SEG_STRLIN: - DrawLine( d, p0, p1, (wDrawWidth)floor(segPtr->width*factor+0.5), color1 ); + DrawLine( d, p0, p1, (d->options&DC_THICK)?thick:(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) ) { + if ( (d->options&DC_SEGTRACK) ) { DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); tempPtr = &tempSegs(tempSegs_da.cnt-1); memcpy( tempPtr, segPtr, sizeof segPtr[0] ); @@ -1848,7 +1805,7 @@ EXPORT void DrawSegsO( 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 ); + DrawDimLine( d, p0, p1, FormatDistance(FindDistance(p0,p1)), fs, 0.5, (d->options&DC_THICK)?thick:0, color, option & 0x00 ); break; case SEG_BENCH: DrawBench( d, p0, p1, color1, color2, options, segPtr->u.l.option ); @@ -1876,10 +1833,10 @@ EXPORT void DrawSegsO( fabs(segPtr->u.c.radius), a0, segPtr->u.c.a1, p0, p1, - NULL, trackGauge, color1, options ); + trk, 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 ); + FALSE, (d->options&DC_THICK)?thick:(wDrawWidth)floor(segPtr->width*factor+0.5), color1 ); } break; case SEG_BEZTRK: @@ -1889,7 +1846,7 @@ EXPORT void DrawSegsO( color1 = normalColor; if ( segPtr->color == wDrawColorWhite ) break; - } + } else REORIGIN(p0, segPtr->u.b.pos[0], angle, orig); REORIGIN(p1, segPtr->u.b.pos[1], angle, orig); REORIGIN(p2, segPtr->u.b.pos[2], angle, orig); @@ -1911,10 +1868,10 @@ EXPORT void DrawSegsO( fabs(tempPtr->u.c.radius), a0, tempPtr->u.c.a1, p0, p1, - NULL, trackGauge, color1, options ); + trk, color1, options ); } else if (tempPtr->type == SEG_CRVLIN) { DrawArc( d, c, fabs(tempPtr->u.c.radius), a0, tempPtr->u.c.a1, - FALSE, (wDrawWidth)floor(tempPtr->width*factor+0.5), color1 ); + FALSE, (d->options&DC_THICK)?thick:(wDrawWidth)floor(tempPtr->width*factor+0.5), color1 ); } break; case SEG_STRTRK: @@ -1922,15 +1879,14 @@ EXPORT void DrawSegsO( if ( tempPtr->color == wDrawColorWhite ) break; REORIGIN(p0,tempPtr->u.l.pos[0], angle, orig); REORIGIN(p1,tempPtr->u.l.pos[1], angle, orig); - DrawStraightTrack( d, - p0, p1, - FindAngle(p0, p1 ), - NULL, trackGauge, color1, options ); + DrawStraightTrack( d, p0, p1, + FindAngle(p1, p0 ), + trk, color1, options ); break; case SEG_STRLIN: REORIGIN(p0,tempPtr->u.l.pos[0], angle, orig); REORIGIN(p1,tempPtr->u.l.pos[1], angle, orig); - DrawLine( d, p0, p1, (wDrawWidth)floor(tempPtr->width*factor+0.5), color1 ); + DrawLine( d, p0, p1, (d->options&DC_THICK)?thick:(wDrawWidth)floor(tempPtr->width*factor+0.5), color1 ); break; } } @@ -1941,48 +1897,37 @@ EXPORT void DrawSegsO( break; case SEG_TEXT: REORIGIN( p0, segPtr->u.t.pos, angle, orig ) - DrawMultiString( d, p0, segPtr->u.t.string, segPtr->u.t.fontP, segPtr->u.t.fontSize, color1, NormalizeAngle(angle + segPtr->u.t.angle), NULL, NULL ); + DrawMultiString( d, p0, segPtr->u.t.string, segPtr->u.t.fontP, segPtr->u.t.fontSize, color1, NormalizeAngle(angle + segPtr->u.t.angle), NULL, NULL, segPtr->u.t.boxed ); 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 */ - coOrd *tempPts = malloc(sizeof(coOrd)*segPtr->u.p.cnt); -// coOrd tempPts[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, color1 ); - free(tempPts); - 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; + ; + /* Note: if we call tempSegDrawFillPoly we get a nasty bug + /+ because we don't make a private copy of p.pts */ + coOrd *tempPts = malloc(sizeof(coOrd)*segPtr->u.p.cnt); + int *tempTypes = malloc(sizeof(int)*segPtr->u.p.cnt); +// coOrd tempPts[segPtr->u.p.cnt]; + for (j=0;j<segPtr->u.p.cnt;j++) { + REORIGIN( tempPts[j], segPtr->u.p.pts[j].pt, angle, orig ); + tempTypes[j] = segPtr->u.p.pts[j].pt_type; } - DrawLine( d, p0, c, (wDrawWidth)floor(segPtr->width*factor+0.5), color1 ); + bFill = (segPtr->type == SEG_FILPOLY); + if ( (d->options&DC_SIMPLE) && programMode != MODE_TRAIN ) + bFill = FALSE; + DrawPoly( d, segPtr->u.p.cnt, tempPts, tempTypes, color1, (d->options&DC_THICK)?thick:(wDrawWidth)floor(segPtr->width*factor+0.5), bFill?1:0, segPtr->u.p.polyType==POLYLINE?1:0); + free(tempPts); + free(tempTypes); break; case SEG_FILCRCL: REORIGIN( c, segPtr->u.c.center, angle, orig ) - if ( (d->options&DC_GROUP) != 0 || - d->funcs != &tempSegDrawFuncs ) { + bFill = TRUE; + if ( (d->options&DC_SIMPLE) && programMode != MODE_TRAIN ) + bFill = FALSE; + if ( bFill ) { DrawFillCircle( d, c, fabs(segPtr->u.c.radius), color1 ); } else { DrawArc( d, c, fabs(segPtr->u.c.radius), 0, 360, - FALSE, (wDrawWidth)0, color1 ); + FALSE, (d->options&DC_THICK)?thick:(wDrawWidth)0, color1 ); } break; } @@ -2003,7 +1948,8 @@ EXPORT void DrawSegs( DIST_T trackGauge, wDrawColor color ) { - DrawSegsO( d, NULL, orig, angle, segPtr, segCnt, trackGauge, color, 0 ); + + DrawSegsO( d, NULL, orig, angle, segPtr, segCnt, trackGauge, color, DTS_LEFT|DTS_RIGHT ); } /* @@ -2031,16 +1977,170 @@ EXPORT void CleanSegs(dynArr_t * seg_p) { seg_p->max = 0; } +/* + * Copy Segs from one array to another + */ +EXPORT void AppendSegsToArray(dynArr_t * seg_to, dynArr_t * seg_from) { + if (seg_from->cnt ==0) return; + int j = 0; + DYNARR_APPEND(trkSeg_t, * seg_to, seg_from->cnt); + for (int i=0; i<seg_from->cnt;i++,j++) { + trkSeg_p from_p = &DYNARR_N(trkSeg_t, * seg_from,j); + trkSeg_p to_p = &DYNARR_N(trkSeg_t, * seg_to,i); + memcpy((void *)to_p,(void *)from_p,sizeof( trkSeg_t)); + if (from_p->type == SEG_BEZLIN || from_p->type == SEG_BEZTRK) { + if (from_p->bezSegs.ptr) { + to_p->bezSegs.ptr = memdup(from_p->bezSegs.ptr,from_p->bezSegs.cnt*sizeof(trkSeg_t)); + } + } + if (from_p->type == SEG_POLY || from_p->type == SEG_FILPOLY) { + if (from_p->u.p.pts) { + to_p->u.p.pts = memdup(from_p->u.p.pts,from_p->u.p.cnt*sizeof(pts_t)); + } + } + } +} + +EXPORT void AppendTransformedSegs(dynArr_t * seg_to, dynArr_t * seg_from, coOrd orig, coOrd rotateOrig, ANGLE_T angle) { + if (seg_from->cnt ==0) return; + int j = 0; + DYNARR_APPEND(trkSeg_t, * seg_to, seg_from->cnt); + for (int i=0; i<seg_from->cnt;i++,j++) { + trkSeg_p from_p = &DYNARR_N(trkSeg_t, * seg_from,j); + trkSeg_p to_p = &DYNARR_N(trkSeg_t, * seg_to,i); + memcpy((void *)to_p,(void *)from_p,sizeof( trkSeg_t)); + if (from_p->type == SEG_BEZLIN || from_p->type == SEG_BEZTRK) { + if (from_p->bezSegs.ptr) { + to_p->bezSegs.ptr = memdup(from_p->bezSegs.ptr,from_p->bezSegs.cnt*sizeof(trkSeg_t)); + } + } + if (from_p->type == SEG_POLY || from_p->type == SEG_FILPOLY) { + if (from_p->u.p.pts) { + to_p->u.p.pts = memdup(from_p->u.p.pts,from_p->u.p.cnt*sizeof(pts_t)); + } + } + RotateSegs(1,to_p,rotateOrig,angle); + coOrd move; + move.x = orig.x - rotateOrig.x; + move.y = orig.y - rotateOrig.y; + MoveSegs(1,to_p,move); + } +} + EXPORT void CopyPoly(trkSeg_p p, wIndex_t segCnt) { - coOrd * newPts; + pts_t * newPts; for (int i=0;i<segCnt;i++,p++) { - if (p->type == SEG_POLY || p->type == SEG_FILPOLY) { - newPts = memdup( p->u.p.pts, p->u.p.cnt*sizeof (coOrd) ); + if ((p->type == SEG_POLY) || (p->type == SEG_FILPOLY)) { + newPts = memdup( p->u.p.pts, p->u.p.cnt*sizeof (pts_t) ); p->u.p.pts = newPts; } + if ( p->type == SEG_TEXT ) { + p->u.t.string = MyStrdup( p->u.t.string ); + } } } - - +EXPORT wBool_t CompareSegs( + trkSeg_p segPtr1, + int segCnt1, + trkSeg_p segPtr2, + int segCnt2 ) +{ + char * cp = message+strlen(message); + if ( segCnt1 != segCnt2 ) { + sprintf( cp, "SegCnt %d %d\n", segCnt1, segCnt2 ); + return FALSE; + } + char * cq = cp-2;; + for ( int segInx = 0; segInx < segCnt1; segInx++ ) { + cp = cq; + sprintf( cp, "SEG:%d - ", segInx ); + cp += strlen( cp ); + trkSeg_p segP1 = &segPtr1[segInx]; + trkSeg_p segP2 = &segPtr2[segInx]; + REGRESS_CHECK_INT( "Type", segP1, segP2, type ); + REGRESS_CHECK_COLOR( "Color", segP1, segP2, color ); + switch( segP1->type ) { + case SEG_DIMLIN: + case SEG_TBLEDGE: + case SEG_BENCH: + // These don't have widths + break; + default: + REGRESS_CHECK_WIDTH( "Width", segP1, segP2, width ); + } + switch( segP1->type ) { + case SEG_DIMLIN: + case SEG_TBLEDGE: + // These don't have color + break; + default: + REGRESS_CHECK_COLOR( "Color", segP1, segP2, color ); + } + switch( segP1->type ) { + case SEG_STRTRK: + case SEG_STRLIN: + case SEG_DIMLIN: + case SEG_BENCH: + case SEG_TBLEDGE: + REGRESS_CHECK_POS( "Pos[0]", segP1, segP2, u.l.pos[0] ) + REGRESS_CHECK_POS( "Pos[1]", segP1, segP2, u.l.pos[1] ) +// REGRESS_CHECK_POS( "Pos[2]", segP1, segP2, u.l.pos[2] ) +// REGRESS_CHECK_POS( "Pos[3]", segP1, segP2, u.l.pos[3] ) + REGRESS_CHECK_ANGLE( "Angle", segP1, segP2, u.l.angle ) + REGRESS_CHECK_INT( "Option", segP1, segP2, u.l.option ) + break; + case SEG_BEZTRK: + case SEG_BEZLIN: + break; // u.b + case SEG_CRVLIN: + case SEG_CRVTRK: + case SEG_FILCRCL: + REGRESS_CHECK_POS( "Center", segP1, segP2, u.c.center ) + REGRESS_CHECK_ANGLE( "A0", segP1, segP2, u.c.a0 ) + REGRESS_CHECK_ANGLE( "A1", segP1, segP2, u.c.a1 ) + REGRESS_CHECK_DIST( "Radius", segP1, segP2, u.c.radius ) + break; // u.c + case SEG_JNTTRK: + REGRESS_CHECK_POS( "Pos", segP1, segP2, u.j.pos ) + REGRESS_CHECK_ANGLE( "Angle", segP1, segP2, u.j.angle ) + REGRESS_CHECK_DIST( "R", segP1, segP2, u.j.R ) + REGRESS_CHECK_DIST( "L", segP1, segP2, u.j.L ) + REGRESS_CHECK_DIST( "L0", segP1, segP2, u.j.l0 ) + REGRESS_CHECK_DIST( "L1", segP1, segP2, u.j.l1 ); + REGRESS_CHECK_INT( "Flip", segP1, segP2, u.j.flip ) + REGRESS_CHECK_INT( "Negate", segP1, segP2, u.j.negate ) + REGRESS_CHECK_INT( "Scurve", segP1, segP2, u.j.Scurve ) + break; // u.j + case SEG_TEXT: + REGRESS_CHECK_POS( "Pos", segP1, segP2, u.t.pos ) + REGRESS_CHECK_ANGLE( "Angle", segP1, segP2, u.t.angle ) + // CHECK fontP + REGRESS_CHECK_DIST( "Fontsize", segP1, segP2, u.t.fontSize ) + REGRESS_CHECK_INT( "Boxed", segP1, segP2, u.t.boxed ) + // CHECK string + break; // u.t + case SEG_POLY: + case SEG_FILPOLY: + REGRESS_CHECK_INT( "Cnt", segP1, segP2, u.p.cnt ) + // CHECK pts + REGRESS_CHECK_POS( "Orig", segP1, segP2, u.p.orig ) + REGRESS_CHECK_ANGLE( "Angle", segP1, segP2, u.p.angle ) + break; // u.p + // EndPts + case SEG_UNCEP: + case SEG_CONEP: + break; + // Turnout/Struct + case SEG_PATH: + case SEG_SPEC: + case SEG_CUST: + case SEG_DOFF: + break; + default: + break; + } + } + return TRUE; +} diff --git a/app/bin/tstraigh.c b/app/bin/tstraigh.c index 5cf1cda..f9b666f 100644 --- a/app/bin/tstraigh.c +++ b/app/bin/tstraigh.c @@ -163,7 +163,7 @@ static void UpdateStraight( track_p trk, int inx, descData_p descUpd, BOOL_T fin 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 ); + ComputeElev( trk, 1-ep, FALSE, &strData.elev[1-ep], NULL, TRUE ); if ( strData.length > minLength ) strData.grade = fabs( (strData.elev[0]-strData.elev[1])/strData.length )*100.0; else @@ -242,8 +242,8 @@ static void DescribeStraight( track_p trk, char * str, CSIZE_T len ) 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 ); + ComputeElev( trk, 0, FALSE, &strData.elev[0], NULL, FALSE ); + ComputeElev( trk, 1, FALSE, &strData.elev[1], NULL, FALSE ); strData.length = FindDistance( strData.endPt[0], strData.endPt[1] ); strData.layerNumber = GetTrkLayer(trk); if ( strData.length > minLength ) @@ -274,18 +274,12 @@ static DIST_T DistanceStraight( track_p t, coOrd * p ) 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; + long widthOptions = DTS_LEFT|DTS_RIGHT; 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 ); - } + t, color, widthOptions ); + DrawEndPt( d, t, 0, color ); + DrawEndPt( d, t, 1, color ); } static void DeleteStraight( track_p t ) @@ -297,14 +291,14 @@ 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; + GetTrkScaleName(t), GetTrkVisible(t)|(GetTrkNoTies(t)?1<<2:0)|(GetTrkBridge(t)?1<<3:0) )>0; rc &= WriteEndPt( f, t, 0 ); rc &= WriteEndPt( f, t, 1 ); - rc &= fprintf(f, "\tEND\n" )>0; + rc &= fprintf(f, "\t%s\n", END_SEGS)>0; return rc; } -static void ReadStraight( char * line ) +static BOOL_T ReadStraight( char * line ) { track_p trk; wIndex_t index; @@ -314,15 +308,25 @@ static void ReadStraight( char * line ) long options; if ( !GetArgs( line+8, paramVersion<3?"dXZsd":"dLl00sd", &index, &layer, &options, scale, &visible ) ) - return; + return FALSE; + if ( !ReadSegs() ) + return FALSE; trk = NewTrack( index, T_STRAIGHT, 0, 0 ); SetTrkScale( trk, LookupScale(scale) ); - SetTrkVisible(trk, visible); + if ( paramVersion < 3 ) { + SetTrkVisible(trk, visible!=0); + SetTrkNoTies(trk, FALSE); + SetTrkBridge(trk, FALSE); + } else { + SetTrkVisible(trk, visible&2); + SetTrkNoTies(trk, visible&4); + SetTrkBridge(trk, visible&8); + } SetTrkLayer(trk, layer); SetTrkWidth( trk, (int)(options&3) ); - ReadSegs(); SetEndPts( trk, 2 ); ComputeBoundingBox( trk ); + return TRUE; } static void MoveStraight( track_p trk, coOrd orig ) @@ -362,11 +366,16 @@ static BOOL_T SplitStraight( track_p trk, coOrd pos, EPINX_T ep, track_p *leftov { track_p trk1; - trk1 = NewStraightTrack( GetTrkEndPos(trk,ep), pos ); + trk1 = NewStraightTrack( 1-ep?GetTrkEndPos(trk,ep):pos, 1-ep?pos:GetTrkEndPos(trk,ep) ); + DIST_T height; + int opt; + GetTrkEndElev(trk,ep,&opt,&height); + UpdateTrkEndElev( trk1, ep, opt, height, (opt==ELEV_STATION)?GetTrkEndElevStation(trk,ep):NULL ); AdjustStraightEndPt( trk, ep, pos ); + UpdateTrkEndElev( trk, ep, ELEV_NONE, 0, NULL); *leftover = trk1; - *ep0 = 1; - *ep1 = 0; + *ep0 = 1-ep; + *ep1 = ep; return TRUE; } @@ -413,7 +422,7 @@ static BOOL_T EnumerateStraight( track_p trk ) return TRUE; } -static BOOL_T TrimStraight( track_p trk, EPINX_T ep, DIST_T dist ) +static BOOL_T TrimStraight( track_p trk, EPINX_T ep, DIST_T dist, coOrd endpos, ANGLE_T angle, DIST_T radius, coOrd center ) { DIST_T d; ANGLE_T a; @@ -427,8 +436,10 @@ static BOOL_T TrimStraight( track_p trk, EPINX_T ep, DIST_T dist ) UndrawNewTrack( trk ); AdjustStraightEndPt( trk, ep, pos ); DrawNewTrack( trk ); - } else + } else { + UndrawNewTrack( trk ); DeleteTrack( trk, TRUE ); + } return TRUE; } @@ -493,6 +504,7 @@ BOOL_T ExtendStraightToJoin( DisconnectTracks( trk1, 1-ep1, trk1x, ep1x ); } if (trk2) { + UndrawNewTrack( trk1 ); DeleteTrack( trk1, TRUE ); } else { trk2 = trk1; @@ -555,17 +567,13 @@ static STATUS_T ModifyStraight( track_p trk, wAction_t action, coOrd pos ) if (action == C_MOVE) InfoMessage( _("Straight: Length=%s Angle=%0.3f"), FormatDistance( d ), PutAngle( GetTrkEndAngle( trk, ep ) ) ); - MainRedraw(); - MapRedraw(); return C_CONTINUE; case C_UP: if (valid) AdjustStraightEndPt( trk, ep, tempSegs(0).u.l.pos[1] ); tempSegs_da.cnt = 0; - DrawNewTrack( trk ); - MainRedraw(); - MapRedraw(); + DrawNewTrack( trk ); return C_TERMINATE; default: @@ -584,9 +592,8 @@ static DIST_T GetLengthStraight( track_p trk ) 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 if (inx == PARAMS_CORNU ){ + if ( inx == PARAMS_NODES ) return FALSE; + if ((inx == PARAMS_CORNU) || (inx == PARAMS_1ST_JOIN) || (inx == PARAMS_2ND_JOIN) ){ params->ep = PickEndPoint( pos, trk); params->arcP = zero; params->arcR = 0.0; @@ -627,6 +634,7 @@ static BOOL_T QueryStraight( track_p trk, int query ) case Q_ISTRACK: case Q_CORNU_CAN_MODIFY: case Q_MODIFY_CAN_SPLIT: + case Q_CAN_EXTEND: return TRUE; default: return FALSE; @@ -647,9 +655,11 @@ static BOOL_T MakeParallelStraight( track_p trk, coOrd pos, DIST_T sep, + DIST_T factor, track_p * newTrkR, coOrd * p0R, - coOrd * p1R ) + coOrd * p1R, + BOOL_T track) { ANGLE_T angle = GetTrkEndAngle(trk,1); coOrd p0, p1; @@ -660,12 +670,23 @@ static BOOL_T MakeParallelStraight( Translate( &p0, GetTrkEndPos(trk,0), angle, sep ); Translate( &p1, GetTrkEndPos(trk,1), angle, sep ); if ( newTrkR ) { - *newTrkR = NewStraightTrack( p0, p1 ); + if (track) + *newTrkR = NewStraightTrack( p0, p1 ); + else { + tempSegs(0).color = wDrawColorBlack; + tempSegs(0).width = 0; + tempSegs_da.cnt = 1; + tempSegs(0).type = SEG_STRLIN; + tempSegs(0).u.l.pos[0] = p0; + tempSegs(0).u.l.pos[1] = p1; + *newTrkR = MakeDrawFromSeg( zero, 0.0, &tempSegs(0) ); + } + } else { tempSegs(0).color = wDrawColorBlack; tempSegs(0).width = 0; tempSegs_da.cnt = 1; - tempSegs(0).type = SEG_STRTRK; + tempSegs(0).type = track?SEG_STRTRK:SEG_STRLIN; tempSegs(0).u.l.pos[0] = p0; tempSegs(0).u.l.pos[1] = p1; } @@ -675,6 +696,12 @@ static BOOL_T MakeParallelStraight( } +static wBool_t CompareStraight( track_cp trk1, track_cp trk2 ) +{ + return TRUE; +} + + static trackCmd_t straightCmds = { "STRAIGHT", DrawStraight, @@ -704,8 +731,13 @@ static trackCmd_t straightCmds = { NULL, NULL, NULL, - MakeParallelStraight }; - + MakeParallelStraight, + NULL, + NULL, + NULL, + NULL, + NULL, + CompareStraight }; EXPORT void StraightSegProc( segProc_e cmd, @@ -810,6 +842,8 @@ EXPORT void StraightSegProc( */ + + track_p NewStraightTrack( coOrd p0, coOrd p1 ) { track_p t; diff --git a/app/bin/unittest/CMakeLists.txt b/app/bin/unittest/CMakeLists.txt index 32e2ddb..7055d0b 100644 --- a/app/bin/unittest/CMakeLists.txt +++ b/app/bin/unittest/CMakeLists.txt @@ -1,10 +1,10 @@ # build unit tests for the xtrkcad library -add_executable(dxfformattest +add_executable(dxfformattest dxfformattest.c ../dxfformat.c ) - + target_link_libraries(dxfformattest dynstring ${LIBS}) @@ -29,3 +29,35 @@ target_link_libraries(defaultstest ${LIBS}) add_test(DefaultsTest defaultstest) + +add_executable(shortentest + shortentest.c + ../shortentext.c + ) + +target_link_libraries(shortentest + ${LIBS}) + +add_test(ShortenTest shortentest) + +add_test(CatalogTest catalogtest) + +set (TESTXTP + "atl83ho.xtp" "atlasn.xtp" "HO-Peco-Code83.xtp" + ) + +foreach(testfile IN LISTS TESTXTP ) + configure_file ( ${CMAKE_CURRENT_SOURCE_DIR}/testfiles/${testfile} + ${CMAKE_CURRENT_BINARY_DIR} + COPYONLY ) +endforeach() + +add_executable(catalogtest + catalogtest.c + ../partcatalog.c + ../paths.c + ) + +target_link_libraries(catalogtest + dynstring + ${LIBS})
\ No newline at end of file diff --git a/app/bin/unittest/catalogtest.c b/app/bin/unittest/catalogtest.c new file mode 100644 index 0000000..6025990 --- /dev/null +++ b/app/bin/unittest/catalogtest.c @@ -0,0 +1,124 @@ +/** \file catalog.c +* Unit tests for part catalog management +*/ + +#include <malloc.h> +#include <stdarg.h> +#include <stddef.h> +#include <string.h> +#include <stdio.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "../include/partcatalog.h" + +TrackLibrary *trackLib; +CatalogEntry *catalog; + + +// some dummy functions to suppress linking problems +wGetUserHomeDir() +{ + +} + +wPrefSetString() +{ + +} + +wPrefGetString() +{ + +} + +AbortProg() +{ + +} + +static void +CreateLib(void **state) +{ + (void)state; + trackLib = CreateLibrary("."); + assert_non_null((void *)trackLib); +} + +static void ScanEmptyDir(void **state) +{ + (void)state; + bool success; + EmptyCatalog(trackLib->catalog); + success = GetTrackFiles(trackLib, "//"); + assert_false(success); +} + +static void ScanTestFiles(void **state) +{ + (void)state; + bool success; + EmptyCatalog(trackLib->catalog); + success = GetTrackFiles(trackLib, "."); + assert_true(success); +} + +static void CreateIndex(void **state) +{ + (void)state; + unsigned int words = CreateLibraryIndex(trackLib); + assert_true(words > 0); +} + +static void SearchNothing(void **state) +{ + (void)state; + unsigned int files = SearchLibrary(trackLib, "djfhdkljhf", catalog); + assert_true(files == 0); + EmptyCatalog(catalog); +} + +static void SearchSingle(void **state) +{ + (void)state; + int files = SearchLibrary(trackLib, "peco", catalog ); + assert_true(files==1); + EmptyCatalog(catalog); +} + +static void SearchMultiple(void **state) +{ + (void)state; + int files = SearchLibrary(trackLib, "atlas", catalog); + assert_true(files == 2); + EmptyCatalog(catalog); +} + +static void FilterTest(void **state) +{ + (void)state; + bool res; + + res = FilterKeyword("test"); + assert_false(res); + assert_true(FilterKeyword("&")); + assert_false(FilterKeyword("n")); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(CreateLib), + cmocka_unit_test(ScanEmptyDir), + cmocka_unit_test(ScanTestFiles), + cmocka_unit_test(CreateIndex), + cmocka_unit_test(SearchNothing), + cmocka_unit_test(SearchSingle), + cmocka_unit_test(SearchMultiple), + cmocka_unit_test(FilterTest), + }; + + catalog = InitCatalog(); + + return cmocka_run_group_tests(tests, NULL, NULL); +}
\ No newline at end of file diff --git a/app/bin/unittest/defaultstest.c b/app/bin/unittest/defaultstest.c index d877f46..e930468 100644 --- a/app/bin/unittest/defaultstest.c +++ b/app/bin/unittest/defaultstest.c @@ -56,11 +56,17 @@ wPrefGetFloatBasic(const char *section, const char *name, double *result, double return(TRUE); } -const char * wPrefGetStringBasic(const char *section, const char *name) +char * wPrefGetStringBasic(const char *section, const char *name) { return(NULL); } +/* dummy to make the linker happy */ +void +wPrefSetInteger(const char *section, const char *name, long value) +{ + return; +} static void BinarySearch(void **state) { int result; diff --git a/app/bin/unittest/pathstest.c b/app/bin/unittest/pathstest.c index b7e792e..3ee830e 100644 --- a/app/bin/unittest/pathstest.c +++ b/app/bin/unittest/pathstest.c @@ -41,6 +41,11 @@ char *wPrefGetStringExt(const char *section, const char *key) return(NULL); } +char *wPrefGetString(const char *section, const char *key) +{ + return(DEFAULTPATH); +} + const char *wGetUserHomeDir(void) { return(DEFAULTPATH); diff --git a/app/bin/unittest/shortentest.c b/app/bin/unittest/shortentest.c new file mode 100644 index 0000000..4d24b84 --- /dev/null +++ b/app/bin/unittest/shortentest.c @@ -0,0 +1,152 @@ +/** \file stringutiltest.c +* Unit tests for the dxfformat module +*/ + +#include <malloc.h> +#include <stdarg.h> +#include <stddef.h> +#include <string.h> +#include <stdio.h> +#include <setjmp.h> +#include <cmocka.h> + +#include <shortentext.h> + +#define RESULTSTRING "This is a test!" + + +static void NoRemoveBlanks(void **state) +{ + char *result = malloc(strlen(RESULTSTRING) + 20); + (void)state; + + RemoveFormatChars(RESULTSTRING, result); + assert_string_equal(result, RESULTSTRING); + + RemoveFormatChars(" " RESULTSTRING, result); + assert_string_equal(result, " " RESULTSTRING); +} + +static void RemoveMultipleBlanks(void **state) +{ + char *result = malloc(strlen(RESULTSTRING) + 20); + (void)state; + + RemoveFormatChars("This is a test!", result); + assert_string_equal(result, RESULTSTRING); + + RemoveFormatChars("This is a test!", result); + assert_string_equal(result, RESULTSTRING); +} + + +static void RemoveTabs(void **state) +{ + char *result = malloc(strlen(RESULTSTRING) + 20); + (void)state; + + RemoveFormatChars("This \tis\ta test!", result); + assert_string_equal(result, RESULTSTRING); + + RemoveFormatChars("This\t\tis a\ttest!", result); + assert_string_equal(result, RESULTSTRING); + + RemoveFormatChars("\tThis is a test!", result); + assert_string_equal(result, " " RESULTSTRING); +} + +static void RemoveCRs(void **state) +{ + char *result = malloc(strlen(RESULTSTRING) + 20); + (void)state; + + RemoveFormatChars("This\r is a test!", result); + assert_string_equal(result, RESULTSTRING); + + RemoveFormatChars("This\ris a test!", result); + assert_string_equal(result, RESULTSTRING); + + RemoveFormatChars("This is a test!\r", result); + assert_string_equal(result, RESULTSTRING); +} + +static void RemoveLFs(void **state) +{ + char *result = malloc(strlen(RESULTSTRING) + 20); + (void)state; + + RemoveFormatChars("This\n is a test!", result); + assert_string_equal(result, RESULTSTRING); + + RemoveFormatChars("This\nis a test!", result); + assert_string_equal(result, RESULTSTRING); + + RemoveFormatChars("\nThis is a test!", result); + assert_string_equal(result, " " RESULTSTRING); + + RemoveFormatChars("This is a test!\r\n", result); + assert_string_equal(result, RESULTSTRING); +} + +#define LONGSTRING "The strrchr() function in C/C++ locates the last occurrence of a character in a string. It returns a pointer to the last occurrence in the string. The terminating null character is considered part of the C string. ... str : specifies the pointer to the null terminated string to be searched for." + +static void NoEllipsizeText(void **state) +{ + char *result = malloc(strlen(RESULTSTRING) + 20); + + (void)state; + + EllipsizeString(RESULTSTRING, result, strlen(RESULTSTRING) + 10); + assert_string_equal(result, RESULTSTRING); + + EllipsizeString(RESULTSTRING, NULL, strlen(RESULTSTRING) + 10); + assert_string_equal(result, RESULTSTRING); +} + +static void EllipsizeText(void **state) +{ + char *result = malloc(sizeof(LONGSTRING)); + (void)state; + + EllipsizeString(LONGSTRING, result, strlen(LONGSTRING)); + assert_string_equal(result, LONGSTRING); + + EllipsizeString(LONGSTRING, result, 40); + assert_string_equal(result, "The strrchr() function in C/C++..."); + + EllipsizeString(LONGSTRING, result, 23); + assert_string_equal(result, "The strrchr()..."); + + strcpy(result, LONGSTRING); + EllipsizeString(result, NULL, 23); + assert_string_equal(result, "The strrchr()..."); + +} + +static void LongText(void **state) +{ + char *result = malloc(sizeof(LONGSTRING)); + (void)state; + + strcpy(result, "abcdefghijklmnopqrstuvwxyz"); + EllipsizeString(result, NULL, 23); + assert_string_equal(result, "abcdefghijklmnopqrst..."); + + EllipsizeString("abcdefghijklmnopqrstuvwxyz", result, 10); + assert_string_equal(result, "abcdefg..."); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(NoRemoveBlanks), + cmocka_unit_test(RemoveMultipleBlanks), + cmocka_unit_test(RemoveTabs), + cmocka_unit_test(RemoveCRs), + cmocka_unit_test(RemoveLFs), + cmocka_unit_test(NoEllipsizeText), + cmocka_unit_test(EllipsizeText), + cmocka_unit_test(LongText), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +}
\ No newline at end of file diff --git a/app/bin/unittest/testfiles/HO-Peco-Code83.xtp b/app/bin/unittest/testfiles/HO-Peco-Code83.xtp new file mode 100644 index 0000000..09bf426 --- /dev/null +++ b/app/bin/unittest/testfiles/HO-Peco-Code83.xtp @@ -0,0 +1,236 @@ +CONTENTS Peco North American Code 83 HO Scale Turnouts +#Updated based on PECO Website and PDF's +SUBCONTENTS Peco Code 83 HO Turnouts +TURNOUT HO "PECO Code 83 #5 Left Hand Turnout SL-8352/SLE-8352" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.000000 270.000000 + E 8.259842 0.000000 90.000000 + E 8.259842 1.000000 78.600000 + S 0 0.000000 0.000000 0.000000 0.649600 0.000000 + S 0 0.000000 0.649600 0.000000 8.259842 0.000000 + C 0 0.000000 -26.558075 0.649635 26.558075 168.599924 11.400152 + S 0 0.000000 5.899063 0.523973 8.259842 1.000000 +END +TURNOUT HO "PECO Code 83 #5 Right Hand Turnout SL-8351/SLE-8351" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.000000 270.000000 + E 8.259842 0.000000 90.000000 + E 8.259842 -1.000000 101.400000 + S 0 0.000000 0.000000 0.000000 0.649600 0.000000 + S 0 0.000000 0.649600 0.000000 8.259842 0.000000 + C 0 0.000000 26.558075 0.649494 -26.558075 0.000076 11.400152 + S 0 0.000000 5.899063 -0.523973 8.259842 -1.000000 +END +TURNOUT HO "PECO Code 83 #6 Left Hand Turnout SL-8362/SLE-8362" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.000000 270.000000 + E 9.192913 0.000000 90.000000 + E 9.192913 1.000000 80.500000 + S 0 0.000000 0.000000 0.000000 0.649600 0.000000 + S 0 0.000000 0.649600 0.000000 9.192913 0.000000 + C 0 0.000000 -30.900336 0.649641 30.900336 170.499924 9.500152 + S 0 0.000000 5.749703 0.423792 9.192913 1.000000 +END +TURNOUT HO "PECO Code 83 #6 Right Hand Turnout SL-8361/SLE-8361" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.000000 270.000000 + E 9.192913 0.000000 90.000000 + E 9.192913 -1.000000 99.500000 + S 0 0.000000 0.000000 0.000000 0.649600 0.000000 + S 0 0.000000 0.649600 0.000000 9.192913 0.000000 + C 0 0.000000 30.900336 0.649477 -30.900336 0.000076 9.500152 + S 0 0.000000 5.749703 -0.423792 9.192913 -1.000000 +END +TURNOUT HO "PECO Code 83 #8 Left Hand Turnout SL-8382/SLE-8382" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.000000 270.000000 + E 12.649606 0.000000 90.000000 + E 12.649606 1.000000 82.850000 + S 0 0.000000 0.000000 0.000000 0.649600 0.000000 + S 0 0.000000 0.649600 0.000000 12.649606 0.000000 + C 0 0.000000 -64.478236 0.649686 64.478236 172.849924 7.150152 + S 0 0.000000 8.675202 0.501423 12.649606 1.000000 +END +TURNOUT HO "PECO Code 83 #8 Right Hand Turnout SL-8381/SLE-8381" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.000000 270.000000 + E 12.649606 0.000000 90.000000 + E 12.649606 -1.000000 97.150000 + S 0 0.000000 0.000000 0.000000 0.649600 0.000000 + S 0 0.000000 0.649600 0.000000 12.649606 0.000000 + C 0 0.000000 64.478236 0.649343 -64.478236 0.000076 7.150152 + S 0 0.000000 8.675202 -0.501423 12.649606 -1.000000 +END + +SUBCONTENTS Peco Code 83 HO Wye Turnouts +TURNOUT HO "PECO Code 83 #4 WYE Turnout SL-8348/SLE-8348" + P "Left" 1 2 3 + P "Right" 1 4 5 + E 0.000000 0.000000 270.000000 + E 7.159055 0.500000 82.850000 + E 7.159055 -0.500000 97.150000 + S 0 0.000000 0.000000 0.000000 0.649600 0.000000 + C 0 0.000000 -40.393551 0.649654 40.393551 172.849924 7.150152 + S 0 0.000000 5.677382 0.314125 7.159055 0.500000 + C 0 0.000000 40.393551 0.649439 -40.393551 0.000076 7.150152 + S 0 0.000000 5.677382 -0.314125 7.159055 -0.500000 +END + +SUBCONTENTS Peco Code 83 HO Crossings +TURNOUT HO "PECO Code 83 #6 Diamond Crossing SL-8364/SLE-8364" + P "Normal" 1 0 2 + E 0.000000 0.000000 270.000000 + E 12.035433 0.000000 90.000000 + E 0.082528 0.993201 279.500000 + E 11.952905 -0.993201 99.500000 + S 0 0.000000 0.000000 0.000000 12.035433 0.000000 + S 0 0.000000 0.082528 0.993201 11.952905 -0.993201 +END +TURNOUT HO "PECO Code 83 90d Crossing SL-8390" + P "Normal" 1 0 2 + E 0.000000 0.000000 270.000000 + E 2.000000 0.000000 90.000000 + E 1.000000 -1.000000 180.000000 + E 1.000000 1.000000 0.000000 + S 0 0.000000 0.000000 0.000000 2.000000 0.000000 + S 0 0.000000 1.000000 1.000000 1.000000 -1.000000 +END + +SUBCONTENTS Peco Code 83 HO Curved Turnouts +TURNOUT HO "Peco Code 83 #7 Curved Left Turnout SL-8377/SLE-8377" + P "Normal" 1 4 5 + P "Reverse" 1 2 3 + E 0.000000 0.000000 270.000000 + E 11.106693 0.984252 81.000000 + E 10.865748 1.968504 72.000000 + S 0 0.000000 0.000000 0.000000 0.649600 0.000000 + C 0 0.000000 -26.251070 0.649635 26.251070 161.999924 18.000152 + S 0 0.000000 8.761686 1.284838 10.865748 1.968504 + C 0 0.000000 -53.910688 0.649672 53.910688 170.999924 9.000152 + S 0 0.000000 9.083224 0.663751 11.106693 0.984252 +END +TURNOUT HO "Peco Code 83 #7 Curved Right Turnout SL-8376/SLE-8376" + P "Normal" 1 4 5 + P "Reverse" 1 2 3 + E 0.000000 0.000000 270.000000 + E 11.106693 -0.984252 99.000000 + E 10.865748 -1.968504 108.000000 + S 0 0.000000 0.000000 0.000000 0.649600 0.000000 + C 0 0.000000 26.251070 0.649496 -26.251070 0.000076 18.000152 + S 0 0.000000 8.761686 -1.284838 10.865748 -1.968504 + C 0 0.000000 53.910688 0.649385 -53.910688 0.000076 9.000152 + S 0 0.000000 9.083224 -0.663751 11.106693 -0.984252 +END + +SUBCONTENTS Peco Code 83 HO Slip Turnouts +TURNOUT HO "Peco Code 83 #6 Double Slip Switch SL-U8363" + P "Normal" 1 2 3 0 4 5 6 + P "Reverse" 1 7 6 0 4 8 3 + E 0.000000 0.000000 270.000000 + E 12.035433 0.000000 90.000000 + E 0.082528 0.993201 279.500000 + E 11.952905 -0.993201 99.500000 + S 0 0.000000 0.000000 0.000000 1.299468 0.000000 + S 0 0.000000 1.299468 0.000000 10.735965 0.000000 + S 0 0.000000 10.735965 0.000000 12.035433 0.000000 + S 0 0.000000 0.082528 0.993201 1.363761 0.778782 + S 0 0.000000 1.363761 0.778782 10.671672 -0.778782 + S 0 0.000000 10.671672 -0.778782 11.952905 -0.993201 + C 0 0.000000 56.784006 1.299241 -56.784006 0.000076 9.500152 + C 0 0.000000 -56.784006 10.736040 56.783993 180.000076 9.500152 +END + +SUBCONTENTS Peco Code 83 HO Inspection Pit +TURNOUT HO "Peco Code 83 Inspection Pit SL-8356" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 11.692913 0.000000 90.000000 + S 0 0.000000 0.000000 0.000000 11.692913 0.000000 + F 15720651 0.000000 4 + 0.000000 0.551091 0 + 11.692913 0.551091 0 + 11.692913 -0.551091 0 + 0.000000 -0.551091 0 + F 12632256 0.000000 4 + 0.000000 0.280000 0 + 0.560000 0.280000 0 + 0.560000 -0.280000 0 + 0.000000 -0.280000 0 + L 0 0.020000 0.000000 0.280000 0.000000 -0.280000 + L 0 0.020000 0.112000 0.280000 0.112000 -0.280000 + L 0 0.020000 0.224000 0.280000 0.224000 -0.280000 + L 0 0.020000 0.336000 0.280000 0.336000 -0.280000 + L 0 0.020000 0.448000 0.280000 0.448000 -0.280000 + L 0 0.020000 0.560000 0.280000 0.560000 -0.280000 + F 12632256 0.000000 4 + 11.132913 0.280000 0 + 11.692913 0.280000 0 + 11.692913 -0.280000 0 + 11.132913 -0.280000 0 + L 0 0.020000 11.132913 0.280000 11.132913 -0.280000 + L 0 0.020000 11.244913 0.280000 11.244913 -0.280000 + L 0 0.020000 11.356913 0.280000 11.356913 -0.280000 + L 0 0.020000 11.468913 0.280000 11.468913 -0.280000 + L 0 0.020000 11.580913 0.280000 11.580913 -0.280000 + L 0 0.020000 11.692913 0.280000 11.692913 -0.280000 + A 0 0.020000 0.062500 0.974409 0.000000 0.000000 360.000000 + A 0 0.020000 0.062500 2.923228 0.000000 0.000000 360.000000 + A 0 0.020000 0.062500 4.872047 0.000000 0.000000 360.000000 + A 0 0.020000 0.062500 6.820866 0.000000 0.000000 360.000000 + A 0 0.020000 0.062500 8.769685 0.000000 0.000000 360.000000 + A 0 0.020000 0.062500 10.718504 0.000000 0.000000 360.000000 + L 0 0.020000 0.000000 0.551091 11.692913 0.551091 + L 0 0.020000 0.000000 0.280000 11.692913 0.28000 + L 0 0.020000 0.000000 -0.280000 11.692913 -0.280000 + L 0 0.020000 0.000000 -0.551091 11.692913 -0.551091 +END +TURNOUT HO "Peco Code 83 Inspection Pit(Stair End) SL-8356A" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 1.948819 0.000000 90.000000 + S 0 0.000000 0.000000 0.000000 1.948819 0.000000 + F 15720651 0.000000 4 + 0.000000 0.551091 0 + 1.948819 0.551091 0 + 1.948819 -0.551091 0 + 0.000000 -0.551091 0 + F 12632256 0.000000 4 + 0.000000 0.280000 0 + 0.560000 0.280000 0 + 0.560000 -0.280000 0 + 0.000000 -0.280000 0 + L 0 0.020000 0.000000 0.280000 0.000000 -0.280000 + L 0 0.020000 0.112000 0.280000 0.112000 -0.280000 + L 0 0.020000 0.224000 0.280000 0.224000 -0.280000 + L 0 0.020000 0.336000 0.280000 0.336000 -0.280000 + L 0 0.020000 0.448000 0.280000 0.448000 -0.280000 + L 0 0.020000 0.560000 0.280000 0.560000 -0.280000 + A 0 0.020000 0.062500 0.974409 0.000000 0.000000 360.000000 + L 0 0.020000 0.000000 0.551091 1.948819 0.551091 + L 0 0.020000 0.000000 0.280000 1.948819 0.28000 + L 0 0.020000 0.000000 -0.280000 1.948819 -0.280000 + L 0 0.020000 0.000000 -0.551091 1.948819 -0.551091 + +END +TURNOUT HO "Peco Code 83 Inspection Pit(Mid Section) SL-8356B" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 1.948819 0.000000 90.000000 + S 0 0.000000 0.000000 0.000000 1.948819 0.000000 + F 15720651 0.000000 4 + 0.000000 0.551091 0 + 1.948819 0.551091 0 + 1.948819 -0.551091 0 + 0.000000 -0.551091 0 + A 0 0.020000 0.062500 0.974409 0.000000 0.000000 360.000000 + L 0 0.020000 0.000000 0.551091 1.948819 0.551091 + L 0 0.020000 0.000000 0.280000 1.948819 0.28000 + L 0 0.020000 0.000000 -0.280000 1.948819 -0.280000 + L 0 0.020000 0.000000 -0.551091 1.948819 -0.551091 +END
\ No newline at end of file diff --git a/app/bin/unittest/testfiles/atl83ho.xtp b/app/bin/unittest/testfiles/atl83ho.xtp new file mode 100644 index 0000000..02452da --- /dev/null +++ b/app/bin/unittest/testfiles/atl83ho.xtp @@ -0,0 +1,561 @@ +CONTENTS Atlas Code 83 - HO Scale
+SUBCONTENTS Atlas HO-Scale C83 - Switched
+TURNOUT HO "Atlas #6 Left C83 Super 505"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 11.613000 0.000000 90.000000
+ E 11.613000 1.335000 80.500000
+ S 0 0.000000 0.000000 0.000000 0.649600 0.000000
+ S 0 0.000000 0.649600 0.000000 11.613000 0.000000
+ C 0 0.000000 -35.933506 0.649648 35.933506 170.499924 9.500152
+ S 0 0.000000 6.580428 0.492821 11.613000 1.335000
+ END
+TURNOUT HO "Atlas #6 Right C83 Super 506"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 11.613000 0.000000 90.000000
+ E 11.613000 -1.335000 99.500000
+ S 0 0.000000 0.000000 0.000000 0.649600 0.000000
+ S 0 0.000000 0.649600 0.000000 11.613000 0.000000
+ C 0 0.000000 35.933506 0.649457 -35.933506 0.000076 9.500152
+ S 0 0.000000 6.580428 -0.492821 11.613000 -1.335000
+ END
+TURNOUT HO "Atlas C83 Left Remote/Manual Snap Switch 540/542"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ E 7.656300 1.085500 70.000000
+ S 0 0.000000 0.000000 0.000000 2.149777 0.000000
+ S 0 0.000000 2.149777 0.000000 9.000000 0.000000
+ C 0 0.000000 -14.315249 2.149796 14.315249 159.999924 20.000152
+ S 0 0.000000 7.045913 0.863327 7.656300 1.085500
+ END
+TURNOUT HO "Atlas C83 Right Remote/Manual Snap Switch 541/543"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ E 7.656300 -1.085500 110.000000
+ S 0 0.000000 0.000000 0.000000 2.149777 0.000000
+ S 0 0.000000 2.149777 0.000000 9.000000 0.000000
+ C 0 0.000000 14.315249 2.149720 -14.315249 0.000076 20.000152
+ S 0 0.000000 7.045913 -0.863327 7.656300 -1.085500
+ END
+TURNOUT HO "Atlas C83 Left Remote/Manual 22in Snap Switch 546/544"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 10.500000 0.000000 90.000000
+ E 10.500000 1.674650 67.500000
+ S 0 0.000000 0.000000 0.000000 2.730631 0.000000
+ S 0 0.000000 2.730631 0.000000 10.500000 0.000000
+ C 0 0.000000 -18.734087 2.730656 18.734087 157.500000 22.500000
+ S 0 0.000000 9.899897 1.451414 10.500000 1.674650
+ END
+TURNOUT HO "Atlas C83 Right Remote/Manual 22in Snap Switch 547/545"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 10.500000 0.000000 90.000000
+ E 10.500000 -1.674650 112.500000
+ S 0 0.000000 0.000000 0.000000 2.730631 0.000000
+ S 0 0.000000 2.730631 0.000000 10.500000 0.000000
+ C 0 0.000000 18.734087 2.730656 -18.734087 0.000000 22.500000
+ S 0 0.000000 9.899897 -1.451414 10.500000 -1.674650
+ END
+TURNOUT HO "Atlas C83 Wye Switch 560"
+ P "Left" 1 2 3
+ P "Right" 1 4 5
+ E 0.000000 0.000000 270.000000
+ E 7.805900 0.655500 81.000000
+ E 7.805900 -0.655500 99.000000
+ S 0 0.000000 0.000000 0.000000 0.649600 0.000000
+ C 0 0.000000 -38.343529 0.649651 38.343529 170.999924 9.000152
+ S 0 0.000000 6.647945 0.472088 7.805900 0.655500
+ C 0 0.000000 38.343529 0.649447 -38.343529 0.000076 9.000152
+ S 0 0.000000 6.647945 -0.472088 7.805900 -0.655500
+ END
+TURNOUT HO "Atlas #6 Left C83 Customline 563"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 12.000000 0.000000 90.000000
+ E 9.999000 0.874800 81.000000
+ S 0 0.000000 0.000000 0.000000 0.649600 0.000000
+ S 0 0.000000 0.649600 0.000000 12.000000 0.000000
+ C 0 0.000000 -48.616653 0.649665 48.616653 170.999924 9.000152
+ S 0 0.000000 8.255041 0.598571 9.999000 0.874800
+ END
+TURNOUT HO "Atlas #6 Right C83 Customline 564"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 12.000000 0.000000 90.000000
+ E 9.999000 -0.874800 99.000000
+ S 0 0.000000 0.000000 0.000000 0.649600 0.000000
+ S 0 0.000000 0.649600 0.000000 12.000000 0.000000
+ C 0 0.000000 48.616653 0.649406 -48.616653 0.000076 9.000152
+ S 0 0.000000 8.255041 -0.598571 9.999000 -0.874800
+ END
+TURNOUT HO "Atlas #4 Left C83 Customline 561"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ E 8.000000 1.000000 75.522476
+ S 0 0.000000 0.000000 0.000000 0.776702 0.000000
+ S 0 0.000000 0.776702 0.000000 9.000000 0.000000
+ C 0 0.000000 -26.377309 0.776737 26.377309 165.522400 14.477676
+ S 0 0.000000 7.371097 0.837607 8.000000 1.000000
+ END
+TURNOUT HO "Atlas #4 Right C83 Customline 562"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ E 8.000000 -1.000000 104.477524
+ S 0 0.000000 0.000000 0.000000 0.776702 0.000000
+ S 0 0.000000 0.776702 0.000000 9.000000 0.000000
+ C 0 0.000000 26.377309 0.776597 -26.377309 0.000076 14.477676
+ S 0 0.000000 7.371097 -0.837607 8.000000 -1.000000
+ END
+TURNOUT HO "Atlas #8 Left C83 Customline 565"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 13.500000 0.000000 90.000000
+ E 11.400000 0.900000 82.819238
+ S 0 0.000000 0.000000 0.000000 0.649600 0.000000
+ S 0 0.000000 0.649600 0.000000 13.500000 0.000000
+ C 0 0.000000 -57.485559 0.649676 57.485559 172.819162 7.180914
+ S 0 0.000000 7.835446 0.450893 11.400000 0.900000
+ END
+TURNOUT HO "Atlas #8 Right C83 Customline 566"
+ P "Normal" 1 2
+ P "Reverse" 1 3 4
+ E 0.000000 0.000000 270.000000
+ E 13.500000 0.000000 90.000000
+ E 11.400000 -0.900000 97.180762
+ S 0 0.000000 0.000000 0.000000 0.649600 0.000000
+ S 0 0.000000 0.649600 0.000000 13.500000 0.000000
+ C 0 0.000000 57.485559 0.649371 -57.485559 0.000076 7.180914
+ S 0 0.000000 7.835446 -0.450893 11.400000 -0.900000
+ END
+TURNOUT HO "Atlas Curve Left C83 Customline 595"
+ P "Normal" 1
+ P "Reverse" 2
+ E 0.000000 0.000000 270.000000
+ E 15.000000 4.019238 60.000000
+ E 13.392751 4.546227 52.500000
+ C 0 0.000000 30.000000 0.000000 30.000000 150.000000 30.000000
+ C 0 0.000000 22.000000 0.000000 22.000000 142.500000 37.500000
+ END
+TURNOUT HO "Atlas Curve Right C83 Customline 596"
+ P "Normal" 1
+ P "Reverse" 2
+ E 0.000000 0.000000 270.000000
+ E 15.000000 -4.019238 120.000000
+ E 13.392751 -4.546227 127.500000
+ C 0 0.000000 30.000000 0.000000 -30.000000 0.000000 30.000000
+ C 0 0.000000 22.000000 0.000000 -22.000000 0.000000 37.500000
+ END
+
+
+
+SUBCONTENTS Atlas HO-Scale C83 - Crossings
+TURNOUT HO "Atlas 12.5D Crossing 571"
+ P "Normal" 1 0 2
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ E 0.106666 0.973972 282.500000
+ E 8.893334 -0.973972 102.500000
+ S 0 0.000000 0.000000 0.000000 9.000000 0.000000
+ S 0 0.000000 0.106666 0.973972 8.893334 -0.973972
+ END
+TURNOUT HO "Atlas 571 Fitter Piece 571a"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 0.100000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 0.100000 0.000000
+ END
+TURNOUT HO "Atlas 19D Crossing 572"
+ P "Normal" 1 0 2
+ E 0.000000 0.000000 270.000000
+ E 6.082500 0.000000 90.000000
+ E 0.165690 0.990130 289.000000
+ E 5.916810 -0.990130 109.000000
+ S 0 0.000000 0.000000 0.000000 6.082500 0.000000
+ S 0 0.000000 0.165690 0.990130 5.916810 -0.990130
+ END
+TURNOUT HO "Atlas 30D Crossing 573"
+ P "Normal" 1 0 2
+ E 0.000000 0.000000 270.000000
+ E 4.000000 0.000000 90.000000
+ E 0.267947 0.999997 300.000000
+ E 3.732053 -0.999997 120.000000
+ S 0 0.000000 0.000000 0.000000 4.000000 0.000000
+ S 0 0.000000 0.267947 0.999997 3.732053 -0.999997
+ END
+TURNOUT HO "Atlas 25D Crossing 574"
+ P "Normal" 1 0 2
+ E 0.000000 0.000000 270.000000
+ E 4.608100 0.000000 90.000000
+ E 0.215870 0.973730 295.000000
+ E 4.392230 -0.973730 115.000000
+ S 0 0.000000 0.000000 0.000000 4.608100 0.000000
+ S 0 0.000000 0.215870 0.973730 4.392230 -0.973730
+ END
+TURNOUT HO "Atlas 45D Crossing 575"
+ P "Normal" 1 0 2
+ E 0.000000 0.000000 270.000000
+ E 3.000000 0.000000 90.000000
+ E 0.439338 1.060658 315.000000
+ E 2.560662 -1.060658 135.000000
+ S 0 0.000000 0.000000 0.000000 3.000000 0.000000
+ S 0 0.000000 0.439338 1.060658 2.560662 -1.060658
+ END
+TURNOUT HO "Atlas 60D Crossing 576"
+ P "Normal" 1 0 2
+ E 0.000000 0.000000 270.000000
+ E 3.000000 0.000000 90.000000
+ E 0.749997 1.299036 330.000000
+ E 2.250003 -1.299036 150.000000
+ S 0 0.000000 0.000000 0.000000 3.000000 0.000000
+ S 0 0.000000 0.749997 1.299036 2.250003 -1.299036
+ END
+TURNOUT HO "Atlas 90D Crossing 577"
+ P "Normal" 1 0 2
+ E 0.000000 0.000000 270.000000
+ E 2.000000 0.000000 90.000000
+ E 1.000003 1.000000 360.000000
+ E 0.999997 -1.000000 180.000000
+ S 0 0.000000 0.000000 0.000000 2.000000 0.000000
+ S 0 0.000000 1.000003 1.000000 0.999997 -1.000000
+ END
+
+SUBCONTENTS Atlas HO-Scale C83 - Straight Track
+TURNOUT HO "Atlas C83 9"" Straight 510/520"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 9.000000 0.000000
+ END
+TURNOUT HO "Atlas C83 6"" Straight 521"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 6.000000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 6.000000 0.000000
+ END
+TURNOUT HO "Atlas C83 3"" Straight 522"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 3.000000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 3.000000 0.000000
+ END
+TURNOUT HO "Atlas C83 1.5"" Straight 523"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 1.500000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 1.500000 0.000000
+ END
+TURNOUT HO "Atlas C83 2.0"" Straight 525"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 2.000000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 2.000000 0.000000
+ END
+TURNOUT HO "Atlas C83 0.75"" Straight 524a"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 0.750000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 0.750000 0.000000
+ END
+TURNOUT HO "Atlas C83 1"" Straight 524b"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 1.000000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 1.000000 0.000000
+ END
+TURNOUT HO "Atlas C83 1.25"" Straight 524c"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 1.250000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 1.250000 0.000000
+ END
+TURNOUT HO "Atlas C83 1.5"" Straight 524d"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 1.500000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 1.500000 0.000000
+ END
+TURNOUT HO "Atlas C83 2.0"" Straight 524e"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 2.000000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 2.000000 0.000000
+ END
+TURNOUT HO "Atlas C83 2.5"" Straight 524f"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 2.500000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 2.500000 0.000000
+ END
+
+SUBCONTENTS Atlas HO-Scale C83 - Curve Track
+TURNOUT HO "Atlas C83 15"" 30D Curve 511/530"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 7.499994 -2.009616 120.000000
+ C 0 0 15.000000 0.000000 -15.000000 0.000000 30.000000
+ END
+TURNOUT HO "Atlas C83 15"" 15D Curve 531"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 3.882282 0.511112 75.000000
+ C 0 0 -15.000000 0.000020 15.000000 165.000076 15.000000
+ END
+TURNOUT HO "Atlas C83 18"" 30D Curve 512/532"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 8.999993 -2.411539 120.000000
+ C 0 0 18.000000 0.000000 -18.000000 0.000000 30.000000
+ END
+TURNOUT HO "Atlas C83 18"" 15D Curve 533"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 4.658739 -0.613334 105.000000
+ C 0 0 18.000000 0.000000 -18.000000 0.000000 15.000000
+ END
+TURNOUT HO "Atlas C83 18"" 10D Curve 534"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 3.125665 -0.273460 100.000000
+ C 0 0 18.000000 0.000000 -18.000000 0.000000 10.000000
+ END
+TURNOUT HO "Atlas C83 22"" 22.5D Curve 513/535"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 8.419029 -1.674647 112.500000
+ C 0 0 22.000000 0.000000 -22.000000 0.000000 22.500000
+ END
+TURNOUT HO "Atlas C83 22"" 7.5D Curve 537"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 2.871574 0.188213 82.500000
+ C 0 0.000000 -22.000000 0.000029 22.000000 172.500076 7.500000
+ END
+TURNOUT HO "Atlas C83 24"" 22.5D Curve 536"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 9.184395 1.826888 67.500000
+ C 0 0.000000 -24.000000 0.000032 24.000000 157.500076 22.500000
+ END
+
+
+SUBCONTENTS Atlas HO-Scale C83 - Misc Track
+TURNOUT HO "Atlas C83 9"" Straight Rerailer 519"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 9.000000 0.000000
+ F 12566463 0.000000 4
+ 0.500000 0.584375 0
+ 1.000000 0.334375 0
+ 8.000000 0.334375 0
+ 8.500000 0.584375 0
+ F 12566463 0.000000 6
+ 0.500000 0.000000 0
+ 1.000000 -0.250000 0
+ 8.000000 -0.250000 0
+ 8.500000 0.000000 0
+ 8.000000 0.250000 0
+ 1.000000 0.250000 0
+ F 12566463 0.000000 4
+ 0.500000 -0.584375 0
+ 1.000000 -0.334375 0
+ 8.000000 -0.334375 0
+ 8.500000 -0.584375 0
+ END
+TURNOUT HO "Atlas C83 Bumper 518"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ S 0 0 0.000000 0.000000 3.750000 0.000000
+ END
+
+SUBCONTENTS Atlas HO-Scale C83 - Bridges
+TURNOUT HO "Atlas C83 9"" Warren Truss Bridge 590"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 9.000000 0.000000
+ F 11579568 0.000000 4
+ 0.000000 1.200000 0
+ 9.000000 1.200000 0
+ 9.000000 0.625000 0
+ 0.000000 0.625000 0
+ F 11579568 0.000000 4
+ 0.000000 -1.200000 0
+ 9.000000 -1.200000 0
+ 9.000000 -0.625000 0
+ 0.000000 -0.625000 0
+ L 8421504 0.100000 0.000000 1.200000 9.000000 1.200000
+ L 8421504 0.100000 0.000000 -1.200000 9.000000 -1.200000
+END
+TURNOUT HO "Atlas C83 9"" Deck Truss Bridge 591"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 9.000000 0.000000
+ L 11579568 0.050000 0.000000 0.700000 9.000000 0.700000
+ L 11579568 0.050000 0.000000 0.600000 0.000000 0.700000
+ L 11579568 0.050000 9.000000 0.600000 9.000000 0.700000
+ L 11579568 0.050000 0.000000 -0.700000 9.000000 -0.700000
+ L 11579568 0.050000 0.000000 -0.600000 0.000000 -0.700000
+ L 11579568 0.050000 9.000000 -0.600000 9.000000 -0.700000
+END
+TURNOUT HO "Atlas C83 9"" Plate Girder Bridge 592"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 9.000000 0.000000
+ F 11579568 0.000000 4
+ 0.000000 1.300000 0
+ 9.000000 1.300000 0
+ 9.000000 0.625000 0
+ 0.000000 0.625000 0
+ F 11579568 0.000000 4
+ 0.000000 -1.300000 0
+ 9.000000 -1.300000 0
+ 9.000000 -0.625000 0
+ 0.000000 -0.625000 0
+ L 8421504 0.100000 0.000000 1.300000 9.000000 1.300000
+ L 8421504 0.050000 0.900000 1.000000 0.900000 1.300000
+ L 8421504 0.050000 2.700000 1.000000 2.700000 1.300000
+ L 8421504 0.050000 4.500000 1.000000 4.500000 1.300000
+ L 8421504 0.050000 6.300000 1.000000 6.300000 1.300000
+ L 8421504 0.050000 8.100000 1.000000 8.100000 1.300000
+ L 8421504 0.100000 0.000000 -1.300000 9.000000 -1.300000
+ L 8421504 0.050000 0.900000 -1.000000 0.900000 -1.300000
+ L 8421504 0.050000 2.700000 -1.000000 2.700000 -1.300000
+ L 8421504 0.050000 4.500000 -1.000000 4.500000 -1.300000
+ L 8421504 0.050000 6.300000 -1.000000 6.300000 -1.300000
+ L 8421504 0.050000 8.100000 -1.000000 8.100000 -1.300000
+END
+TURNOUT HO "Atlas C83 18"" Through Truss Bridge 593/594"
+ P "Normal" 1
+ E 0.000000 0.000000 270.000000
+ E 18.000000 0.000000 90.000000
+ S 0 0 0.000000 0.000000 18.000000 0.000000
+ L 11579568 0.050000 0.000000 1.200000 18.000000 1.200000
+ L 11579568 0.050000 0.000000 0.750000 0.000000 1.200000
+ L 11579568 0.050000 18.000000 0.750000 18.000000 1.200000
+ L 11579568 0.050000 0.000000 -1.200000 18.000000 -1.200000
+ L 11579568 0.050000 0.000000 -0.750000 0.000000 -1.200000
+ L 11579568 0.050000 18.000000 -0.750000 18.000000 -1.200000
+ L 11579568 0.050000 3.000000 -1.200000 3.000000 1.200000
+ L 11579568 0.050000 6.000000 -1.200000 6.000000 1.200000
+ L 11579568 0.050000 9.000000 -1.200000 9.000000 1.200000
+ L 11579568 0.050000 12.000000 -1.200000 12.000000 1.200000
+ L 11579568 0.050000 15.000000 -1.200000 15.000000 1.200000
+ L 11579568 0.050000 3.000000 -1.200000 6.000000 1.200000
+ L 11579568 0.050000 3.000000 1.200000 6.000000 -1.200000
+ L 11579568 0.050000 6.000000 -1.200000 9.000000 1.200000
+ L 11579568 0.050000 6.000000 1.200000 9.000000 -1.200000
+ L 11579568 0.050000 9.000000 -1.200000 12.000000 1.200000
+ L 11579568 0.050000 9.000000 1.200000 12.000000 -1.200000
+ L 11579568 0.050000 12.000000 -1.200000 15.000000 1.200000
+ L 11579568 0.050000 12.000000 1.200000 15.000000 -1.200000
+END
+TURNOUT HO "Atlas C83 9"" Single-Track Thru-Girder Bridge 70000027"
+ P "P0" 1
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ S 0 0.000000 0.000000 0.000000 9.000000 0.000000
+ L 11579568 0.100000 0.500000 1.000000 8.500000 1.000000
+ L 11579568 0.050000 0.750000 1.000000 0.750000 0.625000
+ L 11579568 0.050000 2.000000 1.000000 2.000000 0.625000
+ L 11579568 0.050000 3.250000 1.000000 3.250000 0.625000
+ L 11579568 0.050000 4.500000 1.000000 4.500000 0.625000
+ L 11579568 0.050000 5.750000 1.000000 5.750000 0.625000
+ L 11579568 0.050000 7.000000 1.000000 7.000000 0.625000
+ L 11579568 0.050000 8.250000 1.000000 8.250000 0.625000
+ L 11579568 0.100000 0.500000 -1.000000 8.500000 -1.000000
+ L 11579568 0.050000 0.750000 -1.000000 0.750000 -0.625000
+ L 11579568 0.050000 2.000000 -1.000000 2.000000 -0.625000
+ L 11579568 0.050000 3.250000 -1.000000 3.250000 -0.625000
+ L 11579568 0.050000 4.500000 -1.000000 4.500000 -0.625000
+ L 11579568 0.050000 5.750000 -1.000000 5.750000 -0.625000
+ L 11579568 0.050000 7.000000 -1.000000 7.000000 -0.625000
+ L 11579568 0.050000 8.250000 -1.000000 8.250000 -0.625000
+END
+TURNOUT HO "Atlas C83 9"" Double-Track Thru-Girder Bridge 70000028"
+ P "P0" 1 0 1
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ E 0.000000 -2.000000 270.000000
+ E 9.000000 -2.000000 90.000000
+ S 0 0.000000 0.000000 0.000000 9.000000 0.000000
+ S 0 0.000000 0.000000 -2.000000 9.000000 -2.000000
+ L 11579568 0.100000 0.500000 1.000000 8.500000 1.000000
+
+ L 11579568 0.050000 0.750000 1.000000 0.750000 0.625000
+ L 11579568 0.050000 2.000000 1.000000 2.000000 0.625000
+ L 11579568 0.050000 3.250000 1.000000 3.250000 0.625000
+ L 11579568 0.050000 4.500000 1.000000 4.500000 0.625000
+ L 11579568 0.050000 5.750000 1.000000 5.750000 0.625000
+ L 11579568 0.050000 7.000000 1.000000 7.000000 0.625000
+ L 11579568 0.050000 8.250000 1.000000 8.250000 0.625000
+
+ L 11579568 0.050000 0.750000 -1.000000 0.750000 -0.625000
+ L 11579568 0.050000 2.000000 -1.000000 2.000000 -0.625000
+ L 11579568 0.050000 3.250000 -1.000000 3.250000 -0.625000
+ L 11579568 0.050000 4.500000 -1.000000 4.500000 -0.625000
+ L 11579568 0.050000 5.750000 -1.000000 5.750000 -0.625000
+ L 11579568 0.050000 7.000000 -1.000000 7.000000 -0.625000
+ L 11579568 0.050000 8.250000 -1.000000 8.250000 -0.625000
+
+ L 11579568 0.100000 0.500000 -1.000000 8.500000 -1.000000
+
+ L 11579568 0.050000 0.750000 -1.000000 0.750000 -1.375000
+ L 11579568 0.050000 2.000000 -1.000000 2.000000 -1.375000
+ L 11579568 0.050000 3.250000 -1.000000 3.250000 -1.375000
+ L 11579568 0.050000 4.500000 -1.000000 4.500000 -1.375000
+ L 11579568 0.050000 5.750000 -1.000000 5.750000 -1.375000
+ L 11579568 0.050000 7.000000 -1.000000 7.000000 -1.375000
+ L 11579568 0.050000 8.250000 -1.000000 8.250000 -1.375000
+
+ L 11579568 0.050000 0.750000 -3.000000 0.750000 -2.625000
+ L 11579568 0.050000 2.000000 -3.000000 2.000000 -2.625000
+ L 11579568 0.050000 3.250000 -3.000000 3.250000 -2.625000
+ L 11579568 0.050000 4.500000 -3.000000 4.500000 -2.625000
+ L 11579568 0.050000 5.750000 -3.000000 5.750000 -2.625000
+ L 11579568 0.050000 7.000000 -3.000000 7.000000 -2.625000
+ L 11579568 0.050000 8.250000 -3.000000 8.250000 -2.625000
+
+ L 11579568 0.100000 0.500000 -3.000000 8.500000 -3.000000
+END
+TURNOUT HO "Atlas C83 9"" Single-Track Add-On Thru-Girder Bridge 70000029"
+ P "P0" 1
+ E 0.000000 0.000000 270.000000
+ E 9.000000 0.000000 90.000000
+ S 0 0.000000 0.000000 0.000000 9.000000 0.000000
+ #L 11579568 0.100000 0.500000 1.000000 8.500000 1.000000
+ L 11579568 0.050000 0.750000 1.000000 0.750000 0.625000
+ L 11579568 0.050000 2.000000 1.000000 2.000000 0.625000
+ L 11579568 0.050000 3.250000 1.000000 3.250000 0.625000
+ L 11579568 0.050000 4.500000 1.000000 4.500000 0.625000
+ L 11579568 0.050000 5.750000 1.000000 5.750000 0.625000
+ L 11579568 0.050000 7.000000 1.000000 7.000000 0.625000
+ L 11579568 0.050000 8.250000 1.000000 8.250000 0.625000
+ L 11579568 0.100000 0.500000 -1.000000 8.500000 -1.000000
+ L 11579568 0.050000 0.750000 -1.000000 0.750000 -0.625000
+ L 11579568 0.050000 2.000000 -1.000000 2.000000 -0.625000
+ L 11579568 0.050000 3.250000 -1.000000 3.250000 -0.625000
+ L 11579568 0.050000 4.500000 -1.000000 4.500000 -0.625000
+ L 11579568 0.050000 5.750000 -1.000000 5.750000 -0.625000
+ L 11579568 0.050000 7.000000 -1.000000 7.000000 -0.625000
+ L 11579568 0.050000 8.250000 -1.000000 8.250000 -0.625000
+END
diff --git a/app/bin/unittest/testfiles/atlasn.xtp b/app/bin/unittest/testfiles/atlasn.xtp new file mode 100644 index 0000000..882e94c --- /dev/null +++ b/app/bin/unittest/testfiles/atlasn.xtp @@ -0,0 +1,697 @@ +CONTENTS Atlas N-Scale Track +SUBCONTENTS Atlas N-Scale Track - Straight +TURNOUT N "Atlas 5in Straight 2501" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 4.910000 0.000000 90.000000 + S 0 0 0.000000 0.000000 4.910000 0.000000 + END +TURNOUT N "Atlas 2 1/2in Straight XXX" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 2.455000 0.000000 90.000000 + S 0 0 0.000000 0.000000 2.455000 0.000000 + END +TURNOUT N "Atlas 1 1/4in Straight XXX" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 1.227500 0.000000 90.000000 + S 0 0 0.000000 0.000000 1.227500 0.000000 + END +TURNOUT N "Atlas 5/8in Straight XXX" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 0.613750 0.000000 90.000000 + S 0 0 0.000000 0.000000 0.613750 0.000000 + END + +SUBCONTENTS Atlas N-Scale Track - Curved +TURNOUT N "Atlas 9 3/4R full section 2510" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 4.875000 -1.306000 120.000000 + C 0 0 9.750000 0.000000 -9.750000 0.000000 30.000000 + END +TURNOUT N "Atlas 9 3/4R half section 2511" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 2.523400 -0.332300 105.000000 + C 0 0 9.750000 0.000000 -9.750000 0.002000 15.000000 + END +TURNOUT N "Atlas 11R full section 2520" + P "Normal" 1 + E 0.000000 1.473718 270.000000 + E 5.499996 0.000000 120.000000 + C 0 0.000000 11.000000 0.000000 -9.526282 0.000000 30.000000 + END +TURNOUT N "Atlas 11R half section 2521" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 2.847000 -0.375000 105.000000 + C 0 0 11.000000 0.000000 -11.000000 0.000000 15.000000 + END +TURNOUT N "Atlas 19R section 2526" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 4.917000 -0.646414 105.000000 + C 0 0 19.000000 0.000000 -19.000000 0.000000 15.000000 + END + +SUBCONTENTS Atlas N-Scale Track - Bumper / Rerailer +# Bumper Track Design by Bob Blackwell +TURNOUT N "Atlas Bumper Track 2536" + P "Normal" 1 + E 0.000000 0.312508 270.000000 + S 0 0.000000 0.000000 0.312508 2.750000 0.312512 + F3 0 0.000000 4 + 1.625000 0.625000 0 + 2.500000 0.625000 0 + 2.500000 0.000000 0 + 1.625000 0.000000 0 + END +# Rerailer Track Design by Bob Blackwell +TURNOUT N "Atlas Rerailer Track 2532" + P "NORMAL" 1 + E 0.000000 0.479167 270.000000 + E 4.906250 0.479167 90.000000 + S 0 0.000000 0.000000 0.479167 4.906250 0.479167 + L3 0 0.020833 1.609375 0.729169 0 3.292817 0.729169 0 + L3 0 0.020833 1.000000 0.947917 0 3.914062 0.947917 0 + L3 0 0.020833 3.906248 0.010417 0 3.906249 0.947917 0 + L3 0 0.020833 3.914062 0.010417 0 1.000000 0.010417 0 + L3 0 0.020833 0.999998 0.947917 0 1.000001 0.010417 0 + L3 0 0.020833 1.609375 0.229170 0 3.296875 0.229172 0 + L3 0 0.020833 1.609375 0.229167 0 1.000000 0.010417 0 + L3 0 0.020833 3.296875 0.229167 0 3.906250 0.010417 0 + L3 0 0.020833 1.609375 0.729167 0 1.000000 0.947917 0 + L3 0 0.020833 3.296875 0.729167 0 3.906250 0.947917 0 + L3 0 0.020833 1.734375 0.604167 0 3.171875 0.604169 0 + L3 0 0.020833 1.734375 0.354167 0 3.171875 0.354169 0 + L3 0 0.020833 1.312500 0.479167 0 1.734375 0.604167 0 + L3 0 0.020833 1.312500 0.479167 0 1.734375 0.354167 0 + L3 0 0.020833 3.171875 0.604167 0 3.593750 0.479167 0 + L3 0 0.020833 3.171875 0.354167 0 3.593750 0.479167 0 + END + +SUBCONTENTS Atlas N-Scale Track - Bridges +# Warren Truss Bridge Design by Bob Blackwell +TURNOUT N "Atlas Warren Truss Bridge 2546" + P "Normal" 1 + E 0.010420 0.744792 269.999848 + E 4.916670 0.744798 89.999848 + S 0 0.000000 0.010420 0.744792 4.916670 0.744798 + L3 0 0.020833 0.010420 0.010417 0 4.916670 0.010417 0 + L3 0 0.020833 4.916665 0.010417 0 4.916669 1.479167 0 + L3 0 0.020833 4.916670 1.479167 0 0.010420 1.479167 0 + L3 0 0.020833 0.010419 1.479167 0 0.010417 0.010417 0 + L3 0 0.020833 4.916670 0.104167 0 0.010420 0.104167 0 + L3 0 0.020833 4.916670 1.385417 0 0.010420 1.385417 0 + L3 0 0.020833 0.713545 1.479167 0 0.713545 1.385417 0 + L3 0 0.020833 4.197920 1.479167 0 4.197920 1.385417 0 + L3 0 0.020833 0.713545 0.104167 0 0.713545 0.010417 0 + L3 0 0.020833 4.197920 0.104167 0 4.197920 0.010417 0 + END +# Deck Truss Bridge Design by Bob Blackwell +TURNOUT N "Atlas Deck Truss Bridge 2547" + P "Normal" 1 + E 0.010418 0.385417 269.999854 + E 4.916668 0.385423 89.999854 + S 0 0.000000 0.010418 0.385417 4.916668 0.385423 + L3 0 0.020833 0.010418 0.010417 0 4.916668 0.010417 0 + L3 0 0.020833 4.916667 0.010417 0 4.916668 0.760417 0 + L3 0 0.020833 4.916668 0.760417 0 0.010418 0.760417 0 + L3 0 0.020833 0.010418 0.760417 0 0.010417 0.010417 0 + L3 0 0.020833 4.916668 0.072917 0 0.010418 0.072917 0 + L3 0 0.020833 4.916668 0.697917 0 0.010418 0.697917 0 + END +# Plate Girder Bridge Design by Bob Blackwell +TURNOUT N "Atlas Plate Girder Bridge 2548" + P "Normal" 1 + E 0.010417 0.760417 269.999848 + E 4.916667 0.760423 89.999848 + S 0 0.000000 0.010417 0.760417 4.916667 0.760423 + L3 0 0.020833 0.010417 0.010417 0 4.916667 0.010417 0 + L3 0 0.020833 4.916667 0.010417 0 4.916667 1.510417 0 + L3 0 0.020833 4.916667 1.510417 0 0.010417 1.510417 0 + L3 0 0.020833 0.010417 1.510417 0 0.010417 0.010417 0 + L3 0 0.020833 4.916667 0.104180 0 0.010417 0.104173 0 + L3 0 0.020833 4.916667 1.416667 0 0.010417 1.416667 0 + END +TURNOUT N "Atlas Through Truss Bridge 10in 2570/71" + P "Normal" 1 + E 0.000000 0.000000 270.000000 + E 10.000000 0.000000 90.000000 + S 0 0 0.000000 0.000000 10.000000 0.000000 + L 0 0.053333 0.000000 0.84375 10.000000 0.84375 + L 0 0.053333 0.000000 -0.84375 10.000000 -0.84375 + L 0 0.053333 1.625000 -0.84375 1.625000 0.84375 + L 0 0.053333 1.625000 -0.84375 3.312500 0.84375 + L 0 0.053333 3.312500 -0.84375 1.625000 0.84375 + L 0 0.053333 3.312500 -0.84375 3.312500 0.84375 + L 0 0.053333 3.312500 -0.84375 5.000000 0.84375 + L 0 0.053333 5.000000 -0.84375 3.312500 0.84375 + L 0 0.053333 5.000000 -0.84375 5.000000 0.84375 + L 0 0.053333 5.000000 -0.84375 6.687500 0.84375 + L 0 0.053333 6.687500 -0.84375 5.000000 0.84375 + L 0 0.053333 6.687500 -0.84375 6.687500 0.84375 + L 0 0.053333 6.687500 -0.84375 8.375000 0.84375 + L 0 0.053333 8.375000 -0.84375 6.687500 0.84375 + L 0 0.053333 8.375000 -0.84375 8.375000 0.84375 + END + +SUBCONTENTS Atlas N-Scale Track - Crossings +# 90 Degree Crossing Design by Bob Blackwell +TURNOUT N "Atlas 90 Degree Crossing 2569" + P "Normal" 1 0 2 + E 0.000000 0.000000 270.000000 + E 4.910000 0.000000 90.000000 + E 2.455000 2.455000 0.000000 + E 2.455000 -2.455000 180.000000 + S 0 0 0.000000 0.000000 4.910000 0.000000 + S 0 0 2.455000 2.455000 2.455000 -2.455000 + END +# 60 Degree Crossing Design by Bob Blackwell +TURNOUT N "Atlas 60 Degree Crossing 2568" + P "Normal" 1 0 2 + E 0.000000 1.650872 270.000000 + E 2.859358 3.301727 29.999802 + E 3.812500 1.650872 90.000000 + E 0.953116 0.000000 209.999802 + S 0 0.000000 0.000000 1.650872 3.812500 1.650872 + S 0 0.000000 2.859358 3.301727 0.953116 0.000000 + END +# 45 Degree Crossing Design by Bob Blackwell +TURNOUT N "Atlas 45 Degree Crossing 2567" + P "Normal" 1 0 2 + E 0.000000 1.414217 270.000000 + E 3.414208 2.828431 44.999886 + E 4.000000 1.414217 90.000000 + E 0.585785 0.000000 224.999886 + S 0 0.000000 0.000000 1.414217 4.000000 1.414217 + S 0 0.000000 3.414208 2.828431 0.585785 0.000000 + END +# 30 Degree Crossing Design by Bob Blackwell +TURNOUT N "Atlas 30 Degree Crossing 2566" + P "Normal" 1 0 2 + E 0.000000 0.648442 270.000000 + E 2.419997 1.296881 59.999819 + E 2.593750 0.648442 90.000000 + E 0.173747 0.000000 239.999819 + S 0 0.000000 0.000000 0.648442 2.593750 0.648442 + S 0 0.000000 2.419997 1.296881 0.173747 0.000000 + END +# 20 Degree Crossing Design by Bob Blackwell +TURNOUT N "Atlas 20 Degree Crossing 2565" + P "Normal" 1 0 2 + E 0.000000 0.849704 270.000007 + E 4.818923 1.699405 70.000081 + E 4.968750 0.849703 90.000007 + E 0.149822 0.000000 250.000081 + S 0 0.000000 0.149822 0.000000 4.818923 1.699405 + S 0 0.000000 0.000000 0.849704 4.968750 0.849703 + END +# 15 Degree Crossing Design by Bob Blackwell +TURNOUT N "Atlas 15 Degree Crossover 2564" + P "Normal" 1 0 2 + E 0.085186 0.000000 255.000000 + E 4.914815 1.294094 75.000000 + E 0.000000 0.647044 270.000000 + E 5.000001 0.647050 90.000000 + S 0 0.000000 0.085186 0.000000 4.914815 1.294094 + S 0 0.000000 0.000000 0.647044 5.000001 0.647050 + END + +SUBCONTENTS Atlas N-Scale Track - Standard Remote Switches +# Remote Std #4 LH Switch 2700 Design by Bob Blackwell +TURNOUT N "Atlas Remote Std #4 LH Switch 2700" + P "Normal" 1 + P "Reverse" 2 + E 0.001050 0.708331 270.000000 + E 4.911050 0.708331 90.000000 + E 4.918050 1.354745 75.000000 + S 0 0.000000 0.001050 0.708331 4.911050 0.708331 + C 0 0.000000 -19.000000 0.000050 19.708331 165.000000 15.000000 + L3 0 0.020833 4.131258 0.479164 0 0.537508 0.479170 0 + L3 0 0.020833 4.131258 0.479164 0 4.131258 0.244789 0 + L3 0 0.020833 0.537508 0.479174 0 0.537508 0.244799 0 + L3 0 0.020833 3.131258 0.276042 0 1.537508 0.276046 0 + L3 0 0.020833 1.537507 0.010421 0 3.131257 0.010417 0 + L3 0 0.020833 4.131258 0.244789 0 3.131257 0.010417 0 + L3 0 0.020833 1.537507 0.010421 0 0.537508 0.244799 0 + L3 0 0.020833 3.131257 0.010417 0 3.131258 0.276042 0 + L3 0 0.020833 1.537507 0.010421 0 1.537508 0.276046 0 + L3 0 0.020833 3.881258 0.479165 0 3.881258 0.197915 0 + L3 0 0.020833 0.787508 0.479173 0 0.787508 0.197923 0 + A3 0 0.010417 0.064424 4.006258 0.354164 0 180.000000 360.000000 + A3 0 0.010417 0.049411 4.006258 0.354164 0 180.000000 360.000000 + A3 0 0.010417 0.062500 0.662508 0.354173 0 180.000000 360.000000 + A3 0 0.010417 0.044194 0.662508 0.354173 0 180.000000 360.000000 + END +# Remote Std #4 RH Switch 2701 Design by Bob Blackwell +TURNOUT N "Atlas Remote Std #4 RH Switch 2701" + P "Normal" 1 + P "Reverse" 2 + E 0.001000 0.647408 270.000000 + E 4.911000 0.647408 90.000000 + E 4.911000 0.000994 105.000000 + S 0 0.000000 0.001000 0.647408 4.911000 0.647408 + C 0 0.000000 19.000000 0.000000 -18.352592 0.000000 15.000000 + L3 0 0.020833 0.527042 0.876575 0 4.120792 0.876579 0 + L3 0 0.020833 0.527042 0.876575 0 0.527042 1.110950 0 + L3 0 0.020833 4.120792 0.876575 0 4.120792 1.110950 0 + L3 0 0.020833 1.527042 1.079700 0 3.120792 1.079700 0 + L3 0 0.020833 3.120792 1.345325 0 1.527042 1.345325 0 + L3 0 0.020833 0.527042 1.110950 0 1.527042 1.345325 0 + L3 0 0.020833 3.120792 1.345325 0 4.120792 1.110950 0 + L3 0 0.020833 1.527042 1.345325 0 1.527042 1.079700 0 + L3 0 0.020833 3.120792 1.345325 0 3.120792 1.079700 0 + L3 0 0.020833 0.777042 0.876575 0 0.777042 1.157825 0 + L3 0 0.020833 3.870792 0.876575 0 3.870792 1.157825 0 + A3 0 0.010417 0.064424 0.652042 1.001575 0 0.000000 360.000000 + A3 0 0.010417 0.049411 0.652042 1.001575 0 0.000000 360.000000 + A3 0 0.010417 0.062500 3.995792 1.001575 0 0.000000 360.000000 + A3 0 0.010417 0.044194 3.995792 1.001575 0 0.000000 360.000000 + END +# Remote #6 LH Switch 2704 Design by Bob Blackwell +TURNOUT N "Atlas Remote #6 LH Switch 2704" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.708331 270.000000 + E 6.109375 0.708331 90.000000 + E 6.156250 1.364581 80.500000 + S 0 0.000000 0.000000 0.708331 0.353100 0.708331 + S 0 0.000000 0.353100 0.708331 6.109375 0.708331 + C 0 0.000000 -22.644376 0.353130 23.352707 170.499924 9.500152 + S 0 0.000000 4.090556 1.018894 6.156250 1.364581 + L3 0 0.020833 4.489583 0.479164 0 0.895833 0.479170 0 + L3 0 0.020833 4.489583 0.479164 0 4.489582 0.244789 0 + L3 0 0.020833 0.895833 0.479174 0 0.895832 0.244799 0 + L3 0 0.020833 3.489582 0.276042 0 1.895832 0.276046 0 + L3 0 0.020833 1.895832 0.010421 0 3.489582 0.010417 0 + L3 0 0.020833 4.489582 0.244789 0 3.489582 0.010417 0 + L3 0 0.020833 1.895832 0.010421 0 0.895832 0.244799 0 + L3 0 0.020833 3.489582 0.010417 0 3.489582 0.276042 0 + L3 0 0.020833 1.895832 0.010421 0 1.895832 0.276046 0 + L3 0 0.020833 4.239583 0.479165 0 4.239582 0.197915 0 + L3 0 0.020833 1.145833 0.479173 0 1.145832 0.197923 0 + A3 0 0.010417 0.064424 4.364583 0.354164 0 180.000000 360.000000 + A3 0 0.010417 0.049411 4.364583 0.354164 0 180.000000 360.000000 + A3 0 0.010417 0.062500 1.020833 0.354173 0 180.000000 360.000000 + A3 0 0.010417 0.044194 1.020833 0.354173 0 180.000000 360.000000 + END +# Remote #6 RH Switch 2705 Design by Bob Blackwell +TURNOUT N "Atlas Remote #6 RH Switch 2705" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.656250 270.000000 + E 6.109375 0.656250 90.000000 + E 6.156250 0.000000 99.500000 + S 0 0.000000 0.000000 0.656250 0.353100 0.656250 + S 0 0.000000 0.353100 0.656250 6.109375 0.656250 + C 0 0.000000 22.644376 0.353010 -21.988126 0.000076 9.500152 + S 0 0.000000 4.090556 0.345687 6.156250 0.000000 + L3 0 0.020833 0.885417 0.885417 0 4.479167 0.885421 0 + L3 0 0.020833 0.885417 0.885417 0 0.885417 1.119792 0 + L3 0 0.020833 4.479167 0.885417 0 4.479167 1.119792 0 + L3 0 0.020833 1.885417 1.088542 0 3.479167 1.088542 0 + L3 0 0.020833 3.479167 1.354167 0 1.885417 1.354167 0 + L3 0 0.020833 0.885417 1.119792 0 1.885417 1.354167 0 + L3 0 0.020833 3.479167 1.354167 0 4.479167 1.119792 0 + L3 0 0.020833 1.885417 1.354167 0 1.885417 1.088542 0 + L3 0 0.020833 3.479167 1.354167 0 3.479167 1.088542 0 + L3 0 0.020833 1.135417 0.885417 0 1.135417 1.166667 0 + L3 0 0.020833 4.229167 0.885417 0 4.229167 1.166667 0 + A3 0 0.010417 0.064424 1.010417 1.010417 0 0.000000 360.000000 + A3 0 0.010417 0.049411 1.010417 1.010417 0 0.000000 360.000000 + A3 0 0.010417 0.062500 4.354167 1.010417 0 0.000000 360.000000 + A3 0 0.010417 0.044194 4.354167 1.010417 0 0.000000 360.000000 + END +SUBCONTENTS Atlas N-Scale Track - Standard Manual Switches +# Manual Std #4 LH Switch 2702 Design by Bob Blackwell +TURNOUT N "Atlas Manual Std #4 LH Switch 2702" + P "Normal" 1 + P "Reverse" 2 + E 0.001050 0.708331 270.000000 + E 4.911050 0.708331 90.000000 + E 4.918050 1.354745 75.000000 + S 0 0.000000 0.001050 0.708331 4.911050 0.708331 + C 0 0.000000 -19.000000 0.000050 19.708331 165.000000 15.000000 + L3 0 0.020833 4.131258 0.479164 0 0.537508 0.479170 0 + L3 0 0.020833 4.131258 0.479164 0 4.131258 0.244789 0 + L3 0 0.020833 0.537508 0.479174 0 0.537508 0.244799 0 + L3 0 0.020833 3.131258 0.276042 0 1.537508 0.276046 0 + L3 0 0.020833 1.537507 0.010421 0 3.131257 0.010417 0 + L3 0 0.020833 4.131258 0.244789 0 3.131257 0.010417 0 + L3 0 0.020833 1.537507 0.010421 0 0.537508 0.244799 0 + L3 0 0.020833 3.131257 0.010417 0 3.131258 0.276042 0 + L3 0 0.020833 1.537507 0.010421 0 1.537508 0.276046 0 + L3 0 0.020833 3.881258 0.479165 0 3.881258 0.197915 0 + L3 0 0.020833 0.787508 0.479173 0 0.787508 0.197923 0 + A3 0 0.010417 0.064424 4.006258 0.354164 0 180.000000 360.000000 + A3 0 0.010417 0.049411 4.006258 0.354164 0 180.000000 360.000000 + A3 0 0.010417 0.062500 0.662508 0.354173 0 180.000000 360.000000 + A3 0 0.010417 0.044194 0.662508 0.354173 0 180.000000 360.000000 + END +# Manual Std #4 RH Switch 2703 Design by Bob Blackwell +TURNOUT N "Atlas Manual Std #4 RH Switch 2703" + P "Normal" 1 + P "Reverse" 2 + E 0.001000 0.647408 270.000000 + E 4.911000 0.647408 90.000000 + E 4.911000 0.000994 105.000000 + S 0 0.000000 0.001000 0.647408 4.911000 0.647408 + C 0 0.000000 19.000000 0.000000 -18.352592 0.000000 15.000000 + L3 0 0.020833 0.527042 0.876575 0 4.120792 0.876579 0 + L3 0 0.020833 0.527042 0.876575 0 0.527042 1.110950 0 + L3 0 0.020833 4.120792 0.876575 0 4.120792 1.110950 0 + L3 0 0.020833 1.527042 1.079700 0 3.120792 1.079700 0 + L3 0 0.020833 3.120792 1.345325 0 1.527042 1.345325 0 + L3 0 0.020833 0.527042 1.110950 0 1.527042 1.345325 0 + L3 0 0.020833 3.120792 1.345325 0 4.120792 1.110950 0 + L3 0 0.020833 1.527042 1.345325 0 1.527042 1.079700 0 + L3 0 0.020833 3.120792 1.345325 0 3.120792 1.079700 0 + L3 0 0.020833 0.777042 0.876575 0 0.777042 1.157825 0 + L3 0 0.020833 3.870792 0.876575 0 3.870792 1.157825 0 + A3 0 0.010417 0.064424 0.652042 1.001575 0 0.000000 360.000000 + A3 0 0.010417 0.049411 0.652042 1.001575 0 0.000000 360.000000 + A3 0 0.010417 0.062500 3.995792 1.001575 0 0.000000 360.000000 + A3 0 0.010417 0.044194 3.995792 1.001575 0 0.000000 360.000000 + END +# Manual #6 LH Switch 2706 Design by Bob Blackwell +TURNOUT N "Atlas Manual #6 LH Switch 2706" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.708331 270.000000 + E 6.109375 0.708331 90.000000 + E 6.156250 1.364581 80.500000 + S 0 0.000000 0.000000 0.708331 0.353100 0.708331 + S 0 0.000000 0.353100 0.708331 6.109375 0.708331 + C 0 0.000000 -22.644376 0.353130 23.352707 170.499924 9.500152 + S 0 0.000000 4.090556 1.018894 6.156250 1.364581 + L3 0 0.020833 4.489583 0.479164 0 0.895833 0.479170 0 + L3 0 0.020833 4.489583 0.479164 0 4.489582 0.244789 0 + L3 0 0.020833 0.895833 0.479174 0 0.895832 0.244799 0 + L3 0 0.020833 3.489582 0.276042 0 1.895832 0.276046 0 + L3 0 0.020833 1.895832 0.010421 0 3.489582 0.010417 0 + L3 0 0.020833 4.489582 0.244789 0 3.489582 0.010417 0 + L3 0 0.020833 1.895832 0.010421 0 0.895832 0.244799 0 + L3 0 0.020833 3.489582 0.010417 0 3.489582 0.276042 0 + L3 0 0.020833 1.895832 0.010421 0 1.895832 0.276046 0 + L3 0 0.020833 4.239583 0.479165 0 4.239582 0.197915 0 + L3 0 0.020833 1.145833 0.479173 0 1.145832 0.197923 0 + A3 0 0.010417 0.064424 4.364583 0.354164 0 180.000000 360.000000 + A3 0 0.010417 0.049411 4.364583 0.354164 0 180.000000 360.000000 + A3 0 0.010417 0.062500 1.020833 0.354173 0 180.000000 360.000000 + A3 0 0.010417 0.044194 1.020833 0.354173 0 180.000000 360.000000 + END +# Manual #6 RH Switch 2707 Design by Bob Blackwell +TURNOUT N "Atlas Manual #6 RH Switch 2707" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.656250 270.000000 + E 6.109375 0.656250 90.000000 + E 6.156250 0.000000 99.500000 + S 0 0.000000 0.000000 0.656250 0.353100 0.656250 + S 0 0.000000 0.353100 0.656250 6.109375 0.656250 + C 0 0.000000 22.644376 0.353010 -21.988126 0.000076 9.500152 + S 0 0.000000 4.090556 0.345687 6.156250 0.000000 + L3 0 0.020833 0.885417 0.885417 0 4.479167 0.885421 0 + L3 0 0.020833 0.885417 0.885417 0 0.885417 1.119792 0 + L3 0 0.020833 4.479167 0.885417 0 4.479167 1.119792 0 + L3 0 0.020833 1.885417 1.088542 0 3.479167 1.088542 0 + L3 0 0.020833 3.479167 1.354167 0 1.885417 1.354167 0 + L3 0 0.020833 0.885417 1.119792 0 1.885417 1.354167 0 + L3 0 0.020833 3.479167 1.354167 0 4.479167 1.119792 0 + L3 0 0.020833 1.885417 1.354167 0 1.885417 1.088542 0 + L3 0 0.020833 3.479167 1.354167 0 3.479167 1.088542 0 + L3 0 0.020833 1.135417 0.885417 0 1.135417 1.166667 0 + L3 0 0.020833 4.229167 0.885417 0 4.229167 1.166667 0 + A3 0 0.010417 0.064424 1.010417 1.010417 0 0.000000 360.000000 + A3 0 0.010417 0.049411 1.010417 1.010417 0 0.000000 360.000000 + A3 0 0.010417 0.062500 4.354167 1.010417 0 0.000000 360.000000 + A3 0 0.010417 0.044194 4.354167 1.010417 0 0.000000 360.000000 + END +TURNOUT N "Atlas Remote Standard Wye 2708" + P "Left" 1 2 3 + P "Right" 1 4 5 + E 0.000000 0.000000 270.000000 + E 4.910000 0.646414 75.000000 + E 4.910000 -0.646414 105.000000 + S 0 0.000000 0.000000 0.000000 0.354344 0.000000 + C 0 0.000000 -16.279453 0.354366 16.279453 164.999924 15.000152 + S 0 0.000000 4.567815 0.554719 4.910000 0.646414 + C 0 0.000000 16.279453 0.354280 -16.279453 0.000076 15.000152 + S 0 0.000000 4.567815 -0.554719 4.910000 -0.646414 + L3 0 0.020833 0.616960 0.202769 0 4.178616 0.681977 0 + L3 0 0.020833 0.616960 0.202769 0 0.585708 0.435051 0 + L3 0 0.020833 4.178617 0.681973 0 4.147364 0.914255 0 + L3 0 0.020833 1.580944 0.537424 0 3.160462 0.749940 0 + L3 0 0.020833 3.125042 1.013193 0 1.545524 0.800677 0 + L3 0 0.020833 0.585708 0.435051 0 1.545524 0.800677 0 + L3 0 0.020833 3.125042 1.013193 0 4.147364 0.914255 0 + L3 0 0.020833 1.545524 0.800677 0 1.580944 0.537424 0 + L3 0 0.020833 3.125042 1.013193 0 3.160462 0.749940 0 + L3 0 0.020833 0.864727 0.236105 0 0.827224 0.514844 0 + L3 0 0.020833 3.930850 0.648638 0 3.893346 0.927376 0 + A3 0 0.010417 0.064424 0.724175 0.343321 0 352.337139 360.000000 + A3 0 0.010417 0.049411 0.724175 0.343321 0 352.337139 360.000000 + A3 0 0.010417 0.062500 4.038066 0.789189 0 352.337139 360.000000 + A3 0 0.010417 0.044194 4.038066 0.789189 0 352.337139 360.000000 + END +TURNOUT N "Atlas Manual Standard Wye 2709" + P "Left" 1 2 3 + P "Right" 1 4 5 + E 0.000000 0.000000 270.000000 + E 4.910000 0.646414 75.000000 + E 4.910000 -0.646414 105.000000 + S 0 0.000000 0.000000 0.000000 0.354344 0.000000 + C 0 0.000000 -16.279453 0.354366 16.279453 164.999924 15.000152 + S 0 0.000000 4.567815 0.554719 4.910000 0.646414 + C 0 0.000000 16.279453 0.354280 -16.279453 0.000076 15.000152 + S 0 0.000000 4.567815 -0.554719 4.910000 -0.646414 + L3 0 0.020833 0.616960 0.202769 0 4.178616 0.681977 0 + L3 0 0.020833 0.616960 0.202769 0 0.585708 0.435051 0 + L3 0 0.020833 4.178617 0.681973 0 4.147364 0.914255 0 + L3 0 0.020833 1.580944 0.537424 0 3.160462 0.749940 0 + L3 0 0.020833 3.125042 1.013193 0 1.545524 0.800677 0 + L3 0 0.020833 0.585708 0.435051 0 1.545524 0.800677 0 + L3 0 0.020833 3.125042 1.013193 0 4.147364 0.914255 0 + L3 0 0.020833 1.545524 0.800677 0 1.580944 0.537424 0 + L3 0 0.020833 3.125042 1.013193 0 3.160462 0.749940 0 + L3 0 0.020833 0.864727 0.236105 0 0.827224 0.514844 0 + L3 0 0.020833 3.930850 0.648638 0 3.893346 0.927376 0 + A3 0 0.010417 0.064424 0.724175 0.343321 0 352.337139 360.000000 + A3 0 0.010417 0.049411 0.724175 0.343321 0 352.337139 360.000000 + A3 0 0.010417 0.062500 4.038066 0.789189 0 352.337139 360.000000 + A3 0 0.010417 0.044194 4.038066 0.789189 0 352.337139 360.000000 + END + +SUBCONTENTS Atlas N-Scale Track - Custom (Motorless) Switches +# Custom Std #4 LH Switch 2750 Design by Bob Blackwell +TURNOUT N "Atlas Custom Std #4 LH Switch 2750" + P "Normal" 1 + P "Reverse" 2 + E 0.000000 0.000000 270.000000 + E 4.910000 0.000000 90.000000 + E 4.917000 0.646414 75.000000 + S 0 0 0.000000 0.000000 4.910000 0.000000 + C 0 0 -19.000000 -0.001000 19.000000 165.000000 15.000000 + END +# Custom Std #4 RH Switch 2751 Design by Bob Blackwell +TURNOUT N "Atlas Custom Std #4 RH Switch 2751" + P "Normal" 1 + P "Reverse" 2 + E 0.000000 0.000000 270.000000 + E 4.910000 0.000000 90.000000 + E 4.910000 -0.646414 105.000000 + S 0 0 0.000000 0.000000 4.910000 0.000000 + C 0 0 19.000000 -0.001000 -19.000000 0.000000 15.000000 + END +# Custom LH #6 Switch 2752 Design by Bob Blackwell +TURNOUT N "Atlas Custom LH #6 Switch 2752" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.000000 270.000000 + E 6.109375 0.000000 90.000000 + E 6.156250 0.656250 80.500000 + S 0 0.000000 0.000000 0.000000 0.353100 0.000000 + S 0 0.000000 0.353100 0.000000 6.109375 0.000000 + C 0 0.000000 -22.644376 0.353130 22.644376 170.499924 9.500152 + S 0 0.000000 4.090556 0.310563 6.156250 0.656250 + END +# Custom RH #6 Switch 2753 Design by Bob Blackwell +TURNOUT N "Atlas Custom RH #6 Switch 2753" + P "Normal" 1 2 + P "Reverse" 1 3 4 + E 0.000000 0.000000 270.000000 + E 6.109375 0.000000 90.000000 + E 6.156250 -0.656250 99.500000 + S 0 0.000000 0.000000 0.000000 0.353100 0.000000 + S 0 0.000000 0.353100 0.000000 6.109375 0.000000 + C 0 0.000000 22.644376 0.353010 -22.644376 0.000076 9.500152 + S 0 0.000000 4.090556 -0.310563 6.156250 -0.656250 + END +TURNOUT N "Atlas Custom Standard Wye 2754" + P "Left" 1 2 3 + P "Right" 1 4 5 + E 0.000000 0.000000 270.000000 + E 4.910000 0.646414 75.000000 + E 4.910000 -0.646414 105.000000 + S 0 0.000000 0.000000 0.000000 0.354344 0.000000 + C 0 0.000000 -16.279453 0.354366 16.279453 164.999924 15.000152 + S 0 0.000000 4.567815 0.554719 4.910000 0.646414 + C 0 0.000000 16.279453 0.354280 -16.279453 0.000076 15.000152 + S 0 0.000000 4.567815 -0.554719 4.910000 -0.646414 + END + +SUBCONTENTS Atlas N-Scale Structures - Bridge +# Bridge Pier (Base) Design by Bob Blackwell +STRUCTURE N "Atlas Bridge Pier 2541" + X pier 0.140625 "1" 0.281250 "2" 0.421875 "3" 0.562500 "4" 0.703125 "5" 0.843750 "6" 0.984375 "7" 1.125000 "8" 1.265625 "9" 1.406250 "10" 1.546875 "11" 1.687500 "12" + A3 0 0.020833 0.312500 -0.000000 0.687500 0 270.000000 180.000000 + A3 0 0.020833 0.312500 -0.000000 -0.687500 0 90.000000 180.000000 + L3 0 0.020833 0.312500 0.687500 0 0.312500 -0.687500 0 + L3 0 0.020833 -0.312500 0.687500 0 -0.312500 -0.687500 0 + A3 0 0.020833 0.250000 -0.000000 -0.375000 0 90.000000 180.000000 + A3 0 0.020833 0.250000 -0.000000 0.375000 0 270.000000 180.000000 + L3 0 0.020833 -0.250000 0.375000 0 -0.249999 -0.375000 0 + L3 0 0.020833 0.250000 0.375000 0 0.250001 -0.375000 0 + END +STRUCTURE N "Atlas Bridge Pier (Base) 2543" + X pier 1.687500 "B" + A3 0 0.020833 0.312500 -0.000000 0.687500 0 270.000000 180.000000 + A3 0 0.020833 0.312500 -0.000000 -0.687500 0 90.000000 180.000000 + L3 0 0.020833 0.312500 0.687500 0 0.312500 -0.687500 0 + L3 0 0.020833 -0.312500 0.687500 0 -0.312500 -0.687500 0 + A3 0 0.020833 0.250000 -0.000000 -0.375000 0 90.000000 180.000000 + A3 0 0.020833 0.250000 -0.000000 0.375000 0 270.000000 180.000000 + L3 0 0.020833 -0.250000 0.375000 0 -0.249999 -0.375000 0 + L3 0 0.020833 0.250000 0.375000 0 0.250001 -0.375000 0 + END +# Viaduct Kit Design by Bob Blackwell +STRUCTURE N "Atlas Viaduct Kit 2826" + L3 0 0.020833 0.010419 0.010417 0 4.385419 0.010422 0 + L3 0 0.020833 4.385419 0.010417 0 4.385419 1.135417 0 + L3 0 0.020833 4.385419 1.135422 0 0.010419 1.135417 0 + L3 0 0.020833 0.010418 1.135417 0 0.010417 0.010417 0 + L3 0 0.020833 4.385419 0.104172 0 0.010419 0.104167 0 + L3 0 0.020833 4.385419 1.041676 0 0.010419 1.041670 0 + L3 0 0.020833 4.385419 1.088547 0 0.010419 1.088542 0 + L3 0 0.020833 4.385419 0.057297 0 0.010419 0.057292 0 + END +SUBCONTENTS Atlas N-Scale Structures - Switch Machines +# Remote Switch Machine Design by Bob Blackwell +STRUCTURE N "Atlas Remote Switch Machine 271x" + L3 0 0.020833 0.010417 0.010417 0 3.604167 0.010421 0 + L3 0 0.020833 0.010417 0.010417 0 0.010417 0.244792 0 + L3 0 0.020833 3.604167 0.010417 0 3.604167 0.244792 0 + L3 0 0.020833 1.010417 0.213542 0 2.604167 0.213542 0 + L3 0 0.020833 2.604167 0.479167 0 1.010417 0.479167 0 + L3 0 0.020833 0.010417 0.244792 0 1.010417 0.479167 0 + L3 0 0.020833 2.604167 0.479167 0 3.604167 0.244792 0 + L3 0 0.020833 1.010417 0.479167 0 1.010417 0.213542 0 + L3 0 0.020833 2.604167 0.479167 0 2.604167 0.213542 0 + L3 0 0.020833 0.260417 0.010417 0 0.260417 0.291667 0 + L3 0 0.020833 3.354167 0.010417 0 3.354167 0.291667 0 + A3 0 0.010417 0.064424 0.135417 0.135417 0 0.000000 360.000000 + A3 0 0.010417 0.049411 0.135417 0.135417 0 0.000000 360.000000 + A3 0 0.010417 0.062500 3.479167 0.135417 0 0.000000 360.000000 + A3 0 0.010417 0.044194 3.479167 0.135417 0 0.000000 360.000000 + END + + +SUBCONTENTS Atlas N-Scale Structures - Turn Table +TURNOUT N "Atlas Turn Table 2790" +# TT was designed with 7.5in bridge, 8.5in outside dia., 15 degree spacing + P "1" 1 2 3 + P "2" 4 5 6 + P "3" 7 8 9 + P "4" 10 11 12 + P "5" 13 14 15 + P "6" 16 17 18 + P "7" 19 20 21 + P "8" 22 23 24 + P "9" 25 26 27 + P "10" 28 29 30 + P "11" 31 32 33 + P "12" 34 35 36 + + E 0.000000 4.250000 0.000000 + E 1.099981 4.105185 15.000000 + E 2.125000 3.680608 30.000000 + E 3.005204 3.005204 45.000000 + E 3.680608 2.125000 60.000000 +# E 4.105185 1.099981 75.000000 +# E 4.250000 0.000000 90.000000 +# E 4.105185 -1.099981 105.000000 + E 3.680608 -2.125000 120.000000 + E 3.005204 -3.005204 135.000000 + E 2.125000 -3.680608 150.000000 + E 1.099981 -4.105185 165.000000 + E 0.000000 -4.250000 180.000000 + E -1.099981 -4.105185 195.000000 + E -2.125000 -3.680608 210.000000 + E -3.005204 -3.005204 225.000000 + E -3.680608 -2.125000 240.000000 + E -4.105185 -1.099981 255.000000 + E -4.250000 0.000000 270.000000 + E -4.105185 1.099981 285.000000 + E -3.680608 2.125000 300.000000 + E -3.005204 3.005204 315.000000 + E -2.125000 3.680608 330.000000 + E -1.080111 4.031029 345.000000 +#0/360 + S 0 0 0.000000 4.250000 0.000000 3.750000 + S 0 0 0.000000 3.750000 0.000000 -3.750000 + S 0 0 0.000000 -3.750000 0.000000 -4.250000 +#15/195 + S 0 0 1.099981 4.105185 0.970571 3.622222 + S 0 0 0.970571 3.622222 -0.970571 -3.622222 + S 0 0 -0.970571 -3.622222 -1.099981 -4.105185 +#30/210 + S 0 0 2.125000 3.680608 1.875000 3.247595 + S 0 0 1.836614 3.247595 -1.875000 -3.247595 + S 0 0 -1.875000 -3.247595 -2.125000 -3.680608 +#45/225 + S 0 0 3.005204 3.005204 2.651650 2.651650 + S 0 0 2.651650 2.651650 -2.651650 -2.651650 + S 0 0 -2.651650 -2.651650 -3.005204 -3.005204 +#60/240 + S 0 0 3.680608 2.125000 3.247595 1.875000 + S 0 0 3.247595 1.875000 -3.247595 -1.875000 + S 0 0 -3.247595 -1.875000 -3.680608 -2.125000 +#75/255 + S 0 0 4.105185 1.099981 3.622222 0.970571 + S 0 0 3.622222 0.970571 -3.622222 -0.970571 + S 0 0 -3.622222 -0.970571 -4.105185 -1.099981 +#90/270 + S 0 0 4.250000 0.000000 3.750000 0.000000 + S 0 0 3.750000 0.000000 -3.750000 0.000000 + S 0 0 -3.750000 0.000000 -4.250000 0.000000 +#105/285 + S 0 0 4.105185 -1.099981 3.622222 -0.970571 + S 0 0 3.622222 -0.970571 -3.622222 0.970571 + S 0 0 -3.622222 0.970571 -4.105185 1.099981 +#120/300 + S 0 0 3.680608 -2.125000 3.247595 -1.875000 + S 0 0 3.247595 -1.875000 -3.247595 1.875000 + S 0 0 -3.247595 1.875000 -3.680608 2.125000 +#135/315 + S 0 0 3.005204 -3.005204 2.651650 -2.651650 + S 0 0 2.651650 -2.651650 -2.651650 2.651650 + S 0 0 -2.651650 2.651650 -3.005204 3.005204 +#150/330 + S 0 0 2.125000 -3.680608 1.875000 -3.247595 + S 0 0 1.836614 -3.247595 -1.875000 3.247595 + S 0 0 -1.875000 3.247595 -2.125000 3.680608 +#165/345 + S 0 0 1.099981 -4.105185 0.970571 -3.622222 + S 0 0 0.970571 -3.622222 -0.970571 3.622222 + S 0 0 -0.970571 3.622222 -1.099981 4.105185 + + A 11579568 0.053333 4.250000 0.000000 0.000000 0.000000 360.000000 + A 11579568 0.053333 3.750000 0.000000 0.000000 0.000000 360.000000 + L 11579568 0.053333 4.000000 1.437500 6.000000 1.437500 0 + L 11579568 0.053333 6.000000 1.437500 6.000000 -1.437500 0 + L 11579568 0.053333 4.000000 -1.437500 6.000000 -1.437500 0 + END
\ No newline at end of file diff --git a/app/bin/utf8convert.c b/app/bin/utf8convert.c new file mode 100644 index 0000000..0573c93 --- /dev/null +++ b/app/bin/utf8convert.c @@ -0,0 +1,96 @@ +/** + * \file utf8convert.c + * + * UTF8 conversion functions + */ + + /* XTrackCad - Model Railroad CAD + * Copyright (C) 2020 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 <string.h> + +#include <wlib.h> +#include "misc.h" +#include "include/utf8convert.h" + +/** + * Convert to UTF-8. The string must by a dynamically allocated storage block + * allocated with MyMalloc(). The functions returns a pointer to the converted + * string. If no conversion was necessary the returned string is identical to + * the parameter. Otherwise a new buffer is allocated and returned. + * + * \param [in] string If non-null, the string. + * + * \returns a pointer to a MyMalloc'ed string in UTF-8 format. + */ + +char * +Convert2UTF8( char *string ) +{ + if (RequiresConvToUTF8(string)) { + unsigned cnt = strlen(string) * 2 + 2; + unsigned char *out = MyMalloc(cnt); + wSystemToUTF8(string, out, cnt); + MyFree(string); + return(out); + } else { + return(string); + } +} + +/** + * Convert a string from UTF-8 to system codepage in place. As the length of + * the result most be equal or smaller than the input this is a safe + * approach. + * + * \param [in,out] in the string to be converted + */ + +void +ConvertUTF8ToSystem(unsigned char *in) +{ + if (wIsUTF8(in)) { + unsigned cnt = strlen(in) * 2 + 1; + unsigned char *out = MyMalloc(cnt); + wUTF8ToSystem(in, out, cnt); + strcpy(in, out); + MyFree(out); + } +} + +/** + * Requires convert to UTF-8 If at least one character is >127 the string + * has to be converted. + * + * \param [in9 string the string. + * + * \returns True if conversion is required, false if not. + */ + +bool +RequiresConvToUTF8(char *string) +{ + while (*string) { + if (*string++ & 0x7F) { + return(true); + } + } + return(false); +} + + diff --git a/app/bin/utility.c b/app/bin/utility.c index d1f798c..93f9979 100644 --- a/app/bin/utility.c +++ b/app/bin/utility.c @@ -217,6 +217,115 @@ void FindPos( coOrd * res, double * beyond, coOrd pos, coOrd orig, double angle, } +/* Find Arc Intersection + * Given two circles described by centers and radius (C1, R1) (C2, R2) Find the zero, one or two intersection points + * + */ +BOOL_T FindArcIntersections ( coOrd *Pc, coOrd *Pc2, coOrd center1, DIST_T radius1, coOrd center2, DIST_T radius2) { + double d,a,h; + + d = sqrt((center2.x-center1.x)*(center2.x-center1.x)+(center2.y-center1.y)*(center2.y-center1.y)); + if (d >(radius1+radius2)) return FALSE; //Too far apart + if (d<fabs(radius1-radius2)) return FALSE; //Inside each other + if ((d == 0) && (radius1 == radius2)) return FALSE; // Coincident and the same + a=((radius1*radius1)-(radius2*radius2)+(d*d))/(2*d); + if ((radius1*radius1)<(a*a)) return FALSE; + h = sqrt((radius1*radius1)-(a*a)); + + coOrd center_c; + center_c.x = center1.x+a*(center2.x - center1.x)/d; + center_c.y = center1.y+a*(center2.y - center1.y)/d; + + (*Pc).x = center_c.x+h*(center2.y-center1.y)/d; + (*Pc).y = center_c.y-h*(center2.x-center1.x)/d; + (*Pc2).x = center_c.x-h*(center2.y-center1.y)/d; + (*Pc2).y = center_c.y+h*(center2.x-center1.x)/d; + + return TRUE; +} + +/* + * Find Intersections between a line and a circle + * + * |c-x|^2 = r^2 + * + * π₯(π‘)=π+π‘π + * + * where π is a point and π is a vector. + * + * For a point on this line to satisfy the equation, you need to have + * + * (π‘π+(πβπ))β
(π‘π+(πβπ))=π^2 + * + * which is a quadratic in π‘: + * |b|^2*t^2 + 2(a-c).bt +(|a-c|^2-r^2) = 0 + * + * whose solutions are + * + * t = (-2(a-c).b +/- SQRT([2(a-c).b]^2 - 4|b|^2(|a-c|^2-r^2)) / 2|b|^2 + * + */ + +double VectorLength (coOrd v) { + return sqrt(v.x*v.x+v.y+v.y); +} +double VectorDot (coOrd v1, coOrd v2) { + return (v1.x*v2.x+ v1.y*v2.y); +} +coOrd VectorSubtract (coOrd v1, coOrd v2) { + coOrd result; + result.x = v1.x-v2.x; + result.y = v1.y-v2.y; + return result; +} +coOrd VectorAdd (coOrd v1, coOrd v2) { + coOrd result; + result.x = v1.x+v2.x; + result.y = v1.y+v2.y; + return result; +} + +BOOL_T FindArcAndLineIntersections(coOrd *intersection1, coOrd *intersection2, coOrd c, DIST_T radius, + coOrd point1, coOrd point2 ) +{ + double dx, dy, cx, cy, A, B, C, det, t; + + dx = point2.x - point1.x; + dy = point2.y - point1.y; + + cx = c.x; + cy = c.y; + + A = dx * dx + dy * dy; + B = 2 * (dx * (point1.x - cx) + dy * (point1.x - cy)); + C = (point1.x - cx) * (point1.x - cx) + (point1.y - cy) * (point1.y - cy) - radius * radius; + + det = B * B - 4 * A * C; + if ((A <= 0.0000001) || (det < 0)) + { + return FALSE; + } + else if (det == 0) + { + // One solution. + t = -B / (2 * A); + (*intersection1).x = point1.x + t * dx; + (*intersection1).y = point1.y + t * dy; + intersection2 = intersection1; + return TRUE; + } + else + { + // Two solutions. + t = (float)((-B + sqrt(det)) / (2 * A)); + (*intersection1).x = point1.x + t * dx; + (*intersection1).y = point1.y + t * dy; + t = (float)((-B - sqrt(det)) / (2 * A)); + (*intersection2).x = point1.x + t * dx; + (*intersection2).y = point1.y + t * dy; + return TRUE; + } +} /* Find intersection: Given 2 lines each described by a point and angle (P0,A0) (P1,A1) @@ -489,6 +598,7 @@ static void IntersectBox( coOrd *p1, coOrd p0, coOrd size, int x1, int y1 ) #ifndef WINDOWS else fprintf(stderr, "intersectBox bogus\n" ); + getchar(); #endif } @@ -589,6 +699,67 @@ BOOL_T ClipLine( coOrd *fp0, coOrd *fp1, coOrd orig, double angle, coOrd size ) return 1; } +coOrd FindCentroid(int vertexCount, pts_t vertices[] ) +{ + coOrd centroid = {0, 0}; + double signedArea = 0.0; + double x0 = 0.0; // Current vertex X + double y0 = 0.0; // Current vertex Y + double x1 = 0.0; // Next vertex X + double y1 = 0.0; // Next vertex Y + double a = 0.0; // Partial signed area + + // For all vertices except last + int i=0; + for (i=0; i<vertexCount-1; ++i) + { + x0 = vertices[i].pt.x; + y0 = vertices[i].pt.y; + x1 = vertices[i+1].pt.x; + y1 = vertices[i+1].pt.y; + a = x0*y1 - x1*y0; + signedArea += a; + centroid.x += (x0 + x1)*a; + centroid.y += (y0 + y1)*a; + } + + // Do last vertex separately to avoid performing an expensive + // modulus operation in each iteration. + x0 = vertices[i].pt.x; + y0 = vertices[i].pt.y; + x1 = vertices[0].pt.x; + y1 = vertices[0].pt.y; + a = x0*y1 - x1*y0; + signedArea += a; + centroid.x += (x0 + x1)*a; + centroid.y += (y0 + y1)*a; + + signedArea *= 0.5; + centroid.x /= (6.0*signedArea); + centroid.y /= (6.0*signedArea); + + return centroid; +} + +double FindArcCenter( + 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 + (90.0-a1) ); + Translate( pos, p0, a0, radius ); + return a1*2.0; +} + + #ifdef LATER BOOL_T ClipArc( double a0, double a1, coOrd pos, double radius, coOrd orig, double angle, double size ) { diff --git a/app/bin/utility.h b/app/bin/utility.h index 8666e6b..fad74be 100644 --- a/app/bin/utility.h +++ b/app/bin/utility.h @@ -57,11 +57,15 @@ 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 ); +BOOL_T FindArcAndLineIntersections (coOrd *Pc1, coOrd *Pc2, coOrd c, DIST_T r, coOrd p0, coOrd p1); +BOOL_T FindArcIntersections ( coOrd *Pc, coOrd *Pc2, coOrd center1, DIST_T radius1, coOrd center2, DIST_T radius2); 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 ); +coOrd FindCentroid(int vertexCount, pts_t vertices[] ); +double FindArcCenter(coOrd * c,coOrd p0,coOrd p1, double r ); #endif diff --git a/app/bin/validator.c b/app/bin/validator.c new file mode 100644 index 0000000..c415471 --- /dev/null +++ b/app/bin/validator.c @@ -0,0 +1,87 @@ +/** \file validator.c +* Validators for misc textformats +*/ + +/* XTrackCAD - Model Railroad CAD +* Copyright (C) 2019 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 <stdbool.h> +#include "validator.h" + +/** +* Simplistic checking of URL syntax validity. Checks: +* if scheme is present, it has to be terminated by double / +* hostname has at least 5 characters +* +* \param testString +* \return TRUE if valid, FALSE otherwise +*/ + +enum URLSCANSTATE { STATE_SCHEME, STATE_ENDOFSCHEME, STATE_HIER, STATE_PATH, STATE_ERROR }; + +#define MINURLLENGTH 5 /* 2 chars domain name, dot, 2 chars TLD */ + +bool +IsValidURL(char *testString) +{ + char *result = testString; + char *hostname = testString; + enum URLSCANSTATE state = STATE_SCHEME; + + if (!*result) { + return(false); + } + + while (*result && state != STATE_ERROR) { + switch (*result) { + case ':': + if (state == STATE_SCHEME) { + if (result == testString) { + state = STATE_ERROR; + } else { + state = STATE_ENDOFSCHEME; + } + } + break; + case '/': + if (state == STATE_ENDOFSCHEME) { + if (*(result + 1) == '/') { + state = STATE_HIER; + hostname = result + 2; + result++; + } else { + state = STATE_ERROR; + } + } else { + if (state == STATE_HIER || state == STATE_SCHEME) { + state = STATE_PATH; + if (result - hostname < MINURLLENGTH) { + state = STATE_ERROR; + } + } + } + break; + default: + break; + + } + result++; + } + + return (state != STATE_ERROR); +} diff --git a/app/bin/validator.h b/app/bin/validator.h new file mode 100644 index 0000000..3706f0f --- /dev/null +++ b/app/bin/validator.h @@ -0,0 +1,29 @@ +/** \file validator.h + * Validators for misc textformats +*/ + +/* XTrackCAD - Model Railroad CAD +* Copyright (C) 2019 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 +*/
+
+#ifndef HAVE_VALIDATOR_H
+#define HAVE_VALIDATOR_H
+
+#include <stdbool.h>
+bool IsValidURL(char *testString);
+
+#endif // !HAVE_VALIDATOR_H
+
|