ADDED: Git integration support

Adds support for project-based git integration, branch support, commit,
revert and updates

Fixes https://gitlab.com/kicad/code/kicad/issues/10441
This commit is contained in:
Seth Hillbrand 2023-09-14 14:39:42 -07:00
parent 8f96009fe5
commit d99641be40
196 changed files with 13121 additions and 38 deletions

View File

@ -746,6 +746,12 @@ find_package(ZLIB REQUIRED)
#
find_package( CURL REQUIRED )
#
# Find libssl, required
#
find_package( OpenSSL REQUIRED )
include_directories( SYSTEM ${OPENSSL_INCLUDE_DIR} )
#
# Find Cairo library, required
#
@ -755,6 +761,12 @@ include_directories( SYSTEM ${CAIRO_INCLUDE_DIR} )
find_package( Pixman 0.30 REQUIRED )
include_directories( SYSTEM ${PIXMAN_INCLUDE_DIR} )
# Find libgit2, required
find_package( libgit2 REQUIRED )
# Set include directories for libgit
include_directories(${LIBGIT2_INCLUDE_DIRS})
#
# Find Boost headers and libraries, required.
set( BOOST_REQUESTED_COMPONENTS locale ) # locale is required by nanoodbc/database libraries

38
cmake/Findlibgit2.cmake Normal file
View File

@ -0,0 +1,38 @@
# SPDX-FileCopyrightText: 2014 Dan Leinir Turthra Jensen <admin@leinir.dk
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# - Try to find the libgit2 library
# Once done this will define
#
# LIBGIT2_FOUND - System has libgit2
# LIBGIT2_INCLUDE_DIR - The libgit2 include directory
# LIBGIT2_LIBRARIES - The libraries needed to use libgit2
# LIBGIT2_DEFINITIONS - Compiler switches required for using libgit2
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
find_package(PkgConfig)
pkg_search_module(PC_LIBGIT2 libgit2)
set(LIBGIT2_DEFINITIONS ${PC_LIBGIT2_CFLAGS_OTHER})
find_path(LIBGIT2_INCLUDE_DIR NAMES git2.h
HINTS
${PC_LIBGIT2_INCLUDEDIR}
${PC_LIBGIT2_INCLUDE_DIRS}
)
find_library(LIBGIT2_LIBRARIES NAMES git2
HINTS
${PC_LIBGIT2_LIBDIR}
${PC_LIBGIT2_LIBRARY_DIRS}
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(libgit2 DEFAULT_MSG LIBGIT2_LIBRARIES LIBGIT2_INCLUDE_DIR)
mark_as_advanced(LIBGIT2_INCLUDE_DIR LIBGIT2_LIBRARIES)

View File

@ -131,6 +131,8 @@ target_link_libraries( kicommon
fmt::fmt
CURL::libcurl
${wxWidgets_LIBRARIES}
${LIBGIT2_LIBRARIES}
${OPENSSL_LIBRARIES}
# needed by kiid to allow linking for Boost for the UUID against bcrypt (msys2 only)
${EXTRA_LIBS}
@ -195,7 +197,19 @@ set( COMMON_ABOUT_DLG_SRCS
dialog_about/dialog_about_base.cpp
)
set( COMMON_GIT_DLG_SRCS
dialogs/git/dialog_git_commit.cpp
dialogs/git/dialog_git_switch.cpp
dialogs/git/dialog_git_auth.cpp
dialogs/git/dialog_git_repository.cpp
dialogs/git/dialog_git_repository_base.cpp
dialogs/git/panel_git_repos.cpp
dialogs/git/panel_git_repos_base.cpp
)
set( COMMON_DLG_SRCS
${COMMON_GIT_DLG_SRCS}
dialogs/dialog_assign_netclass.cpp
dialogs/dialog_assign_netclass_base.cpp
dialogs/dialog_book_reporter.cpp
@ -390,6 +404,20 @@ set( COMMON_IMPORT_GFX_SRCS
import_gfx/svg_import_plugin.cpp
)
set( COMMON_GIT_SRCS
git/git_add_to_index_handler.cpp
git/git_clone_handler.cpp
git/git_commit_handler.cpp
git/git_pull_handler.cpp
git/git_push_handler.cpp
git/git_remove_from_index_handler.cpp
git/git_resolve_conflict_handler.cpp
git/git_revert_handler.cpp
git/git_sync_handler.cpp
git/kicad_git_common.cpp
git/kicad_git_errors.cpp
)
set( COMMON_SRCS
${LIB_KICAD_SRCS}
${COMMON_ABOUT_DLG_SRCS}
@ -405,6 +433,7 @@ set( COMMON_SRCS
${PLUGINS_EASYEDAPRO_SRCS}
${FONT_SRCS}
${COMMON_IMPORT_GFX_SRCS}
${COMMON_GIT_SRCS}
jobs/job_dispatcher.cpp
background_jobs_monitor.cpp
base_screen.cpp

View File

@ -215,6 +215,8 @@ static const wxChar UseClipper2[] = wxT( "UseClipper2" );
static const wxChar EnableGenerators[] = wxT( "EnableGenerators" );
static const wxChar EnableGit[] = wxT( "EnableGit" );
/**
* The time in milliseconds to wait before displaying a disambiguation menu.
*/
@ -345,6 +347,7 @@ ADVANCED_CFG::ADVANCED_CFG()
m_ShowRepairSchematic = false;
m_ShowPropertiesPanel = false;
m_EnableGenerators = false;
m_EnableGit = false;
m_3DRT_BevelHeight_um = 30;
m_3DRT_BevelExtentFactor = 1.0 / 16.0;
@ -518,6 +521,9 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg )
configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableGenerators,
&m_EnableGenerators, m_EnableGenerators ) );
configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableGit,
&m_EnableGit, m_EnableGit ) );
// Special case for trace mask setting...we just grab them and set them immediately

View File

