PCM implementation

This commit is contained in:
qu1ck 2020-10-26 03:54:36 -07:00 committed by Seth Hillbrand
parent 4937cd537b
commit 0f7c0e3872
63 changed files with 9429 additions and 110 deletions

View File

@ -146,12 +146,17 @@ option( KICAD_GAL_PROFILE
"Enable profiling info for GAL"
OFF )
option( KICAD_PCM
"Build Plugin and Content Manager"
OFF )
cmake_dependent_option( KICAD_WIN32_INSTALL_PDBS
"Installs debug pdb to the bin folder, DO NOT USE (NOR REQUIRED) FOR DEBUGGING THE SOURCE ON MSVC. This is purely for release building.
MSVC can find the PDBs without this."
OFF "WIN32"
OFF )
# Global setting: exports are explicit
set( CMAKE_CXX_VISIBILITY_PRESET "hidden" )
set( CMAKE_VISIBILITY_INLINES_HIDDEN ON )
@ -881,7 +886,7 @@ set( wxWidgets_REQ_VERSION ${WXPYTHON_WXVERSION} )
# See line 49 of CMakeModules/FindwxWidgets.cmake
set( wxWidgets_CONFIG_OPTIONS ${wxWidgets_CONFIG_OPTIONS} --static=no )
find_package( wxWidgets ${wxWidgets_REQ_VERSION} COMPONENTS gl aui adv html core net base propgrid xml stc REQUIRED )
find_package( wxWidgets ${wxWidgets_REQ_VERSION} COMPONENTS gl aui adv html core net base propgrid xml stc richtext REQUIRED )
# Include wxWidgets macros.
include( ${wxWidgets_USE_FILE} )

View File

