[gnome-maps/wip/mlundblad/transit-routing: 23/33] sidebar: Add functionallity for transit routing
- From: Marcus Lundblad <mlundblad src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-maps/wip/mlundblad/transit-routing: 23/33] sidebar: Add functionallity for transit routing
- Date: Tue, 29 Nov 2016 22:47:43 +0000 (UTC)
commit de1270bc24ec2221b6b9147643b46cc34ce75b25
Author: Marcus Lundblad <ml update uu se>
Date: Thu Mar 17 21:34:11 2016 +0100
sidebar: Add functionallity for transit routing
Adds a new mode button (enabled when the service file
indicates an OpenTripPlanner instance, or if explicitly
pointed out via an env variable).
Adds showing transit itineraries in addition to regular
turn-by-turn-based routes, and options for transit route
search.
https://bugzilla.gnome.org/show_bug.cgi?id=755808
data/gnome-maps.css | 5 +
data/ui/sidebar.ui | 453 +++++++++++++++++++++++++++++++++++++++++++++++---
src/sidebar.js | 459 ++++++++++++++++++++++++++++++++++++++++++++++----
3 files changed, 855 insertions(+), 62 deletions(-)
---
diff --git a/data/gnome-maps.css b/data/gnome-maps.css
index 5f500fe..bde40bf 100644
--- a/data/gnome-maps.css
+++ b/data/gnome-maps.css
@@ -86,6 +86,11 @@
color: rgb(0, 0, 0);
}
+.shaded {
+ background-color: alpha(black, 0.1);
+ border-bottom: 1px solid alpha(black, 0.2);
+}
+
.route-label {
padding-left: 2px;
padding-right: 2px;
diff --git a/data/ui/sidebar.ui b/data/ui/sidebar.ui
index 77347ed..6f44286 100644
--- a/data/ui/sidebar.ui
+++ b/data/ui/sidebar.ui
@@ -94,6 +94,28 @@
</style>
</object>
</child>
+ <child>
+ <object class="GtkRadioButton" id="modeTransitToggle">
+ <property name="name">mode-transit-toggle</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="draw_indicator">False</property>
+ <property name="group">modeBikeToggle</property>
+ <property name="height-request">32</property>
+ <property name="width-request">42</property>
+ <child>
+ <object class="GtkImage" id="mode-transit-image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon-name">route-transit-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="transportation-mode-button"/>
+ </style>
+ </object>
+ </child>
<style>
<class name="linked"/>
</style>
@@ -140,55 +162,436 @@
</object>
</child>
<child>
- <object class="GtkStack" id="instructionStack">
+ <object class="GtkGrid">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="valign">fill</property>
+ <property name="vexpand">True</property>
+ <property name="hexpand_set">True</property>
+ <style>
+ <class name="frame"/>
+ </style>
+ <child>
+ <object class="GtkRevealer" id="transitRevealer">
+ <child>
+ <object class="GtkStack" id="transitHeader">
+ <property name="visible">True</property>
+ <property name="transition-type">GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT</property>
+ <child>
+ <object class="GtkGrid" id="transitOptionsGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="no-show-all">True</property>
+ <style>
+ <class name="shaded"/>
+ </style>
+ <child>
+ <object class="GtkComboBoxText"
+ id="transitTimeOptionsComboBox">
+ <property name="visible">True</property>
+ <property name="active_id">leaveNow</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="margin_top">4</property>
+ <property name="margin_bottom">4</property>
+ <items>
+ <item translatable="yes" id="leaveNow">Leave Now</item>
+ <item translatable="yes" id="leaveBy">Leave By</item>
+ <item translatable="yes" id="arriveBy">Arrive By</item>
+ </items>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="transitTimeEntry">
+ <property name="visible">False</property>
+ <property name="width_chars">5</property>
+ <property name="margin_start">3</property>
+ <property name="margin_end">3</property>
+ <property name="margin_top">4</property>
+ <property name="margin_bottom">4</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="transitDateButton">
+ <property name="visible">False</property>
+ <property name="popover">transitDatePopover</property>
+ <property name="margin_start">3</property>
+ <property name="margin_end">3</property>
+ <property name="margin_top">4</property>
+ <property name="margin_bottom">4</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="transitParametersMenuButton">
+ <property name="visible">True</property>
+ <property name="popover">transitParametersPopover</property>
+ <property name="halign">GTK_ALIGN_END</property>
+ <property name="margin_start">3</property>
+ <property name="margin_end">6</property>
+ <property name="margin_top">4</property>
+ <property name="margin_bottom">4</property>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="valign">GTK_ALIGN_CENTER</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">GTK_ALIGN_CENTER</property>
+ <property name="hexpand">True</property>
+ <property name="icon-name">view-more-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="name">options</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="transitItineraryHeader">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <style>
+ <class name="shaded"/>
+ </style>
+ <child>
+ <object class="GtkButton" id="transitItineraryBackButton">
+ <property name="visible">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="margin-top">4</property>
+ <property name="margin-bottom">4</property>
+ <property name="halign">GTK_ALIGN_START</property>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="valign">GTK_ALIGN_CENTER</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">False</property>
+ <property name="icon-name">go-previous-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="transitItineraryTimeLabel">
+ <property name="visible">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="margin-top">4</property>
+ <property name="margin-bottom">4</property>
+ <property name="hexpand">False</property>
+ <property name="halign">GTK_ALIGN_START</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="transitItineraryDurationLabel">
+ <property name="visible">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="margin-top">4</property>
+ <property name="margin-bottom">4</property>
+ <property name="hexpand">True</property>
+ <property name="halign">GTK_ALIGN_START</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left-attach">2</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="name">itinerary-header</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
<child>
- <object class="GtkScrolledWindow" id="instructionWindow">
- <property name="name">instruction-window</property>
+ <object class="GtkStack" id="instructionStack">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="valign">fill</property>
- <property name="vexpand">True</property>
- <property name="margin">1</property>
- <property name="hscrollbar_policy">never</property>
<child>
- <object class="GtkListBox" id="instructionList">
- <property name="name">instruction-list</property>
+ <object class="GtkScrolledWindow" id="instructionWindow">
+ <property name="name">instruction-window</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">fill</property>
- <style>
- <class name="frame"/>
- </style>
+ <property name="vexpand">True</property>
+ <property name="margin">1</property>
+ <property name="hscrollbar_policy">never</property>
+ <child>
+ <object class="GtkListBox" id="instructionList">
+ <property name="name">instruction-list</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">fill</property>
+ <property name="hexpand">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="transitWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">fill</property>
+ <property name="vexpand">True</property>
+ <property name="margin">1</property>
+ <property name="hscrollbar_policy">never</property>
+ <child>
+ <object class="GtkStack" id="transitListStack">
+ <property name="visible">True</property>
+ <property
name="transition-type">GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT</property>
+ <child>
+ <object class="GtkListBox" id="transitOverviewListBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ </object>
+ <packing>
+ <property name="name">overview</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkListBox" id="transitItineraryListBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="selection-mode">GTK_SELECTION_NONE</property>
+ </object>
+ <packing>
+ <property name="name">itinerary</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinner" id="instructionSpinner">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="active">True</property>
</object>
</child>
</object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
</child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="linkButtonStack">
<child>
- <object class="GtkSpinner" id="instructionSpinner">
+ <object class="GtkLinkButton" id="graphHopperLinkButton">
+ <property name="label" translatable="yes">Route search by GraphHopper</property>
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="active">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="relief">none</property>
+ <property name="uri">https://graphhopper.com</property>
+ <style>
+ <class name="small-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="name">graphHopper</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="halign">GTK_ALIGN_END</property>
+ <child>
+ <object class="GtkLinkButton" id="openTripPlannerLinkButton">
+ <property name="label" translatable="yes">Route search by OpenTripPlanner</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="relief">none</property>
+ <!-- opentripplanner.org uses an SSL cert only valid for github
+ domains... -->
+ <property name="uri">http://www.opentripplanner.org</property>
+ <style>
+ <class name="small-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkMenuButton">
+ <property name="visible">True</property>
+ <property name="popover">openTripPlannerDisclaimerPopover</property>
+ <property name="halign">GTK_ALIGN_END</property>
+ <property name="margin-top">5</property>
+ <property name="margin-bottom">5</property>
+ <property name="margin-end">5</property>
+ <property name="margin-start">5</property>
+ <style>
+ <class name="flat"/>
+ </style>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="valign">GTK_ALIGN_CENTER</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">GTK_ALIGN_CENTER</property>
+ <property name="hexpand">False</property>
+ <property name="icon-name">dialog-information-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
</object>
+ <packing>
+ <property name="name">openTripPlanner</property>
+ </packing>
</child>
</object>
</child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkPopover" id="transitDatePopover">
+ <property name="visible">False</property>
+ <child>
+ <object class="GtkCalendar" id="transitDateCalendar">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkPopover" id="transitParametersPopover">
+ <property name="visible">False</property>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="margin">6</property>
+ <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
- <object class="GtkLinkButton" id="linkbutton1">
- <property name="label" translatable="yes">Route search by GraphHopper</property>
+ <object class="GtkLabel">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
- <property name="relief">none</property>
- <property name="uri">https://graphhopper.com</property>
+ <property name="halign">GTK_ALIGN_START</property>
+ <property name="label" translatable="yes">Show</property>
+ <property name="margin_start">6</property>
<style>
- <class name="small-label"/>
+ <class name="dim-label"/>
</style>
</object>
</child>
+ <child>
+ <object class="GtkCheckButton" id="busCheckButton">
+ <property name="visible">True</property>
+ <property name="active">True</property>
+ <property name="label" translatable="yes">Buses</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="tramCheckButton">
+ <property name="visible">True</property>
+ <property name="active">True</property>
+ <property name="label" translatable="yes">Trams</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="trainCheckButton">
+ <property name="visible">True</property>
+ <property name="active">True</property>
+ <property name="label" translatable="yes">Trains</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="subwayCheckButton">
+ <property name="visible">True</property>
+ <property name="active">True</property>
+ <property name="label" translatable="yes">Subway</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="ferryCheckButton">
+ <property name="visible">True</property>
+ <property name="active">True</property>
+ <property name="label" translatable="yes">Ferries</property>
+ </object>
+ </child>
</object>
</child>
- </template>
+ </object>
+ <object class="GtkPopover" id="openTripPlannerDisclaimerPopover">
+ <property name="visible">False</property>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="margin-top">5</property>
+ <property name="margin-bottom">5</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Routing itineraries for public transit is provided by
GNOME
+using timetable data obtained from transit companies or agencies.
+The companies and agencies can not be held responsible for the results shown.
+GNOME can not guarantee correctness of the itineraries and schedules shown.
+Names and brands shown are to be considered as registred trademarks when applicable.</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
</interface>
diff --git a/src/sidebar.js b/src/sidebar.js
index 64ec5e6..57900e2 100644
--- a/src/sidebar.js
+++ b/src/sidebar.js
@@ -20,8 +20,11 @@
* Mattias Bengtsson <mattias jc bengtsson gmail com>
*/
+const C_ = imports.gettext.dgettext;
const Cairo = imports.cairo;
const Gdk = imports.gi.Gdk;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
@@ -33,8 +36,20 @@ const PlaceStore = imports.placeStore;
const RouteEntry = imports.routeEntry;
const RouteQuery = imports.routeQuery;
const StoredRoute = imports.storedRoute;
+const TransitArrivalRow = imports.transitArrivalRow;
+const TransitItineraryRow = imports.transitItineraryRow;
+const TransitLegRow = imports.transitLegRow;
+const TransitMoreRow = imports.transitMoreRow;
+const TransitOptions = imports.transitOptions;
+const TransitPlan = imports.transitPlan;
const Utils = imports.utils;
+// in org.gnome.desktop.interface
+const CLOCK_FORMAT_KEY = 'clock-format';
+
+let _desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
+let clockFormat = _desktopSettings.get_string(CLOCK_FORMAT_KEY);
+
const Sidebar = new Lang.Class({
Name: 'Sidebar',
Extends: Gtk.Revealer,
@@ -48,40 +63,69 @@ const Sidebar = new Lang.Class({
'modeBikeToggle',
'modeCarToggle',
'modePedestrianToggle',
- 'timeInfo' ],
+ 'modeTransitToggle',
+ 'timeInfo',
+ 'linkButtonStack',
+ 'transitWindow',
+ 'transitRevealer',
+ 'transitHeader',
+ 'transitTimeOptionsComboBox',
+ 'transitTimeEntry',
+ 'transitDateButton',
+ 'transitDateCalendar',
+ 'transitParametersMenuButton',
+ 'transitListStack',
+ 'transitOverviewListBox',
+ 'transitItineraryListBox',
+ 'transitItineraryBackButton',
+ 'transitItineraryTimeLabel',
+ 'transitItineraryDurationLabel',
+ 'busCheckButton',
+ 'tramCheckButton',
+ 'trainCheckButton',
+ 'subwayCheckButton',
+ 'ferryCheckButton' ],
_init: function(mapView) {
this.parent({ transition_type: Gtk.RevealerTransitionType.SLIDE_LEFT });
this._mapView = mapView;
+ this._query = Application.routeQuery;
this._initInstructionList();
-
+ this._initTransitOptions();
this._initTransportationToggles(this._modePedestrianToggle,
this._modeBikeToggle,
- this._modeCarToggle);
- this._initQuerySignals();
+ this._modeCarToggle,
+ this._modeTransitToggle);
- let query = Application.routeService.query;
-
- query.addPoint(0);
- query.addPoint(1);
+ this._initQuerySignals();
+ this._query.addPoint(0);
+ this._query.addPoint(1);
+ this._switchRoutingMode(RouteQuery.Transportation.CAR);
+ /* enable/disable transit mode switch based on the presence of
+ * OpenTripPlanner */
+ this._modeTransitToggle.sensitive = Application.openTripPlanner.enabled;
},
- _initTransportationToggles: function(pedestrian, bike, car) {
- let query = Application.routeService.query;
+ _initTransportationToggles: function(pedestrian, bike, car, transit) {
let transport = RouteQuery.Transportation;
let onToggle = function(mode, button) {
- if (button.active && query.transportation !== mode)
- query.transportation = mode;
+ let previousMode = this._query.transportation;
+
+ if (button.active && previousMode !== mode) {
+ this._switchRoutingMode(mode);
+ this._query.transportation = mode;
+ }
};
pedestrian.connect('toggled', onToggle.bind(this, transport.PEDESTRIAN));
car.connect('toggled', onToggle.bind(this, transport.CAR));
bike.connect('toggled', onToggle.bind(this, transport.BIKE));
+ transit.connect('toggled', onToggle.bind(this, transport.TRANSIT))
let setToggles = function() {
- switch(query.transportation) {
+ switch(Application.routeQuery.transportation) {
case transport.PEDESTRIAN:
pedestrian.active = true;
break;
@@ -91,21 +135,39 @@ const Sidebar = new Lang.Class({
case transport.BIKE:
bike.active = true;
break;
+ case transport.TRANSIT:
+ transit.active = true;
+ break;
}
+
+ this._switchRoutingMode(Application.routeQuery.transportation);
};
- setToggles();
- query.connect('notify::transportation', setToggles);
+ setToggles.bind(this)();
+ this._query.connect('notify::transportation', setToggles.bind(this));
},
- _initQuerySignals: function() {
- let query = Application.routeService.query;
+ _switchRoutingMode: function(mode) {
+ if (mode === RouteQuery.Transportation.TRANSIT) {
+ Application.setTransitRouting(true);
+ this._linkButtonStack.visible_child_name = 'openTripPlanner';
+ this._resetTransitOptions();
+ this._transitRevealer.reveal_child = true;
+ this._clearInstructions();
+ } else {
+ Application.setTransitRouting(false);
+ this._linkButtonStack.visible_child_name = 'graphHopper';
+ this._transitRevealer.reveal_child = false;
+ Application.openTripPlanner.plan.deselectItinerary();
+ }
+ },
- query.connect('point-added', (function(obj, point, index) {
+ _initQuerySignals: function() {
+ this._query.connect('point-added', (function(obj, point, index) {
this._createRouteEntry(index, point);
}).bind(this));
- query.connect('point-removed', (function(obj, point, index) {
+ this._query.connect('point-removed', (function(obj, point, index) {
let row = this._entryList.get_row_at_index(index);
row.destroy();
}).bind(this));
@@ -133,17 +195,17 @@ const Sidebar = new Lang.Class({
if (type === RouteEntry.Type.FROM) {
routeEntry.button.connect('clicked', (function() {
let lastIndex = this._entryList.get_children().length;
- Application.routeService.query.addPoint(lastIndex - 1);
+ this._query.addPoint(lastIndex - 1);
}).bind(this));
this.bind_property('child-revealed',
routeEntry.entry, 'has_focus',
GObject.BindingFlags.DEFAULT);
} else if (type === RouteEntry.Type.VIA) {
- routeEntry.button.connect('clicked', function() {
+ routeEntry.button.connect('clicked', (function() {
let row = routeEntry.get_parent();
- Application.routeService.query.removePoint(row.get_index());
- });
+ this._query.removePoint(row.get_index());
+ }).bind(this));
}
this._initRouteDragAndDrop(routeEntry);
@@ -151,22 +213,38 @@ const Sidebar = new Lang.Class({
_initInstructionList: function() {
let route = Application.routeService.route;
- let query = Application.routeService.query;
+ let transitPlan = Application.openTripPlanner.plan;
route.connect('reset', (function() {
this._clearInstructions();
let length = this._entryList.get_children().length;
for (let index = 1; index < (length - 1); index++) {
- query.removePoint(index);
+ this._query.removePoint(index);
}
}).bind(this));
- query.connect('notify', (function() {
- if (query.isValid())
+ transitPlan.connect('reset', (function() {
+ this._clearTransitOverview();
+ this._showTransitOverview();
+ this._instructionStack.visible_child = this._transitWindow;
+ /* don't remove query points as with the turn-based routing,
+ * since we might get "no route" because of the time selected
+ * and so on */
+ }).bind(this));
+
+ this._query.connect('notify', (function() {
+ if (this._query.isValid()) {
this._instructionStack.visible_child = this._instructionSpinner;
- else
- this._clearInstructions();
+ } else {
+ if (this._query.transportation ===
+ RouteQuery.Transportation.TRANSIT) {
+ this._clearTransitOverview();
+ this._showTransitOverview();
+ } else {
+ this._clearInstructions();
+ }
+ }
if (this._storeRouteTimeoutId)
this._cancelStore();
@@ -181,11 +259,11 @@ const Sidebar = new Lang.Class({
this._storeRouteTimeoutId = Mainloop.timeout_add(5000, (function() {
let placeStore = Application.placeStore;
- let places = query.filledPoints.map(function(point) {
+ let places = this._query.filledPoints.map(function(point) {
return point.place;
});
let storedRoute = new StoredRoute.StoredRoute({
- transportation: query.transportation,
+ transportation: this._query.transportation,
route: route,
places: places,
geoclue: Application.geoclue
@@ -213,6 +291,315 @@ const Sidebar = new Lang.Class({
if (row)
this._mapView.showTurnPoint(row.turnPoint);
}).bind(this));
+
+ transitPlan.connect('update', (function() {
+ this._clearTransitOverview();
+ this._showTransitOverview();
+ this._populateTransitItineraryOverview();
+ }).bind(this));
+
+ /* use list separators for the transit itinerary overview list */
+ this._transitOverviewListBox.set_header_func((function(row, prev) {
+ if (prev)
+ row.set_header(new Gtk.Separator());
+ }).bind(this));
+
+ this._transitOverviewListBox.connect('row-activated',
+ this._onItineraryOverviewRowActivated.bind(this));
+ this._transitItineraryBackButton.connect('clicked',
+ this._showTransitOverview.bind(this));
+
+ },
+
+ _clearTransitOverview: function() {
+ let listBox = this._transitOverviewListBox;
+ listBox.forall(listBox.remove.bind(listBox));
+
+ this._instructionStack.visible_child = this._transitWindow;
+ this._timeInfo.label = '';
+ this._distanceInfo.label = '';
+ },
+
+ _clearTransitItinerary: function() {
+ let listBox = this._transitItineraryListBox;
+ listBox.forall(listBox.remove.bind(listBox));
+ },
+
+ _showTransitOverview: function() {
+ let plan = Application.openTripPlanner.plan;
+
+ this._transitListStack.visible_child_name = 'overview';
+ this._transitHeader.visible_child_name = 'options';
+ plan.deselectItinerary();
+ },
+
+ _showTransitItineraryView: function() {
+ this._transitListStack.visible_child_name = 'itinerary';
+ this._transitHeader.visible_child_name = 'itinerary-header';
+ },
+
+ _populateTransitItineraryOverview: function() {
+ let plan = Application.openTripPlanner.plan;
+
+ plan.itineraries.forEach((function(itinerary) {
+ let row =
+ new TransitItineraryRow.TransitItineraryRow({ visible: true,
+ itinerary: itinerary });
+ this._transitOverviewListBox.add(row);
+ }).bind(this));
+ /* add the "load more" row */
+ this._transitOverviewListBox.add(
+ new TransitMoreRow.TransitMoreRow({ visible: true }));
+
+ /* add an empty list row to get a final separator */
+ this._transitOverviewListBox.add(new Gtk.ListBoxRow({ visible: true }));
+ },
+
+ _onItineraryActivated: function(itinerary) {
+ let plan = Application.openTripPlanner.plan;
+
+ this._populateTransitItinerary(itinerary);
+ this._showTransitItineraryView();
+ plan.selectItinerary(itinerary);
+ },
+
+ _onMoreActivated: function(row) {
+ row.startLoading();
+ Application.openTripPlanner.fetchMoreResults();
+ },
+
+ _onItineraryOverviewRowActivated: function(listBox, row) {
+ this._transitOverviewListBox.unselect_all();
+
+ if (row.itinerary)
+ this._onItineraryActivated(row.itinerary);
+ else
+ this._onMoreActivated(row);
+ },
+
+ _populateTransitItinerary: function(itinerary) {
+ this._transitItineraryTimeLabel.label =
+ itinerary.prettyPrintTimeInterval();
+ this._transitItineraryDurationLabel.label =
+ itinerary.prettyPrintDuration();
+
+ this._clearTransitItinerary();
+ for (let i = 0; i < itinerary.legs.length; i++) {
+ let leg = itinerary.legs[i];
+ let row = new TransitLegRow.TransitLegRow({ leg: leg,
+ start: i === 0,
+ mapView: this._mapView });
+ this._transitItineraryListBox.add(row);
+ }
+
+ /* insert the additional arrival row, showing the arrival place and time */
+ this._transitItineraryListBox.add(
+ new TransitArrivalRow.TransitArrivalRow({ itinerary: itinerary,
+ mapView: this._mapView }));
+ },
+
+ _initTransitOptions: function() {
+ this._transitTimeOptionsComboBox.connect('changed',
+ this._onTransitTimeOptionsComboboxChanged.bind(this));
+ this._transitTimeEntry.connect('activate',
+ this._onTransitTimeEntryActivated.bind(this));
+ /* trigger an update of the query time as soon as focus leave the time
+ * entry, to allow the user to enter a time before selecting start
+ * and destination without having to press enter */
+ this._transitTimeEntry.connect('focus-out-event',
+ this._onTransitTimeEntryActivated.bind(this));
+ this._transitDateButton.popover.get_child().connect('day-selected-double-click',
+ this._onTransitDateCalenderDaySelected.bind(this));
+ this._transitDateButton.connect('toggled',
+ this._onTransitDateButtonToogled.bind(this));
+ this._transitParametersMenuButton.connect('toggled',
+ this._onTransitParametersToggled.bind(this))
+ },
+
+ _resetTransitOptions: function() {
+ /* reset to indicate departure now and forget any previous manually
+ * set time and date */
+ this._transitTimeOptionsComboBox.active_id = 'leaveNow';
+ this._timeSelected = false;
+ this._dateSelected = false;
+ },
+
+ _onTransitTimeOptionsComboboxChanged: function() {
+ if (this._transitTimeOptionsComboBox.active_id === 'leaveNow') {
+ this._transitTimeEntry.visible = false;
+ this._transitDateButton.visible = false;
+ this._query.arriveBy = false;
+ this._query.time = null;
+ this._query.date = null;
+ this._timeSelected = null;
+ this._dateSelected = null;
+ } else {
+ this._transitTimeEntry.visible = true;
+ this._transitDateButton.visible = true;
+
+ if (!this._timeSelected)
+ this._updateTransitTimeEntry(GLib.DateTime.new_now_local());
+
+ if (!this._dateSelected)
+ this._updateTransitDateButton(GLib.DateTime.new_now_local());
+
+ if (this._transitTimeOptionsComboBox.active_id === 'arriveBy') {
+ this._query.arriveBy = true;
+ } else {
+ this._query.arriveBy = false;
+ }
+ }
+ },
+
+ /* parse a time from free-format into a string representation:
+ * hour:min
+ * TODO: maybe try to use some library to get better locale handling,
+ * or push for something in GLib */
+ _parseTimeString: function(timeString) {
+ let pmSet = false;
+ let hours;
+ let mins;
+ /* remove extra whitespaces */
+ timeString = timeString.replace(/\s+/g, '');
+
+ if (timeString.endsWith('am')) {
+ timeString = timeString.substring(0, timeString.length - 2);
+ } else if (timeString.endsWith('pm')) {
+ timeString = timeString.substring(0, timeString.length - 2);
+ pmSet = true;
+ }
+
+ /* allow using :, ., and the ratio symbol to separate hours:mins */
+ if (timeString.charAt(2) === ':' || timeString.charAt(1) === ':')
+ timeString = timeString.replace(':', '');
+ else if (timeString.charAt(2) === '.' || timeString.charAt(1) === '.')
+ timeString = timeString.replace('.', '');
+ else if (timeString.charAt(2) === '\u2236' ||
+ timeString.charAt(1) === '\u2236')
+ timeString = timeString.replace('\u2236', '');
+
+ if (timeString.length === 4) {
+ /* expect a full time specification (hours, minutes) */
+ hours = timeString.substring(0, 2);
+ mins = timeString.substring(2, 4);
+ } else if (timeString.length === 3) {
+ /* interpret a 3 digit string as h:mm */
+ hours = '0' + timeString.substring(0, 1);
+ mins = timeString.substring(1, 3);
+ } else if (timeString.length === 2) {
+ /* expect just the hour part */
+ hours = timeString.substring(0, 2);
+ mins = '00';
+ } else if (timeString.length === 1) {
+ /* expect just the hour part, one digit */
+ hours = '0' + timeString;
+ mins = '00';
+ } else {
+ /* this makes no sense, just bail out */
+ return null;
+ }
+
+ /* check if the parts can be interpreted as numbers */
+ if (hours % 1 === 0 && mins % 1 === 0) {
+ if (pmSet)
+ hours = parseInt(hours) + 12;
+
+ /* if the hours or minutes is out-of-range, bail out */
+ if (hours < 0 || hours > 24 || mins < 0 || mins > 59)
+ return null;
+
+ return hours + ':' + mins;
+ } else {
+ return null;
+ }
+ },
+
+ _updateTransitTimeEntry: function(time) {
+ if (clockFormat === '24h')
+ this._transitTimeEntry.text = time.format('%R');
+ else
+ this._transitTimeEntry.text = time.format('%r');
+ },
+
+ _onTransitTimeEntryActivated: function() {
+ let timeString = this._transitTimeEntry.text;
+
+ if (timeString && timeString.length > 0) {
+ timeString = this._parseTimeString(timeString);
+
+ /* only trigger an update if a different time was entered */
+ if (timeString && timeString !== this._timeSelected) {
+ this._query.time = timeString;
+ /* remember that the user has selected a time */
+ this._timeSelected = timeString;
+ }
+ }
+ },
+
+ _updateTransitDateButton: function(date) {
+ /*
+ * Translators: this is a format string giving the equivalent to
+ * "may 29" according to the current locale's convensions.
+ */
+ this._transitDateButton.label =
+ date.format(C_("month-day-date", "%b %e"));
+ },
+
+ _onTransitDateCalenderDaySelected: function() {
+ let calendar = this._transitDateButton.popover.get_child();
+ let year = calendar.year;
+ let month = calendar.month + 1;
+ let day = calendar.day;
+ let date = year + '-' + month + '-' + day;
+
+ /* only trigger an update if a different date was selected */
+ if (date !== this._dateSelected) {
+ this._query.date = date;
+ this._transitDateButton.active = false;
+ this._updateTransitDateButton(GLib.DateTime.new_local(year, month, day,
+ 0, 0, 0));
+ /* remember that the user has already selected a date */
+ this._dateSelected = date;
+ }
+ },
+
+ _onTransitDateButtonToogled: function() {
+ if (!this._transitDateButton.active)
+ this._onTransitDateCalenderDaySelected();
+ },
+
+ _createTransitOptions: function() {
+ let options = new TransitOptions.TransitOptions();
+ let busSelected = this._busCheckButton.active;
+ let tramSelected = this._tramCheckButton.active;
+ let trainSelected = this._trainCheckButton.active;
+ let subwaySelected = this._subwayCheckButton.active;
+ let ferrySelected = this._ferryCheckButton.active;
+
+ if (busSelected && tramSelected && trainSelected && subwaySelected &&
+ ferrySelected) {
+ options.showAllRouteTypes = true;
+ } else {
+ if (busSelected)
+ options.addRouteTypeToShow(TransitPlan.RouteType.BUS);
+ if (tramSelected)
+ options.addRouteTypeToShow(TransitPlan.RouteType.TRAM);
+ if (trainSelected)
+ options.addRouteTypeToShow(TransitPlan.RouteType.TRAIN);
+ if (subwaySelected)
+ options.addRouteTypeToShow(TransitPlan.RouteType.SUBWAY);
+ if (ferrySelected)
+ options.addRouteTypeToShow(TransitPlan.RouteType.FERRY);
+ }
+
+ return options;
+ },
+
+ _onTransitParametersToggled: function() {
+ if (!this._transitParametersMenuButton.active) {
+ let options = this._createTransitOptions();
+ this._query.transitOptions = options;
+ }
},
_clearInstructions: function() {
@@ -226,8 +613,7 @@ const Sidebar = new Lang.Class({
// Iterate over points and establish the new order of places
_reorderRoutePoints: function(srcIndex, destIndex) {
- let query = Application.routeService.query;
- let points = query.points;
+ let points = this._query.points;
let srcPlace = this._draggedPoint.place;
// Determine if we are swapping from "above" or "below"
@@ -235,19 +621,18 @@ const Sidebar = new Lang.Class({
// Hold off on notifying the changes to query.points until
// we have re-arranged the places.
- query.freeze_notify();
+ this._query.freeze_notify();
for (let i = destIndex; i !== (srcIndex + step); i += step) {
// swap
[points[i].place, srcPlace] = [srcPlace, points[i].place];
}
- query.thaw_notify();
+ this._query.thaw_notify();
},
_onDragDrop: function(row, context, x, y, time) {
- let query = Application.routeService.query;
- let srcIndex = query.points.indexOf(this._draggedPoint);
+ let srcIndex = this._query.points.indexOf(this._draggedPoint);
let destIndex = row.get_index();
this._reorderRoutePoints(srcIndex, destIndex);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]