@ -30,6 +30,13 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::e_48].emplace_back( BITMAPS::e_48, wxT( "e_48_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::e_96].emplace_back( BITMAPS::e_96, wxT( "e_96_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::e_192].emplace_back( BITMAPS::e_192, wxT( "e_192_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::git_add].emplace_back( BITMAPS::git_add, wxT( "git_add_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::git_changed_ahead].emplace_back( BITMAPS::git_changed_ahead, wxT( "git_changed_ahead_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::git_conflict].emplace_back( BITMAPS::git_conflict, wxT( "git_conflict_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::git_delete].emplace_back( BITMAPS::git_delete, wxT( "git_delete_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::git_good_check].emplace_back( BITMAPS::git_good_check, wxT( "git_good_check_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::git_modified].emplace_back( BITMAPS::git_modified, wxT( "git_modified_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::git_out_of_date].emplace_back( BITMAPS::git_out_of_date, wxT( "git_out_of_date_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::icon_bitmap2component_16].emplace_back( BITMAPS::icon_bitmap2component_16, wxT( "icon_bitmap2component_16_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::icon_eeschema_16].emplace_back( BITMAPS::icon_eeschema_16, wxT( "icon_eeschema_16_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::icon_gerbview_16].emplace_back( BITMAPS::icon_gerbview_16, wxT( "icon_gerbview_16_16.png" ), 16, wxT( "light" ) );
@ -101,6 +108,13 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::visibility].emplace_back( BITMAPS::visibility, wxT( "visibility_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::visibility_off].emplace_back( BITMAPS::visibility_off, wxT( "visibility_off_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::www].emplace_back( BITMAPS::www, wxT( "www_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::git_add].emplace_back( BITMAPS::git_add, wxT( "git_add_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::git_changed_ahead].emplace_back( BITMAPS::git_changed_ahead, wxT( "git_changed_ahead_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::git_conflict].emplace_back( BITMAPS::git_conflict, wxT( "git_conflict_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::git_delete].emplace_back( BITMAPS::git_delete, wxT( "git_delete_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::git_good_check].emplace_back( BITMAPS::git_good_check, wxT( "git_good_check_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::git_modified].emplace_back( BITMAPS::git_modified, wxT( "git_modified_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::git_out_of_date].emplace_back( BITMAPS::git_out_of_date, wxT( "git_out_of_date_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::icon_bitmap2component_16].emplace_back( BITMAPS::icon_bitmap2component_16, wxT( "icon_bitmap2component_16_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::icon_eeschema_16].emplace_back( BITMAPS::icon_eeschema_16, wxT( "icon_eeschema_16_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::icon_gerbview_16].emplace_back( BITMAPS::icon_gerbview_16, wxT( "icon_gerbview_16_dark_16.png" ), 16, wxT( "dark" ) );

View File

@ -0,0 +1,96 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "dialog_git_auth.h"
DIALOG_GIT_AUTH::DIALOG_GIT_AUTH(wxWindow* parent)
: DIALOG_SHIM(parent, wxID_ANY, _("Connection"), wxDefaultPosition, wxSize(400, 300))
{
CreateControls();
Centre();
}
DIALOG_GIT_AUTH::~DIALOG_GIT_AUTH()
{
}
void DIALOG_GIT_AUTH::CreateControls()
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
this->SetSizer(mainSizer);
m_NameTextCtrl = new wxTextCtrl(this, wxID_ANY);
m_UrlTextCtrl = new wxTextCtrl(this, wxID_ANY);
m_AuthChoice = new wxChoice(this, wxID_ANY);
m_AuthChoice->Append(_("Basic"));
m_AuthChoice->Append(_("SSH"));
m_UserNameTextCtrl = new wxTextCtrl(this, wxID_ANY);
m_PasswordTextCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD);
m_PublicKeyPicker = new wxFilePickerCtrl(this, wxID_ANY);
m_PublicKeyPicker->Hide();
m_TestButton = new wxButton(this, wxID_ANY, _("Test"));
m_OkButton = new wxButton(this, wxID_OK, _("OK"));
m_CancelButton = new wxButton(this, wxID_CANCEL, _("Cancel"));
mainSizer->Add(new wxStaticText(this, wxID_ANY, _("Name")), 0, wxALL, 10);
mainSizer->Add(m_NameTextCtrl, 0, wxEXPAND | wxALL, 10);
mainSizer->Add(new wxStaticText(this, wxID_ANY, _("Url")), 0, wxALL, 10);
mainSizer->Add(m_UrlTextCtrl, 0, wxEXPAND | wxALL, 10);
mainSizer->Add(new wxStaticText(this, wxID_ANY, _("Authentication")), 0, wxALL, 10);
mainSizer->Add(m_AuthChoice, 0, wxEXPAND | wxALL, 10);
mainSizer->Add(new wxStaticText(this, wxID_ANY, _("User Name")), 0, wxALL, 10);
mainSizer->Add(m_UserNameTextCtrl, 0, wxEXPAND | wxALL, 10);
mainSizer->Add(new wxStaticText(this, wxID_ANY, _("Password")), 0, wxALL, 10);
mainSizer->Add(m_PasswordTextCtrl, 0, wxEXPAND | wxALL, 10);
mainSizer->Add(m_PublicKeyPicker, 0, wxEXPAND | wxALL, 10);
wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
buttonSizer->Add(m_TestButton, 1, wxALL, 10);
buttonSizer->Add(m_OkButton, 1, wxALL, 10);
buttonSizer->Add(m_CancelButton, 1, wxALL, 10);
mainSizer->Add(buttonSizer, 0, wxALIGN_RIGHT);
mainSizer->Layout();
// Bind event for authentication method choice change
m_AuthChoice->Bind(wxEVT_CHOICE, &DIALOG_GIT_AUTH::onAuthChoiceChanged, this);
m_TestButton->Bind(wxEVT_BUTTON, &DIALOG_GIT_AUTH::onTestClick, this );
}
void DIALOG_GIT_AUTH::onAuthChoiceChanged(wxCommandEvent& event)
{
if (m_AuthChoice->GetStringSelection() == "SSH")
{
m_PasswordTextCtrl->Hide();
m_PublicKeyPicker->Show();
}
else
{
m_PasswordTextCtrl->Show();
m_PublicKeyPicker->Hide();
}
Layout(); // Re-arrange the controls
}

View File

@ -0,0 +1,59 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GIT_REPOSITORY_DIALOG_H
#define GIT_REPOSITORY_DIALOG_H
#include <wx/button.h>
#include <wx/choice.h>
#include <wx/filepicker.h>
#include <wx/textctrl.h>
#include <wx/wx.h>
#include <dialog_shim.h>
class DIALOG_GIT_AUTH : public DIALOG_SHIM
{
public:
DIALOG_GIT_AUTH( wxWindow* parent );
virtual ~DIALOG_GIT_AUTH();
private:
void CreateControls();
void onAuthChoiceChanged( wxCommandEvent& event );
void onTestClick( wxCommandEvent& aEvent );
void onOKClick( wxCommandEvent& aEvent );
void onCancelClick( wxCommandEvent& aEvent );
wxTextCtrl* m_NameTextCtrl;
wxTextCtrl* m_UrlTextCtrl;
wxChoice* m_AuthChoice;
wxTextCtrl* m_UserNameTextCtrl;
wxTextCtrl* m_PasswordTextCtrl;
wxButton* m_TestButton;
wxButton* m_OkButton;
wxButton* m_CancelButton;
wxFilePickerCtrl* m_PublicKeyPicker;
};
#endif // GIT_REPOSITORY_DIALOG_H

View File

@ -0,0 +1,204 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "dialog_git_commit.h"
#include <bitmaps/bitmaps_list.h>
#include <bitmaps/bitmap_types.h>
#include <git/kicad_git_common.h>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/listctrl.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
DIALOG_GIT_COMMIT::DIALOG_GIT_COMMIT( wxWindow* parent, git_repository* repo,
const wxString& defaultAuthorName,
const wxString& defaultAuthorEmail,
const std::map<wxString, int>& filesToCommit ) :
DIALOG_SHIM( parent, wxID_ANY, "Commit Changes" )
{
wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
// List Control for files to commit
m_listCtrl = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxLC_REPORT | wxLC_SINGLE_SEL );
// Set up columns
m_listCtrl->EnableCheckBoxes();
m_listCtrl->AppendColumn( "Filename" );
m_listCtrl->AppendColumn( "Placeholder" );
// Set column widths
m_listCtrl->SetColumnWidth(0, 200);
m_listCtrl->SetColumnWidth(1, 200);
// Set up image list for icons
wxImageList* imageList = new wxImageList( 16, 16, true,
static_cast<int>( KIGIT_COMMON::GIT_STATUS::GIT_STATUS_LAST ) );
imageList->Add( KiBitmap( BITMAPS::git_good_check ) ); // PLACEHOLDER
imageList->Add( KiBitmap( BITMAPS::git_good_check ) ); // GIT_STATUS_CURRENT
imageList->Add( KiBitmap( BITMAPS::git_modified ) ); // GIT_STATUS_MODIFIED
imageList->Add( KiBitmap( BITMAPS::git_add ) ); // GIT_STATUS_ADDED
imageList->Add( KiBitmap( BITMAPS::git_delete ) ); // GIT_STATUS_DELETED
imageList->Add( KiBitmap( BITMAPS::git_out_of_date ) ); // GIT_STATUS_BEHIND
imageList->Add( KiBitmap( BITMAPS::git_changed_ahead ) );// GIT_STATUS_AHEAD
imageList->Add( KiBitmap( BITMAPS::git_conflict ) ); // GIT_STATUS_CONFLICTED
// Assign the image list to the list control
m_listCtrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
// Populate list control with items
for ( auto& [filename, status] : filesToCommit )
{
int i = m_listCtrl->GetItemCount();
m_listCtrl->InsertItem(i, filename );
if( status & ( GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW ) )
{
m_listCtrl->SetItem( i, 1, "New" );
m_listCtrl->SetItemImage(i, static_cast<int>( KIGIT_COMMON::GIT_STATUS::GIT_STATUS_ADDED ) );
}
else if( status & ( GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED ) )
{
m_listCtrl->SetItem( i, 1, "Modified" );
m_listCtrl->SetItemImage(i, static_cast<int>( KIGIT_COMMON::GIT_STATUS::GIT_STATUS_MODIFIED ) );
}
else if( status & ( GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED ) )
{
m_listCtrl->SetItem( i, 1, "Deleted" );
m_listCtrl->SetItemImage(i, static_cast<int>( KIGIT_COMMON::GIT_STATUS::GIT_STATUS_DELETED ) );
}
else
{
printf(" Unknown status: %d\n", status );
}
}
sizer->Add(m_listCtrl, 1, wxEXPAND | wxALL, 5);
// Commit Message Text Control
wxStaticText* commitMessageLabel = new wxStaticText( this, wxID_ANY, _( "Commit Message:" ) );
m_commitMessageTextCtrl =
new wxTextCtrl( this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE );
sizer->Add( commitMessageLabel, 0, wxALL, 5 );
sizer->Add( m_commitMessageTextCtrl, 1, wxEXPAND | wxALL, 5 );
// Author Name and Email Text Control
wxStaticText* authorLabel = new wxStaticText( this, wxID_ANY, _( "Author:" ) );
wxString defaultAuthor = defaultAuthorName + " <" + defaultAuthorEmail + ">";
m_authorTextCtrl =
new wxTextCtrl( this, wxID_ANY, defaultAuthor, wxDefaultPosition, wxDefaultSize, 0 );
sizer->Add( authorLabel, 0, wxALL, 5 );
sizer->Add( m_authorTextCtrl, 0, wxEXPAND | wxALL, 5 );
// OK and Cancel Buttons
wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer();
m_okButton = new wxButton( this, wxID_OK, _( "OK" ) );
wxButton* cancelButton = new wxButton( this, wxID_CANCEL, _( "Cancel" ) );
buttonSizer->Add( cancelButton, 0, wxALL, 5 );
buttonSizer->Add( m_okButton, 0, wxALL, 5 );
buttonSizer->Realize();
sizer->Add( buttonSizer, 0, wxALIGN_RIGHT | wxALL, 5 );
SetSizerAndFit( sizer );
SetupStandardButtons( { { wxID_OK, _( "C&ommit" ) } } );
// Bind events
Bind( wxEVT_TEXT, &DIALOG_GIT_COMMIT::OnTextChanged, this, m_commitMessageTextCtrl->GetId() );
// Set the repository and defaults
m_repo = repo;
m_defaultAuthorName = defaultAuthorName;
m_defaultAuthorEmail = defaultAuthorEmail;
}
void DIALOG_GIT_COMMIT::OnTextChanged( wxCommandEvent& aEvent )
{
if( m_commitMessageTextCtrl->GetValue().IsEmpty() )
{
m_okButton->Disable();
m_okButton->SetToolTip( _( "Commit message cannot be empty" ) );
}
else
{
m_okButton->Enable();
m_okButton->SetToolTip( wxEmptyString );
}
}
wxString DIALOG_GIT_COMMIT::GetCommitMessage() const
{
return m_commitMessageTextCtrl->GetValue();
}
wxString DIALOG_GIT_COMMIT::GetAuthorName() const
{
wxString authorText = m_authorTextCtrl->GetValue();
size_t pos = authorText.find( '<' );
if( pos != wxString::npos )
return authorText.substr( 0, pos ).Trim();
return wxEmptyString;
}
wxString DIALOG_GIT_COMMIT::GetAuthorEmail() const
{
wxString authorText = m_authorTextCtrl->GetValue();
size_t startPos = authorText.find( '<' );
size_t endPos = authorText.find( '>' );
if( startPos != wxString::npos && endPos != wxString::npos && startPos < endPos )
return authorText.substr( startPos + 1, endPos - startPos - 1 ).Trim();
return wxEmptyString;
}
std::vector<wxString> DIALOG_GIT_COMMIT::GetSelectedFiles() const
{
std::vector<wxString> selectedFiles;
long item = -1;
while( ( item = m_listCtrl->GetNextItem( item, wxLIST_NEXT_ALL ) )
!= -1 )
{
if( m_listCtrl->IsItemChecked( item ) )
selectedFiles.push_back( m_listCtrl->GetItemText( item ) );
}
return selectedFiles;
}

View File

@ -0,0 +1,62 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021-2022 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, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef DIALOG_GIT_COMMIT_H
#define DIALOG_GIT_COMMIT_H
#include <dialog_shim.h>
#include <git2.h>
class wxCheckBox;
class wxListCtrl;
class DIALOG_GIT_COMMIT : public DIALOG_SHIM
{
public:
DIALOG_GIT_COMMIT( wxWindow* parent, git_repository* repo,
const wxString& defaultAuthorName,
const wxString& defaultAuthorEmail,
const std::map<wxString, int>& filesToCommit );
wxString GetCommitMessage() const;
wxString GetAuthorName() const;
wxString GetAuthorEmail() const;
std::vector<wxString> GetSelectedFiles() const;
void OnTextChanged( wxCommandEvent& event );
private:
wxTextCtrl* m_commitMessageTextCtrl;
wxTextCtrl* m_authorTextCtrl;
wxListCtrl* m_listCtrl;
wxButton* m_okButton;
git_repository* m_repo;
wxString m_defaultAuthorName;
wxString m_defaultAuthorEmail;
std::vector<wxString> m_filesToCommit;
};
#endif // DIALOG_GIT_COMMIT_H

View File

@ -0,0 +1,49 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021-2022 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, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <wx/progdlg.h>
#include <wx/statusbr.h>
#include <dialog_shim.h>
class GIT_PROGRESS : public DIALOG_SHIM {
public:
GIT_PROGRESS(wxWindow* aParent, int aMaxValue1, int aMaxValue2);
~GIT_PROGRESS();
void SetStatusText(const wxString& aText);
bool UpdateProgressBar1(int aValue);
bool UpdateProgressBar2(int aValue);
private:
void OnCancel(wxCommandEvent& aEvent);
wxGauge* m_progBar1;
wxGauge* m_progBar2;
wxButton* m_cancelBtn;
wxStatusBar* m_statusBar;
bool m_cancelled;
DECLARE_EVENT_TABLE()
};

View File

@ -0,0 +1,448 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "dialog_git_repository.h"
#include <confirm.h>
#include <git2.h>
#include <gestfich.h>
#include <cerrno>
#include <cstring>
#include <fstream>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <wx/clipbrd.h>
#include <wx/msgdlg.h>
#include <wx/regex.h>
#include <wx/stdpaths.h>
DIALOG_GIT_REPOSITORY::DIALOG_GIT_REPOSITORY( wxWindow* aParent, git_repository* aRepository, wxString aURL ) :
DIALOG_GIT_REPOSITORY_BASE( aParent ), m_repository( aRepository ), m_prevFile( wxEmptyString ),
m_tested( 0 ), m_failedTest( false ), m_testError( wxEmptyString ), m_tempRepo( false ),
m_repoType( KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_LOCAL )
{
m_txtName->SetFocus();
if( !m_repository )
{
// Make a temporary repository to test the connection
m_tempRepo = true;
m_tempPath = wxFileName::CreateTempFileName( "kicadtestrepo" );
git_repository_init_options options = GIT_REPOSITORY_INIT_OPTIONS_INIT;
options.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT;
git_repository_init_ext( &m_repository, m_tempPath.ToStdString().c_str(), &options );
}
if( !aURL.empty() )
m_txtURL->SetValue( aURL );
else
extractClipboardData();
if( !m_txtURL->GetValue().IsEmpty() )
updateURLData();
SetupStandardButtons();
updateAuthControls();
}
DIALOG_GIT_REPOSITORY::~DIALOG_GIT_REPOSITORY()
{
if( m_tempRepo )
{
git_repository_free( m_repository );
RmDirRecursive( m_tempPath );
}
}
bool DIALOG_GIT_REPOSITORY::extractClipboardData()
{
if( wxTheClipboard->Open() && wxTheClipboard->IsSupported( wxDF_TEXT ) )
{
wxString clipboardText;
wxTextDataObject textData;
if( wxTheClipboard->GetData( textData ) && !( clipboardText = textData.GetText() ).empty() )
{
if( std::get<0>( isValidHTTPS( clipboardText ) )
|| std::get<0>( isValidSSH( clipboardText ) ) )
m_txtURL->SetValue( clipboardText );
}
wxTheClipboard->Close();
}
return false;
}
void DIALOG_GIT_REPOSITORY::setDefaultSSHKey()
{
wxFileName sshKey;
sshKey.SetPath( wxGetUserHome() );
wxString retval;
sshKey.AppendDir( ".ssh" );
sshKey.SetFullName( "id_rsa" );
if( sshKey.FileExists() )
{
retval = sshKey.GetFullPath();
}
else if( sshKey.SetFullName( "id_dsa" ); sshKey.FileExists() )
{
retval = sshKey.GetFullPath();
}
else if( sshKey.SetFullName( "id_ecdsa" ); sshKey.FileExists() )
{
retval = sshKey.GetFullPath();
}
if( !retval.empty() )
{
m_fpSSHKey->SetFileName( retval );
wxFileDirPickerEvent evt;
evt.SetPath( retval );
OnFileUpdated( evt );
}
}
void DIALOG_GIT_REPOSITORY::OnUpdateUI( wxUpdateUIEvent& event )
{
// event.Enable( !m_txtName->GetValue().IsEmpty() && !m_txtURL->GetValue().IsEmpty() );
}
void DIALOG_GIT_REPOSITORY::SetEncrypted( bool aEncrypted )
{
if( aEncrypted )
{
m_txtPassword->Enable();
m_txtPassword->SetToolTip( _( "Enter the password for the SSH key" ) );
}
else
{
m_txtPassword->SetValue( wxEmptyString );
m_txtPassword->SetToolTip( wxEmptyString );
m_txtPassword->Disable();
}
}
std::tuple<bool,wxString,wxString,wxString> DIALOG_GIT_REPOSITORY::isValidHTTPS( const wxString& url )
{
wxRegEx regex( R"((https?:\/\/)(([^:]+)(:([^@]+))?@)?([^\/]+\/[^\s]+))" );
if( regex.Matches( url ) )
{
wxString username = regex.GetMatch( url, 3 );
wxString password = regex.GetMatch( url, 5 );
wxString repoAddress = regex.GetMatch( url, 1 ) + regex.GetMatch( url, 6 );
return std::make_tuple( true, username, password, repoAddress );
}
return std::make_tuple( false, "", "", "" );
}
std::tuple<bool,wxString, wxString> DIALOG_GIT_REPOSITORY::isValidSSH( const wxString& url )
{
wxRegEx regex( R"((?:ssh:\/\/)?([^@]+)@([^\/]+\/[^\s]+))" );
if( regex.Matches( url ) )
{
wxString username = regex.GetMatch( url, 1 );
wxString repoAddress = regex.GetMatch( url, 2 );
return std::make_tuple( true, username, repoAddress );
}
return std::make_tuple( false, "", "" );
}
static wxString get_repo_name( wxString& aRepoAddr )
{
wxString retval;
size_t last_slash = aRepoAddr.find_last_of( '/' );
bool ends_with_dot_git = aRepoAddr.EndsWith( ".git" );
if( ends_with_dot_git )
retval = aRepoAddr.substr( last_slash + 1, aRepoAddr.size() - last_slash - 5 );
else
retval = aRepoAddr.substr( last_slash + 1, aRepoAddr.size() - last_slash );
return retval;
}
void DIALOG_GIT_REPOSITORY::OnLocationExit( wxFocusEvent& event )
{
updateURLData();
updateAuthControls();
}
void DIALOG_GIT_REPOSITORY::updateURLData()
{
wxString url = m_txtURL->GetValue();
if( url.IsEmpty() )
return;
if( url.Contains( "https://" ) || url.Contains( "http://" ) )
{
auto [valid, username, password, repoAddress] = isValidHTTPS( url );
if( valid )
{
m_ConnType->SetSelection( static_cast<int>( KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_HTTPS ) );
SetUsername( username );
SetPassword( password );
m_txtURL->SetValue( repoAddress );
if( m_txtName->GetValue().IsEmpty() )
m_txtName->SetValue( get_repo_name( repoAddress ) );
}
}
else if( url.Contains( "ssh://" ) || url.Contains( "git@" ) )
{
auto [valid, username, repoAddress] = isValidSSH( url );
if( valid )
{
m_ConnType->SetSelection( static_cast<int>( KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_SSH ) );
m_txtUsername->SetValue( username );
m_txtURL->SetValue( repoAddress );
if( m_txtName->GetValue().IsEmpty() )
m_txtName->SetValue( get_repo_name( repoAddress ) );
setDefaultSSHKey();
}
}
}
void DIALOG_GIT_REPOSITORY::OnTestClick( wxCommandEvent& event )
{
git_remote* remote = nullptr;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
// We track if we have already tried to connect.
// If we have, the server may come back to offer another connection
// type, so we need to keep track of how many times we have tried.
m_tested = 0;
callbacks.credentials = []( git_cred** aOut, const char* aUrl, const char* aUsername,
unsigned int aAllowedTypes, void* aPayload ) -> int
{
DIALOG_GIT_REPOSITORY* dialog = static_cast<DIALOG_GIT_REPOSITORY*>( aPayload );
if( dialog->GetRepoType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_LOCAL )
return GIT_PASSTHROUGH;
if( aAllowedTypes & GIT_CREDENTIAL_USERNAME
&& !( dialog->GetTested() & GIT_CREDTYPE_USERNAME ) )
{
wxString username = dialog->GetUsername().Trim().Trim( false );
git_cred_username_new( aOut, username.ToStdString().c_str() );
dialog->GetTested() |= GIT_CREDTYPE_USERNAME;
}
else if( dialog->GetRepoType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_HTTPS
&& ( aAllowedTypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT )
&& !( dialog->GetTested() & GIT_CREDTYPE_USERPASS_PLAINTEXT ) )
{
wxString username = dialog->GetUsername().Trim().Trim( false );
wxString password = dialog->GetPassword().Trim().Trim( false );
git_cred_userpass_plaintext_new( aOut, username.ToStdString().c_str(),
password.ToStdString().c_str() );
dialog->GetTested() |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
}
else if( dialog->GetRepoType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_SSH
&& ( aAllowedTypes & GIT_CREDENTIAL_SSH_KEY )
&& !( dialog->GetTested() & GIT_CREDTYPE_SSH_KEY ) )
{
// SSH key authentication
wxString sshKey = dialog->GetRepoSSHPath();
wxString sshPubKey = sshKey + ".pub";
wxString username = dialog->GetUsername().Trim().Trim( false );
wxString password = dialog->GetPassword().Trim().Trim( false );
git_cred_ssh_key_new( aOut, username.ToStdString().c_str(),
sshPubKey.ToStdString().c_str(), sshKey.ToStdString().c_str(),
password.ToStdString().c_str() );
dialog->GetTested() |= GIT_CREDTYPE_SSH_KEY;
}
else
{
return GIT_PASSTHROUGH;
}
return GIT_OK;
};
callbacks.payload = this;
git_remote_create_with_fetchspec( &remote, m_repository, "origin", m_txtURL->GetValue(),
"+refs/heads/*:refs/remotes/origin/*" );
if( git_remote_connect( remote, GIT_DIRECTION_FETCH, &callbacks, nullptr, nullptr ) != GIT_OK )
SetTestResult( true, git_error_last()->message );
else
SetTestResult( false, wxEmptyString );
git_remote_disconnect( remote );
git_remote_free( remote );
auto dlg = wxMessageDialog( this, wxEmptyString, _( "Test connection" ), wxOK | wxICON_INFORMATION );
if( !m_failedTest )
{
dlg.SetMessage( _( "Connection successful" ) );
}
else
{
dlg.SetMessage( wxString::Format( _( "Could not connect to '%s' " ), m_txtURL->GetValue() ) );
dlg.SetExtendedMessage( m_testError );
}
dlg.ShowModal();
}
void DIALOG_GIT_REPOSITORY::OnFileUpdated( wxFileDirPickerEvent& aEvent )
{
wxString file = aEvent.GetPath();
if( file.ends_with( ".pub" ) )
file = file.Left( file.size() - 4 );
std::ifstream ifs( file.ToStdString() );
if( !ifs.good() || !ifs.is_open() )
{
DisplayErrorMessage( this, wxString::Format( _( "Could not open private key '%s'" ), file ),
wxString::Format( "%s: %d", std::strerror( errno ), errno ) );
return;
}
std::string line;
std::getline( ifs, line );
bool isValid = ( line.find( "PRIVATE KEY" ) != std::string::npos );
bool isEncrypted = ( line.find( "ENCRYPTED" ) != std::string::npos );
if( !isValid )
{
DisplayErrorMessage( this, _( "Invalid SSH Key" ), _( "The selected file is not a valid SSH private key" ) );
CallAfter( [this] { SetRepoSSHPath( m_prevFile ); } );
return;
}
if( isEncrypted )
{
m_txtPassword->Enable();
m_txtPassword->SetToolTip( _( "Enter the password for the SSH key" ) );
}
else
{
m_txtPassword->SetValue( wxEmptyString );
m_txtPassword->SetToolTip( wxEmptyString );
m_txtPassword->Disable();
}
ifs.close();
std::ifstream pubIfs( file + ".pub" );
if( !pubIfs.good() || !pubIfs.is_open() )
{
DisplayErrorMessage( this, wxString::Format( _( "Could not open public key '%s'" ), file + ".pub" ),
wxString::Format( "%s: %d", std::strerror( errno ), errno ) );
aEvent.SetPath( wxEmptyString );
CallAfter( [this] { SetRepoSSHPath( m_prevFile ); } );
return;
}
m_prevFile = file;
pubIfs.close();
}
void DIALOG_GIT_REPOSITORY::OnOKClick( wxCommandEvent& event )
{
// Save the repository details
if( m_txtName->GetValue().IsEmpty() )
{
DisplayErrorMessage( this, _( "Missing information" ), _( "Please enter a name for the repository" ) );
return;
}
if( m_txtURL->GetValue().IsEmpty() )
{
DisplayErrorMessage( this, _( "Missing information" ), _( "Please enter a URL for the repository" ) );
return;
}
EndModal( wxID_OK );
}
void DIALOG_GIT_REPOSITORY::updateAuthControls()
{
if( m_ConnType->GetSelection() == static_cast<int>( KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_LOCAL ) )
{
m_panelAuth->Show( false );
}
else
{
m_panelAuth->Show( true );
if( m_ConnType->GetSelection() == static_cast<int>( KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_SSH ) )
{
m_fpSSHKey->Show( true );
m_labelSSH->Show( true );
m_labelPass1->SetLabel( _( "SSH Key Password" ) );
}
else
{
m_fpSSHKey->Show( false );
m_labelSSH->Show( false );
m_labelPass1->SetLabel( _( "Password" ) );
setDefaultSSHKey();
}
}
}
void DIALOG_GIT_REPOSITORY::OnSelectConnType( wxCommandEvent& event )
{
updateAuthControls();
}

View File

@ -0,0 +1,124 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef DIALOG_GIT_REPOSITORY_H_
#define DIALOG_GIT_REPOSITORY_H_
#include "dialog_git_repository_base.h"
#include <git/kicad_git_common.h>
#include <git2.h>
class DIALOG_GIT_REPOSITORY : public DIALOG_GIT_REPOSITORY_BASE
{
public:
DIALOG_GIT_REPOSITORY( wxWindow* aParent, git_repository* aRepository, wxString aURL = wxEmptyString );
~DIALOG_GIT_REPOSITORY() override;
void SetTestResult( bool aFailed, const wxString& aError )
{
m_failedTest = aFailed;
m_testError = aError;
}
void SetRepoType( KIGIT_COMMON::GIT_CONN_TYPE aType )
{
m_ConnType->SetSelection( static_cast<int>( aType ) );
updateAuthControls();
}
KIGIT_COMMON::GIT_CONN_TYPE GetRepoType() const { return static_cast<KIGIT_COMMON::GIT_CONN_TYPE>( m_ConnType->GetSelection() ); }
void SetRepoName( const wxString& aName ) { m_txtName->SetValue( aName ); }
wxString GetRepoName() const { return m_txtName->GetValue(); }
void SetRepoURL( const wxString& aURL ) { m_txtURL->SetValue( aURL ); }
wxString GetRepoURL() const { return m_txtURL->GetValue(); }
/**
* @brief Get the Bare Repo U R L object
*
* @return wxString without the protocol
*/
wxString GetBareRepoURL() const
{
wxString url = m_txtURL->GetValue();
if( url.StartsWith( "https://" ) )
url = url.Mid( 8 );
else if( url.StartsWith( "http://" ) )
url = url.Mid( 7 );
else if( url.StartsWith( "ssh://" ) )
url = url.Mid( 6 );
return url;
}
void SetUsername( const wxString& aUsername ) { m_txtUsername->SetValue( aUsername ); }
wxString GetUsername() const { return m_txtUsername->GetValue(); }
void SetPassword( const wxString& aPassword ) { m_txtPassword->SetValue( aPassword ); }
wxString GetPassword() const { return m_txtPassword->GetValue(); }
void SetRepoSSHPath( const wxString& aPath ) { m_fpSSHKey->SetFileName( aPath ); m_prevFile = aPath; }
wxString GetRepoSSHPath() const { return m_fpSSHKey->GetFileName().GetFullPath(); }
unsigned& GetTested() { return m_tested; }
void SetEncrypted( bool aEncrypted = true );
private:
void OnUpdateUI( wxUpdateUIEvent& event ) override;
void OnLocationExit( wxFocusEvent& event ) override;
void OnOKClick( wxCommandEvent& event ) override;
void OnSelectConnType( wxCommandEvent& event ) override;
void OnTestClick( wxCommandEvent& event ) override;
void OnFileUpdated( wxFileDirPickerEvent& event ) override;
void setDefaultSSHKey();
void updateAuthControls();
void updateURLData();
bool extractClipboardData();
std::tuple<bool,wxString,wxString,wxString> isValidHTTPS( const wxString& url );
std::tuple<bool,wxString, wxString> isValidSSH( const wxString& url );
git_repository* m_repository;
wxString m_prevFile;
unsigned m_tested;
bool m_failedTest;
wxString m_testError;
bool m_tempRepo;
wxString m_tempPath;
KIGIT_COMMON::GIT_CONN_TYPE m_repoType;
};
#endif /* DIALOG_GIT_REPOSITORY_H_ */

View File

@ -0,0 +1,172 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-254-gc2ef7767)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "dialog_git_repository_base.h"
///////////////////////////////////////////////////////////////////////////
DIALOG_GIT_REPOSITORY_BASE::DIALOG_GIT_REPOSITORY_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( -1,-1 ), wxDefaultSize );
wxBoxSizer* bSizerMain;
bSizerMain = new wxBoxSizer( wxVERTICAL );
m_staticText1 = new wxStaticText( this, wxID_ANY, _("Connection"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText1->Wrap( -1 );
bSizerMain->Add( m_staticText1, 0, wxLEFT|wxTOP, 10 );
m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizerMain->Add( m_staticline1, 0, wxEXPAND|wxTOP, 5 );
wxFlexGridSizer* fgSizer2;
fgSizer2 = new wxFlexGridSizer( 0, 2, 0, 0 );
fgSizer2->AddGrowableCol( 1 );
fgSizer2->SetFlexibleDirection( wxBOTH );
fgSizer2->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticText3 = new wxStaticText( this, wxID_ANY, _("Name"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText3->Wrap( -1 );
fgSizer2->Add( m_staticText3, 0, wxALL, 5 );
m_txtName = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizer2->Add( m_txtName, 0, wxALL|wxEXPAND, 5 );
m_staticText4 = new wxStaticText( this, wxID_ANY, _("Location"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText4->Wrap( -1 );
fgSizer2->Add( m_staticText4, 0, wxALL, 5 );
m_txtURL = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizer2->Add( m_txtURL, 0, wxALL|wxEXPAND, 5 );
m_staticText9 = new wxStaticText( this, wxID_ANY, _("Connection Type"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText9->Wrap( -1 );
fgSizer2->Add( m_staticText9, 0, wxALL, 5 );
wxBoxSizer* bSizer3;
bSizer3 = new wxBoxSizer( wxHORIZONTAL );
wxString m_ConnTypeChoices[] = { _("HTTPS"), _("SSH"), _("Local") };
int m_ConnTypeNChoices = sizeof( m_ConnTypeChoices ) / sizeof( wxString );
m_ConnType = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ConnTypeNChoices, m_ConnTypeChoices, 0 );
m_ConnType->SetSelection( 0 );
bSizer3->Add( m_ConnType, 1, wxEXPAND|wxLEFT|wxRIGHT, 5 );
bSizer3->Add( 0, 0, 1, wxEXPAND, 5 );
fgSizer2->Add( bSizer3, 1, wxEXPAND, 5 );
bSizerMain->Add( fgSizer2, 1, wxEXPAND, 5 );
m_panelAuth = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* m_szAuth;
m_szAuth = new wxBoxSizer( wxVERTICAL );
m_szAuth->Add( 0, 0, 1, wxEXPAND, 5 );
wxBoxSizer* bSizer11;
bSizer11 = new wxBoxSizer( wxHORIZONTAL );
m_staticText2 = new wxStaticText( m_panelAuth, wxID_ANY, _("Authentication"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText2->Wrap( -1 );
bSizer11->Add( m_staticText2, 0, wxLEFT|wxTOP, 10 );
bSizer11->Add( 0, 0, 1, wxEXPAND, 5 );
m_szAuth->Add( bSizer11, 0, wxEXPAND, 5 );
m_staticline2 = new wxStaticLine( m_panelAuth, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
m_szAuth->Add( m_staticline2, 0, wxEXPAND | wxALL, 5 );
wxFlexGridSizer* fgSshSizer;
fgSshSizer = new wxFlexGridSizer( 0, 2, 0, 0 );
fgSshSizer->AddGrowableCol( 1 );
fgSshSizer->SetFlexibleDirection( wxBOTH );
fgSshSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_labelSSH = new wxStaticText( m_panelAuth, wxID_ANY, _("SSH Private Key"), wxDefaultPosition, wxDefaultSize, 0 );
m_labelSSH->Wrap( -1 );
fgSshSizer->Add( m_labelSSH, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxRESERVE_SPACE_EVEN_IF_HIDDEN, 5 );
wxBoxSizer* bSizer41;
bSizer41 = new wxBoxSizer( wxHORIZONTAL );
m_fpSSHKey = new wxFilePickerCtrl( m_panelAuth, wxID_ANY, wxEmptyString, _("Select SSH private key file"), _("*"), wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE|wxFLP_FILE_MUST_EXIST|wxFLP_OPEN );
bSizer41->Add( m_fpSSHKey, 1, wxEXPAND|wxLEFT|wxRESERVE_SPACE_EVEN_IF_HIDDEN|wxRIGHT, 5 );
m_btnTest = new wxButton( m_panelAuth, wxID_ANY, _("Test"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer41->Add( m_btnTest, 0, wxLEFT|wxRIGHT, 5 );
fgSshSizer->Add( bSizer41, 1, wxEXPAND, 5 );
m_staticText11 = new wxStaticText( m_panelAuth, wxID_ANY, _("Username"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText11->Wrap( -1 );
fgSshSizer->Add( m_staticText11, 0, wxALL, 5 );
m_txtUsername = new wxTextCtrl( m_panelAuth, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSshSizer->Add( m_txtUsername, 0, wxALL|wxEXPAND, 5 );
m_labelPass1 = new wxStaticText( m_panelAuth, wxID_ANY, _("SSH Key Password"), wxDefaultPosition, wxDefaultSize, 0 );
m_labelPass1->Wrap( -1 );
fgSshSizer->Add( m_labelPass1, 0, wxALL, 5 );
m_txtPassword = new wxTextCtrl( m_panelAuth, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSshSizer->Add( m_txtPassword, 0, wxALL|wxEXPAND, 5 );
m_szAuth->Add( fgSshSizer, 1, wxEXPAND, 5 );
m_panelAuth->SetSizer( m_szAuth );
m_panelAuth->Layout();
m_szAuth->Fit( m_panelAuth );
bSizerMain->Add( m_panelAuth, 1, wxALL|wxEXPAND|wxRESERVE_SPACE_EVEN_IF_HIDDEN, 0 );
m_sdbSizer = new wxStdDialogButtonSizer();
m_sdbSizerOK = new wxButton( this, wxID_OK );
m_sdbSizer->AddButton( m_sdbSizerOK );
m_sdbSizerCancel = new wxButton( this, wxID_CANCEL );
m_sdbSizer->AddButton( m_sdbSizerCancel );
m_sdbSizer->Realize();
bSizerMain->Add( m_sdbSizer, 0, wxALL|wxEXPAND, 5 );
this->SetSizer( bSizerMain );
this->Layout();
this->Centre( wxBOTH );
// Connect Events
this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnClose ) );
this->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnUpdateUI ) );
m_txtURL->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnLocationExit ), NULL, this );
m_ConnType->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnSelectConnType ), NULL, this );
m_fpSSHKey->Connect( wxEVT_COMMAND_FILEPICKER_CHANGED, wxFileDirPickerEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnFileUpdated ), NULL, this );
m_btnTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnTestClick ), NULL, this );
m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnOKClick ), NULL, this );
}
DIALOG_GIT_REPOSITORY_BASE::~DIALOG_GIT_REPOSITORY_BASE()
{
// Disconnect Events
this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnClose ) );
this->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnUpdateUI ) );
m_txtURL->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnLocationExit ), NULL, this );
m_ConnType->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnSelectConnType ), NULL, this );
m_fpSSHKey->Disconnect( wxEVT_COMMAND_FILEPICKER_CHANGED, wxFileDirPickerEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnFileUpdated ), NULL, this );
m_btnTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnTestClick ), NULL, this );
m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GIT_REPOSITORY_BASE::OnOKClick ), NULL, this );
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,82 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-254-gc2ef7767)
// 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 "dialog_shim.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/statline.h>
#include <wx/textctrl.h>
#include <wx/choice.h>
#include <wx/sizer.h>
#include <wx/filepicker.h>
#include <wx/button.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/panel.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_GIT_REPOSITORY_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_GIT_REPOSITORY_BASE : public DIALOG_SHIM
{
private:
protected:
wxStaticText* m_staticText1;
wxStaticLine* m_staticline1;
wxStaticText* m_staticText3;
wxTextCtrl* m_txtName;
wxStaticText* m_staticText4;
wxTextCtrl* m_txtURL;
wxStaticText* m_staticText9;
wxChoice* m_ConnType;
wxPanel* m_panelAuth;
wxStaticText* m_staticText2;
wxStaticLine* m_staticline2;
wxStaticText* m_labelSSH;
wxFilePickerCtrl* m_fpSSHKey;
wxButton* m_btnTest;
wxStaticText* m_staticText11;
wxTextCtrl* m_txtUsername;
wxStaticText* m_labelPass1;
wxTextCtrl* m_txtPassword;
wxStdDialogButtonSizer* m_sdbSizer;
wxButton* m_sdbSizerOK;
wxButton* m_sdbSizerCancel;
// Virtual event handlers, override them in your derived class
virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
virtual void OnUpdateUI( wxUpdateUIEvent& event ) { event.Skip(); }
virtual void OnLocationExit( wxFocusEvent& event ) { event.Skip(); }
virtual void OnSelectConnType( wxCommandEvent& event ) { event.Skip(); }
virtual void OnFileUpdated( wxFileDirPickerEvent& event ) { event.Skip(); }
virtual void OnTestClick( wxCommandEvent& event ) { event.Skip(); }
virtual void OnOKClick( wxCommandEvent& event ) { event.Skip(); }
public:
DIALOG_GIT_REPOSITORY_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Git Repository"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 682,598 ), long style = wxCAPTION|wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~DIALOG_GIT_REPOSITORY_BASE();
};

