Logo Search packages:      
Sourcecode: libjsw version File versions

fb.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <gtk/gtk.h>
#include <gdk/gdkx.h>

#include "../include/string.h"
#include "../include/disk.h"

#include "fb.h"

#ifdef MEMWATCH
# include "memwatch.h"
#endif


/* File browser structure. */
typedef struct {

        gbool initialized;
        gbool map_state;

        GtkWidget       *file_browser,  /* File selection widget. */
                        *type_combo;

        GList           *type_combo_glist;

} fb_data_struct;


/* Callbacks. */
static void FileBrowserOKCB(GtkWidget *widget, gpointer data);
static void FileBrowserCancelCB(GtkWidget *widget, gpointer data);
static gint FileBrowserCloseCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
);
static void FileBrowserTypeChangeCB(GtkWidget *widget, gpointer data);

/* Front ends. */
gint FileBrowserInit(void);
void FileBrowserSetTransientFor(GtkWidget *w);
gbool FileBrowserIsQuery(void);
void FileBrowserBreakQuery(void);
gbool FileBrowserGetResponse(
      const gchar *title,
      const gchar *ok_label, const gchar *cancel_label,
        const gchar *path,
        fb_type_struct **type, gint total_types,
        gchar ***path_rtn, gint *path_total_rtns,
        fb_type_struct **type_rtn
);
void FileBrowserMap(void);
void FileBrowserUnmap(void);
void FileBrowserShutdown(void);

/* File browser file type extension management. */
gint FileBrowserTypeListNew(
        fb_type_struct ***list, gint *total,
        const gchar *ext,     /* Space separated list of extensions. */
        const gchar *name     /* Descriptive name. */
);
void FileBrowserDeleteTypeList(
        fb_type_struct **t, gint total
);


#define FB_DEFAULT_TYPE_STR   "All files (*.*)"


static gint block_loop_level;
static fb_data_struct file_browser_data;
static gbool fb_got_user_response;

static gint fb_response_total_paths;
static gchar **fb_response_path;

static fb_type_struct fb_response_type;


/*
 *    Ok button callback.
 */
static void FileBrowserOKCB(GtkWidget *widget, gpointer data)
{
      const gchar *cstrptr;
        GtkFileSelection *fs;
      fb_data_struct *fb = (fb_data_struct *)data;
        if(fb == NULL)
            return;

        if(!fb->initialized)
            return;

      /* Get pointer to file selection widget. */
        fs = GTK_FILE_SELECTION(fb->file_browser);

      /* Mark that we got response. */
      fb_got_user_response = TRUE;

      /* Get selected path. */
      cstrptr = (const gchar *)gtk_file_selection_get_filename(fs);
      if(cstrptr != NULL)
      {
          gint n;

          /* Append response path. */
          if(fb_response_total_paths < 0)
            fb_response_total_paths = 0;

          n = fb_response_total_paths;
          fb_response_total_paths++;
          fb_response_path = (gchar **)g_realloc(
            fb_response_path,
            fb_response_total_paths * sizeof(gchar *)
          );
          if(fb_response_path == NULL)
          {
            fb_response_total_paths = 0;
          }
          else
          {
            fb_response_path[n] = g_strdup(cstrptr);
          }
      }

      /* Unmap. */
      FileBrowserUnmap();

      /* Break out of blocking loop. */
      gtk_main_quit();
      block_loop_level--;
}

/*
 *    File browser cancel callback.
 */
static void FileBrowserCancelCB(GtkWidget *widget, gpointer data)
{
        fb_data_struct *fb = (fb_data_struct *)data;
        if(fb == NULL)
            return;

        if(!fb->initialized)
            return;

        /* Set responses. */
        fb_got_user_response = FALSE;

      /* Unmap. */
        FileBrowserUnmap();

        /* Break out of blocking loop. */
        gtk_main_quit();
      block_loop_level--;
}           

/*
 *      File browser close callback.
 */
static gint FileBrowserCloseCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
)
{
      FileBrowserCancelCB(widget, data);

      return(TRUE);
}


/*
 *    File browser type combo box change callback.
 */
