Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,12 @@ set_target_properties(salext PROPERTIES

add_subdirectory(src/plugins)

# ==============================================================================
# Optional Developer Tools
# ==============================================================================

include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/sal_dev_tools.cmake)

# ==============================================================================
# Installation Rules
# ==============================================================================
Expand Down
87 changes: 87 additions & 0 deletions cmake/sal_dev_tools.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Optional developer tools for Sally.
# These targets are intentionally excluded from the default build and release packaging.

include_guard(GLOBAL)

if(NOT WIN32)
return()
endif()

set(SAL_DEVTOOLS_OUTPUT_DIR "${SAL_OUTPUT_BASE}/$<CONFIG>_${SAL_PLATFORM}/devtools")

function(sal_configure_dev_tool TARGET_NAME)
set_target_properties(${TARGET_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${SAL_DEVTOOLS_OUTPUT_DIR}"
ARCHIVE_OUTPUT_DIRECTORY "${SAL_DEVTOOLS_OUTPUT_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${SAL_DEVTOOLS_OUTPUT_DIR}/bin"
)

if(MSVC)
target_compile_options(${TARGET_NAME} PRIVATE /MP /W3 /J)
target_link_options(${TARGET_NAME} PRIVATE /MANIFEST:NO)
endif()
endfunction()

# utfnames: utility for creating and inspecting non-well-formed UTF-16 filenames.
set(SAL_UTFNAMES_MANIFEST_RC "${CMAKE_CURRENT_BINARY_DIR}/devtools/utfnames_manifest.rc")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/devtools")
file(WRITE "${SAL_UTFNAMES_MANIFEST_RC}"
"#include <winuser.h>\n"
"CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST \"${SAL_ROOT}/tools/utfnames/utfnames.manifest\"\n"
)

add_executable(utfnames EXCLUDE_FROM_ALL
"${SAL_ROOT}/tools/utfnames/utfnames.cpp"
"${SAL_UTFNAMES_MANIFEST_RC}"
)
target_compile_features(utfnames PRIVATE cxx_std_20)
target_compile_definitions(utfnames PRIVATE
_CONSOLE
WIN32
WINVER=0x0601
_WIN32_WINNT=0x0601
$<$<CONFIG:Debug>:_DEBUG>
$<${SAL_IS_RELEASE}:NDEBUG>
)
target_link_libraries(utfnames PRIVATE ntdll)
sal_configure_dev_tool(utfnames)

# salbreak: hidden hotkey helper that asks running Sally instances to break.
add_executable(salbreak WIN32 EXCLUDE_FROM_ALL
"${SAL_ROOT}/tools/salbreak/md5.cpp"
"${SAL_ROOT}/tools/salbreak/salbreak.cpp"
"${SAL_ROOT}/tools/salbreak/tasklist.cpp"
"${SAL_ROOT}/tools/salbreak/salbreak.rc"
)
target_include_directories(salbreak PRIVATE "${SAL_ROOT}/tools/salbreak")
target_compile_definitions(salbreak PRIVATE
WIN32
_WINDOWS
WINVER=0x0601
_WIN32_WINNT=0x0601
$<$<CONFIG:Debug>:_DEBUG>
$<${SAL_IS_RELEASE}:NDEBUG>
)
target_link_libraries(salbreak PRIVATE advapi32)
if(MSVC)
set_target_properties(salbreak PROPERTIES
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
)
endif()
sal_configure_dev_tool(salbreak)

# RegParser: standalone registry file parser/dumper built from reglib.
add_executable(regparser EXCLUDE_FROM_ALL
"${SAL_SRC}/reglib/src/tester.cpp"
"${SAL_SRC}/reglib/src/regparser.rc"
)
set_target_properties(regparser PROPERTIES OUTPUT_NAME "RegParser")
target_include_directories(regparser PRIVATE "${SAL_SRC}/reglib/src")
target_compile_definitions(regparser PRIVATE
_CONSOLE
${SAL_COMMON_DEFINES}
$<$<CONFIG:Debug>:${SAL_DEBUG_DEFINES}>
$<${SAL_IS_RELEASE}:${SAL_RELEASE_DEFINES}>
)
target_link_libraries(regparser PRIVATE reglib)
sal_configure_dev_tool(regparser)
16 changes: 16 additions & 0 deletions doc/DEV.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@ A few Altap Salamander 4.0 plugins are either not included or cannot be compiled

All source code uses UTF-8-BOM encoding and is formatted with `clang-format`. Refer to the `\tools\normalize.ps1` script for more information.

## Developer Tool Targets

The CMake build keeps a few standalone maintenance utilities as explicit targets only. They are not part of default builds, `populate`, installs, or release packages. These targets replace old first-party `.vcxproj` files that were preserved from the Open Salamander tree:

- `utfnames`: creates and lists unusual NTFS names, including names with invalid UTF-16 sequences.
- `salbreak`: hidden hotkey helper that asks running Sally instances to trigger their break/report path.
- `regparser`: builds `RegParser.exe`, a standalone parser/dumper for `.reg` files using `src/reglib`.

```bash
cmake --build build --config Debug --target utfnames salbreak regparser
```

The resulting executables are written under `build/out/sally/<Config>_<Arch>/devtools/`.

The old Portables plugin Visual Studio project was removed without a separate dev-tool replacement because the normal CMake plugin build already covers `plugin_portables` and its English language file. `packages.config` is still used by `cmake/sal_nuget.cmake` to restore the WebView2 SDK for the CMake build.

## Localization

For the supported Translator workflow, generated workspaces, and contribution steps, see `doc/LOCALIZATION.md`.
38 changes: 34 additions & 4 deletions src/darkmode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ BOOL InitSupportLogged = FALSE;
BOOL SupportWarningLogged = FALSE;
BOOL CaptionColorAttrSupported = TRUE;
BOOL TextColorAttrSupported = TRUE;
BOOL BorderColorAttrSupported = TRUE;
thread_local int ListTreeThemeApplyDepth = 0;
thread_local int GroupBoxThemeApplyDepth = 0;

const DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_NEW = 20; // Win10 1903+
const DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_OLD = 19; // older Win10 builds
const DWORD DWMWA_BORDER_COLOR = 34;
const DWORD DWMWA_CAPTION_COLOR = 35;
const DWORD DWMWA_TEXT_COLOR = 36;
const COLORREF DWMWA_COLOR_DEFAULT = 0xFFFFFFFF;
Expand Down Expand Up @@ -705,6 +707,23 @@ BOOL DarkMode_OnSettingChange(LPARAM lParam)
return changed;
}

