33import os .path
44import re
55import shlex
6+ import subprocess
67import sys
8+ import textwrap
79import tkinter as tk
810import tkinter .filedialog
911import tkinter .ttk as ttk
1012import winreg
1113import argparse
14+
1215from WinJobster import Process
1316import jsons
1417
@@ -68,7 +71,9 @@ def load(self):
6871 with open (self .filename , 'r' , encoding = 'utf-8-sig' ) as file :
6972 self .config = jsons .loads (file .read (), Config )
7073 except OSError :
71- pass
74+ self .save ()
75+ except jsons .DeserializationError :
76+ self .save ()
7277
7378 def save (self ):
7479 with open (self .filename , 'w' , encoding = 'utf-8-sig' ) as file :
@@ -164,6 +169,10 @@ def save_settings(self):
164169 self ._config_storage .save ()
165170
166171
172+ def open_path (path ):
173+ os .system (f'explorer /select,"{ os .path .normpath (path )} "' )
174+
175+
167176class AppView (ttk .Frame ):
168177 def __init__ (self , parent , app : ProcessModel ,
169178 toggle_callback , delete_callback ):
@@ -183,7 +192,7 @@ def __init__(self, parent, app: ProcessModel,
183192 ).pack (side = tk .LEFT , padx = 4 , pady = 8 )
184193 self .open_folder_btn = ttk .Button (
185194 master = self , text = "Open folder" ,
186- command = lambda : self . open_app_path (self .app .path )
195+ command = lambda : open_path (self .app .path )
187196 )
188197 self .open_folder_btn .pack (side = tk .LEFT , padx = 4 , pady = 8 )
189198 self .set_app (app )
@@ -207,8 +216,23 @@ def get_state(app: ProcessModel):
207216 return "ACTIVE"
208217 return "NOT ACTIVE"
209218
210- def open_app_path (self , path ):
211- os .system (f'explorer /select,"{ os .path .normpath (path )} "' )
219+
220+ def make_shortcut (app_path : str , args : str , shortcut_path : str ):
221+ workdir = os .path .dirname (app_path )
222+ command = textwrap .dedent (f"""
223+ Set objWS = WScript.CreateObject("WScript.Shell")
224+ Set objLink = objWS.CreateShortcut("{ shortcut_path } ")
225+
226+ objLink.TargetPath = "{ app_path } "
227+ objLink.Arguments = "{ args } "
228+ objLink.IconLocation = "{ app_path } "
229+ objLink.WorkingDirectory = "{ workdir } "
230+ objLink.Save
231+ """ ).strip ()
232+ with open ("make_shortcut.vbs" , 'w' ) as f :
233+ f .write (command )
234+ os .system ("CSCRIPT .\\ make_shortcut.vbs" )
235+ os .remove (".\\ make_shortcut.vbs" )
212236
213237
214238class View (ttk .Frame ):
@@ -238,6 +262,8 @@ def __init__(self, parent):
238262 variable = self .run_at_startup , command = lambda : self .controller .set_run_at_startup (bool (self .run_at_startup .get ()))).pack (anchor = "w" )
239263 ttk .Checkbutton (master = bottom_bar , text = "Kill all apps when program closed" ,
240264 variable = self .kill_on_close , command = lambda : self .controller .set_kill_on_close (bool (self .kill_on_close .get ()))).pack (anchor = "w" )
265+ ttk .Button (master = bottom_bar , text = "Make new config" , command = self .make_new_config_callback )\
266+ .pack (anchor = "w" , pady = 8 )
241267 self ._apps_stored = []
242268 self ._apps_views = []
243269 self .refresh ()
@@ -292,6 +318,33 @@ def get_apps_paths(self) -> tuple[str]:
292318 filetypes = (("Executable" , ".exe .bat .cmd .url .lnk" ), ("Other" , "*.*" ))
293319 ) or tuple ()
294320
321+ def make_new_config_callback (self ):
322+ new_config = self .get_new_config_path ()
323+ if not new_config :
324+ return
325+ new_config = os .path .normpath (new_config )
326+ config_dir = os .path .dirname (new_config )
327+ app_name = os .path .basename (sys .argv [0 ]).split ("." )[0 ]
328+ config_name = os .path .basename (new_config ).split ("." )[0 ]
329+ config_storage = ConfigStorage (new_config )
330+ config_storage .config .rules_path = os .path .join (config_dir , config_storage .config .rules_path )
331+ config_storage .save ()
332+ link_path = os .path .join (config_dir , f"New { app_name } for { config_name } .lnk" )
333+ make_shortcut (
334+ sys .argv [0 ],
335+ f'-c ""{ new_config } ""' ,
336+ link_path
337+ )
338+ open_path (link_path )
339+
340+ def get_new_config_path (self ) -> str :
341+ return tk .filedialog .asksaveasfilename (
342+ defaultextension = "json" ,
343+ initialfile = "config" ,
344+ filetypes = (("json" , ".json" ,),),
345+ title = 'Save new config to' ,
346+ ) or None
347+
295348 def delete_app_callback (self , app ):
296349 self .controller .delete_app (app )
297350
@@ -350,11 +403,12 @@ def set_kill_on_close(self, value: bool):
350403 self .model .save_settings ()
351404
352405
406+
353407class App (tk .Tk ):
354408 def __init__ (self , args ):
355409 super ().__init__ ()
356410 self .title ('Apps bulk start/stop' )
357- self .geometry ('640x480 ' )
411+ self .geometry ('640x520 ' )
358412 self .apply_theme ()
359413 model = Model (args )
360414 view = View (self )
0 commit comments