@ -36,6 +36,7 @@ string( REGEX MATCH
if( CMAKE_MATCH_COUNT EQUAL 2 )
# Match slot 0 is the full string, so we want slots 1 & 2
set( KICAD_MAJOR_MINOR_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" )
set( KICAD_MAJOR_MINOR_VERSION_TUPLE "{ ${CMAKE_MATCH_1}, ${CMAKE_MATCH_2} }" )
set( KICAD_MAJOR_VERSION "${CMAKE_MATCH_1}" )
set( KICAD_WIN32_RC_FILEVER_STR "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.0.0\\0" )
set( KICAD_WIN32_RC_FILEVER "${CMAKE_MATCH_1}, ${CMAKE_MATCH_2}, 0, 0" )
@ -54,14 +55,15 @@ set( _wvh_new_version_text
#ifndef __KICAD_VERSION_H__
#define __KICAD_VERSION_H__
#define KICAD_VERSION_FULL \"${KICAD_VERSION_FULL}\"
#define KICAD_SEMANTIC_VERSION \"${KICAD_SEMANTIC_VERSION}\"
#define KICAD_MAJOR_VERSION \"${KICAD_MAJOR_VERSION}\"
#define KICAD_MAJOR_MINOR_VERSION \"${KICAD_MAJOR_MINOR_VERSION}\"
#define KICAD_WIN32_RC_PRODVER ${KICAD_WIN32_RC_PRODVER}
#define KICAD_WIN32_RC_PRODVER_STR \"${KICAD_WIN32_RC_PRODVER_STR}\"
#define KICAD_WIN32_RC_FILEVER ${KICAD_WIN32_RC_FILEVER}
#define KICAD_WIN32_RC_FILEVER_STR \"${KICAD_WIN32_RC_FILEVER_STR}\"
#define KICAD_VERSION_FULL \"${KICAD_VERSION_FULL}\"
#define KICAD_SEMANTIC_VERSION \"${KICAD_SEMANTIC_VERSION}\"
#define KICAD_MAJOR_VERSION \"${KICAD_MAJOR_VERSION}\"
#define KICAD_MAJOR_MINOR_VERSION \"${KICAD_MAJOR_MINOR_VERSION}\"
#define KICAD_MAJOR_MINOR_VERSION_TUPLE ${KICAD_MAJOR_MINOR_VERSION_TUPLE}
#define KICAD_WIN32_RC_PRODVER ${KICAD_WIN32_RC_PRODVER}
#define KICAD_WIN32_RC_PRODVER_STR \"${KICAD_WIN32_RC_PRODVER_STR}\"
#define KICAD_WIN32_RC_FILEVER ${KICAD_WIN32_RC_FILEVER}
#define KICAD_WIN32_RC_FILEVER_STR \"${KICAD_WIN32_RC_FILEVER_STR}\"
#endif /* __KICAD_VERSION_H__ */
" )

View File

@ -280,6 +280,7 @@ set( BMAPS_MID
icon_cvpcb_24
icon_footprint_browser
icon_gerbview_24
icon_pcm_24
import3d
image
import_brd_file
@ -529,6 +530,7 @@ set( BMAPS_BIG
icon_bitmap2component
icon_pagelayout_editor
icon_pcbcalculator
icon_pcm
reannotate_down_left
reannotate_down_right
reannotate_left_down

View File

@ -0,0 +1,71 @@
/* Do not modify this file, it was automatically generated by the
* PNG2cpp CMake script, using a *.png file as input.
*/
#include <bitmaps_png/bitmaps_list.h>
static const unsigned char png[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x08, 0x06, 0x00, 0x00, 0x00, 0xe0, 0x77, 0x3d,
0xf8, 0x00, 0x00, 0x03, 0x64, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x95, 0x5b, 0x68, 0x13,
0x69, 0x18, 0x86, 0x83, 0x87, 0x8b, 0x15, 0xc5, 0x3b, 0xbd, 0xd8, 0x2b, 0xf1, 0x42, 0x30, 0x21,
0xad, 0x8d, 0xa6, 0x93, 0x49, 0x67, 0xfe, 0xff, 0x9f, 0xcc, 0x24, 0x99, 0x26, 0x69, 0x26, 0x87,
0x31, 0xa1, 0xa7, 0xb8, 0x45, 0xc4, 0x43, 0x15, 0x8a, 0xf5, 0x5c, 0xb4, 0xa5, 0xe0, 0xc6, 0x43,
0xab, 0xa2, 0x6e, 0xd4, 0x56, 0x42, 0x77, 0xdb, 0x0a, 0x5a, 0x5b, 0x0f, 0xc8, 0xba, 0x74, 0xad,
0x4a, 0xf1, 0x78, 0x51, 0xa5, 0x55, 0x44, 0xf0, 0x6c, 0x35, 0xb5, 0x9a, 0x63, 0x4d, 0x4a, 0x8d,
0x9b, 0xdf, 0xa4, 0x50, 0x4d, 0x51, 0xb4, 0x6b, 0x7b, 0xa7, 0x1f, 0x3c, 0x17, 0x03, 0xf3, 0xbd,
0x0f, 0x7c, 0x33, 0xf0, 0x4a, 0x24, 0x63, 0x98, 0x8c, 0x0c, 0xd5, 0xaf, 0x40, 0x0d, 0xaa, 0x58,
0x80, 0x1e, 0x24, 0x79, 0x44, 0x93, 0xd0, 0x2d, 0x97, 0xd3, 0x73, 0x24, 0xe3, 0x19, 0x95, 0x4a,
0xf5, 0x0b, 0xa1, 0xd4, 0xb8, 0x54, 0xd9, 0xf0, 0xb1, 0xcd, 0xa0, 0x79, 0xd3, 0x5c, 0x6b, 0x7d,
0xdf, 0x77, 0xb9, 0x10, 0xbf, 0xea, 0x2c, 0xc2, 0x67, 0x0f, 0xd9, 0x12, 0xcb, 0x0a, 0x74, 0x3e,
0x94, 0x83, 0xee, 0x93, 0x04, 0xdc, 0x20, 0x93, 0x65, 0xcf, 0x1e, 0x53, 0xa8, 0x28, 0x8a, 0x93,
0xb3, 0xb2, 0xa0, 0x5e, 0x4d, 0x72, 0x5d, 0xa6, 0x5c, 0xa1, 0xaf, 0x6e, 0x6f, 0xc5, 0x60, 0x73,
0x5d, 0xf5, 0x3b, 0x97, 0xd3, 0xe1, 0xcf, 0xb7, 0x99, 0x02, 0xc7, 0x6a, 0xac, 0x71, 0xdf, 0xa5,
0x22, 0x1c, 0xbc, 0x56, 0x3c, 0x4c, 0x6f, 0x47, 0x21, 0x6e, 0xda, 0x2d, 0xc4, 0x0b, 0x2c, 0xac,
0x8f, 0x83, 0x4c, 0x37, 0xa1, 0x84, 0x4b, 0xe7, 0xcd, 0x53, 0xcf, 0xf8, 0x2c, 0x78, 0xfe, 0x7c,
0xb5, 0x94, 0x83, 0xa8, 0x5e, 0xcf, 0x30, 0x4f, 0xb6, 0x97, 0x1b, 0x03, 0x55, 0x6b, 0x8c, 0x51,
0x33, 0xaf, 0x0d, 0x97, 0x97, 0xba, 0x82, 0xd7, 0xce, 0x1f, 0xc0, 0xb1, 0x67, 0xad, 0xb8, 0xb7,
0xbb, 0x09, 0x1f, 0xde, 0xb3, 0x29, 0x66, 0x31, 0x99, 0xc3, 0xa5, 0x25, 0xe6, 0xc0, 0xbf, 0xde,
0xc5, 0x09, 0xff, 0x95, 0x4f, 0xb2, 0x27, 0xed, 0x85, 0xd8, 0xfb, 0xbb, 0x10, 0xb3, 0xf0, 0xec,
0x4b, 0x0e, 0xc2, 0x4e, 0x45, 0x26, 0x25, 0x28, 0x14, 0x8a, 0xa9, 0x92, 0xcc, 0x0c, 0x2a, 0x64,
0xe1, 0x99, 0xc8, 0xb9, 0x43, 0xf6, 0x8f, 0x2f, 0x8f, 0xd0, 0xd5, 0xe2, 0xc4, 0x5b, 0xd7, 0x98,
0x5e, 0x1b, 0xb4, 0xfa, 0xc8, 0xf6, 0x8a, 0xd2, 0xe8, 0xfd, 0xeb, 0x5e, 0x3c, 0xf8, 0xbc, 0x0d,
0xdf, 0xbd, 0x52, 0x8f, 0x77, 0x56, 0x96, 0x05, 0x05, 0xa3, 0x31, 0x5c, 0xb1, 0x5a, 0x18, 0xb8,
0x79, 0xdc, 0x31, 0x6a, 0xef, 0x7c, 0x9d, 0x88, 0x45, 0x93, 0x66, 0x20, 0x99, 0x1d, 0x93, 0xe8,
0x10, 0xe8, 0xf3, 0x56, 0xa2, 0x77, 0xbf, 0xd9, 0xe0, 0x1b, 0xb3, 0x0e, 0x85, 0x3d, 0x55, 0xe6,
0xa1, 0x87, 0xff, 0x14, 0x8c, 0x5a, 0xe8, 0x4f, 0xde, 0xfd, 0x8c, 0xc7, 0xfe, 0x5f, 0x89, 0x43,
0x1f, 0x72, 0x08, 0xa6, 0x70, 0xa3, 0x67, 0x5b, 0xbc, 0xff, 0xde, 0x31, 0x1c, 0x7d, 0xda, 0x8a,
0x3b, 0xda, 0x6a, 0x13, 0x2b, 0x97, 0x16, 0x07, 0xf2, 0x78, 0x3e, 0xb2, 0xca, 0x95, 0x1b, 0x15,
0x4d, 0x6c, 0xb0, 0xc4, 0xc1, 0x05, 0x5b, 0xf7, 0x9b, 0x12, 0x46, 0x2d, 0xf2, 0x49, 0x78, 0x04,
0x7c, 0x3d, 0xcd, 0x2c, 0x4e, 0x71, 0xc3, 0xcb, 0xe2, 0x03, 0x1b, 0x99, 0x21, 0xa7, 0x11, 0xf8,
0xad, 0xb9, 0x4c, 0xa4, 0xc1, 0x2d, 0xc4, 0x53, 0x77, 0x4e, 0x97, 0xa5, 0x4e, 0x71, 0xa4, 0xda,
0x32, 0x68, 0x33, 0x68, 0xc3, 0x25, 0xf9, 0xf6, 0xd0, 0x8e, 0xca, 0xb2, 0xf8, 0x92, 0xc2, 0x82,
0xa0, 0xd3, 0x2e, 0x86, 0x38, 0xa4, 0x8f, 0x3e, 0xfc, 0xdb, 0x8e, 0x7d, 0x17, 0xc5, 0x61, 0x3e,
0x13, 0xa4, 0x73, 0xf5, 0x28, 0x8b, 0xf7, 0xac, 0x43, 0x51, 0x41, 0x07, 0xc2, 0xc5, 0x56, 0x4d,
0xa4, 0xb9, 0xc6, 0x92, 0x48, 0xff, 0x8b, 0x56, 0x14, 0xe9, 0xfd, 0x06, 0x16, 0x0d, 0x90, 0x04,
0x3b, 0xf4, 0xa2, 0xa7, 0x71, 0xf8, 0x74, 0xf9, 0x76, 0x8b, 0x7f, 0x24, 0xfc, 0x9b, 0x82, 0x74,
0x2e, 0xfc, 0xa1, 0xc1, 0x95, 0xcb, 0x61, 0x84, 0x03, 0xe0, 0xad, 0x16, 0xa1, 0xb7, 0xee, 0x72,
0xfd, 0xc0, 0xad, 0x16, 0xeb, 0x70, 0x48, 0x1e, 0x6f, 0x88, 0xa4, 0xc2, 0xc7, 0x25, 0x18, 0xe1,
0xe0, 0x26, 0x84, 0x1b, 0xdc, 0x46, 0x9c, 0x1e, 0x92, 0x12, 0xa4, 0xbe, 0xc5, 0xa5, 0x33, 0xfb,
0x12, 0x56, 0x43, 0x6e, 0x68, 0xc2, 0x05, 0x1a, 0xc0, 0xc5, 0xec, 0xe6, 0xbc, 0xa0, 0x7b, 0xbd,
0x39, 0xd4, 0xd3, 0x66, 0xc3, 0x13, 0x2e, 0xb8, 0x7d, 0xd2, 0x3a, 0xea, 0x79, 0xc2, 0x05, 0x5f,
0xe3, 0xa7, 0xe0, 0x47, 0x11, 0xa8, 0xb3, 0xa9, 0xc0, 0xd9, 0x5a, 0xcd, 0x84, 0x0b, 0x3a, 0xff,
0x12, 0xb0, 0x3a, 0x1b, 0x04, 0x25, 0x72, 0x39, 0x95, 0x45, 0x93, 0xf4, 0x69, 0x3d, 0xa2, 0x5e,
0x78, 0x36, 0x33, 0x43, 0x5d, 0x7f, 0x7e, 0xbf, 0xe0, 0x59, 0xbb, 0x88, 0x1b, 0x77, 0x1a, 0xe3,
0x79, 0x3a, 0xd4, 0xaf, 0x85, 0xb0, 0x5d, 0x2a, 0xa5, 0x94, 0x1f, 0x4b, 0x47, 0xa1, 0x60, 0x67,
0xe6, 0x10, 0xf4, 0x16, 0x40, 0xd2, 0x4f, 0xd7, 0x2e, 0x41, 0xe1, 0x0e, 0x8f, 0x66, 0xcc, 0x82,
0x3b, 0xa7, 0x6c, 0x78, 0xd7, 0x06, 0x3e, 0xc2, 0xd2, 0xe0, 0x25, 0x45, 0x82, 0x1a, 0xb9, 0x9c,
0x9c, 0xf5, 0xb5, 0xe6, 0x9c, 0x94, 0x99, 0x09, 0x38, 0xa8, 0xa6, 0x2e, 0x0a, 0x3a, 0xba, 0xbf,
0xa1, 0x0a, 0x25, 0xba, 0x9b, 0xbe, 0x2c, 0x68, 0xaf, 0x37, 0x63, 0x97, 0xc8, 0xbe, 0x06, 0x6a,
0x78, 0x67, 0xe1, 0x02, 0xca, 0x09, 0x21, 0x9c, 0xf2, 0xbf, 0x8a, 0x5f, 0x26, 0x23, 0xe7, 0xd2,
0x2a, 0x70, 0x98, 0xc9, 0xa1, 0x7b, 0xab, 0x4b, 0x61, 0xac, 0xb6, 0x1c, 0xe1, 0xba, 0x6a, 0x43,
0xb2, 0x0f, 0x0c, 0x83, 0x3c, 0x83, 0xfa, 0x40, 0x0e, 0x3c, 0x21, 0x97, 0x43, 0x99, 0x64, 0xbc,
0x93, 0xec, 0xd6, 0x69, 0x84, 0x82, 0x5a, 0x4d, 0x2c, 0xa2, 0x7d, 0x84, 0x92, 0x7e, 0x45, 0x28,
0xa9, 0x32, 0xa9, 0x14, 0x4e, 0x1f, 0xcb, 0xee, 0x07, 0xd5, 0xc3, 0xc2, 0xa5, 0x0e, 0x15, 0xa0,
0xc9, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
};
const BITMAP_OPAQUE pcm_xpm[1] = {{ png, sizeof( png ), "pcm_xpm" }};
//EOF

View File

@ -0,0 +1,128 @@
/* Do not modify this file, it was automatically generated by the
* PNG2cpp CMake script, using a *.png file as input.
*/
#include <bitmaps_png/bitmaps_list.h>
static const unsigned char png[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x08, 0x06, 0x00, 0x00, 0x00, 0x57, 0x02, 0xf9,
0x87, 0x00, 0x00, 0x06, 0xec, 0x49, 0x44, 0x41, 0x54, 0x68, 0xde, 0xed, 0x9a, 0x69, 0x70, 0x53,
0x55, 0x14, 0xc7, 0x2b, 0x8e, 0x3a, 0x7e, 0x71, 0xfb, 0xa0, 0x7e, 0xd0, 0xf1, 0x03, 0xf8, 0xa1,
0x24, 0x79, 0x69, 0x9b, 0x36, 0xa5, 0x21, 0xef, 0xbd, 0xbc, 0xbc, 0xa4, 0x4d, 0xba, 0x84, 0xb4,
0x4d, 0xe8, 0x5e, 0x84, 0x6a, 0x5b, 0x16, 0x29, 0xd0, 0x82, 0x2c, 0x2a, 0xbb, 0x50, 0x06, 0x11,
0x94, 0x8e, 0x2c, 0x45, 0xa0, 0x2c, 0x05, 0xca, 0x22, 0x9b, 0x45, 0xd9, 0x8b, 0x05, 0x1d, 0x28,
0x02, 0x0a, 0xc3, 0xbe, 0x88, 0x88, 0x88, 0x80, 0x83, 0xb2, 0x68, 0xfb, 0xde, 0xf1, 0x9e, 0xfb,
0xf2, 0x42, 0x87, 0xd2, 0x05, 0xa1, 0x35, 0x32, 0xc9, 0xcc, 0x99, 0xe4, 0x4d, 0x72, 0x73, 0xff,
0xbf, 0x7b, 0xcf, 0x39, 0xf7, 0xe5, 0x9c, 0x84, 0x84, 0xb4, 0xc3, 0x43, 0xab, 0xe5, 0x5e, 0xd5,
0xe9, 0xcc, 0xef, 0x84, 0xe9, 0xd9, 0xc3, 0x7a, 0x3d, 0x7b, 0x9c, 0x5c, 0x8f, 0x09, 0x0d, 0xe5,
0x5e, 0x0f, 0x09, 0xe4, 0x47, 0x97, 0x2e, 0xd1, 0xcf, 0x10, 0xd1, 0xbd, 0x19, 0x1d, 0xbb, 0x43,
0xa7, 0x65, 0x65, 0x9d, 0x8e, 0x95, 0x33, 0xdd, 0x36, 0x39, 0x2b, 0xc5, 0x4e, 0x5e, 0x73, 0xa0,
0xd5, 0xb2, 0x10, 0xc6, 0xb0, 0xfb, 0x08, 0x4c, 0x91, 0x46, 0x63, 0x79, 0x39, 0x20, 0x44, 0x7b,
0xbd, 0xde, 0xc7, 0x89, 0x68, 0x1b, 0x11, 0x57, 0xc1, 0x30, 0xec, 0x4d, 0x14, 0xe9, 0x14, 0xad,
0x0d, 0x1f, 0x8e, 0x70, 0xc1, 0x0f, 0xeb, 0x32, 0xe0, 0xda, 0x9e, 0x5c, 0x6a, 0x27, 0xaa, 0x33,
0x61, 0xce, 0x78, 0x37, 0xa4, 0xbb, 0xed, 0x12, 0x7e, 0x06, 0xe1, 0x88, 0xd5, 0x92, 0xb1, 0xf9,
0x08, 0xde, 0xe1, 0xc2, 0xbb, 0x76, 0xed, 0xae, 0xd1, 0x68, 0xb8, 0xc9, 0x44, 0xf4, 0x15, 0x14,
0x44, 0xdc, 0x44, 0xce, 0x49, 0x8d, 0x85, 0x2f, 0xcb, 0xbd, 0x7e, 0xd1, 0xcd, 0xd9, 0x81, 0xd5,
0x19, 0x80, 0x80, 0x71, 0x56, 0x6b, 0x03, 0x8e, 0x25, 0x3b, 0xf6, 0x17, 0x79, 0x5e, 0x4f, 0x60,
0xbc, 0xe4, 0x4b, 0x9f, 0x6c, 0x37, 0xd1, 0xaa, 0x5f, 0xeb, 0x74, 0xfc, 0x09, 0xea, 0x0e, 0x61,
0x82, 0x54, 0x54, 0x98, 0x07, 0x25, 0x6f, 0xe7, 0x83, 0x28, 0xc4, 0x53, 0x31, 0x11, 0xe1, 0xbc,
0x3c, 0x38, 0xcf, 0x09, 0x9b, 0x08, 0xc8, 0x95, 0xdd, 0xb9, 0xad, 0xc2, 0x7c, 0xb3, 0x2c, 0x0d,
0xc6, 0x14, 0x25, 0x02, 0x6b, 0xb2, 0xa8, 0x30, 0xd7, 0x71, 0x37, 0x71, 0x57, 0xc9, 0x94, 0x9d,
0x1e, 0x58, 0x74, 0x58, 0x98, 0xe5, 0x39, 0x8d, 0xc6, 0x9c, 0xcb, 0x30, 0xdc, 0x4e, 0x75, 0xeb,
0x3d, 0x6e, 0x8f, 0xbc, 0x74, 0xce, 0x58, 0xb8, 0x72, 0x7c, 0x05, 0xdc, 0x3a, 0xbf, 0x86, 0xda,
0xcd, 0x1f, 0x57, 0xc3, 0xee, 0xea, 0x4f, 0x60, 0x54, 0x71, 0x5f, 0x30, 0x46, 0x29, 0x2b, 0xcb,
0x75, 0x17, 0xa4, 0x89, 0xc5, 0x89, 0x50, 0xb7, 0x32, 0xbd, 0x55, 0x10, 0x84, 0xdd, 0x59, 0xd1,
0x13, 0x46, 0xf6, 0x4b, 0x80, 0xc8, 0x08, 0x8e, 0xba, 0x99, 0x9e, 0x61, 0x2f, 0x92, 0x45, 0x9b,
0x41, 0xe6, 0x37, 0xdf, 0x67, 0x30, 0x3a, 0x9f, 0x22, 0x83, 0x92, 0x88, 0xd8, 0x45, 0x24, 0xf8,
0x6e, 0xe3, 0x97, 0x39, 0x88, 0x5f, 0xc7, 0x0a, 0x02, 0xfa, 0x2d, 0x42, 0x40, 0x56, 0xcf, 0x34,
0xb9, 0x72, 0xee, 0x38, 0xb8, 0x7c, 0x6c, 0xb9, 0x1f, 0x42, 0xb5, 0xdf, 0x4f, 0x55, 0xc1, 0xda,
0xc5, 0x93, 0x21, 0xff, 0x8d, 0x1c, 0x0c, 0x5e, 0x19, 0xc7, 0xbb, 0x9d, 0x76, 0x69, 0xd6, 0xb8,
0x1e, 0x70, 0x6a, 0x53, 0x56, 0xab, 0x30, 0xbf, 0xd4, 0xe4, 0xc0, 0x86, 0x59, 0x1e, 0x18, 0xd0,
0xcb, 0x21, 0xeb, 0xf5, 0xca, 0x78, 0xe2, 0xa2, 0x47, 0x31, 0x93, 0x69, 0xb5, 0xa6, 0xce, 0xcd,
0x0a, 0x27, 0xdb, 0x66, 0x40, 0x62, 0xe2, 0xd7, 0x57, 0x71, 0x90, 0xc5, 0x6c, 0xa9, 0xc7, 0xed,
0xdd, 0xbb, 0xe2, 0xce, 0x0a, 0x1e, 0xdb, 0x98, 0x09, 0x33, 0x47, 0xbb, 0x80, 0xc0, 0xd0, 0x55,
0x0e, 0x0f, 0xe3, 0xe5, 0xbe, 0x79, 0xbd, 0x60, 0xc3, 0xd2, 0x52, 0xf8, 0xe3, 0xec, 0xaa, 0x26,
0x30, 0x97, 0x8e, 0x54, 0x02, 0xee, 0x56, 0x86, 0xb7, 0xa7, 0xcf, 0x45, 0x38, 0xe8, 0xe5, 0x8d,
0x85, 0xca, 0x69, 0xc9, 0x70, 0x71, 0x47, 0x4e, 0xab, 0x30, 0xe7, 0xb7, 0x65, 0xd3, 0xcf, 0x92,
0x31, 0x92, 0xba, 0x18, 0xc4, 0x1b, 0xf6, 0x63, 0x26, 0x63, 0x18, 0xd3, 0x8b, 0x3e, 0xdf, 0xc6,
0x00, 0x62, 0xaf, 0xe1, 0x9b, 0xdd, 0xa2, 0xf8, 0x86, 0x51, 0xfd, 0xe2, 0xe9, 0x76, 0x5e, 0xdd,
0xdd, 0x36, 0xff, 0x35, 0x45, 0x2b, 0xfe, 0x1b, 0xd3, 0xcd, 0x26, 0xbf, 0x37, 0xb4, 0x3f, 0xec,
0xd9, 0x34, 0xb3, 0x09, 0x08, 0xda, 0xe1, 0xda, 0x72, 0x98, 0x31, 0x69, 0x98, 0x3f, 0x5e, 0x8c,
0x91, 0xbc, 0x54, 0xfc, 0x96, 0x13, 0x36, 0x7f, 0xe6, 0x6d, 0x75, 0x2e, 0xb4, 0xa3, 0xbe, 0xc5,
0x73, 0xc5, 0x89, 0x74, 0x3c, 0x31, 0xe2, 0x6a, 0xe6, 0xb3, 0x21, 0xbe, 0x0b, 0xc0, 0x5c, 0x8d,
0x39, 0x7b, 0x61, 0x69, 0x32, 0x9c, 0xdb, 0xd2, 0xfa, 0x56, 0xab, 0x76, 0x69, 0x97, 0xba, 0xe5,
0x4e, 0x49, 0xcf, 0x28, 0xab, 0x94, 0xe8, 0x74, 0xc9, 0x28, 0xf6, 0x74, 0xdd, 0xc2, 0x26, 0x20,
0x37, 0xce, 0xad, 0xa6, 0x90, 0x08, 0x6b, 0x88, 0x10, 0xa8, 0xbf, 0xf3, 0x24, 0x5e, 0x26, 0x95,
0x24, 0xc1, 0xa1, 0xcf, 0x33, 0x9a, 0x9d, 0x07, 0xdf, 0x9b, 0x36, 0x32, 0x09, 0x1c, 0x56, 0xab,
0xa4, 0xea, 0xc5, 0x67, 0x0a, 0x30, 0xa4, 0x97, 0x05, 0x46, 0xf7, 0x15, 0x40, 0x34, 0x73, 0xf5,
0x6a, 0xb0, 0xaa, 0x5b, 0xfd, 0x73, 0x1b, 0xb6, 0x5a, 0x35, 0x04, 0x5f, 0x58, 0xea, 0x86, 0x34,
0x97, 0xdd, 0xef, 0x32, 0x24, 0x5e, 0xe0, 0xee, 0xa0, 0x57, 0xed, 0xf2, 0xb1, 0x65, 0xb0, 0x72,
0xfe, 0x04, 0xe8, 0x9d, 0x9d, 0xe5, 0x3f, 0xec, 0x52, 0x12, 0xec, 0x12, 0x9e, 0x19, 0x67, 0x36,
0x67, 0xf9, 0xbf, 0x0f, 0xcf, 0x0f, 0x8c, 0x3d, 0x25, 0x9e, 0x44, 0xa9, 0x6c, 0x74, 0x02, 0x8c,
0x2d, 0x72, 0xdc, 0x01, 0x40, 0xf1, 0xdf, 0x2f, 0xb5, 0x51, 0xfb, 0x62, 0x86, 0x48, 0xaf, 0x79,
0x93, 0x02, 0x13, 0x1e, 0xc6, 0xc9, 0xf9, 0x99, 0x71, 0xb0, 0x7a, 0x66, 0x2a, 0xfc, 0xba, 0xab,
0xed, 0x30, 0xfb, 0xaa, 0xd2, 0x61, 0xc2, 0x90, 0x04, 0x7f, 0x8a, 0x8c, 0x34, 0x08, 0xf2, 0xf0,
0xc1, 0x05, 0x50, 0xb3, 0xfe, 0x63, 0xba, 0x0b, 0x77, 0xc3, 0x9c, 0xde, 0xb7, 0x10, 0x66, 0x4e,
0x19, 0x0e, 0x0e, 0x7b, 0x92, 0x7a, 0xd8, 0xd1, 0x85, 0xc4, 0xd7, 0xf1, 0x36, 0x41, 0x2a, 0x1d,
0xea, 0x84, 0xba, 0x2a, 0x12, 0x3b, 0xdb, 0xbd, 0xd4, 0xf0, 0xfa, 0x9e, 0x00, 0xaa, 0x1d, 0x5c,
0x22, 0x42, 0x55, 0xa9, 0x15, 0x4a, 0x7a, 0x5b, 0x20, 0xc6, 0xc8, 0x51, 0x11, 0xd1, 0x91, 0x1c,
0xf5, 0x5b, 0x74, 0x99, 0xb6, 0xe4, 0x79, 0x35, 0x45, 0x6e, 0x9a, 0xeb, 0x81, 0xa2, 0xde, 0x0e,
0x99, 0x2c, 0x06, 0x15, 0x67, 0xe5, 0x9d, 0xf2, 0xd4, 0xf1, 0xc5, 0x70, 0xa4, 0x76, 0x9e, 0xdf,
0xad, 0x76, 0xae, 0x9b, 0x0e, 0xc3, 0x8a, 0x0a, 0x08, 0xa8, 0xe2, 0x22, 0x24, 0x03, 0x61, 0x22,
0x81, 0xcd, 0xe5, 0x3d, 0xfc, 0xa2, 0x1b, 0x5b, 0xab, 0x00, 0x8d, 0xed, 0xd0, 0x12, 0x1b, 0x54,
0x7e, 0x60, 0x85, 0xc2, 0x74, 0x5e, 0x36, 0x84, 0xb3, 0x74, 0x02, 0xce, 0x24, 0x48, 0x18, 0xc4,
0x6d, 0x09, 0x78, 0xd5, 0x7e, 0x22, 0x59, 0xa5, 0x62, 0x4a, 0x32, 0x64, 0x34, 0x72, 0x09, 0xce,
0x1c, 0x0b, 0x46, 0xa3, 0x48, 0x57, 0xda, 0x18, 0x25, 0x4a, 0x08, 0x81, 0x30, 0x03, 0x0b, 0xfa,
0x40, 0x56, 0xb2, 0x78, 0x4f, 0xf1, 0xf7, 0x0d, 0xd0, 0xd8, 0xea, 0x2a, 0x6c, 0xb0, 0x60, 0xac,
0x00, 0x79, 0x1e, 0x5e, 0x26, 0x87, 0x0d, 0x9d, 0x58, 0xe4, 0x94, 0x20, 0x3c, 0xd8, 0x42, 0x10,
0xaa, 0xf6, 0xdd, 0x2a, 0xe5, 0x56, 0x42, 0x64, 0x95, 0x00, 0xa6, 0x3b, 0x6b, 0x14, 0x69, 0x2a,
0xbe, 0x7e, 0x66, 0xa5, 0xdf, 0xa5, 0x06, 0x16, 0xb6, 0x13, 0x40, 0x63, 0xfb, 0x76, 0xbe, 0x0d,
0xca, 0x46, 0x08, 0x90, 0xe3, 0xe6, 0x25, 0xc6, 0xe7, 0xaf, 0x89, 0x76, 0x51, 0x42, 0x81, 0x47,
0xd6, 0x67, 0xb6, 0x70, 0x33, 0xc7, 0x41, 0x5a, 0x92, 0x55, 0x9e, 0x37, 0x31, 0x01, 0xd2, 0x7b,
0x88, 0xe0, 0x4e, 0x4c, 0x6e, 0x12, 0x13, 0x1d, 0x02, 0xd0, 0xd8, 0x6a, 0xe6, 0x88, 0x30, 0x65,
0xb0, 0x00, 0x3d, 0x62, 0xf9, 0x06, 0x55, 0x24, 0xc9, 0x18, 0x72, 0xb2, 0x53, 0xa4, 0xd9, 0x48,
0xc9, 0x30, 0xd6, 0x06, 0xcc, 0x20, 0x87, 0xd7, 0xa6, 0xfa, 0x85, 0xa0, 0xc8, 0x80, 0x00, 0x68,
0x6c, 0x5b, 0xca, 0x44, 0x18, 0xd7, 0xdf, 0x42, 0x6e, 0xee, 0x58, 0x39, 0x22, 0x9c, 0x93, 0x71,
0xb2, 0xbd, 0xcb, 0x53, 0xee, 0x29, 0x24, 0x20, 0x01, 0x54, 0xeb, 0x99, 0xc0, 0xb7, 0x28, 0xe2,
0x91, 0x03, 0xb8, 0x76, 0x72, 0x05, 0xac, 0xa9, 0x98, 0x04, 0x79, 0xb9, 0xd9, 0xf4, 0x46, 0xb0,
0x20, 0xcb, 0xfe, 0xff, 0x00, 0x10, 0xf8, 0x78, 0x18, 0x3e, 0xa8, 0x10, 0xa2, 0x22, 0x45, 0x1a,
0xe4, 0x9e, 0x04, 0xf1, 0xef, 0xf2, 0x09, 0x89, 0x70, 0xb2, 0x3a, 0x35, 0xf0, 0x01, 0x72, 0x52,
0x44, 0xf0, 0xfd, 0x7e, 0xa8, 0x9f, 0x38, 0xc4, 0x41, 0x62, 0x25, 0xb9, 0xc5, 0xcf, 0x07, 0x1c,
0x00, 0x9e, 0xb4, 0xeb, 0xca, 0x5c, 0x70, 0x61, 0x9b, 0xb7, 0x4d, 0xc2, 0x03, 0x0e, 0xe0, 0xdf,
0x5a, 0x10, 0x20, 0x08, 0x10, 0x04, 0x08, 0x02, 0x04, 0x01, 0x82, 0x00, 0x41, 0x80, 0x20, 0x40,
0x10, 0xe0, 0x11, 0x01, 0xb8, 0x15, 0xae, 0x67, 0xa5, 0xa2, 0x1c, 0x0b, 0x6c, 0xf8, 0xc8, 0x1a,
0xf0, 0x00, 0x5f, 0x2f, 0x76, 0xc3, 0xb0, 0x82, 0x38, 0xf0, 0x15, 0xc9, 0x1a, 0x10, 0x80, 0xd1,
0x6a, 0xb9, 0x79, 0xe4, 0x99, 0xf6, 0x02, 0x52, 0x9d, 0xbc, 0x34, 0xfb, 0x5d, 0x2b, 0x1c, 0x58,
0x1c, 0x38, 0x00, 0xe7, 0xb7, 0x78, 0x60, 0xe9, 0xd4, 0x24, 0x48, 0x73, 0x29, 0x05, 0x30, 0xf2,
0x73, 0xb3, 0x5e, 0xa3, 0x31, 0x2f, 0xd1, 0x68, 0x58, 0xa3, 0xbf, 0x3f, 0x60, 0x30, 0xd8, 0x9e,
0xc5, 0xa6, 0x9b, 0x9e, 0xc1, 0xb6, 0x28, 0x0b, 0x26, 0x23, 0xdb, 0x30, 0xe2, 0x4d, 0x01, 0xb6,
0x7f, 0x2a, 0xfe, 0x67, 0x00, 0x58, 0x7e, 0x99, 0x3e, 0xca, 0x89, 0x25, 0xc6, 0x06, 0x5f, 0xd7,
0xe6, 0x92, 0xd2, 0x9b, 0xb3, 0xbc, 0xd2, 0x52, 0x93, 0xa6, 0x13, 0xf6, 0xa8, 0x74, 0x1a, 0xb6,
0x4a, 0xab, 0x35, 0x4b, 0xd8, 0x32, 0xcd, 0x75, 0xf3, 0xb4, 0xb4, 0x88, 0x25, 0xc6, 0x8e, 0x00,
0xa8, 0xa9, 0x70, 0xc3, 0xa0, 0x3e, 0xb1, 0xa0, 0x56, 0xfe, 0x74, 0x3a, 0xb6, 0x0e, 0x5b, 0x5d,
0x06, 0x83, 0xe1, 0x89, 0xfb, 0x6c, 0xec, 0x99, 0x3a, 0xfb, 0xba, 0x91, 0xb4, 0x01, 0x62, 0x63,
0x39, 0x09, 0x8b, 0x57, 0x7b, 0x17, 0x3c, 0x7c, 0x80, 0x73, 0x5f, 0x79, 0xa0, 0xa2, 0x34, 0x11,
0x5c, 0x71, 0x4a, 0x71, 0x97, 0x61, 0xb8, 0x3f, 0xc9, 0xdc, 0xb3, 0x89, 0xbb, 0xe8, 0x1e, 0x42,
0x13, 0x9b, 0xf6, 0xcd, 0x72, 0xf5, 0x7a, 0xf6, 0x00, 0x2d, 0x95, 0x47, 0xb0, 0xd2, 0xc0, 0x6c,
0x0b, 0x2d, 0xc5, 0x3f, 0x28, 0xc0, 0xbe, 0x15, 0x29, 0x30, 0x7e, 0x90, 0x13, 0xba, 0x19, 0x95,
0x8a, 0x1e, 0x59, 0xac, 0x53, 0x4a, 0x37, 0xd4, 0xfc, 0x7c, 0xbb, 0xb4, 0x5b, 0xb1, 0x97, 0x46,
0x57, 0x46, 0xab, 0x34, 0x00, 0x53, 0x1c, 0x9c, 0x5c, 0xfe, 0xbe, 0x00, 0x07, 0x17, 0xb7, 0x1d,
0xe0, 0xc2, 0x56, 0x0f, 0x6c, 0x9c, 0xe5, 0x82, 0xbc, 0x34, 0x9b, 0xd2, 0x38, 0xd4, 0x52, 0x57,
0xd9, 0x8c, 0xcd, 0x45, 0x32, 0xc5, 0x63, 0x1d, 0xd2, 0xec, 0xd6, 0x6a, 0xa3, 0x5f, 0xc2, 0x95,
0xd2, 0x33, 0xdc, 0x79, 0x04, 0x31, 0x47, 0xb3, 0x12, 0x9e, 0x27, 0xb5, 0xe5, 0xcd, 0x03, 0x9c,
0xac, 0xf6, 0x00, 0xd6, 0x46, 0x45, 0x96, 0x57, 0x8b, 0xbc, 0xbf, 0xa1, 0x8b, 0x86, 0x86, 0x9a,
0x5e, 0xfb, 0x2f, 0xff, 0x71, 0xd0, 0x49, 0x69, 0xcb, 0x72, 0x5b, 0x89, 0x28, 0x59, 0xcf, 0x98,
0xa5, 0x3c, 0x8f, 0x05, 0x56, 0x4e, 0xb1, 0xfa, 0x01, 0x76, 0x2d, 0x22, 0xb9, 0x3b, 0xdf, 0x9f,
0xbb, 0xb1, 0xd8, 0xbb, 0x1f, 0x33, 0x5e, 0x4c, 0x4c, 0xcc, 0xd3, 0x01, 0xf5, 0x87, 0x0f, 0x92,
0x97, 0xbb, 0x92, 0x33, 0xa5, 0x8c, 0xf8, 0xf1, 0x0d, 0x14, 0x1a, 0x15, 0xc1, 0xc9, 0xc6, 0x48,
0x5e, 0xcd, 0xdd, 0xb7, 0xf1, 0xbc, 0x21, 0xef, 0x45, 0x84, 0x04, 0xfa, 0x43, 0xf9, 0xe7, 0x0a,
0x3b, 0xc0, 0xd7, 0xbe, 0xbd, 0x4e, 0x84, 0x97, 0x68, 0x34, 0x31, 0x2f, 0xb4, 0xc7, 0x5c, 0xff,
0x00, 0x9f, 0xf5, 0x05, 0x14, 0xf5, 0x92, 0x8b, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
0x44, 0xae, 0x42, 0x60, 0x82,
};
const BITMAP_OPAQUE icon_pcm_xpm[1] = {{ png, sizeof( png ), "icon_pcm_xpm" }};
//EOF

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -20,7 +20,8 @@ ICON_FILES="icon_3d.svg
icon_pl_editor.svg
icon_cvpcb.svg
icon_pcbnew.svg
icon_eeschema.svg"
icon_eeschema.svg
icon_pcm.svg"
# convert .svg files into .png files
for fl in $ICON_FILES

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<g id="XMLID_9_">
<g>
<g>
<g>
<polygon fill="#E7BF55" points="63,12 63,50 32,63 32,23 43.6,18.9 44,19 44,29 52,26 52,16 51.9,15.9 "/>
</g>
</g>
<g>
<g>
<polygon fill="#EFC75E" points="63,12 51.9,15.9 22,5 21.9,4.7 32,1 "/>
</g>
</g>
<g>
<g>
<polygon fill="#F7E3AF" points="52,16 52,26 44,29 44,19 43.6,18.9 51.9,15.9 "/>
</g>
</g>
<g>
<g>
<polygon fill="#F7E3AF" points="51.9,15.9 43.6,18.9 14,8 13.9,7.6 21.9,4.7 22,5 "/>
</g>
</g>
<g>
<g>
<polygon fill="#EFC75E" points="43.6,18.9 32,23 1,12.2 13.9,7.6 14,8 "/>
</g>
</g>
<g>
<g>
<polygon fill="#D3A644" points="32,23 32,63 1,50 1,12.2 "/>
</g>
</g>
</g>
<g>
<g>
<g>
<path fill="#2C2C3D" d="M32,24c-0.1,0-0.2,0-0.3-0.1l-31-10.8c-0.5-0.2-0.8-0.8-0.6-1.3s0.8-0.8,1.3-0.6L32,21.9l30.7-10.9
c0.5-0.2,1.1,0.1,1.3,0.6c0.2,0.5-0.1,1.1-0.6,1.3l-31,11C32.2,24,32.1,24,32,24z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M32,64c-0.1,0-0.3,0-0.4-0.1l-31-13C0.2,50.8,0,50.4,0,50V12.2c0-0.4,0.3-0.8,0.7-0.9l31-11.2
c0.2-0.1,0.5-0.1,0.7,0l31,11c0.4,0.1,0.7,0.5,0.7,0.9v38c0,0.4-0.2,0.8-0.6,0.9l-31,13C32.3,64,32.1,64,32,64z M2,49.3l30,12.6
l30-12.6V12.7L32,2.1L2,12.9V49.3z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M32,64c-0.6,0-1-0.4-1-1V23c0-0.6,0.4-1,1-1s1,0.4,1,1v40C33,63.6,32.6,64,32,64z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M44,30c-0.2,0-0.4-0.1-0.6-0.2C43.2,29.6,43,29.3,43,29v-9.3L13.7,8.9c-0.5-0.2-0.8-0.8-0.6-1.3
c0.2-0.5,0.8-0.8,1.3-0.6l30,11c0.4,0.1,0.7,0.5,0.7,0.9v8.6l6-2.2v-8.6L21.7,5.9c-0.5-0.2-0.8-0.8-0.6-1.3
c0.2-0.5,0.8-0.8,1.3-0.6l30,11c0.4,0.1,0.6,0.5,0.6,0.9v10c0,0.4-0.3,0.8-0.6,0.9l-8,3C44.2,30,44.1,30,44,30z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<g id="XMLID_9_">
<g>
<g>
<g>
<polygon fill="#E7BF55" points="63,12 63,50 32,63 32,23 43.6,18.9 44,19 44,29 52,26 52,16 51.9,15.9 "/>
</g>
</g>
<g>
<g>
<polygon fill="#EFC75E" points="63,12 51.9,15.9 22,5 21.9,4.7 32,1 "/>
</g>
</g>
<g>
<g>
<polygon fill="#F7E3AF" points="52,16 52,26 44,29 44,19 43.6,18.9 51.9,15.9 "/>
</g>
</g>
<g>
<g>
<polygon fill="#F7E3AF" points="51.9,15.9 43.6,18.9 14,8 13.9,7.6 21.9,4.7 22,5 "/>
</g>
</g>
<g>
<g>
<polygon fill="#EFC75E" points="43.6,18.9 32,23 1,12.2 13.9,7.6 14,8 "/>
</g>
</g>
<g>
<g>
<polygon fill="#D3A644" points="32,23 32,63 1,50 1,12.2 "/>
</g>
</g>
</g>
<g>
<g>
<g>
<path fill="#2C2C3D" d="M32,24c-0.1,0-0.2,0-0.3-0.1l-31-10.8c-0.5-0.2-0.8-0.8-0.6-1.3s0.8-0.8,1.3-0.6L32,21.9l30.7-10.9
c0.5-0.2,1.1,0.1,1.3,0.6c0.2,0.5-0.1,1.1-0.6,1.3l-31,11C32.2,24,32.1,24,32,24z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M32,64c-0.1,0-0.3,0-0.4-0.1l-31-13C0.2,50.8,0,50.4,0,50V12.2c0-0.4,0.3-0.8,0.7-0.9l31-11.2
c0.2-0.1,0.5-0.1,0.7,0l31,11c0.4,0.1,0.7,0.5,0.7,0.9v38c0,0.4-0.2,0.8-0.6,0.9l-31,13C32.3,64,32.1,64,32,64z M2,49.3l30,12.6
l30-12.6V12.7L32,2.1L2,12.9V49.3z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M32,64c-0.6,0-1-0.4-1-1V23c0-0.6,0.4-1,1-1s1,0.4,1,1v40C33,63.6,32.6,64,32,64z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M44,30c-0.2,0-0.4-0.1-0.6-0.2C43.2,29.6,43,29.3,43,29v-9.3L13.7,8.9c-0.5-0.2-0.8-0.8-0.6-1.3
c0.2-0.5,0.8-0.8,1.3-0.6l30,11c0.4,0.1,0.7,0.5,0.7,0.9v8.6l6-2.2v-8.6L21.7,5.9c-0.5-0.2-0.8-0.8-0.6-1.3
c0.2-0.5,0.8-0.8,1.3-0.6l30,11c0.4,0.1,0.6,0.5,0.6,0.9v10c0,0.4-0.3,0.8-0.6,0.9l-8,3C44.2,30,44.1,30,44,30z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<g id="XMLID_9_">
<g>
<g>
<g>
<polygon fill="#E7BF55" points="63,12 63,50 32,63 32,23 43.6,18.9 44,19 44,29 52,26 52,16 51.9,15.9 "/>
</g>
</g>
<g>
<g>
<polygon fill="#EFC75E" points="63,12 51.9,15.9 22,5 21.9,4.7 32,1 "/>
</g>
</g>
<g>
<g>
<polygon fill="#F7E3AF" points="52,16 52,26 44,29 44,19 43.6,18.9 51.9,15.9 "/>
</g>
</g>
<g>
<g>
<polygon fill="#F7E3AF" points="51.9,15.9 43.6,18.9 14,8 13.9,7.6 21.9,4.7 22,5 "/>
</g>
</g>
<g>
<g>
<polygon fill="#EFC75E" points="43.6,18.9 32,23 1,12.2 13.9,7.6 14,8 "/>
</g>
</g>
<g>
<g>
<polygon fill="#D3A644" points="32,23 32,63 1,50 1,12.2 "/>
</g>
</g>
</g>
<g>
<g>
<g>
<path fill="#2C2C3D" d="M32,24c-0.1,0-0.2,0-0.3-0.1l-31-10.8c-0.5-0.2-0.8-0.8-0.6-1.3s0.8-0.8,1.3-0.6L32,21.9l30.7-10.9
c0.5-0.2,1.1,0.1,1.3,0.6c0.2,0.5-0.1,1.1-0.6,1.3l-31,11C32.2,24,32.1,24,32,24z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M32,64c-0.1,0-0.3,0-0.4-0.1l-31-13C0.2,50.8,0,50.4,0,50V12.2c0-0.4,0.3-0.8,0.7-0.9l31-11.2
c0.2-0.1,0.5-0.1,0.7,0l31,11c0.4,0.1,0.7,0.5,0.7,0.9v38c0,0.4-0.2,0.8-0.6,0.9l-31,13C32.3,64,32.1,64,32,64z M2,49.3l30,12.6
l30-12.6V12.7L32,2.1L2,12.9V49.3z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M32,64c-0.6,0-1-0.4-1-1V23c0-0.6,0.4-1,1-1s1,0.4,1,1v40C33,63.6,32.6,64,32,64z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M44,30c-0.2,0-0.4-0.1-0.6-0.2C43.2,29.6,43,29.3,43,29v-9.3L13.7,8.9c-0.5-0.2-0.8-0.8-0.6-1.3
c0.2-0.5,0.8-0.8,1.3-0.6l30,11c0.4,0.1,0.7,0.5,0.7,0.9v8.6l6-2.2v-8.6L21.7,5.9c-0.5-0.2-0.8-0.8-0.6-1.3
c0.2-0.5,0.8-0.8,1.3-0.6l30,11c0.4,0.1,0.6,0.5,0.6,0.9v10c0,0.4-0.3,0.8-0.6,0.9l-8,3C44.2,30,44.1,30,44,30z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<g id="XMLID_9_">
<g>
<g>
<g>
<polygon fill="#E7BF55" points="63,12 63,50 32,63 32,23 43.6,18.9 44,19 44,29 52,26 52,16 51.9,15.9 "/>
</g>
</g>
<g>
<g>
<polygon fill="#EFC75E" points="63,12 51.9,15.9 22,5 21.9,4.7 32,1 "/>
</g>
</g>
<g>
<g>
<polygon fill="#F7E3AF" points="52,16 52,26 44,29 44,19 43.6,18.9 51.9,15.9 "/>
</g>
</g>
<g>
<g>
<polygon fill="#F7E3AF" points="51.9,15.9 43.6,18.9 14,8 13.9,7.6 21.9,4.7 22,5 "/>
</g>
</g>
<g>
<g>
<polygon fill="#EFC75E" points="43.6,18.9 32,23 1,12.2 13.9,7.6 14,8 "/>
</g>
</g>
<g>
<g>
<polygon fill="#D3A644" points="32,23 32,63 1,50 1,12.2 "/>
</g>
</g>
</g>
<g>
<g>
<g>
<path fill="#2C2C3D" d="M32,24c-0.1,0-0.2,0-0.3-0.1l-31-10.8c-0.5-0.2-0.8-0.8-0.6-1.3s0.8-0.8,1.3-0.6L32,21.9l30.7-10.9
c0.5-0.2,1.1,0.1,1.3,0.6c0.2,0.5-0.1,1.1-0.6,1.3l-31,11C32.2,24,32.1,24,32,24z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M32,64c-0.1,0-0.3,0-0.4-0.1l-31-13C0.2,50.8,0,50.4,0,50V12.2c0-0.4,0.3-0.8,0.7-0.9l31-11.2
c0.2-0.1,0.5-0.1,0.7,0l31,11c0.4,0.1,0.7,0.5,0.7,0.9v38c0,0.4-0.2,0.8-0.6,0.9l-31,13C32.3,64,32.1,64,32,64z M2,49.3l30,12.6
l30-12.6V12.7L32,2.1L2,12.9V49.3z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M32,64c-0.6,0-1-0.4-1-1V23c0-0.6,0.4-1,1-1s1,0.4,1,1v40C33,63.6,32.6,64,32,64z"/>
</g>
</g>
<g>
<g>
<path fill="#2C2C3D" d="M44,30c-0.2,0-0.4-0.1-0.6-0.2C43.2,29.6,43,29.3,43,29v-9.3L13.7,8.9c-0.5-0.2-0.8-0.8-0.6-1.3
c0.2-0.5,0.8-0.8,1.3-0.6l30,11c0.4,0.1,0.7,0.5,0.7,0.9v8.6l6-2.2v-8.6L21.7,5.9c-0.5-0.2-0.8-0.8-0.6-1.3
c0.2-0.5,0.8-0.8,1.3-0.6l30,11c0.4,0.1,0.6,0.5,0.6,0.9v10c0,0.4-0.3,0.8-0.6,0.9l-8,3C44.2,30,44.1,30,44,30z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -22,7 +22,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <kicad_curl/kicad_curl.h>
#include <kicad_curl/kicad_curl_easy.h>
#include <cstdarg>
@ -37,6 +36,19 @@
#include <pgm_base.h>
struct CURL_PROGRESS
{
KICAD_CURL_EASY* curl;
TRANSFER_CALLBACK callback;
curl_off_t last_run_time;
curl_off_t interval;
CURL_PROGRESS( KICAD_CURL_EASY* c, TRANSFER_CALLBACK cb, curl_off_t i ) :
curl( c ), callback( cb ), last_run_time( 0 ), interval( i )
{
}
};
static size_t write_callback( void* contents, size_t size, size_t nmemb, void* userp )
{
size_t realsize = size * nmemb;
@ -48,9 +60,19 @@ static size_t write_callback( void* contents, size_t size, size_t nmemb, void* u
return realsize;
}
static size_t stream_write_callback( void* contents, size_t size, size_t nmemb, void* userp )
{
size_t realsize = size * nmemb;
KICAD_CURL_EASY::KICAD_CURL_EASY() :
m_headers( nullptr )
std::ostream* p = (std::ostream*) userp;
p->write( (const char*) contents, realsize );
return realsize;
}
KICAD_CURL_EASY::KICAD_CURL_EASY() : m_headers( nullptr )
{
// Call KICAD_CURL::Init() from in here everytime, but only the first time
// will incur any overhead. This strategy ensures that libcurl is never loaded
@ -89,6 +111,7 @@ KICAD_CURL_EASY::KICAD_CURL_EASY() :
user_agent << "/" << GetBuildDate();
setOption<const char*>( CURLOPT_USERAGENT, user_agent.ToStdString().c_str() );
setOption( CURLOPT_ACCEPT_ENCODING, "gzip,deflate" );
}
@ -101,7 +124,7 @@ KICAD_CURL_EASY::~KICAD_CURL_EASY()
}
void KICAD_CURL_EASY::Perform()
int KICAD_CURL_EASY::Perform()
{
if( m_headers )
{
@ -113,12 +136,7 @@ void KICAD_CURL_EASY::Perform()
CURLcode res = curl_easy_perform( m_CURL );
if( res != CURLE_OK )
{
std::string msg = "curl_easy_perform()=";
msg += (int)res; msg += " "; msg += GetErrorText( res ).c_str();
THROW_IO_ERROR( msg );
}
return res;
}
@ -129,15 +147,16 @@ void KICAD_CURL_EASY::SetHeader( const std::string& aName, const std::string& aV
}
template <typename T> int KICAD_CURL_EASY::setOption( int aOption, T aArg )
template <typename T>
int KICAD_CURL_EASY::setOption( int aOption, T aArg )
{
return curl_easy_setopt( m_CURL, (CURLoption)aOption, aArg );
return curl_easy_setopt( m_CURL, (CURLoption) aOption, aArg );
}
const std::string KICAD_CURL_EASY::GetErrorText( int aCode )
{
return curl_easy_strerror( (CURLcode)aCode );
return curl_easy_strerror( (CURLcode) aCode );
}
@ -154,7 +173,7 @@ bool KICAD_CURL_EASY::SetUserAgent( const std::string& aAgent )
bool KICAD_CURL_EASY::SetURL( const std::string& aURL )
{
if( setOption<const char *>( CURLOPT_URL, aURL.c_str() ) == CURLE_OK )
if( setOption<const char*>( CURLOPT_URL, aURL.c_str() ) == CURLE_OK )
{
return true;
}
@ -165,7 +184,7 @@ bool KICAD_CURL_EASY::SetURL( const std::string& aURL )
bool KICAD_CURL_EASY::SetFollowRedirects( bool aFollow )
{
if( setOption<long>( CURLOPT_FOLLOWLOCATION , (aFollow ? 1 : 0) ) == CURLE_OK )
if( setOption<long>( CURLOPT_FOLLOWLOCATION, ( aFollow ? 1 : 0 ) ) == CURLE_OK )
{
return true;
}
@ -183,3 +202,48 @@ std::string KICAD_CURL_EASY::Escape( const std::string& aUrl )
return ret;
}
int KICAD_CURL_EASY::xferinfo( void* p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
curl_off_t ulnow )
{
CURL_PROGRESS* progress = (CURL_PROGRESS*) p;
curl_off_t curtime = 0;
curl_easy_getinfo( progress->curl->m_CURL, CURLINFO_TOTAL_TIME_T, &curtime );
if( curtime - progress->last_run_time >= progress->interval )
{
progress->last_run_time = curtime;
return progress->callback( dltotal, dlnow, ultotal, ulnow );
}
return CURLE_OK;
}
bool KICAD_CURL_EASY::SetTransferCallback( const TRANSFER_CALLBACK& aCallback, size_t aInterval )
{
progress = std::make_unique<CURL_PROGRESS>( this, aCallback, (curl_off_t) aInterval );
setOption( CURLOPT_XFERINFOFUNCTION, xferinfo );
setOption( CURLOPT_XFERINFODATA, progress.get() );
setOption( CURLOPT_NOPROGRESS, 0L );
return true;
}
bool KICAD_CURL_EASY::SetOutputStream( const std::ostream* aOutput )
{
curl_easy_setopt( m_CURL, CURLOPT_WRITEFUNCTION, stream_write_callback );
curl_easy_setopt( m_CURL, CURLOPT_WRITEDATA, (void*) aOutput );
return true;
}
int KICAD_CURL_EASY::GetTransferTotal( uint64_t& aDownloadedBytes ) const
{
curl_off_t dl;
int result = curl_easy_getinfo( m_CURL, CURLINFO_SIZE_DOWNLOAD_T, &dl );
aDownloadedBytes = (uint64_t) dl;
return result;
}

View File

@ -29,6 +29,7 @@
#include <project/project_archiver.h>
#include <reporter.h>
#include <wildcards_and_files_ext.h>
#include <wxstream_helper.h>
#include <wx/log.h>
@ -41,46 +42,6 @@ PROJECT_ARCHIVER::PROJECT_ARCHIVER()
// Unarchive Files code comes from wxWidgets sample/archive/archive.cpp
static bool CopyStreamData( wxInputStream& inputStream, wxOutputStream& outputStream,
wxFileOffset size )
{
wxChar buf[128 * 1024];
int readSize = 128 * 1024;
wxFileOffset copiedData = 0;
for( ; ; )
{
if(size != -1 && copiedData + readSize > size )
readSize = size - copiedData;
inputStream.Read( buf, readSize );
size_t actuallyRead = inputStream.LastRead();
outputStream.Write( buf, actuallyRead );
if( outputStream.LastWrite() != actuallyRead )
{
wxLogError( _( "Failed to output data." ) );
//return false;
}
if( size == -1 )
{
if( inputStream.Eof() )
break;
}
else
{
copiedData += actuallyRead;
if( copiedData >= size )
break;
}
}
return true;
}
bool PROJECT_ARCHIVER::Unarchive( const wxString& aSrcFile, const wxString& aDestDir,
REPORTER& aReporter )
{
@ -113,30 +74,12 @@ bool PROJECT_ARCHIVER::Unarchive( const wxString& aSrcFile, const wxString& aDes
wxString fullname = aDestDir + entry->GetName();
// Ensure the target directory exists and created it if not
// Ensure the target directory exists and create it if not
wxString t_path = wxPathOnly( fullname );
if( !wxDirExists( t_path ) )
{
// To create t_path, we need to create all subdirs from unzipDir
// to t_path.
wxFileName pathToCreate;
pathToCreate.AssignDir( t_path );
pathToCreate.MakeRelativeTo( aDestDir );
// Create the list of subdirs candidates
wxArrayString subDirs;
subDirs = pathToCreate.GetDirs();
pathToCreate.AssignDir( aDestDir );
for( size_t ii = 0; ii < subDirs.Count(); ii++ )
{
pathToCreate.AppendDir( subDirs[ii] );
wxString currPath = pathToCreate.GetPath();
if( !wxDirExists( currPath ) )
wxMkdir( currPath );
}
wxFileName::Mkdir( t_path, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
}
// Directory entries need only be created, not extracted (0 size)

View File

@ -297,6 +297,7 @@ const std::vector<BITMAP_INFO> g_BitmapInfo = {
{ BITMAPS::icon_cvpcb_24, wxT( "icon_cvpcb_24_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::icon_footprint_browser, wxT( "icon_footprint_browser_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::icon_gerbview_24, wxT( "icon_gerbview_24_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::icon_pcm_24, wxT( "icon_pcm_24_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::import3d, wxT( "import3d_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::image, wxT( "image_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::import_brd_file, wxT( "import_brd_file_24.png" ), 24, wxT( "light" ) },
@ -662,6 +663,7 @@ const std::vector<BITMAP_INFO> g_BitmapInfo = {
{ BITMAPS::icon_cvpcb_24, wxT( "icon_cvpcb_24_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::icon_footprint_browser, wxT( "icon_footprint_browser_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::icon_gerbview_24, wxT( "icon_gerbview_24_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::icon_pcm_24, wxT( "icon_pcm_24_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::import3d, wxT( "import3d_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::image, wxT( "image_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::import_brd_file, wxT( "import_brd_file_dark_24.png" ), 24, wxT( "dark" ) },
@ -956,6 +958,7 @@ const std::vector<BITMAP_INFO> g_BitmapInfo = {
{ BITMAPS::icon_bitmap2component, wxT( "icon_bitmap2component_48.png" ), 48, wxT( "light" ) },
{ BITMAPS::icon_pagelayout_editor, wxT( "icon_pagelayout_editor_48.png" ), 48, wxT( "light" ) },
{ BITMAPS::icon_pcbcalculator, wxT( "icon_pcbcalculator_48.png" ), 48, wxT( "light" ) },
{ BITMAPS::icon_pcm, wxT( "icon_pcm_48.png" ), 48, wxT( "light" ) },
{ BITMAPS::reannotate_down_left, wxT( "reannotate_down_left_48.png" ), 48, wxT( "light" ) },
{ BITMAPS::reannotate_down_right, wxT( "reannotate_down_right_48.png" ), 48, wxT( "light" ) },
{ BITMAPS::reannotate_left_down, wxT( "reannotate_left_down_48.png" ), 48, wxT( "light" ) },
@ -978,6 +981,7 @@ const std::vector<BITMAP_INFO> g_BitmapInfo = {
{ BITMAPS::icon_bitmap2component, wxT( "icon_bitmap2component_dark_48.png" ), 48, wxT( "dark" ) },
{ BITMAPS::icon_pagelayout_editor, wxT( "icon_pagelayout_editor_dark_48.png" ), 48, wxT( "dark" ) },
{ BITMAPS::icon_pcbcalculator, wxT( "icon_pcbcalculator_dark_48.png" ), 48, wxT( "dark" ) },
{ BITMAPS::icon_pcm, wxT( "icon_pcm_dark_48.png" ), 48, wxT( "dark" ) },
{ BITMAPS::reannotate_down_left, wxT( "reannotate_down_left_dark_48.png" ), 48, wxT( "dark" ) },
{ BITMAPS::reannotate_down_right, wxT( "reannotate_down_right_dark_48.png" ), 48, wxT( "dark" ) },
{ BITMAPS::reannotate_left_down, wxT( "reannotate_left_down_dark_48.png" ), 48, wxT( "dark" ) },

View File

@ -266,6 +266,8 @@ enum class BITMAPS : unsigned int
icon_pcbnew_16,
icon_pcbnew_24,
icon_pcbnew_32,
icon_pcm,
icon_pcm_24,
image,
import,
import3d,

View File

@ -32,6 +32,10 @@
* so including kicad_curl.h could be needed in a few sources
*/
#include <functional>
#include <kicad_curl/kicad_curl.h>
#include <memory>
#include <ostream>
#include <string>
typedef void CURL;
@ -56,6 +60,10 @@ struct curl_slist;
*/
typedef std::function<int( size_t, size_t, size_t, size_t )> TRANSFER_CALLBACK;
struct CURL_PROGRESS;
class KICAD_CURL_EASY
{
public:
@ -66,10 +74,8 @@ public:
* Function perform
* equivalent to curl_easy_perform. Executes the request
* that was previously setup.
*
* @throw IO_ERROR, if there is a CURL request error
*/
void Perform();
int Perform();
/**
* Function SetHeader
@ -117,18 +123,21 @@ public:
*/
const std::string GetErrorText( int aCode );
int GetTransferTotal( uint64_t& aDownloadedBytes ) const;
/**
* Function GetBuffer
* returns a const reference to the received data buffer
*/
const std::string& GetBuffer()
{
return m_buffer;
}
const std::string& GetBuffer() { return m_buffer; }
/// Escapes a string for use as a URL
std::string Escape( const std::string& aUrl );
bool SetTransferCallback( const TRANSFER_CALLBACK& aCallback, size_t aInterval );
bool SetOutputStream( const std::ostream* aOutput );
private:
/**
* Function setOption
@ -138,12 +147,16 @@ private:
* @param aArg is the argument being passed to CURL, ensure it is the right type per manual
* @return int - a CURL error code, will return CURLE_OK unless a problem was encountered
*/
template <typename T> int setOption( int aOption, T aArg );
template <typename T>
int setOption( int aOption, T aArg );
static int xferinfo( void*, curl_off_t, curl_off_t, curl_off_t, curl_off_t );
CURL* m_CURL;
curl_slist* m_headers;
std::string m_buffer;
CURL* m_CURL;
curl_slist* m_headers;
std::string m_buffer;
std::unique_ptr<CURL_PROGRESS> progress;
};
#endif // KICAD_CURL_EASY_H_

67
include/wxstream_helper.h Normal file
View File

@ -0,0 +1,67 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 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/>.
*/
#ifndef WXSTREAM_HELPER_H
#define WXSTREAM_HELPER_H
#include <wx/log.h>
#include <wx/wfstream.h>
static bool CopyStreamData( wxInputStream& inputStream, wxOutputStream& outputStream,
wxFileOffset size )
{
wxChar buf[128 * 1024];
int readSize = 128 * 1024;
wxFileOffset copiedData = 0;
for( ; ; )
{
if(size != -1 && copiedData + readSize > size )
readSize = size - copiedData;
inputStream.Read( buf, readSize );
size_t actuallyRead = inputStream.LastRead();
outputStream.Write( buf, actuallyRead );
if( outputStream.LastWrite() != actuallyRead )
{
wxLogError( _("Failed to output data") );
//return false;
}
if( size == -1 )
{
if( inputStream.Eof() )
break;
}
else
{
copiedData += actuallyRead;
if( copiedData >= size )
break;
}
}
return true;
}
#endif // WXSTREAM_HELPER_H

View File

@ -13,6 +13,11 @@ include_directories(
${INC_AFTER}
)
if ( KICAD_PCM )
add_definitions( -DPCM )
add_subdirectory( pcm )
include_directories( ./pcm )
endif ( KICAD_PCM )
set( KICAD_SRCS
dialogs/dialog_template_selector_base.cpp
@ -91,6 +96,10 @@ else()
)
endif()
if( KICAD_PCM )
target_link_libraries( kicad pcm )
endif()
install( TARGETS kicad
DESTINATION ${KICAD_BIN}
COMPONENT binary

View File

@ -136,5 +136,11 @@ void PANEL_KICAD_LAUNCHER::CreateLaunchers()
_( "Edit drawing sheet borders and title blocks for use in schematics and PCB "
"designs" ) );
#ifdef PCM
addLauncher( KICAD_MANAGER_ACTIONS::showPluginManager,
KiBitmap( BITMAPS::icon_pcm, 48 ),
_( "Manage downloadable packages from KiCad and 3rd party repositories" ) );
#endif
Layout();
}

View File

@ -19,7 +19,7 @@
*/
#include "kicad_settings.h"
#include "nlohmann/json.hpp"
#include <nlohmann/json.hpp>
#include <settings/parameters.h>
@ -46,6 +46,7 @@ KICAD_SETTINGS::KICAD_SETTINGS() :
{
js.push_back( nlohmann::json( { { "name", pair.first.ToUTF8() },
{ "url", pair.second.ToUTF8() } } ) );
}
return js;
},

View File

@ -27,7 +27,6 @@
class KICAD_SETTINGS : public APP_SETTINGS_BASE
{
public:
KICAD_SETTINGS();
virtual ~KICAD_SETTINGS() {}
@ -44,7 +43,6 @@ public:
#endif
protected:
virtual std::string getLegacyFrameName() const override { return "KicadFrame"; }
};

View File

@ -157,6 +157,9 @@ void KICAD_MANAGER_FRAME::ReCreateMenuBar()
toolsMenu->Add( KICAD_MANAGER_ACTIONS::convertImage );
toolsMenu->Add( KICAD_MANAGER_ACTIONS::showCalculator );
toolsMenu->Add( KICAD_MANAGER_ACTIONS::editDrawingSheet );
#ifdef PCM
toolsMenu->Add( KICAD_MANAGER_ACTIONS::showPluginManager );
#endif
toolsMenu->AppendSeparator();
toolsMenu->Add( _( "Edit Local File..." ),

55
kicad/pcm/CMakeLists.txt Normal file
View File

@ -0,0 +1,55 @@
# Add all the warnings to the files
if( COMPILER_SUPPORTS_WARNINGS )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARN_FLAGS_CXX}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARN_FLAGS_C}")
endif()
include_directories( BEFORE ${INC_BEFORE} )
add_definitions( -DPCM )
include_directories(
${CMAKE_SOURCE_DIR}/common
${CMAKE_SOURCE_DIR}/kicad
)
set ( PCM_DLG_SRCS
dialogs/dialog_manage_repositories_base.cpp
dialogs/dialog_manage_repositories.cpp
dialogs/dialog_pcm_base.cpp
dialogs/dialog_pcm.cpp
dialogs/dialog_pcm_progress_base.cpp
dialogs/dialog_pcm_progress.cpp
dialogs/panel_package_base.cpp
dialogs/panel_package.cpp
dialogs/panel_packages_view_base.cpp
dialogs/panel_packages_view.cpp
)
set( PCM_SRCS
${PCM_DLG_SRCS}
pcm.cpp
pcm_data.cpp
pcm_task_manager.cpp
)
add_library( pcm STATIC
${PCM_SRCS}
)
target_include_directories(
pcm PUBLIC dialogs
)
target_link_libraries( pcm
${wxWidgets_LIBRARIES}
common
picosha2
nlohmann_json_schema_validator
)
INSTALL( DIRECTORY
schemas
DESTINATION ${KICAD_DATA}
COMPONENT Runtime
)

View File

@ -0,0 +1,253 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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 "dialog_manage_repositories.h"
#include "bitmaps/bitmap_types.h"
#include "bitmaps/bitmaps_list.h"
#include "grid_tricks.h"
#include "widgets/wx_grid.h"
#define GRID_CELL_MARGIN 4
DIALOG_MANAGE_REPOSITORIES::DIALOG_MANAGE_REPOSITORIES(
wxWindow* parent, std::shared_ptr<PLUGIN_CONTENT_MANAGER> pcm ) :
DIALOG_MANAGE_REPOSITORIES_BASE( parent ),
m_pcm( pcm )
{
m_buttonAdd->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
m_buttonRemove->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
m_buttonMoveUp->SetBitmap( KiBitmap( BITMAPS::small_up ) );
m_buttonMoveDown->SetBitmap( KiBitmap( BITMAPS::small_down ) );
m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
for( int col = 0; col < m_grid->GetNumberCols(); col++ )
{
const wxString& heading = m_grid->GetColLabelValue( col );
int headingWidth = GetTextExtent( heading ).x + 2 * GRID_CELL_MARGIN;
// Set the minimal width to the column label size.
m_grid->SetColMinimalWidth( col, headingWidth );
}
}
void DIALOG_MANAGE_REPOSITORIES::setColumnWidths()
{
for( int col = 0; col < m_grid->GetNumberCols(); col++ )
{
// Set the width to see the full contents
m_grid->SetColSize( col, m_grid->GetVisibleWidth( col, true, true, false ) );
}
}
void DIALOG_MANAGE_REPOSITORIES::OnAddButtonClicked( wxCommandEvent& event )
{
wxTextEntryDialog* entry_dialog = new wxTextEntryDialog(
this, _( "Please enter fully qualified repository url" ), _( "Add repository" ) );
if( entry_dialog->ShowModal() == wxID_OK )
{
PCM_REPOSITORY repository;
wxString url = entry_dialog->GetValue();
const auto find_row = [&]( const int col, const wxString& val )
{
for( int row = 0; row < m_grid->GetNumberRows(); row++ )
{
if( m_grid->GetCellValue( row, col ) == val )
return row;
}
return -1;
};
int matching_row;
if( ( matching_row = find_row( 1, url ) ) >= 0 )
{
selectRow( matching_row );
}
else
{
if( m_pcm->FetchRepository( url, repository ) )
{
wxString name = repository.name;
int increment = 1;
while( find_row( 0, name ) >= 0 )
name = wxString::Format( "%s (%d)", repository.name, increment++ );
m_grid->Freeze();
m_grid->AppendRows();
int row = m_grid->GetNumberRows() - 1;
m_grid->SetCellValue( row, 0, name );
m_grid->SetCellValue( row, 1, url );
setColumnWidths();
m_grid->Thaw();
selectRow( row );
}
}
}
entry_dialog->Destroy();
}
void DIALOG_MANAGE_REPOSITORIES::OnRemoveButtonClicked( wxCommandEvent& event )
{
auto selectedRows = m_grid->GetSelectedRows();
// If nothing is selected or multiple rows are selected don't do anything.
if( selectedRows.size() != 1 )
{
wxBell();
return;
}
int selectedRow = selectedRows[0];
m_grid->DeleteRows( selectedRow );
setColumnWidths();
if( m_grid->GetNumberRows() > 0 )
m_grid->SelectRow( selectedRow == m_grid->GetNumberRows() ? selectedRow - 1 : selectedRow );
}
void DIALOG_MANAGE_REPOSITORIES::OnMoveUpButtonClicked( wxCommandEvent& event )
{
auto selectedRows = m_grid->GetSelectedRows();
// If nothing is selected or multiple rows are selected don't do anything.
if( selectedRows.size() != 1 )
return;
int selectedRow = selectedRows[0];
// If first row is selected, then it can't go any further up.
if( selectedRow == 0 )
{
wxBell();
return;
}
swapRows( selectedRow, selectedRow - 1 );
selectRow( selectedRow - 1 );
}
void DIALOG_MANAGE_REPOSITORIES::OnMoveDownButtonClicked( wxCommandEvent& event )
{
auto selectedRows = m_grid->GetSelectedRows();
// If nothing is selected or multiple rows are selected don't do anything.
if( selectedRows.size() != 1 )
return;
int selectedRow = selectedRows[0];
// If last row is selected, then it can't go any further down.
if( selectedRow + 1 == m_grid->GetNumberRows() )
{
wxBell();
return;
}
swapRows( selectedRow, selectedRow + 1 );
selectRow( selectedRow + 1 );
}
void DIALOG_MANAGE_REPOSITORIES::swapRows( int aRowA, int aRowB )
{
m_grid->Freeze();
wxString tempStr;
for( int column = 0; column < m_grid->GetNumberCols(); column++ )
{
tempStr = m_grid->GetCellValue( aRowA, column );
m_grid->SetCellValue( aRowA, column, m_grid->GetCellValue( aRowB, column ) );
m_grid->SetCellValue( aRowB, column, tempStr );
}
m_grid->Thaw();
}
void DIALOG_MANAGE_REPOSITORIES::OnGridCellClicked( wxGridEvent& event )
{
selectRow( event.GetRow() );
}
void DIALOG_MANAGE_REPOSITORIES::selectRow( int aRow )
{
m_grid->ClearSelection();
m_grid->SelectRow( aRow );
}
void DIALOG_MANAGE_REPOSITORIES::SetData( const STRING_PAIR_LIST& aData )
{
m_grid->Freeze();
m_grid->DeleteRows( 0, m_grid->GetNumberRows() );
m_grid->AppendRows( aData.size() );
for( size_t i = 0; i < aData.size(); i++ )
{
m_grid->SetCellValue( i, 0, aData[i].first );
m_grid->SetCellValue( i, 1, aData[i].second );
}
setColumnWidths();
m_grid->Thaw();
}
STRING_PAIR_LIST DIALOG_MANAGE_REPOSITORIES::GetData()
{
STRING_PAIR_LIST result;
for( int i = 0; i < m_grid->GetNumberRows(); i++ )
{
result.push_back(
std::make_pair( m_grid->GetCellValue( i, 0 ), m_grid->GetCellValue( i, 1 ) ) );
}
return result;
}
void DIALOG_MANAGE_REPOSITORIES::OnSaveClicked( wxCommandEvent& event )
{
EndModal( wxID_SAVE );
}

View File

@ -0,0 +1,56 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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/>.
*/
#ifndef DIALOG_MANAGE_REPOSITORIES_H_
#define DIALOG_MANAGE_REPOSITORIES_H_
#include "dialog_manage_repositories_base.h"
#include "pcm.h"
#include <memory>
#include <vector>
/** Implementing DIALOG_MANAGE_REPOSITORIES_BASE */
class DIALOG_MANAGE_REPOSITORIES : public DIALOG_MANAGE_REPOSITORIES_BASE
{
protected:
// Handlers for DIALOG_MANAGE_REPOSITORIES_BASE events.
void OnAddButtonClicked( wxCommandEvent& event ) override;
void OnRemoveButtonClicked( wxCommandEvent& event ) override;
void OnMoveUpButtonClicked( wxCommandEvent& event ) override;
void OnMoveDownButtonClicked( wxCommandEvent& event ) override;
void OnGridCellClicked( wxGridEvent& event ) override;
void OnSaveClicked( wxCommandEvent& event ) override;
public:
/** Constructor */
DIALOG_MANAGE_REPOSITORIES( wxWindow* parent, std::shared_ptr<PLUGIN_CONTENT_MANAGER> aPcm );
void SetData( const STRING_PAIR_LIST& aData );
STRING_PAIR_LIST GetData();
private:
void swapRows( int aRowA, int aRowB );
void selectRow( int aRow );
void setColumnWidths();
std::shared_ptr<PLUGIN_CONTENT_MANAGER> m_pcm;
};
#endif // DIALOG_MANAGE_REPOSITORIES_H_

View File

@ -0,0 +1,110 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "widgets/wx_grid.h"
#include "dialog_manage_repositories_base.h"
///////////////////////////////////////////////////////////////////////////
DIALOG_MANAGE_REPOSITORIES_BASE::DIALOG_MANAGE_REPOSITORIES_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxSize( 500,300 ), wxDefaultSize );
wxBoxSizer* m_MainSizer;
m_MainSizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* m_TopSizer;
m_TopSizer = new wxBoxSizer( wxVERTICAL );
m_grid = new WX_GRID( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
// Grid
m_grid->CreateGrid( 5, 2 );
m_grid->EnableEditing( false );
m_grid->EnableGridLines( true );
m_grid->EnableDragGridSize( false );
m_grid->SetMargins( 0, 0 );
// Columns
m_grid->EnableDragColMove( false );
m_grid->EnableDragColSize( true );
m_grid->SetColLabelSize( 30 );
m_grid->SetColLabelValue( 0, _("Name") );
m_grid->SetColLabelValue( 1, _("URL") );
m_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Rows
m_grid->EnableDragRowSize( true );
m_grid->SetRowLabelSize( 0 );
m_grid->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Label Appearance
// Cell Defaults
m_grid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP );
m_TopSizer->Add( m_grid, 1, wxALL|wxEXPAND, 5 );
m_MainSizer->Add( m_TopSizer, 1, wxEXPAND, 5 );
wxBoxSizer* m_BottomSizer;
m_BottomSizer = new wxBoxSizer( wxHORIZONTAL );
m_buttonAdd = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_BottomSizer->Add( m_buttonAdd, 0, wxALL, 5 );
m_buttonRemove = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_BottomSizer->Add( m_buttonRemove, 0, wxALL, 5 );
m_BottomSizer->Add( 20, 0, 0, wxEXPAND, 5 );
m_buttonMoveUp = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_BottomSizer->Add( m_buttonMoveUp, 0, wxALL, 5 );
m_buttonMoveDown = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_BottomSizer->Add( m_buttonMoveDown, 0, wxALL, 5 );
m_sdbSizer1 = new wxStdDialogButtonSizer();
m_sdbSizer1Save = new wxButton( this, wxID_SAVE );
m_sdbSizer1->AddButton( m_sdbSizer1Save );
m_sdbSizer1Cancel = new wxButton( this, wxID_CANCEL );
m_sdbSizer1->AddButton( m_sdbSizer1Cancel );
m_sdbSizer1->Realize();
m_BottomSizer->Add( m_sdbSizer1, 1, wxEXPAND, 5 );
m_MainSizer->Add( m_BottomSizer, 0, wxEXPAND, 5 );
this->SetSizer( m_MainSizer );
this->Layout();
this->Centre( wxBOTH );
// Connect Events
m_grid->Connect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnGridCellClicked ), NULL, this );
m_buttonAdd->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnAddButtonClicked ), NULL, this );
m_buttonRemove->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnRemoveButtonClicked ), NULL, this );
m_buttonMoveUp->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnMoveUpButtonClicked ), NULL, this );
m_buttonMoveDown->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnMoveDownButtonClicked ), NULL, this );
m_sdbSizer1Save->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnSaveClicked ), NULL, this );
}
DIALOG_MANAGE_REPOSITORIES_BASE::~DIALOG_MANAGE_REPOSITORIES_BASE()
{
// Disconnect Events
m_grid->Disconnect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnGridCellClicked ), NULL, this );
m_buttonAdd->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnAddButtonClicked ), NULL, this );
m_buttonRemove->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnRemoveButtonClicked ), NULL, this );
m_buttonMoveUp->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnMoveUpButtonClicked ), NULL, this );
m_buttonMoveDown->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnMoveDownButtonClicked ), NULL, this );
m_sdbSizer1Save->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MANAGE_REPOSITORIES_BASE::OnSaveClicked ), NULL, this );
}

