/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr * 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 */ /** * @file zoom.cpp */ /* * Manage zoom, grid step, and auto crop. */ #include #include #include #include #include #include #include #include #include #include #include void EDA_DRAW_FRAME::RedrawScreen( const wxPoint& aCenterPoint, bool aWarpPointer ) { if( IsGalCanvasActive() ) return; AdjustScrollBars( aCenterPoint ); // Move the mouse cursor to the on grid graphic cursor position if( aWarpPointer ) m_canvas->MoveCursorToCrossHair(); m_canvas->Refresh(); m_canvas->Update(); } void EDA_DRAW_FRAME::RedrawScreen2( const wxPoint& posBefore ) { if( IsGalCanvasActive() ) return; // Account for scrollbars (see EDA_DRAW_FRAME::AdjustScrollBars that takes // in account scroolbars area to adjust scroll bars) wxSize scrollbarSize = m_canvas->GetSize() - m_canvas->GetClientSize(); wxSize sizeAdjusted = m_canvas->GetClientSize() - scrollbarSize; wxPoint dPos = posBefore - sizeAdjusted / 2; // screen position of crosshair after zoom wxPoint newScreenPos = m_canvas->ToDeviceXY( GetCrossHairPosition() ); wxPoint newCenter = m_canvas->ToLogicalXY( newScreenPos - dPos ); AdjustScrollBars( newCenter ); m_canvas->Refresh(); m_canvas->Update(); } // Factor out the calculation portion of the various BestZoom() implementations. // // Note that like it's forerunners this routine has an intentional side-effect: it // sets the scroll centre position. While I'm not happy about that, it's probably // not worth fixing as its days are numbered (GAL canvases use a different method). double EDA_DRAW_FRAME::bestZoom( double sizeX, double sizeY, double scaleFactor, wxPoint centre ) { double bestzoom = std::max( sizeX * scaleFactor / (double) m_canvas->GetClientSize().x, sizeY * scaleFactor / (double) m_canvas->GetClientSize().y ); // Take scrollbars into account DSIZE scrollbarSize = m_canvas->GetSize() - m_canvas->GetClientSize(); centre.x -= int( bestzoom * scrollbarSize.x / 2.0 ); centre.y -= int( bestzoom * scrollbarSize.y / 2.0 ); SetScrollCenterPosition( centre ); return bestzoom; } void EDA_DRAW_FRAME::Zoom_Automatique( bool aWarpPointer ) { BASE_SCREEN* screen = GetScreen(); // Set the best zoom and get center point. // BestZoom() can compute an illegal zoom if the client window size // is small, say because frame is not maximized. So use the clamping form // of SetZoom(): double bestzoom = BestZoom(); screen->SetScalingFactor( bestzoom ); if( screen->m_FirstRedraw ) SetCrossHairPosition( GetScrollCenterPosition() ); if( !IsGalCanvasActive() ) RedrawScreen( GetScrollCenterPosition(), aWarpPointer ); else m_toolManager->RunAction( "common.Control.zoomFitScreen", true ); } void EDA_DRAW_FRAME::Window_Zoom( EDA_RECT& Rect ) { // Compute the best zoom Rect.Normalize(); wxSize size = m_canvas->GetClientSize(); // Use ceil to at least show the full rect double scalex = (double) Rect.GetSize().x / size.x; double bestscale = (double) Rect.GetSize().y / size.y; bestscale = std::max( bestscale, scalex ); GetScreen()->SetScalingFactor( bestscale ); RedrawScreen( Rect.Centre(), true ); } void EDA_DRAW_FRAME::OnZoom( wxCommandEvent& event ) { if( m_canvas == NULL ) return; int id = event.GetId(); bool zoom_at_cursor = false; BASE_SCREEN* screen = GetScreen(); wxPoint center = GetScrollCenterPosition(); if ( id == ID_KEY_ZOOM_IN ) { id = GetCanvas()->GetEnableZoomNoCenter() ? ID_OFFCENTER_ZOOM_IN : ID_POPUP_ZOOM_IN; } else if ( id == ID_KEY_ZOOM_OUT ) { id = GetCanvas()->GetEnableZoomNoCenter() ? ID_OFFCENTER_ZOOM_OUT : ID_POPUP_ZOOM_OUT; } switch( id ) { case ID_OFFCENTER_ZOOM_IN: center = m_canvas->ToDeviceXY( GetCrossHairPosition() ); if( screen->SetPreviousZoom() ) RedrawScreen2( center ); break; case ID_POPUP_ZOOM_IN: zoom_at_cursor = true; center = GetCrossHairPosition(); // fall thru case ID_VIEWER_ZOOM_IN: case ID_ZOOM_IN: if( screen->SetPreviousZoom() ) RedrawScreen( center, zoom_at_cursor ); break; case ID_OFFCENTER_ZOOM_OUT: center = m_canvas->ToDeviceXY( GetCrossHairPosition() ); if( screen->SetNextZoom() ) RedrawScreen2( center ); break; case ID_POPUP_ZOOM_OUT: zoom_at_cursor = true; center = GetCrossHairPosition(); // fall thru case ID_VIEWER_ZOOM_OUT: case ID_ZOOM_OUT: if( screen->SetNextZoom() ) RedrawScreen( center, zoom_at_cursor ); break; case ID_VIEWER_ZOOM_REDRAW: case ID_POPUP_ZOOM_REDRAW: case ID_ZOOM_REDRAW: m_canvas->Refresh(); break; case ID_POPUP_ZOOM_CENTER: center = GetCrossHairPosition(); RedrawScreen( center, true ); break; case ID_POPUP_ZOOM_PAGE: case ID_VIEWER_ZOOM_PAGE: case ID_ZOOM_PAGE: Zoom_Automatique( false ); break; case ID_POPUP_ZOOM_SELECT: break; case ID_POPUP_CANCEL: m_canvas->MoveCursorToCrossHair(); break; default: SetPresetZoom( id - ID_POPUP_ZOOM_LEVEL_START ); } UpdateStatusBar(); } void EDA_DRAW_FRAME::SetNextZoom() { GetScreen()->SetNextZoom(); } void EDA_DRAW_FRAME::SetPrevZoom() { GetScreen()->SetPreviousZoom(); } void EDA_DRAW_FRAME::SetPresetZoom( int aIndex ) { BASE_SCREEN* screen = GetScreen(); if( aIndex >= (int) screen->m_ZoomList.size() ) { wxLogDebug( wxT( "%s %d: index %d is outside the bounds of the zoom list." ), __TFILE__, __LINE__, aIndex ); return; } if( m_zoomSelectBox ) m_zoomSelectBox->SetSelection( aIndex ); if( screen->SetZoom( screen->m_ZoomList[aIndex] ) ) RedrawScreen( GetScrollCenterPosition(), true ); UpdateStatusBar(); } void EDA_DRAW_FRAME::AddMenuZoomAndGrid( wxMenu* MasterMenu ) { int maxZoomIds; double zoom; wxString msg; BASE_SCREEN* screen = m_canvas->GetScreen(); msg = AddHotkeyName( _( "Center" ), m_hotkeysDescrList, HK_ZOOM_CENTER ); AddMenuItem( MasterMenu, ID_POPUP_ZOOM_CENTER, msg, KiBitmap( zoom_center_on_screen_xpm ) ); msg = AddHotkeyName( _( "Zoom In" ), m_hotkeysDescrList, HK_ZOOM_IN ); AddMenuItem( MasterMenu, ID_POPUP_ZOOM_IN, msg, KiBitmap( zoom_in_xpm ) ); msg = AddHotkeyName( _( "Zoom Out" ), m_hotkeysDescrList, HK_ZOOM_OUT ); AddMenuItem( MasterMenu, ID_POPUP_ZOOM_OUT, msg, KiBitmap( zoom_out_xpm ) ); msg = AddHotkeyName( _( "Redraw View" ), m_hotkeysDescrList, HK_ZOOM_REDRAW ); AddMenuItem( MasterMenu, ID_POPUP_ZOOM_REDRAW, msg, KiBitmap( zoom_redraw_xpm ) ); msg = AddHotkeyName( _( "Zoom to Fit" ), m_hotkeysDescrList, HK_ZOOM_AUTO ); AddMenuItem( MasterMenu, ID_POPUP_ZOOM_PAGE, msg, KiBitmap( zoom_fit_in_page_xpm ) ); wxMenu* zoom_choice = new wxMenu; AddMenuItem( MasterMenu, zoom_choice, ID_POPUP_ZOOM_SELECT, _( "Zoom" ), KiBitmap( zoom_selection_xpm ) ); zoom = screen->GetZoom(); maxZoomIds = ID_POPUP_ZOOM_LEVEL_END - ID_POPUP_ZOOM_LEVEL_START; maxZoomIds = ( (size_t) maxZoomIds < screen->m_ZoomList.size() ) ? maxZoomIds : screen->m_ZoomList.size(); // Populate zoom submenu. for( int i = 0; i < maxZoomIds; i++ ) { msg.Printf( wxT( "%.2f" ), m_zoomLevelCoeff / screen->m_ZoomList[i] ); zoom_choice->Append( ID_POPUP_ZOOM_LEVEL_START + i, _( "Zoom: " ) + msg, wxEmptyString, wxITEM_CHECK ); if( zoom == screen->m_ZoomList[i] ) zoom_choice->Check( ID_POPUP_ZOOM_LEVEL_START + i, true ); } // Create grid submenu as required. if( screen->GetGridCount() ) { wxMenu* gridMenu = new wxMenu; AddMenuItem( MasterMenu, gridMenu, ID_POPUP_GRID_SELECT, _( "Grid" ), KiBitmap( grid_select_xpm ) ); wxArrayString gridsList; int icurr = screen->BuildGridsChoiceList( gridsList, GetUserUnits() != INCHES ); for( unsigned i = 0; i < gridsList.GetCount(); i++ ) { GRID_TYPE& grid = screen->GetGrid( i ); gridMenu->Append( grid.m_CmdId, gridsList[i], wxEmptyString, wxITEM_CHECK ); if( (int)i == icurr ) gridMenu->Check( grid.m_CmdId, true ); } } MasterMenu->AppendSeparator(); AddMenuItem( MasterMenu, ID_POPUP_CANCEL, _( "Close" ), KiBitmap( cancel_xpm ) ); }