feat: implement natural sorting and column-based list view sorting with visual indicators

This commit is contained in:
Martin Tranberg
2026-03-31 16:56:10 +02:00
parent 784ca755d5
commit c5f18b520a

View File

@@ -9,6 +9,7 @@ import msal
import wx
import wx.lib.newevent
import webbrowser
import re
# --- STIHÅNDTERING (Til EXE-brug) ---
if getattr(sys, 'frozen', False):
@@ -246,6 +247,10 @@ STRINGS = {
if not os.path.exists(TEMP_DIR):
os.makedirs(TEMP_DIR)
def natural_sort_key(s):
return [int(text) if text.isdigit() else text.lower()
for text in re.split('([0-9]+)', str(s))]
class UploadDropTarget(wx.FileDropTarget):
def __init__(self, window, app):
wx.FileDropTarget.__init__(self)
@@ -436,14 +441,25 @@ class SharePointApp(wx.Frame):
self.active_edits = {} # item_id -> { "name": name, "event": Event, "waiting": bool }
self.favorites = settings.get("favorites", [])
self.fav_visible = settings.get("fav_visible", True)
self.sort_col = 0 # Default Name
self.sort_asc = True
# System Ikoner (Brug ART_CMN_DIALOG eller ART_TOOLBAR for mere farve på Windows)
# System Ikoner
self.image_list = wx.ImageList(16, 16)
self.idx_site = self.image_list.Add(wx.ArtProvider.GetBitmap(wx.ART_GO_HOME, wx.ART_CMN_DIALOG, (16, 16))) # Site
self.idx_drive = self.image_list.Add(wx.ArtProvider.GetBitmap(wx.ART_HARDDISK, wx.ART_CMN_DIALOG, (16, 16))) # Drive
self.idx_folder = self.image_list.Add(wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_CMN_DIALOG, (16, 16))) # Folder
self.idx_file = self.image_list.Add(wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_CMN_DIALOG, (16, 16))) # File
self.idx_star = self.image_list.Add(wx.ArtProvider.GetBitmap(wx.ART_ADD_BOOKMARK, wx.ART_CMN_DIALOG, (16, 16))) # Favorit stjerne
def add_icon(art_id, client=wx.ART_CMN_DIALOG):
bmp = wx.ArtProvider.GetBitmap(art_id, client, (16, 16))
if not bmp.IsOk():
bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, client, (16, 16)) # Fallback
return self.image_list.Add(bmp)
self.idx_site = add_icon(wx.ART_GO_HOME)
self.idx_drive = add_icon(wx.ART_HARDDISK)
self.idx_folder = add_icon(wx.ART_FOLDER)
self.idx_file = add_icon(wx.ART_NORMAL_FILE)
self.idx_star = add_icon(wx.ART_ADD_BOOKMARK)
self.idx_up = add_icon(wx.ART_GO_UP, wx.ART_TOOLBAR)
self.idx_down = add_icon(wx.ART_GO_DOWN, wx.ART_TOOLBAR)
# Threading/Sync til filredigering
@@ -638,6 +654,7 @@ class SharePointApp(wx.Frame):
self.list_ctrl.InsertColumn(3, self.get_txt("col_modified"), width=180)
self.list_ctrl.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_item_activated)
self.list_ctrl.Bind(wx.EVT_LIST_COL_CLICK, self.on_column_click)
self.list_ctrl.Bind(wx.EVT_CONTEXT_MENU, self.on_right_click)
# AKTIVER DRAG & DROP
@@ -1483,25 +1500,73 @@ class SharePointApp(wx.Frame):
wx.CallAfter(self._populate_list_ctrl, items_data, data)
def on_column_click(self, event):
col = event.GetColumn()
if col == self.sort_col:
self.sort_asc = not self.sort_asc
else:
self.sort_col = col
self.sort_asc = True
self.apply_sorting()
def apply_sorting(self):
if not self.current_items: return
# Priority: SITE < DRIVE < FOLDER < FILE
type_prio = {"SITE": 0, "DRIVE": 1, "FOLDER": 2, "FILE": 3}
def sort_logic(item):
# Altid grupper efter type først (mapper øverst)
p = type_prio.get(item['type'], 9)
val = ""
if self.sort_col == 0: # Name
val = natural_sort_key(item['name'])
elif self.sort_col == 1: # Type
val = item['type']
elif self.sort_col == 2: # Size
val = item['size'] if item['size'] is not None else -1
elif self.sort_col == 3: # Modified
val = item['modified']
return (p, val)
self.current_items.sort(key=sort_logic, reverse=not self.sort_asc)
self._update_list_view_only()
def _update_list_view_only(self):
self.list_ctrl.DeleteAllItems()
for i, item in enumerate(self.current_items):
img_idx = self.idx_file
if item['type'] == "FOLDER": img_idx = self.idx_folder
elif item['type'] == "DRIVE": img_idx = self.idx_drive
elif item['type'] == "SITE": img_idx = self.idx_site
self.list_ctrl.InsertItem(i, item['name'], img_idx)
type_str = self.get_txt("type_folder") if item['type'] == "FOLDER" else self.get_txt("type_file") if item['type'] == "FILE" else self.get_txt("type_drive")
self.list_ctrl.SetItem(i, 1, type_str)
size_str = format_size(item['size']) if item['size'] is not None else ""
self.list_ctrl.SetItem(i, 2, size_str)
self.list_ctrl.SetItem(i, 3, item['modified'])
# Opdater kolonne ikoner
for col in range(4):
info = self.list_ctrl.GetColumn(col)
if col == self.sort_col:
info.SetImage(self.idx_up if self.sort_asc else self.idx_down)
else:
info.SetImage(-1)
self.list_ctrl.SetColumn(col, info)
def _populate_list_ctrl(self, items_data, parent_data):
if not self: return
try:
self.list_ctrl.DeleteAllItems()
self.current_items = []
for i, item in enumerate(items_data):
img_idx = self.idx_file
if item['type'] == "FOLDER": img_idx = self.idx_folder
elif item['type'] == "DRIVE": img_idx = self.idx_drive
elif item['type'] == "SITE": img_idx = self.idx_site
self.list_ctrl.InsertItem(i, item['name'], img_idx)
type_str = self.get_txt("type_folder") if item['type'] == "FOLDER" else self.get_txt("type_file") if item['type'] == "FILE" else self.get_txt("type_drive")
self.list_ctrl.SetItem(i, 1, type_str)
size_str = format_size(item['size']) if item['size'] is not None else ""
self.list_ctrl.SetItem(i, 2, size_str)
self.list_ctrl.SetItem(i, 3, item['modified'])
self.current_items.append(item)
self.current_items = items_data
# Anvend sortering før visning
self.apply_sorting()
self.set_status(self.get_txt("status_ready"))
if parent_data['type'] == "SITE":