View File

@ -0,0 +1,288 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "dialog_git_switch.h"
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/listctrl.h>
#include <wx/event.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <git2.h>
DIALOG_GIT_SWITCH::DIALOG_GIT_SWITCH( wxWindow* aParent, git_repository* aRepository ) :
DIALOG_SHIM( aParent, wxID_ANY, _( "Git Branch Switch" ), wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
m_timer( this ), m_repository( aRepository )
{
wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
// Add explanation text
wxStaticText* explanationText =
new wxStaticText( this, wxID_ANY, _( "Select or enter a branch name:" ) );
sizer->Add( explanationText, 0, wxALL, 10 );
// Add branch list with three columns
m_branchList = new wxListView( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxLC_REPORT | wxLC_SINGLE_SEL );
m_branchList->InsertColumn( 0, _( "Branch" ) );
m_branchList->InsertColumn( 1, _( "Last Commit" ) );
m_branchList->InsertColumn( 2, _( "Last Updated" ) );
sizer->Add( m_branchList, 1, wxALL | wxEXPAND, 10 );
// Add branch name text box
m_branchNameText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition,
wxDefaultSize, wxTE_PROCESS_ENTER );
sizer->Add( m_branchNameText, 0, wxALL | wxEXPAND, 10 );
// Add buttons
wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer();
m_switchButton = new wxButton( this, wxID_OK, _( "Switch" ) );
buttonSizer->AddButton( m_switchButton );
m_switchButton->Disable();
wxButton* cancelButton = new wxButton( this, wxID_CANCEL, _( "Cancel" ) );
buttonSizer->AddButton( cancelButton );
buttonSizer->Realize();
sizer->Add( buttonSizer, 0, wxALIGN_RIGHT | wxALL, 10 );
// Bind events
Bind( wxEVT_LIST_ITEM_SELECTED, &DIALOG_GIT_SWITCH::OnBranchListSelection, this, m_branchList->GetId() );
Bind( wxEVT_LIST_ITEM_ACTIVATED, &DIALOG_GIT_SWITCH::OnBranchListDClick, this, m_branchList->GetId() );
Bind( wxEVT_BUTTON, &DIALOG_GIT_SWITCH::OnSwitchButton, this, m_switchButton->GetId() );
Bind( wxEVT_BUTTON, &DIALOG_GIT_SWITCH::OnCancelButton, this, cancelButton->GetId() );
Bind( wxEVT_TEXT, &DIALOG_GIT_SWITCH::OnTextChanged, this, m_branchNameText->GetId() );
Bind( wxEVT_TIMER, &DIALOG_GIT_SWITCH::OnTimer, this, m_timer.GetId() );
// Populate branch list
PopulateBranchList();
// Set sizer for the dialog
SetSizerAndFit( sizer );
finishDialogSettings();
m_existingBranch = false;
}
DIALOG_GIT_SWITCH::~DIALOG_GIT_SWITCH()
{
StopTimer();
Unbind( wxEVT_TIMER, &DIALOG_GIT_SWITCH::OnTimer, this, m_timer.GetId() );
}
void DIALOG_GIT_SWITCH::PopulateBranchList()
{
m_branchList->DeleteAllItems();
// Get the branches
GetBranches();
// Populate the list
for( auto& [ name, data ] : m_branches )
{
wxDateTime lastUpdated( data.lastUpdated );
wxString lastUpdatedString = lastUpdated.Format();
long itemIndex = m_branchList->InsertItem( m_branchList->GetItemCount(), name );
m_branchList->SetItem( itemIndex, 1, data.commitString );
m_branchList->SetItem( itemIndex, 2, lastUpdatedString );
}
m_branchList->SetColumnWidth( 0, wxLIST_AUTOSIZE );
m_branchList->SetColumnWidth( 1, wxLIST_AUTOSIZE );
m_branchList->SetColumnWidth( 2, wxLIST_AUTOSIZE );
}
void DIALOG_GIT_SWITCH::OnBranchListDClick( wxListEvent& aEvent )
{
int selection = aEvent.GetIndex();
if( selection != wxNOT_FOUND )
{
wxString branchName = m_branchList->GetItemText( selection );
m_branchNameText->SetValue( branchName );
if( branchName != m_currentBranch )
EndModal( wxID_OK );
}
}
void DIALOG_GIT_SWITCH::OnBranchListSelection( wxListEvent& aEvent )
{
int selection = aEvent.GetIndex();
if( selection != wxNOT_FOUND )
{
wxString branchName = m_branchList->GetItemText( selection );
m_branchNameText->SetValue( branchName );
m_switchButton->SetLabel( _( "Switch" ) );
m_switchButton->Enable( branchName != m_currentBranch );
}
else
{
// Deselect all elements in the list
for( int ii = 0; ii < m_branchList->GetItemCount(); ++ii )
m_branchList->SetItemState( ii, 0, 0 );
}
}
void DIALOG_GIT_SWITCH::OnSwitchButton(wxCommandEvent& aEvent)
{
wxString branchName = m_branchNameText->GetValue();
// Check if the branch name exists
bool branchExists = m_branches.count(branchName);
if (branchExists)
{
EndModal(wxID_OK); // Return Switch code
}
else
{
EndModal(wxID_ADD); // Return Add code
}
}
void DIALOG_GIT_SWITCH::OnCancelButton(wxCommandEvent& aEvent)
{
EndModal(wxID_CANCEL); // Return Cancel code
}
wxString DIALOG_GIT_SWITCH::GetBranchName() const
{
return m_branchNameText->GetValue();
}
void DIALOG_GIT_SWITCH::StartTimer()
{
m_timer.Start( 500, true );
}
void DIALOG_GIT_SWITCH::StopTimer()
{
m_timer.Stop();
}
void DIALOG_GIT_SWITCH::OnTimer( wxTimerEvent& aEvt )
{
wxString branchName = m_branchNameText->GetValue();
if( branchName == m_lastEnteredText )
return;
m_lastEnteredText = branchName;
// Check if the branch name exists
bool branchExists = m_branches.count( branchName );
if( branchExists )
{
m_switchButton->SetLabel( _( "Switch" ) );
m_switchButton->Enable( branchName != m_currentBranch );
}
else
{
m_switchButton->SetLabel( _( "Add" ) );
m_switchButton->Enable();
}
}
void DIALOG_GIT_SWITCH::OnTextChanged( wxCommandEvent& aEvt )
{
StartTimer();
}
void DIALOG_GIT_SWITCH::GetBranches()
{
// Clear the branch list
m_branches.clear();
git_branch_iterator* branchIterator = nullptr;
git_branch_t branchType;
// Get Current Branch
git_reference* currentBranchReference = nullptr;
git_repository_head( &currentBranchReference, m_repository );
// Get the current branch name
if( currentBranchReference )
{
m_currentBranch = git_reference_shorthand( currentBranchReference );
git_reference_free( currentBranchReference );
}
// Initialize branch iterator
git_branch_iterator_new( &branchIterator, m_repository, GIT_BRANCH_ALL );
// Iterate over local branches
git_reference* branchReference = nullptr;
while( git_branch_next( &branchReference, &branchType, branchIterator ) == 0 )
{
// Get the branch OID
const git_oid* branchOid = git_reference_target( branchReference );
// Skip this branch if it doesn't have an OID
if( !branchOid )
{
git_reference_free( branchReference );
continue;
}
git_commit* commit = nullptr;
if( git_commit_lookup( &commit, m_repository, branchOid ) )
{
// Skip this branch if it doesn't have a commit
git_reference_free( branchReference );
continue;
}
// Retrieve commit details
BranchData branchData;
branchData.commitString = git_commit_message( commit );
branchData.lastUpdated = static_cast<time_t>( git_commit_time( commit ) );
branchData.isRemote = branchType == GIT_BRANCH_REMOTE;
m_branches[git_reference_shorthand( branchReference )] = branchData;
git_commit_free( commit );
git_reference_free( branchReference );
}
git_branch_iterator_free( branchIterator );
}

View File

@ -0,0 +1,79 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021-2022 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, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef DIALOG_GIT_SWITCH_H
#define DIALOG_GIT_SWITCH_H
#include <dialog_shim.h>
#include <git2.h>
class wxButton;
class wxListView;
class wxTextCtrl;
class wxTimer;
class wxListEvent;
struct BranchData
{
wxString commitString;
time_t lastUpdated;
bool isRemote;
};
class DIALOG_GIT_SWITCH : public DIALOG_SHIM
{
public:
DIALOG_GIT_SWITCH(wxWindow* aParent, git_repository* aRepository);
virtual ~DIALOG_GIT_SWITCH();
wxString GetBranchName() const;
private:
void PopulateBranchList();
void OnBranchListSelection(wxListEvent& event);
void OnBranchListDClick(wxListEvent& event);
void OnSwitchButton(wxCommandEvent& event);
void OnCancelButton(wxCommandEvent& event);
void OnTextChanged(wxCommandEvent& event);
void OnTimer(wxTimerEvent& event);
void GetBranches();
wxListView* m_branchList;
wxTextCtrl* m_branchNameText;
wxButton* m_switchButton;
wxTimer m_timer;
wxString m_currentBranch;
git_repository* m_repository;
wxString m_lastEnteredText;
bool m_existingBranch;
std::map<wxString, BranchData> m_branches;
void StartTimer();
void StopTimer();
};
#endif // DIALOG_GIT_SWITCH_H

View File

@ -0,0 +1,377 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018-2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "panel_git_repos.h"
#include <bitmaps.h>
#include <dialogs/git/dialog_git_repository.h>
#include <kiplatform/secrets.h>
#include <pgm_base.h>
#include <settings/common_settings.h>
#include <widgets/std_bitmap_button.h>
#include <widgets/wx_grid.h>
#include <git2.h>
#include <wx/bmpbuttn.h>
#include <wx/button.h>
#include <wx/checkbox.h>
PANEL_GIT_REPOS::PANEL_GIT_REPOS( wxWindow* aParent ) : PANEL_GIT_REPOS_BASE( aParent)
{
m_btnAddRepo->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
m_btnEditRepo->SetBitmap( KiBitmap( BITMAPS::small_edit ) );
m_btnDelete->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
}
PANEL_GIT_REPOS::~PANEL_GIT_REPOS()
{
}
void PANEL_GIT_REPOS::ResetPanel()
{
m_grid->ClearGrid();
m_cbDefault->SetValue( true );
m_author->SetValue( wxEmptyString );
m_authorEmail->SetValue( wxEmptyString );
}
static std::pair<wxString, wxString> getDefaultAuthorEmail()
{
wxString name;
wxString email;
git_config_entry* name_c = nullptr;
git_config_entry* email_c = nullptr;
git_config* config = nullptr;
if( git_config_open_default( &config ) != 0 )
{
printf( "Failed to open default Git config: %s\n", giterr_last()->message );
return std::make_pair( name, email );
}
if( git_config_get_entry( &name_c, config, "user.name" ) != 0 )
{
printf( "Failed to get user.name from Git config: %s\n", giterr_last()->message );
}
if( git_config_get_entry( &email_c, config, "user.email" ) != 0 )
{
printf( "Failed to get user.email from Git config: %s\n", giterr_last()->message );
}
if( name_c )
name = name_c->value;
if( email_c )
email = email_c->value;
git_config_entry_free( name_c );
git_config_entry_free( email_c );
git_config_free( config );
return std::make_pair( name, email );
}
bool PANEL_GIT_REPOS::TransferDataFromWindow()
{
COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
auto& repos = settings->m_Git.repositories;
repos.clear();
for( int row = 0; row < m_grid->GetNumberRows(); row++ )
{
COMMON_SETTINGS::GIT_REPOSITORY repo;
repo.active = m_grid->GetCellValue( row, COL_ACTIVE ) == "1";
repo.name = m_grid->GetCellValue( row, COL_NAME );
repo.path = m_grid->GetCellValue( row, COL_PATH );
repo.authType = m_grid->GetCellValue( row, COL_AUTH_TYPE );
repo.username = m_grid->GetCellValue( row, COL_USERNAME );
KIPLATFORM::SECRETS::StoreSecret( repo.path, repo.username, m_grid->GetCellValue( row, COL_PASSWORD ) );
repo.ssh_path = m_grid->GetCellValue( row, COL_SSH_PATH );
repos.push_back( repo );
}
settings->m_Git.useDefaultAuthor = m_cbDefault->GetValue();
settings->m_Git.authorName = m_author->GetValue();
settings->m_Git.authorEmail = m_authorEmail->GetValue();
return true;
}
static bool testRepositoryConnection( COMMON_SETTINGS::GIT_REPOSITORY& repository)
{
git_libgit2_init();
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
typedef struct
{
COMMON_SETTINGS::GIT_REPOSITORY* repo;
bool success;
} callbacksPayload;
callbacksPayload cb_data( { &repository, true } ); // If we don't need authentication, then, we are successful
callbacks.payload = &cb_data;
callbacks.credentials = [](git_cred** out, const char* url, const char* username, unsigned int allowed_types, void* payload) -> int {
// If we are asking for credentials, then, we need authentication
callbacksPayload* data = static_cast<callbacksPayload*>(payload);
data->success = false;
if( allowed_types & GIT_CREDTYPE_USERNAME )
{
data->success = true;
}
else if( data->repo->authType == "ssh" && ( allowed_types & GIT_CREDTYPE_SSH_KEY ) )
{
wxString sshKeyPath = data->repo->ssh_path;
// Check if the SSH key exists and is readable
if( wxFileExists( sshKeyPath ) && wxFile::Access( sshKeyPath, wxFile::read ) )
data->success = true;
}
else if( data->repo->authType == "password" )
{
data->success = ( allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT );
}
return 0;
};
// Create a temporary directory to initialize the Git repository
wxString tempDirPath = wxFileName::CreateTempFileName(wxT("kigit_temp"));
wxMkDir(tempDirPath, wxS_DIR_DEFAULT );
// Initialize the Git repository
git_repository* repo = nullptr;
int result = git_repository_init(&repo, tempDirPath.mb_str(wxConvUTF8), 0);
if (result != 0) {
git_repository_free(repo);
git_libgit2_shutdown();
wxRmdir(tempDirPath);
return false;
}
git_remote* remote = nullptr;
result = git_remote_create_anonymous(&remote, repo, tempDirPath.mb_str(wxConvUTF8));
if (result != 0) {
git_remote_free(remote);
git_repository_free(repo);
git_libgit2_shutdown();
wxRmdir(tempDirPath);
return false;
}
// We don't really care about the result of this call, the authentication callback
// will set the return values we need
git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, nullptr, nullptr);
git_remote_disconnect(remote);
git_remote_free(remote);
git_repository_free(repo);
git_libgit2_shutdown();
// Clean up the temporary directory
wxRmdir(tempDirPath);
return cb_data.success;
}
bool PANEL_GIT_REPOS::TransferDataToWindow()
{
COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
m_grid->ClearGrid();
for( COMMON_SETTINGS::GIT_REPOSITORY& repo : settings->m_Git.repositories )
{
if( repo.name.IsEmpty() || repo.path.IsEmpty() )
continue;
int row = m_grid->GetNumberRows();
m_grid->AppendRows( 1 );
m_grid->SetCellRenderer( row, COL_ACTIVE, new wxGridCellBoolRenderer() );
m_grid->SetCellEditor( row, COL_ACTIVE, new wxGridCellBoolEditor() );
m_grid->SetCellValue( row, COL_ACTIVE, repo.active ? "1" : "0" );
m_grid->SetCellValue( row, COL_NAME, repo.name );
m_grid->SetCellValue( row, COL_PATH, repo.path );
m_grid->SetCellValue( row, COL_AUTH_TYPE, repo.authType );
m_grid->SetCellValue( row, COL_USERNAME, repo.username );
wxString password;
KIPLATFORM::SECRETS::GetSecret( repo.path, repo.username, password );
m_grid->SetCellValue( row, COL_PASSWORD, password );
m_grid->SetCellValue( row, COL_SSH_PATH, repo.ssh_path );
if( repo.active )
m_grid->SetCellValue( row, 3, testRepositoryConnection( repo ) ? "C" : "NC" );
}
m_cbDefault->SetValue( settings->m_Git.useDefaultAuthor );
if( settings->m_Git.useDefaultAuthor )
{
auto defaultAuthor = getDefaultAuthorEmail();
m_author->SetValue( defaultAuthor.first );
m_authorEmail->SetValue( defaultAuthor.second );
m_author->Disable();
m_authorEmail->Disable();
}
else
{
m_author->SetValue( settings->m_Git.authorName );
m_authorEmail->SetValue( settings->m_Git.authorEmail );
}
return true;
}
void PANEL_GIT_REPOS::onDefaultClick( wxCommandEvent& event )
{
m_author->Enable( !m_cbDefault->GetValue() );
m_authorEmail->Enable( !m_cbDefault->GetValue() );
m_authorLabel->Enable( !m_cbDefault->GetValue() );
m_authorEmailLabel->Enable( !m_cbDefault->GetValue() );
}
void PANEL_GIT_REPOS::onGridDClick( wxGridEvent& event )
{
if( m_grid->GetNumberRows() <= 0 )
{
wxCommandEvent evt;
onAddClick( evt );
return;
}
int row = event.GetRow();
if( row < 0 || row >= m_grid->GetNumberRows() )
return;
DIALOG_GIT_REPOSITORY dialog( this, nullptr );
dialog.SetRepoName( m_grid->GetCellValue( row, COL_NAME ) );
dialog.SetRepoURL( m_grid->GetCellValue( row, COL_PATH ) );
dialog.SetUsername( m_grid->GetCellValue( row, COL_USERNAME ) );
dialog.SetRepoSSHPath( m_grid->GetCellValue( row, COL_SSH_PATH ) );
dialog.SetPassword( m_grid->GetCellValue( row, COL_PASSWORD ) );
wxString type = m_grid->GetCellValue( row, COL_AUTH_TYPE );
if( type == "password" )
dialog.SetRepoType( KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_HTTPS );
else if( type == "ssh" )
dialog.SetRepoType( KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_SSH );
else
dialog.SetRepoType( KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_LOCAL);
if( dialog.ShowModal() == wxID_OK )
{
m_grid->SetCellValue( row, COL_NAME, dialog.GetRepoName() );
m_grid->SetCellValue( row, COL_PATH, dialog.GetRepoURL() );
m_grid->SetCellValue( row, COL_USERNAME, dialog.GetUsername() );
m_grid->SetCellValue( row, COL_SSH_PATH, dialog.GetRepoSSHPath() );
m_grid->SetCellValue( row, COL_PASSWORD, dialog.GetPassword() );
if( dialog.GetRepoType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_HTTPS )
{
m_grid->SetCellValue( row, COL_AUTH_TYPE, "password" );
}
else if( dialog.GetRepoType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_SSH )
{
m_grid->SetCellValue( row, COL_AUTH_TYPE, "ssh" );
}
else
{
m_grid->SetCellValue( row, COL_AUTH_TYPE, "none" );
}
}
}
void PANEL_GIT_REPOS::onAddClick( wxCommandEvent& event )
{
DIALOG_GIT_REPOSITORY dialog( m_parent, nullptr );
if( dialog.ShowModal() == wxID_OK )
{
int row = m_grid->GetNumberRows();
m_grid->AppendRows( 1 );
m_grid->SetCellValue( row, COL_NAME, dialog.GetRepoName() );
m_grid->SetCellValue( row, COL_PATH, dialog.GetRepoURL() );
m_grid->SetCellValue( row, COL_USERNAME, dialog.GetUsername() );
m_grid->SetCellValue( row, COL_SSH_PATH, dialog.GetRepoSSHPath() );
m_grid->SetCellValue( row, COL_PASSWORD, dialog.GetPassword() );
if( dialog.GetRepoType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_HTTPS )
{
m_grid->SetCellValue( row, COL_AUTH_TYPE, "password" );
}
else if( dialog.GetRepoType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_SSH )
{
m_grid->SetCellValue( row, COL_AUTH_TYPE, "ssh" );
}
else
{
m_grid->SetCellValue( row, COL_AUTH_TYPE, "none" );
}
m_grid->MakeCellVisible( row, 0 );
}
}
void PANEL_GIT_REPOS::onEditClick( wxCommandEvent& event )
{
wxGridEvent evt( m_grid->GetId(), wxEVT_GRID_CELL_LEFT_DCLICK, m_grid,
m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
onGridDClick( evt );
}
void PANEL_GIT_REPOS::onDeleteClick( wxCommandEvent& event )
{
if( !m_grid->CommitPendingChanges() || m_grid->GetNumberRows() <= 0 )
return;
int curRow = m_grid->GetGridCursorRow();
m_grid->DeleteRows( curRow );
curRow = std::max( 0, curRow - 1 );
m_grid->MakeCellVisible( curRow, m_grid->GetGridCursorCol() );
m_grid->SetGridCursor( curRow, m_grid->GetGridCursorCol() );
}

View File

@ -0,0 +1,59 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018-2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PANEL_GIT_REPOS_H
#define PANEL_GIT_REPOS_H
#include <git/panel_git_repos_base.h>
#include <widgets/wx_grid.h>
class PANEL_GIT_REPOS : public PANEL_GIT_REPOS_BASE
{
public:
PANEL_GIT_REPOS( wxWindow* parent );
~PANEL_GIT_REPOS() override;
void ResetPanel() override;
bool TransferDataFromWindow() override;
bool TransferDataToWindow() override;
enum COLS
{
COL_ACTIVE = 0,
COL_NAME,
COL_PATH,
COL_STATUS,
COL_AUTH_TYPE,
COL_USERNAME,
COL_PASSWORD,
COL_SSH_KEY,
COL_SSH_PATH
};
private:
void onDefaultClick( wxCommandEvent& event ) override;
void onGridDClick( wxGridEvent& event ) override;
void onAddClick( wxCommandEvent& event ) override;
void onEditClick( wxCommandEvent& event ) override;
void onDeleteClick( wxCommandEvent& event ) override;
};
#endif // PANEL_GIT_REPOS_H

View File

@ -0,0 +1,167 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-254-gc2ef7767)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "widgets/std_bitmap_button.h"
#include "widgets/wx_grid.h"
#include "panel_git_repos_base.h"
///////////////////////////////////////////////////////////////////////////
PANEL_GIT_REPOS_BASE::PANEL_GIT_REPOS_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : RESETTABLE_PANEL( parent, id, pos, size, style, name )
{
wxBoxSizer* bPanelSizer;
bPanelSizer = new wxBoxSizer( wxHORIZONTAL );
wxBoxSizer* bLeftSizer;
bLeftSizer = new wxBoxSizer( wxVERTICAL );
m_staticText12 = new wxStaticText( this, wxID_ANY, _("Git Commit Data"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText12->Wrap( -1 );
bLeftSizer->Add( m_staticText12, 0, wxEXPAND|wxLEFT|wxTOP, 10 );
wxFlexGridSizer* fgSizer1;
fgSizer1 = new wxFlexGridSizer( 0, 2, 0, 0 );
fgSizer1->AddGrowableCol( 1 );
fgSizer1->SetFlexibleDirection( wxBOTH );
fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_cbDefault = new wxCheckBox( this, wxID_ANY, _("Use default values"), wxDefaultPosition, wxDefaultSize, 0 );
m_cbDefault->SetValue(true);
fgSizer1->Add( m_cbDefault, 0, wxALL, 5 );
fgSizer1->Add( 0, 0, 0, wxEXPAND, 5 );
m_authorLabel = new wxStaticText( this, wxID_ANY, _("Author name:"), wxDefaultPosition, wxDefaultSize, 0 );
m_authorLabel->Wrap( -1 );
m_authorLabel->Enable( false );
fgSizer1->Add( m_authorLabel, 0, wxALL, 5 );
m_author = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_author->Enable( false );
fgSizer1->Add( m_author, 0, wxALL|wxEXPAND, 5 );
m_authorEmailLabel = new wxStaticText( this, wxID_ANY, _("Author e-mail:"), wxDefaultPosition, wxDefaultSize, 0 );
m_authorEmailLabel->Wrap( -1 );
m_authorEmailLabel->Enable( false );
fgSizer1->Add( m_authorEmailLabel, 0, wxALL, 5 );
m_authorEmail = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_authorEmail->Enable( false );
fgSizer1->Add( m_authorEmail, 0, wxALL|wxEXPAND, 5 );
bLeftSizer->Add( fgSizer1, 1, wxBOTTOM|wxEXPAND|wxLEFT|wxTOP, 13 );
m_staticline3 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bLeftSizer->Add( m_staticline3, 0, wxEXPAND|wxBOTTOM, 5 );
m_staticText20 = new wxStaticText( this, wxID_ANY, _("Git Repositories"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText20->Wrap( -1 );
bLeftSizer->Add( m_staticText20, 0, wxEXPAND|wxLEFT|wxRIGHT, 13 );
wxBoxSizer* bAntialiasingSizer;
bAntialiasingSizer = new wxBoxSizer( wxVERTICAL );
m_grid = new WX_GRID( this, wxID_ANY, wxDefaultPosition, wxSize( 820,200 ), 0 );
// Grid
m_grid->CreateGrid( 0, 10 );
m_grid->EnableEditing( false );
m_grid->EnableGridLines( true );
m_grid->EnableDragGridSize( false );
m_grid->SetMargins( 0, 0 );
// Columns
m_grid->SetColSize( 0, 60 );
m_grid->SetColSize( 1, 200 );
m_grid->SetColSize( 2, 500 );
m_grid->SetColSize( 3, 60 );
m_grid->SetColSize( 4, 0 );
m_grid->SetColSize( 5, 0 );
m_grid->SetColSize( 6, 0 );
m_grid->SetColSize( 7, 0 );
m_grid->SetColSize( 8, 0 );
m_grid->SetColSize( 9, 0 );
m_grid->EnableDragColMove( false );
m_grid->EnableDragColSize( true );
m_grid->SetColLabelValue( 0, _("Active") );
m_grid->SetColLabelValue( 1, _("Name") );
m_grid->SetColLabelValue( 2, _("Path") );
m_grid->SetColLabelValue( 3, _("Status") );
m_grid->SetColLabelSize( 22 );
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 );
bAntialiasingSizer->Add( m_grid, 5, wxALL|wxEXPAND, 5 );
bLeftSizer->Add( bAntialiasingSizer, 0, wxEXPAND|wxLEFT|wxTOP, 5 );
wxBoxSizer* bButtonsSizer;
bButtonsSizer = new wxBoxSizer( wxHORIZONTAL );
m_btnAddRepo = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_btnAddRepo->SetToolTip( _("Add new repository") );
bButtonsSizer->Add( m_btnAddRepo, 0, wxALL, 5 );
m_btnEditRepo = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_btnEditRepo->SetToolTip( _("Edit repository properties") );
bButtonsSizer->Add( m_btnEditRepo, 0, wxALL, 5 );
bButtonsSizer->Add( 0, 0, 1, wxEXPAND, 5 );
m_btnDelete = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_btnDelete->SetToolTip( _("Remove Git Repository") );
bButtonsSizer->Add( m_btnDelete, 0, wxBOTTOM|wxRIGHT|wxTOP, 5 );
bLeftSizer->Add( bButtonsSizer, 1, wxALL|wxEXPAND, 5 );
bPanelSizer->Add( bLeftSizer, 0, wxRIGHT, 20 );
this->SetSizer( bPanelSizer );
this->Layout();
bPanelSizer->Fit( this );
// Connect Events
m_cbDefault->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PANEL_GIT_REPOS_BASE::onDefaultClick ), NULL, this );
m_grid->Connect( wxEVT_GRID_CELL_LEFT_DCLICK, wxGridEventHandler( PANEL_GIT_REPOS_BASE::onGridDClick ), NULL, this );
m_btnAddRepo->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_GIT_REPOS_BASE::onAddClick ), NULL, this );
m_btnEditRepo->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_GIT_REPOS_BASE::onEditClick ), NULL, this );
m_btnDelete->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_GIT_REPOS_BASE::onDeleteClick ), NULL, this );
}
PANEL_GIT_REPOS_BASE::~PANEL_GIT_REPOS_BASE()
{
// Disconnect Events
m_cbDefault->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PANEL_GIT_REPOS_BASE::onDefaultClick ), NULL, this );
m_grid->Disconnect( wxEVT_GRID_CELL_LEFT_DCLICK, wxGridEventHandler( PANEL_GIT_REPOS_BASE::onGridDClick ), NULL, this );
m_btnAddRepo->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_GIT_REPOS_BASE::onAddClick ), NULL, this );
m_btnEditRepo->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_GIT_REPOS_BASE::onEditClick ), NULL, this );
m_btnDelete->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_GIT_REPOS_BASE::onDeleteClick ), NULL, this );
}