View File

@ -0,0 +1,496 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="15" />
<object class="Project" expanded="1">
<property name="class_decoration">; </property>
<property name="code_generation">C++</property>
<property name="disconnect_events">1</property>
<property name="disconnect_mode">source_name</property>
<property name="disconnect_php_events">0</property>
<property name="disconnect_python_events">0</property>
<property name="embedded_files_path"></property>
<property name="encoding">UTF-8</property>
<property name="event_generation">connect</property>
<property name="file">dialog_manage_repositories_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="indent_with_spaces"></property>
<property name="internationalize">1</property>
<property name="name">dialog_manage_repositories_base</property>
<property name="namespace"></property>
<property name="path">./</property>
<property name="precompiled_header"></property>
<property name="relative_path">1</property>
<property name="skip_lua_events">1</property>
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="ui_table">UI</property>
<property name="use_enum">0</property>
<property name="use_microsoft_bom">0</property>
<object class="Dialog" expanded="1">
<property name="aui_managed">0</property>
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
<property name="bg"></property>
<property name="center">wxBOTH</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="enabled">1</property>
<property name="event_handler">impl_virtual</property>
<property name="extra_style"></property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="minimum_size">500,300</property>
<property name="name">DIALOG_MANAGE_REPOSITORIES_BASE</property>
<property name="pos"></property>
<property name="size">500,300</property>
<property name="style">wxDEFAULT_DIALOG_STYLE</property>
<property name="subclass">; ; forward_declare</property>
<property name="title">Manage Repositores</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">m_MainSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">m_TopSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxGrid" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="autosize_cols">0</property>
<property name="autosize_rows">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="cell_bg"></property>
<property name="cell_font"></property>
<property name="cell_horiz_alignment">wxALIGN_LEFT</property>
<property name="cell_text"></property>
<property name="cell_vert_alignment">wxALIGN_TOP</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="col_label_horiz_alignment">wxALIGN_CENTER</property>
<property name="col_label_size">30</property>
<property name="col_label_values">&quot;Name&quot; &quot;URL&quot;</property>
<property name="col_label_vert_alignment">wxALIGN_CENTER</property>
<property name="cols">2</property>
<property name="column_sizes"></property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_col_move">0</property>
<property name="drag_col_size">1</property>
<property name="drag_grid_size">0</property>
<property name="drag_row_size">1</property>
<property name="editing">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="grid_line_color"></property>
<property name="grid_lines">1</property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label_bg"></property>
<property name="label_font"></property>
<property name="label_text"></property>
<property name="margin_height">0</property>
<property name="margin_width">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_grid</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="row_label_horiz_alignment">wxALIGN_CENTER</property>
<property name="row_label_size">0</property>
<property name="row_label_values"></property>
<property name="row_label_vert_alignment">wxALIGN_CENTER</property>
<property name="row_sizes"></property>
<property name="rows">5</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">WX_GRID; widgets/wx_grid.h; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnGridCellLeftClick">OnGridCellClicked</event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">m_BottomSizer</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxBitmapButton" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Add repository</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_buttonAdd</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">OnAddButtonClicked</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxBitmapButton" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Remove repository</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_buttonRemove</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">OnRemoveButtonClicked</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="spacer" expanded="1">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">20</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxBitmapButton" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Move Up</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_buttonMoveUp</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">OnMoveUpButtonClicked</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxBitmapButton" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Move Down</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_buttonMoveDown</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">OnMoveDownButtonClicked</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxStdDialogButtonSizer" expanded="1">
<property name="Apply">0</property>
<property name="Cancel">1</property>
<property name="ContextHelp">0</property>
<property name="Help">0</property>
<property name="No">0</property>
<property name="OK">0</property>
<property name="Save">1</property>
<property name="Yes">0</property>
<property name="minimum_size"></property>
<property name="name">m_sdbSizer1</property>
<property name="permission">protected</property>
<event name="OnSaveButtonClick">OnSaveClicked</event>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</wxFormBuilder_Project>

