Changeset View
Changeset View
Standalone View
Standalone View
plugins/grepview/grepoutputmodel.cpp
Show All 24 Lines | |||||
25 | #include <KLocalizedString> | 25 | #include <KLocalizedString> | ||
26 | 26 | | |||
27 | #include <QModelIndex> | 27 | #include <QModelIndex> | ||
28 | #include <QFontDatabase> | 28 | #include <QFontDatabase> | ||
29 | 29 | | |||
30 | 30 | | |||
31 | using namespace KDevelop; | 31 | using namespace KDevelop; | ||
32 | 32 | | |||
33 | GrepOutputItem::GrepOutputItem(const DocumentChangePointer& change, const QString &text, bool checkable) | 33 | GrepOutputItem::GrepOutputItem(const DocumentChangePointer &change, const QString &text, bool checkable) | ||
34 | : QStandardItem(), m_change(change) | 34 | : QStandardItem(), m_change(change) | ||
35 | { | 35 | { | ||
36 | setText(text); | 36 | setText(text); | ||
37 | setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); | 37 | setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); | ||
38 | 38 | | |||
39 | setCheckable(checkable); | 39 | setCheckable(checkable); | ||
40 | if(checkable) | 40 | if(checkable) | ||
41 | setCheckState(Qt::Checked); | 41 | setCheckState(Qt::Checked); | ||
42 | } | 42 | } | ||
43 | 43 | | |||
44 | GrepOutputItem::GrepOutputItem(const QString& filename, const QString& text, bool checkable) | 44 | GrepOutputItem::GrepOutputItem(const QString& filename, const QString& text, bool checkable) | ||
45 | : QStandardItem(), m_change(new DocumentChange(IndexedString(filename), KTextEditor::Range::invalid(), QString(), QString())) | 45 | : QStandardItem(), m_change(new DocumentChange(IndexedString(filename), KTextEditor::Range::invalid(), QString(), QString())) | ||
46 | { | 46 | { | ||
47 | setText(text); | 47 | setText(text); | ||
48 | setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); | 48 | setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); | ||
49 | setCheckable(checkable); | 49 | setCheckable(checkable); | ||
50 | if(checkable) | 50 | if(checkable) | ||
51 | { | 51 | { | ||
52 | setAutoTristate(true); | 52 | setAutoTristate(true); | ||
53 | setCheckState(Qt::Checked); | 53 | setCheckState(Qt::Checked); | ||
54 | } | 54 | } | ||
55 | } | 55 | } | ||
56 | GrepOutputItem::GrepOutputItem(const QString &filename, const QString &text, int line, bool checkable) | ||||
57 | : QStandardItem(), m_change(new DocumentChange(IndexedString(filename), KTextEditor::Range(line, 0, line, text.length() - 1), QString(), QString())) | ||||
58 | { | ||||
59 | setText(text); | ||||
60 | setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); | ||||
61 | setCheckable(checkable); | ||||
62 | if(checkable) | ||||
63 | { | ||||
64 | setAutoTristate(true); | ||||
65 | setCheckState(Qt::Checked); | ||||
66 | } | ||||
67 | } | ||||
56 | 68 | | |||
57 | int GrepOutputItem::lineNumber() const | 69 | int GrepOutputItem::lineNumber() const | ||
58 | { | 70 | { | ||
59 | // line starts at 0 for cursor but we want to start at 1 | 71 | // line starts at 0 for cursor but we want to start at 1 | ||
60 | return m_change->m_range.start().line() + 1; | 72 | return m_change->m_range.start().line() + 1; | ||
61 | } | 73 | } | ||
62 | 74 | | |||
63 | QString GrepOutputItem::filename() const | 75 | QString GrepOutputItem::filename() const | ||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Line(s) | 150 | { | |||
139 | p->refreshState(); | 151 | p->refreshState(); | ||
140 | } | 152 | } | ||
141 | } | 153 | } | ||
142 | 154 | | |||
143 | QVariant GrepOutputItem::data ( int role ) const { | 155 | QVariant GrepOutputItem::data ( int role ) const { | ||
144 | auto *grepModel = static_cast<GrepOutputModel *>(model()); | 156 | auto *grepModel = static_cast<GrepOutputModel *>(model()); | ||
145 | if(role == Qt::ToolTipRole && grepModel && isText()) | 157 | if(role == Qt::ToolTipRole && grepModel && isText()) | ||
146 | { | 158 | { | ||
159 | if (hasChildren()) { | ||||
160 | QString toolTip; | ||||
161 | KTextEditor::Range previousRange(lineNumber(), 0, lineNumber(), 0); | ||||
162 | for (int i = 0; i < rowCount(); ++i) { | ||||
163 | const GrepOutputItem *item = static_cast<GrepOutputItem*>(child(i)); | ||||
164 | const KTextEditor::Range range = item->change()->m_range; | ||||
165 | toolTip += text().mid(previousRange.end().column(), range.start().column() - previousRange.end().column()).toHtmlEscaped(); | ||||
166 | const QString match = isCheckable() ? grepModel->replacementFor(item->change()->m_oldText) : item->change()->m_oldText; | ||||
167 | toolTip += QLatin1String("<b>") + match.toHtmlEscaped() + QLatin1String("</b>"); | ||||
168 | previousRange = range; | ||||
169 | } | ||||
170 | toolTip += text().mid(previousRange.end().column()); | ||||
171 | return QString(QLatin1String("<span style=\"white-space:nowrap\">") + toolTip.trimmed() + QLatin1String("</span>")); | ||||
172 | } else { | ||||
147 | QString start = text().left(m_change->m_range.start().column()).toHtmlEscaped(); | 173 | QString start = text().left(m_change->m_range.start().column()).toHtmlEscaped(); | ||
148 | // show replaced version in tooltip if we are in replace mode | 174 | // show replaced version in tooltip if we are in replace mode | ||
149 | const QString match = isCheckable() ? grepModel->replacementFor(m_change->m_oldText) : m_change->m_oldText; | 175 | const QString match = isCheckable() ? grepModel->replacementFor(m_change->m_oldText) : m_change->m_oldText; | ||
150 | const QString repl = QLatin1String("<b>") + match.toHtmlEscaped() + QLatin1String("</b>"); | 176 | const QString repl = QLatin1String("<b>") + match.toHtmlEscaped() + QLatin1String("</b>"); | ||
151 | QString end = text().mid(m_change->m_range.end().column()).toHtmlEscaped(); | 177 | QString end = text().mid(m_change->m_range.end().column()).toHtmlEscaped(); | ||
152 | const QString toolTip = QLatin1String("<span style=\"white-space:nowrap\">") + QString(start + repl + end).trimmed() + QLatin1String("</span>"); | 178 | const QString toolTip = QLatin1String("<span style=\"white-space:nowrap\">") + QString(start + repl + end).trimmed() + QLatin1String("</span>"); | ||
153 | return toolTip; | 179 | return toolTip; | ||
180 | } | ||||
154 | } else if (role == Qt::FontRole) { | 181 | } else if (role == Qt::FontRole) { | ||
155 | return QFontDatabase::systemFont(QFontDatabase::FixedFont); | 182 | return QFontDatabase::systemFont(QFontDatabase::FixedFont); | ||
156 | } else { | 183 | } else { | ||
157 | return QStandardItem::data(role); | 184 | return QStandardItem::data(role); | ||
158 | } | 185 | } | ||
159 | } | 186 | } | ||
160 | 187 | | |||
161 | GrepOutputItem::~GrepOutputItem() | 188 | GrepOutputItem::~GrepOutputItem() | ||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Line(s) | 275 | if (!currentIdx.isValid()) { | |||
255 | } | 282 | } | ||
256 | return QModelIndex(); | 283 | return QModelIndex(); | ||
257 | } | 284 | } | ||
258 | else | 285 | else | ||
259 | current_item = static_cast<GrepOutputItem*>(itemFromIndex(currentIdx)); | 286 | current_item = static_cast<GrepOutputItem*>(itemFromIndex(currentIdx)); | ||
260 | 287 | | |||
261 | if (current_item->parent() != nullptr) { | 288 | if (current_item->parent() != nullptr) { | ||
262 | int row = currentIdx.row(); | 289 | int row = currentIdx.row(); | ||
263 | | ||||
264 | if(!current_item->isText()) // the item is a file | 290 | if(!current_item->isText()) // the item is a file | ||
265 | { | 291 | { | ||
266 | int item_row = current_item->row(); | 292 | int item_row = current_item->row(); | ||
267 | if(item_row > 0) | 293 | if(item_row > 0) { | ||
294 | int last_line = current_item->parent()->child(item_row-1)->rowCount() - 1; | ||||
295 | int last_item = current_item->parent()->child(item_row-1)->child(last_line)->rowCount() - 1; | ||||
296 | return current_item->parent()->child(item_row-1)->child(last_line)->child(last_item)->index(); | ||||
297 | } | ||||
298 | } | ||||
299 | else if (current_item->hasChildren())//the item is a line | ||||
300 | { | ||||
301 | int idx_line = current_item->row(); | ||||
302 | if (idx_line > 0) | ||||
268 | { | 303 | { | ||
269 | int idx_last_item = current_item->parent()->child(item_row - 1)->rowCount() - 1; | 304 | int last_item = current_item->parent()->child(idx_line - 1)->rowCount() - 1; | ||
270 | return current_item->parent()->child(item_row - 1)->child(idx_last_item)->index(); | 305 | return current_item->parent()->child(idx_line - 1)->child(last_item)->index(); | ||
271 | } | 306 | } | ||
307 | return previousItemIndex(current_item->parent()->index()); | ||||
272 | } | 308 | } | ||
273 | else // the item is a match | 309 | else // the item is a match | ||
274 | { | 310 | { | ||
275 | if(row > 0) | 311 | if(row > 0) { | ||
276 | return current_item->parent()->child(row - 1)->index(); | 312 | return current_item->parent()->child(row - 1)->index(); | ||
277 | else // we return the index of the last item of the previous file | | |||
278 | { | | |||
279 | int parrent_row = current_item->parent()->row(); | | |||
280 | if(parrent_row > 0) | | |||
281 | { | | |||
282 | int idx_last_item = current_item->parent()->parent()->child(parrent_row - 1)->rowCount() - 1; | | |||
283 | return current_item->parent()->parent()->child(parrent_row - 1)->child(idx_last_item)->index(); | | |||
284 | } | | |||
285 | } | 313 | } | ||
314 | return previousItemIndex(current_item->parent()->index()); | ||||
286 | } | 315 | } | ||
287 | } | 316 | } | ||
288 | return currentIdx; | 317 | return currentIdx; | ||
289 | } | 318 | } | ||
290 | 319 | | |||
291 | QModelIndex GrepOutputModel::nextItemIndex(const QModelIndex ¤tIdx) const | 320 | QModelIndex GrepOutputModel::nextItemIndex(const QModelIndex ¤tIdx) const | ||
292 | { | 321 | { | ||
293 | GrepOutputItem* current_item = nullptr; | 322 | GrepOutputItem* current_item = nullptr; | ||
Show All 14 Lines | 336 | else | |||
308 | return QModelIndex(); | 337 | return QModelIndex(); | ||
309 | } else { | 338 | } else { | ||
310 | int row = currentIdx.row(); | 339 | int row = currentIdx.row(); | ||
311 | if(!current_item->isText()) // the item is a file | 340 | if(!current_item->isText()) // the item is a file | ||
312 | { | 341 | { | ||
313 | int item_row = current_item->row(); | 342 | int item_row = current_item->row(); | ||
314 | if(item_row < current_item->parent()->rowCount()) | 343 | if(item_row < current_item->parent()->rowCount()) | ||
315 | { | 344 | { | ||
316 | return current_item->parent()->child(item_row)->child(0)->index(); | 345 | return current_item->parent()->child(item_row)->child(0)->child(0)->index(); | ||
346 | } | ||||
317 | } | 347 | } | ||
348 | else if(current_item->hasChildren()) //the item is a line | ||||
349 | { | ||||
350 | return current_item->child(0)->index(); | ||||
318 | } | 351 | } | ||
319 | else // the item is a match | 352 | else // the item is a match | ||
320 | { | 353 | { | ||
321 | if(row < current_item->parent()->rowCount() - 1) | 354 | if(row < current_item->parent()->rowCount() - 1) | ||
322 | return current_item->parent()->child(row + 1)->index(); | 355 | return current_item->parent()->child(row + 1)->index(); | ||
356 | else if(current_item->parent()->row() < current_item->parent()->parent()->rowCount() - 1) //next line | ||||
357 | { | ||||
358 | return current_item->parent()->parent()->child(current_item->parent()->row() + 1)->child(0)->index(); | ||||
359 | } | ||||
323 | else // we return the index of the first item of the next file | 360 | else // we return the index of the first item of the next file | ||
324 | { | 361 | { | ||
325 | int parrent_row = current_item->parent()->row(); | 362 | int parrent_row = current_item->parent()->parent()->row(); | ||
326 | if(parrent_row < current_item->parent()->parent()->rowCount() - 1) | 363 | if(parrent_row < current_item->parent()->parent()->rowCount() - 1) | ||
327 | { | 364 | { | ||
328 | return current_item->parent()->parent()->child(parrent_row + 1)->child(0)->index(); | 365 | return current_item->parent()->parent()->parent()->child(parrent_row + 1)->child(0)->child(0)->index(); | ||
329 | } | 366 | } | ||
330 | } | 367 | } | ||
331 | } | 368 | } | ||
332 | } | 369 | } | ||
333 | return currentIdx; | 370 | return currentIdx; | ||
334 | } | 371 | } | ||
335 | 372 | | |||
336 | const GrepOutputItem *GrepOutputModel::getRootItem() const { | 373 | const GrepOutputItem *GrepOutputModel::getRootItem() const { | ||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Line(s) | 405 | { | |||
383 | 420 | | |||
384 | m_rootItem->setText(i18nc("%1 is e.g. '4 matches', %2 is e.g. '1 file'", "<b>%1 in %2</b>", matchText, fileText)); | 421 | m_rootItem->setText(i18nc("%1 is e.g. '4 matches', %2 is e.g. '1 file'", "<b>%1 in %2</b>", matchText, fileText)); | ||
385 | 422 | | |||
386 | QString fnString = i18np("%2: 1 match", "%2: %1 matches", | 423 | QString fnString = i18np("%2: 1 match", "%2: %1 matches", | ||
387 | items.length(), ICore::self()->projectController()->prettyFileName(QUrl::fromLocalFile(filename))); | 424 | items.length(), ICore::self()->projectController()->prettyFileName(QUrl::fromLocalFile(filename))); | ||
388 | 425 | | |||
389 | auto *fileItem = new GrepOutputItem(filename, fnString, m_itemsCheckable); | 426 | auto *fileItem = new GrepOutputItem(filename, fnString, m_itemsCheckable); | ||
390 | m_rootItem->appendRow(fileItem); | 427 | m_rootItem->appendRow(fileItem); | ||
428 | GrepOutputItem *lineItem = new GrepOutputItem(filename, items[0].text(), items[0].change()->m_range.start().line(), m_itemsCheckable); | ||||
391 | for (const GrepOutputItem& item : items) { | 429 | for (const GrepOutputItem& item : items) { | ||
392 | auto* copy = new GrepOutputItem(item); | 430 | auto* copy = new GrepOutputItem(item); | ||
393 | copy->setCheckable(m_itemsCheckable); | 431 | copy->setCheckable(m_itemsCheckable); | ||
394 | if(m_itemsCheckable) | 432 | if(m_itemsCheckable) | ||
395 | { | 433 | { | ||
396 | copy->setCheckState(Qt::Checked); | 434 | copy->setCheckState(Qt::Checked); | ||
397 | if(copy->rowCount()) | 435 | if(copy->rowCount()) | ||
398 | copy->setAutoTristate(true); | 436 | copy->setAutoTristate(true); | ||
399 | } | 437 | } | ||
400 | 438 | if (copy->change()->m_range.start().line() != lineItem->change()->m_range.start().line()) { | |||
401 | fileItem->appendRow(copy); | 439 | fileItem->appendRow(lineItem); | ||
440 | lineItem = new GrepOutputItem(filename, copy->text(), copy->change()->m_range.start().line(), m_itemsCheckable); | ||||
441 | } | ||||
442 | lineItem->appendRow(copy); | ||||
402 | } | 443 | } | ||
444 | fileItem->appendRow(lineItem); | ||||
403 | } | 445 | } | ||
404 | 446 | | |||
405 | void GrepOutputModel::updateCheckState(QStandardItem* item) | 447 | void GrepOutputModel::updateCheckState(QStandardItem* item) | ||
406 | { | 448 | { | ||
407 | // if we don't disconnect the SIGNAL, the setCheckState will call it in loop | 449 | // if we don't disconnect the SIGNAL, the setCheckState will call it in loop | ||
408 | disconnect(this, &GrepOutputModel::itemChanged, nullptr, nullptr); | 450 | disconnect(this, &GrepOutputModel::itemChanged, nullptr, nullptr); | ||
409 | 451 | | |||
410 | // try to update checkstate on non checkable items would make a checkbox appear | 452 | // try to update checkstate on non checkable items would make a checkbox appear | ||
Show All 14 Lines | 465 | { | |||
425 | if (!m_rootItem) | 467 | if (!m_rootItem) | ||
426 | return; // nothing to do, abort | 468 | return; // nothing to do, abort | ||
427 | 469 | | |||
428 | DocumentChangeSet changeSet; | 470 | DocumentChangeSet changeSet; | ||
429 | changeSet.setFormatPolicy(DocumentChangeSet::NoAutoFormat); | 471 | changeSet.setFormatPolicy(DocumentChangeSet::NoAutoFormat); | ||
430 | for(int fileRow = 0; fileRow < m_rootItem->rowCount(); fileRow++) | 472 | for(int fileRow = 0; fileRow < m_rootItem->rowCount(); fileRow++) | ||
431 | { | 473 | { | ||
432 | auto *file = static_cast<GrepOutputItem *>(m_rootItem->child(fileRow)); | 474 | auto *file = static_cast<GrepOutputItem *>(m_rootItem->child(fileRow)); | ||
433 | 475 | for(int lineRow = 0; lineRow < file->rowCount(); lineRow++) | |||
434 | for(int matchRow = 0; matchRow < file->rowCount(); matchRow++) | 476 | { | ||
477 | auto *line = static_cast<GrepOutputItem*>(file->child(lineRow)); | ||||
478 | for(int matchRow = 0; matchRow < line->rowCount(); matchRow++) | ||||
435 | { | 479 | { | ||
436 | auto *match = static_cast<GrepOutputItem *>(file->child(matchRow)); | 480 | auto *match = static_cast<GrepOutputItem *>(line->child(matchRow)); | ||
437 | if(match->checkState() == Qt::Checked) | 481 | if(match->checkState() == Qt::Checked) | ||
438 | { | 482 | { | ||
439 | DocumentChangePointer change = match->change(); | 483 | DocumentChangePointer change = match->change(); | ||
440 | // setting replacement text based on current replace value | 484 | // setting replacement text based on current replace value | ||
441 | change->m_newText = replacementFor(change->m_oldText); | 485 | change->m_newText = replacementFor(change->m_oldText); | ||
442 | changeSet.addChange(change); | 486 | changeSet.addChange(change); | ||
443 | // this item cannot be checked anymore | 487 | // this item cannot be checked anymore | ||
444 | match->setCheckState(Qt::Unchecked); | 488 | match->setCheckState(Qt::Unchecked); | ||
445 | match->setEnabled(false); | 489 | match->setEnabled(false); | ||
446 | } | 490 | } | ||
447 | } | 491 | } | ||
448 | } | 492 | } | ||
493 | } | ||||
449 | 494 | | |||
450 | DocumentChangeSet::ChangeResult result = changeSet.applyAllChanges(); | 495 | DocumentChangeSet::ChangeResult result = changeSet.applyAllChanges(); | ||
451 | if(!result.m_success) | 496 | if(!result.m_success) | ||
452 | { | 497 | { | ||
453 | DocumentChangePointer ch = result.m_reasonChange; | 498 | DocumentChangePointer ch = result.m_reasonChange; | ||
454 | if(ch) | 499 | if(ch) | ||
455 | emit showErrorMessage(i18nc("%1 is the old text, %2 is the new text, %3 is the file path, %4 and %5 are its row and column", | 500 | emit showErrorMessage(i18nc("%1 is the old text, %2 is the new text, %3 is the file path, %4 and %5 are its row and column", | ||
456 | "Failed to replace <b>%1</b> by <b>%2</b> in %3:%4:%5", | 501 | "Failed to replace <b>%1</b> by <b>%2</b> in %3:%4:%5", | ||
Show All 25 Lines |