diff --git a/src/webdrop_bridge/core/drag_interceptor.py b/src/webdrop_bridge/core/drag_interceptor.py index 6f37ce5..76782fe 100644 --- a/src/webdrop_bridge/core/drag_interceptor.py +++ b/src/webdrop_bridge/core/drag_interceptor.py @@ -146,7 +146,7 @@ class DragInterceptor(QWidget): file_paths: Single local file path or list of local file paths Returns: - True if drag was created successfully + True if drag was initiated successfully """ try: # Normalize to list @@ -170,10 +170,15 @@ class DragInterceptor(QWidget): # drag.setPixmap(...) # Start drag operation (blocks until drop or cancel) - # Qt.CopyAction allows copying files + # In RDP/remote environments, drag.exec() may not return the expected result + # even though the drag was successful. We accept the drag as successful if + # it completes without exception. result = drag.exec(Qt.DropAction.CopyAction) - return result == Qt.DropAction.CopyAction + # Accept any drop action result (including NoDropAction) as successful + # in remote environments like RDP where OS feedback is unreliable. + logger.debug(f"Drag operation completed with result: {result}") + return True except Exception as e: logger.exception(f"Error creating native drag: {e}") diff --git a/src/webdrop_bridge/ui/main_window.py b/src/webdrop_bridge/ui/main_window.py index 2549857..f75d872 100644 --- a/src/webdrop_bridge/ui/main_window.py +++ b/src/webdrop_bridge/ui/main_window.py @@ -1185,20 +1185,55 @@ class MainWindow(QMainWindow): if sys.platform == "darwin": # Prompt for an app and open the file with the selected app. - script = ( - "on run argv\n" - "set targetFile to POSIX file (item 1 of argv)\n" - "set chosenApp to choose application\n" - 'tell application "Finder" to open targetFile using chosenApp\n' - "end run" - ) - result = subprocess.run( - ["osascript", "-e", script, file_path], - check=False, - capture_output=True, - text=True, - ) - return result.returncode == 0 + # Use AppleScript with choose application and open the file. + # This more reliably opens files with chosen applications. + # Use a simple, more direct approach + # Get the chosen app via AppleScript, then use open command + get_app_script = '''choose application with title "Select an application to open the file"''' + try: + # Get the chosen application + app_result = subprocess.run( + ["osascript", "-e", get_app_script], + check=False, + capture_output=True, + text=True, + timeout=30, + ) + + if app_result.returncode != 0: + logger.warning(f"User cancelled app chooser or error occurred: {app_result.stderr}") + return False + + # Get the application name (strip whitespace) + chosen_app = app_result.stdout.strip() + if not chosen_app: + logger.warning("No application was selected") + return False + + logger.info(f"User selected app: {chosen_app}") + + # Now open the file with the chosen app using the 'open' command + open_result = subprocess.run( + ["open", "-a", chosen_app, normalized_path], + check=False, + capture_output=True, + text=True, + timeout=10, + ) + + if open_result.returncode == 0: + logger.info(f"Opened '{normalized_path}' with '{chosen_app}'") + return True + else: + logger.warning(f"Failed to open file with '{chosen_app}': {open_result.stderr}") + return False + + except subprocess.TimeoutExpired: + logger.warning("App chooser timed out") + return False + except Exception as e: + logger.warning(f"Error in macOS app chooser: {e}") + return False logger.warning(f"Open-with chooser not implemented for platform: {sys.platform}") return False diff --git a/tests/unit/test_main_window.py b/tests/unit/test_main_window.py index 161be8a..be89ab6 100644 --- a/tests/unit/test_main_window.py +++ b/tests/unit/test_main_window.py @@ -206,11 +206,31 @@ class TestMainWindowOpenWith: test_file = sample_config.allowed_roots[0] / "open_with_macos.txt" test_file.write_text("test") - class _Result: + call_count = [0] # Use list to make it mutable in nested function + + class _AppChooseResult: returncode = 0 - + stdout = "TextEdit" # Simulated chosen app name + + class _OpenResult: + returncode = 0 + stdout = "" + + def mock_run(*args, **kwargs): + """Mock subprocess.run with two different behaviors per call.""" + call_count[0] += 1 + # First call: osascript to choose application + if call_count[0] == 1: + return _AppChooseResult() + # Second call: open command to open the file + elif call_count[0] == 2: + return _OpenResult() + else: + raise AssertionError(f"Unexpected call #{call_count[0]} to subprocess.run") + + with patch("webdrop_bridge.ui.main_window.sys.platform", "darwin"): - with patch("webdrop_bridge.ui.main_window.subprocess.run", return_value=_Result()): + with patch("webdrop_bridge.ui.main_window.subprocess.run", side_effect=mock_run): assert window._open_with_app_chooser(str(test_file)) is True def test_open_with_app_chooser_unsupported_platform(self, qtbot, sample_config):