]> uap-core.de Git - mizunara.git/commitdiff
add basic construct for gtk-filesview
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 20 Jan 2025 20:55:15 +0000 (21:55 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 20 Jan 2025 20:55:15 +0000 (21:55 +0100)
mizunara/application.h
mizunara/filebrowser.c
mizunara/filebrowser.h
mizunara/gtk-filesview.c
mizunara/gtk-filesview.h
mizunara/window.c
mizunara/window.h

index dfa9c89d25762979298dec6cbe639d62ecc80b0e..af25e8bc67d5307ad72de857d308acbb512d9469 100644 (file)
 #define MZ_APPLICATION_H
 
 #include <ui/ui.h>
+#include <cx/list.h>
+
+#include <inttypes.h>
+#include <sys/types.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+typedef struct MainWindow   MainWindow;
+typedef struct FileBrowser  FileBrowser;
+typedef struct FilesSection FilesSection;
+typedef struct FileInfo     FileInfo;
+
+typedef void (*update_files_view_func)(
+        FileBrowser *browser,
+        FilesSection *sections,
+        size_t numsections,
+        void *userdata);
+
 /*
  * main window data
  */
-typedef struct MainWindow {
+struct MainWindow {
+    UiObject *obj;
+    
     /*
      * sidebar default directories
      */
@@ -48,14 +65,39 @@ typedef struct MainWindow {
      * sidebar xdg user directories
      */
     UiList *user_dirs;
-} MainWindow;
+    
+    /*
+     * files icon/grid view
+     */
+    UIWIDGET files_gridview;
+};
 
 /*
  * main window document object
  * later, the MainWindow will contain multiple documents (document tabview)
  */
-typedef struct FileBrowser {
+struct FileBrowser {
     UiContext *ctx;
+    MainWindow *window;
+    
+    /*
+     * current operation number
+     * 
+     * Each background operation (directory loading) gets a new opnum
+     * increased by 1. When the operation is finished, the result is only
+     * added, when the operation opnum is bigger than the FileBrowser opnum.
+     * 
+     * This makes sure, an older operation, that does take much longer for
+     * some reason, doesn't override the result of a newer operation, that was
+     * initiated later.
+     */
+    uint64_t opnum;
+    
+    /*
+     * current path
+     * used by the path textfield
+     */
+    UiString *path;
     
     /*
      * view tabview (icon-grid, list)
@@ -63,15 +105,40 @@ typedef struct FileBrowser {
     UiInteger *view;
     
     /*
-     * gridview file list
+     * update_grid(FileBrowser *, FilesSection *,size_t, void *)
+     * 
+     * file gridview update function
+     */
+    update_files_view_func update_grid;
+    
+    /*
+     * update_grid userdata
      */
-    UiList *grid_files;
+    void *update_grid_data;
     
     /*
      * file listview list
      */
     UiList *list_files;
-} FileBrowser;
+};
+
+/*
+ * When a directory is loaded, the files are grouped into one or multiple
+ * sections
+ */
+struct FilesSection {
+    CxList *files;
+    size_t num_bytes;
+    time_t mtime_from;
+    time_t mtime_to;
+};
+
+struct FileInfo {
+    char *name;
+    size_t size;
+    time_t mtime;
+    mode_t mode;
+};
     
 
 void application_init(void);
index 1a7b216a5d1fd001a95011262df5ba1a486e6480..278717248714c9d031b4ecfa892b71c0ca1ed4b2 100644 (file)
 
 #include "filebrowser.h"
 
+#include <unistd.h>
+#include <dirent.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <cx/array_list.h>
+
 
 FileBrowser* filebrowser_new(const char *url) {
     FileBrowser *browser = ui_document_new(sizeof(FileBrowser));
     UiContext *ctx = ui_document_context(browser);
     browser->ctx = ctx;
     
+    browser->path = ui_string_new(ctx, "path");
     browser->view = ui_int_new(ctx, "view");
     
-    browser->grid_files = ui_list_new(ctx, "grid_files");
     browser->list_files = ui_list_new(ctx, "list_files");
     
     return browser;
 }
+
+void filebrowser_load(FileBrowser *browser, const char *opt_newurl) {
+    const char *url;
+    if(opt_newurl) {
+        url = opt_newurl;
+    } else {
+        url = ui_get(browser->path);
+        if(strlen(url) == 0) {
+            url = "/";
+            ui_set(browser->path, url);
+        }
+    }
+    
+    FileBrowserOp *op = filebrowser_op_new(browser, url);
+    ui_job(browser->window->obj, jobthr_filebrowser_op_load, op, filebrowser_op_finished, op);
+}
+
+FileBrowserOp* filebrowser_op_new(FileBrowser *browser, const char *url) {
+    FileBrowserOp *op = malloc(sizeof(FileBrowserOp));
+    op->browser = browser;
+    op->url = strdup(url);
+    op->opnum = browser->opnum+1;
+    op->result = NULL;
+    op->error = 0;
+    return op;
+}
+
+static void cleanup_file_info(FileInfo *f) {
+    free(f->name);
+}
+
+/*
+ * This function must only be used with ui_job().
+ * 
+ * Always return 0 to make sure, filebrowser_op_finished is called.
+ */
+int jobthr_filebrowser_op_load(void *data) {
+    FileBrowserOp *op = data;
+    printf("jobthr_filebrowser_op_load\n");
+    
+    int fd = open(op->url, O_RDONLY);
+    if(fd < 0) {
+        op->error = errno;
+        return 0;
+    }
+    
+    DIR *dir = fdopendir(fd);
+    if(!dir) {
+        op->error = errno;
+        close(fd);
+        return 0;
+    }
+    
+    op->result = cxArrayListCreateSimple(sizeof(FileInfo), 1024);
+    op->result->collection.simple_destructor = (cx_destructor_func)cleanup_file_info;
+    struct dirent *ent;
+    struct stat s;
+    while((ent = readdir(dir)) != NULL) {
+        if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
+            continue;
+        }
+        
+        FileInfo f;
+        f.name = strdup(ent->d_name);
+        
+        if(!fstatat(fd, ent->d_name, &s, 0)) {
+            f.size = s.st_size;
+            f.mtime = s.st_mtime;
+            f.mode = s.st_mode;
+        } else {
+            f.size = 0;
+            f.mtime = 0;
+            f.mode = 0;
+        }
+        
+        cxListAdd(op->result, &f);
+    }
+    
+    closedir(dir);
+    return 0;
+}
+
+void filebrowser_op_finished(UiEvent *event, void *data) {
+    FileBrowserOp *op = data;
+    printf("filebrowser_op_finished\n");
+    
+    if(op->result) {
+        int view = ui_get(op->browser->view);
+        if(view == 0 && op->browser->update_grid) {   
+            // TODO: group feature
+            // for now, just create a single section and move the result list
+            FilesSection *section = malloc(sizeof(FilesSection));
+            memset(section, 0, sizeof(FilesSection));
+            section->files = op->result;
+            op->result = NULL;
+            
+            // update_grid takes ownership of section
+            op->browser->update_grid(op->browser, section, 1, op->browser->update_grid_data);
+        } else if(view == 1) {
+            
+        }
+    } else {
+        // TODO: error msg
+    }
+    
+    cxListFree(op->result);
+    
+    free(op->url);
+    free(op);
+}
index 482c78ac434cd231e98ab44b4c8474ede44687e6..c5a76afa54904e6f115c7a5f1be4e8b5dc62f865 100644 (file)
 
 #include "application.h"
 
+#include <time.h>
+#include <cx/list.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
-
+    
+/*
+ * FileBrowser directory loading operation
+ * 
+ * Make sure to update filebrowser_op_new when changing this struct!
+ */
+typedef struct FileBrowserOp {
+    FileBrowser *browser;
+    char *url;
+    
+    /*
+     * array type: FileInfo
+     */
+    CxList *result;
+    
+    /*
+     * error number in case result is NULL
+     */
+    int error; 
+    
+    /*
+     * see FileBrowser opnum
+     */
+    uint64_t opnum;
+} FileBrowserOp;
+    
+    
 FileBrowser* filebrowser_new(const char *url);
 
 
+FileBrowserOp* filebrowser_op_new(FileBrowser *browser, const char *url);
+
+/*
+ * Load the current (or new) url
+ */
+void filebrowser_load(FileBrowser *browser, const char *opt_newurl);
+
+int jobthr_filebrowser_op_load(void *data);
+
+void filebrowser_op_finished(UiEvent *event, void *data);
+
+
 #ifdef __cplusplus
 }
 #endif
