88from PySide6 .QtWidgets import (QApplication , QMainWindow , QWidget , QVBoxLayout ,
99 QHBoxLayout , QListWidget , QListWidgetItem ,
1010 QPushButton , QLineEdit , QSplitter , QMessageBox ,
11- QListView , QMenu , QProgressDialog , QCompleter )
11+ QListView , QMenu , QProgressDialog , QCompleter ,
12+ QDialog , QLabel )
1213from PySide6 .QtCore import Qt , QSize
1314from PySide6 .QtGui import QIcon , QPixmap , QShortcut , QKeySequence
1415
@@ -215,6 +216,60 @@ class HistoryEntry:
215216 name : str
216217 clicked_item_id : Optional [str ] = None
217218
219+
220+ class MoveFileDialog (QDialog ):
221+ def __init__ (self , parent = None , gcache = None ):
222+ super ().__init__ (parent )
223+ self .setWindowTitle ("Move File" )
224+ self .setMinimumWidth (400 )
225+ self .gcache = gcache
226+ self .resulting_tuple = None
227+
228+ layout = QVBoxLayout (self )
229+ layout .addWidget (QLabel ("Move to folder (e.g. 'course/subfolder'):" ))
230+
231+ self .line_edit = QLineEdit ()
232+ self .completer = QCompleter ()
233+ self .completer_model = QStringListModel ()
234+ self .completer .setModel (self .completer_model )
235+ self .completer .setCaseSensitivity (Qt .CaseInsensitive )
236+ self .completer .setCompletionMode (QCompleter .PopupCompletion )
237+ self .completer .setFilterMode (Qt .MatchContains )
238+ self .line_edit .setCompleter (self .completer )
239+ self .line_edit .textEdited .connect (self .update_completer )
240+ layout .addWidget (self .line_edit )
241+
242+ buttons = QHBoxLayout ()
243+ self .move_btn = QPushButton ("Move" )
244+ self .move_btn .clicked .connect (self .handle_move )
245+ self .cancel_btn = QPushButton ("Cancel" )
246+ self .cancel_btn .clicked .connect (self .reject )
247+ buttons .addWidget (self .move_btn )
248+ buttons .addWidget (self .cancel_btn )
249+ layout .addLayout (buttons )
250+
251+ self .line_edit .returnPressed .connect (self .handle_move )
252+
253+ def update_completer (self , text ):
254+ if not text or not self .gcache :
255+ return
256+ import gdrive
257+ suggestions = gdrive .get_course_suggestions (text )
258+ self .completer_model .setStringList (suggestions )
259+
260+ def handle_move (self ):
261+ query = self .line_edit .text ()
262+ if not query :
263+ return
264+ import gdrive
265+ try :
266+ self .resulting_tuple = gdrive .get_gfolders_for_course (query , invite_to_add = False )
267+ self .accept ()
268+ except FileNotFoundError as e :
269+ QMessageBox .warning (self , "Not Found" , str (e ))
270+ except Exception as e :
271+ QMessageBox .critical (self , "Error" , f"An error occurred: { e } " )
272+
218273class GDriveApp (QMainWindow ):
219274 thumbnail_loaded_signal = Signal (str , QImage )
220275
@@ -644,6 +699,7 @@ def on_context_menu(self, pos):
644699 copy_id_action = menu .addAction ("Copy ID" )
645700 copy_link_action = menu .addAction ("Copy URL" )
646701 open_browser_action = menu .addAction ("Open in browser..." )
702+ move_file_action = menu .addAction ("Move file..." )
647703
648704 action = menu .exec (self .file_view .viewport ().mapToGlobal (pos ))
649705 url = gdrive_base .GENERIC_LINK_PREFIX + file_data ['id' ]
@@ -653,6 +709,36 @@ def on_context_menu(self, pos):
653709 QApplication .clipboard ().setText (url )
654710 elif action == open_browser_action :
655711 webbrowser .open (url )
712+ elif action == move_file_action :
713+ self .move_file (file_data )
714+
715+ def move_file (self , file_data : dict [str , Any ]):
716+ dialog = MoveFileDialog (self , self .gcache )
717+ if dialog .exec ():
718+ try :
719+ import gdrive
720+ # Use a progress dialog for the move operation as it can be slow
721+ progress = QProgressDialog ("Moving file..." , None , 0 , 0 , self )
722+ progress .setWindowTitle ("Moving" )
723+ progress .setWindowModality (Qt .WindowModal )
724+ progress .show ()
725+ QApplication .processEvents ()
726+
727+ gdrive .move_gfile (file_data ['id' ], dialog .resulting_tuple )
728+
729+ progress .close ()
730+
731+ self .refresh ()
732+
733+ except Exception as e :
734+ QMessageBox .critical (self , "Error" , f"Failed to move file: { e } " )
735+
736+ def refresh (self ):
737+ """Reloads the current folder (to pick up file changes)"""
738+ if self .current_folder_id in ["my_drive" , "shared_with_me" ]:
739+ self .load_root (self .current_folder_id , add_history = False )
740+ else :
741+ self .load_folder (self .current_folder_id , self .address_bar .text (), add_history = False )
656742
657743 def on_item_activated (self , item : QListWidgetItem ):
658744 file_data = item .data (Qt .UserRole )
0 commit comments