Skip to content

fix(cpn): prevent drag'n'drop and paste if incompatible boards#7376

Open
elecpower wants to merge 2 commits into
mainfrom
elecpower/cpn-add-board-checks-to-clipboard-functions
Open

fix(cpn): prevent drag'n'drop and paste if incompatible boards#7376
elecpower wants to merge 2 commits into
mainfrom
elecpower/cpn-add-board-checks-to-clipboard-functions

Conversation

@elecpower
Copy link
Copy Markdown
Collaborator

@elecpower elecpower commented May 17, 2026

Fixes #3950

Summary of changes:

  • add current board to clipboard metadata
  • check source and destination boards the same for menu paste count, paste and drop

Summary by CodeRabbit

  • Refactor
    • Improved model drag-and-drop: transfers now enforce board-type compatibility and more robust validation.
    • Updated model transfer format and parsing to ensure consistent, reliable counts and compatibility checks across drag/drop operations.

Review Change Stack

@elecpower elecpower added bug 🪲 Something isn't working companion Related to the companion software backport/2.12 To be backported to a 2.12 release also. labels May 17, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 79087609-31a9-45d8-9e5b-c8399a7a475c

📥 Commits

Reviewing files that changed from the base of the PR and between 6046e4b and 87c11ad.

📒 Files selected for processing (2)
  • companion/src/modelslist.cpp
  • companion/src/modelslist.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • companion/src/modelslist.cpp

📝 Walkthrough

Walkthrough

ModelsListModel drag-and-drop MIME header now includes board type and a version macro; model payloads no longer embed board. Incoming MIME is checked for board compatibility before counting or accepting model drops.

Changes

Board-aware MIME drag-and-drop validation

Layer / File(s) Summary
Header contract, macros and public declarations
companion/src/modelslist.h, companion/src/modelslist.cpp
Add MIME_HEADER_DATA_VERSION = 2. Extend MimeHeaderData with Board::Type board. Add getMimeDataBoard() and hasCompatibleBoardMimeData() declarations and change countModelsInMimeData(...) from static to an instance const method.
Header initialization and (de)serialization
companion/src/modelslist.cpp
Initialize mimeHeaderData.board from current board and version. encodeHeaderData() writes version, instanceId, then board. decodeHeaderData() reads board only when dataVersion >= 2, else sets BOARD_UNKNOWN.
Model payload format and parsing
companion/src/modelslist.cpp
encodeModelsData() stops writing board into payload; payload begins with model count. decodeMimeData() and countModelsInMimeData() read model count directly and require board compatibility before parsing model buffers.
Drop eligibility gating
companion/src/modelslist.cpp
canDropMimeData() now requires hasCompatibleBoardMimeData() in addition to existing MIME-format and target-position checks, preventing drops from incompatible boards.
Helpers: board extraction & compatibility
companion/src/modelslist.cpp
Add getMimeDataBoard() to extract board from MIME header and hasCompatibleBoardMimeData() to compare with the instance's stored board.

Sequence Diagram

sequenceDiagram
  participant Source as Source Instance
  participant SourceModel as Source ModelsListModel
  participant MIME as MIME Header
  participant TargetModel as Target ModelsListModel
  Source->>SourceModel: create mimeHeaderData {version=2, board}
  SourceModel->>MIME: encodeHeaderData(stream: version, instanceId, board)
  SourceModel->>Source: encodeModelsData(payload: mdlCnt, modelBuffers...)
  Source->>TargetModel: drag with MIME(header, payload)
  TargetModel->>MIME: decodeHeaderData()
  TargetModel->>TargetModel: getMimeDataBoard()
  TargetModel->>TargetModel: hasCompatibleBoardMimeData()? 
  alt compatible
    TargetModel->>TargetModel: countModelsInMimeData() -> read mdlCnt, parse models
    TargetModel->>TargetModel: accept drop
  else incompatible
    TargetModel->>TargetModel: reject drop
  end
