Code

tpdfview: Added simple presentation mode.
[tpdfview.git] / src / tpdfview.c
index 79532672c13154c95ee124869e017bbe64d68fa1..4d0cec8d09f56ded20459f926c7fd58821725195 100644 (file)
@@ -36,6 +36,9 @@
 #include "tpdfv.h"
 #include "tpdfv_features.h"
 
+#include "gtk-tpdfv.h"
+#include "tpdfv-ctl.h"
+
 #if HAVE_LIBGEN_H
 #      include <libgen.h>
 #else /* HAVE_LIBGEN_H */
@@ -44,8 +47,6 @@
 
 #include <assert.h>
 
-#include <errno.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 
 
 #include <unistd.h>
 
-#include <cairo.h>
-
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
-#include <poppler.h>
-
-typedef struct {
-       char *filename;
-
-       PopplerDocument *doc;
-       PopplerPage     *current_page;
-
-       int current_page_no;
-       int total_pages;
-
-       double zoom_factor;
-
-       double delta_x;
-       double delta_y;
-} tpdfv_t;
-
-static tpdfv_t *
-tpdfv_new(const char *filename)
-{
-       GError  *err = NULL;
-       tpdfv_t *pdf;
-
-       /* XXX: error reporting mechanism */
-
-       pdf = (tpdfv_t *)malloc(sizeof(*pdf));
-       if (! pdf) {
-               fprintf(stderr, "Failed to allocate PDF object: %s.\n",
-                       strerror(errno));
-               return NULL;
-       }
-
-       if (strstr(filename, "://"))
-               pdf->filename = strdup(filename);
-       else {
-               size_t len = strlen("file://") + strlen(filename);
-               pdf->filename = (char *)malloc(len + 1);
-               if (pdf->filename) {
-                       *pdf->filename = '\0';
-                       strncat(pdf->filename, "file://", len);
-                       strncat(pdf->filename, filename, len - strlen("file://"));
-               }
-       }
-
-       if (! pdf->filename) {
-               fprintf(stderr, "Failed to allocate string: %s.\n",
-                               strerror(errno));
-               return NULL;
-       }
-
-       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 NULL;
-       }
-
-       pdf->current_page_no = 0;
-       pdf->current_page = poppler_document_get_page(pdf->doc,
-                       pdf->current_page_no);
-       if (! pdf->current_page) {
-               fprintf(stderr, "Failed to open first page of the document.\n");
-               return NULL;
-       }
 
-       pdf->total_pages = poppler_document_get_n_pages(pdf->doc);
-
-       pdf->zoom_factor = 1.0;
-       pdf->delta_x = pdf->delta_y = 0.0;
-       return pdf;
-} /* tpdfv_new */
+/*
+ * Global variables.
+ */
+static gtk_tpdfv_screens_t *screens = NULL;
 
 static void
 exit_usage(char *name, int status)
@@ -162,16 +95,20 @@ exit_version(void)
 } /* exit_version */
 
 static void