void DarkMode_DrawSunkenFrame(HDC hDC, const RECT* r, const DarkModeMainFramePalette& palette)
{
HGDIOBJ oldPen = SelectObject(hDC, GetStockObject(DC_PEN));

SetDCPenColor(hDC, palette.LineDark);
MoveToEx(hDC, r->left, r->bottom - 1, NULL);
LineTo(hDC, r->left, r->top);
LineTo(hDC, r->right - 1, r->top);

SetDCPenColor(hDC, palette.Border);
MoveToEx(hDC, r->right - 1, r->top, NULL);
LineTo(hDC, r->right - 1, r->bottom - 1);
LineTo(hDC, r->left - 1, r->bottom - 1);

SelectObject(hDC, oldPen);
}

void DarkMode_ApplyTitleBar(HWND hwnd)
{
EnsureInitialized();
Expand Down Expand Up @@ -748,14 +767,24 @@ void DarkMode_ApplyTitleBar(HWND hwnd)
int normalizedTheme = NormalizeThemeMode(ThemeMode);
COLORREF captionColor = DWMWA_COLOR_DEFAULT;
COLORREF textColor = DWMWA_COLOR_DEFAULT;
COLORREF borderColor = DWMWA_COLOR_DEFAULT;
if (normalizedTheme == THEME_MODE_DARK)
{
captionColor = RGB(32, 32, 32);
textColor = RGB(255, 255, 255);
}
if (useDark)
borderColor = MAINFRAME_DARK_LINE_DARK;

HRESULT hrCaption = S_OK;
HRESULT hrText = S_OK;
HRESULT hrBorder = S_OK;
if (BorderColorAttrSupported)
{
hrBorder = DwmSetWindowAttributePtr(hwnd, DWMWA_BORDER_COLOR, &borderColor, sizeof(borderColor));
if (FAILED(hrBorder))
BorderColorAttrSupported = FALSE;
}
if (CaptionColorAttrSupported)
{
hrCaption = DwmSetWindowAttributePtr(hwnd, DWMWA_CAPTION_COLOR, &captionColor, sizeof(captionColor));
Expand All @@ -768,11 +797,12 @@ void DarkMode_ApplyTitleBar(HWND hwnd)
if (FAILED(hrText))
TextColorAttrSupported = FALSE;
}
if (FAILED(hrCaption) || FAILED(hrText))
if (FAILED(hrBorder) || FAILED(hrCaption) || FAILED(hrText))
{
TRACE_I("DarkMode: caption/text color attributes not available or failed, hwnd=" << hwnd
<< ", hrCaption=" << std::hex << hrCaption
<< ", hrText=" << std::hex << hrText);
TRACE_I("DarkMode: border/caption/text color attributes not available or failed, hwnd=" << hwnd
<< ", hrBorder=" << std::hex << hrBorder
<< ", hrCaption=" << std::hex << hrCaption
<< ", hrText=" << std::hex << hrText);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/darkmode.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ void DarkMode_ApplyTitleBar(HWND hwnd);
void DarkMode_ApplyToThreadTopLevelWindows(DWORD threadId);
void DarkMode_ApplyListTreeThemeRecursive(HWND root);
void DarkMode_ApplyGroupBoxThemeRecursive(HWND root);
void DarkMode_DrawSunkenFrame(HDC hDC, const RECT* r, const DarkModeMainFramePalette& palette);
65 changes: 62 additions & 3 deletions src/editwnd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const COLORREF EDITWND_DARK_BG = RGB(45, 45, 48);
const COLORREF EDITWND_DARK_INPUT_BG = RGB(30, 30, 30);
const COLORREF EDITWND_DARK_TEXT = RGB(232, 232, 232);
const COLORREF EDITWND_DARK_DISABLED_TEXT = RGB(140, 140, 140);
const COLORREF EDITWND_DARK_BORDER_OUTER = RGB(75, 75, 75);
const COLORREF EDITWND_DARK_BORDER_INNER = RGB(95, 95, 95);
const COLORREF EDITWND_DARK_BORDER_OUTER = RGB(45, 45, 48);
const COLORREF EDITWND_DARK_BORDER_INNER = RGB(62, 62, 66);
const COLORREF EDITWND_DARK_BUTTON_BG = RGB(52, 52, 56);

static void FillRectSolid(HDC hDC, const RECT* rect, COLORREF color)
Expand All @@ -35,6 +35,28 @@ static void FillRectSolid(HDC hDC, const RECT* rect, COLORREF color)
SelectObject(hDC, oldBrush);
}

static void DrawDarkComboFrame(HWND hwnd, HDC hDC)
{
RECT r;
GetWindowRect(hwnd, &r);
OffsetRect(&r, -r.left, -r.top);

HGDIOBJ oldPen = SelectObject(hDC, GetStockObject(DC_PEN));
HGDIOBJ oldBrush = SelectObject(hDC, GetStockObject(NULL_BRUSH));

SetDCPenColor(hDC, EDITWND_DARK_BORDER_OUTER);
Rectangle(hDC, r.left, r.top, r.right, r.bottom);

if (r.right - r.left > 3 && r.bottom - r.top > 3)
{
SetDCPenColor(hDC, EDITWND_DARK_BORDER_INNER);
Rectangle(hDC, r.left + 1, r.top + 1, r.right - 1, r.bottom - 1);
}

SelectObject(hDC, oldBrush);
SelectObject(hDC, oldPen);
}

} // namespace

