/** \file dcar.c * TRAIN */ /* XTrkCad - Model Railroad CAD * Copyright (C) 2005 Dave Bullis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "cselect.h" #include "ctrain.h" #include "custom.h" #include "fileio.h" #include "layout.h" #include "param.h" #include "paths.h" #include "track.h" #include "include/paramfile.h" #include "common-ui.h" static int log_carList; static int log_carInvList; static int log_carDlgState; static int log_carDlgList; static paramFloatRange_t r0_99999 = { 0, 99999, 80 }; static paramFloatRange_t r0d001_99999 = { 0.001, 99999, 80 }; static paramFloatRange_t r9999_9999 = {-99999, 99999, 80}; static paramIntegerRange_t i1_999999999 = { 1, 999999999, 80, PDO_NORANGECHECK_HIGH }; static paramIntegerRange_t i1_9999 = { 1, 9999, 50 }; static char * isLocoLabels[] = { "", 0 }; static char * cplrModeLabels[] = { N_("Truck"), N_("Body"), 0 }; static BOOL_T carProtoListChanged; static void CarInvListAdd( carItem_p item ); static void CarInvListUpdate( carItem_p item ); #define T_MANUF (0) #define T_PROTO (1) #define T_DESC (2) #define T_PART (3) #define T_ROADNAME (4) #define T_REPMARK (5) #define T_NUMBER (6) typedef struct { char * name; long value; } nameLongMap_t; #define CAR_DESC_COUPLER_MODE_BODY (1L<<0) #define CAR_DESC_IS_LOCO (1L<<1) #define CAR_DESC_IS_LOCO_MASTER (1L<<2) #define CAR_ITEM_HASNOTES (1L<<8) #define CAR_ITEM_ONLAYOUT (1L<<9) #define CAR_DESC_BITS (0x000000FF) #define CAR_ITEM_BITS (0x0000FF00) typedef struct carProto_t * carProto_p; typedef struct { DIST_T carLength; DIST_T carWidth; DIST_T truckCenter; DIST_T truckCenterOffset; DIST_T coupledLength; } carDim_t; typedef struct { char * number; FLOAT_T purchPrice; FLOAT_T currPrice; long condition; long purchDate; long serviceDate; char * notes; } carData_t; struct carItem_t { long index; SCALEINX_T scaleInx; char * contentsLabel; char * title; carProto_p proto; DIST_T barScale; wDrawColor color; long options; long type; carDim_t dim; carData_t data; wIndex_t segCnt; trkSeg_p segPtr; track_p car; coOrd pos; ANGLE_T angle; }; /* * Utilities */ typedef struct { char * ptr; int len; } tabString_t, *tabString_p; static void TabStringExtract( char * string, int count, tabString_t * tabs ) { int inx; char * next = string; for ( inx=0; inxlen+1 ); memcpy( ret, tab->ptr, tab->len ); ret[tab->len] = '\0'; return ret; } static char * TabStringCpy( char * dst, tabString_t * tab ) { memcpy( dst, tab->ptr, tab->len ); dst[tab->len] = '\0'; return dst+tab->len; } static int TabStringCmp( char * src, tabString_t * tab ) { size_t srclen = strlen(src); size_t len = srclen; int rc; if ( len > tab->len ) len = tab->len; rc = strncasecmp( src, tab->ptr, len ); if ( rc != 0 || srclen == tab->len ) return rc; else if ( srclen > tab->len ) return 1; else return -1; } static long TabGetLong( tabString_t * tab ) { char old_c; long val; if ( tab->len <= 0 ) return 0; old_c = tab->ptr[tab->len]; tab->ptr[tab->len] = '\0'; val = atol( tab->ptr ); tab->ptr[tab->len] = old_c; return val; } static FLOAT_T TabGetFloat( tabString_t * tab ) { char old_c; FLOAT_T val; if ( tab->len <= 0 ) return 0.0; old_c = tab->ptr[tab->len]; tab->ptr[tab->len] = '\0'; val = atof( tab->ptr ); tab->ptr[tab->len] = old_c; return val; } static void RotatePts( int cnt, coOrd * pts, coOrd orig, ANGLE_T angle ) { int inx; for ( inx=0; inxcnt-1; while (lo <= hi ) { mid = (lo+hi)/2; rc = cmpFunc( key, DYNARR_N(void*,*da,mid) ); if ( rc == 0 ) { lookupListIndex = mid; return DYNARR_N(void*,*da,mid); } if ( rc > 0 ) lo = mid+1; else hi = mid-1; } if ( elem_size == 0 ) { lookupListIndex = -1; return NULL; } DYNARR_APPEND( void*, *da, 10 ); for ( mid=da->cnt-1; mid>lo; mid-- ) DYNARR_N(void*,*da,mid) = DYNARR_N(void*,*da,mid-1); DYNARR_N(void*,*da,lo) = MyMalloc(elem_size); memset( DYNARR_N(void*,*da,lo), 0, elem_size ); lookupListIndex = lo; return DYNARR_N(void*,*da,lo); } static void RemoveListElem( dynArr_t * da, void * elem ) { int inx; for ( inx=0; inxcnt; inx++ ) if ( DYNARR_N(void*,*da,inx) == elem ) break; if ( inx>=da->cnt ) AbortProg( "removeListElem" ); for ( inx++; inxcnt; inx++ ) DYNARR_N(void*,*da,inx-1) = DYNARR_N(void*,*da,inx); da->cnt--; } /* * Draw Car Parts */ #define BW (8) #define TW (45) #define SI (30) #define SO (37) static coOrd truckOutline[] = { { -TW, -SO }, { TW, -SO }, { TW, -SI }, { BW, -SI }, { BW, SI }, { TW, SI }, { TW, SO }, { -TW, SO }, { -TW, SI }, { -BW, SI }, { -BW, -SI }, { -TW, -SI } }; #define WO ((56.6-2)/2) #define WI ((56.6-12)/2) #define Wd (36/2) #define AW (8/2) static coOrd wheelOutline[] = { { -Wd, -WO }, { -AW, -WO }, { -AW, -SI }, { AW, -SI }, { AW, -WO }, { Wd, -WO }, { Wd, -WI }, { AW, -WI }, { AW, WI }, { Wd, WI }, { Wd, WO }, { AW, WO }, { AW, SI }, { -AW, SI }, { -AW, WO }, { -Wd, WO }, { -Wd, WI }, { -AW, WI }, { -AW, -WI }, { -Wd, -WI } }; static void MovePts( int cnt, coOrd * pts, coOrd orig ) { int inx; for ( inx=0; inx 6 then draw Sills */ MovePts( COUNT( couplerOutline ), p, pp ); RescalePts( COUNT( couplerOutline ), p, ratio, ratio ); RotatePts( COUNT( couplerOutline ), p, zero, angle-90.0 ); MovePts( COUNT( couplerOutline ), p, pos ); DrawPoly( d, COUNT( couplerOutline ), p, NULL, color, 0, DRAW_FILL ); } /* * Car Proto */ struct carProto_t; typedef struct carProto_t carProto_t; struct carProto_t { char * contentsLabel; wIndex_t paramFileIndex; char * desc; long options; long type; carDim_t dim; int segCnt; trkSeg_p segPtr; coOrd size; coOrd orig; }; static dynArr_t carProto_da; #define carProto(N) DYNARR_N( carProto_t*, carProto_da, N ) #define N_TYPELISTMAP (7) static nameLongMap_t typeListMap[N_TYPELISTMAP] = { { N_("Diesel Loco"), 10101 }, { N_("Steam Loco"), 10201 }, { N_("Elect Loco"), 10301 }, { N_("Freight Car"), 30100 }, { N_("Psngr Car"), 50100 }, { N_("M-O-W"), 70100 }, { N_("Other"), 90100 } }; static trkSeg_p carProtoSegPtr; static int carProtoSegCnt; static pts_t dummyOutlineSegPts[5]; static trkSeg_t dummyOutlineSegs; static void CarProtoDlgCreateDummyOutline( int * segCntP, trkSeg_p * segPtrP, BOOL_T isLoco, DIST_T length, DIST_T width, wDrawColor color ) { trkSeg_p segPtr; pts_t * pts; DIST_T length2; *segCntP = 1; segPtr = *segPtrP = &dummyOutlineSegs; segPtr->type = SEG_FILPOLY; segPtr->color = color; segPtr->width = 0; segPtr->u.p.cnt = isLoco?5:4; segPtr->u.p.pts = pts = dummyOutlineSegPts; segPtr->u.p.orig.x = 0; segPtr->u.p.orig.y = 0; segPtr->u.p.angle = 0; length2 = length; if ( isLoco ) { pts->pt.x = length; pts->pt.y = width/2.0; pts->pt_type = 0; pts++; length2 -= width/2.0; } pts->pt.x = length2; pts->pt.y = 0.0; pts->pt_type = 0; pts++; pts->pt.x = 0.0; pts->pt.y = 0.0; pts->pt_type = 0; pts++; pts->pt.x = 0.0; pts->pt.y = width; pts->pt_type = 0; pts++; pts->pt.x = length2; pts->pt.y = width; pts->pt_type = 0; } static int CarProtoFindTypeCode( long code ) { int inx; for ( inx=0; inx code ) { if ( inx == 0 ) return N_TYPELISTMAP-1; else return inx-1; } } return N_TYPELISTMAP-1; } static int CmpCarProto( void * key, void * elem ) { char * key_val=key; carProto_p elem_val=elem; return strcasecmp( key_val, elem_val->desc ); } static carProto_p CarProtoFind( char * desc ) { return LookupListElem( &carProto_da, desc, CmpCarProto, 0 ); } static carProto_p CarProtoLookup( char * desc, BOOL_T createMissing, BOOL_T isLoco, DIST_T length, DIST_T width ) { carProto_p proto; trkSeg_p segPtr; proto = LookupListElem( &carProto_da, desc, CmpCarProto, createMissing?sizeof *proto:0 ); if ( proto == NULL ) return NULL; if ( proto->desc == NULL ) { proto->desc = MyStrdup(desc); proto->contentsLabel = "Car Prototype"; proto->paramFileIndex = PARAM_LAYOUT; proto->options = (isLoco?CAR_DESC_IS_LOCO:0); proto->dim.carLength = length; proto->dim.carWidth = width; proto->dim.truckCenter = length - 2.0*59.0; proto->dim.coupledLength = length + 2.0*16.0; CarProtoDlgCreateDummyOutline( &proto->segCnt, &segPtr, isLoco, length, width, drawColorBlue ); proto->segPtr = (trkSeg_p)memdup( segPtr, (sizeof *(trkSeg_p)0) * proto->segCnt ); CloneFilledDraw( proto->segCnt, proto->segPtr, FALSE ); GetSegBounds( zero, 0.0, proto->segCnt, proto->segPtr, &proto->orig, &proto->size ); carProtoListChanged = TRUE; // return proto; } return proto; } enum paramFileState GetCarProtoCompatibility(int paramFileIndex, SCALEINX_T scaleIndex) { int i; enum paramFileState ret = PARAMFILE_NOTUSABLE; DIST_T ratio = GetScaleRatio(scaleIndex); if (!IsParamValid(paramFileIndex)) { return(PARAMFILE_UNLOADED); } for (i = 0; i < carProto_da.cnt; i++) { carProto_t *carProto = carProto(i); if (carProto->paramFileIndex == paramFileIndex) { ret = PARAMFILE_FIT; break; } } return(ret); } static carProto_p CarProtoNew( carProto_p proto, int paramFileIndex, char * desc, long options, long type, carDim_t * dim, wIndex_t segCnt, trkSeg_p segPtr ) { if ( proto == NULL ) { proto = LookupListElem( &carProto_da, desc, CmpCarProto, sizeof *proto ); if ( proto->desc != NULL ) { if ( proto->paramFileIndex == PARAM_CUSTOM && paramFileIndex != PARAM_CUSTOM ) return proto; } } if ( proto->desc != NULL ) { MyFree( proto->desc ); } proto->desc = MyStrdup(desc); proto->contentsLabel = "Car Prototype"; proto->paramFileIndex = paramFileIndex; proto->options = options; proto->type = type; proto->dim = *dim; proto->segCnt = segCnt; //if (proto->segPtr) Can't do this because segPtr could be static // free(proto->segPtr); proto->segPtr = (trkSeg_p)memdup( segPtr, (sizeof *(trkSeg_p)0) * proto->segCnt ); CloneFilledDraw( proto->segCnt, proto->segPtr, FALSE ); GetSegBounds( zero, 0.0, proto->segCnt, proto->segPtr, &proto->orig, &proto->size ); carProtoListChanged = TRUE; return proto; } static void CarProtoDelete( carProto_p protoP ) { if ( protoP == NULL ) return; RemoveListElem( &carProto_da, protoP ); if ( protoP->desc ) MyFree( protoP->desc ); MyFree( protoP ); } /** * Delete all car prototype definitions that came from a specific parameter file. * Due to the way the definitions are loaded from file it is safe to * assume that they form a contiguous block in the array. * * \param [IN] fileIndex parameter file */ void DeleteCarProto(int fileIndex) { int inx = 0; int startInx = -1; int cnt = 0; // go to the start of the block while (inx < carProto_da.cnt && carProto(inx)->paramFileIndex != fileIndex) { startInx = inx++; } // delete them for (; inx < carProto_da.cnt && carProto(inx)->paramFileIndex == fileIndex; inx++) { carProto_t * cp = carProto(inx); if (cp->paramFileIndex == fileIndex) { CarProtoDelete(cp); cnt++; } } // copy down the rest of the list to fill the gap startInx++; while (inx < carProto_da.cnt) { carProto(startInx++) = carProto(inx++); } // and reduce the actual number carProto_da.cnt -= cnt; } static BOOL_T CarProtoRead( char * line ) { char * desc; long options; long type; carDim_t dim; long longCenterOffset; if ( !GetArgs( line+9, "qllff0lff", &desc, &options, &type, &dim.carLength, &dim.carWidth, &longCenterOffset, &dim.truckCenter, &dim.coupledLength ) ) return FALSE; dim.truckCenterOffset = longCenterOffset/1000.0; if ( !ReadSegs() ) return FALSE; CarProtoNew( NULL, curParamFileIndex, desc, options, type, &dim, tempSegs_da.cnt, &tempSegs(0) ); FreeFilledDraw(tempSegs_da.cnt,&tempSegs(0)); MyFree(desc); return TRUE; } static BOOL_T CarProtoWrite( FILE * f, carProto_t * proto ) { BOOL_T rc = TRUE; SetCLocale(); long longCenterOffset = (long)(proto->dim.truckCenterOffset*1000); rc &= fprintf( f, "CARPROTO \"%s\" %ld %ld %0.3f %0.3f 0 %ld %0.3f %0.3f\n", PutTitle(proto->desc), proto->options, proto->type, proto->dim.carLength, proto->dim.carWidth, longCenterOffset, proto->dim.truckCenter, proto->dim.coupledLength )>0; rc &= WriteSegs( f, proto->segCnt, proto->segPtr ); SetUserLocale(); return rc; } static BOOL_T CarProtoCustomSave( FILE * f ) { int inx; carProto_t * proto; BOOL_T rc = TRUE; for ( inx=0; inxparamFileIndex == PARAM_CUSTOM ) rc &= CarProtoWrite( f, proto ); } return rc; } /* * Car Desc */ struct carPart_t; typedef struct carPart_t carPart_t; typedef carPart_t * carPart_p; struct carPartParent_t; typedef struct carPartParent_t carPartParent_t; typedef carPartParent_t * carPartParent_p; typedef struct { char * name; int len; } cmp_key_t; typedef struct { tabString_t manuf; tabString_t proto; SCALEINX_T scale; } cmp_partparent_t; struct carPartParent_t { char * manuf; char * proto; SCALEINX_T scale; dynArr_t parts_da; }; struct carPart_t { carPartParent_p parent; wIndex_t paramFileIndex; char * title; long options; long type; carDim_t dim; wDrawColor color; char * partnoP; int partnoL; }; static dynArr_t carPartParent_da; #define carPartParent(N) DYNARR_N(carPartParent_p, carPartParent_da, N) #define carPart(P,N) DYNARR_N(carPart_p, (P)->parts_da, N) struct roadnameMap_t; typedef struct roadnameMap_t roadnameMap_t; typedef roadnameMap_t * roadnameMap_p; struct roadnameMap_t { char * roadname; char * repmark; }; static dynArr_t roadnameMap_da; #define roadnameMap(N) DYNARR_N(roadnameMap_p, roadnameMap_da, N) static BOOL_T roadnameMapChanged; static long carPartChangeLevel = 0; static int Cmp_part( void * key, void * elem ) { carPart_p cmp_key=key; carPart_p part_elem=elem; int rc; int len; len = min( cmp_key->partnoL, part_elem->partnoL ); rc = strncasecmp( cmp_key->partnoP, part_elem->partnoP, len+1 ); if ( rc != 0 ) return rc; if ( cmp_key->paramFileIndex == part_elem->paramFileIndex ) return 0; if ( cmp_key->paramFileIndex == PARAM_DEMO ) return -1; if ( part_elem->paramFileIndex == PARAM_DEMO ) return 1; if ( cmp_key->paramFileIndex == PARAM_CUSTOM ) return -1; if ( part_elem->paramFileIndex == PARAM_CUSTOM ) return 1; if ( cmp_key->paramFileIndex == PARAM_LAYOUT ) return 1; if ( part_elem->paramFileIndex == PARAM_LAYOUT ) return -1; if ( cmp_key->paramFileIndex > part_elem->paramFileIndex ) return -1; else return 1; } static int Cmp_partparent( void * key, void * elem ) { cmp_partparent_t * cmp_key=key; carPartParent_p part_elem=elem; int rc; rc = - TabStringCmp( part_elem->manuf, &cmp_key->manuf ); if ( rc != 0 ) return rc; rc = cmp_key->scale - part_elem->scale; if ( rc != 0 ) return rc; rc = - TabStringCmp( part_elem->proto, &cmp_key->proto ); return rc; } static int Cmp_roadnameMap( void * key, void * elem ) { cmp_key_t * cmp_key=key; roadnameMap_p roadname_elem=elem; int rc; rc = strncasecmp( cmp_key->name, roadname_elem->roadname, cmp_key->len ); if ( rc == 0 && roadname_elem->roadname[cmp_key->len] ) return -1; return rc; } static roadnameMap_p LoadRoadnameList( tabString_p roadnameTab, tabString_p repmarkTab ) { cmp_key_t cmp_key; roadnameMap_p roadnameMapP; lookupListIndex = -1; if ( roadnameTab->len<=0 ) return NULL; if ( TabStringCmp( "undecorated", roadnameTab ) == 0 ) return NULL; cmp_key.name = roadnameTab->ptr; cmp_key.len = roadnameTab->len; roadnameMapP = LookupListElem( &roadnameMap_da, &cmp_key, Cmp_roadnameMap, sizeof (roadnameMap_t) ); if ( roadnameMapP->roadname == NULL ) { roadnameMapP->roadname = TabStringDup(roadnameTab); roadnameMapP->repmark = TabStringDup(repmarkTab); roadnameMapChanged = TRUE; } else if ( repmarkTab->len > 0 && ( roadnameMapP->repmark == NULL || roadnameMapP->repmark[0] == '\0' ) ) { roadnameMapP->repmark = TabStringDup(repmarkTab); roadnameMapChanged = TRUE; } return roadnameMapP; } static carPart_p CarPartFind( char * manufP, int manufL, char * partnoP, int partnoL, SCALEINX_T scale ) { wIndex_t inx1, inx2; carPart_p partP; carPartParent_p parentP; for ( inx1=0; inx1manuf) && strncasecmp( manufP, parentP->manuf, manufL ) == 0 && scale == parentP->scale ) { for ( inx2=0; inx2parts_da.cnt; inx2++ ) { partP = carPart( parentP, inx2 ); if ( partnoL == partP->partnoL && strncasecmp( partnoP, partP->partnoP, partnoL ) == 0 ) { return partP; } } } } return NULL; } static void CarPartParentDelete( carPartParent_p parentP ) { RemoveListElem( &carPartParent_da, parentP ); MyFree( parentP->manuf ); MyFree( parentP->proto ); MyFree( parentP ); } static void CarPartUnlink( carPart_p partP ) { carPartParent_p parentP = partP->parent; RemoveListElem( &parentP->parts_da, partP ); if ( parentP->parts_da.cnt <= 0 ) { CarPartParentDelete( parentP ); } } static carPartParent_p CarPartParentNew( char * manufP, int manufL, char *protoP, int protoL, SCALEINX_T scale ) { carPartParent_p parentP; cmp_partparent_t cmp_key; cmp_key.manuf.ptr = manufP; cmp_key.manuf.len = manufL; cmp_key.proto.ptr = protoP; cmp_key.proto.len = protoL; cmp_key.scale = scale; parentP = (carPartParent_p)LookupListElem( &carPartParent_da, &cmp_key, Cmp_partparent, sizeof * parentP); if ( parentP->manuf == NULL ) { parentP->manuf = (char*)MyMalloc( manufL+1 ); memcpy( parentP->manuf, manufP, manufL ); parentP->manuf[manufL] = '\0'; parentP->proto = (char*)MyMalloc( protoL+1 ); memcpy( parentP->proto, protoP, protoL ); parentP->proto[protoL] = '\0'; parentP->scale = scale; } return parentP; } static carPart_p CarPartNew( carPart_p partP, int paramFileIndex, SCALEINX_T scaleInx, char * title, long options, long type, carDim_t *dim, wDrawColor color) { carPartParent_p parentP; carPart_t cmp_key; tabString_t tabs[7]; TabStringExtract(title, 7, tabs); if (TabStringCmp("Undecorated", &tabs[T_MANUF]) == 0 || TabStringCmp("Custom", &tabs[T_MANUF]) == 0 || tabs[T_PART].len == 0) return NULL; if (tabs[T_PROTO].len == 0) return NULL; if (partP == NULL) { partP = CarPartFind(tabs[T_MANUF].ptr, tabs[T_MANUF].len, tabs[T_PART].ptr, tabs[T_PART].len, scaleInx); if (partP != NULL && partP->paramFileIndex == PARAM_CUSTOM && paramFileIndex != PARAM_CUSTOM) return partP; LOG(log_carList, 2, ("new car part: %s (%d) at %d\n", title, paramFileIndex, lookupListIndex)) } if (partP != NULL) { CarPartUnlink(partP); if (partP->title != NULL) MyFree(partP->title); LOG(log_carList, 2, ("upd car part: %s (%d)\n", title, paramFileIndex)) } LoadRoadnameList(&tabs[T_ROADNAME], &tabs[T_REPMARK]); parentP = CarPartParentNew(tabs[T_MANUF].ptr, tabs[T_MANUF].len, tabs[T_PROTO].ptr, tabs[T_PROTO].len, scaleInx); cmp_key.title = title; cmp_key.parent = parentP; cmp_key.paramFileIndex = paramFileIndex; cmp_key.options = options; cmp_key.type = type; cmp_key.dim = *dim; cmp_key.color = color; cmp_key.partnoP = tabs[T_PART].ptr; cmp_key.partnoL = tabs[T_PART].len; partP = (carPart_p)LookupListElem(&parentP->parts_da, &cmp_key, Cmp_part, sizeof * partP); if (partP->title != NULL) MyFree(partP->title); *partP = cmp_key; sprintf(message, "\t\t%s", tabs[2].ptr); partP->title = MyStrdup(message); partP->partnoP = partP->title + 2 + tabs[2].len + 1;; partP->partnoL = tabs[T_PART].len; return partP; } static void CarPartDelete( carPart_p partP ) { if ( partP == NULL ) return; CarPartUnlink( partP ); if ( partP->title ) MyFree( partP->title ); MyFree( partP ); } /** * Delete all car part definitions that came from a specific parameter file. * CarParts are stored in DYNARR for the specific car model. These DYNARRs * are linked from CarPartParents, again DYNARRs. Thes parents are created * from part definition and only contain manufacturer and type information. * * \param [IN] fileIndex parameter file */ void DeleteCarPart(int fileIndex) { int inxParent = 0; int inx; while (inxParent < carPartParent_da.cnt) { inx = 0; while (inx < carPartParent(inxParent)->parts_da.cnt) { carPart_p part = carPart(carPartParent(inxParent), inx++); if (part->paramFileIndex == fileIndex) { CarPartDelete(part); } } inxParent++; } } static BOOL_T CarPartRead( char * line ) { char scale[10]; long options; long type; char * title; carDim_t dim; long rgb; long longCenterOffset; if ( !GetArgs( line+8, "sqllff0lffl", scale, &title, &options, &type, &dim.carLength, &dim.carWidth, &longCenterOffset, &dim.truckCenter, &dim.coupledLength, &rgb ) ) return FALSE; dim.truckCenterOffset = longCenterOffset/1000.0; CarPartNew( NULL, curParamFileIndex, LookupScale(scale), title, options, type, &dim, wDrawFindColor(rgb) ); MyFree( title ); return TRUE; } static BOOL_T CarPartWrite( FILE * f, carPart_p partP ) { BOOL_T rc = TRUE; carPartParent_p parentP=partP->parent; tabString_t tabs[7]; SetCLocale(); TabStringExtract( partP->title, 7, tabs ); sprintf( message, "%s\t%s\t%.*s\t%.*s\t%.*s\t%.*s\t%.*s", parentP->manuf, parentP->proto, tabs[T_DESC].len, tabs[T_DESC].ptr, tabs[T_PART].len, tabs[T_PART].ptr, tabs[T_ROADNAME].len, tabs[T_ROADNAME].ptr, tabs[T_REPMARK].len, tabs[T_REPMARK].ptr, tabs[T_NUMBER].len, tabs[T_NUMBER].ptr ); rc &= fprintf( f, "CARPART %s \"%s\"", GetScaleName(partP->parent->scale), PutTitle(message) )>0; rc &= fprintf( f, " %ld %ld %0.3f %0.3f 0 0 %0.3f %0.3f %ld\n", partP->options, partP->type, partP->dim.carLength, partP->dim.carWidth, partP->dim.truckCenter, partP->dim.coupledLength, wDrawGetRGB(partP->color) )>0; SetUserLocale(); return rc; } static BOOL_T CarDescCustomSave( FILE * f ) { int parentX; carPartParent_p parentP; int partX; carPart_p partP; BOOL_T rc = TRUE; for ( parentX=0; parentXparts_da.cnt; partX++ ) { partP = carPart(parentP,partX); if ( partP->paramFileIndex == PARAM_CUSTOM ) rc &= CarPartWrite(f, partP ); } } return rc; } /* * Car Item */ static dynArr_t carItemInfo_da; #define carItemInfo(N) DYNARR_N( carItem_t*, carItemInfo_da, N ) #define N_CONDLISTMAP (6) static nameLongMap_t condListMap[N_CONDLISTMAP] = { { N_("N/A"), 0 }, { N_("Mint"), 100 }, { N_("Excellent"), 80 }, { N_("Good"), 60 }, { N_("Fair"), 40 }, { N_("Poor"), 20 } }; static wIndex_t MapCondition( long conditionValue ) { if ( conditionValue < 10 ) return 0; else if ( conditionValue < 30 ) return 5; else if ( conditionValue < 50 ) return 4; else if ( conditionValue < 70 ) return 3; else if ( conditionValue < 90 ) return 2; else return 1; } static carItem_p CarItemNew( carItem_p item, int paramFileIndex, long itemIndex, SCALEINX_T scale, char * title, long options, long type, carDim_t *dim, wDrawColor color, FLOAT_T purchPrice, FLOAT_T currPrice, long condition, long purchDate, long serviceDate ) { carPart_p partP; tabString_t tabs[7]; TabStringExtract( title, 7, tabs ); if ( paramFileIndex != PARAM_CUSTOM ) { partP = CarPartFind( tabs[T_MANUF].ptr, tabs[T_MANUF].len, tabs[T_PART].ptr, tabs[T_PART].len, scale ); if ( partP == NULL ) { CarPartNew( NULL, PARAM_LAYOUT, scale, title, options, type, dim, color ); } } if ( item == NULL ) { DYNARR_APPEND( carItem_t*, carItemInfo_da, 10 ); item = (carItem_t*)MyMalloc( sizeof * item ); carItemInfo(carItemInfo_da.cnt-1) = item; } else { if ( item->title ) MyFree( item->title ); if ( item->data.number ) MyFree( item->data.number ); } item->index = itemIndex; item->scaleInx = scale; item->title = MyStrdup(title); item->contentsLabel = "Car Item"; item->barScale = curBarScale>0?curBarScale:(60.0*12.0/curScaleRatio); item->options = options; item->type = type; item->dim = *dim; item->color = color; if ( tabs[T_REPMARK].len>0 || tabs[T_NUMBER].len>0 ) { sprintf( message, "%.*s%s%.*s", tabs[T_REPMARK].len, tabs[T_REPMARK].ptr, (tabs[T_REPMARK].len>0&&tabs[T_NUMBER].len>0)?" ":"", tabs[T_NUMBER].len, tabs[T_NUMBER].ptr ); } else { sprintf( message, "#%ld", item->index ); } item->data.number = MyStrdup( message ); item->data.purchPrice = purchPrice; item->data.currPrice = currPrice; item->data.condition = condition; item->data.purchDate = purchDate; item->data.serviceDate = serviceDate; item->data.notes = NULL; item->segCnt = 0; item->segPtr = NULL; LoadRoadnameList( &tabs[T_ROADNAME], &tabs[T_REPMARK] ); return item; } /** * Check the whether the parameter file has CARPARTS that are a fit or compatible * with the current state. * * \param paramFileIndex IN the parameter file * \param scaleIndex IN the scale to check against * \return the compatibility state of the the */ enum paramFileState GetCarPartCompatibility(int paramFileIndex, SCALEINX_T scaleIndex) { int i; enum paramFileState ret = PARAMFILE_NOTUSABLE; DIST_T ratio = GetScaleRatio(scaleIndex); DIST_T gauge = GetScaleTrackGauge(scaleIndex); if (!IsParamValid(paramFileIndex)) { return(PARAMFILE_UNLOADED); } for (i = 0; i < carPartParent_da.cnt && ret != PARAMFILE_FIT; i++) { carPartParent_t *carPartParent = carPartParent( i ); SCALE_FIT_T fit = CompatibleScale(FIT_CAR,carPartParent->scale,scaleIndex); if(fit == FIT_EXACT) { for(int j = 0; j < carPartParent->parts_da.cnt; j++ ) { carPart_t *carPart = carPart( carPartParent, j ); if (carPart->paramFileIndex == paramFileIndex) { ret = PARAMFILE_FIT; break; } } } if (fit == FIT_COMPATIBLE) { ret = PARAMFILE_COMPATIBLE; } } return(ret); } EXPORT BOOL_T CarItemRead( char * line ) { long itemIndex; char scale[10]; char * title; long options; long type; carDim_t dim; long rgb; FLOAT_T purchPrice = 0; FLOAT_T currPrice = 0; long condition = 0; long purchDate = 0; long serviceDate = 0; carItem_p item; char * cp; wIndex_t layer; coOrd pos; ANGLE_T angle; wIndex_t index; long longCenterOffset; char * sNote = NULL; if ( !GetArgs( line+4, "lsqll" "ff0lffl" "fflll000000c", &itemIndex, scale, &title, &options, &type, &dim.carLength, &dim.carWidth, &longCenterOffset, &dim.truckCenter, &dim.coupledLength, &rgb, &purchPrice, &currPrice, &condition, &purchDate, &serviceDate, &cp ) ) return FALSE; dim.truckCenterOffset = longCenterOffset/1000.0; if ( paramVersion < VERSION_INLINENOTE ) { if ( (options&CAR_ITEM_HASNOTES) ) { sNote = ReadMultilineText(); } } else { if ( !GetArgs( cp, "qc", &sNote, &cp ) ) return FALSE; } item = CarItemNew( NULL, curParamFileIndex, itemIndex, LookupScale(scale), title, options&(CAR_DESC_BITS|CAR_ITEM_BITS), type, &dim, wDrawFindColor(rgb), purchPrice, currPrice, condition, purchDate, serviceDate ); if ( (options&CAR_ITEM_HASNOTES) ) item->data.notes = sNote; MyFree(title); if ( (options&CAR_ITEM_ONLAYOUT) ) { if ( !GetArgs( cp, "dLpf", &index, &layer, &pos, &angle ) ) return FALSE; if ( !ReadSegs() ) return FALSE; item->car = NewCar( index, item, pos, angle ); SetTrkLayer( item->car, layer ); SetEndPts( item->car, 2 ); ComputeBoundingBox( item->car ); } return TRUE; } static BOOL_T CarItemWrite( FILE * f, carItem_t * item, BOOL_T layout ) { long options = (item->options&CAR_DESC_BITS); coOrd pos; ANGLE_T angle; BOOL_T rc = TRUE; long longCenterOffset = (long)(item->dim.truckCenterOffset*1000); SetCLocale(); if ( item->data.notes && item->data.notes[0] ) options |= CAR_ITEM_HASNOTES; if ( layout && item->car && !IsTrackDeleted(item->car) ) options |= CAR_ITEM_ONLAYOUT; rc &= fprintf( f, "CAR %ld %s \"%s\" %ld %ld %0.3f %0.3f 0 %ld %0.3f %0.3f %ld %0.3f %0.3f %ld %ld %ld 0 0 0 0 0 0", item->index, GetScaleName(item->scaleInx), PutTitle(item->title), options, item->type, item->dim.carLength, item->dim.carWidth, longCenterOffset, item->dim.truckCenter, item->dim.coupledLength, wDrawGetRGB(item->color), item->data.purchPrice, item->data.currPrice, item->data.condition, item->data.purchDate, item->data.serviceDate )>0; if ( (options&CAR_ITEM_HASNOTES) ) { char * sEscapedNote = ConvertToEscapedText( item->data.notes ); rc &= fprintf( f, " \"%s\"", sEscapedNote )>0; MyFree( sEscapedNote ); } else { rc &= fprintf( f, " \"\"" ) > 0; } if ( ( options&CAR_ITEM_ONLAYOUT) ) { CarGetPos( item->car, &pos, &angle ); rc &= fprintf( f, " %d %u %0.3f %0.3f %0.3f\n", GetTrkIndex(item->car), GetTrkLayer(item->car), pos.x, pos.y, angle )>0; rc &= WriteEndPt( f, item->car, 0 ); rc &= WriteEndPt( f, item->car, 1 ); rc &= fprintf( f, "\t%s\n", END_SEGS )>0; } else { rc &= fprintf( f, "\n" )>0; } SetUserLocale(); return rc; } EXPORT carItem_p CarItemFind( long itemInx ) { if ( itemInx >= 0 && itemInx < carItemInfo_da.cnt ) return carItemInfo(itemInx); else return NULL; } EXPORT long CarItemFindIndex( carItem_p item ) { long inx; for ( inx=0; inxscaleInx); TabStringExtract( item->title, 7, tabs ); TabStringCpy( message, &tabs[T_PROTO] ); protoP = CarProtoLookup( message, FALSE, FALSE, 0.0, 0.0 ); if ( protoP != NULL ) { item->segCnt = protoP->segCnt; segPtr = protoP->segPtr; orig = protoP->orig; } else { CarProtoDlgCreateDummyOutline( &item->segCnt, &segPtr, (item->options&CAR_DESC_IS_LOCO)!=0, item->dim.carLength, item->dim.carWidth, item->color ); orig = zero; } item->segPtr = (trkSeg_p)MyMalloc( item->segCnt * sizeof *(segPtr) ); memcpy( item->segPtr, segPtr, item->segCnt * sizeof *(segPtr) ); CloneFilledDraw( item->segCnt, item->segPtr, FALSE ); if ( protoP ) { orig.x = -orig.x; orig.y = -orig.y; MoveSegs( item->segCnt, item->segPtr, orig ); RescaleSegs( item->segCnt, item->segPtr, item->dim.carLength/protoP->size.x, item->dim.carWidth/protoP->size.y, 1/ratio ); RecolorSegs( item->segCnt, item->segPtr, item->color ); } } EXPORT BOOL_T WriteCars( FILE * f ) { int inx; BOOL_T rc = TRUE; for ( inx=0; inxtitle, 7, tabs1 ); TabStringExtract( item2->title, 7, tabs2 ); for ( mode=carHotbarModes[carHotbarModeInx],rc=0; mode!=0&&rc==0; mode>>=4 ) { switch ( mode&0x000F ) { case 4: rc = (int)(item1->index-item2->index); break; case 1: rc = strncasecmp( tabs1[T_MANUF].ptr, tabs2[T_MANUF].ptr, max(tabs1[T_MANUF].len,tabs2[T_MANUF].len) ); break; case 3: rc = strncasecmp( tabs1[T_PART].ptr, tabs2[T_PART].ptr, max(tabs1[T_PART].len,tabs2[T_PART].len) ); break; case 2: if ( item1->type < item2->type ) rc = -1; else if ( item1->type > item2->type ) rc = 1; else rc = strncasecmp( tabs1[T_PROTO].ptr, tabs2[T_PROTO].ptr, max(tabs1[T_PROTO].len,tabs2[T_PROTO].len) ); break; } } return rc; } static void CarItemHotbarUpdate( paramGroup_p pg, int inx, void * data ) { wIndex_t carItemInx; carItem_p item; if ( inx == 0 ) { carItemInx = (wIndex_t)*(long*)data; if ( carItemInx < 0 ) return; carItemInx = (wIndex_t)VP2L(wListGetItemContext( (wList_p)pg->paramPtr[inx].control, carItemInx )); item = carItemHotbar(carItemInx); if ( item != NULL ) currCarItemPtr = item; } } static char * FormatCarTitle( carItem_p item, long mode ) { tabString_t tabs[7]; char * cp; TabStringExtract( item->title, 7, tabs ); cp = message; for ( ; mode!=0; mode>>=4 ) { switch ( mode&0x000F ) { case 1: cp = TabStringCpy( cp, &tabs[T_MANUF] ); break; case 2: cp = TabStringCpy( cp, &tabs[T_PROTO] ); break; case 3: cp = TabStringCpy( cp, &tabs[T_PART] ); break; case 4: sprintf( cp, "%ld ", item->index ); cp += strlen(cp); break; case 5: strcpy( cp, typeListMap[CarProtoFindTypeCode(item->type)].name ); cp += strlen(cp); break; } *cp++ = '/'; } *--cp = '\0'; return message; } EXPORT char * CarItemDescribe( carItem_p item, long mode, long * index ) { tabString_t tabs[7]; char * cp; static char desc[STR_LONG_SIZE]; TabStringExtract( item->title, 7, tabs ); cp = desc; if ( mode != -1 ) { sprintf( cp, "%ld ", item->index ); cp = desc+strlen(cp); } if ( (mode&0xF)!=1 && ((mode>>4)&0xF)!=1 && ((mode>>8)&0xF)!=1 && ((mode>>12)&0xF)!=1 ) { cp = TabStringCpy( cp, &tabs[T_MANUF] ); *cp++ = ' '; } if ( (mode&0xF)!=3 && ((mode>>4)&0xF)!=3 && ((mode>>8)&0xF)!=3 && ((mode>>12)&0xF)!=3 ) { cp = TabStringCpy( cp, &tabs[T_PART] ); *cp++ = ' '; } if ( (mode&0xF)!=2 && ((mode>>4)&0xF)!=2 && ((mode>>8)&0xF)!=2 && ((mode>>12)&0xF)!=2 ) { cp = TabStringCpy( cp, &tabs[T_PROTO] ); *cp++ = ' '; } if ( tabs[T_DESC].len > 0 ) { cp = TabStringCpy( cp, &tabs[T_DESC] ); *cp++ = ' '; } if ( mode != -1 ) { if ( tabs[T_REPMARK].len > 0 ) { cp = TabStringCpy( cp, &tabs[T_REPMARK] ); *cp++ = ' '; } else if ( tabs[T_ROADNAME].len > 0 ) { cp = TabStringCpy( cp, &tabs[T_ROADNAME] ); *cp++ = ' '; } if ( tabs[T_NUMBER].len > 0 ) { cp = TabStringCpy( cp, &tabs[T_NUMBER] ); *cp++ = ' '; } } *--cp = '\0'; if ( index != NULL ) *index = item->index; return desc; } EXPORT void CarItemLoadList( void * unused ) { wIndex_t inx; carItem_p item; char * cp; wWinPix_t w, h; DYNARR_SET( carItem_t*, carItemHotbar_da, carItemInfo_da.cnt ); memcpy( carItemHotbar_da.ptr, carItemInfo_da.ptr, carItemInfo_da.cnt * sizeof item ); wListClear( (wList_p)newCarPLs[0].control ); for ( inx=0; inxcar && !IsTrackDeleted(item->car) ) continue; cp = CarItemDescribe( item, 0, NULL ); wListAddValue( (wList_p)newCarPLs[0].control, cp, NULL, I2VP(inx) ); } /*wListSetValue( (wList_p)newCarPLs[0].control, "Select a car" );*/ wListSetIndex( (wList_p)newCarPLs[0].control, 0 ); strcpy( newCarLabel1, _("Select") ); ParamLoadControl( &newCarPG, 0 ); InfoSubstituteControls( newCarControls, newCarLabels ); wWinGetSize( mainW, &w, &h ); w -= wControlGetPosX( newCarControls[0] ) + 4; if ( w > 20 ) wListSetSize( (wList_p)newCarControls[0], w, wControlGetHeight( newCarControls[0] ) ); } static char * CarItemHotbarProc( hotBarProc_e op, void * data, drawCmd_p d, coOrd * origP ) { wIndex_t carItemInx = (wIndex_t)VP2L(data); carItem_p item; wIndex_t inx; long mode; char * cp; wWinPix_t w, h; item = carItemHotbar(carItemInx); if ( item == NULL ) return NULL; switch ( op ) { case HB_SELECT: currCarItemPtr = item; mode = carHotbarModes[carHotbarModeInx]; if ( (mode&0xF000) == 0 ) { wListClear( (wList_p)newCarPLs[0].control ); for ( inx=carItemInx; inxcar && !IsTrackDeleted(item->car) ) continue; cp = CarItemDescribe( item, mode, NULL ); wListAddValue( (wList_p)newCarPLs[0].control, cp, NULL, I2VP(inx) ); } /*wListSetValue( (wList_p)newCarPLs[0].control, "Select a car" );*/ wListSetIndex( (wList_p)newCarPLs[0].control, 0 ); cp = CarItemHotbarProc( HB_BARTITLE, I2VP(carItemInx), NULL, NULL ); strncpy( newCarLabel1, cp, sizeof newCarLabel1 ); ParamLoadControls( &newCarPG ); ParamGroupRecord( &newCarPG ); InfoSubstituteControls( newCarControls, newCarLabels ); wWinGetSize( mainW, &w, &h ); w -= wControlGetPosX( newCarControls[0] ) + 4; if ( w > 20 ) wListSetSize( (wList_p)newCarControls[0], w, wControlGetHeight( newCarControls[0] ) ); } else { InfoSubstituteControls( NULL, NULL ); cp = CarItemDescribe( item, 0, NULL ); InfoMessage( cp ); } break; case HB_LISTTITLE: case HB_BARTITLE: return FormatCarTitle( item, carHotbarModes[carHotbarModeInx] ); case HB_FULLTITLE: return item->title; case HB_DRAW: if ( item->segCnt == 0 ) CarItemGetSegs( item ); DrawSegs( d, *origP, 0.0, item->segPtr, item->segCnt, trackGauge, wDrawColorBlack ); return NULL; } return NULL; } EXPORT int CarAvailableCount( void ) { wIndex_t inx; int cnt = 0; carItem_t * item; for ( inx=0; inx < carItemHotbar_da.cnt; inx ++ ) { item = carItemHotbar(inx); if (FIT_NONE == CompatibleScale( FIT_CAR, item->scaleInx, GetLayoutCurScale())) continue; cnt++; } return cnt; } EXPORT void AddHotBarCarDesc( void ) { wIndex_t inx; carItem_t * item0, * item1; coOrd orig; coOrd size; DYNARR_SET( carItem_t*, carItemHotbar_da, carItemInfo_da.cnt ); memcpy( carItemHotbar_da.ptr, carItemInfo_da.ptr, carItemInfo_da.cnt * sizeof item0 ); qsort( carItemHotbar_da.ptr, carItemHotbar_da.cnt, sizeof item0, Cmp_carHotbar ); for ( inx=0,item0=NULL; inx < carItemHotbar_da.cnt; inx ++ ) { item1 = carItemHotbar(inx); if ( item1->car && !IsTrackDeleted(item1->car) ) continue; if ( FIT_NONE == CompatibleScale(FIT_CAR,item1->scaleInx,GetLayoutCurScale())) continue; if ( (carHotbarModes[carHotbarModeInx]&0xF000)!=0 || ( item0 == NULL || Cmp_carHotbar( &item0, &item1 ) != 0 ) ) { #ifdef DESCFIX orig.x = - item->orig.x; orig.y = - item->orig.y; #endif orig = zero; size.x = item1->dim.carLength; size.y = item1->dim.carWidth; AddHotBarElement( FormatCarTitle( item1, carHotbarContents[carHotbarModeInx] ), size, orig, FALSE, FALSE, (60.0*12.0/curScaleRatio), I2VP(inx), CarItemHotbarProc ); } item0 = item1; } } EXPORT void CarItemFindCouplerMountPoint( carItem_p item, traverseTrack_t trvTrk0, coOrd pos[2] ) { // We assume the coupler pivot is 'couplerLength' before the end of the car DIST_T couplerLength = (item->dim.coupledLength - item->dim.carLength) / 2.0; if ( IsClose(item->dim.truckCenter) ) { // Single truck/bogie DIST_T d = item->dim.carLength/2.0 - couplerLength; Translate( &pos[0], trvTrk0.pos, trvTrk0.angle, d + item->dim.truckCenterOffset ); FlipTraverseTrack( &trvTrk0 ); Translate( &pos[1], trvTrk0.pos, trvTrk0.angle, d - item->dim.truckCenterOffset ); return; } // Find the pos of the 2 trucks // Note this is a slight simplification, we should use the car center, not the on-track position traverseTrack_t trvTrk1 = trvTrk0; TraverseTrack2( &trvTrk0, item->dim.truckCenter/2.0 + item->dim.truckCenterOffset ); FlipTraverseTrack( & trvTrk1 ); TraverseTrack2( &trvTrk1, item->dim.truckCenter/2.0 - item->dim.truckCenterOffset ); // Get the angle to translate from the truck ANGLE_T angle[2]; if ( trvTrk0.trk == NULL || (item->options&CAR_DESC_COUPLER_MODE_BODY)!=0 ) { // Body mount couplers // Angle is same as the car angle[0] = FindAngle( trvTrk1.pos, trvTrk0.pos ); angle[1] = NormalizeAngle( angle[0]+180.0 ); } else { // Truck mounted couplers // Angle is same as the trucks angle[0] = trvTrk0.angle; angle[1] = trvTrk1.angle; } // Get the distance to translate DIST_T d[2]; d[0] = item->dim.carLength/2.0 - couplerLength - ( item->dim.truckCenter/2.0 + item->dim.truckCenterOffset ); d[1] = item->dim.carLength/2.0 - couplerLength - ( item->dim.truckCenter/2.0 - item->dim.truckCenterOffset ); // And translate Translate( &pos[0], trvTrk0.pos, angle[0], d[0] ); Translate( &pos[1], trvTrk1.pos, angle[1], d[1] ); #ifdef LATER if ( trvTrk.trk == NULL || (item->options&CAR_DESC_COUPLER_MODE_BODY)!=0 ) { couplerOffset = item->dim.coupledLength/2.0; Translate( &pos, trvTrk.pos, trvTrk.angle, couplerOffset ); } else { if (dir) TraverseTrack2( &trvTrk, item->dim.truckCenter/2.0-item->dim.truckCenterOffset ); else TraverseTrack2( &trvTrk, item->dim.truckCenter/2.0+item->dim.truckCenterOffset ); /*Translate( &pos1, trvTrk.pos, trvTrk.angle, item->dim.truckCenter/2.0 );*/ couplerOffset = (item->dim.coupledLength-item->dim.truckCenter)/2.0; if (dir) couplerOffset = couplerOffset + item->dim.truckCenterOffset; else couplerOffset = couplerOffset - item->dim.truckCenterOffset; Translate( &pos, trvTrk.pos, trvTrk.angle, couplerOffset ); } #endif } EXPORT void CarItemSize( carItem_p item, coOrd * size ) { size->x = item->dim.carLength; size->y = item->dim.carWidth; } EXPORT char * CarItemNumber( carItem_p item ) { return item->data.number; } static DIST_T CarItemTruckCenter( carItem_p item ) { return item->dim.truckCenter; } static DIST_T CarItemTruckOffset( carItem_p item ) { return item->dim.truckCenterOffset; } EXPORT DIST_T CarItemCoupledLength( carItem_p item ) { return item->dim.coupledLength; } EXPORT BOOL_T CarItemIsLoco( carItem_p item ) { return (item->options&CAR_DESC_IS_LOCO) == (CAR_DESC_IS_LOCO); } EXPORT BOOL_T CarItemIsLocoMaster( carItem_p item ) { return (item->options&(CAR_DESC_IS_LOCO|CAR_DESC_IS_LOCO_MASTER)) == (CAR_DESC_IS_LOCO|CAR_DESC_IS_LOCO_MASTER); } EXPORT void CarItemSetLocoMaster( carItem_p item, BOOL_T locoIsMaster ) { if ( locoIsMaster ) item->options |= CAR_DESC_IS_LOCO_MASTER; else item->options &= ~CAR_DESC_IS_LOCO_MASTER; } EXPORT void CarItemSetTrack( carItem_p item, track_p trk ) { item->car = trk; if ( trk != NULL ) SetTrkScale( trk, item->scaleInx ); } static DIST_T CarItemCouplerLength( carItem_p item, int dir ) { return item->dim.coupledLength-item->dim.carLength; } EXPORT BOOL_T StoreCarItem (carItem_p item, void **data,long *len) { *data = item; *len = sizeof (carItem_t); return TRUE; } EXPORT BOOL_T ReplayCarItem(carItem_p item, void *data,long len) { item->pos = ((carItem_t *)data)->pos; item->angle = ((carItem_t *)data)->angle; return TRUE; } EXPORT void CarItemPlace( carItem_p item, traverseTrack_p trvTrk, DIST_T * dists ) { DIST_T dist; DIST_T offset; traverseTrack_t trks[2]; dist = CarItemTruckCenter(item)/2.0; offset = CarItemTruckOffset(item); //Offset is the amount the truck centers are displaced trks[0] = trks[1] = *trvTrk; TraverseTrack2( &trks[0], dist+offset ); TraverseTrack2( &trks[1], -dist+offset ); item->angle = FindAngle( trks[1].pos, trks[0].pos ); item->pos.x = (trks[0].pos.x+trks[1].pos.x)/2.0; item->pos.y = (trks[0].pos.y+trks[1].pos.y)/2.0; Translate(&item->pos,item->pos,item->angle, -offset); // Put truck center back along line by offset dists[0] = dists[1] = CarItemCoupledLength(item)/2.0; } static dynArr_t clearance; static void ClearClearancePoints(void) { //DYNARR_RESET(trkSeg_t,clearance); } static void CreateClearancePoint(coOrd pos, int position) { //DYNARR_APPEND(trkSeg_t,clearance,1); } static void DrawClearancePoints(void) { //for (int i=0;iscale >= ((d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale) ) { simplePts[0].pt.x = simplePts[3].pt.x = -size.x/2.0; simplePts[1].pt.x = simplePts[2].pt.x = size.x/2.0; simplePts[0].pt.y = simplePts[1].pt.y = -size.y/2.0; simplePts[2].pt.y = simplePts[3].pt.y = size.y/2.0; simplePts[0].pt_type = 0; simplePts[1].pt_type = 0; simplePts[2].pt_type = 0; simplePts[3].pt_type = 0; simpleSegs[0].type = SEG_FILPOLY; simpleSegs[0].color = item->color; simpleSegs[0].width = 0; simpleSegs[0].u.p.cnt = 4; simpleSegs[0].u.p.pts = simplePts; simpleSegs[0].u.p.orig = zero; simpleSegs[0].u.p.angle = 0.0; DrawSegs( d, item->pos, item->angle-90.0, simpleSegs, 1, 0.0, color ); } else { if ( item->segCnt == 0 ) CarItemGetSegs( item ); Translate( &pos, item->pos, item->angle, -size.x/2.0 ); Translate( &pos, pos, item->angle-90, -size.y/2.0 ); DrawSegs( d, pos, item->angle-90.0, item->segPtr, item->segCnt, 0.0, color ); } if (pencils) { ClearClearancePoints(); coOrd posm1,posm2; Translate( &posm1, item->pos, item->angle-90, -size.y/2.0 ); Translate( &posm2, item->pos, item->angle+90, -size.y/2.0 ); coOrd posm1a = posm1; coOrd posm2a = posm2; if (GetTrkDistance(traverse, &posm1a)>GetTrkDistance(traverse, &posm2a)) CreateClearancePoint(posm1,1); else CreateClearancePoint(posm2,2); coOrd pose1,pose2; Translate( &pose1, item->pos, item->angle, size.x/2.0 ); Translate( &pose1, pose1, item->angle-90, -size.y/2.0 ); Translate( &pose2, pose1, item->angle+90, -size.y ); traverseTrack_t traverseTrk; traverseTrk.trk = traverse; traverseTrk.pos = item->pos; traverseTrk.angle = item->angle; TraverseTrack2(&traverseTrk,size.x/2.0); coOrd pose1a = pose1; coOrd pose2a = pose2; if (GetTrkDistance(traverseTrk.trk, &pose1a)>GetTrkDistance(traverseTrk.trk, &pose2a)) CreateClearancePoint(pose1,3); else CreateClearancePoint(pose2,4); DrawClearancePoints(); } if ( drawCarTrucks ) { length = item->dim.truckCenter/2.0; double offset = CarItemTruckOffset(item); Translate( &pos, item->pos, item->angle, length+(direction?offset:-offset) ); DrawArc( d, pos, trackGauge/2.0, 0.0, 360.0, FALSE, 0, color ); Translate( &pos, item->pos, item->angle+180, length+(direction?-offset:offset) ); DrawArc( d, pos, trackGauge/2.0, 0.0, 360.0, FALSE, 0, color ); } if ( (labelEnable&LABELENABLE_CARS) ) { fp = wStandardFont( F_HELV, FALSE, FALSE ); DrawBoxedString( BOX_BACKGROUND, d, item->pos, item->data.number, fp, (wFontSize_t)descriptionFontSize, color, 0.0 ); } /* draw loco head light */ if ( (item->options&CAR_DESC_IS_LOCO)!=0 ) { Translate( &pos, item->pos, item->angle+(direction?180.0:0.0), size.x/2.0-trackGauge/2.0 ); if ( locoIsMaster ) { DrawFillCircle( d, pos, trackGauge/2.0, (color==wDrawColorBlack?drawColorGold:color) ); } else { width = (wDrawWidth)floor( trackGauge/8.0 * d->dpi / d->scale ); DrawArc( d, pos, trackGauge/2.0, 0.0, 360.0, FALSE, width, (color==wDrawColorBlack?drawColorGold:color) ); } } /* draw coupler */ scale2rail = ((d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale); if ( d->scale >= scale2rail ) return; scale2rail /= 2; rad = trackGauge/8.0; for ( dir=0; dir<2; dir++ ) { Translate( &pos, coupler[dir].pos, coupler[dir].angle, CarItemCouplerLength(item,dir) ); DrawLine( d, coupler[dir].pos, pos, couplerLineWidth, color ); if ( d->scale < scale2rail ) { /*DrawFillCircle( d, p0, rad, dir==0?color:selectedColor );*/ Translate( &pos2, pos, coupler[dir].angle+90.0, trackGauge/3 ); DrawLine( d, pos2, pos, couplerLineWidth, color ); } } } EXPORT void CarItemUpdate( carItem_p item ) { DoChangeNotification( CHANGE_SCALE ); } /* * Car Item/Part Dlg */ static int carDlgChanged; static SCALEINX_T carDlgScaleInx; static carItem_p carDlgUpdateItemPtr; static carPart_p carDlgUpdatePartPtr; static carProto_p carDlgUpdateProtoPtr; static carPart_p carDlgNewPartPtr; static carProto_p carDlgNewProtoPtr; static BOOL_T carDlgFlipToggle; static wIndex_t carDlgManufInx; static char carDlgManufStr[STR_SIZE]; static wIndex_t carDlgKindInx; static wIndex_t carDlgProtoInx; static char carDlgProtoStr[STR_SIZE]; static wIndex_t carDlgPartnoInx; static char carDlgPartnoStr[STR_SIZE]; static char carDlgDescStr[STR_SIZE]; static long carDlgDispMode; static wIndex_t carDlgRoadnameInx; static char carDlgRoadnameStr[STR_SIZE]; static char carDlgRepmarkStr[STR_SIZE]; static char carDlgNumberStr[STR_SIZE]; static wDrawColor carDlgBodyColor; static long carDlgIsLoco; static wIndex_t carDlgTypeInx; static carDim_t carDlgDim; static DIST_T carDlgCouplerLength; static long carDlgCouplerMount; static long carDlgItemIndex = 1; static FLOAT_T carDlgPurchPrice; static char carDlgPurchPriceStr[STR_SIZE]; static FLOAT_T carDlgCurrPrice; static char carDlgCurrPriceStr[STR_SIZE]; static wIndex_t carDlgConditionInx; static long carDlgCondition; static long carDlgPurchDate; static char carDlgPurchDateStr[STR_SIZE]; static long carDlgServiceDate; static char carDlgServiceDateStr[STR_SIZE]; static long carDlgQuantity = 1; static long carDlgMultiNum; static char *dispmodeLabels[] = { N_("Information"), N_("Customize"), NULL }; static drawCmd_t carDlgD = { NULL, &screenDrawFuncs, 0, 1.0, 0.0, { 0, 0 }, { 0, 0 }, Pix2CoOrd, CoOrd2Pix }; static void CarDlgRedraw( wDraw_p d, void * context, wWinPix_t x, wWinPix_t y ); static paramDrawData_t carDlgDrawData = { 455, 100, CarDlgRedraw, NULL, &carDlgD }; static paramTextData_t notesData = { 440, 100 }; static char *multinumLabels[] = { N_("Sequential"), N_("Repeated"), NULL }; static void CarDlgNewProto( void ); static void CarDlgUpdate( paramGroup_p, int, void * ); static void CarDlgNewDesc( void ); static void CarDlgNewProto( void ); static paramData_t carDlgPLs[] = { #define A (0) #define I_CD_MANUF_LIST (A+0) { PD_DROPLIST, &carDlgManufInx, "manuf", PDO_NOPREF, I2VP(350), N_("Manufacturer"), BL_EDITABLE }, #define I_CD_PROTOTYPE_STR (A+1) { PD_STRING, &carDlgProtoStr, "prototype", PDO_NOPREF|PDO_NOTBLANK, I2VP(350), N_("Prototype"), 0, 0, sizeof(carDlgProtoStr)}, #define I_CD_PROTOKIND_LIST (A+2) { PD_DROPLIST, &carDlgKindInx, "protokind-list", PDO_NOPREF, I2VP(125), N_("Prototype"), 0 }, #define I_CD_PROTOTYPE_LIST (A+3) { PD_DROPLIST, &carDlgProtoInx, "prototype-list", PDO_NOPREF|PDO_DLGHORZ, I2VP(225-3), NULL, 0 }, #define I_CD_TYPE_LIST (A+4) { PD_DROPLIST, &carDlgTypeInx, "type", PDO_NOPREF, I2VP(350), N_("Type"), 0 }, #define I_CD_PARTNO_LIST (A+5) { PD_DROPLIST, &carDlgPartnoInx, "partno-list", PDO_NOPREF, I2VP(350), N_("Part"), BL_EDITABLE }, #define I_CD_PARTNO_STR (A+6) { PD_STRING, &carDlgPartnoStr, "partno", PDO_NOPREF|PDO_NOTBLANK, I2VP(350), N_("Part Number"), 0, 0, sizeof(carDlgPartnoStr)}, #define I_CD_ISLOCO (A+7) { PD_TOGGLE, &carDlgIsLoco, "isLoco", PDO_NOPREF|PDO_DLGWIDE, isLocoLabels, N_("Loco?"), BC_HORZ|BC_NOBORDER }, #define I_CD_DESC_STR (A+8) { PD_STRING, &carDlgDescStr, "desc", PDO_NOPREF|PDO_STRINGLIMITLENGTH, I2VP(350), N_("Description"), 0, 0, sizeof(carDlgDescStr)}, #define I_CD_IMPORT (A+9) { PD_BUTTON, NULL, "import", 0, 0, N_("Import") }, #define I_CD_RESET (A+10) { PD_BUTTON, NULL, "reset", PDO_DLGHORZ, 0, N_("Reset") }, #define I_CD_FLIP (A+11) { PD_BUTTON, NULL, "flip", PDO_DLGHORZ|PDO_DLGWIDE|PDO_DLGBOXEND, 0, N_("Flip") }, #define I_CD_DISPMODE (A+12) { PD_RADIO, &carDlgDispMode, "dispmode", PDO_NOPREF|PDO_DLGWIDE, dispmodeLabels, N_("Mode"), BC_HORZ|BC_NOBORDER }, #define B (A+13) #define I_CD_ROADNAME_LIST (B+0) { PD_DROPLIST, &carDlgRoadnameInx, "road", PDO_NOPREF|PDO_DLGWIDE, I2VP(350), N_("Road"), BL_EDITABLE }, #define I_CD_REPMARK (B+1) { PD_STRING, carDlgRepmarkStr, "repmark", PDO_NOPREF|PDO_STRINGLIMITLENGTH, I2VP(60), N_("Reporting Mark"), 0, 0, sizeof(carDlgRepmarkStr)}, #define I_CD_NUMBER (B+2) { PD_STRING, carDlgNumberStr, "number", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ|PDO_STRINGLIMITLENGTH, I2VP(80), N_("Number"), 0, 0, sizeof(carDlgNumberStr)}, #define I_CD_BODYCOLOR (B+3) { PD_COLORLIST, &carDlgBodyColor, "bodyColor", PDO_DLGWIDE|PDO_DLGHORZ, NULL, N_("Color") }, #define I_CD_CARLENGTH (B+4) { PD_FLOAT, &carDlgDim.carLength, "carLength", PDO_DIM|PDO_NOPREF|PDO_DLGWIDE, &r0d001_99999, N_("Car Length") }, #define I_CD_CARWIDTH (B+5) { PD_FLOAT, &carDlgDim.carWidth, "carWidth", PDO_DIM|PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ, &r0d001_99999, N_("Width") }, #define I_CD_TRKCENTER (B+6) { PD_FLOAT, &carDlgDim.truckCenter, "trkCenter", PDO_DIM|PDO_NOPREF, &r0d001_99999, N_("Truck Centers") }, #define I_CD_TRKOFFSET (B+7) { PD_FLOAT, &carDlgDim.truckCenterOffset, "trkCenterOffset", PDO_DIM|PDO_NOPREF|PDO_DLGHORZ|PDO_DLGWIDE, &r9999_9999, N_("Center Offset") }, #define I_CD_CPLRMNT (B+8) { PD_RADIO, &carDlgCouplerMount, "cplrMount", PDO_NOPREF, cplrModeLabels, N_("Coupler Mount"), BC_HORZ|BC_NOBORDER }, #define I_CD_CPLDLEN (B+9) { PD_FLOAT, &carDlgDim.coupledLength, "cpldLen", PDO_DIM|PDO_NOPREF, &r0d001_99999, N_("Coupled Length") }, #define I_CD_CPLRLEN (B+10) { PD_FLOAT, &carDlgCouplerLength, "cplrLen", PDO_DIM|PDO_NOPREF|PDO_DLGHORZ, &r0d001_99999, N_("Coupler Length") }, #define I_CD_CANVAS (B+11) { PD_DRAW, NULL, "canvas", PDO_NOPSHUPD|PDO_DLGWIDE|PDO_DLGNOLABELALIGN|PDO_DLGRESETMARGIN|PDO_DLGBOXEND|PDO_DLGRESIZE, &carDlgDrawData, NULL, 0 }, #define C (B+12) #define I_CD_ITEMINDEX (C+0) { PD_LONG, &carDlgItemIndex, "index", PDO_NOPREF|PDO_DLGWIDE, &i1_999999999, N_("Index"), 0 }, #define I_CD_PURPRC (C+1) { PD_STRING, &carDlgPurchPriceStr, "purchPrice", PDO_NOPREF|PDO_DLGWIDE|PDO_STRINGLIMITLENGTH, I2VP(50), N_("Purchase Price"), 0, &carDlgPurchPrice, sizeof(carDlgPurchPriceStr) }, #define I_CD_CURPRC (C+2) { PD_STRING, &carDlgCurrPriceStr, "currPrice", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ|PDO_STRINGLIMITLENGTH, I2VP(50), N_("Current Price"), 0, &carDlgCurrPrice, sizeof(carDlgCurrPriceStr) }, #define I_CD_COND (C+3) { PD_DROPLIST, &carDlgConditionInx, "condition", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ, I2VP(90), N_("Condition") }, #define I_CD_PURDAT (C+4) { PD_STRING, &carDlgPurchDateStr, "purchDate", PDO_NOPREF|PDO_DLGWIDE|PDO_STRINGLIMITLENGTH, I2VP(80), N_("Purchase Date"), 0, &carDlgPurchDate, sizeof(carDlgPurchDateStr) }, #define I_CD_SRVDAT (C+5) { PD_STRING, &carDlgServiceDateStr, "serviceDate", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ|PDO_STRINGLIMITLENGTH, I2VP(80), N_("Service Date"), 0, &carDlgServiceDate, sizeof(carDlgServiceDateStr) }, #define I_CD_QTY (C+6) { PD_LONG, &carDlgQuantity, "quantity", PDO_NOPREF|PDO_DLGWIDE, &i1_9999, N_("Quantity") }, #define I_CD_MLTNUM (C+7) { PD_RADIO, &carDlgMultiNum, "multinum", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGHORZ, multinumLabels, N_("Numbers"), BC_HORZ|BC_NOBORDER }, #define I_CD_NOTES (C+8) { PD_TEXT, NULL, "notes", PDO_NOPREF|PDO_DLGWIDE|PDO_DLGNOLABELALIGN|PDO_DLGRESETMARGIN, ¬esData, N_("Notes") }, #define D (C+9) #define I_CD_MSG (D+0) { PD_MESSAGE, NULL, NULL, PDO_DLGNOLABELALIGN|PDO_DLGRESETMARGIN|PDO_DLGBOXEND, I2VP(450) }, #define I_CD_NEW (D+1) { PD_MENU, NULL, "new-menu", PDO_DLGCMDBUTTON, NULL, N_("New"), 0, I2VP(0) }, { PD_MENUITEM, CarDlgNewDesc, "new-part-mi", 0, NULL, N_("Car Part"), 0, I2VP(0) }, { PD_MENUITEM, CarDlgNewProto, "new-proto-mi", 0, NULL, N_("Car Prototype"), 0, I2VP(0) }, #define I_CD_NEWPROTO (D+4) { PD_BUTTON, CarDlgNewProto, "new", PDO_DLGCMDBUTTON, NULL, N_("New"), 0, I2VP(0) } }; static paramGroup_t carDlgPG = { "carpart", 0, carDlgPLs, COUNT( carDlgPLs ) }; static dynArr_t carDlgSegs_da; #define carDlgSegs(N) DYNARR_N( trkSeg_t, carDlgSegs_da, N ) typedef enum { T_ItemSel, T_ItemEnter, T_ProtoSel, T_ProtoEnter, T_PartnoSel, T_PartnoEnter } carDlgTransistion_e; static char *carDlgTransistion_s[] = { "ItemSel", "ItemEnter", "ProtoSel", "ProtoEnter", "PartnoSel", "PartnoEnter" }; typedef enum { S_Error, S_ItemSel, S_ItemEnter, S_PartnoSel, S_PartnoEnter, S_ProtoSel } carDlgState_e; static char *carDlgState_s[] = { "Error", "ItemSel", "ItemEnter", "PartnoSel", "PartnoEnter", "ProtoSel" }; typedef enum { A_Return, A_SError, A_Else, A_SItemSel, A_SItemEnter, A_SPartnoSel, A_SPartnoEnter, A_SProtoSel, A_IsCustom, A_IsNewPart, A_IsNewProto, A_LoadDataFromPartList, A_LoadDimsFromStack, A_LoadManufListForScale, A_LoadManufListAll, A_LoadProtoListForManuf, A_LoadProtoListAll, A_LoadPartnoList, A_LoadLists, A_LoadDimsFromProtoList, A_ConvertDimsToProto, A_Redraw, A_ClrManuf, A_ClrPartnoStr, A_ClrNumberStr, A_LoadProtoStrFromList, A_ShowPartnoList, A_HidePartnoList, A_PushDims, A_PopDims, A_PopTitleAndTypeinx, A_PopCouplerLength, A_ShowControls, A_LoadInfoFromUpdateItem, A_LoadDataFromUpdatePart, A_InitProto, A_RecallCouplerLength, A_Last } carDlgAction_e; static char *carDlgAction_s[] = { "Return", "SError", "Else", "SItemSel", "SItemEnter", "SPartnoSel", "SPartnoEnter", "SProtoSel", "IsCustom", "IsNewPart", "IsNewProto", "LoadDataFromPartList", "LoadDimsFromStack", "LoadManufListForScale", "LoadManufListAll", "LoadProtoListForManuf", "LoadProtoListAll", "LoadPartnoList", "LoadLists", "LoadDimsFromProtoList", "ConvertDimsToProto", "Redraw", "ClrManuf", "ClrPartnoStr", "ClrNumberStr", "LoadProtoStrFromList", "ShowPartnoList", "HidePartnoList", "PushDims", "PopDims", "PopTitleAndTypeinx", "PopCouplerLength", "ShowControls", "LoadInfoFromUpdateItem", "LoadDataFromUpdatePart", "InitProto", "RecallCouplerLength", "Last" }; static carDlgAction_e stateMachine[7][7][10] = { /* A_SError */{ {A_SError}, {A_SError}, {A_SError}, {A_SError}, {A_SError}, {A_SError}, {A_SError} }, /*A_SItemSel*/{ /*T_ItemSel*/ { A_LoadProtoListForManuf, A_LoadPartnoList, A_LoadDataFromPartList, A_Redraw }, /*T_ItemEnter*/ { A_SItemEnter, A_LoadProtoListAll, A_ClrPartnoStr, A_ClrNumberStr, A_LoadDimsFromProtoList, A_Redraw, A_HidePartnoList }, /*T_ProtoSel*/ { A_LoadPartnoList, A_LoadDataFromPartList, A_Redraw }, /*T_ProtoEnter*/ { A_SError }, /*T_PartnoSel*/ { A_LoadDataFromPartList, A_Redraw }, /*T_PartnoEnter*/{ A_SItemEnter, A_LoadProtoListAll, A_HidePartnoList } }, /*A_SItemEnter*/{ /*T_ItemSel*/ { A_SItemSel, A_LoadProtoListForManuf, A_LoadPartnoList, A_LoadDataFromPartList, A_Redraw, A_ShowPartnoList }, /*T_ItemEnter*/ { A_Return }, /*T_ProtoSel*/ { A_LoadDimsFromProtoList, A_Redraw }, /*T_ProtoEnter*/ { A_SError }, /*T_PartnoSel*/ { A_SError }, /*T_PartnoEnter*/{ A_Return } }, /*A_SPartnoSel*/{ /*T_ItemSel*/ { A_SPartnoSel }, /*T_ItemEnter*/ { A_SPartnoSel }, /*T_ProtoSel*/ { A_SPartnoSel, A_LoadDimsFromProtoList, A_Redraw }, /*T_ProtoEnter*/ { A_SError }, /*T_PartnoSel*/ { A_SError } }, /*A_SPartnoEnter*/{ /*T_ItemSel*/ { A_SPartnoSel }, /*T_ItemEnter*/ { A_SPartnoEnter }, /*T_ProtoSel*/ { A_SPartnoEnter, A_LoadDimsFromProtoList, A_Redraw }, /*T_ProtoEnter*/ { A_SError }, /*T_PartnoSel*/ { A_SError }, /*T_PartnoEnter*/{ A_SPartnoEnter } }, /*A_SProtoSel*/{ /*T_ItemSel*/ { A_SError }, /*T_ItemEnter*/ { A_SError }, /*T_ProtoSel*/ { A_SError }, /*T_ProtoEnter*/ { A_SProtoSel }, /*T_PartnoSel*/ { A_SError }, /*T_PartnoEnter*/{ A_SError } } }; static carDlgAction_e itemNewActions[] = { A_RecallCouplerLength, A_LoadLists, A_IsCustom, 2+3, A_LoadDimsFromProtoList, A_ClrPartnoStr, A_ClrNumberStr, A_Else, 1, A_LoadDataFromPartList, A_ShowControls, A_Return }; static carDlgAction_e itemUpdActions[] = { A_LoadInfoFromUpdateItem, /*A_LoadManufListForScale, A_IsCustom, 5, A_LoadProtoListAll, A_HidePartnoList, A_SItemEnter, A_Else, 5, A_LoadProtoListForManuf, A_LoadPartnoList, A_LoadDataFromPartList, A_ShowPartnoList, A_SItemSel,*/ A_ShowControls, A_Return }; static carDlgAction_e partNewActions[] = { A_RecallCouplerLength, A_LoadManufListAll, A_LoadProtoListAll, A_ClrPartnoStr, A_ClrNumberStr, A_SPartnoSel, A_LoadDimsFromProtoList, A_ShowControls, A_Redraw, A_Return }; static carDlgAction_e partUpdActions[] = { A_LoadDataFromUpdatePart, A_SPartnoSel, A_ShowControls, A_Return }; static carDlgAction_e protoNewActions[] = { A_InitProto, A_SProtoSel, A_ShowControls, A_Return }; static carDlgAction_e protoUpdActions[] = { A_InitProto, A_SProtoSel, A_ShowControls, A_Return }; static carDlgAction_e item2partActions[] = { A_PushDims, A_LoadManufListAll, A_LoadProtoListAll, A_IsCustom, 0+1, A_ClrManuf, A_SPartnoSel, A_ShowControls, A_Return }; static carDlgAction_e part2itemActions[] = { A_IsNewPart, 2+0, A_Else, 1, A_PopTitleAndTypeinx, A_LoadLists, A_IsCustom, 2+1, A_LoadDimsFromProtoList, A_Else, 1, A_LoadDataFromPartList, #ifdef LATER A_IsNewPart, 2+0, A_Else, 1, A_LoadDimsFromStack, #endif A_ShowControls, A_Return }; static carDlgAction_e item2protoActions[] = { A_PushDims, A_ConvertDimsToProto, A_SProtoSel, A_ShowControls, A_Return }; static carDlgAction_e proto2itemActions[] = { A_IsCustom, 2+2+3, A_IsNewProto, 2+3, A_LoadProtoListAll, A_PopCouplerLength, A_LoadDimsFromProtoList, A_Else, 2, A_LoadDimsFromStack, A_LoadProtoStrFromList, A_ShowControls, A_Return }; static carDlgAction_e part2protoActions[] = { A_PushDims, A_ConvertDimsToProto, A_SProtoSel, A_ShowControls, A_Return }; static carDlgAction_e proto2partActions[] = { A_IsNewProto, 2+3, A_LoadProtoListAll, A_PopCouplerLength, A_LoadDimsFromProtoList, A_Else, 2, A_LoadDimsFromStack, A_LoadProtoStrFromList, A_ShowControls, A_Return }; #define CARDLG_STK_SIZE (2) int carDlgStkPtr = 0; struct { carDim_t dim; DIST_T couplerLength; carDlgState_e state; int changed; carPart_p partP; wIndex_t typeInx; } carDlgStk[CARDLG_STK_SIZE]; static carDlgState_e currState = S_Error; #define S_ITEM (currState==S_ItemSel||currState==S_ItemEnter) #define S_PART (currState==S_PartnoSel) #define S_PROTO (currState==S_ProtoSel) static void CarDlgLoadDimsFromPart( carPart_p partP ) { tabString_t tabs[7]; if ( partP == NULL ) return; carDlgDim = partP->dim; carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); wPrefSetFloat( carDlgPG.nameStr, message, carDlgCouplerLength ); carDlgIsLoco = (partP->options&CAR_DESC_IS_LOCO)?1:0; carDlgBodyColor = partP->color; ParamLoadControl( &carDlgPG, I_CD_CARLENGTH ); ParamLoadControl( &carDlgPG, I_CD_CARWIDTH ); ParamLoadControl( &carDlgPG, I_CD_TRKCENTER ); ParamLoadControl( &carDlgPG, I_CD_CPLDLEN ); wColorSelectButtonSetColor( (wButton_p)carDlgPLs[I_CD_BODYCOLOR].control, *(wDrawColor*)carDlgPLs[I_CD_BODYCOLOR].valueP ); TabStringExtract( partP->title, 7, tabs ); } static void CarDlgLoadDimsFromProto( carProto_p protoP ) { DIST_T ratio = GetScaleRatio(carDlgScaleInx); carDlgDim.carLength = protoP->dim.carLength/ratio; carDlgDim.carWidth = protoP->dim.carWidth/ratio; carDlgDim.truckCenter = protoP->dim.truckCenter/ratio; carDlgDim.truckCenterOffset = protoP->dim.truckCenterOffset/ratio; carDlgDim.coupledLength = carDlgDim.carLength + carDlgCouplerLength*2; /*carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0;*/ carDlgIsLoco = (protoP->options&CAR_DESC_IS_LOCO)?1:0; ParamLoadControl( &carDlgPG, I_CD_CARLENGTH ); ParamLoadControl( &carDlgPG, I_CD_CARWIDTH ); ParamLoadControl( &carDlgPG, I_CD_TRKCENTER ); ParamLoadControl( &carDlgPG, I_CD_CPLDLEN ); } static void CarDlgRedraw( wDraw_p d, void * context, wWinPix_t x, wWinPix_t y ) { wWinPix_t w, h; DIST_T ww, hh; DIST_T scale_w, scale_h; coOrd orig, pos, size; carProto_p protoP; FLOAT_T ratio; int segCnt; trkSeg_p segPtr; if ( S_PROTO ) ratio = 1; else ratio = 1/GetScaleRatio(carDlgScaleInx); wDrawClear( carDlgD.d ); if ( carDlgDim.carLength <= 0 || carDlgDim.carWidth <= 0 ) return; FreeFilledDraw( carDlgSegs_da.cnt, &carDlgSegs(0) ); if ( !S_PROTO ) { if ( carDlgProtoInx < 0 || (protoP = CarProtoLookup( carDlgProtoStr, FALSE, FALSE, 0.0, 0.0 )) == NULL || protoP->segCnt == 0 ) { CarProtoDlgCreateDummyOutline( &segCnt, &segPtr, (BOOL_T)carDlgIsLoco, carDlgDim.carLength, carDlgDim.carWidth, carDlgBodyColor ); } else { segCnt = protoP->segCnt; segPtr = protoP->segPtr; } } else { if ( carProtoSegCnt <= 0 ) { CarProtoDlgCreateDummyOutline( &segCnt, &segPtr, (BOOL_T)carDlgIsLoco, carDlgDim.carLength, carDlgDim.carWidth, drawColorBlue ); } else { segCnt = carProtoSegCnt; segPtr = carProtoSegPtr; } } DYNARR_SET( trkSeg_t, carDlgSegs_da, segCnt ); memcpy( &carDlgSegs(0), segPtr, segCnt * sizeof *(trkSeg_t*)0 ); CloneFilledDraw( carDlgSegs_da.cnt, &carDlgSegs(0), TRUE ); GetSegBounds( zero, 0.0, carDlgSegs_da.cnt, &carDlgSegs(0), &orig, &size ); scale_w = carDlgDim.carLength/size.x; scale_h = carDlgDim.carWidth/size.y; RescaleSegs( carDlgSegs_da.cnt, &carDlgSegs(0), scale_w, scale_h, ratio ); if ( !S_PROTO ) { RecolorSegs( carDlgSegs_da.cnt, &carDlgSegs(0), carDlgBodyColor ); } else { if ( carDlgFlipToggle ) { pos.x = carDlgDim.carLength/2.0; pos.y = carDlgDim.carWidth/2.0; RotateSegs( carDlgSegs_da.cnt, &carDlgSegs(0), pos, 180.0 ); } } wDrawGetSize( carDlgD.d, &w, &h ); ww = w/carDlgD.dpi-1.0; hh = h/carDlgD.dpi-0.5; scale_w = carDlgDim.carLength/ww; scale_h = carDlgDim.carWidth/hh; if ( scale_w > scale_h ) carDlgD.scale = scale_w; else carDlgD.scale = scale_h; orig.x = 0.50*carDlgD.scale; orig.y = 0.25*carDlgD.scale; DrawSegs( &carDlgD, orig, 0.0, &carDlgSegs(0), carDlgSegs_da.cnt, 0.0, wDrawColorBlack ); pos.y = orig.y+carDlgDim.carWidth/2.0; if ( carDlgDim.truckCenter > 0.0 ) { pos.x = orig.x+(carDlgDim.carLength-carDlgDim.truckCenter)/2.0-carDlgDim.truckCenterOffset; CarProtoDrawTruck( &carDlgD, trackGauge*curScaleRatio, ratio, pos, 0.0 ); pos.x = orig.x+(carDlgDim.carLength+carDlgDim.truckCenter)/2.0-carDlgDim.truckCenterOffset; CarProtoDrawTruck( &carDlgD, trackGauge*curScaleRatio, ratio, pos, 0.0 ); } if ( carDlgDim.coupledLength > carDlgDim.carLength ) { pos.x = orig.x; CarProtoDrawCoupler( &carDlgD, (carDlgDim.coupledLength-carDlgDim.carLength)/2.0, ratio, pos, 270.0 ); pos.x = orig.x+carDlgDim.carLength; CarProtoDrawCoupler( &carDlgD, (carDlgDim.coupledLength-carDlgDim.carLength)/2.0, ratio, pos, 90.0 ); } } static void CarDlgLoadRoadnameList( void ) /* Loads RoadnameList. * Set carDlgRoadnameInx to entry matching carDlgRoadnameStr (if found) * Otherwise not set */ { wIndex_t inx; roadnameMap_p roadnameMapP; if ( !roadnameMapChanged ) return; wListClear( (wList_p)carDlgPLs[I_CD_ROADNAME_LIST].control ); wListAddValue( (wList_p)carDlgPLs[I_CD_ROADNAME_LIST].control, _("Undecorated"), NULL, NULL ); for ( inx=0; inxroadname, NULL, roadnameMapP ); if ( strcasecmp( carDlgRoadnameStr, roadnameMapP->roadname )==0 ) carDlgRoadnameInx = inx+1; } roadnameMapChanged = FALSE; } static BOOL_T CheckAvail( carPartParent_p parentP ) { wIndex_t inx; carPart_p partP; for ( inx=0; inxparts_da.cnt; inx++ ) { partP = carPart(parentP,inx); if ( IsParamValid(partP->paramFileIndex) ) return TRUE; } return FALSE; } static BOOL_T CarDlgLoadManufList( BOOL_T bLoadAll, BOOL_T bInclCustomUnknown, SCALEINX_T scale ) { carPartParent_p manufP, manufP1; wIndex_t inx, listInx=-1; BOOL_T found = TRUE; char * firstName = NULL; LOG( log_carDlgList, 3, ( "CarDlgLoadManufList( %s, %s, %d )\n carDlgManufStr=\"%s\"\n", bLoadAll?"TRUE":"FALSE", bInclCustomUnknown?"TRUE":"FALSE", scale, carDlgManufStr ) ) carDlgManufInx = -1; manufP1 = NULL; wListClear( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control ); for ( inx=0; inxmanuf, manufP->manuf ) == 0 ) continue; if ( bLoadAll==FALSE && manufP->scale != scale ) continue; if ( !CheckAvail(manufP) ) continue; listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, manufP->manuf, NULL, manufP ); if ( carDlgManufInx < 0 && ( carDlgManufStr[0] == '\0' || strcasecmp( carDlgManufStr, manufP->manuf ) == 0 ) ) { LOG( log_carDlgList, 4, ( " found manufStr (inx=%d, listInx=%d)\n", inx, listInx ) ) carDlgManufInx = listInx; if ( carDlgManufStr[0] == '\0' ) strcpy( carDlgManufStr, manufP->manuf ); } if ( firstName == NULL ) firstName = manufP->manuf; manufP1 = manufP; } if ( bInclCustomUnknown ) { listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, _("Custom"), NULL, NULL ); if ( carDlgManufInx < 0 && ( carDlgManufStr[0] == '\0' || strcasecmp( carDlgManufStr, "Custom" ) == 0 ) ) { LOG( log_carDlgList, 4, ( " found Cus manufStr (inx=%d, listInx=%d)\n", inx, listInx ) ) carDlgManufInx = listInx; if ( carDlgManufStr[0] == '\0' ) strcpy( carDlgManufStr, _("Custom") ); } if ( firstName == NULL ) firstName = "Custom"; wListAddValue( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, _("Unknown"), NULL, NULL ); if ( carDlgManufInx < 0 && ( carDlgManufStr[0] == '\0' || strcasecmp( carDlgManufStr, "Unknown" ) == 0 ) ) { LOG( log_carDlgList, 4, ( " found Unk manufStr (inx=%d, listInx=%d)\n", inx, listInx ) ) carDlgManufInx = listInx; if ( carDlgManufStr[0] == '\0' ) strcpy( carDlgManufStr, _("Unknown") ); } } if ( carDlgManufInx < 0 ) { found = FALSE; if ( firstName != NULL ) { LOG( log_carDlgList, 4, ( " didn't find manufStr, using [0] = %s\n", firstName ) ) carDlgManufInx = 0; strcpy( carDlgManufStr, firstName ); } } return found; } static BOOL_T CarDlgLoadProtoList( char * manuf, SCALEINX_T scale, BOOL_T loadTypeList ) { carPartParent_p parentP; wIndex_t inx, listInx, inx1; BOOL_T found; carProto_p protoP; carPart_p partP; char * firstName; int typeCount[N_TYPELISTMAP]; int listTypeInx, currTypeInx; listTypeInx = -1; carDlgProtoInx = -1; firstName = NULL; wListClear( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control ); memset( typeCount, 0, N_TYPELISTMAP * sizeof typeCount[0] ); LOG( log_carDlgList, 3, ( "CarDlgLoadProtoList( %s, %d, %s )\n carDlgProtoStr=\"%s\", carDlgTypeInx=%d\n", manuf?manuf:"NULL", scale, loadTypeList?"TRUE":"FALSE", carDlgProtoStr, carDlgTypeInx ) ) if ( manuf==NULL ) { if ( carProto_da.cnt <= 0 ) return FALSE; if ( listTypeInx < 0 && carDlgProtoStr[0] && (protoP=CarProtoFind(carDlgProtoStr)) ) listTypeInx = CarProtoFindTypeCode(protoP->type); if ( listTypeInx < 0 ) listTypeInx = CarProtoFindTypeCode(carProto(0)->type); for ( inx=0; inxtype); typeCount[currTypeInx]++; if ( carDlgTypeInx >= 0 && listTypeInx != carDlgTypeInx && currTypeInx == carDlgTypeInx ) { LOG( log_carDlgList, 4, ( " found typeinx, reset list (old=%d)\n", listTypeInx ) ) wListClear( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control ); listTypeInx = carDlgTypeInx; carDlgProtoInx = -1; firstName = NULL; } if ( currTypeInx != listTypeInx ) continue; listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, protoP->desc, NULL, protoP ); if ( carDlgProtoInx < 0 && carDlgProtoStr[0] && strcasecmp( carDlgProtoStr, protoP->desc ) == 0 ) { LOG( log_carDlgList, 4, ( " found protoStr (inx=%d, listInx=%d)\n", inx, listInx ) ) carDlgProtoInx = listInx; if ( carDlgProtoStr[0] == '\0' ) strcpy( carDlgProtoStr, protoP->desc ); } if ( firstName == NULL ) firstName = protoP->desc; } } else { for ( inx=0; inxmanuf ) != 0 || scale != parentP->scale ) continue; if ( !CheckAvail(parentP) ) continue; found = FALSE; for ( inx1=0; inx1parts_da.cnt; inx1++ ) { partP = carPart( parentP, inx1 ); currTypeInx = CarProtoFindTypeCode(partP->type); typeCount[currTypeInx]++; if ( listTypeInx < 0 ) listTypeInx = currTypeInx; if ( carDlgTypeInx >= 0 && listTypeInx != carDlgTypeInx && currTypeInx == carDlgTypeInx ) { LOG( log_carDlgList, 4, ( " found typeinx, reset list (old=%d)\n", listTypeInx ) ) wListClear( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control ); listTypeInx = carDlgTypeInx; carDlgProtoInx = -1; firstName = NULL; } if ( listTypeInx == currTypeInx ) found = TRUE; } if ( !found ) continue; listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, parentP->proto, NULL, parentP ); if ( carDlgProtoInx < 0 && ( carDlgProtoStr[0] == '\0' || strcasecmp( carDlgProtoStr, parentP->proto ) == 0 ) ) { LOG( log_carDlgList, 4, ( " found protoStr (inx=%d, listInx=%d)\n", inx, listInx ) ) carDlgProtoInx = listInx; if ( carDlgProtoStr[0] == '\0' ) { strcpy( carDlgProtoStr, parentP->proto ); } } if ( firstName == NULL ) firstName = parentP->proto; } } found = TRUE; if ( carDlgProtoInx < 0 ) { found = FALSE; if ( firstName != NULL ) { LOG( log_carDlgList, 4, ( " didn't find protoStr, using [0] = %s\n", firstName ) ) carDlgProtoInx = 0; strcpy( carDlgProtoStr, firstName ); } } wListSetIndex( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); if ( loadTypeList ) { LOG( log_carDlgList, 4, ( " loading typelist\n" ) ) wListClear( (wList_p)carDlgPLs[I_CD_PROTOKIND_LIST].control ); for ( currTypeInx=0; currTypeInx 0 ) { listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_PROTOKIND_LIST].control, _(typeListMap[currTypeInx].name), NULL, I2VP(currTypeInx) ); if ( currTypeInx == listTypeInx ) { LOG( log_carDlgList, 4, ( " current = %d\n", listInx ) ) carDlgKindInx = listInx; } } } } return found; } static void ConstructPartDesc( tabString_t * tabs ) { char * cp; cp = message; *cp = '\0'; if ( tabs[T_PART].len ) { cp = TabStringCpy( cp, &tabs[T_PART] ); *cp++ = ' '; } if ( tabs[T_DESC].len ) { cp = TabStringCpy( cp, &tabs[T_DESC] ); *cp++ = ' '; } if ( tabs[T_REPMARK].len ) { cp = TabStringCpy( cp, &tabs[T_REPMARK] ); *cp++ = ' '; } else if ( tabs[T_ROADNAME].len ) { cp = TabStringCpy( cp, &tabs[T_ROADNAME] ); *cp++ = ' '; } else { strcpy( cp, _("Undecorated ") ); cp += strlen( cp ); } if ( tabs[T_NUMBER].len ) { cp = TabStringCpy( cp, &tabs[T_NUMBER] ); *cp++ = ' '; } *cp = '\0'; } static BOOL_T CarDlgLoadPartList( carPartParent_p parentP ) /* Loads PartList from parentP * Set carDlgPartnoInx to entry matching carDlgPartnoStr (if set and found) * Otherwise set carDlgPartnoInx and carDlgPartnoStr to 1st entry on list * Set carDlgDescStr to found entry */ { wIndex_t listInx; wIndex_t inx; carPart_p partP; carPart_t lastPart; tabString_t tabs[7]; BOOL_T found; carPart_p selPartP; carDlgPartnoInx = -1; wListClear( (wList_p)carDlgPLs[I_CD_PARTNO_LIST].control ); if ( parentP==NULL ) { carDlgPartnoStr[0] = '\0'; carDlgDescStr[0] = '\0'; return FALSE; } found = FALSE; selPartP = NULL; lastPart.title = NULL; for ( inx=0; inxparts_da.cnt; inx++ ) { partP = carPart(parentP,inx); TabStringExtract( partP->title, 7, tabs ); ConstructPartDesc( tabs ); lastPart.paramFileIndex = partP->paramFileIndex; if ( message[0] && IsParamValid(partP->paramFileIndex) && ( lastPart.title == NULL || Cmp_part( &lastPart, partP ) != 0 ) ) { listInx = wListAddValue( (wList_p)carDlgPLs[I_CD_PARTNO_LIST].control, message, NULL, partP ); if ( carDlgPartnoInx<0 && (carDlgPartnoStr[0]?TabStringCmp( carDlgPartnoStr, &tabs[T_PART] ) == 0:TRUE) ) { carDlgPartnoInx = listInx; found = TRUE; selPartP = partP; } if ( selPartP == NULL ) selPartP = partP; lastPart = *partP; } } if ( selPartP == NULL ) { carDlgPartnoStr[0] = '\0'; carDlgDescStr[0] = '\0'; } else { if ( carDlgPartnoInx<0 ) carDlgPartnoInx = 0; TabStringExtract( selPartP->title, 7, tabs ); TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); TabStringCpy( carDlgDescStr, &tabs[T_DESC] ); } return found; } static void CarDlgLoadPart( carPart_p partP ) { tabString_t tabs[7]; roadnameMap_p roadnameMapP; CarDlgLoadDimsFromPart( partP ); carDlgBodyColor = partP->color; carDlgTypeInx = CarProtoFindTypeCode( partP->type ); carDlgIsLoco = ((partP->type)&1)!=0; TabStringExtract( partP->title, 7, tabs ); TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); TabStringCpy( carDlgDescStr, &tabs[T_DESC] ); roadnameMapP = LoadRoadnameList( &tabs[T_ROADNAME], &tabs[T_REPMARK] ); carDlgRoadnameInx = lookupListIndex+1; if ( roadnameMapP ) { TabStringCpy( carDlgRoadnameStr, &tabs[T_ROADNAME] ); CarDlgLoadRoadnameList(); TabStringCpy( carDlgRepmarkStr, &tabs[T_REPMARK] ); } else { carDlgRoadnameInx = 0; strcpy( carDlgRoadnameStr, _("Undecorated") ); carDlgRepmarkStr[0] = '\0'; } TabStringCpy( carDlgNumberStr, &tabs[T_NUMBER] ); carDlgBodyColor = partP->color; } static BOOL_T CarDlgLoadLists( BOOL_T isItem, tabString_t * tabs, SCALEINX_T scale ) { BOOL_T loadCustomUnknown = isItem; DIST_T ratio; carPartParent_p parentP; static carProto_t protoTmp; static char protoTmpDesc[STR_SIZE]; if ( tabs ) TabStringCpy( carDlgManufStr, &tabs[T_MANUF] ); if ( strcasecmp( carDlgManufStr, "unknown" ) == 0 || strcasecmp( carDlgManufStr, "custom" ) == 0 ) { loadCustomUnknown = TRUE; /*isItem = FALSE;*/ } if ( (!CarDlgLoadManufList( !isItem, loadCustomUnknown, scale )) && tabs ) { TabStringCpy( carDlgManufStr, &tabs[T_MANUF] ); carDlgManufInx = wListAddValue( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, carDlgManufStr, NULL, NULL ); isItem = FALSE; } if ( isItem ) { parentP = (carPartParent_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, carDlgManufInx ); if ( parentP ) { if ( tabs ) TabStringCpy( carDlgProtoStr, &tabs[T_PROTO] ); if ( CarDlgLoadProtoList( carDlgManufStr, scale, TRUE ) || !tabs ) { parentP = (carPartParent_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); if ( parentP ) { if ( tabs ) TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); if ( CarDlgLoadPartList( parentP ) || ( (!tabs) && carDlgPartnoInx>=0 ) ) { return TRUE; } } } } } if ( tabs ) TabStringCpy( carDlgProtoStr, &tabs[T_PROTO] ); if ( !CarDlgLoadProtoList( NULL, 0, TRUE ) && tabs ) { /* create dummy proto */ ratio = GetScaleRatio( scale ); protoTmp.contentsLabel = "temporary"; protoTmp.paramFileIndex = PARAM_LAYOUT; strcpy( protoTmpDesc, carDlgProtoStr ); protoTmp.desc = protoTmpDesc; protoTmp.options = (carDlgIsLoco?CAR_DESC_IS_LOCO:0); protoTmp.type = typeListMap[carDlgTypeInx].value; protoTmp.dim.carWidth = carDlgDim.carWidth*ratio; protoTmp.dim.carLength = carDlgDim.carLength*ratio; protoTmp.dim.coupledLength = carDlgDim.coupledLength*ratio; protoTmp.dim.truckCenter = carDlgDim.truckCenter*ratio; protoTmp.dim.truckCenterOffset = carDlgDim.truckCenterOffset*ratio; CarProtoDlgCreateDummyOutline( &carProtoSegCnt, &carProtoSegPtr, (BOOL_T)carDlgIsLoco, protoTmp.dim.carLength, protoTmp.dim.carWidth, drawColorBlue ); protoTmp.segCnt = carProtoSegCnt; protoTmp.segPtr = carProtoSegPtr; GetSegBounds( zero, 0.0, carProtoSegCnt, carProtoSegPtr, &protoTmp.orig, &protoTmp.size ); TabStringCpy( carDlgProtoStr, &tabs[T_PROTO] ); carDlgProtoInx = wListAddValue( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoStr, NULL, &protoTmp );/*??*/ } carDlgPartnoInx = -1; if ( tabs ) { TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); TabStringCpy( carDlgDescStr, &tabs[T_DESC] ); } return FALSE; } static void CarDlgShowControls( void ) { /*ParamControlActive( &carDlgPG, I_CD_MANUF_LIST, S_ITEM||(S_PART&&carDlgUpdatePartPtr) );*/ ParamControlShow( &carDlgPG, I_CD_NEW, S_ITEM ); ParamControlShow( &carDlgPG, I_CD_NEWPROTO, S_PART ); ParamControlShow( &carDlgPG, I_CD_ITEMINDEX, S_ITEM && carDlgDispMode==0 ); ParamControlShow( &carDlgPG, I_CD_PURPRC, S_ITEM && carDlgDispMode==0 ); ParamControlShow( &carDlgPG, I_CD_CURPRC, S_ITEM && carDlgDispMode==0 ); ParamControlShow( &carDlgPG, I_CD_COND, S_ITEM && carDlgDispMode==0 ); ParamControlShow( &carDlgPG, I_CD_PURDAT, S_ITEM && carDlgDispMode==0 ); ParamControlShow( &carDlgPG, I_CD_SRVDAT, S_ITEM && carDlgDispMode==0 ); ParamControlShow( &carDlgPG, I_CD_NOTES, S_ITEM && carDlgDispMode==0 ); ParamControlShow( &carDlgPG, I_CD_MLTNUM, S_ITEM && carDlgUpdateItemPtr==NULL && carDlgDispMode==0 ); ParamControlShow( &carDlgPG, I_CD_QTY, S_ITEM && carDlgUpdateItemPtr==NULL && carDlgDispMode==0 ); ParamControlShow( &carDlgPG, I_CD_ROADNAME_LIST, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); ParamControlShow( &carDlgPG, I_CD_REPMARK, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); ParamControlShow( &carDlgPG, I_CD_NUMBER, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); ParamControlShow( &carDlgPG, I_CD_BODYCOLOR, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); ParamControlShow( &carDlgPG, I_CD_CARLENGTH, !( S_ITEM && carDlgDispMode==0 ) ); ParamControlShow( &carDlgPG, I_CD_CARWIDTH, !( S_ITEM && carDlgDispMode==0 ) ); ParamControlShow( &carDlgPG, I_CD_TRKCENTER, !( S_ITEM && carDlgDispMode==0 ) ); ParamControlShow( &carDlgPG, I_CD_TRKOFFSET, !( S_ITEM && carDlgDispMode==0 ) ); ParamControlShow( &carDlgPG, I_CD_CANVAS, !( S_ITEM && carDlgDispMode==0 ) ); ParamControlShow( &carDlgPG, I_CD_CPLRLEN, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); ParamControlShow( &carDlgPG, I_CD_CPLDLEN, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); ParamControlShow( &carDlgPG, I_CD_CPLRMNT, S_PART || ( S_ITEM && carDlgDispMode==1 ) ); ParamControlShow( &carDlgPG, I_CD_DISPMODE, S_ITEM ); ParamControlShow( &carDlgPG, I_CD_TYPE_LIST, S_PROTO ); ParamControlShow( &carDlgPG, I_CD_FLIP, S_PROTO ); ParamControlShow( &carDlgPG, I_CD_DESC_STR, S_PART || (currState==S_ItemEnter) ); ParamControlShow( &carDlgPG, I_CD_IMPORT, S_PROTO ); ParamControlShow( &carDlgPG, I_CD_RESET, S_PROTO ); ParamControlShow( &carDlgPG, I_CD_PARTNO_STR, S_PART || (currState==S_ItemEnter) ); ParamControlShow( &carDlgPG, I_CD_PARTNO_LIST, (currState==S_ItemSel) ); ParamControlShow( &carDlgPG, I_CD_ISLOCO, S_PROTO ); ParamControlShow( &carDlgPG, I_CD_PROTOKIND_LIST, !S_PROTO ); ParamControlShow( &carDlgPG, I_CD_PROTOTYPE_LIST, !S_PROTO ); ParamControlShow( &carDlgPG, I_CD_PROTOTYPE_STR, S_PROTO ); ParamControlShow( &carDlgPG, I_CD_MANUF_LIST, !S_PROTO ); /*ParamControlActive( &carDlgPG, I_CD_PROTOTYPE_STR, S_PROTO && carDlgUpdateProtoPtr==NULL );*/ ParamControlActive( &carDlgPG, I_CD_ITEMINDEX, S_ITEM && carDlgUpdateItemPtr==NULL ); ParamControlActive( &carDlgPG, I_CD_MLTNUM, S_ITEM && carDlgQuantity>1 ); ParamControlActive( &carDlgPG, I_CD_IMPORT, selectedTrackCount > 0 ); ParamLoadMessage( &carDlgPG, I_CD_MSG, "" ); if ( S_ITEM ) { if ( carDlgUpdateItemPtr == NULL ) { sprintf( message, _("New %s Scale Car"), GetScaleName( carDlgScaleInx ) ); wButtonSetLabel( carDlgPG.okB, _("Add") ); } else { sprintf( message, _("Update %s Scale Car"), GetScaleName( carDlgScaleInx ) ); wButtonSetLabel( carDlgPG.okB, _("Update") ); } wWinSetTitle( carDlgPG.win, message ); } else if ( S_PART ) { if ( carDlgUpdatePartPtr == NULL ) { sprintf( message, _("New %s Scale Car Part"), GetScaleName( carDlgScaleInx ) ); wButtonSetLabel( carDlgPG.okB, _("Add") ); } else { sprintf( message, _("Update %s Scale Car Part"), GetScaleName( carDlgScaleInx ) ); wButtonSetLabel( carDlgPG.okB, _("Update") ); } wWinSetTitle( carDlgPG.win, message ); } else if ( S_PROTO ) { if ( carDlgUpdateProtoPtr == NULL ) { wWinSetTitle( carDlgPG.win, _("New Prototype") ); wButtonSetLabel( carDlgPG.okB, _("Add") ); } else { wWinSetTitle( carDlgPG.win, _("Update Prototype") ); wButtonSetLabel( carDlgPG.okB, _("Update") ); } } ParamLoadControls( &carDlgPG ); ParamDialogOkActive( &carDlgPG, S_ITEM ); CarDlgUpdate( &carDlgPG, -1, NULL ); } static void CarDlgDoActions( carDlgAction_e * actions ) { carPart_p partP; carPartParent_p parentP; carProto_p protoP; wIndex_t inx; int offset; DIST_T ratio; tabString_t tabs[7]; char * cp; BOOL_T reload[COUNT( carDlgPLs )]; #define RELOAD_DIMS \ reload[I_CD_CARLENGTH] = reload[I_CD_CARWIDTH] = reload[I_CD_CPLDLEN] = \ reload[I_CD_TRKCENTER] = reload[I_CD_TRKOFFSET] = reload[I_CD_CPLRLEN] = TRUE #define RELOAD_PARTDATA \ RELOAD_DIMS; \ reload[I_CD_PARTNO_STR] = reload[I_CD_DESC_STR] = \ reload[I_CD_ROADNAME_LIST] = reload[I_CD_REPMARK] = \ reload[I_CD_NUMBER] = reload[I_CD_BODYCOLOR] = TRUE #define RELOAD_LISTS \ reload[I_CD_MANUF_LIST] = \ reload[I_CD_PROTOKIND_LIST] = \ reload[I_CD_PROTOTYPE_LIST] = \ reload[I_CD_PARTNO_LIST] = TRUE memset( reload, 0, sizeof reload ); while ( 1 ) { LOG( log_carDlgState, 2, ( "Action = %s\n", carDlgAction_s[*actions] ) ) switch ( *actions++ ) { case A_Return: for ( inx=0; inxtitle, 7, tabs ); TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); TabStringCpy( carDlgDescStr, &tabs[T_DESC] ); reload[I_CD_PARTNO_STR] = reload[I_CD_DESC_STR] = TRUE; } break; case A_IsNewProto: offset = (int)*actions++; if (carDlgNewProtoPtr==NULL) { actions += offset; } else { strcpy( carDlgProtoStr, carDlgNewProtoPtr->desc ); } break; case A_LoadDataFromPartList: partP = (carPart_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PARTNO_LIST].control, carDlgPartnoInx ); if ( partP != NULL ){ CarDlgLoadPart(partP); RELOAD_PARTDATA; RELOAD_PARTDATA; } break; case A_LoadDimsFromStack: carDlgDim = carDlgStk[carDlgStkPtr].dim; carDlgCouplerLength = carDlgStk[carDlgStkPtr].couplerLength; carDlgTypeInx = carDlgStk[carDlgStkPtr].typeInx; carDlgIsLoco = (typeListMap[carDlgTypeInx].value&1) != 0; RELOAD_DIMS; break; case A_LoadManufListForScale: CarDlgLoadManufList( FALSE, TRUE, carDlgScaleInx ); reload[I_CD_MANUF_LIST] = TRUE; break; case A_LoadManufListAll: CarDlgLoadManufList( TRUE, FALSE, carDlgScaleInx ); reload[I_CD_MANUF_LIST] = TRUE; break; case A_LoadProtoListForManuf: parentP = (carPartParent_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, carDlgManufInx ); CarDlgLoadProtoList( parentP->manuf, parentP->scale, TRUE ); reload[I_CD_PROTOKIND_LIST] = TRUE; reload[I_CD_PROTOTYPE_LIST] = TRUE; break; case A_LoadProtoListAll: CarDlgLoadProtoList( NULL, 0, TRUE ); reload[I_CD_PROTOKIND_LIST] = TRUE; reload[I_CD_PROTOTYPE_LIST] = TRUE; break; case A_LoadPartnoList: parentP = (carPartParent_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); CarDlgLoadPartList( parentP ); reload[I_CD_PARTNO_LIST] = TRUE; break; case A_LoadLists: if ( CarDlgLoadLists( TRUE, NULL, carDlgScaleInx ) ) currState = S_ItemSel; else currState = S_ItemEnter; break; case A_LoadDimsFromProtoList: protoP = (carProto_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); if ( protoP ) { CarDlgLoadDimsFromProto( protoP ); carDlgTypeInx = CarProtoFindTypeCode( protoP->type ); carDlgIsLoco = (protoP->options&CAR_DESC_IS_LOCO)!=0; } else { ratio = GetScaleRatio( carDlgScaleInx ); carDlgDim.carLength = 50*12/ratio; carDlgDim.carWidth = 10*12/ratio; carDlgDim.coupledLength = carDlgDim.carLength+carDlgCouplerLength*2; carDlgDim.truckCenter = carDlgDim.carLength-59.0*2.0/ratio; carDlgTypeInx = 0; carDlgIsLoco = (typeListMap[0].value&1); } RELOAD_DIMS; reload[I_CD_TYPE_LIST] = reload[I_CD_ISLOCO] = TRUE; break; case A_ConvertDimsToProto: ratio = GetScaleRatio( carDlgScaleInx ); carDlgDim.carLength *= ratio; carDlgDim.carWidth *= ratio; carDlgCouplerLength = 16.0; carDlgDim.coupledLength = carDlgDim.carLength + 2 * carDlgCouplerLength; carDlgDim.truckCenter *= ratio; carDlgDim.truckCenterOffset *= ratio; RELOAD_DIMS; break; case A_Redraw: CarDlgRedraw( carDlgD.d, NULL, 0, 0 ); break; case A_ClrManuf: carDlgManufStr[0] = '\0'; wListSetValue( (wList_p)carDlgPLs[I_CD_MANUF_LIST].control, "" ); carDlgManufInx = -1; break; case A_ClrPartnoStr: carDlgPartnoStr[0] = '\0'; carDlgDescStr[0] = '\0'; reload[I_CD_PARTNO_STR] = reload[I_CD_DESC_STR] = TRUE; break; case A_ClrNumberStr: carDlgNumberStr[0] = '\0'; reload[I_CD_NUMBER] = TRUE; break; case A_LoadProtoStrFromList: wListGetValues( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoStr, sizeof carDlgProtoStr, NULL, NULL ); #ifdef LATER protoP = (carProto_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); if ( protoP ) { carDlgTypeInx = CarProtoFindTypeCode( protoP->type ); carDlgIsLoco = (protoP->options&CAR_DESC_IS_LOCO)!=0; } #endif break; case A_ShowPartnoList: reload[I_CD_PARTNO_LIST] = TRUE; ParamControlShow( &carDlgPG, I_CD_PARTNO_LIST, TRUE ); ParamControlShow( &carDlgPG, I_CD_DESC_STR, FALSE ); ParamControlShow( &carDlgPG, I_CD_PARTNO_STR, FALSE ); break; case A_HidePartnoList: reload[I_CD_PARTNO_STR] = reload[I_CD_DESC_STR] = TRUE; ParamControlShow( &carDlgPG, I_CD_PARTNO_LIST, FALSE ); ParamControlShow( &carDlgPG, I_CD_DESC_STR, TRUE ); ParamControlShow( &carDlgPG, I_CD_PARTNO_STR, TRUE ); break; case A_PushDims: if ( carDlgStkPtr >= CARDLG_STK_SIZE ) AbortProg( "carDlgNewDesc: CARDLG_STK_SIZE" ); carDlgStk[carDlgStkPtr].dim = carDlgDim; carDlgStk[carDlgStkPtr].couplerLength = carDlgCouplerLength; carDlgStk[carDlgStkPtr].state = currState; carDlgStk[carDlgStkPtr].changed = carDlgChanged; carDlgStk[carDlgStkPtr].typeInx = carDlgTypeInx; if ( currState == S_ItemSel && carDlgPartnoInx >= 0 ) carDlgStk[carDlgStkPtr].partP = (carPart_p)wListGetItemContext( (wList_p)carDlgPLs[I_CD_PARTNO_LIST].control, carDlgPartnoInx ); else carDlgStk[carDlgStkPtr].partP = NULL; carDlgStkPtr++; break; case A_PopDims: break; case A_PopTitleAndTypeinx: if ( carDlgStk[carDlgStkPtr].partP ) { TabStringExtract( carDlgStk[carDlgStkPtr].partP->title, 7, tabs ); strcpy( carDlgManufStr, carDlgStk[carDlgStkPtr].partP->parent->manuf ); strcpy( carDlgProtoStr, carDlgStk[carDlgStkPtr].partP->parent->proto ); TabStringCpy( carDlgPartnoStr, &tabs[T_PART] ); TabStringCpy( carDlgDescStr, &tabs[T_DESC] ); } carDlgTypeInx = carDlgStk[carDlgStkPtr].typeInx; break; case A_PopCouplerLength: carDlgCouplerLength = carDlgStk[carDlgStkPtr].couplerLength; break; case A_ShowControls: CarDlgShowControls(); break; case A_LoadInfoFromUpdateItem: carDlgScaleInx = carDlgUpdateItemPtr->scaleInx; carDlgItemIndex = carDlgUpdateItemPtr->index; TabStringExtract( carDlgUpdateItemPtr->title, 7, tabs ); TabStringCpy( carDlgManufStr, &tabs[T_MANUF] ); TabStringCpy( carDlgProtoStr, &tabs[T_PROTO] ); TabStringCpy( carDlgRoadnameStr, &tabs[T_ROADNAME] ); TabStringCpy( carDlgRepmarkStr, &tabs[T_REPMARK] ); TabStringCpy( carDlgNumberStr, &tabs[T_NUMBER] ); carDlgDim = carDlgUpdateItemPtr->dim; carDlgBodyColor = carDlgUpdateItemPtr->color; carDlgTypeInx = CarProtoFindTypeCode( carDlgUpdateItemPtr->type ); carDlgIsLoco = (carDlgUpdateItemPtr->type&1)!=0; carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); wPrefSetFloat( carDlgPG.nameStr, message, carDlgCouplerLength ); carDlgCouplerMount = (carDlgUpdateItemPtr->options&CAR_DESC_COUPLER_MODE_BODY)!=0; carDlgIsLoco = (carDlgUpdateItemPtr->options&CAR_DESC_IS_LOCO)!=0; carDlgPurchPrice = carDlgUpdateItemPtr->data.purchPrice; sprintf( carDlgPurchPriceStr, "%0.2f", carDlgPurchPrice ); carDlgCurrPrice = carDlgUpdateItemPtr->data.currPrice; sprintf( carDlgCurrPriceStr, "%0.2f", carDlgCurrPrice ); carDlgCondition = carDlgUpdateItemPtr->data.condition; carDlgConditionInx = MapCondition( carDlgUpdateItemPtr->data.condition ); carDlgPurchDate = carDlgUpdateItemPtr->data.purchDate; if ( carDlgPurchDate ) sprintf( carDlgPurchDateStr, "%ld", carDlgPurchDate ); else carDlgPurchDateStr[0] = '\0'; carDlgServiceDate = carDlgUpdateItemPtr->data.serviceDate; if ( carDlgServiceDate ) sprintf( carDlgServiceDateStr, "%ld", carDlgServiceDate ); else carDlgServiceDateStr[0] = '\0'; wTextClear( (wText_p)carDlgPLs[I_CD_NOTES].control ); if ( carDlgUpdateItemPtr->data.notes ) { strncpy( message, carDlgUpdateItemPtr->data.notes, sizeof message ); message[sizeof message - 1] = '\0'; for ( cp=message; *cp; cp++ ) if ( *cp == '\n' ) *cp = ' '; wTextAppend( (wText_p)carDlgPLs[I_CD_NOTES].control, message ); } LoadRoadnameList( &tabs[T_ROADNAME], &tabs[T_REPMARK] ); CarDlgLoadRoadnameList(); carDlgRoadnameInx = lookupListIndex+1; memset( reload, 1, sizeof reload ); if ( CarDlgLoadLists( TRUE, tabs, carDlgScaleInx ) ) currState = S_ItemSel; else currState = S_ItemEnter; break; case A_LoadDataFromUpdatePart: carDlgScaleInx = carDlgUpdatePartPtr->parent->scale; TabStringExtract( carDlgUpdatePartPtr->title, 7, tabs ); tabs[T_MANUF].ptr = carDlgUpdatePartPtr->parent->manuf; tabs[T_MANUF].len = (int)strlen(carDlgUpdatePartPtr->parent->manuf); tabs[T_PROTO].ptr = carDlgUpdatePartPtr->parent->proto; tabs[T_PROTO].len = (int)strlen(carDlgUpdatePartPtr->parent->proto); CarDlgLoadLists( FALSE, tabs, carDlgScaleInx ); CarDlgLoadPart( carDlgUpdatePartPtr ); RELOAD_LISTS; RELOAD_DIMS; RELOAD_PARTDATA; break; case A_InitProto: if ( carDlgUpdateProtoPtr==NULL ) { carDlgProtoStr[0] = 0; carDlgDim.carLength = 50*12; carDlgDim.carWidth = 10*12; carDlgDim.coupledLength = carDlgDim.carLength+16.0*2.0; carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; carDlgDim.truckCenter = carDlgDim.carLength-59.0*2.0; carDlgDim.truckCenterOffset = 0; carDlgIsLoco = (typeListMap[carDlgTypeInx].value&1); } else { strcpy( carDlgProtoStr , carDlgUpdateProtoPtr->desc ); carDlgDim = carDlgUpdateProtoPtr->dim; carDlgCouplerLength = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; carDlgIsLoco = (carDlgUpdateProtoPtr->options&CAR_DESC_IS_LOCO)!=0; carDlgTypeInx = CarProtoFindTypeCode( carDlgUpdateProtoPtr->type ); carProtoSegCnt = carDlgUpdateProtoPtr->segCnt; carProtoSegPtr = carDlgUpdateProtoPtr->segPtr; currState = S_ProtoSel; } RELOAD_DIMS; break; case A_RecallCouplerLength: sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); carDlgCouplerLength = 16.0/GetScaleRatio(carDlgScaleInx); wPrefGetFloat( carDlgPG.nameStr, message, &carDlgCouplerLength, carDlgCouplerLength ); break; default: AbortProg( "carDlgDoActions: bad action" ); break; } } } static void CarDlgDoStateActions( carDlgAction_e * actions ) { CarDlgDoActions( actions ); LOG( log_carDlgState, 1, ( " ==> S_%s\n", carDlgState_s[currState] ) ) } static void CarDlgStateMachine( carDlgTransistion_e transistion ) { LOG( log_carDlgState, 1, ( "S_%s[T_%s]\n", carDlgState_s[currState], carDlgTransistion_s[transistion] ) ) CarDlgDoStateActions( stateMachine[currState][transistion] ); } static BOOL_T CheckCarDlgItemIndex( long * index ) { BOOL_T found = TRUE; BOOL_T updated = FALSE; int inx; carItem_p item; while ( found ) { found = FALSE; for ( inx=0; inxindex == *index ) { (*index)++; found = TRUE; updated = TRUE; break; } } } return !updated; } void CarDlgError( wBool_t ok, paramData_p p, char * msg ) { p->bInvalid = !ok; ParamHilite( p->group->win, p->control, !ok ); wWinPix_t h = wControlGetHeight(p->control); wControlSetBalloon( p->control, 0, -h*3/4, ok?NULL:msg ); } static void CarDlgUpdate( paramGroup_p pg, int inx, void * valueP ) { BOOL_T redraw = FALSE; roadnameMap_p roadnameMapP; char * cp, *cq; long valL, d, m; FLOAT_T ratio; BOOL_T ok = TRUE; DIST_T len; BOOL_T checkTruckCenter = FALSE; cmp_key_t cmp_key; coOrd orig, size, size2; carPartParent_p parentP; static DIST_T carDlgTruckOffsetL; static DIST_T carDlgTruckOffsetR; static long carDlgClock; static long carDlgCarLengthClock; static long carDlgTruckCenterClock; static long carDlgCoupledLengthClock; static long carDlgCouplerLengthClock; ratio = (S_PROTO?1.0:GetScaleRatio(carDlgScaleInx)); LOG( log_carDlgState, 3, ( "CarDlgUpdate( %d )\n", inx ) ) switch ( inx ) { case -1: if ( carDlgDim.truckCenter > 0 && carDlgDim.carLength > carDlgDim.truckCenter ) { carDlgTruckOffsetL = (carDlgDim.carLength - carDlgDim.truckCenter)/2 - carDlgDim.truckCenterOffset; carDlgTruckOffsetR = (carDlgDim.carLength - carDlgDim.truckCenter)/2 + carDlgDim.truckCenterOffset; } else { carDlgTruckOffsetL = 0; carDlgTruckOffsetR = 0; } carDlgCarLengthClock = carDlgCoupledLengthClock = carDlgTruckCenterClock = carDlgCouplerLengthClock = carDlgClock = 0; redraw = TRUE; break; case I_CD_MANUF_LIST: carDlgChanged++; wListGetValues( (wList_p)pg->paramPtr[inx].control, carDlgManufStr, sizeof carDlgManufStr, NULL, NULL ); if ( carDlgManufInx < 0 || wListGetItemContext( (wList_p)pg->paramPtr[inx].control, carDlgManufInx ) == NULL ) CarDlgStateMachine( T_ItemEnter ); #ifdef LATER else if ( strcasecmp( carDlgManufStr, "unknown" ) == 0 || strcasecmp( carDlgManufStr, "custom" ) == 0 ) CarDlgStateMachine( T_ItemEnter ); #endif else CarDlgStateMachine( T_ItemSel ); /*ParamControlShow( &carDlgPG, I_CD_MANUF_LIST, TRUE );*/ break; case I_CD_PROTOKIND_LIST: carDlgChanged++; carDlgTypeInx = (int)VP2L(wListGetItemContext( (wList_p)pg->paramPtr[inx].control, carDlgKindInx )); if ( S_PART || (currState==S_ItemEnter) ) { CarDlgLoadProtoList( NULL, 0, FALSE ); } else { parentP = NULL; if ( carDlgProtoInx >= 0 ) parentP = (carPartParent_p)wListGetItemContext( (wList_p)pg->paramPtr[I_CD_PROTOTYPE_LIST].control, carDlgProtoInx ); CarDlgLoadProtoList( carDlgManufStr, (parentP?parentP->scale:0), FALSE ); } CarDlgStateMachine( T_ProtoSel ); break; case I_CD_PROTOTYPE_LIST: carDlgChanged++; wListGetValues( (wList_p)pg->paramPtr[inx].control, carDlgProtoStr, sizeof carDlgProtoStr, NULL, NULL ); CarDlgStateMachine( T_ProtoSel ); break; case I_CD_PARTNO_LIST: carDlgChanged++; wListGetValues( (wList_p)pg->paramPtr[inx].control, carDlgPartnoStr, sizeof carDlgPartnoStr, NULL, NULL ); if ( carDlgPartnoInx >= 0 ) { CarDlgStateMachine( T_PartnoSel ); } else { CarDlgStateMachine( T_PartnoEnter ); wControlSetFocus( pg->paramPtr[I_CD_PARTNO_STR].control ); } break; case I_CD_DISPMODE: if ( !ParamCheckInputs( &carDlgPG, carDlgPLs[I_CD_DISPMODE].control ) ) { carDlgDispMode = 1-carDlgDispMode; ParamLoadControl( &carDlgPG, I_CD_DISPMODE ); break; } for ( inx=B; inx 0 ) { roadnameMapP = (roadnameMap_p)wListGetItemContext( (wList_p)pg->paramPtr[I_CD_ROADNAME_LIST].control, (wIndex_t)*(long*)valueP ); strcpy( carDlgRoadnameStr, roadnameMapP->roadname ); } else { wListGetValues( (wList_p)pg->paramPtr[I_CD_ROADNAME_LIST].control, carDlgRoadnameStr, sizeof carDlgRoadnameStr, NULL, NULL ); cmp_key.name = carDlgRoadnameStr; cmp_key.len = (int)strlen(carDlgRoadnameStr); roadnameMapP = LookupListElem( &roadnameMap_da, &cmp_key, Cmp_roadnameMap, 0 ); } if ( roadnameMapP ) { strcpy( carDlgRepmarkStr, roadnameMapP->repmark ); } else { carDlgRepmarkStr[0] = '\0'; } ParamLoadControl( pg, I_CD_REPMARK ); break; case I_CD_CARLENGTH: carDlgChanged++; if ( carDlgDim.carLength == 0.0 ) { carDlgCarLengthClock = 0; } else if ( carDlgDim.carLength < 100/ratio ) { return; } else if ( carDlgCouplerLength != 0 && ( carDlgDim.coupledLength == 0 || carDlgCouplerLengthClock >= carDlgCoupledLengthClock ) ) { len = carDlgDim.carLength+carDlgCouplerLength*2.0; if ( len > 0 ) { carDlgDim.coupledLength = len; ParamLoadControl( &carDlgPG, I_CD_CPLDLEN ); } carDlgCarLengthClock = ++carDlgClock; } else if ( carDlgDim.coupledLength != 0 && ( carDlgCouplerLength == 0 || carDlgCoupledLengthClock > carDlgCouplerLengthClock ) ) { len = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; if ( len > 0 ) { carDlgCouplerLength = len; ParamLoadControl( &carDlgPG, I_CD_CPLRLEN ); if ( !S_PROTO ) { sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); wPrefSetFloat( carDlgPG.nameStr, message, carDlgCouplerLength ); } } carDlgCarLengthClock = ++carDlgClock; } checkTruckCenter = TRUE; redraw = TRUE; break; case I_CD_CPLDLEN: carDlgChanged++; if ( carDlgDim.coupledLength == 0 ) { carDlgCoupledLengthClock = 0; } else if ( carDlgDim.coupledLength < 100/ratio ) { return; } else if ( carDlgDim.carLength != 0 && ( carDlgCouplerLength == 0 || carDlgCarLengthClock > carDlgCouplerLengthClock ) ) { len = (carDlgDim.coupledLength-carDlgDim.carLength)/2.0; if ( len > 0 ) { carDlgCouplerLength = len; ParamLoadControl( &carDlgPG, I_CD_CPLRLEN ); if ( !S_PROTO ) { sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); wPrefSetFloat( carDlgPG.nameStr, message, carDlgCouplerLength ); } } carDlgCoupledLengthClock = ++carDlgClock; } else if ( carDlgCouplerLength != 0 && ( carDlgDim.carLength == 0 || carDlgCouplerLengthClock >= carDlgCarLengthClock ) ) { len = carDlgDim.coupledLength-carDlgCouplerLength*2.0; if ( len > 0 ) { carDlgDim.carLength = len; ParamLoadControl( &carDlgPG, I_CD_CARLENGTH ); checkTruckCenter = TRUE; } carDlgCoupledLengthClock = ++carDlgClock; } redraw = TRUE; break; case I_CD_CPLRLEN: carDlgChanged++; if ( carDlgCouplerLength == 0 ) { carDlgCouplerLengthClock = 0; redraw = TRUE; break; } else if ( carDlgCouplerLength < 1/ratio ) { return; } else if ( carDlgDim.carLength != 0 && ( carDlgDim.coupledLength == 0 || carDlgCarLengthClock >= carDlgCoupledLengthClock ) ) { len = carDlgDim.carLength+carDlgCouplerLength*2.0; if ( len > 0 ) { carDlgDim.coupledLength = len; ParamLoadControl( &carDlgPG, I_CD_CPLDLEN ); } carDlgCouplerLengthClock = ++carDlgClock; } else if ( carDlgDim.coupledLength != 0 && ( carDlgDim.carLength == 0 || carDlgCoupledLengthClock > carDlgCarLengthClock ) ) { len = carDlgDim.coupledLength-carDlgCouplerLength*2.0; if ( len > 0 ) { carDlgDim.carLength = len; ParamLoadControl( &carDlgPG, I_CD_CARLENGTH ); checkTruckCenter = TRUE; } carDlgCouplerLengthClock = ++carDlgClock; } if ( !S_PROTO ) { sprintf( message, "%s-%s", carDlgPLs[I_CD_CPLRLEN].nameStr, GetScaleName(carDlgScaleInx) ); wPrefSetFloat( carDlgPG.nameStr, message, carDlgCouplerLength ); } redraw = TRUE; break; case I_CD_CARWIDTH: carDlgChanged++; if ( carDlgDim.carLength < 30/ratio ) return; redraw = TRUE; break; case I_CD_BODYCOLOR: carDlgChanged++; RecolorSegs( carDlgSegs_da.cnt, &carDlgSegs(0), carDlgBodyColor ); redraw = TRUE; break; case I_CD_ISLOCO: carDlgChanged++; redraw = TRUE; break; case I_CD_TRKOFFSET: carDlgChanged++; if ( carDlgDim.truckCenterOffset == 0 ) { carDlgTruckOffsetL = carDlgDim.truckCenter/2; carDlgTruckOffsetR = carDlgTruckOffsetL; } else if (carDlgDim.carLength - carDlgDim.truckCenter > 2*fabs(carDlgDim.truckCenterOffset)) { carDlgTruckOffsetL = carDlgDim.truckCenter/2 - carDlgDim.truckCenterOffset; carDlgTruckOffsetR = carDlgDim.truckCenter/2 + carDlgDim.truckCenterOffset; } else { carDlgTruckOffsetL = 0; carDlgTruckOffsetR = 0; } if ( 2*carDlgDim.truckCenterOffset > carDlgDim.carLength - carDlgDim.truckCenter) { ok = FALSE; CarDlgError( ok, &carDlgPLs[I_CD_TRKOFFSET], _("Truck Center Offset plus Truck Centers must be less than Car Length") ); } redraw = TRUE; break; case I_CD_TRKCENTER: carDlgChanged++; if ( carDlgDim.truckCenter == 0 ) { carDlgTruckOffsetL = 0; carDlgTruckOffsetR = 0; } else if ( carDlgDim.truckCenter < 100/ratio /*&& carDlgDim.carLength == 0.0*/ ) { carDlgTruckOffsetL = 0; carDlgTruckOffsetR = 0; return; } else if ( carDlgDim.carLength - carDlgDim.truckCenter > 2*fabs(carDlgDim.truckCenterOffset) ) { carDlgTruckOffsetL = carDlgDim.truckCenter/2-carDlgDim.truckCenterOffset; carDlgTruckOffsetR = carDlgDim.truckCenter/2+carDlgDim.truckCenterOffset; } else { carDlgTruckOffsetL = 0; carDlgTruckOffsetR = 0; } redraw = TRUE; break; case I_CD_QTY: wControlActive( carDlgPLs[I_CD_MLTNUM].control, carDlgQuantity>1 ); break; case I_CD_PURPRC: case I_CD_CURPRC: carDlgChanged++; *(FLOAT_T*)(pg->paramPtr[inx].context) = strtod( (char*)pg->paramPtr[inx].valueP, &cp ); if ( cp==NULL || *cp!='\0' ) { *(FLOAT_T*)(pg->paramPtr[inx].context) = -1; ok = FALSE; sprintf( message, "%s not valid", pg->paramPtr[inx].winLabel ); } CarDlgError( ok, &pg->paramPtr[inx], message ); break; case I_CD_COND: carDlgChanged++; carDlgCondition = (carDlgConditionInx==0)?0: (carDlgConditionInx==1)?100: (carDlgConditionInx==2)?80: (carDlgConditionInx==3)?60: (carDlgConditionInx==4)?40:20; break; case I_CD_PURDAT: case I_CD_SRVDAT: carDlgChanged++; for ( cp = (char*)pg->paramPtr[inx].valueP; *cp && isspace(*(unsigned char*)cp); cp++ ); if ( *cp ) { valL = strtol( cp, &cq, 10 ); if ( cq==NULL || *cq !='\0' ) { cp = N_("Enter a 8 digit numeric date (yyyymmdd)"); ok = FALSE; } else { if ( strlen(cp) != 8 || valL == 0) { cp = N_("Enter a 8 digit numeric date (yyyymmdd)"); ok = FALSE; } else if ( valL < 19000101 || valL > 21991231 ) { cp = N_("Enter a date between 19000101 and 21991231"); ok = FALSE; } else { d = valL % 100; m = (valL / 100) % 100; if ( m < 1 || m > 12 ) { cp = N_("Invalid month"); ok = FALSE; } else if ( d < 1 || d > 31 ) { cp = N_("Invalid day"); ok = FALSE; } else { cp = NULL; } } } if ( !ok ) { valL = -1; } } else { cp = NULL; valL = 0; } CarDlgError( ok, &pg->paramPtr[inx], cp ); if (inx == I_CD_PURDAT) carDlgPurchDate = valL; else carDlgServiceDate = valL; break; case I_CD_TYPE_LIST: carDlgChanged++; carDlgIsLoco = (typeListMap[carDlgTypeInx].value&1); ParamLoadControl( &carDlgPG, I_CD_ISLOCO ); redraw = TRUE; break; case I_CD_IMPORT: carDlgChanged++; WriteSelectedTracksToTempSegs(); carProtoSegCnt = tempSegs_da.cnt; carProtoSegPtr = (trkSeg_t*)tempSegs_da.ptr; CloneFilledDraw( carProtoSegCnt, carProtoSegPtr, TRUE ); GetSegBounds( zero, 0.0, carProtoSegCnt, carProtoSegPtr, &orig, &size ); if ( size.x <= 0.0 || size.y <= 0.0 || size.x < size.y ) { NoticeMessage( MSG_CARPROTO_BADSEGS, _("Ok"), NULL ); return; } orig.x = -orig.x; orig.y = -orig.y; MoveSegs( carProtoSegCnt, carProtoSegPtr, orig ); size2.x = floor(size.x*curScaleRatio+0.5); size2.y = floor(size.y*curScaleRatio+0.5); RescaleSegs( carProtoSegCnt, carProtoSegPtr, size2.x/size.x, size2.y/size.y, curScaleRatio ); carDlgDim.carLength = size2.x; carDlgDim.carWidth = size2.y; carDlgDim.coupledLength = carDlgDim.carLength + 32; if ( carDlgDim.carLength > 120 ) { carDlgDim.truckCenter = carDlgDim.carLength - 120; carDlgTruckOffsetL = (carDlgDim.carLength - carDlgDim.truckCenter)/2; carDlgTruckOffsetR = (carDlgDim.carLength - carDlgDim.truckCenter)/2; } else { carDlgDim.truckCenter = 0; carDlgTruckOffsetL = 0; carDlgTruckOffsetR = 0; } carDlgFlipToggle = FALSE; ParamLoadControl( &carDlgPG, I_CD_CARLENGTH ); ParamLoadControl( &carDlgPG, I_CD_CARWIDTH ); ParamLoadControl( &carDlgPG, I_CD_CPLRLEN ); ParamLoadControl( &carDlgPG, I_CD_TRKCENTER ); ParamLoadControl( &carDlgPG, I_CD_TRKOFFSET ); redraw = TRUE; break; case I_CD_RESET: carDlgChanged++; carProtoSegCnt = 0; redraw = TRUE; break; case I_CD_FLIP: carDlgChanged++; carDlgFlipToggle = ! carDlgFlipToggle; redraw = TRUE; break; } if ( checkTruckCenter && carDlgDim.carLength > 0 ) { if ( carDlgTruckOffsetL > 0 || carDlgTruckOffsetR > 0 ) { carDlgDim.truckCenter = carDlgTruckOffsetL + carDlgTruckOffsetR; carDlgDim.truckCenterOffset = (carDlgTruckOffsetR - carDlgTruckOffsetL)/2; } else { carDlgDim.truckCenter = carDlgDim.carLength * 0.75; carDlgDim.truckCenterOffset = 0; } ParamLoadControl( &carDlgPG, I_CD_TRKCENTER ); ParamLoadControl( &carDlgPG, I_CD_TRKOFFSET ); } if ( S_PART && carDlgManufStr[0] == '\0' ) ParamLoadMessage( &carDlgPG, I_CD_MSG, _("Select or Enter a Manufacturer") ); else if ( S_ITEM && carDlgUpdateItemPtr==NULL && ( valL = carDlgItemIndex , !CheckCarDlgItemIndex(&carDlgItemIndex) ) ) { sprintf( message, _("Item Index %ld duplicated an existing item: updated to new value"), valL ); ParamLoadControl( &carDlgPG, I_CD_ITEMINDEX ); ParamLoadMessage( &carDlgPG, I_CD_MSG, message ); ok = TRUE; } else { ParamLoadMessage( pg, I_CD_MSG, "" ); ok = TRUE; } if ( redraw ) CarDlgRedraw( carDlgD.d, NULL, 0, 0 ); ParamDialogOkActive( pg, ok ); } static void CarDlgNewDesc( void ) { carDlgNewPartPtr = NULL; carDlgNewProtoPtr = NULL; carDlgUpdatePartPtr = NULL; carDlgNumberStr[0] = '\0'; ParamLoadControl( &carDlgPG, I_CD_NUMBER ); CarDlgDoStateActions( item2partActions ); carDlgChanged = 0; } static void CarDlgNewProto( void ) { carProto_p protoP = CarProtoFind( carDlgProtoStr ); if ( protoP != NULL ) { carProtoSegCnt = protoP->segCnt;; carProtoSegPtr = protoP->segPtr;; } else { carProtoSegCnt = 0; carProtoSegPtr = NULL; } carDlgUpdateProtoPtr = NULL; carDlgNewProtoPtr = NULL; if ( S_ITEM ) CarDlgDoStateActions( item2protoActions ); else CarDlgDoStateActions( part2protoActions ); carDlgChanged = 0; } static void CarDlgClose( wWin_p win ) { carDlgState_e oldState; if ( carDlgChanged ) { if ( !inPlayback ) { if ( NoticeMessage( MSG_CARDESC_CHANGED, _("Yes"), _("No") ) <= 0 ) return; } else { PlaybackMessage( "Car Desc Changed\n" ); } } if ( carDlgStkPtr > 0 ) { carDlgStkPtr--; oldState = currState; currState = carDlgStk[carDlgStkPtr].state; carDlgChanged = carDlgStk[carDlgStkPtr].changed; if ( oldState == S_ProtoSel ) if ( S_PART ) CarDlgDoStateActions( proto2partActions ); else CarDlgDoStateActions( proto2itemActions ); else CarDlgDoStateActions( part2itemActions ); } else { wTextClear( (wText_p)carDlgPLs[I_CD_NOTES].control ); wHide( carDlgPG.win ); } } static void CarDlgOk( void * unused ) { long options = 0; int len; FILE * f; long number; char * cp; long count; tabString_t tabs[7]; char title[STR_LONG_SIZE]; carItem_p itemP=NULL; carPart_p partP=NULL; carProto_p protoP; BOOL_T reloadRoadnameList = FALSE; LOG( log_carDlgState, 3, ( "CarDlgOk()\n" ) ) ParamUpdate(&carDlgPG); /*ParamUpdate( &carDlgPG );*/ if ( carDlgDim.carLength <= 0.0 || carDlgDim.carWidth <= 0.0 || carDlgDim.truckCenter <= 0.0 || carDlgDim.truckCenterOffset < 0.0 || carDlgDim.coupledLength <= 0.0 ) { NoticeMessage( MSG_CARDESC_VALUE_ZERO, _("Ok"), NULL ); return; } if ( carDlgDim.carLength <= carDlgDim.carWidth || carDlgDim.truckCenter >= carDlgDim.carLength ) { NoticeMessage( MSG_CARDESC_BAD_DIM_VALUE, _("Ok"), NULL ); return; } if ( carDlgDim.coupledLength <= carDlgDim.carLength ) { NoticeMessage( MSG_CARDESC_BAD_COUPLER_LENGTH_VALUE, _("Ok"), NULL ); return; } if ( S_ITEM && carDlgUpdateItemPtr==NULL && !CheckCarDlgItemIndex(&carDlgItemIndex) ) { NoticeMessage( MSG_CARITEM_BAD_INDEX, _("Ok"), NULL ); ParamLoadControl( &carDlgPG, I_CD_ITEMINDEX ); return; } if ( S_ITEM && (carDlgPurchDate<0 || carDlgServiceDate<0 || carDlgPurchPrice <0 || carDlgCurrPrice<0)) return; if ( S_PROTO && carDlgProtoStr[0] == '\0' ) return; if ( S_PART && (carDlgManufStr[0] == '\0' || carDlgPartnoStr[0] == '\0')) return; if ( S_ITEM && carDlgItemIndex <= 0 ) return; if ( (!S_PROTO) && carDlgCouplerMount != 0 ) options |= CAR_DESC_COUPLER_MODE_BODY; if ( carDlgIsLoco == 1 ) options |= CAR_DESC_IS_LOCO; if ( S_ITEM ) { len = wTextGetSize( (wText_p)carDlgPLs[I_CD_NOTES].control ); sprintf( title, "%s\t%s\t%s\t%s\t%s\t%s\t%s", carDlgManufStr, carDlgProtoStr, carDlgDescStr, carDlgPartnoStr, carDlgRoadnameStr, carDlgRepmarkStr, carDlgNumberStr ); partP = NULL; if ( ( carDlgManufInx < 0 || carDlgPartnoInx < 0 ) && carDlgPartnoStr[0] ) { partP = CarPartFind( carDlgManufStr, (int)strlen(carDlgManufStr), carDlgPartnoStr, (int)strlen(carDlgPartnoStr), carDlgScaleInx ); if ( partP != NULL && NoticeMessage( MSG_CARPART_DUPNAME, _("Yes"), _("No") ) <= 0 ) return; partP = CarPartNew( NULL, PARAM_CUSTOM, carDlgScaleInx, title, options, typeListMap[carDlgTypeInx].value, &carDlgDim, carDlgBodyColor ); if ( partP != NULL ) { if ( ( f = OpenCustom("a") ) ) { SetCLocale(); CarPartWrite( f, partP ); fclose(f); SetUserLocale(); } } } if ( carDlgUpdateItemPtr!=NULL ) { carDlgQuantity = 1; } for ( count=0; count1 && carDlgMultiNum==0 ) { number = strtol( carDlgNumberStr, &cp, 10 ); if ( cp && *cp == 0 && number > 0 ) { sprintf( carDlgNumberStr, "%ld", number+1 ); sprintf( title, "%s\t%s\t%s\t%s\t%s\t%s\t%s", carDlgManufStr, carDlgProtoStr, carDlgDescStr, carDlgPartnoStr, carDlgRoadnameStr, carDlgRepmarkStr, carDlgNumberStr ); } } } if ( len > 0 ) { if ( itemP->data.notes ) itemP->data.notes = MyRealloc( itemP->data.notes, (len+2) * sizeof(wchar_t) ); else itemP->data.notes = MyMalloc( (len+2) * sizeof(wchar_t) ); // itemP->data.notes = (char*)MyMalloc( (len+2) * sizeof(wchar_t) ); wTextGetText( (wText_p)carDlgPLs[I_CD_NOTES].control, itemP->data.notes, len ); if ( itemP->data.notes[len-1] != '\n' ) { itemP->data.notes[len] = '\n'; itemP->data.notes[len+1] = '\0'; } else { itemP->data.notes[len] = '\0'; } } else if ( itemP->data.notes ) { MyFree( itemP->data.notes ); itemP->data.notes = NULL; } } if ( carDlgUpdateItemPtr==NULL ) CarInvListAdd( itemP ); else CarInvListUpdate( itemP ); SetFileChanged(); reloadRoadnameList = TRUE; if ( carDlgUpdateItemPtr==NULL ) { if ( carDlgQuantity > 1 ) { sprintf( message, _("Added %ld new Cars"), carDlgQuantity ); } else { strcpy( message, _("Added new Car") ); } } else { strcpy( message, _("Updated Car") ); } sprintf( message+strlen(message), "%s: %s %s %s %s %s %s", (partP?_(" and Part"):""), carDlgManufStr, carDlgPartnoStr, carDlgProtoStr, carDlgDescStr, (carDlgRepmarkStr[ 0 ]?carDlgRepmarkStr:carDlgRoadnameStr), carDlgNumberStr ); carDlgQuantity = 1; ParamLoadControl( &carDlgPG, I_CD_QTY ); } else if ( S_PART ) { if ( strcasecmp( carDlgRoadnameStr, "undecorated" ) == 0 ) { carDlgRoadnameStr[0] = '\0'; carDlgRepmarkStr[0] = '\0'; } if ( carDlgUpdatePartPtr==NULL ) { partP = CarPartFind( carDlgManufStr, (int)strlen(carDlgManufStr), carDlgPartnoStr, (int)strlen(carDlgPartnoStr), carDlgScaleInx ); if ( partP != NULL && NoticeMessage( MSG_CARPART_DUPNAME, _("Yes"), _("No") ) <= 0 ) return; } sprintf( message, "%s\t%s\t%s\t%s\t%s\t%s\t%s", carDlgManufStr, carDlgProtoStr, carDlgDescStr, carDlgPartnoStr, carDlgRoadnameStr, carDlgRepmarkStr, carDlgNumberStr ); carDlgNewPartPtr = CarPartNew( carDlgUpdatePartPtr, PARAM_CUSTOM, carDlgScaleInx, message, options, typeListMap[carDlgTypeInx].value, &carDlgDim, carDlgBodyColor ); if ( carDlgNewPartPtr != NULL && ( f = OpenCustom("a") ) ) { SetCLocale(); CarPartWrite( f, carDlgNewPartPtr ); fclose(f); SetUserLocale(); } reloadRoadnameList = TRUE; sprintf( message, _("%s Part: %s %s %s %s %s %s"), carDlgUpdatePartPtr==NULL?_("Added new"):_("Updated"), carDlgManufStr, carDlgPartnoStr, carDlgProtoStr, carDlgDescStr, carDlgRepmarkStr[ 0 ]?carDlgRepmarkStr:carDlgRoadnameStr, carDlgNumberStr ); } else if ( S_PROTO ) { if ( carDlgUpdateProtoPtr==NULL ) { protoP = CarProtoFind( carDlgProtoStr ); if ( protoP != NULL && NoticeMessage( MSG_CARPROTO_DUPNAME, _("Yes"), _("No") ) <= 0 ) return; } carDlgNewProtoPtr = CarProtoNew( carDlgUpdateProtoPtr, PARAM_CUSTOM, carDlgProtoStr, options, typeListMap[carDlgTypeInx].value, &carDlgDim, carDlgSegs_da.cnt, &carDlgSegs(0) ); if ( (f = OpenCustom("a") ) ) { SetCLocale(); CarProtoWrite( f, carDlgNewProtoPtr ); fclose(f); SetUserLocale(); } sprintf( message, _("%s Prototype: %s%s."), carDlgUpdateProtoPtr==NULL?_("Added new"):_("Updated"), carDlgProtoStr, carDlgUpdateProtoPtr==NULL?_(". Enter new values or press Close"):"" ); } if ( reloadRoadnameList ) { tabs[0].ptr = carDlgRoadnameStr; tabs[0].len = (int)strlen(carDlgRoadnameStr); tabs[1].ptr = carDlgRepmarkStr; tabs[1].len = (int)strlen(carDlgRepmarkStr); LoadRoadnameList( &tabs[0], &tabs[1] ); CarDlgLoadRoadnameList(); ParamLoadControl( &carDlgPG, I_CD_ROADNAME_LIST ); } ParamLoadMessage( &carDlgPG, I_CD_MSG, message ); DoChangeNotification( CHANGE_PARAMS ); carDlgChanged = 0; if ( S_ITEM ) { if ( carDlgUpdateItemPtr==NULL ) { if ( partP ) { TabStringExtract( title, 7, tabs ); if ( CarDlgLoadLists( TRUE, tabs, GetLayoutCurScale()) ) currState = S_ItemSel; else currState = S_ItemEnter; ParamLoadControl( &carDlgPG, I_CD_MANUF_LIST ); ParamLoadControl( &carDlgPG, I_CD_PROTOKIND_LIST ); ParamLoadControl( &carDlgPG, I_CD_PROTOTYPE_LIST ); ParamLoadControl( &carDlgPG, I_CD_PARTNO_LIST ); ParamLoadControl( &carDlgPG, I_CD_PARTNO_STR ); ParamLoadControl( &carDlgPG, I_CD_DESC_STR ); ParamControlShow( &carDlgPG, I_CD_PARTNO_LIST, carDlgPartnoInx>=0 ); ParamControlShow( &carDlgPG, I_CD_PARTNO_STR, carDlgPartnoInx<0 ); ParamControlShow( &carDlgPG, I_CD_DESC_STR, carDlgPartnoInx<0 ); } else if ( carDlgManufInx == -1 ) { carDlgManufStr[0] = '\0'; } return; } } else if ( S_PART ) { if ( carDlgUpdatePartPtr==NULL ) { number = strtol( carDlgPartnoStr, &cp, 10 ); if ( cp && *cp == 0 && number > 0 ) sprintf( carDlgPartnoStr, "%ld", number+1 ); else carDlgPartnoStr[0] = '\0'; carDlgNumberStr[0] = '\0'; ParamLoadControl( &carDlgPG, I_CD_PARTNO_STR ); ParamLoadControl( &carDlgPG, I_CD_NUMBER ); return; } } else if ( S_PROTO ) { if ( carDlgUpdateProtoPtr==NULL ) { carDlgProtoStr[0] = '\0'; ParamLoadControl( &carDlgPG, I_CD_PROTOTYPE_STR ); return; } } CarDlgClose( carDlgPG.win ); } static void CarDlgLayout( paramData_t * pd, int inx, wWinPix_t currX, wWinPix_t *xx, wWinPix_t *yy ) { static wWinPix_t col2pos = 0; wWinPix_t y0, y1; switch (inx) { case I_CD_PROTOTYPE_STR: case I_CD_PARTNO_STR: case I_CD_ISLOCO: case I_CD_IMPORT: case I_CD_TYPE_LIST: *yy = wControlGetPosY(carDlgPLs[inx-1].control); break; case I_CD_NEWPROTO: *yy = wControlGetPosY(carDlgPLs[I_CD_NEW].control); break; case I_CD_CPLRLEN: case I_CD_CARWIDTH: if ( col2pos == 0 ) col2pos = wLabelWidth( _("Coupler Length") )+20; *xx = wControlBeside(carDlgPLs[inx-1].control) + col2pos; break; case I_CD_DESC_STR: *yy = wControlBelow(carDlgPLs[I_CD_PARTNO_STR].control) + 3; break; case I_CD_CPLRMNT: *yy = wControlBelow(carDlgPLs[I_CD_TRKOFFSET].control) + 3; break; case I_CD_CPLDLEN: *yy = wControlBelow(carDlgPLs[I_CD_CPLRMNT].control) + 3; break; case I_CD_CANVAS: *yy = wControlBelow(carDlgPLs[I_CD_CPLDLEN].control)+5; break; case C: *yy = wControlGetPosY(carDlgPLs[B].control); break; case I_CD_MSG: y0 = wControlBelow(carDlgPLs[C-1].control); y1 = wControlBelow(carDlgPLs[D-1].control); *yy = ((y0>y1)?y0:y1) + 10; break; } } static void DoCarPartDlg( carDlgAction_e *actions ) { paramData_t * pd; int inx; if ( carDlgPG.win == NULL ) { ParamCreateDialog( &carDlgPG, MakeWindowTitle(_("New Car Part")), _("Add"), CarDlgOk, CarDlgClose, TRUE, CarDlgLayout, F_BLOCK|F_RESIZE|F_RECALLSIZE|PD_F_ALT_CANCELLABEL, CarDlgUpdate ); if ( carDlgDim.carWidth==0 ) carDlgDim.carWidth = 12.0*10.0/curScaleRatio; for ( pd=carDlgPG.paramPtr; pd<&carDlgPG.paramPtr[carDlgPG.paramCnt]; pd++ ) { if ( pd->type == PD_FLOAT && pd->valueP ) { sprintf( message, "%s-%s", pd->nameStr, curScaleName ); wPrefGetFloat( carDlgPG.nameStr, message, (FLOAT_T*)pd->valueP, *(FLOAT_T*)pd->valueP ); } } roadnameMapChanged = TRUE; for ( inx=0; inxbInvalid = FALSE; CarDlgDoStateActions( actions ); wShow( carDlgPG.win ); } EXPORT void CarDlgAddProto( void ) { /*carDlgPrototypeStr[0] = 0; */ carDlgTypeInx = 0; carDlgUpdateProtoPtr = NULL; DoCarPartDlg( protoNewActions ); } EXPORT void CarDlgAddDesc( void ) { if ( carProto_da.cnt <= 0 ) { NoticeMessage( MSG_NO_CARPROTO, _("Ok"), NULL ); return; } carDlgIsLoco = FALSE; carDlgUpdatePartPtr = NULL; carDlgNumberStr[0] = '\0'; ParamLoadControl( &carDlgPG, I_CD_NUMBER ); DoCarPartDlg( partNewActions ); } /* * Car Inventory List */ static wIndex_t carInvInx; static wIndex_t carInvSort[] = { 0, 1, 2, 3 }; #define N_SORT (COUNT( carInvSort )) static void CarInvDlgAdd( void ); static void CarInvDlgEdit( void ); static void CarInvDlgDelete( void ); static void CarInvDlgImportCsv( void ); static void CarInvDlgExportCsv( void ); static void CarInvDlgSaveText( void ); static void CarInvListLoad( void ); static wWinPix_t carInvColumnWidths[] = { -40, 30, 100, -50, 50, 130, 120, 100, -50, -50, 60, 55, 55, 40, 200 }; static const char * carInvColumnTitles[] = { N_("Index"), N_("Scale"), N_("Manufacturer"), N_("Part No"), N_("Type"), N_("Description"), N_("Roadname"), N_("Rep Marks"), N_("Purc Price"), N_("Curr Price"), N_("Condition"), N_("Purc Date"), N_("Srvc Date"), N_("Locat'n"), N_("Notes") }; static char * sortOrders[] = { N_("Index"), N_("Scale"), N_("Manufacturer"), N_("Part No"), N_("Type"), N_("Description"), N_("Roadname"), N_("RepMarks"), N_("Purch Price"), N_("Curr Price"), N_("Condition"), N_("Purch Date"), N_("Service Date") }; #define S_INDEX (0) #define S_SCALE (1) #define S_MANUF (2) #define S_PARTNO (3) #define S_TYPE (4) #define S_DESC (5) #define S_ROADNAME (6) #define S_REPMARKS (7) #define S_PURCHPRICE (8) #define S_CURRPRICE (9) #define S_CONDITION (10) #define S_PURCHDATE (11) #define S_SRVDATE (12) static paramListData_t carInvListData = { 30, 600, COUNT( carInvColumnTitles ), carInvColumnWidths, carInvColumnTitles }; static paramData_t carInvPLs[] = { #define I_CI_SORT (0) { PD_DROPLIST, &carInvSort[0], "sort1", PDO_LISTINDEX|0, I2VP(110), N_("Sort By") }, { PD_DROPLIST, &carInvSort[1], "sort2", PDO_LISTINDEX|PDO_DLGHORZ, I2VP(110), "" }, { PD_DROPLIST, &carInvSort[2], "sort3", PDO_LISTINDEX|PDO_DLGHORZ, I2VP(110), "" }, { PD_DROPLIST, &carInvSort[3], "sort4", PDO_LISTINDEX|PDO_DLGHORZ, I2VP(110), "" }, #define S (4) #define I_CI_LIST (S+0) { PD_LIST, &carInvInx, "list", PDO_LISTINDEX|PDO_DLGRESIZE|PDO_DLGNOLABELALIGN|PDO_DLGRESETMARGIN, &carInvListData, NULL, BO_READONLY|BL_MANY }, #define I_CI_EDIT (S+1) { PD_BUTTON, CarInvDlgEdit, "edit", PDO_DLGCMDBUTTON, NULL, N_("Edit") }, #define I_CI_ADD (S+2) { PD_BUTTON, CarInvDlgAdd, "add", 0, NULL, N_("Add"), 0, 0 }, #define I_CI_DELETE (S+3) { PD_BUTTON, CarInvDlgDelete, "delete", PDO_DLGWIDE, NULL, N_("Delete") }, #define I_CI_IMPORT_CSV (S+4) { PD_BUTTON, CarInvDlgImportCsv, "import", PDO_DLGWIDE, NULL, N_("Import") }, #define I_CI_EXPORT_CSV (S+5) { PD_BUTTON, CarInvDlgExportCsv, "export", 0, NULL, N_("Export") }, #define I_CI_PRINT (S+6) { PD_BUTTON, CarInvDlgSaveText, "savetext", 0, NULL, N_("List") } }; static paramGroup_t carInvPG = { "carinv", 0, carInvPLs, COUNT( carInvPLs ) }; static carItem_p CarInvDlgFindCurrentItem( void ) { wIndex_t selcnt = wListGetSelectedCount( (wList_p)carInvPLs[I_CI_LIST].control ); wIndex_t inx, cnt; if ( selcnt != 1 ) return NULL; cnt = wListGetCount( (wList_p)carInvPLs[I_CI_LIST].control ); for ( inx=0; inx=cnt ) return NULL; return (carItem_p)wListGetItemContext( (wList_p)carInvPLs[I_CI_LIST].control, inx ); } static void CarInvDlgFind( void * unused ) { carItem_p item = CarInvDlgFindCurrentItem(); coOrd pos; ANGLE_T angle; if ( item == NULL || item->car == NULL || IsTrackDeleted(item->car) ) return; CarGetPos( item->car, &pos, &angle ); CarSetVisible( item->car ); panCenter = pos; LOG( log_pan, 2, ( "PanCenter:%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); PanHere( I2VP(0) ); // CarInvDlgFind } static void CarInvDlgAdd( void ) { if ( carProto_da.cnt <= 0 ) { NoticeMessage( MSG_NO_CARPROTO, _("Ok"), NULL ); return; } carDlgUpdateItemPtr = NULL; DoCarPartDlg( itemNewActions ); } static void CarInvDlgEdit( void ) { carDlgUpdateItemPtr = CarInvDlgFindCurrentItem(); if ( carDlgUpdateItemPtr == NULL ) return; DoCarPartDlg( itemUpdActions ); } static void CarInvDlgDelete( void ) { carItem_p item; wIndex_t inx, inx1, cnt, selcnt; selcnt = wListGetSelectedCount( (wList_p)carInvPLs[I_CI_LIST].control ); if ( selcnt == 0 ) return; if ( NoticeMessage( MSG_CARINV_DELETE_CONFIRM, _("Yes"), _("No"), selcnt ) <= 0 ) return; cnt = wListGetCount( (wList_p)carInvPLs[I_CI_LIST].control ); for ( inx=0; inxcar && !IsTrackDeleted(item->car) ) continue; wListDelete( (wList_p)carInvPLs[I_CI_LIST].control, inx ); if ( item->title ) MyFree( item->title ); if ( item->data.number ) MyFree( item->data.number ); MyFree( item ); for ( inx1=inx; inx1 0 ); ParamDialogOkActive( &carInvPG, FALSE ); } static int CarInvSaveText( int files, char ** fileName, void * data ) { FILE * f; carItem_p item; int inx; int widths[9], width; tabString_t tabs[7]; char * cp0, * cp1; int len; assert( fileName != NULL ); assert( files == 1 ); SetCurrentPath( CARSPATHKEY, fileName[0] ); f = fopen( fileName[0], "w" ); if ( f == NULL ) { NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Car Inventory"), fileName[0], strerror(errno) ); return FALSE; } memset( widths, 0, sizeof widths ); for ( inx=0; inxtitle, 7, tabs ); sprintf( message, "%ld", item->index ); width = (int)strlen( message ); if ( width > widths[0] ) widths[0] = width; width = (int)strlen(GetScaleName(item->scaleInx)) + 1 + tabs[T_MANUF].len + 1 + tabs[T_PART].len; if ( width > widths[1] ) widths[1] = width; if ( tabs[T_PROTO].len > widths[2] ) widths[2] = tabs[T_PROTO].len; width = tabs[T_REPMARK].len + tabs[T_NUMBER].len; if ( tabs[T_REPMARK].len > 0 && tabs[T_NUMBER].len > 0 ) width += 1; if ( width > widths[3] ) widths[3] = width; if ( item->data.purchDate > 0 ) widths[4] = 8; if ( item->data.purchPrice > 0 ) { sprintf( message, "%0.2f", item->data.purchPrice ); width = (int)strlen(message); if ( width > widths[5] ) widths[5] = width; } if ( item->data.condition != 0 ) widths[6] = 5; if ( item->data.currPrice > 0 ) { sprintf( message, "%0.2f", item->data.currPrice ); width = (int)strlen(message); if ( width > widths[7] ) widths[7] = width; } if ( item->data.serviceDate > 0 ) widths[8] = 8; } fprintf( f, "%-*.*s %-*.*s %-*.*s %-*.*s", widths[0], widths[0], "#", widths[1], widths[1], "Part", widths[2], widths[2], "Description", widths[3], widths[3], "Rep Mark" ); if ( widths[4] ) fprintf( f, " %-*.*s", widths[4], widths[4], "PurDate" ); if ( widths[5] ) fprintf( f, " %-*.*s", widths[5], widths[5], "PurPrice" ); if ( widths[6] ) fprintf( f, " %-*.*s", widths[6], widths[6], "Cond" ); if ( widths[7] ) fprintf( f, " %-*.*s", widths[7], widths[7], "CurPrice" ); if ( widths[8] ) fprintf( f, " %-*.*s", widths[8], widths[8], "SrvDate" ); fprintf( f, "\n" ); for ( inx=0; inxtitle, 7, tabs ); sprintf( message, "%ld", item->index ); fprintf( f, "%.*s", widths[0], message ); width = tabs[T_MANUF].len + 1 + tabs[T_PART].len; sprintf( message, "%s %.*s %.*s", GetScaleName(item->scaleInx), tabs[T_MANUF].len, tabs[T_MANUF].ptr, tabs[T_PART].len, tabs[T_PART].ptr ); fprintf( f, " %-*s", widths[1], message ); fprintf( f, " %-*.*s", widths[2], tabs[T_PROTO].len, tabs[T_PROTO].ptr ); width = tabs[T_REPMARK].len + tabs[T_NUMBER].len; sprintf( message, "%.*s%s%.*s", tabs[T_REPMARK].len, tabs[T_REPMARK].ptr, (tabs[T_REPMARK].len > 0 && tabs[T_NUMBER].len > 0)?" ":"", tabs[T_NUMBER].len, tabs[T_NUMBER].ptr ); fprintf( f, " %-*s", widths[3], message ); if ( widths[4] > 0 ) { if ( item->data.purchDate > 0 ) { sprintf( message, "%ld", item->data.purchDate ); fprintf( f, " %*.*s", widths[4], widths[4], message ); } else { fprintf( f, " %*s", widths[4], " " ); } } if ( widths[5] > 0 ) { if ( item->data.purchPrice > 0 ) { sprintf( message, "%0.2f", item->data.purchPrice ); fprintf( f, " %*.*s", widths[5], widths[5], message ); } else { fprintf( f, " %*s", widths[5], " " ); } } if ( widths[6] > 0 ) { if ( item->data.condition != 0 ) { fprintf( f, " %-*.*s", widths[6], widths[6], condListMap[MapCondition(item->data.condition)].name ); } else { fprintf( f, " %*s", widths[6], " " ); } } if ( widths[7] > 0 ) { if ( item->data.purchPrice > 0 ) { sprintf( message, "%0.2f", item->data.purchPrice ); fprintf( f, " %*.*s", widths[7], widths[7], message ); } else { fprintf( f, " %*s", widths[7], " " ); } } if ( widths[8] > 0 ) { if ( item->data.serviceDate > 0 ) { sprintf( message, "%ld", item->data.serviceDate ); fprintf( f, " %*.*s", widths[8], widths[8], message ); } else { fprintf( f, " %*s", widths[8], " " ); } } fprintf( f, "\n" ); if ( item->data.notes ) { cp0 = item->data.notes; while ( 1 ) { cp1 = strchr( cp0, '\n' ); if ( cp1 ) { len = (int)(cp1-cp0); } else { len = (int)strlen( cp0 ); if ( len == 0 ) break; } fprintf( f, "%*.*s %*.*s\n", widths[0], widths[0], " ", len, len, cp0 ); if ( cp1 == NULL ) break; cp0 = cp1+1; } } } fclose( f ); return TRUE; } static struct wFilSel_t * carInvSaveText_fs; static void CarInvDlgSaveText( void ) { if ( carInvSaveText_fs == NULL ) carInvSaveText_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("List Cars"), "Text (*.txt)|*.txt", CarInvSaveText, NULL ); wFilSelect( carInvSaveText_fs, GetCurrentPath(CARSPATHKEY)); } static char *carCsvColumnTitles[] = { "Index", "Scale", "Manufacturer", "Type", "Partno", "Prototype", "Description", "Roadname", "Repmark", "Number", "Options", "CarLength", "CarWidth", "CoupledLength", "TruckOffset", "TruckCenter", "Color", "PurchPrice", "CurrPrice", "Condition", "PurchDate", "ServiceDate", "Notes" }; #define M_INDEX (0) #define M_SCALE (1) #define M_MANUF (2) #define M_TYPE (3) #define M_PARTNO (4) #define M_PROTO (5) #define M_DESC (6) #define M_ROADNAME (7) #define M_REPMARK (8) #define M_NUMBER (9) #define M_OPTIONS (10) #define M_CARLENGTH (11) #define M_CARWIDTH (12) #define M_CPLDLENGTH (13) #define M_TRKOFFSET (14) #define M_TRKCENTER (15) #define M_COLOR (16) #define M_PURCHPRICE (17) #define M_CURRPRICE (18) #define M_CONDITION (19) #define M_PURCHDATE (20) #define M_SRVDATE (21) #define M_NOTES (22) static int ParseCsvLine( char * line, int max_elem, tabString_t * tabs, int * map ) { int elem = 0; char * cp, * cq, * ptr; int rc, len; cp = line; for ( cq=cp+strlen(cp)-1; cq>cp&&isspace((unsigned char)*cq); cq-- ); cq[1] = '\0'; for ( elem=0; elem= 0 ) { tabs[map[elem]].ptr = ptr; tabs[map[elem]].len = len; } if ( *cp ) cp++; elem++; } return elem; } static int CarInvImportCsv( int files, char **fileName, void * data ) { FILE * f; carItem_p item; tabString_t tabs[40], partTabs[7]; int map[40]; int i, j, cnt, numCol, len, rc; char * cp, * cq; long type = 0; char title[STR_LONG_SIZE]; long index, options, color, condition, purchDate, srvcDate; carDim_t dim; FLOAT_T purchPrice, currPrice; int duplicateIndexError = 0; SCALEINX_T scale; carPart_p partP; int requiredCols; assert( fileName != NULL ); assert( files == 1 ); SetCurrentPath( CARSPATHKEY, fileName[0] ); f = fopen( fileName[0], "r" ); if ( f == NULL ) { NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Import Cars"), fileName[0], strerror(errno) ); return FALSE; } SetCLocale(); if ( fgets( message, sizeof message, f ) == NULL ) { NoticeMessage( MSG_CARIMP_NO_DATA, _("Continue"), NULL ); fclose( f ); SetUserLocale(); return FALSE; } for ( j=0; j<40; j++ ) map[j] = j; numCol = ParseCsvLine( message, 40, tabs, map ); if ( numCol <= 0 ) { fclose( f ); SetUserLocale(); return FALSE; } for ( j=0; j<40; j++ ) map[j] = -1; requiredCols = 0; for ( i=0; i= 0 ) { NoticeMessage( MSG_CARIMP_DUP_COLUMNS, _("Continue"), NULL, carCsvColumnTitles[j] ); fclose( f ); SetUserLocale(); return FALSE; } map[i] = j; /*j = COUNT( carCsvColumnTitles );*/ if ( j == M_SCALE || j == M_PROTO || j == M_MANUF || j == M_PARTNO ) requiredCols++; } } if ( map[i] == -1 ) { tabs[i].ptr[tabs[i].len] = '\0'; NoticeMessage( MSG_CARIMP_IGNORED_COLUMN, _("Continue"), NULL, tabs[i].ptr ); tabs[i].ptr[tabs[i].len] = ','; } } if ( requiredCols != 4 ) { NoticeMessage( MSG_CARIMP_MISSING_COLUMNS, _("Continue"), NULL ); fclose( f ); SetUserLocale(); return FALSE; } while ( fgets( message, sizeof message, f ) != NULL ) { cnt = ParseCsvLine( message, 40, tabs, map ); if ( cnt > numCol ) cnt = numCol; tabs[M_SCALE].ptr[tabs[M_SCALE].len] = '\0'; scale = LookupScale( tabs[M_SCALE].ptr ); tabs[M_SCALE].ptr[tabs[M_SCALE].len] = ','; index = TabGetLong( &tabs[M_INDEX] ); if ( index == 0 ) { CheckCarDlgItemIndex( &carDlgItemIndex ); index = carDlgItemIndex; } else { carDlgItemIndex = index; if ( !CheckCarDlgItemIndex(&index) ) { if ( !duplicateIndexError ) { NoticeMessage( MSG_CARIMP_DUP_INDEX, _("Ok"), NULL ); duplicateIndexError++; } carDlgItemIndex = index; } } #ifdef OBSOLETE if ( TabStringCmp( "Unknown", &tabs[M_MANUF] ) != 0 && TabStringCmp( "Custom", &tabs[M_MANUF] ) != 0 ) { if ( tabs[M_PARTNO].len == 0 ) { rc = NoticeMessage( MSG_CARIMP_MISSING_PARTNO, _("Continue"), _("Stop"), tabs[M_MANUF].ptr ); if ( rc <= 0 ) { fclose( f ); SetUserLocale(); return FALSE; } continue; } } #endif dim.carLength = TabGetFloat( &tabs[M_CARLENGTH] ); dim.carWidth = TabGetFloat( &tabs[M_CARWIDTH] ); dim.coupledLength = TabGetFloat( &tabs[M_CPLDLENGTH] ); dim.truckCenter = TabGetFloat( &tabs[M_TRKCENTER] ); dim.truckCenterOffset = TabGetFloat( &tabs[M_TRKOFFSET] ); partP = NULL; if ( tabs[M_MANUF].len > 0 && tabs[M_PARTNO].len > 0 ) partP = CarPartFind( tabs[M_MANUF].ptr, tabs[M_MANUF].len, tabs[M_PARTNO].ptr, tabs[M_PARTNO].len, scale ); if ( partP ) { TabStringExtract( partP->title, 7, partTabs ); if ( tabs[M_PROTO].len == 0 && partTabs[T_PROTO].len > 0 ) { tabs[M_PROTO].ptr = partTabs[T_PROTO].ptr; tabs[M_PROTO].len = partTabs[T_PROTO].len; } if ( tabs[M_DESC].len == 0 && partTabs[T_DESC].len > 0 ) { tabs[M_DESC].ptr = partTabs[T_DESC].ptr; tabs[M_DESC].len = partTabs[T_DESC].len; } if ( tabs[M_ROADNAME].len == 0 && partTabs[T_ROADNAME].len > 0 ) { tabs[M_ROADNAME].ptr = partTabs[T_ROADNAME].ptr; tabs[M_ROADNAME].len = partTabs[T_ROADNAME].len; } if ( tabs[M_REPMARK].len == 0 && partTabs[T_REPMARK].len > 0 ) { tabs[M_REPMARK].ptr = partTabs[T_REPMARK].ptr; tabs[M_REPMARK].len = partTabs[T_REPMARK].len; } if ( tabs[M_NUMBER].len == 0 && partTabs[T_NUMBER].len > 0 ) { tabs[M_NUMBER].ptr = partTabs[T_NUMBER].ptr; tabs[M_NUMBER].len = partTabs[T_NUMBER].len; } if ( dim.carLength <= 0 ) dim.carLength = partP->dim.carLength; if ( dim.carWidth <= 0 ) dim.carWidth = partP->dim.carWidth; if ( dim.coupledLength <= 0 ) dim.coupledLength = partP->dim.coupledLength; if ( dim.truckCenter <= 0 ) dim.truckCenter = partP->dim.truckCenter; if ( dim.truckCenterOffset < 0 ) dim.truckCenterOffset = partP->dim.truckCenterOffset; } if (dim.truckCenterOffset <0) dim.truckCenterOffset = 0; cp = TabStringCpy( title, &tabs[M_MANUF] ); *cp++ = '\t'; cp = TabStringCpy( cp, &tabs[M_PROTO] ); *cp++ = '\t'; cp = TabStringCpy( cp, &tabs[M_DESC] ); *cp++ = '\t'; cp = TabStringCpy( cp, &tabs[M_PARTNO] ); *cp++ = '\t'; cp = TabStringCpy( cp, &tabs[M_ROADNAME] ); *cp++ = '\t'; cp = TabStringCpy( cp, &tabs[M_REPMARK] ); *cp++ = '\t'; cp = TabStringCpy( cp, &tabs[M_NUMBER] ); *cp = '\0'; options = TabGetLong( &tabs[M_OPTIONS] ); type = TabGetLong( &tabs[M_TYPE] ); color = TabGetLong( &tabs[M_COLOR] ); purchPrice = TabGetFloat( &tabs[M_PURCHPRICE] ); currPrice = TabGetFloat( &tabs[M_CURRPRICE] ); condition = TabGetLong( &tabs[M_CONDITION] ); purchDate = TabGetLong( &tabs[M_PURCHDATE] ); srvcDate = TabGetLong( &tabs[M_SRVDATE] ); if ( dim.carLength <= 0 || dim.carWidth <= 0 || dim.coupledLength <= 0 || dim.truckCenter <= 0 ) { rc = NoticeMessage( MSG_CARIMP_MISSING_DIMS, _("Yes"), _("No"), message ); if ( rc <= 0 ) { fclose( f ); SetUserLocale(); return FALSE; } continue; } item = CarItemNew( NULL, PARAM_CUSTOM, index, scale, title, options, type, &dim, wDrawFindColor(color), purchPrice, currPrice, condition, purchDate, srvcDate ); if ( tabs[M_NOTES].len > 0 ) { item->data.notes = cp = MyMalloc( (tabs[M_NOTES].len+2) ); for ( cq=tabs[M_NOTES].ptr,len=tabs[M_NOTES].len; *cq&&len; ) { if ( strncmp( cq, "", 4 ) == 0 ) { *cp++ = '\n'; cq += 4; len -= 4; } else { *cp++ = *cq++; len -= 1; } } } SetFileChanged(); } fclose( f ); SetUserLocale(); CarInvListLoad(); return TRUE; } static struct wFilSel_t * carInvImportCsv_fs; static void CarInvDlgImportCsv( void ) { if ( carInvImportCsv_fs == NULL ) carInvImportCsv_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Import Cars"), _("Comma-Separated-Values (*.csv)|*.csv"), CarInvImportCsv, NULL ); wFilSelect( carInvImportCsv_fs, GetCurrentPath(CARSPATHKEY)); } static void CsvFormatString( FILE * f, char * str, int len, char * sep ) { while ( str && len>0 && str[len-1]=='\n' ) len--; if ( *str && len ) { fputc( '"', f ); for ( ; *str && len; str++,len-- ) { if ( !iscntrl((unsigned char) *str ) ) { if ( *str == '"' ) fputc( '"', f ); fputc( *str, f ); } else if ( *str == '\n' && str[1] && len > 1 ) { fprintf( f, "" ); } } fputc( '"', f ); } fprintf( f, "%s", sep ); } static void CsvFormatLong( FILE * f, long val, char * sep ) { if ( val != 0 ) fprintf( f, "%ld", val ); fprintf( f, "%s", sep ); } static void CsvFormatFloat( FILE * f, FLOAT_T val, int digits, char * sep ) { if ( val != 0.0 ) fprintf( f, "%0.*f", digits, val ); fprintf( f, "%s", sep ); } static int CarInvExportCsv( int files, char ** fileName, void * data ) { FILE * f; carItem_p item; long inx; tabString_t tabs[7]; char * sp; assert( fileName != NULL ); assert( files == 1 ); SetCurrentPath( CARSPATHKEY, fileName[0] ); f = fopen( fileName[0], "w" ); if ( f == NULL ) { NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Export Cars"), fileName[0], strerror(errno) ); return FALSE; } SetCLocale(); for ( inx=0; inxtitle, 7, tabs ); CsvFormatLong( f, item->index, "," ); sp = GetScaleName(item->scaleInx); CsvFormatString( f, sp, (int)strlen(sp), "," ); CsvFormatString( f, tabs[T_MANUF].ptr, tabs[T_MANUF].len, "," ); CsvFormatLong( f, item->type, "," ); CsvFormatString( f, tabs[T_PART].ptr, tabs[T_PART].len, "," ); CsvFormatString( f, tabs[T_PROTO].ptr, tabs[T_PROTO].len, "," ); CsvFormatString( f, tabs[T_DESC].ptr, tabs[T_DESC].len, "," ); CsvFormatString( f, tabs[T_ROADNAME].ptr, tabs[T_ROADNAME].len, "," ); CsvFormatString( f, tabs[T_REPMARK].ptr, tabs[T_REPMARK].len, "," ); CsvFormatString( f, tabs[T_NUMBER].ptr, tabs[T_NUMBER].len, "," ); CsvFormatLong( f, item->options, "," ); CsvFormatFloat( f, item->dim.carLength, 3, "," ); CsvFormatFloat( f, item->dim.carWidth, 3, "," ); CsvFormatFloat( f, item->dim.coupledLength, 3, "," ); CsvFormatFloat( f, item->dim.truckCenterOffset, 3, ","); CsvFormatFloat( f, item->dim.truckCenter, 3, "," ); CsvFormatLong( f, wDrawGetRGB(item->color), "," ); CsvFormatFloat( f, item->data.purchPrice, 2, "," ); CsvFormatFloat( f, item->data.currPrice, 2, "," ); CsvFormatLong( f, item->data.condition, "," ); CsvFormatLong( f, item->data.purchDate, "," ); CsvFormatLong( f, item->data.serviceDate, "," ); if ( item->data.notes ) CsvFormatString( f, item->data.notes, (int)strlen(item->data.notes), "\n" ); else CsvFormatString( f, "", (int)strlen(""), "\n" ); } fclose( f ); SetUserLocale(); return TRUE; } static struct wFilSel_t * carInvExportCsv_fs; static void CarInvDlgExportCsv( void ) { if ( carItemInfo_da.cnt <= 0 ) return; if ( carInvExportCsv_fs == NULL ) carInvExportCsv_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Export Cars"), _("Comma-Separated-Values (*.csv)|*.csv"), CarInvExportCsv, NULL ); wFilSelect( carInvExportCsv_fs, GetCurrentPath(CARSPATHKEY)); } static void CarInvLoadItem( carItem_p item ) { /* "Index", "Scale", "Manufacturer", "Type", "Part No", "Description", "Roadname", "RepMarks", "Purch Price", "Curr Price", "Condition", "Purch Date", "Service Date", "Location", "Notes" */ char *condition; char *location; char *manuf; char *road; char notes[100]; tabString_t tabs[7]; TabStringExtract( item->title, 7, tabs ); if ( item->data.notes ) { strncpy( notes, item->data.notes, sizeof notes - 1 ); notes[sizeof notes - 1] = '\0'; } else { notes[0] = '\0'; } condition = (item->data.condition < 10) ? N_("N/A"): (item->data.condition < 30) ? N_("Poor"): (item->data.condition < 50) ? N_("Fair"): (item->data.condition < 70) ? N_("Good"): (item->data.condition < 90) ? N_("Excellent"): N_("Mint"); if ( item->car && !IsTrackDeleted(item->car) ) location = N_("Layout"); else location = N_("Shelf"); manuf = TabStringDup(&tabs[T_MANUF]); road = TabStringDup(&tabs[T_ROADNAME]); sprintf( message, "%ld\t%s\t%s\t%.*s\t%s\t%.*s%s%.*s\t%s\t%.*s%s%.*s\t%0.2f\t%0.2f\t%s\t%ld\t%ld\t%s\t%s", item->index, GetScaleName(item->scaleInx), _(manuf), tabs[T_PART].len, tabs[T_PART].ptr, _(typeListMap[CarProtoFindTypeCode(item->type)].name), tabs[T_PROTO].len, tabs[T_PROTO].ptr, (tabs[T_PROTO].len>0 && tabs[T_DESC].len)?"/":"", tabs[T_DESC].len, tabs[T_DESC].ptr, _(road), tabs[T_REPMARK].len, tabs[T_REPMARK].ptr, (tabs[T_REPMARK].len>0&&tabs[T_NUMBER].len>0)?" ":"", tabs[T_NUMBER].len, tabs[T_NUMBER].ptr, item->data.purchPrice, item->data.currPrice, _(condition), item->data.purchDate, item->data.serviceDate, _(location), notes ); if (manuf) MyFree(manuf); if (road) MyFree(road); wListAddValue( (wList_p)carInvPLs[I_CI_LIST].control, message, NULL, item ); } static int Cmp_carInvItem( const void * ptr1, const void * ptr2 ) { carItem_p item1 = *(carItem_p*)ptr1; carItem_p item2 = *(carItem_p*)ptr2; tabString_t tabs1[7], tabs2[7]; int inx; int rc; TabStringExtract( item1->title, 7, tabs1 ); TabStringExtract( item2->title, 7, tabs2 ); for ( inx=0,rc=0; inxindex-item2->index); break; case S_SCALE: rc = (int)(item1->scaleInx-item2->scaleInx); case S_MANUF: rc = strncasecmp( tabs1[T_MANUF].ptr, tabs2[T_MANUF].ptr, max(tabs1[T_MANUF].len,tabs2[T_MANUF].len) ); break; case S_TYPE: rc = (int)(item1->type-item2->type); break; case S_PARTNO: rc = strncasecmp( tabs1[T_PART].ptr, tabs2[T_PART].ptr, max(tabs1[T_PART].len,tabs2[T_PART].len) ); break; case S_DESC: rc = strncasecmp( tabs1[T_PROTO].ptr, tabs2[T_PROTO].ptr, max(tabs1[T_PROTO].len,tabs2[T_PROTO].len) ); if ( rc != 0 ) break; rc = strncasecmp( tabs1[T_DESC].ptr, tabs2[T_DESC].ptr, max(tabs1[T_DESC].len,tabs2[T_DESC].len) ); break; case S_ROADNAME: rc = strncasecmp( tabs1[T_ROADNAME].ptr, tabs2[T_ROADNAME].ptr, max(tabs1[T_ROADNAME].len,tabs2[T_ROADNAME].len) ); break; case S_REPMARKS: rc = strncasecmp( tabs1[T_REPMARK].ptr, tabs2[T_REPMARK].ptr, max(tabs1[T_REPMARK].len,tabs2[T_REPMARK].len) ); break; case S_PURCHPRICE: rc = (int)(item1->data.purchPrice-item2->data.purchPrice); break; case S_CURRPRICE: rc = (int)(item1->data.currPrice-item2->data.currPrice); break; case S_CONDITION: rc = (int)(item1->data.condition-item2->data.condition); break; case S_PURCHDATE: rc = (int)(item1->data.purchDate-item2->data.purchDate); break; case S_SRVDATE: rc = (int)(item1->data.serviceDate-item2->data.serviceDate); break; default: break; } } return rc; } static void CarInvListLoad( void ) { int inx; carItem_p item; qsort( carItemInfo_da.ptr, carItemInfo_da.cnt, sizeof item, Cmp_carInvItem ); ParamControlShow( &carInvPG, I_CI_LIST, FALSE ); wListClear( (wList_p)carInvPLs[I_CI_LIST].control ); for ( inx=0; inx 0 ); ParamDialogOkActive( &carInvPG, FALSE ); } static void CarInvDlgUpdate( paramGroup_p pg, int inx, void * valueP ) { carItem_p item = NULL; wIndex_t cnt, selinx, selcnt; wBool_t enableDelete; if ( inx >= I_CI_SORT && inx < I_CI_SORT+N_SORT ) { item = CarInvDlgFindCurrentItem(); CarInvListLoad(); if ( item ) { carInvInx = (wIndex_t)CarItemFindIndex( item ); if ( carInvInx >= 0 ) ParamLoadControl( &carInvPG, I_CI_LIST ); } } else if ( inx == I_CI_LIST ) { cnt = wListGetCount( (wList_p)carInvPLs[I_CI_LIST].control ); enableDelete = TRUE; for ( selinx=selcnt=0; selinxcar && !IsTrackDeleted( item->car ) ) { enableDelete = FALSE; break; } } } item = CarInvDlgFindCurrentItem(); ParamDialogOkActive( pg, selcnt==1 && item && item->car && !IsTrackDeleted(item->car) ); ParamControlActive( &carInvPG, I_CI_EDIT, selcnt==1 && item && (item->car==NULL || IsTrackDeleted(item->car)) ); ParamControlActive( &carInvPG, I_CI_DELETE, selcnt>0 && enableDelete ); } } static void CarInvListAdd( carItem_p item ) { CarInvListLoad(); carInvInx = (wIndex_t)CarItemFindIndex( item ); if ( carInvInx >= 0 ) { ParamLoadControl( &carInvPG, I_CI_LIST ); } } static void CarInvListUpdate( carItem_p item ) { CarInvListLoad(); carInvInx = (wIndex_t)CarItemFindIndex( item ); if ( carInvInx >= 0 ) { ParamLoadControl( &carInvPG, I_CI_LIST ); } } EXPORT void DoCarDlg( void * unused ) { int inx, inx2; if ( carInvPG.win == NULL ) { ParamCreateDialog( &carInvPG, MakeWindowTitle(_("Car Inventory")), _("Find"), CarInvDlgFind, wHide, TRUE, NULL, F_BLOCK|F_RESIZE|F_RECALLSIZE|PD_F_ALT_CANCELLABEL, CarInvDlgUpdate ); for ( inx=I_CI_SORT; inxtitle, 7, tabs ); rd_inx = T_REPMARK; if ( tabs[T_REPMARK].len == 0 ) rd_inx = T_ROADNAME; sprintf( message, "\t%s\t%s\t%.*s\t%s%s%.*s%s%.*s%s%.*s", partP->parent->manuf, GetScaleName(partP->parent->scale), tabs[T_PART].len, tabs[T_PART].ptr, partP->parent->proto, tabs[T_DESC].len?", ":"", tabs[T_DESC].len, tabs[T_DESC].ptr, tabs[rd_inx].len?", ":"", tabs[rd_inx].len, tabs[rd_inx].ptr, tabs[T_NUMBER].len?" ":"", tabs[T_NUMBER].len, tabs[T_NUMBER].ptr ); return TRUE; } return FALSE; } static int CarProtoCustMgmProc( int cmd, void * data ) { carProto_p protoP = (carProto_p)data; switch ( cmd ) { case CUSTMGM_DO_COPYTO: return CarProtoWrite( customMgmF, protoP ); case CUSTMGM_CAN_EDIT: return TRUE; case CUSTMGM_DO_EDIT: if ( protoP == NULL ) return FALSE; carDlgUpdateProtoPtr = protoP; DoCarPartDlg( protoUpdActions ); return TRUE; case CUSTMGM_CAN_DELETE: return TRUE; case CUSTMGM_DO_DELETE: CarProtoDelete( protoP ); return TRUE; case CUSTMGM_GET_TITLE: sprintf( message, "\t%s\t\t%s\t%s", _("Prototype"), _(typeListMap[CarProtoFindTypeCode(protoP->type)].name), protoP->desc ); return TRUE; } return FALSE; } #include "bitmaps/carpart.xpm" #include "bitmaps/carproto.xpm" EXPORT void CarCustMgmLoad( void ) { long parentX, partX, protoX; carPartParent_p parentP; carPart_p partP; carProto_p carProtoP; static wIcon_p carpartI = NULL; static wIcon_p carprotoI = NULL; if ( carpartI == NULL ) carpartI = wIconCreatePixMap( carpart_xpm ); if ( carprotoI == NULL ) carprotoI = wIconCreatePixMap( carproto_xpm ); for ( parentX=0; parentXparts_da.cnt; partX++ ) { partP = carPart(parentP,partX); if ( partP->paramFileIndex != PARAM_CUSTOM ) continue; CustMgmLoad( carpartI, CarPartCustMgmProc, partP ); } } for ( protoX=0; protoXparamFileIndex != PARAM_CUSTOM ) continue; if (carProtoP->paramFileIndex == PARAM_CUSTOM) { CustMgmLoad( carprotoI, CarProtoCustMgmProc, carProtoP ); } } }