static void FileBrowserTypeChangeCB(GtkWidget *widget, gpointer data)
{
      GtkWidget *w, *entry;
      gchar *type_str, *strptr;
      fb_data_struct *fb = (fb_data_struct *)data;
      if(fb == NULL)
          return;

      if(!fb->initialized)
          return;

      w = fb->type_combo;
      if(w == NULL)
          return;

      /* Get value from type combo box. */
      entry = GTK_COMBO(w)->entry;
      type_str = gtk_entry_get_text(GTK_ENTRY(entry));
      if(type_str == NULL)
          return;


      /* Begin parsing extension type response values, format
       * is:
       *
       *    "<name> (<ext>)"
       *
       * Where <ext> is a space seperated list of extensions.
       */

      /* Seek past spaces. */
      while(ISBLANK(*type_str))
          type_str++;

      /* Name. */
      free(fb_response_type.name);
      fb_response_type.name = NULL;

      strptr = strchr(type_str, '(');
      if(strptr != NULL)
      {
          fb_response_type.name = g_strdup(type_str);

          if(fb_response_type.name != NULL)
          {
              /* Seek to '(' char and deliminate first space. */
              strptr = strchr(fb_response_type.name, '(');
              if(strptr != NULL)
              {
                while(strptr > fb_response_type.name)
                {
                    if(ISBLANK(*strptr))
                    {
                      *strptr = '\0';
                      break;
                    }
                    strptr--;
                }
            }
          }
      }

      /* Extension. */
        g_free(fb_response_type.ext);
        fb_response_type.ext = NULL;

      strptr = strchr(type_str, '(');
      if(strptr == NULL)
      {
          /* No name to parse through. */
          fb_response_type.ext = g_strdup(type_str);
      }
      else
      {
          strptr++;     /* Seek past left parens. */

          /* Seek past initial spaces. */
          while(ISBLANK(*strptr))
            strptr++;

          fb_response_type.ext = g_strdup(strptr);
          if(fb_response_type.ext != NULL)
          {
            strptr = strchr(fb_response_type.ext, ')');
            if(strptr != NULL)
            {
                (*strptr) = '\0';

                /* Strip tailing spaces. */
                strptr--;
                while(strptr > fb_response_type.ext)
                {
                  if(ISBLANK(*strptr))
                      (*strptr) = '\0';
                  else
                      break;
                  strptr--;
                }
            }
          }
      }
}


/*
 *    Initializes file browser.
 */
