/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ /* this file is part of evince, a gnome document viewer * * Copyright (C) 2009 litl, LLC * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include #include "evince-view.h" #include "plugin.h" #include "scriptable.h" Plugin::Plugin (NPP instance) : mInstance (instance), mScriptableObject (0), mWindow (0), mDocument (0), mLoadJob (0), mFindJob (0), mTitle (0), mHasSelection (false), mCanFindOnPage (false) { mScrolledWindow = gtk_scrolled_window_new (NULL, NULL); mView = ev_view_new (); g_signal_connect (G_OBJECT (mView), "notify::sizing-mode", G_CALLBACK (Plugin::SizingModeCallback), reinterpret_cast(this)); g_signal_connect (G_OBJECT (mView), "notify::has-selection", G_CALLBACK (Plugin::HasSelectionCallback), reinterpret_cast(this)); g_signal_connect (G_OBJECT (mView), "external-link", G_CALLBACK (Plugin::ExternalLinkCallback), reinterpret_cast(this)); /* FIXME should be set automatically in EvView. It seem to be currently used only to calculate the minimum and maximux allowed zoom level. */ ev_view_set_screen_dpi (EV_VIEW(mView), 96); ev_view_find_set_highlight_search (EV_VIEW (mView), TRUE); gtk_container_add (GTK_CONTAINER(mScrolledWindow), mView); gtk_widget_show (mView); UpdateSizingMode (); } Plugin::~Plugin () { ClearLoadJob (); ClearFindJob (); g_free (mTitle); if (mScriptableObject) { NPN_ReleaseObject (mScriptableObject); mScriptableObject = 0; } } void Plugin::ZoomIn () { ev_view_set_sizing_mode (EV_VIEW (mView), EV_SIZING_FREE); ev_view_zoom_in (EV_VIEW (mView)); } void Plugin::ZoomOut () { ev_view_set_sizing_mode (EV_VIEW (mView), EV_SIZING_FREE); ev_view_zoom_out (EV_VIEW (mView)); } void Plugin::SetZoom (double zoom) { ev_view_set_sizing_mode (EV_VIEW (mView), EV_SIZING_FREE); ev_view_set_zoom (EV_VIEW (mView), zoom, FALSE); } double Plugin::GetZoom (void) const { return ev_view_get_zoom (EV_VIEW (mView)); } void Plugin::ClearFindJob () { if (!mFindJob) return; if (!ev_job_is_finished (mFindJob)) ev_job_cancel (mFindJob); g_signal_handlers_disconnect_by_func (mFindJob, (gpointer)Plugin::FindJobUpdatedCallback, reinterpret_cast (this)); g_object_unref (mFindJob); mFindJob = 0; } void Plugin::Find (const char *text) { if (!mDocument || !EV_IS_DOCUMENT_FIND (mDocument)) return; ev_view_find_search_changed (EV_VIEW (mView)); ClearFindJob (); if (text && text[0]) { EvPageCache *page_cache = ev_page_cache_get (mDocument); mFindJob = ev_job_find_new (mDocument, ev_page_cache_get_current_page (page_cache), ev_page_cache_get_n_pages (page_cache), text, /*case_sensitive=*/ FALSE); g_signal_connect (mFindJob, "updated", G_CALLBACK (Plugin::FindJobUpdatedCallback), reinterpret_cast (this)); ev_job_scheduler_push_job (mFindJob, EV_JOB_PRIORITY_NONE); } else { UpdateActions (); gtk_widget_queue_draw (mView); } } void Plugin::FindNext () { ev_view_find_next (EV_VIEW (mView)); } void Plugin::FindPrevious () { ev_view_find_previous (EV_VIEW (mView)); } void Plugin::CopyClipboard () { ev_view_copy (EV_VIEW (mView)); } void Plugin::SetWindow (GdkNativeWindow window) { if (mWindow != 0) { return; } mWindow = window; GtkWidget *plug = gtk_plug_new (window); gtk_container_add (GTK_CONTAINER (plug), mScrolledWindow); gtk_widget_show (mScrolledWindow); gtk_widget_show (plug); } void Plugin::ShowLoadingError () { /* FIXME display loading error in the UI */ } void Plugin::ClearLoadJob () { if (mLoadJob != NULL) { if (!ev_job_is_finished (mLoadJob)) ev_job_cancel (mLoadJob); g_signal_handlers_disconnect_by_func ( mLoadJob, (gpointer)LoadJobFinishedCallback, reinterpret_cast(this)); g_object_unref (mLoadJob); mLoadJob = NULL; } } void Plugin::Load (const char *fname) { GFile *file = g_file_new_for_path (fname); char *uri = g_file_get_uri (file); g_object_unref (file); ClearLoadJob (); mLoadJob = ev_job_load_new (uri); ev_job_scheduler_push_job (mLoadJob, EV_JOB_PRIORITY_NONE); g_signal_connect (mLoadJob, "finished", G_CALLBACK (LoadJobFinishedCallback), reinterpret_cast(this)); g_free (uri); } NPObject * Plugin::GetScriptableNPObject () { if (!mScriptableObject) { mScriptableObject = NPN_CreateObject ( mInstance, GET_NPOBJECT_CLASS (ScriptablePluginObject)); } if (mScriptableObject) { NPN_RetainObject (mScriptableObject); } return mScriptableObject; } void Plugin::CallBrowser(const char *function_name) { ScriptablePluginObject *obj = static_cast (mScriptableObject); if (obj) { obj->CallBrowser (function_name); } } void Plugin::UpdateSizingMode () { EvSizingMode sizingMode; g_signal_handlers_disconnect_by_func ( mView, (gpointer)ev_view_update_view_size, mScrolledWindow); g_object_get (G_OBJECT (mView), "sizing-mode", &sizingMode, NULL); switch (sizingMode) { case EV_SIZING_BEST_FIT: case EV_SIZING_FIT_WIDTH: ev_view_update_view_size (EV_VIEW (mView), GTK_SCROLLED_WINDOW (mScrolledWindow)); g_object_set (G_OBJECT (mScrolledWindow), "hscrollbar-policy", GTK_POLICY_NEVER, "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL); g_signal_connect (mView, "zoom-invalid", G_CALLBACK (ev_view_update_view_size), mScrolledWindow); break; case EV_SIZING_FREE: g_object_set (G_OBJECT (mScrolledWindow), "hscrollbar-policy", GTK_POLICY_AUTOMATIC, "vscrollbar-policy", GTK_POLICY_AUTOMATIC, NULL); break; } } void Plugin::SizingModeCallback (EvView *view, GParamSpec *pspec, gpointer data) { Plugin *plugin = reinterpret_cast (data); plugin->UpdateSizingMode (); } void Plugin::UpdateActions () { bool can_find = mFindJob && ev_job_find_has_results (EV_JOB_FIND (mFindJob)); if (can_find != mCanFindOnPage) { mCanFindOnPage = can_find; CallBrowser ("onFindChanged"); } } void Plugin::HasSelectionCallback (EvView *view, GParamSpec *pspec, gpointer data) { Plugin *plugin = reinterpret_cast (data); gboolean has_selection = ev_view_get_has_selection (view); if (has_selection != plugin->mHasSelection) { plugin->mHasSelection = has_selection; plugin->CallBrowser ("onClipboardChanged"); } } void Plugin::ExternalLinkCallback (EvView *view, EvLinkAction *action, gpointer data) { Plugin *plugin = reinterpret_cast (data); const char *uri = NULL; switch (ev_link_action_get_action_type (action)) { case EV_LINK_ACTION_TYPE_EXTERNAL_URI: uri = ev_link_action_get_uri (action); if (g_strstr_len (uri, strlen (uri), "http")) { NPN_GetURL (plugin->mInstance, uri, "_blank"); } break; default: break; } } void Plugin::LoadJobFinishedCallback (EvJob *job, gpointer data) { Plugin *plugin = reinterpret_cast (data); if (!ev_job_is_failed (job)) { // let EvView own the document and mDocument have weak ref plugin->mDocument = EV_JOB (job)->document; ev_view_set_document (EV_VIEW(plugin->mView), plugin->mDocument); EvDocumentInfo *doc_info = ev_document_get_info (plugin->mDocument); if (doc_info) { if (doc_info->fields_mask & EV_DOCUMENT_INFO_TITLE) { plugin->mTitle = g_strdup (doc_info->title); plugin->CallBrowser ("onTitleChanged"); } ev_document_info_free (doc_info); } } else { plugin->mDocument = 0; plugin->ShowLoadingError (); } plugin->ClearLoadJob (); } void Plugin::FindJobUpdatedCallback (EvJobFind *job, gint page, gpointer data) { Plugin *plugin = reinterpret_cast (data); plugin->UpdateActions (); ev_view_find_changed (EV_VIEW (plugin->mView), ev_job_find_get_results (job), page); }