index 0ce526c7749cae1af6f35ec2f7be9cb6492f4e4f..5299b5ab44639f3c82b440f0a0943b85c7e6c07b 100644 (file)
 
 #include "gtk-filesview.h"
 
-
+#include <sys/stat.h>
+#include <sys/types.h>
 
 
 G_DEFINE_TYPE(MzFilesView, mz_files_view, GTK_TYPE_WIDGET)
 
+
+static void drag_begin_cb(
+        GtkGestureDrag* self,
+        gdouble start_x,
+        gdouble start_y,
+        gpointer user_data);
+static void drag_end_cb(
+        GtkGestureDrag* self,
+        gdouble x,
+        gdouble y,
+        gpointer user_data);
+static void drag_update_cb(
+        GtkGestureDrag* self,
+        gdouble x,
+        gdouble y,
+        gpointer user_data);
+
+
 static void mz_files_view_class_init(MzFilesViewClass *klass) {
     printf("mz_files_view_class_init\n");
-    klass->parent_class.snapshot = mz_files_view_snapshot; 
+    klass->parent_class.snapshot = mz_files_view_snapshot;
 }
 
 static void mz_files_view_init(MzFilesView *self) {
-    self->data = NULL;
+    self->sections = NULL;
+    self->numsections = 0;
+    
+    self->items = NULL;
+    self->numitems = 0;
+    
+    // event handler
+    GtkGesture *drag = gtk_gesture_drag_new();
+    g_signal_connect(drag, "drag-begin", G_CALLBACK(drag_begin_cb), self);
+    g_signal_connect(drag, "drag-end", G_CALLBACK(drag_end_cb), self);
+    g_signal_connect(drag, "drag-update", G_CALLBACK(drag_update_cb), self);
+    gtk_widget_add_controller(GTK_WIDGET(self), GTK_EVENT_CONTROLLER(drag));
 }
 
 MzFilesView* mz_files_view_new(void) {
     MzFilesView *obj = g_object_new(mz_files_view_get_type(), NULL);
-    obj->data = NULL;
     return obj;
 }
 
 void mz_files_view_snapshot(GtkWidget *widget, GtkSnapshot *snapshot) {
-    printf("mz_files_view_snapshot\n");
+    MzFilesView *view = (MzFilesView*)widget;
+    printf("MzFilesView snapshot\n");
+    
+    int width = gtk_widget_get_width(widget);
+    int height = gtk_widget_get_height(widget);
+    
+    int item_width = 80;
+    int item_height = 120;
+    
+    int items_per_line = width / item_width;
     
-    GdkRGBA red;
-    gdk_rgba_parse (&red, "red");
+    if(view->numitems == 0) {
+        return;
+    }
+    
+    GtkAllocation img_alloc;
+    GtkAllocation label_alloc;
+    img_alloc.width = 64;
+    img_alloc.height = 64;
+    label_alloc.width = 76;
+    label_alloc.height = 48;
+    
+    int x = 0;
+    int y = 0;
+    for(size_t i=0;i<view->numitems;i++) {
+        img_alloc.x = 20 + x * item_width;
+        img_alloc.y = 20 + y * item_height;
+        gtk_widget_size_allocate(view->items[i].image, &img_alloc, -1);
+        label_alloc.x = img_alloc.x -1;
+        label_alloc.y = img_alloc.y + 64 + 4;
+        gtk_widget_size_allocate(view->items[i].label, &label_alloc, -1);
+        
+        x++;
+        if(x >= items_per_line) {
+            x = 0;
+            y++;
+        }
+    }
+
+    //gtk_widget_size_allocate(view->items[0].image, &child_allocation, -1);
+    
+    // draw children
+    GtkWidgetClass *cls = mz_files_view_parent_class;
+    cls->snapshot(widget, snapshot);
+}
+
 
