/***************************************************************/ /* base_screen.cpp - fonctions des classes du type BASE_SCREEN */ /***************************************************************/ #ifdef __GNUG__ #pragma implementation #endif #include "fctsys.h" #include "common.h" #include "base_struct.h" #include "sch_item_struct.h" #include "class_base_screen.h" /* Implement wxSize array for grid list implementation. */ #include WX_DEFINE_OBJARRAY( GridArray ); /* defines locaux */ #define CURSOR_SIZE 12 /* taille de la croix du curseur PCB */ /*******************************************************/ /* Class BASE_SCREEN: classe de gestion d'un affichage */ /*******************************************************/ BASE_SCREEN::BASE_SCREEN( KICAD_T aType ) : EDA_BaseStruct( aType ) { EEDrawList = NULL; /* Schematic items list */ m_UndoList = NULL; m_RedoList = NULL; m_UndoRedoCountMax = 1; m_FirstRedraw = TRUE; m_ScreenNumber = 1; m_NumberOfScreen = 1; /* Hierarchy: Root: ScreenNumber = 1 */ m_ZoomScalar = 10; m_Zoom = 32 * m_ZoomScalar; m_Grid = wxSize( 50, 50 ); /* Default grid size */ m_UserGridIsON = FALSE; m_Center = true; m_CurrentSheetDesc = &g_Sheet_A4; InitDatas(); } /******************************/ BASE_SCREEN::~BASE_SCREEN() /******************************/ { ClearUndoRedoList(); } /*******************************/ void BASE_SCREEN::InitDatas() /*******************************/ { if( m_Center ) { m_Curseur.x = m_Curseur.y = 0; m_DrawOrg.x = -ReturnPageSize().x / 2; m_DrawOrg.y = -ReturnPageSize().y / 2; } else { m_DrawOrg.x = m_DrawOrg.y = 0; m_Curseur.x = ReturnPageSize().x / 2; m_Curseur.y = ReturnPageSize().y / 2; } // DrawOrg est rendu multiple du zoom min : m_DrawOrg.x -= m_DrawOrg.x % 256; m_DrawOrg.y -= m_DrawOrg.y % 256; m_O_Curseur = m_Curseur; SetCurItem( NULL ); /* indicateurs divers */ m_FlagRefreshReq = 0; /* Redraw screen requste flag */ m_FlagModified = 0; // Set when any change is made on borad m_FlagSave = 1; // Used in auto save: set when an auto save is made } /* * Get screen units scalar. * * Default implimentation returns scalar used for schematic screen. The * internal units used by the schematic screen is 1 mil (0.001"). Override * this in derived classes that require internal units other than 1 mil. */ int BASE_SCREEN::GetInternalUnits( void ) { return EESCHEMA_INTERNAL_UNIT; } wxSize BASE_SCREEN::ReturnPageSize( void ) { int internal_units = GetInternalUnits(); return wxSize( ( m_CurrentSheetDesc->m_Size.x * internal_units ) / 1000, ( m_CurrentSheetDesc->m_Size.y * internal_units ) / 1000 ); } /******************************************************************/ wxPoint BASE_SCREEN::CursorRealPosition( const wxPoint& ScreenPos ) /******************************************************************/ /** Function CursorRealPosition * @return the position in user units of location ScreenPos * @param ScreenPos = the screen (in pixel) position co convert */ { wxPoint curpos = ScreenPos; Unscale( curpos ); curpos += m_DrawOrg; return curpos; } /** Function SetScalingFactor * calculates the .m_Zoom member to have a given scaling facort * @param the the current scale used to draw items on screen * draw coordinates are user coordinates * GetScalingFactor( ) */ void BASE_SCREEN::SetScalingFactor(double aScale ) { int zoom = static_cast( ceil(aScale * m_ZoomScalar) ); // Limit zoom to max and min allowed values: if (zoom < m_ZoomList[0]) zoom = m_ZoomList[0]; int idxmax = m_ZoomList.GetCount() - 1; if (zoom > m_ZoomList[idxmax]) zoom = m_ZoomList[idxmax]; SetZoom( zoom ); } /** * Calculate coordinate value for zooming. * * Call this method when drawing on the device context. It scales the * coordinate using the current zoom settings. Zooming in Kicad occurs * by actually scaling the entire drawing using the zoom setting. * * FIXME: We should probably use wxCoord instead of int here but that would * require using wxCoord in all of the other code that makes device * context calls as well. */ int BASE_SCREEN::Scale( int coord ) { #ifdef WX_ZOOM return coord; #else if( !m_Zoom ) return 0; if( !m_ZoomScalar || !m_Zoom ) return 0; return wxRound( (double) ( coord * m_ZoomScalar ) / (double) m_Zoom ); #endif } void BASE_SCREEN::Scale( wxPoint& pt ) { pt.x = Scale( pt.x ); pt.y = Scale( pt.y ); } void BASE_SCREEN::Scale( wxSize& sz ) { sz.SetHeight( Scale( sz.GetHeight() ) ); sz.SetWidth( Scale( sz.GetWidth() ) ); } /** * Calculate the physical (unzoomed) location of a coordinate. * * Call this method when you want to find the unzoomed (physical) location * of a coordinate on the drawing. */ int BASE_SCREEN::Unscale( int coord ) { #ifdef WX_ZOOM return coord; #else if( !m_Zoom || !m_ZoomScalar ) return 0; return wxRound( (double) ( coord * m_Zoom ) / (double) m_ZoomScalar ); #endif } void BASE_SCREEN::Unscale( wxPoint& pt ) { pt.x = Unscale( pt.x ); pt.y = Unscale( pt.y ); } void BASE_SCREEN::Unscale( wxSize& sz ) { sz.SetHeight( Unscale( sz.GetHeight() ) ); sz.SetWidth( Unscale( sz.GetWidth() ) ); } void BASE_SCREEN::SetZoomList( const wxArrayInt& zoomlist ) { if( !m_ZoomList.IsEmpty() ) m_ZoomList.Empty(); m_ZoomList = zoomlist; } void BASE_SCREEN::SetFirstZoom() { if( m_ZoomList.IsEmpty() ) m_Zoom = m_ZoomScalar; else m_Zoom = m_ZoomList[0]; } int BASE_SCREEN::GetZoom() const { return m_Zoom; } void BASE_SCREEN::SetZoom( int coeff ) { m_Zoom = coeff; if( m_Zoom < 1 ) m_Zoom = 1; } void BASE_SCREEN::SetNextZoom() { size_t i; if( m_ZoomList.IsEmpty() || m_Zoom >= m_ZoomList.Last() ) return; for( i = 0; i < m_ZoomList.GetCount(); i++ ) { if( m_Zoom < m_ZoomList[i] ) { m_Zoom = m_ZoomList[i]; break; } } } void BASE_SCREEN::SetPreviousZoom() { size_t i; if( m_ZoomList.IsEmpty() || m_Zoom <= m_ZoomList[0] ) return; for( i = m_ZoomList.GetCount(); i != 0; i-- ) { if( m_Zoom > m_ZoomList[i - 1] ) { m_Zoom = m_ZoomList[i - 1]; break; } } } void BASE_SCREEN::SetLastZoom() { if( m_ZoomList.IsEmpty() ) return; m_Zoom = m_ZoomList.Last(); } /********************************************/ void BASE_SCREEN::SetGridList( GridArray& gridlist ) /********************************************/ /* init liste des zoom (NULL terminated) */ { if( !m_GridList.IsEmpty() ) m_GridList.Clear(); m_GridList = gridlist; } /**********************************************/ void BASE_SCREEN::SetGrid( const wxSize& size ) /**********************************************/ { wxASSERT( !m_GridList.IsEmpty() ); size_t i; for( i = 0; i < m_GridList.GetCount(); i++ ) { if( m_GridList[i].m_Size == size ) { m_Grid = m_GridList[i].m_Size; return; } } m_Grid = m_GridList[0].m_Size; wxLogWarning( _( "Grid size( %d, %d ) not in grid list, falling back to " \ "grid size( %d, %d )." ), size.x, size.y, m_Grid.x, m_Grid.y ); } /* Set grid size from command ID. */ void BASE_SCREEN::SetGrid( int id ) { wxASSERT( !m_GridList.IsEmpty() ); size_t i; for( i = 0; i < m_GridList.GetCount(); i++ ) { if( m_GridList[i].m_Id == id ) { m_Grid = m_GridList[i].m_Size; return; } } m_Grid = m_GridList[0].m_Size; wxLogWarning( _( "Grid ID %d not in grid list, falling back to " \ "grid size( %d, %d )." ), id, m_Grid.x, m_Grid.y ); } void BASE_SCREEN::AddGrid( const GRID_TYPE& grid ) { size_t i; for( i = 0; i < m_GridList.GetCount(); i++ ) { if( m_GridList[i].m_Size == grid.m_Size ) { wxLogDebug( wxT( "Discarding duplicate grid size( %d, %d )." ), grid.m_Size.x, grid.m_Size.y ); return; } if( m_GridList[i].m_Id == grid.m_Id ) { wxLogDebug( wxT( "Changing grid ID %d from size( %d, %d ) to " \ "size( %d, %d )." ), grid.m_Id, m_GridList[i].m_Size.x, m_GridList[i].m_Size.y, grid.m_Size.x, grid.m_Size.y ); m_GridList[i].m_Size = grid.m_Size; return; } } // wxLogDebug( wxT( "Adding grid ID %d size( %d, %d ) to grid list." ), grid.m_Id, grid.m_Size.x, grid.m_Size.y ); m_GridList.Add( grid ); } void BASE_SCREEN::AddGrid( const wxSize& size, int id ) { GRID_TYPE grid; grid.m_Size = size; grid.m_Id = id; AddGrid( grid ); } void BASE_SCREEN::AddGrid( const wxRealPoint& size, int units, int id ) { double x, y; wxSize new_size; GRID_TYPE new_grid; if( units == MILLIMETRE ) { x = size.x / 25.4; y = size.y / 25.4; } else if( units == CENTIMETRE ) { x = size.x / 2.54; y = size.y / 2.54; } else { x = size.x; y = size.y; } new_size = wxSize( (int) round( x * (double) GetInternalUnits() ), (int) round( y * (double) GetInternalUnits() ) ); new_grid.m_Id = id; new_grid.m_Size = new_size; AddGrid( new_grid ); } /*********************************/ wxSize BASE_SCREEN::GetGrid() /*********************************/ { return m_Grid; } /*****************************************/ void BASE_SCREEN::ClearUndoRedoList() /*****************************************/ /* free the undo and the redo lists */ { EDA_BaseStruct* nextitem; while( m_UndoList ) { nextitem = m_UndoList->Next(); delete m_UndoList; m_UndoList = nextitem; } while( m_RedoList ) { nextitem = m_RedoList->Next(); delete m_RedoList; m_RedoList = nextitem; } } /***********************************************************/ void BASE_SCREEN::AddItemToUndoList( EDA_BaseStruct* newitem ) /************************************************************/ /* Put newitem in head of undo list * Deletes olds items if > count max. */ { int ii; EDA_BaseStruct* item; EDA_BaseStruct* nextitem; if( newitem == NULL ) return; newitem->SetNext( m_UndoList ); m_UndoList = newitem; /* Free first items, if count max reached */ for( ii = 0, item = m_UndoList; ii < m_UndoRedoCountMax; ii++ ) { if( item->Next() == NULL ) return; item = item->Next(); } if( item == NULL ) return; nextitem = item->Next(); item->SetNext( NULL ); // Set end of chain // Delete the extra items for( item = nextitem; item != NULL; item = nextitem ) { nextitem = item->Next(); delete item; } } /***********************************************************/ void BASE_SCREEN::AddItemToRedoList( EDA_BaseStruct* newitem ) /***********************************************************/ { int ii; EDA_BaseStruct* item, * nextitem; if( newitem == NULL ) return; newitem->SetNext( m_RedoList ); m_RedoList = newitem; /* Free first items, if count max reached */ for( ii = 0, item = m_RedoList; ii < m_UndoRedoCountMax; ii++ ) { if( item->Next() == NULL ) break; item = item->Next(); } if( item == NULL ) return; nextitem = item->Next(); item->SetNext( NULL ); // Set end of chain // Delete the extra items for( item = nextitem; item != NULL; item = nextitem ) { nextitem = item->Next(); delete item; } } /*****************************************************/ EDA_BaseStruct* BASE_SCREEN::GetItemFromUndoList() /*****************************************************/ { EDA_BaseStruct* item = m_UndoList; if( item ) m_UndoList = item->Next(); return item; } /******************************************************/ EDA_BaseStruct* BASE_SCREEN::GetItemFromRedoList() /******************************************************/ { EDA_BaseStruct* item = m_RedoList; if( item ) m_RedoList = item->Next(); return item; } #if defined(DEBUG) /** * Function Show * is used to output the object tree, currently for debugging only. * @param nestLevel An aid to prettier tree indenting, and is the level * of nesting of this object within the overall tree. * @param os The ostream& to output to. */ void BASE_SCREEN::Show( int nestLevel, std::ostream& os ) { SCH_ITEM* item = EEDrawList; // for now, make it look like XML, expand on this later. NestedSpace( nestLevel, os ) << '<' << GetClass().Lower().mb_str() << ">\n"; for( ; item; item = item->Next() ) { item->Show( nestLevel+1, os ); } NestedSpace( nestLevel, os ) << "\n"; } #endif