View File

@ -0,0 +1,73 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-254-gc2ef7767)
// 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 STD_BITMAP_BUTTON;
class WX_GRID;
#include "widgets/resettable_panel.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/checkbox.h>
#include <wx/textctrl.h>
#include <wx/sizer.h>
#include <wx/statline.h>
#include <wx/grid.h>
#include <wx/bmpbuttn.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/button.h>
#include <wx/panel.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class PANEL_GIT_REPOS_BASE
///////////////////////////////////////////////////////////////////////////////
class PANEL_GIT_REPOS_BASE : public RESETTABLE_PANEL
{
private:
protected:
wxStaticText* m_staticText12;
wxCheckBox* m_cbDefault;
wxStaticText* m_authorLabel;
wxTextCtrl* m_author;
wxStaticText* m_authorEmailLabel;
wxTextCtrl* m_authorEmail;
wxStaticLine* m_staticline3;
wxStaticText* m_staticText20;
WX_GRID* m_grid;
STD_BITMAP_BUTTON* m_btnAddRepo;
STD_BITMAP_BUTTON* m_btnEditRepo;
STD_BITMAP_BUTTON* m_btnDelete;
// Virtual event handlers, override them in your derived class
virtual void onDefaultClick( wxCommandEvent& event ) { event.Skip(); }
virtual void onGridDClick( wxGridEvent& event ) { event.Skip(); }
virtual void onAddClick( wxCommandEvent& event ) { event.Skip(); }
virtual void onEditClick( wxCommandEvent& event ) { event.Skip(); }
virtual void onDeleteClick( wxCommandEvent& event ) { event.Skip(); }
public:
PANEL_GIT_REPOS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
~PANEL_GIT_REPOS_BASE();
};

View File

@ -0,0 +1,953 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="16" />
<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">panel_git_repos_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="image_path_wrapper_function_name"></property>
<property name="indent_with_spaces"></property>
<property name="internationalize">1</property>
<property name="name">PanelGitRepos</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_array_enum">0</property>
<property name="use_enum">1</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="drag_accept_files">0</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_GIT_REPOS_BASE</property>
<property name="pos"></property>
<property name="size">-1,-1</property>
<property name="subclass">RESETTABLE_PANEL; widgets/resettable_panel.h; Not forward_declare</property>
<property name="tooltip"></property>
<property name="two_step_creation">0</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">bPanelSizer</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">20</property>
<property name="flag">wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bLeftSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="0">
<property name="border">10</property>
<property name="flag">wxEXPAND|wxLEFT|wxTOP</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="0">
<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="drag_accept_files">0</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">Git Commit Data</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_staticText12</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">13</property>
<property name="flag">wxBOTTOM|wxEXPAND|wxLEFT|wxTOP</property>
<property name="proportion">1</property>
<object class="wxFlexGridSizer" expanded="1">
<property name="cols">2</property>
<property name="flexible_direction">wxBOTH</property>
<property name="growablecols">1</property>
<property name="growablerows"></property>
<property name="hgap">0</property>
<property name="minimum_size"></property>
<property name="name">fgSizer1</property>
<property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
<property name="permission">none</property>
<property name="rows">0</property>
<property name="vgap">0</property>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="0">
<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">1</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="drag_accept_files">0</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">Use default values</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_cbDefault</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">onDefaultClick</event>
</object>
</object>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="spacer" expanded="0">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="0">
<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="drag_accept_files">0</property>
<property name="enabled">0</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">Author name:</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_authorLabel</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="0">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxTextCtrl" expanded="0">
<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="drag_accept_files">0</property>
<property name="enabled">0</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_author</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>
</object>
</object>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="0">
<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="drag_accept_files">0</property>
<property name="enabled">0</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">Author e-mail:</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_authorEmailLabel</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="0">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxTextCtrl" expanded="0">
<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="drag_accept_files">0</property>
<property name="enabled">0</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_authorEmail</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>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxBOTTOM</property>
<property name="proportion">0</property>
<object class="wxStaticLine" expanded="0">
<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="drag_accept_files">0</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_staticline3</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">wxLI_HORIZONTAL</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="0">
<property name="border">13</property>
<property name="flag">wxEXPAND|wxLEFT|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="0">
<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="drag_accept_files">0</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">Git Repositories</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_staticText20</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">wxEXPAND|wxLEFT|wxTOP</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bAntialiasingSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">5</property>
<object class="wxGrid" expanded="0">
<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">22</property>
<property name="col_label_values">&quot;Active&quot; &quot;Name&quot; &quot;Path&quot; &quot;Status&quot;</property>
<property name="col_label_vert_alignment">wxALIGN_CENTER</property>
<property name="cols">10</property>
<property name="column_sizes">60,200,500,60,0,0,0,0,0,0</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_accept_files">0</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">0</property>
<property name="show">1</property>
<property name="size">820,200</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="OnGridCellLeftDClick">onGridDClick</event>
</object>
</object>
</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="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bButtonsSizer</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="auth_needed">0</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="drag_accept_files">0</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">MyButton</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_btnAddRepo</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">STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Add new repository</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">onAddClick</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="auth_needed">0</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="drag_accept_files">0</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">MyButton</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_btnEditRepo</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">STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Edit repository properties</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">onEditClick</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 class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxRIGHT|wxTOP</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="auth_needed">0</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="drag_accept_files">0</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">MyButton</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_btnDelete</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">STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Remove Git Repository</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">onDeleteClick</event>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</wxFormBuilder_Project>

View File

