diff --git a/backend/Makefile.am b/backend/Makefile.am index db21e4c..4800281 100644 --- a/backend/Makefile.am +++ b/backend/Makefile.am @@ -41,6 +41,8 @@ libevbackend_la_SOURCES= \ ev-document-find.c \ ev-document-find.h \ ev-document-info.h \ + ev-form.h \ + ev-form.c \ ev-ps-exporter.c \ ev-ps-exporter.h \ ev-render-context.h \ diff --git a/backend/ev-document.c b/backend/ev-document.c index 8598c11..22a8b14 100644 --- a/backend/ev-document.c +++ b/backend/ev-document.c @@ -225,6 +225,33 @@ ev_document_render_pixbuf (EvDocument return retval; } +GList * +ev_document_get_form_fields (EvDocument *document, + int page) +{ + EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); + GList *retval; + + LOG("ev_document_get_form_fields"); + if (iface->get_form_fields == NULL) + return NULL; + retval = iface->get_form_fields (document, page); + return retval; +} + +gboolean +ev_document_get_crop_box (EvDocument *document, int page, EvRectangle *rect) +{ + EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); + LOG("ev_document_get_crop_box"); + if (iface->get_crop_box == NULL) + return FALSE; + + iface->get_crop_box(document, page, rect); + return TRUE; + +} + void ev_document_info_free (EvDocumentInfo *info) { diff --git a/backend/ev-document.h b/backend/ev-document.h index d0a0e14..4da29fb 100644 --- a/backend/ev-document.h +++ b/backend/ev-document.h @@ -27,6 +27,7 @@ #include #include "ev-link.h" +#include "ev-form.h" #include "ev-document-info.h" #include "ev-render-context.h" @@ -88,11 +89,18 @@ struct _EvDocumentIface char * (* get_text) (EvDocument *document, int page, EvRectangle *rect); + GList * (* get_form_fields) (EvDocument *document, + int page); + gboolean (* has_attachments) (EvDocument *document); GList * (* get_attachments) (EvDocument *document); GdkPixbuf * (* render_pixbuf) (EvDocument *document, EvRenderContext *rc); EvDocumentInfo * (* get_info) (EvDocument *document); + + void (* get_crop_box) (EvDocument *document, + int page, + EvRectangle *rect); }; GType ev_document_get_type (void); @@ -127,6 +135,13 @@ GdkPixbuf *ev_document_render_pixbu gint ev_rect_cmp (EvRectangle *a, EvRectangle *b); +GList *ev_document_get_form_fields (EvDocument *document, + int page); + +gboolean ev_document_get_crop_box (EvDocument *document, + int page, + EvRectangle *rect); + G_END_DECLS diff --git a/backend/ev-form.c b/backend/ev-form.c new file mode 100644 index 0000000..2812efb --- /dev/null +++ b/backend/ev-form.c @@ -0,0 +1,402 @@ +/* -*- 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) 2005 Red Hat, Inc. + * Copyright (C) 2006 Julien Rebetez + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ev-form.h" + +enum { + PROP_0, + PROP_TITLE, + PROP_TYPE, + PROP_X1, + PROP_Y1, + PROP_X2, + PROP_Y2 +}; + +struct _EvFormField { + GObject base_instance; + EvFormFieldPrivate *priv; +}; + +struct _EvFormFieldClass { + GObjectClass base_class; +}; + +struct _EvFormFieldPrivate { + char *title; + EvFormFieldType type; + gdouble x1; + gdouble y1; + gdouble x2; + gdouble y2; +}; + +G_DEFINE_TYPE (EvFormField, ev_form_field, G_TYPE_OBJECT) + +#define EV_FORM_FIELD_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_FORM_FIELD, EvFormFieldPrivate)) + +GType +ev_form_field_type_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GEnumValue values[] = { + { EV_FORM_FIELD_TYPE_BUTTON, "EV_FORM_FIELD_TYPE_TITLE", "button" }, + { EV_FORM_FIELD_TYPE_TEXT, "EV_FORM_FIELD_TYPE_TEXT", "text" }, + { EV_FORM_FIELD_TYPE_CHOICE, "EV_FORM_FIELD_TYPE_CHOICE", "choice" }, + { EV_FORM_FIELD_TYPE_SIGNATURE, "EV_FORM_FIELD_TYPE_SIGNATURE", "signature"}, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("EvFormFieldType", values); + } + + return type; +} + +const char * +ev_form_field_get_title (EvFormField *self) +{ + g_return_val_if_fail (EV_IS_FORM_FIELD (self), NULL); + + return self->priv->title; +} + + +EvFormFieldType +ev_form_field_get_form_field_type (EvFormField *self) +{ + g_return_val_if_fail (EV_IS_FORM_FIELD (self), 0); + + return self->priv->type; +} + +double +ev_form_field_get_x1 (EvFormField *field) +{ + g_return_val_if_fail (EV_IS_FORM_FIELD(field), 0); + return field->priv->x1; +} + +double +ev_form_field_get_y1 (EvFormField *field) +{ + g_return_val_if_fail (EV_IS_FORM_FIELD(field), 0); + return field->priv->y1; +} + +double +ev_form_field_get_x2 (EvFormField *field) +{ + g_return_val_if_fail (EV_IS_FORM_FIELD(field), 0); + return field->priv->x2; +} + +double +ev_form_field_get_y2 (EvFormField *field) +{ + g_return_val_if_fail (EV_IS_FORM_FIELD(field), 0); + return field->priv->y2; +} + +static void +ev_form_field_get_property (GObject *object, guint prop_id, GValue *value, + GParamSpec *param_spec) +{ + EvFormField *self; + + self = EV_FORM_FIELD (object); + + switch (prop_id) { + case PROP_TITLE: + g_value_set_string (value, self->priv->title); + break; + case PROP_TYPE: + g_value_set_enum (value, self->priv->type); + break; + case PROP_X1: + g_value_set_double (value, self->priv->x1); + break; + case PROP_Y1: + g_value_set_double (value, self->priv->y1); + break; + case PROP_X2: + g_value_set_double (value, self->priv->x2); + break; + case PROP_Y2: + g_value_set_double (value, self->priv->y2); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, + prop_id, + param_spec); + break; + } +} + +static void +ev_form_field_set_property (GObject *object, guint prop_id, const GValue *value, + GParamSpec *param_spec) +{ + EvFormField *field = EV_FORM_FIELD (object); + + switch (prop_id) { + case PROP_TITLE: + field->priv->title = g_strdup (g_value_get_string (value)); + break; + case PROP_TYPE: + field->priv->type = g_value_get_enum (value); + break; + case PROP_X1: + field->priv->x1 = g_value_get_double (value); + break; + case PROP_Y1: + field->priv->y1 = g_value_get_double (value); + break; + case PROP_X2: + field->priv->x2 = g_value_get_double (value); + break; + case PROP_Y2: + field->priv->y2 = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, + prop_id, + param_spec); + break; + } +} + +static void +ev_window_dispose (GObject *object) +{ + EvFormFieldPrivate *priv; + + g_return_if_fail (EV_IS_FORM_FIELD (object)); + + priv = EV_FORM_FIELD (object)->priv; + + if (priv->title) { + g_free (priv->title); + priv->title = NULL; + } + + G_OBJECT_CLASS (ev_form_field_parent_class)->dispose (object); +} + +static void +ev_form_field_init (EvFormField *ev_form_field) +{ + ev_form_field->priv = EV_FORM_FIELD_GET_PRIVATE (ev_form_field); +/* ev_form_field->priv->page = -1;*/ + ev_form_field->priv->type = EV_FORM_FIELD_TYPE_BUTTON; +} + +static void +ev_form_field_class_init (EvFormFieldClass *ev_window_class) +{ + GObjectClass *g_object_class; + + g_object_class = G_OBJECT_CLASS (ev_window_class); + g_object_class->dispose = ev_window_dispose; + g_object_class->set_property = ev_form_field_set_property; + g_object_class->get_property = ev_form_field_get_property; + + g_type_class_add_private (g_object_class, sizeof (EvFormFieldPrivate)); + + g_object_class_install_property (g_object_class, + PROP_TITLE, + g_param_spec_string ("title", + "Form Field Title", + "The form field title", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (g_object_class, + PROP_TYPE, + g_param_spec_enum ("type", + "Form Field Type", + "The form field type", + EV_TYPE_FORM_FIELD_TYPE, + EV_FORM_FIELD_TYPE_BUTTON, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (g_object_class, + PROP_X1, + g_param_spec_double ("x1", + "Bounding rect x1 coord", + "The x1 coord of bounding rect", + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (g_object_class, + PROP_Y1, + g_param_spec_double ("y1", + "Bounding rect y1 coord", + "The y1 coord of bounding rect", + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (g_object_class, + PROP_X2, + g_param_spec_double ("x2", + "Bounding rect x2 coord", + "The x2 coord of bounding rect", + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (g_object_class, + PROP_Y2, + g_param_spec_double ("y2", + "Bounding rect y2 coord", + "The y2 coord of bounding rect", + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +EvFormField * +ev_form_field_new_button (const char *title, + gdouble px1, + gdouble py1, + gdouble px2, + gdouble py2) +{ + return EV_FORM_FIELD (g_object_new (EV_TYPE_FORM_FIELD, + "title", title, + "type", EV_FORM_FIELD_TYPE_BUTTON, + "x1", px1, + "y1", py1, + "x2", px2, + "y2", py2, + NULL)); +} + +EvFormField * +ev_form_field_new_text (const char *title, + gdouble px1, + gdouble py1, + gdouble px2, + gdouble py2) +{ + return EV_FORM_FIELD (g_object_new (EV_TYPE_FORM_FIELD, + "title", title, + "type", EV_FORM_FIELD_TYPE_TEXT, + "x1", px1, + "y1", py1, + "x2", px2, + "y2", py2, + NULL)); +} + +EvFormField * +ev_form_field_new_choice (const char *title, + gdouble px1, + gdouble py1, + gdouble px2, + gdouble py2) +{ + return EV_FORM_FIELD (g_object_new (EV_TYPE_FORM_FIELD, + "title", title, + "type", EV_FORM_FIELD_TYPE_CHOICE, + "x1", px1, + "y1", py1, + "x2", px2, + "y2", py2, + NULL)); +} + +EvFormField * +ev_form_field_new_signature (const char *title, + gdouble px1, + gdouble py1, + gdouble px2, + gdouble py2) +{ + return EV_FORM_FIELD (g_object_new (EV_TYPE_FORM_FIELD, + "title", title, + "type", EV_FORM_FIELD_TYPE_SIGNATURE, + "x1", px1, + "y1", py1, + "x2", px2, + "y2", py2, + NULL)); +} + +static void +ev_form_field_mapping_free_foreach (EvFormFieldMapping *form_field_mapping) +{ + g_object_unref (G_OBJECT (form_field_mapping->field)); + g_free (form_field_mapping); +} + +void +ev_form_field_mapping_free (GList *form_field_mapping) +{ + if (form_field_mapping == NULL) + return; + + g_list_foreach (form_field_mapping, (GFunc) (ev_form_field_mapping_free_foreach), NULL); + g_list_free (form_field_mapping); +} + + +EvFormField * +ev_form_field_mapping_find (GList *form_field_mapping, + gdouble x, + gdouble y) +{ + GList *list; + EvFormField *field = NULL; + int i; + + i = 0; + + for (list = form_field_mapping; list; list = list->next) { + EvFormFieldMapping *mapping = list->data; + + i++; + if ((x >= mapping->x1) && + (y >= mapping->y1) && + (x <= mapping->x2) && + (y <= mapping->y2)) { + field = mapping->field; + break; + } + } + + return field; +} + diff --git a/backend/ev-form.h b/backend/ev-form.h new file mode 100644 index 0000000..d8506fe --- /dev/null +++ b/backend/ev-form.h @@ -0,0 +1,100 @@ +/* this file is part of evince, a gnome document viewer + * + * Copyright (C) 2005 Red Hat, Inc. + * Copyright (C) 2006 Julien Rebetez + * + * 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. + */ + +#ifndef EV_FORM_H +#define EV_FORM_H + +#include + +G_BEGIN_DECLS + +typedef struct _EvFormField EvFormField; +typedef struct _EvFormFieldClass EvFormFieldClass; +typedef struct _EvFormFieldPrivate EvFormFieldPrivate; + +#define EV_TYPE_FORM_FIELD (ev_form_field_get_type()) +#define EV_FORM_FIELD(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_FORM_FIELD, EvFormField)) +#define EV_FORM_FIELD_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), EV_TYPE_FORM_FIELD, EvFormField)) +#define EV_IS_FORM_FIELD(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_FORM_FIELD)) +#define EV_IS_FORM_FIELD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_FORM_FIELD)) +#define EV_FORM_FIELD_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_LINK, EvLinkClass)) + +#define EV_TYPE_FORM_FIELD_TYPE (ev_form_field_type_get_type ()) + + + +typedef enum +{ + EV_FORM_FIELD_TYPE_BUTTON, + EV_FORM_FIELD_TYPE_TEXT, + EV_FORM_FIELD_TYPE_CHOICE, + EV_FORM_FIELD_TYPE_SIGNATURE +} EvFormFieldType; + +GType ev_form_field_type_get_type (void); +GType ev_form_field_get_type (void); + +EvFormField *ev_form_field_new_button (const char *title, + gdouble px1, + gdouble py1, + gdouble px2, + gdouble py2); +EvFormField *ev_form_field_new_text (const char *title, + gdouble px1, + gdouble py1, + gdouble px2, + gdouble py2); +EvFormField *ev_form_field_new_choice (const char *title, + gdouble px1, + gdouble py1, + gdouble px2, + gdouble py2); +EvFormField *ev_form_field_new_signature (const char *title, + gdouble px1, + gdouble py1, + gdouble px2, + gdouble py2); + +const char* ev_form_field_get_title (EvFormField *field); +EvFormFieldType ev_form_field_get_form_field_type (EvFormField *field); +double ev_form_field_get_x1 (EvFormField *field); +double ev_form_field_get_y1 (EvFormField *field); +double ev_form_field_get_x2 (EvFormField *field); +double ev_form_field_get_y2 (EvFormField *field); + + +/* Form Mapping Stuff */ +typedef struct _EvFormFieldMapping EvFormFieldMapping; +struct _EvFormFieldMapping +{ + EvFormField *field; + gdouble x1; + gdouble y1; + gdouble x2; + gdouble y2; +}; + +void ev_form_field_mapping_free (GList *form_field_mapping); +EvFormField *ev_form_field_mapping_find (GList *form_field_mapping, + gdouble x, + gdouble y); +G_END_DECLS + +#endif /* !EV_FORM_H */ diff --git a/pdf/ev-poppler.cc b/pdf/ev-poppler.cc index a3d3872..05f1276 100644 --- a/pdf/ev-poppler.cc +++ b/pdf/ev-poppler.cc @@ -620,6 +620,86 @@ pdf_document_get_text (EvDocument *docum return text; } +static GList * +pdf_document_get_form_fields (EvDocument *document, + int page) +{ + PdfDocument *pdf_document; + PopplerPage *poppler_page; + GList *retval = NULL; + GList *fields; + GList *list; + double height; + //printf("pdf_document_get_form_fields\n"); + + pdf_document = PDF_DOCUMENT (document); + poppler_page = poppler_document_get_page (pdf_document->document, + page); + fields = poppler_page_get_form_fields (poppler_page); + poppler_page_get_size (poppler_page, NULL, &height); + + for (list = fields; list; list = list->next) { + PopplerFormField *field; + EvFormFieldMapping *field_mapping; + double rect[4]; + + field = (PopplerFormField *)list->data; + /* Invert y for X-style coordinates */ + rect[0] = field->area.x1; rect[1] = height - field->area.y2; + rect[2] = field->area.x2; rect[3] = height - field->area.y1; + + field_mapping = g_new (EvFormFieldMapping, 1); + switch (field->type) { + case POPPLER_FORM_FIELD_BUTTON: + field_mapping->field = ev_form_field_new_button("test",rect[0],rect[1],rect[2],rect[3]); + break; + case POPPLER_FORM_FIELD_TEXT: + field_mapping->field = ev_form_field_new_text("test",rect[0],rect[1],rect[2],rect[3]); + break; + case POPPLER_FORM_FIELD_CHOICE: + field_mapping->field = ev_form_field_new_choice("test",rect[0],rect[1],rect[2],rect[3]); + break; + case POPPLER_FORM_FIELD_SIGNATURE: + field_mapping->field = ev_form_field_new_signature("test",rect[0],rect[1],rect[2],rect[3]); + break; + default: + //we should add a widget which said it is a malformed entry or something + field_mapping->field = ev_form_field_new_button("test",rect[0],rect[1],rect[2],rect[3]); + printf("pdf_document_get_form_fields, unhandled field->type\n"); + break; + } + field_mapping->x1 = rect[0]; + field_mapping->x2 = rect[2]; + field_mapping->y1 = rect[1]; + field_mapping->y2 = rect[3]; + + retval = g_list_prepend(retval, field_mapping); + } + poppler_page_free_form_fields(fields); + g_object_unref (poppler_page); + + return g_list_reverse(retval); +} + +void +pdf_document_get_crop_box (EvDocument *document, + int page, + EvRectangle *rect) +{ + PdfDocument *pdf_document; + PopplerPage *poppler_page; + PopplerRectangle poppler_rect; + + //printf("pdf_document_get_crop_box\n"); + pdf_document = PDF_DOCUMENT (document); + poppler_page = poppler_document_get_page (pdf_document->document, page); + poppler_page_get_crop_box (poppler_page, &poppler_rect); + rect->x1 = poppler_rect.x1; + rect->x2 = poppler_rect.x2; + rect->y1 = poppler_rect.y1; + rect->y2 = poppler_rect.y2; +} + static void pdf_document_document_iface_init (EvDocumentIface *iface) { @@ -632,6 +712,8 @@ pdf_document_document_iface_init (EvDocu iface->get_attachments = pdf_document_get_attachments; iface->render_pixbuf = pdf_document_render_pixbuf; iface->get_text = pdf_document_get_text; + iface->get_form_fields = pdf_document_get_form_fields; + iface->get_crop_box = pdf_document_get_crop_box; iface->can_get_text = pdf_document_can_get_text; iface->get_info = pdf_document_get_info; }; @@ -1501,3 +1583,6 @@ pdf_document_new (void) { return PDF_DOCUMENT (g_object_new (PDF_TYPE_DOCUMENT, NULL)); } + + + diff --git a/shell/ev-jobs.c b/shell/ev-jobs.c index e9f29eb..4861484 100644 --- a/shell/ev-jobs.c +++ b/shell/ev-jobs.c @@ -213,6 +213,7 @@ ev_job_render_new (EvDocument *docu EvRectangle *selection_points, GdkColor *text, GdkColor *base, + gboolean include_form, gboolean include_links, gboolean include_text, gboolean include_selection) @@ -231,6 +232,7 @@ ev_job_render_new (EvDocument *docu job->target_height = height; job->text = *text; job->base = *base; + job->include_form = include_form; job->include_links = include_links; job->include_text = include_text; job->include_selection = include_selection; @@ -271,6 +273,10 @@ ev_job_render_run (EvJobRender *job) G_CALLBACK (render_finished_cb), job); } else { job->pixbuf = ev_document_render_pixbuf (EV_JOB (job)->document, job->rc); + //printf("include_form: %i\n", job->include_form); + if (job->include_form) + job->form_field_mapping = + ev_document_get_form_fields (EV_JOB (job)->document, job->rc->page); if (job->include_links && EV_IS_DOCUMENT_LINKS (EV_JOB (job)->document)) job->link_mapping = ev_document_links_get_links (EV_DOCUMENT_LINKS (EV_JOB (job)->document), diff --git a/shell/ev-jobs.h b/shell/ev-jobs.h index bf9c86b..472f3cc 100644 --- a/shell/ev-jobs.h +++ b/shell/ev-jobs.h @@ -116,13 +116,15 @@ struct _EvJobRender GList *link_mapping; GdkRegion *text_mapping; + GList *form_field_mapping; GdkPixbuf *selection; GdkRegion *selection_region; EvRectangle selection_points; GdkColor base; GdkColor text; - + + gint include_form : 1; gint include_links : 1; gint include_text : 1; gint include_selection : 1; @@ -191,6 +193,7 @@ EvJob *ev_job_render_new EvRectangle *selection_points, GdkColor *text, GdkColor *base, + gboolean include_form, gboolean include_links, gboolean include_text, gboolean include_selection); diff --git a/shell/ev-pixbuf-cache.c b/shell/ev-pixbuf-cache.c index fff1249..710be8c 100644 --- a/shell/ev-pixbuf-cache.c +++ b/shell/ev-pixbuf-cache.c @@ -12,6 +12,7 @@ typedef struct _CacheJobInfo GdkPixbuf *pixbuf; GList *link_mapping; GdkRegion *text_mapping; + GList *form_field_mapping; /* Selection data. * Selection_points are the coordinates encapsulated in selection. @@ -22,6 +23,7 @@ typedef struct _CacheJobInfo GdkPixbuf *selection; GdkRegion *selection_region; + } CacheJobInfo; struct _EvPixbufCache @@ -148,6 +150,10 @@ dispose_cache_job_info (CacheJobInfo *jo g_object_unref (G_OBJECT (job_info->pixbuf)); job_info->pixbuf = NULL; } + if (job_info->form_field_mapping) { + ev_form_field_mapping_free (job_info->form_field_mapping); + job_info->form_field_mapping = NULL; + } if (job_info->link_mapping) { ev_link_mapping_free (job_info->link_mapping); job_info->link_mapping = NULL; @@ -312,6 +318,7 @@ move_one_job (CacheJobInfo *job_info, job_info->job = NULL; job_info->pixbuf = NULL; job_info->link_mapping = NULL; + job_info->form_field_mapping = NULL; if (new_priority != priority && target_page->job) { ev_job_queue_update_job (target_page->job, new_priority); @@ -391,6 +398,7 @@ ev_pixbuf_cache_update_range (EvPixbufCa pixbuf_cache->end_page = end_page; } + static void copy_job_to_job_info (EvJobRender *job_render, CacheJobInfo *job_info, @@ -410,7 +418,9 @@ copy_job_to_job_info (EvJobRender *job if (job_render->link_mapping) job_info->link_mapping = job_render->link_mapping; if (job_render->text_mapping) - job_info->text_mapping = job_render->text_mapping; + job_info->text_mapping = job_render->text_mapping; + if (job_render->form_field_mapping) + job_info->form_field_mapping = job_render->form_field_mapping; if (job_render->include_selection) { pixbuf = g_object_ref (job_render->selection); @@ -497,6 +507,7 @@ add_job_if_needed (EvPixbufCache *pixbuf gfloat scale, EvJobPriority priority) { + gboolean include_form = FALSE; gboolean include_links = FALSE; gboolean include_text = FALSE; gboolean include_selection = FALSE; @@ -524,6 +535,11 @@ add_job_if_needed (EvPixbufCache *pixbuf } /* Figure out what else we need for this job */ + /* if we don't set include_form to TRUE, sometimes the render job's form_field_mapping is NULL + although the page has some fields + if (job_info->form_field_mapping == NULL) + */ + include_form = TRUE; if (job_info->link_mapping == NULL) include_links = TRUE; if (job_info->text_mapping == NULL) @@ -541,6 +557,7 @@ add_job_if_needed (EvPixbufCache *pixbuf width, height, &(job_info->target_points), text, base, + include_form, include_links, include_text, include_selection); @@ -662,6 +679,24 @@ ev_pixbuf_cache_get_link_mapping (EvPixb return job_info->link_mapping; } +GList * +ev_pixbuf_cache_get_form_field_mapping (EvPixbufCache *pixbuf_cache, + gint page) +{ + CacheJobInfo *job_info; + + job_info = find_job_cache (pixbuf_cache, page); + if(job_info == NULL) + return NULL; + + if(job_info->job && + EV_JOB(job_info->job)->finished) { + copy_job_to_job_info (EV_JOB_RENDER(job_info->job), job_info, pixbuf_cache); + } + return job_info->form_field_mapping; +} + + static gboolean new_selection_pixbuf_needed (EvPixbufCache *pixbuf_cache, CacheJobInfo *job_info, diff --git a/shell/ev-pixbuf-cache.h b/shell/ev-pixbuf-cache.h index c956832..cbe6172 100644 --- a/shell/ev-pixbuf-cache.h +++ b/shell/ev-pixbuf-cache.h @@ -63,6 +63,8 @@ GList *ev_pixbuf_cache_get_link_ gint page); GdkRegion *ev_pixbuf_cache_get_text_mapping (EvPixbufCache *pixbuf_cache, gint page); +GList *ev_pixbuf_cache_get_form_field_mapping (EvPixbufCache *pixbuf_cache, + gint page); void ev_pixbuf_cache_clear (EvPixbufCache *pixbuf_cache); void ev_pixbuf_cache_style_changed (EvPixbufCache *pixbuf_cache); diff --git a/shell/ev-view-private.h b/shell/ev-view-private.h index a53fe54..8eac977 100644 --- a/shell/ev-view-private.h +++ b/shell/ev-view-private.h @@ -57,7 +57,8 @@ typedef enum { } EvViewCursor; struct _EvView { - GtkWidget parent_instance; + //GtkWidget parent_instance; + GtkContainer parent_instance; EvDocument *document; @@ -116,10 +117,18 @@ struct _EvView { /* Links */ GtkWidget *link_tooltip; EvLink *hovered_link; + + /* Container */ + gint current_width; + gint current_height; + /*GList *children; + GList *field_widgets;*/ + GList **children; }; struct _EvViewClass { - GtkWidgetClass parent_class; + //GtkWidgetClass parent_class; + GtkContainerClass parent_class; void (*set_scroll_adjustments) (EvView *view, GtkAdjustment *hadjustment, diff --git a/shell/ev-view.c b/shell/ev-view.c index 3c2090d..b192430 100644 --- a/shell/ev-view.c +++ b/shell/ev-view.c @@ -41,6 +41,7 @@ #include "ev-page-cache.h" #include "ev-pixbuf-cache.h" #include "ev-tooltip.h" +#include "ev-debug.h" #define EV_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass)) #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW)) @@ -62,6 +63,12 @@ enum { }; enum { + CHILD_PROP_0, + CHILD_PROP_X, + CHILD_PROP_Y +}; + +enum { SIGNAL_BINDING_ACTIVATED, SIGNAL_ZOOM_INVALID, SIGNAL_EXTERNAL_LINK, @@ -91,6 +98,16 @@ typedef enum { EV_VIEW_FIND_PREV } EvViewFindDirection; +typedef struct _EvViewChild EvViewChild; + +struct _EvViewChild { + GtkWidget *widget; + gint x; + gint y; + gint page; + EvPoint p[2]; +}; + #define ZOOM_IN_FACTOR 1.2 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR) @@ -160,6 +177,14 @@ static char* tip_from_link static void handle_link_over_xy (EvView *view, gint x, gint y); + +/*** Forms ***/ +static EvFormField * ev_view_get_form_field_at_location (EvView *view, + gdouble x, + gdouble y); +static void handle_form_field_over_xy (EvView* view, + gint x, + gint y); /*** GtkWidget implementation ***/ static void ev_view_size_request_continuous_dual_page (EvView *view, GtkRequisition *requisition); @@ -312,7 +337,59 @@ static void ev_view_primary_clear_ static void ev_view_update_primary_selection (EvView *ev_view); -G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET) +/*** Container ***/ +static void ev_view_map (GtkWidget *widget); + +static void ev_view_add (GtkContainer *cont, + GtkWidget *widget); + +static void ev_view_remove (GtkContainer *cont, + GtkWidget *widget); + +static void ev_view_put (EvView *view, + GtkWidget *widget, + gint x, + gint y, + double x1, + double y1, + double x2, + double y2, + gint page); + +static void ev_view_forall (GtkContainer *cont, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); + +static void ev_view_set_child_property (GtkContainer *cont, + GtkWidget *child, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void ev_view_get_child_property (GtkContainer *cont, + GtkWidget *child, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void ev_view_allocate_child (EvView *view, + EvViewChild *child); +static void ev_view_move_internal (EvView *view, + GtkWidget *widget, + gboolean change_x, + gint x, + gboolean change_y, + gint y); + +/*** Form fields ***/ +static void form_field_widgets_clean_and_add (EvView *view); +static void form_field_widgets_resize (EvView *view); +static void form_field_widgets_create_for_page (EvView *view, gint page); + + +//G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET) +G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_CONTAINER) static void scroll_to_current_page (EvView *view, GtkOrientation orientation) @@ -427,6 +504,8 @@ view_update_range_and_current_page (EvVi { gint current_page; gint best_current_page = -1; + gint initial_start_page = view->start_page; + gint initial_end_page = view->end_page; if (view->pending_scroll != SCROLL_TO_KEEP_POSITION) return; @@ -444,7 +523,7 @@ view_update_range_and_current_page (EvVi if (!(view->vadjustment && view->hadjustment)) return; - + current_area.x = view->hadjustment->value; current_area.width = view->hadjustment->upper; current_area.y = view->vadjustment->value; @@ -501,6 +580,15 @@ view_update_range_and_current_page (EvVi ev_page_cache_set_current_page (view->page_cache, best_current_page); } + if (view->start_page != initial_start_page || view->end_page != initial_end_page) { + + //form_field_widgets_clean_and_add (view); + gint i; + for (i=view->start_page; i<=view->end_page; i++) { + form_field_widgets_create_for_page (view, i); + } + } + ev_pixbuf_cache_set_page_range (view->pixbuf_cache, view->start_page, view->end_page, @@ -1446,6 +1534,46 @@ handle_link_over_xy (EvView *view, gint return; } +/*** Form ***/ +static EvFormField * +ev_view_get_form_field_at_location (EvView *view, + gdouble x, + gdouble y) +{ + gint page = -1; + gint x_offset = 0, y_offset = 0; + GList *form_field_mapping; + + x += view->scroll_x; + y += view->scroll_y; + + find_page_at_location (view, x, y, &page, &x_offset, &y_offset); + + if (page == -1) + return NULL; + + form_field_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache, page); + + if (form_field_mapping) + return ev_form_field_mapping_find (form_field_mapping, x_offset / view->scale, y_offset / view->scale); + else + return NULL; +} + + +static void +handle_form_field_over_xy (EvView *view, gint x, gint y) +{ + EvFormField *field; + + field = ev_view_get_form_field_at_location (view, x, y); + if(field) { + ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK); + } else { + ev_view_set_cursor(view, EV_VIEW_CURSOR_NORMAL); + } +} + /*** GtkWidget implementation ***/ static void @@ -1573,6 +1701,7 @@ ev_view_size_request (GtkWidget *wi GtkRequisition *requisition) { EvView *view = EV_VIEW (widget); + gint page_count; if (view->document == NULL) { requisition->width = 1; @@ -1586,6 +1715,34 @@ ev_view_size_request (GtkWidget *wi return; } + if (view->children) { + for (page_count = view->start_page; page_count <= view->end_page; page_count++) { + GList *tmp_list; + tmp_list = view->children[page_count]; + while (tmp_list) { + EvViewChild *child = tmp_list->data; + GtkRequisition child_requisition; + + tmp_list = tmp_list->next; + gtk_widget_size_request (child->widget, &child_requisition); + } + } + } + + //printf("%i %i => requested : %i %i\n", view->current_width, view->current_height, requisition->width, requisition->height); + if(!(requisition->width == view->current_width && requisition->height == view->current_height)) { + //printf("call to form_field_widgets\n"); + + /* FIXME: In the case we have a sizing mode other than EV_SIZING_FIT_*, since the + rendered page 'move' (it is centered) when we resize, we need to move + the child widgets too, like what we do in 'on_adjustment_value_changed'*/ + + //form_field_widgets_clean_and_add(view); + form_field_widgets_resize(view); + } + view->current_width = requisition->width; + view->current_height = requisition->height; + if (view->continuous && view->dual_page) ev_view_size_request_continuous_dual_page (view, requisition); else if (view->continuous) @@ -1601,6 +1758,20 @@ ev_view_size_allocate (GtkWidget *w GtkAllocation *allocation) { EvView *view = EV_VIEW (widget); + gint page_count; + + if (view->children) { + for (page_count = view->start_page; page_count <= view->end_page; page_count++) { + GList *tmp_list; + tmp_list = view->children[page_count]; + while (tmp_list) { + EvViewChild *child = tmp_list->data; + + tmp_list = tmp_list->next; + ev_view_allocate_child(view, child); + } + } + } if (view->sizing_mode == EV_SIZING_FIT_WIDTH || view->sizing_mode == EV_SIZING_BEST_FIT) { @@ -1627,6 +1798,7 @@ ev_view_realize (GtkWidget *widget) { EvView *view = EV_VIEW (widget); GdkWindowAttr attributes; + gint page_count; GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); @@ -1662,6 +1834,22 @@ ev_view_realize (GtkWidget *widget) gdk_window_set_background (widget->window, &widget->style->black); else gdk_window_set_background (widget->window, &widget->style->mid [GTK_STATE_NORMAL]); + + + if (view->children) { + for (page_count = view->start_page; page_count <= view->end_page; page_count++) { + GList *tmp_list; + tmp_list = view->children[page_count]; + while (tmp_list) { + EvViewChild *child = tmp_list->data; + + tmp_list = tmp_list->next; + gtk_widget_set_parent_window(child->widget, GTK_WIDGET(view)->window); + ev_view_allocate_child(view, child); + } + } + } + } static gboolean @@ -1741,6 +1929,7 @@ find_selection_for_page (EvView *view, return NULL; } + static gboolean ev_view_expose_event (GtkWidget *widget, GdkEventExpose *event) @@ -1778,7 +1967,7 @@ ev_view_expose_event (GtkWidget *wi if (page_ready && EV_IS_DOCUMENT_FIND (view->document)) highlight_find_results (view, i); } - + (* GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event) (widget, event); return FALSE; } @@ -2019,6 +2208,7 @@ ev_view_motion_notify_event (GtkWidget } else if (view->pressed_button <= 0 && view->rotation == 0) { handle_link_over_xy (view, x, y); + handle_form_field_over_xy (view, x, y); return TRUE; } @@ -2278,7 +2468,7 @@ draw_one_page (EvView *view, GdkRectangle real_page_area; EvViewSelection *selection; gint current_page; - + g_assert (view->document); if (! gdk_rectangle_intersect (page_area, expose_area, &overlap)) @@ -2382,7 +2572,7 @@ ev_view_finalize (GObject *object) { EvView *view = EV_VIEW (object); - LOG ("Finalize"); +// LOG ("Finalize"); g_free (view->status); g_free (view->find_status); @@ -2542,6 +2732,7 @@ ev_view_class_init (EvViewClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); GtkBindingSet *binding_set; @@ -2549,6 +2740,10 @@ ev_view_class_init (EvViewClass *class) object_class->set_property = ev_view_set_property; object_class->get_property = ev_view_get_property; + container_class->set_child_property = ev_view_set_child_property; + container_class->get_child_property = ev_view_get_child_property; + + widget_class->map = ev_view_map; widget_class->expose_event = ev_view_expose_event; widget_class->button_press_event = ev_view_button_press_event; widget_class->motion_notify_event = ev_view_motion_notify_event; @@ -2567,6 +2762,10 @@ ev_view_class_init (EvViewClass *class) widget_class->popup_menu = ev_view_popup_menu; gtk_object_class->destroy = ev_view_destroy; + container_class->add = ev_view_add; + container_class->remove = ev_view_remove; + container_class->forall = ev_view_forall; + class->set_scroll_adjustments = ev_view_set_scroll_adjustments; class->binding_activated = ev_view_scroll; @@ -2615,6 +2814,26 @@ ev_view_class_init (EvViewClass *class) G_TYPE_NONE, 1, G_TYPE_OBJECT); + gtk_container_class_install_child_property (container_class, + CHILD_PROP_X, + g_param_spec_int ("x", + "X position", + "X position of child widget", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + gtk_container_class_install_child_property (container_class, + CHILD_PROP_Y, + g_param_spec_int ("y", + "Y position", + "Y position of child widget", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_STATUS, g_param_spec_string ("status", @@ -2712,7 +2931,10 @@ static void ev_view_init (EvView *view) { GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS); - + + view->current_width = 0; + view->current_height = 0; + view->children = NULL; view->spacing = 5; view->scale = 1.0; view->current_page = 0; @@ -2732,6 +2954,216 @@ ev_view_init (EvView *view) view->jump_to_find_result = TRUE; } +static void remove_field_widget (gpointer data, gpointer user_data) +{ + g_return_if_fail(GTK_IS_CONTAINER(user_data)); + g_return_if_fail(GTK_IS_WIDGET(data)); + ev_view_remove(GTK_CONTAINER(user_data), GTK_WIDGET(data)); +} + +/*static void field_inc_count (gpointer data, gpointer user_data) +{ + int* count = (int*)user_data; + (*count)++; +}*/ + +static void +form_field_widgets_resize_for_page (EvView *view, gint page) +{ + GList *tmp_list; + tmp_list = view->children[page]; + while (tmp_list) { + GdkPoint d[2]; + gint v[4]; + EvViewChild *child = tmp_list->data; + tmp_list = tmp_list->next; + + doc_point_to_view_point(view, child->page, &child->p[0], &d[0]); + doc_point_to_view_point(view, child->page, &child->p[1], &d[1]); + + v[0] = d[0].x - view->scroll_x; v[1] = d[0].y - view->scroll_y; + v[2] = d[1].x - view->scroll_x; v[3] = d[1].y - view->scroll_y; + + gtk_widget_set_usize(child->widget, v[2]-v[0], v[3]-v[1]); + ev_view_move_internal(view, child->widget, TRUE, v[0], TRUE, v[1]); + } + +} + +static void +form_field_widgets_resize (EvView *view) +{ + gint page_count; + for (page_count = view->start_page; page_count <= view->end_page; page_count++) { + form_field_widgets_resize_for_page(view, page_count); + } +} + +static void +form_field_widgets_create_for_page (EvView *view, gint page) +{ + GList *form_field_mapping; + GList *tmp_list; + EvRectangle crop_box; + gboolean has_crop; + + /* If they are already added, we need to 'refresh' them, otherwise they aren't going to be shown + until the next resize from the user. + FIXME: resize involve a lot of computation, we need to call only the relevant function + */ + if (view->children[page]) { + form_field_widgets_resize_for_page(view, page); + return; + } + + has_crop = ev_document_get_crop_box(view->document, page, &crop_box); + form_field_mapping = ev_pixbuf_cache_get_form_field_mapping(view->pixbuf_cache, page); + tmp_list = form_field_mapping; + while (tmp_list) { + EvFormFieldMapping *mapping = tmp_list->data; + EvFormField *field; + GtkWidget *wid; + EvPoint p[2]; + GdkPoint d[2]; + gint v[4]; + + tmp_list = tmp_list->next; + + field = mapping->field; + //printf("field : %i\n", ev_form_field_get_form_field_type(field)); + p[0].x = ev_form_field_get_x1(field); + p[0].y = ev_form_field_get_y1(field); + p[1].x = ev_form_field_get_x2(field); + p[1].y = ev_form_field_get_y2(field); + if(has_crop) { + p[0].x -= crop_box.x1; + p[0].y += crop_box.y1; + p[1].x -= crop_box.x1; + p[1].y += crop_box.y1; + } + + doc_point_to_view_point(view, page, &p[0], &d[0]); + doc_point_to_view_point(view, page, &p[1], &d[1]); + v[0] = d[0].x - view->scroll_x; v[1] = d[0].y - view->scroll_y; + v[2] = d[1].x - view->scroll_x; v[3] = d[1].y - view->scroll_y; + + switch (ev_form_field_get_form_field_type(field)) { + case EV_FORM_FIELD_TYPE_BUTTON: + wid = gtk_button_new_with_label("Button"); + break; + case EV_FORM_FIELD_TYPE_TEXT: + wid = gtk_entry_new(); + break; + case EV_FORM_FIELD_TYPE_CHOICE: + wid = gtk_label_new ("choice"); + break; + case EV_FORM_FIELD_TYPE_SIGNATURE: + wid = gtk_label_new ("signature"); + break; + default: + printf("form_field_widgets_clean_and_add, unhandled value\n"); + break; + } + gtk_widget_set_usize(wid, v[2]-v[0], v[3]-v[1]); + ev_view_put(view, wid,v[0],v[1],p[0].x,p[0].y,p[1].x,p[1].y,page); + gtk_widget_show(wid); + + //view->field_widgets = g_list_append(view->field_widgets, wid); + + } +} + +/*static void +form_field_widgets_clean_and_add (EvView *view) +{ + //int count = 0; + GList *form_field_mapping; + GList *tmp_list; + gint page_count = 0; + + if (view->field_widgets) { + //g_list_foreach(view->field_widgets, (GFunc)field_inc_count, &count); + g_list_foreach(view->field_widgets, (GFunc)remove_field_widget, GTK_CONTAINER(view)); + view->field_widgets = NULL; + } + //printf("- removed %i widgets\n", count); + //printf(" range : %i -> %i\n", view->start_page, view->end_page); + //count = 0; + for (page_count = view->start_page; page_count <= view->end_page; page_count++) { + EvRectangle crop_box; + gboolean has_crop; + + has_crop = ev_document_get_crop_box(view->document, page_count, &crop_box); + + form_field_mapping = ev_pixbuf_cache_get_form_field_mapping(view->pixbuf_cache, page_count); + if (form_field_mapping == NULL) { + //printf("form_field_mapping == NULL -> skipping\n"); + continue; + } + + tmp_list = form_field_mapping; + while (tmp_list) { + EvFormFieldMapping *mapping = tmp_list->data; + EvFormField *field; + GtkWidget *wid; + EvPoint p[2]; + GdkPoint d[2]; + gint v[4]; + + tmp_list = tmp_list->next; + + field = mapping->field; + //printf("field : %i\n", ev_form_field_get_form_field_type(field)); + p[0].x = ev_form_field_get_x1(field); + p[0].y = ev_form_field_get_y1(field); + p[1].x = ev_form_field_get_x2(field); + p[1].y = ev_form_field_get_y2(field); + if(has_crop) { + p[0].x -= crop_box.x1; + p[0].y += crop_box.y1; + p[1].x -= crop_box.x1; + p[1].y += crop_box.y1; + } + + doc_point_to_view_point(view, page_count, &p[0], &d[0]); + doc_point_to_view_point(view, page_count, &p[1], &d[1]); + //printf("\nd[0]: %i, %i\n", d[0].x, d[0].y); + // printf("d[1]: %i, %i\n", d[1].x, d[1].y); + // printf("view->scroll: %i, %i\n", view->scroll_x, view->scroll_y); + v[0] = d[0].x - view->scroll_x; v[1] = d[0].y - view->scroll_y; + v[2] = d[1].x - view->scroll_x; v[3] = d[1].y - view->scroll_y; + //printf("v[]: %i, %i, %i, %i\n", v[0],v[1],v[2],v[3]); + switch (ev_form_field_get_form_field_type(field)) { + case EV_FORM_FIELD_TYPE_BUTTON: + wid = gtk_button_new_with_label("Button"); + break; + case EV_FORM_FIELD_TYPE_TEXT: + wid = gtk_entry_new(); + break; + case EV_FORM_FIELD_TYPE_CHOICE: + wid = gtk_label_new ("choice"); + break; + case EV_FORM_FIELD_TYPE_SIGNATURE: + wid = gtk_label_new ("signature"); + break; + default: + printf("form_field_widgets_clean_and_add, unhandled value\n"); + break; + } + //wid = gtk_button_new_with_label("Bonjour"); + //printf("%i, %i\n", v[2]-v[0], v[3]-v[1]); + gtk_widget_set_usize(wid, v[2]-v[0], v[3]-v[1]); + ev_view_put(view, wid,v[0],v[1],p[0].x,p[0].y,p[1].x,p[1].y,page_count); + gtk_widget_show(wid); + + view->field_widgets = g_list_append(view->field_widgets, wid); + //count++; + + } + } + //printf("- added %i widgets\n", count); +}*/ + /*** Callbacks ***/ static void @@ -2757,9 +3189,15 @@ static void job_finished_cb (EvPixbufCache *pixbuf_cache, EvView *view) { + gint i; + for (i=view->start_page; i<=view->end_page; i++) { + form_field_widgets_create_for_page(view, i); + } + //form_field_widgets_clean_and_add(view); gtk_widget_queue_draw (GTK_WIDGET (view)); } + static void page_changed_cb (EvPageCache *page_cache, int new_page, @@ -2783,7 +3221,8 @@ static void on_adjustment_value_changed EvView *view) { int dx = 0, dy = 0; - + gint page_count; + if (! GTK_WIDGET_REALIZED (view)) return; @@ -2801,6 +3240,17 @@ static void on_adjustment_value_changed view->scroll_y = 0; } + /* move the childs widgets (form fields) in order they follow the window scrolling */ + for (page_count = view->start_page; page_count <= view->end_page; page_count++) { + GList *tmp_list; + tmp_list = view->children[page_count]; + while (tmp_list) { + EvViewChild *child = tmp_list->data; + tmp_list = tmp_list->next; + + ev_view_move_internal(view, child->widget, TRUE, child->x + dx, TRUE, child->y + dy); + } + } if (view->pending_resize) gtk_widget_queue_draw (GTK_WIDGET (view)); @@ -2852,6 +3302,45 @@ ev_view_set_loading (EvView *view, gtk_widget_queue_draw (GTK_WIDGET (view)); } +static void +setup_fields_list (EvView *view) +{ + gint n_pages = 0; + gint i; + + n_pages = EV_DOCUMENT_GET_IFACE(view->document)->get_n_pages(view->document); + view->children = g_new(GList*, n_pages); + for (i=0; ichildren[i] = NULL; + } +} + +static void +clear_fields_list (EvView *view) +{ + if (!view->document) + return; + + gint n_pages = EV_DOCUMENT_GET_IFACE(view->document)->get_n_pages(view->document); + int i; + if (view->children) { + for (i=0; ichildren[i]; + while (tmp_list) { + EvViewChild* child = tmp_list->data; + tmp_list = tmp_list->next; + + ev_view_remove(GTK_CONTAINER(view), child->widget); + } + } + + g_free (view->children); + view->children = NULL; + } + +} + void ev_view_set_document (EvView *view, EvDocument *document) @@ -2862,7 +3351,8 @@ ev_view_set_document (EvView *view, if (document != view->document) { clear_caches (view); - + clear_fields_list (view); + if (view->document) { g_signal_handlers_disconnect_by_func (view->document, find_changed_cb, @@ -2870,6 +3360,8 @@ ev_view_set_document (EvView *view, g_object_unref (view->document); view->page_cache = NULL; + + } view->document = document; @@ -2885,6 +3377,7 @@ ev_view_set_document (EvView *view, } setup_caches (view); + setup_fields_list (view); } gtk_widget_queue_resize (GTK_WIDGET (view)); @@ -3385,7 +3878,7 @@ ev_view_set_zoom_for_size (EvView *view, if (view->document == NULL) return; - + if (view->presentation) ev_view_zoom_for_size_presentation (view, width, height); else if (view->continuous && view->dual_page) @@ -4218,3 +4711,215 @@ ev_scroll_type_get_type (void) } return etype; } + +/*** Container ***/ +static void +ev_view_map (GtkWidget *widget) +{ + EvView *view = EV_VIEW(widget); + gint page_count; + + GTK_WIDGET_SET_FLAGS(widget, GTK_MAPPED); + + if (view->children) { + for (page_count = view->start_page; page_count <= view->end_page; page_count++) { + GList *tmp_list; + tmp_list = view->children[page_count]; + while (tmp_list) { + EvViewChild *child = tmp_list->data; + tmp_list = tmp_list->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) { + if (!GTK_WIDGET_MAPPED (child->widget)) { + gtk_widget_map(child->widget); + } + } + + } + } + } + + gdk_window_show(GTK_WIDGET(view)->window); +} + +static void +ev_view_add (GtkContainer *cont, GtkWidget *widget) +{ + printf("WARNING: use ev_view_put instead of ev_view_add\n"); +} + +static void +ev_view_remove (GtkContainer *cont, GtkWidget *widget) +{ + EvView *view = EV_VIEW(cont); + EvViewChild *child = NULL; + int i = 0; + + + for (i = view->start_page; i <= view->end_page; i++) { + GList *tmp_list; + tmp_list = view->children[i]; + while (tmp_list) { + child = tmp_list->data; + if(child->widget == widget) + break; + tmp_list = tmp_list->next; + } + if (tmp_list) { + gtk_widget_unparent (widget); + view->children[i] = g_list_remove_link(view->children[i], tmp_list); + g_list_free_1(tmp_list); + g_free(child); + return; + } + + } + +} + +static void +ev_view_put (EvView *view, GtkWidget *widget, gint x, gint y, double x1, double y1, double x2, double y2, gint page) +{ + EvViewChild *child; + + child = g_new(EvViewChild, 1); + child->widget = widget; + child->x = x; + child->y = y; + child->p[0].x = x1; + child->p[0].y = y1; + child->p[1].x = x2; + child->p[1].y = y2; + child->page = page; + + view->children[page] = g_list_append(view->children[page], child); + + if (GTK_WIDGET_REALIZED(view)) + gtk_widget_set_parent_window (widget, GTK_WIDGET(view)->window); + gtk_widget_set_parent(widget, GTK_WIDGET(view)); +} + +static void +ev_view_forall (GtkContainer *cont, gboolean include_internals, GtkCallback callback, gpointer callback_data) +{ + EvView *view = EV_VIEW(cont); + EvViewChild *child; + gint i; + + if (!view->children) + return; + + for (i = view->start_page; i <= view->end_page; i++) { + GList *tmp_list; + tmp_list = view->children[i]; + while (tmp_list) { + child = tmp_list->data; + tmp_list = tmp_list->next; + + (*callback)(child->widget, callback_data); + } + } +} + +static EvViewChild* +get_child (EvView *view, GtkWidget *widget) +{ + gint i; + + for (i = view->start_page; i <= view->end_page; i++) { + + GList *tmp_list = view->children[i]; + while (tmp_list) { + EvViewChild *child; + child = tmp_list->data; + tmp_list = tmp_list->next; + if (child->widget == widget) + return child; + } + } + return NULL; +} + +static void +ev_view_move_internal (EvView *view, GtkWidget *widget, gboolean change_x, gint x, gboolean change_y, gint y) +{ + EvViewChild *child; + child = get_child(view, widget); + + g_assert(child); + + gtk_widget_freeze_child_notify(widget); + + if(change_x) { + child->x = x; + gtk_widget_child_notify(widget, "x"); + } + if(change_y) { + child->y = y; + gtk_widget_child_notify(widget, "y"); + } + gtk_widget_thaw_child_notify(widget); + + if (GTK_WIDGET_VISIBLE(widget) && GTK_WIDGET_VISIBLE(view)) + gtk_widget_queue_resize (GTK_WIDGET(widget)); +} + +static void +ev_view_set_child_property (GtkContainer *cont, + GtkWidget *child, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case CHILD_PROP_X: + ev_view_move_internal(EV_VIEW(cont), child, TRUE, g_value_get_int(value), FALSE, 0); + break; + case CHILD_PROP_Y: + ev_view_move_internal(EV_VIEW(cont), child, FALSE, 0, TRUE, g_value_get_int(value)); + break; + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(cont, property_id, pspec); + break; + } +} + +static void +ev_view_get_child_property (GtkContainer *cont, + GtkWidget *child, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EvViewChild *view_child; + view_child = get_child(EV_VIEW(cont), child); + + switch (property_id) { + case CHILD_PROP_X: + g_value_set_int (value, view_child->x); + break; + case CHILD_PROP_Y: + g_value_set_int (value, view_child->y); + break; + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(cont, property_id, pspec); + break; + } +} + +static void +ev_view_allocate_child (EvView *view, EvViewChild *child) +{ + GtkAllocation allocation; + GtkRequisition requisition; + allocation.x = child->x; + allocation.y = child->y; + gtk_widget_get_child_requisition (child->widget, &requisition); + allocation.width = requisition.width; + allocation.height = requisition.height; + + gtk_widget_size_allocate (child->widget, &allocation); +} + + + diff --git a/shell/ev-view.h b/shell/ev-view.h index e6dd395..d45b43e 100644 --- a/shell/ev-view.h +++ b/shell/ev-view.h @@ -21,6 +21,7 @@ #define __EV_VIEW_H__ #include +#include #include "ev-document.h" #include "ev-link.h"