View File

@ -0,0 +1,64 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#pragma once
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
class WX_GRID;
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/font.h>
#include <wx/grid.h>
#include <wx/gdicmn.h>
#include <wx/sizer.h>
#include <wx/bmpbuttn.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/button.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_MANAGE_REPOSITORIES_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_MANAGE_REPOSITORIES_BASE : public wxDialog
{
private:
protected:
WX_GRID* m_grid;
wxBitmapButton* m_buttonAdd;
wxBitmapButton* m_buttonRemove;
wxBitmapButton* m_buttonMoveUp;
wxBitmapButton* m_buttonMoveDown;
wxStdDialogButtonSizer* m_sdbSizer1;
wxButton* m_sdbSizer1Save;
wxButton* m_sdbSizer1Cancel;
// Virtual event handlers, overide them in your derived class
virtual void OnGridCellClicked( wxGridEvent& event ) { event.Skip(); }
virtual void OnAddButtonClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnRemoveButtonClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnMoveUpButtonClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnMoveDownButtonClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnSaveClicked( wxCommandEvent& event ) { event.Skip(); }
public:
DIALOG_MANAGE_REPOSITORIES_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Manage Repositores"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 500,300 ), long style = wxDEFAULT_DIALOG_STYLE );
~DIALOG_MANAGE_REPOSITORIES_BASE();
};

View File

@ -0,0 +1,435 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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 "dialog_pcm.h"
#include "bitmaps.h"
#include "dialog_manage_repositories.h"
#include "grid_tricks.h"
#include "ki_exception.h"
#include "kicad_curl/kicad_curl_easy.h"
#include "kicad_settings.h"
#include "pcm_task_manager.h"
#include "pgm_base.h"
#include "settings/settings_manager.h"
#include "thread"
#include "widgets/wx_progress_reporters.h"
#include "widgets/wx_grid.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <wx/filedlg.h>
#include <wx/msgdlg.h>
#define GRID_CELL_MARGIN 4
static std::vector<std::pair<PCM_PACKAGE_TYPE, wxString>> PACKAGE_TYPE_LIST = {
{ PT_PLUGIN, _( "Plugins (%d)" ) },
{ PT_LIBRARY, _( "Libraries (%d)" ) }
};
DIALOG_PCM::DIALOG_PCM( wxWindow* parent ) : DIALOG_PCM_BASE( parent )
{
m_defaultBitmap = KiBitmap( BITMAPS::icon_pcm );
m_pcm = std::make_shared<PLUGIN_CONTENT_MANAGER>( this );
m_gridPendingActions->PushEventHandler( new GRID_TRICKS( m_gridPendingActions ) );
m_discardActionButton->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
m_panelPending->Layout();
m_installedPanel = new PANEL_PACKAGES_VIEW( m_panelInstalledHolder, m_pcm );
m_panelInstalledHolder->GetSizer()->Add( m_installedPanel, 1, wxEXPAND );
m_panelInstalledHolder->Layout();
for( const auto& entry : PACKAGE_TYPE_LIST )
{
PANEL_PACKAGES_VIEW* panel = new PANEL_PACKAGES_VIEW( m_contentNotebook, m_pcm );
m_contentNotebook->AddPage( panel, wxString::Format( std::get<1>( entry ), 0 ) );
m_repositoryContentPanels.insert( { std::get<0>( entry ), panel } );
}
m_dialogNotebook->SetPageText( 0, wxString::Format( _( "Repository (%d)" ), 0 ) );
m_callback = [this]( const PACKAGE_VIEW_DATA& aData, PCM_PACKAGE_ACTION aAction,
const wxString& aVersion )
{
m_gridPendingActions->Freeze();
PCM_PACKAGE_STATE new_state;
m_gridPendingActions->AppendRows();
int row = m_gridPendingActions->GetNumberRows() - 1;
m_gridPendingActions->SetCellValue( row, PENDING_COL_NAME, aData.package.name );
m_gridPendingActions->SetCellValue( row, PENDING_COL_REPOSITORY, aData.repository_name );
if( aAction == PPA_INSTALL )
{
m_gridPendingActions->SetCellValue( row, PENDING_COL_ACTION, _( "Install" ) );
m_gridPendingActions->SetCellValue( row, PENDING_COL_VERSION, aVersion );
m_pendingActions.emplace_back( aAction, aData.repository_id, aData.package, aVersion );
new_state = PPS_PENDING_INSTALL;
}
else
{
m_gridPendingActions->SetCellValue( row, PENDING_COL_ACTION, _( "Uninstall" ) );
m_gridPendingActions->SetCellValue(
row, PENDING_COL_VERSION,
m_pcm->GetInstalledPackageVersion( aData.package.identifier ) );
m_pendingActions.emplace_back( aAction, aData.repository_id, aData.package, aVersion );
new_state = PPS_PENDING_UNINSTALL;
}
m_gridPendingActions->Thaw();
updatePendingActionsTab();
m_installedPanel->SetPackageState( aData.package.identifier, new_state );
for( const auto& entry : m_repositoryContentPanels )
entry.second->SetPackageState( aData.package.identifier, new_state );
};
setInstalledPackages();
updatePendingActionsTab();
m_dialogNotebook->SetSelection( 0 );
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
KICAD_SETTINGS* app_settings = mgr.GetAppSettings<KICAD_SETTINGS>();
m_pcm->SetRepositoryList( app_settings->m_PcmRepositories );
setRepositoryListFromPcm();
SetDefaultItem( m_closeButton );
for( int col = 0; col < m_gridPendingActions->GetNumberCols(); col++ )
{
const wxString& heading = m_gridPendingActions->GetColLabelValue( col );
int headingWidth = GetTextExtent( heading ).x + 2 * GRID_CELL_MARGIN;
// Set the minimal width to the column label size.
m_gridPendingActions->SetColMinimalWidth( col, headingWidth );
}
}
DIALOG_PCM::~DIALOG_PCM()
{
m_gridPendingActions->PopEventHandler( true );
}
void DIALOG_PCM::OnCloseClicked( wxCommandEvent& event )
{
if( m_pendingActions.size() == 0
|| wxMessageBox( _( "Are you sure you want to close the package manager "
"and discard pending changes?" ),
_( "Plugin and Content Manager" ), wxICON_QUESTION | wxYES_NO, this )
== wxYES )
{
EndModal( wxID_OK );
}
}
void DIALOG_PCM::OnManageRepositoriesClicked( wxCommandEvent& event )
{
DIALOG_MANAGE_REPOSITORIES* dialog = new DIALOG_MANAGE_REPOSITORIES( this, m_pcm );
STRING_PAIR_LIST dialog_data;
STRING_TUPLE_LIST repo_list = m_pcm->GetRepositoryList();
for( const auto& repo : repo_list )
{
dialog_data.push_back( std::make_pair( std::get<1>( repo ), std::get<2>( repo ) ) );
}
dialog->SetData( dialog_data );
if( dialog->ShowModal() == wxID_SAVE )
{
dialog_data = dialog->GetData();
m_pcm->SetRepositoryList( dialog_data );
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
KICAD_SETTINGS* app_settings = mgr.GetAppSettings<KICAD_SETTINGS>();
app_settings->m_PcmRepositories = std::move( dialog_data );
setRepositoryListFromPcm();
}
dialog->Destroy();
}
void DIALOG_PCM::setRepositoryListFromPcm()
{
STRING_TUPLE_LIST repositories = m_pcm->GetRepositoryList();
m_choiceRepository->Clear();
for( const auto& entry : repositories )
{
m_choiceRepository->Append( std::get<1>( entry ),
new wxStringClientData( std::get<0>( entry ) ) );
}
if( repositories.size() > 0 )
{
m_choiceRepository->SetSelection( 0 );
m_selectedRepositoryId = std::get<0>( repositories[0] );
setRepositoryData( m_selectedRepositoryId );
}
else
{
m_selectedRepositoryId = "";
}
}
void DIALOG_PCM::OnRefreshClicked( wxCommandEvent& event )
{
m_pcm->DiscardRepositoryCache( m_selectedRepositoryId );
setRepositoryData( m_selectedRepositoryId );
}
void DIALOG_PCM::OnInstallFromFileClicked( wxCommandEvent& event )
{
wxFileDialog open_file_dialog( this, _( "Choose package file" ), wxEmptyString, wxEmptyString,
"Zip files (*.zip)|*.zip", wxFD_OPEN | wxFD_FILE_MUST_EXIST );
if( open_file_dialog.ShowModal() == wxID_CANCEL )
return;
PCM_TASK_MANAGER task_manager( m_pcm );
task_manager.InstallFromFile( this, open_file_dialog.GetPath() );
setInstalledPackages();
if( !m_selectedRepositoryId.IsEmpty() )
setRepositoryData( m_selectedRepositoryId );
}
void DIALOG_PCM::OnRepositoryChoice( wxCommandEvent& event )
{
wxStringClientData* data = static_cast<wxStringClientData*>(
m_choiceRepository->GetClientObject( m_choiceRepository->GetSelection() ) );
m_selectedRepositoryId = data->GetData();
setRepositoryData( m_selectedRepositoryId );
}
void DIALOG_PCM::setRepositoryData( const wxString& aRepositoryId )
{
if( m_pcm->CacheRepository( aRepositoryId ) )
{
for( const auto& entry : m_repositoryContentPanels )
entry.second->ClearData();
m_packageBitmaps = m_pcm->GetRepositoryPackageBitmaps( aRepositoryId );
const std::vector<PCM_PACKAGE> packages = m_pcm->GetRepositoryPackages( aRepositoryId );
std::unordered_map<PCM_PACKAGE_TYPE, std::vector<PACKAGE_VIEW_DATA>> data;
for( const PCM_PACKAGE& pkg : packages )
{
PACKAGE_VIEW_DATA package_data( pkg );
if( m_packageBitmaps.count( package_data.package.identifier ) > 0 )
package_data.bitmap = &m_packageBitmaps.at( package_data.package.identifier );
else
package_data.bitmap = &m_defaultBitmap;
package_data.state = m_pcm->GetPackageState( aRepositoryId, pkg.identifier );
for( const auto& action : m_pendingActions )
{
if( action.package.identifier != pkg.identifier )
continue;
if( action.action == PPA_INSTALL )
package_data.state = PPS_PENDING_INSTALL;
else
package_data.state = PPS_PENDING_UNINSTALL;
break;
}
package_data.repository_id = aRepositoryId;
package_data.repository_name = m_choiceRepository->GetStringSelection();
data[pkg.type].emplace_back( package_data );
}
for( size_t i = 0; i < PACKAGE_TYPE_LIST.size(); i++ )
{
PCM_PACKAGE_TYPE type = PACKAGE_TYPE_LIST[i].first;
m_repositoryContentPanels[type]->SetData( data[type], m_callback );
m_contentNotebook->SetPageText(
i, wxString::Format( PACKAGE_TYPE_LIST[i].second, (int) data[type].size() ) );
}
m_dialogNotebook->SetPageText(
0, wxString::Format( _( "Repository (%d)" ), (int) packages.size() ) );
}
}
void DIALOG_PCM::OnPendingActionsCellClicked( wxGridEvent& event )
{
m_gridPendingActions->ClearSelection();
m_gridPendingActions->SelectRow( event.GetRow() );
}
void DIALOG_PCM::updatePendingActionsTab()
{
m_dialogNotebook->SetPageText(
2, wxString::Format( _( "Pending (%d)" ), (int) m_pendingActions.size() ) );
for( int col = 0; col < m_gridPendingActions->GetNumberCols(); col++ )
{
// Set the width to see the full contents
m_gridPendingActions->SetColSize(
col, m_gridPendingActions->GetVisibleWidth( col, true, true, false ) );
}
}
void DIALOG_PCM::setInstalledPackages()
{
m_installedPanel->ClearData();
const std::vector<PCM_INSTALLATION_ENTRY> installed = m_pcm->GetInstalledPackages();
std::vector<PACKAGE_VIEW_DATA> package_list;
m_installedBitmaps = m_pcm->GetInstalledPackageBitmaps();
for( const auto& entry : installed )
{
PACKAGE_VIEW_DATA package_data( entry );
if( m_installedBitmaps.count( package_data.package.identifier ) > 0 )
package_data.bitmap = &m_installedBitmaps.at( package_data.package.identifier );
else
package_data.bitmap = &m_defaultBitmap;
package_list.emplace_back( package_data );
}
m_installedPanel->SetData( package_list, m_callback );
m_dialogNotebook->SetPageText(
1, wxString::Format( _( "Installed (%d)" ), (int) package_list.size() ) );
}
void DIALOG_PCM::OnApplyChangesClicked( wxCommandEvent& event )
{
if( m_pendingActions.size() == 0 )
return;
PCM_TASK_MANAGER task_manager( m_pcm );
for( const auto& action : m_pendingActions )
{
if( action.action == PPA_UNINSTALL )
task_manager.Uninstall( action.package );
else
task_manager.DownloadAndInstall( action.package, action.version, action.repository_id );
}
task_manager.RunQueue( this );
setInstalledPackages();
wxCommandEvent dummy;
OnDiscardChangesClicked( dummy );
if( !m_selectedRepositoryId.IsEmpty() )
setRepositoryData( m_selectedRepositoryId );
}
void DIALOG_PCM::OnDiscardChangesClicked( wxCommandEvent& event )
{
m_gridPendingActions->Freeze();
for( int i = m_pendingActions.size() - 1; i >= 0; i-- )
discardAction( i );
updatePendingActionsTab();
m_gridPendingActions->Thaw();
}
void DIALOG_PCM::OnDiscardActionClicked( wxCommandEvent& event )
{
wxArrayInt rows = m_gridPendingActions->GetSelectedRows();
std::sort( rows.begin(), rows.end(),
[]( const int& a, const int& b )
{
return a > b;
} );
m_gridPendingActions->Freeze();
for( int row : rows )
discardAction( row );
updatePendingActionsTab();
m_gridPendingActions->Thaw();
}
void DIALOG_PCM::discardAction( int aIndex )
{
m_gridPendingActions->DeleteRows( aIndex );
PENDING_ACTION action = m_pendingActions[aIndex];
PCM_PACKAGE_STATE state =
m_pcm->GetPackageState( action.repository_id, action.package.identifier );
m_installedPanel->SetPackageState( action.package.identifier, state );
for( const auto& entry : m_repositoryContentPanels )
entry.second->SetPackageState( action.package.identifier, state );
m_pendingActions.erase( m_pendingActions.begin() + aIndex );
}

View File

@ -0,0 +1,124 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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/>.
*/
#ifndef DIALOG_PCM_H_
#define DIALOG_PCM_H_
#include "core/wx_stl_compat.h"
#include "dialog_pcm_base.h"
#include "panel_package.h"
#include "panel_packages_view.h"
#include "pcm.h"
#include "pcm_data.h"
#include <vector>
/** Implementing pcm main dialog. */
class DIALOG_PCM : public DIALOG_PCM_BASE
{
public:
/** Constructor */
DIALOG_PCM( wxWindow* parent );
~DIALOG_PCM();
///< Closes the window, asks user confirmation if there are pending actions
void OnCloseClicked( wxCommandEvent& event ) override;
///< Opens repository management dialog, saves changes to PCM
void OnManageRepositoriesClicked( wxCommandEvent& event ) override;
///< Discards current repo cache, fetches it anew and displays
void OnRefreshClicked( wxCommandEvent& event ) override;
///< Opens file selection dialog and installs selected package archive
void OnInstallFromFileClicked( wxCommandEvent& event ) override;
///< Enqueues current pending actions in PCM_TASK_MANAGER and runs the queue
void OnApplyChangesClicked( wxCommandEvent& event ) override;
///< Discards all pending changes
void OnDiscardChangesClicked( wxCommandEvent& event ) override;
///< Switches to another repository
void OnRepositoryChoice( wxCommandEvent& event ) override;
///< Selects the whole row in the grid if a cell is clicked
void OnPendingActionsCellClicked( wxGridEvent& event ) override;
///< Discards selected pending actions
void OnDiscardActionClicked( wxCommandEvent& event ) override;
private:
/**
* @brief Gets package data from PCM and displays it on repository tab
*
* @param aRepositoryId id of the repository
*/
void setRepositoryData( const wxString& aRepositoryId );
///< Sets repository choice list values
void setRepositoryListFromPcm();
///< Updates pending actions tab caption and content-fits the grid
void updatePendingActionsTab();
///< Gets installed packages list from PCM and displays it on installed tab
void setInstalledPackages();
///< Discards specified pending action
void discardAction( int aIndex );
std::shared_ptr<PLUGIN_CONTENT_MANAGER> m_pcm;
ActionCallback m_callback;
PANEL_PACKAGES_VIEW* m_installedPanel;
std::unordered_map<PCM_PACKAGE_TYPE, PANEL_PACKAGES_VIEW*> m_repositoryContentPanels;
wxString m_selectedRepositoryId;
std::unordered_map<wxString, wxBitmap> m_packageBitmaps;
std::unordered_map<wxString, wxBitmap> m_installedBitmaps;
wxBitmap m_defaultBitmap;
struct PENDING_ACTION
{
PCM_PACKAGE_ACTION action;
wxString repository_id;
PCM_PACKAGE package;
wxString version;
PENDING_ACTION( const PCM_PACKAGE_ACTION& aAction, const wxString& aRepositoryId,
const PCM_PACKAGE& aPackage, const wxString& aVersion ) :
action( aAction ),
repository_id( aRepositoryId ), package( aPackage ), version( aVersion )
{
}
};
std::vector<PENDING_ACTION> m_pendingActions;
enum PendingActionsGridColumns
{
PENDING_COL_ACTION = 0,
PENDING_COL_NAME,
PENDING_COL_VERSION,
PENDING_COL_REPOSITORY
};
};
#endif // DIALOG_PCM_H_

View File

@ -0,0 +1,179 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "widgets/wx_grid.h"
#include "dialog_pcm_base.h"
///////////////////////////////////////////////////////////////////////////
DIALOG_PCM_BASE::DIALOG_PCM_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxSize( 650,500 ), wxDefaultSize );
wxBoxSizer* m_MainSizer;
m_MainSizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* m_TopSizer;
m_TopSizer = new wxBoxSizer( wxVERTICAL );
m_dialogNotebook = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
m_panelRepository = new wxPanel( m_dialogNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer4;
bSizer4 = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* bSizer6;
bSizer6 = new wxBoxSizer( wxHORIZONTAL );
wxArrayString m_choiceRepositoryChoices;
m_choiceRepository = new wxChoice( m_panelRepository, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceRepositoryChoices, 0 );
m_choiceRepository->SetSelection( 0 );
bSizer6->Add( m_choiceRepository, 1, wxALIGN_CENTER|wxALL, 5 );
m_buttonManage = new wxButton( m_panelRepository, wxID_ANY, _("Manage"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer6->Add( m_buttonManage, 0, wxALL, 5 );
bSizer4->Add( bSizer6, 0, wxEXPAND, 5 );
m_contentNotebook = new wxNotebook( m_panelRepository, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
bSizer4->Add( m_contentNotebook, 1, wxEXPAND | wxALL, 5 );
m_panelRepository->SetSizer( bSizer4 );
m_panelRepository->Layout();
bSizer4->Fit( m_panelRepository );
m_dialogNotebook->AddPage( m_panelRepository, _("Repository (%d)"), false );
m_panelInstalledHolder = new wxPanel( m_dialogNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer7;
bSizer7 = new wxBoxSizer( wxVERTICAL );
m_panelInstalledHolder->SetSizer( bSizer7 );
m_panelInstalledHolder->Layout();
bSizer7->Fit( m_panelInstalledHolder );
m_dialogNotebook->AddPage( m_panelInstalledHolder, _("Installed (%d)"), false );
m_panelPending = new wxPanel( m_dialogNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelPending->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
m_panelPending->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
wxBoxSizer* bSizer8;
bSizer8 = new wxBoxSizer( wxVERTICAL );
m_gridPendingActions = new WX_GRID( m_panelPending, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
// Grid
m_gridPendingActions->CreateGrid( 0, 4 );
m_gridPendingActions->EnableEditing( false );
m_gridPendingActions->EnableGridLines( true );
m_gridPendingActions->EnableDragGridSize( false );
m_gridPendingActions->SetMargins( 0, 0 );
// Columns
m_gridPendingActions->AutoSizeColumns();
m_gridPendingActions->EnableDragColMove( false );
m_gridPendingActions->EnableDragColSize( true );
m_gridPendingActions->SetColLabelSize( 30 );
m_gridPendingActions->SetColLabelValue( 0, _("Action") );
m_gridPendingActions->SetColLabelValue( 1, _("Package") );
m_gridPendingActions->SetColLabelValue( 2, _("Version") );
m_gridPendingActions->SetColLabelValue( 3, _("Repository") );
m_gridPendingActions->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Rows
m_gridPendingActions->EnableDragRowSize( false );
m_gridPendingActions->SetRowLabelSize( 0 );
m_gridPendingActions->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Label Appearance
// Cell Defaults
m_gridPendingActions->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP );
bSizer8->Add( m_gridPendingActions, 1, wxALL|wxEXPAND, 5 );
wxBoxSizer* bSizer9;
bSizer9 = new wxBoxSizer( wxHORIZONTAL );
m_discardActionButton = new wxBitmapButton( m_panelPending, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_discardActionButton->SetToolTip( _("Discard action") );
bSizer9->Add( m_discardActionButton, 0, wxLEFT|wxRIGHT, 5 );
bSizer9->Add( 0, 0, 1, wxEXPAND, 5 );
bSizer8->Add( bSizer9, 0, wxEXPAND, 0 );
m_panelPending->SetSizer( bSizer8 );
m_panelPending->Layout();
bSizer8->Fit( m_panelPending );
m_dialogNotebook->AddPage( m_panelPending, _("Pending (%d)"), true );
m_TopSizer->Add( m_dialogNotebook, 1, wxEXPAND | wxALL, 5 );
m_MainSizer->Add( m_TopSizer, 1, wxEXPAND, 5 );
wxBoxSizer* m_BottomSizer;
m_BottomSizer = new wxBoxSizer( wxHORIZONTAL );
m_refreshButton = new wxButton( this, wxID_ANY, _("Refresh"), wxDefaultPosition, wxDefaultSize, 0 );
m_BottomSizer->Add( m_refreshButton, 0, wxALL, 5 );
m_BottomSizer->Add( 0, 0, 1, wxEXPAND, 5 );
m_installLocalButton = new wxButton( this, wxID_ANY, _("Install from file..."), wxDefaultPosition, wxDefaultSize, 0 );
m_BottomSizer->Add( m_installLocalButton, 0, wxALL, 5 );
m_applyButton = new wxButton( this, wxID_ANY, _("Apply changes"), wxDefaultPosition, wxDefaultSize, 0 );
m_BottomSizer->Add( m_applyButton, 0, wxALL, 5 );
m_discardButton = new wxButton( this, wxID_ANY, _("Discard changes"), wxDefaultPosition, wxDefaultSize, 0 );
m_BottomSizer->Add( m_discardButton, 0, wxALL, 5 );
m_closeButton = new wxButton( this, wxID_CANCEL, _("Close"), wxDefaultPosition, wxDefaultSize, 0 );
m_BottomSizer->Add( m_closeButton, 0, wxALL, 5 );
m_MainSizer->Add( m_BottomSizer, 0, wxEXPAND, 5 );
this->SetSizer( m_MainSizer );
this->Layout();
this->Centre( wxBOTH );
// Connect Events
m_choiceRepository->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PCM_BASE::OnRepositoryChoice ), NULL, this );
m_buttonManage->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnManageRepositoriesClicked ), NULL, this );
m_gridPendingActions->Connect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( DIALOG_PCM_BASE::OnPendingActionsCellClicked ), NULL, this );
m_discardActionButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnDiscardActionClicked ), NULL, this );
m_refreshButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnRefreshClicked ), NULL, this );
m_installLocalButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnInstallFromFileClicked ), NULL, this );
m_applyButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnApplyChangesClicked ), NULL, this );
m_discardButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnDiscardChangesClicked ), NULL, this );
m_closeButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnCloseClicked ), NULL, this );
}
DIALOG_PCM_BASE::~DIALOG_PCM_BASE()
{
// Disconnect Events
m_choiceRepository->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PCM_BASE::OnRepositoryChoice ), NULL, this );
m_buttonManage->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnManageRepositoriesClicked ), NULL, this );
m_gridPendingActions->Disconnect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( DIALOG_PCM_BASE::OnPendingActionsCellClicked ), NULL, this );
m_discardActionButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnDiscardActionClicked ), NULL, this );
m_refreshButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnRefreshClicked ), NULL, this );
m_installLocalButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnInstallFromFileClicked ), NULL, this );
m_applyButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnApplyChangesClicked ), NULL, this );
m_discardButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnDiscardChangesClicked ), NULL, this );
m_closeButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_BASE::OnCloseClicked ), NULL, this );
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,77 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#pragma once
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
class WX_GRID;
#include "dialog_shim.h"
#include <wx/string.h>
#include <wx/choice.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/button.h>
#include <wx/sizer.h>
#include <wx/notebook.h>
#include <wx/panel.h>
#include <wx/grid.h>
#include <wx/bmpbuttn.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_PCM_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_PCM_BASE : public DIALOG_SHIM
{
private:
protected:
wxNotebook* m_dialogNotebook;
wxPanel* m_panelRepository;
wxChoice* m_choiceRepository;
wxButton* m_buttonManage;
wxNotebook* m_contentNotebook;
wxPanel* m_panelInstalledHolder;
wxPanel* m_panelPending;
WX_GRID* m_gridPendingActions;
wxBitmapButton* m_discardActionButton;
wxButton* m_refreshButton;
wxButton* m_installLocalButton;
wxButton* m_applyButton;
wxButton* m_discardButton;
wxButton* m_closeButton;
// Virtual event handlers, overide them in your derived class
virtual void OnRepositoryChoice( wxCommandEvent& event ) { event.Skip(); }
virtual void OnManageRepositoriesClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnPendingActionsCellClicked( wxGridEvent& event ) { event.Skip(); }
virtual void OnDiscardActionClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnRefreshClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnInstallFromFileClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnApplyChangesClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnDiscardChangesClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnCloseClicked( wxCommandEvent& event ) { event.Skip(); }
public:
DIALOG_PCM_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Plugin And Content Manager"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 860,620 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~DIALOG_PCM_BASE();
};

View File