//*****************************************************************************
Expand Down Expand Up @@ -1779,6 +1801,21 @@ CEditWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
return result;
}

case WM_NCPAINT:
{
if (DarkMode_ShouldUseDark())
{
HDC hDC = HANDLES(GetWindowDC(HWindow));
if (hDC != NULL)
{
DrawDarkComboFrame(HWindow, hDC);
HANDLES(ReleaseDC(HWindow, hDC));
}
return 0;
}
break;
}

case WM_DESTROY:
{
if (EditLine != NULL)
Expand Down Expand Up @@ -1841,8 +1878,29 @@ CEditWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
btnArea.right = cr.right - 3;
btnArea.bottom = cr.bottom - 3;
if (btnArea.right > btnArea.left && btnArea.bottom > btnArea.top)
{
FillRectSolid(hDC, &btnArea, EDITWND_DARK_BUTTON_BG);

// The default combo WM_PAINT no longer runs in this branch,
// so the dropdown arrow glyph must be drawn here.
int centerX = (btnArea.left + btnArea.right) / 2;
int centerY = (btnArea.top + btnArea.bottom) / 2;
POINT arrow[3] = {
{centerX - 3, centerY - 1},
{centerX + 4, centerY - 1},
{centerX, centerY + 3},
};
HPEN hArrowPen = HANDLES(CreatePen(PS_SOLID, 1, EDITWND_DARK_TEXT));
HBRUSH hArrowBrush = HANDLES(CreateSolidBrush(EDITWND_DARK_TEXT));
HGDIOBJ oldArrowPen = SelectObject(hDC, hArrowPen);
HGDIOBJ oldArrowBrush = SelectObject(hDC, hArrowBrush);
Polygon(hDC, arrow, 3);
SelectObject(hDC, oldArrowBrush);
SelectObject(hDC, oldArrowPen);
HANDLES(DeleteObject(hArrowBrush));
HANDLES(DeleteObject(hArrowPen));
}

HGDIOBJ oldPen = SelectObject(hDC, GetStockObject(DC_PEN));
HGDIOBJ oldBrush = SelectObject(hDC, GetStockObject(NULL_BRUSH));
COLORREF oldPenColor = SetDCPenColor(hDC, EDITWND_DARK_BORDER_OUTER);
Expand Down Expand Up @@ -1893,7 +1951,8 @@ CEditWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
if (r.right > r.left && r.bottom > r.top)
ValidateRect(HWindow, &r);

break;
ValidateRect(HWindow, NULL);
return 0;
}

