diff options
-rw-r--r-- | backend/epub/epub-document.c | 80 | ||||
-rw-r--r-- | libview/ev-jobs.c | 9 | ||||
-rw-r--r-- | libview/ev-web-view.c | 349 | ||||
-rw-r--r-- | libview/ev-web-view.h | 64 | ||||
-rw-r--r-- | shell/ev-convert-metadata.c | 4 | ||||
-rw-r--r-- | shell/ev-window.c | 39 |
6 files changed, 500 insertions, 45 deletions
diff --git a/backend/epub/epub-document.c b/backend/epub/epub-document.c index bf1dc00b..09ad973d 100644 --- a/backend/epub/epub-document.c +++ b/backend/epub/epub-document.c @@ -71,6 +71,8 @@ struct _EpubDocument GList* contentList ; /* A variable to hold our epubDocument for unzipping*/ unzFile epubDocument ; + /* A pointer to an offscreen WebKitWebView, for thumbnails*/ + WebKitWebView *webview; }; static void epub_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); @@ -81,27 +83,39 @@ EV_BACKEND_REGISTER_WITH_CODE (EpubDocument, epub_document, epub_document_document_thumbnails_iface_init); } ); +/* A cairo surface for the thumbnails, this probably dosen't need to be global, but I couldn't find a better solution.*/ +static cairo_surface_t* surface = NULL ; +static gboolean completed = FALSE; +static GdkPixbuf *thumbnail=NULL ; + static void -epub_webkit_render(cairo_surface_t **surface, - const char* uri); +epub_webkit_render(EpubDocument *document,const char* uri); + static GdkPixbuf * epub_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, EvRenderContext *rc, gboolean border) { - GdkPixbuf *thumbnail; - cairo_surface_t* surface=NULL; gchar* uri = (gchar*) rc->page->backend_page; - epub_webkit_render (&surface,uri); - if ( !surface ) { - return NULL ; - } - + EpubDocument *epub_document = EPUB_DOCUMENT(document); + completed = FALSE ; + thumbnail=NULL; if (surface) { - thumbnail = ev_document_misc_pixbuf_from_surface (surface); cairo_surface_destroy (surface); + surface=NULL; + } + epub_webkit_render (epub_document,uri); + + while (completed != TRUE ) { + /*Wait for the job to complete*/ + } + + if (thumbnail) { + return thumbnail; + } + else { + return NULL; } - return thumbnail; } static void @@ -110,8 +124,11 @@ epub_document_thumbnails_get_dimensions (EvDocumentThumbnails *document, gint *width, gint *height) { - gdouble page_width = 800; - gdouble page_height = 600; + gdouble page_width, page_height; + + page_width = 800; + page_height = 600; + *width = MAX ((gint)(page_width * rc->scale + 0.5), 1); *height = MAX ((gint)(page_height * rc->scale + 0.5), 1); } @@ -147,30 +164,40 @@ epub_document_get_n_pages (EvDocument *document) static void webkit_render_cb(GtkWidget *web_view, GParamSpec *specification, - cairo_surface_t **surface) + gpointer data) { WebKitLoadStatus status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW(web_view)); if ( status == WEBKIT_LOAD_FINISHED ) { - *(surface) = webkit_web_view_get_snapshot (WEBKIT_WEB_VIEW(web_view)); + surface = webkit_web_view_get_snapshot (WEBKIT_WEB_VIEW(web_view)); + thumbnail = ev_document_misc_pixbuf_from_surface(surface); + completed=TRUE; } } -static void -epub_webkit_render(cairo_surface_t **surface, - const char* uri) +static void epub_webkit_render(EpubDocument *epub_document,const char* uri) { - GtkWidget *offscreen_window = gtk_offscreen_window_new (); + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(epub_document->webview),uri); +} + +static WebKitWebView* +offscreen_webview_init() +{ + GtkWidget *offscreen_window = gtk_offscreen_window_new(); gtk_window_set_default_size(GTK_WINDOW(offscreen_window),800,600); + GtkWidget* scroll_view = gtk_scrolled_window_new (NULL,NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scroll_view),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); GtkWidget* web_view = webkit_web_view_new (); - g_signal_connect(WEBKIT_WEB_VIEW(web_view),"notify::load-status",G_CALLBACK(webkit_render_cb),surface); + WebKitWebSettings *webviewsettings = webkit_web_settings_new (); + g_object_set (G_OBJECT(webviewsettings), "enable-plugins", FALSE, NULL); + webkit_web_view_set_settings (WEBKIT_WEB_VIEW(web_view),webviewsettings); + g_signal_connect(WEBKIT_WEB_VIEW(web_view),"notify::load-status",G_CALLBACK(webkit_render_cb),NULL); gtk_container_add(GTK_CONTAINER(scroll_view),web_view); gtk_container_add(GTK_CONTAINER(offscreen_window),scroll_view); gtk_widget_show_all (offscreen_window); - webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view),uri); + return WEBKIT_WEB_VIEW(web_view); } #else /* The webkit2 code for GTK3 */ @@ -203,20 +230,21 @@ webkit_render_cb(WebKitWebView *webview, surface); } -static void epub_webkit_render(cairo_surface_t **surface, - const char* uri) { GtkWidget *offscreen_window = gtk_offscreen_window_new (); gtk_window_set_default_size(GTK_WINDOW(offscreen_window),800,600); GtkWidget* scroll_view = gtk_scrolled_window_new (NULL,NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scroll_view),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); GtkWidget* web_view = webkit_web_view_new (); + gtk_container_add(GTK_CONTAINER(offscreen_window),scroll_view); gtk_container_add(GTK_CONTAINER(scroll_view),web_view); - webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view),uri); + gtk_widget_show_all(offscreen_window); g_signal_connect(web_view,"load-changed",G_CALLBACK(webkit_render_cb),surface); + return web_view ; } + #endif /** * epub_remove_temporary_dir : Removes a directory recursively. @@ -863,6 +891,7 @@ epub_document_init (EpubDocument *epub_document) epub_document->archivename = NULL ; epub_document->tmp_archive_dir = NULL ; epub_document->contentList = NULL ; + epub_document->webview = offscreen_webview_init(); } static gboolean @@ -1037,13 +1066,12 @@ epub_document_class_init (EpubDocumentClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); - + gobject_class->finalize = epub_document_finalize; ev_document_class->load = epub_document_load; ev_document_class->save = epub_document_save; ev_document_class->get_n_pages = epub_document_get_n_pages; -/* ev_document_class->wekit_render->render = epub_document_render;*/ ev_document_class->get_info = epub_document_get_info; ev_document_class->get_page = epub_document_get_page; } diff --git a/libview/ev-jobs.c b/libview/ev-jobs.c index 76e183a1..7792fafa 100644 --- a/libview/ev-jobs.c +++ b/libview/ev-jobs.c @@ -211,7 +211,7 @@ ev_job_run (EvJob *job) { EvJobClass *class = EV_JOB_GET_CLASS (job); - return class->run (job); + return class-> run (job); } void @@ -791,7 +791,12 @@ ev_job_thumbnail_run (EvJob *job) ev_document_doc_mutex_lock (); page = ev_document_get_page (job->document, job_thumb->page); - rc = ev_render_context_new (page, job_thumb->rotation, job_thumb->scale); + if (job->document->iswebdocument == TRUE ) { + rc = ev_render_context_new (page, 0, job_thumb->scale); + } + else { + rc = ev_render_context_new (page, job_thumb->rotation, job_thumb->scale); + } g_object_unref (page); job_thumb->thumbnail = ev_document_thumbnails_get_thumbnail (EV_DOCUMENT_THUMBNAILS (job->document), diff --git a/libview/ev-web-view.c b/libview/ev-web-view.c new file mode 100644 index 00000000..96fb6c46 --- /dev/null +++ b/libview/ev-web-view.c @@ -0,0 +1,349 @@ +/* this file is part of atril, a mate document viewer + * + * Copyright (C) 2014 Avishkar Gupta + * Based on ev-view.c + * + * Atril 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. + * + * Atril 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 <glib/gi18n-lib.h> +#include <gtk/gtk.h> + +#if GTK_CHECK_VERSION (3, 0, 0) +#include <webkit2/webkit2.h> +#else +#include <webkit/webkit.h> +#endif +#include "ev-web-view.h" +#include "ev-document-model.h" +#define EV_WEB_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_WEB_VIEW, EvWebViewClass)) +#define EV_IS_WEB_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_WEB_VIEW)) +#define EV_WEB_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_WEB_VIEW, EvWebViewClass)) + +struct _EvWebView +{ + WebKitWebView web_view; + EvDocument *document; + EvDocumentModel *model; + gint current_page; + gboolean inverted_colors ; + gboolean fullscreen; +}; + +struct _EvWebViewClass +{ + WebKitWebViewClass base_class; +}; +G_DEFINE_TYPE (EvWebView, ev_web_view, WEBKIT_TYPE_WEB_VIEW) + +/*** Callbacks ***/ +static void ev_web_view_change_page (EvWebView *webview, + gint new_page); + +static void ev_web_view_page_changed_cb (EvDocumentModel *model, + gint old_page, + gint new_page, + EvWebView *webview); +/*** GObject ***/ + +#if GTK_CHECK_VERSION (3, 0, 0) +static void ev_view_dispose (GObject *object); +#else +static void ev_web_view_destroy (GtkObject *object); +#endif +static void ev_web_view_class_init (EvWebViewClass *klass); +static void ev_web_view_init (EvWebView *webview); + +static void +web_view_update_range_and_current_page (EvWebView *webview) +{ + g_return_if_fail(EV_IS_WEB_VIEW(webview)); + + if (ev_document_get_n_pages (webview->document) <= 0) + return; + + ev_document_model_set_page(webview->model, 0); + webview->current_page = 0; + EvPage *webpage = ev_document_get_page(webview->document,0); + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview),(gchar*)webpage->backend_page); +} + +static void +#if GTK_CHECK_VERSION (3, 0, 0) +ev_web_view_dispose (GObject *object) +#else +ev_web_view_destroy (GtkObject *object) +#endif +{ + EvWebView *webview = EV_WEB_VIEW (object); + + if (webview->document) { + g_object_unref(webview->document); + webview->document = NULL ; + } + + if (webview->model) { + g_object_unref(webview->model); + webview->model = NULL; + } + WebKitWebViewClass *klass = WEBKIT_WEB_VIEW_GET_CLASS(webview); + +#if GTK_CHECK_VERSION (3, 0, 0) + G_OBJECT_CLASS (ev_web_view_parent_class)->dispose (object); +#else + GTK_OBJECT_CLASS (ev_web_view_parent_class)->destroy (object); +#endif +} + +static void +ev_web_view_class_init (EvWebViewClass *klass) +{ +} + +static void +ev_web_view_init (EvWebView *webview) +{ + gtk_widget_set_can_focus (GTK_WIDGET (webview), TRUE); +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_widget_set_has_window (GTK_WIDGET (webview), TRUE); +#endif + + webview->current_page = 0; + + webview->fullscreen = FALSE; + +} + +/*** Callbacks ***/ + +static void +ev_web_view_change_page (EvWebView *webview, + gint new_page) +{ + g_return_if_fail(EV_IS_WEB_VIEW(webview)); + + EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS(webview->document); + EvPage *page = klass->get_page(webview->document,new_page); + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview),(gchar*)page->backend_page); + + webview->current_page = new_page; +} + +static void +ev_web_view_page_changed_cb (EvDocumentModel *model, + gint old_page, + gint new_page, + EvWebView *webview) +{ + if (!webview->document) + return; + + if (webview->current_page != new_page) { + ev_web_view_change_page (webview, new_page); + } else { + webkit_web_view_reload (WEBKIT_WEB_VIEW(webview)); + } +} + +GtkWidget* +ev_web_view_new (void) +{ + GtkWidget *webview; + + webview = g_object_new (EV_TYPE_WEB_VIEW, NULL); + + return webview; +} + +static void +ev_web_view_document_changed_cb (EvDocumentModel *model, + GParamSpec *pspec, + EvWebView *webview) +{ + g_return_if_fail(EV_IS_WEB_VIEW(webview)); + + EvDocument *document = ev_document_model_get_document (model); + + if (document != webview->document) { + + if (webview->document ) + g_object_unref(webview->document); + + webview->document = document ; + + if(webview->document) { + g_object_ref(webview->document); + } + + gint current_page = ev_document_model_get_page(model); + if (webview->current_page != current_page) { + ev_web_view_change_page (webview, current_page); + } else { + webkit_web_view_reload (webview); + } + + } +} + +static void +ev_web_view_inverted_colors_changed_cb (EvDocumentModel *model, + GParamSpec *pspec, + EvWebView *webview) +{ + guint inverted_colors = ev_document_model_get_inverted_colors (model); + inverted_colors = !inverted_colors; + /*TODO*/ +} + +static void +ev_web_view_fullscreen_changed_cb (EvDocumentModel *model, + GParamSpec *pspec, + EvWebView *webview) +{ + gboolean fullscreen = ev_document_model_get_fullscreen (model); + + webview->fullscreen = fullscreen; +#if GTK_CHECK_VERSION (3, 0, 0) + WebKitWindowProperties *window_properties = + webkit_web_view_get_window_properties (WEBKIT_WEB_VIEW(webview)); + + webkit_window_properties_get_fullscreen(window_properties); + /*TODO*/ +#else + webkit_web_view_set_view_mode(WEBKIT_WEB_VIEW(webview), WEBKIT_WEB_VIEW_VIEW_MODE_FULLSCREEN); +#endif +} + +void +ev_web_view_set_model (EvWebView *webview, + EvDocumentModel *model) +{ + g_return_if_fail (EV_IS_WEB_VIEW (webview)); + g_return_if_fail (EV_IS_DOCUMENT_MODEL (model)); + + if (model == webview->model) + return; + + if (webview->model) { + g_signal_handlers_disconnect_by_func (webview->model, + ev_web_view_document_changed_cb, + webview); + g_signal_handlers_disconnect_by_func (webview->model, + ev_web_view_page_changed_cb, + webview); + g_object_unref (webview->model); + } + webview->model = g_object_ref (model); + + /* Initialize webview from model */ + webview->fullscreen = ev_document_model_get_fullscreen (webview->model); + webview->inverted_colors = ev_document_model_get_inverted_colors(webview->model); + ev_web_view_document_changed_cb (webview->model, NULL, webview); + + g_signal_connect (webview->model, "notify::document", + G_CALLBACK (ev_web_view_document_changed_cb), + webview); + g_signal_connect (webview->model, "notify::inverted-colors", + G_CALLBACK (ev_web_view_inverted_colors_changed_cb), + webview); + g_signal_connect (webview->model,"page-changed", + G_CALLBACK(ev_web_view_page_changed_cb), + webview); +} + +void +ev_web_view_reload_page (EvWebView *webview, + gint page) +{ + webkit_web_view_reload (WEBKIT_WEB_VIEW(webview)); +} + +void +ev_web_view_reload (EvWebView *webview) +{ + web_view_update_range_and_current_page (webview); +} + + +gboolean +ev_web_view_next_page (EvWebView *webview) +{ + int page, n_pages; + + g_return_val_if_fail (EV_IS_WEB_VIEW (webview), FALSE); + + if (!webview->document) + return FALSE; + + page = ev_document_model_get_page (webview->model); + n_pages = ev_document_get_n_pages (webview->document); + + page = page + 1; + + if (page < n_pages) { + ev_document_model_set_page (webview->model, page); + return TRUE; + } else if (page == n_pages) { + ev_document_model_set_page (webview->model, page - 1); + return TRUE; + } else { + return FALSE; + } +} + +gboolean +ev_web_view_previous_page (EvWebView *webview) +{ + int page; + + g_return_val_if_fail (EV_IS_WEB_VIEW (webview), FALSE); + + if (!webview->document) + return FALSE; + + page = ev_document_model_get_page (webview->model); + + page = page - 1 ; + + if (page >= 0) { + ev_document_model_set_page (webview->model, page); + return TRUE; + } else if (page == -1) { + ev_document_model_set_page (webview->model, 0); + return TRUE; + } else { + return FALSE; + } +} + +void +ev_web_view_handle_link(EvWebView *webview,EvLink *link) +{ + +} + +void +ev_web_view_find_next(EvWebView *webview) +{ + +} + +void +ev_web_view_find_previous(EvWebView *webview) +{ + +} diff --git a/libview/ev-web-view.h b/libview/ev-web-view.h new file mode 100644 index 00000000..6aaa17ff --- /dev/null +++ b/libview/ev-web-view.h @@ -0,0 +1,64 @@ +/* this file is part of atril, a mate document web_viewer + * + * Copyright (C) 2014 Avishkar Gupta + * Based on ev-view.h + * + * Atril 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. + * + * Atril 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. + */ + +#if !defined (__EV_ATRIL_VIEW_H_INSIDE__) && !defined (ATRIL_COMPILATION) +#error "Only <atril-web_view.h> can be included directly." +#endif + +#ifndef __EV_WEB_VIEW_H__ +#define __EV_WEB_VIEW_H__ + +#include <gtk/gtk.h> + +#include <atril-document.h> + +#include "ev-document-model.h" +#include <glib-object.h> +G_BEGIN_DECLS + + +typedef struct _EvWebView EvWebView; +typedef struct _EvWebViewClass EvWebViewClass; + +#define EV_TYPE_WEB_VIEW (ev_web_view_get_type ()) +#define EV_WEB_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_WEB_VIEW, EvWebView)) +#define EV_IS_WEB_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_WEB_VIEW)) + +GType ev_web_view_get_type (void) G_GNUC_CONST; + +GtkWidget* ev_web_view_new (void); +void ev_web_view_set_model (EvWebView *webview, + EvDocumentModel *model); +void ev_web_view_reload (EvWebView *webview); + +/* Navigation */ +gboolean ev_web_view_next_page (EvWebView *webview); +gboolean ev_web_view_previous_page (EvWebView *webview); + +/*Sidebar links*/ +void ev_web_view_handle_link (EvWebView *webview, EvLink* link); + +/*Searching*/ +void ev_web_view_find_next (EvWebView *webview); +void ev_web_view_find_previous (EvWebView *webview); + +G_END_DECLS + +#endif /* __EV_WEB_VIEW_H__ */ diff --git a/shell/ev-convert-metadata.c b/shell/ev-convert-metadata.c index 5664253b..72431dd5 100644 --- a/shell/ev-convert-metadata.c +++ b/shell/ev-convert-metadata.c @@ -26,7 +26,7 @@ #include <glib/gi18n.h> #include <string.h> #include <libxml/tree.h> - +#include <X11/Xlib.h> #define EV_METADATA_NAMESPACE "metadata::atril" typedef struct { @@ -294,7 +294,7 @@ main (gint argc, gchar **argv) g_printerr ("%s\n", "Usage: atril-convert-metadata FILE"); return 1; } - + XInitThreads(); gtk_init (&argc, &argv); if (!convert_metadata_file (argv[1])) diff --git a/shell/ev-window.c b/shell/ev-window.c index 4f2c1ac2..900d9155 100644 --- a/shell/ev-window.c +++ b/shell/ev-window.c @@ -471,7 +471,9 @@ ev_window_setup_action_sensitivity (EvWindow *ev_window) static void ev_window_update_actions (EvWindow *ev_window) { - EvView *view = EV_VIEW (ev_window->priv->view); + EvWebView *webview = NULL; + EvView *view = NULL; + int n_pages = 0, page = -1; gboolean has_pages = FALSE; gboolean presentation_mode; @@ -483,13 +485,20 @@ ev_window_update_actions (EvWindow *ev_window) n_pages = ev_document_get_n_pages (ev_window->priv->document); has_pages = n_pages > 0; } + + if (ev_window->priv->document && ev_window->priv->document->iswebdocument == TRUE ) { + webview = EV_WEB_VIEW(ev_window->priv->webview); + } else { + view = EV_VIEW (ev_window->priv->view); + } can_find_in_page = (ev_window->priv->find_job && ev_job_find_has_results (EV_JOB_FIND (ev_window->priv->find_job))); - - ev_window_set_action_sensitive (ev_window, "EditCopy", - has_pages && - ev_view_get_has_selection (view)); + if (view) { + ev_window_set_action_sensitive (ev_window, "EditCopy", + has_pages && + ev_view_get_has_selection (view)); + } ev_window_set_action_sensitive (ev_window, "EditFindNext", has_pages && can_find_in_page); ev_window_set_action_sensitive (ev_window, "EditFindPrevious", @@ -1409,7 +1418,7 @@ ev_window_setup_document (EvWindow *ev_window) if (EV_WINDOW_IS_PRESENTATION (ev_window) && document->iswebdocument == FALSE) gtk_widget_grab_focus (ev_window->priv->presentation_view); else { - if ( gtk_widget_get_parent(ev_window->priv->view) != NULL ) + if ( document->iswebdocument == FALSE ) gtk_widget_grab_focus (ev_window->priv->view); else gtk_widget_grab_focus (ev_window->priv->webview); @@ -5510,19 +5519,19 @@ ev_window_key_press_event (GtkWidget *widget, * It's needed to be able to type in * annot popups windows */ - GtkWidget* parent = gtk_widget_get_parent(priv->view); - if (priv->view && parent != NULL) { - g_object_ref (priv->view); - if (gtk_widget_is_sensitive (priv->view)) - handled = gtk_widget_event (priv->view, (GdkEvent*) event); - g_object_unref (priv->view); - } - - else if ( priv->webview && (parent=gtk_widget_get_parent(priv->webview) ) != NULL) { + if (priv->webview && priv->document && priv->document->iswebdocument == TRUE) { g_object_ref (priv->webview); if (gtk_widget_is_sensitive (priv->webview)) handled = gtk_widget_event (priv->webview, (GdkEvent*) event); g_object_unref (priv->webview); + + } + + else if ( priv->view) { + g_object_ref (priv->view); + if (gtk_widget_is_sensitive (priv->view)) + handled = gtk_widget_event (priv->view, (GdkEvent*) event); + g_object_unref (priv->view); } if (!handled && !EV_WINDOW_IS_PRESENTATION (ev_window)) { |