@ -0,0 +1,157 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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 "dialog_pcm_progress.h"
#include <wx/msgdlg.h>
#define GAUGE_RANGE 1000
DIALOG_PCM_PROGRESS::DIALOG_PCM_PROGRESS( wxWindow* parent, bool aShowDownloadSection ) :
DIALOG_PCM_PROGRESS_BASE( parent )
#if wxCHECK_VERSION( 3, 1, 0 )
,
m_appProgressIndicator( parent->GetParent(), GAUGE_RANGE )
#endif
{
#if wxCHECK_VERSION( 3, 1, 0 )
m_appProgressIndicator.Pulse();
#endif
m_cancelled.store( false );
m_reporter->SetImmediateMode();
m_downloadGauge->SetRange( GAUGE_RANGE );
m_overallGauge->SetRange( GAUGE_RANGE );
m_overallPhases = 1;
if( !aShowDownloadSection )
m_panelDownload->Hide();
}
void DIALOG_PCM_PROGRESS::OnCancelClicked( wxCommandEvent& event )
{
m_cancelled.store( true );
m_buttonCancel->Disable();
}
void DIALOG_PCM_PROGRESS::OnCloseClicked( wxCommandEvent& event )
{
EndModal( wxID_OK );
}
void DIALOG_PCM_PROGRESS::Report( const wxString& aText, SEVERITY aSeverity )
{
CallAfter(
[=]
{
m_reporter->Report( aText, aSeverity );
} );
}
void DIALOG_PCM_PROGRESS::SetDownloadProgress( uint64_t aDownloaded, uint64_t aTotal )
{
if( aDownloaded > aTotal )
aDownloaded = aTotal;
int value = 0;
if( aTotal > 0 )
value = aDownloaded * GAUGE_RANGE / aTotal;
CallAfter(
[=]
{
m_downloadText->SetLabel( wxString::Format( _( "Downloaded %lld/%lld Kb" ),
toKb( aDownloaded ), toKb( aTotal ) ) );
m_downloadGauge->SetValue( value );
} );
}
uint64_t DIALOG_PCM_PROGRESS::toKb( uint64_t aValue )
{
return ( aValue + 1023 ) / 1024;
}
void DIALOG_PCM_PROGRESS::SetOverallProgress( uint64_t aProgress, uint64_t aTotal )
{
double current = ( m_currentPhase + aProgress / (double) aTotal ) / m_overallPhases;
if( current > 1.0 )
current = 1.0;
int value = current * GAUGE_RANGE;
CallAfter(
[=]
{
m_overallGauge->SetValue( value );
#if wxCHECK_VERSION( 3, 1, 0 )
m_appProgressIndicator.SetValue( value );
#endif
} );
}
void DIALOG_PCM_PROGRESS::SetOverallProgressPhases( int aPhases )
{
m_currentPhase = 0;
m_overallPhases = aPhases;
}
void DIALOG_PCM_PROGRESS::AdvanceOverallProgressPhase()
{
m_currentPhase++;
SetOverallProgress( 0, 1 );
}
void DIALOG_PCM_PROGRESS::SetFinished()
{
CallAfter(
[this]
{
m_buttonCancel->Disable();
m_buttonClose->Enable();
} );
}
void DIALOG_PCM_PROGRESS::SetDownloadsFinished()
{
CallAfter(
[this]
{
m_downloadText->SetLabel( _( "All downloads finished" ) );
} );
}
bool DIALOG_PCM_PROGRESS::IsCancelled()
{
return m_cancelled.load();
}

View File

@ -0,0 +1,87 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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/>.
*/
#ifndef DIALOG_PCM_PROGRESS_H_
#define DIALOG_PCM_PROGRESS_H_
#include "dialog_pcm_progress_base.h"
#include "reporter.h"
#include <atomic>
#if wxCHECK_VERSION( 3, 1, 0 )
#include <wx/appprogress.h>
#endif
/**
* @brief Progress dialog for PCM system
*
* This dialog is designed to work with PCM_TASK_MANAGER's threading system.
* Some of it's methods are safe to call from a non-UI thread.
*
* SetOverallProgress(), SetOverallProgressPhases() and AdvanceOverallProgressPhase()
* are meant to be called from the same thread.
*/
class DIALOG_PCM_PROGRESS : public DIALOG_PCM_PROGRESS_BASE
{
protected:
// Handlers for DIALOG_PCM_PROGRESS_BASE events.
void OnCancelClicked( wxCommandEvent& event ) override;
void OnCloseClicked( wxCommandEvent& event ) override;
public:
/** Constructor */
DIALOG_PCM_PROGRESS( wxWindow* parent, bool aShowDownloadSection = true );
///< Thread safe. Adds a message to detailed report window.
void Report( const wxString& aText, SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED );
///< Thread safe. Sets current download progress gauge and text.
void SetDownloadProgress( uint64_t aDownloaded, uint64_t aTotal );
///< Safe to call from non-UI thread. Sets current overall progress gauge.
void SetOverallProgress( uint64_t aProgress, uint64_t aTotal );
///< Safe to call from non-UI thread. Sets number of phases for overall progress gauge.
void SetOverallProgressPhases( int aPhases );
///< Safe to call from non-UI thread. Increments current phase by one.
void AdvanceOverallProgressPhase();
///< Thread safe. Updates download text.
void SetDownloadsFinished();
///< Thread safe. Disables cancel button, enables close button.
void SetFinished();
///< Thread safe. Return true if cancel was clicked.
bool IsCancelled();
private:
static uint64_t toKb( uint64_t aValue );
int m_currentPhase;
int m_overallPhases;
std::atomic_bool m_cancelled;
#if wxCHECK_VERSION( 3, 1, 0 )
wxAppProgressIndicator m_appProgressIndicator;
#endif
};
#endif // DIALOG_PCM_PROGRESS_H_

View File

@ -0,0 +1,100 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "dialog_pcm_progress_base.h"
///////////////////////////////////////////////////////////////////////////
DIALOG_PCM_PROGRESS_BASE::DIALOG_PCM_PROGRESS_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxSize( 400,300 ), wxDefaultSize );
wxBoxSizer* bSizer1;
bSizer1 = new wxBoxSizer( wxVERTICAL );
m_panelDownload = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxStaticBoxSizer* sbSizer1;
sbSizer1 = new wxStaticBoxSizer( new wxStaticBox( m_panelDownload, wxID_ANY, _("Download progress") ), wxVERTICAL );
m_downloadText = new wxStaticText( sbSizer1->GetStaticBox(), wxID_ANY, _("Waiting..."), wxDefaultPosition, wxDefaultSize, 0 );
m_downloadText->Wrap( -1 );
sbSizer1->Add( m_downloadText, 0, wxALL, 5 );
m_downloadGauge = new wxGauge( sbSizer1->GetStaticBox(), wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL|wxGA_SMOOTH );
m_downloadGauge->SetValue( 0 );
sbSizer1->Add( m_downloadGauge, 1, wxALL|wxEXPAND, 5 );
m_panelDownload->SetSizer( sbSizer1 );
m_panelDownload->Layout();
sbSizer1->Fit( m_panelDownload );
bSizer1->Add( m_panelDownload, 0, wxEXPAND|wxLEFT|wxRIGHT|wxTOP, 5 );
m_panel2 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer3;
bSizer3 = new wxBoxSizer( wxVERTICAL );
wxStaticBoxSizer* sbSizer2;
sbSizer2 = new wxStaticBoxSizer( new wxStaticBox( m_panel2, wxID_ANY, _("Overall progress") ), wxVERTICAL );
m_overallGauge = new wxGauge( sbSizer2->GetStaticBox(), wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL );
m_overallGauge->SetValue( 0 );
sbSizer2->Add( m_overallGauge, 1, wxALL|wxEXPAND, 5 );
bSizer3->Add( sbSizer2, 0, wxEXPAND, 5 );
wxStaticBoxSizer* sbSizer3;
sbSizer3 = new wxStaticBoxSizer( new wxStaticBox( m_panel2, wxID_ANY, _("Details") ), wxVERTICAL );
m_reporter = new WX_HTML_REPORT_BOX( sbSizer3->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxSize( -1,200 ), wxHW_SCROLLBAR_AUTO );
sbSizer3->Add( m_reporter, 1, wxALL|wxEXPAND, 5 );
bSizer3->Add( sbSizer3, 1, wxEXPAND, 5 );
wxBoxSizer* bSizer2;
bSizer2 = new wxBoxSizer( wxHORIZONTAL );
bSizer2->Add( 0, 0, 1, wxEXPAND, 5 );
m_buttonCancel = new wxButton( m_panel2, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer2->Add( m_buttonCancel, 0, wxALL, 5 );
m_buttonClose = new wxButton( m_panel2, wxID_OK, _("Close"), wxDefaultPosition, wxDefaultSize, 0 );
m_buttonClose->SetDefault();
m_buttonClose->Enable( false );
bSizer2->Add( m_buttonClose, 0, wxALL, 5 );
bSizer3->Add( bSizer2, 0, wxEXPAND, 5 );
m_panel2->SetSizer( bSizer3 );
m_panel2->Layout();
bSizer3->Fit( m_panel2 );
bSizer1->Add( m_panel2, 1, wxEXPAND | wxALL, 5 );
this->SetSizer( bSizer1 );
this->Layout();
// Connect Events
m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_PROGRESS_BASE::OnCancelClicked ), NULL, this );
m_buttonClose->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_PROGRESS_BASE::OnCloseClicked ), NULL, this );
}
DIALOG_PCM_PROGRESS_BASE::~DIALOG_PCM_PROGRESS_BASE()
{
// Disconnect Events
m_buttonCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_PROGRESS_BASE::OnCancelClicked ), NULL, this );
m_buttonClose->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PCM_PROGRESS_BASE::OnCloseClicked ), NULL, this );
}

View File

@ -0,0 +1,635 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="15" />
<object class="Project" expanded="1">
<property name="class_decoration">; </property>
<property name="code_generation">C++</property>
<property name="disconnect_events">1</property>
<property name="disconnect_mode">source_name</property>
<property name="disconnect_php_events">0</property>
<property name="disconnect_python_events">0</property>
<property name="embedded_files_path">res</property>
<property name="encoding">UTF-8</property>
<property name="event_generation">connect</property>
<property name="file">dialog_pcm_progress_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="indent_with_spaces"></property>
<property name="internationalize">1</property>
<property name="name">dialog_pcm_progress_base</property>
<property name="namespace"></property>
<property name="path">.</property>
<property name="precompiled_header"></property>
<property name="relative_path">1</property>
<property name="skip_lua_events">1</property>
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="ui_table">UI</property>
<property name="use_enum">0</property>
<property name="use_microsoft_bom">0</property>
<object class="Dialog" expanded="1">
<property name="aui_managed">0</property>
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
<property name="bg"></property>
<property name="center"></property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="enabled">1</property>
<property name="event_handler">impl_virtual</property>
<property name="extra_style"></property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="minimum_size">400,300</property>
<property name="name">DIALOG_PCM_PROGRESS_BASE</property>
<property name="pos"></property>
<property name="size">500,500</property>
<property name="style">wxCAPTION</property>
<property name="subclass">; ; forward_declare</property>
<property name="title">Applying package changes</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer1</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxLEFT|wxRIGHT|wxTOP</property>
<property name="proportion">0</property>
<object class="wxPanel" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_panelDownload</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
<object class="wxStaticBoxSizer" expanded="1">
<property name="id">wxID_ANY</property>
<property name="label">Download progress</property>
<property name="minimum_size"></property>
<property name="name">sbSizer1</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Waiting...</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_downloadText</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxGauge" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_downloadGauge</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="range">100</property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxGA_HORIZONTAL|wxGA_SMOOTH</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value">0</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND | wxALL</property>
<property name="proportion">1</property>
<object class="wxPanel" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_panel2</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer3</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxStaticBoxSizer" expanded="1">
<property name="id">wxID_ANY</property>
<property name="label">Overall progress</property>
<property name="minimum_size"></property>
<property name="name">sbSizer2</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxGauge" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_overallGauge</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="range">100</property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxGA_HORIZONTAL</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value">0</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxStaticBoxSizer" expanded="1">
<property name="id">wxID_ANY</property>
<property name="label">Details</property>
<property name="minimum_size"></property>
<property name="name">sbSizer3</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxHtmlWindow" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_reporter</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size">-1,200</property>
<property name="style">wxHW_SCROLLBAR_AUTO</property>
<property name="subclass">WX_HTML_REPORT_BOX; dialogs/wx_html_report_box.h; Not forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer2</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="1">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxButton" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_CANCEL</property>
<property name="label">Cancel</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_buttonCancel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">OnCancelClicked</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxButton" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">1</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">0</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_OK</property>
<property name="label">Close</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_buttonClose</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">OnCloseClicked</event>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</wxFormBuilder_Project>

View File

@ -0,0 +1,62 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#pragma once
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
#include "dialogs/wx_html_report_box.h"
#include <wx/string.h>
#include <wx/stattext.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/gauge.h>
#include <wx/sizer.h>
#include <wx/statbox.h>
#include <wx/panel.h>
#include <wx/html/htmlwin.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/button.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_PCM_PROGRESS_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_PCM_PROGRESS_BASE : public wxDialog
{
private:
protected:
wxPanel* m_panelDownload;
wxStaticText* m_downloadText;
wxGauge* m_downloadGauge;
wxPanel* m_panel2;
wxGauge* m_overallGauge;
WX_HTML_REPORT_BOX* m_reporter;
wxButton* m_buttonCancel;
wxButton* m_buttonClose;
// Virtual event handlers, overide them in your derived class
virtual void OnCancelClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnCloseClicked( wxCommandEvent& event ) { event.Skip(); }
public:
DIALOG_PCM_PROGRESS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Applying package changes"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 500,500 ), long style = wxCAPTION );
~DIALOG_PCM_PROGRESS_BASE();
};

View File

@ -0,0 +1,161 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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 <wx/dcclient.h>
#include "panel_package.h"
PANEL_PACKAGE::PANEL_PACKAGE( wxWindow* parent, const ActionCallback& aCallback,
const PACKAGE_VIEW_DATA& aData ) :
PANEL_PACKAGE_BASE( parent ),
m_actionCallback( aCallback ), m_data( aData )
{
// Propagate clicks on static elements to the panel handler.
m_name->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( PANEL_PACKAGE::OnClick ), NULL, this );
m_desc->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( PANEL_PACKAGE::OnClick ), NULL, this );
m_bitmap->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( PANEL_PACKAGE::OnClick ), NULL, this );
wxColour bgColor = wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK );
m_desc->SetBackgroundColour( bgColor );
m_name->SetBackgroundColour( bgColor );
m_bitmap->SetBackgroundColour( bgColor );
m_name->SetLabel( m_data.package.name );
m_bitmap->SetBitmap( *m_data.bitmap );
// Set min width to 0 otherwise wxStaticLabel really doesn't want to shrink on resize
m_desc->SetMinSize( wxSize( 0, -1 ) );
Layout();
m_desc->SetLabel( m_data.package.description );
m_desc->Wrap( m_desc->GetClientSize().GetWidth() );
SetState( m_data.state );
}
void PANEL_PACKAGE::OnSize( wxSizeEvent& event )
{
Layout();
m_desc->SetLabel( m_data.package.description );
m_desc->Wrap( m_desc->GetClientSize().GetWidth() );
}
void PANEL_PACKAGE::SetState( PCM_PACKAGE_STATE aState )
{
m_data.state = aState;
switch( aState )
{
case PCM_PACKAGE_STATE::PPS_AVAILABLE:
m_button->SetLabel( _( "Install" ) );
m_button->Enable();
break;
case PCM_PACKAGE_STATE::PPS_UNAVAILABLE:
m_button->SetLabel( _( "Install" ) );
m_button->Disable();
break;
case PCM_PACKAGE_STATE::PPS_INSTALLED:
m_button->SetLabel( _( "Uninstall" ) );
m_button->Enable();
break;
case PCM_PACKAGE_STATE::PPS_PENDING_INSTALL:
m_button->SetLabel( _( "Pending install" ) );
m_button->Disable();
break;
case PCM_PACKAGE_STATE::PPS_PENDING_UNINSTALL:
m_button->SetLabel( _( "Pending uninstall" ) );
m_button->Disable();
break;
}
// Relayout to change button size to fit the label.
wxSizeEvent dummy;
OnSize( dummy );
}
void PANEL_PACKAGE::OnButtonClicked( wxCommandEvent& event )
{
// Versions are already presorted in descending order
if( m_data.state == PPS_AVAILABLE )
{
// Find last stable compatible version
auto ver_it = std::find_if( m_data.package.versions.begin(), m_data.package.versions.end(),
[]( const PACKAGE_VERSION& ver )
{
return ver.compatible && ver.status == PVS_STABLE;
} );
// If not found then find any compatible version
if( ver_it == m_data.package.versions.end() )
ver_it = std::find_if( m_data.package.versions.begin(), m_data.package.versions.end(),
[]( const PACKAGE_VERSION& ver )
{
return ver.compatible;
} );
if( ver_it == m_data.package.versions.end() )
return; // Shouldn't happen
m_actionCallback( m_data, PPA_INSTALL, ver_it->version );
}
else
{
m_actionCallback( m_data, PPA_UNINSTALL, m_data.current_version );
}
}
void PANEL_PACKAGE::SetSelectCallback( const std::function<void()>& aCallback )
{
m_selectCallback = aCallback;
}
void PANEL_PACKAGE::OnClick( wxMouseEvent& event )
{
m_selectCallback();
}
void PANEL_PACKAGE::OnPaint( wxPaintEvent& event )
{
wxPaintDC dc( this );
dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK ) );
dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ), 1 ) );
if( m_selected )
dc.SetPen( wxPen( *wxBLACK, 3 ) );
dc.DrawRectangle( wxPoint( 0, 0 ), GetClientSize() );
if( !m_selected )
dc.DrawLine( 0, 0, GetClientSize().GetX(), 0 );
}
void PANEL_PACKAGE::SetSelected( bool aSelected )
{
m_selected = aSelected;
Refresh();
}

View File

@ -0,0 +1,88 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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/>.
*/
#ifndef PANEL_PACKAGE_H_
#define PANEL_PACKAGE_H_
#include <functional>
#include "panel_package_base.h"
#include "pcm.h"
///< Collection of data relevant to the package display panel
struct PACKAGE_VIEW_DATA
{
const PCM_PACKAGE package;
wxBitmap* bitmap;
PCM_PACKAGE_STATE state;
wxString repository_id;
wxString repository_name;
wxString current_version;
PACKAGE_VIEW_DATA( const PCM_PACKAGE aPackage ) : package( std::move( aPackage ) ){};
PACKAGE_VIEW_DATA( const PCM_INSTALLATION_ENTRY& aEntry ) :
package( std::move( aEntry.package ) )
{
state = PPS_INSTALLED;
repository_id = aEntry.repository_id;
repository_name = aEntry.repository_name;
current_version = aEntry.current_version;
}
};
///< Callback for (un)install button
using ActionCallback = std::function<void( const PACKAGE_VIEW_DATA& aData,
PCM_PACKAGE_ACTION aAction, const wxString& aVersion )>;
class PANEL_PACKAGE : public PANEL_PACKAGE_BASE
{
public:
PANEL_PACKAGE( wxWindow* parent, const ActionCallback& aCallback,
const PACKAGE_VIEW_DATA& aData );
///< Sets callback for OnClick action
void SetSelectCallback( const std::function<void()>& aCallback );
///< Marks panel as selected
void SetSelected( bool aSelected );
void OnButtonClicked( wxCommandEvent& event ) override;
///< Changes state of the (un)install button
void SetState( PCM_PACKAGE_STATE aState );
///< Called when anywhere on the panel is clicked (except install button)
void OnClick( wxMouseEvent& event ) override;
void OnSize( wxSizeEvent& event ) override;
const PACKAGE_VIEW_DATA& GetPackageData() const { return m_data; };
private:
void OnPaint( wxPaintEvent& event ) override;
std::function<void()> m_selectCallback;
bool m_selected = false;
const ActionCallback& m_actionCallback;
PACKAGE_VIEW_DATA m_data;
};
#endif // PANEL_PACKAGE_H_

View File

@ -0,0 +1,98 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "panel_package_base.h"
///////////////////////////////////////////////////////////////////////////
PANEL_PACKAGE_BASE::PANEL_PACKAGE_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : wxPanel( parent, id, pos, size, style, name )
{
wxBoxSizer* bSizer1;
bSizer1 = new wxBoxSizer( wxHORIZONTAL );
wxBoxSizer* bSizer5;
bSizer5 = new wxBoxSizer( wxVERTICAL );
bSizer5->SetMinSize( wxSize( 70,-1 ) );
bSizer5->Add( 0, 0, 1, wxEXPAND, 5 );
wxBoxSizer* bSizer6;
bSizer6 = new wxBoxSizer( wxHORIZONTAL );
bSizer6->Add( 0, 0, 1, wxEXPAND, 5 );
m_bitmap = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
bSizer6->Add( m_bitmap, 0, wxALL, 5 );
bSizer6->Add( 0, 0, 1, wxEXPAND, 5 );
bSizer5->Add( bSizer6, 1, wxEXPAND, 5 );
bSizer5->Add( 0, 0, 1, wxEXPAND, 5 );
bSizer1->Add( bSizer5, 0, wxEXPAND, 5 );
wxBoxSizer* bSizer2;
bSizer2 = new wxBoxSizer( wxVERTICAL );
m_name = new wxStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END );
m_name->Wrap( -1 );
m_name->SetFont( wxFont( 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer2->Add( m_name, 0, wxEXPAND|wxLEFT|wxRIGHT|wxTOP, 5 );
wxBoxSizer* bSizer3;
bSizer3 = new wxBoxSizer( wxHORIZONTAL );
m_desc = new wxStaticText( this, wxID_ANY, _("MyLabel"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END|wxST_NO_AUTORESIZE );
m_desc->Wrap( -1 );
bSizer3->Add( m_desc, 1, wxALL|wxEXPAND, 5 );
wxBoxSizer* bSizer4;
bSizer4 = new wxBoxSizer( wxVERTICAL );
bSizer4->Add( 0, 0, 1, wxEXPAND, 5 );
m_button = new wxButton( this, wxID_ANY, _("Install"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer4->Add( m_button, 0, wxALL, 5 );
bSizer3->Add( bSizer4, 0, wxEXPAND, 5 );
bSizer2->Add( bSizer3, 1, wxEXPAND, 5 );
bSizer1->Add( bSizer2, 1, wxEXPAND, 5 );
this->SetSizer( bSizer1 );
this->Layout();
// Connect Events
this->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( PANEL_PACKAGE_BASE::OnClick ) );
this->Connect( wxEVT_PAINT, wxPaintEventHandler( PANEL_PACKAGE_BASE::OnPaint ) );
this->Connect( wxEVT_SIZE, wxSizeEventHandler( PANEL_PACKAGE_BASE::OnSize ) );
m_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGE_BASE::OnButtonClicked ), NULL, this );
}
PANEL_PACKAGE_BASE::~PANEL_PACKAGE_BASE()
{
// Disconnect Events
this->Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( PANEL_PACKAGE_BASE::OnClick ) );
this->Disconnect( wxEVT_PAINT, wxPaintEventHandler( PANEL_PACKAGE_BASE::OnPaint ) );
this->Disconnect( wxEVT_SIZE, wxSizeEventHandler( PANEL_PACKAGE_BASE::OnSize ) );
m_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGE_BASE::OnButtonClicked ), NULL, this );
}

View File

@ -0,0 +1,421 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="15" />
<object class="Project" expanded="1">
<property name="class_decoration">; </property>
<property name="code_generation">C++</property>
<property name="disconnect_events">1</property>
<property name="disconnect_mode">source_name</property>
<property name="disconnect_php_events">0</property>
<property name="disconnect_python_events">0</property>
<property name="embedded_files_path"></property>
<property name="encoding">UTF-8</property>
<property name="event_generation">connect</property>
<property name="file">panel_package_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="indent_with_spaces"></property>
<property name="internationalize">1</property>
<property name="name">panel_package_base</property>
<property name="namespace"></property>
<property name="path">./</property>
<property name="precompiled_header"></property>
<property name="relative_path">1</property>
<property name="skip_lua_events">1</property>
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="ui_table">UI</property>
<property name="use_enum">0</property>
<property name="use_microsoft_bom">0</property>
<object class="Panel" expanded="1">
<property name="aui_managed">0</property>
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
<property name="bg"></property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="enabled">1</property>
<property name="event_handler">impl_virtual</property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">PANEL_PACKAGE_BASE</property>
<property name="pos"></property>
<property name="size">-1,84</property>
<property name="subclass">; ; forward_declare</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxBORDER_NONE|wxTAB_TRAVERSAL</property>
<event name="OnLeftDown">OnClick</event>
<event name="OnPaint">OnPaint</event>
<event name="OnSize">OnSize</event>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer1</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size">70,-1</property>
<property name="name">bSizer5</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="1">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer6</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="1">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticBitmap" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_bitmap</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="1">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="1">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer2</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxLEFT|wxRIGHT|wxTOP</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font">,90,92,12,70,0</property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_name</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxST_ELLIPSIZE_END</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer3</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">MyLabel</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_desc</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxST_ELLIPSIZE_END|wxST_NO_AUTORESIZE</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer4</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="1">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxButton" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Install</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_button</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">OnButtonClicked</event>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</wxFormBuilder_Project>

View File

@ -0,0 +1,56 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#pragma once
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/statbmp.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/button.h>
#include <wx/panel.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class PANEL_PACKAGE_BASE
///////////////////////////////////////////////////////////////////////////////
class PANEL_PACKAGE_BASE : public wxPanel
{
private:
protected:
wxStaticBitmap* m_bitmap;
wxStaticText* m_name;
wxStaticText* m_desc;
wxButton* m_button;
// Virtual event handlers, overide them in your derived class
virtual void OnClick( wxMouseEvent& event ) { event.Skip(); }
virtual void OnPaint( wxPaintEvent& event ) { event.Skip(); }
virtual void OnSize( wxSizeEvent& event ) { event.Skip(); }
virtual void OnButtonClicked( wxCommandEvent& event ) { event.Skip(); }
public:
PANEL_PACKAGE_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,84 ), long style = wxBORDER_NONE|wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
~PANEL_PACKAGE_BASE();
};

View File

