Files
evolution/modules/calendar/e-task-shell-view.c
T
Milan Crha 8752647eca Make Calendar, Memos and Tasks views non-UI-blocking
The Calendar, Memos and Tasks views use to do D-Bus calls to
the backends on the main (UI) thread, which could result in UI
freezes, until the operation was done on the backend (and server)
side. This commit fixes that by invoking the operations in
a dedicated thread. It has few additional advantages too:
- operations can be cancelled
- proper error reporting to a user
- less code duplication between the views for common operations

There had been fixed some performance issues when selecting/unselecting
sources in the source selector as well.
2014-10-06 12:33:03 +02:00

536 lines
15 KiB
C

/*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
* Copyright (C) 2014 Red Hat, Inc. (www.redhat.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gtk/gtk.h>
#include "e-cal-base-shell-sidebar.h"
#include "e-task-shell-content.h"
#include "e-task-shell-view.h"
#include "e-task-shell-view-private.h"
#define E_TASK_SHELL_VIEW_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_TASK_SHELL_VIEW, ETaskShellViewPrivate))
G_DEFINE_DYNAMIC_TYPE (ETaskShellView, e_task_shell_view, E_TYPE_CAL_BASE_SHELL_VIEW)
enum {
PROP_0,
PROP_CONFIRM_PURGE
};
static void
task_shell_view_execute_search (EShellView *shell_view)
{
ETaskShellContent *task_shell_content;
EShellWindow *shell_window;
EShellContent *shell_content;
EShellSearchbar *searchbar;
EActionComboBox *combo_box;
GtkRadioAction *action;
ECalComponentPreview *task_preview;
EPreviewPane *preview_pane;
ETaskTable *task_table;
EWebView *web_view;
ECalModel *model;
ECalDataModel *data_model;
icaltimezone *timezone;
struct icaltimetype current_time;
time_t start_range;
time_t end_range;
time_t now_time;
gchar *start, *end;
gchar *query;
gchar *temp;
gint value;
shell_window = e_shell_view_get_shell_window (shell_view);
shell_content = e_shell_view_get_shell_content (shell_view);
task_shell_content = E_TASK_SHELL_CONTENT (shell_content);
searchbar = e_task_shell_content_get_searchbar (task_shell_content);
task_shell_content = E_TASK_SHELL_CONTENT (shell_content);
task_table = e_task_shell_content_get_task_table (task_shell_content);
model = e_task_table_get_model (task_table);
data_model = e_cal_model_get_data_model (model);
timezone = e_cal_model_get_timezone (model);
current_time = icaltime_current_time_with_zone (timezone);
now_time = time_day_begin (icaltime_as_timet (current_time));
action = GTK_RADIO_ACTION (ACTION (TASK_SEARCH_ANY_FIELD_CONTAINS));
value = gtk_radio_action_get_current_value (action);
if (value == TASK_SEARCH_ADVANCED) {
query = e_shell_view_get_search_query (shell_view);
if (!query)
query = g_strdup ("");
} else {
const gchar *format;
const gchar *text;
GString *string;
text = e_shell_searchbar_get_search_text (searchbar);
if (text == NULL || *text == '\0') {
text = "";
value = TASK_SEARCH_SUMMARY_CONTAINS;
}
switch (value) {
default:
text = "";
/* fall through */
case TASK_SEARCH_SUMMARY_CONTAINS:
format = "(contains? \"summary\" %s)";
break;
case TASK_SEARCH_DESCRIPTION_CONTAINS:
format = "(contains? \"description\" %s)";
break;
case TASK_SEARCH_ANY_FIELD_CONTAINS:
format = "(contains? \"any\" %s)";
break;
}
/* Build the query. */
string = g_string_new ("");
e_sexp_encode_string (string, text);
query = g_strdup_printf (format, string->str);
g_string_free (string, TRUE);
}
/* Apply selected filter. */
combo_box = e_shell_searchbar_get_filter_combo_box (searchbar);
value = e_action_combo_box_get_current_value (combo_box);
switch (value) {
case TASK_FILTER_ANY_CATEGORY:
break;
case TASK_FILTER_UNMATCHED:
temp = g_strdup_printf (
"(and (has-categories? #f) %s)", query);
g_free (query);
query = temp;
break;
case TASK_FILTER_NEXT_7_DAYS_TASKS:
start_range = now_time;
end_range = time_day_end (time_add_day (start_range, 7));
start = isodate_from_time_t (start_range);
end = isodate_from_time_t (end_range);
temp = g_strdup_printf (
"(and %s (due-in-time-range? "
"(make-time \"%s\") (make-time \"%s\")))",
query, start, end);
g_free (query);
query = temp;
break;
case TASK_FILTER_ACTIVE_TASKS:
start_range = now_time;
end_range = time_day_end (time_add_day (start_range, 365));
start = isodate_from_time_t (start_range);
end = isodate_from_time_t (end_range);
temp = g_strdup_printf (
"(and %s (due-in-time-range? "
"(make-time \"%s\") (make-time \"%s\")) "
"(not (is-completed?)))",
query, start, end);
g_free (query);
query = temp;
break;
case TASK_FILTER_OVERDUE_TASKS:
start_range = 0;
end_range = time_day_end (now_time);
start = isodate_from_time_t (start_range);
end = isodate_from_time_t (end_range);
temp = g_strdup_printf (
"(and %s (due-in-time-range? "
"(make-time \"%s\") (make-time \"%s\")) "
"(not (is-completed?)))",
query, start, end);
g_free (query);
query = temp;
break;
case TASK_FILTER_COMPLETED_TASKS:
temp = g_strdup_printf (
"(and (is-completed?) %s)", query);
g_free (query);
query = temp;
break;
case TASK_FILTER_TASKS_WITH_ATTACHMENTS:
temp = g_strdup_printf (
"(and (has-attachments?) %s)", query);
g_free (query);
query = temp;
break;
default:
{
GList *categories;
const gchar *category_name;
categories = e_util_dup_searchable_categories ();
category_name = g_list_nth_data (categories, value);
g_list_free_full (categories, g_free);
temp = g_strdup_printf (
"(and (has-categories? \"%s\") %s)",
category_name, query);
g_free (query);
query = temp;
break;
}
}
/* Honor the user's preference to hide completed tasks. */
temp = calendar_config_get_hide_completed_tasks_sexp (FALSE);
if (temp != NULL) {
gchar *temp2;
temp2 = g_strdup_printf ("(and %s %s)", temp, query);
g_free (query);
g_free (temp);
query = temp2;
}
/* Submit the query. */
e_cal_data_model_set_filter (data_model, query);
g_free (query);
preview_pane = e_task_shell_content_get_preview_pane (task_shell_content);
web_view = e_preview_pane_get_web_view (preview_pane);
task_preview = E_CAL_COMPONENT_PREVIEW (web_view);
e_cal_component_preview_clear (task_preview);
}
static void
task_shell_view_update_actions (EShellView *shell_view)
{
EShellContent *shell_content;
EShellSidebar *shell_sidebar;
EShellWindow *shell_window;
GtkAction *action;
const gchar *label;
gboolean sensitive;
guint32 state;
/* Be descriptive. */
gboolean any_tasks_selected;
gboolean has_primary_source;
gboolean multiple_tasks_selected;
gboolean primary_source_is_writable;
gboolean primary_source_is_removable;
gboolean primary_source_is_remote_deletable;
gboolean primary_source_in_collection;
gboolean selection_has_url;
gboolean selection_is_assignable;
gboolean single_task_selected;
gboolean some_tasks_complete;
gboolean some_tasks_incomplete;
gboolean sources_are_editable;
gboolean refresh_supported;
/* Chain up to parent's update_actions() method. */
E_SHELL_VIEW_CLASS (e_task_shell_view_parent_class)->update_actions (shell_view);
shell_window = e_shell_view_get_shell_window (shell_view);
shell_content = e_shell_view_get_shell_content (shell_view);
state = e_shell_content_check_state (shell_content);
single_task_selected =
(state & E_CAL_BASE_SHELL_CONTENT_SELECTION_SINGLE);
multiple_tasks_selected =
(state & E_CAL_BASE_SHELL_CONTENT_SELECTION_MULTIPLE);
selection_is_assignable =
(state & E_CAL_BASE_SHELL_CONTENT_SELECTION_CAN_ASSIGN);
sources_are_editable =
(state & E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_EDITABLE);
some_tasks_complete =
(state & E_CAL_BASE_SHELL_CONTENT_SELECTION_HAS_COMPLETE);
some_tasks_incomplete =
(state & E_CAL_BASE_SHELL_CONTENT_SELECTION_HAS_INCOMPLETE);
selection_has_url =
(state & E_CAL_BASE_SHELL_CONTENT_SELECTION_HAS_URL);
shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
state = e_shell_sidebar_check_state (shell_sidebar);
has_primary_source =
(state & E_CAL_BASE_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE);
primary_source_is_writable =
(state & E_CAL_BASE_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_WRITABLE);
primary_source_is_removable =
(state & E_CAL_BASE_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOVABLE);
primary_source_is_remote_deletable =
(state & E_CAL_BASE_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOTE_DELETABLE);
primary_source_in_collection =
(state & E_CAL_BASE_SHELL_SIDEBAR_PRIMARY_SOURCE_IN_COLLECTION);
refresh_supported =
(state & E_CAL_BASE_SHELL_SIDEBAR_SOURCE_SUPPORTS_REFRESH);
any_tasks_selected = (single_task_selected || multiple_tasks_selected);
action = ACTION (TASK_ASSIGN);
sensitive =
single_task_selected && sources_are_editable &&
selection_is_assignable;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_DELETE);
sensitive = any_tasks_selected && sources_are_editable;
gtk_action_set_sensitive (action, sensitive);
if (multiple_tasks_selected)
label = _("Delete Tasks");
else
label = _("Delete Task");
gtk_action_set_label (action, label);
action = ACTION (TASK_FIND);
sensitive = single_task_selected;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_FORWARD);
sensitive = single_task_selected;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_LIST_COPY);
sensitive = has_primary_source;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_LIST_DELETE);
sensitive =
primary_source_is_removable ||
primary_source_is_remote_deletable;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_LIST_PROPERTIES);
sensitive = primary_source_is_writable;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_LIST_REFRESH);
sensitive = refresh_supported;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_LIST_RENAME);
sensitive =
primary_source_is_writable &&
!primary_source_in_collection;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_MARK_COMPLETE);
sensitive =
any_tasks_selected &&
sources_are_editable &&
some_tasks_incomplete;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_MARK_INCOMPLETE);
sensitive =
any_tasks_selected &&
sources_are_editable &&
some_tasks_complete;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_OPEN);
sensitive = single_task_selected;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_OPEN_URL);
sensitive = single_task_selected && selection_has_url;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_PRINT);
sensitive = single_task_selected;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_PURGE);
sensitive = sources_are_editable;
gtk_action_set_sensitive (action, sensitive);
action = ACTION (TASK_SAVE_AS);
sensitive = single_task_selected;
gtk_action_set_sensitive (action, sensitive);
}
static void
task_shell_view_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_CONFIRM_PURGE:
e_task_shell_view_set_confirm_purge (
E_TASK_SHELL_VIEW (object),
g_value_get_boolean (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
task_shell_view_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_CONFIRM_PURGE:
g_value_set_boolean (
value, e_task_shell_view_get_confirm_purge (
E_TASK_SHELL_VIEW (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
task_shell_view_dispose (GObject *object)
{
e_task_shell_view_private_dispose (E_TASK_SHELL_VIEW (object));
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_task_shell_view_parent_class)->dispose (object);
}
static void
task_shell_view_finalize (GObject *object)
{
e_task_shell_view_private_finalize (E_TASK_SHELL_VIEW (object));
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_task_shell_view_parent_class)->finalize (object);
}
static void
task_shell_view_constructed (GObject *object)
{
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_task_shell_view_parent_class)->constructed (object);
e_task_shell_view_private_constructed (E_TASK_SHELL_VIEW (object));
}
static void
e_task_shell_view_class_init (ETaskShellViewClass *class)
{
GObjectClass *object_class;
EShellViewClass *shell_view_class;
ECalBaseShellViewClass *cal_base_shell_view_class;
g_type_class_add_private (class, sizeof (ETaskShellViewPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->set_property = task_shell_view_set_property;
object_class->get_property = task_shell_view_get_property;
object_class->dispose = task_shell_view_dispose;
object_class->finalize = task_shell_view_finalize;
object_class->constructed = task_shell_view_constructed;
shell_view_class = E_SHELL_VIEW_CLASS (class);
shell_view_class->label = _("Tasks");
shell_view_class->icon_name = "evolution-tasks";
shell_view_class->ui_definition = "evolution-tasks.ui";
shell_view_class->ui_manager_id = "org.gnome.evolution.tasks";
shell_view_class->search_options = "/task-search-options";
shell_view_class->search_rules = "tasktypes.xml";
shell_view_class->new_shell_content = e_task_shell_content_new;
shell_view_class->new_shell_sidebar = e_cal_base_shell_sidebar_new;
shell_view_class->execute_search = task_shell_view_execute_search;
shell_view_class->update_actions = task_shell_view_update_actions;
cal_base_shell_view_class = E_CAL_BASE_SHELL_VIEW_CLASS (class);
cal_base_shell_view_class->source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
g_object_class_install_property (
object_class,
PROP_CONFIRM_PURGE,
g_param_spec_boolean (
"confirm-purge",
"Confirm Purge",
NULL,
TRUE,
G_PARAM_READWRITE));
/* Ensure the GalView types we need are registered. */
g_type_ensure (GAL_TYPE_VIEW_ETABLE);
}
static void
e_task_shell_view_class_finalize (ETaskShellViewClass *class)
{
}
static void
e_task_shell_view_init (ETaskShellView *task_shell_view)
{
task_shell_view->priv = E_TASK_SHELL_VIEW_GET_PRIVATE (task_shell_view);
e_task_shell_view_private_init (task_shell_view);
}
void
e_task_shell_view_type_register (GTypeModule *type_module)
{
/* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
* function, so we have to wrap it with a public function in
* order to register types from a separate compilation unit. */
e_task_shell_view_register_type (type_module);
}
gboolean
e_task_shell_view_get_confirm_purge (ETaskShellView *task_shell_view)
{
g_return_val_if_fail (E_IS_TASK_SHELL_VIEW (task_shell_view), FALSE);
return task_shell_view->priv->confirm_purge;
}
void
e_task_shell_view_set_confirm_purge (ETaskShellView *task_shell_view,
gboolean confirm_purge)
{
g_return_if_fail (E_IS_TASK_SHELL_VIEW (task_shell_view));
if (task_shell_view->priv->confirm_purge == confirm_purge)
return;
task_shell_view->priv->confirm_purge = confirm_purge;
g_object_notify (G_OBJECT (task_shell_view), "confirm-purge");
}