Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F134430
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
166 KB
Referenced Files
None
Subscribers
None
View Options
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 ¤tBranch() const;
void setCurrentBranch(Git::Branch const &b);
const RepositoryItem ¤tRepository() 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 = ¤tRepository();
openTerminal(repo);
}
void MainWindow::on_action_explorer_triggered()
{
auto const *repo = ¤tRepository();
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
Details
Attached
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)
Attached To
Mode
R77 Guitar
Attached
Detach File
Event Timeline