-win_redraw(GtkWidget *widget)
+toggle_fullscreen(GtkWindow *win)
 {
-       GdkRegion *region;
+       GdkWindowState state;
 
-       region = gdk_drawable_get_clip_region(widget->window);
-       gdk_window_invalidate_region(widget->window, region, TRUE);
-       gdk_window_process_updates(widget->window, TRUE);
+       if (! gtk_widget_get_realized(GTK_WIDGET(win)))
+               return;
 
-       gdk_region_destroy(region);
-} /* win_redraw */
+       state = gdk_window_get_state(GTK_WIDGET(win)->window);
+
+       if (state & GDK_WINDOW_STATE_FULLSCREEN)
+               gtk_window_unfullscreen(win);
+       else
+               gtk_window_fullscreen(win);
+} /* toggle_fullscreen */
 
 static void
 on_destroy(GtkWidget __attribute__((unused)) *widget,
@@ -181,128 +118,120 @@ on_destroy(GtkWidget __attribute__((unused)) *widget,
 } /* on_destroy */
 
 static gboolean
-on_expose(GtkWidget *widget, GdkEventExpose __attribute__((unused)) *event,
-               gpointer data)
-{
-       cairo_t *cr;
-
-       tpdfv_t *pdf = (tpdfv_t *)data;
-       assert(data);
-
-       cr = gdk_cairo_create(widget->window);
-
-       /* zoom */
-       cairo_scale(cr, pdf->zoom_factor, pdf->zoom_factor);
-       cairo_translate(cr, pdf->delta_x, pdf->delta_y);
-
-       poppler_page_render(pdf->current_page, cr);
-       cairo_destroy(cr);
-       return FALSE;
-} /* on_expose */
-
-static gboolean
-key_press(GtkWidget __attribute__((unused)) *widget,
-               GdkEventKey *event, gpointer data)
+key_press(GtkWidget *window, GdkEventKey *event, gpointer data)
 {
-       tpdfv_t *pdf = (tpdfv_t *)data;
-
-       int old_page_no;
-       _Bool do_redraw = 0;
-
-       assert(data);
+       GObject *obj;
 
-       old_page_no = pdf->current_page_no;
+       obj = G_OBJECT(data);
+       assert(obj);
 
        switch (event->keyval) {
                case GDK_q:
                        gtk_main_quit();
                        break;
 
+               case GDK_r:
+                       tpdfv_ctl_reload(obj);
+                       break;
+
+               case GDK_F:
+                       toggle_fullscreen(GTK_WINDOW(window));
+                       break;
+
                /* navigation */
                case GDK_Page_Up:
-                       if (pdf->current_page_no) {
-                               --pdf->current_page_no;
-                       }
+                       tpdfv_ctl_page_up(obj);
                        break;
                case GDK_Page_Down:
                        /* fall through */
                case GDK_space:
-                       if (pdf->current_page_no < pdf->total_pages - 1) {
-                               ++pdf->current_page_no;
-                       }
+                       tpdfv_ctl_page_down(obj);
                        break;
                case GDK_Home:
-                       pdf->current_page_no = 0;
+                       tpdfv_ctl_first_page(obj);
                        break;
                case GDK_End:
-                       pdf->current_page_no = pdf->total_pages - 1;
+                       tpdfv_ctl_last_page(obj);
                        break;
 
                /* zoom */
                case GDK_plus:
-                       pdf->zoom_factor *= 1.2;
-                       do_redraw = 1;
+                       tpdfv_ctl_zoom_in(obj);
                        break;
                case GDK_minus:
-                       if (pdf->zoom_factor > DBL_EPSILON * 2.0) {
-                               pdf->zoom_factor *= 1.0 / 1.2;
-                               do_redraw = 1;
-                       }
+                       tpdfv_ctl_zoom_out(obj);
                        break;
                case GDK_1:
-                       pdf->zoom_factor = 1.0;
-                       do_redraw = 1;
+                       tpdfv_ctl_zoom_1(obj);
+                       break;
+               case GDK_w:
+                       tpdfv_ctl_zoom_width(obj);
+                       break;
+               case GDK_h:
+                       tpdfv_ctl_zoom_height(obj);
+                       break;
+               case GDK_z:
+                       tpdfv_ctl_zoom_fit(obj);
                        break;
 
                /* scrolling */
                case GDK_Up:
-                       pdf->delta_y += 10.0;
-                       do_redraw = 1;
+                       tpdfv_ctl_scroll_up(obj);
                        break;
                case GDK_Down:
-                       pdf->delta_y -= 10.0;
-                       do_redraw = 1;
+                       tpdfv_ctl_scroll_down(obj);
                        break;
                case GDK_Left:
-                       pdf->delta_x += 10.0;
-                       do_redraw = 1;
+                       tpdfv_ctl_scroll_left(obj);
                        break;
                case GDK_Right:
-                       pdf->delta_x -= 10.0;
-                       do_redraw = 1;
+                       tpdfv_ctl_scroll_right(obj);
                        break;
-       }
 
-       if (old_page_no != pdf->current_page_no) {
-               pdf->current_page = poppler_document_get_page(pdf->doc,
-                               pdf->current_page_no);
-               if (! pdf->current_page)
-                       fprintf(stderr, "Failed to open page %i of the document.\n",
-                                       pdf->current_page_no + 1);
-               else
-                       do_redraw = 1;
-       }
+               /* screen management */
+               case GDK_M:
+                       {
+                               gint n_screens = gtk_tpdfv_screens_number(screens);
+                               gint screen = gtk_tpdfv_screens_window_get(screens,
+                                               GTK_WINDOW(window));
 
-       if (do_redraw)
-               win_redraw(widget);
+                               gtk_tpdfv_screens_window_set(screens,
+                                               GTK_WINDOW(window), (screen + 1) % n_screens);
+                       }
+                       break;
+       }
        return FALSE;
 } /* key_press */
 
 int
 main(int argc, char **argv)
 {
-       GtkWidget *win = NULL;
-       tpdfv_t   *pdf = NULL;
+       GtkWidget *win;
+       GtkWidget *tpdfv;
+
+       GObject *ctl;
+
+       char win_title[1024];
+
+       _Bool presentation_mode = 0;
+
+       GdkColor bg_color;
+
+       char *filename;
 
        gtk_init(&argc, &argv);
 
        while (42) {
-               int opt = getopt(argc, argv, "hV");
+               int opt = getopt(argc, argv, "PhV");
 
                if (-1 == opt)
                        break;
 
                switch (opt) {
+                       case 'P':
+                               presentation_mode = 1;
+                               break;
+
                        case 'h':
                                exit_usage(argv[0], 0);
                                break;
@@ -319,21 +248,99 @@ main(int argc, char **argv)
                exit_usage(argv[0], 1);
        }
 
-       pdf = tpdfv_new(/* filename = */ argv[optind]);
-       if (! pdf)
+       filename = argv[optind];
+
+       tpdfv = gtk_tpdfv_new(filename);
+       if (! tpdfv)
                return 1;
 
        win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+       if (! win)
+               return 1;
+
+       screens = gtk_tpdfv_screens_init(/* display = */ NULL);
+       if (! screens)
+               return 1;
+
+       snprintf(win_title, sizeof(win_title), "tpdfview: %s",
+                       basename(filename));
+       gtk_window_set_title(GTK_WINDOW(win), win_title);
+
+       gtk_container_add(GTK_CONTAINER(win), tpdfv);
+
+       ctl = tpdfv_ctl_new(GTK_TPDFV(tpdfv));
+       if (! ctl)
+               return 1;
+
+       /* TODO: use resource file */
+       gdk_color_parse("#000000", &bg_color);
+
+       if (presentation_mode) {
+               GtkWidget *pres_win;
+               GtkWidget *paned;
+               GtkWidget *tpdfv1, *tpdfv2;
+
+               tpdfv1 = gtk_tpdfv_new(filename);
+               if (! tpdfv1)
+                       return 1;
+
+               if (! tpdfv_ctl_add_slave(ctl, GTK_TPDFV(tpdfv1),
+                                       /* factor = */ 1, /* offset = */ 0))
+                       return 1;
+
+               tpdfv2 = gtk_tpdfv_new(filename);
+               if (! tpdfv2)
+                       return 1;
+
+               if (! tpdfv_ctl_add_slave(ctl, GTK_TPDFV(tpdfv2),
+                                       /* factor = */ 1, /* offset = */ 1))
+                       return 1;
+
+               paned = gtk_hpaned_new();
+               if (! paned)
+                       return 1;
+
+               gtk_paned_pack1(GTK_PANED(paned), tpdfv1, TRUE, TRUE);
+               gtk_paned_pack2(GTK_PANED(paned), tpdfv2, TRUE, TRUE);
+
+               gtk_tpdfv_zoom_fit(tpdfv1);
+               gtk_tpdfv_zoom_fit(tpdfv2);
+
+               pres_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+               if (! pres_win)
+                       return 1;
+
+               gtk_window_set_title(GTK_WINDOW(pres_win), win_title);
+
+               gtk_container_add(GTK_CONTAINER(pres_win), paned);
+
+               gtk_widget_modify_bg(tpdfv1, GTK_STATE_NORMAL, &bg_color);
+               gtk_widget_modify_bg(tpdfv2, GTK_STATE_NORMAL, &bg_color);
+
+               g_signal_connect(G_OBJECT(pres_win), "destroy",
+                               G_CALLBACK(on_destroy), NULL);
+               g_signal_connect(G_OBJECT(pres_win), "key-press-event",
+                               G_CALLBACK(key_press), ctl);
+
+               gtk_widget_modify_bg(pres_win, GTK_STATE_NORMAL, &bg_color);
+               gtk_widget_modify_bg(tpdfv1, GTK_STATE_NORMAL, &bg_color);
+               gtk_widget_modify_bg(tpdfv2, GTK_STATE_NORMAL, &bg_color);
+
+               gtk_widget_show_all(pres_win);
+       }
+
        g_signal_connect(G_OBJECT(win), "destroy",
                        G_CALLBACK(on_destroy), NULL);
-       g_signal_connect(G_OBJECT(win), "expose-event",
-                       G_CALLBACK(on_expose), pdf);
        g_signal_connect(G_OBJECT(win), "key-press-event",
-                       G_CALLBACK(key_press), pdf);
-       gtk_widget_set_app_paintable(win, TRUE);
+                       G_CALLBACK(key_press), ctl);
+
+       gtk_widget_modify_bg(tpdfv, GTK_STATE_NORMAL, &bg_color);
+
        gtk_widget_show_all(win);
 
        gtk_main();
+
+       gtk_tpdfv_screens_destroy(screens);
        return 0;
 } /* main */