├── .gitignore ├── README.md ├── actions └── commandaction.py ├── cascadingmenumaker.py ├── contextdocparser.py ├── examples ├── big.yml └── simple.yml ├── icons ├── dllicon.py ├── exefileicon.py └── fileicon.py ├── main.py └── titles └── stringtitle.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # dotenv 80 | .env 81 | 82 | # virtualenv 83 | .venv 84 | venv/ 85 | ENV/ 86 | 87 | # Spyder project settings 88 | .spyderproject 89 | 90 | # Rope project settings 91 | .ropeproject 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # context-menu-maker 2 | 3 | A program that makes Windows context menus from YAML files. It's extremely 4 | customizable, and easy to use. 5 | 6 | ![Demo Image](https://cloud.githubusercontent.com/assets/7797957/23379972/018fb536-fd07-11e6-8fc2-0787d84af43f.png) 7 | 8 | ## Install 9 | 10 | Just download the contents of this repository and place them in a location that 11 | shouldn't change much. I used `C:\Python36\Scripts\context-menu-maker`. 12 | 13 | ## Usage 14 | 15 | ### Example 16 | 17 | Consider the following snippet: 18 | 19 | ```yaml 20 | name: 'settings' 21 | title: 'Settings' 22 | exists: [^folder] 23 | tree: 24 | control-panel: 25 | title: 'Control Panel' 26 | icon: 27 | handler: 'exefile' 28 | executable: 'control.exe' 29 | action: 'control.exe' 30 | other-options: 31 | title: 'Other...' 32 | icon: 'C:\insert_icon_file_here.ico' 33 | tree: 34 | reg: 35 | title: 'Registry Editor' 36 | icon: 37 | handler: 'exefile' 38 | executable: 'regedit.exe' 39 | action: 'regedit.exe' 40 | ``` 41 | When installed, right clicking on a folder and then clicking on a button 42 | entitled "Settings" would produce a context menu that looks like this: 43 | 44 | ![Example Image](https://cloud.githubusercontent.com/assets/7797957/23379902/abb01cc8-fd06-11e6-9575-155931b27e4d.png) 45 | 46 | ### Syntax 47 | 48 | #### Header 49 | 50 | Each menu descriptor file always includes a 'name', 'title', and 'exists' 51 | field at the top. 52 | 53 | ##### Name 54 | 55 | The 'name' field describes the internal name that the menu shall assume in Windows. 56 | It's used for placing the correct registry keys. Give it a simple name 57 | (preferably without spaces) 58 | 59 | ##### Title 60 | 61 | This field describes the external name (or, rather, 'verb' as given by MSDN) 62 | that your context menu will use. It's the name that shows up in Explorer's context menu. 63 | 64 | ##### Exists 65 | 66 | This field is a bit more complicated. It describes if your context menu will show up 67 | depending on what you click on/where you click. For example: 68 | `exists: [^allfiles]` would tell Explorer that your context menu can be triggered off 69 | of any file (the context menu produced by right clicking a file will have your menu 70 | on it). This does not include directories. 71 | 72 | There are multiple things that you can put in your exists section; here they are: 73 | 74 | | Name | Location | 75 | |------|----------| 76 | |^allfiles|Every file type| 77 | |^desktop|Files on the desktop| 78 | |^directory|Not sure, but it's in the registry. Will have to figure out what this does.| 79 | |^directoryba|Same as above, but the background.| 80 | |^folder|Any folder| 81 | |^folderba|The background of an open folder window| 82 | |^drive|Any drive| 83 | |^driveba|The background of an open drive window| 84 | |^everything|All of the above| 85 | 86 | Additionally, you can specify single file types that you wish to include: 87 | `exists: [doc, pdf, jpg]` 88 | 89 | ##### Icon 90 | 91 | This field provides an icon that will be shown in the context menu; note that it _cannot_ 92 | use a handler (mentioned later); it must be a pointer to a file containing the icon that 93 | you wish to use. 94 | 95 | #### Handlers 96 | 97 | Handlers is a term I've used to describe a piece of code that modifies the menu in a specific way. 98 | Vague, I know. Here's an example: 99 | 100 | ```yaml 101 | icon: 102 | handler: exefile 103 | executable: 'C:\Windows\System32\regedit.exe' 104 | ``` 105 | 106 | It uses a handler called "exefile", which pulls an icon from an executable file. It has different 107 | functionality than the default icon field behavior, which normally loads an icon from a 108 | given file. 109 | 110 | Handlers are useful when an action is more efficiently completed by using a method other 111 | than the default behavior. 112 | 113 | #### Tree 114 | 115 | Creating nested context menus is done by traversing the `tree` field in the .yml file. 116 | Items in the tree can have actions (an action object), or another tree (a submenu). You can name them 117 | whatever you want; the program doesn't use the name. 118 | 119 | ##### Objects 120 | 121 | Objects in the tree have properties that must be fulfulled in order for the object 122 | to display correctly. These properties can use certain handlers that provide resources 123 | in certain ways. For example, an icon handler might take an icon from a provided .exe 124 | file. Using handlers helps create better customizability and gives the user a lot of 125 | power to create their own options easily. 126 | 127 | In the tree, objects are defined as sub-fields: 128 | ```yaml 129 | tree: 130 | object-action: 131 | ... 132 | object-submenu: 133 | tree: 134 | object-action-nested: 135 | ... 136 | object-submenu-nested: 137 | ... 138 | ``` 139 | 140 | ##### Action objects 141 | 142 | These objects are displayed as buttons in the context menu; pressing them launches an action, 143 | as opposed to displaying a submenu. To make an action, one must add an object with a field entitled 144 | `action`: 145 | ```yaml 146 | tree: 147 | object-action: 148 | action: 'C:\Windows\System32\regedit.exe' 149 | ... 150 | ``` 151 | 152 | An action can have a _handler_ associated with it. For more information on handlers, jump to the 153 | "handlers" section above. 154 | 155 | ##### Submenu objects 156 | 157 | Submenu objects are simpler. They must contain a `tree` field, which, in turn, includes other 158 | objects, be they actions or more submenus: 159 | ```yaml 160 | tree: 161 | object-submenu: 162 | tree: 163 | object-action: 164 | ... 165 | object-submenu: 166 | tree: 167 | ... 168 | ``` 169 | 170 | #### Shared properties 171 | 172 | The `title` field and the `icon` (optional) field can be used on both submenu and action objects. 173 | The `title` field gives an external name to the object (the name that the end user will see in 174 | the context menu), and the `icon` field obviously gives the icon to be used for that particular object. 175 | 176 | ### Installation 177 | Context menus are installed once, and can always be uninstalled. 178 | 179 | To install a context menu from a .yml file, run at an administrative command 180 | prompt: 181 | 182 | ```cmd 183 | C:\path_to_python\python.exe C:\path_to_main.py C:\path_to_yml_file install 184 | ``` 185 | 186 | To install the example .yml, the command would be something like this 187 | (at least, for me): 188 | 189 | ```cmd 190 | C:\Python36\python.exe C:\Python36\Scripts\context-menu-maker\main.py C:\Users\me\context-menus\example.yml install 191 | ``` 192 | 193 | To uninstall a context menu, simply run the install command and substitute 194 | "install" with "remove". 195 | 196 | ```cmd 197 | C:\path_to_python\python.exe C:\path_to_main.py C:\path_to_yml_file remove 198 | ``` 199 | 200 | To test a context menu: 201 | 202 | ```cmd 203 | C:\path_to_python\python.exe C:\path_to_main.py C:\path_to_yml_file run 204 | ``` 205 | -------------------------------------------------------------------------------- /actions/commandaction.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | class CommandAction: 5 | name = "command" 6 | def __init__(self,handler,command): 7 | self.command = command 8 | def run(self): 9 | try: 10 | subprocess.Popen("C:\Windows\System32\cmd.exe /c "\ 11 | +self.command.replace("%1",sys.argv[3]),shell=True) 12 | except: 13 | subprocess.Popen("C:\Windows\System32\cmd.exe /c "\ 14 | +self.command,shell=True) 15 | 16 | exports = [CommandAction] -------------------------------------------------------------------------------- /cascadingmenumaker.py: -------------------------------------------------------------------------------- 1 | # Much of this code was taken from Simon Brunning's SysTrayIcon.py. 2 | # I used most of the code pertaining to creating a popup menu. 3 | import os 4 | import sys 5 | import win32api 6 | import win32con 7 | import win32gui_struct 8 | import win32gui 9 | 10 | class CascadingMenuMaker: 11 | def __init__(self,options,docObj): 12 | self.menu_options = options 13 | self.action_map = {} 14 | # Initializes win32gui components 15 | self.window_class = win32gui.WNDCLASS() 16 | self.documentObject = docObj 17 | self.hinst = self.window_class.hInstance = \ 18 | win32gui.GetModuleHandle(None) 19 | self.message_map = {win32con.WM_COMMAND: self.callback, 20 | win32con.WM_DESTROY: self.destroy, 21 | win32con.WM_UNINITMENUPOPUP: self.destroy} 22 | self.window_class.lpfnWndProc = self.message_map 23 | self.window_menu_name = self.documentObject["title"] 24 | self.window_class_name = "Python.ContextMenu."+ \ 25 | self.documentObject["name"].upper() 26 | self.number_ids(self.menu_options,0) 27 | self.create_window() 28 | def number_ids(self,menu_opts,onum): 29 | # Recursively gives numerical UIDs to objects in the command tree. 30 | for opt in menu_opts: 31 | if type(opt[2]) == type([]): 32 | opt.append(onum) 33 | onum += 1 34 | onum = self.number_ids(opt[2],onum) 35 | else: 36 | opt.append(onum) 37 | self.action_map[onum] = opt[2] 38 | onum += 1 39 | return onum 40 | def create_window(self): 41 | # Creates the dummy invisible window that the popup will come from. 42 | self.window_class.lpszMenuName = self.window_menu_name 43 | self.window_class.lpszClassName = self.window_class_name 44 | self.classAtom = win32gui.RegisterClass(self.window_class) 45 | self.style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU 46 | self.hwnd = win32gui.CreateWindow(self.classAtom, 47 | self.window_class_name, 48 | self.style, 49 | 0, 50 | 0, 51 | win32con.CW_USEDEFAULT, 52 | win32con.CW_USEDEFAULT, 53 | 0, 54 | 0, 55 | self.hinst, 56 | None) 57 | win32gui.UpdateWindow(self.hwnd) 58 | self.notify_id = None 59 | self.add_popup() 60 | win32gui.PumpMessages() 61 | def add_popup(self): 62 | # Creates the popup from the dummy window. 63 | menu = win32gui.CreatePopupMenu() 64 | self.create_menu(menu,self.menu_options) 65 | pos = win32gui.GetCursorPos() 66 | win32gui.SetForegroundWindow(self.hwnd) 67 | win32gui.TrackPopupMenu(menu, 68 | win32con.TPM_LEFTALIGN, 69 | pos[0], 70 | pos[1], 71 | 0, 72 | self.hwnd, 73 | None) 74 | win32gui.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0) 75 | def create_menu(self, menu, menopts): 76 | # Inserts all items into the popup (again, recursive) 77 | for opt in menopts: 78 | if opt[1] != None: 79 | opt_icon = opt[1].get() 80 | else: 81 | opt_icon = None 82 | mi_fstate = 0 83 | mi_ftype = 0 84 | if opt[3]["disabled"]: 85 | mi_fstate = mi_fstate | win32con.MFS_DISABLED 86 | if opt[3]["highlight"]: 87 | mi_fstate = mi_fstate | win32con.MFS_HILITE 88 | if opt[3]["separator"]: 89 | mi_ftype = mi_ftype | win32con.MFT_SEPARATOR 90 | if type(opt[2]) == type([]): 91 | submenu = win32gui.CreatePopupMenu() 92 | self.create_menu(submenu, opt[2]) 93 | item, extras = win32gui_struct.PackMENUITEMINFO( 94 | text=opt[0].get(), 95 | hbmpItem=opt_icon, 96 | hSubMenu=submenu, 97 | fState=mi_fstate, 98 | fType=mi_ftype, 99 | wID=opt[-1]) 100 | else: 101 | item, extras = win32gui_struct.PackMENUITEMINFO( 102 | text=opt[0].get(), 103 | hbmpItem=opt_icon, 104 | fState=mi_fstate, 105 | fType=mi_ftype, 106 | wID=opt[-1]) 107 | win32gui.InsertMenuItem(menu, 0, 1, item) 108 | def destroy(self, hwnd, msg, wparam, lparam): 109 | # Called from win32gui when we want to quit. 110 | self.destroy_nothing() 111 | def destroy_nothing(self): 112 | # Called by other things when we want to quit. 113 | win32gui.PostQuitMessage(0) 114 | def callback(self, hwnd, msg, wparam, lparam): 115 | # Called when the user clicks an item. 116 | self.destroy_nothing() 117 | self.action_map[win32gui.LOWORD(wparam)].run() 118 | sys.exit(0) -------------------------------------------------------------------------------- /contextdocparser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import yaml 4 | import winreg 5 | import glob 6 | import importlib 7 | import ctypes 8 | 9 | class ContextDocParser: 10 | def __init__(self,document,main): 11 | self.doc = document 12 | self.main = main 13 | self.documentObject = yaml.load(document) 14 | self.contextName = self.documentObject["name"] 15 | self.contextTitle = self.documentObject["title"] 16 | self.SPECIAL_FEXT_LIST ={ "^allfiles" : "*", 17 | "^desktop" : "DesktopBackground", 18 | "^directory" : "Directory", 19 | "^directoryba" : "Directory\\Background", 20 | "^folder" : "Folder", 21 | "^folderba" : "Folder\\Background", 22 | "^drive" : "Drive", 23 | "^driveba" : "Drive\\Background" } 24 | self.actionslist = {} 25 | self.iconslist = {} 26 | self.titleslist = {} 27 | self.out = [] 28 | def run(self): 29 | # Runs the command tree. Basically, this translates the YAML into 30 | # a whole bunch of nested lists. 31 | self.load_extensions() 32 | self.traverse_tree(self.documentObject["tree"],self.out) 33 | self.out.reverse() 34 | def load_extensions(self): 35 | # Loads the extensions contained in the "actions" and "icon" folders. 36 | # NEW: titles can be added too, from the "title" folder. 37 | for item in glob.glob("actions/*.py"): 38 | mod = importlib.import_module(item[0:-3].replace("\\",".")) 39 | for ex in mod.exports: 40 | self.actionslist[ex.name] = ex 41 | for item in glob.glob("icons/*.py"): 42 | mod = importlib.import_module(item[0:-3].replace("\\",".")) 43 | for ex in mod.exports: 44 | self.iconslist[ex.name] = ex 45 | for item in glob.glob("titles/*.py"): 46 | mod = importlib.import_module(item[0:-3].replace("\\",".")) 47 | for ex in mod.exports: 48 | self.titleslist[ex.name] = ex 49 | def make_action(self,action): 50 | # Takes an action tree and returns an initialized action class. 51 | return self.actionslist[action['handler']](**action) 52 | def make_icon(self,icon): 53 | # Takes an icon tree and returns an initialized icon class. 54 | return self.iconslist[icon['handler']](**icon) 55 | def make_title(self,title): 56 | # Takes an title tree and returns an initialized title class. 57 | return self.titleslist[title['handler']](**title) 58 | def make_action_def(self,command): 59 | # Takes an action name and returns an initialized CommandAction class. 60 | return self.actionslist['command']('command',command) 61 | def make_icon_def(self,iconname): 62 | # Takes an icon file and returns an initialized FileIcon class. 63 | return self.iconslist['file']('file',iconname) 64 | def make_title_def(self,title): 65 | # Takes an title tree and returns an initialized StringTitle class. 66 | return self.titleslist['string']('string',title) 67 | def traverse_tree(self,mainList,outx): 68 | # Recursively looks through tree and adds to main list. 69 | for itemName, itemValue in mainList.items(): 70 | flags = {} 71 | if "separator" in itemValue: 72 | if itemValue["separator"] == True: 73 | flags["separator"] = True 74 | else: 75 | flags["separator"] = False 76 | else: 77 | flags["separator"] = False 78 | if "disabled" in itemValue: 79 | if itemValue["disabled"] == True: 80 | flags["disabled"] = True 81 | else: 82 | flags["disabled"] = False 83 | else: 84 | flags["disabled"] = False 85 | if "highlight" in itemValue: 86 | if itemValue["highlight"] == True: 87 | flags["highlight"] = True 88 | else: 89 | flags["highlight"] = False 90 | else: 91 | flags["highlight"] = False 92 | if "icon" in itemValue: 93 | if type(itemValue["icon"]) == type(""): 94 | ico = self.make_icon_def(itemValue["icon"]) 95 | elif type(itemValue["icon"]) == type({}): 96 | ico = self.make_icon(itemValue["icon"]) 97 | else: 98 | ico = None 99 | else: 100 | ico = None 101 | if type(itemValue["title"]) == type(""): 102 | title = self.make_title_def(itemValue["title"]) 103 | elif type(itemValue["title"]) == type({}): 104 | title = self.make_title(itemValue["title"]) 105 | else: 106 | title = None 107 | if "action" in itemValue: 108 | if type(itemValue["action"]) == type(""): 109 | act = self.make_action_def(itemValue["action"]) 110 | elif type(itemValue["action"]) == type({}): 111 | act = self.make_action(itemValue["action"]) 112 | else: 113 | act = None 114 | outx.append([title,ico,act,flags]) 115 | elif "tree" in itemValue: 116 | outx.append([title,ico,[],flags]) 117 | self.traverse_tree(itemValue['tree'],outx[-1][2]) 118 | outx[-1][2].reverse() 119 | def install(self): 120 | # Installs registry keys, effectively initializing the script. 121 | self.verify_uac() 122 | exe = sys.executable 123 | exe = sys.executable.replace("python.exe","pythonw.exe") 124 | for fext in self.generate_fexts(self.documentObject["exists"]): 125 | fk = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT,fext+"\\shell\\" \ 126 | +self.documentObject["name"]) 127 | fsk = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT,fext+"\\shell\\" \ 128 | +self.documentObject["name"]+"\\command") 129 | winreg.SetValueEx(fk,"",0,winreg.REG_SZ, \ 130 | self.documentObject["title"]) 131 | if "icon" in self.documentObject: 132 | winreg.SetValueEx(fk,"Icon",0,winreg.REG_SZ, \ 133 | self.documentObject["icon"]) 134 | winreg.SetValueEx(fsk,"",0,winreg.REG_SZ,exe+" \""\ 135 | +os.path.realpath(self.main+"\" \""\ 136 | +os.path.realpath(self.doc.name))+"\" run %L") 137 | def remove(self): 138 | # Removes registry keys cleanly. 139 | self.verify_uac() 140 | for fext in self.generate_fexts(self.documentObject["exists"]): 141 | winreg.DeleteKey(winreg.HKEY_CLASSES_ROOT,fext+"\\shell\\"\ 142 | +self.documentObject["name"]+"\\command") 143 | winreg.DeleteKey(winreg.HKEY_CLASSES_ROOT,fext+"\\shell\\"\ 144 | +self.documentObject["name"]) 145 | def verify_uac(self): 146 | # Verifies that the use is an admin, so no errors are produced. 147 | isen = None 148 | try: 149 | isen = ctypes.windll.shell32.IsUserAnAdmin() 150 | except: 151 | isen = False 152 | if isen == False: 153 | print("Administrator privileges are needed to install...") 154 | sys.exit(0) 155 | def generate_fexts(self,exists): 156 | # Helper to generate a list of registry keys. 157 | fextlist = [] 158 | for exist in exists: 159 | if exist.startswith("^"): 160 | if exist == "^everything": 161 | return self.SPECIAL_FEXT_LIST.values() 162 | elif exist in self.SPECIAL_FEXT_LIST: 163 | fextlist.append(self.SPECIAL_FEXT_LIST[exist]) 164 | else: 165 | fextlist.append(exist) 166 | return fextlist -------------------------------------------------------------------------------- /examples/big.yml: -------------------------------------------------------------------------------- 1 | name: random-commands 2 | title: "Random Commands" 3 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_5323.ico' 4 | exists: [^allfiles] 5 | tree: 6 | config-admin: 7 | title: 'Configuration/Administration' 8 | icon: 9 | handler: 'file' 10 | iconfile: 'C:\contextmenu-assets\icons\imageres\imageres_34.ico' 11 | tree: 12 | admin-tools: 13 | title: 'Administrative Tools' 14 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_36.ico' 15 | tree: 16 | computer-mng: 17 | title: 'Computer Management' 18 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_114.ico' 19 | action: 'mmc compmgmt.msc' 20 | defrag: 21 | title: 'Disk Defragmentation' 22 | icon: 23 | handler: 'exefile' 24 | executable: 'dfrgui.exe' 25 | action: 'dfrgui.exe' 26 | disk-clean: 27 | title: 'Disk Cleanup' 28 | icon: 29 | handler: 'exefile' 30 | executable: 'cleanmgr.exe' 31 | action: 'cleanmgr.exe' 32 | event-viewer: 33 | title: 'Event Viewer' 34 | icon: 35 | handler: 'exefile' 36 | executable: 'eventvwr.exe' 37 | action: 'eventvwr.exe' 38 | firewall: 39 | title: 'Firewall Settings' 40 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_151.ico' 41 | action: 'mmc WF.msc' 42 | memdiag: 43 | title: 'Memory Diagnostics' 44 | icon: 45 | handler: 'exefile' 46 | executable: 'mdsched.exe' 47 | action: 'mdsched.exe' 48 | perf-mon: 49 | title: 'Performance Monitor' 50 | icon: 51 | handler: 'exefile' 52 | executable: 'perfmon.exe' 53 | action: 'perfmon.exe' 54 | res-mon: 55 | title: 'Resource Monitor' 56 | icon: 57 | handler: 'exefile' 58 | executable: 'resmon.exe' 59 | action: 'resmon.exe' 60 | services: 61 | title: 'Services' 62 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_68.ico' 63 | action: 'mmc services.msc' 64 | sys-config: 65 | title: 'System Configuration' 66 | icon: 67 | handler: 'exefile' 68 | executable: 'msconfig.exe' 69 | action: 'msconfig.exe' 70 | sys-info: 71 | title: 'System Information' 72 | icon: 73 | handler: 'exefile' 74 | executable: 'msinfo32.exe' 75 | action: 'msinfo32.exe' 76 | task-sched: 77 | title: 'Task Scheduler' 78 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_5368.ico' 79 | action: 'mmc taskschd.msc /s' 80 | config: 81 | title: 'Configuration' 82 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_SHIDI_SHIELD_INTERNAL.ico' 83 | tree: 84 | control-panel: 85 | title: 'Control Panel' 86 | icon: 87 | handler: 'exefile' 88 | executable: 'control.exe' 89 | tree: 90 | self: 91 | title: 'Control Panel' 92 | icon: 93 | handler: 'exefile' 94 | executable: 'control.exe' 95 | action: 'control.exe' 96 | god-mode: 97 | title: 'All Tasks' 98 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_101.ico' 99 | action: 'explorer shell:::{ED7BA470-8E54-465E-825C-99712043E01C}' 100 | task-mgr: 101 | title: 'Task Manager' 102 | icon: 103 | handler: 'exefile' 104 | executable: 'taskmgr.exe' 105 | action: 'taskmgr.exe' 106 | regedit: 107 | title: 'Registry Editor' 108 | icon: 109 | handler: 'exefile' 110 | executable: 'regedit.exe' 111 | action: 'regedit.exe' 112 | personalization: 113 | title: 'Personalization Settings' 114 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_5346.ico' 115 | tree: 116 | appearance: 117 | title: 'Appearance Settings' 118 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_151.ico' 119 | action: 'control desk.cpl,,2' 120 | desktop-background: 121 | title: 'Desktop Background' 122 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_72.ico' 123 | action: 'control desk.cpl,,@desktop' 124 | desktop-icons: 125 | title: 'Desktop Icons' 126 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_71.ico' 127 | action: 'control desk.cpl,,0' 128 | dpi-scaling: 129 | title: 'DPI Scaling' 130 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_5366.ico' 131 | action: 'dpiscaling' 132 | item-size: 133 | title: 'Item Size' 134 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_116.ico' 135 | action: 'explorer.exe shell:::{C555438B-3C23-4769-A71F-B6D3D9B6053A}' 136 | screen-resolution: 137 | title: 'Screen Resolution' 138 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_110.ico' 139 | action: 'control desk.cpl,,3' 140 | screensaver: 141 | title: 'Screen Saver' 142 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_70.ico' 143 | action: 'control desk.cpl,,1' 144 | themes: 145 | title: 'Theme' 146 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_80.ico' 147 | action: 'control desk.cpl,,@themes' 148 | window-colorization: 149 | title: 'Window Color' 150 | icon: 'C:\contextmenu-assets\icons\imageres\imageres_151.ico' 151 | action: 'explorer shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\1\::{ED834ED6-4B5A-4bfe-8F11-A626DCB6A921}\pageColorization' 152 | 153 | -------------------------------------------------------------------------------- /examples/simple.yml: -------------------------------------------------------------------------------- 1 | name: 'random-settings' 2 | title: 'Settings' 3 | exists: [^folder] 4 | tree: 5 | control-panel: 6 | title: 'Control Panel' 7 | icon: 8 | handler: 'exefile' 9 | executable: 'control.exe' 10 | action: 'control.exe' 11 | other-options: 12 | title: 'Other...' 13 | tree: 14 | reg: 15 | title: 'Registry Editor' 16 | icon: 17 | handler: 'exefile' 18 | executable: 'regedit.exe' 19 | action: 'regedit.exe' -------------------------------------------------------------------------------- /icons/dllicon.py: -------------------------------------------------------------------------------- 1 | import win32api 2 | import win32gui 3 | import win32con 4 | 5 | class DllFileIconHandler: 6 | name = "dll" 7 | def __init__(self,handler,dll,id): 8 | self.dll = dll 9 | self.id = id 10 | self.hbm = self.prep() 11 | def prep(self): 12 | ico_x = win32api.GetSystemMetrics(win32con.SM_CXSMICON) 13 | ico_y = win32api.GetSystemMetrics(win32con.SM_CYSMICON) 14 | lr, sm = win32gui.ExtractIconEx(self.dll,self.id) 15 | win32gui.DestroyIcon(lr[0]) 16 | hicon = sm[0] 17 | hdcBitmap = win32gui.CreateCompatibleDC(0) 18 | hdcScreen = win32gui.GetDC(0) 19 | hbm = win32gui.CreateCompatibleBitmap(hdcScreen, ico_x, ico_y) 20 | hbmOld = win32gui.SelectObject(hdcBitmap, hbm) 21 | brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU) 22 | win32gui.FillRect(hdcBitmap, (0, 0, 16, 16), brush) 23 | win32gui.DrawIconEx(hdcBitmap, 0, 0, hicon, ico_x, ico_y, 0, 0, win32con.DI_NORMAL) 24 | win32gui.SelectObject(hdcBitmap, hbmOld) 25 | win32gui.DeleteDC(hdcBitmap) 26 | return hbm 27 | def get(self): 28 | return self.hbm 29 | 30 | exports = [DllFileIconHandler] -------------------------------------------------------------------------------- /icons/exefileicon.py: -------------------------------------------------------------------------------- 1 | import win32api 2 | import win32gui 3 | import win32con 4 | 5 | class ExeFileIconHandler: 6 | name = "exefile" 7 | def __init__(self,handler,executable): 8 | self.executable = executable 9 | self.hbm = self.prep() 10 | def prep(self): 11 | ico_x = win32api.GetSystemMetrics(win32con.SM_CXSMICON) 12 | ico_y = win32api.GetSystemMetrics(win32con.SM_CYSMICON) 13 | lr, sm = win32gui.ExtractIconEx(self.executable,0) 14 | win32gui.DestroyIcon(lr[0]) 15 | hicon = sm[0] 16 | hdcBitmap = win32gui.CreateCompatibleDC(0) 17 | hdcScreen = win32gui.GetDC(0) 18 | hbm = win32gui.CreateCompatibleBitmap(hdcScreen, ico_x, ico_y) 19 | hbmOld = win32gui.SelectObject(hdcBitmap, hbm) 20 | brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU) 21 | win32gui.FillRect(hdcBitmap, (0, 0, 16, 16), brush) 22 | win32gui.DrawIconEx(hdcBitmap, 0, 0, hicon, ico_x, ico_y, 0, 0, win32con.DI_NORMAL) 23 | win32gui.SelectObject(hdcBitmap, hbmOld) 24 | win32gui.DeleteDC(hdcBitmap) 25 | return hbm 26 | def get(self): 27 | return self.hbm 28 | 29 | exports = [ExeFileIconHandler] -------------------------------------------------------------------------------- /icons/fileicon.py: -------------------------------------------------------------------------------- 1 | import win32api 2 | import win32gui 3 | import win32con 4 | 5 | global fileiconhandler_cache 6 | fileiconhandler_cache = {} 7 | 8 | class FileIconHandler: 9 | name = "file" 10 | def __init__(self,handler,iconfile): 11 | self.icon = iconfile 12 | if iconfile in fileiconhandler_cache: 13 | self.hbm = fileiconhandler_cache[iconfile] 14 | else: 15 | fileiconhandler_cache[iconfile] = self.prep() 16 | self.hbm = fileiconhandler_cache[iconfile] 17 | def prep(self): 18 | ico_x = win32api.GetSystemMetrics(win32con.SM_CXSMICON) 19 | ico_y = win32api.GetSystemMetrics(win32con.SM_CYSMICON) 20 | hicon = win32gui.LoadImage(0, self.icon, win32con.IMAGE_ICON, \ 21 | ico_x, ico_y, win32con.LR_LOADFROMFILE) 22 | hdcBitmap = win32gui.CreateCompatibleDC(0) 23 | hdcScreen = win32gui.GetDC(0) 24 | hbm = win32gui.CreateCompatibleBitmap(hdcScreen, ico_x, ico_y) 25 | hbmOld = win32gui.SelectObject(hdcBitmap, hbm) 26 | brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU) 27 | win32gui.FillRect(hdcBitmap, (0, 0, 16, 16), brush) 28 | win32gui.DrawIconEx(hdcBitmap, 0, 0, hicon, ico_x, ico_y, 0, 0, \ 29 | win32con.DI_NORMAL) 30 | win32gui.SelectObject(hdcBitmap, hbmOld) 31 | win32gui.DeleteDC(hdcBitmap) 32 | return hbm 33 | def get(self): 34 | return self.hbm 35 | 36 | exports = [FileIconHandler] -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | import os 3 | import sys 4 | os.chdir(os.path.split(os.path.realpath(sys.argv[0]))[0]) 5 | # The script won't work unless the file is run in a directory with 6 | # extension folders. 7 | import yaml 8 | 9 | from contextdocparser import ContextDocParser 10 | from cascadingmenumaker import CascadingMenuMaker 11 | 12 | condocp = ContextDocParser(open(sys.argv[1],'r'),os.path.realpath(__file__)) 13 | if sys.argv[2] == "install": 14 | condocp.install() 15 | sys.exit(0) 16 | elif sys.argv[2] == "remove": 17 | condocp.remove() 18 | sys.exit(0) 19 | elif sys.argv[2] == "run": 20 | condocp.run() 21 | casman = CascadingMenuMaker(condocp.out,condocp.documentObject) -------------------------------------------------------------------------------- /titles/stringtitle.py: -------------------------------------------------------------------------------- 1 | class StringTitle: 2 | name = 'string' 3 | def __init__(self,handler,string): 4 | self.title = string 5 | def get(self): 6 | return self.title 7 | 8 | exports = [StringTitle] --------------------------------------------------------------------------------