Kaydet (Commit) e1e9e2aa authored tarafından Muhammet Kara's avatar Muhammet Kara

Kill Mozilla personas

As per ESC & Design Team decisions.

It has become unusable anyway after major changes
on the Mozilla side.

Long live Libreoffice Themes! :)

Change-Id: I2893fbc5e4f5637ee9715fa426b92ca58534f126
Reviewed-on: https://gerrit.libreoffice.org/72811
Tested-by: Jenkins
Reviewed-by: 's avatarJulien Nabet <serval2412@yahoo.fr>
Reviewed-by: 's avatarHeiko Tietze <tietze.heiko@gmail.com>
Reviewed-by: 's avatarMuhammet Kara <muhammet.kara@collabora.com>
üst dd5c9897
......@@ -179,7 +179,6 @@ $(eval $(call gb_UIConfig_add_uifiles,cui,\
cui/uiconfig/ui/searchattrdialog \
cui/uiconfig/ui/searchformatdialog \
cui/uiconfig/ui/securityoptionsdialog \
cui/uiconfig/ui/select_persona_dialog \
cui/uiconfig/ui/selectpathdialog \
cui/uiconfig/ui/shadowtabpage \
cui/uiconfig/ui/showcoldialog \
......
......@@ -368,7 +368,6 @@
#define RID_SVXSTR_NORESULTS NC_("RID_SVXSTR_NORESULTS", "No results found.")
#define RID_SVXSTR_APPLYPERSONA NC_("RID_SVXSTR_APPLYPERSONA", "Applying Theme...")
#define RID_SVXSTR_INVALIDPERSONAURL NC_("RID_SVXSTR_INVALIDPERSONAURL", "Please enter a valid theme address or a search term.")
#define RID_SVXSTR_MOZAPIUNREACHABLE NC_("RID_SVXSTR_MOZAPIUNREACHABLE", "The Mozilla Themes API is currently unavailable.")
#define RID_SVXSTR_TABLE_PRESET_NONE NC_("RID_SVXSTR_TABLE_PRESET_NONE", "Set No Borders")
#define RID_SVXSTR_TABLE_PRESET_ONLYOUTER NC_("RID_SVXSTR_TABLE_PRESET_ONLYOUTER", "Set Outer Border Only")
......
......@@ -47,7 +47,6 @@ cui/ui/scriptorganizer.ui
cui/ui/searchattrdialog.ui
cui/ui/searchformatdialog.ui
cui/ui/securityoptionsdialog.ui
cui/ui/select_persona_dialog.ui
cui/ui/selectpathdialog.ui
cui/ui/shadowtabpage.ui
cui/ui/showcoldialog.ui
......
......@@ -20,12 +20,7 @@
#include <rtl/strbuf.hxx>
#include <tools/urlobj.hxx>
#include <tools/stream.hxx>
#include <vcl/edit.hxx>
#include <vcl/event.hxx>
#include <vcl/fixed.hxx>
#include <vcl/fixedhyper.hxx>
#include <vcl/weld.hxx>
#include <vcl/lstbox.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/graphicfilter.hxx>
......@@ -41,12 +36,6 @@
#include <ucbhelper/content.hxx>
#include <comphelper/simplefileaccessinteraction.hxx>
#include <curl/curl.h>
#include <orcus/json_document_tree.hpp>
#include <orcus/config.hpp>
#include <orcus/pstring.hpp>
#include <vector>
using namespace com::sun::star;
......@@ -54,12 +43,6 @@ using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::ucb;
using namespace ::com::sun::star::beans;
#ifdef UNX
static const char kUserAgent[] = "LibreOffice PersonaDownloader/1.0 (Linux)";
#else
static const char kUserAgent[] = "LibreOffice PersonaDownloader/1.0 (unknown platform)";
#endif
struct PersonaInfo
{
OUString sSlug;
......@@ -70,343 +53,12 @@ struct PersonaInfo
OUString sTextColor;
};
namespace {
// Callback to get the response data from server.
size_t WriteCallback(void *ptr, size_t size,
size_t nmemb, void *userp)
{
if (!userp)
return 0;
std::string* response = static_cast<std::string *>(userp);
size_t real_size = size * nmemb;
response->append(static_cast<char *>(ptr), real_size);
return real_size;
}
// Callback to get the response data from server to a file.
size_t WriteCallbackFile(void *ptr, size_t size,
size_t nmemb, void *userp)
{
if (!userp)
return 0;
SvStream* response = static_cast<SvStream *>(userp);
size_t real_size = size * nmemb;
response->WriteBytes(ptr, real_size);
return real_size;
}
// Gets the content of the given URL and returns as a standard string
std::string curlGet(const OString& rURL)
{
CURL* curl = curl_easy_init();
if (!curl)
return std::string();
curl_easy_setopt(curl, CURLOPT_URL, rURL.getStr());
curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
std::string response_body;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA,
static_cast<void *>(&response_body));
CURLcode cc = curl_easy_perform(curl);
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code != 200)
{
SAL_WARN("cui.options", "Download failed. Error code: " << http_code);
}
if (cc != CURLE_OK)
{
SAL_WARN("cui.options", "curl error: " << cc);
}
return response_body;
}
// Downloads and saves the file at the given rURL to a local path (sFileURL)
void curlDownload(const OString& rURL, const OUString& sFileURL)
{
CURL* curl = curl_easy_init();
SvFileStream aFile( sFileURL, StreamMode::WRITE );
if (!curl)
return;
curl_easy_setopt(curl, CURLOPT_URL, rURL.getStr());
curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallbackFile);
curl_easy_setopt(curl, CURLOPT_WRITEDATA,
static_cast<void *>(&aFile));
CURLcode cc = curl_easy_perform(curl);
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code != 200)
{
SAL_WARN("cui.options", "Download failed. Error code: " << http_code);
}
if (cc != CURLE_OK)
{
SAL_WARN("cui.options", "curl error: " << cc);
}
}
} //End of anonymous namespace
SelectPersonaDialog::SelectPersonaDialog(weld::Window *pParent)
: GenericDialogController(pParent, "cui/ui/select_persona_dialog.ui", "SelectPersonaDialog")
, m_xEdit(m_xBuilder->weld_entry("search_term"))
, m_xSearchButton(m_xBuilder->weld_button("search_personas"))
, m_xProgressLabel(m_xBuilder->weld_label("progress_label"))
, m_xCategories(m_xBuilder->weld_combo_box("categoriesCB"))
, m_xOkButton(m_xBuilder->weld_button("ok"))
, m_xCancelButton(m_xBuilder->weld_button("cancel"))
, m_vResultList{ m_xBuilder->weld_button("result1"),
m_xBuilder->weld_button("result2"),
m_xBuilder->weld_button("result3"),
m_xBuilder->weld_button("result4"),
m_xBuilder->weld_button("result5"),
m_xBuilder->weld_button("result6"),
m_xBuilder->weld_button("result7"),
m_xBuilder->weld_button("result8"),
m_xBuilder->weld_button("result9") }
{
m_xSearchButton->connect_clicked( LINK( this, SelectPersonaDialog, SearchPersonas ) );
m_xCategories->connect_changed( LINK( this, SelectPersonaDialog, SelectCategory ) );
m_xOkButton->connect_clicked( LINK( this, SelectPersonaDialog, ActionOK ) );
m_xCancelButton->connect_clicked( LINK( this, SelectPersonaDialog, ActionCancel ) );
for (auto & nIndex : m_vResultList)
{
nIndex->connect_clicked( LINK( this, SelectPersonaDialog, SelectPersona ) );
nIndex->set_sensitive(false);
}
m_xCategories->set_active_text("Featured");
SelectCategory(*m_xCategories);
}
SelectPersonaDialog::~SelectPersonaDialog()
{
if (m_pSearchThread.is())
{
// Release the solar mutex, so the thread is not affected by the race
// when it's after the m_bExecute check but before taking the solar
// mutex.
SolarMutexReleaser aReleaser;
m_pSearchThread->join();
}
}
OUString SelectPersonaDialog::GetSelectedPersona() const
{
if( !m_aSelectedPersona.isEmpty( ) )
return m_aSelectedPersona;
return OUString();
}
IMPL_LINK_NOARG( SelectPersonaDialog, SearchPersonas, weld::Button&, void )
{
OUString searchTerm = m_xEdit->get_text();
if( searchTerm.isEmpty( ) )
return;
if( m_pSearchThread.is() )
m_pSearchThread->StopExecution();
// Direct url of a persona given
if ( searchTerm.startsWith( "https://addons.mozilla.org/" ) )
{
OUString sSlug = searchTerm.getToken( 6, '/' );
// Check if we got the slug
if ( sSlug.isEmpty() )
{
SolarMutexGuard aGuard;
OUString sError = CuiResId( RID_SVXSTR_INVALIDPERSONAURL );
std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
VclMessageType::Error, VclButtonsType::Ok,
sError));
xBox->run();
return;
}
// Remove invalid characters
searchTerm = searchTerm.replaceAll("?", "");
m_pSearchThread = new SearchAndParseThread( this, sSlug, true );
}
// Search term given
else
{
// Remove invalid characters
searchTerm = searchTerm.replaceAll("/", "");
searchTerm = searchTerm.replaceAll("?", "");
// 15 results so that invalid and duplicate search results whose names, textcolors etc. are null can be skipped
OUString rSearchURL = "https://addons.mozilla.org/api/v3/addons/search/?q=" + searchTerm + "&type=persona&page_size=15";
m_pSearchThread = new SearchAndParseThread( this, rSearchURL, false );
}
m_pSearchThread->launch();
}
IMPL_LINK_NOARG( SelectPersonaDialog, ActionOK, weld::Button&, void )
{
OUString aSelectedPersona = GetSelectedPersona();
if( !aSelectedPersona.isEmpty() )
{
m_pGetPersonaThread = new GetPersonaThread( this, aSelectedPersona );
m_pGetPersonaThread->launch();
}
else
{
if ( m_pSearchThread.is() )
m_pSearchThread->StopExecution();
m_xDialog->response(RET_OK);
}
}
IMPL_LINK_NOARG( SelectPersonaDialog, ActionCancel, weld::Button&, void )
{
if( m_pSearchThread.is() )
m_pSearchThread->StopExecution();
if( m_pGetPersonaThread.is() )
m_pGetPersonaThread->StopExecution();
m_xDialog->response(RET_CANCEL);
}
IMPL_LINK_NOARG( SelectPersonaDialog, SelectCategory, weld::ComboBox&, void )
{
OUString searchTerm = m_xCategories->get_active_id();
OUString rSearchURL;
if (searchTerm.isEmpty())
return;
if( m_pSearchThread.is() )
m_pSearchThread->StopExecution();
// 15 results so that invalid and duplicate search results whose names, textcolors etc. are null can be skipped
if (searchTerm == "featured")
rSearchURL = "https://addons.mozilla.org/api/v3/addons/search/?type=persona&app=firefox&status=public&sort=users&featured=true&page_size=15";
else
rSearchURL = "https://addons.mozilla.org/api/v3/addons/search/?type=persona&app=firefox&category=" + searchTerm + "&status=public&sort=downloads&page_size=15";
m_pSearchThread = new SearchAndParseThread( this, rSearchURL, false );
m_pSearchThread->launch();
}
IMPL_LINK( SelectPersonaDialog, SelectPersona, weld::Button&, rButton, void )
{
if( m_pSearchThread.is() )
m_pSearchThread->StopExecution();
if ( m_pGetPersonaThread.is() )
return;
for( sal_Int32 index = 0; index < MAX_RESULTS; index++ )
{
if( &rButton == m_vResultList[index].get() )
{
if( !m_vPersonaSettings[index].isEmpty() )
{
m_aSelectedPersona = m_vPersonaSettings[index];
// get the persona name from the setting variable to show in the progress.
OUString aName( m_aSelectedPersona.getToken( 1, ';' ) );
OUString aProgress( CuiResId(RID_SVXSTR_SELECTEDPERSONA) + aName );
SetProgress( aProgress );
}
break;
}
}
}
void SelectPersonaDialog::SetAppliedPersonaSetting( OUString const & rPersonaSetting )
{
m_aAppliedPersona = rPersonaSetting;
}
const OUString& SelectPersonaDialog::GetAppliedPersonaSetting() const
{
return m_aAppliedPersona;
}
void SelectPersonaDialog::SetProgress( const OUString& rProgress )
{
if(rProgress.isEmpty())
m_xProgressLabel->hide();
else
{
SolarMutexGuard aGuard;
m_xProgressLabel->show();
m_xProgressLabel->set_label( rProgress );
m_xDialog->resize_to_request(); //TODO
}
}
void SelectPersonaDialog::SetImages( VirtualDevice& rImage, const OUString& sName, const sal_Int32& nIndex )
{
m_vResultList[nIndex]->set_sensitive(true);
m_vResultList[nIndex]->set_image(&rImage);
m_vResultList[nIndex]->set_tooltip_text( sName );
}
void SelectPersonaDialog::AddPersonaSetting( OUString const & rPersonaSetting )
{
m_vPersonaSettings.push_back( rPersonaSetting );
}
void SelectPersonaDialog::ClearSearchResults()
{
// for VCL to be able to destroy bitmaps
SolarMutexGuard aGuard;
m_vPersonaSettings.clear();
m_aSelectedPersona.clear();
for(auto & nIndex : m_vResultList)
{
nIndex->set_sensitive(false);
nIndex->set_image(nullptr);
}
}
SvxPersonalizationTabPage::SvxPersonalizationTabPage( vcl::Window *pParent, const SfxItemSet &rSet )
: SfxTabPage( pParent, "PersonalizationTabPage", "cui/ui/personalization_tab.ui", &rSet )
{
// persona
get( m_pNoPersona, "no_persona" );
get( m_pDefaultPersona, "default_persona" );
get( m_pAppliedThemeLabel, "applied_theme_link" );
get( m_pOwnPersona, "own_persona" );
get( m_pSelectPersona, "select_persona" );
// Mozilla API and the Mozilla personas are no longer useful for us
// We will probably remove this altogether before 6.3
m_pSelectPersona->Disable();
m_pOwnPersona->Disable();
m_pSelectPersona->SetQuickHelpText( CuiResId( RID_SVXSTR_MOZAPIUNREACHABLE ) );
m_pOwnPersona->SetQuickHelpText( CuiResId( RID_SVXSTR_MOZAPIUNREACHABLE ) );
for (sal_uInt32 i = 0; i < MAX_DEFAULT_PERSONAS; ++i)
{
......@@ -415,16 +67,7 @@ SvxPersonalizationTabPage::SvxPersonalizationTabPage( vcl::Window *pParent, cons
m_vDefaultPersonaImages[i]->SetClickHdl( LINK( this, SvxPersonalizationTabPage, DefaultPersona ) );
}
get( m_pPersonaList, "installed_personas" );
m_pPersonaList->SetSelectHdl( LINK( this, SvxPersonalizationTabPage, SelectInstalledPersona ) );
get( m_pExtensionPersonaPreview, "persona_preview" );
get ( m_pExtensionLabel, "extensions_label" );
CheckAppliedTheme();
LoadDefaultImages();
LoadExtensionThemes();
}
SvxPersonalizationTabPage::~SvxPersonalizationTabPage()
......@@ -436,14 +79,8 @@ void SvxPersonalizationTabPage::dispose()
{
m_pNoPersona.clear();
m_pDefaultPersona.clear();
m_pOwnPersona.clear();
m_pSelectPersona.clear();
for (VclPtr<PushButton> & i : m_vDefaultPersonaImages)
i.clear();
m_pExtensionPersonaPreview.clear();
m_pPersonaList.clear();
m_pExtensionLabel.clear();
m_pAppliedThemeLabel.clear();
SfxTabPage::dispose();
}
......@@ -459,8 +96,6 @@ bool SvxPersonalizationTabPage::FillItemSet( SfxItemSet * )
OUString aPersona( "default" );
if ( m_pNoPersona->IsChecked() )
aPersona = "no";
else if ( m_pOwnPersona->IsChecked() )
aPersona = "own";
bool bModified = false;
uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
......@@ -503,54 +138,10 @@ void SvxPersonalizationTabPage::Reset( const SfxItemSet * )
if ( aPersona == "no" )
m_pNoPersona->Check();
else if ( aPersona == "own" )
m_pOwnPersona->Check();
else
m_pDefaultPersona->Check();
}
void SvxPersonalizationTabPage::SetPersonaSettings( const OUString& aPersonaSettings )
{
m_aPersonaSettings = aPersonaSettings;
ShowAppliedThemeLabel( m_aPersonaSettings );
m_pOwnPersona->Check();
}
void SvxPersonalizationTabPage::CheckAppliedTheme()
{
uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
OUString aPersona( "default" ), aPersonaSetting;
if ( xContext.is())
{
aPersona = officecfg::Office::Common::Misc::Persona::get( xContext );
aPersonaSetting = officecfg::Office::Common::Misc::PersonaSettings::get( xContext );
}
if(aPersona == "own")
ShowAppliedThemeLabel(aPersonaSetting);
}
void SvxPersonalizationTabPage::ShowAppliedThemeLabel(const OUString& aPersonaSetting)
{
OUString aSlug, aName;
sal_Int32 nIndex = 0;
aSlug = aPersonaSetting.getToken( 0, ';', nIndex );
if ( nIndex > 0 )
aName = "(" + aPersonaSetting.getToken( 0, ';', nIndex ) + ")";
if ( !aName.isEmpty() )
{
m_pAppliedThemeLabel->SetText( aName );
m_pAppliedThemeLabel->SetURL( "https://addons.mozilla.org/en-US/firefox/addon/" + aSlug + "/" );
m_pAppliedThemeLabel->Show();
}
else
{
SAL_WARN("cui.options", "Applied persona doesn't have a name!");
}
}
void SvxPersonalizationTabPage::LoadDefaultImages()
{
// Load the pre saved personas
......@@ -593,71 +184,6 @@ void SvxPersonalizationTabPage::LoadDefaultImages()
m_pDefaultPersona->Enable(foundOne);
}
void SvxPersonalizationTabPage::LoadExtensionThemes()
{
// See if any extensions are used to install personas. If yes, load them.
css::uno::Sequence<OUString> installedPersonas( officecfg::Office::Common::Misc::PersonasList::get()->getElementNames() );
sal_Int32 nLength = installedPersonas.getLength();
if( nLength == 0 )
return;
m_pPersonaList->Show();
m_pExtensionLabel->Show();
for( sal_Int32 nIndex = 0; nIndex < nLength; nIndex++ )
{
Reference< XPropertySet > xPropertySet( officecfg::Office::Common::Misc::PersonasList::get()->getByName( installedPersonas[nIndex] ), UNO_QUERY_THROW );
OUString aPersonaSlug, aPersonaName, aPreviewFile, aHeaderFile, aFooterFile, aTextColor, aPersonaSettings;
Any aValue = xPropertySet->getPropertyValue( "Slug" );
aValue >>= aPersonaSlug;
aValue = xPropertySet->getPropertyValue( "Name" );
aValue >>= aPersonaName;
m_pPersonaList->InsertEntry( aPersonaName );
aValue = xPropertySet->getPropertyValue( "Preview" );
aValue >>= aPreviewFile;
aValue = xPropertySet->getPropertyValue( "Header" );
aValue >>= aHeaderFile;
aValue = xPropertySet->getPropertyValue( "Footer" );
aValue >>= aFooterFile;
aValue = xPropertySet->getPropertyValue( "TextColor" );
aValue >>= aTextColor;
aPersonaSettings = aPersonaSlug + ";" + aPersonaName + ";" + aPreviewFile
+ ";" + aHeaderFile + ";" + aFooterFile + ";" + aTextColor;
rtl::Bootstrap::expandMacros( aPersonaSettings );
m_vExtensionPersonaSettings.push_back( aPersonaSettings );
}
}
IMPL_LINK_NOARG( SvxPersonalizationTabPage, SelectPersona, Button*, void )
{
m_pOwnPersona->Check();
SelectPersonaDialog aDialog(GetDialogFrameWeld());
if (aDialog.run() == RET_OK)
{
OUString aPersonaSetting(aDialog.GetAppliedPersonaSetting());
if ( !aPersonaSetting.isEmpty() )
{
SetPersonaSettings( aPersonaSetting );
}
}
}
IMPL_LINK( SvxPersonalizationTabPage, ForceSelect, Button*, pButton, void )
{
if ( pButton == m_pOwnPersona && m_aPersonaSettings.isEmpty() )
SelectPersona( m_pSelectPersona );
}
IMPL_LINK( SvxPersonalizationTabPage, DefaultPersona, Button*, pButton, void )
{
m_pDefaultPersona->Check();
......@@ -668,391 +194,4 @@ IMPL_LINK( SvxPersonalizationTabPage, DefaultPersona, Button*, pButton, void )
}
}
IMPL_LINK_NOARG( SvxPersonalizationTabPage, SelectInstalledPersona, ListBox&, void)
{
m_pOwnPersona->Check();
// Get the details of the selected theme.
m_pExtensionPersonaPreview->Show();
sal_Int32 nSelectedPos = m_pPersonaList->GetSelectedEntryPos();
OUString aSettings = m_vExtensionPersonaSettings[nSelectedPos];
sal_Int32 nIndex = aSettings.indexOf( ';' );
OUString aPreviewFile = aSettings.copy( 0, nIndex );
m_aPersonaSettings = aSettings.copy( nIndex + 1 );
// Show the preview file in the button.
GraphicFilter aFilter;
Graphic aGraphic;
INetURLObject aURLObj( aPreviewFile );
aFilter.ImportGraphic( aGraphic, aURLObj );
BitmapEx aBmp = aGraphic.GetBitmapEx();
m_pExtensionPersonaPreview->SetModeImage( Image( aBmp ) );
}
SearchAndParseThread::SearchAndParseThread( SelectPersonaDialog* pDialog,
const OUString& rURL, bool bDirectURL ) :
Thread( "cuiPersonasSearchThread" ),
m_pPersonaDialog( pDialog ),
m_aURL( rURL ),
m_bExecute( true ),
m_bDirectURL( bDirectURL )
{
}
SearchAndParseThread::~SearchAndParseThread()
{
}
namespace {
bool getPreviewFile( const PersonaInfo& aPersonaInfo, OUString& pPreviewFile )
{
uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
// copy the images to the user's gallery
OUString gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
rtl::Bootstrap::expandMacros( gallery );
gallery += "/user/gallery/personas/" + aPersonaInfo.sSlug + "/";
OUString aPreviewFile( INetURLObject( aPersonaInfo.sPreviewURL ).getName() );
OString aPreviewURL = OUStringToOString( aPersonaInfo.sPreviewURL, RTL_TEXTENCODING_UTF8 );
try {
osl::Directory::createPath( gallery );
if ( !xFileAccess->exists( gallery + aPreviewFile ) )
curlDownload(aPreviewURL, gallery + aPreviewFile);
}
catch ( const uno::Exception & )
{
return false;
}
pPreviewFile = gallery + aPreviewFile;
return true;
}
void parseResponse(const std::string& rResponse, std::vector<PersonaInfo> & aPersonas)
{
orcus::json::document_tree aJsonDoc;
orcus::json_config aConfig;
if (rResponse.empty())
return;
aJsonDoc.load(rResponse, aConfig);
auto aDocumentRoot = aJsonDoc.get_document_root();
if (aDocumentRoot.type() != orcus::json::node_t::object)
{
SAL_WARN("cui.options", "invalid root entries: " << rResponse);
return;
}
auto resultsArray = aDocumentRoot.child("results");
for (size_t i = 0; i < resultsArray.child_count(); ++i)
{
auto arrayElement = resultsArray.child(i);
try
{
PersonaInfo aNewPersona = {
OStringToOUString( OString(arrayElement.child("slug").string_value().get()),
RTL_TEXTENCODING_UTF8 ),
OStringToOUString( OString(arrayElement.child("name").child("en-US").string_value().get()),
RTL_TEXTENCODING_UTF8 ),
OStringToOUString( OString(arrayElement.child("theme_data").child("previewURL").string_value().get()),
RTL_TEXTENCODING_UTF8 ),
OStringToOUString( OString(arrayElement.child("theme_data").child("headerURL").string_value().get()),
RTL_TEXTENCODING_UTF8 ),
OStringToOUString( OString(arrayElement.child("theme_data").child("footerURL").string_value().get()),
RTL_TEXTENCODING_UTF8 ),
OStringToOUString( OString(arrayElement.child("theme_data").child("textcolor").string_value().get()),
RTL_TEXTENCODING_UTF8 )
};
aPersonas.push_back(aNewPersona);
}
catch(orcus::json::document_error& e)
{
// This usually happens when one of the values is null (type() == orcus::json::node_t::null)
// TODO: Allow null values in personas.
SAL_WARN("cui.options", "Persona JSON parse error: " << e.what());
}
}
}
PersonaInfo parseSingleResponse(const std::string& rResponse)
{
orcus::json::document_tree aJsonDoc;
orcus::json_config aConfig;
if (rResponse.empty())
return PersonaInfo();
aJsonDoc.load(rResponse, aConfig);
auto aDocumentRoot = aJsonDoc.get_document_root();
if (aDocumentRoot.type() != orcus::json::node_t::object)
{
SAL_WARN("cui.options", "invalid root entries: " << rResponse);
return PersonaInfo();
}
try
{
auto theme_data = aDocumentRoot.child("theme_data");
PersonaInfo aNewPersona = {
OUString(),
OStringToOUString( OString(theme_data.child("name").string_value().get()),
RTL_TEXTENCODING_UTF8 ),
OStringToOUString( OString(theme_data.child("previewURL").string_value().get()),
RTL_TEXTENCODING_UTF8 ),
OStringToOUString( OString(theme_data.child("headerURL").string_value().get()),
RTL_TEXTENCODING_UTF8 ),
OStringToOUString( OString(theme_data.child("footerURL").string_value().get()),
RTL_TEXTENCODING_UTF8 ),
OStringToOUString( OString(theme_data.child("textcolor").string_value().get()),
RTL_TEXTENCODING_UTF8 )
};
return aNewPersona;
}
catch(orcus::json::document_error& e)
{
// This usually happens when one of the values is null (type() == orcus::json::node_t::null)
// TODO: Allow null values in personas.
// TODO: Give a message to the user
SAL_WARN("cui.options", "Persona JSON parse error: " << e.what());
return PersonaInfo();
}
}
} //End of anonymous namespace
void SearchAndParseThread::execute()
{
m_pPersonaDialog->ClearSearchResults();
OUString sProgress( CuiResId( RID_SVXSTR_SEARCHING ) );
m_pPersonaDialog->SetProgress( sProgress );
uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
if (!m_bDirectURL)
{
OString rURL = OUStringToOString( m_aURL, RTL_TEXTENCODING_UTF8 );
std::string sResponse = curlGet(rURL);
std::vector<PersonaInfo> personaInfos;
parseResponse(sResponse, personaInfos);
if ( personaInfos.empty() )
{
sProgress = CuiResId( RID_SVXSTR_NORESULTS );
m_pPersonaDialog->SetProgress( sProgress );
return;
}
else
{
//Get Preview Files
sal_Int32 nIndex = 0;
for (const auto & personaInfo : personaInfos)
{
if( !m_bExecute )
return;
OUString aPreviewFile;
bool bResult = getPreviewFile(personaInfo, aPreviewFile);
if (!bResult)
{
SAL_INFO("cui.options", "Couldn't get the preview file. Skipping: " << aPreviewFile);
continue;
}
GraphicFilter aFilter;
Graphic aGraphic;
INetURLObject aURLObj( aPreviewFile );
OUString aPersonaSetting = personaInfo.sSlug
+ ";" + personaInfo.sName
+ ";" + personaInfo.sPreviewURL
+ ";" + personaInfo.sHeaderURL
+ ";" + personaInfo.sFooterURL
+ ";" + personaInfo.sTextColor;
m_pPersonaDialog->AddPersonaSetting( aPersonaSetting );
// for VCL to be able to create bitmaps / do visual changes in the thread
SolarMutexGuard aGuard;
aFilter.ImportGraphic( aGraphic, aURLObj );
BitmapEx aBmp = aGraphic.GetBitmapEx();
ScopedVclPtr<VirtualDevice> xVirDev(VclPtr<VirtualDevice>::Create());
xVirDev->SetOutputSizePixel(aBmp.GetSizePixel());
xVirDev->DrawBitmapEx(Point(0, 0), aBmp);
m_pPersonaDialog->SetImages(*xVirDev, personaInfo.sName, nIndex );
if (++nIndex >= MAX_RESULTS)
break;
}
//TODO: Give a message to the user if nIndex == 0
}
}
else
{
//Now we have a slug instead a search term in m_aURL
OString rURL = "https://addons.mozilla.org/api/v3/addons/addon/"
+ OUStringToOString( m_aURL, RTL_TEXTENCODING_UTF8 )
+ "/";
std::string sResponse = curlGet(rURL);
PersonaInfo aPersonaInfo = parseSingleResponse(sResponse);
aPersonaInfo.sSlug = m_aURL;
if ( aPersonaInfo.sName.isEmpty() )
{
//TODO: Give error message to user
sProgress = CuiResId( RID_SVXSTR_NORESULTS );
m_pPersonaDialog->SetProgress( sProgress );
return;
}
else
{
//Get the preview file
if( !m_bExecute )
return;
OUString aPreviewFile;
bool bResult = getPreviewFile(aPersonaInfo, aPreviewFile);
if (!bResult)
{
//TODO: Inform the user
SAL_WARN("cui.options", "Couldn't get the preview file: " << aPreviewFile);
return;
}
GraphicFilter aFilter;
Graphic aGraphic;
INetURLObject aURLObj( aPreviewFile );
OUString aPersonaSetting = aPersonaInfo.sSlug
+ ";" + aPersonaInfo.sName
+ ";" + aPersonaInfo.sPreviewURL
+ ";" + aPersonaInfo.sHeaderURL
+ ";" + aPersonaInfo.sFooterURL
+ ";" + aPersonaInfo.sTextColor;
m_pPersonaDialog->AddPersonaSetting( aPersonaSetting );
// for VCL to be able to create bitmaps / do visual changes in the thread
SolarMutexGuard aGuard;
aFilter.ImportGraphic( aGraphic, aURLObj );
BitmapEx aBmp = aGraphic.GetBitmapEx();
ScopedVclPtr<VirtualDevice> xVirDev(VclPtr<VirtualDevice>::Create());
xVirDev->SetOutputSizePixel(aBmp.GetSizePixel());
xVirDev->DrawBitmapEx(Point(0, 0), aBmp);
m_pPersonaDialog->SetImages( *xVirDev, aPersonaInfo.sName, 0 );
}
}
if( !m_bExecute )
return;
SolarMutexGuard aGuard;
sProgress.clear();
m_pPersonaDialog->SetProgress( sProgress );
}
GetPersonaThread::GetPersonaThread( SelectPersonaDialog* pDialog,
const OUString& rSelectedPersona ) :
Thread( "cuiPersonasGetPersonaThread" ),
m_pPersonaDialog( pDialog ),
m_aSelectedPersona( rSelectedPersona ),
m_bExecute( true )
{
}
GetPersonaThread::~GetPersonaThread()
{
//TODO: Clean-up
}
void GetPersonaThread::execute()
{
OUString sProgress( CuiResId( RID_SVXSTR_APPLYPERSONA ) ), sError;
m_pPersonaDialog->SetProgress( sProgress );
uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
if ( !xFileAccess.is() )
return;
OUString aSlug, aName, aPreviewURL, aHeaderURL, aFooterURL, aTextColor;
OUString aPersonaSetting;
// get the required fields from m_aSelectedPersona
sal_Int32 nIndex = 0;
aSlug = m_aSelectedPersona.getToken(0, ';', nIndex);
aName = m_aSelectedPersona.getToken(0, ';', nIndex);
aPreviewURL = m_aSelectedPersona.getToken(0, ';', nIndex);
aHeaderURL = m_aSelectedPersona.getToken(0, ';', nIndex);
aFooterURL = m_aSelectedPersona.getToken(0, ';', nIndex);
aTextColor = m_aSelectedPersona.getToken(0, ';', nIndex);
// copy the images to the user's gallery
OUString gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
rtl::Bootstrap::expandMacros( gallery );
gallery += "/user/gallery/personas/";
OUString aPreviewFile( aSlug + "/" + INetURLObject( aPreviewURL ).getName() );
OUString aHeaderFile( aSlug + "/" + INetURLObject( aHeaderURL ).getName() );
OUString aFooterFile( aSlug + "/" + INetURLObject( aFooterURL ).getName() );
try {
osl::Directory::createPath( gallery );
if ( !xFileAccess->exists(gallery + aHeaderFile) )
curlDownload( OUStringToOString(aHeaderURL, RTL_TEXTENCODING_UTF8), gallery + aHeaderFile );
if ( !xFileAccess->exists(gallery + aFooterFile) )
curlDownload( OUStringToOString(aFooterURL, RTL_TEXTENCODING_UTF8), gallery + aFooterFile );
}
catch ( const uno::Exception & )
{
SolarMutexGuard aGuard;
sError = CuiResId( RID_SVXSTR_SEARCHERROR );
sError = sError.replaceAll("%1", m_aSelectedPersona);
m_pPersonaDialog->SetProgress( OUString() );
std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
VclMessageType::Error, VclButtonsType::Ok,
sError));
xBox->run();
return;
}
if( !m_bExecute )
return;
SolarMutexGuard aGuard;
aPersonaSetting = aSlug + ";" + aName + ";" + aPreviewFile + ";" + aHeaderFile + ";" + aFooterFile
+ ";" + aTextColor;
m_pPersonaDialog->SetAppliedPersonaSetting( aPersonaSetting );
m_pPersonaDialog->response( RET_OK );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
......@@ -11,22 +11,13 @@
#define INCLUDED_CUI_SOURCE_OPTIONS_PERSONALIZATION_HXX
#include <sfx2/tabdlg.hxx>
#include <salhelper/thread.hxx>
#include <rtl/ref.hxx>
#include <vcl/prgsbar.hxx>
#include <vector>
#include <array>
#include <atomic>
#define MAX_RESULTS 9 // Maximum number of search results
#define MAX_DEFAULT_PERSONAS 6 // Maximum number of default personas
class Edit;
class FixedText;
class FixedHyperlink;
class SearchAndParseThread;
class GetPersonaThread;
class SvxPersonalizationTabPage : public SfxTabPage
{
using SfxTabPage::DeactivatePage;
......@@ -34,17 +25,10 @@ class SvxPersonalizationTabPage : public SfxTabPage
private:
VclPtr<RadioButton> m_pNoPersona; ///< Just the default look, without any bitmap
VclPtr<RadioButton> m_pDefaultPersona; ///< Use the built-in bitmap
VclPtr<RadioButton> m_pOwnPersona; ///< Use the user-defined bitmap
VclPtr<PushButton> m_pSelectPersona; ///< Let the user select in the 'own' case
VclPtr<PushButton> m_vDefaultPersonaImages[MAX_DEFAULT_PERSONAS]; ///< Buttons to show the default persona images
VclPtr<PushButton> m_pExtensionPersonaPreview; ///< Buttons to show the last 3 personas installed via extensions
VclPtr<ListBox> m_pPersonaList; ///< The ListBox to show the list of installed personas
OUString m_aPersonaSettings; ///< Header and footer images + color to be set in the settings.
VclPtr<FixedText> m_pExtensionLabel; ///< The "select persona installed via extensions" label
VclPtr<FixedHyperlink> m_pAppliedThemeLabel; ///< The label for showing applied custom theme
std::vector<OUString> m_vDefaultPersonaSettings;
std::vector<OUString> m_vExtensionPersonaSettings;
public:
SvxPersonalizationTabPage( vcl::Window *pParent, const SfxItemSet &rSet );
......@@ -59,10 +43,6 @@ public:
/// Reset to default settings ([Revert] button).
virtual void Reset( const SfxItemSet *rSet ) override;
void SetPersonaSettings( const OUString& );
void CheckAppliedTheme();
void ShowAppliedThemeLabel( const OUString& );
/*
* Loads the default personas from the shared personas directory
* which resides in the shared gallery.
......@@ -77,105 +57,13 @@ public:
* abstract;Abstract;abstract/preview.jpg;abstract/Header2.jpg;abstract/Footer2.jpg;#ffffff
*/
void LoadDefaultImages();
void LoadExtensionThemes();
private:
/// Handle the Persona selection
DECL_LINK( SelectPersona, Button*, void );
/// When 'own' is chosen, but the Persona is not chosen yet.
DECL_LINK( ForceSelect, Button*, void );
/// Handle the default Persona selection
DECL_LINK( DefaultPersona, Button*, void );
/// Handle the Personas installed through extensions selection
DECL_LINK( SelectInstalledPersona, ListBox&, void );
};
/** Dialog that will allow the user to choose a Persona to use. */
class SelectPersonaDialog : public weld::GenericDialogController
{
private:
std::vector<OUString> m_vPersonaSettings;
OUString m_aSelectedPersona;
OUString m_aAppliedPersona;
std::unique_ptr<weld::Entry> m_xEdit; ///< The input line for the search term
std::unique_ptr<weld::Button> m_xSearchButton; ///< The search button
std::unique_ptr<weld::Label> m_xProgressLabel; ///< The label for showing progress of search
std::unique_ptr<weld::ComboBox> m_xCategories; ///< The list of categories
std::unique_ptr<weld::Button> m_xOkButton; ///< The OK button
std::unique_ptr<weld::Button> m_xCancelButton; ///< The Cancel button
std::unique_ptr<weld::Button> m_vResultList[MAX_RESULTS]; ///< List of buttons to show search results
::rtl::Reference< SearchAndParseThread > m_pSearchThread;
::rtl::Reference< GetPersonaThread > m_pGetPersonaThread;
public:
explicit SelectPersonaDialog(weld::Window *pParent);
virtual ~SelectPersonaDialog() override;
OUString GetSelectedPersona() const;
void SetProgress( const OUString& );
/**
* @brief Assigns preview images to result buttons
* @param aPreviewImage Persona preview image
* @param sName Name of the persona
* @param nIndex Index number of the result button
*/
void SetImages( VirtualDevice& rPreviewImage, const OUString& sName, const sal_Int32& nIndex );
void AddPersonaSetting( OUString const & );
void ClearSearchResults();
void SetAppliedPersonaSetting( OUString const & );
const OUString& GetAppliedPersonaSetting() const;
private:
/// Handle the Search button
DECL_LINK( SearchPersonas, weld::Button&, void );
/// Handle persona categories list box
DECL_LINK( SelectCategory, weld::ComboBox&, void );
DECL_LINK( SelectPersona, weld::Button&, void );
DECL_LINK( ActionOK, weld::Button&, void );
DECL_LINK( ActionCancel, weld::Button&, void );
};
class SearchAndParseThread: public salhelper::Thread
{
private:
SelectPersonaDialog* m_pPersonaDialog;
OUString m_aURL;
std::atomic<bool> m_bExecute;
bool m_bDirectURL;
virtual ~SearchAndParseThread() override;
virtual void execute() override;
public:
SearchAndParseThread( SelectPersonaDialog* pDialog,
const OUString& rURL, bool bDirectURL );
void StopExecution() { m_bExecute = false; }
};
class GetPersonaThread: public salhelper::Thread
{
private:
SelectPersonaDialog* m_pPersonaDialog;
OUString m_aSelectedPersona;
std::atomic<bool> m_bExecute;
virtual ~GetPersonaThread() override;
virtual void execute() override;
public:
GetPersonaThread( SelectPersonaDialog* pDialog,
const OUString& rSelectedPersona );
void StopExecution() { m_bExecute = false; }
};
#endif // INCLUDED_CUI_SOURCE_OPTIONS_PERSONALIZATION_HXX
......
......@@ -52,7 +52,6 @@
<property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<property name="group">own_persona</property>
</object>
<packing>
<property name="expand">False</property>
......@@ -183,111 +182,6 @@
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">12</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkRadioButton" id="own_persona">
<property name="label" translatable="yes" context="personalization_tab|own_persona">Own Theme</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<property name="group">no_persona</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLinkButton" id="applied_theme_link">
<property name="label" translatable="no">Applied persona's name and URL</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="select_persona">
<property name="label" translatable="yes" context="personalization_tab|select_persona">Load Firefox theme</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="extensions_label">
<property name="can_focus">False</property>
<property name="label" translatable="yes" context="personalization_tab|extensions_label">Or, select from the Themes installed via extensions:</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkTreeView" id="installed_personas:border">
<property name="can_focus">True</property>
<property name="show_expanders">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection2"/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="persona_preview">
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
</object>
</child>
</object>
......@@ -296,7 +190,7 @@
<object class="GtkLabel" id="personas_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes" context="personalization_tab|personas_label">Firefox Themes</property>
<property name="label" translatable="yes" context="personalization_tab|personas_label">LibreOffice Themes</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
......@@ -310,9 +204,4 @@
</packing>
</child>
</object>
<object class="GtkSizeGroup" id="sizegroup1">
<widgets>
<widget name="select_persona"/>
</widgets>
</object>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface domain="cui">
<requires lib="gtk+" version="3.18"/>
<object class="GtkDialog" id="SelectPersonaDialog">
<property name="can_focus">False</property>
<property name="border_width">6</property>
<property name="title" translatable="yes" context="select_persona_dialog|SelectPersonaDialog">Select Firefox Theme</property>
<property name="modal">True</property>
<property name="default_width">0</property>
<property name="default_height">0</property>
<property name="type_hint">dialog</property>
<child>
<placeholder/>
</child>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="ok">
<property name="label">gtk-ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cancel">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="help">
<property name="label">gtk-help</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
<property name="secondary">True</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="progress_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="ellipsize">end</property>
<property name="width_chars">45</property>
<property name="max_width_chars">45</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
<property name="secondary">True</property>
<property name="non_homogeneous">True</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkEntry" id="search_term">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="activates_default">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="placeholder_text" translatable="yes" context="select_persona_dialog|search_term">Search term or address</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="search_personas">
<property name="label" context="select_persona_dialog|search_personas">gtk-find</property>
<property name="width_request">85</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box6">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="categories_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes" context="select_persona_dialog|categories_label">Ca_tegory:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">categoriesCB</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="categoriesCB">
<property name="visible">True</property>
<property name="can_focus">False</property>
<items>
<item id="abstract" translatable="yes" context="select_persona_dialog|categoriesCB">Abstract</item>
<item id="causes" translatable="yes" context="select_persona_dialog|categoriesCB">Causes</item>
<item id="fashion" translatable="yes" context="select_persona_dialog|categoriesCB">Fashion</item>
<item id="film-and-tv" translatable="yes" context="select_persona_dialog|categoriesCB">Film and TV</item>
<item id="firefox" translatable="yes" context="select_persona_dialog|categoriesCB">Firefox</item>
<item id="foxkeh" translatable="yes" comments="https://addons.mozilla.org/en-US/firefox/themes/" context="select_persona_dialog|categoriesCB">Foxkeh</item>
<item id="holiday" translatable="yes" context="select_persona_dialog|categoriesCB">Holiday</item>
<item id="music" translatable="yes" context="select_persona_dialog|categoriesCB">Music</item>
<item id="nature" translatable="yes" context="select_persona_dialog|categoriesCB">Nature</item>
<item id="other" translatable="yes" context="select_persona_dialog|categoriesCB">Other</item>
<item id="scenery" translatable="yes" context="select_persona_dialog|categoriesCB">Scenery</item>
<item id="seasonal" translatable="yes" context="select_persona_dialog|categoriesCB">Seasonal</item>
<item id="solid" translatable="yes" context="select_persona_dialog|categoriesCB">Solid</item>
<item id="sports" translatable="yes" context="select_persona_dialog|categoriesCB">Sports</item>
<item id="websites" translatable="yes" context="select_persona_dialog|categoriesCB">Websites</item>
<item id="featured" translatable="yes" context="select_persona_dialog|categoriesCB">Featured</item>
</items>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid1">
<property name="width_request">624</property>
<property name="height_request">219</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkButton" id="result1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="result2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="result3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="result4">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="result5">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="result6">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="result7">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="result8">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="result9">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-5">ok</action-widget>
<action-widget response="-6">cancel</action-widget>
<action-widget response="-11">help</action-widget>
</action-widgets>
</object>
</interface>
......@@ -390,23 +390,6 @@ cui/uiconfig/ui/securityoptionsdialog.ui://GtkImage[@id='locksavesenddocs'] no-l
cui/uiconfig/ui/securityoptionsdialog.ui://GtkImage[@id='lockwhensigning'] no-labelled-by
cui/uiconfig/ui/securityoptionsdialog.ui://GtkImage[@id='lockwhenpdf'] no-labelled-by
cui/uiconfig/ui/securityoptionsdialog.ui://GtkLabel[@id='label3'] orphan-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkEntry[@id='search_term'] no-labelled-by
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='suggestion1'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='suggestion2'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='suggestion3'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='suggestion4'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='suggestion5'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='suggestion6'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='result1'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='result2'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='result3'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='result4'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='result5'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='result6'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='result7'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='result8'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkButton[@id='result9'] button-no-label
cui/uiconfig/ui/select_persona_dialog.ui://GtkLabel[@id='progress_label'] orphan-label
cui/uiconfig/ui/signsignatureline.ui://GtkTextView[@id='edit_comment'] duplicate-mnemonic
cui/uiconfig/ui/signsignatureline.ui://GtkLabel[@id='label_or'] orphan-label
cui/uiconfig/ui/specialcharacters.ui://GtkLabel[@id='hexulabel'] orphan-label
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment