aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Program.c68
-rw-r--r--src/main.c147
2 files changed, 215 insertions, 0 deletions
diff --git a/src/Program.c b/src/Program.c
new file mode 100644
index 0000000..526f64b
--- /dev/null
+++ b/src/Program.c
@@ -0,0 +1,68 @@
+#include "../include/Program.h"
+#include <unistd.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#define READ_END 0
+#define WRITE_END 1
+
+int exec_program(char **args, ProgramOutputCallback output_callback, void *userdata) {
+ /* 1 arguments */
+ if(args[0] == NULL)
+ return -1;
+
+ int fd[2];
+ if(pipe(fd) == -1) {
+ perror("Failed to open pipe");
+ return -2;
+ }
+
+ pid_t pid = fork();
+ if(pid == -1) {
+ perror("Failed to fork");
+ return -3;
+ } else if(pid == 0) { /* child */
+ dup2(fd[WRITE_END], STDOUT_FILENO);
+ close(fd[READ_END]);
+ close(fd[WRITE_END]);
+
+ execv(args[0], args);
+ return 0;
+ } else { /* parent */
+ close(fd[WRITE_END]);
+
+ int status;
+ waitpid(pid, &status, 0);
+ if(!WIFEXITED(status))
+ return -4;
+
+ int exit_status = WEXITSTATUS(status);
+ if(exit_status != 0) {
+ fprintf(stderr, "Failed to execute program, exit status %d\n", exit_status);
+ return -exit_status;
+ }
+
+ char buffer[2048];
+ int result = 0;
+
+ for(;;) {
+ ssize_t bytes_read = read(fd[READ_END], buffer, sizeof(buffer));
+ if(bytes_read == 0) {
+ break;
+ } else if(bytes_read == -1) {
+ int err = errno;
+ fprintf(stderr, "Failed to read from pipe to program %s, error: %s\n", args[0], strerror(err));
+ result = -err;
+ break;
+ }
+
+ if(output_callback(buffer, bytes_read, userdata) != 0)
+ break;
+ }
+
+ close(fd[READ_END]);
+ return result;
+ }
+} \ No newline at end of file
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..a909d28
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,147 @@
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <quickmedia/HtmlSearch.h>
+
+static GtkWidget *window = NULL;
+static GtkWidget *search_entry = NULL;
+static GtkWidget *list = NULL;
+/* TODO: Optimize this. There shouldn't be a need to copy the whole buffer everytime it's modified */
+static gchar *search_text = NULL;
+
+typedef struct {
+ const char *data;
+ size_t size;
+} StringView;
+
+typedef enum {
+ DATA_INVALID,
+ DATA_TITLE,
+ DATA_DESCRIPTION,
+ DATA_IMAGE
+} DataType;
+
+static GtkLabel* get_list_item_title(GtkListBoxRow *row) {
+ GtkWidget *child_widget = GTK_WIDGET(gtk_container_get_children(GTK_CONTAINER(row))->data);
+ return GTK_LABEL(gtk_grid_get_child_at(GTK_GRID(child_widget), 0, 0));
+}
+
+static gboolean focus_out_callback(GtkWidget *widget, GdkEvent *event, gpointer userdata) {
+ gtk_widget_destroy(window);
+ return FALSE;
+}
+
+static gboolean filter_func(GtkListBoxRow *row, gpointer userdata) {
+ GtkLabel *row_title_label = get_list_item_title(row);
+ gboolean show = !search_text || strlen(search_text) == 0 || strstr(gtk_label_get_text(row_title_label), search_text) != NULL;
+ if(!show && gtk_list_box_get_selected_row(GTK_LIST_BOX(list)) == row) {
+ gtk_list_box_unselect_row(GTK_LIST_BOX(list), row);
+ }
+ return show;
+}
+
+static void list_move_select(GtkListBox *list, gint direction) {
+ GtkListBoxRow *row = gtk_list_box_get_selected_row(GTK_LIST_BOX(list));
+ if(!row)
+ return;
+
+ gint current_row_index = gtk_list_box_row_get_index(row);
+ GtkListBoxRow *new_row = gtk_list_box_get_row_at_index(GTK_LIST_BOX(list), current_row_index + direction);
+ if(new_row)
+ gtk_list_box_select_row(GTK_LIST_BOX(list), new_row);
+}
+
+static gboolean keypress_callback(GtkWidget *widget, GdkEventKey *event, gpointer userdata) {
+ if(event->keyval == GDK_KEY_BackSpace) {
+ gtk_editable_delete_text(GTK_EDITABLE(search_entry), 0, -1);
+ } else if(event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
+ GtkListBoxRow *row = gtk_list_box_get_selected_row(GTK_LIST_BOX(list));
+ if(row) {
+ if(gtk_widget_is_visible(GTK_WIDGET(row))) {
+ GtkLabel *row_title_label = get_list_item_title(row);
+ puts(gtk_label_get_text(row_title_label));
+ } else {
+ puts(gtk_entry_get_text(GTK_ENTRY(search_entry)));
+ }
+ }
+ gtk_widget_destroy(window);
+ return FALSE;
+ } else if(event->keyval == GDK_KEY_Escape) {
+ gtk_widget_destroy(window);
+ return FALSE;
+ } else if(event->keyval == GDK_KEY_Up) {
+ list_move_select(GTK_LIST_BOX(list), -1);
+ return FALSE;
+ } else if(event->keyval == GDK_KEY_Down) {
+ list_move_select(GTK_LIST_BOX(list), 1);
+ return FALSE;
+ } else {
+ gint position = -1;
+ gtk_editable_insert_text(GTK_EDITABLE(search_entry), event->string, -1, &position);
+ //printf("key press %d\n", event->keyval);
+ }
+
+ g_free(search_text);
+ search_text = gtk_editable_get_chars(GTK_EDITABLE(search_entry), 0, -1);
+ gtk_list_box_invalidate_filter(GTK_LIST_BOX(list));
+ return TRUE;
+}
+
+static GtkWidget* create_entry(const char *text, const char *description) {
+ GtkWidget *entry = gtk_grid_new();
+ GtkWidget *label_widget = gtk_label_new(text);
+ gtk_grid_attach(GTK_GRID(entry), label_widget, 0, 0, 1, 1);
+ GtkWidget *description_widget = gtk_label_new(description);
+ gtk_grid_attach(GTK_GRID(entry), description_widget, 1, 0, 1, 1);
+ return entry;
+}
+
+static void activate(GtkApplication *app, gpointer userdata) {
+ window = gtk_application_window_new(app);
+ gtk_window_set_title(GTK_WINDOW(window), "QuickMedia");
+ gtk_window_set_default_size(GTK_WINDOW(window), 600, 300);
+
+ GtkWidget *grid = gtk_grid_new();
+ gtk_container_add(GTK_CONTAINER(window), grid);
+
+ search_entry = gtk_search_entry_new();
+ gtk_widget_set_sensitive(search_entry, FALSE);
+ GdkRGBA text_color;
+ gdk_rgba_parse(&text_color, "black");
+ gtk_widget_override_color(search_entry, GTK_STATE_FLAG_INSENSITIVE, &text_color);
+ gtk_widget_set_hexpand(search_entry, TRUE);
+ gtk_grid_attach(GTK_GRID(grid), search_entry, 0, 0, 1, 1);
+
+ GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_grid_attach(GTK_GRID(grid), scrolled_window, 0, 1, 1, 1);
+
+ list = gtk_list_box_new();
+ gtk_widget_set_hexpand(list, TRUE);
+ gtk_widget_set_vexpand(list, TRUE);
+ gtk_list_box_set_filter_func(GTK_LIST_BOX(list), filter_func, NULL, NULL);
+ gtk_container_add(GTK_CONTAINER(scrolled_window), list);
+
+ for(int i = 0; i < 100; ++i) {
+ GtkWidget *item = create_entry("hello, world!", "description");
+ gtk_list_box_insert(GTK_LIST_BOX(list), item, i);
+ }
+
+ gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
+ gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS);
+ gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
+ gtk_widget_show_all(window);
+
+ gtk_widget_add_events(window, GDK_KEY_PRESS_MASK | GDK_FOCUS_CHANGE_MASK);
+ g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(keypress_callback), NULL);
+ g_signal_connect(G_OBJECT(window), "focus-out-event", G_CALLBACK(focus_out_callback), NULL);
+}
+
+int main(int argc, char **argv) {
+ GtkApplication *app = gtk_application_new("org.dec05eba.quickmedia", G_APPLICATION_FLAGS_NONE);
+ g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
+ int status = g_application_run(G_APPLICATION(app), argc, argv);
+ g_object_unref(app);
+ return status;
+}