1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <classes/fwkresid.hxx> 21 #include <services.h> 22 #include <strings.hrc> 23 #include <vcl/svapp.hxx> 24 #include <vcl/window.hxx> 25 #include <vcl/status.hxx> 26 #include <toolkit/helper/convert.hxx> 27 28 #include <cppuhelper/supportsservice.hxx> 29 #include <com/sun/star/awt/PopupMenu.hpp> 30 #include <com/sun/star/awt/PopupMenuDirection.hpp> 31 #include <svtools/langtab.hxx> 32 #include <svtools/statusbarcontroller.hxx> 33 #include <sal/types.h> 34 #include <sal/log.hxx> 35 #include <com/sun/star/awt/MenuItemStyle.hpp> 36 #include <com/sun/star/document/XDocumentLanguages.hpp> 37 #include <com/sun/star/lang/XServiceInfo.hpp> 38 #include <com/sun/star/frame/ModuleManager.hpp> 39 #include <i18nlangtag/mslangid.hxx> 40 #include <com/sun/star/i18n/ScriptType.hpp> 41 42 #include <com/sun/star/frame/XFrame.hpp> 43 44 #include <tools/gen.hxx> 45 #include <com/sun/star/awt/Command.hpp> 46 #include <svl/languageoptions.hxx> 47 48 #include <helper/mischelper.hxx> 49 50 #include <rtl/ustrbuf.hxx> 51 #include <rtl/ref.hxx> 52 53 #include <stdtypes.h> 54 55 #include <map> 56 #include <set> 57 58 using namespace ::cppu; 59 using namespace ::com::sun::star; 60 using namespace css::uno; 61 using namespace css::lang; 62 using namespace css::frame; 63 using namespace css::i18n; 64 using namespace css::document; 65 using namespace framework; 66 67 namespace { 68 69 class LangSelectionStatusbarController: 70 public svt::StatusbarController 71 { 72 public: 73 explicit LangSelectionStatusbarController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); 74 LangSelectionStatusbarController(const LangSelectionStatusbarController&) = delete; 75 LangSelectionStatusbarController& operator=(const LangSelectionStatusbarController&) = delete; 76 77 // XInitialization 78 virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; 79 80 // XStatusListener 81 virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; 82 83 // XStatusbarController 84 virtual void SAL_CALL command( const css::awt::Point& aPos, 85 ::sal_Int32 nCommand, 86 sal_Bool bMouseEvent, 87 const css::uno::Any& aData ) override; 88 virtual void SAL_CALL click( const css::awt::Point& aPos ) override; 89 90 private: 91 virtual ~LangSelectionStatusbarController() override {} 92 93 bool m_bShowMenu; // if the menu is to be displayed or not (depending on the selected object/text) 94 SvtScriptType m_nScriptType; // the flags for the different script types available in the selection, LATIN = 0x0001, ASIAN = 0x0002, COMPLEX = 0x0004 95 OUString m_aCurLang; // the language of the current selection, "*" if there are more than one languages 96 OUString m_aKeyboardLang; // the keyboard language 97 OUString m_aGuessedTextLang; // the 'guessed' language for the selection, "" if none could be guessed 98 LanguageGuessingHelper m_aLangGuessHelper; 99 100 /// @throws css::uno::RuntimeException 101 void LangMenu( const css::awt::Point& aPos ); 102 }; 103 104 LangSelectionStatusbarController::LangSelectionStatusbarController( const uno::Reference< uno::XComponentContext >& xContext ) : 105 svt::StatusbarController( xContext, uno::Reference< frame::XFrame >(), OUString(), 0 ), 106 m_bShowMenu( true ), 107 m_nScriptType( SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX ), 108 m_aLangGuessHelper( xContext ) 109 { 110 } 111 112 void SAL_CALL LangSelectionStatusbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) 113 { 114 SolarMutexGuard aSolarMutexGuard; 115 116 svt::StatusbarController::initialize( aArguments ); 117 118 if ( m_xStatusbarItem.is() ) 119 { 120 m_xStatusbarItem->setText( FwkResId(STR_LANGSTATUS_MULTIPLE_LANGUAGES) ); 121 m_xStatusbarItem->setQuickHelpText(FwkResId(STR_LANGSTATUS_HINT)); 122 } 123 } 124 125 void LangSelectionStatusbarController::LangMenu( 126 const css::awt::Point& aPos ) 127 { 128 if (!m_bShowMenu) 129 return; 130 131 const Reference<XServiceInfo> xService(m_xFrame->getController()->getModel(), UNO_QUERY); 132 bool bWriter = xService.is() && xService->supportsService("com.sun.star.text.GenericTextDocument"); 133 //add context menu 134 Reference< awt::XPopupMenu > xPopupMenu( awt::PopupMenu::create( m_xContext ) ); 135 //sub menu that contains all items except the last two items: Separator + Set Language for Paragraph 136 Reference< awt::XPopupMenu > subPopupMenu( awt::PopupMenu::create( m_xContext ) ); 137 138 // get languages to be displayed in the menu 139 std::set< OUString > aLangItems; 140 FillLangItems( aLangItems, m_xFrame, m_aLangGuessHelper, 141 m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedTextLang ); 142 143 // add first few entries to main menu 144 sal_Int16 nItemId = static_cast< sal_Int16 >(MID_LANG_SEL_1); 145 const OUString sAsterisk("*"); // multiple languages in current selection 146 const OUString sNone( SvtLanguageTable::GetLanguageString( LANGUAGE_NONE )); 147 std::map< sal_Int16, OUString > aLangMap; 148 for (auto const& langItem : aLangItems) 149 { 150 if ( langItem != sNone && 151 langItem != sAsterisk && 152 !langItem.isEmpty()) // 'no language found' from language guessing 153 { 154 SAL_WARN_IF( MID_LANG_SEL_1 > nItemId || nItemId > MID_LANG_SEL_9, 155 "fwk.uielement", "nItemId outside of expected range!" ); 156 xPopupMenu->insertItem( nItemId, langItem, 0, nItemId ); 157 if ( langItem == m_aCurLang ) 158 { 159 //make a sign for the current language 160 xPopupMenu->checkItem( nItemId, true ); 161 } 162 aLangMap[ nItemId ] = langItem; 163 ++nItemId; 164 } 165 } 166 167 if (bWriter) 168 { 169 xPopupMenu->insertItem( MID_LANG_SEL_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_SEL_NONE ); 170 if ( sNone == m_aCurLang ) 171 xPopupMenu->checkItem( MID_LANG_SEL_NONE, true ); 172 xPopupMenu->insertItem( MID_LANG_SEL_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_SEL_RESET ); 173 xPopupMenu->insertItem( MID_LANG_SEL_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_SEL_MORE ); 174 175 // add entries to submenu ('set language for paragraph') 176 nItemId = static_cast< sal_Int16 >(MID_LANG_PARA_1); 177 for (auto const& langItem : aLangItems) 178 { 179 if( langItem != sNone && 180 langItem != sAsterisk && 181 !langItem.isEmpty()) // 'no language found' from language guessing 182 { 183 SAL_WARN_IF( MID_LANG_PARA_1 > nItemId || nItemId > MID_LANG_PARA_9, 184 "fwk.uielement", "nItemId outside of expected range!" ); 185 subPopupMenu->insertItem( nItemId, langItem, 0, nItemId ); 186 aLangMap[nItemId] = langItem; 187 ++nItemId; 188 } 189 } 190 subPopupMenu->insertItem( MID_LANG_PARA_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_PARA_NONE ); 191 subPopupMenu->insertItem( MID_LANG_PARA_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_PARA_RESET ); 192 subPopupMenu->insertItem( MID_LANG_PARA_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_PARA_MORE ); 193 194 // add last two entries to main menu 195 xPopupMenu->insertSeparator( MID_LANG_PARA_SEPARATOR ); 196 xPopupMenu->insertItem( MID_LANG_PARA_STRING, FwkResId(STR_SET_LANGUAGE_FOR_PARAGRAPH), 0, MID_LANG_PARA_STRING ); 197 xPopupMenu->setPopupMenu( MID_LANG_PARA_STRING, subPopupMenu ); 198 } 199 else 200 { 201 xPopupMenu->insertItem( MID_LANG_DEF_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_DEF_NONE ); 202 if ( sNone == m_aCurLang ) 203 xPopupMenu->checkItem( MID_LANG_DEF_NONE, true ); 204 xPopupMenu->insertItem( MID_LANG_DEF_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_DEF_RESET ); 205 xPopupMenu->insertItem( MID_LANG_DEF_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_DEF_MORE ); 206 } 207 208 // now display the popup menu and execute every command ... 209 210 Reference< awt::XWindowPeer > xParent( m_xParentWindow, UNO_QUERY ); 211 css::awt::Rectangle aRect( aPos.X, aPos.Y, 0, 0 ); 212 sal_Int16 nId = xPopupMenu->execute( xParent, aRect, css::awt::PopupMenuDirection::EXECUTE_UP+16 ); 213 //click "More..." 214 if ( nId && m_xFrame.is() ) 215 { 216 OUStringBuffer aBuff; 217 //set selected language as current language for selection 218 const OUString aSelectedLang = aLangMap[nId]; 219 220 if (MID_LANG_SEL_1 <= nId && nId <= MID_LANG_SEL_9) 221 { 222 if (bWriter) 223 aBuff.append( ".uno:LanguageStatus?Language:string=Current_" ); 224 else 225 aBuff.append( ".uno:LanguageStatus?Language:string=Default_" ); 226 227 aBuff.append( aSelectedLang ); 228 } 229 else if (nId == MID_LANG_SEL_NONE) 230 { 231 //set None as current language for selection 232 aBuff.append( ".uno:LanguageStatus?Language:string=Current_LANGUAGE_NONE" ); 233 } 234 else if (nId == MID_LANG_SEL_RESET) 235 { 236 // reset language attributes for selection 237 aBuff.append( ".uno:LanguageStatus?Language:string=Current_RESET_LANGUAGES" ); 238 } 239 else if (nId == MID_LANG_SEL_MORE) 240 { 241 //open the dialog "format/character" for current selection 242 aBuff.append( ".uno:FontDialog?Page:string=font" ); 243 } 244 else if (nId == MID_LANG_DEF_NONE) 245 { 246 aBuff.append( ".uno:LanguageStatus?Language:string=Default_LANGUAGE_NONE" ); 247 } 248 else if (nId == MID_LANG_DEF_RESET) 249 { 250 aBuff.append( ".uno:LanguageStatus?Language:string=Default_RESET_LANGUAGES" ); 251 } 252 else if (nId == MID_LANG_DEF_MORE) 253 { 254 aBuff.append( ".uno:LanguageStatus?Language:string=*" ); 255 } 256 else if (MID_LANG_PARA_1 <= nId && nId <= MID_LANG_PARA_9) 257 { 258 aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_" ); 259 aBuff.append( aSelectedLang ); 260 } 261 else if (nId == MID_LANG_PARA_NONE) 262 { 263 //set None as language for current paragraph 264 aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_LANGUAGE_NONE" ); 265 } 266 else if (nId == MID_LANG_PARA_RESET) 267 { 268 // reset language attributes for paragraph 269 aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_RESET_LANGUAGES" ); 270 } 271 else if (nId == MID_LANG_PARA_MORE) 272 { 273 //open the dialog "format/character" for current paragraph 274 aBuff.append( ".uno:FontDialogForParagraph" ); 275 } 276 277 const Sequence< beans::PropertyValue > aDummyArgs; 278 execute( aBuff.makeStringAndClear(), aDummyArgs ); 279 } 280 } 281 282 void SAL_CALL LangSelectionStatusbarController::command( 283 const css::awt::Point& aPos, 284 ::sal_Int32 nCommand, 285 sal_Bool /*bMouseEvent*/, 286 const css::uno::Any& /*aData*/ ) 287 { 288 if ( nCommand & ::awt::Command::CONTEXTMENU ) 289 { 290 LangMenu( aPos ); 291 } 292 } 293 294 void SAL_CALL LangSelectionStatusbarController::click( 295 const css::awt::Point& aPos ) 296 { 297 LangMenu( aPos ); 298 } 299 300 // XStatusListener 301 void SAL_CALL LangSelectionStatusbarController::statusChanged( const FeatureStateEvent& Event ) 302 { 303 // This function will be called when observed data changes, 304 // for example the selection or keyboard language. 305 // - It displays the language in use in the status bar 306 // - and it stores the relevant data for creating the menu 307 // at some later point in the member variables 308 // m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedText 309 310 SolarMutexGuard aSolarMutexGuard; 311 312 if ( m_bDisposed ) 313 return; 314 315 m_bShowMenu = true; 316 m_nScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; //set the default value 317 318 if ( m_xStatusbarItem.is() ) 319 { 320 OUString aStrValue; 321 Sequence< OUString > aSeq; 322 323 if ( Event.State >>= aStrValue ) 324 { 325 m_xStatusbarItem->setText( aStrValue ); 326 m_aCurLang = aStrValue; 327 } 328 else if ( Event.State >>= aSeq ) 329 { 330 if ( aSeq.getLength() == 4 ) 331 { 332 OUString aStatusText = aSeq[0]; 333 if (aStatusText == "*") 334 { 335 aStatusText = FwkResId(STR_LANGSTATUS_MULTIPLE_LANGUAGES); 336 } 337 m_xStatusbarItem->setText( aStatusText ); 338 339 // Retrieve all other values from the sequence and 340 // store it members! 341 m_aCurLang = aSeq[0]; 342 m_nScriptType = static_cast< SvtScriptType >( aSeq[1].toInt32() ); 343 m_aKeyboardLang = aSeq[2]; 344 m_aGuessedTextLang = aSeq[3]; 345 } 346 } 347 else if ( !Event.State.hasValue() ) 348 { 349 m_xStatusbarItem->setText( OUString() ); 350 m_bShowMenu = false; // no language -> no menu 351 } 352 } 353 } 354 355 } 356 357 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * 358 com_sun_star_comp_framework_LangSelectionStatusbarController_get_implementation( 359 css::uno::XComponentContext *context, 360 css::uno::Sequence<css::uno::Any> const &) 361 { 362 return cppu::acquire(new LangSelectionStatusbarController(context)); 363 } 364 365 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 366
