vcl/osx/salframeview.mm | 111 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 4 deletions(-)
New commits: commit 07f1edfe9d2dd6aaf9c50fdb680f55ddecb96f38 Author: Patrick Luby <guibmac...@gmail.com> AuthorDate: Sat May 24 19:55:33 2025 -0400 Commit: Patrick Luby <guibomac...@gmail.com> CommitDate: Wed Jun 4 23:40:59 2025 +0200 tdf#128600 Implement handling of macOS "Reverse Conversion" menu item When a Japanese keyboard is selected, the keyboard's "Reverse Conversion" menu item would silently fail when nil was returned by the unimplemented -[SalFrameView attributedSubstringForProposedRange:actualRange:]. So return a valid string in that call using the following steps: 1. If there is marked text, return the last marked text 2. If LibreOffice is selected text, return the selected text Similar steps in the same order are in -[SalFrameView selectedRange]. Limitations: 1. "Reverse Conversion" with Japanese keyboards only edits the first non-whitespace chunk of selected text but I don't know how to adjust LibreOffice's selected range to match the substring that the input method will edit. This causes the entire LibreOffice selection to be overwritten by the substring so return nil if there is any whitespace in the selection. 2. "Reverse Conversion" with Japanese keyboards when there is no text selected should select the convertible text chunk nearest to the cursor but I don't know how to retrieve unselected text and set the selection to a chunk of text. Change-Id: Ic8377ed7fd9db687061548a79062cfa033df758a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185741 Tested-by: Jenkins Reviewed-by: Patrick Luby <guibomac...@gmail.com> diff --git a/vcl/osx/salframeview.mm b/vcl/osx/salframeview.mm index 7ea26a3d4fad..7dd9b64d12ca 100644 --- a/vcl/osx/salframeview.mm +++ b/vcl/osx/salframeview.mm @@ -32,6 +32,10 @@ #include <vcl/svapp.hxx> #include <vcl/window.hxx> #include <vcl/commandevent.hxx> +#include <vcl/toolkit/edit.hxx> + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/text/XTextRange.hpp> #include <osx/a11yfactory.h> #include <osx/salframe.h> @@ -330,6 +334,62 @@ static void updateWindowCollectionBehavior( const SalFrameStyleFlags nStyle, con [pNSWindow setCollectionBehavior: eCollectionBehavior]; } +static NSString* getCurrentSelection() +{ + SolarMutexGuard aGuard; + + // The following is needed for text fields in dialogs, etc. + vcl::Window *pWin = ImplGetSVData()->mpWinData->mpFocusWin; + if (pWin) + { + Edit *pEditWin = dynamic_cast<Edit*>(pWin); + if (pEditWin) + return [CreateNSString(pEditWin->GetSelected()) autorelease]; + } + + css::uno::Reference<css::frame::XDesktop> xDesktop = css::frame::Desktop::create(::comphelper::getProcessComponentContext()); + if (xDesktop.is()) + { + css::uno::Reference<css::frame::XModel> xModel(xDesktop->getCurrentComponent(), css::uno::UNO_QUERY); + if (xModel) + { + css::uno::Reference<css::uno::XInterface> xSelection(xModel->getCurrentSelection(), css::uno::UNO_QUERY); + if (xSelection) + { + css::uno::Reference<css::container::XIndexAccess> xIndexAccess(xSelection, css::uno::UNO_QUERY); + if (xIndexAccess.is()) + { + if (xIndexAccess->getCount() > 0) + { + css::uno::Reference<css::text::XTextRange> xTextRange(xIndexAccess->getByIndex(0), css::uno::UNO_QUERY); + if (xTextRange.is()) + return [CreateNSString(xTextRange->getString()) autorelease]; + } + } + + // The Basic IDE returns a XEnumeration with a single item + // Note: the following code was adapted from + // svx/source/tbxctrls/tbunosearchcontrollers.cxx + css::uno::Reference<css::container::XEnumeration> xEnum(xSelection, css::uno::UNO_QUERY); + if (xEnum.is() && xEnum->hasMoreElements()) + { + OUString aString; + xEnum->nextElement() >>= aString; + return [CreateNSString(aString) autorelease]; + } + + // The following is needed for cells and text fields in Calc + // and Impress + css::uno::Reference<css::text::XTextRange> xTextRange(xSelection, css::uno::UNO_QUERY); + if (xTextRange.is()) + return [CreateNSString(xTextRange->getString()) autorelease]; + } + } + } + + return nil; +} + @interface NSResponder (SalFrameWindow) -(BOOL)accessibilityIsIgnored; @end @@ -2305,6 +2365,20 @@ static void updateWindowCollectionBehavior( const SalFrameStyleFlags nStyle, con // NSNotFound, -[NSResponder interpretKeyEvents:] will not call // [self firstRectForCharacterRange:actualRange:] and will not display the // special character input method popup. + // tdf#128600 Implement handling of macOS "Reverse Conversion" menu item + // When a Japanese keyboard is selected, the keyboard's "Reverse Conversion" + // menu item would silently fail when an empty range was returned by + // -[SalFrameView selectedRange]. + // So return a valid range in that call using the following steps: + // 1. If there is marked text, return the marked text range + // 2. If LibreOffice is selected text, return the selected text length + // Similar steps in the same order are in + // -[SalFrameView attributedSubstringForProposedRange:actualRange:]. + if ( [self hasMarkedText] ) + return ( mMarkedRange.location == NSNotFound ? NSMakeRange( 0, 0 ) : mMarkedRange ); + NSString *pSelectedText = getCurrentSelection(); + if ( pSelectedText ) + return NSMakeRange( 0, [pSelectedText length] ); return ( mSelectedRange.location == NSNotFound ? NSMakeRange( 0, 0 ) : mSelectedRange ); } @@ -2442,10 +2516,39 @@ static void updateWindowCollectionBehavior( const SalFrameStyleFlags nStyle, con - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - (void) aRange; - (void) actualRange; - - // FIXME - Implement + (void)aRange; + + // tdf#128600 Implement handling of macOS "Reverse Conversion" menu item + // When a Japanese keyboard is selected, the keyboard's "Reverse Conversion" + // menu item would silently fail when nil was returned by the unimplemented + // -[SalFrameView attributedSubstringForProposedRange:actualRange:]. + // So return a valid string in that call using the following steps: + // 1. If there is marked text, return the last marked text + // 2. If LibreOffice is selected text, return the selected text + // Similar steps in the same order are in -[SalFrameView selectedRange]. + if ( [self hasMarkedText] ) + { + if ( actualRange ) + *actualRange = mMarkedRange; + return mpLastMarkedText; + } + NSString *pSelectedText = getCurrentSelection(); + if ( pSelectedText ) + { + // Related: tdf#128600 "Reverse Conversion" with Japanese keyboards + // only edits the first non-whitespace chunk of selected text but + // I don't know how to adjust LibreOffice's selected range to match + // the substring that the input method will edit. This causes the + // entire LibreOffice selection to be overwritten by the substring + // so return nil if there is any whitespace in the selection. + NSRange aWhitespaceRange = [pSelectedText rangeOfCharacterFromSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if ( aWhitespaceRange.location == NSNotFound ) + { + if ( actualRange ) + *actualRange = NSMakeRange( 0, [pSelectedText length] ); + return [[[NSAttributedString alloc] initWithString: pSelectedText] autorelease]; + } + } return nil; }