1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #define _LINUX_SOURCE_COMPAT
22 #include <sys/timer.h>
23 #undef _LINUX_SOURCE_COMPAT
26 #include <config_gio.h>
28 #include <com/sun/star/awt/SystemDependentXWindow.hpp>
29 #include <com/sun/star/awt/Toolkit.hpp>
30 #include <com/sun/star/awt/XSystemDependentWindowPeer.hpp>
31 #include <com/sun/star/frame/Desktop.hpp>
32 #include <com/sun/star/lang/IllegalArgumentException.hpp>
33 #include <com/sun/star/lang/SystemDependent.hpp>
34 #include <com/sun/star/uno/XComponentContext.hpp>
35 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
36 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
37 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
38 #include <osl/diagnose.h>
39 #include <osl/process.h>
40 #include <rtl/process.h>
41 #include <sal/log.hxx>
42 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
43 #include <com/sun/star/ui/dialogs/ControlActions.hpp>
44 #include <com/sun/star/uno/Any.hxx>
45 #include <unx/gtk/gtkdata.hxx>
46 #include <unx/gtk/gtkinst.hxx>
48 #include <vcl/svapp.hxx>
50 #include <tools/urlobj.hxx>
56 #include <gtk/fpicker/SalGtkFilePicker.hxx>
58 using namespace ::com::sun::star
;
59 using namespace ::com::sun::star::ui::dialogs
;
60 using namespace ::com::sun::star::ui::dialogs::TemplateDescription
;
61 using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds
;
62 using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds
;
63 using namespace ::com::sun::star::lang
;
64 using namespace ::com::sun::star::beans
;
65 using namespace ::com::sun::star::uno
;
67 void SalGtkFilePicker::dialog_mapped_cb(GtkWidget
*, SalGtkFilePicker
*pobjFP
)
69 pobjFP
->InitialMapping();
72 void SalGtkFilePicker::InitialMapping()
76 gtk_widget_hide( m_pPreview
);
77 gtk_file_chooser_set_preview_widget_active( GTK_FILE_CHOOSER( m_pDialog
), false);
79 gtk_widget_set_size_request (m_pPreview
, -1, -1);
82 SalGtkFilePicker::SalGtkFilePicker( const uno::Reference
< uno::XComponentContext
>& xContext
) :
83 SalGtkPicker( xContext
),
84 SalGtkFilePicker_Base( m_rbHelperMtx
),
85 m_pParentWidget ( nullptr ),
87 mnHID_FolderChange( 0 ),
88 mnHID_SelectionChange( 0 ),
89 bVersionWidthUnset( false ),
90 mbPreviewState( false ),
92 m_pPreview( nullptr ),
93 m_pPseudoFilter( nullptr )
97 for( i
= 0; i
< TOGGLE_LAST
; i
++ )
99 m_pToggles
[i
] = nullptr;
100 mbToggleVisibility
[i
] = false;
103 for( i
= 0; i
< BUTTON_LAST
; i
++ )
105 m_pButtons
[i
] = nullptr;
106 mbButtonVisibility
[i
] = false;
109 for( i
= 0; i
< LIST_LAST
; i
++ )
111 m_pHBoxs
[i
] = nullptr;
112 m_pAligns
[i
] = nullptr;
113 m_pLists
[i
] = nullptr;
114 m_pListLabels
[i
] = nullptr;
115 mbListVisibility
[i
] = false;
118 OUString aFilePickerTitle
= getResString( FILE_PICKER_TITLE_OPEN
);
120 m_pDialog
= gtk_file_chooser_dialog_new(
121 OUStringToOString( aFilePickerTitle
, RTL_TEXTENCODING_UTF8
).getStr(),
123 GTK_FILE_CHOOSER_ACTION_OPEN
,
124 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
125 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
,
128 gtk_dialog_set_default_response( GTK_DIALOG (m_pDialog
), GTK_RESPONSE_ACCEPT
);
131 gtk_file_chooser_set_local_only( GTK_FILE_CHOOSER( m_pDialog
), false );
133 gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( m_pDialog
), false );
135 m_pVBox
= gtk_vbox_new( false, 0 );
137 // We don't want clickable items to have a huge hit-area
138 GtkWidget
*pHBox
= gtk_hbox_new( false, 0 );
139 GtkWidget
*pThinVBox
= gtk_vbox_new( false, 0 );
141 gtk_box_pack_end (GTK_BOX( m_pVBox
), pHBox
, false, false, 0);
142 gtk_box_pack_start (GTK_BOX( pHBox
), pThinVBox
, false, false, 0);
143 gtk_widget_show( pHBox
);
144 gtk_widget_show( pThinVBox
);
148 for( i
= 0; i
< TOGGLE_LAST
; i
++ )
150 m_pToggles
[i
] = gtk_check_button_new();
152 #define LABEL_TOGGLE( elem ) \
154 aLabel = getResString( CHECKBOX_##elem ); \
155 setLabel( CHECKBOX_##elem, aLabel ); \
159 LABEL_TOGGLE( AUTOEXTENSION
);
160 LABEL_TOGGLE( PASSWORD
);
161 LABEL_TOGGLE( GPGENCRYPTION
);
162 LABEL_TOGGLE( FILTEROPTIONS
);
163 LABEL_TOGGLE( READONLY
);
164 LABEL_TOGGLE( LINK
);
165 LABEL_TOGGLE( PREVIEW
);
166 LABEL_TOGGLE( SELECTION
);
168 SAL_WARN( "vcl.gtk", "Handle unknown control " << i
);
172 gtk_box_pack_end( GTK_BOX( pThinVBox
), m_pToggles
[i
], false, false, 0 );
175 for( i
= 0; i
< LIST_LAST
; i
++ )
177 m_pHBoxs
[i
] = gtk_hbox_new( false, 0 );
179 m_pAligns
[i
] = gtk_alignment_new(0, 0, 0, 1);
181 GtkListStore
*pListStores
[ LIST_LAST
];
182 pListStores
[i
] = gtk_list_store_new (1, G_TYPE_STRING
);
183 m_pLists
[i
] = gtk_combo_box_new_with_model(GTK_TREE_MODEL(pListStores
[i
]));
184 g_object_unref (pListStores
[i
]); // owned by the widget.
185 GtkCellRenderer
*pCell
= gtk_cell_renderer_text_new ();
186 gtk_cell_layout_pack_start(
187 GTK_CELL_LAYOUT(m_pLists
[i
]), pCell
, TRUE
);
188 gtk_cell_layout_set_attributes(
189 GTK_CELL_LAYOUT (m_pLists
[i
]), pCell
, "text", 0, nullptr);
191 m_pListLabels
[i
] = gtk_label_new( "" );
193 #define LABEL_LIST( elem ) \
195 aLabel = getResString( LISTBOX_##elem##_LABEL ); \
196 setLabel( LISTBOX_##elem##_LABEL, aLabel ); \
201 LABEL_LIST( VERSION
);
202 LABEL_LIST( TEMPLATE
);
203 LABEL_LIST( IMAGE_TEMPLATE
);
204 LABEL_LIST( IMAGE_ANCHOR
);
206 SAL_WARN( "vcl.gtk", "Handle unknown control " << i
);
210 gtk_container_add( GTK_CONTAINER( m_pAligns
[i
]), m_pLists
[i
] );
211 gtk_box_pack_end( GTK_BOX( m_pHBoxs
[i
] ), m_pAligns
[i
], false, false, 0 );
212 gtk_box_pack_end( GTK_BOX( m_pHBoxs
[i
] ), m_pListLabels
[i
], false, false, 0 );
213 gtk_label_set_mnemonic_widget( GTK_LABEL(m_pListLabels
[i
]), m_pLists
[i
] );
214 gtk_box_set_spacing( GTK_BOX( m_pHBoxs
[i
] ), 12 );
216 gtk_box_pack_end( GTK_BOX( m_pVBox
), m_pHBoxs
[i
], false, false, 0 );
219 aLabel
= getResString( FILE_PICKER_FILE_TYPE
);
220 m_pFilterExpander
= gtk_expander_new_with_mnemonic(
221 OUStringToOString( aLabel
, RTL_TEXTENCODING_UTF8
).getStr());
223 gtk_box_pack_end( GTK_BOX( m_pVBox
), m_pFilterExpander
, false, true, 0 );
225 GtkWidget
*scrolled_window
= gtk_scrolled_window_new (nullptr, nullptr);
226 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window
),
227 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
228 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window
),
230 gtk_container_add (GTK_CONTAINER (m_pFilterExpander
), scrolled_window
);
231 gtk_widget_show (scrolled_window
);
233 m_pFilterStore
= gtk_list_store_new (4, G_TYPE_STRING
, G_TYPE_STRING
,
234 G_TYPE_STRING
, G_TYPE_STRING
);
235 m_pFilterView
= gtk_tree_view_new_with_model (GTK_TREE_MODEL(m_pFilterStore
));
236 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(m_pFilterView
), false);
237 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(m_pFilterView
), true);
239 GtkCellRenderer
*cell
= nullptr;
241 for (i
= 0; i
< 2; ++i
)
243 GtkTreeViewColumn
*column
= gtk_tree_view_column_new ();
244 cell
= gtk_cell_renderer_text_new ();
245 gtk_tree_view_column_set_expand (column
, true);
246 gtk_tree_view_column_pack_start (column
, cell
, false);
247 gtk_tree_view_column_set_attributes (column
, cell
, "text", i
, nullptr);
248 gtk_tree_view_append_column (GTK_TREE_VIEW(m_pFilterView
), column
);
251 gtk_container_add (GTK_CONTAINER (scrolled_window
), m_pFilterView
);
252 gtk_widget_show (m_pFilterView
);
254 gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( m_pDialog
), m_pVBox
);
256 m_pPreview
= gtk_image_new();
257 gtk_file_chooser_set_preview_widget( GTK_FILE_CHOOSER( m_pDialog
), m_pPreview
);
259 g_signal_connect( G_OBJECT( m_pToggles
[PREVIEW
] ), "toggled",
260 G_CALLBACK( preview_toggled_cb
), this );
261 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW(m_pFilterView
)), "changed",
262 G_CALLBACK ( type_changed_cb
), this);
263 g_signal_connect( G_OBJECT( m_pDialog
), "notify::filter",
264 G_CALLBACK( filter_changed_cb
), this);
265 g_signal_connect( G_OBJECT( m_pFilterExpander
), "activate",
266 G_CALLBACK( expander_changed_cb
), this);
267 g_signal_connect (G_OBJECT( m_pDialog
), "map",
268 G_CALLBACK (dialog_mapped_cb
), this);
270 gtk_widget_show( m_pVBox
);
272 PangoLayout
*layout
= gtk_widget_create_pango_layout (m_pFilterView
, nullptr);
274 PangoRectangle row_height
;
275 pango_layout_set_markup (layout
, "All Files", -1);
276 pango_layout_get_pixel_extents (layout
, nullptr, &row_height
);
277 g_object_unref (layout
);
279 g_object_get (cell
, "ypad", &ypad
, nullptr);
280 guint height
= (row_height
.height
+ 2*ypad
) * 5;
281 gtk_widget_set_size_request (m_pFilterView
, -1, height
);
282 gtk_widget_set_size_request (m_pPreview
, 1, height
);
284 gtk_file_chooser_set_preview_widget_active( GTK_FILE_CHOOSER( m_pDialog
), true);
287 // XFilePickerNotifier
289 void SAL_CALL
SalGtkFilePicker::addFilePickerListener( const uno::Reference
<XFilePickerListener
>& xListener
)
293 OSL_ENSURE(!m_xListener
.is(),
294 "SalGtkFilePicker only talks with one listener at a time...");
295 m_xListener
= xListener
;
298 void SAL_CALL
SalGtkFilePicker::removeFilePickerListener( const uno::Reference
<XFilePickerListener
>& )
305 // FilePicker Event functions
307 void SalGtkFilePicker::impl_fileSelectionChanged( const FilePickerEvent
& aEvent
)
309 if (m_xListener
.is()) m_xListener
->fileSelectionChanged( aEvent
);
312 void SalGtkFilePicker::impl_directoryChanged( const FilePickerEvent
& aEvent
)
314 if (m_xListener
.is()) m_xListener
->directoryChanged( aEvent
);
317 void SalGtkFilePicker::impl_controlStateChanged( const FilePickerEvent
& aEvent
)
319 if (m_xListener
.is()) m_xListener
->controlStateChanged( aEvent
);
328 css::uno::Sequence
< css::beans::StringPair
> m_aSubFilters
;
331 FilterEntry( const OUString
& _rTitle
, const OUString
& _rFilter
)
333 ,m_sFilter( _rFilter
)
337 const OUString
& getTitle() const { return m_sTitle
; }
338 const OUString
& getFilter() const { return m_sFilter
; }
340 /// determines if the filter has sub filter (i.e., the filter is a filter group in real)
341 bool hasSubFilters( ) const;
343 /** retrieves the filters belonging to the entry
345 void getSubFilters( css::uno::Sequence
< css::beans::StringPair
>& _rSubFilterList
);
347 // helpers for iterating the sub filters
348 const css::beans::StringPair
* beginSubFilters() const { return m_aSubFilters
.begin(); }
349 const css::beans::StringPair
* endSubFilters() const { return m_aSubFilters
.end(); }
352 bool FilterEntry::hasSubFilters() const
354 return m_aSubFilters
.hasElements();
357 void FilterEntry::getSubFilters( css::uno::Sequence
< css::beans::StringPair
>& _rSubFilterList
)
359 _rSubFilterList
= m_aSubFilters
;
363 isFilterString( const OUString
&rFilterString
, const char *pMatch
)
365 sal_Int32 nIndex
= 0;
367 bool bIsFilter
= true;
369 OUString
aMatch(OUString::createFromAscii(pMatch
));
373 aToken
= rFilterString
.getToken( 0, ';', nIndex
);
374 if( !aToken
.match( aMatch
) )
380 while( nIndex
>= 0 );
386 shrinkFilterName( const OUString
&rFilterName
, bool bAllowNoStar
= false )
389 int nBracketLen
= -1;
390 int nBracketEnd
= -1;
391 const sal_Unicode
*pStr
= rFilterName
.getStr();
392 OUString aRealName
= rFilterName
;
394 for( i
= aRealName
.getLength() - 1; i
> 0; i
-- )
398 else if( pStr
[i
] == '(' )
400 nBracketLen
= nBracketEnd
- i
;
401 if( nBracketEnd
<= 0 )
403 if( isFilterString( rFilterName
.copy( i
+ 1, nBracketLen
- 1 ), "*." ) )
404 aRealName
= aRealName
.replaceAt( i
, nBracketLen
+ 1, OUString() );
405 else if (bAllowNoStar
)
407 if( isFilterString( rFilterName
.copy( i
+ 1, nBracketLen
- 1 ), ".") )
408 aRealName
= aRealName
.replaceAt( i
, nBracketLen
+ 1, OUString() );
417 dialog_remove_buttons(GtkWidget
*pActionArea
)
419 GtkContainer
*pContainer
= GTK_CONTAINER( pActionArea
);
421 g_return_if_fail( pContainer
!= nullptr );
423 GList
*pChildren
= gtk_container_get_children( pContainer
);
425 for( GList
*p
= pChildren
; p
; p
= p
->next
)
427 GtkWidget
*pWidget
= GTK_WIDGET( p
->data
);
428 if ( GTK_IS_BUTTON( pWidget
) )
429 gtk_widget_destroy( pWidget
);
432 g_list_free( pChildren
);
436 dialog_remove_buttons( GtkDialog
*pDialog
)
438 g_return_if_fail( GTK_IS_DIALOG( pDialog
) );
440 #if GTK_CHECK_VERSION(3,0,0)
441 GtkWidget
*pHeaderBar
= gtk_dialog_get_header_bar(pDialog
);
442 if( pHeaderBar
!= nullptr )
443 dialog_remove_buttons( pHeaderBar
);
445 dialog_remove_buttons(gtk_dialog_get_action_area(pDialog
));
447 dialog_remove_buttons(pDialog
->action_area
);
453 struct FilterTitleMatch
456 const OUString
& rTitle
;
459 explicit FilterTitleMatch( const OUString
& _rTitle
) : rTitle( _rTitle
) { }
461 bool operator () ( const FilterEntry
& _rEntry
)
464 if( !_rEntry
.hasSubFilters() )
466 bMatch
= (_rEntry
.getTitle() == rTitle
)
467 || (shrinkFilterName(_rEntry
.getTitle()) == rTitle
);
469 // a filter group -> search the sub filters
472 _rEntry
.beginSubFilters(),
473 _rEntry
.endSubFilters(),
479 bool operator () ( const css::beans::StringPair
& _rEntry
)
481 OUString aShrunkName
= shrinkFilterName( _rEntry
.First
);
482 return aShrunkName
== rTitle
;
487 bool SalGtkFilePicker::FilterNameExists( const OUString
& rTitle
)
491 if( m_pFilterVector
)
494 m_pFilterVector
->begin(),
495 m_pFilterVector
->end(),
496 FilterTitleMatch( rTitle
)
502 bool SalGtkFilePicker::FilterNameExists( const css::uno::Sequence
< css::beans::StringPair
>& _rGroupedFilters
)
506 if( m_pFilterVector
)
508 bRet
= std::any_of(_rGroupedFilters
.begin(), _rGroupedFilters
.end(),
509 [&](const css::beans::StringPair
& rFilter
) {
510 return ::std::any_of( m_pFilterVector
->begin(), m_pFilterVector
->end(), FilterTitleMatch( rFilter
.First
) ); });
516 void SalGtkFilePicker::ensureFilterVector( const OUString
& _rInitialCurrentFilter
)
518 if( !m_pFilterVector
)
520 m_pFilterVector
.reset( new std::vector
<FilterEntry
> );
522 // set the first filter to the current filter
523 if ( m_aCurrentFilter
.isEmpty() )
524 m_aCurrentFilter
= _rInitialCurrentFilter
;
528 void SAL_CALL
SalGtkFilePicker::appendFilter( const OUString
& aTitle
, const OUString
& aFilter
)
532 OSL_ASSERT( m_pDialog
!= nullptr );
534 if( FilterNameExists( aTitle
) )
535 throw IllegalArgumentException();
537 // ensure that we have a filter list
538 ensureFilterVector( aTitle
);
541 m_pFilterVector
->insert( m_pFilterVector
->end(), FilterEntry( aTitle
, aFilter
) );
544 void SAL_CALL
SalGtkFilePicker::setCurrentFilter( const OUString
& aTitle
)
548 OSL_ASSERT( m_pDialog
!= nullptr );
550 if( aTitle
!= m_aCurrentFilter
)
552 m_aCurrentFilter
= aTitle
;
553 SetCurFilter( m_aCurrentFilter
);
556 // TODO m_pImpl->setCurrentFilter( aTitle );
559 void SalGtkFilePicker::updateCurrentFilterFromName(const gchar
* filtername
)
561 OUString
aFilterName(filtername
, strlen(filtername
), RTL_TEXTENCODING_UTF8
);
564 for (auto const& filter
: *m_pFilterVector
)
566 if (aFilterName
== shrinkFilterName(filter
.getTitle()))
568 m_aCurrentFilter
= filter
.getTitle();
575 void SalGtkFilePicker::UpdateFilterfromUI()
577 // Update the filtername from the users selection if they have had a chance to do so.
578 // If the user explicitly sets a type then use that, if not then take the implicit type
579 // from the filter of the files glob on which he is currently searching
580 if (!mnHID_FolderChange
|| !mnHID_SelectionChange
)
583 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(m_pFilterView
));
586 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
))
589 gtk_tree_model_get (model
, &iter
, 2, &title
, -1);
590 updateCurrentFilterFromName(title
);
593 else if( GtkFileFilter
*filter
= gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(m_pDialog
)))
595 if (m_pPseudoFilter
!= filter
)
596 updateCurrentFilterFromName(gtk_file_filter_get_name( filter
));
598 updateCurrentFilterFromName(OUStringToOString( m_aInitialFilter
, RTL_TEXTENCODING_UTF8
).getStr());
602 OUString SAL_CALL
SalGtkFilePicker::getCurrentFilter()
606 OSL_ASSERT( m_pDialog
!= nullptr );
608 UpdateFilterfromUI();
610 return m_aCurrentFilter
;
613 // XFilterGroupManager functions
615 void SAL_CALL
SalGtkFilePicker::appendFilterGroup( const OUString
& /*sGroupTitle*/, const uno::Sequence
<beans::StringPair
>& aFilters
)
619 OSL_ASSERT( m_pDialog
!= nullptr );
621 // TODO m_pImpl->appendFilterGroup( sGroupTitle, aFilters );
623 if( FilterNameExists( aFilters
) )
624 // TODO: a more precise exception message
625 throw IllegalArgumentException();
627 // ensure that we have a filter list
628 OUString sInitialCurrentFilter
;
629 if( aFilters
.hasElements() )
630 sInitialCurrentFilter
= aFilters
[0].First
;
632 ensureFilterVector( sInitialCurrentFilter
);
635 for( const auto& rSubFilter
: aFilters
)
636 m_pFilterVector
->insert( m_pFilterVector
->end(), FilterEntry( rSubFilter
.First
, rSubFilter
.Second
) );
640 // XFilePicker functions
642 void SAL_CALL
SalGtkFilePicker::setMultiSelectionMode( sal_Bool bMode
)
646 OSL_ASSERT( m_pDialog
!= nullptr );
648 gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(m_pDialog
), bMode
);
651 void SAL_CALL
SalGtkFilePicker::setDefaultName( const OUString
& aName
)
655 OSL_ASSERT( m_pDialog
!= nullptr );
657 OString aStr
= OUStringToOString( aName
, RTL_TEXTENCODING_UTF8
);
658 GtkFileChooserAction eAction
= gtk_file_chooser_get_action( GTK_FILE_CHOOSER( m_pDialog
) );
660 // set_current_name launches a Gtk critical error if called for other than save
661 if( GTK_FILE_CHOOSER_ACTION_SAVE
== eAction
)
662 gtk_file_chooser_set_current_name( GTK_FILE_CHOOSER( m_pDialog
), aStr
.getStr() );
665 void SAL_CALL
SalGtkFilePicker::setDisplayDirectory( const OUString
& rDirectory
)
669 implsetDisplayDirectory(rDirectory
);
672 OUString SAL_CALL
SalGtkFilePicker::getDisplayDirectory()
676 return implgetDisplayDirectory();
679 uno::Sequence
<OUString
> SAL_CALL
SalGtkFilePicker::getFiles()
681 // no member access => no mutex needed
683 uno::Sequence
< OUString
> aFiles
= getSelectedFiles();
685 The previous multiselection API design was completely broken
686 and unimplementable for some heterogeneous pseudo-URIs eg. search:
687 Thus crop unconditionally to a single selection.
696 bool lcl_matchFilter( const OUString
& rFilter
, const OUString
& rExt
)
698 const sal_Unicode cSep
{';'};
703 const sal_Int32 nBegin
= rFilter
.indexOf(rExt
, nIdx
);
705 if (nBegin
<0) // not found
708 // Let nIdx point to end of matched string, useful in order to
709 // check string boundaries and also for a possible next iteration
710 nIdx
= nBegin
+ rExt
.getLength();
712 // Check if the found occurrence is an exact match: right side
713 if (nIdx
<rFilter
.getLength() && rFilter
[nIdx
]!=cSep
)
716 // Check if the found occurrence is an exact match: left side
717 if (nBegin
>0 && rFilter
[nBegin
-1]!=cSep
)
728 uno::Sequence
<OUString
> SAL_CALL
SalGtkFilePicker::getSelectedFiles()
732 OSL_ASSERT( m_pDialog
!= nullptr );
734 GSList
* pPathList
= gtk_file_chooser_get_uris( GTK_FILE_CHOOSER(m_pDialog
) );
736 int nCount
= g_slist_length( pPathList
);
739 // get the current action setting
740 GtkFileChooserAction eAction
= gtk_file_chooser_get_action(
741 GTK_FILE_CHOOSER( m_pDialog
));
743 uno::Sequence
< OUString
> aSelectedFiles(nCount
);
746 for( GSList
*pElem
= pPathList
; pElem
; pElem
= pElem
->next
)
748 gchar
*pURI
= static_cast<gchar
*>(pElem
->data
);
749 aSelectedFiles
[ nIndex
] = uritounicode(pURI
);
751 if( GTK_FILE_CHOOSER_ACTION_SAVE
== eAction
)
753 OUString sFilterName
;
754 sal_Int32 nTokenIndex
= 0;
755 bool bExtensionTypedIn
= false;
757 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(m_pFilterView
));
760 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
))
762 gchar
*title
= nullptr;
763 gtk_tree_model_get (model
, &iter
, 2, &title
, -1);
765 sFilterName
= OUString( title
, strlen( title
), RTL_TEXTENCODING_UTF8
);
767 sFilterName
= OUString();
772 if( aSelectedFiles
[nIndex
].indexOf('.') > 0 )
777 sExtension
= aSelectedFiles
[nIndex
].getToken( 0, '.', nTokenIndex
);
778 while( nTokenIndex
>= 0 );
780 if( sExtension
.getLength() >= 3 ) // 3 = typical/minimum extension length
783 OUString aOldFilter
= getCurrentFilter();
784 bool bChangeFilter
= true;
785 if ( m_pFilterVector
)
786 for (auto const& filter
: *m_pFilterVector
)
788 if( lcl_matchFilter( filter
.getFilter(), "*." + sExtension
) )
790 if( aNewFilter
.isEmpty() )
791 aNewFilter
= filter
.getTitle();
793 if( aOldFilter
== filter
.getTitle() )
794 bChangeFilter
= false;
796 bExtensionTypedIn
= true;
799 if( bChangeFilter
&& bExtensionTypedIn
)
801 #if GTK_CHECK_VERSION(3,0,0)
802 gchar
* pCurrentName
= gtk_file_chooser_get_current_name(GTK_FILE_CHOOSER(m_pDialog
));
803 setCurrentFilter( aNewFilter
);
804 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(m_pDialog
), pCurrentName
);
805 g_free(pCurrentName
);
807 setCurrentFilter( aNewFilter
);
813 GtkFileFilter
*filter
= gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(m_pDialog
));
814 if (m_pPseudoFilter
!= filter
)
816 const gchar
* filtername
= filter
? gtk_file_filter_get_name(filter
) : nullptr;
818 sFilterName
= OUString(filtername
, strlen( filtername
), RTL_TEXTENCODING_UTF8
);
823 sFilterName
= m_aInitialFilter
;
828 auto aVectorIter
= ::std::find_if(
829 m_pFilterVector
->begin(), m_pFilterVector
->end(), FilterTitleMatch(sFilterName
) );
832 if (aVectorIter
!= m_pFilterVector
->end())
833 aFilter
= aVectorIter
->getFilter();
839 sToken
= aFilter
.getToken( 0, '.', nTokenIndex
);
841 if ( sToken
.lastIndexOf( ';' ) != -1 )
843 sToken
= sToken
.getToken(0, ';');
847 while( nTokenIndex
>= 0 );
849 if( !bExtensionTypedIn
&& ( sToken
!= "*" ) )
851 //if the filename does not already have the auto extension, stick it on
852 OUString sExtension
= "." + sToken
;
853 OUString
&rBase
= aSelectedFiles
[nIndex
];
854 sal_Int32 nExtensionIdx
= rBase
.getLength() - sExtension
.getLength();
857 "idx are " << rBase
.lastIndexOf(sExtension
) << " "
860 if( rBase
.lastIndexOf( sExtension
) != nExtensionIdx
)
871 g_slist_free( pPathList
);
873 return aSelectedFiles
;
876 // XExecutableDialog functions
878 void SAL_CALL
SalGtkFilePicker::setTitle( const OUString
& rTitle
)
882 implsetTitle(rTitle
);
885 sal_Int16 SAL_CALL
SalGtkFilePicker::execute()
889 OSL_ASSERT( m_pDialog
!= nullptr );
891 sal_Int16 retVal
= 0;
895 // tdf#84431 - set the filter after the corresponding widget is created
896 if ( !m_aCurrentFilter
.isEmpty() )
897 SetCurFilter(m_aCurrentFilter
);
900 g_signal_connect( GTK_FILE_CHOOSER( m_pDialog
), "current-folder-changed",
901 G_CALLBACK( folder_changed_cb
), static_cast<gpointer
>(this) );
903 mnHID_SelectionChange
=
904 g_signal_connect( GTK_FILE_CHOOSER( m_pDialog
), "selection-changed",
905 G_CALLBACK( selection_changed_cb
), static_cast<gpointer
>(this) );
907 int btn
= GTK_RESPONSE_NO
;
909 uno::Reference
< awt::XExtendedToolkit
> xToolkit(
910 awt::Toolkit::create(m_xContext
),
913 uno::Reference
< frame::XDesktop
> xDesktop(
914 frame::Desktop::create(m_xContext
),
917 GtkWindow
*pParent
= GTK_WINDOW(m_pParentWidget
);
920 SAL_WARN( "vcl.gtk", "no parent widget set");
921 pParent
= RunDialog::GetTransientFor();
924 gtk_window_set_transient_for(GTK_WINDOW(m_pDialog
), pParent
);
925 RunDialog
* pRunDialog
= new RunDialog(m_pDialog
, xToolkit
, xDesktop
);
926 uno::Reference
< awt::XTopWindowListener
> xLifeCycle(pRunDialog
);
927 while( GTK_RESPONSE_NO
== btn
)
929 btn
= GTK_RESPONSE_YES
; // we don't want to repeat unless user clicks NO for file save.
931 gint nStatus
= pRunDialog
->run();
934 case GTK_RESPONSE_ACCEPT
:
935 if( GTK_FILE_CHOOSER_ACTION_SAVE
== gtk_file_chooser_get_action( GTK_FILE_CHOOSER( m_pDialog
) ) )
937 Sequence
< OUString
> aPathSeq
= getFiles();
938 if( aPathSeq
.getLength() == 1 )
940 OString sFileName
= unicodetouri( aPathSeq
[0] );
941 gchar
*gFileName
= g_filename_from_uri ( sFileName
.getStr(), nullptr, nullptr );
942 if( g_file_test( gFileName
, G_FILE_TEST_IS_REGULAR
) )
944 INetURLObject
aFileObj( OStringToOUString(sFileName
, RTL_TEXTENCODING_UTF8
) );
949 INetURLObject::LAST_SEGMENT
,
951 INetURLObject::DecodeMechanism::WithCharset
953 RTL_TEXTENCODING_UTF8
958 getResString( FILE_PICKER_OVERWRITE_PRIMARY
),
959 RTL_TEXTENCODING_UTF8
962 OString
toReplace("$filename$");
964 aMsg
= aMsg
.replaceAt(
965 aMsg
.indexOf( toReplace
),
966 toReplace
.getLength(),
970 GtkWidget
*dlg
= gtk_message_dialog_new( nullptr,
972 GTK_MESSAGE_QUESTION
,
978 sal_Int32 nSegmentCount
= aFileObj
.getSegmentCount();
979 if (nSegmentCount
>= 2)
986 INetURLObject::DecodeMechanism::WithCharset
988 RTL_TEXTENCODING_UTF8
994 getResString( FILE_PICKER_OVERWRITE_SECONDARY
),
995 RTL_TEXTENCODING_UTF8
998 toReplace
= "$dirname$";
1000 aMsg
= aMsg
.replaceAt(
1001 aMsg
.indexOf( toReplace
),
1002 toReplace
.getLength(),
1006 gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( dlg
), "%s", aMsg
.getStr() );
1009 gtk_window_set_title( GTK_WINDOW( dlg
),
1010 OUStringToOString(getResString(FILE_PICKER_TITLE_SAVE
),
1011 RTL_TEXTENCODING_UTF8
).getStr() );
1012 #if GTK_CHECK_VERSION(3,0,0)
1013 gtk_window_set_transient_for(GTK_WINDOW(dlg
), GTK_WINDOW(m_pDialog
));
1016 gtk_window_set_transient_for(GTK_WINDOW(dlg
), pParent
);
1018 RunDialog
* pAnotherDialog
= new RunDialog(dlg
, xToolkit
, xDesktop
);
1019 uno::Reference
< awt::XTopWindowListener
> xAnotherLifeCycle(pAnotherDialog
);
1020 btn
= pAnotherDialog
->run();
1022 gtk_widget_destroy( dlg
);
1026 if( btn
== GTK_RESPONSE_YES
)
1027 retVal
= ExecutableDialogResults::OK
;
1031 retVal
= ExecutableDialogResults::OK
;
1034 case GTK_RESPONSE_CANCEL
:
1035 retVal
= ExecutableDialogResults::CANCEL
;
1040 FilePickerEvent evt
;
1041 evt
.ElementId
= PUSHBUTTON_PLAY
;
1042 impl_controlStateChanged( evt
);
1043 btn
= GTK_RESPONSE_NO
;
1052 gtk_widget_hide(m_pDialog
);
1054 if (mnHID_FolderChange
)
1055 g_signal_handler_disconnect(GTK_FILE_CHOOSER( m_pDialog
), mnHID_FolderChange
);
1056 if (mnHID_SelectionChange
)
1057 g_signal_handler_disconnect(GTK_FILE_CHOOSER( m_pDialog
), mnHID_SelectionChange
);
1062 // cf. offapi/com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.idl
1063 GtkWidget
*SalGtkFilePicker::getWidget( sal_Int16 nControlId
, GType
*pType
)
1065 GType tType
= GTK_TYPE_TOGGLE_BUTTON
; //prevent warning by initializing
1066 GtkWidget
*pWidget
= nullptr;
1068 #define MAP_TOGGLE( elem ) \
1069 case ExtendedFilePickerElementIds::CHECKBOX_##elem: \
1070 pWidget = m_pToggles[elem]; tType = GTK_TYPE_TOGGLE_BUTTON; \
1072 #define MAP_BUTTON( elem ) \
1073 case ExtendedFilePickerElementIds::PUSHBUTTON_##elem: \
1074 pWidget = m_pButtons[elem]; tType = GTK_TYPE_BUTTON; \
1076 #define MAP_LIST( elem ) \
1077 case ExtendedFilePickerElementIds::LISTBOX_##elem: \
1078 pWidget = m_pLists[elem]; tType = GTK_TYPE_COMBO_BOX; \
1080 #define MAP_LIST_LABEL( elem ) \
1081 case ExtendedFilePickerElementIds::LISTBOX_##elem##_LABEL: \
1082 pWidget = m_pListLabels[elem]; tType = GTK_TYPE_LABEL; \
1085 switch( nControlId
)
1087 MAP_TOGGLE( AUTOEXTENSION
);
1088 MAP_TOGGLE( PASSWORD
);
1089 MAP_TOGGLE( GPGENCRYPTION
);
1090 MAP_TOGGLE( FILTEROPTIONS
);
1091 MAP_TOGGLE( READONLY
);
1093 MAP_TOGGLE( PREVIEW
);
1094 MAP_TOGGLE( SELECTION
);
1096 MAP_LIST( VERSION
);
1097 MAP_LIST( TEMPLATE
);
1098 MAP_LIST( IMAGE_TEMPLATE
);
1099 MAP_LIST( IMAGE_ANCHOR
);
1100 MAP_LIST_LABEL( VERSION
);
1101 MAP_LIST_LABEL( TEMPLATE
);
1102 MAP_LIST_LABEL( IMAGE_TEMPLATE
);
1103 MAP_LIST_LABEL( IMAGE_ANCHOR
);
1105 SAL_WARN( "vcl.gtk", "Handle unknown control " << nControlId
);
1115 // XFilePickerControlAccess functions
1117 static void HackWidthToFirst(GtkComboBox
*pWidget
)
1119 GtkRequisition requisition
;
1120 gtk_widget_size_request(GTK_WIDGET(pWidget
), &requisition
);
1121 gtk_widget_set_size_request(GTK_WIDGET(pWidget
), requisition
.width
, -1);
1124 static void ComboBoxAppendText(GtkComboBox
*pCombo
, const OUString
&rStr
)
1127 GtkListStore
*pStore
= GTK_LIST_STORE(gtk_combo_box_get_model(pCombo
));
1128 OString aStr
= OUStringToOString(rStr
, RTL_TEXTENCODING_UTF8
);
1129 gtk_list_store_append(pStore
, &aIter
);
1130 gtk_list_store_set(pStore
, &aIter
, 0, aStr
.getStr(), -1);
1133 void SalGtkFilePicker::HandleSetListValue(GtkComboBox
*pWidget
, sal_Int16 nControlAction
, const uno::Any
& rValue
)
1135 switch (nControlAction
)
1137 case ControlActions::ADD_ITEM
:
1141 ComboBoxAppendText(pWidget
, sItem
);
1142 if (!bVersionWidthUnset
)
1144 HackWidthToFirst(pWidget
);
1145 bVersionWidthUnset
= true;
1149 case ControlActions::ADD_ITEMS
:
1151 Sequence
< OUString
> aStringList
;
1152 rValue
>>= aStringList
;
1153 for (const auto& rString
: std::as_const(aStringList
))
1155 ComboBoxAppendText(pWidget
, rString
);
1156 if (!bVersionWidthUnset
)
1158 HackWidthToFirst(pWidget
);
1159 bVersionWidthUnset
= true;
1164 case ControlActions::DELETE_ITEM
:
1170 GtkListStore
*pStore
= GTK_LIST_STORE(
1171 gtk_combo_box_get_model(GTK_COMBO_BOX(pWidget
)));
1172 if(gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pStore
), &aIter
, nullptr, nPos
))
1173 gtk_list_store_remove(pStore
, &aIter
);
1176 case ControlActions::DELETE_ITEMS
:
1178 gtk_combo_box_set_active(pWidget
, -1);
1179 GtkListStore
*pStore
= GTK_LIST_STORE(
1180 gtk_combo_box_get_model(GTK_COMBO_BOX(pWidget
)));
1181 gtk_list_store_clear(pStore
);
1184 case ControlActions::SET_SELECT_ITEM
:
1188 gtk_combo_box_set_active(pWidget
, nPos
);
1192 SAL_WARN( "vcl.gtk", "undocumented/unimplemented ControlAction for a list " << nControlAction
);
1196 //I think its best to make it insensitive unless there is the chance to
1197 //actually select something from the list.
1198 gint nItems
= gtk_tree_model_iter_n_children(
1199 gtk_combo_box_get_model(pWidget
), nullptr);
1200 gtk_widget_set_sensitive(GTK_WIDGET(pWidget
), nItems
> 1);
1203 uno::Any
SalGtkFilePicker::HandleGetListValue(GtkComboBox
*pWidget
, sal_Int16 nControlAction
)
1206 switch (nControlAction
)
1208 case ControlActions::GET_ITEMS
:
1210 Sequence
< OUString
> aItemList
;
1212 GtkTreeModel
*pTree
= gtk_combo_box_get_model(pWidget
);
1214 if (gtk_tree_model_get_iter_first(pTree
, &iter
))
1216 sal_Int32 nSize
= gtk_tree_model_iter_n_children(
1219 aItemList
.realloc(nSize
);
1220 for (sal_Int32 i
=0; i
< nSize
; ++i
)
1223 gtk_tree_model_get(gtk_combo_box_get_model(pWidget
),
1224 &iter
, 0, &item
, -1);
1225 aItemList
[i
] = OUString(item
, strlen(item
), RTL_TEXTENCODING_UTF8
);
1227 (void)gtk_tree_model_iter_next(pTree
, &iter
);
1233 case ControlActions::GET_SELECTED_ITEM
:
1236 if (gtk_combo_box_get_active_iter(pWidget
, &iter
))
1239 gtk_tree_model_get(gtk_combo_box_get_model(pWidget
),
1240 &iter
, 0, &item
, -1);
1241 OUString
sItem(item
, strlen(item
), RTL_TEXTENCODING_UTF8
);
1247 case ControlActions::GET_SELECTED_ITEM_INDEX
:
1249 gint nActive
= gtk_combo_box_get_active(pWidget
);
1250 aAny
<<= static_cast< sal_Int32
>(nActive
);
1254 SAL_WARN( "vcl.gtk", "undocumented/unimplemented ControlAction for a list " << nControlAction
);
1260 void SAL_CALL
SalGtkFilePicker::setValue( sal_Int16 nControlId
, sal_Int16 nControlAction
, const uno::Any
& rValue
)
1264 OSL_ASSERT( m_pDialog
!= nullptr );
1269 if( !( pWidget
= getWidget( nControlId
, &tType
) ) )
1270 SAL_WARN( "vcl.gtk", "enable unknown control " << nControlId
);
1271 else if( tType
== GTK_TYPE_TOGGLE_BUTTON
)
1273 bool bChecked
= false;
1274 rValue
>>= bChecked
;
1275 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( pWidget
), bChecked
);
1277 else if( tType
== GTK_TYPE_COMBO_BOX
)
1278 HandleSetListValue(GTK_COMBO_BOX(pWidget
), nControlAction
, rValue
);
1281 SAL_WARN( "vcl.gtk", "Can't set value on button / list " << nControlId
<< " " << nControlAction
);
1285 uno::Any SAL_CALL
SalGtkFilePicker::getValue( sal_Int16 nControlId
, sal_Int16 nControlAction
)
1289 OSL_ASSERT( m_pDialog
!= nullptr );
1296 if( !( pWidget
= getWidget( nControlId
, &tType
) ) )
1297 SAL_WARN( "vcl.gtk", "enable unknown control " << nControlId
);
1298 else if( tType
== GTK_TYPE_TOGGLE_BUTTON
)
1299 aRetval
<<= bool( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( pWidget
) ) );
1300 else if( tType
== GTK_TYPE_COMBO_BOX
)
1301 aRetval
= HandleGetListValue(GTK_COMBO_BOX(pWidget
), nControlAction
);
1303 SAL_WARN( "vcl.gtk", "Can't get value on button / list " << nControlId
<< " " << nControlAction
);
1308 void SAL_CALL
SalGtkFilePicker::enableControl( sal_Int16 nControlId
, sal_Bool bEnable
)
1312 OSL_ASSERT( m_pDialog
!= nullptr );
1316 if ( ( pWidget
= getWidget( nControlId
) ) )
1320 gtk_widget_set_sensitive( pWidget
, true );
1324 gtk_widget_set_sensitive( pWidget
, false );
1328 SAL_WARN( "vcl.gtk", "enable unknown control " << nControlId
);
1331 void SAL_CALL
SalGtkFilePicker::setLabel( sal_Int16 nControlId
, const OUString
& rLabel
)
1335 OSL_ASSERT( m_pDialog
!= nullptr );
1340 if( !( pWidget
= getWidget( nControlId
, &tType
) ) )
1342 SAL_WARN( "vcl.gtk", "Set label on unknown control " << nControlId
);
1346 OString aTxt
= OUStringToOString( rLabel
.replace('~', '_'), RTL_TEXTENCODING_UTF8
);
1347 if (nControlId
== ExtendedFilePickerElementIds::PUSHBUTTON_PLAY
)
1349 #ifdef GTK_STOCK_MEDIA_PLAY
1350 if (msPlayLabel
.isEmpty())
1351 msPlayLabel
= rLabel
;
1352 if (msPlayLabel
== rLabel
)
1353 gtk_button_set_label(GTK_BUTTON(pWidget
), GTK_STOCK_MEDIA_PLAY
);
1355 gtk_button_set_label(GTK_BUTTON(pWidget
), GTK_STOCK_MEDIA_STOP
);
1357 gtk_button_set_label(GTK_BUTTON(pWidget
), aTxt
.getStr());
1360 else if( tType
== GTK_TYPE_TOGGLE_BUTTON
|| tType
== GTK_TYPE_BUTTON
|| tType
== GTK_TYPE_LABEL
)
1361 g_object_set( pWidget
, "label", aTxt
.getStr(),
1362 "use_underline", true, nullptr );
1364 SAL_WARN( "vcl.gtk", "Can't set label on list");
1367 OUString SAL_CALL
SalGtkFilePicker::getLabel( sal_Int16 nControlId
)
1371 OSL_ASSERT( m_pDialog
!= nullptr );
1377 if( !( pWidget
= getWidget( nControlId
, &tType
) ) )
1378 SAL_WARN( "vcl.gtk", "Get label on unknown control " << nControlId
);
1379 else if( tType
== GTK_TYPE_TOGGLE_BUTTON
|| tType
== GTK_TYPE_BUTTON
|| tType
== GTK_TYPE_LABEL
)
1380 aTxt
= gtk_button_get_label( GTK_BUTTON( pWidget
) );
1382 SAL_WARN( "vcl.gtk", "Can't get label on list");
1384 return OStringToOUString( aTxt
, RTL_TEXTENCODING_UTF8
);
1387 // XFilePreview functions
1389 uno::Sequence
<sal_Int16
> SAL_CALL
SalGtkFilePicker::getSupportedImageFormats()
1393 OSL_ASSERT( m_pDialog
!= nullptr );
1395 // TODO return m_pImpl->getSupportedImageFormats();
1396 return uno::Sequence
<sal_Int16
>();
1399 sal_Int32 SAL_CALL
SalGtkFilePicker::getTargetColorDepth()
1403 OSL_ASSERT( m_pDialog
!= nullptr );
1405 // TODO return m_pImpl->getTargetColorDepth();
1409 sal_Int32 SAL_CALL
SalGtkFilePicker::getAvailableWidth()
1413 OSL_ASSERT( m_pDialog
!= nullptr );
1415 return g_PreviewImageWidth
;
1418 sal_Int32 SAL_CALL
SalGtkFilePicker::getAvailableHeight()
1422 OSL_ASSERT( m_pDialog
!= nullptr );
1424 return g_PreviewImageHeight
;
1427 void SAL_CALL
SalGtkFilePicker::setImage( sal_Int16
/*aImageFormat*/, const uno::Any
& /*aImage*/ )
1431 OSL_ASSERT( m_pDialog
!= nullptr );
1433 // TODO m_pImpl->setImage( aImageFormat, aImage );
1436 void SalGtkFilePicker::implChangeType( GtkTreeSelection
*selection
)
1438 OUString aLabel
= getResString( FILE_PICKER_FILE_TYPE
);
1441 GtkTreeModel
*model
;
1442 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
))
1445 gtk_tree_model_get (model
, &iter
, 2, &title
, -1);
1447 aLabel
+= OUString( title
, strlen(title
), RTL_TEXTENCODING_UTF8
);
1450 gtk_expander_set_label (GTK_EXPANDER (m_pFilterExpander
),
1451 OUStringToOString( aLabel
, RTL_TEXTENCODING_UTF8
).getStr());
1452 FilePickerEvent evt
;
1453 evt
.ElementId
= LISTBOX_FILTER
;
1454 impl_controlStateChanged( evt
);
1457 void SalGtkFilePicker::type_changed_cb( GtkTreeSelection
*selection
, SalGtkFilePicker
*pobjFP
)
1459 pobjFP
->implChangeType(selection
);
1462 void SalGtkFilePicker::unselect_type()
1464 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(m_pFilterView
)));
1467 void SalGtkFilePicker::expander_changed_cb( GtkExpander
*expander
, SalGtkFilePicker
*pobjFP
)
1469 if (gtk_expander_get_expanded(expander
))
1470 pobjFP
->unselect_type();
1473 void SalGtkFilePicker::filter_changed_cb( GtkFileChooser
*, GParamSpec
*,
1474 SalGtkFilePicker
*pobjFP
)
1476 FilePickerEvent evt
;
1477 evt
.ElementId
= LISTBOX_FILTER
;
1478 SAL_INFO( "vcl.gtk", "filter_changed, isn't it great " << pobjFP
);
1479 pobjFP
->impl_controlStateChanged( evt
);
1482 void SalGtkFilePicker::folder_changed_cb( GtkFileChooser
*, SalGtkFilePicker
*pobjFP
)
1484 FilePickerEvent evt
;
1485 SAL_INFO( "vcl.gtk", "folder_changed, isn't it great " << pobjFP
);
1486 pobjFP
->impl_directoryChanged( evt
);
1489 void SalGtkFilePicker::selection_changed_cb( GtkFileChooser
*, SalGtkFilePicker
*pobjFP
)
1491 FilePickerEvent evt
;
1492 SAL_INFO( "vcl.gtk", "selection_changed, isn't it great " << pobjFP
);
1493 pobjFP
->impl_fileSelectionChanged( evt
);
1496 void SalGtkFilePicker::update_preview_cb( GtkFileChooser
*file_chooser
, SalGtkFilePicker
* pobjFP
)
1498 gboolean have_preview
= false;
1500 GtkWidget
* preview
= pobjFP
->m_pPreview
;
1501 char* filename
= gtk_file_chooser_get_preview_filename( file_chooser
);
1503 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pobjFP
->m_pToggles
[PREVIEW
])) && filename
&& g_file_test(filename
, G_FILE_TEST_IS_REGULAR
))
1505 GdkPixbuf
*pixbuf
= gdk_pixbuf_new_from_file_at_size(
1507 g_PreviewImageWidth
,
1508 g_PreviewImageHeight
, nullptr );
1510 have_preview
= ( pixbuf
!= nullptr );
1512 gtk_image_set_from_pixbuf( GTK_IMAGE( preview
), pixbuf
);
1514 g_object_unref( G_OBJECT( pixbuf
) );
1518 gtk_file_chooser_set_preview_widget_active( file_chooser
, have_preview
);
1524 sal_Bool SAL_CALL
SalGtkFilePicker::setShowState( sal_Bool bShowState
)
1528 OSL_ASSERT( m_pDialog
!= nullptr );
1530 // TODO return m_pImpl->setShowState( bShowState );
1531 if( bool(bShowState
) != mbPreviewState
)
1538 mHID_Preview
= g_signal_connect(
1539 GTK_FILE_CHOOSER( m_pDialog
), "update-preview",
1540 G_CALLBACK( update_preview_cb
), static_cast<gpointer
>(this) );
1542 gtk_widget_show( m_pPreview
);
1547 gtk_widget_hide( m_pPreview
);
1550 // also emit the signal
1551 g_signal_emit_by_name( G_OBJECT( m_pDialog
), "update-preview" );
1553 mbPreviewState
= bShowState
;
1558 sal_Bool SAL_CALL
SalGtkFilePicker::getShowState()
1562 OSL_ASSERT( m_pDialog
!= nullptr );
1564 return mbPreviewState
;
1569 void SAL_CALL
SalGtkFilePicker::initialize( const uno::Sequence
<uno::Any
>& aArguments
)
1571 // parameter checking
1573 if( !aArguments
.hasElements() )
1574 throw lang::IllegalArgumentException(
1576 static_cast<XFilePicker2
*>( this ), 1 );
1578 aAny
= aArguments
[0];
1580 if( ( aAny
.getValueType() != cppu::UnoType
<sal_Int16
>::get()) &&
1581 (aAny
.getValueType() != cppu::UnoType
<sal_Int8
>::get()) )
1582 throw lang::IllegalArgumentException(
1583 "invalid argument type",
1584 static_cast<XFilePicker2
*>( this ), 1 );
1586 sal_Int16 templateId
= -1;
1587 aAny
>>= templateId
;
1589 css::uno::Reference
<css::awt::XWindow
> xParentWindow
;
1590 if (aArguments
.getLength() > 1)
1592 aArguments
[1] >>= xParentWindow
;
1595 if (xParentWindow
.is())
1597 if (SalGtkXWindow
* pGtkXWindow
= dynamic_cast<SalGtkXWindow
*>(xParentWindow
.get()))
1598 m_pParentWidget
= pGtkXWindow
->getWidget();
1601 css::uno::Reference
<css::awt::XSystemDependentWindowPeer
> xSysDepWin(xParentWindow
, css::uno::UNO_QUERY
);
1602 if (xSysDepWin
.is())
1604 css::uno::Sequence
<sal_Int8
> aProcessIdent(16);
1605 rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8
*>(aProcessIdent
.getArray()));
1606 aAny
= xSysDepWin
->getWindowHandle(aProcessIdent
, css::lang::SystemDependent::SYSTEM_XWINDOW
);
1607 css::awt::SystemDependentXWindow tmp
;
1609 m_pParentWidget
= GetGtkSalData()->GetGtkDisplay()->findGtkWidgetForNativeHandle(tmp
.WindowHandle
);
1614 GtkFileChooserAction eAction
= GTK_FILE_CHOOSER_ACTION_OPEN
;
1615 const gchar
*first_button_text
= GTK_STOCK_OPEN
;
1619 // TODO: extract full semantic from
1620 // svtools/source/filepicker/filepicker.cxx (getWinBits)
1621 switch( templateId
)
1623 case FILEOPEN_SIMPLE
:
1624 eAction
= GTK_FILE_CHOOSER_ACTION_OPEN
;
1625 first_button_text
= GTK_STOCK_OPEN
;
1627 case FILESAVE_SIMPLE
:
1628 eAction
= GTK_FILE_CHOOSER_ACTION_SAVE
;
1629 first_button_text
= GTK_STOCK_SAVE
;
1631 case FILESAVE_AUTOEXTENSION_PASSWORD
:
1632 eAction
= GTK_FILE_CHOOSER_ACTION_SAVE
;
1633 first_button_text
= GTK_STOCK_SAVE
;
1634 mbToggleVisibility
[PASSWORD
] = true;
1635 mbToggleVisibility
[GPGENCRYPTION
] = true;
1638 case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS
:
1639 eAction
= GTK_FILE_CHOOSER_ACTION_SAVE
;
1640 first_button_text
= GTK_STOCK_SAVE
;
1641 mbToggleVisibility
[PASSWORD
] = true;
1642 mbToggleVisibility
[GPGENCRYPTION
] = true;
1643 mbToggleVisibility
[FILTEROPTIONS
] = true;
1646 case FILESAVE_AUTOEXTENSION_SELECTION
:
1647 eAction
= GTK_FILE_CHOOSER_ACTION_SAVE
; // SELECT_FOLDER ?
1648 first_button_text
= GTK_STOCK_SAVE
;
1649 mbToggleVisibility
[SELECTION
] = true;
1652 case FILESAVE_AUTOEXTENSION_TEMPLATE
:
1653 eAction
= GTK_FILE_CHOOSER_ACTION_SAVE
;
1654 first_button_text
= GTK_STOCK_SAVE
;
1655 mbListVisibility
[TEMPLATE
] = true;
1658 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE
:
1659 eAction
= GTK_FILE_CHOOSER_ACTION_OPEN
;
1660 first_button_text
= GTK_STOCK_OPEN
;
1661 mbToggleVisibility
[LINK
] = true;
1662 mbToggleVisibility
[PREVIEW
] = true;
1663 mbListVisibility
[IMAGE_TEMPLATE
] = true;
1666 case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR
:
1667 eAction
= GTK_FILE_CHOOSER_ACTION_OPEN
;
1668 first_button_text
= GTK_STOCK_OPEN
;
1669 mbToggleVisibility
[LINK
] = true;
1670 mbToggleVisibility
[PREVIEW
] = true;
1671 mbListVisibility
[IMAGE_ANCHOR
] = true;
1675 eAction
= GTK_FILE_CHOOSER_ACTION_OPEN
;
1676 first_button_text
= GTK_STOCK_OPEN
;
1677 mbButtonVisibility
[PLAY
] = true;
1680 case FILEOPEN_LINK_PLAY
:
1681 eAction
= GTK_FILE_CHOOSER_ACTION_OPEN
;
1682 first_button_text
= GTK_STOCK_OPEN
;
1683 mbToggleVisibility
[LINK
] = true;
1684 mbButtonVisibility
[PLAY
] = true;
1687 case FILEOPEN_READONLY_VERSION
:
1688 eAction
= GTK_FILE_CHOOSER_ACTION_OPEN
;
1689 first_button_text
= GTK_STOCK_OPEN
;
1690 mbToggleVisibility
[READONLY
] = true;
1691 mbListVisibility
[VERSION
] = true;
1693 case FILEOPEN_LINK_PREVIEW
:
1694 eAction
= GTK_FILE_CHOOSER_ACTION_OPEN
;
1695 first_button_text
= GTK_STOCK_OPEN
;
1696 mbToggleVisibility
[LINK
] = true;
1697 mbToggleVisibility
[PREVIEW
] = true;
1700 case FILESAVE_AUTOEXTENSION
:
1701 eAction
= GTK_FILE_CHOOSER_ACTION_SAVE
;
1702 first_button_text
= GTK_STOCK_SAVE
;
1705 case FILEOPEN_PREVIEW
:
1706 eAction
= GTK_FILE_CHOOSER_ACTION_OPEN
;
1707 first_button_text
= GTK_STOCK_OPEN
;
1708 mbToggleVisibility
[PREVIEW
] = true;
1712 throw lang::IllegalArgumentException(
1714 static_cast< XFilePicker2
* >( this ),
1718 if( GTK_FILE_CHOOSER_ACTION_SAVE
== eAction
)
1720 OUString
aFilePickerTitle(getResString( FILE_PICKER_TITLE_SAVE
));
1721 gtk_window_set_title ( GTK_WINDOW( m_pDialog
),
1722 OUStringToOString( aFilePickerTitle
, RTL_TEXTENCODING_UTF8
).getStr() );
1725 gtk_file_chooser_set_action( GTK_FILE_CHOOSER( m_pDialog
), eAction
);
1726 dialog_remove_buttons( GTK_DIALOG( m_pDialog
) );
1727 gtk_dialog_add_button( GTK_DIALOG( m_pDialog
), GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
);
1728 for( int nTVIndex
= 0; nTVIndex
< BUTTON_LAST
; nTVIndex
++ )
1730 if( mbButtonVisibility
[nTVIndex
] )
1732 #ifdef GTK_STOCK_MEDIA_PLAY
1733 m_pButtons
[ nTVIndex
] = gtk_dialog_add_button( GTK_DIALOG( m_pDialog
), GTK_STOCK_MEDIA_PLAY
, 1 );
1735 OString aPlay
= OUStringToOString( getResString( PUSHBUTTON_PLAY
), RTL_TEXTENCODING_UTF8
);
1736 m_pButtons
[ nTVIndex
] = gtk_dialog_add_button( GTK_DIALOG( m_pDialog
), aPlay
.getStr(), 1 );
1740 gtk_dialog_add_button( GTK_DIALOG( m_pDialog
), first_button_text
, GTK_RESPONSE_ACCEPT
);
1742 gtk_dialog_set_default_response( GTK_DIALOG (m_pDialog
), GTK_RESPONSE_ACCEPT
);
1744 // Setup special flags
1745 for( int nTVIndex
= 0; nTVIndex
< TOGGLE_LAST
; nTVIndex
++ )
1747 if( mbToggleVisibility
[nTVIndex
] )
1748 gtk_widget_show( m_pToggles
[ nTVIndex
] );
1751 for( int nTVIndex
= 0; nTVIndex
< LIST_LAST
; nTVIndex
++ )
1753 if( mbListVisibility
[nTVIndex
] )
1755 gtk_widget_set_sensitive( m_pLists
[ nTVIndex
], false );
1756 gtk_widget_show( m_pLists
[ nTVIndex
] );
1757 gtk_widget_show( m_pListLabels
[ nTVIndex
] );
1758 gtk_widget_show( m_pAligns
[ nTVIndex
] );
1759 gtk_widget_show( m_pHBoxs
[ nTVIndex
] );
1764 void SalGtkFilePicker::preview_toggled_cb( GObject
*cb
, SalGtkFilePicker
* pobjFP
)
1766 if( pobjFP
->mbToggleVisibility
[PREVIEW
] )
1767 pobjFP
->setShowState( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( cb
) ) );
1772 void SAL_CALL
SalGtkFilePicker::cancel()
1776 OSL_ASSERT( m_pDialog
!= nullptr );
1778 // TODO m_pImpl->cancel();
1783 void SalGtkFilePicker::SetCurFilter( const OUString
& rFilter
)
1785 // Get all the filters already added
1786 GSList
*filters
= gtk_file_chooser_list_filters ( GTK_FILE_CHOOSER( m_pDialog
) );
1787 bool bFound
= false;
1789 for( GSList
*iter
= filters
; !bFound
&& iter
; iter
= iter
->next
)
1791 GtkFileFilter
* pFilter
= static_cast<GtkFileFilter
*>( iter
->data
);
1792 G_CONST_RETURN gchar
* filtername
= gtk_file_filter_get_name( pFilter
);
1793 OUString
sFilterName( filtername
, strlen( filtername
), RTL_TEXTENCODING_UTF8
);
1795 OUString aShrunkName
= shrinkFilterName( rFilter
);
1796 if( aShrunkName
== sFilterName
)
1798 SAL_INFO( "vcl.gtk", "actually setting " << filtername
);
1799 gtk_file_chooser_set_filter( GTK_FILE_CHOOSER( m_pDialog
), pFilter
);
1804 g_slist_free( filters
);
1810 case_insensitive_filter (const GtkFileFilterInfo
*filter_info
, gpointer data
)
1812 gboolean bRetval
= false;
1813 const char *pFilter
= static_cast<const char *>(data
);
1815 g_return_val_if_fail( data
!= nullptr, false );
1816 g_return_val_if_fail( filter_info
!= nullptr, false );
1818 if( !filter_info
->uri
)
1821 const char *pExtn
= strrchr( filter_info
->uri
, '.' );
1826 if( !g_ascii_strcasecmp( pFilter
, pExtn
) )
1829 SAL_INFO( "vcl.gtk", "'" << filter_info
->uri
<< "' match extn '" << pExtn
<< "' vs '" << pFilter
<< "' yields " << bRetval
);
1835 GtkFileFilter
* SalGtkFilePicker::implAddFilter( const OUString
& rFilter
, const OUString
& rType
)
1837 GtkFileFilter
*filter
= gtk_file_filter_new();
1839 OUString aShrunkName
= shrinkFilterName( rFilter
);
1840 OString aFilterName
= OUStringToOString( aShrunkName
, RTL_TEXTENCODING_UTF8
);
1841 gtk_file_filter_set_name( filter
, aFilterName
.getStr() );
1843 OUStringBuffer aTokens
;
1845 bool bAllGlob
= rType
== "*.*" || rType
== "*";
1847 gtk_file_filter_add_pattern( filter
, "*" );
1850 sal_Int32 nIndex
= 0;
1853 OUString aToken
= rType
.getToken( 0, ';', nIndex
);
1854 // Assume all have the "*.<extn>" syntax
1855 sal_Int32 nStarDot
= aToken
.lastIndexOf( "*." );
1857 aToken
= aToken
.copy( nStarDot
+ 2 );
1858 if (!aToken
.isEmpty())
1860 if (!aTokens
.isEmpty())
1861 aTokens
.append(",");
1862 aTokens
.append(aToken
);
1863 gtk_file_filter_add_custom (filter
, GTK_FILE_FILTER_URI
,
1864 case_insensitive_filter
,
1865 g_strdup( OUStringToOString(aToken
, RTL_TEXTENCODING_UTF8
).getStr() ),
1868 SAL_INFO( "vcl.gtk", "fustering with " << aToken
);
1870 #if OSL_DEBUG_LEVEL > 0
1873 g_warning( "Duff filter token '%s'\n",
1875 rType
.getToken( 0, ';', nIndex
), RTL_TEXTENCODING_UTF8
).getStr() );
1879 while( nIndex
>= 0 );
1882 gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( m_pDialog
), filter
);
1887 gtk_list_store_append (m_pFilterStore
, &iter
);
1888 gtk_list_store_set (m_pFilterStore
, &iter
,
1889 0, OUStringToOString(shrinkFilterName( rFilter
, true ), RTL_TEXTENCODING_UTF8
).getStr(),
1890 1, OUStringToOString(aTokens
.makeStringAndClear(), RTL_TEXTENCODING_UTF8
).getStr(),
1891 2, aFilterName
.getStr(),
1892 3, OUStringToOString(rType
, RTL_TEXTENCODING_UTF8
).getStr(),
1898 void SalGtkFilePicker::implAddFilterGroup( const OUString
& /*_rFilter*/, const Sequence
< StringPair
>& _rFilters
)
1900 // Gtk+ has no filter group concept I think so ...
1901 // implAddFilter( _rFilter, String() );
1902 for( const auto& rSubFilter
: _rFilters
)
1903 implAddFilter( rSubFilter
.First
, rSubFilter
.Second
);
1906 void SalGtkFilePicker::SetFilters()
1908 if (m_aInitialFilter
.isEmpty())
1909 m_aInitialFilter
= m_aCurrentFilter
;
1911 OUString sPseudoFilter
;
1912 if( GTK_FILE_CHOOSER_ACTION_SAVE
== gtk_file_chooser_get_action( GTK_FILE_CHOOSER( m_pDialog
) ) )
1914 std::set
<OUString
> aAllFormats
;
1915 if( m_pFilterVector
)
1917 for (auto & filter
: *m_pFilterVector
)
1919 if( filter
.hasSubFilters() )
1920 { // it's a filter group
1921 css::uno::Sequence
< css::beans::StringPair
> aSubFilters
;
1922 filter
.getSubFilters( aSubFilters
);
1923 for( const auto& rSubFilter
: std::as_const(aSubFilters
) )
1924 aAllFormats
.insert(rSubFilter
.Second
);
1927 aAllFormats
.insert(filter
.getFilter());
1930 if (aAllFormats
.size() > 1)
1932 OUStringBuffer sAllFilter
;
1933 for (auto const& format
: aAllFormats
)
1935 if (!sAllFilter
.isEmpty())
1936 sAllFilter
.append(";");
1937 sAllFilter
.append(format
);
1939 sPseudoFilter
= getResString(FILE_PICKER_ALLFORMATS
);
1940 m_pPseudoFilter
= implAddFilter( sPseudoFilter
, sAllFilter
.makeStringAndClear() );
1944 if( m_pFilterVector
)
1946 for (auto & filter
: *m_pFilterVector
)
1948 if( filter
.hasSubFilters() )
1949 { // it's a filter group
1951 css::uno::Sequence
< css::beans::StringPair
> aSubFilters
;
1952 filter
.getSubFilters( aSubFilters
);
1954 implAddFilterGroup( filter
.getTitle(), aSubFilters
);
1958 // it's a single filter
1960 implAddFilter( filter
.getTitle(), filter
.getFilter() );
1965 // We always hide the expander now and depend on the user using the glob
1966 // list, or type a filename suffix, to select a filter by inference.
1967 gtk_widget_hide(m_pFilterExpander
);
1969 // set the default filter
1970 if (!sPseudoFilter
.isEmpty())
1971 SetCurFilter( sPseudoFilter
);
1972 else if(!m_aCurrentFilter
.isEmpty())
1973 SetCurFilter( m_aCurrentFilter
);
1975 SAL_INFO( "vcl.gtk", "end setting filters");
1978 SalGtkFilePicker::~SalGtkFilePicker()
1984 for( i
= 0; i
< TOGGLE_LAST
; i
++ )
1985 gtk_widget_destroy( m_pToggles
[i
] );
1987 for( i
= 0; i
< LIST_LAST
; i
++ )
1989 gtk_widget_destroy( m_pListLabels
[i
] );
1990 gtk_widget_destroy( m_pAligns
[i
] ); //m_pAligns[i] owns m_pLists[i]
1991 gtk_widget_destroy( m_pHBoxs
[i
] );
1994 m_pFilterVector
.reset();
1996 gtk_widget_destroy( m_pVBox
);
1999 uno::Reference
< ui::dialogs::XFilePicker2
>
2000 GtkInstance::createFilePicker( const css::uno::Reference
< css::uno::XComponentContext
> &xMSF
)
2002 return uno::Reference
< ui::dialogs::XFilePicker2
>(
2003 new SalGtkFilePicker( xMSF
) );
2006 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */