/* \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 <stdlib.h>
#include <stdio.h>
#ifndef WINDOWS
#include <unistd.h>
#include <dirent.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#ifdef WINDOWS
#include <io.h>
#include <windows.h>
#include "getopt.h"
#define R_OK (02)
#define access _access
#if _MSC_VER >1300
	#define strdup _strdup
#endif
#else
#include <sys/stat.h>
#endif
#include <locale.h>
#include <stdarg.h>
#include <stdint.h>

#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<NUM_FILELIST; inx++ ) {
			fileName = wMenuListGet( fileList_ml, inx, &pathName );
			if (fileName) {
				file[4] = '0'+inx;
				sprintf( message, "%s", (char*)pathName );
				wPrefSetString( "filelist", file, message );
			}
		}
	}
	wPrefFlush();
	LogClose();
}

/*
 * Clean up before quitting
 */
static void DoQuitAfter( void )
{
	changed = 0;
	SaveState();

	CleanupFiles();
}
/**
 * Process shutdown request. This function is called when the user requests
 * to close the application. Before shutting down confirmation is gotten to
 * prevent data loss.
 */
void DoQuit( void )
{
	Confirm(_("Quit"), DoQuitAfter );
#ifdef CHECK_UNUSED_BALLOONHELP
	ShowUnusedBalloonHelp();
#endif
	LogClose();
	wExit(0);
}

static void DoClearAfter( void )
{
	
	ClearTracks();

	/* set all layers to their default properties and set current layer to 0 */
	DefaultLayerProperties();
	DoLayout(NULL);
	checkPtMark = 0;
	Reset();
	DoChangeNotification( CHANGE_MAIN|CHANGE_MAP );
	EnableCommands();
	SetLayoutFullPath("");
	SetWindowTitle();
}

static void DoClear( void )
{
	Confirm(_("Clear"), DoClearAfter);
}

/**
 * Toggle visibility state of map window.
 */

void MapWindowToggleShow(void)
{
    MapWindowShow(!mapVisible);
}

/**
 * Set visibility state of map window.
 *
 * \param state IN TRUE if visible, FALSE if hidden
 */

void MapWindowShow(int state)
{
    mapVisible = state;
    wPrefSetInteger("misc", "mapVisible", mapVisible);
    wMenuToggleSet(mapShowMI, mapVisible);

    if (mapVisible) {
        DoChangeNotification(CHANGE_MAP);
    }

    wWinShow(mapW, mapVisible);
    wButtonSetBusy(mapShowB, (wBool_t)mapVisible);
}

static void DoShowWindow(
		int index,
		const char * name,
		void * data )
{
	if (data == mapW) {
		if (mapVisible == FALSE) {
			MapWindowShow( TRUE );
			return;
		}
	}
	wWinShow( (wWin_p)data, TRUE );
}


static dynArr_t demoWindows_da;
#define demoWindows(N) DYNARR_N( wWin_p, demoWindows_da, N )

EXPORT void wShow(
		wWin_p win )
{
	int inx;
	if (inPlayback && win != demoW) {
		wWinSetBusy( win, TRUE );
		for ( inx=0; inx<demoWindows_da.cnt; inx++ )
			if ( demoWindows(inx) == win )
				break;
		if ( inx >= demoWindows_da.cnt ) {
			for ( inx=0; inx<demoWindows_da.cnt; inx++ )
				if ( demoWindows(inx) == NULL )
					 break;
			if ( inx >= demoWindows_da.cnt ) {
				DYNARR_APPEND( wWin_p, demoWindows_da, 10 );
				inx = demoWindows_da.cnt-1;
			}
			demoWindows(inx) = win;
		}
	}
	if (win != mainW)
		wMenuListAdd( winList_mi, -1, wWinGetTitle(win), win );
	wWinShow( win, TRUE );
}


EXPORT void wHide(
		wWin_p win )
{
	int inx;
	wWinShow( win, FALSE );
	wWinSetBusy( win, FALSE );
	if ( inMainW && win == aboutW )
		return;
	wMenuListDelete( winList_mi, wWinGetTitle(win) );
	if ( inPlayback )
		for ( inx=0; inx<demoWindows_da.cnt; inx++ )
			if ( demoWindows(inx) == win )
				demoWindows(inx) = NULL;
}


EXPORT void CloseDemoWindows( void )
{
	int inx;
	for ( inx=0; inx<demoWindows_da.cnt; inx++ )
		if ( demoWindows(inx) != NULL )
			wHide( demoWindows(inx) );
	demoWindows_da.cnt = 0;
}


EXPORT void DefaultProc(
		wWin_p win,
		winProcEvent e,
		void * data )
{
	switch( e ) {
	case wClose_e:
		wMenuListDelete( winList_mi, wWinGetTitle(win) );
		if (data != NULL)
			ConfirmReset( FALSE );
		wWinDoCancel( win );
		break;
	default:
		break;
	}
}


static void NextWindow( void )
{
}

EXPORT void SelectFont( void )
{
	wSelectFont(_("XTrackCAD Font"));
}

/*****************************************************************************
 *
 * COMMAND
 *
 */

#define COMMAND_MAX (170)
#define BUTTON_MAX (170)
#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];
static int buttonCnt = 0;

static struct {
		procCommand_t cmdProc;
		char * helpKey;
		wIndex_t buttInx;
		char * labelStr;
		wIcon_p icon;
		int reqLevel;
		wBool_t enabled;
		long options;
		long stickyMask;
		long acclKey;
		wMenuPush_p menu[NUM_CMDMENUS];
		void * context;
		} commandList[COMMAND_MAX];
static int commandCnt = 0;


#ifdef CHECK_UNUSED_BALLOONHELP
int * balloonHelpCnts;
#endif

EXPORT const char * GetBalloonHelpStr( char * helpKey )
{
	wBalloonHelp_t * bh;
#ifdef CHECK_UNUSED_BALLOONHELP
	if ( balloonHelpCnts == NULL ) {
		for ( bh=balloonHelp; bh->name; bh++ );
		balloonHelpCnts = (int*)malloc( (sizeof *(int*)0) * (bh-balloonHelp) );
		memset( balloonHelpCnts, 0, (sizeof *(int*)0) * (bh-balloonHelp) );
	}
#endif
	for ( bh=balloonHelp; bh->name; bh++ ) {
		if ( strcmp( bh->name, helpKey ) == 0 ) {
#ifdef CHECK_UNUSED_BALLOONHELP
			balloonHelpCnts[(bh-balloonHelp)]++;
#endif
			return _(bh->value);
		}
	}
#ifdef CHECK_BALLOONHELP
fprintf( stderr, _("No balloon help for %s\n"), helpKey );
#endif
	return _("No Help");
}


#ifdef CHECK_UNUSED_BALLOONHELP
static void ShowUnusedBalloonHelp( void )
{
	int cnt;
	for ( cnt=0; balloonHelp[cnt].name; cnt++ )
		if ( balloonHelpCnts[cnt] == 0 )
			fprintf( stderr, "unused BH %s\n", balloonHelp[cnt].name );
}
#endif


EXPORT void EnableCommands( void )
{
	int inx, minx;
	wBool_t enable;

LOG( log_command, 5, ( "COMMAND enable S%d M%d\n", selectedTrackCount, programMode ) )
	for ( inx=0; inx<commandCnt; inx++ ) {
		if (commandList[inx].buttInx) {
			if ( (commandList[inx].options & IC_SELECTED) &&
				 selectedTrackCount <= 0 )
				enable = FALSE;
			else if ( (programMode==MODE_TRAIN&&(commandList[inx].options&(IC_MODETRAIN_TOO|IC_MODETRAIN_ONLY))==0) ||
					  (programMode!=MODE_TRAIN&&(commandList[inx].options&IC_MODETRAIN_ONLY)!=0) )
				enable = FALSE;
			else
				enable = TRUE;
			if ( commandList[inx].enabled != enable ) {
				if ( commandList[inx].buttInx >= 0 )
					wControlActive( buttonList[commandList[inx].buttInx].control, enable );
				for ( minx=0; minx<NUM_CMDMENUS; minx++ )
					if (commandList[inx].menu[minx])
						wMenuPushEnable( commandList[inx].menu[minx], enable );
				commandList[inx].enabled = enable;
			}
		}
	}

	for ( inx=0; inx<menuPG.paramCnt; inx++ ) {
		if ( menuPLs[inx].control == NULL )
			continue;
		if ( (menuPLs[inx].option & IC_SELECTED) &&
			 selectedTrackCount <= 0 )
			enable = FALSE;
		else if ( (programMode==MODE_TRAIN&&(menuPLs[inx].option&(IC_MODETRAIN_TOO|IC_MODETRAIN_ONLY))==0) ||
				  (programMode!=MODE_TRAIN&&(menuPLs[inx].option&IC_MODETRAIN_ONLY)!=0) )
			enable = FALSE;
		else
			enable = TRUE;
		wMenuPushEnable( (wMenuPush_p)menuPLs[inx].control, enable );
	}

	for ( inx=0; inx<buttonCnt; inx++ ) {
		if ( buttonList[inx].cmdInx < 0 && (buttonList[inx].options&IC_SELECTED) )
			wControlActive( buttonList[inx].control, selectedTrackCount>0 );
	 }
}


