aboutsummaryrefslogtreecommitdiff
path: root/src/uplanfahrwin.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uplanfahrwin.c')
-rw-r--r--src/uplanfahrwin.c688
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);
+}