Skip to content

Commit d771a89

Browse files
committed
MenuManager reconfiguring dynamic menus
- Dynamic menu items rebuilt after Config save - Scripts menu rebuilt after save a file in either scripts directory
1 parent 096bfde commit d771a89

7 files changed

Lines changed: 213 additions & 33 deletions

File tree

PythonScript/src/ConfigFile.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ ConfigFile* ConfigFile::create(const TCHAR *configDir, const TCHAR *pluginDir, H
1818
ConfigFile::ConfigFile(const TCHAR *configDir, const TCHAR *pluginDir, HINSTANCE hInst)
1919
: m_configFilename(configDir),
2020
m_pluginDir(pluginDir),
21-
m_hInst (hInst)
21+
m_hInst (hInst),
22+
m_machineScriptsDir(pluginDir),
23+
m_userScriptsDir(configDir)
2224
{
2325
m_configFilename.append(_T("\\PythonScriptStartup.cnf"));
26+
27+
m_machineScriptsDir.append(_T("\\PythonScript\\scripts"));
28+
m_userScriptsDir.append(_T("\\PythonScript\\scripts"));
2429

2530
readConfig();
2631
}

PythonScript/src/ConfigFile.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class ConfigFile
3232

3333
void refresh() { clearItems(); readConfig(); };
3434

35+
36+
const tstring& getMachineScriptsDir() { return m_machineScriptsDir; };
37+
const tstring& getUserScriptsDir() { return m_userScriptsDir; };
38+
39+
3540
protected:
3641
explicit ConfigFile(const TCHAR *configDir);
3742
virtual void readConfig();
@@ -43,6 +48,9 @@ class ConfigFile
4348
tstring m_configFilename;
4449
tstring m_pluginDir;
4550

51+
tstring m_machineScriptsDir;
52+
tstring m_userScriptsDir;
53+
4654
MenuItemsTD m_menuItems;
4755
std::vector< std::string > m_menuScripts;
4856

PythonScript/src/MenuManager.cpp

Lines changed: 150 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ int MenuManager::s_startCommandID;
1717
int MenuManager::s_endCommandID;
1818
int MenuManager::s_startFixedID;
1919
int MenuManager::s_endFixedID;
20+
int MenuManager::s_startDynamicEntryID;
21+
int MenuManager::s_endDynamicEntryID;
2022
bool MenuManager::s_menuItemClicked;
2123

2224
void (*MenuManager::s_runScript)(int);
@@ -55,7 +57,11 @@ MenuManager::MenuManager(HWND hNotepad, HINSTANCE hInst, void(*runScript)(const
5557
m_hNotepad (hNotepad),
5658
m_runScript (runScript),
5759
m_pythonPluginMenu (NULL)
60+
5861
{
62+
s_startDynamicEntryID = 1;
63+
s_endDynamicEntryID = 0;
64+
5965
m_runScriptFuncs[0] = runScript0;
6066
m_runScriptFuncs[1] = runScript1;
6167
m_runScriptFuncs[2] = runScript2;
@@ -114,24 +120,26 @@ MenuManager::MenuManager(HWND hNotepad, HINSTANCE hInst, void(*runScript)(const
114120
/* This code was shamefully robbed from NppExec from Dovgan Vitaliy*/
115121
HMENU MenuManager::getOurMenu()
116122
{
117-
118-
HMENU hPluginMenu = (HMENU)::SendMessage(m_hNotepad, NPPM_GETMENUHANDLE, 0, 0);
119-
HMENU hPythonMenu = NULL;
120-
int iMenuItems = GetMenuItemCount(hPluginMenu);
121-
for ( int i = 0; i < iMenuItems; i++ )
123+
if (NULL == m_pythonPluginMenu)
122124
{
123-
HMENU hSubMenu = ::GetSubMenu(hPluginMenu, i);
124-
// does our About menu command exist here?
125-
if ( ::GetMenuState(hSubMenu, m_funcItems[0]._cmdID, MF_BYCOMMAND) != -1 )
125+
HMENU hPluginMenu = (HMENU)::SendMessage(m_hNotepad, NPPM_GETMENUHANDLE, 0, 0);
126+
HMENU hPythonMenu = NULL;
127+
int iMenuItems = GetMenuItemCount(hPluginMenu);
128+
for ( int i = 0; i < iMenuItems; i++ )
126129
{
127-
// this is our "Python Script" sub-menu
128-
hPythonMenu = hSubMenu;
129-
break;
130+
HMENU hSubMenu = ::GetSubMenu(hPluginMenu, i);
131+
// does our About menu command exist here?
132+
if ( ::GetMenuState(hSubMenu, m_funcItems[0]._cmdID, MF_BYCOMMAND) != -1 )
133+
{
134+
// this is our "Python Script" sub-menu
135+
m_pythonPluginMenu = hSubMenu;
136+
break;
137+
}
130138
}
139+
131140
}
132141

133-
134-
return hPythonMenu;
142+
return m_pythonPluginMenu;
135143
}
136144

137145
void MenuManager::stopScriptEnabled(bool enabled)
@@ -155,32 +163,35 @@ bool MenuManager::populateScriptsMenu()
155163
else
156164
{
157165

158-
HMENU hScriptsMenu = CreateMenu();
166+
m_hScriptsMenu = CreateMenu();
159167
//funcItem[g_aboutFuncIndex]._cmdID + 1000
160168
s_startCommandID = m_funcItems[0]._cmdID + ADD_CMD_ID;
161169

162-
InsertMenu(m_pythonPluginMenu, m_scriptsMenuIndex, MF_BYPOSITION | MF_POPUP, reinterpret_cast<UINT_PTR>(hScriptsMenu), _T("Scripts"));
163-
m_submenus.insert(pair<string, HMENU>("\\", hScriptsMenu));
170+
InsertMenu(m_pythonPluginMenu, m_scriptsMenuIndex, MF_BYPOSITION | MF_POPUP, reinterpret_cast<UINT_PTR>(m_hScriptsMenu), _T("Scripts"));
171+
m_submenus.insert(pair<string, HMENU>("\\", m_hScriptsMenu));
164172

165173
TCHAR pluginDir[MAX_PATH];
166174
TCHAR configDir[MAX_PATH];
167175
::SendMessage(m_hNotepad, NPPM_GETNPPDIRECTORY, MAX_PATH, reinterpret_cast<LPARAM>(pluginDir));
168176
::SendMessage(m_hNotepad, NPPM_GETPLUGINSCONFIGDIR, MAX_PATH, reinterpret_cast<LPARAM>(configDir));
169177
shared_ptr<char> path = WcharMbcsConverter::tchar2char(pluginDir);
170-
string machineScriptsPath(path.get());
171-
machineScriptsPath.append("\\plugins\\PythonScript\\scripts");
178+
m_machineScriptsPath = path.get();
179+
m_machineScriptsPath.append("\\plugins\\PythonScript\\scripts");
172180

173181
path = WcharMbcsConverter::tchar2char(configDir);
174-
string userScriptsPath(path.get());
175-
userScriptsPath.append("\\PythonScript\\scripts");
176-
177-
int nextID = findScripts(hScriptsMenu, machineScriptsPath.size(), s_startCommandID, machineScriptsPath);
178-
179-
s_endCommandID = findScripts(hScriptsMenu, userScriptsPath.size(), nextID, userScriptsPath);
182+
m_userScriptsPath = path.get();
183+
m_userScriptsPath.append("\\PythonScript\\scripts");
180184

185+
181186
// Assume here that the func items are assigned from start to finish
182187
s_startFixedID = m_funcItems[m_dynamicStartIndex]._cmdID;
183-
s_endFixedID = m_funcItems[m_funcItemCount - 1]._cmdID;
188+
s_endFixedID = m_funcItems[m_funcItemCount - 1]._cmdID;
189+
190+
// Fill the actual menu
191+
refreshScriptsMenu();
192+
193+
// Dynamic scripts will start at one lower index now we've inserted the Scripts submenu
194+
++m_dynamicStartIndex;
184195

185196
subclassNotepadPlusPlus();
186197

@@ -189,6 +200,35 @@ bool MenuManager::populateScriptsMenu()
189200
return true;
190201
}
191202

203+
// Fills the Scripts menu
204+
void MenuManager::refreshScriptsMenu()
205+
{
206+
// This will try to delete all scripts menu items
207+
// - scripts in sub directories will be unsuccessful,
208+
// but less trouble (read: CPU) than trying to work out which ones we should delete (ie. root dir)
209+
for(ScriptCommandsTD::iterator it = m_scriptCommands.begin(); it != m_scriptCommands.end(); ++it)
210+
{
211+
DeleteMenu(m_hScriptsMenu, it->first, MF_BYCOMMAND);
212+
}
213+
214+
m_scriptCommands.erase(m_scriptCommands.begin(), m_scriptCommands.end());
215+
m_machineScriptNames.erase(m_machineScriptNames.begin(), m_machineScriptNames.end());
216+
for(SubmenusTD::iterator it = m_submenus.begin(); it != m_submenus.end(); ++it)
217+
{
218+
if (it->first != "\\")
219+
{
220+
DestroyMenu(it->second);
221+
}
222+
}
223+
224+
int nextID = findScripts(m_hScriptsMenu, m_machineScriptsPath.size(), s_startCommandID, m_machineScriptsPath);
225+
226+
s_endCommandID = findScripts(m_hScriptsMenu, m_userScriptsPath.size(), nextID, m_userScriptsPath);
227+
228+
229+
}
230+
231+
192232
int MenuManager::findScripts(HMENU hBaseMenu, int basePathLength, int startID, string& path)
193233
{
194234
WIN32_FIND_DATAA findData;
@@ -315,6 +355,12 @@ LRESULT CALLBACK notepadWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l
315355
{
316356
MenuManager::s_menuItemClicked = true;
317357
}
358+
else if (LOWORD(wParam) >= MenuManager::s_startDynamicEntryID && LOWORD(wParam) < MenuManager::s_endDynamicEntryID && HIWORD(wParam) == 0)
359+
{
360+
MenuManager::s_menuItemClicked = true;
361+
MenuManager::getInstance()->menuCommand(LOWORD(wParam));
362+
return TRUE;
363+
}
318364

319365
}
320366

@@ -396,9 +442,88 @@ FuncItem* MenuManager::getFuncItemArray(int *nbF, ItemVectorTD items, void (*run
396442

397443
m_dynamicStartIndex = dynamicStartIndex;
398444
m_dynamicCount = menuItems.size();
445+
m_originalDynamicCount = m_dynamicCount;
399446
m_scriptsMenuIndex = scriptsMenuIndex;
400447
m_stopScriptIndex = stopScriptIndex;
401448

402449
return m_funcItems;
403450

451+
}
452+
453+
454+
// Reconfigure the dynamic menus from the config
455+
void MenuManager::reconfigure()
456+
{
457+
ConfigFile *configFile = ConfigFile::getInstance();
458+
ConfigFile::MenuItemsTD menuItems = configFile->getMenuItems();
459+
460+
461+
TCHAR buffer[MAX_PATH];
462+
TCHAR *filename;
463+
464+
// Remove the current list of script commands
465+
m_scriptCommands.clear();
466+
467+
HMENU hPluginMenu = getOurMenu();
468+
int dynamicEntryID = m_funcItems[0]._cmdID + DYNAMIC_ADD_ID;
469+
s_startDynamicEntryID = dynamicEntryID;
470+
// Remove the current "extra" entries - ie. entries after the original list in funcItems
471+
for(int position = m_originalDynamicCount; position < m_dynamicCount; ++position)
472+
{
473+
::DeleteMenu(hPluginMenu, m_dynamicStartIndex + position, MF_BYPOSITION);
474+
}
475+
476+
int position = 0;
477+
478+
for(ConfigFile::MenuItemsTD::iterator it = menuItems.begin(); it != menuItems.end(); ++it)
479+
{
480+
_tcscpy_s<MAX_PATH>(buffer, (*it).c_str());
481+
482+
filename = PathFindFileName(buffer);
483+
PathRemoveExtension(filename);
484+
485+
// If it's less than the original funcItem count given
486+
// back from getFuncItems
487+
if (position < m_originalDynamicCount)
488+
{
489+
// If we're currently passed the number of CURRENT
490+
// dynamic entries, then we need to create the HMENU item again
491+
if (position >= m_dynamicCount)
492+
{
493+
// put a menu item back with the same ID
494+
// (N++ will believe this to be genuine :)
495+
496+
// scripts sub menu didn't exist when dynamicStartIndex was set, hence the -1
497+
::InsertMenu(hPluginMenu, position + m_dynamicStartIndex, MF_BYPOSITION, m_funcItems[m_dynamicStartIndex + position - 1]._cmdID, filename);
498+
}
499+
else
500+
{
501+
// Update the existing menu
502+
// scripts sub menu didn't exist when dynamicStartIndex was set, hence the -1
503+
::ModifyMenu(hPluginMenu, position + m_dynamicStartIndex, MF_BYPOSITION, m_funcItems[m_dynamicStartIndex + position - 1]._cmdID, filename);
504+
}
505+
506+
m_scriptCommands.insert(pair<int, string>(m_funcItems[position]._cmdID, string(WcharMbcsConverter::tchar2char(it->c_str()).get())));
507+
}
508+
else // position >= m_funcItemCount, so just add a new one
509+
{
510+
::InsertMenu(hPluginMenu, position + m_dynamicStartIndex, MF_BYPOSITION, dynamicEntryID, filename);
511+
m_scriptCommands.insert(pair<int, string>(dynamicEntryID, string(WcharMbcsConverter::tchar2char(it->c_str()).get())));
512+
++dynamicEntryID;
513+
}
514+
515+
++position;
516+
}
517+
518+
// Delete the extra menus
519+
if (menuItems.size() < static_cast<size_t>(m_dynamicCount))
520+
{
521+
for(int currentCount = position; currentCount < m_dynamicCount; ++currentCount)
522+
{
523+
::DeleteMenu(hPluginMenu, position + m_dynamicStartIndex, MF_BYPOSITION);
524+
}
525+
}
526+
527+
m_dynamicCount = menuItems.size();
528+
s_endDynamicEntryID = dynamicEntryID;
404529
}

PythonScript/src/MenuManager.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22

33
#include "PythonScript.h"
44

5-
#define ADD_CMD_ID 1750
5+
6+
// The DYNAMIC_ADD_ID is used for the CommandIDs for dynamic menu entries added between restarts
7+
// It is added to the ID allocated to the first /real/ menu item (ie. probably "New Script")
8+
// Therefore, it should be a minimum of 50 below the ADD_CMD_ID, as there can only be 50
9+
// dynamic entries.
10+
#define DYNAMIC_ADD_ID 1700
11+
12+
// Added to first real menu item command ID for the dynamic Scripts sub menu
13+
#define ADD_CMD_ID 1750
614

715
struct FuncItem;
816

@@ -27,10 +35,17 @@ class MenuManager
2735
bool populateScriptsMenu();
2836
void menuCommand(int commandID);
2937

38+
void reconfigure();
39+
40+
void refreshScriptsMenu();
41+
3042
static int s_startCommandID;
3143
static int s_endCommandID;
3244
static int s_startFixedID;
3345
static int s_endFixedID;
46+
static int s_startDynamicEntryID;
47+
static int s_endDynamicEntryID;
48+
3449
static bool s_menuItemClicked;
3550
static WNDPROC s_origWndProc;
3651

@@ -47,22 +62,29 @@ class MenuManager
4762

4863
void (*m_runScript)(const char*);
4964

65+
typedef std::set<std::string> MachineScriptNamesTD;
66+
typedef std::map<int, std::string> ScriptCommandsTD;
67+
typedef std::map<std::string, HMENU> SubmenusTD;
5068

51-
std::set<std::string> m_machineScriptNames;
52-
std::map<int, std::string> m_scriptCommands;
53-
std::map<std::string, HMENU> m_submenus;
69+
MachineScriptNamesTD m_machineScriptNames;
70+
ScriptCommandsTD m_scriptCommands;
71+
SubmenusTD m_submenus;
5472

5573
static MenuManager* s_menuManager;
5674

5775
HWND m_hNotepad;
5876
HINSTANCE m_hInst;
5977
int m_dynamicStartIndex;
6078
int m_dynamicCount;
79+
int m_originalDynamicCount;
6180
int m_scriptsMenuIndex;
6281
int m_stopScriptIndex;
6382
HMENU m_pythonPluginMenu;
83+
HMENU m_hScriptsMenu;
6484
FuncItem* m_funcItems;
6585
int m_funcItemCount;
86+
std::string m_machineScriptsPath;
87+
std::string m_userScriptsPath;
6688

6789
// Function pointer to the real run script function
6890
static void (*s_runScript)(int);

PythonScript/src/PythonScript.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,22 @@ extern "C" __declspec(dllexport) void beNotified(SCNotification *notifyCode)
253253
initialise();
254254
}
255255
break;
256+
257+
case NPPN_FILESAVED:
258+
{
259+
TCHAR filename[MAX_PATH];
260+
::SendMessage(nppData._nppHandle, NPPM_GETFULLPATHFROMBUFFERID, notifyCode->nmhdr.idFrom, reinterpret_cast<LPARAM>(filename));
261+
ConfigFile *configFile = ConfigFile::getInstance();
262+
const tstring machineScripts = configFile->getMachineScriptsDir().c_str();
263+
const tstring userScripts = configFile->getUserScriptsDir().c_str();
264+
265+
if (_tcsnicmp(filename, machineScripts.c_str(), machineScripts.size()) == 0
266+
|| _tcsnicmp(filename, userScripts.c_str(), userScripts.size()) == 0)
267+
{
268+
MenuManager::getInstance()->refreshScriptsMenu();
269+
}
270+
}
271+
break;
256272
}
257273

258274
// Notify the scripts

PythonScript/src/PythonScript.rc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ BEGIN
8383
GROUPBOX "Scripts",IDC_STATIC,7,7,362,136
8484
CONTROL "Machine Scripts",IDC_RADMACHINE,"Button",BS_AUTORADIOBUTTON,19,18,65,10
8585
CONTROL "User Scripts",IDC_RADUSER,"Button",BS_AUTORADIOBUTTON,94,18,54,10
86-
LISTBOX IDC_MENUITEMLIST,7,172,171,106,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP,WS_EX_RIGHT
86+
LISTBOX IDC_MENUITEMLIST,7,172,171,106,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP,WS_EX_RIGHT
8787
PUSHBUTTON "Add",IDC_MENUADD,21,146,50,14
8888
PUSHBUTTON "Remove",IDC_MENUREMOVE,89,146,50,14
89-
LISTBOX IDC_TOOLBARITEMLIST,184,172,176,105,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP,WS_EX_RIGHT
89+
LISTBOX IDC_TOOLBARITEMLIST,184,172,176,105,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP,WS_EX_RIGHT
9090
PUSHBUTTON "Add",IDC_TOOLBARADD,219,145,50,14
9191
PUSHBUTTON "Remove",IDC_TOOLBARREMOVE,281,146,50,14
9292
LTEXT "Menu items",IDC_STATIC,10,162,37,8

0 commit comments

Comments
 (0)