EXPORT void Reset( void )
{
	if (recordF) {
		fprintf( recordF, "RESET\n" );
		fflush( recordF );
	}
LOG( log_command, 2, ( "COMMAND CANCEL %s\n", commandList[curCommand].helpKey ) )
	commandList[curCommand].cmdProc( C_CANCEL, zero );
	if ( commandList[curCommand].buttInx>=0 )
		wButtonSetBusy( (wButton_p)buttonList[commandList[curCommand].buttInx].control, FALSE );
	curCommand = (preSelect?selectCmdInx:describeCmdInx);
	commandContext = commandList[curCommand].context;
	if ( commandList[curCommand].buttInx >= 0 )
		wButtonSetBusy( (wButton_p)buttonList[commandList[curCommand].buttInx].control, TRUE );
	tempSegs_da.cnt = 0;
	if (checkPtInterval > 0 &&
		changed >= checkPtMark+(wIndex_t)checkPtInterval &&
		!inPlayback ) {
		DoCheckPoint();
		checkPtMark = changed;
	}
    MainRedraw();
    MapRedraw();
	EnableCommands();
	ResetMouseState();
LOG( log_command, 1, ( "COMMAND RESET %s\n", commandList[curCommand].helpKey ) )
	(void)commandList[curCommand].cmdProc( C_START, zero );
}


static BOOL_T CheckClick(
		wAction_t *action,
		coOrd *pos,
		BOOL_T checkLeft,
		BOOL_T checkRight )
{
	static long time0;
	static coOrd pos0;
	long time1;
	long timeDelta;
	DIST_T distDelta;

	switch (*action) {
	case C_DOWN:
		if (!checkLeft)
			return TRUE;
		time0 = wGetTimer() - adjTimer;
		pos0 = *pos;
		return FALSE;
	case C_MOVE:
		if (!checkLeft)
			return TRUE;
		if (time0 != 0) {
			time1 = wGetTimer() - adjTimer;
			timeDelta = time1 - time0;
			distDelta = FindDistance( *pos, pos0 );
			if ( timeDelta > dragTimeout ||
				 distDelta > dragDistance ) {
				time0 = 0;
				*pos = pos0;
				*action = C_DOWN;
			} else {
				return FALSE;
			}
		}
		break;
	case C_UP:
		if (!checkLeft)
			return TRUE;
		if (time0 != 0) {
			time1 = wGetTimer() - adjTimer;
			timeDelta = time1 - time0;
			distDelta = FindDistance( *pos, pos0 );
			time0 = 0;
			*action = C_LCLICK;
		}
		break;
	case C_RDOWN:
		if (!checkRight)
			return TRUE;
		time0 = wGetTimer() - adjTimer;
		pos0 = *pos;
		return FALSE;
	case C_RMOVE:
		if (!checkRight)
			return TRUE;
		if (time0 != 0) {
			time1 = wGetTimer() - adjTimer;
			timeDelta = time1 - time0;
			distDelta = FindDistance( *pos, pos0 );
			if ( timeDelta > dragTimeout ||
				 distDelta > dragDistance ) {
				time0 = 0;
				*pos = pos0;
				*action = C_RDOWN;
			} else {
				return FALSE;
			}
		}
		break;
	case C_RUP:
		if (!checkRight)
			return TRUE;
		if (time0 != 0) {
			time0 = 0;
			*action = C_RCLICK;
		}
		break;
	}
	return TRUE;
}


EXPORT wBool_t DoCurCommand( wAction_t action, coOrd pos )
{
	wAction_t rc;
	int mode;

	if ( action == wActionMove && (commandList[curCommand].options & IC_WANT_MOVE) == 0 )
		return C_CONTINUE;

	if ( !CheckClick( &action, &pos,
		 (int)(commandList[curCommand].options & IC_LCLICK), TRUE ) )
		return C_CONTINUE;

	if ( action == C_RCLICK && (commandList[curCommand].options&IC_RCLICK)==0 ) {
		if ( !inPlayback ) {
			mode = MyGetKeyState();
			if ( ( mode & (~WKEY_SHIFT) ) != 0 ) {
				wBeep();
				return C_CONTINUE;
			}
			if ( ((mode&WKEY_SHIFT) == 0) == (rightClickMode==0) ) {
				if ( selectedTrackCount > 0 ) {
					if (commandList[curCommand].options & IC_CMDMENU) {
					}
					wMenuPopupShow( popup2M );
				} else {
					wMenuPopupShow( popup1M );
				}
				return C_CONTINUE;
			} else if ( (commandList[curCommand].options & IC_CMDMENU) ) {
				cmdMenuPos = pos;
				action = C_CMDMENU;
			} else {
				wBeep();
				return C_CONTINUE;
			}
		} else {
			return C_CONTINUE;
		}
	}

LOG( log_command, 2, ( "COMMAND MOUSE %s %d @ %0.3f %0.3f\n", commandList[curCommand].helpKey, (int)action, pos.x, pos.y ) )
	rc = commandList[curCommand].cmdProc( action, pos );
LOG( log_command, 4, ( "    COMMAND returns %d\n", rc ) )
	if ( (rc == C_TERMINATE || rc == C_INFO) &&
		 (commandList[curCommand].options & IC_STICKY) &&
		 (commandList[curCommand].stickyMask & stickySet) ) {
		tempSegs_da.cnt = 0;
		UpdateAllElevations();
		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 ) {
		case C_CONTINUE:
			break;
		case C_ERROR:
			Reset();
#ifdef VERBOSE
			lprintf( "Start returns Error");
#endif
			break;
		case C_TERMINATE:
			InfoMessage( "" );
		case C_INFO:
			Reset();
			break;
		}
	}
	return rc;
}


EXPORT void ConfirmReset( BOOL_T retry )
{
	wAction_t rc;
	if (curCommand != describeCmdInx && curCommand != selectCmdInx ) {
LOG( log_command, 3, ( "COMMAND CONFIRM %s\n", commandList[curCommand].helpKey ) )
		rc = commandList[curCommand].cmdProc( C_CONFIRM, zero );
LOG( log_command, 4, ( "    COMMAND returns %d\n", rc ) )
		if ( rc == C_ERROR ) {
			if (retry)
				rc = wNotice3(
				_("Cancelling the current command will undo the changes\n"
				"you are currently making. Do you want to update?"),
				_("Yes"), _("No"), _("Cancel") );
			else
				rc = wNoticeEx( NT_WARNING,
				_("Cancelling the current command will undo the changes\n"
				"you are currently making. Do you want to update?"),
				_("Yes"), _("No") );
			if (rc == 1) {
LOG( log_command, 3, ( "COMMAND OK %s\n", commandList[curCommand].helpKey ) )
				commandList[curCommand].cmdProc( C_OK, zero );
				return;
			} else if (rc == -1) {
				return;
			}
		} else if ( rc == C_TERMINATE ) {
			return;
		}
	}
	Reset();
	if (retry) {
		/* because user pressed esc */
		SetAllTrackSelect( FALSE );
	}
LOG( log_command, 1, ( "COMMAND RESET %s\n", commandList[curCommand].helpKey ) )
	commandList[curCommand].cmdProc( C_START, zero );
}


EXPORT void ResetIfNotSticky( void )
{
	if ( (commandList[curCommand].options & IC_STICKY) == 0 ||
		 (commandList[curCommand].stickyMask & stickySet) == 0 )
		Reset();
}


EXPORT void DoCommandB(
		void * data )
{
	wIndex_t inx = (wIndex_t)(long)data;
	STATUS_T rc;
	static coOrd pos = {0,0};
	static int inDoCommandB = FALSE;
	wIndex_t buttInx;

	if (inDoCommandB)
		return;
	inDoCommandB = TRUE;

	if (inx < 0 || inx >= commandCnt) {
		ASSERT( FALSE );
		inDoCommandB = FALSE;
		return;
	}

	if ( (!inPlayback) && (!commandList[inx].enabled) ) {
		ErrorMessage( MSG_COMMAND_DISABLED );
		inx = describeCmdInx;
	}

	InfoMessage( "" );
	if (curCommand != selectCmdInx ) {
LOG( log_command, 3, ( "COMMAND FINISH %s\n", commandList[curCommand].helpKey ) )
		rc = commandList[curCommand].cmdProc( C_FINISH, zero );
LOG( log_command, 3, ( "COMMAND CONFIRM %s\n", commandList[curCommand].helpKey ) )
		rc = commandList[curCommand].cmdProc( C_CONFIRM, zero );
LOG( log_command, 4, ( "    COMMAND returns %d\n", rc ) )
		if ( rc == C_ERROR ) {
			rc = wNotice3(
				_("Cancelling the current command will undo the changes\n"
				"you are currently making. Do you want to update?"),
				_("Yes"), _("No"), _("Cancel") );
			if (rc == 1)
				commandList[curCommand].cmdProc( C_OK, zero );
			else if (rc == -1) {
				inDoCommandB = FALSE;
				return;
			}
		}
LOG( log_command, 3, ( "COMMAND CANCEL %s\n", commandList[curCommand].helpKey ) )
		commandList[curCommand].cmdProc( C_CANCEL, pos );
		tempSegs_da.cnt = 0;
	}
	if (commandList[curCommand].buttInx>=0)
		wButtonSetBusy( (wButton_p)buttonList[commandList[curCommand].buttInx].control, FALSE );

	if (recordF) {
		fprintf( recordF, "COMMAND %s\n", commandList[inx].helpKey+3 );
		fflush( recordF );
	}

	curCommand = inx;
	commandContext = commandList[curCommand].context;
	if ( (buttInx=commandList[curCommand].buttInx) >= 0 ) {
		if ( buttonList[buttInx].cmdInx != curCommand ) {
			wButtonSetLabel( (wButton_p)buttonList[buttInx].control, (char*)commandList[curCommand].icon );
			wControlSetHelp( buttonList[buttInx].control, GetBalloonHelpStr(commandList[curCommand].helpKey) );
			wControlSetContext( buttonList[buttInx].control, (void*)(intptr_t)curCommand );
			buttonList[buttInx].cmdInx = curCommand;
		}
		wButtonSetBusy( (wButton_p)buttonList[commandList[curCommand].buttInx].control, TRUE );
	}
LOG( log_command, 1, ( "COMMAND START %s\n", commandList[curCommand].helpKey ) )
	rc = commandList[curCommand].cmdProc( C_START, pos );
LOG( log_command, 4, ( "    COMMAND returns %d\n", rc ) )
	switch( rc ) {
	case C_CONTINUE:
		break;
	case C_ERROR:
		Reset();
#ifdef VERBOSE
		lprintf( "Start returns Error");
#endif
		break;
	case C_TERMINATE:
	case C_INFO:
		if (rc == C_TERMINATE)
			InfoMessage( "" );
		Reset();
		break;
	}
	inDoCommandB = FALSE;
}


