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 <uielement/langselectionmenucontroller.hxx>
21 
22 #include <services.h>
23 
24 #include <com/sun/star/awt/XDevice.hpp>
25 #include <com/sun/star/beans/PropertyValue.hpp>
26 #include <com/sun/star/awt/MenuItemStyle.hpp>
27 #include <com/sun/star/frame/XDispatchProvider.hpp>
28 
29 #include <vcl/menu.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/i18nhelp.hxx>
32 #include <rtl/ustrbuf.hxx>
33 #include <vcl/mnemonic.hxx>
34 
35 #include <com/sun/star/document/XDocumentLanguages.hpp>
36 
37 #include <i18nlangtag/mslangid.hxx>
38 #include <svl/languageoptions.hxx>
39 #include <svtools/langtab.hxx>
40 #include <classes/fwkresid.hxx>
41 
42 #include <strings.hrc>
43 
44 #include <helper/mischelper.hxx>
45 #include <osl/mutex.hxx>
46 
47 #include <map>
48 #include <set>
49 
50 //  Defines
51 
52 using namespace ::com::sun::star;
53 using namespace com::sun::star::uno;
54 using namespace com::sun::star::lang;
55 using namespace com::sun::star::frame;
56 using namespace com::sun::star::beans;
57 using namespace com::sun::star::util;
58 
59 namespace framework
60 {
61 
62 DEFINE_XSERVICEINFO_MULTISERVICE_2      (   LanguageSelectionMenuController         ,
63                                             OWeakObject                             ,
64                                             SERVICENAME_POPUPMENUCONTROLLER         ,
65                                             IMPLEMENTATIONNAME_LANGUAGESELECTIONMENUCONTROLLER
66                                         )
67 
68 DEFINE_INIT_SERVICE                     (   LanguageSelectionMenuController, {} )
69 
70 LanguageSelectionMenuController::LanguageSelectionMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext )
71     : svt::PopupMenuControllerBase(xContext)
72     , m_bShowMenu(true)
73     , m_nScriptType(SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX)
74     , m_aLangGuessHelper(xContext)
75 {
76 }
77 
78 LanguageSelectionMenuController::~LanguageSelectionMenuController()
79 {
80 }
81 
82 // XEventListener
83 void SAL_CALL LanguageSelectionMenuController::disposing( const EventObject& )
84 {
85     Reference< css::awt::XMenuListener > xHolder(static_cast<OWeakObject *>(this), UNO_QUERY );
86 
87     osl::MutexGuard aLock( m_aMutex );
88     m_xFrame.clear();
89     m_xDispatch.clear();
90     m_xLanguageDispatch.clear();
91 
92     if ( m_xPopupMenu.is() )
93         m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(static_cast<OWeakObject *>(this), UNO_QUERY ));
94     m_xPopupMenu.clear();
95 }
96 
97 // XStatusListener
98 void SAL_CALL LanguageSelectionMenuController::statusChanged( const FeatureStateEvent& Event )
99 {
100     SolarMutexGuard aSolarMutexGuard;
101 
102     if (rBHelper.bDisposed || rBHelper.bInDispose)
103         return;
104 
105     m_bShowMenu = true;
106     m_nScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;  //set the default value
107 
108     Sequence< OUString > aSeq;
109 
110     if ( Event.State >>= aSeq )
111     {
112         if ( aSeq.getLength() == 4 )
113         {
114             // Retrieve all other values from the sequence and
115             // store it members!
116             m_aCurLang          = aSeq[0];
117             m_nScriptType       = static_cast< SvtScriptType >(aSeq[1].toInt32());
118             m_aKeyboardLang     = aSeq[2];
119             m_aGuessedTextLang  = aSeq[3];
120         }
121     }
122     else if ( !Event.State.hasValue() )
123     {
124         m_bShowMenu = false;    // no language -> no sub-menu entries -> disable menu
125     }
126 }
127 
128 // XPopupMenuController
129 void LanguageSelectionMenuController::impl_setPopupMenu()
130 {
131     Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
132 
133     css::util::URL aTargetURL;
134 
135     // Register for language updates
136     aTargetURL.Complete = m_aLangStatusCommandURL;
137     m_xURLTransformer->parseStrict( aTargetURL );
138     m_xLanguageDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
139 
140     // Register for setting languages and opening language dialog
141     aTargetURL.Complete = m_aMenuCommandURL_Lang;
142     m_xURLTransformer->parseStrict( aTargetURL );
143     m_xMenuDispatch_Lang = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
144 
145     // Register for opening character dialog
146     aTargetURL.Complete = m_aMenuCommandURL_Font;
147     m_xURLTransformer->parseStrict( aTargetURL );
148     m_xMenuDispatch_Font = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
149 
150     // Register for opening character dialog with preselected paragraph
151     aTargetURL.Complete = m_aMenuCommandURL_CharDlgForParagraph;
152     m_xURLTransformer->parseStrict( aTargetURL );
153     m_xMenuDispatch_CharDlgForParagraph = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
154 }
155 
156 void LanguageSelectionMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu , const Mode eMode )
157 {
158     VCLXPopupMenu* pVCLPopupMenu = static_cast<VCLXPopupMenu *>(VCLXMenu::GetImplementation( rPopupMenu ));
159     PopupMenu*     pPopupMenu    = nullptr;
160 
161     SolarMutexGuard aSolarMutexGuard;
162 
163     resetPopupMenu( rPopupMenu );
164     if (!m_bShowMenu)
165         return;
166 
167     if ( pVCLPopupMenu )
168         pPopupMenu = static_cast<PopupMenu *>(pVCLPopupMenu->GetMenu());
169 
170     OUString aCmd_Dialog;
171     OUString aCmd_Language;
172     if( eMode == MODE_SetLanguageSelectionMenu )
173     {
174         aCmd_Dialog += ".uno:FontDialog?Page:string=font";
175         aCmd_Language += ".uno:LanguageStatus?Language:string=Current_";
176     }
177     else if ( eMode == MODE_SetLanguageParagraphMenu )
178     {
179         aCmd_Dialog += ".uno:FontDialogForParagraph";
180         aCmd_Language += ".uno:LanguageStatus?Language:string=Paragraph_";
181     }
182     else if ( eMode == MODE_SetLanguageAllTextMenu )
183     {
184         aCmd_Dialog += ".uno:LanguageStatus?Language:string=*";
185         aCmd_Language += ".uno:LanguageStatus?Language:string=Default_";
186     }
187 
188     // get languages to be displayed in the menu
189     std::set< OUString > aLangItems;
190     FillLangItems( aLangItems, m_xFrame, m_aLangGuessHelper,
191             m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedTextLang );
192 
193     // now add menu entries
194     // the different menus purpose will be handled by the different string
195     // for aCmd_Dialog and aCmd_Language
196 
197     sal_Int16 nItemId = 1;  // in this control the item id is not important for executing the command
198     const OUString sAsterisk("*");  // multiple languages in current selection
199     const OUString sNone( SvtLanguageTable::GetLanguageString( LANGUAGE_NONE ));
200     for (auto const& langItem : aLangItems)
201     {
202         if (langItem != sNone &&
203             langItem != sAsterisk &&
204             !langItem.isEmpty()) // 'no language found' from language guessing
205         {
206             pPopupMenu->InsertItem( nItemId, langItem);
207             OUString aCmd = aCmd_Language + langItem;
208             pPopupMenu->SetItemCommand( nItemId, aCmd );
209             if (langItem == m_aCurLang && eMode == MODE_SetLanguageSelectionMenu )
210             {
211                 //make a sign for the current language
212                 pPopupMenu->CheckItem( nItemId );
213             }
214             ++nItemId;
215         }
216     }
217 
218     // entry for LANGUAGE_NONE
219     ++nItemId;
220     pPopupMenu->InsertItem( nItemId, FwkResId(STR_LANGSTATUS_NONE) );
221     OUString aCmd = aCmd_Language + "LANGUAGE_NONE";
222     pPopupMenu->SetItemCommand( nItemId, aCmd );
223 
224     // entry for 'Reset to default language'
225     ++nItemId;
226     pPopupMenu->InsertItem( nItemId, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE) );
227     aCmd = aCmd_Language + "RESET_LANGUAGES";
228     pPopupMenu->SetItemCommand( nItemId, aCmd );
229 
230     // entry for opening the Format/Character dialog
231     ++nItemId;
232     pPopupMenu->InsertItem( nItemId, FwkResId(STR_LANGSTATUS_MORE));
233     pPopupMenu->SetItemCommand( nItemId, aCmd_Dialog );
234 }
235 
236 void SAL_CALL LanguageSelectionMenuController::updatePopupMenu()
237 {
238     svt::PopupMenuControllerBase::updatePopupMenu();
239 
240     // Force status update to get information about the current languages
241     osl::ClearableMutexGuard aLock( m_aMutex );
242     Reference< XDispatch > xDispatch( m_xLanguageDispatch );
243     css::util::URL aTargetURL;
244     aTargetURL.Complete = m_aLangStatusCommandURL;
245     m_xURLTransformer->parseStrict( aTargetURL );
246     aLock.clear();
247 
248     if ( xDispatch.is() )
249     {
250         xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
251         xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
252     }
253 
254     // TODO: Fill menu with the information retrieved by the status update
255 
256     if ( m_aCommandURL == ".uno:SetLanguageSelectionMenu" )
257     {
258         fillPopupMenu(m_xPopupMenu, MODE_SetLanguageSelectionMenu );
259     }
260     else if ( m_aCommandURL == ".uno:SetLanguageParagraphMenu" )
261     {
262         fillPopupMenu(m_xPopupMenu, MODE_SetLanguageParagraphMenu );
263     }
264     else if ( m_aCommandURL == ".uno:SetLanguageAllTextMenu" )
265     {
266         fillPopupMenu(m_xPopupMenu, MODE_SetLanguageAllTextMenu );
267     }
268 }
269 
270 // XInitialization
271 void SAL_CALL LanguageSelectionMenuController::initialize( const Sequence< Any >& aArguments )
272 {
273     osl::MutexGuard aLock( m_aMutex );
274 
275     bool bInitalized( m_bInitialized );
276     if ( !bInitalized )
277     {
278         svt::PopupMenuControllerBase::initialize(aArguments);
279 
280         if ( m_bInitialized )
281         {
282             m_aLangStatusCommandURL               = ".uno:LanguageStatus";
283             m_aMenuCommandURL_Lang                = m_aLangStatusCommandURL;
284             m_aMenuCommandURL_Font                = ".uno:FontDialog";
285             m_aMenuCommandURL_CharDlgForParagraph = ".uno:FontDialogForParagraph";
286         }
287     }
288 }
289 
290 }
291 
292 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
293