Drag and drop fix

This commit is contained in:
Rusfort 2025-05-16 09:42:02 +02:00
parent 4bf2513f31
commit 415a3d64e8

View File

@ -907,21 +907,22 @@ class UnifiedViewModel(QAbstractItemModel):
stream = QDataStream(encoded_data, QIODevice.OpenModeFlag.WriteOnly) stream = QDataStream(encoded_data, QIODevice.OpenModeFlag.WriteOnly)
# Store QPersistentModelIndex for robustness # Store QPersistentModelIndex for robustness
persistent_indices = [] # Collect file paths of dragged FileRule items
file_paths = []
for index in indexes: for index in indexes:
if index.isValid() and index.column() == 0: if index.isValid() and index.column() == 0:
item = index.internalPointer() item = index.internalPointer()
if isinstance(item, FileRule): if isinstance(item, FileRule):
persistent_indices.append(QPersistentModelIndex(index)) file_paths.append(item.file_path)
log.debug(f"mimeData: Added persistent index for file: {Path(item.file_path).name}") log.debug(f"mimeData: Added file path for file: {Path(item.file_path).name}")
# Write the number of items first, then each persistent index # Write the number of items first, then each file path string
stream.writeInt32(len(persistent_indices)) # Use writeInt32 for potentially more items stream.writeInt32(len(file_paths)) # Use writeInt32 for potentially more items
for p_index in persistent_indices: for file_path in file_paths:
stream.writeQPersistentModelIndex(p_index) stream.writeQString(file_path) # Use writeQString for strings
mime_data.setData(self.MIME_TYPE, encoded_data) 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 return mime_data
def canDropMimeData(self, data: QMimeData, action: Qt.DropAction, row: int, column: int, parent: QModelIndex) -> bool: 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) encoded_data = data.data(self.MIME_TYPE)
stream = QDataStream(encoded_data, QIODevice.OpenModeFlag.ReadOnly) stream = QDataStream(encoded_data, QIODevice.OpenModeFlag.ReadOnly)
# Read QPersistentModelIndex objects # Read file paths from the stream
persistent_indices = [] dragged_file_paths = []
num_items = stream.readInt32() 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): for _ in range(num_items):
p_index = stream.readQPersistentModelIndex() dragged_file_paths.append(stream.readQString()) # Use readQString for strings
if p_index.isValid():
persistent_indices.append(p_index)
else:
log.warning("dropMimeData: Decoded invalid persistent index. Skipping.")
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: if not dragged_file_paths:
log.warning("dropMimeData: No valid persistent index information decoded.") 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 return False
# Keep track of original parents that might become empty # Keep track of original parents that might become empty
original_parents_to_check = set() original_parents_to_check = set()
moved_files_new_indices = {} moved_files_new_indices = {}
# Process moves using the persistent indices # Process moves using the retrieved items and their current indices
for p_source_index in persistent_indices: for file_item, source_file_index in dragged_items_with_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
# Track original parent for cleanup using the parent back-reference # Track original parent for cleanup using the parent back-reference
old_parent_asset = getattr(file_item, 'parent_asset', None) old_parent_asset = getattr(file_item, 'parent_asset', None)
if old_parent_asset and isinstance(old_parent_asset, AssetRule): if old_parent_asset and isinstance(old_parent_asset, AssetRule):
original_parents_to_check.add(old_parent_asset) source_rule = getattr(old_parent_asset, 'parent_source', None)
else: if source_rule:
log.warning(f"dropMimeData: File '{Path(file_item.file_path).name}' has no valid parent asset reference for cleanup tracking.") # 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 # 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: 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}'") 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 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: try:
# Find the new row of the file item within the target parent's list # Find the new row of the file item within the target parent's list
new_row = target_asset_item.files.index(file_item) 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) new_file_index_target_col = self.index(new_row, self.COL_TARGET_ASSET, parent)
if new_file_index_target_col.isValid(): if new_file_index_target_col.isValid():
moved_files_new_indices[file_item.file_path] = new_file_index_target_col moved_files_new_indices[file_item.file_path] = new_file_index_target_col
else: else:
log.warning(f" Could not get valid *new* index for target column of moved file: {Path(file_item.file_path).name}") 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: except ValueError:
log.error(f" Could not find moved file '{Path(file_item.file_path).name}' in target parent's list after move.") 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]) self.dataChanged.emit(new_index, new_index, [Qt.DisplayRole, Qt.EditRole])
# --- Cleanup: Remove any original parent AssetRules that are now empty --- # --- 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]}") 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 and allow removal from the model # Convert set to list to iterate
for asset_rule_to_check in list(original_parents_to_check): for source_path, asset_name_to_check in list(original_parents_to_check):
try: found_asset_rule_to_check = None
# Re-check if the asset is still in the model and is now empty # Find the AssetRule object based on source_path and asset_name
# Use parent back-reference to find the source rule for source_rule in self._source_rules:
source_rule = getattr(asset_rule_to_check, 'parent_source', None) if source_rule.input_path == source_path:
if source_rule: for asset_rule in source_rule.assets:
# Check if the asset rule is still in its parent's list if asset_rule.asset_name == asset_name_to_check:
if asset_rule_to_check in source_rule.assets: found_asset_rule_to_check = asset_rule
if not asset_rule_to_check.files and asset_rule_to_check is not target_asset_item: break
log.info(f"dropMimeData: Attempting cleanup of now empty original parent: '{asset_rule_to_check.asset_name}'") if found_asset_rule_to_check: break
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.")
except Exception as e: if found_asset_rule_to_check:
log.exception(f"dropMimeData: Error during cleanup check for parent '{asset_rule_to_check.asset_name}': {e}") 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 return True