To get in context, i am making a GUI application that based on a input command through a GtkEntry like ls
it would pop up a window with the respective files in that directory. So the GTK4 demo has an example of a File Browser that will suite perfectly my needs, but i just can't make it work!
This is my project structure:
main.c
#include <gtk/gtk.h>
#include "../headers/mainApp.h"
int main(int argc, char* argv[]){
g_setenv("GSETTINGS_SCHEMA_DIR", ".", FALSE);
return g_application_run(G_APPLICATION(main_app_new()), argc, argv);
}
mainApp.c
#include <gtk/gtk.h>
#include "../headers/mainApp.h"
#include "../headers/mainAppWindow.h"
#include "../headers/mainAppPreferences.h"
static void main_app_init(MainApp* app);
static void main_app_class_init(MainAppClass* class);
static void main_app_open(GApplication* app, GFile** files, int n_files, const char* hint);
static void preferences_activated(GSimpleAction* action, GVariant* parameter, gpointer app);
static void quit_activated(GSimpleAction* action, GVariant* parameter, gpointer app);
static void main_app_startup(GApplication* app);
static void main_app_activate(GApplication* app);
struct _MainApp {
GtkApplication parent;
};
G_DEFINE_TYPE(MainApp, main_app, GTK_TYPE_APPLICATION)
MainApp* main_app_new(void) {
return g_object_new(MAIN_APP_TYPE, "application-id", "org.gtk.mainapp", "flags", G_APPLICATION_HANDLES_OPEN, NULL);
}
static void main_app_init(MainApp* app){}
static void main_app_open(GApplication* app, GFile** files, int n_files, const char* hint){
GList* windows;
MainAppWindow* window;
int i;
windows = gtk_application_get_windows(GTK_APPLICATION(app));
if(windows)
window = MAIN_APP_WINDOW(MAIN_APP(app));
else
window = main_app_window_new(MAIN_APP(app));
/*for(i = 0; i < n_files; i++)
main_app_window_open(window, files[i]);*/
gtk_window_present(GTK_WINDOW(window));
}
static void main_app_class_init(MainAppClass* class){
G_APPLICATION_CLASS (class)->startup = main_app_startup;
G_APPLICATION_CLASS (class)->activate = main_app_activate;
G_APPLICATION_CLASS (class)->open = main_app_open;
}
static void preferences_activated(GSimpleAction* action, GVariant* parameter, gpointer app){
MainAppPreferences *preferences;
GtkWindow *window;
window = gtk_application_get_active_window (GTK_APPLICATION (app));
preferences = main_app_preferences_new(MAIN_APP_WINDOW(window));
gtk_window_present (GTK_WINDOW (preferences));
}
static void quit_activated(GSimpleAction* action, GVariant* parameter, gpointer app){
g_application_quit (G_APPLICATION (app));
}
static GActionEntry app_entries[] = {
{ "preferences", preferences_activated, NULL, NULL, NULL },
{ "quit", quit_activated, NULL, NULL, NULL }
};
static void main_app_startup(GApplication* app){
const char* quit_accels[2] = {"<Ctrl>Q", NULL};
G_APPLICATION_CLASS(main_app_parent_class)->startup (app);
g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries, G_N_ELEMENTS (app_entries), app);
gtk_application_set_accels_for_action (GTK_APPLICATION (app), "app.quit", quit_accels);
}
static void main_app_activate(GApplication* app){
MainAppWindow* window;
window = main_app_window_new(MAIN_APP(app));
gtk_window_present(GTK_WINDOW(window));
}
mainAppWindow.c
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <stdio.h>
#include "../headers/mainApp.h"
#include "../headers/mainAppWindow.h"
#include "../headers/fileBrowserView.h"
static void main_app_window_class_init(MainAppWindowClass* class);
static void main_app_window_dispose(GObject* object);
static void main_app_window_init(MainAppWindow* window);
static void command_changed(GtkEntry* entry, MainAppWindow* window);
static void command_submit_pressed(GtkButton *button, gpointer user_data);
static GtkTreeModel* create_completion_model(void);
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
struct _MainAppWindow {
GtkApplicationWindow parent;
GSettings* settings;
GtkWidget* gears;
GtkWidget* commandEntry;
GtkWidget* btnCommandSubmit;
GtkWidget* stack;
};
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
G_DEFINE_TYPE (MainAppWindow, main_app_window, GTK_TYPE_APPLICATION_WINDOW)
MainAppWindow* main_app_window_new(MainApp* app){
return g_object_new(MAIN_APP_WINDOW_TYPE, "application", app, NULL);
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
static void main_app_window_class_init(MainAppWindowClass* class){
GObjectClass* gobject_class = G_OBJECT_CLASS(class);
gobject_class->dispose = main_app_window_dispose;
gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), "/org/gtk/mainapp/ui/window.ui");
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), MainAppWindow, gears);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), MainAppWindow, commandEntry);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), MainAppWindow, btnCommandSubmit);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), MainAppWindow, stack);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), command_changed);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), command_submit_pressed);
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
static void main_app_window_init(MainAppWindow* window){
GtkBuilder* builder;
GMenuModel* menu;
GAction* action;
GtkEntryCompletion* completion;
GtkTreeModel *completion_model;
GtkBox* box;
gtk_widget_init_template(GTK_WIDGET(window));
builder = gtk_builder_new_from_resource("/org/gtk/mainapp/ui/gears-menu.ui");
menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(window->gears), menu);
window->stack = GTK_WIDGET(gtk_builder_get_object(builder, "stack"));
g_object_unref(builder);
window->settings = g_settings_new("org.gtk.mainapp");
//Create completion object.
completion = gtk_entry_completion_new();
//Asign the completion to the entry.
gtk_entry_set_completion(GTK_ENTRY(window->commandEntry), completion);
g_object_unref(completion);
//Create a tree model and use it as the completion model.
completion_model = create_completion_model();
gtk_entry_completion_set_model(completion, completion_model);
g_object_unref(completion_model);
/* Use model column 0 as the text column */
gtk_entry_completion_set_text_column (completion, 0);
gtk_entry_completion_set_inline_completion (completion, TRUE);
gtk_entry_completion_set_inline_selection (completion, TRUE);
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
void main_app_window_open(MainAppWindow* window, GFile* file){
char *basename;
GtkWidget *scrolled, *view;
char *contents;
gsize length;
GtkTextBuffer *buffer;
GtkTextTag *tag;
GtkTextIter start_iter, end_iter;
basename = g_file_get_basename (file);
scrolled = gtk_scrolled_window_new ();
gtk_widget_set_hexpand (scrolled, TRUE);
gtk_widget_set_vexpand (scrolled, TRUE);
view = gtk_text_view_new ();
gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled), view);
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
if (g_file_load_contents (file, NULL, &contents, &length, NULL, NULL))
{
gtk_text_buffer_set_text (buffer, contents, length);
g_free (contents);
}
tag = gtk_text_buffer_create_tag (buffer, NULL, NULL);
g_settings_bind (window->settings, "font",
tag, "font",
G_SETTINGS_BIND_DEFAULT);
gtk_text_buffer_get_start_iter (buffer, &start_iter);
gtk_text_buffer_get_end_iter (buffer, &end_iter);
gtk_text_buffer_apply_tag (buffer, tag, &start_iter, &end_iter);
g_free (basename);
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
static void main_app_window_dispose(GObject* object){
MainAppWindow* window;
window = MAIN_APP_WINDOW(object);
g_clear_object(&window->settings);
G_OBJECT_CLASS(main_app_window_parent_class)->dispose(object);
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
static void command_changed(GtkEntry* entry, MainAppWindow* window){
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
static void command_submit_pressed(GtkButton *button, gpointer user_data){
GtkWidget* window;
window = do_listview_filebrowser(NULL);
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
static GtkTreeModel* create_completion_model(void){
const char* commands[] = {
"ls [dir]",
"ls -l [dir]",
"cat [file_path]",
"pwd",
"adduser [user_name]",
"deluser [user_name]",
NULL
};
int i;
GtkListStore *store;
GtkTreeIter iter;
store = gtk_list_store_new (1, G_TYPE_STRING);
for (i = 0; commands[i]; i++)
{
/* Append one word */
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter, 0, commands[i], -1);
}
return GTK_TREE_MODEL (store);
}
///////////////////////////////////////////////////////////////////////////////////////
/* Layout:
+-------------------------------------+
| +-----------++-------++-----------+ |
| | CmdEntry || Space || Submit | |
| +-----------++-------++-----------+ |
+-------------------------------------+
Constraints:
super.start = cmdEntry.start - 8
cmdEntry.end = space.start
space.end = Submit.start
submit.end = super.end - 8
*/
///////////////////////////////////////////////////////////////////////////////////////
fileBrowserView.c
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include "../headers/mainAppWindow.h"
#include "../headers/fileBrowserView.h"
static void file_browser_view_set_property (GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec);
static void file_browser_view_get_property (GObject* object, guint property_id, GValue* value, GParamSpec* pspec);
static void file_browser_view_finalize (GObject *object);
char* filebrowser_get_display_name (GObject* object, GFileInfo* info);
char* filebrowser_get_content_type (GObject* object, GFileInfo* info);
char* filebrowser_get_size (GObject* object, GFileInfo* info);
GIcon* filebrowser_get_icon (GObject* object, GFileInfo* info);
void filebrowser_up_clicked_cb (GtkButton* button, GtkDirectoryList* list);
void filebrowser_view_activated_cb (GtkGridView* view, guint pos, GtkDirectoryList* list);
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
struct _FileBrowserView {
GObject parent_instance;
GtkListItemFactory *factory;
char *icon_name;
char *title;
GtkOrientation orientation;
};
enum {
PROP_0,
PROP_FACTORY,
PROP_ICON_NAME,
PROP_TITLE,
PROP_ORIENTATION,
N_PROPS
};
static GParamSpec* properties[N_PROPS] = { NULL, };
static GtkWidget *window = NULL;
G_DEFINE_TYPE (FileBrowserView, file_browser_view, G_TYPE_OBJECT)
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
FileBrowserView* file_browser_view_new(GtkWindow* window){
return g_object_new(FILE_BROWSER_TYPE, NULL, window, NULL);
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
static void file_browser_view_class_init(FileBrowserViewClass* view){
GObjectClass* gobject_class = G_OBJECT_CLASS(view);
gobject_class->get_property = file_browser_view_get_property;
gobject_class->set_property = file_browser_view_set_property;
gobject_class->finalize = file_browser_view_finalize;
properties[PROP_FACTORY] =
g_param_spec_object ("factory",
"factory",
"factory to use in the main view",
GTK_TYPE_LIST_ITEM_FACTORY,
G_PARAM_READWRITE);
properties[PROP_ICON_NAME] =
g_param_spec_string ("icon-name",
"icon name",
"icon to display for selecting this view",
NULL,
G_PARAM_READWRITE);
properties[PROP_TITLE] =
g_param_spec_string ("title",
"title",
"title to display for selecting this view",
NULL,
G_PARAM_READWRITE);
properties[PROP_ORIENTATION] =
g_param_spec_enum ("orientation",
"orientation",
"orientation of the view",
GTK_TYPE_ORIENTATION,
GTK_ORIENTATION_VERTICAL,
G_PARAM_READWRITE);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
static void file_browser_view_init(FileBrowserView* view) {}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
static void file_browser_view_get_property (GObject* object, guint property_id, GValue* value, GParamSpec* pspec) {
FileBrowserView *self = FILE_BROWSER_VIEW (object);
switch (property_id) {
case PROP_FACTORY:
g_value_set_object (value, self->factory);
break;
case PROP_ICON_NAME:
g_value_set_string (value, self->icon_name);
break;
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
case PROP_ORIENTATION:
g_value_set_enum (value, self->orientation);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
static void file_browser_view_set_property (GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) {
FileBrowserView *self = FILE_BROWSER_VIEW (object);
switch (prop_id) {
case PROP_FACTORY:
g_set_object (&self->factory, g_value_get_object (value));
break;
case PROP_ICON_NAME:
g_free (self->icon_name);
self->icon_name = g_value_dup_string (value);
break;
case PROP_TITLE:
g_free (self->title);
self->title = g_value_dup_string (value);
break;
case PROP_ORIENTATION:
self->orientation = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
static void file_browser_view_finalize (GObject *object) {
FileBrowserView *self = FILE_BROWSER_VIEW (object);
g_object_unref (self->factory);
g_free (self->icon_name);
g_free (self->title);
G_OBJECT_CLASS (file_browser_view_parent_class)->dispose (object);
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
char* filebrowser_get_display_name (GObject* object, GFileInfo* info) {
if (!info)
return NULL;
return g_strdup (g_file_info_get_attribute_string (info, "standard::display-name"));
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
char* filebrowser_get_content_type (GObject* object, GFileInfo* info) {
if (!info)
return NULL;
return g_strdup (g_file_info_get_attribute_string (info, "standard::content-type"));
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
char* filebrowser_get_size (GObject* object, GFileInfo* info) {
if (!info)
return NULL;
return g_format_size (g_file_info_get_attribute_uint64 (info, "standard::size"));
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
GIcon* filebrowser_get_icon (GObject* object, GFileInfo* info) {
GIcon *icon;
if (info)
icon = G_ICON (g_file_info_get_attribute_object (info, "standard::icon"));
else
icon = NULL;
if (icon)
g_object_ref (icon);
return icon;
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
void filebrowser_up_clicked_cb (GtkButton* button, GtkDirectoryList* list) {
GFile *file;
file = g_file_get_parent (gtk_directory_list_get_file (list));
if (file == NULL)
return;
gtk_directory_list_set_file (list, file);
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
void filebrowser_view_activated_cb (GtkGridView* view, guint pos, GtkDirectoryList* list) {
GFileInfo *info;
info = g_list_model_get_item (G_LIST_MODEL (gtk_grid_view_get_model (view)), pos);
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
gtk_directory_list_set_file (list, G_FILE (g_file_info_get_attribute_object (info, "standard::file")));
g_object_unref (info);
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
GtkWidget* do_listview_filebrowser (GtkWidget *do_widget) {
if (!window)
{
GtkWidget *view;
GtkBuilder *builder;
GtkDirectoryList *dirlist;
GFile *file;
char *cwd;
GtkCssProvider *provider;
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_resource (provider, "/org/gtk/mainapp/css/fileBrowser.css");
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (provider),
800);
g_object_unref (provider);
builder = gtk_builder_new_from_resource ("/org/gtk/mainapp/ui/file-browser.ui");
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
g_signal_connect(window, "clicked", G_CALLBACK(filebrowser_up_clicked_cb), NULL);
g_signal_connect(window, "activate", G_CALLBACK(filebrowser_view_activated_cb), NULL);
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
/* Create the model and fill it with the contents of the current directory */
cwd = g_get_current_dir ();
file = g_file_new_for_path (cwd);
g_free (cwd);
dirlist = GTK_DIRECTORY_LIST (gtk_builder_get_object (builder, "dirlist"));
gtk_directory_list_set_file (dirlist, file);
g_object_unref (file);
/* grab focus in the view */
view = GTK_WIDGET (gtk_builder_get_object (builder, "view"));
gtk_widget_grab_focus (view);
g_object_unref (builder);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
file-browser.ui
https://github.com/panchis7u7/Encryption_Algorithms/blob/master/GUI_User_Resource_Manager/ui/file-browser.ui
If i try to run the code i get a failed to add UI from resource /org/gtk/mainapp/ui/file-browser.ui: .:66:1 Invalid type 'FileBrowserView'
As a solution i just call the method to create a new FileBrowserView via the file_browser_view_new()
method, and worked, but then the signals in file-browser.ui
where not located: failed to add UI from resource /org/gtk/mainapp/ui/file-browser.ui: No function named filebrowser_up_clicked_cb.
For mor details, here is the project in github: https://github.com/panchis7u7/Encryption_Algorithms/tree/master/GUI_User_Resource_Manager
After doing some more investigation into the make file you had in your repository, I believe I discovered the main issue for the "ui" failures when launching the program built by making program "mainapp". Therefore, I decided to edit my earlier answer with this additional information.
The original make file contains a variable definition that is supposed to contain all of the object files to be used when the "gcc" compiler is called to build the program.
OBJS = $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SRCS))
The result of that definition is that is does not contain object file "resources.o" in the group of objects. So, later in the "mainapp" section the "gcc" compiler command is executed without that file in the group. Since file "resources.o" contains function references, no error occurs and the "mainapp" program is still built. However, when the program is executed, it cannot find any internal reference to the "ui" definitions and therefore throws the errors.
I am not very experienced yet with the composition of make files, but I was able to create a revised version of your make file to address the missing pieces to the final program build. First, I appended the reference to the "resources.o" file to your variable definition. Also, since creating the "resources.o" file depends upon the creation of the other object files within the "$(OBJ)/%.o:", I placed the creation of the "resources.c" file and the compilation of the "resources.o" file within that section of the make file. The resulting make file is as follows:
CC ?= gcc
PKGCONFIG = $(shell which pkg-config)
CFLAGS = $(shell $(PKGCONFIG) --cflags gtk4)
LIBS = $(shell $(PKGCONFIG) --libs gtk4)
GLIB_COMPILE_RESOURCES = $(shell $(PKGCONFIG) --variable=glib_compile_resources gio-2.0)
GLIB_COMPILE_SCHEMAS = $(shell $(PKGCONFIG) --variable=glib_compile_schemas gio-2.0)
SRC=src
OBJ=obj
BIN=bin
SRCS=$(wildcard $(SRC)/*.c)
BUILT_SRC = $(SRC)/resources.c
OBJS = $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SRCS))
OBJS += $(OBJ)/resources.o
all: mainapp
org.gtk.mainapp.gschema.valid: org.gtk.mainapp.gschema.xml
$(GLIB_COMPILE_SCHEMAS) --strict --dry-run --schema-file=$< && mkdir -p $(@D)/ui && touch $@
gschemas.compiled: org.gtk.mainapp.gschema.valid
$(GLIB_COMPILE_SCHEMAS) .
$(OBJ)/%.o: $(SRC)/%.c
$(CC) -c -o $(OBJ)/$(@F) $(CFLAGS) $<
$(GLIB_COMPILE_RESOURCES) mainapp.gresource.xml --target=$(SRC)/resources.c --sourcedir=. --generate-source
$(CC) -o $(OBJ)/resources.o -c $(SRC)/resources.c $(CFLAGS)
mainapp: $(OBJS) gschemas.compiled
$(CC) -o $(@F) $(OBJS) $(LIBS)
clean:
rm -f org.gtk.mainapp.gschema.valid
rm -f gschemas.compiled
rm -f $(BUILT_SRC)
rm -f $(OBJS)
rm -f mainapp
Using this revised make file, I was able to build the "mainapp" program and was able to execute the "mainapp" program to display a directory view in the window.
Hopefully, a second set of eyes was helpful in resolving this issue for you.
Regards.