@ -23,9 +23,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <advanced_config.h>
#include <bitmaps.h>
#include <bitmap_store.h>
#include <dialog_shim.h>
#include <dialogs/git/panel_git_repos.h>
#include <dialogs/panel_common_settings.h>
#include <dialogs/panel_mouse_settings.h>
#include <dialogs/panel_data_collection.h>
@ -1071,6 +1073,15 @@ void EDA_BASE_FRAME::ShowPreferences( wxString aStartPage, wxString aStartParent
book->AddPage( hotkeysPanel, _( "Hotkeys" ) );
// This currently allows pre-defined repositories that we
// don't use, so keep it disabled at the moment
if( ADVANCED_CFG::GetCfg().m_EnableGit && false )
book->AddLazyPage(
[]( wxWindow* aParent ) -> wxWindow*
{
return new PANEL_GIT_REPOS( aParent );
}, _( "Version Control" ) );
#ifdef KICAD_USE_SENTRY
book->AddLazyPage(
[]( wxWindow* aParent ) -> wxWindow*

View File

@ -1721,6 +1721,129 @@ PLOT_DASH_TYPE EDA_SHAPE::GetLineStyle() const
}
bool EDA_SHAPE::operator==( const EDA_SHAPE& aOther ) const
{
if( GetShape() != aOther.GetShape() )
return false;
if( m_fill != aOther.m_fill )
return false;
if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
return false;
if( m_stroke.GetPlotStyle() != aOther.m_stroke.GetPlotStyle() )
return false;
if( m_fillColor != aOther.m_fillColor )
return false;
if( m_start != aOther.m_start )
return false;
if( m_end != aOther.m_end )
return false;
if( m_arcCenter != aOther.m_arcCenter )
return false;
if( m_bezierC1 != aOther.m_bezierC1 )
return false;
if( m_bezierC2 != aOther.m_bezierC2 )
return false;
if( m_bezierPoints != aOther.m_bezierPoints )
return false;
for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
{
if( m_poly.CVertex( ii ) != aOther.m_poly.CVertex( ii ) )
return false;
}
return true;
}
double EDA_SHAPE::Similarity( const EDA_SHAPE& aOther ) const
{
if( GetShape() != aOther.GetShape() )
return 0.0;
double similarity = 1.0;
if( m_fill != aOther.m_fill )
similarity *= 0.9;
if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
similarity *= 0.9;
if( m_stroke.GetPlotStyle() != aOther.m_stroke.GetPlotStyle() )
similarity *= 0.9;
if( m_fillColor != aOther.m_fillColor )
similarity *= 0.9;
if( m_start != aOther.m_start )
similarity *= 0.9;
if( m_end != aOther.m_end )
similarity *= 0.9;
if( m_arcCenter != aOther.m_arcCenter )
similarity *= 0.9;
if( m_bezierC1 != aOther.m_bezierC1 )
similarity *= 0.9;
if( m_bezierC2 != aOther.m_bezierC2 )
similarity *= 0.9;
{
int m = m_bezierPoints.size();
int n = aOther.m_bezierPoints.size();
size_t longest = alg::longest_common_subset( m_bezierPoints, aOther.m_bezierPoints );
similarity *= std::pow( 0.9, m + n - 2 * longest );
}
{
int m = m_poly.TotalVertices();
int n = aOther.m_poly.TotalVertices();
std::vector<VECTOR2I> poly;
std::vector<VECTOR2I> otherPoly;
VECTOR2I lastPt( 0, 0 );
// We look for the longest common subset of the two polygons, but we need to
// offset each point because we're actually looking for overall similarity, not just
// exact matches. So if the zone is moved by 1IU, we only want one point to be
// considered "moved" rather than the entire polygon. In this case, the first point
// will not be a match but the rest of the sequence will.
for( int ii = 0; ii < m; ++ii )
{
poly.emplace_back( lastPt - m_poly.CVertex( ii ) );
lastPt = m_poly.CVertex( ii );
}
lastPt = VECTOR2I( 0, 0 );
for( int ii = 0; ii < n; ++ii )
{
otherPoly.emplace_back( lastPt - aOther.m_poly.CVertex( ii ) );
lastPt = aOther.m_poly.CVertex( ii );
}
size_t longest = alg::longest_common_subset( poly, otherPoly );
similarity *= std::pow( 0.9, m + n - 2 * longest );
}
return similarity;
}
IMPLEMENT_ENUM_TO_WXANY( SHAPE_T )
IMPLEMENT_ENUM_TO_WXANY( PLOT_DASH_TYPE )

View File

@ -1025,6 +1025,67 @@ bool EDA_TEXT::ValidateHyperlink( const wxString& aURL )
return false;
}
double EDA_TEXT::Levenshtein( const EDA_TEXT& aOther ) const
{
// Compute the Levenshtein distance between the two strings
const wxString& str1 = GetText();
const wxString& str2 = aOther.GetText();
int m = str1.length();
int n = str2.length();
if( n == 0 || m == 0 )
return 0.0;
// Create a matrix to store the distance values
std::vector<std::vector<int>> distance(m + 1, std::vector<int>(n + 1));
// Initialize the matrix
for( int i = 0; i <= m; i++ )
distance[i][0] = i;
for( int j = 0; j <= n; j++ )
distance[0][j] = j;
// Calculate the distance
for( int i = 1; i <= m; i++ )
{
for( int j = 1; j <= n; j++ )
{
if( str1[i - 1] == str2[j - 1] )
{
distance[i][j] = distance[i - 1][j - 1];
}
else
{
distance[i][j] = std::min( { distance[i - 1][j], distance[i][j - 1],
distance[i - 1][j - 1] } ) + 1;
}
}
}
// Calculate similarity score
int maxLen = std::max( m, n );
double similarity = 1.0 - ( static_cast<double>( distance[m][n] ) / maxLen );
return similarity;
}
double EDA_TEXT::Similarity( const EDA_TEXT& aOther ) const
{
double retval = 1.0;
if( !( m_attributes == aOther.m_attributes ) )
retval *= 0.9;
if( m_pos != aOther.m_pos )
retval *= 0.9;
retval *= Levenshtein( aOther );
return retval;
}
bool EDA_TEXT::IsGotoPageHref( const wxString& aHref, wxString* aDestination )
{

View File

@ -40,6 +40,8 @@
#include <launch_ext.h>
#include "wx/tokenzr.h"
#include <filesystem>
void QuoteString( wxString& string )
{
if( !string.StartsWith( wxT( "\"" ) ) )
@ -292,3 +294,51 @@ wxString QuoteFullPath( wxFileName& fn, wxPathFormat format )
{
return wxT( "\"" ) + fn.GetFullPath( format ) + wxT( "\"" );
}
bool RmDirRecursive( const wxString& aFileName, wxString* aErrors )
{
namespace fs = std::filesystem;
std::string rmDir = aFileName.ToStdString();
if( rmDir.length() < 3 )
{
if( aErrors )
*aErrors = _( "Invalid directory name, cannot remove root" );
return false;
}
if( !fs::exists( rmDir ) )
{
if( aErrors )
*aErrors = wxString::Format( _( "Directory '%s' does not exist" ), aFileName );
return false;
}
fs::path path( rmDir );
if( !fs::is_directory( path ) )
{
if( aErrors )
*aErrors = wxString::Format( _( "'%s' is not a directory" ), aFileName );
return false;
}
try
{
fs::remove_all( path );
}
catch( const fs::filesystem_error& e )
{
if( aErrors )
*aErrors = wxString::Format( _( "Error removing directory '%s': %s" ), aFileName, e.what() );
return false;
}
return true;
}

View File

@ -0,0 +1,108 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "git_add_to_index_handler.h"
#include <wx/string.h>
#include <wx/log.h>
GIT_ADD_TO_INDEX_HANDLER::GIT_ADD_TO_INDEX_HANDLER( git_repository* aRepository )
{
m_repository = aRepository;
m_filesToAdd.clear();
}
GIT_ADD_TO_INDEX_HANDLER::~GIT_ADD_TO_INDEX_HANDLER()
{
}
bool GIT_ADD_TO_INDEX_HANDLER::AddToIndex( const wxString& aFilePath )
{
// Test if file is currently in the index
git_index* index = nullptr;
size_t at_pos = 0;
if( git_repository_index( &index, m_repository ) != 0 )
{
wxLogError( "Failed to get repository index" );
return false;
}
git_index_find( &at_pos, index, aFilePath.ToUTF8().data() );
if( at_pos >= 0)
{
git_index_free( index );
wxLogError( "%s already in index", aFilePath );
return false;
}
git_index_free( index );
// Add file to index if not already there
m_filesToAdd.push_back( aFilePath );
return true;
}
bool GIT_ADD_TO_INDEX_HANDLER::PerformAddToIndex()
{
git_index* index = nullptr;
m_filesFailedToAdd.clear();
if( git_repository_index( &index, m_repository ) != 0 )
{
wxLogError( "Failed to get repository index" );
std::copy( m_filesToAdd.begin(), m_filesToAdd.end(), std::back_inserter( m_filesFailedToAdd ) );
return false;
}
for( auto& file : m_filesToAdd )
{
if( git_index_add_bypath( index, file.ToUTF8().data() ) != 0 )
{
wxLogError( "Failed to add %s to index", file );
m_filesFailedToAdd.push_back( file );
continue;
}
}
if( git_index_write( index ) != 0 )
{
wxLogError( "Failed to write index" );
m_filesFailedToAdd.clear();
std::copy( m_filesToAdd.begin(), m_filesToAdd.end(), std::back_inserter( m_filesFailedToAdd ) );
git_index_free( index );
return false;
}
git_index_free( index );
return true;
}

View File

@ -0,0 +1,50 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GIT_ADD_TO_INDEX_HANDLER_H_
#define GIT_ADD_TO_INDEX_HANDLER_H_
#include <git2.h>
#include <vector>
class wxString;
class GIT_ADD_TO_INDEX_HANDLER
{
public:
GIT_ADD_TO_INDEX_HANDLER( git_repository* aRepository );
virtual ~GIT_ADD_TO_INDEX_HANDLER();
bool AddToIndex( const wxString& aFilePath );
bool PerformAddToIndex();
private:
git_repository* m_repository;
std::vector<wxString> m_filesToAdd;
std::vector<wxString> m_filesFailedToAdd;
};
#endif /* GIT_ADD_TO_INDEX_HANDLER_H_ */

View File

@ -0,0 +1,82 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "git_clone_handler.h"
#include <git/kicad_git_common.h>
#include <git2.h>
#include <wx/filename.h>
GIT_CLONE_HANDLER::GIT_CLONE_HANDLER() : KIGIT_COMMON( nullptr )
{}
GIT_CLONE_HANDLER::~GIT_CLONE_HANDLER()
{
if( m_repo )
git_repository_free( m_repo );
}
bool GIT_CLONE_HANDLER::PerformClone()
{
wxFileName clonePath( m_clonePath );
if( !clonePath.DirExists() )
{
if( !clonePath.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
{
AddErrorString( wxString::Format( _( "Could not create directory '%s'" ), m_clonePath ) );
return false;
}
}
git_clone_options cloneOptions = GIT_CLONE_OPTIONS_INIT;
cloneOptions.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
cloneOptions.checkout_opts.progress_cb = clone_progress_cb;
cloneOptions.checkout_opts.progress_payload = this;
cloneOptions.fetch_opts.callbacks.transfer_progress = transfer_progress_cb;
cloneOptions.fetch_opts.callbacks.credentials = credentials_cb;
cloneOptions.fetch_opts.callbacks.payload = this;
m_testedTypes = 0;
if( git_clone( &m_repo, m_URL.ToStdString().c_str(), m_clonePath.ToStdString().c_str(), &cloneOptions ) != 0 )
{
AddErrorString( wxString::Format( _( "Could not clone repository '%s'" ), m_URL ) );
return false;
}
if( m_progressReporter )
m_progressReporter->Hide();
m_previousProgress = 0;
return true;
}
void GIT_CLONE_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage )
{
ReportProgress( aCurrent, aTotal, aMessage );
}

View File

@ -0,0 +1,56 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GIT_CLONE_HANDLER_H_
#define GIT_CLONE_HANDLER_H_
#include <git/kicad_git_common.h>
#include <git/git_progress.h>
class GIT_CLONE_HANDLER : public KIGIT_COMMON, public GIT_PROGRESS
{
public:
GIT_CLONE_HANDLER();
~GIT_CLONE_HANDLER();
bool PerformClone();
void SetURL( const wxString& aURL ) { m_URL = aURL; }
wxString GetURL() const { return m_URL; }
void SetBranch( const wxString& aBranch ) { m_branch = aBranch; }
wxString GetBranch() const { return m_branch; }
void SetClonePath( const wxString& aPath ) { m_clonePath = aPath; }
wxString GetClonePath() const { return m_clonePath; }
void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) override;
private:
wxString m_URL;
wxString m_branch;
wxString m_clonePath;
};
#endif

View File

@ -0,0 +1,49 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "git_commit_handler.h"
GIT_COMMIT_HANDLER::GIT_COMMIT_HANDLER( git_repository* aRepo ) :
KIGIT_COMMON( aRepo )
{}
GIT_COMMIT_HANDLER::~GIT_COMMIT_HANDLER()
{}
GIT_COMMIT_HANDLER::CommitResult GIT_COMMIT_HANDLER::PerformCommit( const std::vector<std::string>& aFilesToCommit )
{
return CommitResult::Success;
}
std::string GIT_COMMIT_HANDLER::GetErrorString() const
{
return m_errorString;
}
void GIT_COMMIT_HANDLER::AddErrorString( const std::string& aErrorString )
{
m_errorString += aErrorString;
}

View File

@ -0,0 +1,58 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GIT_COMMIT_HANDLER_H
#define GIT_COMMIT_HANDLER_H
// Define a class to handle git commit operations
#include <git/kicad_git_common.h>
#include <git2.h>
#include <string>
#include <vector>
class GIT_COMMIT_HANDLER : public KIGIT_COMMON
{
public:
GIT_COMMIT_HANDLER( git_repository* aRepo );
virtual ~GIT_COMMIT_HANDLER();
enum class CommitResult
{
Success,
Error,
Cancelled
};
CommitResult PerformCommit( const std::vector<std::string>& aFilesToCommit );
std::string GetErrorString() const;
private:
void AddErrorString( const std::string& aErrorString );
std::string m_errorString;
};
#endif // GIT_COMMIT_HANDLER_H

View File

View File

72
common/git/git_progress.h Normal file
View File

@ -0,0 +1,72 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GIT_PROGRESS_H_
#define GIT_PROGRESS_H_
#include <widgets/wx_progress_reporters.h>
#include <memory>
class GIT_PROGRESS
{
public:
GIT_PROGRESS() : m_previousProgress( 0 )
{
m_progressReporter.reset();
}
void SetProgressReporter( std::unique_ptr<WX_PROGRESS_REPORTER> aProgressReporter )
{
m_progressReporter = std::move( aProgressReporter );
}
void ReportProgress( int aCurrent, int aTotal, const wxString& aMessage )
{
if( m_progressReporter )
{
if( aCurrent == m_previousProgress || aTotal == 0 )
{
m_progressReporter->Pulse( aMessage );
}
else
{
m_progressReporter->SetCurrentProgress( static_cast<double>( aCurrent ) / aTotal );
m_progressReporter->Report( aMessage );
}
m_previousProgress = aCurrent;
m_progressReporter->KeepRefreshing();
}
}
protected:
int m_previousProgress;
std::unique_ptr<WX_PROGRESS_REPORTER> m_progressReporter;
};
#endif // GIT_PROGRESS_H__

View File

@ -0,0 +1,343 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "git_pull_handler.h"
#include <git/kicad_git_common.h>
#include <iostream>
GIT_PULL_HANDLER::GIT_PULL_HANDLER( git_repository* aRepo ) : KIGIT_COMMON( aRepo )
{}
GIT_PULL_HANDLER::~GIT_PULL_HANDLER()
{}
bool GIT_PULL_HANDLER::PerformFetch()
{
// Fetch updates from remote repository
git_remote* remote = nullptr;
if( git_remote_lookup( &remote, m_repo, "origin" ) != 0 )
{
AddErrorString( wxString::Format( _( "Could not lookup remote '%s'" ), "origin" ).ToStdString() );
return false;
}
git_remote_callbacks remoteCallbacks = GIT_REMOTE_CALLBACKS_INIT;
remoteCallbacks.sideband_progress = progress_cb;
remoteCallbacks.transfer_progress = transfer_progress_cb;
remoteCallbacks.credentials = credentials_cb;
remoteCallbacks.payload = this;
if( git_remote_connect( remote, GIT_DIRECTION_FETCH, &remoteCallbacks, nullptr, nullptr ) )
{
git_remote_free( remote );
AddErrorString( wxString::Format( _( "Could not connect to remote '%s'" ), "origin" ).ToStdString() );
return false;
}
git_fetch_options fetchOptions = GIT_FETCH_OPTIONS_INIT;
fetchOptions.callbacks = remoteCallbacks;
if( git_remote_fetch( remote, nullptr, &fetchOptions, nullptr ) )
{
git_remote_free( remote );
AddErrorString( wxString::Format( _( "Could not fetch data from remote '%s'" ), "origin" ) );
return false;
}
git_remote_free( remote );
return true;
}
PullResult GIT_PULL_HANDLER::PerformPull()
{
PullResult result = PullResult::Success;
if( !PerformFetch() )
return PullResult::Error;
git_oid pull_merge_oid = {};
if( git_repository_fetchhead_foreach( m_repo, fetchhead_foreach_cb, &pull_merge_oid ) )
{
AddErrorString( _( "Could not read 'FETCH_HEAD'" ) );
return PullResult::Error;
}
git_annotated_commit* fetchhead_commit;
if( git_annotated_commit_lookup( &fetchhead_commit, m_repo, &pull_merge_oid ) )
{
AddErrorString( _( "Could not lookup commit" ) );
return PullResult::Error;
}
const git_annotated_commit* merge_commits[] = { fetchhead_commit };
git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_preference = GIT_MERGE_PREFERENCE_NONE;
if( git_merge_analysis( &merge_analysis, &merge_preference, m_repo, merge_commits, 1 ) )
{
AddErrorString( _( "Could not analyze merge" ) );
git_annotated_commit_free( fetchhead_commit );
return PullResult::Error;
}
if( !( merge_analysis & GIT_MERGE_ANALYSIS_NORMAL ) )
git_annotated_commit_free( fetchhead_commit );
if( merge_analysis & GIT_MERGE_ANALYSIS_UNBORN )
{
AddErrorString( _( "Invalid HEAD. Cannot merge." ) );
return PullResult::MergeFailed;
}
// Nothing to do if the repository is up to date
if( merge_analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE )
{
git_repository_state_cleanup( m_repo );
return PullResult::UpToDate;
}
// Fast-forward is easy, just update the local reference
if( merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD )
{
return handleFastForward();
}
if( merge_analysis & GIT_MERGE_ANALYSIS_NORMAL )
{
PullResult ret = handleMerge( merge_commits, 1 );
git_annotated_commit_free( fetchhead_commit );
return ret;
}
//TODO: handle merges when they need to be resolved
return result;
}
const std::vector<std::pair<std::string, std::vector<CommitDetails>>>& GIT_PULL_HANDLER::GetFetchResults() const {
return m_fetchResults;
}
std::string GIT_PULL_HANDLER::getFirstLineFromCommitMessage( const std::string& aMessage )
{
size_t firstLineEnd = aMessage.find_first_of( '\n' );
if( firstLineEnd != std::string::npos )
return aMessage.substr( 0, firstLineEnd );
return aMessage;
}
std::string GIT_PULL_HANDLER::getFormattedCommitDate( const git_time& aTime )
{
char dateBuffer[64];
strftime( dateBuffer, sizeof( dateBuffer ), "%Y-%b-%d %H:%M:%S", gmtime( &aTime.time ) );
return dateBuffer;
}
PullResult GIT_PULL_HANDLER::handleFastForward()
{
// Update local references with fetched data
git_reference* updatedRef = nullptr;
if( git_repository_head( &updatedRef, m_repo ) )
{
AddErrorString( _( "Could not get repository head" ) );
return PullResult::Error;
}
const char* updatedRefName = git_reference_name( updatedRef );
git_reference_free( updatedRef );
git_oid updatedRefOid;
if( git_reference_name_to_id( &updatedRefOid, m_repo, updatedRefName ) )
{
AddErrorString( wxString::Format( _( "Could not get reference OID for reference '%s'" ), updatedRefName ) );
return PullResult::Error;
}
git_checkout_options checkoutOptions = GIT_CHECKOUT_OPTIONS_INIT;
checkoutOptions.checkout_strategy = GIT_CHECKOUT_SAFE;
if( git_checkout_head( m_repo, &checkoutOptions ) )
{
AddErrorString( _( "Failed to perform checkout operation." ) );
return PullResult::Error;
}
// Collect commit details for updated references
git_revwalk* revWalker = nullptr;
git_revwalk_new( &revWalker, m_repo );
git_revwalk_sorting( revWalker, GIT_SORT_TIME );
git_revwalk_push_glob( revWalker, updatedRefName );
git_oid commitOid;
while( git_revwalk_next( &commitOid, revWalker ) == 0 )
{
git_commit* commit = nullptr;
if( git_commit_lookup( &commit, m_repo, &commitOid ) )
{
AddErrorString( wxString::Format( _( "Could not lookup commit '{}'" ), git_oid_tostr_s( &commitOid ) ) );
git_revwalk_free( revWalker );
return PullResult::Error;
}
CommitDetails details;
details.m_sha = git_oid_tostr_s( &commitOid );
details.m_firstLine = getFirstLineFromCommitMessage( git_commit_message( commit ) );
details.m_author = git_commit_author( commit )->name;
details.m_date = getFormattedCommitDate( git_commit_author( commit )->when );
std::pair<std::string, std::vector<CommitDetails>>& branchCommits =
m_fetchResults.emplace_back();
branchCommits.first = updatedRefName;
branchCommits.second.push_back( details );
//TODO: log these to the parent
git_commit_free( commit );
}
git_revwalk_free( revWalker );
git_repository_state_cleanup( m_repo );
return PullResult::FastForward;
}
PullResult GIT_PULL_HANDLER::handleMerge( const git_annotated_commit** aMergeHeads,
size_t aMergeHeadsCount )
{
git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
if( git_merge( m_repo, aMergeHeads, aMergeHeadsCount, &merge_opts, &checkout_opts ) )
{
AddErrorString( _( "Could not merge commits" ) );
return PullResult::Error;
}
// Get the repository index
git_index* index;
if( git_repository_index( &index, m_repo ) )
{
AddErrorString( _( "Could not get repository index" ) );
return PullResult::Error;
}
// Check for conflicts
git_index_conflict_iterator* conflicts;
if( git_index_conflict_iterator_new( &conflicts, index ) )
{
AddErrorString( _( "Could not get conflict iterator" ) );
return PullResult::Error;
}
const git_index_entry* ancestor;
const git_index_entry* our;
const git_index_entry* their;
std::vector<ConflictData> conflict_data;
while( git_index_conflict_next( &ancestor, &our, &their, conflicts ) == 0 )
{
// Case 3: Both files have changed
if( ancestor && our && their )
{
ConflictData conflict_datum;
conflict_datum.filename = our->path;
conflict_datum.our_oid = our->id;
conflict_datum.their_oid = their->id;
conflict_datum.our_commit_time = our->mtime.seconds;
conflict_datum.their_commit_time = their->mtime.seconds;
conflict_datum.our_status = _( "Changed" );
conflict_datum.their_status = _( "Changed" );
conflict_data.push_back( conflict_datum );
}
// Case 4: File added in both ours and theirs
else if( !ancestor && our && their )
{
ConflictData conflict_datum;
conflict_datum.filename = our->path;
conflict_datum.our_oid = our->id;
conflict_datum.their_oid = their->id;
conflict_datum.our_commit_time = our->mtime.seconds;
conflict_datum.their_commit_time = their->mtime.seconds;
conflict_datum.our_status = _( "Added" );
conflict_datum.their_status = _( "Added" );
conflict_data.push_back( conflict_datum );
}
// Case 1: Remote file has changed or been added, local file has not
else if( their && !our )
{
// Accept their changes
git_index_add( index, their );
}
// Case 2: Local file has changed or been added, remote file has not
else if( our && !their )
{
// Accept our changes
git_index_add( index, our );
}
else
{
ConflictData conflict_datum;
conflict_datum.filename = our->path;
conflict_datum.our_oid = our->id;
conflict_datum.their_oid = their->id;
conflict_datum.our_commit_time = our->mtime.seconds;
conflict_datum.their_commit_time = their->mtime.seconds;
conflict_datum.our_status = _( "Other" );
conflict_datum.their_status = _( "Other" );
conflict_data.push_back( conflict_datum );
}
}
if( conflict_data.empty() )
{
git_index_conflict_cleanup( index );
git_index_write( index );
}
git_index_conflict_iterator_free( conflicts );
git_index_free( index );
return conflict_data.empty() ? PullResult::Success : PullResult::MergeFailed;
}
void GIT_PULL_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage )
{
ReportProgress( aCurrent, aTotal, aMessage );
}

View File

@ -0,0 +1,100 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GITPULLHANDLER_HPP
#define GITPULLHANDLER_HPP
#include <git2.h>
#include <functional>
#include <vector>
#include <string>
#include "kicad_git_common.h"
#include <git/git_progress.h>
// Structure to store commit details
struct CommitDetails
{
std::string m_sha;
std::string m_firstLine;
std::string m_author;
std::string m_date;
};
// Enum for result codes
enum class PullResult
{
Success,
Error,
UpToDate,
FastForward,
MergeFailed
};
struct ConflictData
{
std::string filename;
std::string our_status;
std::string their_status;
git_oid our_oid;
git_oid their_oid;
git_time_t our_commit_time;
git_time_t their_commit_time;
bool use_ours; // Flag indicating user's choice (true = ours, false = theirs)
};
class GIT_PULL_HANDLER : public KIGIT_COMMON, public GIT_PROGRESS
{
public:
GIT_PULL_HANDLER( git_repository* aRepo );
~GIT_PULL_HANDLER();
PullResult PerformPull();
bool PerformFetch();
const std::vector<std::pair<std::string, std::vector<CommitDetails>>>& GetFetchResults() const;
// Set the callback function for conflict resolution
void SetConflictCallback(
std::function<int( std::vector<ConflictData>& aConflicts )> aCallback )
{
m_conflictCallback = aCallback;
}
void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) override;
private:
std::string getFirstLineFromCommitMessage( const std::string& aMessage );
std::string getFormattedCommitDate( const git_time& aTime );private:
PullResult handleFastForward();
PullResult handleMerge( const git_annotated_commit** aMergeHeads, size_t aMergeHeadsCount);
std::vector<std::pair<std::string, std::vector<CommitDetails>>> m_fetchResults;
std::function<int( std::vector<ConflictData>& aConflicts )> m_conflictCallback;
};
#endif // GITPULLHANDLER_HPP

View File

@ -0,0 +1,79 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "git_push_handler.h"
#include <git/kicad_git_common.h>
#include <iostream>
GIT_PUSH_HANDLER::GIT_PUSH_HANDLER( git_repository* aRepo ) : KIGIT_COMMON( aRepo )
{}
GIT_PUSH_HANDLER::~GIT_PUSH_HANDLER()
{}
PushResult GIT_PUSH_HANDLER::PerformPush()
{
PushResult result = PushResult::Success;
// Fetch updates from remote repository
git_remote* remote = nullptr;
if( git_remote_lookup( &remote, m_repo, "origin" ) != 0 )
{
AddErrorString( _( "Could not lookup remote" ) );
return PushResult::Error;
}
git_remote_callbacks remoteCallbacks = GIT_REMOTE_CALLBACKS_INIT;
remoteCallbacks.sideband_progress = progress_cb;
remoteCallbacks.transfer_progress = transfer_progress_cb;
remoteCallbacks.update_tips = update_cb;
remoteCallbacks.push_transfer_progress = push_transfer_progress_cb;
remoteCallbacks.payload = this;
if( git_remote_connect( remote, GIT_DIRECTION_PUSH, &remoteCallbacks, nullptr, nullptr ) )
{
git_remote_free( remote );
AddErrorString( _( "Could not connect to remote" ) );
return PushResult::Error;
}
git_push_options pushOptions = GIT_PUSH_OPTIONS_INIT;
pushOptions.callbacks = remoteCallbacks;
if( git_remote_push( remote, nullptr, &pushOptions ) )
{
git_remote_free( remote );
AddErrorString( _( "Could not push to remote" ) );
return PushResult::Error;
}
return result;
}
void GIT_PUSH_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage )
{
ReportProgress( aCurrent, aTotal, aMessage );
}

View File

@ -0,0 +1,57 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GITPUSHHANDLER_HPP
#define GITPUSHHANDLER_HPP
#include <git2.h>
#include <functional>
#include <vector>
#include <string>
#include "kicad_git_common.h"
#include <git/git_progress.h>
// Enum for result codes
enum class PushResult
{
Success,
Error,
UpToDate
};
class GIT_PUSH_HANDLER : public KIGIT_COMMON, public GIT_PROGRESS
{
public:
GIT_PUSH_HANDLER( git_repository* aRepo );
~GIT_PUSH_HANDLER();
PushResult PerformPush();
void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) override;
private:
};
#endif // GITPUSHHANDLER_HPP

View File

@ -0,0 +1,98 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <wx/string.h>
#include <wx/log.h>
#include "git_remove_from_index_handler.h"
GIT_REMOVE_FROM_INDEX_HANDLER::GIT_REMOVE_FROM_INDEX_HANDLER( git_repository* aRepository )
{
m_repository = aRepository;
m_filesToRemove.clear();
}
GIT_REMOVE_FROM_INDEX_HANDLER::~GIT_REMOVE_FROM_INDEX_HANDLER()
{
}
bool GIT_REMOVE_FROM_INDEX_HANDLER::RemoveFromIndex( const wxString& aFilePath )
{
// Test if file is currently in the index
git_index* index = nullptr;
size_t at_pos = 0;
if( git_repository_index( &index, m_repository ) != 0 )
{
wxLogError( "Failed to get repository index" );
return false;
}
if( git_index_find( &at_pos, index, aFilePath.ToUTF8().data() ) != 0 )
{
git_index_free( index );
wxLogError( "Failed to find index entry for %s", aFilePath );
return false;
}
git_index_free( index );
m_filesToRemove.push_back( aFilePath );
return true;
}
void GIT_REMOVE_FROM_INDEX_HANDLER::PerformRemoveFromIndex()
{
for( auto& file : m_filesToRemove )
{
git_index* index = nullptr;
git_oid oid;
if( git_repository_index( &index, m_repository ) != 0 )
{
wxLogError( "Failed to get repository index" );
return;
}
if( git_index_remove_bypath( index, file.ToUTF8().data() ) != 0 )
{
wxLogError( "Failed to remove index entry for %s", file );
return;
}
if( git_index_write( index ) != 0 )
{
wxLogError( "Failed to write index" );
return;
}
if( git_index_write_tree( &oid, index ) != 0 )
{
wxLogError( "Failed to write index tree" );
return;
}
git_index_free( index );
}
}

