00001
00002
00003
00004
00005
00006
00007
00008
00009 #include <qlineedit.h>
00010 #include <qtextbrowser.h>
00011 #include <qlistview.h>
00012 #include <qpushbutton.h>
00013 #include <qtoolbutton.h>
00014 #include <qpainter.h>
00015 #include <qstringlist.h>
00016 #include <qlistbox.h>
00017 #include <qfontmetrics.h>
00018 #include <qcombobox.h>
00019 #include <qeventloop.h>
00020 #include <qapplication.h>
00021 #include <qwidgetlist.h>
00022 #include <qmessagebox.h>
00023 #include <qstatusbar.h>
00024 #include <qheader.h>
00025 #include <qpopupmenu.h>
00026 #include <qcursor.h>
00027 #include <qfiledialog.h>
00028 #include <qsettings.h>
00029 #include <qaction.h>
00030 #include <qinputdialog.h>
00031 #include <qaccel.h>
00032 #include <qsplitter.h>
00033 #include <qtabwidget.h>
00034 #include <qobjectlist.h>
00035 #include <qlayout.h>
00036 #include <qtooltip.h>
00037 #include "config.h"
00038 #include "help.h"
00039 #include "helpbase.h"
00040 #include "consoleimpl.h"
00041 #include "customactionimpl.h"
00042 #include "settingsimpl.h"
00043 #include "revbase.h"
00044 #include "filebase.h"
00045 #include "patchbase.h"
00046 #include "common.h"
00047 #include "git.h"
00048 #include "listview.h"
00049 #include "treeview.h"
00050 #include "patchview.h"
00051 #include "fileview.h"
00052 #include "commitimpl.h"
00053 #include "revsview.h"
00054 #include "revdesc.h"
00055 #include "mainimpl.h"
00056
00057 using namespace QGit;
00058
00059 class MyPushButton : public QPushButton {
00060 public:
00061 MyPushButton(const QIconSet& i, SCRef t, QWidget* p) : QPushButton(i, t, p) {}
00062
00063
00064
00065
00066
00067
00068
00069 virtual QSize sizeHint() const {
00070
00071 return iconSet()->iconSize(QIconSet::Small);
00072 }
00073 };
00074
00075 MainImpl::MainImpl(SCRef cd, QWidget* p) : MainBase(p, "", Qt::WDestructiveClose) {
00076
00077 EM_INIT(exExiting, "Exiting");
00078
00079 git = new Git(this);
00080 QAccel* accel = new QAccel(this);
00081 setupAccelerator(accel);
00082 qApp->installEventFilter(this);
00083
00084
00085 setRepositoryBusy = false;
00086
00087
00088 shortLogRE.setMinimal(true);
00089 shortLogRE.setCaseSensitive(false);
00090 longLogRE.setMinimal(true);
00091 longLogRE.setCaseSensitive(false);
00092
00093
00094 QSettings set;
00095 QString font(set.readEntry(APP_KEY + FONT_KEY));
00096 if (font.isEmpty()) {
00097 QFont fnt = QApplication::font();
00098 fnt.setStyleHint(QFont::TypeWriter, QFont::PreferDefault);
00099 fnt.setFixedPitch(true);
00100 fnt.setFamily(fnt.defaultFamily());
00101 font = fnt.toString();
00102 }
00103 QGit::TYPE_WRITER_FONT.fromString(font);
00104
00105
00106 delete tabWdg->currentPage();
00107 rv = new RevsView(this, git);
00108
00109
00110 pbCloseTabBase->hide();
00111 MyPushButton* pb = new MyPushButton(*pbCloseTabBase->iconSet(), "", tabWdg);
00112 QToolTip::add(pb, "Close tab");
00113 tabWdg->setCornerWidget(pb);
00114 connect(pb, SIGNAL(clicked()), this, SLOT(pushButtonCloseTab_clicked()));
00115 connect(this, SIGNAL(closeTabButtonEnabled(bool)), pb, SLOT(setEnabled(bool)));
00116
00117
00118 treeView->hide();
00119
00120
00121 connect(File, SIGNAL(activated(int)), this, SLOT(on_openRecent_activated(int)));
00122 recentRepoMenuPos = 0;
00123 while (File->idAt(recentRepoMenuPos) != -1)
00124 recentRepoMenuPos++;
00125 doUpdateRecentRepoMenu("");
00126
00127
00128 connect(Actions, SIGNAL(activated(int)), this, SLOT(on_customAction_activated(int)));
00129 QStringList sl(QStringList::split(",", set.readEntry(APP_KEY + MCR_LIST_KEY, "")));
00130 doUpdateCustomActionMenu(sl);
00131
00132
00133 QColor l(rv->tab()->listViewLog->paletteBackgroundColor());
00134 QColor d(int(l.red() * 0.97), int(l.green() * 0.97), int(l.blue() * 0.97));
00135 QGit::ODD_LINE_COL = l;
00136 QGit::EVEN_LINE_COL = d;
00137
00138
00139 QString tmp;
00140 tmp.fill('8', 41);
00141 int wd = lineEditSHA->fontMetrics().boundingRect(tmp).width();
00142 lineEditSHA->setMinimumWidth(wd);
00143
00144 connect(git, SIGNAL(newRevsAdded(const FileHistory*, const QValueVector<QString>&)),
00145 this, SLOT(on_newRevsAdded(const FileHistory*, const QValueVector<QString>&)));
00146
00147
00148 connect(rv->tab()->listViewLog, SIGNAL(doubleClicked(QListViewItem*)),
00149 this, SLOT(on_listViewLog_doubleClicked(QListViewItem*)));
00150
00151 connect(rv->tab()->listBoxFiles, SIGNAL(doubleClicked(QListBoxItem*)),
00152 this, SLOT(on_fileList_doubleClicked(QListBoxItem*)));
00153
00154 connect(treeView, SIGNAL(doubleClicked(QListViewItem*)),
00155 this, SLOT(on_treeView_doubleClicked(QListViewItem*)));
00156
00157
00158
00159 startUpDir = (cd.isEmpty()) ? QDir::current().absPath() : cd;
00160 QTimer::singleShot(10, this, SLOT(initWithEventLoopActive()));
00161 }
00162
00163 void MainImpl::initWithEventLoopActive() {
00164
00165 git->checkEnvironment();
00166 setRepository(startUpDir, false, false);
00167 startUpDir = "";
00168 }
00169
00170 void MainImpl::lineEditSHA_returnPressed() {
00171
00172 rv->st.setSha(lineEditSHA->text());
00173 UPDATE_DOMAIN(rv);
00174 }
00175
00176 void MainImpl::ActBack_activated() {
00177
00178 lineEditSHA->undo();
00179 if (lineEditSHA->text().isEmpty())
00180 lineEditSHA->undo();
00181
00182 lineEditSHA_returnPressed();
00183 }
00184
00185 void MainImpl::ActForward_activated() {
00186
00187 lineEditSHA->redo();
00188 lineEditSHA_returnPressed();
00189 }
00190
00191
00192
00193 void MainImpl::ActExternalDiff_activated() {
00194
00195 QStringList args;
00196 getExternalDiffArgs(&args);
00197 ExternalDiffProc* externalDiff = new ExternalDiffProc(args, this);
00198 externalDiff->setWorkingDirectory(curDir);
00199 if (!externalDiff->start()) {
00200 QString text("Cannot start external viewer: ");
00201 text.append(externalDiff->arguments()[0]);
00202 QMessageBox::warning(this, "Error - QGit", text);
00203 delete externalDiff;
00204 }
00205 }
00206
00207 void MainImpl::getExternalDiffArgs(QStringList* args) {
00208
00209
00210
00211 QFileInfo f(rv->st.fileName());
00212 QString prevRevSha(rv->st.diffToSha());
00213 if (prevRevSha.isEmpty()) {
00214 const Rev* r = git->revLookup(rv->st.sha());
00215 prevRevSha = (r && r->parentsCount() > 0) ? r->parent(0) : rv->st.sha();
00216 }
00217 QString fName1(curDir + "/" + rv->st.sha().left(6) + "_" + f.fileName());
00218 QString fName2(curDir + "/" + prevRevSha.left(6) + "_" + f.fileName());
00219
00220 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
00221
00222 QByteArray fileContent;
00223 git->getFile(rv->st.fileName(), rv->st.sha(), NULL, &fileContent);
00224 if (!writeToFile(fName1, QString(fileContent)))
00225 statusBar()->message("Unable to save " + fName1);
00226
00227 git->getFile(rv->st.fileName(), prevRevSha, NULL, &fileContent);
00228 if (!writeToFile(fName2, QString(fileContent)))
00229 statusBar()->message("Unable to save " + fName2);
00230
00231
00232 QSettings settings;
00233 SCRef extDiff(settings.readEntry(APP_KEY + EXT_DIFF_KEY, EXT_DIFF_DEF));
00234
00235 QApplication::restoreOverrideCursor();
00236
00237
00238 args->append(extDiff);
00239 args->append(fName2);
00240 args->append(fName1);
00241 }
00242
00243
00244
00245 void MainImpl::setRepository(SCRef newDir, bool refresh, bool keepSelection,
00246 QStringList* filterList) {
00247
00248
00249
00250
00251 if (setRepositoryBusy)
00252 return;
00253
00254 setRepositoryBusy = true;
00255
00256
00257 if (ActFilterTree->isOn() && filterList == NULL)
00258
00259
00260 ActFilterTree->toggle();
00261
00262 try {
00263 EM_REGISTER(exExiting);
00264
00265 bool archiveChanged;
00266 curDir = git->getBaseDir(&archiveChanged, newDir);
00267
00268 if (!git->stop(archiveChanged)) {
00269
00270
00271
00272 setRepositoryBusy = false;
00273 EM_REMOVE(exExiting);
00274
00275
00276 new setRepoDelayed(this, newDir, refresh, keepSelection, filterList);
00277 return;
00278 }
00279 if (archiveChanged && refresh)
00280 dbs("ASSERT in setRepository: different dir with no range select");
00281
00282
00283 setCaption(curDir + " - QGit");
00284 rv->clear(refresh && keepSelection);
00285 if (archiveChanged)
00286 emit closeAllTabs();
00287
00288
00289 updateGlobalActions(false);
00290 updateContextActions("", "", false, false);
00291 ActCommit_setEnabled(false);
00292
00293 if (ActFilterTree->isOn())
00294 setCaption(caption() + " - FILTER ON < " + filterList->join(" ") + " >");
00295
00296
00297
00298 QString n(curDir);
00299 rv->treeView->setTreeName(n.prepend('/').section('/', -1, -1));
00300
00301 bool quit;
00302 bool ok = git->init(curDir, !refresh, filterList, &quit);
00303 if (quit)
00304 goto exit;
00305
00306 updateCommitMenu(ok && git->isStGITStack());
00307 ActCheckWorkDir->setOn(testFlag(DIFF_INDEX_F));
00308
00309 if (ok) {
00310 updateGlobalActions(true);
00311 if (archiveChanged)
00312 updateRecentRepoMenu(curDir);
00313 } else
00314 statusBar()->message("Not a git archive");
00315
00316 exit:
00317 setRepositoryBusy = false;
00318 EM_REMOVE(exExiting);
00319
00320 if (quit && !startUpDir.isEmpty())
00321 close();
00322
00323 } catch (int i) {
00324 EM_REMOVE(exExiting);
00325
00326 if (EM_MATCH(i, exExiting, "loading repository")) {
00327 EM_THROW_PENDING;
00328 return;
00329 }
00330 const QString info("Exception \'" + EM_DESC(i) + "\' not "
00331 "handled in setRepository...re-throw");
00332 dbs(info);
00333 throw;
00334 }
00335 }
00336
00337 void MainImpl::updateGlobalActions(bool b) {
00338
00339 ActRefresh->setEnabled(b);
00340 ActCheckWorkDir->setEnabled(b);
00341 ActViewRev->setEnabled(b);
00342 ActViewDiff->setEnabled(b);
00343 ActViewDiffNewTab->setEnabled(b && firstTab<PatchView>());
00344 ActShowTree->setEnabled(b);
00345 ActMailApplyPatch->setEnabled(b);
00346 ActMailFormatPatch->setEnabled(b);
00347
00348 rv->setEnabled(b);
00349 }
00350
00351 void MainImpl::updateContextActions(SCRef newRevSha, SCRef newFileName,
00352 bool isDir, bool found) {
00353
00354 bool pathActionsEnabled = !newFileName.isEmpty();
00355 bool fileActionsEnabled = (pathActionsEnabled && !isDir);
00356
00357 ActViewFile->setEnabled(fileActionsEnabled);
00358 ActViewFileNewTab->setEnabled(fileActionsEnabled && firstTab<FileView>());
00359 ActExternalDiff->setEnabled(fileActionsEnabled);
00360 ActSaveFile->setEnabled(fileActionsEnabled);
00361 ActFilterTree->setEnabled(pathActionsEnabled || ActFilterTree->isOn());
00362
00363 bool isTag, isUnApplied, isApplied;
00364 isTag = isUnApplied = isApplied = false;
00365
00366 if (found) {
00367 const Rev* r = git->revLookup(newRevSha);
00368 isTag = git->checkRef(newRevSha, Git::TAG);
00369 isUnApplied = r->isUnApplied;
00370 isApplied = r->isApplied;
00371 }
00372 ActTag->setEnabled(found && (newRevSha != ZERO_SHA) && !isUnApplied);
00373 ActTagDelete->setEnabled(found && isTag && (newRevSha != ZERO_SHA) && !isUnApplied);
00374 ActPush->setEnabled(found && isUnApplied && git->isNothingToCommit());
00375 ActPop->setEnabled(found && isApplied && git->isNothingToCommit());
00376 }
00377
00378
00379
00380 void MainImpl::on_listViewLog_doubleClicked(QListViewItem* item) {
00381
00382 if (item && ActViewDiff->isEnabled())
00383 ActViewDiff->activate();
00384 }
00385
00386 void MainImpl::on_histListView_doubleClicked(QListViewItem* item) {
00387
00388 if (item && ActViewRev->isEnabled())
00389 ActViewRev->activate();
00390 }
00391
00392 void MainImpl::on_fileList_doubleClicked(QListBoxItem* item) {
00393
00394 if (item && rv->st.isMerge() && item->prev() == 0)
00395 return;
00396
00397 if (item && item->listBox() == rv->tab()->listBoxFiles && ActViewDiff->isEnabled())
00398 ActViewDiff->activate();
00399
00400 if (item && item->listBox() != rv->tab()->listBoxFiles && ActViewFile->isEnabled())
00401 ActViewFile->activate();
00402 }
00403
00404 void MainImpl::on_treeView_doubleClicked(QListViewItem* item) {
00405
00406 if (item && ActViewFile->isEnabled())
00407 ActViewFile->activate();
00408 }
00409
00410 void MainImpl::pushButtonCloseTab_clicked() {
00411
00412 int curPos = tabWdg->currentPageIndex();
00413 Domain* t;
00414 switch (currentTabType(&t)) {
00415 case TAB_REV:
00416 break;
00417 case TAB_PATCH:
00418 t->deleteWhenDone();
00419 emit tabClosed(curPos);
00420 ActViewDiffNewTab->setEnabled(ActViewDiff->isEnabled() && firstTab<PatchView>());
00421 break;
00422 case TAB_FILE:
00423 t->deleteWhenDone();
00424 emit tabClosed(curPos);
00425 ActViewFileNewTab->setEnabled(ActViewFile->isEnabled() && firstTab<FileView>());
00426 break;
00427 default:
00428 dbs("ASSERT in pushButtonCloseTab_clicked: unknown current page");
00429 break;
00430 }
00431 }
00432
00433 void MainImpl::ActViewRev_activated() {
00434
00435 Domain* t;
00436 if (currentTabType(&t) == TAB_FILE) {
00437 rv->st = t->st;
00438 UPDATE_DOMAIN(rv);
00439 }
00440 tabWdg->setCurrentPage(rv->tabPos());
00441 }
00442
00443 void MainImpl::ActViewFile_activated() {
00444
00445 openFileTab(firstTab<FileView>());
00446 }
00447
00448 void MainImpl::ActViewFileNewTab_activated() {
00449
00450 openFileTab();
00451 }
00452
00453 void MainImpl::openFileTab(FileView* fv) {
00454
00455 if (!fv) {
00456 fv = new FileView(this, git);
00457
00458 connect(fv->tab()->histListView, SIGNAL(doubleClicked(QListViewItem*)),
00459 this, SLOT(on_histListView_doubleClicked(QListViewItem*)));
00460
00461 connect(this, SIGNAL(closeAllTabs()), fv, SLOT(on_closeAllTabs()));
00462
00463 ActViewFileNewTab->setEnabled(ActViewFile->isEnabled());
00464 }
00465 tabWdg->setCurrentPage(fv->tabPos());
00466 fv->st = rv->st;
00467 UPDATE_DOMAIN(fv);
00468 }
00469
00470 void MainImpl::ActViewDiff_activated() {
00471
00472 Domain* t;
00473 if (currentTabType(&t) == TAB_FILE) {
00474 rv->st = t->st;
00475 UPDATE_DOMAIN(rv);
00476 }
00477 rv->viewPatch(false);
00478 ActViewDiffNewTab->setEnabled(true);
00479
00480 if (toolButtonFilter->isOn() || toolButtonBold->isOn()) {
00481 bool isRegExp = (cmbSearch->currentItem() == 6);
00482 emit highlightPatch(lineEditFilter->text(), isRegExp);
00483 }
00484 }
00485
00486 void MainImpl::ActViewDiffNewTab_activated() {
00487
00488 rv->viewPatch(true);
00489 }
00490
00491 bool MainImpl::eventFilter(QObject* obj, QEvent* ev) {
00492
00493 if (ev->type() == QEvent::Wheel) {
00494
00495 QWheelEvent* e = static_cast<QWheelEvent*>(ev);
00496 if (e->state() == Qt::AltButton) {
00497
00498 int idx = tabWdg->currentPageIndex();
00499 if (e->delta() < 0)
00500 idx = (++idx == tabWdg->count()) ? 0 : idx;
00501 else
00502 idx = (--idx < 0) ? tabWdg->count() - 1 : idx;
00503
00504 tabWdg->setCurrentPage(idx);
00505 return true;
00506 }
00507 }
00508 return MainBase::eventFilter(obj, ev);
00509 }
00510
00511
00512
00513 void MainImpl::on_newRevsAdded(const FileHistory* fh, const QValueVector<QString>&) {
00514
00515 if (!git->isMainHistory(fh))
00516 return;
00517
00518 if (toolButtonFilter->isOn())
00519 toolButtonFilter_toggled(true);
00520
00521 if (toolButtonBold->isOn())
00522 toolButtonBold_toggled(true);
00523
00524
00525 if ( (!git->isNothingToCommit() || git->isUnknownFiles())
00526 && !ActCommit->isEnabled()
00527 && !git->isCommittingMerge())
00528 ActCommit_setEnabled(true);
00529 }
00530
00531 void MainImpl::lineEditFilter_returnPressed() {
00532
00533 toolButtonFilter->setOn(true);
00534 }
00535
00536 void MainImpl::toolButtonFilter_toggled(bool isOn) {
00537
00538 toolButtonBold->setEnabled(!isOn);
00539 toolButtonFilter->setEnabled(false);
00540 filterList(isOn, false);
00541 toolButtonFilter->setEnabled(true);
00542 }
00543
00544 void MainImpl::toolButtonBold_toggled(bool isOn) {
00545
00546 toolButtonFilter->setEnabled(!isOn);
00547 toolButtonBold->setEnabled(false);
00548 filterList(isOn, true);
00549 toolButtonBold->setEnabled(true);
00550 }
00551
00552 void MainImpl::filterList(bool isOn, bool onlyHighlight) {
00553
00554 lineEditFilter->setEnabled(!isOn);
00555 cmbSearch->setEnabled(!isOn);
00556
00557 SCRef filter(lineEditFilter->text());
00558 if (filter.isEmpty())
00559 return;
00560
00561 QMap<QString, bool> shaMap;
00562 bool descNeedsUpdate, patchNeedsUpdate, isRegExp;
00563 descNeedsUpdate = patchNeedsUpdate = isRegExp = false;
00564 int idx = cmbSearch->currentItem(), colNum = 0;
00565 if (isOn) {
00566 switch (idx) {
00567 case 0:
00568 colNum = LOG_COL;
00569 shortLogRE.setPattern(filter);
00570 descNeedsUpdate = true;
00571 break;
00572 case 1:
00573 colNum = LOG_MSG_COL;
00574 longLogRE.setPattern(filter);
00575 descNeedsUpdate = true;
00576 break;
00577 case 2:
00578 colNum = AUTH_COL;
00579 break;
00580 case 3:
00581 colNum = COMMIT_COL;
00582 break;
00583 case 4:
00584 case 5:
00585 case 6:
00586 colNum = SHA_MAP_COL;
00587 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
00588 EM_PROCESS_EVENTS;
00589 if (idx == 4)
00590 git->getFileFilter(filter, shaMap);
00591 else {
00592 isRegExp = (idx == 6);
00593 if (!git->getPatchFilter(filter, isRegExp, shaMap)) {
00594 QApplication::restoreOverrideCursor();
00595 toolButtonFilter->toggle();
00596 return;
00597 }
00598 patchNeedsUpdate = (shaMap.count() > 0);
00599 }
00600 QApplication::restoreOverrideCursor();
00601 break;
00602 }
00603 } else {
00604 patchNeedsUpdate = ((idx == 5) || (idx == 6));
00605 descNeedsUpdate = !(shortLogRE.isEmpty() && longLogRE.isEmpty());
00606 shortLogRE.setPattern("");
00607 longLogRE.setPattern("");
00608 }
00609 bool evenLine = false;
00610 int visibleCnt = 0;
00611 QRegExp re(filter, false, true);
00612 QListViewItem* firstItem = NULL;
00613 QListView* lv = rv->tab()->listViewLog;
00614 QListViewItemIterator it(lv);
00615 while (it.current()) {
00616 ListViewItem* item = static_cast<ListViewItem*>(it.current());
00617 if (isOn) {
00618 if (passFilter(item, re, colNum, shaMap)) {
00619 if (onlyHighlight)
00620 item->setHighlighted(true);
00621 else {
00622 item->setEven(evenLine);
00623 evenLine = !evenLine;
00624 }
00625 visibleCnt++;
00626 if (visibleCnt == 1)
00627 firstItem = item;
00628
00629 } else if (!onlyHighlight)
00630 item->setVisible(false);
00631 } else {
00632 item->setHighlighted(false);
00633 item->setEven(evenLine);
00634 evenLine = !evenLine;
00635 if (!item->isVisible())
00636 item->setVisible(true);
00637 }
00638 ++it;
00639 }
00640 lv->triggerUpdate();
00641
00642
00643 QListViewItem* curItem = lv->currentItem();
00644 QListViewItem* newItem = (curItem && curItem->isVisible()) ? curItem : firstItem;
00645 rv->st.setSha(newItem ? ((ListViewItem*)newItem)->sha() : "");
00646 UPDATE_DOMAIN(rv);
00647
00648
00649 if (descNeedsUpdate && (newItem == curItem))
00650 emit updateRevDesc();
00651
00652 if (patchNeedsUpdate)
00653 emit highlightPatch(isOn ? filter : "", isRegExp);
00654
00655 QString msg;
00656 if (isOn)
00657 msg = QString("Found %1 matches. Toggle filter/highlight "
00658 "button to remove the filter").arg(visibleCnt);
00659
00660
00661 QApplication::postEvent(rv, new MessageEvent(msg));
00662 }
00663
00664 bool MainImpl::passFilter(ListViewItem* item, const QRegExp& filter, int colNum,
00665 const QMap<QString, bool>& shaMap) {
00666
00667 if (colNum == SHA_MAP_COL)
00668
00669 return shaMap.contains(item->sha());
00670
00671 QString field;
00672 if (colNum != LOG_MSG_COL && colNum != COMMIT_COL) {
00673 int adj = -1;
00674 field = item->text(colNum + adj);
00675 }
00676 if (field.isEmpty()) {
00677
00678 const Rev* c = git->revLookup(item->sha());
00679 if (colNum == LOG_COL)
00680 field = c->shortLog();
00681 else if (colNum == AUTH_COL)
00682 field = c->author();
00683 else if (colNum == LOG_MSG_COL)
00684 field = c->longLog();
00685 else if (colNum == COMMIT_COL)
00686 field = item->sha();
00687 }
00688
00689 return (field.find(filter) != -1);
00690 }
00691
00692 void MainImpl::customEvent(QCustomEvent* e) {
00693
00694 BaseEvent* de = dynamic_cast<BaseEvent*>(e);
00695 if (de == NULL) {
00696 MainBase::customEvent(e);
00697 return;
00698 }
00699 SCRef data = de->myData();
00700
00701 switch (e->type()) {
00702 case ERROR_EV: {
00703 QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
00704 EM_PROCESS_EVENTS;
00705 MainExecErrorEvent* me = (MainExecErrorEvent*)e;
00706 QString text("An error occurred while executing command:\n\n");
00707 text.append(me->command() + "\n\nGit says: \n\n" + me->report());
00708 QMessageBox::warning(this, "Error - QGit", text);
00709 QApplication::restoreOverrideCursor(); }
00710 break;
00711 case MSG_EV:
00712 statusBar()->message(data);
00713 break;
00714 case POPUP_LIST_EV:
00715 doContexPopup(data);
00716 break;
00717 case POPUP_FILE_EV:
00718 case POPUP_TREE_EV:
00719 doFileContexPopup(data, e->type());
00720 break;
00721 default:
00722 dbp("ASSERT in MainImpl::customEvent unhandled event %1", e->type());
00723 break;
00724 }
00725 }
00726
00727 int MainImpl::currentTabType(Domain** t) {
00728
00729 *t = NULL;
00730 int curPos = tabWdg->currentPageIndex();
00731 if (curPos == rv->tabPos()) {
00732 *t = rv;
00733 return TAB_REV;
00734 }
00735 QPtrList<PatchView>* l = getTabs<PatchView>(curPos);
00736 if (l->count() > 0) {
00737 *t = l->first();
00738 delete l;
00739 return TAB_PATCH;
00740 }
00741 delete l;
00742 QPtrList<FileView>* l2 = getTabs<FileView>(curPos);
00743 if (l2->count() > 0) {
00744 *t = l2->first();
00745 delete l2;
00746 return TAB_FILE;
00747 }
00748 if (l2->count() > 0)
00749 dbs("ASSERT in tabType file not found");
00750
00751 delete l2;
00752 return -1;
00753 }
00754
00755 template<class X> QPtrList<X>* MainImpl::getTabs(int tabPos) {
00756
00757 X dummy;
00758 QObjectList* l = this->queryList(dummy.className());
00759 QPtrList<X>* ret = new QPtrList<X>;
00760 for (QObject* item = l->first(); item; item = l->next()) {
00761
00762 X* x = static_cast<X*>(item);
00763 if (tabPos == -1 || x->tabPos() == tabPos)
00764 ret->append(x);
00765 }
00766 delete l;
00767 return ret;
00768 }
00769
00770 template<class X> X* MainImpl::firstTab(int startPos) {
00771
00772 int minVal = 99, firstVal = 99;
00773 X* min = NULL;
00774 X* first = NULL;
00775 QPtrList<X>* l = getTabs<X>();
00776 for (X* d = l->first(); d; d = l->next()) {
00777
00778 if (d->tabPos() < minVal) {
00779 minVal = d->tabPos();
00780 min = d;
00781 }
00782 if (d->tabPos() < firstVal && d->tabPos() > startPos) {
00783 firstVal = d->tabPos();
00784 first = d;
00785 }
00786 }
00787 delete l;
00788 return first ? first : min;
00789 }
00790
00791 void MainImpl::tabWdg_currentChanged(QWidget* w) {
00792
00793 if (w == NULL)
00794 return;
00795
00796
00797 Domain* t;
00798 switch (currentTabType(&t)) {
00799 case TAB_REV:
00800 static_cast<RevsView*>(t)->tab()->listViewLog->setFocus();
00801 emit closeTabButtonEnabled(false);
00802 break;
00803 case TAB_PATCH:
00804 static_cast<PatchView*>(t)->tab()->textEditDiff->setFocus();
00805 emit closeTabButtonEnabled(true);
00806 break;
00807 case TAB_FILE:
00808 static_cast<FileView*>(t)->tab()->histListView->setFocus();
00809 emit closeTabButtonEnabled(true);
00810 break;
00811 default:
00812 dbs("ASSERT in tabWdg_currentChanged: unknown current page");
00813 break;
00814 }
00815 }
00816
00817 void MainImpl::accelActivated(int id) {
00818
00819 switch (id) {
00820 case KEY_UP:
00821 scrollListView(-1);
00822 break;
00823 case KEY_DOWN:
00824 scrollListView(1);
00825 break;
00826 case SHIFT_KEY_UP:
00827 goMatch(-1);
00828 break;
00829 case SHIFT_KEY_DOWN:
00830 goMatch(1);
00831 break;
00832 case KEY_LEFT:
00833 ActBack_activated();
00834 break;
00835 case KEY_RIGHT:
00836 ActForward_activated();
00837 break;
00838 case CTRL_PLUS:
00839 adjustFontSize(1);
00840 break;
00841 case CTRL_MINUS:
00842 adjustFontSize(-1);
00843 break;
00844 case KEY_U:
00845 scrollTextEdit(-18);
00846 break;
00847 case KEY_D:
00848 scrollTextEdit(18);
00849 break;
00850 case KEY_DELETE:
00851 case KEY_B:
00852 case KEY_BCKSPC:
00853 scrollTextEdit(-1);
00854 break;
00855 case KEY_SPACE:
00856 scrollTextEdit(1);
00857 break;
00858 case KEY_R:
00859 tabWdg->setCurrentPage(rv->tabPos());
00860 break;
00861 case KEY_P:
00862 case KEY_F:{
00863 int cp = tabWdg->currentPageIndex();
00864 Domain* d = (id == KEY_P) ? static_cast<Domain*>(firstTab<PatchView>(cp)) :
00865 static_cast<Domain*>(firstTab<FileView>(cp));
00866 if (d)
00867 tabWdg->setCurrentPage(d->tabPos()); }
00868 break;
00869 }
00870 }
00871
00872 void MainImpl::setupAccelerator(QAccel* accel) {
00873
00874 accel->insertItem(Key_Up, KEY_UP);
00875 accel->insertItem(Key_I, KEY_UP);
00876 accel->insertItem(Key_Down, KEY_DOWN);
00877 accel->insertItem(Key_N, KEY_DOWN);
00878 accel->insertItem(Key_K, KEY_DOWN);
00879 accel->insertItem(Key_Left, KEY_LEFT);
00880 accel->insertItem(Key_Right, KEY_RIGHT);
00881 accel->insertItem(SHIFT+Key_Up, SHIFT_KEY_UP);
00882 accel->insertItem(SHIFT+Key_Down, SHIFT_KEY_DOWN);
00883 accel->insertItem(CTRL+Key_Plus, CTRL_PLUS);
00884 accel->insertItem(CTRL+Key_Minus, CTRL_MINUS);
00885 accel->insertItem(Key_U, KEY_U);
00886 accel->insertItem(Key_D, KEY_D);
00887 accel->insertItem(Key_Delete, KEY_DELETE);
00888 accel->insertItem(Key_B, KEY_B);
00889 accel->insertItem(Key_Backspace, KEY_BCKSPC);
00890 accel->insertItem(Key_Space, KEY_SPACE);
00891 accel->insertItem(Key_R, KEY_R);
00892 accel->insertItem(Key_P, KEY_P);
00893 accel->insertItem(Key_F, KEY_F);
00894
00895 connect(accel, SIGNAL(activated(int)), this, SLOT(accelActivated(int)));
00896 }
00897
00898 void MainImpl::goMatch(int delta) {
00899
00900 if (!toolButtonBold->isOn())
00901 return;
00902
00903 QListViewItemIterator it(rv->tab()->listViewLog->currentItem());
00904 if (delta > 0)
00905 ++it;
00906 else
00907 --it;
00908
00909 while (it.current()) {
00910 ListViewItem* item = static_cast<ListViewItem*>(it.current());
00911 if (item->highlighted()) {
00912 QListView* lv = rv->tab()->listViewLog;
00913 lv->clearSelection();
00914 lv->setCurrentItem(item);
00915 lv->ensureItemVisible(lv->currentItem());
00916 return;
00917 }
00918 if (delta > 0)
00919 ++it;
00920 else
00921 --it;
00922 }
00923 }
00924
00925 QTextEdit* MainImpl::getCurrentTextEdit() {
00926
00927 QTextEdit* te = NULL;
00928 Domain* t;
00929 switch (currentTabType(&t)) {
00930 case TAB_REV:
00931 te = static_cast<RevsView*>(t)->tab()->textBrowserDesc;
00932 break;
00933 case TAB_PATCH:
00934 te = static_cast<PatchView*>(t)->tab()->textEditDiff;
00935 break;
00936 case TAB_FILE:
00937 te = static_cast<FileView*>(t)->tab()->textEditFile;
00938 break;
00939 default:
00940 break;
00941 }
00942 return te;
00943 }
00944
00945 void MainImpl::scrollTextEdit(int delta) {
00946
00947 QTextEdit* te = getCurrentTextEdit();
00948 if (!te)
00949 return;
00950
00951 int h = te->visibleHeight();
00952 int ls = te->fontMetrics().lineSpacing();
00953 if (delta == 1 || delta == -1) {
00954 te->scrollBy(0, delta * (h - ls));
00955 return;
00956 }
00957 te->scrollBy(0, delta * ls);
00958 }
00959
00960 void MainImpl::scrollListView(int delta) {
00961
00962 QWidget* lv = NULL;
00963 Domain* t;
00964 switch (currentTabType(&t)) {
00965 case TAB_REV:
00966 lv = static_cast<RevsView*>(t)->tab()->listViewLog;
00967 break;
00968 case TAB_FILE:
00969 lv = static_cast<FileView*>(t)->tab()->histListView;
00970 break;
00971 default:
00972 lv = qApp->focusWidget();
00973 break;
00974 }
00975 if (!lv)
00976 return;
00977
00978 int key = (delta == 1) ? Key_Down : Key_Up;
00979 QKeyEvent p(QEvent::KeyPress, key, 0, 0);
00980 QKeyEvent r(QEvent::KeyRelease, key, 0, 0);
00981 QApplication::sendEvent(lv, &p);
00982 QApplication::sendEvent(lv, &r);
00983 }
00984
00985 void MainImpl::adjustFontSize(int delta) {
00986
00987
00988 int ps = listViewFont.pointSize() + delta;
00989 if (ps < 2)
00990 return;
00991
00992 listViewFont.setPointSize(ps);
00993 emit repaintListViews(listViewFont);
00994 }
00995
00996
00997
00998 void MainImpl::updateCommitMenu(bool isStGITStack) {
00999
01000 int i = 0;
01001 bool found = false;
01002 while (!found && Edit->idAt(i) != -1) {
01003 SCRef txt(Edit->text(Edit->idAt(i++)));
01004 found = (txt == "&Commit..." || txt == "St&GIT patch...");
01005 }
01006 if (!found)
01007 return;
01008
01009 const QString newText(isStGITStack ? "St&GIT patch..." : "&Commit...");
01010 Edit->changeItem(Edit->idAt(--i), newText);
01011 }
01012
01013 void MainImpl::updateRecentRepoMenu(SCRef newEntry) {
01014
01015
01016 QWidgetList* list = QApplication::topLevelWidgets();
01017 QWidgetListIt it(*list);
01018 while (it.current() != 0) {
01019 MainImpl* w = dynamic_cast<MainImpl*>(it.current());
01020 if (w)
01021 w->doUpdateRecentRepoMenu(newEntry);
01022 ++it;
01023 }
01024 delete list;
01025 }
01026
01027 void MainImpl::doUpdateRecentRepoMenu(SCRef newEntry) {
01028
01029 while (File->idAt(recentRepoMenuPos) != -1)
01030 File->removeItemAt(recentRepoMenuPos);
01031
01032 QSettings settings;
01033 SCRef r(settings.readEntry(APP_KEY + REC_REP_KEY, ""));
01034 if (r.isEmpty() && newEntry.isEmpty())
01035 return;
01036
01037 QStringList recents(QStringList::split(',', r));
01038 QStringList::iterator it = recents.find(newEntry);
01039 if (it != recents.end())
01040 recents.remove(it);
01041
01042 if (!newEntry.isEmpty())
01043 recents.prepend(newEntry);
01044
01045 File->insertSeparator();
01046
01047 QStringList::const_iterator it2 = recents.constBegin();
01048 for (int i = 1; it2 != recents.constEnd() && i <= MAX_RECENT_REPOS; ++it2, ++i)
01049 File->insertItem(QString::number(i) + " " + *it2);
01050
01051 for (int i = recents.count() - MAX_RECENT_REPOS; i > 0; i--)
01052 recents.pop_back();
01053
01054 settings.writeEntry(APP_KEY + REC_REP_KEY, recents.join(","));
01055 }
01056
01057 void MainImpl::doContexPopup(SCRef sha) {
01058
01059
01060
01061 delete contextMenu;
01062 delete contextSubMenu;
01063 delete contextRmtMenu;
01064 contextMenu = new QPopupMenu(this);
01065 contextSubMenu = new QPopupMenu(this);
01066 contextRmtMenu = new QPopupMenu(this);
01067 connect(contextMenu, SIGNAL(activated(int)), this, SLOT(on_goRef_activated(int)));
01068 connect(contextSubMenu, SIGNAL(activated(int)), this, SLOT(on_goRef_activated(int)));
01069 connect(contextRmtMenu, SIGNAL(activated(int)), this, SLOT(on_goRef_activated(int)));
01070
01071 Domain* t;
01072 int tt = currentTabType(&t);
01073 bool isRevPage = (tt == TAB_REV);
01074 bool isPatchPage = (tt == TAB_PATCH);
01075 bool isFilePage = (tt == TAB_FILE);
01076
01077 if (!isFilePage && ActCheckWorkDir->isEnabled()) {
01078 ActCheckWorkDir->addTo(contextMenu);
01079 contextMenu->insertSeparator();
01080 }
01081 if (isFilePage && ActViewRev->isEnabled())
01082 ActViewRev->addTo(contextMenu);
01083
01084 if (!isPatchPage && ActViewDiff->isEnabled())
01085 ActViewDiff->addTo(contextMenu);
01086
01087 if (isRevPage && ActViewDiffNewTab->isEnabled())
01088 ActViewDiffNewTab->addTo(contextMenu);
01089
01090 if (!isFilePage && ActExternalDiff->isEnabled())
01091 ActExternalDiff->addTo(contextMenu);
01092
01093 if (isRevPage) {
01094 if (ActCommit->isEnabled() && (sha == ZERO_SHA))
01095 ActCommit->addTo(contextMenu);
01096 if (ActTag->isEnabled())
01097 ActTag->addTo(contextMenu);
01098 if (ActTagDelete->isEnabled())
01099 ActTagDelete->addTo(contextMenu);
01100 if (ActMailFormatPatch->isEnabled())
01101 ActMailFormatPatch->addTo(contextMenu);
01102 if (ActPush->isEnabled())
01103 ActPush->addTo(contextMenu);
01104 if (ActPop->isEnabled())
01105 ActPop->addTo(contextMenu);
01106
01107 const QStringList& bn(git->getAllRefNames(Git::BRANCH, Git::optOnlyLoaded));
01108 const QStringList& rbn(git->getAllRefNames(Git::RMT_BRANCH, Git::optOnlyLoaded));
01109 const QStringList& tn(git->getAllRefNames(Git::TAG, Git::optOnlyLoaded));
01110 if (bn.empty() && rbn.empty() && tn.empty()) {
01111 contextMenu->exec(QCursor::pos());
01112 return;
01113 }
01114 int id = 1;
01115 if (!rbn.empty()) {
01116 QStringList::const_iterator it = rbn.constBegin();
01117 for ( ; it != rbn.constEnd(); ++it, id++)
01118 contextRmtMenu->insertItem(*it, id);
01119 }
01120 int rbnCnt = contextRmtMenu->count();
01121 if (rbnCnt > 0)
01122 contextMenu->insertItem("Remote branches", contextRmtMenu);
01123
01124 if (!bn.empty()) {
01125 contextMenu->insertSeparator();
01126 QStringList::const_iterator it = bn.constBegin();
01127 for ( ; it != bn.constEnd(); ++it, id++) {
01128
01129 if (id < MAX_MENU_ENTRIES + rbnCnt)
01130 contextMenu->insertItem(*it, id);
01131 else
01132 contextSubMenu->insertItem(*it, id);
01133 }
01134 }
01135 if (!tn.empty()) {
01136 contextMenu->insertSeparator();
01137 QStringList::const_iterator it = tn.constBegin();
01138 for ( ; it != tn.constEnd(); ++it, id++) {
01139
01140 if (id < MAX_MENU_ENTRIES + rbnCnt)
01141 contextMenu->insertItem(*it, id);
01142 else
01143 contextSubMenu->insertItem(*it, id);
01144 }
01145 }
01146 if (contextSubMenu->count() > 0)
01147 contextMenu->insertItem("More...", contextSubMenu);
01148 }
01149 contextMenu->popup(QCursor::pos());
01150 }
01151
01152 void MainImpl::doFileContexPopup(SCRef fileName, int type) {
01153
01154 QPopupMenu contextMenu;
01155
01156 Domain* t;
01157 int tt = currentTabType(&t);
01158 bool isRevPage = (tt == TAB_REV);
01159 bool isPatchPage = (tt == TAB_PATCH);
01160 bool isDir = rv->treeView->isDir(fileName);
01161
01162 if (type == POPUP_FILE_EV)
01163 if (!isPatchPage && ActViewDiff->isEnabled())
01164 ActViewDiff->addTo(&contextMenu);
01165
01166 if (!isDir && ActViewFile->isEnabled())
01167 ActViewFile->addTo(&contextMenu);
01168
01169 if (!isDir && ActViewFileNewTab->isEnabled())
01170 ActViewFileNewTab->addTo(&contextMenu);
01171
01172 if (!isRevPage && (type == POPUP_FILE_EV) && ActViewRev->isEnabled())
01173 ActViewRev->addTo(&contextMenu);
01174
01175 if (ActFilterTree->isEnabled())
01176 ActFilterTree->addTo(&contextMenu);
01177
01178 if (!isDir) {
01179 if (ActSaveFile->isEnabled())
01180 ActSaveFile->addTo(&contextMenu);
01181 if ((type == POPUP_FILE_EV) && ActExternalDiff->isEnabled())
01182 ActExternalDiff->addTo(&contextMenu);
01183 }
01184 contextMenu.exec(QCursor::pos());
01185 }
01186
01187 void MainImpl::on_goRef_activated(int id) {
01188
01189 if (id <= 0)
01190 return;
01191
01192 SCRef refSha(git->getRefSha(contextMenu->text(id)));
01193 rv->st.setSha(refSha);
01194 UPDATE_DOMAIN(rv);
01195 }
01196
01197 void MainImpl::ActSplitView_activated() {
01198
01199 Domain* t;
01200 switch (currentTabType(&t)) {
01201 case TAB_REV: {
01202 RevsView* rv = static_cast<RevsView*>(t);
01203 QWidget* w = rv->tab()->textBrowserDesc;
01204 QSplitter* sp = static_cast<QSplitter*>(w->parent());
01205 sp->setHidden(w->isVisible()); }
01206 break;
01207 case TAB_PATCH: {
01208 PatchView* pv = static_cast<PatchView*>(t);
01209 QWidget* w = pv->tab()->textBrowserDesc;
01210 w->setHidden(w->isVisible()); }
01211 break;
01212 case TAB_FILE: {
01213 FileView* fv = static_cast<FileView*>(t);
01214 QWidget* w = fv->tab()->histListView;
01215 w->setHidden(w->isVisible()); }
01216 break;
01217 default:
01218 dbs("ASSERT in ActSplitView_activated: unknown current page");
01219 break;
01220 }
01221 }
01222
01223 void MainImpl::ActShowDescHeader_toggled(bool) {
01224
01225 emit updateRevDesc();
01226 }
01227
01228 void MainImpl::ActShowTree_toggled(bool b) {
01229
01230 if (b) {
01231 treeView->show();
01232 UPDATE_DOMAIN(rv);
01233 } else
01234 treeView->hide();
01235 }
01236
01237 void MainImpl::ActSaveFile_activated() {
01238
01239 QFileInfo f(rv->st.fileName());
01240 const QString fileName(QFileDialog::getSaveFileName(f.fileName(), "",
01241 this, "save file dialog", "Save file as"));
01242
01243 if (fileName.isEmpty())
01244 return;
01245
01246 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
01247
01248 if (!git->saveFile(rv->st.fileName(), rv->st.sha(), fileName))
01249 statusBar()->message("Unable to save " + fileName);
01250
01251 QApplication::restoreOverrideCursor();
01252 }
01253
01254 void MainImpl::on_openRecent_activated(int id) {
01255
01256 bool ok;
01257 File->text(id).left(1).toInt(&ok);
01258 if (!ok)
01259 return;
01260
01261 const QString workDir(File->text(id).section(' ', 1));
01262 if (!workDir.isEmpty())
01263 setRepository(workDir, false, false);
01264 }
01265
01266 void MainImpl::ActOpenRepo_activated() {
01267
01268 const QString dirName(QFileDialog::getExistingDirectory(curDir,
01269 this, "", "Choose a directory"));
01270
01271 if (!dirName.isEmpty()) {
01272 QDir d(dirName);
01273 setRepository(d.absPath(), false, false);
01274 }
01275 }
01276
01277 void MainImpl::ActOpenRepoNewWindow_activated() {
01278
01279 const QString dirName(QFileDialog::getExistingDirectory(curDir,
01280 this, "", "Choose a directory"));
01281
01282 if (!dirName.isEmpty()) {
01283 QDir d(dirName);
01284 MainImpl* newWin = new MainImpl(d.absPath());
01285 newWin->show();
01286 }
01287 }
01288
01289 void MainImpl::refreshRepo(bool b) {
01290
01291 setRepository(curDir, true, b);
01292 }
01293
01294 void MainImpl::ActRefresh_activated() {
01295
01296 refreshRepo(true);
01297 }
01298
01299 void MainImpl::ActMailFormatPatch_activated() {
01300
01301 if (rv->tab()->listViewLog->childCount() == 0)
01302 return;
01303
01304 if (rv->tab()->listViewLog->currentItem() == NULL) {
01305 statusBar()->message("At least one selected revision needed");
01306 return;
01307 }
01308 QStringList selectedItems;
01309 rv->listViewLog->getSelectedItems(selectedItems);
01310 if (selectedItems.contains(ZERO_SHA)) {
01311 statusBar()->message("Unable to format patch for not committed content");
01312 return;
01313 }
01314 QSettings settings;
01315 QString outDir(settings.readEntry(APP_KEY + FP_DIR_KEY, curDir));
01316 QString dirPath(QFileDialog::getExistingDirectory(outDir, this, "",
01317 "Choose destination directory - Format Patch"));
01318 if (dirPath.isEmpty())
01319 return;
01320
01321 QDir d(dirPath);
01322 settings.writeEntry(APP_KEY + FP_DIR_KEY, d.absPath());
01323 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
01324 git->formatPatch(selectedItems, d.absPath());
01325 QApplication::restoreOverrideCursor();
01326 }
01327
01328 bool MainImpl::askApplyPatchParameters(bool* workDirOnly, bool* fold) {
01329
01330 int ret = 0;
01331 if (!git->isStGITStack()) {
01332 ret = QMessageBox::question(this, "Apply Patch",
01333 "Do you want to commit or just to apply changes to "
01334 "working directory?", "&Cancel", "&Working dir", "&Commit", 0, 0);
01335 *workDirOnly = (ret == 1);
01336 *fold = false;
01337 } else {
01338 ret = QMessageBox::question(this, "Apply Patch", "Do you want to "
01339 "import or fold the patch?", "&Cancel", "&Fold", "&Import", 0, 0);
01340 *workDirOnly = false;
01341 *fold = (ret == 1);
01342 }
01343 return (ret != 0);
01344 }
01345
01346 void MainImpl::ActMailApplyPatch_activated() {
01347
01348 QSettings settings;
01349 QString outDir(settings.readEntry(APP_KEY + FP_DIR_KEY, curDir));
01350 QString patchName(QFileDialog::getOpenFileName(outDir, NULL, this,
01351 "", "Choose the patch file - Apply Patch"));
01352 if (patchName.isEmpty())
01353 return;
01354
01355 QFileInfo f(patchName);
01356 settings.writeEntry(APP_KEY + FP_DIR_KEY, f.dirPath(true));
01357
01358 bool workDirOnly, fold;
01359 if (!askApplyPatchParameters(&workDirOnly, &fold))
01360 return;
01361
01362 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
01363
01364 bool ok = git->applyPatchFile(f.absFilePath(), fold, !Git::optDragDrop);
01365 if (workDirOnly && ok)
01366 git->resetCommits(1);
01367
01368 QApplication::restoreOverrideCursor();
01369 refreshRepo(false);
01370 }
01371
01372 void MainImpl::ActCheckWorkDir_toggled(bool b) {
01373
01374 if (!ActCheckWorkDir->isEnabled())
01375 return;
01376
01377 setFlag(DIFF_INDEX_F, b);
01378 bool keepSelection = (rv->st.sha() != ZERO_SHA);
01379 refreshRepo(keepSelection);
01380 }
01381
01382 void MainImpl::ActSettings_activated() {
01383
01384 SettingsImpl* setView = new SettingsImpl(this, git);
01385 setView->exec();
01386
01387
01388 if (ActCheckWorkDir->isOn() != testFlag(DIFF_INDEX_F))
01389 ActCheckWorkDir->toggle();
01390 }
01391
01392 void MainImpl::ActCustomActionSetup_activated() {
01393
01394 CustomActionImpl* ca = new CustomActionImpl();
01395
01396 connect(this, SIGNAL(closeAllWindows()), ca, SLOT(close()));
01397
01398 connect(ca, SIGNAL(listChanged(const QStringList&)),
01399 this, SLOT(on_customActionListChanged(const QStringList&)));
01400
01401 ca->show();
01402 }
01403
01404 void MainImpl::on_customActionListChanged(const QStringList& list) {
01405
01406
01407 QWidgetList* l = QApplication::topLevelWidgets();
01408 QWidgetListIt it(*l);
01409 while (it.current() != 0) {
01410 MainImpl* w = dynamic_cast<MainImpl*>(it.current());
01411 if (w)
01412 w->doUpdateCustomActionMenu(list);
01413 ++it;
01414 }
01415 delete l;
01416 }
01417
01418 void MainImpl::doUpdateCustomActionMenu(const QStringList& list) {
01419
01420 while (Actions->idAt(1) != -1)
01421 Actions->removeItemAt(1);
01422
01423 if (list.isEmpty())
01424 return;
01425
01426 Actions->insertSeparator();
01427 FOREACH_SL (it, list)
01428 Actions->insertItem(*it);
01429 }
01430
01431 void MainImpl::on_customAction_activated(int id) {
01432
01433 const QString name(Actions->text(id));
01434 if (name == "Setup actions...")
01435 return;
01436
01437 QSettings set;
01438 const QStringList sl(QStringList::split(",", set.readEntry(APP_KEY + MCR_LIST_KEY, "")));
01439
01440 if (sl.findIndex(name) == -1) {
01441 dbp("ASSERT in on_customAction_activated, action %1 not found", name);
01442 return;
01443 }
01444 const QString header("Macro " + name + "/");
01445 QString cmdArgs;
01446
01447 if (testFlag(MCR_CMD_LINE_F, header)) {
01448 bool ok;
01449 cmdArgs = QInputDialog::getText("Run action - QGit", "Enter command line "
01450 "arguments for '" + name + "'", QLineEdit::Normal, "", &ok, this);
01451 cmdArgs.prepend(' ');
01452 if (!ok)
01453 return;
01454 }
01455 SCRef cmd = set.readEntry(APP_KEY + header + MCR_TEXT_KEY, "");
01456 if (cmd.isEmpty())
01457 return;
01458
01459 ConsoleImpl* c = new ConsoleImpl(name, git);
01460
01461 connect(this, SIGNAL(closeAllWindows()), c, SLOT(close()));
01462
01463 connect(c, SIGNAL(customAction_exited(const QString&)),
01464 this, SLOT(on_customAction_exited(const QString&)));
01465
01466 QStringList selectedItems, selectedItemsReversed;
01467 rv->listViewLog->getSelectedItems(selectedItems);
01468
01469 FOREACH_SL(it, selectedItems)
01470 selectedItemsReversed.prepend(*it);
01471
01472
01473
01474
01475 QStringList env;
01476 env.append("QGIT_CURRENT_REVISION=" + lineEditSHA->text());
01477 env.append("QGIT_SELECTED_REVISIONS=" + selectedItems.join(" "));
01478 env.append("QGIT_SELECTED_REVISIONS_REVERSE=" + selectedItemsReversed.join(" "));
01479
01480 if (c->start(cmd, cmdArgs, &env))
01481 c->show();
01482 }
01483
01484 void MainImpl::on_customAction_exited(const QString& name) {
01485
01486 const QString header("Macro " + name + "/");
01487 if (testFlag(MCR_REFRESH_F, header))
01488 QTimer::singleShot(10, this, SLOT(refreshRepo()));
01489 }
01490
01491 void MainImpl::ActCommit_activated() {
01492
01493 CommitImpl* c = new CommitImpl(git);
01494
01495 connect(this, SIGNAL(closeAllWindows()), c, SLOT(close()));
01496 connect(c, SIGNAL(changesCommitted(bool)), this, SLOT(on_changesCommitted(bool)));
01497 c->show();
01498 }
01499
01500 void MainImpl::on_changesCommitted(bool ok) {
01501
01502 if (ok)
01503 refreshRepo(false);
01504 else
01505 statusBar()->message("Failed to commit changes");
01506 }
01507
01508 void MainImpl::ActCommit_setEnabled(bool b) {
01509
01510
01511
01512 if (b) {
01513 ActPush->setEnabled(false);
01514 ActPop->setEnabled(false);
01515 }
01516 ActCommit->setEnabled(b);
01517 }
01518
01519 void MainImpl::ActTag_activated() {
01520
01521 int adj = -1;
01522 QString tag(rv->tab()->listViewLog->currentItem()->text(LOG_COL + adj));
01523 bool ok;
01524 tag = QInputDialog::getText("Make tag - QGit", "Enter tag name:",
01525 QLineEdit::Normal, tag, &ok, this);
01526 if (!ok || tag.isEmpty())
01527 return;
01528
01529 QString tmp(tag.simplifyWhiteSpace());
01530 if (tag != tmp.remove(' ')) {
01531 QMessageBox::warning(this, "Make tag - QGit",
01532 "Sorry, control characters or spaces\n"
01533 "are not allowed in tag name.");
01534 return;
01535 }
01536 if (!git->getRefSha(tag, Git::TAG, false).isEmpty()) {
01537 QMessageBox::warning(this, "Make tag - QGit",
01538 "Sorry, tag name already exists.\n"
01539 "Please choose a different name.");
01540 return;
01541 }
01542 QString msg(QInputDialog::getText("Make tag - QGit",
01543 "Enter tag message, if any:", QLineEdit::Normal, "", &ok, this));
01544
01545 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
01546 ok = git->makeTag(lineEditSHA->text(), tag, msg);
01547 QApplication::restoreOverrideCursor();
01548 if (ok)
01549 refreshRepo(true);
01550 else
01551 statusBar()->message("Sorry, unable to tag the revision");
01552 }
01553
01554 void MainImpl::ActTagDelete_activated() {
01555
01556 if (QMessageBox::question(this, "Delete tag - QGit",
01557 "Do you want to un-tag selected revision?",
01558 "&Yes", "&No", QString::null, 0, 1) == 1)
01559 return;
01560
01561 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
01562 bool ok = git->deleteTag(lineEditSHA->text());
01563 QApplication::restoreOverrideCursor();
01564 if (ok)
01565 refreshRepo(true);
01566 else
01567 statusBar()->message("Sorry, unable to un-tag the revision");
01568 }
01569
01570 void MainImpl::ActPush_activated() {
01571
01572 QStringList selectedItems;
01573 rv->listViewLog->getSelectedItems(selectedItems);
01574 for (uint i = 0; i < selectedItems.count(); i++) {
01575 if (!git->checkRef(selectedItems[i], Git::UN_APPLIED)) {
01576 statusBar()->message("Please, select only unapplied patches");
01577 return;
01578 }
01579 }
01580 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
01581 bool ok = true;
01582 for (uint i = 0; i < selectedItems.count(); i++) {
01583 const QString tmp(QString("Pushing patch %1 of %2")
01584 .arg(i+1).arg(selectedItems.count()));
01585 statusBar()->message(tmp);
01586 SCRef sha = selectedItems[selectedItems.count() - i - 1];
01587 if (!git->stgPush(sha)) {
01588 statusBar()->message("Failed to push patch " + sha);
01589 ok = false;
01590 break;
01591 }
01592 }
01593 if (ok)
01594 statusBar()->clear();
01595
01596 QApplication::restoreOverrideCursor();
01597 refreshRepo(false);
01598 }
01599
01600 void MainImpl::ActPop_activated() {
01601
01602 QStringList selectedItems;
01603 rv->listViewLog->getSelectedItems(selectedItems);
01604 if (selectedItems.count() > 1) {
01605 statusBar()->message("Please, select one revision only");
01606 return;
01607 }
01608 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
01609 git->stgPop(selectedItems[0]);
01610 QApplication::restoreOverrideCursor();
01611 refreshRepo(false);
01612 }
01613
01614 void MainImpl::ActFilterTree_toggled(bool b) {
01615
01616 if (!ActFilterTree->isEnabled()) {
01617 dbs("ASSERT ActFilterTree_toggled while disabled");
01618 return;
01619 }
01620 if (b) {
01621 QStringList selectedItems;
01622 if (!treeView->isVisible())
01623 rv->treeView->update();
01624
01625 rv->treeView->getTreeSelectedItems(selectedItems);
01626 if (selectedItems.count() == 0) {
01627 dbs("ASSERT tree filter action activated with no selected items");
01628 return;
01629 }
01630 statusBar()->message("Filter view on " + selectedItems.join(" "));
01631 setRepository(curDir, true, true, &selectedItems);
01632 } else
01633 refreshRepo(true);
01634 }
01635
01636 void MainImpl::ActFindNext_activated() {
01637
01638 QTextEdit* te = getCurrentTextEdit();
01639 if (!te || textToFind.isEmpty())
01640 return;
01641
01642 bool endOfDocument = false;
01643 while (true) {
01644 if (te->find(textToFind, false, false))
01645 return;
01646
01647 if (endOfDocument) {
01648 QMessageBox::warning(this, "Find text - QGit", "Text \"" +
01649 textToFind + "\" not found!", QMessageBox::Ok, 0);
01650 return;
01651 }
01652 if (QMessageBox::question(this, "Find text - QGit", "End of document "
01653 "reached\n\nDo you want to continue from beginning?", QMessageBox::Yes,
01654 QMessageBox::No | QMessageBox::Escape) == QMessageBox::No)
01655 return;
01656
01657 endOfDocument = true;
01658 te->setCursorPosition(0, 0);
01659 }
01660 }
01661
01662 void MainImpl::ActFind_activated() {
01663
01664 QTextEdit* te = getCurrentTextEdit();
01665 if (!te)
01666 return;
01667
01668 QString def(textToFind);
01669 if (te->hasSelectedText()) {
01670 TextFormat tf = te->textFormat();
01671 te->setTextFormat(Qt::PlainText);
01672 def = te->selectedText().section('\n', 0, 0);
01673 te->setTextFormat(tf);
01674 }
01675 bool ok;
01676 QString str(QInputDialog::getText("Find text - QGit", "Text to find:",
01677 QLineEdit::Normal, def, &ok, this));
01678 if (!ok || str.isEmpty())
01679 return;
01680
01681 textToFind = str;
01682 ActFindNext_activated();
01683 }
01684
01685 void MainImpl::ActHelp_activated() {
01686
01687
01688 HelpBase* helpDlg = new HelpBase(NULL, 0, Qt::WDestructiveClose);
01689
01690 connect(this, SIGNAL(closeAllWindows()), helpDlg, SLOT(close()));
01691
01692 helpDlg->textEditHelp->setText(QString::fromLatin1(helpInfo));
01693 helpDlg->show();
01694 helpDlg->raise();
01695 }
01696
01697 void MainImpl::ActAbout_activated() {
01698
01699 static const char* aboutMsg =
01700 "<center><p><b>QGit version " PACKAGE_VERSION "</b></p><br>"
01701 "<p>Copyright (c) 2005, 2006 Marco Costalba</p>"
01702 "<p>Use and redistribute under the "
01703 "terms of the GNU General Public License</p></center>";
01704 QMessageBox::about(this, "About QGit", QString::fromLatin1(aboutMsg));
01705 }
01706
01707 void MainImpl::closeEvent(QCloseEvent* ce) {
01708
01709
01710
01711
01712
01713
01714
01715
01716
01717
01718
01719 emit closeAllWindows();
01720 hide();
01721
01722 EM_RAISE(exExiting);
01723
01724 if (!git->stop(Git::optSaveCache)) {
01725
01726
01727
01728 QTimer::singleShot(100, this, SLOT(ActClose_activated()));
01729 ce->ignore();
01730 return;
01731 }
01732 emit closeAllTabs();
01733 delete rv;
01734 MainBase::closeEvent(ce);
01735 }
01736
01737 void MainImpl::ActClose_activated() {
01738
01739 close();
01740 }
01741
01742 void MainImpl::ActExit_activated() {
01743
01744 qApp->closeAllWindows();
01745 }