-    int w = gtk_widget_get_width(widget);
-    int h = gtk_widget_get_height(widget);
+/* -------------------------- event handler -------------------------- */
 
-    gtk_snapshot_append_color(snapshot, &red, &GRAPHENE_RECT_INIT(0, 0, w, h));
+static void drag_begin_cb(
+        GtkGestureDrag* self,
+        gdouble start_x,
+        gdouble start_y,
+        gpointer user_data)
+{
+    
+}
+
+static void drag_end_cb(
+        GtkGestureDrag* self,
+        gdouble x,
+        gdouble y,
+        gpointer user_data)
+{
+    
+}
+
+static void drag_update_cb(
+        GtkGestureDrag* self,
+        gdouble x,
+        gdouble y,
+        gpointer user_data)
+{
+    
+}
+
+
+/* -------------------------- public -------------------------- */
+
+void mz_update_files_view(MzFilesView *view, FilesSection *sections, size_t numsections) {
+    printf("mz_update_files_view\n");
+    
+    // TODO: free previous files
+    free(view->items);
+    
+    
+    view->sections = calloc(numsections, sizeof(MzFilesView));
+    size_t nchildren = 0;
+    for(size_t i=0;i<numsections;i++) {
+        view->sections[i].section = sections[i];
+        nchildren += cxListSize(sections[i].files);
+    }
+    view->numsections = numsections;
+    
+    GtkIconTheme *icontheme = gtk_icon_theme_get_for_display(gdk_display_get_default());
+    GtkIconPaintable *dir_icon = gtk_icon_theme_lookup_icon(icontheme, "folder", NULL, 64, 1, GTK_TEXT_DIR_LTR, GTK_ICON_LOOKUP_FORCE_REGULAR);
+    GtkIconPaintable *file_icon = gtk_icon_theme_lookup_icon(icontheme, "folder", NULL, 64, 1, GTK_TEXT_DIR_LTR, GTK_ICON_LOOKUP_FORCE_REGULAR);
+    
+    // create widgets
+    view->items = calloc(nchildren, sizeof(MzIconGadget));
+    view->numitems = nchildren;
+    
+    size_t w = 0;
+    for(size_t i=0;i<numsections;i++) {
+        MzViewSection sec = view->sections[i];
+        CxIterator f = cxListIterator(sec.section.files);
+        cx_foreach(FileInfo *, finfo, f) {
+            MzIconGadget gadget;
+            gadget.image = gtk_image_new_from_paintable(GDK_PAINTABLE(S_ISDIR(finfo->mode) ? dir_icon : file_icon));
+            gadget.label = gtk_label_new(finfo->name);
+            
+            gtk_widget_set_parent(gadget.image, GTK_WIDGET(view));
+            gtk_widget_set_parent(gadget.label, GTK_WIDGET(view));
+            
+            view->items[w++] = gadget;
+        }
+    }
+    
+    gtk_widget_queue_draw(GTK_WIDGET(view));
 }