@ -0,0 +1,507 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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 "panel_packages_view.h"
#include "bitmaps.h"
#include "grid_tricks.h"
#include "kicad_settings.h"
#include "pgm_base.h"
#include "settings/settings_manager.h"
#include <cmath>
#include <fstream>
#include <wx/filedlg.h>
#include <wx/font.h>
#include <wx/tokenzr.h>
#define GRID_CELL_MARGIN 4
std::unordered_map<PCM_PACKAGE_VERSION_STATUS, wxString> PANEL_PACKAGES_VIEW::STATUS_ENUM_TO_STR = {
{ PVS_INVALID, "invalid" },
{ PVS_STABLE, "stable" },
{ PVS_TESTING, "testing" },
{ PVS_DEVELOPMENT, "development" },
{ PVS_DEPRECATED, "deprecated" }
};
PANEL_PACKAGES_VIEW::PANEL_PACKAGES_VIEW( wxWindow* parent,
std::shared_ptr<PLUGIN_CONTENT_MANAGER> aPcm ) :
PANEL_PACKAGES_VIEW_BASE( parent ),
m_pcm( aPcm )
{
m_searchBitmap->SetBitmap( KiBitmap( BITMAPS::find, 24 ) );
m_gridVersions->PushEventHandler( new GRID_TRICKS( m_gridVersions ) );
for( int col = 0; col < m_gridVersions->GetNumberCols(); col++ )
{
const wxString& heading = m_gridVersions->GetColLabelValue( col );
int headingWidth = GetTextExtent( heading ).x + 2 * GRID_CELL_MARGIN;
// Set the minimal width to the column label size.
m_gridVersions->SetColMinimalWidth( col, headingWidth );
m_gridVersions->SetColSize( col,
m_gridVersions->GetVisibleWidth( col, true, true, false ) );
}
m_infoText->SetBackgroundColour( wxStaticText::GetClassDefaultAttributes().colBg );
// Try to disable the caret on platforms that show it even in read-only controls
m_infoText->Bind( wxEVT_SET_FOCUS,
[&]( wxFocusEvent& event )
{
wxCaret* caret = m_infoText->GetCaret();
if( caret )
caret->Hide();
} );
ClearData();
}
PANEL_PACKAGES_VIEW::~PANEL_PACKAGES_VIEW()
{
m_gridVersions->PopEventHandler( true );
}
void PANEL_PACKAGES_VIEW::ClearData()
{
unsetPackageDetails();
m_currentSelected = nullptr;
m_packagePanels.clear();
m_packageInitialOrder.clear();
m_packageListWindow->GetSizer()->Clear( true ); // Delete panels
m_packageListWindow->GetSizer()->FitInside( m_packageListWindow );
m_packageListWindow->Layout();
}
void PANEL_PACKAGES_VIEW::SetData( const std::vector<PACKAGE_VIEW_DATA>& aPackageData,
ActionCallback aCallback )
{
m_actionCallback = aCallback;
ClearData();
wxSizer* sizer = m_packageListWindow->GetSizer();
for( const PACKAGE_VIEW_DATA& data : aPackageData )
{
PANEL_PACKAGE* package_panel =
new PANEL_PACKAGE( m_packageListWindow, m_actionCallback, data );
package_panel->SetSelectCallback(
[package_panel, this]()
{
if( m_currentSelected && m_currentSelected != package_panel )
m_currentSelected->SetSelected( false );
package_panel->SetSelected( true );
m_currentSelected = package_panel;
setPackageDetails( package_panel->GetPackageData() );
Layout();
} );
m_packagePanels.insert( { data.package.identifier, package_panel } );
m_packageInitialOrder.push_back( data.package.identifier );
}
updatePackageList();
}
void PANEL_PACKAGES_VIEW::setPackageDetails( const PACKAGE_VIEW_DATA& aPackageData )
{
const PCM_PACKAGE& package = aPackageData.package;
// Details
m_infoText->Clear();
m_infoText->BeginParagraphSpacing( 0, 30 );
m_infoText->WriteText( package.description_full );
m_infoText->Newline();
m_infoText->EndParagraphSpacing();
m_infoText->BeginFontSize( floor( m_infoText->GetDefaultStyle().GetFontSize() * 1.1 ) );
m_infoText->WriteText( _( "Metadata" ) );
m_infoText->Newline();
m_infoText->EndFontSize();
m_infoText->BeginParagraphSpacing( 0, 10 );
m_infoText->BeginSymbolBullet( wxString::FromUTF8( u8"\u25CF" ), 30, 40 );
m_infoText->WriteText(
wxString::Format( _( "Package identifier: %s\n" ), package.identifier ) );
m_infoText->WriteText( wxString::Format( _( "License: %s\n" ), package.license ) );
if( package.tags.size() > 0 )
{
wxString tags_str;
for( const wxString& tag : package.tags )
{
if( !tags_str.IsEmpty() )
tags_str += ", ";
tags_str += tag;
}
m_infoText->WriteText( wxString::Format( _( "Tags: %s\n" ), tags_str ) );
}
const auto write_contact = [&]( const wxString& type, const PCM_CONTACT& contact )
{
m_infoText->WriteText( wxString::Format( "%s: %s\n", type, contact.name ) );
m_infoText->BeginLeftIndent( 60, 40 );
for( const auto& entry : contact.contact )
m_infoText->WriteText( wxString::Format( "%s: %s\n", entry.first, entry.second ) );
m_infoText->EndLeftIndent();
};
write_contact( _( "Author" ), package.author );
if( package.maintainer )
write_contact( _( "Maintainer" ), package.maintainer.get() );
if( package.resources.size() > 0 )
{
m_infoText->WriteText( _( "Resources" ) );
m_infoText->Newline();
m_infoText->BeginLeftIndent( 60, 40 );
for( const auto& entry : package.resources )
{
m_infoText->WriteText( wxString::Format( "%s: %s\n", entry.first, entry.second ) );
}
m_infoText->EndLeftIndent();
}
m_infoText->EndSymbolBullet();
m_infoText->EndParagraphSpacing();
// Versions table
m_gridVersions->Freeze();
if( m_gridVersions->GetNumberRows() != 0 )
m_gridVersions->DeleteRows( 0, m_gridVersions->GetNumberRows() );
int row = 0;
wxString current_version;
if( aPackageData.state == PPS_INSTALLED )
current_version = m_pcm->GetInstalledPackageVersion( package.identifier );
wxFont bold_font = m_gridVersions->GetDefaultCellFont().Bold();
for( const PACKAGE_VERSION& version : package.versions )
{
if( !version.compatible && !m_showAllVersions->IsChecked() )
continue;
m_gridVersions->InsertRows( row );
m_gridVersions->SetCellValue( row, COL_VERSION, version.version );
m_gridVersions->SetCellValue( row, COL_DOWNLOAD_SIZE,
toHumanReadableSize( version.download_size ) );
m_gridVersions->SetCellValue( row, COL_INSTALL_SIZE,
toHumanReadableSize( version.install_size ) );
m_gridVersions->SetCellValue( row, COL_COMPATIBILITY,
version.compatible ? wxT( "\u2714" ) : wxEmptyString );
m_gridVersions->SetCellValue( row, COL_STATUS, STATUS_ENUM_TO_STR.at( version.status ) );
m_gridVersions->SetCellAlignment( row, COL_COMPATIBILITY, wxALIGN_CENTER, wxALIGN_CENTER );
if( current_version == version.version )
{
for( int col = 0; col < m_gridVersions->GetNumberCols(); col++ )
m_gridVersions->SetCellFont( row, col, bold_font );
}
row++;
}
for( int col = 0; col < m_gridVersions->GetNumberCols(); col++ )
{
// Set the width to see the full contents
m_gridVersions->SetColSize( col,
m_gridVersions->GetVisibleWidth( col, true, true, false ) );
}
m_gridVersions->Thaw();
if( aPackageData.state == PPS_AVAILABLE || aPackageData.state == PPS_UNAVAILABLE )
m_buttonInstall->Enable();
else
m_buttonInstall->Disable();
}
void PANEL_PACKAGES_VIEW::unsetPackageDetails()
{
m_infoText->ChangeValue( _( "Pick a package on the left panel to view it's description." ) );
m_gridVersions->Freeze();
if( m_gridVersions->GetNumberRows() > 0 )
m_gridVersions->DeleteRows( 0, m_gridVersions->GetNumberRows() );
m_gridVersions->Thaw();
}
wxString PANEL_PACKAGES_VIEW::toHumanReadableSize( const boost::optional<uint64_t> size ) const
{
if( !size )
return "-";
uint64_t b = size.get();
if( b >= 1024 * 1024 )
return wxString::Format( "%.1f Mb", b / 1024.0 / 1024.0 );
if( b >= 1024 )
return wxString::Format( "%lld Kb", b / 1024 );
return wxString::Format( "%lld b", b );
}
void PANEL_PACKAGES_VIEW::SetPackageState( const wxString& aPackageId,
const PCM_PACKAGE_STATE aState ) const
{
auto it = m_packagePanels.find( aPackageId );
if( it != m_packagePanels.end() )
{
it->second->SetState( aState );
if( m_currentSelected && m_currentSelected == it->second )
{
wxMouseEvent dummy;
m_currentSelected->OnClick( dummy );
}
}
}
void PANEL_PACKAGES_VIEW::OnVersionsCellClicked( wxGridEvent& event )
{
m_gridVersions->ClearSelection();
m_gridVersions->SelectRow( event.GetRow() );
}
void PANEL_PACKAGES_VIEW::OnDownloadVersionClicked( wxCommandEvent& event )
{
const auto rows = m_gridVersions->GetSelectedRows();
if( !m_currentSelected || rows.size() != 1 )
{
wxBell();
return;
}
wxString version = m_gridVersions->GetCellValue( rows[0], COL_VERSION );
const PCM_PACKAGE& package = m_currentSelected->GetPackageData().package;
auto ver_it = std::find_if( package.versions.begin(), package.versions.end(),
[&]( const PACKAGE_VERSION& ver )
{
return ver.version == version;
} );
wxASSERT_MSG( ver_it != package.versions.end(), "Could not find package version" );
if( !ver_it->download_url )
{
wxMessageBox( _( "Package download url is not specified" ),
_( "Error downloading package" ), wxICON_INFORMATION | wxOK, this );
return;
}
const wxString& url = ver_it->download_url.get();
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
KICAD_SETTINGS* app_settings = mgr.GetAppSettings<KICAD_SETTINGS>();
wxFileDialog dialog( this, _( "Save package" ), app_settings->m_PcmLastDownloadDir,
wxString::Format( "%s_v%s.zip", package.identifier, version ),
"ZIP files (*.zip)|*.zip", wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
if( dialog.ShowModal() == wxID_CANCEL )
return;
wxString path = dialog.GetPath();
app_settings->m_PcmLastDownloadDir = wxPathOnly( path );
std::ofstream output( path.ToUTF8(), std::ios_base::binary );
bool success = m_pcm->DownloadToStream( url, &output, _( "Downloading package" ), 0 );
output.close();
if( success )
{
if( ver_it->download_sha256 )
{
std::ifstream stream( path.ToUTF8(), std::ios_base::binary );
bool matches = m_pcm->VerifyHash( stream, ver_it->download_sha256.get() );
stream.close();
if( !matches
&& wxMessageBox(
_( "Integrity of the downloaded package could not be verified, hash "
"does not match. Are you sure you want to keep this file?" ),
_( "Keep downloaded file" ), wxICON_EXCLAMATION | wxYES_NO, this )
== wxNO )
{
wxRemoveFile( path );
}
}
}
else
{
if( wxFileExists( path ) )
wxRemoveFile( path );
}
}
void PANEL_PACKAGES_VIEW::OnInstallVersionClicked( wxCommandEvent& event )
{
const auto rows = m_gridVersions->GetSelectedRows();
if( m_currentSelected && rows.size() != 1 )
{
wxBell();
return;
}
wxString version = m_gridVersions->GetCellValue( rows[0], COL_VERSION );
const PCM_PACKAGE& package = m_currentSelected->GetPackageData().package;
auto ver_it = std::find_if( package.versions.begin(), package.versions.end(),
[&]( const PACKAGE_VERSION& ver )
{
return ver.version == version;
} );
wxASSERT_MSG( ver_it != package.versions.end(), "Could not find package version" );
if( !ver_it->compatible
&& wxMessageBox( _( "This package version is incompatible with your kicad version or "
"platform. Are you sure you want to install it anyway?" ),
_( "Install package" ), wxICON_EXCLAMATION | wxYES_NO, this )
== wxNO )
{
return;
}
m_actionCallback( m_currentSelected->GetPackageData(), PPA_INSTALL, version );
}
void PANEL_PACKAGES_VIEW::OnShowAllVersionsClicked( wxCommandEvent& event )
{
if( m_currentSelected )
{
wxMouseEvent dummy;
m_currentSelected->OnClick( dummy );
}
}
void PANEL_PACKAGES_VIEW::OnSearchTextChanged( wxCommandEvent& event )
{
unsetPackageDetails();
if( m_currentSelected )
m_currentSelected->SetSelected( false );
m_currentSelected = nullptr;
updatePackageList();
}
void PANEL_PACKAGES_VIEW::updatePackageList()
{
// Sort by descending rank, ascending index
std::vector<std::pair<int, int>> package_ranks;
const wxString search_term = m_searchCtrl->GetValue().Trim();
for( size_t index = 0; index < m_packageInitialOrder.size(); index++ )
{
int rank = 1;
const PCM_PACKAGE& pkg =
m_packagePanels[m_packageInitialOrder[index]]->GetPackageData().package;
if( search_term.size() > 2 )
rank = m_pcm->GetPackageSearchRank( pkg, search_term );
// Packages with no versions are delisted and should not be shown
if( pkg.versions.size() == 0 )
rank = 0;
package_ranks.emplace_back( rank, index );
}
std::sort( package_ranks.begin(), package_ranks.end(),
[]( const std::pair<int, int>& a, const std::pair<int, int>& b )
{
return a.first > b.first || ( a.first == b.first && a.second < b.second );
} );
// Rearrange panels, hide ones with 0 rank
wxSizer* sizer = m_packageListWindow->GetSizer();
sizer->Clear( false ); // Don't delete panels
for( const auto& pair : package_ranks )
{
PANEL_PACKAGE* panel = m_packagePanels[m_packageInitialOrder[pair.second]];
if( pair.first > 0 )
{
sizer->Add( panel, 0, wxEXPAND );
panel->Show();
}
else
{
panel->Hide();
}
}
sizer->FitInside( m_packageListWindow );
m_packageListWindow->SetScrollRate( 0, 15 );
m_packageListWindow->Layout();
}

View File

@ -0,0 +1,106 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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/>.
*/
#ifndef PANEL_PACKAGES_VIEW_H_
#define PANEL_PACKAGES_VIEW_H_
#include "core/wx_stl_compat.h"
#include "panel_package.h"
#include "panel_packages_view_base.h"
#include "pcm.h"
#include "pcm_data.h"
#include <memory>
#include <unordered_map>
#include <vector>
class PANEL_PACKAGES_VIEW : public PANEL_PACKAGES_VIEW_BASE
{
public:
PANEL_PACKAGES_VIEW( wxWindow* parent, std::shared_ptr<PLUGIN_CONTENT_MANAGER> aPcm );
~PANEL_PACKAGES_VIEW();
/**
* @brief Recreates package panels and displays daya
*
* @param aPackageData list of package view data
* @param aCallback (un)install button callback
*/
void SetData( const std::vector<PACKAGE_VIEW_DATA>& aPackageData, ActionCallback aCallback );
/**
* @brief Set the state of package
*
* @param aPackageId id of the package
* @param aState new state
*/
void SetPackageState( const wxString& aPackageId, const PCM_PACKAGE_STATE aState ) const;
///< Destroys package panels
void ClearData();
///< Selects full row of the clicked cell
void OnVersionsCellClicked( wxGridEvent& event ) override;
///< Opens file chooser dialog and downloads selected package version archive
void OnDownloadVersionClicked( wxCommandEvent& event ) override;
///< Schedules installation of selected package version
void OnInstallVersionClicked( wxCommandEvent& event ) override;
///< Shows all versions including incompatible ones
void OnShowAllVersionsClicked( wxCommandEvent& event ) override;
///< Ranks packages for entered search term and rearranges/hides panels according to their rank
void OnSearchTextChanged( wxCommandEvent& event ) override;
private:
///< Updates package listing according to search term
void updatePackageList();
///< Updates details panel
void setPackageDetails( const PACKAGE_VIEW_DATA& aPackageData );
///< Clears details panel
void unsetPackageDetails();
///< Bytes to Kb/Mb/Gb string or "-" if absent
wxString toHumanReadableSize( const boost::optional<uint64_t> size ) const;
ActionCallback m_actionCallback;
std::unordered_map<wxString, PANEL_PACKAGE*> m_packagePanels;
std::vector<wxString> m_packageInitialOrder;
PANEL_PACKAGE* m_currentSelected;
std::shared_ptr<PLUGIN_CONTENT_MANAGER> m_pcm;
enum PACKAGE_VERSIONS_GRID_COLUMNS
{
COL_VERSION = 0,
COL_DOWNLOAD_SIZE,
COL_INSTALL_SIZE,
COL_COMPATIBILITY,
COL_STATUS
};
static std::unordered_map<PCM_PACKAGE_VERSION_STATUS, wxString> STATUS_ENUM_TO_STR;
};
#endif // PANEL_PACKAGES_VIEW_H_

View File

@ -0,0 +1,153 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "panel_packages_view_base.h"
///////////////////////////////////////////////////////////////////////////
PANEL_PACKAGES_VIEW_BASE::PANEL_PACKAGES_VIEW_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : wxPanel( parent, id, pos, size, style, name )
{
wxBoxSizer* bSizer1;
bSizer1 = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* bSizer7;
bSizer7 = new wxBoxSizer( wxHORIZONTAL );
m_searchBitmap = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
bSizer7->Add( m_searchBitmap, 0, wxALL, 5 );
m_searchCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
bSizer7->Add( m_searchCtrl, 1, wxALL|wxEXPAND, 5 );
bSizer1->Add( bSizer7, 0, wxEXPAND, 5 );
m_splitter1 = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D );
m_splitter1->SetSashGravity( 0.55 );
m_splitter1->Connect( wxEVT_IDLE, wxIdleEventHandler( PANEL_PACKAGES_VIEW_BASE::m_splitter1OnIdle ), NULL, this );
m_splitter1->SetMinimumPaneSize( 300 );
m_packageListWindow = new wxScrolledWindow( m_splitter1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxALWAYS_SHOW_SB|wxBORDER_SUNKEN|wxFULL_REPAINT_ON_RESIZE|wxVSCROLL );
m_packageListWindow->SetScrollRate( 5, 5 );
wxBoxSizer* bSizer2;
bSizer2 = new wxBoxSizer( wxVERTICAL );
m_packageListWindow->SetSizer( bSizer2 );
m_packageListWindow->Layout();
bSizer2->Fit( m_packageListWindow );
m_panelDetails = new wxPanel( m_splitter1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer3;
bSizer3 = new wxBoxSizer( wxVERTICAL );
m_notebook = new wxNotebook( m_panelDetails, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
m_panelDescription = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer4;
bSizer4 = new wxBoxSizer( wxVERTICAL );
m_infoText = new wxRichTextCtrl( m_panelDescription, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_AUTO_URL|wxTE_READONLY|wxVSCROLL|wxHSCROLL|wxNO_BORDER|wxWANTS_CHARS );
bSizer4->Add( m_infoText, 1, wxEXPAND, 5 );
m_panelDescription->SetSizer( bSizer4 );
m_panelDescription->Layout();
bSizer4->Fit( m_panelDescription );
m_notebook->AddPage( m_panelDescription, _("Description"), true );
m_panelVersions = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer5;
bSizer5 = new wxBoxSizer( wxVERTICAL );
m_showAllVersions = new wxCheckBox( m_panelVersions, wxID_ANY, _("Show all versions"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer5->Add( m_showAllVersions, 0, wxALL, 5 );
m_gridVersions = new WX_GRID( m_panelVersions, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
// Grid
m_gridVersions->CreateGrid( 0, 5 );
m_gridVersions->EnableEditing( false );
m_gridVersions->EnableGridLines( true );
m_gridVersions->EnableDragGridSize( false );
m_gridVersions->SetMargins( 0, 0 );
// Columns
m_gridVersions->AutoSizeColumns();
m_gridVersions->EnableDragColMove( false );
m_gridVersions->EnableDragColSize( true );
m_gridVersions->SetColLabelSize( 30 );
m_gridVersions->SetColLabelValue( 0, _("Version") );
m_gridVersions->SetColLabelValue( 1, _("Dl Size") );
m_gridVersions->SetColLabelValue( 2, _("Inst Size") );
m_gridVersions->SetColLabelValue( 3, _("Comp") );
m_gridVersions->SetColLabelValue( 4, _("Status") );
m_gridVersions->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Rows
m_gridVersions->EnableDragRowSize( false );
m_gridVersions->SetRowLabelSize( 0 );
m_gridVersions->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Label Appearance
// Cell Defaults
m_gridVersions->SetDefaultCellAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
bSizer5->Add( m_gridVersions, 1, wxEXPAND, 5 );
wxBoxSizer* bSizer6;
bSizer6 = new wxBoxSizer( wxHORIZONTAL );
bSizer6->Add( 0, 0, 1, wxEXPAND, 5 );
m_buttonDownload = new wxButton( m_panelVersions, wxID_ANY, _("Download"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer6->Add( m_buttonDownload, 0, wxALL, 5 );
m_buttonInstall = new wxButton( m_panelVersions, wxID_ANY, _("Install"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer6->Add( m_buttonInstall, 0, wxALL, 5 );
bSizer6->Add( 0, 0, 1, wxEXPAND, 5 );
bSizer5->Add( bSizer6, 0, wxEXPAND, 5 );
m_panelVersions->SetSizer( bSizer5 );
m_panelVersions->Layout();
bSizer5->Fit( m_panelVersions );
m_notebook->AddPage( m_panelVersions, _("Versions"), false );
bSizer3->Add( m_notebook, 1, wxEXPAND, 5 );
m_panelDetails->SetSizer( bSizer3 );
m_panelDetails->Layout();
bSizer3->Fit( m_panelDetails );
m_splitter1->SplitVertically( m_packageListWindow, m_panelDetails, 0 );
bSizer1->Add( m_splitter1, 1, wxEXPAND, 5 );
this->SetSizer( bSizer1 );
this->Layout();
// Connect Events
m_searchCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnSearchTextChanged ), NULL, this );
m_showAllVersions->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnShowAllVersionsClicked ), NULL, this );
m_gridVersions->Connect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( PANEL_PACKAGES_VIEW_BASE::OnVersionsCellClicked ), NULL, this );
m_buttonDownload->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnDownloadVersionClicked ), NULL, this );
m_buttonInstall->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnInstallVersionClicked ), NULL, this );
}
PANEL_PACKAGES_VIEW_BASE::~PANEL_PACKAGES_VIEW_BASE()
{
// Disconnect Events
m_searchCtrl->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnSearchTextChanged ), NULL, this );
m_showAllVersions->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnShowAllVersionsClicked ), NULL, this );
m_gridVersions->Disconnect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( PANEL_PACKAGES_VIEW_BASE::OnVersionsCellClicked ), NULL, this );
m_buttonDownload->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnDownloadVersionClicked ), NULL, this );
m_buttonInstall->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnInstallVersionClicked ), NULL, this );
}

View File

@ -0,0 +1,957 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="15" />
<object class="Project" expanded="1">
<property name="class_decoration">; </property>
<property name="code_generation">C++</property>
<property name="disconnect_events">1</property>
<property name="disconnect_mode">source_name</property>
<property name="disconnect_php_events">0</property>
<property name="disconnect_python_events">0</property>
<property name="embedded_files_path"></property>
<property name="encoding">UTF-8</property>
<property name="event_generation">connect</property>
<property name="file">panel_packages_view_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="indent_with_spaces"></property>
<property name="internationalize">1</property>
<property name="name">panel_packages_view_base</property>
<property name="namespace"></property>
<property name="path">./</property>
<property name="precompiled_header"></property>
<property name="relative_path">1</property>
<property name="skip_lua_events">1</property>
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="ui_table">UI</property>
<property name="use_enum">0</property>
<property name="use_microsoft_bom">0</property>
<object class="Panel" expanded="1">
<property name="aui_managed">0</property>
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
<property name="bg"></property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="enabled">1</property>
<property name="event_handler">impl_virtual</property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">PANEL_PACKAGES_VIEW_BASE</property>
<property name="pos"></property>
<property name="size">500,300</property>
<property name="subclass">; ; forward_declare</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer1</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer7</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticBitmap" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_searchBitmap</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxTextCtrl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="maxlength"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_searchCtrl</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnText">OnSearchTextChanged</event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxSplitterWindow" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_pane_size">300</property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_splitter1</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="sashgravity">0.55</property>
<property name="sashpos">0</property>
<property name="sashsize">-1</property>
<property name="show">1</property>
<property name="size"></property>
<property name="splitmode">wxSPLIT_VERTICAL</property>
<property name="style">wxSP_3D</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<object class="splitteritem" expanded="1">
<object class="wxScrolledWindow" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_packageListWindow</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="scroll_rate_x">5</property>
<property name="scroll_rate_y">5</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxALWAYS_SHOW_SB|wxBORDER_SUNKEN|wxFULL_REPAINT_ON_RESIZE|wxVSCROLL</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer2</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
</object>
</object>
</object>
<object class="splitteritem" expanded="1">
<object class="wxPanel" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_panelDetails</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer3</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxNotebook" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmapsize"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_notebook</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<object class="notebookpage" expanded="1">
<property name="bitmap"></property>
<property name="label">Description</property>
<property name="select">1</property>
<object class="wxPanel" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_panelDescription</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer4</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxRichTextCtrl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_infoText</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxTE_AUTO_URL|wxTE_READONLY</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxVSCROLL|wxHSCROLL|wxNO_BORDER|wxWANTS_CHARS</property>
</object>
</object>
</object>
</object>
</object>
<object class="notebookpage" expanded="1">
<property name="bitmap"></property>
<property name="label">Versions</property>
<property name="select">0</property>
<object class="wxPanel" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_panelVersions</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer5</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Show all versions</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_showAllVersions</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnCheckBox">OnShowAllVersionsClicked</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxGrid" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="autosize_cols">1</property>
<property name="autosize_rows">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="cell_bg"></property>
<property name="cell_font"></property>
<property name="cell_horiz_alignment">wxALIGN_CENTER</property>
<property name="cell_text"></property>
<property name="cell_vert_alignment">wxALIGN_CENTER</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="col_label_horiz_alignment">wxALIGN_CENTER</property>
<property name="col_label_size">30</property>
<property name="col_label_values">&quot;Version&quot; &quot;Dl Size&quot; &quot;Inst Size&quot; &quot;Comp&quot; &quot;Status&quot;</property>
<property name="col_label_vert_alignment">wxALIGN_CENTER</property>
<property name="cols">5</property>
<property name="column_sizes"></property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_col_move">0</property>
<property name="drag_col_size">1</property>
<property name="drag_grid_size">0</property>
<property name="drag_row_size">0</property>
<property name="editing">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="grid_line_color"></property>
<property name="grid_lines">1</property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label_bg"></property>
<property name="label_font"></property>
<property name="label_text"></property>
<property name="margin_height">0</property>
<property name="margin_width">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_gridVersions</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="row_label_horiz_alignment">wxALIGN_CENTER</property>
<property name="row_label_size">0</property>
<property name="row_label_values"></property>
<property name="row_label_vert_alignment">wxALIGN_CENTER</property>
<property name="row_sizes"></property>
<property name="rows">0</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">WX_GRID; widgets/wx_grid.h; Not forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnGridCellLeftClick">OnVersionsCellClicked</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer6</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="1">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxButton" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Download</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_buttonDownload</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">OnDownloadVersionClicked</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxButton" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Install</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_buttonInstall</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">OnInstallVersionClicked</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="1">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</wxFormBuilder_Project>

View File

@ -0,0 +1,79 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#pragma once
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
#include "widgets/wx_grid.h"
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/statbmp.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/textctrl.h>
#include <wx/sizer.h>
#include <wx/scrolwin.h>
#include <wx/richtext/richtextctrl.h>
#include <wx/panel.h>
#include <wx/checkbox.h>
#include <wx/grid.h>
#include <wx/button.h>
#include <wx/notebook.h>
#include <wx/splitter.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class PANEL_PACKAGES_VIEW_BASE
///////////////////////////////////////////////////////////////////////////////
class PANEL_PACKAGES_VIEW_BASE : public wxPanel
{
private:
protected:
wxStaticBitmap* m_searchBitmap;
wxTextCtrl* m_searchCtrl;
wxSplitterWindow* m_splitter1;
wxScrolledWindow* m_packageListWindow;
wxPanel* m_panelDetails;
wxNotebook* m_notebook;
wxPanel* m_panelDescription;
wxRichTextCtrl* m_infoText;
wxPanel* m_panelVersions;
wxCheckBox* m_showAllVersions;
WX_GRID* m_gridVersions;
wxButton* m_buttonDownload;
wxButton* m_buttonInstall;
// Virtual event handlers, overide them in your derived class
virtual void OnSearchTextChanged( wxCommandEvent& event ) { event.Skip(); }
virtual void OnShowAllVersionsClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnVersionsCellClicked( wxGridEvent& event ) { event.Skip(); }
virtual void OnDownloadVersionClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnInstallVersionClicked( wxCommandEvent& event ) { event.Skip(); }
public:
PANEL_PACKAGES_VIEW_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 500,300 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
~PANEL_PACKAGES_VIEW_BASE();
void m_splitter1OnIdle( wxIdleEvent& )
{
m_splitter1->SetSashPosition( 0 );
m_splitter1->Disconnect( wxEVT_IDLE, wxIdleEventHandler( PANEL_PACKAGES_VIEW_BASE::m_splitter1OnIdle ), NULL, this );
}
};

813
kicad/pcm/pcm.cpp Normal file
View File

