Compare commits

..

No commits in common. 'master' and 'lba_new' have entirely different histories.

Binary file not shown.

Binary file not shown.

@ -1560,17 +1560,13 @@ namespace NppDarkMode
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
//窗口子类化的示例,它为指定的窗口处理了自定义的边框绘制逻辑。
void subclassTabControl(HWND hwnd)
{
SetWindowSubclass(hwnd, TabSubclass, g_tabSubclassID, 0); //将窗口 hwnd 子类化为 TabSubclass并分配一个唯一的子类 ID g_tabSubclassID。
SetWindowSubclass(hwnd, TabSubclass, g_tabSubclassID, 0);
}
constexpr UINT_PTR g_customBorderSubclassID = 42;
//这个函数是一个回调函数,用于处理窗口消息。它被用作窗口子类的回调函数。
//当窗口接收到消息时,该函数会被调用来处理消息。
//注意,该函数的签名必须与 SUBCLASSPROC 类型匹配。
LRESULT CALLBACK CustomBorderSubclass(
HWND hWnd,
UINT uMsg,
@ -1584,9 +1580,6 @@ namespace NppDarkMode
static bool isHotStatic = false;
//在这个函数中,根据不同的消息类型进行不同的处理。
//其中WM_NCPAINT 消息用于非客户区绘制,即窗口的边框绘制。
//在这里,通过重绘窗口的边框,实现了自定义的边框绘制效果。
switch (uMsg)
{
case WM_NCPAINT:
@ -1642,25 +1635,19 @@ namespace NppDarkMode
return 0;
}
break;
//以上部分代码使用 GetWindowDC 函数获取窗口的设备上下文,然后根据窗口的样式和状态计算绘制边框所需的相关信息。
//通过调用 NppDarkMode::paintRoundFrameRect 函数来绘制圆角矩形边框,使用不同的画笔来绘制不同的边框样式。
//绘制完成后,通过 ReleaseDC 函数释放设备上下文。最后,返回值为 0表示消息已经被处理完毕。
case WM_NCCALCSIZE:
{
if (!NppDarkMode::isEnabled()) //检查是否启用了 NppDarkMode如果没有启用则直接跳过。
if (!NppDarkMode::isEnabled())
{
break;
}
auto lpRect = reinterpret_cast<LPRECT>(lParam); //通过将 lParam 强制转换为 LPRECT 类型,获取指向窗口矩形结构的指针。
::InflateRect(lpRect, -(::GetSystemMetrics(SM_CXEDGE)), -(::GetSystemMetrics(SM_CYEDGE))); //利用 InflateRect 函数扩展客户区矩形的尺寸,将其减去窗口边框的宽度和高度(使用 SM_CXEDGE 和 SM_CYEDGE 系统度量值)。这样可以得到去除边框后的客户区矩形。
auto lpRect = reinterpret_cast<LPRECT>(lParam);
::InflateRect(lpRect, -(::GetSystemMetrics(SM_CXEDGE)), -(::GetSystemMetrics(SM_CYEDGE)));
auto style = ::GetWindowLongPtr(hWnd, GWL_STYLE);
bool hasVerScrollbar = (style & WS_VSCROLL) == WS_VSCROLL;
//根据窗口的样式,检查是否有垂直和水平滚动条。如果有垂直滚动条,则将客户区矩形的右边减去垂直滚动条的宽度(使用 SM_CXVSCROLL 系统度量值)。
//如果有水平滚动条,则将客户区矩形的底部减去水平滚动条的高度(使用 SM_CYHSCROLL 系统度量值)。
if (hasVerScrollbar)
{
lpRect->right -= ::GetSystemMetrics(SM_CXVSCROLL);
@ -1672,14 +1659,10 @@ namespace NppDarkMode
lpRect->bottom -= ::GetSystemMetrics(SM_CYHSCROLL);
}
return 0; //返回值为 0表示已经处理完 WM_NCCALCSIZE 消息。
return 0;
}
break;
//在这个代码块中,首先检查是否启用了 NppDarkMode如果没有启用则直接跳过。
//然后,通过调用 GetFocus 函数检查窗口是否具有焦点,如果有焦点则跳过。
//接下来,创建一个 TRACKMOUSEEVENT 结构体,并设置其中的成员变量。这个结构体用于跟踪鼠标离开窗口的消息。通过调用 TrackMouseEvent 函数,将该结构体传递给系统,以便在鼠标离开窗口时收到 WM_MOUSELEAVE 消息。
//然后,检查 isHotStatic 变量的值。如果它为假,则将其设置为真,并通过调用 SetWindowPos 函数更新窗口的位置和大小。这里使用了 SWP_NOMOVE、SWP_NOSIZE 和 SWP_NOZORDER 标志,表示不改变窗口的位置、大小和层次关系,只刷新窗口的边框样式(使用 SWP_FRAMECHANGED 标志)。
case WM_MOUSEMOVE:
{
if (!NppDarkMode::isEnabled())
@ -1707,10 +1690,6 @@ namespace NppDarkMode
}
break;
//在这个代码块中,首先检查是否启用了 NppDarkMode如果没有启用则直接跳过。
//然后,检查 isHotStatic 变量的值。如果它为真,则将其设置为假,并通过调用 SetWindowPos 函数更新窗口的位置和大小。
//接下来,同样创建一个 TRACKMOUSEEVENT 结构体,并设置其中的成员变量。
//然后,通过调用 TrackMouseEvent 函数,取消对鼠标离开窗口的跟踪,并清除鼠标悬停时间。
case WM_MOUSELEAVE:
{
if (!NppDarkMode::isEnabled())
@ -1735,21 +1714,20 @@ namespace NppDarkMode
case WM_NCDESTROY:
{
RemoveWindowSubclass(hWnd, CustomBorderSubclass, uIdSubclass); //调用 RemoveWindowSubclass 函数,将窗口子类移除,以确保在窗口销毁时不再收到自定义边框的消息。
RemoveWindowSubclass(hWnd, CustomBorderSubclass, uIdSubclass);
}
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam); //通过调用 DefSubclassProc 函数,将消息传递给默认的窗口过程进行处理。
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
void subclassCustomBorderForListBoxAndEditControls(HWND hwnd) //hwnd为要进行子类化的窗口句柄。
void subclassCustomBorderForListBoxAndEditControls(HWND hwnd)
{
SetWindowSubclass(hwnd, CustomBorderSubclass, g_customBorderSubclassID, 0);
}
constexpr UINT_PTR g_comboBoxSubclassID = 42; //标识子类化的ID
constexpr UINT_PTR g_comboBoxSubclassID = 42;
//子类函数 ComboBoxSubclass它是一个回调函数用于处理窗口消息。
LRESULT CALLBACK ComboBoxSubclass(
HWND hWnd,
UINT uMsg,
@ -1758,7 +1736,6 @@ namespace NppDarkMode
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData
)
{
auto hwndEdit = reinterpret_cast<HWND>(dwRefData);
@ -1770,32 +1747,25 @@ namespace NppDarkMode
{
break;
}
//定义一个名为 rc 的 RECT 结构体,并使用 ::GetClientRect 函数获取窗口客户区的矩形区域信息。
RECT rc{};
::GetClientRect(hWnd, &rc);
//定义一个名为 ps 的 PAINTSTRUCT 结构体,并调用 ::BeginPaint 函数开始绘制操作,将得到的设备上下文句柄保存在 hdc 变量中。
PAINTSTRUCT ps{};
auto hdc = ::BeginPaint(hWnd, &ps);
//选择关联字体
::SelectObject(hdc, reinterpret_cast<HFONT>(::SendMessage(hWnd, WM_GETFONT, 0, 0)));
::SetBkColor(hdc, NppDarkMode::getBackgroundColor());
auto holdBrush = ::SelectObject(hdc, NppDarkMode::getDarkerBackgroundBrush());
auto& dpiManager = NppParameters::getInstance()._dpiManager;
//定义一个名为 rcArrow 的 RECT 结构体变量,用于存储组合框箭头的位置和尺寸信息。
RECT rcArrow{};
//定义一个名为 cbi 的 COMBOBOXINFO 结构体,并设置其 cbSize 成员为结构体的大小。
//然后通过调用 ::GetComboBoxInfo 函数获取与指定组合框窗口相关的信息,并将结果存储在 cbi 变量中。
COMBOBOXINFO cbi{};
cbi.cbSize = sizeof(COMBOBOXINFO);
const bool resultCbi = ::GetComboBoxInfo(hWnd, &cbi) != FALSE;
//根据获取到的组合框信息或者客户区的尺寸,动态地确定箭头区域的位置和尺寸
if (resultCbi)
{
rcArrow = cbi.rcButton;
@ -1809,16 +1779,12 @@ namespace NppDarkMode
};
}
bool hasFocus = false; //定义了一个 hasFocus 变量,并且获取了窗口是否启用的状态。
bool hasFocus = false;
const bool isWindowEnabled = ::IsWindowEnabled(hWnd) == TRUE;
// CBS_DROPDOWN text is handled by parent by WM_CTLCOLOREDIT
auto style = ::GetWindowLongPtr(hWnd, GWL_STYLE);
//如果组合框是下拉列表框CBS_DROPDOWNLIST则根据获取到的组合框信息或者客户区的尺寸确定文本背景的矩形区域 rcTextBg并使用 FillRect 函数擦除文本背景。
//然后根据当前选中的项来设置文本颜色和背景颜色,并绘制文本到指定的位置上。
//最后,如果组合框有焦点且未展开下拉列表,则使用 DrawFocusRect 函数绘制焦点矩形框。
if ((style & CBS_DROPDOWNLIST) == CBS_DROPDOWNLIST)
{
hasFocus = ::GetFocus() == hWnd;
@ -1862,8 +1828,6 @@ namespace NppDarkMode
::DrawFocusRect(hdc, &rcTextBg);
}
}
//如果组合框是可编辑的下拉框CBS_DROPDOWN则判断编辑框是否拥有焦点并根据情况设置 hasFocus 变量的值。
else if ((style & CBS_DROPDOWN) == CBS_DROPDOWN && hwndEdit != nullptr)
{
hasFocus = ::GetFocus() == hwndEdit;
@ -1888,10 +1852,10 @@ namespace NppDarkMode
auto holdPen = static_cast<HPEN>(::SelectObject(hdc, hSelectedPen));
POINT edge[] = {
{rcArrow.left - 1, rcArrow.top}, //箭头区域左侧顶部
{rcArrow.left - 1, rcArrow.bottom} //箭头区域左侧底部
{rcArrow.left - 1, rcArrow.top},
{rcArrow.left - 1, rcArrow.bottom}
};
::Polyline(hdc, edge, _countof(edge)); //绘制连线(边框线)
::Polyline(hdc, edge, _countof(edge));
int roundCornerValue = NppDarkMode::isWindows11() ? dpiManager.scaleX(4) : 0;
NppDarkMode::paintRoundFrameRect(hdc, rc, hSelectedPen, roundCornerValue, roundCornerValue);
@ -1911,10 +1875,7 @@ namespace NppDarkMode
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
// 以上代码用于自定义列表框和编辑控件的边框和外观,通过调用 SetWindowSubclass 函数将子类函数与窗口句柄关联起来,实现自定义的绘制效果。
//给组合框控件设置子类化
void subclassComboBoxControl(HWND hwnd)
{
DWORD_PTR hwndEditData = 0;

@ -20,11 +20,11 @@
#include "Common.h" // for generic_string
namespace NppDarkMode //命名空间NppDarkMode
namespace NppDarkMode
{
struct Colors //Colors颜色结构体
struct Colors
{
COLORREF background = 0; //COLORREF 是一个 32-bit 整型数值,它代表了一种颜色。
COLORREF background = 0;
COLORREF softerBackground = 0;
COLORREF hotBackground = 0;
COLORREF pureBackground = 0;
@ -38,30 +38,30 @@ namespace NppDarkMode //
COLORREF disabledEdge = 0;
};
struct Options //Options选项结构体
struct Options
{
bool enable = false;
bool enableMenubar = false;
bool enablePlugin = false;
};
struct NppDarkModeParams //params参数结构体
struct NppDarkModeParams
{
const wchar_t* _themeClassName = nullptr;
bool _subclass = false;
bool _theme = false;
};
enum class ToolTipsType //域内枚举类型为ToolTipsType
enum class ToolTipsType
{
tooltip, //括号里是常量
tooltip,
toolbar,
listview,
treeview,
tabbar
};
enum ColorTone { //色温
enum ColorTone {
blackTone = 0,
redTone = 1,
greenTone = 2,
@ -72,14 +72,14 @@ namespace NppDarkMode //
customizedTone = 32
};
enum class TreeViewStyle //风格
enum class TreeViewStyle
{
classic = 0,
light = 1,
dark = 2
};
struct AdvOptDefaults //默认选项
struct AdvOptDefaults
{
generic_string _xmlFileName;
int _toolBarIconSet = -1;
@ -87,7 +87,7 @@ namespace NppDarkMode //
bool _tabUseTheme = false;
};
struct AdvancedOptions //高级选项
struct AdvancedOptions
{
bool _enableWindowsMode = false;
@ -108,7 +108,7 @@ namespace NppDarkMode //
bool isWindowsModeEnabled();
void setWindowsMode(bool enable);
generic_string getThemeName(); //generic_string 类
generic_string getThemeName();
void setThemeName(const generic_string& newThemeName);
int getToolBarIconSet(bool useDark);
void setToolBarIconSet(int state2Set, bool useDark);
@ -119,7 +119,7 @@ namespace NppDarkMode //
bool isWindows10();
bool isWindows11();
DWORD getWindowsBuildNumber(); //DWORD 就是 Double Word 每个word为2个字节的长度DWORD 双字即为4个字节每个字节是8位共32位。
DWORD getWindowsBuildNumber();
COLORREF invertLightness(COLORREF c);
COLORREF invertLightnessSofter(COLORREF c);
@ -142,7 +142,7 @@ namespace NppDarkMode //
COLORREF getHotEdgeColor();
COLORREF getDisabledEdgeColor();
HBRUSH getBackgroundBrush(); //HBRUSH 取画刷
HBRUSH getBackgroundBrush();
HBRUSH getDarkerBackgroundBrush();
HBRUSH getSofterBackgroundBrush();
HBRUSH getHotBackgroundBrush();
@ -152,7 +152,7 @@ namespace NppDarkMode //
HBRUSH getHotEdgeBrush();
HBRUSH getDisabledEdgeBrush();
HPEN getDarkerTextPen(); //画笔
HPEN getDarkerTextPen();
HPEN getEdgePen();
HPEN getHotEdgePen();
HPEN getDisabledEdgePen();
@ -175,12 +175,12 @@ namespace NppDarkMode //
Colors getDarkModeDefaultColors();
void changeCustomTheme(const Colors& colors);
// handle events 处理事件
// handle events
void handleSettingChange(HWND hwnd, LPARAM lParam, bool isFromBtn = false);
bool isDarkModeReg();
// processes messages related to UAH / custom menubar drawing. 处理相关信息
// return true if handled, false to continue with normal processing in your wndproc 解决了返回true否则false继续处理
// processes messages related to UAH / custom menubar drawing.
// return true if handled, false to continue with normal processing in your wndproc
bool runUAHWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* lr);
void drawUAHMenuNCBottomLine(HWND hWnd);
@ -191,7 +191,7 @@ namespace NppDarkMode //
bool allowDarkModeForWindow(HWND hWnd, bool allow);
void setTitleBarThemeColor(HWND hWnd);
// enhancements to DarkMode.h 对于DarkMode.h的改进
// enhancements to DarkMode.h
void enableDarkScrollBarForWindowAndChildren(HWND hwnd);
inline void paintRoundFrameRect(HDC hdc, const RECT rect, const HPEN hpen, int width = 0, int height = 0);
@ -235,13 +235,13 @@ namespace NppDarkMode //
bool isThemeDark();
void setBorder(HWND hwnd, bool border = true);
BOOL CALLBACK enumAutocompleteProc(HWND hwnd, LPARAM lParam); //Windows API中的回调函数用于枚举自动完成列表中的项。HWND hwnd自动完成列表的窗口句柄。 LPARAM lParam用户定义的附加参数。
BOOL CALLBACK enumAutocompleteProc(HWND hwnd, LPARAM lParam);
void setDarkAutoCompletion();
LRESULT onCtlColor(HDC hdc); //LRESULT是一个数据类型指的是从窗口程序或者回调函数返回的32位值
LRESULT onCtlColor(HDC hdc);
LRESULT onCtlColorSofter(HDC hdc);
LRESULT onCtlColorDarker(HDC hdc);
LRESULT onCtlColorError(HDC hdc);
LRESULT onCtlColorDarkerBGStaticText(HDC hdc, bool isTextEnabled);
INT_PTR onCtlColorListbox(WPARAM wParam, LPARAM lParam); //INT_PTR是特殊定义的类型是为了解决32位与64位编译器的兼容性而设置的关键字
INT_PTR onCtlColorListbox(WPARAM wParam, LPARAM lParam);
}

@ -16,331 +16,291 @@
// 引入LastRecentFileList类的头文件
#include "lastRecentFileList.h"
// 引入菜单命令ID的头文件
#include "menuCmdID.h"
// 引入本地化的头文件,可能用于支持多语言
#include "localization.h"
// LastRecentFileList类的initMenu函数
void LastRecentFileList::initMenu(HMENU hMenu, int idBase, int posBase, Accelerator* pAccelerator, bool doSubMenu)
#include "lastRecentFileList.h"
#include "menuCmdID.h"
#include "localization.h"
void LastRecentFileList::initMenu(HMENU hMenu, int idBase, int posBase, Accelerator *pAccelerator, bool doSubMenu)
{
// 如果doSubMenu为真表示需要创建一个弹出菜单
if (doSubMenu)
{
// 设置父菜单句柄
_hParentMenu = hMenu;
// 创建一个新的弹出菜单
_hMenu = ::CreatePopupMenu();
}
// 如果doSubMenu为假表示直接使用传入的菜单句柄
else
{
// 设置父菜单句柄为NULL表示主菜单
_hParentMenu = NULL;
// 使用传入的菜单句柄作为当前菜单
_hMenu = hMenu;
}
// 设置ID的基础值
_idBase = idBase;
// 设置位置的基础值
_posBase = posBase;
// 设置指向Accelerator对象的指针用于处理键盘快捷键等加速功能
_pAccelerator = pAccelerator;
// 设置本地化语言编码为Windows的1252编码这是一种西欧语言编码
_nativeLangEncoding = NPP_CP_WIN_1252;
// 初始化idFreeArray数组所有元素都设置为true表示ID可用
for (size_t i = 0; i < sizeof(_idFreeArray); ++i)
_idFreeArray[i] = true;
if (doSubMenu)
{
_hParentMenu = hMenu;
_hMenu = ::CreatePopupMenu();
}
else
{
_hParentMenu = NULL;
_hMenu = hMenu;
}
_idBase = idBase;
_posBase = posBase;
_pAccelerator = pAccelerator;
_nativeLangEncoding = NPP_CP_WIN_1252;
for (size_t i = 0 ; i < sizeof(_idFreeArray) ; ++i)
_idFreeArray[i] = true;
}
// LastRecentFileList类的switchMode函数用于切换模式
void LastRecentFileList::switchMode()
{
// 从菜单中移除所有相关菜单项
::RemoveMenu(_hMenu, IDM_FILE_RESTORELASTCLOSEDFILE, MF_BYCOMMAND);
::RemoveMenu(_hMenu, IDM_OPEN_ALL_RECENT_FILE, MF_BYCOMMAND);
::RemoveMenu(_hMenu, IDM_CLEAN_RECENT_FILE_LIST, MF_BYCOMMAND);
// 遍历最近打开文件列表,移除所有菜单项
for (int i = 0; i < _size; ++i)
{
::RemoveMenu(_hMenu, _lrfl.at(i)._id, MF_BYCOMMAND);
}
// 根据_hParentMenu的值判断当前模式主菜单模式或子菜单模式
if (_hParentMenu == NULL) // mode main menu主菜单模式
{
if (_size > 0) // 如果最近打开文件列表不为空
{
// 从主菜单中移除一些特定的菜单项(具体移除哪些项未给出)
::RemoveMenu(_hMenu, _posBase, MF_BYPOSITION);
::RemoveMenu(_hMenu, _posBase, MF_BYPOSITION);
}
// 切换到子菜单模式创建一个新的弹出菜单作为当前菜单并设置_hParentMenu为当前菜单即新创建的弹出菜单
// 同时移除一些特定的菜单项(具体移除哪些项未给出)
_hParentMenu = _hMenu;
_hMenu = ::CreatePopupMenu();
::RemoveMenu(_hMenu, _posBase + 1, MF_BYPOSITION);
}
else // mode sub-menu子菜单模式
{
if (_size > 0) // 如果最近打开文件列表不为空
{
// 从子菜单中移除一些特定的菜单项(具体移除哪些项未给出)
::RemoveMenu(_hParentMenu, _posBase, MF_BYPOSITION);
::RemoveMenu(_hParentMenu, _posBase, MF_BYPOSITION);
}
// switch to main menu mode
::DestroyMenu(_hMenu);
_hMenu = _hParentMenu;
_hParentMenu = NULL;
}
_hasSeparators = false;
//Remove all menu items
::RemoveMenu(_hMenu, IDM_FILE_RESTORELASTCLOSEDFILE, MF_BYCOMMAND);
::RemoveMenu(_hMenu, IDM_OPEN_ALL_RECENT_FILE, MF_BYCOMMAND);
::RemoveMenu(_hMenu, IDM_CLEAN_RECENT_FILE_LIST, MF_BYCOMMAND);
for (int i = 0; i < _size; ++i)
{
::RemoveMenu(_hMenu, _lrfl.at(i)._id, MF_BYCOMMAND);
}
if (_hParentMenu == NULL) // mode main menu
{ if (_size > 0)
{
::RemoveMenu(_hMenu, _posBase, MF_BYPOSITION);
::RemoveMenu(_hMenu, _posBase, MF_BYPOSITION);
}
// switch to sub-menu mode
_hParentMenu = _hMenu;
_hMenu = ::CreatePopupMenu();
::RemoveMenu(_hMenu, _posBase+1, MF_BYPOSITION);
}
else // mode sub-menu
{
if (_size > 0)
{
::RemoveMenu(_hParentMenu, _posBase, MF_BYPOSITION);
::RemoveMenu(_hParentMenu, _posBase, MF_BYPOSITION);
}
// switch to main menu mode
::DestroyMenu(_hMenu);
_hMenu = _hParentMenu;
_hParentMenu = NULL;
}
_hasSeparators = false;
}
void LastRecentFileList::updateMenu()
{
// 获取 NppParameters 的单例实例
NppParameters& nppParam = NppParameters::getInstance();
// 检查是否没有分隔符且大小大于0满足条件则进入下面的 if 语句块
if (!_hasSeparators && _size > 0)
{
// 获取本地化语言的发言人对象
NativeLangSpeaker* pNativeLangSpeaker = nppParam.getNativeLangSpeaker();
// 从发言人对象中获取一些本地化菜单字符串
generic_string recentFileList = pNativeLangSpeaker->getSubMenuEntryName("file-recentFiles");
generic_string openRecentClosedFile = pNativeLangSpeaker->getNativeLangMenuString(IDM_FILE_RESTORELASTCLOSEDFILE);
generic_string openAllFiles = pNativeLangSpeaker->getNativeLangMenuString(IDM_OPEN_ALL_RECENT_FILE);
generic_string cleanFileList = pNativeLangSpeaker->getNativeLangMenuString(IDM_CLEAN_RECENT_FILE_LIST);
// 如果获取的字符串为空,则使用默认的字符串进行初始化
if (recentFileList == TEXT(""))
recentFileList = TEXT("&Recent Files");
if (openRecentClosedFile == TEXT(""))
openRecentClosedFile = TEXT("Restore Recent Closed File");
if (openAllFiles == TEXT(""))
openAllFiles = TEXT("Open All Recent Files");
if (cleanFileList == TEXT(""))
cleanFileList = TEXT("Empty Recent Files List");
// 如果当前不是子菜单模式,则在菜单中插入一个分隔符
if (!isSubMenuMode())
::InsertMenu(_hMenu, _posBase + 0, MF_BYPOSITION, static_cast<UINT_PTR>(-1), 0);
// 在菜单中插入四个子菜单项,分别是恢复最近关闭的文件、打开所有最近文件、清空最近文件列表和另一个分隔符
::InsertMenu(_hMenu, _posBase + 1, MF_BYPOSITION, IDM_FILE_RESTORELASTCLOSEDFILE, openRecentClosedFile.c_str());
::InsertMenu(_hMenu, _posBase + 2, MF_BYPOSITION, IDM_OPEN_ALL_RECENT_FILE, openAllFiles.c_str());
::InsertMenu(_hMenu, _posBase + 3, MF_BYPOSITION, IDM_CLEAN_RECENT_FILE_LIST, cleanFileList.c_str());
::InsertMenu(_hMenu, _posBase + 4, MF_BYPOSITION, static_cast<UINT_PTR>(-1), 0); // 插入分隔符
// 设置标志表示已有分隔符
_hasSeparators = true;
// 如果当前是子菜单模式,则在父菜单中插入子菜单和分隔符
if (isSubMenuMode())
{
::InsertMenu(_hParentMenu, _posBase + 0, MF_BYPOSITION | MF_POPUP, reinterpret_cast<UINT_PTR>(_hMenu), (LPCTSTR)recentFileList.c_str());
::InsertMenu(_hParentMenu, _posBase + 1, MF_BYPOSITION, static_cast<UINT_PTR>(-1), 0); // 插入分隔符
}
}
else if (_hasSeparators && _size == 0) // 当已有分隔符且大小为0时进入下面的 if 语句块,用于移除分隔符和菜单项
{
// 从菜单中移除一个分隔符和清空最近文件列表的菜单项
::RemoveMenu(_hMenu, _posBase + 4, MF_BYPOSITION);
::RemoveMenu(_hMenu, IDM_CLEAN_RECENT_FILE_LIST, MF_BYCOMMAND);
::RemoveMenu(_hMenu, IDM_OPEN_ALL_RECENT_FILE, MF_BYCOMMAND);
::RemoveMenu(_hMenu, IDM_FILE_RESTORELASTCLOSEDFILE, MF_BYCOMMAND);
::RemoveMenu(_hMenu, _posBase + 0, MF_BYPOSITION);
_hasSeparators = false;
if (isSubMenuMode())
{
// Remove "Recent Files" Entry and the separator from the main menu
::RemoveMenu(_hParentMenu, _posBase + 1, MF_BYPOSITION);
::RemoveMenu(_hParentMenu, _posBase + 0, MF_BYPOSITION);
// Remove the last left separator from the submenu
::RemoveMenu(_hMenu, 0, MF_BYPOSITION);
}
}
_pAccelerator->updateFullMenu();
//Remove all menu items
for (int i = 0; i < _size; ++i)
{
::RemoveMenu(_hMenu, _lrfl.at(i)._id, MF_BYCOMMAND);
}
//Then readd them, so everything stays in sync
for (int j = 0; j < _size; ++j)
{
generic_string strBuffer(BuildMenuFileName(nppParam.getRecentFileCustomLength(), j, _lrfl.at(j)._name));
::InsertMenu(_hMenu, _posBase + j, MF_BYPOSITION, _lrfl.at(j)._id, strBuffer.c_str());
}
NppParameters& nppParam = NppParameters::getInstance();
if (!_hasSeparators && _size > 0)
{
//add separators
NativeLangSpeaker *pNativeLangSpeaker = nppParam.getNativeLangSpeaker();
generic_string recentFileList = pNativeLangSpeaker->getSubMenuEntryName("file-recentFiles");
generic_string openRecentClosedFile = pNativeLangSpeaker->getNativeLangMenuString(IDM_FILE_RESTORELASTCLOSEDFILE);
generic_string openAllFiles = pNativeLangSpeaker->getNativeLangMenuString(IDM_OPEN_ALL_RECENT_FILE);
generic_string cleanFileList = pNativeLangSpeaker->getNativeLangMenuString(IDM_CLEAN_RECENT_FILE_LIST);
if (recentFileList == TEXT(""))
recentFileList = TEXT("&Recent Files");
if (openRecentClosedFile == TEXT(""))
openRecentClosedFile = TEXT("Restore Recent Closed File");
if (openAllFiles == TEXT(""))
openAllFiles = TEXT("Open All Recent Files");
if (cleanFileList == TEXT(""))
cleanFileList = TEXT("Empty Recent Files List");
if (!isSubMenuMode())
::InsertMenu(_hMenu, _posBase + 0, MF_BYPOSITION, static_cast<UINT_PTR>(-1), 0);
::InsertMenu(_hMenu, _posBase + 1, MF_BYPOSITION, IDM_FILE_RESTORELASTCLOSEDFILE, openRecentClosedFile.c_str());
::InsertMenu(_hMenu, _posBase + 2, MF_BYPOSITION, IDM_OPEN_ALL_RECENT_FILE, openAllFiles.c_str());
::InsertMenu(_hMenu, _posBase + 3, MF_BYPOSITION, IDM_CLEAN_RECENT_FILE_LIST, cleanFileList.c_str());
::InsertMenu(_hMenu, _posBase + 4, MF_BYPOSITION, static_cast<UINT_PTR>(-1), 0);
_hasSeparators = true;
if (isSubMenuMode())
{
::InsertMenu(_hParentMenu, _posBase + 0, MF_BYPOSITION | MF_POPUP, reinterpret_cast<UINT_PTR>(_hMenu), (LPCTSTR)recentFileList.c_str());
::InsertMenu(_hParentMenu, _posBase + 1, MF_BYPOSITION, static_cast<UINT_PTR>(-1), 0);
}
}
else if (_hasSeparators && _size == 0) //remove separators
{
::RemoveMenu(_hMenu, _posBase + 4, MF_BYPOSITION);
::RemoveMenu(_hMenu, IDM_CLEAN_RECENT_FILE_LIST, MF_BYCOMMAND);
::RemoveMenu(_hMenu, IDM_OPEN_ALL_RECENT_FILE, MF_BYCOMMAND);
::RemoveMenu(_hMenu, IDM_FILE_RESTORELASTCLOSEDFILE, MF_BYCOMMAND);
::RemoveMenu(_hMenu, _posBase + 0, MF_BYPOSITION);
_hasSeparators = false;
if (isSubMenuMode())
{
// Remove "Recent Files" Entry and the separator from the main menu
::RemoveMenu(_hParentMenu, _posBase + 1, MF_BYPOSITION);
::RemoveMenu(_hParentMenu, _posBase + 0, MF_BYPOSITION);
// Remove the last left separator from the submenu
::RemoveMenu(_hMenu, 0, MF_BYPOSITION);
}
}
_pAccelerator->updateFullMenu();
//Remove all menu items
for (int i = 0; i < _size; ++i)
{
::RemoveMenu(_hMenu, _lrfl.at(i)._id, MF_BYCOMMAND);
}
//Then readd them, so everything stays in sync
for (int j = 0; j < _size; ++j)
{
generic_string strBuffer(BuildMenuFileName(nppParam.getRecentFileCustomLength(), j, _lrfl.at(j)._name));
::InsertMenu(_hMenu, _posBase + j, MF_BYPOSITION, _lrfl.at(j)._id, strBuffer.c_str());
}
}
// 向最近的文件列表中添加一个文件名。如果达到用户定义的最大限制或列表被锁定,则不会添加。
// 如果文件名已存在,则会先将其移除再添加。
void LastRecentFileList::add(const TCHAR* fn)
void LastRecentFileList::add(const TCHAR *fn)
{
if (_userMax == 0 || _locked) // 如果用户定义的最大数量为0或列表被锁定直接返回不进行添加
return;
RecentItem itemToAdd(fn); // 创建一个新的RecentItem对象使用传入的文件名初始化
int index = find(fn); // 查找该文件名在列表中的索引
if (index != -1) // 如果找到
{
// 如果文件名已存在,先将其移除,然后重新添加
remove(index);
}
if (_size == _userMax) // 如果列表已满
{
itemToAdd._id = _lrfl.back()._id; // 使用最后一个元素的ID初始化新元素的ID
_lrfl.pop_back(); // 移除列表中的最后一个元素(即最旧的一个)
}
else // 如果列表未满
{
itemToAdd._id = popFirstAvailableID(); // 获取第一个可用的ID并初始化新元素的ID
++_size; // 增加列表的大小
}
_lrfl.push_front(itemToAdd); // 在列表的前面添加新元素
updateMenu(); // 更新菜单
if (_userMax == 0 || _locked)
return;
RecentItem itemToAdd(fn);
int index = find(fn);
if (index != -1)
{
//already in list, bump upwards
remove(index);
}
if (_size == _userMax)
{
itemToAdd._id = _lrfl.back()._id;
_lrfl.pop_back(); //remove oldest
}
else
{
itemToAdd._id = popFirstAvailableID();
++_size;
}
_lrfl.push_front(itemToAdd);
updateMenu();
}
// 根据文件名从最近的文件列表中移除一个文件。如果找到,则移除;否则什么也不做。
void LastRecentFileList::remove(const TCHAR* fn)
{
int index = find(fn); // 查找文件名在列表中的索引
if (index != -1) // 如果找到
remove(index); // 移除该元素
void LastRecentFileList::remove(const TCHAR *fn)
{
int index = find(fn);
if (index != -1)
remove(index);
}
// 根据索引从最近的文件列表中移除一个文件。如果索引有效,则移除;否则什么也不做。
void LastRecentFileList::remove(size_t index)
void LastRecentFileList::remove(size_t index)
{
if (_size == 0 || _locked) // 如果列表为空或被锁定,直接返回,不进行移除操作
return;
if (index < _lrfl.size()) // 如果索引有效
{
::RemoveMenu(_hMenu, _lrfl.at(index)._id, MF_BYCOMMAND); // 从菜单中移除该ID对应的菜单项
setAvailable(_lrfl.at(index)._id); // 将该ID标记为可用可能是为了下次添加时重复使用
_lrfl.erase(_lrfl.begin() + index); // 从列表中移除该元素
--_size; // 减少列表的大小
updateMenu(); // 更新菜单
}
if (_size == 0 || _locked)
return;
if (index < _lrfl.size())
{
::RemoveMenu(_hMenu, _lrfl.at(index)._id, MF_BYCOMMAND);
setAvailable(_lrfl.at(index)._id);
_lrfl.erase(_lrfl.begin() + index);
--_size;
updateMenu();
}
}
// 清空最近的文件列表。所有文件名和ID都会被移除菜单也会相应地更新。
void LastRecentFileList::clear()
void LastRecentFileList::clear()
{
if (_size == 0) // 如果列表为空,直接返回,不进行任何操作
return;
for (int i = (_size - 1); i >= 0; i--) // 从后向前遍历列表,因为移除元素会影响后续元素的索引
{
::RemoveMenu(_hMenu, _lrfl.at(i)._id, MF_BYCOMMAND); // 从菜单中移除该ID对应的菜单项
setAvailable(_lrfl.at(i)._id); // 将该ID标记为可用可能是为了下次添加时重复使用
_lrfl.erase(_lrfl.begin() + i); // 从列表中移除该元素
}
_size = 0; // 将列表大小设置为0
updateMenu(); // 更新菜单
if (_size == 0)
return;
for (int i = (_size-1); i >= 0; i--)
{
::RemoveMenu(_hMenu, _lrfl.at(i)._id, MF_BYCOMMAND);
setAvailable(_lrfl.at(i)._id);
_lrfl.erase(_lrfl.begin() + i);
}
_size = 0;
updateMenu();
}
LastRecentFileList
cpp
// 根据ID从最近的文件列表中获取一个文件名。如果找到则返回对应的文件名否则返回列表中的第一个文件名。
generic_string& LastRecentFileList::getItem(int id)
generic_string & LastRecentFileList::getItem(int id)
{
int i = 0; // 从索引0开始查找因为如果ID不存在可能会返回第一个文件名作为默认值。
for (; i < _size; ++i) // 遍历整个列表查找匹配的ID。如果找不到i会等于_size此时i会被重新设置为0。这是一个后备计划以防ID不存在于列表
{
if (_lrfl.at(i)._id == id) // 如果找到匹配的ID
break;
}
if (i == _size) // 如果遍历完整个列表都没有找到匹配的ID
i = 0; // 将索引重置为0这样可以从列表的第一个元素开始返回文件名作为默认值
return _lrfl.at(i)._name; // 返回找到的文件名或默认文件名
int i = 0;
for (; i < _size; ++i)
{
if (_lrfl.at(i)._id == id)
break;
}
if (i == _size)
i = 0;
return _lrfl.at(i)._name; //if not found, return first
}
// 根据索引从最近的文件列表中获取一个文件名。如果索引有效,则返回对应的文件名;否则返回列表中的第一个文件名。
generic_string& LastRecentFileList::getIndex(int index)
generic_string & LastRecentFileList::getIndex(int index)
{
return _lrfl.at(index)._name; // 返回找到的文件名或默认文件名
return _lrfl.at(index)._name; //if not found, return first
}
// 设置用户定义的最大最近文件列表大小。如果当前列表大小大于新设置的大小,则移除最旧的文件直到列表大小等于新设置的大小。
void LastRecentFileList::setUserMaxNbLRF(int size)
{
_userMax = size; // 设置用户定义的最大列表大小
if (_size > _userMax) // 如果当前列表大小大于新设置的大小
{
int toPop = _size - _userMax; // 计算需要移除的文件数量
while (toPop > 0) // 当还有文件需要移除时
{
::RemoveMenu(_hMenu, _lrfl.back()._id, MF_BYCOMMAND); // 从菜单中移除该ID对应的菜单项
setAvailable(_lrfl.back()._id); // 将该ID标记为可用可能是为了下次添加时重复使用
_lrfl.pop_back(); // 从列表中移除该元素
toPop--; // 减少剩余需要移除的文件数量
_size--; // 减少当前列表大小
}
updateMenu(); // 更新菜单
_size = _userMax; // 将当前列表大小设置为新设置的大小
}
_userMax = size;
if (_size > _userMax)
{ //start popping items
int toPop = _size-_userMax;
while (toPop > 0)
{
::RemoveMenu(_hMenu, _lrfl.back()._id, MF_BYCOMMAND);
setAvailable(_lrfl.back()._id);
_lrfl.pop_back();
toPop--;
_size--;
}
updateMenu();
_size = _userMax;
}
}
// 将最近的文件列表保存到配置文件中。保存的文件名顺序与加载时相同。
void LastRecentFileList::saveLRFL()
{
NppParameters& nppParams = NppParameters::getInstance(); // 获取NppParameters的实例引用
if (nppParams.writeRecentFileHistorySettings(_userMax)) // 如果成功写入最近的文件历史设置
{
for (int i = _size - 1; i >= 0; i--) // 按照从后往前的顺序保存文件名,确保加载时的顺序正确
{
nppParams.writeHistory(_lrfl.at(i)._name.c_str()); // 将文件名写入配置文件的历史记录部分
}
}
NppParameters& nppParams = NppParameters::getInstance();
if (nppParams.writeRecentFileHistorySettings(_userMax))
{
for (int i = _size - 1; i >= 0; i--) //reverse order: so loading goes in correct order
{
nppParams.writeHistory(_lrfl.at(i)._name.c_str());
}
}
}
// 在最近的文件列表中查找指定的文件名。
int LastRecentFileList::find(const TCHAR* fn)
int LastRecentFileList::find(const TCHAR *fn)
{
for (int i = 0; i < _size; ++i) // 遍历整个列表
{
if (OrdinalIgnoreCaseCompareStrings(_lrfl.at(i)._name.c_str(), fn) == 0) // 如果找到匹配的文件名(忽略大小写)
{
return i; // 返回该文件在列表中的索引
}
}
return -1; // 如果未找到,返回-1
for (int i = 0; i < _size; ++i)
{
if (OrdinalIgnoreCaseCompareStrings(_lrfl.at(i)._name.c_str(), fn) == 0)
{
return i;
}
}
return -1;
}
// 弹出第一个可用的ID。
int LastRecentFileList::popFirstAvailableID()
int LastRecentFileList::popFirstAvailableID()
{
for (int i = 0; i < NB_MAX_LRF_FILE; ++i) // 遍历所有可能的ID
{
if (_idFreeArray[i]) // 如果该ID可用未被使用
{
_idFreeArray[i] = false; // 标记该ID为已使用
return i + _idBase; // 返回该ID的值从_idBase开始计算
}
}
return 0; // 如果所有ID都已使用返回0或默认值
for (int i = 0 ; i < NB_MAX_LRF_FILE ; ++i)
{
if (_idFreeArray[i])
{
_idFreeArray[i] = false;
return i + _idBase;
}
}
return 0;
}
// 设置指定的ID为可用。
void LastRecentFileList::setAvailable(int id)
{
int index = id - _idBase; // 根据ID计算其在_idFreeArray中的索引
_idFreeArray[index] = true; // 将该索引对应的值设置为true表示该ID可用
int index = id - _idBase;
_idFreeArray[index] = true;
}

Loading…
Cancel
Save