kicad/common/base_screen.cpp

475 lines
12 KiB
C++
Raw Normal View History

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
2012-06-09 09:38:58 +00:00
* Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2012 Wayne Stambaugh <stambaughw@gmail.com>
* Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
2007-05-06 16:03:28 +00:00
/**
* @file base_screen.cpp
* @brief BASE_SCREEN object implementation.
*/
2007-05-06 16:03:28 +00:00
#include <fctsys.h>
#include <macros.h>
#include <common.h>
#include <base_struct.h>
2018-01-29 10:37:29 +00:00
#include <base_screen.h>
#include <id.h>
#include <base_units.h>
#include <trace_helpers.h>
2007-05-06 16:03:28 +00:00
wxString BASE_SCREEN::m_PageLayoutDescrFileName; // the name of the page layout descr file.
BASE_SCREEN::BASE_SCREEN( KICAD_T aType ) :
EDA_ITEM( aType )
2007-05-06 16:03:28 +00:00
{
m_UndoRedoCountMax = DEFAULT_MAX_UNDO_ITEMS;
m_Initialized = false;
m_ScreenNumber = 1;
m_NumberOfScreens = 1; // Hierarchy: Root: ScreenNumber = 1
m_Zoom = 32.0;
m_Grid.m_Size = wxRealPoint( 50, 50 ); // Default grid size
m_Grid.m_CmdId = ID_POPUP_GRID_LEVEL_50;
m_Center = true;
m_IsPrinting = false;
m_ScrollPixelsPerUnitX = 1;
m_ScrollPixelsPerUnitY = 1;
2007-05-06 16:03:28 +00:00
m_FlagModified = false; // Set when any change is made on board.
m_FlagSave = false; // Used in auto save set when an auto save is required.
2007-05-06 16:03:28 +00:00
SetCurItem( NULL );
2007-05-06 16:03:28 +00:00
}
BASE_SCREEN::~BASE_SCREEN()
{
}
void BASE_SCREEN::InitDataPoints( const wxSize& aPageSizeIU )
2007-05-06 16:03:28 +00:00
{
if( m_Center )
{
m_crossHairPosition.x = 0;
m_crossHairPosition.y = 0;
m_DrawOrg.x = -aPageSizeIU.x / 2;
m_DrawOrg.y = -aPageSizeIU.y / 2;
}
else
{
m_crossHairPosition.x = aPageSizeIU.x / 2;
m_crossHairPosition.y = aPageSizeIU.y / 2;
m_DrawOrg.x = 0;
m_DrawOrg.y = 0;
}
m_O_Curseur.x = m_O_Curseur.y = 0;
}
double BASE_SCREEN::GetScalingFactor() const
{
double scale = 1.0 / GetZoom();
return scale;
}
void BASE_SCREEN::SetScalingFactor( double aScale )
{
// Limit zoom to max and min allowed values:
double zoom = Clamp( GetMinAllowedZoom(), aScale, GetMaxAllowedZoom() );
SetZoom( zoom );
}
bool BASE_SCREEN::SetFirstZoom()
2007-05-06 16:03:28 +00:00
{
return SetZoom( GetMinAllowedZoom() );
2007-05-06 16:03:28 +00:00
}
bool BASE_SCREEN::SetLastZoom()
2007-05-06 16:03:28 +00:00
{
return SetZoom( GetMaxAllowedZoom() );
2007-05-06 16:03:28 +00:00
}
bool BASE_SCREEN::SetZoom( double iu_per_du )
2007-05-06 16:03:28 +00:00
{
if( iu_per_du == m_Zoom )
return false;
wxLogTrace( traceScreen, "Zoom:%.16g 1/Zoom:%.16g", iu_per_du, 1/iu_per_du );
if( iu_per_du < GetMinAllowedZoom() )
return false;
if( iu_per_du > GetMaxAllowedZoom() )
return false;
m_Zoom = iu_per_du;
return true;
2007-05-06 16:03:28 +00:00
}
bool BASE_SCREEN::SetNextZoom()
2007-05-06 16:03:28 +00:00
{
2018-09-03 13:58:47 +00:00
// Step must be AT LEAST 1.2
double target = m_Zoom * 1.2;
for( unsigned i=0; i < m_ZoomList.size(); ++i )
{
2018-09-03 13:58:47 +00:00
if( target < m_ZoomList[i] )
{
SetZoom( m_ZoomList[i] );
return true;
}
}
return false;
2007-05-06 16:03:28 +00:00
}
bool BASE_SCREEN::SetPreviousZoom()
2007-05-06 16:03:28 +00:00
{
2018-09-03 13:58:47 +00:00
// Step must be AT LEAST 1.2
double target = m_Zoom / 1.2;
for( unsigned i = m_ZoomList.size(); i != 0; --i )
{
2018-09-03 13:58:47 +00:00
if( target > m_ZoomList[i - 1] )
{
SetZoom( m_ZoomList[i - 1] );
return true;
}
}
return false;
2007-05-06 16:03:28 +00:00
}
int BASE_SCREEN::BuildGridsChoiceList( wxArrayString& aGridsList, bool aMmFirst) const
{
wxString msg;
wxRealPoint curr_grid_size = GetGridSize();
int idx = -1;
int idx_usergrid = -1;
for( size_t i = 0; i < GetGridCount(); i++ )
{
const GRID_TYPE& grid = m_grids[i];
double gridValueMils = To_User_Unit( INCHES, grid.m_Size.x ) * 1000;
double gridValue_mm = To_User_Unit( MILLIMETRES, grid.m_Size.x );
if( grid.m_CmdId == ID_POPUP_GRID_USER )
{
if( aMmFirst )
msg.Printf( _( "User grid: %.4f mm (%.2f mils)" ),
gridValue_mm, gridValueMils );
else
msg.Printf( _( "User grid: %.2f mils (%.4f mm)" ),
gridValueMils, gridValue_mm );
idx_usergrid = i;
}
else
{
if( aMmFirst )
msg.Printf( _( "Grid: %.4f mm (%.2f mils)" ),
gridValue_mm, gridValueMils );
else
msg.Printf( _( "Grid: %.2f mils (%.4f mm)" ),
gridValueMils, gridValue_mm );
}
aGridsList.Add( msg );
if( curr_grid_size == grid.m_Size )
idx = i;
}
if( idx < 0 )
idx = idx_usergrid;
return idx;
}
2007-05-06 16:03:28 +00:00
void BASE_SCREEN::SetGridList( GRIDS& gridlist )
2007-05-06 16:03:28 +00:00
{
if( !m_grids.empty() )
m_grids.clear();
m_grids = gridlist;
}
int BASE_SCREEN::SetGrid( const wxRealPoint& size )
2007-05-06 16:03:28 +00:00
{
wxASSERT( !m_grids.empty() );
GRID_TYPE nearest_grid = m_grids[0];
int gridIdx = 0;
for( unsigned i = 0; i < m_grids.size(); i++ )
{
if( m_grids[i].m_Size == size )
{
m_Grid = m_grids[i];
return m_grids[i].m_CmdId - ID_POPUP_GRID_LEVEL_1000;
}
// keep track of the nearest larger grid size, if the exact size is not found
if ( size.x < m_grids[i].m_Size.x )
{
gridIdx = m_grids[i].m_CmdId - ID_POPUP_GRID_LEVEL_1000;
nearest_grid = m_grids[i];
}
}
2007-05-06 16:03:28 +00:00
2009-02-06 11:45:35 +00:00
m_Grid = nearest_grid;
return gridIdx;
}
int BASE_SCREEN::SetGrid( int aCommandId )
2007-05-06 16:03:28 +00:00
{
wxASSERT( !m_grids.empty() );
for( unsigned i = 0; i < m_grids.size(); i++ )
{
if( m_grids[i].m_CmdId == aCommandId )
{
m_Grid = m_grids[i];
return m_grids[i].m_CmdId - ID_POPUP_GRID_LEVEL_1000;
}
}
2007-05-06 16:03:28 +00:00
m_Grid = m_grids[0];
return m_grids[0].m_CmdId - ID_POPUP_GRID_LEVEL_1000;
}
void BASE_SCREEN::AddGrid( const GRID_TYPE& grid )
2007-05-06 16:03:28 +00:00
{
for( unsigned i = 0; i < m_grids.size(); i++ )
{
if( m_grids[i].m_Size == grid.m_Size && grid.m_CmdId != ID_POPUP_GRID_USER )
{
wxLogTrace( traceScreen, "Discarding duplicate grid size( %g, %g ).",
grid.m_Size.x, grid.m_Size.y );
return;
}
if( m_grids[i].m_CmdId == grid.m_CmdId )
{
wxLogTrace( traceScreen, wxT( "Changing grid ID %d from size( %g, %g ) to " ) \
wxT( "size( %g, %g )." ),
grid.m_CmdId, m_grids[i].m_Size.x,
m_grids[i].m_Size.y, grid.m_Size.x, grid.m_Size.y );
m_grids[i].m_Size = grid.m_Size;
return;
}
}
m_grids.push_back( grid );
2007-05-06 16:03:28 +00:00
}
void BASE_SCREEN::AddGrid( const wxRealPoint& size, int id )
{
GRID_TYPE grid;
2007-05-06 16:03:28 +00:00
grid.m_Size = size;
grid.m_CmdId = id;
AddGrid( grid );
}
void BASE_SCREEN::AddGrid( const wxRealPoint& size, EDA_UNITS_T aUnit, int id )
2007-05-06 16:03:28 +00:00
{
wxRealPoint new_size;
GRID_TYPE new_grid;
new_size.x = From_User_Unit( aUnit, size.x );
new_size.y = From_User_Unit( aUnit, size.y );
new_grid.m_CmdId = id;
new_grid.m_Size = new_size;
AddGrid( new_grid );
}
2007-05-06 16:03:28 +00:00
GRID_TYPE& BASE_SCREEN::GetGrid( size_t aIndex )
{
wxCHECK_MSG( !m_grids.empty() && aIndex < m_grids.size(), m_Grid,
wxT( "Cannot get grid object outside the bounds of the grid list." ) );
return m_grids[ aIndex ];
}
bool BASE_SCREEN::GridExists( int aCommandId )
{
// tests for grid command ID (not an index in grid list, but a wxID) exists in grid list.
for( unsigned i = 0; i < m_grids.size(); i++ )
{
if( m_grids[i].m_CmdId == aCommandId )
return true;
}
return false;
}
wxPoint BASE_SCREEN::getNearestGridPosition( const wxPoint& aPosition,
const wxPoint& aGridOrigin, wxRealPoint* aGridSize ) const
{
wxPoint pt;
wxRealPoint gridSize;
if( aGridSize )
gridSize = *aGridSize;
else
gridSize = GetGridSize();
double offset = fmod( aGridOrigin.x, gridSize.x );
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
2012-04-19 06:55:45 +00:00
int x = KiROUND( (aPosition.x - offset) / gridSize.x );
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
2012-04-19 06:55:45 +00:00
pt.x = KiROUND( x * gridSize.x + offset );
offset = fmod( aGridOrigin.y, gridSize.y );
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
2012-04-19 06:55:45 +00:00
int y = KiROUND( (aPosition.y - offset) / gridSize.y );
pt.y = KiROUND ( y * gridSize.y + offset );
return pt;
}
wxPoint BASE_SCREEN::getCursorPosition( bool aOnGrid, const wxPoint& aGridOrigin,
wxRealPoint* aGridSize ) const
{
if( aOnGrid )
return getNearestGridPosition( m_crossHairPosition, aGridOrigin, aGridSize );
return m_crossHairPosition;
}
wxPoint BASE_SCREEN::getCrossHairScreenPosition() const
{
wxPoint pos = m_crossHairPosition - m_DrawOrg;
double scalar = GetScalingFactor();
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
2012-04-19 06:55:45 +00:00
pos.x = KiROUND( (double) pos.x * scalar );
pos.y = KiROUND( (double) pos.y * scalar );
return pos;
}
void BASE_SCREEN::setCrossHairPosition( const wxPoint& aPosition, const wxPoint& aGridOrigin,
bool aSnapToGrid )
{
if( aSnapToGrid )
2013-08-03 20:20:55 +00:00
m_crossHairPosition = getNearestGridPosition( aPosition, aGridOrigin, NULL );
else
m_crossHairPosition = aPosition;
}
void BASE_SCREEN::ClearUndoRedoList()
2007-05-06 16:03:28 +00:00
{
ClearUndoORRedoList( m_UndoList );
ClearUndoORRedoList( m_RedoList );
2007-05-06 16:03:28 +00:00
}
void BASE_SCREEN::PushCommandToUndoList( PICKED_ITEMS_LIST* aNewitem )
2007-05-06 16:03:28 +00:00
{
m_UndoList.PushCommand( aNewitem );
// Delete the extra items, if count max reached
if( m_UndoRedoCountMax > 0 )
{
int extraitems = GetUndoCommandCount() - m_UndoRedoCountMax;
if( extraitems > 0 )
ClearUndoORRedoList( m_UndoList, extraitems );
}
2007-05-06 16:03:28 +00:00
}
2009-07-23 15:37:00 +00:00
void BASE_SCREEN::PushCommandToRedoList( PICKED_ITEMS_LIST* aNewitem )
2007-05-06 16:03:28 +00:00
{
m_RedoList.PushCommand( aNewitem );
// Delete the extra items, if count max reached
if( m_UndoRedoCountMax > 0 )
{
int extraitems = GetRedoCommandCount() - m_UndoRedoCountMax;
if( extraitems > 0 )
ClearUndoORRedoList( m_RedoList, extraitems );
}
2007-05-06 16:03:28 +00:00
}
2009-07-23 15:37:00 +00:00
PICKED_ITEMS_LIST* BASE_SCREEN::PopCommandFromUndoList( )
2007-05-06 16:03:28 +00:00
{
return m_UndoList.PopCommand();
2007-05-06 16:03:28 +00:00
}
2009-07-23 15:37:00 +00:00
PICKED_ITEMS_LIST* BASE_SCREEN::PopCommandFromRedoList( )
2007-05-06 16:03:28 +00:00
{
return m_RedoList.PopCommand();
}
2007-09-20 21:06:49 +00:00
#if defined(DEBUG)
void BASE_SCREEN::Show( int nestLevel, std::ostream& os ) const
{
2007-09-20 21:06:49 +00:00
// for now, make it look like XML, expand on this later.
NestedSpace( nestLevel, os ) << '<' << GetClass().Lower().mb_str() << ">\n";
/* this class will eventually go away, but here's a place holder until then.
for( EDA_ITEM* item = m_drawList; item; item = item->Next() )
2007-09-20 21:06:49 +00:00
{
item->Show( nestLevel+1, os );
}
*/
2007-09-20 21:06:49 +00:00
NestedSpace( nestLevel, os ) << "</" << GetClass().Lower().mb_str() << ">\n";
}
2007-09-20 21:06:49 +00:00
#endif