Skip to content

Commit

Permalink
Merge pull request #9 from molsonkiko/nppn_reloadnativelang-proof_of_…
Browse files Browse the repository at this point in the history
…concept

implement NPPN_NATIVELANGCHANGED
  • Loading branch information
molsonkiko authored Aug 29, 2024
2 parents 82edb82 + 148af09 commit c014f29
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 69 deletions.
25 changes: 14 additions & 11 deletions NppCSharpPluginPack/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ class Main
/// </summary>
private static NppListener nppListener = null;
/// <summary>
/// If this is true, <see cref="nppListener"/> will be initialized.<br></br>
/// If the Notepad++ version is higher than 8.6.9, this boolean does not matter.<br></br>
/// If this is true, and the Notepad++ version is 8.6.9 or lower, <see cref="nppListener"/> will be initialized.<br></br>
/// <b>SETTING THIS TO <c>true</c> COMES AT A REAL PERFORMANCE COST <i>EVEN WHEN YOUR PLUGIN IS NOT IN USE</i></b> (possibly up to 10% of all CPU usage associated with Notepad++)<br></br>
/// <i>Do NOT</i> set this to true unless it is very important to you that your plugin's UI language can dynamically adjust to the Notepad++ UI language.<br></br>
/// Note that <b>translation will work even if this is <c>false</c></b>, so the only upside of this is that
Expand All @@ -70,8 +71,6 @@ static internal void CommandMenuInit()
// see https://github.com/oleg-shilo/cs-script.npp/issues/66#issuecomment-1086657272 for more info
AppDomain.CurrentDomain.AssemblyResolve += LoadDependency;

// next load the translations file so we can translate the menu items
Translator.LoadTranslations();
// Initialization of your plugin commands

// with function :
Expand Down Expand Up @@ -116,9 +115,9 @@ static internal void CommandMenuInit()
PluginBase.SetCommand(20, "Open a pop-up dialog", OpenPopupDialog);
PluginBase.SetCommand(21, "---", null);
PluginBase.SetCommand(22, "Allocate indicators demo", AllocateIndicatorsDemo);
// start listening to messages that aren't broadcast by the plugin manager.
if (FOLLOW_NPP_UI_LANGUAGE)
if (FOLLOW_NPP_UI_LANGUAGE && (Npp.nppVersion[0] < 8 || (Npp.nppVersion[0] == 8 && (Npp.nppVersion[1] < 6 || (Npp.nppVersion[1] == 6 && Npp.nppVersion[2] <= 9)))))
{
// start listening to messages that aren't broadcast by the plugin manager (for versions of Notepad++ 8.6.9 or earlier, because later versions have NPPN_NATIVELANGCHANGED)
nppListener = new NppListener();
nppListener.AssignHandle(PluginBase.nppData._nppHandle);
}
Expand Down Expand Up @@ -232,11 +231,15 @@ public static void OnNotification(ScNotification notification)
if (bufferModified == activeFname)
modsSinceBufferOpened++;
break;
//if (code > int.MaxValue) // windows messages
//{
// int wm = -(int)code;
// }
//}
//if (code > int.MaxValue) // windows messages
//{
// int wm = -(int)code;
// }
//}
case (uint)NppMsg.NPPN_READY:
case (uint)NppMsg.NPPN_NATIVELANGCHANGED:
Translator.ResetTranslations();
break;
}
}

Expand All @@ -250,7 +253,7 @@ static internal void PluginCleanUp()
}
isShuttingDown = true;
}
#endregion
#endregion

#region " Menu functions "

Expand Down
32 changes: 27 additions & 5 deletions NppCSharpPluginPack/PluginInfrastructure/Msgs_h.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@ public enum NppMsg : uint
STATUSBAR_UNICODE_TYPE = 4,
STATUSBAR_TYPING_MODE = 5,

