Search code examples
cgtk3filechooser

GTK+3 file chooser in a non-GTK application


I have a C project (on Linux) which does not use GTK at all, but I would like to use GTK only for some specific tasks like selecting a file (file chooser dialog). So I have no GTK parent window, no gtk main loop, etc, I only want a file chooser dialog, which should block the execution of my program till user selected a file (or cancelled) and I don't use GTK then after that ever. What I've tried:

https://developer.gnome.org/gtk3/stable/GtkFileChooserDialog.html

I used the code at "Typical usage", first example. I put gtk_init(&argc, &argv) at the start of my program, and when I need the file chooser, I call a function with code from that example (I use parent as NULL, since there is no parent). The result is a flashing window for a fraction of second, then SIGSEGV. Before that I have this message:

Gtk-Message: GtkDialog mapped without a transient parent. This is discouraged.

I already read questions/answers on this message here at stackoverflow, but crashing the application is a more serious thing for me. I've also tried to put this:

gtk_widget_show_all(dialog);

after gtk_file_chooser_dialog_new() which causes no crash, I can select the file, but then I have the SIGSEGV again around gtk_file_chooser_get_filename().

When using gdb, I got this:

Program received signal SIGSEGV, Segmentation fault.
__GI___pthread_mutex_lock (mutex=0x3c3) at ../nptl/pthread_mutex_lock.c:67

Can you help me what mistake I did? I am not familiar with GTK programming too much, so I tried to use examples from the manual, but it does not seem to work. Thanks a lot in advance!


Solution

  • You cannot use widgets without a gtk mainloop.

    Well, it seems I was wrong. My sincere apologies! I had actually tried to do this before and the conclusions were exactly what I described. But the issue has bugged me for the last days, so I did some more digging and experimenting and came up with the following program:

    /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*-  */
    /*
     * main.c
     * Copyright (C) 2015 John Coppens <john@jcoppens.com>
     * 
     * standalone_filechooser is free software: you can redistribute it and/or modify it
     * under the terms of the GNU General Public License as published by the
     * Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     * 
     * standalone_filechooser is distributed in the hope that it will be useful, but
     * WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     * See the GNU General Public License for more details.
     * 
     * You should have received a copy of the GNU General Public License along
     * with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    
    #include <stdio.h>
    #include <gtk/gtk.h>
    
    GtkWidget *
    create_filechooser_dialog(char *init_path, GtkFileChooserAction action)
    {
      GtkWidget *wdg = NULL;
    
      switch (action) {
        case GTK_FILE_CHOOSER_ACTION_SAVE:
          wdg = gtk_file_chooser_dialog_new("Save file", NULL, action,
            "Cancel", GTK_RESPONSE_CANCEL,
            "Save", GTK_RESPONSE_OK,
            NULL);
          break;
    
        case GTK_FILE_CHOOSER_ACTION_OPEN:
          wdg = gtk_file_chooser_dialog_new("Open file", NULL, action,
            "Cancel", GTK_RESPONSE_CANCEL,
            "Open", GTK_RESPONSE_OK,
            NULL);
          break;
    
        case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
        case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
          break;
      }
    
      return wdg;
    }
    
    int main(int argc, char *argv[])
    {
      GtkWidget *wdg;
      char *fname = "";
    
      if (argc == 2)
        fname = argv[1];
    
      gtk_init(&argc, &argv);
    
      wdg = create_filechooser_dialog(fname, GTK_FILE_CHOOSER_ACTION_OPEN);
      if (gtk_dialog_run(GTK_DIALOG(wdg)) == GTK_RESPONSE_OK) {
        printf("%s", gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(wdg)));
        return(0);
      } else  
        return (1);
    }
    

    You can call the dialog from another program (or even the terminal) with

    standalone_filechooser [default file]
    

    If default file is provided (no brackets), it'll be selected. If a file is selected, it will be printed on stdout, else the program will return with error=1

    There is still a small issue with running a widget without main window, which causes a message to be sent to stderr: GtkDialog mapped without a transient parent. This is discouraged. I think this is really a bug (and it might be solved in a more recent version of gtk3). As the message is sent to stderr, it shouldn't interfere with normal use.