@ -0,0 +1,813 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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 "pcm.h"
#include "core/wx_stl_compat.h"
#include "kicad_build_version.h"
#include "kicad_curl/kicad_curl_easy.h"
#include "paths.h"
#include "pgm_base.h"
#include "picosha2.h"
#include "settings/settings_manager.h"
#include "widgets/wx_progress_reporters.h"
#include <fstream>
#include <iomanip>
#include <memory>
#include <wx/dir.h>
#include <wx/filefn.h>
#include <wx/fs_zip.h>
#include <wx/image.h>
#include <wx/mstream.h>
#include <wx/sstream.h>
#include <wx/tokenzr.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
const std::tuple<int, int> PLUGIN_CONTENT_MANAGER::m_kicad_version =
KICAD_MAJOR_MINOR_VERSION_TUPLE;
PLUGIN_CONTENT_MANAGER::PLUGIN_CONTENT_MANAGER( wxWindow* aParent ) : m_dialog( aParent )
{
// Get 3rd party path
const ENV_VAR_MAP& env = Pgm().GetLocalEnvVariables();
auto it = env.find( "KICAD6_3RD_PARTY" );
if( it != env.end() && !it->second.GetValue().IsEmpty() )
m_3rdparty_path = it->second.GetValue();
else
m_3rdparty_path = PATHS::GetDefault3rdPartyPath();
// Read and store pcm schema
wxFileName schema_file( PATHS::GetStockDataPath(), "pcm.v1.schema.json" );
schema_file.AppendDir( "schemas" );
std::ifstream schema_stream( schema_file.GetFullPath().ToUTF8() );
nlohmann::json schema;
try
{
schema_stream >> schema;
m_schema_validator.set_root_schema( schema );
}
catch( std::exception& e )
{
wxLogError( wxString::Format( _( "Error loading schema: %s" ), e.what() ) );
}
// Load currently installed packages
wxFileName f( SETTINGS_MANAGER::GetUserSettingsPath(), "installed_packages.json" );
if( f.FileExists() )
{
std::ifstream installed_stream( f.GetFullPath().ToUTF8() );
nlohmann::json installed;
try
{
installed_stream >> installed;
if( installed.contains( "packages" ) && installed["packages"].is_array() )
{
for( const auto& js_entry : installed["packages"] )
{
PCM_INSTALLATION_ENTRY entry = js_entry.get<PCM_INSTALLATION_ENTRY>();
m_installed.emplace( entry.package.identifier, entry );
}
}
}
catch( std::exception& e )
{
wxLogError( wxString::Format( _( "Error loading installed packages list: %s" ),
e.what() ) );
}
}
// As a fall back populate installed from names of directories
for( const wxString& dir : PCM_PACKAGE_DIRECTORIES )
{
wxFileName d( m_3rdparty_path, "" );
d.AppendDir( dir );
if( d.DirExists() )
{
wxDir package_dir( d.GetPath() );
if( !package_dir.IsOpened() )
continue;
wxString subdir;
bool more = package_dir.GetFirst( &subdir, "", wxDIR_DIRS | wxDIR_HIDDEN );
while( more )
{
if( m_installed.find( subdir ) == m_installed.end() )
{
PCM_INSTALLATION_ENTRY entry;
wxFileName subdir_file( d.GetPath(), subdir );
// wxFileModificationTime bugs out on windows for directories
wxStructStat stat;
wxStat( subdir_file.GetFullPath(), &stat );
entry.package.name = subdir;
entry.package.identifier = subdir;
entry.current_version = "0.0";
entry.install_timestamp = stat.st_mtime;
entry.repository_name = wxT( "<unknown>" );
m_installed.emplace( subdir, entry );
}
more = package_dir.GetNext( &subdir );
}
}
}
// Calculate package compatibility
std::for_each( m_installed.begin(), m_installed.end(),
[&]( auto& entry )
{
preparePackage( entry.second.package );
} );
}
bool PLUGIN_CONTENT_MANAGER::DownloadToStream( const wxString& aUrl, std::ostream* aOutput,
const wxString& aDialogTitle,
const size_t aSizeLimit )
{
bool size_exceeded = false;
std::unique_ptr<WX_PROGRESS_REPORTER> reporter(
new WX_PROGRESS_REPORTER( m_dialog, aDialogTitle, 1 ) );
TRANSFER_CALLBACK callback = [&]( size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow )
{
if( aSizeLimit > 0 && ( dltotal > aSizeLimit || dlnow > aSizeLimit ) )
{
size_exceeded = true;
// Non zero return means abort.
return true;
}
if( dltotal > 1024 )
{
reporter->SetCurrentProgress( dlnow / (double) dltotal );
reporter->Report( wxString::Format( _( "Downloading %lld/%lld Kb" ), dlnow / 1024,
dltotal / 1024 ) );
}
else
{
reporter->SetCurrentProgress( 0.0 );
}
return !reporter->KeepRefreshing();
};
KICAD_CURL_EASY curl;
curl.SetOutputStream( aOutput );
curl.SetURL( aUrl.ToUTF8().data() );
curl.SetFollowRedirects( true );
curl.SetTransferCallback( callback, 250000L );
int code = curl.Perform();
if( !reporter->IsCancelled() )
reporter->SetCurrentProgress( 1.0 );
if( code != CURLE_OK )
{
if( code == CURLE_ABORTED_BY_CALLBACK && size_exceeded )
wxMessageBox( _( "Download is too large." ) );
else if( code != CURLE_ABORTED_BY_CALLBACK )
wxLogError( wxString( curl.GetErrorText( code ) ) );
return false;
}
return true;
}
bool PLUGIN_CONTENT_MANAGER::FetchRepository( const wxString& aUrl, PCM_REPOSITORY& aRepository )
{
std::stringstream repository_stream;
if( !DownloadToStream( aUrl, &repository_stream, _( "Fetching repository" ), 20480 ) )
{
wxLogError( _( "Unable to load repository url" ) );
return false;
}
nlohmann::json repository_json;
try
{
repository_stream >> repository_json;
ValidateJson( repository_json, nlohmann::json_uri( "#/definitions/Repository" ) );
aRepository = repository_json.get<PCM_REPOSITORY>();
}
catch( const std::exception& e )
{
wxLogError( wxString::Format( _( "Unable to parse repository:\n\n%s" ), e.what() ) );
return false;
}
return true;
}
void PLUGIN_CONTENT_MANAGER::ValidateJson( const nlohmann::json& aJson,
const nlohmann::json_uri& aUri ) const
{
nlohmann::json_schema::basic_error_handler error_handler;
m_schema_validator.validate( aJson, error_handler, aUri );
}
bool PLUGIN_CONTENT_MANAGER::fetchPackages( const wxString& aUrl,
const boost::optional<wxString>& aHash,
std::vector<PCM_PACKAGE>& aPackages )
{
std::stringstream packages_stream;
if( !DownloadToStream( aUrl, &packages_stream, _( "Fetching repository packages" ) ) )
{
wxLogError( _( "Unable to load repository packages url." ) );
return false;
}
std::istringstream isstream( packages_stream.str() );
if( aHash && !VerifyHash( isstream, aHash.get() ) )
{
wxLogError( _( "Packages hash doesn't match. Repository may be corrupted." ) );
return false;
}
try
{
nlohmann::json packages_json = nlohmann::json::parse( packages_stream.str() );
ValidateJson( packages_json, nlohmann::json_uri( "#/definitions/PackageArray" ) );
for( nlohmann::json& package : packages_json["packages"] )
{
aPackages.push_back( package.get<PCM_PACKAGE>() );
}
}
catch( std::exception& e )
{
wxLogError( wxString::Format( _( "Unable to parse packages metadata:\n\n%s" ), e.what() ) );
return false;
}
return true;
}
bool PLUGIN_CONTENT_MANAGER::VerifyHash( std::istream& aStream, const wxString& aHash ) const
{
std::vector<unsigned char> bytes( picosha2::k_digest_size );
picosha2::hash256( std::istreambuf_iterator<char>( aStream ), std::istreambuf_iterator<char>(),
bytes.begin(), bytes.end() );
std::string hex_str = picosha2::bytes_to_hex_string( bytes.begin(), bytes.end() );
return aHash.compare( hex_str ) == 0;
}
const PCM_REPOSITORY&
PLUGIN_CONTENT_MANAGER::getCachedRepository( const wxString& aRepositoryId ) const
{
wxASSERT_MSG( m_repository_cache.find( aRepositoryId ) != m_repository_cache.end(),
"Repository is not cached." );
return m_repository_cache.at( aRepositoryId );
}
const bool PLUGIN_CONTENT_MANAGER::CacheRepository( const wxString& aRepositoryId )
{
if( m_repository_cache.find( aRepositoryId ) != m_repository_cache.end() )
return true;
const auto repository_tuple =
std::find_if( m_repository_list.begin(), m_repository_list.end(),
[&aRepositoryId]( const std::tuple<wxString, wxString, wxString>& t )
{
return std::get<0>( t ) == aRepositoryId;
} );
if( repository_tuple == m_repository_list.end() )
return false;
wxString url = std::get<2>( *repository_tuple );
nlohmann::json js;
PCM_REPOSITORY current_repo;
if( !FetchRepository( url, current_repo ) )
return false;
bool packages_cache_exists = false;
// First load repository data from local filesystem if available.
wxFileName repo_cache = wxFileName( m_3rdparty_path, "repository.json" );
repo_cache.AppendDir( "cache" );
repo_cache.AppendDir( aRepositoryId );
wxFileName packages_cache( repo_cache.GetPath(), "packages.json" );
if( repo_cache.FileExists() && packages_cache.FileExists() )
{
std::ifstream repo_stream( repo_cache.GetFullPath().ToUTF8() );
repo_stream >> js;
PCM_REPOSITORY saved_repo = js.get<PCM_REPOSITORY>();
if( saved_repo.packages.update_timestamp == current_repo.packages.update_timestamp )
{
// Cached repo is up to date, use data on disk
js.clear();
std::ifstream packages_cache_stream( packages_cache.GetFullPath().ToUTF8() );
try
{
packages_cache_stream >> js;
saved_repo.package_list = js["packages"].get<std::vector<PCM_PACKAGE>>();
std::for_each( saved_repo.package_list.begin(), saved_repo.package_list.end(),
&preparePackage );
m_repository_cache[aRepositoryId] = std::move( saved_repo );
packages_cache_exists = true;
}
catch( ... )
{
wxLogError( _( "Packages cache for current repository is "
"corrupted, it will be redownloaded." ) );
}
}
}
if( !packages_cache_exists )
{
// Cache doesn't exist or is out of date
if( !fetchPackages( current_repo.packages.url, current_repo.packages.sha256,
current_repo.package_list ) )
{
return false;
}
std::for_each( current_repo.package_list.begin(), current_repo.package_list.end(),
&preparePackage );
repo_cache.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
std::ofstream repo_cache_stream( repo_cache.GetFullPath().ToUTF8() );
repo_cache_stream << std::setw( 4 ) << nlohmann::json( current_repo ) << std::endl;
std::ofstream packages_cache_stream( packages_cache.GetFullPath().ToUTF8() );
js.clear();
js["packages"] = nlohmann::json( current_repo.package_list );
packages_cache_stream << std::setw( 4 ) << js << std::endl;
m_repository_cache[aRepositoryId] = std::move( current_repo );
}
if( current_repo.resources )
{
// Check resources file date, redownload if needed
PCM_RESOURCE_REFERENCE& resources = current_repo.resources.get();
wxFileName resource_file( repo_cache.GetPath(), "resources.zip" );
time_t mtime = 0;
if( resource_file.FileExists() )
mtime = wxFileModificationTime( resource_file.GetFullPath() );
if( mtime + 600 < getCurrentTimestamp() && mtime < (time_t) resources.update_timestamp )
{
std::ofstream resources_stream( resource_file.GetFullPath().ToUTF8(),
std::ios_base::binary );
// 100 Mb resource file limit
bool success = DownloadToStream( resources.url, &resources_stream,
_( "Downloading resources" ), 100 * 1024 * 1024 );
resources_stream.close();
if( success )
{
std::ifstream read_stream( resource_file.GetFullPath().ToUTF8(),
std::ios_base::binary );
if( resources.sha256 && !VerifyHash( read_stream, resources.sha256.get() ) )
{
read_stream.close();
wxLogError(
_( "Resources file hash doesn't match and will not be used. Repository "
"may be corrupted." ) );
wxRemoveFile( resource_file.GetFullPath() );
}
}
else
{
// Not critical, just clean up the file
wxRemoveFile( resource_file.GetFullPath() );
}
}
}
return true;
}
void PLUGIN_CONTENT_MANAGER::preparePackage( PCM_PACKAGE& aPackage )
{
// Parse package version strings
for( PACKAGE_VERSION& ver : aPackage.versions )
{
int epoch = 0, major = 0, minor = 0, patch = 0;
if( ver.version_epoch )
epoch = ver.version_epoch.get();
wxStringTokenizer version_tokenizer( ver.version, "." );
major = wxAtoi( version_tokenizer.GetNextToken() );
if( version_tokenizer.HasMoreTokens() )
minor = wxAtoi( version_tokenizer.GetNextToken() );
if( version_tokenizer.HasMoreTokens() )
patch = wxAtoi( version_tokenizer.GetNextToken() );
ver.parsed_version = std::make_tuple( epoch, major, minor, patch );
// Determine compatibility
ver.compatible = true;
auto parse_major_minor = []( const wxString& version )
{
wxStringTokenizer tokenizer( version, "." );
int major = wxAtoi( tokenizer.GetNextToken() );
int minor = wxAtoi( tokenizer.GetNextToken() );
return std::tuple<int, int>( major, minor );
};
if( parse_major_minor( ver.kicad_version ) > m_kicad_version )
ver.compatible = false;
if( ver.kicad_version_max
&& parse_major_minor( ver.kicad_version_max.get() ) < m_kicad_version )
ver.compatible = false;
#ifdef __WXMSW__
wxString platform = wxT( "windows" );
#endif
#ifdef __WXOSX__
wxString platform = wxT( "macos" );
#endif
#ifdef __WXGTK__
wxString platform = wxT( "linux" );
#endif
if( ver.platforms.size() > 0
&& std::find( ver.platforms.begin(), ver.platforms.end(), platform )
== ver.platforms.end() )
{
ver.compatible = false;
}
}
// Sort by descending version
std::sort( aPackage.versions.begin(), aPackage.versions.end(),
[]( const PACKAGE_VERSION& a, const PACKAGE_VERSION& b )
{
return a.parsed_version > b.parsed_version;
} );
}
const std::vector<PCM_PACKAGE>&
PLUGIN_CONTENT_MANAGER::GetRepositoryPackages( const wxString& aRepositoryId ) const
{
return getCachedRepository( aRepositoryId ).package_list;
}
void PLUGIN_CONTENT_MANAGER::SetRepositoryList( const STRING_PAIR_LIST& aRepositories )
{
// Clean up cache folder if repository is not in new list
for( const auto& entry : m_repository_list )
{
auto it = std::find_if( aRepositories.begin(), aRepositories.end(),
[&]( const auto& new_entry )
{
return new_entry.first == std::get<1>( entry );
} );
if( it == aRepositories.end() )
{
DiscardRepositoryCache( std::get<0>( entry ) );
}
}
m_repository_list.clear();
m_repository_cache.clear();
for( const auto& repo : aRepositories )
{
std::string url_sha = picosha2::hash256_hex_string( repo.second );
m_repository_list.push_back(
std::make_tuple( url_sha.substr( 0, 16 ), repo.first, repo.second ) );
}
}
void PLUGIN_CONTENT_MANAGER::DiscardRepositoryCache( const wxString& aRepositoryId )
{
if( m_repository_cache.count( aRepositoryId ) > 0 )
m_repository_cache.erase( aRepositoryId );
wxFileName repo_cache( m_3rdparty_path, "" );
repo_cache.AppendDir( "cache" );
repo_cache.AppendDir( aRepositoryId );
if( repo_cache.DirExists() )
repo_cache.Rmdir( wxPATH_RMDIR_RECURSIVE );
}
void PLUGIN_CONTENT_MANAGER::MarkInstalled( const PCM_PACKAGE& aPackage, const wxString& aVersion,
const wxString& aRepositoryId )
{
PCM_INSTALLATION_ENTRY entry;
entry.package = aPackage;
entry.current_version = aVersion;
entry.repository_id = aRepositoryId;
if( !aRepositoryId.IsEmpty() )
entry.repository_name = getCachedRepository( aRepositoryId ).name;
else
entry.repository_name = _( "Local file" );
entry.install_timestamp = getCurrentTimestamp();
m_installed.emplace( aPackage.identifier, entry );
}
void PLUGIN_CONTENT_MANAGER::MarkUninstalled( const PCM_PACKAGE& aPackage )
{
m_installed.erase( aPackage.identifier );
}
PCM_PACKAGE_STATE PLUGIN_CONTENT_MANAGER::GetPackageState( const wxString& aRepositoryId,
const wxString& aPackageId )
{
if( m_installed.find( aPackageId ) != m_installed.end() )
return PPS_INSTALLED;
if( aRepositoryId.IsEmpty() || !CacheRepository( aRepositoryId ) )
return PPS_UNAVAILABLE;
const PCM_REPOSITORY& repo = getCachedRepository( aRepositoryId );
auto pkg_it = std::find_if( repo.package_list.begin(), repo.package_list.end(),
[&aPackageId]( const PCM_PACKAGE& pkg )
{
return pkg.identifier == aPackageId;
} );
if( pkg_it == repo.package_list.end() )
return PPS_UNAVAILABLE;
const PCM_PACKAGE& pkg = *pkg_it;
auto ver_it = std::find_if( pkg.versions.begin(), pkg.versions.end(),
[]( const PACKAGE_VERSION& ver )
{
return ver.compatible;
} );
if( ver_it == pkg.versions.end() )
return PPS_UNAVAILABLE;
else
return PPS_AVAILABLE;
}
time_t PLUGIN_CONTENT_MANAGER::getCurrentTimestamp() const
{
return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch() )
.count();
}
PLUGIN_CONTENT_MANAGER::~PLUGIN_CONTENT_MANAGER()
{
// Save current installed packages list.
nlohmann::json js;
js["packages"] = nlohmann::json::array();
for( const auto& entry : m_installed )
{
js["packages"].emplace_back( entry.second );
}
wxFileName f( SETTINGS_MANAGER::GetUserSettingsPath(), "installed_packages.json" );
std::ofstream stream( f.GetFullPath().ToUTF8() );
stream << std::setw( 4 ) << js << std::endl;
}
const std::vector<PCM_INSTALLATION_ENTRY> PLUGIN_CONTENT_MANAGER::GetInstalledPackages() const
{
std::vector<PCM_INSTALLATION_ENTRY> v;
std::for_each( m_installed.begin(), m_installed.end(),
[&v]( const auto& entry )
{
v.push_back( entry.second );
} );
return v;
}
const wxString&
PLUGIN_CONTENT_MANAGER::GetInstalledPackageVersion( const wxString& aPackageId ) const
{
wxASSERT_MSG( m_installed.find( aPackageId ) != m_installed.end(),
"Installed package not found." );
return m_installed.at( aPackageId ).current_version;
}
int PLUGIN_CONTENT_MANAGER::GetPackageSearchRank( const PCM_PACKAGE& aPackage,
const wxString& aSearchTerm )
{
wxArrayString terms = wxStringTokenize( aSearchTerm.Lower(), " ", wxTOKEN_STRTOK );
int rank = 0;
const auto find_term_matches = [&]( const wxString& str )
{
int result = 0;
wxString lower = str.Lower();
for( const wxString& term : terms )
if( lower.Find( term ) != wxNOT_FOUND )
result += 1;
return result;
};
// Match on package id
if( terms.size() == 1 && terms[0] == aPackage.identifier )
rank += 10000;
if( terms.size() == 1 && find_term_matches( aPackage.identifier ) )
rank += 1000;
// Match on package name
rank += 500 * find_term_matches( aPackage.name );
// Match on tags
for( const wxString& tag : aPackage.tags )
rank += 100 * find_term_matches( tag );
// Match on package description
rank += 10 * find_term_matches( aPackage.description );
rank += 10 * find_term_matches( aPackage.description_full );
// Match on author/maintainer
rank += find_term_matches( aPackage.author.name );
if( aPackage.maintainer )
rank += 3 * find_term_matches( aPackage.maintainer.get().name );
// Match on resources
for( const auto& entry : aPackage.resources )
{
rank += find_term_matches( entry.first );
rank += find_term_matches( entry.second );
}
// Match on license
if( terms.size() == 1 && terms[0] == aPackage.license )
rank += 1;
return rank;
}
std::unordered_map<wxString, wxBitmap>
PLUGIN_CONTENT_MANAGER::GetRepositoryPackageBitmaps( const wxString& aRepositoryId )
{
std::unordered_map<wxString, wxBitmap> bitmaps;
wxFileName resources_file = wxFileName( m_3rdparty_path, "resources.zip" );
resources_file.AppendDir( "cache" );
resources_file.AppendDir( aRepositoryId );
if( !resources_file.FileExists() )
return bitmaps;
wxFFileInputStream stream( resources_file.GetFullPath() );
wxZipInputStream zip( stream );
if( !zip.IsOk() || zip.GetTotalEntries() == 0 )
return bitmaps;
for( wxArchiveEntry* entry = zip.GetNextEntry(); entry; entry = zip.GetNextEntry() )
{
wxArrayString path_parts =
wxSplit( entry->GetName(), wxFileName::GetPathSeparator(), (wxChar) NULL );
if( path_parts.size() != 2 || path_parts[1] != "icon.png" )
continue;
try
{
wxMemoryInputStream image_stream( zip, entry->GetSize() );
wxImage image( image_stream, wxBITMAP_TYPE_PNG );
bitmaps.emplace( path_parts[0], wxBitmap( image ) );
}
catch( ... )
{
// Log and ignore
wxLogTrace( "Error loading png bitmap for entry %s from %s", entry->GetName(),
resources_file.GetFullPath() );
}
}
return bitmaps;
}
std::unordered_map<wxString, wxBitmap> PLUGIN_CONTENT_MANAGER::GetInstalledPackageBitmaps()
{
std::unordered_map<wxString, wxBitmap> bitmaps;
wxFileName resources_dir_fn( m_3rdparty_path, "" );
resources_dir_fn.AppendDir( "resources" );
wxDir resources_dir( resources_dir_fn.GetPath() );
if( !resources_dir.IsOpened() )
return bitmaps;
wxString subdir;
bool more = resources_dir.GetFirst( &subdir, "", wxDIR_DIRS | wxDIR_HIDDEN );
while( more )
{
wxFileName icon( resources_dir_fn.GetPath(), "icon.png" );
icon.AppendDir( subdir );
if( icon.FileExists() )
{
try
{
wxBitmap bitmap( icon.GetFullPath(), wxBITMAP_TYPE_PNG );
bitmaps.emplace( subdir, bitmap );
}
catch( ... )
{
// Log and ignore
wxLogTrace( "Error loading png bitmap from %s", icon.GetFullPath() );
}
}
more = resources_dir.GetNext( &subdir );
}
return bitmaps;
}

314
kicad/pcm/pcm.h Normal file
View File

@ -0,0 +1,314 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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/>.
*/
#ifndef PCM_H_
#define PCM_H_
#include "core/wx_stl_compat.h"
#include "pcm_data.h"
#include <iostream>
#include <map>
#include <nlohmann/json-schema.hpp>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <wx/wx.h>
///< Contains list of all valid directories that get extracted from a package archive
const std::unordered_set<wxString> PCM_PACKAGE_DIRECTORIES( {
"plugins",
"footprints",
"models",
"symbols",
"resources",
} );
///< Package states
///< Package is available if it is not installed and there is a compatible version
///< Package is unavailable if it is not installed and there are no compatible versions
///< Pending states are intermediary when (un)installation is scheduled but not yet performed
enum PCM_PACKAGE_STATE
{
PPS_AVAILABLE = 0,
PPS_UNAVAILABLE = 1,
PPS_INSTALLED = 2,
PPS_PENDING_INSTALL = 3,
PPS_PENDING_UNINSTALL = 4
};
///< Package actions
enum PCM_PACKAGE_ACTION
{
PPA_INSTALL = 0,
PPA_UNINSTALL = 1
};
typedef std::vector<std::pair<wxString, wxString>> STRING_PAIR_LIST;
typedef std::vector<std::tuple<wxString, wxString, wxString>> STRING_TUPLE_LIST;
/**
* @brief Main class of Plugin and Content Manager subsystem
*
* This class handles logistics of repository management, caching, json validation,
* tracking installed packages and provides some utility methods.
*
* Repository caching is done in $KICADX_3RD_PARTY/cache directory with each
* repository storing it's metadata, packages and optionally resource files under
* it's id subdirectory.
*
* Repository id is a prefix of sha256 of it's main url.
*
* JSON schema file is expected to be in $KICAD_DATA/schemas directory
*
* Installed packages are stored in <user_settings>/installed_packages.json
* If that file is missing PCM will try to reconstruct it from existing
* directory structure inside $KICADX_3RD_PARTY but package descriptions
* and other metadata will be lost.
*/
class PLUGIN_CONTENT_MANAGER
{
public:
PLUGIN_CONTENT_MANAGER( wxWindow* aParent );
~PLUGIN_CONTENT_MANAGER();
/**
* @brief Fetches repository metadata from given url
*
* @param url URL of the repository
* @param repository fetched repository metadata
* @return true if successful
* @return false if URL could not be downloaded or result could not be parsed
*/
bool FetchRepository( const wxString& aUrl, PCM_REPOSITORY& aRepository );
/**
* @brief Validates json against a specific definition in the PCM schema
*
* @param aJson JSON object to validate
* @param aUri JSON URI of a definition to validate against, default is root
* @throws std::invalid_argument on validation failure
*/
void ValidateJson( const nlohmann::json& aJson,
const nlohmann::json_uri& aUri = nlohmann::json_uri( "#" ) ) const;
/**
* @brief Verifies SHA256 hash of a binary stream
*
* @param aStream input stream
* @param aHash sha256 lowercase hex string
* @return true if hash matches
*/
bool VerifyHash( std::istream& aStream, const wxString& aHash ) const;
/**
* @brief Set list of repositories
*
* Discards cache of repositories that were previously cached but are not
* on the new list of repositories.
*
* @param aRepositories list of <URL, name> pairs of repositories
*/
void SetRepositoryList( const STRING_PAIR_LIST& aRepositories );
/**
* @brief Discard in-memory and on-disk cache of a repository
*
* @param aRepositoryId id of the repository
*/
void DiscardRepositoryCache( const wxString& aRepositoryId );
/**
* @brief Mark package as installed
*
* @param aPackage package metadata
* @param aVersion installed package version
* @param aRepositoryId id of the source repository or empty estring if
* installed from a local file
*/
void MarkInstalled( const PCM_PACKAGE& aPackage, const wxString& aVersion,
const wxString& aRepositoryId );
/**
* @brief Mark package as uninstalled
*
* @param aPackage package metadata
*/
void MarkUninstalled( const PCM_PACKAGE& aPackage );
/**
* @brief Get list of repositories
*
* @return const STRING_TUPLE_LIST& list of repositories in <id, URL, name> tuple format
*/
const STRING_TUPLE_LIST& GetRepositoryList() const { return m_repository_list; }
/**
* @brief Cache specified repository packages and other metadata
*
* This method fetches latest repository metadata, checks if there is cache on disk,
* compares it's last update timestamp to repository metadata and redownloads packages
* list if necessary.
* Then it checks sha256 if provided and parses the package list.
* Parsed packages metadata is stored in memory.
*
* Process is repeated with resources file except it is just stored on disk and
* any errors at this stage are ignored.
*
* @param aRepositoryId id of the repository to cache
* @return true if packages metadata was cached successfully
*/
const bool CacheRepository( const wxString& aRepositoryId );
/**
* @brief Get the packages metadata from a previously cached repository
*
* This should only be called after a successful CacheRepository call
*
* @param aRepositoryId id of the repository
* @return list of package metadata objects
*/
const std::vector<PCM_PACKAGE>& GetRepositoryPackages( const wxString& aRepositoryId ) const;
/**
* @brief Get list of installed packages
*
* @return vector of PCM_INSTALLATION_ENTRY objects
*/
const std::vector<PCM_INSTALLATION_ENTRY> GetInstalledPackages() const;
/**
* @brief Get the current version of an installed package
*
* @param aPackageId id of the package
* @return current version
* @throws std::out_of_range if package with given id is not installed
*/
const wxString& GetInstalledPackageVersion( const wxString& aPackageId ) const;
///< Returns current 3rd party directory path
const wxString& Get3rdPartyPath() const { return m_3rdparty_path; };
/**
* @brief Get current state of the package
*
* @param aRepositoryId repository id
* @param aPackageId package id
* @return PCM_PACKAGE_STATE
*/
PCM_PACKAGE_STATE GetPackageState( const wxString& aRepositoryId, const wxString& aPackageId );
/**
* @brief Downloads url to an output stream
*
* @param aUrl URL to download
* @param aOutput output stream
* @param aDialogTitle title of the spawned WX_PROGRESS_REPORTER dialog
* @param aSizeLimit maximum download size, 0 for unlimited
* @return true if download was successful
* @return false if download failed or was too large
*/
bool DownloadToStream( const wxString& aUrl, std::ostream* aOutput,
const wxString& aDialogTitle,
const size_t aSizeLimit = DEFAULT_DOWNLOAD_MEM_LIMIT );
/**
* @brief Get the approximate measure of how much given package matches the search term
*
* @param aPackage package metadata object
* @param aSearchTerm search term
* @return int search rank, higher number means better match, 0 means no match
*/
int GetPackageSearchRank( const PCM_PACKAGE& aPackage, const wxString& aSearchTerm );
/**
* @brief Get the icon bitmaps for repository packages
*
* Repository package icons are taken from repository's resources zip file
*
* @param aRepositoryId id of the repository
* @return map of package id -> bitmap
*/
std::unordered_map<wxString, wxBitmap>
GetRepositoryPackageBitmaps( const wxString& aRepositoryId );
/**
* @brief Get the icon bitmaps for installed packages
*
* Icons for installed packages are taken from package extracted files in
* $KICADX_3RD_PARTY/resources/<packageid> directories
*
* @return map of package id -> bitmap
*/
std::unordered_map<wxString, wxBitmap> GetInstalledPackageBitmaps();
private:
///< Default download limit of 10 Mb to not use too much memory
static constexpr size_t DEFAULT_DOWNLOAD_MEM_LIMIT = 10 * 1024 * 1024;
/**
* @brief Downloads packages metadata to in memory stream, verifies hash and attempts to parse it
*
* @param aUrl URL of the packages metadata
* @param aHash optional sha256 hash
* @param aPackages resulting packages metadata list
* @return true if packages were successfully downloaded, verified and parsed
*/
bool fetchPackages( const wxString& aUrl, const boost::optional<wxString>& aHash,
std::vector<PCM_PACKAGE>& aPackages );
/**
* @brief Get the cached repository metadata
*
* @param aRepositoryId id of the repository
* @return const PCM_REPOSITORY&
*/
const PCM_REPOSITORY& getCachedRepository( const wxString& aRepositoryId ) const;
/**
* @brief Parses version strings and calculates compatibility
*
* This should be called after loading package metadata from repository or from
* installation entries
*
* @param aPackage package metadata object
*/
static void preparePackage( PCM_PACKAGE& aPackage );
///< Returns current UTC timestamp
time_t getCurrentTimestamp() const;
wxWindow* m_dialog;
nlohmann::json_schema::json_validator m_schema_validator;
wxString m_3rdparty_path;
wxString m_cache_path;
std::unordered_map<wxString, PCM_REPOSITORY> m_repository_cache;
STRING_TUPLE_LIST m_repository_list; // (id, name, url) tuples
// Using sorted map to keep order of entries in installed list stable
std::map<wxString, PCM_INSTALLATION_ENTRY> m_installed;
const static std::tuple<int, int> m_kicad_version;
};
#endif // PCM_H_