static void DoCommandBIndirect( void * cmdInxP )
{
	wIndex_t cmdInx;
	cmdInx = *(wIndex_t*)cmdInxP;
	DoCommandB( (void*)(intptr_t)cmdInx );
}


EXPORT void LayoutSetPos(
		wIndex_t inx )
{
	wPos_t w, h;
	static wPos_t toolbarRowHeight = 0;
	static wPos_t width;
	static int lastGroup;
	static wPos_t gap;
	static int layerButtCnt;
	int currGroup;

	if ( inx == 0 ) {
		lastGroup = 0;
		wWinGetSize( mainW, &width, &h );
		gap = 5;
		toolbarWidth = width-20+5;
		layerButtCnt = 0;
		toolbarHeight = 0;
	}

	if (buttonList[inx].control) {
		if ( toolbarRowHeight <= 0 )
			toolbarRowHeight = wControlGetHeight( buttonList[inx].control );

		currGroup = buttonList[inx].group & ~BG_BIGGAP;
		if ( currGroup != lastGroup && (buttonList[inx].group&BG_BIGGAP) ) {
				gap = 15;
		}
		if ((toolbarSet & (1<<currGroup)) &&
				(programMode!=MODE_TRAIN||(buttonList[inx].options&(IC_MODETRAIN_TOO|IC_MODETRAIN_ONLY))) &&
				(programMode==MODE_TRAIN||(buttonList[inx].options&IC_MODETRAIN_ONLY)==0) &&
				((buttonList[inx].group&~BG_BIGGAP) != BG_LAYER ||
				  layerButtCnt++ <= layerCount) ) {
				if (currGroup != lastGroup) {
					toolbarWidth += gap;
					lastGroup = currGroup;
					gap = 5;
				}
				w = wControlGetWidth( buttonList[inx].control );
				h = wControlGetHeight( buttonList[inx].control );
				if ( inx<buttonCnt-1 && (buttonList[inx+1].options&IC_ABUT) )
					w += wControlGetWidth( buttonList[inx+1].control );
				if (toolbarWidth+w>width-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 );
		} else {
				wControlShow( buttonList[inx].control, FALSE );
		}
	}
}


EXPORT void LayoutToolBar( void * data )
{
	int inx;

	for (inx = 0; inx<buttonCnt; inx++) {
		LayoutSetPos( inx );
	}
	if (toolbarSet&(1<<BG_HOTBAR)) {
		LayoutHotBar(data);
	} else {
		HideHotBar();
	}
}


static void ToolbarChange( long changes )
{
	if ( (changes&CHANGE_TOOLBAR) ) {
		/*if ( !(changes&CHANGE_MAIN) )*/
			MainProc( mainW, wResize_e, NULL, NULL );
		/*else
			LayoutToolBar();*/
	}
}

/***************************************************************************
 *
 *
 *
 */


EXPORT BOOL_T CommandEnabled(
		wIndex_t cmdInx )
{
	return commandList[cmdInx].enabled;
}


static wIndex_t AddCommand(
		procCommand_t cmdProc,
		char * helpKey,
		char * nameStr,
		wIcon_p icon,
		int reqLevel,
		long options,
		long acclKey,
		void * context )
{
	if (commandCnt >= COMMAND_MAX-1) {
		AbortProg("addCommand: too many commands" );
	}
	commandList[commandCnt].labelStr = MyStrdup(nameStr);
	commandList[commandCnt].helpKey = MyStrdup(helpKey);
	commandList[commandCnt].cmdProc = cmdProc;
	commandList[commandCnt].icon = icon;
	commandList[commandCnt].reqLevel = reqLevel;
	commandList[commandCnt].enabled = TRUE;
	commandList[commandCnt].options = options;
	commandList[commandCnt].acclKey = acclKey;
	commandList[commandCnt].context = context;
	commandList[commandCnt].buttInx = -1;
	commandList[commandCnt].menu[0] = NULL;
	commandList[commandCnt].menu[1] = NULL;
	commandList[commandCnt].menu[2] = NULL;
	commandList[commandCnt].menu[3] = NULL;
	commandCnt++;
	return commandCnt-1;
}

EXPORT void AddToolbarControl(
		wControl_p control,
		long options )
{
	if (buttonCnt >= COMMAND_MAX-1) {
		AbortProg("addToolbarControl: too many buttons" );
	}
	buttonList[buttonCnt].enabled = TRUE;
	buttonList[buttonCnt].options = options;
	buttonList[buttonCnt].group = cmdGroup;
	buttonList[buttonCnt].x = 0;
	buttonList[buttonCnt].y = 0;
	buttonList[buttonCnt].control = control;
	buttonList[buttonCnt].cmdInx = -1;
	LayoutSetPos( buttonCnt );
	buttonCnt++;
}


EXPORT wButton_p AddToolbarButton(
		char * helpStr,
		wIcon_p icon,
		long options,
		wButtonCallBack_p action,
		void * context )
{
	wButton_p bb;
	wIndex_t inx;

	GetBalloonHelpStr(helpStr);
	if ( context == NULL ) {
		for ( inx=0; inx<menuPG.paramCnt; inx++ ) {
			if ( action != DoCommandB && menuPLs[inx].valueP == (void*)action ) {
				context = &menuPLs[inx];
				action = ParamMenuPush;
				menuPLs[inx].context = (void*)(intptr_t)buttonCnt;
				menuPLs[inx].option |= IC_PLAYBACK_PUSH;
				break;
			}
		}
	}
	bb = wButtonCreate( mainW, 0, 0, helpStr, (char*)icon,
				BO_ICON/*|((options&IC_CANCEL)?BB_CANCEL:0)*/, 0,
				action, context );
	AddToolbarControl( (wControl_p)bb, options );
	return bb;
}


EXPORT void PlaybackButtonMouse(
		wIndex_t buttInx )
{
	wPos_t cmdX, cmdY;

	if ( buttInx < 0 || buttInx >= buttonCnt ) return;
	if ( buttonList[buttInx].control == NULL ) return;
	cmdX = buttonList[buttInx].x+17;
	cmdY = toolbarHeight - (buttonList[buttInx].y+17) +
				   (wPos_t)(mainD.size.y/mainD.scale*mainD.dpi) + 30;
	MovePlaybackCursor( &mainD, cmdX, cmdY );
	if ( playbackTimer == 0 ) {
		wButtonSetBusy( (wButton_p)buttonList[buttInx].control, TRUE );
		wFlush();
		wPause( 500 );
		wButtonSetBusy( (wButton_p)buttonList[buttInx].control, FALSE );
		wFlush();
	}
}


#include "bitmaps/openbutt.xpm"
static char * buttonGroupMenuTitle;
static char * buttonGroupHelpKey;
static char * buttonGroupStickyLabel;
static wMenu_p buttonGroupPopupM;

EXPORT void ButtonGroupBegin(
		char * menuTitle,
		char * helpKey,
		char * stickyLabel )
{
	buttonGroupMenuTitle = menuTitle;
	buttonGroupHelpKey = helpKey;
	buttonGroupStickyLabel = stickyLabel;
	buttonGroupPopupM = NULL;
}

EXPORT void ButtonGroupEnd( void )
{
	buttonGroupMenuTitle = NULL;
	buttonGroupHelpKey = NULL;
	buttonGroupPopupM = NULL;
}


