/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright The 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 3 * 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. * * Inspired by the Inkscape preferences widget, which is * Authors: * Marco Scholten * Bruno Dilly * * Copyright (C) 2004, 2006, 2007 Authors * * Released under GNU GPL v2+ */ #include #include #include #include #include #include #include #include #include class ZOOM_CORRECTION_RULER : public wxPanel { public: ZOOM_CORRECTION_RULER( wxWindow* aParent ) : wxPanel( aParent, wxID_ANY, wxDefaultPosition, aParent->FromDIP( wxSize( 200, 30 ) ), wxTAB_TRAVERSAL | wxNO_BORDER | wxFULL_REPAINT_ON_RESIZE ) { Bind( wxEVT_PAINT, &ZOOM_CORRECTION_RULER::OnPaint, this ); } private: void OnPaint( wxPaintEvent& ) { wxPaintDC dc( this ); wxSize size = GetClientSize(); dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) ) ); // Draw baseline dc.DrawLine( 0, size.y - 1, size.x, size.y - 1 ); ZOOM_CORRECTION_CTRL* parent = static_cast( GetParent() ); if( !parent ) return; double value = parent->GetValue(); ZOOM_CORRECTION_UNITS units = static_cast( parent->GetUnitsSelection() ); double dpi = ADVANCED_CFG::GetCfg().m_ScreenDPI; double unitsPerInch = 25.4; if( units == ZOOM_CORRECTION_UNITS::CM ) unitsPerInch = 2.54; else if( units == ZOOM_CORRECTION_UNITS::INCH ) unitsPerInch = 1.0; double pxPerUnit = dpi / unitsPerInch * value; // Minimum spacing between number labels to prevent overlap (in pixels) int minLabelSpacing = GetTextExtent( wxT( "000_" ) ).x; double lastLabelX = -minLabelSpacing; int majorTickEvery = 10; double tickLabelDiv = 1; double pxPerMinorTick; // Draw major and minor tick marks with numbering if( units == ZOOM_CORRECTION_UNITS::INCH ) { majorTickEvery = 4; tickLabelDiv = 4; pxPerMinorTick = pxPerUnit / 4.0; // 1/4 inch intervals } else if( units == ZOOM_CORRECTION_UNITS::CM ) { majorTickEvery = 10; tickLabelDiv = 10; pxPerMinorTick = pxPerUnit / 10.0; // 1mm intervals } else // MM { // For mm: Same as cm ruler but numbers count by 10 (so 10mm, 20mm, etc.) majorTickEvery = 10; tickLabelDiv = 1; pxPerMinorTick = pxPerUnit; } if( pxPerMinorTick < 3 ) { pxPerMinorTick *= 2; majorTickEvery /= 2; tickLabelDiv /= 2; } for( double x = 0; x <= size.x; x += pxPerMinorTick ) { int tickCount = (int) round( x / pxPerMinorTick ); if( tickCount % majorTickEvery == 0 ) { // Major tick dc.DrawLine( x, size.y - 1, x, size.y - 16 ); int labelNum = wxRound(tickCount / tickLabelDiv); if( labelNum > 0 && x < size.x - 10 && ( x - lastLabelX ) >= minLabelSpacing ) { wxString label = wxString::Format( wxT( "%d" ), labelNum ); wxSize textSize = dc.GetTextExtent( label ); dc.DrawText( label, x - textSize.x / 2, 0 ); lastLabelX = x; } } else { // Minor tick dc.DrawLine( x, size.y - 1, x, size.y - 8 ); } } } }; ZOOM_CORRECTION_CTRL::ZOOM_CORRECTION_CTRL( wxWindow* aParent, double& aValue, double aBaseValue ) : wxPanel( aParent, wxID_ANY ), m_baseValue( aBaseValue ), m_value( &aValue ) { wxBoxSizer* topSizer = new wxBoxSizer( wxVERTICAL ); // Top section: Label, spinner, auto button wxBoxSizer* controlsSizer = new wxBoxSizer( wxHORIZONTAL ); m_label = new wxStaticText( this, wxID_ANY, _( "Display PPI: " ) ); controlsSizer->Add( m_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, KIUI::GetStdMargin() ); m_spinner = new wxSpinCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 10, 1000, (int)( aValue * m_baseValue ) ); controlsSizer->Add( m_spinner, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, KIUI::GetStdMargin() ); m_autoButton = new wxButton( this, wxID_ANY, _( "Detect" ) ); controlsSizer->Add( m_autoButton, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, KIUI::GetStdMargin() ); topSizer->Add( controlsSizer, 0, wxEXPAND, KIUI::GetStdMargin() ); // Middle section: Ruler and units choice wxBoxSizer* rulerSizer = new wxBoxSizer( wxHORIZONTAL ); m_ruler = new ZOOM_CORRECTION_RULER( this ); rulerSizer->Add( m_ruler, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, KIUI::GetStdMargin() ); wxArrayString choices; choices.Add( wxT( "mm" ) ); choices.Add( wxT( "cm" ) ); choices.Add( wxT( "in" ) ); m_unitsChoice = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices ); m_unitsChoice->SetSelection( 0 ); // Default to MM rulerSizer->Add( m_unitsChoice, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT | wxTOP, KIUI::GetStdMargin() ); topSizer->Add( rulerSizer, 0, wxEXPAND | wxTOP, KIUI::GetStdMargin() ); SetSizer( topSizer ); m_autoButton->Bind( wxEVT_BUTTON, &ZOOM_CORRECTION_CTRL::autoPressed, this ); m_spinner->Bind( wxEVT_SPINCTRL, &ZOOM_CORRECTION_CTRL::spinnerChanged, this ); m_unitsChoice->Bind( wxEVT_CHOICE, &ZOOM_CORRECTION_CTRL::unitsChanged, this ); // Ensure initial synchronization of all controls m_ruler->Refresh(); } void ZOOM_CORRECTION_CTRL::SetDisplayedValue( double aValue ) { m_spinner->SetValue( (int)( aValue * m_baseValue ) ); m_ruler->Refresh(); } double ZOOM_CORRECTION_CTRL::GetValue() const { return (double) m_spinner->GetValue() / m_baseValue; } int ZOOM_CORRECTION_CTRL::GetUnitsSelection() const { return m_unitsChoice->GetSelection(); } bool ZOOM_CORRECTION_CTRL::TransferDataToWindow() { m_spinner->SetValue( (int)( *m_value * m_baseValue ) ); m_ruler->Refresh(); return true; } bool ZOOM_CORRECTION_CTRL::TransferDataFromWindow() { *m_value = GetValue(); return true; } void ZOOM_CORRECTION_CTRL::spinnerChanged( wxSpinEvent& ) { *m_value = m_spinner->GetValue() / m_baseValue; m_ruler->Refresh(); } void ZOOM_CORRECTION_CTRL::unitsChanged( wxCommandEvent& ) { m_ruler->Refresh(); } void ZOOM_CORRECTION_CTRL::autoPressed( wxCommandEvent& aEvent ) { wxDisplay dpy( this ); double val = 0.0; #if wxCHECK_VERSION( 3, 3, 2 ) wxSize rawPPI = dpy.GetRawPPI(); val = wxRound( ( rawPPI.x + rawPPI.y ) / 2.0 ); #endif if( val < 10 ) { #ifdef wxHAS_DPI_INDEPENDENT_PIXELS val = dpy.GetStdPPIValue() / dpy.GetScaleFactor(); #else wxSize ppi = dpy.GetPPI(); val = wxRound( ( ppi.x + ppi.y ) / 2.0 ); #endif } m_spinner->SetValue( val ); m_ruler->Refresh(); }