Commit 035c1cbd authored by Carlos Garcia Campos's avatar Carlos Garcia Campos

Use GtkOverlay to show the loading message

Remove the EvLoadingWindow from libview and move the implementation to
the shell using a EvLoadingMessage widget and GtkOverlay. EvView has now
a is-loading property that allows the users to implement their own
loading notification system. This fixes several realted to the loading
window.
parent d82f1276
......@@ -2,7 +2,6 @@ lib_LTLIBRARIES = libevview3.la
NOINST_H_SRC_FILES = \
ev-annotation-window.h \
ev-loading-window.h \
ev-page-cache.h \
ev-pixbuf-cache.h \
ev-timeline.h \
......@@ -33,7 +32,6 @@ nodist_header_DATA = $(INST_H_BUILT_FILES)
libevview3_la_SOURCES = \
ev-annotation-window.c \
ev-document-model.c \
ev-loading-window.c \
ev-jobs.c \
ev-job-scheduler.c \
ev-page-cache.c \
......
/* ev-loading-window.c
* this file is part of evince, a gnome document viewer
*
* Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
*
* Evince is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Evince 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include <string.h>
#include <glib/gi18n.h>
#include "ev-loading-window.h"
enum {
PROP_0,
PROP_PARENT
};
struct _EvLoadingWindow {
GtkWindow base_instance;
GtkWindow *parent;
GtkWidget *spinner;
gint x;
gint y;
gint width;
gint height;
};
struct _EvLoadingWindowClass {
GtkWindowClass base_class;
};
G_DEFINE_TYPE (EvLoadingWindow, ev_loading_window, GTK_TYPE_WINDOW)
static void
ev_loading_window_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
EvLoadingWindow *window = EV_LOADING_WINDOW (object);
switch (prop_id) {
case PROP_PARENT:
window->parent = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ev_loading_window_init (EvLoadingWindow *window)
{
GtkWindow *gtk_window = GTK_WINDOW (window);
GtkWidget *widget = GTK_WIDGET (window);
GtkWidget *hbox;
GtkWidget *label;
GtkStyleContext *context;
GdkRGBA fg, bg;
const gchar *loading_text = _("Loading…");
const gchar *fg_color_name = "info_fg_color";
const gchar *bg_color_name = "info_bg_color";
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
window->spinner = gtk_spinner_new ();
gtk_box_pack_start (GTK_BOX (hbox), window->spinner, FALSE, FALSE, 0);
gtk_widget_show (window->spinner);
label = gtk_label_new (loading_text);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
gtk_container_add (GTK_CONTAINER (window), hbox);
gtk_widget_show (hbox);
gtk_widget_set_app_paintable (widget, TRUE);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_window_set_type_hint (gtk_window, GDK_WINDOW_TYPE_HINT_NOTIFICATION);
gtk_window_set_accept_focus (gtk_window, FALSE);
gtk_window_set_decorated (gtk_window, FALSE);
gtk_window_set_resizable (gtk_window, FALSE);
context = gtk_widget_get_style_context (widget);
if (!gtk_style_context_lookup_color (context, fg_color_name, &fg) ||
!gtk_style_context_lookup_color (context, bg_color_name, &bg)) {
fg.red = 0.7;
fg.green = 0.67;
fg.blue = 0.63;
fg.alpha = 1.0;
bg.red = 0.99;
bg.green = 0.99;
bg.blue = 0.71;
bg.alpha = 1.0;
}
gtk_widget_override_background_color (widget, GTK_STATE_NORMAL, &bg);
gtk_widget_override_color (widget, GTK_STATE_NORMAL, &fg);
}
static GObject *
ev_loading_window_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params)
{
GObject *object;
EvLoadingWindow *window;
GtkWindow *gtk_window;
object = G_OBJECT_CLASS (ev_loading_window_parent_class)->constructor (type,
n_construct_properties,
construct_params);
window = EV_LOADING_WINDOW (object);
gtk_window = GTK_WINDOW (window);
gtk_window_set_transient_for (gtk_window, window->parent);
gtk_window_set_destroy_with_parent (gtk_window, TRUE);
return object;
}
static void
_cairo_rounded_rectangle (cairo_t *cr,
gint width,
gint height,
gdouble radius)
{
cairo_move_to (cr, radius, 0);
cairo_line_to (cr, width - radius, 0);
cairo_curve_to (cr,
width, 0,
width, 0,
width,
radius);
cairo_line_to (cr, width, height - radius);
cairo_curve_to (cr,
width,height,
width, height,
width - radius,
height);
cairo_line_to (cr, radius, height);
cairo_curve_to (cr,
0, height,
0, height,
0, height - radius);
cairo_line_to (cr, 0, radius);
cairo_curve_to (cr,
0, 0,
0, 0,
radius, 0);
}
static void
ev_loading_window_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
EvLoadingWindow *window = EV_LOADING_WINDOW (widget);
cairo_surface_t *surface;
cairo_region_t *shape;
cairo_t *cr;
double r;
GTK_WIDGET_CLASS (ev_loading_window_parent_class)->size_allocate (widget, allocation);
if (allocation->width == window->width && allocation->height == window->height)
return;
window->width = allocation->width;
window->height = allocation->height;
surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
window->width,
window->height);
cr = cairo_create (surface);
cairo_save (cr);
cairo_rectangle (cr, 0, 0, window->width, window->height);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_fill (cr);
cairo_restore (cr);
cairo_set_source_rgb (cr, 1., 1., 1.);
r = MIN (window->width, window->height) / 2.;
_cairo_rounded_rectangle (cr, window->width, window->height, r);
cairo_fill (cr);
cairo_destroy (cr);
shape = gdk_cairo_region_create_from_surface (surface);
cairo_surface_destroy (surface);
gtk_widget_shape_combine_region (widget, shape);
cairo_region_destroy (shape);
}
static void
ev_loading_window_hide (GtkWidget *widget)
{
EvLoadingWindow *window = EV_LOADING_WINDOW (widget);
window->x = window->y = 0;
gtk_spinner_stop (GTK_SPINNER (window->spinner));
GTK_WIDGET_CLASS (ev_loading_window_parent_class)->hide (widget);
}
static void
ev_loading_window_show (GtkWidget *widget)
{
EvLoadingWindow *window = EV_LOADING_WINDOW (widget);
gtk_spinner_start (GTK_SPINNER (window->spinner));
GTK_WIDGET_CLASS (ev_loading_window_parent_class)->show (widget);
}
static void
ev_loading_window_class_init (EvLoadingWindowClass *klass)
{
GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *gtk_widget_class = GTK_WIDGET_CLASS (klass);
g_object_class->constructor = ev_loading_window_constructor;
g_object_class->set_property = ev_loading_window_set_property;
gtk_widget_class->size_allocate = ev_loading_window_size_allocate;
gtk_widget_class->show = ev_loading_window_show;
gtk_widget_class->hide = ev_loading_window_hide;
g_object_class_install_property (g_object_class,
PROP_PARENT,
g_param_spec_object ("parent",
"Parent",
"The parent window",
GTK_TYPE_WINDOW,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
}
/* Public methods */
GtkWidget *
ev_loading_window_new (GtkWindow *parent)
{
GtkWidget *window;
g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
window = g_object_new (EV_TYPE_LOADING_WINDOW,
"type", GTK_WINDOW_POPUP,
"parent", parent,
NULL);
return window;
}
void
ev_loading_window_get_size (EvLoadingWindow *window,
gint *width,
gint *height)
{
if (width) *width = window->width;
if (height) *height = window->height;
}
void
ev_loading_window_move (EvLoadingWindow *window,
gint x,
gint y)
{
if (x == window->x && y == window->y)
return;
window->x = x;
window->y = y;
gtk_window_move (GTK_WINDOW (window), x, y);
}
......@@ -39,7 +39,6 @@
#include "ev-view-marshal.h"
#include "ev-document-annotations.h"
#include "ev-annotation-window.h"
#include "ev-loading-window.h"
#include "ev-view.h"
#include "ev-view-accessible.h"
#include "ev-view-private.h"
......@@ -69,6 +68,7 @@ enum {
enum {
PROP_0,
PROP_IS_LOADING,
PROP_HADJUSTMENT,
PROP_VADJUSTMENT,
PROP_HSCROLL_POLICY,
......@@ -205,13 +205,9 @@ static void draw_one_page (EvView
GtkBorder *border,
GdkRectangle *expose_area,
gboolean *page_ready);
static void show_loading_window (EvView *view);
static void hide_loading_window (EvView *view);
static void ev_view_reload_page (EvView *view,
gint page,
cairo_region_t *region);
static void ev_view_loading_window_move (EvView *view);
/*** Callbacks ***/
static void ev_view_change_page (EvView *view,
gint new_page);
......@@ -700,7 +696,7 @@ view_update_range_and_current_page (EvView *view)
if (view->current_page != best_current_page) {
view->current_page = best_current_page;
hide_loading_window (view);
ev_view_set_loading (view, FALSE);
ev_document_model_set_page (view->model, best_current_page);
}
}
......@@ -3410,13 +3406,6 @@ ev_view_draw (GtkWidget *widget,
gint i;
GdkRectangle clip_rect;
if (view->loading) {
show_loading_window (view);
} else if (view->loading_window &&
gtk_widget_get_visible (view->loading_window)) {
ev_view_loading_window_move (view);
}
if (view->document == NULL)
return FALSE;
......@@ -4349,81 +4338,6 @@ focus_annotation (EvView *view,
rect.width + 1, rect.height + 1);
}
static void
ev_view_loading_window_move (EvView *view)
{
GtkWidget *widget = GTK_WIDGET (view);
EvLoadingWindow *window = EV_LOADING_WINDOW (view->loading_window);
gint root_x, root_y;
gint window_width;
GtkAllocation allocation;
gtk_widget_get_allocation (widget, &allocation);
gdk_window_get_origin (gtk_widget_get_window (widget), &root_x, &root_y);
ev_loading_window_get_size (window, &window_width, NULL);
root_x += allocation.width - window_width - 10;
root_y += 10;
ev_loading_window_move (window, root_x, root_y);
}
static gboolean
show_loading_window_cb (EvView *view)
{
if (!view->loading_window) {
GtkWindow *parent;
GdkScreen *screen;
parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view)));
view->loading_window = ev_loading_window_new (parent);
/* Show the window off screen to get a valid size asap */
screen = gtk_widget_get_screen (GTK_WIDGET (view));
gtk_window_move (GTK_WINDOW (view->loading_window),
gdk_screen_get_width (screen) + 1,
gdk_screen_get_height (screen) + 1);
gtk_widget_show (view->loading_window);
}
ev_view_loading_window_move (view);
gtk_widget_show (view->loading_window);
view->loading_timeout = 0;
return FALSE;
}
static void
show_loading_window (EvView *view)
{
if (view->loading_window && gtk_widget_get_visible (view->loading_window)) {
ev_view_loading_window_move (view);
return;
}
if (!view->loading_timeout) {
view->loading_timeout =
g_timeout_add_full (G_PRIORITY_LOW,
0.5, (GSourceFunc)show_loading_window_cb,
view, NULL);
}
}
static void
hide_loading_window (EvView *view)
{
if (view->loading_timeout) {
g_source_remove (view->loading_timeout);
view->loading_timeout = 0;
}
if (view->loading_window && gtk_widget_get_visible (view->loading_window)) {
gtk_widget_hide (view->loading_window);
}
}
static void
draw_one_page (EvView *view,
gint page,
......@@ -4471,7 +4385,7 @@ draw_one_page (EvView *view,
if (!page_surface) {
if (page == current_page)
show_loading_window (view);
ev_view_set_loading (view, TRUE);
*page_ready = FALSE;
......@@ -4479,7 +4393,7 @@ draw_one_page (EvView *view,
}
if (page == current_page)
hide_loading_window (view);
ev_view_set_loading (view, FALSE);
ev_view_get_page_size (view, page, &width, &height);
......@@ -4619,11 +4533,6 @@ ev_view_dispose (GObject *object)
view->drag_info.release_timeout_id = 0;
}
if (view->loading_timeout) {
g_source_remove (view->loading_timeout);
view->loading_timeout = 0;
}
gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (view), NULL);
gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (view), NULL);
......@@ -4639,6 +4548,9 @@ ev_view_get_property (GObject *object,
EvView *view = EV_VIEW (object);
switch (prop_id) {
case PROP_IS_LOADING:
g_value_set_boolean (value, view->loading);
break;
case PROP_HADJUSTMENT:
g_value_set_object (value, view->hadjustment);
break;
......@@ -4666,6 +4578,9 @@ ev_view_set_property (GObject *object,
EvView *view = EV_VIEW (object);
switch (prop_id) {
case PROP_IS_LOADING:
ev_view_set_loading (view, g_value_get_boolean (value));
break;
case PROP_HADJUSTMENT:
ev_view_set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL,
(GtkAdjustment *) g_value_get_object (value));
......@@ -4818,6 +4733,15 @@ ev_view_class_init (EvViewClass *class)
class->binding_activated = ev_view_scroll;
g_object_class_install_property (object_class,
PROP_IS_LOADING,
g_param_spec_boolean ("is-loading",
"Is Loading",
"Whether the view is loading",
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/* Scrollable interface */
g_object_class_override_property (object_class, PROP_HADJUSTMENT, "hadjustment");
g_object_class_override_property (object_class, PROP_VADJUSTMENT, "vadjustment");
......@@ -4964,7 +4888,7 @@ ev_view_change_page (EvView *view,
view->current_page = new_page;
view->pending_scroll = SCROLL_TO_PAGE_POSITION;
hide_loading_window (view);
ev_view_set_loading (view, FALSE);
ev_document_misc_get_pointer_position (GTK_WIDGET (view), &x, &y);
ev_view_handle_cursor_over_xy (view, x, y);
......@@ -5135,10 +5059,17 @@ void
ev_view_set_loading (EvView *view,
gboolean loading)
{
if (view->loading && !loading)
hide_loading_window (view);
if (view->loading == loading)
return;
view->loading = loading;
gtk_widget_queue_draw (GTK_WIDGET (view));
g_object_notify (G_OBJECT (view), "is-loading");
}
gboolean
ev_view_is_loading (EvView *view)
{
return view->loading;
}
static gboolean
......@@ -5237,7 +5168,7 @@ ev_view_document_changed_cb (EvDocumentModel *model,
view->find_result = 0;
if (view->document) {
view->loading = FALSE;
ev_view_set_loading (view, FALSE);
g_object_ref (view->document);
setup_caches (view);
}
......
......@@ -49,8 +49,10 @@ GType ev_view_get_type (void) G_GNUC_CONST;
GtkWidget* ev_view_new (void);
void ev_view_set_model (EvView *view,
EvDocumentModel *model);
EV_DEPRECATED
void ev_view_set_loading (EvView *view,
gboolean loading);
gboolean ev_view_is_loading (EvView *view);
void ev_view_reload (EvView *view);
void ev_view_set_page_cache_size (EvView *view,
gsize cache_size);
......
......@@ -399,7 +399,6 @@ ev_previewer_window_set_document (EvPreviewerWindow *window,
g_signal_connect (model, "notify::sizing-mode",
G_CALLBACK (view_sizing_mode_changed),
window);
ev_view_set_loading (window->view, FALSE);
gtk_action_group_set_sensitive (window->action_group, TRUE);
gtk_action_group_set_sensitive (window->accels_group, TRUE);
}
......@@ -592,7 +591,6 @@ ev_previewer_window_constructor (GType type,
window, 0);
ev_view_set_model (window->view, window->model);
ev_document_model_set_continuous (window->model, FALSE);
ev_view_set_loading (window->view, TRUE);
gtk_container_add (GTK_CONTAINER (window->swindow), GTK_WIDGET (window->view));
gtk_widget_show (GTK_WIDGET (window->view));
......
......@@ -43,6 +43,8 @@ evince_SOURCES= \
ev-history.h \
ev-keyring.h \
ev-keyring.c \
ev-loading-message.c \
ev-loading-message.h \
ev-message-area.c \
ev-message-area.h \
ev-metadata.c \
......
/* ev-loading-message.c
* this file is part of evince, a gnome document viewer
*
* Copyright (C) 2010, 2012 Carlos Garcia Campos <carlosgc@gnome.org>
*
* Evince is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Evince 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "ev-loading-message.h"
#include <string.h>
#include <glib/gi18n.h>
struct _EvLoadingMessage {
GtkBox base_instance;
GtkWidget *spinner;
};
struct _EvLoadingMessageClass {
GtkBoxClass base_class;
};
G_DEFINE_TYPE (EvLoadingMessage, ev_loading_message, GTK_TYPE_BOX)
static void
ev_loading_message_init (EvLoadingMessage *message)
{
GtkWidget *widget = GTK_WIDGET (message);
GtkWidget *label;
GtkStyleContext *context;
gtk_container_set_border_width (GTK_CONTAINER (message), 10);
message->spinner = gtk_spinner_new ();
gtk_box_pack_start (GTK_BOX (message), message->spinner, FALSE, FALSE, 0);
gtk_widget_show (message->spinner);
label = gtk_label_new (_("Loading…"));
gtk_box_pack_start (GTK_BOX (message), label, FALSE, FALSE, 0);
gtk_widget_show (label);
}
static void
get_widget_padding (GtkWidget *widget,
GtkBorder *padding)
{
GtkStyleContext *context;
GtkStateFlags state;
context = gtk_widget_get_style_context (widget);
state = gtk_style_context_get_state (context);
gtk_style_context_get_padding (context, state, padding);
}
static void
ev_loading_message_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
EvLoadingMessage *message = EV_LOADING_MESSAGE (widget);
GtkAllocation child_allocation;
GtkBorder padding;
get_widget_padding (widget, &padding);
child_allocation.y = allocation->x + padding.left;
child_allocation.x = allocation->y + padding.top;
child_allocation.width = MAX (1, allocation->width - (padding.left + padding.right));
child_allocation.height = MAX (1, allocation->height - (padding.top + padding.bottom));
GTK_WIDGET_CLASS (ev_loading_message_parent_class)->size_allocate (widget, &child_allocation);
gtk_widget_set_allocation (widget, allocation);
}
static void
ev_loading_message_get_preferred_width (GtkWidget *widget,
gint *minimum_size,
gint *natural_size)
{
GtkBorder padding;
GTK_WIDGET_CLASS (ev_loading_message_parent_class)->get_preferred_width (widget, minimum_size, natural_size);
get_widget_padding (widget, &padding);
*minimum_size += padding.left + padding.right;
*natural_size += padding.left + padding.right;
}
static void
ev_loading_message_get_preferred_height (GtkWidget *widget,
gint *minimum_size,
gint *natural_size)
{
GtkBorder padding;
GTK_WIDGET_CLASS (ev_loading_message_parent_class)->get_preferred_height (widget, minimum_size, natural_size);
get_widget_padding (widget, &padding);
*minimum_size += padding.top + padding.bottom;
*natural_size += padding.top + padding.bottom;
}
static gboolean
ev_loading_message_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkStyleContext *context;
gint width, height;
context = gtk_widget_get_style_context (widget);
width = gtk_widget_get_allocated_width (widget);
height = gtk_widget_get_allocated_height (widget);
gtk_render_background (context, cr, 0, 0, width, height);
gtk_render_frame (context, cr, 0, 0, width, height);
GTK_WIDGET_CLASS (ev_loading_message_parent_class)->draw (widget, cr);
return TRUE;
}
static void
ev_loading_message_hide (GtkWidget *widget)
{
EvLoadingMessage *message = EV_LOADING_MESSAGE (widget);
gtk_spinner_stop (GTK_SPINNER (message->spinner));
GTK_WIDGET_CLASS (ev_loading_message_parent_class)->hide (widget);
}
static void
ev_loading_message_show (GtkWidget *widget)
{
EvLoadingMessage *message = EV_LOADING_MESSAGE (widget);
gtk_spinner_start (GTK_SPINNER (message->spinner));
GTK_WIDGET_CLASS (ev_loading_message_parent_class)->show (widget);
}
static void
ev_loading_message_class_init (EvLoadingMessageClass *klass)
{
GtkWidgetClass *gtk_widget_class = GTK_WIDGET_CLASS (klass);
gtk_widget_class->size_allocate = ev_loading_message_size_allocate;
gtk_widget_class->get_preferred_width = ev_loading_message_get_preferred_width;