gint FileBrowserInit(void)
{
      GtkWidget *w, *parent;
      GtkFileSelection *fs;
      GList *glist;
      fb_data_struct *fb = &file_browser_data;


      /* Reset local globals. */
        block_loop_level = 0;
        fb_got_user_response = FALSE;
        fb_response_path = NULL;
      fb_response_total_paths = 0;
      memset(&fb_response_type, 0x00, sizeof(fb_type_struct));


      /* Reset values. */
        fb->initialized = TRUE;
        fb->map_state = FALSE;

      fb->type_combo_glist = NULL;


      /* Create the file browser. */
      w = gtk_file_selection_new("File selection");
        if(w == NULL)
            return(-1);
      fb->file_browser = w;
      fs = GTK_FILE_SELECTION(w);
        gtk_widget_realize(w);

        /* Begin set callbacks. */

      /* Close. */
        gtk_signal_connect(
            GTK_OBJECT(w), "delete_event",
            GTK_SIGNAL_FUNC(FileBrowserCloseCB),
            (gpointer)fb
        );

      /* Ok button. */
      gtk_signal_connect(
          GTK_OBJECT(GTK_FILE_SELECTION(w)->ok_button), "clicked",
            GTK_SIGNAL_FUNC(FileBrowserOKCB),
            (gpointer)fb
        );
      /* Cancel button. */
        gtk_signal_connect(
            GTK_OBJECT(GTK_FILE_SELECTION(w)->cancel_button), "clicked",
            GTK_SIGNAL_FUNC(FileBrowserCancelCB),
            (gpointer)fb
        );

      /* Create type pull down. */
      w = gtk_table_new(1, 2, FALSE);
      gtk_box_pack_start(GTK_BOX(fs->main_vbox), w, FALSE, FALSE, 0);
      gtk_widget_show(w);
      parent = w;

        w = gtk_label_new("Type:");
      gtk_table_attach(GTK_TABLE(parent), w,
          0, 1,
          0, 1,
          0,
            0,
          2, 2
      );
      gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_RIGHT);
      gtk_widget_show(w);

      fb->type_combo = w = gtk_combo_new();
        gtk_table_attach(GTK_TABLE(parent), w,
            1, 2,
            0, 1,
            GTK_EXPAND | GTK_FILL,            
            0,
            2, 2
        );
      gtk_combo_disable_activate(GTK_COMBO(w));
      gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(w)->entry), FALSE);
      gtk_signal_connect(
          GTK_OBJECT(GTK_COMBO(w)->entry), "activate",
          GTK_SIGNAL_FUNC(FileBrowserTypeChangeCB),
          fb
      );
        gtk_signal_connect(
            GTK_OBJECT(GTK_COMBO(w)->entry), "changed",
            GTK_SIGNAL_FUNC(FileBrowserTypeChangeCB),
            fb
        );
      gtk_widget_show(w);

      /* Set default type string. */
      gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(w)->entry), FB_DEFAULT_TYPE_STR);

      /* Create default glist on type combo. */
      glist = NULL;
      glist = g_list_append(glist, g_strdup(FB_DEFAULT_TYPE_STR));
      gtk_combo_set_popdown_strings(GTK_COMBO(w), glist);

      /* Update type combo's glist pointer. */
      fb->type_combo_glist = glist;
      glist = NULL;

      return(0);
}

/*
 *      Sets dialog to be a transient for the given toplevel window
 *      widget w. If w is NULL then no transient for will be unset.
 */
void FileBrowserSetTransientFor(GtkWidget *w)
{
      fb_data_struct *fb = &file_browser_data;

        if(!fb->initialized)
            return;

        if(fb->file_browser != NULL)
        {
            if(w != NULL)
            {
                /* Given widget if not NULL, must be a window. */
                if(!GTK_IS_WINDOW(GTK_OBJECT(w)))
                    return;

                gtk_window_set_modal(
                    GTK_WINDOW(fb->file_browser), TRUE
                );
/*              gtk_grab_add(fb->file_browser); */
                gtk_window_set_transient_for(
                    GTK_WINDOW(fb->file_browser), GTK_WINDOW(w)
                );
            }
            else
            {
                gtk_window_set_modal(
                    GTK_WINDOW(fb->file_browser), FALSE
                );
                gtk_window_set_transient_for(
                    GTK_WINDOW(fb->file_browser), NULL
                );
            }
        }
}

/*
 *      Returns TRUE if currently blocking for query.
 */
gbool FileBrowserIsQuery(void) 
{
        if(block_loop_level > 0)
            return(TRUE);
        else
            return(FALSE);
}

/*
 *      Ends query if any and returns a not available response.
 */
void FileBrowserBreakQuery(void)
{
        fb_got_user_response = FALSE;

        /* Break out of an additional blocking loops. */
        while(block_loop_level > 0)
        {
            gtk_main_quit();
            block_loop_level--;
        }
        block_loop_level = 0;
}

/*
 *    Maps the file browser and sets up the inital values.
 *
 *    Returns TRUE if a path was selected or FALSE if user canceled.
 *
 *    For most values that are set NULL, the value is left unchanged.
 *    All given values are coppied.
 *
 *    If type is set to NULL however, then the type list on the file
 *    browser will be left empty.
 *
 *    All returned pointer values should be considered statically
 *    allocated. The returned pointer for type_rtn may point to
 *    a structure in the input type list.
 */
