2100 lines
67 KiB
C++
2100 lines
67 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <config_gio.h>
|
|
#include <config_gpgme.h>
|
|
|
|
#include <com/sun/star/awt/SystemDependentXWindow.hpp>
|
|
#include <com/sun/star/awt/Toolkit.hpp>
|
|
#include <com/sun/star/awt/XSystemDependentWindowPeer.hpp>
|
|
#include <com/sun/star/frame/Desktop.hpp>
|
|
#include <com/sun/star/lang/IllegalArgumentException.hpp>
|
|
#include <com/sun/star/lang/SystemDependent.hpp>
|
|
#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
|
|
#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
|
|
#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
|
|
#include <osl/diagnose.h>
|
|
#include <rtl/process.h>
|
|
#include <sal/log.hxx>
|
|
#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
|
|
#include <com/sun/star/ui/dialogs/ControlActions.hpp>
|
|
#include <com/sun/star/uno/Any.hxx>
|
|
#include <unx/gtk/gtkdata.hxx>
|
|
#include <unx/gtk/gtkinst.hxx>
|
|
|
|
#include <utility>
|
|
#include <vcl/svapp.hxx>
|
|
|
|
#include <o3tl/string_view.hxx>
|
|
#include <tools/urlobj.hxx>
|
|
#include <unotools/ucbhelper.hxx>
|
|
|
|
#include <algorithm>
|
|
#include <set>
|
|
#include <string.h>
|
|
#include <string_view>
|
|
|
|
#include "SalGtkFilePicker.hxx"
|
|
|
|
using namespace ::com::sun::star;
|
|
using namespace ::com::sun::star::ui::dialogs;
|
|
using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
|
|
using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
|
|
using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
|
|
using namespace ::com::sun::star::lang;
|
|
using namespace ::com::sun::star::beans;
|
|
using namespace ::com::sun::star::uno;
|
|
|
|
struct FilterEntry
|
|
{
|
|
protected:
|
|
OUString m_sTitle;
|
|
OUString m_sFilter;
|
|
|
|
css::uno::Sequence< css::beans::StringPair > m_aSubFilters;
|
|
|
|
public:
|
|
FilterEntry( OUString _aTitle, OUString _aFilter )
|
|
:m_sTitle(std::move( _aTitle ))
|
|
,m_sFilter(std::move( _aFilter ))
|
|
{
|
|
}
|
|
|
|
const OUString& getTitle() const { return m_sTitle; }
|
|
const OUString& getFilter() const { return m_sFilter; }
|
|
|
|
/// determines if the filter has sub filter (i.e., the filter is a filter group in real)
|
|
bool hasSubFilters( ) const;
|
|
|
|
/** retrieves the filters belonging to the entry
|
|
*/
|
|
void getSubFilters( css::uno::Sequence< css::beans::StringPair >& _rSubFilterList );
|
|
|
|
// helpers for iterating the sub filters
|
|
const css::beans::StringPair* beginSubFilters() const { return m_aSubFilters.begin(); }
|
|
const css::beans::StringPair* endSubFilters() const { return m_aSubFilters.end(); }
|
|
};
|
|
|
|
bool FilterEntry::hasSubFilters() const
|
|
{
|
|
return m_aSubFilters.hasElements();
|
|
}
|
|
|
|
void FilterEntry::getSubFilters( css::uno::Sequence< css::beans::StringPair >& _rSubFilterList )
|
|
{
|
|
_rSubFilterList = m_aSubFilters;
|
|
}
|
|
|
|
void SalGtkFilePicker::dialog_mapped_cb(GtkWidget *, SalGtkFilePicker *pobjFP)
|
|
{
|
|
pobjFP->InitialMapping();
|
|
}
|
|
|
|
void SalGtkFilePicker::InitialMapping()
|
|
{
|
|
if (!mbPreviewState )
|
|
{
|
|
gtk_widget_hide( m_pPreview );
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_file_chooser_set_preview_widget_active( GTK_FILE_CHOOSER( m_pDialog ), false);
|
|
#endif
|
|
}
|
|
gtk_widget_set_size_request (m_pPreview, -1, -1);
|
|
}
|
|
|
|
SalGtkFilePicker::SalGtkFilePicker( const uno::Reference< uno::XComponentContext >& xContext ) :
|
|
SalGtkPicker( xContext ),
|
|
SalGtkFilePicker_Base( m_rbHelperMtx ),
|
|
m_pVBox ( nullptr ),
|
|
mnHID_FolderChange( 0 ),
|
|
mnHID_SelectionChange( 0 ),
|
|
bVersionWidthUnset( false ),
|
|
mbPreviewState( false ),
|
|
mbInitialized(false),
|
|
mHID_Preview( 0 ),
|
|
m_pPreview( nullptr ),
|
|
m_pPseudoFilter( nullptr )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < TOGGLE_LAST; i++ )
|
|
{
|
|
m_pToggles[i] = nullptr;
|
|
mbToggleVisibility[i] = false;
|
|
}
|
|
|
|
for( i = 0; i < BUTTON_LAST; i++ )
|
|
{
|
|
m_pButtons[i] = nullptr;
|
|
mbButtonVisibility[i] = false;
|
|
}
|
|
|
|
for( i = 0; i < LIST_LAST; i++ )
|
|
{
|
|
m_pHBoxs[i] = nullptr;
|
|
m_pLists[i] = nullptr;
|
|
m_pListLabels[i] = nullptr;
|
|
mbListVisibility[i] = false;
|
|
}
|
|
|
|
OUString aFilePickerTitle = getResString( FILE_PICKER_TITLE_OPEN );
|
|
|
|
m_pDialog = GTK_WIDGET(g_object_new(GTK_TYPE_FILE_CHOOSER_DIALOG,
|
|
"title", OUStringToOString(aFilePickerTitle, RTL_TEXTENCODING_UTF8).getStr(),
|
|
"action", GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
nullptr));
|
|
gtk_window_set_modal(GTK_WINDOW(m_pDialog), true);
|
|
gtk_dialog_set_default_response( GTK_DIALOG (m_pDialog), GTK_RESPONSE_ACCEPT );
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
#if ENABLE_GIO
|
|
gtk_file_chooser_set_local_only( GTK_FILE_CHOOSER( m_pDialog ), false );
|
|
#endif
|
|
#endif
|
|
|
|
gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( m_pDialog ), false );
|
|
|
|
m_pVBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
|
|
|
// We don't want clickable items to have a huge hit-area
|
|
GtkWidget *pHBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
|
GtkWidget *pThinVBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_box_pack_end (GTK_BOX( m_pVBox ), pHBox, false, false, 0);
|
|
gtk_box_pack_start (GTK_BOX( pHBox ), pThinVBox, false, false, 0);
|
|
#else
|
|
gtk_box_append(GTK_BOX(m_pVBox), pHBox);
|
|
gtk_box_prepend(GTK_BOX(m_pVBox), pThinVBox);
|
|
#endif
|
|
gtk_widget_show( pHBox );
|
|
gtk_widget_show( pThinVBox );
|
|
|
|
OUString aLabel;
|
|
|
|
for( i = 0; i < TOGGLE_LAST; i++ )
|
|
{
|
|
m_pToggles[i] = gtk_check_button_new();
|
|
|
|
#define LABEL_TOGGLE( elem ) \
|
|
case elem : \
|
|
aLabel = getResString( CHECKBOX_##elem ); \
|
|
setLabel( CHECKBOX_##elem, aLabel ); \
|
|
break
|
|
|
|
switch( i ) {
|
|
LABEL_TOGGLE( AUTOEXTENSION );
|
|
LABEL_TOGGLE( PASSWORD );
|
|
LABEL_TOGGLE( GPGENCRYPTION );
|
|
LABEL_TOGGLE( GPGSIGN );
|
|
LABEL_TOGGLE( FILTEROPTIONS );
|
|
LABEL_TOGGLE( READONLY );
|
|
LABEL_TOGGLE( LINK );
|
|
LABEL_TOGGLE( PREVIEW );
|
|
LABEL_TOGGLE( SELECTION );
|
|
default:
|
|
SAL_WARN( "vcl.gtk", "Handle unknown control " << i);
|
|
break;
|
|
}
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_box_pack_end( GTK_BOX( pThinVBox ), m_pToggles[i], false, false, 0 );
|
|
#else
|
|
gtk_box_append(GTK_BOX(pThinVBox), m_pToggles[i]);
|
|
#endif
|
|
}
|
|
|
|
for( i = 0; i < LIST_LAST; i++ )
|
|
{
|
|
m_pHBoxs[i] = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
|
|
|
GtkListStore *pListStores[ LIST_LAST ];
|
|
pListStores[i] = gtk_list_store_new (1, G_TYPE_STRING);
|
|
m_pLists[i] = gtk_combo_box_new_with_model(GTK_TREE_MODEL(pListStores[i]));
|
|
g_object_unref (pListStores[i]); // owned by the widget.
|
|
GtkCellRenderer *pCell = gtk_cell_renderer_text_new ();
|
|
gtk_cell_layout_pack_start(
|
|
GTK_CELL_LAYOUT(m_pLists[i]), pCell, true);
|
|
gtk_cell_layout_set_attributes(
|
|
GTK_CELL_LAYOUT (m_pLists[i]), pCell, "text", 0, nullptr);
|
|
|
|
m_pListLabels[i] = gtk_label_new( "" );
|
|
|
|
#define LABEL_LIST( elem ) \
|
|
case elem : \
|
|
aLabel = getResString( LISTBOX_##elem##_LABEL ); \
|
|
setLabel( LISTBOX_##elem##_LABEL, aLabel ); \
|
|
break
|
|
|
|
switch( i )
|
|
{
|
|
LABEL_LIST( VERSION );
|
|
LABEL_LIST( TEMPLATE );
|
|
LABEL_LIST( IMAGE_TEMPLATE );
|
|
LABEL_LIST( IMAGE_ANCHOR );
|
|
default:
|
|
SAL_WARN( "vcl.gtk", "Handle unknown control " << i);
|
|
break;
|
|
}
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_box_pack_end( GTK_BOX( m_pHBoxs[i] ), m_pLists[i], false, false, 0 );
|
|
gtk_box_pack_end( GTK_BOX( m_pHBoxs[i] ), m_pListLabels[i], false, false, 0 );
|
|
#else
|
|
gtk_box_append(GTK_BOX(m_pHBoxs[i]), m_pLists[i]);
|
|
gtk_box_append(GTK_BOX(m_pHBoxs[i]), m_pListLabels[i]);
|
|
#endif
|
|
gtk_label_set_mnemonic_widget( GTK_LABEL(m_pListLabels[i]), m_pLists[i] );
|
|
gtk_box_set_spacing( GTK_BOX( m_pHBoxs[i] ), 12 );
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_box_pack_end( GTK_BOX( m_pVBox ), m_pHBoxs[i], false, false, 0 );
|
|
#else
|
|
gtk_box_append(GTK_BOX(m_pVBox), m_pHBoxs[i]);
|
|
#endif
|
|
}
|
|
|
|
aLabel = getResString( FILE_PICKER_FILE_TYPE );
|
|
m_pFilterExpander = gtk_expander_new_with_mnemonic(
|
|
OUStringToOString( aLabel, RTL_TEXTENCODING_UTF8 ).getStr());
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_box_pack_end( GTK_BOX( m_pVBox ), m_pFilterExpander, false, true, 0 );
|
|
#else
|
|
gtk_box_append(GTK_BOX(m_pVBox), m_pFilterExpander);
|
|
#endif
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
GtkWidget *scrolled_window = gtk_scrolled_window_new (nullptr, nullptr);
|
|
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
|
|
GTK_SHADOW_IN);
|
|
#else
|
|
GtkWidget *scrolled_window = gtk_scrolled_window_new();
|
|
gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(scrolled_window), true);
|
|
#endif
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_container_add (GTK_CONTAINER (m_pFilterExpander), scrolled_window);
|
|
#else
|
|
gtk_expander_set_child(GTK_EXPANDER(m_pFilterExpander), scrolled_window);
|
|
#endif
|
|
gtk_widget_show (scrolled_window);
|
|
|
|
m_pFilterStore = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING,
|
|
G_TYPE_STRING, G_TYPE_STRING);
|
|
m_pFilterView = gtk_tree_view_new_with_model (GTK_TREE_MODEL(m_pFilterStore));
|
|
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(m_pFilterView), false);
|
|
|
|
GtkCellRenderer *cell = nullptr;
|
|
|
|
for (i = 0; i < 2; ++i)
|
|
{
|
|
GtkTreeViewColumn *column = gtk_tree_view_column_new ();
|
|
cell = gtk_cell_renderer_text_new ();
|
|
gtk_tree_view_column_set_expand (column, true);
|
|
gtk_tree_view_column_pack_start (column, cell, false);
|
|
gtk_tree_view_column_set_attributes (column, cell, "text", i, nullptr);
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW(m_pFilterView), column);
|
|
}
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_container_add (GTK_CONTAINER (scrolled_window), m_pFilterView);
|
|
#else
|
|
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled_window), m_pFilterView);
|
|
#endif
|
|
gtk_widget_show (m_pFilterView);
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( m_pDialog ), m_pVBox );
|
|
#endif
|
|
|
|
m_pPreview = gtk_image_new();
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_file_chooser_set_preview_widget( GTK_FILE_CHOOSER( m_pDialog ), m_pPreview );
|
|
#endif
|
|
|
|
g_signal_connect( G_OBJECT( m_pToggles[PREVIEW] ), "toggled",
|
|
G_CALLBACK( preview_toggled_cb ), this );
|
|
g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW(m_pFilterView)), "changed",
|
|
G_CALLBACK ( type_changed_cb ), this);
|
|
g_signal_connect( G_OBJECT( m_pDialog ), "notify::filter",
|
|
G_CALLBACK( filter_changed_cb ), this);
|
|
g_signal_connect( G_OBJECT( m_pFilterExpander ), "activate",
|
|
G_CALLBACK( expander_changed_cb ), this);
|
|
g_signal_connect (G_OBJECT( m_pDialog ), "map",
|
|
G_CALLBACK (dialog_mapped_cb), this);
|
|
|
|
gtk_widget_show( m_pVBox );
|
|
|
|
PangoLayout *layout = gtk_widget_create_pango_layout (m_pFilterView, nullptr);
|
|
guint ypad;
|
|
PangoRectangle row_height;
|
|
pango_layout_set_markup (layout, "All Files", -1);
|
|
pango_layout_get_pixel_extents (layout, nullptr, &row_height);
|
|
g_object_unref (layout);
|
|
|
|
g_object_get (cell, "ypad", &ypad, nullptr);
|
|
guint height = (row_height.height + 2*ypad) * 5;
|
|
gtk_widget_set_size_request (m_pFilterView, -1, height);
|
|
gtk_widget_set_size_request (m_pPreview, 1, height);
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_file_chooser_set_preview_widget_active( GTK_FILE_CHOOSER( m_pDialog ), true);
|
|
#endif
|
|
}
|
|
|
|
// XFilePickerNotifier
|
|
|
|
void SAL_CALL SalGtkFilePicker::addFilePickerListener( const uno::Reference<XFilePickerListener>& xListener )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ENSURE(!m_xListener.is(),
|
|
"SalGtkFilePicker only talks with one listener at a time...");
|
|
m_xListener = xListener;
|
|
}
|
|
|
|
void SAL_CALL SalGtkFilePicker::removeFilePickerListener( const uno::Reference<XFilePickerListener>& )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
m_xListener.clear();
|
|
}
|
|
|
|
// FilePicker Event functions
|
|
|
|
void SalGtkFilePicker::impl_fileSelectionChanged( const FilePickerEvent& aEvent )
|
|
{
|
|
if (m_xListener.is()) m_xListener->fileSelectionChanged( aEvent );
|
|
}
|
|
|
|
void SalGtkFilePicker::impl_directoryChanged( const FilePickerEvent& aEvent )
|
|
{
|
|
if (m_xListener.is()) m_xListener->directoryChanged( aEvent );
|
|
}
|
|
|
|
void SalGtkFilePicker::impl_controlStateChanged( const FilePickerEvent& aEvent )
|
|
{
|
|
if (m_xListener.is()) m_xListener->controlStateChanged( aEvent );
|
|
}
|
|
|
|
static bool
|
|
isFilterString( std::u16string_view rFilterString, const char *pMatch )
|
|
{
|
|
sal_Int32 nIndex = 0;
|
|
bool bIsFilter = true;
|
|
|
|
OUString aMatch(OUString::createFromAscii(pMatch));
|
|
|
|
do
|
|
{
|
|
std::u16string_view aToken = o3tl::getToken(rFilterString, 0, ';', nIndex );
|
|
if( !o3tl::starts_with(aToken, aMatch) )
|
|
{
|
|
bIsFilter = false;
|
|
break;
|
|
}
|
|
}
|
|
while( nIndex >= 0 );
|
|
|
|
return bIsFilter;
|
|
}
|
|
|
|
static OUString
|
|
shrinkFilterName( const OUString &rFilterName, bool bAllowNoStar = false )
|
|
{
|
|
int i;
|
|
int nBracketLen = -1;
|
|
int nBracketEnd = -1;
|
|
const sal_Unicode *pStr = rFilterName.getStr();
|
|
OUString aRealName = rFilterName;
|
|
|
|
for( i = aRealName.getLength() - 1; i > 0; i-- )
|
|
{
|
|
if( pStr[i] == ')' )
|
|
nBracketEnd = i;
|
|
else if( pStr[i] == '(' )
|
|
{
|
|
nBracketLen = nBracketEnd - i;
|
|
if( nBracketEnd <= 0 )
|
|
continue;
|
|
if( isFilterString( rFilterName.subView( i + 1, nBracketLen - 1 ), "*." ) )
|
|
aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
|
|
else if (bAllowNoStar)
|
|
{
|
|
if( isFilterString( rFilterName.subView( i + 1, nBracketLen - 1 ), ".") )
|
|
aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
|
|
}
|
|
}
|
|
}
|
|
|
|
return aRealName;
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct FilterTitleMatch
|
|
{
|
|
protected:
|
|
const OUString& rTitle;
|
|
|
|
public:
|
|
explicit FilterTitleMatch( const OUString& _rTitle ) : rTitle( _rTitle ) { }
|
|
|
|
bool operator () ( const FilterEntry& _rEntry )
|
|
{
|
|
bool bMatch;
|
|
if( !_rEntry.hasSubFilters() )
|
|
// a real filter
|
|
bMatch = (_rEntry.getTitle() == rTitle)
|
|
|| (shrinkFilterName(_rEntry.getTitle()) == rTitle);
|
|
else
|
|
// a filter group -> search the sub filters
|
|
bMatch =
|
|
::std::any_of(
|
|
_rEntry.beginSubFilters(),
|
|
_rEntry.endSubFilters(),
|
|
*this
|
|
);
|
|
|
|
return bMatch;
|
|
}
|
|
bool operator () ( const css::beans::StringPair& _rEntry )
|
|
{
|
|
OUString aShrunkName = shrinkFilterName( _rEntry.First );
|
|
return aShrunkName == rTitle;
|
|
}
|
|
};
|
|
}
|
|
|
|
bool SalGtkFilePicker::FilterNameExists( const OUString& rTitle )
|
|
{
|
|
bool bRet = false;
|
|
|
|
if( m_pFilterVector )
|
|
bRet =
|
|
::std::any_of(
|
|
m_pFilterVector->begin(),
|
|
m_pFilterVector->end(),
|
|
FilterTitleMatch( rTitle )
|
|
);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
bool SalGtkFilePicker::FilterNameExists( const css::uno::Sequence< css::beans::StringPair >& _rGroupedFilters )
|
|
{
|
|
bool bRet = false;
|
|
|
|
if( m_pFilterVector )
|
|
{
|
|
bRet = std::any_of(_rGroupedFilters.begin(), _rGroupedFilters.end(),
|
|
[&](const css::beans::StringPair& rFilter) {
|
|
return ::std::any_of( m_pFilterVector->begin(), m_pFilterVector->end(), FilterTitleMatch( rFilter.First ) ); });
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
void SalGtkFilePicker::ensureFilterVector( const OUString& _rInitialCurrentFilter )
|
|
{
|
|
if( !m_pFilterVector )
|
|
{
|
|
m_pFilterVector.reset( new std::vector<FilterEntry> );
|
|
|
|
// set the first filter to the current filter
|
|
if ( m_aCurrentFilter.isEmpty() )
|
|
m_aCurrentFilter = _rInitialCurrentFilter;
|
|
}
|
|
}
|
|
|
|
void SAL_CALL SalGtkFilePicker::appendFilter( const OUString& aTitle, const OUString& aFilter )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
if( FilterNameExists( aTitle ) )
|
|
throw IllegalArgumentException();
|
|
|
|
// ensure that we have a filter list
|
|
ensureFilterVector( aTitle );
|
|
|
|
// append the filter
|
|
m_pFilterVector->insert( m_pFilterVector->end(), FilterEntry( aTitle, aFilter ) );
|
|
}
|
|
|
|
void SAL_CALL SalGtkFilePicker::setCurrentFilter( const OUString& aTitle )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
if( aTitle != m_aCurrentFilter )
|
|
{
|
|
m_aCurrentFilter = aTitle;
|
|
SetCurFilter( m_aCurrentFilter );
|
|
}
|
|
|
|
// TODO m_pImpl->setCurrentFilter( aTitle );
|
|
}
|
|
|
|
void SalGtkFilePicker::updateCurrentFilterFromName(const gchar* filtername)
|
|
{
|
|
OUString aFilterName(filtername, strlen(filtername), RTL_TEXTENCODING_UTF8);
|
|
if (m_pFilterVector)
|
|
{
|
|
for (auto const& filter : *m_pFilterVector)
|
|
{
|
|
if (aFilterName == shrinkFilterName(filter.getTitle()))
|
|
{
|
|
m_aCurrentFilter = filter.getTitle();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SalGtkFilePicker::UpdateFilterfromUI()
|
|
{
|
|
// Update the filtername from the users selection if they have had a chance to do so.
|
|
// If the user explicitly sets a type then use that, if not then take the implicit type
|
|
// from the filter of the files glob on which he is currently searching
|
|
if (!mnHID_FolderChange || !mnHID_SelectionChange)
|
|
return;
|
|
|
|
GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_pFilterView));
|
|
GtkTreeIter iter;
|
|
GtkTreeModel *model;
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
{
|
|
gchar *title;
|
|
gtk_tree_model_get (model, &iter, 2, &title, -1);
|
|
updateCurrentFilterFromName(title);
|
|
g_free (title);
|
|
}
|
|
else if( GtkFileFilter *filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(m_pDialog)))
|
|
{
|
|
if (m_pPseudoFilter != filter)
|
|
updateCurrentFilterFromName(gtk_file_filter_get_name( filter ));
|
|
else
|
|
updateCurrentFilterFromName(OUStringToOString( m_aInitialFilter, RTL_TEXTENCODING_UTF8 ).getStr());
|
|
}
|
|
}
|
|
|
|
OUString SAL_CALL SalGtkFilePicker::getCurrentFilter()
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
UpdateFilterfromUI();
|
|
|
|
return m_aCurrentFilter;
|
|
}
|
|
|
|
// XFilterGroupManager functions
|
|
|
|
void SAL_CALL SalGtkFilePicker::appendFilterGroup( const OUString& /*sGroupTitle*/, const uno::Sequence<beans::StringPair>& aFilters )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
// TODO m_pImpl->appendFilterGroup( sGroupTitle, aFilters );
|
|
// check the names
|
|
if( FilterNameExists( aFilters ) )
|
|
// TODO: a more precise exception message
|
|
throw IllegalArgumentException();
|
|
|
|
// ensure that we have a filter list
|
|
OUString sInitialCurrentFilter;
|
|
if( aFilters.hasElements() )
|
|
sInitialCurrentFilter = aFilters[0].First;
|
|
|
|
ensureFilterVector( sInitialCurrentFilter );
|
|
|
|
// append the filter
|
|
for( const auto& rSubFilter : aFilters )
|
|
m_pFilterVector->insert( m_pFilterVector->end(), FilterEntry( rSubFilter.First, rSubFilter.Second ) );
|
|
|
|
}
|
|
|
|
// XFilePicker functions
|
|
|
|
void SAL_CALL SalGtkFilePicker::setMultiSelectionMode( sal_Bool bMode )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(m_pDialog), bMode );
|
|
}
|
|
|
|
void SAL_CALL SalGtkFilePicker::setDefaultName( const OUString& aName )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
OString aStr = OUStringToOString( aName, RTL_TEXTENCODING_UTF8 );
|
|
GtkFileChooserAction eAction = gtk_file_chooser_get_action( GTK_FILE_CHOOSER( m_pDialog ) );
|
|
|
|
// set_current_name launches a Gtk critical error if called for other than save
|
|
if( GTK_FILE_CHOOSER_ACTION_SAVE == eAction )
|
|
gtk_file_chooser_set_current_name( GTK_FILE_CHOOSER( m_pDialog ), aStr.getStr() );
|
|
}
|
|
|
|
void SAL_CALL SalGtkFilePicker::setDisplayDirectory( const OUString& rDirectory )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
implsetDisplayDirectory(rDirectory);
|
|
}
|
|
|
|
OUString SAL_CALL SalGtkFilePicker::getDisplayDirectory()
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
return implgetDisplayDirectory();
|
|
}
|
|
|
|
uno::Sequence<OUString> SAL_CALL SalGtkFilePicker::getFiles()
|
|
{
|
|
// no member access => no mutex needed
|
|
|
|
uno::Sequence< OUString > aFiles = getSelectedFiles();
|
|
/*
|
|
The previous multiselection API design was completely broken
|
|
and unimplementable for some heterogeneous pseudo-URIs eg. search:
|
|
Thus crop unconditionally to a single selection.
|
|
*/
|
|
aFiles.realloc (1);
|
|
return aFiles;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
bool lcl_matchFilter( std::u16string_view rFilter, std::u16string_view rExt )
|
|
{
|
|
const sal_Unicode cSep {';'};
|
|
size_t nIdx {0};
|
|
|
|
for (;;)
|
|
{
|
|
const size_t nBegin = rFilter.find(rExt, nIdx);
|
|
|
|
if (nBegin == std::u16string_view::npos) // not found
|
|
break;
|
|
|
|
// Let nIdx point to end of matched string, useful in order to
|
|
// check string boundaries and also for a possible next iteration
|
|
nIdx = nBegin + rExt.size();
|
|
|
|
// Check if the found occurrence is an exact match: right side
|
|
if (nIdx < rFilter.size() && rFilter[nIdx]!=cSep)
|
|
continue;
|
|
|
|
// Check if the found occurrence is an exact match: left side
|
|
if (nBegin>0 && rFilter[nBegin-1]!=cSep)
|
|
continue;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
uno::Sequence<OUString> SAL_CALL SalGtkFilePicker::getSelectedFiles()
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
GSList* pPathList = gtk_file_chooser_get_uris( GTK_FILE_CHOOSER(m_pDialog) );
|
|
int nCount = g_slist_length( pPathList );
|
|
#else
|
|
GListModel* pPathList = gtk_file_chooser_get_files(GTK_FILE_CHOOSER(m_pDialog));
|
|
int nCount = g_list_model_get_n_items(pPathList);
|
|
#endif
|
|
|
|
int nIndex = 0;
|
|
|
|
// get the current action setting
|
|
GtkFileChooserAction eAction = gtk_file_chooser_get_action(
|
|
GTK_FILE_CHOOSER( m_pDialog ));
|
|
|
|
uno::Sequence< OUString > aSelectedFiles(nCount);
|
|
auto aSelectedFilesRange = asNonConstRange(aSelectedFiles);
|
|
|
|
// Convert to OOo
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
for( GSList *pElem = pPathList; pElem; pElem = pElem->next)
|
|
{
|
|
gchar *pURI = static_cast<gchar*>(pElem->data);
|
|
#else
|
|
while (gpointer pElem = g_list_model_get_item(pPathList, nIndex))
|
|
{
|
|
gchar *pURI = g_file_get_uri(static_cast<GFile*>(pElem));
|
|
#endif
|
|
|
|
aSelectedFilesRange[ nIndex ] = uritounicode(pURI);
|
|
|
|
if( GTK_FILE_CHOOSER_ACTION_SAVE == eAction )
|
|
{
|
|
OUString sFilterName;
|
|
sal_Int32 nTokenIndex = 0;
|
|
bool bExtensionTypedIn = false;
|
|
|
|
GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_pFilterView));
|
|
GtkTreeIter iter;
|
|
GtkTreeModel *model;
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
{
|
|
gchar *title = nullptr;
|
|
gtk_tree_model_get (model, &iter, 2, &title, -1);
|
|
if (title)
|
|
sFilterName = OUString( title, strlen( title ), RTL_TEXTENCODING_UTF8 );
|
|
else
|
|
sFilterName = OUString();
|
|
g_free (title);
|
|
}
|
|
else
|
|
{
|
|
if( aSelectedFiles[nIndex].indexOf('.') > 0 )
|
|
{
|
|
std::u16string_view sExtension;
|
|
nTokenIndex = 0;
|
|
do
|
|
sExtension = o3tl::getToken(aSelectedFiles[nIndex], 0, '.', nTokenIndex );
|
|
while( nTokenIndex >= 0 );
|
|
|
|
if( sExtension.size() >= 3 ) // 3 = typical/minimum extension length
|
|
{
|
|
OUString aNewFilter;
|
|
OUString aOldFilter = getCurrentFilter();
|
|
bool bChangeFilter = true;
|
|
if ( m_pFilterVector)
|
|
for (auto const& filter : *m_pFilterVector)
|
|
{
|
|
if( lcl_matchFilter( filter.getFilter(), Concat2View(OUString::Concat("*.") + sExtension) ) )
|
|
{
|
|
if( aNewFilter.isEmpty() )
|
|
aNewFilter = filter.getTitle();
|
|
|
|
if( aOldFilter == filter.getTitle() )
|
|
bChangeFilter = false;
|
|
|
|
bExtensionTypedIn = true;
|
|
}
|
|
}
|
|
if( bChangeFilter && bExtensionTypedIn )
|
|
{
|
|
gchar* pCurrentName = gtk_file_chooser_get_current_name(GTK_FILE_CHOOSER(m_pDialog));
|
|
setCurrentFilter( aNewFilter );
|
|
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(m_pDialog), pCurrentName);
|
|
g_free(pCurrentName);
|
|
}
|
|
}
|
|
}
|
|
|
|
GtkFileFilter *filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(m_pDialog));
|
|
if (m_pPseudoFilter != filter)
|
|
{
|
|
const gchar* filtername = filter ? gtk_file_filter_get_name(filter) : nullptr;
|
|
if (filtername)
|
|
sFilterName = OUString(filtername, strlen( filtername ), RTL_TEXTENCODING_UTF8);
|
|
else
|
|
sFilterName.clear();
|
|
}
|
|
else
|
|
sFilterName = m_aInitialFilter;
|
|
}
|
|
|
|
if (m_pFilterVector)
|
|
{
|
|
auto aVectorIter = ::std::find_if(
|
|
m_pFilterVector->begin(), m_pFilterVector->end(), FilterTitleMatch(sFilterName) );
|
|
|
|
OUString aFilter;
|
|
if (aVectorIter != m_pFilterVector->end())
|
|
aFilter = aVectorIter->getFilter();
|
|
|
|
nTokenIndex = 0;
|
|
OUString sToken;
|
|
do
|
|
{
|
|
sToken = aFilter.getToken( 0, '.', nTokenIndex );
|
|
|
|
if ( sToken.lastIndexOf( ';' ) != -1 )
|
|
{
|
|
sToken = sToken.getToken(0, ';');
|
|
break;
|
|
}
|
|
}
|
|
while( nTokenIndex >= 0 );
|
|
|
|
if( !bExtensionTypedIn && ( sToken != "*" ) )
|
|
{
|
|
//if the filename does not already have the auto extension, stick it on
|
|
OUString sExtension = "." + sToken;
|
|
OUString &rBase = aSelectedFilesRange[nIndex];
|
|
sal_Int32 nExtensionIdx = rBase.getLength() - sExtension.getLength();
|
|
SAL_INFO(
|
|
"vcl.gtk",
|
|
"idx are " << rBase.lastIndexOf(sExtension) << " "
|
|
<< nExtensionIdx);
|
|
|
|
if( rBase.lastIndexOf( sExtension ) != nExtensionIdx )
|
|
rBase += sExtension;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
nIndex++;
|
|
g_free( pURI );
|
|
}
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
g_slist_free( pPathList );
|
|
#else
|
|
g_object_unref(pPathList);
|
|
#endif
|
|
|
|
return aSelectedFiles;
|
|
}
|
|
|
|
// XExecutableDialog functions
|
|
|
|
void SAL_CALL SalGtkFilePicker::setTitle( const OUString& rTitle )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
implsetTitle(rTitle);
|
|
}
|
|
|
|
sal_Int16 SAL_CALL SalGtkFilePicker::execute()
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
if (!mbInitialized)
|
|
{
|
|
// tdf#144084 if not initialized default to FILEOPEN_SIMPLE
|
|
impl_initialize(nullptr, FILEOPEN_SIMPLE);
|
|
assert(mbInitialized);
|
|
}
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
sal_Int16 retVal = 0;
|
|
|
|
SetFilters();
|
|
|
|
// tdf#84431 - set the filter after the corresponding widget is created
|
|
if ( !m_aCurrentFilter.isEmpty() )
|
|
SetCurFilter(m_aCurrentFilter);
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
mnHID_FolderChange =
|
|
g_signal_connect( GTK_FILE_CHOOSER( m_pDialog ), "current-folder-changed",
|
|
G_CALLBACK( folder_changed_cb ), static_cast<gpointer>(this) );
|
|
#else
|
|
// no replacement in 4-0 that I can see :-(
|
|
mnHID_FolderChange = 0;
|
|
#endif
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
mnHID_SelectionChange =
|
|
g_signal_connect( GTK_FILE_CHOOSER( m_pDialog ), "selection-changed",
|
|
G_CALLBACK( selection_changed_cb ), static_cast<gpointer>(this) );
|
|
#else
|
|
// no replacement in 4-0 that I can see :-(
|
|
mnHID_SelectionChange = 0;
|
|
#endif
|
|
|
|
int btn = GTK_RESPONSE_NO;
|
|
|
|
uno::Reference< awt::XExtendedToolkit > xToolkit(
|
|
awt::Toolkit::create(m_xContext),
|
|
UNO_QUERY_THROW );
|
|
|
|
uno::Reference< frame::XDesktop > xDesktop(
|
|
frame::Desktop::create(m_xContext),
|
|
UNO_QUERY_THROW );
|
|
|
|
GtkWindow *pParent = GTK_WINDOW(m_pParentWidget);
|
|
if (!pParent)
|
|
{
|
|
SAL_WARN( "vcl.gtk", "no parent widget set");
|
|
pParent = RunDialog::GetTransientFor();
|
|
}
|
|
if (pParent)
|
|
gtk_window_set_transient_for(GTK_WINDOW(m_pDialog), pParent);
|
|
rtl::Reference<RunDialog> pRunDialog = new RunDialog(m_pDialog, xToolkit, xDesktop);
|
|
while( GTK_RESPONSE_NO == btn )
|
|
{
|
|
btn = GTK_RESPONSE_YES; // we don't want to repeat unless user clicks NO for file save.
|
|
|
|
gint nStatus = pRunDialog->run();
|
|
switch( nStatus )
|
|
{
|
|
case GTK_RESPONSE_ACCEPT:
|
|
if( GTK_FILE_CHOOSER_ACTION_SAVE == gtk_file_chooser_get_action( GTK_FILE_CHOOSER( m_pDialog ) ) )
|
|
{
|
|
Sequence < OUString > aPathSeq = getFiles();
|
|
if( aPathSeq.getLength() == 1 )
|
|
{
|
|
const OUString& sFileName = aPathSeq[0];
|
|
if (::utl::UCBContentHelper::Exists(sFileName))
|
|
{
|
|
INetURLObject aFileObj(sFileName);
|
|
|
|
OString baseName(
|
|
OUStringToOString(
|
|
aFileObj.getName(
|
|
INetURLObject::LAST_SEGMENT,
|
|
true,
|
|
INetURLObject::DecodeMechanism::WithCharset
|
|
),
|
|
RTL_TEXTENCODING_UTF8
|
|
)
|
|
);
|
|
OString aMsg(
|
|
OUStringToOString(
|
|
getResString( FILE_PICKER_OVERWRITE_PRIMARY ),
|
|
RTL_TEXTENCODING_UTF8
|
|
)
|
|
);
|
|
OString toReplace("$filename$"_ostr);
|
|
|
|
aMsg = aMsg.replaceAt(
|
|
aMsg.indexOf( toReplace ),
|
|
toReplace.getLength(),
|
|
baseName
|
|
);
|
|
|
|
GtkWidget *dlg = gtk_message_dialog_new( nullptr,
|
|
GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_QUESTION,
|
|
GTK_BUTTONS_YES_NO,
|
|
"%s",
|
|
aMsg.getStr()
|
|
);
|
|
|
|
GtkWidget* pOkButton = gtk_dialog_get_widget_for_response(GTK_DIALOG(dlg), GTK_RESPONSE_YES);
|
|
GtkStyleContext* pStyleContext = gtk_widget_get_style_context(pOkButton);
|
|
gtk_style_context_add_class(pStyleContext, "destructive-action");
|
|
|
|
sal_Int32 nSegmentCount = aFileObj.getSegmentCount();
|
|
if (nSegmentCount >= 2)
|
|
{
|
|
OString dirName(
|
|
OUStringToOString(
|
|
aFileObj.getName(
|
|
nSegmentCount-2,
|
|
true,
|
|
INetURLObject::DecodeMechanism::WithCharset
|
|
),
|
|
RTL_TEXTENCODING_UTF8
|
|
)
|
|
);
|
|
|
|
aMsg =
|
|
OUStringToOString(
|
|
getResString( FILE_PICKER_OVERWRITE_SECONDARY ),
|
|
RTL_TEXTENCODING_UTF8
|
|
);
|
|
|
|
toReplace = "$dirname$"_ostr;
|
|
|
|
aMsg = aMsg.replaceAt(
|
|
aMsg.indexOf( toReplace ),
|
|
toReplace.getLength(),
|
|
dirName
|
|
);
|
|
|
|
gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( dlg ), "%s", aMsg.getStr() );
|
|
}
|
|
|
|
gtk_window_set_title( GTK_WINDOW( dlg ),
|
|
OUStringToOString(getResString(FILE_PICKER_TITLE_SAVE ),
|
|
RTL_TEXTENCODING_UTF8 ).getStr() );
|
|
gtk_window_set_transient_for(GTK_WINDOW(dlg), GTK_WINDOW(m_pDialog));
|
|
rtl::Reference<RunDialog> pAnotherDialog = new RunDialog(dlg, xToolkit, xDesktop);
|
|
btn = pAnotherDialog->run();
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_widget_destroy(dlg);
|
|
#else
|
|
gtk_window_destroy(GTK_WINDOW(dlg));
|
|
#endif
|
|
}
|
|
|
|
if( btn == GTK_RESPONSE_YES )
|
|
retVal = ExecutableDialogResults::OK;
|
|
}
|
|
}
|
|
else
|
|
retVal = ExecutableDialogResults::OK;
|
|
break;
|
|
|
|
case GTK_RESPONSE_CANCEL:
|
|
retVal = ExecutableDialogResults::CANCEL;
|
|
break;
|
|
|
|
case 1: //PLAY
|
|
{
|
|
FilePickerEvent evt;
|
|
evt.ElementId = PUSHBUTTON_PLAY;
|
|
impl_controlStateChanged( evt );
|
|
btn = GTK_RESPONSE_NO;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
retVal = 0;
|
|
break;
|
|
}
|
|
}
|
|
gtk_widget_hide(m_pDialog);
|
|
|
|
if (mnHID_FolderChange)
|
|
g_signal_handler_disconnect(GTK_FILE_CHOOSER( m_pDialog ), mnHID_FolderChange);
|
|
if (mnHID_SelectionChange)
|
|
g_signal_handler_disconnect(GTK_FILE_CHOOSER( m_pDialog ), mnHID_SelectionChange);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
// cf. offapi/com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.idl
|
|
GtkWidget *SalGtkFilePicker::getWidget( sal_Int16 nControlId, GType *pType )
|
|
{
|
|
GType tType = GTK_TYPE_CHECK_BUTTON; //prevent warning by initializing
|
|
GtkWidget *pWidget = nullptr;
|
|
|
|
#define MAP_TOGGLE( elem ) \
|
|
case ExtendedFilePickerElementIds::CHECKBOX_##elem: \
|
|
pWidget = m_pToggles[elem]; tType = GTK_TYPE_CHECK_BUTTON; \
|
|
break
|
|
#define MAP_BUTTON( elem ) \
|
|
case CommonFilePickerElementIds::PUSHBUTTON_##elem: \
|
|
pWidget = m_pButtons[elem]; tType = GTK_TYPE_BUTTON; \
|
|
break
|
|
#define MAP_EXT_BUTTON( elem ) \
|
|
case ExtendedFilePickerElementIds::PUSHBUTTON_##elem: \
|
|
pWidget = m_pButtons[elem]; tType = GTK_TYPE_BUTTON; \
|
|
break
|
|
#define MAP_LIST( elem ) \
|
|
case ExtendedFilePickerElementIds::LISTBOX_##elem: \
|
|
pWidget = m_pLists[elem]; tType = GTK_TYPE_COMBO_BOX; \
|
|
break
|
|
#define MAP_LIST_LABEL( elem ) \
|
|
case ExtendedFilePickerElementIds::LISTBOX_##elem##_LABEL: \
|
|
pWidget = m_pListLabels[elem]; tType = GTK_TYPE_LABEL; \
|
|
break
|
|
|
|
switch( nControlId )
|
|
{
|
|
MAP_TOGGLE( AUTOEXTENSION );
|
|
MAP_TOGGLE( PASSWORD );
|
|
MAP_TOGGLE( GPGENCRYPTION );
|
|
MAP_TOGGLE( GPGSIGN );
|
|
MAP_TOGGLE( FILTEROPTIONS );
|
|
MAP_TOGGLE( READONLY );
|
|
MAP_TOGGLE( LINK );
|
|
MAP_TOGGLE( PREVIEW );
|
|
MAP_TOGGLE( SELECTION );
|
|
MAP_BUTTON( OK );
|
|
MAP_BUTTON( CANCEL );
|
|
MAP_EXT_BUTTON( PLAY );
|
|
MAP_LIST( VERSION );
|
|
MAP_LIST( TEMPLATE );
|
|
MAP_LIST( IMAGE_TEMPLATE );
|
|
MAP_LIST( IMAGE_ANCHOR );
|
|
MAP_LIST_LABEL( VERSION );
|
|
MAP_LIST_LABEL( TEMPLATE );
|
|
MAP_LIST_LABEL( IMAGE_TEMPLATE );
|
|
MAP_LIST_LABEL( IMAGE_ANCHOR );
|
|
case CommonFilePickerElementIds::LISTBOX_FILTER_LABEL:
|
|
// the filter list in gtk typically is not labeled, but has a built-in
|
|
// tooltip to indicate what it does
|
|
break;
|
|
default:
|
|
SAL_WARN( "vcl.gtk", "Handle unknown control " << nControlId);
|
|
break;
|
|
}
|
|
#undef MAP
|
|
|
|
if( pType )
|
|
*pType = tType;
|
|
return pWidget;
|
|
}
|
|
|
|
// XFilePickerControlAccess functions
|
|
|
|
static void HackWidthToFirst(GtkComboBox *pWidget)
|
|
{
|
|
GtkRequisition requisition;
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_widget_size_request(GTK_WIDGET(pWidget), &requisition);
|
|
#else
|
|
gtk_widget_get_preferred_size(GTK_WIDGET(pWidget), &requisition, nullptr);
|
|
#endif
|
|
gtk_widget_set_size_request(GTK_WIDGET(pWidget), requisition.width, -1);
|
|
}
|
|
|
|
static void ComboBoxAppendText(GtkComboBox *pCombo, std::u16string_view rStr)
|
|
{
|
|
GtkTreeIter aIter;
|
|
GtkListStore *pStore = GTK_LIST_STORE(gtk_combo_box_get_model(pCombo));
|
|
OString aStr = OUStringToOString(rStr, RTL_TEXTENCODING_UTF8);
|
|
gtk_list_store_append(pStore, &aIter);
|
|
gtk_list_store_set(pStore, &aIter, 0, aStr.getStr(), -1);
|
|
}
|
|
|
|
void SalGtkFilePicker::HandleSetListValue(GtkComboBox *pWidget, sal_Int16 nControlAction, const uno::Any& rValue)
|
|
{
|
|
switch (nControlAction)
|
|
{
|
|
case ControlActions::ADD_ITEM:
|
|
{
|
|
OUString sItem;
|
|
rValue >>= sItem;
|
|
ComboBoxAppendText(pWidget, sItem);
|
|
if (!bVersionWidthUnset)
|
|
{
|
|
HackWidthToFirst(pWidget);
|
|
bVersionWidthUnset = true;
|
|
}
|
|
}
|
|
break;
|
|
case ControlActions::ADD_ITEMS:
|
|
{
|
|
Sequence< OUString > aStringList;
|
|
rValue >>= aStringList;
|
|
for (const auto& rString : aStringList)
|
|
{
|
|
ComboBoxAppendText(pWidget, rString);
|
|
if (!bVersionWidthUnset)
|
|
{
|
|
HackWidthToFirst(pWidget);
|
|
bVersionWidthUnset = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ControlActions::DELETE_ITEM:
|
|
{
|
|
sal_Int32 nPos=0;
|
|
rValue >>= nPos;
|
|
|
|
GtkTreeIter aIter;
|
|
GtkListStore *pStore = GTK_LIST_STORE(
|
|
gtk_combo_box_get_model(GTK_COMBO_BOX(pWidget)));
|
|
if(gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pStore), &aIter, nullptr, nPos))
|
|
gtk_list_store_remove(pStore, &aIter);
|
|
}
|
|
break;
|
|
case ControlActions::DELETE_ITEMS:
|
|
{
|
|
gtk_combo_box_set_active(pWidget, -1);
|
|
GtkListStore *pStore = GTK_LIST_STORE(
|
|
gtk_combo_box_get_model(GTK_COMBO_BOX(pWidget)));
|
|
gtk_list_store_clear(pStore);
|
|
}
|
|
break;
|
|
case ControlActions::SET_SELECT_ITEM:
|
|
{
|
|
sal_Int32 nPos=0;
|
|
rValue >>= nPos;
|
|
gtk_combo_box_set_active(pWidget, nPos);
|
|
}
|
|
break;
|
|
default:
|
|
SAL_WARN( "vcl.gtk", "undocumented/unimplemented ControlAction for a list " << nControlAction);
|
|
break;
|
|
}
|
|
|
|
//I think its best to make it insensitive unless there is the chance to
|
|
//actually select something from the list.
|
|
gint nItems = gtk_tree_model_iter_n_children(
|
|
gtk_combo_box_get_model(pWidget), nullptr);
|
|
gtk_widget_set_sensitive(GTK_WIDGET(pWidget), nItems > 1);
|
|
}
|
|
|
|
uno::Any SalGtkFilePicker::HandleGetListValue(GtkComboBox *pWidget, sal_Int16 nControlAction)
|
|
{
|
|
uno::Any aAny;
|
|
switch (nControlAction)
|
|
{
|
|
case ControlActions::GET_ITEMS:
|
|
{
|
|
Sequence< OUString > aItemList;
|
|
|
|
GtkTreeModel *pTree = gtk_combo_box_get_model(pWidget);
|
|
GtkTreeIter iter;
|
|
if (gtk_tree_model_get_iter_first(pTree, &iter))
|
|
{
|
|
sal_Int32 nSize = gtk_tree_model_iter_n_children(
|
|
pTree, nullptr);
|
|
|
|
aItemList.realloc(nSize);
|
|
auto pItemList = aItemList.getArray();
|
|
for (sal_Int32 i=0; i < nSize; ++i)
|
|
{
|
|
gchar *item;
|
|
gtk_tree_model_get(gtk_combo_box_get_model(pWidget),
|
|
&iter, 0, &item, -1);
|
|
pItemList[i] = OUString(item, strlen(item), RTL_TEXTENCODING_UTF8);
|
|
g_free(item);
|
|
(void)gtk_tree_model_iter_next(pTree, &iter);
|
|
}
|
|
}
|
|
aAny <<= aItemList;
|
|
}
|
|
break;
|
|
case ControlActions::GET_SELECTED_ITEM:
|
|
{
|
|
GtkTreeIter iter;
|
|
if (gtk_combo_box_get_active_iter(pWidget, &iter))
|
|
{
|
|
gchar *item;
|
|
gtk_tree_model_get(gtk_combo_box_get_model(pWidget),
|
|
&iter, 0, &item, -1);
|
|
OUString sItem(item, strlen(item), RTL_TEXTENCODING_UTF8);
|
|
aAny <<= sItem;
|
|
g_free(item);
|
|
}
|
|
}
|
|
break;
|
|
case ControlActions::GET_SELECTED_ITEM_INDEX:
|
|
{
|
|
gint nActive = gtk_combo_box_get_active(pWidget);
|
|
aAny <<= static_cast< sal_Int32 >(nActive);
|
|
}
|
|
break;
|
|
default:
|
|
SAL_WARN( "vcl.gtk", "undocumented/unimplemented ControlAction for a list " << nControlAction);
|
|
break;
|
|
}
|
|
return aAny;
|
|
}
|
|
|
|
void SAL_CALL SalGtkFilePicker::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
GType tType;
|
|
GtkWidget *pWidget;
|
|
|
|
if( !( pWidget = getWidget( nControlId, &tType ) ) )
|
|
SAL_WARN( "vcl.gtk", "enable unknown control " << nControlId);
|
|
else if( tType == GTK_TYPE_CHECK_BUTTON)
|
|
{
|
|
bool bChecked = false;
|
|
rValue >>= bChecked;
|
|
#if GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_check_button_set_active(GTK_CHECK_BUTTON(pWidget), bChecked);
|
|
#else
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pWidget), bChecked);
|
|
#endif
|
|
}
|
|
else if( tType == GTK_TYPE_COMBO_BOX )
|
|
HandleSetListValue(GTK_COMBO_BOX(pWidget), nControlAction, rValue);
|
|
else
|
|
{
|
|
SAL_WARN( "vcl.gtk", "Can't set value on button / list " << nControlId << " " << nControlAction );
|
|
}
|
|
}
|
|
|
|
uno::Any SAL_CALL SalGtkFilePicker::getValue( sal_Int16 nControlId, sal_Int16 nControlAction )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
uno::Any aRetval;
|
|
|
|
GType tType;
|
|
GtkWidget *pWidget;
|
|
|
|
if( !( pWidget = getWidget( nControlId, &tType ) ) )
|
|
SAL_WARN( "vcl.gtk", "enable unknown control " << nControlId);
|
|
else if( tType == GTK_TYPE_CHECK_BUTTON)
|
|
{
|
|
#if GTK_CHECK_VERSION(4, 0, 0)
|
|
aRetval <<= bool(gtk_check_button_get_active(GTK_CHECK_BUTTON(pWidget)));
|
|
#else
|
|
aRetval <<= bool(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pWidget)));
|
|
#endif
|
|
}
|
|
else if( tType == GTK_TYPE_COMBO_BOX )
|
|
aRetval = HandleGetListValue(GTK_COMBO_BOX(pWidget), nControlAction);
|
|
else
|
|
SAL_WARN( "vcl.gtk", "Can't get value on button / list " << nControlId << " " << nControlAction );
|
|
|
|
return aRetval;
|
|
}
|
|
|
|
void SAL_CALL SalGtkFilePicker::enableControl( sal_Int16 nControlId, sal_Bool bEnable )
|
|
{
|
|
// skip this built-in one which is Enabled by default
|
|
if (nControlId == ExtendedFilePickerElementIds::LISTBOX_FILTER_SELECTOR && bEnable)
|
|
return;
|
|
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
GtkWidget *pWidget;
|
|
|
|
if ( ( pWidget = getWidget( nControlId ) ) )
|
|
{
|
|
if( bEnable )
|
|
{
|
|
gtk_widget_set_sensitive( pWidget, true );
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_set_sensitive( pWidget, false );
|
|
}
|
|
}
|
|
else
|
|
SAL_WARN( "vcl.gtk", "enable unknown control " << nControlId );
|
|
}
|
|
|
|
void SAL_CALL SalGtkFilePicker::setLabel( sal_Int16 nControlId, const OUString& rLabel )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
GType tType;
|
|
GtkWidget *pWidget;
|
|
|
|
if( !( pWidget = getWidget( nControlId, &tType ) ) )
|
|
{
|
|
SAL_WARN_IF(nControlId != CommonFilePickerElementIds::LISTBOX_FILTER_LABEL,
|
|
"vcl.gtk", "Set label '" << rLabel << "' on unknown control " << nControlId);
|
|
return;
|
|
}
|
|
|
|
OString aTxt = OUStringToOString( rLabel.replace('~', '_'), RTL_TEXTENCODING_UTF8 );
|
|
if( tType == GTK_TYPE_CHECK_BUTTON || tType == GTK_TYPE_BUTTON || tType == GTK_TYPE_LABEL )
|
|
g_object_set( pWidget, "label", aTxt.getStr(),
|
|
"use_underline", true, nullptr );
|
|
else
|
|
SAL_WARN( "vcl.gtk", "Can't set label on list");
|
|
}
|
|
|
|
OUString SAL_CALL SalGtkFilePicker::getLabel( sal_Int16 nControlId )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
GType tType;
|
|
OString aTxt;
|
|
GtkWidget *pWidget;
|
|
|
|
if( !( pWidget = getWidget( nControlId, &tType ) ) )
|
|
SAL_WARN( "vcl.gtk", "Get label on unknown control " << nControlId);
|
|
else if( tType == GTK_TYPE_CHECK_BUTTON || tType == GTK_TYPE_BUTTON || tType == GTK_TYPE_LABEL )
|
|
aTxt = gtk_button_get_label( GTK_BUTTON( pWidget ) );
|
|
else
|
|
SAL_WARN( "vcl.gtk", "Can't get label on list");
|
|
|
|
return OStringToOUString( aTxt, RTL_TEXTENCODING_UTF8 );
|
|
}
|
|
|
|
// XFilePreview functions
|
|
|
|
uno::Sequence<sal_Int16> SAL_CALL SalGtkFilePicker::getSupportedImageFormats()
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
// TODO return m_pImpl->getSupportedImageFormats();
|
|
return uno::Sequence<sal_Int16>();
|
|
}
|
|
|
|
sal_Int32 SAL_CALL SalGtkFilePicker::getTargetColorDepth()
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
// TODO return m_pImpl->getTargetColorDepth();
|
|
return 0;
|
|
}
|
|
|
|
sal_Int32 SAL_CALL SalGtkFilePicker::getAvailableWidth()
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
return g_PreviewImageWidth;
|
|
}
|
|
|
|
sal_Int32 SAL_CALL SalGtkFilePicker::getAvailableHeight()
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
return g_PreviewImageHeight;
|
|
}
|
|
|
|
void SAL_CALL SalGtkFilePicker::setImage( sal_Int16 /*aImageFormat*/, const uno::Any& /*aImage*/ )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
// TODO m_pImpl->setImage( aImageFormat, aImage );
|
|
}
|
|
|
|
void SalGtkFilePicker::implChangeType( GtkTreeSelection *selection )
|
|
{
|
|
OUString aLabel = getResString( FILE_PICKER_FILE_TYPE );
|
|
|
|
GtkTreeIter iter;
|
|
GtkTreeModel *model;
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
{
|
|
gchar *title;
|
|
gtk_tree_model_get (model, &iter, 2, &title, -1);
|
|
aLabel += ": " +
|
|
OUString( title, strlen(title), RTL_TEXTENCODING_UTF8 );
|
|
g_free (title);
|
|
}
|
|
gtk_expander_set_label (GTK_EXPANDER (m_pFilterExpander),
|
|
OUStringToOString( aLabel, RTL_TEXTENCODING_UTF8 ).getStr());
|
|
FilePickerEvent evt;
|
|
evt.ElementId = LISTBOX_FILTER;
|
|
impl_controlStateChanged( evt );
|
|
}
|
|
|
|
void SalGtkFilePicker::type_changed_cb( GtkTreeSelection *selection, SalGtkFilePicker *pobjFP )
|
|
{
|
|
pobjFP->implChangeType(selection);
|
|
}
|
|
|
|
void SalGtkFilePicker::unselect_type()
|
|
{
|
|
gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(m_pFilterView)));
|
|
}
|
|
|
|
void SalGtkFilePicker::expander_changed_cb( GtkExpander *expander, SalGtkFilePicker *pobjFP )
|
|
{
|
|
if (gtk_expander_get_expanded(expander))
|
|
pobjFP->unselect_type();
|
|
}
|
|
|
|
void SalGtkFilePicker::filter_changed_cb( GtkFileChooser *, GParamSpec *,
|
|
SalGtkFilePicker *pobjFP )
|
|
{
|
|
FilePickerEvent evt;
|
|
evt.ElementId = LISTBOX_FILTER;
|
|
SAL_INFO( "vcl.gtk", "filter_changed, isn't it great " << pobjFP );
|
|
pobjFP->impl_controlStateChanged( evt );
|
|
}
|
|
|
|
void SalGtkFilePicker::folder_changed_cb( GtkFileChooser *, SalGtkFilePicker *pobjFP )
|
|
{
|
|
FilePickerEvent evt;
|
|
SAL_INFO( "vcl.gtk", "folder_changed, isn't it great " << pobjFP );
|
|
pobjFP->impl_directoryChanged( evt );
|
|
}
|
|
|
|
void SalGtkFilePicker::selection_changed_cb( GtkFileChooser *, SalGtkFilePicker *pobjFP )
|
|
{
|
|
FilePickerEvent evt;
|
|
SAL_INFO( "vcl.gtk", "selection_changed, isn't it great " << pobjFP );
|
|
pobjFP->impl_fileSelectionChanged( evt );
|
|
}
|
|
|
|
void SalGtkFilePicker::update_preview_cb( GtkFileChooser *file_chooser, SalGtkFilePicker* pobjFP )
|
|
{
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
bool have_preview = false;
|
|
|
|
GtkWidget* preview = pobjFP->m_pPreview;
|
|
char* filename = gtk_file_chooser_get_preview_filename( file_chooser );
|
|
|
|
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pobjFP->m_pToggles[PREVIEW])) && filename && g_file_test(filename, G_FILE_TEST_IS_REGULAR))
|
|
{
|
|
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size(
|
|
filename,
|
|
g_PreviewImageWidth,
|
|
g_PreviewImageHeight, nullptr );
|
|
|
|
have_preview = ( pixbuf != nullptr );
|
|
|
|
gtk_image_set_from_pixbuf( GTK_IMAGE( preview ), pixbuf );
|
|
if( pixbuf )
|
|
g_object_unref( G_OBJECT( pixbuf ) );
|
|
|
|
}
|
|
|
|
gtk_file_chooser_set_preview_widget_active( file_chooser, have_preview );
|
|
|
|
if( filename )
|
|
g_free( filename );
|
|
#else
|
|
(void)file_chooser;
|
|
(void)pobjFP;
|
|
#endif
|
|
}
|
|
|
|
sal_Bool SAL_CALL SalGtkFilePicker::setShowState( sal_Bool bShowState )
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
// TODO return m_pImpl->setShowState( bShowState );
|
|
if( bool(bShowState) != mbPreviewState )
|
|
{
|
|
if( bShowState )
|
|
{
|
|
// Show
|
|
if( !mHID_Preview )
|
|
{
|
|
mHID_Preview = g_signal_connect(
|
|
GTK_FILE_CHOOSER( m_pDialog ), "update-preview",
|
|
G_CALLBACK( update_preview_cb ), static_cast<gpointer>(this) );
|
|
}
|
|
gtk_widget_show( m_pPreview );
|
|
}
|
|
else
|
|
{
|
|
// Hide
|
|
gtk_widget_hide( m_pPreview );
|
|
}
|
|
|
|
// also emit the signal
|
|
g_signal_emit_by_name( G_OBJECT( m_pDialog ), "update-preview" );
|
|
|
|
mbPreviewState = bShowState;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
sal_Bool SAL_CALL SalGtkFilePicker::getShowState()
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
return mbPreviewState;
|
|
}
|
|
|
|
GtkWidget* SalGtkPicker::GetParentWidget(const uno::Sequence<uno::Any>& rArguments)
|
|
{
|
|
GtkWidget* pParentWidget = nullptr;
|
|
|
|
css::uno::Reference<css::awt::XWindow> xParentWindow;
|
|
if (rArguments.getLength() > 1)
|
|
{
|
|
rArguments[1] >>= xParentWindow;
|
|
}
|
|
|
|
if (xParentWindow.is())
|
|
{
|
|
if (SalGtkXWindow* pGtkXWindow = dynamic_cast<SalGtkXWindow*>(xParentWindow.get()))
|
|
pParentWidget = pGtkXWindow->getGtkWidget();
|
|
else
|
|
{
|
|
css::uno::Reference<css::awt::XSystemDependentWindowPeer> xSysDepWin(xParentWindow, css::uno::UNO_QUERY);
|
|
if (xSysDepWin.is())
|
|
{
|
|
css::uno::Sequence<sal_Int8> aProcessIdent(16);
|
|
rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8*>(aProcessIdent.getArray()));
|
|
uno::Any aAny = xSysDepWin->getWindowHandle(aProcessIdent, css::lang::SystemDependent::SYSTEM_XWINDOW);
|
|
css::awt::SystemDependentXWindow tmp;
|
|
aAny >>= tmp;
|
|
pParentWidget = GetGtkSalData()->GetGtkDisplay()->findGtkWidgetForNativeHandle(tmp.WindowHandle);
|
|
}
|
|
}
|
|
}
|
|
|
|
return pParentWidget;
|
|
}
|
|
|
|
// XInitialization
|
|
|
|
void SAL_CALL SalGtkFilePicker::initialize( const uno::Sequence<uno::Any>& aArguments )
|
|
{
|
|
// parameter checking
|
|
uno::Any aAny;
|
|
if( !aArguments.hasElements() )
|
|
throw lang::IllegalArgumentException(
|
|
u"no arguments"_ustr,
|
|
static_cast<XFilePicker2*>( this ), 1 );
|
|
|
|
aAny = aArguments[0];
|
|
|
|
if( ( aAny.getValueType() != cppu::UnoType<sal_Int16>::get()) &&
|
|
(aAny.getValueType() != cppu::UnoType<sal_Int8>::get()) )
|
|
throw lang::IllegalArgumentException(
|
|
u"invalid argument type"_ustr,
|
|
static_cast<XFilePicker2*>( this ), 1 );
|
|
|
|
sal_Int16 templateId = -1;
|
|
aAny >>= templateId;
|
|
|
|
impl_initialize(GetParentWidget(aArguments), templateId);
|
|
}
|
|
|
|
void SalGtkFilePicker::impl_initialize(GtkWidget* pParentWidget, sal_Int16 templateId)
|
|
{
|
|
m_pParentWidget = pParentWidget;
|
|
|
|
GtkFileChooserAction eAction = GTK_FILE_CHOOSER_ACTION_OPEN;
|
|
OString sOpen = getOpenText();
|
|
OString sSave = getSaveText();
|
|
const gchar *first_button_text;
|
|
|
|
SolarMutexGuard g;
|
|
|
|
// TODO: extract full semantic from
|
|
// svtools/source/filepicker/filepicker.cxx (getWinBits)
|
|
switch( templateId )
|
|
{
|
|
case FILEOPEN_SIMPLE:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_OPEN;
|
|
first_button_text = sOpen.getStr();
|
|
break;
|
|
case FILESAVE_SIMPLE:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_SAVE;
|
|
first_button_text = sSave.getStr();
|
|
break;
|
|
case FILESAVE_AUTOEXTENSION_PASSWORD:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_SAVE;
|
|
first_button_text = sSave.getStr();
|
|
mbToggleVisibility[PASSWORD] = true;
|
|
mbToggleVisibility[GPGENCRYPTION] = true;
|
|
#if HAVE_FEATURE_GPGME
|
|
mbToggleVisibility[GPGSIGN] = true;
|
|
#endif
|
|
// TODO
|
|
break;
|
|
case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_SAVE;
|
|
first_button_text = sSave.getStr();
|
|
mbToggleVisibility[PASSWORD] = true;
|
|
mbToggleVisibility[GPGENCRYPTION] = true;
|
|
#if HAVE_FEATURE_GPGME
|
|
mbToggleVisibility[GPGSIGN] = true;
|
|
#endif
|
|
mbToggleVisibility[FILTEROPTIONS] = true;
|
|
// TODO
|
|
break;
|
|
case FILESAVE_AUTOEXTENSION_SELECTION:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_SAVE; // SELECT_FOLDER ?
|
|
first_button_text = sSave.getStr();
|
|
mbToggleVisibility[SELECTION] = true;
|
|
// TODO
|
|
break;
|
|
case FILESAVE_AUTOEXTENSION_TEMPLATE:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_SAVE;
|
|
first_button_text = sSave.getStr();
|
|
mbListVisibility[TEMPLATE] = true;
|
|
// TODO
|
|
break;
|
|
case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_OPEN;
|
|
first_button_text = sOpen.getStr();
|
|
mbToggleVisibility[LINK] = true;
|
|
mbToggleVisibility[PREVIEW] = true;
|
|
mbListVisibility[IMAGE_TEMPLATE] = true;
|
|
// TODO
|
|
break;
|
|
case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_OPEN;
|
|
first_button_text = sOpen.getStr();
|
|
mbToggleVisibility[LINK] = true;
|
|
mbToggleVisibility[PREVIEW] = true;
|
|
mbListVisibility[IMAGE_ANCHOR] = true;
|
|
// TODO
|
|
break;
|
|
case FILEOPEN_PLAY:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_OPEN;
|
|
first_button_text = sOpen.getStr();
|
|
mbButtonVisibility[PLAY] = true;
|
|
// TODO
|
|
break;
|
|
case FILEOPEN_LINK_PLAY:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_OPEN;
|
|
first_button_text = sOpen.getStr();
|
|
mbToggleVisibility[LINK] = true;
|
|
mbButtonVisibility[PLAY] = true;
|
|
// TODO
|
|
break;
|
|
case FILEOPEN_READONLY_VERSION:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_OPEN;
|
|
first_button_text = sOpen.getStr();
|
|
mbToggleVisibility[READONLY] = true;
|
|
mbListVisibility[VERSION] = true;
|
|
break;
|
|
case FILEOPEN_LINK_PREVIEW:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_OPEN;
|
|
first_button_text = sOpen.getStr();
|
|
mbToggleVisibility[LINK] = true;
|
|
mbToggleVisibility[PREVIEW] = true;
|
|
// TODO
|
|
break;
|
|
case FILESAVE_AUTOEXTENSION:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_SAVE;
|
|
first_button_text = sSave.getStr();
|
|
// TODO
|
|
break;
|
|
case FILEOPEN_PREVIEW:
|
|
eAction = GTK_FILE_CHOOSER_ACTION_OPEN;
|
|
first_button_text = sOpen.getStr();
|
|
mbToggleVisibility[PREVIEW] = true;
|
|
// TODO
|
|
break;
|
|
default:
|
|
throw lang::IllegalArgumentException(
|
|
u"Unknown template"_ustr,
|
|
static_cast< XFilePicker2* >( this ),
|
|
1 );
|
|
}
|
|
|
|
if( GTK_FILE_CHOOSER_ACTION_SAVE == eAction )
|
|
{
|
|
OUString aFilePickerTitle(getResString( FILE_PICKER_TITLE_SAVE ));
|
|
gtk_window_set_title ( GTK_WINDOW( m_pDialog ),
|
|
OUStringToOString( aFilePickerTitle, RTL_TEXTENCODING_UTF8 ).getStr() );
|
|
}
|
|
|
|
gtk_file_chooser_set_action( GTK_FILE_CHOOSER( m_pDialog ), eAction);
|
|
m_pButtons[CANCEL] = gtk_dialog_add_button(GTK_DIALOG(m_pDialog), getCancelText().getStr(), GTK_RESPONSE_CANCEL);
|
|
mbButtonVisibility[CANCEL] = true;
|
|
|
|
if (mbButtonVisibility[PLAY])
|
|
{
|
|
OString aPlay = OUStringToOString(getResString(PUSHBUTTON_PLAY), RTL_TEXTENCODING_UTF8);
|
|
m_pButtons[PLAY] = gtk_dialog_add_button(GTK_DIALOG(m_pDialog), aPlay.getStr(), 1);
|
|
}
|
|
|
|
m_pButtons[OK] = gtk_dialog_add_button(GTK_DIALOG(m_pDialog), first_button_text, GTK_RESPONSE_ACCEPT);
|
|
mbButtonVisibility[OK] = true;
|
|
|
|
gtk_dialog_set_default_response( GTK_DIALOG (m_pDialog), GTK_RESPONSE_ACCEPT );
|
|
|
|
// Setup special flags
|
|
for( int nTVIndex = 0; nTVIndex < TOGGLE_LAST; nTVIndex++ )
|
|
{
|
|
if( mbToggleVisibility[nTVIndex] )
|
|
gtk_widget_show( m_pToggles[ nTVIndex ] );
|
|
}
|
|
|
|
for( int nTVIndex = 0; nTVIndex < LIST_LAST; nTVIndex++ )
|
|
{
|
|
if( mbListVisibility[nTVIndex] )
|
|
{
|
|
gtk_widget_set_sensitive( m_pLists[ nTVIndex ], false );
|
|
gtk_widget_show( m_pLists[ nTVIndex ] );
|
|
gtk_widget_show( m_pListLabels[ nTVIndex ] );
|
|
gtk_widget_show( m_pHBoxs[ nTVIndex ] );
|
|
}
|
|
}
|
|
|
|
mbInitialized = true;
|
|
}
|
|
|
|
void SalGtkFilePicker::preview_toggled_cb( GObject *cb, SalGtkFilePicker* pobjFP )
|
|
{
|
|
if( pobjFP->mbToggleVisibility[PREVIEW] )
|
|
pobjFP->setShowState( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( cb ) ) );
|
|
}
|
|
|
|
// XCancellable
|
|
|
|
void SAL_CALL SalGtkFilePicker::cancel()
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
OSL_ASSERT( m_pDialog != nullptr );
|
|
|
|
// TODO m_pImpl->cancel();
|
|
}
|
|
|
|
// Misc
|
|
|
|
void SalGtkFilePicker::SetCurFilter( const OUString& rFilter )
|
|
{
|
|
// Get all the filters already added
|
|
#if GTK_CHECK_VERSION(4, 0, 0)
|
|
GListModel *filters = gtk_file_chooser_get_filters(GTK_FILE_CHOOSER(m_pDialog));
|
|
#else
|
|
GSList *filters = gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(m_pDialog));
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(4, 0, 0)
|
|
int nIndex = 0;
|
|
while (gpointer pElem = g_list_model_get_item(filters, nIndex))
|
|
{
|
|
GtkFileFilter* pFilter = static_cast<GtkFileFilter*>(pElem);
|
|
++nIndex;
|
|
#else
|
|
for( GSList *iter = filters; iter; iter = iter->next )
|
|
{
|
|
GtkFileFilter* pFilter = static_cast<GtkFileFilter*>( iter->data );
|
|
#endif
|
|
const gchar * filtername = gtk_file_filter_get_name( pFilter );
|
|
OUString sFilterName( filtername, strlen( filtername ), RTL_TEXTENCODING_UTF8 );
|
|
|
|
OUString aShrunkName = shrinkFilterName( rFilter );
|
|
if( aShrunkName == sFilterName )
|
|
{
|
|
SAL_INFO( "vcl.gtk", "actually setting " << filtername );
|
|
gtk_file_chooser_set_filter( GTK_FILE_CHOOSER( m_pDialog ), pFilter );
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
g_slist_free( filters );
|
|
#else
|
|
g_object_unref (filters);
|
|
#endif
|
|
}
|
|
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
extern "C"
|
|
{
|
|
static gboolean
|
|
case_insensitive_filter (const GtkFileFilterInfo *filter_info, gpointer data)
|
|
{
|
|
bool bRetval = false;
|
|
const char *pFilter = static_cast<const char *>(data);
|
|
|
|
g_return_val_if_fail( data != nullptr, false );
|
|
g_return_val_if_fail( filter_info != nullptr, false );
|
|
|
|
if( !filter_info->uri )
|
|
return false;
|
|
|
|
const char *pExtn = strrchr( filter_info->uri, '.' );
|
|
if( !pExtn )
|
|
return false;
|
|
pExtn++;
|
|
|
|
if( !g_ascii_strcasecmp( pFilter, pExtn ) )
|
|
bRetval = true;
|
|
|
|
SAL_INFO( "vcl.gtk", "'" << filter_info->uri << "' match extn '" << pExtn << "' vs '" << pFilter << "' yields " << bRetval );
|
|
|
|
return bRetval;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
GtkFileFilter* SalGtkFilePicker::implAddFilter( const OUString& rFilter, const OUString& rType )
|
|
{
|
|
GtkFileFilter *filter = gtk_file_filter_new();
|
|
|
|
OUString aShrunkName = shrinkFilterName( rFilter );
|
|
OString aFilterName = OUStringToOString( aShrunkName, RTL_TEXTENCODING_UTF8 );
|
|
gtk_file_filter_set_name( filter, aFilterName.getStr() );
|
|
|
|
OUStringBuffer aTokens;
|
|
|
|
bool bAllGlob = rType == "*.*" || rType == "*";
|
|
if (bAllGlob)
|
|
gtk_file_filter_add_pattern( filter, "*" );
|
|
else
|
|
{
|
|
sal_Int32 nIndex = 0;
|
|
do
|
|
{
|
|
OUString aToken = rType.getToken( 0, ';', nIndex );
|
|
// Assume all have the "*.<extn>" syntax
|
|
sal_Int32 nStarDot = aToken.lastIndexOf( "*." );
|
|
if (nStarDot >= 0)
|
|
aToken = aToken.copy( nStarDot + 2 );
|
|
if (!aToken.isEmpty())
|
|
{
|
|
if (!aTokens.isEmpty())
|
|
aTokens.append(",");
|
|
aTokens.append(aToken);
|
|
#if GTK_CHECK_VERSION(4, 0, 0)
|
|
gtk_file_filter_add_suffix(filter, aToken.toUtf8().getStr());
|
|
#else
|
|
gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_URI,
|
|
case_insensitive_filter,
|
|
g_strdup( OUStringToOString(aToken, RTL_TEXTENCODING_UTF8).getStr() ),
|
|
g_free );
|
|
#endif
|
|
|
|
SAL_INFO( "vcl.gtk", "fustering with " << aToken );
|
|
}
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
else
|
|
{
|
|
g_warning( "Duff filter token '%s'\n",
|
|
OUStringToOString(
|
|
o3tl::getToken(rType, 0, ';', nIndex ), RTL_TEXTENCODING_UTF8 ).getStr() );
|
|
}
|
|
#endif
|
|
}
|
|
while( nIndex >= 0 );
|
|
}
|
|
|
|
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( m_pDialog ), filter );
|
|
|
|
if (!bAllGlob)
|
|
{
|
|
GtkTreeIter iter;
|
|
gtk_list_store_append (m_pFilterStore, &iter);
|
|
gtk_list_store_set (m_pFilterStore, &iter,
|
|
0, OUStringToOString(shrinkFilterName( rFilter, true ), RTL_TEXTENCODING_UTF8).getStr(),
|
|
1, OUStringToOString(aTokens, RTL_TEXTENCODING_UTF8).getStr(),
|
|
2, aFilterName.getStr(),
|
|
3, OUStringToOString(rType, RTL_TEXTENCODING_UTF8).getStr(),
|
|
-1);
|
|
}
|
|
return filter;
|
|
}
|
|
|
|
void SalGtkFilePicker::implAddFilterGroup( const Sequence< StringPair >& _rFilters )
|
|
{
|
|
// Gtk+ has no filter group concept I think so ...
|
|
// implAddFilter( _rFilter, String() );
|
|
for( const auto& rSubFilter : _rFilters )
|
|
implAddFilter( rSubFilter.First, rSubFilter.Second );
|
|
}
|
|
|
|
void SalGtkFilePicker::SetFilters()
|
|
{
|
|
if (m_aInitialFilter.isEmpty())
|
|
m_aInitialFilter = m_aCurrentFilter;
|
|
|
|
OUString sPseudoFilter;
|
|
if( GTK_FILE_CHOOSER_ACTION_SAVE == gtk_file_chooser_get_action( GTK_FILE_CHOOSER( m_pDialog ) ) )
|
|
{
|
|
std::set<OUString> aAllFormats;
|
|
if( m_pFilterVector )
|
|
{
|
|
for (auto & filter : *m_pFilterVector)
|
|
{
|
|
if( filter.hasSubFilters() )
|
|
{ // it's a filter group
|
|
css::uno::Sequence< css::beans::StringPair > aSubFilters;
|
|
filter.getSubFilters( aSubFilters );
|
|
for (const auto& rSubFilter : aSubFilters)
|
|
aAllFormats.insert(rSubFilter.Second);
|
|
}
|
|
else
|
|
aAllFormats.insert(filter.getFilter());
|
|
}
|
|
}
|
|
if (aAllFormats.size() > 1)
|
|
{
|
|
OUStringBuffer sAllFilter;
|
|
for (auto const& format : aAllFormats)
|
|
{
|
|
if (!sAllFilter.isEmpty())
|
|
sAllFilter.append(";");
|
|
sAllFilter.append(format);
|
|
}
|
|
sPseudoFilter = getResString(FILE_PICKER_ALLFORMATS);
|
|
m_pPseudoFilter = implAddFilter( sPseudoFilter, sAllFilter.makeStringAndClear() );
|
|
}
|
|
}
|
|
|
|
if( m_pFilterVector )
|
|
{
|
|
for (auto & filter : *m_pFilterVector)
|
|
{
|
|
if( filter.hasSubFilters() )
|
|
{ // it's a filter group
|
|
|
|
css::uno::Sequence< css::beans::StringPair > aSubFilters;
|
|
filter.getSubFilters( aSubFilters );
|
|
|
|
implAddFilterGroup( aSubFilters );
|
|
}
|
|
else
|
|
{
|
|
// it's a single filter
|
|
|
|
implAddFilter( filter.getTitle(), filter.getFilter() );
|
|
}
|
|
}
|
|
}
|
|
|
|
// We always hide the expander now and depend on the user using the glob
|
|
// list, or type a filename suffix, to select a filter by inference.
|
|
gtk_widget_hide(m_pFilterExpander);
|
|
|
|
// set the default filter
|
|
if (!sPseudoFilter.isEmpty())
|
|
SetCurFilter( sPseudoFilter );
|
|
else if(!m_aCurrentFilter.isEmpty())
|
|
SetCurFilter( m_aCurrentFilter );
|
|
|
|
SAL_INFO( "vcl.gtk", "end setting filters");
|
|
}
|
|
|
|
SalGtkFilePicker::~SalGtkFilePicker()
|
|
{
|
|
#if !GTK_CHECK_VERSION(4, 0, 0)
|
|
SolarMutexGuard g;
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < TOGGLE_LAST; i++ )
|
|
gtk_widget_destroy( m_pToggles[i] );
|
|
|
|
for( i = 0; i < LIST_LAST; i++ )
|
|
{
|
|
gtk_widget_destroy( m_pListLabels[i] );
|
|
gtk_widget_destroy( m_pLists[i] );
|
|
gtk_widget_destroy( m_pHBoxs[i] );
|
|
}
|
|
|
|
m_pFilterVector.reset();
|
|
|
|
gtk_widget_destroy( m_pVBox );
|
|
#endif
|
|
}
|
|
|
|
uno::Reference< ui::dialogs::XFilePicker2 >
|
|
GtkInstance::createFilePicker( const css::uno::Reference< css::uno::XComponentContext > &xMSF )
|
|
{
|
|
return uno::Reference< ui::dialogs::XFilePicker2 >(
|
|
new SalGtkFilePicker( xMSF ) );
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|