index cb510065de5a283398fd00b87de6803805cfa0a0..f315b22022794fa7bb3950d8f01b154f2577022f 100644 (file)
 #define MZ_GTK_FILEVIEW_H
 
 #include "application.h"
+#include "filebrowser.h"
+
+#include <cx/array_list.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
     
+
+    
+typedef struct MzIconGadget {
+    GtkWidget *image;
+    GtkWidget *label;
+} MzIconGadget;
+  
+typedef struct MzViewSection {
+    FilesSection section;
+    GtkWidget *heading;
+    int x;
+    int height;
+} MzViewSection;
+
 typedef struct MzFilesView {
     GtkWidget widget;
-    void *data;
+    MzIconGadget *items;
+    size_t numitems;
+    
+    MzViewSection *sections;
+    size_t numsections;
 } MzFilesView;
 
 typedef struct MzFilesViewClass {
@@ -48,6 +69,8 @@ MzFilesView* mz_files_view_new(void);
 
 void mz_files_view_snapshot(GtkWidget *widget, GtkSnapshot *snapshot);
 
+void mz_update_files_view(MzFilesView *view, FilesSection *sections, size_t numsections);
+
 #ifdef __cplusplus
 }
 #endif
index 8293e5d066630a62f2945d22606a6185adff4ad0..adb29ac415761d2f678346ececbfffa7b2a60f48 100644 (file)
 #ifdef GTK_MAJOR_VERSION
 #include "gtk-filesview.h"
 #include "bookmarks.h"
+#include "common/context.h"
 #endif
 
+
+/*
+ * maybe the update_files_view_func should be replaced with a UiGeneric wrapper 
+ */
+void window_update_gridview(
+        FileBrowser *browser,
+        FilesSection *sections,
+        size_t numsections,
+        void *userdata)
+{
+    MainWindow *win = userdata;
+    mz_update_files_view((MzFilesView*)win->files_gridview, sections, numsections);
+}
+
 UiObject* window_create(const char *url) {
     // toplevel window object
     UiObject *obj = ui_sidebar_window("Mizunara", NULL);
@@ -45,16 +60,20 @@ UiObject* window_create(const char *url) {
     memset(wdata, 0, sizeof(MainWindow));
     windowdata_init(obj->ctx, wdata);
     obj->window = wdata;
+    wdata->obj = obj;
     
     // create browser document
     // TODO: in the future we want multiple documents per window (tabs)
     FileBrowser *browser = filebrowser_new(url);
     ui_attach_document(obj->ctx, browser);
+    browser->window = wdata;
+    browser->update_grid = window_update_gridview;
+    browser->update_grid_data = wdata;
     
     // add UI
     ui_sidebar0(obj) {  
         UiSubList sublists[] = {
-                { .value = wdata->default_dirs, .userdata = window_sidebar_default_dirs_item },
+                { .value = wdata->default_dirs, .userdata = window_sidebar_user_dirs_item },
                 { .value = wdata->user_dirs, .userdata = window_sidebar_user_dirs_item, .separator = TRUE }
         };
         ui_sourcelist(obj, .sublists = sublists, .numsublists = 2, .getvalue = window_sidebar_getvalue);
@@ -63,7 +82,7 @@ UiObject* window_create(const char *url) {
     ui_hbox(obj, .spacing = 4, .fill = UI_OFF) {
         ui_button(obj, .style_class = "flat", .icon = UI_ICON_GO_BACK);
         ui_button(obj, .style_class = "flat", .icon = UI_ICON_GO_FORWARD);
-        ui_path_textfield(obj, .varname = "path", .fill = UI_ON);
+        ui_path_textfield(obj, .varname = "path", .fill = UI_ON, .onactivate = action_pathbar_activate);
     }
     
     window_create_browser_view(obj, wdata);
@@ -84,7 +103,7 @@ static UIWIDGET create_filesview(UiObject *obj, UiWidgetArgs args, void *userdat
 void window_create_browser_view(UiObject *obj, MainWindow *win) {
     ui_tabview(obj, .tabview = UI_TABVIEW_INVISIBLE, .varname = "view") {
         ui_tab(obj, "iconview") {
-            ui_customwidget(obj, create_filesview, NULL, .fill = UI_ON);
+            win->files_gridview = ui_customwidget(obj, create_filesview, NULL, .fill = UI_ON);
         }
         
         ui_tab(obj, "listview") {
@@ -98,10 +117,7 @@ void windowdata_init(UiContext *ctx, MainWindow *win) {
     win->default_dirs = ui_list_new(ctx, NULL);
     win->user_dirs = ui_list_new(ctx, NULL);
     
-    bookmarks_init_windowdata(win->user_dirs);
-    
-    // TODO: init lists
-    ui_list_append(win->default_dirs, "Test");
+    bookmarks_init_windowdata(win->default_dirs, win->user_dirs);
 }
 
 /*
@@ -114,13 +130,17 @@ void window_sidebar_getvalue(void *sublist_userdata, void *rowdata, int index, U
     getvalue(NULL, rowdata, index, item);
 }
 
-void window_sidebar_default_dirs_item(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item) {
-    item->icon = strdup("user-home-symbolic");
-    item->label = strdup("Home");
-}
-
 void window_sidebar_user_dirs_item(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item) {
     MZBookmark *bookmark = rowdata;
     item->icon = strdup(bookmark->icon);
     item->label = strdup(bookmark->name);
 }
+
+
+
+void action_pathbar_activate(UiEvent *event, void *userdata) {
+    printf("action_pathbar_activate: %s\n", event->eventdata);
+    char *path = event->eventdata;
+    FileBrowser *browser = event->document;
+    filebrowser_load(browser, path);
+}
index d3dd96af801633b00c1a5bb602f6cabbc908284e..d803d17579fbce27ca8f32c4ae94662abb098bb3 100644 (file)
@@ -43,9 +43,10 @@ void windowdata_init(UiContext *ctx, MainWindow *win);
 
 void window_sidebar_getvalue(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item);
 
-void window_sidebar_default_dirs_item(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item);
 void window_sidebar_user_dirs_item(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item);
 
+void action_pathbar_activate(UiEvent *event, void *userdata);
+
 
 #ifdef __cplusplus
 }