Finding files within a directory

I’m working on a gallery and I’m having trouble warping my head around working with files and directories in GTK4. The file structure looks like this:

Main Folder
  ├──Sub Folder
  │       ├── Image File
  │       ├── Executable File
  │       └── Other Folder
  ├──Sub Folder
  │       ├── Image File
  │       ├── Executable File
  │       └── Other Folder
  ├── ...
  ...

with Image File acts as a cover for Sub Folder in the gallery but I can’t think of a way to find it. Please give me some hints as to how I could do it.

I’m using GTK-demo Icon View as a source of reference.

Thank you!

Is the image filename always "Image File" or can it be something else?

I would create a path as directory / Image File by using g_file_new_build_filename:

GFile *image_file = g_file_new_build_filename (directory, "Image File", NULL);

Then create a GdkTexture from such file:

GError *error = NULL;
GdkTexture *texture = gdk_texture_new_from_file (image_file, &error);

Finally, you can create either a GtkImage or a GtkPicture from the texture:

GtkImage *image_widget = gtk_image_new_from_paintable (GDK_PAINTABLE (texture));


Here’s a complete sample:

static GtkWidget *
create_gtk_image_widget_from_file (const char *directory)
{
  GFile *image_file = NULL;
  GdkTexture *texture = NULL;
  GtkImage *image_widget = NULL;
  GError *error = NULL;

  image_file = g_file_new_build_filename (directory, "Image File", NULL);
  texture = gdk_texture_new_from_file (image_file, &error);
  if (!texture)
    {
      g_print ("%s failed", "gdk_texture_new_from_file");
      if (error)
        {
          g_print (": %s", error->message);
          g_clear_error (&error);
        }
      g_print ("\n");
      goto cleanup;
    }

  image_widget = gtk_image_new_from_paintable (GDK_PAINTABLE (texture));

cleanup:
  g_clear_object (&texture);
  g_clear_object (&image_file);

  return image_widget;
}
1 Like

@lb90 Thank you so much for the detailed solution!

Image File is named cover.png. With that being said, can I directly create a GtkImage from said file and skip GdkTexture?

Yep, you can call gtk_image_new_from_file():

char *image_file = g_build_filename (directory, "cover.png", NULL);
GtkImage *image_widget = gtk_image_new_from_file (image_file);

g_clear_pointer (&image_file, g_free);
1 Like

One thing that I noticed from your sample is that it only works with a single directory within Main Folder. In my case, Main Folder contains multiple Sub Folder, each with their own unique cover.png, which I should have specified more clearly.

If you don’t mind can you tweak it a bit to better fit my case?

Ah, you may want to enumerate subdirectories then! Consider the following sample code:

#include <glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>

/**
 * utility_print_error:
 *
 * @function_name: the name of the function that failed
 * @ptr_error: GError containing additional informations
 *
 * Utility function to print a detailed error report
 * when a function fails.
 */
static void
utility_print_error (const char *function_name,
                     GError     **ptr_error)
{
  GError *error = ptr_error ? *ptr_error : NULL;
  g_print ("%s failed", function_name);

  if (error)
    {
      g_print (": %s", error->message);
      g_clear_error (ptr_error);
    }

  g_print ("\n");
}

/**
 * enumerate_child_directories:
 *
 * @directory: path to an existing directory
 *
 * Return value:
 * A GList containing the names of all the subdirectories
 * found within directory. The caller must free the result
 * with `g_list_free_full (result, g_free)`
 */
static GList *
enumerate_child_directories (const char *directory)
{
  GFile *dir = NULL;
  GFileEnumerator *direnum = NULL;
  GList *results = NULL;
  GError *error = NULL;

  dir = g_file_new_for_path (directory);
  direnum = g_file_enumerate_children (dir,
                                       "standard::*",
                                       G_FILE_QUERY_INFO_NONE,
                                       NULL, &error);
  if (!direnum)
    {
      utility_print_error ("g_file_enumerate_children", &error);
      goto cleanup;
    }

  while (TRUE)
    {
      GFileInfo *info;

      if (!g_file_enumerator_iterate (direnum, &info,
                                      NULL, NULL, &error))
        {
          utility_print_error ("g_file_enumerator_iterate", &error);
          goto cleanup;
        }

      if (!info)
        {
          /* Done! */
          break;
        }

      if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
        results = g_list_prepend (results, g_strdup (g_file_info_get_name (info)));
    }

cleanup:
  g_clear_object (&direnum); /* Note: frees the last @info */
  g_clear_object (&dir);

  return g_list_reverse (results);
}

int main()
{
  /* Instead of "/usr/include" one may pass anything else, e.g "C:\\" on Windows */
  GList *child_dirs = enumerate_child_directories ("/usr/include");

  for (GList *l = child_dirs; l != NULL; l = l->next)
    {
      g_print ("%s\n", (const char*) l->data);
    }

  g_list_free_full (child_dirs, g_free);
}

On Unix systems the file name may not be printable, neither on screen nor on a graphical UI. You may also want to acquire the display name of files using g_file_info_get_display_name(). But that’s generally usable as-is

1 Like

Splendid! Though that took quite some time to swallow.

My idea for the next step is to turn child_dir into an array of char that I can then use a loop to create covers for each folder.

Is there a better way to do this?

NVM I finally learned how to work with linked list, it works now! Thank you so much!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.