// drag_out — pure implementation. See drag_out.h. #include "core/ui/drag_out.h" #include namespace reasampler::ui { namespace { bool insideClient(int px, int py, const PanelClientRect& c) { return px >= c.x && px < c.x + c.width && py >= c.y && py < c.y + c.height; } } // namespace bool needsSurfaceProbe(int px, int py, const PanelClientRect& client) { return !insideClient(px, py, client); } DropClass decideDropClass(int px, int py, const PanelClientRect& client, const DropContext& ctx) { if (!ctx.drag.dragging || !ctx.drag.hasArmedSamples) return DropClass::None; if (insideClient(px, py, client)) return DropClass::Internal; // The ONE route to OsHandoff, ahead of the surface switch, and it needs BOTH halves. The // shell's ownership proof alone is not sufficient: REAPER runs a bridged plugin's UI in a // separate process, so a window failing that test can still be REAPER's own UI. A recognised // token therefore VETOES the hand-off instead of producing one — a veto can only move the // answer toward staying inside REAPER, so pointerOffHost remains the only member whose true // value can reach OLE. An off-host point REAPER DID name falls through to that surface's own // outcome below: over a bridged FX window that is the instrument drop, as it is for a native // one. if (ctx.pointerOffHost && ctx.surface == ReaperSurface::Other && !ctx.haveTrack) return DropClass::OsHandoff; switch (ctx.surface) { case ReaperSurface::TrackPanel: case ReaperSurface::FxSurface: // One instance holds one capture, so a multi payload names no instrument to build — // it refuses with a cue rather than falling through to some other surface's outcome. return (ctx.singlePayload && ctx.haveTrack) ? DropClass::InstrumentDrop : DropClass::Refuse; case ReaperSurface::Arrange: // GetThingFromPoint may return a null track with a valid info string (the SDK says // so): over the arrange that is the region below the last track, which names no lane // to place on. Refuse rather than guess a track. return ctx.haveTrack ? DropClass::ArrangeInsert : DropClass::Refuse; case ReaperSurface::FxEmbed: case ReaperSurface::Other: return DropClass::Refuse; case ReaperSurface::Count: break; // the sentinel is not a surface; it falls to the refusal below } return DropClass::Refuse; // an unclassifiable surface still refuses visibly, never silently } DropCue cueForDropClass(DropClass cls) { switch (cls) { case DropClass::Internal: return DropCue::Internal; case DropClass::InstrumentDrop: return DropCue::Instrument; case DropClass::ArrangeInsert: return DropCue::ArrangeInsert; case DropClass::Refuse: return DropCue::Refuse; case DropClass::OsHandoff: return DropCue::OsOwned; case DropClass::None: return DropCue::None; } return DropCue::None; } PathList assemblePathList(const std::vector& resolved) { PathList out; std::unordered_set seen; seen.reserve(resolved.size()); for (const ResolvedSample& s : resolved) { if (s.absolutePath.empty()) { ++out.skippedUnresolved; continue; } if (!s.fileExists) { ++out.skippedMissing; continue; } if (!seen.insert(s.absolutePath).second) { ++out.skippedDuplicate; continue; } out.paths.push_back(s.absolutePath); } return out; } OsHandoff decideOsHandoff(const std::vector& paths) { OsHandoff out; out.handOffToOs = !paths.empty(); return out; } } // namespace reasampler::ui