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