1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/filectrl.cpp
3 // Purpose:     wxGtkFileCtrl Implementation
4 // Author:      Diaa M. Sami
5 // Created:     2007-08-10
6 // Copyright:   (c) Diaa M. Sami
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 #include "wx/wxprec.h"
11 
12 #if wxUSE_FILECTRL && !defined(__WXUNIVERSAL__)
13 
14 #include "wx/filectrl.h"
15 
16 #include "wx/gtk/private.h"
17 #include "wx/filename.h"
18 #include "wx/scopeguard.h"
19 #include "wx/tokenzr.h"
20 
21 //-----------------------------------------------------------------------------
22 // wxGtkFileChooser implementation
23 //-----------------------------------------------------------------------------
24 
SetWidget(GtkFileChooser * w)25 void wxGtkFileChooser::SetWidget(GtkFileChooser *w)
26 {
27     // check arguments
28     wxASSERT( w );
29     wxASSERT( GTK_FILE_CHOOSER( w ) );
30 
31     this->m_widget = w;
32 }
33 
GetPath() const34 wxString wxGtkFileChooser::GetPath() const
35 {
36     wxGtkString str( gtk_file_chooser_get_filename( m_widget ) );
37 
38     wxString string;
39     if (str)
40         string = wxString::FromUTF8(str);
41     return string;
42 }
43 
GetFilenames(wxArrayString & files) const44 void wxGtkFileChooser::GetFilenames( wxArrayString& files ) const
45 {
46     GetPaths( files );
47     for ( size_t n = 0; n < files.GetCount(); ++n )
48     {
49         const wxFileName file( files[n] );
50         files[n] = file.GetFullName();
51     }
52 }
53 
GetPaths(wxArrayString & paths) const54 void wxGtkFileChooser::GetPaths( wxArrayString& paths ) const
55 {
56     paths.Empty();
57     if ( gtk_file_chooser_get_select_multiple( m_widget ) )
58     {
59         GSList *gpathsi = gtk_file_chooser_get_filenames( m_widget );
60         GSList *gpaths = gpathsi;
61         while ( gpathsi )
62         {
63             wxString file(wxString::FromUTF8(static_cast<gchar *>(gpathsi->data)));
64             paths.Add( file );
65             g_free( gpathsi->data );
66             gpathsi = gpathsi->next;
67         }
68 
69         g_slist_free( gpaths );
70     }
71     else
72         paths.Add( GetPath() );
73 }
74 
SetPath(const wxString & path)75 bool wxGtkFileChooser::SetPath( const wxString& path )
76 {
77     if ( path.empty() )
78         return true;
79 
80     switch ( gtk_file_chooser_get_action( m_widget ) )
81     {
82         case GTK_FILE_CHOOSER_ACTION_SAVE:
83             {
84                 wxFileName fn(path);
85 
86                 const wxString fname = fn.GetFullName();
87                 gtk_file_chooser_set_current_name( m_widget, fname.utf8_str() );
88 
89                 // set the initial file name and/or directory
90                 const wxString dir = fn.GetPath();
91                 return gtk_file_chooser_set_current_folder( m_widget,
92                                                             dir.utf8_str() ) != 0;
93             }
94 
95         case GTK_FILE_CHOOSER_ACTION_OPEN:
96             return gtk_file_chooser_set_filename( m_widget, path.utf8_str() ) != 0;
97 
98         case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
99         case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
100             break;
101     }
102 
103     wxFAIL_MSG( "Unexpected file chooser type" );
104 
105     return false;
106 }
107 
SetDirectory(const wxString & dir)108 bool wxGtkFileChooser::SetDirectory( const wxString& dir )
109 {
110     return gtk_file_chooser_set_current_folder( m_widget, dir.utf8_str() ) != 0;
111 }
112 
GetDirectory() const113 wxString wxGtkFileChooser::GetDirectory() const
114 {
115     const wxGtkString str( gtk_file_chooser_get_current_folder( m_widget ) );
116     return wxString::FromUTF8(str);
117 }
118 
GetFilename() const119 wxString wxGtkFileChooser::GetFilename() const
120 {
121     return wxFileName( GetPath() ).GetFullName();
122 }
123 
SetWildcard(const wxString & wildCard)124 void wxGtkFileChooser::SetWildcard( const wxString& wildCard )
125 {
126     m_wildcards.Empty();
127 
128     // parse filters
129     wxArrayString wildDescriptions, wildFilters;
130 
131     if ( !wxParseCommonDialogsFilter( wildCard, wildDescriptions, wildFilters ) )
132     {
133         wxFAIL_MSG( wxT( "wxGtkFileChooser::SetWildcard - bad wildcard string" ) );
134     }
135     else
136     {
137         // Parsing went fine. Set m_wildCard to be returned by wxGtkFileChooserBase::GetWildcard
138         GtkFileChooser* chooser = m_widget;
139 
140         // empty current filter list:
141         GSList* ifilters = gtk_file_chooser_list_filters( chooser );
142         GSList* filters = ifilters;
143 
144         m_ignoreNextFilterEvent = true;
145         wxON_BLOCK_EXIT_SET(m_ignoreNextFilterEvent, false);
146 
147         while ( ifilters )
148         {
149             gtk_file_chooser_remove_filter( chooser, GTK_FILE_FILTER( ifilters->data ) );
150             ifilters = ifilters->next;
151         }
152         g_slist_free( filters );
153 
154         if (!wildCard.empty())
155         {
156             // add parsed to GtkChooser
157             for ( size_t n = 0; n < wildFilters.GetCount(); ++n )
158             {
159                 GtkFileFilter* filter = gtk_file_filter_new();
160 
161                 gtk_file_filter_set_name( filter, wxGTK_CONV_SYS( wildDescriptions[n] ) );
162 
163                 wxStringTokenizer exttok( wildFilters[n], wxT( ";" ) );
164 
165                 int n1 = 1;
166                 while ( exttok.HasMoreTokens() )
167                 {
168                     wxString token = exttok.GetNextToken();
169                     gtk_file_filter_add_pattern( filter, wxGTK_CONV_SYS( token ) );
170 
171                     if (n1 == 1)
172                         m_wildcards.Add( token ); // Only add first pattern to list, used later when saving
173                     n1++;
174                 }
175 
176                 gtk_file_chooser_add_filter( chooser, filter );
177             }
178 
179             // Reset the filter index
180             SetFilterIndex( 0 );
181         }
182     }
183 }
184 
SetFilterIndex(int filterIndex)185 void wxGtkFileChooser::SetFilterIndex( int filterIndex )
186 {
187     gpointer filter;
188     GtkFileChooser *chooser = m_widget;
189     GSList *filters = gtk_file_chooser_list_filters( chooser );
190 
191     filter = g_slist_nth_data( filters, filterIndex );
192 
193     if ( filter != NULL )
194     {
195         gtk_file_chooser_set_filter( chooser, GTK_FILE_FILTER( filter ) );
196     }
197     else
198     {
199         wxFAIL_MSG( wxT( "wxGtkFileChooser::SetFilterIndex - bad filter index" ) );
200     }
201 
202     g_slist_free( filters );
203 }
204 
GetFilterIndex() const205 int wxGtkFileChooser::GetFilterIndex() const
206 {
207     GtkFileChooser *chooser = m_widget;
208     GtkFileFilter *filter = gtk_file_chooser_get_filter( chooser );
209     GSList *filters = gtk_file_chooser_list_filters( chooser );
210     const gint index = g_slist_index( filters, filter );
211     g_slist_free( filters );
212 
213     if ( index == -1 )
214     {
215         wxFAIL_MSG( wxT( "wxGtkFileChooser::GetFilterIndex - bad filter index returned by gtk+" ) );
216         return 0;
217     }
218     else
219         return index;
220 }
221 
HasFilterChoice() const222 bool wxGtkFileChooser::HasFilterChoice() const
223 {
224     return gtk_file_chooser_get_filter( m_widget ) != NULL;
225 }
226 
227 //-----------------------------------------------------------------------------
228 // end wxGtkFileChooser Implementation
229 //-----------------------------------------------------------------------------
230 
231 #if wxUSE_FILECTRL
232 
233 // gtk signal handlers
234 
235 extern "C"
236 {
237     static void
gtkfilechooserwidget_file_activated_callback(GtkWidget * WXUNUSED (widget),wxGtkFileCtrl * fileCtrl)238     gtkfilechooserwidget_file_activated_callback( GtkWidget *WXUNUSED( widget ), wxGtkFileCtrl *fileCtrl )
239     {
240         wxGenerateFileActivatedEvent( fileCtrl, fileCtrl );
241     }
242 }
243 
244 extern "C"
245 {
246     static void
gtkfilechooserwidget_selection_changed_callback(GtkWidget * WXUNUSED (widget),wxGtkFileCtrl * fileCtrl)247     gtkfilechooserwidget_selection_changed_callback( GtkWidget *WXUNUSED( widget ), wxGtkFileCtrl *fileCtrl )
248     {
249         // check next selection event and ignore it if it has 0 files
250         // because such events are redundantly generated by gtk.
251         if ( fileCtrl->m_checkNextSelEvent )
252         {
253             wxArrayString filenames;
254             fileCtrl->GetFilenames( filenames );
255 
256             if ( filenames.Count() != 0 )
257                 fileCtrl->m_checkNextSelEvent = false;
258         }
259 
260         if ( !fileCtrl->m_checkNextSelEvent )
261             wxGenerateSelectionChangedEvent( fileCtrl, fileCtrl );
262     }
263 }
264 
265 extern "C"
266 {
267     static void
gtkfilechooserwidget_folder_changed_callback(GtkWidget * WXUNUSED (widget),wxGtkFileCtrl * fileCtrl)268     gtkfilechooserwidget_folder_changed_callback( GtkWidget *WXUNUSED( widget ), wxGtkFileCtrl *fileCtrl )
269     {
270         if ( fileCtrl->m_ignoreNextFolderChangeEvent )
271         {
272             fileCtrl->m_ignoreNextFolderChangeEvent = false;
273         }
274         else
275         {
276             wxGenerateFolderChangedEvent( fileCtrl, fileCtrl );
277         }
278 
279         fileCtrl->m_checkNextSelEvent = true;
280     }
281 }
282 
283 extern "C"
284 {
285     static void
gtkfilechooserwidget_notify_callback(GObject * WXUNUSED (gobject),GParamSpec * arg1,wxGtkFileCtrl * fileCtrl)286     gtkfilechooserwidget_notify_callback( GObject *WXUNUSED( gobject ), GParamSpec *arg1, wxGtkFileCtrl *fileCtrl )
287     {
288         const char *name = g_param_spec_get_name (arg1);
289         if ( strcmp( name, "filter" ) == 0 &&
290              fileCtrl->HasFilterChoice() &&
291              !fileCtrl->GTKShouldIgnoreNextFilterEvent() )
292         {
293             wxGenerateFilterChangedEvent( fileCtrl, fileCtrl );
294         }
295     }
296 }
297 
298 // wxGtkFileCtrl implementation
299 
300 wxIMPLEMENT_DYNAMIC_CLASS(wxGtkFileCtrl, wxControl);
301 
~wxGtkFileCtrl()302 wxGtkFileCtrl::~wxGtkFileCtrl()
303 {
304     if (m_fcWidget)
305         GTKDisconnect(m_fcWidget);
306 }
307 
Init()308 void wxGtkFileCtrl::Init()
309 {
310     m_checkNextSelEvent = false;
311 
312     // ignore the first folder change event which is fired upon startup.
313     m_ignoreNextFolderChangeEvent = true;
314 }
315 
Create(wxWindow * parent,wxWindowID id,const wxString & defaultDirectory,const wxString & defaultFileName,const wxString & wildCard,long style,const wxPoint & pos,const wxSize & size,const wxString & name)316 bool wxGtkFileCtrl::Create( wxWindow *parent,
317                             wxWindowID id,
318                             const wxString& defaultDirectory,
319                             const wxString& defaultFileName,
320                             const wxString& wildCard,
321                             long style,
322                             const wxPoint& pos,
323                             const wxSize& size,
324                             const wxString& name )
325 {
326     if ( !PreCreation( parent, pos, size ) ||
327             !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ) )
328     {
329         wxFAIL_MSG( wxT( "wxGtkFileCtrl creation failed" ) );
330         return false;
331     }
332 
333     GtkFileChooserAction gtkAction = GTK_FILE_CHOOSER_ACTION_OPEN;
334 
335     if ( style & wxFC_SAVE )
336         gtkAction = GTK_FILE_CHOOSER_ACTION_SAVE;
337 
338     m_fcWidget = GTK_FILE_CHOOSER( gtk_file_chooser_widget_new(gtkAction) );
339     m_widget = GTK_WIDGET(m_fcWidget);
340     g_object_ref(m_widget);
341 
342     m_focusWidget = GTK_WIDGET( m_fcWidget );
343 
344     g_signal_connect ( m_fcWidget, "file-activated",
345                        G_CALLBACK ( gtkfilechooserwidget_file_activated_callback ),
346                        this );
347 
348     g_signal_connect ( m_fcWidget, "current-folder-changed",
349                        G_CALLBACK ( gtkfilechooserwidget_folder_changed_callback ),
350                        this );
351 
352     g_signal_connect ( m_fcWidget, "selection-changed",
353                        G_CALLBACK ( gtkfilechooserwidget_selection_changed_callback ),
354                        this );
355 
356     g_signal_connect ( m_fcWidget, "notify",
357                        G_CALLBACK ( gtkfilechooserwidget_notify_callback ),
358                        this );
359 
360     m_fc.SetWidget( m_fcWidget );
361 
362     if ( style & wxFC_MULTIPLE )
363         gtk_file_chooser_set_select_multiple( m_fcWidget, true );
364 
365     SetWildcard( wildCard );
366 
367     // if defaultDir is specified it should contain the directory and
368     // defaultFileName should contain the default name of the file, however if
369     // directory is not given, defaultFileName contains both
370     wxFileName fn;
371     if ( defaultDirectory.empty() )
372         fn.Assign( defaultFileName );
373     else if ( !defaultFileName.empty() )
374         fn.Assign( defaultDirectory, defaultFileName );
375     else
376         fn.AssignDir( defaultDirectory );
377 
378     // set the initial file name and/or directory
379     const wxString dir = fn.GetPath();
380     if ( !dir.empty() )
381     {
382         gtk_file_chooser_set_current_folder( m_fcWidget,
383                                              wxGTK_CONV_FN(dir) );
384     }
385 
386     const wxString fname = fn.GetFullName();
387     if ( style & wxFC_SAVE )
388     {
389         if ( !fname.empty() )
390         {
391             gtk_file_chooser_set_current_name( m_fcWidget,
392                                                wxGTK_CONV_FN(fname) );
393         }
394     }
395     else // wxFC_OPEN
396     {
397         if ( !fname.empty() )
398         {
399             gtk_file_chooser_set_filename( m_fcWidget,
400                                            wxGTK_CONV_FN(fn.GetFullPath()) );
401         }
402     }
403 
404     m_parent->DoAddChild( this );
405 
406     PostCreation( size );
407 
408     return TRUE;
409 }
410 
SetPath(const wxString & path)411 bool wxGtkFileCtrl::SetPath( const wxString& path )
412 {
413     return m_fc.SetPath( path );
414 }
415 
SetDirectory(const wxString & dir)416 bool wxGtkFileCtrl::SetDirectory( const wxString& dir )
417 {
418     return m_fc.SetDirectory( dir );
419 }
420 
SetFilename(const wxString & name)421 bool wxGtkFileCtrl::SetFilename( const wxString& name )
422 {
423     if ( HasFlag( wxFC_SAVE ) )
424     {
425         gtk_file_chooser_set_current_name( m_fcWidget, wxGTK_CONV( name ) );
426         return true;
427     }
428     else
429         return SetPath( wxFileName( GetDirectory(), name ).GetFullPath() );
430 }
431 
SetWildcard(const wxString & wildCard)432 void wxGtkFileCtrl::SetWildcard( const wxString& wildCard )
433 {
434     m_wildCard = wildCard;
435 
436     m_fc.SetWildcard( wildCard );
437 }
438 
SetFilterIndex(int filterIndex)439 void wxGtkFileCtrl::SetFilterIndex( int filterIndex )
440 {
441     m_fc.SetFilterIndex( filterIndex );
442 }
443 
GetPath() const444 wxString wxGtkFileCtrl::GetPath() const
445 {
446     return m_fc.GetPath();
447 }
448 
GetPaths(wxArrayString & paths) const449 void wxGtkFileCtrl::GetPaths( wxArrayString& paths ) const
450 {
451     m_fc.GetPaths( paths );
452 }
453 
GetDirectory() const454 wxString wxGtkFileCtrl::GetDirectory() const
455 {
456     return m_fc.GetDirectory();
457 }
458 
GetFilename() const459 wxString wxGtkFileCtrl::GetFilename() const
460 {
461     return m_fc.GetFilename();
462 }
463 
GetFilenames(wxArrayString & files) const464 void wxGtkFileCtrl::GetFilenames( wxArrayString& files ) const
465 {
466     m_fc.GetFilenames( files );
467 }
468 
ShowHidden(bool show)469 void wxGtkFileCtrl::ShowHidden(bool show)
470 {
471     gtk_file_chooser_set_show_hidden(m_fcWidget, show);
472 }
473 
474 #endif // wxUSE_FILECTRL
475 
476 #endif // wxUSE_FILECTRL && !defined(__WXUNIVERSAL__)
477