EXPORT wIndex_t AddMenuButton(
		wMenu_p menu,
		procCommand_t command,
		char * helpKey,
		char * nameStr,
		wIcon_p icon,
		int reqLevel,
		long options,
		long acclKey,
		void * context )
{
	wIndex_t buttInx = -1;
	wIndex_t cmdInx;
	BOOL_T newButtonGroup = FALSE;
	wMenu_p tm, p1m, p2m;
	static wIcon_p openbuttIcon = NULL;
	static wMenu_p commandsSubmenu;
	static wMenu_p popup1Submenu;
	static wMenu_p popup2Submenu;

	if ( icon ) {
		if ( buttonGroupPopupM!=NULL ) {
			buttInx = buttonCnt-2;
		} else {
			buttInx = buttonCnt;
			AddToolbarButton( helpKey, icon, options, (wButtonCallBack_p)DoCommandB, (void*)(intptr_t)commandCnt );
			buttonList[buttInx].cmdInx = commandCnt;
		}
		if ( buttonGroupMenuTitle!=NULL && buttonGroupPopupM==NULL ) {
			if ( openbuttIcon == NULL )
				openbuttIcon = wIconCreatePixMap(openbutt_xpm);
			buttonGroupPopupM = wMenuPopupCreate( mainW, buttonGroupMenuTitle );
			AddToolbarButton( buttonGroupHelpKey, openbuttIcon, IC_ABUT, (wButtonCallBack_p)wMenuPopupShow, (void*)buttonGroupPopupM );
			newButtonGroup = TRUE;
			commandsSubmenu = wMenuMenuCreate( menu, "", buttonGroupMenuTitle );
			popup1Submenu = wMenuMenuCreate( ((options&IC_POPUP2)?popup1aM:popup1M), "", buttonGroupMenuTitle );
			popup2Submenu = wMenuMenuCreate( ((options&IC_POPUP2)?popup2aM:popup2M), "", buttonGroupMenuTitle );
		}
	}
	cmdInx = AddCommand( command, helpKey, nameStr, icon, reqLevel, options, acclKey, context );
	commandList[cmdInx].buttInx = buttInx;
	if (nameStr[0] == '\0')
		return cmdInx;
	if (commandList[cmdInx].options&IC_STICKY) {
		if ( buttonGroupPopupM==NULL || newButtonGroup ) {
			if ( stickyCnt > 32 )
				AbortProg( "stickyCnt>32" );
			stickyCnt++;
		}
		if ( buttonGroupPopupM==NULL) {
			stickyLabels[stickyCnt-1] = nameStr;
		} else {
			stickyLabels[stickyCnt-1] = buttonGroupStickyLabel;
		}
		stickyLabels[stickyCnt] = NULL;
		commandList[cmdInx].stickyMask = 1L<<(stickyCnt-1);
	}
	if ( buttonGroupPopupM ) {
		commandList[cmdInx].menu[0] =
		wMenuPushCreate( buttonGroupPopupM, helpKey, GetBalloonHelpStr(helpKey), 0, DoCommandB, (void*)(intptr_t)cmdInx );
		tm = commandsSubmenu;
		p1m = popup1Submenu;
		p2m = popup2Submenu;
	} else {
		tm = menu;
		p1m = (options&IC_POPUP2)?popup1aM:popup1M;
		p2m = (options&IC_POPUP2)?popup2aM:popup2M;
	}
	commandList[cmdInx].menu[1] =
	wMenuPushCreate( tm, helpKey, nameStr, acclKey, DoCommandB, (void*)(intptr_t)cmdInx );
	if ( (options & (IC_POPUP|IC_POPUP2)) ) {
		if ( !(options & IC_SELECTED) ) {
			commandList[cmdInx].menu[2] =
			wMenuPushCreate( p1m, helpKey, nameStr, 0, DoCommandB, (void*)(intptr_t)cmdInx );
		}
		commandList[cmdInx].menu[3] =
		wMenuPushCreate( p2m, helpKey, nameStr, 0, DoCommandB, (void*)(intptr_t)cmdInx );
	}

	return cmdInx;
}


EXPORT wIndex_t InitCommand(
		wMenu_p menu,
		procCommand_t command,
		char * nameStr,
		char * bits,
		int reqLevel,
		long options,
		long acclKey )
{
	char helpKey[STR_SHORT_SIZE];
	wIcon_p icon = NULL;
	if (bits)
		icon = wIconCreateBitMap( 16, 16, bits, wDrawColorBlack );
	strcpy( helpKey, "cmd" );
	strcat( helpKey, nameStr );
	return AddMenuButton( menu, command, helpKey, _(nameStr), icon, reqLevel, options, acclKey, NULL );
}

/*--------------------------------------------------------------------*/

EXPORT void PlaybackCommand(
		char * line,
		wIndex_t lineNum )
{
	wIndex_t inx;
	wIndex_t buttInx;
	int len1, len2;
	len1 = strlen(line+8);
	for (inx=0;inx<commandCnt;inx++) {
		len2 = strlen(commandList[inx].helpKey+3);
		if (len1 == len2 && strncmp( line+8, commandList[inx].helpKey+3, len2 ) == 0) {
			break;
		}
	}
	if (inx >= commandCnt) {
		fprintf(stderr, "Unknown playback COMMAND command %d : %s\n",
			lineNum, line );
	} else {
		wPos_t cmdX, cmdY;
		if ((buttInx=commandList[inx].buttInx)>=0) {
			cmdX = buttonList[buttInx].x+17;
			cmdY = toolbarHeight - (buttonList[buttInx].y+17) +
				   (wPos_t)(mainD.size.y/mainD.scale*mainD.dpi) + 30;
			MovePlaybackCursor( &mainD, cmdX, cmdY );
		}
		if (strcmp( line+8, "Undo") == 0) {
			if (buttInx>0 && playbackTimer == 0) {
				wButtonSetBusy( (wButton_p)buttonList[buttInx].control, TRUE );
				wFlush();
				wPause( 500 );
				wButtonSetBusy( (wButton_p)buttonList[buttInx].control, FALSE );
				wFlush();
			}
			UndoUndo();
		} else if (strcmp( line+8, "Redo") == 0) {
			if (buttInx>=0 && playbackTimer == 0) {
				wButtonSetBusy( (wButton_p)buttonList[buttInx].control, TRUE );
				wFlush();
				wPause( 500 );
				wButtonSetBusy( (wButton_p)buttonList[buttInx].control, FALSE );
				wFlush();
			}
			UndoRedo();
		} else {
			if ( buttInx>=0 &&
				 playbackTimer == 0 ) {
				wButtonSetBusy( (wButton_p)buttonList[buttInx].control, TRUE );
				wFlush();
				wPause( 500 );
				wButtonSetBusy( (wButton_p)buttonList[buttInx].control, FALSE );
				wFlush();
			}
			DoCommandB( (void*)(intptr_t)inx );
		}
	}
}


/*--------------------------------------------------------------------*/
typedef struct {
		char * label;
		wMenu_p menu;
		} menuTrace_t, *menuTrace_p;
static dynArr_t menuTrace_da;
#define menuTrace(N) DYNARR_N( menuTrace_t, menuTrace_da, N )


static void DoMenuTrace(
		wMenu_p menu,
		const char * label,
		void * data )
{
	/*printf( "MENUTRACE: %s/%s\n", (char*)data, label );*/
	if (recordF) {
		fprintf( recordF, "MOUSE 1 %0.3f %0.3f\n", oldMarker.x, oldMarker.y );
		fprintf( recordF, "MENU %0.3f %0.3f \"%s\" \"%s\"\n", oldMarker.x, oldMarker.y, (char*)data, label );
	}
}


EXPORT wMenu_p MenuRegister( char * label )
{
	wMenu_p m;
	menuTrace_p mt;
	m = wMenuPopupCreate( mainW, label );
	DYNARR_APPEND( menuTrace_t, menuTrace_da, 10 );
	mt = &menuTrace( menuTrace_da.cnt-1 );
	mt->label = strdup(label);
	mt->menu = m;
	wMenuSetTraceCallBack( m, DoMenuTrace, mt->label );
	return m;
}


void MenuPlayback( char * line )
{
	char * menuName, * itemName;
	coOrd pos;
	wPos_t x, y;
	menuTrace_p mt;

	if (!GetArgs( line, "pqq", &pos, &menuName, &itemName ))
		return;
	for ( mt=&menuTrace(0); mt<&menuTrace(menuTrace_da.cnt); mt++ ) {
		if ( strcmp( mt->label, menuName ) == 0 ) {
			mainD.CoOrd2Pix( &mainD, pos, &x, &y );
			MovePlaybackCursor( &mainD, x, y );
			oldMarker = cmdMenuPos = pos;
			wMenuAction( mt->menu, _(itemName) );
			return;
		}
	}
	AbortProg( "menuPlayback: %s not found", menuName );
}
/*--------------------------------------------------------------------*/


static wWin_p stickyW;

static void StickyOk( void * );
static paramData_t stickyPLs[] = {
	{   PD_TOGGLE, &stickySet, "set", 0, stickyLabels } };
static paramGroup_t stickyPG = { "sticky", PGO_RECORD, stickyPLs, sizeof stickyPLs/sizeof stickyPLs[0] };


static void StickyOk( void * junk )
{
	wHide( stickyW );
}

static void DoSticky( void )
{
	if ( !stickyW )
		stickyW = ParamCreateDialog( &stickyPG, MakeWindowTitle(_("Sticky Commands")), _("Ok"), StickyOk, NULL, TRUE, NULL, 0, NULL );
	ParamLoadControls( &stickyPG );
	wShow( stickyW );
}
/*--------------------------------------------------------------------*/

/*
 * These array control the choices available in the Toolbar setup.
 * For each choice, the text is given and the respective mask is
 * specified in the following array.
 * Note: text and choices must be given in the same order.
 */
