Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
166 KB
Referenced Files
None
Subscribers
None
diff --git a/src/AboutDialog.h b/src/AboutDialog.h
index 88922c0..8534787 100644
--- a/src/AboutDialog.h
+++ b/src/AboutDialog.h
@@ -1,26 +1,25 @@
#ifndef ABOUTDIALOG_H
#define ABOUTDIALOG_H
#include <QDialog>
namespace Ui {
class AboutDialog;
}
-class AboutDialog : public QDialog
-{
+class AboutDialog : public QDialog {
Q_OBJECT
private:
Ui::AboutDialog *ui;
QPixmap pixmap;
public:
explicit AboutDialog(QWidget *parent = nullptr);
~AboutDialog() override;
static QString appVersion();
protected:
void mouseReleaseEvent(QMouseEvent *) override;
void paintEvent(QPaintEvent *event) override;
};
#endif // ABOUTDIALOG_H
diff --git a/src/AbstractProcess.h b/src/AbstractProcess.h
index f5fecd6..01d193a 100644
--- a/src/AbstractProcess.h
+++ b/src/AbstractProcess.h
@@ -1,31 +1,31 @@
#ifndef ABSTRACTPROCESS_H
#define ABSTRACTPROCESS_H
#include <QObject>
#include <QVariant>
#include <functional>
class QString;
class AbstractPtyProcess : public QObject {
Q_OBJECT
protected:
QString change_dir;
QVariant user_data;
public:
void setChangeDir(QString const &dir);
- void setVariant(QVariant const &userdata);
+ void setVariant(QVariant const &value);
QVariant const &userVariant() const;
virtual bool isRunning() const = 0;
virtual void writeInput(char const *ptr, int len) = 0;
virtual int readOutput(char *ptr, int len) = 0;
virtual void start(QString const &cmd, QVariant const &userdata = QVariant()) = 0;
virtual bool wait(unsigned long time = ULONG_MAX) = 0;
virtual void stop() = 0;
virtual int getExitCode() const = 0;
virtual QString getMessage() const = 0;
signals:
void completed(bool, QVariant);
};
#endif // ABSTRACTPROCESS_H
diff --git a/src/BasicMainWindow.h b/src/BasicMainWindow.h
index 8969ca4..8853057 100644
--- a/src/BasicMainWindow.h
+++ b/src/BasicMainWindow.h
@@ -1,383 +1,383 @@
#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)
{
}
};
enum {
IndexRole = Qt::UserRole,
FilePathRole,
DiffIndexRole,
HunkIndexRole,
HeaderRole,
};
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 {
GroupItem = -1,
};
struct Private;
Private *m;
private:
static bool git_callback(void *cookie, const char *ptr, int len);
QString selectCommand_(QString const &cmdname, QStringList const &cmdfiles, QStringList const &list, QString path, std::function<void (QString const &)> const &callback);
QString selectCommand_(QString const &cmdname, QString const &cmdfile, QStringList const &list, QString const &path, std::function<void (QString const &)> const &callback);
bool checkGitCommand();
bool checkFileCommand();
bool checkExecutable(QString const &path);
void internalSetCommand(QString const &path, bool save, QString const &name, QString *out);
QStringList whichCommand_(QString const &cmdfile1, QString const &cmdfile2 = QString());
void setWindowTitle_(Git::User const &user);
bool execSetGlobalUserDialog();
bool saveByteArrayAs(const QByteArray &ba, QString const &dstpath);
bool saveFileAs(QString const &srcpath, QString const &dstpath);
bool saveBlobAs(QString const &id, QString const &dstpath);
void updateFilesList(Git::CommitItem const &commit, bool wait);
void revertAllFiles();
void setCurrentRemoteName(QString const &name);
void deleteTags(Git::CommitItem const &commit);
bool isAvatarEnabled() const;
bool isGitHub() const;
QStringList remotes() const;
QList<Git::Branch> findBranch(QString const &id);
QString saveAsTemp(QString const &id);
QString tempfileHeader() const;
void deleteTempFiles();
QString getCommitIdFromTag(QString const &tag);
bool isValidRemoteURL(QString const &url);
QString getObjectID(QListWidgetItem *item);
void addWorkingCopyDir(QString dir, QString name, bool open);
static QString makeRepositoryName(QString const &loc);
void clearAuthentication();
RepositoryItem const *findRegisteredRepository(QString *workdir) const;
void queryRemotes(const GitPtr &g);
bool runOnRepositoryDir(std::function<void (QString)> const &callback, RepositoryItem const *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();
bool saveRepositoryBookmarks() const;
QString getBookmarksFilePath() const;
void stopPtyProcess();
void abortPtyProcess();
bool execWelcomeWizardDialog();
void execRepositoryPropertyDialog(QString workdir, bool open_repository_menu = false);
void execSetUserDialog(Git::User const &global_user, Git::User const &repo_user, QString const &reponame);
void setGitCommand(QString const &path, bool save);
void setFileCommand(QString const &path, bool save);
void setGpgCommand(QString const &path, bool save);
void logGitVersion();
void setUnknownRepositoryInfo();
void internalClearRepositoryInfo();
void checkUser();
void openRepository(bool validate, bool waitcursor = true, bool keep_selection = false);
void updateRepository();
void reopenRepository(bool log, const std::function<void(GitPtr const &)> &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 (QString const &, QString, int)> &add_item);
Git::CommitItemList retrieveCommitLog(const GitPtr &g);
void queryBranches(const GitPtr &g);
std::map<QString, QList<Git::Branch>> &branchMapRef();
void updateCommitTableLater();
void updateRemoteInfo();
void updateWindowTitle(const GitPtr &g);
QString makeCommitInfoText(int row, QList<Label> *label_list);
void removeRepositoryFromBookmark(int index, bool ask);
void clone(QString url = QString(), QString dir = QString());
void checkout();
void commit(bool amend = false);
void commitAmend();
void pushSetUpstream(QString const &remote, QString const &branch);
bool pushSetUpstream(bool testonly);
void push();
void openTerminal(RepositoryItem const *repo);
void openExplorer(RepositoryItem const *repo);
Git::CommitItem const *selectedCommitItem() const;
void deleteBranch(Git::CommitItem const *commit);
void deleteBranch();
bool askAreYouSureYouWantToRun(QString const &title, QString const &command);
bool editFile(QString const &path, QString const &title);
void resetFile(QStringList const &paths);
void saveRepositoryBookmark(RepositoryItem item);
void setCurrentRepository(RepositoryItem const &repo, bool clear_authentication);
void internalDeleteTags(QStringList const &tagnames);
bool internalAddTag(QString const &name);
NamedCommitList namedCommitItems(int flags);
int rowFromCommitId(QString const &id);
void createRepository(QString const &dir);
void setLogEnabled(const GitPtr &g, bool f);
QList<Git::Tag> findTag(QString const &id);
void sshSetPassphrase(std::string const &user, std::string const &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;
Git::CommitItem const *getLog(int index) const;
protected:
bool event(QEvent *event) override;
void postUserFunctionEvent(const std::function<void(QVariant const &)> &fn, QVariant const &v = QVariant());
void doGitCommand(std::function<void(GitPtr)> const &callback);
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 setPtyUserData(QVariant const &userdata);
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(QString const &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(QString const &head_id);
void setPtyProcessCompletionData(QVariant const &value);
QVariant const &getTempRepoForCloneCompleteV() const;
void updateCommitGraph();
bool fetch(const GitPtr &g, bool prune);
protected:
virtual void setCurrentLogRow(int row) = 0;
virtual void updateFilesList(QString id, bool wait) = 0;
virtual void setRepositoryInfo(QString const &reponame, QString const &brname) = 0;
virtual void deleteTags(QStringList const &tagnames) = 0;
virtual void internalWriteLog(const char *ptr, int len) = 0;
virtual void removeSelectedRepositoryFromBookmark(bool ask) = 0;
virtual void openRepository_(GitPtr g, bool keep_selection = false) = 0;
virtual void updateRepositoriesList() = 0;
virtual void clearFileList() = 0;
virtual RepositoryItem const *selectedRepositoryItem() const = 0;
- virtual void setRemoteMonitoringEnabled(bool enable) { (void)enable; };
- virtual void updateStatusBarText() {};
+ virtual void setRemoteMonitoringEnabled(bool enable) { (void)enable; }
+ virtual void updateStatusBarText() {}
void msgNoRepositorySelected();
bool isRepositoryOpened() const;
public:
explicit BasicMainWindow(QWidget *parent = nullptr);
- ~BasicMainWindow();
+ ~BasicMainWindow() override;
ApplicationSettings *appsettings();
const ApplicationSettings *appsettings() const;
WebContext *webContext();
QString gitCommand() const;
void autoOpenRepository(QString dir);
GitPtr git(QString const &dir) const;
GitPtr git();
QPixmap getTransparentPixmap();
QIcon committerIcon(int row) const;
Git::CommitItem const *commitItem(int row) const;
QIcon verifiedIcon(char s) const;
virtual QString currentWorkingCopyDir() const;
QList<BasicMainWindow::Label> const *label(int row);
bool saveAs(QString const &id, QString const &dstpath);
bool testRemoteRepositoryValidity(QString const &url);
QString defaultWorkingDir() const;
void addWorkingCopyDir(const QString &dir, bool open);
bool queryCommit(QString const &id, Git::CommitItem *out);
QAction *addMenuActionProperty(QMenu *menu);
void checkout(QWidget *parent, Git::CommitItem const *commit);
void jumpToCommit(QString id);
void execCommitViewWindow(Git::CommitItem const *commit);
void execCommitExploreWindow(QWidget *parent, Git::CommitItem const *commit);
void execCommitPropertyDialog(QWidget *parent, Git::CommitItem const *commit);
void execFileHistory(QString const &path);
void execFileHistory(QListWidgetItem *item);
void execFilePropertyDialog(QListWidgetItem *item);
QString selectGitCommand(bool save);
QString selectFileCommand(bool save);
QString selectGpgCommand(bool save);
Git::Branch const &currentBranch() const;
void setCurrentBranch(Git::Branch const &b);
const RepositoryItem &currentRepository() const;
QString currentRepositoryName() const;
QString currentRemoteName() const;
QString currentBranchName() const;
bool isValidWorkingCopy(const GitPtr &g) const;
QString determinFileType_(QString const &path, bool mime, std::function<void (QString const &, QByteArray *)> const &callback) const;
QString determinFileType(QString const &path, bool mime);
QString determinFileType(QByteArray in, bool mime);
QList<Git::Tag> queryTagList();
int limitLogCount() const;
TextEditorThemePtr themeForTextEditor();
Git::Object cat_file_(const GitPtr &g, QString const &id);
Git::Object cat_file(QString const &id);
QString newTempFilePath();
QString findFileID(QString const &commit_id, QString const &file);
void updateFilesList(QString const &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;
virtual bool isOnlineMode() const = 0;
virtual int selectedLogIndex() const = 0;
static QString abbrevCommitID(Git::CommitItem const &commit);
protected slots:
void onAvatarUpdated();
public:
QStringList findGitObject(const QString &id) const;
void writeLog(const char *ptr, int len);
void writeLog(QString const &str);
void emitWriteLog(const QByteArray &ba);
public slots:
void writeLog_(QByteArray ba);
signals:
void signalWriteLog(QByteArray ba);
void remoteInfoChanged();
// void signalCheckRemoteUpdate();
};
#endif // BASICMAINWINDOW_H
diff --git a/src/BlameWindow.h b/src/BlameWindow.h
index 9d22930..a53c543 100644
--- a/src/BlameWindow.h
+++ b/src/BlameWindow.h
@@ -1,49 +1,46 @@
#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 {
Q_OBJECT
private:
struct Private;
Private *m;
public:
explicit BlameWindow(MainWindow *parent, QString const &filename, QList<BlameItem> const &list);
~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);
-
+ void on_tableWidget_customContextMenuRequested(const QPoint &pos);
+ void on_tableWidget_itemDoubleClicked(QTableWidgetItem *item);
private:
Ui::BlameWindow *ui;
MainWindow *mainwindow();
// QObject interface
QString getCommitId(QTableWidgetItem *item) const;
QString currentCommitId() const;
};
#endif // BLAMEWINDOW_H
diff --git a/src/CloneDialog.h b/src/CloneDialog.h
index 42d553d..d4f79d6 100644
--- a/src/CloneDialog.h
+++ b/src/CloneDialog.h
@@ -1,45 +1,45 @@
#ifndef CLONEDIALOG_H
#define CLONEDIALOG_H
#include <QDialog>
#include <QThread>
#include "Git.h"
namespace Ui {
class CloneDialog;
}
class BasicMainWindow;
class CloneDialog : public QDialog {
Q_OBJECT
private:
struct Private;
Private *m;
using GitPtr = std::shared_ptr<Git>;
public:
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;
BasicMainWindow *mainwindow();
private slots:
- void on_lineEdit_repo_location_textChanged(QString const &arg1);
+ void on_lineEdit_repo_location_textChanged(QString const &text);
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/CloneFromGitHubDialog.h b/src/CloneFromGitHubDialog.h
index bdd70ad..1deff33 100644
--- a/src/CloneFromGitHubDialog.h
+++ b/src/CloneFromGitHubDialog.h
@@ -1,27 +1,27 @@
#ifndef CLONEFROMGITHUBDIALOG_H
#define CLONEFROMGITHUBDIALOG_H
#include <QDialog>
namespace Ui {
class CloneFromGitHubDialog;
}
class CloneFromGitHubDialog : public QDialog
{
Q_OBJECT
private:
Ui::CloneFromGitHubDialog *ui;
QString url_;
void updateUI();
public:
explicit CloneFromGitHubDialog(QWidget *parent, QString const &username, QString const &reponame);
- ~CloneFromGitHubDialog();
+ ~CloneFromGitHubDialog() override;
QString url() const;
private slots:
void onHyperlinkClicked();
void on_radioButton_ssh_clicked();
void on_radioButton_http_clicked();
};
#endif // CLONEFROMGITHUBDIALOG_H
diff --git a/src/ConfigSigningDialog.cpp b/src/ConfigSigningDialog.cpp
index 4886c03..6f005ae 100644
--- a/src/ConfigSigningDialog.cpp
+++ b/src/ConfigSigningDialog.cpp
@@ -1,76 +1,76 @@
#include "ConfigSigningDialog.h"
#include "BasicMainWindow.h"
#include "ui_ConfigSigningDialog.h"
-ConfigSigningDialog::ConfigSigningDialog(QWidget *parent, BasicMainWindow *mw, bool local_enable) :
- QDialog(parent),
- ui(new Ui::ConfigSigningDialog)
+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;
}
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/DialogHeaderFrame.h b/src/DialogHeaderFrame.h
index 19a56a5..24c5d78 100644
--- a/src/DialogHeaderFrame.h
+++ b/src/DialogHeaderFrame.h
@@ -1,14 +1,14 @@
#ifndef DIALOGHEADERFRAME_H
#define DIALOGHEADERFRAME_H
#include <QFrame>
class DialogHeaderFrame : public QFrame {
Q_OBJECT
public:
explicit DialogHeaderFrame(QWidget *parent = nullptr);
protected:
- void paintEvent(QPaintEvent *);
+ void paintEvent(QPaintEvent *) override;
};
#endif // DIALOGHEADERFRAME_H
diff --git a/src/FileDiffSliderWidget.h b/src/FileDiffSliderWidget.h
index a4ae11e..42b9528 100644
--- a/src/FileDiffSliderWidget.h
+++ b/src/FileDiffSliderWidget.h
@@ -1,51 +1,51 @@
#ifndef FILEDIFFSLIDERWIDGET_H
#define FILEDIFFSLIDERWIDGET_H
#include "AbstractCharacterBasedApplication.h"
#include "MainWindow.h"
#include "Theme.h"
#include <QPixmap>
#include <QWidget>
#include <functional>
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;
+using fn_pixmap_maker_t = std::function<QPixmap (DiffPane, int, int)>;
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 = nullptr);
~FileDiffSliderWidget() override;
void clear(bool v);
void setScrollPos(int total, int value, int size);
void init(fn_pixmap_maker_t const &pixmap_maker, const ThemePtr &theme);
void updatePixmap();
static QPixmap makeDiffPixmap(int width, int height, TextDiffLineList const &lines, ThemePtr theme);
protected:
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/FileViewWidget.h b/src/FileViewWidget.h
index e8e6697..1dc1a39 100644
--- a/src/FileViewWidget.h
+++ b/src/FileViewWidget.h
@@ -1,77 +1,74 @@
#ifndef FILEVIEWWIDGET_H
#define FILEVIEWWIDGET_H
#include <QWidget>
#include "texteditor/TextEditorWidget.h"
class QScrollBar;
struct PreEditText;
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()
- {
- }
+ explicit FileViewWidget(QWidget *parent = nullptr);
void setTextCodec(QTextCodec *codec);
void setViewType(FileViewType type);
void setImage(const QString &mimetype, const QByteArray &ba, QString const &object_id, const QString &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(const 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(QMainWindow *mw, FileDiffWidget *fdw, QScrollBar *vsb, QScrollBar *hsb, const TextEditorThemePtr &theme);
};
#endif // FILEVIEWWIDGET_H
diff --git a/src/FindCommitDialog.h b/src/FindCommitDialog.h
index f465290..ec1e160 100644
--- a/src/FindCommitDialog.h
+++ b/src/FindCommitDialog.h
@@ -1,22 +1,22 @@
#ifndef FINDCOMMITDIALOG_H
#define FINDCOMMITDIALOG_H
#include <QDialog>
namespace Ui {
class FindCommitDialog;
}
class FindCommitDialog : public QDialog {
Q_OBJECT
public:
explicit FindCommitDialog(QWidget *parent, const QString &text);
- ~FindCommitDialog();
+ ~FindCommitDialog() override;
QString text() const;
private:
Ui::FindCommitDialog *ui;
};
#endif // FINDCOMMITDIALOG_H
diff --git a/src/Git.h b/src/Git.h
index 20cde67..7e68869 100644
--- a/src/Git.h
+++ b/src/Git.h
@@ -1,485 +1,485 @@
#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;
};
using NamedCommitList = QList<NamedCommitItem>;
class Git;
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() = 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;
bool strange_date = false;
};
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() = 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;
}
};
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_s_();
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;
~Git() override;
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_s();
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 = nullptr);
void fetch(AbstractPtyProcess *pty = nullptr, bool prune = false);
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);
+ static bool isValidID(QString const &id);
QString status();
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 = 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();
bool reset_hard();
bool clean_df();
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 message;
struct File {
QString atts_a;
QString atts_b;
QString id_a;
QString id_b;
QString type;
QString path;
};
QList<File> files;
};
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);
struct RemoteInfo {
QString commit_id;
QString name;
};
QList<RemoteInfo> ls_remote();
bool stash();
bool stash_apply();
bool stash_drop();
};
void parseDiff(std::string const &s, Git::Diff const *info, Git::Diff *out);
#endif // GIT_H
diff --git a/src/GitPackIdxV2.h b/src/GitPackIdxV2.h
index a99eaee..75f62ec 100644
--- a/src/GitPackIdxV2.h
+++ b/src/GitPackIdxV2.h
@@ -1,68 +1,68 @@
#ifndef GITPACKIDXV2_H
#define GITPACKIDXV2_H
#include <QIODevice>
#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);
+ bool parse(QString const &idxfile);
GitPackIdxItem const *item(QString const &id) const;
GitPackIdxItem const *item(size_t offset) const;
void each(std::function<bool(GitPackIdxItem const *)> const &fn) const;
};
using GitPackIdxPtr = std::shared_ptr<GitPackIdxV2>;
#endif // GITPACKIDXV2_H
diff --git a/src/LogTableWidget.h b/src/LogTableWidget.h
index dd59cdb..45b1435 100644
--- a/src/LogTableWidget.h
+++ b/src/LogTableWidget.h
@@ -1,26 +1,26 @@
#ifndef LOGTABLEWIDGET_H
#define LOGTABLEWIDGET_H
#include <QTableWidget>
class MainWindow;
class LogTableWidgetDelegate;
class LogTableWidget : public QTableWidget {
Q_OBJECT
friend class LogTableWidgetDelegate;
private:
struct Private;
Private *m;
MainWindow *mainwindow();
public:
explicit LogTableWidget(QWidget *parent = nullptr);
~LogTableWidget() override;
protected:
void paintEvent(QPaintEvent *) override;
- void resizeEvent(QResizeEvent *e);
+ void resizeEvent(QResizeEvent *e) override;
protected slots:
- void verticalScrollbarValueChanged(int value);
+ void verticalScrollbarValueChanged(int value) override;
};
#endif // LOGTABLEWIDGET_H
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 685226c..55fd6fd 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -1,2785 +1,2785 @@
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "AboutDialog.h"
#include "ApplicationGlobal.h"
#include "AreYouSureYouWantToContinueConnectingDialog.h"
#include "AvatarLoader.h"
#include "BlameWindow.h"
#include "CloneFromGitHubDialog.h"
#include "CommitPropertyDialog.h"
#include "DeleteBranchDialog.h"
#include "EditGitIgnoreDialog.h"
#include "EditTagsDialog.h"
#include "FileDiffWidget.h"
#include "FindCommitDialog.h"
#include "GitDiff.h"
#include "JumpDialog.h"
#include "LineEditDialog.h"
#include "MySettings.h"
#include "ObjectBrowserDialog.h"
#include "ReflogWindow.h"
#include "RemoteWatcher.h"
#include "SetGpgSigningDialog.h"
#include "SettingsDialog.h"
#include "StatusLabel.h"
#include "TextEditDialog.h"
#include "common/joinpath.h"
#include "common/misc.h"
#include "platform.h"
#include "webclient.h"
#include <QClipboard>
#include <QDirIterator>
#include <QFileDialog>
#include <QFileIconProvider>
#include <QMessageBox>
#include <QMimeData>
#include <QPainter>
#include <QStandardPaths>
#include <QTimer>
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);
}
struct MainWindow::Private {
bool is_online_mode = true;
QTimer interval_10ms_timer;
QImage graph_color;
QPixmap digits;
StatusLabel *status_bar_label;
QObject *last_focused_file_list = nullptr;
QListWidgetItem *last_selected_file_item = nullptr;
bool searching = false;
QString search_text;
RemoteWatcher remote_watcher;
int repos_panel_width = 0;
std::set<QString> ancestors;
};
MainWindow::MainWindow(QWidget *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
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();
initNetworking();
showFileList(FilesListType::SingleList);
m->digits.load(":/image/digits.png");
m->graph_color = global->theme->graphColorMap();
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, &BasicMainWindow::signalWriteLog, this, &BasicMainWindow::writeLog_);
connect(ui->dockWidget_log, &QDockWidget::visibilityChanged, this, &MainWindow::onLogVisibilityChanged);
connect(ui->widget_log, &TextEditorWidget::idle, this, &MainWindow::onLogIdle);
connect(ui->treeWidget_repos, &RepositoriesTreeWidget::dropped, this, &MainWindow::onRepositoriesTreeDropped);
connect((AbstractPtyProcess *)getPtyProcess(), &AbstractPtyProcess::completed, this, &MainWindow::onPtyProcessCompleted);
connect(this, &BasicMainWindow::remoteInfoChanged, [&](){
ui->lineEdit_remote->setText(currentRemoteName());
});
connect(this, &MainWindow::signalSetRemoteChanged, [&](bool f){
setRemoteChanged(f);
updateButton();
});
//
QString path = getBookmarksFilePath();
*getReposPtr() = RepositoryBookmark::load(path);
updateRepositoriesList();
webContext()->set_keep_alive_enabled(true);
getAvatarLoader()->start(this);
connect(getAvatarLoader(), &AvatarLoader::updated, this, &MainWindow::onAvatarUpdated);
*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());
settings.endGroup();
if (maximized) {
state |= Qt::WindowMaximized;
setWindowState(state);
}
}
startTimers();
}
MainWindow::~MainWindow()
{
stopPtyProcess();
getAvatarLoader()->stop();
m->remote_watcher.quit();
m->remote_watcher.wait();
delete m;
delete ui;
}
void MainWindow::notifyRemoteChanged(bool f)
{
postUserFunctionEvent([&](QVariant const &v){
setRemoteChanged(v.toBool());
updateButton();
}, QVariant(f));
}
bool MainWindow::isUninitialized()
{
return !misc::isExecutable(appsettings()->git_command) || !misc::isExecutable(appsettings()->file_command);
}
bool MainWindow::shown()
{
while (isUninitialized()) {
if (!execWelcomeWizardDialog()) {
return false;
}
}
m->repos_panel_width = ui->stackedWidget_leftpanel->width();
ui->stackedWidget_leftpanel->setCurrentWidget(ui->page_repos);
ui->action_repositories_panel->setChecked(true);
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 settings;
{
settings.beginGroup("Remote");
bool f = settings.value("Online", true).toBool();
settings.endGroup();
setRemoteOnline(f, false, false);
}
{
settings.beginGroup("MainWindow");
int n = settings.value("FirstColumnWidth", 50).toInt();
if (n < 10) n = 50;
ui->tableWidget_log->setColumnWidth(0, n);
settings.endGroup();
}
}
updateUI();
setUnknownRepositoryInfo();
checkUser();
return true;
}
void MainWindow::startTimers()
{
// interval 10ms
connect(&m->interval_10ms_timer, &QTimer::timeout, [&](){
const int ms = 10;
auto *p1 = ptrUpdateCommitTableCounter();
if (*p1 > 0) {
if (*p1 > ms) {
*p1 -= ms;
} else {
*p1 = 0;
ui->tableWidget_log->viewport()->update();
}
}
auto *p2 = ptrUpdateFilesListCounter();
if (*p2 > 0) {
if (*p2 > ms) {
*p2 -= ms;
} else {
*p2 = 0;
updateCurrentFilesList();
}
}
});
m->interval_10ms_timer.setInterval(10);
m->interval_10ms_timer.start();
startTimer(10);
}
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 BasicMainWindow::event(event);
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
QEvent::Type et = event->type();
if (et == QEvent::KeyPress) {
if (QApplication::activeModalWidget()) {
// thru
} else {
auto *e = dynamic_cast<QKeyEvent *>(event);
Q_ASSERT(e);
int k = e->key();
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) {
auto SelectItem = [](QListWidget *w){
int row = w->currentRow();
if (row < 0) {
row = 0;
w->setCurrentRow(row);
}
w->setItemSelected(w->item(row), true);
w->viewport()->update();
};
// ファイルリストがフォーカスを得たとき、diffビューを更新する。(コンテキストメニュー対応)
if (watched == ui->listWidget_unstaged) {
m->last_focused_file_list = watched;
updateStatusBarText();
updateUnstagedFileCurrentItem();
SelectItem(ui->listWidget_unstaged);
return true;
}
if (watched == ui->listWidget_staged) {
m->last_focused_file_list = watched;
updateStatusBarText();
updateStagedFileCurrentItem();
SelectItem(ui->listWidget_staged);
return true;
}
if (watched == ui->listWidget_files) {
m->last_focused_file_list = watched;
SelectItem(ui->listWidget_files);
return true;
}
}
return false;
}
void MainWindow::closeEvent(QCloseEvent *event)
{
MySettings settings;
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);
}
{
settings.beginGroup("MainWindow");
settings.setValue("Maximized", maximized);
settings.setValue("Geometry", saveGeometry());
settings.endGroup();
}
}
{
settings.beginGroup("MainWindow");
settings.setValue("FirstColumnWidth", ui->tableWidget_log->columnWidth(0));
settings.endGroup();
}
QMainWindow::closeEvent(event);
}
void MainWindow::setStatusBarText(QString const &text)
{
m->status_bar_label->setText(text);
}
void MainWindow::clearStatusBarText()
{
setStatusBarText(QString());
}
void MainWindow::onLogVisibilityChanged()
{
ui->action_window_log->setChecked(ui->dockWidget_log->isVisible());
}
void MainWindow::internalWriteLog(char const *ptr, int len)
{
ui->widget_log->logicalMoveToBottom();
ui->widget_log->write(ptr, len, false);
ui->widget_log->setChanged(false);
setInteractionCanceled(false);
}
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);
}
*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 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;
}
QString MainWindow::currentWorkingCopyDir() const
{
QString workdir = BasicMainWindow::currentWorkingCopyDir();
if (workdir.isEmpty()) {
RepositoryItem const *repo = selectedRepositoryItem();
if (repo) {
workdir = repo->local_dir;
return workdir;
}
}
return workdir;
}
RepositoryItem const *BasicMainWindow::findRegisteredRepository(QString *workdir) const
{
*workdir = QDir(*workdir).absolutePath();
workdir->replace('\\', '/');
if (Git::isValidWorkingCopy(*workdir)) {
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 < getRepos().size()) {
return i;
}
}
return -1;
}
RepositoryItem const *MainWindow::repositoryItem(QTreeWidgetItem const *item) const
{
int row = repositoryIndex_(item);
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, getFolderIcon());
item->setFlags(item->flags() | Qt::ItemIsEditable);
return item;
}
void MainWindow::updateRepositoriesList()
{
QString path = getBookmarksFilePath();
auto *repos = getReposPtr();
*repos = RepositoryBookmark::load(path);
QString filter = getRepositoryFilterText();
ui->treeWidget_repos->clear();
std::map<QString, QTreeWidgetItem *> parentmap;
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, 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_filelist->setCurrentWidget(ui->page_files);
break;
case FilesListType::SideBySide:
ui->stackedWidget_filelist->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()
{
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::updateFilesList(QString id, bool wait)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
if (!wait) return;
clearFileList();
Git::FileStatusList stats = g->status_s();
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(filename);
item->setSizeHint(QSize(item->sizeHint().width(), 18));
item->setData(FilePathRole, filename);
item->setData(DiffIndexRole, idiff);
item->setData(HunkIndexRole, -1);
item->setData(HeaderRole, header);
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, diffResult())) {
return;
}
std::map<QString, int> diffmap;
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, diffResult())) {
return;
}
showFileList(files_list_type);
addDiffItems(diffResult(), AddItem);
}
for (Git::Diff const &diff : *diffResult()) {
QString key = GitDiff::makeKey(diff);
(*getDiffCacheMap())[key] = diff;
}
}
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 count = (int)logs.size();
if (row < count) {
updateFilesList(logs[row], true);
}
}
void MainWindow::prepareLogTableWidget()
{
QStringList cols = {
tr("Graph"),
tr("Commit"),
tr("Date"),
tr("Author"),
tr("Message"),
};
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::detectGitServerType(GitPtr const &g)
{
setServerType(ServerType::Standard);
*ptrGitHub() = 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;
}
}
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);
p->owner_account_name = user;
p->repository_name = repo;
}
setServerType(ServerType::GitHub);
}
}
void MainWindow::clearLog()
{
clearLogs();
clearLabelMap();
setUncommitedChanges(false);
ui->tableWidget_log->clearContents();
ui->tableWidget_log->scrollToTop();
}
void MainWindow::openRepository_(GitPtr g, bool keep_selection)
{
getObjCache()->setup(g);
int scroll_pos = -1;
int select_row = -1;
if (keep_selection) {
scroll_pos = ui->tableWidget_log->verticalScrollBar()->value();
select_row = ui->tableWidget_log->currentRow();
}
if (isValidWorkingCopy(g)) {
bool do_fetch = isOnlineMode() && (getForceFetch() || appsettings()->automatically_fetch_when_opening_the_repository);
setForceFetch(false);
if (do_fetch) {
if (!fetch(g, false)) {
return;
}
}
clearLog();
clearRepositoryInfo();
detectGitServerType(g);
updateFilesList(QString(), true);
bool canceled = false;
ui->tableWidget_log->setEnabled(false);
// ログを取得
setLogs(retrieveCommitLog(g));
// ブランチを取得
queryBranches(g);
// タグを取得
ptrTagMap()->clear();
QList<Git::Tag> tags = g->tags();
for (Git::Tag const &tag : tags) {
Git::Tag t = tag;
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);
setHeadId(getObjCache()->revParse("HEAD"));
if (isThereUncommitedChanges()) {
Git::CommitItem item;
item.parent_ids.push_back(currentBranch().id);
item.message = tr("Uncommited changes");
auto p = getLogsPtr();
p->insert(p->begin(), item);
}
prepareLogTableWidget();
auto const &logs = getLogs();
const int count = logs.size();
ui->tableWidget_log->setRowCount(count);
int selrow = 0;
for (int row = 0; row < count; 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 == 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, &(*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);
}
int t = ui->tableWidget_log->columnWidth(0);
ui->tableWidget_log->resizeColumnsToContents();
ui->tableWidget_log->setColumnWidth(0, t);
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(false);
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(true);
m->last_focused_file_list = nullptr;
ui->tableWidget_log->setFocus();
if (select_row < 0) {
setCurrentLogRow(selrow);
} else {
setCurrentLogRow(select_row);
ui->tableWidget_log->verticalScrollBar()->setValue(scroll_pos >= 0 ? scroll_pos : 0);
}
m->remote_watcher.setCurrent(currentRemoteName(), currentBranchName());
updateUI();
}
void MainWindow::removeSelectedRepositoryFromBookmark(bool ask)
{
int i = indexOfRepository(ui->treeWidget_repos->currentItem());
removeRepositoryFromBookmark(i, ask);
}
void MainWindow::setNetworkingCommandsEnabled(bool enabled)
{
ui->action_clone->setEnabled(enabled);
ui->toolButton_clone->setEnabled(enabled);
if (!Git::isValidWorkingCopy(currentWorkingCopyDir())) {
enabled = false;
}
bool opened = !currentRepository().name.isEmpty();
ui->action_fetch->setEnabled(enabled || opened);
ui->toolButton_fetch->setEnabled(enabled || opened);
if (isOnlineMode()) {
ui->action_fetch->setText(tr("Fetch"));
ui->toolButton_fetch->setText(tr("Fetch"));
} else {
ui->action_fetch->setText(tr("Update"));
ui->toolButton_fetch->setText(tr("Update"));
}
ui->action_fetch_prune->setEnabled(enabled);
ui->action_pull->setEnabled(enabled);
ui->action_push->setEnabled(enabled);
ui->action_push_u->setEnabled(enabled);
ui->action_push_all_tags->setEnabled(enabled);
ui->toolButton_pull->setEnabled(enabled);
ui->toolButton_push->setEnabled(enabled);
}
void MainWindow::updateUI()
{
setNetworkingCommandsEnabled(isOnlineMode());
ui->toolButton_fetch->setDot(getRemoteChanged());
Git::Branch b = currentBranch();
ui->toolButton_push->setNumber(b.ahead > 0 ? b.ahead : -1);
ui->toolButton_pull->setNumber(b.behind > 0 ? b.behind : -1);
{
bool f = isRepositoryOpened();
ui->toolButton_status->setEnabled(f);
ui->toolButton_terminal->setEnabled(f);
ui->toolButton_explorer->setEnabled(f);
ui->action_repository_status->setEnabled(f);
ui->action_terminal->setEnabled(f);
ui->action_explorer->setEnabled(f);
}
}
void MainWindow::updateStatusBarText()
{
QString text;
QWidget *w = qApp->focusWidget();
if (w == ui->treeWidget_repos) {
RepositoryItem const *repo = selectedRepositoryItem();
if (repo) {
text = QString("%1 : %2")
.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)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::on_action_commit_triggered()
{
commit();
}
void MainWindow::on_action_fetch_triggered()
{
if (isOnlineMode()) {
reopenRepository(true, [&](GitPtr g){
fetch(g, false);
});
} else {
updateRepository();
}
}
void MainWindow::on_action_fetch_prune_triggered()
{
if (!isOnlineMode()) return;
reopenRepository(true, [&](GitPtr g){
fetch(g, true);
});
}
void MainWindow::on_action_push_triggered()
{
push();
}
void MainWindow::on_action_pull_triggered()
{
if (!isOnlineMode()) return;
reopenRepository(true, [&](GitPtr g){
setPtyCondition(PtyCondition::Pull);
setPtyProcessOk(true);
g->pull(getPtyProcess());
while (1) {
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::showStatus()
{
auto g = git();
if (!g->isValidWorkingCopy()) {
msgNoRepositorySelected();
return;
}
QString s = g->status();
TextEditDialog dlg(this);
dlg.setWindowTitle(tr("Status"));
dlg.setText(s, true);
dlg.exec();
}
void MainWindow::on_toolButton_status_clicked()
{
showStatus();
}
void MainWindow::on_action_repository_status_triggered()
{
showStatus();
}
void MainWindow::on_treeWidget_repos_currentItemChanged(QTreeWidgetItem * /*current*/, QTreeWidgetItem * /*previous*/)
{
updateStatusBarText();
}
void MainWindow::on_treeWidget_repos_itemDoubleClicked(QTreeWidgetItem * /*item*/, int /*column*/)
{
openSelectedRepository();
}
void BasicMainWindow::execCommitPropertyDialog(QWidget *parent, Git::CommitItem const *commit)
{
CommitPropertyDialog dlg(parent, this, commit);
dlg.exec();
}
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
a_open_terminal->setIcon(QIcon(":/image/terminal.svg"));
QAction *a_open_folder = menu.addAction(tr("Open &folder"));
a_open_folder->setIcon(QIcon(":/image/explorer.svg"));
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::on_tableWidget_log_customContextMenuRequested(const QPoint &pos)
{
int row = selectedLogIndex();
Git::CommitItem const *commit = commitItem(row);
if (commit) {
bool is_valid_commit_id = Git::isValidID(commit->commit_id);
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 = menu.addAction(tr("Checkout/Branch..."));
menu.addSeparator();
QAction *a_edit_message = nullptr;
auto canEditMessage = [&](){
if (commit->has_child) return false; // 子がないこと
if (Git::isUncommited(*commit)) return false; // 未コミットがないこと
bool is_head = false;
bool has_remote_branch = false;
QList<Label> const *labels = label(row);
for (const Label &label : *labels) {
if (label.kind == Label::Head) {
is_head = true;
} else if (label.kind == Label::RemoteBranch) {
has_remote_branch = true;
}
}
return is_head && !has_remote_branch; // HEAD && リモートブランチ無し
};
if (canEditMessage()) {
a_edit_message = menu.addAction(tr("Edit message..."));
}
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 == 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, nullptr).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_message) {
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);
}
}
}
}
}
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();
}
void MainWindow::for_each_selected_files(std::function<void(QString const&)> const &fn)
{
for (QString const &path : selectedFiles()) {
fn(path);
}
}
void BasicMainWindow::execFileHistory(QListWidgetItem *item)
{
if (item) {
QString path = getFilePath(item);
if (!path.isEmpty()) {
execFileHistory(path);
}
}
}
void MainWindow::doLogCurrentItemChanged()
{
clearFileList();
int row = selectedLogIndex();
QTableWidgetItem *item = ui->tableWidget_log->item(row, 0);
if (item) {
auto const &logs = getLogs();
int row = item->data(IndexRole).toInt();
if (row < (int)logs.size()) {
updateStatusBarText();
*ptrUpdateFilesListCounter() = 200;
}
} else {
row = -1;
}
updateAncestorCommitMap();
ui->tableWidget_log->viewport()->update();
}
void MainWindow::findNext()
{
if (m->search_text.isEmpty()) {
return;
}
auto const &logs = getLogs();
for (int pass = 0; pass < 2; pass++) {
int row = 0;
if (pass == 0) {
row = selectedLogIndex();
if (row < 0) {
row = 0;
} else if (m->searching) {
row++;
}
}
while (row < (int)logs.size()) {
Git::CommitItem const commit = logs[row];
if (!Git::isUncommited(commit)) {
if (commit.message.indexOf(m->search_text, 0, Qt::CaseInsensitive) >= 0) {
bool b = ui->tableWidget_log->blockSignals(true);
setCurrentLogRow(row);
ui->tableWidget_log->blockSignals(b);
m->searching = true;
return;
}
}
row++;
}
}
}
void MainWindow::findText(QString const &text)
{
m->search_text = text;
}
bool MainWindow::isAncestorCommit(QString const &id)
{
auto it = m->ancestors.find(id);
return it != m->ancestors.end();
}
void MainWindow::updateAncestorCommitMap()
{
m->ancestors.clear();
auto const &logs = getLogs();
const size_t LogCount = logs.size();
const size_t index = selectedLogIndex();
if (index < LogCount) {
// ok
} else {
return;
}
auto *logsp = getLogsPtr();
auto LogItem = [&](size_t i)->Git::CommitItem &{ return logsp->at(i); };
std::map<QString, size_t> commit_to_index_map;
size_t end = LogCount;
- if (index >= 0 && index < end) {
+ if (index < end) {
for (size_t i = index; i < end; i++) {
Git::CommitItem const &commit = LogItem(i);
commit_to_index_map[commit.commit_id] = i;
auto *item = ui->tableWidget_log->item(i, 0);
QRect r = ui->tableWidget_log->visualItemRect(item);
if (r.y() >= ui->tableWidget_log->height()) {
end = i + 1;
break;
}
}
}
Git::CommitItem *item = &LogItem(index);
if (item) {
m->ancestors.insert(m->ancestors.end(), item->commit_id);
}
for (size_t i = index; i < end; i++) {
Git::CommitItem *item = &LogItem(i);
if (isAncestorCommit(item->commit_id)) {
for (QString const &parent : item->parent_ids) {
m->ancestors.insert(m->ancestors.end(), parent);
}
}
}
}
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*/)
{
doLogCurrentItemChanged();
m->searching = false;
}
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();
}
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)logs.size()) {
return i;
}
return -1;
}
void MainWindow::updateDiffView(QListWidgetItem *item)
{
clearDiffView();
m->last_selected_file_item = item;
if (!item) return;
int idiff = indexOfDiff(item);
if (idiff >= 0 && idiff < diffResult()->size()) {
Git::Diff const &diff = diffResult()->at(idiff);
QString key = GitDiff::makeKey(diff);
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)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::dragEnterEvent(QDragEnterEvent *event)
{
if (QApplication::modalWindow()) return;
if (event->mimeData()->hasUrls()) {
event->acceptProposedAction();
event->accept();
}
}
void MainWindow::timerEvent(QTimerEvent *)
{
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) {
setInteractionMode(InteractionMode::None);
}
while (1) {
char tmp[1024];
int len = getPtyProcess()->readOutput(tmp, sizeof(tmp));
if (len < 1) break;
writeLog(tmp, len);
}
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
int c = event->key();
if (c == Qt::Key_T && (event->modifiers() & Qt::ControlModifier)) {
test();
return;
}
if (QApplication::focusWidget() == ui->widget_log) {
auto write_char = [&](char c){
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);
}
}
}
void MainWindow::on_action_edit_settings_triggered()
{
SettingsDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) {
ApplicationSettings const &newsettings = dlg.settings();
setAppSettings(newsettings);
setGitCommand(appsettings()->git_command, false);
setFileCommand(appsettings()->file_command, false);
setGpgCommand(appsettings()->gpg_command, false);
setRemoteMonitoringEnabled(true);
}
}
void MainWindow::onCloneCompleted(bool success, QVariant const &userdata)
{
if (success) {
RepositoryItem r = userdata.value<RepositoryItem>();
saveRepositoryBookmark(r);
setCurrentRepository(r, false);
openRepository(true);
}
}
void MainWindow::onPtyProcessCompleted(bool /*ok*/, QVariant const &userdata)
{
switch (getPtyCondition()) {
case PtyCondition::Clone:
onCloneCompleted(getPtyProcessOk(), userdata);
break;
}
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(getRepositoryFilterText() + c);
}
void MainWindow::backspaceRepoFilter()
{
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)
{
setRepositoryFilterText(text);
updateRepositoriesList();
}
void MainWindow::on_toolButton_erase_filter_clicked()
{
clearRepoFilter();
ui->lineEdit_filter->setFocus();
}
void MainWindow::deleteTags(QStringList const &tagnames)
{
int row = ui->tableWidget_log->currentRow();
internalDeleteTags(tagnames);
ui->tableWidget_log->selectRow(row);
}
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)
{
int row = ui->tableWidget_log->currentRow();
bool ok = internalAddTag(name);
ui->tableWidget_log->selectRow(row);
return ok;
}
void MainWindow::on_action_push_all_tags_triggered()
{
reopenRepository(false, [&](GitPtr g){
g->push(true);
});
}
void MainWindow::on_tableWidget_log_itemDoubleClicked(QTableWidgetItem *)
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
execCommitPropertyDialog(this, commit);
}
}
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_filelist->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;
}
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);
}
void MainWindow::on_action_repo_jump_triggered()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
NamedCommitList items = namedCommitItems(Branches | Tags | Remotes);
{
NamedCommitItem head;
head.name = "HEAD";
head.id = getHeadId();
items.push_front(head);
}
JumpDialog dlg(this, items);
if (dlg.exec() == QDialog::Accepted) {
QString text = dlg.text();
if (text.isEmpty()) return;
QString id = g->rev_parse(text);
if (id.isEmpty() && Git::isValidID(text)) {
QStringList list = findGitObject(text);
if (list.isEmpty()) {
QMessageBox::warning(this, tr("Jump"), QString("%1\n\n").arg(text) + tr("No such commit"));
return;
}
ObjectBrowserDialog dlg2(this, list);
if (dlg2.exec() == QDialog::Accepted) {
id = dlg2.text();
if (id.isEmpty()) return;
}
}
if (g->objectType(id) == "tag") {
id = getObjCache()->getCommitIdFromTag(id);
}
int row = rowFromCommitId(id);
if (row < 0) {
QMessageBox::warning(this, tr("Jump"), QString("%1\n(%2)\n\n").arg(text).arg(id) + tr("No such commit"));
} else {
setCurrentLogRow(row);
}
}
}
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::on_action_repo_checkout_triggered()
{
checkout();
}
void MainWindow::on_action_delete_branch_triggered()
{
deleteBranch();
}
void MainWindow::on_toolButton_terminal_clicked()
{
openTerminal(nullptr);
}
void MainWindow::on_toolButton_explorer_clicked()
{
openExplorer(nullptr);
}
void MainWindow::on_action_reset_HEAD_1_triggered()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->reset_head1();
openRepository(false);
}
void MainWindow::on_action_create_a_repository_triggered()
{
createRepository(QString());
}
bool MainWindow::isOnlineMode() const
{
return m->is_online_mode;
}
void MainWindow::setRemoteOnline(bool f, bool update_ui, bool save)
{
m->is_online_mode = f;
if (update_ui) {
QRadioButton *rb = nullptr;
rb = f ? ui->radioButton_remote_online : ui->radioButton_remote_offline;
rb->blockSignals(true);
rb->click();
rb->blockSignals(false);
ui->action_online->setCheckable(true);
ui->action_offline->setCheckable(true);
ui->action_online->setChecked(f);
ui->action_offline->setChecked(!f);
setNetworkingCommandsEnabled(f);
}
if (save) {
MySettings s;
s.beginGroup("Remote");
s.setValue("Online", f);
s.endGroup();
}
}
void MainWindow::on_radioButton_remote_online_clicked()
{
setRemoteOnline(true, true, true);
}
void MainWindow::on_radioButton_remote_offline_clicked()
{
setRemoteOnline(false, true, true);
}
void MainWindow::on_verticalScrollBar_log_valueChanged(int)
{
ui->widget_log->refrectScrollBar();
}
void MainWindow::on_horizontalScrollBar_log_valueChanged(int)
{
ui->widget_log->refrectScrollBar();
}
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::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;
setInteractionMode(InteractionMode::Busy);
QApplication::restoreOverrideCursor();
TheDlg dlg(this);
if (dlg.exec() == QDialog::Accepted) {
TheDlg::Result r = dlg.result();
if (r == TheDlg::Result::Yes) {
getPtyProcess()->writeInput("yes\n", 4);
} else {
setPtyProcessOk(false); // abort
getPtyProcess()->writeInput("no\n", 3);
QThread::msleep(300);
stopPtyProcess();
}
} else {
ui->widget_log->setFocus();
setInteractionCanceled(true);
}
setInteractionMode(InteractionMode::Busy);
}
void MainWindow::onLogIdle()
{
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';
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 == sshPassphraseUser() && !sshPassphrasePass().empty()) {
std::string text = sshPassphrasePass() + '\n';
getPtyProcess()->writeInput(text.c_str(), text.size());
} else {
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);
*value = s;
return true;
}
}
return false;
};
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;
}
}
}
}
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);
}
void MainWindow::on_action_delete_remote_branch_triggered()
{
deleteRemoteBranch(selectedCommitItem());
}
void MainWindow::on_action_terminal_triggered()
{
auto const *repo = &currentRepository();
openTerminal(repo);
}
void MainWindow::on_action_explorer_triggered()
{
auto const *repo = &currentRepository();
openExplorer(repo);
}
void MainWindow::on_action_reset_hard_triggered()
{
doGitCommand([&](GitPtr g){
g->reset_hard();
});
}
void MainWindow::on_action_clean_df_triggered()
{
doGitCommand([&](GitPtr g){
g->clean_df();
});
}
void MainWindow::postOpenRepositoryFromGitHub(QString const &username, QString const &reponame)
{
QVariantList list;
list.push_back(username);
list.push_back(reponame);
postUserFunctionEvent([&](QVariant const &v){
QVariantList l = v.toList();
QString uname = l[0].toString();
QString rname = l[1].toString();
CloneFromGitHubDialog dlg(this, uname, rname);
if (dlg.exec() == QDialog::Accepted) {
clone(dlg.url());
}
}, QVariant(list));
}
void MainWindow::on_action_stash_triggered()
{
doGitCommand([&](GitPtr g){
g->stash();
});
}
void MainWindow::on_action_stash_apply_triggered()
{
doGitCommand([&](GitPtr g){
g->stash_apply();
});
}
void MainWindow::on_action_stash_drop_triggered()
{
doGitCommand([&](GitPtr g){
g->stash_drop();
});
}
void MainWindow::on_action_online_triggered()
{
ui->radioButton_remote_online->click();
}
void MainWindow::on_action_offline_triggered()
{
ui->radioButton_remote_offline->click();
}
void MainWindow::on_action_repositories_panel_triggered()
{
bool checked = ui->action_repositories_panel->isChecked();
ui->stackedWidget_leftpanel->setCurrentWidget(checked ? ui->page_repos : ui->page_collapsed);
if (checked) {
ui->stackedWidget_leftpanel->setFixedWidth(m->repos_panel_width);
ui->stackedWidget_leftpanel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
ui->stackedWidget_leftpanel->setMinimumWidth(QWIDGETSIZE_MAX);
ui->stackedWidget_leftpanel->setMaximumWidth(QWIDGETSIZE_MAX);
} else {
m->repos_panel_width = ui->stackedWidget_leftpanel->width();
ui->stackedWidget_leftpanel->setFixedWidth(24);
}
}
void MainWindow::on_action_find_triggered()
{
m->searching = false;
if (getLogs().empty()) {
return;
}
FindCommitDialog dlg(this, m->search_text);
if (dlg.exec() == QDialog::Accepted) {
m->search_text = dlg.text();
ui->tableWidget_log->setFocus();
findNext();
}
}
void MainWindow::on_action_find_next_triggered()
{
if (m->search_text.isEmpty()) {
on_action_find_triggered();
} else {
findNext();
}
}
void MainWindow::on_action_repo_jump_to_head_triggered()
{
QString name = "HEAD";
GitPtr g = git();
QString id = g->rev_parse(name);
int row = rowFromCommitId(id);
if (row < 0) {
qDebug() << "No such commit";
} else {
setCurrentLogRow(row);
}
}
void MainWindow::test()
{
}
diff --git a/src/ObjectBrowserDialog.h b/src/ObjectBrowserDialog.h
index 652c2b8..6188a15 100644
--- a/src/ObjectBrowserDialog.h
+++ b/src/ObjectBrowserDialog.h
@@ -1,31 +1,31 @@
#ifndef OBJECTBROWSERDIALOG_H
#define OBJECTBROWSERDIALOG_H
#include <QDialog>
#include "Git.h"
class BasicMainWindow;
class QListWidgetItem;
class QTableWidgetItem;
namespace Ui {
class ObjectBrowserDialog;
}
class ObjectBrowserDialog : public QDialog {
Q_OBJECT
private:
Ui::ObjectBrowserDialog *ui;
BasicMainWindow *mainwindow();
GitPtr git();
public:
explicit ObjectBrowserDialog(BasicMainWindow *parent, QStringList const &list);
- ~ObjectBrowserDialog();
+ ~ObjectBrowserDialog() override;
QString text() const;
private slots:
void on_pushButton_inspect_clicked();
void on_tableWidget_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
void on_tableWidget_itemDoubleClicked(QTableWidgetItem *item);
};
#endif // OBJECTBROWSERDIALOG_H
diff --git a/src/SelectItemDialog.h b/src/SelectItemDialog.h
index 3f6f122..9907b49 100644
--- a/src/SelectItemDialog.h
+++ b/src/SelectItemDialog.h
@@ -1,40 +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() = default;
Item(QString const &id, QString const &text)
: id(id)
, text(text)
{
}
};
public:
explicit SelectItemDialog(QWidget *parent = nullptr);
~SelectItemDialog() override;
- void addItem(QString const &item, QString const &text);
+ void addItem(QString const &id, QString const &text);
Item item() const;
void select(QString const &id);
private slots:
void on_listWidget_itemDoubleClicked(QListWidgetItem *);
};
#endif // SELECTITEMDIALOG_H
diff --git a/src/common/misc.h b/src/common/misc.h
index 10f6fe3..fd482c5 100644
--- a/src/common/misc.h
+++ b/src/common/misc.h
@@ -1,57 +1,57 @@
#ifndef MISC_H
#define MISC_H
#include <QApplication>
#include <QStringList>
#include <QDateTime>
#include <functional>
#include <vector>
#include <cstdint>
#include <QColor>
class QContextMenuEvent;
class misc {
public:
static QString getApplicationDir();
- static QStringList splitLines(QByteArray const &text, std::function<QString(char const *ptr, size_t len)> const &tos);
+ static QStringList splitLines(QByteArray const &ba, std::function<QString(char const *ptr, size_t len)> const &tos);
static QStringList splitLines(QString const &text);
static void splitLines(char const *begin, char const *end, std::vector<std::string> *out, bool keep_newline);
static void splitLines(std::string const &text, std::vector<std::string> *out, bool need_crlf);
static QStringList splitWords(QString const &text);
static QString getFileName(QString const &path);
static QString makeDateTimeString(const QDateTime &dt);
static bool starts_with(std::string const &str, std::string const &with);
static std::string mid(std::string const &str, int start, int length = -1);
static QString normalizePathSeparator(QString const &str);
static QString joinWithSlash(QString const &left, QString const &right);
static void setFixedSize(QWidget *w);
static void drawFrame(QPainter *pr, int x, int y, int w, int h, QColor color_topleft, QColor color_bottomright = QColor());
static void dump(const uint8_t *ptr, size_t len);
static void dump(QByteArray const *in);
static bool isText(QString const &mimetype);
static bool isImage(QString const &mimetype);
static bool isSVG(QString const &mimetype);
static bool isPSD(QString const &mimetype);
static QString abbrevBranchName(QString const &name);
static QString determinFileType(QString const &filecommand, QString const &path, bool mime, std::function<void(QString const &, QByteArray *)> const &callback);
static std::string makeProxyServerURL(std::string text);
static QString makeProxyServerURL(QString text);
static QPoint contextMenuPos(QWidget *w, QContextMenuEvent *e);
static bool isExecutable(QString const &cmd);
};
class OverrideWaitCursor_ {
public:
OverrideWaitCursor_()
{
qApp->setOverrideCursor(Qt::WaitCursor);
}
~OverrideWaitCursor_()
{
qApp->restoreOverrideCursor();
}
};
#define OverrideWaitCursor OverrideWaitCursor_ waitcursor_; (void)waitcursor_;
#endif // MISC_H
diff --git a/src/darktheme/DarkStyle.h b/src/darktheme/DarkStyle.h
index 95fe685..41cf2ae 100644
--- a/src/darktheme/DarkStyle.h
+++ b/src/darktheme/DarkStyle.h
@@ -1,64 +1,64 @@
#ifndef DARKSTYLE_H
#define DARKSTYLE_H
#include <QPainter>
#include <QProxyStyle>
class QStyleOptionViewItem;
class DarkStyle : public QProxyStyle {
public:
private:
struct Private;
Private *m;
class ButtonImages {
public:
QImage im_normal;
QImage im_hover;
};
struct ScrollBarTextures {
QImage page_bg;
ButtonImages sub_line;
ButtonImages add_line;
ButtonImages slider;
};
QImage colorizeImage(QImage image);
QImage loadColorizedImage(QString const &path, QString const &role = QString());
ButtonImages generateButtonImages(QString const &path);
QImage generateHoverImage(const QImage &source);
QPixmap pixmapFromImage(QImage const &image, QSize size) const;
void loadImages();
QColor selectionColor() const;
QColor colorForItemView(QStyleOption const *opt) const;
void drawNinePatchImage(QPainter *p, QImage const &image, QRect const &r, int w, int h) const;
void drawGutter(QPainter *p, QRect const &r) const;
void drawSelectedItemFrame(QPainter *p, QRect rect, bool focus) const;
void drawFocusFrame(QPainter *p, const QRect &rect, int margin) const;
void drawButton(QPainter *p, QStyleOption const *option, bool mac_margin = true) const;
void drawToolButton(QPainter *p, QStyleOption const *option) const;
void drawMenuBarBG(QPainter *p, const QStyleOption *option, const QWidget *widget) const;
QColor color(int level, int alpha = 255) const;
void drawItemViewText(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect, bool abbreviation) const;
public:
DarkStyle(const QColor &base_color = QColor());
~DarkStyle() override;
QColor getBaseColor();
void setBaseColor(const QColor &color);
void setScrollBarExtent(int n);
void polish(QPalette &palette) override;
int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const override;
QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *option, SubControl sc, const QWidget *widget) const override;
- int styleHint(StyleHint stylehint, const QStyleOption *opt = nullptr, const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const;
+ int styleHint(StyleHint stylehint, const QStyleOption *opt = nullptr, const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override;
void drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p, const QWidget *widget) const override;
void drawControl(ControlElement ce, const QStyleOption *option, QPainter *p, const QWidget *widget) const override;
void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, QPainter *p, const QWidget *widget) const override;
};
#endif // DARKSTYLE_H
diff --git a/src/darktheme/StandardStyle.h b/src/darktheme/StandardStyle.h
index 2f43269..8f475a8 100644
--- a/src/darktheme/StandardStyle.h
+++ b/src/darktheme/StandardStyle.h
@@ -1,20 +1,20 @@
#ifndef STANDARDSTYLE_H
#define STANDARDSTYLE_H
#include "TraditionalWindowsStyleTreeControl.h"
#include <QProxyStyle>
class StandardStyle : public QProxyStyle {
private:
TraditionalWindowsStyleTreeControl legacy_windows_;
public:
StandardStyle()
- : QProxyStyle(0)
+ : QProxyStyle(nullptr)
{
}
- void drawPrimitive(PrimitiveElement element, QStyleOption const *option, QPainter *painter, QWidget const *widget = 0) const;
+ void drawPrimitive(PrimitiveElement element, QStyleOption const *option, QPainter *painter, QWidget const *widget = nullptr) const override;
};
#endif // STANDARDSTYLE_H
diff --git a/src/darktheme/TraditionalWindowsStyleTreeControl.h b/src/darktheme/TraditionalWindowsStyleTreeControl.h
index bee409d..c6fb209 100644
--- a/src/darktheme/TraditionalWindowsStyleTreeControl.h
+++ b/src/darktheme/TraditionalWindowsStyleTreeControl.h
@@ -1,21 +1,21 @@
#ifndef TRADITIONALWINDOWSSTYLETREECONTROL_H
#define TRADITIONALWINDOWSSTYLETREECONTROL_H
#include <QBrush>
#include <QPixmap>
#include <QStyle>
class QPainter;
class QWidget;
class TraditionalWindowsStyleTreeControl {
private:
QBrush br_branch;
QPixmap pm_plus;
QPixmap pm_minus;
public:
TraditionalWindowsStyleTreeControl();
- bool drawPrimitive(QStyle::PrimitiveElement element, QStyleOption const *option, QPainter *painter, QWidget const *widget = 0) const;
+ bool drawPrimitive(QStyle::PrimitiveElement element, QStyleOption const *option, QPainter *painter, QWidget const *widget = nullptr) const;
};
#endif // TRADITIONALWINDOWSSTYLETREECONTROL_H
diff --git a/src/main.cpp b/src/main.cpp
index 6389ca2..a241d9d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,168 +1,168 @@
#include "main.h"
#include "ApplicationGlobal.h"
#include "MainWindow.h"
#include "MySettings.h"
#include "SettingGeneralForm.h"
#include "common/joinpath.h"
#include "common/misc.h"
#include "darktheme/DarkStyle.h"
#include "platform.h"
#include "webclient.h"
#include <QApplication>
#include <QDebug>
#include <QDir>
#include <QMessageBox>
#include <QProxyStyle>
#include <QStandardPaths>
#include <QTranslator>
#include <string>
#ifdef Q_OS_WIN
#include "win32/win32.h"
#include "SettingGeneralForm.h"
#endif
#ifndef APP_GUITAR
#error APP_GUITAR is not defined.
#endif
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();
}
void setEnvironmentVariable(QString const &name, QString const &value);
int main(int argc, char *argv[])
{
#ifdef Q_OS_WIN
setEnvironmentVariable("UNICODEMAP_JP", "cp932");
#else
setenv("UNICODEMAP_JP", "cp932", 1);
#endif
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);
qRegisterMetaType<RepositoryItem>("RepositoryItem");
{
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;
}
// 設定ファイルがないときは、言語の選択をする。
- if (!QFileInfo(global->config_file_path).exists()) {
+ if (!QFileInfo::exists(global->config_file_path)) {
auto langs = SettingGeneralForm::languages();
SettingGeneralForm::execSelectLanguageDialog(nullptr, langs, [](){});
}
QTranslator translator;
{
if (global->language_id.isEmpty() || global->language_id == "en") {
// thru
} else {
QString path = ":/translations/Guitar_" + global->language_id;
bool f = translator.load(path, QApplication::applicationDirPath());
if (!f) {
qDebug() << QString("Failed to load the translation file: %1").arg(path);
}
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/texteditor/InputMethodPopup.h b/src/texteditor/InputMethodPopup.h
index fbefb2a..92f9e9a 100644
--- a/src/texteditor/InputMethodPopup.h
+++ b/src/texteditor/InputMethodPopup.h
@@ -1,33 +1,23 @@
#ifndef INPUTMETHODPOPUP_H
#define INPUTMETHODPOPUP_H
#include "TextEditorWidget.h"
#include <QWidget>
-class InputMethodPopup : public QWidget
-{
+class InputMethodPopup : public QWidget {
Q_OBJECT
private:
struct {
PreEditText preedit;
} data;
+protected:
+ void paintEvent(QPaintEvent *) override;
public:
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);
+ void setVisible(bool visible) override;
};
#endif // INPUTMETHODPOPUP_H
diff --git a/src/texteditor/TextEditorWidget.h b/src/texteditor/TextEditorWidget.h
index a8078a4..72e43c3 100644
--- a/src/texteditor/TextEditorWidget.h
+++ b/src/texteditor/TextEditorWidget.h
@@ -1,106 +1,105 @@
#ifndef OREWIDGET_H
#define OREWIDGET_H
#include <QTextFormat>
#include <QWidget>
#include <vector>
-#include <stdint.h>
+#include <cstdint>
#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);
+ void paintEvent(QPaintEvent *) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void wheelEvent(QWheelEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void contextMenuEvent(QContextMenuEvent *event) override;
QFont textFont();
void drawText(QPainter *painter, int px, int py, QString const &str);
public:
explicit TextEditorWidget(QWidget *parent = nullptr);
- ~TextEditorWidget();
+ ~TextEditorWidget() override;
void setTheme(const 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);
+ void updateVisibility(bool ensure_current_line_visible, bool change_col, bool auto_scroll) override;
- bool event(QEvent *event);
+ bool event(QEvent *event) override;
void bindScrollBar(QScrollBar *vsb, QScrollBar *hsb);
void setupForLogWidget(QScrollBar *vsb, QScrollBar *hsb, const TextEditorThemePtr &theme);
QPoint mapFromPixel(const QPoint &pt);
QPoint mapToPixel(const QPoint &pt);
- QVariant inputMethodQuery(Qt::InputMethodQuery q) const;
- void inputMethodEvent(QInputMethodEvent *e);
+ QVariant inputMethodQuery(Qt::InputMethodQuery q) const override;
+ void inputMethodEvent(QInputMethodEvent *e) override;
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 layoutEditor() override;
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 *);
+ void timerEvent(QTimerEvent *) override;
};
#endif // OREWIDGET_H
diff --git a/src/texteditor/UnicodeWidth.h b/src/texteditor/UnicodeWidth.h
index 9e4640a..4d91f4c 100644
--- a/src/texteditor/UnicodeWidth.h
+++ b/src/texteditor/UnicodeWidth.h
@@ -1,44 +1,44 @@
#ifndef UNICODEWIDTH_H
#define UNICODEWIDTH_H
-#include <stdint.h>
+#include <cstdint>
class UnicodeWidth {
public:
enum class Type {
Unknown,
Neutral,
Narrow,
Half,
Full,
Wide,
Ambigous,
};
private:
struct Range {
uint32_t lo;
uint32_t hi;
Type type;
};
- UnicodeWidth();
public:
+ UnicodeWidth() = delete;
static Type type(uint32_t c);
static int width(Type t)
{
switch (t) {
case Type::Neutral:
case Type::Narrow:
case Type::Half:
return 1;
case Type::Full:
case Type::Wide:
case Type::Ambigous:
return 2;
}
return 0;
}
};
#endif // UNICODEWIDTH_H
diff --git a/src/texteditor/unicode.h b/src/texteditor/unicode.h
index aa4df18..c626a39 100644
--- a/src/texteditor/unicode.h
+++ b/src/texteditor/unicode.h
@@ -1,154 +1,152 @@
#ifndef UNICODE_H_
#define UNICODE_H_
-#include <string.h>
-#include <stdlib.h>
-#include <stdint.h>
+#include <cstring>
+#include <cstdlib>
+#include <cstdint>
#include <functional>
namespace unicode_helper_ {
struct utf8_reader_state_t {
int a;
uint32_t b;
};
class utf8decoder {
private:
char const *begin;
char const *end;
size_t pos;
utf8_reader_state_t s;
public:
utf8decoder(char const *begin, char const *end);
uint32_t next();
size_t offset() const
{
return pos;
}
};
}
class abstract_unicode_reader;
class utf8encoder {
private:
struct internal_data {
abstract_unicode_reader *reader;
char c;
char buf[8];
int pos, len;
} data;
class internal_writer;
void set(abstract_unicode_reader *reader);
bool next();
inline bool next_();
public:
- utf8encoder(abstract_unicode_reader *reader = 0);
+ utf8encoder(abstract_unicode_reader *reader = nullptr);
char get();
int pos() const;
};
class utf16encoder {
private:
struct internal_data {
abstract_unicode_reader *reader;
uint16_t c;
uint16_t buf[2];
int pos, len;
} data;
class internal_writer;
void set(abstract_unicode_reader *reader);
bool next();
bool next_();
public:
- utf16encoder(abstract_unicode_reader *reader = 0);
+ utf16encoder(abstract_unicode_reader *reader = nullptr);
uint16_t get();
};
class abstract_unicode_reader {
public:
- virtual ~abstract_unicode_reader()
- {
- }
+ virtual ~abstract_unicode_reader() = default;
virtual uint32_t next() = 0;
- void to_utf8(std::function<bool(char, int)> fn)
+ void to_utf8(std::function<bool(char, int)> const &fn)
{
utf8encoder e(this);
while (1) {
int pos = e.pos();
int c = e.get();
if (c == 0) break;
- if (!fn(c, pos)) break;
+ if (!fn((char)c, pos)) break;
}
}
- void to_utf16(std::function<bool(uint16_t)> fn)
+ void to_utf16(std::function<bool(uint16_t)> const &fn)
{
utf16encoder e(this);
while (1) {
int c = e.get();
if (c == 0) break;
- if (!fn(c)) break;
+ if (!fn((uint16_t)c)) break;
}
}
- void to_utf32(std::function<bool(uint32_t)> fn)
+ void to_utf32(std::function<bool(uint32_t)> const &fn)
{
while (1) {
uint32_t c = next();
if (c == 0) break;
if (!fn(c)) break;
}
}
};
class utf32 : public abstract_unicode_reader {
private:
struct {
uint32_t const *ptr;
uint32_t const *end;
} data;
public:
utf32(uint32_t const *ptr, uint32_t const *end);
utf32(uint32_t const *ptr);
utf32(uint32_t const *ptr, size_t len);
- uint32_t next();
+ uint32_t next() override;
};
class utf16 : public abstract_unicode_reader {
private:
struct {
uint16_t const *ptr;
uint16_t const *end;
} data;
public:
utf16(uint16_t const *ptr, uint16_t const *end);
utf16(uint16_t const *ptr);
utf16(uint16_t const *ptr, size_t len);
- uint32_t next();
+ uint32_t next() override;
};
class utf8 : public abstract_unicode_reader {
private:
unicode_helper_::utf8decoder reader;
public:
utf8(char const *ptr, char const *end);
utf8(char const *ptr);
utf8(char const *ptr, size_t len);
- uint32_t next();
+ uint32_t next() override;
size_t offset() const
{
return reader.offset();
}
};
#endif
diff --git a/src/unix/UnixPtyProcess.h b/src/unix/UnixPtyProcess.h
index 9bcabb0..bbfaf3e 100644
--- a/src/unix/UnixPtyProcess.h
+++ b/src/unix/UnixPtyProcess.h
@@ -1,26 +1,26 @@
#ifndef UNIXPTYPROCESS_H
#define UNIXPTYPROCESS_H
#include "AbstractProcess.h"
#include <QThread>
class UnixPtyProcess : public AbstractPtyProcess, public QThread {
private:
struct Private;
Private *m;
protected:
- void run();
+ void run() override;
public:
UnixPtyProcess();
- ~UnixPtyProcess();
- bool isRunning() const;
- void writeInput(char const *ptr, int len);
- int readOutput(char *ptr, int len);
- void start(QString const &cmd, QVariant const &userdata);
- bool wait(unsigned long time = ULONG_MAX);
- void stop();
- int getExitCode() const;
- QString getMessage() const;
+ ~UnixPtyProcess() override;
+ bool isRunning() const override;
+ void writeInput(char const *ptr, int len) override;
+ int readOutput(char *ptr, int len) override;
+ void start(QString const &cmd, QVariant const &userdata) override;
+ bool wait(unsigned long time = ULONG_MAX) override;
+ void stop() override;
+ int getExitCode() const override;
+ QString getMessage() const override;
};
#endif // UNIXPTYPROCESS_H
diff --git a/src/webclient.cpp b/src/webclient.cpp
index a406f5f..95e6b8b 100644
--- a/src/webclient.cpp
+++ b/src/webclient.cpp
@@ -1,1237 +1,1237 @@
#include "webclient.h"
#include <cstring>
-#include <stdint.h>
+#include <cstdint>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.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 <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define closesocket(S) ::close(S)
using socket_t = int;
#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 <cassert>
#include "charvec.h"
#define USER_AGENT "Generic Web Client"
struct WebContext::Private {
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(std::string const &message)
{
throw WebClient::Error(message);
}
struct WebClient::Private {
std::vector<std::string> request_header;
Error error;
WebClient::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(Request const &req, Post const *post, RequestOption const &opt)
{
std::vector<std::string> header;
auto AddHeader = [&](std::string const &s){
header.push_back(s);
};
AddHeader("Host: " + req.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 += 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);
}
if (req.auth.type == Authorization::Basic) {
std::string s = req.auth.uid + ':' + req.auth.pwd;
AddHeader("Authorization: Basic " + base64_encode(s));
}
// header.insert(header.end(), m->request_header.begin(), m->request_header.end());
header.insert(header.end(), req.headers.begin(), req.headers.end());
m->request_header = std::move(header);
}
std::string WebClient::make_http_request(Request const &req, Post const *post, WebProxy const *proxy, bool https)
{
std::string str;
str = post ? "POST " : "GET ";
if (proxy && !https) {
str += req.url.data.full_request;
str += " HTTP/1.0";
str += "\r\n";
} else {
str += req.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)> const &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;
}
}
}
}
}
namespace {
struct sockaddr_in getinetaddr(char const *name)
{
struct sockaddr_in addr = {};
#if 0
struct hostent *host;
uint32_t a = inet_addr(name);
if (a != INADDR_NONE) {
host = gethostbyaddr((const char *)&a, 4, AF_INET);
} else {
host = gethostbyname(name);
}
if (!host) {
throw WebClient::Error(std::string("gethostbyname failed: ") + name);
}
addr.sin_family = AF_INET;
memcpy((char *)&addr.sin_addr, host->h_addr, host->h_length);
#else
struct addrinfo hints = {};
struct addrinfo *res = nullptr;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;
getaddrinfo(name, nullptr, &hints, &res);
if (res) {
if (res->ai_family == AF_INET) {
addr = *reinterpret_cast<struct sockaddr_in *>(res->ai_addr);
}
freeaddrinfo(res);
}
if (addr.sin_family == 0) {
throw WebClient::Error(std::string("getaddrinfo failed: ") + name);
}
#endif
return addr;
}
}
bool WebClient::http_get(Request const &request, Post const *post, RequestOption const &opt, std::vector<char> *out)
{
clear_error();
out->clear();
Request server_req;
WebProxy const *proxy = m->webcx->http_proxy();
if (proxy) {
server_req = Request(proxy->server);
} else {
server_req = request;
}
std::string hostname = server_req.url.host();
int port = get_port(&server_req.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 sockaddr_in server = getinetaddr(server_req.url.host().c_str());
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, post, opt);
std::string req = make_http_request(request, post, proxy, false);
send_(m->sock, req.c_str(), (int)req.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(Request const &request_req, Post const *post, RequestOption const &opt, std::vector<char> *out)
{
#if USE_OPENSSL
auto *sslctx = m->webcx->m->ctx;
if (!m->webcx || !sslctx) {
output_debug_string("SSL context is null.\n");
return false;
}
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;
};
Request server_req;
WebProxy const *proxy = m->webcx->https_proxy();
if (proxy) {
server_req = Request(proxy->server);
} else {
server_req = request_req;
}
std::string hostname = server_req.url.host();
int port = get_port(&server_req.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) {
struct sockaddr_in server = getinetaddr(server_req.url.host().c_str());
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_req.url, "https", "tcp"));
std::string str = "CONNECT ";
str += request_req.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);
int ret;
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_req, post, opt);
std::string request = make_http_request(request_req, 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;
}
bool WebClient::get(Request const &req, 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 (req.url.isssl()) {
#if USE_OPENSSL
https_get(req, post, opt, &res);
#endif
} else {
http_get(req, 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 true;
} catch (Error const &e) {
m->error = e;
close();
}
*out = Response();
return false;
}
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(Request const &req, WebClientHandler *handler)
{
get(req, nullptr, &m->response, handler);
return m->response.code;
}
int WebClient::post(Request const &req, Post const *post, WebClientHandler *handler)
{
get(req, 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(std::string const &proxy)
{
m->http_proxy = WebProxy();
m->http_proxy.server = proxy;
}
void WebContext::set_https_proxy(std::string const &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
}
//
static unsigned char const PAD = '=';
static const unsigned char _encode_table[] = {
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f,
};
static const unsigned char _decode_table[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
};
static inline unsigned char enc(int c)
{
return _encode_table[c & 63];
}
static inline unsigned char dec(int c)
{
return _decode_table[c & 127];
}
void base64_encode(char const *src, size_t length, std::vector<char> *out)
{
size_t srcpos, dstlen, dstpos;
dstlen = (length + 2) / 3 * 4;
out->resize(dstlen);
if (dstlen == 0) {
return;
}
char *dst = &out->at(0);
dstpos = 0;
for (srcpos = 0; srcpos < length; srcpos += 3) {
int v = (unsigned char)src[srcpos] << 16;
if (srcpos + 1 < length) {
v |= (unsigned char)src[srcpos + 1] << 8;
if (srcpos + 2 < length) {
v |= (unsigned char)src[srcpos + 2];
dst[dstpos + 3] = enc(v);
} else {
dst[dstpos + 3] = PAD;
}
dst[dstpos + 2] = enc(v >> 6);
} else {
dst[dstpos + 2] = PAD;
dst[dstpos + 3] = PAD;
}
dst[dstpos + 1] = enc(v >> 12);
dst[dstpos] = enc(v >> 18);
dstpos += 4;
}
}
void base64_decode(char const *src, size_t length, std::vector<char> *out)
{
unsigned char const *begin = (unsigned char const *)src;
unsigned char const *end = begin + length;
unsigned char const *ptr = begin;
out->clear();
out->reserve(length * 3 / 4);
int count = 0;
int bits = 0;
while (1) {
if (isspace(*ptr)) {
ptr++;
} else {
unsigned char c = 0xff;
if (ptr < end && *ptr < 0x80) {
c = dec(*ptr);
}
if (c < 0x40) {
bits = (bits << 6) | c;
count++;
} else {
if (count < 4) {
bits <<= (4 - count) * 6;
}
c = 0xff;
}
if (count == 4 || c == 0xff) {
if (count >= 2) {
out->push_back(bits >> 16);
if (count >= 3) {
out->push_back(bits >> 8);
if (count == 4) {
out->push_back(bits);
}
}
}
count = 0;
bits = 0;
if (c == 0xff) {
break;
}
}
ptr++;
}
}
}
void base64_encode(std::vector<char> const *src, std::vector<char> *out)
{
base64_encode(&src->at(0), src->size(), out);
}
void base64_decode(std::vector<char> const *src, std::vector<char> *out)
{
base64_decode(&src->at(0), src->size(), out);
}
void base64_encode(char const *src, std::vector<char> *out)
{
base64_encode((char const *)src, strlen(src), out);
}
void base64_decode(char const *src, std::vector<char> *out)
{
base64_decode((char const *)src, strlen(src), out);
}
diff --git a/src/webclient.h b/src/webclient.h
index eb9486e..5c90956 100644
--- a/src/webclient.h
+++ b/src/webclient.h
@@ -1,249 +1,248 @@
#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;
class WebClientHandler {
protected:
void abort(std::string const &message = {});
public:
virtual ~WebClientHandler() = default;
virtual void checkHeader(WebClient *wc)
{
(void)wc;
}
virtual void checkContent(char const *ptr, size_t len)
{
(void)ptr;
(void)len;
}
};
class WebProxy {
public:
std::string server;
bool empty() const
{
return server.empty();
}
};
#define CT_APPLICATION_OCTET_STREAM "application/octet-stream"
#define CT_APPLICATION_X_WWW_FORM_URLENCODED "application/x-www-form-urlencoded"
#define CT_MULTIPART_FORM_DATA "multipart/form-data"
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() = default;
URL(std::string const &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;
};
struct Authorization {
enum Type {
None,
Basic,
} type = None;
std::string uid;
std::string pwd;
};
class Request {
friend class ::WebClient;
private:
URL url;
Authorization auth;
std::vector<std::string> headers;
public:
Request() = default;
Request(std::string const &loc, std::vector<std::string> const &headers = {})
: url(loc)
, headers(headers)
{
}
void set_location(std::string const &loc)
{
url = URL(loc);
}
void set_authorization(Authorization::Type type, std::string const &uid, std::string const &pwd)
{
auth.type = type;
auth.uid = uid;
auth.pwd = pwd;
}
void set_basic_authorization(std::string const &uid, std::string const &pwd)
{
set_authorization(WebClient::Authorization::Basic, uid, pwd);
}
};
class Error {
private:
std::string msg_;
public:
Error() = default;
Error(std::string const &message)
: msg_(message)
{
}
- 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() = default;
Part(char const *data, size_t size, std::string const &content_type = {})
: 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(const Request &url, Post const *post, const RequestOption &opt);
- std::string make_http_request(const Request &url, Post const *post, const WebProxy *proxy, bool https);
+ void set_default_header(const Request &req, Post const *post, const RequestOption &opt);
+ std::string make_http_request(const Request &req, 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(const Request &request_req, Post const *post, RequestOption const &opt, std::vector<char> *out);
- bool https_get(const Request &request_url, Post const *post, RequestOption const &opt, std::vector<char> *out);
+ bool https_get(const Request &request_req, Post const *post, RequestOption const &opt, std::vector<char> *out);
bool get(const Request &req, 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, std::string const &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)> const &, 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(const Request &req, WebClientHandler *handler = nullptr);
int post(const Request &req, 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, std::string const &boundary);
static void make_multipart_form_data(char const *data, size_t size, WebClient::Post *out, std::string const &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);
};
//
void base64_encode(char const *src, size_t length, std::vector<char> *out);
void base64_decode(char const *src, size_t length, std::vector<char> *out);
void base64_encode(std::vector<char> const *src, std::vector<char> *out);
void base64_decode(std::vector<char> const *src, std::vector<char> *out);
void base64_encode(char const *src, std::vector<char> *out);
void base64_decode(char const *src, std::vector<char> *out);
static inline std::string to_s_(std::vector<char> const *vec)
{
if (!vec || vec->empty()) return std::string();
return std::string((char const *)&(*vec)[0], vec->size());
}
static inline std::string base64_encode(std::string const &src)
{
std::vector<char> vec;
base64_encode((char const *)src.c_str(), src.size(), &vec);
return to_s_(&vec);
}
static inline std::string base64_decode(std::string const &src)
{
std::vector<char> vec;
base64_decode((char const *)src.c_str(), src.size(), &vec);
return to_s_(&vec);
}
#endif

File Metadata

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

Event Timeline