diff --git a/sharepoint_browser.py b/sharepoint_browser.py index 7844c51..4b563ca 100644 --- a/sharepoint_browser.py +++ b/sharepoint_browser.py @@ -260,7 +260,7 @@ class SharePointApp(wx.Frame): self.tree_item_data = {} # Mappenoder -> {type, id, name, drive_id, path} self.tree_root = None self.is_navigating_back = False - self.is_editing = False # Låse-state ved filredigering + self.active_edits = {} # item_id -> { "name": name, "event": Event, "waiting": bool } # System Ikoner (ArtProvider - mest basale for kompatibilitet) self.image_list = wx.ImageList(16, 16) @@ -270,7 +270,6 @@ class SharePointApp(wx.Frame): self.idx_file = self.image_list.Add(wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, (16, 16))) # File # Threading/Sync til filredigering - self.edit_wait_event = threading.Event() # MSAL Cache self.msal_app = msal.PublicClientApplication(CLIENT_ID, authority=AUTHORITY) @@ -440,7 +439,7 @@ class SharePointApp(wx.Frame): if item['type'] == "FILE": edit_item = menu.Append(wx.ID_ANY, self.get_txt("msg_edit_file")) - self.Bind(wx.EVT_MENU, lambda e: threading.Thread(target=self.process_file, args=(item['id'], item['name']), daemon=True).start(), edit_item) + self.Bind(wx.EVT_MENU, lambda e, i=item: self.open_file(i), edit_item) if item['type'] in ["FILE", "FOLDER"]: rename_item = menu.Append(wx.ID_ANY, f"{self.get_txt('msg_rename')} '{item['name']}'") @@ -475,7 +474,6 @@ class SharePointApp(wx.Frame): menu.Destroy() def on_tree_right_click(self, event): - if self.is_editing: return item = event.GetItem() if not item.IsOk() or item == self.tree_root: return @@ -697,10 +695,34 @@ class SharePointApp(wx.Frame): wx.CallAfter(_do) def on_done_editing_clicked(self, event): - print("[DEBUG] 'Jeg er færdig'-knap klikket.") - self.set_status("Knap trykket - Uploader nu...") - self.edit_wait_event.set() - self.info_bar.Dismiss() + waiting_files = [fid for fid, d in self.active_edits.items() if d.get("waiting")] + if not waiting_files: + return + + if len(waiting_files) == 1: + fid = waiting_files[0] + self.active_edits[fid]["event"].set() + else: + # Show menu to let user pick which file is finished + menu = wx.Menu() + for fid in waiting_files: + name = self.active_edits[fid]["name"] + item = menu.Append(wx.ID_ANY, f"Gem '{name}'") + # closure to capture fid + def make_handler(f_id): + return lambda e: self.active_edits[f_id]["event"].set() + self.Bind(wx.EVT_MENU, make_handler(fid), item) + + menu.AppendSeparator() + item_all = menu.Append(wx.ID_ANY, "Gem alle") + def handle_all(e): + for f in waiting_files: + if f in self.active_edits: + self.active_edits[f]["event"].set() + self.Bind(wx.EVT_MENU, handle_all, item_all) + + self.PopupMenu(menu) + menu.Destroy() def on_language_changed(self, event): selection = self.lang_choice.GetSelection() @@ -743,7 +765,7 @@ class SharePointApp(wx.Frame): self._refresh_current_view() # Gendanner list-item tekster (Mappe/Fil) def on_close_window(self, event): - if self.is_editing: + if self.active_edits: self.show_info(self.get_txt("msg_edit_warning"), wx.ICON_WARNING, auto_hide=False) return event.Skip() @@ -759,7 +781,6 @@ class SharePointApp(wx.Frame): wx.CallAfter(_do) def on_refresh(self, event=None): - if self.is_editing: return selected = self.tree_ctrl.GetSelection() if not selected.IsOk() or selected == self.tree_root: @@ -1009,7 +1030,7 @@ class SharePointApp(wx.Frame): self.tree_ctrl.SelectItem(target_node) def on_tree_selected(self, event): - if not self or self.is_editing: return + if not self: return item = event.GetItem() data = self.tree_item_data.get(item) if not data: @@ -1120,15 +1141,13 @@ class SharePointApp(wx.Frame): pass def on_item_activated(self, event): - if self.is_editing: return item_idx = event.GetIndex() item = self.current_items[item_idx] if item['type'] in ["SITE", "DRIVE", "FOLDER"]: self._sync_tree_selection(item['id']) elif item['type'] == "FILE": - self.current_drive_id = item['drive_id'] - threading.Thread(target=self.process_file, args=(item['id'], item['name']), daemon=True).start() + self.open_file(item) def _sync_tree_selection(self, target_id): selected = self.tree_ctrl.GetSelection() @@ -1151,7 +1170,6 @@ class SharePointApp(wx.Frame): child, cookie = self.tree_ctrl.GetNextChild(selected, cookie) def go_back(self, event=None): - if self.is_editing: return if len(self.history) > 1: self.history.pop() # Remove current prev_item = self.history[-1] # Peak at previous @@ -1159,15 +1177,58 @@ class SharePointApp(wx.Frame): self.tree_ctrl.SelectItem(prev_item) self.is_navigating_back = False - def process_file(self, item_id, file_name): + def open_file(self, item): + item_id = item['id'] + file_name = item['name'] + drive_id = item['drive_id'] + + if item_id in self.active_edits: + self.show_info(f"'{file_name}' er allerede ved at blive redigeret.", wx.ICON_INFORMATION) + return + + if len(self.active_edits) >= 10: + wx.MessageBox("Du kan kun have 10 filer åbne til redigering ad gangen.", "Maksimum grænse nået", wx.OK | wx.ICON_WARNING) + return + + threading.Thread(target=self.process_file, args=(item_id, file_name, drive_id), daemon=True).start() + + def update_edit_ui(self): + def _do(): + try: + if not self: return + count = len(self.active_edits) + waiting_count = sum(1 for d in self.active_edits.values() if d.get("waiting")) + + if waiting_count > 0: + self.done_btn.SetLabel(f"{self.get_txt('btn_save_changes')} ({waiting_count})") + self.done_btn.Show() + else: + self.done_btn.Hide() + + # Opdater statusbesked hvis der er aktive opgaver + if count > 0: + status_msg = f"Aktive filer: {count}" + if waiting_count > 0: + status_msg += f" ({waiting_count} venter på gem)" + self.set_status(status_msg) + else: + self.set_status(self.get_txt("status_ready")) + + self.Layout() + except RuntimeError: + pass + wx.CallAfter(_do) + + def process_file(self, item_id, file_name, drive_id): if not self.ensure_valid_token(): return - self.is_editing = True - self.lock_ui(True) + + edit_event = threading.Event() + self.active_edits[item_id] = {"name": file_name, "event": edit_event, "waiting": False} + self.update_edit_ui() + try: # 1. Lokation info - site_id = self.current_site_id - drive_id = self.current_drive_id - base_url = f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives/{drive_id}/items/{item_id}" + base_url = f"https://graph.microsoft.com/v1.0/drives/{drive_id}/items/{item_id}" # Unik undermappe baseret på ID, men brug originalt filnavn indeni item_hash = hashlib.md5(item_id.encode()).hexdigest()[:8] @@ -1217,12 +1278,15 @@ class SharePointApp(wx.Frame): pass else: self.set_status(self.get_txt("msg_waiting_for_file", name=file_name)) - self.edit_wait_event.clear() - wx.CallAfter(self.done_btn.Show) - wx.CallAfter(self.Layout) - self.edit_wait_event.wait() - wx.CallAfter(self.done_btn.Hide) - wx.CallAfter(self.Layout) + edit_event.clear() + self.active_edits[item_id]["waiting"] = True + self.update_edit_ui() + + edit_event.wait() + + if item_id in self.active_edits: + self.active_edits[item_id]["waiting"] = False + self.update_edit_ui() # 4. Tjek om noget er ændret new_hash = get_file_hash(local_path) @@ -1255,8 +1319,9 @@ class SharePointApp(wx.Frame): self.set_status(f"{self.get_txt('msg_error')}: {str(e)}") self.show_info(f"{self.get_txt('msg_error')}: {e}", wx.ICON_ERROR) finally: - self.is_editing = False - self.lock_ui(False) + if item_id in self.active_edits: + del self.active_edits[item_id] + self.update_edit_ui() if __name__ == "__main__": app = wx.App()