Skip to content

fix(windows): embed comctl32 v6 + PerMonitorV2 manifest in alc and wxcas - #796

Merged
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:fix/win-manifest-alc-wxcas
May 31, 2026
Merged

fix(windows): embed comctl32 v6 + PerMonitorV2 manifest in alc and wxcas#796
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:fix/win-manifest-alc-wxcas

Conversation

@got3nks

@got3nks got3nks commented May 31, 2026

Copy link
Copy Markdown
Contributor

Fixes #792.

ngosang reported alc.exe and wxcas.exe rendering with the unthemed Win95-era look on Windows 11 (screenshots). Root cause: the embedded manifests have only trustInfo + supportedOS IDs — no Microsoft.Windows.Common-Controls v6 dependency — so Windows falls back to the classic comctl32 v5 styling.

Verified empirically by extracting the manifest from the actual alc.exe / wxcas.exe shipped in run 26720270704:

<!-- alc.exe / wxcas.exe today: no Common-Controls dep -->
<assembly ...>
  <trustInfo>...</trustInfo>
  <compatibility>...supportedOS for Vista/7/8/8.1/10...</compatibility>
</assembly>

vs amule.exe which got the full treatment from #780:

<assembly ...>
  <dependency><dependentAssembly>
    <assemblyIdentity ... name="Microsoft.Windows.Common-Controls" version="6.0.0.0" .../>
  </dependentAssembly></dependency>
  <asmv3:application><asmv3:windowsSettings>
    <dpiAwareness>PerMonitorV2,PerMonitor</dpiAwareness>
  </asmv3:windowsSettings></asmv3:application>
</assembly>

Changes

File Change
src/utils/aLinkCreator/alc.manifest New. Mirror of amule.manifest with assemblyIdentity name="amule-project.alc".
src/utils/wxCas/wxcas.manifest New. Mirror with assemblyIdentity name="amule-project.wxcas".
src/utils/aLinkCreator/alc.rc Embed 1 24 "alc.manifest", #define wxUSE_RC_MANIFEST 0.
src/utils/wxCas/wxcas.rc Embed 1 24 "wxcas.manifest", same wx-disable.
src/utils/aLinkCreator/src/CMakeLists.txt Add alc.rc to target_sources(alc) — it wasn't being compiled.
src/utils/wxCas/src/CMakeLists.txt Add wxcas.rc to target_sources(wxcas) — same.

Note the second pre-existing bug surfaced by this change: alc.rc and wxcas.rc were sitting in the tree but never compiled, because CMake's target_sources only listed ${CMAKE_BINARY_DIR}/version.rc. The #include "wx/msw/wx.rc" line they already contained was effectively dead code. This PR brings them into the build.

alcc is CLI (wxUSE_GUI=0) so doesn't need a comctl32 manifest; intentionally skipped.

Verification plan

Will trigger a packaging run on the branch and post the extracted manifest from the new alc.exe / wxcas.exe plus a Windows screenshot once @ngosang or anyone with a Windows machine confirms the visual fix.

amule-project#780 added an explicit application manifest for amule.exe so the
main GUI renders with themed Common Controls v6 (and survives
non-100% DPI scaling).  alc.exe and wxcas.exe weren't covered:
their .rc files (alc.rc / wxcas.rc) only did
`#include "wx/msw/wx.rc"`, and the manifest wxWidgets emits in
that header on our MinGW/CLANGARM64 build chain doesn't declare
the Microsoft.Windows.Common-Controls v6 dependency.  Result is
the classic Win95-style unthemed widgets reported in amule-project#792.

Verified empirically against the packaging-run artefacts: the
manifest embedded in alc.exe / wxcas.exe today contains only
trustInfo + the supportedOS compat list, while amule.exe has the
full Common-Controls + dpiAwareness block from amule.manifest.

A second pre-existing bug: alc.rc and wxcas.rc were sitting in
the tree but never compiled — CMake's target_sources only listed
${CMAKE_BINARY_DIR}/version.rc, so the wx.rc include they
contain was dead code.  This commit fixes that by adding the
.rc files to target_sources for the alc and wxcas targets;
alcc is CLI (wxUSE_GUI=0) so doesn't need the manifest.

Mirrors amule-project#780's pattern exactly: a per-binary .manifest file
embedded at resource ID 1 / type 24, with wxUSE_RC_MANIFEST=0
to prevent wx.rc from duplicating the slot.  Each manifest
sets assemblyIdentity name to "amule-project.alc" /
"amule-project.wxcas" for accurate side-by-side metadata.
@got3nks

got3nks commented May 31, 2026

Copy link
Copy Markdown
Contributor Author

