/* \file misc.c * Main routine and initialization for the application */ /* XTrkCad - Model Railroad CAD * Copyright (C) 2005 Dave Bullis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #ifndef WINDOWS #include #include #endif #ifdef HAVE_MALLOC_H #include #endif #include #include #include #include #ifdef WINDOWS #include #include #include "getopt.h" #define R_OK (02) #define access _access #if _MSC_VER >1300 #define strdup _strdup #endif #else #include #endif #include #include #include #include "cjoin.h" #include "common.h" #include "compound.h" #include "cselect.h" #include "cundo.h" #include "custom.h" #include "draw.h" #include "fileio.h" #include "i18n.h" #include "layout.h" #include "messages.h" #include "misc.h" #include "param.h" #include "paths.h" #include "smalldlg.h" #include "track.h" #include "utility.h" #define DEFAULT_SCALE ("N") char *userLocale = NULL; extern wBalloonHelp_t balloonHelp[]; #ifdef DEBUG #define CHECK_BALLOONHELP /*#define CHECK_UNUSED_BALLOONHELP*/ #endif #ifdef CHECK_UNUSED_BALLOONHELP static void ShowUnusedBalloonHelp(void); #endif void DoCarDlg(void); /**************************************************************************** * EXPORTED VARIABLES * */ EXPORT int foobar = 0; EXPORT int log_error; static int log_command; EXPORT wWin_p mainW; EXPORT wIndex_t changed = 0; EXPORT char message[STR_LONG_SIZE]; static char message2[STR_LONG_SIZE]; EXPORT REGION_T curRegion = 0; EXPORT long paramVersion = -1; EXPORT coOrd zero = { 0.0, 0.0 }; EXPORT wBool_t extraButtons = FALSE; EXPORT long onStartup; /**< controls behaviour after startup: load last layout if zero, else start with blank canvas */ EXPORT wButton_p undoB; EXPORT wButton_p redoB; EXPORT wButton_p zoomUpB; EXPORT wButton_p zoomDownB; wButton_p mapShowB; EXPORT wIndex_t checkPtMark = 0; EXPORT wMenu_p demoM; EXPORT wMenu_p popup1M, popup2M; EXPORT wMenu_p popup1aM, popup2aM; static wIndex_t curCommand = 0; EXPORT void * commandContext; EXPORT wIndex_t cmdGroup; EXPORT wIndex_t joinCmdInx; EXPORT wIndex_t modifyCmdInx; EXPORT long rightClickMode = 0; EXPORT DIST_T easementVal = 0.0; EXPORT DIST_T easeR = 0.0; EXPORT DIST_T easeL = 0.0; EXPORT coOrd cmdMenuPos; EXPORT wPos_t DlgSepLeft = 12; EXPORT wPos_t DlgSepMid = 18; EXPORT wPos_t DlgSepRight = 12; EXPORT wPos_t DlgSepTop = 12; EXPORT wPos_t DlgSepBottom = 12; EXPORT wPos_t DlgSepNarrow = 6; EXPORT wPos_t DlgSepWide = 12; EXPORT wPos_t DlgSepFrmLeft = 4; EXPORT wPos_t DlgSepFrmRight = 4; EXPORT wPos_t DlgSepFrmTop = 4; EXPORT wPos_t DlgSepFrmBottom = 4; static int verbose = 0; static wMenuList_p winList_mi; static BOOL_T inMainW = TRUE; static long stickySet; static long stickyCnt = 0; static char * stickyLabels[33]; #define TOOLBARSET_INIT (0xFFFF) EXPORT long toolbarSet = TOOLBARSET_INIT; EXPORT wPos_t toolbarHeight = 0; static wPos_t toolbarWidth = 0; static wMenuList_p messageList_ml; static BOOL_T messageListEmpty = TRUE; #define MESSAGE_LIST_EMPTY N_("No Messages") #define NUM_FILELIST (5) extern long curTurnoutEp; static wIndex_t printCmdInx; static wIndex_t gridCmdInx; static paramData_t menuPLs[101] = { { PD_LONG, &toolbarSet, "toolbarset" }, { PD_LONG, &curTurnoutEp, "cur-turnout-ep" } }; static paramGroup_t menuPG = { "misc", PGO_RECORD, menuPLs, 2 }; /**************************************************************************** * * LOCAL UTILITIES * */ EXPORT long totalMallocs = 0; EXPORT long totalMalloced = 0; EXPORT long totalRealloced = 0; EXPORT long totalReallocs = 0; EXPORT long totalFreeed = 0; EXPORT long totalFrees = 0; static unsigned long guard0 = 0xDEADBEEF; static unsigned long guard1 = 0xAF00BA8A; static int log_malloc; EXPORT void * MyMalloc ( long size ) { void * p; totalMallocs++; totalMalloced += size; #if defined(WINDOWS) && ! defined(WIN32) if ( size > 65500L ) { AbortProg( "mallocing > 65500 bytes" ); } #endif p = malloc( (size_t)size + sizeof (size_t) + 2 * sizeof (unsigned long) ); if (p == NULL) AbortProg( "No memory" ); LOG1( log_malloc, ( "Malloc(%ld) = %lx (%lx-%lx)\n", size, (long)((char*)p+sizeof (size_t) + sizeof (unsigned long)), (long)p, (long)((char*)p+size+sizeof (size_t) + 2 * sizeof(unsigned long)) )); *(size_t*)p = (size_t)size; p = (char*)p + sizeof (size_t); *(unsigned long*)p = guard0; p = (char*)p + sizeof (unsigned long); *(unsigned long*)((char*)p+size) = guard1; memset( p, 0, (size_t)size ); return p; } EXPORT void * MyRealloc( void * old, long size ) { size_t oldSize; void * new; if (old==NULL) return MyMalloc( size ); totalReallocs++; totalRealloced += size; #if defined(WINDOWS) && ! defined(WIN32) if ( size > 65500L ) { AbortProg( "reallocing > 65500 bytes" ); } #endif if ( *(unsigned long*)((char*)old - sizeof (unsigned long)) != guard0 ) { AbortProg( "Guard0 is hosed" ); } oldSize = *(size_t*)((char*)old - sizeof (unsigned long) - sizeof (size_t)); if ( *(unsigned long*)((char*)old + oldSize) != guard1 ) { AbortProg( "Guard1 is hosed" ); } LOG1( log_malloc, ("Realloc(%lx,%ld) was %d\n", (long)old, size, oldSize ) ) if ((long)oldSize == size) { return old; } if (size == 0) { free( (char*)old - sizeof *(long*)0 - sizeof *(size_t*)0 ); return NULL; } new = MyMalloc( size ); if (new == NULL && size) AbortProg( "No memory" ); memcpy( new, old, min((size_t)size, oldSize) ); MyFree(old); return new; } EXPORT void MyFree( void * ptr ) { size_t oldSize; totalFrees++; if (ptr) { if ( *(unsigned long*)((char*)ptr - sizeof (unsigned long)) != guard0 ) { AbortProg( "Guard0 is hosed" ); } oldSize = *(size_t*)((char*)ptr - sizeof (unsigned long) - sizeof (size_t)); if ( *(unsigned long*)((char*)ptr + oldSize) != guard1 ) { AbortProg( "Guard1 is hosed" ); } LOG1( log_malloc, ("Free %d at %lx (%lx-%lx)\n", oldSize, (long)ptr, (long)((char*)ptr-sizeof *(size_t*)0-sizeof *(long*)0), (long)((char*)ptr+oldSize+sizeof *(long*)0)) ) totalFreeed += oldSize; free( (char*)ptr - sizeof *(long*)0 - sizeof *(size_t*)0 ); } } EXPORT void * memdup( void * src, size_t size ) { void * p; p = MyMalloc( size ); if (p == NULL) AbortProg( "No memory" ); memcpy( p, src, size ); return p; } EXPORT char * MyStrdup( const char * str ) { char * ret; ret = (char*)MyMalloc( strlen( str ) + 1 ); strcpy( ret, str ); return ret; } /* * Convert Text into the equivalent form that can be written to a file or put in a text box by adding escape characters * * The following special characters are produced - * \n for LineFeed 0x0A * \t for Tab 0x09 * "" for " This is so that a CSV conformant type program can interpret the file output * */ EXPORT char * ConvertToEscapedText(const char * text) { int text_i=0; int add = 0; //extra chars for escape while(text[text_i]) { switch (text[text_i]) { case '\n': add++; break; case '\t': add++; break; case '\\': add++; break; case '\"': add++; break; } text_i++; } char * cout = MyMalloc(strlen(text)+1+add); int cout_i = 0; text_i = 0; 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++; } text_i++; } cout[cout_i] = '\0'; return cout; } /* * Convert Text that has embedded escape characters into the equivalent form that can be shown on the screen * * The following special characters are supported - * \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; } EXPORT void AbortProg( char * msg, ... ) { static BOOL_T abort2 = FALSE; int rc; va_list ap; va_start( ap, msg ); vsprintf( message, msg, ap ); va_end( ap ); if (abort2) { wNoticeEx( NT_ERROR, message, _("ABORT"), NULL ); } else { strcat( message, _("\nDo you want to save your layout?") ); rc = wNoticeEx( NT_ERROR, message, _("Ok"), _("ABORT") ); if (rc) { DoSaveAs( (doSaveCallBack_p)abort ); } else { abort(); } } } EXPORT char * Strcpytrimed( char * dst, char * src, BOOL_T double_quotes ) { char * cp; while (*src && isspace((unsigned char)*src) ) src++; if (!*src) return dst; cp = src+strlen(src)-1; while ( cp>src && isspace((unsigned char)*cp) ) cp--; while ( src<=cp ) { if (*src == '"' && double_quotes) *dst++ = '"'; *dst++ = *src++; } *dst = '\0'; return dst; } EXPORT char * BuildTrimedTitle( char * cp, char * sep, char * mfg, char * desc, char * partno ) { cp = Strcpytrimed( cp, mfg, FALSE ); strcpy( cp, sep ); cp += strlen(cp); cp = Strcpytrimed( cp, desc, FALSE ); strcpy( cp, sep ); cp += strlen(cp); cp = Strcpytrimed( cp, partno, FALSE ); return cp; } static void ShowMessageHelp( int index, const char * label, void * data ) { char msgKey[STR_SIZE], *cp, *msgSrc; msgSrc = (char*)data; if (!msgSrc) return; cp = strchr( msgSrc, '\t' ); if (cp==NULL) { sprintf( msgKey, _("No help for %s"), msgSrc ); wNoticeEx( NT_INFORMATION, msgKey, _("Ok"), NULL ); return; } memcpy( msgKey, msgSrc, cp-msgSrc ); msgKey[cp-msgSrc] = 0; wHelp( msgKey ); } static char * ParseMessage( char *msgSrc ) { char *cp1=NULL, *cp2=NULL; static char shortMsg[STR_SIZE]; cp1 = strchr( _(msgSrc), '\t' ); if (cp1) { cp2 = strchr( cp1+1, '\t' ); if (cp2) { cp1++; memcpy( shortMsg, cp1, cp2-cp1 ); shortMsg[cp2-cp1] = 0; cp1 = shortMsg; cp2++; } else { cp1++; cp2 = cp1; } if (messageListEmpty) { wMenuListDelete( messageList_ml, _(MESSAGE_LIST_EMPTY) ); messageListEmpty = FALSE; } wMenuListAdd( messageList_ml, 0, cp1, _(msgSrc) ); return cp2; } else { return _(msgSrc); } } EXPORT void InfoMessage( char * format, ... ) { va_list ap; va_start( ap, format ); format = ParseMessage( format ); vsprintf( message2, format, ap ); va_end( ap ); /*InfoSubstituteControl( NULL, NULL );*/ if (inError) return; SetMessage( message2 ); } EXPORT void ErrorMessage( char * format, ... ) { va_list ap; va_start( ap, format ); format = ParseMessage( format ); vsprintf( message2, format, ap ); va_end( ap ); InfoSubstituteControls( NULL, NULL ); SetMessage( message2 ); wBeep(); inError = TRUE; } EXPORT int NoticeMessage( char * format, char * yes, char * no, ... ) { va_list ap; va_start( ap, no ); format = ParseMessage( format ); vsprintf( message2, format, ap ); va_end( ap ); return wNotice( message2, yes, no ); } EXPORT int NoticeMessage2( int playbackRC, char * format, char * yes, char * no, ... ) { va_list ap; if ( inPlayback ) return playbackRC; va_start( ap, no ); format = ParseMessage( format ); vsprintf( message2, format, ap ); va_end( ap ); return wNoticeEx( NT_INFORMATION, message2, yes, no ); } /***************************************************************************** * * MAIN BUTTON HANDLERS * */ EXPORT void Confirm( char * label2, doSaveCallBack_p after ) { int rc; if (changed) { rc = wNotice3( _("Save changes to the layout design before closing?\n\n" "If you don't save now, your unsaved changes will be discarded."), _("&Save"), _("&Cancel"), _("&Don't Save") ); if (rc == 1) { DoSave( after ); return; } else if (rc == 0) { return; } } after(); return; } static void ChkLoad( void ) { Confirm(_("Load"), DoLoad); } static void ChkRevert( void ) { int rc; if( changed) { rc = wNoticeEx( NT_WARNING, _("Do you want to return to the last saved state?\n\n" "Revert will cause all changes done since last save to be lost."), _("&Revert"), _("&Cancel") ); if( rc ) { /* load the file */ char *filename = GetLayoutFullPath(); LoadTracks( 1, &filename, NULL ); } } } static char * fileListPathName; static void AfterFileList( void ) { DoFileList( 0, NULL, fileListPathName ); } static void ChkFileList( int index, const char * label, void * data ) { fileListPathName = (char*)data; Confirm( _("Load"), AfterFileList ); } /** * Save information about current files and some settings to preferences file. */ EXPORT void SaveState( void ) { wPos_t width, height; const char * fileName; void * pathName; char file[6]; int inx; wWinGetSize( mainW, &width, &height ); wPrefSetInteger( "draw", "mainwidth", width ); wPrefSetInteger( "draw", "mainheight", height ); RememberParamFiles(); ParamUpdatePrefs(); wPrefSetString( "misc", "lastlayout", GetLayoutFullPath()); if ( fileList_ml ) { strcpy( file, "file" ); file[5] = 0; for ( inx=0; inx