@@ -1100,6 +1100,13 @@ LRESULT CALLBACK ResultDock::sciSubclassProc(HWND hwnd, UINT msg, WPARAM wp, LPA
1100
1100
::SendMessage (nppData._nppHandle, NPPM_DMMHIDE, 0 , (LPARAM)ResultDock::instance()._hDock);
1101
1101
return TRUE ;
1102
1102
1103
+ case WM_KEYDOWN:
1104
+ if (wp == VK_DELETE) {
1105
+ deleteSelectedItems (hwnd);
1106
+ return 0 ; // eat the key
1107
+ }
1108
+ break ;
1109
+
1103
1110
case WM_NCDESTROY:
1104
1111
s_prevSciProc = nullptr ;
1105
1112
break ;
@@ -1121,12 +1128,17 @@ LRESULT CALLBACK ResultDock::sciSubclassProc(HWND hwnd, UINT msg, WPARAM wp, LPA
1121
1128
::AppendMenuW (hMenu, flags, id, txt.c_str());
1122
1129
};
1123
1130
1131
+ const bool hasSel =
1132
+ ::SendMessage (hwnd, SCI_GETSELECTIONSTART, 0 , 0 ) !=
1133
+ ::SendMessage(hwnd, SCI_GETSELECTIONEND, 0 , 0 );
1134
+
1124
1135
// -------- exact order & separators (matches screenshot) ----------------
1125
1136
1126
1137
add (IDM_RD_FOLD_ALL, L" rdmenu_fold_all" );
1127
1138
add (IDM_RD_UNFOLD_ALL, L" rdmenu_unfold_all" );
1128
1139
::AppendMenuW (hMenu, MF_SEPARATOR, 0 , nullptr );
1129
-
1140
+ UINT copyFlags = MF_STRING | (hasSel ? 0 : MF_GRAYED);
1141
+ add (IDM_RD_COPY_STD, L" rdmenu_copy_std" , copyFlags);
1130
1142
add (IDM_RD_COPY_LINES, L" rdmenu_copy_lines" );
1131
1143
add (IDM_RD_COPY_PATHS, L" rdmenu_copy_paths" );
1132
1144
add (IDM_RD_SELECT_ALL, L" rdmenu_select_all" );
@@ -1161,6 +1173,10 @@ LRESULT CALLBACK ResultDock::sciSubclassProc(HWND hwnd, UINT msg, WPARAM wp, LPA
1161
1173
::SendMessage (hwnd, SCI_FOLDALL, SC_FOLDACTION_EXPAND, 0 );
1162
1174
break ;
1163
1175
1176
+ case IDM_RD_COPY_STD:
1177
+ ::SendMessage (hwnd, SCI_COPY, 0 , 0 );
1178
+ break ;
1179
+
1164
1180
// ── select / clear ────────────────────────────
1165
1181
case IDM_RD_SELECT_ALL:
1166
1182
::SendMessage (hwnd, SCI_SELECTALL, 0 , 0 );
@@ -1286,7 +1302,6 @@ namespace {
1286
1302
} // anonymous namespace
1287
1303
1288
1304
1289
-
1290
1305
// ==========================================================================
1291
1306
// copySelectedLines (IDM_RD_COPY_LINES)
1292
1307
// ==========================================================================
@@ -1459,7 +1474,6 @@ void ResultDock::copySelectedPaths(HWND hSci) {
1459
1474
}
1460
1475
1461
1476
1462
-
1463
1477
// --------------------------------------------------------------------------
1464
1478
// openSelectedPaths – helper for IDM_RD_OPEN_PATHS
1465
1479
// --------------------------------------------------------------------------
@@ -1496,3 +1510,154 @@ void ResultDock::openSelectedPaths(HWND hSci) {
1496
1510
}
1497
1511
}
1498
1512
}
1513
+
1514
+ // --------------------------------------------------------------------------
1515
+ // deleteSelectedItems – remove selected lines incl. subordinate hierarchy
1516
+ // --------------------------------------------------------------------------
1517
+ void ResultDock::deleteSelectedItems (HWND hSci)
1518
+ {
1519
+ auto & dock = ResultDock::instance ();
1520
+
1521
+ // ------------------------------------------------------------------
1522
+ // Determine anchor / caret lines
1523
+ // ------------------------------------------------------------------
1524
+ Sci_Position a = ::SendMessage (hSci, SCI_GETSELECTIONNANCHOR, 0 , 0 );
1525
+ Sci_Position c = ::SendMessage (hSci, SCI_GETCURRENTPOS, 0 , 0 );
1526
+ if (a > c) std::swap (a, c);
1527
+
1528
+ int firstLine = (int )::SendMessage (hSci, SCI_LINEFROMPOSITION, a, 0 );
1529
+ int lastLine = (int )::SendMessage (hSci, SCI_LINEFROMPOSITION, c, 0 );
1530
+ bool hasSel = (a != c);
1531
+
1532
+ // ------------------------------------------------------------------
1533
+ // Build list of display‑line ranges that must be deleted
1534
+ // ------------------------------------------------------------------
1535
+ struct DelRange { int first; int last; };
1536
+ std::vector<DelRange> ranges;
1537
+
1538
+ auto subtreeEnd = [&](int fromLine, int minIndent) -> int
1539
+ {
1540
+ int total = (int )::SendMessage (hSci, SCI_GETLINECOUNT, 0 , 0 );
1541
+ for (int l = fromLine; l < total; ++l)
1542
+ {
1543
+ int len = (int )::SendMessage (hSci, SCI_LINELENGTH, l, 0 );
1544
+ std::string raw (len, ' \0 ' );
1545
+ ::SendMessage (hSci, SCI_GETLINE, l, (LPARAM)raw.data());
1546
+ raw.resize (strnlen (raw.c_str (), len));
1547
+
1548
+ int indent = ResultDock::leadingSpaces (raw.c_str (), (int )raw.size ());
1549
+ if (indent <= minIndent && classify (raw) != LineKind::HitLine)
1550
+ return l - 1 ; // previous line ends subtree
1551
+ }
1552
+ return (int )::SendMessage (hSci, SCI_GETLINECOUNT, 0 , 0 ) - 1 ; // EOF
1553
+ };
1554
+
1555
+ auto pushRange = [&](int first, int last)
1556
+ {
1557
+ if (ranges.empty () || first > ranges.back ().last + 1 )
1558
+ ranges.push_back ({ first, last });
1559
+ else
1560
+ ranges.back ().last = (std::max)(ranges.back ().last , last);
1561
+ };
1562
+
1563
+ // ------------------------------------------------------------------
1564
+ // Selection mode: collect every header / hit and expand hierarchy
1565
+ // ------------------------------------------------------------------
1566
+ if (hasSel)
1567
+ {
1568
+ for (int l = firstLine; l <= lastLine; ++l)
1569
+ {
1570
+ // skip lines already included by previous pushRange
1571
+ if (!ranges.empty () && l <= ranges.back ().last ) continue ;
1572
+
1573
+ int len = (int )::SendMessage (hSci, SCI_LINELENGTH, l, 0 );
1574
+ std::string raw (len, ' \0 ' );
1575
+ ::SendMessage (hSci, SCI_GETLINE, l, (LPARAM)raw.data());
1576
+ raw.resize (strnlen (raw.c_str (), len));
1577
+
1578
+ LineKind kind = classify (raw);
1579
+ int endLine = l; // default: only this line
1580
+
1581
+ switch (kind)
1582
+ {
1583
+ case LineKind::HitLine: endLine = l; break ;
1584
+ case LineKind::CritHdr: endLine = subtreeEnd (l + 1 , 8 ); break ;
1585
+ case LineKind::FileHdr: endLine = subtreeEnd (l + 1 , 4 ); break ;
1586
+ case LineKind::SearchHdr: endLine = subtreeEnd (l + 1 , 0 ); break ;
1587
+ default : continue ; // ignore blanks
1588
+ }
1589
+ pushRange (l, endLine);
1590
+ }
1591
+ }
1592
+ else
1593
+ {
1594
+ // ------------------------------------------------------------------
1595
+ // Caret‑only mode: single logical block
1596
+ // ------------------------------------------------------------------
1597
+ int len = (int )::SendMessage (hSci, SCI_LINELENGTH, firstLine, 0 );
1598
+ std::string raw (len, ' \0 ' );
1599
+ ::SendMessage (hSci, SCI_GETLINE, firstLine, (LPARAM)raw.data());
1600
+ raw.resize (strnlen (raw.c_str (), len));
1601
+
1602
+ LineKind kind = classify (raw);
1603
+ int endLine = firstLine;
1604
+
1605
+ switch (kind)
1606
+ {
1607
+ case LineKind::HitLine: endLine = firstLine; break ;
1608
+ case LineKind::CritHdr: endLine = subtreeEnd (firstLine + 1 , 8 ); break ;
1609
+ case LineKind::FileHdr: endLine = subtreeEnd (firstLine + 1 , 4 ); break ;
1610
+ case LineKind::SearchHdr: endLine = subtreeEnd (firstLine + 1 , 0 ); break ;
1611
+ default : return ; // nothing deletable on this line
1612
+ }
1613
+ ranges.push_back ({ firstLine, endLine });
1614
+ }
1615
+
1616
+ if (ranges.empty ())
1617
+ return ;
1618
+
1619
+ // ------------------------------------------------------------------
1620
+ // Delete ranges bottom‑up and update _hits offsets
1621
+ // ------------------------------------------------------------------
1622
+ ::SendMessage (hSci, SCI_SETREADONLY, FALSE , 0 );
1623
+
1624
+ for (auto it = ranges.rbegin (); it != ranges.rend (); ++it)
1625
+ {
1626
+ int fLine = it->first ;
1627
+ int lLine = it->last ;
1628
+
1629
+ Sci_Position start = ::SendMessage (hSci, SCI_POSITIONFROMLINE, fLine , 0 );
1630
+ Sci_Position end = (lLine + 1 < ::SendMessage (hSci, SCI_GETLINECOUNT, 0 , 0 ))
1631
+ ? ::SendMessage (hSci, SCI_POSITIONFROMLINE, lLine + 1 , 0 )
1632
+ : ::SendMessage (hSci, SCI_GETLENGTH, 0 , 0 );
1633
+
1634
+ Sci_Position delta = end - start;
1635
+ if (delta <= 0 ) continue ;
1636
+
1637
+ // ---- remove from Scintilla buffer
1638
+ ::SendMessage (hSci, SCI_DELETERANGE, start, delta);
1639
+
1640
+ // ---- sync internal hit list
1641
+ for (auto h = dock._hits .begin (); h != dock._hits .end (); )
1642
+ {
1643
+ if (h->displayLineStart >= start && h->displayLineStart < end)
1644
+ {
1645
+ h = dock._hits .erase (h); // entry deleted
1646
+ }
1647
+ else
1648
+ {
1649
+ if (h->displayLineStart >= end)
1650
+ h->displayLineStart -= (int )delta; // shift left
1651
+ ++h;
1652
+ }
1653
+ }
1654
+ }
1655
+
1656
+ ::SendMessage (hSci, SCI_SETREADONLY, TRUE , 0 );
1657
+
1658
+ // ------------------------------------------------------------------
1659
+ // Re‑fold and re‑style remaining lines
1660
+ // ------------------------------------------------------------------
1661
+ dock.rebuildFolding ();
1662
+ dock.applyStyling ();
1663
+ }
0 commit comments