void AMActionHistoryTreeView3::onEnteredIndex(const QModelIndex &index){ AMActionHistoryModel3 *historyModel = qobject_cast<AMActionHistoryModel3*>(model()); if(!historyModel) return; if(shiftKeyDown_){ // If shift key is down and we don't already have the forbidden cursor and we're hovering over an item that is not a sibling then we can't shift click ... go forbidden if(!hasOverriddenShiftCursor_ && (index.parent() != lastClickedIndex_.parent()) ){ hasOverriddenShiftCursor_ = true; setCursor(*forbiddenCursor_); } // Or if the shift key is down and we already have the forbidden cursor and we're hovering over an item that is a sibling then we can shift click ... go default // Also check lastClickWasDeselect ... we can never shift click if that sucker is on else if(hasOverriddenShiftCursor_ && (index.parent() == lastClickedIndex_.parent() && !lastClickWasDeselect_) ){ hasOverriddenShiftCursor_ = false; setCursor(*regularCursor_); } } if(controlKeyDown_){ ParentSelectMap selectMap = model()->data(index, AMActionHistoryModel3::ParentSelectRole).value<ParentSelectMap>(); // If control key is down and we have overridden the cursor ... go default if(hasOverriddenControlCursor_){ hasOverriddenControlCursor_ = false; setCursor(*regularCursor_); } // If control key is down and we don't already have the forbidden cursor and this viewer is in the selection map and this index isn't parentSelected // and we have a parent somewhere selected and our direct parent isn't selected one way or the other ... go forbidden // Just trying to make sure that you if you have three levels (A parents B, B parents C) if A or above is selected but B is not then you can't control-click in C or below ... what would that mean anyway if(!hasOverriddenControlCursor_ && selectMap.contains(this) && !selectMap.value(this) && hasSelectedParent(index) && !(selectionModel()->isSelected(index.parent()) || model()->data(index.parent(), AMActionHistoryModel3::ParentSelectRole).value<ParentSelectMap>().value(this)) ){ hasOverriddenControlCursor_ = true; setCursor(*forbiddenCursor_); } } }
void AMActionHistoryModel3::markIndexGroup(const QModelIndex &index, QAbstractItemView *viewer, bool selected){ // Just do the regular markParentSelected if this is a top-level action log item (parent index is invalid) if(!index.parent().isValid()){ recurseMarkParentSelected(index, viewer, selected); return; } // Check if the action log item inherited the loop action (if so, then we need to multi-select/deselect the children) AMActionLogItem3 *item = logItem(index.parent()); if(item->actionInheritedLoop()){ // Figure out your index in the original loop, and select/deselect all of your siblings with the same loop index int actionsPerLoop = rowCount(index.parent())/item->numberOfLoops(); int indexInLoop = (index.row()%actionsPerLoop); QModelIndex markingIndex; for(int x = 0; x < rowCount(index.parent()); x++){ if(x%actionsPerLoop == indexInLoop){ markingIndex = this->index(x, 0, index.parent()); recurseMarkParentSelected(markingIndex, viewer, selected); emit dataChanged(markingIndex, markingIndex); } } } else recurseMarkParentSelected(index, viewer, selected); // Check to see if there are any children left at this level. If not, then deselect the parent as well (no point selecting a list or loop when the user has removed all of the contents) int siblingsSelected = 0; for(int x = 0; x < rowCount(index.parent()); x++){ ParentSelectMap selectMap = data(this->index(x, 0, index.parent()), AMActionHistoryModel3::ParentSelectRole).value<ParentSelectMap>(); if(selectMap.contains(viewer) && selectMap.value(viewer)) siblingsSelected++; } if(siblingsSelected == 0){ // Actually deselect in the selection model if the parent was actually selected if(viewer->selectionModel()->isSelected(index.parent())){ QItemSelectionModel::SelectionFlags deselectFlag = QItemSelectionModel::Deselect; viewer->selectionModel()->select(index.parent(), deselectFlag); ParentSelectMap selectMap = data(index.parent(), AMActionHistoryModel3::ParentSelectRole).value<ParentSelectMap>(); if(selectMap.contains(viewer)){ AMActionLogItem3 *item = logItem(index.parent()); // Make sure it doesn't get parentSelected somehow item->setParentSelected(viewer, false); } emit dataChanged(index.parent(), index.parent()); } // Otherwise just use the group deselect for the parentSelected mapping else markIndexGroupAsDeselected(index.parent(), viewer); } }
QItemSelectionModel::SelectionFlags AMActionHistoryTreeView3::selectionCommand(const QModelIndex &index, const QEvent *selectionEvent) const{ // Initialize by calling the parent version QItemSelectionModel::SelectionFlags retFlags = QTreeView::selectionCommand(index, selectionEvent); bool hasShiftModifier, hasControlModifier; // If we have a valid index we have a lot of work to do (also make sure this thing is actually an AMActionHistoryModel viewing tree) if(index.isValid()){ AMActionHistoryModel3 *historyModel = qobject_cast<AMActionHistoryModel3*>(model()); // We only care about the mouse button press events otherwise return the parent class call if( !historyModel || (selectionEvent->type() != QEvent::MouseButtonPress) ) return QTreeView::selectionCommand(index, selectionEvent); QMouseEvent *mouseEvent = (QMouseEvent*)selectionEvent; // Ignore clicks on non-selectable indices // We can ignore: not enabled indexes, and not selectable indexes if(!index.flags().testFlag(Qt::ItemIsEnabled) || !index.flags().testFlag(Qt::ItemIsSelectable)) return QTreeView::selectionCommand(index, selectionEvent); // Grab keyboard shift and control modifiers Qt::KeyboardModifiers currentModifiers = mouseEvent->modifiers(); hasControlModifier = currentModifiers&Qt::ControlModifier; hasShiftModifier = currentModifiers&Qt::ShiftModifier; ParentSelectMap selectMap = model()->data(index, AMActionHistoryModel3::ParentSelectRole).value<ParentSelectMap>(); // This is pretty bad, you can tell too much is in here that doesn't belong when I need to do a const cast AMActionHistoryTreeView3 *checkViewer = const_cast<AMActionHistoryTreeView3*>(this); bool parentIsSelected = false; // Figure out if the parent is selected via the mapping or the selection model if(index.parent().isValid()){ ParentSelectMap parentsSelectMap = model()->data(index.parent(), AMActionHistoryModel3::ParentSelectRole).value<ParentSelectMap>(); if(parentsSelectMap.contains(checkViewer)) parentIsSelected |= parentsSelectMap.value(checkViewer); parentIsSelected |= selectionModel()->isSelected(index.parent()); } // On control clicks where the parent is selected and this index is as well, do the group deselect and return noUpdate in the selection flags if(hasControlModifier && index.parent().isValid() && parentIsSelected && selectMap.contains(checkViewer) && selectMap.value(checkViewer)){ historyModel->markIndexGroupAsDeselected(index, checkViewer); return QItemSelectionModel::NoUpdate; } // On control clicks where the parent is selected and this index is not, do the group select and return noUpdate in the selection flags else if(hasControlModifier && index.parent().isValid() && parentIsSelected && selectMap.contains(checkViewer) && !selectMap.value(checkViewer)){ historyModel->markIndexGroupAsSelected(index, checkViewer); return QItemSelectionModel::NoUpdate; } // Ignore regular clicks on the an index that is already actually selected if it's the only one selected if( !hasControlModifier && (actuallySelectedByClickingCount_ == 1) && (selectionModel()->selectedIndexes().contains(index)) ){ return QItemSelectionModel::NoUpdate; } } // Log some flags ... might be an easier way now that I know how to use QFlag::testFlag() bool clearFlag = (retFlags&QItemSelectionModel::Clear); bool selectFlag = (retFlags&QItemSelectionModel::Select); bool deselectFlag = (retFlags&QItemSelectionModel::Deselect); bool toggleFlag = (retFlags&QItemSelectionModel::Toggle); bool indexAlreadyOn = selectionModel()->selectedIndexes().contains(index); bool noUpdateFlag = (retFlags&QItemSelectionModel::NoUpdate); // An actual selection! Selected OR (Toggled AND NOT Currently On) OR (noUpdate AND Currently On) bool selectedTrue = selectFlag || (toggleFlag && !indexAlreadyOn) || (noUpdateFlag && indexAlreadyOn); // An actual deselection! Deselected OR (Toggled AND Currently On) OR (noUpdate AND NOT Currently On) OR (Actually selected an invalid index) bool selectedFalse = deselectFlag || (toggleFlag && indexAlreadyOn) || (noUpdateFlag && !indexAlreadyOn) || (selectedTrue && !index.isValid()); // If we actually selected and the column is 0 (we only care about that one) log the state and emit actuallySelectedByClicking if(selectedTrue && (index.column() == 0) ){ lastClickWasDeselect_ = false; lastClickedIndex_ = index; emit actuallySelectedByClicking(index, clearFlag); } // If we actually deselected and the column is 0 (we only care about that one) log the state (particularily lastClickWasDeselect) and emit actuallyDeselectedByClicking else if(selectedFalse && (index.column() == 0) ){ QModelIndexList currentSelections = selectionModel()->selection().indexes(); if(currentSelections.count() == 2){ currentSelections.removeOne(index); lastClickedIndex_ = currentSelections.at(0); lastClickWasDeselect_ = false; } else lastClickWasDeselect_ = true; emit actuallyDeselectedByClicking(index, clearFlag); } // If neither selected nor deselected but clear flag is on then emit clearedByClicking else if(clearFlag){ lastClickWasDeselect_ = false; emit clearedByClicking(); } // Return the flags we have return retFlags; }