loplugin:sequenceloop in unoxml..vcl
[LibreOffice.git] / vcl / unx / gtk / fpicker / SalGtkFilePicker.cxx
blobe9b16217882bd56e8503abd5c223b69517c791c1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
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 .
20 #ifdef AIX
21 #define _LINUX_SOURCE_COMPAT
22 #include <sys/timer.h>
23 #undef _LINUX_SOURCE_COMPAT
24 #endif
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>
52 #include <algorithm>
53 #include <set>
54 #include <string.h>
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()
74 if (!mbPreviewState )
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 ),
86 m_pVBox ( nullptr ),
87 mnHID_FolderChange( 0 ),
88 mnHID_SelectionChange( 0 ),
89 bVersionWidthUnset( false ),
90 mbPreviewState( false ),
91 mHID_Preview( 0 ),
92 m_pPreview( nullptr ),
93 m_pPseudoFilter( nullptr )
95 int i;
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(),
122 nullptr,
123 GTK_FILE_CHOOSER_ACTION_OPEN,
124 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
125 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
126 nullptr );
128 gtk_dialog_set_default_response( GTK_DIALOG (m_pDialog), GTK_RESPONSE_ACCEPT );
130 #if ENABLE_GIO
131 gtk_file_chooser_set_local_only( GTK_FILE_CHOOSER( m_pDialog ), false );
132 #endif
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 );
146 OUString aLabel;
148 for( i = 0; i < TOGGLE_LAST; i++ )
150 m_pToggles[i] = gtk_check_button_new();
152 #define LABEL_TOGGLE( elem ) \
153 case elem : \
154 aLabel = getResString( CHECKBOX_##elem ); \
155 setLabel( CHECKBOX_##elem, aLabel ); \
156 break
158 switch( i ) {
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 );
167 default:
168 SAL_WARN( "vcl.gtk", "Handle unknown control " << i);
169 break;
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 ) \
194 case elem : \
195 aLabel = getResString( LISTBOX_##elem##_LABEL ); \
196 setLabel( LISTBOX_##elem##_LABEL, aLabel ); \
197 break
199 switch( i )
201 LABEL_LIST( VERSION );
202 LABEL_LIST( TEMPLATE );
203 LABEL_LIST( IMAGE_TEMPLATE );
204 LABEL_LIST( IMAGE_ANCHOR );
205 default:
206 SAL_WARN( "vcl.gtk", "Handle unknown control " << i);
207 break;
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),
229 GTK_SHADOW_IN);
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);
273 guint ypad;
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 )
291 SolarMutexGuard g;
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>& )
300 SolarMutexGuard g;
302 m_xListener.clear();
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 );
322 struct FilterEntry
324 protected:
325 OUString m_sTitle;
326 OUString m_sFilter;
328 css::uno::Sequence< css::beans::StringPair > m_aSubFilters;
330 public:
331 FilterEntry( const OUString& _rTitle, const OUString& _rFilter )
332 :m_sTitle( _rTitle )
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;
362 static bool
363 isFilterString( const OUString &rFilterString, const char *pMatch )
365 sal_Int32 nIndex = 0;
366 OUString aToken;
367 bool bIsFilter = true;
369 OUString aMatch(OUString::createFromAscii(pMatch));
373 aToken = rFilterString.getToken( 0, ';', nIndex );
374 if( !aToken.match( aMatch ) )
376 bIsFilter = false;
377 break;
380 while( nIndex >= 0 );
382 return bIsFilter;
385 static OUString
386 shrinkFilterName( const OUString &rFilterName, bool bAllowNoStar = false )
388 int i;
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-- )
396 if( pStr[i] == ')' )
397 nBracketEnd = i;
398 else if( pStr[i] == '(' )
400 nBracketLen = nBracketEnd - i;
401 if( nBracketEnd <= 0 )
402 continue;
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() );
413 return aRealName;
416 static void
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 );
435 static void
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 );
444 else
445 dialog_remove_buttons(gtk_dialog_get_action_area(pDialog));
446 #else
447 dialog_remove_buttons(pDialog->action_area);
448 #endif
451 namespace {
453 struct FilterTitleMatch
455 protected:
456 const OUString& rTitle;
458 public:
459 explicit FilterTitleMatch( const OUString& _rTitle ) : rTitle( _rTitle ) { }
461 bool operator () ( const FilterEntry& _rEntry )
463 bool bMatch;
464 if( !_rEntry.hasSubFilters() )
465 // a real filter
466 bMatch = (_rEntry.getTitle() == rTitle)
467 || (shrinkFilterName(_rEntry.getTitle()) == rTitle);
468 else
469 // a filter group -> search the sub filters
470 bMatch =
471 ::std::any_of(
472 _rEntry.beginSubFilters(),
473 _rEntry.endSubFilters(),
474 *this
477 return bMatch;
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 )
489 bool bRet = false;
491 if( m_pFilterVector )
492 bRet =
493 ::std::any_of(
494 m_pFilterVector->begin(),
495 m_pFilterVector->end(),
496 FilterTitleMatch( rTitle )
499 return bRet;
502 bool SalGtkFilePicker::FilterNameExists( const css::uno::Sequence< css::beans::StringPair >& _rGroupedFilters )
504 bool bRet = false;
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 ) ); });
513 return bRet;
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 )
530 SolarMutexGuard g;
532 OSL_ASSERT( m_pDialog != nullptr );
534 if( FilterNameExists( aTitle ) )
535 throw IllegalArgumentException();
537 // ensure that we have a filter list
538 ensureFilterVector( aTitle );
540 // append the filter
541 m_pFilterVector->insert( m_pFilterVector->end(), FilterEntry( aTitle, aFilter ) );
544 void SAL_CALL SalGtkFilePicker::setCurrentFilter( const OUString& aTitle )
546 SolarMutexGuard g;
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);
562 if (m_pFilterVector)
564 for (auto const& filter : *m_pFilterVector)
566 if (aFilterName == shrinkFilterName(filter.getTitle()))
568 m_aCurrentFilter = filter.getTitle();
569 break;
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)
581 return;
583 GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_pFilterView));
584 GtkTreeIter iter;
585 GtkTreeModel *model;
586 if (gtk_tree_selection_get_selected (selection, &model, &iter))
588 gchar *title;
589 gtk_tree_model_get (model, &iter, 2, &title, -1);
590 updateCurrentFilterFromName(title);
591 g_free (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 ));
597 else
598 updateCurrentFilterFromName(OUStringToOString( m_aInitialFilter, RTL_TEXTENCODING_UTF8 ).getStr());
602 OUString SAL_CALL SalGtkFilePicker::getCurrentFilter()
604 SolarMutexGuard g;
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 )
617 SolarMutexGuard g;
619 OSL_ASSERT( m_pDialog != nullptr );
621 // TODO m_pImpl->appendFilterGroup( sGroupTitle, aFilters );
622 // check the names
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 );
634 // append the filter
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 )
644 SolarMutexGuard g;
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 )
653 SolarMutexGuard g;
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 )
667 SolarMutexGuard g;
669 implsetDisplayDirectory(rDirectory);
672 OUString SAL_CALL SalGtkFilePicker::getDisplayDirectory()
674 SolarMutexGuard g;
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.
689 aFiles.realloc (1);
690 return aFiles;
693 namespace
696 bool lcl_matchFilter( const OUString& rFilter, const OUString& rExt )
698 const sal_Unicode cSep {';'};
699 sal_Int32 nIdx {0};
701 for (;;)
703 const sal_Int32 nBegin = rFilter.indexOf(rExt, nIdx);
705 if (nBegin<0) // not found
706 break;
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)
714 continue;
716 // Check if the found occurrence is an exact match: left side
717 if (nBegin>0 && rFilter[nBegin-1]!=cSep)
718 continue;
720 return true;
723 return false;
728 uno::Sequence<OUString> SAL_CALL SalGtkFilePicker::getSelectedFiles()
730 SolarMutexGuard g;
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 );
737 int nIndex = 0;
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);
745 // Convert to OOo
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));
758 GtkTreeIter iter;
759 GtkTreeModel *model;
760 if (gtk_tree_selection_get_selected (selection, &model, &iter))
762 gchar *title = nullptr;
763 gtk_tree_model_get (model, &iter, 2, &title, -1);
764 if (title)
765 sFilterName = OUString( title, strlen( title ), RTL_TEXTENCODING_UTF8 );
766 else
767 sFilterName = OUString();
768 g_free (title);
770 else
772 if( aSelectedFiles[nIndex].indexOf('.') > 0 )
774 OUString sExtension;
775 nTokenIndex = 0;
777 sExtension = aSelectedFiles[nIndex].getToken( 0, '.', nTokenIndex );
778 while( nTokenIndex >= 0 );
780 if( sExtension.getLength() >= 3 ) // 3 = typical/minimum extension length
782 OUString aNewFilter;
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);
806 #else
807 setCurrentFilter( aNewFilter );
808 #endif
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;
817 if (filtername)
818 sFilterName = OUString(filtername, strlen( filtername ), RTL_TEXTENCODING_UTF8);
819 else
820 sFilterName.clear();
822 else
823 sFilterName = m_aInitialFilter;
826 if (m_pFilterVector)
828 auto aVectorIter = ::std::find_if(
829 m_pFilterVector->begin(), m_pFilterVector->end(), FilterTitleMatch(sFilterName) );
831 OUString aFilter;
832 if (aVectorIter != m_pFilterVector->end())
833 aFilter = aVectorIter->getFilter();
835 nTokenIndex = 0;
836 OUString sToken;
839 sToken = aFilter.getToken( 0, '.', nTokenIndex );
841 if ( sToken.lastIndexOf( ';' ) != -1 )
843 sToken = sToken.getToken(0, ';');
844 break;
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();
855 SAL_INFO(
856 "vcl.gtk",
857 "idx are " << rBase.lastIndexOf(sExtension) << " "
858 << nExtensionIdx);
860 if( rBase.lastIndexOf( sExtension ) != nExtensionIdx )
861 rBase += sExtension;
867 nIndex++;
868 g_free( pURI );
871 g_slist_free( pPathList );
873 return aSelectedFiles;
876 // XExecutableDialog functions
878 void SAL_CALL SalGtkFilePicker::setTitle( const OUString& rTitle )
880 SolarMutexGuard g;
882 implsetTitle(rTitle);
885 sal_Int16 SAL_CALL SalGtkFilePicker::execute()
887 SolarMutexGuard g;
889 OSL_ASSERT( m_pDialog != nullptr );
891 sal_Int16 retVal = 0;
893 SetFilters();
895 // tdf#84431 - set the filter after the corresponding widget is created
896 if ( !m_aCurrentFilter.isEmpty() )
897 SetCurFilter(m_aCurrentFilter);
899 mnHID_FolderChange =
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),
911 UNO_QUERY_THROW );
913 uno::Reference< frame::XDesktop > xDesktop(
914 frame::Desktop::create(m_xContext),
915 UNO_QUERY_THROW );
917 GtkWindow *pParent = GTK_WINDOW(m_pParentWidget);
918 if (!pParent)
920 SAL_WARN( "vcl.gtk", "no parent widget set");
921 pParent = RunDialog::GetTransientFor();
923 if (pParent)
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();
932 switch( nStatus )
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) );
946 OString baseName(
947 OUStringToOString(
948 aFileObj.getName(
949 INetURLObject::LAST_SEGMENT,
950 true,
951 INetURLObject::DecodeMechanism::WithCharset
953 RTL_TEXTENCODING_UTF8
956 OString aMsg(
957 OUStringToOString(
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(),
967 baseName
970 GtkWidget *dlg = gtk_message_dialog_new( nullptr,
971 GTK_DIALOG_MODAL,
972 GTK_MESSAGE_QUESTION,
973 GTK_BUTTONS_YES_NO,
974 "%s",
975 aMsg.getStr()
978 sal_Int32 nSegmentCount = aFileObj.getSegmentCount();
979 if (nSegmentCount >= 2)
981 OString dirName(
982 OUStringToOString(
983 aFileObj.getName(
984 nSegmentCount-2,
985 true,
986 INetURLObject::DecodeMechanism::WithCharset
988 RTL_TEXTENCODING_UTF8
992 aMsg =
993 OUStringToOString(
994 getResString( FILE_PICKER_OVERWRITE_SECONDARY ),
995 RTL_TEXTENCODING_UTF8
998 toReplace = "$dirname$";
1000 aMsg = aMsg.replaceAt(
1001 aMsg.indexOf( toReplace ),
1002 toReplace.getLength(),
1003 dirName
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));
1014 #else
1015 if (pParent)
1016 gtk_window_set_transient_for(GTK_WINDOW(dlg), pParent);
1017 #endif
1018 RunDialog* pAnotherDialog = new RunDialog(dlg, xToolkit, xDesktop);
1019 uno::Reference < awt::XTopWindowListener > xAnotherLifeCycle(pAnotherDialog);
1020 btn = pAnotherDialog->run();
1022 gtk_widget_destroy( dlg );
1024 g_free (gFileName);
1026 if( btn == GTK_RESPONSE_YES )
1027 retVal = ExecutableDialogResults::OK;
1030 else
1031 retVal = ExecutableDialogResults::OK;
1032 break;
1034 case GTK_RESPONSE_CANCEL:
1035 retVal = ExecutableDialogResults::CANCEL;
1036 break;
1038 case 1: //PLAY
1040 FilePickerEvent evt;
1041 evt.ElementId = PUSHBUTTON_PLAY;
1042 impl_controlStateChanged( evt );
1043 btn = GTK_RESPONSE_NO;
1045 break;
1047 default:
1048 retVal = 0;
1049 break;
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);
1059 return retVal;
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; \
1071 break
1072 #define MAP_BUTTON( elem ) \
1073 case ExtendedFilePickerElementIds::PUSHBUTTON_##elem: \
1074 pWidget = m_pButtons[elem]; tType = GTK_TYPE_BUTTON; \
1075 break
1076 #define MAP_LIST( elem ) \
1077 case ExtendedFilePickerElementIds::LISTBOX_##elem: \
1078 pWidget = m_pLists[elem]; tType = GTK_TYPE_COMBO_BOX; \
1079 break
1080 #define MAP_LIST_LABEL( elem ) \
1081 case ExtendedFilePickerElementIds::LISTBOX_##elem##_LABEL: \
1082 pWidget = m_pListLabels[elem]; tType = GTK_TYPE_LABEL; \
1083 break
1085 switch( nControlId )
1087 MAP_TOGGLE( AUTOEXTENSION );
1088 MAP_TOGGLE( PASSWORD );
1089 MAP_TOGGLE( GPGENCRYPTION );
1090 MAP_TOGGLE( FILTEROPTIONS );
1091 MAP_TOGGLE( READONLY );
1092 MAP_TOGGLE( LINK );
1093 MAP_TOGGLE( PREVIEW );
1094 MAP_TOGGLE( SELECTION );
1095 MAP_BUTTON( PLAY );
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 );
1104 default:
1105 SAL_WARN( "vcl.gtk", "Handle unknown control " << nControlId);
1106 break;
1108 #undef MAP
1110 if( pType )
1111 *pType = tType;
1112 return pWidget;
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)
1126 GtkTreeIter aIter;
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:
1139 OUString sItem;
1140 rValue >>= sItem;
1141 ComboBoxAppendText(pWidget, sItem);
1142 if (!bVersionWidthUnset)
1144 HackWidthToFirst(pWidget);
1145 bVersionWidthUnset = true;
1148 break;
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;
1163 break;
1164 case ControlActions::DELETE_ITEM:
1166 sal_Int32 nPos=0;
1167 rValue >>= nPos;
1169 GtkTreeIter aIter;
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);
1175 break;
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);
1183 break;
1184 case ControlActions::SET_SELECT_ITEM:
1186 sal_Int32 nPos=0;
1187 rValue >>= nPos;
1188 gtk_combo_box_set_active(pWidget, nPos);
1190 break;
1191 default:
1192 SAL_WARN( "vcl.gtk", "undocumented/unimplemented ControlAction for a list " << nControlAction);
1193 break;
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)
1205 uno::Any aAny;
1206 switch (nControlAction)
1208 case ControlActions::GET_ITEMS:
1210 Sequence< OUString > aItemList;
1212 GtkTreeModel *pTree = gtk_combo_box_get_model(pWidget);
1213 GtkTreeIter iter;
1214 if (gtk_tree_model_get_iter_first(pTree, &iter))
1216 sal_Int32 nSize = gtk_tree_model_iter_n_children(
1217 pTree, nullptr);
1219 aItemList.realloc(nSize);
1220 for (sal_Int32 i=0; i < nSize; ++i)
1222 gchar *item;
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);
1226 g_free(item);
1227 (void)gtk_tree_model_iter_next(pTree, &iter);
1230 aAny <<= aItemList;
1232 break;
1233 case ControlActions::GET_SELECTED_ITEM:
1235 GtkTreeIter iter;
1236 if (gtk_combo_box_get_active_iter(pWidget, &iter))
1238 gchar *item;
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);
1242 aAny <<= sItem;
1243 g_free(item);
1246 break;
1247 case ControlActions::GET_SELECTED_ITEM_INDEX:
1249 gint nActive = gtk_combo_box_get_active(pWidget);
1250 aAny <<= static_cast< sal_Int32 >(nActive);
1252 break;
1253 default:
1254 SAL_WARN( "vcl.gtk", "undocumented/unimplemented ControlAction for a list " << nControlAction);
1255 break;
1257 return aAny;
1260 void SAL_CALL SalGtkFilePicker::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
1262 SolarMutexGuard g;
1264 OSL_ASSERT( m_pDialog != nullptr );
1266 GType tType;
1267 GtkWidget *pWidget;
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);
1279 else
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 )
1287 SolarMutexGuard g;
1289 OSL_ASSERT( m_pDialog != nullptr );
1291 uno::Any aRetval;
1293 GType tType;
1294 GtkWidget *pWidget;
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);
1302 else
1303 SAL_WARN( "vcl.gtk", "Can't get value on button / list " << nControlId << " " << nControlAction );
1305 return aRetval;
1308 void SAL_CALL SalGtkFilePicker::enableControl( sal_Int16 nControlId, sal_Bool bEnable )
1310 SolarMutexGuard g;
1312 OSL_ASSERT( m_pDialog != nullptr );
1314 GtkWidget *pWidget;
1316 if ( ( pWidget = getWidget( nControlId ) ) )
1318 if( bEnable )
1320 gtk_widget_set_sensitive( pWidget, true );
1322 else
1324 gtk_widget_set_sensitive( pWidget, false );
1327 else
1328 SAL_WARN( "vcl.gtk", "enable unknown control " << nControlId );
1331 void SAL_CALL SalGtkFilePicker::setLabel( sal_Int16 nControlId, const OUString& rLabel )
1333 SolarMutexGuard g;
1335 OSL_ASSERT( m_pDialog != nullptr );
1337 GType tType;
1338 GtkWidget *pWidget;
1340 if( !( pWidget = getWidget( nControlId, &tType ) ) )
1342 SAL_WARN( "vcl.gtk", "Set label on unknown control " << nControlId);
1343 return;
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);
1354 else
1355 gtk_button_set_label(GTK_BUTTON(pWidget), GTK_STOCK_MEDIA_STOP);
1356 #else
1357 gtk_button_set_label(GTK_BUTTON(pWidget), aTxt.getStr());
1358 #endif
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 );
1363 else
1364 SAL_WARN( "vcl.gtk", "Can't set label on list");
1367 OUString SAL_CALL SalGtkFilePicker::getLabel( sal_Int16 nControlId )
1369 SolarMutexGuard g;
1371 OSL_ASSERT( m_pDialog != nullptr );
1373 GType tType;
1374 OString aTxt;
1375 GtkWidget *pWidget;
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 ) );
1381 else
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()
1391 SolarMutexGuard g;
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()
1401 SolarMutexGuard g;
1403 OSL_ASSERT( m_pDialog != nullptr );
1405 // TODO return m_pImpl->getTargetColorDepth();
1406 return 0;
1409 sal_Int32 SAL_CALL SalGtkFilePicker::getAvailableWidth()
1411 SolarMutexGuard g;
1413 OSL_ASSERT( m_pDialog != nullptr );
1415 return g_PreviewImageWidth;
1418 sal_Int32 SAL_CALL SalGtkFilePicker::getAvailableHeight()
1420 SolarMutexGuard g;
1422 OSL_ASSERT( m_pDialog != nullptr );
1424 return g_PreviewImageHeight;
1427 void SAL_CALL SalGtkFilePicker::setImage( sal_Int16 /*aImageFormat*/, const uno::Any& /*aImage*/ )
1429 SolarMutexGuard g;
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 );
1440 GtkTreeIter iter;
1441 GtkTreeModel *model;
1442 if (gtk_tree_selection_get_selected (selection, &model, &iter))
1444 gchar *title;
1445 gtk_tree_model_get (model, &iter, 2, &title, -1);
1446 aLabel += ": ";
1447 aLabel += OUString( title, strlen(title), RTL_TEXTENCODING_UTF8 );
1448 g_free (title);
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(
1506 filename,
1507 g_PreviewImageWidth,
1508 g_PreviewImageHeight, nullptr );
1510 have_preview = ( pixbuf != nullptr );
1512 gtk_image_set_from_pixbuf( GTK_IMAGE( preview ), pixbuf );
1513 if( pixbuf )
1514 g_object_unref( G_OBJECT( pixbuf ) );
1518 gtk_file_chooser_set_preview_widget_active( file_chooser, have_preview );
1520 if( filename )
1521 g_free( filename );
1524 sal_Bool SAL_CALL SalGtkFilePicker::setShowState( sal_Bool bShowState )
1526 SolarMutexGuard g;
1528 OSL_ASSERT( m_pDialog != nullptr );
1530 // TODO return m_pImpl->setShowState( bShowState );
1531 if( bool(bShowState) != mbPreviewState )
1533 if( bShowState )
1535 // Show
1536 if( !mHID_Preview )
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 );
1544 else
1546 // Hide
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;
1555 return true;
1558 sal_Bool SAL_CALL SalGtkFilePicker::getShowState()
1560 SolarMutexGuard g;
1562 OSL_ASSERT( m_pDialog != nullptr );
1564 return mbPreviewState;
1567 // XInitialization
1569 void SAL_CALL SalGtkFilePicker::initialize( const uno::Sequence<uno::Any>& aArguments )
1571 // parameter checking
1572 uno::Any aAny;
1573 if( !aArguments.hasElements() )
1574 throw lang::IllegalArgumentException(
1575 "no arguments",
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();
1599 else
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;
1608 aAny >>= 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;
1617 SolarMutexGuard g;
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;
1626 break;
1627 case FILESAVE_SIMPLE:
1628 eAction = GTK_FILE_CHOOSER_ACTION_SAVE;
1629 first_button_text = GTK_STOCK_SAVE;
1630 break;
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;
1636 // TODO
1637 break;
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;
1644 // TODO
1645 break;
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;
1650 // TODO
1651 break;
1652 case FILESAVE_AUTOEXTENSION_TEMPLATE:
1653 eAction = GTK_FILE_CHOOSER_ACTION_SAVE;
1654 first_button_text = GTK_STOCK_SAVE;
1655 mbListVisibility[TEMPLATE] = true;
1656 // TODO
1657 break;
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;
1664 // TODO
1665 break;
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;
1672 // TODO
1673 break;
1674 case FILEOPEN_PLAY:
1675 eAction = GTK_FILE_CHOOSER_ACTION_OPEN;
1676 first_button_text = GTK_STOCK_OPEN;
1677 mbButtonVisibility[PLAY] = true;
1678 // TODO
1679 break;
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;
1685 // TODO
1686 break;
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;
1692 break;
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;
1698 // TODO
1699 break;
1700 case FILESAVE_AUTOEXTENSION:
1701 eAction = GTK_FILE_CHOOSER_ACTION_SAVE;
1702 first_button_text = GTK_STOCK_SAVE;
1703 // TODO
1704 break;
1705 case FILEOPEN_PREVIEW:
1706 eAction = GTK_FILE_CHOOSER_ACTION_OPEN;
1707 first_button_text = GTK_STOCK_OPEN;
1708 mbToggleVisibility[PREVIEW] = true;
1709 // TODO
1710 break;
1711 default:
1712 throw lang::IllegalArgumentException(
1713 "Unknown template",
1714 static_cast< XFilePicker2* >( this ),
1715 1 );
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 );
1734 #else
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 );
1737 #endif
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 ) ) );
1770 // XCancellable
1772 void SAL_CALL SalGtkFilePicker::cancel()
1774 SolarMutexGuard g;
1776 OSL_ASSERT( m_pDialog != nullptr );
1778 // TODO m_pImpl->cancel();
1781 // Misc
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 );
1800 bFound = true;
1804 g_slist_free( filters );
1807 extern "C"
1809 static gboolean
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 )
1819 return false;
1821 const char *pExtn = strrchr( filter_info->uri, '.' );
1822 if( !pExtn )
1823 return false;
1824 pExtn++;
1826 if( !g_ascii_strcasecmp( pFilter, pExtn ) )
1827 bRetval = true;
1829 SAL_INFO( "vcl.gtk", "'" << filter_info->uri << "' match extn '" << pExtn << "' vs '" << pFilter << "' yields " << bRetval );
1831 return 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 == "*";
1846 if (bAllGlob)
1847 gtk_file_filter_add_pattern( filter, "*" );
1848 else
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( "*." );
1856 if (nStarDot >= 0)
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() ),
1866 g_free );
1868 SAL_INFO( "vcl.gtk", "fustering with " << aToken );
1870 #if OSL_DEBUG_LEVEL > 0
1871 else
1873 g_warning( "Duff filter token '%s'\n",
1874 OUStringToOString(
1875 rType.getToken( 0, ';', nIndex ), RTL_TEXTENCODING_UTF8 ).getStr() );
1877 #endif
1879 while( nIndex >= 0 );
1882 gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( m_pDialog ), filter );
1884 if (!bAllGlob)
1886 GtkTreeIter iter;
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(),
1893 -1);
1895 return filter;
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);
1926 else
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 );
1956 else
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()
1980 SolarMutexGuard g;
1982 int i;
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: */