R5.5: ElyTable right-click menus, Excel-style; three rounds used, last fixes tested

Grid and header menus share ely_office::menu with ElyDoc. Each item's
availability is the kernel's state for the exact command it runs
(patch 0045). Patch 0046 lets a header select its row or column while
a shape is selected or its text is edited: the view shell ends the
drawing edit, keeps the typed text, leaves drawing mode, then marks.
Ely no longer sends Esc first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-14 13:02:10 -04:00
1 parent 07c0b43542
commit 87886c8a8d
30 files changed
+1010 -106

No files matched your search

@@ -0,0 +1,86 @@
LOK: read whether listed commands may run now
LOK pushes enable states only for a fixed list, and only after
idle bindings update. A menu opened right after a selection needs
the answer now, for commands outside that list (hide rows, column
width, clear contents). getCommandValues(".uno:CommandStates?
commands=.uno:A,.uno:B") flushes the dispatcher and asks it for
each slot, as the menu bar would: true unless disabled or unknown
to the current shells. A name that is no slot fails the read.
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -152,6 +152,7 @@
#include <sfx2/docfile.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/viewfrm.hxx>
+#include <sfx2/msg.hxx>
#include <sfx2/msgpool.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/lokcomponenthelpers.hxx>
@@ -6672,6 +6673,45 @@ enum class UndoOrRedo
}
+/// Whether each listed command may run now.
+static char* getCommandStates(std::string_view aList)
+{
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (!pViewShell)
+ {
+ SetLastExceptionMsg(u"CommandStates: no current view"_ustr);
+ return nullptr;
+ }
+ SfxViewFrame& rFrame = pViewShell->GetViewFrame();
+ SfxDispatcher* pDispatcher = rFrame.GetBindings().GetDispatcher();
+ pDispatcher->Flush();
+ const SfxSlotPool& rPool = SfxSlotPool::GetSlotPool(&rFrame);
+ tools::JsonWriter aJson;
+ {
+ auto aStates = aJson.startNode("commandStates");
+ sal_Int32 nIndex = 0;
+ do
+ {
+ std::string_view aCommand = o3tl::getToken(aList, 0, ',', nIndex);
+ if (!aCommand.starts_with(".uno:"))
+ {
+ SetLastExceptionMsg(u"CommandStates: not a .uno: command"_ustr);
+ return nullptr;
+ }
+ const SfxSlot* pSlot = rPool.GetUnoSlot(OUString::fromUtf8(aCommand.substr(5)));
+ if (!pSlot)
+ {
+ SetLastExceptionMsg(OUString::fromUtf8(aCommand) + u" : unknown command"_ustr);
+ return nullptr;
+ }
+ SfxPoolItemHolder aState;
+ const SfxItemState eState = pDispatcher->QueryState(pSlot->GetSlotId(), aState);
+ aJson.put(aCommand, eState != SfxItemState::DISABLED && eState != SfxItemState::UNKNOWN);
+ } while (nIndex >= 0);
+ }
+ return convertOString(aJson.finishAndGetAsOString());
+}
+
/// Returns the JSON representation of either an undo or a redo stack.
static char* getUndoOrRedo(LibreOfficeKitDocument* pThis, UndoOrRedo eCommand)
{
@@ -6808,6 +6848,7 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
static constexpr std::string_view aViewRowColumnHeaders(".uno:ViewRowColumnHeaders");
static constexpr std::string_view aSheetGeometryData(".uno:SheetGeometryData");
static constexpr std::string_view aFontSubset(".uno:FontSubset&name=");
+ static constexpr std::string_view aCommandStates(".uno:CommandStates?commands=");
ITiledRenderable* pDoc = getTiledRenderable(pThis);
if (!pDoc)
@@ -6983,6 +7024,10 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
pDoc->getCellCursor(aJsonWriter);
return convertOString(aJsonWriter.finishAndGetAsOString());
}
+ else if (aCommand.starts_with(aCommandStates))
+ {
+ return getCommandStates(aCommand.substr(aCommandStates.size()));
+ }
else if (aCommand.starts_with(aFontSubset))
{
return getFontSubset(aCommand.substr(aFontSubset.size()));
@@ -0,0 +1,83 @@
LOK: selecting a row or column leaves a drawing first
Calc's own headers call DrawDeselectAll before marking. Over LOK the
header is the client's, and .uno:SelectRow / .uno:SelectColumn live
only on the cell shell. While a shape is selected or its text is
being edited, a drawing shell replaces the cell shell, so the command
finds no slot and is dropped. The view shell now takes both slots in
LOK: it ends any drawing edit (keeping the typed text), deselects,
ends drawing mode as Escape does (rerunning the active tool's slot),
then marks, as a header click does. Desktop keeps them on the cell
shell only.
diff --git a/sc/sdi/tabvwsh.sdi b/sc/sdi/tabvwsh.sdi
--- a/sc/sdi/tabvwsh.sdi
+++ b/sc/sdi/tabvwsh.sdi
@@ -42,6 +42,8 @@
FID_TABLE_HIDE [ ExecMethod = ExecuteTable; StateMethod = GetStateTable; ]
FID_TABLE_SHOW [ ExecMethod = ExecuteTable; StateMethod = GetStateTable; ]
SID_SELECT_TABLES [ ExecMethod = Execute; StateMethod = GetState; ]
+ SID_SELECT_COL [ ExecMethod = Execute; StateMethod = GetState; ]
+ SID_SELECT_ROW [ ExecMethod = Execute; StateMethod = GetState; ]
FID_TAB_MENU_SET_TAB_BG_COLOR [ ExecMethod = ExecuteTable; StateMethod = GetStateTable; ]
FID_TAB_EVENTS [ ExecMethod = ExecuteTable; StateMethod = GetStateTable; ]
}
diff --git a/sc/source/ui/view/tabvwsh3.cxx b/sc/source/ui/view/tabvwsh3.cxx
--- a/sc/source/ui/view/tabvwsh3.cxx
+++ b/sc/source/ui/view/tabvwsh3.cxx
@@ -45,6 +45,7 @@
#include <reffact.hxx>
#include <tabprotection.hxx>
#include <protectiondlg.hxx>
+#include <fupoor.hxx>
#include <kitsheet.hxx>
#include <docfunc.hxx>
#include <boost/property_tree/json_parser.hpp>
@@ -1304,6 +1305,31 @@
rReq.Done();
break;
+ // A drawing shell is on top: leave it, then mark.
+ case SID_SELECT_COL:
+ case SID_SELECT_ROW:
+ {
+ const SfxInt32Item* pIndex = rReq.GetArg<SfxInt32Item>(FN_PARAM_1);
+ const SfxInt16Item* pModifier = rReq.GetArg<SfxInt16Item>(FN_PARAM_2);
+ DrawDeselectAll();
+ // End drawing mode as Escape does: rerun the tool's slot.
+ if (FuPoor* pFunc = GetDrawFuncPtr(); pFunc && IsDrawSelMode())
+ GetViewData().GetDispatcher().Execute(pFunc->GetSlotID(), SfxCallMode::SLOT | SfxCallMode::RECORD);
+ if (nSlot == SID_SELECT_COL)
+ {
+ if (pIndex && pModifier)
+ MarkColumns(static_cast<SCCOL>(pIndex->GetValue()), pModifier->GetValue());
+ else
+ MarkColumns();
+ }
+ else if (pIndex && pModifier)
+ MarkRows(static_cast<SCROW>(pIndex->GetValue()), pModifier->GetValue());
+ else
+ MarkRows();
+ rReq.Done();
+ break;
+ }
+
case SID_SELECT_TABLES:
{
ScViewData& rViewData = GetViewData();
diff --git a/sc/source/ui/view/tabvwsha.cxx b/sc/source/ui/view/tabvwsha.cxx
--- a/sc/source/ui/view/tabvwsha.cxx
+++ b/sc/source/ui/view/tabvwsha.cxx
@@ -553,6 +553,12 @@
rSet.DisableItem( nWhich ); // enabled only if several sheets are selected
break;
+ case SID_SELECT_COL:
+ case SID_SELECT_ROW:
+ if (!comphelper::LibreOfficeKit::isActive())
+ rSet.DisableItem( nWhich ); // desktop: the cell shell owns them
+ break;
+
case FID_TOGGLEHIDDENCOLROW:
const svtools::ColorConfig& rColorCfg = ScModule::get()->GetColorConfig();
rSet.Put( SfxBoolItem( nWhich, rColorCfg.GetColorValue(svtools::CALCHIDDENROWCOL).bIsVisible) );