/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "misc.h" #include "cselect.h" #include "cundo.h" #include "custom.h" #include "draw.h" #include "fileio.h" #include "layout.h" #include "param.h" #include "include/paramfilelist.h" #include "paths.h" #include "smalldlg.h" #include "track.h" #include "common-ui.h" #include #define DEFAULT_SCALE ("N") /**************************************************************************** * EXPORTED VARIABLES * */ EXPORT int iconSize = 0; EXPORT wWinPix_t displayWidth; EXPORT wWinPix_t displayHeight; EXPORT wWin_p mainW; EXPORT char message[STR_HUGE_SIZE]; static char message2[STR_LONG_SIZE]; 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 */ static int verbose = 0; static BOOL_T inMainW = TRUE; EXPORT long units = 0; /**< measurement units: 0 = English, 1 = metric */ EXPORT long labelScale = 8; EXPORT long labelEnable = (LABELENABLE_ENDPT_ELEV|LABELENABLE_CARS); /** @prefs [draw] label-when=2 Unknown */ EXPORT long labelWhen = 2; EXPORT long dontHideCursor = 0; #ifdef HIDESELECTIONWINDOW EXPORT long hideSelectionWindow = 0; #endif /**************************************************************************** * * MEMORY ALLOCATION * */ static size_t totalMallocs = 0; static size_t totalMalloced = 0; static size_t totalRealloced = 0; static size_t totalReallocs = 0; static size_t totalFreeed = 0; static size_t totalFrees = 0; static void * StorageLog; typedef struct slog_t { void * storage_p; size_t storage_size; BOOL_T freed; } slog_t, * slog_p; static int StorageLogCurrent = 0; #define LOG_SIZE 1000000 static unsigned long guard0 = 0xDEADBEEF; static unsigned long guard1 = 0xAF00BA8A; static int log_malloc; static void RecordMalloc(void * p, size_t size) { if (!StorageLog) { StorageLog = malloc(sizeof(slog_t)*LOG_SIZE); } slog_p log_p = StorageLog; if (StorageLogCurrent src && isspace((unsigned char) *cp)) { cp--; } while (src <= cp) { if (*src == '"' && double_quotes) { *dst++ = '"'; } *dst++ = *src++; } *dst = '\0'; return dst; } // ??? Referenced from gtklib.browserhelp.c ??? static char * directory; EXPORT wBool_t CheckHelpTopicExists(const char * topic) { char * htmlFile; // Check the file exits in the distro if (!directory) { directory = malloc(BUFSIZ); } if (directory == NULL) { return 0; } sprintf(directory, "%s/html/", wGetAppLibDir()); htmlFile = malloc(strlen(directory)+strlen(topic) + 6); sprintf(htmlFile, "%s%s.html", directory, topic); if( access( htmlFile, F_OK ) == -1 ) { printf("Missing help topic %s\n",topic); free(htmlFile); return 0; } free(htmlFile); return 1; } static const char * ParseMessage(const 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; } MessageListAppend( cp1, _(msgSrc) ); return cp2; } else { return _(msgSrc); } } EXPORT void InfoMessage(const char * format, ...) { va_list ap; va_start(ap, format); format = ParseMessage(format); vsnprintf(message2, 1020, format, ap); va_end(ap); /*InfoSubstituteControl( NULL, NULL );*/ if (inError) { return; } SetMessage(message2); } EXPORT void ErrorMessage(const char * format, ...) { va_list ap; va_start(ap, format); format = ParseMessage(format); vsnprintf(message2, 1020, format, ap); va_end(ap); InfoSubstituteControls( NULL, NULL); SetMessage(message2); wBeep(); inError = TRUE; } EXPORT int NoticeMessage(const char * format, const char * yes, const char * no, ...) { va_list ap; va_start(ap, no); format = ParseMessage(format); vsnprintf(message2, 1020, format, ap); va_end(ap); return wNotice(message2, yes, no); } EXPORT int NoticeMessage2(int playbackRC, const char * format, const char * yes, const char * no, ...) { va_list ap; if (inPlayback) { return playbackRC; } va_start(ap, no); format = ParseMessage(format); vsnprintf(message2, 1020, format, ap); va_end(ap); return wNoticeEx( NT_INFORMATION, message2, yes, no); } /***************************************************************************** * * MAIN BUTTON HANDLERS * */ /** * Confirm a requested operation in case of possible loss of changes. * * \param label2 IN operation to be cancelled, unused at the moment * \param after IN function to be executed on positive confirmation * \return true if proceed, false if cancel operation */ /** TODO: make sensible messages when requesting confirmation */ EXPORT bool Confirm(char * label2, doSaveCallBack_p after) { int rc = -1; if (changed) { rc = wNotice3(_("Save changes to the layout design before closing?\n\n" "If you don't save now, your unsaved changes will be discarded."), _("&Save"), _("&Cancel"), _("&Don't Save")); } switch (rc) { case -1: /* do not save */ after(); break; case 0: /* cancel operation */ break; case 1: /* save */ //LayoutBackGroundInit(FALSE); //LayoutBackGroundSave(); DoSave(after); break; } return(rc != 0); } /* * Clean up before quitting */ static void DoQuitAfter(void) { changed = 0; CleanupCheckpointFiles(); //Get rid of checkpoint if we quit. CleanupTempArchive(); // removefiels used for archive handling SaveState(); } /** * 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. */ EXPORT void DoQuit(void * unused) { if (Confirm(_("Quit"), DoQuitAfter)) { #ifdef CHECK_UNUSED_BALLOONHELP ShowUnusedBalloonHelp(); #endif LogClose(); wExit(0); } } static void DoClearAfter(void) { Reset(); ClearTracks(); ResetLayers(); // set all layers to their default properties and set current layer to 0 DoLayout(NULL); checkPtMark = changed = 0; DoChangeNotification( CHANGE_MAIN|CHANGE_MAP ); bReadOnly = TRUE; EnableCommands(); SetLayoutFullPath(""); SetWindowTitle(); LayoutBackGroundInit(TRUE); } EXPORT void DoClear(void * unused) { Confirm(_("Clear"), DoClearAfter); } /** * Toggle visibility state of map window. */ EXPORT void MapWindowToggleShow(void * unused) { MapWindowShow(!mapVisible); } /** * Set visibility state of map window. * * \param state IN TRUE if visible, FALSE if hidden */ EXPORT 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); } EXPORT 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)); ParamResetInvalid( 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)); } DYNARR_RESET( wWin_p, demoWindows_da ); } 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) { } /***************************************************************************** * * ACCEL KEYS * */ enum eAccelAction_t { EA_ZOOMUP, EA_ZOOMDOWN, EA_REDRAW, EA_DELETE, EA_UNDO, EA_COPY, EA_PASTE, EA_CUT, EA_NEXT, EA_HELP }; struct accelKey_s { const char * sPrefName; wAccelKey_e eKey; int iMode; enum eAccelAction_t iAction; int iContext; } aAccelKeys[] = { { "zoomUp", wAccelKey_Pgdn, 0, EA_ZOOMUP, 1 }, { "zoomDown", wAccelKey_Pgup, 0, EA_ZOOMDOWN, 1 }, { "redraw", wAccelKey_F5, 0, EA_REDRAW, 0 }, #ifdef WINDOWS { "delete", wAccelKey_Del, 0, EA_DELETE, 0 }, #endif { "undo", wAccelKey_Back, WKEY_SHIFT, EA_UNDO, 0 }, { "copy", wAccelKey_Ins, WKEY_CTRL, EA_COPY, 0 }, { "paste", wAccelKey_Ins, WKEY_SHIFT, EA_PASTE, 0 }, { "cut", wAccelKey_Del, WKEY_SHIFT, EA_CUT, 0 }, { "nextWindow", wAccelKey_F6, 0, EA_NEXT, 0 }, { "zoomUp", wAccelKey_Numpad_Add, WKEY_CTRL, EA_ZOOMUP, 1 }, { "zoomDown", wAccelKey_Numpad_Subtract, WKEY_CTRL, EA_ZOOMDOWN, 1 }, { "help", wAccelKey_F1, WKEY_SHIFT, EA_HELP, 1 }, { "help-context", wAccelKey_F1, 0, EA_HELP, 3 } }; static void AccelKeyDispatch( wAccelKey_e key, void * accelKeyIndexVP ) { int iAccelKeyIndex = (int)VP2L(accelKeyIndexVP); switch( aAccelKeys[iAccelKeyIndex].iAction ) { case EA_ZOOMUP: DoZoomUp( I2VP(aAccelKeys[iAccelKeyIndex].iContext) ); break; case EA_ZOOMDOWN: DoZoomDown( I2VP(aAccelKeys[iAccelKeyIndex].iContext) ); break; case EA_REDRAW: MainRedraw(); break; case EA_DELETE: TrySelectDelete(); break; case EA_UNDO: UndoUndo(NULL); break; case EA_COPY: EditCopy(NULL); break; case EA_PASTE: EditPaste(NULL); break; case EA_CUT: EditCut(NULL); break; case EA_NEXT: NextWindow(); break; case EA_HELP: wDoAccelHelp(key, I2VP(aAccelKeys[iAccelKeyIndex].iContext)); break; default: CHECK(FALSE); } } 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 SetAccelKeys() { for ( int iAccelKey = 0; iAccelKey < COUNT( aAccelKeys ); iAccelKey++ ) { struct accelKey_s * akP = &aAccelKeys[iAccelKey]; int eKey = akP->eKey; int iMode = akP->iMode; const char * sPrefValue = wPrefGetString("accelKey", akP->sPrefName); if (sPrefValue != NULL) { int iMode1 = 0; while (sPrefValue[1] == '-') { switch (sPrefValue[0]) { case 'S': iMode1 |= WKEY_SHIFT; break; case 'C': iMode1 |= WKEY_CTRL; break; case 'A': iMode1 |= WKEY_ALT; break; default: ; } sPrefValue += 2; } for (int inx = 0; inx < COUNT( accelKeyNames ); inx++) { if (strcmp(sPrefValue, accelKeyNames[inx]) == 0) { eKey = inx + 1; iMode = iMode1; break; } } } wAttachAccelKey(eKey, iMode, AccelKeyDispatch, I2VP(iAccelKey)); } } //EXPORT void InitCmdEnumerate(void) { // AddToolbarButton("cmdEnumerate", wIconCreatePixMap(partlist_xpm), // IC_SELECTED | IC_ACCLKEY, EnumerateTracks, // NULL); //} /**************************************************************************** * * WMAIN * */ /* 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 = wNotice3( _( "Program was not terminated properly. Do you want to resume working on the previous trackplan?"), _("Resume"), _("Resume with New Name"), _("Ignore Checkpoint")); //ret==1 Same, ret==-1 New, ret==0 Ignore if (ret == 1) { printf(_("Reload Checkpoint Selected\n")); } else if (ret == -1) { printf(_("Reload Checkpoint With New Name Selected\n")); } else { printf(_("Ignore Checkpoint Selected\n")); } if (ret>=0) { /* load the checkpoint file */ LoadCheckpoint(ret==1); ret = TRUE; } return (ret>=0); } 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 buffer[STR_SIZE]; unsigned int i; BOOL_T bRunTests = FALSE; strcpy(buffer, sProdNameLower); /* Initialize application name */ wInitAppName(buffer); /* Initialize gettext */ InitGettext(); /* Save user locale */ SetCLocale(); SetUserLocale(); /* * ARGUMENTS */ opterr = 0; LogSet("dummy",0); while ((c = getopt(argc, argv, "vl:d:c:mVT")) != -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 'm': // temporary: use MainRedraw instead of TempRedraw wDrawDoTempDraw = FALSE; break; case ':': NoticeMessage("Missing parameter for %s", _("Ok"), NULL, argv[optind - 1]); exit(1); break; case 'V': // display version printf("Version: %s\n",XTRKCAD_VERSION); exit(0); break; case 'T': // run tests LogSet( "regression", 2 ); bRunTests = TRUE; break; default: CHECK(FALSE); } if (optind < argc) { initialFile = strdup(argv[optind]); } extraButtons = (getenv(sEnvExtra) != NULL); LogOpen(logFileName); log_init = LogFindIndex("init"); log_malloc = LogFindIndex("malloc"); 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); wGetDisplaySize(&displayWidth, &displayHeight); mainW = wWinMainCreate(buffer, (displayWidth * 2) / 3, (displayHeight * 2) / 3, "xtrkcadW", message, "main", F_RESIZE | F_MENUBAR | F_NOTAB | F_RECALLPOS | F_RECALLSIZE | F_HIDE, MainProc, NULL); if (mainW == NULL) { return NULL; } wSetGeometry(mainW, displayWidth/2, displayWidth, displayHeight/2, displayHeight, -1, -1, -1); InitAppDefaults(); 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, ( "initColor\n" )) InitColor(); LOG1(log_init, ( "initInfoBar\n" )) InitInfoBar(); wSetSplashInfo("Scale Init..."); LOG1(log_init, ( "ScaleInit\n" )) ScaleInit(); wPrefGetInteger( "draw", "label-when", &labelWhen, labelWhen ); 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(); SetAccelKeys(); LOG1(log_init, ( "initialize\n" )) if (!Initialize()) { return NULL; } LOG1(log_init, ( "loadFileList\n" )) LoadFileList(); //CreateDebugW(); /* * TIDY UP */ if (toolbarSet&(1<