Bump version to 4.3-4
[LibreOffice.git] / cui / source / options / personalization.cxx
blob21cb2b4dde416e9b4579adf0ea329a200d15775d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #include <config_folders.h>
12 #include "personalization.hxx"
14 #include <comphelper/processfactory.hxx>
15 #include <officecfg/Office/Common.hxx>
16 #include <osl/file.hxx>
17 #include <rtl/bootstrap.hxx>
18 #include <tools/urlobj.hxx>
19 #include <vcl/edit.hxx>
20 #include <vcl/msgbox.hxx>
21 #include <vcl/svapp.hxx>
22 #include <vcl/settings.hxx>
24 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
25 #include <com/sun/star/system/SystemShellExecute.hpp>
26 #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
27 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
28 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
29 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
30 #include <com/sun/star/ui/dialogs/FilePicker.hpp>
31 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
32 #include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
33 #include <com/sun/star/ui/dialogs/XFilterManager.hpp>
35 using namespace com::sun::star;
37 /** Dialog that will allow the user to choose a Persona to use.
39 So far there is no better possibility than just to paste the URL from
40 https://addons.mozilla.org/firefox/themes ...
42 class SelectPersonaDialog : public ModalDialog
44 private:
45 Edit *m_pEdit; ///< The input line for the Persona URL
47 public:
48 SelectPersonaDialog( Window *pParent );
50 /// Get the URL from the Edit field.
51 OUString GetPersonaURL() const;
53 private:
54 /// Handle the [Visit Firefox Personas] button
55 DECL_LINK( VisitPersonas, PushButton* );
58 SelectPersonaDialog::SelectPersonaDialog( Window *pParent )
59 : ModalDialog( pParent, "SelectPersonaDialog", "cui/ui/select_persona_dialog.ui" )
61 PushButton *pButton;
62 get( pButton, "visit_personas" );
63 pButton->SetClickHdl( LINK( this, SelectPersonaDialog, VisitPersonas ) );
65 get( m_pEdit, "persona_url" );
66 m_pEdit->SetPlaceholderText( "https://addons.mozilla.org/firefox/themes/" );
69 OUString SelectPersonaDialog::GetPersonaURL() const
71 OUString aText( m_pEdit->GetText() );
73 if ( aText.startsWith( "https://addons.mozilla.org/" ) )
74 return aText;
76 return OUString();
79 IMPL_LINK( SelectPersonaDialog, VisitPersonas, PushButton*, /*pButton*/ )
81 uno::Reference< com::sun::star::system::XSystemShellExecute > xSystemShell( com::sun::star::system::SystemShellExecute::create( ::comphelper::getProcessComponentContext() ) );
83 xSystemShell->execute( "https://addons.mozilla.org/firefox/themes/", OUString(), com::sun::star::system::SystemShellExecuteFlags::URIS_ONLY );
85 return 0;
88 SvxPersonalizationTabPage::SvxPersonalizationTabPage( Window *pParent, const SfxItemSet &rSet )
89 : SfxTabPage( pParent, "PersonalizationTabPage", "cui/ui/personalization_tab.ui", rSet )
91 // persona
92 get( m_pNoPersona, "no_persona" );
93 get( m_pDefaultPersona, "default_persona" );
95 get( m_pOwnPersona, "own_persona" );
96 m_pOwnPersona->SetClickHdl( LINK( this, SvxPersonalizationTabPage, ForceSelect ) );
98 get( m_pSelectPersona, "select_persona" );
99 m_pSelectPersona->SetClickHdl( LINK( this, SvxPersonalizationTabPage, SelectPersona ) );
102 SvxPersonalizationTabPage::~SvxPersonalizationTabPage()
106 SfxTabPage* SvxPersonalizationTabPage::Create( Window *pParent, const SfxItemSet &rSet )
108 return new SvxPersonalizationTabPage( pParent, rSet );
111 bool SvxPersonalizationTabPage::FillItemSet( SfxItemSet & )
113 // persona
114 OUString aPersona( "default" );
115 if ( m_pNoPersona->IsChecked() )
116 aPersona = "no";
117 else if ( m_pOwnPersona->IsChecked() )
118 aPersona = "own";
120 bool bModified = false;
121 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
122 if ( xContext.is() &&
123 ( aPersona != officecfg::Office::Common::Misc::Persona::get( xContext ) ||
124 m_aPersonaSettings != officecfg::Office::Common::Misc::PersonaSettings::get( xContext ) ) )
126 bModified = true;
129 // write
130 boost::shared_ptr< comphelper::ConfigurationChanges > batch( comphelper::ConfigurationChanges::create() );
132 officecfg::Office::Common::Misc::Persona::set( aPersona, batch );
133 officecfg::Office::Common::Misc::PersonaSettings::set( m_aPersonaSettings, batch );
135 batch->commit();
137 if ( bModified )
139 // broadcast the change
140 DataChangedEvent aDataChanged( DATACHANGED_SETTINGS, NULL, SETTINGS_STYLE );
141 Application::NotifyAllWindows( aDataChanged );
144 return bModified;
147 void SvxPersonalizationTabPage::Reset( const SfxItemSet & )
149 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
151 // persona
152 OUString aPersona( "default" );
153 if ( xContext.is() )
155 aPersona = officecfg::Office::Common::Misc::Persona::get( xContext );
156 m_aPersonaSettings = officecfg::Office::Common::Misc::PersonaSettings::get( xContext );
159 if ( aPersona == "no" )
160 m_pNoPersona->Check();
161 else if ( aPersona == "own" )
162 m_pOwnPersona->Check();
163 else
164 m_pDefaultPersona->Check();
167 IMPL_LINK( SvxPersonalizationTabPage, SelectPersona, PushButton*, /*pButton*/ )
169 SelectPersonaDialog aDialog( NULL );
171 while ( aDialog.Execute() == RET_OK )
173 OUString aURL( aDialog.GetPersonaURL() );
174 if ( !aURL.isEmpty() )
176 if ( CopyPersonaToGallery( aURL ) )
177 m_pOwnPersona->Check();
178 break;
180 // else TODO msgbox that the URL did not match
183 return 0;
186 IMPL_LINK( SvxPersonalizationTabPage, ForceSelect, RadioButton*, pButton )
188 if ( pButton == m_pOwnPersona && m_aPersonaSettings.isEmpty() )
189 SelectPersona( m_pSelectPersona );
191 return 0;
194 /// Find the value on the Persona page, and convert it to a usable form.
195 static OUString searchValue( const OString &rBuffer, sal_Int32 from, const OString &rIdentifier )
197 sal_Int32 where = rBuffer.indexOf( rIdentifier, from );
198 if ( where < 0 )
199 return OUString();
201 where += rIdentifier.getLength();
203 sal_Int32 end = rBuffer.indexOf( "&#34;", where );
204 if ( end < 0 )
205 return OUString();
207 OString aOString( rBuffer.copy( where, end - where ) );
208 OUString aString( aOString.getStr(), aOString.getLength(), RTL_TEXTENCODING_UTF8, OSTRING_TO_OUSTRING_CVTFLAGS );
210 return aString.replaceAll( "\\/", "/" );
213 /// Parse the Persona web page, and find where to get the bitmaps + the color values.
214 static bool parsePersonaInfo( const OString &rBuffer, OUString *pHeaderURL, OUString *pFooterURL, OUString *pTextColor, OUString *pAccentColor )
216 // it is the first attribute that contains "persona="
217 sal_Int32 persona = rBuffer.indexOf( "data-browsertheme=\"{" );
218 if ( persona < 0 )
219 return false;
221 // now search inside
222 *pHeaderURL = searchValue( rBuffer, persona, "&#34;headerURL&#34;:&#34;" );
223 if ( pHeaderURL->isEmpty() )
224 return false;
226 *pFooterURL = searchValue( rBuffer, persona, "&#34;footerURL&#34;:&#34;" );
227 if ( pFooterURL->isEmpty() )
228 return false;
230 *pTextColor = searchValue( rBuffer, persona, "&#34;textcolor&#34;:&#34;" );
231 if ( pTextColor->isEmpty() )
232 return false;
234 *pAccentColor = searchValue( rBuffer, persona, "&#34;accentcolor&#34;:&#34;" );
235 if ( pAccentColor->isEmpty() )
236 return false;
238 return true;
241 bool SvxPersonalizationTabPage::CopyPersonaToGallery( const OUString &rURL )
243 // init the input stream
244 uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
245 if ( !xFileAccess.is() )
246 return false;
248 uno::Reference< io::XInputStream > xStream;
249 try {
250 xStream = xFileAccess->openFileRead( rURL );
252 catch (...)
254 return false;
256 if ( !xStream.is() )
257 return false;
259 // read the persona specification
260 // NOTE: Parsing for real is an overkill here; and worse - I tried, and
261 // the HTML the site provides is not 100% valid ;-)
262 const sal_Int32 BUF_LEN = 8000;
263 uno::Sequence< sal_Int8 > buffer( BUF_LEN );
264 OStringBuffer aBuffer( 64000 );
266 sal_Int32 nRead = 0;
267 while ( ( nRead = xStream->readBytes( buffer, BUF_LEN ) ) == BUF_LEN )
268 aBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead );
270 if ( nRead > 0 )
271 aBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead );
273 xStream->closeInput();
275 // get the important bits of info
276 OUString aHeaderURL, aFooterURL, aTextColor, aAccentColor;
278 if ( !parsePersonaInfo( aBuffer.makeStringAndClear(), &aHeaderURL, &aFooterURL, &aTextColor, &aAccentColor ) )
279 return false;
281 // copy the images to the user's gallery
282 OUString gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
283 rtl::Bootstrap::expandMacros( gallery );
284 gallery += "/user/gallery/personas/";
285 osl::Directory::createPath( gallery );
287 OUString aHeaderFile( INetURLObject( aHeaderURL ).getName() );
288 OUString aFooterFile( INetURLObject( aFooterURL ).getName() );
290 try {
291 xFileAccess->copy( aHeaderURL, gallery + aHeaderFile );
292 xFileAccess->copy( aFooterURL, gallery + aFooterFile );
294 catch ( const uno::Exception & )
296 return false;
299 m_aPersonaSettings = aHeaderFile + ";" + aFooterFile + ";" + aTextColor + ";" + aAccentColor;
301 return true;
304 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */