Chapter 20. Selecting Files

Objective

If you are a Gtk+ veteran, you may be familiar with the feeling of inferiority coming over you when using the Gtk2::FileSelection widget. This widget looked like a typical Tk design and was not very flexible.

Today we are blessed with the more functional and appealing Gtk2::FileChooser interface and the widgets making use of it. They are the Gtk2::FileChooserWidget, Gtk2::FileChooserDialog, and Gtk2::FileChooserButton.

This lesson will show the reader how to utilize these classes.

20.1. A simple program to demonstrate

We create a simple program which shows the two types of Gtk2::FileChooserButtons. It also shows the effect that different values of the 'action' property have on the appearance of the Gtk2::FileChooserDialog.

Table 20-1. Gtk2 object classes used

Gtk2 Class Name
Gtk2::Window
Gtk2::VBox
Gtk2::HBox
Gtk2::Frame
Gtk2::FileChooserButton
Gtk2::FileChooserDialog
Gtk2::Button
Gtk2::MessageDialog

20.1.1. Screenshots.

Figure 20-1. The main program

Figure 20-2. File Chooser - Open

Figure 20-3. File Chooser - Save

20.1.2. The Code.

The program can be found here: 'gtk2_file_chooser.pl'

  1 #! /usr/bin/perl -w
  2 
  3 use strict;
  4 use Gtk2 '-init';
  5 use Glib qw/TRUE FALSE/; 
  6 
  7 #standard window creation, placement, and signal connecting
  8 my $window = Gtk2::Window->new('toplevel');
  9 $window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
 10 $window->set_border_width(5);
 11 $window->set_position('center_always');
 12 
 13 #add and show the vbox
 14 $window->add(&ret_vbox);
 15 $window->show();
 16 
 17 #our main event-loop
 18 Gtk2->main();
 19 
 20 sub ret_vbox {
 21 
 22 my $vbox = Gtk2::VBox->new(FALSE,5);
 23 
 24     #***************************************
 25     #Show the filechooserbuttons (open and select-folder action types)
 26     my $frm_fl_chooser_button = Gtk2::Frame->new('Gtk2::FileChooserButton');
 27     $frm_fl_chooser_button->set_border_width(5);
 28 
 29         my $hbox_fl_chooser_button = Gtk2::HBox->new(FALSE,5);
 30         $hbox_fl_chooser_button->set_border_width(10);
 31 
 32     #Open a file dialog button----->
 33             my $fc_btn_file =Gtk2::FileChooserButton->new ('select a file' , 'open');
 34             $fc_btn_file->set_filename("/etc/passwd");
 35         $hbox_fl_chooser_button->pack_start($fc_btn_file,TRUE,TRUE,6);
 36 
 37     #Open a folder dialog button---->
 38             my $fc_btn_folder =Gtk2::FileChooserButton->new ('select a folder' , 'select-folder');
 39         $hbox_fl_chooser_button->pack_start($fc_btn_folder,TRUE,TRUE,6);
 40 
 41     $frm_fl_chooser_button->add($hbox_fl_chooser_button);
 42 $vbox->pack_start($frm_fl_chooser_button,FALSE,FALSE,6);
 43 
 44     #***************************************
 45     #Show the filechooserdialog action types (open save select-folder create-folder)
 46     my $frm_fl_chooser_dialog = Gtk2::Frame->new('Gtk2::FileChooserDialog Incarnations');
 47 
 48         my $hbox_fl_chooser_dialog = Gtk2::HBox->new(FALSE,5);
 49         $hbox_fl_chooser_dialog->set_border_width(10);
 50 
 51     #Open---->
 52             my $btn_open            = Gtk2::Button ->new('_Open');
 53             $btn_open->signal_connect('clicked' => 
 54                         sub{ show_chooser('File Chooser type open','open',ret_png_filter()) });
 55         $hbox_fl_chooser_dialog->pack_start($btn_open,TRUE,TRUE,6);
 56 
 57     #Save---->
 58             my $btn_save            = Gtk2::Button->new('_Save');
 59             $btn_save->signal_connect('clicked' => 
 60                         sub{ show_chooser('File Chooser type save','save') });
 61         $hbox_fl_chooser_dialog->pack_start($btn_save,TRUE,TRUE,6);
 62 
 63     #Select Folder---->
 64             my $btn_select_folder   = Gtk2::Button->new('S_elect Folder');
 65             $btn_select_folder->signal_connect('clicked' => 
 66                         sub{ show_chooser('File Chooser type select-folder','select-folder') });
 67         $hbox_fl_chooser_dialog->pack_start($btn_select_folder,TRUE,TRUE,6);
 68 
 69     #Create Folder---->
 70             my $btn_create_folder   = Gtk2::Button->new('_Create Folder');
 71             $btn_create_folder->signal_connect('clicked' => 
 72                         sub{ show_chooser('File Chooser type create-folder','create-folder') });
 73         $hbox_fl_chooser_dialog->pack_start($btn_create_folder,TRUE,TRUE,6);
 74 
 75        $frm_fl_chooser_dialog->add($hbox_fl_chooser_dialog);
 76 $vbox->pack_start($frm_fl_chooser_dialog,FALSE,FALSE,6);
 77 
 78 
 79 $vbox->show_all();
 80 return $vbox;
 81 }
 82 
 83 sub show_chooser {
 84 #---------------------------------------------------
 85 #Pops up a standard file chooser--------------------
 86 #Specify a header to be displayed-------------------
 87 #Specify a type depending on your needs-------------
 88 #Optionally add a filter to show only certain files-
 89 #will return a path, if valid----------------------
 90 #---------------------------------------------------
 91 
 92     my($heading,$type,$filter) =@_;
 93 #$type can be:
 94 #* 'open' 
 95 #* 'save' 
 96 #* 'select-folder'
 97 #* 'create-folder' 
 98     my $file_chooser =  Gtk2::FileChooserDialog->new ( 
 99                             $heading,
100                             undef,
101                             $type,
102                             'gtk-cancel' => 'cancel',
103                             'gtk-ok' => 'ok'
104                         );
105     (defined $filter)&&($file_chooser->add_filter($filter));
106     
107     #if action = 'save' suggest a filename
108     ($type eq 'save')&&($file_chooser->set_current_name("suggeste_this_file.name"));
109 
110     my $filename;
111 
112     if ('ok' eq $file_chooser->run){    
113        $filename = $file_chooser->get_filename;
114        print "filename $filename\n";
115     }
116 
117     $file_chooser->destroy;
118 
119     if (defined $filename){
120         if ((-f $filename)&&($type eq 'save')) {
121             my $overwrite =show_message_dialog( $window,
122                                                 'question'
123                                                 ,'Overwrite existing file:'."<b>\n$filename</b>"
124                                                 ,'yes-no'
125                                     );
126             return  if ($overwrite eq 'no');
127         }
128         return $filename;
129     }
130     return;
131 }
132 
133 sub show_message_dialog {
134 #---------------------------------------------------
135 #you tell it what to display, and how to display it
136 #$parent is the parent window, or "undef"
137 #$icon can be one of the following: a) 'info'
138 #                   b) 'warning'
139 #                   c) 'error'
140 #                   d) 'question'
141 #$text can be pango markup text, or just plain text, IE the message
142 #$button_type can be one of the following:  a) 'none'
143 #                       b) 'ok'
144 #                       c) 'close'
145 #                       d) 'cancel'
146 #                       e) 'yes-no'
147 #                       f) 'ok-cancel'
148 #---------------------------------------------------
149 
150 my ($parent,$icon,$text,$button_type) = @_;
151   
152 my $dialog = Gtk2::MessageDialog->new_with_markup ($parent,
153                     [qw/modal destroy-with-parent/],
154                     $icon,
155                     $button_type,
156                     sprintf "$text");
157     my $retval = $dialog->run;
158     $dialog->destroy;
159     return $retval;
160 }
161 
162 
163 sub ret_png_filter {
164 #----------------------------------------
165 #Returns a filter, filtering only png files
166 #----------------------------------------
167 
168     my $filter = Gtk2::FileFilter->new();
169     $filter->set_name("Images");
170     $filter->add_mime_type("image/png");
171     
172     return $filter;
173 }

20.1.3. Points of interest.

  • The following widgets make use of the Gtk2::FileChooser interface: Gtk2::FileChooserDialog, Gtk2::FileChooserButton, Gtk2::FileChooserWidget.

    This means that the methods and properties available to the Gtk2::FileChooser should also be available to these widgets.

  • Depending how the widget makes use of the Gtk2::FileChooser interface, it may enable / disable the use of certain properties. The 'set_current_name' method for instance can't be used if the 'action' property is set to 'open'.

  • The Gtk2::FileChooserButton can be constructed using a few methods. The one we use automatically creates a Gtk2::FileChooserDialog and associates it with the Gtk2::FileChooserButton.

    Note

    Remember: The only actions Gtk2::FileChooserButton can take is 'open' and 'select-folder'.

  • Talking about 'set_current_name'. This is used to suggest a name when saving a file.

  • The Gtk2::FileChooser has a property 'do-overwrite-confirmation'. It seems that setting this to TRUE will ask for confirmation when a user selected a file already in existence. ('action' must also be equal to 'save')

    The tests I've done could not get this working, thus in the sample program, when an existing file is chosen the yes/no question is invoked in code.

  • Rather than creating a Gtk2::FileChooserDialog each time you need one, it is good practice to have a sub that takes certain arguments to manipulate the appearance of the Gtk2::FileChooserDialog and return the value specified.

  • On the Gtk2::FileChooserDialog POD page, you will see the following: * ... (list) list of button-text => response-id pairs. This will typically be an 'ok' and 'cancel' button.

    'gtk-cancel' => 'cancel',
    'gtk-ok' => 'ok'

  • The Gtk2::FileChooserWidget was not used directly, but it is used inside the Gtk2::FileChooserDialog. You may use it on its own, but the Gtk2::FileChooserDialog is just way more convienient.

  • Adding a Gtk2::FileFilter to the Gtk2::FileChooser will let the Gtk2::FileChooser show only those file types we asked for.

    Two methods are generally used to add a filetype to a Gtk2::FileFilter.

    The recommended one is 'add_mime_type'. This will let the filechooser 'look into' a file, thus if you have a .png file without the .png eextension, it will still show when you specified the following:

    $filter->add_mime_type("image/png");

    The other method is 'add_pattern'. This method makes use of wild charts. Typical sample code will look as follows:

    $filter->add_pattern("*.png");
    $filter->add_pattern("*.jpg");
    $filter->add_pattern("*.gif");
    $filter->add_pattern("*.tif");
    $filter->add_pattern("*.xpm");
    $filter->add_pattern("*.bmp");
    Note that they can be stacked!

20.1.4. Conclusion.

With the addition of Gtk2::FileChooser to the newer versions of Gtk+, we finally do not have to be ashamed any more when asking someone to open or save a file in our program. Gtk2::FileChooser was dearly needed and is just as dearly appreciated!