Packaging-run 26721666180 finished the Windows x64 leg; extracted the manifests from alc.exe and wxcas.exe in the new portable zip:

<!-- alc.exe (post-fix) -->
<assembly ... xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
  <assemblyIdentity type="win32" name="amule-project.alc" version="1.0.0.0" .../>
  <description>aMule ED2K link creator</description>
  <dependency><dependentAssembly>
    <assemblyIdentity ... name="Microsoft.Windows.Common-Controls" version="6.0.0.0" .../>
  </dependentAssembly></dependency>
  <asmv3:application><asmv3:windowsSettings>
    <dpiAwareness ...>PerMonitorV2,PerMonitor</dpiAwareness>
    <dpiAware ...>True/PM</dpiAware>
  </asmv3:windowsSettings></asmv3:application>
</assembly>

Same shape for wxcas.exe (with name="amule-project.wxcas", description="aMule statistics tool").

Both manifests match amule.manifest exactly except for the assembly identity / description. CMake glue is working — the .rc files are now being compiled into the targets (they weren't before this PR).

@mrjimenez
mrjimenez merged commit e812cb5 into amule-project:master May 31, 2026
7 checks passed
@got3nks
got3nks deleted the fix/win-manifest-alc-wxcas branch June 3, 2026 14:16
got3nks added a commit to got3nks/amule that referenced this pull request Jun 4, 2026
…ndex

Adds 55+ merged PRs to the 3.0.0 changelog since the last update
(amule-project#747, 2026-05-27). Narrative additions cover:

- Packaging: expanded the top list to include the macOS per-arch .app
  bundles and the Windows NSIS installer alongside the existing
  AppImage / Flatpak / .dmg / .zip entries. New bullets for amule-project#785
  (alc/alcc/cas/wxcas everywhere + Windows amuleweb), amule-project#794 (.dmg
  amuleweb path), amule-project#789 (<OS>-<arch> artifact naming), amule-project#780 / amule-project#796
  (Windows DPI + comctl32 manifest), amule-project#784 (FHS share/amule paths).

- Bug Fixes & Stability: post-amule-project#744 fixes including EC notification
  leak (amule-project#797), big-library scaling (amule-project#736, amule-project#840 superseding amule-project#728),
  amulegui ghost entries (amule-project#810, amule-project#819, amule-project#841, amule-project#824, amule-project#830, amule-project#760),
  PartFile early hash (amule-project#762), server protocol fixes (amule-project#835, amule-project#788,
  amule-project#721, amule-project#787), crypto stream UB (amule-project#779), UAF prevention (amule-project#756),
  Kad rotation (amule-project#795, amule-project#799/amule-project#805), GTK warning silencing (amule-project#833,
  amule-project#826/amule-project#836), and the clang-tidy worklist (amule-project#770, amule-project#772-amule-project#774).

- Translations: late-cycle wave covering French/Turkish manpages
  (amule-project#753/amule-project#754/amule-project#776), Galician (amule-project#763), Slovenian (amule-project#771), pt-BR
  (amule-project#768/amule-project#775/amule-project#812), French (amule-project#811), plus man-page tooling for
  date+version drift (amule-project#802).

- Contributors: added ngosang for UX feedback on the late-3.0
  cycle (amule-project#817/amule-project#818/amule-project#821/amule-project#828/amule-project#844) and ongoing work on the
  user-facing manual at amule-org.github.io.

- Merged PRs flat index: extended with amule-project#746-amule-project#845 + amule-project#841.
got3nks added a commit to got3nks/amule that referenced this pull request Jun 4, 2026
…ndex

Adds 55+ merged PRs to the 3.0.0 changelog since the last update
(amule-project#747, 2026-05-27). Narrative additions cover:

- Packaging: expanded the top list to include the macOS per-arch .app
  bundles and the Windows NSIS installer alongside the existing
  AppImage / Flatpak / .dmg / .zip entries. New bullets for amule-project#785
  (alc/alcc/cas/wxcas everywhere + Windows amuleweb), amule-project#794 (.dmg
  amuleweb path), amule-project#789 (<OS>-<arch> artifact naming), amule-project#780 / amule-project#796
  (Windows DPI + comctl32 manifest), amule-project#784 (FHS share/amule paths).

- Bug Fixes & Stability: post-amule-project#744 fixes including EC notification
  leak (amule-project#797), big-library scaling (amule-project#736, amule-project#840 superseding amule-project#728),
  amulegui ghost entries (amule-project#810, amule-project#819, amule-project#841, amule-project#824, amule-project#830, amule-project#760),
  PartFile early hash (amule-project#762), server protocol fixes (amule-project#835, amule-project#788,
  amule-project#721, amule-project#787), crypto stream UB (amule-project#779), UAF prevention (amule-project#756),
  Kad rotation (amule-project#795, amule-project#799/amule-project#805), GTK warning silencing (amule-project#833,
  amule-project#826/amule-project#836), and the clang-tidy worklist (amule-project#770, amule-project#772-amule-project#774).

- Translations: late-cycle wave covering French/Turkish manpages
  (amule-project#753/amule-project#754/amule-project#776), Galician (amule-project#763), Slovenian (amule-project#771), pt-BR
  (amule-project#768/amule-project#775/amule-project#812), French (amule-project#811), plus man-page tooling for
  date+version drift (amule-project#802).

- Contributors: added ngosang for UX feedback on the late-3.0
  cycle (amule-project#817/amule-project#818/amule-project#821/amule-project#828/amule-project#844) and ongoing work on the
  user-facing manual at amule-org.github.io.

- Merged PRs flat index: extended with amule-project#746-amule-project#845 + amule-project#841.
mrjimenez pushed a commit that referenced this pull request Jun 4, 2026
Adds 55+ merged PRs to the 3.0.0 changelog since the last update
(#747, 2026-05-27). Narrative additions cover:

- Packaging: expanded the top list to include the macOS per-arch .app
  bundles and the Windows NSIS installer alongside the existing
  AppImage / Flatpak / .dmg / .zip entries. New bullets for #785
  (alc/alcc/cas/wxcas everywhere + Windows amuleweb), #794 (.dmg
  amuleweb path), #789 (<OS>-<arch> artifact naming), #780 / #796
  (Windows DPI + comctl32 manifest), #784 (FHS share/amule paths).

- Bug Fixes & Stability: post-#744 fixes including EC notification
  leak (#797), big-library scaling (#736, #840 superseding #728),
  amulegui ghost entries (#810, #819, #841, #824, #830, #760),
  PartFile early hash (#762), server protocol fixes (#835, #788,
  #721, #787), crypto stream UB (#779), UAF prevention (#756),
  Kad rotation (#795, #799/#805), GTK warning silencing (#833,
  #826/#836), and the clang-tidy worklist (#770, #772-#774).

- Translations: late-cycle wave covering French/Turkish manpages
  (#753/#754/#776), Galician (#763), Slovenian (#771), pt-BR
  (#768/#775/#812), French (#811), plus man-page tooling for
  date+version drift (#802).

- Contributors: added ngosang for UX feedback on the late-3.0
  cycle (#817/#818/#821/#828/#844) and ongoing work on the
  user-facing manual at amule-org.github.io.

- Merged PRs flat index: extended with #746-#845 + #841.
got3nks added a commit to got3nks/amule that referenced this pull request Aug 5, 2026
Two follow-ups from the PR amule-project#796 review that the previous commit didn't
cover.

CSearchDlg::UpdateHitCount() walks the search's whole result list twice
-- GetItemCount() and GetHiddenItemCount(), each looping every result's
children through ShouldShow()/IsFiltered() -- and it ran once per
arriving result, so a search was quadratic in its own result count. The
child path made it worse: since the notification rework, a duplicate
result also reaches CSearchDlg::AddResult, so a heavily grouped search
paid the double walk per duplicate as well.

Results arrive in bursts and only a burst's final label is ever seen, so
AddResult/UpdateResult now just mark the tab pending and one recompute
per idle flushes them -- the same coalescing CSearchListCtrl::OnIdle
already does for the tree rebuild. The flush iterates the notebook's
live pages and updates those present in the pending set, so a tab closed
between mark and flush is never matched and its pointer never followed.
The other UpdateHitCount() call sites are user actions (tab change,
filter toggle, browse status) and stay immediate.

CSearchDlg needs wxWS_EX_PROCESS_IDLE for any of that to run: amuleDlg
sets wxIdleEvent::SetMode(wxIDLE_PROCESS_SPECIFIED), so idle events only
reach windows that opt in (CSearchListCtrl already does for its own
OnIdle). Without the style the flush never fires and the tab label stops
updating entirely -- it builds and lints clean either way.

CSearchListCtrl::RemoveResult() and CSearchListModel::NotifyFileRemoved()
are dead after this port: their only caller was ShowChildren(), the flat
-list expand/collapse emulation wxDataViewCtrl makes unnecessary, and
CSearchList::RemoveResults() never took that path -- it fires
Notify_Search_Removed and closes the tab before freeing anything.
Removed both. (This also retires the ShouldShow() gate an earlier review
comment of mine flagged as a use-after-free; the gate was latent-wrong
rather than reachable, since nothing called into it.)
got3nks added a commit to got3nks/amule that referenced this pull request Aug 7, 2026
…e-project#180, amule-project#801) (amule-project#839)

* feat(gui): port CFileDetailListCtrl to CMuleVirtualDataViewCtrl (amule-project#180, amule-project#801)

Continues the wxListCtrl -> wxDataViewCtrl migration (amule-project#180/amule-project#801) onto
the shared base from amule-project#811, after Search (amule-project#796), Servers (amule-project#807/amule-project#811)
and Friends (amule-project#830). CFileDetailListCtrl was next because it has no
CBarShader rendering (unlike Downloads/SharedFiles/Sources-Peers,
which additionally need a still-nonexistent wxDataViewCustomRenderer).

Its caller, CFileDetailDialog::FillSourcenameList(), drove the old
list with raw position-indexed CRUD (FindItem by name, SetItemPtrData,
SetItem by column index, DeleteItem) rather than the pointer-identity
style CServerListCtrl/CFriendListCtrl already use, so porting the
control cleanly meant refactoring the caller too:

- CFileDetailDialog gained a std::map<wxString, SourcenameItem *>
  m_sourcenames member, replacing "search the list widget by name"
  with a map lookup. FillSourcenameList()'s reset/update/prune shape
  is otherwise unchanged, just re-keyed to the map and driven through
  the control's new AddSource/RefreshSource/RemoveSource API.
- OnBnClickedTakeOver()/OnListClickedTakeOver() resolve the selected
  row through GetSelectedItemData() (inherited from
  CMuleVirtualDataViewCtrl) instead of GetNextItem+GetItemText.
- The dialog's EVT_LIST_ITEM_ACTIVATED binding on IDC_LISTCTRLFILENAMES
  becomes EVT_DATAVIEW_ITEM_ACTIVATED; wxDataViewEvent is a
  wxNotifyEvent/wxCommandEvent descendant so it still propagates from
  the child control to the dialog-level handler the same way.
- Fixed a latent leak: nothing in ~CFileDetailDialog() freed the
  SourcenameItem objects still referenced by open rows -- only the
  "not a partfile" and "pruned to zero" paths in FillSourcenameList()
  ever deleted them. Now freed in the destructor via m_sourcenames.

CMuleDataViewCtrl always ORs in wxDV_MULTIPLE (no single-selection
mode exists on the shared base), but this list's "take over filename"
actions assume exactly one selection, same as the old list did (it
never actually requested wxLC_SINGLE_SEL either -- checked
muuli_wdr.cpp -- it enforced single-selection itself via OnSelect()
deselecting every other row). CFileDetailListCtrl::OnSelectionChanged()
does the equivalent: collapses to the just-clicked row whenever more
than one ends up selected.

Also deleted a vestigial nested SourcenameItem struct in
FileDetailListCtrl.h that duplicated (by accident of matching layout)
the real one in PartFile.h, and dropped a per-row background-colour
set that reproduced the default and carried its own "do we still need
this?" comment.

Verified: builds clean; clang-format v18 (pinned Docker image) applied;
clang-tidy Tier-1 (whole-tree) and Tier-2 (changed-lines,
.clang-tidy-new-code) both clean via the ~/aMuleTest/ci-local replica.
Grepped for other CFileDetailListCtrl/IDC_LISTCTRLFILENAMES references
to confirm muuli_wdr.cpp's construction call needed no changes.
Interactive verification (source list populate/re-tally, take-over via
button and double-click, single-selection enforcement, sort, VoiceOver)
left for manual testing per project convention.

* fix(gui): keep the source-name list sorted, and drop rows before freeing them

Review follow-up to the port.

FillSourcenameList() zeroes every count and then rewrites them in place, so
while it runs the list is not ordered by the column it is sorted on -- and
AddSource() places a new row with a binary search, which needs that ordering
to hold. An insertion therefore leaves rows in arbitrary positions, and the
repair has to happen here, exactly as the pre-port code did.

Only on insertion, though. A count that merely changed is what the live-sort
preference governs: RefreshSource() re-sorts when it is on, and when it is
off the row is meant to stay put rather than move under the user. Sorting
unconditionally would quietly override that setting for this list.

The destructor and the not-a-partfile path also freed the SourcenameItem
objects while the list control still held pointers to them. Nothing can
paint or sort in either window today -- both dialog call sites are stack
temporaries, so destruction is synchronous -- but that rests on wx's teardown
order rather than on anything guaranteed, and the base states the rule
plainly: it has to be told before the caller frees the item data. Both now
clear the rows first, as the prune loop already did.

* fix(gui): decide the source list's live re-sort per column

IsLiveSortColumn() answered true for every column, so a refresh tick
scheduled a re-sort even when the list was sorted by File Name -- a value
that never changes, since the name is the key each row was created under.

Answer per column instead, the shape CServerListCtrl already uses, so only a
sources-sorted list re-sorts on its own. The header comment already
described it this way; only the implementation did not.

* chore: drop a duplicated paragraph from the file header

FileDetailDialog.cpp carried the "Any parts of this program derived from
the xMule, lMule or eMule project" paragraph twice. It is the only file in
src/ that does, and the file is already being touched here.

---------

Co-authored-by: got3nks <[email protected]>
got3nks added a commit to got3nks/amule that referenced this pull request Aug 8, 2026
Each arriving result marked the model dirty and the idle flush answered
with wxDataViewModel::Cleared() -- "everything changed". The control
responds by rebuilding, which loses the scroll position, so a running
search could not be read: the list snapped back to the top on every
burst, and with an eD2k search that is continuous.

Arrivals now batch per idle into ItemsAdded()/ItemsChanged(), grouped by
parent since wx takes one parent per batch. Measured against a
standalone sorted wxDataViewCtrl, an insert or a value change holds the
top row where Cleared() drops it to the first.

Cleared() is kept where it is still the only correct answer: a filter
change, and any arrival while a filter is active -- m_filterKnown makes
a row's visibility a live function of its download status, so a value
change can require a row to appear or disappear, which only
re-evaluating the tree catches.

Grouped results -- ones that joined an existing result -- also keep it,
on every platform except macOS. PR amule-project#796 had already found that GTK and
MSW would not re-derive container-ness from ItemChanged() or a
delete-and-re-add. ItemAdded() under the new parent, the notification
that actually means "this row now has a child", does worse than fail on
GTK: it aborts inside GtkTreeView's own red-black tree,

  gtkrbtree.c:471:_gtk_rbtree_insert_after:
    assertion failed: (_gtk_rbtree_is_nil (tree->root))

because the row is inserted into a child tree GTK never built for a
parent it still considers a leaf. Native NSOutlineView has no such
structure to corrupt and re-queries IsContainer() as it draws, which is
why it takes the incremental path happily. Only grouping is affected: a
plain new result and a value change stay incremental everywhere, and
those are the bulk of what arrives.

The batch holds CSearchFile pointers between the notification and the
flush, where the old code held only a bool. A dropped search frees its
results in that window, so the flush re-checks each one: top-level
results against the indexed list, children against their parent still
being live and still owning them (IndexResult() keeps only top-level
results, so children are not in that list).

The control skips its save-and-restore of selection and expansion on the
incremental path, having nothing to protect them from.
got3nks added a commit to got3nks/amule that referenced this pull request Aug 8, 2026
…ointers

Five things review turned up in the incremental-notification work.

Forming a group creates two children, not one. AddChild() synthesises a
copy of the parent as the first child -- the result received first -- and
notifies nobody, while AddToList() infers survival from the child count
and announces only the incoming result. So the batch said one child where
the model reports two. macOS hides it by re-reading the model as it
draws, which is the same thing that masked the original amule-project#796 defect; a
backend that builds its rows from the notifications shows a two-variant
group holding one row. Announced at the point of creation now, so a
freshly formed group is reported in full.

Sorting left the cursor addressed by row. SetSelectedItemData() restores
the selection by identity, but nothing round-tripped GetCurrentItem();
Reset() used to invalidate it along with the rest of the view, and a
repaint does not. On a live-sorted list it comes to rest on whatever file
moved into that row, so the next arrow key steps from the wrong place and
a shifted page key extends from the wrong anchor -- the same disagreement
between cursor and view that the macOS page keys were fixed for.

The pending batches held pointers vouched for by membership in a parent's
child list, which is not a liveness test: nothing in the tree ever
unlinks a child, so being listed survives being freed. CSearchFile
already broadcasts its own destruction, and CCommentDialogLst already
consumes it for this exact purpose -- the model now does too, and drops
the pointer when the result dies. The membership dance goes away with it.

Both batches now de-duplicate. SetDownloadStatus() notifies every child
of the parent it updated, so a single arrival into a 50-variant group
queued 51 entries and a busy idle window multiplied that; the control is
handed each row once.

And the comment above the idle flush still described one Cleared() per
idle as the policy, which is now only the fallback branch.
got3nks added a commit to got3nks/amule that referenced this pull request Aug 8, 2026
Reporting arrivals one at a time aborts amulegui on GTK:

  gtkrbtree.c:471:_gtk_rbtree_insert_after:
    assertion failed: (_gtk_rbtree_is_nil (tree->root))

First seen for ItemAdded() under a newly formed group, and routing
grouped results back through Cleared() did not avoid it -- the same abort
came back from the batch of top-level additions. wx's GTK backend keeps
its own mirror of the model's tree, and this model's arrivals corrupt
GtkTreeView's red-black tree whichever notification carries them.

PR amule-project#796 had already found that neither ItemChanged() nor a
delete-and-re-add made GTK or MSW re-derive container-ness, and settled
on Cleared() for that reason. Two aborts from two different notifications
say the constraint is broader than container-ness, so this stops looking
for the subset GTK tolerates and gives those platforms back exactly the
behaviour they had: one Cleared() per idle.

macOS keeps the incremental path, which is where the problem was reported
and where it is verified. Native NSOutlineView keeps no parallel tree --
it re-queries the model as it draws, which is what masked the original
amule-project#796 defect -- so it takes the notifications happily.

MSW is grouped with GTK deliberately: its generic implementation has tree
bookkeeping of its own, it has not been tested here, and keeping today's
behaviour costs it nothing but a repaint.

The rest of the review fixes stand on their own and apply everywhere: the
synthetic child is announced, the sort restores the cursor, the batches
are de-duplicated, and a destroyed result is dropped from them.
got3nks added a commit to got3nks/amule that referenced this pull request Aug 8, 2026
…e-project#860)

Three ways the ported lists threw away the row the user was reading.

SortList() ended in wxDataViewIndexListModel::Reset(), which says the
model was replaced; every backend rebuilds and lands at row 0, both
axes. A sort reorders rows without replacing them and the count is the
same on both sides of the std::sort, so a repaint is all it needs. The
bulk path is the exception: AppendItemData() adds rows without telling
the control -- that is what makes a bulk load cheap -- so Reset() was
doubling as the only notification that the rows existed, and repainting
there drew a list the control still believed was empty. The sort now
splits into SortItems(), which is silent, SortList(), which repaints,
and FinishBulkLoad(), which keeps its Reset(). Affects servers, shared
files, clients, downloads, file details and friends.

The unshifted page and home/end keys on macOS were left to
NSOutlineView, which scrolls without moving the cursor, so the next
arrow key snapped the view straight back and the page key looked inert.
They now run through the handler the shifted keys already used:
unshifted replaces the selection with the row landed on, shifted keeps
extending, and both move the cursor. GTK and MSW get this from their
backends.

The search list answered every arriving result with Cleared(), which
during a search is continuous. Arrivals now batch per idle into
ItemsAdded()/ItemsChanged() on macOS. GTK cannot take them at all --
feeding this model's arrivals in one notification at a time aborts
inside GtkTreeView's red-black tree, first from ItemAdded() under a
newly formed group and again from the batch of top-level additions
after that case was routed away. PR amule-project#796 had already found that neither
ItemChanged() nor a delete-and-re-add made GTK or MSW re-derive
container-ness; two aborts from two different notifications say the
constraint is broader than that, so those platforms keep one Cleared()
per idle and put the view back afterwards instead. The idle branch
already restored the selection and the expanded rows, so the top row
joins them -- which works here and not in the virtual lists because a
CSearchFile pointer still names the same result after a rebuild, where
a row-addressed item names whatever has since moved into it.

Measured against wx 3.3.3 rather than argued: Refresh() and
SetSelections() move no viewport on either backend; SetCurrentItem()
moves it only when the target is off screen, and then onto the cursor;
EnsureVisible() after Cleared() restores exactly on GTK and lands at
the bottom on macOS. So the cursor is restored only while its row is on
screen -- with no row ever clicked GTK's cursor sits at row 0, and
restoring it unconditionally clamped the view to the top. GetScrollPos()
is unusable on this control: wx asserts that the window is not
scrollable and answers 0.

Four things review turned up on the way. The synthetic first child of a
new group is now announced -- AddChild() creates a copy of the parent as
the first child and notified nobody, so a freshly formed group reported
one child where the model had two. Both batches de-duplicate, since
SetDownloadStatus() notifies every child of the parent it updated and a
single arrival into a 50-variant group queued 51 entries. Pending
pointers are dropped through MuleNotify::SearchFileBeingDestroyed, which
CCommentDialogLst already consumes for this purpose, replacing a guard
that checked membership in a list nothing ever unlinks from. And
SortList() restores the cursor by identity alongside the selection,
which Reset() used to invalidate for it.

Builds clean on macOS ARM64, Ubuntu ARM64 and Windows ARM64, with
clang-format and both clang-tidy tiers clean over the diff. Confirmed
interactively on amulegui on all three: sorting holds position in shared
files and clients, the search list holds position while results stream
in, group expansion is intact, and the macOS page keys move the cursor
with the view.
mrjimenez pushed a commit to mrjimenez/amule that referenced this pull request Aug 8, 2026
… entry

The wxDataViewCtrl port's rewritten CSearchListCtrl::OnRightClick
dropped the #if 0-guarded "Mark as known file" menu entry that was
never actually compiled/shown (OnMarkAsKnown itself is untouched,
just permanently unreachable, exactly as before). xgettext scans
_("...") calls textually regardless of #if 0, so removing that dead
line dropped the string from the source scan and put the checked-in
catalogs out of sync with scripts/update-po.sh's output -- caught by
CI's "App catalogs in sync with source" gate on PR amule-project#796.

Verified: only change across every po/*.po and po/amule.pot is the
removal of the single "Mark as known file" msgid (and its already-
untranslated-or-obsoleted msgstr entries) -- symmetric, matches the
one line removed from source, no POT-Creation-Date-only churn.
mrjimenez pushed a commit to mrjimenez/amule that referenced this pull request Aug 8, 2026
…und in review

Addresses four issues got3nks found reviewing PR amule-project#796 (the
CSearchListCtrl wxDataViewCtrl port):

1. Grouped results only showed the filename column until expanded.
   wxDataViewModel::HasValue() defaults to drawing just column 0 for
   a container (HasContainerColumns() defaults to false) -- a
   wxListCtrl had no notion of container rows, so this only surfaced
   with the port. A grouped parent is a real result carrying the
   same size/sources/rating as any other row, not a section header,
   so CSearchListModel::HasContainerColumns() now returns true.

2. Grouped results never got an expander triangle on GTK or MSW
   (confirmed by got3nks building this branch on Ubuntu 26.04 arm64
   and Windows 11 arm64 -- child rows were completely unreachable on
   both). CSearchList::AddToList's duplicate-result path calls
   item->AddChild(toadd) then only Notify_Search_Update_Sources(item),
   which reaches the model as ItemChanged(parent) -- a *value*
   notification. Leaf-to-container is a *structural* change, which
   the wxDataViewModel notifier API signals via ItemAdded/ItemDeleted
   instead. macOS masked this: its NSOutlineView backend re-queries
   IsContainer() on every draw, so the triangle appeared as soon as
   HasChildren() flipped regardless of notification. GTK's
   GtkTreeView and the MSW generic implementation both maintain their
   own node tree and only learn container-ness from structural
   notifications, which this path never sent. Fixed by also calling
   Notify_Search_Add_Result(toadd) -- routes through the existing
   CSearchDlg::AddResult -> CSearchListCtrl::AddResult ->
   CSearchListModel::NotifyFileAdded path, which already handles a
   child correctly (ItemAdded(parent, child)) once actually called.

3. NotifyFileRemoved() didn't gate on the same ShouldShow() check
   NotifyFileAdded() does, so a result that was filtered out (and
   therefore never added to the control) could still get an
   ItemDeleted call for a row the control never knew about. Now
   symmetric.

4. A comment in CSearchList::RemoveResults still named SetItemPtrData
   and m_filteredOut, both gone with the port; updated to describe
   the current mechanism (wxDataViewItem IDs are the raw CSearchFile
   pointers).

Verified: full `amule` target builds clean (macOS); clang-format v18
(pinned) applied; clang-tidy CI replica (Tier-1 whole-tree + Tier-2
changed-lines) both clean.
mrjimenez pushed a commit to mrjimenez/amule that referenced this pull request Aug 8, 2026
…se2-port

feat(search): port CSearchListCtrl to wxDataViewCtrl
mrjimenez pushed a commit to mrjimenez/amule that referenced this pull request Aug 8, 2026
amule-project#796 ported the search list to wxDataViewCtrl and amule-project#805 fixed what that
port left behind. amule-project#801 asked whether the remaining lists should each be
ported independently or share a base, and deferred the answer until the
shape was clear from more than one list. It now is.

CMuleDataViewCtrl is the wxDataViewCtrl counterpart of CMuleListCtrl:
column widths and their persistence, the header show/hide menu, hidden
state, the multi-column sort chain, type-to-select, Cmd/Ctrl+A, the
macOS shifted page/home/end keys, the trailing-column spacer, and
drag-resize detection. It owns no data; a list supplies its rows through
GetDisplayOrder(), their label through GetRowLabel(), and how two of
them compare through CompareByColumn().

CMuleVirtualDataViewCtrl is the counterpart of CMuleVirtualListCtrl,
and virtual in the same sense: rows are addressed by index through a
wxDataViewIndexListModel and nothing is materialised per row. It carries
the item-identity bookkeeping an identity-addressed port avoids -- a
wxDataViewItem from a row-addressed model encodes the row number, so a
deletion silently retargets any item held across it, which is why
everything here speaks in wxUIntPtr and re-resolves selection after each
mutation. Also the legacy filter API, live re-sort coalesced through one
CallAfter and deferred while the user is interacting, bulk append and
batch removal, and icon columns.

CSearchListCtrl moves onto the plain base, losing 499 lines.
mrjimenez pushed a commit to mrjimenez/amule that referenced this pull request Aug 8, 2026
…oject#180, amule-project#801) (amule-project#830)

* feat(gui): port CFriendListCtrl to CMuleVirtualDataViewCtrl (amule-project#180, amule-project#801)

Continues the wxListCtrl -> wxDataViewCtrl migration (amule-project#180/amule-project#801) onto
the shared base PR amule-project#811 extracted after the Servers port (amule-project#807, now
absorbed into amule-project#811). CFriendListCtrl was picked next after reading all
the remaining candidates directly rather than going by line count
alone: it has no CBarShader rendering (unlike Downloads/SharedFiles/
Sources-Peers, which additionally need a still-nonexistent
wxDataViewCustomRenderer), it's already pointer-identity addressed
(UpdateFriend(CFriend*)/RemoveFriend(CFriend*) map directly onto
AddItemData/RefreshItemData/RemoveItemData), and its blast radius is
tiny (ChatWnd.cpp, muuli_wdr.cpp construction only).

CFileDetailListCtrl looked smaller by line count but its caller
(FileDetailDialog.cpp) drives it with raw position-indexed CRUD
(FindItem by name, SetItem by column index, DeleteItem) plus a
pre-existing duplicate-type quirk (two unrelated SourcenameItem
structs relying on compatible layout) -- porting it cleanly means
refactoring the caller too, which is a separate, more invasive piece
of work than this one.

CServerListCtrl (current, post-amule-project#811) is the template mirrored here:
same AppendTextColumn/AppendSpacerColumn/AssociateVirtualModel/
LoadColumnSettings/InitColumnState ctor sequence, same GetItemColumnText/
GetItemAttr/CompareItemData/OnListKey hook shape. Public API
(UpdateFriend, RemoveFriend) and the constructor signature are
unchanged, so ChatWnd.cpp and muuli_wdr.cpp needed no edits.

Notable deltas from the pre-port behaviour: added a real CompareItemData
so header-click sort now works (the old list never called SetSortFunc
at all); GetItemAttr replaces the old SetItemTextColour call with the
same visible result (blue for linked friends, default text colour
otherwise).

Verified: builds clean; clang-format v18 (pinned Docker image) applied;
clang-tidy Tier-1 (whole-tree) and Tier-2 (changed-lines,
.clang-tidy-new-code) both clean via the ~/aMuleTest/ci-local replica
-- Tier-2 caught one real modernize-use-nullptr hit, fixed. Grepped
ChatWnd.cpp/.h and muuli_wdr.cpp to confirm no other call sites exist.
Interactive verification (sort, right-click menu states, Delete-key
removal, chat-session activation, VoiceOver) left for manual testing
per project convention, same as PR amule-project#796/amule-project#807.

* fix(gui): resolve activated friend through selection, not row-as-pointer

got3nks's review on amule-project#830 found a real crash: OnItemActivated() cast
event.GetItem()'s ID directly to CFriend* on the assumption it was the
item's data pointer, but CMuleVirtualDataViewCtrl's row-addressed
model returns the row index (+1) as that ID -- the "item identity is
not row identity" case MuleVirtualDataViewCtrl.h itself documents.
Every double-click/Enter on a friend dereferenced a bogus pointer.
Fixed by selecting the activated row and resolving it through
GetSelectedItemData(), matching CServerListCtrl::OnItemActivated.

Also from the same review: IsLiveSortColumn() now returns true, since
the name (the only sortable column) can change after a friend is
already listed and the UpdateFriend() comment claimed a re-sort that
the base's default-false hook never actually triggered; and dropped a
no-op static_cast<int>() around a call that already returns int.

Verified: builds clean, clang-format v18 applied, clang-tidy Tier-2
(changed lines, .clang-tidy-new-code) clean via the local CI replica.

* chore: retrigger CI

The previous run hit a GitHub Actions infrastructure outage (job not
acquired by any runner, "Failed to resolve action download info" /
"Service Unavailable" on clang-format, Translation checks, mingw-w64
Debug and clang-tidy Tier-1) unrelated to this branch's code -- every
job that did run passed. No admin rights to rerun the failed jobs
directly, so retriggering with an empty commit instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feat: wxcas and alc utilities looks old / bad on Windows

2 participants