NPPM_GETMENUHANDLE = Constants.NPPMSG + 25,
NPPPLUGINMENU = 0,
/// <summary>
/// INT NPPM_GETMENUHANDLE(INT menuChoice, 0)
/// Return: menu handle (HMENU) of choice (plugin menu handle or Notepad++ main menu handle)
/// INT NPPM_GETMENUHANDLE(INT menuChoice, 0)<br></br>
/// Return: menu handle (HMENU) of choice (plugin menu handle (<see cref="NPPPLUGINMENU"/>) or Notepad++ main menu handle (<see cref="NPPMAINMENU"/>))
/// </summary>
NPPM_GETMENUHANDLE = Constants.NPPMSG + 25,
NPPPLUGINMENU = 0,
NPPMAINMENU = 1,

/// <summary>
Expand Down Expand Up @@ -582,6 +582,17 @@ public enum NppMsg : uint
// then indicator ID 7 is preserved by Notepad++, and it is safe to be used by the plugin.
NPPM_ALLOCATEINDICATOR = Constants.NPPMSG + 113,

/// <summary>
/// int NPPM_GETNATIVELANGFILENAME(size_t strLen, char* nativeLangFileName)<br></br>
/// Get the Current native language file name string.<br></br>
/// Users should call it with nativeLangFileName as NULL to get the required number of char (not including the terminating nul character),<br></br>
/// allocate commandLineStr buffer with the return value + 1, then call it again to get the current native language file name string.<br></br>
/// wParam[in]: strLen is "commandLineStr" buffer length<br></br>
/// lParam[out]: commandLineStr recieves all copied native language file name string<br></br>
/// Return the number of char copied/to copy
/// </summary>
NPPM_GETNATIVELANGFILENAME = Constants.NPPMSG + 116,

RUNCOMMAND_USER = Constants.WM_USER + 3000,
NPPM_GETFULLCURRENTPATH = RUNCOMMAND_USER + FULL_CURRENT_PATH,
NPPM_GETCURRENTDIRECTORY = RUNCOMMAND_USER + CURRENT_DIRECTORY,
Expand Down Expand Up @@ -858,10 +869,21 @@ public enum NppMsg : uint
/// <strong>This notification is implemented in Notepad++ v8.6.5.</strong><br></br>
/// scnNotification->nmhdr.code = NPPN_GLOBALMODIFIED;<br></br>
/// scnNotification->nmhdr.hwndFrom = BufferID;<br></br>
/// scnNotification->nmhdr.idFrom = 0; // preserved for the future use, must be zero
/// scnNotifiNATIVELANGCHANGEDcation->nmhdr.idFrom = 0; // preserved for the future use, must be zero
/// </summary>
NPPN_GLOBALMODIFIED = NPPN_FIRST + 30,


///<summary>
/// To notify plugins that the current native language is just changed to another one.<br></br>
/// Use NPPM_GETNATIVELANGFILENAME to get current native language file name.<br></br>
/// Use NPPM_GETMENUHANDLE(NPPPLUGINMENU, 0) to get submenu "Plugins" handle (HMENU)<br></br>
/// scnNotification->nmhdr.code = NPPN_NATIVELANGCHANGED;<br></br>
/// scnNotification->nmhdr.hwndFrom = hwndNpp<br></br>
/// scnNotification->nmhdr.idFrom = 0; // preserved for the future use, must be zero
///</summary>
NPPN_NATIVELANGCHANGED = NPPN_FIRST + 31,

/* --Autogenerated -- end of section automatically generated from notepad-plus-plus\PowerEditor\src\MISC\PluginsManager\Notepad_plus_msgs.h * */
}

Expand Down
27 changes: 27 additions & 0 deletions NppCSharpPluginPack/PluginInfrastructure/NotepadPPGateway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ public interface INotepadPPGateway
/// <returns></returns>
bool AllocateIndicators(int numberOfIndicators, out int[] indicators);

/// <summary>
/// get the English name of the Notepad++ UI language.<br></br>
/// a return value of false indicates that something went wrong and fname should not be used
/// </summary>
bool TryGetNativeLangName(out string langName);

}

