Warning, file /sdk/cervisia/updateview.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * Copyright (C) 1999-2002 Bernd Gehrmann <bernd@mail.berlios.de> 0003 * Copyright (c) 2003-2008 André Wöbbeking <Woebbeking@kde.org> 0004 * 0005 * This program is free software; you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation; either version 2 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program; if not, write to the Free Software 0017 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "updateview.h" 0021 0022 #include <set> 0023 0024 #include <KLocalizedString> 0025 #include <QHeaderView> 0026 #include <kconfiggroup.h> 0027 #include <qapplication.h> 0028 #include <qfileinfo.h> 0029 #include <qstack.h> 0030 0031 #include "cervisiasettings.h" 0032 #include "updateview_items.h" 0033 #include "updateview_visitors.h" 0034 0035 using Cervisia::Entry; 0036 using Cervisia::EntryStatus; 0037 0038 UpdateView::UpdateView(KConfig &partConfig, QWidget *parent) 0039 : QTreeWidget(parent) 0040 , m_partConfig(partConfig) 0041 , m_unfoldingTree(false) 0042 { 0043 setAllColumnsShowFocus(true); 0044 setUniformRowHeights(true); 0045 setRootIsDecorated(false); 0046 header()->setSortIndicatorShown(true); 0047 setSortingEnabled(true); 0048 setSelectionMode(QAbstractItemView::ExtendedSelection); 0049 0050 setHeaderLabels(QStringList() << i18n("File Name") << i18n("Status") << i18n("Revision") << i18n("Tag/Date") << i18n("Timestamp")); 0051 0052 header()->resizeSection(0, 280); 0053 header()->resizeSection(1, 90); 0054 header()->resizeSection(2, 70); 0055 header()->resizeSection(3, 90); 0056 header()->resizeSection(4, 120); 0057 0058 setFilter(NoFilter); 0059 0060 connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(itemExecuted(QTreeWidgetItem *, int))); 0061 0062 connect(this, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(itemExpandedSlot(QTreeWidgetItem *))); 0063 0064 KConfigGroup cg(&m_partConfig, "UpdateView"); 0065 QByteArray state = cg.readEntry<QByteArray>("Columns", QByteArray()); 0066 header()->restoreState(state); 0067 } 0068 0069 UpdateView::~UpdateView() 0070 { 0071 KConfigGroup cg(&m_partConfig, "UpdateView"); 0072 cg.writeEntry("Columns", header()->saveState()); 0073 } 0074 0075 void UpdateView::setFilter(Filter filter) 0076 { 0077 filt = filter; 0078 0079 if (auto item = static_cast<UpdateDirItem *>(topLevelItem(0))) { 0080 ApplyFilterVisitor applyFilterVisitor(filter); 0081 item->accept(applyFilterVisitor); 0082 } 0083 } 0084 0085 UpdateView::Filter UpdateView::filter() const 0086 { 0087 return filt; 0088 } 0089 0090 // returns true iff exactly one UpdateFileItem is selected 0091 bool UpdateView::hasSingleSelection() const 0092 { 0093 const QList<QTreeWidgetItem *> &listSelectedItems(selectedItems()); 0094 0095 return (listSelectedItems.count() == 1) && isFileItem(listSelectedItems.first()); 0096 } 0097 0098 void UpdateView::getSingleSelection(QString *filename, QString *revision) const 0099 { 0100 const QList<QTreeWidgetItem *> &listSelectedItems(selectedItems()); 0101 0102 QString tmpFileName; 0103 QString tmpRevision; 0104 if ((listSelectedItems.count() == 1) && isFileItem(listSelectedItems.first())) { 0105 auto fileItem(static_cast<UpdateFileItem *>(listSelectedItems.first())); 0106 tmpFileName = fileItem->filePath(); 0107 tmpRevision = fileItem->entry().m_revision; 0108 } 0109 0110 *filename = tmpFileName; 0111 if (revision) 0112 *revision = tmpRevision; 0113 } 0114 0115 QStringList UpdateView::multipleSelection() const 0116 { 0117 QStringList res; 0118 0119 const QList<QTreeWidgetItem *> &listSelectedItems(selectedItems()); 0120 foreach (QTreeWidgetItem *item, listSelectedItems) { 0121 if (!item->isHidden()) 0122 res.append(static_cast<UpdateItem *>(item)->filePath()); 0123 } 0124 0125 return res; 0126 } 0127 0128 QStringList UpdateView::fileSelection() const 0129 { 0130 QStringList res; 0131 0132 const QList<QTreeWidgetItem *> &listSelectedItems(selectedItems()); 0133 foreach (QTreeWidgetItem *item, listSelectedItems) { 0134 if (isFileItem(item) && !item->isHidden()) 0135 res.append(static_cast<UpdateFileItem *>(item)->filePath()); 0136 } 0137 0138 return res; 0139 } 0140 0141 const QColor &UpdateView::conflictColor() const 0142 { 0143 return m_conflictColor; 0144 } 0145 0146 const QColor &UpdateView::localChangeColor() const 0147 { 0148 return m_localChangeColor; 0149 } 0150 0151 const QColor &UpdateView::remoteChangeColor() const 0152 { 0153 return m_remoteChangeColor; 0154 } 0155 0156 const QColor &UpdateView::notInCvsColor() const 0157 { 0158 return m_notInCvsColor; 0159 } 0160 0161 bool UpdateView::isUnfoldingTree() const 0162 { 0163 return m_unfoldingTree; 0164 } 0165 0166 // updates internal data 0167 void UpdateView::replaceItem(QTreeWidgetItem *oldItem, QTreeWidgetItem *newItem) 0168 { 0169 const int index(relevantSelection.indexOf(oldItem)); 0170 if (index >= 0) 0171 relevantSelection.replace(index, newItem); 0172 } 0173 0174 void UpdateView::unfoldSelectedFolders() 0175 { 0176 QApplication::setOverrideCursor(Qt::WaitCursor); 0177 0178 int previousDepth = 0; 0179 bool isUnfolded = false; 0180 0181 QStringList selection = multipleSelection(); 0182 0183 // setup name of selected folder 0184 QString selectedItem = selection.first(); 0185 if (selectedItem.contains('/')) 0186 selectedItem.remove(0, selectedItem.lastIndexOf('/') + 1); 0187 0188 // avoid flicker 0189 const bool _updatesEnabled = updatesEnabled(); 0190 setUpdatesEnabled(false); 0191 0192 QTreeWidgetItemIterator it(this); 0193 while (QTreeWidgetItem *item = (*it)) { 0194 if (isDirItem(item)) { 0195 auto dirItem = static_cast<UpdateDirItem *>(item); 0196 0197 // below selected folder? 0198 if (previousDepth && dirItem->depth() > previousDepth) { 0199 // if this dir wasn't scanned already scan it recursive 0200 // (this is only a hack to reduce the processEvents() calls, 0201 // setOpen() would scan the dir too) 0202 if (dirItem->wasScanned() == false) { 0203 const bool recursive = true; 0204 dirItem->maybeScanDir(recursive); 0205 0206 // scanning can take some time so keep the gui alive 0207 qApp->processEvents(); 0208 } 0209 0210 dirItem->setOpen(!isUnfolded); 0211 } 0212 // selected folder? 0213 else if (selectedItem == dirItem->entry().m_name) { 0214 previousDepth = dirItem->depth(); 0215 isUnfolded = dirItem->isExpanded(); 0216 0217 // if this dir wasn't scanned already scan it recursive 0218 // (this is only a hack to reduce the processEvents() calls, 0219 // setOpen() would scan the dir too) 0220 if (dirItem->wasScanned() == false) { 0221 const bool recursive = true; 0222 dirItem->maybeScanDir(recursive); 0223 0224 // scanning can take some time so keep the gui alive 0225 qApp->processEvents(); 0226 } 0227 0228 dirItem->setOpen(!isUnfolded); 0229 } 0230 // back to the level of the selected folder or above? 0231 else if (previousDepth && dirItem->depth() >= previousDepth) { 0232 previousDepth = 0; 0233 } 0234 } 0235 0236 ++it; 0237 } 0238 0239 // maybe some UpdateDirItem was opened the first time so check the whole tree 0240 setFilter(filter()); 0241 0242 setUpdatesEnabled(_updatesEnabled); 0243 viewport()->update(); 0244 0245 QApplication::restoreOverrideCursor(); 0246 } 0247 0248 void UpdateView::unfoldTree() 0249 { 0250 QApplication::setOverrideCursor(Qt::WaitCursor); 0251 0252 m_unfoldingTree = true; 0253 0254 const bool _updatesEnabled = updatesEnabled(); 0255 0256 setUpdatesEnabled(false); 0257 0258 QTreeWidgetItemIterator it(this); 0259 while (QTreeWidgetItem *item = (*it)) { 0260 if (isDirItem(item)) { 0261 auto dirItem(static_cast<UpdateDirItem *>(item)); 0262 0263 // if this dir wasn't scanned already scan it recursive 0264 // (this is only a hack to reduce the processEvents() calls, 0265 // setOpen() would scan the dir too) 0266 if (dirItem->wasScanned() == false) { 0267 const bool recursive(true); 0268 dirItem->maybeScanDir(recursive); 0269 0270 // scanning can take some time so keep the gui alive 0271 qApp->processEvents(); 0272 } 0273 0274 dirItem->setOpen(true); 0275 } 0276 0277 ++it; 0278 } 0279 0280 // maybe some UpdateDirItem was opened the first time so check the whole tree 0281 setFilter(filter()); 0282 0283 setUpdatesEnabled(_updatesEnabled); 0284 0285 viewport()->update(); 0286 0287 m_unfoldingTree = false; 0288 0289 QApplication::restoreOverrideCursor(); 0290 } 0291 0292 void UpdateView::foldTree() 0293 { 0294 QTreeWidgetItemIterator it(this); 0295 while (QTreeWidgetItem *item = (*it)) { 0296 // don't close the top level directory 0297 if (isDirItem(item) && item->parent()) 0298 item->setExpanded(false); 0299 0300 ++it; 0301 } 0302 } 0303 0304 /** 0305 * Clear the tree view and insert the directory dirname 0306 * into it as the new root item 0307 */ 0308 void UpdateView::openDirectory(const QString &dirName) 0309 { 0310 clear(); 0311 0312 // do this each time as the configuration could be changed 0313 updateColors(); 0314 0315 Entry entry; 0316 entry.m_name = dirName; 0317 entry.m_type = Entry::Dir; 0318 0319 auto item = new UpdateDirItem(this, entry); 0320 item->setExpanded(true); 0321 setCurrentItem(item); 0322 item->setSelected(true); 0323 } 0324 0325 /** 0326 * Start a job. We want to be able to change the status field 0327 * correctly afterwards, so we have to remember the current 0328 * selection (which the user may change during the update). 0329 * In the recursive case, we collect all relevant directories. 0330 * Furthermore, we have to change the items to undefined state. 0331 */ 0332 void UpdateView::prepareJob(bool recursive, Action action) 0333 { 0334 act = action; 0335 0336 // Scan recursively all entries - there's no way around this here 0337 if (recursive) 0338 static_cast<UpdateDirItem *>(topLevelItem(0))->maybeScanDir(true); 0339 0340 rememberSelection(recursive); 0341 if (act != Add) 0342 markUpdated(false, false); 0343 } 0344 0345 /** 0346 * Finishes a job. What we do depends a bit on 0347 * whether the command was successful or not. 0348 */ 0349 void UpdateView::finishJob(bool normalExit, int exitStatus) 0350 { 0351 // cvs exitStatus == 1 only means that there're conflicts 0352 // ... which is not correct (e.g. server not reachable also returns 1) 0353 const bool success(normalExit && (exitStatus == 0)); 0354 0355 if (act != Add) 0356 markUpdated(true, success); 0357 syncSelection(); 0358 0359 // maybe some new items were created or 0360 // visibility of items changed so check the whole tree 0361 setFilter(filter()); 0362 } 0363 0364 /** 0365 * Marking non-selected items in a directory updated (as a consequence 0366 * of not appearing in 'cvs update' output) is done in two steps: In the 0367 * first, they are marked as 'indefinite', so that their status on the screen 0368 * isn't misrepresented. In the second step, they are either set 0369 * to 'UpToDate' (success=true) or 'Unknown'. 0370 */ 0371 void UpdateView::markUpdated(bool laststage, bool success) 0372 { 0373 foreach (QTreeWidgetItem *it, relevantSelection) { 0374 if (isDirItem(it)) { 0375 for (int i = 0; i < it->childCount(); i++) { 0376 QTreeWidgetItem *item = it->child(i); 0377 if (isFileItem(item)) { 0378 auto fileItem = static_cast<UpdateFileItem *>(item); 0379 fileItem->markUpdated(laststage, success); 0380 } 0381 } 0382 } else { 0383 auto fileItem = static_cast<UpdateFileItem *>(it); 0384 fileItem->markUpdated(laststage, success); 0385 } 0386 } 0387 } 0388 0389 /** 0390 * Remember the selection, see prepareJob() 0391 */ 0392 void UpdateView::rememberSelection(bool recursive) 0393 { 0394 std::set<QTreeWidgetItem *> setItems; 0395 for (QTreeWidgetItemIterator it(this); *it; ++it) { 0396 QTreeWidgetItem *item(*it); 0397 0398 // if this item is selected and if it was not inserted already 0399 // and if we work recursive and if it is a dir item then insert 0400 // all sub dirs 0401 // DON'T CHANGE TESTING ORDER 0402 if (item->isSelected() && setItems.insert(item).second && recursive && isDirItem(item)) { 0403 QStack<QTreeWidgetItem *> s; 0404 int childNum = 0; 0405 QTreeWidgetItem *startItem = item; 0406 QTreeWidgetItem *childItem = startItem->child(childNum); 0407 while (childItem) { 0408 // if this item is a dir item and if it was not 0409 // inserted already then insert all sub dirs 0410 // DON'T CHANGE TESTING ORDER 0411 if (isDirItem(childItem) && setItems.insert(childItem).second) { 0412 if (QTreeWidgetItem *childChildItem = childItem->child(0)) 0413 s.push(childChildItem); 0414 } 0415 0416 if (++childNum < startItem->childCount()) 0417 childItem = startItem->child(childNum); 0418 else { 0419 if (s.isEmpty()) 0420 break; 0421 else { 0422 childItem = s.pop(); 0423 startItem = childItem->parent(); 0424 childNum = 0; 0425 } 0426 } 0427 } 0428 } 0429 } 0430 0431 // Copy the set to the list 0432 relevantSelection.clear(); 0433 std::set<QTreeWidgetItem *>::const_iterator const itItemEnd = setItems.end(); 0434 for (auto itItem = setItems.begin(); itItem != itItemEnd; ++itItem) 0435 relevantSelection.append(*itItem); 0436 0437 #if 0 0438 qDebug() << "Relevant:"; 0439 foreach (QTreeWidgetItem * item, relevantSelection) 0440 qDebug() << " " << item->text(0); 0441 qDebug() << "End"; 0442 #endif 0443 } 0444 0445 /** 0446 * Use the remembered selection to resynchronize 0447 * with the actual directory and Entries content. 0448 */ 0449 void UpdateView::syncSelection() 0450 { 0451 // compute all directories which are selected or contain a selected file 0452 // (in recursive mode this includes all sub directories) 0453 std::set<UpdateDirItem *> setDirItems; 0454 foreach (QTreeWidgetItem *item, relevantSelection) { 0455 UpdateDirItem *dirItem(0); 0456 if (isDirItem(item)) 0457 dirItem = static_cast<UpdateDirItem *>(item); 0458 else if (QTreeWidgetItem *parentItem = item->parent()) 0459 dirItem = static_cast<UpdateDirItem *>(parentItem); 0460 0461 if (dirItem) 0462 setDirItems.insert(dirItem); 0463 } 0464 0465 QApplication::setOverrideCursor(Qt::WaitCursor); 0466 0467 std::set<UpdateDirItem *>::const_iterator const itDirItemEnd = setDirItems.end(); 0468 for (auto itDirItem = setDirItems.begin(); itDirItem != itDirItemEnd; ++itDirItem) { 0469 UpdateDirItem *dirItem = *itDirItem; 0470 0471 dirItem->syncWithDirectory(); 0472 dirItem->syncWithEntries(); 0473 0474 qApp->processEvents(); 0475 } 0476 0477 QApplication::restoreOverrideCursor(); 0478 } 0479 0480 /** 0481 * Get the colors from the configuration each time the list view items 0482 * are created. 0483 */ 0484 void UpdateView::updateColors() 0485 { 0486 KConfigGroup cs(&m_partConfig, "Colors"); 0487 0488 m_conflictColor = cs.readEntry("Conflict", QColor(255, 130, 130)); 0489 m_localChangeColor = cs.readEntry("LocalChange", QColor(130, 130, 255)); 0490 m_remoteChangeColor = cs.readEntry("RemoteChange", QColor(70, 210, 70)); 0491 0492 m_notInCvsColor = CervisiaSettings::notInCvsColor(); 0493 } 0494 0495 /** 0496 * Process one line from the output of 'cvs update'. If parseAsStatus 0497 * is true, it is assumed that the output is from a command 0498 * 'cvs update -n', i.e. cvs actually changes no files. 0499 */ 0500 void UpdateView::processUpdateLine(QString str) 0501 { 0502 if (str.length() > 2 && str[1] == ' ') { 0503 EntryStatus status(Cervisia::Unknown); 0504 switch (str[0].toLatin1()) { 0505 case 'C': 0506 status = Cervisia::Conflict; 0507 break; 0508 case 'A': 0509 status = Cervisia::LocallyAdded; 0510 break; 0511 case 'R': 0512 status = Cervisia::LocallyRemoved; 0513 break; 0514 case 'M': 0515 status = Cervisia::LocallyModified; 0516 break; 0517 case 'U': 0518 status = (act == UpdateNoAct) ? Cervisia::NeedsUpdate : Cervisia::Updated; 0519 break; 0520 case 'P': 0521 status = (act == UpdateNoAct) ? Cervisia::NeedsPatch : Cervisia::Patched; 0522 break; 0523 case '?': 0524 status = Cervisia::NotInCVS; 0525 break; 0526 default: 0527 return; 0528 } 0529 updateItem(str.mid(2), status, false); 0530 } 0531 0532 const QString removedFileStart(QLatin1String("cvs server: ")); 0533 const QString removedFileEnd(QLatin1String(" is no longer in the repository")); 0534 if (str.startsWith(removedFileStart) && str.endsWith(removedFileEnd)) { } 0535 0536 #if 0 0537 else if (str.left(21) == "cvs server: Updating " || 0538 str.left(21) == "cvs update: Updating ") 0539 updateItem(str.right(str.length()-21), Unknown, true); 0540 #endif 0541 } 0542 0543 void UpdateView::updateItem(const QString &filePath, EntryStatus status, bool isdir) 0544 { 0545 if (isdir && filePath == QLatin1String(".")) 0546 return; 0547 0548 const QFileInfo fileInfo(filePath); 0549 0550 auto rootItem = static_cast<UpdateDirItem *>(topLevelItem(0)); 0551 UpdateDirItem *dirItem = findOrCreateDirItem(fileInfo.path(), rootItem); 0552 0553 dirItem->updateChildItem(fileInfo.fileName(), status, isdir); 0554 } 0555 0556 void UpdateView::itemExecuted(QTreeWidgetItem *item, int) 0557 { 0558 if (isFileItem(item)) 0559 emit fileOpened(static_cast<UpdateFileItem *>(item)->filePath()); 0560 } 0561 0562 void UpdateView::itemExpandedSlot(QTreeWidgetItem *item) 0563 { 0564 static_cast<UpdateItem *>(item)->setOpen(true); 0565 } 0566 0567 // Local Variables: 0568 // c-basic-offset: 4 0569 // End: