/** \file cselect.c * Handle selecting / unselecting track and basic operations on the selection */ /* XTrkCad - Model Railroad CAD * Copyright (C) 2005 Dave Bullis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include "draw.h" #include "ccurve.h" #include "tcornu.h" #include "tbezier.h" #include "track.h" #include "compound.h" #include "cselect.h" #include "cundo.h" #include "custom.h" #include "fileio.h" #include "layout.h" #include "param.h" #include "track.h" #include "cjoin.h" #include "draw.h" #include "misc.h" #include "common-ui.h" #include "ctrain.h" #include "bitmaps/bmendpt.xbm" #include "bitmaps/bma0.xbm" #include "bitmaps/bma45.xbm" #include "bitmaps/bma90.xbm" #include "bitmaps/bma135.xbm" #define SETMOVEMODE "MOVEMODE" EXPORT wIndex_t selectCmdInx; EXPORT wIndex_t moveCmdInx; EXPORT wIndex_t rotateCmdInx; EXPORT wIndex_t flipCmdInx; EXPORT long selectMode = 0; EXPORT long selectZero = 1; #define MAXMOVEMODE (3) static long moveMode = MAXMOVEMODE; static BOOL_T enableMoveDraw = TRUE; static BOOL_T move0B; static wDrawBitMap_p endpt_bm; static wDrawBitMap_p angle_bm[4]; track_p IsInsideABox(coOrd pos); static track_p moveDescTrk; static coOrd moveDescPos; int incrementalDrawLimit = 0; static int microCount = 0; static dynArr_t tlist_da; #define Tlist(N) DYNARR_N( track_p, tlist_da, N ) #define TlistAppend( T ) \ { DYNARR_APPEND( track_p, tlist_da, 10 );\ Tlist(tlist_da.cnt-1) = T; } BOOL_T TListSearch(track_p T) { for (int i=0; i incrementalDrawLimit) { doRedraw = TRUE; } else { wDrawDelayUpdate( mainD.d, TRUE ); } selectedTrackCount = 0; trk = NULL; while ( TrackIterate( &trk ) ) { if ((!select) || (GetLayerVisible( GetTrkLayer( trk )) && !GetLayerFrozen(GetTrkLayer( trk )) )) { if (select) { selectedTrackCount++; } if ((GetTrkSelected(trk)!=0) != select) { if (select) { SetTrkBits( trk, TB_SELECTED ); } else { ClrTrkBits( trk, TB_SELECTED ); } if (!doRedraw) { SetTrkBits( trk, TB_SELREDRAW ); } DrawTrackAndEndPts( trk, wDrawColorBlack ); } } } SelectedTrackCountChange(); if (doRedraw) { MainRedraw(); // SetAllTrackSelect } else { RedrawSelectedTracksBoundary(); wDrawDelayUpdate( mainD.d, FALSE ); } } /* Invert selected state of all visible non-module, non-frozen objects. * * \param none * \return none */ EXPORT void InvertTrackSelect( void * unused ) { track_p trk; trk = NULL; while ( TrackIterate( &trk ) ) { if (GetLayerVisible( GetTrkLayer( trk )) && !GetLayerModule(GetTrkLayer( trk )) && !GetLayerFrozen(GetTrkLayer( trk )) ) { SelectOneTrack( trk, GetTrkSelected(trk)==0 ); } } RedrawSelectedTracksBoundary(); SelectedTrackCountChange(); MainRedraw(); // InvertTrackSelect } /* Select orphaned (ie single) track pieces (ignore frozen and module) * * \param none * \return none */ EXPORT void OrphanedTrackSelect( void *ptr ) { track_p trk; EPINX_T ep; int cnt ; trk = NULL; while( TrackIterate( &trk ) ) { cnt = 0; if( GetLayerVisible( GetTrkLayer( trk ) && !GetLayerModule(GetTrkLayer(trk)) && !GetLayerFrozen(GetTrkLayer(trk)))) { for( ep = 0; ep < GetTrkEndPtCnt( trk ); ep++ ) { if( GetTrkEndTrk( trk, ep ) ) { cnt++; } } if( !cnt && GetTrkEndPtCnt( trk )) { SetTrkBits( trk, TB_SELECTED ); DrawTrackAndEndPts( trk, wDrawColorBlack ); selectedTrackCount++; } } } RedrawSelectedTracksBoundary(); SelectedTrackCountChange(); MainRedraw(); // OrphanTrackSelect } static void SelectOneTrack( track_p trk, wBool_t selected ) { BOOL_T bRedraw = (GetTrkSelected(trk) != 0) != selected; if ( !bRedraw ) { ClrTrkBits( trk, TB_SELREDRAW ); return; } SetTrkBits( trk, TB_SELREDRAW ); if (selected) { SetTrkBits( trk, TB_SELECTED ); selectedTrackCount++; } else { ClrTrkBits( trk, TB_SELECTED ); selectedTrackCount--; } SelectedTrackCountChange(); } EXPORT void HighlightSelectedTracks( track_p trk_ignore, BOOL_T keep, BOOL_T invert ) { track_p trk = NULL; if ( selectedTrackCount == 0 ) { return; } while ( TrackIterate( &trk ) ) { if (trk == trk_ignore) { continue; } if(GetTrkSelected(trk)) { if (!GetLayerVisible( GetTrkLayer( trk ))) { continue; } if (keep) { DrawTrack(trk,&tempD,selectedColor); } else if (invert) { DrawTrack(trk,&tempD,wDrawColorPreviewUnselected); } else { DrawTrack(trk,&tempD,wDrawColorPreviewSelected ); } } } } /* * Select all tracks connected walking the tree until hitting ends or already selected tracks * * Ignore Frozen Tracks */ static void SelectConnectedTracks( track_p trk, BOOL_T display_only ) { track_p trk1; int inx; EPINX_T ep; DYNARR_RESET( track_p, tlist_da ); TlistAppend( trk ); InfoCount( 0 ); if (!display_only) { wDrawDelayUpdate( mainD.d, FALSE ); } for (inx=0; inx 0 && (selectedTrackCount == 0) && !display_only ) { return; } trk = Tlist(inx); if (!GetLayerFrozen(GetTrkLayer(trk))) { if (inx!=0 && GetTrkSelected(trk)) { if (display_only) { DrawTrack(trk,&tempD,wDrawColorPreviewSelected ); } continue; } else if (GetTrkSelected(trk)) { if (display_only) { DrawTrack(trk,&tempD,wDrawColorPreviewUnselected); } continue; } } for (ep=0; ep0) { BOOL_T UndoStarted = FALSE; if (!TestAllSelectedTracks(QueryTrack, (int)Q_ISTRAIN)) { // If all Cars, don't bother with UndoStart as there will be nothing to delete UndoStarted = TRUE; UndoStart( _("Delete Tracks"), "delete" ); } wDrawDelayUpdate( mainD.d, TRUE ); wDrawDelayUpdate( mapD.d, TRUE ); DoSelectedTracks( DeleteTrack ); DoRedraw(); // SelectDelete wDrawDelayUpdate( mainD.d, FALSE ); wDrawDelayUpdate( mapD.d, FALSE ); selectedTrackCount = 0; SelectedTrackCountChange(); if (UndoStarted) { UndoEnd(); } } else { ErrorMessage( MSG_NO_SELECTED_TRK ); } return 0; } /* * Called By Windows directly with Delete Key. We first try a simple Delete, and if that doesn't work saying "In Modify" we call Modify with a Text key for Delete */ EXPORT void TrySelectDelete( void ) { if(SelectDelete() == 1) { CmdModify((C_TEXT+(int)(127<<8)),zero); } } BOOL_T flipHiddenDoSelectRecount; static BOOL_T FlipHidden( track_p trk, BOOL_T unused ) { EPINX_T i; track_p trk2; DrawTrackAndEndPts( trk, wDrawColorWhite ); /*UndrawNewTrack( trk ); for (i=0; i0) { flipHiddenDoSelectRecount = FALSE; UndoStart( _("Hide Tracks (Tunnel)"), "tunnel" ); wDrawDelayUpdate( mainD.d, TRUE ); DoSelectedTracks( FlipHidden ); wDrawDelayUpdate( mainD.d, FALSE ); UndoEnd(); } else { ErrorMessage( MSG_NO_SELECTED_TRK ); } if ( flipHiddenDoSelectRecount ) { SelectRecount(); } } EXPORT void SelectBridge( void * unused ) { if (SelectedTracksAreFrozen()) { return; } if (selectedTrackCount>0) { flipHiddenDoSelectRecount = FALSE; UndoStart( _("Bridge Tracks "), "bridge" ); wDrawDelayUpdate( mainD.d, TRUE ); DoSelectedTracks( FlipBridge ); wDrawDelayUpdate( mainD.d, FALSE ); UndoEnd(); } else { ErrorMessage( MSG_NO_SELECTED_TRK ); } MainRedraw(); // SelectBridge } EXPORT void SelectRoadbed( void * unused ) { if (SelectedTracksAreFrozen()) { return; } if (selectedTrackCount>0) { flipHiddenDoSelectRecount = FALSE; UndoStart( _("Roadbed Tracks "), "roadbed" ); wDrawDelayUpdate( mainD.d, TRUE ); DoSelectedTracks( FlipRoadbed ); wDrawDelayUpdate( mainD.d, FALSE ); UndoEnd(); } else { ErrorMessage( MSG_NO_SELECTED_TRK ); } MainRedraw(); // SelectBridge } EXPORT void SelectTies( void * unused ) { if (SelectedTracksAreFrozen()) { return; } if (selectedTrackCount>0) { flipHiddenDoSelectRecount = FALSE; UndoStart( _("Ties Tracks "), "noties" ); wDrawDelayUpdate( mainD.d, TRUE ); DoSelectedTracks( FlipTies ); wDrawDelayUpdate( mainD.d, FALSE ); UndoEnd(); } else { ErrorMessage( MSG_NO_SELECTED_TRK ); } MainRedraw(); // SelectTies } void SelectRecount( void ) { track_p trk; selectedTrackCount = 0; trk = NULL; while ( TrackIterate( &trk ) ) { if (GetTrkSelected(trk)) { selectedTrackCount++; } } SelectedTrackCountChange(); } static BOOL_T SetLayer( track_p trk, BOOL_T unused ) { UndoModify( trk ); SetTrkLayer( trk, curLayer ); return TRUE; } EXPORT void MoveSelectedTracksToCurrentLayer( void * unused ) { if (SelectedTracksAreFrozen()) { return; } if (selectedTrackCount>0) { UndoStart( _("Move To Current Layer"), "changeLayer" ); DoSelectedTracks( SetLayer ); UndoEnd(); } else { ErrorMessage( MSG_NO_SELECTED_TRK ); } } EXPORT void SelectCurrentLayer( void * unused ) { track_p trk; trk = NULL; if (GetLayerFrozen(curLayer)) { return; } while ( TrackIterate( &trk ) ) { if ((!GetTrkSelected(trk)) && GetTrkLayer(trk) == curLayer) { SelectOneTrack( trk, TRUE ); } } RedrawSelectedTracksBoundary(); } EXPORT void DeselectLayer( unsigned int layer ) { track_p trk; trk = NULL; while ( TrackIterate( &trk ) ) { if ((GetTrkSelected(trk)) && GetTrkLayer(trk) == layer) { SelectOneTrack( trk, FALSE ); } } RedrawSelectedTracksBoundary(); } static BOOL_T ClearElevation( track_p trk, BOOL_T unused ) { EPINX_T ep; for ( ep=0; ep0) { UndoStart( _("Clear Elevations"), "clear elevations" ); DoSelectedTracks( ClearElevation ); UpdateAllElevations(); UndoEnd(); } else { ErrorMessage( MSG_NO_SELECTED_TRK ); } } static DIST_T elevDelta; static BOOL_T AddElevation( track_p trk, BOOL_T unused ) { track_p trk1; EPINX_T ep, ep1; int mode; DIST_T elev; for ( ep=0; ep= 0) { if (GetTrkSelected(trk1) && GetTrkIndex(trk1)0) { elevDelta = delta; UndoStart( _("Add Elevations"), "add elevations" ); DoSelectedTracks( AddElevation ); UndoEnd(); } else { ErrorMessage( MSG_NO_SELECTED_TRK ); } UpdateAllElevations(); } EXPORT void DoRefreshCompound( void * unused ) { if (SelectedTracksAreFrozen()) { return; } if (selectedTrackCount>0) { UndoStart( _("Refresh Compound"), "refresh compound" ); DoSelectedTracks( RefreshCompound ); RefreshCompound( NULL, FALSE ); UndoEnd(); MainRedraw(); // DoRefreshCompound } else { ErrorMessage( MSG_NO_SELECTED_TRK ); } } static drawCmd_t tempSegsD = { NULL, &tempSegDrawFuncs, 0, 1, 0.0, {0.0, 0.0}, {0.0, 0.0}, Pix2CoOrd, CoOrd2Pix }; EXPORT void WriteSelectedTracksToTempSegs( void ) { track_p trk; DYNARR_RESET( trkSeg_t, tempSegs_da ); tempSegsD.dpi = mainD.dpi; for ( trk=NULL; TrackIterate(&trk); ) { if ( GetTrkSelected( trk ) ) { if ( IsTrack( trk ) ) { continue; } ClrTrkBits( trk, TB_SELECTED ); DrawTrack( trk, &tempSegsD, wDrawColorBlack ); SetTrkBits( trk, TB_SELECTED ); } } } static void DrawSelectedTracksD( drawCmd_p d, wDrawColor color ) { wIndex_t inx; track_p trk; coOrd lo, hi; /*wDrawDelayUpdate( d->d, TRUE );*/ for (inx=0; inxorig, d->size, lo, hi ) ) { continue; } } if (color != wDrawColorWhite) { ClrTrkBits(trk, TB_UNDRAWN); } if (color == wDrawColorWhite) { SetTrkBits( trk, TB_UNDRAWN ); } } MainRedraw(); //Omitting all the tracks with TB_UNDRAWN set /*wDrawDelayUpdate( d->d, FALSE );*/ } static BOOL_T AddSelectedTrack( track_p trk, BOOL_T unused ) { DYNARR_APPEND( track_p, tlist_da, 10 ); DYNARR_LAST( track_p, tlist_da ) = trk; return TRUE; } static BOOL_T RemoveSelectedTrack(track_p trk) { for(int i=0; i getSelectedBoundsHi.x ) { getSelectedBoundsHi.x = hi.x; } if ( hi.y > getSelectedBoundsHi.y ) { getSelectedBoundsHi.y = hi.y; } } getSelectedBoundsCount++; return TRUE; } EXPORT void GetSelectedBounds( coOrd * low, coOrd * high ) { getSelectedBoundsCount = 0; DoSelectedTracks( GetBoundsDoIt ); *low = getSelectedBoundsLo; *high = getSelectedBoundsHi; } static coOrd moveOrig; static ANGLE_T moveAngle; static coOrd moveD_hi, moveD_lo; static drawCmd_t moveD = { NULL, &tempSegDrawFuncs, DC_SIMPLE, 1, 0.0, {0.0, 0.0}, {0.0, 0.0}, Pix2CoOrd, CoOrd2Pix }; /* Draw selected (on-screen) tracks to tempSegs, and use drawSegs to draw them (moved/rotated) to mainD Incremently add new tracks as they scroll on-screen. */ static int movedCnt; static void AccumulateTracks( void ) { wIndex_t inx; track_p trk; coOrd lo, hi; /*wDrawDelayUpdate( moveD.d, TRUE );*/ movedCnt =0; for ( inx = 0; inx= moveD_lo.x && lo.y <= moveD_hi.y && hi.y >= moveD_lo.y ) { if (!QueryTrack(trk,Q_IS_CORNU)) { DrawTrack( trk, &moveD, wDrawColorBlack ); } } movedCnt++; } } InfoCount( movedCnt ); /*wDrawDelayUpdate( moveD.d, FALSE );*/ } static dynArr_t auto_select_da; static void AddEndCornus() { for (int i=0; i=0; j--) { tc = GetTrkEndTrk(trk,j); if (tc && !GetTrkSelected(tc) && QueryTrack(tc,Q_IS_CORNU) && !QueryTrack(trk,Q_IS_CORNU)) { //On end and cornu SelectOneTrack( tc, TRUE ); DYNARR_APPEND(track_p,tlist_da,1); //Add to selected list DYNARR_LAST(track_p,tlist_da) = tc; DYNARR_APPEND(track_p,auto_select_da,1); DYNARR_LAST(track_p,auto_select_da) = tc; } } } } static void RemoveEndCornus() { track_p tc; for (int i=0; i moveD_hi.x) { moveD_hi.x = corner[inx].x; } if (corner[inx].y > moveD_hi.y) { moveD_hi.y = corner[inx].y; } } } AccumulateTracks(); } static void DrawMovedTracks( void ) { int inx; track_p trk; dynArr_t cornu_segs; DYNARR_INIT( trkSeg_t, cornu_segs ); DrawSegsDA( &tempD, NULL, moveOrig, moveAngle, &tempSegs_da, 0.0, selectedColor, 0 ); for ( inx=0; inx=0) { DIST_T d = FindDistance(pos1,GetTrkEndPos(tt,epp)); if (IsClose(d)) { *ep1 = epp; *ep2 = i; *t1 = tt; *t2 = ts; return TRUE; } } else { epp = PickEndPoint(pos2,tt); //Any close end point (even joined) if (epp>=0) { ct = GetTrkEndTrk(tt,epp); if (ct && GetTrkSelected( ct)) { //Point is junction to selected track - so will be broken DIST_T d = FindDistance(pos1,GetTrkEndPos(tt,epp)); if (IsClose(d)) { *ep1 = epp; *ep2 = i; *t1 = tt; *t2 = ts; return TRUE; } } } } } } } } return FALSE; } void DrawHighlightLayer(int layer) { track_p ts = NULL; BOOL_T initial = TRUE; coOrd layer_hi = zero,layer_lo = zero; while ( TrackIterate( &ts ) ) { if ( !GetLayerVisible( GetTrkLayer( ts))) { continue; } if (!GetTrkSelected(ts)) { continue; } if (GetTrkLayer(ts) != layer) { continue; } coOrd hi,lo; GetBoundingBox(ts, &hi, &lo); if (initial) { layer_hi = hi; layer_lo = lo; initial = FALSE; } else { if (layer_hi.x < hi.x ) { layer_hi.x = hi.x; } if (layer_hi.y < hi.y ) { layer_hi.y = hi.y; } if (layer_lo.x > lo.x ) { layer_lo.x = lo.x; } if (layer_lo.y > lo.y ) { layer_lo.y = lo.y; } } } wDrawPix_t margin = (10.5*mainD.scale/mainD.dpi); layer_hi.x +=margin; layer_hi.y +=margin; layer_lo.x -=margin; layer_lo.y -=margin; int type[4]; type[0] = type[1] = type[2] = type[3] = 0; coOrd rect[4]; // r3 r2 // r0 r1 rect[0].x = rect[3].x = layer_lo.x; rect[1].x = rect[2].x = layer_hi.x; rect[0].y = rect[1].y = layer_lo.y; rect[2].y = rect[3].y = layer_hi.y; DrawPoly(&tempD,4,rect,type,wDrawColorPowderedBlue,wDrawLineDash,DRAW_CLOSED); } void SetUpMenu2(coOrd pos, track_p trk) { wMenuPushEnable( menuPushModify,FALSE); wMenuPushEnable(descriptionMI,FALSE); wMenuPushEnable( rotateAlignMI, FALSE ); wMenuPushEnable( hideMI, FALSE ); wMenuPushEnable( bridgeMI, FALSE ); wMenuPushEnable( tiesMI, FALSE ); if ((trk) && QueryTrack(trk, Q_CAN_ADD_ENDPOINTS)) { //Turntable snap to center if within 1/4 radius trackParams_t trackParams; if (GetTrackParams(PARAMS_CORNU, trk, pos, &trackParams)) { DIST_T dist = FindDistance(pos, trackParams.ttcenter); if (dist < trackParams.ttradius/4) { cmdMenuPos = trackParams.ttcenter; } } } if (trk && !QueryTrack( trk, Q_IS_DRAW )) { wMenuPushEnable( hideMI, TRUE ); wMenuPushEnable( bridgeMI, TRUE ); wMenuPushEnable( tiesMI, TRUE ); } if (trk) { wMenuPushEnable( menuPushModify, (QueryTrack( trk, Q_CAN_MODIFY_CONTROL_POINTS ) || QueryTrack( trk, Q_IS_CORNU ) || (QueryTrack( trk, Q_IS_DRAW ) && !QueryTrack( trk, Q_IS_TEXT )) || QueryTrack( trk, Q_IS_ACTIVATEABLE))); } if ((trk)) { wMenuPushEnable(descriptionMI, QueryTrack( trk, Q_HAS_DESC )); moveDescTrk = trk; moveDescPos = pos; } if (selectedTrackCount>0) { wMenuPushEnable( rotateAlignMI, TRUE ); } } static STATUS_T CmdMove( wAction_t action, coOrd pos ) { static coOrd base; static coOrd orig; static int state; static EPINX_T ep1; static EPINX_T ep2; static track_p t1; static track_p t2; static BOOL_T doingMove; switch( action & 0xFF) { case C_START: DYNARR_RESET(trkSeg_t,anchors_da); if (selectedTrackCount == 0) { ErrorMessage( MSG_NO_SELECTED_TRK ); return C_TERMINATE; } if (SelectedTracksAreFrozen()) { return C_TERMINATE; } InfoMessage( _("Drag to move selected tracks - Shift+Ctrl+Arrow micro-steps the move") ); state = 0; ep1 = -1; ep2 = -1; doingMove = FALSE; break; case wActionMove: DYNARR_RESET(trkSeg_t,anchors_da); CreateMoveAnchor(pos); break; case C_DOWN: DYNARR_RESET(trkSeg_t,anchors_da); if (doingMove) { doingMove = FALSE; UndoEnd(); } if (SelectedTracksAreFrozen()) { return C_TERMINATE; } UndoStart( _("Move Tracks"), "move" ); base = zero; orig = pos; DYNARR_RESET(track_p,auto_select_da); GetMovedTracks(TRUE); SetMoveD( TRUE, base, 0.0 ); drawCount = 0; state = 1; return C_CONTINUE; case C_MOVE: DYNARR_RESET(trkSeg_t,anchors_da); ep1=-1; ep2=-1; drawEnable = enableMoveDraw; base.x = pos.x - orig.x; base.y = pos.y - orig.y; if ((MyGetKeyState() & WKEY_ALT) == 0) { SnapPos( &base ); } SetMoveD( TRUE, base, 0.0 ); if (((MyGetKeyState()&(WKEY_ALT)) == 0) == magneticSnap) { // ALT if (FindEndIntersection(base,zero,0.0,&t1,&ep1,&t2,&ep2)) { coOrd pos2 = GetTrkEndPos(t2,ep2); pos2.x +=base.x; pos2.y +=base.y; CreateEndAnchor(pos2,FALSE); CreateEndAnchor(GetTrkEndPos(t1,ep1),TRUE); } } #ifdef DRAWCOUNT InfoMessage( " [%s %s] #%ld", FormatDistance(base.x), FormatDistance(base.y), drawCount ); #else InfoMessage( " [%s %s]", FormatDistance(base.x), FormatDistance(base.y) ); #endif drawEnable = TRUE; return C_CONTINUE; case C_UP: DYNARR_RESET(trkSeg_t,anchors_da); state = 0; FreeTempStrings(); if (t1 && ep1>=0 && t2 && ep2>=0) { MoveToJoin(t2,ep2,t1,ep1); } else { MoveTracks( FALSE, TRUE, FALSE, base, zero, 0.0, TRUE ); } ep1 = -1; ep2 = -1; RemoveEndCornus(); DYNARR_RESET( track_p, tlist_da ); return C_TERMINATE; case C_CMDMENU: if (doingMove) { UndoEnd(); } doingMove = FALSE; base = pos; track_p trk = OnTrack(&pos, FALSE, FALSE); //Note pollutes pos if turntable if ((trk) && QueryTrack(trk, Q_CAN_ADD_ENDPOINTS)) { //Turntable snap to center if within 1/4 radius trackParams_t trackParams; if (GetTrackParams(PARAMS_CORNU, trk, pos, &trackParams)) { DIST_T dist = FindDistance(base, trackParams.ttcenter); if (dist < trackParams.ttradius/4) { cmdMenuPos = trackParams.ttcenter; } } } moveDescPos = pos; moveDescTrk = trk; SetUpMenu2(pos,trk); menuPos = pos; wMenuPopupShow( selectPopup2M ); return C_CONTINUE; case C_TEXT: if ((action>>8) == 'c') { panCenter = pos; LOG( log_pan, 2, ( "PanCenter:Sel-%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); PanHere(I2VP(0)); } if ((action>>8) == 'e') { DoZoomExtents(I2VP(0)); } if ((action>>8 == 's')) { DoZoomExtents(I2VP(1)); } if ((action>>8) == '0' || (action>>8 == 'o')) { PanMenuEnter(I2VP('o')); } if ((action>>8) == 127 || (action>>8) == 8) { SelectDelete(); } break; case C_REDRAW: /* DO_REDRAW */ //Draw all existing highlight boxes only DrawHighlightBoxes(FALSE, FALSE, NULL); HighlightSelectedTracks(NULL, TRUE, TRUE); DrawSegsDA( &tempD, NULL, zero, 0.0, &anchors_da, trackGauge, wDrawColorBlack, 0 ); if ( state == 0 ) { break; } DrawMovedTracks(); break; case wActionExtKey: if (state) { return C_CONTINUE; } if (SelectedTracksAreFrozen()) { return C_TERMINATE; } if ((MyGetKeyState() & (WKEY_SHIFT | WKEY_CTRL)) == (WKEY_SHIFT | WKEY_CTRL)) { //Both base = zero; DIST_T w = tempD.scale/tempD.dpi; switch((wAccelKey_e) action>>8) { case wAccelKey_Up: base.y = w; break; case wAccelKey_Down: base.y = -w; break; case wAccelKey_Left: base.x = -w; break; case wAccelKey_Right: base.x = w; break; default: return C_CONTINUE; break; } drawEnable = enableMoveDraw; GetMovedTracks(TRUE); if (!doingMove) { UndoStart( _("Move Tracks"), "move" ); } doingMove = TRUE; SetMoveD( TRUE, base, 0.0 ); MoveTracks( FALSE, TRUE, FALSE, base, zero, 0.0, FALSE ); ++microCount; if (microCount>5) { microCount = 0; MainRedraw(); // Micro step move } RemoveEndCornus(); return C_CONTINUE; } break; case C_FINISH: if (doingMove) { doingMove = FALSE; UndoEnd(); } RemoveEndCornus(); DYNARR_RESET( track_p, tlist_da ); break; case C_CONFIRM: case C_CANCEL: if (doingMove) { doingMove = FALSE; UndoUndo(NULL); } RemoveEndCornus(); DYNARR_RESET( track_p, tlist_da ); break; default: break; } return C_CONTINUE; } static int rotateAlignState = 0; static void RotateAlign( void * alignVP ) { BOOL_T align = (BOOL_T)VP2L(alignVP); rotateAlignState = 0; if (align) { rotateAlignState = 1; doingAlign = TRUE; mode = MOVE; InfoMessage( _("Align: Click on a selected object to be aligned") ); } } static STATUS_T CmdRotate( wAction_t action, coOrd pos ) { static coOrd base; static coOrd orig_base; static coOrd orig; static ANGLE_T angle; static BOOL_T drawnAngle; static ANGLE_T baseAngle; static BOOL_T clockwise; static BOOL_T direction_set; static track_p trk; ANGLE_T angle1; coOrd pos1; static int state; static EPINX_T ep1; static EPINX_T ep2; static track_p t1; static track_p t2; switch( action ) { case C_START: DYNARR_RESET(trkSeg_t,anchors_da); state = 0; if (selectedTrackCount == 0) { ErrorMessage( MSG_NO_SELECTED_TRK ); return C_TERMINATE; } if (SelectedTracksAreFrozen()) { return C_TERMINATE; } InfoMessage( _("Drag to rotate selected tracks, Shift+RightClick for QuickRotate Menu") ); wMenuPushEnable( rotateAlignMI, TRUE ); rotateAlignState = 0; ep1 = -1; ep2 = -1; break; case wActionMove: DYNARR_RESET(trkSeg_t,anchors_da); CreateRotateAnchor(pos); break; case C_DOWN: DYNARR_RESET(trkSeg_t,anchors_da); state = 1; if (SelectedTracksAreFrozen()) { return C_TERMINATE; } UndoStart( _("Rotate Tracks"), "rotate" ); DYNARR_RESET(track_p,auto_select_da); if ( rotateAlignState == 0 ) { drawnAngle = FALSE; angle = 0.0; base = orig = pos; trk = OnTrack(&pos, FALSE, FALSE); //Note pollutes pos if turntable if ((trk) && QueryTrack(trk, Q_CAN_ADD_ENDPOINTS)) { //Turntable snap to center if within 1/4 radius trackParams_t trackParams; if (GetTrackParams(PARAMS_CORNU, trk, pos, &trackParams)) { DIST_T dist = FindDistance(base, trackParams.ttcenter); if (dist < trackParams.ttradius/4) { base = orig = trackParams.ttcenter; InfoMessage( _("Center of Rotation snapped to Turntable center") ); } } } CreateRotateAnchor(orig); GetMovedTracks(TRUE); SetMoveD( FALSE, base, angle ); /*DrawLine( &mainD, base, orig, 0, wDrawColorBlack ); DrawMovedTracks(FALSE, orig, angle);*/ } else { pos1 = pos; drawnAngle = FALSE; onTrackInSplit = TRUE; trk = OnTrack( &pos, TRUE, FALSE ); onTrackInSplit = FALSE; if ( trk == NULL ) { return C_CONTINUE; } angle1 = NormalizeAngle( GetAngleAtPoint( trk, pos, NULL, NULL ) ); if ( rotateAlignState == 1 ) { if ( !GetTrkSelected(trk) ) { NoticeMessage( MSG_1ST_TRACK_MUST_BE_SELECTED, _("Ok"), NULL ); } else { coOrd low, high; base = pos; baseAngle = angle1; GetSelectedBounds( &low, &high ); // getboundsCount = 0; // DoSelectedTracks( GetboundsDoIt ); // orig.x = (getboundsLo.x+getboundsHi.x)/2.0; // orig.y = (getboundsLo.y+getboundsHi.y)/2.0; orig.x = (low.x+high.x)/2.0; orig.y = (low.y+high.y)/2.0; /*printf( "orig = [%0.3f %0.3f], baseAngle = %0.3f\n", orig.x, orig.y, baseAngle );*/ } } else { if ( GetTrkSelected(trk) ) { ErrorMessage( MSG_2ND_TRACK_MUST_BE_UNSELECTED ); angle = 0; } else { angle = NormalizeAngle(angle1-baseAngle); //if ( angle > 90 && angle < 270 ) // angle = NormalizeAngle( angle + 180.0 ); //if ( NormalizeAngle( FindAngle( base, pos1 ) - angle1 ) < 180.0 ) // angle = NormalizeAngle( angle + 180.0 ); /*printf( "angle 1 = %0.3f\n", angle );*/ if ( angle1 > 180.0 ) { angle1 -= 180.0; } InfoMessage( _("Angle %0.3f"), angle1 ); } GetMovedTracks(TRUE); SetMoveD( FALSE, orig, angle ); } } return C_CONTINUE; case C_MOVE: DYNARR_RESET(trkSeg_t,anchors_da); ep1=-1; ep2=-1; if ( rotateAlignState == 1 ) { return C_CONTINUE; } if ( rotateAlignState == 2 ) { pos1 = pos; onTrackInSplit = TRUE; trk = OnTrack( &pos, TRUE, FALSE ); onTrackInSplit = FALSE; if ( trk == NULL ) { return C_CONTINUE; } if ( GetTrkSelected(trk) ) { ErrorMessage( MSG_2ND_TRACK_MUST_BE_UNSELECTED ); return C_CONTINUE; } angle1 = NormalizeAngle( GetAngleAtPoint( trk, pos, NULL, NULL ) ); angle = NormalizeAngle(angle1-baseAngle); if ( angle > 90 && angle < 270 ) { angle = NormalizeAngle( angle + 180.0 ); } if ( NormalizeAngle( FindAngle( pos, pos1 ) - angle1 ) < 180.0 ) { angle = NormalizeAngle( angle + 180.0 ); } if ( angle1 > 180.0 ) { angle1 -= 180.0; } InfoMessage( _("Angle %0.3f"), angle1 ); SetMoveD( FALSE, orig, angle ); /*printf( "angle 2 = %0.3f\n", angle );*/ return C_CONTINUE; } ANGLE_T diff_angle = 0.0; base = pos; drawEnable = enableMoveDraw; if ( FindDistance( orig, pos ) > (20.0/BASE_DPI)*mainD.scale ) { ANGLE_T old_angle = angle; angle = FindAngle( orig, pos ); if (!drawnAngle) { baseAngle = angle; drawnAngle = TRUE; direction_set = FALSE; } else { if (!direction_set) { if (DifferenceBetweenAngles(baseAngle,angle)>=0) { clockwise = TRUE; } else { clockwise = FALSE; } direction_set = TRUE; } else { if (clockwise) { if (DifferenceBetweenAngles(baseAngle,angle)<0 && fabs(DifferenceBetweenAngles(baseAngle, old_angle))<5) { clockwise = FALSE; } } else { if (DifferenceBetweenAngles(baseAngle,angle)>=0 && fabs(DifferenceBetweenAngles(baseAngle,old_angle))<5) { clockwise = TRUE; } } } } orig_base = base = pos; //angle = NormalizeAngle( angle-baseAngle ); diff_angle = DifferenceBetweenAngles(baseAngle,angle); if ( (MyGetKeyState() & (WKEY_CTRL|WKEY_SHIFT)) == (WKEY_CTRL|WKEY_SHIFT) ) { //Both Shift+Ctrl if (clockwise) { if (diff_angle<0) { diff_angle+=360; } } else { if (diff_angle>0) { diff_angle-=360; } } diff_angle = floor((diff_angle+7.5)/15.0)*15.0; angle = baseAngle+diff_angle; } Translate( &base, orig, angle, FindDistance(orig,pos) ); //Line one Translate( &orig_base,orig, baseAngle, FindDistance(orig, pos)<=(60.0/BASE_DPI*mainD.scale)?FindDistance(orig, pos):60.0/BASE_DPI*mainD.scale ); //Line two SetMoveD( FALSE, orig, NormalizeAngle( angle-baseAngle ) ); if (((MyGetKeyState()&(WKEY_ALT)) == WKEY_ALT) != magneticSnap) { //Just Shift if (FindEndIntersection(zero,orig,NormalizeAngle( angle-baseAngle ),&t1,&ep1, &t2,&ep2)) { coOrd pos2 = GetTrkEndPos(t2,ep2); coOrd pos1 = GetTrkEndPos(t1,ep1); Rotate(&pos2,orig,NormalizeAngle( angle-baseAngle )); CreateEndAnchor(pos2,FALSE); CreateEndAnchor(pos1,TRUE); } } #ifdef DRAWCOUNT InfoMessage( _("Angle %0.3f #%ld"), fabs(diff_angle), drawCount ); #else InfoMessage( _("Angle %0.3f %s"), fabs(diff_angle), clockwise?"Clockwise":"Counter-Clockwise" ); #endif wFlush(); drawEnable = TRUE; } else { InfoMessage( _("Origin Set. Drag away to set start angle")); } return C_CONTINUE; case C_UP: DYNARR_RESET(trkSeg_t,anchors_da); state = 0; if (t1 && ep1>=0 && t2 && ep2>=0) { MoveToJoin(t2,ep2,t1,ep1); CleanSegs(&tempSegs_da); rotateAlignState = 0; } else { if ( rotateAlignState == 1 ) { if ( trk && GetTrkSelected(trk) ) { InfoMessage( _("Align: Click on the 2nd unselected object") ); rotateAlignState = 2; } return C_CONTINUE; } CleanSegs(&tempSegs_da); if ( rotateAlignState == 2 ) { MoveTracks( FALSE, FALSE, TRUE, zero, orig, angle, TRUE ); rotateAlignState = 0; } else if (drawnAngle) { MoveTracks( FALSE, FALSE, TRUE, zero, orig, NormalizeAngle( angle-baseAngle ), TRUE ); } } UndoEnd(); RemoveEndCornus(); DYNARR_RESET( track_p, tlist_da ); return C_TERMINATE; case C_CMDMENU: base = pos; trk = OnTrack(&pos, FALSE, FALSE); //Note pollutes pos if turntable if ((trk) && QueryTrack(trk, Q_CAN_ADD_ENDPOINTS)) { //Turntable snap to center if within 1/4 radius trackParams_t trackParams; if (GetTrackParams(PARAMS_CORNU, trk, pos, &trackParams)) { DIST_T dist = FindDistance(base, trackParams.ttcenter); if (dist < trackParams.ttradius/4) { cmdMenuPos = trackParams.ttcenter; } } } moveDescPos = pos; moveDescTrk = trk; SetUpMenu2(pos,trk); menuPos = pos; wMenuPopupShow( selectPopup2M ); return C_CONTINUE; case C_TEXT: if ((action>>8) == 'd') { panCenter = pos; LOG( log_pan, 2, ( "PanCenter:Sel-%d %0.3f %0.3f\n", __LINE__, panCenter.x, panCenter.y ) ); PanHere(I2VP(0)); } if ((action>>8) == 'e') { DoZoomExtents(I2VP(0)); } if ((action>>8) == 's') { DoZoomExtents(I2VP(1)); } if ((action>>8) == '0' || (action>>8 == 'o')) { PanMenuEnter(I2VP('o')); } break; case C_REDRAW: DrawHighlightBoxes(FALSE,FALSE,NULL); HighlightSelectedTracks(NULL, TRUE, TRUE); DrawSegsDA( &tempD, NULL, zero, 0.0, &anchors_da, trackGauge, wDrawColorBlack, 0 ); /* DO_REDRAW */ if ( state == 0 ) { break; } if ( rotateAlignState != 2 ) { DIST_T width = tempD.scale*0.15; DrawLine( &tempD, base, orig, 0, wDrawColorBlue ); if (drawnAngle) { DrawLine( &tempD, orig_base, orig, (wDrawWidth)width/2, wDrawColorBlue ); ANGLE_T a = DifferenceBetweenAngles(FindAngle(orig, orig_base),FindAngle(orig, base)); DIST_T dist = FindDistance(orig,base); if (dist>(60.0/BASE_DPI)*mainD.scale) { dist = (60.0/BASE_DPI)*mainD.scale; } if (direction_set) { if (clockwise) { if (a<0) { a = a + 360; } DrawArc( &tempD, orig, dist/2, FindAngle(orig,orig_base), a, FALSE, 0, wDrawColorBlue); } else { if (a>0) { a = a - 360; } DrawArc( &tempD, orig, dist/2, FindAngle(orig,base), fabs(a), FALSE, 0, wDrawColorBlue); } DIST_T d; d = mainD.scale*0.25; ANGLE_T arrow_a = NormalizeAngle(FindAngle(orig,orig_base)+a/2); coOrd arr1,arr2,arr3; Translate(&arr2,orig,arrow_a,dist/2); if (clockwise) { arrow_a +=90; } else { arrow_a -=90; } Translate(&arr1,arr2,arrow_a+135,d/2); Translate(&arr3,arr2,arrow_a-135,d/2); DrawLine( &tempD, arr1, arr2, 0, wDrawColorBlue ); DrawLine( &tempD, arr2, arr3, 0, wDrawColorBlue ); } } } DrawMovedTracks(); break; } return C_CONTINUE; } static void QuickMove( void* pos) { coOrd move_pos = *(coOrd*)pos; DYNARR_RESET(track_p,auto_select_da); if ( SelectedTracksAreFrozen() ) { return; } wDrawDelayUpdate( mainD.d, TRUE ); GetMovedTracks(FALSE); UndoStart( _("Move Tracks"), "Move Tracks" ); MoveTracks( TRUE, TRUE, FALSE, move_pos, zero, 0.0, TRUE ); wDrawDelayUpdate( mainD.d, FALSE ); } static track_p SelectTrackByIndex(TRKINX_T ti, char * message ) { track_p trk = FindTrack(ti); if (trk) { if (!GetLayerFrozen( GetTrkLayer( trk ) ) ) { if (GetLayerModule(GetTrkLayer(trk))) { DoModuleTracks(GetTrkLayer(trk),DrawSingleTrack,TRUE); snprintf(message, STR_LONG_SIZE, "%s %d",_("In module layer:"), GetTrkLayer(trk)+1); } else { if (!GetLayerVisible(GetTrkLayer(trk))) { FlipLayer(I2VP(GetTrkLayer(trk))); } if (!GetTrkVisible(trk) && drawTunnel==0 ) { drawTunnel = 1; } //Force DRAW_TUNNEL_DASH SelectOneTrack(trk,TRUE); } } else { snprintf(message, STR_LONG_SIZE, "%s %d",_("Frozen Layer:"),GetTrkLayer(trk)+1); trk = NULL; } } else { snprintf(message, STR_LONG_SIZE, "%s",_("Not found")); } return trk; } EXPORT void SelectByIndex( void* string) { char result[STR_LONG_SIZE] = ""; char * message; SetAllTrackSelect(FALSE); char * cp = (char *)string; cp = strtok(cp,","); BOOL_T single = TRUE; track_p trk = NULL; while (cp) { long ti = strtol(cp,&cp,0); if (ti>0) { message = MyMalloc(STR_LONG_SIZE); trk = SelectTrackByIndex(ti, message); if (!trk || message[0]) { size_t len = strlen(result); snprintf(result+len,(sizeof(result) - len),"I:%ld %s", ti, message); MyFree(message); } } cp = strtok(NULL,","); if (cp) { single = FALSE; } } DoZoomExtents(I2VP(1)); if (strlen(result)) { InfoMessage(result); } else if (single && trk) { char msg[STR_SIZE]; DescribeTrack( trk, msg, sizeof msg ); InfoMessage( msg ); } else if (!single) { InfoMessage(_("Multiple Selected")); } } static void QuickRotate( void* pangle ) { ANGLE_T angle = (ANGLE_T)VP2L(pangle); DYNARR_RESET(track_p,auto_select_da); if ( SelectedTracksAreFrozen() ) { return; } wDrawDelayUpdate( mainD.d, TRUE ); GetMovedTracks(FALSE); //DrawSelectedTracksD( &mainD, wDrawColorWhite ); UndoStart( _("Rotate Tracks"), "Rotate Tracks" ); MoveTracks( TRUE, FALSE, TRUE, zero, cmdMenuPos, (double)angle/1000, TRUE); wDrawDelayUpdate( mainD.d, FALSE ); } static wMenu_p moveDescM; static wMenuToggle_p moveDescMI; static wMenuToggle_p moveDetailDescMI; static void ChangeDetailedFlag( void * mode ) { wDrawDelayUpdate( mainD.d, TRUE ); UndoStart( _("Toggle Detail"), "Modedetail( T%d )", GetTrkIndex(moveDescTrk) ); UndoModify( moveDescTrk ); UndrawNewTrack( moveDescTrk ); if ( ( GetTrkBits( moveDescTrk ) & TB_DETAILDESC ) == 0 ) { ClrTrkBits( moveDescTrk, TB_HIDEDESC ); SetTrkBits( moveDescTrk, TB_DETAILDESC ); } else { ClrTrkBits( moveDescTrk, TB_DETAILDESC ); } DrawNewTrack( moveDescTrk ); wDrawDelayUpdate( mainD.d, FALSE ); } static void ChangeDescFlag( void * mode ) { wDrawDelayUpdate( mainD.d, TRUE ); UndoStart( _("Toggle Label"), "Modedesc( T%d )", GetTrkIndex(moveDescTrk) ); UndoModify( moveDescTrk ); UndrawNewTrack( moveDescTrk ); if ( ( GetTrkBits( moveDescTrk ) & TB_HIDEDESC ) == 0 ) { SetTrkBits( moveDescTrk, TB_HIDEDESC ); } else { ClrTrkBits( moveDescTrk, TB_HIDEDESC ); } DrawNewTrack( moveDescTrk ); wDrawDelayUpdate( mainD.d, FALSE ); } /* * Mode_o -1 = everything, 0 = elevations only, 1 = descriptions only */ track_p FindTrackDescription(coOrd pos, EPINX_T * ep_o, int * mode_o, BOOL_T show_hidden, BOOL_T * hidden_o) { track_p trk = NULL; DIST_T d, dd = DIST_INF; track_p trk1 = NULL; EPINX_T ep1=-1, ep=-1; BOOL_T hidden_t, hidden; coOrd dpos = pos; // coOrd cpos; int mode = -1; while ( TrackIterate( &trk1 ) ) { if ( !GetLayerVisible(GetTrkLayer(trk1)) ) { continue; } if ( (!GetTrkVisible(trk1)) && drawTunnel==0 ) { continue; } if ( GetLayerFrozen( GetTrkLayer( trk1 ) )) { continue; } if ( (labelEnable&LABELENABLE_ENDPT_ELEV)!=0 && *mode_o <= 0) { for ( ep1=0; ep1