View File

@ -0,0 +1,50 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GIT_REMOVE_FROM_INDEX_HANDLER_H_
#define GIT_REMOVE_FROM_INDEX_HANDLER_H_
#include <git2.h>
#include <vector>
class wxString;
class GIT_REMOVE_FROM_INDEX_HANDLER
{
public:
GIT_REMOVE_FROM_INDEX_HANDLER( git_repository* aRepository );
virtual ~GIT_REMOVE_FROM_INDEX_HANDLER();
bool RemoveFromIndex( const wxString& aFilePath );
void PerformRemoveFromIndex();
private:
git_repository* m_repository;
std::vector<wxString> m_filesToRemove;
};
#endif /* GIT_REMOVE_FROM_INDEX_HANDLER_H_ */

View File

View File

View File

@ -0,0 +1,39 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "git_resolve_conflict_handler.h"
GIT_RESOLVE_CONFLICT_HANDLER::GIT_RESOLVE_CONFLICT_HANDLER( git_repository* aRepository )
{
m_repository = aRepository;
}
GIT_RESOLVE_CONFLICT_HANDLER::~GIT_RESOLVE_CONFLICT_HANDLER()
{
}
bool GIT_RESOLVE_CONFLICT_HANDLER::PerformResolveConflict()
{
return true;
}

View File

@ -0,0 +1,43 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GIT_RESOLVE_CONFLICT_HANDLER_H
#define GIT_RESOLVE_CONFLICT_HANDLER_H
#include <git2.h>
class wxString;
class GIT_RESOLVE_CONFLICT_HANDLER
{
public:
GIT_RESOLVE_CONFLICT_HANDLER( git_repository* aRepository );
virtual ~GIT_RESOLVE_CONFLICT_HANDLER();
bool PerformResolveConflict();
private:
git_repository* m_repository;
};
#endif // GIT_RESOLVE_CONFLICT_HANDLER_H

View File

@ -0,0 +1,108 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "git_revert_handler.h"
#include <wx/log.h>
#include <wx/string.h>
GIT_REVERT_HANDLER::GIT_REVERT_HANDLER( git_repository* aRepository )
{
m_repository = aRepository;
}
GIT_REVERT_HANDLER::~GIT_REVERT_HANDLER()
{
}
bool GIT_REVERT_HANDLER::Revert( const wxString& aFilePath )
{
m_filesToRevert.push_back( aFilePath );
return true;
}
static void checkout_progress_cb(const char *path, size_t cur, size_t tot, void *payload)
{
wxLogDebug( "checkout_progress_cb: %s %zu/%zu", path, cur, tot );
}
static int checkout_notify_cb(git_checkout_notify_t why, const char *path,
const git_diff_file *baseline,
const git_diff_file *target,
const git_diff_file *workdir, void *payload)
{
GIT_REVERT_HANDLER* handler = static_cast<GIT_REVERT_HANDLER*>(payload);
if( why & ( GIT_CHECKOUT_NOTIFY_CONFLICT | GIT_CHECKOUT_NOTIFY_IGNORED | GIT_CHECKOUT_NOTIFY_UPDATED ) )
handler->PushFailedFile( path );
return 0;
}
void GIT_REVERT_HANDLER::PerformRevert()
{
git_object* head_commit = NULL;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
// Get the HEAD commit
if (git_revparse_single(&head_commit, m_repository, "HEAD") != 0) {
// Handle error. If we cannot get the HEAD, then there's no point proceeding.
return;
}
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
char** paths = new char*[m_filesToRevert.size()];
for( size_t ii = 0; ii < m_filesToRevert.size(); ii++ )
{
// Set paths to the specific file
paths[ii] = wxStrdup( m_filesToRevert[ii].ToUTF8() );
}
git_strarray arr = { paths, m_filesToRevert.size() };
opts.paths = arr;
opts.progress_cb = checkout_progress_cb;
opts.notify_cb = checkout_notify_cb;
opts.notify_payload = static_cast<void*>(this);
// Attempt to checkout the file(s)
if (git_checkout_tree(m_repository, head_commit, &opts) != 0)
{
const git_error *e = git_error_last();
if (e)
{
wxLogError( "Checkout failed: %d: %s", e->klass, e->message );
}
}
// Free the HEAD commit
for( size_t ii = 0; ii < m_filesToRevert.size(); ii++ )
delete( paths[ii] );
delete[] paths;
git_object_free(head_commit);
}

View File

@ -0,0 +1,54 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GIT_REVERT_HANDLER_H_
#define GIT_REVERT_HANDLER_H_
#include <git2.h>
#include <vector>
class wxString;
class GIT_REVERT_HANDLER
{
public:
GIT_REVERT_HANDLER( git_repository* aRepository );
virtual ~GIT_REVERT_HANDLER();
bool Revert( const wxString& aFilePath );
void PerformRevert();
void PushFailedFile( const wxString& aFilePath )
{
m_filesFailedToRevert.push_back( aFilePath );
}
private:
git_repository* m_repository;
std::vector<wxString> m_filesToRevert;
std::vector<wxString> m_filesFailedToRevert;
};
#endif /* GIT_REVERT_HANDLER_H_ */

View File

View File

View File

@ -0,0 +1,44 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "git_sync_handler.h"
#include <wx/string.h>
#include <git2.h>
GIT_SYNC_HANDLER::GIT_SYNC_HANDLER( git_repository* aRepository )
{
m_repository = aRepository;
}
GIT_SYNC_HANDLER::~GIT_SYNC_HANDLER()
{
}
bool GIT_SYNC_HANDLER::PerformSync()
{
return true;
}

View File

@ -0,0 +1,43 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GIT_SYNC_HANDLER_H_
#define GIT_SYNC_HANDLER_H_
#include <git2.h>
class wxString;
class GIT_SYNC_HANDLER
{
public:
GIT_SYNC_HANDLER( git_repository* aRepository );
virtual ~GIT_SYNC_HANDLER();
bool PerformSync();
private:
git_repository* m_repository;
};
#endif /* GIT_SYNC_HANDLER_H_ */

View File

@ -0,0 +1,97 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <streambuf>
#include <istream>
#include <string>
#include <git2.h>
#include <richio.h>
class BLOB_BUFFER_STREAM : public std::streambuf
{
public:
BLOB_BUFFER_STREAM( git_blob* aBlob )
{
// Yay C++
setg( const_cast<char*>( static_cast<const char*>( git_blob_rawcontent( aBlob ) ) ),
const_cast<char*>( static_cast<const char*>( git_blob_rawcontent( aBlob ) ) ),
const_cast<char*>( static_cast<const char*>( git_blob_rawcontent( aBlob ) ) ) + git_blob_rawsize( aBlob ) );
}
~BLOB_BUFFER_STREAM() override
{
}
int overflow( int c ) override
{
return traits_type::eof();
}
std::streamsize xsputn( const char* s, std::streamsize n ) override
{
return 0;
}
};
// Build a class that implements LINE_READER for git_blobs
class BLOB_READER : public LINE_READER
{
public:
BLOB_READER( git_blob* aBlob ) : m_blob( aBlob )
{
m_stream = new BLOB_BUFFER_STREAM( m_blob );
m_istream = new std::istream( m_stream );
m_line = nullptr;
m_lineNum = 0;
}
~BLOB_READER() override
{
delete m_istream;
delete m_stream;
}
char* ReadLine() override
{
getline( *m_istream, m_buffer );
m_buffer.append( 1, '\n' );
m_length = m_buffer.size();
m_line = (char*) m_buffer.data(); //ew why no const??
// lineNum is incremented even if there was no line read, because this
// leads to better error reporting when we hit an end of file.
++m_lineNum;
return m_istream->eof() ? nullptr : m_line;
}
private:
git_blob* m_blob;
BLOB_BUFFER_STREAM* m_stream;
std::istream* m_istream;
std::string m_buffer;
};

View File

@ -0,0 +1,541 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "kicad_git_common.h"
#include <wx/filename.h>
#include <wx/log.h>
#include <map>
#include <vector>
KIGIT_COMMON::KIGIT_COMMON( git_repository* aRepo ) : m_repo( aRepo )
{}
KIGIT_COMMON::~KIGIT_COMMON()
{}
git_repository* KIGIT_COMMON::GetRepo() const
{
return m_repo;
}
wxString KIGIT_COMMON::GetCurrentBranchName() const
{
git_reference* head = nullptr;
int retval = git_repository_head( &head, m_repo );
if( retval && retval != GIT_EUNBORNBRANCH && retval != GIT_ENOTFOUND )
return wxEmptyString;
git_reference *branch;
if( git_reference_resolve( &branch, head ) )
{
git_reference_free( head );
return wxEmptyString;
}
git_reference_free( head );
const char* branchName = "";
if( git_branch_name( &branchName, branch ) )
{
git_reference_free( branch );
return wxEmptyString;
}
git_reference_free( branch );
return branchName;
}
std::vector<wxString> KIGIT_COMMON::GetBranchNames() const
{
std::vector<wxString> branchNames;
std::map<git_time_t, wxString> branchNamesMap;
wxString firstName;
git_branch_iterator* branchIterator = nullptr;
if( git_branch_iterator_new( &branchIterator, m_repo, GIT_BRANCH_LOCAL ) )
return branchNames;
git_reference* branchReference = nullptr;
git_branch_t branchType;
while( git_branch_next( &branchReference, &branchType, branchIterator ) != GIT_ITEROVER )
{
const char* branchName = "";
if( git_branch_name( &branchName, branchReference ) )
continue;
const git_oid* commitId = git_reference_target( branchReference );
git_commit* commit = nullptr;
if( git_commit_lookup( &commit, m_repo, commitId ) )
continue;
git_time_t commitTime = git_commit_time( commit );
if( git_branch_is_head( branchReference ) )
firstName = branchName;
else
branchNamesMap.emplace( commitTime, branchName );
git_commit_free( commit );
git_reference_free( branchReference );
}
git_branch_iterator_free( branchIterator );
// Add the current branch to the top of the list
if( !firstName.IsEmpty() )
branchNames.push_back( firstName );
// Add the remaining branches in order from newest to oldest
for( auto rit = branchNamesMap.rbegin(); rit != branchNamesMap.rend(); ++rit )
branchNames.push_back( rit->second );
return branchNames;
}
std::vector<wxString> KIGIT_COMMON::GetProjectDirs()
{
std::vector<wxString> projDirs;
git_oid oid;
git_commit* commit;
git_tree *tree;
if( git_reference_name_to_id( &oid, m_repo, "HEAD" ) != GIT_OK )
{
wxLogError( "An error occurred: %s", git_error_last()->message );
return projDirs;
}
if( git_commit_lookup( &commit, m_repo, &oid ) != GIT_OK )
{
wxLogError( "An error occurred: %s", git_error_last()->message );
return projDirs;
}
if( git_commit_tree( &tree, commit ) != GIT_OK )
{
wxLogError( "An error occurred: %s", git_error_last()->message );
return projDirs;
}
// Define callback
git_tree_walk(
tree, GIT_TREEWALK_PRE,
[]( const char* root, const git_tree_entry* entry, void* payload )
{
std::vector<wxString>* prjs = static_cast<std::vector<wxString>*>( payload );
wxFileName root_fn( git_tree_entry_name( entry ) );
root_fn.SetPath( root );
if( git_tree_entry_type( entry ) == GIT_OBJECT_BLOB
&& ( ( root_fn.GetExt() == "kicad_pro" ) || ( root_fn.GetExt() == "pro" ) ) )
{
prjs->push_back( root_fn.GetFullPath() );
}
return 0; // continue walking
},
&projDirs );
git_tree_free( tree );
git_commit_free( commit );
std::sort( projDirs.begin(), projDirs.end(),
[]( const wxString& a, const wxString& b )
{
int a_freq = a.Freq( wxFileName::GetPathSeparator() );
int b_freq = b.Freq( wxFileName::GetPathSeparator() );
if( a_freq == b_freq )
return a < b;
else
return a_freq < b_freq;
} );
return projDirs;
}
std::pair<std::set<wxString>,std::set<wxString>> KIGIT_COMMON::GetDifferentFiles() const
{
auto get_modified_files = [&]( git_oid* from_oid, git_oid* to_oid ) -> std::set<wxString>
{
std::set<wxString> modified_set;
git_revwalk* walker = nullptr;
if( git_revwalk_new( &walker, m_repo ) != GIT_OK )
return modified_set;
if( ( git_revwalk_push( walker, from_oid ) != GIT_OK )
|| ( git_revwalk_hide( walker, to_oid ) != GIT_OK ) )
{
git_revwalk_free( walker );
return modified_set;
}
git_oid oid;
git_commit* commit;
// iterate over all local commits not in remote
while( git_revwalk_next( &oid, walker ) == GIT_OK )
{
if( git_commit_lookup( &commit, m_repo, &oid ) != GIT_OK )
continue;
git_tree *tree, *parent_tree = nullptr;
if( git_commit_tree( &tree, commit ) != GIT_OK )
{
git_commit_free( commit );
continue;
}
// get parent commit tree to diff against
if( !git_commit_parentcount( commit ) )
{
git_tree_free( tree );
git_commit_free( commit );
continue;
}
git_commit* parent;
if( git_commit_parent( &parent, commit, 0 ) != GIT_OK )
{
git_tree_free( tree );
git_commit_free( commit );
continue;
}
if( git_commit_tree( &parent_tree, parent ) != GIT_OK )
{
git_tree_free( tree );
git_commit_free( commit );
git_commit_free( parent );
continue;
}
git_diff* diff;
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
if( git_diff_tree_to_tree( &diff, m_repo, parent_tree, tree, &diff_opts ) == GIT_OK )
{
size_t num_deltas = git_diff_num_deltas( diff );
for( size_t i = 0; i < num_deltas; ++i )
{
const git_diff_delta* delta = git_diff_get_delta( diff, i );
modified_set.insert( delta->new_file.path );
}
git_diff_free( diff );
}
git_tree_free( parent_tree );
git_commit_free( parent );
git_tree_free( tree );
git_commit_free( commit );
}
git_revwalk_free( walker );
return modified_set;
};
std::pair<std::set<wxString>,std::set<wxString>> modified_files;
if( !m_repo )
return modified_files;
git_reference* head = nullptr;
git_reference* remote_head = nullptr;
if( git_repository_head( &head, m_repo ) != GIT_OK )
return modified_files;
if( git_branch_upstream( &remote_head, head ) != GIT_OK )
{
git_reference_free( head );
return modified_files;
}
git_oid head_oid = *git_reference_target( head );
git_oid remote_oid = *git_reference_target( remote_head );
git_reference_free( head );
git_reference_free( remote_head );
modified_files.first = get_modified_files( &head_oid, &remote_oid );
modified_files.second = get_modified_files( &remote_oid, &head_oid );
return modified_files;
}
bool KIGIT_COMMON::HasLocalCommits() const
{
if( !m_repo )
return false;
git_reference* head = nullptr;
git_reference* remote_head = nullptr;
if( git_repository_head( &head, m_repo ) != GIT_OK )
return false;
if( git_branch_upstream( &remote_head, head ) != GIT_OK )
{
git_reference_free( head );
return false;
}
git_oid head_oid = *git_reference_target( head );
git_oid remote_oid = *git_reference_target( remote_head );
git_reference_free( head );
git_reference_free( remote_head );
git_revwalk* walker = nullptr;
if( git_revwalk_new( &walker, m_repo ) != GIT_OK )
return false;
if( ( git_revwalk_push( walker, &head_oid ) != GIT_OK )
|| ( git_revwalk_hide( walker, &remote_oid ) != GIT_OK ) )
{
git_revwalk_free( walker );
return false;
}
git_oid oid;
// If we can't walk to the next commit, then we are at or behind the remote
if( git_revwalk_next( &oid, walker ) != GIT_OK )
{
git_revwalk_free( walker );
return false;
}
git_revwalk_free( walker );
return true;
}
bool KIGIT_COMMON::HasPushAndPullRemote() const
{
git_remote* remote = nullptr;
if( git_remote_lookup( &remote, m_repo, "origin" ) != GIT_OK )
{
return false;
}
// Get the URLs associated with the remote
const char* fetch_url = git_remote_url( remote );
const char* push_url = git_remote_pushurl( remote );
// If no push URL is set, libgit2 defaults to using the fetch URL for pushing
if( !push_url )
{
push_url = fetch_url;
}
// Clean up the remote object
git_remote_free( remote );
// Check if both URLs are valid (i.e., not NULL)
return fetch_url && push_url;
}
extern "C" int fetchhead_foreach_cb( const char*, const char*,
const git_oid* aOID, unsigned int aIsMerge, void* aPayload )
{
if( aIsMerge )
git_oid_cpy( (git_oid*) aPayload, aOID );
return 0;
}
extern "C" void clone_progress_cb( const char* aStr, size_t aLen, size_t aTotal, void* data )
{
KIGIT_COMMON* parent = (KIGIT_COMMON*) data;
wxString progressMessage( aStr );
parent->UpdateProgress( aLen, aTotal, progressMessage );
}
extern "C" int progress_cb( const char* str, int len, void* data )
{
KIGIT_COMMON* parent = (KIGIT_COMMON*) data;
wxString progressMessage( str, len );
parent->UpdateProgress( 0, 0, progressMessage );
return 0;
}
extern "C" int transfer_progress_cb( const git_transfer_progress* aStats, void* aPayload )
{
KIGIT_COMMON* parent = (KIGIT_COMMON*) aPayload;
wxString progressMessage = wxString::Format( _( "Received %u of %u objects" ),
aStats->received_objects, aStats->total_objects );
parent->UpdateProgress( aStats->received_objects, aStats->total_objects, progressMessage );
return 0;
}
extern "C" int update_cb( const char* aRefname, const git_oid* aFirst, const git_oid* aSecond,
void* aPayload )
{
constexpr int cstring_len = 8;
char a_str[cstring_len + 1];
char b_str[cstring_len + 1];
KIGIT_COMMON* parent = (KIGIT_COMMON*) aPayload;
wxString status;
git_oid_tostr( b_str, cstring_len, aSecond );
if( !git_oid_is_zero( aFirst ) )
{
git_oid_tostr( a_str, cstring_len, aFirst );
status = wxString::Format( _( "* [updated] %s..%s %s" ), a_str, b_str, aRefname );
}
else
{
status = wxString::Format( _( "* [new] %s %s" ), b_str, aRefname );
}
parent->UpdateProgress( 0, 0, status );
return 0;
}
extern "C" int push_transfer_progress_cb( unsigned int aCurrent, unsigned int aTotal, size_t aBytes,
void* aPayload )
{
int64_t progress = 100;
KIGIT_COMMON* parent = (KIGIT_COMMON*) aPayload;
if( aTotal != 0 )
{
progress = ( aCurrent * 100 ) / aTotal;
}
wxString progressMessage = wxString::Format( _( "Writing objects: %d%% (%d/%d), %d bytes" ),
progress, aCurrent, aTotal, aBytes );
parent->UpdateProgress( aCurrent, aTotal, progressMessage );
return 0;
}
extern "C" int push_update_reference_cb( const char* aRefname, const char* aStatus, void* aPayload )
{
KIGIT_COMMON* parent = (KIGIT_COMMON*) aPayload;
wxString status( aStatus );
if( !status.IsEmpty() )
{
wxString statusMessage = wxString::Format( _( "* [rejected] %s (%s)" ), aRefname, aStatus );
parent->UpdateProgress( 0, 0, statusMessage );
}
else
{
wxString statusMessage = wxString::Format( _( "[updated] %s" ), aRefname );
parent->UpdateProgress( 0, 0, statusMessage );
}
return 0;
}
extern "C" int credentials_cb( git_cred** aOut, const char* aUrl, const char* aUsername,
unsigned int aAllowedTypes, void* aPayload )
{
KIGIT_COMMON* parent = static_cast<KIGIT_COMMON*>( aPayload );
if( parent->GetConnType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_LOCAL )
return GIT_PASSTHROUGH;
if( aAllowedTypes & GIT_CREDENTIAL_USERNAME
&& !( parent->TestedTypes() & GIT_CREDTYPE_USERNAME ) )
{
wxString username = parent->GetUsername().Trim().Trim( false );
git_cred_username_new( aOut, username.ToStdString().c_str() );
parent->TestedTypes() |= GIT_CREDTYPE_USERNAME;
}
else if( parent->GetConnType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_HTTPS
&& ( aAllowedTypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT )
&& !( parent->TestedTypes() & GIT_CREDTYPE_USERPASS_PLAINTEXT ) )
{
wxString username = parent->GetUsername().Trim().Trim( false );
wxString password = parent->GetPassword().Trim().Trim( false );
git_cred_userpass_plaintext_new( aOut, username.ToStdString().c_str(),
password.ToStdString().c_str() );
parent->TestedTypes() |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
}
else if( parent->GetConnType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_SSH
&& ( aAllowedTypes & GIT_CREDENTIAL_SSH_KEY )
&& !( parent->TestedTypes() & GIT_CREDTYPE_SSH_KEY ) )
{
// SSH key authentication
wxString sshKey = parent->GetSSHKey();
wxString sshPubKey = sshKey + ".pub";
wxString username = parent->GetUsername().Trim().Trim( false );
wxString password = parent->GetPassword().Trim().Trim( false );
git_cred_ssh_key_new( aOut, username.ToStdString().c_str(),
sshPubKey.ToStdString().c_str(),
sshKey.ToStdString().c_str(),
password.ToStdString().c_str() );
parent->TestedTypes() |= GIT_CREDTYPE_SSH_KEY;
}
else
{
return GIT_PASSTHROUGH;
}
return GIT_OK;
};

View File

