diff options
Diffstat (limited to 'src/uplanfahrwin.c')
-rw-r--r-- | src/uplanfahrwin.c | 688 |
1 files changed, 688 insertions, 0 deletions
diff --git a/src/uplanfahrwin.c b/src/uplanfahrwin.c new file mode 100644 index 0000000..1e4301d --- /dev/null +++ b/src/uplanfahrwin.c @@ -0,0 +1,688 @@ +#include "config.h" + +#include <string.h> + +#include <gtk/gtk.h> +#include <libplanfahr/libplanfahr.h> + +#include "uplanfahr.h" +#include "uplanfahrwin.h" +#include "uplanfahrtripview.h" + +LpfProvider *provider; +LpfManager *manager; + +struct _UPlanFahrWindow +{ + GtkApplicationWindow parent; +}; + +struct _UPlanFahrWindowClass +{ + GtkApplicationWindowClass parent_class; +}; + +typedef struct _UPlanFahrWindowPrivate UPlanFahrWindowPrivate; + +struct _UPlanFahrWindowPrivate +{ + GSettings *settings; + GtkWidget *stack; + GtkWidget *search; + GtkWidget *searchbar; + GtkWidget *searchentry; + GtkWidget *gears; + GtkWidget *sidebar; + GtkWidget *words; + GtkWidget *lines; + GtkWidget *lines_label; + GtkWidget *popover; + GtkWidget *listbox; + GtkWidget *start_radiobutton; + GtkWidget *tripview; + + gboolean fullscreen; + guint searchentry_pulse_id; +}; + +enum { + LPF_STOP_PROP_0 = 0, + U_PLAN_FAHR_WINDOW_PROP_FULLSCREEN, +}; + +G_DEFINE_TYPE_WITH_PRIVATE(UPlanFahrWindow, u_plan_fahr_window, GTK_TYPE_APPLICATION_WINDOW); + + +static void +set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + UPlanFahrWindow *self = U_PLAN_FAHR_WINDOW (object); + UPlanFahrWindowPrivate *priv; + + priv = u_plan_fahr_window_get_instance_private (self); + + switch (property_id) { + case U_PLAN_FAHR_WINDOW_PROP_FULLSCREEN: + priv->fullscreen = g_value_get_boolean (value); + if (priv->fullscreen) + gtk_window_fullscreen (GTK_WINDOW(self)); + else + gtk_window_unfullscreen (GTK_WINDOW(self)); + break; + + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + UPlanFahrWindow *self = U_PLAN_FAHR_WINDOW (object); + UPlanFahrWindowPrivate *priv; + + priv = u_plan_fahr_window_get_instance_private (self); + + switch (property_id) { + case U_PLAN_FAHR_WINDOW_PROP_FULLSCREEN: + g_value_set_boolean (value, priv->fullscreen); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static gboolean +window_key_press_event_cb (GtkWidget *win, + GdkEvent *event, + GtkSearchBar *searchbar) +{ + return gtk_search_bar_handle_event (searchbar, event); +} + + +static void +got_trips (GSList *trips, gpointer user_data, GError *err) +{ + GtkWidget *view = user_data; + + if (err) { + g_warning ("%s", err->message); + return; + } + + g_message ("Found %d trips", g_slist_length (trips)); + + u_plan_fahr_trip_view_store_trips (U_PLAN_FAHR_TRIP_VIEW(view), trips); +} + + +static gboolean +u_planfahr_get_trips_cb (gpointer user_data) +{ + LpfLoc *start, *end; + GDateTime *when; + GtkWidget *view = GTK_WIDGET(user_data); + + start = g_object_get_data (G_OBJECT(view), "start"); + end = g_object_get_data (G_OBJECT(view), "end"); + when = g_date_time_new_now_local (); + + if (start && end && when) { + g_message ("Looking for trip from %s to %s", lpf_loc_get_name (start), lpf_loc_get_name (end)); + lpf_provider_get_trips (provider, start, end, when, 0, got_trips, view); + } else { + g_message("Missing info"); + } + return G_SOURCE_CONTINUE; +} + + +static void +u_planfahr_get_trips (GtkWidget *view) +{ + u_planfahr_get_trips_cb (view); + g_timeout_add_seconds (30, u_planfahr_get_trips_cb, view); +} + + +static void +set_loc (LpfLoc *loc, GtkWidget *view, gboolean is_start) +{ + const char *type = is_start ? "start" : "end"; + + g_object_set_data (G_OBJECT(view), type, loc); +} + + +static LpfLoc* +get_loc (GtkWidget *view, gboolean is_start) +{ + const char *type = is_start ? "start" : "end"; + + return LPF_LOC(g_object_get_data (G_OBJECT(view), type)); +} + + +static void +update_title (UPlanFahrWindowPrivate *priv) +{ + GtkWidget *tab, *view; + LpfLoc *start, *end; + gchar *title; + + tab = gtk_stack_get_visible_child (GTK_STACK (priv->stack)); + view = gtk_bin_get_child (GTK_BIN (tab)); + + start = get_loc (view, TRUE); + end = get_loc (view, FALSE); + + title = g_strdup_printf ("%s ⇨ %s", lpf_loc_get_name(start), lpf_loc_get_name(end)); + + gtk_container_child_set(GTK_CONTAINER(priv->stack), tab, "title", title, NULL); + + g_free (title); +} + + +static void +row_activated (GtkListBox *listbox G_GNUC_UNUSED, GtkListBoxRow *row, gpointer user_data) +{ + UPlanFahrWindowPrivate *priv = user_data; + LpfLoc *loc; + gchar *name; + GtkWidget *tab, *view; + gboolean is_start; + + if (row == NULL) + return; + + is_start = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(priv->start_radiobutton)); + + loc = LPF_LOC(g_object_get_data (G_OBJECT(row), "loc")); + g_return_if_fail (loc != NULL); + + g_object_get (loc, "name", &name, NULL); + g_message ("Row activated %s", name); + gtk_search_bar_set_search_mode (GTK_SEARCH_BAR(priv->searchbar), FALSE); + + tab = gtk_stack_get_visible_child (GTK_STACK (priv->stack)); + view = gtk_bin_get_child (GTK_BIN (tab)); + + set_loc (loc, view, is_start); + update_title (priv); + + u_planfahr_get_trips (view); + + gtk_widget_hide (GTK_WIDGET(priv->popover)); + g_free (name); +} + + +static void +clear_search_popover(UPlanFahrWindowPrivate *priv) +{ + GList *iter, *children; + + children = gtk_container_get_children(GTK_CONTAINER(priv->listbox)); + for(iter = children; iter != NULL; iter = g_list_next(iter)) + gtk_container_remove(GTK_CONTAINER(priv->listbox), GTK_WIDGET(iter->data)); + g_list_free(children); +} + + +static void +popover_closed (GtkPopover *popover, UPlanFahrWindowPrivate *priv) +{ + /* popup closed without selection, back to searchentry */ + gtk_widget_grab_focus (priv->searchentry); + gtk_editable_set_position (GTK_EDITABLE(priv->searchentry), -1); +} + +static gboolean +searchentry_pulse_step(GtkEntry *entry) +{ + gtk_entry_progress_pulse (entry); + return TRUE; +} + + +static void +searchentry_start_progressbar (UPlanFahrWindowPrivate *priv) +{ + gtk_entry_set_progress_pulse_step (GTK_ENTRY(priv->searchentry), 0.25); + searchentry_pulse_step (GTK_ENTRY (priv->searchentry)); + if (!priv->searchentry_pulse_id) + priv->searchentry_pulse_id = g_timeout_add_seconds (1, (GSourceFunc)searchentry_pulse_step, GTK_ENTRY(priv->searchentry)); +} + + +static void +searchentry_stop_progressbar (UPlanFahrWindowPrivate *priv) +{ + if (priv->searchentry_pulse_id) + g_source_remove (priv->searchentry_pulse_id); + gtk_entry_set_progress_pulse_step (GTK_ENTRY(priv->searchentry), 0); + priv->searchentry_pulse_id = 0; +} + + +static void +populate_search_popover(GSList *locs, gpointer user_data, GError *err) +{ + UPlanFahrWindow *win; + UPlanFahrWindowPrivate *priv; + GtkEntry *entry = GTK_ENTRY(user_data); + GtkWidget *label, *row; + LpfLoc *loc; + GSList *iter; + gchar *name; + + win = U_PLAN_FAHR_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (entry))); + priv = u_plan_fahr_window_get_instance_private (win); + + if (err) { + g_warning ("%s", err->message); + return; + } + + g_message("Found %d locs", g_slist_length(locs)); + for (iter = locs; iter; iter = g_slist_next (iter)) { + loc = iter->data; + g_object_get (loc, "name", &name, NULL); + label = gtk_label_new(name); + row = gtk_list_box_row_new (); + g_object_set_data (G_OBJECT(row), "loc", loc); + gtk_container_add (GTK_CONTAINER(row), label); + gtk_list_box_insert (GTK_LIST_BOX(priv->listbox), row, -1); + } + + row = GTK_WIDGET (gtk_list_box_get_row_at_index (GTK_LIST_BOX(priv->listbox), 0)); + gtk_list_box_select_row ( GTK_LIST_BOX(priv->listbox), GTK_LIST_BOX_ROW(row)); + + searchentry_stop_progressbar (priv); + + gtk_widget_show_all (priv->popover); +} + + +static void +trigger_populate_search_popover(UPlanFahrWindowPrivate *priv, const gchar *text) +{ + g_message ("Searching for %s", text); + lpf_provider_get_locs (provider, text, 0, populate_search_popover, priv->searchentry); + searchentry_start_progressbar (priv); +} + + +static void +searchentry_text_changed (GtkEntry *entry) +{ + UPlanFahrWindow *win; + UPlanFahrWindowPrivate *priv; + const gchar *text; + + win = U_PLAN_FAHR_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (entry))); + priv = u_plan_fahr_window_get_instance_private (win); + + clear_search_popover(priv); + text = gtk_entry_get_text (entry); + + if (text[0] == '\0') { + gtk_widget_hide (priv->popover); + return; + } + + searchentry_start_progressbar (priv); + + trigger_populate_search_popover (priv, text); +} + + +static void +update_words (UPlanFahrWindow *win) +{ +#if 0 + UPlanFahrWindowPrivate *priv; + GHashTable *strings; + GHashTableIter iter; + GtkWidget *tab, *view, *row; + GtkTextBuffer *buffer; + GtkTextIter start, end; + GList *children, *l; + gchar *word, *key; + + priv = u_plan_fahr_window_get_instance_private (win); + + tab = gtk_stack_get_visible_child (GTK_STACK (priv->stack)); + + if (tab == NULL) + return; + + view = gtk_bin_get_child (GTK_BIN (tab)); + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + strings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + gtk_text_buffer_get_start_iter (buffer, &start); + while (!gtk_text_iter_is_end (&start)) + { + while (!gtk_text_iter_starts_word (&start)) + { + if (!gtk_text_iter_forward_char (&start)) + goto done; + } + end = start; + if (!gtk_text_iter_forward_word_end (&end)) + goto done; + word = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + g_hash_table_add (strings, g_utf8_strdown (word, -1)); + g_free (word); + start = end; + } + + done: + children = gtk_container_get_children (GTK_CONTAINER (priv->words)); + for (l = children; l; l = l->next) + gtk_container_remove (GTK_CONTAINER (priv->words), GTK_WIDGET (l->data)); + g_list_free (children); + + g_hash_table_iter_init (&iter, strings); + while (g_hash_table_iter_next (&iter, (gpointer *)&key, NULL)) + { + row = gtk_button_new_with_label (key); + g_signal_connect (row, "clicked", + G_CALLBACK (find_word), win); + gtk_widget_show (row); + gtk_container_add (GTK_CONTAINER (priv->words), row); + } + + g_hash_table_unref (strings); +#endif +} + + +static void +update_lines (UPlanFahrWindow *win) +{ +#if 0 + UPlanFahrWindowPrivate *priv; + GtkWidget *tab, *view; + GtkTextBuffer *buffer; + GtkTextIter iter; + int count; + gchar *lines; + + priv = u_plan_fahr_window_get_instance_private (win); + + tab = gtk_stack_get_visible_child (GTK_STACK (priv->stack)); + + if (tab == NULL) + return; + + view = gtk_bin_get_child (GTK_BIN (tab)); + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + count = 0; + + gtk_text_buffer_get_start_iter (buffer, &iter); + while (!gtk_text_iter_is_end (&iter)) + { + count++; + if (!gtk_text_iter_forward_line (&iter)) + break; + } + + lines = g_strdup_printf ("%d", count); + gtk_label_set_text (GTK_LABEL (priv->lines), lines); + g_free (lines); +#endif +} + + +static void +visible_child_changed (GObject *stack, + GParamSpec *pspec) +{ +#if 0 + UPlanFahrWindow *win; + UPlanFahrWindowPrivate *priv; + + if (gtk_widget_in_destruction (GTK_WIDGET (stack))) + return; + + win = U_PLAN_FAHR_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (stack))); + priv = u_plan_fahr_window_get_instance_private (win); + gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->searchbar), FALSE); + update_words (win); + update_lines (win); +#endif +} + + +static void +words_changed (GObject *sidebar, + GParamSpec *pspec, + UPlanFahrWindow *win) +{ + update_words (win); +} + + +static void +u_plan_fahr_window_init (UPlanFahrWindow *win) +{ + UPlanFahrWindowPrivate *priv; + GtkBuilder *builder; + GMenuModel *menu; + GAction *action; + + priv = u_plan_fahr_window_get_instance_private (win); + gtk_widget_init_template (GTK_WIDGET (win)); + priv->settings = g_settings_new ("org.sigxcpu.uplanfahr"); + + g_settings_bind (priv->settings, "fullscreen", + win, "fullscreen", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind (priv->settings, "show-words", + priv->sidebar, "reveal-child", + G_SETTINGS_BIND_DEFAULT); + + g_object_bind_property (priv->search, "active", + priv->searchbar, "search-mode-enabled", + G_BINDING_BIDIRECTIONAL); + + g_signal_connect (priv->sidebar, "notify::reveal-child", + G_CALLBACK (words_changed), win); + + builder = gtk_builder_new_from_resource ("/org/sigxcpu/uplanfahr/gears-menu.ui"); + menu = G_MENU_MODEL (gtk_builder_get_object (builder, "menu")); + gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (priv->gears), menu); + g_object_unref (builder); + + action = g_settings_create_action (priv->settings, "show-words"); + g_action_map_add_action (G_ACTION_MAP (win), action); + g_object_unref (action); + + action = (GAction*) g_property_action_new ("show-lines", priv->lines, "visible"); + g_action_map_add_action (G_ACTION_MAP (win), action); + g_object_unref (action); + + g_object_bind_property (priv->lines, "visible", + priv->lines_label, "visible", + G_BINDING_DEFAULT); + + gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE); + + /* search bar and popup */ + priv->listbox = gtk_list_box_new (); + + priv->popover = gtk_popover_new (GTK_WIDGET (priv->searchentry)); + gtk_style_context_add_class (gtk_widget_get_style_context (priv->popover), + GTK_STYLE_CLASS_OSD); + gtk_popover_set_position (GTK_POPOVER(priv->popover), GTK_POS_BOTTOM); + gtk_popover_set_modal (GTK_POPOVER (priv->popover), FALSE); + gtk_container_add (GTK_CONTAINER (priv->popover), + priv->listbox); + gtk_container_set_border_width (GTK_CONTAINER (priv->popover), 4); + + g_signal_connect (priv->popover, "closed", + G_CALLBACK (popover_closed), priv); + + g_signal_connect (priv->listbox, "row-activated", + G_CALLBACK (row_activated), priv); + + g_signal_connect (win, "key-press-event", + G_CALLBACK (window_key_press_event_cb), priv->searchbar); + + gtk_widget_show (priv->listbox); +} + + +static void +u_plan_fahr_window_dispose (GObject *object) +{ + UPlanFahrWindow *win; + UPlanFahrWindowPrivate *priv; + + win = U_PLAN_FAHR_WINDOW (object); + priv = u_plan_fahr_window_get_instance_private (win); + + g_clear_object (&priv->settings); + + G_OBJECT_CLASS (u_plan_fahr_window_parent_class)->dispose (object); +} + + +static void +u_plan_fahr_window_class_init (UPlanFahrWindowClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->dispose = u_plan_fahr_window_dispose; + + gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), + "/org/sigxcpu/uplanfahr/window.ui"); + + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), UPlanFahrWindow, stack); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), UPlanFahrWindow, search); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), UPlanFahrWindow, searchbar); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), UPlanFahrWindow, searchentry); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), UPlanFahrWindow, gears); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), UPlanFahrWindow, words); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), UPlanFahrWindow, sidebar); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), UPlanFahrWindow, lines); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), UPlanFahrWindow, lines_label); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), UPlanFahrWindow, start_radiobutton); + + gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), searchentry_text_changed); + gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), visible_child_changed); + + object_class->set_property = set_property; + object_class->get_property = get_property; + + g_object_class_install_property (object_class, + U_PLAN_FAHR_WINDOW_PROP_FULLSCREEN, + g_param_spec_boolean ("fullscreen", + "Fullscreen", + "Fullscreen window", + FALSE, + G_PARAM_READWRITE)); +} + +UPlanFahrWindow * +u_plan_fahr_window_new (UPlanFahr *app) +{ + UPlanFahrWindow *win; + + win = g_object_new (U_PLAN_FAHR_WINDOW_TYPE, "application", app, NULL); + + return win; +} + + + + +static void +initial_got_start (GSList *locs, gpointer user_data, GError *err) +{ + GtkWidget *view = GTK_WIDGET(user_data); + + if (err) { + g_warning ("%s", err->message); + return; + } + + set_loc (locs->data, view, TRUE); +} + + +static void +initial_got_end (GSList *locs, gpointer user_data, GError *err) +{ + GtkWidget *view = GTK_WIDGET(user_data); + + if (err) { + g_warning ("%s", err->message); + return; + } + + set_loc (locs->data, view, FALSE); + u_planfahr_get_trips (view); +} + + +void +u_plan_fahr_window_open (UPlanFahrWindow *win, + GFile *from, + GFile *to) +{ + UPlanFahrWindowPrivate *priv; + GtkWidget *scrolled, *view; + gchar *fromname, *toname, *title; + + priv = u_plan_fahr_window_get_instance_private (win); + + /* use the first two args as from and to */ + fromname = g_file_get_basename(from); + toname = g_file_get_basename(to); + + title = g_strdup_printf ("%s ⇨ %s", fromname, toname); + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolled); + gtk_widget_set_hexpand (scrolled, TRUE); + gtk_widget_set_vexpand (scrolled, TRUE); + view = u_plan_fahr_trip_view_new (); + gtk_widget_show (view); + + gtk_container_add (GTK_CONTAINER (scrolled), view); + gtk_stack_add_titled (GTK_STACK (priv->stack), scrolled, title, title); + g_free (title); + + /* FIXME: move out and fill initial data */ + manager = lpf_manager_new(); + provider = lpf_manager_activate_provider(manager, "de-db", NULL); + lpf_provider_get_locs(provider, fromname, 0, initial_got_start, view); + lpf_provider_get_locs(provider, toname, 0, initial_got_end, view); + + gtk_widget_set_sensitive (priv->search, TRUE); + + update_words (win); + update_lines (win); + + g_free (title); +} |