00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <qlistview.h>
00012 #include <qsettings.h>
00013 #include <qlabel.h>
00014 #include <qtextedit.h>
00015 #include <qmessagebox.h>
00016 #include <qapplication.h>
00017 #include <qcursor.h>
00018 #include <qsplitter.h>
00019 #include <qpushbutton.h>
00020 #include <qinputdialog.h>
00021 #include <qpopupmenu.h>
00022 #include <qregexp.h>
00023 #include <qtooltip.h>
00024 #include <qstylesheet.h>
00025 #include <qtextcodec.h>
00026 #include "exceptionmanager.h"
00027 #include "common.h"
00028 #include "git.h"
00029 #include "settingsimpl.h"
00030 #include "commitimpl.h"
00031
00032 using namespace QGit;
00033
00034
00035
00036 class CheckListFileItem: public QCheckListItem {
00037 public:
00038 CheckListFileItem(QListView* lv, SCRef file, const RevFile* f, int i) :
00039 QCheckListItem(lv, file, QCheckListItem::CheckBox) {
00040
00041 bool inIndex = f->statusCmp(i, RevFile::IN_INDEX);
00042 bool isNew = (f->statusCmp(i, RevFile::NEW) || f->statusCmp(i, RevFile::UNKNOWN));
00043 setText(1, inIndex ? "In sync" : "Out of sync");
00044 setOn(inIndex || !isNew);
00045 myColor = Qt::black;
00046 if (isNew)
00047 myColor = Qt::darkGreen;
00048
00049 else if (f->statusCmp(i, RevFile::DELETED))
00050 myColor = Qt::red;
00051 }
00052 virtual void paintCell(QPainter *p, const QColorGroup& cg, int column,
00053 int width, int alignment) {
00054
00055 QColorGroup _cg(cg);
00056 if (column == 0)
00057 _cg.setColor(QColorGroup::Text, myColor);
00058
00059 QCheckListItem::paintCell(p, _cg, column, width, alignment);
00060 }
00061 private:
00062 QColor myColor;
00063 };
00064
00065
00066
00067 CommitImpl::CommitImpl(Git* g) : CommitBase(0, 0, Qt::WDestructiveClose), git(g) {
00068
00069
00070 listViewFiles->setColumnAlignment(1, Qt::AlignHCenter);
00071 listViewFiles->setColumnAlignment(2, Qt::AlignHCenter);
00072 textEditMsg->setFont(TYPE_WRITER_FONT);
00073
00074
00075 QSettings settings;
00076 QString tmp = settings.readEntry(APP_KEY + CMT_GEOM_KEY, CMT_GEOM_DEF);
00077 QStringList sl = QStringList::split(',', tmp);
00078 QPoint pos(sl[0].toInt(), sl[1].toInt());
00079 QSize size(sl[2].toInt(), sl[3].toInt());
00080 resize(size);
00081 move(pos);
00082 tmp = settings.readEntry(APP_KEY + CMT_SPLIT_KEY, CMT_SPLIT_DEF);
00083 sl = QStringList::split(',', tmp);
00084 QValueList<int> sz;
00085 sz.append(sl[0].toInt());
00086 sz.append(sl[1].toInt());
00087 splitter->setSizes(sz);
00088 tmp = settings.readEntry(APP_KEY + CMT_TEMPL_KEY, CMT_TEMPL_DEF);
00089 QString msg;
00090 QDir d;
00091 if (d.exists(tmp))
00092 readFromFile(tmp, msg);
00093
00094
00095 const RevFile* f = git->getFiles(ZERO_SHA);
00096 for (int i = 0; i < f->count(); ++i)
00097 new CheckListFileItem(listViewFiles, git->filePath(*f, i), f, i);
00098
00099
00100 QString status(git->getDefCommitMsg());
00101 status.prepend('\n').replace(QRegExp("\\n([^#])"), "\n#\\1");
00102 msg.append(status.stripWhiteSpace());
00103 textEditMsg->setText(msg);
00104 textEditMsg->setCursorPosition(0, 0);
00105
00106
00107
00108 origMsg = msg;
00109
00110
00111 if (git->isStGITStack()) {
00112 pushButtonOk->setText("&New patch");
00113 pushButtonOk->setAccel(QKeySequence("Alt+N"));
00114 QToolTip::remove(pushButtonOk);
00115 QToolTip::add(pushButtonOk, "Create a new patch");
00116 pushButtonUpdateCache->setText("&Add to top");
00117 pushButtonOk->setAccel(QKeySequence("Alt+A"));
00118 QToolTip::remove(pushButtonUpdateCache);
00119 QToolTip::add(pushButtonUpdateCache, "Refresh top stack patch");
00120 }
00121
00122 contextMenu = new QPopupMenu(this);
00123 CHECK_ALL = contextMenu->insertItem("Select All");
00124 UNCHECK_ALL = contextMenu->insertItem("Unselect All");
00125 connect(contextMenu, SIGNAL(activated(int)), this, SLOT(checkUncheck(int)));
00126 connect(listViewFiles, SIGNAL(contextMenuRequested(QListViewItem*, const QPoint&, int)),
00127 this, SLOT(contextMenuPopup(QListViewItem*, const QPoint&, int)));
00128 }
00129
00130 CommitImpl::~CommitImpl() {
00131
00132 QSettings settings;
00133 QString tmp = QString("%1,%2,%3,%4").arg(pos().x()).arg(pos().y())
00134 .arg(size().width()).arg(size().height());
00135 settings.writeEntry(APP_KEY + CMT_GEOM_KEY, tmp);
00136 QValueList<int> sz = splitter->sizes();
00137 tmp = QString::number(sz[0]) + "," + QString::number(sz[1]);
00138 settings.writeEntry(APP_KEY + CMT_SPLIT_KEY, tmp);
00139 close();
00140 }
00141
00142 void CommitImpl::contextMenuPopup(QListViewItem*, const QPoint& pos, int) {
00143
00144 contextMenu->popup(pos);
00145 }
00146
00147 void CommitImpl::checkUncheck(int id) {
00148
00149 QListViewItemIterator it(listViewFiles);
00150 while (it.current()) {
00151 ((QCheckListItem*)it.current())->setOn(id == CHECK_ALL);
00152 ++it;
00153 }
00154 }
00155
00156 bool CommitImpl::checkFiles(SList selFiles) {
00157
00158
00159 selFiles.clear();
00160 QListViewItemIterator it(listViewFiles);
00161 while (it.current()) {
00162 if (((QCheckListItem*)it.current())->isOn())
00163 selFiles.append(it.current()->text(0));
00164 ++it;
00165 }
00166 if (selFiles.isEmpty())
00167 QMessageBox::warning(this, "Commit changes - QGit",
00168 "Sorry, no files are selected for updating.",
00169 QMessageBox::Ok, QMessageBox::NoButton);
00170 return !selFiles.isEmpty();
00171 }
00172
00173 bool CommitImpl::checkMsg(QString& msg) {
00174
00175 msg = textEditMsg->text();
00176 msg.remove(QRegExp("(^|\\n)\\s*#[^\\n]*"));
00177 msg.replace(QRegExp("[ \\t\\r\\f\\v]+\\n"), "\n");
00178 msg = msg.stripWhiteSpace();
00179 if (msg.isEmpty()) {
00180 QMessageBox::warning(this, "Commit changes - QGit",
00181 "Sorry, I don't want an empty message.",
00182 QMessageBox::Ok, QMessageBox::NoButton);
00183 return false;
00184 }
00185
00186 QString subj(msg.section('\n', 0, 0, QString::SectionIncludeTrailingSep));
00187 QString body(msg.section('\n', 1).stripWhiteSpace());
00188 msg = subj + '\n' + body + '\n';
00189 return true;
00190 }
00191
00192 bool CommitImpl::checkPatchName(QString& patchName) {
00193
00194 bool ok;
00195 patchName = patchName.simplifyWhiteSpace().stripWhiteSpace();
00196 patchName.replace(' ', "_");
00197 patchName = QInputDialog::getText("Create new patch - QGit", "Enter patch name:",
00198 QLineEdit::Normal, patchName, &ok, this);
00199 if (!ok || patchName.isEmpty())
00200 return false;
00201
00202 QString tmp(patchName.simplifyWhiteSpace());
00203 if (patchName != tmp.remove(' ')) {
00204 QMessageBox::warning(this, "Create new patch - QGit", "Sorry, control "
00205 "characters or spaces\n are not allowed in patch name.");
00206 return false;
00207 }
00208 if (git->isPatchName(patchName)) {
00209 QMessageBox::warning(this, "Create new patch - QGit", "Sorry, patch name "
00210 "already exists.\nPlease choose a different name.");
00211 return false;
00212 }
00213 return true;
00214 }
00215
00216 bool CommitImpl::checkConfirm(SCRef msg, SCRef patchName, SCList selFiles) {
00217
00218 QTextCodec* tc = QTextCodec::codecForCStrings();
00219 QTextCodec::setCodecForCStrings(0);
00220
00221 QString whatToDo = (git->isStGITStack()) ? "create a new patch with" : "commit";
00222 QString text(msg);
00223 text.replace("\n", "\n\t");
00224 text.prepend("Do you want to " + whatToDo + " the following file(s)?\n\n\t" +
00225 selFiles.join("\n\t") +"\n\nwith the message:\n\n\t");
00226
00227 if (git->isStGITStack())
00228 text.append("\n\nAnd patch name: " + patchName);
00229
00230 text = QStyleSheet::convertFromPlainText(text);
00231 QTextCodec::setCodecForCStrings(tc);
00232
00233 if (QMessageBox::question(this, "Commit changes - QGit", text, "&Yes",
00234 "&No", QString::null, 0, 1) == 1)
00235 return false;
00236
00237 return true;
00238 }
00239
00240 void CommitImpl::pushButtonOk_clicked() {
00241
00242 QStringList selFiles;
00243 if (!checkFiles(selFiles))
00244 return;
00245
00246 QString msg;
00247 if (!checkMsg(msg))
00248 return;
00249
00250 QString patchName(msg.section('\n', 0, 0));
00251 if (git->isStGITStack() && !checkPatchName(patchName))
00252 return;
00253
00254 if (!checkConfirm(msg, patchName, selFiles))
00255 return;
00256
00257
00258 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
00259 EM_PROCESS_EVENTS;
00260 bool ok;
00261 if (git->isStGITStack())
00262 ok = git->stgCommit(selFiles, msg, patchName, !Git::optFold);
00263 else
00264 ok = git->commitFiles(selFiles, msg);
00265
00266 QApplication::restoreOverrideCursor();
00267 hide();
00268 emit changesCommitted(ok);
00269 close();
00270 }
00271
00272 void CommitImpl::pushButtonUpdateCache_clicked() {
00273
00274 QStringList selFiles;
00275 if (!checkFiles(selFiles))
00276 return;
00277
00278 if (git->isStGITStack())
00279 if (QMessageBox::question(this, "Refresh stack - QGit",
00280 "Do you want to refresh current top stack patch?",
00281 "&Yes", "&No", QString::null, 0, 1) == 1)
00282 return;
00283
00284 QString msg(textEditMsg->text());
00285 if (msg == origMsg)
00286 msg = "";
00287
00288 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
00289 EM_PROCESS_EVENTS;
00290 bool ok;
00291 if (git->isStGITStack())
00292 ok = git->stgCommit(selFiles, msg, "", Git::optFold);
00293 else
00294 ok = git->updateIndex(selFiles);
00295
00296 QApplication::restoreOverrideCursor();
00297 emit changesCommitted(ok);
00298 close();
00299 }
00300
00301 void CommitImpl::pushButtonSettings_clicked() {
00302
00303 SettingsImpl* setView = new SettingsImpl(this, git, 3);
00304 setView->exec();
00305
00306 }
00307
00308 void CommitImpl::pushButtonCancel_clicked() {
00309
00310 close();
00311 }
00312
00313 void CommitImpl::textEditMsg_cursorPositionChanged(int para, int pos) {
00314
00315 int col_pos, line_pos;
00316 computePosition(para, pos, col_pos, line_pos);
00317 QString lineNumber = QString("Line: %1 Col: %2")
00318 .arg(line_pos + 1).arg(col_pos + 1);
00319 textLabelLineCol->setText(lineNumber);
00320 }
00321
00322
00323
00324
00325
00326
00327 void CommitImpl::computePosition(int line, int col, int &col_pos, int &line_pos) {
00328
00329
00330 line_pos = 0;
00331 if (textEditMsg->wordWrap() == QTextEdit::NoWrap)
00332 line_pos = line;
00333 else {
00334 for (int i = 0; i < line; i++)
00335 line_pos += textEditMsg->linesOfParagraph(i);
00336 }
00337 int line_offset = textEditMsg->lineOfChar(line, col);
00338 line_pos += line_offset;
00339
00340
00341 const QString linetext(QString::number(line));
00342 int start_of_line = 0;
00343 if (line_offset > 0) {
00344 start_of_line = col;
00345 while (textEditMsg->lineOfChar(line, --start_of_line) == line_offset);
00346 start_of_line++;
00347 }
00348
00349
00350
00351
00352
00353
00354 int coltemp = col - start_of_line;
00355 int pos = 0;
00356 int find = 0;
00357 int mem = 0;
00358 bool found_one = false;
00359
00360
00361
00362 while (find >= 0 && find <= coltemp - 1) {
00363 find = linetext.find('\t', find + start_of_line, true) - start_of_line;
00364 if (find >=0 && find <= coltemp - 1) {
00365 found_one = true;
00366 pos = pos + find - mem;
00367 pos = pos + 8 - pos % 8;
00368 mem = find;
00369 find ++;
00370 }
00371 }
00372
00373 pos = pos + coltemp - mem;
00374 if (found_one)
00375 pos = pos - 1;
00376
00377 col_pos = pos;
00378 }