@ -0,0 +1,145 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef _GIT_COMMON_H_
#define _GIT_COMMON_H_
#include <git/kicad_git_errors.h>
#include <git2.h>
#include <set>
#include <wx/string.h>
class KIGIT_COMMON : public KIGIT_ERRORS
{
public:
KIGIT_COMMON( git_repository* aRepo );
~KIGIT_COMMON();
git_repository* GetRepo() const;
void SetRepo( git_repository* aRepo )
{
m_repo = aRepo;
}
wxString GetCurrentBranchName() const;
std::vector<wxString> GetBranchNames() const;
virtual void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) {};
/**
* Return a vector of project files in the repository. Sorted by the depth of
* the project file in the directory tree
*
* @return std::vector<wxString> of project files
*/
std::vector<wxString> GetProjectDirs();
/**
* Return a pair of sets of files that differ locally from the remote repository
* The first set is files that have been committed locally but not pushed
* The second set is files that have been committed remotely but not pulled
*/
std::pair<std::set<wxString>,std::set<wxString>> GetDifferentFiles() const;
enum class GIT_STATUS
{
GIT_STATUS_UNTRACKED,
GIT_STATUS_CURRENT,
GIT_STATUS_MODIFIED, // File changed but not committed to local repository
GIT_STATUS_ADDED,
GIT_STATUS_DELETED,
GIT_STATUS_BEHIND, // File changed in remote repository but not in local
GIT_STATUS_AHEAD, // File changed in local repository but not in remote
GIT_STATUS_CONFLICTED,
GIT_STATUS_LAST
};
enum class GIT_CONN_TYPE
{
GIT_CONN_HTTPS = 0,
GIT_CONN_SSH,
GIT_CONN_LOCAL,
GIT_CONN_LAST
};
wxString GetUsername() const { return m_username; }
wxString GetPassword() const { return m_password; }
wxString GetSSHKey() const { return m_sshKey; }
GIT_CONN_TYPE GetConnType() const { return m_connType; }
void SetUsername( const wxString& aUsername ) { m_username = aUsername; }
void SetPassword( const wxString& aPassword ) { m_password = aPassword; }
void SetSSHKey( const wxString& aSSHKey ) { m_sshKey = aSSHKey; }
void SetConnType( GIT_CONN_TYPE aConnType ) { m_connType = aConnType; }
void SetConnType( unsigned aConnType )
{
if( aConnType < static_cast<unsigned>( GIT_CONN_TYPE::GIT_CONN_LAST ) )
m_connType = static_cast<GIT_CONN_TYPE>( aConnType );
}
// Holds a temporary variable that can be used by the authentication callback
// to remember which types of authentication have been tested so that we
// don't loop forever.
unsigned& TestedTypes() { return m_testedTypes; }
// Returns true if the repository has local commits that have not been pushed
bool HasLocalCommits() const;
// Returns true if the repository has a remote that can be pushed to pulled from
bool HasPushAndPullRemote() const;
protected:
git_repository* m_repo;
GIT_CONN_TYPE m_connType;
wxString m_username;
wxString m_password;
wxString m_sshKey;
unsigned m_testedTypes;
};
extern "C" int progress_cb( const char* str, int len, void* data );
extern "C" void clone_progress_cb( const char* str, size_t len, size_t total, void* data );
extern "C" int transfer_progress_cb( const git_transfer_progress* aStats, void* aPayload );
extern "C" int update_cb( const char* aRefname, const git_oid* aFirst, const git_oid* aSecond,
void* aPayload );
extern "C" int push_transfer_progress_cb( unsigned int aCurrent, unsigned int aTotal,
size_t aBytes, void* aPayload );
extern "C" int push_update_reference_cb( const char* aRefname, const char* aStatus,
void* aPayload );
extern "C" int fetchhead_foreach_cb( const char*, const char*,
const git_oid* aOID, unsigned int aIsMerge, void* aPayload );
extern "C" int credentials_cb( git_cred** aOut, const char* aUrl, const char* aUsername,
unsigned int aAllowedTypes, void* aPayload );
#endif // _GIT_COMMON_H_

View File

@ -0,0 +1,66 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
namespace KIGIT_ERROR
{
#undef _
#define _(a) a
// General errors
const char* const kInvalidRepository = _("Invalid repository.");
const char* const kCommitFailed = _("Failed to commit changes.");
const char* const kMergeFailed = _("Failed to merge branches.");
// Clone errors
const char* const kCloneFailed = _("Failed to clone repository.");
const char* const kRemoteNotFound = _("Remote repository not found.");
const char* const kAuthenticationFailed = _("Authentication failed for remote repository.");
// Branch errors
const char* const kBranchNotFound = _("Branch not found.");
const char* const kBranchCreationFailed = _("Failed to create branch.");
const char* const kBranchDeletionFailed = _("Failed to delete branch.");
// Checkout errors
const char* const kCheckoutFailed = _("Failed to perform checkout operation.");
const char* const kFileNotFoundInCheckout = _("File not found during checkout operation.");
// Conflict errors
const char* const kMergeConflict = _("Merge conflict encountered.");
const char* const kRebaseConflict = _("Rebase conflict encountered.");
// Pull/Push errors
const char* const kPullFailed = _("Failed to pull changes from remote repository.");
const char* const kPushFailed = _("Failed to push changes to remote repository.");
const char* const kNoUpstreamBranch = _("No upstream branch configured.");
const char* const kRemoteConnectionError = _("Failed to establish connection with remote repository.");
// Tag errors
const char* const kTagNotFound = _("Tag not found.");
const char* const kTagCreationFailed = _("Failed to create tag.");
const char* const kTagDeletionFailed = _("Failed to delete tag.");
const char* const kUnknownError = _("Unknown error.");
const char* const kNoError = _("No error.");
}

View File

@ -0,0 +1,81 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef KICAD_GIT_ERRORS_H
#define KICAD_GIT_ERRORS_H
#include <vector>
#include <wx/translation.h>
class KIGIT_ERRORS
{
public:
KIGIT_ERRORS() = default;
const std::vector<wxString>& GetErrorStrings() const
{
return m_errorStrings;
}
const wxString& PeekErrorString() const
{
if( m_errorStrings.empty() )
return _( "No error" );
else
return m_errorStrings.back();
}
wxString GetErrorString()
{
if( m_errorStrings.empty() )
return _( "No error" );
const wxString errorString( m_errorStrings.back() );
m_errorStrings.pop_back();
return errorString;
}
void AddErrorString( const wxString aErrorString )
{
m_errorStrings.emplace_back( aErrorString );
}
void AddErrorString( const std::string aErrorString )
{
m_errorStrings.emplace_back( aErrorString );
}
void ClearErrorStrings()
{
m_errorStrings.clear();
}
private:
std::vector<wxString> m_errorStrings;
};
#endif // KICAD_GIT_ERRORS_H

View File

@ -177,6 +177,18 @@ PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( PROJECT* aProject, const wxStrin
ZONE_DISPLAY_MODE::SHOW_FILLED, ZONE_DISPLAY_MODE::SHOW_FILLED,
ZONE_DISPLAY_MODE::SHOW_TRIANGULATION ) );
m_params.emplace_back( new PARAM<wxString>( "git.repo_username",
&m_GitRepoUsername, "" ) );
m_params.emplace_back( new PARAM<wxString>( "git.repo_password",
&m_GitRepoPassword, "" ) );
m_params.emplace_back( new PARAM<wxString>( "git.repo_type",
&m_GitRepoType, "" ) );
m_params.emplace_back( new PARAM<wxString>( "git.ssh_key",
&m_GitSSHKey, "" ) );
m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "project.files",
[&]() -> nlohmann::json
{

View File

@ -347,6 +347,61 @@ COMMON_SETTINGS::COMMON_SETTINGS() :
m_params.emplace_back( new PARAM<int>( "package_manager.sash_pos",
&m_PackageManager.sash_pos, 380 ) );
m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "git.repositories",
[&]() -> nlohmann::json
{
nlohmann::json ret = {};
for( const GIT_REPOSITORY& repo : m_Git.repositories )
{
nlohmann::json repoJson = {};
repoJson["name"] = repo.name;
repoJson["path"] = repo.path;
repoJson["authType"] = repo.authType;
repoJson["username"] = repo.username;
repoJson["ssh_path"] = repo.ssh_path;
repoJson["active"] = repo.active;
ret.push_back( repoJson );
}
return ret;
},
[&]( const nlohmann::json& aJson )
{
if( !aJson.is_array() )
return;
m_Git.repositories.clear();
for( const auto& repoJson : aJson )
{
GIT_REPOSITORY repo;
repo.name = repoJson["name"].get<wxString>();
repo.path = repoJson["path"].get<wxString>();
repo.authType = repoJson["authType"].get<wxString>();
repo.username = repoJson["username"].get<wxString>();
repo.ssh_path = repoJson["ssh_path"].get<wxString>();
repo.active = repoJson["active"].get<bool>();
m_Git.repositories.push_back( repo );
}
},
{} ) );
m_params.emplace_back( new PARAM<wxString>( "git.authorName",
&m_Git.authorName, wxS( "" ) ) );
m_params.emplace_back( new PARAM<wxString>( "git.authorEmail",
&m_Git.authorEmail, wxS( "" ) ) );
m_params.emplace_back( new PARAM<bool>( "git.useDefaultAuthor",
&m_Git.useDefaultAuthor, true ) );
registerMigration( 0, 1, std::bind( &COMMON_SETTINGS::migrateSchema0to1, this ) );
registerMigration( 1, 2, std::bind( &COMMON_SETTINGS::migrateSchema1to2, this ) );
registerMigration( 2, 3, std::bind( &COMMON_SETTINGS::migrateSchema2to3, this ) );

View File

@ -52,6 +52,8 @@
#include <kiplatform/app.h>
#include <kiplatform/environment.h>
#include <git2.h>
#ifdef KICAD_USE_SENTRY
#include <sentry.h>
#endif
@ -73,6 +75,7 @@ static struct PGM_SINGLE_TOP : public PGM_BASE
void OnPgmExit()
{
Kiway.OnKiwayEnd();
if( m_settings_manager && m_settings_manager->IsOK() )
@ -84,6 +87,7 @@ static struct PGM_SINGLE_TOP : public PGM_BASE
// Destroy everything in PGM_BASE, especially wxSingleInstanceCheckerImpl
// earlier than wxApp and earlier than static destruction would.
PGM_BASE::Destroy();
git_libgit2_shutdown();
}
void MacOpenFile( const wxString& aFileName ) override
@ -298,6 +302,9 @@ bool PGM_SINGLE_TOP::OnPgmInit()
}
#endif
// Initialize the git library before trying to initialize individual programs
git_libgit2_init();
// Not all kicad applications use the python stuff. skip python init
// for these apps.
bool skip_python_initialization = false;

View File

