/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 Jon Evans * Copyright (C) 2020-2023 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. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include ///! Update the schema version whenever a migration is required const int fpEditSchemaVersion = 2; FOOTPRINT_EDITOR_SETTINGS::FOOTPRINT_EDITOR_SETTINGS() : APP_SETTINGS_BASE( "fpedit", fpEditSchemaVersion ), m_DesignSettings( nullptr, "fpedit.settings" ), m_MagneticItems(), m_Display(), m_UserGrid(), m_PolarCoords( false ), m_RotationAngle( ANGLE_90 ), m_Use45Limit( true ), m_ArcEditMode( ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS ), m_LibWidth( 250 ), m_LastExportPath(), m_FootprintTextShownColumns() { m_MagneticItems.pads = MAGNETIC_OPTIONS::CAPTURE_ALWAYS; m_MagneticItems.tracks = MAGNETIC_OPTIONS::NO_EFFECT; m_MagneticItems.graphics = true; m_MagneticItems.allLayers = false; m_AuiPanels.appearance_panel_tab = 0; m_AuiPanels.right_panel_width = -1; m_AuiPanels.show_layer_manager = true; m_params.emplace_back( new PARAM( "window.lib_width", &m_LibWidth, 250 ) ); m_params.emplace_back( new PARAM( "aui.show_layer_manager", &m_AuiPanels.show_layer_manager, true ) ); m_params.emplace_back( new PARAM( "aui.right_panel_width", &m_AuiPanels.right_panel_width, -1 ) ); m_params.emplace_back( new PARAM( "aui.appearance_panel_tab", &m_AuiPanels.appearance_panel_tab, 0, 0, 2 ) ); m_params.emplace_back( new PARAM( "aui.properties_panel_width", &m_AuiPanels.properties_panel_width, -1 ) ); m_params.emplace_back( new PARAM( "aui.properties_splitter_proportion", &m_AuiPanels.properties_splitter, 0.5f ) ); m_params.emplace_back( new PARAM( "aui.show_properties", &m_AuiPanels.show_properties, false ) ); m_params.emplace_back( new PARAM( "library.sort_mode", &m_LibrarySortMode, 0 ) ); m_params.emplace_back( new PARAM( "system.last_import_export_path", &m_LastExportPath, "" ) ); m_params.emplace_back( new PARAM( "window.footprint_text_shown_columns", &m_FootprintTextShownColumns, "0 1 2 3 4 5 6" ) ); m_params.emplace_back( new PARAM( "editing.magnetic_pads", reinterpret_cast( &m_MagneticItems.pads ), static_cast( MAGNETIC_OPTIONS::CAPTURE_ALWAYS ) ) ); m_params.emplace_back( new PARAM( "editing.magnetic_graphics", &m_MagneticItems.graphics, true ) ); m_params.emplace_back( new PARAM( "editing.magnetic_all_layers", &m_MagneticItems.allLayers, false ) ); m_params.emplace_back( new PARAM( "editing.polar_coords", &m_PolarCoords, false ) ); m_params.emplace_back( new PARAM_LAMBDA( "editing.rotation_angle", [this] () -> int { return m_RotationAngle.AsTenthsOfADegree(); }, [this] ( int aVal ) { if( aVal ) m_RotationAngle = EDA_ANGLE( aVal, TENTHS_OF_A_DEGREE_T ); }, 900 ) ); m_params.emplace_back( new PARAM( "editing.fp_use_45_degree_limit", &m_Use45Limit, false ) ); m_params.emplace_back( new PARAM_LAYER_PRESET( "pcb_display.layer_presets", &m_LayerPresets ) ); m_params.emplace_back( new PARAM( "pcb_display.active_layer_preset", &m_ActiveLayerPreset, "" ) ); m_params.emplace_back( new PARAM_LAMBDA( "design_settings.default_footprint_text_items", [&] () -> nlohmann::json { nlohmann::json js = nlohmann::json::array(); for( const TEXT_ITEM_INFO& item : m_DesignSettings.m_DefaultFPTextItems ) { js.push_back( nlohmann::json( { item.m_Text.ToUTF8(), item.m_Visible, item.m_Layer } ) ); } return js; }, [&] ( const nlohmann::json& aObj ) { m_DesignSettings.m_DefaultFPTextItems.clear(); if( !aObj.is_array() ) return; for( const nlohmann::json& entry : aObj ) { if( entry.empty() || !entry.is_array() ) continue; TEXT_ITEM_INFO textInfo( wxT( "" ), true, F_SilkS ); textInfo.m_Text = entry.at(0).get(); textInfo.m_Visible = entry.at(1).get(); textInfo.m_Layer = entry.at(2).get(); m_DesignSettings.m_DefaultFPTextItems.push_back( std::move( textInfo ) ); } }, nlohmann::json::array( { { "REF**", true, F_SilkS }, { "", true, F_Fab }, { "${REFERENCE}", true, F_Fab } } ) ) ); int minTextSize = pcbIUScale.MilsToIU( TEXT_MIN_SIZE_MILS ); int maxTextSize = pcbIUScale.MilsToIU( TEXT_MAX_SIZE_MILS ); int minStroke = 1; int maxStroke = pcbIUScale.mmToIU( 100 ); m_params.emplace_back( new PARAM_SCALED( "design_settings.silk_line_width", &m_DesignSettings.m_LineThickness[ LAYER_CLASS_SILK ], pcbIUScale.mmToIU( DEFAULT_SILK_LINE_WIDTH ), minStroke, maxStroke, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.silk_text_size_h", &m_DesignSettings.m_TextSize[ LAYER_CLASS_SILK ].x, pcbIUScale.mmToIU( DEFAULT_SILK_TEXT_SIZE ), minTextSize, maxTextSize, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.silk_text_size_v", &m_DesignSettings.m_TextSize[ LAYER_CLASS_SILK ].y, pcbIUScale.mmToIU( DEFAULT_SILK_TEXT_SIZE ), minTextSize, maxTextSize, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.silk_text_thickness", &m_DesignSettings.m_TextThickness[ LAYER_CLASS_SILK ], pcbIUScale.mmToIU( DEFAULT_SILK_TEXT_WIDTH ), 1, maxTextSize, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM( "design_settings.silk_text_italic", &m_DesignSettings.m_TextItalic[ LAYER_CLASS_SILK ], false ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.copper_line_width", &m_DesignSettings.m_LineThickness[ LAYER_CLASS_COPPER ], pcbIUScale.mmToIU( DEFAULT_COPPER_LINE_WIDTH ), minStroke, maxStroke, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.copper_text_size_h", &m_DesignSettings.m_TextSize[ LAYER_CLASS_COPPER ].x, pcbIUScale.mmToIU( DEFAULT_COPPER_TEXT_SIZE ), minTextSize, maxTextSize, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.copper_text_size_v", &m_DesignSettings.m_TextSize[ LAYER_CLASS_COPPER ].y, pcbIUScale.mmToIU( DEFAULT_COPPER_TEXT_SIZE ), minTextSize, maxTextSize, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.copper_text_thickness", &m_DesignSettings.m_TextThickness[ LAYER_CLASS_COPPER ], pcbIUScale.mmToIU( DEFAULT_COPPER_TEXT_WIDTH ), minStroke, maxStroke, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM( "design_settings.copper_text_italic", &m_DesignSettings.m_TextItalic[ LAYER_CLASS_COPPER ], false ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.edge_line_width", &m_DesignSettings.m_LineThickness[ LAYER_CLASS_EDGES ], pcbIUScale.mmToIU( DEFAULT_EDGE_WIDTH ), minStroke, maxStroke, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.courtyard_line_width", &m_DesignSettings.m_LineThickness[ LAYER_CLASS_COURTYARD ], pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH ), minStroke, maxStroke, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.fab_line_width", &m_DesignSettings.m_LineThickness[ LAYER_CLASS_FAB ], pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ), minStroke, maxStroke, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.fab_text_size_h", &m_DesignSettings.m_TextSize[ LAYER_CLASS_FAB ].x, pcbIUScale.mmToIU( DEFAULT_TEXT_SIZE ), minTextSize, maxTextSize, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.fab_text_size_v", &m_DesignSettings.m_TextSize[ LAYER_CLASS_FAB ].y, pcbIUScale.mmToIU( DEFAULT_TEXT_SIZE ), minTextSize, maxTextSize, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.fab_text_thickness", &m_DesignSettings.m_TextThickness[ LAYER_CLASS_FAB ], pcbIUScale.mmToIU( DEFAULT_TEXT_WIDTH ), 1, maxTextSize, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM( "design_settings.fab_text_italic", &m_DesignSettings.m_TextItalic[ LAYER_CLASS_FAB ], false ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.others_line_width", &m_DesignSettings.m_LineThickness[ LAYER_CLASS_OTHERS ], pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ), minStroke, maxStroke, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.others_text_size_h", &m_DesignSettings.m_TextSize[ LAYER_CLASS_OTHERS ].x, pcbIUScale.mmToIU( DEFAULT_TEXT_SIZE ), minTextSize, maxTextSize, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.others_text_size_v", &m_DesignSettings.m_TextSize[ LAYER_CLASS_OTHERS ].y, pcbIUScale.mmToIU( DEFAULT_TEXT_SIZE ), minTextSize, maxTextSize, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM_SCALED( "design_settings.others_text_thickness", &m_DesignSettings.m_TextThickness[ LAYER_CLASS_OTHERS ], pcbIUScale.mmToIU( DEFAULT_TEXT_WIDTH ), 1, maxTextSize, pcbIUScale.MM_PER_IU ) ); m_params.emplace_back( new PARAM( "design_settings.others_text_italic", &m_DesignSettings.m_TextItalic[ LAYER_CLASS_OTHERS ], false ) ); m_params.emplace_back( new PARAM_LAMBDA( "editing.selection_filter", [&]() -> nlohmann::json { nlohmann::json ret; ret["lockedItems"] = m_SelectionFilter.lockedItems; ret["footprints"] = m_SelectionFilter.footprints; ret["text"] = m_SelectionFilter.text; ret["tracks"] = m_SelectionFilter.tracks; ret["vias"] = m_SelectionFilter.vias; ret["pads"] = m_SelectionFilter.pads; ret["graphics"] = m_SelectionFilter.graphics; ret["zones"] = m_SelectionFilter.zones; ret["keepouts"] = m_SelectionFilter.keepouts; ret["dimensions"] = m_SelectionFilter.dimensions; ret["otherItems"] = m_SelectionFilter.otherItems; return ret; }, [&]( const nlohmann::json& aVal ) { if( aVal.empty() || !aVal.is_object() ) return; SetIfPresent( aVal, "lockedItems", m_SelectionFilter.lockedItems ); SetIfPresent( aVal, "footprints", m_SelectionFilter.footprints ); SetIfPresent( aVal, "text", m_SelectionFilter.text ); SetIfPresent( aVal, "tracks", m_SelectionFilter.tracks ); SetIfPresent( aVal, "vias", m_SelectionFilter.vias ); SetIfPresent( aVal, "pads", m_SelectionFilter.pads ); SetIfPresent( aVal, "graphics", m_SelectionFilter.graphics ); SetIfPresent( aVal, "zones", m_SelectionFilter.zones ); SetIfPresent( aVal, "keepouts", m_SelectionFilter.keepouts ); SetIfPresent( aVal, "dimensions", m_SelectionFilter.dimensions ); SetIfPresent( aVal, "otherItems", m_SelectionFilter.otherItems ); }, { { "lockedItems", false }, { "footprints", true }, { "text", true }, { "tracks", true }, { "vias", true }, { "pads", true }, { "graphics", true }, { "zones", true }, { "keepouts", true }, { "dimensions", true }, { "otherItems", true } } ) ); registerMigration( 0, 1, std::bind( &FOOTPRINT_EDITOR_SETTINGS::migrateSchema0to1, this ) ); registerMigration( 1, 2, [&]() -> bool { // This is actually a migration for APP_SETTINGS_BASE::m_LibTree return migrateLibTreeWidth(); } ); } bool FOOTPRINT_EDITOR_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) { bool ret = APP_SETTINGS_BASE::MigrateFromLegacy( aCfg ); // // NOTE: there's no value in line-wrapping these; it just makes the table unreadable. // ret &= fromLegacy( aCfg, "ModeditLibWidth", "window.lib_width" ); ret &= fromLegacyString( aCfg, "import_last_path", "system.last_import_export_path" ); ret &= fromLegacyString( aCfg, "LibFootprintTextShownColumns", "window.footprint_text_shown_columns" ); ret &= fromLegacy( aCfg, "FpEditorMagneticPads", "editing.magnetic_pads" ); ret &= fromLegacy( aCfg, "FpEditorDisplayPolarCoords", "editing.polar_coords" ); ret &= fromLegacy( aCfg, "FpEditorUse45DegreeGraphicSegments", "editing.use_45_degree_graphic_segments" ); ret &= fromLegacy( aCfg, "FpEditorGraphicLinesDisplayMode", "pcb_display.graphic_items_fill" ); ret &= fromLegacy( aCfg, "FpEditorPadDisplayMode", "pcb_display.pad_fill" ); ret &= fromLegacy( aCfg, "FpEditorTextsDisplayMode", "pcb_display.footprint_text" ); ret &= fromLegacy( aCfg, "FpEditorSilkLineWidth", "design_settings.silk_line_width" ); ret &= fromLegacy( aCfg, "FpEditorSilkTextSizeH", "design_settings.silk_text_size_h" ); ret &= fromLegacy( aCfg, "FpEditorSilkTextSizeV", "design_settings.silk_text_size_v" ); ret &= fromLegacy( aCfg, "FpEditorSilkTextThickness", "design_settings.silk_text_thickness" ); ret &= fromLegacy( aCfg, "FpEditorSilkTextItalic", "design_settings.silk_text_italic" ); ret &= fromLegacy( aCfg, "FpEditorCopperLineWidth", "design_settings.copper_line_width" ); ret &= fromLegacy( aCfg, "FpEditorCopperTextSizeH", "design_settings.copper_text_size_h" ); ret &= fromLegacy( aCfg, "FpEditorCopperTextSizeV", "design_settings.copper_text_size_v" ); ret &= fromLegacy( aCfg, "FpEditorCopperTextThickness", "design_settings.copper_text_thickness" ); ret &= fromLegacy( aCfg, "FpEditorCopperTextItalic", "design_settings.copper_text_italic" ); ret &= fromLegacy( aCfg, "FpEditorEdgeCutLineWidth", "design_settings.edge_line_width" ); ret &= fromLegacy( aCfg, "FpEditorCourtyardLineWidth", "design_settings.courtyard_line_width" ); ret &= fromLegacy( aCfg, "FpEditorOthersLineWidth", "design_settings.others_line_width" ); ret &= fromLegacy( aCfg, "FpEditorOthersTextSizeH", "design_settings.others_text_size_h" ); ret &= fromLegacy( aCfg, "FpEditorOthersTextSizeV", "design_settings.others_text_size_v" ); ret &= fromLegacy( aCfg, "FpEditorOthersTextThickness", "design_settings.others_text_thickness" ); ret &= fromLegacy( aCfg, "FpEditorOthersTextItalic", "design_settings.others_text_italic" ); nlohmann::json textItems = nlohmann::json::array( { { "REF**", true, F_SilkS }, { "", true, F_Fab } } ); Set( "design_settings.default_footprint_text_items", textItems ); ret &= fromLegacyString( aCfg, "FpEditorRefDefaultText", "design_settings.default_footprint_text_items.0.0" ); ret &= fromLegacy( aCfg, "FpEditorRefDefaultVisibility", "design_settings.default_footprint_text_items.0.1" ); ret &= fromLegacy( aCfg, "FpEditorRefDefaultLayer", "design_settings.default_footprint_text_items.0.2" ); ret &= fromLegacyString( aCfg, "FpEditorValueDefaultText", "design_settings.default_footprint_text_items.1.0" ); ret &= fromLegacy( aCfg, "FpEditorValueDefaultVisibility", "design_settings.default_footprint_text_items.1.1" ); ret &= fromLegacy( aCfg, "FpEditorValueDefaultLayer", "design_settings.default_footprint_text_items.1.2" ); std::string f = "ModEdit"; // Migrate color settings that were stored in the pcbnew config file // We create a copy of the user scheme for the footprint editor context SETTINGS_MANAGER& manager = Pgm().GetSettingsManager(); COLOR_SETTINGS* cs = manager.AddNewColorSettings( "user_footprints" ); cs->SetName( wxT( "User (Footprints)" ) ); manager.Save( cs ); auto migrateLegacyColor = [&] ( const std::string& aKey, int aLayerId ) { wxString str; if( aCfg->Read( aKey, &str ) ) cs->SetColor( aLayerId, COLOR4D( str ) ); }; for( int i = 0; i < PCB_LAYER_ID_COUNT; ++i ) { wxString layer = LSET::Name( PCB_LAYER_ID( i ) ); migrateLegacyColor( f + "Color4DPCBLayer_" + layer.ToStdString(), PCB_LAYER_ID( i ) ); } migrateLegacyColor( f + "Color4DAnchorEx", LAYER_ANCHOR ); migrateLegacyColor( f + "Color4DAuxItems", LAYER_AUX_ITEMS ); migrateLegacyColor( f + "Color4DGrid", LAYER_GRID ); migrateLegacyColor( f + "Color4DNonPlatedEx", LAYER_NON_PLATEDHOLES ); migrateLegacyColor( f + "Color4DPadThruHoleEx", LAYER_PADS_TH ); migrateLegacyColor( f + "Color4DPCBBackground", LAYER_PCB_BACKGROUND ); migrateLegacyColor( f + "Color4DPCBCursor", LAYER_CURSOR ); migrateLegacyColor( f + "Color4DRatsEx", LAYER_RATSNEST ); migrateLegacyColor( f + "Color4DTxtInvisEx", LAYER_HIDDEN_TEXT ); migrateLegacyColor( f + "Color4DViaBBlindEx", LAYER_VIA_BBLIND ); migrateLegacyColor( f + "Color4DViaMicroEx", LAYER_VIA_MICROVIA ); migrateLegacyColor( f + "Color4DViaThruEx", LAYER_VIA_THROUGH ); migrateLegacyColor( f + "Color4DWorksheet", LAYER_DRAWINGSHEET ); manager.SaveColorSettings( cs, "board" ); ( *m_internals )[m_internals->PointerFromString( "appearance.color_theme" )] = "user_footprints"; double x = 0, y = 0; f = "ModEditFrame"; if( aCfg->Read( f + "PcbUserGrid_X", &x ) && aCfg->Read( f + "PcbUserGrid_Y", &y ) ) { EDA_UNITS u = static_cast( aCfg->ReadLong( f + "PcbUserGrid_Unit", static_cast( EDA_UNITS::INCHES ) ) ); // Convert to internal units x = EDA_UNIT_UTILS::UI::FromUserUnit( pcbIUScale, u, x ); y = EDA_UNIT_UTILS::UI::FromUserUnit( pcbIUScale, u, y ); Set( "window.grid.user_grid_x", EDA_UNIT_UTILS::UI::StringFromValue( pcbIUScale, u, x ) ); Set( "window.grid.user_grid_y", EDA_UNIT_UTILS::UI::StringFromValue( pcbIUScale, u, y ) ); } return ret; } bool FOOTPRINT_EDITOR_SETTINGS::migrateSchema0to1() { /** * Schema version 0 to 1: * * - Check to see if a footprints version of the currently selected theme exists. * - If so, select it */ if( !m_manager ) { wxLogTrace( traceSettings, wxT( "Error: FOOTPRINT_EDITOR_SETTINGS migration cannot run unmanaged!" ) ); return false; } std::string theme_ptr( "appearance.color_theme" ); if( !Contains( theme_ptr ) ) return true; wxString selected = At( theme_ptr ).get(); wxString search = selected + wxT( "_footprints" ); for( COLOR_SETTINGS* settings : Pgm().GetSettingsManager().GetColorSettingsList() ) { if( settings->GetFilename() == search ) { wxLogTrace( traceSettings, wxT( "Updating footprint editor theme from %s to %s" ), selected, search ); Set( theme_ptr, search ); return true; } } return true; }