root/trunk/src/pdfcube.cc

Revision 73, 58.5 KB (checked in by mirko, 8 weeks ago)

Preparing for 0.0.5 release.

Line 
1// ex: set ts=2: -*- mode: C++; mode: flyspell-prog; mode: flymake; c-basic-offset: 2; indent-tabs-mode: nil -*-
2//
3// PDFCube source file - pdfcube.cc
4//
5// Copyright (C) 2006-2012
6//               Mirko Maischberger <mirko.maischberger@gmail.com>
7// Copyright (C) 2008
8//               Karol Sokolowski   <sokoow@gmail.com>
9//
10// This program is free software; you can redistribute it and/or
11// modify it under the terms of the GNU General Public License as
12// published by the Free Software Foundation; either version 2 of the
13// License, or (at your option) any later version.
14//
15// This program is distributed in the hope that it will be useful, but
16// WITHOUT ANY WAPRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18// General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with this program; if not, write to the Free Software
22// Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA
23// 02110-1301, USA
24//
25// Notes: please indent using 2 spaces.
26
27#define NDEBUG
28
29#include <iostream>
30#include <cstdio>
31#include <cstdlib>
32#include <cmath>
33#include <strstream>
34#include <fstream>
35// Removing GTK+ dependencies to ease portability.
36// Gtk+ (pkg-config gtk+-2.0)
37#include <gtk/gtk.h>
38#include <gdk/gdkkeysyms.h>
39
40// GtkGLExt (pkg-config gtkglext-1.0)
41#include <gtk/gtkgl.h>
42
43// OpenGL
44#include <GL/gl.h>
45#include <GL/glu.h>
46#include <GL/glx.h>
47
48// PDF to cairo_surface_t using Cairo (pkg-config poppler-glib cairo)
49#include <poppler.h>
50#include <cairo.h>
51
52#include <boost/program_options.hpp>
53
54using namespace std;
55namespace po = boost::program_options;
56
57//////////////////////////////////////////////////////////////////////////
58// Macros
59
60#include <cassert>
61
62#define DEFAULT_WIDTH 640
63#define DEFAULT_HEIGHT 480
64#define DEFAULT_TITLE  "PDFCube"
65#define N_FRAMES 30
66#define TIMEOUT_INTERVAL 25
67
68//////////////////////////////////////////////////////////////////////////
69// Globals (will be moved inside classes some day)
70
71enum animation { ANIM_NONE,
72                 CUBE_NEXT, CUBE_PREV,
73                 ZOOM0, ZOOM1, ZOOM2, ZOOM3, ZOOM4, ZOOMC,
74                 SWITCH_FW, SWITCH_BW
75};
76
77static gboolean fullscreen;
78static gboolean animating = FALSE;
79animation active_animation = ANIM_NONE;
80animation previous_animation = ANIM_NONE;
81animation last_animation = ANIM_NONE;
82
83// Cube Transitions on the command line
84// (space advances simply or with the rotating cube)
85// depending on the values in this array
86static bool *page_transition;
87static int *page_duration;
88static guint auto_advance_id = 0;
89static double pdf_aspect_ratio = 4.0/3.0;
90// static double screen_aspect_ratio = 16.0/10.0;
91
92//////////////////////////////////////////////////////////////////////////
93// Forward declarations
94
95static void timeout_add(GtkWidget * widget);
96static void timeout_remove(GtkWidget * widget);
97
98static void start_animation(GtkWidget * widget, animation);
99static void stop_animation(GtkWidget * widget);
100
101static GdkGLConfig *configure_gl(void);
102
103static GtkWidget *create_window(GdkGLConfig * glconfig);
104static GtkWidget *drawing_area;
105
106
107static GLfloat clear_color[4] = { 0.6, 0.0, 0.0, 0.0 };
108static GLfloat top_color[4] = { 0.7, 0.6, 0.6, 0.0 };
109static double animation_emphasis = 2.4;
110
111static bool
112sleeping()
113{
114  return !animating && active_animation == ANIM_NONE;
115}
116
117//////////////////////////////////////////////////////////////////////////
118//
119// pdfcube
120//
121// This is an attemp to move some dirtyness inside a class
122// some work still needs to be done to transform this quick
123// hack in a serious pdf-viewer
124//
125class pdfcube {
126public:
127  pdfcube(PopplerDocument* d)
128    :doc(d),
129     current_page(0),
130     current_face(0),
131     total_pages(poppler_document_get_n_pages(d)),
132     frame(0),
133     lookposx(0.0), lookposy(0.0), lookposz(3.48),
134     atx(0.0), aty(0.0), atz(0.0), persp(44.0), angle(0.0), 
135     context(0), pixmap(0) {
136    texmap[0] = 0;
137    texmap[1] = 1;
138    texmap[2] = 2;
139    cube_faces=0;
140    PopplerPage *page(0);
141    double w,h;
142    page = poppler_document_get_page(d, 0);
143    poppler_page_get_size(page, &w, &h);
144    pdf_aspect_ratio = ((double)w)/h;
145
146    // g_free(page);
147    tex_width = 1024;
148    tex_height = 1024; 
149    pixmap =
150      cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 
151                                 (double)tex_width,
152                                 (double)tex_height);
153    context = cairo_create(pixmap);
154    cairo_scale(context, (((double)tex_width)/w), (((double)tex_height)/h));
155
156    steps = new GLfloat[N_FRAMES];
157    xsteps = new double[N_FRAMES];
158    zsteps = new double[N_FRAMES];
159    zoomsteps = new double[N_FRAMES];
160    perspsteps = new double[N_FRAMES];
161    perspstepsc = new double[N_FRAMES];
162  }
163
164  // Dtor.
165  ~pdfcube() {
166    delete[]steps;
167    delete[]xsteps;
168    delete[]zsteps;
169    delete[]zoomsteps;
170    delete[]perspsteps;
171    delete[]perspstepsc;
172  }
173
174  /// Current page.
175  int page() {
176    return current_page;
177  }
178
179  /// Total pages
180  int pages() {
181    return total_pages;
182  }
183
184  // Cube Normals
185  static GLfloat n[6][3];
186
187  // Cube Faces
188  static GLint faces[6][4];
189
190  // Cube vertex (filled in pdfcube->initialize())
191  GLfloat v[8][3];
192
193  // Cube texture mapping
194  static GLfloat mapping[6][8];
195
196  // Cube Rotation Animation steps (17 frames)
197  // Cube rotation at each frame
198  GLfloat *steps;
199  //  ... x camera movement
200  double *xsteps;
201  //  ... z camera movement
202  double *zsteps;
203
204  // Other animations
205  double *zoomsteps;
206  double *perspsteps;
207  double *perspstepsc;
208
209  // Restart pdf
210  void restart(GtkWidget * widget) {
211    current_page = 0;
212    update_textures(widget);
213  }
214
215  // Jump to page
216  void go_to(GtkWidget * widget, int page) {
217    if (page >= 0 && page < total_pages) {
218      current_page = page;
219      update_textures(widget);
220    }
221  }
222
223  // Jump to section (1..9)
224  void section(GtkWidget * widget, int section) {
225#ifndef NDEBUG
226    cerr << "Section: " << section << " total pages: " <<
227      total_pages << endl;
228#endif
229    int ii;
230    for (ii = 0; ii < total_pages; ++ii) {
231      if (page_transition[ii])
232        section--;
233      if (section == 0)
234        break;
235    }
236#ifndef NDEBUG
237    cerr << "Page: " << ii << endl;
238#endif
239    if (ii < total_pages) {
240      current_page = ii;
241      update_textures(widget);
242    }
243  }
244
245  // OpenGL initialization
246  void
247  initialize(GtkWidget * widget) {
248    GLfloat position[] = { 1.0, 0.0, 0.0, 1.0 };
249    GLfloat local_view[] = { 0.0 };
250
251    glShadeModel(GL_SMOOTH);
252    glEnable(GL_TEXTURE_RECTANGLE_ARB);
253
254    GLfloat mat_ambient[] = { 1.0, 1.0, 1.0, 0.00 };
255    // GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 0.00 };
256    // GLfloat mat_shininess[] = { 0.2 };
257
258    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
259    // glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
260    // glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
261
262    glEnable(GL_LIGHTING);
263    glEnable(GL_LIGHT0);
264    // glEnable(GL_BLEND);
265    glEnable(GL_CULL_FACE);
266    // glEnable(GL_POLYGON_SMOOTH);
267    // glPolygonMode(GL_FRONT, GL_FILL);
268    // glEdgeFlag(GL_FALSE);
269   
270    glClearColor(clear_color[0], clear_color[1], clear_color[2], 
271                 clear_color[3]);
272    glCullFace(GL_FRONT);
273    // glDisable(GL_DEPTH_TEST);
274
275    // glEnable(GL_DEPTH_TEST);
276    // glDepthFunc(GL_LEQUAL);
277    glLightfv(GL_LIGHT0, GL_POSITION, position);
278    glLightfv(GL_LIGHT0, GL_COLOR, mat_ambient);
279    // glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);
280
281    // glFrontFace(GL_CCW);
282    // glEnable(GL_LIGHTING);
283    // glEnable(GL_LIGHT0);
284    glEnable(GL_AUTO_NORMAL);
285    glEnable(GL_NORMALIZE);
286
287    glEnable(GL_COLOR_MATERIAL);
288
289#ifdef ENABLE_FOG
290    glEnable(GL_FOG);
291    {
292      GLfloat fogColor[4] = { 0.6, 0.6, 0.6, 0.5 };
293
294      glFogi(GL_FOG_MODE, GL_LINEAR);
295      glFogf(GL_FOG_START, 2.1f);
296      glFogf(GL_FOG_END, 6.0f);
297      glFogfv(GL_FOG_COLOR, fogColor);
298      glFogf(GL_FOG_DENSITY, 0.25);
299      glHint(GL_FOG_HINT, GL_DONT_CARE);
300      glClearColor(fogColor[0], fogColor[1], 
301                   fogColor[2], fogColor[3]);
302    }
303#endif
304    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
305
306    glGenTextures(3, textures);
307
308    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
309    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
310                    GL_CLAMP_TO_EDGE);
311    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
312                    GL_CLAMP_TO_EDGE);
313
314    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
315                    GL_LINEAR_MIPMAP_LINEAR);
316    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
317                    GL_LINEAR);
318
319    update_textures(widget);
320
321    GLint size;
322    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
323#ifndef NDEBUG
324    printf("Max-texture size: %upx\n", size);
325#endif
326    assert(size >= 512);
327    assert(glIsTexture(textures[0]));
328    // Cube vertex
329    v[0][0] = v[1][0] = v[2][0] = v[3][0] = -1;
330    v[4][0] = v[5][0] = v[6][0] = v[7][0] = 1;
331    v[0][1] = v[1][1] = v[4][1] = v[5][1] = -1;
332    v[2][1] = v[3][1] = v[6][1] = v[7][1] = 1;
333    v[0][2] = v[3][2] = v[4][2] = v[7][2] = 1;
334    v[1][2] = v[2][2] = v[5][2] = v[6][2] = -1;
335
336    glMatrixMode(GL_PROJECTION);
337    gluPerspective(persp, 1.0, 0.5, 10.0);
338
339    glMatrixMode(GL_MODELVIEW);
340 
341    // up is in positive Y direction
342    gluLookAt(lookposx, lookposy, lookposz, atx, aty, atz, 0.0, 1.0, 0.0);       
343
344    matrix_setup();
345    buildLists();
346  }
347
348  // Redraw scene
349  void
350  redraw(GtkWidget * widget) {
351    double yoffset = 0.1;
352    if (animating) {
353      switch (active_animation) {
354      case ANIM_NONE:
355       
356#ifndef NDEBUG
357        cerr << "No animation... stopping right now." <<
358          endl;
359#endif
360        frame = 0;
361        stop_animation(widget);
362        break;
363      case CUBE_NEXT:
364#ifndef NDEBUG
365        cerr << "cube+ " << frame << endl;
366#endif
367        if (frame == N_FRAMES) {
368          frame = 0;
369          stop_animation(widget);
370          quick_reset(widget);
371        } else {
372          glClear(GL_COLOR_BUFFER_BIT |
373                  GL_DEPTH_BUFFER_BIT);
374          glMatrixMode(GL_MODELVIEW);
375          glLoadIdentity();
376          lookposz -= animation_emphasis*zsteps[frame];
377          lookposy = 1.5*animation_emphasis*xsteps[frame];
378          gluLookAt(lookposx, lookposy, lookposz,
379                    atx, aty, atz, 0, 1, 0);
380          angle -= steps[frame];
381          glRotatef(angle, 0.0, 1.0, 0.0);
382          drawCube();
383          frame++;
384        }
385        break;
386      case CUBE_PREV:
387#ifndef NDEBUG
388        cerr << "cube- " << frame << endl;
389#endif
390        if (frame == N_FRAMES) {
391          frame = 0;
392          stop_animation(widget);
393          quick_reset(widget);
394        } else {
395          glClear(GL_COLOR_BUFFER_BIT |
396                  GL_DEPTH_BUFFER_BIT);
397          glMatrixMode(GL_MODELVIEW);
398          glLoadIdentity();
399          lookposz -= 2.0 * zsteps[frame];
400          lookposy = 3.0 * xsteps[frame];
401          gluLookAt(lookposx, lookposy, lookposz,
402                    atx, aty, atz, 0, 1, 0);
403          angle -= steps[frame];
404          glRotatef(angle, 0.0, -1.0, 0.0);
405          drawCube();
406          frame++;
407        }
408        break;
409      case ZOOM0:
410       
411#ifndef NDEBUG
412        cerr << "zoom0 " << frame << endl;
413#endif
414        if (frame == N_FRAMES) {
415          frame = 0;
416          stop_animation(widget);
417          quick_reset(widget);
418        } else {
419          glClear(GL_COLOR_BUFFER_BIT |
420                  GL_DEPTH_BUFFER_BIT);
421          switch (previous_animation) {
422          case ZOOM1:
423            persp =
424              perspsteps[(N_FRAMES - 1) -
425                         frame];
426            atx = lookposx =
427              -(1.3 *
428                zoomsteps[(N_FRAMES - 1) -
429                          frame]);
430            aty = lookposy =
431              zoomsteps[(N_FRAMES - 1) -
432                        frame] -
433              yoffset / N_FRAMES *
434              ((N_FRAMES - 1) - frame);
435            break;
436          case ZOOM2:
437            persp =
438              perspsteps[(N_FRAMES - 1) -
439                         frame];
440            atx = lookposx =
441              1.3 *
442              zoomsteps[(N_FRAMES - 1) -
443                        frame];
444            aty = lookposy =
445              zoomsteps[(N_FRAMES - 1) -
446                        frame] -
447              yoffset / N_FRAMES *
448              ((N_FRAMES - 1) - frame);
449            break;
450          case ZOOM3:
451            persp =
452              perspsteps[(N_FRAMES - 1) -
453                         frame];
454            atx = lookposx =
455              -1.3 *
456              zoomsteps[(N_FRAMES - 1) -
457                        frame];
458            aty = lookposy =
459              -zoomsteps[(N_FRAMES - 1) -
460                         frame] -
461              yoffset / N_FRAMES *
462              ((N_FRAMES - 1) - frame);
463            break;
464          case ZOOM4:
465            persp =
466              perspsteps[(N_FRAMES - 1) -
467                         frame];
468            atx = lookposx =
469              1.3 *
470              zoomsteps[(N_FRAMES - 1) -
471                        frame];
472            aty = lookposy =
473              -zoomsteps[(N_FRAMES - 1) -
474                         frame] -
475              yoffset / N_FRAMES *
476              ((N_FRAMES - 1) - frame);
477            break;
478          case ZOOMC:
479            persp =
480              perspstepsc[(N_FRAMES - 1) -
481                          frame];
482            aty = lookposy =
483              -zoomsteps[(N_FRAMES - 1) -
484                         frame] * 0.38;
485            break;
486          default:
487
488#ifndef NDEBUG
489            cerr << "Should not reach" <<
490              endl;
491#endif
492            break;
493          }
494          glMatrixMode(GL_PROJECTION);
495          glLoadIdentity();
496          gluPerspective(persp, 1.0, 0.5, 10.0);
497          glMatrixMode(GL_MODELVIEW);
498          glLoadIdentity();
499          gluLookAt(lookposx, lookposy, lookposz,
500                    atx, aty, atz, 0, 1, 0);
501          glRotatef(angle, 0.0, 1.0, 0.0);
502          drawCube();
503          frame++;
504        }
505        break;
506      case ZOOM1:
507#ifndef NDEBUG
508        cerr << "zoom1 " << frame << endl;
509#endif
510        if (frame == N_FRAMES) {
511          frame = 0;
512          stop_animation(widget);
513        } else {
514          glClear(GL_COLOR_BUFFER_BIT |
515                  GL_DEPTH_BUFFER_BIT);
516          glMatrixMode(GL_PROJECTION);
517          glLoadIdentity();
518          persp = perspsteps[frame];
519          gluPerspective(persp, 1.0, 0.5, 10.0);
520          glMatrixMode(GL_MODELVIEW);
521          glLoadIdentity();
522          atx = lookposx =
523            -1.3 * zoomsteps[frame];
524          aty = lookposy =
525            zoomsteps[frame] -
526            yoffset / N_FRAMES * (frame);
527          gluLookAt(lookposx, lookposy, lookposz,
528                    atx, aty, atz, 0, 1, 0);
529          glRotatef(angle, 0.0, 1.0, 0.0);
530          drawCube();
531          frame++;
532        }
533        break;
534      case ZOOM2:
535#ifndef NDEBUG
536        cerr << "zoom1 " << frame << endl;
537#endif
538        if (frame == N_FRAMES) {
539          frame = 0;
540          stop_animation(widget);
541        } else {
542          glClear(GL_COLOR_BUFFER_BIT |
543                  GL_DEPTH_BUFFER_BIT);
544          glMatrixMode(GL_PROJECTION);
545          glLoadIdentity();
546          persp = perspsteps[frame];
547          gluPerspective(persp, 1.0, 0.5, 10.0);
548          glMatrixMode(GL_MODELVIEW);
549          glLoadIdentity();
550          atx = lookposx = 1.3 * zoomsteps[frame];
551          aty = lookposy =
552            zoomsteps[frame] -
553            yoffset / N_FRAMES * (frame);
554          gluLookAt(lookposx, lookposy, lookposz,
555                    atx, aty, atz, 0, 1, 0);
556          glRotatef(angle, 0.0, 1.0, 0.0);
557          drawCube();
558          frame++;
559        }
560        break;
561      case ZOOM3:
562#ifndef NDEBUG
563        cerr << "zoom1 " << frame << endl;
564#endif
565        if (frame == N_FRAMES) {
566          frame = 0;
567          stop_animation(widget);
568        } else {
569          glClear(GL_COLOR_BUFFER_BIT |
570                  GL_DEPTH_BUFFER_BIT);
571          glMatrixMode(GL_PROJECTION);
572          glLoadIdentity();
573          persp = perspsteps[frame];
574          gluPerspective(persp, 1.0, 0.5, 10.0);
575          glMatrixMode(GL_MODELVIEW);
576          glLoadIdentity();
577          atx = lookposx =
578            -1.3 * zoomsteps[frame];
579          aty = lookposy =
580            -zoomsteps[frame] -
581            yoffset / N_FRAMES * (frame);
582          gluLookAt(lookposx, lookposy, lookposz,
583                    atx, aty, atz, 0, 1, 0);
584          glRotatef(angle, 0.0, 1.0, 0.0);
585          drawCube();
586          frame++;
587        }
588        break;
589      case ZOOM4:
590#ifndef NDEBUG
591        cerr << "zoom1 " << frame << endl;
592#endif
593        if (frame == N_FRAMES) {
594          frame = 0;
595          stop_animation(widget);
596        } else {
597          glClear(GL_COLOR_BUFFER_BIT |
598                  GL_DEPTH_BUFFER_BIT);
599          glMatrixMode(GL_PROJECTION);
600          glLoadIdentity();
601          persp = perspsteps[frame];
602          gluPerspective(persp, 1.0, 0.5, 10.0);
603          glMatrixMode(GL_MODELVIEW);
604          glLoadIdentity();
605          atx = lookposx = 1.3 * zoomsteps[frame];
606          aty = lookposy =
607            -zoomsteps[frame] -
608            yoffset / N_FRAMES * (frame);
609          gluLookAt(lookposx, lookposy, lookposz,
610                    atx, aty, atz, 0, 1, 0);
611          glRotatef(angle, 0.0, 1.0, 0.0);
612          drawCube();
613          frame++;
614        }
615        break;
616      case ZOOMC:
617#ifndef NDEBUG
618        cerr << "zoomc " << frame << endl;
619#endif
620        if (frame == N_FRAMES) {
621          frame = 0;
622          stop_animation(widget);
623        } else {
624          glClear(GL_COLOR_BUFFER_BIT |
625                  GL_DEPTH_BUFFER_BIT);
626          persp = perspstepsc[frame];
627          aty = lookposy =
628            -zoomsteps[frame] * 0.38;
629          glMatrixMode(GL_PROJECTION);
630          glLoadIdentity();
631          gluPerspective(persp, 1.0, 0.5, 10.0);
632          glMatrixMode(GL_MODELVIEW);
633          glLoadIdentity();
634          gluLookAt(lookposx, lookposy, lookposz,
635                    atx, aty, atz, 0, 1, 0);
636          glRotatef(angle, 0.0, 1.0, 0.0);
637          drawCube();
638          frame++;
639        }
640        break;
641      case SWITCH_FW:
642#ifndef NDEBUG
643        cerr << "fw " << frame << endl;
644#endif
645        if (frame == 1) {
646          frame = 0;
647          stop_animation(widget);
648        } else {
649          glClear(GL_COLOR_BUFFER_BIT |
650                  GL_DEPTH_BUFFER_BIT);
651          glMatrixMode(GL_MODELVIEW);
652          glLoadIdentity();
653          gluLookAt(lookposx, lookposy, lookposz,
654                    atx, aty, atz, 0, 1, 0);
655          angle -= 90;
656          glRotatef(angle, 0.0, 1.0, 0.0);
657          drawCube();
658          frame++;
659        }
660        break;
661      case SWITCH_BW:
662#ifndef NDEBUG
663        cerr << "bw " << frame << endl;
664#endif
665        if (frame == 1) {
666          frame = 0;
667          stop_animation(widget);
668        } else {
669          glClear(GL_COLOR_BUFFER_BIT |
670                  GL_DEPTH_BUFFER_BIT);
671          glMatrixMode(GL_MODELVIEW);
672          glLoadIdentity();
673          gluLookAt(lookposx, lookposy, lookposz,
674                    atx, aty, atz, 0, 1, 0);
675          angle += 90;
676          glRotatef(angle, 0.0, 1.0, 0.0);
677          drawCube();
678          frame++;
679        }
680        break;
681      }
682    } else {
683      switch (active_animation) {
684      case ANIM_NONE:
685#ifndef NDEBUG
686        cerr << "Redrawing" << endl;
687#endif
688        break;
689      case CUBE_NEXT:
690#ifndef NDEBUG
691        cerr << "cube stop" << endl;
692#endif
693        forward(widget);
694        current_face = next_face();
695        quick_reset(widget);
696        break;
697      case CUBE_PREV:
698#ifndef NDEBUG
699        cerr << "cube stop" << endl;
700#endif
701        backward(widget);
702        current_face = prev_face();
703        quick_reset(widget);
704        break;
705      case SWITCH_FW:
706#ifndef NDEBUG
707        cerr << "fw stop" << endl;
708#endif
709        forward(widget);
710        current_face = next_face();
711        quick_reset(widget);
712        break;
713      case SWITCH_BW:
714#ifndef NDEBUG
715        cerr << "bw stop" << endl;
716#endif
717        backward(widget);
718        current_face = prev_face();
719        quick_reset(widget);
720        break;
721      case ZOOM0:
722      case ZOOM1:
723      case ZOOM2:
724      case ZOOM3:
725      case ZOOM4:
726      case ZOOMC:
727      default:
728#ifndef NDEBUG
729        cerr << "default stop" << endl;
730#endif
731        break;
732
733      }
734
735      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
736      glMatrixMode(GL_PROJECTION);
737      glLoadIdentity();
738      gluPerspective(persp, 1.0, 0.5, 10.0);
739      glMatrixMode(GL_MODELVIEW);
740      glLoadIdentity();
741      gluLookAt(lookposx, lookposy, lookposz, atx, aty, atz,
742                0, 1, 0);
743      glRotatef(angle, 0.0, 1.0, 0.0);
744      drawCube();
745
746      glRasterPos3f(0, -1.4, 0);
747      GLuint rcube[] = {
748        0, 0, 0, 127
749      };
750      glDrawPixels(1, 1, GL_BGRA, GL_UNSIGNED_BYTE, rcube);
751
752      active_animation = ANIM_NONE;
753
754#ifndef NDEBUG
755      cerr << "Ok!" << endl;
756#endif
757      glFlush();
758    }
759  }
760
761  // Prev face of the cube in [0..3]
762  int prev_face() {
763    return (current_face+3) % 4;
764  }
765
766  // Next face of the cube in [0..3]
767  int next_face() {
768    return (current_face+1) % 4;
769  }
770
771  // Prev document page
772  int prev_page() {
773    assert(current_page >= 0);
774    if (current_page == 0)
775      return total_pages - 1;
776    else
777      return current_page - 1;
778  }
779
780  // Next document page
781  int next_page() {
782    assert(current_page < total_pages);
783    if (current_page == total_pages - 1)
784      return 0;
785    else
786      return current_page + 1;
787  }
788
789  void
790  forward(GtkWidget * widget) {
791    update_textures_dir(widget, true);
792#ifndef NDEBUG
793    cerr << "Current page: " << current_page << endl;
794#endif
795  }
796
797  // Initialization of animation vectors
798  void matrix_setup() {
799    double sum = 0.;
800    for(int i=0; i<N_FRAMES; i++)
801      {
802        steps[i] =std::pow(::sin(i*M_PI/double(N_FRAMES)), 2);
803        sum += steps[i];
804      } 
805    double factor = 90./sum;
806    for(int i=0; i<N_FRAMES; i++)
807      {
808        steps[i] *= factor;
809      }
810#ifndef NDEBUG
811    cout << "Matrix ";
812    for (int i = 0; i < N_FRAMES; i++)
813      cout << steps[i] << " ";
814    cout << endl;
815#endif
816
817    float xstep_ratio = 0.4;
818    float xstep =
819      double (xstep_ratio) / (double(N_FRAMES-1) / 2.0);
820    for (int i = 0; i < N_FRAMES / 2; i++) {
821      xsteps[i] = i * xstep;
822      if (xsteps[i] > xstep_ratio)
823        xsteps[i] = xstep_ratio;
824    }
825    for (int i = N_FRAMES / 2; i < N_FRAMES; i++) {
826      xsteps[i] = xstep_ratio - (i - double(N_FRAMES-1) / 2.0) * xstep;
827      if (xsteps[i] < 0.01)
828        xsteps[i] = 0.0;
829    }
830
831#ifndef NDEBUG
832    cout << "Step x " << xstep << endl;
833    cout << "Matrix2 ";
834    for (int i = 0; i < N_FRAMES; i++)
835      cout << xsteps[i] << " ";
836    cout << endl;
837#endif
838
839    float granular = 0.07;
840    float zstep = granular / (double(N_FRAMES) / 4.0);
841    for (int i = 0; i < N_FRAMES / 4; i++) {
842      zsteps[i] = -i * zstep;
843    }
844    for (int i = N_FRAMES / 4; i < N_FRAMES / 2; i++) {
845      zsteps[i] = -granular + (i - double(N_FRAMES) / 4.0) * zstep;
846    }
847    for (int i = N_FRAMES / 2; i < N_FRAMES; i++) {
848      zsteps[i] = -zsteps[i - N_FRAMES / 2];
849    }
850
851#ifndef NDEBUG
852    cout << "Step z " << zstep << endl;
853    cout << "Matrix3 ";
854    for (int i = 0; i < N_FRAMES; i++)
855      cout << zsteps[i] << " ";
856    cout << endl;
857#endif
858
859    float zoomstop = 0.38;
860    float zoomstep = (zoomstop / double (N_FRAMES-1));
861    for (int i = 0; i < N_FRAMES; i++) {
862      zoomsteps[i] = i * zoomstep;
863    }
864
865#ifndef NDEBUG
866    cout << "Step zoom " << zoomstep << endl;
867    cout << "Matrix4 ";
868    for (int i = 0; i < N_FRAMES; i++)
869      cout << zoomsteps[i] << " ";
870    cout << endl;
871#endif
872
873    float perspstart = 44.00;
874    float perspstop = 21.00;
875    for (int i = 0; i < N_FRAMES; i++) {
876      perspsteps[i] = (cos(i*M_PI/(double(N_FRAMES)*2.0)))*(perspstart - perspstop)+perspstop;
877    }
878#ifndef NDEBUG
879    // cout << "Step persp " << perspstep << endl;
880    cout << "Matrix5 ";
881    for (int i = 0; i < N_FRAMES; i++)
882      cout << perspsteps[i] << " ";
883    cout << endl;
884#endif
885
886    float perspcstart = 44.00;
887    float perspcstop = 30.00;
888    for (int i = 0; i < N_FRAMES; i++) {
889      perspstepsc[i] = (1.0+cos(i*M_PI/double(N_FRAMES)))/2.0*(perspcstart - perspcstop)+perspcstop;
890    }
891#ifndef NDEBUG
892    cout << "Matrix6 ";
893    for (int i = 0; i < N_FRAMES; i++)
894      cout << perspstepsc[i] << " ";
895    cout << endl;
896#endif
897  }
898
899  // Go to the previous page
900  void
901  backward(GtkWidget * widget) {
902    update_textures_dir(widget, false);
903#ifndef NDEBUG
904    cerr << "Current page: " << current_page << endl;
905#endif
906  }
907
908  // Reset internal status and updates all textures
909  void
910  reset(GtkWidget * widget) {
911    animating = FALSE;
912    frame = 0;
913    lookposx = 0.0;
914    lookposy = 0.0;
915    lookposz = 3.48;
916    atx = 0.0;
917    aty = 0.0;
918    atz = 0.0;
919    persp = 44.0;
920    angle = 0.0;
921    current_face = 0;
922    active_animation = ANIM_NONE;
923    previous_animation = ANIM_NONE;
924    last_animation = ANIM_NONE;
925    update_textures(widget);
926  }
927
928  // Reset status without updating textures
929  void
930  quick_reset(GtkWidget * widget) {
931    // update_textures(widget);
932    animating = FALSE;
933    frame = 0;
934    lookposx = 0.0;
935    lookposy = 0.0;
936    lookposz = 3.48;
937    atx = 0.0;
938    aty = 0.0;
939    atz = 0.0;
940    persp = 44.0;
941    angle = 0.0;
942    current_face = 0;
943    // active_animation = ANIM_NONE;
944    previous_animation = ANIM_NONE;
945    last_animation = ANIM_NONE;
946  }
947
948  // shift old textures and render the new page
949  // texmap[0] -> current page
950  // texmap[1] -> prev page
951  // texmap[2] -> next page
952  void update_textures_dir(GtkWidget * widget, bool forward) {
953    assert(current_page >= 0);
954    assert(current_page < total_pages);
955    if (forward) {
956      current_page = next_page();
957      int tmp = texmap[2];
958      texmap[2] = texmap[1];
959      texmap[1] = texmap[0];
960      texmap[0] = tmp;
961      render_page(next_page());
962    } else {
963      current_page = prev_page();
964      int tmp = texmap[0];
965      texmap[0] = texmap[1];
966      texmap[1] = texmap[2];
967      texmap[2] = tmp;
968      render_page(prev_page());
969    }
970
971    glDeleteTextures(1,
972                     &textures[texmap[forward ? 2 : 1]]);
973    glBindTexture(GL_TEXTURE_RECTANGLE_ARB,
974                  textures[texmap[forward ? 2 : 1]]);
975    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
976                 0,
977                 GL_RGBA,
978                 tex_width,
979                 tex_height,
980                 0,
981                 GL_BGRA,
982                 GL_UNSIGNED_BYTE, cairo_image_surface_get_data(pixmap));
983
984    gdk_window_invalidate_rect(widget->window, &widget->allocation,
985                               FALSE);
986
987  }
988
989  // render all (3) textures
990  void update_textures(GtkWidget * widget) {
991
992    assert(current_page >= 0);
993    assert(current_page < total_pages);
994    render_page(current_page);
995    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textures[texmap[0]]);
996    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
997                 0,
998                 GL_RGBA,
999                 tex_width,
1000                 tex_height,
1001                 0,
1002                 GL_BGRA,
1003                 GL_UNSIGNED_BYTE, cairo_image_surface_get_data(pixmap));
1004
1005    render_page(prev_page());
1006    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textures[texmap[1]]);
1007    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
1008                 0,
1009                 GL_RGBA,
1010                 tex_width,
1011                 tex_height,
1012                 0,
1013                 GL_BGRA,
1014                 GL_UNSIGNED_BYTE, cairo_image_surface_get_data(pixmap));
1015
1016    render_page(next_page());
1017    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textures[texmap[2]]);
1018    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
1019                 0,
1020                 GL_RGBA,
1021                 tex_width,
1022                 tex_height,
1023                 0,
1024                 GL_BGRA,
1025                 GL_UNSIGNED_BYTE, cairo_image_surface_get_data(pixmap));
1026
1027    gdk_window_invalidate_rect(widget->window, &widget->allocation,
1028                               FALSE);
1029
1030  }
1031
1032protected:
1033  PopplerDocument * doc;
1034  int current_page;
1035  int current_face;
1036  const int total_pages;
1037  int frame;
1038  double lookposx, lookposy, lookposz;
1039  double atx, aty, atz;
1040  double persp, angle;
1041  cairo_surface_t *pixmap;
1042  cairo_t *context;
1043  int texmap[3];
1044
1045  // OpenGL Textures
1046  GLuint textures[3];
1047  GLuint cube_faces;
1048
1049  // Width and Height of the rendered pixmap (aspect
1050  // ratio is fixed, should instead depend on the
1051  // aspect ratio of the pdf page)
1052  gint tex_width; //(gint) (3 * 1024 / 2);
1053  gint tex_height; //(gint) (3 * 768 / 2);
1054
1055  // renders the poppler page on a pixmap
1056  void
1057  render_page(int i) {
1058    PopplerPage *page(0);
1059    double w, h;
1060    page = poppler_document_get_page(doc, i);
1061    poppler_page_get_size(page, &w, &h);
1062    cairo_save(context);
1063    cairo_rectangle(context, 0.0, 0.0, w, h);
1064    cairo_set_source_rgb(context, 1.0, 1.0, 1.0);
1065    cairo_fill(context);
1066    poppler_page_render(page, context);
1067    cairo_restore(context);
1068    // g_free(page);
1069  }
1070
1071  void buildLists()
1072  {
1073    int i;
1074    cube_faces=glGenLists(6);
1075   
1076    for (i = 0; i < 6; i++) {
1077      glNewList(cube_faces+i,GL_COMPILE);
1078      glBegin(GL_QUADS);
1079      if(i<4)
1080        glMultiTexCoord2f(GL_TEXTURE0_ARB,(1.0 - mapping[i][4]) * tex_width,
1081                          mapping[i][5] * tex_height);
1082      glVertex3fv(&v[faces[i][0]][0]);
1083     
1084      if(i<4)
1085        glMultiTexCoord2f(GL_TEXTURE0_ARB,(1.0 - mapping[i][6]) * tex_width,
1086                          mapping[i][7] * tex_height);
1087      glVertex3fv(&v[faces[i][1]][0]);
1088     
1089      if(i<4)
1090        glMultiTexCoord2f(GL_TEXTURE0_ARB,(1.0 - mapping[i][0]) * tex_width,
1091                          mapping[i][1] * tex_height);
1092      glVertex3fv(&v[faces[i][2]][0]);
1093     
1094      if(i<4)
1095        glMultiTexCoord2f(GL_TEXTURE0_ARB,(1.0 - mapping[i][2]) * tex_width,
1096                          mapping[i][3] * tex_height);
1097      glVertex3fv(&v[faces[i][3]][0]);
1098      glEnd();
1099      glEndList();
1100    }
1101   
1102  }
1103
1104  void
1105  drawCube(void) {
1106    int i;
1107
1108    for (i = 0; i < 6; i++) {
1109      if (i == current_face) {
1110        glEnable(GL_TEXTURE_RECTANGLE_ARB);
1111        glActiveTextureARB(GL_TEXTURE0_ARB);   
1112        glBindTexture(GL_TEXTURE_RECTANGLE_ARB,
1113                      textures[texmap[0]]);
1114      } else if (i == prev_face()) {
1115        glEnable(GL_TEXTURE_RECTANGLE_ARB);
1116        glActiveTextureARB(GL_TEXTURE0_ARB);   
1117        glBindTexture(GL_TEXTURE_RECTANGLE_ARB,
1118                      textures[texmap[1]]);
1119      } else if (i == next_face()) {
1120        glEnable(GL_TEXTURE_RECTANGLE_ARB);
1121        glActiveTextureARB(GL_TEXTURE0_ARB);   
1122        glBindTexture(GL_TEXTURE_RECTANGLE_ARB,
1123                      textures[texmap[2]]);
1124      } else if (i <= 3) {
1125        glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
1126        glDisable(GL_TEXTURE_RECTANGLE_ARB);
1127        glColor4f(top_color[0], top_color[1], top_color[2], top_color[3]);
1128      } else {
1129        glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
1130        glDisable(GL_TEXTURE_RECTANGLE_ARB);
1131        glColor4f(top_color[0], top_color[1], top_color[2], top_color[3]);
1132      }
1133      glPolygonMode(GL_FRONT, GL_FILL);
1134      glCallList(cube_faces+i);
1135    }
1136  }
1137};
1138
1139// cube normals
1140GLfloat
1141pdfcube::n[6][3] = {
1142  {0.0, 0.0, -1.0}, 
1143  {1.0, 0.0, 0.0}, 
1144  {0.0, 0.0, 1.0}, 
1145  {-1.0, 0.0, 0.0},
1146  {0.0, 1.0, 0.0},
1147  {0.0, -1.0, 0.0}
1148};
1149//bcube faces
1150GLint
1151pdfcube::faces[6][4] = {
1152  {7, 4, 0, 3},
1153  {7, 6, 5, 4},
1154  {5, 6, 2, 1},
1155  {0, 1, 2, 3},
1156  {3, 2, 6, 7},
1157  {4, 5, 1, 0}
1158};
1159// face mapping
1160GLfloat
1161pdfcube::mapping[6][8] = {
1162  {1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0}
1163  ,
1164  {0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0}
1165  ,
1166  {0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0}
1167  ,
1168  {1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0}
1169  ,
1170  {1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0}
1171  ,                        // top
1172  {1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0}
1173  ,                        // bottom
1174};
1175
1176// the pdf-cube
1177pdfcube* pc;
1178
1179//////////////////////////////////////////////////////////////////////////
1180// Callbacks
1181
1182/***
1183 *** The "realize" signal handler. All the OpenGL initialization
1184 *** should be performed here, such as default background colour,
1185 *** certain states etc.
1186 ***/
1187static void
1188realize(GtkWidget * widget, gpointer data)
1189{
1190  GdkGLContext *
1191    glcontext = gtk_widget_get_gl_context(widget);
1192  GdkGLDrawable *
1193    gldrawable = gtk_widget_get_gl_drawable(widget);
1194
1195#ifndef NDEBUG
1196  g_print("%s: \"realize\"\n", gtk_widget_get_name(widget));
1197#endif
1198  //g_mutex_lock (gl_mutex);
1199
1200  /*** OpenGL BEGIN ***/
1201  if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
1202    return;
1203
1204  pc->initialize(widget);
1205
1206  gdk_gl_drawable_gl_end(gldrawable);
1207  /*** OpenGL END ***/
1208  //g_mutex_unlock (gl_mutex);
1209}
1210
1211/***
1212 *** The "configure_event" signal handler. Any processing required when
1213 *** the OpenGL-capable drawing area is re-configured should be done here.
1214 *** Almost always it will be used to resize the OpenGL viewport when
1215 *** the window is resized.
1216 ***/
1217static
1218gboolean
1219configure_event(GtkWidget * widget, GdkEventConfigure * event, gpointer data)
1220{
1221  GdkGLContext *
1222    glcontext = gtk_widget_get_gl_context(widget);
1223  GdkGLDrawable *
1224    gldrawable = gtk_widget_get_gl_drawable(widget);
1225
1226  GLsizei
1227    w = widget->allocation.width;
1228  GLsizei
1229    h = widget->allocation.height;
1230
1231#ifndef NDEBUG
1232  g_print("%s: \"configure_event\"\n", gtk_widget_get_name(widget));
1233#endif
1234  //g_mutex_lock (gl_mutex);
1235  /*** OpenGL BEGIN ***/
1236  if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
1237    return FALSE;
1238
1239  glViewport(0, 0, w, h);
1240
1241  gdk_gl_drawable_gl_end(gldrawable);
1242
1243  /*** OpenGL END ***/
1244  //g_mutex_unlock (gl_mutex);
1245
1246  return TRUE;
1247}
1248
1249/***
1250 *** The "expose_event" signal handler. All the OpenGL re-drawing should
1251 *** be done here. This is repeatedly called as the painting routine
1252 *** every time the 'expose'/'draw' event is signalled.
1253 ***/
1254static
1255gboolean
1256expose_event(GtkWidget * widget, GdkEventExpose * event, gpointer data)
1257{
1258  GdkGLContext *
1259    glcontext = gtk_widget_get_gl_context(widget);
1260  GdkGLDrawable *
1261    gldrawable = gtk_widget_get_gl_drawable(widget);
1262
1263  //g_mutex_lock (gl_mutex);
1264  /*** OpenGL BEGIN ***/
1265  if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
1266    return FALSE;
1267
1268  glDrawBuffer(GL_BACK);
1269
1270  pc->redraw(widget);
1271
1272  /* Swap buffers */
1273  if (gdk_gl_drawable_is_double_buffered(gldrawable))
1274    gdk_gl_drawable_swap_buffers(gldrawable);
1275  else
1276    glFlush();
1277
1278  gdk_gl_drawable_gl_end(gldrawable);
1279  /*** OpenGL END ***/
1280
1281  //g_mutex_unlock (gl_mutex);
1282
1283  return TRUE;
1284}
1285
1286/***
1287 *** Auto advance to next slide
1288 ***/
1289static
1290gboolean
1291auto_advance(GtkWidget * widget)
1292{
1293  if(sleeping())
1294    {
1295      if(page_duration[(pc->page()+1)%pc->pages()] + (page_transition[(pc->page()+1)%pc->pages()] ? 750 : 0) > 0)
1296        auto_advance_id = g_timeout_add(page_duration[(pc->page()+1)%pc->pages()] + (page_transition[(pc->page()+1)%pc->pages()] ? 750 : 0),
1297                                        (GSourceFunc)auto_advance, 
1298                                        widget);
1299      else
1300        auto_advance_id = 0;
1301#ifndef NDEBUG
1302      cerr << "auto advance mode: " << (page_duration[(pc->page()+1)%pc->pages()] + (page_transition[(pc->page()+1)%pc->pages()] ? 750 : 0)) << endl;
1303#endif
1304      if (page_transition[(pc->page()+1)%pc->pages()])
1305        start_animation(widget, CUBE_NEXT);
1306      else
1307        start_animation(widget, SWITCH_FW);
1308      return FALSE;
1309    }
1310  return TRUE;
1311}
1312
1313/***
1314 *** The timeout function. Often in animations,
1315 *** timeout functions are suitable for continous
1316 *** frame updates.
1317 ***/
1318static
1319gboolean
1320timeout(GtkWidget * widget)
1321{
1322  /* Invalidate the whole window. */
1323  gdk_window_invalidate_rect(widget->window, &widget->allocation, FALSE);
1324
1325  /* Update synchronously. */
1326  gdk_window_process_updates(widget->window, FALSE);
1327
1328  return TRUE;
1329}
1330
1331/***
1332 *** The "unrealize" signal handler. Any processing required when
1333 *** the OpenGL-capable window is unrealized should be done here.
1334 ***/
1335static void
1336unrealize(GtkWidget * widget, gpointer data)
1337{
1338  GdkGLContext *
1339    glcontext = gtk_widget_get_gl_context(widget);
1340  GdkGLDrawable *
1341    gldrawable = gtk_widget_get_gl_drawable(widget);
1342
1343#ifndef NDEBUG
1344  g_print("%s: \"unrealize\"\n", gtk_widget_get_name(widget));
1345#endif
1346  //g_mutex_lock (gl_mutex);
1347
1348  /*** OpenGL BEGIN ***/
1349  if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
1350    return;
1351
1352  gdk_gl_drawable_gl_end(gldrawable);
1353  /*** OpenGL END ***/
1354  //g_mutex_unlock (gl_mutex);
1355}
1356
1357/***
1358 *** The "motion_notify_event" signal handler. Any processing required when
1359 *** the OpenGL-capable drawing area is under drag motion should be done here.
1360 ***/
1361static
1362gboolean
1363motion_notify_event(GtkWidget * widget, GdkEventMotion * event, gpointer data)
1364{
1365#ifndef NDEBUG
1366  g_print("%s: \"motion_notify_event\": button",
1367          gtk_widget_get_name(widget));
1368  if (event->state & GDK_BUTTON1_MASK) {
1369    g_print(" 1");
1370  }
1371
1372  if (event->state & GDK_BUTTON2_MASK) {
1373    g_print(" 2");
1374  }
1375
1376  if (event->state & GDK_BUTTON3_MASK) {
1377    g_print(" 3");
1378  }
1379
1380  g_print("\n");
1381#endif
1382  return FALSE;
1383}
1384
1385/***
1386 *** The "button_press_event" signal handler. Any processing required when
1387 *** mouse buttons (only left and middle buttons) are pressed on the OpenGL-
1388 *** capable drawing area should be done here.
1389 ***/
1390static
1391gboolean
1392button_press_event(GtkWidget * widget, GdkEventButton * event, gpointer data)
1393{
1394#ifndef NDEBUG
1395  g_print("%s: \"button_press_event\": ", gtk_widget_get_name(widget));
1396#endif
1397  if (event->button == 1) {
1398#ifndef NDEBUG
1399    g_print("button 1\n");
1400#endif
1401    return TRUE;
1402  }
1403
1404  if (event->button == 2) {
1405#ifndef NDEBUG
1406    g_print("button 2\n");
1407#endif
1408    return TRUE;
1409  }
1410#ifndef NDEBUG
1411  g_print("\n");
1412#endif
1413  return FALSE;
1414}
1415
1416/***
1417 *** The "key_press_event" signal handler. Any processing required when key
1418 *** presses occur should be done here.
1419 ***/
1420static
1421gboolean
1422key_press_event(GtkWidget * widget, GdkEventKey * event, gpointer data)
1423{
1424#ifndef NDEBUG
1425  g_print("%s: \"key_press_event\": ", gtk_widget_get_name(widget));
1426#endif
1427  if (event->state == GDK_CONTROL_MASK)
1428    {
1429      switch (event->keyval) {
1430      case GDK_1:
1431      case GDK_2:
1432      case GDK_3:
1433      case GDK_4:
1434      case GDK_5:
1435      case GDK_6:
1436      case GDK_7:
1437      case GDK_8:
1438      case GDK_9:
1439#ifndef NDEBUG
1440        g_print("Ctrl-n key\n");
1441#endif
1442        if (sleeping())
1443          pc->section(widget, event->keyval - GDK_1 + 1);
1444        break;
1445       
1446        // Let's quit
1447      case GDK_q:
1448#ifndef NDEBUG
1449        g_print("Ctrl-q key\n");
1450#endif
1451        gtk_main_quit();
1452        break;
1453       
1454        // Update all textures
1455      case GDK_l:
1456#ifndef NDEBUG
1457        g_print("Ctrl-l key\n");
1458        cerr << "Pagina: " << pc->page() << endl;
1459#endif
1460        pc->reset(widget);
1461        break;
1462      }
1463    } 
1464  else
1465    {
1466      switch (event->keyval) {
1467       
1468      case GDK_t:
1469        if (sleeping())
1470          {
1471            if (auto_advance_id == 0) 
1472              {
1473                auto_advance_id = g_timeout_add(page_duration[pc->page()],
1474                                                (GSourceFunc)auto_advance, 
1475                                                widget);
1476#ifndef NDEBUG
1477                cerr << "auto advance mode: " << page_duration[pc->page()] << endl;
1478#endif
1479              }
1480            else
1481              {
1482                g_source_remove(auto_advance_id);
1483                auto_advance_id = 0;
1484              }
1485          }
1486        break;
1487
1488        // return to page 1
1489      case GDK_1:
1490      case GDK_2:
1491      case GDK_3:
1492      case GDK_4:
1493      case GDK_5:
1494      case GDK_6:
1495      case GDK_7:
1496      case GDK_8:
1497      case GDK_9:
1498#ifndef NDEBUG
1499        g_print("n key\n");
1500#endif
1501        if (sleeping())
1502          pc->go_to(widget, (event->keyval - GDK_1) * 5);
1503        break;
1504       
1505        // Animated Cube Advancement
1506      case GDK_a:
1507#ifndef NDEBUG
1508        g_print("a key\n");
1509#endif
1510        if (sleeping())
1511          start_animation(widget, CUBE_PREV);
1512       
1513        break;
1514      case GDK_c:
1515#ifndef NDEBUG
1516        g_print("c key\n");
1517#endif
1518        if (sleeping())
1519          start_animation(widget, CUBE_NEXT);
1520        break;
1521       
1522        // Quick switch to next page
1523      case GDK_Right:
1524#ifndef NDEBUG
1525        g_print("- key\n");
1526#endif
1527        if (sleeping())
1528          start_animation(widget, SWITCH_FW);
1529        break;
1530       
1531        // Quick switch to previous page
1532      case GDK_Page_Up:
1533      case GDK_Left:
1534#ifndef NDEBUG
1535        g_print("+ key\n");
1536#endif
1537        if (sleeping())
1538          start_animation(widget, SWITCH_BW);
1539        break;
1540       
1541      case GDK_g:
1542#ifndef NDEBUG
1543        g_print("Zoom0 key\n");
1544#endif
1545        if (sleeping())
1546          if (last_animation >=
1547              ZOOM1 and last_animation <= ZOOMC)
1548            start_animation(widget, ZOOM0);
1549        break;
1550       
1551      case GDK_h:
1552#ifndef NDEBUG
1553        g_print("Zoom1 key\n");
1554#endif
1555        if (sleeping())
1556          if (last_animation >=
1557              ZOOM1 and last_animation <= ZOOMC)
1558            start_animation(widget, ZOOM0);
1559          else
1560            start_animation(widget, ZOOM1);
1561        break;
1562       
1563      case GDK_j:
1564#ifndef NDEBUG
1565        g_print("Zoom2 key\n");
1566#endif
1567        if (sleeping())
1568          if (last_animation >=
1569              ZOOM1 and last_animation <= ZOOMC)
1570            start_animation(widget, ZOOM0);
1571          else
1572            start_animation(widget, ZOOM2);
1573        break;
1574       
1575      case GDK_k:
1576#ifndef NDEBUG
1577        g_print("Zoom3 key\n");
1578#endif
1579        if (sleeping())
1580          if (last_animation >=
1581              ZOOM1 and last_animation <= ZOOMC)
1582            start_animation(widget, ZOOM0);
1583          else
1584            start_animation(widget, ZOOM3);
1585        break;
1586       
1587      case GDK_l:
1588#ifndef NDEBUG
1589        g_print("Zoom4 key\n");
1590#endif
1591        if (sleeping())
1592          if (last_animation >=
1593              ZOOM1 and last_animation <= ZOOMC)
1594            start_animation(widget, ZOOM0);
1595          else
1596            start_animation(widget, ZOOM4);
1597        break;
1598       
1599      case GDK_z:
1600#ifndef NDEBUG
1601        g_print("Zoom key\n");
1602#endif
1603        if (sleeping())
1604          if (last_animation >=
1605              ZOOM1 and last_animation <= ZOOMC)
1606            start_animation(widget, ZOOM0);
1607          else
1608            start_animation(widget, ZOOMC);
1609        break;
1610       
1611        // Automatic advance (you should set the Animated slides on the command line)
1612      case GDK_Page_Down:
1613      case GDK_space:
1614#ifndef NDEBUG
1615        g_print("Advance key\n");
1616#endif
1617        if (page_transition[(pc->page()+1)%pc->pages()] and sleeping())
1618          start_animation(widget, CUBE_NEXT);
1619        else if (sleeping())
1620          start_animation(widget, SWITCH_FW);
1621       
1622        break;
1623       
1624        // switch fullscreen
1625      case GDK_f:
1626#ifndef NDEBUG
1627        g_print("f key\n");
1628#endif
1629        if ((fullscreen = !fullscreen) == true)
1630          gtk_window_fullscreen((GtkWindow *) (data));
1631        else
1632          gtk_window_unfullscreen((GtkWindow *) (data));
1633        break;
1634       
1635        // Let's quit
1636      case GDK_Escape:
1637#ifndef NDEBUG
1638        g_print("Escape key\n");
1639#endif
1640        gtk_main_quit();
1641        break;
1642       
1643      default:
1644#ifndef NDEBUG
1645        g_print("Unknown key\n");
1646#endif
1647        return FALSE;
1648      }
1649    }
1650  return TRUE;
1651}
1652
1653//////////////////////////////////////////////////////////////////////////
1654// Timeout functions
1655
1656/***
1657 *** Helper functions to add or remove the timeout function.
1658 ***/
1659
1660static guint
1661timeout_id = 0;
1662
1663static void
1664timeout_add(GtkWidget * widget)
1665{
1666  if (timeout_id == 0) {
1667    timeout_id = g_timeout_add(TIMEOUT_INTERVAL,
1668                               (GSourceFunc) timeout, widget);
1669  }
1670}
1671
1672static void
1673timeout_remove(GtkWidget * widget)
1674{
1675  if (timeout_id != 0) {
1676    g_source_remove(timeout_id);
1677    timeout_id = 0;
1678  }
1679}
1680
1681/***
1682 *** The "map_event" signal handler. Any processing required when the
1683 *** OpenGL-capable drawing area is mapped should be done here.
1684 ***/
1685static
1686gboolean
1687map_event(GtkWidget * widget, GdkEvent * event, gpointer data)
1688{
1689#ifndef NDEBUG
1690  g_print("%s: \"map_event\":\n", gtk_widget_get_name(widget));
1691#endif
1692  if (animating)
1693    timeout_add(widget);
1694
1695  return TRUE;
1696}
1697
1698/***
1699 *** The "unmap_event" signal handler. Any processing required when the
1700 *** OpenGL-capable drawing area is unmapped should be done here.
1701 ***/
1702static
1703gboolean
1704unmap_event(GtkWidget * widget, GdkEvent * event, gpointer data)
1705{
1706#ifndef NDEBUG
1707  g_print("%s: \"unmap_event\":\n", gtk_widget_get_name(widget));
1708#endif
1709  timeout_remove(widget);
1710
1711  return TRUE;
1712}
1713
1714/***
1715 *** The "visibility_notify_event" signal handler. Any processing required
1716 *** when the OpenGL-capable drawing area is visually obscured should be
1717 *** done here.
1718 ***/
1719static
1720gboolean
1721visibility_notify_event(GtkWidget * widget,
1722                        GdkEventVisibility * event, gpointer data)
1723{
1724  if (animating) {
1725    if (event->state == GDK_VISIBILITY_FULLY_OBSCURED)
1726      timeout_remove(widget);
1727    else
1728      timeout_add(widget);
1729  }
1730
1731  return TRUE;
1732}
1733
1734/**************************************************************************
1735 * The following section contains some miscellaneous utility functions.
1736 **************************************************************************/
1737
1738/***
1739 *** Toggle animation.
1740 ***/
1741static void
1742start_animation(GtkWidget * widget, enum animation a)
1743{
1744  if (sleeping()) {
1745    animating = true;
1746    previous_animation = last_animation;
1747    last_animation = active_animation = a;
1748    timeout_add(widget);
1749  }
1750}
1751
1752static void
1753stop_animation(GtkWidget * widget)
1754{
1755  animating = false;
1756  timeout_remove(widget);
1757  gdk_window_invalidate_rect(widget->window, &widget->allocation, FALSE);
1758  gdk_window_process_updates(widget->window, FALSE);
1759}
1760
1761//////////////////////////////////////////////////////////////////////////
1762// GTK+ GUI Functions
1763
1764/***
1765 *** Creates the simple application window with one
1766 *** drawing area that has an OpenGL-capable visual.
1767 ***/
1768static GtkWidget *
1769create_window(GdkGLConfig * glconfig)
1770{
1771  GtkWidget *
1772    window;
1773
1774  GtkWidget* alignment;
1775  GtkWidget* keep_aspect;
1776  /*
1777   * Top-level window.
1778   */
1779
1780  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1781  gtk_window_set_title(GTK_WINDOW(window), DEFAULT_TITLE);
1782
1783  /* Get automatically redrawn if any of their children changed allocation. */
1784  gtk_container_set_reallocate_redraws(GTK_CONTAINER(window), TRUE);
1785
1786  /* Connect signal handlers to the window */
1787  g_signal_connect(G_OBJECT(window), "delete_event",
1788                   G_CALLBACK(gtk_main_quit), NULL);
1789
1790  /*
1791   * Aspect ratio frame
1792   */
1793  keep_aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, pdf_aspect_ratio, FALSE);
1794  gtk_widget_show(keep_aspect);
1795
1796  /*
1797   * Drawing area to draw OpenGL scene.
1798   */
1799
1800  drawing_area = gtk_drawing_area_new();
1801  gtk_widget_set_size_request(drawing_area, DEFAULT_WIDTH,
1802                              DEFAULT_HEIGHT);
1803
1804  /* Set OpenGL-capability to the widget */
1805  gtk_widget_set_gl_capability(drawing_area,
1806                               glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE);
1807
1808  gtk_widget_add_events(drawing_area,
1809                        GDK_BUTTON1_MOTION_MASK |
1810                        GDK_BUTTON2_MOTION_MASK |
1811                        GDK_BUTTON_PRESS_MASK |
1812                        GDK_VISIBILITY_NOTIFY_MASK);
1813
1814  /* Connect signal handlers to the drawing area */
1815  g_signal_connect_after(G_OBJECT(drawing_area), "realize",
1816                         G_CALLBACK(realize), NULL);
1817  g_signal_connect(G_OBJECT(drawing_area), "configure_event",
1818                   G_CALLBACK(configure_event), NULL);
1819  g_signal_connect(G_OBJECT(drawing_area), "expose_event",
1820                   G_CALLBACK(expose_event), NULL);
1821  g_signal_connect(G_OBJECT(drawing_area), "unrealize",
1822                   G_CALLBACK(unrealize), NULL);
1823
1824  g_signal_connect(G_OBJECT(drawing_area), "motion_notify_event",
1825                   G_CALLBACK(motion_notify_event), NULL);
1826  g_signal_connect(G_OBJECT(drawing_area), "button_press_event",
1827                   G_CALLBACK(button_press_event), NULL);
1828
1829  /* key_press_event handler for top-level window */
1830  g_signal_connect_swapped(G_OBJECT(window), "key_press_event",
1831                           G_CALLBACK(key_press_event), drawing_area);
1832 
1833  /* For timeout function. */
1834  g_signal_connect(G_OBJECT(drawing_area), "map_event",
1835                   G_CALLBACK(map_event), NULL);
1836  g_signal_connect(G_OBJECT(drawing_area), "unmap_event",
1837                   G_CALLBACK(unmap_event), NULL);
1838  g_signal_connect(G_OBJECT(drawing_area), "visibility_notify_event",
1839                   G_CALLBACK(visibility_notify_event), NULL);
1840
1841  gtk_container_add(GTK_CONTAINER(window), keep_aspect);
1842  gtk_container_add(GTK_CONTAINER(keep_aspect), drawing_area);
1843
1844  gtk_container_set_border_width (GTK_CONTAINER (window), 0); 
1845  gtk_container_set_border_width (GTK_CONTAINER (keep_aspect), 0); 
1846  // gtk_container_set_border_width (GTK_CONTAINER (drawing_area), 0);
1847
1848  gtk_frame_set_shadow_type(GTK_FRAME(keep_aspect), GTK_SHADOW_NONE);
1849
1850  GdkColor color;
1851  color.red = clear_color[0]*65535;
1852  color.green = clear_color[1]*65535;
1853  color.blue = clear_color[2]*65535;
1854  gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &color);
1855 
1856  gtk_widget_show(drawing_area);
1857
1858  return window;
1859}
1860
1861/***
1862 *** Configure the OpenGL framebuffer.
1863 ***/
1864static GdkGLConfig *
1865configure_gl(void)
1866{
1867  GdkGLConfig *
1868    glconfig;
1869
1870  /* Try double-buffered visual */
1871  glconfig = gdk_gl_config_new_by_mode((GdkGLConfigMode)
1872                                       (GDK_GL_MODE_RGB |
1873                                        GDK_GL_MODE_DEPTH |
1874                                        GDK_GL_MODE_DOUBLE
1875                                        ));
1876  if (glconfig == NULL) {
1877    g_print("\n*** Cannot find the double-buffered visual.\n");
1878    g_print("\n*** Trying single-buffered visual.\n");
1879
1880    /* Try single-buffered visual */
1881    glconfig = gdk_gl_config_new_by_mode((GdkGLConfigMode)
1882                                         (GDK_GL_MODE_RGB |
1883                                          GDK_GL_MODE_DEPTH));
1884    if (glconfig == NULL) {
1885      g_print
1886        ("*** No appropriate OpenGL-capable visual found.\n");
1887      exit(1);
1888    }
1889  }
1890
1891  return glconfig;
1892}
1893
1894///
1895/// @brief Gets the absolute path of a filename.
1896///
1897/// This function checks if the given @a fileName is an absolute path. If
1898/// it is then it returns a copy of it, otherwise it prepends the current
1899/// working directory to it.
1900///
1901/// @param fileName The filename to get the absolute path from.
1902///
1903/// @return A copy of the absolute path to the file name. This copy must be
1904///         freed when no longer needed.
1905///
1906gchar *
1907get_absolute_file_name(const gchar * fileName)
1908{
1909  gchar *
1910    absoluteFileName = NULL;
1911  if (g_path_is_absolute(fileName)) {
1912    absoluteFileName = g_strdup(fileName);
1913  } else {
1914    gchar *
1915      currentDir = g_get_current_dir();
1916    absoluteFileName = g_build_filename(currentDir, fileName, NULL);
1917    g_free(currentDir);
1918  }
1919
1920  return absoluteFileName;
1921}
1922
1923//////////////////////////////////////////////////////////////////////////
1924// Main function: should we use getopts? (who doesn't?)
1925// (update: we do, let's use boost::program_options ;))
1926
1927int
1928main(int argc, char *argv[])
1929{
1930
1931  GtkWidget *
1932    window;
1933  GdkGLConfig *
1934    glconfig;
1935
1936  /* Initialize GTK. */
1937  gtk_init(&argc, &argv);
1938
1939  /* Initialize GtkGLExt. */
1940  gtk_gl_init(&argc, &argv);
1941
1942  po::options_description generic("Generic options");
1943  generic.add_options()
1944    ("help,h", 
1945     "This help message.")
1946    ("version,v",
1947     "Version information");
1948
1949  po::options_description opts("Available options");
1950  opts.add_options()
1951    ("bgcolor,b", po::value<std::string>(),
1952     "Background color is 'r:g:b' with real values between 0.0 and 1.0, no spaces.")
1953    ("top-color,t", po::value<std::string>(),
1954     "Cube top color in 'r:g:b' format again with reals in [0,1].")
1955    ("demo", "auto advance slides based on transition duration (\\transduration)")
1956    ("program", po::value<std::string>(), "Specify a program: duration:transition:duration:transition:... e.g. 5::5:c:7: (overrides \\transboxin and \\transduration PDF attributes)")
1957    ("no-fullscreen,n", 
1958     "Don't activate full-screen mode by default.");
1959
1960  po::options_description hidden("Hidden options");
1961  hidden.add_options()
1962    ("input-file,i", po::value<std::string>(), "PDF file to show.");
1963 
1964  po::positional_options_description p;
1965  p.add("input-file", -1);
1966
1967  po::options_description commandline;
1968  commandline.add(generic).add(opts).add(hidden);
1969   
1970  po::options_description dotopts;
1971  dotopts.add(opts);
1972
1973  po::options_description visible;
1974  visible.add(generic).add(opts);
1975   
1976  po::variables_map vm;
1977
1978  char* home(0);
1979  home = getenv("HOME");
1980  if(home)
1981    {
1982      std::ostrstream dotfilename;
1983      dotfilename << home << "/.pdfcuberc";
1984      std::cerr << "Reading options from " << dotfilename.str() << std::endl;
1985      std::ifstream dotfile(dotfilename.str());
1986      if(dotfile.good())
1987        {
1988          try {
1989            po::store(po::parse_config_file(dotfile, dotopts), vm);
1990          } catch (const boost::program_options::error& e) {
1991            std::cerr << "Error parsing the .pdfcuberc dotfile: " << e.what() << std::endl;
1992            return -1;
1993          }
1994        }
1995    }
1996
1997  try {
1998    po::store(po::command_line_parser(argc, argv).
1999              options(commandline).positional(p).run(), vm);
2000  } catch (const boost::program_options::error& e) {
2001    std::cerr << "Command line option parsing error: " << e.what() << std::endl;
2002    return -1;
2003  }
2004 
2005  po::notify(vm);
2006
2007  string input_file;
2008
2009  if(vm.count("help")) {
2010    cout << endl << "pdfcube 0.0.5" << endl;
2011    cout << "=============" << endl;
2012    cout << "Copyright (C) 2006-2012 Mirko Maischberger <mirko.maischberger@gmail.com>" << endl;
2013    cout << "                   2008 Karol Sokolowski   <sokoow@gmail.com>" <<  endl << endl;
2014    cout << visible << endl;
2015    cout << endl;
2016    cout << "Usage examples:" << endl;
2017    cout << "  $ pdfcube" << endl;
2018    cout << "  $ pdfcube presentation.pdf" << endl;
2019    cout << 
2020      "  $ pdfcube presentation.pdf --bgcolor 0:0:0 --top-color 0.6:0.2:0.2" <<
2021      endl << endl <<
2022      "  The \"Box\" transition present in PDF files is rendered as a 3D cube." 
2023         <<
2024      endl <<
2025      "  (e.g in beamer you can use the \\transboxin command)" <<
2026      endl << endl <<
2027      "  If no file is given on the command line a file open dialog is shown." 
2028         <<
2029      endl << endl <<
2030      "  NB: floating point numbers are locale aware on some C libraries." <<
2031      endl << endl << endl;
2032    return 0;
2033  }
2034 
2035  if(vm.count("version")) {
2036    cout << "pdfcube 0.0.5" << endl;
2037    return 0;
2038  }
2039
2040  if(vm.count("bgcolor")) {
2041    vector<double> cc; 
2042    string v;
2043    std::istringstream iss(vm["bgcolor"].as<std::string>());
2044    while(getline(iss, v, ':')) cc.push_back(::atof(v.c_str())); 
2045    if(cc.size() != 3)
2046      {
2047        cerr << "You should specify 3 values for background-color." << endl;
2048        exit(1);
2049      }
2050    std::copy(&cc[0], &cc[3], &clear_color[0]);
2051  }
2052
2053  if(vm.count("top-color")) {
2054    vector<double> tc; 
2055    string v;
2056    std::istringstream iss(vm["top-color"].as<std::string>());
2057    while(getline(iss, v, ':')) tc.push_back(::atof(v.c_str())); 
2058    if(tc.size() != 3)
2059      {
2060        cerr << "You should specify 3 values for top-color." << endl;
2061        exit(1);
2062      }
2063    std::copy(&tc[0], &tc[3], &top_color[0]);
2064  }
2065
2066  if (vm.count("input-file")) {
2067    input_file = vm["input-file"].as<std::string>();
2068  } else {
2069    GtkWidget* filesel;
2070    filesel = gtk_file_chooser_dialog_new("Choose a PDF presentation...",
2071                                          NULL,
2072                                          GTK_FILE_CHOOSER_ACTION_OPEN,
2073                                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2074                                          GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2075                                          NULL);
2076    if (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT)
2077      {
2078        char *filename;
2079        filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel));
2080        input_file = filename;
2081        g_free(filename);
2082      }
2083    else
2084      return 0;
2085    gtk_widget_destroy(filesel);
2086  }
2087
2088  /* Configure OpenGL framebuffer. */
2089  glconfig = configure_gl();
2090
2091  gchar *
2092    absoluteFileName = get_absolute_file_name(input_file.c_str());
2093  gchar *
2094    filename_uri = g_filename_to_uri(absoluteFileName, NULL, NULL);
2095  g_free(absoluteFileName);
2096  if (NULL == filename_uri) {
2097    cerr << "File name error." << endl;
2098  }
2099  clog << "Opening: " << filename_uri << endl;
2100  GError *error = NULL;
2101  PopplerDocument *
2102    document = poppler_document_new_from_file(filename_uri, NULL, &error);
2103
2104  if (document == NULL) {
2105    cerr << "Invaild PDF file." << error->message << endl;
2106    exit(1);
2107  }
2108
2109  pc = new pdfcube(document);
2110
2111  // Read transitions form PDF file
2112  page_transition = new bool[pc->pages()];
2113  page_duration = new int[pc->pages()];
2114  for(int ii(0); ii != pc->pages(); ++ii)
2115    {
2116      page_transition[ii] = false;
2117      page_duration[ii] = 0;
2118
2119      PopplerPage* page = 
2120        poppler_document_get_page(document, ii);
2121      PopplerPageTransition* transition = 
2122        poppler_page_get_transition(page);
2123
2124      if(!transition) 
2125        {
2126          page_transition[ii] = false;
2127        }
2128      else
2129        {
2130          page_duration[ii] = (transition->duration * 1000);
2131          switch(transition->type)
2132            {
2133            case POPPLER_PAGE_TRANSITION_REPLACE:
2134              page_transition[ii] = false;
2135              break;
2136            case POPPLER_PAGE_TRANSITION_BOX:
2137              cerr << "Supported transition type (" << transition->type
2138                   << ") at page " << (ii+1) << endl;
2139              page_transition[ii] = true;
2140              break;
2141            default:
2142              cerr << "Unsupported transition type (" << transition->type
2143                   << ") at page " << (ii+1) << endl;
2144              break;
2145            }
2146        }
2147    }
2148 
2149  if(vm.count("program"))
2150    {
2151      std::istringstream iss(vm["program"].as<std::string>());
2152      int c1(0), c2(0);
2153      int last_duration(0);
2154      bool last_transition(false);
2155      for(;;)
2156        {
2157          string v;
2158          if(getline(iss, v, ':')) 
2159            last_duration = page_duration[c1++] = ::atof(v.c_str())*1000;
2160          else break;
2161          if(getline(iss, v, ':')) 
2162            last_transition = page_transition[(++c2)%pc->pages()] = (v == "c");
2163          else break;
2164          if(c1 == pc->pages()) break;
2165        }
2166      // repeat last option for all following slides
2167      for(; c1!=pc->pages(); ++c1)
2168        page_duration[c1] = last_duration;
2169      for(; c2!=pc->pages(); ++c2)
2170        page_transition[(c2+1)%pc->pages()] = last_transition;
2171    }
2172 
2173  cerr << "program: ";
2174  for(int pp(0); pp!=pc->pages(); ++pp)
2175    cerr  << page_duration[pp] << ":" 
2176          << (page_transition[(pp+1)%pc->pages()] ? 'c' : 'p') << ":";
2177  cerr << endl;
2178
2179  /* Create and show the application window. */
2180  window = create_window(glconfig);
2181 
2182  if(vm.count("no-fullscreen"))
2183    fullscreen = FALSE;
2184  else
2185    fullscreen = TRUE;
2186 
2187  if(fullscreen)
2188    gtk_window_fullscreen((GtkWindow *) (window));
2189 
2190  gtk_widget_show(window);
2191 
2192  if(vm.count("demo"))
2193    {
2194      auto_advance_id = g_timeout_add(page_duration[pc->page()],
2195                                      (GSourceFunc)auto_advance, 
2196                                      drawing_area);
2197#ifndef NDEBUG
2198      cerr << "auto advance mode: " << page_duration[pc->page()] << endl;
2199#endif
2200    }
2201 
2202  gtk_main();
2203
2204  return 0;
2205}
2206
2207// EOF
2208//////////////////////////////////////////////////////////////////////////
Note: See TracBrowser for help on using the browser.