Code

gtk-tpdfv: Added _get_n_pages() and _get_current_page().
[tpdfview.git] / src / gtk-tpdfv.c
index b04d2c6f9cb3ffb177e039dc45f10fa268b3f84d..3ebbfab7ba965c74341d7867900eeaa3b5439d5a 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include <sys/stat.h>
+
+#define TPDFV_MIN(a, b) ((a) <= (b) ? (a) : (b))
+#define TPDFV_MAX(a, b) ((a) >= (b) ? (a) : (b))
+
+typedef enum {
+       TPDFV_ZOOM_CUSTOM = 0,
+       TPDFV_ZOOM_WIDTH,
+       TPDFV_ZOOM_HEIGHT,
+       TPDFV_ZOOM_FIT,
+} tpdfv_zoommode_t;
+
 typedef struct {
        char *filename;
 
+       time_t mtime;
+
        PopplerDocument *doc;
        PopplerPage     *current_page;
 
        int current_page_no;
        int total_pages;
 
+       tpdfv_zoommode_t zoom_mode;
        double zoom_factor;
 
        double delta_x;
@@ -68,11 +83,52 @@ G_DEFINE_TYPE(GtkTPDFV, gtk_tpdfv, GTK_TYPE_DRAWING_AREA);
  * Private helper functions.
  */
 
-static gboolean
-tpdfv_init(gtk_tpdfv_t *pdf, const char *filename)
+static void
+tpdfv_open(gtk_tpdfv_t *pdf)
 {
        GError *err = NULL;
 
+       struct stat statbuf;
+
+       if ((! pdf) || (! pdf->filename))
+               return;
+
+       memset(&statbuf, 0, sizeof(statbuf));
+       if (stat(pdf->filename + strlen("file://"), &statbuf) != 0) {
+               char errbuf[1024];
+               strerror_r(errno, errbuf, sizeof(errbuf));
+               fprintf(stderr, "Failed to access PDF: %s.\n", errbuf);
+               return;
+       }
+
+       pdf->mtime = statbuf.st_mtime;
+
+       pdf->doc = poppler_document_new_from_file(pdf->filename,
+                       /* password = */ NULL, &err);
+       if (! pdf->doc) {
+               fprintf(stderr, "Failed to open PDF: %s.\n", err->message);
+               return;
+       }
+
+       pdf->total_pages = poppler_document_get_n_pages(pdf->doc);
+       if (pdf->current_page_no >= pdf->total_pages)
+               pdf->current_page_no = pdf->total_pages - 1;
+} /* tpdfv_open */
+
+static void
+tpdfv_close(gtk_tpdfv_t *pdf)
+{
+       if (! pdf)
+               return;
+
+       g_object_unref(pdf->doc);
+       pdf->doc = NULL;
+       pdf->current_page = NULL;
+} /* tpdfv_close */
+
+static gboolean
+tpdfv_init(gtk_tpdfv_t *pdf, const char *filename)
+{
        if (strstr(filename, "://"))
                pdf->filename = strdup(filename);
        else {
@@ -94,12 +150,8 @@ tpdfv_init(gtk_tpdfv_t *pdf, const char *filename)
                return FALSE;
        }
 
-       pdf->doc = poppler_document_new_from_file(pdf->filename,
-                       /* password = */ NULL, &err);
-       if (! pdf->doc) {
-               fprintf(stderr, "Failed to open PDF: %s.\n", err->message);
-               return FALSE;
-       }
+       pdf->doc = NULL;
+       tpdfv_open(pdf);
 
        pdf->current_page_no = 0;
        pdf->current_page = poppler_document_get_page(pdf->doc,
@@ -109,8 +161,7 @@ tpdfv_init(gtk_tpdfv_t *pdf, const char *filename)
                return FALSE;
        }
 
-       pdf->total_pages = poppler_document_get_n_pages(pdf->doc);
-
+       pdf->zoom_mode   = TPDFV_ZOOM_CUSTOM;
        pdf->zoom_factor = 1.0;
        pdf->delta_x = pdf->delta_y = 0.0;
        return TRUE;
@@ -122,8 +173,7 @@ tpdfv_clean(gtk_tpdfv_t *pdf)
        if (! pdf)
                return;
 
-       g_object_unref(pdf->doc);
-       pdf->doc          = NULL;
+       tpdfv_close(pdf);
        pdf->current_page = NULL;
        free(pdf->filename);
        pdf->filename = NULL;
@@ -138,8 +188,8 @@ do_redraw(GtkWidget *widget)
 
        pdf = GTK_TPDFV_GET_PRIVATE(widget);
 
-       if (poppler_page_get_index(pdf->current_page)
-                       != pdf->current_page_no) {
+       if ((! pdf->current_page) || (poppler_page_get_index(pdf->current_page)
+                       != pdf->current_page_no)) {
                pdf->current_page = poppler_document_get_page(pdf->doc,
                pdf->current_page_no);
 
@@ -167,19 +217,76 @@ gtk_tpdfv_expose(GtkWidget *tpdfv, GdkEventExpose *event)
        gtk_tpdfv_t *pdf;
        cairo_t *cr;
 
+       struct stat statbuf;
+
+       gdouble width, height;
+
+       gdouble page_width  = 0.0;
+       gdouble page_height = 0.0;
+
+       double zoom_factor  = 1.0;
+       double x, y;
+
        pdf = GTK_TPDFV_GET_PRIVATE(tpdfv);
 
        if (! pdf)
                return FALSE;
 
+       memset(&statbuf, 0, sizeof(statbuf));
+       if (stat(pdf->filename + strlen("file://"), &statbuf)) {
+               char errbuf[1024];
+               strerror_r(errno, errbuf, sizeof(errbuf));
+               fprintf(stderr, "Failed to access PDF: %s.\n", errbuf);
+               return FALSE;
+       }
+
+       if (statbuf.st_mtime > pdf->mtime)
+               gtk_tpdfv_reload(tpdfv);
+
        cr = gdk_cairo_create(tpdfv->window);
        cairo_rectangle(cr, event->area.x, event->area.y,
                        event->area.width, event->area.height);
        cairo_clip(cr);
 
-       /* zoom, scrolling */
-       cairo_scale(cr, pdf->zoom_factor, pdf->zoom_factor);
-       cairo_translate(cr, pdf->delta_x, pdf->delta_y);
+       poppler_page_get_size(pdf->current_page, &page_width, &page_height);
+       width  = (gdouble)tpdfv->allocation.width;
+       height = (gdouble)tpdfv->allocation.height;
+
+       /* zoom */
+       if (pdf->zoom_mode == TPDFV_ZOOM_CUSTOM) {
+               zoom_factor = pdf->zoom_factor;
+       }
+       else {
+               if (pdf->zoom_mode == TPDFV_ZOOM_WIDTH) {
+                       zoom_factor = (double)(width / page_width);
+               }
+               else if (pdf->zoom_mode == TPDFV_ZOOM_HEIGHT) {
+                       zoom_factor = (double)(height / page_height);
+               }
+               else if (pdf->zoom_mode == TPDFV_ZOOM_FIT) {
+                       zoom_factor = (double)TPDFV_MIN(width / page_width,
+                                       height / page_height);
+               }
+               else {
+                       assert(0);
+               }
+       }
+       cairo_scale(cr, zoom_factor, zoom_factor);
+
+       /* centered position / scrolling */
+       x = (double)(width - page_width * zoom_factor) / 2.0 / zoom_factor;
+       y = (double)(height - page_height * zoom_factor) / 2.0 / zoom_factor;
+
+       cairo_translate(cr, x + pdf->delta_x, y + pdf->delta_y);
+
+       /* draw empty white page */
+       cairo_save(cr);
+
+       cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
+       cairo_rectangle(cr, 0.0, 0.0, page_width, page_height);
+       cairo_fill(cr);
+
+       cairo_restore(cr);
 
        poppler_page_render(pdf->current_page, cr);
 
@@ -240,6 +347,41 @@ gtk_tpdfv_new(const char *filename)
        return GTK_WIDGET(tpdfv);
 } /* gtk_tpdfv_new */
 
+void
+gtk_tpdfv_reload(GtkWidget *widget)
+{
+       gtk_tpdfv_t *pdf;
+
+       pdf = GTK_TPDFV_GET_PRIVATE(widget);
+       tpdfv_close(pdf);
+       tpdfv_open(pdf);
+       do_redraw(widget);
+} /* gtk_tpdfv_reload */
+
+int
+gtk_tpdfv_get_n_pages(GtkWidget *widget)
+{
+       gtk_tpdfv_t *pdf;
+
+       if (! widget)
+               return -1;
+
+       pdf = GTK_TPDFV_GET_PRIVATE(widget);
+       return pdf->total_pages;
+} /* gtk_tpdfv_get_n_pages */
+
+int
+gtk_tpdfv_get_current_page(GtkWidget *widget)
+{
+       gtk_tpdfv_t *pdf;
+
+       if (! widget)
+               return -1;
+
+       pdf = GTK_TPDFV_GET_PRIVATE(widget);
+       return pdf->current_page_no;
+} /* gtk_tpdfv_get_current_page */
+
 void
 gtk_tpdfv_page_up(GtkWidget *widget)
 {
@@ -291,6 +433,7 @@ gtk_tpdfv_zoom_in(GtkWidget *widget)
 
        pdf = GTK_TPDFV_GET_PRIVATE(widget);
        pdf->zoom_factor *= 1.2;
+       pdf->zoom_mode    = TPDFV_ZOOM_CUSTOM;
        do_redraw(widget);
 } /* gtk_tpdfv_zoom_in */
 
@@ -303,6 +446,7 @@ gtk_tpdfv_zoom_out(GtkWidget *widget)
 
        if (pdf->zoom_factor > DBL_EPSILON * 2.0) {
                pdf->zoom_factor /= 1.2;
+               pdf->zoom_mode    = TPDFV_ZOOM_CUSTOM;
                do_redraw(widget);
        }
 } /* gtk_tpdfv_zoom_out */
@@ -314,9 +458,40 @@ gtk_tpdfv_zoom_1(GtkWidget *widget)
 
        pdf = GTK_TPDFV_GET_PRIVATE(widget);
        pdf->zoom_factor = 1.0;
+       pdf->zoom_mode   = TPDFV_ZOOM_CUSTOM;
        do_redraw(widget);
 } /* gtk_tpdfv_zoom_1 */
 
+void
+gtk_tpdfv_zoom_width(GtkWidget *widget)
+{
+       gtk_tpdfv_t *pdf;
+
+       pdf = GTK_TPDFV_GET_PRIVATE(widget);
+       pdf->zoom_mode = TPDFV_ZOOM_WIDTH;
+       do_redraw(widget);
+} /* gtk_tpdfv_zoom_width */
+
+void
+gtk_tpdfv_zoom_height(GtkWidget *widget)
+{
+       gtk_tpdfv_t *pdf;
+
+       pdf = GTK_TPDFV_GET_PRIVATE(widget);
+       pdf->zoom_mode = TPDFV_ZOOM_HEIGHT;
+       do_redraw(widget);
+} /* gtk_tpdfv_zoom_width */
+
+void
+gtk_tpdfv_zoom_fit(GtkWidget *widget)
+{
+       gtk_tpdfv_t *pdf;
+
+       pdf = GTK_TPDFV_GET_PRIVATE(widget);
+       pdf->zoom_mode = TPDFV_ZOOM_FIT;
+       do_redraw(widget);
+} /* gtk_tpdfv_zoom_width */
+
 void
 gtk_tpdfv_scroll_up(GtkWidget *widget)
 {