Initial Commit
This commit is contained in:
+307
@@ -0,0 +1,307 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import tkinter as tk
|
||||
from tkinter import scrolledtext
|
||||
import tkinter.font as tkfont
|
||||
import shutil
|
||||
import configparser
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CONFIG:
|
||||
config = configparser.ConfigParser()
|
||||
config.read(r"C:\ProgramData\Program-Search\config.ini")
|
||||
SEARCH_ROOT = config["Paths"]["SEARCH_ROOT"]
|
||||
TRASH_DIR = config["Paths"]["TRASH_DIR"]
|
||||
EDITOR_ENABLED = config.getboolean("Features", "EDITOR_ENABLED")
|
||||
AUTO_CLOSE_SECONDS = config.getint("Features", "AUTO_CLOSE_SECONDS")
|
||||
CLOSE_ALL_ENABLED = config.getboolean("Features", "CLOSE_ALL_ENABLED")
|
||||
# ============================================================
|
||||
|
||||
os.makedirs(TRASH_DIR, exist_ok=True)
|
||||
|
||||
|
||||
def custom_messagebox(title, message, kind="info"):
|
||||
win = tk.Toplevel()
|
||||
win.title(title)
|
||||
win.geometry("600x300")
|
||||
win.grab_set()
|
||||
win.focus_force()
|
||||
center_window(win)
|
||||
|
||||
frame = tk.Frame(win, padx=20, pady=20)
|
||||
frame.pack(expand=True, fill="both")
|
||||
|
||||
tk.Label(frame, text=message, font=("Consolas", 24), wraplength=550).pack(pady=20)
|
||||
|
||||
if kind == "yesno":
|
||||
result = {"value": False}
|
||||
|
||||
def yes():
|
||||
result["value"] = True
|
||||
win.destroy()
|
||||
|
||||
def no():
|
||||
result["value"] = False
|
||||
win.destroy()
|
||||
|
||||
tk.Button(frame, text="Yes", font=("Consolas", 24), padx=10, pady=10, command=yes).pack(side="left", padx=16)
|
||||
tk.Button(frame, text="No", font=("Consolas", 24), padx=10, pady=10, command=no).pack(side="right", padx=16)
|
||||
|
||||
win.wait_window()
|
||||
return result["value"]
|
||||
|
||||
else:
|
||||
def close():
|
||||
win.destroy()
|
||||
|
||||
tk.Button(frame, text="OK", font=("Consolas", 24), padx=10, pady=10, command=close).pack(pady=20)
|
||||
win.bind("<Return>", lambda event: close())
|
||||
win.wait_window()
|
||||
|
||||
|
||||
def center_window(win):
|
||||
win.update_idletasks()
|
||||
w = win.winfo_width()
|
||||
h = win.winfo_height()
|
||||
sw = win.winfo_screenwidth()
|
||||
sh = win.winfo_screenheight()
|
||||
x = (sw // 2) - (w // 2)
|
||||
y = (sh // 2) - (h // 2)
|
||||
win.geometry(f"{w}x{h}+{x}+{y}")
|
||||
|
||||
|
||||
def popup_search_input():
|
||||
win = tk.Toplevel()
|
||||
win.title("Search Trash")
|
||||
win.geometry("600x300")
|
||||
center_window(win)
|
||||
|
||||
tk.Label(win, text="Enter part number or name:", font=("Consolas", 18)).pack(pady=20)
|
||||
|
||||
entry = tk.Entry(win, font=("Consolas", 18))
|
||||
entry.pack(pady=10)
|
||||
entry.focus_set()
|
||||
|
||||
result = {"value": None}
|
||||
|
||||
def submit():
|
||||
result["value"] = entry.get().strip().lower()
|
||||
win.destroy()
|
||||
|
||||
entry.bind("<Return>", lambda event: submit())
|
||||
win.bind("<Return>", lambda event: submit())
|
||||
|
||||
tk.Button(win, text="Search", font=("Consolas", 18), padx=20, pady=10, command=submit).pack(pady=40)
|
||||
|
||||
win.wait_window()
|
||||
return result["value"]
|
||||
|
||||
|
||||
def search_files(term):
|
||||
matches = []
|
||||
|
||||
for root, dirs, files in os.walk(TRASH_DIR):
|
||||
for fname in files:
|
||||
path = os.path.join(root, fname)
|
||||
fname_lower = fname.lower()
|
||||
|
||||
filename_match = term in fname_lower
|
||||
contents_match = False
|
||||
|
||||
try:
|
||||
with open(path, "r", errors="ignore") as f:
|
||||
if term in f.read().lower():
|
||||
contents_match = True
|
||||
except:
|
||||
pass
|
||||
|
||||
if filename_match or contents_match:
|
||||
matches.append(path)
|
||||
|
||||
return matches
|
||||
|
||||
|
||||
def popup_results_list(matches):
|
||||
win = tk.Toplevel()
|
||||
win.title("Matches Found")
|
||||
win.geometry("1200x1000")
|
||||
center_window(win)
|
||||
|
||||
tk.Label(win, text="Select files to preview:", font=("Consolas", 18)).pack(pady=20)
|
||||
|
||||
lb = tk.Listbox(win, selectmode=tk.MULTIPLE, font=("Consolas", 18))
|
||||
lb.pack(expand=True, fill="both")
|
||||
|
||||
for m in matches:
|
||||
lb.insert(tk.END, m)
|
||||
|
||||
result = {"selected": []}
|
||||
|
||||
def select_all():
|
||||
lb.select_set(0, tk.END)
|
||||
|
||||
def submit():
|
||||
sel = lb.curselection()
|
||||
result["selected"] = [matches[i] for i in sel]
|
||||
win.destroy()
|
||||
|
||||
lb.bind("<Return>", lambda event: submit())
|
||||
win.bind("<Return>", lambda event: submit())
|
||||
|
||||
tk.Button(win, text="Open Selected", font=("Consolas", 18), padx=20, pady=10, command=submit).pack(side="right", padx=20, pady=20)
|
||||
tk.Button(win, text="Select All", font=("Consolas", 18), padx=20, pady=10, command=select_all).pack(side="left", padx=20, pady=20)
|
||||
|
||||
win.wait_window()
|
||||
return result["selected"]
|
||||
|
||||
|
||||
def popup_file_preview(path, on_close_callback):
|
||||
win = tk.Toplevel()
|
||||
win.title(path)
|
||||
win.geometry("900x1000")
|
||||
center_window(win)
|
||||
|
||||
box = scrolledtext.ScrolledText(win, wrap=tk.WORD, font=("Consolas", 18))
|
||||
box.pack(expand=True, fill="both")
|
||||
|
||||
try:
|
||||
with open(path, "r", errors="ignore") as f:
|
||||
original_text = f.read()
|
||||
box.insert(tk.END, original_text)
|
||||
box.edit_modified(False)
|
||||
except Exception as e:
|
||||
box.insert(tk.END, f"Error reading file:\n{e}")
|
||||
|
||||
modified = {"changed": False}
|
||||
|
||||
def mark_modified(event=None):
|
||||
if box.edit_modified():
|
||||
modified["changed"] = True
|
||||
box.edit_modified(False)
|
||||
|
||||
if EDITOR_ENABLED:
|
||||
box.bind("<<Modified>>", mark_modified)
|
||||
else:
|
||||
box.configure(state="disabled")
|
||||
|
||||
frame = tk.Frame(win)
|
||||
frame.pack(fill="x", pady=10)
|
||||
|
||||
def save_file():
|
||||
try:
|
||||
with open(path, "w", errors="ignore") as f:
|
||||
f.write(box.get("1.0", tk.END))
|
||||
modified["changed"] = False
|
||||
custom_messagebox("Saved", f"File saved:\n{path}")
|
||||
except Exception as e:
|
||||
custom_messagebox("Save Error", str(e))
|
||||
|
||||
if EDITOR_ENABLED:
|
||||
tk.Button(frame, text="Save", fg="green", font=("Consolas", 18), padx=15, pady=8, command=save_file).pack(side="left", padx=10, pady=10)
|
||||
|
||||
def restore_file():
|
||||
if EDITOR_ENABLED and modified["changed"]:
|
||||
ask = custom_messagebox("Unsaved Changes", "You have unsaved changes.\nSave before restoring?", kind="yesno")
|
||||
if ask:
|
||||
save_file()
|
||||
|
||||
try:
|
||||
rel_path = os.path.relpath(path, TRASH_DIR)
|
||||
dest_path = os.path.join(SEARCH_ROOT, rel_path)
|
||||
|
||||
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
||||
shutil.move(path, dest_path)
|
||||
|
||||
custom_messagebox("Restored", f"File restored to:\n{dest_path}")
|
||||
win.destroy()
|
||||
on_close_callback()
|
||||
|
||||
except Exception as e:
|
||||
custom_messagebox("Restore Error", str(e))
|
||||
|
||||
tk.Button(frame, text="Restore File", fg="blue", font=("Consolas", 18), padx=15, pady=8, command=restore_file).pack(side="left", padx=10, pady=10)
|
||||
|
||||
def close():
|
||||
if EDITOR_ENABLED and modified["changed"]:
|
||||
ask = custom_messagebox("Unsaved Changes", "You have unsaved changes.\nSave before closing?", kind="yesno")
|
||||
if ask:
|
||||
save_file()
|
||||
|
||||
win.destroy()
|
||||
on_close_callback()
|
||||
|
||||
tk.Button(frame, text="Close", font=("Consolas", 18), padx=15, pady=8, command=close).pack(side="right", padx=10, pady=10)
|
||||
|
||||
def close_all_button():
|
||||
ask = custom_messagebox("WARNING!", "Are you sure you want to close all windows?", kind="yesno")
|
||||
if ask:
|
||||
close_all(win.master)
|
||||
|
||||
if CLOSE_ALL_ENABLED:
|
||||
tk.Button(frame, text="Close All", font=("Consolas", 18), padx=15, pady=8, command=close_all_button).pack(side="right", padx=10, pady=10)
|
||||
|
||||
def auto_close():
|
||||
if win.winfo_exists():
|
||||
win.destroy()
|
||||
on_close_callback()
|
||||
|
||||
win.protocol("WM_DELETE_WINDOW", close)
|
||||
win.after(AUTO_CLOSE_SECONDS * 1000, auto_close)
|
||||
|
||||
|
||||
def close_all(root):
|
||||
for w in root.winfo_children():
|
||||
try:
|
||||
w.destroy()
|
||||
except:
|
||||
pass
|
||||
root.destroy()
|
||||
|
||||
|
||||
def main():
|
||||
root = tk.Tk()
|
||||
root.withdraw()
|
||||
|
||||
default_font = tkfont.nametofont("TkDefaultFont")
|
||||
default_font.configure(family="Consolas", size=18)
|
||||
|
||||
text_font = tkfont.nametofont("TkTextFont")
|
||||
text_font.configure(family="Consolas", size=18)
|
||||
|
||||
fixed_font = tkfont.nametofont("TkFixedFont")
|
||||
fixed_font.configure(family="Consolas", size=18)
|
||||
|
||||
while True:
|
||||
search_term = popup_search_input()
|
||||
if not search_term:
|
||||
root.destroy()
|
||||
return
|
||||
|
||||
matches = search_files(search_term)
|
||||
|
||||
if matches:
|
||||
break
|
||||
|
||||
custom_messagebox("No Results", "No matching files found.")
|
||||
|
||||
selected = popup_results_list(matches)
|
||||
if not selected:
|
||||
root.destroy()
|
||||
return
|
||||
|
||||
open_windows = {"count": len(selected)}
|
||||
|
||||
def window_closed():
|
||||
open_windows["count"] -= 1
|
||||
if open_windows["count"] == 0:
|
||||
root.destroy()
|
||||
|
||||
for path in selected:
|
||||
popup_file_preview(path, window_closed)
|
||||
|
||||
root.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user