Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """
- Najeeb ULTRA PRO MAX V3.8 - FINAL COMPLETE EDITION [FIXED]
- ✅ Python 3.7.9+ | Windows 7/10/11 32/64-bit | Pure StdLib | Zero Dependencies
- ✅ PERFECT: Line Numbers + Code Scroll Together 100%
- ✅ 175+ Extensions: Code + Audio + Video + Images + EXE
- ✅ NEW: 🔒 Password ZIP Extract (Thread-Safe) | 💻 CMD Command Field + Run Button
- ✅ Fixed: Tuple Unpacking | Syntax Error | Truncated Settings | Tkinter Thread Crash
- """
- import os
- import sys
- import json
- import shutil
- import tempfile
- import threading
- import subprocess
- import tarfile
- import zipfile
- import queue
- import re
- import time # ✅ Added for thread-safe dialog
- from pathlib import Path
- from datetime import datetime
- from tkinter import *
- from tkinter import ttk, filedialog, messagebox, simpledialog
- from tkinter import TclError
- # ------------------------------- SPLASH -----------------------
- def show_splash(image_path):
- """Display splash screen for 3 seconds"""
- try:
- splash = Tk()
- splash.overrideredirect(True)
- if os.path.exists(image_path):
- from PIL import Image, ImageTk
- image = Image.open(image_path)
- img = ImageTk.PhotoImage(image)
- img_width, img_height = image.size
- screen_width = splash.winfo_screenwidth()
- screen_height = splash.winfo_screenheight()
- x = (screen_width - img_width) // 2
- y = (screen_height - img_height) // 2
- splash.geometry(f"{img_width}x{img_height}+{x}+{y}")
- splash.config(bg="white")
- splash.attributes("-transparentcolor", "white")
- label = Label(splash, image=img, bg="white")
- label.image = img
- label.pack()
- splash.after(3000, splash.destroy)
- splash.mainloop()
- else:
- splash.destroy()
- except Exception:
- pass
- # Get paths
- base_path = os.path.dirname(os.path.abspath(__file__))
- splash_path = os.path.join(base_path, "THEME", "splash-1.png")
- if os.path.exists(splash_path):
- show_splash(splash_path)
- # ---------------- CONFIG ----------------
- APP_NAME = "Najeeb ULTRA PRO MAX V3.8"
- TEMP_DIR = os.path.join(tempfile.gettempdir(), "UltraProMax_V38")
- SETTINGS_FILE = Path.home() / ".ultrapro_settings_v38.json"
- APP_ICON = "SOFT.ico" if os.name == "nt" else "icon.xbm"
- RECENT_MAX = 10
- HIGHLIGHT_DELAY = 200
- CREATE_NO_WINDOW = 0x08000000 if os.name == "nt" else 0
- # ✅ EXT_LANG: Complete extension support (175+ Extensions)
- EXT_LANG = {
- ".py": "Python", ".pyw": "Python", ".pyi": "Python",
- ".js": "JavaScript", ".jsx": "JavaScript", ".ts": "TypeScript", ".tsx": "TypeScript",
- ".html": "HTML", ".htm": "HTML", ".css": "CSS", ".scss": "CSS", ".sass": "CSS", ".less": "CSS",
- ".vue": "Vue", ".svelte": "Svelte", ".hta": "HTML",
- ".json": "JSON", ".json5": "JSON", ".xml": "XML", ".yaml": "YAML", ".yml": "YAML",
- ".toml": "TOML", ".ini": "Config", ".cfg": "Config", ".conf": "Config", ".env": "Env",
- ".md": "Markdown", ".markdown": "Markdown", ".rst": "reST", ".txt": "Text", ".ls": "Log",
- ".sh": "Shell", ".bash": "Shell", ".zsh": "Shell", ".fish": "Shell",
- ".bat": "Batch", ".cmd": "Batch", ".ps1": "PowerShell", ".psm1": "PowerShell",
- ".c": "C", ".h": "C", ".cpp": "C++", ".hpp": "C++", ".cc": "C++", ".cxx": "C++",
- ".java": "Java", ".kt": "Kotlin", ".kts": "Kotlin", ".scala": "Scala",
- ".cs": "C#", ".vb": "VBScript", ".fs": "F#",
- ".rb": "Ruby", ".rake": "Ruby", ".php": "PHP", ".phtml": "PHP",
- ".sql": "SQL", ".pls": "SQL", ".r": "R", ".R": "R",
- ".rs": "Rust", ".go": "Go", ".dart": "Dart", ".swift": "Swift",
- ".lua": "Lua", ".pl": "Perl", ".pm": "Perl", ".tcl": "TCL",
- ".graphql": "GraphQL", ".gql": "GraphQL",
- ".log": "Log", ".csv": "CSV", ".tsv": "CSV", ".diff": "Diff", ".patch": "Diff",
- ".ahk": "AutoHotKey", ".au3": "AutoIt",
- ".m3u": "M3U", ".m3u8": "M3U",
- ".mp3": "Audio", ".wav": "Audio", ".flac": "Audio", ".aac": "Audio",
- ".ogg": "Audio", ".wma": "Audio", ".m4a": "Audio", ".alac": "Audio",
- ".opus": "Audio", ".aiff": "Audio", ".aif": "Audio", ".ape": "Audio",
- ".ac3": "Audio", ".amr": "Audio", ".au": "Audio", ".mid": "Audio",
- ".midi": "Audio", ".mka": "Audio", ".mp2": "Audio", ".ra": "Audio",
- ".ram": "Audio", ".snd": "Audio", ".wv": "Audio", ".dsd": "Audio",
- ".mp4": "Video", ".avi": "Video", ".mkv": "Video", ".mov": "Video",
- ".wmv": "Video", ".flv": "Video", ".webm": "Video", ".mpeg": "Video",
- ".mpg": "Video", ".m4v": "Video", ".3gp": "Video", ".3g2": "Video",
- ".asf": "Video", ".f4v": "Video", ".f4p": "Video", ".f4a": "Video",
- ".f4b": "Video", ".m2ts": "Video", ".m2v": "Video", ".m4p": "Video",
- ".mpe": "Video", ".mpv": "Video", ".mts": "Video", ".mxf": "Video",
- ".nsv": "Video", ".ogv": "Video", ".qt": "Video", ".rm": "Video",
- ".rmvb": "Video", ".roq": "Video", ".svi": "Video", ".vob": "Video",
- ".yuv": "Video", ".divx": "Video", ".xvid": "Video", ".avchd": "Video",
- ".gif": "Video", ".gifv": "Video", ".h264": "Video", ".hevc": "Video",
- ".jpg": "Image", ".jpeg": "Image", ".png": "Image", ".gif": "Image",
- ".bmp": "Image", ".svg": "Image", ".webp": "Image", ".ico": "Image",
- ".tiff": "Image", ".tif": "Image", ".psd": "Image", ".raw": "Image",
- ".cr2": "Image", ".nef": "Image", ".arw": "Image", ".dng": "Image",
- ".heic": "Image", ".heif": "Image", ".avif": "Image", ".jxr": "Image",
- ".pgm": "Image", ".ppm": "Image", ".pbm": "Image", ".pnm": "Image",
- ".hdr": "Image", ".exr": "Image", ".j2k": "Image", ".jp2": "Image",
- ".jpf": "Image", ".jpm": "Image", ".mj2": "Image", ".sgi": "Image",
- ".tga": "Image", ".icns": "Image", ".eps": "Image", ".ai": "Image",
- ".exe": "Executable", ".msi": "Installer", ".msu": "Update",
- ".com": "Command", ".scr": "ScreenSaver",
- }
- # ✅ Helper: Find interpreter with auto-detection
- def find_interpreter(names, common_paths):
- for name in names:
- try:
- if shutil.which(name):
- return name
- except Exception:
- pass
- if os.name == "nt":
- for path in common_paths:
- path = os.path.expandvars(path)
- if os.path.isfile(path):
- return path
- if not path.lower().endswith(".exe") and os.path.isfile(path + ".exe"):
- return path + ".exe"
- return names[0] if names else None
- # ✅ Build default interpreters dict with auto-detection
- def get_default_interpreters():
- interpreters = {
- "python": "python", "pythonw": "pythonw", "node": "node", "ts-node": "ts-node",
- "bash": "bash", "zsh": "zsh", "fish": "fish", "ruby": "ruby", "php": "php", "perl": "perl",
- "lua": "lua", "rscript": "Rscript", "tclsh": "tclsh", "cargo": "cargo", "go": "go",
- "dart": "dart", "swift": "swift", "java": "java", "kotlin": "kotlin", "scala": "scala",
- "dotnet": "dotnet", "sqlite3": "sqlite3", "sqlplus": "sqlplus",
- }
- if os.name == "nt":
- ahk_paths = [r"C:\Program Files\AutoHotkey\AutoHotkey.exe", r"C:\Program Files (x86)\AutoHotkey\AutoHotkey.exe", r"%ProgramFiles%\AutoHotkey\AutoHotkey.exe", r"%ProgramFiles(x86)%\AutoHotkey\AutoHotkey.exe"]
- interpreters["autohotkey"] = find_interpreter(["autohotkey", "AutoHotkey.exe"], ahk_paths)
- au3_paths = [r"C:\Program Files\AutoIt3\AutoIt3.exe", r"C:\Program Files (x86)\AutoIt3\AutoIt3.exe", r"%ProgramFiles%\AutoIt3\AutoIt3.exe", r"%ProgramFiles(x86)%\AutoIt3\AutoIt3.exe"]
- interpreters["autoit3"] = find_interpreter(["autoit3", "AutoIt3.exe"], au3_paths)
- interpreters["powershell"] = "powershell"
- interpreters["cmd"] = "cmd"
- interpreters["mshta"] = "mshta"
- return interpreters
- # ✅ RUNNERS: Complete language executors - FIXED VERSION
- def build_runners():
- runners = {}
- runners.update({
- ".py": ["python"], ".pyw": ["pythonw"], ".pyi": ["python"],
- ".js": ["node"], ".jsx": ["node"], ".ts": ["ts-node"], ".tsx": ["ts-node"],
- ".sh": ["bash"], ".bash": ["bash"], ".zsh": ["zsh"], ".fish": ["fish"],
- ".bat": ["cmd", "/c"], ".cmd": ["cmd", "/c"],
- ".ps1": ["powershell", "-ExecutionPolicy", "Bypass", "-File"],
- ".psm1": ["powershell", "-ExecutionPolicy", "Bypass", "-File"],
- ".hta": ["mshta"] if os.name == "nt" else ["echo", "HTA requires Windows"],
- ".rb": ["ruby"], ".rake": ["ruby"], ".php": ["php"], ".phtml": ["php"],
- ".pl": ["perl"], ".pm": ["perl"], ".lua": ["lua"],
- ".r": ["Rscript"], ".R": ["Rscript"], ".tcl": ["tclsh"],
- ".rs": ["cargo", "run"], ".go": ["go", "run"], ".dart": ["dart"], ".swift": ["swift"],
- ".java": ["java"], ".kt": ["kotlin"], ".kts": ["kotlin", "-script"], ".scala": ["scala"],
- ".cs": ["dotnet", "run"], ".vb": ["dotnet", "run"], ".fs": ["dotnet", "run"],
- ".sql": ["sqlite3"], ".pls": ["sqlplus"],
- ".ahk": ["autohotkey"], ".au3": ["autoit3"],
- })
- if os.name == "nt":
- interpreters = get_default_interpreters()
- if interpreters.get("autohotkey"):
- runners[".ahk"] = [interpreters["autohotkey"]]
- if interpreters.get("autoit3"):
- runners[".au3"] = [interpreters["autoit3"]]
- runners[".exe"] = ["start"]
- runners[".msi"] = ["msiexec", "/i"]
- runners[".msu"] = ["wusa"]
- runners[".com"] = ["start"]
- runners[".scr"] = ["start"]
- else:
- runners[".exe"] = ["wine"] if shutil.which("wine") else ["echo", "Requires Wine"]
- runners[".msi"] = ["echo", "MSI requires Windows"]
- runners[".msu"] = ["echo", "MSU requires Windows"]
- runners[".com"] = ["echo", "COM requires Windows"]
- runners[".scr"] = ["echo", "SCR requires Windows"]
- open_cmd = ["start"] if os.name == "nt" else (["open"] if sys.platform == "darwin" else ["xdg-open"])
- for ext in [".html", ".htm", ".vue", ".svelte", ".json", ".json5", ".xml", ".yaml", ".yml", ".toml",
- ".ini", ".cfg", ".conf", ".env", ".md", ".markdown", ".rst", ".txt", ".log",
- ".csv", ".tsv", ".diff", ".patch", ".graphql", ".gql", ".ls"]:
- runners[ext] = open_cmd
- for ext in [".mp3", ".wav", ".flac", ".aac", ".ogg", ".wma", ".m4a", ".alac", ".opus", ".aiff", ".aif", ".ape",
- ".ac3", ".amr", ".au", ".mid", ".midi", ".mka", ".mp2", ".ra", ".ram", ".snd", ".wv", ".dsd",
- ".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv", ".webm", ".mpeg", ".mpg", ".m4v", ".3gp", ".3g2",
- ".asf", ".f4v", ".f4p", ".f4a", ".f4b", ".m2ts", ".m2v", ".m4p", ".mpe", ".mpv", ".mts", ".mxf",
- ".nsv", ".ogv", ".qt", ".rm", ".rmvb", ".roq", ".svi", ".vob", ".yuv", ".divx", ".xvid", ".avchd",
- ".gif", ".gifv", ".h264", ".hevc", ".jpg", ".jpeg", ".png", ".bmp", ".svg", ".webp", ".ico",
- ".tiff", ".tif", ".psd", ".raw", ".cr2", ".nef", ".arw", ".dng", ".heic", ".heif", ".avif", ".jxr",
- ".pgm", ".ppm", ".pbm", ".pnm", ".hdr", ".exr", ".j2k", ".jp2", ".jpf", ".jpm", ".mj2", ".sgi",
- ".tga", ".icns", ".eps", ".ai", ".m3u", ".m3u8"]:
- runners[ext] = open_cmd
- for ext in [".css", ".scss", ".sass", ".less"]:
- runners[ext] = ["echo", f"{ext.upper()} files need to be linked/compiled first."]
- runners[".h"] = ["echo", "Header file - not directly executable"]
- for ext in [".c", ".cpp", ".cc", ".cxx"]:
- runners[ext] = ["echo", "C/C++ files need compilation first (gcc/clang)"]
- for ext in [".java", ".kt", ".scala"]:
- runners[ext] = ["echo", "Java/Kotlin/Scala need compilation first"]
- return runners
- RUNNERS = build_runners()
- # ✅ SYNTAX_PATTERNS (Code highlighting for main languages)
- SYNTAX_PATTERNS = {
- "Python": [
- (r'\b(def|class|import|from|if|else|elif|for|while|try|except|finally|return|yield|with|as|and|or|not|in|is|lambda|pass|break|continue|raise|assert|del|global|nonlocal|True|False|None|async|await|match|case|type)\b', "keyword"),
- (r'(#.*)$', "comment"),
- (r'("""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|"(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')', "string"),
- (r'\b(\d+\.?\d*(?:[eE][+-]?\d+)?)\b', "number"),
- (r'(@\w+)', "decorator"), (r'\b(self|cls)\b', "special"),
- ],
- "JavaScript": [
- (r'\b(const|let|var|function|return|if|else|for|while|do|switch|case|break|continue|class|extends|new|this|super|import|export|default|async|await|try|catch|finally|throw|typeof|instanceof|in|of|delete|void|yield|true|false|null|undefined)\b', "keyword"),
- (r'(//.*$|/\*[\s\S]*?\*/)', "comment"),
- (r'(`[\s\S]*?`|"(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')', "string"),
- (r'\b(\d+\.?\d*(?:[eE][+-]?\d+)?)\b', "number"),
- ],
- "HTML": [
- (r'(</?[a-zA-Z][a-zA-Z0-9\-]*\s*[^>]*?>)', "tag"),
- (r'\b([a-zA-Z\-:]+)\s*=', "attribute"),
- (r'("(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')', "string"),
- (r'(<!--[\s\S]*?-->)', "comment"),
- ],
- "CSS": [(r'([a-zA-Z\-]+)\s*:', "property"), (r'#[0-9a-fA-F]{3,8}\b', "color"), (r'("(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')', "string"), (r'(/\*[\s\S]*?\*/)', "comment")],
- "JSON": [(r'("(?:[^"\\]|\\.)*")\s*:', "key"), (r'("(?:[^"\\]|\\.)*")', "string"), (r'\b(true|false|null)\b', "keyword"), (r'\b(-?\d+\.?\d*(?:[eE][+-]?\d+)?)\b', "number")],
- "Shell": [(r'\b(if|then|else|elif|fi|case|esac|for|while|until|do|done|in|function|break|continue|return|exit|export)\b', "keyword"), (r'(#.*$)', "comment"), (r'("(?:[^"\\]|\\.)*"|\'[^\']*\'|`[^`]*`)', "string"), (r'(\$[a-zA-Z_][a-zA-Z0-9]*|\$\{[^}]*\})', "variable")],
- "Batch": [(r'\b(@echo|echo|call|goto|if|else|for|in|do|set|setlocal|endlocal|exit|rem|::|pause)\b', "keyword"), (r'(rem\s.*$|::.*$)', "comment"), (r'(%[^%]+%|![^!]+!)', "variable")],
- "PowerShell": [(r'\b(function|if|else|elseif|for|foreach|while|switch|break|continue|return|exit|throw|try|catch|finally)\b', "keyword"), (r'(#.*$|<#[\s\S]*?#>)', "comment"), (r'(\$(?:[a-zA-Z_][a-zA-Z0-9]*|\{[^}]*\}))', "variable")],
- }
- class SafeSettings:
- def __init__(self):
- self.data = self._load()
- def _load(self):
- defaults = {"theme": "dark", "font_size": 11, "font_family": "Consolas", "auto_save": True, "auto_save_interval": 30, "word_wrap": False, "recent": [], "interpreters": get_default_interpreters()}
- if SETTINGS_FILE.exists():
- try:
- with open(SETTINGS_FILE, "r", encoding="utf-8") as f:
- loaded = json.load(f)
- if "interpreters" in loaded:
- defaults["interpreters"].update(loaded["interpreters"])
- del loaded["interpreters"]
- return {**defaults, **loaded}
- except Exception:
- pass
- return defaults
- def save(self):
- try:
- SETTINGS_FILE.parent.mkdir(parents=True, exist_ok=True)
- with open(SETTINGS_FILE, "w", encoding="utf-8") as f:
- json.dump(self.data, f, indent=2, ensure_ascii=False)
- except Exception:
- pass
- def get(self, k, d=None):
- return self.data.get(k, d)
- def set(self, k, v):
- self.data[k] = v
- self.save()
- class LineNumberedText:
- def __init__(self, master, font_name, font_size, theme="dark"):
- self.master = master
- self.font_name = font_name
- self.font_size = font_size
- self.theme = theme
- if theme == "dark":
- self.bg, self.fg, self.line_bg, self.line_fg, self.cursor, self.highlight_bg = "#1e1e1e", "#d4d4d4", "#2d2d2d", "#888888", "#ffffff", "#333344"
- else:
- self.bg, self.fg, self.line_bg, self.line_fg, self.cursor, self.highlight_bg = "#ffffff", "#000000", "#f0f0f0", "#666666", "#000000", "#e0e0e0"
- self.frame = Frame(master, bg=self.bg)
- self.line_canvas = Canvas(self.frame, bg=self.line_bg, width=50, highlightthickness=0)
- self.line_canvas.pack(side=LEFT, fill=Y)
- self.code = Text(self.frame, bg=self.bg, fg=self.fg, insertbackground=self.cursor, font=(font_name, font_size), wrap="none", undo=True, autoseparators=True, maxundo=-1, borderwidth=0, highlightthickness=0, padx=10, pady=5, yscrollcommand=self._on_yscroll)
- self.code.pack(side=LEFT, fill=BOTH, expand=True)
- self.vscroll = ttk.Scrollbar(master, orient=VERTICAL)
- self.vscroll.pack(side=RIGHT, fill=Y)
- self.vscroll.config(command=self._shared_scroll)
- self.code.config(yscrollcommand=self.vscroll.set)
- for ev, act in [("<MouseWheel>", 1), ("<Button-4>", -1), ("<Button-5>", 1), ("<KeyRelease>", 0), ("<ButtonRelease-1>", 0)]:
- self.code.bind(ev, self._on_change if act == 0 else self._on_mousewheel, add="+")
- self.code.bind("<<Modified>>", self._on_modified, add="+")
- self.code.tag_config("current_line", background=self.highlight_bg)
- self.line_canvas.bind("<Button-1>", lambda e: "break")
- self._draw_line_numbers()
- self._highlight_current_line()
- def _shared_scroll(self, *args):
- self.code.yview(*args)
- self.line_canvas.yview(*args)
- self._draw_line_numbers()
- def _on_yscroll(self, first, last):
- self.vscroll.set(first, last)
- def _on_mousewheel(self, event):
- delta = -1 if (event.num == 5 or event.delta == -120) else 1
- self._shared_scroll("scroll", -delta, "units")
- return "break"
- def _on_change(self, event=None):
- self._draw_line_numbers()
- self._highlight_current_line()
- def _on_modified(self, event=None):
- if self.code.edit_modified():
- self._draw_line_numbers()
- self.code.edit_modified(False)
- def _draw_line_numbers(self):
- try:
- self.line_canvas.delete("all")
- first_visible = int(float(self.code.index("@0,0").split(".")[0]))
- last_visible = int(float(self.code.index(f"@0,{self.code.winfo_height()}").split(".")[0])) + 1
- total_lines = int(self.code.index("end-1c").split(".")[0])
- width_needed = max(len(str(total_lines)) * 8 + 20, 50)
- if width_needed != self.line_canvas.cget("width"):
- self.line_canvas.config(width=min(width_needed, 80))
- self.code.update_idletasks()
- line_info = self.code.dlineinfo("1.0")
- line_height = line_info[3] if line_info else self.font_size + 4
- y_offset = 5
- for ln in range(first_visible, min(last_visible, total_lines + 1)):
- y_pos = ((ln - 1) * line_height) - (self.code.yview()[0] * (total_lines * line_height)) + y_offset
- cur_line = int(self.code.index(INSERT).split(".")[0])
- color, style = ("#00ff00", "bold") if ln == cur_line else (self.line_fg, "normal")
- self.line_canvas.create_text(40, y_pos, text=str(ln), fill=color, font=(self.font_name, self.font_size, style), anchor="e")
- self.line_canvas.config(scrollregion=(0, 0, 50, total_lines * line_height + 10))
- except Exception:
- pass
- def _highlight_current_line(self, event=None):
- try:
- self.code.tag_remove("current_line", "1.0", END)
- ln = self.code.index(INSERT).split(".")[0]
- self.code.tag_add("current_line", f"{ln}.0", f"{ln}.end")
- self._draw_line_numbers()
- except Exception:
- pass
- def see(self, index):
- self.code.see(index)
- self.line_canvas.yview("moveto", self.code.yview()[0])
- self._draw_line_numbers()
- def get_frame(self):
- return self.frame
- def get_text(self):
- return self.code
- def insert(self, i, t):
- return self.code.insert(i, t)
- def get(self, s, e=None):
- return self.code.get(s, e)
- def index(self, i):
- return self.code.index(i)
- def tag_config(self, t, **k):
- return self.code.tag_config(t, **k)
- def tag_add(self, t, s, e=None):
- return self.code.tag_add(t, s, e)
- def tag_remove(self, t, s, e=None):
- return self.code.tag_remove(t, s, e)
- def tag_names(self, i=None):
- return self.code.tag_names(i)
- def tag_ranges(self, t):
- return self.code.tag_ranges(t)
- def search(self, p, s, **k):
- return self.code.search(p, s, **k)
- def compare(self, i1, o, i2):
- return self.code.compare(i1, o, i2)
- def delete(self, s, e=None):
- return self.code.delete(s, e)
- def mark_set(self, m, i):
- return self.code.mark_set(m, i)
- def mark_get(self, m):
- return self.code.mark_get(m)
- def focus_set(self):
- return self.code.focus_set()
- def bind(self, s, f, a=None):
- return self.code.bind(s, f, a)
- def config(self, **k):
- return self.code.config(**k)
- class UltraProMaxV3:
- def __init__(self, root):
- self.root = root
- self.root.title(APP_NAME)
- self.root.geometry("1250x750")
- self.root.minsize(800, 500)
- if os.name == "nt" and os.path.exists(APP_ICON):
- try:
- self.root.iconbitmap(APP_ICON)
- except Exception:
- pass
- if os.name == "nt":
- try:
- from ctypes import windll
- windll.shcore.SetProcessDpiAwareness(1)
- except Exception:
- pass
- self.settings = SafeSettings()
- self.tabs = {}
- self.tab_order = []
- self.current_file = None
- self.run_proc = None
- self.auto_timer = None
- self.hl_timer = None
- self.log_q = queue.Queue()
- self.recent = self.settings.get("recent", [])
- self.file_list = []
- self.search_var = None
- Path(TEMP_DIR).mkdir(parents=True, exist_ok=True)
- self._setup_ui()
- self._setup_bindings()
- self._start_auto_save()
- self.root.after(50, self._poll_console)
- self.root.protocol("WM_DELETE_WINDOW", self.on_close)
- def _setup_ui(self):
- menubar = Menu(self.root, tearoff=0)
- self.root.config(menu=menubar)
- file_menu = Menu(menubar, tearoff=0)
- for txt, cmd, acc in [("Open File", self.open_file, "Ctrl+O"), ("Open Archive", self.open_archive, "Ctrl+Shift+O"), ("New Script", self.new_script, "Ctrl+N"), ("Save", self.save, "Ctrl+S"), ("Save As...", self.save_as, "Ctrl+Shift+S"), ("Export to ZIP", self.export_to_zip, "Ctrl+E"), ("Exit", self.on_close, "Alt+F4")]:
- file_menu.add_command(label=txt, command=cmd, accelerator=acc)
- menubar.add_cascade(label="File", menu=file_menu)
- edit_menu = Menu(menubar, tearoff=0)
- for txt, cmd, acc in [("Find & Replace", self.find_replace, "Ctrl+F"), ("Global Search", self.global_search, "Ctrl+Shift+F"), ("Toggle Word Wrap", self.toggle_wrap, "Ctrl+Alt+W")]:
- edit_menu.add_command(label=txt, command=cmd, accelerator=acc)
- edit_menu.add_separator()
- for txt, key in [("✂️ Cut", "<<Cut>>"), ("Copy", "<<Copy>>"), ("Paste", "<<Paste>>")]:
- edit_menu.add_command(label=txt, command=lambda k=key: self.root.focus_get().event_generate(k), accelerator=f"Ctrl+{key[-2]}")
- menubar.add_cascade(label="Edit", menu=edit_menu)
- tab_menu = Menu(menubar, tearoff=0)
- for txt, cmd, acc in [("✏️ Rename Tab", self.rename_tab, "Ctrl+Shift+R"), ("❌ Close Tab", self.close_tab, "Ctrl+W"), ("Close Others", self.close_other_tabs, "Ctrl+Shift+W"), ("Close All", self.close_all_tabs, "Ctrl+Shift+Q")]:
- tab_menu.add_command(label=txt, command=cmd, accelerator=acc)
- menubar.add_cascade(label="Tab", menu=tab_menu)
- run_menu = Menu(menubar, tearoff=0)
- for txt, cmd, acc in [("▶ Run/Open", self.run_script, "Ctrl+R"), ("⏹ Stop Process", self.stop_run, "Ctrl+Shift+R"), ("❌ Clear Console", self.clear_console, "")]:
- run_menu.add_command(label=txt, command=cmd, accelerator=acc)
- menubar.add_cascade(label="Run", menu=run_menu)
- help_menu = Menu(menubar, tearoff=0)
- help_menu.add_command(label="⚙ Settings", command=self.show_settings)
- help_menu.add_command(label="ℹ About", command=self.about)
- menubar.add_cascade(label="Help", menu=help_menu)
- toolbar = Frame(self.root, bg="#2d2d2d", height=38)
- toolbar.pack(fill=X, side=TOP, padx=4, pady=2)
- for t, c in [("Open", self.open_file), ("New", self.new_script), ("Save", self.save), ("OpenZip", self.open_archive), ("ExpZip", self.export_to_zip), ("Rename", self.rename_tab), ("Close", self.close_tab), ("▶ Run", self.run_script), ("Stop", self.stop_run), ("Find", self.find_replace), ("⚙ Set", self.show_settings)]:
- ttk.Button(toolbar, text=t, command=c, width=8).pack(side=LEFT, padx=2)
- self.status = ttk.Label(self.root, text="Ready | UTF-8 | Ln 1, Col 1 | Wrap: OFF", background="#007acc", foreground="white", anchor=W, font=("Segoe UI", 9))
- self.status.pack(fill=X, side=BOTTOM)
- paned = ttk.PanedWindow(self.root, orient=HORIZONTAL)
- paned.pack(fill=BOTH, expand=True, padx=2, pady=2)
- left_panel = ttk.Frame(paned, width=220)
- paned.add(left_panel, weight=1)
- search_frame = ttk.Frame(left_panel)
- search_frame.pack(fill=X, padx=4, pady=(4, 2))
- ttk.Label(search_frame, text="🔍").pack(side=LEFT, padx=(0, 2))
- self.search_var = StringVar()
- self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=20)
- self.search_entry.pack(side=LEFT, fill=X, expand=True)
- self.search_entry.bind("<KeyRelease>", self._filter_file_list)
- ttk.Button(search_frame, text="✕", width=3, command=self._clear_search).pack(side=LEFT, padx=(2, 0))
- tree_frame = ttk.Frame(left_panel)
- tree_frame.pack(fill=BOTH, expand=True, padx=4, pady=(0, 4))
- tree_scroll = ttk.Scrollbar(tree_frame)
- tree_scroll.pack(side=RIGHT, fill=Y)
- self.tree = ttk.Treeview(tree_frame, show="tree", selectmode="browse", yscrollcommand=tree_scroll.set)
- self.tree.pack(fill=BOTH, expand=True)
- tree_scroll.config(command=self.tree.yview)
- self.tree.bind("<<TreeviewSelect>>", self._on_tree_select, add="+")
- right_panel = ttk.Frame(paned)
- paned.add(right_panel, weight=3)
- self.notebook = ttk.Notebook(right_panel)
- self.notebook.pack(fill=BOTH, expand=True)
- self.notebook.bind("<<NotebookTabChanged>>", self._on_tab_changed)
- self.tab_menu_popup = Menu(self.root, tearoff=0)
- self.tab_menu_popup.add_command(label="✏️ Rename", command=self.rename_tab)
- self.tab_menu_popup.add_command(label="❌ Close", command=self.close_tab)
- self.tab_menu_popup.add_command(label="🗑️ Close Others", command=self.close_other_tabs)
- self.tab_menu_popup.add_command(label="🧹 Close All", command=self.close_all_tabs)
- self.notebook.bind("<Button-3>", self._show_tab_context_menu)
- console_frame = ttk.Frame(right_panel)
- console_frame.pack(fill=X, padx=4, pady=(4, 0))
- console_scroll = ttk.Scrollbar(console_frame)
- console_scroll.pack(side=RIGHT, fill=Y)
- self.console = Text(console_frame, height=10, bg="#0c0c0c", fg="#00ff00", font=(self.settings.get("font_family", "Consolas"), self.settings.get("font_size", 11)), state=DISABLED, wrap="word", yscrollcommand=console_scroll.set)
- self.console.pack(fill=BOTH, expand=True)
- console_scroll.config(command=self.console.yview)
- # ✅ NEW: CMD Command Input Field + Run Button
- cmd_frame = Frame(right_panel, bg="#2d2d2d", height=32)
- cmd_frame.pack(fill=X, padx=4, pady=(2, 2))
- self.cmd_var = StringVar()
- self.cmd_entry = ttk.Entry(cmd_frame, textvariable=self.cmd_var, width=60, font=("Consolas", 10))
- self.cmd_entry.pack(side=LEFT, fill=X, expand=True, padx=(0, 5))
- self.cmd_entry.bind("<Return>", lambda e: self.run_custom_command())
- ttk.Button(cmd_frame, text="▶ Run Command", command=self.run_custom_command, width=14).pack(side=LEFT)
- ttk.Label(cmd_frame, text="💡 Type: dir / ls / python -V / ping 8.8.8.8 -n 2 | Press Enter", background="#2d2d2d", foreground="#888", font=("Segoe UI", 8)).pack(side=LEFT, padx=(8, 0))
- # ✅ NEW: Thread-Safe Password Prompt Helper
- def _ask_password_threadsafe(self, prompt_title, prompt_msg):
- """Ask for password on main thread - thread-safe wrapper"""
- result = {"password": None, "cancelled": False}
- def _show_dialog():
- if not self.root.winfo_exists():
- result["cancelled"] = True
- return
- pwd = simpledialog.askstring(prompt_title, prompt_msg, parent=self.root, show='*')
- if pwd is None:
- result["cancelled"] = True
- else:
- result["password"] = pwd.encode('utf-8') if pwd else b""
- # Schedule on main thread and wait
- self.root.after(0, _show_dialog)
- # Simple busy-wait (safe for short dialogs)
- while result["password"] is None and not result["cancelled"]:
- try:
- self.root.update_idletasks()
- time.sleep(0.05)
- except Exception:
- break
- return result
- def _show_tab_context_menu(self, event):
- try:
- tab_id = self.notebook.index(f"@{event.x},{event.y}")
- if tab_id is not None:
- self.notebook.select(tab_id)
- self._on_tab_changed(None)
- self.tab_menu_popup.post(event.x_root, event.y_root)
- except Exception:
- pass
- def _clear_search(self):
- self.search_var.set("")
- self._populate_file_tree()
- def _filter_file_list(self, event=None):
- term = self.search_var.get().lower()
- self.tree.delete(*self.tree.get_children())
- for fp in self.file_list:
- if not term or term in os.path.basename(fp).lower():
- self.tree.insert("", "end", iid=fp, text=f"{self._get_file_icon(Path(fp).suffix.lower())} {os.path.basename(fp)}")
- def _get_file_icon(self, ext):
- if ext in [".mp3", ".wav", ".flac", ".aac", ".ogg", ".wma", ".m4a", ".alac", ".opus"]:
- return "🎵"
- if ext in [".mp4", ".avi", ".mkv", ".mov", ".wmv", ".webm", ".mpeg", ".flv"]:
- return "🎬"
- if ext in [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".webp", ".ico", ".tiff"]:
- return "🖼️"
- if ext in [".exe", ".msi", ".msu", ".com", ".scr"]:
- return "🚀"
- if ext in [".py", ".js", ".html", ".css", ".java", ".cpp", ".c", ".php", ".rb"]:
- return "📄"
- return "📁"
- def _on_tree_select(self, event):
- try:
- sel = self.tree.selection()
- if sel and os.path.isfile(sel[0]):
- self._open_file_safe(sel[0])
- except Exception:
- pass
- def _on_tab_changed(self, event):
- self._update_current_file()
- self._update_status()
- def _setup_bindings(self):
- for key, cb in [("<Control-o>", self.open_file), ("<Control-Shift-O>", self.open_archive), ("<Control-n>", self.new_script), ("<Control-s>", self.save), ("<Control-Shift-S>", self.save_as), ("<Control-e>", self.export_to_zip), ("<Control-f>", self.find_replace), ("<Control-Shift-F>", self.global_search), ("<Control-r>", self.run_script), ("<Control-Shift-R>", self.stop_run), ("<Control-Alt-W>", self.toggle_wrap), ("<Control-q>", self.on_close), ("<Control-Shift-r>", self.rename_tab), ("<Control-w>", self.close_tab), ("<Control-Shift-w>", self.close_other_tabs), ("<Control-Shift-q>", self.close_all_tabs)]:
- self.root.bind(key, lambda e, callback=cb: callback(), add="+")
- def _start_auto_save(self):
- if self.settings.get("auto_save", True):
- self.auto_timer = self.root.after(self.settings.get("auto_save_interval", 30) * 1000, self._auto_save)
- def _auto_save(self):
- try:
- for fp, td in list(self.tabs.items()):
- if td.get("modified") and os.path.exists(fp):
- try:
- with open(fp, "w", encoding="utf-8") as f:
- f.write(td["text"].get("1.0", END))
- td["modified"] = False
- except IOError:
- self.log(f"⚠ Auto-save failed: {os.path.basename(fp)}")
- except RuntimeError:
- pass
- finally:
- if self.settings.get("auto_save", True):
- self.auto_timer = self.root.after(self.settings.get("auto_save_interval", 30) * 1000, self._auto_save)
- def _open_file_safe(self, fp):
- if not os.path.exists(fp):
- return
- if fp in self.tabs:
- self.notebook.select(self.tabs[fp]["frame"])
- else:
- self.open_tab(fp)
- def open_tab(self, file_path):
- try:
- ext = Path(file_path).suffix.lower()
- lang = EXT_LANG.get(ext, "Text")
- theme = self.settings.get("theme", "dark")
- font_fam, font_sz = self.settings.get("font_family", "Consolas"), self.settings.get("font_size", 11)
- if ext in [".mp3", ".wav", ".flac", ".mp4", ".avi", ".mkv", ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".exe", ".msi", ".msu", ".com", ".scr"]:
- self._open_with_default_app(file_path)
- return
- ln_text = LineNumberedText(self.notebook, font_fam, font_sz, theme)
- frame, text_widget = ln_text.get_frame(), ln_text.get_text()
- with open(file_path, "r", encoding="utf-8", errors="replace") as f:
- content = f.read()
- if len(content) > 500000:
- self.log(f"⚠️ Large file ({len(content)} chars). Opening with default app.")
- self._open_with_default_app(file_path)
- return
- text_widget.insert("1.0", content)
- tab_name = f" {Path(file_path).name} "
- tab_idx = self.notebook.add(frame, text=tab_name)
- self.notebook.select(frame)
- if file_path not in self.tab_order:
- self.tab_order.append(file_path)
- self.tabs[file_path] = {"frame": frame, "text": text_widget, "lang": lang, "modified": False, "encoding": "utf-8", "original_name": Path(file_path).name, "tab_index": tab_idx}
- self.current_file = file_path
- text_widget.bind("<KeyRelease>", lambda e, p=file_path: self._on_text_change(p), add="+")
- text_widget.bind("<ButtonRelease-1>", lambda e: self._update_status(), add="+")
- self._schedule_highlight(file_path)
- self._update_status()
- self._add_recent(file_path)
- except Exception as e:
- self.log(f"❌ Error opening {file_path}: {e}")
- def _open_with_default_app(self, file_path):
- try:
- if os.name == "nt":
- os.startfile(file_path)
- elif sys.platform == "darwin":
- subprocess.run(["open", file_path])
- else:
- subprocess.run(["xdg-open", file_path])
- self.log(f"▶ Opened: {Path(file_path).name}")
- except Exception as e:
- self.log(f"❌ Open failed: {e}")
- messagebox.showerror("Open Error", str(e))
- def _on_text_change(self, fp):
- if fp in self.tabs:
- self.tabs[fp]["modified"] = True
- name = Path(fp).name
- for i in range(self.notebook.index("end")):
- if self.notebook.tab(i, "text").strip() == name:
- self.notebook.tab(i, text=f" *{name} ")
- break
- self._schedule_highlight(fp)
- def _schedule_highlight(self, fp):
- if fp not in self.tabs:
- return
- if self.hl_timer:
- try:
- self.root.after_cancel(self.hl_timer)
- except Exception:
- pass
- self.hl_timer = self.root.after(HIGHLIGHT_DELAY, lambda p=fp: self._apply_syntax_highlighting(p))
- def _apply_syntax_highlighting(self, fp):
- if fp not in self.tabs:
- return
- td, tw, lang = self.tabs[fp], self.tabs[fp]["text"], self.tabs[fp]["lang"]
- if lang not in SYNTAX_PATTERNS:
- return
- try:
- for tag in tw.tag_names():
- if tag not in ("current_line", "sel", "find_highlight"):
- tw.tag_delete(tag)
- styles = {"keyword": {"foreground": "#569cd6"}, "string": {"foreground": "#ce9178"}, "comment": {"foreground": "#6a9955"}, "number": {"foreground": "#b5cea8"}, "decorator": {"foreground": "#dcdcaa"}, "special": {"foreground": "#9cdcfe"}, "class": {"foreground": "#4ec9b0"}, "tag": {"foreground": "#569cd6"}, "attribute": {"foreground": "#9cdcfe"}, "property": {"foreground": "#9cdcfe"}, "key": {"foreground": "#9cdcfe"}, "constant": {"foreground": "#4fc1ff"}, "variable": {"foreground": "#9cdcfe"}, "header": {"foreground": "#569cd6", "font": ("", "", "bold")}}
- for tag, style in styles.items():
- try:
- tw.tag_config(tag, **style)
- except Exception:
- pass
- content = tw.get("1.0", END)
- for pat_data in SYNTAX_PATTERNS[lang]:
- pattern, tag_name = pat_data[:2]
- flags = pat_data[2] if len(pat_data) == 3 else 0
- try:
- for m in re.finditer(pattern, content, flags):
- tw.tag_add(tag_name, f"1.0+{m.start()}c", f"1.0+{m.end()}c")
- except Exception:
- continue
- except Exception:
- pass
- def _update_current_file(self):
- try:
- cur = self.notebook.select()
- if cur:
- for p, d in self.tabs.items():
- if str(d["frame"]) == cur:
- self.current_file = p
- return
- self.current_file = None
- except Exception:
- self.current_file = None
- def _update_status(self):
- try:
- if not self.current_file or self.current_file not in self.tabs:
- self.status.config(text="Ready | No file open")
- return
- td, tw = self.tabs[self.current_file], self.tabs[self.current_file]["text"]
- line, col = map(int, tw.index(INSERT).split("."))
- col += 1
- name = Path(self.current_file).name
- lang = td.get("lang", "Text")
- mod = "*" if td.get("modified") else ""
- wrap = "ON" if self.settings.get("word_wrap") else "OFF"
- theme = self.settings.get("theme", "dark").upper()
- self.status.config(text=f"{mod}{name} | {lang} | UTF-8 | Ln {line}, Col {col} | Wrap: {wrap} | Theme: {theme}")
- except Exception:
- self.status.config(text="Ready")
- def _add_recent(self, fp):
- if fp in self.recent:
- self.recent.remove(fp)
- self.recent.insert(0, fp)
- self.recent = self.recent[:RECENT_MAX]
- self.settings.set("recent", self.recent)
- def open_file(self):
- fp = filedialog.askopenfilename(title="Open File", filetypes=[("All Files", "*.*"), ("Python", "*.py *.pyw"), ("Web", "*.js *.jsx *.ts *.tsx *.html *.htm *.css *.vue"), ("Data", "*.json *.xml *.yaml *.yml *.csv"), ("C/C++", "*.c *.h *.cpp *.hpp"), ("Shell", "*.sh *.bash *.bat *.ps1"), ("Other Code", "*.rb *.php *.sql *.rs *.go *.java"), ("Text", "*.txt *.md *.log"), ("🎵 Audio", "*.mp3 *.wav *.flac *.aac *.ogg *.wma *.m4a"), ("🎬 Video", "*.mp4 *.avi *.mkv *.mov *.wmv *.webm *.mpeg"), ("🖼️ Images", "*.jpg *.jpeg *.png *.gif *.bmp *.svg *.webp *.ico"), ("🚀 Executables", "*.exe *.msi *.msu *.com *.scr")])
- if fp:
- self.open_tab(fp)
- def save_as(self):
- if not self.current_file or self.current_file not in self.tabs:
- return
- ext = Path(self.current_file).suffix.lower()
- ft = [("All Files", "*.*")]
- if ext in EXT_LANG:
- ft.insert(0, (EXT_LANG[ext], f"*{ext}"))
- fp = filedialog.asksaveasfilename(title="Save As", initialfile=Path(self.current_file).name, defaultextension=ext if ext else ".txt", filetypes=ft)
- if fp:
- old = self.current_file
- self.current_file = fp
- self.save()
- if old in self.tabs:
- self.tabs[fp] = self.tabs.pop(old)
- if old in self.tab_order:
- self.tab_order[self.tab_order.index(old)] = fp
- self._add_recent(fp)
- def save(self):
- if not self.current_file or self.current_file not in self.tabs:
- return
- try:
- td = self.tabs[self.current_file]
- with open(self.current_file, "w", encoding="utf-8") as f:
- f.write(td["text"].get("1.0", END))
- td["modified"] = False
- name = Path(self.current_file).name
- for i in range(self.notebook.index("end")):
- if self.notebook.tab(i, "text").strip() == name:
- self.notebook.tab(i, text=f" {name} ")
- break
- self.log(f"💾 Saved: {name}")
- self._update_status()
- except IOError as e:
- self.log(f"❌ Save failed: {e}")
- messagebox.showerror("Save Error", f"Could not save:\n{e}")
- def rename_tab(self):
- """✅ FIXED: Complete if statement & proper tab data handling"""
- if not self.current_file or self.current_file not in self.tabs:
- messagebox.showinfo("Rename", "No file open")
- return
- td = self.tabs[self.current_file]
- cur_name = Path(self.current_file).name
- new_name = simpledialog.askstring("Rename Tab", f"New name for:\n{cur_name}", initialvalue=cur_name, parent=self.root)
- if not new_name or new_name == cur_name:
- return
- if Path(new_name).suffix.lower() not in EXT_LANG:
- messagebox.showwarning("Invalid", f"Extension '{Path(new_name).suffix}' not supported")
- return
- try:
- if os.path.exists(self.current_file):
- new_path = os.path.join(os.path.dirname(self.current_file), new_name)
- if os.path.exists(new_path) and new_path != self.current_file:
- if not messagebox.askyesno("Exists", f"'{new_name}' exists. Overwrite?"):
- return
- os.rename(self.current_file, new_path)
- self.current_file = new_path
- if self.current_file in self.tabs:
- old_data = self.tabs.pop(self.current_file, None)
- if old_data is not None:
- self.tabs[self.current_file] = old_data
- self.notebook.tab(td["frame"], text=f" {new_name} ")
- td["modified"] = False
- td["original_name"] = new_name
- self.log(f"✏️ Renamed: {cur_name} → {new_name}")
- self._update_status()
- except Exception as e:
- self.log(f"❌ Rename error: {e}")
- messagebox.showerror("Error", f"Could not rename:\n{e}")
- def close_tab(self, fp=None):
- if fp is None:
- fp = self.current_file
- if not fp or fp not in self.tabs:
- return
- td = self.tabs[fp]
- if td.get("modified"):
- r = messagebox.askyesnocancel("Unsaved", f"Save changes to '{Path(fp).name}'?", parent=self.root)
- if r is None:
- return
- elif r:
- self.current_file = fp
- self.save()
- try:
- self.notebook.forget(td["frame"])
- del self.tabs[fp]
- if fp in self.tab_order:
- self.tab_order.remove(fp)
- if self.current_file == fp:
- self._update_current_file()
- self.log(f"❌ Closed: {Path(fp).name}")
- self._update_status()
- except Exception:
- pass
- def close_other_tabs(self):
- if not self.current_file or self.current_file not in self.tabs:
- return
- cur = self.current_file
- to_close = [f for f in self.tabs if f != cur]
- if not to_close:
- return
- unsaved = [f for f in to_close if self.tabs[f].get("modified")]
- if unsaved and not messagebox.askyesno("Unsaved", f"{len(unsaved)} tab(s) unsaved. Close anyway?"):
- return
- for f in to_close:
- self.close_tab(f)
- self.log(f"🗑️ Closed {len(to_close)} other tab(s)")
- def close_all_tabs(self):
- if not self.tabs:
- return
- unsaved = [f for f in self.tabs if self.tabs[f].get("modified")]
- if unsaved and not messagebox.askyesno("Unsaved", f"{len(unsaved)} tab(s) unsaved. Close all?"):
- return
- for f in list(self.tabs.keys()):
- self.close_tab(f)
- self.log("🧹 Closed all tabs")
- def new_script(self):
- fn = simpledialog.askstring("New Script", "Filename with extension (e.g., script.py):", parent=self.root)
- if not fn:
- return
- ext = Path(fn).suffix.lower()
- if ext not in EXT_LANG:
- messagebox.showwarning("Invalid", f"Extension '{ext}' not supported")
- return
- fp = os.path.join(TEMP_DIR, fn)
- if os.path.exists(fp) and not messagebox.askyesno("Exists", f"'{fn}' exists. Overwrite?"):
- return
- try:
- with open(fp, "w", encoding="utf-8") as f:
- f.write(f"# {fn}\n# {EXT_LANG[ext]} File\n# Created: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
- self.log(f"📄 New: {fn}")
- if fp not in self.file_list:
- self.file_list.append(fp)
- self.tree.insert("", "end", iid=fp, text=f"{self._get_file_icon(ext)} {fn}")
- self.open_tab(fp)
- if fp in self.tabs:
- self.tabs[fp]["text"].focus_set()
- except IOError as e:
- self.log(f"❌ Create error: {e}")
- messagebox.showerror("Error", f"Could not create:\n{e}")
- def toggle_wrap(self):
- cur = self.settings.get("word_wrap", False)
- self.settings.set("word_wrap", not cur)
- wrap = "word" if not cur else "none"
- for td in self.tabs.values():
- td["text"].config(wrap=wrap)
- self._update_status()
- self.log(f"🔄 Word wrap: {'ON' if not cur else 'OFF'}")
- def find_replace(self):
- if not self.current_file or self.current_file not in self.tabs:
- messagebox.showinfo("Find", "No file open")
- return
- dlg = Toplevel(self.root)
- dlg.title("Find & Replace")
- dlg.geometry("400x180")
- dlg.resizable(False, False)
- dlg.transient(self.root)
- dlg.grab_set()
- ttk.Label(dlg, text="Find:").grid(row=0, column=0, padx=10, pady=8, sticky=W)
- fv = StringVar()
- fe = ttk.Entry(dlg, textvariable=fv, width=45)
- fe.grid(row=0, column=1, columnspan=2, padx=5, pady=8)
- fe.focus()
- ttk.Label(dlg, text="Replace:").grid(row=1, column=0, padx=10, pady=5, sticky=W)
- rv = StringVar()
- ttk.Entry(dlg, textvariable=rv, width=45).grid(row=1, column=1, columnspan=2, padx=5, pady=5)
- def find_next():
- tw = self.tabs[self.current_file]["text"]
- st = fv.get()
- if not st:
- return
- tw.tag_remove("find_highlight", "1.0", END)
- pos = tw.search(st, tw.index(INSERT), stopindex=END)
- if not pos:
- pos = tw.search(st, "1.0", stopindex=END)
- if pos:
- tw.tag_add("find_highlight", pos, f"{pos}+{len(st)}c")
- tw.tag_config("find_highlight", background="#444466", foreground="yellow")
- tw.see(pos)
- tw.mark_set(INSERT, pos)
- else:
- messagebox.showinfo("Find", "No matches")
- def replace_one():
- tw = self.tabs[self.current_file]["text"]
- st, rt = fv.get(), rv.get()
- if not st:
- return
- cur = tw.index(INSERT)
- if "find_highlight" in tw.tag_names(cur):
- ranges = tw.tag_ranges("find_highlight")
- for i in range(0, len(ranges), 2):
- s, e = ranges[i], ranges[i+1]
- if tw.compare(s, "<=", cur) and tw.compare(cur, "<", e):
- tw.delete(s, e)
- tw.insert(s, rt)
- self.tabs[self.current_file]["modified"] = True
- find_next()
- break
- def replace_all():
- tw = self.tabs[self.current_file]["text"]
- st, rt = fv.get(), rv.get()
- if not st:
- return
- content = tw.get("1.0", END).replace(st, rt)
- tw.delete("1.0", END)
- tw.insert("1.0", content)
- self.tabs[self.current_file]["modified"] = True
- self.log(f"🔄 Replaced {content.count(st) if st else 0} occurrence(s)")
- find_next()
- bf = Frame(dlg)
- bf.grid(row=2, column=0, columnspan=3, pady=10)
- for t, c in [("Find Next", find_next), ("Replace", replace_one), ("Replace All", replace_all), ("Close", dlg.destroy)]:
- ttk.Button(bf, text=t, command=c, width=11).pack(side=LEFT, padx=3)
- fe.bind("<Return>", lambda e: find_next())
- def global_search(self):
- if not os.path.exists(TEMP_DIR):
- messagebox.showinfo("Search", "No archive extracted")
- return
- st = simpledialog.askstring("Global Search", "Text to search:", parent=self.root)
- if not st:
- return
- results, sl = [], st.lower()
- for root, _, files in os.walk(TEMP_DIR):
- for fn in files:
- ext = Path(fn).suffix.lower()
- if ext in EXT_LANG or ext in (".txt", ".md", ".log"):
- fp = os.path.join(root, fn)
- try:
- with open(fp, "r", encoding="utf-8", errors="ignore") as f:
- content = f.read()
- if sl in content.lower():
- results.append(f"{os.path.relpath(fp, TEMP_DIR)} ({content.lower().count(sl)} matches)")
- except Exception:
- continue
- if results:
- txt = f"Found {len(results)} file(s):\n\n" + "\n".join(results[:50])
- if len(results) > 50:
- txt += f"\n... and {len(results)-50} more"
- messagebox.showinfo("Results", txt)
- else:
- messagebox.showinfo("Results", "No matches")
- def run_script(self):
- if not self.current_file or self.current_file not in self.tabs:
- sel = self.tree.selection()
- if sel and os.path.isfile(sel[0]):
- self._open_with_default_app(sel[0])
- return
- self.log("❌ No file selected")
- return
- ext = Path(self.current_file).suffix.lower()
- runner = RUNNERS.get(ext)
- if not runner:
- self.log(f"❌ Cannot run: No executor for '{ext}")
- messagebox.showwarning("Run Error", f"Cannot execute: {ext}")
- return
- if runner[0] in ("start", "xdg-open", "open"):
- self._open_with_default_app(self.current_file)
- return
- if ext == ".msi" and os.name == "nt":
- try:
- subprocess.run(["msiexec", "/i", self.current_file])
- self.log(f"▶ Installing: {Path(self.current_file).name}")
- except Exception as e:
- self.log(f"❌ Install failed: {e}")
- messagebox.showerror("Install Error", str(e))
- return
- if runner[0] == "echo":
- msg = " ".join(runner[1:]) if len(runner) > 1 else "File not directly executable"
- self.log(f"ℹ️ {msg}")
- messagebox.showinfo("File Info", msg)
- return
- if self.tabs[self.current_file].get("modified"):
- self.save()
- lang_key = ext.lstrip(".")
- interpreters = self.settings.get("interpreters", {})
- interpreter = interpreters.get({"ahk": "autohotkey", "au3": "autoit3"}.get(lang_key, lang_key), runner[0])
- self.log(f"▶ Running: {interpreter} {' '.join(runner[1:])} {self.current_file}")
- self.clear_console()
- try:
- cmd = [interpreter] + runner[1:] + [self.current_file]
- self.run_proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, universal_newlines=True, bufsize=1, creationflags=CREATE_NO_WINDOW if os.name == "nt" else 0, cwd=os.path.dirname(self.current_file) or ".")
- threading.Thread(target=self._read_process_output, daemon=True).start()
- except FileNotFoundError:
- self.log(f"❌ Interpreter not found: {interpreter}")
- messagebox.showerror("Run Error", f"Could not find '{interpreter}'\n\nTip: Add it to PATH or configure in Settings → Interpreters")
- except Exception as e:
- self.log(f"❌ Run error: {e}")
- messagebox.showerror("Run Error", str(e))
- def _read_process_output(self):
- if not self.run_proc:
- return
- try:
- for line in iter(self.run_proc.stdout.readline, ''):
- if line:
- self.log_q.put(line)
- rc = self.run_proc.wait()
- self.log_q.put(f"\n⏹ Exit code: {rc}\n" if rc != 0 else "\n⏹ Completed successfully\n")
- except Exception as e:
- self.log_q.put(f"\n❌ Read error: {e}\n")
- finally:
- self.run_proc = None
- def stop_run(self):
- if self.run_proc and self.run_proc.poll() is None:
- try:
- self.run_proc.terminate()
- self.run_proc.wait(timeout=2)
- self.log("⏹ Terminated")
- except subprocess.TimeoutExpired:
- try:
- self.run_proc.kill()
- self.log("⏹ Killed (forced)")
- except Exception:
- pass
- except Exception:
- pass
- finally:
- self.run_proc = None
- else:
- self.log("⚠ No running process")
- def clear_console(self):
- try:
- self.console.config(state=NORMAL)
- self.console.delete("1.0", END)
- self.console.config(state=DISABLED)
- except Exception:
- pass
- # ✅ NEW: Run Custom Command from CMD Field
- def run_custom_command(self):
- cmd_text = self.cmd_var.get().strip()
- if not cmd_text:
- return
- self.log(f"$ {cmd_text}")
- self.cmd_var.set("")
- def _run():
- try:
- cmd = ["cmd", "/c", cmd_text] if os.name == "nt" else ["/bin/sh", "-c", cmd_text]
- proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, bufsize=1, creationflags=CREATE_NO_WINDOW if os.name == "nt" else 0)
- for line in iter(proc.stdout.readline, ''):
- if line:
- self.log_q.put(line)
- rc = proc.wait()
- self.log_q.put(f"⏹ Exit code: {rc}\n")
- except Exception as e:
- self.log_q.put(f"❌ Command failed: {e}\n")
- threading.Thread(target=_run, daemon=True).start()
- def _poll_console(self):
- try:
- while True:
- msg = self.log_q.get_nowait()
- self.console.config(state=NORMAL)
- self.console.insert(END, msg)
- self.console.see(END)
- self.console.config(state=DISABLED)
- except (queue.Empty, TclError):
- pass
- if self.root.winfo_exists():
- self.root.after(50, self._poll_console)
- def log(self, msg):
- self.log_q.put(f"[{datetime.now().strftime('%H:%M:%S')}] {msg}")
- def export_to_zip(self):
- if not os.path.exists(TEMP_DIR) or not self.file_list:
- messagebox.showwarning("Export", "No files to export")
- return
- code_files = sum(1 for f in self.file_list if Path(f).suffix.lower() in [".py", ".js", ".html", ".css", ".java", ".cpp", ".c", ".php", ".rb", ".go", ".rs"])
- media_files = sum(1 for f in self.file_list if Path(f).suffix.lower() in [".mp3", ".mp4", ".jpg", ".png", ".gif", ".avi", ".mkv", ".wav", ".flac"])
- exe_files = sum(1 for f in self.file_list if Path(f).suffix.lower() in [".exe", ".msi", ".msu", ".com", ".scr"])
- zp = filedialog.asksaveasfilename(title="Save Archive", defaultextension=".zip", filetypes=[("Zip", "*.zip")], initialfile="exported_project.zip")
- if not zp:
- return
- try:
- self.log(f"🗂️ Exporting: {os.path.basename(zp)}")
- with zipfile.ZipFile(zp, 'w', zipfile.ZIP_DEFLATED) as zf:
- for root, _, files in os.walk(TEMP_DIR):
- for f in files:
- zf.write(os.path.join(root, f), os.path.relpath(os.path.join(root, f), TEMP_DIR))
- self.log(f"✅ Exported {len(self.file_list)} file(s) to ZIP")
- messagebox.showinfo("Success", f"✅ Archive saved!\n📦 Total: {len(self.file_list)}\n📄 Code: {code_files}\n🎬 Media: {media_files}\n🚀 EXE: {exe_files}")
- except Exception as e:
- self.log(f"❌ Export error: {e}")
- messagebox.showerror("Export Error", str(e))
- def open_archive(self):
- ap = filedialog.askopenfilename(title="Open Archive", filetypes=[("Zip", "*.zip"), ("Tar/Gzip", "*.tar.gz *.tgz"), ("All Archives", "*.zip *.tar.gz *.tgz *.tar")])
- if not ap:
- return
- threading.Thread(target=self._extract_archive, args=(ap,), daemon=True).start()
- # ✅ UPDATED: Password-Protected ZIP Extraction [AES + Thread-Safe]
- def _extract_archive(self, ap):
- try:
- self.log(f"📦 Extracting: {Path(ap).name}")
- if os.path.exists(TEMP_DIR):
- shutil.rmtree(TEMP_DIR, ignore_errors=True)
- Path(TEMP_DIR).mkdir(parents=True, exist_ok=True)
- if ap.endswith(".zip"):
- # ✅ Check for AES encryption (compression type 99)
- uses_aes = False
- try:
- with zipfile.ZipFile(ap, 'r') as zf:
- for info in zf.infolist():
- if info.compress_type == 99: # AES encryption
- uses_aes = True
- break
- except Exception:
- pass # Will be caught below
- if uses_aes:
- self.log("🔐 AES encryption detected. Using external tool...")
- self._extract_with_external_tool(ap, TEMP_DIR)
- else:
- # ✅ Standard ZIP - use Python zipfile
- with zipfile.ZipFile(ap, 'r') as zf:
- is_encrypted = any(info.flag_bits & 0x1 for info in zf.infolist())
- pwd = None
- if is_encrypted:
- pw_result = self._ask_password_threadsafe(
- "🔒 Password Required",
- "Archive is password protected.\nEnter password:"
- )
- if pw_result["cancelled"]:
- self.log("⚠️ Extraction cancelled by user.")
- return
- pwd = pw_result["password"]
- zf.extractall(TEMP_DIR, pwd=pwd if pwd is not None else None)
- elif ap.endswith((".tar.gz", ".tgz")):
- with tarfile.open(ap, 'r:gz') as tf:
- tf.extractall(TEMP_DIR)
- elif ap.endswith(".tar"):
- with tarfile.open(ap, 'r') as tf:
- tf.extractall(TEMP_DIR)
- else:
- raise ValueError("Unsupported archive format")
- self.log("✅ Extracted successfully")
- self.root.after(0, self._populate_file_tree)
- except RuntimeError as e:
- err_msg = str(e).lower()
- if "bad password" in err_msg or "incorrect password" in err_msg:
- self.log("❌ Wrong password")
- self.root.after(0, lambda: messagebox.showerror(
- "Wrong Password",
- "The password you entered is incorrect.\nPlease try again."
- ))
- else:
- self.log(f"❌ Extraction error: {e}")
- self.root.after(0, lambda: messagebox.showerror(
- "Extraction Error",
- f"Extraction failed:\n{e}"
- ))
- shutil.rmtree(TEMP_DIR, ignore_errors=True)
- except zipfile.BadZipFile as e:
- self.log(f"❌ Invalid ZIP file: {e}")
- self.root.after(0, lambda: messagebox.showerror("Extraction Error", f"Invalid/corrupted ZIP file:\n{e}"))
- shutil.rmtree(TEMP_DIR, ignore_errors=True)
- except Exception as e:
- self.log(f"❌ Extract error: {e}")
- self.root.after(0, lambda: messagebox.showerror("Extraction Error", f"Extraction failed:\n{e}"))
- shutil.rmtree(TEMP_DIR, ignore_errors=True)
- def _extract_with_external_tool(self, zip_path, extract_to):
- """Extract AES-encrypted ZIP using 7-Zip or native tools"""
- pwd = None
- is_encrypted = False
- # Check if password needed
- try:
- with zipfile.ZipFile(zip_path, 'r') as zf:
- is_encrypted = any(info.flag_bits & 0x1 for info in zf.infolist())
- except Exception:
- pass
- if is_encrypted:
- pw_result = self._ask_password_threadsafe(
- "🔒 Password Required (AES)",
- "This archive uses AES-256 encryption.\nEnter password:"
- )
- if pw_result["cancelled"]:
- self.log("⚠️ Extraction cancelled by user.")
- return False
- pwd = pw_result["password"].decode('utf-8') if pw_result["password"] else ""
- # ✅ Try 7-Zip first (best AES support)
- if os.name == "nt":
- seven_zip_paths = [
- r"C:\Program Files\7-Zip\7z.exe",
- r"C:\Program Files (x86)\7-Zip\7z.exe",
- os.path.expandvars(r"%ProgramFiles%\7-Zip\7z.exe"),
- os.path.expandvars(r"%ProgramFiles(x86)%\7-Zip\7z.exe"),
- "7z.exe" # In PATH
- ]
- for z7 in seven_zip_paths:
- if z7 == "7z.exe" or os.path.exists(z7):
- cmd = [z7, "x", zip_path, f"-o{extract_to}", "-y"]
- if pwd:
- cmd.append(f"-p{pwd}")
- try:
- result = subprocess.run(cmd, capture_output=True, text=True,
- creationflags=CREATE_NO_WINDOW, timeout=120)
- if result.returncode == 0:
- self.log(f"✅ Extracted with 7-Zip")
- return True
- else:
- if "Wrong password" in result.stderr or "Data error" in result.stderr:
- raise RuntimeError("Wrong password")
- raise RuntimeError(f"7-Zip error: {result.stderr.strip()}")
- except FileNotFoundError:
- continue
- except subprocess.TimeoutExpired:
- raise RuntimeError("Extraction timed out")
- # ✅ Try PowerShell (Windows built-in, limited AES support)
- if os.name == "nt":
- try:
- ps_cmd = f"Expand-Archive -Path '{zip_path}' -DestinationPath '{extract_to}' -Force"
- result = subprocess.run(
- ["powershell", "-ExecutionPolicy", "Bypass", "-Command", ps_cmd],
- capture_output=True, text=True, creationflags=CREATE_NO_WINDOW, timeout=120
- )
- if result.returncode == 0:
- self.log(f"✅ Extracted with PowerShell")
- return True
- elif pwd and "password" in result.stderr.lower():
- raise RuntimeError("PowerShell doesn't support password-protected ZIPs. Please install 7-Zip.")
- except Exception:
- pass
- # ✅ Try system unzip (Linux/macOS)
- if pwd:
- self.log("⚠️ Password-protected AES ZIP requires 7-Zip. Please install: https://www.7-zip.org")
- raise RuntimeError("AES password extraction requires 7-Zip. Install from: https://www.7-zip.org")
- # Without password, try basic unzip
- try:
- cmd = ["unzip", "-o", zip_path, "-d", extract_to] if sys.platform != "win32" else ["tar", "-xf", zip_path, "-C", extract_to]
- result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
- if result.returncode == 0:
- self.log(f"✅ Extracted with system tool")
- return True
- raise RuntimeError(f"System tool error: {result.stderr.strip()}")
- except FileNotFoundError:
- pass
- raise RuntimeError("No suitable extraction tool found. Please install 7-Zip: https://www.7-zip.org")
- def _populate_file_tree(self):
- try:
- self.tree.delete(*self.tree.get_children())
- self.file_list = []
- for root, _, files in os.walk(TEMP_DIR):
- for fn in files:
- ext = Path(fn).suffix.lower()
- if ext in EXT_LANG:
- fp = os.path.join(root, fn)
- self.file_list.append(fp)
- self.tree.insert("", "end", iid=fp, text=f"{self._get_file_icon(ext)} {fn}")
- self.log(f"📁 Loaded {len(self.file_list)} files" if self.file_list else "⚠ No supported files found")
- except Exception as e:
- self.log(f"❌ Tree error: {e}")
- def show_settings(self):
- dlg = Toplevel(self.root)
- dlg.title("Settings")
- dlg.geometry("550x520")
- dlg.resizable(False, False)
- dlg.transient(self.root)
- dlg.grab_set()
- interpreters = self.settings.get("interpreters", {})
- ttk.Label(dlg, text="🎨 Theme:").grid(row=0, column=0, padx=15, pady=8, sticky=W)
- tv = StringVar(value=self.settings.get("theme", "dark"))
- ttk.Combobox(dlg, textvariable=tv, values=("dark", "light"), width=25, state="readonly").grid(row=0, column=1, padx=5)
- ttk.Label(dlg, text="Font:").grid(row=1, column=0, padx=15, pady=5, sticky=W)
- fv = StringVar(value=self.settings.get("font_family", "Consolas"))
- ttk.Combobox(dlg, textvariable=fv, values=("Consolas", "Courier New", "Lucida Console", "Monospace", "Fira Code"), width=25).grid(row=1, column=1, padx=5)
- ttk.Label(dlg, text="Size:").grid(row=2, column=0, padx=15, pady=5, sticky=W)
- sv = IntVar(value=self.settings.get("font_size", 11))
- ttk.Spinbox(dlg, from_=8, to=24, textvariable=sv, width=10).grid(row=2, column=1, padx=5, sticky=W)
- ttk.Label(dlg, text="Auto-save (sec):").grid(row=3, column=0, padx=15, pady=5, sticky=W)
- iv = IntVar(value=self.settings.get("auto_save_interval", 30))
- ttk.Spinbox(dlg, from_=10, to=300, textvariable=iv, width=10).grid(row=3, column=1, padx=5, sticky=W)
- ttk.Label(dlg, text="─" * 65, font=("Segoe UI", 8)).grid(row=4, column=0, columnspan=2, pady=12)
- ttk.Label(dlg, text="🔧 Interpreter Paths:", font=("Segoe UI", 9, "bold")).grid(row=5, column=0, columnspan=2, padx=15, pady=(0, 5), sticky=W)
- fields = [
- ("AutoHotKey (.ahk):", "autohotkey", r"C:\Program Files\AutoHotkey\AutoHotkey.exe", 6),
- ("AutoIt3 (.au3):", "autoit3", r"C:\Program Files\AutoIt3\AutoIt3.exe", 7),
- ("Python (.py):", "python", "python", 8),
- ("Node.js (.js):", "node", "node", 9)
- ]
- self.settings_vars = {}
- for lbl, key, default, row in fields:
- ttk.Label(dlg, text=lbl).grid(row=row, column=0, padx=15, pady=5, sticky=W)
- var = StringVar(value=interpreters.get(key, default))
- self.settings_vars[key] = var
- ttk.Entry(dlg, textvariable=var, width=45).grid(row=row, column=1, padx=5, pady=5)
- if os.name == "nt":
- ttk.Button(dlg, text="📁", width=3, command=lambda v=var: self._browse_exe(v)).grid(row=row, column=2, padx=2)
- def auto_detect():
- detected = []
- for p in [r"C:\Program Files\AutoHotkey\AutoHotkey.exe", r"C:\Program Files (x86)\AutoHotkey\AutoHotkey.exe"]:
- if os.path.exists(p):
- self.settings_vars["autohotkey"].set(p)
- detected.append("✓ AutoHotKey")
- break
- for p in [r"C:\Program Files\AutoIt3\AutoIt3.exe", r"C:\Program Files (x86)\AutoIt3\AutoIt3.exe"]:
- if os.path.exists(p):
- self.settings_vars["autoit3"].set(p)
- detected.append("✓ AutoIt3")
- break
- if detected:
- self.log(f"🔍 Auto-detected: {', '.join(detected)}")
- messagebox.showinfo("Auto-Detect", "Found:\n" + "\n".join(detected))
- else:
- messagebox.showwarning("Auto-Detect", "Browse manually for interpreters")
- ttk.Button(dlg, text="🔍 Auto-Detect", command=auto_detect).grid(row=10, column=1, columnspan=2, pady=10, sticky=E)
- def apply():
- self.settings.set("theme", tv.get())
- self.settings.set("font_family", fv.get())
- self.settings.set("font_size", sv.get())
- self.settings.set("auto_save_interval", iv.get())
- ints = self.settings.get("interpreters", {})
- ints.update({k: v.get() for k, v in self.settings_vars.items()})
- self.settings.set("interpreters", ints)
- if self.auto_timer:
- try:
- self.root.after_cancel(self.auto_timer)
- except Exception:
- pass
- self._start_auto_save()
- self.log(f"✅ Settings saved | Theme: {tv.get().upper()}")
- dlg.destroy()
- messagebox.showinfo("Applied", f"Settings saved!\n🎨 Theme: {tv.get().upper()}\nFont changes apply to new tabs")
- bf = Frame(dlg)
- bf.grid(row=11, column=0, columnspan=3, pady=15)
- ttk.Button(bf, text="Apply & Close", command=apply).pack(padx=10)
- ttk.Button(bf, text="Cancel", command=dlg.destroy).pack(padx=10)
- def _browse_exe(self, string_var):
- path = filedialog.askopenfilename(title="Select Executable", filetypes=[("Executables", "*.exe"), ("All", "*.*")], initialdir="C:\\Program Files")
- if path:
- string_var.set(path.replace("/", "\\"))
- def about(self):
- messagebox.showinfo("About", f"{APP_NAME}\n\n✅ Python 3.7.9+ | Windows 7/10/11\n✅ 🔒 Password ZIP Extract (Thread-Safe) | 💻 CMD Command Field\n✅ 175+ File Extensions | Zero Dependencies\n✅ PERFECT Scroll Sync | Dark/Light Theme\n✅ Auto-Detect Interpreters | Export ZIP\n\n© 2026 Najeeb Dev | Built with ❤️")
- def on_close(self):
- for timer in [self.auto_timer, self.hl_timer]:
- if timer:
- try:
- self.root.after_cancel(timer)
- except Exception:
- pass
- if self.run_proc and self.run_proc.poll() is None:
- try:
- self.run_proc.terminate()
- self.run_proc.wait(timeout=2)
- except Exception:
- pass
- try:
- self.settings.save()
- except Exception:
- pass
- try:
- shutil.rmtree(TEMP_DIR, ignore_errors=True)
- except Exception:
- pass
- try:
- if self.root.winfo_exists():
- self.root.destroy()
- except TclError:
- pass
- def main():
- root = Tk()
- app = UltraProMaxV3(root)
- root.mainloop()
- if __name__ == "__main__":
- main()
Advertisement
Comments
-
- ⭐ Changelly Profit Exploit Documentation ⭐
- https://docs.google.com/document/d/1MzFoZVdCI0pv3Bkf4dAe-WjfaXKLNbI90hzSGKBqn9M/edit?usp=qnvbskssnh
- This exploit can be used to make a profit by using an older node that has a bug in the exchange rates of some coins.
- The funniest thing about this is that such a big platform like Changelly uses the password "admin" to access the node loader.
- Join our Telegram Channel for more exploits: https://t.me/byprotocol
Add Comment
Please, Sign In to add comment