/// <summary>
Expand Down Expand Up @@ -267,6 +273,27 @@ public unsafe bool AllocateIndicators(int numberOfIndicators, out int[] indicato
}
}

public unsafe bool TryGetNativeLangName(out string langName)
{
langName = "";
int fnameLen = Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_GETNATIVELANGFILENAME, IntPtr.Zero, IntPtr.Zero).ToInt32() + 1;
if (fnameLen == 1)
return false;
var fnameArr = new byte[fnameLen];
fixed (byte * fnameBuf = fnameArr)
{
IntPtr fnamePtr = (IntPtr)fnameBuf;
Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_GETNATIVELANGFILENAME, (IntPtr)fnameLen, fnamePtr);
langName = Marshal.PtrToStringAnsi(fnamePtr);
}
if (!string.IsNullOrEmpty(langName) && langName.EndsWith(".xml"))
{
langName = langName.Substring(0, langName.Length - 4);
return true;
}
return false;
}

}

/// <summary>
Expand Down
48 changes: 13 additions & 35 deletions NppCSharpPluginPack/PluginInfrastructure/NppPluginNETBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,62 +143,40 @@ private static unsafe bool TryGetSubMenuWithName(IntPtr hMenu, string subMenuNam
private static IntPtr _thisPluginMenuHandle = IntPtr.Zero;

/// <summary>
/// if we can get a valid handle to this plugin's drop-down menu, set thisPluginMenuHandle to that handle and return true.<br></br>
/// Else return false and set thisPluginMenuHandle to IntPtr.Zero.
/// If allPluginsMenuHandle is a valid menu handle, and this plugin's name is the name of one of the submenus of allPluginsMenuHandle,<br></br>
/// set _allPluginsMenuHandle to allPluginsMenuHandle, and set _thisPluginMenuHandle to the handle of the submenu with the same name as this plugin.
/// </summary>
/// <param name="pluginMenuName">the name of the plugins submenu of the Notepad++ main menu. Normally this is "&amp;Plugins;", but it will vary depending on the Notepad++ UI language</param>
/// <param name="thisPluginMenuHandle"></param>
/// <param name="allPluginsMenuHandle">the menu handle to the Plugins submenu of the Notepad++ main menu</param>
/// <returns></returns>
private static unsafe bool TryGetThisPluginMenu(string pluginMenuName, out IntPtr thisPluginMenuHandle)
private static unsafe bool TrySetPluginsMenuHandle(IntPtr allPluginsMenuHandle)
{
thisPluginMenuHandle = IntPtr.Zero;
if (_thisPluginMenuHandle != IntPtr.Zero && _allPluginsMenuHandle != IntPtr.Zero && _thisPluginIdxInAllPluginsMenu >= 0
&& TryGetMenuItemText(_allPluginsMenuHandle, _thisPluginIdxInAllPluginsMenu, out string pluginName)
&& pluginName == Main.PluginName)
{
thisPluginMenuHandle = _thisPluginMenuHandle;
return true;
}
if (!TryGetSubMenuWithName(Win32.GetMenu(nppData._nppHandle), pluginMenuName, out _allPluginsMenuHandle, out _))
if (!TryGetSubMenuWithName(allPluginsMenuHandle, Main.PluginName, out _thisPluginMenuHandle, out _thisPluginIdxInAllPluginsMenu))
return false;
if (!TryGetSubMenuWithName(_allPluginsMenuHandle, Main.PluginName, out _thisPluginMenuHandle, out _thisPluginIdxInAllPluginsMenu))
return false;
thisPluginMenuHandle = _thisPluginMenuHandle;
_allPluginsMenuHandle = allPluginsMenuHandle;
return true;
}

public static bool TryGetThisPluginMenuItemText(string pluginMenuName, int index, out string text)
{
if (!TryGetThisPluginMenu(pluginMenuName, out IntPtr hMenu))
{
text = null;
return false;
}
return TryGetMenuItemText(hMenu, index, out text);
}

/// <summary>
///
/// attempt to change the names of this plugin's menu items to newNames, assuming that allPluginsMenuHandle is the handle of the Plugins submenu of the Notepad++ main menu.<br></br>
/// Returns true if and only if all the menu items could be renamed.
/// </summary>
/// <param name="pluginMenuName">the name of the menu for all plugins. This is normally "&amp;Plugins", but will vary depending on Notepad++ UI language</param>
/// <param name="index"></param>
/// <param name="newText"></param>
/// <param name="allPluginsMenuHandle"></param>
/// <param name="newNames"></param>
/// <returns></returns>
public static bool SetThisPluginMenuItemText(string pluginMenuName, int index, string newText)
{
if (!TryGetThisPluginMenu(pluginMenuName, out IntPtr hMenu))
return false;
return SetMenuItemText(hMenu, index, newText);
}

public static bool ChangePluginMenuItemNames(string pluginMenuName, List<string> newNames)
public static bool ChangePluginMenuItemNames(IntPtr allPluginsMenuHandle, List<string> newNames)
{
if (newNames.Count != _funcItems.Items.Count || !TryGetThisPluginMenu(pluginMenuName, out IntPtr hMenu))
if (newNames.Count != _funcItems.Items.Count || !TrySetPluginsMenuHandle(allPluginsMenuHandle))
return false;
for (int ii = 0; ii < newNames.Count; ii++)
{
string newName = newNames[ii];
if (newName != "---" && !SetMenuItemText(hMenu, ii, newName))
if (newName != "---" && !SetMenuItemText(_thisPluginMenuHandle, ii, newName))
return false;
}
return true;
Expand Down
3 changes: 2 additions & 1 deletion NppCSharpPluginPack/PluginInfrastructure/SettingsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ public virtual void OnSettingsChanged()
public SettingsBase(bool loadFromFile = true)
{
// Set defaults
Translator.LoadTranslations();
foreach (var propertyInfo in GetType().GetProperties())
{
if (propertyInfo.GetCustomAttributes(typeof(DefaultValueAttribute), false).FirstOrDefault() is DefaultValueAttribute def)
Expand Down Expand Up @@ -179,6 +178,8 @@ public void SaveToIniFile(string filename = null)
{
filename = filename ?? IniFilePath;
Npp.CreateConfigSubDirectoryIfNotExists();
if (!Translator.HasTranslations)
Translator.LoadTranslations(false);

// Win32.WritePrivateProfileSection (that NppPlugin uses) doesn't work well with non-ASCII characters. So we roll our own.
using (var fp = new StreamWriter(filename, false, Encoding.UTF8))
Expand Down
4 changes: 2 additions & 2 deletions NppCSharpPluginPack/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@
// Build Number
// Revision
//
[assembly: AssemblyVersion("0.0.3.11")]
[assembly: AssemblyFileVersion("0.0.3.11")]
[assembly: AssemblyVersion("0.0.3.14")]
[assembly: AssemblyFileVersion("0.0.3.14")]
Binary file modified NppCSharpPluginPack/Release_x64.zip
Binary file not shown.
Binary file modified NppCSharpPluginPack/Release_x86.zip
Binary file not shown.
40 changes: 28 additions & 12 deletions NppCSharpPluginPack/Utils/Translator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,26 +278,42 @@ public static string GetTranslatedMenuItem(string menuItem)
/// When the UI language of Notepad++ changes, attempt to translate JsonTools to the Notepad++ UI language automatically.<br></br>
/// If translation fails, or if the current UI language is english, restore the language of JsonTools to the default english.
/// </summary>
public static bool ResetTranslations()
/// <returns>true if and only if translation was successful AND the plugin was translated to a language other than English</returns>
public static bool ResetTranslations(string langName = "")
{
if (!string.IsNullOrEmpty(langName) || Npp.notepad.TryGetNativeLangName(out langName))
{
if (!TryGetTranslationFileName(langName, out _))
return ResetTranslationsHelper(true);
if (languageName == langName)
return false; // we're already in that language
languageName = langName;
return ResetTranslationsHelper(langName == DEFAULT_LANG);
}
string oldLanguageName = languageName;
languageName = DEFAULT_LANG;
if (!TryGetNppNativeLangXmlText(out string nativeLangXml))
return ResetTranslationsHelper(true);
Match langNameMtch = Regex.Match(nativeLangXml, "<Native-Langue .*? filename=\"(.*?)\\.xml\"");
string newLanguageName = "";
if (langNameMtch.Success)
languageName = langNameMtch.Groups[1].Value.Trim().ToLower();
if (languageName == DEFAULT_LANG)
return ResetTranslationsHelper(true);
// find the name of the plugins menu, so that we can find the submenu for our plugin
Match pluginsItemNameMtch = Regex.Match(nativeLangXml, "<Item menuId=\"Plugins\" name=\"([^\"]+)\"");
if (!pluginsItemNameMtch.Success)
return ResetTranslationsHelper(true);
string pluginMenuName = pluginsItemNameMtch.Groups[1].Value.Replace("&amp;", "&");
return ResetTranslationsHelper(false, pluginMenuName);
newLanguageName = langNameMtch.Groups[1].Value.Trim().ToLower();
if (newLanguageName == oldLanguageName)
return false; // unchanged, don't waste time
languageName = newLanguageName;
return ResetTranslationsHelper(languageName == DEFAULT_LANG);
}

private static bool ResetTranslationsHelper(bool restoreToEnglish, string pluginMenuName = DEFAULT_PLUGINS_MENU_NAME)
/// <summary>
///
/// </summary>
/// <param name="restoreToEnglish"></param>
/// <returns>true if and only if translation was successful AND the plugin was translated to a language other than English</returns>
private static bool ResetTranslationsHelper(bool restoreToEnglish)
{
IntPtr allPluginsMenuHandle = Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_GETMENUHANDLE, (int)NppMsg.NPPPLUGINMENU, 0);
if (allPluginsMenuHandle == IntPtr.Zero)
return false;
bool result = true;
List<string> newMenuItemNames = PluginBase.GetUntranslatedFuncItemNames();
if (restoreToEnglish)
Expand All @@ -312,7 +328,7 @@ private static bool ResetTranslationsHelper(bool restoreToEnglish, string plugin
LoadTranslations(false, languageName);
newMenuItemNames = newMenuItemNames.Select(x => GetTranslatedMenuItem(x)).ToList();
}
PluginBase.ChangePluginMenuItemNames(pluginMenuName, newMenuItemNames);
PluginBase.ChangePluginMenuItemNames(allPluginsMenuHandle, newMenuItemNames);
if (Main.selectionRememberingForm != null && !Main.selectionRememberingForm.IsDisposed)
{
TranslateForm(Main.selectionRememberingForm);
Expand Down
6 changes: 3 additions & 3 deletions most recent errors.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Test results for CSharpPluginPack v0.0.3.11 on Notepad++ 8.6.9 64bit
Test results for CSharpPluginPack v0.0.3.14 on Notepad++ 8.6.9 64bit
NOTE: Ctrl-F (regular expressions *on*) for "Failed [1-9]\d*" to find all failed tests
No tests failed
=========================
Expand All @@ -21,8 +21,8 @@ Testing Performance of something
Performance tests for My benchmarks (test1)
=========================

To run query "foo" on file of size 7913 into took 0 +/- 0.002 ms over 32 trials
Query times (ms): 0.011, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
To run query "foo" on file of size 7913 into took 0 +/- 0.001 ms over 32 trials
Query times (ms): 0.005, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
Preview of result: Preview of result
=========================
Performance tests for My benchmarks (test2)
Expand Down

0 comments on commit c014f29

Please sign in to comment.