Mantid
Loading...
Searching...
No Matches
JobTreeView.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
16#include <QKeyEvent>
17#include <QStandardItemModel>
18#include <algorithm>
19
20namespace {
21QAbstractItemView::EditTriggers getEditTriggers() {
22 auto trigger =
23 QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed | QAbstractItemView::AnyKeyPressed;
24 return trigger;
25}
26} // namespace
27
29
30JobTreeView::JobTreeView(QStringList const &columnHeadings, Cell const &emptyCellStyle, QWidget *parent)
31 : QTreeView(parent), m_notifyee(nullptr), m_mainModel(0, columnHeadings.size(), this),
32 m_adaptedMainModel(m_mainModel, emptyCellStyle), m_filteredModel(RowLocationAdapter(m_mainModel), this),
33 m_lastEdited(QModelIndex()) {
34 setModel(&m_mainModel);
35 setHeaderLabels(columnHeadings);
36 setSelectionMode(QAbstractItemView::ExtendedSelection);
37 setEditTriggers(getEditTriggers());
38 setItemDelegate(new CellDelegate(this, *this, m_filteredModel, m_mainModel));
39 setContextMenuPolicy(Qt::ActionsContextMenu);
41}
42
43void JobTreeView::commitData(QWidget *editor) {
44 auto current_filtered_index = fromFilteredModel(currentIndex());
46 QTreeView::commitData(editor);
48 // cppcheck-suppress knownConditionTrueFalse
49 if (cellText != cellTextBefore) {
50 resizeColumnToContents(m_lastEdited.column());
51 m_hasEditorOpen = false;
53 cellText);
54 editAt(current_filtered_index);
55 }
56}
57
58void JobTreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
59 QTreeView::selectionChanged(selected, deselected);
60 if (!m_notifyee)
61 return;
63}
64
65void JobTreeView::filterRowsBy(std::unique_ptr<RowPredicate> predicate) {
66 m_filteredModel.setPredicate(std::move(predicate));
67 expandAll();
68}
69
71 m_filteredModel.setPredicate(std::unique_ptr<RowPredicate>(predicate));
72 expandAll();
73}
74
79
81
82bool JobTreeView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) {
84 m_hasEditorOpen = true;
85 return QTreeView::edit(index, trigger, event);
86}
87
89
90std::optional<std::vector<Subtree>> JobTreeView::selectedSubtrees() const {
91 auto selected = selectedRowLocations();
92 std::sort(selected.begin(), selected.end());
93
94 auto selectedRows = std::vector<Row>();
95 selectedRows.reserve(selected.size());
96
97 std::transform(selected.cbegin(), selected.cend(), std::back_inserter(selectedRows),
98 [&](RowLocation const &location) -> Row { return Row(location, cellsAt(location)); });
99
100 auto extractSubtrees = ExtractSubtrees();
101 return extractSubtrees(selectedRows);
102}
103
104std::optional<std::vector<RowLocation>> JobTreeView::selectedSubtreeRoots() const {
105 auto findSubtreeRoots = FindSubtreeRoots();
106 return findSubtreeRoots(selectedRowLocations());
107}
108
109bool JobTreeView::hasNoSelectedDescendants(QModelIndex const &index) const {
110 for (auto row = 0; row < m_filteredModel.rowCount(index); ++row) {
111 auto childIndex = m_filteredModel.index(row, 0, index);
112 if (selectionModel()->isSelected(childIndex))
113 return false;
114 else if (!hasNoSelectedDescendants(childIndex))
115 return false;
116 }
117 return true;
118}
119
120void JobTreeView::appendAllUnselectedDescendants(QModelIndexList &descendantsList, QModelIndex const &index) const {
121 for (auto row = 0; row < m_filteredModel.rowCount(index); ++row) {
122 auto childIndex = m_filteredModel.index(row, 0, index);
123 if (!selectionModel()->isSelected(childIndex))
124 descendantsList.append(childIndex);
125 appendAllUnselectedDescendants(descendantsList, childIndex);
126 }
127}
128
129void JobTreeView::setHintsForColumn(int column, std::unique_ptr<HintStrategy> hintStrategy) {
130 setItemDelegateForColumn(column, new HintingLineEditFactory(itemDelegate(), std::move(hintStrategy)));
131}
132
133void JobTreeView::setHintsForColumn(int column, HintStrategy *hintStrategy) {
134 setHintsForColumn(column, std::unique_ptr<HintStrategy>(hintStrategy));
135}
136
137QModelIndexList JobTreeView::findImplicitlySelected(QModelIndexList const &selectedRows) const {
138 auto implicitlySelected = QModelIndexList();
139 for (auto &&row : selectedRows) {
140 if (isExpanded(row)) {
142 appendAllUnselectedDescendants(implicitlySelected, row);
143 } else {
144 appendAllUnselectedDescendants(implicitlySelected, row);
145 }
146 }
147 return implicitlySelected;
148}
149
150std::vector<RowLocation> JobTreeView::selectedRowLocations() const {
151 auto selection = selectionModel()->selectedRows();
152 selection.append(findImplicitlySelected(selection));
153
154 std::vector<RowLocation> rowSelection;
155 rowSelection.reserve(selection.size());
156 std::transform(selection.begin(), selection.end(), std::back_inserter(rowSelection),
157 [&](QModelIndex const &index) -> RowLocation {
158 auto indexForMainModel = mapToMainModel(fromFilteredModel(index));
159 return rowLocation().atIndex(indexForMainModel);
160 });
161 return rowSelection;
162}
163
165
167
169
171
173
175
177
178void JobTreeView::removeRows(std::vector<RowLocation> rowsToRemove) {
179 std::sort(rowsToRemove.begin(), rowsToRemove.end());
180 for (auto rowIt = rowsToRemove.crbegin(); rowIt < rowsToRemove.crend(); ++rowIt)
181 removeRowAt(*rowIt);
182}
183
184Cell JobTreeView::cellAt(RowLocation location, int column) const {
185 auto const cellIndex = rowLocation().indexAt(location, column);
186 return m_adaptedMainModel.cellFromCellIndex(cellIndex);
187}
188
190 return hasEditorOpen() && fromFilteredModel(currentIndex()) == cellIndex;
191}
192
194 if (cellIndex.isValid() && isBeingEdited(cellIndex))
195 closePersistentEditor(cellIndex.untyped());
196}
197
199 if (!cell.isEditable())
201}
202
203void JobTreeView::setCellAt(RowLocation location, int column, Cell const &cell) {
204 auto const cellIndex = rowLocation().indexAt(location, column);
205 m_adaptedMainModel.setCellAtCellIndex(cellIndex, cell);
206 closeEditorIfCellIsUneditable(cellIndex, cell);
207}
208
209std::vector<Cell> JobTreeView::cellsAt(RowLocation const &location) const {
210 return m_adaptedMainModel.cellsAtRow(rowLocation().indexAt(location));
211}
212
214 std::vector<Cell> const &cells) {
215 m_adaptedMainModel.enumerateCellsInRow(firstCellOnRow, m_mainModel.columnCount(),
216 [&](QModelIndexForMainModel const &cellIndex, int column) -> void {
217 closeEditorIfCellIsUneditable(cellIndex, cells[column]);
218 });
219}
220
221void JobTreeView::setCellsAt(RowLocation const &location, std::vector<Cell> const &cells) {
222 auto firstCellOnRow = rowLocation().indexAt(location);
223 m_adaptedMainModel.setCellsAtRow(firstCellOnRow, cells);
224 closeAnyOpenEditorsOnUneditableCells(firstCellOnRow, cells);
225}
226
227void JobTreeView::replaceSubtreeAt(RowLocation const &rootToRemove, Subtree const &toInsert) {
228 auto const insertionParent = rootToRemove.parent();
229 auto insertionIndex = rootToRemove.rowRelativeToParent();
230 // Insert the new row first (to avoid possibility of an empty table, which
231 // will cause problems because it is not allowed and will insert an empty
232 // group which we don't want)
233 insertSubtreeAt(insertionParent, insertionIndex, toInsert);
234 // Now find the new index of the root we're replacing and remove it. It is
235 // now the sibling below.
236 auto originalRootIndexToRemove = rowLocation().indexAt(rootToRemove);
237 auto newRootIndexToRemove = below(originalRootIndexToRemove.untyped());
238 removeRowAt(rowLocation().atIndex(fromMainModel(newRootIndexToRemove)));
239}
240
241void JobTreeView::insertSubtreeAt(RowLocation const &parent, int index, Subtree const &subtree) {
243 build(parent, index, subtree);
244}
245
246void JobTreeView::appendSubtreesAt(RowLocation const &parent, std::vector<Subtree> subtrees) {
247 for (auto &&subtree : subtrees)
248 appendSubtreeAt(parent, subtree);
249}
250
251void JobTreeView::appendSubtreeAt(RowLocation const &parent, Subtree const &subtree) {
252 auto parentIndex = rowLocation().indexAt(parent);
253 insertSubtreeAt(parent, m_mainModel.rowCount(parentIndex.untyped()), subtree);
254}
255
256void JobTreeView::replaceRows(std::vector<RowLocation> replacementPoints, std::vector<Subtree> replacements) {
257 assertOrThrow(replacementPoints.size() > 0, "replaceRows: Passed an empty list of replacement points."
258 "At least one replacement point is required.");
259 auto replacementPoint = replacementPoints.cbegin();
260 auto replacement = replacements.cbegin();
261
262 for (; replacementPoint != replacementPoints.cend() && replacement != replacements.cend();
263 ++replacementPoint, ++replacement) {
264 replaceSubtreeAt(*replacementPoint, *replacement);
265 }
266
267 if (replacementPoints.size() > replacements.size())
268 for (; replacementPoint != replacementPoints.cend(); ++replacementPoint)
269 removeRowAt(*replacementPoint);
270 else if (replacementPoints.size() < replacements.size())
271 for (; replacement != replacements.cend(); ++replacement)
272 appendSubtreeAt(replacementPoints.back().parent(), *replacement);
273}
274
275void JobTreeView::setHeaderLabels(QStringList const &columnHeadings) {
276 m_mainModel.setHorizontalHeaderLabels(columnHeadings);
277
278 for (auto i = 0; i < model()->columnCount(); ++i)
279 resizeColumnToContents(i);
280}
281
282void JobTreeView::subscribe(JobTreeViewSubscriber *subscriber) { m_notifyee = subscriber; }
283
285
287 return areOnSameRow(mapToMainModel(fromFilteredModel(currentIndex())).untyped(), indexToRemove.untyped()) &&
289}
290
291QModelIndex JobTreeView::siblingIfExistsElseParent(QModelIndex const &index) const {
292 if (hasRowAbove(index)) {
293 return above(index);
294 } else if (hasRowBelow(index)) {
295 return below(index);
296 } else {
297 return index.parent();
298 }
299}
300
302 auto parentIndex = index.parent();
303 return m_mainModel.rowCount(parentIndex.untyped()) == 1;
304}
305
307 return isOnlyChildOfRoot(rowLocation().indexAt(index));
308}
309
311 return !index.parent().isValid() && isOnlyChild(index);
312}
313
315 auto indexToRemove = rowLocation().indexAt(location);
316 assertOrThrow(indexToRemove.isValid(), "removeRowAt: Attempted to remove the invisible root item.");
317 // We can't delete the only child of the invisible root
318 // for the main model unless we use removeAllRows()
319 if (isOnlyChildOfRoot(indexToRemove)) {
321 return;
322 }
323
324 if (rowRemovalWouldBeIneffective(indexToRemove)) {
325 // implies that indexToRemove corresponds to an index in the filtered model.
326 auto rowIndexToSwitchTo = siblingIfExistsElseParent(mapToFilteredModel(indexToRemove).untyped());
327 if (rowIndexToSwitchTo.isValid()) {
328 setCurrentIndex(rowIndexToSwitchTo);
329 } else {
330 resetFilter();
331 rowIndexToSwitchTo = siblingIfExistsElseParent(mapToFilteredModel(indexToRemove).untyped());
332 setCurrentIndex(rowIndexToSwitchTo);
333 }
334 }
335 m_adaptedMainModel.removeRowFrom(indexToRemove);
336}
337
340 auto firstChild = std::vector<int>{0};
341 while (!isOnlyChildOfRoot(firstChild)) {
342 removeRowAt(firstChild);
343 }
345}
346
348 auto const child = m_adaptedMainModel.insertEmptyChildRow(rowLocation().indexAt(parent), beforeRow);
350 return rowLocation().atIndex(child);
351}
352
353RowLocation JobTreeView::insertChildRowOf(RowLocation const &parent, int beforeRow, std::vector<Cell> const &cells) {
354 assertOrThrow(static_cast<int>(cells.size()) <= m_mainModel.columnCount(),
355 "Attempted to add row with more cells than columns. Increase "
356 "the number of columns by increasing the number of headings.");
357 auto child = m_adaptedMainModel.insertChildRow(rowLocation().indexAt(parent), beforeRow,
358 paddedCellsToWidth(cells, g_deadCell, m_mainModel.columnCount()));
360 return rowLocation().atIndex(child);
361}
362
363Cell const JobTreeView::g_deadCell = Cell("", "white", 0, "transparent", 0, false);
364
368
369RowLocation JobTreeView::appendChildRowOf(RowLocation const &parent, std::vector<Cell> const &cells) {
370 auto parentIndex = rowLocation().indexAt(parent);
371 return rowLocation().atIndex(
372 m_adaptedMainModel.appendChildRow(parentIndex, paddedCellsToWidth(cells, g_deadCell, m_mainModel.columnCount())));
373}
374
376 if (isEditable(index.untyped())) {
377 QTreeView::clearSelection();
378 setCurrentIndex(index.untyped());
379 edit(index.untyped());
380 }
381}
382
383void JobTreeView::clearSelection() { QTreeView::clearSelection(); }
384
385void JobTreeView::expandAll() { QTreeView::expandAll(); }
386
387void JobTreeView::collapseAll() { QTreeView::collapseAll(); }
388
390 auto expandAt = index.untyped();
391 while (expandAt.isValid()) {
392 setExpanded(expandAt, true);
393 expandAt = m_filteredModel.parent(expandAt);
394 }
395 return index;
396}
397
399
401
402std::pair<QModelIndexForFilteredModel, bool>
404 if (hasRowBelow(index.untyped())) {
405 return std::make_pair(fromFilteredModel(below(index.untyped())), false);
406 } else {
407 auto indexForModel = mapToMainModel(index);
408 resetFilter();
409 auto newIndex = m_adaptedMainModel.appendEmptySiblingRow(indexForModel);
410 return std::make_pair(mapToFilteredModel(newIndex), true);
411 }
412}
413
415 return QModelIndexForMainModel(m_filteredModel.mapToSource(filteredModelIndex.untyped()));
416}
417
421
423 resetFilter();
424 auto const parent = mapToMainModel(fromFilteredModel(currentIndex()));
425 auto const child = m_adaptedMainModel.appendEmptyChildRow(parent);
426 m_notifyee->notifyRowInserted(rowLocation().atIndex(child));
428}
429
431 auto current = currentIndex();
432 setCurrentIndex(QModelIndex());
433 setCurrentIndex(current);
434 if (current != m_mainModel.index(-1, -1)) {
435 auto const cell = findOrMakeCellBelow(fromFilteredModel(current));
436 auto index = cell.first;
437 auto isNew = cell.second;
438
439 if (isNew) {
441 }
442 editAt(index);
443 }
444}
445
447 if (hasRowAbove(currentIndex()))
448 editAt(fromFilteredModel(above(currentIndex())));
449}
450
452 m_filteredModel.setDynamicSortFilter(false);
453 m_filteredModel.setSourceModel(&m_mainModel);
454 setModel(&m_filteredModel);
455}
456
457void JobTreeView::keyPressEvent(QKeyEvent *event) {
458 switch (event->key()) {
459 case Qt::Key_I:
460 if (event->modifiers() & Qt::ControlModifier) {
462 }
463 break;
464 case Qt::Key_Return:
465 case Qt::Key_Enter: {
466 if (event->modifiers() & Qt::ShiftModifier) {
468 } else {
470 }
471 break;
472 }
473 case Qt::Key_Delete:
475 break;
476 case Qt::Key_C: {
477 if (event->modifiers() & Qt::ControlModifier) {
479 }
480 break;
481 }
482 case Qt::Key_V: {
483 if (event->modifiers() & Qt::ControlModifier) {
485 }
486 break;
487 }
488 case Qt::Key_X: {
489 if (event->modifiers() & Qt::ControlModifier) {
491 }
492 break;
493 }
494 default:
495 QTreeView::keyPressEvent(event);
496 }
497}
498
499QModelIndexForMainModel JobTreeView::fromMainModel(QModelIndex const &mainModelIndex) const {
500 return ::MantidQt::MantidWidgets::Batch::fromMainModel(mainModelIndex, m_mainModel);
501}
502
503QModelIndexForFilteredModel JobTreeView::fromFilteredModel(QModelIndex const &filteredModelIndex) const {
504 return ::MantidQt::MantidWidgets::Batch::fromFilteredModel(filteredModelIndex, m_filteredModel);
505}
506
507bool JobTreeView::isEditable(QModelIndex const &index) const { return index.flags() & Qt::ItemIsEditable; }
508
510 auto result = navigation().moveCursorNext(startingPoint);
511 while (!result.first && result.second.isValid() && !isEditable(result.second))
512 result = navigation().moveCursorNext(result.second);
513 return result;
514}
515
516QModelIndex JobTreeView::movePreviousUntilEditable(QModelIndex const &startingPoint) {
517 auto currentIndex = navigation().moveCursorPrevious(startingPoint);
518 while (currentIndex.isValid() && !isEditable(currentIndex))
519 currentIndex = navigation().moveCursorPrevious(currentIndex);
520 return currentIndex;
521}
522
523QModelIndex JobTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) {
524 if (cursorAction == QAbstractItemView::MoveNext)
525 return applyNavigationResult(moveNextUntilEditable(currentIndex()));
526 else if (cursorAction == QAbstractItemView::MovePrevious)
527 return movePreviousUntilEditable(currentIndex());
528 else
529 return QTreeView::moveCursor(cursorAction, modifiers);
530}
531
533 auto shouldMakeNewRowBelow = result.first;
534 if (shouldMakeNewRowBelow) {
535 // `newCellIndex` is the model index of the cell in the new row with a
536 // column which matches
537 // the column currently selected by the user. To correctly get the
538 // RowLocation we need the
539 // model index of the first cell in the new row.
541 auto newRowIndex = fromMainModel(firstCellOnRowOf(newCellIndex.untyped()));
542
543 // Resetting the filter ensures that the new row is visible and has a
544 // corresponding index in
545 // the filtered model.
546 resetFilter();
547
548 auto newRowLocation = rowLocation().atIndex(newRowIndex);
549 m_notifyee->notifyRowInserted(newRowLocation);
550
551 // The subscriber is entitled to remove the row at `newRowIndex` when we
552 // call
553 // `notifyRowInserted` hence we have to assume they did and try to get the
554 // index again.
555 auto maybeIndexOfNewRow = rowLocation().indexIfExistsAt(newRowLocation);
556 if (maybeIndexOfNewRow.has_value()) {
557 return expanded(mapToFilteredModel(maybeIndexOfNewRow.value())).untyped();
558 } else {
559 return QModelIndex();
560 }
561 } else {
562 return result.second;
563 }
564}
565
566int JobTreeView::currentColumn() const { return currentIndex().column(); }
567
568} // namespace MantidQt::MantidWidgets::Batch
void assertOrThrow(bool condition, std::string const &message)
See the developer documentation for Batch Widget at developer.mantidproject.org/BatchWidget/index....
std::map< DeltaEMode::Type, std::string > index
std::string const & contentText() const
Definition Cell.cpp:23
void setPredicate(std::unique_ptr< RowPredicate > predicate)
virtual void notifyRowInserted(RowLocation const &newRowLocation)=0
virtual void notifyRemoveRowsRequested(std::vector< RowLocation > const &locationsOfRowsToRemove)=0
virtual void notifyCellTextChanged(RowLocation const &itemIndex, int column, std::string const &oldValue, std::string const &newValue)=0
QtTreeCursorNavigation navigation() const
void appendAllUnselectedDescendants(QModelIndexList &selectedRows, QModelIndex const &index) const
void appendSubtreesAt(RowLocation const &parent, std::vector< Subtree > subtrees) override
std::optional< std::vector< RowLocation > > selectedSubtreeRoots() const override
std::optional< std::vector< Subtree > > selectedSubtrees() const override
RowLocation appendChildRowOf(RowLocation const &parent) override
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override
bool edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) override
JobTreeView(QStringList const &columnHeadings, Cell const &defaultCellStyle, QWidget *parent=nullptr)
QModelIndexForFilteredModel fromFilteredModel(QModelIndex const &filteredModelIndex) const
QModelIndex siblingIfExistsElseParent(QModelIndex const &index) const
void closeAnyOpenEditorsOnUneditableCells(QModelIndexForMainModel const &firstCellOnRow, std::vector< Cell > const &cells)
QModelIndexForFilteredModel mapToFilteredModel(QModelIndexForMainModel const &mainModelIndex) const
QModelIndex movePreviousUntilEditable(QModelIndex const &startingPoint)
QModelIndexList findImplicitlySelected(QModelIndexList const &selectedRows) const
bool isOnlyChildOfRoot(RowLocation const &location) const override
void closeEditorIfCellIsUneditable(QModelIndexForMainModel const &cellIndex, Cell const &cell)
bool rowRemovalWouldBeIneffective(QModelIndexForMainModel const &indexToRemove) const
QtStandardItemTreeModelAdapter m_adaptedMainModel
std::vector< RowLocation > selectedRowLocations() const override
QModelIndexForFilteredModel expanded(QModelIndexForFilteredModel const &index)
void setHeaderLabels(QStringList const &columnHeadings)
void insertSubtreeAt(RowLocation const &parent, int index, Subtree const &subtree) override
Cell cellAt(RowLocation location, int column) const override
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override
bool hasNoSelectedDescendants(QModelIndex const &index) const
QModelIndex applyNavigationResult(QtTreeCursorNavigationResult const &result)
QtTreeCursorNavigationResult moveNextUntilEditable(QModelIndex const &startingPoint)
void subscribe(JobTreeViewSubscriber *subscriber) override
bool isBeingEdited(QModelIndexForFilteredModel const &cellIndex) const
void removeRowAt(RowLocation const &location) override
bool isOnlyChild(QModelIndexForMainModel const &index) const
bool isEditable(const QModelIndex &index) const
void closeEditorIfOpenAtCell(QModelIndexForFilteredModel const &cellIndex)
QModelIndexForMainModel mapToMainModel(QModelIndexForFilteredModel const &filteredModelIndex) const
void replaceRows(std::vector< RowLocation > replacementPoints, std::vector< Subtree > replacements) override
void replaceSubtreeAt(RowLocation const &rootToRemove, Subtree const &toInsert) override
void appendSubtreeAt(RowLocation const &parent, Subtree const &subtree) override
RowLocation insertChildRowOf(RowLocation const &parent, int beforeRow, std::vector< Cell > const &rowText) override
std::pair< QModelIndexForFilteredModel, bool > findOrMakeCellBelow(QModelIndexForFilteredModel const &index)
QModelIndexForMainModel fromMainModel(QModelIndex const &mainModelIndex) const
void setHintsForColumn(int column, std::unique_ptr< HintStrategy > hintStrategy) override
void setCellsAt(RowLocation const &location, std::vector< Cell > const &rowText) override
void editAt(QModelIndexForFilteredModel const &index)
void filterRowsBy(std::unique_ptr< RowPredicate > predicate) override
std::vector< Cell > cellsAt(RowLocation const &location) const override
void keyPressEvent(QKeyEvent *event) override
void setCellAt(RowLocation location, int column, Cell const &cellText) override
void removeRows(std::vector< RowLocation > rowsToRemove) override
void setCellAtCellIndex(QModelIndexForMainModel const &index, Cell const &newCellProperties)
QModelIndexForMainModel appendEmptyChildRow(QModelIndexForMainModel const &parent)
QModelIndexForMainModel appendChildRow(QModelIndexForMainModel const &parent, std::vector< Cell > const &cells)
void setCellsAtRow(QModelIndexForMainModel const &rowIndex, std::vector< Cell > const &cells)
QModelIndexForMainModel appendEmptySiblingRow(QModelIndexForMainModel const &index)
std::vector< Cell > cellsAtRow(QModelIndexForMainModel const &firstCellIndex) const
QModelIndexForMainModel insertChildRow(QModelIndexForMainModel const &parent, int row, std::vector< Cell > const &cells)
QModelIndexForMainModel insertEmptyChildRow(QModelIndexForMainModel const &parent, int column)
void enumerateCellsInRow(QModelIndexForMainModel const &startIndex, int columns, Action const &action) const
Enumerates the first columnCount number of cells to the right of startAtCell, moving left to right.
Cell cellFromCellIndex(QModelIndexForMainModel const &index) const
QtTreeCursorNavigationResult moveCursorNext(QModelIndex const &currentIndex) const
QModelIndex moveCursorPrevious(QModelIndex const &currentIndex) const
RowLocation atIndex(QModelIndexForMainModel const &index) const
std::optional< QModelIndexForMainModel > indexIfExistsAt(RowLocation const &location, int column=0) const
QModelIndexForMainModel indexAt(RowLocation const &location, int column=0) const
HintStrategy : Provides an interface for generating hints to be used by a HintingLineEdit.
HintingLineEditFactory : A QStyledItemDelegate that produces HintingLineEdits using the given hint st...
std::pair< bool, QModelIndex > QtTreeCursorNavigationResult
EXPORT_OPT_MANTIDQT_COMMON std::vector< Cell > paddedCellsToWidth(std::vector< Cell > const &cells, Cell const &paddingCell, int paddedWidth)
Definition Cell.cpp:95
bool hasRowAbove(QModelIndex const &index)
std::vector< Row > Subtree
Definition Subtree.h:20
QModelIndex firstCellOnRowOf(QModelIndex const &index)
bool hasRowBelow(QModelIndex const &index)
QModelIndex above(QModelIndex const &index)
QModelIndex below(QModelIndex const &index)
bool areOnSameRow(QModelIndex const &a, QModelIndex const &b)