Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
188 KB
Referenced Files
None
Subscribers
None
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index aca1cdd..16901f9 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -1,2604 +1,2609 @@
#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 "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;
QTimer remote_watcher_timer;
QImage graph_color;
QPixmap digits;
StatusLabel *status_bar_label;
QObject *last_focused_file_list = nullptr;
QListWidgetItem *last_selected_file_item = nullptr;
RemoteWatcher remote_watcher;
int repos_panel_width = 0;
};
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, &BasicMainWindow::signalCheckRemoteUpdate, &m->remote_watcher, &RemoteWatcher::checkRemoteUpdate);
connect(&m->remote_watcher_timer, &QTimer::timeout, &m->remote_watcher, &RemoteWatcher::checkRemoteUpdate);
connect(this, &MainWindow::updateButton, [&](){
doUpdateButton();
});
m->remote_watcher.start(this);
setRemoteMonitoringEnabled(true);
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);
}
}
// setTabOrder(ui->treeWidget_repos, ui->widget_log);
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 (!misc::isExecutable(appsettings()->git_command) || !misc::isExecutable(appsettings()->file_command)) {
+ 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);
}
{
settings.beginGroup("MainWindow");
int n = settings.value("FirstColumnWidth", 50).toInt();
if (n < 10) n = 50;
ui->tableWidget_log->setColumnWidth(0, n);
settings.endGroup();
}
}
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();
setUncommitedChanges(!stats.empty());
FilesListType files_list_type = FilesListType::SingleList;
bool staged = false;
auto AddItem = [&](QString const &filename, QString header, int idiff){
if (header.isEmpty()) {
header = "(??\?) "; // damn trigraph
}
QListWidgetItem *item = new QListWidgetItem(header + filename);
item->setData(FilePathRole, filename);
item->setData(DiffIndexRole, idiff);
item->setData(HunkIndexRole, -1);
switch (files_list_type) {
case FilesListType::SingleList:
ui->listWidget_files->addItem(item);
break;
case FilesListType::SideBySide:
if (staged) {
ui->listWidget_staged->addItem(item);
} else {
ui->listWidget_unstaged->addItem(item);
}
break;
}
};
if (id.isEmpty()) {
bool uncommited = isThereUncommitedChanges();
if (uncommited) {
files_list_type = FilesListType::SideBySide;
}
if (!makeDiff(uncommited ? QString() : id, 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());
checkRemoteUpdate();
doUpdateButton();
}
void MainWindow::removeSelectedRepositoryFromBookmark(bool ask)
{
int i = indexOfRepository(ui->treeWidget_repos->currentItem());
removeRepositoryFromBookmark(i, ask);
}
void MainWindow::doUpdateButton()
{
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);
}
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::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)
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
bool is_valid_commit_id = Git::isValidID(commit->commit_id);
int row = selectedLogIndex();
QMenu menu;
QAction *a_copy_id_7_letters = menu.addAction(tr("Copy commit id (7 letters)"));
QAction *a_copy_id_complete = menu.addAction(tr("Copy commit id (completely)"));
menu.addSeparator();
QAction *a_checkout = menu.addAction(tr("Checkout/Branch..."));
menu.addSeparator();
QAction *a_edit_message = nullptr;
if (row == 0 && currentBranch().ahead > 0) {
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::on_action_open_existing_working_copy_triggered()
{
QString dir = defaultWorkingDir();
dir = QFileDialog::getExistingDirectory(this, tr("Add existing working copy"), dir);
addWorkingCopyDir(dir, false);
}
void MainWindow::on_action_view_refresh_triggered()
{
openRepository(true);
}
void MainWindow::on_tableWidget_log_currentItemChanged(QTableWidgetItem * /*current*/, QTableWidgetItem * /*previous*/)
{
clearFileList();
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (!item) return;
auto const &logs = getLogs();
int row = item->data(IndexRole).toInt();
if (row < (int)logs.size()) {
updateStatusBarText();
*ptrUpdateFilesListCounter() = 200;
}
}
void MainWindow::on_toolButton_stage_clicked()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->stage(selectedFiles());
updateCurrentFilesList();
}
void MainWindow::on_toolButton_unstage_clicked()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->unstage(selectedFiles());
updateCurrentFilesList();
}
void MainWindow::on_toolButton_select_all_clicked()
{
if (ui->listWidget_unstaged->count() > 0) {
ui->listWidget_unstaged->setFocus();
ui->listWidget_unstaged->selectAll();
} else if (ui->listWidget_staged->count() > 0) {
ui->listWidget_staged->setFocus();
ui->listWidget_staged->selectAll();
}
}
void MainWindow::on_toolButton_commit_clicked()
{
ui->action_commit->trigger();
}
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::setWatchRemoteInterval(int mins)
{
if (mins > 0) {
m->remote_watcher_timer.start(mins * 60000);
} else {
m->remote_watcher_timer.stop();
}
}
void MainWindow::setRemoteMonitoringEnabled(bool enable)
{
if (enable) {
setWatchRemoteInterval(appsettings()->watch_remote_changes_every_mins);
} else {
setWatchRemoteInterval(0);
}
}
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());
}
void MainWindow::setNetworkingCommandsEnabled(bool f)
{
ui->action_clone->setEnabled(f);
ui->toolButton_clone->setEnabled(f);
if (!Git::isValidWorkingCopy(currentWorkingCopyDir())) {
f = false;
}
bool opened = !currentRepository().name.isEmpty();
ui->action_fetch->setEnabled(f || opened);
ui->toolButton_fetch->setEnabled(f || 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(f);
ui->action_pull->setEnabled(f);
ui->action_push->setEnabled(f);
ui->action_push_u->setEnabled(f);
ui->action_push_all_tags->setEnabled(f);
ui->toolButton_pull->setEnabled(f);
ui->toolButton_push->setEnabled(f);
}
bool MainWindow::isOnlineMode() const
{
return m->is_online_mode;
}
void MainWindow::setRemoteOnline(bool f)
{
m->is_online_mode = f;
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);
MySettings s;
s.beginGroup("Remote");
s.setValue("Online", f);
s.endGroup();
}
void MainWindow::on_radioButton_remote_online_clicked()
{
setRemoteOnline(true);
}
void MainWindow::on_radioButton_remote_offline_clicked()
{
setRemoteOnline(false);
}
void MainWindow::on_verticalScrollBar_log_valueChanged(int)
{
ui->widget_log->refrectScrollBar();
}
void MainWindow::on_horizontalScrollBar_log_valueChanged(int)
{
ui->widget_log->refrectScrollBar();
}
void MainWindow::on_toolButton_stop_process_clicked()
{
abortPtyProcess();
}
void MainWindow::on_action_stop_process_triggered()
{
abortPtyProcess();
}
void MainWindow::on_action_exit_triggered()
{
close();
}
void MainWindow::on_action_reflog_triggered()
{
GitPtr g = git();
Git::ReflogItemList reflog;
g->reflog(&reflog);
ReflogWindow dlg(this, this, reflog);
dlg.exec();
}
void MainWindow::blame(QListWidgetItem *item)
{
QList<BlameItem> list;
QString path = getFilePath(item);
{
GitPtr g = git();
QByteArray ba = g->blame(path);
if (!ba.isEmpty()) {
char const *begin = ba.data();
char const *end = begin + ba.size();
list = BlameWindow::parseBlame(begin, end);
}
}
if (!list.isEmpty()) {
qApp->setOverrideCursor(Qt::WaitCursor);
BlameWindow win(this, path, list);
qApp->restoreOverrideCursor();
win.exec();
}
}
void MainWindow::blame()
{
blame(currentFileItem());
}
void MainWindow::on_action_repository_property_triggered()
{
execRepositoryPropertyDialog(currentWorkingCopyDir());
}
void MainWindow::on_action_set_gpg_signing_triggered()
{
GitPtr g = git();
QString global_key_id = g->signingKey(Git::Source::Global);
QString repository_key_id;
if (g->isValidWorkingCopy()) {
repository_key_id = g->signingKey(Git::Source::Local);
}
SetGpgSigningDialog dlg(this, currentRepositoryName(), global_key_id, repository_key_id);
if (dlg.exec() == QDialog::Accepted) {
g->setSigningKey(dlg.id(), dlg.isGlobalChecked());
}
}
void MainWindow::execAreYouSureYouWantToContinueConnectingDialog()
{
using TheDlg = AreYouSureYouWantToContinueConnectingDialog;
setInteractionMode(InteractionMode::Busy);
QApplication::restoreOverrideCursor();
TheDlg dlg(this);
if (dlg.exec() == QDialog::Accepted) {
TheDlg::Result r = dlg.result();
if (r == TheDlg::Result::Yes) {
getPtyProcess()->writeInput("yes\n", 4);
} else {
setPtyProcessOk(false); // abort
getPtyProcess()->writeInput("no\n", 3);
QThread::msleep(300);
stopPtyProcess();
}
} else {
ui->widget_log->setFocus();
setInteractionCanceled(true);
}
setInteractionMode(InteractionMode::Busy);
}
void MainWindow::onLogIdle()
{
if (interactionCanceled()) return;
if (interactionMode() != InteractionMode::None) return;
static char const are_you_sure_you_want_to_continue_connecting[] = "Are you sure you want to continue connecting (yes/no)?";
static char const enter_passphrase[] = "Enter passphrase: ";
static char const enter_passphrase_for_key[] = "Enter passphrase for key '";
static char const fatal_authentication_failed_for[] = "fatal: Authentication failed for '";
std::vector<char> vec;
ui->widget_log->retrieveLastText(&vec, 100);
if (!vec.empty()) {
std::string line;
int n = (int)vec.size();
int i = n;
while (i > 0) {
i--;
if (i + 1 < n && vec[i] == '\n') {
i++;
line.assign(&vec[i], n - i);
break;
}
}
if (!line.empty()) {
auto ExecLineEditDialog = [&](QWidget *parent, QString const &title, QString const &prompt, QString const &val, bool password){
LineEditDialog dlg(parent, title, prompt, val, password);
if (dlg.exec() == QDialog::Accepted) {
std::string ret = dlg.text().toStdString();
std::string str = ret + '\n';
getPtyProcess()->writeInput(str.c_str(), str.size());
return ret;
}
abortPtyProcess();
return std::string();
};
auto Match = [&](char const *str){
int n = strlen(str);
if (strncmp(line.c_str(), str, n) == 0) {
char const *p = line.c_str() + n;
while (1) {
if (!*p) return true;
if (!isspace((unsigned char)*p)) break;
p++;
}
}
return false;
};
auto StartsWith = [&](char const *str){
char const *p = line.c_str();
while (*str) {
if (*p != *str) return false;
str++;
p++;
}
return true;
};
if (Match(are_you_sure_you_want_to_continue_connecting)) {
execAreYouSureYouWantToContinueConnectingDialog();
return;
}
if (line == enter_passphrase) {
ExecLineEditDialog(this, "Passphrase", QString::fromStdString(line), QString(), true);
return;
}
if (StartsWith(enter_passphrase_for_key)) {
std::string keyfile;
{
int i = strlen(enter_passphrase_for_key);
char const *p = line.c_str() + i;
char const *q = strrchr(p, ':');
if (q && p + 2 < q && q[-1] == '\'') {
keyfile.assign(p, q - 1);
}
}
if (!keyfile.empty()) {
if (keyfile == sshPassphraseUser() && !sshPassphrasePass().empty()) {
std::string text = sshPassphrasePass() + '\n';
getPtyProcess()->writeInput(text.c_str(), text.size());
} else {
std::string secret = ExecLineEditDialog(this, "Passphrase for key", QString::fromStdString(line), QString(), true);
sshSetPassphrase(keyfile, secret);
}
return;
}
}
char const *begin = line.c_str();
char const *end = line.c_str() + line.size();
auto Input = [&](QString const &title, bool password, std::string *value){
Q_ASSERT(value);
std::string header = QString("%1 for '").arg(title).toStdString();
if (strncmp(begin, header.c_str(), header.size()) == 0) {
QString msg;
if (memcmp(end - 2, "':", 2) == 0) {
msg = QString::fromUtf8(begin, end - begin - 1);
} else if (memcmp(end - 3, "': ", 3) == 0) {
msg = QString::fromUtf8(begin, end - begin - 2);
}
if (!msg.isEmpty()) {
std::string s = ExecLineEditDialog(this, title, msg, value ? QString::fromStdString(*value) : QString(), password);
*value = s;
return true;
}
}
return false;
};
std::string uid = httpAuthenticationUser();
std::string pwd = httpAuthenticationPass();
bool ok = false;
if (Input("Username", false, &uid)) ok = true;
if (Input("Password", true, &pwd)) ok = true;
if (ok) {
httpSetAuthentication(uid, pwd);
return;
}
if (StartsWith(fatal_authentication_failed_for)) {
QMessageBox::critical(this, tr("Authentication Failed"), QString::fromStdString(line));
abortPtyProcess();
return;
}
}
}
}
void MainWindow::on_action_edit_tags_triggered()
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit && Git::isValidID(commit->commit_id)) {
EditTagsDialog dlg(this, commit);
dlg.exec();
}
}
void MainWindow::on_action_push_u_triggered()
{
pushSetUpstream(false);
}
void MainWindow::on_action_delete_remote_branch_triggered()
{
deleteRemoteBranch(selectedCommitItem());
}
void MainWindow::on_action_terminal_triggered()
{
auto const *repo = &currentRepository();
openTerminal(repo);
}
void MainWindow::on_action_explorer_triggered()
{
auto const *repo = &currentRepository();
openExplorer(repo);
}
void MainWindow::on_action_reset_hard_triggered()
{
doGitCommand([&](GitPtr g){
g->reset_hard();
});
}
void MainWindow::on_action_clean_df_triggered()
{
doGitCommand([&](GitPtr g){
g->clean_df();
});
}
void MainWindow::postOpenRepositoryFromGitHub(QString const &username, QString const &reponame)
{
QVariantList list;
list.push_back(username);
list.push_back(reponame);
postUserFunctionEvent([&](QVariant const &v){
QVariantList l = v.toList();
QString uname = l[0].toString();
QString rname = l[1].toString();
CloneFromGitHubDialog dlg(this, uname, rname);
if (dlg.exec() == QDialog::Accepted) {
clone(dlg.url());
}
}, QVariant(list));
}
void MainWindow::on_action_stash_triggered()
{
doGitCommand([&](GitPtr g){
g->stash();
});
}
void MainWindow::on_action_stash_apply_triggered()
{
doGitCommand([&](GitPtr g){
g->stash_apply();
});
}
void MainWindow::on_action_stash_drop_triggered()
{
doGitCommand([&](GitPtr g){
g->stash_drop();
});
}
void MainWindow::on_action_online_triggered()
{
ui->radioButton_remote_online->click();
}
void MainWindow::on_action_offline_triggered()
{
ui->radioButton_remote_offline->click();
}
void MainWindow::on_action_repositories_panel_triggered()
{
bool checked = ui->action_repositories_panel->isChecked();
ui->stackedWidget_leftpanel->setCurrentWidget(checked ? ui->page_repos : ui->page_collapsed);
if (checked) {
ui->stackedWidget_leftpanel->setFixedWidth(m->repos_panel_width);
ui->stackedWidget_leftpanel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
ui->stackedWidget_leftpanel->setMinimumWidth(QWIDGETSIZE_MAX);
ui->stackedWidget_leftpanel->setMaximumWidth(QWIDGETSIZE_MAX);
} else {
m->repos_panel_width = ui->stackedWidget_leftpanel->width();
ui->stackedWidget_leftpanel->setFixedWidth(24);
}
}
void MainWindow::test()
{
}
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 6efa898..d3d907a 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -1,205 +1,206 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "BasicMainWindow.h"
namespace Ui {
class MainWindow;
}
class QTableWidgetItem;
class HunkItem {
public:
int hunk_number = -1;
size_t pos, len;
std::vector<std::string> lines;
};
class MainWindow : public BasicMainWindow {
Q_OBJECT
friend class ImageViewWidget;
friend class FileDiffSliderWidget;
friend class FileHistoryWindow;
friend class FileDiffWidget;
friend class AboutDialog;
private:
struct Private;
Private *m;
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
QPixmap const &digitsPixmap() const;
QString currentWorkingCopyDir() const override;
QColor color(unsigned int i);
bool isOnlineMode() const override;
private:
Ui::MainWindow *ui;
void updateFilesList(QString id, bool wait) override;
void updateFilesList(Git::CommitItem const &commit, bool wait);
void updateRepositoriesList() override;
void openRepository_(GitPtr g, bool keep_selection = false) override;
void prepareLogTableWidget();
QStringList selectedFiles_(QListWidget *listwidget) const;
QStringList selectedFiles() const;
void for_each_selected_files(std::function<void (QString const &)> const &fn);
void showFileList(FilesListType files_list_type);
void clearLog();
void clearFileList() override;
void clearDiffView();
void clearRepositoryInfo();
int repositoryIndex_(const QTreeWidgetItem *item) const;
RepositoryItem const *repositoryItem(const QTreeWidgetItem *item) const;
int selectedLogIndex() const override;
QTreeWidgetItem *newQTreeWidgetFolderItem(QString const &name);
void buildRepoTree(QString const &group, QTreeWidgetItem *item, QList<RepositoryItem> *repos);
void refrectRepositories();
void updateDiffView(QListWidgetItem *item);
void updateDiffView();
void updateUnstagedFileCurrentItem();
void updateStagedFileCurrentItem();
void updateStatusBarText() override;
void setRepositoryInfo(QString const &reponame, QString const &brname) override;
int indexOfRepository(const QTreeWidgetItem *treeitem) const;
void clearRepoFilter();
void appendCharToRepoFilter(ushort c);
void backspaceRepoFilter();
void revertCommit();
void cherrypick(Git::CommitItem const *commit);
void mergeBranch(Git::CommitItem const *commit);
void rebaseBranch(Git::CommitItem const *commit);
void detectGitServerType(const GitPtr &g);
void setRemoteOnline(bool f);
void startTimers();
void onCloneCompleted(bool success, const QVariant &userdata);
void setNetworkingCommandsEnabled(bool f);
void blame(QListWidgetItem *item);
void blame();
QListWidgetItem *currentFileItem() const;
void execAreYouSureYouWantToContinueConnectingDialog();
void deleteRemoteBranch(Git::CommitItem const *commit);
QStringList remoteBranches(QString const &id, QStringList *all);
void setWatchRemoteInterval(int mins);
void test();
+ bool isUninitialized();
protected:
void dragEnterEvent(QDragEnterEvent *event) override;
void timerEvent(QTimerEvent *) override;
void keyPressEvent(QKeyEvent *event) override;
bool event(QEvent *event) override;
bool eventFilter(QObject *watched, QEvent *event) override;
public:
void drawDigit(QPainter *pr, int x, int y, int n) const;
int digitWidth() const;
int digitHeight() const;
void setStatusBarText(QString const &text);
void clearStatusBarText();
void setCurrentLogRow(int row) override;
bool shown();
void deleteTags(QStringList const &tagnames) override;
bool addTag(QString const &name);
void updateCurrentFilesList();
void notifyRemoteChanged(bool f);
void postOpenRepositoryFromGitHub(const QString &username, const QString &reponame);
private slots:
void doUpdateButton();
void onLogVisibilityChanged();
void onPtyProcessCompleted(bool ok, const QVariant &userdata);
void onRepositoriesTreeDropped();
void on_action_about_triggered();
void on_action_clean_df_triggered();
void on_action_clone_triggered();
void on_action_commit_triggered();
void on_action_create_a_repository_triggered();
void on_action_delete_branch_triggered();
void on_action_delete_remote_branch_triggered();
void on_action_edit_git_config_triggered();
void on_action_edit_gitignore_triggered();
void on_action_edit_global_gitconfig_triggered();
void on_action_edit_settings_triggered();
void on_action_edit_tags_triggered();
void on_action_exit_triggered();
void on_action_explorer_triggered();
void on_action_fetch_prune_triggered();
void on_action_fetch_triggered();
void on_action_offline_triggered();
void on_action_online_triggered();
void on_action_open_existing_working_copy_triggered();
void on_action_pull_triggered();
void on_action_push_all_tags_triggered();
void on_action_push_triggered();
void on_action_push_u_triggered();
void on_action_reflog_triggered();
void on_action_repo_checkout_triggered();
void on_action_repo_jump_triggered();
void on_action_repository_property_triggered();
void on_action_reset_HEAD_1_triggered();
void on_action_reset_hard_triggered();
void on_action_set_config_user_triggered();
void on_action_set_gpg_signing_triggered();
void on_action_stash_apply_triggered();
void on_action_stash_drop_triggered();
void on_action_stash_triggered();
void on_action_stop_process_triggered();
void on_action_terminal_triggered();
void on_action_view_refresh_triggered();
void on_action_window_log_triggered(bool checked);
void on_horizontalScrollBar_log_valueChanged(int);
void on_lineEdit_filter_textChanged(QString const &text);
void on_listWidget_files_currentRowChanged(int currentRow);
void on_listWidget_files_customContextMenuRequested(const QPoint &pos);
void on_listWidget_files_itemDoubleClicked(QListWidgetItem *item);
void on_listWidget_staged_currentRowChanged(int currentRow);
void on_listWidget_staged_customContextMenuRequested(const QPoint &pos);
void on_listWidget_staged_itemDoubleClicked(QListWidgetItem *item);
void on_listWidget_unstaged_currentRowChanged(int currentRow);
void on_listWidget_unstaged_customContextMenuRequested(const QPoint &pos);
void on_listWidget_unstaged_itemDoubleClicked(QListWidgetItem *item);
void on_radioButton_remote_offline_clicked();
void on_radioButton_remote_online_clicked();
void on_tableWidget_log_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
void on_tableWidget_log_customContextMenuRequested(const QPoint &pos);
void on_tableWidget_log_itemDoubleClicked(QTableWidgetItem *);
void on_toolButton_clone_clicked();
void on_toolButton_commit_clicked();
void on_toolButton_erase_filter_clicked();
void on_toolButton_explorer_clicked();
void on_toolButton_fetch_clicked();
void on_toolButton_pull_clicked();
void on_toolButton_push_clicked();
void on_toolButton_select_all_clicked();
void on_toolButton_stage_clicked();
void on_toolButton_stop_process_clicked();
void on_toolButton_terminal_clicked();
void on_toolButton_unstage_clicked();
void on_treeWidget_repos_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_treeWidget_repos_customContextMenuRequested(const QPoint &pos);
void on_treeWidget_repos_itemDoubleClicked(QTreeWidgetItem *item, int column);
void on_verticalScrollBar_log_valueChanged(int);
void on_action_repositories_panel_triggered();
protected:
void closeEvent(QCloseEvent *event) override;
void internalWriteLog(const char *ptr, int len) override;
RepositoryItem const *selectedRepositoryItem() const override;
void removeSelectedRepositoryFromBookmark(bool ask) override;
void setRemoteMonitoringEnabled(bool enable) override;
protected slots:
void onLogIdle();
signals:
void signalSetRemoteChanged(bool f);
void onEscapeKeyPressed();
void updateButton();
};
#endif // MAINWINDOW_H
diff --git a/src/SettingGeneralForm.cpp b/src/SettingGeneralForm.cpp
index d04e73d..7e1c5e0 100644
--- a/src/SettingGeneralForm.cpp
+++ b/src/SettingGeneralForm.cpp
@@ -1,112 +1,123 @@
#include "MySettings.h"
#include "SelectItemDialog.h"
#include "ApplicationGlobal.h"
#include "SettingGeneralForm.h"
#include "ui_SettingGeneralForm.h"
#include "common/misc.h"
#include <QFileDialog>
SettingGeneralForm::SettingGeneralForm(QWidget *parent) :
AbstractSettingForm(parent),
ui(new Ui::SettingGeneralForm)
{
ui->setupUi(this);
- langs.push_back(SelectItemDialog::Item("en", tr("English")));
- langs.push_back(SelectItemDialog::Item("ja", tr("Japanese")));
- langs.push_back(SelectItemDialog::Item("ru", tr("Russian")));
+ langs = languages();
themes.push_back(SelectItemDialog::Item("standard", tr("Standard")));
themes.push_back(SelectItemDialog::Item("dark", tr("Dark")));
updateLanguage();
updateTheme();
}
SettingGeneralForm::~SettingGeneralForm()
{
delete ui;
}
+QList<SelectItemDialog::Item> SettingGeneralForm::languages()
+{
+ QList<SelectItemDialog::Item> langs;
+ langs.push_back(SelectItemDialog::Item("en", tr("English")));
+ langs.push_back(SelectItemDialog::Item("ja", tr("Japanese")));
+ langs.push_back(SelectItemDialog::Item("ru", tr("Russian")));
+ return langs;
+}
+
void SettingGeneralForm::exchange(bool save)
{
if (save) {
settings()->remember_and_restore_window_position = ui->checkBox_save_window_pos->isChecked();
settings()->enable_high_dpi_scaling = ui->checkBox_enable_high_dpi_scaling->isChecked();
} else {
ui->checkBox_save_window_pos->setChecked(settings()->remember_and_restore_window_position);
ui->checkBox_enable_high_dpi_scaling->setChecked(settings()->enable_high_dpi_scaling);
}
}
void SettingGeneralForm::on_pushButton_browse_default_working_dir_clicked()
{
}
void SettingGeneralForm::updateLanguage()
{
QString id = global->language_id;
if (id.isEmpty()) {
id = "en";
}
for (SelectItemDialog::Item const &item : langs) {
if (item.id == id) {
ui->lineEdit_language->setText(item.text);
return;
}
}
}
void SettingGeneralForm::updateTheme()
{
QString id = global->theme_id;
if (id.isEmpty()) {
id = "standard";
}
for (SelectItemDialog::Item const &item : themes) {
if (item.id == id) {
ui->lineEdit_theme->setText(item.text);
return;
}
}
}
-void SettingGeneralForm::on_pushButton_change_language_clicked()
+void SettingGeneralForm::execSelectLanguageDialog(QWidget *parent, QList<SelectItemDialog::Item> const &langs, std::function<void()> const &done)
{
-
- SelectItemDialog dlg(this);
+ SelectItemDialog dlg(parent);
dlg.setWindowTitle(tr("Select Language"));
for (SelectItemDialog::Item const &item : langs) {
dlg.addItem(item.id, item.text);
}
dlg.select(global->language_id.isEmpty() ? "en" : global->language_id);
if (dlg.exec() == QDialog::Accepted) {
SelectItemDialog::Item item = dlg.item();
global->language_id = item.id;
MySettings s;
s.beginGroup("UI");
s.setValue("Language", global->language_id);
s.endGroup();
- updateLanguage();
+ done();
}
}
+void SettingGeneralForm::on_pushButton_change_language_clicked()
+{
+ execSelectLanguageDialog(this, langs, [&](){updateLanguage();});
+}
+
void SettingGeneralForm::on_pushButton_change_theme_clicked()
{
SelectItemDialog dlg(this);
dlg.setWindowTitle(tr("Select Theme"));
for (SelectItemDialog::Item const &item : themes) {
dlg.addItem(item.id, item.text);
}
dlg.select(global->theme_id.isEmpty() ? "standard" : global->theme_id);
if (dlg.exec() == QDialog::Accepted) {
SelectItemDialog::Item item = dlg.item();
global->theme_id = item.id;
MySettings s;
s.beginGroup("UI");
s.setValue("Theme", global->theme_id);
s.endGroup();
updateTheme();
}
}
diff --git a/src/SettingGeneralForm.h b/src/SettingGeneralForm.h
index 25e3560..0400d2c 100644
--- a/src/SettingGeneralForm.h
+++ b/src/SettingGeneralForm.h
@@ -1,34 +1,36 @@
#ifndef SETTINGGENERALFORM_H
#define SETTINGGENERALFORM_H
#include "AbstractSettingForm.h"
#include "SelectItemDialog.h"
#include <QWidget>
namespace Ui {
class SettingGeneralForm;
}
class SettingGeneralForm : public AbstractSettingForm
{
Q_OBJECT
private:
Ui::SettingGeneralForm *ui;
QList<SelectItemDialog::Item> langs;
QList<SelectItemDialog::Item> themes;
void updateLanguage();
void updateTheme();
public:
explicit SettingGeneralForm(QWidget *parent = nullptr);
~SettingGeneralForm() override;
void exchange(bool save) override;
+ static QList<SelectItemDialog::Item> languages();
+ static void execSelectLanguageDialog(QWidget *parent, const QList<SelectItemDialog::Item> &langs, const std::function<void ()> &done);
private slots:
void on_pushButton_browse_default_working_dir_clicked();
void on_pushButton_change_language_clicked();
void on_pushButton_change_theme_clicked();
};
#endif // SETTINGGENERALFORM_H
diff --git a/src/main.cpp b/src/main.cpp
index 3c62526..e1aaffd 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,159 +1,167 @@
#include "main.h"
#include "ApplicationGlobal.h"
#include "MainWindow.h"
#include "MySettings.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()) {
+ 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/resources/translations/Guitar_ja.qm b/src/resources/translations/Guitar_ja.qm
index 3f82038..b881a92 100644
Binary files a/src/resources/translations/Guitar_ja.qm and b/src/resources/translations/Guitar_ja.qm differ
diff --git a/src/resources/translations/Guitar_ru.qm b/src/resources/translations/Guitar_ru.qm
index 08c6fff..685cba3 100644
Binary files a/src/resources/translations/Guitar_ru.qm and b/src/resources/translations/Guitar_ru.qm differ
diff --git a/src/resources/translations/Guitar_ru.ts b/src/resources/translations/Guitar_ru.ts
index 514f592..51ad387 100644
--- a/src/resources/translations/Guitar_ru.ts
+++ b/src/resources/translations/Guitar_ru.ts
@@ -1,2746 +1,2645 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ru_RU">
<context>
<name>AboutDialog</name>
<message>
<location filename="../../AboutDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="../../AboutDialog.ui" line="29"/>
<location filename="../../AboutDialog.ui" line="36"/>
<location filename="../../AboutDialog.ui" line="43"/>
<location filename="../../AboutDialog.ui" line="50"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../AboutDialog.cpp" line="25"/>
<source>About %1</source>
<translation>О %1</translation>
</message>
</context>
<context>
<name>AreYouSureYouWantToContinueConnectingDialog</name>
<message>
<location filename="../../AreYouSureYouWantToContinueConnectingDialog.ui" line="14"/>
<source>Unknown Host</source>
<translation>Узел не найден</translation>
</message>
<message>
<location filename="../../AreYouSureYouWantToContinueConnectingDialog.ui" line="20"/>
<source>Are you sure you want to continue connecting (yes/no)?</source>
<translation>Повторить попытку соединения? (Да/нет)?</translation>
</message>
<message>
<location filename="../../AreYouSureYouWantToContinueConnectingDialog.ui" line="52"/>
<source>Continue</source>
<translation>Да, продолжить</translation>
</message>
<message>
<location filename="../../AreYouSureYouWantToContinueConnectingDialog.ui" line="59"/>
<source>Close</source>
<translation>Нет, закрыть</translation>
</message>
</context>
<context>
<name>BasicMainWindow</name>
<message>
<location filename="../../BasicMainWindow.cpp" line="345"/>
<source>The URL is a valid repository</source>
<translation>URL ссылается на корректный репозиторий</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="346"/>
<source>Failed to access the URL</source>
<translation>Нет доступа по URL</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="351"/>
<source>Remote Repository</source>
<translation>Репозиторий-истоник</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="376"/>
<source>&amp;Property</source>
<translation>&amp;Свойства</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1201"/>
<source>Select %1 command</source>
<translation>Выбор %1 команды</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1380"/>
<source>Revert all files</source>
<translation>Отменить (Revert) изменения во всех файлах</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1520"/>
<source>The folder is not a valid git repository.</source>
<translation>Папка не является репозиторием Git.</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1524"/>
<source>Do you want to initialize it as a git repository ?</source>
<translation>Инициализировать репозиторий Git?</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1525"/>
<source>Initialize Repository</source>
<translation>Инициализировать репозиторий</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1663"/>
<source>No repository selected</source>
<translation>Репозиторий не выбран</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1817"/>
<source>Repository Property</source>
<translation>Свойства репозитория</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1817"/>
<location filename="../../BasicMainWindow.cpp" line="1919"/>
<source>Not a valid git repository</source>
<translation>Каталог не является репозиторием Git</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1912"/>
<location filename="../../BasicMainWindow.cpp" line="1919"/>
<source>Open Repository</source>
<translation>Открыть репозиторий</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1912"/>
<source>No such folder</source>
<translation>Каталог не существует</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1912"/>
<source>Remove from bookmark ?</source>
<translation>Удалить из закладок?</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2150"/>
<source>, %1 ahead</source>
<translation>, %1 впереди</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2153"/>
<source>, %1 behind</source>
<translation>, %1 позади</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2174"/>
<source>Confirm Remove</source>
<translation>Подтвердите удаление</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2174"/>
<source>Are you sure you want to remove the repository from bookmarks ?</source>
<translation>Вы точно хотите удалить репозиторий из закладок?</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2174"/>
<source>(Files will NOT be deleted)</source>
<translation>(Файлы НЕ будут удалены)</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2204"/>
<source>A file with same name already exists</source>
<translation>Существует файл с тем же именем</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2205"/>
<location filename="../../BasicMainWindow.cpp" line="2210"/>
<location filename="../../BasicMainWindow.cpp" line="2227"/>
<location filename="../../BasicMainWindow.cpp" line="2232"/>
<source>Clone</source>
<translation>Клонировать (Clone)</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2209"/>
<source>A folder with same name already exists</source>
<translation>Существует папка с тем же именем</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2226"/>
<source>Invalid folder</source>
<translation>Ошибочный каталог</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2231"/>
<source>No such folder. Create it now ?</source>
<translation>Каталог не существует. Создать?</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2291"/>
<source>Commit</source>
<translation>Фиксировать (Commit)</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2291"/>
<source>Commit message can not be omitted.</source>
<translation>Комментарий к фиксации является обязательным.</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2308"/>
<source>Failed to commit</source>
<translation>Не удалось зафиксировать</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2342"/>
<location filename="../../BasicMainWindow.cpp" line="2426"/>
<source>Connection refused.</source>
<translation>Отказано в соединении.</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2395"/>
<source>No remote repository is registered.</source>
<translation>Репозиторий источника не зарегистрирован.</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2417"/>
<source>The current branch %1 has no upstream branch.</source>
<translation>Текущая ветвь %1 не содержится в источнике.</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2420"/>
<source>You try push --set-upstream</source>
<translation>Попытайтесь отправить (Push) с опцией --set-upstream</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2509"/>
<source>Failed to delete the branch &apos;%1&apos;</source>
<translation>Не удалось удалить ветвь &apos;%1&apos;</translation>
</message>
<message>
<source>Failed to delete the branch &apos;%1&apos;
</source>
<translation type="vanished">ブランチの削除に失敗しました : &apos;%1&apos;</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2573"/>
<source>Are you sure you want to run the following command ?</source>
<translation>Вы точно хотите выполнить следующую команду?</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2594"/>
<source>Reset a file</source>
<translation>Отбросить изменения (Reset) файла</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2608"/>
<source>Unnamed</source>
<translation>Безымянный</translation>
</message>
</context>
<context>
<name>BasicRepositoryDialog</name>
<message>
<location filename="../../BasicRepositoryDialog.cpp" line="66"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../BasicRepositoryDialog.cpp" line="67"/>
<source>Purpose</source>
<translation>Назначение</translation>
</message>
<message>
<location filename="../../BasicRepositoryDialog.cpp" line="68"/>
<source>URL</source>
<translation></translation>
</message>
</context>
<context>
<name>BigDiffWindow</name>
<message>
<location filename="../../BigDiffWindow.ui" line="14"/>
<source>Diff</source>
<translation></translation>
</message>
</context>
<context>
<name>BlameWindow</name>
<message>
<location filename="../../BlameWindow.ui" line="14"/>
<source>Blame</source>
<translation></translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="45"/>
<source>Information</source>
<translation>Информация</translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="60"/>
<source>Commit</source>
<translation>Фиксация (Commit)</translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="70"/>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="90"/>
<source>Message</source>
<translation>Комментарий</translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="80"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="118"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
</context>
<context>
<name>CheckoutDialog</name>
<message>
<location filename="../../CheckoutDialog.ui" line="14"/>
<source>Checkout</source>
<translation>Проверка (Checkout)</translation>
</message>
<message>
<location filename="../../CheckoutDialog.ui" line="20"/>
<location filename="../../CheckoutDialog.ui" line="27"/>
<location filename="../../CheckoutDialog.ui" line="34"/>
<source>RadioButton</source>
<translation></translation>
</message>
<message>
<location filename="../../CheckoutDialog.ui" line="63"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../CheckoutDialog.ui" line="70"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>CloneDialog</name>
<message>
<location filename="../../CloneDialog.ui" line="14"/>
<location filename="../../CloneDialog.ui" line="103"/>
<source>Clone</source>
<translation>Клонировать (Clone)</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="22"/>
<source>Remote</source>
<translation>Источник</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="39"/>
<source>&amp;Test</source>
<translation>&amp;Тестировать</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="46"/>
<source>Local</source>
<translation>Локальный</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="56"/>
<source>Browse</source>
<translation>Обзор</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="83"/>
<source>Open existing local directory...</source>
<translation>Открыть локальную копию...</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="113"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../CloneDialog.cpp" line="40"/>
<source>Search</source>
<translation>Поиск</translation>
</message>
<message>
<location filename="../../CloneDialog.cpp" line="41"/>
<source>GitHub</source>
<translation></translation>
</message>
<message>
<location filename="../../CloneDialog.cpp" line="108"/>
<source>Checkout into</source>
<translation>Проверить в</translation>
</message>
<message>
<location filename="../../CloneDialog.cpp" line="120"/>
<source>Open existing directory</source>
<translation>Открыть существующий каталог</translation>
</message>
</context>
<context>
<name>CloneFromGitHubDialog</name>
<message>
<location filename="../../CloneFromGitHubDialog.ui" line="14"/>
<source>Clone from GitHub</source>
<translation>Клонировать из GitHub</translation>
</message>
<message>
<location filename="../../CloneFromGitHubDialog.ui" line="45"/>
<source>ssh</source>
<translation></translation>
</message>
<message>
<location filename="../../CloneFromGitHubDialog.ui" line="55"/>
<source>http</source>
<translation></translation>
</message>
<message>
<location filename="../../CloneFromGitHubDialog.ui" line="95"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../CloneFromGitHubDialog.ui" line="102"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>CommitDialog</name>
<message>
<location filename="../../CommitDialog.ui" line="14"/>
<source>Commit</source>
<translation>Фиксация (Commit)</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="27"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="49"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="68"/>
<location filename="../../CommitDialog.ui" line="94"/>
<location filename="../../CommitDialog.ui" line="139"/>
<location filename="../../CommitDialog.ui" line="159"/>
<location filename="../../CommitDialog.ui" line="179"/>
<source>---</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="75"/>
<location filename="../../CommitDialog.ui" line="166"/>
<source>Mail</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="111"/>
<source>GPG Signing</source>
<translation>Подпись GPG</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="126"/>
<source>ID</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="146"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="189"/>
<source>Configure...</source>
<translation>Настроить...</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="198"/>
<source>Message</source>
<translation>Комментарий</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="223"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="233"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>CommitExploreWindow</name>
<message>
<location filename="../../CommitExploreWindow.ui" line="14"/>
<source>Commit Explorer</source>
<translation>Обзор фиксации</translation>
</message>
<message>
<location filename="../../CommitExploreWindow.ui" line="65"/>
<source>Commit ID</source>
<translation>ID фиксации</translation>
</message>
<message>
<location filename="../../CommitExploreWindow.ui" line="82"/>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<location filename="../../CommitExploreWindow.ui" line="89"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<location filename="../../CommitExploreWindow.cpp" line="78"/>
<source>Commit</source>
<translation>Фиксация (Commit)</translation>
</message>
</context>
<context>
<name>CommitPropertyDialog</name>
<message>
<location filename="../../CommitPropertyDialog.ui" line="14"/>
<source>Commit Property</source>
<oldsource>Commit Properties</oldsource>
<translation>Свойства фиксации</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="46"/>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="56"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="66"/>
<location filename="../../CommitPropertyDialog.ui" line="196"/>
<source>Mail</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="154"/>
<source>GPG Sign</source>
<translation>Подпись GPG</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="176"/>
<source>ID</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="186"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="233"/>
<source>Commit ID</source>
<translation>ID фиксации</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="243"/>
<source>Parent IDs</source>
<translation>ID родительских фиксаций</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="265"/>
<source>Files...</source>
<translation>Файлы...</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="285"/>
<source>Explorer</source>
<translation>Обзор</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="305"/>
<source>Checkout</source>
<translation>Проверка (Checkout)</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="312"/>
<source>Jump</source>
<translation>Перейти</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="332"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.cpp" line="60"/>
<source>&lt;Unknown&gt;</source>
<translation>&lt;Неизвестно&gt;</translation>
</message>
</context>
<context>
<name>CommitViewWindow</name>
<message>
<location filename="../../CommitViewWindow.ui" line="14"/>
<source>Commit View</source>
<translation>Просмотр фиксации (Commit)</translation>
</message>
<message>
<location filename="../../CommitViewWindow.cpp" line="58"/>
<source>History</source>
<translation>История</translation>
</message>
</context>
<context>
<name>ConfigCredentialHelperDialog</name>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="26"/>
<source>wincred</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="39"/>
<source>winstore</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="52"/>
<source>None</source>
<translation>Нет</translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="65"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="78"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="91"/>
<source>Other</source>
<translation>Другие</translation>
</message>
</context>
<context>
<name>ConfigSigningDialog</name>
<message>
<location filename="../../ConfigSigningDialog.ui" line="14"/>
<source>Signing Policy</source>
<translation>Политика подписания</translation>
</message>
<message>
<location filename="../../ConfigSigningDialog.ui" line="20"/>
<source>Config commit.gpgsign</source>
<translation>Настройка commit.gpgsign</translation>
</message>
<message>
<location filename="../../ConfigSigningDialog.ui" line="32"/>
<source>global</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigSigningDialog.ui" line="42"/>
<source>local</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigSigningDialog.ui" line="70"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigSigningDialog.ui" line="77"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>CreateRepositoryDialog</name>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="14"/>
<location filename="../../CreateRepositoryDialog.cpp" line="42"/>
<location filename="../../CreateRepositoryDialog.cpp" line="46"/>
<location filename="../../CreateRepositoryDialog.cpp" line="50"/>
<location filename="../../CreateRepositoryDialog.cpp" line="54"/>
<source>Create Repository</source>
<translation>Создать репозиторий</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="22"/>
<source>Path</source>
<translation>Путь</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="32"/>
<source>Browse</source>
<translation>Обзор</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="54"/>
<source>Bookmark</source>
<translation>Закладка</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="63"/>
<location filename="../../CreateRepositoryDialog.ui" line="88"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="76"/>
<source>Remote</source>
<translation>Источник</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="98"/>
<source>URL</source>
<translation></translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="108"/>
<source>Test</source>
<translation>Тестировать</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="146"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="153"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.cpp" line="19"/>
<source>A valid git repository already exists there.</source>
<translation>Репозиторий Git уже существует.</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.cpp" line="42"/>
<location filename="../../CreateRepositoryDialog.cpp" line="50"/>
<source>The specified path is not a directory.</source>
<translation>Путь не является каталогом.</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.cpp" line="54"/>
<source>Remote name is invalid.</source>
<translation>Имя источника некорректно.</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.cpp" line="62"/>
<source>Destination Path</source>
<translation>Путь назначения</translation>
</message>
</context>
<context>
<name>DeleteBranchDialog</name>
<message>
<location filename="../../DeleteBranchDialog.ui" line="14"/>
<source>Delete Branch</source>
<translation>Удалить ветвь</translation>
</message>
<message>
<location filename="../../DeleteBranchDialog.ui" line="25"/>
<source>&amp;All branches</source>
<translation>Все ветви (&amp;A)</translation>
</message>
<message>
<location filename="../../DeleteBranchDialog.ui" line="45"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../DeleteBranchDialog.ui" line="52"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../DeleteBranchDialog.cpp" line="23"/>
<source>Delete Remote Branch</source>
<translation>Удалить ветвь в источнике</translation>
</message>
</context>
<context>
<name>DeleteTagsDialog</name>
<message>
<location filename="../../DeleteTagsDialog.ui" line="14"/>
<source>Delete tags</source>
<translation>Удалить тэги</translation>
</message>
<message>
<location filename="../../DeleteTagsDialog.ui" line="32"/>
<source>Check all</source>
<translation>Проверить все</translation>
</message>
<message>
<location filename="../../DeleteTagsDialog.ui" line="52"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../DeleteTagsDialog.ui" line="62"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>DoYouWantToInitDialog</name>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="14"/>
<source>Init Repository</source>
<translation>Создать репозиторий</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="20"/>
<source>The folder:</source>
<translation>Каталог:</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="32"/>
<source>is not a valid git working copy.</source>
<translation>не является репозиторием.</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="39"/>
<source>Do you want to init it as a git repository ?</source>
<translation>Инициализировать репозиторий Git?</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="81"/>
<source>Yes, Please init it.</source>
<translation>Да, инициализировать.</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="88"/>
<source>No, Stop it.</source>
<translation>Нет, отменить.</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="140"/>
<source>Please choose option</source>
<translation>Продолжить</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.cpp" line="20"/>
<source>Next...</source>
<translation>Далее...</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.cpp" line="26"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>EditGitIgnoreDialog</name>
<message>
<location filename="../../EditGitIgnoreDialog.ui" line="14"/>
<source>Edit Git Ignore</source>
<translation>Редактировать список игнорирования</translation>
</message>
<message>
<location filename="../../EditGitIgnoreDialog.ui" line="20"/>
<location filename="../../EditGitIgnoreDialog.ui" line="27"/>
<location filename="../../EditGitIgnoreDialog.ui" line="34"/>
<location filename="../../EditGitIgnoreDialog.ui" line="41"/>
<source>RadioButton</source>
<translation></translation>
</message>
<message>
<location filename="../../EditGitIgnoreDialog.ui" line="63"/>
<source>Edit the file</source>
<translation>Редактировать</translation>
</message>
<message>
<location filename="../../EditGitIgnoreDialog.ui" line="83"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../EditGitIgnoreDialog.ui" line="90"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>EditRemoteDialog</name>
<message>
<location filename="../../EditRemoteDialog.ui" line="14"/>
<source>Edit Remote</source>
<translation>Свойства источника</translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="20"/>
<source>Remote</source>
<translation>Источник</translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="26"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="33"/>
<source>URL</source>
<translation></translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="53"/>
<source>&amp;Test</source>
<translation>&amp;Проверить</translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="81"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="88"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
</context>
<context>
<name>EditTagsDialog</name>
<message>
<location filename="../../EditTagsDialog.ui" line="14"/>
<source>Edit Tags</source>
<translation>Редактор тэгов</translation>
</message>
<message>
<location filename="../../EditTagsDialog.ui" line="41"/>
<source>Add...</source>
<translation>Добавить...</translation>
</message>
<message>
<location filename="../../EditTagsDialog.ui" line="48"/>
<source>Delete</source>
<translation>Удалить</translation>
</message>
<message>
<location filename="../../EditTagsDialog.ui" line="68"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
</context>
<context>
<name>ExperimentDialog</name>
<message>
<location filename="../../ExperimentDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
</context>
<context>
<name>FileDiffWidget</name>
<message>
<location filename="../../FileDiffWidget.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
</context>
<context>
<name>FileHistoryWindow</name>
<message>
<location filename="../../FileHistoryWindow.ui" line="14"/>
<source>File History</source>
<translation>История файла</translation>
</message>
<message>
<location filename="../../FileHistoryWindow.ui" line="20"/>
<location filename="../../FileHistoryWindow.ui" line="27"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../FileHistoryWindow.cpp" line="112"/>
<source>Commit</source>
<translation>Фиксация (Commit)</translation>
</message>
<message>
<location filename="../../FileHistoryWindow.cpp" line="113"/>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<location filename="../../FileHistoryWindow.cpp" line="114"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<location filename="../../FileHistoryWindow.cpp" line="115"/>
<source>Message</source>
<translation>Комментарий</translation>
</message>
<message>
<source>Description</source>
<translation type="vanished">概要</translation>
</message>
</context>
<context>
<name>FilePropertyDialog</name>
<message>
<location filename="../../FilePropertyDialog.ui" line="14"/>
<source>File Property</source>
<oldsource>File Properties</oldsource>
<translation>Свойства файла</translation>
</message>
<message>
<location filename="../../FilePropertyDialog.ui" line="78"/>
<source>&amp;Close</source>
<translation>&amp;Закрыть</translation>
</message>
</context>
<context>
<name>FileViewWidget</name>
<message>
<location filename="../../FileViewWidget.cpp" line="31"/>
<source>Form</source>
<translation></translation>
</message>
</context>
<context>
<name>InputNewTagDialog</name>
<message>
<location filename="../../InputNewTagDialog.ui" line="14"/>
<source>Edit tag</source>
<translation>Редактор тэга</translation>
</message>
<message>
<location filename="../../InputNewTagDialog.ui" line="31"/>
<source>Tag</source>
<translation>Тэг</translation>
</message>
<message>
<location filename="../../InputNewTagDialog.ui" line="58"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../InputNewTagDialog.ui" line="65"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>JumpDialog</name>
<message>
<location filename="../../JumpDialog.ui" line="14"/>
<source>Jump</source>
<translation>Перейти</translation>
</message>
<message>
<source>Branches and Tags</source>
<translation type="vanished">ブランチとタグ</translation>
</message>
<message>
<source>&amp;Filter</source>
<translation type="vanished">フィルタ(&amp;F)</translation>
</message>
<message>
<source>Find</source>
<translation type="vanished">検索</translation>
</message>
<message>
<source>&amp;Checkout</source>
<translation type="vanished">チェックアウト(&amp;C)</translation>
</message>
<message>
<location filename="../../JumpDialog.ui" line="20"/>
<source>Branch, Tag or Commit-ID :</source>
<translation>Ветвь, Тэг или ID фиксации :</translation>
</message>
<message>
<location filename="../../JumpDialog.ui" line="71"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../JumpDialog.ui" line="81"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../JumpDialog.cpp" line="38"/>
<source>Name</source>
<translation>Имя</translation>
</message>
</context>
<context>
<name>LineEditDialog</name>
<message>
<location filename="../../LineEditDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="../../LineEditDialog.ui" line="20"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../LineEditDialog.ui" line="45"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../LineEditDialog.ui" line="52"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../../MainWindow.ui" line="14"/>
<source>Guitar</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="96"/>
<location filename="../../MainWindow.ui" line="1352"/>
<location filename="../../MainWindow.ui" line="1355"/>
<source>Clone</source>
<translation>Клонировать</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="131"/>
<location filename="../../MainWindow.cpp" line="2197"/>
<location filename="../../MainWindow.cpp" line="2198"/>
<source>Fetch</source>
<translation>Загрузить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="166"/>
<source>Pull</source>
<translation>Обновить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="201"/>
<source>Push</source>
<translation>Выгрузить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="252"/>
<location filename="../../MainWindow.ui" line="1476"/>
<source>Terminal</source>
<translation>Терминал</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="287"/>
<location filename="../../MainWindow.ui" line="1485"/>
<source>Explorer</source>
<translation>Обзор</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="373"/>
<source>Repository</source>
<translation>Репозиторий</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="380"/>
<source>Branch Name</source>
<translation>Имя ветви</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="432"/>
<location filename="../../MainWindow.ui" line="1520"/>
<source>Offline</source>
<translation>Офлайн</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="422"/>
<location filename="../../MainWindow.ui" line="1515"/>
<source>Online</source>
<translation>Онлайн</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="748"/>
<location filename="../../MainWindow.cpp" line="1594"/>
<source>Unstage</source>
<translation>Не отслеживать</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="777"/>
<source>Select all</source>
<translation>Выбрать всё</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="812"/>
<location filename="../../MainWindow.cpp" line="1464"/>
<source>Stage</source>
<translation>Отслеживать</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="841"/>
<location filename="../../MainWindow.cpp" line="841"/>
<source>Commit</source>
<translation>Фиксация</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="972"/>
<source>&amp;File</source>
<translation>&amp;Файл</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="983"/>
<source>&amp;View</source>
<translation>Вид(&amp;V)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="989"/>
<source>&amp;Edit</source>
<translation>Редактировать(&amp;E)</translation>
</message>
<message>
<source>Destructive</source>
<translation type="vanished">注意を要するコマンド</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1343"/>
<source>Settings</source>
<translation>Настройки</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1365"/>
<location filename="../../MainWindow.cpp" line="1328"/>
<source>Edit tags...</source>
<translation>Правка тэгов...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1368"/>
<source>Edit tags</source>
<translation>Правка тэгов</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1490"/>
<source>Clean -df</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1495"/>
<source>Reset --hard</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1422"/>
<source>Create a repository</source>
<translation>Создание репозитория</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1409"/>
<source>Push upstream</source>
<translation>Выгрузить в источник</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1431"/>
<source>Stop process</source>
<translation>Остановить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1436"/>
<source>E&amp;xit</source>
<translation>В&amp;ыход</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1439"/>
<source>Ctrl+Q</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1444"/>
<source>Reflog...</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1449"/>
<source>Property...</source>
<translation>Свойства...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1454"/>
<location filename="../../MainWindow.ui" line="1457"/>
<source>Set GPG signing</source>
<translation>Настройка подписей GPG</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1467"/>
<source>Fetch --prune</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1001"/>
<source>&amp;Help</source>
<translation>Помощь (&amp;H)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="605"/>
<source>...</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1007"/>
<source>&amp;Window</source>
<translation>Окно(&amp;W)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1013"/>
<source>&amp;Repository</source>
<translation>Репозиторий(&amp;R)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1017"/>
<location filename="../../MainWindow.ui" line="1500"/>
<source>Stash</source>
<translation>Спрятать</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1040"/>
<source>Re&amp;mote</source>
<translation>Источник (&amp;M)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1055"/>
<source>&amp;Destructive</source>
<translation>Разрушительные (&amp;D)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1086"/>
<source>Log</source>
<translation>Журнал</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1256"/>
<source>&amp;Open existing working copy...</source>
<translation>Открыть существующий репозиторий (&amp;O)...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1259"/>
<location filename="../../MainWindow.cpp" line="1657"/>
<source>Add existing working copy</source>
<translation>Добавить существующий репозиторий</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1264"/>
<location filename="../../MainWindow.ui" line="1267"/>
<source>Refresh</source>
<translation>Обновить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1270"/>
<source>F5</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1279"/>
<source>&amp;Commit</source>
<translation>Фиксация (&amp;Commit)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1288"/>
<source>&amp;Push</source>
<translation>Выгрузить (&amp;Push)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1296"/>
<source>test</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1299"/>
<source>Ctrl+T</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1308"/>
<source>Pu&amp;ll</source>
<translation>Обновить (Pu&amp;ll)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1317"/>
<source>&amp;Fetch</source>
<translation>Загрузить (&amp;Fetch)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1322"/>
<location filename="../../MainWindow.ui" line="1325"/>
<source>Edit global .gitconfig</source>
<translation>Прака глобального .gitconfig</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1330"/>
<source>Edit .git/config</source>
<translation>Правка .git/config</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1335"/>
<source>Edit .gitignore</source>
<translation>Правка .gitignore</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1340"/>
<source>&amp;Settings...</source>
<translation>Настройки (&amp;S)...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1391"/>
<source>&amp;Jump...</source>
<translation>Переход (&amp;Jump)...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1394"/>
<source>Ctrl+J</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1399"/>
<source>Check&amp;out...</source>
<translation>Проверка (C&amp;heckout)...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1404"/>
<location filename="../../MainWindow.cpp" line="1340"/>
<source>Delete branch...</source>
<translation>Удалить ветвь...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1505"/>
<source>Apply</source>
<translation>Применить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1510"/>
<source>Drop</source>
<translation>Отменить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1528"/>
<source>Repositories panel</source>
<translation>Панель репозиториев</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1531"/>
<source>Ctrl+R</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1414"/>
<location filename="../../MainWindow.ui" line="1417"/>
<source>Reset HEAD~1</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1462"/>
<location filename="../../MainWindow.cpp" line="1341"/>
<source>Delete remote branch...</source>
<translation>Удалить ветвь источника...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1360"/>
<source>&amp;About</source>
<translation>&amp;О программе</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1373"/>
<source>Push all tags</source>
<translation>Выгрузить все тэги</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1378"/>
<source>Set config user</source>
<translation>Выбрать пользователя</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1386"/>
<source>&amp;Log</source>
<translation>Журнал (&amp;Log)</translation>
</message>
<message>
<source>Unnamed</source>
<translation type="vanished">無名</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="643"/>
<source>Default</source>
<translation>По-умолчанию</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="840"/>
<source>Graph</source>
<translation>Граф</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="842"/>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="843"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<source>Description</source>
<translation type="vanished">概要</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="990"/>
<location filename="../../MainWindow.cpp" line="1115"/>
<source>Uncommited changes</source>
<translation>Незафиксированные изменения</translation>
</message>
<message>
<source>Are you sure you want to remove the repository from bookmarks ?</source>
<translation type="vanished">リポジトリをブックマークから削除してよろしいですか?</translation>
</message>
<message>
<source>(Files will NOT be deleted)</source>
<translation type="vanished">(ファルは削除されません)</translation>
</message>
<message>
<source>Open Repository</source>
<translation type="vanished">リポジトリを開く</translation>
</message>
<message>
<source>No such folder</source>
<translation type="vanished">そのようなフォルダはありません</translation>
</message>
<message>
<source>Not a valid git repository</source>
<translation type="vanished">有効なリポジトリではありません</translation>
</message>
<message>
<source>Commit message can not be omitted.</source>
<translation type="vanished">コミットメッセージを空にすることはできません。</translation>
</message>
<message>
<source>Repository Property</source>
<translation type="vanished">リポジトリのプロパティ</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1218"/>
<source>&amp;Add new group</source>
<translation>Новая &amp;группа</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1219"/>
<source>&amp;Delete group</source>
<translation>Удалить &amp;группу</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1220"/>
<source>&amp;Rename group</source>
<translation>Переименовать группу(&amp;R)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1225"/>
<source>New group</source>
<translation>Новая группа</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1249"/>
<source>Open &amp;terminal</source>
<translation>Открыть терминал (&amp;T)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1250"/>
<source>Open command promp&amp;t</source>
<translation>Открыть терминал (&amp;T)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1252"/>
<source>&amp;Open</source>
<translation>Открыть (&amp;Open)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1263"/>
<source>Open &amp;folder</source>
<translation>Открыть каталог (&amp;F)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1268"/>
<source>&amp;Remove</source>
<translation>Удалить (&amp;Remove)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1311"/>
<source>Copy commit id (7 letters)</source>
<translation>Скопируйте ID фиксации(7 символов)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1312"/>
<source>Copy commit id (completely)</source>
<translation>Скопируйте ID фиксации(полностью)</translation>
</message>
<message>
<source>Edit comment...</source>
<translation type="vanished">コメントを編集...</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1326"/>
<location filename="../../MainWindow.cpp" line="2130"/>
<source>Rebase</source>
<translation>Перебазирование</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1345"/>
<source>Explore</source>
<translation>Обзор</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1334"/>
<source>Reset HEAD</source>
<translation>Откат HEAD</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="844"/>
<source>Message</source>
<translation>Комментарий</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1420"/>
<location filename="../../MainWindow.cpp" line="1469"/>
<location filename="../../MainWindow.cpp" line="1595"/>
<source>History</source>
<translation>История</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="2088"/>
<location filename="../../MainWindow.cpp" line="2102"/>
<source>No such commit</source>
<translation>Фиксация не существует</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="2200"/>
<location filename="../../MainWindow.cpp" line="2201"/>
<source>Update</source>
<translation>Обновить</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="2481"/>
<source>Authentication Failed</source>
<translation>Авторизация не удалась</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1329"/>
<source>Revert</source>
<translation>Отмена (Revert)</translation>
</message>
<message>
<source>Failed to commit</source>
<translation type="vanished">コミット失敗</translation>
</message>
<message>
<source>No remote repository is registered.</source>
<translation type="vanished">リモートリポジトリが登録されていません。</translation>
</message>
<message>
<source>The current branch %1 has no upstream branch.</source>
<translation type="vanished">現在のブランチ「%1」には上流ブランチがありません。</translation>
</message>
<message>
<source>You try push --set-upstream</source>
<translation type="vanished">--set-upstream を試してください</translation>
</message>
<message>
<source>Connection refused.</source>
<translation type="vanished">接続が拒否されました。</translation>
</message>
<message>
<source>&amp;Property</source>
<translation type="vanished">プロパティ(&amp;P)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1316"/>
<source>Checkout/Branch...</source>
<translation>Проверка/ветвь...</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1322"/>
<source>Edit message...</source>
<translation>Правка комментария...</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1325"/>
<source>Merge</source>
<translation>Слияние</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1327"/>
<source>Cherry-pick</source>
<translation>Выборочное применение (Cherry-pick)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1419"/>
<location filename="../../MainWindow.cpp" line="1468"/>
<source>Untrack</source>
<translation>Не отслеживать</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1421"/>
<location filename="../../MainWindow.cpp" line="1470"/>
<location filename="../../MainWindow.cpp" line="1596"/>
<source>Blame</source>
<translation>Обращение</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1429"/>
<source>Delete selected files.</source>
<translation>Удалить выбранные файлы.</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1440"/>
<source>rm --cached files</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1465"/>
<source>Reset</source>
<translation>Откат (Reset)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1466"/>
<source>Ignore</source>
<translation>Игнорировать</translation>
</message>
<message>
<source>Reset a file</source>
<translation type="vanished">ファイルをリセットします</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="2127"/>
<source>Are you sure you want to rebase the commit ?</source>
<translation>Вы уверены, что хотите переместить фиксацию?</translation>
</message>
<message>
<source>No repository selected</source>
<translation type="vanished">リポジトリが選択されていません</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1418"/>
<location filename="../../MainWindow.cpp" line="1467"/>
<source>Delete</source>
<translation>Удалить</translation>
</message>
<message>
<source>Are you sure you want to run the following command ?</source>
<translation type="vanished">次のコマンドを実行してよろしいですか?</translation>
</message>
<message>
<source>Revert all files</source>
<translation type="vanished">すべてのファイルの変更を破棄</translation>
</message>
<message>
<source>Select %1 command</source>
<translation type="vanished">%1 コマンドの選択</translation>
</message>
<message>
<source>A file with same name already exists</source>
<translation type="vanished">同じ名前のファイルが既に存在しています</translation>
</message>
<message>
<source>A folder with same name already exists</source>
<translation type="vanished">同じ名前のフォルダが既に存在しています</translation>
</message>
<message>
<source>Invalid folder</source>
<translation type="vanished">無効なフォルダ</translation>
</message>
<message>
<source>No such folder. Create it now ?</source>
<translation type="vanished">このフォルダはありません。作成しますか?</translation>
</message>
<message>
<source>The URL is a valid repository</source>
<translation type="vanished">このURLは有効なリポジトリです</translation>
</message>
<message>
<source>Failed to access the URL</source>
<translation type="vanished">このURLへのアクセスに失敗しました</translation>
</message>
<message>
<source>Remote Repository</source>
<translation type="vanished">リモートリポジトリ</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="2088"/>
<location filename="../../MainWindow.cpp" line="2102"/>
<source>Jump</source>
<translation>Перейти</translation>
</message>
<message>
<source>That commmit has not foud or not read yet</source>
<translation type="vanished">そのコミットは、見つからないか、まだ読み込まれていません</translation>
</message>
<message>
<source>Failed to delete the branch &apos;%1&apos;
</source>
<translation type="vanished">ブランチの削除に失敗しました : &apos;%1&apos;</translation>
</message>
</context>
<context>
<name>MergeBranchDialog</name>
<message>
<location filename="../../MergeBranchDialog.ui" line="14"/>
<source>Merge</source>
<translation>Слияние</translation>
</message>
<message>
<location filename="../../MergeBranchDialog.ui" line="26"/>
<source>Current branch :</source>
<translation>Текущая ветвь :</translation>
</message>
<message>
<location filename="../../MergeBranchDialog.ui" line="39"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../MergeBranchDialog.ui" line="62"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../MergeBranchDialog.ui" line="75"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>MyImageViewWidget</name>
<message>
<location filename="../../MyImageViewWidget.cpp" line="35"/>
<source>Save as...</source>
<translation>Сохранить как...</translation>
</message>
<message>
<location filename="../../MyImageViewWidget.cpp" line="43"/>
<source>Save as</source>
<translation>Сохранить как</translation>
</message>
</context>
<context>
<name>MyTextEditorWidget</name>
<message>
<location filename="../../MyTextEditorWidget.cpp" line="35"/>
<source>Save as...</source>
<translation>Сохранить как...</translation>
</message>
<message>
<location filename="../../MyTextEditorWidget.cpp" line="36"/>
<source>Copy</source>
<translation>Копировать</translation>
</message>
<message>
<location filename="../../MyTextEditorWidget.cpp" line="43"/>
<source>Save as</source>
<translation>Сохранить как</translation>
</message>
</context>
<context>
<name>ObjectBrowserDialog</name>
<message>
<location filename="../../ObjectBrowserDialog.ui" line="14"/>
<source>Object Browser</source>
<translation>Обозреватель объектов</translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.ui" line="38"/>
<source>Inspect</source>
<translation>Проверка</translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.ui" line="58"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.ui" line="65"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.cpp" line="23"/>
<source>ID</source>
<translation></translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.cpp" line="24"/>
<source>Type</source>
<translation>Тип</translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.cpp" line="119"/>
<source>Object Inspection</source>
<translation>Проверка объектов</translation>
</message>
</context>
<context>
<name>PushDialog</name>
<message>
<location filename="../../PushDialog.ui" line="14"/>
<source>Push</source>
<translation>Выгрузить</translation>
</message>
<message>
<location filename="../../PushDialog.ui" line="20"/>
<source>push --set-upstream</source>
<translation>Выгрузить в источник</translation>
</message>
<message>
<location filename="../../PushDialog.ui" line="26"/>
<source>Remote</source>
<translation>Источник</translation>
</message>
<message>
<location filename="../../PushDialog.ui" line="33"/>
<source>Branch</source>
<translation>Ветвь</translation>
</message>
<message>
<location filename="../../PushDialog.ui" line="64"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../PushDialog.ui" line="71"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>RebaseOntoDialog</name>
<message>
<source>Branch</source>
<translation type="vanished">ブランチ</translation>
</message>
<message>
<source>Cancel</source>
<translation type="vanished">キャンセル</translation>
</message>
</context>
<context>
<name>ReflogWindow</name>
<message>
<location filename="../../ReflogWindow.ui" line="14"/>
<source>Reflog</source>
<translation></translation>
</message>
<message>
<location filename="../../ReflogWindow.ui" line="57"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="35"/>
<source>Commit</source>
<translation>Фиксация (Commit)</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="36"/>
<source>Head</source>
<translation>Текущая</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="37"/>
<source>Command</source>
<translation>Команда</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="38"/>
<source>Message</source>
<translation>Комментарий</translation>
</message>
<message>
<source>Comment</source>
<translation type="vanished">コメント</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="98"/>
<source>Checkout</source>
<translation>Проверка (Checkout)</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="99"/>
<source>Explorer</source>
<translation>Обзор</translation>
</message>
</context>
<context>
<name>RemoteRepositoriesTableWidget</name>
<message>
<location filename="../../RemoteRepositoriesTableWidget.cpp" line="27"/>
<source>Copy URL</source>
<translation>Копировать URL</translation>
</message>
</context>
<context>
<name>RepositoryPropertyDialog</name>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="14"/>
<source>Repository Property</source>
<oldsource>Repository Properties</oldsource>
<translation>Свойства репозитория</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="65"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="95"/>
<source>Local dir :</source>
<translation>Локальный каталог:</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="114"/>
<source>Remote URLs</source>
<translation>URL источников</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="124"/>
<source>Remote</source>
<translation>Источник</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="130"/>
<source>Add</source>
<translation>Добавить</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="137"/>
<source>Edit</source>
<translation>Редактировать</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="157"/>
<source>Remove</source>
<translation>Удалить</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="169"/>
<source>&amp;Remote menu</source>
<translation>Меню источника (&amp;R)</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="189"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.cpp" line="132"/>
<source>Confirm Remove</source>
<translation>Подтвердите удаление</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.cpp" line="132"/>
<source>Are you sure you want to remove the remote &apos;%1&apos; from the repository &apos;%2&apos; ?</source>
<translation>Вы точно хотите удалить источник &apos;%1&apos; из репозитория &apos;%2&apos; ?</translation>
</message>
</context>
<context>
<name>SearchFromGitHubDialog</name>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="14"/>
<source>Search From GitHub</source>
<translation>Поиск на GitHub</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="25"/>
<source>Search</source>
<translation>Поиск</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="78"/>
<source>ssh</source>
<translation></translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="88"/>
<source>http</source>
<translation></translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="115"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="122"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.cpp" line="58"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.cpp" line="59"/>
<source>Owner</source>
<translation>Владелец</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.cpp" line="60"/>
<source>Score</source>
<translation>Популярность</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.cpp" line="61"/>
<source>Description</source>
<translation>Описание</translation>
</message>
</context>
<context>
<name>SelectCommandDialog</name>
<message>
<location filename="../../SelectCommandDialog.ui" line="14"/>
<source>Select git command</source>
<translation>Выбрать команду git</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.ui" line="20"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectCommandDialog.ui" line="32"/>
<source>&amp;Browse...</source>
<translation>&amp;Обзор...</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.ui" line="52"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectCommandDialog.ui" line="59"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.cpp" line="29"/>
<source>Please select the &apos;%1&apos; command you want to use.</source>
<translation>Выберите &apos;%1&apos; команду, которую вы хотите выполнить.</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.cpp" line="58"/>
<location filename="../../SelectCommandDialog.cpp" line="65"/>
<source>%1 command (%2);;</source>
<translation>%1 команда (%2);;</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.cpp" line="60"/>
<source>Executable files (*.exe)</source>
<translation>Исполняемые файлы (*.exe)</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.cpp" line="71"/>
<source>%1 command</source>
<translation>%1 команда</translation>
</message>
</context>
<context>
<name>SelectGpgKeyDialog</name>
<message>
<location filename="../../SelectGpgKeyDialog.ui" line="14"/>
<source>Select GPG Key</source>
<translation>Выбор ключа GPG</translation>
</message>
<message>
<location filename="../../SelectGpgKeyDialog.ui" line="57"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectGpgKeyDialog.ui" line="64"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../SelectGpgKeyDialog.cpp" line="31"/>
<source>ID</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectGpgKeyDialog.cpp" line="32"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SelectGpgKeyDialog.cpp" line="33"/>
<source>Mail</source>
<translation></translation>
</message>
</context>
<context>
<name>SelectItemDialog</name>
<message>
<location filename="../../SelectItemDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectItemDialog.ui" line="25"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectItemDialog.ui" line="32"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>SetGlobalUserDialog</name>
<message>
<location filename="../../SetGlobalUserDialog.ui" line="14"/>
<source>Global User Setting</source>
<translation>Глобальная настройка пользователя</translation>
</message>
<message>
<location filename="../../SetGlobalUserDialog.ui" line="31"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SetGlobalUserDialog.ui" line="41"/>
<source>Mail</source>
<translation></translation>
</message>
<message>
<location filename="../../SetGlobalUserDialog.ui" line="68"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SetGlobalUserDialog.ui" line="78"/>
<source>Skip</source>
<translation>Пропустить</translation>
</message>
</context>
<context>
<name>SetGpgSigningDialog</name>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="14"/>
<source>Set GPG Signing</source>
<translation>Настройка подписей GPG</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="26"/>
<source>Global</source>
<translation>Глобально</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="39"/>
<location filename="../../SetGpgSigningDialog.cpp" line="29"/>
<source>Repository</source>
<translation>Репозиторий</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="61"/>
<source>ID</source>
<translation></translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="71"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="81"/>
<source>Mail</source>
<translation></translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="93"/>
<source>Select</source>
<translation>Выбрать</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="113"/>
<source>Clear</source>
<translation>Очистить</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="127"/>
<source>Configure...</source>
<translation>Настроить...</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="147"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="154"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>SetRemoteUrlDialog</name>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="14"/>
<source>Set Remote URL</source>
<translation>Установка URL источника</translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="20"/>
<source>Current URLs</source>
<translation>Текущий URL</translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="30"/>
<source>New URL</source>
<translation>Новый URL</translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="36"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="91"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="98"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="43"/>
<source>URL</source>
<translation></translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="63"/>
<source>&amp;Test</source>
<translation>&amp;Тестировать</translation>
</message>
</context>
<context>
<name>SetUserDialog</name>
<message>
<location filename="../../SetUserDialog.ui" line="14"/>
<source>Set User</source>
<translation>Выбрать пользователя</translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="20"/>
<source>Global</source>
<translation>Глобально</translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="27"/>
<location filename="../../SetUserDialog.cpp" line="31"/>
<source>Repository</source>
<translation>Репозиторий</translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="42"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="52"/>
<source>Mail</source>
<translation></translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="66"/>
<source>Get icon from Gravatar</source>
<translation>Получить аватар с Gravatar</translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="122"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="132"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>SettingBehaviorForm</name>
<message>
<location filename="../../SettingBehaviorForm.ui" line="14"/>
<source>Behavior</source>
<translation>Поведение</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="20"/>
<location filename="../../SettingBehaviorForm.cpp" line="77"/>
<source>Default working folder</source>
<translation>Рабочий каталог по-умолчанию</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="29"/>
<source>Browse...</source>
<translation>Обзор...</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="39"/>
<source>Automatically fetch when opening the repository</source>
<translation>Автоматически загружать при открытии репозитория</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="46"/>
<source>Get committer&apos;s icon from gravatar.com</source>
<translation>Получить аватар коммитера с gravatar.com</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="61"/>
<source>Maximum number of commit item acquisitions</source>
<translation>Максимальное число загружаемых коммитов</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="88"/>
<source>Watch remote changes periodically</source>
<oldsource>Periodically watch remote updates</oldsource>
<translation>Отслеживать изменения в источнике</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="108"/>
<source>interval</source>
<oldsource>interval in min:</oldsource>
<translation>интервал</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="128"/>
<source>GPG signing policy</source>
<oldsource>GPG Signing Policy</oldsource>
<translation>Политика подписания</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="135"/>
<source>Configure...</source>
<translation>Настроить...</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.cpp" line="38"/>
<source>Disable</source>
<translation>Выключить</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.cpp" line="40"/>
<source>1 min</source>
<translation>1 минута</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.cpp" line="42"/>
<source>%1 mins</source>
<translation>%1 минут</translation>
</message>
</context>
<context>
<name>SettingExampleForm</name>
<message>
<location filename="../../SettingExampleForm.ui" line="14"/>
<source>Example</source>
<translation></translation>
</message>
<message>
<location filename="../../SettingExampleForm.ui" line="20"/>
<source>Underconstruction</source>
<translation></translation>
</message>
</context>
<context>
<name>SettingGeneralForm</name>
<message>
<location filename="../../SettingGeneralForm.ui" line="14"/>
<source>General</source>
<translation>Общие</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="20"/>
<source>Language</source>
<translation>Язык</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="31"/>
<source>Change Language...</source>
<translation>Изменить язык...</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="54"/>
<source>Change Theme...</source>
<translation>Изменить тему...</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="66"/>
<source>Remember and restore window position</source>
<translation>Помнить позицию и размер окна</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="73"/>
<source>Enable high DPI scaling</source>
<translation>Включить highDPI масштабирование</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="43"/>
<source>Theme</source>
<translation>Тема</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="16"/>
<source>English</source>
<translation></translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="17"/>
<source>Japanese</source>
<translation>Японский</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="19"/>
<source>Standard</source>
<translation>Стандартная</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="20"/>
<source>Dark</source>
<translation>Тёмная</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="78"/>
<source>Select Language</source>
<translation>Выбор языка</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="97"/>
<source>Select Theme</source>
<translation>Выбор темы</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="93"/>
<source>(Changes are applied at next run)</source>
<translation>(Изменения применятся при следующем запуске)</translation>
</message>
</context>
<context>
<name>SettingNetworkForm</name>
<message>
<location filename="../../SettingNetworkForm.ui" line="14"/>
<source>Network</source>
<translation>Сеть</translation>
</message>
<message>
<location filename="../../SettingNetworkForm.ui" line="20"/>
<source>Proxy server</source>
<oldsource>Proxy Server</oldsource>
<translation>Прокси-сервер</translation>
</message>
<message>
<location filename="../../SettingNetworkForm.ui" line="26"/>
<source>No proxy</source>
<oldsource>No Proxy</oldsource>
<translation>Без прокси</translation>
</message>
<message>
<location filename="../../SettingNetworkForm.ui" line="33"/>
<source>Auto detect</source>
<oldsource>Auto Detect</oldsource>
<translation>Авто-определение</translation>
</message>
<message>
<location filename="../../SettingNetworkForm.ui" line="40"/>
<source>Manual</source>
<translation>Вручную</translation>
</message>
</context>
<context>
<name>SettingProgramsForm</name>
<message>
<location filename="../../SettingProgramsForm.ui" line="14"/>
<source>Programs</source>
<translation>Программы</translation>
</message>
<message>
<location filename="../../SettingProgramsForm.ui" line="20"/>
<source>Git command</source>
<translation>путь к git</translation>
</message>
<message>
<location filename="../../SettingProgramsForm.ui" line="29"/>
<location filename="../../SettingProgramsForm.ui" line="48"/>
<location filename="../../SettingProgramsForm.ui" line="67"/>
<source>Browse...</source>
<translation>Обзор...</translation>
</message>
<message>
<location filename="../../SettingProgramsForm.ui" line="39"/>
<source>File command</source>
<translation>путь к file</translation>
</message>
<message>
<location filename="../../SettingProgramsForm.ui" line="58"/>
<source>GPG command (option)</source>
<translation>путь к gpg (опционально)</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../../SettingsDialog.ui" line="14"/>
<source>Settings</source>
<translation>Настройки</translation>
</message>
<message>
<location filename="../../SettingsDialog.ui" line="141"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SettingsDialog.ui" line="148"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>TextEditDialog</name>
<message>
<location filename="../../TextEditDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="../../TextEditDialog.ui" line="48"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../TextEditDialog.ui" line="58"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
-<context>
- <name>WelcomeWizardDialog</name>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="14"/>
- <source>Welcome to the Guitar Wizard</source>
- <translation>Guitarウィザードへようこそ</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="45"/>
- <source>Helper Tools</source>
- <translation>ヘルパープログラム</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="61"/>
- <location filename="../../WelcomeWizardDialog.ui" line="338"/>
- <source>git</source>
- <translation></translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="71"/>
- <location filename="../../WelcomeWizardDialog.ui" line="88"/>
- <location filename="../../WelcomeWizardDialog.ui" line="248"/>
- <source>Browse</source>
- <translation>参照</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="78"/>
- <location filename="../../WelcomeWizardDialog.ui" line="352"/>
- <source>file</source>
- <translation></translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="120"/>
- <source>Global User Information</source>
- <translation>グローバルユーザー情報</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="142"/>
- <source>git config --global user.name</source>
- <translation></translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="152"/>
- <source>git config --global user.email</source>
- <translation></translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="168"/>
- <source>Get icon from Gravatar</source>
- <translation>Gravatarからアイコンを取得</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="222"/>
- <location filename="../../WelcomeWizardDialog.cpp" line="155"/>
- <source>Default Working Folder</source>
- <translation>規定の作業フォルダ</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="238"/>
- <location filename="../../WelcomeWizardDialog.ui" line="324"/>
- <source>folder</source>
- <translation>フォルダ</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="280"/>
- <source>Ready to play the Guitar !</source>
- <translation>Guitarの準備ができました!</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="296"/>
- <source>user</source>
- <translation>ユーザー</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="310"/>
- <source>email</source>
- <translation>メール</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="426"/>
- <location filename="../../WelcomeWizardDialog.cpp" line="147"/>
- <source>&lt;&lt; Prev</source>
- <translation>&lt;&lt; 戻る</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="433"/>
- <location filename="../../WelcomeWizardDialog.cpp" line="148"/>
- <source>Next &gt;&gt;</source>
- <translation>次へ &gt;&gt;</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.cpp" line="126"/>
- <source>Cancel</source>
- <translation>キャンセル</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.cpp" line="145"/>
- <source>Finish</source>
- <translation>完了</translation>
- </message>
-</context>
</TS>

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jun 15, 11:34 PM (2 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70112
Default Alt Text
(188 KB)

Event Timeline