Skip to content

ADD: CSV/TSV Table Grid Lister Plugin with Inline Find & Replace - #37

Open
pplupo wants to merge 231 commits into
doublecmd:masterfrom
pplupo:csvview
Open

ADD: CSV/TSV Table Grid Lister Plugin with Inline Find & Replace#37
pplupo wants to merge 231 commits into
doublecmd:masterfrom
pplupo:csvview

Conversation

@pplupo

@pplupo pplupo commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

This PR updates the CSV plugin for Double Commander built with Qt6 to visualize, navigate, edit, and export CSV and TSV files in an interactive spreadsheet-like grid (QTableWidget). It also includes a fully integrated, focus-safe Find & Replace utility panel.

Key Features

1. Spreadsheet Grid View & Visuals

  • Grid Rendering: Visualizes files in a spreadsheet layout. Supports optional grid lines and auto-resizing column widths to fit content.
  • Header Row Toggle: Treat the first row of a file as column headers (with sorting capability) or as a regular data row.
  • Text / Source View Mode: Toggle to a raw, unparsed text view of the file with optional word wrap.
  • External Application Integration: Launch the default external system spreadsheet editor directly from the toolbar.

2. Auto-Detection & RFC Parsing

  • Encoding Engine: Auto-detects Cyrillic, UTF-8, Latin, etc. via Enca 1.19 (compiled and statically linked to the plugin library).
  • Double-Quote Parsing: Handles double-quoted fields containing separators (commas/tabs) or newlines, adhering to RFC standard specifications.

