#include "config.h" #include #include #include #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); }