From 415a3d64e8845aac88663d6a3f42bcb4c1a22bfd Mon Sep 17 00:00:00 2001 From: Rusfort Date: Fri, 16 May 2025 09:42:02 +0200 Subject: [PATCH] Drag and drop fix --- gui/unified_view_model.py | 167 ++++++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 62 deletions(-) diff --git a/gui/unified_view_model.py b/gui/unified_view_model.py index 54ccc11..35488ad 100644 --- a/gui/unified_view_model.py +++ b/gui/unified_view_model.py @@ -907,21 +907,22 @@ class UnifiedViewModel(QAbstractItemModel): stream = QDataStream(encoded_data, QIODevice.OpenModeFlag.WriteOnly) # Store QPersistentModelIndex for robustness - persistent_indices = [] + # Collect file paths of dragged FileRule items + file_paths = [] for index in indexes: if index.isValid() and index.column() == 0: item = index.internalPointer() if isinstance(item, FileRule): - persistent_indices.append(QPersistentModelIndex(index)) - log.debug(f"mimeData: Added persistent index for file: {Path(item.file_path).name}") + file_paths.append(item.file_path) + log.debug(f"mimeData: Added file path for file: {Path(item.file_path).name}") - # Write the number of items first, then each persistent index - stream.writeInt32(len(persistent_indices)) # Use writeInt32 for potentially more items - for p_index in persistent_indices: - stream.writeQPersistentModelIndex(p_index) + # Write the number of items first, then each file path string + stream.writeInt32(len(file_paths)) # Use writeInt32 for potentially more items + for file_path in file_paths: + stream.writeQString(file_path) # Use writeQString for strings mime_data.setData(self.MIME_TYPE, encoded_data) - log.debug(f"mimeData: Encoded {len(persistent_indices)} FileRule persistent indices.") + log.debug(f"mimeData: Encoded {len(file_paths)} FileRule file paths.") return mime_data def canDropMimeData(self, data: QMimeData, action: Qt.DropAction, row: int, column: int, parent: QModelIndex) -> bool: @@ -956,48 +957,68 @@ class UnifiedViewModel(QAbstractItemModel): encoded_data = data.data(self.MIME_TYPE) stream = QDataStream(encoded_data, QIODevice.OpenModeFlag.ReadOnly) - # Read QPersistentModelIndex objects - persistent_indices = [] + # Read file paths from the stream + dragged_file_paths = [] num_items = stream.readInt32() - log.debug(f"dropMimeData: Decoding {num_items} persistent indices.") + log.debug(f"dropMimeData: Decoding {num_items} file paths.") for _ in range(num_items): - p_index = stream.readQPersistentModelIndex() - if p_index.isValid(): - persistent_indices.append(p_index) - else: - log.warning("dropMimeData: Decoded invalid persistent index. Skipping.") + dragged_file_paths.append(stream.readQString()) # Use readQString for strings - log.debug(f"dropMimeData: Decoded {len(persistent_indices)} valid persistent indices. Target Asset: '{target_asset_item.asset_name}'") + log.debug(f"dropMimeData: Decoded {len(dragged_file_paths)} file paths. Target Asset: '{target_asset_item.asset_name}'") - if not persistent_indices: - log.warning("dropMimeData: No valid persistent index information decoded.") + if not dragged_file_paths: + log.warning("dropMimeData: No file path information decoded.") + return False + + # Find the current FileRule objects and their indices based on file paths + dragged_items_with_indices = [] + for file_path in dragged_file_paths: + found_item = None + found_index = QModelIndex() + # Iterate through the model to find the FileRule object by file_path + for sr_row, source_rule in enumerate(self._source_rules): + for ar_row, asset_rule in enumerate(source_rule.assets): + for fr_row, file_rule in enumerate(asset_rule.files): + if file_rule.file_path == file_path: + found_item = file_rule + # Get the current index for this item + parent_asset_index = self.index(ar_row, 0, self.createIndex(sr_row, 0, source_rule)) + if parent_asset_index.isValid(): + found_index = self.index(fr_row, 0, parent_asset_index) + if found_index.isValid(): + dragged_items_with_indices.append((found_item, found_index)) + log.debug(f"dropMimeData: Found item and index for file: {Path(file_path).name}") + else: + log.warning(f"dropMimeData: Could not get valid index for found file item: {Path(file_path).name}") + else: + log.warning(f"dropMimeData: Could not get valid parent asset index for found file item: {Path(file_path).name}") + break # Found the file rule, move to the next dragged file path + if found_item: break # Found the file rule, move to the next dragged file path + if found_item: break # Found the file rule, move to the next dragged file path + + if not found_item: + log.warning(f"dropMimeData: Could not find FileRule item for path: {file_path}. Skipping.") + + + if not dragged_items_with_indices: + log.warning("dropMimeData: No valid FileRule items found in the model for the dragged paths.") return False # Keep track of original parents that might become empty original_parents_to_check = set() moved_files_new_indices = {} - # Process moves using the persistent indices - for p_source_index in persistent_indices: - # Convert persistent index back to a model index - source_file_index = QModelIndex(p_source_index) - - if not source_file_index.isValid(): - log.warning(f"dropMimeData: Persistent index is no longer valid. Skipping item.") - continue - - # Get the file item - file_item = source_file_index.internalPointer() - if not isinstance(file_item, FileRule): - log.error(f"dropMimeData: Index points to non-FileRule item after conversion. Skipping.") - continue - + # Process moves using the retrieved items and their current indices + for file_item, source_file_index in dragged_items_with_indices: # Track original parent for cleanup using the parent back-reference old_parent_asset = getattr(file_item, 'parent_asset', None) if old_parent_asset and isinstance(old_parent_asset, AssetRule): - original_parents_to_check.add(old_parent_asset) - else: - log.warning(f"dropMimeData: File '{Path(file_item.file_path).name}' has no valid parent asset reference for cleanup tracking.") + source_rule = getattr(old_parent_asset, 'parent_source', None) + if source_rule: + # Store a hashable representation (tuple of identifiers) + original_parents_to_check.add((source_rule.input_path, old_parent_asset.asset_name)) + else: + log.warning(f"dropMimeData: Original parent asset '{old_parent_asset.asset_name}' has no parent source reference for cleanup tracking.") # Perform the move using the model's method and the valid source_file_index @@ -1009,16 +1030,25 @@ class UnifiedViewModel(QAbstractItemModel): if file_item.target_asset_name_override != target_asset_item.asset_name: log.debug(f" Updating target override for '{Path(file_item.file_path).name}' to '{target_asset_item.asset_name}'") file_item.target_asset_name_override = target_asset_item.asset_name - # Need the *new* index of the moved file to emit dataChanged + # Need the *new* index of the moved file to emit dataChanged AND the override changed signal try: # Find the new row of the file item within the target parent's list new_row = target_asset_item.files.index(file_item) - # Create the index for the target asset column + # Create the index for the target asset column (for dataChanged) new_file_index_target_col = self.index(new_row, self.COL_TARGET_ASSET, parent) if new_file_index_target_col.isValid(): moved_files_new_indices[file_item.file_path] = new_file_index_target_col else: log.warning(f" Could not get valid *new* index for target column of moved file: {Path(file_item.file_path).name}") + + # Emit the targetAssetOverrideChanged signal for the handler + new_file_index_col_0 = self.index(new_row, 0, parent) # Index for column 0 + if new_file_index_col_0.isValid(): + self.targetAssetOverrideChanged.emit(file_item, target_asset_item.asset_name, new_file_index_col_0) + log.debug(f" Emitted targetAssetOverrideChanged for '{Path(file_item.file_path).name}'") + else: + log.warning(f" Could not get valid *new* index for column 0 of moved file to emit signal: {Path(file_item.file_path).name}") + except ValueError: log.error(f" Could not find moved file '{Path(file_item.file_path).name}' in target parent's list after move.") @@ -1034,30 +1064,43 @@ class UnifiedViewModel(QAbstractItemModel): self.dataChanged.emit(new_index, new_index, [Qt.DisplayRole, Qt.EditRole]) # --- Cleanup: Remove any original parent AssetRules that are now empty --- - log.debug(f"dropMimeData: Checking original parents for cleanup: {[asset.asset_name for asset in original_parents_to_check]}") - # Convert set to list to iterate and allow removal from the model - for asset_rule_to_check in list(original_parents_to_check): - try: - # Re-check if the asset is still in the model and is now empty - # Use parent back-reference to find the source rule - source_rule = getattr(asset_rule_to_check, 'parent_source', None) - if source_rule: - # Check if the asset rule is still in its parent's list - if asset_rule_to_check in source_rule.assets: - if not asset_rule_to_check.files and asset_rule_to_check is not target_asset_item: - log.info(f"dropMimeData: Attempting cleanup of now empty original parent: '{asset_rule_to_check.asset_name}'") - if not self.removeAssetRule(asset_rule_to_check): - log.warning(f"dropMimeData: Failed to remove empty original parent '{asset_rule_to_check.asset_name}'.") - elif asset_rule_to_check.files: - log.debug(f"dropMimeData: Original parent '{asset_rule_to_check.asset_name}' is not empty after moves. Skipping cleanup.") - # If it's the target asset, we don't remove it - else: - log.warning(f"dropMimeData: Cleanup check failed. Original parent asset '{asset_rule_to_check.asset_name}' not found in its source rule's list.") - else: - log.warning(f"dropMimeData: Cleanup check failed. Original parent asset '{asset_rule_to_check.asset_name}' has no parent source reference.") + log.debug(f"dropMimeData: Checking original parents for cleanup: {[f'{path}/{name}' for path, name in original_parents_to_check]}") + # Convert set to list to iterate + for source_path, asset_name_to_check in list(original_parents_to_check): + found_asset_rule_to_check = None + # Find the AssetRule object based on source_path and asset_name + for source_rule in self._source_rules: + if source_rule.input_path == source_path: + for asset_rule in source_rule.assets: + if asset_rule.asset_name == asset_name_to_check: + found_asset_rule_to_check = asset_rule + break + if found_asset_rule_to_check: break - except Exception as e: - log.exception(f"dropMimeData: Error during cleanup check for parent '{asset_rule_to_check.asset_name}': {e}") + if found_asset_rule_to_check: + try: + # Re-check if the asset is still in the model and is now empty + # Use parent back-reference to find the source rule (should be the same as source_rule found above) + source_rule = getattr(found_asset_rule_to_check, 'parent_source', None) + if source_rule: + # Check if the asset rule is still in its parent's list + if found_asset_rule_to_check in source_rule.assets: + if not found_asset_rule_to_check.files and found_asset_rule_to_check is not target_asset_item: + log.info(f"dropMimeData: Attempting cleanup of now empty original parent: '{found_asset_rule_to_check.asset_name}'") + if not self.removeAssetRule(found_asset_rule_to_check): + log.warning(f"dropMimeData: Failed to remove empty original parent '{found_asset_rule_to_check.asset_name}'.") + elif found_asset_rule_to_check.files: + log.debug(f"dropMimeData: Original parent '{found_asset_rule_to_check.asset_name}' is not empty after moves. Skipping cleanup.") + # If it's the target asset, we don't remove it + else: + log.warning(f"dropMimeData: Cleanup check failed. Original parent asset '{found_asset_rule_to_check.asset_name}' not found in its source rule's list.") + else: + log.warning(f"dropMimeData: Cleanup check failed. Original parent asset '{found_asset_rule_to_check.asset_name}' has no parent source reference.") + + except Exception as e: + log.exception(f"dropMimeData: Error during cleanup check for parent '{found_asset_rule_to_check.asset_name}': {e}") + else: + log.warning(f"dropMimeData: Could not find original parent asset '{asset_name_to_check}' for cleanup.") return True \ No newline at end of file