Files
Seth Hillbrand c52ab91c84 Reduce compile times by extracting headers
Break the most expensive header include chains that contaminate the
largest number of translation units

New lightweight headers for extractdded types:
font_metrics.h, mouse_drag_action.h, board_project_settings_params.h,
common_settings_internals.h, wx_infobar_message_type.h,
zone_layer_properties_grid.h
2026-02-13 17:10:45 -08:00

401 lines
13 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ranges>
#include <tuple>
#include <api/api_handler_common.h>
#include <build_version.h>
#include <eda_shape.h>
#include <eda_text.h>
#include <gestfich.h>
#include <geometry/shape_compound.h>
#include <google/protobuf/empty.pb.h>
#include <paths.h>
#include <pgm_base.h>
#include <api/api_plugin.h>
#include <api/api_utils.h>
#include <project/net_settings.h>
#include <project/project_file.h>
#include <settings/settings_manager.h>
#include <wx/string.h>
using namespace kiapi::common::commands;
using namespace kiapi::common::types;
using google::protobuf::Empty;
API_HANDLER_COMMON::API_HANDLER_COMMON() :
API_HANDLER()
{
registerHandler<commands::GetVersion, GetVersionResponse>( &API_HANDLER_COMMON::handleGetVersion );
registerHandler<GetKiCadBinaryPath, PathResponse>(
&API_HANDLER_COMMON::handleGetKiCadBinaryPath );
registerHandler<GetNetClasses, NetClassesResponse>( &API_HANDLER_COMMON::handleGetNetClasses );
registerHandler<SetNetClasses, Empty>( &API_HANDLER_COMMON::handleSetNetClasses );
registerHandler<Ping, Empty>( &API_HANDLER_COMMON::handlePing );
registerHandler<GetTextExtents, types::Box2>( &API_HANDLER_COMMON::handleGetTextExtents );
registerHandler<GetTextAsShapes, GetTextAsShapesResponse>(
&API_HANDLER_COMMON::handleGetTextAsShapes );
registerHandler<ExpandTextVariables, ExpandTextVariablesResponse>(
&API_HANDLER_COMMON::handleExpandTextVariables );
registerHandler<GetPluginSettingsPath, StringResponse>(
&API_HANDLER_COMMON::handleGetPluginSettingsPath );
registerHandler<GetTextVariables, project::TextVariables>(
&API_HANDLER_COMMON::handleGetTextVariables );
registerHandler<SetTextVariables, Empty>(
&API_HANDLER_COMMON::handleSetTextVariables );
}
HANDLER_RESULT<GetVersionResponse> API_HANDLER_COMMON::handleGetVersion(
const HANDLER_CONTEXT<commands::GetVersion>& )
{
GetVersionResponse reply;
reply.mutable_version()->set_full_version( GetBuildVersion().ToStdString() );
std::tuple<int, int, int> version = GetMajorMinorPatchTuple();
reply.mutable_version()->set_major( std::get<0>( version ) );
reply.mutable_version()->set_minor( std::get<1>( version ) );
reply.mutable_version()->set_patch( std::get<2>( version ) );
return reply;
}
HANDLER_RESULT<PathResponse> API_HANDLER_COMMON::handleGetKiCadBinaryPath(
const HANDLER_CONTEXT<GetKiCadBinaryPath>& aCtx )
{
wxFileName fn( wxEmptyString, wxString::FromUTF8( aCtx.Request.binary_name() ) );
#ifdef _WIN32
fn.SetExt( wxT( "exe" ) );
#endif
wxString path = FindKicadFile( fn.GetFullName() );
PathResponse reply;
reply.set_path( path.ToUTF8() );
return reply;
}
HANDLER_RESULT<NetClassesResponse> API_HANDLER_COMMON::handleGetNetClasses(
const HANDLER_CONTEXT<GetNetClasses>& aCtx )
{
NetClassesResponse reply;
std::shared_ptr<NET_SETTINGS>& netSettings =
Pgm().GetSettingsManager().Prj().GetProjectFile().m_NetSettings;
google::protobuf::Any any;
netSettings->GetDefaultNetclass()->Serialize( any );
any.UnpackTo( reply.add_net_classes() );
for( const auto& netClass : netSettings->GetNetclasses() | std::views::values )
{
netClass->Serialize( any );
any.UnpackTo( reply.add_net_classes() );
}
return reply;
}
HANDLER_RESULT<Empty> API_HANDLER_COMMON::handleSetNetClasses(
const HANDLER_CONTEXT<SetNetClasses>& aCtx )
{
std::shared_ptr<NET_SETTINGS>& netSettings =
Pgm().GetSettingsManager().Prj().GetProjectFile().m_NetSettings;
if( aCtx.Request.merge_mode() == MapMergeMode::MMM_REPLACE )
netSettings->ClearNetclasses();
auto netClasses = netSettings->GetNetclasses();
google::protobuf::Any any;
for( const auto& ncProto : aCtx.Request.net_classes() )
{
any.PackFrom( ncProto );
wxString name = wxString::FromUTF8( ncProto.name() );
if( name == wxT( "Default" ) )
{
netSettings->GetDefaultNetclass()->Deserialize( any );
}
else
{
if( !netClasses.contains( name ) )
netClasses.insert( { name, std::make_shared<NETCLASS>( name, false ) } );
netClasses[name]->Deserialize( any );
}
}
netSettings->SetNetclasses( netClasses );
return Empty();
}
HANDLER_RESULT<Empty> API_HANDLER_COMMON::handlePing( const HANDLER_CONTEXT<Ping>& aCtx )
{
return Empty();
}
HANDLER_RESULT<types::Box2> API_HANDLER_COMMON::handleGetTextExtents(
const HANDLER_CONTEXT<GetTextExtents>& aCtx )
{
EDA_TEXT text( pcbIUScale );
google::protobuf::Any any;
any.PackFrom( aCtx.Request.text() );
if( !text.Deserialize( any ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "Could not decode text in GetTextExtents message" );
return tl::unexpected( e );
}
types::Box2 response;
BOX2I bbox = text.GetTextBox( nullptr );
EDA_ANGLE angle = text.GetTextAngle();
if( !angle.IsZero() )
bbox = bbox.GetBoundingBoxRotated( text.GetTextPos(), text.GetTextAngle() );
response.mutable_position()->set_x_nm( bbox.GetPosition().x );
response.mutable_position()->set_y_nm( bbox.GetPosition().y );
response.mutable_size()->set_x_nm( bbox.GetSize().x );
response.mutable_size()->set_y_nm( bbox.GetSize().y );
return response;
}
HANDLER_RESULT<GetTextAsShapesResponse> API_HANDLER_COMMON::handleGetTextAsShapes(
const HANDLER_CONTEXT<GetTextAsShapes>& aCtx )
{
GetTextAsShapesResponse reply;
for( const TextOrTextBox& textMsg : aCtx.Request.text() )
{
Text dummyText;
const Text* textPtr = &textMsg.text();
if( textMsg.has_textbox() )
{
dummyText.set_text( textMsg.textbox().text() );
dummyText.mutable_attributes()->CopyFrom( textMsg.textbox().attributes() );
textPtr = &dummyText;
}
EDA_TEXT text( pcbIUScale );
google::protobuf::Any any;
any.PackFrom( *textPtr );
if( !text.Deserialize( any ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "Could not decode text in GetTextAsShapes message" );
return tl::unexpected( e );
}
std::shared_ptr<SHAPE_COMPOUND> shapes = text.GetEffectiveTextShape( false );
TextWithShapes* entry = reply.add_text_with_shapes();
entry->mutable_text()->CopyFrom( textMsg );
for( SHAPE* subshape : shapes->Shapes() )
{
EDA_SHAPE proxy( *subshape );
proxy.Serialize( any );
GraphicShape* shapeMsg = entry->mutable_shapes()->add_shapes();
any.UnpackTo( shapeMsg );
}
if( textMsg.has_textbox() )
{
GraphicShape* border = entry->mutable_shapes()->add_shapes();
int width = textMsg.textbox().attributes().stroke_width().value_nm();
border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
VECTOR2I tl = UnpackVector2( textMsg.textbox().top_left() );
VECTOR2I br = UnpackVector2( textMsg.textbox().bottom_right() );
// top
PackVector2( *border->mutable_segment()->mutable_start(), tl );
PackVector2( *border->mutable_segment()->mutable_end(), VECTOR2I( br.x, tl.y ) );
// right
border = entry->mutable_shapes()->add_shapes();
border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
PackVector2( *border->mutable_segment()->mutable_start(), VECTOR2I( br.x, tl.y ) );
PackVector2( *border->mutable_segment()->mutable_end(), br );
// bottom
border = entry->mutable_shapes()->add_shapes();
border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
PackVector2( *border->mutable_segment()->mutable_start(), br );
PackVector2( *border->mutable_segment()->mutable_end(), VECTOR2I( tl.x, br.y ) );
// left
border = entry->mutable_shapes()->add_shapes();
border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
PackVector2( *border->mutable_segment()->mutable_start(), VECTOR2I( tl.x, br.y ) );
PackVector2( *border->mutable_segment()->mutable_end(), tl );
}
}
return reply;
}
HANDLER_RESULT<ExpandTextVariablesResponse> API_HANDLER_COMMON::handleExpandTextVariables(
const HANDLER_CONTEXT<ExpandTextVariables>& aCtx )
{
if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_UNHANDLED );
// No error message, this is a flag that the server should try a different handler
return tl::unexpected( e );
}
ExpandTextVariablesResponse reply;
PROJECT& project = Pgm().GetSettingsManager().Prj();
for( const std::string& textMsg : aCtx.Request.text() )
{
wxString result = ExpandTextVars( wxString::FromUTF8( textMsg ), &project );
reply.add_text( result.ToUTF8() );
}
return reply;
}
HANDLER_RESULT<StringResponse> API_HANDLER_COMMON::handleGetPluginSettingsPath(
const HANDLER_CONTEXT<GetPluginSettingsPath>& aCtx )
{
wxString identifier = wxString::FromUTF8( aCtx.Request.identifier() );
if( identifier.IsEmpty() )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "plugin identifier is missing" );
return tl::unexpected( e );
}
if( API_PLUGIN::IsValidIdentifier( identifier ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "plugin identifier is invalid" );
return tl::unexpected( e );
}
wxFileName path( PATHS::GetUserSettingsPath(), wxEmptyString );
path.AppendDir( "plugins" );
// Create the base plugins path if needed, but leave the specific plugin to create its own path
PATHS::EnsurePathExists( path.GetPath() );
path.AppendDir( identifier );
StringResponse reply;
reply.set_response( path.GetPath() );
return reply;
}
HANDLER_RESULT<project::TextVariables> API_HANDLER_COMMON::handleGetTextVariables(
const HANDLER_CONTEXT<GetTextVariables>& aCtx )
{
if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_UNHANDLED );
// No error message, this is a flag that the server should try a different handler
return tl::unexpected( e );
}
const PROJECT& project = Pgm().GetSettingsManager().Prj();
if( project.IsNullProject() )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_NOT_READY );
e.set_error_message( "no valid project is loaded, cannot get text variables" );
return tl::unexpected( e );
}
const std::map<wxString, wxString>& vars = project.GetTextVars();
project::TextVariables reply;
auto map = reply.mutable_variables();
for( const auto& [key, value] : vars )
( *map )[ std::string( key.ToUTF8() ) ] = value.ToUTF8();
return reply;
}
HANDLER_RESULT<Empty> API_HANDLER_COMMON::handleSetTextVariables(
const HANDLER_CONTEXT<SetTextVariables>& aCtx )
{
if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_UNHANDLED );
// No error message, this is a flag that the server should try a different handler
return tl::unexpected( e );
}
PROJECT& project = Pgm().GetSettingsManager().Prj();
if( project.IsNullProject() )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_NOT_READY );
e.set_error_message( "no valid project is loaded, cannot set text variables" );
return tl::unexpected( e );
}
const project::TextVariables& newVars = aCtx.Request.variables();
std::map<wxString, wxString>& vars = project.GetTextVars();
if( aCtx.Request.merge_mode() == MapMergeMode::MMM_REPLACE )
vars.clear();
for( const auto& [key, value] : newVars.variables() )
vars[wxString::FromUTF8( key )] = wxString::FromUTF8( value );
Pgm().GetSettingsManager().SaveProject();
return Empty();
}