/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2023 Jon Evans * Copyright (C) 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 #include #include #include using namespace kiapi::common::commands; using namespace kiapi::common::types; using google::protobuf::Empty; API_HANDLER_COMMON::API_HANDLER_COMMON() : API_HANDLER() { registerHandler( &API_HANDLER_COMMON::handleGetVersion ); registerHandler( &API_HANDLER_COMMON::handleGetNetClasses ); registerHandler( &API_HANDLER_COMMON::handlePing ); registerHandler( &API_HANDLER_COMMON::handleGetTextExtents ); registerHandler( &API_HANDLER_COMMON::handleGetTextAsShapes ); registerHandler( &API_HANDLER_COMMON::handleExpandTextVariables ); registerHandler( &API_HANDLER_COMMON::handleGetPluginSettingsPath ); } HANDLER_RESULT API_HANDLER_COMMON::handleGetVersion( const HANDLER_CONTEXT& ) { GetVersionResponse reply; reply.mutable_version()->set_full_version( GetBuildVersion().ToStdString() ); std::tuple 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 API_HANDLER_COMMON::handleGetNetClasses( const HANDLER_CONTEXT& aCtx ) { NetClassesResponse reply; std::shared_ptr& netSettings = Pgm().GetSettingsManager().Prj().GetProjectFile().m_NetSettings; for( const auto& [name, netClass] : netSettings->GetNetclasses() ) { reply.add_net_classes()->set_name( name.ToStdString() ); } return reply; } HANDLER_RESULT API_HANDLER_COMMON::handlePing( const HANDLER_CONTEXT& aCtx ) { return Empty(); } HANDLER_RESULT API_HANDLER_COMMON::handleGetTextExtents( const HANDLER_CONTEXT& 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(); 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 API_HANDLER_COMMON::handleGetTextAsShapes( const HANDLER_CONTEXT& 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 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 API_HANDLER_COMMON::handleExpandTextVariables( const HANDLER_CONTEXT& 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 API_HANDLER_COMMON::handleGetPluginSettingsPath( const HANDLER_CONTEXT& 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; }