Initial commit
authorDmitriy Morozov <dmitriy@mrzv.org>
Mon, 08 Nov 2010 22:42:06 -0800
changeset 0 7726fefc58a4
child 1 14e9780340d7
Initial commit
Makefile
canvasview.cpp
canvasview.h
mainwindow.cpp
mainwindow.h
mainwindow.ui
pagedialog.cpp
pagedialog.h
timelabel.cpp
timelabel.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Mon Nov 08 22:42:06 2010 -0800
@@ -0,0 +1,36 @@
+# --------------------------------------------------------------------
+# Makefile for IpePresenter
+# --------------------------------------------------------------------
+
+QT_CFLAGS = -I/usr/include/QtGui -I/usr/include/QtCore
+QT_LIBS = -lQtGui -lQtCore
+CPPFLAGS += -Iipeqtcanvas $(QT_CFLAGS)
+LIBS += -lipeqtcanvas -lipecairo -lipe $(QT_LIBS) $(CAIRO_LIBS) -Llib
+
+OBJECTS = mainwindow.o moc_mainwindow.o canvasview.o moc_canvasview.o \
+		  timelabel.o moc_timelabel.o pagedialog.o moc_pagedialog.o
+
+# .INTERMEDIATE: $(OBJECTS)		  
+
+all: ipepresenter
+
+moc_%.cpp: %.h
+	moc -o $@ $<
+
+# %.o: %.cpp
+#     g++ $< -c $(CPPFLAGS)
+
+# moc_%.o: moc_%.cpp
+#     g++ $< -c $(CPPFLAGS)
+
+ui_%.h: %.ui
+	uic $< > $@
+
+mainwindow.o: mainwindow.cpp ui_mainwindow.h
+	g++ $< -c $(CPPFLAGS)
+
+ipepresenter: $(OBJECTS)
+	g++ $+ -o $@ $(LIBS)
+
+clean:
+	rm $(OBJECTS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/canvasview.cpp	Mon Nov 08 22:42:06 2010 -0800
@@ -0,0 +1,118 @@
+// --------------------------------------------------------------------
+// CanvasView
+// --------------------------------------------------------------------
+/*
+
+    This file is based on ipeview, part of the extensible drawing editor Ipe.
+    Copyright (C) 1993-2009  Otfried Cheong
+    Copyright (C) 2010       Dmitriy Morozov
+
+    Ipe 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 3 of the License, or
+    (at your option) any later version.
+
+    As a special exception, you have permission to link Ipe with the
+    CGAL library and distribute executables, as long as you follow the
+    requirements of the Gnu General Public License in regard to all of
+    the software in the executable aside from CGAL.
+
+    Ipe 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 Ipe; if not, you can find it at
+    "http://www.gnu.org/copyleft/gpl.html", or write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "canvasview.h"
+#include "ipetool.h"
+
+using namespace ipe;
+using namespace ipeqt;
+
+// --------------------------------------------------------------------
+
+IpeAction::IpeAction(int cmd, const QString &text, const char *shortcut,
+		             QObject *parent): 
+    QAction(text, parent)
+{
+    iCommand = cmd;
+    connect(this, SIGNAL(triggered()), SLOT(forwardTrigger()));
+    connect(this, SIGNAL(triggered(int)), parent, SLOT(cmd(int)));
+    if (shortcut)
+        setShortcut(QKeySequence(shortcut));
+}
+
+void IpeAction::forwardTrigger()
+{
+    emit triggered(iCommand);
+}
+
+// --------------------------------------------------------------------
+
+CanvasView::CanvasView(Qt::WFlags f): 
+    QMainWindow(0, f)
+{
+    iDoc = 0;
+
+    iCanvas = new Canvas(this);
+    setCentralWidget(iCanvas);
+
+    iCanvas->setPretty(true);
+
+    iSnap.iSnap = 0; // Snap::ESnapGrid;
+    iSnap.iGridVisible = false;
+    iSnap.iGridSize = 8;
+    iSnap.iAngleSize = M_PI / 6.0;
+    iSnap.iSnapDistance = 10;
+    iSnap.iWithAxes = false;
+    iSnap.iOrigin = Vector::ZERO;
+    iSnap.iDir = 0;
+    iCanvas->setSnap(iSnap);
+    // iCanvas->setFifiVisible(true);
+}
+
+bool CanvasView::load(Document* doc, int iPageNo, int iViewNo)
+{
+    iDoc = doc;
+
+    // iDoc->page(iPageNo)->setSelect(0, EPrimarySelected);
+    iCanvas->setFontPool(iDoc->fontPool());
+    iCanvas->setPage(iDoc->page(iPageNo), iViewNo, iDoc->cascade());
+    iCanvas->setPan(Vector(300, 400));
+    iCanvas->update();
+
+    return true;
+}
+  
+void CanvasView::fitBox(const Rect &box)
+{
+    if (box.isEmpty())
+        return;
+
+    ipeDebug("canvas: %d x %d", iCanvas->width(), iCanvas->height());
+    double xfactor = box.width() > 0.0  ? (iCanvas->width() / box.width()) : 20.0;
+    double yfactor = box.height() > 0.0 ? (iCanvas->height() / box.height()) : 20.0;
+    double zoom = (xfactor > yfactor) ? yfactor : xfactor;
+    iCanvas->setPan(0.5 * (box.bottomLeft() + box.topRight()));
+    iCanvas->setZoom(zoom);
+    iCanvas->update();
+}
+
+void CanvasView::setView(int pageNo, int viewNo)
+{
+    iCanvas->setPage(iDoc->page(pageNo), viewNo, iDoc->cascade());
+    iCanvas->update();
+}
+
+void CanvasView::resizeEvent (QResizeEvent *event)
+{
+    fitBox(iDoc->cascade()->findLayout()->paper());
+}
+
+// --------------------------------------------------------------------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/canvasview.h	Mon Nov 08 22:42:06 2010 -0800
@@ -0,0 +1,87 @@
+// -*- C++ -*-
+// --------------------------------------------------------------------
+// CanvasView
+// --------------------------------------------------------------------
+/*
+
+    This file is based on ipeview, part of the extensible drawing editor Ipe.
+    Copyright (C) 1993-2009  Otfried Cheong
+    Copyright (C) 2010       Dmitriy Morozov
+
+    Ipe 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 3 of the License, or
+    (at your option) any later version.
+
+    As a special exception, you have permission to link Ipe with the
+    CGAL library and distribute executables, as long as you follow the
+    requirements of the Gnu General Public License in regard to all of
+    the software in the executable aside from CGAL.
+
+    Ipe 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 Ipe; if not, you can find it at
+    "http://www.gnu.org/copyleft/gpl.html", or write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef CANVASVIEW_H
+#define CANVASVIEW_H
+
+#include "ipeqtcanvas.h"
+
+#include <QMainWindow>
+#include <QAction>
+
+using namespace ipe;
+using namespace ipeqt;
+
+// --------------------------------------------------------------------
+
+class IpeAction: public QAction 
+{
+    Q_OBJECT
+
+    public:
+                    IpeAction(int cmd, const QString &text,
+                              const char *shortcut, QObject *parent);
+    
+    signals:
+        void        triggered(int cmd);
+    
+    private slots:
+        void        forwardTrigger();
+    
+    private:
+        int         iCommand;
+};
+
+// --------------------------------------------------------------------
+
+class CanvasView: public QMainWindow 
+{
+    Q_OBJECT
+
+    public:
+                    CanvasView(Qt::WFlags f=0);
+
+        void        setView(int pageNo, int ViewNo);
+        void        fitBox(const Rect &box);
+        bool        load(Document* doc, int iPageNo, int iViewNo);
+
+    protected:
+        void        resizeEvent (QResizeEvent *event);
+
+    private:
+        Document    *iDoc;
+        Canvas      *iCanvas;
+        Snap        iSnap;
+};
+
+// --------------------------------------------------------------------
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mainwindow.cpp	Mon Nov 08 22:42:06 2010 -0800
@@ -0,0 +1,360 @@
+// --------------------------------------------------------------------
+// MainWindow
+// --------------------------------------------------------------------
+/*
+
+    This file is based on ipeview, part of the extensible drawing editor Ipe.
+    Copyright (C) 1993-2009  Otfried Cheong
+    Copyright (C) 2010       Dmitriy Morozov
+
+    Ipe 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 3 of the License, or
+    (at your option) any later version.
+
+    As a special exception, you have permission to link Ipe with the
+    CGAL library and distribute executables, as long as you follow the
+    requirements of the Gnu General Public License in regard to all of
+    the software in the executable aside from CGAL.
+
+    Ipe 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 Ipe; if not, you can find it at
+    "http://www.gnu.org/copyleft/gpl.html", or write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "mainwindow.h"
+#include "canvasview.h"
+#include "ipetool.h"
+
+using namespace ipe;
+using namespace ipeqt;
+
+// --------------------------------------------------------------------
+
+MainWindow::
+MainWindow(CanvasView* cv, Qt::WFlags f): 
+    QMainWindow(0, f), canvas_view(cv)
+{
+    setupUi(this);
+
+    iDoc = 0;
+    page_dialog = 0;
+
+    // iFileMenu = menuBar()->addMenu(tr("&File"));
+    // iFileMenu->addAction(new IpeAction(EOpen, "Open", "Ctrl+O", this));
+    // iFileMenu->addAction(new IpeAction(EQuit, "Quit", "Ctrl+Q", this));
+    
+    iViewMenu = menuBar()->addMenu(tr("&View"));
+    iMoveMenu = menuBar()->addMenu(tr("&Move"));
+
+    iViewMenu->addAction(new IpeAction(EFullScreen, "Full screen", "F11", this));
+    iViewMenu->addAction(new IpeAction(EFitPage, "Fit page", ".", this));
+    iViewMenu->addAction(new IpeAction(EZoomIn, "Zoom in", "=", this));
+    iViewMenu->addAction(new IpeAction(EZoomOut, "Zoom out", "-", this));
+    iViewMenu->addAction(new IpeAction(ESetTime, "Set time", "", this));
+    iTimeCountdown = new IpeAction(ETimeCountdown, "Count down", "/", this);
+    iTimeCountdown->setCheckable(true);
+    iViewMenu->addAction(iTimeCountdown);
+    IpeAction* iToggleTime = new IpeAction(EToggleTimeCounting, "Count time", "T", this);
+    iToggleTime->setCheckable(true);
+    iViewMenu->addAction(iToggleTime);
+    iViewMenu->addAction(new IpeAction(EShowPresentation, "Show presentation", "", this));
+
+    iMoveMenu->addAction(new IpeAction(ENextView, "Next view", "PgDown", this));
+    iMoveMenu->addAction(new IpeAction(EPreviousView, "Previous view", "PgUp", this));
+    iMoveMenu->addAction(new IpeAction(EFirstView, "First view", "Home", this));
+    iMoveMenu->addAction(new IpeAction(ELastView, "Last view", "End", this));
+    iMoveMenu->addAction(new IpeAction(EJumpTo, "Jump to...", "J", this));
+
+    currentViewCanvas->setPretty(true);
+    nextViewCanvas->setPretty(true);
+
+    iSnap.iSnap = 0; // Snap::ESnapGrid;
+    iSnap.iGridVisible = false;
+    iSnap.iGridSize = 8;
+    iSnap.iAngleSize = M_PI / 6.0;
+    iSnap.iSnapDistance = 10;
+    iSnap.iWithAxes = false;
+    iSnap.iOrigin = Vector::ZERO;
+    iSnap.iDir = 0;
+    currentViewCanvas->setSnap(iSnap);
+    nextViewCanvas->setSnap(iSnap);
+    // currentViewCanvas->setFifiVisible(true);
+    // nextViewCanvas->setFifiVisible(true);
+
+    // connect(currentViewCanvas, SIGNAL(wheelMoved(int)), this, SLOT(wheelZoom(int)));
+    // connect(currentViewCanvas, SIGNAL(mouseAction(int)), this, SLOT(startPan(int)));
+  
+    connect(splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(splitterMoved(int,int)));
+    connect(splitter_2, SIGNAL(splitterMoved(int,int)), this, SLOT(splitterMoved(int,int)));
+
+    currentViewCanvas->setFocus();
+}
+
+void MainWindow::cmd(int c)
+{
+    ipeDebug("Command %d", c);
+    switch (c) 
+    {
+        case EOpen:
+            break;
+        case EQuit:
+            QApplication::exit();
+            break;
+        case EToggleTimeCounting:
+            timeLabel->toggleCounting();
+            break;
+        case EFullScreen:
+            canvas_view->setWindowState(canvas_view->windowState() ^ Qt::WindowFullScreen);
+            break;
+        case EFitPage:
+            fitBox(iDoc->cascade()->findLayout()->paper(), currentViewCanvas);
+            fitBox(iDoc->cascade()->findLayout()->paper(), nextViewCanvas);
+            canvas_view->fitBox(iDoc->cascade()->findLayout()->paper());
+            break;
+        // case EZoomIn:
+        //     zoom(+1);
+        //     break;
+        // case EZoomOut:
+        //     zoom(-1);
+        //     break;
+        case ENextView:
+            nextView(+1);
+            break;
+        case EPreviousView:
+            nextView(-1);
+            break;
+        case EFirstView:
+            firstView();
+            break;
+        case ELastView:
+            lastView();
+            break;
+        case ETimeCountdown:
+            timeLabel->toggleCountdown();
+            break;
+        case ESetTime:
+            timeLabel->setTime();
+            break;
+        case EJumpTo:
+        {
+            page_dialog->reset();
+            page_dialog->exec();
+            if (page_dialog->isOk())
+            {
+                iPageNo = page_dialog->page();
+                iViewNo = 0;
+                setView();
+            }
+            break;
+        }
+        case EShowPresentation:
+            canvas_view->show();
+            break;
+        default:
+            // unknown action
+            return;
+    }
+}
+
+bool MainWindow::load(const char *fname)
+{
+    Document *doc = Document::loadWithErrorReport(fname);
+    if (!doc)
+        return false;
+    
+    iFileName = String(fname);
+    doc->runLatex();
+    
+    delete iDoc;
+    iDoc = doc;
+    
+    iPageNo = 0;
+    iViewNo = 0;
+    
+    // iDoc->page(iPageNo)->setSelect(0, EPrimarySelected);
+    currentViewCanvas->setFontPool(iDoc->fontPool());
+    nextViewCanvas->setFontPool(iDoc->fontPool());
+    
+    currentViewCanvas->setPage(iDoc->page(iPageNo), iViewNo, iDoc->cascade());
+    currentViewCanvas->setPan(Vector(300, 400));
+    currentViewCanvas->update();
+    setNextViewCanvas();
+    
+    canvas_view->load(doc, iPageNo, iViewNo);
+     
+    delete page_dialog;
+    page_dialog = new PageDialog(iDoc);
+    
+    updateLabel();
+    return true;
+}
+  
+void MainWindow::updateLabel()
+{
+    String s = iFileName;
+    if (iFileName.rfind('/') >= 0)
+        s = iFileName.substr(iFileName.rfind('/') + 1);
+    if (iDoc->countTotalViews() > 1) {
+        ipe::StringStream ss(s);
+        ss << " (" << iPageNo + 1 << "-" << iViewNo + 1 << ")";
+    }
+    setWindowTitle(QIpe(s));
+}
+
+void MainWindow::fitBox(const Rect &box, Canvas* canvas)
+{
+    if (box.isEmpty())
+        return;
+    ipeDebug("canvas: %d x %d", canvas->width(), canvas->height());
+    double xfactor = box.width() > 0.0  ? (canvas->width() / box.width()) : 20.0;
+    double yfactor = box.height() > 0.0 ? (canvas->height() / box.height()) : 20.0;
+    double zoom = (xfactor > yfactor) ? yfactor : xfactor;
+    canvas->setPan(0.5 * (box.bottomLeft() + box.topRight()));
+    canvas->setZoom(zoom);
+    canvas->update();
+}
+
+// void MainWindow::zoom(int delta)
+// {
+//     double zoom = currentViewCanvas->zoom();
+//     while (delta > 0) {zoom *= 1.3; --delta;}
+//     while (delta < 0) {zoom /= 1.3; ++delta;}
+//     currentViewCanvas->setZoom(zoom);
+//     currentViewCanvas->update();
+// }
+
+// void MainWindow::wheelZoom(int degrees)
+// {
+//     if (degrees > 0)
+//         zoom(+1);
+//     else
+//         zoom(-1);
+// }
+
+// void MainWindow::startPan(int button)
+// {
+//     Tool *tool = new PanTool(currentViewCanvas, iDoc->page(iPageNo), iViewNo);
+//     currentViewCanvas->setTool(tool);
+// }
+
+std::pair<int,int> MainWindow::nextPageView(int delta)
+{
+    const Page *page = iDoc->page(iPageNo);
+    int iPageNo = this->iPageNo;
+    int iViewNo = this->iViewNo;
+    if (0 <= iViewNo + delta && iViewNo + delta < page->countViews()) 
+    {
+        iViewNo += delta;
+    } else if (0 <= iPageNo + delta && iPageNo + delta < iDoc->countPages()) 
+    {
+        iPageNo += delta;
+        if (delta > 0)
+            iViewNo = 0;
+        else
+            iViewNo = iDoc->page(iPageNo)->countViews() - 1;
+    } 
+    
+    return std::make_pair(iPageNo, iViewNo);
+}
+
+void MainWindow::setNextViewCanvas()
+{
+    std::pair<int,int> page_view = nextPageView(1);
+    int iPageNo = page_view.first;
+    int iViewNo = page_view.second;
+    nextViewCanvas->setPage(iDoc->page(iPageNo), iViewNo, iDoc->cascade());
+    nextViewCanvas->update();
+}
+
+void MainWindow::nextView(int delta)
+{
+    std::pair<int,int> page_view = nextPageView(delta);
+    iPageNo = page_view.first;
+    iViewNo = page_view.second;
+    setView();
+}
+
+void MainWindow::firstView()
+{
+    iPageNo = 0;
+    iViewNo = 0;
+    setView();
+}
+
+void MainWindow::lastView()
+{
+    iPageNo = iDoc->countPages() - 1;
+    iViewNo = iDoc->page(iPageNo)->countViews() - 1;
+    setView();
+}
+
+void MainWindow::resizeEvent (QResizeEvent *event)
+{
+    cmd(EFitPage);
+}
+  
+void MainWindow::splitterMoved(int pos, int index)
+{
+    cmd(EFitPage);
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+    canvas_view->close();
+    QMainWindow::closeEvent(event);
+}
+
+void MainWindow::setView()
+{
+    currentViewCanvas->setPage(iDoc->page(iPageNo), iViewNo, iDoc->cascade());
+    currentViewCanvas->update();
+    updateLabel();
+
+    canvas_view->setView(iPageNo, iViewNo);
+    
+    setNextViewCanvas();
+}
+
+// --------------------------------------------------------------------
+
+static void usage()
+{
+    fprintf(stderr, "Usage: ipepresenter <filename>\n");
+    exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+    Platform::initLib(IPELIB_VERSION);
+    QApplication a(argc, argv);
+    setlocale(LC_NUMERIC, "C");
+    
+    if (argc != 2)
+        usage();
+    
+    const char *load = argv[1];
+    
+    CanvasView *cv = new CanvasView();
+    MainWindow *mw = new MainWindow(cv);
+    
+    if (!mw->load(load))
+        exit(2);
+    
+    cv->show();
+    mw->show();
+    
+    mw->cmd(MainWindow::EFitPage);
+    
+    QObject::connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));
+    return a.exec();
+}
+
+// --------------------------------------------------------------------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mainwindow.h	Mon Nov 08 22:42:06 2010 -0800
@@ -0,0 +1,111 @@
+// -*- C++ -*-
+// --------------------------------------------------------------------
+// MainWindow
+// --------------------------------------------------------------------
+/*
+
+    This file is based on ipeview, part of the extensible drawing editor Ipe.
+    Copyright (C) 1993-2009  Otfried Cheong
+    Copyright (C) 2010       Dmitriy Morozov
+
+    Ipe 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 3 of the License, or
+    (at your option) any later version.
+
+    As a special exception, you have permission to link Ipe with the
+    CGAL library and distribute executables, as long as you follow the
+    requirements of the Gnu General Public License in regard to all of
+    the software in the executable aside from CGAL.
+
+    Ipe 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 Ipe; if not, you can find it at
+    "http://www.gnu.org/copyleft/gpl.html", or write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "ipeqtcanvas.h"
+#include "canvasview.h"
+#include "pagedialog.h"
+#include "ui_mainwindow.h"
+
+#include <QMainWindow>
+#include <QAction>
+#include <QTimer>
+#include <QTime>
+
+using namespace ipe;
+using namespace ipeqt;
+
+
+// --------------------------------------------------------------------
+
+class MainWindow: public QMainWindow, Ui::MainWindow 
+{
+    Q_OBJECT
+
+    public:
+        enum TAction 
+        { 
+            EOpen, EQuit, EFullScreen, EFitPage,
+            EZoomIn, EZoomOut, ENextView, EPreviousView, EFirstView, ELastView,
+            ENumActions, EToggleTimeCounting, ETimeCountdown, ESetTime,
+            EJumpTo, EShowPresentation
+        };
+
+    public:
+        MainWindow(CanvasView* cv, Qt::WFlags f=0);
+
+        // void zoom(int delta);
+        void nextView(int delta);
+        void firstView();
+        void lastView();
+        void fitBox(const Rect &box, Canvas* canvas);
+        void updateLabel();
+        bool load(const char* fn);
+
+    public slots:
+        void cmd(int cmd);
+        // void wheelZoom(int degrees);
+        // void startPan(int button);
+        std::pair<int,int> nextPageView(int delta);
+        void setNextViewCanvas();
+        void closeEvent(QCloseEvent *event);
+
+    protected:
+        void resizeEvent (QResizeEvent *event);
+
+    private:
+        void setView();
+
+    private:
+        Document *iDoc;
+        Snap iSnap;
+
+        // QMenu* iFileMenu;
+        QMenu* iViewMenu;
+        QMenu* iMoveMenu;
+        IpeAction* iTimeCountdown;
+
+        String iFileName;
+        int iPageNo;
+        int iViewNo;
+        
+        CanvasView* canvas_view;
+        PageDialog* page_dialog;
+
+    private slots:
+        void splitterMoved(int pos, int index);
+};
+
+// --------------------------------------------------------------------
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mainwindow.ui	Mon Nov 08 22:42:06 2010 -0800
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1008</width>
+    <height>620</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QHBoxLayout" name="horizontalLayout">
+    <item>
+     <widget class="QSplitter" name="splitter_2">
+      <property name="baseSize">
+       <size>
+        <width>0</width>
+        <height>0</height>
+       </size>
+      </property>
+      <property name="orientation">
+       <enum>Qt::Horizontal</enum>
+      </property>
+      <widget class="QWidget" name="layoutWidget">
+       <layout class="QVBoxLayout" name="CurrentViewLayout">
+        <item>
+         <widget class="QLabel" name="currentViewLabel">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>Current View:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="Canvas" name="currentViewCanvas" native="true">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="minimumSize">
+           <size>
+            <width>600</width>
+            <height>0</height>
+           </size>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QSplitter" name="splitter">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <widget class="QWidget" name="layoutWidget">
+        <layout class="QVBoxLayout" name="verticalLayout_2">
+         <item>
+          <widget class="TimeLabel" name="timeLabel">
+           <property name="font">
+            <font>
+             <pointsize>28</pointsize>
+            </font>
+           </property>
+           <property name="text">
+            <string>00:00:00</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLabel" name="label_4">
+           <property name="text">
+            <string>Notes:</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPlainTextEdit" name="plainTextEdit"/>
+         </item>
+        </layout>
+       </widget>
+       <widget class="QWidget" name="layoutWidget">
+        <layout class="QVBoxLayout" name="verticalLayout">
+         <item>
+          <widget class="QLabel" name="label_2">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Next view:</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="Canvas" name="nextViewCanvas" native="true">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>1008</width>
+     <height>23</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>Canvas</class>
+   <extends>QWidget</extends>
+   <header>ipeqtcanvas.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>TimeLabel</class>
+   <extends>QLabel</extends>
+   <header>timelabel.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pagedialog.cpp	Mon Nov 08 22:42:06 2010 -0800
@@ -0,0 +1,63 @@
+#include <QtGui>
+
+#include "pagedialog.h"
+
+PageDialog::PageDialog(Document* d): iDoc(d)
+{
+    // buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+    // connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
+    // connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+
+    QGridLayout *mainLayout = new QGridLayout;
+    fillPages(mainLayout);
+    setLayout(mainLayout);
+    setWindowTitle(tr("Select page"));
+    reset();
+}
+
+void PageDialog::fillPages(QGridLayout* layout)
+{
+    for (unsigned i = 0; i < iDoc->countPages(); ++i)
+    {
+        Canvas* c = new Canvas(this);
+        c->setFontPool(iDoc->fontPool());
+        c->setPretty(true);
+        c->setPage(iDoc->page(i), 0, iDoc->cascade());
+        connect(c, SIGNAL(mouseAction(int)), this, SLOT(pageChosen(int)));
+        canvases.push_back(c);
+        layout->addWidget(c, i/5, i%5);
+    }
+}
+
+void PageDialog::fitBox(const Rect &box, Canvas* canvas)
+{
+    if (box.isEmpty())
+        return;
+    ipeDebug("canvas: %d x %d", canvas->width(), canvas->height());
+    double xfactor = box.width() > 0.0  ? (canvas->width() / box.width()) : 20.0;
+    double yfactor = box.height() > 0.0 ? (canvas->height() / box.height()) : 20.0;
+    double zoom = (xfactor > yfactor) ? yfactor : xfactor;
+    canvas->setPan(0.5 * (box.bottomLeft() + box.topRight()));
+    canvas->setZoom(zoom);
+    canvas->update();
+}
+
+void PageDialog::pageChosen(int button)
+{
+    for (unsigned i = 0; i < canvases.size(); ++i)
+        if (canvases[i] == QObject::sender())
+        {
+            ok = true;
+            pageChoice = i;
+        }
+    accept();
+}
+
+void PageDialog::resizeEvent (QResizeEvent *event)
+{
+    for (unsigned i = 0; i < canvases.size(); ++i)
+    {
+        fitBox(iDoc->cascade()->findLayout()->paper(), canvases[i]);
+        canvases[i]->update();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pagedialog.h	Mon Nov 08 22:42:06 2010 -0800
@@ -0,0 +1,43 @@
+#ifndef PAGEDIALOG_H
+#define PAGEDIALOG_H
+
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include "ipeqtcanvas.h"
+#include <vector>
+
+using namespace ipe;
+using namespace ipeqt;
+
+
+class PageDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+            PageDialog(Document* d);
+
+    bool    isOk()     { return ok; }
+    int     page()     { return pageChoice; }
+    void    reset()    { ok = false; }
+
+public slots:
+    void    pageChosen(int button);
+
+protected:
+    void    resizeEvent (QResizeEvent *event);
+
+private:
+    void    fillPages(QGridLayout* layout);
+    void    fitBox(const Rect &box, Canvas* canvas);
+
+    std::vector<Canvas*>    canvases;
+    Document*               iDoc;
+    QDialogButtonBox*       buttonBox;
+
+    bool                    ok;
+    int                     pageChoice;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/timelabel.cpp	Mon Nov 08 22:42:06 2010 -0800
@@ -0,0 +1,56 @@
+#include "timelabel.h"
+#include <QInputDialog>
+#include <iostream>
+
+TimeLabel::TimeLabel(QWidget* parent): 
+    QLabel(parent), time(0,0,0), counting(false), countingDown(false)
+{
+    timer = new QTimer(this);
+    connect(timer, SIGNAL(timeout()), this, SLOT(countTime()));
+    timer->start(1000);       // one second
+}
+
+void TimeLabel::countTime()
+{
+    if (!counting) return;
+    
+    if (countingDown && !(time.hour() == 0 && time.minute() == 0 && time.second() == 0))
+        time = time.addSecs(-1);
+    
+    if (!countingDown)
+        time = time.addSecs(1);
+
+    setText(time.toString("hh:mm:ss"));
+}
+        
+void TimeLabel::mouseDoubleClickEvent(QMouseEvent* event)
+{
+    setTime();
+}
+
+void TimeLabel::setTime()
+{
+    bool counting_state = counting;
+    counting = false;
+
+    bool ok;
+    int minutes = QInputDialog::getInt(this, tr("Minutes"),
+                                       tr("Minutes to count down:"), 
+                                       0, 0, 10000, 1, &ok);
+    if (ok && minutes >= 0)
+        time.setHMS(minutes/60,minutes%60,0);
+
+    counting = counting_state;        
+    
+    setText(time.toString("hh:mm:ss"));
+}
+
+void TimeLabel::toggleCounting()
+{
+    counting = !counting;
+}
+
+void TimeLabel::toggleCountdown()
+{
+    countingDown = !countingDown;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/timelabel.h	Mon Nov 08 22:42:06 2010 -0800
@@ -0,0 +1,30 @@
+#ifndef TIMELABEL_H
+#define TIMELABEL_H
+
+#include <QLabel>
+#include <QTime>
+#include <QTimer>
+
+class TimeLabel: public QLabel
+{
+    Q_OBJECT
+
+    public:
+                TimeLabel(QWidget* parent);
+        void    mouseDoubleClickEvent(QMouseEvent* event);
+        void    toggleCounting();
+        void    toggleCountdown();
+        void    setTime();
+
+    private:
+        QTimer* timer;
+        QTime   time;
+        bool    counting;
+        bool    countingDown;
+
+    private slots:
+        void    countTime();
+        
+};
+
+#endif