Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
672 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ca8bd2e..2831737 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,384 +1,386 @@
cmake_minimum_required(VERSION 3.2)
project(Guitar
VERSION 0.9.0
LANGUAGES CXX C
)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/")
find_package(Qt5 COMPONENTS Core Widgets Network Svg REQUIRED)
find_package(zlib REQUIRED )
find_package(OpenSSL REQUIRED )
find_package(Qt5LinguistTools REQUIRED)
if(WIN32)
# check package at
# https://github.com/rprichard/winpty
find_package(winpty REQUIRED )
endif()
# extract version information
string(TIMESTAMP Guitar_copyright_year "%Y")
execute_process(
COMMAND git rev-parse --short=7 HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE Guitar_git_hash
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_compile_options(-std=c++11)
configure_file(version.h.in version.h)
configure_file(win.rc.in win.rc.h)
configure_file(Info.plist.in Info.plist)
set(Guitar_SOURCES
src/main.cpp
src/MainWindow.cpp
src/Git.cpp
src/common/joinpath.cpp
src/common/misc.cpp
src/ConfigCredentialHelperDialog.cpp
src/MySettings.cpp
src/FileDiffWidget.cpp
src/TextEditDialog.cpp
src/LogTableWidget.cpp
src/FileDiffSliderWidget.cpp
src/FileUtil.cpp
src/SettingsDialog.cpp
src/MergeBranchDialog.cpp
src/CloneDialog.cpp
src/AboutDialog.cpp
src/RepositoryInfoFrame.cpp
src/RepositoryPropertyDialog.cpp
src/RepositoryData.cpp
src/MyToolButton.cpp
src/GitDiff.cpp
src/CommitPropertyDialog.cpp
src/Terminal.cpp
src/DeleteTagsDialog.cpp
src/RepositoriesTreeWidget.cpp
src/SelectCommandDialog.cpp
src/ImageViewWidget.cpp
src/FileHistoryWindow.cpp
src/GitPackIdxV2.cpp
src/GitPack.cpp
src/GitObjectManager.cpp
src/FilePropertyDialog.cpp
src/BigDiffWindow.cpp
src/MaximizeButton.cpp
src/CommitExploreWindow.cpp
src/ReadOnlyLineEdit.cpp
src/ReadOnlyPlainTextEdit.cpp
src/MyTableWidgetDelegate.cpp
src/SetRemoteUrlDialog.cpp
src/ClearButton.cpp
src/SetUserDialog.cpp
src/SearchFromGitHubDialog.cpp
src/webclient.cpp
src/charvec.cpp
src/urlencode.cpp
src/HyperLinkLabel.cpp
src/JumpDialog.cpp
src/CheckoutDialog.cpp
src/DeleteBranchDialog.cpp
src/BasicRepositoryDialog.cpp
src/RemoteRepositoriesTableWidget.cpp
src/LocalSocketReader.cpp
src/PushDialog.cpp
src/StatusLabel.cpp
src/RepositoryLineEdit.cpp
src/DirectoryLineEdit.cpp
src/AbstractSettingForm.cpp
src/SettingExampleForm.cpp
src/CreateRepositoryDialog.cpp
src/GitHubAPI.cpp
src/MemoryReader.cpp
src/ExperimentDialog.cpp
src/gunzip.cpp
src/AvatarLoader.cpp
src/SettingNetworkForm.cpp
src/Photoshop.cpp
src/SettingBehaviorForm.cpp
src/MyProcess.cpp
src/FileViewWidget.cpp
src/MyTextEditorWidget.cpp
src/AbstractProcess.cpp
src/texteditor/AbstractCharacterBasedApplication.cpp
src/texteditor/InputMethodPopup.cpp
src/texteditor/TextEditorTheme.cpp
src/texteditor/TextEditorWidget.cpp
src/texteditor/unicode.cpp
src/texteditor/UnicodeWidth.cpp
src/MyImageViewWidget.cpp
src/SetGlobalUserDialog.cpp
src/ReflogWindow.cpp
src/Theme.cpp
src/ApplicationGlobal.cpp
src/BlameWindow.cpp
src/MenuButton.cpp
src/SettingGeneralForm.cpp
src/WelcomeWizardDialog.cpp
src/DialogHeaderFrame.cpp
src/CommitViewWindow.cpp
src/EditRemoteDialog.cpp
src/gpg.cpp
src/SelectGpgKeyDialog.cpp
src/SetGpgSigningDialog.cpp
src/CommitDialog.cpp
src/ConfigSigningDialog.cpp
src/AreYouSureYouWantToContinueConnectingDialog.cpp
src/LineEditDialog.cpp
src/SettingProgramsForm.cpp
src/InputNewTagDialog.cpp
src/EditTagsDialog.cpp
src/darktheme/DarkStyle.cpp
src/darktheme/NinePatch.cpp
src/darktheme/StandardStyle.cpp
src/darktheme/TraditionalWindowsStyleTreeControl.cpp
src/EditGitIgnoreDialog.cpp
src/SelectItemDialog.cpp
src/RebaseOntoDialog.cpp
src/RemoteWatcher.cpp
src/DoYouWantToInitDialog.cpp
+ src/BasicMainWindow.cpp
)
set(Guitar_HEADERS
src/MainWindow.h
src/Git.h
src/common/joinpath.h
src/common/misc.h
src/ConfigCredentialHelperDialog.h
src/MySettings.h
src/main.h
src/FileDiffWidget.h
src/TextEditDialog.h
src/LogTableWidget.h
src/FileDiffSliderWidget.h
src/FileUtil.h
src/SettingsDialog.h
src/MergeBranchDialog.h
src/CloneDialog.h
src/AboutDialog.h
src/RepositoryInfoFrame.h
src/RepositoryPropertyDialog.h
src/RepositoryData.h
src/MyToolButton.h
src/GitDiff.h
src/CommitPropertyDialog.h
src/Terminal.h
src/DeleteTagsDialog.h
src/RepositoriesTreeWidget.h
src/SelectCommandDialog.h
src/ImageViewWidget.h
src/FileHistoryWindow.h
src/Debug.h
src/GitPackIdxV2.h
src/GitPack.h
src/GitObjectManager.h
src/FilePropertyDialog.h
src/BigDiffWindow.h
src/MaximizeButton.h
src/CommitExploreWindow.h
src/ReadOnlyLineEdit.h
src/ReadOnlyPlainTextEdit.h
src/MyTableWidgetDelegate.h
src/SetRemoteUrlDialog.h
src/ClearButton.h
src/SetUserDialog.h
src/SearchFromGitHubDialog.h
src/webclient.h
src/charvec.h
src/urlencode.h
src/HyperLinkLabel.h
src/JumpDialog.h
src/CheckoutDialog.h
src/DeleteBranchDialog.h
src/BasicRepositoryDialog.h
src/RemoteRepositoriesTableWidget.h
src/LocalSocketReader.h
src/PushDialog.h
src/StatusLabel.h
src/RepositoryLineEdit.h
src/DirectoryLineEdit.h
src/AbstractSettingForm.h
src/SettingExampleForm.h
src/CreateRepositoryDialog.h
src/GitHubAPI.h
src/MemoryReader.h
src/ExperimentDialog.h
src/gunzip.h
src/AvatarLoader.h
src/SettingNetworkForm.h
src/Photoshop.h
src/SettingBehaviorForm.h
src/MyProcess.h
src/FileViewWidget.h
src/MyTextEditorWidget.h
src/AbstractProcess.h
src/texteditor/AbstractCharacterBasedApplication.h
src/texteditor/InputMethodPopup.h
src/texteditor/TextEditorTheme.h
src/texteditor/TextEditorWidget.h
src/texteditor/unicode.h
src/texteditor/UnicodeWidth.h
src/MyImageViewWidget.h
src/SetGlobalUserDialog.h
src/ReflogWindow.h
src/Theme.h
src/ApplicationGlobal.h
src/BlameWindow.h
src/MenuButton.h
src/SettingGeneralForm.h
src/WelcomeWizardDialog.h
src/DialogHeaderFrame.h
src/CommitViewWindow.h
src/EditRemoteDialog.h
src/gpg.h
src/SelectGpgKeyDialog.h
src/SetGpgSigningDialog.h
src/CommitDialog.h
src/ConfigSigningDialog.h
src/AreYouSureYouWantToContinueConnectingDialog.h
src/LineEditDialog.h
src/SettingProgramsForm.h
src/InputNewTagDialog.h
src/EditTagsDialog.h
src/darktheme/DarkStyle.h
src/darktheme/NinePatch.h
src/darktheme/StandardStyle.h
src/darktheme/TraditionalWindowsStyleTreeControl.h
src/EditGitIgnoreDialog.h
src/SelectItemDialog.h
src/RebaseOntoDialog.h
src/RemoteWatcher.h
src/DoYouWantToInitDialog.h
+ src/BasicMainWindow.h
)
if(UNIX)
list(APPEND Guitar_SOURCES
src/unix/UnixProcess.cpp
src/unix/UnixPtyProcess.cpp
)
list(APPEND Guitar_HEADERS
src/unix/UnixProcess.h
src/unix/UnixPtyProcess.h
)
elseif(WIN32)
list(APPEND Guitar_SOURCES
src/win32/thread.cpp
src/win32/event.cpp
src/win32/win32.cpp
src/win32/Win32Process.cpp
src/win32/Win32PtyProcess.cpp
)
list(APPEND Guitar_HEADERS
src/win32/thread.h
src/win32/event.h
src/win32/mutex.h
src/win32/win32.h
src/win32/Win32Process.h
src/win32/Win32PtyProcess.h
)
endif()
set(Guitar_UIS
src/MainWindow.ui
src/ConfigCredentialHelperDialog.ui
src/TextEditDialog.ui
src/SettingsDialog.ui
src/MergeBranchDialog.ui
src/CloneDialog.ui
src/AboutDialog.ui
src/RepositoryPropertyDialog.ui
src/CommitPropertyDialog.ui
src/DeleteTagsDialog.ui
src/SelectCommandDialog.ui
src/FileDiffWidget.ui
src/FileHistoryWindow.ui
src/FilePropertyDialog.ui
src/BigDiffWindow.ui
src/CommitExploreWindow.ui
src/SetRemoteUrlDialog.ui
src/SetUserDialog.ui
src/SearchFromGitHubDialog.ui
src/JumpDialog.ui
src/CheckoutDialog.ui
src/DeleteBranchDialog.ui
src/PushDialog.ui
src/SettingExampleForm.ui
src/CreateRepositoryDialog.ui
src/ExperimentDialog.ui
src/SettingNetworkForm.ui
src/SettingBehaviorForm.ui
src/SetGlobalUserDialog.ui
src/ReflogWindow.ui
src/BlameWindow.ui
src/SettingGeneralForm.ui
src/WelcomeWizardDialog.ui
src/CommitViewWindow.ui
src/EditRemoteDialog.ui
src/SelectGpgKeyDialog.ui
src/SetGpgSigningDialog.ui
src/CommitDialog.ui
src/ConfigSigningDialog.ui
src/AreYouSureYouWantToContinueConnectingDialog.ui
src/LineEditDialog.ui
src/SettingProgramsForm.ui
src/InputNewTagDialog.ui
src/EditTagsDialog.ui
src/EditGitIgnoreDialog.ui
src/SelectItemDialog.ui
src/RebaseOntoDialog.ui
src/DoYouWantToInitDialog.ui
)
set(Guitar_RESOURCES
src/resources/resources.qrc
)
set(Guitar_TRANSLATIONS
src/resources/translations/ja_JPN.ts
)
qt5_add_translation(Guitar_QM_FILES ${Guitar_TRANSLATIONS})
configure_file(${Guitar_RESOURCES} ${CMAKE_BINARY_DIR} COPYONLY)
add_custom_target(translations ALL DEPENDS ${Guitar_QM_FILES})
add_custom_target(resources ALL DEPENDS ${Guitar_RESOURCES})
add_dependencies(resources translations)
include_directories(src)
include_directories(src/texteditor)
add_executable(${PROJECT_NAME}
${Guitar_SOURCES}
${Guitar_HEADERS}
${Guitar_UIS}
${Guitar_RESOURCES}
${Guitar_QM_FILES}
)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 11
CXX_EXTENSIONS OFF
)
target_link_libraries(${PROJECT_NAME}
Qt5::Widgets
Qt5::Network
Qt5::Svg
zlib
OpenSSL::SSL OpenSSL::Crypto
)
add_subdirectory(extra)
diff --git a/Guitar.pro b/Guitar.pro
index 6eb8e4b..ad90007 100644
--- a/Guitar.pro
+++ b/Guitar.pro
@@ -1,394 +1,395 @@
QT += core gui widgets svg network
win32:QT += winextras
CONFIG(debug,debug|release):TARGET = Guitard
CONFIG(release,debug|release):TARGET = Guitar
TEMPLATE = app
CONFIG += c++11
DESTDIR = $$PWD/_bin
TRANSLATIONS = Guitar_ja.ts
DEFINES += APP_GUITAR
-DEFINES += USE_DARK_THEME
-
DEFINES += HAVE_POSIX_OPENPT
macx:DEFINES += HAVE_SYS_TIME_H
macx:DEFINES += HAVE_UTMPX
gcc:QMAKE_CXXFLAGS += -Wall -Wextra -Werror=return-type -Werror=trigraphs -Wno-switch -Wno-reorder
linux:QMAKE_RPATHDIR += $ORIGIN
macx:QMAKE_RPATHDIR += @executable_path/../Frameworks
linux:QTPLUGIN += ibusplatforminputcontextplugin
#linux:QTPLUGIN += fcitxplatforminputcontextplugin
INCLUDEPATH += $$PWD/src
INCLUDEPATH += $$PWD/src/texteditor
win32:INCLUDEPATH += $$PWD/misc/winpty
win32:LIBS += $$PWD/misc/winpty/winpty.lib
# OpenSSL
linux:LIBS += -lssl -lcrypto
haiku:LIBS += -lssl -lcrypto -lnetwork
macx:INCLUDEPATH += /usr/local/include
macx:LIBS += /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a
win32:msvc {
INCLUDEPATH += C:\openssl\include
INCLUDEPATH += $$PWD/../zlib
LIBS += -LC:\openssl\lib
}
win32:gcc {
INCLUDEPATH += C:\Qt\Tools\mingw530_32\opt\include
LIBS += -LC:\Qt\Tools\mingw530_32\opt\lib
LIBS += -lcrypto -lssl
}
# execute 'ruby prepare.rb' automatically
prepare.target = prepare
prepare.commands = cd $$PWD && ruby -W0 prepare.rb
QMAKE_EXTRA_TARGETS += prepare
PRE_TARGETDEPS += prepare
# zlib
win32:msvc {
CONFIG(debug, debug|release):LIBS += $$PWD/_bin/libz.lib
CONFIG(release, debug|release):LIBS += $$PWD/_bin/libz.lib
}
win32:gcc {
CONFIG(debug, debug|release):LIBS += $$PWD/_bin/liblibz.a
CONFIG(release, debug|release):LIBS += $$PWD/_bin/liblibz.a
}
!haiku {
unix:CONFIG(debug, debug|release):LIBS += $$PWD/_bin/libzd.a
unix:CONFIG(release, debug|release):LIBS += $$PWD/_bin/libz.a
#unix:LIBS += -lz
}
haiku:LIBS += -lz
win32 {
LIBS += -ladvapi32 -lshell32 -luser32 -lws2_32
RC_FILE = win.rc
QMAKE_SUBSYSTEM_SUFFIX=,5.01
}
macx {
QMAKE_INFO_PLIST = Info.plist
ICON += Guitar.icns
t.path=Contents/Resources
QMAKE_BUNDLE_DATA += t
}
SOURCES += \
src/main.cpp\
src/MainWindow.cpp \
src/Git.cpp \
src/common/joinpath.cpp \
src/common/misc.cpp \
src/ConfigCredentialHelperDialog.cpp \
src/MySettings.cpp \
src/FileDiffWidget.cpp \
src/TextEditDialog.cpp \
src/LogTableWidget.cpp \
src/FileDiffSliderWidget.cpp \
src/FileUtil.cpp \
src/SettingsDialog.cpp \
src/MergeBranchDialog.cpp \
src/CloneDialog.cpp \
src/AboutDialog.cpp \
src/RepositoryInfoFrame.cpp \
src/RepositoryPropertyDialog.cpp \
src/RepositoryData.cpp \
src/MyToolButton.cpp \
src/GitDiff.cpp \
src/CommitPropertyDialog.cpp \
src/Terminal.cpp \
src/DeleteTagsDialog.cpp \
src/RepositoriesTreeWidget.cpp \
src/SelectCommandDialog.cpp \
src/ImageViewWidget.cpp \
src/FileHistoryWindow.cpp \
src/GitPackIdxV2.cpp \
src/GitPack.cpp \
src/GitObjectManager.cpp \
src/FilePropertyDialog.cpp \
src/BigDiffWindow.cpp \
src/MaximizeButton.cpp \
src/CommitExploreWindow.cpp \
src/ReadOnlyLineEdit.cpp \
src/ReadOnlyPlainTextEdit.cpp \
src/MyTableWidgetDelegate.cpp \
src/SetRemoteUrlDialog.cpp \
src/ClearButton.cpp \
src/SetUserDialog.cpp \
src/SearchFromGitHubDialog.cpp \
src/webclient.cpp \
src/charvec.cpp \
src/urlencode.cpp \
src/HyperLinkLabel.cpp \
src/JumpDialog.cpp \
src/CheckoutDialog.cpp \
src/DeleteBranchDialog.cpp \
src/BasicRepositoryDialog.cpp \
src/RemoteRepositoriesTableWidget.cpp \
src/LocalSocketReader.cpp \
src/PushDialog.cpp \
src/StatusLabel.cpp \
src/RepositoryLineEdit.cpp \
src/DirectoryLineEdit.cpp \
src/AbstractSettingForm.cpp \
src/SettingExampleForm.cpp \
src/CreateRepositoryDialog.cpp \
src/GitHubAPI.cpp \
src/MemoryReader.cpp \
src/ExperimentDialog.cpp \
src/gunzip.cpp \
src/AvatarLoader.cpp \
src/SettingNetworkForm.cpp \
src/Photoshop.cpp \
src/SettingBehaviorForm.cpp \
src/MyProcess.cpp \
src/FileViewWidget.cpp \
src/MyTextEditorWidget.cpp \
src/AbstractProcess.cpp \
src/texteditor/AbstractCharacterBasedApplication.cpp \
src/texteditor/InputMethodPopup.cpp \
src/texteditor/TextEditorTheme.cpp \
src/texteditor/TextEditorWidget.cpp \
src/texteditor/unicode.cpp \
src/texteditor/UnicodeWidth.cpp \
src/MyImageViewWidget.cpp \
src/SetGlobalUserDialog.cpp \
src/ReflogWindow.cpp \
src/Theme.cpp \
src/ApplicationGlobal.cpp \
src/BlameWindow.cpp \
src/MenuButton.cpp \
src/SettingGeneralForm.cpp \
src/WelcomeWizardDialog.cpp \
src/DialogHeaderFrame.cpp \
src/CommitViewWindow.cpp \
src/EditRemoteDialog.cpp \
src/gpg.cpp \
src/SelectGpgKeyDialog.cpp \
src/SetGpgSigningDialog.cpp \
src/CommitDialog.cpp \
src/ConfigSigningDialog.cpp \
src/AreYouSureYouWantToContinueConnectingDialog.cpp \
src/LineEditDialog.cpp \
src/SettingProgramsForm.cpp \
src/InputNewTagDialog.cpp \
src/EditTagsDialog.cpp \
src/darktheme/DarkStyle.cpp \
src/darktheme/NinePatch.cpp \
src/darktheme/StandardStyle.cpp \
src/darktheme/TraditionalWindowsStyleTreeControl.cpp \
src/EditGitIgnoreDialog.cpp \
src/SelectItemDialog.cpp \
src/RebaseOntoDialog.cpp \
src/DoYouWantToInitDialog.cpp \
- src/RemoteWatcher.cpp
+ src/RemoteWatcher.cpp \
+ src/BasicMainWindow.cpp
HEADERS += \
src/MainWindow.h \
src/Git.h \
src/common/joinpath.h \
src/common/misc.h \
src/ConfigCredentialHelperDialog.h \
src/MySettings.h \
src/main.h \
src/FileDiffWidget.h \
src/TextEditDialog.h \
src/LogTableWidget.h \
src/FileDiffSliderWidget.h \
src/FileUtil.h \
src/SettingsDialog.h \
src/MergeBranchDialog.h \
src/CloneDialog.h \
src/AboutDialog.h \
src/RepositoryInfoFrame.h \
src/RepositoryPropertyDialog.h \
src/RepositoryData.h \
src/MyToolButton.h \
src/GitDiff.h \
src/CommitPropertyDialog.h \
src/Terminal.h \
src/DeleteTagsDialog.h \
src/RepositoriesTreeWidget.h \
src/SelectCommandDialog.h \
src/ImageViewWidget.h \
src/FileHistoryWindow.h \
src/Debug.h \
src/GitPackIdxV2.h \
src/GitPack.h \
src/GitObjectManager.h \
src/FilePropertyDialog.h \
src/BigDiffWindow.h \
src/MaximizeButton.h \
src/CommitExploreWindow.h \
src/ReadOnlyLineEdit.h \
src/ReadOnlyPlainTextEdit.h \
src/MyTableWidgetDelegate.h \
src/SetRemoteUrlDialog.h \
src/ClearButton.h \
src/SetUserDialog.h \
src/SearchFromGitHubDialog.h \
src/webclient.h \
src/charvec.h \
src/urlencode.h \
src/HyperLinkLabel.h \
src/JumpDialog.h \
src/CheckoutDialog.h \
src/DeleteBranchDialog.h \
src/BasicRepositoryDialog.h \
src/RemoteRepositoriesTableWidget.h \
src/LocalSocketReader.h \
src/PushDialog.h \
src/StatusLabel.h \
src/RepositoryLineEdit.h \
src/DirectoryLineEdit.h \
src/AbstractSettingForm.h \
src/SettingExampleForm.h \
src/CreateRepositoryDialog.h \
src/GitHubAPI.h \
src/MemoryReader.h \
src/ExperimentDialog.h \
src/gunzip.h \
src/AvatarLoader.h \
src/SettingNetworkForm.h \
src/Photoshop.h \
src/SettingBehaviorForm.h \
src/MyProcess.h \
src/MyTextEditorWidget.h \
src/AbstractProcess.h \
src/texteditor/AbstractCharacterBasedApplication.h \
src/texteditor/InputMethodPopup.h \
src/texteditor/TextEditorTheme.h \
src/texteditor/TextEditorWidget.h \
src/texteditor/unicode.h \
src/texteditor/UnicodeWidth.h \
src/MyImageViewWidget.h \
src/SetGlobalUserDialog.h \
src/ReflogWindow.h \
src/Theme.h \
src/ApplicationGlobal.h \
src/BlameWindow.h \
src/MenuButton.h \
src/SettingGeneralForm.h \
src/WelcomeWizardDialog.h \
src/DialogHeaderFrame.h \
src/CommitViewWindow.h \
src/EditRemoteDialog.h \
src/gpg.h \
src/SelectGpgKeyDialog.h \
src/SetGpgSigningDialog.h \
src/CommitDialog.h \
src/ConfigSigningDialog.h \
src/AreYouSureYouWantToContinueConnectingDialog.h \
src/LineEditDialog.h \
src/SettingProgramsForm.h \
src/InputNewTagDialog.h \
src/EditTagsDialog.h \
src/darktheme/DarkStyle.h \
src/darktheme/NinePatch.h \
src/darktheme/StandardStyle.h \
src/darktheme/TraditionalWindowsStyleTreeControl.h \
src/EditGitIgnoreDialog.h \
src/SelectItemDialog.h \
src/RebaseOntoDialog.h \
src/DoYouWantToInitDialog.h \
- src/RemoteWatcher.h
+ src/RemoteWatcher.h \
+ src/BasicMainWindow.h \
+ src/version.h
FORMS += \
src/MainWindow.ui \
src/ConfigCredentialHelperDialog.ui \
src/TextEditDialog.ui \
src/SettingsDialog.ui \
src/MergeBranchDialog.ui \
src/CloneDialog.ui \
src/AboutDialog.ui \
src/RepositoryPropertyDialog.ui \
src/CommitPropertyDialog.ui \
src/DeleteTagsDialog.ui \
src/SelectCommandDialog.ui \
src/FileDiffWidget.ui \
src/FileHistoryWindow.ui \
src/FilePropertyDialog.ui \
src/BigDiffWindow.ui \
src/CommitExploreWindow.ui \
src/SetRemoteUrlDialog.ui \
src/SetUserDialog.ui \
src/SearchFromGitHubDialog.ui \
src/JumpDialog.ui \
src/CheckoutDialog.ui \
src/DeleteBranchDialog.ui \
src/PushDialog.ui \
src/SettingExampleForm.ui \
src/CreateRepositoryDialog.ui \
src/ExperimentDialog.ui \
src/SettingNetworkForm.ui \
src/SettingBehaviorForm.ui \
src/SetGlobalUserDialog.ui \
src/ReflogWindow.ui \
src/BlameWindow.ui \
src/SettingGeneralForm.ui \
src/WelcomeWizardDialog.ui \
src/CommitViewWindow.ui \
src/EditRemoteDialog.ui \
src/SelectGpgKeyDialog.ui \
src/SetGpgSigningDialog.ui \
src/CommitDialog.ui \
src/ConfigSigningDialog.ui \
src/AreYouSureYouWantToContinueConnectingDialog.ui \
src/LineEditDialog.ui \
src/SettingProgramsForm.ui \
src/InputNewTagDialog.ui \
src/EditTagsDialog.ui \
src/EditGitIgnoreDialog.ui \
src/SelectItemDialog.ui \
src/RebaseOntoDialog.ui \
src/DoYouWantToInitDialog.ui
RESOURCES += \
src/resources/resources.qrc
unix {
SOURCES += \
src/unix/UnixProcess.cpp \
src/unix/UnixPtyProcess.cpp
HEADERS += \
src/unix/UnixProcess.h \
src/unix/UnixPtyProcess.h
}
win32 {
SOURCES += \
src/win32/thread.cpp \
src/win32/event.cpp \
src/win32/win32.cpp \
src/win32/Win32Process.cpp \
src/win32/Win32PtyProcess.cpp
HEADERS += \
src/win32/thread.h \
src/win32/event.h \
src/win32/mutex.h \
src/win32/win32.h \
src/win32/Win32Process.h \
src/win32/Win32PtyProcess.h
}
diff --git a/src/AboutDialog.h b/src/AboutDialog.h
index 87f4ef0..88922c0 100644
--- a/src/AboutDialog.h
+++ b/src/AboutDialog.h
@@ -1,27 +1,26 @@
#ifndef ABOUTDIALOG_H
#define ABOUTDIALOG_H
#include <QDialog>
namespace Ui {
class AboutDialog;
}
class AboutDialog : public QDialog
{
Q_OBJECT
private:
Ui::AboutDialog *ui;
QPixmap pixmap;
public:
- explicit AboutDialog(QWidget *parent = 0);
- ~AboutDialog();
+ explicit AboutDialog(QWidget *parent = nullptr);
+ ~AboutDialog() override;
static QString appVersion();
protected:
- void mouseReleaseEvent(QMouseEvent *);
-protected:
- void paintEvent(QPaintEvent *event);
+ void mouseReleaseEvent(QMouseEvent *) override;
+ void paintEvent(QPaintEvent *event) override;
};
#endif // ABOUTDIALOG_H
diff --git a/src/AbstractSettingForm.h b/src/AbstractSettingForm.h
index 7c49a11..8f25d1d 100644
--- a/src/AbstractSettingForm.h
+++ b/src/AbstractSettingForm.h
@@ -1,20 +1,20 @@
#ifndef ABSTRACTSETTINGFORM_H
#define ABSTRACTSETTINGFORM_H
#include <QWidget>
#include "main.h"
#include "SettingsDialog.h"
class MainWindow;
class AbstractSettingForm : public QWidget {
Q_OBJECT
protected:
MainWindow *mainwindow();
ApplicationSettings *settings();
public:
- AbstractSettingForm(QWidget *parent = 0);
+ AbstractSettingForm(QWidget *parent = nullptr);
virtual void exchange(bool save) = 0;
};
#endif // ABSTRACTSETTINGFORM_H
diff --git a/src/AreYouSureYouWantToContinueConnectingDialog.h b/src/AreYouSureYouWantToContinueConnectingDialog.h
index 26890a5..4962713 100644
--- a/src/AreYouSureYouWantToContinueConnectingDialog.h
+++ b/src/AreYouSureYouWantToContinueConnectingDialog.h
@@ -1,37 +1,36 @@
#ifndef AREYOUSUREYOUWANTTOCONTINUECONNECTINGDIALOG_H
#define AREYOUSUREYOUWANTTOCONTINUECONNECTINGDIALOG_H
#include <QDialog>
namespace Ui {
class AreYouSureYouWantToContinueConnectingDialog;
}
-class AreYouSureYouWantToContinueConnectingDialog : public QDialog
-{
+class AreYouSureYouWantToContinueConnectingDialog : public QDialog {
Q_OBJECT
public:
enum Result {
Cancel,
Yes,
No,
};
public:
- explicit AreYouSureYouWantToContinueConnectingDialog(QWidget *parent = 0);
- ~AreYouSureYouWantToContinueConnectingDialog();
+ explicit AreYouSureYouWantToContinueConnectingDialog(QWidget *parent = nullptr);
+ ~AreYouSureYouWantToContinueConnectingDialog() override;
Result result() const;
private slots:
void on_pushButton_continue_clicked();
void on_pushButton_close_clicked();
private:
Ui::AreYouSureYouWantToContinueConnectingDialog *ui;
Result result_ = Result::Cancel;
// QDialog interface
public slots:
- void reject();
+ void reject() override;
};
#endif // AREYOUSUREYOUWANTTOCONTINUECONNECTINGDIALOG_H
diff --git a/src/AvatarLoader.cpp b/src/AvatarLoader.cpp
index 8ce5483..414629c 100644
--- a/src/AvatarLoader.cpp
+++ b/src/AvatarLoader.cpp
@@ -1,182 +1,182 @@
#include "AvatarLoader.h"
#include "MemoryReader.h"
#include "webclient.h"
#include <QCryptographicHash>
#include <QDebug>
namespace {
const int MAX_CACHE_COUNT = 1000;
const int ICON_SIZE = 64;
}
using WebClientPtr = std::shared_ptr<WebClient>;
struct AvatarLoader::Private {
QMutex data_mutex;
QMutex thread_mutex;
QWaitCondition condition;
std::map<std::string, std::string> avatar_url_cache;
std::deque<RequestItem> requested;
std::deque<RequestItem> completed;
std::set<std::string> notfound;
WebContext *webcx = nullptr;
WebClientPtr web;
};
AvatarLoader::AvatarLoader()
: m(new Private)
{
}
AvatarLoader::~AvatarLoader()
{
delete m;
}
void AvatarLoader::start(WebContext *webcx)
{
m->webcx = webcx;
QThread::start();
}
void AvatarLoader::run()
{
m->web = std::make_shared<WebClient>(m->webcx);
while (1) {
std::deque<RequestItem> requests;
if (isInterruptionRequested()) return;
m->thread_mutex.lock();
{
QMutexLocker lock(&m->data_mutex);
requests = std::move(m->requested);
}
if (requests.empty()) {
m->condition.wait(&m->thread_mutex);
{
QMutexLocker lock(&m->data_mutex);
requests = std::move(m->requested);
}
}
m->thread_mutex.unlock();
if (isInterruptionRequested()) return;
for (RequestItem &item : requests) {
if (isInterruptionRequested()) return;
if (item.email.empty()) continue;
if (strchr(item.email.c_str(), '@')) {
QCryptographicHash hash(QCryptographicHash::Md5);
hash.addData(item.email.c_str(), item.email.size());
QByteArray ba = hash.result();
char tmp[100];
for (int i = 0; i < ba.size(); i++) {
sprintf(tmp + i * 2, "%02x", ba.data()[i] & 0xff);
}
QString id = tmp;
QString url = "https://www.gravatar.com/avatar/%1?s=%2";
url = url.arg(id).arg(ICON_SIZE);
if (m->web->get(WebClient::URL(url.toStdString())) == 200) {
if (!m->web->response().content.empty()) {
MemoryReader reader(m->web->response().content.data(), m->web->response().content.size());
reader.open(MemoryReader::ReadOnly);
QImage image;
image.load(&reader, nullptr);
int w = image.width();
int h = image.height();
if (w > 0 && h > 0) {
item.icon = QIcon(QPixmap::fromImage(image));
{
QMutexLocker lock(&m->data_mutex);
while (m->completed.size() >= MAX_CACHE_COUNT) {
m->completed.pop_back();
}
m->completed.push_front(item);
}
emit updated();
continue;
}
}
}
}
{ // not found
QMutexLocker lock(&m->data_mutex);
m->notfound.insert(m->notfound.end(), item.email);
}
}
}
}
namespace {
bool isValidGitHubName(std::string const &name)
{
size_t n = name.size();
if (n < 1 || n > 39) return false;
char const *begin = name.c_str();
char const *end = begin + name.size();
char const *ptr = begin;
while (ptr < end) {
int c = (unsigned char)*ptr;
ptr++;
if (isalnum(c)) {
// ok
} else if (c == '-') {
if (ptr == begin) return false;
if (ptr < end && *ptr == '-') return false;
}
}
return true;
}
}
-QIcon AvatarLoader::fetch(std::string const &email, bool request)
+QIcon AvatarLoader::fetch(std::string const &email, bool request) const
{
RequestItem item;
item.email = email;
if (isValidGitHubName(item.email)) {
QMutexLocker lock(&m->data_mutex);
auto it = m->notfound.find(item.email);
if (it == m->notfound.end()) {
for (size_t i = 0; i < m->completed.size(); i++) {
if (item.email == m->completed[i].email) {
item = m->completed[i];
m->completed.erase(m->completed.begin() + i);
m->completed.insert(m->completed.begin(), item);
return item.icon;
}
}
if (request) {
bool waiting = false;
for (RequestItem const &r : m->requested) {
if (item.email == r.email) {
waiting = true;
break;
}
}
if (!waiting) {
m->requested.push_back(item);
m->condition.wakeOne();
}
}
}
}
return QIcon();
}
void AvatarLoader::stop()
{
requestInterruption();
if (m->web) m->web->close();
m->condition.wakeAll();
wait();
}
diff --git a/src/AvatarLoader.h b/src/AvatarLoader.h
index 5e1a3b6..da372b7 100644
--- a/src/AvatarLoader.h
+++ b/src/AvatarLoader.h
@@ -1,37 +1,37 @@
#ifndef AVATARLOADER_H
#define AVATARLOADER_H
#include "GitHubAPI.h"
#include <QIcon>
#include <QThread>
#include <QMutex>
#include <deque>
#include <QWaitCondition>
#include <set>
#include <string>
class WebContext;
class AvatarLoader : public QThread {
Q_OBJECT
private:
struct RequestItem {
std::string email;
QIcon icon;
};
struct Private;
Private *m;
protected:
- void run();
+ void run() override;
public:
AvatarLoader();
- ~AvatarLoader();
- QIcon fetch(const std::string &email, bool request);
+ ~AvatarLoader() override;
+ QIcon fetch(const std::string &email, bool request) const;
void stop();
void start(WebContext *webcx);
signals:
void updated();
};
#endif // AVATARLOADER_H
diff --git a/src/BasicMainWindow.cpp b/src/BasicMainWindow.cpp
new file mode 100644
index 0000000..625413d
--- /dev/null
+++ b/src/BasicMainWindow.cpp
@@ -0,0 +1,2350 @@
+
+#include "ApplicationGlobal.h"
+#include "BasicMainWindow.h"
+#include "CheckoutDialog.h"
+#include "CloneDialog.h"
+#include "CommitDialog.h"
+#include "CommitExploreWindow.h"
+#include "CommitViewWindow.h"
+#include "CreateRepositoryDialog.h"
+#include "DeleteBranchDialog.h"
+#include "DoYouWantToInitDialog.h"
+#include "FileHistoryWindow.h"
+#include "FilePropertyDialog.h"
+#include "FileUtil.h"
+#include "Git.h"
+#include "GitDiff.h"
+#include "JumpDialog.h"
+#include "MemoryReader.h"
+#include "MySettings.h"
+#include "PushDialog.h"
+#include "RepositoryPropertyDialog.h"
+#include "SelectCommandDialog.h"
+#include "SetGlobalUserDialog.h"
+#include "SetUserDialog.h"
+#include "SettingsDialog.h"
+#include "Terminal.h"
+#include "TextEditDialog.h"
+#include "WelcomeWizardDialog.h"
+#include "common/joinpath.h"
+#include "common/misc.h"
+#include "gpg.h"
+#include "gunzip.h"
+#include "platform.h"
+#include "webclient.h"
+#include <QDesktopServices>
+#include <QDir>
+#include <QDirIterator>
+#include <QFileIconProvider>
+#include <QListWidget>
+#include <QMenu>
+#include <QMessageBox>
+#include <QStandardPaths>
+#include <QTreeWidgetItem>
+#include <memory>
+
+
+class AsyncExecGitThread_ : public QThread {
+private:
+ GitPtr g;
+ std::function<void(GitPtr g)> callback;
+public:
+ AsyncExecGitThread_(GitPtr g, std::function<void(GitPtr g)> callback)
+ : g(g)
+ , callback(callback)
+ {
+ }
+protected:
+ void run() override
+ {
+ callback(g);
+ }
+};
+
+
+//
+
+struct BasicMainWindow::Private {
+ ApplicationSettings appsettings;
+
+ QIcon repository_icon;
+ QIcon folder_icon;
+ QIcon signature_good_icon;
+ QIcon signature_dubious_icon;
+ QIcon signature_bad_icon;
+ QPixmap transparent_pixmap;
+
+ QString starting_dir;
+ Git::Context gcx;
+ RepositoryItem current_repo;
+
+ QList<RepositoryItem> repos;
+ Git::CommitItemList logs;
+ QList<Git::Diff> diff_result;
+
+ QStringList added;
+ QStringList remotes;
+ QString current_remote_name;
+ Git::Branch current_branch;
+ unsigned int temp_file_counter = 0;
+
+ std::string ssh_passphrase_user;
+ std::string ssh_passphrase_pass;
+
+ std::string http_uid;
+ std::string http_pwd;
+
+ std::map<QString, GitHubAPI::User> committer_map; // key is email
+
+ PtyProcess pty_process;
+ bool pty_process_ok = false;
+ PtyCondition pty_condition = PtyCondition::None;
+
+ WebContext webcx;
+
+ AvatarLoader avatar_loader;
+ int update_files_list_counter = 0;
+ int update_commit_table_counter = 0;
+
+ bool interaction_canceled = false;
+ InteractionMode interaction_mode = InteractionMode::None;
+
+ QString repository_filter_text;
+ bool uncommited_changes = false;
+
+ std::map<QString, QList<Git::Branch>> branch_map;
+ std::map<QString, QList<Git::Tag>> tag_map;
+ std::map<int, QList<Label>> label_map;
+ std::map<QString, Git::Diff> diff_cache;
+ GitObjectCache objcache;
+
+ bool remote_changed = false;
+
+ ServerType server_type = ServerType::Standard;
+ GitHubRepositoryInfo github;
+
+ QString head_id;
+ bool force_fetch = false;
+
+ RepositoryItem temp_repo_for_clone_complete;
+};
+
+BasicMainWindow::BasicMainWindow(QWidget *parent)
+ : QMainWindow(parent)
+ , m(new Private)
+{
+ SettingsDialog::loadSettings(&m->appsettings);
+ m->starting_dir = QDir::current().absolutePath();
+
+ { // load graphic resources
+ QFileIconProvider icons;
+ m->folder_icon = icons.icon(QFileIconProvider::Folder);
+ m->repository_icon = QIcon(":/image/repository.png");
+ m->signature_good_icon = QIcon(":/image/signature-good.png");
+ m->signature_bad_icon = QIcon(":/image/signature-bad.png");
+ m->signature_dubious_icon = QIcon(":/image/signature-dubious.png");
+ m->transparent_pixmap = QPixmap(":/image/transparent.png");
+ }
+}
+
+BasicMainWindow::~BasicMainWindow()
+{
+ deleteTempFiles();
+ delete m;
+}
+
+ApplicationSettings *BasicMainWindow::appsettings()
+{
+ return &m->appsettings;
+}
+
+const ApplicationSettings *BasicMainWindow::appsettings() const
+{
+ return &m->appsettings;
+}
+
+WebContext *BasicMainWindow::webContext()
+{
+ return &m->webcx;
+}
+
+QString BasicMainWindow::gitCommand() const
+{
+ return m->gcx.git_command;
+}
+
+void BasicMainWindow::autoOpenRepository(QString dir)
+{
+ auto Open = [&](RepositoryItem const &item){
+ setCurrentRepository(item, true);
+ openRepository(false, true);
+ };
+
+ RepositoryItem const *repo = findRegisteredRepository(&dir);
+ if (repo) {
+ Open(*repo);
+ return;
+ }
+
+ RepositoryItem newitem;
+ GitPtr g = git(dir);
+ if (isValidWorkingCopy(g)) {
+ ushort const *left = dir.utf16();
+ ushort const *right = left + dir.size();
+ if (right[-1] == '/' || right[-1] == '\\') {
+ right--;
+ }
+ ushort const *p = right;
+ while (left + 1 < p && !(p[-1] == '/' || p[-1] == '\\')) p--;
+ if (p < right) {
+ newitem.local_dir = dir;
+ newitem.name = QString::fromUtf16(p, right - p);
+ saveRepositoryBookmark(newitem);
+ Open(newitem);
+ return;
+ }
+ } else {
+ DoYouWantToInitDialog dlg(this, dir);
+ if (dlg.exec() == QDialog::Accepted) {
+ createRepository(dir);
+ }
+ }
+}
+
+GitPtr BasicMainWindow::git(const QString &dir) const
+{
+ const_cast<BasicMainWindow *>(this)->checkGitCommand();
+
+ GitPtr g = std::make_shared<Git>(m->gcx, dir);
+ g->setLogCallback(git_callback, (void *)this);
+ return g;
+}
+
+GitPtr BasicMainWindow::git()
+{
+ return git(currentWorkingCopyDir());
+}
+
+QPixmap BasicMainWindow::getTransparentPixmap()
+{
+ return m->transparent_pixmap;
+}
+
+QIcon BasicMainWindow::committerIcon(int row) const
+{
+ QIcon icon;
+ if (isAvatarEnabled()) {
+ auto const &logs = getLogs();
+ if (row >= 0 && row < (int)logs.size()) {
+ Git::CommitItem const &commit = logs[row];
+ if (commit.email.indexOf('@') > 0) {
+ std::string email = commit.email.toStdString();
+ icon = getAvatarLoader()->fetch(email, true); // from gavatar
+ }
+ }
+ }
+ return icon;
+}
+
+const Git::CommitItem *BasicMainWindow::commitItem(int row) const
+{
+ auto const &logs = getLogs();
+ if (row >= 0 && row < (int)logs.size()) {
+ return &logs[row];
+ }
+ return nullptr;
+}
+
+QIcon BasicMainWindow::verifiedIcon(char s) const
+{
+ Git::SignatureGrade g = Git::evaluateSignature(s);
+ switch (g) {
+ case Git::SignatureGrade::Good:
+ return m->signature_good_icon;
+ case Git::SignatureGrade::Bad:
+ return m->signature_bad_icon;
+ case Git::SignatureGrade::Unknown:
+ case Git::SignatureGrade::Dubious:
+ case Git::SignatureGrade::Missing:
+ return m->signature_dubious_icon;
+ }
+ return QIcon();
+}
+
+QString BasicMainWindow::currentWorkingCopyDir() const
+{
+ QString workdir = m->current_repo.local_dir;
+ return workdir.isEmpty() ? QString() : workdir;
+}
+
+const QList<BasicMainWindow::Label> *BasicMainWindow::label(int row)
+{
+ auto it = getLabelMap()->find(row);
+ if (it != getLabelMap()->end()) {
+ return &it->second;
+ }
+ return nullptr;
+}
+
+bool BasicMainWindow::saveAs(const QString &id, const QString &dstpath)
+{
+ if (id.startsWith(PATH_PREFIX)) {
+ return saveFileAs(id.mid(1), dstpath);
+ } else {
+ return saveBlobAs(id, dstpath);
+ }
+}
+
+bool BasicMainWindow::testRemoteRepositoryValidity(const QString &url)
+{
+ bool ok;
+ {
+ OverrideWaitCursor;
+ ok = isValidRemoteURL(url);
+ }
+
+ QString pass = tr("The URL is a valid repository");
+ QString fail = tr("Failed to access the URL");
+
+ QString text = "%1\n\n%2";
+ text = text.arg(url).arg(ok ? pass : fail);
+
+ QString title = tr("Remote Repository");
+
+ if (ok) {
+ QMessageBox::information(this, title, text);
+ } else {
+ QMessageBox::critical(this, title, text);
+ }
+
+ return ok;
+}
+
+void BasicMainWindow::addWorkingCopyDir(QString dir, bool open)
+{
+ addWorkingCopyDir(dir, QString(), open);
+}
+
+bool BasicMainWindow::queryCommit(const QString &id, Git::CommitItem *out)
+{
+ *out = Git::CommitItem();
+ GitPtr g = git();
+ return g->queryCommit(id, out);
+}
+
+QAction *BasicMainWindow::addMenuActionProperty(QMenu *menu)
+{
+ return menu->addAction(tr("&Property"));
+}
+
+void BasicMainWindow::checkout(QWidget *parent, const Git::CommitItem *commit)
+{
+ if (!commit) return;
+
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return;
+
+ QStringList tags;
+ QStringList local_branches;
+ QStringList remote_branches;
+ {
+ NamedCommitList named_commits = namedCommitItems(Branches | Tags | Remotes);
+ JumpDialog::sort(&named_commits);
+ for (NamedCommitItem const &item : named_commits) {
+ if (item.id == commit->commit_id) {
+ QString name = item.name;
+ if (item.type == NamedCommitItem::Type::Tag) {
+ tags.push_back(name);
+ } else if (item.type == NamedCommitItem::Type::Branch) {
+ int i = name.lastIndexOf('/');
+ if (i < 0 && name == "HEAD") continue;
+ if (i > 0 && name.mid(i + 1) == "HEAD") continue;
+ if (item.remote.isNull()) {
+ local_branches.push_back(name);
+ } else {
+ remote_branches.push_back(name);
+ }
+ }
+ }
+ }
+ }
+
+ CheckoutDialog dlg(parent, tags, local_branches, remote_branches);
+ if (dlg.exec() == QDialog::Accepted) {
+ CheckoutDialog::Operation op = dlg.operation();
+ QString name = dlg.branchName();
+ QString id = commit->commit_id;
+ bool ok = false;
+ setLogEnabled(g, true);
+ if (op == CheckoutDialog::Operation::HeadDetached) {
+ ok = g->git(QString("checkout \"%1\"").arg(id), true);
+ } else if (op == CheckoutDialog::Operation::CreateLocalBranch) {
+ ok = g->git(QString("checkout -b \"%1\" \"%2\"").arg(name).arg(id), true);
+ } else if (op == CheckoutDialog::Operation::ExistingLocalBranch) {
+ ok = g->git(QString("checkout \"%1\"").arg(name), true);
+ }
+ if (ok) {
+ openRepository(true);
+ }
+ }
+}
+
+void BasicMainWindow::jumpToCommit(QString id)
+{
+ GitPtr g = git();
+ id = g->rev_parse(id);
+ if (!id.isEmpty()) {
+ int row = rowFromCommitId(id);
+ setCurrentLogRow(row);
+ }
+}
+
+void BasicMainWindow::execCommitViewWindow(const Git::CommitItem *commit)
+{
+ CommitViewWindow win(this, commit);
+ win.exec();
+}
+
+void BasicMainWindow::execCommitExploreWindow(QWidget *parent, const Git::CommitItem *commit)
+{
+ CommitExploreWindow win(parent, this, getObjCache(), commit);
+ win.exec();
+}
+
+void BasicMainWindow::execFileHistory(const QString &path)
+{
+ if (path.isEmpty()) return;
+
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return;
+
+ FileHistoryWindow dlg(this);
+ dlg.prepare(g, path);
+ dlg.exec();
+}
+
+void BasicMainWindow::execFilePropertyDialog(QListWidgetItem *item)
+{
+ if (item) {
+ QString path = getFilePath(item);
+ QString id = getObjectID(item);
+ FilePropertyDialog dlg(this);
+ dlg.exec(this, path, id);
+ }
+}
+
+QString BasicMainWindow::selectGitCommand(bool save)
+{
+ char const *exe = GIT_COMMAND;
+
+ QString path = gitCommand();
+
+ auto fn = [&](QString const &path){
+ setGitCommand(path, save);
+ };
+
+ QStringList list = whichCommand_(exe);
+#ifdef Q_OS_WIN
+ {
+ QStringList newlist;
+ QString suffix1 = "\\bin\\" GIT_COMMAND;
+ QString suffix2 = "\\cmd\\" GIT_COMMAND;
+ for (QString const &s : list) {
+ newlist.push_back(s);
+ auto DoIt = [&](QString const &suffix){
+ if (s.endsWith(suffix)) {
+ QString t = s.mid(0, s.size() - suffix.size());
+ QString t1 = t + "\\mingw64\\bin\\" GIT_COMMAND;
+ if (misc::isExecutable(t1)) newlist.push_back(t1);
+ QString t2 = t + "\\mingw\\bin\\" GIT_COMMAND;
+ if (misc::isExecutable(t2)) newlist.push_back(t2);
+ }
+ };
+ DoIt(suffix1);
+ DoIt(suffix2);
+ }
+ std::sort(newlist.begin(), newlist.end());
+ auto end = std::unique(newlist.begin(), newlist.end());
+ list.clear();
+ for (auto it = newlist.begin(); it != end; it++) {
+ list.push_back(*it);
+ }
+ }
+#endif
+ return selectCommand_("Git", exe, list, path, fn);
+}
+
+QString BasicMainWindow::selectFileCommand(bool save)
+{
+ char const *exe = FILE_COMMAND;
+
+ QString path = global->file_command;
+
+ auto fn = [&](QString const &path){
+ setFileCommand(path, save);
+ };
+
+ QStringList list = whichCommand_(exe);
+
+#ifdef Q_OS_WIN
+ QString dir = misc::getApplicationDir();
+ QString path1 = dir / FILE_COMMAND;
+ QString path2;
+ int i = dir.lastIndexOf('/');
+ int j = dir.lastIndexOf('\\');
+ if (i < j) i = j;
+ if (i > 0) {
+ path2 = dir.mid(0, i) / FILE_COMMAND;
+ }
+ path1 = misc::normalizePathSeparator(path1);
+ path2 = misc::normalizePathSeparator(path2);
+ if (misc::isExecutable(path1)) list.push_back(path1);
+ if (misc::isExecutable(path2)) list.push_back(path2);
+#endif
+
+ return selectCommand_("File", exe, list, path, fn);
+}
+
+QString BasicMainWindow::selectGpgCommand(bool save)
+{
+ QString path = global->gpg_command;
+
+ auto fn = [&](QString const &path){
+ setGpgCommand(path, save);
+ };
+
+ QStringList list = whichCommand_(GPG_COMMAND, GPG2_COMMAND);
+
+ QStringList cmdlist;
+ cmdlist.push_back(GPG_COMMAND);
+ cmdlist.push_back(GPG2_COMMAND);
+ return selectCommand_("GPG", cmdlist, list, path, fn);
+}
+
+const Git::Branch &BasicMainWindow::currentBranch() const
+{
+ return m->current_branch;
+}
+
+void BasicMainWindow::setCurrentBranch(const Git::Branch &b)
+{
+ m->current_branch = b;
+}
+
+QString BasicMainWindow::currentRepositoryName() const
+{
+ return m->current_repo.name;
+}
+
+QString BasicMainWindow::currentRemoteName() const
+{
+ return m->current_remote_name;
+}
+
+QString BasicMainWindow::currentBranchName() const
+{
+ return currentBranch().name;
+}
+
+bool BasicMainWindow::isValidWorkingCopy(const GitPtr &g) const
+{
+ return g && g->isValidWorkingCopy();
+}
+
+QString BasicMainWindow::determinFileType_(const QString &path, bool mime, std::function<void (const QString &, QByteArray *)> callback) const
+{
+ const_cast<BasicMainWindow *>(this)->checkFileCommand();
+ return misc::determinFileType(global->file_command, path, mime, callback);
+}
+
+QString BasicMainWindow::determinFileType(const QString &path, bool mime)
+{
+ return determinFileType_(path, mime, [](QString const &cmd, QByteArray *ba){
+ misc2::runCommand(cmd, ba);
+ });
+}
+
+QString BasicMainWindow::determinFileType(QByteArray in, bool mime)
+{
+ if (in.isEmpty()) return QString();
+
+ if (in.size() > 10) {
+ if (memcmp(in.data(), "\x1f\x8b\x08", 3) == 0) { // gzip
+ QBuffer buf;
+ MemoryReader reader(in.data(), in.size());
+ reader.open(MemoryReader::ReadOnly);
+ buf.open(QBuffer::WriteOnly);
+ gunzip z;
+ z.set_maximul_size(100000);
+ z.decode(&reader, &buf);
+ in = buf.buffer();
+ }
+ }
+
+ // ファイル名を "-" にすると、リダイレクトで標準入力へ流し込める。
+ return determinFileType_("-", mime, [&](QString const &cmd, QByteArray *ba){
+ int r = misc2::runCommand(cmd, &in, ba);
+ if (r != 0) {
+ ba->clear();
+ }
+ });
+}
+
+QList<Git::Tag> BasicMainWindow::queryTagList()
+{
+ QList<Git::Tag> list;
+ Git::CommitItem const *commit = selectedCommitItem();
+ if (commit && Git::isValidID(commit->commit_id)) {
+ list = findTag(commit->commit_id);
+ }
+ return list;
+}
+
+int BasicMainWindow::limitLogCount() const
+{
+ int n = appsettings()->maximum_number_of_commit_item_acquisitions;
+ return (n >= 1 && n <= 100000) ? n : 10000;
+}
+
+Git::Object BasicMainWindow::cat_file_(GitPtr g, const QString &id)
+{
+ if (isValidWorkingCopy(g)) {
+ QString path_prefix = PATH_PREFIX;
+ if (id.startsWith(path_prefix)) {
+ QString path = g->workingRepositoryDir();
+ path = path / id.mid(path_prefix.size());
+ QFile file(path);
+ if (file.open(QFile::ReadOnly)) {
+ Git::Object obj;
+ obj.content = file.readAll();
+ return obj;
+ }
+ } else if (Git::isValidID(id)) {
+ return getObjCache()->catFile(id);;
+ }
+ }
+ return Git::Object();
+}
+
+Git::Object BasicMainWindow::cat_file(const QString &id)
+{
+ return cat_file_(git(), id);
+}
+
+QString BasicMainWindow::newTempFilePath()
+{
+ QString tmpdir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+ QString path = tmpdir / tempfileHeader() + QString::number(m->temp_file_counter);
+ m->temp_file_counter++;
+ return path;
+}
+
+QString BasicMainWindow::findFileID(GitPtr, const QString &commit_id, const QString &file)
+{
+ return lookupFileID(getObjCache(), commit_id, file);
+}
+
+void BasicMainWindow::updateFilesList(QString const &id, QList<Git::Diff> *diff_list, QListWidget *listwidget)
+{
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return;
+
+ listwidget->clear();
+
+ auto AddItem = [&](QString const &filename, QString header, int idiff){
+ if (header.isEmpty()) {
+ header = "(??\?) "; // damn trigraph
+ }
+ QListWidgetItem *item = new QListWidgetItem(header + filename);
+ item->setData(FilePathRole, filename);
+ item->setData(DiffIndexRole, idiff);
+ item->setData(HunkIndexRole, -1);
+ listwidget->addItem(item);
+ };
+
+ GitDiff dm(getObjCache());
+ if (!dm.diff(id, diff_list)) return;
+
+ addDiffItems(diff_list, AddItem);
+}
+
+void BasicMainWindow::writeLog(const char *ptr, int len)
+{
+ internalWriteLog(ptr, len);
+}
+
+void BasicMainWindow::setAppSettings(const ApplicationSettings &appsettings)
+{
+ m->appsettings = appsettings;
+}
+
+QPixmap BasicMainWindow::getTransparentPixmap() const
+{
+ return m->transparent_pixmap;
+}
+
+QIcon BasicMainWindow::getSignatureBadIcon() const
+{
+ return m->signature_bad_icon;
+}
+
+QIcon BasicMainWindow::getSignatureDubiousIcon() const
+{
+ return m->signature_dubious_icon;
+}
+
+QIcon BasicMainWindow::getSignatureGoodIcon() const
+{
+ return m->signature_good_icon;
+}
+
+QIcon BasicMainWindow::getFolderIcon() const
+{
+ return m->folder_icon;
+}
+
+QIcon BasicMainWindow::getRepositoryIcon() const
+{
+ return m->repository_icon;
+}
+
+const Git::CommitItemList &BasicMainWindow::getLogs() const
+{
+ return m->logs;
+}
+
+Git::CommitItem const *BasicMainWindow::getLog(int index) const
+{
+ return (index >= 0 && index < (int)m->logs.size()) ? &m->logs[index] : nullptr;
+}
+
+Git::CommitItemList *BasicMainWindow::getLogsPtr()
+{
+ return &m->logs;
+}
+
+void BasicMainWindow::setLogs(const Git::CommitItemList &logs)
+{
+ m->logs = logs;
+}
+
+void BasicMainWindow::clearLogs()
+{
+ m->logs.clear();
+}
+
+BasicMainWindow::PtyCondition BasicMainWindow::getPtyCondition()
+{
+ return m->pty_condition;
+}
+
+void BasicMainWindow::setPtyCondition(const PtyCondition &ptyCondition)
+{
+ m->pty_condition = ptyCondition;
+}
+
+PtyProcess *BasicMainWindow::getPtyProcess()
+{
+ return &m->pty_process;
+}
+
+bool BasicMainWindow::getPtyProcessOk() const
+{
+ return m->pty_process_ok;
+}
+
+void BasicMainWindow::setPtyProcessOk(bool pty_process_ok)
+{
+ m->pty_process_ok = pty_process_ok;
+}
+
+const QList<RepositoryItem> &BasicMainWindow::getRepos() const
+{
+ return m->repos;
+}
+
+QList<RepositoryItem> *BasicMainWindow::getReposPtr()
+{
+ return &m->repos;
+}
+
+void BasicMainWindow::setCurrentRemoteName(const QString &name)
+{
+ m->current_remote_name = name;
+}
+
+AvatarLoader *BasicMainWindow::getAvatarLoader()
+{
+ return &m->avatar_loader;
+}
+
+AvatarLoader const *BasicMainWindow::getAvatarLoader() const
+{
+ return &m->avatar_loader;
+}
+
+int *BasicMainWindow::ptrUpdateFilesListCounter()
+{
+ return &m->update_files_list_counter;
+}
+
+int *BasicMainWindow::ptrUpdateCommitTableCounter()
+{
+ return &m->update_commit_table_counter;
+}
+
+bool BasicMainWindow::interactionCanceled() const
+{
+ return m->interaction_canceled;
+}
+
+void BasicMainWindow::setInteractionCanceled(bool canceled)
+{
+ m->interaction_canceled = canceled;
+}
+
+BasicMainWindow::InteractionMode BasicMainWindow::interactionMode() const
+{
+ return m->interaction_mode;
+}
+
+void BasicMainWindow::setInteractionMode(const InteractionMode &im)
+{
+ m->interaction_mode = im;
+}
+
+QString BasicMainWindow::getRepositoryFilterText() const
+{
+ return m->repository_filter_text;
+}
+
+void BasicMainWindow::setRepositoryFilterText(const QString &text)
+{
+ m->repository_filter_text = text;
+}
+
+void BasicMainWindow::setUncommitedChanges(bool uncommited_changes)
+{
+ m->uncommited_changes = uncommited_changes;
+}
+
+QList<Git::Diff> *BasicMainWindow::diffResult()
+{
+ return &m->diff_result;
+}
+
+std::map<QString, Git::Diff> *BasicMainWindow::getDiffCacheMap()
+{
+ return &m->diff_cache;
+}
+
+bool BasicMainWindow::getRemoteChanged() const
+{
+ return m->remote_changed;
+}
+
+void BasicMainWindow::setRemoteChanged(bool remote_changed)
+{
+ m->remote_changed = remote_changed;
+}
+
+void BasicMainWindow::setServerType(const ServerType &server_type)
+{
+ m->server_type = server_type;
+}
+
+GitHubRepositoryInfo *BasicMainWindow::ptrGitHub()
+{
+ return &m->github;
+}
+
+std::map<int, QList<BasicMainWindow::Label>> *BasicMainWindow::getLabelMap()
+{
+ return &m->label_map;
+}
+
+void BasicMainWindow::clearLabelMap()
+{
+ m->label_map.clear();
+}
+
+GitObjectCache *BasicMainWindow::getObjCache()
+{
+ return &m->objcache;
+}
+
+
+
+bool BasicMainWindow::getForceFetch() const
+{
+ return m->force_fetch;
+}
+
+void BasicMainWindow::setForceFetch(bool force_fetch)
+{
+ m->force_fetch = force_fetch;
+}
+
+std::map<QString, QList<Git::Tag>> *BasicMainWindow::ptrTagMap()
+{
+ return &m->tag_map;
+}
+
+QString BasicMainWindow::getHeadId() const
+{
+ return m->head_id;
+}
+
+void BasicMainWindow::setHeadId(const QString &head_id)
+{
+ m->head_id = head_id;
+}
+
+const RepositoryItem &BasicMainWindow::getTempRepoForCloneComplete() const
+{
+ return m->temp_repo_for_clone_complete;
+}
+
+bool BasicMainWindow::git_callback(void *cookie, const char *ptr, int len)
+{
+ auto *mw = (BasicMainWindow *)cookie;
+ mw->emitWriteLog(QByteArray(ptr, len));
+ return true;
+}
+
+QString BasicMainWindow::selectCommand_(const QString &cmdname, const QStringList &cmdfiles, const QStringList &list, QString path, std::function<void (const QString &)> const &callback)
+{
+ QString window_title = tr("Select %1 command");
+ window_title = window_title.arg(cmdfiles.front());
+
+ SelectCommandDialog dlg(this, cmdname, cmdfiles, path, list);
+ dlg.setWindowTitle(window_title);
+ if (dlg.exec() == QDialog::Accepted) {
+ path = dlg.selectedFile();
+ path = misc::normalizePathSeparator(path);
+ QFileInfo info(path);
+ if (info.isExecutable()) {
+ callback(path);
+ return path;
+ }
+ }
+ return QString();
+}
+
+QString BasicMainWindow::selectCommand_(const QString &cmdname, const QString &cmdfile, const QStringList &list, QString const &path, std::function<void (const QString &)> const &callback)
+{
+ QStringList cmdfiles;
+ cmdfiles.push_back(cmdfile);
+ return selectCommand_(cmdname, cmdfiles, list, path, callback);
+}
+
+bool BasicMainWindow::checkGitCommand()
+{
+ while (1) {
+ if (misc::isExecutable(gitCommand())) {
+ return true;
+ }
+ if (selectGitCommand(true).isEmpty()) {
+ close();
+ return false;
+ }
+ }
+}
+
+bool BasicMainWindow::checkFileCommand()
+{
+ while (1) {
+ if (misc::isExecutable(global->file_command)) {
+ return true;
+ }
+ if (selectFileCommand(true).isEmpty()) {
+ close();
+ return false;
+ }
+ }
+}
+
+bool BasicMainWindow::checkExecutable(const QString &path)
+{
+ if (QFileInfo(path).isExecutable()) {
+ return true;
+ }
+ QString text = "The specified program '%1' is not executable.\n";
+ text = text.arg(path);
+ writeLog(text);
+ return false;
+}
+
+void BasicMainWindow::internalSetCommand(const QString &path, bool save, const QString &name, QString *out)
+{
+ if (!path.isEmpty() && checkExecutable(path)) {
+ if (save) {
+ MySettings s;
+ s.beginGroup("Global");
+ s.setValue(name, path);
+ s.endGroup();
+ }
+ *out = path;
+ } else {
+ *out = QString();
+ }
+}
+
+QStringList BasicMainWindow::whichCommand_(const QString &cmdfile1, const QString &cmdfile2)
+{
+ QStringList list;
+
+ if (!cmdfile1.isEmpty()){
+ std::vector<std::string> vec;
+ FileUtil::which(cmdfile1.toStdString(), &vec);
+ for (std::string const &s : vec) {
+ list.push_back(QString::fromStdString(s));
+ }
+ }
+ if (!cmdfile2.isEmpty()){
+ std::vector<std::string> vec;
+ FileUtil::which(cmdfile2.toStdString(), &vec);
+ for (std::string const &s : vec) {
+ list.push_back(QString::fromStdString(s));
+ }
+ }
+
+ return list;
+}
+
+void BasicMainWindow::emitWriteLog(QByteArray ba)
+{
+ emit signalWriteLog(ba);
+}
+
+void BasicMainWindow::setWindowTitle_(const Git::User &user)
+{
+ if (user.name.isEmpty() && user.email.isEmpty()) {
+ setWindowTitle(qApp->applicationName());
+ } else {
+ setWindowTitle(QString("%1 : %2 <%3>")
+ .arg(qApp->applicationName())
+ .arg(user.name)
+ .arg(user.email)
+ );
+ }
+}
+
+bool BasicMainWindow::execSetGlobalUserDialog()
+{
+ SetGlobalUserDialog dlg(this);
+ if (dlg.exec() == QDialog::Accepted) {
+ GitPtr g = git();
+ Git::User user = dlg.user();
+ g->setUser(user, true);
+ updateWindowTitle(g);
+ return true;
+ }
+ return false;
+}
+
+bool BasicMainWindow::saveByteArrayAs(const QByteArray &ba, const QString &dstpath)
+{
+ QFile file(dstpath);
+ if (file.open(QFile::WriteOnly)) {
+ file.write(ba);
+ file.close();
+ return true;
+ }
+ QString msg = "Failed to open the file '%1' for write";
+ msg = msg.arg(dstpath);
+ qDebug() << msg;
+ return false;
+}
+
+bool BasicMainWindow::saveFileAs(const QString &srcpath, const QString &dstpath)
+{
+ QFile f(srcpath);
+ if (f.open(QFile::ReadOnly)) {
+ QByteArray ba = f.readAll();
+ if (saveByteArrayAs(ba, dstpath)) {
+ return true;
+ }
+ } else {
+ QString msg = "Failed to open the file '%1' for read";
+ msg = msg.arg(srcpath);
+ qDebug() << msg;
+ }
+ return false;
+}
+
+bool BasicMainWindow::saveBlobAs(const QString &id, const QString &dstpath)
+{
+ Git::Object obj = cat_file(id);
+ if (!obj.content.isEmpty()) {
+ if (saveByteArrayAs(obj.content, dstpath)) {
+ return true;
+ }
+ } else {
+ QString msg = "Failed to get the content of the object '%1'";
+ msg = msg.arg(id);
+ qDebug() << msg;
+ }
+ return false;
+}
+
+void BasicMainWindow::revertAllFiles()
+{
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return;
+
+ QString cmd = "git reset --hard HEAD";
+ if (askAreYouSureYouWantToRun(tr("Revert all files"), "> " + cmd)) {
+ g->resetAllFiles();
+ openRepository(true);
+ }
+}
+
+void BasicMainWindow::deleteTags(const Git::CommitItem &commit)
+{
+ auto it = ptrTagMap()->find(commit.commit_id);
+ if (it != ptrTagMap()->end()) {
+ QStringList names;
+ QList<Git::Tag> const &tags = it->second;
+ for (Git::Tag const &tag : tags) {
+ names.push_back(tag.name);
+ }
+ deleteTags(names);
+ }
+}
+
+bool BasicMainWindow::isAvatarEnabled() const
+{
+ return appsettings()->get_committer_icon;
+}
+
+bool BasicMainWindow::isGitHub() const
+{
+ return m->server_type == ServerType::GitHub;
+}
+
+QStringList BasicMainWindow::remotes() const
+{
+ return m->remotes;
+}
+
+QList<Git::Branch> BasicMainWindow::findBranch(const QString &id)
+{
+ auto it = branchMapRef().find(id);
+ if (it != branchMapRef().end()) {
+ return it->second;
+ }
+ return QList<Git::Branch>();
+}
+
+QString BasicMainWindow::saveAsTemp(const QString &id)
+{
+ QString path = newTempFilePath();
+ saveAs(id, path);
+ return path;
+}
+
+QString BasicMainWindow::tempfileHeader() const
+{
+ QString name = "jp_soramimi_Guitar_%1_";
+ return name.arg(QApplication::applicationPid());
+}
+
+void BasicMainWindow::deleteTempFiles()
+{
+ QString dir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+ QString name = tempfileHeader();
+ QDirIterator it(dir, { name + "*" });
+ while (it.hasNext()) {
+ QString path = it.next();
+ QFile::remove(path);
+ }
+}
+
+QString BasicMainWindow::getCommitIdFromTag(const QString &tag)
+{
+ return getObjCache()->getCommitIdFromTag(tag);
+}
+
+bool BasicMainWindow::isValidRemoteURL(const QString &url)
+{
+ if (url.indexOf('\"') >= 0) {
+ return false;
+ }
+ stopPtyProcess();
+ GitPtr g = git(QString());
+ QString cmd = "ls-remote \"%1\" HEAD";
+ bool f = g->git(cmd.arg(url), false, false, getPtyProcess());
+ {
+ QTime time;
+ time.start();
+ while (!getPtyProcess()->wait(10)) {
+ if (time.elapsed() > 10000) {
+ f = false;
+ break;
+ }
+ QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+ }
+ stopPtyProcess();
+ }
+ QString line = g->resultText().trimmed();
+ QString head;
+ int i = line.indexOf('\t');
+ if (i == GIT_ID_LENGTH) {
+ QString id = line.mid(0, i);
+ QString name = line.mid(i + 1);
+ qDebug() << id << name;
+ if (name == "HEAD" && Git::isValidID(id)) {
+ head = id;
+ }
+ }
+ if (f) {
+ qDebug() << "This is a valid repository.";
+ if (head.isEmpty()) {
+ qDebug() << "But HEAD not found";
+ }
+ return true;
+ }
+ qDebug() << "It is not a repository.";
+ return false;
+}
+
+void BasicMainWindow::addWorkingCopyDir(QString dir, QString name, bool open)
+{
+ if (dir.endsWith(".git")) {
+ int i = dir.size();
+ if (i > 4) {
+ ushort c = dir.utf16()[i - 5];
+ if (c == '/' || c == '\\') {
+ dir = dir.mid(0, i - 5);
+ }
+ }
+ }
+
+ if (!Git::isValidWorkingCopy(dir)) {
+ qDebug() << "Invalid working dir: " + dir;
+ return;
+ }
+
+ if (name.isEmpty()) {
+ name = makeRepositoryName(dir);
+ }
+
+ RepositoryItem item;
+ item.local_dir = dir;
+ item.name = name;
+ saveRepositoryBookmark(item);
+
+ if (open) {
+ setCurrentRepository(item, true);
+ GitPtr g = git(item.local_dir);
+ openRepository_(g);
+ }
+}
+
+QString BasicMainWindow::makeRepositoryName(const QString &loc)
+{
+ int i = loc.lastIndexOf('/');
+ int j = loc.lastIndexOf('\\');
+ if (i < j) i = j;
+ if (i >= 0) {
+ i++;
+ j = loc.size();
+ if (loc.endsWith(".git")) {
+ j -= 4;
+ }
+ return loc.mid(i, j - i);
+ }
+ return QString();
+}
+
+void BasicMainWindow::clearAuthentication()
+{
+ clearSshAuthentication();
+ m->http_uid.clear();
+ m->http_pwd.clear();
+}
+
+void BasicMainWindow::onAvatarUpdated()
+{
+ updateCommitTableLater();
+}
+
+void BasicMainWindow::queryRemotes(GitPtr g)
+{
+ m->remotes = g->getRemotes();
+ std::sort(m->remotes.begin(), m->remotes.end());
+}
+
+bool BasicMainWindow::runOnRepositoryDir(std::function<void (QString)> callback, const RepositoryItem *repo)
+{
+ if (!repo) {
+ repo = &m->current_repo;
+ }
+ QString dir = repo->local_dir;
+ dir.replace('\\', '/');
+ if (QFileInfo(dir).isDir()) {
+ callback(dir);
+ return true;
+ }
+ QMessageBox::warning(this, qApp->applicationName(), tr("No repository selected"));
+ return false;
+}
+
+void BasicMainWindow::clearSshAuthentication()
+{
+ m->ssh_passphrase_user.clear();
+ m->ssh_passphrase_pass.clear();
+}
+
+QString BasicMainWindow::getFilePath(QListWidgetItem *item)
+{
+ if (!item) return QString();
+ return item->data(FilePathRole).toString();
+}
+
+bool BasicMainWindow::isGroupItem(QTreeWidgetItem *item)
+{
+ if (item) {
+ int index = item->data(0, IndexRole).toInt();
+ if (index == GroupItem) {
+ return true;
+ }
+ }
+ return false;
+}
+
+int BasicMainWindow::indexOfLog(QListWidgetItem *item)
+{
+ if (!item) return -1;
+ return item->data(IndexRole).toInt();
+}
+
+int BasicMainWindow::indexOfDiff(QListWidgetItem *item)
+{
+ if (!item) return -1;
+ return item->data(DiffIndexRole).toInt();
+}
+
+int BasicMainWindow::getHunkIndex(QListWidgetItem *item)
+{
+ if (!item) return -1;
+ return item->data(HunkIndexRole).toInt();
+}
+
+void BasicMainWindow::initNetworking()
+{
+ std::string http_proxy;
+ std::string https_proxy;
+ if (appsettings()->proxy_type == "auto") {
+#ifdef Q_OS_WIN
+ http_proxy = misc::makeProxyServerURL(getWin32HttpProxy().toStdString());
+#else
+ auto getienv = [](std::string const &name)->char const *{
+ char **p = environ;
+ while (*p) {
+ if (strncasecmp(*p, name.c_str(), name.size()) == 0) {
+ char const *e = *p + name.size();
+ if (*e == '=') {
+ return e + 1;
+ }
+ }
+ p++;
+ }
+ return nullptr;
+ };
+ char const *p;
+ p = getienv("http_proxy");
+ if (p) {
+ http_proxy = misc::makeProxyServerURL(std::string(p));
+ }
+ p = getienv("https_proxy");
+ if (p) {
+ https_proxy = misc::makeProxyServerURL(std::string(p));
+ }
+#endif
+ } else if (appsettings()->proxy_type == "manual") {
+ http_proxy = appsettings()->proxy_server.toStdString();
+ }
+ webContext()->set_http_proxy(http_proxy);
+ webContext()->set_https_proxy(https_proxy);
+}
+
+QString BasicMainWindow::abbrevCommitID(const Git::CommitItem &commit)
+{
+ return commit.commit_id.mid(0, 7);
+}
+
+bool BasicMainWindow::saveRepositoryBookmarks() const
+{
+ QString path = getBookmarksFilePath();
+ return RepositoryBookmark::save(path, &getRepos());
+}
+
+QString BasicMainWindow::getBookmarksFilePath() const
+{
+ return global->app_config_dir / "bookmarks.xml";
+}
+
+void BasicMainWindow::stopPtyProcess()
+{
+ getPtyProcess()->stop();
+ QDir::setCurrent(m->starting_dir);
+}
+
+void BasicMainWindow::abortPtyProcess()
+{
+ stopPtyProcess();
+ setPtyProcessOk(false);
+ setInteractionCanceled(true);
+}
+
+bool BasicMainWindow::execWelcomeWizardDialog()
+{
+ WelcomeWizardDialog dlg(this);
+ dlg.set_git_command_path(appsettings()->git_command);
+ dlg.set_file_command_path(appsettings()->file_command);
+ dlg.set_default_working_folder(appsettings()->default_working_dir);
+ if (misc::isExecutable(appsettings()->git_command)) {
+ gitCommand() = appsettings()->git_command;
+ Git g(m->gcx, QString());
+ Git::User user = g.getUser(Git::Source::Global);
+ dlg.set_user_name(user.name);
+ dlg.set_user_email(user.email);
+ }
+ if (dlg.exec() == QDialog::Accepted) {
+ appsettings()->git_command = gitCommand() = dlg.git_command_path();
+ appsettings()->file_command = global->file_command = dlg.file_command_path();
+ appsettings()->default_working_dir = dlg.default_working_folder();
+ SettingsDialog::saveSettings(&m->appsettings);
+
+ if (misc::isExecutable(appsettings()->git_command)) {
+ GitPtr g = git();
+ Git::User user;
+ user.name = dlg.user_name();
+ user.email = dlg.user_email();
+ g->setUser(user, true);
+ }
+
+ return true;
+ }
+ return false;
+}
+
+void BasicMainWindow::execRepositoryPropertyDialog(QString workdir, bool open_repository_menu)
+{
+ if (workdir.isEmpty()) {
+ workdir = currentWorkingCopyDir();
+ }
+ QString name;
+ RepositoryItem const *repo = findRegisteredRepository(&workdir);
+ if (repo) {
+ name = repo->name;
+ } else {
+ QMessageBox::warning(this, tr("Repository Property"), tr("Not a valid git repository") + "\n\n" + workdir);
+ return;
+ }
+ if (name.isEmpty()) {
+ name = makeRepositoryName(workdir);
+ }
+ GitPtr g = git(workdir);
+ RepositoryPropertyDialog dlg(this, g, *repo, open_repository_menu);
+ dlg.exec();
+ if (dlg.isRemoteChanged()) {
+ emit remoteInfoChanged();
+ }
+}
+
+void BasicMainWindow::execSetUserDialog(const Git::User &global_user, const Git::User &repo_user, const QString &reponame)
+{
+ SetUserDialog dlg(this, global_user, repo_user, reponame);
+ if (dlg.exec() == QDialog::Accepted) {
+ GitPtr g = git();
+ Git::User user = dlg.user();
+ if (dlg.isGlobalChecked()) {
+ g->setUser(user, true);
+ }
+ if (dlg.isRepositoryChecked()) {
+ g->setUser(user, false);
+ }
+ updateWindowTitle(g);
+ }
+}
+
+void BasicMainWindow::setGitCommand(const QString &path, bool save)
+{
+ internalSetCommand(path, save, "GitCommand", &m->gcx.git_command);
+}
+
+void BasicMainWindow::setFileCommand(const QString &path, bool save)
+{
+ internalSetCommand(path, save, "FileCommand", &global->file_command);
+}
+
+void BasicMainWindow::setGpgCommand(const QString &path, bool save)
+{
+ internalSetCommand(path, save, "GpgCommand", &global->gpg_command);
+ if (!global->gpg_command.isEmpty()) {
+ GitPtr g = git();
+ g->configGpgProgram(global->gpg_command, true);
+ }
+}
+
+void BasicMainWindow::logGitVersion()
+{
+ GitPtr g = git();
+ QString s = g->version();
+ if (!s.isEmpty()) {
+ s += '\n';
+ writeLog(s);
+ }
+}
+
+void BasicMainWindow::setUnknownRepositoryInfo()
+{
+ setRepositoryInfo("---", "");
+
+ Git g(m->gcx, QString());
+ Git::User user = g.getUser(Git::Source::Global);
+ setWindowTitle_(user);
+}
+
+void BasicMainWindow::internalClearRepositoryInfo()
+{
+ setHeadId(QString());
+ setCurrentBranch(Git::Branch());
+ setServerType(ServerType::Standard);
+ m->github = GitHubRepositoryInfo();
+}
+
+void BasicMainWindow::checkUser()
+{
+ Git g(m->gcx, QString());
+ while (1) {
+ Git::User user = g.getUser(Git::Source::Global);
+ if (!user.name.isEmpty() && !user.email.isEmpty()) {
+ return; // ok
+ }
+ if (!execSetGlobalUserDialog()) {
+ return;
+ }
+ }
+}
+
+void BasicMainWindow::openRepository(bool validate, bool waitcursor)
+{
+ if (validate) {
+ QString dir = currentWorkingCopyDir();
+ if (!QFileInfo(dir).isDir()) {
+ int r = QMessageBox::warning(this, tr("Open Repository"), dir + "\n\n" + tr("No such folder") + "\n\n" + tr("Remove from bookmark ?"), QMessageBox::Ok, QMessageBox::Cancel);
+ if (r == QMessageBox::Ok) {
+ removeSelectedRepositoryFromBookmark(false);
+ }
+ return;
+ }
+ if (!Git::isValidWorkingCopy(dir)) {
+ QMessageBox::warning(this, tr("Open Repository"), tr("Not a valid git repository") + "\n\n" + dir);
+ return;
+ }
+ }
+
+ if (waitcursor) {
+ OverrideWaitCursor;
+ openRepository(false, false);
+ return;
+ }
+
+ GitPtr g = git(); // ポインタの有効性チェックはしない(nullptrでも続行)
+ openRepository_(g);
+}
+
+void BasicMainWindow::reopenRepository(bool log, std::function<void (GitPtr)> callback)
+{
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return;
+
+ OverrideWaitCursor;
+ if (log) {
+ setLogEnabled(g, true);
+ AsyncExecGitThread_ th(g, callback);
+ th.start();
+ while (1) {
+ if (th.wait(1)) break;
+ QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+ }
+ setLogEnabled(g, false);
+ } else {
+ callback(g);
+ }
+ openRepository_(g);
+}
+
+void BasicMainWindow::openSelectedRepository()
+{
+ RepositoryItem const *repo = selectedRepositoryItem();
+ if (repo) {
+ setCurrentRepository(*repo, true);
+ openRepository(true);
+ }
+}
+
+void BasicMainWindow::checkRemoteUpdate()
+{
+ if (getPtyProcess()->isRunning()) return;
+
+ emit signalCheckRemoteUpdate();
+}
+
+bool BasicMainWindow::isThereUncommitedChanges() const
+{
+ return m->uncommited_changes;
+}
+
+bool BasicMainWindow::makeDiff(QString id, QList<Git::Diff> *out)
+{
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return false;
+
+ Git::FileStatusList list = g->status();
+ setUncommitedChanges(!list.empty());
+
+ if (id.isEmpty() && !isThereUncommitedChanges()) {
+ id = getObjCache()->revParse("HEAD");
+ }
+
+ bool uncommited = (id.isEmpty() && isThereUncommitedChanges());
+
+ GitDiff dm(getObjCache());
+ if (uncommited) {
+ dm.diff_uncommited(out);
+ } else {
+ dm.diff(id, out);
+ }
+
+ return true; // success
+}
+
+void BasicMainWindow::addDiffItems(const QList<Git::Diff> *diff_list, const std::function<void (const QString &, QString, int)> &add_item)
+{
+ for (int idiff = 0; idiff < diff_list->size(); idiff++) {
+ Git::Diff const &diff = diff_list->at(idiff);
+ QString header;
+
+ switch (diff.type) {
+ case Git::Diff::Type::Modify: header = "(chg) "; break;
+ case Git::Diff::Type::Copy: header = "(cpy) "; break;
+ case Git::Diff::Type::Rename: header = "(ren) "; break;
+ case Git::Diff::Type::Create: header = "(add) "; break;
+ case Git::Diff::Type::Delete: header = "(del) "; break;
+ case Git::Diff::Type::ChType: header = "(chg) "; break;
+ case Git::Diff::Type::Unmerged: header = "(unmerged) "; break;
+ }
+
+ add_item(diff.path, header, idiff);
+ }
+}
+
+Git::CommitItemList BasicMainWindow::retrieveCommitLog(GitPtr g)
+{
+ Git::CommitItemList list = g->log(limitLogCount());
+
+ // 親子関係を調べて、順番が狂っていたら、修正する。
+
+ std::set<QString> set;
+
+ size_t i = 0;
+ while (i < list.size()) {
+ size_t newpos = -1;
+ for (QString const &parent : list[i].parent_ids) {
+ auto it = set.find(parent);
+ if (it != set.end()) {
+ for (size_t j = 0; j < i; j++) {
+ if (parent == list[j].commit_id) {
+ if (newpos == (size_t)-1 || j < newpos) {
+ newpos = j;
+ }
+ qDebug() << "fix commit order" << parent;
+ break;
+ }
+ }
+ }
+ }
+ set.insert(set.end(), list[i].commit_id);
+ if (newpos != (size_t)-1) {
+ Git::CommitItem t = list[newpos];
+ list.erase(list.begin() + newpos);
+ list.insert(list.begin() + i, t);
+ }
+ i++;
+ }
+
+ return list;
+}
+
+void BasicMainWindow::queryBranches(GitPtr g)
+{
+ Q_ASSERT(g);
+ m->branch_map.clear();
+ QList<Git::Branch> branches = g->branches();
+ for (Git::Branch const &b : branches) {
+ if (b.isCurrent()) {
+ setCurrentBranch(b);
+ }
+ branchMapRef()[b.id].append(b);
+ }
+}
+
+std::map<QString, QList<Git::Branch> > &BasicMainWindow::branchMapRef()
+{
+ return m->branch_map;
+}
+
+void BasicMainWindow::updateCommitTableLater()
+{
+ *ptrUpdateCommitTableCounter() = 200;
+}
+
+void BasicMainWindow::updateRemoteInfo()
+{
+ queryRemotes(git());
+
+ m->current_remote_name = QString();
+ {
+ Git::Branch const &r = currentBranch();
+ m->current_remote_name = r.remote;
+ }
+ if (m->current_remote_name.isEmpty()) {
+ if (m->remotes.size() == 1) {
+ m->current_remote_name = m->remotes[0];
+ }
+ }
+
+ emit remoteInfoChanged();
+}
+
+void BasicMainWindow::updateWindowTitle(GitPtr g)
+{
+ if (isValidWorkingCopy(g)) {
+ Git::User user = g->getUser(Git::Source::Default);
+ setWindowTitle_(user);
+ } else {
+ setUnknownRepositoryInfo();
+ }
+}
+
+QString BasicMainWindow::makeCommitInfoText(int row, QList<BasicMainWindow::Label> *label_list)
+{
+ QString message_ex;
+ Git::CommitItem const *commit = &getLogs()[row];
+ { // branch
+ if (label_list) {
+ if (commit->commit_id == getHeadId()) {
+ Label label(Label::Head);
+ label.text = "HEAD";
+ label_list->push_back(label);
+ }
+ }
+ QList<Git::Branch> list = findBranch(commit->commit_id);
+ for (Git::Branch const &b : list) {
+ if (b.flags & Git::Branch::HeadDetachedAt) continue;
+ if (b.flags & Git::Branch::HeadDetachedFrom) continue;
+ Label label(Label::LocalBranch);
+ label.text = b.name;
+ if (!b.remote.isEmpty()) {
+ label.kind = Label::RemoteBranch;
+ label.text = "remotes" / b.remote / label.text;
+ }
+ if (b.ahead > 0) {
+ label.text += tr(", %1 ahead").arg(b.ahead);
+ }
+ if (b.behind > 0) {
+ label.text += tr(", %1 behind").arg(b.behind);
+ }
+ message_ex += " {" + label.text + '}';
+ if (label_list) label_list->push_back(label);
+ }
+ }
+ { // tag
+ QList<Git::Tag> list = findTag(commit->commit_id);
+ for (Git::Tag const &t : list) {
+ Label label(Label::Tag);
+ label.text = t.name;
+ message_ex += QString(" {#%1}").arg(label.text);
+ if (label_list) label_list->push_back(label);
+ }
+ }
+ return message_ex;
+}
+
+void BasicMainWindow::removeRepositoryFromBookmark(int index, bool ask)
+{
+ if (ask) {
+ int r = QMessageBox::warning(this, tr("Confirm Remove"), tr("Are you sure you want to remove the repository from bookmarks ?") + '\n' + tr("(Files will NOT be deleted)"), QMessageBox::Ok, QMessageBox::Cancel);
+ if (r != QMessageBox::Ok) return;
+ }
+ auto *repos = getReposPtr();
+ if (index >= 0 && index < repos->size()) {
+ repos->erase(repos->begin() + index);
+ saveRepositoryBookmarks();
+ updateRepositoriesList();
+ }
+}
+
+void BasicMainWindow::clone()
+{
+ if (!isRemoteOnline()) return;
+
+ QString url;
+ QString dir = defaultWorkingDir();
+ while (1) {
+ CloneDialog dlg(this, url, dir);
+ if (dlg.exec() != QDialog::Accepted) {
+ return;
+ }
+
+ url = dlg.url();
+ dir = dlg.dir();
+
+ if (dlg.action() == CloneDialog::Action::Clone) {
+ // 既存チェック
+
+ QFileInfo info(dir);
+ if (info.isFile()) {
+ QString msg = dir + "\n\n" + tr("A file with same name already exists");
+ QMessageBox::warning(this, tr("Clone"), msg);
+ continue;
+ }
+ if (info.isDir()) {
+ QString msg = dir + "\n\n" + tr("A folder with same name already exists");
+ QMessageBox::warning(this, tr("Clone"), msg);
+ continue;
+ }
+
+ // クローン先ディレクトリを求める
+
+ Git::CloneData clone_data = Git::preclone(url, dir);
+
+ // クローン先ディレクトリの存在チェック
+
+ QString basedir = misc::normalizePathSeparator(clone_data.basedir);
+ if (!QFileInfo(basedir).isDir()) {
+ int i = basedir.indexOf('/');
+ int j = basedir.indexOf('\\');
+ if (i < j) i = j;
+ if (i < 0) {
+ QString msg = basedir + "\n\n" + tr("Invalid folder");
+ QMessageBox::warning(this, tr("Clone"), msg);
+ continue;
+ }
+
+ QString msg = basedir + "\n\n" + tr("No such folder. Create it now ?");
+ if (QMessageBox::warning(this, tr("Clone"), msg, QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) {
+ continue;
+ }
+
+ // ディレクトリを作成
+
+ QString base = basedir.mid(0, i + 1);
+ QString sub = basedir.mid(i + 1);
+ QDir(base).mkpath(sub);
+ }
+
+ m->temp_repo_for_clone_complete.local_dir = dir;
+ m->temp_repo_for_clone_complete.local_dir.replace('\\', '/');
+ m->temp_repo_for_clone_complete.name = makeRepositoryName(dir);
+
+ GitPtr g = git(QString());
+ setPtyCondition(PtyCondition::Clone);
+ setPtyProcessOk(true);
+ g->clone(clone_data, getPtyProcess());
+
+ } else if (dlg.action() == CloneDialog::Action::AddExisting) {
+ addWorkingCopyDir(dir, true);
+ }
+
+ return; // done
+ }
+}
+
+void BasicMainWindow::checkout()
+{
+ checkout(this, selectedCommitItem());
+}
+
+void BasicMainWindow::commit(bool amend)
+{
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return;
+
+ while (1) {
+ Git::User user = g->getUser(Git::Source::Default);
+ QString sign_id = g->signingKey(Git::Source::Default);
+ gpg::Data key;
+ {
+ QList<gpg::Data> keys;
+ gpg::listKeys(global->gpg_command, &keys);
+ for (gpg::Data const &k : keys) {
+ if (k.id == sign_id) {
+ key = k;
+ }
+ }
+ }
+ CommitDialog dlg(this, currentRepositoryName(), user, key);
+ if (amend) {
+ dlg.setText(getLogs()[0].message);
+ }
+ if (dlg.exec() == QDialog::Accepted) {
+ QString text = dlg.text();
+ if (text.isEmpty()) {
+ QMessageBox::warning(this, tr("Commit"), tr("Commit message can not be omitted."));
+ continue;
+ }
+ bool sign = dlg.isSigningEnabled();
+ bool ok;
+ if (amend) {
+ ok = g->commit_amend_m(text, sign, getPtyProcess());
+ } else {
+ ok = g->commit(text, sign, getPtyProcess());
+ }
+ if (ok) {
+ setForceFetch(true);
+ openRepository(true);
+ } else {
+ QString err = g->errorMessage().trimmed();
+ err += "\n*** ";
+ err += tr("Failed to commit");
+ err += " ***\n";
+ writeLog(err);
+ }
+ }
+ break;
+ }
+}
+
+void BasicMainWindow::commitAmend()
+{
+ commit(true);
+}
+
+void BasicMainWindow::pushSetUpstream(const QString &remote, const QString &branch)
+{
+ if (remote.isEmpty()) return;
+ if (branch.isEmpty()) return;
+
+ int exitcode = 0;
+ QString errormsg;
+
+ reopenRepository(true, [&](GitPtr g){
+ g->push_u(remote, branch, getPtyProcess());
+ while (1) {
+ if (getPtyProcess()->wait(1)) break;
+ QApplication::processEvents();
+ }
+ exitcode = getPtyProcess()->getExitCode();
+ errormsg = getPtyProcess()->getMessage();
+ });
+
+ if (exitcode == 128) {
+ if (errormsg.indexOf("Connection refused") >= 0) {
+ QMessageBox::critical(this, qApp->applicationName(), tr("Connection refused."));
+ return;
+ }
+ }
+
+ updateRemoteInfo();
+}
+
+bool BasicMainWindow::pushSetUpstream(bool testonly)
+{
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return false;
+ QStringList remotes = g->getRemotes();
+
+ QString current_branch = currentBranchName();
+
+ QStringList branches;
+ for (Git::Branch const &b : g->branches()) {
+ branches.push_back(b.name);
+ }
+
+ if (remotes.isEmpty() || branches.isEmpty()) {
+ return false;
+ }
+
+ if (testonly) {
+ return true;
+ }
+
+ PushDialog dlg(this, remotes, branches, PushDialog::RemoteBranch(QString(), current_branch));
+ if (dlg.exec() == QDialog::Accepted) {
+ PushDialog::Action a = dlg.action();
+ if (a == PushDialog::PushSimple) {
+ push();
+ } else if (a == PushDialog::PushSetUpstream) {
+ QString remote = dlg.remote();
+ QString branch = dlg.branch();
+ pushSetUpstream(remote, branch);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void BasicMainWindow::push()
+{
+ if (!isRemoteOnline()) return;
+
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return;
+
+ if (g->getRemotes().isEmpty()) {
+ QMessageBox::warning(this, qApp->applicationName(), tr("No remote repository is registered."));
+ execRepositoryPropertyDialog(QString(), true);
+ return;
+ }
+
+ int exitcode = 0;
+ QString errormsg;
+
+ reopenRepository(true, [&](GitPtr g){
+ g->push(false, getPtyProcess());
+ while (1) {
+ if (getPtyProcess()->wait(1)) break;
+ QApplication::processEvents();
+ }
+ exitcode = getPtyProcess()->getExitCode();
+ errormsg = getPtyProcess()->getMessage();
+ });
+
+ if (exitcode == 128) {
+ if (errormsg.indexOf("no upstream branch") >= 0) {
+ QString brname = currentBranchName();
+
+ QString msg = tr("The current branch %1 has no upstream branch.");
+ msg = msg.arg(brname);
+ msg += '\n';
+ msg += tr("You try push --set-upstream");
+ QMessageBox::warning(this, qApp->applicationName(), msg);
+ pushSetUpstream(false);
+ return;
+ }
+ if (errormsg.indexOf("Connection refused") >= 0) {
+ QMessageBox::critical(this, qApp->applicationName(), tr("Connection refused."));
+ return;
+ }
+ }
+}
+
+void BasicMainWindow::openTerminal(const RepositoryItem *repo)
+{
+ runOnRepositoryDir([](QString dir){
+#ifdef Q_OS_MAC
+ if (!isValidDir(dir)) return;
+ QString cmd = "open -n -a /Applications/Utilities/Terminal.app --args \"%1\"";
+ cmd = cmd.arg(dir);
+ QProcess::execute(cmd);
+#else
+ Terminal::open(dir);
+#endif
+ }, repo);
+}
+
+void BasicMainWindow::openExplorer(const RepositoryItem *repo)
+{
+ runOnRepositoryDir([](QString dir){
+#ifdef Q_OS_MAC
+ if (!isValidDir(dir)) return;
+ QString cmd = "open \"%1\"";
+ cmd = cmd.arg(dir);
+ QProcess::execute(cmd);
+#else
+ QDesktopServices::openUrl(dir);
+#endif
+ }, repo);
+}
+
+const Git::CommitItem *BasicMainWindow::selectedCommitItem() const
+{
+ int i = selectedLogIndex();
+ return commitItem(i);
+}
+
+void BasicMainWindow::deleteBranch(const Git::CommitItem *commit)
+{
+ if (!commit) return;
+
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return;
+
+ QStringList all_local_branch_names;
+ QStringList current_local_branch_names;
+ {
+ NamedCommitList named_commits = namedCommitItems(Branches);
+ JumpDialog::sort(&named_commits);
+ for (NamedCommitItem const &item : named_commits) {
+ if (item.name == "HEAD") continue;
+ if (item.id == commit->commit_id) {
+ current_local_branch_names.push_back(item.name);
+ }
+ all_local_branch_names.push_back(item.name);
+ }
+ }
+
+ DeleteBranchDialog dlg(this, false, all_local_branch_names, current_local_branch_names);
+ if (dlg.exec() == QDialog::Accepted) {
+ setLogEnabled(g, true);
+ QStringList names = dlg.selectedBranchNames();
+ int count = 0;
+ for (QString const &name : names) {
+ if (g->git(QString("branch -D \"%1\"").arg(name))) {
+ count++;
+ } else {
+ writeLog(tr("Failed to delete the branch '%1'\n").arg(name));
+ }
+ }
+ if (count > 0) {
+ openRepository(true);
+ }
+ }
+}
+
+void BasicMainWindow::deleteBranch()
+{
+ deleteBranch(selectedCommitItem());
+}
+
+bool BasicMainWindow::askAreYouSureYouWantToRun(const QString &title, const QString &command)
+{
+ QString message = tr("Are you sure you want to run the following command ?");
+ QString text = "%1\n\n%2";
+ text = text.arg(message).arg(command);
+ return QMessageBox::warning(this, title, text, QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok;
+}
+
+bool BasicMainWindow::editFile(const QString &path, const QString &title)
+{
+ return TextEditDialog::editFile(this, path, title);
+}
+
+void BasicMainWindow::resetFile(const QStringList &paths)
+{
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return;
+
+ if (paths.isEmpty()) {
+ // nop
+ } else {
+ QString cmd = "git checkout -- \"%1\"";
+ cmd = cmd.arg(paths[0]);
+ if (askAreYouSureYouWantToRun(tr("Reset a file"), "> " + cmd)) {
+ for (QString const &path : paths) {
+ g->resetFile(path);
+ }
+ openRepository(true);
+ }
+ }
+}
+
+void BasicMainWindow::saveRepositoryBookmark(RepositoryItem item)
+{
+ if (item.local_dir.isEmpty()) return;
+
+ if (item.name.isEmpty()) {
+ item.name = tr("Unnamed");
+ }
+
+ auto *repos = getReposPtr();
+
+ bool done = false;
+ for (auto &repo : *repos) {
+ RepositoryItem *p = &repo;
+ if (item.local_dir == p->local_dir) {
+ *p = item;
+ done = true;
+ break;
+ }
+ }
+ if (!done) {
+ repos->push_back(item);
+ }
+ saveRepositoryBookmarks();
+ updateRepositoriesList();
+}
+
+void BasicMainWindow::setCurrentRepository(const RepositoryItem &repo, bool clear_authentication)
+{
+ if (clear_authentication) {
+ clearAuthentication();
+ }
+ m->current_repo = repo;
+}
+
+void BasicMainWindow::internalDeleteTags(const QStringList &tagnames)
+{
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return;
+
+ if (!tagnames.isEmpty()) {
+ reopenRepository(false, [&](GitPtr g){
+ for (QString const &name : tagnames) {
+ g->delete_tag(name, true);
+ }
+ });
+ }
+}
+
+bool BasicMainWindow::internalAddTag(const QString &name)
+{
+ if (name.isEmpty()) return false;
+
+ GitPtr g = git();
+ if (!isValidWorkingCopy(g)) return false;
+
+ QString commit_id;
+
+ Git::CommitItem const *commit = selectedCommitItem();
+ if (commit && !commit->commit_id.isEmpty()) {
+ commit_id = commit->commit_id;
+ }
+
+ if (!Git::isValidID(commit_id)) return false;
+
+ bool ok = false;
+ reopenRepository(false, [&](GitPtr g){
+ ok = g->tag(name, commit_id);
+ });
+
+ return ok;
+}
+
+NamedCommitList BasicMainWindow::namedCommitItems(int flags)
+{
+ NamedCommitList items;
+ if (flags & Branches) {
+ for (auto pair: branchMapRef()) {
+ QList<Git::Branch> const &list = pair.second;
+ for (Git::Branch const &b : list) {
+ if (b.isHeadDetached()) continue;
+ QString key = b.name;
+ if (flags & NamedCommitFlag::Remotes) {
+ if (!b.remote.isEmpty()) {
+ key = "remotes" / b.remote / key;
+ }
+ } else {
+ if (!b.remote.isEmpty()) continue;
+ }
+ NamedCommitItem item;
+ item.type = NamedCommitItem::Type::Branch;
+ item.remote = b.remote;
+ item.name = b.name;
+ item.id = b.id;
+ items.push_back(item);
+ }
+ }
+ }
+ if (flags & Tags) {
+ for (auto pair: *ptrTagMap()) {
+ QList<Git::Tag> const &list = pair.second;
+ for (Git::Tag const &t : list) {
+ NamedCommitItem item;
+ item.type = NamedCommitItem::Type::Tag;
+ item.name = t.name;
+ item.id = t.id;
+ items.push_back(item);
+ }
+ }
+ }
+ return items;
+}
+
+int BasicMainWindow::rowFromCommitId(const QString &id)
+{
+ auto const &logs = getLogs();
+ for (size_t i = 0; i < logs.size(); i++) {
+ Git::CommitItem const &item = logs[i];
+ if (item.commit_id == id) {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
+void BasicMainWindow::createRepository(const QString &dir)
+{
+ CreateRepositoryDialog dlg(this, dir);
+ if (dlg.exec() == QDialog::Accepted) {
+ QString path = dlg.path();
+ if (QFileInfo(path).isDir()) {
+ if (Git::isValidWorkingCopy(path)) {
+ // A valid git repository already exists there.
+ } else {
+ GitPtr g = git(path);
+ if (g->init()) {
+ QString name = dlg.name();
+ if (!name.isEmpty()) {
+ addWorkingCopyDir(path, name, true);
+ }
+ QString remote_name = dlg.remoteName();
+ QString remote_url = dlg.remoteURL();
+ if (!remote_name.isEmpty() && !remote_url.isEmpty()) {
+ g->addRemoteURL(remote_name, remote_url);
+ }
+ }
+ }
+ } else {
+ // not dir
+ }
+ }
+}
+
+void BasicMainWindow::setLogEnabled(GitPtr g, bool f)
+{
+ if (f) {
+ g->setLogCallback(git_callback, this);
+ } else {
+ g->setLogCallback(nullptr, nullptr);
+ }
+}
+
+QList<Git::Tag> BasicMainWindow::findTag(const QString &id)
+{
+ auto it = ptrTagMap()->find(id);
+ if (it != ptrTagMap()->end()) {
+ return it->second;
+ }
+ return QList<Git::Tag>();
+}
+
+void BasicMainWindow::sshSetPassphrase(const std::string &user, const std::string &pass)
+{
+ m->ssh_passphrase_user = user;
+ m->ssh_passphrase_pass = pass;
+}
+
+std::string BasicMainWindow::sshPassphraseUser() const
+{
+ return m->ssh_passphrase_user;
+}
+
+std::string BasicMainWindow::sshPassphrasePass() const
+{
+ return m->ssh_passphrase_pass;
+}
+
+void BasicMainWindow::httpSetAuthentication(const std::string &user, const std::string &pass)
+{
+ m->http_uid = user;
+ m->http_pwd = pass;
+}
+
+std::string BasicMainWindow::httpAuthenticationUser() const
+{
+ return m->http_uid;
+}
+
+std::string BasicMainWindow::httpAuthenticationPass() const
+{
+ return m->http_uid;
+}
+
diff --git a/src/BasicMainWindow.h b/src/BasicMainWindow.h
new file mode 100644
index 0000000..11a8dc0
--- /dev/null
+++ b/src/BasicMainWindow.h
@@ -0,0 +1,366 @@
+#ifndef BASICMAINWINDOW_H
+#define BASICMAINWINDOW_H
+
+#include "Git.h"
+#include "GitObjectManager.h"
+#include "MyProcess.h"
+#include "RepositoryData.h"
+#include "TextEditorTheme.h"
+#include <QMainWindow>
+#include <functional>
+
+class ApplicationSettings;
+class AvatarLoader;
+class QListWidget;
+class QListWidgetItem;
+class QTreeWidgetItem;
+class WebContext;
+
+struct GitHubRepositoryInfo {
+ QString owner_account_name;
+ QString repository_name;
+};
+
+#define PATH_PREFIX "*"
+
+class BasicMainWindow : public QMainWindow {
+ Q_OBJECT
+public:
+ struct Label {
+ enum {
+ Head,
+ LocalBranch,
+ RemoteBranch,
+ Tag,
+ };
+ int kind;
+ QString text;
+ Label(int kind = LocalBranch)
+ : kind(kind)
+ {
+
+ }
+ };
+protected:
+
+ enum class PtyCondition {
+ None,
+ Clone,
+ Fetch,
+ Pull,
+ Push,
+ };
+
+ enum InteractionMode {
+ None,
+ Busy,
+ };
+
+ enum NamedCommitFlag {
+ Branches = 0x0001,
+ Tags = 0x0002,
+ Remotes = 0x0100,
+ };
+
+ enum class FilesListType {
+ SingleList,
+ SideBySide,
+ };
+
+ struct Task {
+ int index = 0;
+ int parent = 0;
+ Task() = default;
+ Task(int index, int parent)
+ : index(index)
+ , parent(parent)
+ {
+ }
+ };
+
+ struct Element {
+ int depth = 0;
+ std::vector<int> indexes;
+ };
+
+ enum {
+ IndexRole = Qt::UserRole,
+ FilePathRole,
+ DiffIndexRole,
+ HunkIndexRole,
+ };
+
+ enum {
+ GroupItem = -1,
+ };
+
+ struct Private;
+ Private *m;
+protected:
+private:
+private:
+ static bool git_callback(void *cookie, const char *ptr, int len);
+
+ QString selectCommand_(const QString &cmdname, const QStringList &cmdfiles, const QStringList &list, QString path, std::function<void (const QString &)> const &callback);
+ QString selectCommand_(const QString &cmdname, const QString &cmdfile, const QStringList &list, const QString &path, std::function<void (const QString &)> const &callback);
+
+ bool checkGitCommand();
+ bool checkFileCommand();
+ bool checkExecutable(const QString &path);
+ void internalSetCommand(const QString &path, bool save, const QString &name, QString *out);
+ QStringList whichCommand_(const QString &cmdfile1, const QString &cmdfile2 = QString());
+
+ void emitWriteLog(QByteArray ba);
+
+ void setWindowTitle_(const Git::User &user);
+ bool execSetGlobalUserDialog();
+
+ bool saveByteArrayAs(const QByteArray &ba, const QString &dstpath);
+ bool saveFileAs(const QString &srcpath, const QString &dstpath);
+ bool saveBlobAs(const QString &id, const QString &dstpath);
+
+ void updateFilesList(const Git::CommitItem &commit, bool wait);
+
+ void revertAllFiles();
+
+ void setCurrentRemoteName(const QString &name);
+
+ void deleteTags(const Git::CommitItem &commit);
+
+ bool isAvatarEnabled() const;
+ bool isGitHub() const;
+ QStringList remotes() const;
+ QList<Git::Branch> findBranch(const QString &id);
+ QString saveAsTemp(const QString &id);
+ QString tempfileHeader() const;
+ void deleteTempFiles();
+ QString getCommitIdFromTag(const QString &tag);
+ bool isValidRemoteURL(const QString &url);
+ QString getObjectID(QListWidgetItem *item);
+ void addWorkingCopyDir(QString dir, QString name, bool open);
+ static QString makeRepositoryName(const QString &loc);
+ void clearAuthentication();
+ const RepositoryItem *findRegisteredRepository(QString *workdir) const;
+ void queryRemotes(GitPtr g);
+ bool runOnRepositoryDir(std::function<void (QString)> callback, const RepositoryItem *repo);
+ void clearSshAuthentication();
+protected:
+ static QString getFilePath(QListWidgetItem *item);
+ static bool isGroupItem(QTreeWidgetItem *item);
+ static int indexOfLog(QListWidgetItem *item);
+ static int indexOfDiff(QListWidgetItem *item);
+ static int getHunkIndex(QListWidgetItem *item);
+
+ void initNetworking();
+ static QString abbrevCommitID(const Git::CommitItem &commit);
+ bool saveRepositoryBookmarks() const;
+ QString getBookmarksFilePath() const;
+
+ void stopPtyProcess();
+ void abortPtyProcess();
+
+ bool execWelcomeWizardDialog();
+ void execRepositoryPropertyDialog(QString workdir, bool open_repository_menu = false);
+ void execSetUserDialog(const Git::User &global_user, const Git::User &repo_user, const QString &reponame);
+ void setGitCommand(const QString &path, bool save);
+ void setFileCommand(const QString &path, bool save);
+ void setGpgCommand(const QString &path, bool save);
+ void logGitVersion();
+ void setUnknownRepositoryInfo();
+ void internalClearRepositoryInfo();
+ void checkUser();
+ void openRepository(bool validate, bool waitcursor = true);
+ void reopenRepository(bool log, std::function<void (GitPtr)> callback);
+ void openSelectedRepository();
+ void checkRemoteUpdate();
+ bool isThereUncommitedChanges() const;
+ bool makeDiff(QString id, QList<Git::Diff> *out);
+ void addDiffItems(const QList<Git::Diff> *diff_list, const std::function<void (const QString &, QString, int)> &add_item);
+ Git::CommitItemList retrieveCommitLog(GitPtr g);
+
+ void queryBranches(GitPtr g);
+ std::map<QString, QList<Git::Branch>> &branchMapRef();
+
+ void updateCommitTableLater();
+ void updateRemoteInfo();
+ void updateWindowTitle(GitPtr g);
+ QString makeCommitInfoText(int row, QList<Label> *label_list);
+ void removeRepositoryFromBookmark(int index, bool ask);
+
+ void clone();
+ void checkout();
+ void commit(bool amend = false);
+ void commitAmend();
+ void pushSetUpstream(const QString &remote, const QString &branch);
+ bool pushSetUpstream(bool testonly);
+ void push();
+
+ void openTerminal(const RepositoryItem *repo);
+ void openExplorer(const RepositoryItem *repo);
+ const Git::CommitItem *selectedCommitItem() const;
+ void deleteBranch(const Git::CommitItem *commit);
+ void deleteBranch();
+ bool askAreYouSureYouWantToRun(const QString &title, const QString &command);
+ bool editFile(const QString &path, const QString &title);
+ void resetFile(const QStringList &paths);
+ void saveRepositoryBookmark(RepositoryItem item);
+ void setCurrentRepository(const RepositoryItem &repo, bool clear_authentication);
+ void internalDeleteTags(const QStringList &tagnames);
+ bool internalAddTag(const QString &name);
+ NamedCommitList namedCommitItems(int flags);
+ int rowFromCommitId(const QString &id);
+ void createRepository(const QString &dir);
+ void setLogEnabled(GitPtr g, bool f);
+ QList<Git::Tag> findTag(const QString &id);
+ void sshSetPassphrase(const std::string &user, const std::string &pass);
+ std::string sshPassphraseUser() const;
+ std::string sshPassphrasePass() const;
+ void httpSetAuthentication(std::string const &user, std::string const &pass);
+ std::string httpAuthenticationUser() const;
+ std::string httpAuthenticationPass() const;
+public:
+ Git::CommitItemList const &getLogs() const;
+ const Git::CommitItem *getLog(int index) const;
+protected:
+ Git::CommitItemList *getLogsPtr();
+ void setLogs(const Git::CommitItemList &logs);
+ void clearLogs();
+
+ PtyProcess *getPtyProcess();
+ PtyCondition getPtyCondition();
+ bool getPtyProcessOk() const;
+ void setPtyProcessOk(bool pty_process_ok);
+ void setPtyCondition(const PtyCondition &ptyCondition);
+
+ QList<RepositoryItem> const &getRepos() const;
+ QList<RepositoryItem> *getReposPtr();
+
+ QString getCurrentRemoteName() const;
+
+ AvatarLoader *getAvatarLoader();
+ const AvatarLoader *getAvatarLoader() const;
+
+ int *ptrUpdateFilesListCounter();
+ int *ptrUpdateCommitTableCounter();
+
+ bool interactionCanceled() const;
+ void setInteractionCanceled(bool canceled);
+
+ InteractionMode interactionMode() const;
+ void setInteractionMode(const InteractionMode &im);
+
+ QString getRepositoryFilterText() const;
+ void setRepositoryFilterText(const QString &text);
+
+ void setUncommitedChanges(bool uncommited_changes);
+
+ QList<Git::Diff> *diffResult();
+ std::map<QString, Git::Diff> *getDiffCacheMap();
+
+ bool getRemoteChanged() const;
+ void setRemoteChanged(bool remote_changed);
+
+ void setServerType(const ServerType &server_type);
+
+ GitHubRepositoryInfo *ptrGitHub();
+
+ std::map<int, QList<Label> > *getLabelMap();
+ void clearLabelMap();
+
+ GitObjectCache *getObjCache();
+
+ bool getForceFetch() const;
+ void setForceFetch(bool force_fetch);
+
+ std::map<QString, QList<Git::Tag>> *ptrTagMap();
+
+ QString getHeadId() const;
+ void setHeadId(const QString &head_id);
+
+ RepositoryItem const &getTempRepoForCloneComplete() const;
+
+protected:
+ virtual void setCurrentLogRow(int row) = 0;
+ virtual void updateFilesList(QString id, bool wait) = 0;
+ virtual void setRepositoryInfo(const QString &reponame, const QString &brname) = 0;
+ virtual void deleteTags(const QStringList &tagnames) = 0;
+ virtual void internalWriteLog(const char *ptr, int len) = 0;
+ virtual void removeSelectedRepositoryFromBookmark(bool ask) = 0;
+ virtual void openRepository_(GitPtr g) = 0;
+ virtual void updateRepositoriesList() = 0;
+ virtual int selectedLogIndex() const = 0;
+ virtual void clearFileList() = 0;
+ virtual const RepositoryItem *selectedRepositoryItem() const = 0;
+ virtual bool isRemoteOnline() const = 0;
+public:
+ explicit BasicMainWindow(QWidget *parent = nullptr);
+ ~BasicMainWindow();
+ ApplicationSettings *appsettings();
+ const ApplicationSettings *appsettings() const;
+ WebContext *webContext();
+ QString gitCommand() const;
+ void autoOpenRepository(QString dir);
+ GitPtr git(const QString &dir) const;
+ GitPtr git();
+ QPixmap getTransparentPixmap();
+ QIcon committerIcon(int row) const;
+ const Git::CommitItem *commitItem(int row) const;
+ QIcon verifiedIcon(char s) const;
+ virtual QString currentWorkingCopyDir() const;
+ const QList<BasicMainWindow::Label> *label(int row);
+ bool saveAs(const QString &id, const QString &dstpath);
+ bool testRemoteRepositoryValidity(const QString &url);
+ QString defaultWorkingDir() const;
+ void addWorkingCopyDir(QString dir, bool open);
+ bool queryCommit(const QString &id, Git::CommitItem *out);
+ QAction *addMenuActionProperty(QMenu *menu);
+ void checkout(QWidget *parent, const Git::CommitItem *commit);
+ void jumpToCommit(QString id);
+ void execCommitViewWindow(const Git::CommitItem *commit);
+ void execCommitExploreWindow(QWidget *parent, const Git::CommitItem *commit);
+ void execCommitPropertyDialog(QWidget *parent, const Git::CommitItem *commit);
+ void execFileHistory(const QString &path);
+ void execFileHistory(QListWidgetItem *item);
+ void execFilePropertyDialog(QListWidgetItem *item);
+ QString selectGitCommand(bool save);
+ QString selectFileCommand(bool save);
+ QString selectGpgCommand(bool save);
+ const Git::Branch &currentBranch() const;
+ void setCurrentBranch(const Git::Branch &b);
+ QString currentRepositoryName() const;
+ QString currentRemoteName() const;
+ QString currentBranchName() const;
+ bool isValidWorkingCopy(const GitPtr &g) const;
+ QString determinFileType_(const QString &path, bool mime, std::function<void (const QString &, QByteArray *)> callback) const;
+ QString determinFileType(const QString &path, bool mime);
+ QString determinFileType(QByteArray in, bool mime);
+ QList<Git::Tag> queryTagList();
+ int limitLogCount() const;
+ TextEditorThemePtr themeForTextEditor();
+ Git::Object cat_file_(GitPtr g, const QString &id);
+ Git::Object cat_file(const QString &id);
+ QString newTempFilePath();
+ QString findFileID(GitPtr, const QString &commit_id, const QString &file);
+ void updateFilesList(const QString &id, QList<Git::Diff> *diff_list, QListWidget *listwidget);
+ void setAppSettings(const ApplicationSettings &appsettings);
+
+ QIcon getRepositoryIcon() const;
+ QIcon getFolderIcon() const;
+ QIcon getSignatureGoodIcon() const;
+ QIcon getSignatureDubiousIcon() const;
+ QIcon getSignatureBadIcon() const;
+ QPixmap getTransparentPixmap() const;
+
+protected slots:
+ void onAvatarUpdated();
+public slots:
+ void writeLog(const char *ptr, int len);
+ void writeLog(const QString &str);
+ void writeLog(QByteArray ba);
+signals:
+ void signalWriteLog(QByteArray ba);
+ void remoteInfoChanged();
+ void signalCheckRemoteUpdate();
+};
+
+#endif // BASICMAINWINDOW_H
diff --git a/src/BasicRepositoryDialog.cpp b/src/BasicRepositoryDialog.cpp
index 537259d..e91ffce 100644
--- a/src/BasicRepositoryDialog.cpp
+++ b/src/BasicRepositoryDialog.cpp
@@ -1,89 +1,89 @@
#include "BasicRepositoryDialog.h"
-#include "MainWindow.h"
+#include "BasicMainWindow.h"
#include <QTableWidget>
#include <QHeaderView>
struct BasicRepositoryDialog::Private {
- MainWindow *mainwindow = nullptr;
+ BasicMainWindow *mainwindow = nullptr;
GitPtr git;
QList<Git::Remote> remotes;
};
-BasicRepositoryDialog::BasicRepositoryDialog(MainWindow *mainwindow, GitPtr g)
+BasicRepositoryDialog::BasicRepositoryDialog(BasicMainWindow *mainwindow, GitPtr g)
: QDialog(mainwindow)
, m(new Private)
{
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
m->mainwindow = mainwindow;
m->git = g;
}
BasicRepositoryDialog::~BasicRepositoryDialog()
{
delete m;
}
-MainWindow *BasicRepositoryDialog::mainwindow()
+BasicMainWindow *BasicRepositoryDialog::mainwindow()
{
return m->mainwindow;
}
GitPtr BasicRepositoryDialog::git()
{
return m->git;
}
QList<Git::Remote> const *BasicRepositoryDialog::remotes() const
{
return &m->remotes;
}
QString BasicRepositoryDialog::updateRemotesTable(QTableWidget *tablewidget)
{
tablewidget->clear();
m->remotes.clear();
GitPtr g = git();
if (g->isValidWorkingCopy()) {
g->getRemoteURLs(&m->remotes);
}
QString url;
QString alturl;
int rows = m->remotes.size();
tablewidget->setColumnCount(3);
tablewidget->setRowCount(rows);
auto newQTableWidgetItem = [](QString const &text){
auto *item = new QTableWidgetItem;
item->setSizeHint(QSize(20, 20));
item->setText(text);
return item;
};
auto SetHeaderItem = [&](int col, QString const &text){
tablewidget->setHorizontalHeaderItem(col, newQTableWidgetItem(text));
};
SetHeaderItem(0, tr("Name"));
SetHeaderItem(1, tr("Purpose"));
SetHeaderItem(2, tr("URL"));
for (int row = 0; row < rows; row++) {
Git::Remote const &r = m->remotes[row];
if (r.purpose == "push") {
url = r.url;
} else {
alturl = r.url;
}
auto SetItem = [&](int col, QString const &text){
tablewidget->setItem(row, col, newQTableWidgetItem(text));
};
SetItem(0, r.name);
SetItem(1, r.purpose);
SetItem(2, r.url);
}
tablewidget->horizontalHeader()->setStretchLastSection(true);
if (url.isEmpty()) {
url = alturl;
}
return url;
}
diff --git a/src/BasicRepositoryDialog.h b/src/BasicRepositoryDialog.h
index ca01633..6b98a98 100644
--- a/src/BasicRepositoryDialog.h
+++ b/src/BasicRepositoryDialog.h
@@ -1,26 +1,26 @@
#ifndef BASICREPOSITORYDIALOG_H
#define BASICREPOSITORYDIALOG_H
#include <QDialog>
#include "Git.h"
class QTableWidget;
-class MainWindow;
+class BasicMainWindow;
class BasicRepositoryDialog : public QDialog {
public:
- explicit BasicRepositoryDialog(MainWindow *mainwindow, GitPtr g);
- virtual ~BasicRepositoryDialog();
+ explicit BasicRepositoryDialog(BasicMainWindow *mainwindow, GitPtr g);
+ ~BasicRepositoryDialog() override;
private:
struct Private;
Private *m;
protected:
- MainWindow *mainwindow();
+ BasicMainWindow *mainwindow();
GitPtr git();
QString updateRemotesTable(QTableWidget *tablewidget);
const QList<Git::Remote> *remotes() const;
};
#endif // BASICREPOSITORYDIALOG_H
diff --git a/src/BigDiffWindow.cpp b/src/BigDiffWindow.cpp
index 7921743..21186ca 100644
--- a/src/BigDiffWindow.cpp
+++ b/src/BigDiffWindow.cpp
@@ -1,67 +1,67 @@
#include <memory>
#include "BigDiffWindow.h"
#include "ui_BigDiffWindow.h"
struct BigDiffWindow::Private {
TextEditorEnginePtr text_editor_engine;
FileDiffWidget::InitParam_ param;
};
BigDiffWindow::BigDiffWindow(QWidget *parent)
: QDialog(parent)
, ui(new Ui::BigDiffWindow)
, m(new Private)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
flags |= Qt::WindowMaximizeButtonHint;
setWindowFlags(flags);
ui->widget_diff->setMaximizeButtonEnabled(false);
connect(ui->widget_diff, &FileDiffWidget::textcodecChanged, [&](){ updateDiffView(); });
}
BigDiffWindow::~BigDiffWindow()
{
delete m;
delete ui;
}
void BigDiffWindow::setTextCodec(QTextCodec *codec)
{
m->text_editor_engine = std::make_shared<TextEditorEngine>();
ui->widget_diff->setTextCodec(codec);
}
void BigDiffWindow::updateDiffView()
{
ui->widget_diff->updateDiffView(m->param.diff, m->param.uncommited);
}
-void BigDiffWindow::init(MainWindow *mw, FileDiffWidget::InitParam_ const &param)
+void BigDiffWindow::init(BasicMainWindow *mw, FileDiffWidget::InitParam_ const &param)
{
ui->widget_diff->bind(mw);
m->param = param;
switch (m->param.view_style) {
case FileDiffWidget::ViewStyle::LeftOnly:
ui->widget_diff->setLeftOnly(m->param.bytes_a, m->param.diff);
break;
case FileDiffWidget::ViewStyle::RightOnly:
ui->widget_diff->setRightOnly(m->param.bytes_b, m->param.diff);
break;
case FileDiffWidget::ViewStyle::SideBySideText:
ui->widget_diff->setSideBySide(m->param.bytes_a, m->param.diff, m->param.uncommited, m->param.workingdir);
break;
case FileDiffWidget::ViewStyle::SideBySideImage:
ui->widget_diff->setSideBySide_(m->param.bytes_a, m->param.bytes_b, m->param.workingdir);
break;
}
}
diff --git a/src/BigDiffWindow.h b/src/BigDiffWindow.h
index 9e89110..b153b64 100644
--- a/src/BigDiffWindow.h
+++ b/src/BigDiffWindow.h
@@ -1,29 +1,28 @@
#ifndef BIGDIFFWINDOW_H
#define BIGDIFFWINDOW_H
#include <QDialog>
#include "Git.h"
#include "FileDiffWidget.h"
namespace Ui {
class BigDiffWindow;
}
-class BigDiffWindow : public QDialog
-{
+class BigDiffWindow : public QDialog {
Q_OBJECT
private:
struct Private;
Private *m;
public:
- explicit BigDiffWindow(QWidget *parent = 0);
- ~BigDiffWindow();
+ explicit BigDiffWindow(QWidget *parent = nullptr);
+ ~BigDiffWindow() override;
- void init(MainWindow *mw, const FileDiffWidget::InitParam_ &param);
+ void init(BasicMainWindow *mw, const FileDiffWidget::InitParam_ &param);
void setTextCodec(QTextCodec *codec);
private:
Ui::BigDiffWindow *ui;
void updateDiffView();
};
#endif // BIGDIFFWINDOW_H
diff --git a/src/BlameWindow.h b/src/BlameWindow.h
index 39f3813..9d22930 100644
--- a/src/BlameWindow.h
+++ b/src/BlameWindow.h
@@ -1,50 +1,49 @@
#ifndef BLAMEWINDOW_H
#define BLAMEWINDOW_H
#include <QDateTime>
#include <QDialog>
class MainWindow;
class QTableWidgetItem;
namespace Ui {
class BlameWindow;
}
struct BlameItem {
QString commit_id;
QString author;
QDateTime time;
int line_number = 0;
QString text;
};
-class BlameWindow : public QDialog
-{
+class BlameWindow : public QDialog {
Q_OBJECT
private:
struct Private;
Private *m;
public:
explicit BlameWindow(MainWindow *parent, QString const &filename, QList<BlameItem> const &list);
- ~BlameWindow();
+ ~BlameWindow() override;
static QList<BlameItem> parseBlame(char const *begin, char const *end);
private slots:
void on_tableWidget_itemDoubleClicked(QTableWidgetItem *item);
void on_tableWidget_customContextMenuRequested(const QPoint &pos);
void on_tableWidget_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
private:
Ui::BlameWindow *ui;
MainWindow *mainwindow();
// QObject interface
QString getCommitId(QTableWidgetItem *item) const;
QString currentCommitId() const;
};
#endif // BLAMEWINDOW_H
diff --git a/src/CheckoutDialog.h b/src/CheckoutDialog.h
index 03d9e06..1403dc6 100644
--- a/src/CheckoutDialog.h
+++ b/src/CheckoutDialog.h
@@ -1,45 +1,45 @@
#ifndef CHECKOUTDIALOG_H
#define CHECKOUTDIALOG_H
#include <QDialog>
#include "Git.h"
namespace Ui {
class CheckoutDialog;
}
class CheckoutDialog : public QDialog
{
Q_OBJECT
private:
struct Private;
Private *m;
public:
explicit CheckoutDialog(QWidget *parent, QStringList const &tags, QStringList const &local_branches, QStringList const &remote_branches);
- ~CheckoutDialog();
+ ~CheckoutDialog() override;
enum class Operation {
HeadDetached,
CreateLocalBranch,
ExistingLocalBranch,
};
Operation operation() const;
QString branchName() const;
private slots:
void on_radioButton_head_detached_toggled(bool checked);
void on_radioButton_existing_local_branch_toggled(bool checked);
void on_radioButton_create_local_branch_toggled(bool checked);
private:
Ui::CheckoutDialog *ui;
void updateUI();
int makeComboBoxOptions(QStringList const &names);
void clearComboBoxOptions();
};
#endif // CHECKOUTDIALOG_H
diff --git a/src/ClearButton.h b/src/ClearButton.h
index 22ec954..82e80bf 100644
--- a/src/ClearButton.h
+++ b/src/ClearButton.h
@@ -1,22 +1,16 @@
#ifndef CLEARBUTTON_H
#define CLEARBUTTON_H
#include <QToolButton>
-class ClearButton : public QToolButton
-{
+class ClearButton : public QToolButton {
Q_OBJECT
private:
QPixmap pixmap;
public:
- explicit ClearButton(QWidget *parent = 0);
-
-signals:
-
-public slots:
-
+ explicit ClearButton(QWidget *parent = nullptr);
protected:
- void paintEvent(QPaintEvent *event);
+ void paintEvent(QPaintEvent *event) override;
};
#endif // CLEARBUTTON_H
diff --git a/src/CloneDialog.cpp b/src/CloneDialog.cpp
index fb59888..03f5b82 100644
--- a/src/CloneDialog.cpp
+++ b/src/CloneDialog.cpp
@@ -1,138 +1,138 @@
#include "CloneDialog.h"
#include "ui_CloneDialog.h"
#include "common/misc.h"
#include "common/joinpath.h"
-#include "MainWindow.h"
+#include "BasicMainWindow.h"
#include "SearchFromGitHubDialog.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QThread>
enum SearchRepository {
None,
GitHub,
};
struct CloneDialog::Private {
QString url;
QString repo_name;
QString default_working_dir;
bool ok = false;
QString errmsg;
CloneDialog::Action action = CloneDialog::Action::Clone;
};
-CloneDialog::CloneDialog(MainWindow *parent, QString const &url, QString const &defworkdir)
+CloneDialog::CloneDialog(BasicMainWindow *parent, QString const &url, QString const &defworkdir)
: QDialog(parent)
, ui(new Ui::CloneDialog)
, m(new Private)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
ui->lineEdit_repo_location->setText(url);
m->default_working_dir = defworkdir;
ui->lineEdit_working_dir->setText(m->default_working_dir);
ui->comboBox->addItem(tr("Search"));
ui->comboBox->addItem(tr("GitHub"));
}
CloneDialog::~CloneDialog()
{
delete m;
delete ui;
}
CloneDialog::Action CloneDialog::action() const
{
return m->action;
}
-MainWindow *CloneDialog::mainwindow()
+BasicMainWindow *CloneDialog::mainwindow()
{
- return qobject_cast<MainWindow *>(parent());
+ return qobject_cast<BasicMainWindow *>(parent());
}
QString CloneDialog::url()
{
return ui->lineEdit_repo_location->text();
}
QString CloneDialog::dir()
{
return ui->lineEdit_working_dir->text();
}
void CloneDialog::on_lineEdit_repo_location_textChanged(QString const &text)
{
QString path;
int i = text.lastIndexOf('/');
int j = text.lastIndexOf('\\');
if (i < j) i = j;
j = text.size();
if (text.endsWith(".git")) {
j -= 4;
}
if (i >= 0 && i < j) {
path = text.mid(i, j - i);
}
m->repo_name = path;
path = m->default_working_dir / m->repo_name;
path = misc::normalizePathSeparator(path);
ui->lineEdit_working_dir->setText(path);
}
void CloneDialog::on_comboBox_currentIndexChanged(int index)
{
if (index == GitHub) {
SearchFromGitHubDialog dlg(this, mainwindow());
if (dlg.exec() == QDialog::Accepted) {
ui->lineEdit_repo_location->setText(dlg.url());
}
}
ui->comboBox->setCurrentIndex(0);
}
void CloneDialog::on_pushButton_test_clicked()
{
mainwindow()->testRemoteRepositoryValidity(url());
}
void CloneDialog::on_pushButton_browse_clicked()
{
QString path = ui->lineEdit_working_dir->text();
path = QFileDialog::getExistingDirectory(this, tr("Checkout into"), path);
if (!path.isEmpty()) {
m->default_working_dir = path;
path = m->default_working_dir / m->repo_name;
path = misc::normalizePathSeparator(path);
ui->lineEdit_working_dir->setText(path);
}
}
void CloneDialog::on_pushButton_open_existing_clicked()
{
QString dir = mainwindow()->defaultWorkingDir();
dir = QFileDialog::getExistingDirectory(this, tr("Open existing directory"), dir);
if (QFileInfo(dir).isDir()) {
QString url;
GitPtr g = mainwindow()->git(dir);
QList<Git::Remote> vec;
if (g->isValidWorkingCopy()) {
g->getRemoteURLs(&vec);
}
for (Git::Remote const &r : vec) {
if (r.purpose == "push" || url.isEmpty()) {
url = r.url;
}
}
ui->lineEdit_repo_location->setText(url);
ui->lineEdit_working_dir->setText(dir);
m->action = CloneDialog::Action::AddExisting;
done(Accepted);
}
}
diff --git a/src/CloneDialog.h b/src/CloneDialog.h
index 554a2b7..dc17cfa 100644
--- a/src/CloneDialog.h
+++ b/src/CloneDialog.h
@@ -1,46 +1,45 @@
#ifndef CLONEDIALOG_H
#define CLONEDIALOG_H
#include <QDialog>
#include <QThread>
#include "Git.h"
namespace Ui {
class CloneDialog;
}
-class MainWindow;
+class BasicMainWindow;
-class CloneDialog : public QDialog
-{
+class CloneDialog : public QDialog {
Q_OBJECT
private:
struct Private;
Private *m;
typedef std::shared_ptr<Git> GitPtr;
public:
- explicit CloneDialog(MainWindow *parent, QString const &url, QString const &defworkdir);
- ~CloneDialog();
+ explicit CloneDialog(BasicMainWindow *parent, QString const &url, QString const &defworkdir);
+ ~CloneDialog() override;
enum class Action {
Clone,
AddExisting,
};
Action action() const;
QString url();
QString dir();
private:
Ui::CloneDialog *ui;
- MainWindow *mainwindow();
+ BasicMainWindow *mainwindow();
private slots:
void on_lineEdit_repo_location_textChanged(QString const &arg1);
void on_pushButton_test_clicked();
void on_comboBox_currentIndexChanged(int index);
void on_pushButton_browse_clicked();
void on_pushButton_open_existing_clicked();
};
#endif // CLONEDIALOG_H
diff --git a/src/CommitDialog.cpp b/src/CommitDialog.cpp
index 516ec63..0fc1e03 100644
--- a/src/CommitDialog.cpp
+++ b/src/CommitDialog.cpp
@@ -1,95 +1,95 @@
#include "CommitDialog.h"
#include "ui_CommitDialog.h"
-#include "MainWindow.h"
+#include "BasicMainWindow.h"
#include "ConfigSigningDialog.h"
#include <QDir>
-CommitDialog::CommitDialog(MainWindow *parent, QString const &reponame, const Git::User &user, const gpg::Data &key) :
+CommitDialog::CommitDialog(BasicMainWindow *parent, QString const &reponame, const Git::User &user, const gpg::Data &key) :
QDialog(parent),
ui(new Ui::CommitDialog)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
key_ = key;
ui->label_reponame->setText(reponame);
ui->label_commit_author->setText(user.name);
ui->label_commit_mail->setText(user.email);
updateSigningInfo();
ui->plainTextEdit->setFocus();
}
CommitDialog::~CommitDialog()
{
delete ui;
}
-MainWindow *CommitDialog::mainwindow()
+BasicMainWindow *CommitDialog::mainwindow()
{
- return qobject_cast<MainWindow *>(parent());
+ return qobject_cast<BasicMainWindow *>(parent());
}
void CommitDialog::updateSigningInfo()
{
GitPtr g = mainwindow()->git();
Git::SignPolicy pol = g->signPolicy(Git::Source::Default);
if (!key_.id.isEmpty()) {
if (pol == Git::SignPolicy::True) {
ui->groupBox_gpg_sign->setCheckable(false);
} else {
ui->groupBox_gpg_sign->setCheckable(true);
ui->groupBox_gpg_sign->setChecked(false);
}
ui->label_sign_id->setText(key_.id);
ui->label_sign_name->setText(key_.name);
ui->label_sign_mail->setText(key_.mail);
} else {
ui->groupBox_gpg_sign->setChecked(false);
ui->groupBox_gpg_sign->setEnabled(false);
}
}
bool CommitDialog::isSigningEnabled() const
{
return ui->groupBox_gpg_sign->isChecked();
}
void CommitDialog::setText(QString const &text)
{
ui->plainTextEdit->setPlainText(text);
QTextCursor cur = ui->plainTextEdit->textCursor();
cur.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
ui->plainTextEdit->setTextCursor(cur);
}
QString CommitDialog::text() const
{
return ui->plainTextEdit->toPlainText();
}
void CommitDialog::keyPressEvent(QKeyEvent *event)
{
if (event->modifiers() & Qt::ControlModifier) {
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
event->accept();
accept();
return;
}
}
QDialog::keyPressEvent(event);
}
void CommitDialog::on_pushButton_config_signing_clicked()
{
ConfigSigningDialog dlg(this, mainwindow(), true);
if (dlg.exec() == QDialog::Accepted) {
updateSigningInfo();
}
}
diff --git a/src/CommitDialog.h b/src/CommitDialog.h
index dee4248..dcbbad1 100644
--- a/src/CommitDialog.h
+++ b/src/CommitDialog.h
@@ -1,39 +1,37 @@
#ifndef COMMITDIALOG_H
#define COMMITDIALOG_H
#include "Git.h"
#include "gpg.h"
#include <QDialog>
-class MainWindow;
+class BasicMainWindow;
namespace Ui {
class CommitDialog;
}
-class CommitDialog : public QDialog
-{
+class CommitDialog : public QDialog {
Q_OBJECT
-
public:
- explicit CommitDialog(MainWindow *parent, QString const &reponame, Git::User const &user, gpg::Data const &key);
- ~CommitDialog();
+ explicit CommitDialog(BasicMainWindow *parent, QString const &reponame, Git::User const &user, gpg::Data const &key);
+ ~CommitDialog() override;
void setText(QString const &text);
QString text() const;
bool isSigningEnabled() const;
protected:
- void keyPressEvent(QKeyEvent *event);
+ void keyPressEvent(QKeyEvent *event) override;
private:
Ui::CommitDialog *ui;
gpg::Data key_;
// QDialog interface
- MainWindow *mainwindow();
+ BasicMainWindow *mainwindow();
void updateSigningInfo();
private slots:
void on_pushButton_config_signing_clicked();
};
#endif // COMMITDIALOG_H
diff --git a/src/CommitExploreWindow.cpp b/src/CommitExploreWindow.cpp
index c744100..0f28297 100644
--- a/src/CommitExploreWindow.cpp
+++ b/src/CommitExploreWindow.cpp
@@ -1,281 +1,279 @@
+
#include "CommitExploreWindow.h"
#include "ui_CommitExploreWindow.h"
#include "GitObjectManager.h"
#include "ImageViewWidget.h"
-#include "common/misc.h"
#include "MainWindow.h"
+#include "common/misc.h"
#include "main.h"
-
+#include "platform.h"
#include <QFileIconProvider>
#include <QMenu>
-
-#ifdef Q_OS_WIN
-#include "win32/win32.h"
-#endif
+#include <memory>
static QTreeWidgetItem *newQTreeWidgetItem()
{
auto *item = new QTreeWidgetItem;
item->setSizeHint(0, QSize(20, 20));
return item;
}
enum {
ItemTypeRole = Qt::UserRole,
ObjectIdRole,
FilePathRole,
};
struct CommitExploreWindow::Private {
- MainWindow *mainwindow;
+ BasicMainWindow *mainwindow;
GitObjectCache *objcache;
Git::CommitItem const *commit;
QString root_tree_id;
GitTreeItemList tree_item_list;
Git::Object content_object;
ObjectContent content;
TextEditorEnginePtr text_editor_engine;
};
-CommitExploreWindow::CommitExploreWindow(QWidget *parent, MainWindow *mainwin, GitObjectCache *objcache, const Git::CommitItem *commit)
+CommitExploreWindow::CommitExploreWindow(QWidget *parent, BasicMainWindow *mainwin, GitObjectCache *objcache, const Git::CommitItem *commit)
: QDialog(parent)
, ui(new Ui::CommitExploreWindow)
, m(new Private)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
flags |= Qt::WindowMaximizeButtonHint;
setWindowFlags(flags);
m->mainwindow = mainwin;
m->objcache = objcache;
m->commit = commit;
- m->text_editor_engine = TextEditorEnginePtr(new TextEditorEngine);
+ m->text_editor_engine = std::make_shared<TextEditorEngine>();
ui->widget_fileview->bind(mainwin, nullptr, ui->verticalScrollBar, ui->horizontalScrollBar, mainwin->themeForTextEditor());
ui->widget_fileview->setDiffMode(m->text_editor_engine, ui->verticalScrollBar, ui->horizontalScrollBar);
ui->splitter->setSizes({100, 100, 200});
// set text
ui->lineEdit_commit_id->setText(commit->commit_id);
ui->lineEdit_date->setText(misc::makeDateTimeString(commit->commit_date));
ui->lineEdit_author->setText(commit->author);
{
GitCommit c;
c.parseCommit(objcache, m->commit->commit_id);
m->root_tree_id = c.tree_id;
}
{
GitCommitTree tree(objcache);
tree.parseTree(m->root_tree_id);
}
{
QTreeWidgetItem *rootitem = newQTreeWidgetItem();
rootitem->setText(0, tr("Commit"));
rootitem->setData(0, ItemTypeRole, (int)GitTreeItem::TREE);
rootitem->setData(0, ObjectIdRole, m->root_tree_id);
ui->treeWidget->addTopLevelItem(rootitem);
loadTree(m->root_tree_id);
rootitem->setExpanded(true);
}
}
CommitExploreWindow::~CommitExploreWindow()
{
delete m;
delete ui;
}
-MainWindow *CommitExploreWindow::mainwindow()
+BasicMainWindow *CommitExploreWindow::mainwindow()
{
return m->mainwindow;
}
void CommitExploreWindow::clearContent()
{
m->content = ObjectContent();
}
void CommitExploreWindow::expandTreeItem_(QTreeWidgetItem *item)
{
if (item->childCount() == 1) {
if (item->child(0)->text(0).isEmpty()) {
delete item->takeChild(0);
}
}
if (item->childCount() == 0) {
QFileIconProvider icons;
QString path = item->data(0, FilePathRole).toString();
QString tree_id = item->data(0, ObjectIdRole).toString();
loadTree(tree_id);
for (GitTreeItem const &ti : m->tree_item_list) {
if (ti.type == GitTreeItem::TREE) {
QTreeWidgetItem *child = newQTreeWidgetItem();
child->setIcon(0, icons.icon(QFileIconProvider::Folder));
child->setText(0, ti.name);
child->setData(0, ItemTypeRole, (int)ti.type);
child->setData(0, ObjectIdRole, ti.id);
child->setData(0, FilePathRole, misc::joinWithSlash(path, ti.name));
QTreeWidgetItem *placeholder = newQTreeWidgetItem();
child->addChild(placeholder);
item->addChild(child);
}
}
}
}
void CommitExploreWindow::on_treeWidget_itemExpanded(QTreeWidgetItem *item)
{
expandTreeItem_(item);
}
void CommitExploreWindow::loadTree(QString const &tree_id)
{
GitCommitTree tree(m->objcache);
tree.parseTree(tree_id);
m->tree_item_list = *tree.treelist();
std::sort(m->tree_item_list.begin(), m->tree_item_list.end(), [](GitTreeItem const &left, GitTreeItem const &right){
int l = (left.type == GitTreeItem::TREE) ? 0 : 1;
int r = (right.type == GitTreeItem::TREE) ? 0 : 1;
if (l != r) return l < r;
return left.name.compare(right.name, Qt::CaseInsensitive) < 0;
});
}
void CommitExploreWindow::doTreeItemChanged_(QTreeWidgetItem *current)
{
ui->listWidget->clear();
QString path = current->data(0, FilePathRole).toString();
QString tree_id = current->data(0, ObjectIdRole).toString();
loadTree(tree_id);
QFileIconProvider icons;
for (GitTreeItem const &ti : m->tree_item_list) {
QIcon icon;
if (ti.type == GitTreeItem::TREE) {
icon = icons.icon(QFileIconProvider::Folder);
} else {
#ifdef Q_OS_WIN
{
int i = ti.name.lastIndexOf('.');
if (i > 0) {
QString ext = ti.name.mid(i + 1);
icon = winIconFromExtensionLarge(ext);
}
}
#endif
if (icon.isNull()) {
icon = icons.icon(QFileIconProvider::File);
}
}
auto *p = new QListWidgetItem;
p->setIcon(icon);
p->setText(ti.name);
p->setData(ItemTypeRole, (int)ti.type);
p->setData(ObjectIdRole, ti.id);
p->setData(FilePathRole, misc::joinWithSlash(path, ti.name));
ui->listWidget->addItem(p);
}
}
void CommitExploreWindow::on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem * /*previous*/)
{
clearContent();
doTreeItemChanged_(current);
}
void CommitExploreWindow::on_listWidget_itemDoubleClicked(QListWidgetItem *item)
{
Q_ASSERT(item);
GitTreeItem::Type type = (GitTreeItem::Type)item->data(ItemTypeRole).toInt();
if (type == GitTreeItem::TREE) {
QString tree_id = item->data(ObjectIdRole).toString();
clearContent();
QTreeWidgetItem *parent = ui->treeWidget->currentItem();
expandTreeItem_(parent);
parent->setExpanded(true);
int n = parent->childCount();
for (int i = 0; i < n; i++) {
QTreeWidgetItem *child = parent->child(i);
if (child->data(0, ItemTypeRole).toInt() == GitTreeItem::TREE) {
QString tid = child->data(0, ObjectIdRole).toString();
if (tid == tree_id) {
child->setExpanded(true);
ui->treeWidget->setCurrentItem(child);
break;
}
}
}
}
}
void CommitExploreWindow::on_listWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem * /*previous*/)
{
if (!current) return;
GitTreeItem::Type type = (GitTreeItem::Type)current->data(ItemTypeRole).toInt();
if (type == GitTreeItem::BLOB) {
QString id = current->data(ObjectIdRole).toString();
m->content_object = m->objcache->catFile(id);
QString path = current->data(FilePathRole).toString();
clearContent();
QString mimetype = mainwindow()->determinFileType(m->content_object.content, true);
if (misc::isImage(mimetype)) {
ui->widget_fileview->setImage(mimetype, m->content_object.content, id, path);
} else {
ui->widget_fileview->setText(m->content_object.content, mainwindow(), id, path);
}
} else {
clearContent();
}
}
void CommitExploreWindow::on_verticalScrollBar_valueChanged(int)
{
ui->widget_fileview->refrectScrollBar();
}
void CommitExploreWindow::on_horizontalScrollBar_valueChanged(int)
{
ui->widget_fileview->refrectScrollBar();
}
void CommitExploreWindow::on_listWidget_customContextMenuRequested(const QPoint &pos)
{
(void)pos;
QListWidgetItem *current = ui->listWidget->currentItem();
if (!current) return;
GitTreeItem::Type type = (GitTreeItem::Type)current->data(ItemTypeRole).toInt();
if (type == GitTreeItem::BLOB) {
QMenu menu;
QAction *a_history = menu.addAction("History");
QAction *a = menu.exec(QCursor::pos() + QPoint(8, -8));
if (a) {
if (a == a_history) {
QString path = current->data(FilePathRole).toString();
mainwindow()->execFileHistory(path);
return;
}
}
}
}
diff --git a/src/CommitExploreWindow.h b/src/CommitExploreWindow.h
index 5d3d2cf..2a887e1 100644
--- a/src/CommitExploreWindow.h
+++ b/src/CommitExploreWindow.h
@@ -1,43 +1,42 @@
#ifndef COMMITEXPLOREWINDOW_H
#define COMMITEXPLOREWINDOW_H
#include <QDialog>
#include "FileDiffWidget.h"
namespace Ui {
class CommitExploreWindow;
}
class QTreeWidgetItem;
class QListWidgetItem;
class GitObjectCache;
-class CommitExploreWindow : public QDialog
-{
+class CommitExploreWindow : public QDialog {
Q_OBJECT
private:
Ui::CommitExploreWindow *ui;
struct Private;
Private *m;
void loadTree(QString const &tree_id);
void doTreeItemChanged_(QTreeWidgetItem *current);
void expandTreeItem_(QTreeWidgetItem *item);
- MainWindow *mainwindow();
+ BasicMainWindow *mainwindow();
public:
- explicit CommitExploreWindow(QWidget *parent, MainWindow *mainwin, GitObjectCache *objcache, Git::CommitItem const *commit);
- ~CommitExploreWindow();
+ explicit CommitExploreWindow(QWidget *parent, BasicMainWindow *mainwin, GitObjectCache *objcache, Git::CommitItem const *commit);
+ ~CommitExploreWindow() override;
void clearContent();
private slots:
void on_treeWidget_itemExpanded(QTreeWidgetItem *item);
void on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_listWidget_itemDoubleClicked(QListWidgetItem *item);
void on_listWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
void on_verticalScrollBar_valueChanged(int);
void on_horizontalScrollBar_valueChanged(int);
void on_listWidget_customContextMenuRequested(const QPoint &pos);
};
#endif // COMMITEXPLOREWINDOW_H
diff --git a/src/CommitPropertyDialog.cpp b/src/CommitPropertyDialog.cpp
index 017e339..f6b752a 100644
--- a/src/CommitPropertyDialog.cpp
+++ b/src/CommitPropertyDialog.cpp
@@ -1,168 +1,169 @@
#include "CommitPropertyDialog.h"
-#include "MainWindow.h"
-#include "gpg.h"
#include "ui_CommitPropertyDialog.h"
#include "ApplicationGlobal.h"
#include "AvatarLoader.h"
+#include "BasicMainWindow.h"
#include "common/misc.h"
+#include "gpg.h"
+#include "main.h"
struct CommitPropertyDialog::Private {
- MainWindow *mainwindow;
+ BasicMainWindow *mainwindow;
Git::CommitItem commit;
AvatarLoader avatar_loader;
};
-void CommitPropertyDialog::init(MainWindow *mw)
+void CommitPropertyDialog::init(BasicMainWindow *mw)
{
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
ui->pushButton_jump->setVisible(false);
m->mainwindow = mw;
ui->lineEdit_description->setText(m->commit.message);
ui->lineEdit_commit_id->setText(m->commit.commit_id);
ui->lineEdit_date->setText(misc::makeDateTimeString(m->commit.commit_date));
ui->lineEdit_author->setText(m->commit.author);
ui->lineEdit_mail->setText(m->commit.email);
QString text;
for (QString const &id : m->commit.parent_ids) {
text += id + '\n';
}
ui->plainTextEdit_parent_ids->setPlainText(text);
gpg::Data key;
int n1 = m->commit.fingerprint.size();
if (n1 > 0) {
QList<gpg::Data> keys;
if (gpg::listKeys(global->gpg_command, &keys)) {
for (gpg::Data const &k : keys) {
int n2 = k.fingerprint.size();
if (n2 > 0) {
int n = std::min(n1, n2);
char const *p1 = m->commit.fingerprint.data() + n1 - n;
char const *p2 = k.fingerprint.data() + n2 - n;
if (memcmp(p1, p2, n) == 0) {
key = k;
break;
}
}
}
} else {
qDebug() << "Failed to get gpg keys";
}
if (key.id.isEmpty()) {
// gpgコマンドが登録されていないなど、keyidが取得できなかったとき
key.id = tr("<Unknown>");
}
}
if (key.id.isEmpty()) {
ui->frame_sign->setVisible(false);
} else {
{
int w = ui->label_signature_icon->width();
int h = ui->label_signature_icon->width();
QIcon icon = mainwindow()->verifiedIcon(m->commit.signature);
ui->label_signature_icon->setPixmap(icon.pixmap(w, h));
}
ui->lineEdit_sign_id->setText(key.id);
ui->lineEdit_sign_name->setText(key.name);
ui->lineEdit_sign_mail->setText(key.mail);
}
m->avatar_loader.start(mainwindow()->webContext());
connect(&m->avatar_loader, &AvatarLoader::updated, [&](){
UpdateAvatar(false);
});
UpdateAvatar(true);
}
void CommitPropertyDialog::UpdateAvatar(bool request)
{
auto SetAvatar = [&](QString const &email, QLabel *label){
if (mainwindow()->appsettings()->get_committer_icon) {
label->setFixedSize(QSize(48, 48));
QIcon icon = m->avatar_loader.fetch(email.toStdString(), request);
setAvatar(icon, label);
} else {
label->setVisible(false);
}
};
SetAvatar(ui->lineEdit_mail->text(), ui->label_user_avatar);
SetAvatar(ui->lineEdit_sign_mail->text(), ui->label_sign_avatar);
}
-CommitPropertyDialog::CommitPropertyDialog(QWidget *parent, MainWindow *mw, Git::CommitItem const *commit)
+CommitPropertyDialog::CommitPropertyDialog(QWidget *parent, BasicMainWindow *mw, Git::CommitItem const *commit)
: QDialog(parent)
, ui(new Ui::CommitPropertyDialog)
, m(new Private)
{
ui->setupUi(this);
m->commit = *commit;
init(mw);
}
-CommitPropertyDialog::CommitPropertyDialog(QWidget *parent, MainWindow *mw, QString const &commit_id)
+CommitPropertyDialog::CommitPropertyDialog(QWidget *parent, BasicMainWindow *mw, QString const &commit_id)
: QDialog(parent)
, ui(new Ui::CommitPropertyDialog)
, m(new Private)
{
ui->setupUi(this);
mw->queryCommit(commit_id, &m->commit);
init(mw);
}
CommitPropertyDialog::~CommitPropertyDialog()
{
m->avatar_loader.stop();
delete m;
delete ui;
}
-MainWindow *CommitPropertyDialog::mainwindow()
+BasicMainWindow *CommitPropertyDialog::mainwindow()
{
return m->mainwindow;
}
-void CommitPropertyDialog::setAvatar(QIcon icon, QLabel *label)
+void CommitPropertyDialog::setAvatar(QIcon const &icon, QLabel *label)
{
QPixmap pm = icon.pixmap(label->size());
label->setPixmap(pm);
}
void CommitPropertyDialog::showCheckoutButton(bool f)
{
ui->pushButton_checkout->setVisible(f);
}
void CommitPropertyDialog::showJumpButton(bool f)
{
ui->pushButton_jump->setVisible(f);
}
void CommitPropertyDialog::on_pushButton_checkout_clicked()
{
mainwindow()->checkout(this, &m->commit);
done(QDialog::Rejected);
}
void CommitPropertyDialog::on_pushButton_jump_clicked()
{
mainwindow()->jumpToCommit(m->commit.commit_id);
done(QDialog::Accepted);
}
void CommitPropertyDialog::on_pushButton_details_clicked()
{
mainwindow()->execCommitViewWindow(&m->commit);
}
void CommitPropertyDialog::on_pushButton_explorer_clicked()
{
mainwindow()->execCommitExploreWindow(this, &m->commit);
}
diff --git a/src/CommitPropertyDialog.h b/src/CommitPropertyDialog.h
index a78808d..74b9695 100644
--- a/src/CommitPropertyDialog.h
+++ b/src/CommitPropertyDialog.h
@@ -1,47 +1,46 @@
#ifndef COMMITPROPERTYDIALOG_H
#define COMMITPROPERTYDIALOG_H
#include <QDialog>
#include "Git.h"
class QLabel;
namespace Ui {
class CommitPropertyDialog;
}
-class MainWindow;
+class BasicMainWindow;
-class CommitPropertyDialog : public QDialog
-{
+class CommitPropertyDialog : public QDialog {
Q_OBJECT
private:
struct Private;
Private *m;
public:
- explicit CommitPropertyDialog(QWidget *parent, MainWindow *mw, Git::CommitItem const *commit);
- explicit CommitPropertyDialog(QWidget *parent, MainWindow *mw, QString const &commit_id);
- ~CommitPropertyDialog();
+ explicit CommitPropertyDialog(QWidget *parent, BasicMainWindow *mw, Git::CommitItem const *commit);
+ explicit CommitPropertyDialog(QWidget *parent, BasicMainWindow *mw, QString const &commit_id);
+ ~CommitPropertyDialog() override;
void showCheckoutButton(bool f);
void showJumpButton(bool f);
private slots:
void on_pushButton_checkout_clicked();
void on_pushButton_jump_clicked();
void on_pushButton_details_clicked();
void on_pushButton_explorer_clicked();
private:
Ui::CommitPropertyDialog *ui;
- void init(MainWindow *mw);
- MainWindow *mainwindow();
- void setAvatar(QIcon icon, QLabel *label);
+ void init(BasicMainWindow *mw);
+ BasicMainWindow *mainwindow();
+ void setAvatar(const QIcon &icon, QLabel *label);
void UpdateAvatar(bool request);
};
#endif // COMMITPROPERTYDIALOG_H
diff --git a/src/CommitPropertyDialog.ui b/src/CommitPropertyDialog.ui
index 0560ed9..ac9a0c8 100644
--- a/src/CommitPropertyDialog.ui
+++ b/src/CommitPropertyDialog.ui
@@ -1,386 +1,386 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CommitPropertyDialog</class>
<widget class="QDialog" name="CommitPropertyDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>527</width>
<height>482</height>
</rect>
</property>
<property name="windowTitle">
<string>Commit Property</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="ReadOnlyLineEdit" name="lineEdit_description">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="verticalSpacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Date</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_date"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Author</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_author"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Mail</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_mail"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_user_avatar">
<property name="minimumSize">
<size>
<width>64</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>64</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="frame_sign">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="label_signature_icon">
<property name="minimumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="pixmap">
- <pixmap resource="resources/resources.qrc">:/image/signature-good.png</pixmap>
+ <pixmap resource="../resources.qrc">:/image/signature-good.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>GPG Sign</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QFormLayout" name="formLayout_2">
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="verticalSpacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>ID</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_sign_id"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_sign_name"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Mail</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_sign_mail"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_sign_avatar">
<property name="minimumSize">
<size>
<width>64</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Commit ID</string>
</property>
</widget>
</item>
<item>
<widget class="ReadOnlyLineEdit" name="lineEdit_commit_id"/>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Parent IDs</string>
</property>
</widget>
</item>
<item>
<widget class="ReadOnlyPlainTextEdit" name="plainTextEdit_parent_ids">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButton_details">
<property name="text">
<string>Files...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_explorer">
<property name="text">
<string>Explorer</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_checkout">
<property name="text">
<string>Checkout</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_jump">
<property name="text">
<string>Jump</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_close">
<property name="text">
<string>Close</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ReadOnlyLineEdit</class>
<extends>QLineEdit</extends>
<header>ReadOnlyLineEdit.h</header>
</customwidget>
<customwidget>
<class>ReadOnlyPlainTextEdit</class>
<extends>QPlainTextEdit</extends>
<header>ReadOnlyPlainTextEdit.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>lineEdit_description</tabstop>
<tabstop>lineEdit_date</tabstop>
<tabstop>lineEdit_author</tabstop>
<tabstop>lineEdit_mail</tabstop>
<tabstop>lineEdit_commit_id</tabstop>
<tabstop>plainTextEdit_parent_ids</tabstop>
<tabstop>pushButton_checkout</tabstop>
<tabstop>pushButton_close</tabstop>
</tabstops>
<resources>
- <include location="resources/resources.qrc"/>
+ <include location="../resources.qrc"/>
</resources>
<connections>
<connection>
<sender>pushButton_close</sender>
<signal>clicked()</signal>
<receiver>CommitPropertyDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>368</x>
<y>347</y>
</hint>
<hint type="destinationlabel">
<x>367</x>
<y>394</y>
</hint>
</hints>
</connection>
</connections>
</ui>
diff --git a/src/CommitViewWindow.cpp b/src/CommitViewWindow.cpp
index 27c2912..bed5d30 100644
--- a/src/CommitViewWindow.cpp
+++ b/src/CommitViewWindow.cpp
@@ -1,71 +1,71 @@
#include "CommitViewWindow.h"
#include "ui_CommitViewWindow.h"
#include <QMenu>
struct CommitViewWindow::Private {
Git::CommitItem const *commit = nullptr;
QList<Git::Diff> diff_list;
};
-CommitViewWindow::CommitViewWindow(MainWindow *parent, const Git::CommitItem *commit)
+CommitViewWindow::CommitViewWindow(BasicMainWindow *parent, const Git::CommitItem *commit)
: QDialog(parent)
, ui(new Ui::CommitViewWindow)
, m(new Private)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
m->commit = commit;
ui->widget_diff->bind(mainwindow());
ui->lineEdit_message->setText(m->commit->message);
ui->lineEdit_id->setText(m->commit->commit_id);
mainwindow()->updateFilesList(m->commit->commit_id, &m->diff_list, ui->listWidget_files);
ui->listWidget_files->setCurrentRow(0);
}
CommitViewWindow::~CommitViewWindow()
{
delete m;
delete ui;
}
-MainWindow *CommitViewWindow::mainwindow()
+BasicMainWindow *CommitViewWindow::mainwindow()
{
- return qobject_cast<MainWindow *>(parent());
+ return qobject_cast<BasicMainWindow *>(parent());
}
void CommitViewWindow::on_listWidget_files_currentRowChanged(int currentRow)
{
if (currentRow >= 0 && currentRow < m->diff_list.size()) {
Git::Diff const &diff = m->diff_list[currentRow];
ui->widget_diff->updateDiffView(diff, false);
}
}
void CommitViewWindow::on_listWidget_files_customContextMenuRequested(const QPoint &pos)
{
GitPtr g = mainwindow()->git();
if (!mainwindow()->isValidWorkingCopy(g)) return;
QMenu menu;
QAction *a_history = menu.addAction(tr("History"));
QAction *a_properties = mainwindow()->addMenuActionProperty(&menu);
QPoint pt = ui->listWidget_files->mapToGlobal(pos) + QPoint(8, -8);
QAction *a = menu.exec(pt);
if (a) {
QListWidgetItem *item = ui->listWidget_files->currentItem();
if (a == a_history) {
mainwindow()->execFileHistory(item);
} else if (a == a_properties) {
mainwindow()->execFilePropertyDialog(item);
}
}
}
diff --git a/src/CommitViewWindow.h b/src/CommitViewWindow.h
index 232eef0..6097663 100644
--- a/src/CommitViewWindow.h
+++ b/src/CommitViewWindow.h
@@ -1,34 +1,33 @@
#ifndef COMMITVIEWWINDOW_H
#define COMMITVIEWWINDOW_H
#include "Git.h"
#include <QDialog>
-class MainWindow;
+class BasicMainWindow;
namespace Ui {
class CommitViewWindow;
}
-class CommitViewWindow : public QDialog
-{
+class CommitViewWindow : public QDialog {
Q_OBJECT
private:
struct Private;
Private *m;
- MainWindow *mainwindow();
+ BasicMainWindow *mainwindow();
public:
- explicit CommitViewWindow(MainWindow *parent, Git::CommitItem const *commit);
- ~CommitViewWindow();
+ explicit CommitViewWindow(BasicMainWindow *parent, Git::CommitItem const *commit);
+ ~CommitViewWindow() override;
private slots:
void on_listWidget_files_currentRowChanged(int currentRow);
void on_listWidget_files_customContextMenuRequested(const QPoint &pos);
private:
Ui::CommitViewWindow *ui;
};
#endif // COMMITVIEWWINDOW_H
diff --git a/src/ConfigCredentialHelperDialog.h b/src/ConfigCredentialHelperDialog.h
index 54a7da5..1814711 100644
--- a/src/ConfigCredentialHelperDialog.h
+++ b/src/ConfigCredentialHelperDialog.h
@@ -1,25 +1,24 @@
#ifndef CONFIGCREDENTIALHELPERDIALOG_H
#define CONFIGCREDENTIALHELPERDIALOG_H
#include <QDialog>
namespace Ui {
class ConfigCredentialHelperDialog;
}
-class ConfigCredentialHelperDialog : public QDialog
-{
+class ConfigCredentialHelperDialog : public QDialog {
Q_OBJECT
public:
- explicit ConfigCredentialHelperDialog(QWidget *parent = 0);
- ~ConfigCredentialHelperDialog();
+ explicit ConfigCredentialHelperDialog(QWidget *parent = nullptr);
+ ~ConfigCredentialHelperDialog() override;
QString helper() const;
void setHelper(QString const &helper);
private:
Ui::ConfigCredentialHelperDialog *ui;
};
#endif // CONFIGCREDENTIALHELPERDIALOG_H
diff --git a/src/ConfigSigningDialog.cpp b/src/ConfigSigningDialog.cpp
index 703ecb4..4886c03 100644
--- a/src/ConfigSigningDialog.cpp
+++ b/src/ConfigSigningDialog.cpp
@@ -1,76 +1,76 @@
#include "ConfigSigningDialog.h"
-#include "MainWindow.h"
+#include "BasicMainWindow.h"
#include "ui_ConfigSigningDialog.h"
-ConfigSigningDialog::ConfigSigningDialog(QWidget *parent, MainWindow *mw, bool local_enable) :
+ConfigSigningDialog::ConfigSigningDialog(QWidget *parent, BasicMainWindow *mw, bool local_enable) :
QDialog(parent),
ui(new Ui::ConfigSigningDialog)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
mainwindow_ = mw;
if (!mainwindow()->git()->isValidWorkingCopy()) {
local_enable = false;
}
ui->label_local->setVisible(local_enable);
ui->comboBox_sign_local->setVisible(local_enable);
updateSigningInfo();
}
ConfigSigningDialog::~ConfigSigningDialog()
{
delete ui;
}
-MainWindow *ConfigSigningDialog::mainwindow()
+BasicMainWindow *ConfigSigningDialog::mainwindow()
{
return mainwindow_;
}
void ConfigSigningDialog::updateSigningInfo()
{
GitPtr g = mainwindow()->git();
auto InitComboBox = [](QComboBox *cb, Git::SignPolicy pol){
cb->addItem("unset");
cb->addItem("false");
cb->addItem("true");
QString t;
if (pol == Git::SignPolicy::Unset) {
t = "unset";
} else if (pol == Git::SignPolicy::False) {
t = "false";
} else if (pol == Git::SignPolicy::True) {
t = "true";
}
cb->setCurrentText(t);
};
gpol_ = g->signPolicy(Git::Source::Global);
lpol_ = g->signPolicy(Git::Source::Local);
InitComboBox(ui->comboBox_sign_global, gpol_);
InitComboBox(ui->comboBox_sign_local, lpol_);
}
void ConfigSigningDialog::accept()
{
GitPtr g = mainwindow()->git();
auto SetSignPolicy = [&](QComboBox *cb, Git::Source src, Git::SignPolicy oldpol){
Git::SignPolicy pol = Git::SignPolicy::Unset;
QString s = cb->currentText();
if (s == "false") {
pol = Git::SignPolicy::False;
} else if (s == "true") {
pol = Git::SignPolicy::True;
}
if (pol != oldpol) g->setSignPolicy(src, pol);
};
SetSignPolicy(ui->comboBox_sign_global, Git::Source::Global, gpol_);
SetSignPolicy(ui->comboBox_sign_local, Git::Source::Local, lpol_);
QDialog::accept();
}
diff --git a/src/ConfigSigningDialog.h b/src/ConfigSigningDialog.h
index 300bdf4..40bc4d5 100644
--- a/src/ConfigSigningDialog.h
+++ b/src/ConfigSigningDialog.h
@@ -1,37 +1,35 @@
#ifndef CONFIGSIGNINGDIALOG_H
#define CONFIGSIGNINGDIALOG_H
#include "Git.h"
#include "gpg.h"
#include <QDialog>
-class MainWindow;
+class BasicMainWindow;
namespace Ui {
class ConfigSigningDialog;
}
-class ConfigSigningDialog : public QDialog
-{
+class ConfigSigningDialog : public QDialog {
Q_OBJECT
public:
- explicit ConfigSigningDialog(QWidget *parent, MainWindow *mw, bool local_enable);
- ~ConfigSigningDialog();
+ explicit ConfigSigningDialog(QWidget *parent, BasicMainWindow *mw, bool local_enable);
+ ~ConfigSigningDialog() override;
private:
Ui::ConfigSigningDialog *ui;
- MainWindow *mainwindow_;
- MainWindow *mainwindow();
+ BasicMainWindow *mainwindow_;
+ BasicMainWindow *mainwindow();
Git::SignPolicy gpol_;
Git::SignPolicy lpol_;
void updateSigningInfo();
- // QDialog interface
public slots:
- void accept();
+ void accept() override;
};
#endif // CONFIGSIGNINGDIALOG_H
diff --git a/src/CreateRepositoryDialog.cpp b/src/CreateRepositoryDialog.cpp
index b77849e..3720836 100644
--- a/src/CreateRepositoryDialog.cpp
+++ b/src/CreateRepositoryDialog.cpp
@@ -1,132 +1,132 @@
#include "CreateRepositoryDialog.h"
-#include "MainWindow.h"
+#include "BasicMainWindow.h"
#include "common/misc.h"
#include "ui_CreateRepositoryDialog.h"
#include <QFileDialog>
#include <QMessageBox>
#include "Git.h"
-CreateRepositoryDialog::CreateRepositoryDialog(MainWindow *parent, QString const &dir) :
+CreateRepositoryDialog::CreateRepositoryDialog(BasicMainWindow *parent, QString const &dir) :
QDialog(parent),
ui(new Ui::CreateRepositoryDialog)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
already_exists_ = tr("A valid git repository already exists there.");
ui->lineEdit_path->setText(dir);
ui->groupBox_remote->setChecked(false);
ui->lineEdit_remote_name->setText("origin");
validate(false);
}
CreateRepositoryDialog::~CreateRepositoryDialog()
{
delete ui;
}
-MainWindow *CreateRepositoryDialog::mainwindow()
+BasicMainWindow *CreateRepositoryDialog::mainwindow()
{
- return qobject_cast<MainWindow *>(parent());
+ return qobject_cast<BasicMainWindow *>(parent());
}
void CreateRepositoryDialog::accept()
{
QString path = ui->lineEdit_path->text();
if (!QFileInfo(path).isDir()) {
QMessageBox::warning(this, tr("Create Repository"), tr("The specified path is not a directory."));
return;
}
if (Git::isValidWorkingCopy(path)) {
QMessageBox::warning(this, tr("Create Repository"), already_exists_);
return;
}
if (!QFileInfo(path).isDir()) {
QMessageBox::warning(this, tr("Create Repository"), tr("The specified path is not a directory."));
return;
}
if (remoteName().indexOf('\"') >= 0 || remoteURL().indexOf('\"') >= 0) { // 手抜き
QMessageBox::warning(this, tr("Create Repository"), tr("Remote name is invalid."));
return;
}
done(QDialog::Accepted);
}
void CreateRepositoryDialog::on_pushButton_browse_path_clicked()
{
QString path = QFileDialog::getExistingDirectory(this, tr("Destination Path"), mainwindow()->defaultWorkingDir());
if (!path.isEmpty()) {
path = misc::normalizePathSeparator(path);
ui->lineEdit_path->setText(path);
}
}
QString CreateRepositoryDialog::path() const
{
return ui->lineEdit_path->text();
}
QString CreateRepositoryDialog::name() const
{
return ui->lineEdit_name->text();
}
QString CreateRepositoryDialog::remoteName() const
{
return ui->groupBox_remote->isChecked() ? ui->lineEdit_remote_name->text() : QString();
}
QString CreateRepositoryDialog::remoteURL() const
{
return ui->groupBox_remote->isChecked() ? ui->lineEdit_remote_url->text() : QString();
}
void CreateRepositoryDialog::validate(bool change_name)
{
QString path = this->path();
{
QString text;
if (Git::isValidWorkingCopy(path)) {
text = already_exists_;
}
ui->label_warning->setText(text);
}
if (change_name) {
int i = path.lastIndexOf('/');
int j = path.lastIndexOf('\\');
if (i < j) i = j;
if (i >= 0) {
QString name = path.mid(i + 1);
ui->lineEdit_name->setText(name);
}
}
}
void CreateRepositoryDialog::on_lineEdit_path_textChanged(QString const &)
{
validate(true);
}
void CreateRepositoryDialog::on_lineEdit_name_textChanged(QString const &)
{
validate(false);
}
void CreateRepositoryDialog::on_groupBox_remote_toggled(bool)
{
validate(false);
}
void CreateRepositoryDialog::on_pushButton_test_repo_clicked()
{
QString url = ui->lineEdit_remote_url->text();
mainwindow()->testRemoteRepositoryValidity(url);
validate(false);
}
diff --git a/src/CreateRepositoryDialog.h b/src/CreateRepositoryDialog.h
index 1c16376..8156ff3 100644
--- a/src/CreateRepositoryDialog.h
+++ b/src/CreateRepositoryDialog.h
@@ -1,43 +1,42 @@
#ifndef CREATEREPOSITORYDIALOG_H
#define CREATEREPOSITORYDIALOG_H
#include <QDialog>
namespace Ui {
class CreateRepositoryDialog;
}
-class MainWindow;
+class BasicMainWindow;
-class CreateRepositoryDialog : public QDialog
-{
+class CreateRepositoryDialog : public QDialog {
Q_OBJECT
private:
QString already_exists_;
public:
- explicit CreateRepositoryDialog(MainWindow *parent, QString const &dir = QString());
- ~CreateRepositoryDialog();
+ explicit CreateRepositoryDialog(BasicMainWindow *parent, QString const &dir = QString());
+ ~CreateRepositoryDialog() override;
QString path() const;
QString name() const;
QString remoteName() const;
QString remoteURL() const;
private slots:
void on_lineEdit_path_textChanged(QString const &arg1);
void on_pushButton_browse_path_clicked();
void on_lineEdit_name_textChanged(QString const &arg1);
void on_groupBox_remote_toggled(bool arg1);
void on_pushButton_test_repo_clicked();
private:
Ui::CreateRepositoryDialog *ui;
void validate(bool change_name);
- MainWindow *mainwindow();
+ BasicMainWindow *mainwindow();
public slots:
- void accept();
+ void accept() override;
};
#endif // CREATEREPOSITORYDIALOG_H
diff --git a/src/DeleteBranchDialog.h b/src/DeleteBranchDialog.h
index 7834e9d..630a780 100644
--- a/src/DeleteBranchDialog.h
+++ b/src/DeleteBranchDialog.h
@@ -1,30 +1,29 @@
#ifndef DELETEBRANCHDIALOG_H
#define DELETEBRANCHDIALOG_H
#include <QDialog>
namespace Ui {
class DeleteBranchDialog;
}
-class DeleteBranchDialog : public QDialog
-{
+class DeleteBranchDialog : public QDialog {
Q_OBJECT
private:
struct Private;
Private *m;
public:
explicit DeleteBranchDialog(QWidget *parent, bool remote, QStringList const &all_local_branch_names, QStringList const &current_local_branch_names);
- ~DeleteBranchDialog();
+ ~DeleteBranchDialog() override;
QStringList selectedBranchNames() const;
private slots:
void on_checkBox_all_branches_toggled(bool checked);
private:
Ui::DeleteBranchDialog *ui;
void updateList();
bool isRemote() const;
};
#endif // DELETEBRANCHDIALOG_H
diff --git a/src/DeleteTagsDialog.h b/src/DeleteTagsDialog.h
index 11b2c25..b34d787 100644
--- a/src/DeleteTagsDialog.h
+++ b/src/DeleteTagsDialog.h
@@ -1,29 +1,28 @@
#ifndef DELETETAGSDIALOG_H
#define DELETETAGSDIALOG_H
#include "Git.h"
#include <QDialog>
namespace Ui {
class DeleteTagsDialog;
}
-class DeleteTagsDialog : public QDialog
-{
+class DeleteTagsDialog : public QDialog {
Q_OBJECT
public:
explicit DeleteTagsDialog(QWidget *parent, QList<Git::Tag> const &list);
- ~DeleteTagsDialog();
+ ~DeleteTagsDialog() override;
QStringList selectedTags() const;
private slots:
void on_pushButton_check_all_clicked();
private:
Ui::DeleteTagsDialog *ui;
};
#endif // DELETETAGSDIALOG_H
diff --git a/src/DialogHeaderFrame.h b/src/DialogHeaderFrame.h
index 4d3e31c..19a56a5 100644
--- a/src/DialogHeaderFrame.h
+++ b/src/DialogHeaderFrame.h
@@ -1,21 +1,14 @@
#ifndef DIALOGHEADERFRAME_H
#define DIALOGHEADERFRAME_H
#include <QFrame>
-class DialogHeaderFrame : public QFrame
-{
+class DialogHeaderFrame : public QFrame {
Q_OBJECT
public:
explicit DialogHeaderFrame(QWidget *parent = nullptr);
-
-signals:
-
-public slots:
-
- // QWidget interface
protected:
void paintEvent(QPaintEvent *);
};
#endif // DIALOGHEADERFRAME_H
diff --git a/src/DirectoryLineEdit.h b/src/DirectoryLineEdit.h
index d2250ee..2ee124a 100644
--- a/src/DirectoryLineEdit.h
+++ b/src/DirectoryLineEdit.h
@@ -1,24 +1,16 @@
#ifndef DIRECTORYLINEEDIT_H
#define DIRECTORYLINEEDIT_H
#include <QLineEdit>
#include <QWidget>
-class DirectoryLineEdit : public QLineEdit
-{
+class DirectoryLineEdit : public QLineEdit {
Q_OBJECT
public:
- explicit DirectoryLineEdit(QWidget *parent = 0);
-
-signals:
-
-public slots:
-protected:
- void dragEnterEvent(QDragEnterEvent *event);
-
- // QWidget interface
+ explicit DirectoryLineEdit(QWidget *parent = nullptr);
protected:
- void dropEvent(QDropEvent *event);
+ void dragEnterEvent(QDragEnterEvent *event) override;
+ void dropEvent(QDropEvent *event) override;
};
#endif // DIRECTORYLINEEDIT_H
diff --git a/src/DoYouWantToInitDialog.h b/src/DoYouWantToInitDialog.h
index b34fd9f..e80ffa4 100644
--- a/src/DoYouWantToInitDialog.h
+++ b/src/DoYouWantToInitDialog.h
@@ -1,28 +1,24 @@
#ifndef DOYOUWANTTOINITDIALOG_H
#define DOYOUWANTTOINITDIALOG_H
#include <QDialog>
namespace Ui {
class DoYouWantToInitDialog;
}
class DoYouWantToInitDialog : public QDialog {
Q_OBJECT
-
public:
explicit DoYouWantToInitDialog(QWidget *parent, QString const &dir);
- ~DoYouWantToInitDialog();
+ ~DoYouWantToInitDialog() override;
private slots:
void on_radioButton_yes_clicked();
-
void on_radioButton_no_clicked();
-
void on_pushButton_clicked();
-
private:
Ui::DoYouWantToInitDialog *ui;
};
#endif // DOYOUWANTTOINITDIALOG_H
diff --git a/src/EditGitIgnoreDialog.cpp b/src/EditGitIgnoreDialog.cpp
index 61ee942..e9374d4 100644
--- a/src/EditGitIgnoreDialog.cpp
+++ b/src/EditGitIgnoreDialog.cpp
@@ -1,65 +1,65 @@
#include "EditGitIgnoreDialog.h"
#include "MainWindow.h"
#include "TextEditDialog.h"
#include "ui_EditGitIgnoreDialog.h"
#include <QFileInfo>
-EditGitIgnoreDialog::EditGitIgnoreDialog(MainWindow *parent, QString gitignore_path, QString const &file)
+EditGitIgnoreDialog::EditGitIgnoreDialog(MainWindow *parent, QString const &gitignore_path, QString const &file)
: QDialog(parent)
, ui(new Ui::EditGitIgnoreDialog)
, gitignore_path(gitignore_path)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
QFileInfo info(file);
ui->radioButton_1->setText(file);
ui->radioButton_2->setText("*." + info.suffix());
int i = file.indexOf('/');
if (i > 0) {
ui->radioButton_3->setText(file.mid(0, i + 1));
int j = file.lastIndexOf('/');
if (i < j) {
ui->radioButton_4->setText(file.mid(0, j + 1));
} else {
ui->radioButton_4->setVisible(false);
}
} else {
ui->radioButton_3->setVisible(false);
ui->radioButton_4->setVisible(false);
}
ui->radioButton_1->click();
}
EditGitIgnoreDialog::~EditGitIgnoreDialog()
{
delete ui;
}
MainWindow *EditGitIgnoreDialog::mainwindow()
{
return qobject_cast<MainWindow *>(parent());
}
QString EditGitIgnoreDialog::text() const
{
if (ui->radioButton_1->isChecked()) return ui->radioButton_1->text();
if (ui->radioButton_2->isChecked()) return ui->radioButton_2->text();
if (ui->radioButton_3->isChecked()) return ui->radioButton_3->text();
if (ui->radioButton_4->isChecked()) return ui->radioButton_4->text();
return QString();
}
void EditGitIgnoreDialog::on_pushButton_edit_file_clicked()
{
if (TextEditDialog::editFile(this, gitignore_path, ".gitignore", text() + '\n')) {
mainwindow()->updateCurrentFilesList();
done(QDialog::Accepted);
}
}
diff --git a/src/EditGitIgnoreDialog.h b/src/EditGitIgnoreDialog.h
index d666bd4..7404699 100644
--- a/src/EditGitIgnoreDialog.h
+++ b/src/EditGitIgnoreDialog.h
@@ -1,31 +1,30 @@
#ifndef EDITGITIGNOREDIALOG_H
#define EDITGITIGNOREDIALOG_H
#include <QDialog>
class MainWindow;
namespace Ui {
class EditGitIgnoreDialog;
}
-class EditGitIgnoreDialog : public QDialog
-{
+class EditGitIgnoreDialog : public QDialog {
Q_OBJECT
private:
QString gitignore_path;
public:
- explicit EditGitIgnoreDialog(MainWindow *parent, QString gitignore_path, QString const &file);
- ~EditGitIgnoreDialog();
+ explicit EditGitIgnoreDialog(MainWindow *parent, const QString &gitignore_path, QString const &file);
+ ~EditGitIgnoreDialog() override;
QString text() const;
private slots:
void on_pushButton_edit_file_clicked();
private:
Ui::EditGitIgnoreDialog *ui;
MainWindow *mainwindow();
};
#endif // EDITGITIGNOREDIALOG_H
diff --git a/src/EditRemoteDialog.cpp b/src/EditRemoteDialog.cpp
index a8b4266..4632fa2 100644
--- a/src/EditRemoteDialog.cpp
+++ b/src/EditRemoteDialog.cpp
@@ -1,66 +1,66 @@
#include "EditRemoteDialog.h"
#include "ui_EditRemoteDialog.h"
-#include "MainWindow.h"
+#include "BasicMainWindow.h"
-EditRemoteDialog::EditRemoteDialog(MainWindow *parent, Operation op) :
+EditRemoteDialog::EditRemoteDialog(BasicMainWindow *parent, Operation op) :
QDialog(parent),
ui(new Ui::EditRemoteDialog)
{
ui->setupUi(this);
if (op == RemoteSet) {
ui->lineEdit_name->setReadOnly(true);
ui->lineEdit_name->setEnabled(false);
}
}
EditRemoteDialog::~EditRemoteDialog()
{
delete ui;
}
-MainWindow *EditRemoteDialog::mainwindow()
+BasicMainWindow *EditRemoteDialog::mainwindow()
{
- return qobject_cast<MainWindow *>(parent());
+ return qobject_cast<BasicMainWindow *>(parent());
}
void EditRemoteDialog::setName(QString const &s) const
{
ui->lineEdit_name->setText(s);
}
void EditRemoteDialog::setUrl(QString const &s) const
{
ui->lineEdit_url->setText(s);
}
QString EditRemoteDialog::name() const
{
return ui->lineEdit_name->text();
}
QString EditRemoteDialog::url() const
{
return ui->lineEdit_url->text();
}
int EditRemoteDialog::exec()
{
if (ui->lineEdit_name->text().isEmpty()) {
ui->lineEdit_name->setFocus();
} else {
ui->lineEdit_url->setFocus();
}
return QDialog::exec();
}
void EditRemoteDialog::on_pushButton_test_clicked()
{
QString url = ui->lineEdit_url->text();
if (mainwindow()->testRemoteRepositoryValidity(url)) {
ui->pushButton_ok->setFocus();
} else {
ui->lineEdit_url->setFocus();
}
}
diff --git a/src/EditRemoteDialog.h b/src/EditRemoteDialog.h
index 2b2e115..52d8cfd 100644
--- a/src/EditRemoteDialog.h
+++ b/src/EditRemoteDialog.h
@@ -1,38 +1,36 @@
#ifndef EDITREMOTEDIALOG_H
#define EDITREMOTEDIALOG_H
#include <QDialog>
-class MainWindow;
+class BasicMainWindow;
namespace Ui {
class EditRemoteDialog;
}
-class EditRemoteDialog : public QDialog
-{
+class EditRemoteDialog : public QDialog {
Q_OBJECT
public:
enum Operation {
RemoteAdd,
RemoteSet,
};
+private:
+ Ui::EditRemoteDialog *ui;
+ BasicMainWindow *mainwindow();
public:
- explicit EditRemoteDialog(MainWindow *parent, Operation op);
- ~EditRemoteDialog();
+ explicit EditRemoteDialog(BasicMainWindow *parent, Operation op);
+ ~EditRemoteDialog() override;
void setName(QString const &s) const;
void setUrl(QString const &s) const;
QString name() const;
QString url() const;
- int exec();
+ int exec() override;
private slots:
void on_pushButton_test_clicked();
-
-private:
- Ui::EditRemoteDialog *ui;
- MainWindow *mainwindow();
};
#endif // EDITREMOTEDIALOG_H
diff --git a/src/EditTagsDialog.h b/src/EditTagsDialog.h
index 909d7ef..3485858 100644
--- a/src/EditTagsDialog.h
+++ b/src/EditTagsDialog.h
@@ -1,34 +1,33 @@
#ifndef EDITTAGSDIALOG_H
#define EDITTAGSDIALOG_H
#include "Git.h"
#include <QDialog>
class MainWindow;
namespace Ui {
class EditTagsDialog;
}
-class EditTagsDialog : public QDialog
-{
+class EditTagsDialog : public QDialog {
Q_OBJECT
private:
Ui::EditTagsDialog *ui;
Git::CommitItem const *commit_;
private:
QStringList selectedTags();
MainWindow *mainwindow();
QList<Git::Tag> queryTagList();
void updateTagList();
public:
explicit EditTagsDialog(MainWindow *parent, Git::CommitItem const *commit);
- ~EditTagsDialog();
+ ~EditTagsDialog() override;
private slots:
void on_pushButton_add_clicked();
void on_pushButton_delete_clicked();
};
#endif // EDITTAGSDIALOG_H
diff --git a/src/ExperimentDialog.h b/src/ExperimentDialog.h
index d1cb7f9..6046636 100644
--- a/src/ExperimentDialog.h
+++ b/src/ExperimentDialog.h
@@ -1,24 +1,19 @@
#ifndef EXPERIMENTDIALOG_H
#define EXPERIMENTDIALOG_H
#include <QDialog>
namespace Ui {
class ExperimentDialog;
}
-class ExperimentDialog : public QDialog
-{
+class ExperimentDialog : public QDialog {
Q_OBJECT
-
public:
- explicit ExperimentDialog(QWidget *parent = 0);
- ~ExperimentDialog();
-
-private slots:
-
+ explicit ExperimentDialog(QWidget *parent = nullptr);
+ ~ExperimentDialog() override;
private:
Ui::ExperimentDialog *ui;
};
#endif // EXPERIMENTDIALOG_H
diff --git a/src/FileDiffSliderWidget.h b/src/FileDiffSliderWidget.h
index aed3e87..f6ffd80 100644
--- a/src/FileDiffSliderWidget.h
+++ b/src/FileDiffSliderWidget.h
@@ -1,54 +1,53 @@
#ifndef FILEDIFFSLIDERWIDGET_H
#define FILEDIFFSLIDERWIDGET_H
#include "MainWindow.h"
#include <QWidget>
#include <QPixmap>
#include <functional>
#include "AbstractCharacterBasedApplication.h"
#include "Theme.h"
-typedef Document::Line TextDiffLine;
-typedef QList<Document::Line> TextDiffLineList;
+using TextDiffLine = Document::Line;
+using TextDiffLineList = QList<Document::Line>;
enum class DiffPane {
Left,
Right,
};
typedef std::function<QPixmap(DiffPane pane, int width, int height)> fn_pixmap_maker_t;
-class FileDiffSliderWidget : public QWidget
-{
+class FileDiffSliderWidget : public QWidget {
Q_OBJECT
private:
struct Private;
Private *m;
void scroll_(int pos);
QPixmap makeDiffPixmap(DiffPane pane, int width, int height);
void setValue(int v);
void internalSetValue(int v);
public:
- explicit FileDiffSliderWidget(QWidget *parent = 0);
- ~FileDiffSliderWidget();
+ explicit FileDiffSliderWidget(QWidget *parent = nullptr);
+ ~FileDiffSliderWidget() override;
void clear(bool v);
void setScrollPos(int total, int value, int size);
void init(fn_pixmap_maker_t pixmap_maker, ThemePtr theme);
void updatePixmap();
static QPixmap makeDiffPixmap(int width, int height, const TextDiffLineList &lines, ThemePtr theme);
protected:
- void paintEvent(QPaintEvent *);
- void resizeEvent(QResizeEvent *);
- void mousePressEvent(QMouseEvent *);
- void mouseMoveEvent(QMouseEvent *);
- void wheelEvent(QWheelEvent *event);
+ void paintEvent(QPaintEvent *) override;
+ void resizeEvent(QResizeEvent *) override;
+ void mousePressEvent(QMouseEvent *) override;
+ void mouseMoveEvent(QMouseEvent *) override;
+ void wheelEvent(QWheelEvent *event) override;
signals:
void valueChanged(int value);
void scrollByWheel(int lines);
};
#endif // FILEDIFFSLIDERWIDGET_H
diff --git a/src/FileDiffWidget.cpp b/src/FileDiffWidget.cpp
index 8c35bb8..be6705f 100644
--- a/src/FileDiffWidget.cpp
+++ b/src/FileDiffWidget.cpp
@@ -1,744 +1,744 @@
#include <memory>
#include <memory>
#include "FileDiffWidget.h"
#include "ui_FileDiffWidget.h"
#include "BigDiffWindow.h"
#include "GitDiff.h"
#include "common/joinpath.h"
#include "MainWindow.h"
#include "common/misc.h"
#include "ApplicationGlobal.h"
#include "Theme.h"
#include <QBuffer>
#include <QDebug>
#include <QKeyEvent>
#include <QMenu>
#include <QPainter>
#include <QStyle>
#include <QTextCodec>
enum {
DiffIndexRole = Qt::UserRole,
};
struct FileDiffWidget::Private {
- MainWindow *mainwindow = nullptr;
+ BasicMainWindow *mainwindow = nullptr;
InitParam_ init_param_;
Git::CommitItemList commit_item_list;
std::vector<std::string> original_lines;
TextEditorEnginePtr engine_left;
TextEditorEnginePtr engine_right;
TextDiffLineList left_lines;
TextDiffLineList right_lines;
int max_line_length = 0;
int term_cursor_row = 0;
int term_cursor_col = 0;
QTextCodec *text_codec = nullptr;
};
FileDiffWidget::FileDiffWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::FileDiffWidget)
, m(new Private)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
ui->widget_diff_slider->init([&](DiffPane pane, int width, int height){
return makeDiffPixmap(pane, width, height);
}, global->theme);
connect(ui->widget_diff_slider, SIGNAL(valueChanged(int)), this, SLOT(scrollTo(int)));
connect(ui->widget_diff_left->texteditor(), SIGNAL(moved(int,int,int,int)), this, SLOT(onMoved(int,int,int,int)));
connect(ui->widget_diff_right->texteditor(), SIGNAL(moved(int,int,int,int)), this, SLOT(onMoved(int,int,int,int)));
setFocusAcceptable(true);
int n = style()->pixelMetric(QStyle::PM_ScrollBarExtent);
ui->toolButton_fullscreen->setFixedSize(n, n);
setMaximizeButtonEnabled(true);
m->engine_left = std::make_shared<TextEditorEngine>();
m->engine_right = std::make_shared<TextEditorEngine>();
ui->widget_diff_left->setDiffMode(m->engine_left, ui->verticalScrollBar, ui->horizontalScrollBar);
ui->widget_diff_right->setDiffMode(m->engine_right, ui->verticalScrollBar, ui->horizontalScrollBar);
setViewType(FileViewType::None);
}
FileDiffWidget::~FileDiffWidget()
{
delete m;
delete ui;
}
void FileDiffWidget::setViewType(FileViewType type)
{
ui->widget_diff_left->setViewType(type);
ui->widget_diff_right->setViewType(type);
}
void FileDiffWidget::setMaximizeButtonEnabled(bool f)
{
ui->toolButton_fullscreen->setVisible(f);
ui->toolButton_fullscreen->setEnabled(f);
}
FileDiffWidget::ViewStyle FileDiffWidget::viewstyle() const
{
return m->init_param_.view_style;
}
-void FileDiffWidget::bind(MainWindow *mw)
+void FileDiffWidget::bind(BasicMainWindow *mw)
{
Q_ASSERT(mw);
m->mainwindow = mw;
ui->widget_diff_left->bind(mw, this, ui->verticalScrollBar, ui->horizontalScrollBar, mw->themeForTextEditor());
ui->widget_diff_right->bind(mw, this, ui->verticalScrollBar, ui->horizontalScrollBar, mw->themeForTextEditor());
connect(ui->verticalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(onVerticalScrollValueChanged(int)));
connect(ui->horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(onHorizontalScrollValueChanged(int)));
}
-MainWindow *FileDiffWidget::mainwindow()
+BasicMainWindow *FileDiffWidget::mainwindow()
{
return m->mainwindow;
}
GitPtr FileDiffWidget::git()
{
if (!mainwindow()) {
qDebug() << "Maybe, you forgot to call FileDiffWidget::bind() ?";
return GitPtr();
}
return mainwindow()->git();
}
Git::Object FileDiffWidget::cat_file(GitPtr /*g*/, QString const &id)
{
return mainwindow()->cat_file(id);
}
int FileDiffWidget::totalTextLines() const
{
return m->engine_left->document.lines.size();
}
void FileDiffWidget::clearDiffView()
{
ui->widget_diff_slider->clear(false);
}
int FileDiffWidget::fileviewHeight() const
{
return ui->widget_diff_left->height();
}
void FileDiffWidget::resetScrollBarValue()
{
ui->verticalScrollBar->setValue(0);
ui->horizontalScrollBar->setValue(0);
}
void FileDiffWidget::scrollToBottom()
{
QScrollBar *sb = ui->verticalScrollBar;
sb->setValue(sb->maximum());
}
void FileDiffWidget::updateSliderCursor()
{
if (viewstyle() == SideBySideText) {
ui->widget_diff_slider->clear(true);
}
}
void FileDiffWidget::updateControls()
{
updateSliderCursor();
}
void FileDiffWidget::makeSideBySideDiffData(Git::Diff const &diff, std::vector<std::string> const &original_lines, TextDiffLineList *left_lines, TextDiffLineList *right_lines)
{
left_lines->clear();
right_lines->clear();
m->original_lines = original_lines;
size_t linenum = original_lines.size();
std::vector<HunkItem> hunks;
int number = 0;
for (auto it = diff.hunks.begin(); it != diff.hunks.end(); it++, number++) {
std::string at = it->at;
if (strncmp(at.c_str(), "@@ -", 4) == 0) {
size_t pos = 0;
size_t len = 0;
char const *p = at.c_str() + 4;
auto ParseNumber = [&](){
size_t v = 0;
while (isdigit(*p & 0xff)) {
v = v * 10 + (*p - '0');
p++;
}
return v;
};
pos = ParseNumber();
if (*p == ',') {
p++;
len = ParseNumber();
} else {
len = 1;
}
if (pos > 0) pos--;
HunkItem item;
item.hunk_number = number;
item.pos = pos;
item.len = len;
for (std::string const &line : it->lines) {
item.lines.push_back(line);
}
hunks.push_back(item);
}
}
std::sort(hunks.begin(), hunks.end(), [](HunkItem const &l, HunkItem const &r){
return l.pos + l.len < r.pos + r.len;
});
size_t h = hunks.size();
while (linenum > 0 || h > 0) {
while (h > 0) {
int hunk_number = h - 1;
HunkItem const &hi = hunks[hunk_number];
if (hi.pos + hi.len < linenum) {
break;
}
std::vector<TextDiffLine> tmp_left;
std::vector<TextDiffLine> tmp_right;
int minus = 0;
int plus = 0;
auto FlushBlank = [&](){
while (minus < plus) {
tmp_left.emplace_back();
minus++;
}
while (minus > plus) {
tmp_right.emplace_back();
plus++;
}
minus = plus = 0;
};
for (auto line : hi.lines) {
int c = line[0] & 0xff;
line = line.substr(1);
if (c == '-') {
minus++;
TextDiffLine l(line, TextDiffLine::Del);
l.hunk_number = hunk_number;
tmp_left.push_back(l);
} else if (c == '+') {
plus++;
TextDiffLine l(line, TextDiffLine::Add);
l.hunk_number = hunk_number;
tmp_right.push_back(l);
} else {
FlushBlank();
TextDiffLine l(line, TextDiffLine::Normal);
l.hunk_number = hunk_number;
tmp_left.push_back(l);
tmp_right.push_back(l);
}
}
FlushBlank();
auto ComplementNewLine = [](std::vector<TextDiffLine> *lines){
for (TextDiffLine &line : *lines) {
int n = line.text.size();
if (n > 0) {
int c = line.text[n - 1] & 0xff;
if (c != '\r' && c != '\n') {
line.text.push_back('\n');
}
}
}
};
ComplementNewLine(&tmp_left);
ComplementNewLine(&tmp_right);
for (auto it = tmp_left.rbegin(); it != tmp_left.rend(); it++) {
TextDiffLine l(*it);
if (m->text_codec) {
if (!l.text.isEmpty()) {
QString s = QString::fromUtf8(l.text.data(), l.text.size());
l.text = m->text_codec->fromUnicode(s);
}
}
left_lines->push_back(l);
}
for (auto it = tmp_right.rbegin(); it != tmp_right.rend(); it++) {
TextDiffLine l(*it);
if (m->text_codec) {
if (!l.text.isEmpty()) {
QString s = QString::fromUtf8(l.text.data(), l.text.size());
l.text = m->text_codec->fromUnicode(s);
}
}
right_lines->push_back(l);
}
linenum = hi.pos;
h--;
}
if (linenum > 0) {
linenum--;
if (linenum < (size_t)original_lines.size()) {
std::string line = original_lines[linenum];
left_lines->push_back(TextDiffLine(line, TextDiffLine::Normal));
right_lines->push_back(TextDiffLine(line, TextDiffLine::Normal));
}
}
}
std::reverse(left_lines->begin(), left_lines->end());
std::reverse(right_lines->begin(), right_lines->end());
}
void FileDiffWidget::setDiffText(Git::Diff const &diff, TextDiffLineList const &left, TextDiffLineList const &right)
{
m->max_line_length = 0;
enum Pane {
Left,
Right,
};
auto SetLineNumber = [&](TextDiffLineList const &lines, Pane pane, TextDiffLineList *out){
out->clear();
int linenum = 1;
for (TextDiffLine const &line : lines) {
TextDiffLine item = line;
switch (item.type) {
case TextDiffLine::Normal:
item.line_number = linenum++;
break;
case TextDiffLine::Add:
if (pane == Pane::Right) {
item.line_number = linenum++;
}
break;
case TextDiffLine::Del:
if (pane == Pane::Left) {
item.line_number = linenum++;
}
break;
default:
item.line_number = linenum; // 行番号は設定するが、インクリメントはしない
break;
}
out->push_back(item);
}
};
SetLineNumber(left, Pane::Left, &m->left_lines);
SetLineNumber(right, Pane::Right, &m->right_lines);
ui->widget_diff_left->setText(&m->left_lines, mainwindow(), diff.blob.a_id, diff.path);
ui->widget_diff_right->setText(&m->right_lines, mainwindow(), diff.blob.b_id, diff.path);
refrectScrollBar();
ui->widget_diff_slider->clear(true);
}
FileViewType FileDiffWidget::setupPreviewWidget()
{
clearDiffView();
QString mimetype_l = mainwindow()->determinFileType(m->init_param_.bytes_a, true);
QString mimetype_r = mainwindow()->determinFileType(m->init_param_.bytes_b, true);
if (misc::isImage(mimetype_l) || misc::isImage(mimetype_r)) { // image
ui->verticalScrollBar->setVisible(false);
ui->horizontalScrollBar->setVisible(false);
ui->widget_diff_slider->setVisible(false);
ui->widget_diff_left->setImage(mimetype_l, m->init_param_.bytes_a, m->init_param_.diff.blob.a_id, m->init_param_.diff.path);
ui->widget_diff_right->setImage(mimetype_r, m->init_param_.bytes_b, m->init_param_.diff.blob.b_id, m->init_param_.diff.path);
return FileViewType::Image;
} else { // text
ui->verticalScrollBar->setVisible(true);
ui->horizontalScrollBar->setVisible(true);
ui->widget_diff_slider->setVisible(true);
setViewType(FileViewType::Text);
return FileViewType::Text;
}
}
void FileDiffWidget::setSingleFile(QByteArray const &ba, QString const &id, QString const &path)
{
m->init_param_ = InitParam_();
m->init_param_.view_style = FileDiffWidget::ViewStyle::SingleFile;
m->init_param_.bytes_a = ba;
m->init_param_.diff.path = path;
m->init_param_.diff.blob.a_id = id;
}
void FileDiffWidget::setOriginalLines_(QByteArray const &ba)
{
m->original_lines.clear();
if (!ba.isEmpty()) {
char const *begin = ba.data();
char const *end = begin + ba.size();
misc::splitLines(begin, end, &m->original_lines, true);
}
}
void FileDiffWidget::setLeftOnly(QByteArray const &ba, Git::Diff const &diff)
{
m->init_param_ = InitParam_();
m->init_param_.view_style = FileDiffWidget::ViewStyle::LeftOnly;
m->init_param_.bytes_a = ba;
m->init_param_.diff = diff;
setOriginalLines_(ba);
if (setupPreviewWidget() == FileViewType::Text) {
TextDiffLineList left_lines;
TextDiffLineList right_lines;
for (std::string const &line : m->original_lines) {
left_lines.push_back(TextDiffLine(line, TextDiffLine::Del));
right_lines.push_back(TextDiffLine());
}
setDiffText(diff, left_lines, right_lines);
}
}
void FileDiffWidget::setRightOnly(QByteArray const &ba, Git::Diff const &diff)
{
m->init_param_ = InitParam_();
m->init_param_.view_style = FileDiffWidget::ViewStyle::RightOnly;
m->init_param_.bytes_b = ba;
m->init_param_.diff = diff;
setOriginalLines_(ba);
if (setupPreviewWidget() == FileViewType::Text) {
TextDiffLineList left_lines;
TextDiffLineList right_lines;
for (std::string const &line : m->original_lines) {
left_lines.push_back(TextDiffLine());
right_lines.push_back(TextDiffLine(line, TextDiffLine::Add));
}
setDiffText(diff, left_lines, right_lines);
}
}
void FileDiffWidget::setSideBySide(QByteArray const &ba, Git::Diff const &diff, bool uncommited, QString const &workingdir)
{
m->init_param_ = InitParam_();
m->init_param_.view_style = FileDiffWidget::ViewStyle::SideBySideText;
m->init_param_.bytes_a = ba;
m->init_param_.diff = diff;
m->init_param_.uncommited = uncommited;
m->init_param_.workingdir = workingdir;
setOriginalLines_(ba);
if (setupPreviewWidget() == FileViewType::Text) {
TextDiffLineList left_lines;
TextDiffLineList right_lines;
makeSideBySideDiffData(diff, m->original_lines, &left_lines, &right_lines);
setDiffText(diff, left_lines, right_lines);
}
}
void FileDiffWidget::setSideBySide_(QByteArray const &ba_a, QByteArray const &ba_b, QString const &workingdir)
{
m->init_param_ = InitParam_();
m->init_param_.view_style = FileDiffWidget::ViewStyle::SideBySideImage;
m->init_param_.bytes_a = ba_a;
m->init_param_.bytes_b = ba_b;
m->init_param_.workingdir = workingdir;
setOriginalLines_(ba_a);
if (setupPreviewWidget() == FileViewType::Text) {
TextDiffLineList left_lines;
TextDiffLineList right_lines;
makeSideBySideDiffData(m->init_param_.diff, m->original_lines, &left_lines, &right_lines);
setDiffText(m->init_param_.diff, left_lines, right_lines);
}
}
QString FileDiffWidget::diffObjects(GitPtr g, QString const &a_id, QString const &b_id)
{
if (m->text_codec) {
Git::Object obj_a = mainwindow()->cat_file_(g, a_id);
Git::Object obj_b = mainwindow()->cat_file_(g, b_id);
if (obj_b.type == Git::Object::Type::UNKNOWN) {
obj_b.type = Git::Object::Type::BLOB;
}
if (obj_a.type == Git::Object::Type::BLOB && obj_b.type == Git::Object::Type::BLOB) {
QString path_a = mainwindow()->newTempFilePath();
QString path_b = mainwindow()->newTempFilePath();
QFile file_a(path_a);
QFile file_b(path_b);
if (file_a.open(QFile::WriteOnly) && file_b.open(QFile::WriteOnly)) {
file_a.write(m->text_codec->toUnicode(obj_a.content).toUtf8());
file_b.write(m->text_codec->toUnicode(obj_b.content).toUtf8());
file_a.close();
file_b.close();
QString s = g->diff_file(path_a, path_b);
file_a.remove();
file_b.remove();
return s;
}
}
}
return GitDiff::diffObjects(g, a_id, b_id);
}
bool FileDiffWidget::isValidID_(QString const &id)
{
if (id.startsWith(PATH_PREFIX)) {
return true;
}
return Git::isValidID(id);
}
void FileDiffWidget::updateDiffView(Git::Diff const &info, bool uncommited)
{
GitPtr g = git();
if (!g) return;
if (!g->isValidWorkingCopy()) return;
if (isValidID_(info.blob.a_id) && isValidID_(info.blob.b_id)) {
Git::Object obj_a = cat_file(g, info.blob.a_id);
Git::Object obj_b = cat_file(g, info.blob.b_id);
QString mime_a = mainwindow()->determinFileType(obj_a.content, true);
QString mime_b = mainwindow()->determinFileType(obj_b.content, true);
if (misc::isImage(mime_a) && misc::isImage(mime_b)) {
setSideBySide_(obj_a.content, obj_b.content, g->workingRepositoryDir());
return;
}
}
{
Git::Diff diff;
if (isValidID_(info.blob.a_id) && isValidID_(info.blob.b_id)) {
std::string text = diffObjects(g, info.blob.a_id, info.blob.b_id).toStdString();
GitDiff::parseDiff(text, &info, &diff);
} else {
diff = info;
}
Git::Object obj;
if (isValidID_(diff.blob.a_id)) { // 左が有効
obj = cat_file(g, diff.blob.a_id);
if (isValidID_(diff.blob.b_id)) { // 右が有効
setSideBySide(obj.content, diff, uncommited, g->workingRepositoryDir()); // 通常のdiff表示
} else {
setLeftOnly(obj.content, diff); // 右が無効の時は、削除されたファイル
}
} else if (isValidID_(diff.blob.b_id)) { // 左が無効で右が有効の時は、追加されたファイル
obj = cat_file(g, diff.blob.b_id);
setRightOnly(obj.content, diff);
}
}
}
void FileDiffWidget::updateDiffView(QString id_left, QString id_right, QString const &path)
{
GitPtr g = git();
if (!g) return;
if (!g->isValidWorkingCopy()) return;
Git::Diff diff;
diff.path = path;
diff.blob.a_id = id_left;
diff.blob.b_id = id_right;
diff.mode = "0";
std::string text = diffObjects(g, diff.blob.a_id, diff.blob.b_id).toStdString();
GitDiff::parseDiff(text, &diff, &diff);
Git::Object obj = cat_file(g, diff.blob.a_id);
setSideBySide(obj.content, diff, false, g->workingRepositoryDir());
ui->widget_diff_slider->clear(false);
resetScrollBarValue();
updateControls();
ui->widget_diff_slider->update();
}
void FileDiffWidget::resizeEvent(QResizeEvent *)
{
refrectScrollBar();
}
void FileDiffWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Escape) {
event->ignore(); // Escが押されたとき、親ウィジェットに処理させる。(通常、ウィンドウを閉じる、など)
return;
}
if (focusWidget() == ui->widget_diff_left->texteditor()) {
ui->widget_diff_left->write(event);
} else if (focusWidget() == ui->widget_diff_right->texteditor()) {
ui->widget_diff_right->write(event);
}
}
void FileDiffWidget::scrollTo(int value)
{
ui->verticalScrollBar->setValue(value);
}
void FileDiffWidget::onVerticalScrollValueChanged(int)
{
refrectScrollBar();
}
void FileDiffWidget::onHorizontalScrollValueChanged(int)
{
refrectScrollBar();
}
void FileDiffWidget::onDiffWidgetWheelScroll(int lines)
{
while (lines > 0) {
ui->verticalScrollBar->triggerAction(QScrollBar::SliderSingleStepAdd);
lines--;
}
while (lines < 0) {
ui->verticalScrollBar->triggerAction(QScrollBar::SliderSingleStepSub);
lines++;
}
}
void FileDiffWidget::onScrollValueChanged2(int value)
{
ui->verticalScrollBar->setValue(value);
}
void FileDiffWidget::onDiffWidgetResized()
{
updateControls();
}
void FileDiffWidget::on_toolButton_fullscreen_clicked()
{
BigDiffWindow win(mainwindow());
win.setWindowState(Qt::WindowMaximized);
win.init(mainwindow(), m->init_param_);
win.exec();
}
void FileDiffWidget::setFocusAcceptable(bool f)
{
Qt::FocusPolicy focuspolicy = f ? Qt::StrongFocus : Qt::NoFocus;
ui->widget_diff_left->setFocusPolicy(focuspolicy);
ui->widget_diff_right->setFocusPolicy(focuspolicy);
}
void FileDiffWidget::onUpdateSliderBar()
{
int total = m->engine_left->document.lines.size();
int value = ui->verticalScrollBar->value();
int page = ui->verticalScrollBar->pageStep();
ui->widget_diff_slider->setScrollPos(total, value, page);
}
void FileDiffWidget::refrectScrollBar()
{
ui->widget_diff_left->refrectScrollBar();
ui->widget_diff_right->refrectScrollBar();
onUpdateSliderBar();
}
QPixmap FileDiffWidget::makeDiffPixmap(DiffPane pane, int width, int height)
{
auto Do = [&](TextDiffLineList const &lines){
return FileDiffSliderWidget::makeDiffPixmap(width, height, lines, global->theme);
};
if (pane == DiffPane::Left) return Do(m->left_lines);
if (pane == DiffPane::Right) return Do(m->right_lines);
return QPixmap();
}
void FileDiffWidget::onMoved(int cur_row, int cur_col, int scr_row, int scr_col)
{
(void)cur_col;
(void)cur_row;
ui->widget_diff_left->move(-1, -1, scr_row, scr_col, false);
ui->widget_diff_right->move(-1, -1, scr_row, scr_col, false);
refrectScrollBar();
onUpdateSliderBar();
}
void FileDiffWidget::setTextCodec(QTextCodec *codec)
{
m->text_codec = codec;
ui->widget_diff_left->setTextCodec(codec);
ui->widget_diff_right->setTextCodec(codec);
emit textcodecChanged();
}
void FileDiffWidget::setTextCodec(char const *name)
{
QTextCodec *codec = name ? QTextCodec::codecForName(name) : nullptr;
setTextCodec(codec);
}
void FileDiffWidget::on_toolButton_menu_clicked()
{
QMenu menu;
QAction *a_utf8 = menu.addAction("UTF-8");
QAction *a_sjis = menu.addAction("SJIS (CP932)");
QAction *a_eucjp = menu.addAction("EUC-JP");
QAction *a_iso2022jp = menu.addAction("JIS (ISO-2022-JP)");
QAction *a = menu.exec(QCursor::pos() + QPoint(8, -8));
if (a) {
if (a == a_utf8) {
setTextCodec((char const *)nullptr);
return;
}
if (a == a_sjis) {
setTextCodec("Shift_JIS");
return;
}
if (a == a_eucjp) {
setTextCodec("EUC-JP");
return;
}
if (a == a_iso2022jp) {
setTextCodec("ISO-2022-JP");
return;
}
}
}
diff --git a/src/FileDiffWidget.h b/src/FileDiffWidget.h
index affe692..6695ab9 100644
--- a/src/FileDiffWidget.h
+++ b/src/FileDiffWidget.h
@@ -1,167 +1,170 @@
#ifndef FILEDIFFWIDGET_H
#define FILEDIFFWIDGET_H
-#include "texteditor/AbstractCharacterBasedApplication.h"
-#include <QDialog>
+
+
+
+#include "FileDiffSliderWidget.h"
+#include "FileViewWidget.h"
#include "Git.h"
#include "MainWindow.h"
-#include "FileViewWidget.h"
-#include "FileDiffSliderWidget.h"
+#include "texteditor/AbstractCharacterBasedApplication.h"
+#include <QDialog>
+#include <memory>
namespace Ui {
class FileDiffWidget;
}
enum class ViewType {
None,
Left,
Right
};
-typedef Document::Line TextDiffLine;
-typedef QList<Document::Line> TextDiffLineList;
+using TextDiffLine = Document::Line;
+using TextDiffLineList = QList<Document::Line>;
struct ObjectContent {
QString id;
QString path;
QByteArray bytes;
TextDiffLineList lines;
};
-typedef std::shared_ptr<ObjectContent> ObjectContentPtr;
+using ObjectContentPtr = std::shared_ptr<ObjectContent>;
class QTableWidgetItem;
-class FileDiffWidget : public QWidget
-{
+class FileDiffWidget : public QWidget {
Q_OBJECT
friend class BigDiffWindow;
public:
struct DiffData {
ObjectContentPtr left;
ObjectContentPtr right;
std::vector<std::string> original_lines;
DiffData()
{
clear();
}
void clear()
{
- left = ObjectContentPtr(new ObjectContent());
- right = ObjectContentPtr(new ObjectContent());
+ left = std::make_shared<ObjectContent>();
+ right = std::make_shared<ObjectContent>();
original_lines.clear();
}
};
struct DrawData {
int v_scroll_pos = 0;
int h_scroll_pos = 0;
int char_width = 0;
int line_height = 0;
QColor bgcolor_text;
QColor bgcolor_add;
QColor bgcolor_del;
QColor bgcolor_add_dark;
QColor bgcolor_del_dark;
QColor bgcolor_gray;
QWidget *forcus = nullptr;
DrawData();
};
enum ViewStyle {
None,
SingleFile,
LeftOnly,
RightOnly,
SideBySideText,
SideBySideImage,
};
private:
Ui::FileDiffWidget *ui;
struct Private;
Private *m;
struct InitParam_ {
ViewStyle view_style = ViewStyle::None;
QByteArray bytes_a;
QByteArray bytes_b;
Git::Diff diff;
bool uncommited = false;
QString workingdir;
};
ViewStyle viewstyle() const;
GitPtr git();
Git::Object cat_file(GitPtr g, QString const &id);
int totalTextLines() const;
void resetScrollBarValue();
void updateSliderCursor();
int fileviewHeight() const;
void setDiffText(const Git::Diff &diff, const TextDiffLineList &left, const TextDiffLineList &right);
void setLeftOnly(QByteArray const &ba, const Git::Diff &diff);
void setRightOnly(QByteArray const &ba, const Git::Diff &diff);
void setSideBySide(QByteArray const &ba, const Git::Diff &diff, bool uncommited, QString const &workingdir);
void setSideBySide_(QByteArray const &ba_a, QByteArray const &ba_b, QString const &workingdir);
bool isValidID_(QString const &id);
FileViewType setupPreviewWidget();
void makeSideBySideDiffData(const Git::Diff &diff, const std::vector<std::string> &original_lines, TextDiffLineList *left_lines, TextDiffLineList *right_lines);
void onUpdateSliderBar();
void refrectScrollBar();
void setOriginalLines_(QByteArray const &ba);
QString diffObjects(GitPtr g, QString const &a_id, QString const &b_id);
- MainWindow *mainwindow();
+ BasicMainWindow *mainwindow();
protected:
- void resizeEvent(QResizeEvent *);
- void keyPressEvent(QKeyEvent *event);
+ void resizeEvent(QResizeEvent *) override;
+ void keyPressEvent(QKeyEvent *event) override;
public:
- explicit FileDiffWidget(QWidget *parent = 0);
- ~FileDiffWidget();
+ explicit FileDiffWidget(QWidget *parent = nullptr);
+ ~FileDiffWidget() override;
- void bind(MainWindow *mw);
+ void bind(BasicMainWindow *mw);
void clearDiffView();
void setSingleFile(QByteArray const &ba, QString const &id, QString const &path);
void updateControls();
void scrollToBottom();
void updateDiffView(const Git::Diff &info, bool uncommited);
void updateDiffView(QString id_left, QString id_right, QString const &path = QString());
void setMaximizeButtonEnabled(bool f);
void setFocusAcceptable(bool f);
QPixmap makeDiffPixmap(DiffPane pane, int width, int height);
void setViewType(FileViewType type);
void setTextCodec(QTextCodec *codec);
void setTextCodec(char const *name);
private slots:
void onVerticalScrollValueChanged(int);
void onHorizontalScrollValueChanged(int);
void onDiffWidgetWheelScroll(int lines);
void onScrollValueChanged2(int value);
void onDiffWidgetResized();
void on_toolButton_fullscreen_clicked();
void scrollTo(int value);
void onMoved(int cur_row, int cur_col, int scr_row, int scr_col);
void on_toolButton_menu_clicked();
signals:
void moveNextItem();
void movePreviousItem();
void textcodecChanged();
};
#endif // FILEDIFFWIDGET_H
diff --git a/src/FileHistoryWindow.cpp b/src/FileHistoryWindow.cpp
index fe9b804..db97bce 100644
--- a/src/FileHistoryWindow.cpp
+++ b/src/FileHistoryWindow.cpp
@@ -1,252 +1,252 @@
#include "FileHistoryWindow.h"
-#include "MainWindow.h"
+#include "BasicMainWindow.h"
#include "ui_FileHistoryWindow.h"
#include "common/misc.h"
#include "GitDiff.h"
#include "common/joinpath.h"
#include "FileDiffWidget.h"
#include "MyTableWidgetDelegate.h"
#include <QMenu>
#include <QPainter>
#include <QStyledItemDelegate>
#include <QThread>
struct FileHistoryWindow::Private {
GitPtr g;
QString path;
Git::CommitItemList commit_item_list;
FileDiffWidget::DiffData diff_data;
FileDiffWidget::DrawData draw_data;
};
FileDiffWidget::DiffData *FileHistoryWindow::diffdata()
{
return &m->diff_data;
}
const FileDiffWidget::DiffData *FileHistoryWindow::diffdata() const
{
return &m->diff_data;
}
FileDiffWidget::DrawData *FileHistoryWindow::drawdata()
{
return &m->draw_data;
}
const FileDiffWidget::DrawData *FileHistoryWindow::drawdata() const
{
return &m->draw_data;
}
int FileHistoryWindow::totalTextLines() const
{
return diffdata()->left->lines.size();
}
int FileHistoryWindow::fileviewScrollPos() const
{
return drawdata()->v_scroll_pos;
}
-FileHistoryWindow::FileHistoryWindow(MainWindow *parent)
+FileHistoryWindow::FileHistoryWindow(BasicMainWindow *parent)
: QDialog(parent)
, ui(new Ui::FileHistoryWindow)
, m(new Private)
{
ui->setupUi(this);
ui->tableWidget_log->setItemDelegate(new MyTableWidgetDelegate(this));
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
flags |= Qt::WindowMaximizeButtonHint;
setWindowFlags(flags);
ui->splitter->setSizes({100, 200});
ui->widget_diff_view->bind(mainwindow());
connect(ui->widget_diff_view, SIGNAL(moveNextItem()), this, SLOT(onMoveNextItem()));
connect(ui->widget_diff_view, SIGNAL(movePreviousItem()), this, SLOT(onMovePreviousItem()));
}
FileHistoryWindow::~FileHistoryWindow()
{
delete m;
delete ui;
}
-MainWindow *FileHistoryWindow::mainwindow()
+BasicMainWindow *FileHistoryWindow::mainwindow()
{
- return qobject_cast<MainWindow *>(parent());
+ return qobject_cast<BasicMainWindow *>(parent());
}
void FileHistoryWindow::prepare(GitPtr g, QString const &path)
{
Q_ASSERT(g);
Q_ASSERT(g->isValidWorkingCopy());
this->m->g = g;
this->m->path = path;
QString reponame = mainwindow()->currentRepositoryName();
QString brname = mainwindow()->currentBranch().name;
QString text = "%1 (%2)";
text = text.arg(reponame).arg(brname);
ui->label_repo->setText(text);
ui->label_path->setText(path);
{
OverrideWaitCursor;
m->commit_item_list = m->g->log_all(m->path, mainwindow()->limitLogCount());
}
collectFileHistory();
updateDiffView();
}
void FileHistoryWindow::collectFileHistory()
{
QStringList cols = {
tr("Commit"),
tr("Date"),
tr("Author"),
tr("Description"),
};
int n = cols.size();
ui->tableWidget_log->setColumnCount(n);
ui->tableWidget_log->setRowCount(0);
for (int i = 0; i < n; i++) {
QString const &text = cols[i];
QTableWidgetItem *item = new QTableWidgetItem(text);
ui->tableWidget_log->setHorizontalHeaderItem(i, item);
}
int count = m->commit_item_list.size();
ui->tableWidget_log->setRowCount(count);
for (int row = 0; row < count; row++) {
Git::CommitItem const &commit = m->commit_item_list[row];
int col = 0;
auto AddColumn = [&](QString const &text, QString const &tooltip){
QTableWidgetItem *item = new QTableWidgetItem(text);
item->setToolTip(tooltip);
ui->tableWidget_log->setItem(row, col, item);
col++;
};
QString commit_id = mainwindow()->abbrevCommitID(commit);
QString datetime = misc::makeDateTimeString(commit.commit_date);
AddColumn(commit_id, QString());
AddColumn(datetime, QString());
AddColumn(commit.author, QString());
AddColumn(commit.message, commit.message);
ui->tableWidget_log->setRowHeight(row, 24);
}
ui->tableWidget_log->resizeColumnsToContents();
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(false);
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(true);
ui->tableWidget_log->setFocus();
ui->tableWidget_log->setCurrentCell(0, 0);
}
class FindFileIdThread : public QThread {
private:
- MainWindow *mainwindow;
+ BasicMainWindow *mainwindow;
GitPtr g;
QString commit_id;
QString file;
public:
QString result;
- FindFileIdThread(MainWindow *mainwindow, GitPtr g, QString const &commit_id, QString const &file)
+ FindFileIdThread(BasicMainWindow *BasicMainWindow, GitPtr g, QString const &commit_id, QString const &file)
{
- this->mainwindow = mainwindow;
+ this->mainwindow = BasicMainWindow;
this->g = g;
this->commit_id = commit_id;
this->file = file;
}
protected:
void run() override
{
result = mainwindow->findFileID(g, commit_id, file);
}
};
void FileHistoryWindow::updateDiffView()
{
Q_ASSERT(m->g);
Q_ASSERT(m->g->isValidWorkingCopy());
ui->widget_diff_view->clearDiffView();
int row = ui->tableWidget_log->currentRow();
if (row >= 0 && row + 1 < (int)m->commit_item_list.size()) {
Git::CommitItem const &commit_left = m->commit_item_list[row + 1]; // older
Git::CommitItem const &commit_right = m->commit_item_list[row]; // newer
FindFileIdThread left_thread(mainwindow(), m->g->dup(), commit_left.commit_id, m->path);
FindFileIdThread right_thread(mainwindow(), m->g->dup(), commit_right.commit_id, m->path);
left_thread.start();
right_thread.start();
left_thread.wait();
right_thread.wait();
QString id_left = left_thread.result;
QString id_right = right_thread.result;
ui->widget_diff_view->updateDiffView(id_left, id_right, m->path);
} else if (row >= 0 && row < (int)m->commit_item_list.size()) {
Git::CommitItem const &commit = m->commit_item_list[row]; // newer
QString id = mainwindow()->findFileID(m->g, commit.commit_id, m->path);
Git::Diff diff(id, m->path, QString());
ui->widget_diff_view->updateDiffView(diff, false);
}
}
void FileHistoryWindow::on_tableWidget_log_currentItemChanged(QTableWidgetItem * /*current*/, QTableWidgetItem * /*previous*/)
{
updateDiffView();
}
void FileHistoryWindow::onMoveNextItem()
{
int row = ui->tableWidget_log->currentRow();
int count = ui->tableWidget_log->rowCount();
if (row + 1 < count) {
ui->tableWidget_log->setCurrentCell(row + 1, 0, QItemSelectionModel::ClearAndSelect);
}
}
void FileHistoryWindow::onMovePreviousItem()
{
int row = ui->tableWidget_log->currentRow();
if (row > 0) {
ui->tableWidget_log->setCurrentCell(row - 1, 0, QItemSelectionModel::ClearAndSelect);
}
}
void FileHistoryWindow::on_tableWidget_log_customContextMenuRequested(const QPoint &pos)
{
(void)pos;
Git::CommitItem const *commit = nullptr;
int row = ui->tableWidget_log->currentRow();
if (row >= 0 && row < (int)m->commit_item_list.size()) {
commit = &m->commit_item_list[row];
}
if (!commit) return;
QMenu menu;
QAction *a_property = mainwindow()->addMenuActionProperty(&menu);
QAction *a = menu.exec(QCursor::pos() + QPoint(8, -8));
if (a) {
if (a == a_property) {
mainwindow()->execCommitPropertyDialog(this, commit);
return;
}
}
}
diff --git a/src/FileHistoryWindow.h b/src/FileHistoryWindow.h
index 5364ac1..2d0a268 100644
--- a/src/FileHistoryWindow.h
+++ b/src/FileHistoryWindow.h
@@ -1,49 +1,48 @@
#ifndef FILEHISTORYWINDOW_H
#define FILEHISTORYWINDOW_H
#include <QDialog>
#include "Git.h"
-#include "MainWindow.h"
+#include "BasicMainWindow.h"
#include "FileDiffWidget.h"
namespace Ui {
class FileHistoryWindow;
}
-class MainWindow;
+class BasicMainWindow;
class QTableWidgetItem;
-class FileHistoryWindow : public QDialog
-{
+class FileHistoryWindow : public QDialog {
Q_OBJECT
private:
struct Private;
Private *m;
FileDiffWidget::DiffData *diffdata();
FileDiffWidget::DiffData const *diffdata() const;
FileDiffWidget::DrawData *drawdata();
FileDiffWidget::DrawData const *drawdata() const;
int totalTextLines() const;
int fileviewScrollPos() const;
public:
- explicit FileHistoryWindow(MainWindow *parent);
- ~FileHistoryWindow();
+ explicit FileHistoryWindow(BasicMainWindow *parent);
+ ~FileHistoryWindow() override;
void prepare(GitPtr g, QString const &path);
private slots:
void on_tableWidget_log_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
void onMoveNextItem();
void onMovePreviousItem();
void on_tableWidget_log_customContextMenuRequested(const QPoint &pos);
private:
Ui::FileHistoryWindow *ui;
void collectFileHistory();
void updateDiffView();
- MainWindow *mainwindow();
+ BasicMainWindow *mainwindow();
};
#endif // FILEHISTORYWINDOW_H
diff --git a/src/FilePropertyDialog.cpp b/src/FilePropertyDialog.cpp
index 17b513d..cd9cf07 100644
--- a/src/FilePropertyDialog.cpp
+++ b/src/FilePropertyDialog.cpp
@@ -1,30 +1,30 @@
#include "FilePropertyDialog.h"
#include "MainWindow.h"
#include "ui_FilePropertyDialog.h"
#include "main.h"
FilePropertyDialog::FilePropertyDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::FilePropertyDialog)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
}
FilePropertyDialog::~FilePropertyDialog()
{
delete ui;
}
-void FilePropertyDialog::exec(MainWindow *mw, QString const &path, QString const &id)
+void FilePropertyDialog::exec(BasicMainWindow *mw, QString const &path, QString const &id)
{
mainwindow = mw;
ui->lineEdit_repo->setText(mainwindow->currentRepositoryName());
ui->lineEdit_path->setText(path);
ui->lineEdit_id->setText(id);
QDialog::exec();
}
diff --git a/src/FilePropertyDialog.h b/src/FilePropertyDialog.h
index fa836a4..11a71b6 100644
--- a/src/FilePropertyDialog.h
+++ b/src/FilePropertyDialog.h
@@ -1,26 +1,25 @@
#ifndef FILEPROPERTYDIALOG_H
#define FILEPROPERTYDIALOG_H
#include <QDialog>
namespace Ui {
class FilePropertyDialog;
}
-class MainWindow;
+class BasicMainWindow;
-class FilePropertyDialog : public QDialog
-{
+class FilePropertyDialog : public QDialog {
Q_OBJECT
private:
- MainWindow *mainwindow;
+ BasicMainWindow *mainwindow;
public:
- explicit FilePropertyDialog(QWidget *parent = 0);
- ~FilePropertyDialog();
+ explicit FilePropertyDialog(QWidget *parent = nullptr);
+ ~FilePropertyDialog() override;
- void exec(MainWindow *mw, QString const &path, QString const &id);
+ void exec(BasicMainWindow *mw, QString const &path, QString const &id);
private:
Ui::FilePropertyDialog *ui;
};
#endif // FILEPROPERTYDIALOG_H
diff --git a/src/FileViewWidget.cpp b/src/FileViewWidget.cpp
index 7b22e71..ae199f8 100644
--- a/src/FileViewWidget.cpp
+++ b/src/FileViewWidget.cpp
@@ -1,171 +1,171 @@
#include "FileViewWidget.h"
#include <QMenu>
#include <QPainter>
#include <QStackedWidget>
#include <QVBoxLayout>
#include "common/misc.h"
FileViewWidget::FileViewWidget(QWidget *parent)
: QWidget(parent)
{
setObjectName(QStringLiteral("FileViewWidget"));
ui_verticalLayout = new QVBoxLayout(this);
ui_verticalLayout->setSpacing(0);
ui_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
ui_verticalLayout->setContentsMargins(0, 0, 0, 0);
ui_stackedWidget = new QStackedWidget(this);
ui_stackedWidget->setObjectName(QStringLiteral("stackedWidget"));
ui_page_none = new QWidget();
ui_page_none->setObjectName(QStringLiteral("page_none"));
ui_stackedWidget->addWidget(ui_page_none);
ui_page_text = new X_TextEditorWidget();
ui_page_text->setObjectName(QStringLiteral("page_text"));
ui_page_text->setFocusPolicy(Qt::StrongFocus);
ui_stackedWidget->addWidget(ui_page_text);
ui_page_image = new X_ImageViewWidget();
ui_page_image->setObjectName(QStringLiteral("page_image"));
ui_page_image->setFocusPolicy(Qt::StrongFocus);
ui_stackedWidget->addWidget(ui_page_image);
ui_verticalLayout->addWidget(ui_stackedWidget);
setWindowTitle(QApplication::translate("FileViewWidget", "Form", Q_NULLPTR));
ui_stackedWidget->setCurrentIndex(1);
QMetaObject::connectSlotsByName(this);
ui_page_text->setRenderingMode(TextEditorWidget::DecoratedMode);
ui_page_text->setTheme(TextEditorTheme::Light());
ui_page_text->showHeader(false);
ui_page_text->showFooter(false);
ui_page_text->setAutoLayout(true);
ui_page_text->setReadOnly(true);
ui_page_text->setToggleSelectionAnchorEnabled(false);
ui_page_text->setFocusFrameVisible(true);
ui_stackedWidget->setCurrentWidget(ui_page_none);
}
void FileViewWidget::setTextCodec(QTextCodec *codec)
{
ui_page_text->setTextCodec(codec);
}
-void FileViewWidget::bind(MainWindow *mw, FileDiffWidget *fdw, QScrollBar *vsb, QScrollBar *hsb, TextEditorThemePtr theme)
+void FileViewWidget::bind(QMainWindow *mw, FileDiffWidget *fdw, QScrollBar *vsb, QScrollBar *hsb, TextEditorThemePtr theme)
{
ui_page_text->bindScrollBar(vsb, hsb);
ui_page_image->bind(mw, fdw, vsb, hsb);
ui_page_text->setTheme(theme);
}
void FileViewWidget::setViewType(FileViewType type)
{
view_type = type;
switch (view_type) {
case FileViewType::Text:
ui_stackedWidget->setCurrentWidget(ui_page_text);
return;
case FileViewType::Image:
ui_stackedWidget->setCurrentWidget(ui_page_image);
return;
default:
ui_stackedWidget->setCurrentWidget(ui_page_none);
return;
}
}
const TextEditorTheme *FileViewWidget::theme() const
{
return ui_page_text->theme();
}
int FileViewWidget::latin1Width() const
{
return ui_page_text->latin1Width();
}
int FileViewWidget::lineHeight() const
{
return ui_page_text->lineHeight();
}
void FileViewWidget::setDiffMode(TextEditorEnginePtr editor_engine, QScrollBar *vsb, QScrollBar *hsb)
{
ui_page_text->setTextEditorEngine(editor_engine);
return ui_page_text->bindScrollBar(vsb, hsb);
}
void FileViewWidget::refrectScrollBar()
{
switch (view_type) {
case FileViewType::Text:
ui_page_text->refrectScrollBar();
return;
case FileViewType::Image:
ui_page_image->refrectScrollBar();
return;
}
}
void FileViewWidget::move(int cur_row, int cur_col, int scr_row, int scr_col, bool auto_scroll)
{
return ui_page_text->move(cur_row, cur_col, scr_row, scr_col, auto_scroll);
}
void FileViewWidget::setImage(QString mimetype, QByteArray const &ba, QString const &object_id, QString const &path)
{
setViewType(FileViewType::Image);
this->source_id = object_id;
#ifdef APP_GUITAR
ui_page_image->setImage(mimetype, ba, object_id, path);
#else
ui_page_image->setImage(mimetype, ba);
#endif
}
-void FileViewWidget::setText(const QList<Document::Line> *source, MainWindow *mw, QString const &object_id, QString const &object_path)
+void FileViewWidget::setText(const QList<Document::Line> *source, QMainWindow *mw, QString const &object_id, QString const &object_path)
{
setViewType(FileViewType::Text);
this->source_id = object_id;
#ifdef APP_GUITAR
- ui_page_text->setDocument(source, mw, object_id, object_path);
+ ui_page_text->setDocument(source, qobject_cast<BasicMainWindow *>(mw), object_id, object_path);
scrollToTop();
texteditor()->moveCursorOut(); // 現在行を -1 にして、カーソルを非表示にする。
#else
ui_page_text->setDocument(source);
scrollToTop();
#endif
}
-void FileViewWidget::setText(QByteArray const &ba, MainWindow *mw, QString const &object_id, QString const &object_path)
+void FileViewWidget::setText(QByteArray const &ba, QMainWindow *mw, QString const &object_id, QString const &object_path)
{
std::vector<std::string> lines;
char const *begin = ba.data();
char const *end = begin + ba.size();
misc::splitLines(begin, end, &lines, true);
QList<Document::Line> source;
source.reserve(lines.size());
int num = 0;
for (std::string const &line : lines) {
Document::Line t(line);
t.line_number = ++num;
source.push_back(t);
}
setText(&source, mw, object_id, object_path);
}
void FileViewWidget::scrollToTop()
{
ui_page_text->scrollToTop();
}
void FileViewWidget::write(QKeyEvent *e)
{
ui_page_text->write(e);
}
TextEditorWidget *FileViewWidget::texteditor()
{
return ui_page_text;
}
diff --git a/src/FileViewWidget.h b/src/FileViewWidget.h
index 2655264..ccf7ebb 100644
--- a/src/FileViewWidget.h
+++ b/src/FileViewWidget.h
@@ -1,77 +1,77 @@
#ifndef FILEVIEWWIDGET_H
#define FILEVIEWWIDGET_H
#include <QWidget>
#include "texteditor/TextEditorWidget.h"
class QScrollBar;
struct PreEditText;
-class MainWindow;
+class BasicMainWindow;
class FileDiffWidget;
class QVBoxLayout;
class QStackedWidget;
enum class FileViewType {
None,
Text,
Image,
};
#ifdef APP_GUITAR
#include "MyTextEditorWidget.h"
#include "MyImageViewWidget.h"
#else
#include "ImageViewWidget.h"
#endif
class FileViewWidget : public QWidget {
private:
#ifdef APP_GUITAR
using X_TextEditorWidget = MyTextEditorWidget;
using X_ImageViewWidget = MyImageViewWidget;
#else
using X_TextEditorWidget = TextEditorWidget;
using X_ImageViewWidget = ImageViewWidget;
#endif
QVBoxLayout *ui_verticalLayout;
QStackedWidget *ui_stackedWidget;
QWidget *ui_page_none;
X_TextEditorWidget *ui_page_text;
X_ImageViewWidget *ui_page_image;
QString source_id;
FileViewType view_type = FileViewType::None;
public:
explicit FileViewWidget(QWidget *parent = 0);
~FileViewWidget()
{
}
void setTextCodec(QTextCodec *codec);
void setViewType(FileViewType type);
void setImage(QString mimetype, const QByteArray &ba, QString const &object_id, const QString &path);
- void setText(const QList<Document::Line> *source, MainWindow *mw, QString const &object_id, const QString &object_path);
- void setText(const QByteArray &ba, MainWindow *mw, const QString &object_id, const QString &object_path);
+ void setText(const QList<Document::Line> *source, QMainWindow *mw, QString const &object_id, const QString &object_path);
+ void setText(const QByteArray &ba, QMainWindow *mw, const QString &object_id, const QString &object_path);
void setDiffMode(TextEditorEnginePtr editor_engine, QScrollBar *vsb, QScrollBar *hsb);
int latin1Width() const;
int lineHeight() const;
TextEditorTheme const *theme() const;
void scrollToTop();
void write(QKeyEvent *e);
void refrectScrollBar();
void move(int cur_row, int cur_col, int scr_row, int scr_col, bool auto_scroll);
TextEditorWidget *texteditor();
- void bind(MainWindow *mw, FileDiffWidget *fdw, QScrollBar *vsb, QScrollBar *hsb, TextEditorThemePtr theme);
+ void bind(QMainWindow *mw, FileDiffWidget *fdw, QScrollBar *vsb, QScrollBar *hsb, TextEditorThemePtr theme);
};
#endif // FILEVIEWWIDGET_H
diff --git a/src/Git.h b/src/Git.h
index df8d557..b3d17f7 100644
--- a/src/Git.h
+++ b/src/Git.h
@@ -1,485 +1,479 @@
#ifndef GIT_H
#define GIT_H
#include "AbstractProcess.h"
#include <QDateTime>
#include <QObject>
#include <functional>
#include <QDebug>
#include <QMutex>
#include <memory>
#define SINGLE_THREAD 0
#define GIT_ID_LENGTH (40)
class Win32PtyProcess;
enum class LineSide {
Left,
Right,
};
struct TreeLine {
int index;
int depth;
int color_number = 0;
bool bend_early = false;
TreeLine(int index = -1, int depth = -1)
: index(index)
, depth(depth)
{
}
};
struct NamedCommitItem {
enum class Type {
None,
Branch,
Tag,
};
Type type = Type::None;
QString remote;
QString name;
QString id;
};
-typedef QList<NamedCommitItem> NamedCommitList;
-
+using NamedCommitList = QList<NamedCommitItem>;
class Git;
-typedef std::shared_ptr<Git> GitPtr;
+using GitPtr = std::shared_ptr<Git>;
class Git : QObject {
public:
class Context {
public:
QString git_command;
};
struct Object {
enum class Type {
UNKNOWN = 0,
COMMIT = 1,
TREE = 2,
BLOB = 3,
TAG = 4,
UNDEFINED = 5,
OFS_DELTA = 6,
REF_DELTA = 7,
};
Type type = Type::UNKNOWN;
QByteArray content;
};
class Hunk {
public:
std::string at;
std::vector<std::string> lines;
};
class Diff {
public:
enum class Type {
Unknown,
Modify,
Copy,
Rename,
Create,
Delete,
ChType,
Unmerged,
};
Type type = Type::Unknown;
QString diff;
QString index;
QString path;
QString mode;
struct BLOB_AB_ {
QString a_id;
QString b_id;
} blob;
QList<Hunk> hunks;
- Diff()
- {
- }
+ Diff() = default;
Diff(QString const &id, QString const &path, QString const &mode)
{
makeForSingleFile(this, QString(GIT_ID_LENGTH, '0'), id, path, mode);
-
}
private:
void makeForSingleFile(Git::Diff *diff, QString const &id_a, QString const &id_b, QString const &path, QString const &mode);
};
enum class SignatureGrade {
NoSignature,
Unknown,
Good,
Dubious,
Missing,
Bad,
};
static SignatureGrade evaluateSignature(char s)
{
switch (s) {
case 'G':
return SignatureGrade::Good;
case 'U':
case 'X':
case 'Y':
return SignatureGrade::Dubious;
case 'B':
case 'R':
return SignatureGrade::Bad;
case 'E':
return SignatureGrade::Missing;
case 'N':
case ' ':
case 0:
return SignatureGrade::NoSignature;
}
return SignatureGrade::Unknown;
}
struct CommitItem {
QString commit_id;
QStringList parent_ids;
QString author;
QString email;
QString message;
QDateTime commit_date;
std::vector<TreeLine> parent_lines;
QByteArray fingerprint;
char signature = 0; // git log format:%G?
bool has_child = false;
int marker_depth = -1;
bool resolved = false;
};
- typedef std::vector<CommitItem> CommitItemList;
+ using CommitItemList = std::vector<CommitItem>;
static bool isUncommited(CommitItem const &item)
{
return item.commit_id.isEmpty();
}
struct Branch {
QString name;
QString id;
QString remote;
int ahead = 0;
int behind = 0;
enum {
None,
Current = 0x0001,
HeadDetachedAt = 0x0002,
HeadDetachedFrom = 0x0004,
};
int flags = 0;
operator bool () const
{
if (name.isEmpty()) return false;
if (id.isEmpty()) return false;
return true;
}
bool isCurrent() const
{
return flags & Current;
}
bool isHeadDetached() const
{
return flags & HeadDetachedAt;
}
};
struct Tag {
QString name;
QString id;
};
enum class FileStatusCode : unsigned int {
Unknown,
Ignored,
Untracked,
NotUpdated = 0x10000000,
Staged_ = 0x20000000,
UpdatedInIndex,
AddedToIndex,
DeletedFromIndex,
RenamedInIndex,
CopiedInIndex,
Unmerged_ = 0x40000000,
Unmerged_BothDeleted,
Unmerged_AddedByUs,
Unmerged_DeletedByThem,
Unmerged_AddedByThem,
Unmerged_DeletedByUs,
Unmerged_BothAdded,
Unmerged_BothModified,
Tracked_ = 0xf0000000
};
class FileStatus {
public:
struct Data {
char code_x = 0;
char code_y = 0;
FileStatusCode code = FileStatusCode::Unknown;
QString rawpath1;
QString rawpath2;
QString path1;
QString path2;
} data;
static FileStatusCode parseFileStatusCode(char x, char y);
bool isStaged() const
{
return (int)data.code & (int)FileStatusCode::Staged_;
}
bool isUnmerged() const
{
return (int)data.code & (int)FileStatusCode::Unmerged_;
}
bool isTracked() const
{
return (int)data.code & (int)FileStatusCode::Tracked_;
}
void parse(QString const &text);
- FileStatus()
- {
- }
+ FileStatus() = default;
FileStatus(QString const &text)
{
parse(text);
}
FileStatusCode code() const
{
return data.code;
}
int code_x() const
{
return data.code_x;
}
int code_y() const
{
return data.code_y;
}
bool isDeleted() const
{
return code_x() == 'D' || code_y() == 'D';
}
QString path1() const
{
return data.path1;
}
QString path2() const
{
return data.path2;
}
QString rawpath1() const
{
return data.rawpath1;
}
QString rawpath2() const
{
return data.rawpath2;
}
};
- typedef std::vector<FileStatus> FileStatusList;
+ using FileStatusList = std::vector<FileStatus>;
static QString trimPath(QString const &s);
private:
struct Private;
Private *m;
QStringList make_branch_list_();
QByteArray cat_file_(QString const &id);
FileStatusList status_();
bool commit_(QString const &msg, bool amend, bool sign, AbstractPtyProcess *pty);
bool push_(bool tags, AbstractPtyProcess *pty);
static void parseAheadBehind(QString const &s, Branch *b);
Git();
QString encodeQuotedText(QString const &str);
QStringList refs();
public:
Git(Context const &cx, QString const &repodir);
Git(Git &&r) = delete;
- virtual ~Git();
+ ~Git() override;
- typedef bool (*callback_t)(void *cookie, char const *ptr, int len);
+ using callback_t = bool (*)(void *, const char *, int);
void setLogCallback(callback_t func, void *cookie);
QByteArray toQByteArray() const;
void setGitCommand(QString const &path);
QString gitCommand() const;
void clearResult();
QString resultText() const;
bool chdirexec(std::function<bool ()> const &fn);
bool git(QString const &arg, bool chdir, bool errout = false, AbstractPtyProcess *pty = nullptr);
bool git(QString const &arg)
{
return git(arg, true);
}
void setWorkingRepositoryDir(QString const &repo);
QString const &workingRepositoryDir() const;
QString getCurrentBranchName();
bool isValidWorkingCopy() const;
QString version();
bool init();
QStringList getUntrackedFiles();
CommitItemList log_all(QString const &id, int maxcount);
CommitItemList log(int maxcount);
bool queryCommit(QString const &id, CommitItem *out);
struct CloneData {
QString url;
QString basedir;
QString subdir;
};
static CloneData preclone(QString const &url, QString const &path);
bool clone(CloneData const &data, AbstractPtyProcess *pty);
FileStatusList status();
bool cat_file(QString const &id, QByteArray *out);
void resetFile(QString const &path);
void resetAllFiles();
void removeFile(QString const &path);
void stage(QString const &path);
void stage(QStringList const &paths);
void unstage(QString const &path);
void unstage(QStringList const &paths);
- void pull(AbstractPtyProcess *pty = 0);
+ void pull(AbstractPtyProcess *pty = nullptr);
- void fetch(AbstractPtyProcess *pty = 0);
+ void fetch(AbstractPtyProcess *pty = nullptr);
QList<Branch> branches();
int getProcessExitCode() const;
QString diff(QString const &old_id, QString const &new_id);
QString diff_file(QString const &old_path, QString const &new_path);
struct DiffRaw {
struct AB {
QString id;
QString mode;
} a, b;
QString state;
QStringList files;
};
struct Remote {
QString name;
QString url;
QString purpose;
};
QList<DiffRaw> diff_raw(QString const &old_id, QString const &new_id);
static bool isValidID(QString const &s);
bool commit(QString const &text, bool sign, AbstractPtyProcess *pty);
bool commit_amend_m(QString const &text, bool sign, AbstractPtyProcess *pty);
bool revert(QString const &id);
- bool push(bool tags, AbstractPtyProcess *pty = 0);
+ bool push(bool tags, AbstractPtyProcess *pty = nullptr);
void getRemoteURLs(QList<Remote> *out);
void createBranch(QString const &name);
void checkoutBranch(QString const &name);
void mergeBranch(QString const &name);
void rebaseBranch(QString const &name);
static bool isValidWorkingCopy(QString const &dir);
QString diff_to_file(QString const &old_id, QString const &path);
QString errorMessage() const;
GitPtr dup() const;
QString rev_parse(QString const &name);
QList<Tag> tags();
bool tag(QString const &name, QString const &id = QString());
void delete_tag(QString const &name, bool remote);
void setRemoteURL(QString const &name, QString const &url);
void addRemoteURL(QString const &name, QString const &url);
void removeRemote(QString const &name);
QStringList getRemotes();
struct User {
QString name;
QString email;
};
enum class Source {
Default,
Global,
Local,
};
User getUser(Source purpose);
void setUser(User const&user, bool global);
bool reset_head1();
void push_u(QString const &remote, QString const &branch, AbstractPtyProcess *pty);
QString objectType(QString const &id);
bool rm_cached(QString const &file);
void cherrypick(QString const &name);
struct ReflogItem {
QString id;
QString head;
QString command;
QString comment;
struct File {
QString atts_a;
QString atts_b;
QString id_a;
QString id_b;
QString type;
QString path;
};
QList<File> files;
};
- typedef QList<ReflogItem> ReflogItemList;
+ using ReflogItemList = QList<ReflogItem>;
bool reflog(ReflogItemList *out, int maxcount = 100);
QByteArray blame(QString const &path);
enum SignPolicy {
Unset,
False,
True,
};
QString signingKey(Source purpose);
bool setSigningKey(QString const &id, bool global);
SignPolicy signPolicy(Source source);
bool setSignPolicy(Source source, SignPolicy policy);
bool configGpgProgram(QString const &path, bool global);
void rebaseOnto(QString const &newbase, QString const &upstream, QString const &branch, AbstractPtyProcess *pty);
struct RemoteInfo {
QString commit_id;
QString name;
};
QList<RemoteInfo> ls_remote();
};
void parseDiff(std::string const &s, Git::Diff const *info, Git::Diff *out);
#endif // GIT_H
diff --git a/src/GitDiff.cpp b/src/GitDiff.cpp
index cba9521..420adbd 100644
--- a/src/GitDiff.cpp
+++ b/src/GitDiff.cpp
@@ -1,458 +1,458 @@
#include "GitDiff.h"
#include <QDebug>
#include <QThread>
-#include "MainWindow.h"
+#include "BasicMainWindow.h"
bool parse_tree_(GitObjectCache *objcache, QString const &commit_id, QString const &path_prefix, GitTreeItemList *out)
{
out->clear();
if (!commit_id.isEmpty()) {
Git::Object obj = objcache->catFile(commit_id);
if (!obj.content.isEmpty()) { // 内容を取得
QString s = QString::fromUtf8(obj.content);
QStringList lines = misc::splitLines(s);
for (QString const &line : lines) {
int tab = line.indexOf('\t'); // タブより後ろにパスがある
if (tab > 0) {
QString stat = line.mid(0, tab); // タブの手前まで
QStringList vals = misc::splitWords(stat); // 空白で分割
if (vals.size() >= 3) {
GitTreeItem data;
data.mode = vals[0]; // ファイルモード
data.id = vals[2]; // id(ハッシュ値)
QString type = vals[1]; // 種類(tree/blob)
QString path = line.mid(tab + 1); // パス
path = Git::trimPath(path);
data.name = path_prefix.isEmpty() ? path : misc::joinWithSlash(path_prefix, path);
if (type == "tree") {
data.type = GitTreeItem::TREE;
out->push_back(data);
} else if (type == "blob") {
data.type = GitTreeItem::BLOB;
out->push_back(data);
}
}
}
}
return true;
}
}
return false;
}
// PathToIdMap
class GitDiff::LookupTable {
private:
public:
std::map<QString, QString> path_to_id_map;
std::map<QString, QString> id_to_path_map;
public:
typedef std::map<QString, QString>::const_iterator const_iterator;
void store(QString const &path, QString const &id)
{
path_to_id_map[path] = id;
id_to_path_map[id] = path;
}
void store(GitTreeItemList const &files)
{
for (GitTreeItem const &cd : files) {
store(cd.name, cd.id);
}
}
const_iterator find_path(QString const &path) const
{
return path_to_id_map.find(path);
}
const_iterator end_path() const
{
return path_to_id_map.end();
}
};
// GitDiff
GitPtr GitDiff::git()
{
return objcache->git()->dup();
}
QString GitDiff::makeKey(QString const &a_id, QString const &b_id)
{
return a_id + ".." + b_id;
}
QString GitDiff::makeKey(Git::Diff const &diff)
{
return makeKey(diff.blob.a_id, diff.blob.b_id);
}
QString GitDiff::prependPathPrefix(QString const &path)
{
return PATH_PREFIX + path;
}
QString GitDiff::diffObjects(GitPtr g, QString const &a_id, QString const &b_id)
{
QString path_prefix = PATH_PREFIX;
if (b_id.startsWith(path_prefix)) {
QString path = b_id.mid(path_prefix.size());
return g->diff_to_file(a_id, path);
} else {
return g->diff(a_id, b_id);
}
}
QString GitDiff::diffFiles(GitPtr g, QString const &a_path, QString const &b_path)
{
return g->diff_file(a_path, b_path);
}
void GitDiff::parseDiff(std::string const &s, Git::Diff const *info, Git::Diff *out)
{
std::vector<std::string> lines;
{
char const *begin = s.c_str();
char const *end = begin + s.size();
misc::splitLines(begin, end, &lines, false);
}
out->diff = QString("diff --git ") + ("a/" + info->path) + ' ' + ("b/" + info->path);
out->index = QString("index ") + info->blob.a_id + ".." + info->blob.b_id + ' ' + info->mode;
out->path = info->path;
out->blob = info->blob;
bool atat = false;
for (std::string const &line : lines) {
int c = line[0] & 0xff;
if (c == '@') {
if (strncmp(line.c_str(), "@@ ", 3) == 0) {
out->hunks.push_back(Git::Hunk());
out->hunks.back().at = line;
atat = true;
}
} else {
if (atat && c == '\\') { // e.g. \ No newline at end of file...
// ignore this line
} else {
if (atat) {
if (c == ' ' || c == '-' || c == '+') {
// nop
} else {
atat = false;
}
}
if (atat) {
if (!out->hunks.isEmpty()) {
out->hunks.back().lines.push_back(line);
}
}
}
}
}
}
void GitDiff::retrieveCompleteTree(QString const &dir, GitTreeItemList const *files, std::map<QString, GitTreeItem> *out)
{
for (GitTreeItem const &d : *files) {
QString path = misc::joinWithSlash(dir, d.name);
if (d.type == GitTreeItem::BLOB) {
(*out)[path] = d;
} else if (d.type == GitTreeItem::TREE) {
GitTreeItemList files2;
parse_tree_(objcache, d.id, QString(), &files2);
retrieveCompleteTree(path, &files2, out);
}
}
}
void GitDiff::retrieveCompleteTree(QString const &dir, GitTreeItemList const *files)
{
for (GitTreeItem const &d : *files) {
QString path = misc::joinWithSlash(dir, d.name);
if (d.type == GitTreeItem::BLOB) {
Git::Diff diff(d.id, path, d.mode);
diffs.push_back(diff);
} else if (d.type == GitTreeItem::TREE) {
GitTreeItemList files2;
parse_tree_(objcache, d.id, QString(), &files2);
retrieveCompleteTree(path, &files2);
}
}
}
-bool GitDiff::diff(QString id, QList<Git::Diff> *out)
+bool GitDiff::diff(QString const &id, QList<Git::Diff> *out)
{
out->clear();
diffs.clear();
try {
if (Git::isValidID(id)) { // 有効なID
{ // diff_raw
GitTreeItemList files;
GitCommit newer_commit;
newer_commit.parseCommit(objcache, id);
parse_tree_(objcache, newer_commit.tree_id, QString(), &files);
if (newer_commit.parents.isEmpty()) { // 親がないなら最古のコミット
retrieveCompleteTree(QString(), &files); // ツリー全体を取得
} else {
std::map<QString, Git::Diff> diffmap;
std::set<QString> deleted_set;
std::set<QString> renamed_set;
QList<Git::DiffRaw> list;
for (QString const &parent : newer_commit.parents) {
QList<Git::DiffRaw> l = git()->diff_raw(parent, id);
for (Git::DiffRaw const &item : l) {
if (item.state.startsWith('D')) {
deleted_set.insert(item.a.id);
}
list.push_back(item);
}
}
for (Git::DiffRaw const &item : list) {
if (item.state.startsWith('A') || item.state.startsWith('C')) { // 追加されたファイル
auto it = deleted_set.find(item.b.id); // 同じオブジェクトIDが削除リストに載っているなら
if (it != deleted_set.end()) {
renamed_set.insert(item.b.id); // 名前変更とみなす
}
} else if (item.state.startsWith('R')) { // 名前変更されたファイル
renamed_set.insert(item.b.id);
}
}
for (Git::DiffRaw const &item : list) {
QString file;
if (!item.files.isEmpty()) {
file = item.files.back(); // 名前変更された場合はリストの最後が新しい名前
}
Git::Diff diff;
diff.diff = QString("diff --git a/%1 b/%2").arg(file).arg(file);
diff.index = QString("index %1..%2 %3").arg(item.a.id).arg(item.b.id).arg(item.b.mode);
diff.path = file;
diff.mode = item.b.mode;
if (Git::isValidID(item.a.id)) diff.blob.a_id = item.a.id;
if (Git::isValidID(item.b.id)) diff.blob.b_id = item.b.id;
#if 0
if (!diff.blob.a_id.isEmpty()) {
if (!diff.blob.b_id.isEmpty()) {
if (renamed_set.find(diff.blob.b_id) != renamed_set.end()) {
diff.type = Git::Diff::Type::Rename;
} else {
diff.type = Git::Diff::Type::Modify;
}
} else {
if (renamed_set.find(diff.blob.a_id) != renamed_set.end()) { // 名前変更されたオブジェクトなら
diff.type = Git::Diff::Type::Unknown; // マップに追加しない
} else {
diff.type = Git::Diff::Type::Delete; // 削除されたオブジェクト
}
}
} else if (!diff.blob.b_id.isEmpty()) {
if (renamed_set.find(diff.blob.b_id) != renamed_set.end()) {
diff.type = Git::Diff::Type::Rename;
} else {
diff.type = Git::Diff::Type::Create;
}
}
#else
diff.type = Git::Diff::Type::Unknown;
int state = item.state.utf16()[0];
switch (state) {
case 'A': diff.type = Git::Diff::Type::Create; break;
case 'C': diff.type = Git::Diff::Type::Copy; break;
case 'D': diff.type = Git::Diff::Type::Delete; break;
case 'M': diff.type = Git::Diff::Type::Modify; break;
case 'R': diff.type = Git::Diff::Type::Rename; break;
case 'T': diff.type = Git::Diff::Type::ChType; break;
case 'U': diff.type = Git::Diff::Type::Unmerged; break;
}
#endif
if (diff.type != Git::Diff::Type::Unknown) {
if (diffmap.find(diff.path) == diffmap.end()) {
diffmap[diff.path] = diff;
}
}
}
for (auto const &pair : diffmap) {
diffs.push_back(pair.second);
}
}
}
} else { // 無効なIDなら、HEADと作業コピーのdiff
GitPtr g = objcache->git();
QString head_id = objcache->revParse("HEAD");
Git::FileStatusList stats = g->status(); // git status
GitCommitTree head_tree(objcache);
head_tree.parseCommit(head_id); // HEADが親
QString zeros(GIT_ID_LENGTH, '0');
for (Git::FileStatus const &fs : stats) {
QString path = fs.path1();
Git::Diff item;
GitTreeItem treeitem;
if (head_tree.lookup(path, &treeitem)) {
item.blob.a_id = treeitem.id; // HEADにおけるこのファイルのID
if (fs.isDeleted()) { // 削除されてる
item.blob.b_id = zeros; // 削除された
} else {
item.blob.b_id = prependPathPrefix(path); // IDの代わりに実在するファイルパスを入れる
}
item.mode = treeitem.mode;
} else {
item.blob.a_id = zeros;
item.blob.b_id = prependPathPrefix(path); // 実在するファイルパス
}
item.diff = QString("diff --git a/%1 b/%2").arg(path).arg(path);
item.index = QString("index %1..%2 %3").arg(item.blob.a_id).arg(zeros).arg(item.mode);
item.path = path;
diffs.push_back(item);
}
}
std::sort(diffs.begin(), diffs.end(), [](Git::Diff const &left, Git::Diff const &right){
return left.path.compare(right.path, Qt::CaseInsensitive) < 0;
});
*out = std::move(diffs);
return true;
} catch (Interrupted &) {
out->clear();
}
return false;
}
bool GitDiff::diff_uncommited(QList<Git::Diff> *out)
{
return diff(QString(), out);
}
// GitCommitTree
GitCommitTree::GitCommitTree(GitObjectCache *objcache)
: objcache(objcache)
{
}
GitPtr GitCommitTree::git()
{
return objcache->git();
}
QString GitCommitTree::lookup_(QString const &file, GitTreeItem *out)
{
int i = file.lastIndexOf('/');
if (i >= 0) {
QString subdir = file.mid(0, i);
QString name = file.mid(i + 1);
QString tree_id;
{
auto it = tree_id_map.find(subdir);
if (it != tree_id_map.end()) {
tree_id = it->second;
} else {
tree_id = lookup_(subdir, out);
}
}
GitTreeItemList list;
if (parse_tree_(objcache, tree_id, QString(), &list)) {
QString return_id;
for (GitTreeItem const &d : list) {
if (d.name == name) {
return_id = d.id;
}
QString path = misc::joinWithSlash(subdir, d.name);
if (d.type == GitTreeItem::BLOB) {
if (out && d.name == name) {
*out = d;
}
blob_map[path] = d;
} else if (d.type == GitTreeItem::TREE) {
tree_id_map[path] = d.id;
}
}
return return_id;
}
} else {
QString return_id;
for (GitTreeItem const &d : root_item_list) {
if (d.name == file) {
return_id = d.id;
}
if (d.type == GitTreeItem::BLOB) {
if (out && d.name == file) {
*out = d;
}
blob_map[d.name] = d;
} else if (d.type == GitTreeItem::TREE) {
tree_id_map[d.name] = d.id;
}
}
return return_id;
}
return QString();
}
QString GitCommitTree::lookup(QString const &file)
{
auto it = blob_map.find(file);
if (it != blob_map.end()) {
return it->second.id;
}
return lookup_(file, nullptr);
}
bool GitCommitTree::lookup(QString const &file, GitTreeItem *out)
{
*out = GitTreeItem();
auto it = blob_map.find(file);
if (it != blob_map.end()) {
*out = it->second;
return true;
}
return !lookup_(file, out).isEmpty();
}
void GitCommitTree::parseTree(QString const &tree_id)
{
parse_tree_(objcache, tree_id, QString(), &root_item_list);
}
QString GitCommitTree::parseCommit(QString const &commit_id)
{
GitCommit commit;
commit.parseCommit(objcache, commit_id);
parseTree(commit.tree_id);
return commit.tree_id;
}
//
QString lookupFileID(GitObjectCache *objcache, QString const &commit_id, QString const &file)
// 指定されたコミットに属するファイルのIDを求める
{
GitCommitTree commit_tree(objcache);
commit_tree.parseCommit(commit_id);
QString id = commit_tree.lookup(file);
return id;
}
diff --git a/src/GitDiff.h b/src/GitDiff.h
index f3e6054..36a9aeb 100644
--- a/src/GitDiff.h
+++ b/src/GitDiff.h
@@ -1,63 +1,63 @@
#ifndef GITDIFF_H
#define GITDIFF_H
#include <set>
#include "common/misc.h"
#include "Git.h"
#include "GitObjectManager.h"
class GitDiff {
friend class CommitListThread;
public:
private:
class LookupTable;
private:
GitObjectCache *objcache = nullptr;
QList<Git::Diff> diffs;
bool interrupted = false;
struct Interrupted {
};
void checkInterrupted()
{
if (interrupted) {
throw Interrupted();
}
}
- typedef std::list<LookupTable> MapList;
+ using MapList = std::list<LookupTable>;
GitPtr git();
static void AddItem(Git::Diff *item, QList<Git::Diff> *diffs);
void retrieveCompleteTree(QString const &dir, const GitTreeItemList *files, std::map<QString, GitTreeItem> *out);
void retrieveCompleteTree(QString const &dir, const GitTreeItemList *files);
public:
GitDiff(GitObjectCache *objcache)
{
this->objcache = objcache;
}
- bool diff(QString id, QList<Git::Diff> *out);
+ bool diff(const QString &id, QList<Git::Diff> *out);
bool diff_uncommited(QList<Git::Diff> *out);
void interrupt()
{
interrupted = true;
}
public:
static QString diffObjects(GitPtr g, QString const &a_id, QString const &b_id);
static QString diffFiles(GitPtr g, QString const &a_path, QString const &b_path);
static void parseDiff(std::string const &s, const Git::Diff *info, Git::Diff *out);
static QString makeKey(QString const &a_id, QString const &b_id);
static QString makeKey(const Git::Diff &diff);
static QString prependPathPrefix(QString const &path);
};
#endif // GITDIFF_H
diff --git a/src/GitHubAPI.h b/src/GitHubAPI.h
index b049683..8616b4e 100644
--- a/src/GitHubAPI.h
+++ b/src/GitHubAPI.h
@@ -1,62 +1,62 @@
#ifndef GITHUBAPI_H
#define GITHUBAPI_H
#include <QImage>
#include <QThread>
#include <string>
#include <functional>
#include <memory>
class WebContext;
class WebClient;
class GitHubAPI {
public:
- typedef std::shared_ptr<WebClient> WebClientPtr;
+ using WebClientPtr = std::shared_ptr<WebClient>;
struct User {
std::string login;
std::string avatar_url;
std::string name;
std::string email;
};
struct SearchResultItem {
std::string full_name;
std::string description;
std::string ssh_url;
std::string clone_url;
std::string html_url;
double score = 0;
};
WebContext *webcx;
GitHubAPI(WebContext *webcx)
: webcx(webcx)
{
}
QList<GitHubAPI::SearchResultItem> searchRepository(const std::string &q);
};
class GitHubRequestThread : public QThread {
private:
struct Private;
Private *m;
protected:
- void run();
+ void run() override;
public:
GitHubAPI::WebClientPtr web();
public:
GitHubRequestThread();
- ~GitHubRequestThread();
+ ~GitHubRequestThread() override;
std::string url;
bool ok = false;
std::string text;
std::function<bool(std::string const &text)> callback;
void start(WebContext *webcx);
};
#endif // GITHUBAPI_H
diff --git a/src/GitObjectManager.cpp b/src/GitObjectManager.cpp
index 04e01d6..04494b6 100644
--- a/src/GitObjectManager.cpp
+++ b/src/GitObjectManager.cpp
@@ -1,389 +1,388 @@
+
#include "GitObjectManager.h"
#include "Git.h"
#include "common/joinpath.h"
#include "common/misc.h"
#include <QBuffer>
#include <QDebug>
#include <QDirIterator>
#include <QFile>
+#include <memory>
GitObjectManager::GitObjectManager()
{
subdir_git_objects = ".git/objects";
subdir_git_objects_pack = subdir_git_objects / "pack";
}
void GitObjectManager::setup(GitPtr g)
{
this->g = g;
clearIndexes();
}
QString GitObjectManager::workingDir()
{
return g->workingRepositoryDir();
}
void GitObjectManager::loadIndexes()
{
QMutexLocker lock(&mutex);
if (git_idx_list.empty()) {
QString path = workingDir() / subdir_git_objects_pack;
QDirIterator it(path, { "pack-*.idx" }, QDir::Files | QDir::Readable);
while (it.hasNext()) {
it.next();
- GitPackIdxPtr idx = GitPackIdxPtr(new GitPackIdxV2());
+ GitPackIdxPtr idx = std::make_shared<GitPackIdxV2>();
idx->basename = it.fileInfo().baseName();
idx->parse(it.filePath());
git_idx_list.push_back(idx);
}
}
}
void GitObjectManager::clearIndexes()
{
git_idx_list.clear();
}
void GitObjectManager::applyDelta(QByteArray const *base_obj, QByteArray const *delta_obj, QByteArray *out)
{
if (delta_obj->size() > 0) {
uint8_t const *begin = (uint8_t const *)delta_obj->data();
uint8_t const *end = begin + delta_obj->size();
uint8_t const *ptr = begin;
auto ReadNumber = [&](){
uint64_t n = 0;
int shift = 0;
while (ptr < end) {
uint64_t c = *ptr;
ptr++;
n |= (c & 0x7f) << shift;
shift += 7;
if (!(c & 0x80)) break;
}
return n;
};
uint64_t a = ReadNumber(); // older file size
uint64_t b = ReadNumber(); // newer file size
(void)a;
(void)b;
// cf. https://github.com/github/git-msysgit/blob/master/patch-delta.c
while (ptr < end) {
uint8_t op = *ptr;
ptr++;
if (op & 0x80) { // copy operation
uint32_t offset = 0;
uint32_t length = 0;
if (op & 0x01) offset = *ptr++;
if (op & 0x02) offset |= (*ptr++ << 8);
if (op & 0x04) offset |= (*ptr++ << 16);
if (op & 0x08) offset |= ((unsigned) *ptr++ << 24);
if (op & 0x10) length = *ptr++;
if (op & 0x20) length |= (*ptr++ << 8);
if (op & 0x40) length |= (*ptr++ << 16);
if (length == 0) length = 0x10000;
if (offset + length > (uint32_t)base_obj->size()) {
qDebug() << Q_FUNC_INFO << "base-file or delta-file is corrupted";
out->clear();
return;
}
out->append(base_obj->data() + offset, length);
} else if (op > 0) { // insert operation
int length = op & 0x7f;
if (ptr + length <= end) {
out->append((char const *)ptr, length);
ptr += length;
}
} else {
qDebug() << Q_FUNC_INFO << "unexpected delta opcode 0";
}
}
}
}
-bool GitObjectManager::loadPackedObject(GitPackIdxPtr idx, QIODevice *packfile, GitPackIdxItem const *item, GitPack::Object *out)
+bool GitObjectManager::loadPackedObject(GitPackIdxPtr const &idx, QIODevice *packfile, GitPackIdxItem const *item, GitPack::Object *out)
{
GitPack::Info info;
if (GitPack::seekPackedObject(packfile, item, &info)) {
GitPackIdxItem const *source_item = nullptr;
if (info.type == Git::Object::Type::OFS_DELTA) {
source_item = idx->item(item->offset - info.offset);
} else if (info.type == Git::Object::Type::REF_DELTA) {
source_item = idx->item(info.ref_id);
}
if (source_item) { // if deltified object
GitPack::Object source;
if (source_item && loadPackedObject(idx, packfile, source_item, &source)) {
GitPack::Object delta;
if (GitPack::load(packfile, item, &delta)) {
if (delta.checksum != item->checksum) {
qDebug() << "crc checksum incorrect";
return false;
}
QByteArray ba;
applyDelta(&source.content, &delta.content, &ba);
*out = GitPack::Object();
out->type = source.type;
out->content = std::move(ba);
out->expanded_size = out->content.size();
return true;
}
}
qDebug() << Q_FUNC_INFO << "failed";
return false;
}
}
- if (GitPack::load(packfile, item, out)) {
- return true;
- }
- return false;
+ return GitPack::load(packfile, item, out);
}
-bool GitObjectManager::extractObjectFromPackFile(GitPackIdxPtr idx, GitPackIdxItem const *item, GitPack::Object *out)
+bool GitObjectManager::extractObjectFromPackFile(GitPackIdxPtr const &idx, GitPackIdxItem const *item, GitPack::Object *out)
{
*out = GitPack::Object();
QString packfilepath = workingDir() / subdir_git_objects_pack / (idx->basename + ".pack");
QFile packfile(packfilepath);
if (packfile.open(QFile::ReadOnly)) {
if (loadPackedObject(idx, &packfile, item, out)) {
if (out->type == Git::Object::Type::TREE) {
GitPack::decodeTree(&out->content);
}
return true;
}
}
return false;
}
bool GitObjectManager::extractObjectFromPackFile(QString const &id, QByteArray *out, Git::Object::Type *type)
{
loadIndexes();
for (GitPackIdxPtr idx : git_idx_list) {
GitPackIdxItem const *item = idx->item(id);
if (item) {
GitPack::Object obj;
if (extractObjectFromPackFile(idx, item, &obj)) {
*out = std::move(obj.content);
*type = obj.type;
return true;
}
qDebug() << Q_FUNC_INFO << "failed";
return false;
}
}
return false;
}
QString GitObjectManager::findObjectPath(QString const &id)
{
if (Git::isValidID(id)) {
int count = 0;
QString absolute_path;
QString xx = id.mid(0, 2); // leading two xdigits
QString name = id.mid(2); // remaining xdigits
QString dir = workingDir() / subdir_git_objects / xx; // e.g. /home/user/myproject/.git/objects/5a
QDirIterator it(dir, QDir::Files);
while (it.hasNext()) {
it.next();
if (it.fileName().startsWith(name)) {
QString id = xx + it.fileName(); // complete id
if (id.size() == GIT_ID_LENGTH && Git::isValidID(id)) {
absolute_path = dir / it.fileName();
count++;
}
}
}
if (count == 1) return absolute_path;
if (count > 1) qDebug() << Q_FUNC_INFO << "ambiguous id" << id;
}
return QString(); // not found
}
bool GitObjectManager::loadObject(QString const &id, QByteArray *out, Git::Object::Type *type)
{
QString path = findObjectPath(id);
if (!path.isEmpty()) {
QFile file(path);
if (file.open(QFile::ReadOnly)) {
if (GitPack::decompress(&file, 1000000000, out)) {
*type = GitPack::stripHeader(out);
if (*type == Git::Object::Type::TREE) {
GitPack::decodeTree(out);
}
return true;
}
}
}
return false;
}
bool GitObjectManager::catFile(QString const &id, QByteArray *out, Git::Object::Type *type)
{
*type = Git::Object::Type::UNKNOWN;
if (loadObject(id, out, type)) return true;
if (extractObjectFromPackFile(id, out, type)) return true;
return false;
}
//
size_t GitObjectCache::size() const
{
size_t size = 0;
for (ItemPtr const &item : items) {
size += item->ba.size();
}
return size;
}
void GitObjectCache::setup(GitPtr g)
{
items.clear();
revparsemap.clear();
object_manager.setup(g->dup());
}
QString GitObjectCache::revParse(QString const &name)
{
{
QMutexLocker lock(&object_manager.mutex);
auto it = revparsemap.find(name);
if (it != revparsemap.end()) {
return it->second;
}
}
QString id = git()->rev_parse(name);
{
QMutexLocker lock(&object_manager.mutex);
revparsemap[name] = id;
return id;
}
}
Git::Object GitObjectCache::catFile(QString const &id)
{
{
QMutexLocker lock(&object_manager.mutex);
size_t n = items.size();
size_t i = n;
while (i > 0) {
i--;
if (items[i]->id == id) {
ItemPtr item = items[i];
if (i + 1 < n) {
items.erase(items.begin() + i);
items.push_back(item);
}
Git::Object obj;
obj.type = item->type;
obj.content = item->ba;
return obj;
}
}
while (size() > 100000000) { // 100MB
items.erase(items.begin());
}
}
QByteArray ba;
Git::Object::Type type = Git::Object::Type::UNKNOWN;
auto Store = [&](){
QMutexLocker lock(&object_manager.mutex);
Item *item = new Item();
item->id = id;
item->ba = std::move(ba);
item->type = type;
items.push_back(ItemPtr(item));
Git::Object obj;
obj.type = item->type;
obj.content = item->ba;
return obj;
};
if (object_manager.catFile(id, &ba, &type)) { // 独自実装のファイル取得
return Store();
}
if (true) {
if (git()->cat_file(id, &ba)) { // 外部コマンド起動の git cat-file -p を試してみる
// 上の独自実装のファイル取得が正しく動作していれば、ここには来ないはず
qDebug() << __LINE__ << __FILE__ << Q_FUNC_INFO << id;
return Store();
}
}
qDebug() << "failed to cat file: " << id;
return Git::Object();
}
QString GitObjectCache::getCommitIdFromTag(QString const &tag)
{
QString commit_id;
GitPtr g = git();
if (g && g->isValidWorkingCopy()) {
QString id = g->rev_parse(tag);
Git::Object obj = catFile(id);
switch (obj.type) {
case Git::Object::Type::COMMIT:
commit_id = id;
break;
case Git::Object::Type::TAG:
if (!obj.content.isEmpty()) {
misc::splitLines(obj.content, [&](char const *ptr, size_t len){
if (commit_id.isEmpty()) {
if (len >= 7 + GIT_ID_LENGTH && strncmp(ptr, "object ", 7) == 0) {
QString id = QString::fromUtf8(ptr + 7, len - 7).trimmed();
if (Git::isValidID(id)) {
commit_id = id;
}
}
}
return QString();
});
}
break;
}
}
return commit_id;
}
bool GitCommit::parseCommit(GitObjectCache *objcache, QString const &id)
{
parents.clear();
if (!id.isEmpty()) {
QStringList parents;
{
Git::Object obj = objcache->catFile(id);
if (!obj.content.isEmpty()) {
QStringList lines = misc::splitLines(QString::fromUtf8(obj.content));
for (QString const &line : lines) {
int i = line.indexOf(' ');
if (i < 1) break;
QString key = line.mid(0, i);
QString val = line.mid(i + 1).trimmed();
if (key == "tree") {
tree_id = val;
} else if (key == "parent") {
parents.push_back(val);
}
}
}
}
if (!tree_id.isEmpty()) { // サブディレクトリ
this->parents.append(parents);
return true;
}
}
return false;
}
diff --git a/src/GitObjectManager.h b/src/GitObjectManager.h
index 21b6b71..86c7a5d 100644
--- a/src/GitObjectManager.h
+++ b/src/GitObjectManager.h
@@ -1,129 +1,129 @@
#ifndef GITOBJECTMANAGER_H
#define GITOBJECTMANAGER_H
#include <QMutex>
#include <QString>
#include "GitPack.h"
#include "GitPackIdxV2.h"
#include <map>
class GitPackIdxV2;
class Git;
-typedef std::shared_ptr<Git> GitPtr;
+using GitPtr = std::shared_ptr<Git>;
class GitObjectManager {
friend class GitObjectCache;
private:
GitPtr g;
QMutex mutex;
QString subdir_git_objects;
QString subdir_git_objects_pack;
std::vector<GitPackIdxPtr> git_idx_list;
QString workingDir();
static void applyDelta(const QByteArray *base, const QByteArray *delta, QByteArray *out);
- static bool loadPackedObject(GitPackIdxPtr idx, QIODevice *packfile, const GitPackIdxItem *item, GitPack::Object *out);
- bool extractObjectFromPackFile(GitPackIdxPtr idx, const GitPackIdxItem *item, GitPack::Object *out);
+ static bool loadPackedObject(const GitPackIdxPtr &idx, QIODevice *packfile, const GitPackIdxItem *item, GitPack::Object *out);
+ bool extractObjectFromPackFile(const GitPackIdxPtr &idx, const GitPackIdxItem *item, GitPack::Object *out);
bool extractObjectFromPackFile(QString const &id, QByteArray *out, Git::Object::Type *type);
void loadIndexes();
QString findObjectPath(QString const &id);
bool loadObject(QString const &id, QByteArray *out, Git::Object::Type *type);
GitPtr git()
{
return g;
}
public:
GitObjectManager();
void setup(GitPtr g);
bool catFile(QString const &id, QByteArray *out, Git::Object::Type *type);
void clearIndexes();
};
class GitObjectCache {
public:
struct Item {
QString id;
QByteArray ba;
Git::Object::Type type;
};
private:
GitObjectManager object_manager;
- typedef std::shared_ptr<Item> ItemPtr;
+ using ItemPtr = std::shared_ptr<Item>;
std::vector<ItemPtr> items;
std::map<QString, QString> revparsemap;
size_t size() const;
public:
GitPtr git()
{
return object_manager.git();
}
void setup(GitPtr g);
QString revParse(QString const &name);
Git::Object catFile(QString const &id);
QString getCommitIdFromTag(QString const &tag);
};
class GitCommit {
public:
QString tree_id;
QStringList parents;
bool parseCommit(GitObjectCache *objcache, QString const &id);
};
struct GitTreeItem {
enum Type {
UNKNOWN,
TREE,
BLOB,
};
Type type = UNKNOWN;
QString name;
QString id;
QString mode;
QString to_string_() const
{
QString t;
switch (type) {
case TREE: t = "TREE"; break;
case BLOB: t = "BLOB"; break;
}
return QString("GitTreeItem:{ %1 %2 %3 %4 }").arg(t).arg(id).arg(mode).arg(name);
}
};
-typedef QList<GitTreeItem> GitTreeItemList;
+using GitTreeItemList = QList<GitTreeItem>;
class GitCommitTree {
private:
GitObjectCache *objcache;
GitTreeItemList root_item_list;
std::map<QString, GitTreeItem> blob_map;
std::map<QString, QString> tree_id_map;
GitPtr git();
QString lookup_(QString const &file, GitTreeItem *out);
public:
GitCommitTree(GitObjectCache *objcache);
QString lookup(QString const &file);
bool lookup(QString const &file, GitTreeItem *out);
void parseTree(QString const &tree_id);
QString parseCommit(QString const &commit_id);
GitTreeItemList const *treelist() const
{
return &root_item_list;
}
};
QString lookupFileID(GitObjectCache *objcache, QString const &commit_id, QString const &file);
#endif // GITOBJECTMANAGER_H
diff --git a/src/GitPack.h b/src/GitPack.h
index 319ecc0..42e5757 100644
--- a/src/GitPack.h
+++ b/src/GitPack.h
@@ -1,39 +1,39 @@
#ifndef GITPACK_H
#define GITPACK_H
#include <QIODevice>
-#include <stdint.h>
+#include <cstdint>
#include "Git.h"
struct GitPackIdxItem;
class GitPack {
public:
struct Info {
Git::Object::Type type = Git::Object::Type::UNKNOWN;
size_t expanded_size = 0;
uint64_t offset = 0;
QString ref_id;
uint32_t checksum = 0;
};
struct Object : public Info {
QByteArray content;
size_t packed_size = 0;
};
private:
static uint32_t read_uint32_be(const void *p)
{
- uint8_t const *q = (uint8_t const *)p;
+ auto const *q = (uint8_t const *)p;
return (q[0] << 24) | (q[1] << 16) | (q[2] << 8) | q[3];
}
public:
static bool decompress(QIODevice *in, size_t expanded_size, QByteArray *out, size_t *consumed = nullptr, uint32_t *crc = nullptr);
static bool load(QIODevice *file, GitPackIdxItem const *item, Object *out);
static bool load(QString const &packfile, const GitPackIdxItem *item, Object *out);
static bool seekPackedObject(QIODevice *file, const GitPackIdxItem *item, Info *out);
static void decodeTree(QByteArray *out);
static Git::Object::Type stripHeader(QByteArray *out);
};
#endif // GITPACK_H
diff --git a/src/GitPackIdxV2.h b/src/GitPackIdxV2.h
index 22b39e1..791bfe9 100644
--- a/src/GitPackIdxV2.h
+++ b/src/GitPackIdxV2.h
@@ -1,67 +1,67 @@
#ifndef GITPACKIDXV2_H
#define GITPACKIDXV2_H
#include <QIODevice>
-#include <stdint.h>
+#include <cstdint>
#include <vector>
#include "GitPack.h"
#include <memory>
struct GitPackIdxItem {
QString id;
Git::Object::Type type = Git::Object::Type::UNKNOWN;
size_t offset = 0;
size_t packed_size = 0;
size_t expanded_size = 0;
uint32_t checksum;
};
class GitPackIdxV2 {
friend class GitObjectManager;
friend class MainWindow; // for debug
private:
QString basename; // e.g. "pack-56430ed038c968ded87eb3756dcde85bfafc10ce"
struct header_t {
uint8_t magic[8];
uint32_t fanout[256];
};
struct trailer_t {
uint8_t packfile_checksum[20];
uint8_t idxfile_checksum[20];
};
struct object_id_t {
uint8_t id[20];
};
struct Data {
header_t header;
std::vector<object_id_t> objects;
std::vector<uint32_t> checksums;
std::vector<uint32_t> offsets;
trailer_t trailer;
std::vector<GitPackIdxItem> item_list;
} d;
private:
static QString toString(const uint8_t *p);
static inline uint32_t read_uint32_be(void const *p);
static inline uint32_t get_fanout(header_t const *t, int i);
uint8_t const *object(int i) const;
uint32_t offset(int i) const;
uint32_t checksum(int i) const;
void clear();
bool parse(QIODevice *in);
public:
bool parse(QString const &idxpath);
GitPackIdxItem const *item(QString const &id) const;
GitPackIdxItem const *item(size_t offset) const;
};
-typedef std::shared_ptr<GitPackIdxV2> GitPackIdxPtr;
+using GitPackIdxPtr = std::shared_ptr<GitPackIdxV2>;
#endif // GITPACKIDXV2_H
diff --git a/src/HyperLinkLabel.h b/src/HyperLinkLabel.h
index f18bb07..6f7da5a 100644
--- a/src/HyperLinkLabel.h
+++ b/src/HyperLinkLabel.h
@@ -1,24 +1,17 @@
#ifndef HYPERLINKLABEL_H
#define HYPERLINKLABEL_H
#include <QLabel>
-class HyperLinkLabel : public QLabel
-{
+class HyperLinkLabel : public QLabel {
Q_OBJECT
public:
- explicit HyperLinkLabel(QWidget *parent = 0);
-
- void paintEvent(QPaintEvent *);
-
- void mousePressEvent(QMouseEvent *e);
- void mouseReleaseEvent(QMouseEvent *e);
-
+ explicit HyperLinkLabel(QWidget *parent = nullptr);
+ void paintEvent(QPaintEvent *) override;
+ void mousePressEvent(QMouseEvent *e) override;
+ void mouseReleaseEvent(QMouseEvent *e) override;
signals:
void clicked();
-
-public slots:
-
};
#endif // HYPERLINKLABEL_H
diff --git a/src/ImageViewWidget.cpp b/src/ImageViewWidget.cpp
index 47ff559..a4c54b2 100644
--- a/src/ImageViewWidget.cpp
+++ b/src/ImageViewWidget.cpp
@@ -1,376 +1,373 @@
-#include <memory>
#include "ImageViewWidget.h"
#include "FileDiffSliderWidget.h"
#include "FileDiffWidget.h"
-
#include "MainWindow.h"
-#include "common/misc.h"
-#include "common/joinpath.h"
-#include "Photoshop.h"
#include "MemoryReader.h"
+#include "Photoshop.h"
#include "charvec.h"
-
-#include <math.h>
-#include <functional>
-
+#include "common/joinpath.h"
+#include "common/misc.h"
+#include <QBuffer>
#include <QDebug>
#include <QFileDialog>
#include <QMenu>
#include <QPainter>
-#include <QWheelEvent>
#include <QSvgRenderer>
-#include <QBuffer>
+#include <QWheelEvent>
+#include <cmath>
+#include <functional>
+#include <memory>
using SvgRendererPtr = std::shared_ptr<QSvgRenderer>;
struct ImageViewWidget::Private {
- MainWindow *mainwindow = nullptr;
+ QMainWindow *mainwindow = nullptr;
FileDiffWidget *filediffwidget = nullptr;
FileDiffWidget::DrawData *draw_data = nullptr;
QScrollBar *v_scroll_bar = nullptr;
QScrollBar *h_scroll_bar = nullptr;
QString mime_type;
QPixmap pixmap;
SvgRendererPtr svg;
double image_scroll_x = 0;
double image_scroll_y = 0;
double image_scale = 1;
double scroll_origin_x = 0;
double scroll_origin_y = 0;
QPoint mouse_press_pos;
int wheel_delta = 0;
QPointF interest_pos;
int top_margin = 1;
int bottom_margin = 1;
bool draw_left_border = true;
#ifndef APP_GUITAR
QPixmap transparent_pixmap;
#endif
};
ImageViewWidget::ImageViewWidget(QWidget *parent)
: QWidget(parent)
, m(new Private)
{
#if defined(Q_OS_WIN32)
setFont(QFont("MS Gothic"));
#elif defined(Q_OS_LINUX)
setFont(QFont("Monospace"));
#elif defined(Q_OS_MAC)
setFont(QFont("Menlo"));
#endif
setContextMenuPolicy(Qt::DefaultContextMenu);
}
ImageViewWidget::~ImageViewWidget()
{
delete m;
}
-void ImageViewWidget::bind(MainWindow *mainwindow, FileDiffWidget *filediffwidget, QScrollBar *vsb, QScrollBar *hsb)
+void ImageViewWidget::bind(QMainWindow *mainwindow, FileDiffWidget *filediffwidget, QScrollBar *vsb, QScrollBar *hsb)
{
m->mainwindow = mainwindow;
m->filediffwidget = filediffwidget;
m->v_scroll_bar = vsb;
m->h_scroll_bar = hsb;
}
bool ImageViewWidget::hasFocus() const
{
QWidget *w = qApp->focusWidget();
return w && w != m->filediffwidget && w->isAncestorOf(this);
}
void ImageViewWidget::setLeftBorderVisible(bool f)
{
m->draw_left_border = f;
}
void ImageViewWidget::internalScrollImage(double x, double y)
{
m->image_scroll_x = x;
m->image_scroll_y = y;
QSizeF sz = imageScrollRange();
if (m->image_scroll_x < 0) m->image_scroll_x = 0;
if (m->image_scroll_y < 0) m->image_scroll_y = 0;
if (m->image_scroll_x > sz.width()) m->image_scroll_x = sz.width();
if (m->image_scroll_y > sz.height()) m->image_scroll_y = sz.height();
update();
}
void ImageViewWidget::scrollImage(double x, double y)
{
internalScrollImage(x, y);
if (m->h_scroll_bar) {
m->h_scroll_bar->blockSignals(true);
m->h_scroll_bar->setValue((int)m->image_scroll_x);
m->h_scroll_bar->blockSignals(false);
}
if (m->v_scroll_bar) {
m->v_scroll_bar->blockSignals(true);
m->v_scroll_bar->setValue((int)m->image_scroll_y);
m->v_scroll_bar->blockSignals(false);
}
}
void ImageViewWidget::refrectScrollBar()
{
double e = 0.75;
double x = m->h_scroll_bar->value();
double y = m->v_scroll_bar->value();
if (fabs(x - m->image_scroll_x) < e) x = m->image_scroll_x; // 差が小さいときは値を維持する
if (fabs(y - m->image_scroll_y) < e) y = m->image_scroll_y;
internalScrollImage(x, y);
}
void ImageViewWidget::clear()
{
m->mime_type = QString();
m->pixmap = QPixmap();
setMouseTracking(false);
update();
}
QString ImageViewWidget::formatText(Document::Line const &line)
{
QByteArray const &ba = line.text;
if (ba.isEmpty()) return QString();
std::vector<char> vec;
vec.reserve(ba.size() + 100);
char const *begin = ba.data();
char const *end = begin + ba.size();
char const *ptr = begin;
int x = 0;
while (ptr < end) {
if (*ptr == '\t') {
do {
vec.push_back(' ');
x++;
} while ((x % 4) != 0);
ptr++;
} else {
vec.push_back(*ptr);
ptr++;
x++;
}
}
return QString::fromUtf8(&vec[0], vec.size());
}
QSizeF ImageViewWidget::imageScrollRange() const
{
QSize sz = imageSize();
int w = int(sz.width() * m->image_scale);
int h = int(sz.height() * m->image_scale);
return QSize(w, h);
}
void ImageViewWidget::setScrollBarRange(QScrollBar *h, QScrollBar *v)
{
h->blockSignals(true);
v->blockSignals(true);
QSizeF sz = imageScrollRange();
h->setRange(0, (int)sz.width());
v->setRange(0, (int)sz.height());
h->setPageStep(width());
v->setPageStep(height());
h->blockSignals(false);
v->blockSignals(false);
}
void ImageViewWidget::updateScrollBarRange()
{
setScrollBarRange(m->h_scroll_bar, m->v_scroll_bar);
}
-MainWindow *ImageViewWidget::mainwindow()
+QMainWindow *ImageViewWidget::mainwindow()
{
return m->mainwindow;
}
QBrush ImageViewWidget::getTransparentBackgroundBrush()
{
#ifdef APP_GUITAR
- return mainwindow()->getTransparentPixmap();
+ return qobject_cast<BasicMainWindow *>(mainwindow())->getTransparentPixmap();
#else
if (m->transparent_pixmap.isNull()) {
m->transparent_pixmap = QPixmap(":/image/transparent.png");
}
return m->transparent_pixmap;
#endif
}
bool ImageViewWidget::isValidImage() const
{
return !m->pixmap.isNull() || (m->svg && m->svg->isValid());
}
QSize ImageViewWidget::imageSize() const
{
if (!m->pixmap.isNull()) return m->pixmap.size();
if (m->svg && m->svg->isValid()) return m->svg->defaultSize();
return QSize();
}
void ImageViewWidget::paintEvent(QPaintEvent *)
{
QPainter pr(this);
QSize imagesize = imageSize();
if (imagesize.width() > 0 && imagesize.height() > 0) {
pr.save();
if (!m->draw_left_border) {
pr.setClipRect(1, 0, width() - 1, height());
}
double cx = width() / 2.0;
double cy = height() / 2.0;
double x = cx - m->image_scroll_x;
double y = cy - m->image_scroll_y;
QSizeF sz = imageScrollRange();
if (sz.width() > 0 && sz.height() > 0) {
QBrush br = getTransparentBackgroundBrush();
pr.setBrushOrigin((int)x, (int)y);
pr.fillRect((int)x, (int)y, (int)sz.width(), (int)sz.height(), br);
if (!m->pixmap.isNull()) {
pr.drawPixmap((int)x, (int)y, (int)sz.width(), (int)sz.height(), m->pixmap, 0, 0, imagesize.width(), imagesize.height());
} else if (m->svg && m->svg->isValid()) {
m->svg->render(&pr, QRectF(x, y, sz.width(), sz.height()));
}
}
misc::drawFrame(&pr, (int)x - 1, (int)y - 1, (int)sz.width() + 2, (int)sz.height() + 2, Qt::black);
pr.restore();
}
if (m->draw_left_border) {
pr.fillRect(0, 0, 1, height(), QColor(160, 160, 160));
}
if (hasFocus()) {
misc::drawFrame(&pr, 0, 0, width(), height(), QColor(0, 128, 255, 128));
misc::drawFrame(&pr, 1, 1, width() - 2, height() - 2, QColor(0, 128, 255, 64));
}
}
void ImageViewWidget::resizeEvent(QResizeEvent *)
{
updateScrollBarRange();
}
void ImageViewWidget::setImage(QString mimetype, QByteArray const &ba)
{
if (mimetype.isEmpty()) {
mimetype = "image/x-unknown";
}
setMouseTracking(true);
m->pixmap = QPixmap();
m->svg = SvgRendererPtr();
if (!ba.isEmpty()) {
if (misc::isSVG(mimetype)) {
m->svg = std::make_shared<QSvgRenderer>(ba);
} else if (misc::isPSD(mimetype)) {
if (!ba.isEmpty()) {
MemoryReader reader(ba.data(), ba.size());
if (reader.open(QIODevice::ReadOnly)) {
std::vector<char> jpeg;
photoshop::readThumbnail(&reader, &jpeg);
if (!jpeg.empty()) {
m->pixmap.loadFromData((uchar const *)&jpeg[0], jpeg.size());
}
}
}
} else {
m->pixmap.loadFromData(ba);
}
}
QSize sz = imageSize();
double sx = sz.width();
double sy = sz.height();
if (sx > 0 && sy > 0) {
sx = width() / sx;
sy = height() / sy;
m->image_scale = (sx < sy ? sx : sy) * 0.9;
}
updateScrollBarRange();
m->h_scroll_bar->blockSignals(true);
m->v_scroll_bar->blockSignals(true);
m->h_scroll_bar->setValue(m->h_scroll_bar->maximum() / 2);
m->v_scroll_bar->setValue(m->v_scroll_bar->maximum() / 2);
m->h_scroll_bar->blockSignals(false);
m->v_scroll_bar->blockSignals(false);
refrectScrollBar();
}
void ImageViewWidget::mousePressEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton) {
QPoint pos = mapFromGlobal(QCursor::pos());
m->mouse_press_pos = pos;
m->scroll_origin_x = m->image_scroll_x;
m->scroll_origin_y = m->image_scroll_y;
}
}
void ImageViewWidget::mouseMoveEvent(QMouseEvent *e)
{
if (isValidImage()) {
QPoint pos = mapFromGlobal(QCursor::pos());
if ((e->buttons() & Qt::LeftButton) && hasFocus()) {
int delta_x = pos.x() - m->mouse_press_pos.x();
int delta_y = pos.y() - m->mouse_press_pos.y();
scrollImage(m->scroll_origin_x - delta_x, m->scroll_origin_y - delta_y);
}
double cx = width() / 2.0;
double cy = height() / 2.0;
double x = (pos.x() + 0.5 - cx + m->image_scroll_x) / m->image_scale;
double y = (pos.y() + 0.5 - cy + m->image_scroll_y) / m->image_scale;
m->interest_pos = QPointF(x, y);
m->wheel_delta = 0;
}
}
void ImageViewWidget::setImageScale(double scale)
{
if (scale < 1 / 32.0) scale = 1 / 32.0;
if (scale > 32) scale = 32;
m->image_scale = scale;
}
void ImageViewWidget::wheelEvent(QWheelEvent *e)
{
if (isValidImage()) {
double scale = 1;
const double mul = 1.189207115; // sqrt(sqrt(2))
m->wheel_delta += e->delta();
while (m->wheel_delta >= 120) {
m->wheel_delta -= 120;
scale *= mul;
}
while (m->wheel_delta <= -120) {
m->wheel_delta += 120;
scale /= mul;
}
setImageScale(m->image_scale * scale);
updateScrollBarRange();
double cx = width() / 2.0;
double cy = height() / 2.0;
QPoint pos = mapFromGlobal(QCursor::pos());
double dx = m->interest_pos.x() * m->image_scale + cx - (pos.x() + 0.5);
double dy = m->interest_pos.y() * m->image_scale + cy - (pos.y() + 0.5);
scrollImage(dx, dy);
update();
}
}
diff --git a/src/ImageViewWidget.h b/src/ImageViewWidget.h
index 0e1a8d7..5edfae9 100644
--- a/src/ImageViewWidget.h
+++ b/src/ImageViewWidget.h
@@ -1,57 +1,57 @@
#ifndef IMAGEVIEWWIDGET_H
#define IMAGEVIEWWIDGET_H
#include <QScrollBar>
#include <QWidget>
#include "Git.h"
#include "MainWindow.h"
#include "AbstractCharacterBasedApplication.h"
class FileDiffWidget;
class FileDiffSliderWidget;
class ImageViewWidget : public QWidget {
Q_OBJECT
private:
struct Private;
Private *m;
bool isValidImage() const;
QSize imageSize() const;
QSizeF imageScrollRange() const;
void internalScrollImage(double x, double y);
void scrollImage(double x, double y);
void setImageScale(double scale);
QBrush getTransparentBackgroundBrush();
bool hasFocus() const;
void setScrollBarRange(QScrollBar *h, QScrollBar *v);
void updateScrollBarRange();
protected:
- MainWindow *mainwindow();
- void resizeEvent(QResizeEvent *);
- void paintEvent(QPaintEvent *);
- void mousePressEvent(QMouseEvent *event);
- void mouseMoveEvent(QMouseEvent *event);
- void wheelEvent(QWheelEvent *);
+ QMainWindow *mainwindow();
+ void resizeEvent(QResizeEvent *) override;
+ void paintEvent(QPaintEvent *) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void wheelEvent(QWheelEvent *) override;
public:
- explicit ImageViewWidget(QWidget *parent = 0);
- ~ImageViewWidget();
+ explicit ImageViewWidget(QWidget *parent = nullptr);
+ ~ImageViewWidget() override;
- void bind(MainWindow *m, FileDiffWidget *filediffwidget, QScrollBar *vsb, QScrollBar *hsb);
+ void bind(QMainWindow *m, FileDiffWidget *filediffwidget, QScrollBar *vsb, QScrollBar *hsb);
void clear();
void setImage(QString mimetype, QByteArray const &ba);
void setLeftBorderVisible(bool f);
void refrectScrollBar();
static QString formatText(const Document::Line &line2);
signals:
void scrollByWheel(int lines);
};
#endif // IMAGEVIEWWIDGET_H
diff --git a/src/InputNewTagDialog.h b/src/InputNewTagDialog.h
index 501332c..85bce2f 100644
--- a/src/InputNewTagDialog.h
+++ b/src/InputNewTagDialog.h
@@ -1,23 +1,22 @@
#ifndef INPUTNEWTAGDIALOG_H
#define INPUTNEWTAGDIALOG_H
#include <QDialog>
namespace Ui {
class InputNewTagDialog;
}
-class InputNewTagDialog : public QDialog
-{
+class InputNewTagDialog : public QDialog {
Q_OBJECT
public:
- explicit InputNewTagDialog(QWidget *parent = 0);
- ~InputNewTagDialog();
+ explicit InputNewTagDialog(QWidget *parent = nullptr);
+ ~InputNewTagDialog() override;
QString text() const;
private:
Ui::InputNewTagDialog *ui;
};
#endif // INPUTNEWTAGDIALOG_H
diff --git a/src/JumpDialog.cpp b/src/JumpDialog.cpp
index ea651e0..7cb6ea2 100644
--- a/src/JumpDialog.cpp
+++ b/src/JumpDialog.cpp
@@ -1,145 +1,145 @@
#include "JumpDialog.h"
#include "ui_JumpDialog.h"
#include <QDebug>
struct JumpDialog::Private {
MyTableWidgetDelegate delegate;
QString filter_text;
QString selected_name;
NamedCommitList items;
};
JumpDialog::JumpDialog(QWidget *parent, const NamedCommitList &items)
: QDialog(parent)
, ui(new Ui::JumpDialog)
, m(new Private)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
ui->tableWidget->setItemDelegate(&m->delegate);
m->items = items;
QStringList header = {
tr("Name"),
};
ui->tableWidget->setColumnCount(header.size());
{
for (int i = 0; i < header.size(); i++) {
- QTableWidgetItem *p = new QTableWidgetItem();
+ auto *p = new QTableWidgetItem();
p->setText(header[i]);
ui->tableWidget->setHorizontalHeaderItem(i, p);
}
}
updateTable();
ui->tabWidget->setCurrentWidget(ui->tab_branches_tags);
ui->lineEdit_filter->setFocus();
}
JumpDialog::~JumpDialog()
{
delete m;
delete ui;
}
JumpDialog::Action JumpDialog::action() const
{
QWidget *w = ui->tabWidget->currentWidget();
if (w == ui->tab_branches_tags) return Action::BranchsAndTags;
if (w == ui->tab_find_text) return Action::CommitId;
return Action::None;
}
QString JumpDialog::text() const
{
JumpDialog::Action a = action();
if (a == JumpDialog::Action::BranchsAndTags) {
return m->selected_name;
}
if (a == JumpDialog::Action::CommitId) {
return ui->lineEdit_text->text();
}
return QString();
}
void JumpDialog::sort(NamedCommitList *items)
{
std::sort(items->begin(), items->end(), [](NamedCommitItem const &l, NamedCommitItem const &r){
return l.name.compare(r.name, Qt::CaseInsensitive) < 0;
});
}
void JumpDialog::updateTable_(NamedCommitList const &list)
{
ui->tableWidget->clearContents();
ui->tableWidget->setRowCount(list.size());
for (int i = 0; i < list.size(); i++) {
- QTableWidgetItem *p = new QTableWidgetItem();
+ auto *p = new QTableWidgetItem();
QString name = list[i].name;
p->setText(name);
ui->tableWidget->setItem(i, 0, p);
ui->tableWidget->setRowHeight(i, 24);
}
ui->tableWidget->resizeColumnsToContents();
ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
ui->tableWidget->setCurrentCell(0, 0);
}
void JumpDialog::updateTable()
{
if (m->filter_text.isEmpty()) {
updateTable_(m->items);
} else {
NamedCommitList list;
for (NamedCommitItem const &item: m->items) {
if (item.name.indexOf(m->filter_text, 0, Qt::CaseInsensitive) < 0) {
continue;
}
list.push_back(item);
}
updateTable_(list);
}
}
void JumpDialog::on_lineEdit_filter_textChanged(QString const &text)
{
m->filter_text = text;
updateTable();
}
void JumpDialog::on_toolButton_clicked()
{
ui->lineEdit_filter->clear();
ui->lineEdit_filter->setFocus();
}
void JumpDialog::on_tableWidget_currentItemChanged(QTableWidgetItem * /*current*/, QTableWidgetItem * /*previous*/)
{
int row = ui->tableWidget->currentRow();
QTableWidgetItem *p = ui->tableWidget->item(row, 0);
m->selected_name = p ? p->text() : QString();
}
bool JumpDialog::isCheckoutChecked()
{
return ui->checkBox_checkout->isChecked();
}
void JumpDialog::on_tabWidget_currentChanged(int /*index*/)
{
if (ui->tabWidget->currentWidget() == ui->tab_branches_tags) {
ui->checkBox_checkout->setVisible(true);
} else {
ui->checkBox_checkout->setVisible(false);
}
}
diff --git a/src/JumpDialog.h b/src/JumpDialog.h
index e17f0c7..bb5c8d8 100644
--- a/src/JumpDialog.h
+++ b/src/JumpDialog.h
@@ -1,51 +1,50 @@
#ifndef JUMPDIALOG_H
#define JUMPDIALOG_H
#include "MyTableWidgetDelegate.h"
#include "Git.h"
#include <QDialog>
namespace Ui {
class JumpDialog;
}
class QTableWidgetItem;
-class JumpDialog : public QDialog
-{
+class JumpDialog : public QDialog {
Q_OBJECT
public:
private:
struct Private;
Private *m;
public:
explicit JumpDialog(QWidget *parent, NamedCommitList const &items);
- ~JumpDialog();
+ ~JumpDialog() override;
enum class Action {
None,
BranchsAndTags,
CommitId,
};
Action action() const;
QString text() const;
static void sort(NamedCommitList *items);
bool isCheckoutChecked();
private slots:
void on_toolButton_clicked();
void on_lineEdit_filter_textChanged(QString const &text);
void on_tableWidget_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
void on_tabWidget_currentChanged(int index);
private:
Ui::JumpDialog *ui;
void updateTable();
void updateTable_(const NamedCommitList &list2);
};
#endif // JUMPDIALOG_H
diff --git a/src/LineEditDialog.h b/src/LineEditDialog.h
index 5e0e906..e1ddecd 100644
--- a/src/LineEditDialog.h
+++ b/src/LineEditDialog.h
@@ -1,21 +1,20 @@
#ifndef LINEEDITDIALOG_H
#define LINEEDITDIALOG_H
#include <QDialog>
namespace Ui {
class LineEditDialog;
}
-class LineEditDialog : public QDialog
-{
+class LineEditDialog : public QDialog {
Q_OBJECT
public:
explicit LineEditDialog(QWidget *parent, QString const &title, QString const &prompt, QString const &val, bool password);
- ~LineEditDialog();
+ ~LineEditDialog() override;
QString text() const;
private:
Ui::LineEditDialog *ui;
};
#endif // LINEEDITDIALOG_H
diff --git a/src/LogTableWidget.cpp b/src/LogTableWidget.cpp
index 1de2feb..8188930 100644
--- a/src/LogTableWidget.cpp
+++ b/src/LogTableWidget.cpp
@@ -1,334 +1,334 @@
#include "LogTableWidget.h"
#include <QDebug>
#include <QEvent>
#include <QPainter>
#include <QProxyStyle>
#include <cmath>
#include "MainWindow.h"
#include <QApplication>
#include "MyTableWidgetDelegate.h"
#include "common/misc.h"
struct LogTableWidget::Private {
};
class LogTableWidgetDelegate : public MyTableWidgetDelegate {
private:
MainWindow *mainwindow() const
{
- LogTableWidget *w = dynamic_cast<LogTableWidget *>(QStyledItemDelegate::parent());
+ auto *w = dynamic_cast<LogTableWidget *>(QStyledItemDelegate::parent());
Q_ASSERT(w);
return w->mainwindow();
}
static QColor labelColor(int kind)
{
switch (kind) {
- case MainWindow::Label::Head: return QColor(255, 192, 224); // blue
- case MainWindow::Label::LocalBranch: return QColor(192, 224, 255); // blue
- case MainWindow::Label::RemoteBranch: return QColor(192, 240, 224); // green
- case MainWindow::Label::Tag: return QColor(255, 224, 192); // orange
+ case BasicMainWindow::Label::Head: return QColor(255, 192, 224); // blue
+ case BasicMainWindow::Label::LocalBranch: return QColor(192, 224, 255); // blue
+ case BasicMainWindow::Label::RemoteBranch: return QColor(192, 240, 224); // green
+ case BasicMainWindow::Label::Tag: return QColor(255, 224, 192); // orange
}
return QColor(224, 224, 224); // gray
}
static QColor hiliteColor(QColor const &color)
{
int r = color.red();
int g = color.green();
int b = color.blue();
r = 255 - (255 - r) / 2;
g = 255 - (255 - g) / 2;
b = 255 - (255 - b) / 2;
return QColor(r, g, b);
}
static QColor shadowColor(QColor const &color)
{
return QColor(color.red() / 2, color.green() / 2, color.blue() / 2);
}
void drawSignatureIcon(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const
{
if (!opt.widget->isEnabled()) return;
Git::CommitItem const *commit = mainwindow()->commitItem(index.row());
QIcon icon = mainwindow()->verifiedIcon(commit->signature);
if (!icon.isNull()) {
QRect r = opt.rect.adjusted(6, 3, 0, -3);
int h = r.height();
int w = h;
int x = r.x() + r.width() - w;
int y = r.y();
icon.paint(painter, x, y, w, h);
}
}
void drawAvatar(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const
{
if (!opt.widget->isEnabled()) return;
int row = index.row();
QIcon icon = mainwindow()->committerIcon(row);
if (!icon.isNull()) {
int h = opt.rect.height();
int w = h;
int x = opt.rect.x() + opt.rect.width() - w;
int y = opt.rect.y();
painter->save();
painter->setOpacity(0.5); // 半透明で描画
icon.paint(painter, x, y, w, h);
painter->restore();
}
}
void drawDescription(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const
{
int row = index.row();
- QList<MainWindow::Label> const *labels = mainwindow()->label(row);
+ QList<BasicMainWindow::Label> const *labels = mainwindow()->label(row);
if (labels) {
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
QFontMetrics fm = painter->fontMetrics();
const int space = 8;
int x = opt.rect.x() + opt.rect.width() - 3;
int x1 = x;
int y0 = opt.rect.y();
int y1 = y0 + opt.rect.height() - 1;
int i = labels->size();
while (i > 0) {
i--;
- MainWindow::Label const &label = labels->at(i);
+ BasicMainWindow::Label const &label = labels->at(i);
QString text = misc::abbrevBranchName(label.text);
int w = fm.size(0, text).width() + space * 2;
int x0 = x1 - w;
QRect r(x0, y0, x1 - x0, y1 - y0);
painter->setPen(Qt::NoPen);
auto DrawRect = [&](int dx, int dy, QColor color){
painter->setBrush(color);
painter->drawRoundedRect(r.adjusted(lround(dx + 3), lround(dy + 3), lround(dx - 3), lround(dy - 3)), 3, 3);
};
QColor color = labelColor(label.kind);
QColor hilite = hiliteColor(color);
QColor shadow = shadowColor(color);
DrawRect(-1, -1, hilite);
DrawRect(1, 1, shadow);
DrawRect(0, 0, color);
painter->setPen(Qt::black);
painter->setBrush(Qt::NoBrush);
qApp->style()->drawItemText(painter, r.adjusted(space, 0, 0, 0), opt.displayAlignment, opt.palette, true, text);
x1 = x0;
}
painter->restore();
}
}
public:
explicit LogTableWidgetDelegate(QObject *parent = Q_NULLPTR)
: MyTableWidgetDelegate(parent)
{
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
MyTableWidgetDelegate::paint(painter, option, index);
// signatureの描画
if (index.column() == 1) {
drawSignatureIcon(painter, option, index);
}
// avatarの描画
if (index.column() == 3) {
drawAvatar(painter, option, index);
}
// Descriptionの描画
if (index.column() == 4) {
drawDescription(painter, option, index);
}
}
};
LogTableWidget::LogTableWidget(QWidget *parent)
: QTableWidget(parent)
, m(new Private)
{
setItemDelegate(new LogTableWidgetDelegate(this));
}
LogTableWidget::~LogTableWidget()
{
delete m;
}
MainWindow *LogTableWidget::mainwindow()
{
MainWindow *mw = qobject_cast<MainWindow *>(window());
Q_ASSERT(mw);
return mw;
}
void drawBranch(QPainterPath *path, double x0, double y0, double x1, double y1, double r, bool bend_early)
{
const double k = 0.55228475;
if (x0 == x1) {
path->moveTo(x0, y0);
path->lineTo(x1, y1);
} else {
double ym = bend_early ? (y0 + r) : (y1 - r);
double h = fabs(y1 - y0);
double w = fabs(x1 - x0);
if (r > h / 2) r = h / 2;
if (r > w / 2) r = w / 2;
double s = r;
if (x0 > x1) r = -r;
if (y0 > y1) s = -s;
if (0) {
path->moveTo(x0, y0);
path->lineTo(x0, ym - s);
path->cubicTo(x0, ym - s + s * k, x0 + r - r * k, ym, x0 + r, ym);
path->lineTo(x1 - r, ym);
path->cubicTo(x1 - r + r * k, ym, x1, ym + s - s * k, x1, ym + s);
path->lineTo(x1, y1);
} else {
if (bend_early) {
path->moveTo(x0, y0);
path->cubicTo(x0, ym, x1, ym, x1, ym + ym - y0);
path->lineTo(x1, y1);
} else {
path->moveTo(x0, y0);
path->lineTo(x0, ym + ym - y1);
path->cubicTo(x0, ym, x1, ym, x1, y1);
}
}
}
}
void LogTableWidget::paintEvent(QPaintEvent *e)
{
if (rowCount() < 1) return;
QTableWidget::paintEvent(e);
QPainter pr(viewport());
pr.setRenderHint(QPainter::Antialiasing);
pr.setBrush(QBrush(QColor(255, 255, 255)));
- Git::CommitItemList const *list = mainwindow()->logs();
+ Git::CommitItemList const *list = &mainwindow()->getLogs();
int indent_span = 16;
auto ItemRect = [&](int row){
QRect r;
QTableWidgetItem *p = item(row, 0);
if (p) {
r = visualItemRect(p);
}
return r;
};
int line_width = 2;
auto ItemPoint = [&](int depth, QRect const &rect){
int h = rect.height();
double n = h / 2.0;
double x = floor(rect.x() + n + depth * indent_span);
double y = floor(rect.y() + n);
return QPointF(x, y);
};
auto SetPen = [&](QPainter *pr, int level, bool /*continued*/){
QColor c = mainwindow()->color(level + 1);
Qt::PenStyle s = Qt::SolidLine;
pr->setPen(QPen(c, line_width, s));
};
auto DrawLine = [&](size_t index, int itemrow){
QRect rc1;
if (index < list->size()) {
Git::CommitItem const &item1 = list->at(index);
rc1 = ItemRect(itemrow);
QPointF pt1 = ItemPoint(item1.marker_depth, rc1);
double halfheight = rc1.height() / 2.0;
for (TreeLine const &line : item1.parent_lines) {
if (line.depth >= 0) {
QPainterPath *path = nullptr;
Git::CommitItem const &item2 = list->at(line.index);
QRect rc2 = ItemRect(line.index);
if (index + 1 == (size_t)line.index || line.depth == item1.marker_depth || line.depth == item2.marker_depth) {
QPointF pt2 = ItemPoint(line.depth, rc2);
if (pt2.y() > 0) {
path = new QPainterPath();
drawBranch(path, pt1.x(), pt1.y(), pt2.x(), pt2.y(), halfheight, line.bend_early);
}
} else {
QPointF pt3 = ItemPoint(item2.marker_depth, rc2);
if (pt3.y() > 0) {
path = new QPainterPath();
QRect rc3 = ItemRect(itemrow + 1);
QPointF pt2 = ItemPoint(line.depth, rc3);
drawBranch(path, pt1.x(), pt1.y(), pt2.x(), pt2.y(), halfheight, true);
drawBranch(path, pt2.x(), pt2.y(), pt3.x(), pt3.y(), halfheight, false);
}
}
if (path) {
SetPen(&pr, line.color_number, false);
pr.drawPath(*path);
delete path;
}
}
}
}
return rc1.y();
};
auto DrawMark = [&](size_t index, int row){
double x, y;
y = 0;
if (index < list->size()) {
Git::CommitItem const &item = list->at(index);
QRect rc = ItemRect(row);
QPointF pt = ItemPoint(item.marker_depth, rc);
double r = 4;
x = pt.x() - r;
y = pt.y() - r;
SetPen(&pr, item.marker_depth, false);
if (item.resolved) {
// ◯
pr.drawEllipse((int)x, (int)y, int(r * 2), int(r * 2));
} else {
// ▽
QPainterPath path;
path.moveTo(pt.x(), pt.y() + r);
path.lineTo(pt.x() - r, pt.y() - r);
path.lineTo(pt.x() + r, pt.y() - r);
path.lineTo(pt.x(), pt.y() + r);
pr.drawPath(path);
}
}
return y;
};
// draw lines
pr.setOpacity(0.5);
pr.setBrush(Qt::NoBrush);
for (size_t i = 0; i < list->size(); i++) {
double y = DrawLine(i, i);
if (y >= height()) break;
}
// draw marks
pr.setOpacity(1);
pr.setBrush(mainwindow()->color(0));
for (size_t i = 0; i < list->size(); i++) {
double y = DrawMark(i, i);
if (y >= height()) break;
}
}
diff --git a/src/LogTableWidget.h b/src/LogTableWidget.h
index 07b2acf..1e3fa75 100644
--- a/src/LogTableWidget.h
+++ b/src/LogTableWidget.h
@@ -1,24 +1,23 @@
#ifndef LOGTABLEWIDGET_H
#define LOGTABLEWIDGET_H
#include <QTableWidget>
class MainWindow;
class LogTableWidgetDelegate;
-class LogTableWidget : public QTableWidget
-{
+class LogTableWidget : public QTableWidget {
Q_OBJECT
friend class LogTableWidgetDelegate;
private:
struct Private;
Private *m;
MainWindow *mainwindow();
public:
- explicit LogTableWidget(QWidget *parent = 0);
- ~LogTableWidget();
+ explicit LogTableWidget(QWidget *parent = nullptr);
+ ~LogTableWidget() override;
protected:
- void paintEvent(QPaintEvent *);
+ void paintEvent(QPaintEvent *) override;
};
#endif // LOGTABLEWIDGET_H
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index ec3c857..e5c5080 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -1,4914 +1,2828 @@
-#include "AreYouSureYouWantToContinueConnectingDialog.h"
-#include "BlameWindow.h"
-#include "CommitViewWindow.h"
-#include "Git.h"
-#include "LineEditDialog.h"
#include "MainWindow.h"
-#include "ReflogWindow.h"
-#include "SetGlobalUserDialog.h"
-#include "EditTagsDialog.h"
-#include "WelcomeWizardDialog.h"
#include "ui_MainWindow.h"
-#include "EditGitIgnoreDialog.h"
-#include "DoYouWantToInitDialog.h"
-#include "RemoteWatcher.h"
-#include "ApplicationGlobal.h"
#include "AboutDialog.h"
+#include "ApplicationGlobal.h"
+#include "AreYouSureYouWantToContinueConnectingDialog.h"
#include "AvatarLoader.h"
-#include "CheckoutDialog.h"
-#include "CloneDialog.h"
-#include "CommitExploreWindow.h"
+#include "BlameWindow.h"
#include "CommitPropertyDialog.h"
-#include "common/joinpath.h"
-#include "common/misc.h"
-#include "ConfigCredentialHelperDialog.h"
-#include "CreateRepositoryDialog.h"
#include "DeleteBranchDialog.h"
-#include "DeleteTagsDialog.h"
-#include "InputNewTagDialog.h"
-#include "FileHistoryWindow.h"
-#include "FilePropertyDialog.h"
-#include "FileUtil.h"
-#include "Git.h"
+#include "EditGitIgnoreDialog.h"
+#include "EditTagsDialog.h"
+#include "FileDiffWidget.h"
#include "GitDiff.h"
-#include "gunzip.h"
#include "JumpDialog.h"
-#include "LocalSocketReader.h"
-#include "main.h"
-#include "MemoryReader.h"
-#include "MergeBranchDialog.h"
-#include "MyProcess.h"
+#include "LineEditDialog.h"
#include "MySettings.h"
-#include "PushDialog.h"
-#include "RepositoryData.h"
-#include "RepositoryPropertyDialog.h"
-#include "SelectCommandDialog.h"
-#include "SetRemoteUrlDialog.h"
+#include "RebaseOntoDialog.h"
+#include "ReflogWindow.h"
+#include "RemoteWatcher.h"
+#include "SetGpgSigningDialog.h"
#include "SettingsDialog.h"
-#include "SetUserDialog.h"
#include "StatusLabel.h"
-#include "Terminal.h"
#include "TextEditDialog.h"
-#include "CommitDialog.h"
-#include "SelectGpgKeyDialog.h"
-#include "SetGpgSigningDialog.h"
-#include "gpg.h"
+#include "common/joinpath.h"
+#include "common/misc.h"
+#include "platform.h"
#include "webclient.h"
-#include "RebaseOntoDialog.h"
-#include <QBuffer>
#include <QClipboard>
-#include <QDateTime>
-#include <QDebug>
-#include <QDesktopServices>
-#include <QDirIterator>
-#include <QDragEnterEvent>
#include <QFileDialog>
#include <QFileIconProvider>
-#include <QKeyEvent>
-#include <QLocalServer>
#include <QMessageBox>
#include <QMimeData>
#include <QPainter>
-#include <QProcess>
#include <QStandardPaths>
-#include <QThread>
#include <QTimer>
-#include <deque>
-#include <set>
-#include <stdlib.h>
-
-#ifdef Q_OS_WIN
-#include "win32/win32.h"
-#else
-#include <unistd.h>
-#endif
-
-
-#ifdef Q_OS_MAC
-extern "C" char **environ;
-#endif
-
-struct GitHubRepositoryInfo {
- QString owner_account_name;
- QString repository_name;
-};
-
-class AsyncExecGitThread_ : public QThread {
-private:
- GitPtr g;
- std::function<void(GitPtr g)> callback;
-public:
- AsyncExecGitThread_(GitPtr g, std::function<void(GitPtr g)> callback)
- : g(g)
- , callback(callback)
- {
- }
-protected:
- void run() override
- {
- callback(g);
- }
-};
FileDiffWidget::DrawData::DrawData()
{
bgcolor_text = QColor(255, 255, 255);
bgcolor_gray = QColor(224, 224, 224);
bgcolor_add = QColor(192, 240, 192);
bgcolor_del = QColor(255, 224, 224);
bgcolor_add_dark = QColor(64, 192, 64);
bgcolor_del_dark = QColor(240, 64, 64);
}
-enum {
- IndexRole = Qt::UserRole,
- FilePathRole,
- DiffIndexRole,
- HunkIndexRole,
-};
-
-enum {
- GroupItem = -1,
-};
-
-static inline bool isGroupItem(QTreeWidgetItem *item)
-{
- if (item) {
- int index = item->data(0, IndexRole).toInt();
- if (index == GroupItem) {
- return true;
- }
- }
- return false;
-}
-
-static inline QString getFilePath(QListWidgetItem *item)
-{
- if (!item) return QString();
- return item->data(FilePathRole).toString();
-}
-
-static inline int indexOfLog(QListWidgetItem *item)
-{
- if (!item) return -1;
- return item->data(IndexRole).toInt();
-}
-
-static inline int indexOfDiff(QListWidgetItem *item)
-{
- if (!item) return -1;
- return item->data(DiffIndexRole).toInt();
-}
-
-static inline int getHunkIndex(QListWidgetItem *item)
-{
- if (!item) return -1;
- return item->data(HunkIndexRole).toInt();
-}
-
-enum class PtyCondition {
- None,
- Clone,
- Fetch,
- Pull,
- Push,
-};
-
-enum InteractionMode {
- None,
- Busy,
-};
-
struct MainWindow::Private {
- QString starting_dir;
- Git::Context gcx;
- ApplicationSettings appsettings;
- QList<RepositoryItem> repos;
- RepositoryItem current_repo;
- ServerType server_type = ServerType::Standard;
- GitHubRepositoryInfo github;
- QString current_remote;
- Git::Branch current_branch;
- QString head_id;
- struct Diff {
- QList<Git::Diff> result;
- std::shared_ptr<QThread> thread;
- QList<std::shared_ptr<QThread>> garbage;
- } diff;
- std::map<QString, Git::Diff> diff_cache;
- QStringList added;
- Git::CommitItemList logs;
- std::map<int, QList<Label>> label_map;
- bool force_fetch = false;
- bool uncommited_changes = false;
- int update_files_list_counter = 0;
QTimer interval_10ms_timer;
QTimer remote_watcher_timer;
QImage graph_color;
- QStringList remotes;
- std::map<QString, QList<Git::Branch>> branch_map;
- std::map<QString, QList<Git::Tag>> tag_map;
- QString repository_filter_text;
QPixmap digits;
- QIcon repository_icon;
- QIcon folder_icon;
- QIcon signature_good_icon;
- QIcon signature_dubious_icon;
- QIcon signature_bad_icon;
- unsigned int temp_file_counter = 0;
- GitObjectCache objcache;
- QPixmap transparent_pixmap;
StatusLabel *status_bar_label;
QObject *last_focused_file_list = nullptr;
- WebContext webcx;
- AvatarLoader avatar_loader;
- int update_commit_table_counter = 0;
-
- std::map<QString, GitHubAPI::User> committer_map; // key is email
-
- PtyProcess pty_process;
- PtyCondition pty_condition = PtyCondition::None;
- bool pty_process_ok = false;
-
- RepositoryItem temp_repo;
-
QListWidgetItem *last_selected_file_item = nullptr;
- InteractionMode interaction_mode = InteractionMode::None;
- bool interaction_canceled = false;
-
- bool remote_changed = false;
-
- std::string ssh_passphrase_for;
- std::string ssh_passphrase_secret;
-
- std::string http_uid;
- std::string http_pwd;
-
RemoteWatcher remote_watcher;
};
MainWindow::MainWindow(QWidget *parent)
- : QMainWindow(parent)
+ : BasicMainWindow(parent)
, ui(new Ui::MainWindow)
, m(new Private)
{
ui->setupUi(this);
#ifdef Q_OS_MACX
ui->action_about->setText("About Guitar...");
ui->action_edit_settings->setText("Settings...");
#endif
- m->starting_dir = QDir::current().absolutePath();
-
ui->splitter_v->setSizes({100, 400});
ui->splitter_h->setSizes({200, 100, 200});
m->status_bar_label = new StatusLabel(this);
ui->statusBar->addWidget(m->status_bar_label);
ui->widget_diff_view->bind(this);
qApp->installEventFilter(this);
ui->widget_log->setupForLogWidget(ui->verticalScrollBar_log, ui->horizontalScrollBar_log, themeForTextEditor());
onLogVisibilityChanged();
- SettingsDialog::loadSettings(&m->appsettings);
-
initNetworking();
showFileList(FilesListType::SingleList);
- QFileIconProvider icons;
-
m->digits.load(":/image/digits.png");
m->graph_color = global->theme->graphColorMap();
- m->repository_icon = QIcon(":/image/repository.png");
- m->folder_icon = icons.icon(QFileIconProvider::Folder);
-
- m->signature_good_icon = QIcon(":/image/signature-good.png");
- m->signature_bad_icon = QIcon(":/image/signature-bad.png");
- m->signature_dubious_icon = QIcon(":/image/signature-dubious.png");
-
prepareLogTableWidget();
#ifdef Q_OS_WIN
{
QFont font;
font = ui->label_repo_name->font();
font.setFamily("Meiryo");
ui->label_repo_name->setFont(font);
font = ui->label_branch_name->font();
font.setFamily("Meiryo");
ui->label_branch_name->setFont(font);
}
#endif
connect(this, SIGNAL(signalWriteLog(QByteArray)), this, SLOT(writeLog(QByteArray)));
connect(ui->dockWidget_log, SIGNAL(visibilityChanged(bool)), this, SLOT(onLogVisibilityChanged()));
connect(ui->widget_log, SIGNAL(idle()), this, SLOT(onLogIdle()));
connect(ui->treeWidget_repos, SIGNAL(dropped()), this, SLOT(onRepositoriesTreeDropped()));
- connect((AbstractPtyProcess *)&m->pty_process, SIGNAL(completed()), this, SLOT(onPtyProcessCompleted()));
+ connect((AbstractPtyProcess *)getPtyProcess(), SIGNAL(completed()), this, SLOT(onPtyProcessCompleted()));
+
+ connect(this, &BasicMainWindow::remoteInfoChanged, [&](){
+ ui->lineEdit_remote->setText(currentRemoteName());
+ });
// リモート監視
connect(this, SIGNAL(signalCheckRemoteUpdate()), &m->remote_watcher, SLOT(checkRemoteUpdate()));
connect(&m->remote_watcher_timer, SIGNAL(timeout()), &m->remote_watcher, SLOT(checkRemoteUpdate()));
connect(this, &MainWindow::updateButton, [&](){
doUpdateButton();
});
m->remote_watcher.start(this);
setWatchRemoteInterval(appsettings()->watch_remote_changes_every_mins);
+ connect(this, &MainWindow::signalSetRemoteChanged, [&](bool f){
+ setRemoteChanged(f);
+ updateButton();
+ });
+
//
QString path = getBookmarksFilePath();
- m->repos = RepositoryBookmark::load(path);
+ *getReposPtr() = RepositoryBookmark::load(path);
updateRepositoriesList();
- m->webcx.set_keep_alive_enabled(true);
- m->avatar_loader.start(&m->webcx);
- connect(&m->avatar_loader, SIGNAL(updated()), this, SLOT(onAvatarUpdated()));
+ webContext()->set_keep_alive_enabled(true);
+ getAvatarLoader()->start(webContext());
+ connect(getAvatarLoader(), SIGNAL(updated()), this, SLOT(onAvatarUpdated()));
- m->update_files_list_counter = 0;
+ *ptrUpdateFilesListCounter() = 0;
connect(ui->widget_diff_view, &FileDiffWidget::textcodecChanged, [&](){ updateDiffView(); });
if (!global->start_with_shift_key && appsettings()->remember_and_restore_window_position) {
Qt::WindowStates state = windowState();
MySettings settings;
settings.beginGroup("MainWindow");
bool maximized = settings.value("Maximized").toBool();
restoreGeometry(settings.value("Geometry").toByteArray());
// ui->splitter->restoreState(settings.value("SplitterState").toByteArray());
settings.endGroup();
if (maximized) {
state |= Qt::WindowMaximized;
setWindowState(state);
}
}
startTimers();
}
MainWindow::~MainWindow()
{
stopPtyProcess();
- m->avatar_loader.stop();
+ getAvatarLoader()->stop();
m->remote_watcher.quit();
m->remote_watcher.wait();
- deleteTempFiles();
-
delete m;
delete ui;
}
-bool MainWindow::checkGitCommand()
-{
- while (1) {
- if (misc::isExecutable(m->gcx.git_command)) {
- return true;
- }
- if (selectGitCommand(true).isEmpty()) {
- close();
- return false;
- }
- }
-}
-
-bool MainWindow::checkFileCommand()
-{
- while (1) {
- if (misc::isExecutable(global->file_command)) {
- return true;
- }
- if (selectFileCommand(true).isEmpty()) {
- close();
- return false;
- }
- }
-}
-
-ApplicationSettings *MainWindow::appsettings()
-{
- return &m->appsettings;
-}
-ApplicationSettings const *MainWindow::appsettings() const
-{
- return &m->appsettings;
-}
-bool MainWindow::execWelcomeWizardDialog()
-{
- WelcomeWizardDialog dlg(this);
- dlg.set_git_command_path(appsettings()->git_command);
- dlg.set_file_command_path(appsettings()->file_command);
- dlg.set_default_working_folder(appsettings()->default_working_dir);
- if (misc::isExecutable(appsettings()->git_command)) {
- m->gcx.git_command = appsettings()->git_command;
- Git g(m->gcx, QString());
- Git::User user = g.getUser(Git::Source::Global);
- dlg.set_user_name(user.name);
- dlg.set_user_email(user.email);
- }
- if (dlg.exec() == QDialog::Accepted) {
- appsettings()->git_command = m->gcx.git_command = dlg.git_command_path();
- appsettings()->file_command = global->file_command = dlg.file_command_path();
- appsettings()->default_working_dir = dlg.default_working_folder();
- SettingsDialog::saveSettings(&m->appsettings);
-
- if (misc::isExecutable(appsettings()->git_command)) {
- GitPtr g = git();
- Git::User user;
- user.name = dlg.user_name();
- user.email = dlg.user_email();
- g->setUser(user, true);
- }
- return true;
- }
- return false;
-}
bool MainWindow::shown()
{
while (!misc::isExecutable(appsettings()->git_command) || !misc::isExecutable(appsettings()->file_command)) {
if (!execWelcomeWizardDialog()) {
return false;
}
}
setGitCommand(appsettings()->git_command, false);
setFileCommand(appsettings()->file_command, false);
writeLog(AboutDialog::appVersion() + '\n'); // print application version
logGitVersion(); // print git command version
setGpgCommand(appsettings()->gpg_command, false);
{
MySettings s;
s.beginGroup("Remote");
bool f = s.value("Online", true).toBool();
s.endGroup();
setRemoteOnline(f);
}
setUnknownRepositoryInfo();
checkUser();
return true;
}
-WebContext *MainWindow::webContext()
-{
- return &m->webcx;
-}
-
void MainWindow::startTimers()
{
// interval 10ms
connect(&m->interval_10ms_timer, &QTimer::timeout, [&](){
const int ms = 10;
- if (m->update_commit_table_counter > 0) {
- if (m->update_commit_table_counter > ms) {
- m->update_commit_table_counter -= ms;
+ auto *p1 = ptrUpdateCommitTableCounter();
+ if (*p1 > 0) {
+ if (*p1 > ms) {
+ *p1 -= ms;
} else {
- m->update_commit_table_counter = 0;
+ *p1 = 0;
ui->tableWidget_log->viewport()->update();
}
}
- if (m->update_files_list_counter > 0) {
- if (m->update_files_list_counter > ms) {
- m->update_files_list_counter -= ms;
+ auto *p2 = ptrUpdateFilesListCounter();
+ if (*p2 > 0) {
+ if (*p2 > ms) {
+ *p2 -= ms;
} else {
- m->update_files_list_counter = 0;
+ *p2 = 0;
updateCurrentFilesList();
}
}
});
m->interval_10ms_timer.setInterval(10);
m->interval_10ms_timer.start();
startTimer(10);
}
-TextEditorThemePtr MainWindow::themeForTextEditor()
+TextEditorThemePtr BasicMainWindow::themeForTextEditor()
{
return global->theme->text_editor_theme;
}
void MainWindow::setCurrentLogRow(int row)
{
if (row >= 0 && row < ui->tableWidget_log->rowCount()) {
ui->tableWidget_log->setCurrentCell(row, 2);
ui->tableWidget_log->setFocus();
updateStatusBarText();
}
}
bool MainWindow::event(QEvent *event)
{
QEvent::Type et = event->type();
if (et == QEvent::WindowActivate) {
checkRemoteUpdate();
} else if (et == QEvent::KeyPress) {
auto *e = dynamic_cast<QKeyEvent *>(event);
Q_ASSERT(e);
int k = e->key();
if (k == Qt::Key_Escape) {
emit onEscapeKeyPressed();
} else if (k == Qt::Key_Delete) {
if (qApp->focusWidget() == ui->treeWidget_repos) {
removeSelectedRepositoryFromBookmark(true);
return true;
}
}
}
return QMainWindow::event(event);
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
QEvent::Type et = event->type();
if (et == QEvent::KeyPress) {
auto *e = dynamic_cast<QKeyEvent *>(event);
Q_ASSERT(e);
int k = e->key();
if (k == Qt::Key_Tab) {
if (centralWidget()->isAncestorOf(qApp->focusWidget())) {
QList<QWidget *> tabstops;
tabstops.push_back(ui->treeWidget_repos);
tabstops.push_back(ui->tableWidget_log);
if (ui->stackedWidget->currentWidget() == ui->page_files) {
tabstops.push_back(ui->listWidget_files);
} else if (ui->stackedWidget->currentWidget() == ui->page_uncommited) {
tabstops.push_back(ui->listWidget_unstaged);
tabstops.push_back(ui->toolButton_select_all);
tabstops.push_back(ui->toolButton_stage);
tabstops.push_back(ui->toolButton_unstage);
tabstops.push_back(ui->toolButton_commit);
tabstops.push_back(ui->listWidget_staged);
}
tabstops.push_back(ui->widget_diff_view);
int n = tabstops.size();
if (n > 0) {
QWidget *f = qApp->focusWidget();
int i;
for (i = 0; i < n; i++) {
if (tabstops[i] == f) {
break;
}
}
if (i < n) {
if (e->modifiers() & Qt::ShiftModifier) {
i = (i + n - 1) % n;
} else {
i = (i + 1) % n;
}
tabstops[i]->setFocus();
}
}
return true;
}
}
if (k == Qt::Key_Escape) {
if (centralWidget()->isAncestorOf(qApp->focusWidget())) {
ui->treeWidget_repos->setFocus();
return true;
}
}
if (e->modifiers() & Qt::ControlModifier) {
if (k == Qt::Key_Up || k == Qt::Key_Down) {
int rows = ui->tableWidget_log->rowCount();
int row = ui->tableWidget_log->currentRow();
if (k == Qt::Key_Up) {
if (row > 0) {
row--;
}
} else if (k == Qt::Key_Down) {
if (row + 1 < rows) {
row++;
}
}
ui->tableWidget_log->setCurrentCell(row, 0);
return true;
}
}
if (watched == ui->treeWidget_repos) {
if (k == Qt::Key_Enter || k == Qt::Key_Return) {
openSelectedRepository();
return true;
}
if (!(e->modifiers() & Qt::ControlModifier)) {
if (k >= 0 && k < 128 && QChar((uchar)k).isLetterOrNumber()) {
appendCharToRepoFilter(k);
return true;
}
if (k == Qt::Key_Backspace) {
backspaceRepoFilter();
return true;
}
if (k == Qt::Key_Escape) {
clearRepoFilter();
return true;
}
}
} else if (watched == ui->tableWidget_log) {
if (k == Qt::Key_Home) {
setCurrentLogRow(0);
return true;
}
if (k == Qt::Key_Escape) {
ui->treeWidget_repos->setFocus();
return true;
}
} else if (watched == ui->listWidget_files || watched == ui->listWidget_unstaged || watched == ui->listWidget_staged) {
if (k == Qt::Key_Escape) {
ui->tableWidget_log->setFocus();
return true;
}
}
} else if (et == QEvent::FocusIn) {
// ファイルリストがフォーカスを得たとき、diffビューを更新する。(コンテキストメニュー対応)
if (watched == ui->listWidget_unstaged) {
m->last_focused_file_list = watched;
updateStatusBarText();
updateUnstagedFileCurrentItem();
return true;
}
if (watched == ui->listWidget_staged) {
m->last_focused_file_list = watched;
updateStatusBarText();
updateStagedFileCurrentItem();
return true;
}
if (watched == ui->listWidget_files) {
m->last_focused_file_list = watched;
}
}
return false;
}
void MainWindow::closeEvent(QCloseEvent *event)
{
if (appsettings()->remember_and_restore_window_position) {
setWindowOpacity(0);
Qt::WindowStates state = windowState();
bool maximized = (state & Qt::WindowMaximized) != 0;
if (maximized) {
state &= ~Qt::WindowMaximized;
setWindowState(state);
}
{
MySettings settings;
settings.beginGroup("MainWindow");
settings.setValue("Maximized", maximized);
settings.setValue("Geometry", saveGeometry());
// settings.setValue("SplitterState", ui->splitter->saveState());
settings.endGroup();
}
}
QMainWindow::closeEvent(event);
}
void MainWindow::setStatusBarText(QString const &text)
{
m->status_bar_label->setText(text);
}
void MainWindow::clearStatusBarText()
{
setStatusBarText(QString());
}
-WebContext *MainWindow::getWebContextPtr()
-{
- return &m->webcx;
-}
-
-QString MainWindow::getObjectID(QListWidgetItem *item)
+QString BasicMainWindow::getObjectID(QListWidgetItem *item)
{
int i = indexOfDiff(item);
- if (i >= 0 && i < m->diff.result.size()) {
- Git::Diff const &diff = m->diff.result[i];
+ if (i >= 0 && i < diffResult()->size()) {
+ Git::Diff const &diff = diffResult()->at(i);
return diff.blob.a_id;
}
return QString();
}
void MainWindow::onLogVisibilityChanged()
{
ui->action_window_log->setChecked(ui->dockWidget_log->isVisible());
}
-void MainWindow::writeLog(char const *ptr, int len)
+void MainWindow::internalWriteLog(char const *ptr, int len)
{
ui->widget_log->logicalMoveToBottom();
ui->widget_log->write(ptr, len, false);
ui->widget_log->setChanged(false);
- m->interaction_canceled = false;
+ setInteractionCanceled(false);
}
-void MainWindow::writeLog(QString const &str)
+void BasicMainWindow::writeLog(QString const &str)
{
std::string s = str.toStdString();
writeLog(s.c_str(), s.size());
}
-void MainWindow::writeLog(QByteArray ba)
+void BasicMainWindow::writeLog(QByteArray ba)
{
if (!ba.isEmpty()) {
writeLog(ba.data(), ba.size());
}
}
-void MainWindow::emitWriteLog(QByteArray ba)
-{
- emit signalWriteLog(ba);
-}
-
-bool MainWindow::git_callback(void *cookie, char const *ptr, int len)
-{
- auto *mw = (MainWindow *)cookie;
- mw->emitWriteLog(QByteArray(ptr, len));
- return true;
-}
-
-bool MainWindow::saveRepositoryBookmarks() const
-{
- QString path = getBookmarksFilePath();
- return RepositoryBookmark::save(path, &m->repos);
-}
-
-void MainWindow::saveRepositoryBookmark(RepositoryItem item)
-{
- if (item.local_dir.isEmpty()) return;
-
- if (item.name.isEmpty()) {
- item.name = tr("Unnamed");
- }
-
- bool done = false;
- for (auto &repo : m->repos) {
- RepositoryItem *p = &repo;
- if (item.local_dir == p->local_dir) {
- *p = item;
- done = true;
- break;
- }
- }
- if (!done) {
- m->repos.push_back(item);
- }
- saveRepositoryBookmarks();
- updateRepositoriesList();
-}
-
void MainWindow::buildRepoTree(QString const &group, QTreeWidgetItem *item, QList<RepositoryItem> *repos)
{
QString name = item->text(0);
if (isGroupItem(item)) {
int n = item->childCount();
for (int i = 0; i < n; i++) {
QTreeWidgetItem *child = item->child(i);
QString sub = group / name;
buildRepoTree(sub, child, repos);
}
} else {
RepositoryItem const *repo = repositoryItem(item);
if (repo) {
RepositoryItem newrepo = *repo;
newrepo.name = name;
newrepo.group = group;
item->setData(0, IndexRole, repos->size());
repos->push_back(newrepo);
}
}
}
void MainWindow::refrectRepositories()
{
QList<RepositoryItem> newrepos;
int n = ui->treeWidget_repos->topLevelItemCount();
for (int i = 0; i < n; i++) {
QTreeWidgetItem *item = ui->treeWidget_repos->topLevelItem(i);
buildRepoTree(QString(), item, &newrepos);
}
- m->repos = std::move(newrepos);
+ *getReposPtr() = std::move(newrepos);
saveRepositoryBookmarks();
}
void MainWindow::onRepositoriesTreeDropped()
{
refrectRepositories();
QTreeWidgetItem *item = ui->treeWidget_repos->currentItem();
if (item) item->setExpanded(true);
}
const QPixmap &MainWindow::digitsPixmap() const
{
return m->digits;
}
int MainWindow::digitWidth() const
{
return 5;
}
int MainWindow::digitHeight() const
{
return 7;
}
void MainWindow::drawDigit(QPainter *pr, int x, int y, int n) const
{
int w = digitWidth();
int h = digitHeight();
pr->drawPixmap(x, y, w, h, m->digits, n * w, 0, w, h);
}
-QString MainWindow::defaultWorkingDir() const
+QString BasicMainWindow::defaultWorkingDir() const
{
return appsettings()->default_working_dir;
}
QColor MainWindow::color(unsigned int i)
{
unsigned int n = m->graph_color.width();
if (n > 0) {
n--;
if (i > n) i = n;
QRgb const *p = (QRgb const *)m->graph_color.scanLine(0);
return QColor(qRed(p[i]), qGreen(p[i]), qBlue(p[i]));
}
return Qt::black;
}
-bool MainWindow::isThereUncommitedChanges() const
-{
- return m->uncommited_changes;
-}
-
-Git::CommitItemList const *MainWindow::logs() const
-{
- return &m->logs;
-}
-
QString MainWindow::currentWorkingCopyDir() const
{
- QString workdir = m->current_repo.local_dir;
- if (!workdir.isEmpty()) return workdir;
-
- QTreeWidgetItem *treeitem = ui->treeWidget_repos->currentItem();
- if (treeitem) {
- RepositoryItem const *repo = repositoryItem(treeitem);
+ QString workdir = BasicMainWindow::currentWorkingCopyDir();
+ if (workdir.isEmpty()) {
+ RepositoryItem const *repo = selectedRepositoryItem();
if (repo) {
workdir = repo->local_dir;
return workdir;
}
}
-
- return QString();
-}
-
-QString MainWindow::currentRepositoryName() const
-{
- return m->current_repo.name;
-}
-
-QString MainWindow::currentRemoteName() const
-{
- return m->current_remote;
-}
-
-Git::Branch const &MainWindow::currentBranch() const
-{
- return m->current_branch;
-}
-
-QString MainWindow::currentBranchName() const
-{
- return currentBranch().name;
-}
-
-GitPtr MainWindow::git(QString const &dir) const
-{
- const_cast<MainWindow *>(this)->checkGitCommand();
-
- GitPtr g = std::shared_ptr<Git>(new Git(m->gcx, dir));
- g->setLogCallback(git_callback, (void *)this);
- return g;
-}
-
-GitPtr MainWindow::git()
-{
- return git(currentWorkingCopyDir());
-}
-
-bool MainWindow::queryCommit(QString const &id, Git::CommitItem *out)
-{
- *out = Git::CommitItem();
- GitPtr g = git();
- return g->queryCommit(id, out);
-}
-
-void MainWindow::setLogEnabled(GitPtr g, bool f)
-{
- if (f) {
- g->setLogCallback(git_callback, this);
- } else {
- g->setLogCallback(nullptr, nullptr);
- }
-}
-
-QString MainWindow::makeRepositoryName(QString const &loc)
-{
- int i = loc.lastIndexOf('/');
- int j = loc.lastIndexOf('\\');
- if (i < j) i = j;
- if (i >= 0) {
- i++;
- j = loc.size();
- if (loc.endsWith(".git")) {
- j -= 4;
- }
- return loc.mid(i, j - i);
- }
- return QString();
+ return workdir;
}
-RepositoryItem const *MainWindow::findRegisteredRepository(QString *workdir) const
+RepositoryItem const *BasicMainWindow::findRegisteredRepository(QString *workdir) const
{
*workdir = QDir(*workdir).absolutePath();
workdir->replace('\\', '/');
if (Git::isValidWorkingCopy(*workdir)) {
- for (RepositoryItem const &item : m->repos) {
+ for (RepositoryItem const &item : getRepos()) {
Qt::CaseSensitivity cs = Qt::CaseSensitive;
#ifdef Q_OS_WIN
cs = Qt::CaseInsensitive;
#endif
if (workdir->compare(item.local_dir, cs) == 0) {
return &item;
}
}
}
return nullptr;
}
int MainWindow::repositoryIndex_(QTreeWidgetItem const *item) const
{
if (item) {
int i = item->data(0, IndexRole).toInt();
- if (i >= 0 && i < m->repos.size()) {
+ if (i >= 0 && i < getRepos().size()) {
return i;
}
}
return -1;
}
RepositoryItem const *MainWindow::repositoryItem(QTreeWidgetItem const *item) const
{
int row = repositoryIndex_(item);
- if (row >= 0 && row < m->repos.size()) {
- return &m->repos[row];
- }
- return nullptr;
+ auto const &repos = getRepos();
+ return (row >= 0 && row < repos.size()) ? &repos[row] : nullptr;
+}
+
+RepositoryItem const *MainWindow::selectedRepositoryItem() const
+{
+ return repositoryItem(ui->treeWidget_repos->currentItem());
}
static QTreeWidgetItem *newQTreeWidgetItem()
{
auto *item = new QTreeWidgetItem;
item->setSizeHint(0, QSize(20, 20));
return item;
}
QTreeWidgetItem *MainWindow::newQTreeWidgetFolderItem(QString const &name)
{
QTreeWidgetItem *item = newQTreeWidgetItem();
item->setText(0, name);
item->setData(0, IndexRole, GroupItem);
- item->setIcon(0, m->folder_icon);
+ item->setIcon(0, getFolderIcon());
item->setFlags(item->flags() | Qt::ItemIsEditable);
return item;
}
void MainWindow::updateRepositoriesList()
{
QString path = getBookmarksFilePath();
- m->repos = RepositoryBookmark::load(path);
+ auto *repos = getReposPtr();
+ *repos = RepositoryBookmark::load(path);
- QString filter = m->repository_filter_text;
+ QString filter = getRepositoryFilterText();
ui->treeWidget_repos->clear();
std::map<QString, QTreeWidgetItem *> parentmap;
- for (int i = 0; i < m->repos.size(); i++) {
- RepositoryItem const &repo = m->repos[i];
+ for (int i = 0; i < repos->size(); i++) {
+ RepositoryItem const &repo = repos->at(i);
if (!filter.isEmpty() && repo.name.indexOf(filter, 0, Qt::CaseInsensitive) < 0) {
continue;
}
QTreeWidgetItem *parent = nullptr;
{
QString group = repo.group;
if (group.startsWith('/')) {
group = group.mid(1);
}
auto it = parentmap.find(group);
if (it != parentmap.end()) {
parent = it->second;
}
if (!parent) {
QStringList list = group.split('/', QString::SkipEmptyParts);
if (list.isEmpty()) {
list.push_back(tr("Default"));
}
for (QString const &name : list) {
if (name.isEmpty()) continue;
if (!parent) {
auto it = parentmap.find(name);
if (it != parentmap.end()) {
parent = it->second;
} else {
parent = newQTreeWidgetFolderItem(name);
ui->treeWidget_repos->addTopLevelItem(parent);
}
} else {
QTreeWidgetItem *child = newQTreeWidgetFolderItem(name);
parent->addChild(child);
parent = child;
}
parent->setExpanded(true);
}
Q_ASSERT(parent);
parentmap[group] = parent;
}
parent->setData(0, FilePathRole, "");
}
QTreeWidgetItem *child = newQTreeWidgetItem();
child->setText(0, repo.name);
child->setData(0, IndexRole, i);
- child->setIcon(0, m->repository_icon);
+ child->setIcon(0, getRepositoryIcon());
child->setFlags(child->flags() & ~Qt::ItemIsDropEnabled);
parent->addChild(child);
parent->setExpanded(true);
}
}
void MainWindow::showFileList(FilesListType files_list_type)
{
switch (files_list_type) {
case FilesListType::SingleList:
ui->stackedWidget->setCurrentWidget(ui->page_files);
break;
case FilesListType::SideBySide:
ui->stackedWidget->setCurrentWidget(ui->page_uncommited);
break;
}
}
void MainWindow::clearFileList()
{
showFileList(FilesListType::SingleList);
ui->listWidget_unstaged->clear();
ui->listWidget_staged->clear();
ui->listWidget_files->clear();
}
void MainWindow::clearDiffView()
{
ui->widget_diff_view->clearDiffView();
}
void MainWindow::clearRepositoryInfo()
{
- m->head_id = QString();
- m->current_branch = Git::Branch();
- m->server_type = ServerType::Standard;
- m->github = GitHubRepositoryInfo();
+ internalClearRepositoryInfo();
ui->label_repo_name->setText(QString());
ui->label_branch_name->setText(QString());
-
}
void MainWindow::setRepositoryInfo(QString const &reponame, QString const &brname)
{
ui->label_repo_name->setText(reponame);
ui->label_branch_name->setText(brname);
}
-void MainWindow::setUnknownRepositoryInfo()
-{
- setRepositoryInfo("---", "");
-
- Git g(m->gcx, QString());
- Git::User user = g.getUser(Git::Source::Global);
- setWindowTitle_(user);
-}
-
-bool MainWindow::makeDiff(QString id, QList<Git::Diff> *out)
-{
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return false;
-
- Git::FileStatusList list = g->status();
- m->uncommited_changes = !list.empty();
-
- if (id.isEmpty() && !isThereUncommitedChanges()) {
- id = m->objcache.revParse("HEAD");
- }
-
- bool uncommited = (id.isEmpty() && isThereUncommitedChanges());
-
- GitDiff dm(&m->objcache);
- if (uncommited) {
- dm.diff_uncommited(out);
- } else {
- dm.diff(id, out);
- }
-
- return true; // success
-}
-
-void MainWindow::addDiffItems(QList<Git::Diff> const *diff_list, std::function<void(QString const &filename, QString header, int idiff)> const &add_item)
-{
- for (int idiff = 0; idiff < diff_list->size(); idiff++) {
- Git::Diff const &diff = diff_list->at(idiff);
- QString header;
-
- switch (diff.type) {
- case Git::Diff::Type::Modify: header = "(chg) "; break;
- case Git::Diff::Type::Copy: header = "(cpy) "; break;
- case Git::Diff::Type::Rename: header = "(ren) "; break;
- case Git::Diff::Type::Create: header = "(add) "; break;
- case Git::Diff::Type::Delete: header = "(del) "; break;
- case Git::Diff::Type::ChType: header = "(chg) "; break;
- case Git::Diff::Type::Unmerged: header = "(unmerged) "; break;
- }
-
- add_item(diff.path, header, idiff);
- }
-}
-
void MainWindow::updateFilesList(QString id, bool wait)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
if (!wait) return;
clearFileList();
Git::FileStatusList stats = g->status();
- m->uncommited_changes = !stats.empty();
+ setUncommitedChanges(!stats.empty());
FilesListType files_list_type = FilesListType::SingleList;
bool staged = false;
auto AddItem = [&](QString const &filename, QString header, int idiff){
if (header.isEmpty()) {
header = "(??\?) "; // damn trigraph
}
QListWidgetItem *item = new QListWidgetItem(header + filename);
item->setData(FilePathRole, filename);
item->setData(DiffIndexRole, idiff);
item->setData(HunkIndexRole, -1);
switch (files_list_type) {
case FilesListType::SingleList:
ui->listWidget_files->addItem(item);
break;
case FilesListType::SideBySide:
if (staged) {
ui->listWidget_staged->addItem(item);
} else {
ui->listWidget_unstaged->addItem(item);
}
break;
}
};
if (id.isEmpty()) {
bool uncommited = isThereUncommitedChanges();
if (uncommited) {
files_list_type = FilesListType::SideBySide;
}
- if (!makeDiff(uncommited ? QString() : id, &m->diff.result)) {
+ if (!makeDiff(uncommited ? QString() : id, diffResult())) {
return;
}
std::map<QString, int> diffmap;
- for (int idiff = 0; idiff < m->diff.result.size(); idiff++) {
- Git::Diff const &diff = m->diff.result[idiff];
+ for (int idiff = 0; idiff < diffResult()->size(); idiff++) {
+ Git::Diff const &diff = diffResult()->at(idiff);
QString filename = diff.path;
if (!filename.isEmpty()) {
diffmap[filename] = idiff;
}
}
showFileList(files_list_type);
for (Git::FileStatus const &s : stats) {
staged = (s.isStaged() && s.code_y() == ' ');
int idiff = -1;
QString header;
auto it = diffmap.find(s.path1());
if (it != diffmap.end()) {
idiff = it->second;
}
QString path = s.path1();
if (s.code() == Git::FileStatusCode::Unknown) {
qDebug() << "something wrong...";
} else if (s.code() == Git::FileStatusCode::Untracked) {
// nop
} else if (s.isUnmerged()) {
header += "(unmerged) ";
} else if (s.code() == Git::FileStatusCode::AddedToIndex) {
header = "(add) ";
} else if (s.code_x() == 'D' || s.code_y() == 'D' || s.code() == Git::FileStatusCode::DeletedFromIndex) {
header = "(del) ";
} else if (s.code_x() == 'R' || s.code() == Git::FileStatusCode::RenamedInIndex) {
header = "(ren) ";
path = s.path2(); // renamed newer path
} else if (s.code_x() == 'M' || s.code_y() == 'M') {
header = "(chg) ";
}
AddItem(path, header, idiff);
}
} else {
- if (!makeDiff(id, &m->diff.result)) {
+ if (!makeDiff(id, diffResult())) {
return;
}
showFileList(files_list_type);
- addDiffItems(&m->diff.result, AddItem);
+ addDiffItems(diffResult(), AddItem);
}
- for (Git::Diff const &diff : m->diff.result) {
+ for (Git::Diff const &diff : *diffResult()) {
QString key = GitDiff::makeKey(diff);
- m->diff_cache[key] = diff;
+ (*getDiffCacheMap())[key] = diff;
}
}
-void MainWindow::updateFilesList(QString id, QList<Git::Diff> *diff_list, QListWidget *listwidget)
-{
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return;
-
- listwidget->clear();
-
- auto AddItem = [&](QString const &filename, QString header, int idiff){
- if (header.isEmpty()) {
- header = "(??\?) "; // damn trigraph
- }
- QListWidgetItem *item = new QListWidgetItem(header + filename);
- item->setData(FilePathRole, filename);
- item->setData(DiffIndexRole, idiff);
- item->setData(HunkIndexRole, -1);
- listwidget->addItem(item);
- };
-
- GitDiff dm(&m->objcache);
- if (!dm.diff(id, diff_list)) return;
-
- addDiffItems(diff_list, AddItem);
-}
-
void MainWindow::updateFilesList(Git::CommitItem const &commit, bool wait)
{
QString id;
if (Git::isUncommited(commit)) {
// empty id for uncommited changes
} else {
id = commit.commit_id;
}
updateFilesList(id, wait);
}
void MainWindow::updateCurrentFilesList()
{
+ auto logs = getLogs();
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (!item) return;
int row = item->data(IndexRole).toInt();
- int logs = (int)m->logs.size();
- if (row < logs) {
- updateFilesList(m->logs[row], true);
+ int count = (int)logs.size();
+ if (row < count) {
+ updateFilesList(logs[row], true);
}
}
-void MainWindow::setRemoteChanged(bool f)
-{
- m->remote_changed = f;
- updateButton();
-}
-
void MainWindow::prepareLogTableWidget()
{
QStringList cols = {
tr("Graph"),
tr("Commit"),
tr("Date"),
tr("Author"),
tr("Description"),
};
int n = cols.size();
ui->tableWidget_log->setColumnCount(n);
ui->tableWidget_log->setRowCount(0);
for (int i = 0; i < n; i++) {
QString const &text = cols[i];
auto *item = new QTableWidgetItem(text);
ui->tableWidget_log->setHorizontalHeaderItem(i, item);
}
updateCommitGraph(); // コミットグラフを更新
}
-void MainWindow::clearAuthentication()
+void MainWindow::detectGitServerType(GitPtr g)
{
- m->ssh_passphrase_for.clear();
- m->ssh_passphrase_secret.clear();
- m->http_uid.clear();
- m->http_pwd.clear();
-}
+ setServerType(ServerType::Standard);
+ *ptrGitHub() = GitHubRepositoryInfo();
-void MainWindow::setCurrentRepository(RepositoryItem const &repo, bool clear_authentication)
-{
- if (clear_authentication) {
- clearAuthentication();
- }
- m->current_repo = repo;
-}
-
-bool MainWindow::isValidWorkingCopy(GitPtr const &g) const
-{
- return g && g->isValidWorkingCopy();
-}
-
-void MainWindow::queryRemotes(GitPtr g)
-{
- m->remotes = g->getRemotes();
- std::sort(m->remotes.begin(), m->remotes.end());
-}
-
-void MainWindow::queryBranches(GitPtr g)
-{
- Q_ASSERT(g);
- m->branch_map.clear();
- QList<Git::Branch> branches = g->branches();
- for (Git::Branch const &b : branches) {
- if (b.isCurrent()) {
- m->current_branch = b;
- }
- m->branch_map[b.id].append(b);
- }
-}
-
-void MainWindow::updateRemoteInfo()
-{
- queryRemotes(git());
-
- m->current_remote = QString();
- {
- Git::Branch const &r = currentBranch();
- m->current_remote = r.remote;
- }
- if (m->current_remote.isEmpty()) {
- if (m->remotes.size() == 1) {
- m->current_remote = m->remotes[0];
- }
- }
-
- ui->lineEdit_remote->setText(m->current_remote);
-}
-
-QList<Git::Branch> MainWindow::findBranch(QString const &id)
-{
- auto it = m->branch_map.find(id);
- if (it != m->branch_map.end()) {
- return it->second;
- }
- return QList<Git::Branch>();
-}
-
-QString MainWindow::abbrevCommitID(Git::CommitItem const &commit)
-{
- return commit.commit_id.mid(0, 7);
-}
-
-QString MainWindow::findFileID(GitPtr /*g*/, QString const &commit_id, QString const &file)
-{
- return lookupFileID(&m->objcache, commit_id, file);
-}
-
-bool MainWindow::isGitHub() const
-{
- return m->server_type == ServerType::GitHub;
-}
-
-void MainWindow::updateCommitTableLater()
-{
- m->update_commit_table_counter = 200;
-}
-
-void MainWindow::onAvatarUpdated()
-{
- updateCommitTableLater();
-}
-
-bool MainWindow::isAvatarEnabled() const
-{
- return appsettings()->get_committer_icon;
-}
-
-Git::CommitItem const *MainWindow::commitItem(int row) const
-{
- if (row >= 0 && row < (int)m->logs.size()) {
- return &m->logs[row];
- }
- return nullptr;
-}
-
-QIcon MainWindow::verifiedIcon(char s) const
-{
- Git::SignatureGrade g = Git::evaluateSignature(s);
- switch (g) {
- case Git::SignatureGrade::Good:
- return m->signature_good_icon;
- case Git::SignatureGrade::Bad:
- return m->signature_bad_icon;
- case Git::SignatureGrade::Unknown:
- case Git::SignatureGrade::Dubious:
- case Git::SignatureGrade::Missing:
- return m->signature_dubious_icon;
- }
- return QIcon();
-}
-
-QIcon MainWindow::committerIcon(int row) const
-{
- QIcon icon;
- if (isAvatarEnabled()) {
- if (row >= 0 && row < (int)m->logs.size()) {
- Git::CommitItem const &commit = m->logs[row];
- if (commit.email.indexOf('@') > 0) {
- std::string email = commit.email.toStdString();
- icon = m->avatar_loader.fetch(email, true); // from gavatar
- }
- }
- }
- return icon;
-}
-
-QList<MainWindow::Label> const *MainWindow::label(int row)
-{
- auto it = m->label_map.find(row);
- if (it != m->label_map.end()) {
- return &it->second;
- }
- return nullptr;
-}
-
-QString MainWindow::makeCommitInfoText(int row, QList<Label> *label_list)
-{
- QString message_ex;
- Git::CommitItem const *commit = &m->logs[row];
- { // branch
- if (label_list) {
- if (commit->commit_id == m->head_id) {
- Label label(Label::Head);
- label.text = "HEAD";
- label_list->push_back(label);
- }
- }
- QList<Git::Branch> list = findBranch(commit->commit_id);
- for (Git::Branch const &b : list) {
- if (b.flags & Git::Branch::HeadDetachedAt) continue;
- if (b.flags & Git::Branch::HeadDetachedFrom) continue;
- Label label(Label::LocalBranch);
- label.text = b.name;
- if (!b.remote.isEmpty()) {
- label.kind = Label::RemoteBranch;
- label.text = "remotes" / b.remote / label.text;
- }
- if (b.ahead > 0) {
- label.text += tr(", %1 ahead").arg(b.ahead);
- }
- if (b.behind > 0) {
- label.text += tr(", %1 behind").arg(b.behind);
- }
- message_ex += " {" + label.text + '}';
- if (label_list) label_list->push_back(label);
- }
- }
- { // tag
- QList<Git::Tag> list = findTag(commit->commit_id);
- for (Git::Tag const &t : list) {
- Label label(Label::Tag);
- label.text = t.name;
- message_ex += QString(" {#%1}").arg(label.text);
- if (label_list) label_list->push_back(label);
- }
- }
- return message_ex;
-}
-
-void MainWindow::setWindowTitle_(Git::User const &user)
-{
- if (user.name.isEmpty() && user.email.isEmpty()) {
- setWindowTitle(qApp->applicationName());
- } else {
- setWindowTitle(QString("%1 : %2 <%3>")
- .arg(qApp->applicationName())
- .arg(user.name)
- .arg(user.email)
- );
- }
-}
-
-void MainWindow::updateWindowTitle(GitPtr g)
-{
- if (isValidWorkingCopy(g)) {
- Git::User user = g->getUser(Git::Source::Default);
- setWindowTitle_(user);
- } else {
- setUnknownRepositoryInfo();
- }
-}
-
-QStringList MainWindow::remotes() const
-{
- return m->remotes;
-}
-
-int MainWindow::limitLogCount() const
-{
- int n = appsettings()->maximum_number_of_commit_item_acquisitions;
- return (n >= 1 && n <= 100000) ? n : 10000;
-}
-
-struct TemporaryCommitItem {
- Git::CommitItem const *commit;
- std::vector<TemporaryCommitItem *> children;
-};
-
-Git::CommitItemList MainWindow::retrieveCommitLog(GitPtr g)
-{
- Git::CommitItemList list = g->log(limitLogCount());
-
- // 親子関係を調べて、順番が狂っていたら、修正する。
-
- std::set<QString> set;
-
- size_t i = 0;
- while (i < list.size()) {
- size_t newpos = -1;
- for (QString const &parent : list[i].parent_ids) {
- auto it = set.find(parent);
- if (it != set.end()) {
- for (size_t j = 0; j < i; j++) {
- if (parent == list[j].commit_id) {
- if (newpos == (size_t)-1 || j < newpos) {
- newpos = j;
- }
- qDebug() << "fix commit order" << parent;
- break;
- }
- }
- }
- }
- set.insert(set.end(), list[i].commit_id);
- if (newpos != (size_t)-1) {
- Git::CommitItem t = list[newpos];
- list.erase(list.begin() + newpos);
- list.insert(list.begin() + i, t);
- }
- i++;
- }
-
- return list;
-}
-
-void MainWindow::detectGitServerType(GitPtr g)
-{
- m->server_type = ServerType::Standard;
- m->github = GitHubRepositoryInfo();
-
- QString push_url;
- QList<Git::Remote> remotes;
- g->getRemoteURLs(&remotes);
- for (Git::Remote const &r : remotes) {
- if (r.purpose == "push") {
- push_url = r.url;
- }
+ QString push_url;
+ QList<Git::Remote> remotes;
+ g->getRemoteURLs(&remotes);
+ for (Git::Remote const &r : remotes) {
+ if (r.purpose == "push") {
+ push_url = r.url;
+ }
}
auto Check = [&](QString const &s){
int i = push_url.indexOf(s);
if (i > 0) return i + s.size();
return 0;
};
// check GitHub
int pos = Check("@github.com:");
if (pos == 0) {
pos = Check("://github.com/");
}
if (pos > 0) {
int end = push_url.size();
{
QString s = ".git";
if (push_url.endsWith(s)) {
end -= s.size();
}
}
QString s = push_url.mid(pos, end - pos);
int i = s.indexOf('/');
if (i > 0) {
+ auto *p = ptrGitHub();
QString user = s.mid(0, i);
QString repo = s.mid(i + 1);
- m->github.owner_account_name = user;
- m->github.repository_name = repo;
+ p->owner_account_name = user;
+ p->repository_name = repo;
}
- m->server_type = ServerType::GitHub;
+ setServerType(ServerType::GitHub);
}
}
bool MainWindow::fetch(GitPtr g)
{
- m->pty_condition = PtyCondition::Fetch;
- m->pty_process_ok = true;
- g->fetch(&m->pty_process);
+ setPtyCondition(PtyCondition::Fetch);
+ setPtyProcessOk(true);
+ g->fetch(getPtyProcess());
while (1) {
- if (m->pty_process.wait(1)) break;
+ if (getPtyProcess()->wait(1)) break;
QApplication::processEvents();
}
- return m->pty_process_ok;
+ return getPtyProcessOk();
}
void MainWindow::clearLog()
{
- m->logs.clear();
- m->label_map.clear();
- m->uncommited_changes = false;
+ clearLogs();
+ clearLabelMap();
+ setUncommitedChanges(false);
ui->tableWidget_log->clearContents();
ui->tableWidget_log->scrollToTop();
}
void MainWindow::openRepository_(GitPtr g)
{
- m->objcache.setup(g);
+ getObjCache()->setup(g);
if (isValidWorkingCopy(g)) {
- bool do_fetch = isRemoteOnline() && (m->force_fetch || appsettings()->automatically_fetch_when_opening_the_repository);
- m->force_fetch = false;
+ bool do_fetch = isRemoteOnline() && (getForceFetch() || appsettings()->automatically_fetch_when_opening_the_repository);
+ setForceFetch(false);
if (do_fetch) {
if (!fetch(g)) {
return;
}
}
clearLog();
clearRepositoryInfo();
detectGitServerType(g);
updateFilesList(QString(), true);
bool canceled = false;
ui->tableWidget_log->setEnabled(false);
// ログを取得
- m->logs = retrieveCommitLog(g);
+ setLogs(retrieveCommitLog(g));
// ブランチを取得
queryBranches(g);
// タグを取得
- m->tag_map.clear();
+ ptrTagMap()->clear();
QList<Git::Tag> tags = g->tags();
for (Git::Tag const &tag : tags) {
Git::Tag t = tag;
- t.id = m->objcache.getCommitIdFromTag(t.id);
- m->tag_map[t.id].push_back(t);
+ t.id = getObjCache()->getCommitIdFromTag(t.id);
+ (*ptrTagMap())[t.id].push_back(t);
}
ui->tableWidget_log->setEnabled(true);
updateCommitTableLater();
if (canceled) return;
QString branch_name;
if (currentBranch().flags & Git::Branch::HeadDetachedAt) {
branch_name += QString("(HEAD detached at %1)").arg(currentBranchName());
}
if (currentBranch().flags & Git::Branch::HeadDetachedFrom) {
branch_name += QString("(HEAD detached from %1)").arg(currentBranchName());
}
if (branch_name.isEmpty()) {
branch_name = currentBranchName();
}
QString repo_name = currentRepositoryName();
setRepositoryInfo(repo_name, branch_name);
} else {
clearLog();
clearRepositoryInfo();
}
+
updateRemoteInfo();
updateWindowTitle(g);
- m->head_id = m->objcache.revParse("HEAD");
+ setHeadId(getObjCache()->revParse("HEAD"));
if (isThereUncommitedChanges()) {
Git::CommitItem item;
- item.parent_ids.push_back(m->current_branch.id);
+ item.parent_ids.push_back(currentBranch().id);
item.message = tr("Uncommited changes");
- m->logs.insert(m->logs.begin(), item);
+ auto p = getLogsPtr();
+ p->insert(p->begin(), item);
}
prepareLogTableWidget();
- int count = m->logs.size();
+ auto const &logs = getLogs();
+ const int count = logs.size();
ui->tableWidget_log->setRowCount(count);
int selrow = -1;
for (int row = 0; row < count; row++) {
- Git::CommitItem const *commit = &m->logs[row];
+ Git::CommitItem const *commit = &logs[row];
{
auto *item = new QTableWidgetItem;
item->setData(IndexRole, row);
ui->tableWidget_log->setItem(row, 0, item);
}
int col = 1; // カラム0はコミットグラフなので、その次から
auto AddColumn = [&](QString const &text, bool bold, QString const &tooltip){
auto *item = new QTableWidgetItem(text);
if (!tooltip.isEmpty()) {
QString tt = tooltip;
tt.replace('\n', ' ');
tt = tt.toHtmlEscaped();
tt = "<p style='white-space: pre'>" + tt + "</p>";
item->setToolTip(tt);
}
if (bold) {
QFont font = item->font();
font.setBold(true);
item->setFont(font);
}
ui->tableWidget_log->setItem(row, col, item);
col++;
};
QString commit_id;
QString datetime;
QString author;
QString message;
QString message_ex;
- bool isHEAD = commit->commit_id == m->head_id;
+ bool isHEAD = (commit->commit_id == getHeadId());
bool bold = false;
{
if (Git::isUncommited(*commit)) { // 未コミットの時
bold = true; // 太字
selrow = row;
} else {
if (isHEAD && !isThereUncommitedChanges()) { // HEADで、未コミットがないとき
bold = true; // 太字
selrow = row;
}
commit_id = abbrevCommitID(*commit);
}
datetime = misc::makeDateTimeString(commit->commit_date);
author = commit->author;
message = commit->message;
- message_ex = makeCommitInfoText(row, &m->label_map[row]);
+ message_ex = makeCommitInfoText(row, &(*getLabelMap())[row]);
}
AddColumn(commit_id, false, QString());
AddColumn(datetime, false, QString());
AddColumn(author, false, QString());
AddColumn(message, bold, message + message_ex);
ui->tableWidget_log->setRowHeight(row, 24);
}
ui->tableWidget_log->resizeColumnsToContents();
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(false);
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(true);
m->last_focused_file_list = nullptr;
ui->tableWidget_log->setFocus();
setCurrentLogRow(0);
QTableWidgetItem *p = ui->tableWidget_log->item(selrow < 0 ? 0 : selrow, 2);
ui->tableWidget_log->setCurrentItem(p);
m->remote_watcher.setCurrent(currentRemoteName(), currentBranchName());
checkRemoteUpdate();
doUpdateButton();
}
-void MainWindow::removeRepositoryFromBookmark(int index, bool ask)
-{
- if (ask) {
- int r = QMessageBox::warning(this, tr("Confirm Remove"), tr("Are you sure you want to remove the repository from bookmarks ?") + '\n' + tr("(Files will NOT be deleted)"), QMessageBox::Ok, QMessageBox::Cancel);
- if (r != QMessageBox::Ok) return;
- }
- if (index >= 0 && index < m->repos.size()) {
- m->repos.erase(m->repos.begin() + index);
- saveRepositoryBookmarks();
- updateRepositoriesList();
- }
-}
-
void MainWindow::removeSelectedRepositoryFromBookmark(bool ask)
{
int i = indexOfRepository(ui->treeWidget_repos->currentItem());
removeRepositoryFromBookmark(i, ask);
}
-void MainWindow::openRepository(bool validate, bool waitcursor)
-{
- if (validate) {
- QString dir = currentWorkingCopyDir();
- if (!QFileInfo(dir).isDir()) {
- int r = QMessageBox::warning(this, tr("Open Repository"), dir + "\n\n" + tr("No such folder") + "\n\n" + tr("Remove from bookmark ?"), QMessageBox::Ok, QMessageBox::Cancel);
- if (r == QMessageBox::Ok) {
- removeSelectedRepositoryFromBookmark(false);
- }
- return;
- }
- if (!Git::isValidWorkingCopy(dir)) {
- QMessageBox::warning(this, tr("Open Repository"), tr("Not a valid git repository") + "\n\n" + dir);
- return;
- }
- }
-
- if (waitcursor) {
- OverrideWaitCursor;
- openRepository(false, false);
- return;
- }
-
- GitPtr g = git(); // ポインタの有効性チェックはしない(nullptrでも続行)
- openRepository_(g);
-}
-
-void MainWindow::reopenRepository(bool log, std::function<void(GitPtr g)> callback)
-{
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return;
-
- OverrideWaitCursor;
- if (log) {
- setLogEnabled(g, true);
- AsyncExecGitThread_ th(g, callback);
- th.start();
- while (1) {
- if (th.wait(1)) break;
- QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
- }
- setLogEnabled(g, false);
- } else {
- callback(g);
- }
- openRepository_(g);
-}
-
void MainWindow::doUpdateButton()
{
setNetworkingCommandsEnabled(isRemoteOnline());
- Git::Branch b = currentBranch();
-
- int n;
-
- ui->toolButton_fetch->setDot(m->remote_changed);
-
- n = b.ahead > 0 ? b.ahead : -1;
- ui->toolButton_push->setNumber(n);
-
- n = b.behind > 0 ? b.behind : -1;
- ui->toolButton_pull->setNumber(n);
-}
-
-void MainWindow::autoOpenRepository(QString dir)
-{
- auto Open = [&](RepositoryItem const &item){
- setCurrentRepository(item, true);
- openRepository(false, true);
- };
-
- RepositoryItem const *repo = findRegisteredRepository(&dir);
- if (repo) {
- Open(*repo);
- return;
- }
-
- RepositoryItem newitem;
- GitPtr g = git(dir);
- if (isValidWorkingCopy(g)) {
- ushort const *left = dir.utf16();
- ushort const *right = left + dir.size();
- if (right[-1] == '/' || right[-1] == '\\') {
- right--;
- }
- ushort const *p = right;
- while (left + 1 < p && !(p[-1] == '/' || p[-1] == '\\')) p--;
- if (p < right) {
- newitem.local_dir = dir;
- newitem.name = QString::fromUtf16(p, right - p);
- saveRepositoryBookmark(newitem);
- Open(newitem);
- return;
- }
- } else {
- DoYouWantToInitDialog dlg(this, dir);
- if (dlg.exec() == QDialog::Accepted) {
- createRepository(dir);
- }
- }
-}
-
-void MainWindow::openSelectedRepository()
-{
- QTreeWidgetItem *treeitem = ui->treeWidget_repos->currentItem();
- RepositoryItem const *item = repositoryItem(treeitem);
- if (item) {
- setCurrentRepository(*item, true);
- openRepository(true);
- }
-}
-
-QString MainWindow::getBookmarksFilePath() const
-{
- return global->app_config_dir / "bookmarks.xml";
-}
-
-
-
-void MainWindow::commit(bool amend)
-{
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return;
-
- while (1) {
- Git::User user = g->getUser(Git::Source::Default);
- QString sign_id = g->signingKey(Git::Source::Default);
- gpg::Data key;
- {
- QList<gpg::Data> keys;
- gpg::listKeys(global->gpg_command, &keys);
- for (gpg::Data const &k : keys) {
- if (k.id == sign_id) {
- key = k;
- }
- }
- }
- CommitDialog dlg(this, currentRepositoryName(), user, key);
- if (amend) {
- dlg.setText(m->logs[0].message);
- }
- if (dlg.exec() == QDialog::Accepted) {
- QString text = dlg.text();
- if (text.isEmpty()) {
- QMessageBox::warning(this, tr("Commit"), tr("Commit message can not be omitted."));
- continue;
- }
- bool sign = dlg.isSigningEnabled();
- bool ok;
- if (amend) {
- ok = g->commit_amend_m(text, sign, &m->pty_process);
- } else {
- ok = g->commit(text, sign, &m->pty_process);
- }
- if (ok) {
- m->force_fetch = true;
- openRepository(true);
- } else {
- QString err = g->errorMessage().trimmed();
- err += "\n*** ";
- err += tr("Failed to commit");
- err += " ***\n";
- writeLog(err);
- }
- }
- break;
- }
-}
+ ui->toolButton_fetch->setDot(getRemoteChanged());
-void MainWindow::commit_amend()
-{
- commit(true);
+ Git::Branch b = currentBranch();
+ ui->toolButton_push->setNumber(b.ahead > 0 ? b.ahead : -1);
+ ui->toolButton_pull->setNumber(b.behind > 0 ? b.behind : -1);
}
-
void MainWindow::updateStatusBarText()
{
QString text;
QWidget *w = qApp->focusWidget();
if (w == ui->treeWidget_repos) {
- QTreeWidgetItem *ite = ui->treeWidget_repos->currentItem();
- RepositoryItem const *item = repositoryItem(ite);
- if (item) {
+ RepositoryItem const *repo = selectedRepositoryItem();
+ if (repo) {
text = QString("%1 : %2")
- .arg(item->name)
- .arg(misc::normalizePathSeparator(item->local_dir))
+ .arg(repo->name)
+ .arg(misc::normalizePathSeparator(repo->local_dir))
;
}
} else if (w == ui->tableWidget_log) {
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (item) {
+ auto const &logs = getLogs();
int row = item->data(IndexRole).toInt();
- if (row < (int)m->logs.size()) {
- Git::CommitItem const &commit = m->logs[row];
+ if (row < (int)logs.size()) {
+ Git::CommitItem const &commit = logs[row];
if (Git::isUncommited(commit)) {
text = tr("Uncommited changes");
} else {
QString id = commit.commit_id;
text = QString("%1 : %2%3")
.arg(id.mid(0, 7))
.arg(commit.message)
.arg(makeCommitInfoText(row, nullptr))
;
}
}
}
}
setStatusBarText(text);
}
-void MainWindow::execRepositoryPropertyDialog(QString workdir, bool open_repository_menu)
-{
- if (workdir.isEmpty()) {
- workdir = currentWorkingCopyDir();
- }
- QString name;
- RepositoryItem const *repo = findRegisteredRepository(&workdir);
- if (repo) {
- name = repo->name;
- } else {
- QMessageBox::warning(this, tr("Repository Property"), tr("Not a valid git repository") + "\n\n" + workdir);
- return;
- }
- if (name.isEmpty()) {
- name = makeRepositoryName(workdir);
- }
- GitPtr g = git(workdir);
- RepositoryPropertyDialog dlg(this, g, *repo, open_repository_menu);
- dlg.exec();
- if (dlg.isRemoteChanged()) {
- updateRemoteInfo();
- }
-}
-
-
-
void MainWindow::on_action_commit_triggered()
{
commit();
}
void MainWindow::on_action_fetch_triggered()
{
if (!isRemoteOnline()) return;
reopenRepository(true, [&](GitPtr g){
fetch(g);
});
}
void MainWindow::on_action_push_triggered()
{
- if (!isRemoteOnline()) return;
-
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return;
-
- if (g->getRemotes().isEmpty()) {
- QMessageBox::warning(this, qApp->applicationName(), tr("No remote repository is registered."));
- execRepositoryPropertyDialog(QString(), true);
- return;
- }
-
- int exitcode = 0;
- QString errormsg;
-
- reopenRepository(true, [&](GitPtr g){
- g->push(false, &m->pty_process);
- while (1) {
- if (m->pty_process.wait(1)) break;
- QApplication::processEvents();
- }
- exitcode = m->pty_process.getExitCode();
- errormsg = m->pty_process.getMessage();
- });
-
- if (exitcode == 128) {
- if (errormsg.indexOf("no upstream branch") >= 0) {
- QString brname = currentBranchName();
-
- QString msg = tr("The current branch %1 has no upstream branch.");
- msg = msg.arg(brname);
- msg += '\n';
- msg += tr("You try push --set-upstream");
- QMessageBox::warning(this, qApp->applicationName(), msg);
- pushSetUpstream(false);
- return;
- }
- if (errormsg.indexOf("Connection refused") >= 0) {
- QMessageBox::critical(this, qApp->applicationName(), tr("Connection refused."));
- return;
- }
- }
+ push();
}
void MainWindow::on_action_pull_triggered()
{
if (!isRemoteOnline()) return;
reopenRepository(true, [&](GitPtr g){
- m->pty_condition = PtyCondition::Pull;
- m->pty_process_ok = true;
- g->pull(&m->pty_process);
+ setPtyCondition(PtyCondition::Pull);
+ setPtyProcessOk(true);
+ g->pull(getPtyProcess());
while (1) {
- if (m->pty_process.wait(1)) break;
+ if (getPtyProcess()->wait(1)) break;
QApplication::processEvents();
}
});
}
void MainWindow::on_toolButton_push_clicked()
{
ui->action_push->trigger();
}
void MainWindow::on_toolButton_pull_clicked()
{
ui->action_pull->trigger();
}
void MainWindow::on_treeWidget_repos_currentItemChanged(QTreeWidgetItem * /*current*/, QTreeWidgetItem * /*previous*/)
{
updateStatusBarText();
}
void MainWindow::on_treeWidget_repos_itemDoubleClicked(QTreeWidgetItem * /*item*/, int /*column*/)
{
openSelectedRepository();
}
-void MainWindow::execCommitPropertyDialog(QWidget *parent, Git::CommitItem const *commit)
+void BasicMainWindow::execCommitPropertyDialog(QWidget *parent, Git::CommitItem const *commit)
{
CommitPropertyDialog dlg(parent, this, commit);
dlg.exec();
}
-QAction *MainWindow::addMenuActionProperty(QMenu *menu)
-{
- return menu->addAction(tr("&Property"));
-}
-
int MainWindow::indexOfRepository(QTreeWidgetItem const *treeitem) const
{
if (!treeitem) return -1;
return treeitem->data(0, IndexRole).toInt();
}
void MainWindow::on_treeWidget_repos_customContextMenuRequested(const QPoint &pos)
{
QTreeWidgetItem *treeitem = ui->treeWidget_repos->currentItem();
if (!treeitem) return;
RepositoryItem const *repo = repositoryItem(treeitem);
int index = indexOfRepository(treeitem);
if (isGroupItem(treeitem)) { // group item
QMenu menu;
QAction *a_add_new_group = menu.addAction(tr("&Add new group"));
QAction *a_delete_group = menu.addAction(tr("&Delete group"));
QAction *a_rename_group = menu.addAction(tr("&Rename group"));
QPoint pt = ui->treeWidget_repos->mapToGlobal(pos);
QAction *a = menu.exec(pt + QPoint(8, -8));
if (a) {
if (a == a_add_new_group) {
QTreeWidgetItem *child = newQTreeWidgetFolderItem(tr("New group"));
treeitem->addChild(child);
child->setFlags(child->flags() | Qt::ItemIsEditable);
ui->treeWidget_repos->setCurrentItem(child);
return;
}
if (a == a_delete_group) {
QTreeWidgetItem *parent = treeitem->parent();
if (parent) {
int i = parent->indexOfChild(treeitem);
delete parent->takeChild(i);
} else {
int i = ui->treeWidget_repos->indexOfTopLevelItem(treeitem);
delete ui->treeWidget_repos->takeTopLevelItem(i);
}
refrectRepositories();
return;
}
if (a == a_rename_group) {
ui->treeWidget_repos->editItem(treeitem);
return;
}
}
} else if (repo) { // repository item
QString open_terminal = tr("Open &terminal");
QString open_commandprompt = tr("Open command promp&t");
QMenu menu;
QAction *a_open = menu.addAction(tr("&Open"));
menu.addSeparator();
#ifdef Q_OS_WIN
QAction *a_open_terminal = menu.addAction(open_commandprompt);
(void)open_terminal;
#else
QAction *a_open_terminal = menu.addAction(open_terminal);
(void)open_commandprompt;
#endif
QAction *a_open_folder = menu.addAction(tr("Open &folder"));
menu.addSeparator();
QAction *a_remove = menu.addAction(tr("&Remove"));
menu.addSeparator();
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->treeWidget_repos->mapToGlobal(pos);
QAction *a = menu.exec(pt + QPoint(8, -8));
if (a) {
if (a == a_open) {
openSelectedRepository();
return;
}
if (a == a_open_folder) {
openExplorer(repo);
return;
}
if (a == a_open_terminal) {
openTerminal(repo);
return;
}
if (a == a_remove) {
removeRepositoryFromBookmark(index, true);
return;
}
if (a == a_properties) {
execRepositoryPropertyDialog(repo->local_dir);
return;
}
}
}
}
-void MainWindow::execCommitExploreWindow(QWidget *parent, Git::CommitItem const *commit)
-{
- CommitExploreWindow win(parent, this, &m->objcache, commit);
- win.exec();
-}
-
void MainWindow::on_tableWidget_log_customContextMenuRequested(const QPoint &pos)
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
bool is_valid_commit_id = Git::isValidID(commit->commit_id);
int row = selectedLogIndex();
QMenu menu;
QAction *a_copy_id_7_letters = menu.addAction(tr("Copy commit id (7 letters)"));
QAction *a_copy_id_complete = menu.addAction(tr("Copy commit id (completely)"));
menu.addSeparator();
- QAction *a_checkout = is_valid_commit_id ? menu.addAction(tr("Checkout/Branch...")) : nullptr;
+ QAction *a_checkout = menu.addAction(tr("Checkout/Branch..."));
menu.addSeparator();
QAction *a_edit_comment = nullptr;
if (row == 0 && currentBranch().ahead > 0) {
a_edit_comment = menu.addAction(tr("Edit comment..."));
}
QAction *a_merge = is_valid_commit_id ? menu.addAction(tr("Merge")) : nullptr;
QAction *a_rebase = is_valid_commit_id ? menu.addAction(tr("Rebase")) : nullptr;
QAction *a_cherrypick = is_valid_commit_id ? menu.addAction(tr("Cherry-pick")) : nullptr;
QAction *a_edit_tags = is_valid_commit_id ? menu.addAction(tr("Edit tags...")) : nullptr;
QAction *a_revert = is_valid_commit_id ? menu.addAction(tr("Revert")) : nullptr;
QAction *a_reset_head = nullptr;
#if 0 // 下手に使うと危険なので、とりあえず無効にしておく
- if (is_valid_commit_id && commit->commit_id == m->head_id) {
+ if (is_valid_commit_id && commit->commit_id == head_id_) {
a_reset_head = menu.addAction(tr("Reset HEAD"));
}
#endif
menu.addSeparator();
QAction *a_delbranch = is_valid_commit_id ? menu.addAction(tr("Delete branch...")) : nullptr;
QAction *a_delrembranch = remoteBranches(commit->commit_id).isEmpty() ? nullptr : menu.addAction(tr("Delete remote branch..."));
menu.addSeparator();
QAction *a_explore = is_valid_commit_id ? menu.addAction(tr("Explore")) : nullptr;
QAction *a_properties = addMenuActionProperty(&menu);
QAction *a = menu.exec(ui->tableWidget_log->viewport()->mapToGlobal(pos) + QPoint(8, -8));
if (a) {
if (a == a_copy_id_7_letters) {
qApp->clipboard()->setText(commit->commit_id.mid(0, 7));
return;
}
if (a == a_copy_id_complete) {
qApp->clipboard()->setText(commit->commit_id);
return;
}
if (a == a_properties) {
execCommitPropertyDialog(this, commit);
return;
}
if (a == a_edit_comment) {
- commit_amend();
+ commitAmend();
return;
}
if (a == a_checkout) {
checkout(this, commit);
return;
}
if (a == a_delbranch) {
deleteBranch(commit);
return;
}
if (a == a_delrembranch) {
deleteRemoteBranch(commit);
return;
}
if (a == a_merge) {
mergeBranch(commit);
return;
}
if (a == a_rebase) {
rebaseBranch(commit);
return;
}
if (a == a_cherrypick) {
cherrypick(commit);
return;
}
if (a == a_edit_tags) {
ui->action_edit_tags->trigger();
return;
}
if (a == a_revert) {
revertCommit();
return;
}
if (a == a_explore) {
execCommitExploreWindow(this, commit);
return;
}
if (a == a_reset_head) {
reopenRepository(false, [](GitPtr g){
g->reset_head1();
});
return;
}
}
}
}
void MainWindow::on_listWidget_files_customContextMenuRequested(const QPoint &pos)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QMenu menu;
QAction *a_delete = menu.addAction(tr("Delete"));
QAction *a_untrack = menu.addAction(tr("Untrack"));
QAction *a_history = menu.addAction(tr("History"));
QAction *a_blame = menu.addAction(tr("Blame"));
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->listWidget_files->mapToGlobal(pos) + QPoint(8, -8);
QAction *a = menu.exec(pt);
if (a) {
QListWidgetItem *item = ui->listWidget_files->currentItem();
if (a == a_delete) {
if (askAreYouSureYouWantToRun("Delete", tr("Delete selected files."))) {
for_each_selected_files([&](QString const &path){
g->removeFile(path);
g->chdirexec([&](){
QFile(path).remove();
return true;
});
});
openRepository(false);
}
} else if (a == a_untrack) {
if (askAreYouSureYouWantToRun("Untrack", tr("rm --cached files"))) {
for_each_selected_files([&](QString const &path){
g->rm_cached(path);
});
openRepository(false);
}
} else if (a == a_history) {
execFileHistory(item);
} else if (a == a_blame) {
blame(item);
} else if (a == a_properties) {
execFilePropertyDialog(item);
}
}
}
void MainWindow::on_listWidget_unstaged_customContextMenuRequested(const QPoint &pos)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QList<QListWidgetItem *> items = ui->listWidget_unstaged->selectedItems();
if (!items.isEmpty()) {
QMenu menu;
QAction *a_stage = menu.addAction(tr("Stage"));
QAction *a_reset_file = menu.addAction(tr("Reset"));
QAction *a_ignore = menu.addAction(tr("Ignore"));
QAction *a_delete = menu.addAction(tr("Delete"));
QAction *a_untrack = menu.addAction(tr("Untrack"));
QAction *a_history = menu.addAction(tr("History"));
QAction *a_blame = menu.addAction(tr("Blame"));
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->listWidget_unstaged->mapToGlobal(pos) + QPoint(8, -8);
QAction *a = menu.exec(pt);
if (a) {
QListWidgetItem *item = ui->listWidget_unstaged->currentItem();
if (a == a_stage) {
for_each_selected_files([&](QString const &path){
g->stage(path);
});
updateCurrentFilesList();
return;
}
if (a == a_reset_file) {
QStringList paths;
for_each_selected_files([&](QString const &path){
paths.push_back(path);
});
resetFile(paths);
return;
}
if (a == a_ignore) {
QString gitignore_path = currentWorkingCopyDir() / ".gitignore";
if (items.size() == 1) {
QString file = getFilePath(items[0]);
EditGitIgnoreDialog dlg(this, gitignore_path, file);
if (dlg.exec() == QDialog::Accepted) {
QString appending = dlg.text();
if (!appending.isEmpty()) {
QString text;
QString path = gitignore_path;
path.replace('/', QDir::separator());
{
QFile file(path);
if (file.open(QFile::ReadOnly)) {
text += QString::fromUtf8(file.readAll());
}
}
size_t n = text.size();
if (n > 0 && text[(int)n - 1] != '\n') {
text += '\n'; // 最後に改行を追加
}
text += appending + '\n';
{
QFile file(path);
if (file.open(QFile::WriteOnly)) {
file.write(text.toUtf8());
}
}
updateCurrentFilesList();
return;
}
} else {
return;
}
}
QString append;
for_each_selected_files([&](QString const &path){
if (path == ".gitignore") {
// skip
} else {
append += path + '\n';
}
});
if (TextEditDialog::editFile(this, gitignore_path, ".gitignore", append)) {
updateCurrentFilesList();
}
return;
}
if (a == a_delete) {
if (askAreYouSureYouWantToRun("Delete", "Delete selected files.")) {
for_each_selected_files([&](QString const &path){
g->removeFile(path);
g->chdirexec([&](){
QFile(path).remove();
return true;
});
});
openRepository(false);
}
return;
}
if (a == a_untrack) {
if (askAreYouSureYouWantToRun("Untrack", "rm --cached")) {
for_each_selected_files([&](QString const &path){
g->rm_cached(path);
});
openRepository(false);
}
return;
}
if (a == a_history) {
execFileHistory(item);
return;
}
if (a == a_blame) {
blame(item);
return;
}
if (a == a_properties) {
execFilePropertyDialog(item);
return;
}
}
}
}
void MainWindow::on_listWidget_staged_customContextMenuRequested(const QPoint &pos)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QListWidgetItem *item = ui->listWidget_staged->currentItem();
if (item) {
QString path = getFilePath(item);
QString fullpath = currentWorkingCopyDir() / path;
if (QFileInfo(fullpath).isFile()) {
QMenu menu;
QAction *a_unstage = menu.addAction(tr("Unstage"));
QAction *a_history = menu.addAction(tr("History"));
QAction *a_blame = menu.addAction(tr("Blame"));
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->listWidget_staged->mapToGlobal(pos) + QPoint(8, -8);
QAction *a = menu.exec(pt);
if (a) {
QListWidgetItem *item = ui->listWidget_unstaged->currentItem();
if (a == a_unstage) {
g->unstage(path);
openRepository(false);
} else if (a == a_history) {
execFileHistory(item);
} else if (a == a_blame) {
blame(item);
} else if (a == a_properties) {
execFilePropertyDialog(item);
}
}
}
}
}
-bool MainWindow::askAreYouSureYouWantToRun(QString const &title, QString const &command)
+QStringList MainWindow::selectedFiles_(QListWidget *listwidget) const
{
- QString message = tr("Are you sure you want to run the following command ?");
- QString text = "%1\n\n%2";
- text = text.arg(message).arg(command);
- return QMessageBox::warning(this, title, text, QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok;
+ QStringList list;
+ QList<QListWidgetItem *> items = listwidget->selectedItems();
+ for (QListWidgetItem *item : items) {
+ QString path = getFilePath(item);
+ list.push_back(path);
+ }
+ return list;
+
}
-void MainWindow::resetFile(QStringList const &paths)
+QStringList MainWindow::selectedFiles() const
{
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return;
-
- if (paths.isEmpty()) {
- // nop
- } else {
- QString cmd = "git checkout -- \"%1\"";
- cmd = cmd.arg(paths[0]);
- if (askAreYouSureYouWantToRun(tr("Reset a file"), "> " + cmd)) {
- for (QString const &path : paths) {
- g->resetFile(path);
- }
- openRepository(true);
- }
- }
-}
-
-void MainWindow::revertAllFiles()
-{
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return;
-
- QString cmd = "git reset --hard HEAD";
- if (askAreYouSureYouWantToRun(tr("Revert all files"), "> " + cmd)) {
- g->resetAllFiles();
- openRepository(true);
- }
-}
-
-QStringList MainWindow::selectedFiles_(QListWidget *listwidget) const
-{
- QStringList list;
- QList<QListWidgetItem *> items = listwidget->selectedItems();
- for (QListWidgetItem *item : items) {
- QString path = getFilePath(item);
- list.push_back(path);
- }
- return list;
-
-}
-
-QStringList MainWindow::selectedFiles() const
-{
- if (m->last_focused_file_list == ui->listWidget_files) return selectedFiles_(ui->listWidget_files);
- if (m->last_focused_file_list == ui->listWidget_staged) return selectedFiles_(ui->listWidget_staged);
- if (m->last_focused_file_list == ui->listWidget_unstaged) return selectedFiles_(ui->listWidget_unstaged);
- return QStringList();
-}
+ if (m->last_focused_file_list == ui->listWidget_files) return selectedFiles_(ui->listWidget_files);
+ if (m->last_focused_file_list == ui->listWidget_staged) return selectedFiles_(ui->listWidget_staged);
+ if (m->last_focused_file_list == ui->listWidget_unstaged) return selectedFiles_(ui->listWidget_unstaged);
+ return QStringList();
+}
void MainWindow::for_each_selected_files(std::function<void(QString const&)> fn)
{
for (QString path : selectedFiles()) {
fn(path);
}
}
-void MainWindow::execFileHistory(QString const &path)
-{
- if (path.isEmpty()) return;
-
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return;
-
- FileHistoryWindow dlg(this);
- dlg.prepare(g, path);
- dlg.exec();
-}
-
-void MainWindow::execFileHistory(QListWidgetItem *item)
+void BasicMainWindow::execFileHistory(QListWidgetItem *item)
{
if (item) {
QString path = getFilePath(item);
if (!path.isEmpty()) {
execFileHistory(path);
}
}
}
-void MainWindow::addWorkingCopyDir(QString dir, QString name, bool open)
-{
- if (dir.endsWith(".git")) {
- int i = dir.size();
- if (i > 4) {
- ushort c = dir.utf16()[i - 5];
- if (c == '/' || c == '\\') {
- dir = dir.mid(0, i - 5);
- }
- }
- }
-
- if (!Git::isValidWorkingCopy(dir)) {
- qDebug() << "Invalid working dir: " + dir;
- return;
- }
-
- if (name.isEmpty()) {
- name = makeRepositoryName(dir);
- }
-
- RepositoryItem item;
- item.local_dir = dir;
- item.name = name;
- saveRepositoryBookmark(item);
-
- if (open) {
- setCurrentRepository(item, true);
- GitPtr g = git(item.local_dir);
- openRepository_(g);
- }
-}
-
void MainWindow::on_action_open_existing_working_copy_triggered()
{
QString dir = defaultWorkingDir();
dir = QFileDialog::getExistingDirectory(this, tr("Add existing working copy"), dir);
addWorkingCopyDir(dir, false);
}
void MainWindow::on_action_view_refresh_triggered()
{
openRepository(true);
}
void MainWindow::on_tableWidget_log_currentItemChanged(QTableWidgetItem * /*current*/, QTableWidgetItem * /*previous*/)
{
clearFileList();
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (!item) return;
+ auto const &logs = getLogs();
int row = item->data(IndexRole).toInt();
- if (row < (int)m->logs.size()) {
+ if (row < (int)logs.size()) {
updateStatusBarText();
- m->update_files_list_counter = 200;
+ *ptrUpdateFilesListCounter() = 200;
}
}
void MainWindow::on_toolButton_stage_clicked()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->stage(selectedFiles());
updateCurrentFilesList();
}
void MainWindow::on_toolButton_unstage_clicked()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->unstage(selectedFiles());
updateCurrentFilesList();
}
void MainWindow::on_toolButton_select_all_clicked()
{
if (ui->listWidget_unstaged->count() > 0) {
ui->listWidget_unstaged->setFocus();
ui->listWidget_unstaged->selectAll();
} else if (ui->listWidget_staged->count() > 0) {
ui->listWidget_staged->setFocus();
ui->listWidget_staged->selectAll();
}
}
void MainWindow::on_toolButton_commit_clicked()
{
ui->action_commit->trigger();
}
-bool MainWindow::editFile(QString const &path, QString const &title)
-{
- return TextEditDialog::editFile(this, path, title);
-}
-
-struct Task {
- int index = 0;
- int parent = 0;
- Task() = default;
- Task(int index, int parent)
- : index(index)
- , parent(parent)
- {
- }
-};
-
-struct Element {
- int depth = 0;
- std::vector<int> indexes;
-};
-
void MainWindow::updateCommitGraph()
{
- const size_t LogCount = m->logs.size();
+ auto const &logs = getLogs();
+ auto *logsp = getLogsPtr();
+
+ const size_t LogCount = logs.size();
// 樹形図情報を構築する
if (LogCount > 0) {
- auto LogItem = [&](size_t i)->Git::CommitItem &{ return m->logs[i]; };
+ auto LogItem = [&](size_t i)->Git::CommitItem &{ return logsp->at(i); };
enum { // 有向グラフを構築するあいだ CommitItem::marker_depth をフラグとして使用する
UNKNOWN = 0,
KNOWN = 1,
};
- for (Git::CommitItem &item : m->logs) {
+ for (Git::CommitItem &item : *logsp) {
item.marker_depth = UNKNOWN;
}
// コミットハッシュを検索して、親コミットのインデックスを求める
for (size_t i = 0; i < LogCount; i++) {
Git::CommitItem *item = &LogItem(i);
item->parent_lines.clear();
if (item->parent_ids.empty()) {
item->resolved = true;
} else {
for (int j = 0; j < item->parent_ids.size(); j++) { // 親の数だけループ
QString const &parent_id = item->parent_ids[j]; // 親のハッシュ値
for (size_t k = i + 1; k < LogCount; k++) { // 親を探す
if (LogItem(k).commit_id == parent_id) { // ハッシュ値が一致したらそれが親
item->parent_lines.emplace_back(k); // インデックス値を記憶
LogItem(k).has_child = true;
LogItem(k).marker_depth = KNOWN;
item->resolved = true;
break;
}
}
}
}
}
std::vector<Element> elements; // 線分リスト
{ // 線分リストを作成する
std::deque<Task> tasks; // 未処理タスクリスト
{
for (size_t i = 0; i < LogCount; i++) {
Git::CommitItem *item = &LogItem(i);
if (item->marker_depth == UNKNOWN) {
int n = item->parent_lines.size(); // 最初のコミットアイテム
for (int j = 0; j < n; j++) {
tasks.emplace_back(i, j); // タスクを追加
}
}
item->marker_depth = UNKNOWN;
}
}
while (!tasks.empty()) { // タスクが残っているならループ
Element e;
Task task;
{ // 最初のタスクを取り出す
task = tasks.front();
tasks.pop_front();
}
e.indexes.push_back(task.index); // 先頭のインデックスを追加
int index = LogItem(task.index).parent_lines[task.parent].index; // 開始インデックス
while (index > 0 && (size_t)index < LogCount) { // 最後に到達するまでループ
e.indexes.push_back(index); // インデックスを追加
int n = LogItem(index).parent_lines.size(); // 親の数
if (n == 0) break; // 親がないなら終了
Git::CommitItem *item = &LogItem(index);
if (item->marker_depth == KNOWN) break; // 既知のアイテムに到達したら終了
item->marker_depth = KNOWN; // 既知のアイテムにする
for (int i = 1; i < n; i++) {
tasks.emplace_back(index, i); // タスク追加
}
index = LogItem(index).parent_lines[0].index; // 次の親(親リストの先頭の要素)
}
if (e.indexes.size() >= 2) {
elements.push_back(e);
}
}
}
// 線情報をクリア
- for (Git::CommitItem &item : m->logs) {
+ for (Git::CommitItem &item : *logsp) {
item.marker_depth = -1;
item.parent_lines.clear();
}
// マークと線の深さを決める
if (!elements.empty()) {
{ // 優先順位を調整する
std::sort(elements.begin(), elements.end(), [](Element const &left, Element const &right){
int i = 0;
{ // 長いものを優先して左へ
int l = left.indexes.back() - left.indexes.front();
int r = right.indexes.back() - right.indexes.front();
i = r - l; // 降順
}
if (i == 0) {
// コミットが新しいものを優先して左へ
int l = left.indexes.front();
int r = right.indexes.front();
i = l - r; // 昇順
}
return i < 0;
});
// 子の無いブランチ(タグ等)が複数連続しているとき、古いコミットを右に寄せる
{
for (size_t i = 0; i + 1 < elements.size(); i++) {
Element *e = &elements[i];
int index1 = e->indexes.front();
if (index1 > 0 && !LogItem(index1).has_child) { // 子がない
// 新しいコミットを探す
for (size_t j = i + 1; j < elements.size(); j++) { // 現在位置より後ろを探す
Element *f = &elements[j];
int index2 = f->indexes.front();
if (index1 == index2 + 1) { // 一つだけ新しいコミット
Element t = std::move(*f);
elements.erase(elements.begin() + j); // 移動元を削除
elements.insert(elements.begin() + i, std::move(t)); // 現在位置に挿入
}
}
// 古いコミットを探す
size_t j = 0;
while (j < i) { // 現在位置より前を探す
Element *f = &elements[j];
int index2 = f->indexes.front();
if (index1 + 1 == index2) { // 一つだけ古いコミット
Element t = std::move(*f);
elements.erase(elements.begin() + j); // 移動元を削除
elements.insert(elements.begin() + i, std::move(t)); // 現在位置の次に挿入
index1 = index2;
f = e;
} else {
j++;
}
}
}
}
}
}
{ // 最初の線は深さを0にする
Element *e = &elements.front();
for (size_t i = 0; i < e->indexes.size(); i++) {
int index = e->indexes[i];
LogItem(index).marker_depth = 0; // マークの深さを設定
e->depth = 0; // 線の深さを設定
}
}
// 最初以外の線分の深さを決める
for (size_t i = 1; i < elements.size(); i++) { // 最初以外をループ
Element *e = &elements[i];
int depth = 1;
while (1) { // 失敗したら繰り返し
for (size_t j = 0; j < i; j++) { // 既に処理済みの線を調べる
Element const *f = &elements[j]; // 検査対象
if (e->indexes.size() == 2) { // 二つしかない場合
int from = e->indexes[0]; // 始点
int to = e->indexes[1]; // 終点
if (LogItem(from).has_child) {
for (size_t k = 0; k + 1 < f->indexes.size(); k++) { // 検査対象の全ての線分を調べる
int curr = f->indexes[k];
int next = f->indexes[k + 1];
if (from > curr && to == next) { // 決定済みの線に直結できるか判定
e->indexes.back() = from + 1; // 現在の一行下に直結する
e->depth = elements[j].depth; // 接続先の深さ
goto DONE; // 決定
}
}
}
}
if (depth == f->depth) { // 同じ深さ
if (e->indexes.back() > f->indexes.front() && e->indexes.front() < f->indexes.back()) { // 重なっている
goto FAIL; // この深さには線を置けないのでやりなおし
}
}
}
for (size_t j = 0; j < e->indexes.size(); j++) {
int index = e->indexes[j];
Git::CommitItem *item = &LogItem(index);
if (j == 0 && item->has_child) { // 最初のポイントで子がある場合
// nop
} else if ((j > 0 && j + 1 < e->indexes.size()) || item->marker_depth < 0) { // 最初と最後以外、または、未確定の場合
item->marker_depth = depth; // マークの深さを設定
}
}
e->depth = depth; // 深さを決定
goto DONE; // 決定
FAIL:;
depth++; // 一段深くして再挑戦
}
DONE:;
}
// 線情報を生成する
for (const auto & e : elements) {
auto ColorNumber = [&](){ return e.depth; };
size_t count = e.indexes.size();
for (size_t j = 0; j + 1 < count; j++) {
int curr = e.indexes[j];
int next = e.indexes[j + 1];
TreeLine line(next, e.depth);
line.color_number = ColorNumber();
line.bend_early = (j + 2 < count || !LogItem(next).resolved);
if (j + 2 == count) {
if (count > 2 || !LogItem(curr).has_child) { // 直結ではない、または、子がない
line.depth = LogItem(next).marker_depth; // 合流する先のアイテムの深さと同じにする
}
}
LogItem(curr).parent_lines.push_back(line); // 線を追加
}
}
} else {
if (LogCount == 1) { // コミットが一つだけ
LogItem(0).marker_depth = 0;
}
}
}
}
-QList<Git::Tag> MainWindow::findTag(QString const &id)
-{
- auto it = m->tag_map.find(id);
- if (it != m->tag_map.end()) {
- return it->second;
- }
- return QList<Git::Tag>();
-}
-
void MainWindow::on_action_edit_global_gitconfig_triggered()
{
QString dir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
QString path = dir / ".gitconfig";
editFile(path, ".gitconfig");
}
void MainWindow::on_action_edit_git_config_triggered()
{
QString dir = currentWorkingCopyDir();
QString path = dir / ".git/config";
editFile(path, ".git/config");
}
void MainWindow::on_action_edit_gitignore_triggered()
{
QString dir = currentWorkingCopyDir();
QString path = dir / ".gitignore";
if (editFile(path, ".gitignore")) {
updateCurrentFilesList();
}
}
int MainWindow::selectedLogIndex() const
{
+ auto const &logs = getLogs();
int i = ui->tableWidget_log->currentRow();
- if (i >= 0 && i < (int)m->logs.size()) {
+ if (i >= 0 && i < (int)logs.size()) {
return i;
}
return -1;
}
-Git::CommitItem const *MainWindow::selectedCommitItem() const
-{
- int i = selectedLogIndex();
- return commitItem(i);
-}
-
-Git::Object MainWindow::cat_file_(GitPtr g, QString const &id)
-{
- if (isValidWorkingCopy(g)) {
- QString path_prefix = PATH_PREFIX;
- if (id.startsWith(path_prefix)) {
- QString path = g->workingRepositoryDir();
- path = path / id.mid(path_prefix.size());
- QFile file(path);
- if (file.open(QFile::ReadOnly)) {
- Git::Object obj;
- obj.content = file.readAll();
- return obj;
- }
- } else if (Git::isValidID(id)) {
- return m->objcache.catFile(id);;
- }
- }
- return Git::Object();
-}
-
-Git::Object MainWindow::cat_file(QString const &id)
-{
- return cat_file_(git(), id);
-}
-
void MainWindow::updateDiffView(QListWidgetItem *item)
{
clearDiffView();
m->last_selected_file_item = item;
if (!item) return;
int idiff = indexOfDiff(item);
- if (idiff >= 0 && idiff < m->diff.result.size()) {
- Git::Diff const &diff = m->diff.result[idiff];
+ if (idiff >= 0 && idiff < diffResult()->size()) {
+ Git::Diff const &diff = diffResult()->at(idiff);
QString key = GitDiff::makeKey(diff);
- auto it = m->diff_cache.find(key);
- if (it != m->diff_cache.end()) {
+ auto it = getDiffCacheMap()->find(key);
+ if (it != getDiffCacheMap()->end()) {
+ auto const &logs = getLogs();
int row = ui->tableWidget_log->currentRow();
- bool uncommited = (row >= 0 && row < (int)m->logs.size() && Git::isUncommited(m->logs[row]));
+ bool uncommited = (row >= 0 && row < (int)logs.size() && Git::isUncommited(logs[row]));
ui->widget_diff_view->updateDiffView(it->second, uncommited);
}
}
}
void MainWindow::updateDiffView()
{
updateDiffView(m->last_selected_file_item);
}
void MainWindow::updateUnstagedFileCurrentItem()
{
updateDiffView(ui->listWidget_unstaged->currentItem());
}
void MainWindow::updateStagedFileCurrentItem()
{
updateDiffView(ui->listWidget_staged->currentItem());
}
void MainWindow::on_listWidget_unstaged_currentRowChanged(int /*currentRow*/)
{
updateUnstagedFileCurrentItem();
}
void MainWindow::on_listWidget_staged_currentRowChanged(int /*currentRow*/)
{
updateStagedFileCurrentItem();
}
void MainWindow::on_listWidget_files_currentRowChanged(int /*currentRow*/)
{
updateDiffView(ui->listWidget_files->currentItem());
}
-void MainWindow::logGitVersion()
-{
- GitPtr g = git();
- QString s = g->version();
- if (!s.isEmpty()) {
- s += '\n';
- writeLog(s);
- }
-}
-
-bool MainWindow::checkExecutable(QString const &path)
-{
- if (QFileInfo(path).isExecutable()) {
- return true;
- }
- QString text = "The specified program '%1' is not executable.\n";
- text = text.arg(path);
- writeLog(text);
- return false;
-}
-
-void MainWindow::internalSetCommand(QString const &path, bool save, QString const &name, QString *out)
-{
- if (!path.isEmpty() && checkExecutable(path)) {
- if (save) {
- MySettings s;
- s.beginGroup("Global");
- s.setValue(name, path);
- s.endGroup();
- }
- *out = path;
- } else {
- *out = QString();
- }
-}
-
-void MainWindow::setGitCommand(QString const &path, bool save)
-{
- internalSetCommand(path, save, "GitCommand", &m->gcx.git_command);
-}
-
-void MainWindow::setFileCommand(QString const &path, bool save)
-{
- internalSetCommand(path, save, "FileCommand", &global->file_command);
-}
-
-void MainWindow::setGpgCommand(QString const &path, bool save)
-{
- internalSetCommand(path, save, "GpgCommand", &global->gpg_command);
- if (!global->gpg_command.isEmpty()) {
- GitPtr g = git();
- g->configGpgProgram(global->gpg_command, true);
- }
-}
-
void MainWindow::setWatchRemoteInterval(int mins)
{
if (mins > 0) {
m->remote_watcher_timer.start(mins * 60000);
} else {
m->remote_watcher_timer.stop();
}
}
-#ifdef Q_OS_WIN
-QString getWin32HttpProxy();
-#endif
-
-void MainWindow::initNetworking()
-{
- std::string http_proxy;
- std::string https_proxy;
- if (appsettings()->proxy_type == "auto") {
-#ifdef Q_OS_WIN
- http_proxy = misc::makeProxyServerURL(getWin32HttpProxy().toStdString());
-#else
- auto getienv = [](std::string const &name)->char const *{
- char **p = environ;
- while (*p) {
- if (strncasecmp(*p, name.c_str(), name.size()) == 0) {
- char const *e = *p + name.size();
- if (*e == '=') {
- return e + 1;
- }
- }
- p++;
- }
- return nullptr;
- };
- char const *p;
- p = getienv("http_proxy");
- if (p) {
- http_proxy = misc::makeProxyServerURL(std::string(p));
- }
- p = getienv("https_proxy");
- if (p) {
- https_proxy = misc::makeProxyServerURL(std::string(p));
- }
-#endif
- } else if (appsettings()->proxy_type == "manual") {
- http_proxy = appsettings()->proxy_server.toStdString();
- }
- m->webcx.set_http_proxy(http_proxy);
- m->webcx.set_https_proxy(https_proxy);
-}
-
-
-
-QStringList MainWindow::whichCommand_(QString const &cmdfile1, QString const &cmdfile2)
-{
- QStringList list;
-
- if (!cmdfile1.isEmpty()){
- std::vector<std::string> vec;
- FileUtil::which(cmdfile1.toStdString(), &vec);
- for (std::string const &s : vec) {
- list.push_back(QString::fromStdString(s));
- }
- }
- if (!cmdfile2.isEmpty()){
- std::vector<std::string> vec;
- FileUtil::which(cmdfile2.toStdString(), &vec);
- for (std::string const &s : vec) {
- list.push_back(QString::fromStdString(s));
- }
- }
-
- return list;
-}
-
-QString MainWindow::selectCommand_(QString const &cmdname, QStringList const &cmdfiles, QStringList const &list, QString path, std::function<void(QString const &)> callback)
-{
- QString window_title = tr("Select %1 command");
- window_title = window_title.arg(cmdfiles.front());
-
- SelectCommandDialog dlg(this, cmdname, cmdfiles, path, list);
- dlg.setWindowTitle(window_title);
- if (dlg.exec() == QDialog::Accepted) {
- path = dlg.selectedFile();
- path = misc::normalizePathSeparator(path);
- QFileInfo info(path);
- if (info.isExecutable()) {
- callback(path);
- return path;
- }
- }
- return QString();
-}
-
-QString MainWindow::selectCommand_(QString const &cmdname, QString const &cmdfile, QStringList const &list, QString path, std::function<void(QString const &)> callback)
-{
- QStringList cmdfiles;
- cmdfiles.push_back(cmdfile);
- return selectCommand_(cmdname, cmdfiles, list, path, callback);
-}
-
-#ifdef Q_OS_WIN
-#define GIT_COMMAND "git.exe"
-#define FILE_COMMAND "file.exe"
-#define GPG_COMMAND "gpg.exe"
-#define GPG2_COMMAND "gpg2.exe"
-#else
-#define GIT_COMMAND "git"
-#define FILE_COMMAND "file"
-#define GPG_COMMAND "gpg"
-#define GPG2_COMMAND "gpg2"
-#endif
-
-QString MainWindow::selectGitCommand(bool save)
-{
- char const *exe = GIT_COMMAND;
-
- QString path = m->gcx.git_command;
-
- auto fn = [&](QString const &path){
- setGitCommand(path, save);
- };
-
- QStringList list = whichCommand_(exe);
-#ifdef Q_OS_WIN
- {
- QStringList newlist;
- QString suffix1 = "\\bin\\" GIT_COMMAND;
- QString suffix2 = "\\cmd\\" GIT_COMMAND;
- for (QString const &s : list) {
- newlist.push_back(s);
- auto DoIt = [&](QString const &suffix){
- if (s.endsWith(suffix)) {
- QString t = s.mid(0, s.size() - suffix.size());
- QString t1 = t + "\\mingw64\\bin\\" GIT_COMMAND;
- if (misc::isExecutable(t1)) newlist.push_back(t1);
- QString t2 = t + "\\mingw\\bin\\" GIT_COMMAND;
- if (misc::isExecutable(t2)) newlist.push_back(t2);
- }
- };
- DoIt(suffix1);
- DoIt(suffix2);
- }
- std::sort(newlist.begin(), newlist.end());
- auto end = std::unique(newlist.begin(), newlist.end());
- list.clear();
- for (auto it = newlist.begin(); it != end; it++) {
- list.push_back(*it);
- }
- }
-#endif
- return selectCommand_("Git", exe, list, path, fn);
-}
-
-QString MainWindow::selectFileCommand(bool save)
-{
- char const *exe = FILE_COMMAND;
-
- QString path = global->file_command;
-
- auto fn = [&](QString const &path){
- setFileCommand(path, save);
- };
-
- QStringList list = whichCommand_(exe);
-
-#ifdef Q_OS_WIN
- QString dir = misc::getApplicationDir();
- QString path1 = dir / FILE_COMMAND;
- QString path2;
- int i = dir.lastIndexOf('/');
- int j = dir.lastIndexOf('\\');
- if (i < j) i = j;
- if (i > 0) {
- path2 = dir.mid(0, i) / FILE_COMMAND;
- }
- path1 = misc::normalizePathSeparator(path1);
- path2 = misc::normalizePathSeparator(path2);
- if (misc::isExecutable(path1)) list.push_back(path1);
- if (misc::isExecutable(path2)) list.push_back(path2);
-#endif
-
- return selectCommand_("File", exe, list, path, fn);
-}
-
-QString MainWindow::selectGpgCommand(bool save)
-{
- QString path = global->gpg_command;
-
- auto fn = [&](QString const &path){
- setGpgCommand(path, save);
- };
-
- QStringList list = whichCommand_(GPG_COMMAND, GPG2_COMMAND);
-
- QStringList cmdlist;
- cmdlist.push_back(GPG_COMMAND);
- cmdlist.push_back(GPG2_COMMAND);
- return selectCommand_("GPG", cmdlist, list, path, fn);
-}
-
-void MainWindow::checkUser()
-{
- Git g(m->gcx, QString());
- while (1) {
- Git::User user = g.getUser(Git::Source::Global);
- if (!user.name.isEmpty() && !user.email.isEmpty()) {
- return; // ok
- }
- if (!execSetGlobalUserDialog()) {
- return;
- }
- }
-}
-
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
if (QApplication::modalWindow()) return;
if (event->mimeData()->hasUrls()) {
event->acceptProposedAction();
event->accept();
}
}
void MainWindow::timerEvent(QTimerEvent *)
{
- bool running = m->pty_process.isRunning();
+ bool running = getPtyProcess()->isRunning();
if (ui->toolButton_stop_process->isEnabled() != running) {
ui->toolButton_stop_process->setEnabled(running);
ui->action_stop_process->setEnabled(running);
setNetworkingCommandsEnabled(!running);
}
if (!running) {
- m->interaction_mode = InteractionMode::None;
+ setInteractionMode(InteractionMode::None);
}
while (1) {
char tmp[1024];
- int len = m->pty_process.readOutput(tmp, sizeof(tmp));
+ int len = getPtyProcess()->readOutput(tmp, sizeof(tmp));
if (len < 1) break;
writeLog(tmp, len);
}
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (qApp->focusWidget() == ui->widget_log) {
int c = event->key();
auto write_char = [&](char c){
- if (m->pty_process.isRunning()) {
- m->pty_process.writeInput(&c, 1);
+ if (getPtyProcess()->isRunning()) {
+ getPtyProcess()->writeInput(&c, 1);
}
};
auto write_text = [&](QString const &str){
std::string s = str.toStdString();
for (char i : s) {
write_char(i);
}
};
if (c == Qt::Key_Return || c == Qt::Key_Enter) {
write_char('\n');
} else {
QString text = event->text();
write_text(text);
}
}
}
-bool MainWindow::saveByteArrayAs(QByteArray const &ba, QString const &dstpath)
-{
- QFile file(dstpath);
- if (file.open(QFile::WriteOnly)) {
- file.write(ba);
- file.close();
- return true;
- }
- QString msg = "Failed to open the file '%1' for write";
- msg = msg.arg(dstpath);
- qDebug() << msg;
- return false;
-}
-
-bool MainWindow::saveFileAs(QString const &srcpath, QString const &dstpath)
-{
- QFile f(srcpath);
- if (f.open(QFile::ReadOnly)) {
- QByteArray ba = f.readAll();
- if (saveByteArrayAs(ba, dstpath)) {
- return true;
- }
- } else {
- QString msg = "Failed to open the file '%1' for read";
- msg = msg.arg(srcpath);
- qDebug() << msg;
- }
- return false;
-}
-
-bool MainWindow::saveBlobAs(QString const &id, QString const &dstpath)
-{
- Git::Object obj = cat_file(id);
- if (!obj.content.isEmpty()) {
- if (saveByteArrayAs(obj.content, dstpath)) {
- return true;
- }
- } else {
- QString msg = "Failed to get the content of the object '%1'";
- msg = msg.arg(id);
- qDebug() << msg;
- }
- return false;
-}
-
-bool MainWindow::saveAs(QString const &id, QString const &dstpath)
-{
- if (id.startsWith(PATH_PREFIX)) {
- return saveFileAs(id.mid(1), dstpath);
- } else {
- return saveBlobAs(id, dstpath);
- }
-}
-
-QString MainWindow::saveAsTemp(QString const &id)
-{
- QString path = newTempFilePath();
- saveAs(id, path);
- return path;
-}
-
void MainWindow::on_action_edit_settings_triggered()
{
SettingsDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) {
ApplicationSettings const &newsettings = dlg.settings();
- m->appsettings = newsettings;
+ setAppSettings(newsettings);
setGitCommand(appsettings()->git_command, false);
setFileCommand(appsettings()->file_command, false);
setGpgCommand(appsettings()->gpg_command, false);
setWatchRemoteInterval(appsettings()->watch_remote_changes_every_mins);
}
}
-void MainWindow::clone()
-{
- if (!isRemoteOnline()) return;
-
- QString url;
- QString dir = defaultWorkingDir();
- while (1) {
- CloneDialog dlg(this, url, dir);
- if (dlg.exec() != QDialog::Accepted) {
- return;
- }
-
- url = dlg.url();
- dir = dlg.dir();
-
- if (dlg.action() == CloneDialog::Action::Clone) {
- // 既存チェック
-
- QFileInfo info(dir);
- if (info.isFile()) {
- QString msg = dir + "\n\n" + tr("A file with same name already exists");
- QMessageBox::warning(this, tr("Clone"), msg);
- continue;
- }
- if (info.isDir()) {
- QString msg = dir + "\n\n" + tr("A folder with same name already exists");
- QMessageBox::warning(this, tr("Clone"), msg);
- continue;
- }
-
- // クローン先ディレクトリを求める
-
- Git::CloneData clone_data = Git::preclone(url, dir);
-
- // クローン先ディレクトリの存在チェック
-
- QString basedir = misc::normalizePathSeparator(clone_data.basedir);
- if (!QFileInfo(basedir).isDir()) {
- int i = basedir.indexOf('/');
- int j = basedir.indexOf('\\');
- if (i < j) i = j;
- if (i < 0) {
- QString msg = basedir + "\n\n" + tr("Invalid folder");
- QMessageBox::warning(this, tr("Clone"), msg);
- continue;
- }
-
- QString msg = basedir + "\n\n" + tr("No such folder. Create it now ?");
- if (QMessageBox::warning(this, tr("Clone"), msg, QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) {
- continue;
- }
-
- // ディレクトリを作成
-
- QString base = basedir.mid(0, i + 1);
- QString sub = basedir.mid(i + 1);
- QDir(base).mkpath(sub);
- }
-
- m->temp_repo.local_dir = dir;
- m->temp_repo.local_dir.replace('\\', '/');
- m->temp_repo.name = makeRepositoryName(dir);
-
- GitPtr g = git(QString());
- m->pty_condition = PtyCondition::Clone;
- m->pty_process_ok = true;
- g->clone(clone_data, &m->pty_process);
-
- } else if (dlg.action() == CloneDialog::Action::AddExisting) {
- addWorkingCopyDir(dir, true);
- }
-
- return; // done
- }
-}
-
void MainWindow::onCloneCompleted(bool success)
{
if (success) {
- saveRepositoryBookmark(m->temp_repo);
- setCurrentRepository(m->temp_repo, false);
+ saveRepositoryBookmark(getTempRepoForCloneComplete());
+ setCurrentRepository(getTempRepoForCloneComplete(), false);
openRepository(true);
}
}
void MainWindow::onPtyProcessCompleted()
{
- switch (m->pty_condition) {
+ switch (getPtyCondition()) {
case PtyCondition::Clone:
- onCloneCompleted(m->pty_process_ok);
+ onCloneCompleted(getPtyProcessOk());
break;
}
- m->pty_condition = PtyCondition::None;
+ setPtyCondition(PtyCondition::None);
}
void MainWindow::on_action_clone_triggered()
{
clone();
}
void MainWindow::on_action_about_triggered()
{
AboutDialog dlg(this);
dlg.exec();
}
void MainWindow::on_toolButton_clone_clicked()
{
ui->action_clone->trigger();
}
void MainWindow::on_toolButton_fetch_clicked()
{
ui->action_fetch->trigger();
}
void MainWindow::clearRepoFilter()
{
ui->lineEdit_filter->clear();
}
void MainWindow::appendCharToRepoFilter(ushort c)
{
if (QChar(c).isLetter()) {
c = QChar(c).toLower().unicode();
}
- ui->lineEdit_filter->setText(m->repository_filter_text + c);
+ ui->lineEdit_filter->setText(getRepositoryFilterText() + c);
}
void MainWindow::backspaceRepoFilter()
{
- QString s = m->repository_filter_text;
+ QString s = getRepositoryFilterText();
int n = s.size();
if (n > 0) {
s = s.mid(0, n - 1);
}
ui->lineEdit_filter->setText(s);
}
void MainWindow::on_lineEdit_filter_textChanged(QString const &text)
{
- m->repository_filter_text = text;
+ setRepositoryFilterText(text);
updateRepositoriesList();
}
void MainWindow::on_toolButton_erase_filter_clicked()
{
clearRepoFilter();
ui->lineEdit_filter->setFocus();
}
void MainWindow::deleteTags(QStringList const &tagnames)
{
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return;
-
int row = ui->tableWidget_log->currentRow();
- if (!tagnames.isEmpty()) {
- reopenRepository(false, [&](GitPtr g){
- for (QString const &name : tagnames) {
- g->delete_tag(name, true);
- }
- });
- }
+ internalDeleteTags(tagnames);
ui->tableWidget_log->selectRow(row);
-}
-
-void MainWindow::deleteTags(const Git::CommitItem &commit)
-{
- auto it = m->tag_map.find(commit.commit_id);
- if (it != m->tag_map.end()) {
- QStringList names;
- QList<Git::Tag> const &tags = it->second;
- for (Git::Tag const &tag : tags) {
- names.push_back(tag.name);
- }
- deleteTags(names);
- }
-}
-
-
+}
void MainWindow::revertCommit()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
g->revert(commit->commit_id);
openRepository(false);
}
}
bool MainWindow::addTag(QString const &name)
{
- if (name.isEmpty()) return false;
-
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return false;
-
- QString commit_id;
-
- Git::CommitItem const *commit = selectedCommitItem();
- if (commit && !commit->commit_id.isEmpty()) {
- commit_id = commit->commit_id;
- }
-
- if (!Git::isValidID(commit_id)) return false;
-
int row = ui->tableWidget_log->currentRow();
- bool ok = false;
- reopenRepository(false, [&](GitPtr g){
- ok = g->tag(name, commit_id);
- });
+ bool ok = internalAddTag(name);
ui->tableWidget_log->selectRow(row);
return ok;
}
void MainWindow::on_action_tag_push_all_triggered()
{
reopenRepository(false, [&](GitPtr g){
g->push(true);
});
}
-QString MainWindow::tempfileHeader() const
-{
- QString name = "jp_soramimi_Guitar_%1_";
- return name.arg(QApplication::applicationPid());
-}
-
-void MainWindow::deleteTempFiles()
-{
- QString dir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
- QString name = tempfileHeader();
- QDirIterator it(dir, { name + "*" });
- while (it.hasNext()) {
- QString path = it.next();
- QFile::remove(path);
- }
-}
-
-QString MainWindow::newTempFilePath()
-{
- QString tmpdir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
- QString path = tmpdir / tempfileHeader() + QString::number(m->temp_file_counter);
- m->temp_file_counter++;
- return path;
-}
-
-QString MainWindow::determinFileType_(QString const &path, bool mime, std::function<void(QString const &cmd, QByteArray *ba)> callback) const
-{
- const_cast<MainWindow *>(this)->checkFileCommand();
- return misc::determinFileType(global->file_command, path, mime, callback);
-}
-
-QString MainWindow::determinFileType(QString const &path, bool mime)
-{
- return determinFileType_(path, mime, [](QString const &cmd, QByteArray *ba){
- misc2::runCommand(cmd, ba);
- });
-}
-
-QString MainWindow::determinFileType(QByteArray in, bool mime)
-{
- if (in.isEmpty()) return QString();
-
- if (in.size() > 10) {
- if (memcmp(in.data(), "\x1f\x8b\x08", 3) == 0) { // gzip
- QBuffer buf;
- MemoryReader reader(in.data(), in.size());
- reader.open(MemoryReader::ReadOnly);
- buf.open(QBuffer::WriteOnly);
- gunzip z;
- z.set_maximul_size(100000);
- z.decode(&reader, &buf);
- in = buf.buffer();
- }
- }
-
- // ファイル名を "-" にすると、リダイレクトで標準入力へ流し込める。
- return determinFileType_("-", mime, [&](QString const &cmd, QByteArray *ba){
- int r = misc2::runCommand(cmd, &in, ba);
- if (r != 0) {
- ba->clear();
- }
- });
-}
-
void MainWindow::on_tableWidget_log_itemDoubleClicked(QTableWidgetItem *)
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
execCommitPropertyDialog(this, commit);
}
}
-void MainWindow::execFilePropertyDialog(QListWidgetItem *item)
-{
- if (item) {
- QString path = getFilePath(item);
- QString id = getObjectID(item);
- FilePropertyDialog dlg(this);
- dlg.exec(this, path, id);
- }
-}
-
void MainWindow::on_listWidget_unstaged_itemDoubleClicked(QListWidgetItem * item)
{
execFilePropertyDialog(item);
}
void MainWindow::on_listWidget_staged_itemDoubleClicked(QListWidgetItem *item)
{
execFilePropertyDialog(item);
}
void MainWindow::on_listWidget_files_itemDoubleClicked(QListWidgetItem *item)
{
execFilePropertyDialog(item);
}
QListWidgetItem *MainWindow::currentFileItem() const
{
QListWidget *listwidget = nullptr;
if (ui->stackedWidget->currentWidget() == ui->page_uncommited) {
QWidget *w = qApp->focusWidget();
if (w == ui->listWidget_unstaged) {
listwidget = ui->listWidget_unstaged;
} else if (w == ui->listWidget_staged) {
listwidget = ui->listWidget_staged;
}
} else {
listwidget = ui->listWidget_files;
}
if (listwidget) {
return listwidget->currentItem();
}
return nullptr;
}
-QPixmap MainWindow::getTransparentPixmap()
-{
- if (m->transparent_pixmap.isNull()) {
- m->transparent_pixmap = QPixmap(":/image/transparent.png");
- }
- return m->transparent_pixmap;
-}
-
-QString MainWindow::getCommitIdFromTag(QString const &tag)
-{
- return m->objcache.getCommitIdFromTag(tag);
-}
-
-bool MainWindow::isValidRemoteURL(QString const &url)
-{
- if (url.indexOf('\"') >= 0) {
- return false;
- }
- stopPtyProcess();
- GitPtr g = git(QString());
- QString cmd = "ls-remote \"%1\" HEAD";
- bool f = g->git(cmd.arg(url), false, false, &m->pty_process);
- {
- QTime time;
- time.start();
- while (!m->pty_process.wait(10)) {
- if (time.elapsed() > 10000) {
- f = false;
- break;
- }
- QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
- }
- stopPtyProcess();
- }
- QString line = g->resultText().trimmed();
- QString head;
- int i = line.indexOf('\t');
- if (i == GIT_ID_LENGTH) {
- QString id = line.mid(0, i);
- QString name = line.mid(i + 1);
- qDebug() << id << name;
- if (name == "HEAD" && Git::isValidID(id)) {
- head = id;
- }
- }
- if (f) {
- qDebug() << "This is a valid repository.";
- if (head.isEmpty()) {
- qDebug() << "But HEAD not found";
- }
- return true;
- }
- qDebug() << "It is not a repository.";
- return false;
-}
-
-bool MainWindow::testRemoteRepositoryValidity(QString const &url)
-{
- bool ok;
- {
- OverrideWaitCursor;
- ok = isValidRemoteURL(url);
- }
-
- QString pass = tr("The URL is a valid repository");
- QString fail = tr("Failed to access the URL");
-
- QString text = "%1\n\n%2";
- text = text.arg(url).arg(ok ? pass : fail);
-
- QString title = tr("Remote Repository");
-
- if (ok) {
- QMessageBox::information(this, title, text);
- } else {
- QMessageBox::critical(this, title, text);
- }
-
- return ok;
-}
-
-bool MainWindow::execSetGlobalUserDialog()
-{
- SetGlobalUserDialog dlg(this);
- if (dlg.exec() == QDialog::Accepted) {
- GitPtr g = git();
- Git::User user = dlg.user();
- g->setUser(user, true);
- updateWindowTitle(g);
- return true;
- }
- return false;
-}
-
-void MainWindow::execSetUserDialog(Git::User const &global_user, Git::User const &repo_user, QString const &reponame)
-{
- SetUserDialog dlg(this, global_user, repo_user, reponame);
- if (dlg.exec() == QDialog::Accepted) {
- GitPtr g = git();
- Git::User user = dlg.user();
- if (dlg.isGlobalChecked()) {
- g->setUser(user, true);
- }
- if (dlg.isRepositoryChecked()) {
- g->setUser(user, false);
- }
- updateWindowTitle(g);
- }
-}
-
void MainWindow::on_action_set_config_user_triggered()
{
Git::User global_user;
Git::User repo_user;
GitPtr g = git();
if (isValidWorkingCopy(g)) {
repo_user = g->getUser(Git::Source::Local);
}
global_user = g->getUser(Git::Source::Global);
execSetUserDialog(global_user, repo_user, currentRepositoryName());
}
void MainWindow::on_action_window_log_triggered(bool checked)
{
ui->dockWidget_log->setVisible(checked);
}
-NamedCommitList MainWindow::namedCommitItems(int flags)
-{
- NamedCommitList items;
- if (flags & Branches) {
- for (auto pair: m->branch_map) {
- QList<Git::Branch> const &list = pair.second;
- for (Git::Branch const &b : list) {
- if (b.isHeadDetached()) continue;
- QString key = b.name;
- if (flags & NamedCommitFlag::Remotes) {
- if (!b.remote.isEmpty()) {
- key = "remotes" / b.remote / key;
- }
- } else {
- if (!b.remote.isEmpty()) continue;
- }
- NamedCommitItem item;
- item.type = NamedCommitItem::Type::Branch;
- item.remote = b.remote;
- item.name = b.name;
- item.id = b.id;
- items.push_back(item);
- }
- }
- }
- if (flags & Tags) {
- for (auto pair: m->tag_map) {
- QList<Git::Tag> const &list = pair.second;
- for (Git::Tag const &t : list) {
- NamedCommitItem item;
- item.type = NamedCommitItem::Type::Tag;
- item.name = t.name;
- item.id = t.id;
- items.push_back(item);
- }
- }
- }
- return items;
-}
-
-int MainWindow::rowFromCommitId(QString const &id)
-{
- for (size_t i = 0; i < m->logs.size(); i++) {
- Git::CommitItem const &item = m->logs[i];
- if (item.commit_id == id) {
- return (int)i;
- }
- }
- return -1;
-}
-
void MainWindow::on_action_repo_jump_triggered()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
NamedCommitList items = namedCommitItems(Branches | Tags | Remotes);
JumpDialog::sort(&items);
{
NamedCommitItem head;
head.name = "HEAD";
- head.id = m->head_id;
+ head.id = getHeadId();
items.push_front(head);
}
JumpDialog dlg(this, items);
if (dlg.exec() == QDialog::Accepted) {
JumpDialog::Action action = dlg.action();
if (action == JumpDialog::Action::BranchsAndTags) {
QString name = dlg.text();
QString id = g->rev_parse(name);
if (g->objectType(id) == "tag") {
- id = m->objcache.getCommitIdFromTag(id);
+ id = getObjCache()->getCommitIdFromTag(id);
}
int row = rowFromCommitId(id);
if (row < 0) {
QMessageBox::warning(this, tr("Jump"), QString("%1\n(%2)\n\n").arg(name).arg(id) + tr("That commmit has not foud or not read yet"));
} else {
setCurrentLogRow(row);
if (dlg.isCheckoutChecked()) {
NamedCommitItem item;
for (NamedCommitItem const &t : items) {
if (t.name == name) {
item = t;
break;
}
}
bool ok = false;
if (item.type == NamedCommitItem::Type::Branch) {
ok = g->git(QString("checkout \"%1\"").arg(name), true);
} else if (item.type == NamedCommitItem::Type::Tag) {
ok = g->git(QString("checkout -b \"%1\" \"%2\"").arg(name).arg(id), true);
}
if (ok) {
openRepository(true);
}
}
}
} else if (action == JumpDialog::Action::CommitId) {
QString id = dlg.text();
jumpToCommit(id);
}
}
}
-void MainWindow::jumpToCommit(QString id)
-{
- GitPtr g = git();
- id = g->rev_parse(id);
- if (!id.isEmpty()) {
- int row = rowFromCommitId(id);
- setCurrentLogRow(row);
- }
-}
-
-void MainWindow::checkout(QWidget *parent, Git::CommitItem const *commit)
-{
- if (!commit) return;
-
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return;
-
- QStringList tags;
- QStringList local_branches;
- QStringList remote_branches;
- {
- NamedCommitList named_commits = namedCommitItems(Branches | Tags | Remotes);
- JumpDialog::sort(&named_commits);
- for (NamedCommitItem const &item : named_commits) {
- if (item.id == commit->commit_id) {
- QString name = item.name;
- if (item.type == NamedCommitItem::Type::Tag) {
- tags.push_back(name);
- } else if (item.type == NamedCommitItem::Type::Branch) {
- int i = name.lastIndexOf('/');
- if (i < 0 && name == "HEAD") continue;
- if (i > 0 && name.mid(i + 1) == "HEAD") continue;
- if (item.remote.isNull()) {
- local_branches.push_back(name);
- } else {
- remote_branches.push_back(name);
- }
- }
- }
- }
- }
-
- CheckoutDialog dlg(parent, tags, local_branches, remote_branches);
- if (dlg.exec() == QDialog::Accepted) {
- CheckoutDialog::Operation op = dlg.operation();
- QString name = dlg.branchName();
- QString id = commit->commit_id;
- bool ok = false;
- setLogEnabled(g, true);
- if (op == CheckoutDialog::Operation::HeadDetached) {
- ok = g->git(QString("checkout \"%1\"").arg(id), true);
- } else if (op == CheckoutDialog::Operation::CreateLocalBranch) {
- ok = g->git(QString("checkout -b \"%1\" \"%2\"").arg(name).arg(id), true);
- } else if (op == CheckoutDialog::Operation::ExistingLocalBranch) {
- ok = g->git(QString("checkout \"%1\"").arg(name), true);
- }
- if (ok) {
- openRepository(true);
- }
- }
-}
-
-void MainWindow::deleteBranch(Git::CommitItem const *commit)
-{
- if (!commit) return;
-
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return;
-
- QStringList all_local_branch_names;
- QStringList current_local_branch_names;
- {
- NamedCommitList named_commits = namedCommitItems(Branches);
- JumpDialog::sort(&named_commits);
- for (NamedCommitItem const &item : named_commits) {
- if (item.name == "HEAD") continue;
- if (item.id == commit->commit_id) {
- current_local_branch_names.push_back(item.name);
- }
- all_local_branch_names.push_back(item.name);
- }
- }
-
- DeleteBranchDialog dlg(this, false, all_local_branch_names, current_local_branch_names);
- if (dlg.exec() == QDialog::Accepted) {
- setLogEnabled(g, true);
- QStringList names = dlg.selectedBranchNames();
- int count = 0;
- for (QString const &name : names) {
- if (g->git(QString("branch -D \"%1\"").arg(name))) {
- count++;
- } else {
- writeLog(tr("Failed to delete the branch '%1'\n").arg(name));
- }
- }
- if (count > 0) {
- openRepository(true);
- }
- }
-}
-
void MainWindow::mergeBranch(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->mergeBranch(commit->commit_id);
openRepository(true);
}
void MainWindow::rebaseBranch(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QString text = tr("Are you sure you want to rebase the commit ?");
text += "\n\n";
text += "> git rebase " + commit->commit_id;
int r = QMessageBox::information(this, tr("Rebase"), text, QMessageBox::Ok, QMessageBox::Cancel);
if (r == QMessageBox::Ok) {
g->rebaseBranch(commit->commit_id);
openRepository(true);
}
}
void MainWindow::cherrypick(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->cherrypick(commit->commit_id);
openRepository(true);
}
-void MainWindow::checkout()
-{
- checkout(this, selectedCommitItem());
-}
-
-void MainWindow::deleteBranch()
-{
- deleteBranch(selectedCommitItem());
-}
-
void MainWindow::on_action_repo_checkout_triggered()
{
checkout();
}
void MainWindow::on_action_delete_branch_triggered()
{
deleteBranch();
}
-bool MainWindow::runOnRepositoryDir(std::function<void(QString)> callback, RepositoryItem const *repo)
-{
- if (!repo) {
- repo = &m->current_repo;
- }
- QString dir = repo->local_dir;
- dir.replace('\\', '/');
- if (QFileInfo(dir).isDir()) {
- callback(dir);
- return true;
- }
- QMessageBox::warning(this, qApp->applicationName(), tr("No repository selected"));
- return false;
-}
-
#ifdef Q_OS_MAC
namespace {
bool isValidDir(QString const &dir)
{
if (dir.indexOf('\"') >= 0 || dir.indexOf('\\') >= 0) return false;
return QFileInfo(dir).isDir();
}
}
#endif
-void MainWindow::openTerminal(RepositoryItem const *repo)
-{
- runOnRepositoryDir([](QString dir){
-#ifdef Q_OS_MAC
- if (!isValidDir(dir)) return;
- QString cmd = "open -n -a /Applications/Utilities/Terminal.app --args \"%1\"";
- cmd = cmd.arg(dir);
- QProcess::execute(cmd);
-#else
- Terminal::open(dir);
-#endif
- }, repo);
-}
-
-void MainWindow::openExplorer(RepositoryItem const *repo)
-{
- runOnRepositoryDir([](QString dir){
-#ifdef Q_OS_MAC
- if (!isValidDir(dir)) return;
- QString cmd = "open \"%1\"";
- cmd = cmd.arg(dir);
- QProcess::execute(cmd);
-#else
- QDesktopServices::openUrl(dir);
-#endif
- }, repo);
-}
-
void MainWindow::on_toolButton_terminal_clicked()
{
openTerminal(nullptr);
}
void MainWindow::on_toolButton_explorer_clicked()
{
openExplorer(nullptr);
}
-void MainWindow::pushSetUpstream(QString const &remote, QString const &branch)
-{
- if (remote.isEmpty()) return;
- if (branch.isEmpty()) return;
-
- int exitcode = 0;
- QString errormsg;
-
- reopenRepository(true, [&](GitPtr g){
- g->push_u(remote, branch, &m->pty_process);
- while (1) {
- if (m->pty_process.wait(1)) break;
- QApplication::processEvents();
- }
- exitcode = m->pty_process.getExitCode();
- errormsg = m->pty_process.getMessage();
- });
-
- if (exitcode == 128) {
- if (errormsg.indexOf("Connection refused") >= 0) {
- QMessageBox::critical(this, qApp->applicationName(), tr("Connection refused."));
- return;
- }
- }
-
- updateRemoteInfo();
-}
-
-bool MainWindow::pushSetUpstream(bool testonly)
-{
- GitPtr g = git();
- if (!isValidWorkingCopy(g)) return false;
- QStringList remotes = g->getRemotes();
-
- QString current_branch = currentBranchName();
-
- QStringList branches;
- for (Git::Branch const &b : g->branches()) {
- branches.push_back(b.name);
- }
-
- if (remotes.isEmpty() || branches.isEmpty()) {
- return false;
- }
-
- if (testonly) {
- return true;
- }
-
- PushDialog dlg(this, remotes, branches, PushDialog::RemoteBranch(QString(), current_branch));
- if (dlg.exec() == QDialog::Accepted) {
- PushDialog::Action a = dlg.action();
- if (a == PushDialog::PushSimple) {
- ui->action_push->trigger();
- } else if (a == PushDialog::PushSetUpstream) {
- QString remote = dlg.remote();
- QString branch = dlg.branch();
- pushSetUpstream(remote, branch);
- }
- return true;
- }
-
- return false;
-}
-
void MainWindow::on_action_reset_HEAD_1_triggered()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->reset_head1();
openRepository(false);
}
-void MainWindow::createRepository(QString const &dir)
-{
- CreateRepositoryDialog dlg(this, dir);
- if (dlg.exec() == QDialog::Accepted) {
- QString path = dlg.path();
- if (QFileInfo(path).isDir()) {
- if (Git::isValidWorkingCopy(path)) {
- // A valid git repository already exists there.
- } else {
- GitPtr g = git(path);
- if (g->init()) {
- QString name = dlg.name();
- if (!name.isEmpty()) {
- addWorkingCopyDir(path, name, true);
- }
- QString remote_name = dlg.remoteName();
- QString remote_url = dlg.remoteURL();
- if (!remote_name.isEmpty() && !remote_url.isEmpty()) {
- g->addRemoteURL(remote_name, remote_url);
- }
- }
- }
- } else {
- // not dir
- }
- }
-}
-
void MainWindow::on_action_create_a_repository_triggered()
{
createRepository(QString());
}
bool MainWindow::isRemoteOnline() const
{
return ui->radioButton_remote_online->isChecked();
}
void MainWindow::setNetworkingCommandsEnabled(bool f)
{
ui->action_clone->setEnabled(f);
ui->toolButton_clone->setEnabled(f);
if (!Git::isValidWorkingCopy(currentWorkingCopyDir())) {
f = false;
}
ui->action_fetch->setEnabled(f);
ui->action_pull->setEnabled(f);
ui->action_push->setEnabled(f);
ui->toolButton_fetch->setEnabled(f);
ui->toolButton_pull->setEnabled(f);
ui->toolButton_push->setEnabled(f);
}
void MainWindow::setRemoteOnline(bool f)
{
QRadioButton *rb = nullptr;
rb = f ? ui->radioButton_remote_online : ui->radioButton_remote_offline;
rb->blockSignals(true);
rb->click();
rb->blockSignals(false);
setNetworkingCommandsEnabled(f);
MySettings s;
s.beginGroup("Remote");
s.setValue("Online", f);
s.endGroup();
}
void MainWindow::on_radioButton_remote_online_clicked()
{
setRemoteOnline(true);
}
void MainWindow::on_radioButton_remote_offline_clicked()
{
setRemoteOnline(false);
}
void MainWindow::on_verticalScrollBar_log_valueChanged(int)
{
ui->widget_log->refrectScrollBar();
}
void MainWindow::on_horizontalScrollBar_log_valueChanged(int)
{
ui->widget_log->refrectScrollBar();
}
-void MainWindow::stopPtyProcess()
-{
- m->pty_process.stop();
- QDir::setCurrent(m->starting_dir);
-}
-
-void MainWindow::abortPtyProcess()
-{
- stopPtyProcess();
- m->pty_process_ok = false;
- m->interaction_canceled = true;
-}
-
void MainWindow::on_toolButton_stop_process_clicked()
{
abortPtyProcess();
}
void MainWindow::on_action_stop_process_triggered()
{
abortPtyProcess();
}
void MainWindow::on_action_exit_triggered()
{
close();
}
void MainWindow::on_action_reflog_triggered()
{
GitPtr g = git();
Git::ReflogItemList reflog;
g->reflog(&reflog);
ReflogWindow dlg(this, this, reflog);
dlg.exec();
}
void MainWindow::blame(QListWidgetItem *item)
{
QList<BlameItem> list;
QString path = getFilePath(item);
{
GitPtr g = git();
QByteArray ba = g->blame(path);
if (!ba.isEmpty()) {
char const *begin = ba.data();
char const *end = begin + ba.size();
list = BlameWindow::parseBlame(begin, end);
}
}
if (!list.isEmpty()) {
qApp->setOverrideCursor(Qt::WaitCursor);
BlameWindow win(this, path, list);
qApp->restoreOverrideCursor();
win.exec();
}
}
void MainWindow::blame()
{
blame(currentFileItem());
}
-void MainWindow::execCommitViewWindow(Git::CommitItem const *commit)
-{
- CommitViewWindow win(this, commit);
- win.exec();
-}
-
void MainWindow::on_action_repository_property_triggered()
{
execRepositoryPropertyDialog(currentWorkingCopyDir());
}
void MainWindow::on_action_set_gpg_signing_triggered()
{
GitPtr g = git();
QString global_key_id = g->signingKey(Git::Source::Global);
QString repository_key_id;
if (g->isValidWorkingCopy()) {
repository_key_id = g->signingKey(Git::Source::Local);
}
SetGpgSigningDialog dlg(this, currentRepositoryName(), global_key_id, repository_key_id);
if (dlg.exec() == QDialog::Accepted) {
g->setSigningKey(dlg.id(), dlg.isGlobalChecked());
}
}
void MainWindow::execAreYouSureYouWantToContinueConnectingDialog()
{
using TheDlg = AreYouSureYouWantToContinueConnectingDialog;
- m->interaction_mode = InteractionMode::Busy;
+ setInteractionMode(InteractionMode::Busy);
QApplication::restoreOverrideCursor();
TheDlg dlg(this);
if (dlg.exec() == QDialog::Accepted) {
TheDlg::Result r = dlg.result();
if (r == TheDlg::Result::Yes) {
- m->pty_process.writeInput("yes\n", 4);
+ getPtyProcess()->writeInput("yes\n", 4);
} else {
- m->pty_process_ok = false; // abort
- m->pty_process.writeInput("no\n", 3);
+ setPtyProcessOk(false); // abort
+ getPtyProcess()->writeInput("no\n", 3);
QThread::msleep(300);
stopPtyProcess();
}
} else {
ui->widget_log->setFocus();
- m->interaction_canceled = true;
+ setInteractionCanceled(true);
}
- m->interaction_mode = InteractionMode::Busy;
+ setInteractionMode(InteractionMode::Busy);
}
void MainWindow::onLogIdle()
{
- if (m->interaction_canceled) return;
- if (m->interaction_mode != InteractionMode::None) return;
+ if (interactionCanceled()) return;
+ if (interactionMode() != InteractionMode::None) return;
static char const are_you_sure_you_want_to_continue_connecting[] = "Are you sure you want to continue connecting (yes/no)?";
static char const enter_passphrase[] = "Enter passphrase: ";
static char const enter_passphrase_for_key[] = "Enter passphrase for key '";
static char const fatal_authentication_failed_for[] = "fatal: Authentication failed for '";
std::vector<char> vec;
ui->widget_log->retrieveLastText(&vec, 100);
if (!vec.empty()) {
std::string line;
int n = (int)vec.size();
int i = n;
while (i > 0) {
i--;
if (i + 1 < n && vec[i] == '\n') {
i++;
line.assign(&vec[i], n - i);
break;
}
}
if (!line.empty()) {
auto ExecLineEditDialog = [&](QWidget *parent, QString const &title, QString const &prompt, QString const &val, bool password){
LineEditDialog dlg(parent, title, prompt, val, password);
if (dlg.exec() == QDialog::Accepted) {
std::string ret = dlg.text().toStdString();
std::string str = ret + '\n';
- m->pty_process.writeInput(str.c_str(), str.size());
+ getPtyProcess()->writeInput(str.c_str(), str.size());
return ret;
}
abortPtyProcess();
return std::string();
};
auto Match = [&](char const *str){
int n = strlen(str);
if (strncmp(line.c_str(), str, n) == 0) {
char const *p = line.c_str() + n;
while (1) {
if (!*p) return true;
if (!isspace((unsigned char)*p)) break;
p++;
}
}
return false;
};
auto StartsWith = [&](char const *str){
char const *p = line.c_str();
while (*str) {
if (*p != *str) return false;
str++;
p++;
}
return true;
};
if (Match(are_you_sure_you_want_to_continue_connecting)) {
execAreYouSureYouWantToContinueConnectingDialog();
return;
}
if (line == enter_passphrase) {
ExecLineEditDialog(this, "Passphrase", QString::fromStdString(line), QString(), true);
return;
}
if (StartsWith(enter_passphrase_for_key)) {
std::string keyfile;
{
int i = strlen(enter_passphrase_for_key);
char const *p = line.c_str() + i;
char const *q = strrchr(p, ':');
if (q && p + 2 < q && q[-1] == '\'') {
keyfile.assign(p, q - 1);
}
}
if (!keyfile.empty()) {
- if (keyfile == m->ssh_passphrase_for && !m->ssh_passphrase_secret.empty()) {
- std::string text = m->ssh_passphrase_secret + '\n';
- m->pty_process.writeInput(text.c_str(), text.size());
+ if (keyfile == sshPassphraseUser() && !sshPassphrasePass().empty()) {
+ std::string text = sshPassphrasePass() + '\n';
+ getPtyProcess()->writeInput(text.c_str(), text.size());
} else {
- m->ssh_passphrase_for = keyfile;
- std::string s = ExecLineEditDialog(this, "Passphrase for key", QString::fromStdString(line), QString(), true);
- m->ssh_passphrase_secret = s;
+ std::string secret = ExecLineEditDialog(this, "Passphrase for key", QString::fromStdString(line), QString(), true);
+ sshSetPassphrase(keyfile, secret);
}
return;
}
}
char const *begin = line.c_str();
char const *end = line.c_str() + line.size();
auto Input = [&](QString const &title, bool password, std::string *value){
+ Q_ASSERT(value);
std::string header = QString("%1 for '").arg(title).toStdString();
if (strncmp(begin, header.c_str(), header.size()) == 0) {
QString msg;
if (memcmp(end - 2, "':", 2) == 0) {
msg = QString::fromUtf8(begin, end - begin - 1);
} else if (memcmp(end - 3, "': ", 3) == 0) {
msg = QString::fromUtf8(begin, end - begin - 2);
}
if (!msg.isEmpty()) {
std::string s = ExecLineEditDialog(this, title, msg, value ? QString::fromStdString(*value) : QString(), password);
- if (value) *value = s;
+ *value = s;
return true;
}
}
return false;
};
- std::string uid = m->http_uid;
- std::string pwd = m->http_pwd;
- if (Input("Username", false, &uid)) {
- m->http_uid = uid;
- return;
- }
- if (Input("Password", true, &pwd)) {
- m->http_pwd = pwd;
+ std::string uid = httpAuthenticationUser();
+ std::string pwd = httpAuthenticationPass();
+ bool ok = false;
+ if (Input("Username", false, &uid)) ok = true;
+ if (Input("Password", true, &pwd)) ok = true;
+ if (ok) {
+ httpSetAuthentication(uid, pwd);
return;
}
if (StartsWith(fatal_authentication_failed_for)) {
QMessageBox::critical(this, tr("Authentication Failed"), QString::fromStdString(line));
abortPtyProcess();
return;
}
}
}
}
-QList<Git::Tag> MainWindow::queryTagList()
-{
- QList<Git::Tag> list;
- Git::CommitItem const *commit = selectedCommitItem();
- if (commit && Git::isValidID(commit->commit_id)) {
- list = findTag(commit->commit_id);
- }
- return list;
-}
-
void MainWindow::on_action_edit_tags_triggered()
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit && Git::isValidID(commit->commit_id)) {
EditTagsDialog dlg(this, commit);
dlg.exec();
}
}
void MainWindow::on_action_push_u_triggered()
{
pushSetUpstream(false);
}
QStringList MainWindow::remoteBranches(QString const &id)
{
QStringList list;
GitPtr g = git();
if (isValidWorkingCopy(g)) {
NamedCommitList named_commits = namedCommitItems(Branches | Remotes);
JumpDialog::sort(&named_commits);
for (NamedCommitItem const &item : named_commits) {
if (item.id == id && !item.remote.isEmpty()) {
list.push_back(item.remote / item.name);
}
}
}
return list;
}
void MainWindow::deleteRemoteBranch(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QStringList remote_branches = remoteBranches(commit->commit_id);
if (remote_branches.isEmpty()) return;
DeleteBranchDialog dlg(this, true, QStringList(), remote_branches);
if (dlg.exec() == QDialog::Accepted) {
setLogEnabled(g, true);
QStringList names = dlg.selectedBranchNames();
for (QString const &name : names) {
int i = name.indexOf('/');
if (i > 0) {
QString remote = name.mid(0, i);
QString branch = ':' + name.mid(i + 1);
pushSetUpstream(remote, branch);
}
}
}
}
void MainWindow::on_action_delete_remote_branch_triggered()
{
deleteRemoteBranch(selectedCommitItem());
}
void MainWindow::rebaseOnto()
{
struct Commit {
QStringList parents;
QStringList children;
};
std::map<QString, Commit> commit_map;
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
- for (Git::CommitItem const &item : m->logs) {
+ auto const &logs = getLogs();
+ for (Git::CommitItem const &item : logs) {
Commit c;
c.parents = item.parent_ids;
commit_map[item.commit_id] = c;
}
- for (Git::CommitItem const &item : m->logs) {
+ for (Git::CommitItem const &item : logs) {
for (QString const &parent : item.parent_ids) {
auto it = commit_map.find(parent);
if (it != commit_map.end()) {
it->second.children.push_back(item.commit_id);
}
}
}
QString upstream;
- QString id = m->head_id;
+ QString id = getHeadId();
while (1) {
auto it = commit_map.find(id);
if (it == commit_map.end()) break;
Commit const &c = it->second;
if (c.parents.size() > 1) goto done;
if (c.parents.size() != 1) break;
- if (id != m->head_id) {
+ if (id != getHeadId()) {
if (c.children.size() > 1) goto done;
if (!findTag(id).isEmpty()) goto done;
- auto it2 = m->branch_map.find(id);
- if (it2 != m->branch_map.end()) {
+ auto it2 = branchMapRef().find(id);
+ if (it2 != branchMapRef().end()) {
QList<Git::Branch> b = it2->second;
if (!it2->second.empty()) goto done;
}
}
id = c.parents.first();
continue;
done:;
upstream = id;
break;
}
QString newbase = commit->commit_id;
- QString branch = m->head_id;
+ QString branch = getHeadId();
RebaseOntoDialog dlg(this);
if (dlg.exec(newbase, upstream, branch) == QDialog::Accepted) {
reopenRepository(true, [&](GitPtr g){
- m->pty_condition = PtyCondition::Pull;
- m->pty_process_ok = true;
- g->rebaseOnto(newbase, upstream, branch, &m->pty_process);
+ setPtyCondition(PtyCondition::Pull);
+ setPtyProcessOk(true);
+ g->rebaseOnto(newbase, upstream, branch, getPtyProcess());
while (1) {
- if (m->pty_process.wait(1)) break;
+ if (getPtyProcess()->wait(1)) break;
QApplication::processEvents();
}
});
}
}
}
void MainWindow::on_action_rebase_onto_triggered()
{
rebaseOnto();
}
-void MainWindow::checkRemoteUpdate()
-{
- if (m->pty_process.isRunning()) return;
-
- emit signalCheckRemoteUpdate();
-}
-
void MainWindow::on_action_test_triggered()
{
}
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 81ee9f6..d93870a 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -1,412 +1,193 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
-#include <QMainWindow>
-#include "Git.h"
-#include "RepositoryData.h"
-#include <memory>
-#include <functional>
-#include "GitHubAPI.h"
-#include "main.h"
-#include "TextEditorTheme.h"
+#include "BasicMainWindow.h"
namespace Ui {
class MainWindow;
}
-class QScrollBar;
-
-class QListWidget;
-class QListWidgetItem;
-class QTreeWidgetItem;
class QTableWidgetItem;
-class AboutDialog;
-
-class MySettings;
-
-class LocalSocketReader;
-
-class CommitList;
-class WebContext;
-
-#define PATH_PREFIX "*"
-
class HunkItem {
public:
int hunk_number = -1;
size_t pos, len;
std::vector<std::string> lines;
};
-class MainWindow : public QMainWindow {
+class MainWindow : public BasicMainWindow {
Q_OBJECT
friend class ImageViewWidget;
friend class FileDiffSliderWidget;
friend class FileHistoryWindow;
friend class FileDiffWidget;
friend class AboutDialog;
-public:
- struct Label {
- enum {
- Head,
- LocalBranch,
- RemoteBranch,
- Tag,
- };
- int kind;
- QString text;
- Label(int kind = LocalBranch)
- : kind(kind)
- {
-
- }
- };
private:
struct Private;
Private *m;
public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
+ explicit MainWindow(QWidget *parent = nullptr);
+ ~MainWindow() override;
QPixmap const &digitsPixmap() const;
- QString currentWorkingCopyDir() const;
+ QString currentWorkingCopyDir() const override;
- static QString makeRepositoryName(QString const &loc);
- const Git::CommitItemList *logs() const;
QColor color(unsigned int i);
private:
Ui::MainWindow *ui;
- enum class FilesListType {
- SingleList,
- SideBySide,
- };
-
- void addDiffItems(const QList<Git::Diff> *diff_list, std::function<void (QString const &, QString, int)> const &AddItem_);
- void updateFilesList(QString id, bool wait);
+ void updateFilesList(QString id, bool wait) override;
void updateFilesList(const Git::CommitItem &commit, bool wait);
- void updateRepositoriesList();
- QString getBookmarksFilePath() const;
- bool saveRepositoryBookmarks() const;
+ void updateRepositoriesList() override;
- void openRepository_(GitPtr g);
- void openRepository(bool validate, bool waitcursor = true);
- void reopenRepository(bool log, std::function<void(GitPtr)> callback);
+ void openRepository_(GitPtr g) override;
- void openSelectedRepository();
- bool askAreYouSureYouWantToRun(QString const &title, QString const &command);
- void resetFile(QStringList const &path);
- void revertAllFiles();
void prepareLogTableWidget();
QStringList selectedFiles_(QListWidget *listwidget) const;
QStringList selectedFiles() const;
void for_each_selected_files(std::function<void (QString const &)> fn);
- bool editFile(QString const &path, QString const &title);
void updateCommitGraph();
void showFileList(FilesListType files_list_type);
- bool checkGitCommand();
- bool checkFileCommand();
- void checkUser();
-
void clearLog();
- void clearFileList();
+ void clearFileList() override;
void clearDiffView();
void clearRepositoryInfo();
int repositoryIndex_(const QTreeWidgetItem *item) const;
RepositoryItem const *repositoryItem(const QTreeWidgetItem *item) const;
- bool makeDiff(QString id, QList<Git::Diff> *out);
-
- void commit(bool amend = false);
- void commit_amend();
- void queryBranches(GitPtr g);
- void updateRemoteInfo();
- QList<Git::Branch> findBranch(QString const &id);
- QList<Git::Tag> findTag(QString const &id);
- int selectedLogIndex() const;
- const Git::CommitItem *selectedCommitItem() const;
- void deleteTags(const Git::CommitItem &commit);
+ int selectedLogIndex() const override;
QTreeWidgetItem *newQTreeWidgetFolderItem(QString const &name);
void buildRepoTree(QString const &group, QTreeWidgetItem *item, QList<RepositoryItem> *repos);
void refrectRepositories();
- bool saveByteArrayAs(QByteArray const &ba, QString const &dstpath);
- bool saveFileAs(QString const &srcpath, QString const &dstpath);
- bool saveBlobAs(QString const &id, QString const &dstpath);
- QString selectCommand_(QString const &cmdname, QStringList const &cmdfile, QStringList const &list, QString path, std::function<void (QString const &)> callback);
- QString selectCommand_(QString const &cmdname, QString const &cmdfile, QStringList const &list, QString path, std::function<void(QString const &)> callback);
void updateDiffView(QListWidgetItem *item);
void updateDiffView();
void updateUnstagedFileCurrentItem();
void updateStagedFileCurrentItem();
-// void addTag();
- QStringList whichCommand_(QString const &cmdfile1, QString const &cmdfile2 = QString());
- QString getObjectID(QListWidgetItem *item);
- QString determinFileType_(QString const &path, bool mime, std::function<void(QString const &cmd, QByteArray *ba)> callback) const;
- Git::Object cat_file_(GitPtr g, QString const &id);
- Git::Object cat_file(QString const &id);
void updateStatusBarText();
- void setUnknownRepositoryInfo();
- void setWindowTitle_(const Git::User &user);
- void setRepositoryInfo(QString const &reponame, QString const &brname);
- void updateWindowTitle(GitPtr g);
- void logGitVersion();
- static bool git_callback(void *cookie, char const *ptr, int len);
+ void setRepositoryInfo(QString const &reponame, QString const &brname) override;
int indexOfRepository(const QTreeWidgetItem *treeitem) const;
- void removeRepositoryFromBookmark(int index, bool ask);
-
- enum NamedCommitFlag {
- Branches = 0x0001,
- Tags = 0x0002,
- Remotes = 0x0100,
- };
- NamedCommitList namedCommitItems(int flags);
-
- void deleteBranch(const Git::CommitItem *commit);
- void checkout();
- void clone();
- void deleteBranch();
- Git::CommitItemList retrieveCommitLog(GitPtr g);
- bool runOnRepositoryDir(std::function<void(QString)> callback, const RepositoryItem *repo);
- void openTerminal(const RepositoryItem *repo);
- void openExplorer(const RepositoryItem *repo);
- void pushSetUpstream(QString const &remote, QString const &branch);
- bool pushSetUpstream(bool testonly);
void clearRepoFilter();
void appendCharToRepoFilter(ushort c);
void backspaceRepoFilter();
void revertCommit();
- int rowFromCommitId(QString const &id);
void cherrypick(const Git::CommitItem *commit);
void mergeBranch(const Git::CommitItem *commit);
void rebaseBranch(const Git::CommitItem *commit);
void detectGitServerType(GitPtr g);
- void initNetworking();
void setRemoteOnline(bool f);
- bool isRemoteOnline() const;
+ bool isRemoteOnline() const override;
void startTimers();
void onCloneCompleted(bool success);
bool fetch(GitPtr g);
- void stopPtyProcess();
- void abortPtyProcess();
void setNetworkingCommandsEnabled(bool f);
- void execSetUserDialog(const Git::User &global_user, const Git::User &repo_user, QString const &reponame);
- bool execSetGlobalUserDialog();
void blame(QListWidgetItem *item);
void blame();
QListWidgetItem *currentFileItem() const;
- const RepositoryItem *findRegisteredRepository(QString *workdir) const;
void execAreYouSureYouWantToContinueConnectingDialog();
- void queryRemotes(GitPtr g);
- void internalSetCommand(QString const &path, bool save, QString const &name, QString *out);
void deleteRemoteBranch(const Git::CommitItem *commit);
QStringList remoteBranches(QString const &id);
void rebaseOnto();
- void createRepository(QString const &dir);
- void checkRemoteUpdate();
void setWatchRemoteInterval(int mins);
- void emitWriteLog(QByteArray ba);
protected:
- void dragEnterEvent(QDragEnterEvent *event);
- void timerEvent(QTimerEvent *);
- void keyPressEvent(QKeyEvent *event);
- bool event(QEvent *event);
- bool eventFilter(QObject *watched, QEvent *event);
+ void dragEnterEvent(QDragEnterEvent *event) override;
+ void timerEvent(QTimerEvent *) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ bool event(QEvent *event) override;
+ bool eventFilter(QObject *watched, QEvent *event) override;
public:
-
- QString selectGitCommand(bool save);
- QString selectFileCommand(bool save);
- QString selectGpgCommand(bool save);
-
- int limitLogCount() const;
-
- void setGitCommand(QString const &path, bool save);
- void setFileCommand(QString const &path, bool save);
- void setGpgCommand(QString const &path, bool save);
- void clearAuthentication();
- void setCurrentRepository(const RepositoryItem &repo, bool clear_authentication);
- bool isThereUncommitedChanges() const;
- QString defaultWorkingDir() const;
- void autoOpenRepository(QString dir);
- void saveRepositoryBookmark(RepositoryItem item);
void drawDigit(QPainter *pr, int x, int y, int n) const;
int digitWidth() const;
int digitHeight() const;
- bool isValidWorkingCopy(const GitPtr &g) const;
- QString tempfileHeader() const;
- void deleteTempFiles();
- QString newTempFilePath();
- bool saveAs(QString const &id, QString const &dstpath);
- QString saveAsTemp(QString const &id);
- QString abbrevCommitID(const Git::CommitItem &commit);
- QString findFileID(GitPtr g, QString const &commit_id, QString const &file);
- QString determinFileType(QString const &path, bool mime);
- QString determinFileType(QByteArray in, bool mime);
- QPixmap getTransparentPixmap();
- QString getCommitIdFromTag(QString const &tag);
void setStatusBarText(QString const &text);
void clearStatusBarText();
- QString makeCommitInfoText(int row, QList<Label> *label_list);
- void setLogEnabled(GitPtr g, bool f);
- void addWorkingCopyDir(QString dir, QString name, bool open);
- void addWorkingCopyDir(QString dir, bool open)
- {
- addWorkingCopyDir(dir, QString(), open);
- }
- bool isValidRemoteURL(QString const &url);
- bool testRemoteRepositoryValidity(QString const &url);
- void removeSelectedRepositoryFromBookmark(bool ask);
- void setCurrentLogRow(int row);
- GitPtr git(QString const &dir) const;
- GitPtr git();
- const QList<Label> *label(int row);
- bool isGitHub() const;
- QIcon committerIcon(int row) const;
- void updateCommitTableLater();
- bool isAvatarEnabled() const;
- WebContext *getWebContextPtr();
+ void setCurrentLogRow(int row) override;
bool shown();
- void execFileHistory(QListWidgetItem *item);
- void execFileHistory(QString const &path);
- void execCommitExploreWindow(QWidget *parent, const Git::CommitItem *commit);
- void execCommitPropertyDialog(QWidget *parent, const Git::CommitItem *commit);
- void checkout(QWidget *parent, const Git::CommitItem *commit);
- TextEditorThemePtr themeForTextEditor();
- bool queryCommit(QString const &id, Git::CommitItem *out);
- void jumpToCommit(QString id);
- WebContext *webContext();
- bool execWelcomeWizardDialog();
- void updateFilesList(QString id, QList<Git::Diff> *diff_list, QListWidget *listwidget);
- void execCommitViewWindow(const Git::CommitItem *commit);
- QAction *addMenuActionProperty(QMenu *menu);
- void execFilePropertyDialog(QListWidgetItem *item);
- void execRepositoryPropertyDialog(QString workdir, bool open_repository_menu = false);
- QStringList remotes() const;
- QIcon verifiedIcon(char s) const;
- ApplicationSettings *appsettings();
- const ApplicationSettings *appsettings() const;
- const Git::CommitItem *commitItem(int row) const;
- bool checkExecutable(QString const &path);
- void deleteTags(QStringList const &tagnames);
- QList<Git::Tag> queryTagList();
+ void deleteTags(QStringList const &tagnames) override;
bool addTag(QString const &name);
void updateCurrentFilesList();
- void setRemoteChanged(bool f);
- QString currentRepositoryName() const;
- QString currentRemoteName() const;
- Git::Branch const &currentBranch() const;
- QString currentBranchName() const;
-public slots:
- void writeLog(char const *ptr, int len);
- void writeLog(QString const &str);
- void writeLog(QByteArray ba);
private slots:
+ void doUpdateButton();
+ void onLogVisibilityChanged();
+ void onPtyProcessCompleted();
+ void onRepositoriesTreeDropped();
+ void on_action_about_triggered();
void on_action_clone_triggered();
void on_action_commit_triggered();
+ void on_action_create_a_repository_triggered();
+ void on_action_delete_branch_triggered();
+ void on_action_delete_remote_branch_triggered();
void on_action_edit_git_config_triggered();
void on_action_edit_gitignore_triggered();
void on_action_edit_global_gitconfig_triggered();
void on_action_edit_settings_triggered();
+ void on_action_edit_tags_triggered();
+ void on_action_exit_triggered();
void on_action_fetch_triggered();
void on_action_open_existing_working_copy_triggered();
void on_action_pull_triggered();
void on_action_push_triggered();
+ void on_action_rebase_onto_triggered();
+ void on_action_reflog_triggered();
+ void on_action_repo_checkout_triggered();
+ void on_action_repo_jump_triggered();
+ void on_action_repository_property_triggered();
+ void on_action_reset_HEAD_1_triggered();
+ void on_action_set_config_user_triggered();
+ void on_action_set_gpg_signing_triggered();
+ void on_action_stop_process_triggered();
+ void on_action_tag_push_all_triggered();
void on_action_test_triggered();
void on_action_view_refresh_triggered();
- void on_action_tag_push_all_triggered();
-
- void on_treeWidget_repos_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
- void on_treeWidget_repos_itemDoubleClicked(QTreeWidgetItem *item, int column);
-
- void on_tableWidget_log_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
-
- void on_treeWidget_repos_customContextMenuRequested(const QPoint &pos);
- void on_tableWidget_log_customContextMenuRequested(const QPoint &pos);
+ void on_action_window_log_triggered(bool checked);
+ void on_horizontalScrollBar_log_valueChanged(int);
+ void on_lineEdit_filter_textChanged(QString const &text);
+ void on_listWidget_files_currentRowChanged(int currentRow);
void on_listWidget_files_customContextMenuRequested(const QPoint &pos);
- void on_listWidget_unstaged_customContextMenuRequested(const QPoint &pos);
+ void on_listWidget_files_itemDoubleClicked(QListWidgetItem *item);
+ void on_listWidget_staged_currentRowChanged(int currentRow);
void on_listWidget_staged_customContextMenuRequested(const QPoint &pos);
-
+ void on_listWidget_staged_itemDoubleClicked(QListWidgetItem *item);
void on_listWidget_unstaged_currentRowChanged(int currentRow);
- void on_listWidget_staged_currentRowChanged(int currentRow);
- void on_listWidget_files_currentRowChanged(int currentRow);
-
+ void on_listWidget_unstaged_customContextMenuRequested(const QPoint &pos);
+ void on_listWidget_unstaged_itemDoubleClicked(QListWidgetItem *item);
+ void on_radioButton_remote_offline_clicked();
+ void on_radioButton_remote_online_clicked();
+ void on_tableWidget_log_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
+ void on_tableWidget_log_customContextMenuRequested(const QPoint &pos);
+ void on_tableWidget_log_itemDoubleClicked(QTableWidgetItem *);
+ void on_toolButton_clone_clicked();
void on_toolButton_commit_clicked();
+ void on_toolButton_erase_filter_clicked();
+ void on_toolButton_explorer_clicked();
+ void on_toolButton_fetch_clicked();
void on_toolButton_pull_clicked();
void on_toolButton_push_clicked();
void on_toolButton_select_all_clicked();
void on_toolButton_stage_clicked();
- void on_toolButton_unstage_clicked();
- void on_action_about_triggered();
- void on_toolButton_clone_clicked();
- void on_toolButton_fetch_clicked();
-
- void on_lineEdit_filter_textChanged(QString const &text);
- void on_toolButton_erase_filter_clicked();
-
- void on_tableWidget_log_itemDoubleClicked(QTableWidgetItem *);
- void on_listWidget_unstaged_itemDoubleClicked(QListWidgetItem *item);
- void on_listWidget_staged_itemDoubleClicked(QListWidgetItem *item);
- void on_listWidget_files_itemDoubleClicked(QListWidgetItem *item);
-
- void onRepositoriesTreeDropped();
- void on_action_set_config_user_triggered();
-
- void on_action_window_log_triggered(bool checked);
-
- void onLogVisibilityChanged();
-
- void on_action_repo_jump_triggered();
- void on_action_repo_checkout_triggered();
- void on_action_delete_branch_triggered();
-
+ void on_toolButton_stop_process_clicked();
void on_toolButton_terminal_clicked();
- void on_toolButton_explorer_clicked();
-
- void on_action_reset_HEAD_1_triggered();
-
- void on_action_create_a_repository_triggered();
-
- void onAvatarUpdated();
- void on_radioButton_remote_online_clicked();
-
- void on_radioButton_remote_offline_clicked();
+ void on_toolButton_unstage_clicked();
+ void on_treeWidget_repos_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
+ void on_treeWidget_repos_customContextMenuRequested(const QPoint &pos);
+ void on_treeWidget_repos_itemDoubleClicked(QTreeWidgetItem *item, int column);
void on_verticalScrollBar_log_valueChanged(int);
- void on_horizontalScrollBar_log_valueChanged(int);
-
- void onPtyProcessCompleted();
- void on_toolButton_stop_process_clicked();
-
- void on_action_stop_process_triggered();
-
- void on_action_exit_triggered();
-
- void on_action_reflog_triggered();
-
-
- void on_action_repository_property_triggered();
- void on_action_set_gpg_signing_triggered();
- void on_action_edit_tags_triggered();
-
- void on_action_push_u_triggered();
-
- void on_action_delete_remote_branch_triggered();
-
- void on_action_rebase_onto_triggered();
-
- void doUpdateButton();
-signals:
- void onEscapeKeyPressed();
- void signalCheckRemoteUpdate();
- void updateButton();
- void signalWriteLog(QByteArray ba);
+ void on_action_push_u_triggered();
protected:
- void closeEvent(QCloseEvent *event);
+ void closeEvent(QCloseEvent *event) override;
+ void internalWriteLog(const char *ptr, int len) override;
+ const RepositoryItem *selectedRepositoryItem() const override;
+ void removeSelectedRepositoryFromBookmark(bool ask) override;
protected slots:
void onLogIdle();
+signals:
+ void signalSetRemoteChanged(bool f);
+ void onEscapeKeyPressed();
+ void updateButton();
};
#endif // MAINWINDOW_H
diff --git a/src/MainWindow.ui b/src/MainWindow.ui
index 8438b65..8afa631 100644
--- a/src/MainWindow.ui
+++ b/src/MainWindow.ui
@@ -1,1518 +1,1518 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>848</width>
<height>596</height>
</rect>
</property>
<property name="windowTitle">
<string>Guitar</string>
</property>
<property name="animated">
<bool>false</bool>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_8">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>56</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>56</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>1</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="MyToolButton" name="toolButton_clone">
<property name="minimumSize">
<size>
<width>64</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>48</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Clone</string>
</property>
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/clone.svg</normaloff>:/image/clone.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
<item>
<widget class="MyToolButton" name="toolButton_fetch">
<property name="minimumSize">
<size>
<width>64</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>48</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Fetch</string>
</property>
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/fetch.svg</normaloff>:/image/fetch.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
<item>
<widget class="MyToolButton" name="toolButton_pull">
<property name="minimumSize">
<size>
<width>64</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>48</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Pull</string>
</property>
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/pull.svg</normaloff>:/image/pull.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
<item>
<widget class="MyToolButton" name="toolButton_push">
<property name="minimumSize">
<size>
<width>64</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>48</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Push</string>
</property>
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/push.svg</normaloff>:/image/push.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>8</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="MyToolButton" name="toolButton_terminal">
<property name="minimumSize">
<size>
<width>64</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>48</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Terminal</string>
</property>
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/terminal.svg</normaloff>:/image/terminal.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
<item>
<widget class="MyToolButton" name="toolButton_explorer">
<property name="minimumSize">
<size>
<width>64</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>48</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Explorer</string>
</property>
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/explorer.svg</normaloff>:/image/explorer.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>8</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>503</width>
<height>48</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="RepositoryInfoFrame" name="frame_3">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>48</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QLabel" name="label_repo_name">
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Repository</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_branch_name">
<property name="text">
<string>Branch Name</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>4</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_10">
<property name="spacing">
<number>1</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="spacing">
<number>4</number>
</property>
<item>
<widget class="QRadioButton" name="radioButton_remote_offline">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Offline</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_remote_online">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Online</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="ReadOnlyLineEdit" name="lineEdit_remote">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QSplitter" name="splitter_v">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="RepositoriesTreeWidget" name="treeWidget_repos">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_5">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="lineEdit_filter">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
</widget>
</item>
<item>
<widget class="ClearButton" name="toolButton_erase_filter">
<property name="minimumSize">
<size>
<width>22</width>
<height>17</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>15</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QSplitter" name="splitter_h">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QStackedWidget" name="stackedWidget_log">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_log_table">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="LogTableWidget" name="tableWidget_log">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="tabKeyNavigation">
<bool>false</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_log_progress">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>244</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>243</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_uncommited">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0,1">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="listWidget_unstaged">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="1,1">
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="2" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="MyToolButton" name="toolButton_unstage">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="text">
<string>Unstage</string>
</property>
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/unstage.svg</normaloff>:/image/unstage.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="MyToolButton" name="toolButton_select_all">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="text">
<string>Select all</string>
</property>
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/selall.svg</normaloff>:/image/selall.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>18</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="MyToolButton" name="toolButton_stage">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="text">
<string>Stage</string>
</property>
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/stage.svg</normaloff>:/image/stage.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="MyToolButton" name="toolButton_commit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="text">
<string>Commit</string>
</property>
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/commit.svg</normaloff>:/image/commit.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QListWidget" name="listWidget_staged">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_files">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="listWidget_files">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QFrame" name="frame_6">
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="FileDiffWidget" name="widget_diff_view" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>848</width>
- <height>18</height>
+ <height>24</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
<property name="title">
<string>&amp;File</string>
</property>
<addaction name="action_clone"/>
<addaction name="action_open_existing_working_copy"/>
<addaction name="action_create_a_repository"/>
<addaction name="separator"/>
<addaction name="action_stop_process"/>
<addaction name="separator"/>
<addaction name="action_exit"/>
</widget>
<widget class="QMenu" name="menu_View">
<property name="title">
<string>&amp;View</string>
</property>
<addaction name="action_view_refresh"/>
</widget>
<widget class="QMenu" name="menu_Edit">
<property name="title">
<string>&amp;Edit</string>
</property>
<addaction name="action_edit_gitignore"/>
<addaction name="action_edit_git_config"/>
<addaction name="action_edit_global_gitconfig"/>
<addaction name="action_set_config_user"/>
<addaction name="action_set_gpg_signing"/>
<addaction name="separator"/>
<addaction name="action_edit_settings"/>
</widget>
<widget class="QMenu" name="menu_Help">
<property name="title">
<string>&amp;Help</string>
</property>
<addaction name="action_about"/>
</widget>
<widget class="QMenu" name="menu_Window">
<property name="title">
<string>&amp;Window</string>
</property>
<addaction name="action_window_log"/>
</widget>
<widget class="QMenu" name="menu_Repository">
<property name="title">
<string>&amp;Repository</string>
</property>
<addaction name="action_fetch"/>
<addaction name="action_commit"/>
<addaction name="action_pull"/>
<addaction name="action_push"/>
<addaction name="action_push_u"/>
<addaction name="separator"/>
<addaction name="action_repo_jump"/>
<addaction name="action_repo_checkout"/>
<addaction name="action_delete_branch"/>
<addaction name="action_delete_remote_branch"/>
<addaction name="action_edit_tags"/>
<addaction name="action_reflog"/>
<addaction name="separator"/>
<addaction name="action_clone"/>
<addaction name="separator"/>
<addaction name="action_repository_property"/>
</widget>
<widget class="QMenu" name="menuExperiment">
<property name="title">
<string>Experiment</string>
</property>
<addaction name="action_test"/>
<addaction name="action_tag_push_all"/>
<addaction name="action_reset_HEAD_1"/>
<addaction name="action_rebase_onto"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_View"/>
<addaction name="menu_Edit"/>
<addaction name="menu_Repository"/>
<addaction name="menu_Window"/>
<addaction name="menu_Help"/>
<addaction name="menuExperiment"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<widget class="QDockWidget" name="dockWidget_log">
<property name="floating">
<bool>false</bool>
</property>
<property name="features">
<set>QDockWidget::DockWidgetFeatureMask</set>
</property>
<property name="allowedAreas">
<set>Qt::BottomDockWidgetArea|Qt::TopDockWidgetArea</set>
</property>
<property name="windowTitle">
<string>Log</string>
</property>
<attribute name="dockWidgetArea">
<number>8</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_8">
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>1</number>
</property>
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item>
<widget class="QToolButton" name="toolButton_stop_process">
<property name="minimumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/redsquare.svg</normaloff>:/image/redsquare.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>12</width>
<height>12</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>45</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="1">
<widget class="QScrollBar" name="verticalScrollBar_log">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="TextEditorWidget" name="widget_log" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>80</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QScrollBar" name="horizontalScrollBar_log">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<action name="action_open_existing_working_copy">
<property name="text">
<string>&amp;Open existing working copy...</string>
</property>
<property name="toolTip">
<string>Add existing working copy</string>
</property>
</action>
<action name="action_view_refresh">
<property name="text">
<string>Refresh</string>
</property>
<property name="toolTip">
<string>Refresh</string>
</property>
<property name="shortcut">
<string>F5</string>
</property>
</action>
<action name="action_commit">
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/commit.svg</normaloff>:/image/commit.svg</iconset>
</property>
<property name="text">
<string>&amp;Commit</string>
</property>
</action>
<action name="action_push">
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/push.svg</normaloff>:/image/push.svg</iconset>
</property>
<property name="text">
<string>&amp;Push</string>
</property>
<property name="autoRepeat">
<bool>false</bool>
</property>
</action>
<action name="action_test">
<property name="text">
<string>test</string>
</property>
<property name="shortcut">
<string>Ctrl+T</string>
</property>
</action>
<action name="action_pull">
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/pull.svg</normaloff>:/image/pull.svg</iconset>
</property>
<property name="text">
<string>Pu&amp;ll</string>
</property>
</action>
<action name="action_fetch">
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/fetch.svg</normaloff>:/image/fetch.svg</iconset>
</property>
<property name="text">
<string>&amp;Fetch</string>
</property>
</action>
<action name="action_edit_global_gitconfig">
<property name="text">
<string>Edit global .gitconfig</string>
</property>
<property name="toolTip">
<string>Edit global .gitconfig</string>
</property>
</action>
<action name="action_edit_git_config">
<property name="text">
<string>Edit .git/config</string>
</property>
</action>
<action name="action_edit_gitignore">
<property name="text">
<string>Edit .gitignore</string>
</property>
</action>
<action name="action_edit_settings">
<property name="text">
<string>&amp;Settings...</string>
</property>
<property name="toolTip">
<string>Settings</string>
</property>
</action>
<action name="action_clone">
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/clone.svg</normaloff>:/image/clone.svg</iconset>
</property>
<property name="text">
<string>Clone</string>
</property>
<property name="toolTip">
<string>Clone</string>
</property>
</action>
<action name="action_about">
<property name="text">
<string>&amp;About</string>
</property>
</action>
<action name="action_edit_tags">
<property name="text">
<string>Edit tags...</string>
</property>
<property name="toolTip">
<string>Edit tags</string>
</property>
</action>
<action name="action_tag_push_all">
<property name="text">
<string>Push all tags</string>
</property>
</action>
<action name="action_set_config_user">
<property name="text">
<string>Set config user</string>
</property>
</action>
<action name="action_window_log">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Log</string>
</property>
</action>
<action name="action_repo_jump">
<property name="text">
<string>&amp;Jump...</string>
</property>
<property name="shortcut">
<string>Ctrl+J</string>
</property>
</action>
<action name="action_repo_checkout">
<property name="text">
<string>Check&amp;out...</string>
</property>
</action>
<action name="action_delete_branch">
<property name="text">
<string>Delete branch...</string>
</property>
</action>
<action name="action_push_u">
<property name="text">
<string>Push --set-upstream</string>
</property>
</action>
<action name="action_reset_HEAD_1">
<property name="text">
<string>reset HEAD~1</string>
</property>
</action>
<action name="action_create_a_repository">
<property name="text">
<string>Create a repository</string>
</property>
</action>
<action name="action_stop_process">
<property name="icon">
- <iconset resource="resources/resources.qrc">
+ <iconset resource="../resources.qrc">
<normaloff>:/image/redsquare.svg</normaloff>:/image/redsquare.svg</iconset>
</property>
<property name="text">
<string>Stop process</string>
</property>
</action>
<action name="action_exit">
<property name="text">
<string>E&amp;xit</string>
</property>
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
</action>
<action name="action_reflog">
<property name="text">
<string>Reflog...</string>
</property>
</action>
<action name="action_repository_property">
<property name="text">
<string>Property...</string>
</property>
</action>
<action name="action_set_gpg_signing">
<property name="text">
<string>Set GPG signing</string>
</property>
<property name="toolTip">
<string>Set GPG signing</string>
</property>
</action>
<action name="action_delete_remote_branch">
<property name="text">
<string>Delete remote branch...</string>
</property>
</action>
<action name="action_rebase_onto">
<property name="text">
<string>rebase onto</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
- <customwidget>
- <class>ReadOnlyLineEdit</class>
- <extends>QLineEdit</extends>
- <header>ReadOnlyLineEdit.h</header>
- </customwidget>
<customwidget>
<class>TextEditorWidget</class>
<extends>QWidget</extends>
<header>TextEditorWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ClearButton</class>
<extends>QToolButton</extends>
<header>ClearButton.h</header>
</customwidget>
<customwidget>
<class>LogTableWidget</class>
<extends>QTableWidget</extends>
<header>LogTableWidget.h</header>
</customwidget>
<customwidget>
<class>RepositoryInfoFrame</class>
<extends>QFrame</extends>
<header>RepositoryInfoFrame.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>MyToolButton</class>
<extends>QToolButton</extends>
<header>MyToolButton.h</header>
</customwidget>
<customwidget>
<class>RepositoriesTreeWidget</class>
<extends>QTreeWidget</extends>
<header>RepositoriesTreeWidget.h</header>
</customwidget>
<customwidget>
<class>FileDiffWidget</class>
<extends>QWidget</extends>
<header location="global">FileDiffWidget.h</header>
<container>1</container>
</customwidget>
+ <customwidget>
+ <class>ReadOnlyLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>ReadOnlyLineEdit.h</header>
+ </customwidget>
</customwidgets>
<tabstops>
<tabstop>treeWidget_repos</tabstop>
<tabstop>toolButton_erase_filter</tabstop>
<tabstop>tableWidget_log</tabstop>
<tabstop>listWidget_unstaged</tabstop>
<tabstop>toolButton_select_all</tabstop>
<tabstop>toolButton_stage</tabstop>
<tabstop>toolButton_unstage</tabstop>
<tabstop>toolButton_commit</tabstop>
<tabstop>listWidget_staged</tabstop>
<tabstop>listWidget_files</tabstop>
<tabstop>widget_diff_view</tabstop>
</tabstops>
<resources>
- <include location="resources/resources.qrc"/>
+ <include location="../resources.qrc"/>
</resources>
<connections/>
</ui>
diff --git a/src/MaximizeButton.h b/src/MaximizeButton.h
index 9df2728..cf7caa0 100644
--- a/src/MaximizeButton.h
+++ b/src/MaximizeButton.h
@@ -1,16 +1,16 @@
#ifndef MAXIMIZEBUTTON_H
#define MAXIMIZEBUTTON_H
#include <QToolButton>
class MaximizeButton : public QToolButton {
Q_OBJECT
private:
QPixmap pixmap;
public:
- explicit MaximizeButton(QWidget *parent = 0);
+ explicit MaximizeButton(QWidget *parent = nullptr);
protected:
- void paintEvent(QPaintEvent *event);
+ void paintEvent(QPaintEvent *event) override;
};
#endif // MAXIMIZEBUTTON_H
diff --git a/src/MemoryReader.cpp b/src/MemoryReader.cpp
index 7c3112a..97d6e79 100644
--- a/src/MemoryReader.cpp
+++ b/src/MemoryReader.cpp
@@ -1,91 +1,88 @@
#include "MemoryReader.h"
MemoryReader::MemoryReader(char const *ptr, qint64 len)
{
setData(ptr, len);
}
void MemoryReader::setData(char const *ptr, qint64 len)
{
begin = ptr;
end = begin + len;
}
bool MemoryReader::isSequential() const
{
return false;
}
bool MemoryReader::open(OpenMode mode)
{
mode |= QIODevice::Unbuffered;
return QIODevice::open(mode | QIODevice::Unbuffered);
}
qint64 MemoryReader::pos() const
{
return QIODevice::pos();
}
qint64 MemoryReader::size() const
{
if (begin && begin < end) {
return end - begin;
}
return 0;
}
bool MemoryReader::seek(qint64 pos)
{
return QIODevice::seek(pos);
}
bool MemoryReader::atEnd() const
{
return QIODevice::atEnd();
}
bool MemoryReader::reset()
{
- if (begin && begin < end) {
- return true;
- }
- return false;
+ return begin && begin < end;
}
qint64 MemoryReader::bytesToWrite() const
{
return 0;
}
bool MemoryReader::canReadLine() const
{
return bytesAvailable() > 0;
}
bool MemoryReader::waitForReadyRead(int /*msecs*/)
{
return bytesAvailable() > 0;
}
bool MemoryReader::waitForBytesWritten(int /*msecs*/)
{
return false;
}
qint64 MemoryReader::readData(char *data, qint64 maxlen)
{
qint64 n = bytesAvailable();
if (n > 0) {
if (n > maxlen) {
n = maxlen;
}
memcpy(data, begin + pos(), n);
}
return n;
}
qint64 MemoryReader::writeData(char const * /*data*/, qint64 /*len*/)
{
return 0;
}
diff --git a/src/MemoryReader.h b/src/MemoryReader.h
index d8ecc5a..20bd0e1 100644
--- a/src/MemoryReader.h
+++ b/src/MemoryReader.h
@@ -1,30 +1,30 @@
#ifndef MEMORYREADER_H
#define MEMORYREADER_H
#include <QBuffer>
#include <QIODevice>
class MemoryReader : public QIODevice {
private:
char const *begin;
char const *end;
public:
- MemoryReader(char const *ptr = 0, qint64 len = 0);
+ MemoryReader(char const *ptr = nullptr, qint64 len = 0);
void setData(char const *ptr, qint64 len);
- virtual bool isSequential() const;
- virtual bool open(OpenMode mode);
- virtual qint64 pos() const;
- virtual qint64 size() const;
- virtual bool seek(qint64 pos);
- virtual bool atEnd() const;
- virtual bool reset();
- virtual qint64 bytesToWrite() const;
- virtual bool canReadLine() const;
- virtual bool waitForReadyRead(int msecs);
- virtual bool waitForBytesWritten(int msecs);
+ bool isSequential() const override;
+ bool open(OpenMode mode) override;
+ qint64 pos() const override;
+ qint64 size() const override;
+ bool seek(qint64 pos) override;
+ bool atEnd() const override;
+ bool reset() override;
+ qint64 bytesToWrite() const override;
+ bool canReadLine() const override;
+ bool waitForReadyRead(int msecs) override;
+ bool waitForBytesWritten(int msecs) override;
protected:
- virtual qint64 readData(char *data, qint64 maxlen);
- virtual qint64 writeData(char const *data, qint64 len);
+ qint64 readData(char *data, qint64 maxlen) override;
+ qint64 writeData(char const *data, qint64 len) override;
};
#endif // MEMORYREADER_H
diff --git a/src/MenuButton.h b/src/MenuButton.h
index d7d9122..ab8fbaa 100644
--- a/src/MenuButton.h
+++ b/src/MenuButton.h
@@ -1,16 +1,16 @@
#ifndef MENUBUTTON_H
#define MENUBUTTON_H
#include <QToolButton>
class MenuButton : public QToolButton {
Q_OBJECT
private:
QPixmap pixmap;
public:
explicit MenuButton(QWidget *parent = nullptr);
protected:
- void paintEvent(QPaintEvent *event);
+ void paintEvent(QPaintEvent *event) override;
};
#endif // MENUBUTTON_H
diff --git a/src/MergeBranchDialog.h b/src/MergeBranchDialog.h
index 6e555f9..53d17a3 100644
--- a/src/MergeBranchDialog.h
+++ b/src/MergeBranchDialog.h
@@ -1,24 +1,22 @@
#ifndef MERGEBRANCHDIALOG_H
#define MERGEBRANCHDIALOG_H
#include <QDialog>
#include "Git.h"
namespace Ui {
class MergeBranchDialog;
}
-class MergeBranchDialog : public QDialog
-{
+class MergeBranchDialog : public QDialog {
Q_OBJECT
-
public:
explicit MergeBranchDialog(QWidget *parent, QList<Git::Branch> const &branches);
- ~MergeBranchDialog();
+ ~MergeBranchDialog() override;
QString branchName() const;
private:
Ui::MergeBranchDialog *ui;
};
#endif // MERGEBRANCHDIALOG_H
diff --git a/src/MyImageViewWidget.cpp b/src/MyImageViewWidget.cpp
index 7ed2189..8d3ca85 100644
--- a/src/MyImageViewWidget.cpp
+++ b/src/MyImageViewWidget.cpp
@@ -1,52 +1,53 @@
#include "MyImageViewWidget.h"
#include <QMenu>
#include <QFileDialog>
#include "common/misc.h"
#include "common/joinpath.h"
MyImageViewWidget::MyImageViewWidget(QWidget *parent)
: ImageViewWidget(parent)
{
}
void MyImageViewWidget::setImage(QString mimetype, QByteArray const &ba, QString const &object_id, QString const &path)
{
this->object_id_ = object_id;
this->path_ = path;
ImageViewWidget::setImage(mimetype, ba);
}
void MyImageViewWidget::contextMenuEvent(QContextMenuEvent *e)
{
QString id = object_id_;
if (id.startsWith(PATH_PREFIX)) {
// pass
} else if (Git::isValidID(id)) {
// pass
} else {
return; // invalid id
}
QMenu menu;
QAction *a_save_as = id.isEmpty() ? nullptr : menu.addAction(tr("Save as..."));
if (!menu.actions().isEmpty()) {
update();
QAction *a = menu.exec(misc::contextMenuPos(this, e));
if (a) {
if (a == a_save_as) {
- QString path = mainwindow()->currentWorkingCopyDir() / path_;
+ auto *mw = qobject_cast<MainWindow *>(mainwindow());
+ QString path = mw->currentWorkingCopyDir() / path_;
QString dstpath = QFileDialog::getSaveFileName(window(), tr("Save as"), path);
if (!dstpath.isEmpty()) {
- mainwindow()->saveAs(id, dstpath);
+ mw->saveAs(id, dstpath);
}
update();
return;
}
}
}
}
diff --git a/src/MyProcess.h b/src/MyProcess.h
index 367600c..a3f996b 100644
--- a/src/MyProcess.h
+++ b/src/MyProcess.h
@@ -1,24 +1,24 @@
#ifndef MYPROCESS_H
#define MYPROCESS_H
#include <QtGlobal>
#ifdef Q_OS_WIN
#include "win32/Win32Process.h"
#include "win32/Win32PtyProcess.h"
-typedef Win32Process Process;
-typedef Win32PtyProcess PtyProcess;
+using Process = Win32Process;
+using PtyProcess = Win32PtyProcess;
#else
#include "unix/UnixProcess.h"
#include "unix/UnixPtyProcess.h"
-typedef UnixProcess Process;
-typedef UnixPtyProcess PtyProcess;
+using Process = UnixProcess;
+using PtyProcess = UnixPtyProcess;
#endif
class misc2 {
public:
static int runCommand(QString const &cmd, QByteArray *out);
static int runCommand(QString const &cmd, const QByteArray *in, QByteArray *out);
};
#endif // MYPROCESS_H
diff --git a/src/MySettings.h b/src/MySettings.h
index 3249fc3..da8733d 100644
--- a/src/MySettings.h
+++ b/src/MySettings.h
@@ -1,18 +1,12 @@
#ifndef MYSETTINGS_H
#define MYSETTINGS_H
#include <QSettings>
-class MySettings : public QSettings
-{
+class MySettings : public QSettings {
Q_OBJECT
public:
- explicit MySettings(QObject *parent = 0);
-
-signals:
-
-public slots:
-
+ explicit MySettings(QObject *parent = nullptr);
};
#endif // MYSETTINGS_H
diff --git a/src/MyTableWidgetDelegate.h b/src/MyTableWidgetDelegate.h
index 96ccbc0..5c963e8 100644
--- a/src/MyTableWidgetDelegate.h
+++ b/src/MyTableWidgetDelegate.h
@@ -1,14 +1,13 @@
#ifndef MYTABLEWIDGETDELEGATE_H
#define MYTABLEWIDGETDELEGATE_H
#include <QStyledItemDelegate>
class MyTableWidgetDelegate : public QStyledItemDelegate {
-
public:
- explicit MyTableWidgetDelegate(QObject *parent = Q_NULLPTR);
- void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+ explicit MyTableWidgetDelegate(QObject *parent = nullptr);
+ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
#endif // MYTABLEWIDGETDELEGATE_H
diff --git a/src/MyTextEditorWidget.cpp b/src/MyTextEditorWidget.cpp
index 970fd0f..67c9780 100644
--- a/src/MyTextEditorWidget.cpp
+++ b/src/MyTextEditorWidget.cpp
@@ -1,57 +1,57 @@
#include "MyTextEditorWidget.h"
#include "MainWindow.h"
#include "common/misc.h"
#include <QMenu>
#include <QFileDialog>
#include "common/joinpath.h"
MyTextEditorWidget::MyTextEditorWidget(QWidget *parent)
: TextEditorWidget(parent)
{
}
-void MyTextEditorWidget::setDocument(const QList<Document::Line> *source, MainWindow *mw, QString const &object_id, QString const &object_path)
+void MyTextEditorWidget::setDocument(const QList<Document::Line> *source, BasicMainWindow *mw, QString const &object_id, QString const &object_path)
{
this->mainwindow = mw;
this->object_id = object_id;
this->object_path = object_path;
TextEditorWidget::setDocument(source);
}
void MyTextEditorWidget::contextMenuEvent(QContextMenuEvent *event)
{
QString id = object_id;
if (id.startsWith(PATH_PREFIX)) {
// pass
} else if (Git::isValidID(id)) {
// pass
} else {
return; // invalid id
}
QMenu menu;
QAction *a_save_as = id.isEmpty() ? nullptr : menu.addAction(tr("Save as..."));
QAction *a_copy = menu.addAction(tr("Copy"));
if (!menu.actions().isEmpty()) {
update();
QAction *a = menu.exec(misc::contextMenuPos(this, event));
if (a) {
if (a == a_save_as) {
QString path = mainwindow->currentWorkingCopyDir() / object_path;
QString dstpath = QFileDialog::getSaveFileName(window(), tr("Save as"), path);
if (!dstpath.isEmpty()) {
mainwindow->saveAs(id, dstpath);
}
update();
return;
}
if (a == a_copy) {
editCopy();
return;
}
}
}
}
diff --git a/src/MyTextEditorWidget.h b/src/MyTextEditorWidget.h
index 952cc2e..616e828 100644
--- a/src/MyTextEditorWidget.h
+++ b/src/MyTextEditorWidget.h
@@ -1,22 +1,20 @@
#ifndef MYTEXTEDITORWIDGET_H
#define MYTEXTEDITORWIDGET_H
#include "texteditor/TextEditorWidget.h"
-class MainWindow;
+class BasicMainWindow;
class MyTextEditorWidget : public TextEditorWidget {
private:
- MainWindow *mainwindow;
+ BasicMainWindow *mainwindow;
QString object_id;
QString object_path;
public:
- MyTextEditorWidget(QWidget *parent = 0);
-
- // QWidget interface
- void setDocument(const QList<Document::Line> *source, MainWindow *mw, QString const &object_id, QString const &object_path);
+ MyTextEditorWidget(QWidget *parent = nullptr);
+ void setDocument(const QList<Document::Line> *source, BasicMainWindow *mw, QString const &object_id, QString const &object_path);
protected:
- void contextMenuEvent(QContextMenuEvent *event);
+ void contextMenuEvent(QContextMenuEvent *event) override;
};
#endif // MYTEXTEDITORWIDGET_H
diff --git a/src/MyToolButton.h b/src/MyToolButton.h
index e2435e9..55dc412 100644
--- a/src/MyToolButton.h
+++ b/src/MyToolButton.h
@@ -1,31 +1,26 @@
#ifndef MYTOOLBUTTON_H
#define MYTOOLBUTTON_H
#include <QToolButton>
-class MyToolButton : public QToolButton
-{
+class MyToolButton : public QToolButton {
Q_OBJECT
public:
enum Indicator {
None,
Dot,
Number,
};
private:
Indicator indicator = None;
int number = -1;
void setIndicatorMode(Indicator i);
public:
- explicit MyToolButton(QWidget *parent = 0);
+ explicit MyToolButton(QWidget *parent = nullptr);
void setNumber(int n);
void setDot(bool f);
-signals:
-
-public slots:
-
protected:
- void paintEvent(QPaintEvent *event);
+ void paintEvent(QPaintEvent *event) override;
};
#endif // MYTOOLBUTTON_H
diff --git a/src/Photoshop.cpp b/src/Photoshop.cpp
index 2d2a1bc..12c9941 100644
--- a/src/Photoshop.cpp
+++ b/src/Photoshop.cpp
@@ -1,112 +1,112 @@
#include "Photoshop.h"
-#include <stdint.h>
-#include <stdio.h>
+#include <cstdint>
+#include <cstdio>
#include <vector>
namespace {
inline uint32_t read_uint32_be(void const *p)
{
auto const *q = (uint8_t const *)p;
return (q[0] << 24) | (q[1] << 16) | (q[2] << 8) | q[3];
}
inline uint16_t read_uint16_be(void const *p)
{
auto const *q = (uint8_t const *)p;
return (q[0] << 8) | q[1];
}
-}
+} // namespace
void photoshop::readThumbnail(QIODevice *in, std::vector<char> *jpeg)
{
jpeg->clear();
struct FileHeader {
char sig[4];
char ver[2];
char reserved[6];
char channels[2];
char height[4];
char width[4];
char depth[2];
char colormode[2];
};
FileHeader fh;
in->read((char *)&fh, sizeof(FileHeader));
if (memcmp(fh.sig, "8BPS", 4) == 0) {
char tmp[4];
uint32_t len;
in->read(tmp, 4);
len = read_uint32_be(tmp);
in->seek(in->pos() + len);
in->read(tmp, 4);
len = read_uint32_be(tmp);
while (1) {
struct ImageResourceHeader {
char sig[4];
char id[2];
};
ImageResourceHeader irh;
in->read((char *)&irh, sizeof(ImageResourceHeader));
if (memcmp(irh.sig, "8BIM", 4) == 0) {
std::vector<char> name;
while (1) {
char c;
if (in->read(&c, 1) != 1) break;
if (c == 0) {
if ((name.size() & 1) == 0) {
if (in->read(&c, 1) != 1) break;
}
break;
}
name.push_back(c);
}
in->read(tmp, 4);
len = read_uint32_be(tmp);
qint64 pos = in->pos();
uint16_t resid = read_uint16_be(irh.id);
if (resid == 0x0409 || resid == 0x040c) {
struct ThumbnailResourceHeader {
uint32_t format;
uint32_t width;
uint32_t height;
uint32_t widthbytes;
uint32_t totalsize;
uint32_t size_after_compression;
uint16_t bits_per_pixel;
uint16_t num_of_planes;
};
ThumbnailResourceHeader trh;
in->read((char *)&trh, sizeof(ThumbnailResourceHeader));
if (read_uint32_be(&trh.format) == 1) {
uint32_t size_after_compression = read_uint32_be(&trh.size_after_compression);
if (size_after_compression < 1000000) {
jpeg->resize(size_after_compression);
char *ptr = &jpeg->at(0);
if (in->read(ptr, size_after_compression) == size_after_compression) {
// ok
} else {
jpeg->clear();
}
}
}
break;
}
if (len & 1) len++;
in->seek(pos + len);
} else {
break;
}
}
}
}
diff --git a/src/Photoshop.h b/src/Photoshop.h
index a175b6e..4926e60 100644
--- a/src/Photoshop.h
+++ b/src/Photoshop.h
@@ -1,13 +1,13 @@
#ifndef PHOTOSHOP_H
#define PHOTOSHOP_H
#include <vector>
#include <QIODevice>
namespace photoshop {
void readThumbnail(QIODevice *in, std::vector<char> *jpeg);
-}
+} // namespace
#endif // PHOTOSHOP_H
diff --git a/src/PushDialog.h b/src/PushDialog.h
index 011ddc8..48f1d98 100644
--- a/src/PushDialog.h
+++ b/src/PushDialog.h
@@ -1,48 +1,39 @@
#ifndef PUSHDIALOG_H
#define PUSHDIALOG_H
#include <QDialog>
namespace Ui {
class PushDialog;
}
-class PushDialog : public QDialog
-{
+class PushDialog : public QDialog {
Q_OBJECT
+private:
+ Ui::PushDialog *ui;
public:
struct RemoteBranch {
QString remote;
QString branch;
- RemoteBranch()
- {
- }
+ RemoteBranch() = default;
RemoteBranch(QString const &r, QString const &b)
: remote(r)
, branch(b)
{
}
};
public:
explicit PushDialog(QWidget *parent, QStringList const &remotes, QStringList const &branches, RemoteBranch const &remote_branch);
- ~PushDialog();
+ ~PushDialog() override;
enum Action {
PushSimple,
PushSetUpstream,
};
Action action() const;
QString remote() const;
QString branch() const;
-
-private slots:
-#if 0
- void on_radioButton_push_simply_clicked();
- void on_radioButton_push_set_upstream_clicked();
-#endif
-private:
- Ui::PushDialog *ui;
};
#endif // PUSHDIALOG_H
diff --git a/src/ReadOnlyLineEdit.h b/src/ReadOnlyLineEdit.h
index a039d12..feba2a0 100644
--- a/src/ReadOnlyLineEdit.h
+++ b/src/ReadOnlyLineEdit.h
@@ -1,17 +1,14 @@
#ifndef READONLYLINEEDIT_H
#define READONLYLINEEDIT_H
#include <QLineEdit>
-class ReadOnlyLineEdit : public QLineEdit
-{
+class ReadOnlyLineEdit : public QLineEdit {
public:
- ReadOnlyLineEdit(QWidget *parent = 0);
-
+ ReadOnlyLineEdit(QWidget *parent = nullptr);
void setText(QString const &text);
- // QWidget interface
protected:
- void paintEvent(QPaintEvent *event);
+ void paintEvent(QPaintEvent *event) override;
};
#endif // READONLYLINEEDIT_H
diff --git a/src/ReadOnlyPlainTextEdit.h b/src/ReadOnlyPlainTextEdit.h
index 5cfad93..5cd9ba4 100644
--- a/src/ReadOnlyPlainTextEdit.h
+++ b/src/ReadOnlyPlainTextEdit.h
@@ -1,20 +1,14 @@
#ifndef READONLYPLAINTEXTEDIT_H
#define READONLYPLAINTEXTEDIT_H
#include <QPlainTextEdit>
-class ReadOnlyPlainTextEdit : public QPlainTextEdit
-{
+class ReadOnlyPlainTextEdit : public QPlainTextEdit {
Q_OBJECT
-public:
- explicit ReadOnlyPlainTextEdit(QWidget *parent = 0);
-
-signals:
-
-public slots:
-
protected:
- void mousePressEvent(QMouseEvent *event);
+ void mousePressEvent(QMouseEvent *event) override;
+public:
+ explicit ReadOnlyPlainTextEdit(QWidget *parent = nullptr);
};
#endif // READONLYPLAINTEXTEDIT_H
diff --git a/src/RebaseOntoDialog.h b/src/RebaseOntoDialog.h
index 15dcb18..39f10ea 100644
--- a/src/RebaseOntoDialog.h
+++ b/src/RebaseOntoDialog.h
@@ -1,24 +1,23 @@
#ifndef REBASEONTODIALOG_H
#define REBASEONTODIALOG_H
#include <QDialog>
namespace Ui {
class RebaseOntoDialog;
}
-class RebaseOntoDialog : public QDialog
-{
+class RebaseOntoDialog : public QDialog {
Q_OBJECT
private:
Ui::RebaseOntoDialog *ui;
public:
explicit RebaseOntoDialog(QWidget *parent = nullptr);
- ~RebaseOntoDialog();
+ ~RebaseOntoDialog() override;
int exec(QString const &newbase, QString const &upstream, QString const &branch);
QString newbase() const;
QString upstream() const;
QString branch() const;
};
#endif // REBASEONTODIALOG_H
diff --git a/src/ReflogWindow.h b/src/ReflogWindow.h
index 925b578..e1db28c 100644
--- a/src/ReflogWindow.h
+++ b/src/ReflogWindow.h
@@ -1,41 +1,39 @@
#ifndef REFLOGWINDOW_H
#define REFLOGWINDOW_H
#include "Git.h"
#include <QDialog>
namespace Ui {
class ReflogWindow;
}
class MainWindow;
class QTableWidgetItem;
-class ReflogWindow : public QDialog
-{
+class ReflogWindow : public QDialog {
Q_OBJECT
private:
+ Ui::ReflogWindow *ui;
MainWindow *mainwindow_;
Git::ReflogItemList reflog_;
MainWindow *mainwindow()
{
return mainwindow_;
}
+
+ void updateTable(const Git::ReflogItemList &reflog);
+ bool currentCommit(Git::CommitItem *out);
+
public:
explicit ReflogWindow(QWidget *parent, MainWindow *mainwin, const Git::ReflogItemList &reflog);
- ~ReflogWindow();
+ ~ReflogWindow() override;
private slots:
void on_tableWidget_customContextMenuRequested(const QPoint &pos);
-
void on_tableWidget_itemDoubleClicked(QTableWidgetItem *item);
-
-private:
- Ui::ReflogWindow *ui;
- void updateTable(const Git::ReflogItemList &reflog);
- bool currentCommit(Git::CommitItem *out);
};
#endif // REFLOGWINDOW_H
diff --git a/src/RemoteRepositoriesTableWidget.h b/src/RemoteRepositoriesTableWidget.h
index fa92629..cf53ca4 100644
--- a/src/RemoteRepositoriesTableWidget.h
+++ b/src/RemoteRepositoriesTableWidget.h
@@ -1,15 +1,13 @@
#ifndef REMOTEREPOSITORIESTABLEWIDGET_H
#define REMOTEREPOSITORIESTABLEWIDGET_H
#include <QTableWidget>
class RemoteRepositoriesTableWidget : public QTableWidget {
public:
- RemoteRepositoriesTableWidget(QWidget *parent = 0);
-
- // QWidget interface
+ RemoteRepositoriesTableWidget(QWidget *parent = nullptr);
protected:
- void contextMenuEvent(QContextMenuEvent *event);
+ void contextMenuEvent(QContextMenuEvent *event) override;
};
#endif // REMOTEREPOSITORIESTABLEWIDGET_H
diff --git a/src/RemoteWatcher.cpp b/src/RemoteWatcher.cpp
index f8a1d71..fc46d91 100644
--- a/src/RemoteWatcher.cpp
+++ b/src/RemoteWatcher.cpp
@@ -1,49 +1,51 @@
#include "RemoteWatcher.h"
#include "common/joinpath.h"
#include <QFileInfo>
void RemoteWatcher::start(MainWindow *mw)
{
mainwindow_ = mw;
QThread::start();
moveToThread(this);
}
void RemoteWatcher::setCurrent(QString const &remote, QString const &branch)
{
remote_ = remote;
branch_ = branch;
}
void RemoteWatcher::checkRemoteUpdate()
{
QString dir = mainwindow()->currentWorkingCopyDir();
QString refs;
QString local_id;
if (QFileInfo(dir).isDir()) {
refs = "refs/remotes" / remote_ / branch_;
QString head = dir / ".git" / refs;
QFile file1(head);
if (file1.open(QFile::ReadOnly)) {
local_id = file1.readLine().trimmed();
}
refs = "refs/heads" / branch_;
}
if (local_id.isEmpty()) return;
QString remote_id;
GitPtr g = mainwindow()->git();
auto list = g->ls_remote();
for (auto item : list) {
if (item.name == refs) {
remote_id = item.commit_id;
break;
}
}
- mainwindow()->setRemoteChanged(remote_id != local_id);
+
+ bool changed = remote_id != local_id;
+ emit mainwindow()->signalSetRemoteChanged(changed);
}
diff --git a/src/RepositoriesTreeWidget.h b/src/RepositoriesTreeWidget.h
index 807fc54..b165b5f 100644
--- a/src/RepositoriesTreeWidget.h
+++ b/src/RepositoriesTreeWidget.h
@@ -1,30 +1,22 @@
#ifndef REPOSITORIESTREEWIDGET_H
#define REPOSITORIESTREEWIDGET_H
#include <QTreeWidget>
class MainWindow;
-class RepositoriesTreeWidget : public QTreeWidget
-{
+class RepositoriesTreeWidget : public QTreeWidget {
Q_OBJECT
private:
MainWindow *mainwindow();
QTreeWidgetItem *current_item = nullptr;
+protected:
+ void dropEvent(QDropEvent *event) override;
+ void dragEnterEvent(QDragEnterEvent *event) override;
public:
- explicit RepositoriesTreeWidget(QWidget *parent = 0);
-
+ explicit RepositoriesTreeWidget(QWidget *parent = nullptr);
signals:
void dropped();
-public slots:
-
- // QWidget interface
-protected:
- void dropEvent(QDropEvent *event);
-
- // QWidget interface
-protected:
- void dragEnterEvent(QDragEnterEvent *event);
};
#endif // REPOSITORIESTREEWIDGET_H
diff --git a/src/RepositoryInfoFrame.h b/src/RepositoryInfoFrame.h
index 653c1ba..579101a 100644
--- a/src/RepositoryInfoFrame.h
+++ b/src/RepositoryInfoFrame.h
@@ -1,21 +1,14 @@
#ifndef REPOSITORYINFOFRAME_H
#define REPOSITORYINFOFRAME_H
#include <QFrame>
-class RepositoryInfoFrame : public QFrame
-{
+class RepositoryInfoFrame : public QFrame {
Q_OBJECT
-public:
- explicit RepositoryInfoFrame(QWidget *parent = 0);
-
-signals:
-
-public slots:
-
- // QWidget interface
protected:
- void paintEvent(QPaintEvent *event);
+ void paintEvent(QPaintEvent *event) override;
+public:
+ explicit RepositoryInfoFrame(QWidget *parent = nullptr);
};
#endif // REPOSITORYINFOFRAME_H
diff --git a/src/RepositoryLineEdit.h b/src/RepositoryLineEdit.h
index 7989fc0..d582bdc 100644
--- a/src/RepositoryLineEdit.h
+++ b/src/RepositoryLineEdit.h
@@ -1,21 +1,14 @@
#ifndef REPOSITORYLINEEDIT_H
#define REPOSITORYLINEEDIT_H
#include <QLineEdit>
-class RepositoryLineEdit : public QLineEdit
-{
+class RepositoryLineEdit : public QLineEdit {
Q_OBJECT
-public:
- explicit RepositoryLineEdit(QWidget *parent = 0);
-
-signals:
-
-public slots:
-
- // QWidget interface
protected:
- void dropEvent(QDropEvent *event);
+ void dropEvent(QDropEvent *event) override;
+public:
+ explicit RepositoryLineEdit(QWidget *parent = nullptr);
};
#endif // REPOSITORYLINEEDIT_H
diff --git a/src/RepositoryPropertyDialog.cpp b/src/RepositoryPropertyDialog.cpp
index ef6a4a5..64dd8e9 100644
--- a/src/RepositoryPropertyDialog.cpp
+++ b/src/RepositoryPropertyDialog.cpp
@@ -1,141 +1,141 @@
#include "RepositoryPropertyDialog.h"
#include "ui_RepositoryPropertyDialog.h"
#include "common/misc.h"
#include "MainWindow.h"
#include <QClipboard>
#include <QMenu>
#include <QMessageBox>
-RepositoryPropertyDialog::RepositoryPropertyDialog(MainWindow *parent, GitPtr g, const RepositoryItem &item, bool open_repository_menu)
+RepositoryPropertyDialog::RepositoryPropertyDialog(BasicMainWindow *parent, GitPtr g, const RepositoryItem &item, bool open_repository_menu)
: BasicRepositoryDialog(parent, g)
, ui(new Ui::RepositoryPropertyDialog)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
repository = item;
ui->groupBox_remote->setVisible(open_repository_menu);
ui->label_name->setText(repository.name);
ui->lineEdit_local_dir->setText(misc::normalizePathSeparator(repository.local_dir));
updateRemotesTable();
}
RepositoryPropertyDialog::~RepositoryPropertyDialog()
{
delete ui;
}
void RepositoryPropertyDialog::updateRemotesTable()
{
BasicRepositoryDialog::updateRemotesTable(ui->tableWidget);
}
void RepositoryPropertyDialog::toggleRemoteMenuActivity()
{
ui->groupBox_remote->setVisible(!ui->groupBox_remote->isVisible());
}
bool RepositoryPropertyDialog::execEditRemoteDialog(Git::Remote *remote, EditRemoteDialog::Operation op)
{
auto const *list = remotes();
if (op == EditRemoteDialog::RemoteSet) {
int row = ui->tableWidget->currentRow();
if (row >= 0 && row < list->size()) {
*remote = list->at(row);
}
} else {
*remote = Git::Remote();
}
if (remote->name.isEmpty() && list->isEmpty()) {
op = EditRemoteDialog::RemoteAdd;
remote->name = "origin";
}
EditRemoteDialog dlg(mainwindow(), op);
dlg.setName(remote->name);
dlg.setUrl(remote->url);
if (dlg.exec() == QDialog::Accepted) {
remote->name = dlg.name();
remote->url = dlg.url();
GitPtr g = git();
if (op == EditRemoteDialog::RemoteAdd) {
bool ok = true;
for (Git::Remote const &r : *list) {
if (r.name == remote->name) {
qDebug() << "remote add: Already exists" << remote->name;
ok = false;
break;
}
}
if (ok) {
g->addRemoteURL(remote->name, remote->url);
}
} else if (op == EditRemoteDialog::RemoteSet) {
g->setRemoteURL(remote->name, remote->url);
}
updateRemotesTable();
return true;
}
return false;
}
Git::Remote RepositoryPropertyDialog::selectedRemote() const
{
Git::Remote remote;
auto const *list = remotes();
int row = ui->tableWidget->currentRow();
if (row >= 0 && row < list->size()) {
remote = list->at(row);
}
return remote;
}
bool RepositoryPropertyDialog::isRemoteChanged() const
{
return remote_changed;
}
void RepositoryPropertyDialog::on_pushButton_remote_add_clicked()
{
Git::Remote r;
if (execEditRemoteDialog(&r, EditRemoteDialog::RemoteAdd)) {
remote_changed = true;
}
}
void RepositoryPropertyDialog::on_pushButton_remote_edit_clicked()
{
Git::Remote remote = selectedRemote();
if (execEditRemoteDialog(&remote, EditRemoteDialog::RemoteSet)) {
remote_changed = true;
}
}
void RepositoryPropertyDialog::on_pushButton_remote_remove_clicked()
{
Git::Remote remote = selectedRemote();
if (!remote.name.isEmpty()) {
int r = QMessageBox::warning(this, tr("Confirm Remove"), tr("Are you sure you want to remove the remote '%1' from the repository '%2' ?").arg(remote.name).arg(repository.name), QMessageBox::Ok, QMessageBox::Cancel);
if (r == QMessageBox::Ok) {
GitPtr g = git();
g->removeRemote(remote.name);
updateRemotesTable();
remote_changed = true;
}
}
}
void RepositoryPropertyDialog::on_pushButton_remote_menu_clicked()
{
toggleRemoteMenuActivity();
}
diff --git a/src/RepositoryPropertyDialog.h b/src/RepositoryPropertyDialog.h
index b84af67..798824d 100644
--- a/src/RepositoryPropertyDialog.h
+++ b/src/RepositoryPropertyDialog.h
@@ -1,39 +1,39 @@
#ifndef REPOSITORYPROPERTYDIALOG_H
#define REPOSITORYPROPERTYDIALOG_H
#include "RepositoryData.h"
#include "BasicRepositoryDialog.h"
#include "EditRemoteDialog.h"
#include <QDialog>
#include "Git.h"
+class BasicMainWindow;
namespace Ui {
class RepositoryPropertyDialog;
}
-class RepositoryPropertyDialog : public BasicRepositoryDialog
-{
+class RepositoryPropertyDialog : public BasicRepositoryDialog {
Q_OBJECT
private:
+ Ui::RepositoryPropertyDialog *ui;
RepositoryItem repository;
bool remote_changed = false;
+
+ void updateRemotesTable();
+ bool execEditRemoteDialog(Git::Remote *remote, EditRemoteDialog::Operation op);
+ Git::Remote selectedRemote() const;
+ void toggleRemoteMenuActivity();
public:
- explicit RepositoryPropertyDialog(MainWindow *parent, GitPtr g, RepositoryItem const &item, bool open_repository_menu = false);
- ~RepositoryPropertyDialog();
+ explicit RepositoryPropertyDialog(BasicMainWindow *parent, GitPtr g, RepositoryItem const &item, bool open_repository_menu = false);
+ ~RepositoryPropertyDialog() override;
bool isRemoteChanged() const;
private slots:
void on_pushButton_remote_add_clicked();
void on_pushButton_remote_edit_clicked();
void on_pushButton_remote_remove_clicked();
void on_pushButton_remote_menu_clicked();
-private:
- Ui::RepositoryPropertyDialog *ui;
- void updateRemotesTable();
- bool execEditRemoteDialog(Git::Remote *remote, EditRemoteDialog::Operation op);
- Git::Remote selectedRemote() const;
- void toggleRemoteMenuActivity();
};
#endif // REPOSITORYPROPERTYDIALOG_H
diff --git a/src/SearchFromGitHubDialog.cpp b/src/SearchFromGitHubDialog.cpp
index 02ba15a..f347a42 100644
--- a/src/SearchFromGitHubDialog.cpp
+++ b/src/SearchFromGitHubDialog.cpp
@@ -1,169 +1,169 @@
#include "SearchFromGitHubDialog.h"
#include "ui_SearchFromGitHubDialog.h"
#include "common/misc.h"
#include "urlencode.h"
-#include "MainWindow.h"
+#include "BasicMainWindow.h"
#include <QDebug>
#include <QThread>
#include <QDesktopServices>
#include <QUrl>
#include <functional>
using SearchResultItem = GitHubAPI::SearchResultItem;
static QString toQString(std::string const &s)
{
return QString::fromUtf8(s.c_str(), s.size());
}
-SearchFromGitHubDialog::SearchFromGitHubDialog(QWidget *parent, MainWindow *mw)
+SearchFromGitHubDialog::SearchFromGitHubDialog(QWidget *parent, BasicMainWindow *mw)
: QDialog(parent)
, ui(new Ui::SearchFromGitHubDialog)
, mainwindow(mw)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
ui->tableWidget->setItemDelegate(&item_delegate);
connect(ui->label_hyperlink, SIGNAL(clicked()), this, SLOT(onHyperlinkClicked()));
ui->pushButton_ok->setEnabled(false);
}
SearchFromGitHubDialog::~SearchFromGitHubDialog()
{
delete ui;
}
QString SearchFromGitHubDialog::url() const
{
return url_;
}
void SearchFromGitHubDialog::on_pushButton_search_clicked()
{
std::string q = ui->lineEdit_keywords->text().trimmed().toStdString();
q = url_encode(q);
if (q.empty()) return;
- GitHubAPI github(mainwindow->getWebContextPtr());
+ GitHubAPI github(mainwindow->webContext());
items = github.searchRepository(q);
QStringList cols = {
tr("Name"),
tr("Owner"),
tr("Score"),
tr("Description"),
};
ui->tableWidget->setColumnCount(cols.size());
ui->tableWidget->setRowCount(items.size());
for (int col = 0; col < cols.size(); col++) {
QTableWidgetItem *p = new QTableWidgetItem();
p->setText(cols[col]);
ui->tableWidget->setHorizontalHeaderItem(col, p);
}
for (int row = 0; row < items.size(); row++) {
SearchResultItem const &item = items[row];
QTableWidgetItem *p;
QString name = QString::fromStdString(item.full_name);
QString owner;
int i = name.indexOf('/');
if (i > 0) {
owner = name.mid(0, i);
name = name.mid(i + 1);
}
int col = 0;
auto AddItem = [&](std::function<void(QTableWidgetItem *)> callback){
p = new QTableWidgetItem();
callback(p);
ui->tableWidget->setItem(row, col, p);
col++;
};
AddItem([&](QTableWidgetItem *p){
p->setData(Qt::UserRole, (int)row);
p->setText(name);
});
AddItem([&](QTableWidgetItem *p){
p->setText(owner);
});
AddItem([&](QTableWidgetItem *p){
char tmp[100];
sprintf(tmp, "%.2f", item.score);
p->setText(tmp);
p->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
});
AddItem([&](QTableWidgetItem *p){
p->setText(toQString(item.description));
});
ui->tableWidget->setRowHeight(row, 24);
}
ui->tableWidget->resizeColumnsToContents();
ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
}
void SearchFromGitHubDialog::updateUI()
{
auto Check = [&](){
if (ui->radioButton_ssh->isChecked()) {
url_ = ui->lineEdit_ssh->text();
if (!url_.isEmpty()) {
return true;
}
}
if (ui->radioButton_http->isChecked()) {
url_ = ui->lineEdit_http->text();
if (!url_.isEmpty()) {
return true;
}
}
url_ = QString();
return false;
};
ui->pushButton_ok->setEnabled(Check());
}
void SearchFromGitHubDialog::on_tableWidget_currentItemChanged(QTableWidgetItem * /*current*/, QTableWidgetItem * /*previous*/)
{
int row = ui->tableWidget->currentRow();
QTableWidgetItem *p = ui->tableWidget->item(row, 0);
if (p) {
int i = p->data(Qt::UserRole).toInt();
if (i < items.size()) {
SearchResultItem const &item = items[i];
ui->lineEdit_ssh->setText(toQString(item.ssh_url));
ui->lineEdit_http->setText(toQString(item.clone_url));
ui->label_hyperlink->setText(toQString(item.html_url));
}
}
updateUI();
}
void SearchFromGitHubDialog::on_radioButton_ssh_clicked()
{
updateUI();
}
void SearchFromGitHubDialog::on_radioButton_http_clicked()
{
updateUI();
}
void SearchFromGitHubDialog::onHyperlinkClicked()
{
QString url = ui->label_hyperlink->text();
QDesktopServices::openUrl(QUrl(url));
}
diff --git a/src/SearchFromGitHubDialog.h b/src/SearchFromGitHubDialog.h
index 0a736d9..4418a1a 100644
--- a/src/SearchFromGitHubDialog.h
+++ b/src/SearchFromGitHubDialog.h
@@ -1,42 +1,40 @@
#ifndef SEARCHFROMGITHUBDIALOG_H
#define SEARCHFROMGITHUBDIALOG_H
#include "MyTableWidgetDelegate.h"
#include "GitHubAPI.h"
#include <QDialog>
namespace Ui {
class SearchFromGitHubDialog;
}
class QTableWidgetItem;
+class BasicMainWindow;
-class MainWindow;
-
-class SearchFromGitHubDialog : public QDialog
-{
+class SearchFromGitHubDialog : public QDialog {
Q_OBJECT
private:
+ Ui::SearchFromGitHubDialog *ui;
QList<GitHubAPI::SearchResultItem> items;
QString url_;
MyTableWidgetDelegate item_delegate;
- MainWindow *mainwindow;
+ BasicMainWindow *mainwindow;
+
+ void updateUI();
public:
- explicit SearchFromGitHubDialog(QWidget *parent, MainWindow *mw);
- ~SearchFromGitHubDialog();
+ explicit SearchFromGitHubDialog(QWidget *parent, BasicMainWindow *mw);
+ ~SearchFromGitHubDialog() override;
QString url() const;
private slots:
void on_pushButton_search_clicked();
void on_tableWidget_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
void on_radioButton_ssh_clicked();
void on_radioButton_http_clicked();
void onHyperlinkClicked();
-private:
- Ui::SearchFromGitHubDialog *ui;
- void updateUI();
};
#endif // SEARCHFROMGITHUBDIALOG_H
diff --git a/src/SelectCommandDialog.h b/src/SelectCommandDialog.h
index 17def1d..e91dcfd 100644
--- a/src/SelectCommandDialog.h
+++ b/src/SelectCommandDialog.h
@@ -1,36 +1,31 @@
#ifndef SELECTCOMMANDDIALOG_H
#define SELECTCOMMANDDIALOG_H
#include <QDialog>
namespace Ui {
class SelectCommandDialog;
}
class QListWidgetItem;
class SelectCommandDialog : public QDialog
{
Q_OBJECT
private:
+ Ui::SelectCommandDialog *ui;
QString path;
QString command_name;
QStringList command_files;
public:
explicit SelectCommandDialog(QWidget *parent, QString const &cmdname, QStringList const &cmdfiles, QString const &path, QStringList const &list);
- ~SelectCommandDialog();
+ ~SelectCommandDialog() override;
QString selectedFile() const;
private slots:
-
void on_listWidget_currentTextChanged(QString const &currentText);
-
void on_pushButton_browse_clicked();
-
void on_listWidget_itemDoubleClicked(QListWidgetItem *item);
-
-private:
- Ui::SelectCommandDialog *ui;
};
#endif // SELECTCOMMANDDIALOG_H
diff --git a/src/SelectGpgKeyDialog.h b/src/SelectGpgKeyDialog.h
index 3e5a8c9..5f1aa0d 100644
--- a/src/SelectGpgKeyDialog.h
+++ b/src/SelectGpgKeyDialog.h
@@ -1,33 +1,31 @@
#ifndef SELECTGPGKEYDIALOG_H
#define SELECTGPGKEYDIALOG_H
#include <QDialog>
#include "gpg.h"
class MainWindow;
class QTableWidgetItem;
namespace Ui {
class SelectGpgKeyDialog;
}
-class SelectGpgKeyDialog : public QDialog
-{
+class SelectGpgKeyDialog : public QDialog {
Q_OBJECT
private:
+ Ui::SelectGpgKeyDialog *ui;
QList<gpg::Data> keys_;
+ void updateTable();
public:
explicit SelectGpgKeyDialog(QWidget *parent, const QList<gpg::Data> &keys);
- ~SelectGpgKeyDialog();
+ ~SelectGpgKeyDialog() override;
gpg::Data key() const;
private slots:
void on_tableWidget_itemDoubleClicked(QTableWidgetItem *item);
-private:
- Ui::SelectGpgKeyDialog *ui;
- void updateTable();
};
#endif // SELECTGPGKEYDIALOG_H
diff --git a/src/SelectItemDialog.h b/src/SelectItemDialog.h
index e40e41e..3f6f122 100644
--- a/src/SelectItemDialog.h
+++ b/src/SelectItemDialog.h
@@ -1,43 +1,40 @@
#ifndef SELECTITEMDIALOG_H
#define SELECTITEMDIALOG_H
#include <QDialog>
#include <vector>
class QListWidgetItem;
namespace Ui {
class SelectItemDialog;
}
class SelectItemDialog : public QDialog {
Q_OBJECT
+private:
+ Ui::SelectItemDialog *ui;
public:
struct Item {
QString id;
QString text;
- Item()
- {
- }
+ Item() = default;
Item(QString const &id, QString const &text)
: id(id)
, text(text)
{
}
};
public:
- explicit SelectItemDialog(QWidget *parent = 0);
- ~SelectItemDialog();
+ explicit SelectItemDialog(QWidget *parent = nullptr);
+ ~SelectItemDialog() override;
void addItem(QString const &item, QString const &text);
Item item() const;
void select(QString const &id);
private slots:
void on_listWidget_itemDoubleClicked(QListWidgetItem *);
-
-private:
- Ui::SelectItemDialog *ui;
};
#endif // SELECTITEMDIALOG_H
diff --git a/src/SetGlobalUserDialog.h b/src/SetGlobalUserDialog.h
index 9384125..da93a3d 100644
--- a/src/SetGlobalUserDialog.h
+++ b/src/SetGlobalUserDialog.h
@@ -1,24 +1,22 @@
#ifndef SETGLOBALUSERDIALOG_H
#define SETGLOBALUSERDIALOG_H
#include <QDialog>
#include "Git.h"
namespace Ui {
class SetGlobalUserDialog;
}
-class SetGlobalUserDialog : public QDialog
-{
+class SetGlobalUserDialog : public QDialog {
Q_OBJECT
-
+private:
+ Ui::SetGlobalUserDialog *ui;
public:
- explicit SetGlobalUserDialog(QWidget *parent = 0);
- ~SetGlobalUserDialog();
+ explicit SetGlobalUserDialog(QWidget *parent = nullptr);
+ ~SetGlobalUserDialog() override;
Git::User user() const;
-private:
- Ui::SetGlobalUserDialog *ui;
};
#endif // SETGLOBALUSERDIALOG_H
diff --git a/src/SetGpgSigningDialog.h b/src/SetGpgSigningDialog.h
index efded5b..efcf154 100644
--- a/src/SetGpgSigningDialog.h
+++ b/src/SetGpgSigningDialog.h
@@ -1,48 +1,42 @@
#ifndef SETGPGSIGNINGDIALOG_H
#define SETGPGSIGNINGDIALOG_H
#include <QDialog>
#include "gpg.h"
class MainWindow;
namespace Ui {
class SetGpgSigningDialog;
}
class SetGpgSigningDialog : public QDialog
{
Q_OBJECT
private:
+ Ui::SetGpgSigningDialog *ui;
struct Private;
Private *m;
MainWindow *mainwindow();
+ void setKey_(gpg::Data const &key);
+ void setKey_(QString const &key_id);
public:
explicit SetGpgSigningDialog(QWidget *parent, QString const &repo, QString const &global_key_id, QString const &repository_key_id);
- ~SetGpgSigningDialog();
+ ~SetGpgSigningDialog() override;
QString id() const;
QString name() const;
QString mail() const;
bool isGlobalChecked() const;
bool isRepositoryChecked() const;
private slots:
void on_radioButton_global_clicked();
-
void on_radioButton_repository_clicked();
-
void on_pushButton_select_clicked();
-
void on_pushButton_clear_clicked();
-
void on_pushButton_configure_clicked();
-
-private:
- Ui::SetGpgSigningDialog *ui;
- void setKey_(gpg::Data const &key);
- void setKey_(QString const &key_id);
};
#endif // SETGPGSIGNINGDIALOG_H
diff --git a/src/SetRemoteUrlDialog.h b/src/SetRemoteUrlDialog.h
index 803fadd..69e7b8b 100644
--- a/src/SetRemoteUrlDialog.h
+++ b/src/SetRemoteUrlDialog.h
@@ -1,33 +1,32 @@
#ifndef SETREMOTEURLDIALOG_H
#define SETREMOTEURLDIALOG_H
#include <QDialog>
#include "Git.h"
#include "RepositoryPropertyDialog.h"
class MainWindow;
namespace Ui {
class SetRemoteUrlDialog;
}
class SetRemoteUrlDialog : public BasicRepositoryDialog
{
Q_OBJECT
private:
+ Ui::SetRemoteUrlDialog *ui;
QStringList remotes;
+ void updateRemotesTable();
public:
explicit SetRemoteUrlDialog(MainWindow *mainwindow, QStringList const &remotes, GitPtr g);
- ~SetRemoteUrlDialog();
+ ~SetRemoteUrlDialog() override;
- int exec();
-private:
- Ui::SetRemoteUrlDialog *ui;
- void updateRemotesTable();
-public slots:
- void accept();
+ int exec() override;
private slots:
void on_pushButton_test_clicked();
+public slots:
+ void accept() override;
};
#endif // SETREMOTEURLDIALOG_H
diff --git a/src/SetUserDialog.cpp b/src/SetUserDialog.cpp
index 5530dde..e842daa 100644
--- a/src/SetUserDialog.cpp
+++ b/src/SetUserDialog.cpp
@@ -1,130 +1,130 @@
#include "AvatarLoader.h"
-#include "MainWindow.h"
+#include "BasicMainWindow.h"
#include "SetUserDialog.h"
#include "ui_SetUserDialog.h"
#include "common/misc.h"
struct SetUserDialog::Private {
Git::User global_user;
Git::User repo_user;
AvatarLoader avatar_loader;
};
-SetUserDialog::SetUserDialog(MainWindow *parent, Git::User const &global_user, Git::User const &repo_user, QString const &repo)
+SetUserDialog::SetUserDialog(BasicMainWindow *parent, Git::User const &global_user, Git::User const &repo_user, QString const &repo)
: QDialog(parent)
, ui(new Ui::SetUserDialog)
, m(new Private)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
misc::setFixedSize(this);
m->global_user = global_user;
m->repo_user = repo_user;
if (repo.isEmpty()) {
ui->radioButton_repository->setEnabled(false);
} else {
QString text = tr("Repository");
text += " : ";
text += repo;
ui->radioButton_repository->setText(text);
}
ui->radioButton_global->click();
ui->lineEdit_name->setFocus();
m->avatar_loader.start(mainwindow()->webContext());
connect(&m->avatar_loader, &AvatarLoader::updated, [&](){
QString email = ui->lineEdit_mail->text();
QIcon icon = m->avatar_loader.fetch(email.toStdString(), false);
setAvatar(icon);
});
}
SetUserDialog::~SetUserDialog()
{
m->avatar_loader.stop();
delete m;
delete ui;
}
-MainWindow *SetUserDialog::mainwindow()
+BasicMainWindow *SetUserDialog::mainwindow()
{
- return qobject_cast<MainWindow *>(parent());
+ return qobject_cast<BasicMainWindow *>(parent());
}
bool SetUserDialog::isGlobalChecked() const
{
return ui->radioButton_global->isChecked();
}
bool SetUserDialog::isRepositoryChecked() const
{
return ui->radioButton_repository->isChecked();
}
Git::User SetUserDialog::user() const
{
Git::User user;
user.name = ui->lineEdit_name->text();
user.email = ui->lineEdit_mail->text();
return user;
}
void SetUserDialog::setAvatar(QIcon icon)
{
QPixmap pm = icon.pixmap(QSize(64, 64));
ui->label_avatar->setPixmap(pm);
}
void SetUserDialog::on_radioButton_global_toggled(bool checked)
{
if (checked) {
ui->lineEdit_name->setText(m->global_user.name);
ui->lineEdit_mail->setText(m->global_user.email);
}
}
void SetUserDialog::on_radioButton_repository_toggled(bool checked)
{
if (checked) {
ui->lineEdit_name->setText(m->repo_user.name);
ui->lineEdit_mail->setText(m->repo_user.email);
}
}
void SetUserDialog::on_lineEdit_name_textChanged(QString const &text)
{
if (isGlobalChecked()) {
m->global_user.name = text;
}
if (isRepositoryChecked()) {
m->repo_user.name = text;
}
}
void SetUserDialog::on_lineEdit_mail_textChanged(QString const &text)
{
if (isGlobalChecked()) {
m->global_user.email = text;
}
if (isRepositoryChecked()) {
m->repo_user.email = text;
}
}
void SetUserDialog::on_pushButton_get_icon_clicked()
{
ui->label_avatar->setPixmap(QPixmap());
QString email = ui->lineEdit_mail->text();
if (email.indexOf('@') > 0) {
QIcon icon = m->avatar_loader.fetch(email.toStdString(), true);
if (!icon.isNull()) {
setAvatar(icon);
}
}
}
diff --git a/src/SetUserDialog.h b/src/SetUserDialog.h
index 31c19a9..4ddc934 100644
--- a/src/SetUserDialog.h
+++ b/src/SetUserDialog.h
@@ -1,45 +1,39 @@
#ifndef SETUSERDIALOG_H
#define SETUSERDIALOG_H
#include "Git.h"
#include <QDialog>
-class MainWindow;
+class BasicMainWindow;
namespace Ui {
class SetUserDialog;
}
-class SetUserDialog : public QDialog
-{
+class SetUserDialog : public QDialog {
Q_OBJECT
private:
+ Ui::SetUserDialog *ui;
struct Private;
Private *m;
+
+ void setAvatar(QIcon icon);
+ BasicMainWindow *mainwindow();
public:
- explicit SetUserDialog(MainWindow *parent, const Git::User &global_user, const Git::User &repo_user, QString const &repo);
- ~SetUserDialog();
+ explicit SetUserDialog(BasicMainWindow *parent, const Git::User &global_user, const Git::User &repo_user, QString const &repo);
+ ~SetUserDialog() override;
bool isGlobalChecked() const;
bool isRepositoryChecked() const;
Git::User user() const;
private slots:
void on_radioButton_global_toggled(bool checked);
-
void on_radioButton_repository_toggled(bool checked);
-
void on_lineEdit_name_textChanged(QString const &text);
-
void on_lineEdit_mail_textChanged(QString const &text);
-
void on_pushButton_get_icon_clicked();
-
-private:
- Ui::SetUserDialog *ui;
- void setAvatar(QIcon icon);
- MainWindow *mainwindow();
};
#endif // SETUSERDIALOG_H
diff --git a/src/SettingBehaviorForm.h b/src/SettingBehaviorForm.h
index 210f929..05b1e31 100644
--- a/src/SettingBehaviorForm.h
+++ b/src/SettingBehaviorForm.h
@@ -1,32 +1,30 @@
#ifndef SETTINGBEHAVIORFORM_H
#define SETTINGBEHAVIORFORM_H
#include "AbstractSettingForm.h"
#include <QWidget>
namespace Ui {
class SettingBehaviorForm;
}
-class SettingBehaviorForm : public AbstractSettingForm
-{
+class SettingBehaviorForm : public AbstractSettingForm {
Q_OBJECT
-
-public:
- explicit SettingBehaviorForm(QWidget *parent = 0);
- ~SettingBehaviorForm();
-
private:
Ui::SettingBehaviorForm *ui;
unsigned int getWatchRemoteChangesEveryMins();
void setWatchRemoteChangesEveryMins(unsigned int min);
public:
- void exchange(bool save);
+ explicit SettingBehaviorForm(QWidget *parent = nullptr);
+ ~SettingBehaviorForm() override;
+
+public:
+ void exchange(bool save) override;
private slots:
void on_pushButton_signing_policy_clicked();
void on_pushButton_browse_default_working_dir_clicked();
};
#endif // SETTINGBEHAVIORFORM_H
diff --git a/src/SettingExampleForm.h b/src/SettingExampleForm.h
index ac39be7..c390382 100644
--- a/src/SettingExampleForm.h
+++ b/src/SettingExampleForm.h
@@ -1,25 +1,23 @@
#ifndef SETTINGEXAMPLEFORM_H
#define SETTINGEXAMPLEFORM_H
#include "AbstractSettingForm.h"
#include <QWidget>
namespace Ui {
class SettingExampleForm;
}
-class SettingExampleForm : public AbstractSettingForm
-{
+class SettingExampleForm : public AbstractSettingForm {
Q_OBJECT
-
public:
- explicit SettingExampleForm(QWidget *parent = 0);
- ~SettingExampleForm();
- void exchange(bool save);
+ explicit SettingExampleForm(QWidget *parent = nullptr);
+ ~SettingExampleForm() override;
+ void exchange(bool save) override;
private:
Ui::SettingExampleForm *ui;
};
#endif // SETTINGEXAMPLEFORM_H
diff --git a/src/SettingGeneralForm.h b/src/SettingGeneralForm.h
index bfe4f2a..25e3560 100644
--- a/src/SettingGeneralForm.h
+++ b/src/SettingGeneralForm.h
@@ -1,36 +1,34 @@
#ifndef SETTINGGENERALFORM_H
#define SETTINGGENERALFORM_H
#include "AbstractSettingForm.h"
#include "SelectItemDialog.h"
#include <QWidget>
namespace Ui {
class SettingGeneralForm;
}
class SettingGeneralForm : public AbstractSettingForm
{
Q_OBJECT
private:
+ Ui::SettingGeneralForm *ui;
QList<SelectItemDialog::Item> langs;
QList<SelectItemDialog::Item> themes;
-public:
- explicit SettingGeneralForm(QWidget *parent = 0);
- ~SettingGeneralForm();
-
-private:
- Ui::SettingGeneralForm *ui;
void updateLanguage();
void updateTheme();
public:
- void exchange(bool save);
+ explicit SettingGeneralForm(QWidget *parent = nullptr);
+ ~SettingGeneralForm() override;
+
+ void exchange(bool save) override;
private slots:
void on_pushButton_browse_default_working_dir_clicked();
void on_pushButton_change_language_clicked();
void on_pushButton_change_theme_clicked();
};
#endif // SETTINGGENERALFORM_H
diff --git a/src/SettingNetworkForm.h b/src/SettingNetworkForm.h
index acbfad7..486259f 100644
--- a/src/SettingNetworkForm.h
+++ b/src/SettingNetworkForm.h
@@ -1,31 +1,27 @@
#ifndef SETTINGNETWORKFORM_H
#define SETTINGNETWORKFORM_H
#include "AbstractSettingForm.h"
#include <QWidget>
namespace Ui {
class SettingNetworkForm;
}
-class SettingNetworkForm : public AbstractSettingForm
-{
+class SettingNetworkForm : public AbstractSettingForm {
Q_OBJECT
+private:
+ Ui::SettingNetworkForm *ui;
+ void updateProxyServerLineEdit();
public:
- explicit SettingNetworkForm(QWidget *parent = 0);
- ~SettingNetworkForm();
- void exchange(bool save);
+ explicit SettingNetworkForm(QWidget *parent = nullptr);
+ ~SettingNetworkForm() override;
+ void exchange(bool save) override;
private slots:
void on_radioButton_no_proxy_clicked();
-
void on_radioButton_auto_detect_clicked();
-
void on_radioButton_manual_clicked();
-
-private:
- Ui::SettingNetworkForm *ui;
- void updateProxyServerLineEdit();
};
#endif // SETTINGNETWORKFORM_H
diff --git a/src/SettingProgramsForm.h b/src/SettingProgramsForm.h
index d4d50e0..bd1bd38 100644
--- a/src/SettingProgramsForm.h
+++ b/src/SettingProgramsForm.h
@@ -1,27 +1,25 @@
#ifndef SETTINGPROGRAMSFORM_H
#define SETTINGPROGRAMSFORM_H
#include "AbstractSettingForm.h"
namespace Ui {
class SettingProgramsForm;
}
-class SettingProgramsForm : public AbstractSettingForm
-{
+class SettingProgramsForm : public AbstractSettingForm {
Q_OBJECT
-
+private:
+ Ui::SettingProgramsForm *ui;
public:
- explicit SettingProgramsForm(QWidget *parent = 0);
- ~SettingProgramsForm();
- void exchange(bool save);
+ explicit SettingProgramsForm(QWidget *parent = nullptr);
+ ~SettingProgramsForm() override;
+ void exchange(bool save) override;
private slots:
void on_pushButton_select_git_command_clicked();
void on_pushButton_select_file_command_clicked();
void on_pushButton_select_gpg_command_clicked();
-private:
- Ui::SettingProgramsForm *ui;
};
#endif // SETTINGPROGRAMSFORM_H
diff --git a/src/SettingsDialog.h b/src/SettingsDialog.h
index 5292524..9e16478 100644
--- a/src/SettingsDialog.h
+++ b/src/SettingsDialog.h
@@ -1,62 +1,56 @@
#ifndef SETTINGSDIALOG_H
#define SETTINGSDIALOG_H
#include <QDialog>
#include "MainWindow.h"
#include "main.h"
namespace Ui {
class SettingsDialog;
}
class QTreeWidgetItem;
class SettingsDialog : public QDialog
{
Q_OBJECT
public:
ApplicationSettings set;
private:
+ Ui::SettingsDialog *ui;
MainWindow *mainwindow_;
+ void exchange(bool save);
+
void loadSettings();
void saveSettings();
public:
explicit SettingsDialog(MainWindow *parent);
- ~SettingsDialog();
+ ~SettingsDialog() override;
MainWindow *mainwindow()
{
return mainwindow_;
}
SettingsDialog *dialog()
{
return this;
}
ApplicationSettings const &settings() const
{
return set;
}
static void loadSettings(ApplicationSettings *as);
static void saveSettings(const ApplicationSettings *as);
private slots:
-
void on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
-
-private:
- Ui::SettingsDialog *ui;
-
- void exchange(bool save);
-public slots:
- void accept();
-
- // QDialog interface
public slots:
- void done(int);
+ void accept() override;
+ void done(int) override;
};
#endif // SETTINGSDIALOG_H
diff --git a/src/StatusLabel.h b/src/StatusLabel.h
index e6a9ec0..d45e68d 100644
--- a/src/StatusLabel.h
+++ b/src/StatusLabel.h
@@ -1,14 +1,13 @@
#ifndef STATUSLABEL_H
#define STATUSLABEL_H
#include <QLabel>
-class StatusLabel : public QLabel
-{
+class StatusLabel : public QLabel {
Q_OBJECT
public:
- explicit StatusLabel(QWidget *parent = 0);
- QSize minimumSizeHint() const;
+ explicit StatusLabel(QWidget *parent = nullptr);
+ QSize minimumSizeHint() const override;
};
#endif // STATUSLABEL_H
diff --git a/src/TextEditDialog.h b/src/TextEditDialog.h
index 7590772..49fabfb 100644
--- a/src/TextEditDialog.h
+++ b/src/TextEditDialog.h
@@ -1,31 +1,26 @@
#ifndef TEXTEDITDIALOG_H
#define TEXTEDITDIALOG_H
#include <QDialog>
namespace Ui {
class TextEditDialog;
}
-class TextEditDialog : public QDialog
-{
+class TextEditDialog : public QDialog {
Q_OBJECT
-
+private:
+ Ui::TextEditDialog *ui;
+protected:
+ void keyPressEvent(QKeyEvent *) override;
public:
- explicit TextEditDialog(QWidget *parent = 0);
- ~TextEditDialog();
+ explicit TextEditDialog(QWidget *parent = nullptr);
+ ~TextEditDialog() override;
void setText(QString const &text);
QString text() const;
-
static bool editFile(QWidget *parent, QString path, QString const &title, QString const &append = QString());
-private:
- Ui::TextEditDialog *ui;
-
- // QWidget interface
-protected:
- void keyPressEvent(QKeyEvent *);
};
#endif // TEXTEDITDIALOG_H
diff --git a/src/Theme.cpp b/src/Theme.cpp
index eac0141..351865d 100644
--- a/src/Theme.cpp
+++ b/src/Theme.cpp
@@ -1,130 +1,130 @@
#include "Theme.h"
#include <QApplication>
#include <QRgb>
#include <QProxyStyle>
// AbstractTheme
// StandardTheme
-#include "../darktheme/StandardStyle.h"
+#include "darktheme/StandardStyle.h"
QStyle *StandardTheme::newStyle()
{
return new StandardStyle();
}
QImage StandardTheme::graphColorMap()
{
QImage image;
image.load(":/image/graphcolor.png");
return image;
}
QPixmap StandardTheme::resource_clear_png()
{
return QPixmap(":/image/clear.png");
}
QPixmap StandardTheme::resource_maximize_png()
{
return QPixmap(":/image/maximize.png");
}
QPixmap StandardTheme::resource_menu_png()
{
return QPixmap(":/image/menu.png");
}
ThemePtr createStandardTheme()
{
AbstractTheme *p = new StandardTheme;
p->text_editor_theme = TextEditorTheme::Light();
p->dialog_header_frame_bg = Qt::white;
p->diff_slider_normal_bg = Qt::white;
p->diff_slider_unknown_bg = QColor(208, 208, 208);
p->diff_slider_add_bg = QColor(64, 192, 64);
p->diff_slider_del_bg = QColor(240, 64, 64);
p->diff_slider_handle = Qt::black;
return ThemePtr(p);
}
-//#ifdef USE_DARK_THEME
+#ifdef USE_DARK_THEME
#include "darktheme/DarkStyle.h"
// DarkTheme
QStyle *DarkTheme::newStyle()
{
return new DarkStyle();
}
QImage DarkTheme::graphColorMap()
{
QImage image;
image.load(":/darktheme/graphcolor.png");
return image;
}
static QImage loadInvertedImage(QString const &path)
{
QImage img(path);
int w = img.width();
int h = img.height();
for (int y = 0; y < h; y++) {
QRgb *p = (QRgb *)img.scanLine(y);
for (int x = 0; x < w; x++) {
int r = qRed(*p);
int g = qGreen(*p);
int b = qBlue(*p);
int a = qAlpha(*p);
*p = qRgba(255 - r, 255 - g, 255 - b, a);
p++;
}
}
return img;
}
QPixmap DarkTheme::resource_clear_png()
{
QImage img = loadInvertedImage(":/image/clear.png");
return QPixmap::fromImage(img);
}
QPixmap DarkTheme::resource_maximize_png()
{
QImage img = loadInvertedImage(":/image/maximize.png");
return QPixmap::fromImage(img);
}
QPixmap DarkTheme::resource_menu_png()
{
QImage img = loadInvertedImage(":/image/menu.png");
return QPixmap::fromImage(img);
}
ThemePtr createDarkTheme()
{
AbstractTheme *p = new DarkTheme;
p->text_editor_theme = TextEditorTheme::Dark();
p->dialog_header_frame_bg = QColor(32, 32, 32);
p->diff_slider_normal_bg = QColor(48, 48, 48);
p->diff_slider_unknown_bg = QColor(0, 0, 0);
p->diff_slider_add_bg = QColor(0, 144, 0);
p->diff_slider_del_bg = QColor(160, 0, 0);
p->diff_slider_handle = QColor(255, 255, 255);
return ThemePtr(p);
}
-//#endif // USE_DAR_THEME
+#endif // USE_DAR_THEME
diff --git a/src/Theme.h b/src/Theme.h
index 5095c0b..41d906f 100644
--- a/src/Theme.h
+++ b/src/Theme.h
@@ -1,60 +1,62 @@
#ifndef THEME_H
#define THEME_H
#include <QImage>
#include <QPalette>
#include <memory>
#include "TextEditorTheme.h"
+#define USE_DARK_THEME
+
class QStyle;
class AbstractTheme {
public:
TextEditorThemePtr text_editor_theme;
QColor dialog_header_frame_bg;
QColor diff_slider_normal_bg;
QColor diff_slider_unknown_bg;
QColor diff_slider_add_bg;
QColor diff_slider_del_bg;
QColor diff_slider_handle;
AbstractTheme() = default;
virtual ~AbstractTheme() = default;
virtual QStyle *newStyle() = 0;
virtual QImage graphColorMap() = 0;
virtual QPixmap resource_clear_png() = 0;
virtual QPixmap resource_maximize_png() = 0;
virtual QPixmap resource_menu_png() = 0;
};
using ThemePtr = std::shared_ptr<AbstractTheme>;
class StandardTheme : public AbstractTheme {
public:
StandardTheme() = default;
QStyle *newStyle() override;
QImage graphColorMap() override;
QPixmap resource_clear_png() override;
QPixmap resource_maximize_png() override;
QPixmap resource_menu_png() override;
};
ThemePtr createStandardTheme();
-// #ifdef USE_DARK_THEME
+#ifdef USE_DARK_THEME
class DarkTheme : public AbstractTheme {
public:
DarkTheme() = default;
QStyle *newStyle() override;
QImage graphColorMap() override;
QPixmap resource_clear_png() override;
QPixmap resource_maximize_png() override;
QPixmap resource_menu_png() override;
};
ThemePtr createDarkTheme();
-// #endif
+#endif
#endif // THEME_H
diff --git a/src/WelcomeWizardDialog.cpp b/src/WelcomeWizardDialog.cpp
index f634ba6..2af1a85 100644
--- a/src/WelcomeWizardDialog.cpp
+++ b/src/WelcomeWizardDialog.cpp
@@ -1,190 +1,190 @@
#include "WelcomeWizardDialog.h"
#include "ui_WelcomeWizardDialog.h"
#include <QFileDialog>
#include "MainWindow.h"
#include "common/misc.h"
#include "Git.h"
-WelcomeWizardDialog::WelcomeWizardDialog(MainWindow *parent) :
- QDialog(parent),
- ui(new Ui::WelcomeWizardDialog)
+WelcomeWizardDialog::WelcomeWizardDialog(BasicMainWindow *parent)
+ : QDialog(parent)
+ , ui(new Ui::WelcomeWizardDialog)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
mainwindow_ = parent;
pages_.push_back(ui->page_helper_tools);
pages_.push_back(ui->page_global_user_information);
pages_.push_back(ui->page_default_working_folder);
pages_.push_back(ui->page_finish);
ui->stackedWidget->setCurrentWidget(pages_[0]);
on_stackedWidget_currentChanged(0);
avatar_loader_.start(mainwindow_->webContext());
connect(&avatar_loader_, &AvatarLoader::updated, [&](){
QString email = ui->lineEdit_user_email->text();
QIcon icon = avatar_loader_.fetch(email.toStdString(), false);
setAvatar(icon);
});
ui->lineEdit_user_name->setFocus();
}
WelcomeWizardDialog::~WelcomeWizardDialog()
{
avatar_loader_.stop();
delete ui;
}
void WelcomeWizardDialog::set_user_name(QString const &v)
{
ui->lineEdit_user_name->setText(v);
}
void WelcomeWizardDialog::set_user_email(QString const &v)
{
ui->lineEdit_user_email->setText(v);
}
void WelcomeWizardDialog::set_default_working_folder(QString const &v)
{
ui->lineEdit_default_working_folder->setText(v);
}
void WelcomeWizardDialog::set_git_command_path(QString const &v)
{
ui->lineEdit_git->setText(v);
}
void WelcomeWizardDialog::set_file_command_path(QString const &v)
{
ui->lineEdit_file->setText(v);
}
QString WelcomeWizardDialog::user_name() const
{
return ui->lineEdit_user_name->text();
}
QString WelcomeWizardDialog::user_email() const
{
return ui->lineEdit_user_email->text();
}
QString WelcomeWizardDialog::default_working_folder() const
{
return ui->lineEdit_default_working_folder->text();
}
QString WelcomeWizardDialog::git_command_path() const
{
return ui->lineEdit_git->text();
}
QString WelcomeWizardDialog::file_command_path() const
{
return ui->lineEdit_file->text();
}
void WelcomeWizardDialog::on_pushButton_prev_clicked()
{
int i = pages_.indexOf(ui->stackedWidget->currentWidget());
if (i == 0) {
done(QDialog::Rejected);
return;
}
if (i > 0) {
i--;
QWidget *w = pages_[i];
ui->stackedWidget->setCurrentWidget(w);
}
}
void WelcomeWizardDialog::on_pushButton_next_clicked()
{
if (ui->stackedWidget->currentWidget() == ui->page_finish) {
done(QDialog::Accepted);
}
int i = pages_.indexOf(ui->stackedWidget->currentWidget());
if (i + 1 < pages_.size()) {
i++;
QWidget *w = pages_[i];
ui->stackedWidget->setCurrentWidget(w);
}
}
void WelcomeWizardDialog::on_stackedWidget_currentChanged(int /*arg1*/)
{
QString prev_text;
QString next_text;
QWidget *w = ui->stackedWidget->currentWidget();
if (w == ui->page_helper_tools) {
prev_text = tr("Cancel");
ui->lineEdit_git->setFocus();
} else if (w == ui->page_global_user_information) {
if (user_name().isEmpty() && user_email().isEmpty()) {
Git::Context gcx;
gcx.git_command = git_command_path();
Git g(gcx, QString());
Git::User user = g.getUser(Git::Source::Global);
set_user_name(user.name);
set_user_email(user.email);
}
} else if (w == ui->page_default_working_folder) {
ui->lineEdit_default_working_folder->setFocus();
} else if (w == ui->page_finish) {
ui->lineEdit_preview_user->setText(ui->lineEdit_user_name->text());
ui->lineEdit_preview_email->setText(ui->lineEdit_user_email->text());
ui->lineEdit_preview_folder->setText(ui->lineEdit_default_working_folder->text());
ui->lineEdit_preview_git->setText(ui->lineEdit_git->text());
ui->lineEdit_preview_file->setText(ui->lineEdit_file->text());
next_text = tr("Finish");
}
ui->pushButton_prev->setText(prev_text.isEmpty() ? tr("<< Prev") : prev_text);
ui->pushButton_next->setText(next_text.isEmpty() ? tr("Next >>") : next_text);
ui->pushButton_next->setDefault(true);
}
void WelcomeWizardDialog::on_pushButton_browse_default_workiing_folder_clicked()
{
QString s = ui->lineEdit_default_working_folder->text();
s = QFileDialog::getExistingDirectory(this, tr("Default Working Folder"), s);
s = misc::normalizePathSeparator(s);
ui->lineEdit_default_working_folder->setText(s);
}
void WelcomeWizardDialog::on_pushButton_browse_git_clicked()
{
QString s = mainwindow_->selectGitCommand(false);
ui->lineEdit_git->setText(s);
}
void WelcomeWizardDialog::on_pushButton_browse_file_clicked()
{
QString s = mainwindow_->selectFileCommand(false);
ui->lineEdit_file->setText(s);
}
void WelcomeWizardDialog::setAvatar(QIcon const &icon)
{
QPixmap pm = icon.pixmap(QSize(64, 64));
ui->label_avatar->setPixmap(pm);
}
void WelcomeWizardDialog::on_pushButton_get_icon_clicked()
{
ui->label_avatar->setPixmap(QPixmap());
QString email = ui->lineEdit_user_email->text();
if (email.indexOf('@') > 0) {
QIcon icon = avatar_loader_.fetch(email.toStdString(), true);
if (!icon.isNull()) {
setAvatar(icon);
}
}
}
diff --git a/src/WelcomeWizardDialog.h b/src/WelcomeWizardDialog.h
index 27e32f2..a6d8774 100644
--- a/src/WelcomeWizardDialog.h
+++ b/src/WelcomeWizardDialog.h
@@ -1,51 +1,48 @@
#ifndef WELCOMEWIZARDDIALOG_H
#define WELCOMEWIZARDDIALOG_H
#include "AvatarLoader.h"
#include <QDialog>
-class MainWindow;
+class BasicMainWindow;
namespace Ui {
class WelcomeWizardDialog;
}
-class WelcomeWizardDialog : public QDialog
-{
+class WelcomeWizardDialog : public QDialog {
Q_OBJECT
private:
- MainWindow *mainwindow_;
+ BasicMainWindow *mainwindow_;
AvatarLoader avatar_loader_;
QList<QWidget *> pages_;
public:
- explicit WelcomeWizardDialog(MainWindow *parent = 0);
- ~WelcomeWizardDialog();
+ explicit WelcomeWizardDialog(BasicMainWindow *parent = nullptr);
+ ~WelcomeWizardDialog() override;
void set_user_name(QString const &v);
void set_user_email(QString const &v);
void set_default_working_folder(QString const &v);
void set_git_command_path(QString const &v);
void set_file_command_path(QString const &v);
QString user_name() const;
QString user_email() const;
QString default_working_folder() const;
QString git_command_path() const;
QString file_command_path() const;
private slots:
void on_stackedWidget_currentChanged(int arg1);
void on_pushButton_browse_default_workiing_folder_clicked();
void on_pushButton_browse_git_clicked();
void on_pushButton_browse_file_clicked();
void on_pushButton_get_icon_clicked();
void on_pushButton_prev_clicked();
-
void on_pushButton_next_clicked();
-
private:
Ui::WelcomeWizardDialog *ui;
void setAvatar(const QIcon &icon);
};
#endif // WELCOMEWIZARDDIALOG_H
diff --git a/src/charvec.cpp b/src/charvec.cpp
index 9078faa..572ea6b 100644
--- a/src/charvec.cpp
+++ b/src/charvec.cpp
@@ -1,47 +1,47 @@
#include "charvec.h"
-#include <string.h>
-#include <stdlib.h>
+#include <cstring>
+#include <cstdlib>
void print(std::vector<char> *out, char c)
{
out->push_back(c);
}
void print(std::vector<char> *out, char const *begin, char const *end)
{
out->insert(out->end(), begin, end);
}
void print(std::vector<char> *out, char const *ptr, size_t len)
{
print(out, ptr, ptr + len);
}
void print(std::vector<char> *out, char const *s)
{
print(out, s, s + strlen(s));
}
void print(std::vector<char> *out, std::string const &s)
{
print(out, s.c_str(), s.size());
}
void print(std::vector<char> *out, std::vector<char> const *in)
{
if (in && !in->empty()) {
char const *begin = &(*in)[0];
char const *end = begin + in->size();
print(out, begin, end);
}
}
std::string to_stdstr(std::vector<char> const &vec)
{
if (!vec.empty()) {
char const *begin = &vec.at(0);
char const *end = begin + vec.size();
return std::string(begin, end);
}
return std::string();
}
diff --git a/src/gpg.h b/src/gpg.h
index 31e7c02..079e7ed 100644
--- a/src/gpg.h
+++ b/src/gpg.h
@@ -1,51 +1,24 @@
#ifndef GPG_H
#define GPG_H
#include <QDateTime>
#include <QString>
-
class gpg {
public:
struct Data {
QString id;
unsigned int year = 0;
unsigned int month = 0;
unsigned int day = 0;
QString name;
QString comment;
QString mail;
QByteArray fingerprint;
};
static void parse(char const *begin, char const *end, QList<gpg::Data> *keys);
static bool listKeys(QString const &gpg_command, QList<gpg::Data> *keys);
-
-
-
-
-
-
-//public:
-// struct Key {
-// QString id;
-// QDateTime timestamp;
-// };
-
-// struct UID {
-// QString name;
-// QString email;
-// QString comment;
-// };
-
-// struct Item {
-// Key pub;
-// QList<Key> sub;
-// QList<UID> uid;
-// };
-
-// static QList<Item> load(QByteArray const &json);
-
};
#endif // GPG_H
diff --git a/src/gunzip.cpp b/src/gunzip.cpp
index 958c0ad..abf36f7 100644
--- a/src/gunzip.cpp
+++ b/src/gunzip.cpp
@@ -1,228 +1,228 @@
#include "gunzip.h"
#include <zlib.h>
-#include <stdint.h>
+#include <cstdint>
#include <QFile>
void gunzip::set_header_only(bool f)
{
header_only = f;
}
void gunzip::set_maximul_size(int64_t size)
{
maxsize = size;
}
bool gunzip::decode(QIODevice *input, QIODevice *output)
{
error = QString();
try {
struct Header {
uint8_t id1;
uint8_t id2;
uint8_t cm;
uint8_t flg;
uint8_t mtime[4];
uint8_t xfl;
uint8_t os;
};
enum {
FTEXT = 0x01,
FHCRC = 0x02,
FEXTRA = 0x04,
FNAME = 0x08,
FCOMMENT = 0x10,
};
unsigned char ibuf[4096];
bool ok = true;
int n;
n = input->read((char *)ibuf, sizeof(Header));
if (n != 10) {
throw QString("failed to read the header");
}
auto *h = (Header *)ibuf;
if (h->id1 == 0x1f && h->id2 == 0x8b && h->cm == 8) {
// ok
if (header_only) {
return true;
}
} else {
throw QString("invalid input format");
}
auto ReadText = [&](){
char c;
std::vector<uint8_t> vec;
while (1) {
if (input->read(&c, 1) != 1) {
break;
}
if (c == 0) break;
vec.push_back((uint8_t)c);
}
std::string str;
if (!vec.empty()) {
str.assign((char const *)&vec[0], vec.size());
}
return str;
};
if (h->flg & FEXTRA) {
n = input->read((char *)ibuf, 2);
n = ((uint8_t)ibuf[1] << 8) | (uint8_t)ibuf[0];
input->seek(input->pos() + n);
}
if (h->flg & FNAME) {
std::string name = ReadText();
(void)name;
}
if (h->flg & FCOMMENT) {
std::string comment = ReadText();
(void)comment;
}
if (h->flg & FHCRC) {
input->read((char *)ibuf, 2);
}
z_stream stream;
uint32_t crc = 0;
int err;
stream.zalloc = nullptr;
stream.zfree = nullptr;
stream.opaque = nullptr;
stream.next_in = ibuf;
stream.avail_in = 0;
err = inflateInit2(&stream, -MAX_WBITS);
if (err != Z_OK) {
throw QString("inflateInit2 faled");
}
if (open) {
if (!open(output)) {
ok = false;
}
} else {
if (!output->open(QIODevice::WriteOnly)) {
ok = false;
}
}
if (!ok) throw QString("failed to open the output device");
int64_t inpos = input->pos();
auto Close = [&](){
if (close) {
close(output);
} else {
output->close();
}
inflateEnd(&stream);
};
try {
while (1) {
if (stream.avail_in == 0) {
stream.next_in = ibuf;
}
if (stream.avail_in < sizeof(ibuf)) {
stream.next_in = ibuf + stream.avail_in;
auto len = input->read((char *)stream.next_in, sizeof(ibuf) - stream.avail_in);
stream.avail_in += len;
}
unsigned char obuf[65536];
stream.next_out = obuf; /* discard the output */
stream.avail_out = sizeof(obuf);
if (maxsize != -1 && stream.total_out + stream.avail_out > (unsigned)maxsize && (unsigned)maxsize >= stream.total_out) {
stream.avail_out = maxsize - stream.total_out;
}
uLong total_out = stream.total_out;
err = ::inflate(&stream, Z_NO_FLUSH);
int n = stream.total_out - total_out;
if (write) {
if (!write(output, (char const *)obuf, n)) {
ok = false;
}
} else {
int w = output->write((char const *)obuf, n);
if (w != n) {
ok = false;
}
}
if (!ok) throw QString("failed to write to the output device");
crc = crc32(crc, (unsigned char const *)obuf, n);
if (err == Z_STREAM_END) {
break;
}
if (err != Z_OK) {
throw QString("inflate failed");
}
if (stream.total_out >= (unsigned)maxsize) {
break;
}
}
} catch (...) {
Close();
throw;
}
Close();
input->seek(inpos + stream.total_in);
n = input->read((char *)ibuf, 8);
auto ReadU32LE = [](void const *p)->uint32_t{
auto const *q = (uint8_t const *)p;
return (q[3] << 24) | (q[2] << 16) | (q[1] << 8) | q[0];
};
auto c = ReadU32LE(ibuf);
auto l = ReadU32LE(ibuf + 4);
if (c != crc) throw QString("crc incorrect");
if (l != stream.total_out) throw QString("length incorrect");
return true;
} catch (QString const &e) {
error = e;
}
return false;
}
bool gunzip::decode(QString const &inpath, QString const &outpath)
{
QFile infile(inpath);
QFile outfile(outpath);
if (infile.open(QFile::ReadOnly)) {
if (decode(&infile, &outfile)) {
return true;
}
}
return false;
}
bool gunzip::is_valid_gz_file(QIODevice *input)
{
gunzip z;
z.set_header_only(true);
return z.decode(input, nullptr);
}
bool gunzip::is_valid_gz_file(QString const &inpath)
{
QFile infile(inpath);
if (infile.open(QFile::ReadOnly)) {
gunzip z;
if (z.is_valid_gz_file(&infile)) {
return true;
}
}
return false;
}
diff --git a/src/gunzip.h b/src/gunzip.h
index ffb0077..e78de7b 100644
--- a/src/gunzip.h
+++ b/src/gunzip.h
@@ -1,28 +1,28 @@
#ifndef GUNZIP_H
#define GUNZIP_H
#include <QIODevice>
#include <string>
#include <functional>
-#include <stdint.h>
+#include <cstdint>
class gunzip {
public:
QString error;
bool header_only = false;
int64_t maxsize = -1;
std::function<bool(QIODevice *)> open;
std::function<void(QIODevice *)> close;
std::function<bool(QIODevice *, char const *ptr, int len)> write;
void set_header_only(bool f);
void set_maximul_size(int64_t size);
bool decode(QIODevice *input, QIODevice *output);
bool decode(QString const &inpath, QString const &outpath);
static bool is_valid_gz_file(QIODevice *input);
static bool is_valid_gz_file(QString const &inpath);
};
#endif // GUNZIP_H
diff --git a/src/main.cpp b/src/main.cpp
index 5df87a3..4b159aa 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,144 +1,144 @@
-#include "MainWindow.h"
-#include <QApplication>
+
+#include "darktheme/DarkStyle.h"
#include "ApplicationGlobal.h"
+#include "MainWindow.h"
#include "MySettings.h"
+#include "common/joinpath.h"
+#include "common/misc.h"
#include "main.h"
-#include <string>
-#include <QMessageBox>
-#include <QDir>
+#include "platform.h"
+#include "webclient.h"
+#include <QApplication>
#include <QDebug>
+#include <QDir>
+#include <QMessageBox>
#include <QProxyStyle>
-#include <QTranslator>
-#include "webclient.h"
-#include "win32/win32.h"
-#include "common/misc.h"
-#include "darktheme/DarkStyle.h"
-
#include <QStandardPaths>
-#include "common/joinpath.h"
+#include <QTranslator>
+#include <string>
ApplicationGlobal *global = nullptr;
ApplicationSettings ApplicationSettings::defaultSettings()
{
ApplicationSettings s;
s.proxy_server = "http://squid:3128/";
return s;
}
static bool isHighDpiScalingEnabled()
{
MySettings s;
s.beginGroup("UI");
QVariant v = s.value("EnableHighDpiScaling");
return v.isNull() || v.toBool();
}
int main(int argc, char *argv[])
{
putenv((char *)"UNICODEMAP_JP=cp932");
ApplicationGlobal g;
global = &g;
global->organization_name = ORGANIZATION_NAME;
global->application_name = APPLICATION_NAME;
global->generic_config_dir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
global->app_config_dir = global->generic_config_dir / global->organization_name / global->application_name;
global->config_file_path = joinpath(global->app_config_dir, global->application_name + ".ini");
if (!QFileInfo(global->app_config_dir).isDir()) {
QDir().mkpath(global->app_config_dir);
}
if (isHighDpiScalingEnabled()){
#if (QT_VERSION < QT_VERSION_CHECK(5, 6, 0))
qDebug() << "High DPI scaling is not supported";
#else
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
}
QApplication a(argc, argv);
QApplication::setOrganizationName(global->organization_name);
QApplication::setApplicationName(global->application_name);
{
MySettings s;
s.beginGroup("UI");
global->language_id = s.value("Language").toString();
global->theme_id = s.value("Theme").toString();
if (global->theme_id.compare("dark", Qt::CaseInsensitive) == 0) {
global->theme = createDarkTheme();
} else {
global->theme = createStandardTheme();
}
s.endGroup();
}
QApplication::setStyle(global->theme->newStyle());
if (QApplication::queryKeyboardModifiers() & Qt::ShiftModifier) {
global->start_with_shift_key = true;
}
WebClient::initialize();
bool f_open_here = false;
QStringList args;
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
if (arg[0] == '-') {
if (arg == "--open-here") {
f_open_here = true;
}
} else {
args.push_back(QString::fromStdString(arg));
}
}
if (global->app_config_dir.isEmpty()) {
QMessageBox::warning(nullptr, qApp->applicationName(), "Preparation of data storage folder failed.");
return 1;
}
QTranslator translator;
{
if (global->language_id.isEmpty() || global->language_id == "en") {
// thru
} else {
#if defined(Q_OS_MACX)
QString path = "../Resources/Guitar_" + global->language_id;
#else
QString path = "Guitar_" + global->language_id;
#endif
translator.load(path, QApplication::applicationDirPath());
QApplication::installTranslator(&translator);
}
}
MainWindow w;
global->panel_bg_color = w.palette().color(QPalette::Background);
w.setWindowIcon(QIcon(":/image/guitar.png"));
w.show();
w.shown();
if (f_open_here) {
QString dir = QDir::current().absolutePath();
w.autoOpenRepository(dir);
} else if (args.size() == 1) {
QString dir = args[0] / QString();
if (dir.startsWith("./") || dir.startsWith(".\\")) {
dir = QDir::current().absolutePath() / dir.mid(2);
}
QFileInfo fi(dir);
if (fi.isDir()) {
dir = fi.absolutePath();
w.autoOpenRepository(dir);
}
}
return QApplication::exec();
}
diff --git a/src/platform.h b/src/platform.h
new file mode 100644
index 0000000..6070579
--- /dev/null
+++ b/src/platform.h
@@ -0,0 +1,28 @@
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#include <QApplication>
+
+#ifdef Q_OS_WIN
+#include "win32/win32.h"
+#else
+#include <unistd.h>
+#endif
+
+#ifdef Q_OS_MAC
+extern "C" char **environ;
+#endif
+
+#ifdef Q_OS_WIN
+#define GIT_COMMAND "git.exe"
+#define FILE_COMMAND "file.exe"
+#define GPG_COMMAND "gpg.exe"
+#define GPG2_COMMAND "gpg2.exe"
+#else
+#define GIT_COMMAND "git"
+#define FILE_COMMAND "file"
+#define GPG_COMMAND "gpg"
+#define GPG2_COMMAND "gpg2"
+#endif
+
+#endif // PLATFORM_H
diff --git a/src/texteditor/AbstractCharacterBasedApplication.cpp b/src/texteditor/AbstractCharacterBasedApplication.cpp
index 6b43ce9..f78fc6f 100644
--- a/src/texteditor/AbstractCharacterBasedApplication.cpp
+++ b/src/texteditor/AbstractCharacterBasedApplication.cpp
@@ -1,2585 +1,2585 @@
#include <memory>
#include <memory>
#include "AbstractCharacterBasedApplication.h"
#include "UnicodeWidth.h"
#include "unicode.h"
#include <QApplication>
#include <QClipboard>
#include <QDebug>
#include <QFile>
#include <QTextCodec>
#include <memory>
using WriteMode = AbstractCharacterBasedApplication::WriteMode;
using FormattedLine = AbstractCharacterBasedApplication::FormattedLine;
class EsccapeSequence {
private:
int offset = 0;
unsigned char data[100];
int color_fg = -1;
int color_bg = -1;
public:
bool isActive() const
{
return offset > 0;
}
void write(char c)
{
if (c == 0x1b) {
data[0] = c;
offset = 1;
return;
}
data[offset] = c;
if (offset > 0) {
if (c == 'm') {
if (data[1] == '[' && isdigit(data[2]) && isdigit(data[3])) {
data[offset] = 0;
if (data[2] == '3') {
color_fg = atoi((char const *)data + 3);
if (color_fg == 9) {
color_fg = -1;
}
}
if (data[2] == '4') {
color_bg = atoi((char const *)data + 3);
if (color_bg == 9) {
color_bg = -1;
}
}
}
offset = 0;
return;
}
if (offset + 1 < (int)sizeof(data)) {
offset++;
}
}
}
int fg_color_code() const
{
return color_fg == 9 ? -1 : color_fg;
}
int bg_color_code() const
{
return color_bg == 9 ? -1 : color_bg;
}
};
struct AbstractCharacterBasedApplication::Private {
bool is_changed = false;
bool is_quit_enabled = false;
bool is_open_enabled = false;
bool is_save_enabled = false;
bool is_toggle_selection_anchor_enabled = true;
bool is_read_only = false;
bool is_terminal_mode = false;
bool is_cursor_visible = true;
State state = State::Normal;
int header_line = 1;
int footer_line = 1;
int screen_width = 80;
int screen_height = 24;
bool auto_layout = false;
QString recently_used_path;
bool show_line_number = true;
int left_margin = AbstractCharacterBasedApplication::LEFT_MARGIN;
QString dialog_title;
QString dialog_value;
std::vector<Character> screen;
std::vector<uint8_t> line_flags;
int parsed_row_index = -1;
int parsed_col_index = -1;
bool parsed_for_edit = false;
QByteArray parsed_line;
std::vector<uint32_t> prepared_current_line;
QList<Document::CharAttr_> syntax_table;
bool dialog_mode = false;
DialogHandler dialog_handler;
bool is_painting_suppressed = false;
int valid_line_index = -1;
int line_margin = 3;
WriteMode write_mode = WriteMode::Insert;
QTextCodec *text_codec = nullptr;
Qt::KeyboardModifiers keyboard_modifiers = Qt::KeyboardModifier::NoModifier;
bool ctrl_modifier = false;
bool shift_modifier = false;
EsccapeSequence escape_sequence;
bool cursor_moved_by_mouse = false;
};
AbstractCharacterBasedApplication::AbstractCharacterBasedApplication()
: m(new Private)
{
}
AbstractCharacterBasedApplication::~AbstractCharacterBasedApplication()
{
delete m;
}
void AbstractCharacterBasedApplication::setModifierKeys(Qt::KeyboardModifiers keymod)
{
m->keyboard_modifiers = keymod;
m->ctrl_modifier = m->keyboard_modifiers & Qt::ControlModifier;
m->shift_modifier = m->keyboard_modifiers & Qt::ShiftModifier;
}
void AbstractCharacterBasedApplication::clearShiftModifier()
{
m->shift_modifier = false;
}
bool AbstractCharacterBasedApplication::isControlModifierPressed() const
{
return m->ctrl_modifier;
}
bool AbstractCharacterBasedApplication::isShiftModifierPressed() const
{
return m->shift_modifier;
}
void AbstractCharacterBasedApplication::setTextCodec(QTextCodec *codec)
{
m->text_codec = codec;
clearParsedLine();
invalidateArea();
makeBuffer();
}
void AbstractCharacterBasedApplication::setAutoLayout(bool f)
{
m->auto_layout = f;
layoutEditor();
}
void AbstractCharacterBasedApplication::showHeader(bool f)
{
m->header_line = f ? 1 : 0;
layoutEditor();
}
void AbstractCharacterBasedApplication::showFooter(bool f)
{
m->footer_line = f ? 1 : 0;
layoutEditor();
}
void AbstractCharacterBasedApplication::showLineNumber(bool show, int left_margin)
{
m->show_line_number = show;
m->left_margin = left_margin;
}
void AbstractCharacterBasedApplication::setCursorVisible(bool show)
{
m->is_cursor_visible = show;
}
bool AbstractCharacterBasedApplication::isCursorVisible()
{
return m->is_cursor_visible;
}
void AbstractCharacterBasedApplication::retrieveLastText(std::vector<char> *out, int maxlen) const
{
const_cast<AbstractCharacterBasedApplication *>(this)->document()->retrieveLastText(out, maxlen);
}
bool AbstractCharacterBasedApplication::isChanged() const
{
return m->is_changed;
}
void AbstractCharacterBasedApplication::setChanged(bool f)
{
m->is_changed = f;
}
int AbstractCharacterBasedApplication::leftMargin_() const
{
return m->left_margin;
}
void AbstractCharacterBasedApplication::setRecentlyUsedPath(QString const &path)
{
m->recently_used_path = path;
}
QString AbstractCharacterBasedApplication::recentlyUsedPath()
{
return m->recently_used_path;
}
std::vector<AbstractCharacterBasedApplication::Character> *AbstractCharacterBasedApplication::screen()
{
return &m->screen;
}
const std::vector<AbstractCharacterBasedApplication::Character> *AbstractCharacterBasedApplication::screen() const
{
return &m->screen;
}
std::vector<uint8_t> *AbstractCharacterBasedApplication::line_flags()
{
return &m->line_flags;
}
void AbstractCharacterBasedApplication::makeBuffer()
{
int w = screenWidth();
int h = screenHeight();
int size = w * h;
m->screen.resize(size);
std::fill(m->screen.begin(), m->screen.end(), Character());
m->line_flags.resize(h);
}
void AbstractCharacterBasedApplication::layoutEditor()
{
makeBuffer();
editor_cx->viewport_org_x = leftMargin_();
editor_cx->viewport_org_y = m->header_line;
editor_cx->viewport_width = screenWidth() - cx()->viewport_org_x;
editor_cx->viewport_height = screenHeight() - (m->header_line + m->footer_line);
}
void AbstractCharacterBasedApplication::initEditor()
{
editor_cx = std::make_shared<TextEditorContext>();
layoutEditor();
}
bool AbstractCharacterBasedApplication::isLineNumberVisible() const
{
return m->show_line_number;
}
int AbstractCharacterBasedApplication::charWidth(uint32_t c)
{
return UnicodeWidth::width(UnicodeWidth::type(c));
}
QList<FormattedLine> AbstractCharacterBasedApplication::formatLine(Document::Line const &line, int tab_span, int anchor_a, int anchor_b) const
{
QByteArray ba;
if (m->text_codec) {
ba = m->text_codec->toUnicode(line.text).toUtf8();
} else {
ba = line.text;
}
std::vector<ushort> vec;
vec.reserve(ba.size() + 100);
size_t len = ba.size();
QList<FormattedLine> res;
int col = 0;
int col_start = col;
bool flag_a = false;
bool flag_b = false;
auto Color = [&](size_t offset, size_t *next_offset){
int i = findSyntax(&m->syntax_table, offset);
if (i < m->syntax_table.size() && m->syntax_table[i].offset <= offset) {
if (next_offset) {
if (i + 1 < m->syntax_table.size()) {
*next_offset = m->syntax_table[i + 1].offset;
} else {
*next_offset = -1;
}
}
static int color[] = {
0x000000,
0x0000ff,
0x00ff00,
0x00ffff,
0xff0000,
0xff00ff,
0xffff00,
0xffffff,
};
i = m->syntax_table[i].color;
if (i >= 0 && i < 8) {
return color[i];
}
}
if (next_offset) {
*next_offset = -1;
}
return 0;
};
auto Flush = [&](size_t offset, size_t *next_offset){
if (!vec.empty()) {
int atts = 0;
atts |= Color(offset, next_offset);
if (anchor_a >= 0 || anchor_b >= 0) {
if ((anchor_a < 0 || col_start >= anchor_a) && (anchor_b == -1 || col_start < anchor_b)) {
atts |= FormattedLine::Selected;
}
}
ushort const *left = &vec[0];
ushort const *right = left + vec.size();
while (left < right && (right[-1] == '\r' || right[-1] == '\n')) right--;
if (left < right) {
res.push_back(FormattedLine(QString::fromUtf16(left, right - left), atts));
}
vec.clear();
} else {
*next_offset = -1;
}
col_start = col;
};
size_t offset = 0;
size_t next_offset = -1;
if (len > 0) {
{
size_t o = line.byte_offset;
int i = findSyntax(&m->syntax_table, o);
if (i < m->syntax_table.size() && m->syntax_table[i].offset <= o) {
if (i + 1 < m->syntax_table.size()) {
o = m->syntax_table[i + 1].offset;
if (o != (size_t)-1) {
next_offset = o;
}
}
}
}
utf8 u8(ba.data(), len);
u8.to_utf32([&](uint32_t c){
if (line.byte_offset + u8.offset() == next_offset) {
Flush(line.byte_offset + offset, &next_offset);
offset += u8.offset();
}
if (c == '\t') {
do {
vec.push_back(' ');
col++;
} while (col % tab_span != 0);
} else if (c < ' ') {
// nop
} else {
int cw = charWidth(c);
if (c < 0xffff) {
vec.push_back(c);
} else {
int a = c >> 16;
if (a > 0 && a <= 0x20) {
a--;
int b = c & 0x03ff;
a = (a << 6) | ((c >> 10) & 0x003f);
a |= 0xd800;
b |= 0xdc00;
vec.push_back(a);
vec.push_back(b);
}
}
col += cw;
}
if ((anchor_a >= 0 || anchor_b >= 0) && anchor_a != anchor_b) {
if (!flag_a && col >= anchor_a) {
Flush(line.byte_offset + offset, &next_offset);
flag_a = true;
}
if (!flag_b && col >= anchor_b) {
Flush(line.byte_offset + offset, &next_offset);
flag_b = true;
}
}
return true;
});
}
Flush(line.byte_offset + offset, &next_offset);
return res;
}
void AbstractCharacterBasedApplication::fetchCurrentLine() const
{
QByteArray line;
const int row = cx()->current_row;
int lines = documentLines();
if (row >= 0 && row < lines) {
line = cx()->engine->document.lines[row].text;
}
m->parsed_row_index = row;
m->parsed_line = line;
}
void AbstractCharacterBasedApplication::clearParsedLine()
{
m->parsed_row_index = -1;
m->parsed_for_edit = false;
m->parsed_line.clear();
}
int AbstractCharacterBasedApplication::cursorX() const
{
return cx()->current_col - cx()->scroll_col_pos;
}
int AbstractCharacterBasedApplication::cursorY() const
{
return cx()->current_row - cx()->scroll_row_pos;
}
int AbstractCharacterBasedApplication::screenWidth() const
{
return m->screen_width;
}
int AbstractCharacterBasedApplication::screenHeight() const
{
return m->screen_height;
}
void AbstractCharacterBasedApplication::setScreenSize(int w, int h, bool update_layout)
{
m->screen_width = w;
m->screen_height = h;
if (update_layout) {
layoutEditor();
}
}
bool AbstractCharacterBasedApplication::isPaintingSuppressed() const
{
return m->is_painting_suppressed;
}
void AbstractCharacterBasedApplication::setPaintingSuppressed(bool f)
{
m->is_painting_suppressed = f;
}
void AbstractCharacterBasedApplication::commitLine(std::vector<uint32_t> const &vec)
{
if (isReadOnly()) return;
QByteArray ba;
if (!vec.empty()){
utf32 u32(&vec[0], vec.size());
u32.to_utf8([&](char c, int pos){
(void)pos;
ba.push_back(c);
return true;
});
}
if (m->parsed_row_index == 0 && engine()->document.lines.empty()) {
Document::Line newline;
engine()->document.lines.push_back(newline);
}
Document::Line *line = &engine()->document.lines[m->parsed_row_index];
if (m->parsed_row_index == 0) {
line->byte_offset = 0;
line->line_number = (line->type == Document::Line::Unknown) ? 0 : 1;
}
line->text = ba;
if (m->valid_line_index > m->parsed_row_index) {
m->valid_line_index = m->parsed_row_index;
}
int y = m->parsed_row_index - cx()->scroll_row_pos + cx()->viewport_org_y;
if (y >= 0 && y < (int)m->line_flags.size()) {
m->line_flags[y] |= LineChanged;
}
}
void AbstractCharacterBasedApplication::parseLine(std::vector<uint32_t> *vec, int increase_hint, bool force)
{
if (force) {
clearParsedLine();
}
if (force || !m->parsed_for_edit) {
fetchCurrentLine();
m->parsed_col_index = internalParseLine(vec ? vec : &m->prepared_current_line, increase_hint);
m->parsed_for_edit = true;
} else {
if (vec) {
*vec = m->prepared_current_line;
}
}
}
QByteArray AbstractCharacterBasedApplication::parsedLine() const
{
return m->parsed_line;
}
bool AbstractCharacterBasedApplication::isCurrentLineWritable() const
{
if (isReadOnly()) return false;
int row = cx()->current_row;
if (row >= 0 && row < cx()->engine->document.lines.size()) {
if (cx()->engine->document.lines[row].type != Document::Line::Unknown) {
return true;
}
}
return false;
}
int AbstractCharacterBasedApplication::editorViewportWidth() const
{
return cx()->viewport_width;
}
int AbstractCharacterBasedApplication::editorViewportHeight() const
{
return cx()->viewport_height;
}
int AbstractCharacterBasedApplication::print(int x, int y, QString const &text, const AbstractCharacterBasedApplication::Option &opt)
{
CharAttr attr = opt.char_attr;
if (opt.char_attr.flags & CharAttr::Selected) {
attr.index = CharAttr::Invert;
}
if (opt.char_attr.flags & CharAttr::CurrentLine) {
attr.index = CharAttr::Hilite;
}
const int screen_w = screenWidth();
const int screen_h = screenHeight();
int x_start = 0;
int y_start = 0;
int x_limit = screen_w;
int y_limit = screen_h;
if (!opt.clip.isNull()) {
x_start = opt.clip.x();
y_start = opt.clip.y();
x_limit = x_start + opt.clip.width();
y_limit = y_start + opt.clip.height();
}
if (y >= y_start && y < y_limit) {
bool changed = false;
int x2 = x;
int y2 = y;
if (text.isEmpty()) {
changed = true; // set changed flag if text is empty
} else {
for (int i = 0; i < text.size(); i++) {
ushort c = text.utf16()[i];
if (c < ' ' || c == 0x7f) continue;
int cw = charWidth(c);
if (x2 + cw > x_limit) {
break;
}
for (int j = 0; j < cw; j++) {
if (x2 >= x_start && x2 < screen_w) {
int o = y2 * screen_w + x2;
CharAttr a;
if (j == 0) {
a = attr;
} else {
c = -1;
}
if (c != m->screen[o].c || a != m->screen[o].a) {
m->screen[o].c = c;
m->screen[o].a = a;
changed = true;
}
}
x2++;
}
}
}
if (changed) {
m->line_flags[y] |= LineChanged;
}
x = x2;
}
return x;
}
void AbstractCharacterBasedApplication::initEngine(std::shared_ptr<TextEditorContext> const &cx)
{
cx->engine = std::make_shared<TextEditorEngine>();
}
TextEditorContext *AbstractCharacterBasedApplication::cx()
{
if (dialog_cx) {
if (!dialog_cx->engine) {
initEngine(dialog_cx);
}
return dialog_cx.get();
}
if (!editor_cx->engine) {
initEngine(editor_cx);
}
return editor_cx.get();
}
const TextEditorContext *AbstractCharacterBasedApplication::cx() const
{
return const_cast<AbstractCharacterBasedApplication *>(this)->cx();
}
TextEditorEnginePtr AbstractCharacterBasedApplication::engine()
{
Q_ASSERT(cx()->engine);
return cx()->engine;
}
void AbstractCharacterBasedApplication::setTextEditorEngine(TextEditorEnginePtr const &e)
{
cx()->engine = e;
}
void AbstractCharacterBasedApplication::setDocument(QList<Document::Line> const *source)
{
document()->lines = *source;
}
void AbstractCharacterBasedApplication::openFile(QString const &path)
{
document()->lines.clear();
QFile file(path);
if (file.open(QFile::ReadOnly)) {
size_t offset = 0;
QString line;
unsigned int linenum = 0;
while (!file.atEnd()) {
linenum++;
Document::Line line(file.readLine());
line.byte_offset = offset;
line.type = Document::Line::Normal;
line.line_number = linenum;
document()->lines.push_back(line);
offset += line.text.size();
}
int n = line.size();
if (n > 0) {
ushort const *p = line.utf16();
ushort c = p[n - 1];
if (c == '\r' || c == '\n') {
document()->lines.push_back(Document::Line(QByteArray()));
}
}
m->valid_line_index = (int)document()->lines.size();
setRecentlyUsedPath(path);
}
scrollToTop();
}
void AbstractCharacterBasedApplication::saveFile(QString const &path)
{
QFile file(path);
if (file.open(QFile::WriteOnly)) {
for (Document::Line const &line : document()->lines) {
file.write(line.text);
}
}
}
void AbstractCharacterBasedApplication::pressEnter()
{
deleteIfSelected();
if (isDialogMode()) {
closeDialog(true);
} else {
writeNewLine();
}
}
void AbstractCharacterBasedApplication::pressEscape()
{
if (isTerminalMode()) {
m->escape_sequence.write(0x1b);
return;
}
if (isDialogMode()) {
closeDialog(false);
return;
}
deselect();
updateVisibility(false, false, false);
}
AbstractCharacterBasedApplication::State AbstractCharacterBasedApplication::state() const
{
return m->state;
}
Document *AbstractCharacterBasedApplication::document()
{
return &engine()->document;
}
int AbstractCharacterBasedApplication::documentLines() const
{
return const_cast<AbstractCharacterBasedApplication *>(this)->document()->lines.size();
}
bool AbstractCharacterBasedApplication::isSingleLineMode() const
{
return cx()->single_line;
}
void AbstractCharacterBasedApplication::setLineMargin(int n)
{
m->line_margin = n;
}
void AbstractCharacterBasedApplication::ensureCurrentLineVisible()
{
int margin = (cx()->viewport_height >= 6 && !isSingleLineMode()) ? m->line_margin : 0;
int pos = cx()->scroll_row_pos;
int top = cx()->current_row - margin;
int bottom = cx()->current_row + 1 - editorViewportHeight() + margin;
if (pos > top) pos = top;
if (pos < bottom) pos = bottom;
if (pos < 0) pos = 0;
if (cx()->scroll_row_pos != pos) {
cx()->scroll_row_pos = pos;
invalidateArea();
}
}
int AbstractCharacterBasedApplication::decideColumnScrollPos() const
{
int x = cx()->current_col;
int w = editorViewportWidth() - RIGHT_MARGIN;
if (w < 0) w = 0;
return x > w ? (cx()->current_col - w) : 0;
}
int AbstractCharacterBasedApplication::calcVisualWidth(const Document::Line &line) const
{
QList<FormattedLine> lines = formatLine(line, cx()->tab_span);
int x = 0;
for (FormattedLine const &line : lines) {
if (line.text.isEmpty()) continue;
ushort const *ptr = line.text.utf16();
ushort const *end = ptr + line.text.size();
while (1) {
int c = -1;
if (ptr < end) {
c = *ptr;
ptr++;
}
if (c == -1 || c == '\r' || c == '\n') {
break;
}
if (c == '\t') {
x += cx()->tab_span;
x -= x % cx()->tab_span;
} else {
x += charWidth(c);
}
}
}
return x;
}
void AbstractCharacterBasedApplication::clearRect(int x, int y, int w, int h)
{
int scr_w = screenWidth();
int scr_h = screenHeight();
int y0 = y;
int y1 = y + h;
if (y0 < 0) y0 = 0;
if (y1 > scr_h) y1 = scr_h;
int x0 = x;
int x1 = x + w;
if (x0 < 0) x0 = 0;
if (x1 > scr_w) x1 = scr_w;
for (int y = y0; y < y1; y++) {
for (int x = x0; x < x1; x++) {
int o = y * scr_w + x;
m->screen[o] = Character();
}
}
}
int AbstractCharacterBasedApplication::calcIndexToColumn(const std::vector<uint32_t> &vec, int index) const
{
int col = 0;
for (int i = 0; i < index; i++) {
uint32_t c = vec.at(i);
if (c == '\t') {
col += cx()->tab_span;
col -= col % cx()->tab_span;
} else {
col += charWidth(c);
}
}
return col;
}
void AbstractCharacterBasedApplication::savePos()
{
TextEditorContext *p = editor_cx.get();
if (p) {
p->saved_row = p->current_row;
p->saved_col = p->current_col;
p->saved_col_hint = p->current_col_hint;
}
}
void AbstractCharacterBasedApplication::restorePos()
{
TextEditorContext *p = editor_cx.get();
if (p) {
p->current_row = p->saved_row;
p->current_col = p->saved_col;
p->current_col_hint = p->saved_col_hint;
}
}
void AbstractCharacterBasedApplication::setCursorRow(int row, bool auto_scroll, bool by_mouse)
{
if (cx()->current_row == row) return;
if (isShiftModifierPressed()) {
if (selection_anchor_0.enabled == SelectionAnchor::No) {
setSelectionAnchor(SelectionAnchor::EnabledEasy, true, auto_scroll);
selection_anchor_1 = selection_anchor_0;
}
} else if (selection_anchor_0.enabled == SelectionAnchor::EnabledEasy) {
setSelectionAnchor(SelectionAnchor::No, false, auto_scroll);
}
cx()->current_row = row;
if (selection_anchor_0.enabled != SelectionAnchor::No) {
setSelectionAnchor(selection_anchor_0.enabled, true, auto_scroll);
}
m->cursor_moved_by_mouse = by_mouse;
}
void AbstractCharacterBasedApplication::setCursorCol(int col, bool auto_scroll, bool by_mouse)
{
if (cx()->current_col == col) {
cx()->current_col_hint = col;
return;
}
if (isShiftModifierPressed()) {
if (selection_anchor_0.enabled == SelectionAnchor::No) {
setSelectionAnchor(SelectionAnchor::EnabledEasy, true, auto_scroll);
selection_anchor_1 = selection_anchor_0;
}
} else if (selection_anchor_0.enabled == SelectionAnchor::EnabledEasy) {
setSelectionAnchor(SelectionAnchor::No, false, auto_scroll);
}
cx()->current_col = col;
cx()->current_col_hint = col;
if (selection_anchor_0.enabled != SelectionAnchor::No) {
setSelectionAnchor(selection_anchor_0.enabled, true, auto_scroll);
}
m->cursor_moved_by_mouse = by_mouse;
}
void AbstractCharacterBasedApplication::setCursorPos(int row, int col)
{
setCursorRow(row, false);
setCursorCol(col, false);
}
void AbstractCharacterBasedApplication::setCursorColByIndex(std::vector<uint32_t> const &vec, int col_index)
{
int col = calcIndexToColumn(vec, col_index);
setCursorCol(col);
}
int AbstractCharacterBasedApplication::nextTabStop(int x) const
{
x += cx()->tab_span;
x -= x % cx()->tab_span;
return x;
}
void AbstractCharacterBasedApplication::editSelected(EditOperation op, std::vector<uint32_t> *cutbuffer)
{
if (isReadOnly() && op == EditOperation::Cut) {
op = EditOperation::Copy;
}
SelectionAnchor a = selection_anchor_0;
SelectionAnchor b = selection_anchor_1;
if (!a.enabled) return;
if (!b.enabled) return;
if (a == b) return;
auto UpdateVisibility = [&](){
updateVisibility(false, false, false);
};
if (cutbuffer) {
cutbuffer->clear();
}
std::list<std::vector<uint32_t>> cutlist;
if (a.row > b.row) {
std::swap(a, b);
} else if (a.row == b.row) {
if (a.col > b.col) {
std::swap(a, b);
}
}
int curr_row = cx()->current_row;
int curr_col = cx()->current_col;
cx()->current_row = b.row;
cx()->current_col = b.col;
if (a.row == b.row) {
std::vector<uint32_t> vec1;
parseLine(&vec1, 0, true);
auto begin = vec1.begin() + calcColumnToIndex(a.col);
auto end = vec1.begin() + calcColumnToIndex(b.col);
if (cutbuffer) {
std::vector<uint32_t> cut;
cut.insert(cut.end(), begin, end);
cutlist.push_back(std::move(cut));
}
if (op == EditOperation::Cut) {
vec1.erase(begin, end);
commitLine(vec1);
UpdateVisibility();
}
} else {
std::vector<uint32_t> vec1;
parseLine(&vec1, 0, true);
{
auto begin = vec1.begin();
auto end = vec1.begin() + calcColumnToIndex(b.col);
if (cutbuffer) {
std::vector<uint32_t> cut;
cut.insert(cut.end(), begin, end);
cutlist.push_back(std::move(cut));
}
if (op == EditOperation::Cut) {
vec1.erase(begin, end);
commitLine(vec1);
UpdateVisibility();
}
}
int n = b.row - a.row;
for (int i = 0; i < n; i++) {
QList<Document::Line> *lines = &cx()->engine->document.lines;
if (cutbuffer && i > 0) {
cx()->current_row = b.row - i;
cx()->current_col = 0;
std::vector<uint32_t> cut;
parseLine(&cut, 0, true);
cutlist.push_back(std::move(cut));
}
if (op == EditOperation::Cut) {
lines->erase(lines->begin() + b.row - i);
}
}
cx()->current_row = a.row;
cx()->current_col = a.col;
int index = calcColumnToIndex(a.col);
std::vector<uint32_t> vec2;
parseLine(&vec2, 0, true);
if (cutbuffer) {
std::vector<uint32_t> cut;
cut.insert(cut.end(), vec2.begin() + index, vec2.end());
cutlist.push_back(std::move(cut));
}
if (op == EditOperation::Cut) {
vec2.resize(index);
vec2.insert(vec2.end(), vec1.begin(), vec1.end());
commitLine(vec2);
UpdateVisibility();
}
}
if (cutbuffer) {
size_t size = 0;
for (std::vector<uint32_t> const &v : cutlist) {
size += v.size();
}
cutbuffer->reserve(size);
for (auto it = cutlist.rbegin(); it != cutlist.rend(); it++) {
std::vector<uint32_t> const &v = *it;
cutbuffer->insert(cutbuffer->end(), v.begin(), v.end());
}
}
if (op == EditOperation::Cut) {
deselect();
setCursorPos(a.row, a.col);
invalidateArea(a.row - cx()->scroll_row_pos);
} else {
cx()->current_row = curr_row;
cx()->current_col = curr_col;
invalidateArea(curr_row - cx()->scroll_row_pos);
}
clearParsedLine();
UpdateVisibility();
}
void AbstractCharacterBasedApplication::edit_(EditOperation op)
{
std::vector<uint32_t> u32buf;
editSelected(op, &u32buf);
if (u32buf.empty()) return;
std::vector<ushort> u16buf;
u16buf.reserve(1024);
utf32(&u32buf[0], u32buf.size()).to_utf16([&](uint16_t c){
u16buf.push_back(c);
return true;
});
if (!u16buf.empty()) {
QString s = QString::fromUtf16(&u16buf[0], u16buf.size());
qApp->clipboard()->setText(s);
}
}
bool AbstractCharacterBasedApplication::deleteIfSelected()
{
if (selection_anchor_0.enabled && selection_anchor_1.enabled) {
if (selection_anchor_0 != selection_anchor_1) {
editSelected(EditOperation::Cut, nullptr);
return true;
}
}
return false;
}
void AbstractCharacterBasedApplication::doDelete()
{
if (isReadOnly()) return;
if (isTerminalMode()) return;
if (deleteIfSelected()) {
return;
}
parseLine(nullptr, 0, false);
std::vector<uint32_t> *vec = &m->prepared_current_line;
int index = m->parsed_col_index;
int c = -1;
if (index >= 0 && index < (int)vec->size()) {
c = (*vec)[index];
}
if (c == '\n' || c == '\r' || c == -1) {
if (index == 0) {
m->parsed_row_index--;
}
invalidateAreaBelowTheCurrentLine();
if (isSingleLineMode()) {
// nop
} else {
if (c != -1) {
vec->erase(vec->begin() + index);
if (c == '\r' && index < (int)vec->size() && (*vec)[index] == '\n') {
vec->erase(vec->begin() + index);
}
}
if (vec->empty()) {
clearParsedLine();
if (cx()->current_row + 1 < (int)cx()->engine->document.lines.size()) {
cx()->engine->document.lines.erase(cx()->engine->document.lines.begin() + cx()->current_row);
}
} else {
commitLine(*vec);
setCursorColByIndex(*vec, index);
if (index == (int)vec->size()) {
int nextrow = cx()->current_row + 1;
int lines = documentLines();
if (nextrow < lines) {
Document::Line *ba1 = &cx()->engine->document.lines[cx()->current_row];
Document::Line const &ba2 = cx()->engine->document.lines[nextrow];
ba1->text.append(ba2.text);
cx()->engine->document.lines.erase(cx()->engine->document.lines.begin() + nextrow);
}
}
}
clearParsedLine();
updateVisibility(true, true, true);
}
} else {
vec->erase(vec->begin() + index);
commitLine(*vec);
setCursorColByIndex(*vec, index);
updateVisibility(true, true, true);
}
}
void AbstractCharacterBasedApplication::doBackspace()
{
if (isReadOnly()) return;
if (isTerminalMode()) return;
if (deleteIfSelected()) {
return ;
}
if (cx()->current_row > 0 || cx()->current_col > 0) {
setPaintingSuppressed(true);
moveCursorLeft();
doDelete();
setPaintingSuppressed(false);
updateVisibility(true, true, true);
}
}
bool AbstractCharacterBasedApplication::isDialogMode()
{
return m->dialog_mode;
}
void AbstractCharacterBasedApplication::setDialogOption(QString const &title, QString const &value, DialogHandler handler)
{
m->dialog_title = title;
m->dialog_value = value;
m->dialog_handler = handler;
}
void AbstractCharacterBasedApplication::setDialogMode(bool f)
{
if (f) {
if (!dialog_cx) {
int y = screenHeight() - 2;
dialog_cx = std::make_shared<TextEditorContext>();
dialog_cx->engine = std::make_shared<TextEditorEngine>();
dialog_cx->single_line = true;
dialog_cx->viewport_org_x = 0;
dialog_cx->viewport_org_y = y + 1;
dialog_cx->viewport_width = screenWidth();
dialog_cx->viewport_height = 1;
}
dialog_cx->engine->document.lines.push_back(Document::Line(m->dialog_value.toUtf8()));
editor_cx->viewport_height = screenHeight() - m->header_line - 2;
m->dialog_mode = true;
clearParsedLine();
moveCursorEnd();
} else {
dialog_cx.reset();
m->dialog_mode = false;
layoutEditor();
clearParsedLine();
updateVisibility(true, true, true);
}
}
void AbstractCharacterBasedApplication::execDialog(QString const &dialog_title, QString dialog_value, DialogHandler handler)
{
setDialogOption(dialog_title, dialog_value, handler);
setDialogMode(true);
}
void AbstractCharacterBasedApplication::closeDialog(bool result)
{
if (isDialogMode()) {
deselect();
QString line;
if (!dialog_cx->engine->document.lines.empty()) {
Document::Line const &l = dialog_cx->engine->document.lines.front();
line = QString::fromUtf8(l.text);
}
setDialogMode(false);
if (m->dialog_handler) {
m->dialog_handler(result, line);
}
return;
}
}
int AbstractCharacterBasedApplication::internalParseLine(std::vector<uint32_t> *vec, int increase_hint) const
{
vec->clear();
int index = -1;
int col = 0;
int len = m->parsed_line.size();
if (len > 0) {
vec->reserve(len + increase_hint);
char const *src = m->parsed_line.data();
utf8 u8(src, len);
while (1) {
int n = 0;
uint32_t c = u8.next();
if (c == 0) {
n = 1;
} else {
if (c == '\t') {
int z = nextTabStop(col);
n = z - col;
} else {
n = charWidth(c);
}
}
if (col <= cx()->current_col && col + n > cx()->current_col) {
index = (int)vec->size();
}
if (c == 0) break;
col += n;
vec->push_back(c);
}
}
return index;
}
int AbstractCharacterBasedApplication::calcColumnToIndex(int column)
{
int index = 0;
if (column > 0) {
fetchCurrentLine();
int col = 0;
int len = m->parsed_line.size();
if (len > 0) {
char const *src = m->parsed_line.data();
utf8 u8(src, len);
while (1) {
uint32_t c = u8.next();
int n = 0;
if (c == '\r' || c == '\n' || c == 0) {
break;
}
if (c == '\t') {
int z = nextTabStop(col);
n = z - col;
} else {
n = charWidth(c);
}
col += n;
index++;
if (col >= column) {
break;
}
}
}
}
return index;
}
void AbstractCharacterBasedApplication::invalidateArea(int top_y)
{
int y0 = cx()->viewport_org_y;
int y1 = cx()->viewport_height + y0;
top_y += y0;
if (y0 < top_y) y0 = top_y;
int n = m->line_flags.size();
if (y0 < 0) y0 = 0;
if (y1 > n) y1 = n;
for (int y = y0; y < y1; y++) {
m->line_flags[y] |= LineChanged;
}
}
void AbstractCharacterBasedApplication::invalidateAreaBelowTheCurrentLine()
{
int y;
y = m->parsed_row_index;
if (m->valid_line_index > y) {
m->valid_line_index = y;
}
y = m->parsed_row_index - cx()->scroll_row_pos;
invalidateArea(y);
}
int AbstractCharacterBasedApplication::scrollBottomLimit() const
{
return documentLines() - editorViewportHeight() / 2;
}
void AbstractCharacterBasedApplication::writeCR()
{
deleteIfSelected();
setCursorCol(0);
clearParsedLine();
updateVisibility(true, true, true);
}
void AbstractCharacterBasedApplication::moveCursorOut()
{
setCursorRow(-1);
}
void AbstractCharacterBasedApplication::moveCursorHome()
{
fetchCurrentLine();
QString line = m->parsed_line;
ushort const *ptr = line.utf16();
ushort const *end = ptr + line.size();
int x = 0;
while (1) {
int c = -1;
if (ptr < end) {
c = *ptr;
ptr++;
}
if (c == ' ') {
x++;
} else if (c == '\t') {
x = nextTabStop(x);
} else {
break;
}
}
if (x == cx()->current_col) {
x = 0;
}
setCursorCol(x);
clearParsedLine();
updateVisibility(true, true, true);
}
void AbstractCharacterBasedApplication::moveCursorEnd()
{
fetchCurrentLine();
QByteArray line = m->parsed_line;
int col = calcVisualWidth(Document::Line(line));
setCursorCol(col);
clearParsedLine();
updateVisibility(true, true, true);
}
void AbstractCharacterBasedApplication::scrollUp()
{
if (cx()->scroll_row_pos > 0) {
cx()->scroll_row_pos--;
invalidateArea();
clearParsedLine();
updateVisibility(false, false, true);
}
}
void AbstractCharacterBasedApplication::scrollDown()
{
int limit = scrollBottomLimit();
if (cx()->scroll_row_pos < limit) {
cx()->scroll_row_pos++;
invalidateArea();
clearParsedLine();
updateVisibility(false, false, true);
}
}
void AbstractCharacterBasedApplication::moveCursorUp()
{
if (isSingleLineMode()) {
// nop
} else if (cx()->current_row > 0) {
setCursorRow(cx()->current_row - 1);
clearParsedLine();
updateVisibility(true, false, true);
}
}
void AbstractCharacterBasedApplication::moveCursorDown()
{
if (isSingleLineMode()) {
// nop
} else if (cx()->current_row + 1 < (int)document()->lines.size()) {
setCursorRow(cx()->current_row + 1);
clearParsedLine();
updateVisibility(true, false, true);
}
}
void AbstractCharacterBasedApplication::scrollToTop()
{
if (isSingleLineMode()) return;
setCursorRow(0);
setCursorCol(0);
cx()->scroll_row_pos = 0;
invalidateArea();
clearParsedLine();
updateVisibility(true, false, true);
}
void AbstractCharacterBasedApplication::moveCursorLeft()
{
if (!isShiftModifierPressed() && selection_anchor_0.enabled && selection_anchor_1.enabled) { // 選択領域があったら
if (selection_anchor_0 != selection_anchor_1) {
SelectionAnchor a = std::min(selection_anchor_0, selection_anchor_1);
deselect();
setCursorRow(a.row);
setCursorCol(a.col);
updateVisibility(true, true, true);
return;
}
}
if (cx()->current_col == 0) {
if (isSingleLineMode()) {
// nop
} else {
if (cx()->current_row > 0) {
setCursorRow(cx()->current_row - 1);
clearParsedLine();
moveCursorEnd();
}
}
return;
}
int col = 0;
int newcol = 0;
int index;
for (index = 0; index < (int)m->prepared_current_line.size(); index++) {
ushort c = m->prepared_current_line[index];
if (c == '\r' || c == '\n') {
break;
}
newcol = col;
if (c == '\t') {
col = nextTabStop(col);
} else {
col += charWidth(c);
}
if (col >= cx()->current_col) {
break;
}
}
m->parsed_col_index = index;
setCursorCol(newcol);
updateVisibility(true, true, true);
}
void AbstractCharacterBasedApplication::moveCursorRight()
{
if (!isShiftModifierPressed() && selection_anchor_0.enabled && selection_anchor_1.enabled) { // 選択領域があったら
if (selection_anchor_0 != selection_anchor_1) {
SelectionAnchor a = std::max(selection_anchor_0, selection_anchor_1);
deselect();
setCursorRow(a.row);
setCursorCol(a.col);
updateVisibility(true, true, true);
return;
}
}
int col = 0;
int i = 0;
while (1) {
int c = -1;
if (i < (int)m->prepared_current_line.size()) {
c = m->prepared_current_line[i];
}
if (c == '\r' || c == '\n' || c == -1) {
if (!isSingleLineMode()) {
int nextrow = cx()->current_row + 1;
int lines = document()->lines.size();
if (nextrow < lines) {
setCursorPos(nextrow, 0);
clearParsedLine();
updateVisibility(true, true, true);
return;
}
}
break;
}
if (c == '\t') {
col = nextTabStop(col);
} else {
col += charWidth(c);
}
if (col > cx()->current_col) {
break;
}
i++;
}
if (col != cx()->current_col) {
setCursorCol(col);
clearParsedLine();
updateVisibility(true, true, true);
}
}
void AbstractCharacterBasedApplication::movePageUp()
{
if (!isSingleLineMode()) {
int step = editorViewportHeight();
setCursorRow(cx()->current_row - step);
cx()->scroll_row_pos -= step;
if (cx()->current_row < 0) {
cx()->current_row = 0;
}
if (cx()->scroll_row_pos < 0) {
cx()->scroll_row_pos = 0;
}
invalidateArea();
clearParsedLine();
updateVisibility(true, false, true);
}
}
void AbstractCharacterBasedApplication::movePageDown()
{
if (!isSingleLineMode()) {
int limit = documentLines();
if (limit > 0) {
limit--;
int step = editorViewportHeight();
setCursorRow(cx()->current_row + step);
cx()->scroll_row_pos += step;
if (cx()->current_row > limit) {
cx()->current_row = limit;
}
limit = scrollBottomLimit();
if (cx()->scroll_row_pos > limit) {
cx()->scroll_row_pos = limit;
}
} else {
setCursorRow(0);
cx()->scroll_row_pos = 0;
}
invalidateArea();
clearParsedLine();
updateVisibility(true, false, true);
}
}
void AbstractCharacterBasedApplication::scrollRight()
{
if (cx()->scroll_col_pos > 0) {
cx()->scroll_col_pos--;
} else {
cx()->scroll_col_pos = 0;
}
invalidateArea();
clearParsedLine();
updateVisibility(true, true, true);
}
void AbstractCharacterBasedApplication::scrollLeft()
{
cx()->scroll_col_pos++;
invalidateArea();
clearParsedLine();
updateVisibility(true, true, true);
}
void AbstractCharacterBasedApplication::addNewLineToBottom()
{
int row = cx()->engine->document.lines.size();
if (cx()->current_row >= row) {
setCursorPos(row, 0);
cx()->engine->document.lines.push_back(Document::Line(QByteArray()));
}
}
void AbstractCharacterBasedApplication::appendNewLine(std::vector<uint32_t> *vec)
{
if (isSingleLineMode()) return;
vec->push_back('\r');
vec->push_back('\n');
}
void AbstractCharacterBasedApplication::writeNewLine()
{
if (isReadOnly()) return;
if (isSingleLineMode()) return;
if (!isCurrentLineWritable()) return;
invalidateAreaBelowTheCurrentLine();
std::vector<uint32_t> curr_line;
parseLine(&curr_line, 0, false);
int index = m->parsed_col_index;
if (index < 0) {
addNewLineToBottom();
index = 0;
}
std::vector<uint32_t> next_line;
// split line
next_line.insert(next_line.end(), curr_line.begin() + index, curr_line.end());
// shrink current line
curr_line.resize(index);
// append new line code
appendNewLine(&curr_line);
// next line index
cx()->current_row = m->parsed_row_index + 1;
// commit current line
commitLine(curr_line);
// insert next line
m->parsed_row_index = cx()->current_row;
engine()->document.lines.insert(engine()->document.lines.begin() + m->parsed_row_index, Document::Line(QByteArray()));
// commit next line
commitLine(next_line);
setCursorColByIndex(next_line, 0);
clearParsedLine();
updateVisibility(true, true, true);
}
void AbstractCharacterBasedApplication::makeColumnPosList(std::vector<int> *out)
{
out->clear();
std::vector<uint32_t> const *line = &m->prepared_current_line;
int x = 0;
while (1) {
size_t index = out->size();
out->push_back(x);
int n;
int c = -1;
if (index < line->size()) {
c = line->at(index);
}
if (c == '\r' || c == '\n' || c == -1) {
break;
}
if (c == '\t') {
int z = nextTabStop(x);
n = z - x;
} else {
n = charWidth(c);
}
x += n;
}
}
void AbstractCharacterBasedApplication::updateCursorPos(bool auto_scroll)
{
if (!cx()->engine) {
return;
}
parseLine(&m->prepared_current_line, 0, false);
int index = 0;
int char_span = 0;
int col = cx()->current_col_hint;
{
std::vector<int> pts;
makeColumnPosList(&pts);
if (pts.size() > 1) {
int newcol = pts.back();
int newindex = pts.size() - 1;
for (size_t i = 0; i + 1 < pts.size(); i++) {
int x = pts[i];
if (x <= col && col < pts[i + 1]) {
char_span = pts[i + 1] - pts[i];
newindex = i;
newcol = x;
break;
}
}
index = newindex;
col = newcol;
} else if (!pts.empty()) {
col = pts.back();
} else {
col = 0;
}
}
m->parsed_col_index = index;
cx()->current_col = col;
if (char_span < 1) {
char_span = 1;
}
cx()->current_char_span = char_span;
if (auto_scroll) {
int pos = decideColumnScrollPos();
if (cx()->scroll_col_pos != pos) {
cx()->scroll_col_pos = pos;
invalidateArea();
}
}
}
void AbstractCharacterBasedApplication::printInvertedBar(int x, int y, char const *text, int padchar)
{
int w = screenWidth();
int o = w * y;
for (int i = 0; i < w; i++) {
m->screen[o + i].c = 0;
}
AbstractCharacterBasedApplication::Option opt;
opt.char_attr = CharAttr::Invert;
print(x, y, text, opt);
for (int i = 0; i < w; i++) {
if (m->screen[o + i].c == 0) {
m->screen[o + i].c = padchar;
}
m->screen[o + i].a = opt.char_attr;
}
}
QString AbstractCharacterBasedApplication::statusLine() const
{
QString text = "[%1:%2]";
text = text.arg(cx()->current_row + 1).arg(cx()->current_col + 1);
return text;
}
int AbstractCharacterBasedApplication::printArea(TextEditorContext const *cx, const SelectionAnchor *sel_a, const SelectionAnchor *sel_b)
{
int end_of_line_y = -1;
if (cx) {
int height = cx->viewport_height;
QRect clip(cx->viewport_org_x, cx->viewport_org_y, cx->viewport_width, height);
int row = cx->scroll_row_pos;
for (int i = 0; i < height; i++) {
if (row < 0) continue;
int y = cx->viewport_org_y + i;
if (row < (int)cx->engine->document.lines.size()) {
if (i < height) {
int x = cx->viewport_org_x - cx->scroll_col_pos;
Document::Line const &line = cx->engine->document.lines[row];
int anchor_a = -1;
int anchor_b = -1;
if (sel_a && sel_a->enabled && sel_b && sel_b->enabled) {
SelectionAnchor a = *sel_a;
SelectionAnchor b = *sel_b;
if (a.row > b.row) {
std::swap(a, b);
} else if (a.row == b.row) {
if (a.col > b.col) {
std::swap(a, b);
}
}
if (row > a.row && row < b.row) {
anchor_a = 0;
} else {
if (row == a.row) {
anchor_a = a.col;
}
if (row == b.row) {
anchor_b = b.col;
}
}
}
QList<FormattedLine> lines = formatLine(line, cx->tab_span, anchor_a, anchor_b);
for (FormattedLine const &line : lines) {
AbstractCharacterBasedApplication::Option opt;
if (line.atts & FormattedLine::StyleID) {
opt.char_attr.color = QColor(line.atts & 0xff, (line.atts >> 8) & 0xff, (line.atts >> 16) & 0xff);
}
opt.clip = clip;
if (line.isSelected()) {
opt.char_attr.flags |= CharAttr::Selected;
}
x = print(x, y, line.text, opt);
}
int end_x = clip.x() + clip.width();
if (x < end_x) {
if (x < clip.left()) {
x = clip.left();
}
if (x < end_x) {
clearRect(x, y, end_x - x, 1);
}
}
}
} else {
if (end_of_line_y < 0) {
end_of_line_y = i;
}
clearRect(cx->viewport_org_x, y, cx->viewport_width, 1);
if (y >= 0 && y < (int)m->line_flags.size()) {
m->line_flags[y] |= LineChanged;
}
}
row++;
}
}
return end_of_line_y;
}
void AbstractCharacterBasedApplication::paintLineNumbers(std::function<void(int, QString, Document::Line const *line)> const &draw)
{
auto Line = [&](int index)->Document::Line &{
return editor_cx->engine->document.lines[index];
};
int rightpadding = 2;
int left_margin = editor_cx->viewport_org_x;
int num = 1;
size_t offset = 0;
for (int i = 0; i < editor_cx->viewport_height; i++) {
char tmp[100];
Q_ASSERT(left_margin < (int)sizeof(tmp));
memset(tmp, ' ', left_margin);
tmp[left_margin] = 0;
int row = editor_cx->scroll_row_pos + i;
Document::Line *line = nullptr;
if (row < (int)editor_cx->engine->document.lines.size()) {
if (m->valid_line_index < 0) {
m->valid_line_index = 0;
Document::Line *p = &Line(0);
if (p->type != Document::Line::Unknown) {
p->byte_offset = offset;
p->line_number = num;
offset += p->text.size();
num++;
}
}
line = &Line(row);
if (row >= m->valid_line_index) {
{
Document::Line const &line = Line(m->valid_line_index);
offset = line.byte_offset;
num = line.line_number;
}
while (m->valid_line_index <= row) {
Document::Line const &line = Line(m->valid_line_index);
if (line.type != Document::Line::Unknown) {
offset += line.text.size();
num++;
}
m->valid_line_index++;
if (m->valid_line_index < editor_cx->engine->document.lines.size()) {
Document::Line *p = &Line(m->valid_line_index);
p->byte_offset = offset;
p->line_number = num;
}
}
}
if (left_margin > 1) {
unsigned int linenum = -1;
if (row < m->valid_line_index) {
#if 1
linenum = line->line_number;
#else
linenum = line->byte_offset;
#endif
}
if (linenum != (unsigned int)-1 && line->type != Document::Line::Unknown) {
sprintf(tmp, "%*u ", left_margin - rightpadding, linenum);
}
}
}
int y = editor_cx->viewport_org_y + i;
draw(y, tmp, line);
}
}
bool AbstractCharacterBasedApplication::isAutoLayout() const
{
return m->auto_layout;
}
void AbstractCharacterBasedApplication::preparePaintScreen()
{
if (m->header_line > 0) {
char const *line = "Hello, world\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86";
printInvertedBar(0, 0, line, ' ');
}
if (m->show_line_number) {
Option opt_normal;
paintLineNumbers([&](int y, QString text, Document::Line const *line){
(void)line;
print(0, y, text + '|', opt_normal);
});
}
SelectionAnchor anchor_a;
SelectionAnchor anchor_b;
auto MakeSelectionAnchor = [&](){
if (selection_anchor_0.enabled != SelectionAnchor::No) {
anchor_a = selection_anchor_0;
#if 0
anchor_b.row = cx()->current_row;
anchor_b.col = cx()->current_col;
anchor_b.enabled = selection_anchor_0.enabled;
#else
anchor_b = selection_anchor_1;
#endif
}
};
if (isDialogMode()) {
printArea(editor_cx.get(), &anchor_a, &anchor_b);
std::string text = m->dialog_title.toStdString();
text = ' ' + text + ' ';
int y = screenHeight() - 2;
printInvertedBar(3, y, text.c_str(), '-');
MakeSelectionAnchor();
printArea(dialog_cx.get(), &anchor_a, &anchor_b);
} else {
MakeSelectionAnchor();
editor_cx->bottom_line_y = printArea(editor_cx.get(), &anchor_a, &anchor_b);
if (m->footer_line > 0) {
QString line = statusLine();
int y = screenHeight() - 1;
printInvertedBar(0, y, line.toStdString().c_str(), ' ');
}
}
}
void AbstractCharacterBasedApplication::onQuit()
{
if (!m->is_quit_enabled) return;
if (!isDialogMode()) {
m->state = State::Exit;
}
}
void AbstractCharacterBasedApplication::onOpenFile()
{
if (isReadOnly()) return;
if (!m->is_open_enabled) return;
if (!isDialogMode()) {
execDialog("Open File", recentlyUsedPath(), [&](bool ok, QString const &text){
if (ok) {
openFile(text);
}
});
}
}
void AbstractCharacterBasedApplication::onSaveFile()
{
if (!m->is_save_enabled) return;
if (!isDialogMode()) {
execDialog("Save File", recentlyUsedPath(), [&](bool ok, QString const &text){
if (ok) {
saveFile(text);
}
});
}
}
void AbstractCharacterBasedApplication::setNormalTextEditorMode(bool f)
{
m->is_quit_enabled = f;
m->is_open_enabled = f;
m->is_save_enabled = f;
setTerminalMode(!f);
}
SelectionAnchor AbstractCharacterBasedApplication::currentAnchor(SelectionAnchor::Enabled enabled)
{
SelectionAnchor a;
a.row = cx()->current_row;
a.col = cx()->current_col;
a.enabled = enabled;
return a;
}
void AbstractCharacterBasedApplication::deselect()
{
selection_anchor_0.enabled = SelectionAnchor::No;
selection_anchor_1.enabled = SelectionAnchor::No;
}
bool AbstractCharacterBasedApplication::isSelectionAnchorEnabled() const
{
return selection_anchor_0.enabled != SelectionAnchor::No;
}
void AbstractCharacterBasedApplication::setToggleSelectionAnchorEnabled(bool f)
{
m->is_toggle_selection_anchor_enabled = f;
}
void AbstractCharacterBasedApplication::setReadOnly(bool f)
{
m->is_read_only = f;
}
bool AbstractCharacterBasedApplication::isReadOnly() const
{
return m->is_read_only && !m->is_terminal_mode;
}
void AbstractCharacterBasedApplication::setSelectionAnchor(SelectionAnchor::Enabled enabled, bool update_anchor, bool auto_scroll)
{
if (update_anchor) {
selection_anchor_0 = currentAnchor(enabled);
} else {
selection_anchor_0.enabled = enabled;
}
clearParsedLine();
updateVisibility(false, false, auto_scroll);
}
void AbstractCharacterBasedApplication::toggleSelectionAnchor()
{
if (!m->is_toggle_selection_anchor_enabled) return;
setSelectionAnchor(isSelectionAnchorEnabled() ? SelectionAnchor::No : SelectionAnchor::EnabledHard, true, true);
selection_anchor_1 = selection_anchor_0;
}
void AbstractCharacterBasedApplication::editPaste()
{
if (isReadOnly()) return;
if (isTerminalMode()) return;
setPaintingSuppressed(true);
QString str = qApp->clipboard()->text();
utf16(str.utf16(), str.size()).to_utf32([&](uint32_t c){
write(c, false);
return true;
});
setPaintingSuppressed(false);
updateVisibility(true, true, true);
}
void AbstractCharacterBasedApplication::editCopy()
{
edit_(EditOperation::Copy);
}
void AbstractCharacterBasedApplication::editCut()
{
if (isReadOnly()) return;
if (isTerminalMode()) return;
edit_(EditOperation::Cut);
}
void AbstractCharacterBasedApplication::setWriteMode(WriteMode wm)
{
m->write_mode = wm;
}
bool AbstractCharacterBasedApplication::isInsertMode() const
{
return m->write_mode == WriteMode::Insert && !isTerminalMode();
}
bool AbstractCharacterBasedApplication::isOverwriteMode() const
{
return m->write_mode == WriteMode::Overwrite || isTerminalMode();
}
void AbstractCharacterBasedApplication::setTerminalMode(bool f)
{
m->is_terminal_mode = f;
if (isTerminalMode()) {
showHeader(false);
showFooter(false);
showLineNumber(false, 0);
setLineMargin(0);
setWriteMode(WriteMode::Overwrite);
setReadOnly(true);
}
layoutEditor();
}
bool AbstractCharacterBasedApplication::isTerminalMode() const
{
return m->is_terminal_mode;
}
bool AbstractCharacterBasedApplication::isBottom() const
{
if (cx()->current_row == m->parsed_row_index) {
if (m->parsed_col_index == (int)m->prepared_current_line.size()) {
return true;
}
}
return false;
}
void AbstractCharacterBasedApplication::moveToTop()
{
if (isSingleLineMode()) return;
deselect();
cx()->current_row = 0;
cx()->current_col = 0;
cx()->current_col_hint = 0;
cx()->scroll_row_pos = 0;
scrollToTop();
invalidateArea();
clearParsedLine();
updateVisibility(true, false, true);
}
void AbstractCharacterBasedApplication::logicalMoveToBottom()
{
deselect();
cx()->current_row = documentLines();
cx()->current_col = 0;
if (cx()->current_row > 0) {
cx()->current_row = cx()->current_row - 1;
clearParsedLine();
fetchCurrentLine();
int col = calcVisualWidth(Document::Line(m->parsed_line));
cx()->current_col = col;
cx()->current_col_hint = col;
}
cx()->scroll_row_pos = scrollBottomLimit();
}
void AbstractCharacterBasedApplication::moveToBottom()
{
if (isSingleLineMode()) return;
logicalMoveToBottom();
invalidateArea();
clearParsedLine();
updateVisibility(true, false, true);
}
int AbstractCharacterBasedApplication::findSyntax(QList<Document::CharAttr_> const *list, size_t offset)
{
int lo = 0;
int hi = list->size();
while (lo + 1 < hi) {
int mid = (lo + hi) / 2;
Document::CharAttr_ const *a = &list->at(mid);
if (offset == a->offset) {
return mid;
}
if (offset < a->offset) {
hi = mid;
} else {
lo = mid;
}
}
return lo;
}
void AbstractCharacterBasedApplication::insertSyntax(QList<Document::CharAttr_> *list, size_t offset, Document::CharAttr_ const &a)
{
int i = findSyntax(list, offset);
int n = list->size();
if (i < n) {
if (list->at(i).offset == offset) {
(*list)[i] = a;
} else {
if (i < n) {
if (a.offset < list->at(i).offset) {
if (list->at(i).color == a.color) {
(*list)[i].offset = offset;
return;
}
}
}
if (list->at(i).color == a.color) {
return;
}
if (list->at(i).offset < a.offset) {
i++;
}
list->insert(list->begin() + i, a);
while (++i < n) {
(*list)[i].offset++;
}
}
} else {
list->push_back(a);
}
}
void AbstractCharacterBasedApplication::internalWrite(const ushort *begin, const ushort *end)
{
deleteIfSelected();
clearShiftModifier();
if (cx()->engine->document.lines.isEmpty()) {
Document::Line line;
line.type = Document::Line::Normal;
line.line_number = 1;
cx()->engine->document.lines.push_back(line);
}
if (!isCurrentLineWritable()) return;
int len = end - begin;
parseLine(nullptr, len, false);
int index = m->parsed_col_index;
if (index < 0) {
addNewLineToBottom();
index = 0;
}
std::vector<uint32_t> *vec = &m->prepared_current_line;
auto WriteChar = [&](ushort c){
if (isInsertMode()) {
vec->insert(vec->begin() + index, c);
} else if (isOverwriteMode()) {
if (index < (int)vec->size()) {
ushort d = vec->at(index);
if (d == '\n' || d == '\r') {
vec->insert(vec->begin() + index, c);
} else {
vec->at(index) = c;
}
} else {
vec->push_back(c);
}
}
Document::CharAttr_ a;
a.offset = index;
a.color = m->escape_sequence.fg_color_code();
insertSyntax(&m->syntax_table, index, a);
};
ushort const *ptr = begin;
while (ptr < end) {
ushort c = *ptr;
ptr++;
if (c >= 0xd800 && c < 0xdc00) {
if (ptr < end) {
ushort d = *ptr;
if (d >= 0xdc00 && d < 0xe000) {
ptr++;
int u = 0x10000 + (c - 0xd800) * 0x400 + (d - 0xdc00);
WriteChar(u);
index++;
}
}
} else {
WriteChar(c);
index++;
}
}
m->parsed_col_index = index;
commitLine(*vec);
setCursorColByIndex(*vec, index);
updateVisibility(true, true, true);
}
void AbstractCharacterBasedApplication::pressLetterWithControl(int c)
{
if (c < 0 || c > 0x7f) {
return;
}
if (c < 0x40) {
c += 0x40;
}
c = toupper(c);
switch (c) {
case 'A':
toggleSelectionAnchor();
break;
case 'Q':
onQuit();
break;
case 'O':
onOpenFile();
break;
case 'S':
onSaveFile();
break;
case 'X':
editCut();
break;
case 'C':
editCopy();
break;
case 'V':
editPaste();
break;
}
}
void AbstractCharacterBasedApplication::write(uint32_t c, bool by_keyboard)
{
if (isTerminalMode()) {
if (c == '\r') {
setCursorCol(0);
clearParsedLine();
updateVisibility(true, true, true);
return;
}
if (m->cursor_moved_by_mouse) {
moveToBottom();
}
if (c == 0x1b || m->escape_sequence.isActive()) {
m->escape_sequence.write(c);
return;
}
}
bool ok = !(isTerminalMode() && by_keyboard);
if (c < 0x20) {
if (c == 0x08) {
if (ok) {
doBackspace();
}
} else if (c == 0x09) {
if (ok) {
ushort u = c;
internalWrite(&u, &u + 1);
}
} else if (c == 0x0a) {
if (ok) {
pressEnter();
}
} else if (c == 0x0d) {
if (ok) {
writeCR();
}
} else if (c == 0x1b) {
pressEscape();
} else if (c >= 1 && c <= 26) {
pressLetterWithControl(c);
}
} else if (c == 0x7f) {
if (ok) {
doDelete();
}
} else if (c < 0x10000) {
if (ok) {
ushort u = c;
internalWrite(&u, &u + 1);
}
} else if (c >= 0x10000 && c <= 0x10ffff) {
if (ok) {
ushort t[2];
t[0] = (c - 0x10000) / 0x400 + 0xd800;
t[1] = (c - 0x10000) % 0x400 + 0xdc00;
internalWrite(t, t + 2);
}
} else {
switch (c) {
case EscapeCode::Up:
if (ok) moveCursorUp();
break;
case EscapeCode::Down:
if (ok) moveCursorDown();
break;
case EscapeCode::Right:
if (ok) moveCursorRight();
break;
case EscapeCode::Left:
if (ok) moveCursorLeft();
break;
case EscapeCode::Home:
if (ok) moveCursorHome();
break;
case EscapeCode::End:
if (ok) moveCursorEnd();
break;
case EscapeCode::PageUp:
if (ok) movePageUp();
break;
case EscapeCode::PageDown:
if (ok) movePageDown();
break;
case EscapeCode::Insert:
clearShiftModifier();
break;
case EscapeCode::Delete:
clearShiftModifier();
if (ok) doDelete();
break;
}
}
}
void AbstractCharacterBasedApplication::write(char const *ptr, int len, bool by_keyboard)
{
if (isReadOnly()) return;
char const *begin = ptr;
char const *end = begin + (len < 0 ? strlen(ptr) : len);
char const *left = begin;
char const *right = begin;
while (1) {
int c = -1;
if (right < end) {
c = *right & 0xff;
}
if (c == '\n' || c == '\r' || c < 0) {
utf8 src(left, right);
while (1) {
int d = src.next();
if (d == 0) break;
write(d, by_keyboard);
}
if (c < 0) break;
right++;
if (c == '\r') {
c = isInsertMode() ? '\n' : '\r';
if (right < end && *right == '\n') {
c = '\n';
right++;
}
write(c, by_keyboard);
} else if (c == '\n') {
write('\n', by_keyboard);
}
left = right;
} else {
right++;
}
}
}
void AbstractCharacterBasedApplication::write(const std::string &text)
{
if (!text.empty()) {
write(text.c_str(), text.size(), false);
}
}
void AbstractCharacterBasedApplication::write_(char const *ptr, bool by_keyboard)
{
write(ptr, -1, by_keyboard);
}
-void AbstractCharacterBasedApplication::write_(QString text, bool by_keyboard)
+void AbstractCharacterBasedApplication::write_(QString const &text, bool by_keyboard)
{
if (isReadOnly()) return;
if (text.size() == 1) {
ushort c = text.at(0).unicode();
write(c, by_keyboard);
return;
}
int len = text.size();
if (len > 0) {
ushort const *begin = text.utf16();
ushort const *end = begin + len;
ushort const *left = begin;
ushort const *right = begin;
while (1) {
int c = -1;
if (right < end) {
c = *right;
}
if (c < 0x20) {
if (left < right) {
internalWrite(left, right);
}
if (c == -1) break;
right++;
if (c == '\n' || c == '\r') {
if (c == '\r') {
if (right < end && *right == '\n') {
right++;
}
}
writeNewLine();
} else {
write(c, by_keyboard);
}
left = right;
} else {
right++;
}
}
}
}
void AbstractCharacterBasedApplication::write(QKeyEvent *e)
{
setModifierKeys(e->modifiers());
int c = e->key();
if (c == Qt::Key_Backspace) {
write(0x08, true);
} else if (c == Qt::Key_Delete) {
write(0x7f, true);
} else if (c == Qt::Key_Up) {
if (isControlModifierPressed()) {
scrollUp();
} else {
write(EscapeCode::Up, true);
}
} else if (c == Qt::Key_Down) {
if (isControlModifierPressed()) {
scrollDown();
} else {
write(EscapeCode::Down, true);
}
} else if (c == Qt::Key_Left) {
write(EscapeCode::Left, true);
} else if (c == Qt::Key_Right) {
write(EscapeCode::Right, true);
} else if (c == Qt::Key_PageUp) {
write(EscapeCode::PageUp, true);
} else if (c == Qt::Key_PageDown) {
write(EscapeCode::PageDown, true);
} else if (c == Qt::Key_Home) {
if (isControlModifierPressed()) {
moveToTop();
} else {
write(EscapeCode::Home, true);
}
} else if (c == Qt::Key_End) {
if (isControlModifierPressed()) {
moveToBottom();
} else {
write(EscapeCode::End, true);
}
} else if (c == Qt::Key_Return || c == Qt::Key_Enter) {
write('\n', true);
} else if (c == Qt::Key_Escape) {
write(0x1b, true);
} else if (isControlModifierPressed()) {
if (QChar(c).isLetter()) {
c = QChar(c).toUpper().unicode();
if (c >= 0x40 && c < 0x60) {
write(c - 0x40, true);
}
}
} else {
QString text = e->text();
write_(text, true);
}
}
void Document::retrieveLastText(std::vector<char> *out, int maxlen) const
{
int remain = maxlen;
int i = lines.size();
while (i > 0 && remain > 0) {
i--;
QByteArray const &data = lines[i].text;
int n = data.size();
if (n > remain) {
n = remain;
}
char const *p = data.data() + data.size() - n;
out->insert(out->begin(), p, p + n);
remain -= n;
}
}
diff --git a/src/texteditor/AbstractCharacterBasedApplication.h b/src/texteditor/AbstractCharacterBasedApplication.h
index 34d4f5f..6a6dc7a 100644
--- a/src/texteditor/AbstractCharacterBasedApplication.h
+++ b/src/texteditor/AbstractCharacterBasedApplication.h
@@ -1,421 +1,421 @@
#ifndef ABSTRACTCHARACTERBASEDAPPLICATION_H
#define ABSTRACTCHARACTERBASEDAPPLICATION_H
#include <QList>
#include <QRect>
#include <QString>
#include <QByteArray>
#include <memory>
#include <vector>
#include <functional>
#include <QKeyEvent>
#include <QColor>
namespace EscapeCode {
enum EscapeCode {
Up = 0x1b5b4100,
Down = 0x1b5b4200,
Right = 0x1b5b4300,
Left = 0x1b5b4400,
Home = 0x1b4f4800,
End = 0x1b4f4600,
Insert = 0x1b5b327e,
Delete = 0x1b5b337e,
PageUp = 0x1b5b357e,
PageDown = 0x1b5b367e,
};
}
class Document {
public:
struct CharAttr_ {
size_t offset = 0;
int color = -1;
};
struct Line {
enum Type {
Unknown,
Normal,
Add,
Del,
};
int type = Unknown;
int hunk_number = -1;
int line_number = 0;
size_t byte_offset = 0;
QByteArray text;
Line() = default;
explicit Line(QByteArray const &ba)
: type(Normal)
, text(ba)
{
}
explicit Line(std::string const &str, Type type = Normal)
: type(type)
{
text.append(str.c_str(), str.size());
}
};
QList<Line> lines;
void retrieveLastText(std::vector<char> *out, int maxlen) const;
};
class TextEditorEngine {
public:
Document document;
TextEditorEngine()
{
document.lines.push_back(Document::Line(QByteArray()));
}
};
struct SelectionAnchor {
enum Enabled {
No,
EnabledHard,
EnabledEasy,
};
Enabled enabled = No;
int row = 0;
int col = 0;
int compare(SelectionAnchor const &a) const
{
if (enabled && a.enabled) {
if (row < a.row) return -1;
if (row > a.row) return 1;
if (col < a.col) return -1;
if (col > a.col) return 1;
} else {
if (a.enabled) return -1;
if (enabled) return 1;
}
return 0;
}
bool operator == (SelectionAnchor const &a) const
{
return compare(a) == 0;
}
bool operator != (SelectionAnchor const &a) const
{
return compare(a) != 0;
}
bool operator < (SelectionAnchor const &a) const
{
return compare(a) < 0;
}
bool operator > (SelectionAnchor const &a) const
{
return compare(a) > 0;
}
bool operator <= (SelectionAnchor const &a) const
{
return compare(a) <= 0;
}
bool operator >= (SelectionAnchor const &a) const
{
return compare(a) >= 0;
}
};
using TextEditorEnginePtr = std::shared_ptr<TextEditorEngine>;
struct TextEditorContext {
QRect cursor_rect;
bool single_line = false;
int current_row = 0;
int current_col = 0;
int current_col_hint = 0;
int saved_row = 0;
int saved_col = 0;
int saved_col_hint = 0;
int current_char_span = 1;
int scroll_row_pos = 0;
int scroll_col_pos = 0;
int viewport_org_x = 0;
int viewport_org_y = 1;
int viewport_width = 80;
int viewport_height = 23;
int tab_span = 4;
int bottom_line_y = -1;
TextEditorEnginePtr engine;
};
using DialogHandler = std::function<void (bool, QString const &)>;
class AbstractCharacterBasedApplication {
public:
static const int LEFT_MARGIN = 8;
static const int RIGHT_MARGIN = 10;
enum class WriteMode {
Insert,
Overwrite,
};
enum class State {
Normal,
Exit,
};
struct CharAttr {
uint16_t index;
uint16_t flags = 0;
QColor color;
CharAttr(int index = Normal)
: index(index)
{
}
bool operator == (CharAttr const &r) const
{
return index == r.index && color == r.color;
}
bool operator != (CharAttr const &r) const
{
return !operator == (r);
}
enum Index {
Normal,
Invert,
Hilite,
};
enum Flag {
Selected = 0x0001,
CurrentLine = 0x0002,
};
};
struct Option {
CharAttr char_attr;
QRect clip;
};
struct Character {
uint16_t c = 0;
CharAttr a;
};
enum LineFlag {
LineChanged = 1,
};
static int charWidth(uint32_t c);
class FormattedLine {
public:
QString text;
enum Attr {
StyleID = 0x00ffffff,
Selected = 0x01000000,
};
uint32_t atts;
FormattedLine(QString const &text, int atts)
: text(text)
, atts(atts)
{
}
bool isSelected() const
{
return atts & Selected;
}
};
QList<FormattedLine> formatLine(const Document::Line &line, int tab_span, int anchor_a = -1, int anchor_b = -1) const;
private:
struct Private;
Private *m;
protected:
SelectionAnchor selection_anchor_0;
SelectionAnchor selection_anchor_1;
protected:
std::vector<Character> *screen();
std::vector<Character> const *screen() const;
std::vector<uint8_t> *line_flags();
void initEditor();
void fetchCurrentLine() const;
void clearParsedLine();
int cursorX() const;
int cursorY() const;
int editorViewportWidth() const;
int editorViewportHeight() const;
virtual int print(int x, int y, QString const &text, Option const &opt);
std::shared_ptr<TextEditorContext> editor_cx;
std::shared_ptr<TextEditorContext> dialog_cx;
TextEditorContext *cx();
TextEditorContext const *cx() const;
Document *document();
int documentLines() const;
bool isSingleLineMode() const;
void ensureCurrentLineVisible();
int decideColumnScrollPos() const;
int calcVisualWidth(Document::Line const &line) const;
int leftMargin_() const;
void makeBuffer();
int printArea(const TextEditorContext *cx, SelectionAnchor const *sel_a = nullptr, SelectionAnchor const *sel_b = nullptr);
int calcIndexToColumn(const std::vector<uint32_t> &vec, int index) const;
virtual void updateVisibility(bool ensure_current_line_visible, bool change_col, bool auto_scroll) = 0;
void commitLine(const std::vector<uint32_t> &vec);
void doDelete();
void doBackspace();
bool isDialogMode();
void setDialogMode(bool f);
void closeDialog(bool result);
void setDialogOption(QString const &title, QString const &value, DialogHandler handler);
void execDialog(QString const &dialog_title, QString dialog_value, DialogHandler handler);
void toggleSelectionAnchor();
private:
int internalParseLine(std::vector<uint32_t> *vec, int increase_hint) const;
void internalWrite(const ushort *begin, const ushort *end);
void pressLetterWithControl(int c);
void invalidateAreaBelowTheCurrentLine();
void onQuit();
void onOpenFile();
void onSaveFile();
void printInvertedBar(int x, int y, char const *text, int padchar);
SelectionAnchor currentAnchor(SelectionAnchor::Enabled enabled);
enum class EditOperation {
Cut,
Copy,
};
void editSelected(EditOperation op, std::vector<uint32_t> *cutbuffer);
void deselect();
int calcColumnToIndex(int column);
void edit_(EditOperation op);
bool isCurrentLineWritable() const;
void initEngine(const std::shared_ptr<TextEditorContext>& cx);
void writeCR();
bool deleteIfSelected();
static int findSyntax(const QList<Document::CharAttr_> *list, size_t offset);
static void insertSyntax(QList<Document::CharAttr_> *list, size_t offset, const Document::CharAttr_ &a);
protected:
void parseLine(std::vector<uint32_t> *vec, int increase_hint, bool force);
QByteArray parsedLine() const;
void setCursorRow(int row, bool auto_scroll = true, bool by_mouse = false);
void setCursorCol(int col, bool auto_scroll = true, bool by_mouse = false);
void setCursorPos(int row, int col);
void setCursorColByIndex(const std::vector<uint32_t> &vec, int col_index);
int nextTabStop(int x) const;
int scrollBottomLimit() const;
bool isPaintingSuppressed() const;
void setPaintingSuppressed(bool f);
void scrollRight();
void scrollLeft();
void addNewLineToBottom();
void appendNewLine(std::vector<uint32_t> *vec);
void writeNewLine();
void updateCursorPos(bool auto_scroll);
QString statusLine() const;
void preparePaintScreen();
void setRecentlyUsedPath(QString const &path);
QString recentlyUsedPath();
void clearRect(int x, int y, int w, int h);
void paintLineNumbers(std::function<void(int, QString, Document::Line const *line)> const &draw);
bool isAutoLayout() const;
void invalidateArea(int top_y = 0);
void savePos();
void restorePos();
public:
virtual void layoutEditor();
void scrollUp();
void scrollDown();
void moveCursorOut();
void moveCursorHome();
void moveCursorEnd();
void moveCursorUp();
void moveCursorDown();
void moveCursorLeft();
void moveCursorRight();
void movePageUp();
void movePageDown();
void scrollToTop();
AbstractCharacterBasedApplication();
virtual ~AbstractCharacterBasedApplication();
TextEditorEnginePtr engine();
int screenWidth() const;
int screenHeight() const;
void setScreenSize(int w, int h, bool update_layout);
void setTextEditorEngine(const TextEditorEnginePtr &e);
void openFile(QString const &path);
void saveFile(QString const &path);
void loadExampleFile();
void pressEnter();
void pressEscape();
State state() const;
bool isLineNumberVisible() const;
void showLineNumber(bool show, int left_margin = LEFT_MARGIN);
void showHeader(bool f);
void showFooter(bool f);
void setAutoLayout(bool f);
void setDocument(const QList<Document::Line> *source);
void setSelectionAnchor(SelectionAnchor::Enabled enabled, bool update_anchor, bool auto_scroll);
bool isSelectionAnchorEnabled() const;
void setNormalTextEditorMode(bool f);
void setToggleSelectionAnchorEnabled(bool f);
void setReadOnly(bool f);
bool isReadOnly() const;
void editPaste();
void editCopy();
void editCut();
void setWriteMode(WriteMode wm);
bool isInsertMode() const;
bool isOverwriteMode() const;
void setTerminalMode(bool f);
bool isTerminalMode() const;
void moveToTop();
void moveToBottom();
bool isBottom() const;
void setLineMargin(int n);
void write(uint32_t c, bool by_keyboard);
void write(char const *ptr, int len, bool by_keyboard);
void write(std::string const &text);
void write(QKeyEvent *e);
void setTextCodec(QTextCodec *codec);
void setCursorVisible(bool show);
bool isCursorVisible();
void setModifierKeys(Qt::KeyboardModifiers keymod);
bool isControlModifierPressed() const;
bool isShiftModifierPressed() const;
void clearShiftModifier();
void retrieveLastText(std::vector<char> *out, int maxlen) const;
bool isChanged() const;
void setChanged(bool f);
void logicalMoveToBottom();
protected:
void write_(char const *ptr, bool by_keyboard);
- void write_(QString text, bool by_keyboard);
+ void write_(const QString &text, bool by_keyboard);
void makeColumnPosList(std::vector<int> *out);
};
class AbstractTextEditorApplication : public AbstractCharacterBasedApplication {
};
#endif // ABSTRACTCHARACTERBASEDAPPLICATION_H
diff --git a/src/texteditor/InputMethodPopup.h b/src/texteditor/InputMethodPopup.h
index 208ddfc..fbefb2a 100644
--- a/src/texteditor/InputMethodPopup.h
+++ b/src/texteditor/InputMethodPopup.h
@@ -1,33 +1,33 @@
#ifndef INPUTMETHODPOPUP_H
#define INPUTMETHODPOPUP_H
#include "TextEditorWidget.h"
#include <QWidget>
class InputMethodPopup : public QWidget
{
Q_OBJECT
private:
struct {
PreEditText preedit;
} data;
public:
- explicit InputMethodPopup(QWidget *parent = 0);
+ explicit InputMethodPopup(QWidget *parent = nullptr);
void setPreEditText(const PreEditText &preedit);
signals:
public slots:
// QWidget interface
protected:
void paintEvent(QPaintEvent *);
// QWidget interface
public slots:
void setVisible(bool visible);
};
#endif // INPUTMETHODPOPUP_H
diff --git a/src/texteditor/TextEditorWidget.h b/src/texteditor/TextEditorWidget.h
index ffed245..39ad93b 100644
--- a/src/texteditor/TextEditorWidget.h
+++ b/src/texteditor/TextEditorWidget.h
@@ -1,106 +1,106 @@
#ifndef OREWIDGET_H
#define OREWIDGET_H
#include <QTextFormat>
#include <QWidget>
#include <vector>
#include <stdint.h>
#include <memory>
#include "AbstractCharacterBasedApplication.h"
#include "TextEditorTheme.h"
class QScrollBar;
struct PreEditText {
struct Format {
int start;
int length;
QTextFormat format;
Format(int start, int length, QTextFormat const &f)
: start(start)
, length(length)
, format(f)
{
}
};
QString text;
std::vector<Format> format;
};
class TextEditorWidget : public QWidget, public AbstractTextEditorApplication {
Q_OBJECT
public:
enum RenderingMode {
CharacterMode,
DecoratedMode,
};
private:
struct Private;
Private *m;
void paintScreen(QPainter *painter);
void drawCursor(QPainter *pr);
void drawFocusFrame(QPainter *pr);
QRect updateCursorRect(bool auto_scroll);
RenderingMode renderingMode() const;
QColor defaultForegroundColor();
QColor defaultBackgroundColor();
QColor colorForIndex(CharAttr const &attr, bool foreground);
//QColor colorForIndex(int index, bool foreground);
void internalUpdateVisibility(bool ensure_current_line_visible, bool change_col, bool auto_scroll);
void internalUpdateScrollBar();
void moveCursorByMouse();
protected:
void paintEvent(QPaintEvent *);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event);
void resizeEvent(QResizeEvent *event);
void contextMenuEvent(QContextMenuEvent *event);
QFont textFont();
void drawText(QPainter *painter, int px, int py, QString const &str);
public:
- explicit TextEditorWidget(QWidget *parent = 0);
+ explicit TextEditorWidget(QWidget *parent = nullptr);
~TextEditorWidget();
void setTheme(TextEditorThemePtr theme);
TextEditorTheme const *theme() const;
int latin1Width() const;
int lineHeight() const;
void setPreEditText(PreEditText const &preedit);
void updateVisibility(bool ensure_current_line_visible, bool change_col, bool auto_scroll);
bool event(QEvent *event);
void bindScrollBar(QScrollBar *vsb, QScrollBar *hsb);
void setupForLogWidget(QScrollBar *vsb, QScrollBar *hsb, TextEditorThemePtr theme);
QPoint mapFromPixel(const QPoint &pt);
QPoint mapToPixel(const QPoint &pt);
QVariant inputMethodQuery(Qt::InputMethodQuery q) const;
void inputMethodEvent(QInputMethodEvent *e);
void refrectScrollBar();
void setRenderingMode(RenderingMode mode);
void move(int cur_row, int cur_col, int scr_row, int scr_col, bool auto_scroll);
void layoutEditor();
void setFocusFrameVisible(bool f);
signals:
void moved(int cur_row, int cur_col, int scr_row, int scr_col);
void updateScrollBar();
void idle();
protected:
void timerEvent(QTimerEvent *);
};
#endif // OREWIDGET_H
diff --git a/src/unix/UnixPtyProcess.cpp b/src/unix/UnixPtyProcess.cpp
index fe77fce..2e1d18a 100644
--- a/src/unix/UnixPtyProcess.cpp
+++ b/src/unix/UnixPtyProcess.cpp
@@ -1,239 +1,239 @@
#include "UnixPtyProcess.h"
#include <unistd.h>
-#include <stdlib.h>
-#include <signal.h>
+#include <cstdlib>
+#include <csignal>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <deque>
#include <QMutex>
#include <QDir>
namespace {
void make_argv(char *command, std::vector<char *> *out)
{
char *dst = command;
char *src = command;
char *arg = command;
bool quote = false;
bool accept = false;
while (1) {
char c = *src;
if (c == '\"') {
if (src[1] == '\"' && src[2] == '\"') {
*dst++ = c;
src += 3;
} else {
quote = !quote;
accept = true;
src++;
}
} else if (quote && c != 0) {
*dst++ = *src++;
} else if (c == 0 || isspace(c & 0xff)) {
*dst++ = 0;
if (accept || *arg) {
out->push_back(arg);
}
if (c == 0) break;
accept = false;
arg = dst;
src++;
} else {
*dst++ = *src++;
}
}
}
class OutputReaderThread : public QThread {
private:
QMutex *mutex;
int pty_master;
std::deque<char> *output_queue;
std::vector<char> *output_vector;
protected:
void run()
{
while (1) {
if (isInterruptionRequested()) break;
char buf[1024];
int len = read(pty_master, buf, sizeof(buf));
if (len < 1) break;
{
QMutexLocker lock(mutex);
output_queue->insert(output_queue->end(), buf, buf + len);
output_vector->insert(output_vector->end(), buf, buf + len);
}
}
}
public:
void start(QMutex *mutex, int pty_master, std::deque<char> *outq, std::vector<char> *outv)
{
this->mutex = mutex;
this->pty_master = pty_master;
this->output_queue = outq;
this->output_vector = outv;
QThread::start();
}
};
} // namespace
// UnixPtyProcess
struct UnixPtyProcess::Private {
QMutex mutex;
std::string command;
int pty_master;
std::deque<char> output_queue;
std::vector<char> output_vector;
OutputReaderThread th_output_reader;
int exit_code = -1;
};
UnixPtyProcess::UnixPtyProcess()
: m(new Private)
{
}
UnixPtyProcess::~UnixPtyProcess()
{
stop();
delete m;
}
bool UnixPtyProcess::isRunning() const
{
return QThread::isRunning();
}
void UnixPtyProcess::writeInput(char const *ptr, int len)
{
int r = write(m->pty_master, ptr, len);
(void)r;
}
int UnixPtyProcess::readOutput(char *ptr, int len)
{
QMutexLocker lock(&m->mutex);
int n = m->output_queue.size();
if (n > len) {
n = len;
}
if (n > 0) {
auto it = m->output_queue.begin();
std::copy(it, it + n, ptr);
m->output_queue.erase(it, it + n);
}
return n;
}
void UnixPtyProcess::start(QString const &cmd)
{
if (isRunning()) return;
m->command = cmd.toStdString();
QThread::start();
}
bool UnixPtyProcess::wait(unsigned long time)
{
return QThread::wait(time);
}
void UnixPtyProcess::run()
{
struct termios orig_termios;
struct winsize orig_winsize;
tcgetattr(STDIN_FILENO, &orig_termios);
ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&orig_winsize);
m->pty_master = posix_openpt(O_RDWR);
grantpt(m->pty_master);
unlockpt(m->pty_master);
pid_t pid = fork();
if (pid == 0) {
setsid();
putenv(const_cast<char *>("LANG=C"));
char *pts_name = ptsname(m->pty_master);
int pty_slave = open(pts_name, O_RDWR);
close(m->pty_master);
struct termios tio;
memset(&tio, 0, sizeof(tio));
cfmakeraw(&tio);
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
tio.c_lflag |= ECHO;
tcsetattr(pty_slave, TCSANOW, &tio);
ioctl(pty_slave, TIOCSWINSZ, &orig_winsize);
dup2(pty_slave, STDIN_FILENO);
dup2(pty_slave, STDOUT_FILENO);
dup2(pty_slave, STDERR_FILENO);
close(pty_slave);
QDir::setCurrent(change_dir);
char *command = (char *)alloca(m->command.size() + 1);
strcpy(command, m->command.c_str());
std::vector<char *> argv;
make_argv(command, &argv);
argv.push_back(nullptr);
execvp(argv[0], &argv[0]);
} else {
m->th_output_reader.start(&m->mutex, m->pty_master, &m->output_queue, &m->output_vector);
while (1) {
if (isInterruptionRequested()) break;
int status = 0;
int r = waitpid(pid, &status, WNOHANG);
if (r < 0) break;
QThread::currentThread()->msleep(1);
if (r > 0) {
if (WIFEXITED(status)) {
m->exit_code = WEXITSTATUS(status);
break;
}
if (WIFSIGNALED(status)) {
break;
}
}
}
kill(pid, SIGTERM);
m->th_output_reader.requestInterruption();
m->th_output_reader.wait();
close(m->pty_master);
m->pty_master = -1;
emit completed();
}
}
void UnixPtyProcess::stop()
{
requestInterruption();
wait();
}
int UnixPtyProcess::getExitCode() const
{
return m->exit_code;
}
QString UnixPtyProcess::getMessage() const
{
QString s;
if (!m->output_vector.empty()) {
s = QString::fromUtf8(&m->output_vector[0], m->output_vector.size());
}
return s;
}
diff --git a/src/urlencode.cpp b/src/urlencode.cpp
index d30b0df..40aeda8 100644
--- a/src/urlencode.cpp
+++ b/src/urlencode.cpp
@@ -1,152 +1,152 @@
#include "urlencode.h"
#include "charvec.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
+#include <cstdlib>
+#include <cstdio>
+#include <cctype>
+#include <cstring>
#ifdef WIN32
#pragma warning(disable:4996)
#endif
static void url_encode_(char const *ptr, char const *end, std::vector<char> *out)
{
while (ptr < end) {
int c = (unsigned char)*ptr;
ptr++;
if (isalnum(c) || strchr("_.-~", c)) {
print(out, c);
} else if (c == ' ') {
print(out, '+');
} else {
char tmp[10];
sprintf(tmp, "%%%02X", c);
print(out, tmp[0]);
print(out, tmp[1]);
print(out, tmp[2]);
}
}
}
std::string url_encode(char const *str, char const *end)
{
if (!str) {
return std::string();
}
std::vector<char> out;
out.reserve(end - str + 10);
url_encode_(str, end, &out);
return to_stdstr(out);
}
std::string url_encode(char const *str, size_t len)
{
return url_encode(str, str + len);
}
std::string url_encode(char const *str)
{
return url_encode(str, strlen(str));
}
std::string url_encode(std::string const &str)
{
char const *begin = str.c_str();
char const *end = begin + str.size();
char const *ptr = begin;
while (ptr < end) {
int c = (unsigned char)*ptr;
if (isalnum(c) || strchr("_.-~", c)) {
// thru
} else {
break;
}
ptr++;
}
if (ptr == end) {
return str;
}
std::vector<char> out;
out.reserve(str.size() + 10);
out.insert(out.end(), begin, ptr);
url_encode_(ptr, end, &out);
return to_stdstr(out);
}
static void url_decode_(char const *ptr, char const *end, std::vector<char> *out)
{
while (ptr < end) {
int c = (unsigned char)*ptr;
ptr++;
if (c == '+') {
c = ' ';
} else if (c == '%' && isxdigit((unsigned char)ptr[0]) && isxdigit((unsigned char)ptr[1])) {
char tmp[3]; // '%XX'
tmp[0] = ptr[0];
tmp[1] = ptr[1];
tmp[2] = 0;
c = strtol(tmp, nullptr, 16);
ptr += 2;
}
print(out, c);
}
}
std::string url_decode(char const *str, char const *end)
{
if (!str) {
return std::string();
}
std::vector<char> out;
out.reserve(end - str + 10);
url_decode_(str, end, &out);
return to_stdstr(out);
}
std::string url_decode(char const *str, size_t len)
{
return url_decode(str, str + len);
}
std::string url_decode(char const *str)
{
return url_decode(str, strlen(str));
}
std::string url_decode(std::string const &str)
{
char const *begin = str.c_str();
char const *end = begin + str.size();
char const *ptr = begin;
while (ptr < end) {
int c = *ptr & 0xff;
if (c == '+' || c == '%') {
break;
}
ptr++;
}
if (ptr == end) {
return str;
}
std::vector<char> out;
out.reserve(str.size() + 10);
out.insert(out.end(), begin, ptr);
url_decode_(ptr, end, &out);
return to_stdstr(out);
}
diff --git a/src/webclient.cpp b/src/webclient.cpp
index 5655dbd..5643da6 100644
--- a/src/webclient.cpp
+++ b/src/webclient.cpp
@@ -1,1083 +1,1083 @@
#include "webclient.h"
-#include <string.h>
+#include <cstring>
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#if USE_OPENSSL
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
#endif
typedef SOCKET socket_t;
#pragma warning(disable:4996)
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netdb.h>
#define closesocket(S) ::close(S)
typedef int socket_t;
#define INVALID_SOCKET (-1)
#define SOCKET_ERROR (-1)
#define stricmp(A, B) strcasecmp(A, B)
#define strnicmp(A, B, C) strncasecmp(A, B, C)
#endif
#if USE_OPENSSL
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#else
typedef void SSL;
typedef void SSL_CTX;
#endif
-#include <assert.h>
+#include <cassert>
#include "charvec.h"
#define USER_AGENT "Generic Web Client"
struct WebContext::Private {
- SSL_CTX *ctx;
+ SSL_CTX *ctx = nullptr;
bool use_keep_alive = false;
WebProxy http_proxy;
WebProxy https_proxy;
};
WebClient::URL::URL(std::string const &addr)
{
data.full_request = addr;
char const *str = addr.c_str();
char const *left;
char const *right;
left = str;
right = strstr(left, "://");
if (right) {
data.scheme.assign(str, right - str);
left = right + 3;
}
right = strchr(left, '/');
if (!right) {
right = left + strlen(left);
}
if (right) {
char const *p = strchr(left, ':');
if (p && left < p && p < right) {
int n = 0;
char const *q = p + 1;
while (q < right) {
if (isdigit(*q & 0xff)) {
n = n * 10 + (*q - '0');
} else {
n = -1;
break;
}
q++;
}
data.host.assign(left, p - left);
if (n > 0 && n < 65536) {
data.port = n;
}
} else {
data.host.assign(left, right - left);
}
data.path = right;
}
}
bool WebClient::URL::isssl() const
{
if (scheme() == "https") return true;
if (scheme() == "http") return false;
if (port() == 443) return true;
return false;
}
void WebClientHandler::abort(const std::string &message)
{
throw WebClient::Error(message);
}
struct WebClient::Private {
std::vector<std::string> request_header;
Error error;
Response response;
WebContext *webcx;
int crlf_state = 0;
size_t content_offset = 0;
std::string last_host_name;
int last_port = 0;
bool keep_alive = false;
socket_t sock = INVALID_SOCKET;
SSL *ssl = nullptr;
};
WebClient::WebClient(WebContext *webcx)
: m(new Private)
{
assert(webcx);
m->webcx = webcx;
}
WebClient::~WebClient()
{
close();
delete m;
}
void WebClient::initialize()
{
#ifdef _WIN32
WSADATA wsaData;
WORD wVersionRequested;
wVersionRequested = MAKEWORD(1, 1);
WSAStartup(wVersionRequested, &wsaData);
atexit(cleanup);
#endif
#if USE_OPENSSL
OpenSSL_add_all_algorithms();
#endif
}
void WebClient::cleanup()
{
#if USE_OPENSSL
ERR_free_strings();
#endif
#ifdef _WIN32
WSACleanup();
#endif
}
void WebClient::reset()
{
m->request_header.clear();
m->error = Error();
m->response = Response();
m->crlf_state = 0;
m->content_offset = 0;
}
void WebClient::output_debug_string(char const *str)
{
if (0) {
#ifdef _WIN32
OutputDebugStringA(str);
#else
fwrite(str, 1, strlen(str), stderr);
#endif
}
}
void WebClient::output_debug_strings(std::vector<std::string> const &vec)
{
for (std::string const &s : vec) {
output_debug_string((s + '\n').c_str());
}
}
WebClient::Error const &WebClient::error() const
{
return m->error;
}
void WebClient::clear_error()
{
m->error = Error();
}
int WebClient::get_port(URL const *url, char const *scheme, char const *protocol)
{
int port = url->port();
if (port < 1 || port > 65535) {
struct servent *s;
s = getservbyname(url->scheme().c_str(), protocol);
if (s) {
port = ntohs(s->s_port);
} else {
s = getservbyname(scheme, protocol);
if (s) {
port = ntohs(s->s_port);
}
}
if (port < 1 || port > 65535) {
port = 80;
}
}
return port;
}
static inline std::string to_s(size_t n)
{
char tmp[100];
sprintf(tmp, "%u", (int)n);
return tmp;
}
void WebClient::set_default_header(URL const &url, Post const *post, RequestOption const &opt)
{
std::vector<std::string> header;
auto AddHeader = [&](std::string const &s){
header.push_back(s);
};
AddHeader("Host: " + url.host());
AddHeader("User-Agent: " USER_AGENT);
AddHeader("Accept: */*");
if (opt.keep_alive) {
AddHeader("Connection: keep-alive");
} else {
AddHeader("Connection: close");
}
if (post) {
AddHeader("Content-Length: " + to_s(post->data.size()));
std::string ct = "Content-Type: ";
if (post->content_type.empty()) {
ct += "application/octet-stream";
} else if (post->content_type == CT_MULTIPART_FORM_DATA) {
ct += post->content_type;
if (!post->boundary.empty()) {
ct += "; boundary=";
ct += post->boundary;
}
} else {
ct += post->content_type;
}
AddHeader(ct);
}
header.insert(header.end(), m->request_header.begin(), m->request_header.end());
m->request_header = std::move(header);
}
std::string WebClient::make_http_request(URL const &url, Post const *post, WebProxy const *proxy, bool https)
{
std::string str;
str = post ? "POST " : "GET ";
if (proxy && !https) {
str += url.data.full_request;
str += " HTTP/1.0";
str += "\r\n";
} else {
str += url.path();
str += " HTTP/1.0";
str += "\r\n";
}
for (std::string const &s: m->request_header) {
str += s;
str += "\r\n";
}
str += "\r\n";
return str;
}
void WebClient::parse_http_header(char const *begin, char const *end, std::vector<std::string> *header)
{
if (begin < end) {
char const *left = begin;
char const *right = left;
while (1) {
if (right >= end) {
break;
}
if (*right == '\r' || *right == '\n') {
if (left < right) {
header->push_back(std::string(left, right));
}
if (right + 1 < end && *right == '\r' && right[1] == '\n') {
right++;
}
right++;
if (*right == '\r' || *right == '\n') {
if (right + 1 < end && *right == '\r' && right[1] == '\n') {
right++;
}
right++;
left = right;
break;
}
left = right;
} else {
right++;
}
}
}
}
void WebClient::parse_http_header(char const *begin, char const *end, WebClient::Response *out)
{
*out = Response();
parse_http_header(begin, end, &out->header);
parse_header(&out->header, out);
}
static void send_(socket_t s, char const *ptr, int len)
{
while (len > 0) {
int n = send(s, ptr, len, 0);
if (n < 1 || n > len) {
throw WebClient::Error("send request failed.");
}
ptr += n;
len -= n;
}
}
void WebClient::on_end_header(std::vector<char> const *vec, WebClientHandler *handler)
{
if (vec->empty()) return;
char const *begin = &vec->at(0);
char const *end = begin + vec->size();
parse_http_header(begin, end, &m->response);
if (handler) {
handler->checkHeader(this);
}
}
void WebClient::append(char const *ptr, size_t len, std::vector<char> *out, WebClientHandler *handler)
{
size_t offset = out->size();
out->insert(out->end(), ptr, ptr + len);
if (m->crlf_state < 0) {
// nop
} else {
for (size_t i = 0; i < len; i++) {
int c = ptr[i] & 0xff;
if (c == '\r') {
m->crlf_state |= 1;
} else if (c == '\n') {
m->crlf_state |= 1;
m->crlf_state++;
} else {
m->crlf_state = 0;
}
if (m->crlf_state == 4) {
m->content_offset = offset + i + 1;
on_end_header(out, handler);
m->crlf_state = -1;
break;
}
}
}
if (handler && m->content_offset > 0) {
offset = out->size();
if (offset > m->content_offset) {
size_t len = offset - m->content_offset;
char const *ptr = &out->at(m->content_offset);
handler->checkContent(ptr, len);
}
}
}
static char *stristr(char *str1, char const *str2)
{
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
for (size_t i = 0; i + len2 <= len1; i++) {
if (strnicmp(str1 + i, str2, len2) == 0) {
return str1 + i;
}
}
return nullptr;
}
class ResponseHeader {
public:
size_t pos = 0;
std::vector<char> line;
int content_length = -1;
bool connection_keep_alive = false;
bool connection_close = false;
int lf = 0;
enum State {
Header,
Content,
};
State state = Header;
void put(int c)
{
pos++;
if (state == Header) {
if (c== '\r' || c == '\n') {
if (!line.empty()) {
line.push_back(0);
char *begin = &line[0];
char *p = strchr(begin, ':');
if (p && *p == ':') {
*p++ = 0;
auto IS = [&](char const *name){ return stricmp(begin, name) == 0; };
if (IS("content-length")) {
content_length = strtol(p, nullptr, 10);
} else if (IS("connection")) {
if (stristr(p, "keep-alive")) {
connection_keep_alive = true;
} else if (stristr(p, "close")) {
connection_close = true;
}
}
}
line.clear();
}
if (c== '\r') {
return;
}
if (c == '\n') {
lf++;
if (lf == 2) {
state = Content;
}
return;
}
}
lf = 0;
line.push_back(c);
}
}
};
void WebClient::receive_(RequestOption const &opt, std::function<int(char *, int)> rcv, std::vector<char> *out)
{
char buf[4096];
size_t pos = 0;
ResponseHeader rh;
while (1) {
int n;
if (rh.state == ResponseHeader::Content && rh.content_length >= 0) {
n = rh.pos + rh.content_length - pos;
if (n > (int)sizeof(buf)) {
n = sizeof(buf);
}
if (n < 1) break;
} else {
n = sizeof(buf);
}
n = rcv(buf, n);
if (n < 1) break;
if (0) { // debug
fwrite(buf, 1, n, stderr);
}
append(buf, n, out, opt.handler);
pos += n;
if (rh.state == ResponseHeader::Header) {
for (int i = 0; i < n; i++) {
rh.put(buf[i]);
if (rh.state == ResponseHeader::Content) {
m->keep_alive = rh.connection_keep_alive && !rh.connection_close;
break;
}
}
}
}
}
bool WebClient::http_get(URL const &request_url, Post const *post, RequestOption const &opt, std::vector<char> *out)
{
clear_error();
out->clear();
URL server_url;
WebProxy const *proxy = m->webcx->http_proxy();
if (proxy) {
server_url = URL(proxy->server);
} else {
server_url = request_url;
}
std::string hostname = server_url.host();
int port = get_port(&server_url, "http", "tcp");
m->keep_alive = opt.keep_alive && hostname == m->last_host_name && port == m->last_port;
if (!m->keep_alive) close();
if (m->sock == INVALID_SOCKET) {
struct hostent *servhost;
struct sockaddr_in server;
servhost = gethostbyname(hostname.c_str());
if (!servhost) {
throw Error("gethostbyname failed.");
}
memset((char *)&server, 0, sizeof(server));
server.sin_family = AF_INET;
memcpy((char *)&server.sin_addr, servhost->h_addr, servhost->h_length);
server.sin_port = htons(port);
m->sock = socket(AF_INET, SOCK_STREAM, 0);
if (m->sock == INVALID_SOCKET) {
throw Error("socket failed.");
}
if (connect(m->sock, (struct sockaddr*) &server, sizeof(server)) == SOCKET_ERROR) {
throw Error("connect failed.");
}
}
m->last_host_name = hostname;
m->last_port = port;
set_default_header(request_url, post, opt);
std::string request = make_http_request(request_url, post, proxy, false);
send_(m->sock, request.c_str(), (int)request.size());
if (post && !post->data.empty()) {
send_(m->sock, (char const *)&post->data[0], (int)post->data.size());
}
m->crlf_state = 0;
m->content_offset = 0;
receive_(opt, [&](char *ptr, int len){
return recv(m->sock, ptr, len, 0);
}, out);
if (!m->keep_alive) close();
return true;
}
bool WebClient::https_get(const URL &request_url, Post const *post, RequestOption const &opt, std::vector<char> *out)
{
#if USE_OPENSSL
if (!m->webcx || !m->webcx->m->ctx) {
output_debug_string("SSL context is null.\n");
return false;
}
auto sslctx = [&](){ return m->webcx->m->ctx; };
clear_error();
out->clear();
auto get_ssl_error = []()->std::string{
char tmp[1000];
unsigned long e = ERR_get_error();
ERR_error_string_n(e, tmp, sizeof(tmp));
return tmp;
};
URL server_url;
WebProxy const *proxy = m->webcx->https_proxy();
if (proxy) {
server_url = URL(proxy->server);
} else {
server_url = request_url;
}
std::string hostname = server_url.host();
int port = get_port(&server_url, "https", "tcp");
m->keep_alive = opt.keep_alive && hostname == m->last_host_name && port == m->last_port;
if (!m->keep_alive) close();
socket_t sock = m->sock;
SSL *ssl = m->ssl;
if (sock == INVALID_SOCKET || !ssl) {
int ret;
struct hostent *servhost;
struct sockaddr_in server;
servhost = gethostbyname(server_url.host().c_str());
if (!servhost) {
throw Error("gethostbyname failed.");
}
memset((char *)&server, 0, sizeof(server));
server.sin_family = AF_INET;
memcpy((char *)&server.sin_addr, servhost->h_addr, servhost->h_length);
server.sin_port = htons(port);
if (sock == INVALID_SOCKET) {
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
throw Error("socket failed.");
}
if (connect(sock, (struct sockaddr*) &server, sizeof(server)) == SOCKET_ERROR) {
throw Error("connect failed.");
}
}
if (proxy) { // testing
char port[10];
sprintf(port, ":%u", get_port(&request_url, "https", "tcp"));
std::string str = "CONNECT ";
str += request_url.data.host;
str += port;
str += " HTTP/1.0\r\n\r\n";
send_(sock, str.c_str(), str.size());
char tmp[1000];
int n = recv(sock, tmp, sizeof(tmp), 0);
int i;
for (i = 0; i < n; i++) {
char c = tmp[i];
if (c < 0x20) break;
}
if (i > 0) {
std::string s(tmp, i);
s = "proxy: " + s + '\n';
#ifdef _WIN32
OutputDebugStringA(s.c_str());
#else
fprintf(stderr, "%s", tmp);
#endif
}
}
ssl = SSL_new(sslctx());
if (!ssl) {
throw Error(get_ssl_error());
}
SSL_set_options(ssl, SSL_OP_NO_SSLv2);
SSL_set_options(ssl, SSL_OP_NO_SSLv3);
ret = SSL_set_fd(ssl, sock);
if (ret == 0) {
throw Error(get_ssl_error());
}
RAND_poll();
while (RAND_status() == 0) {
unsigned short rand_ret = rand() % 65536;
RAND_seed(&rand_ret, sizeof(rand_ret));
}
ret = SSL_connect(ssl);
if (ret != 1) {
throw Error(get_ssl_error());
}
std::string cipher = SSL_get_cipher(ssl);
cipher += '\n';
output_debug_string(cipher.c_str());
std::string version = SSL_get_cipher_version(ssl);
version += '\n';
output_debug_string(version.c_str());
X509 *x509 = SSL_get_peer_certificate(ssl);
if (x509) {
#ifndef OPENSSL_NO_SHA1
std::string fingerprint;
for (int i = 0; i < SHA_DIGEST_LENGTH; i++) {
if (i > 0) {
fingerprint += ':';
}
char tmp[10];
sprintf(tmp, "%02X", x509->sha1_hash[i]);
fingerprint += tmp;
}
fingerprint += '\n';
output_debug_string(fingerprint.c_str());
#endif
long l = SSL_get_verify_result(ssl);
if (l == X509_V_OK) {
// ok
} else {
// wrong
std::string err = X509_verify_cert_error_string(l);
err += '\n';
output_debug_string(err.c_str());
}
std::vector<std::string> vec;
auto GETSTRINGS = [](X509_NAME *x509name, std::vector<std::string> *out){
out->clear();
if (x509name) {
int n = X509_NAME_entry_count(x509name);
for (int i = 0; i < n; i++) {
X509_NAME_ENTRY *entry = X509_NAME_get_entry(x509name, i);
ASN1_STRING *asn1str = X509_NAME_ENTRY_get_data(entry);
int asn1len = ASN1_STRING_length(asn1str);
unsigned char *p = ASN1_STRING_data(asn1str);
std::string str((char const *)p, asn1len);
out->push_back(str);
}
}
};
X509_NAME *subject = X509_get_subject_name(x509);
GETSTRINGS(subject, &vec);
output_debug_string("--- subject ---\n");
output_debug_strings(vec);
X509_NAME *issuer = X509_get_issuer_name(x509);
GETSTRINGS(issuer, &vec);
output_debug_string("--- issuer ---\n");
output_debug_strings(vec);
ASN1_TIME *not_before = X509_get_notBefore(x509);
ASN1_TIME *not_after = X509_get_notAfter(x509);
(void)not_before;
(void)not_after;
X509_free(x509);
} else {
// wrong
}
}
m->last_host_name = hostname;
m->last_port = port;
set_default_header(request_url, post, opt);
std::string request = make_http_request(request_url, post, proxy, true);
auto SEND = [&](char const *ptr, int len){
while (len > 0) {
int n = SSL_write(ssl, ptr, len);
if (n < 1 || n > len) {
throw WebClient::Error(get_ssl_error());
}
ptr += n;
len -= n;
}
};
SEND(request.c_str(), (int)request.size());
if (post && !post->data.empty()) {
SEND((char const *)&post->data[0], (int)post->data.size());
}
m->crlf_state = 0;
m->content_offset = 0;
receive_(opt, [&](char *ptr, int len){
return SSL_read(ssl, ptr, len);
}, out);
m->sock = sock;
m->ssl = ssl;
if (!m->keep_alive) close();
return true;
#endif
return false;
}
void WebClient::get(URL const &url, Post const *post, Response *out, WebClientHandler *handler)
{
reset();
try {
if (!m->webcx->m) {
throw Error("WebContext is null.");
}
RequestOption opt;
opt.keep_alive = m->webcx->m->use_keep_alive;
opt.handler = handler;
std::vector<char> res;
if (url.isssl()) {
#if USE_OPENSSL
https_get(url, post, opt, &res);
#endif
} else {
http_get(url, post, opt, &res);
}
if (!res.empty()) {
char const *begin = &res[0];
char const *end = begin + res.size();
char const *ptr = begin + m->content_offset;
if (ptr < end) {
out->content.assign(ptr, end);
}
}
return;
} catch (Error const &e) {
m->error = e;
close();
}
*out = Response();
}
void WebClient::parse_header(std::vector<std::string> const *header, WebClient::Response *res)
{
if (!header->empty()) {
std::string const &line = header->at(0);
char const *begin = line.c_str();
char const *end = begin + line.size();
if (line.size() > 5 && strncmp(line.c_str(), "HTTP/", 5) == 0) {
int state = 0;
res->version.hi = res->version.lo = res->code = 0;
char const *ptr = begin + 5;
while (1) {
int c = 0;
if (ptr < end) {
c = *ptr & 0xff;
}
switch (state) {
case 0:
if (isdigit(c)) {
res->version.hi = res->version.hi * 10 + (c - '0');
} else if (c == '.') {
state = 1;
} else {
state = -1;
}
break;
case 1:
if (isdigit(c)) {
res->version.lo = res->version.lo * 10 + (c - '0');
} else if (isspace(c)) {
state = 2;
} else {
state = -1;
}
break;
case 2:
if (isspace(c)) {
if (res->code != 0) {
state = -1;
}
} else if (isdigit(c)) {
res->code = res->code * 10 + (c - '0');
} else {
state = -1;
}
break;
default:
state = -1;
break;
}
if (c == 0 || state < 0) {
break;
}
ptr++;
}
}
}
}
std::string WebClient::header_value(std::vector<std::string> const *header, std::string const &name)
{
for (size_t i = 1; i < header->size(); i++) {
std::string const &line = header->at(i);
char const *begin = line.c_str();
char const *end = begin + line.size();
char const *colon = strchr(begin, ':');
if (colon) {
if (strnicmp(begin, name.c_str(), name.size()) == 0) {
char const *ptr = colon + 1;
while (ptr < end && isspace(*ptr & 0xff)) ptr++;
return std::string(ptr, end);
}
}
}
return std::string();
}
std::string WebClient::header_value(std::string const &name) const
{
return header_value(&m->response.header, name);
}
std::string WebClient::content_type() const
{
std::string s = header_value("Content-Type");
char const *begin = s.c_str();
char const *end = begin + s.size();
char const *ptr = begin;
while (ptr < end) {
int c = *ptr & 0xff;
if (c == ';' || c < 0x21) break;
ptr++;
}
if (ptr < end) return std::string(begin, ptr);
return s;
}
size_t WebClient::content_length() const
{
return m->response.content.size();
}
char const *WebClient::content_data() const
{
if (m->response.content.empty()) return "";
return &m->response.content[0];
}
int WebClient::get(URL const &url, WebClientHandler *handler)
{
get(url, nullptr, &m->response, handler);
return m->response.code;
}
int WebClient::post(URL const &url, Post const *post, WebClientHandler *handler)
{
get(url, post, &m->response, handler);
return m->response.code;
}
void WebClient::close()
{
#if USE_OPENSSL
if (m->ssl) {
SSL_shutdown(m->ssl);
SSL_free(m->ssl);
m->ssl = nullptr;
}
#endif
if (m->sock != INVALID_SOCKET) {
shutdown(m->sock, 2); // SD_BOTH or SHUT_RDWR
closesocket(m->sock);
m->sock = INVALID_SOCKET;
}
}
void WebClient::add_header(std::string const &text)
{
m->request_header.push_back(text);
}
WebClient::Response const &WebClient::response() const
{
return m->response;
}
void WebClient::make_application_www_form_urlencoded(char const *begin, char const *end, WebClient::Post *out)
{
*out = WebClient::Post();
out->content_type = CT_APPLICATION_X_WWW_FORM_URLENCODED;
print(&out->data, begin, end - begin);
}
void WebClient::make_multipart_form_data(std::vector<Part> const &parts, WebClient::Post *out, std::string const &boundary)
{
*out = WebClient::Post();
out->content_type = CT_MULTIPART_FORM_DATA;
out->boundary = boundary;
for (Part const &part : parts) {
print(&out->data, "--");
print(&out->data, out->boundary);
print(&out->data, "\r\n");
if (!part.content_disposition.type.empty()) {
ContentDisposition const &cd = part.content_disposition;
std::string s;
s = "Content-Disposition: ";
s += cd.type;
auto Add = [&s](std::string const &name, std::string const &value){
if (!value.empty()) {
s += "; " + name + "=\"";
s += value;
s += '\"';
}
};
Add("name", cd.name);
Add("filename", cd.filename);
print(&out->data, s);
print(&out->data, "\r\n");
}
if (!part.content_type.empty()) {
print(&out->data, "Content-Type: " + part.content_type + "\r\n");
}
if (!part.content_transfer_encoding.empty()) {
print(&out->data, "Content-Transfer-Encoding: " + part.content_transfer_encoding + "\r\n");
}
print(&out->data, "\r\n");
print(&out->data, part.data, part.size);
print(&out->data, "\r\n");
}
print(&out->data, "--");
print(&out->data, out->boundary);
print(&out->data, "--\r\n");
}
void WebClient::make_multipart_form_data(char const *data, size_t size, WebClient::Post *out, std::string const &boundary)
{
Part part;
part.data = data;
part.size = size;
std::vector<Part> parts;
parts.push_back(part);
make_multipart_form_data(parts, out, boundary);
}
//
WebContext::WebContext()
: m(new Private)
{
#if USE_OPENSSL
SSL_load_error_strings();
SSL_library_init();
m->ctx = SSL_CTX_new(SSLv23_client_method());
#endif
}
WebContext::~WebContext()
{
#if USE_OPENSSL
SSL_CTX_free(m->ctx);
#endif
delete m;
}
void WebContext::set_keep_alive_enabled(bool f)
{
m->use_keep_alive = f;
}
void WebContext::set_http_proxy(const std::string &proxy)
{
m->http_proxy = WebProxy();
m->http_proxy.server = proxy;
}
void WebContext::set_https_proxy(const std::string &proxy)
{
m->https_proxy = WebProxy();
m->https_proxy.server = proxy;
}
const WebProxy *WebContext::http_proxy() const
{
if (!m->http_proxy.empty()) {
return &m->http_proxy;
}
return nullptr;
}
const WebProxy *WebContext::https_proxy() const
{
if (!m->https_proxy.empty()) {
return &m->https_proxy;
}
if (!m->http_proxy.empty()) {
return &m->http_proxy;
}
return nullptr;
}
bool WebContext::load_cacert(char const *path)
{
#if USE_OPENSSL
int r = SSL_CTX_load_verify_locations(m->ctx, path, nullptr);
return r == 1;
#else
return false;
#endif
}
diff --git a/src/webclient.h b/src/webclient.h
index 48ce76e..28d07f9 100644
--- a/src/webclient.h
+++ b/src/webclient.h
@@ -1,192 +1,184 @@
#ifndef WEBCLIENT_H_
#define WEBCLIENT_H_
#include <vector>
#include <string>
#include <functional>
#define USE_OPENSSL 1
#define OPENSSL_NO_SHA1 1
class WebContext;
class WebClient;
#define CT_APPLICATION_X_WWW_FORM_URLENCODED "application/x-www-form-urlencoded"
#define CT_MULTIPART_FORM_DATA "multipart/form-data"
class WebClientHandler {
protected:
void abort(std::string const &message = std::string());
public:
- virtual ~WebClientHandler()
- {
- }
+ virtual ~WebClientHandler() = default;
virtual void checkHeader(WebClient * /*wc*/)
{
}
virtual void checkContent(char const * /*ptr*/, size_t /*len*/)
{
}
};
class WebProxy {
public:
std::string server;
bool empty() const
{
return server.empty();
}
};
class WebClient {
public:
class URL {
friend class WebClient;
private:
struct Data {
std::string full_request;
std::string scheme;
std::string host;
int port = 0;
std::string path;
} data;
public:
- URL() {}
+ URL() = default;
URL(const std::string &addr);
std::string const &scheme() const { return data.scheme; }
std::string const &host() const { return data.host; }
int port() const { return data.port; }
std::string const &path() const { return data.path; }
bool isssl() const;
};
class Error {
private:
std::string msg_;
public:
- Error()
- {
- }
+ Error() = default;
Error(std::string const &message)
: msg_(message)
{
}
- virtual ~Error()
- {
- }
+ virtual ~Error() = default;
std::string message() const
{
return msg_;
}
};
struct Response {
int code = 0;
struct Version {
unsigned int hi = 0;
unsigned int lo = 0;
} version;
std::vector<std::string> header;
std::vector<char> content;
};
struct Post {
std::string content_type;
std::string boundary;
std::vector<char> data;
};
struct ContentDisposition {
std::string type;
std::string name;
std::string filename;
};
struct Part {
char const *data = nullptr;
size_t size = 0;
std::string content_type;
ContentDisposition content_disposition;
std::string content_transfer_encoding;
- Part()
- {
- }
+ Part() = default;
Part(char const *data, size_t size, std::string const &content_type = std::string())
: data(data)
, size(size)
, content_type(content_type)
{
}
void set_content_disposition(ContentDisposition const &cd)
{
content_disposition = cd;
}
};
struct RequestOption {
WebClientHandler *handler = nullptr;
bool keep_alive = true;
};
private:
struct Private;
Private *m;
void clear_error();
static int get_port(URL const *url, char const *scheme, char const *protocol);
void set_default_header(URL const &url, Post const *post, const RequestOption &opt);
std::string make_http_request(URL const &url, Post const *post, const WebProxy *proxy, bool https);
void parse_http_header(char const *begin, char const *end, std::vector<std::string> *header);
void parse_http_header(char const *begin, char const *end, Response *out);
bool http_get(URL const &request_url, Post const *post, RequestOption const &opt, std::vector<char> *out);
bool https_get(URL const &request_url, Post const *post, RequestOption const &opt, std::vector<char> *out);
void get(URL const &url, Post const *post, Response *out, WebClientHandler *handler);
static void parse_header(std::vector<std::string> const *header, WebClient::Response *res);
static std::string header_value(std::vector<std::string> const *header, const std::string &name);
void append(char const *ptr, size_t len, std::vector<char> *out, WebClientHandler *handler);
void on_end_header(const std::vector<char> *vec, WebClientHandler *handler);
void receive_(const RequestOption &opt, std::function<int (char *, int)>, std::vector<char> *out);
void output_debug_string(char const *str);
void output_debug_strings(const std::vector<std::string> &vec);
static void cleanup();
void reset();
public:
static void initialize();
WebClient(WebContext *webcx);
~WebClient();
WebClient(WebClient const &) = delete;
void operator = (WebClient const &) = delete;
Error const &error() const;
int get(URL const &url, WebClientHandler *handler = nullptr);
int post(URL const &url, Post const *post, WebClientHandler *handler = nullptr);
void close();
void add_header(std::string const &text);
Response const &response() const;
std::string header_value(std::string const &name) const;
std::string content_type() const;
size_t content_length() const;
char const *content_data() const;
static void make_application_www_form_urlencoded(char const *begin, char const *end, WebClient::Post *out);
static void make_multipart_form_data(const std::vector<Part> &parts, WebClient::Post *out, const std::string &boundary);
static void make_multipart_form_data(char const *data, size_t size, WebClient::Post *out, const std::string &boundary);
};
class WebContext {
friend class WebClient;
public:
private:
struct Private;
Private *m;
public:
WebContext();
~WebContext();
WebContext(WebContext const &r) = delete;
void operator = (WebContext const &r) = delete;
void set_keep_alive_enabled(bool f);
void set_http_proxy(std::string const &proxy);
void set_https_proxy(std::string const &proxy);
WebProxy const *http_proxy() const;
WebProxy const *https_proxy() const;
bool load_cacert(char const *path);
};
#endif
diff --git a/src/win32/win32.h b/src/win32/win32.h
index bc55ca8..e52dbbf 100644
--- a/src/win32/win32.h
+++ b/src/win32/win32.h
@@ -1,17 +1,21 @@
#ifndef WIN32_H
#define WIN32_H
#include <QtGlobal>
+#ifdef Q_OS_WIN
#include <QIcon>
#include <QString>
QString getModuleFileName();
QString getAppDataLocation();
int winRunCommand(QString const &cmd, QByteArray *out);
void setEnvironmentVariable(QString const &name, QString const &value);
QIcon winIconFromExtensionLarge(QString const &ext);
QIcon winIconFromExtensionSmall(QString const &ext);
+QString getWin32HttpProxy();
+
+#endif
#endif // WIN32_H

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 17, 9:05 PM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
71319
Default Alt Text
(672 KB)

Event Timeline