gbool FileBrowserGetResponse(
      const gchar *title,
      const gchar *ok_label, const gchar *cancel_label,
      const gchar *path,
      fb_type_struct **type, gint total_types,
        gchar ***path_rtn, gint *path_total_rtns,
      fb_type_struct **type_rtn
)
{
      gint i;
      GdkWindow *root = GDK_ROOT_PARENT();
      GtkWidget *w;
      fb_type_struct *t_ptr;
      fb_data_struct *fb = &file_browser_data;


        /* Do not handle response if already waiting for a response,
         * return with a not available response code.
         */
        if(block_loop_level > 0)
      {
          if(path_rtn != NULL)
            (*path_rtn) = NULL;
          if(path_total_rtns != NULL)
            (*path_total_rtns) = 0;
          if(type_rtn != NULL)
            (*type_rtn) = NULL;

            return(FALSE);
      }

      /* Reset global responses values. */
      fb_got_user_response = FALSE;

      for(i = 0; i < fb_response_total_paths; i++)
          g_free(fb_response_path[i]);
      g_free(fb_response_path);
      fb_response_path = NULL;
      fb_response_total_paths = 0;

      g_free(fb_response_type.ext);
        g_free(fb_response_type.name);
        memset(&fb_response_type, 0x00, sizeof(fb_type_struct));


      /* Reset returns. */
      if(path_rtn != NULL)
          (*path_rtn) = NULL;
      if(path_total_rtns != NULL)
          (*path_total_rtns) = 0;
      if(type_rtn != NULL)
          (*type_rtn) = NULL;

      /* File browser must be initialized. */
      if(!fb->initialized)
          return(fb_got_user_response);

      /* Get pointer to file browser widget. */
      w = fb->file_browser;
      if(w == NULL)
            return(fb_got_user_response);

      /* Update title if specified. */
      if(title != NULL)
          gtk_window_set_title(GTK_WINDOW(w), title);

      /* Update initial path if specified. */
      if(path != NULL)
      {
          struct stat stat_buf;
          char tmp_path[PATH_MAX + NAME_MAX];


          strncpy(tmp_path, path, PATH_MAX + NAME_MAX);
          tmp_path[PATH_MAX + NAME_MAX - 1] = '\0';

          /* Check if it exists and if its a dir. */
          if(!stat(tmp_path, &stat_buf))
          {
            if(S_ISDIR(stat_buf.st_mode))
            {
                /* Is a dir, need to make sure it has a trailing
                 * delimiator.
                 */
                int len = strlen(tmp_path);

                if((len > 0) && (len < (PATH_MAX + NAME_MAX - 1)))
                {
                    if(tmp_path[len - 1] != DIR_DELIMINATOR)
                  {
                      tmp_path[len] = DIR_DELIMINATOR;
                      tmp_path[len + 1] = '\0';
                  }
                }

            }
          }

          /* Set new path. */
          gtk_file_selection_set_filename(
            GTK_FILE_SELECTION(w), tmp_path
          );
      }

      /* Set first type to local global fb_response_type if
       * available.
       */
      if((type != NULL) && (total_types > 0))
      {
          t_ptr = type[0];
          if(t_ptr != NULL)
          {
            /* Set that value to our local global fb_response_type. */
            fb_response_type.ext = ((t_ptr->ext == NULL) ?
                NULL : g_strdup(t_ptr->ext)
            );
          }

          /* Since a new set of file extension types are given, we need
           * need to clear and update the type combo box.
           */
          w = fb->type_combo;
          if(w != NULL)
          {
            GList *glist = NULL;
            char ext_text[NAME_MAX];
            char name_text[NAME_MAX];
            char text[(2 * NAME_MAX) + 80];
                char first_text[(2 * NAME_MAX) + 80];


            (*ext_text) = '\0';
            (*name_text) = '\0';
            (*text) = '\0';
            (*first_text) = '\0';

            /* Itterate through all given file extension types. */
            for(i = 0; i < total_types; i++)
                {
                    t_ptr = type[i];
                    if(t_ptr == NULL)
                        continue;

                /* Parse extensions text. */
                (*ext_text) = '\0';
                if(t_ptr->ext != NULL)
                  strncat(ext_text, t_ptr->ext, NAME_MAX);
                ext_text[NAME_MAX - 1] = '\0';

                /* Parse name text. */
                (*name_text) = '\0';
                if(t_ptr->name != NULL)
                  strncat(name_text, t_ptr->name, NAME_MAX);
                    name_text[NAME_MAX - 1] = '\0';

                /* Generate complete text. */
                if((*name_text) == '\0')
                  sprintf(text, "%s", ext_text);
                else
                  sprintf(text, "%s (%s)",
                      name_text, ext_text
                  );

                /* Add this to the glist. */
                glist = g_list_append(glist, g_strdup(text));

                /* Is this the first extension given? If so then
                 * record it in the first_text buffer.
                 */
                if(i == 0)
                  strcpy(first_text, text);
            }

            /* New glist valid? */
            if(glist != NULL)
            {
                GList *old_glist;

                /* Set new glist to type combo. */
                gtk_combo_set_popdown_strings(GTK_COMBO(w), glist);

                /* Delete old recorded glist and type combo current
                 * glist pointer to the new glist.
                 */
                old_glist = fb->type_combo_glist;
                fb->type_combo_glist = glist;
                if(old_glist != NULL)
                {
                  g_list_foreach(old_glist, (GFunc)g_free, NULL);
                  g_list_free(old_glist);
                  old_glist = NULL;
                }

                /* Set first type text string to combo's entry. */
                gtk_entry_set_text(GTK_ENTRY(
                  GTK_COMBO(w)->entry),
                  first_text
                );
            }     /* New glist valid? */

          }
      }


      /* Center file browser. */
      w = fb->file_browser;
      if((w != NULL) && (root != NULL))
      {
          gint rx, ry, rw, rh, rd;

          gdk_window_get_geometry(
            root, &rx, &ry, &rw, &rh, &rd
          );
          gtk_widget_set_uposition(
            w,
            (rw / 2) - (w->allocation.width / 2),
            (rh / 2) - (w->allocation.height / 2)
          );
      }

        /* Map file browser as needed. */
        FileBrowserMap();

        /* Block GUI untill response. */
      block_loop_level++;
        gtk_main();

        /* Unmap file browser just in case it was not unmapped from
       * any of the callbacks.
       */
        FileBrowserUnmap();

        /* Break out of an additional blocking loops. */
        while(block_loop_level > 0)
        {
            gtk_main_quit();
            block_loop_level--;
        }
        block_loop_level = 0;


      /* Begin setting returns. */

      /* Response path returns. */
      if(path_rtn != NULL)
          (*path_rtn) = fb_response_path;
      if(path_total_rtns != NULL)
          (*path_total_rtns) = fb_response_total_paths;

      /* Set up type return? */
      if(type_rtn != NULL)
      {
          /* The member ext needs to be allocated, otherwise we
           * need to report NULL as the file extension type return.
           */
          if(fb_response_type.ext == NULL)
          {
            /* No extension available. */
            (*type_rtn) = NULL;
          }
          else
          {
            /* Go through types list. */
            for(i = 0; i < total_types; i++)
            {
                t_ptr = type[i];
                if(t_ptr == NULL)
                  continue;

                if(t_ptr->ext == NULL)
                  continue;

                /* Extension types match? */
                if(!strcasecmp(t_ptr->ext, fb_response_type.ext))
                {
                  /* Set matched values. */
                  free(fb_response_type.name);
                  fb_response_type.name = ((t_ptr->name == NULL) ?
                      NULL : g_strdup(t_ptr->name)
                  );
                }
            }

                (*type_rtn) = &fb_response_type;
          }
      }


      return(fb_got_user_response);
}