163
kicad/pcm/pcm_data.cpp Normal file
View File

@ -0,0 +1,163 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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 "pcm_data.h"
template <typename T>
void to_optional( const json& j, const char* key, boost::optional<T>& dest )
{
if( j.contains( key ) )
{
T tmp;
j.at( key ).get_to( tmp );
dest.emplace( tmp );
}
}
void to_json( json& j, const PACKAGE_VERSION& v )
{
j = json{ { "version", v.version },
{ "status", v.status },
{ "kicad_version", v.kicad_version } };
if( v.version_epoch )
j["version_epoch"] = v.version_epoch.get();
if( v.download_url )
j["download_url"] = v.download_url.get();
if( v.download_sha256 )
j["download_sha256"] = v.download_sha256.get();
if( v.download_size )
j["download_size"] = v.download_size.get();
if( v.install_size )
j["install_size"] = v.install_size.get();
if( v.platforms.size() > 0 )
nlohmann::to_json( j["platforms"], v.platforms );
if( v.kicad_version_max )
j["kicad_version_max"] = v.kicad_version_max.get();
}
void from_json( const json& j, PACKAGE_VERSION& v )
{
j.at( "version" ).get_to( v.version );
j.at( "status" ).get_to( v.status );
j.at( "kicad_version" ).get_to( v.kicad_version );
to_optional( j, "version_epoch", v.version_epoch );
to_optional( j, "download_url", v.download_url );
to_optional( j, "download_sha256", v.download_sha256 );
to_optional( j, "download_size", v.download_size );
to_optional( j, "install_size", v.install_size );
to_optional( j, "kicad_version_max", v.kicad_version_max );
if( j.contains( "platforms" ) )
j.at( "platforms" ).get_to( v.platforms );
}
void to_json( json& j, const PCM_PACKAGE& p )
{
j = json{ { "name", p.name },
{ "description", p.description },
{ "description_full", p.description_full },
{ "identifier", p.identifier },
{ "type", p.type },
{ "author", p.author },
{ "license", p.license },
{ "resources", p.resources },
{ "versions", p.versions } };
if( p.maintainer )
j["maintainer"] = p.maintainer.get();
if( p.tags.size() > 0 )
j["tags"] = p.tags;
}
void from_json( const json& j, PCM_PACKAGE& p )
{
j.at( "name" ).get_to( p.name );
j.at( "description" ).get_to( p.description );
j.at( "description_full" ).get_to( p.description_full );
j.at( "identifier" ).get_to( p.identifier );
j.at( "type" ).get_to( p.type );
j.at( "author" ).get_to( p.author );
j.at( "license" ).get_to( p.license );
j.at( "resources" ).get_to( p.resources );
j.at( "versions" ).get_to( p.versions );
to_optional( j, "maintainer", p.maintainer );
if( j.contains( "tags" ) )
j.at( "tags" ).get_to( p.tags );
}
void to_json( json& j, const PCM_RESOURCE_REFERENCE& r )
{
j = json{ { "url", r.url }, { "update_timestamp", r.update_timestamp } };
if( r.sha256 )
j["sha256"] = r.sha256.get();
}
void from_json( const json& j, PCM_RESOURCE_REFERENCE& r )
{
j.at( "url" ).get_to( r.url );
j.at( "update_timestamp" ).get_to( r.update_timestamp );
to_optional(j, "sha256", r.sha256 );
}
void to_json( json& j, const PCM_REPOSITORY& r )
{
j = json{ { "name", r.name }, { "packages", r.packages } };
if( r.resources )
j["resources"] = r.resources.get();
if( r.manifests )
j["manifests"] = r.manifests.get();
if( r.maintainer )
j["maintainer"] = r.maintainer.get();
}
void from_json( const json& j, PCM_REPOSITORY& r )
{
j.at( "name" ).get_to( r.name );
j.at( "packages" ).get_to( r.packages );
to_optional(j, "resources", r.resources );
to_optional(j, "manifests", r.manifests );
to_optional(j, "maintainer", r.maintainer );
}

201
kicad/pcm/pcm_data.h Normal file
View File

@ -0,0 +1,201 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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/>.
*/
#ifndef PCM_DATA_H_
#define PCM_DATA_H_
#include "core/wx_stl_compat.h"
#include <boost/optional.hpp>
#include <map>
#include <nlohmann/json.hpp>
#include <string>
#include <tuple>
#include <vector>
#include <wx/string.h>
using STRING_MAP = std::map<std::string, wxString>;
using nlohmann::json;
///< Supported package types
enum PCM_PACKAGE_TYPE
{
PT_INVALID,
PT_PLUGIN,
PT_LIBRARY,
};
///< Status of specific package version
enum PCM_PACKAGE_VERSION_STATUS
{
PVS_INVALID,
PVS_STABLE,
PVS_TESTING,
PVS_DEVELOPMENT,
PVS_DEPRECATED
};
///< Describes a person's name and contact information
struct PCM_CONTACT
{
wxString name;
STRING_MAP contact;
};
// MSVC, wxWidgets and nlohmann_json don't play nice together and
// create linker errors about redefinition of some vector members
// if an attempt to use vector<wxString> in json is made.
///< Package version metadata
struct PACKAGE_VERSION
{
wxString version;
boost::optional<int> version_epoch;
boost::optional<wxString> download_url;
boost::optional<wxString> download_sha256;
boost::optional<uint64_t> download_size;
boost::optional<uint64_t> install_size;
PCM_PACKAGE_VERSION_STATUS status;
std::vector<std::string> platforms;
wxString kicad_version;
boost::optional<wxString> kicad_version_max;
// Not serialized fields
std::tuple<int, int, int, int> parsed_version; // Full version tuple for sorting
bool compatible;
};
///< Package metadata
struct PCM_PACKAGE
{
wxString name;
wxString description;
wxString description_full;
wxString identifier;
PCM_PACKAGE_TYPE type;
PCM_CONTACT author;
boost::optional<PCM_CONTACT> maintainer;
wxString license;
STRING_MAP resources;
std::vector<std::string> tags;
std::vector<PACKAGE_VERSION> versions;
};
///< Repository reference to a resource
struct PCM_RESOURCE_REFERENCE
{
wxString url;
boost::optional<wxString> sha256;
uint64_t update_timestamp;
};
///< Repository metadata
struct PCM_REPOSITORY
{
wxString name;
PCM_RESOURCE_REFERENCE packages;
boost::optional<PCM_RESOURCE_REFERENCE> resources;
boost::optional<PCM_RESOURCE_REFERENCE> manifests;
boost::optional<PCM_CONTACT> maintainer;
// Not serialized fields
std::vector<PCM_PACKAGE> package_list;
};
///< Package installation entry
struct PCM_INSTALLATION_ENTRY
{
PCM_PACKAGE package;
wxString current_version;
wxString repository_id;
wxString repository_name;
uint64_t install_timestamp;
};
// Teaching json en/decoder to understand wxStrings
namespace nlohmann
{
template <>
struct adl_serializer<wxString>
{
static void to_json( json& j, const wxString& s ) { j = s.ToUTF8(); }
static void from_json( const json& j, wxString& s )
{
s = wxString::FromUTF8( j.get<std::string>().c_str() );
}
};
} // namespace nlohmann
NLOHMANN_JSON_SERIALIZE_ENUM( PCM_PACKAGE_TYPE, {
{ PT_INVALID, "invalid" },
{ PT_PLUGIN, "plugin" },
{ PT_LIBRARY, "library" },
} )
NLOHMANN_JSON_SERIALIZE_ENUM( PCM_PACKAGE_VERSION_STATUS,
{
{ PVS_INVALID, "invalid" },
{ PVS_STABLE, "stable" },
{ PVS_TESTING, "testing" },
{ PVS_DEVELOPMENT, "development" },
{ PVS_DEPRECATED, "deprecated" },
} )
// Following are templates and definitions for en/decoding above structs
// to/from json
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( PCM_CONTACT, name, contact )
void to_json( json& j, const PACKAGE_VERSION& v );
void from_json( const json& j, PACKAGE_VERSION& v );
void to_json( json& j, const PCM_PACKAGE& p );
void from_json( const json& j, PCM_PACKAGE& p );
void to_json( json& j, const PCM_RESOURCE_REFERENCE& r );
void from_json( const json& j, PCM_RESOURCE_REFERENCE& r );
void to_json( json& j, const PCM_REPOSITORY& r );
void from_json( const json& j, PCM_REPOSITORY& r );
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( PCM_INSTALLATION_ENTRY, package, current_version, repository_id,
repository_name, install_timestamp );
#endif // PCM_DATA_H_

View File

@ -0,0 +1,455 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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 "pcm_task_manager.h"
#include "kicad_curl/kicad_curl_easy.h"
#include "paths.h"
#include "picosha2.h"
#include "reporter.h"
#include "wxstream_helper.h"
#include <fstream>
#include <thread>
#include <unordered_set>
#include <wx/dir.h>
#include <wx/filename.h>
#include <wx/fs_zip.h>
#include <wx/msgdlg.h>
#include <wx/sstream.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
void PCM_TASK_MANAGER::DownloadAndInstall( const PCM_PACKAGE& aPackage, const wxString& aVersion,
const wxString& aRepositoryId )
{
PCM_TASK download_task = [aPackage, aVersion, aRepositoryId, this]()
{
wxFileName file_path( m_pcm->Get3rdPartyPath(), "" );
file_path.AppendDir( "cache" );
file_path.SetFullName( wxString::Format( "%s_v%s.zip", aPackage.identifier, aVersion ) );
auto pkgver = std::find_if( aPackage.versions.begin(), aPackage.versions.end(),
[&aVersion]( const PACKAGE_VERSION& pv )
{
return pv.version == aVersion;
} );
wxASSERT_MSG( pkgver != aPackage.versions.end(), "Package version not found" );
if( !wxDirExists( file_path.GetPath() )
&& !wxFileName::Mkdir( file_path.GetPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
{
m_reporter->Report( _( "Unable to create download directory!" ), RPT_SEVERITY_ERROR );
return;
}
int code = downloadFile( file_path.GetFullPath(), pkgver->download_url.get() );
if( code == CURLE_OK )
{
PCM_TASK install_task = [aPackage, aVersion, aRepositoryId, file_path, this]()
{
auto pkgver = std::find_if( aPackage.versions.begin(), aPackage.versions.end(),
[&aVersion]( const PACKAGE_VERSION& pv )
{
return pv.version == aVersion;
} );
const boost::optional<wxString>& hash = pkgver->download_sha256;
bool hash_match = true;
if( hash )
{
std::ifstream stream( file_path.GetFullPath().ToUTF8(), std::ios::binary );
hash_match = m_pcm->VerifyHash( stream, hash.get() );
}
if( !hash_match )
{
m_reporter->Report(
wxString::Format(
_( "Downloaded archive hash for package %s does not match "
"repository entry. This may indicate a problem with the "
"package, if the issue persists report this to repository "
"maintainers." ),
aPackage.identifier ),
RPT_SEVERITY_ERROR );
}
else
{
m_reporter->Report( wxString::Format( _( "Extracting package %s. " ),
aPackage.identifier ) );
if( extract( file_path.GetFullPath(), aPackage.identifier ) )
{
m_pcm->MarkInstalled( aPackage, pkgver->version, aRepositoryId );
// TODO register libraries.
}
else
{
// Cleanup possibly partially extracted package
deletePackageDirectories( aPackage.identifier );
}
}
m_reporter->Report( wxString::Format( _( "Removing downloaded archive %s. " ),
file_path.GetFullName() ) );
wxRemoveFile( file_path.GetFullPath() );
};
m_install_queue.push( install_task );
}
else
{
// Cleanup after ourselves.
wxRemoveFile( file_path.GetFullPath() );
}
};
m_download_queue.push( download_task );
}
int PCM_TASK_MANAGER::downloadFile( const wxString& aFilePath, const wxString& url )
{
TRANSFER_CALLBACK callback = [&]( size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow )
{
if( dltotal > 1024 )
{
m_reporter->SetDownloadProgress( dlnow, dltotal );
}
else
{
m_reporter->SetDownloadProgress( 0.0, 0.0 );
}
return m_reporter->IsCancelled();
};
std::ofstream out( aFilePath.ToUTF8(), std::ofstream::binary );
KICAD_CURL_EASY curl;
curl.SetOutputStream( &out );
curl.SetURL( url.ToUTF8().data() );
curl.SetFollowRedirects( true );
curl.SetTransferCallback( callback, 250000L );
m_reporter->Report( wxString::Format( _( "Downloading package url: %s" ), url ) );
int code = curl.Perform();
out.close();
uint64_t download_total;
if( CURLE_OK == curl.GetTransferTotal( download_total ) )
m_reporter->SetDownloadProgress( download_total, download_total );
if( code != CURLE_OK && code != CURLE_ABORTED_BY_CALLBACK )
{
m_reporter->Report( wxString::Format( _( "Failed to download url %s\n%s" ), url,
curl.GetErrorText( code ) ),
RPT_SEVERITY_ERROR );
}
return code;
}
bool PCM_TASK_MANAGER::extract( const wxString& aFilePath, const wxString& aPackageId )
{
wxFFileInputStream stream( aFilePath );
wxZipInputStream zip( stream );
wxLogNull no_wx_logging;
int entries = zip.GetTotalEntries();
int extracted = 0;
wxArchiveEntry* entry = zip.GetNextEntry();
if( !zip.IsOk() )
{
m_reporter->Report( _( "Error extracting file!" ), RPT_SEVERITY_ERROR );
return false;
}
for( ; entry; entry = zip.GetNextEntry() )
{
wxArrayString path_parts =
wxSplit( entry->GetName(), wxFileName::GetPathSeparator(), (wxChar) NULL );
if( path_parts.size() < 2
|| PCM_PACKAGE_DIRECTORIES.find( path_parts[0] ) == PCM_PACKAGE_DIRECTORIES.end()
|| path_parts[path_parts.size() - 1].IsEmpty() )
{
// Ignore files in the root of the archive and files outside of package dirs.
continue;
}
// m_reporter->Report( wxString::Format( _( "Extracting file \"%s\"\n" ), entry->GetName() ),
// RPT_SEVERITY_INFO );
// Transform paths from
// <PackageRoot>/$folder/$contents
// To
// $KICAD6_3RD_PARTY/$folder/$package_id/$contents
path_parts.Insert( aPackageId, 1 );
path_parts.Insert( m_pcm->Get3rdPartyPath(), 0 );
wxString fullname = wxJoin( path_parts, wxFileName::GetPathSeparator(), (wxChar) NULL );
// Ensure the target directory exists and create it if not.
wxString t_path = wxPathOnly( fullname );
if( !wxDirExists( t_path ) )
{
wxFileName::Mkdir( t_path, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
}
wxTempFileOutputStream out( fullname );
if( !( CopyStreamData( zip, out, entry->GetSize() ) && out.Commit() ) )
{
m_reporter->Report( _( "Error extracting file!" ), RPT_SEVERITY_ERROR );
return false;
}
extracted++;
m_reporter->SetOverallProgress( extracted, entries );
if( m_reporter->IsCancelled() )
break;
}
zip.CloseEntry();
if( m_reporter->IsCancelled() )
{
m_reporter->Report( _( "Aborting package installation." ) );
return false;
}
m_reporter->Report( wxT( "Extracted package\n" ), RPT_SEVERITY_INFO );
m_reporter->SetOverallProgress( entries, entries );
return true;
}
void PCM_TASK_MANAGER::InstallFromFile( wxWindow* aParent, const wxString& aFilePath )
{
wxFFileInputStream stream( aFilePath );
if( !stream.IsOk() )
{
wxLogError( _( "Could not open archive file." ) );
return;
}
wxZipInputStream zip( stream );
if( !zip.IsOk() )
{
wxLogError( _( "Invalid archive file format." ) );
return;
}
nlohmann::json metadata;
for( wxArchiveEntry* entry = zip.GetNextEntry(); entry != nullptr; entry = zip.GetNextEntry() )
{
// Find and load metadata.json
if( entry->GetName() != "metadata.json" )
continue;
wxStringOutputStream strStream;
if( CopyStreamData( zip, strStream, entry->GetSize() ) )
{
try
{
metadata = nlohmann::json::parse( strStream.GetString().ToUTF8().data() );
m_pcm->ValidateJson( metadata );
}
catch( const std::exception& e )
{
wxLogError( wxString::Format( _( "Unable to parse package metadata:\n\n%s" ),
e.what() ) );
break;
}
}
}
if( metadata.empty() )
{
wxLogError( _( "Archive does not contain a valid metadata.json file" ) );
return;
}
PCM_PACKAGE package = metadata.get<PCM_PACKAGE>();
if( package.versions.size() != 1 )
{
wxLogError( _( "Archive metadata must have a single version defined" ) );
return;
}
const auto installed_packages = m_pcm->GetInstalledPackages();
if( std::find_if( installed_packages.begin(), installed_packages.end(),
[&]( const PCM_INSTALLATION_ENTRY& entry )
{
return entry.package.identifier == package.identifier;
} )
!= installed_packages.end() )
{
wxLogError( wxString::Format( _( "Package with identifier %s is already installed, you "
"must first uninstall this package." ),
package.identifier ) );
return;
}
m_reporter = new DIALOG_PCM_PROGRESS( aParent, false );
m_reporter->Show();
if( extract( aFilePath, package.identifier ) )
m_pcm->MarkInstalled( package, package.versions[0].version, "" );
m_reporter->SetFinished();
m_reporter->ShowModal();
m_reporter->Destroy();
}
void PCM_TASK_MANAGER::deletePackageDirectories( const wxString& aPackageId )
{
for( const wxString& dir : PCM_PACKAGE_DIRECTORIES )
{
wxFileName d( m_pcm->Get3rdPartyPath(), "" );
d.AppendDir( dir );
d.AppendDir( aPackageId );
if( d.DirExists() )
{
m_reporter->Report( wxString::Format( _( "Removing directory %s" ), d.GetPath() ) );
if( !d.Rmdir( wxPATH_RMDIR_RECURSIVE ) )
{
m_reporter->Report(
wxString::Format( _( "Failed to remove directory %s" ), d.GetPath() ),
RPT_SEVERITY_ERROR );
}
}
}
}
void PCM_TASK_MANAGER::Uninstall( const PCM_PACKAGE& aPackage )
{
PCM_TASK task = [aPackage, this]
{
deletePackageDirectories( aPackage.identifier );
m_pcm->MarkUninstalled( aPackage );
m_reporter->Report(
wxString::Format( _( "Package %s uninstalled" ), aPackage.identifier ) );
};
m_install_queue.push( task );
}
void PCM_TASK_MANAGER::RunQueue( wxWindow* aParent )
{
m_reporter = new DIALOG_PCM_PROGRESS( aParent );
m_reporter->SetOverallProgressPhases( m_download_queue.size() + m_install_queue.size() );
m_reporter->Show();
std::mutex mutex;
std::condition_variable condvar;
bool download_complete = false;
std::thread download_thread(
[&]()
{
while( !m_download_queue.empty() && !m_reporter->IsCancelled() )
{
PCM_TASK task;
m_download_queue.pop( task );
task();
condvar.notify_all();
}
m_reporter->SetDownloadsFinished();
std::unique_lock<std::mutex> lock( mutex );
download_complete = true;
condvar.notify_all();
} );
std::thread install_thread(
[&]()
{
std::unique_lock<std::mutex> lock( mutex );
do
{
condvar.wait( lock,
[&]()
{
return download_complete || !m_install_queue.empty()
|| m_reporter->IsCancelled();
} );
lock.unlock();
while( !m_install_queue.empty() && !m_reporter->IsCancelled() )
{
PCM_TASK task;
m_install_queue.pop( task );
task();
m_reporter->AdvanceOverallProgressPhase();
}
lock.lock();
} while( ( !m_install_queue.empty() || !download_complete )
&& !m_reporter->IsCancelled() );
if( m_reporter->IsCancelled() )
{
m_reporter->SetOverallProgressPhases( 1 );
m_reporter->SetOverallProgress( 1, 1 );
m_reporter->Report( _( "Aborting remaining tasks." ) );
}
m_reporter->Report( _( "Done." ) );
m_reporter->SetFinished();
} );
m_reporter->ShowModal();
m_reporter->Destroy();
download_thread.join();
install_thread.join();
}

View File

@ -0,0 +1,139 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 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/>.
*/
#ifndef PCM_TASK_MANAGER_H_
#define PCM_TASK_MANAGER_H_
#include "dialogs/dialog_pcm_progress.h"
#include "pcm.h"
#include "pcm_data.h"
#include "sync_queue.h"
#include <atomic>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <nlohmann/json-schema.hpp>
#include <widgets/wx_progress_reporters.h>
#include <wx/string.h>
typedef std::function<void()> PCM_TASK;
/**
* @brief Helper class that handles package (un)installation
*
* Package state changes are first enqueued using DownloadAndInstall/Uninstall methods
* and then applied using RunQueue().
*
* RunQueue() is multithreaded for better experience.
*/
class PCM_TASK_MANAGER
{
public:
PCM_TASK_MANAGER( std::shared_ptr<PLUGIN_CONTENT_MANAGER> pcm ) : m_pcm( pcm ){};
/**
* @brief Enqueue package download and installation
*
* Enqueues a download task for a given package version.
*
* Download task fetches the package archive and if successful enqueues an installation task.
* Installation task verifies sha256 hash if specified, extracts the package, removes the
* downloaded archive and marks package as installed.
*
* Both tasks report their state independently to a progress dialog.
*
* @param aPackage package metadata
* @param aVersion version to be installed
* @param aRepositoryId id of the source repository
*/
void DownloadAndInstall( const PCM_PACKAGE& aPackage, const wxString& aVersion,
const wxString& aRepositoryId );
/**
* @brief Enqueue package uninstallation
*
* Enqueues uninstallation task that removes all package files and marks package
* as uninstalled.
*
* @param aPackage package metadata
*/
void Uninstall( const PCM_PACKAGE& aPackage );
/**
* @brief Run queue of pending actions
*
* This method spawns 2 threads to concurrently run tasks in download and install
* queues until they are drained.
*
* Download queue feeds into install queue so the install thread keeps running until
* download thread indicated that it's finished AND all installs are processed.
*
* @param aParent parent dialog for progress window
*/
void RunQueue( wxWindow* aParent );
/**
* @brief Installs package from an archive file on disk
*
* Unlike DownloadAndInstall/Uninstall methods this one immediately extracts the package
* and marks it as installed.
*
* @param aParent parent dialog for progress window
* @param aFilePath path to the archive file
*/
void InstallFromFile( wxWindow* aParent, const wxString& aFilePath );
private:
/**
* @brief Download URL to a file
*
* @param aFilePath path to file
* @param aUrl URL to download
* @return int CURLE error code
*/
int downloadFile( const wxString& aFilePath, const wxString& aUrl );
/**
* @brief Extract package archive
*
* @param aFilePath path to the archive
* @param aPackageId id of the package
* @return true if archive was extracted successfuly
*/
bool extract( const wxString& aFilePath, const wxString& aPackageId );
/**
* @brief Delete all package files
*
* @param aPackageId id of the package
*/
void deletePackageDirectories( const wxString& aPackageId );
DIALOG_PCM_PROGRESS* m_reporter;
SYNC_QUEUE<PCM_TASK> m_download_queue;
SYNC_QUEUE<PCM_TASK> m_install_queue;
std::shared_ptr<PLUGIN_CONTENT_MANAGER> m_pcm;
};
#endif // PCM_TASK_MANAGER_H_

View File

@ -111,6 +111,14 @@ TOOL_ACTION KICAD_MANAGER_ACTIONS::editDrawingSheet( "kicad.Control.editDrawingS
_( "Drawing Sheet Editor" ), _( "Edit drawing sheet borders and title block" ),
BITMAPS::icon_pagelayout_editor_24 );
#ifdef PCM
TOOL_ACTION KICAD_MANAGER_ACTIONS::showPluginManager( "kicad.Control.pluginContentManager",
AS_GLOBAL,
MD_CTRL + 'M', "",
_( "Plugin and Content Manager" ), _( "Run Plugin and Content Manager" ),
BITMAPS::icon_pcm_24 );
#endif
TOOL_ACTION KICAD_MANAGER_ACTIONS::openTextEditor( "kicad.Control.openTextEditor",
AS_GLOBAL,
0, "",

View File

@ -46,6 +46,9 @@ public:
static TOOL_ACTION showCalculator;
static TOOL_ACTION editDrawingSheet;
static TOOL_ACTION openTextEditor;
#ifdef PCM
static TOOL_ACTION showPluginManager;
#endif
static TOOL_ACTION editOtherSch;
static TOOL_ACTION editOtherPCB;

View File

@ -34,8 +34,12 @@
#include <wx/checkbox.h>
#include <wx/dir.h>
#include <wx/filedlg.h>
#ifdef PCM
#include "dialog_pcm.h"
#endif
///< Helper widget to select whether a new directory should be created for a project.
///> Helper widget to select whether a new directory should be created for a project.
class DIR_CHECKBOX : public wxPanel
{
public:
@ -752,6 +756,14 @@ int KICAD_MANAGER_CONTROL::Execute( const TOOL_EVENT& aEvent )
execFile = EESCHEMA_EXE;
else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::editOtherPCB ) )
execFile = PCBNEW_EXE;
#ifdef PCM
else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::showPluginManager ) )
{
DIALOG_PCM* pcm = new DIALOG_PCM( m_frame );
pcm->ShowModal();
pcm->Destroy();
}
#endif
else
wxFAIL_MSG( "Execute(): unexpected request" );
@ -814,4 +826,8 @@ void KICAD_MANAGER_CONTROL::setTransitions()
Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::editOtherSch.MakeEvent() );
Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::editOtherPCB.MakeEvent() );
#ifdef PCM
Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::showPluginManager.MakeEvent() );
#endif
}

View File

@ -83,7 +83,7 @@ int SCRIPTING_TOOL::reloadPlugins( const TOOL_EVENT& aEvent )
ACTION_PLUGINS::UnloadAll();
{
PyLOCK lock;
PyLOCK lock;
callLoadPlugins();
}
@ -104,18 +104,18 @@ void SCRIPTING_TOOL::callLoadPlugins()
// Load pcbnew inside Python and load all the user plugins and package-based plugins
using namespace pybind11::literals;
auto locals = pybind11::dict( "sys_path"_a = TO_UTF8( SCRIPTING::PyScriptingPath(
SCRIPTING::PATH_TYPE::STOCK ) ),
"user_path"_a = TO_UTF8( SCRIPTING::PyScriptingPath(
SCRIPTING::PATH_TYPE::USER ) ),
"third_party_path"_a = TO_UTF8( SCRIPTING::PyPluginsPath(
SCRIPTING::PATH_TYPE::THIRDPARTY ) ) );
auto locals = pybind11::dict(
"sys_path"_a = TO_UTF8( SCRIPTING::PyScriptingPath( SCRIPTING::PATH_TYPE::STOCK ) ),
"user_path"_a = TO_UTF8( SCRIPTING::PyScriptingPath( SCRIPTING::PATH_TYPE::USER ) ),
"third_party_path"_a =
TO_UTF8( SCRIPTING::PyPluginsPath( SCRIPTING::PATH_TYPE::THIRDPARTY ) ) );
pybind11::exec( R"(
import sys
import pcbnew
pcbnew.LoadPlugins( sys_path, user_path, third_party_path )
)", pybind11::globals(), locals );
)",
pybind11::globals(), locals );
}

View File

@ -459,8 +459,12 @@ wxString SCRIPTING::PyScriptingPath( PATH_TYPE aPathType )
//@todo This should this be a user configurable variable eg KISCRIPT?
switch( aPathType )
{
case STOCK: path = PATHS::GetStockScriptingPath(); break;
case USER: path = PATHS::GetUserScriptingPath(); break;
case STOCK:
path = PATHS::GetStockScriptingPath();
break;
case USER:
path = PATHS::GetUserScriptingPath();
break;
case THIRDPARTY:
const ENV_VAR_MAP& env = Pgm().GetLocalEnvVariables();
auto it = env.find( "KICAD6_3RD_PARTY" );