@ -589,6 +589,58 @@ bool LIB_FIELD::IsMandatory() const
}
bool LIB_FIELD::operator==( const LIB_ITEM& aItem ) const
{
if( aItem.Type() != LIB_FIELD_T )
return false;
const LIB_FIELD& field = static_cast<const LIB_FIELD&>( aItem );
if( m_id != field.m_id )
return false;
if( m_name != field.m_name )
return false;
if( m_parent->m_Uuid != aItem.GetParent()->m_Uuid )
return false;
if( m_id < MANDATORY_FIELDS )
return true;
if( m_Uuid == field.m_Uuid )
return true;
return EDA_TEXT::operator==( field );
}
double LIB_FIELD::Similarity( const LIB_ITEM& aItem ) const
{
if( aItem.Type() != LIB_FIELD_T )
return 0.0;
const LIB_FIELD& field = static_cast<const LIB_FIELD&>( aItem );
if( m_id != field.m_id && m_id < MANDATORY_FIELDS )
return 0.0;
if( m_parent->m_Uuid != aItem.GetParent()->m_Uuid )
return 0.0;
if( m_id < MANDATORY_FIELDS )
return 1.0;
if( m_Uuid == field.m_Uuid )
return 1.0;
double similarity = SimilarityBase( field );
similarity *= EDA_TEXT::Similarity( field );
return similarity;
}
static struct LIB_FIELD_DESC
{
LIB_FIELD_DESC()

View File

@ -198,6 +198,10 @@ public:
bool ShowInChooser() const { return m_showInChooser; }
void SetShowInChooser( bool aShow = true ) { m_showInChooser = aShow; }
double Similarity( const LIB_ITEM& aItem ) const override;
bool operator==( const LIB_ITEM& aItem ) const override;
private:
/**

View File

@ -168,5 +168,3 @@ void LIB_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const
aLayers[1] = LAYER_DEVICE_BACKGROUND;
aLayers[2] = LAYER_SELECTION_SHADOWS;
}

View File

@ -204,6 +204,35 @@ public:
return (LIB_SYMBOL*) m_parent;
}
/**
* Return a measure of how likely the other object is to represent the same
* object. The scale runs from 0.0 (definitely different objects) to 1.0 (same)
*
* This is a pure virtual function. Derived classes must implement this.
*/
virtual double Similarity( const LIB_ITEM& aItem ) const = 0;
/**
* Calculate the boilerplate similarity for all LIB_ITEMs without
* preventing the use above of a pure virtual function that catches at compile
* time when a new object has not been fully implemented
*/
double SimilarityBase( const LIB_ITEM& aItem ) const
{
double similarity = 1.0;
if( m_unit != aItem.m_unit )
similarity *= 0.9;
if( m_convert != aItem.m_convert )
similarity *= 0.9;
if( m_private != aItem.m_private )
similarity *= 0.9;
return similarity;
}
void ViewGetLayers( int aLayers[], int& aCount ) const override;
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override
@ -237,7 +266,7 @@ public:
* @param aOther Object to test against.
* @return True if object is identical to this object.
*/
bool operator==( const LIB_ITEM& aOther ) const;
virtual bool operator==( const LIB_ITEM& aOther ) const;
bool operator==( const LIB_ITEM* aOther ) const
{
return *this == *aOther;

View File

@ -1453,6 +1453,123 @@ wxString LIB_PIN::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
}
bool LIB_PIN::operator==( const LIB_ITEM& aOther ) const
{
if( aOther.Type() != LIB_PIN_T )
return false;
const LIB_PIN* other = static_cast<const LIB_PIN*>( &aOther );
if( m_name != other->m_name )
return false;
if( m_number != other->m_number )
return false;
if( m_position != other->m_position )
return false;
if( m_length != other->m_length )
return false;
if( m_orientation != other->m_orientation )
return false;
if( m_shape != other->m_shape )
return false;
if( m_type != other->m_type )
return false;
if( m_attributes != other->m_attributes )
return false;
if( m_numTextSize != other->m_numTextSize )
return false;
if( m_nameTextSize != other->m_nameTextSize )
return false;
if( m_alternates.size() != other->m_alternates.size() )
return false;
auto lhsItem = m_alternates.begin();
auto rhsItem = other->m_alternates.begin();
while( lhsItem != m_alternates.end() )
{
if( rhsItem == other->m_alternates.end() )
return false;
const ALT& lhsAlt = lhsItem->second;
const ALT& rhsAlt = rhsItem->second;
if( lhsAlt.m_Name != rhsAlt.m_Name )
return false;
if( lhsAlt.m_Type != rhsAlt.m_Type )
return false;
if( lhsAlt.m_Shape != rhsAlt.m_Shape )
return false;
++lhsItem;
++rhsItem;
}
return rhsItem == other->m_alternates.end();
}
double LIB_PIN::Similarity( const LIB_ITEM& aOther ) const
{
if( aOther.m_Uuid == m_Uuid )
return 1.0;
if( aOther.Type() != LIB_PIN_T )
return 0.0;
const LIB_PIN* other = static_cast<const LIB_PIN*>( &aOther );
double similarity = SimilarityBase( aOther );
if( m_name != other->m_name )
similarity *= 0.9;
if( m_number != other->m_number )
similarity *= 0.9;
if( m_position != other->m_position )
similarity *= 0.9;
if( m_length != other->m_length )
similarity *= 0.9;
if( m_orientation != other->m_orientation )
similarity *= 0.9;
if( m_shape != other->m_shape )
similarity *= 0.9;
if( m_type != other->m_type )
similarity *= 0.9;
if( m_attributes != other->m_attributes )
similarity *= 0.9;
if( m_numTextSize != other->m_numTextSize )
similarity *= 0.9;
if( m_nameTextSize != other->m_nameTextSize )
similarity *= 0.9;
if( m_alternates.size() != other->m_alternates.size() )
similarity *= 0.9;
return similarity;
}
std::ostream& LIB_PIN::operator<<( std::ostream& aStream )
{
aStream << "LIB_PIN:" << std::endl

View File

@ -265,6 +265,13 @@ public:
*/
static const wxString GetCanonicalElectricalTypeName( ELECTRICAL_PINTYPE aType );
double Similarity( const LIB_ITEM& aItem ) const override;
bool operator==( const LIB_ITEM& aItem ) const override;
bool operator!=( const LIB_ITEM& aItem ) const { return !operator==( aItem ); }
bool operator<( const LIB_PIN& aRhs ) const { return compare( aRhs, EQUALITY ) < 0; }
bool operator>( const LIB_PIN& aRhs ) const { return compare( aRhs, EQUALITY ) > 0; }
protected:
struct EXTENTS_CACHE
{
@ -300,10 +307,6 @@ protected:
*/
void printPinElectricalTypeName( const RENDER_SETTINGS* aSettings, VECTOR2I& aPosition,
PIN_ORIENTATION aOrientation, bool aDimmed );
bool operator==( const LIB_PIN& aRhs ) const { return compare( aRhs, EQUALITY ) == 0; }
bool operator<( const LIB_PIN& aRhs ) const { return compare( aRhs, EQUALITY ) < 0; }
bool operator>( const LIB_PIN& aRhs ) const { return compare( aRhs, EQUALITY ) > 0; }
std::ostream& operator<<( std::ostream& aStream );
private:

View File

@ -518,6 +518,33 @@ void LIB_SHAPE::AddPoint( const VECTOR2I& aPosition )
}
bool LIB_SHAPE::operator==( const LIB_ITEM& aOther ) const
{
if( aOther.Type() != Type() )
return false;
const LIB_SHAPE& other = static_cast<const LIB_SHAPE&>( aOther );
return LIB_ITEM::operator==( aOther ) && EDA_SHAPE::operator==( other );
}
double LIB_SHAPE::Similarity( const LIB_ITEM& aOther ) const
{
if( m_Uuid == aOther.m_Uuid )
return 1.0;
if( aOther.Type() != Type() )
return 0.0;
const LIB_SHAPE& other = static_cast<const LIB_SHAPE&>( aOther );
double similarity = SimilarityBase( other );
similarity *= EDA_SHAPE::Similarity( other );
return similarity;
}
void LIB_SHAPE::ViewGetLayers( int aLayers[], int& aCount ) const

View File

@ -120,6 +120,10 @@ public:
void ViewGetLayers( int aLayers[], int& aCount ) const override;
double Similarity( const LIB_ITEM& aOther ) const override;
bool operator==( const LIB_ITEM& aOther ) const override;
private:
/**
* @copydoc LIB_ITEM::compare()

View File

@ -1912,3 +1912,139 @@ std::vector<struct LIB_SYMBOL_UNIT> LIB_SYMBOL::GetUniqueUnits()
return uniqueUnits;
}
bool LIB_SYMBOL::operator==( const LIB_SYMBOL& aOther ) const
{
if( m_libId != aOther.m_libId )
return false;
if( m_excludedFromBoard != aOther.m_excludedFromBoard )
return false;
if( m_excludedFromBOM != aOther.m_excludedFromBOM )
return false;
if( m_excludedFromSim != aOther.m_excludedFromSim )
return false;
if( m_flags != aOther.m_flags )
return false;
if( m_unitCount != aOther.m_unitCount )
return false;
if( m_pinNameOffset != aOther.m_pinNameOffset )
return false;
if( m_showPinNames != aOther.m_showPinNames )
return false;
if( m_showPinNumbers != aOther.m_showPinNumbers )
return false;
if( m_drawings.size() != aOther.m_drawings.size() )
return false;
for( auto it1 = m_drawings.begin(), it2 = aOther.m_drawings.begin();
it1 != m_drawings.end(); ++it1, ++it2 )
{
if( !( *it1 == *it2 ) )
return false;
}
const LIB_PINS thisPinList = GetAllLibPins();
const LIB_PINS otherPinList = aOther.GetAllLibPins();
if( thisPinList.size() != otherPinList.size() )
return false;
for( auto it1 = thisPinList.begin(), it2 = otherPinList.begin();
it1 != thisPinList.end(); ++it1, ++it2 )
{
if( !( **it1 == **it2 ) )
return false;
}
for( size_t ii = 0; ii < thisPinList.size(); ++ii )
{
if( !( *thisPinList[ii] == *otherPinList[ii] ) )
return false;
}
return true;
}
double LIB_SYMBOL::Similarity( const LIB_SYMBOL& aOther ) const
{
double similarity = 0.0;
int totalItems = 0;
if( m_Uuid == aOther.m_Uuid )
return 1.0;
for( const LIB_ITEM& item : m_drawings )
{
totalItems += 1;
double max_similarity = 0.0;
for( const LIB_ITEM& otherItem : aOther.m_drawings )
{
double temp_similarity = item.Similarity( otherItem );
max_similarity = std::max( max_similarity, temp_similarity );
if( max_similarity == 1.0 )
break;
}
similarity += max_similarity;
}
for( const LIB_PIN* pin : GetAllLibPins() )
{
totalItems += 1;
double max_similarity = 0.0;
for( const LIB_PIN* otherPin : aOther.GetAllLibPins() )
{
double temp_similarity = pin->Similarity( *otherPin );
max_similarity = std::max( max_similarity, temp_similarity );
if( max_similarity == 1.0 )
break;
}
similarity += max_similarity;
}
if( totalItems == 0 )
similarity = 0.0;
else
similarity /= totalItems;
if( m_excludedFromBoard != aOther.m_excludedFromBoard )
similarity *= 0.9;
if( m_excludedFromBOM != aOther.m_excludedFromBOM )
similarity *= 0.9;
if( m_excludedFromSim != aOther.m_excludedFromSim )
similarity *= 0.9;
if( m_flags != aOther.m_flags )
similarity *= 0.9;
if( m_unitCount != aOther.m_unitCount )
similarity *= 0.5;
if( m_pinNameOffset != aOther.m_pinNameOffset )
similarity *= 0.9;
if( m_showPinNames != aOther.m_showPinNames )
similarity *= 0.9;
if( m_showPinNumbers != aOther.m_showPinNumbers )
similarity *= 0.9;
return similarity;
}

View File

@ -711,11 +711,7 @@ public:
REPORTER* aReporter = nullptr ) const;
bool operator==( const LIB_SYMBOL* aSymbol ) const { return this == aSymbol; }
bool operator==( const LIB_SYMBOL& aSymbol ) const
{
return Compare( aSymbol, LIB_ITEM::COMPARE_FLAGS::EQUALITY ) == 0;
}
bool operator==( const LIB_SYMBOL& aSymbol ) const;
bool operator!=( const LIB_SYMBOL& aSymbol ) const
{
return Compare( aSymbol, LIB_ITEM::COMPARE_FLAGS::EQUALITY ) != 0;
@ -762,6 +758,13 @@ public:
*/
std::vector<LIB_ITEM*> GetUnitDrawItems( int aUnit, int aConvert );
/**
* Return a measure of similarity between this symbol and \a aSymbol.
* @param aSymbol is the symbol to compare to.
*
* @return a measure of similarity from 1.0 (identical) to 0.0 (no similarity).
*/
double Similarity( const LIB_SYMBOL& aSymbol ) const;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif

View File

@ -500,6 +500,33 @@ void LIB_TEXT::CalcEdit( const VECTOR2I& aPosition )
}
bool LIB_TEXT::operator==( const LIB_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
return false;
const LIB_TEXT& other = static_cast<const LIB_TEXT&>( aOther );
return LIB_ITEM::operator==( aOther ) && EDA_TEXT::operator==( other );
}
double LIB_TEXT::Similarity( const LIB_ITEM& aOther ) const
{
if( m_Uuid == aOther.m_Uuid )
return 1.0;
if( aOther.Type() != Type() )
return 0.0;
const LIB_TEXT& other = static_cast<const LIB_TEXT&>( aOther );
double similarity = SimilarityBase( other );
similarity *= EDA_TEXT::Similarity( other );
return similarity;
}
static struct LIB_TEXT_DESC
{
LIB_TEXT_DESC()

View File

@ -113,6 +113,10 @@ public:
EDA_ITEM* Clone() const override;
double Similarity( const LIB_ITEM& aOther ) const override;
bool operator==( const LIB_ITEM& aOther ) const override;
private:
/**
* @copydoc LIB_ITEM::compare()

View File

@ -501,6 +501,35 @@ void LIB_TEXTBOX::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL
}
bool LIB_TEXTBOX::operator==( const LIB_ITEM& aOther ) const
{
if( aOther.Type() != LIB_TEXTBOX_T )
return false;
const LIB_TEXTBOX& other = static_cast<const LIB_TEXTBOX&>( aOther );
return LIB_SHAPE::operator==( other ) && EDA_TEXT::operator==( other );
}
double LIB_TEXTBOX::Similarity( const LIB_ITEM& aOther ) const
{
if( m_Uuid == aOther.m_Uuid )
return 1.0;
if( aOther.Type() != LIB_TEXTBOX_T )
return 0.0;
const LIB_TEXTBOX& other = static_cast<const LIB_TEXTBOX&>( aOther );
double similarity = SimilarityBase( other );
similarity *= LIB_SHAPE::Similarity( other );
similarity *= EDA_TEXT::Similarity( other );
return similarity;
}
void LIB_TEXTBOX::ViewGetLayers( int aLayers[], int& aCount ) const
{
aCount = 3;

View File

@ -93,6 +93,10 @@ public:
void ViewGetLayers( int aLayers[], int& aCount ) const override;
double Similarity( const LIB_ITEM& aOther ) const override;
bool operator==( const LIB_ITEM& aOther ) const override;
protected:
KIFONT::FONT* getDrawFont() const override;

View File

@ -249,3 +249,45 @@ static struct SCH_BITMAP_DESC
propMgr.InheritsAfter( TYPE_HASH( SCH_BITMAP ), TYPE_HASH( SCH_ITEM ) );
}
} _SCH_BITMAP_DESC;
bool SCH_BITMAP::operator==( const SCH_ITEM& aItem ) const
{
if( Type() != aItem.Type() )
return false;
const SCH_BITMAP* bitmap = static_cast<const SCH_BITMAP*>( &aItem );
if( GetPosition() != bitmap->GetPosition() )
return false;
if( GetSize() != bitmap->GetSize() )
return false;
if( GetImage() != bitmap->GetImage() )
return false;
return true;
}
double SCH_BITMAP::Similarity( const SCH_ITEM& aItem ) const
{
if( Type() != aItem.Type() )
return 0.0;
if( m_Uuid == aItem.m_Uuid )
return 1.0;
const SCH_BITMAP* bitmap = static_cast<const SCH_BITMAP*>( &aItem );
if( GetImage() != bitmap->GetImage() )
return 0.0;
// If it is the same image but a different UUID and a different size,
// then it _might be different_.
if( GetSize() != bitmap->GetSize() )
return 0.5;
return 1.0;
}

View File

@ -154,6 +154,10 @@ public:
EDA_ITEM* Clone() const override;
double Similarity( const SCH_ITEM& aOther ) const override;
bool operator==( const SCH_ITEM& aOther ) const override;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override;
#endif

View File

@ -577,6 +577,45 @@ bool SCH_BUS_WIRE_ENTRY::ConnectionPropagatesTo( const EDA_ITEM* aItem ) const
return true;
}
bool SCH_BUS_ENTRY_BASE::operator==( const SCH_ITEM& aItem ) const
{
if( Type() != aItem.Type() )
return false;
const SCH_BUS_ENTRY_BASE* symbol = static_cast<const SCH_BUS_ENTRY_BASE*>( &aItem );
if( GetLayer() != symbol->GetLayer() )
return false;
if( GetPosition() != symbol->GetPosition() )
return false;
if( GetEnd() != symbol->GetEnd() )
return false;
return true;
}
double SCH_BUS_ENTRY_BASE::Similarity( const SCH_ITEM& aItem ) const
{
if( aItem.Type() != Type() )
return 0.0;
if( m_Uuid == aItem.m_Uuid )
return 1.0;
const SCH_BUS_ENTRY_BASE& other = static_cast<const SCH_BUS_ENTRY_BASE&>( aItem );
if( GetLayer() != other.GetLayer() )
return 0.0;
if( GetPosition() != other.GetPosition() )
return 0.0;
return 1.0;
}
static struct SCH_BUS_ENTRY_DESC
{

View File

@ -128,6 +128,10 @@ public:
bool operator <( const SCH_ITEM& aItem ) const override;
double Similarity( const SCH_ITEM& aItem ) const override;
bool operator==( const SCH_ITEM& aItem ) const override;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif

View File

@ -1294,6 +1294,74 @@ bool SCH_FIELD::operator <( const SCH_ITEM& aItem ) const
return GetName() < field->GetName();
}
bool SCH_FIELD::operator==( const SCH_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
return false;
const SCH_FIELD& field = static_cast<const SCH_FIELD&>( aOther );
if( GetId() != field.GetId() )
return false;
if( GetPosition() != field.GetPosition() )
return false;
if( IsNamedVariable() != field.IsNamedVariable() )
return false;
if( IsNameShown() != field.IsNameShown() )
return false;
if( CanAutoplace() != field.CanAutoplace() )
return false;
if( GetText() != field.GetText() )
return false;
return true;
}
double SCH_FIELD::Similarity( const SCH_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
return 0.0;
if( m_Uuid == aOther.m_Uuid )
return 1.0;
const SCH_FIELD& field = static_cast<const SCH_FIELD&>( aOther );
double retval = 0.99; // The UUIDs are different, so we start with non-identity
if( GetId() != field.GetId() )
{
// We don't allow swapping of mandatory fields, so these cannot be the same item
if( GetId() < MANDATORY_FIELDS || field.GetId() < MANDATORY_FIELDS )
return 0.0;
else
retval *= 0.5;
}
if( GetPosition() != field.GetPosition() )
retval *= 0.5;
if( IsNamedVariable() != field.IsNamedVariable() )
retval *= 0.5;
if( IsNameShown() != field.IsNameShown() )
retval *= 0.5;
if( CanAutoplace() != field.CanAutoplace() )
retval *= 0.5;
if( GetText() != field.GetText() )
retval *= Levenshtein( field );
return 1.0;
}
static struct SCH_FIELD_DESC
{

View File

@ -275,6 +275,10 @@ public:
bool operator <( const SCH_ITEM& aItem ) const override;
double Similarity( const SCH_ITEM& aItem ) const override;
bool operator==( const SCH_ITEM& aItem ) const override;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif

View File

@ -279,6 +279,15 @@ public:
bool RenderAsBitmap( double aWorldScale ) const override;
/**
* Return a measure of how likely the other object is to represent the same
* object. The scale runs from 0.0 (definitely different objects) to 1.0 (same)
*
* This is a pure virtual function. Derived classes must implement this.
*/
virtual double Similarity( const SCH_ITEM& aItem ) const = 0;
virtual bool operator==( const SCH_ITEM& aItem ) const = 0;
/**
* Print a schematic item.
*

View File

@ -307,6 +307,51 @@ void SCH_JUNCTION::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANE
}
bool SCH_JUNCTION::operator==( const SCH_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
return false;
const SCH_JUNCTION& other = static_cast<const SCH_JUNCTION&>( aOther );
if( m_pos != other.m_pos )
return false;
if( m_diameter != other.m_diameter )
return false;
if( m_color != other.m_color )
return false;
return true;
}
double SCH_JUNCTION::Similarity( const SCH_ITEM& aOther ) const
{
if( m_Uuid == aOther.m_Uuid )
return 1.0;
if( aOther.Type() != Type() )
return 0.0;
const SCH_JUNCTION& other = static_cast<const SCH_JUNCTION&>( aOther );
double similarity = 1.0;
if( m_pos != other.m_pos )
similarity *= 0.9;
if( m_diameter != other.m_diameter )
similarity *= 0.9;
if( m_color != other.m_color )
similarity *= 0.9;
return similarity;
}
static struct SCH_JUNCTION_DESC
{
SCH_JUNCTION_DESC()

View File

@ -129,6 +129,10 @@ public:
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
double Similarity( const SCH_ITEM& aOther ) const override;
bool operator==( const SCH_ITEM& aOther ) const override;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override;
#endif

View File

@ -579,6 +579,69 @@ bool SCH_LABEL_BASE::IncrementLabel( int aIncrement )
}
bool SCH_LABEL_BASE::operator==( const SCH_ITEM& aOther ) const
{
const SCH_LABEL_BASE* other = dynamic_cast<const SCH_LABEL_BASE*>( &aOther );
if( !other )
return false;
if( m_shape != other->m_shape )
return false;
if( m_connectionType != other->m_connectionType )
return false;
if( m_fields.size() != other->m_fields.size() )
return false;
for( size_t ii = 0; ii < m_fields.size(); ++ii )
{
if( !( m_fields[ii] == other->m_fields[ii] ) )
return false;
}
return SCH_TEXT::operator==( aOther );
}
double SCH_LABEL_BASE::Similarity( const SCH_ITEM& aOther ) const
{
const SCH_LABEL_BASE* other = dynamic_cast<const SCH_LABEL_BASE*>( &aOther );
if( !other )
return 0.0;
if( m_Uuid == other->m_Uuid )
return 1.0;
double similarity = SCH_TEXT::Similarity( aOther );
if( typeid( *this ) != typeid( aOther ) )
similarity *= 0.9;
if( m_shape == other->m_shape )
similarity *= 0.9;
if( m_connectionType == other->m_connectionType )
similarity *= 0.9;
for( size_t ii = 0; ii < m_fields.size(); ++ii )
{
if( ii >= other->m_fields.size() )
break;
similarity *= m_fields[ii].Similarity( other->m_fields[ii] );
}
int diff = std::abs( int( m_fields.size() ) - int( other->m_fields.size() ) );
similarity *= std::pow( 0.9, diff );
return similarity;
}
void SCH_LABEL_BASE::AutoplaceFields( SCH_SCREEN* aScreen, bool aManual )
{
int margin = GetTextOffset() * 2;

View File

@ -311,6 +311,10 @@ public:
*/
virtual bool AutoRotateOnPlacementSupported() const = 0;
double Similarity( const SCH_ITEM& aItem ) const override;
bool operator==( const SCH_ITEM& aItem ) const override;
protected:
void cacheShownText() override;

View File

@ -978,6 +978,69 @@ bool SCH_LINE::IsBus() const
}
bool SCH_LINE::operator==( const SCH_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
return false;
const SCH_LINE& other = static_cast<const SCH_LINE&>( aOther );
if( GetLayer() != other.GetLayer() )
return false;
if( m_start != other.m_start )
return false;
if( m_end != other.m_end )
return false;
if( m_stroke.GetWidth() != other.m_stroke.GetWidth() )
return false;
if( m_stroke.GetColor() != other.m_stroke.GetColor() )
return false;
if( m_stroke.GetPlotStyle() != other.m_stroke.GetPlotStyle() )
return false;
return true;
}
double SCH_LINE::Similarity( const SCH_ITEM& aOther ) const
{
if( m_Uuid == aOther.m_Uuid )
return 1.0;
if( Type() != aOther.Type() )
return 0.0;
const SCH_LINE& other = static_cast<const SCH_LINE&>( aOther );
if( GetLayer() != other.GetLayer() )
return 0.0;
double similarity = 1.0;
if( m_start != other.m_start )
similarity *= 0.9;
if( m_end != other.m_end )
similarity *= 0.9;
if( m_stroke.GetWidth() != other.m_stroke.GetWidth() )
similarity *= 0.9;
if( m_stroke.GetColor() != other.m_stroke.GetColor() )
similarity *= 0.9;
if( m_stroke.GetPlotStyle() != other.m_stroke.GetPlotStyle() )
similarity *= 0.9;
return similarity;
}
static struct SCH_LINE_DESC
{
SCH_LINE_DESC()

View File

@ -326,6 +326,10 @@ public:
*/
bool IsBus() const;
double Similarity( const SCH_ITEM& aOther ) const override;
bool operator==( const SCH_ITEM& aOther ) const override;
private:
/**
* @brief Recursively called function to travel through the connected wires and find a connected

View File

@ -120,6 +120,16 @@ public:
*/
bool IsLegacyMarker() const { return m_isLegacyMarker; }
double Similarity( const SCH_ITEM& aOther ) const override
{
return 0.0;
}
bool operator==( const SCH_ITEM& aOther ) const override
{
return false;
}
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override;
#endif

View File

@ -195,3 +195,34 @@ BITMAPS SCH_NO_CONNECT::GetMenuImage() const
{
return BITMAPS::noconn;
}
bool SCH_NO_CONNECT::operator==( const SCH_ITEM& aOther ) const
{
if( aOther.Type() != Type() )
return false;
const SCH_NO_CONNECT* other = static_cast<const SCH_NO_CONNECT*>( &aOther );
if( m_pos != other->m_pos )
return false;
return true;
}
double SCH_NO_CONNECT::Similarity( const SCH_ITEM& aOther ) const
{
if( m_Uuid == aOther.m_Uuid )
return 1.0;
if( aOther.Type() != Type() )
return 0.0;
const SCH_NO_CONNECT* other = static_cast<const SCH_NO_CONNECT*>( &aOther );
if( m_pos != other->m_pos )
return 0.0;
return 1.0;
}

View File

@ -109,6 +109,10 @@ public:
EDA_ITEM* Clone() const override;
double Similarity( const SCH_ITEM& aOther ) const override;
bool operator==( const SCH_ITEM& aOther ) const override;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif

View File

@ -416,6 +416,43 @@ bool SCH_PIN::ConnectionPropagatesTo( const EDA_ITEM* aItem ) const
}
bool SCH_PIN::operator==( const SCH_ITEM& aOther ) const
{
if( aOther.Type() != SCH_PIN_T )
return false;
const SCH_PIN& other = static_cast<const SCH_PIN&>( aOther );
if( m_number != other.m_number )
return false;
if( m_position != other.m_position )
return false;
return m_libPin == other.m_libPin;
}
double SCH_PIN::Similarity( const SCH_ITEM& aOther ) const
{
if( m_Uuid == aOther.m_Uuid )
return 1.0;
if( aOther.Type() != SCH_PIN_T )
return 0.0;
const SCH_PIN& other = static_cast<const SCH_PIN&>( aOther );
if( m_number != other.m_number )
return 0.0;
if( m_position != other.m_position )
return 0.0;
return m_libPin->Similarity( *other.m_libPin );
}
static struct SCH_PIN_DESC
{
SCH_PIN_DESC()

View File

@ -164,6 +164,10 @@ public:
const wxString& GetOperatingPoint() const { return m_operatingPoint; }
void SetOperatingPoint( const wxString& aText ) { m_operatingPoint = aText; }
double Similarity( const SCH_ITEM& aItem ) const override;
bool operator==( const SCH_ITEM& aItem ) const override;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override {}
#endif

View File

@ -502,6 +502,30 @@ void SCH_SHAPE::AddPoint( const VECTOR2I& aPosition )
}
bool SCH_SHAPE::operator==( const SCH_ITEM& aOther ) const
{
if( aOther.Type() != Type() )
return false;
const SCH_SHAPE& other = static_cast<const SCH_SHAPE&>( aOther );
return EDA_SHAPE::operator==( other );
}
double SCH_SHAPE::Similarity( const SCH_ITEM& aOther ) const
{
if( aOther.Type() != Type() )
return 0.0;
const SCH_SHAPE& other = static_cast<const SCH_SHAPE&>( aOther );
double similarity = EDA_SHAPE::Similarity( other );
return similarity;
}
static struct SCH_SHAPE_DESC
{
SCH_SHAPE_DESC()

View File

@ -107,6 +107,10 @@ public:
void ViewGetLayers( int aLayers[], int& aCount ) const override;
double Similarity( const SCH_ITEM& aOther ) const override;
bool operator==( const SCH_ITEM& aOther ) const override;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif

View File

@ -1387,6 +1387,55 @@ int SCH_SHEET::ComparePageNum( const wxString& aPageNumberA, const wxString& aPa
}
bool SCH_SHEET::operator==( const SCH_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
return false;
const SCH_SHEET* other = static_cast<const SCH_SHEET*>( &aOther );
if( m_pos != other->m_pos )
return false;
if( m_size != other->m_size )
return false;
if( GetBorderColor() != other->GetBorderColor() )
return false;
if( GetBackgroundColor() != other->GetBackgroundColor() )
return false;
if( GetBorderWidth() != other->GetBorderWidth() )
return false;
if( GetFields().size() != other->GetFields().size() )
return false;
for( size_t i = 0; i < GetFields().size(); ++i )
{
if( !( GetFields()[i] == other->GetFields()[i] ) )
return false;
}
return true;
}
double SCH_SHEET::Similarity( const SCH_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
return 0.0;
const SCH_SHEET* other = static_cast<const SCH_SHEET*>( &aOther );
if( m_screen->GetFileName() == other->m_screen->GetFileName() )
return 1.0;
return 0.0;
}
#if defined(DEBUG)
void SCH_SHEET::Show( int nestLevel, std::ostream& os ) const

View File

@ -418,6 +418,10 @@ public:
*/
static int ComparePageNum( const wxString& aPageNumberA, const wxString& aPageNumberB );
double Similarity( const SCH_ITEM& aOther ) const override;
bool operator==( const SCH_ITEM& aOther ) const override;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override;
#endif

View File

@ -80,6 +80,16 @@ public:
void MirrorVertically( int aCenter ) override {}
void Rotate( const VECTOR2I& aCenter ) override {}
double Similarity( const SCH_ITEM& aOther ) const override
{
return 0.0;
}
bool operator==( const SCH_ITEM& aOther ) const override
{
return false;
}
#if defined(DEBUG)
void Show( int , std::ostream& ) const override {}
#endif

View File

@ -356,6 +356,39 @@ bool SCH_SHEET_PIN::HitTest( const VECTOR2I& aPoint, int aAccuracy ) const
}
bool SCH_SHEET_PIN::operator==( const SCH_ITEM& aOther ) const
{
if( aOther.Type() != Type() )
return false;
const SCH_SHEET_PIN* other = static_cast<const SCH_SHEET_PIN*>( &aOther );
return m_edge == other->m_edge && m_number == other->m_number
&& SCH_HIERLABEL::operator==( aOther );
}
double SCH_SHEET_PIN::Similarity( const SCH_ITEM& aOther ) const
{
if( aOther.Type() != Type() )
return 0.0;
const SCH_SHEET_PIN* other = static_cast<const SCH_SHEET_PIN*>( &aOther );
double similarity = 1.0;
if( m_edge != other->m_edge )
similarity *= 0.9;
if( m_number != other->m_number )
similarity *= 0.9;
similarity *= SCH_HIERLABEL::Similarity( aOther );
return similarity;
}
#if defined(DEBUG)
void SCH_SHEET_PIN::Show( int nestLevel, std::ostream& os ) const

View File

@ -200,6 +200,10 @@ public:
EDA_ITEM* Clone() const override;
double Similarity( const SCH_ITEM& aOther ) const override;
bool operator==( const SCH_ITEM& aOther ) const override;
private:
int m_number; ///< Label number use for saving sheet label to file.
///< Sheet label numbering begins at 2.

View File

@ -2519,6 +2519,61 @@ bool SCH_SYMBOL::IsPower() const
}
bool SCH_SYMBOL::operator==( const SCH_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
return false;
auto symbol = static_cast<const SCH_SYMBOL&>( aOther );
if( GetLibId() != symbol.GetLibId() )
return false;
if( GetPosition() != symbol.GetPosition() )
return false;
if( GetUnit() != symbol.GetUnit() )
return false;
if( GetConvert() != symbol.GetConvert() )
return false;
if( GetTransform() != symbol.GetTransform() )
return false;
if( GetFields() != symbol.GetFields() )
return false;
if( m_pins.size() != symbol.m_pins.size() )
return false;
for( unsigned i = 0; i < m_pins.size(); ++i )
{
if( !( *m_pins[i] == *symbol.m_pins[i] ) )
return false;
}
return true;
}
double SCH_SYMBOL::Similarity( const SCH_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
return 0.0;
auto symbol = static_cast<const SCH_SYMBOL&>( aOther );
if( GetLibId() != symbol.GetLibId() )
return 0.0;
if( GetPosition() == symbol.GetPosition() )
return 1.0;
return 0.0;
}
static struct SCH_SYMBOL_DESC
{
SCH_SYMBOL_DESC()

View File

@ -758,6 +758,10 @@ public:
bool IsPower() const;
double Similarity( const SCH_ITEM& aOther ) const override;
bool operator==( const SCH_ITEM& aOther ) const override;
private:
BOX2I doGetBoundingBox( bool aIncludePins, bool aIncludeFields ) const;

Some files were not shown because too many files have changed in this diff Show More