[california/wip/729221-quick-add] Always use quick add when creating events
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california/wip/729221-quick-add] Always use quick add when creating events
- Date: Sat, 31 May 2014 01:26:42 +0000 (UTC)
commit 9eb676ec42d5b444ce69b5ba3a9613f9c12acc1a
Author: Jim Nelson <jim yorba org>
Date: Fri May 30 18:25:39 2014 -0700
Always use quick add when creating events
Only thing missing is a button to go to the full create event dialog
(if the user doesn't want to use parsed string for adding event)
src/component/component-details-parser.vala | 47 +++++++++++++++++++---
src/component/component-event.vala | 56 ++++++++++++++++++++++++++
src/host/host-create-update-event.vala | 12 ++++--
src/host/host-main-window.vala | 42 ++++----------------
src/host/host-quick-create-event.vala | 58 ++++++++++++++++++++++++--
src/host/host-show-event.vala | 51 +----------------------
src/rc/quick-create-event.ui | 47 ++++++++++++++++------
src/view/month/month-grid.vala | 14 +------
8 files changed, 204 insertions(+), 123 deletions(-)
---
diff --git a/src/component/component-details-parser.vala b/src/component/component-details-parser.vala
index 20c42c3..3db383b 100644
--- a/src/component/component-details-parser.vala
+++ b/src/component/component-details-parser.vala
@@ -9,7 +9,7 @@ namespace California.Component {
/**
* Parse the details of a user-entered string into an { link Event}.
*
- * DetailsParser makes no claims of natural language parsing or interpretation. It merely
+ * DetailsParser makes no claims of advanced natural language parsing or interpretation. It merely
* looks for keywords and patterns within the tokenized stream and guesses what Event details
* they refer to.
*
@@ -51,7 +51,7 @@ public class DetailsParser : BaseObject {
/**
* The generated { link Event}.
*/
- public Component.Event event { get; private set; default = new Component.Event.blank(); }
+ public Component.Event event { get; private set; }
private Collection.LookaheadStack<Token> stack;
private StringBuilder summary = new StringBuilder();
@@ -66,18 +66,50 @@ public class DetailsParser : BaseObject {
private bool adding_location = false;
/**
- * Parses a user-entered string of { link Event} details into an Event.
+ * Parses a user-entered string of event details into an { link Event}.
*
- * This always generates an Event, but very little in it may be available. Its backup case
+ * This always generates an Event, but very little in it may be prepared. Its backup case
* is to use the details string as a summary and leave all other fields empty. The caller
* should complete the other fields to generate a valid VEVENT.
*
+ * If the caller wishes to "pre-fill" the Event with certain details, it can supply an Event
+ * that will be used for initial values. This will have an effect on the parser; in particular,
+ * with those details pre-filled in, values detected in the parsed string that would normally
+ * be used in their place will be dropped. In other words, adding initial values will remove
+ * what the user can then add in their own string.
+ *
+ * The { link details} supplied by the user are stored in { link Event.description} verbatim.
+ *
* If the details string is empty, a blank Event is generated.
*/
- public DetailsParser(string? details, Backing.CalendarSource? calendar_source) {
+ public DetailsParser(string? details, Backing.CalendarSource? calendar_source, Event? initial = null) {
+ event = initial ?? new Component.Event.blank();
event.calendar_source = calendar_source;
this.details = details ?? "";
+ // pull out details from the initial Event and add to the local state, which is then
+ // supplanted (but not replaced) by parsed information
+ if (initial != null) {
+ if (!String.is_empty(initial.summary))
+ summary.append(initial.summary.strip());
+
+ if (!String.is_empty(initial.location))
+ location.append(initial.location.strip());
+
+ if (event.is_all_day) {
+ start_date = event.date_span.start_date;
+ end_date = event.date_span.end_date;
+ } else {
+ start_date = event.exact_time_span.start_date;
+ start_time = event.exact_time_span.start_exact_time.to_wall_time();
+ start_time_strict = true;
+
+ end_date = event.exact_time_span.end_date;
+ end_time = event.exact_time_span.end_exact_time.to_wall_time();
+ end_time_strict = true;
+ }
+ }
+
// tokenize the string and arrange as a stack for the parser
string[] tokenized = String.reduce_whitespace(this.details).split(" ");
Gee.LinkedList<Token> list = new Gee.LinkedList<Token>();
@@ -217,7 +249,10 @@ public class DetailsParser : BaseObject {
event.location = location.str;
// store full detail text in the event description for user and for debugging
- event.description = details;
+ if (String.is_empty(event.description))
+ event.description = details;
+ else
+ event.description += "\n" + details;
}
private bool parse_time(Token? specifier, bool strict) {
diff --git a/src/component/component-event.vala b/src/component/component-event.vala
index adba976..ac47026 100644
--- a/src/component/component-event.vala
+++ b/src/component/component-event.vala
@@ -261,6 +261,62 @@ public class Event : Instance, Gee.Comparable<Event> {
}
/**
+ * Returns a prettified string describing the { link Event}'s time span in as concise and
+ * economical manner possible.
+ */
+ public string get_event_time_pretty_string(Calendar.Timezone timezone) {
+ // if any dates are not in current year, display year in all dates
+ Calendar.Date.PrettyFlag date_flags = Calendar.Date.PrettyFlag.NONE;
+ Calendar.DateSpan date_span = get_event_date_span(timezone);
+ if (!date_span.start_date.year.equal_to(Calendar.System.today.year)
+ || !date_span.end_date.year.equal_to(Calendar.System.today.year)) {
+ date_flags |= Calendar.Date.PrettyFlag.INCLUDE_YEAR;
+ }
+
+ // span string is kinda tricky
+ string span;
+ if (is_all_day) {
+ if (date_span.is_same_day) {
+ // All-day one-day event, print that date's "<full date>", including year if not
+ // current year
+ span = date_span.start_date.to_pretty_string(date_flags);
+ } else {
+ // All-day event spanning days, print "<abbrev date> to <abbrev date>"
+ date_flags |= Calendar.Date.PrettyFlag.ABBREV;
+ // Prints a span of dates, i.e. "January 3 to January 6"
+ span = _("%s to %s").printf(date_span.start_date.to_pretty_string(date_flags),
+ date_span.end_date.to_pretty_string(date_flags));
+ }
+ } else {
+ Calendar.ExactTimeSpan exact_time_span = exact_time_span.to_timezone(timezone);
+ if (exact_time_span.is_same_day) {
+ // A span of time, i.e. "3:30pm to 4:30pm"
+ string timespan = _("%s to %s").printf(
+
exact_time_span.start_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE),
+ exact_time_span.end_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE));
+
+ // Single-day timed event, print "<full date>, <full start time> to <full end time>",
+ // including year if not current year
+ span = "%s, %s".printf(exact_time_span.start_date.to_pretty_string(date_flags),
+ timespan);
+ } else {
+ // Multi-day timed event, print "<full time>, <full date>" on both lines,
+ // including year if either not current year
+ // Prints two full time and date strings on separate lines, i.e.:
+ // 12 January 2012, 3:30pm
+ // 13 January 2013, 6:30am
+ span = _("%s, %s\n%s, %s").printf(
+ exact_time_span.start_exact_time.to_pretty_date_string(date_flags),
+
exact_time_span.start_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE),
+ exact_time_span.end_exact_time.to_pretty_date_string(date_flags),
+ exact_time_span.end_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE));
+ }
+ }
+
+ return span;
+ }
+
+ /**
* @inheritDoc
*/
public override bool is_valid() {
diff --git a/src/host/host-create-update-event.vala b/src/host/host-create-update-event.vala
index 96d9674..6fbb06b 100644
--- a/src/host/host-create-update-event.vala
+++ b/src/host/host-create-update-event.vala
@@ -256,8 +256,6 @@ public class CreateUpdateEvent : Gtk.Grid, Toolkit.Card {
update_event_async.begin(null);
else
create_event_async.begin(null);
-
- notify_success();
}
[GtkCallback]
@@ -266,8 +264,11 @@ public class CreateUpdateEvent : Gtk.Grid, Toolkit.Card {
}
private async void create_event_async(Cancellable? cancellable) {
- if (event.calendar_source == null)
+ if (event.calendar_source == null) {
+ notify_failure(_("Unable to create event: calendar must be specified"));
+
return;
+ }
try {
yield event.calendar_source.create_component_async(event, cancellable);
@@ -279,8 +280,11 @@ public class CreateUpdateEvent : Gtk.Grid, Toolkit.Card {
// TODO: Delete from original source if not the same as the new source
private async void update_event_async(Cancellable? cancellable) {
- if (event.calendar_source == null)
+ if (event.calendar_source == null) {
+ notify_failure(_("Unable to update event: calendar must be specified"));
+
return;
+ }
try {
yield event.calendar_source.update_component_async(event, cancellable);
diff --git a/src/host/host-main-window.vala b/src/host/host-main-window.vala
index ddb084a..00aee5e 100644
--- a/src/host/host-main-window.vala
+++ b/src/host/host-main-window.vala
@@ -260,22 +260,7 @@ public class MainWindow : Gtk.ApplicationWindow {
}
private void on_quick_create_event() {
- QuickCreateEvent quick_create = new QuickCreateEvent();
-
- quick_create.success.connect(() => {
- if (quick_create.parsed_event == null)
- return;
-
- if (quick_create.parsed_event.is_valid())
- create_event_async.begin(quick_create.parsed_event, null);
- else
- create_event(quick_create.parsed_event, quick_add_button, null);
- });
-
- Toolkit.Deck deck = new Toolkit.Deck();
- deck.add_cards(iterate<Toolkit.Card>(quick_create).to_array_list());
-
- show_deck(quick_add_button, null, deck);
+ quick_create_event(null, quick_add_button, null);
}
private void on_jump_to_today() {
@@ -303,7 +288,7 @@ public class MainWindow : Gtk.ApplicationWindow {
Component.Event event = new Component.Event.blank();
event.set_event_exact_time_span(initial);
- create_event(event, relative_to, for_location);
+ quick_create_event(event, relative_to, for_location);
}
private void on_request_create_all_day_event(Calendar.Span initial, Gtk.Widget relative_to,
@@ -311,31 +296,20 @@ public class MainWindow : Gtk.ApplicationWindow {
Component.Event event = new Component.Event.blank();
event.set_event_date_span(initial.to_date_span());
- create_event(event, relative_to, for_location);
+ quick_create_event(event, relative_to, for_location);
}
- private void create_event(Component.Event event, Gtk.Widget relative_to, Gdk.Point? for_location) {
- CreateUpdateEvent create_update_event = new CreateUpdateEvent();
- create_update_event.is_update = false;
+ private void quick_create_event(Component.Event? initial, Gtk.Widget relative_to, Gdk.Point?
for_location) {
+ QuickCreateEvent quick_create = new QuickCreateEvent(initial);
+ CreateUpdateEvent create_update = new CreateUpdateEvent();
+ create_update.is_update = false;
Toolkit.Deck deck = new Toolkit.Deck();
- deck.add_cards(iterate<Toolkit.Card>(create_update_event).to_array_list());
- deck.go_home(event);
+ deck.add_cards(iterate<Toolkit.Card>(quick_create, create_update).to_array_list());
show_deck(relative_to, for_location, deck);
}
- private async void create_event_async(Component.Event event, Cancellable? cancellable) {
- if (event.calendar_source == null)
- return;
-
- try {
- yield event.calendar_source.create_component_async(event, cancellable);
- } catch (Error err) {
- debug("Unable to create event: %s", err.message);
- }
- }
-
private void on_request_display_event(Component.Event event, Gtk.Widget relative_to,
Gdk.Point? for_location) {
ShowEvent show_event = new ShowEvent();
diff --git a/src/host/host-quick-create-event.vala b/src/host/host-quick-create-event.vala
index af33921..609d490 100644
--- a/src/host/host-quick-create-event.vala
+++ b/src/host/host-quick-create-event.vala
@@ -14,16 +14,25 @@ public class QuickCreateEvent : Gtk.Grid, Toolkit.Card {
public string? title { get { return null; } }
- public Component.Event? parsed_event { get; private set; default = null; }
+ public new Component.Event? event { get; private set; default = null; }
public Gtk.Widget? default_widget { get { return create_button; } }
public Gtk.Widget? initial_focus { get { return details_entry; } }
[GtkChild]
+ private Gtk.Box when_box;
+
+ [GtkChild]
+ private Gtk.Label when_text_label;
+
+ [GtkChild]
private Gtk.Entry details_entry;
[GtkChild]
+ private Gtk.Label example_label;
+
+ [GtkChild]
private Gtk.ComboBoxText calendar_combo_box;
[GtkChild]
@@ -31,7 +40,26 @@ public class QuickCreateEvent : Gtk.Grid, Toolkit.Card {
private Toolkit.ComboBoxTextModel<Backing.CalendarSource> model;
- public QuickCreateEvent() {
+ public QuickCreateEvent(Component.Event? initial) {
+ event = initial;
+
+ // if initial date/times supplied, reveal to the user and change the example
+ string eg;
+ if (initial != null && (initial.date_span != null || initial.exact_time_span != null)) {
+ when_box.visible = true;
+ when_text_label.label = initial.get_event_time_pretty_string(Calendar.Timezone.local);
+ if (initial.date_span != null)
+ eg = _("Example: Dinner at Tadich Grill 7:30pm");
+ else
+ eg = _("Example: Dinner at Tadich Grill");
+ } else {
+ when_box.visible = false;
+ when_box.no_show_all = true;
+ eg = _("Example: Dinner at Tadich Grill 7:30pm tomorrow");
+ }
+
+ example_label.label = "<small><i>%s</i></small>".printf(eg);
+
// create and initialize combo box model
model = new Toolkit.ComboBoxTextModel<Backing.CalendarSource>(calendar_combo_box,
(cal) => cal.title);
@@ -47,6 +75,7 @@ public class QuickCreateEvent : Gtk.Grid, Toolkit.Card {
// make first item active
calendar_combo_box.active = 0;
+ // enable create button only when the user has typed something
details_entry.bind_property("text-length", create_button, "sensitive", BindingFlags.SYNC_CREATE,
xform_text_length_to_sensitive);
}
@@ -75,10 +104,29 @@ public class QuickCreateEvent : Gtk.Grid, Toolkit.Card {
[GtkCallback]
private void on_create_button_clicked() {
- Component.DetailsParser parser = new Component.DetailsParser(details_entry.text, model.active);
- parsed_event = parser.event;
+ Component.DetailsParser parser = new Component.DetailsParser(details_entry.text, model.active,
+ event);
+ event = parser.event;
- notify_success();
+ if (event.is_valid())
+ create_event_async.begin(null);
+ else
+ jump_to_card_by_name(CreateUpdateEvent.ID, event);
+ }
+
+ private async void create_event_async(Cancellable? cancellable) {
+ if (event.calendar_source == null) {
+ notify_failure(_("Unable to create event: calendar must be specified"));
+
+ return;
+ }
+
+ try {
+ yield event.calendar_source.create_component_async(event, cancellable);
+ notify_success();
+ } catch (Error err) {
+ notify_failure(_("Unable to create event: %s").printf(err.message));
+ }
}
}
diff --git a/src/host/host-show-event.vala b/src/host/host-show-event.vala
index 9bc4a02..67cece1 100644
--- a/src/host/host-show-event.vala
+++ b/src/host/host-show-event.vala
@@ -67,55 +67,8 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
if (!String.is_empty(event.description))
add_lf_lf(builder).append_printf("%s", Markup.escape_text(event.description));
- // if any dates are not in current year, display year in all dates
- Calendar.Date.PrettyFlag date_flags = Calendar.Date.PrettyFlag.NONE;
- Calendar.DateSpan date_span = event.get_event_date_span(Calendar.Timezone.local);
- if (!date_span.start_date.year.equal_to(Calendar.System.today.year)
- || !date_span.end_date.year.equal_to(Calendar.System.today.year)) {
- date_flags |= Calendar.Date.PrettyFlag.INCLUDE_YEAR;
- }
-
- // span string is kinda tricky
- string span;
- if (event.is_all_day) {
- if (date_span.is_same_day) {
- // All-day one-day event, print that date's "<full date>", including year if not
- // current year
- span = date_span.start_date.to_pretty_string(date_flags);
- } else {
- // All-day event spanning days, print "<abbrev date> to <abbrev date>"
- date_flags |= Calendar.Date.PrettyFlag.ABBREV;
- // Prints a span of dates, i.e. "January 3 to January 6"
- span = _("%s to %s").printf(date_span.start_date.to_pretty_string(date_flags),
- date_span.end_date.to_pretty_string(date_flags));
- }
- } else {
- Calendar.ExactTimeSpan exact_time_span = event.exact_time_span.to_timezone(
- Calendar.Timezone.local);
- if (exact_time_span.is_same_day) {
- // Single-day timed event, print "<full date>\n<full start time> to <full end time>",
- // including year if not current year
- // Prints a span of time, i.e. "3:30pm to 4:30pm"
- string timespan = _("%s to %s").printf(
-
exact_time_span.start_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE),
- exact_time_span.end_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE));
- span = "%s\n%s".printf(exact_time_span.start_date.to_pretty_string(date_flags),
- timespan);
- } else {
- // Multi-day timed event, print "<full time>, <full date>" on both lines,
- // including year if either not current year
- // Prints two full time and date strings on separate lines, i.e.:
- // 12 January 2012, 3:30pm
- // 13 January 2013, 6:30am
- span = _("%s, %s\n%s, %s").printf(
- exact_time_span.start_exact_time.to_pretty_date_string(date_flags),
-
exact_time_span.start_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE),
- exact_time_span.end_exact_time.to_pretty_date_string(date_flags),
- exact_time_span.end_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE));
- }
- }
-
- add_lf_lf(builder).append_printf("<small>%s</small>", Markup.escape_text(span));
+ add_lf_lf(builder).append_printf("<small>%s</small>",
+ Markup.escape_text(event.get_event_time_pretty_string(Calendar.Timezone.local)));
text_label.label = builder.str;
diff --git a/src/rc/quick-create-event.ui b/src/rc/quick-create-event.ui
index 827d36d..d717926 100644
--- a/src/rc/quick-create-event.ui
+++ b/src/rc/quick-create-event.ui
@@ -10,8 +10,9 @@
<object class="GtkLabel" id="title_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="margin_bottom">8</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">_Quick add event:</property>
+ <property name="label" translatable="yes">_Quick add event</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">details_entry</property>
<attributes>
@@ -29,7 +30,9 @@
<object class="GtkButtonBox" id="buttonbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="valign">end</property>
<property name="margin_top">8</property>
+ <property name="vexpand">True</property>
<property name="spacing">8</property>
<property name="homogeneous">True</property>
<property name="layout_style">end</property>
@@ -71,6 +74,19 @@
</object>
<packing>
<property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="calendar_combo_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">8</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
@@ -102,9 +118,12 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes"><small><i>Example: Dinner at Tadich Grill
7:30pm tomorrow</i></small></property>
+ <property name="label">(empty)</property>
<property name="use_markup">True</property>
<property name="ellipsize">start</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
</object>
<packing>
<property name="expand">False</property>
@@ -115,25 +134,24 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">1</property>
+ <property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
- <object class="GtkBox" id="box2">
+ <object class="GtkBox" id="when_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">8</property>
<child>
- <object class="GtkLabel" id="calendar_label">
+ <object class="GtkLabel" id="when_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Ca_lendar:</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">calendar_combo_box</property>
- <property name="ellipsize">start</property>
+ <property name="label" translatable="yes">When</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
</object>
<packing>
<property name="expand">False</property>
@@ -142,12 +160,15 @@
</packing>
</child>
<child>
- <object class="GtkComboBoxText" id="calendar_combo_box">
+ <object class="GtkLabel" id="when_text_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="xalign">0</property>
+ <property name="label">(empty)</property>
</object>
<packing>
- <property name="expand">True</property>
+ <property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
@@ -155,7 +176,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">2</property>
+ <property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
diff --git a/src/view/month/month-grid.vala b/src/view/month/month-grid.vala
index 497e4c9..8203f62 100644
--- a/src/view/month/month-grid.vala
+++ b/src/view/month/month-grid.vala
@@ -405,18 +405,8 @@ private class Grid : Gtk.Grid {
if (release_cell.date == null)
return true;
- // TODO: Define default time better
- Calendar.ExactTime start;
- if(release_cell.date.equal_to(Calendar.System.today)) {
- start = new Calendar.ExactTime.now(Calendar.Timezone.local);
- } else {
- start = new Calendar.ExactTime(Calendar.Timezone.local, release_cell.date,
- new Calendar.WallTime(13, 0, 0));
- }
-
- Calendar.ExactTime end = start.adjust_time(1, Calendar.TimeUnit.HOUR);
-
- owner.request_create_timed_event(new Calendar.ExactTimeSpan(start, end), release_cell,
release_point);
+ owner.request_create_all_day_event(new Calendar.DateSpan(press_cell.date, release_cell.date),
+ release_cell, release_point);
// stop propagation
return true;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]