// ensure the 2-pixel frame around the combo is not cleared during painting
Expand Down
3 changes: 1 addition & 2 deletions src/files_box_header_bottom_bar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ void CHeaderLine::PaintItem(HDC hDC, int index, int x)
if (useDarkChrome)
{
HGDIOBJ oldPen = SelectObject(ItemBitmap.HMemDC, GetStockObject(DC_PEN));
SetDCPenColor(ItemBitmap.HMemDC, palette.LineLight);
SetDCPenColor(ItemBitmap.HMemDC, palette.LineDark);
MoveToEx(ItemBitmap.HMemDC, r.left, r.top, NULL);
LineTo(ItemBitmap.HMemDC, r.right, r.top);
if (first)
Expand All @@ -327,7 +327,6 @@ void CHeaderLine::PaintItem(HDC hDC, int index, int x)
LineTo(ItemBitmap.HMemDC, r.left, r.bottom - 2);
}

SetDCPenColor(ItemBitmap.HMemDC, palette.LineDark);
MoveToEx(ItemBitmap.HMemDC, r.left, r.bottom - 1, NULL);
LineTo(ItemBitmap.HMemDC, r.right, r.bottom - 1);
if (!last)
Expand Down
17 changes: 15 additions & 2 deletions src/files_box_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1365,14 +1365,27 @@ CFilesBox::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
GetClientRect(HWindow, &r);
r.right += 2;
r.bottom += 2;
DrawEdge(hdc, &r, BDR_SUNKENOUTER, BF_RECT);
DarkModeMainFramePalette palette;
BOOL useDarkPalette = DarkMode_GetMainFramePalette(&palette);
if (useDarkPalette)
DarkMode_DrawSunkenFrame(hdc, &r, palette);
else
DrawEdge(hdc, &r, BDR_SUNKENOUTER, BF_RECT);
if (Parent->StatusLine != NULL && Parent->StatusLine->HWindow != NULL)
{
r.left = 0;
r.top = r.bottom - 1;
r.right = 1;
r.bottom = r.top + 1;
FillRect(hdc, &r, HMenuGrayTextBrush);
if (useDarkPalette)
{
HGDIOBJ oldBrush = SelectObject(hdc, GetStockObject(DC_BRUSH));
SetDCBrushColor(hdc, palette.LineDark);
FillRect(hdc, &r, (HBRUSH)GetStockObject(DC_BRUSH));
SelectObject(hdc, oldBrush);
}
else
FillRect(hdc, &r, HMenuGrayTextBrush);
}
HANDLES(ReleaseDC(HWindow, hdc));
return 0;
Expand Down
1 change: 1 addition & 0 deletions src/files_window_navigation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3328,6 +3328,7 @@ void CFilesWindow::OnColorsChanged()
// A color/theme switch can happen after the panel was already painted.
// Force a full panel repaint so stale light pixels are not left behind
// until the user moves selection or scrolls.
RedrawWindow(ListBox->HWindow, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_NOCHILDREN);
InvalidateRect(ListBox->HWindow, &ListBox->FilesRect, FALSE);
if (ListBox->HeaderLine.HWindow != NULL)
InvalidateRect(ListBox->HeaderLine.HWindow, NULL, TRUE);
Expand Down
Loading
Loading