3. Edit & Undo/Redo Stack

  • Inline Editing: Double-click any cell to modify its content.
  • Row & Column Operations: Insert empty columns/rows, delete selections, or paste content directly from the clipboard.
  • Interactive Column Reordering: Horizontal drag-and-drop column headers to rearrange columns.
  • Full History: All cell edits, column rearrangements, row/column additions, row/column deletions, and sorting actions are fully managed via a QUndoStack for complete Undo (Ctrl+Z) and Redo (Ctrl+shift+ZorCtrl+Y`) capability.
  • Dirty Indicator: Toolbar indicator ( / *) showing unsaved history changes.

4. Inline Find & Replace Panel

Press Ctrl+F, Ctrl+R, or click the toolbar 🔍 Find/Replace button in the grid view to open a collapsible search panel at the bottom of the layout:

  • Interactive Scope Limits: Confines searches and replacements to All Cells, Selected Cells, Current Column, or Current Row.
  • Flexible Querying: Supports Case Sensitivity, exact cell matching (Match Entire Cell), and Regular Expression (QRegularExpression) patterns.
  • Atomic Replace All: Bundles all replacements in a single transaction under a QUndoStack macro so that they can be undone/redone in a single action.
  • Auto-Quoting Protection: If editing or replacing inserts a separator character (e.g. a comma into a CSV), the plugin automatically sets the cell metadata to wrap the value in double quotes and escape internal quotes on text preview or file save. This metadata change is fully reverted if the edit is undone.

5. Focus & DC Pane Coordination

  • Focus Safety: Implemented click-outside event filtering and synchronous deactivation. When focus moves outside the lister, the plugin safely yields focus to the main container, preventing trapped key events and enabling smooth file list navigation and pane switching in Double Commander.
  • Panel Key Bypass: Prevents Double Commander from trapping navigation and delete keys when typing inside the Find/Replace panel text inputs.

🛠 Build & Installation

  • The plugin compiles under Qt6 using standard makefiles.
  • Configured in build.sh to compile and package csvview_qt6.wlx along with translation files into the release archive under release/wlx/csvview/.
  • Configuration options (Enca language hints, double-quoting handling, grid painting, etc.) are managed in j2969719.ini.

pplupo and others added 30 commits May 12, 2026 10:08
This commit introduces the mpv_wayland WLX (Lister) plugin for Double
Commander. It enables native video playback in the Quick View panel
using the libmpv Render API and QOpenGLWidget. It is specifically
designed for seamless integration on Wayland and HiDPI displays.

Committed files:
- Source code (src/) implementing Qt6/mpv integration
- SDK headers (sdk/) for plugin interface
- CMakeLists.txt for build configuration
- README.md with installation instructions and embedded screenshot
- mpv_wayland.png screenshot asset
- Integration into root build.sh for automated release packaging
This commit introduces the MDK WLX plugin, a high-performance media
viewer powered by the MDK SDK and pure Qt6. It allows instant preview
of video and audio files. A key feature is its out-of-process isolation
via dlopen, ensuring that MDK's dependencies do not conflict with
Double Commander.

Committed files:
- C++ source (plugin.cpp) and Pascal wrappers (mdk.pas, etc.)
- Lazarus project files (.lpi, .lpr)
- Makefile for building the C++ shared object
- SDK headers (sdk/)
- README.md and proposal.md documentation
- Integration into root build.sh for automated release packaging
This commit introduces a specialized log viewer WLX plugin built with
Qt6 and C++20. It is designed to handle massive log files without
freezing Double Commander, utilizing zero-copy file loading (mmap),
fast regex searching (RE2), and a robust focus-isolation architecture
for Wayland.

Committed files:
- Source code (src/) for the log model and viewer widget
- CMakeLists.txt for build configuration
- README.md and detailed design doc (double commander wlx log viewer.md)
- logviewer.png screenshot asset
- Integration into root build.sh for automated release packaging
This commit introduces the kpart WLX plugin. It acts as a host for
KDE KParts, effectively allowing Double Commander to leverage any
installed KDE viewer (like Okular for PDFs or LibreOffice for docs)
directly within the Quick View panel natively on Wayland/Qt6.

Committed files:
- Source code (src/) for KPart hosting logic
- SDK headers (sdk/)
- CMakeLists.txt for build configuration
- README.md with screenshots
- test.svg and screenshot assets (kpart_md.png, kpart_svg.png)
- Integration into root build.sh for automated release packaging
…ete row editing, TSV support, and context menu

- Display CSV/TSV files in an interactive QTableWidget grid
- Auto-detect separator (comma, semicolon, tab); fall back to file extension
- Header Row toggle button: interprets first line as header or data row,
  reloads file on toggle; controls whether copies include the header line
- Ctrl+C: copy selection as TSV (includes header when Header Row is on)
- Right-click context menu: Copy as TSV, Copy as CSV, Delete Selected Rows,
  Insert from Clipboard
- Ctrl+V: insert clipboard rows at selected position; validates column count;
  skips clipboard header line when it matches the current header (Header Row on)
- Delete: remove selected rows
- Ctrl+S: save file; correctly handles editing vs non-editing state without
  disturbing Double Commander focus
- Inline cell editing with Save / Save As / Reload toolbar actions
- Enca encoding auto-detection with Glib conversion fallback
- Double-quoted field parsing per CSV RFC
- F7 search support
- Full README with screenshots, feature docs, and configuration reference
- Attribution to original author j2969719
…nd highlighting

- Export ListSetDefaultParams to retrieve and bind the host's INI file path.
- Add HighlightRule structure and fast RE2-based line matching directly on memory-mapped offsets.
- Create Highlighting Rules SettingsDialog modal with an inline-colored rules list.
- Enable ExtendedSelection on the rules list to support deleting or moving multiple rules up/down, maintaining selection state using QItemSelectionModel.
- Connect double-click on any rule row to open the editing window.
- Write Foreground/Background labels above color picker buttons in RuleDialog.
- Implement session persistence under [HighlightRules] in the INI file.
- Prevent default rules from overriding empty rule configurations on restart.
- Add context-aware "Add Default Rules" button to prepend default log levels relative to the current selection.
- Create sample.log matching each level for diagnostics.
This commit introduces a premium editor WLX plugin based on Native Qt6
and KDE's KTextEditor framework. It brings full syntax highlighting,
code folding, and advanced editing capabilities to Double Commander's
viewer panel without LCL compatibility crashes on Wayland.

Committed files:
- Source code (src/) wrapping KTextEditor
- CMakeLists.txt for build configuration
- README.md with screenshots
- defects.md tracking known issues and resolutions
- Screenshot assets (kate_java.png, kate_md.png, kate_py.png)
- Integration into root build.sh for automated release packaging
…yles, preserve selection, and fix Proper/Title Case logic
…nd highlighting

- Export ListSetDefaultParams to retrieve and bind the host's INI file path.
- Add HighlightRule structure and fast RE2-based line matching directly on memory-mapped offsets.
- Create Highlighting Rules SettingsDialog modal with an inline-colored rules list.
- Enable ExtendedSelection on the rules list to support deleting or moving multiple rules up/down, maintaining selection state using QItemSelectionModel.
- Connect double-click on any rule row to open the editing window.
- Write Foreground/Background labels above color picker buttons in RuleDialog.
- Implement session persistence under [HighlightRules] in the INI file.
- Prevent default rules from overriding empty rule configurations on restart.
- Add context-aware "Add Default Rules" button to prepend default log levels relative to the current selection.
- Create sample.log matching each level for diagnostics.
Manually merged unique changes from all editor-related branches:

- editor-features: focus management (NoFocus on menuBar, focus restoration,
  focus-stealing prevention), Save/Save As/Save Copy As actions with toolbar
  button, Print action with toolbar button, font zoom controls (Ctrl++/Ctrl+-/
  Ctrl+0) with native KTE action fallback, Force RTL Direction toggle, Ctrl+Shift+S
  Save As shortcut precedence fix, QTimer-based focus restoration

- kate-case-conversion: Capitalization submenu replacing flat menu items,
  improved Title Case with minor-word handling, fixed Proper Case (toLower
  instead of preserve), renamed snail_case to snake_case, added PascalCase,
  SCREAMING_SNAKE_CASE, kebab-case, Sentence case, SCREAMING-KEBAB-CASE,
  dot.case, path/case, replaceSelectionPreservingRange helper to restore
  selection after text replacement

- fix-redo-and-write-lock: Ctrl+Shift+Z redo shortcut (in addition to Ctrl+Y),
  extracted toggleReadOnly() helper, Alt+Shift+R shortcut for read-only toggle,
  fixed double-trigger bug by switching QAction::toggled to QAction::triggered,
  added shortcut labels to read-only menu action
…agation

- Remove non-standard `lc_focus` definition from `sdk/wlxplugin.h` and its handler in `wlx_plugin.cpp`.

- Remove custom `hostSetFocus` method from `EditorWidget`.

- Set the editor view as the focus proxy for the main widget.

- Use native Qt event filtering and focus change listeners to handle active state transitions.
This makes the plugin standalone by compiling and linking libenca statically, removing the need for users to manually install libenca.so.0.
Adds ability to insert empty rows or clipboard rows above/below current selection or clicked row.
pplupo and others added 30 commits July 14, 2026 09:53
QPdfView's page-layout/coordinate-mapping internals are private API with
no public hook to map a click to a page + point-within-page, which meant
copy was limited to whole-page/whole-selection text with no way to
highlight or select an arbitrary run of text. MuPDF's structured-text API
(fz_highlight_selection / fz_copy_selection) exposes that directly.

- Add MuPdfWidget/MuPdfContainerWidget, mirroring LOKWidget/
  LOKContainerWidget's structure (continuous page stacking, only visible
  pages rendered, same FocusManager/zoom/wheel-event wiring) so all three
  rendering paths share one interface for ListSendCommand.
- Remove PdfViewerWidget and the Qt6::Pdf/PdfWidgets dependency entirely;
  replace the one QPdfDocument use (page counting for the per-sheet merge
  pipeline) with a small MuPDF-based helper.
- Fix tab-bar desync: sheetStartPages holds page *indices*, but three
  places compared it directly against the scrollbar's pixel position, so
  a few pixels of scroll could exceed a page-index number like 14 or 15
  and snap the active tab to the last sheet. Convert through
  pageYOffset()/pageAtY() so both sides of every comparison share units.
- Fix font aliasing on fractional-scaling displays: page rendering had no
  devicePixelRatio handling, so Qt stretched the under-resolution image up
  to fill the widget's real physical pixels. Render at
  zoom*devicePixelRatioF() and mark the QImage's devicePixelRatio so Qt
  draws it 1:1 instead of scaling it.

Links dynamically against the system libmupdf for now; static linking is
a separate follow-up.

Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Also update the compiled binary to match the current MuPDF-based build.

Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
rclone's Google Drive VFS mount leaves native Google Docs/Sheets/Slides
as 0-byte stub files on disk, since it can't materialize their content
transparently on read like a regular file. When ListLoad hits a 0-byte
file for a supported extension, it now checks /proc/mounts for a
covering fuse.rclone mount, and if found runs `rclone copyto` to export
the real content before handing it to the usual OOXML/ODF rendering
path. If the file isn't under an rclone mount, it fails gracefully
with a short message instead of trying to render nothing.

Google Drive exports get their own independently configurable engine
preference (EngineForGDrive), and any of the four Engines keys can now
be set to Disabled to skip that format family entirely.

The file-size-limit disable sentinel changes from 0 to -1, since 0 is
now a legitimate size for an unmaterialized rclone stub rather than
something to reject outright.

README updated to document all of the above.

Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
De-Qtify: extracted CsvCore (src/core/CsvCore.{h,cpp}) — the CSV/TSV
tokenizer (was a free function `parse_line()` already, just QString-typed)
and the field-escaping logic that was inline in saveFile() — as
std::string-based, toolkit-neutral functions. Rewired plugin.cpp's
parse_line() and saveFile() to call into CsvCore instead of duplicating
the algorithm inline; encoding conversion (EncodingUtils, still Qt-typed)
stays exactly where it was, only the tokenize/escape steps moved.
Preserves an existing quirk exactly: parse_line() still returns an empty
list when the (persisted) gQuoted setting is false, matching prior
behavior bit-for-bit rather than silently changing it.

Verified with a standalone functional test (not just syntax-checked):
simple fields, quoted fields containing the separator, doubled-quote
escaping, CRLF stripping, and a full parse->escape->join round-trip. All
passed. Also rebuilt csvview_qt6.wlx against the rewired plugin.cpp to
confirm the wiring itself compiles and links clean.

GTK3 port (src/gtk3/plugin_gtk3.cpp): deliberately scoped down from full
feature parity. csvview_qt6 is built on EditableGridWidget (QTableView +
QUndoStack), FocusManager, and ScopedFindReplacePanel from wlxbase_wlqt —
there is no wlxbase_gtk yet providing equivalent GTK primitives, and
building one properly (undo stack, focus/shortcut plumbing, a shared
find/replace panel) is a real separate investment, not something to
improvise inline for one plugin. This GTK3 build is a self-contained
GtkTreeView grid: load, inline cell editing, save, separator/header
toggles — but no undo/redo and no find/replace panel. Tracked as
follow-up, not silently dropped.

Verified: both csvview_qt6.wlx and csvview_gtk3.wlx build clean and
export the required WLX ABI symbols. Confirmed via ldd that
csvview_gtk3.wlx has ZERO Qt dependency and csvview_core carries zero Qt
symbols — third plugin in the suite (after mdk, diagramview) to actually
ship a pure-GTK binary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the self-contained (scoped-down) GTK3 grid from the previous
commit with a full build on wlxbase_gtk, bringing it to parity with the
Qt6 build's use of FocusManager/EditableGridWidget/ScopedFindReplacePanel:

- GtkFocusManager provides Ctrl+Z/Ctrl+Shift+Z/Ctrl+Y undo/redo shortcuts,
  Ctrl+F to toggle the find panel, and Ctrl+S to save.
- GtkEditableGridWidget replaces the plain GtkTreeView grid — every cell
  edit, row insert, and row delete now goes through the shared undo
  stack, and Ctrl+C copies the selection as tab-separated text via
  GtkClipboard.
- GtkScopedFindReplacePanel provides Find/Replace/Replace All with a
  scope selector ("All Cells" / "Current Column"), matching-case/
  entire-cell/regex options (std::regex, ECMAScript syntax) — wired up
  in this file's doFind/doReplace/doReplaceAll.

CsvCore (tokenizer/serializer) is unchanged from the previous commit.

Verified: full build (Qt6 + GTK3 + wlxbase_gtk all together) succeeds.
Confirmed via ldd that csvview_gtk3.wlx still has zero Qt dependency
despite the much larger feature set. wlxbase_gtk itself was verified
against a live GTK runtime (see its own commit) — undo/redo, insert
rows, and cell editing all confirmed working before being wired in here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tainer_add()

DC's GTK3 ResizeWindow (uwlxmodule.pas) calls GtkLayout.move() on the
widget this plugin returns from ListLoad(), which asserts the widget's
parent is exactly that GtkLayout -- a contract only gtk_layout_put()
satisfies. gtk_container_add() doesn't register the child that way, so
DC's later move/resize calls hit 'gtk_layout_move: assertion
gtk_widget_get_parent (child_widget) == GTK_WIDGET (layout) failed'
and, in some cases, a real crash further down the paint/layout path.

Found via real dcgtk testing against a from-source GTK3 Double Commander
build (v1.3.0 alpha, Lazarus 4.99) -- this only ever surfaces against a
genuine GtkLayout parent, not the synthetic dlopen/ListLoad test harness
used earlier this session, which passes a plain GtkWindow.
…lbar)

The GTK3 UI was written from scratch separately from the Qt6 build and
never reached feature parity with it -- confirmed via a side-by-side
screenshot comparison. Concretely, it was missing a row-number gutter
column entirely, had a manual delimiter dropdown that doesn't exist in
the Qt6 toolbar at all (Qt auto-detects the separator from file
content), and was missing most of the Qt6 toolbar: Save As, Undo,
Redo, Print, Reload, Show Text, Line Wrap, Open Externally (only Save
and Find/Replace existed).

Added:
  - Row-number gutter column (leading GtkTreeViewColumn with a cell
    data func rendering the model row index -- doesn't touch
    GtkEditableGridWidget itself, purely cosmetic in csvview's own
    code, since that widget is shared with dbview/structview).
  - Separator auto-detection matching the Qt6 build exactly: try ','
    ';' '\t' on the first line, keep the first yielding >1 columns,
    else fall back on the .tsv/.csv extension. Manual delimiter
    dropdown removed.
  - Separator/extension-mismatch dialog (Ignore / Fix Separator /
    Rename Extension), matching the Qt6 build's behavior when e.g. a
    .csv file turns out to be tab-separated.
  - Save As (GtkFileChooserDialog with CSV/TSV filters).
  - Undo/Redo toolbar buttons wired to GtkFocusManager's undo stack
    (already existed in GtkFocusManager, just wasn't exposed in the
    toolbar), with sensitivity kept in sync via canUndo()/canRedo().
  - Print via GtkPrintOperation, paginating a monospace tab-joined
    rendering of the visible rows -- simpler than the Qt6 build's HTML
    table render, but functionally equivalent (opens the system print
    dialog, prints the data).
  - Reload button.
  - Show Text toggle: a plain GtkTextView shown in place of the grid
    (unlike Qt6's colorized-by-column rendering, kept monochrome here
    -- scope reduction, not a missing feature parity point that
    matters functionally).
  - Line Wrap toggle, applied to the Show Text view (GtkEditableGridWidget
    doesn't expose a cell-wrap mode, so this doesn't affect the grid
    itself, same practical limitation as before).
  - Open Externally via g_app_info_launch_default_for_uri.
  - Dirty indicator label ("✓"/"●"), matching the Qt6 build's checkmark.

Also: reload now explicitly clears the undo stack (st->fm->clearUndoStack()) --
needed because GtkFocusManager owns the undo stack (not the grid), unlike
Qt6 where EditableGridWidget owns its own per-instance QUndoStack that a
reload implicitly replaces.

Verified: compiles clean in the gtk-dev container; dlopen+ListLoad+
ListSearchText smoke test against a real CSV file loads, finds text,
and closes cleanly (the GTK_LAYOUT cast warning from the synthetic
test harness's plain-GtkWindow parent is expected/unrelated, per the
gtk_layout_put commit on this and other plugin branches). Not yet
retested in the real dcgtk GTK3 Double Commander session.
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.

2 participants