static char *AllToolbarLabels[] = {
		N_("File Buttons"),
		N_("Zoom Buttons"),
		N_("Undo Buttons"),
		N_("Easement Button"),
		N_("SnapGrid Buttons"),
		N_("Create Track Buttons"),
		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;
	if (set)
		toolbarSet |= mask;
	else
		toolbarSet &= ~mask;
	wPrefSetInteger( "misc", "toolbarset", toolbarSet );
	MainProc( mainW, wResize_e, NULL, NULL );
	if (recordF)
		fprintf( recordF, "PARAMETER %s %s %ld", "misc", "toolbarset", toolbarSet );
}

/**
 * Create the Toolbar configuration submenu. Based on two arrays of descriptions and
 * masks, the toolbar submenu is created dynamically.
 *
 * \param toolbarM IN menu to which the toogles will be added
 */

static void CreateToolbarM( wMenu_p toolbarM )
{
	int inx, cnt;
	long *masks;
	char **labels;
	wBool_t set;

	cnt = sizeof(AllToolbarMasks)/sizeof(AllToolbarMasks[0]);
	masks = AllToolbarMasks;
	labels = AllToolbarLabels;
	for (inx=0; inx<cnt; inx++,masks++,labels++) {
		set = ( toolbarSet & *masks ) != 0;
		wMenuToggleCreate( toolbarM, "toolbarM", _(*labels), 0, set, ToolbarAction, (void*)*masks );
	}
}

/*--------------------------------------------------------------------*/

static wWin_p addElevW;
#define addElevF (wFloat_p)addElevPD.control
EXPORT DIST_T addElevValueV;
static void DoAddElev( void * );

static paramFloatRange_t rn1000_1000 = { -1000.0, 1000.0 };
static paramData_t addElevPLs[] = {
	{   PD_FLOAT, &addElevValueV, "value", PDO_DIM, &rn1000_1000, NULL, 0 } };
static paramGroup_t addElevPG = { "addElev", 0, addElevPLs, sizeof addElevPLs/sizeof addElevPLs[0] };


static void DoAddElev( void * junk )
{
	ParamLoadData( &addElevPG );
	AddElevations( addElevValueV );
	wHide( addElevW );
}


static void ShowAddElevations( void )
{
	if ( selectedTrackCount <= 0 ) {
		ErrorMessage( MSG_NO_SELECTED_TRK );
		return;
	}
	if (addElevW == NULL)
		addElevW = ParamCreateDialog( &addElevPG, MakeWindowTitle(_("Change Elevations")), _("Change"), DoAddElev, wHide, FALSE, NULL, 0, NULL );
	wShow( addElevW );
}

/*--------------------------------------------------------------------*/

static wWin_p rotateW;
static wWin_p moveW;
static long 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 );
	rotateDialogCallBack = func;
	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 );
	moveDialogCallBack = func;
	moveValue = zero;
	wShow( 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 );
	else
		rotateDialogCallBack( (void*)-rotateValue );
	wHide( rotateW );
}


static void RotateDialogInit( void )
{
	ParamRegister( &rotatePG );
}

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 AddRotateMenu(
		wMenu_p m,
		rotateDialogCallBack_t func )
{
	wMenuPushCreate( m, "", _("180 "), 0, func, (void*)180 );
	wMenuPushCreate( m, "", _("90  CW"), 0, func, (void*)(long)(90) );
	wMenuPushCreate( m, "", _("45  CW"), 0, func, (void*)(long)(45) );
	wMenuPushCreate( m, "", _("30  CW"), 0, func, (void*)(long)(30) );
	wMenuPushCreate( m, "", _("15  CW"), 0, func, (void*)(long)(15) );
	wMenuPushCreate( m, "", _("15  CCW"), 0, func, (void*)(long)(360-15) );
	wMenuPushCreate( m, "", _("30  CCW"), 0, func, (void*)(long)(360-30) );
	wMenuPushCreate( m, "", _("45  CCW"), 0, func, (void*)(long)(360-45) );
	wMenuPushCreate( m, "", _("90  CCW"), 0, func, (void*)(long)(360-90) );
	wMenuPushCreate( m, "", _("Enter Angle ..."), 0, (wMenuCallBack_p)StartRotateDialog, (void*)func );
}

/*****************************************************************************
 *
 * INITIALIZATON
 *
 */


static wWin_p debugW;

static int debugCnt = 0;
static paramIntegerRange_t r0_100 = { 0, 100, 80 };
static void DebugOk( void * junk );
static paramData_t debugPLs[30];
static long debug_values[30];
static int debug_index[30];
static paramGroup_t debugPG = { "debug", 0, debugPLs, 0 };

static void DebugOk( void * junk )
{
	for (int i = 0; i<debugCnt;i++) {
		logTable(debug_index[i]).level = debug_values[i];
	}
	wHide( debugW );
}

static void CreateDebugW( void )
{
	debugPG.paramCnt = debugCnt;
	ParamRegister( &debugPG );
	debugW = ParamCreateDialog( &debugPG, MakeWindowTitle(_("Debug")), _("Ok"), DebugOk, NULL, FALSE, NULL, 0, NULL );
	wHide(debugW);
}

EXPORT void DebugInit(void) {

	if (!debugW) {
		BOOL_T default_line = FALSE;
		debugCnt = 0;    //Reset to start building the dynamic dialog over again
		int i = 0;
		for ( int inx=0; inx<logTable_da.cnt; inx++ ) {
			if (logTable(inx).name[0]) {
				debug_values[i] = logTable(inx).level;
				debug_index[i] = inx;
				InitDebug(logTable(inx).name,&debug_values[i]);
				i++;
			} else {
				if (!default_line) {
					debug_values[i] = logTable(inx).level;
					debug_index[i] = inx;
					InitDebug("Default Trace",&debug_values[i]);
					i++;
					default_line = TRUE;
				}
			}
		}
		//ParamCreateControls( &debugPG, NULL );
		CreateDebugW();
	}
	ParamLoadControls( &debugPG );
	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] );
	debugPLs[debugCnt].type = PD_LONG;
	debugPLs[debugCnt].valueP = valueP;
	debugPLs[debugCnt].nameStr = label;
	debugPLs[debugCnt].winData = &r0_100;
	debugPLs[debugCnt].winLabel = label;
	debugCnt++;

}


void RecomputeElevations( void );

static void MiscMenuItemCreate(
		wMenu_p m1,
		wMenu_p m2,
		char * name,
		char * label,
		long acclKey,
		void * func,
		long option,
		void * context )
{
	wMenuPush_p mp;
	mp = wMenuPushCreate( m1, name, label, acclKey, ParamMenuPush, &menuPLs[menuPG.paramCnt] );
	if ( m2 )
		wMenuPushCreate( m2, name, label, acclKey, ParamMenuPush, &menuPLs[menuPG.paramCnt] );
	menuPLs[menuPG.paramCnt].control = (wControl_p)mp;
	menuPLs[menuPG.paramCnt].type = PD_MENUITEM;
	menuPLs[menuPG.paramCnt].valueP = func;
	menuPLs[menuPG.paramCnt].nameStr = name;
	menuPLs[menuPG.paramCnt].option = option;
	menuPLs[menuPG.paramCnt].context = context;

	if ( name ) GetBalloonHelpStr( name );
	menuPG.paramCnt++;
}


static char * accelKeyNames[] = {
      "Del",
      "Ins",
      "Home",
      "End",
      "Pgup",
      "Pgdn",
      "Up",
      "Down",
      "Right",
      "Left",
      "Back",
      "F1",
      "F2",
      "F3",
      "F4",
      "F5",
      "F6",
      "F7",
      "F8",
      "F9",
      "F10",
      "F11",
      "F12",
      "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 );
}