Loading

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: preventing incompatible board operations during drag-and-drop and paste operations.
Description check ✅ Passed The description includes the issue reference and summarizes the key changes, though formatting is minimal.
Linked Issues check ✅ Passed The PR implements board compatibility checks for copy/paste and drag-and-drop across instances [#3950], aligning with the issue's primary objective.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing board compatibility validation for MIME data operations as specified in issue #3950.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch elecpower/cpn-add-board-checks-to-clipboard-functions

Comment @coderabbitai help to get the list of available commands and usage tips.

@elecpower elecpower changed the title fix(cpn): prevent drag'n'drop and paste of incompatible boards fix(cpn): prevent drag'n'drop and paste if incompatible boards May 17, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
companion/src/modelslist.cpp (1)

771-776: 💤 Low value

Consider checking decodeHeaderData return value.

If decodeHeaderData fails (no header present), header.board will be whatever the struct defaults to. This is safe if the struct is properly initialized (see earlier comment), but checking the return value makes intent explicit.

♻️ Optional defensive check
 Board::Type ModelsListModel::getMimeDataBoard(const QMimeData * mimeData) const
 {
   MimeHeaderData header;
-  decodeHeaderData(mimeData, &header);
+  if (!decodeHeaderData(mimeData, &header))
+    return Board::BOARD_UNKNOWN;
   return header.board;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@companion/src/modelslist.cpp` around lines 771 - 776, In
ModelsListModel::getMimeDataBoard, call decodeHeaderData(mimeData, &header) and
check its return value; if it returns false, return a safe default (e.g., a
default-constructed Board::Type or a known sentinel such as Board::Invalid)
instead of unconditionally returning header.board—only return header.board when
decodeHeaderData succeeds to make the intent explicit and defensive.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@companion/src/modelslist.cpp`:
- Line 519: The code unconditionally extracts header->board after reading
header->dataVersion and header->instanceId, which breaks on v1 headers that lack
board; modify the reader in modelslist.cpp to first read header->dataVersion and
header->instanceId, check header->dataVersion (e.g., if >= 2) before attempting
to extract header->board, and for older versions set header->board to a safe
default; also validate the stream state after each extraction (or before
returning true) so failures set the stream/error and the function returns false
instead of leaving header->board uninitialized.

In `@companion/src/modelslist.h`:
- Around line 83-87: The MimeHeaderData struct members should be initialized to
safe defaults to avoid uninitialized `board` when decodeHeaderData fails or
parses older headers; add default member initializers in the MimeHeaderData
definition (e.g., set instanceId to QUuid(), dataVersion to 0, and board to
Board::Type() or an explicit sentinel) so getMimeDataBoard and
hasCompatibleBoardMimeData see a well-defined value; if there are constructors
that set these fields, ensure they also initialize all three members.

---

Nitpick comments:
In `@companion/src/modelslist.cpp`:
- Around line 771-776: In ModelsListModel::getMimeDataBoard, call
decodeHeaderData(mimeData, &header) and check its return value; if it returns
false, return a safe default (e.g., a default-constructed Board::Type or a known
sentinel such as Board::Invalid) instead of unconditionally returning
header.board—only return header.board when decodeHeaderData succeeds to make the
intent explicit and defensive.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a4cb4501-ad66-4b64-8dfd-ba39527f0b4b

📥 Commits

Reviewing files that changed from the base of the PR and between 00d7544 and 6046e4b.

📒 Files selected for processing (2)
  • companion/src/modelslist.cpp
  • companion/src/modelslist.h

Comment thread companion/src/modelslist.cpp Outdated
Comment thread companion/src/modelslist.h
@elecpower elecpower added this to the 3.0 milestone May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport/2.12 To be backported to a 2.12 release also. bug 🪲 Something isn't working companion Related to the companion software

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Companion support radio profile protection for copy/paste and drag'n'drop across multiple instances

1 participant