/*
 *    Maps the file browser as needed.
 */
void FileBrowserMap(void)
{
        fb_data_struct *fb = &file_browser_data;


        if(fb == NULL)
            return;

        if(!fb->initialized)
            return;

        if(!fb->map_state)
        {
            GtkWidget *w = fb->file_browser;

            if(w != NULL)
                gtk_widget_show(w);

            fb->map_state = TRUE;
        }
}

/*
 *    Unmaps the file browser as needed.
 */
void FileBrowserUnmap(void)
{
        fb_data_struct *fb = &file_browser_data;


      if(fb == NULL)
          return;

      if(!fb->initialized)
          return;

      if(fb->map_state)
      {
          GtkWidget *w = fb->file_browser;

          if(w != NULL)
            gtk_widget_hide(w);

          fb->map_state = FALSE;
      }
}

/*
 *    Deallocates file browser resources.
 */
void FileBrowserShutdown(void)
{
      gint i;
      GtkWidget **w;
      GList *glist;
        fb_data_struct *fb = &file_browser_data;


      /* Reset local globals. */
        fb_got_user_response = FALSE; 

        for(i = 0; i < fb_response_total_paths; i++)
            g_free(fb_response_path[i]);
        g_free(fb_response_path);
        fb_response_path = NULL;
        fb_response_total_paths = 0;

        g_free(fb_response_type.ext);
        g_free(fb_response_type.name);
        memset(&fb_response_type, 0x00, sizeof(fb_type_struct));

        /* Break out of an additional blocking loops. */
        while(block_loop_level > 0)
        {
            gtk_main_quit();
            block_loop_level--;
        }
        block_loop_level = 0;


      /* Is file browser initialized? */
      if(fb->initialized)
      {
#define DO_DESTROY_WIDGET       \
{ \
 if((*w) != NULL) \
 { \
  GtkWidget *tmp_w = *w; \
  (*w) = NULL; \
  gtk_widget_destroy(tmp_w); \
 } \
}

          /* Begin destroying widgets. */

          /* File extension type combo. */
          w = &fb->type_combo;
          DO_DESTROY_WIDGET
          /* File extension type combo's list. */
          glist = fb->type_combo_glist;
          fb->type_combo_glist = NULL;
          if(glist != NULL)
          {
            g_list_foreach(glist, (GFunc)g_free, NULL);
            g_list_free(glist);
          }

          /* Actual file browser. */
          w = &fb->file_browser;
          DO_DESTROY_WIDGET

#undef DO_DESTROY_WIDGET
      }

      /* Clear file browser structure. */
      memset(fb, 0x00, sizeof(fb_data_struct));
}