#include "bitmaps/zoomin.xpm"
#include "bitmaps/zoom.xpm"
#include "bitmaps/zoomout.xpm"
#include "bitmaps/edit-undo.xpm"
#include "bitmaps/edit-redo.xpm"
#include "bitmaps/partlist.xpm"
#include "bitmaps/export.xpm"
#include "bitmaps/import.xpm"
#include "bitmaps/document-new.xpm"
#include "bitmaps/document-save.xpm"
#include "bitmaps/document-open.xpm"
#include "bitmaps/document-print.xpm"
#include "bitmaps/map.xpm"

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") );

	/*
	 * 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") );

	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 );

	cmdGroup = BG_ZOOM;
	zoomUpB = AddToolbarButton( "cmdZoomIn", wIconCreatePixMap(zoomin_xpm), IC_MODETRAIN_TOO,
		(addButtonCallBack_t)DoZoomUp, NULL );

	zoomM = wMenuPopupCreate( mainW, "" );
	AddToolbarButton( "cmdZoom", wIconCreatePixMap(zoom_xpm), IC_MODETRAIN_TOO, (wButtonCallBack_p)wMenuPopupShow, zoomM );

	zoomDownB = AddToolbarButton( "cmdZoomOut", wIconCreatePixMap(zoomout_xpm), IC_MODETRAIN_TOO,
		(addButtonCallBack_t)DoZoomDown, NULL );

	cmdGroup = BG_UNDO;
	undoB = AddToolbarButton( "cmdUndo", wIconCreatePixMap(edit_undo), 0, (addButtonCallBack_t)UndoUndo, NULL );
	redoB = AddToolbarButton( "cmdRedo", wIconCreatePixMap(edit_redo), 0, (addButtonCallBack_t)UndoRedo, NULL );

	wControlActive( (wControl_p)undoB, FALSE );
	wControlActive( (wControl_p)redoB, FALSE );


	/*
	 * FILE MENU
	 */
	MiscMenuItemCreate( fileM, NULL, "menuFile-clear", _("&New ..."), ACCL_NEW, (void*)(wMenuCallBack_p)DoClear, 0, (void *)0 );
	wMenuPushCreate( fileM, "menuFile-load", _("&Open ..."), ACCL_OPEN, (wMenuCallBack_p)ChkLoad, NULL );
	wMenuSeparatorCreate( fileM );

	wMenuPushCreate( fileM, "menuFile-save", _("&Save"), ACCL_SAVE, (wMenuCallBack_p)DoSave, NULL );
	wMenuPushCreate( fileM, "menuFile-saveAs", _("Save &As ..."), ACCL_SAVEAS, (wMenuCallBack_p)DoSaveAs, NULL );
	wMenuPushCreate( fileM, "menuFile-revert", _("Revert"), ACCL_REVERT, (wMenuCallBack_p)ChkRevert, NULL );
	wMenuSeparatorCreate( fileM );
	MiscMenuItemCreate( fileM, NULL, "printSetup", _("P&rint Setup ..."), ACCL_PRINTSETUP, (void*)(wMenuCallBack_p)wPrintSetup, 0, (void *)0 );
	printCmdInx = InitCmdPrint( fileM );
	wMenuSeparatorCreate( fileM );
	MiscMenuItemCreate( fileM, NULL, "cmdImport", _("&Import"), ACCL_IMPORT, (void*)(wMenuCallBack_p)DoImport, 0, (void *)0 );
	MiscMenuItemCreate( fileM, NULL, "cmdOutputbitmap", _("Export to &Bitmap"), ACCL_PRINTBM, (void*)(wMenuCallBack_p)OutputBitMapInit(), 0, (void *)0 );
	MiscMenuItemCreate( fileM, NULL, "cmdExport", _("E&xport"), ACCL_EXPORT, (void*)(wMenuCallBack_p)DoExport, IC_SELECTED, (void *)0 );
	MiscMenuItemCreate( fileM, NULL, "cmdExportDXF", _("Export D&XF"), ACCL_EXPORTDXF, (void*)(wMenuCallBack_p)DoExportDXF, IC_SELECTED, (void *)0 );
	wMenuSeparatorCreate( fileM );

	MiscMenuItemCreate( fileM, NULL, "cmdPrmfile", _("Parameter &Files ..."), ACCL_PARAMFILES, (void*)ParamFilesInit(), 0, (void *)0 );
	MiscMenuItemCreate( fileM, NULL, "cmdFileNote", _("No&tes ..."), ACCL_NOTES, (void*)(wMenuCallBack_p)DoNote, 0, (void *)0 );

	wMenuSeparatorCreate( fileM );
	fileList_ml = wMenuListCreate( fileM, "menuFileList", NUM_FILELIST, ChkFileList );
	wMenuSeparatorCreate( fileM );
	wMenuPushCreate( fileM, "menuFile-quit", _("E&xit"), 0,
		(wMenuCallBack_p)DoQuit, NULL );

	/*
	 * EDIT MENU
	 */
	MiscMenuItemCreate( editM, NULL, "cmdUndo", _("&Undo"), ACCL_UNDO, (void*)(wMenuCallBack_p)UndoUndo, 0, (void *)0 );
	MiscMenuItemCreate( editM, NULL, "cmdRedo", _("R&edo"), ACCL_REDO, (void*)(wMenuCallBack_p)UndoRedo, 0, (void *)0 );
	wMenuSeparatorCreate( editM );
	MiscMenuItemCreate( editM, NULL, "cmdCut", _("Cu&t"), ACCL_CUT, (void*)(wMenuCallBack_p)EditCut, IC_SELECTED, (void *)0 );
	MiscMenuItemCreate( editM, NULL, "cmdCopy", _("&Copy"), ACCL_COPY, (void*)(wMenuCallBack_p)EditCopy, IC_SELECTED, (void *)0 );
	MiscMenuItemCreate( editM, NULL, "cmdPaste", _("&Paste"), ACCL_PASTE, (void*)(wMenuCallBack_p)EditPaste, 0, (void *)0 );
	MiscMenuItemCreate( editM, NULL, "cmdDelete", _("De&lete"), ACCL_DELETE, (void*)(wMenuCallBack_p)SelectDelete, IC_SELECTED, (void *)0 );
	MiscMenuItemCreate( editM, NULL, "cmdMoveToCurrentLayer", _("Move To Current Layer"), ACCL_MOVCURLAYER, (void*)(wMenuCallBack_p)MoveSelectedTracksToCurrentLayer, IC_SELECTED, (void *)0 );


	wMenuSeparatorCreate( editM );
	menuPLs[menuPG.paramCnt].context = (void*)1;
	MiscMenuItemCreate( editM, NULL, "cmdSelectAll", _("Select &All"), ACCL_SELECTALL, (void*)(wMenuCallBack_p)SetAllTrackSelect, 0, (void *)1 );
	MiscMenuItemCreate( editM, NULL, "cmdSelectCurrentLayer", _("Select Current Layer"), ACCL_SETCURLAYER, (void*)(wMenuCallBack_p)SelectCurrentLayer, 0, (void *)0 );
	MiscMenuItemCreate( editM, NULL, "cmdDeselectAll", _("&Deselect All"), ACCL_DESELECTALL, (void*)(wMenuCallBack_p)SetAllTrackSelect, 0, (void *)0 );
	MiscMenuItemCreate( editM, NULL,  "cmdSelectInvert", _("&Invert Selection"), 0L, (void*)(wMenuCallBack_p)InvertTrackSelect, 0, (void *)0 );
	MiscMenuItemCreate( editM, NULL,  "cmdSelectOrphaned", _("Select Stranded Track"), 0L, (void*)(wMenuCallBack_p)OrphanedTrackSelect, 0, (void *)0 );
	wMenuSeparatorCreate( editM );
	MiscMenuItemCreate( editM, NULL, "cmdTunnel", _("Tu&nnel"), ACCL_TUNNEL, (void*)(wMenuCallBack_p)SelectTunnel, IC_SELECTED, (void *)0 );
	MiscMenuItemCreate( editM, NULL, "cmdAbove", _("A&bove"), ACCL_ABOVE, (void*)(wMenuCallBack_p)SelectAbove, IC_SELECTED, (void *)0 );
	MiscMenuItemCreate( editM, NULL, "cmdBelow", _("Belo&w"), ACCL_BELOW, (void*)(wMenuCallBack_p)SelectBelow, IC_SELECTED, (void *)0 );

	wMenuSeparatorCreate( editM );
	MiscMenuItemCreate( editM, NULL, "cmdWidth0", _("Thin Tracks"), ACCL_THIN, (void*)(wMenuCallBack_p)SelectTrackWidth, IC_SELECTED, (void *)0 );
	MiscMenuItemCreate( editM, NULL, "cmdWidth2", _("Medium Tracks"), ACCL_MEDIUM, (void*)(wMenuCallBack_p)SelectTrackWidth, IC_SELECTED, (void *)2 );
	MiscMenuItemCreate( editM, NULL, "cmdWidth3", _("Thick Tracks"), ACCL_THICK, (void*)(wMenuCallBack_p)SelectTrackWidth, IC_SELECTED, (void *)3 );

	/*
	 * VIEW MENU
	 */
	zoomInM = wMenuPushCreate( viewM, "menuEdit-zoomIn", _("Zoom &In"), ACCL_ZOOMIN, (wMenuCallBack_p)DoZoomUp, (void*)1 );
	zoomSubM = wMenuMenuCreate( viewM, "menuEdit-zoomTo", _("&Zoom") );
	zoomOutM = wMenuPushCreate( viewM, "menuEdit-zoomOut", _("Zoom &Out"), ACCL_ZOOMOUT, (wMenuCallBack_p)DoZoomDown, (void*)1 );
	wMenuSeparatorCreate( viewM );

	InitCmdZoom( zoomM, zoomSubM );

	/* these menu choices and toolbar buttons are synonymous and should be treated as such */
	wControlLinkedSet( (wControl_p)zoomInM, (wControl_p)zoomUpB );
	wControlLinkedSet( (wControl_p)zoomOutM, (wControl_p)zoomDownB );

	wMenuPushCreate( viewM, "menuEdit-redraw", _("&Redraw"), ACCL_REDRAW, (wMenuCallBack_p)MainRedraw, NULL );
	wMenuPushCreate( viewM, "menuEdit-redraw", _("Redraw All"), ACCL_REDRAWALL, (wMenuCallBack_p)DoRedraw, NULL );
	wMenuSeparatorCreate( viewM );

	snapGridEnableMI = wMenuToggleCreate( viewM, "cmdGridEnable", _("Enable SnapGrid"), ACCL_SNAPENABLE,
		0, (wMenuToggleCallBack_p)SnapGridEnable, NULL );
	snapGridShowMI = wMenuToggleCreate( viewM, "cmdGridShow", _("Show SnapGrid"), ACCL_SNAPSHOW,
		FALSE, (wMenuToggleCallBack_p)SnapGridShow, NULL );
	gridCmdInx = InitGrid( viewM );

	// 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 );

	wMenuSeparatorCreate( viewM );

	toolbarM = wMenuMenuCreate( viewM, "toolbarM", _("&Tool Bar") );
	CreateToolbarM( toolbarM );

   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);

	/*
	 * 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_CONTROL;
	InitCmdBlock( addM );
        InitCmdSwitchMotor( addM );
        InitCmdSignal( addM );
        InitCmdControl( addM );
        InitCmdSensor( addM );
        
	/*
	 * CHANGE MENU
	 */
	cmdGroup = BG_SELECT;
	InitCmdDescribe( changeM );
	InitCmdSelect( changeM );
	InitCmdPan( changeM );
	wMenuSeparatorCreate( changeM );

	cmdGroup = BG_TRKGRP;
	InitCmdMove( changeM );
	InitCmdDelete();
	InitCmdTunnel();
	InitCmdAboveBelow();

	cmdGroup = BG_TRKMOD;
	if (extraButtons)
		MiscMenuItemCreate( changeM, NULL, "loosen", _("&Loosen Tracks"), ACCL_LOOSEN, (void*)(wMenuCallBack_p)LoosenTracks, IC_SELECTED, (void *)0 );

	InitCmdModify( changeM );
	InitCmdJoin( changeM );
	InitCmdPull( changeM );
	InitCmdSplit( changeM );
	InitCmdMoveDescription( changeM );
	wMenuSeparatorCreate( changeM );

	MiscMenuItemCreate( changeM, NULL, "cmdAddElevations", _("Raise/Lower Elevations"), ACCL_CHGELEV, (void*)(wMenuCallBack_p)ShowAddElevations, IC_SELECTED, (void *)0 );
	InitCmdElevation( changeM );
	InitCmdProfile( changeM );

	MiscMenuItemCreate( changeM, NULL, "cmdClearElevations", _("Clear Elevations"), ACCL_CLRELEV, (void*)(wMenuCallBack_p)ClearElevations, IC_SELECTED, (void *)0 );
	MiscMenuItemCreate( changeM, NULL, "cmdElevation", _("Recompute Elevations"), 0, (void*)(wMenuCallBack_p)RecomputeElevations, 0, (void *)0 );
	ParamRegister( &addElevPG );

	wMenuSeparatorCreate( changeM );
	MiscMenuItemCreate( changeM, NULL, "cmdRescale", _("Change Scale"), 0, (void*)(wMenuCallBack_p)DoRescale, IC_SELECTED, (void *)0 );

	/*
	 * DRAW MENU
	 */
	cmdGroup = BG_MISCCRT;
	InitCmdDraw( drawM );
	InitCmdText( drawM );
	InitCmdNote( drawM );

	cmdGroup = BG_RULER;
	InitCmdRuler( drawM );


	/*
	 * OPTION MENU
	 */
	MiscMenuItemCreate( optionM, NULL, "cmdLayout", _("L&ayout ..."), ACCL_LAYOUTW, (void*)LayoutInit(), IC_MODETRAIN_TOO, (void *)0 );
	MiscMenuItemCreate( optionM, NULL, "cmdDisplay", _("&Display ..."), ACCL_DISPLAYW, (void*)DisplayInit(), IC_MODETRAIN_TOO, (void *)0 );
	MiscMenuItemCreate( optionM, NULL, "cmdCmdopt", _("Co&mmand ..."), ACCL_CMDOPTW, (void*)CmdoptInit(), IC_MODETRAIN_TOO, (void *)0 );
	MiscMenuItemCreate( optionM, NULL, "cmdEasement", _("&Easements ..."), ACCL_EASEW, (void*)(wMenuCallBack_p)DoEasementRedir, IC_MODETRAIN_TOO, (void *)0 );
	MiscMenuItemCreate( optionM, NULL, "fontSelW", _("&Fonts ..."), ACCL_FONTW, (void*)(wMenuCallBack_p)SelectFont, IC_MODETRAIN_TOO, (void *)0 );
	MiscMenuItemCreate( optionM, NULL, "cmdSticky", _("Stic&ky ..."), ACCL_STICKY, (void*)(wMenuCallBack_p)DoSticky, IC_MODETRAIN_TOO, (void *)0 );
	if (extraButtons) {
		menuPLs[menuPG.paramCnt].context = debugW;
		MiscMenuItemCreate( optionM, NULL, "cmdDebug", _("&Debug ..."), 0, (void*)(wMenuCallBack_p)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 );

	/*
	 * MACRO MENU
	 */
	wMenuPushCreate( macroM, "cmdRecord", _("&Record ..."), ACCL_RECORD, DoRecord, NULL );
	wMenuPushCreate( macroM, "cmdDemo", _("&Play Back ..."), ACCL_PLAYBACK, DoPlayBack, NULL );


	/*
	 * WINDOW MENU
	 */
	wMenuPushCreate( windowM, "menuWindow", _("Main window"), 0, (wMenuCallBack_p)wShow, mainW );
	winList_mi = wMenuListCreate( windowM, "menuWindow", -1, DoShowWindow );

	/*
	 * HELP MENU
	 */

	/* main help window */
	wMenuAddHelp( helpM );

	/* help on recent messages */
	wMenuSeparatorCreate( helpM );
	messageListM = wMenuMenuCreate( helpM, "menuHelpRecentMessages", _("Recent Messages") );
	messageList_ml = wMenuListCreate( messageListM, "messageListM", 10, ShowMessageHelp );
	wMenuListAdd( messageList_ml, 0, _(MESSAGE_LIST_EMPTY), NULL );

	/* tip of the day */
	wMenuSeparatorCreate( helpM );
	wMenuPushCreate( helpM, "cmdTip", _("Tip of the Day..."), 0, (wMenuCallBack_p)ShowTip, (void *)(SHOWTIP_FORCESHOW | SHOWTIP_NEXTTIP));
	demoM = wMenuMenuCreate( helpM, "cmdDemo", _("&Demos") );

	/* about window */
	wMenuSeparatorCreate( helpM );
	wMenuPushCreate( helpM, "about", _("About"), 0, (wMenuCallBack_p)CreateAboutW, NULL );

	/*
	 * MANAGE MENU
	 */

	cmdGroup = BG_TRAIN|BG_BIGGAP;
	InitCmdTrain( manageM );
	wMenuSeparatorCreate( manageM );

	InitNewTurn( wMenuMenuCreate( manageM, "cmdTurnoutNew", _("Tur&nout Designer...") ) );

        MiscMenuItemCreate( manageM, NULL, "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, "cmdCarInventory", _("Car Inventory"), ACCL_CARINV, (void*)(wMenuCallBack_p)DoCarDlg, IC_MODETRAIN_TOO, (void *)0 );

	wMenuSeparatorCreate( manageM );

	MiscMenuItemCreate( manageM, NULL, "cmdLayer", _("Layers ..."), ACCL_LAYERS, (void*)InitLayersDialog(), 0, (void *)0 );
	wMenuSeparatorCreate( manageM );

	MiscMenuItemCreate( manageM, NULL, "cmdEnumerate", _("Parts &List ..."), ACCL_PARTSLIST, (void*)(wMenuCallBack_p)EnumerateTracks, 0, (void *)0 );
	MiscMenuItemCreate( manageM, NULL, "cmdPricelist", _("Price List..."), ACCL_PRICELIST, (void*)PriceListInit(), 0, (void *)0 );

	cmdGroup = BG_LAYER|BG_BIGGAP;
	InitLayers();

	cmdGroup = BG_HOTBAR;
	InitHotBar();

#ifdef LATER
#ifdef WINDOWS
	wAttachAccelKey( wAccelKey_Pgdn, 0, (wAccelKeyCallBack_p)DoZoomUp, (void*)1 );
	wAttachAccelKey( wAccelKey_Pgup, 0, (wAccelKeyCallBack_p)DoZoomDown, (void*)1 );
	wAttachAccelKey( wAccelKey_F5, 0, (wAccelKeyCallBack_p)MainRedraw, (void*)1 );
#endif
	wAttachAccelKey( wAccelKey_Ins, WKEY_CTRL, (wAccelKeyCallBack_p)EditCopy, 0 );
	wAttachAccelKey( wAccelKey_Ins, WKEY_SHIFT, (wAccelKeyCallBack_p)EditPaste, 0 );
	wAttachAccelKey( wAccelKey_Back, WKEY_SHIFT, (wAccelKeyCallBack_p)UndoUndo, 0 );
	wAttachAccelKey( wAccelKey_Del, WKEY_SHIFT, (wAccelKeyCallBack_p)EditCut, 0 );
	wAttachAccelKey( wAccelKey_F6, 0, (wAccelKeyCallBack_p)NextWindow, 0 );
#endif
	SetAccelKey( "zoomUp", wAccelKey_Pgdn, 0, (wAccelKeyCallBack_p)DoZoomUp, (void*)1 );
	SetAccelKey( "zoomDown", wAccelKey_Pgup, 0, (wAccelKeyCallBack_p)DoZoomDown, (void*)1 );
    SetAccelKey( "redraw", wAccelKey_F5, 0, (wAccelKeyCallBack_p)MainRedraw, (void*)1 );
	SetAccelKey( "delete", wAccelKey_Del, 0, (wAccelKeyCallBack_p)SelectDelete, (void*)1 );
	SetAccelKey( "copy", wAccelKey_Ins, WKEY_CTRL, (wAccelKeyCallBack_p)EditCopy, 0 );
	SetAccelKey( "paste", wAccelKey_Ins, WKEY_SHIFT, (wAccelKeyCallBack_p)EditPaste, 0 );
	SetAccelKey( "undo", wAccelKey_Back, WKEY_SHIFT, (wAccelKeyCallBack_p)UndoUndo, 0 );
	SetAccelKey( "cut", wAccelKey_Del, WKEY_SHIFT, (wAccelKeyCallBack_p)EditCut, 0 );
	SetAccelKey( "nextWindow", wAccelKey_F6, 0, (wAccelKeyCallBack_p)NextWindow, 0 );
    SetAccelKey( "zoomUp", wAccelKey_Numpad_Add, WKEY_CTRL, (wAccelKeyCallBack_p)DoZoomUp, (void*)1 );
	SetAccelKey( "zoomDown", wAccelKey_Numpad_Subtract, WKEY_CTRL, (wAccelKeyCallBack_p)DoZoomDown, (void*)1 );

	InitBenchDialog();
}


static void LoadFileList( void )
{
	char file[6];
	int inx;
	const char * cp;
	const char *fileName, *pathName;
	strcpy( file, "fileX" );
	for (inx=NUM_FILELIST-1; inx>=0; inx--) {
		file[4] = '0'+inx;
		cp = wPrefGetString( "filelist", file );
		if (!cp)
			continue;
		pathName = MyStrdup(cp);
		fileName = FindFilename((char *)pathName);
		if (fileName)
			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 InitCmdExport( void )
{
	AddToolbarButton( "cmdExport", wIconCreatePixMap(export_xpm), IC_SELECTED|IC_ACCLKEY, (addButtonCallBack_t)DoExport, NULL );
	AddToolbarButton( "cmdImport", wIconCreatePixMap(import_xpm), IC_ACCLKEY, (addButtonCallBack_t)DoImport, NULL );
}

/* Give user the option to continue work after crash. This function gives the user
 * the option to load the checkpoint file to continue working after a crash.
 *
 * \param none
 * \return TRUE for resume, FALSE otherwise
 *
 */

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 ) {
		/* load the checkpoint file */
		LoadCheckpoint();
		ret = TRUE;
	}
	return ret;
}


EXPORT wWin_p wMain(
		int argc,
		char * argv[] )
{
	int c;
	int resumeWork;
	char * logFileName = NULL;
	int log_init = 0;
	int initialZoom = 0;
	char * initialFile = NULL;
	const char * pref;
	coOrd roomSize;
	long oldToolbarMax;
	long newToolbarMax;
	char *cp;
	char *oldLocale = NULL;
	char buffer[ STR_SIZE ];
	unsigned int i;
    wPos_t displayWidth;
    wPos_t displayHeight;

	strcpy( buffer, sProdNameLower );

	/* Initialize application name */
	wInitAppName(buffer);

	/* Initialize gettext */
	InitGettext();

	/* Save user locale */
	oldLocale = setlocale(LC_ALL, NULL);
	if (oldLocale)
		userLocale = strdup( oldLocale );

	/*
	 * ARGUMENTS
	 */

	opterr = 0;

	while ((c = getopt (argc, argv, "vl:d:c:")) != -1)
    switch (c) {
		case 'c':					/* configuration name */
			/* test for valid filename */
			for( i = 0; i < strlen( optarg ); i++ ) {
				if( !isalnum( (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 );
			}
			break;
		case 'v':					/* verbose flag  */
			verbose++;
            break;
		case 'd':					/* define loglevel for a group */
			cp = strchr( optarg, '=' );
			if ( cp != NULL ) {
				*cp++ = '\0';
				LogSet( optarg, atoi(cp) );
			} else {
				LogSet( optarg, 1 );
			}
			break;
		case 'l':					/* define log filename */
			logFileName = strdup(optarg);
			break;
        case '?':
			NoticeMessage( MSG_BAD_OPTION, _("Ok"), NULL, argv[ optind - 1 ] );
			exit( 1 );
		case ':':
			NoticeMessage( "Missing parameter for %s", _("Ok"), NULL, argv[ optind - 1 ] );
			exit( 1 );
			break;
        default:
            abort ();
    }
	if( optind < argc )
		initialFile = strdup( argv[ optind ] );

	extraButtons = ( getenv(sEnvExtra) != NULL );
	LogOpen( logFileName );
	log_init = LogFindIndex( "init" );
	log_malloc = LogFindIndex( "malloc" );
	log_error = LogFindIndex( "error" );
	log_command = LogFindIndex( "command" );

LOG1( log_init, ( "initCustom\n" ) )
	InitCustom();

	/*
	 * MAIN WINDOW
	 */
LOG1( log_init, ( "create main window\n" ) )
	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 )
		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) );
	drawColorPurple = wDrawFindColor( wRGB(255,  0,255) );
	drawColorGold   = wDrawFindColor( wRGB(255,215,  0) );
	snapGridColor = drawColorGreen;
	markerColor = drawColorRed;
	borderColor = drawColorBlack;
	crossMajorColor = drawColorRed;
	crossMinorColor = drawColorBlue;
	selectedColor = drawColorRed;
	normalColor = drawColorBlack;
	elevColorIgnore = drawColorBlue;
	elevColorDefined = drawColorGold;
	profilePathColor = drawColorPurple;
	exceptionColor = wDrawFindColor( wRGB(255,0,128) );
	tieColor = wDrawFindColor( wRGB(255,128,0) );

	newToolbarMax = (1<<BG_COUNT)-1;
	wPrefGetInteger( "misc", "toolbarset", &toolbarSet, newToolbarMax );
	wPrefGetInteger( "misc", "max-toolbarset", &oldToolbarMax, 0 );
	toolbarSet |= newToolbarMax & ~oldToolbarMax;
	wPrefSetInteger( "misc", "max-toolbarset", newToolbarMax );
	wPrefSetInteger( "misc", "toolbarset", toolbarSet );

LOG1( log_init, ( "fontInit\n"))

	wInitializeFonts();

LOG1( log_init, ( "fileInit\n" ) )
	FileInit();

	wCreateSplash( sProdName, sVersion );

	if (!initialFile) {
		WDOUBLE_T tmp;
LOG1( log_init, ( "set roomsize\n" ) )
		wPrefGetFloat( "draw", "roomsizeX", &tmp, 96.0 );
		roomSize.x = tmp;
		wPrefGetFloat( "draw", "roomsizeY", &tmp, 48.0 );
		roomSize.y = tmp;
		SetRoomSize( roomSize );
	}

	/*
	 * INITIALIZE
	 */
LOG1( log_init, ( "initInfoBar\n" ) )
	InitInfoBar();
	wSetSplashInfo( "Misc2 Init..." );
LOG1( log_init, ( "misc2Init\n" ) )
	Misc2Init();

	RotateDialogInit();
	MoveDialogInit();

	wSetSplashInfo( _("Initializing commands") );
LOG1( log_init, ( "paramInit\n" ) )
	ParamInit();
LOG1( log_init, ( "initTrkTrack\n" ) )
	InitTrkTrack();

	/*
	 * MENUS
	 */
	wSetSplashInfo( _("Initializing menus") );
LOG1( log_init, ( "createMenus\n" ) )
	CreateMenus();



LOG1( log_init, ( "initialize\n" ) )
	if (!Initialize())
		return NULL;
	ParamRegister( &menuPG );
	ParamRegister( &stickyPG );

	/* initialize the layers */
	DefaultLayerProperties();
LOG1( log_init, ( "loadFileList\n" ) )
	LoadFileList();
	AddPlaybackProc( "MENU", MenuPlayback, NULL );
	//CreateDebugW();

	/*
	 * TIDY UP
	 */
	curCommand = 0;
	commandContext = commandList[curCommand].context;

	/*
	 * READ PARAMETERS
	 */
	if (toolbarSet&(1<<BG_HOTBAR)) {
		LayoutHotBar( NULL );
	} else {
		HideHotBar();
	}
LOG1( log_init, ( "drawInit\n" ) )
	DrawInit( initialZoom );

	MacroInit();
	wSetSplashInfo( _("Reading parameter files") );
LOG1( log_init, ( "paramFileInit\n" ) )
	if (!ParamFileInit())
		return NULL;

	curCommand = describeCmdInx;
LOG1( log_init, ( "Reset\n" ) )
	Reset();

	/*
	 * SCALE
	 */

	/* Set up the data for scale and gauge description */
	DoSetScaleDesc();

	// 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 );

	/* see whether last layout should be reopened on startup */
	wPrefGetInteger( "DialogItem", "pref-onstartup", &onStartup, 0 );

	/*
	 * THE END
	 */

LOG1( log_init, ( "the end\n" ) )
	EnableCommands();
LOG1( log_init, ( "Initialization complete\n" ) )
	wSetSplashInfo( _("Initialization complete") );
	RegisterChangeNotification( ToolbarChange );
	DoChangeNotification( CHANGE_MAIN|CHANGE_MAP );

	wWinShow( mainW, TRUE );
	wWinShow( mapW, mapVisible );
	wDestroySplash();

	/* this has to be called before ShowTip() */
	InitSmallDlg();

	ShowTip(SHOWTIP_NEXTTIP);

	/* check for existing checkpoint file */
	resumeWork = FALSE;
	if (ExistsCheckpoint())
		resumeWork = OfferCheckpoint();

	if (!resumeWork) {
		/* if work is to be resumed and no filename was given on startup, load last layout */
		if ((onStartup == 0) && (!initialFile || !strlen(initialFile))) {
			initialFile = (char*)wPrefGetString("misc", "lastlayout");
		}

		if (initialFile && strlen(initialFile)) {
			DoFileList(0, NULL, initialFile);
		}
	}
	inMainW = FALSE;
	return mainW;
}