/*
 *    Convience function to allocate a new file browser file extension
 *    type structure and append it to the given list. The given list
 *    will be modified and the index number of the newly allocated
 *    structure will be returned.
 *
 *    Can return -1 on error.
 */
gint FileBrowserTypeListNew(
      fb_type_struct ***list, gint *total,
        const gchar *ext,     /* Space separated list of extensions. */
        const gchar *name     /* Descriptive name. */
)
{
      gint n;
      fb_type_struct *fb_type_ptr;


      if((list == NULL) || (total == NULL))
          return(-1);

      /* Increase total. */
      if((*total) < 0)
          (*total) = 0;
      n = (*total);
      (*total) = n + 1;

      /* Allocate more pointers. */
      (*list) = (fb_type_struct **)g_realloc(
          *list,
          (*total) * sizeof(fb_type_struct *)
      );
      if((*list) == NULL)
      {
          (*total) = 0;
          return(-1);
      }

      /* Allocate new structure. */
      fb_type_ptr = (fb_type_struct *)g_malloc0(sizeof(fb_type_struct));
      (*list)[n] = fb_type_ptr;
      if(fb_type_ptr == NULL)
      {
          (*total) = n;
          return(-1);
      }

      /* Set values. */
      if(ext != NULL)
          fb_type_ptr->ext = g_strdup(ext);
      if(name != NULL)
          fb_type_ptr->name = g_strdup(name);

      return(n);
}

/*
 *    Deletes a dynamically allocated file browser file extensions
 *    type list.
 */
void FileBrowserDeleteTypeList(
        fb_type_struct **t, gint total 
)
{
      gint i;
      fb_type_struct *t_ptr;

      for(i = 0; i < total; i++)
      {
          t_ptr = t[i];
          if(t_ptr == NULL)
            continue;

          g_free(t_ptr->ext);
          g_free(t_ptr->name);
          g_free(t_ptr);
      }
      g_free(t);
}



Generated by  Doxygen 1.6.0   Back to index