diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,15 @@
+CHANGELOG for Haskell Pdf Presenter
+========================================
+
+0.2.2
+	* Fixes to cabal meta-data
+	* Make sure all version numbers are up to date
+	* Spell checking and proof reading
+
+0.2.1
+	* Added support for closing files
+	* Added better time parsing
+	* Added CHANGELOG
+
+0.2
+	* Initial release
diff --git a/HaskellPdfPresenter.glade b/HaskellPdfPresenter.glade
new file mode 100644
--- /dev/null
+++ b/HaskellPdfPresenter.glade
@@ -0,0 +1,1736 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkAction" id="dummyAction"/>
+  <object class="GtkAboutDialog" id="aboutDialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <property name="program_name">Haskell Pdf Presenter</property>
+    <property name="version">Version 0.2.2</property>
+    <property name="copyright" translatable="yes">Copyright 2012-2013, Michael D. Adams</property>
+    <property name="website">http://michaeldadams.org</property>
+    <property name="website_label" translatable="yes">http://michaeldadams.org</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="aboutdialog-vbox1">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="aboutdialog-action_area1">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkTable" id="keybindingsTable">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="n_rows">18</property>
+            <property name="n_columns">5</property>
+            <property name="column_spacing">4</property>
+            <property name="row_spacing">4</property>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Keybindings</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="1.2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="right_attach">5</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label17">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Ctrl-G</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">8</property>
+                <property name="bottom_attach">9</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label16">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Go to slide:</property>
+              </object>
+              <packing>
+                <property name="top_attach">8</property>
+                <property name="bottom_attach">9</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label15">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">End</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">7</property>
+                <property name="bottom_attach">8</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label13">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Last slide:</property>
+              </object>
+              <packing>
+                <property name="top_attach">7</property>
+                <property name="bottom_attach">8</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label14">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Home</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">6</property>
+                <property name="bottom_attach">7</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label12">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">First slide:</property>
+              </object>
+              <packing>
+                <property name="top_attach">6</property>
+                <property name="bottom_attach">7</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label11">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Backspace, Page up,
+Left arrow, Up arrow</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">4</property>
+                <property name="bottom_attach">6</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label10">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Prev slide:</property>
+              </object>
+              <packing>
+                <property name="top_attach">4</property>
+                <property name="bottom_attach">6</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Space, Enter, Page down,
+Right arrow, Down arrow</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">4</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label20">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Toggle overview:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">12</property>
+                <property name="bottom_attach">13</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label21">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Tab</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">12</property>
+                <property name="bottom_attach">13</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label7">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">F, F11, Alt-Enter, Ctrl-L
+Escape (exit only)</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">8</property>
+                <property name="bottom_attach">9</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label5">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Ctrl-Q</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">3</property>
+                <property name="bottom_attach">4</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label6">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Toggle fullscreen:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">7</property>
+                <property name="bottom_attach">9</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label4">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Quit:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">3</property>
+                <property name="bottom_attach">4</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label18">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Video mute:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">7</property>
+                <property name="bottom_attach">8</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label19">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">B (black), W (white)</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">7</property>
+                <property name="bottom_attach">8</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label29">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Move divider left:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">14</property>
+                <property name="bottom_attach">15</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label30">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Move divider right:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">15</property>
+                <property name="bottom_attach">16</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label31">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">[, Shift-[ (small adjustments)</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">14</property>
+                <property name="bottom_attach">15</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label32">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">], Shift-] (small adjustments)</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">15</property>
+                <property name="bottom_attach">16</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label33">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Divider to auto-stops:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">13</property>
+                <property name="bottom_attach">14</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label34">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">=</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">13</property>
+                <property name="bottom_attach">14</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label35">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Toggle pause:</property>
+              </object>
+              <packing>
+                <property name="top_attach">12</property>
+                <property name="bottom_attach">13</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label36">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Toggle modes:</property>
+              </object>
+              <packing>
+                <property name="top_attach">14</property>
+                <property name="bottom_attach">15</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label37">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">P, Pause</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">12</property>
+                <property name="bottom_attach">13</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label38">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">C</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">14</property>
+                <property name="bottom_attach">15</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label39">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Adjustment dialog:</property>
+              </object>
+              <packing>
+                <property name="top_attach">16</property>
+                <property name="bottom_attach">17</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label41">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Ctrl-C</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">16</property>
+                <property name="bottom_attach">17</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Next slide:</property>
+              </object>
+              <packing>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">4</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label43">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Slide control</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label22">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Presenter view</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">11</property>
+                <property name="bottom_attach">12</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label23">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Reload file:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">5</property>
+                <property name="bottom_attach">6</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label24">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Ctrl-R</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">5</property>
+                <property name="bottom_attach">6</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label25">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Open file:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">4</property>
+                <property name="bottom_attach">5</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label26">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Ctrl-O</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">4</property>
+                <property name="bottom_attach">5</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label27">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Help:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label28">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">H, ?, Ctrl-H, Ctrl-?</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label8">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">System commands</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label9">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Clock/timer control</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="top_attach">11</property>
+                <property name="bottom_attach">12</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label40">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">	</property>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+                <property name="top_attach">10</property>
+                <property name="bottom_attach">11</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label42">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Reset timer:</property>
+              </object>
+              <packing>
+                <property name="top_attach">15</property>
+                <property name="bottom_attach">16</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label44">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Shift-C</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">15</property>
+                <property name="bottom_attach">16</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label45">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Slide history:</property>
+              </object>
+              <packing>
+                <property name="top_attach">9</property>
+                <property name="bottom_attach">10</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label46">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Alt-Left arrow, Alt-Right arrow
+(w/ Shift = move by 10)</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">9</property>
+                <property name="bottom_attach">10</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label47">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Move windows
+beween monitors:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">9</property>
+                <property name="bottom_attach">10</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label48">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">S, Shift-S (reverse)
+(w/ Ctrl = move only presenter window)
+(w/ Alt = move only audience window)</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">9</property>
+                <property name="bottom_attach">10</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label49">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Toggle stop:</property>
+              </object>
+              <packing>
+                <property name="top_attach">13</property>
+                <property name="bottom_attach">14</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label50">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Shift-P</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">13</property>
+                <property name="bottom_attach">14</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label51">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Close file:</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">6</property>
+                <property name="bottom_attach">7</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label52">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Shift-Q</property>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="top_attach">6</property>
+                <property name="bottom_attach">7</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkWindow" id="audienceWindow">
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">Audience Window - Haskell Pdf Presenter</property>
+    <property name="default_width">600</property>
+    <property name="default_height">450</property>
+    <child>
+      <placeholder/>
+    </child>
+  </object>
+  <object class="GtkAdjustment" id="pageDialogAdjustment">
+    <property name="lower">1</property>
+    <property name="upper">1</property>
+    <property name="value">1</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkDialog" id="pageDialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Goto Slide - Haskell Pdf Presenter</property>
+    <property name="modal">True</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox2">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area2">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="pageDialogCancelButton">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="pageDialogOkButton">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHBox" id="pageDialogBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="spacing">2</property>
+            <child>
+              <object class="GtkLabel" id="pageDialogLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Go to slide (1-1):</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSpinButton" id="pageDialogSpinButton">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">•</property>
+                <property name="activates_default">True</property>
+                <property name="xalign">1</property>
+                <property name="adjustment">pageDialogAdjustment</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">pageDialogCancelButton</action-widget>
+      <action-widget response="-5">pageDialogOkButton</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkWindow" id="presenterWindow">
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">Presenter Window - Haskell Pdf Presenter</property>
+    <property name="default_width">800</property>
+    <property name="default_height">400</property>
+    <child>
+      <object class="GtkVPaned" id="presenter.paned">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <child>
+          <object class="GtkNotebook" id="presenter.notebook">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="show_tabs">False</property>
+            <property name="show_border">False</property>
+            <child>
+              <object class="GtkHPaned" id="preview.paned">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+            </child>
+            <child type="tab">
+              <placeholder/>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow" id="thumbnails.scrolled">
+                <property name="can_focus">True</property>
+                <child>
+                  <object class="GtkLayout" id="thumbnails.layout">
+                    <property name="can_focus">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child type="tab">
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child type="tab">
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="resize">True</property>
+            <property name="shrink">True</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkTable" id="metadataTable">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="n_rows">3</property>
+            <property name="n_columns">7</property>
+            <child>
+              <object class="GtkEventBox" id="videoMuteEventBox">
+                <property name="can_focus">False</property>
+                <property name="visible_window">False</property>
+                <child>
+                  <object class="GtkLabel" id="videoMuteStatusLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">☼</property>
+                    <property name="justify">center</property>
+                    <attributes>
+                      <attribute name="font-desc" value="&lt;Enter Value&gt; 30"/>
+                      <attribute name="foreground" value="#ffffffffffff"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options"/>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEventBox" id="noteModeEventBox">
+                <property name="can_focus">False</property>
+                <property name="visible_window">False</property>
+                <child>
+                  <object class="GtkLabel" id="noteModeLabel">
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">◧</property>
+                    <property name="justify">center</property>
+                    <attributes>
+                      <attribute name="font-desc" value="&lt;Enter Value&gt; 30"/>
+                      <attribute name="foreground" value="#000000000000"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options"/>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEventBox" id="recordingStatusEventBox">
+                <property name="can_focus">False</property>
+                <property name="visible_window">False</property>
+                <child>
+                  <object class="GtkLabel" id="recordingStatusLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="font-desc" value="&lt;Enter Value&gt; 30"/>
+                      <attribute name="foreground" value="#ffffffffffff"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">3</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="x_options"/>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEventBox" id="timerStatusEventBox">
+                <property name="can_focus">False</property>
+                <property name="visible_window">False</property>
+                <child>
+                  <object class="GtkLabel" id="timerStatusLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">▮▮</property>
+                    <property name="justify">center</property>
+                    <property name="width_chars">6</property>
+                    <attributes>
+                      <attribute name="font-desc" value="&lt;Enter Value&gt; 30"/>
+                      <attribute name="foreground" value="#ffffffffffff"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="x_options"/>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEventBox" id="slideEventBox">
+                <property name="can_focus">False</property>
+                <property name="visible_window">False</property>
+                <child>
+                  <object class="GtkLabel" id="slideLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">1</property>
+                    <property name="yalign">1</property>
+                    <attributes>
+                      <attribute name="font-desc" value="&lt;Enter Value&gt; 60"/>
+                      <attribute name="foreground" value="#ffffffffffff"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">6</property>
+                <property name="right_attach">7</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">3</property>
+                <property name="x_options"/>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFixed" id="metadata.rightHSpacer">
+                <property name="width_request">60</property>
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="left_attach">5</property>
+                <property name="right_attach">6</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="x_options"/>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFixed" id="metadata.leftHSpacer">
+                <property name="width_request">60</property>
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="x_options"/>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFixed" id="metadata.leftVExpander">
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="right_attach">4</property>
+                <property name="x_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFixed" id="metadata.rightVExpander">
+                <property name="width_request">60</property>
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="left_attach">5</property>
+                <property name="right_attach">7</property>
+                <property name="x_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkNotebook" id="timeNotebook">
+                <property name="can_focus">False</property>
+                <property name="show_tabs">False</property>
+                <property name="show_border">False</property>
+                <child>
+                  <object class="GtkEventBox" id="timeEventBox">
+                    <property name="width_request">1</property>
+                    <property name="height_request">1</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="visible_window">False</property>
+                    <child>
+                      <object class="GtkLabel" id="timeLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <attributes>
+                          <attribute name="font-desc" value="&lt;Enter Value&gt; 60"/>
+                          <attribute name="foreground" value="#ffffffffffff"/>
+                        </attributes>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="tab">
+                  <placeholder/>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="hiddenTimeLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="single_line_mode">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                    <property name="tab_fill">False</property>
+                  </packing>
+                </child>
+                <child type="tab">
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child type="tab">
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="right_attach">5</property>
+                <property name="bottom_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="alignment1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="yalign">1</property>
+                <property name="yscale">0</property>
+                <child>
+                  <object class="GtkProgressBar" id="renderingProgressbar">
+                    <property name="height_request">4</property>
+                    <property name="sensitive">False</property>
+                    <property name="can_focus">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">6</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="resize">False</property>
+            <property name="shrink">False</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkDialog" id="timerDialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Clock Settings - Haskell Pdf Presenter</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox4">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area4">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="timerDialogCancelButton">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="timerDialogOkButton">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="timerDialogBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="spacing">10</property>
+            <child>
+              <object class="GtkTable" id="timerDialogTable1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="column_spacing">5</property>
+                <child>
+                  <object class="GtkLabel" id="elapsedTimeLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">1</property>
+                    <property name="label" translatable="yes">_Elapsed Time:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">elapsedTimeEntry</property>
+                    <property name="single_line_mode">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="elapsedTimeEntry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">•</property>
+                    <property name="activates_default">True</property>
+                    <property name="width_chars">8</property>
+                    <property name="xalign">1</property>
+                    <property name="invisible_char_set">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="remainingTimeLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">1</property>
+                    <property name="label" translatable="yes">_Remaining Time:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">remainingTimeEntry</property>
+                    <property name="single_line_mode">True</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="remainingTimeEntry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">•</property>
+                    <property name="activates_default">True</property>
+                    <property name="width_chars">8</property>
+                    <property name="xalign">1</property>
+                    <property name="invisible_char_set">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="startTimeLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">1</property>
+                    <property name="label" translatable="yes">S_tart Time:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">startTimeEntry</property>
+                    <property name="single_line_mode">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="warnTimeLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">1</property>
+                    <property name="label" translatable="yes">_Warn Time:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">warnTimeEntry</property>
+                    <property name="single_line_mode">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">3</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="endTimeLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">1</property>
+                    <property name="label" translatable="yes">E_nd Time:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">endTimeEntry</property>
+                    <property name="single_line_mode">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">3</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="startTimeEntry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">•</property>
+                    <property name="activates_default">True</property>
+                    <property name="width_chars">8</property>
+                    <property name="xalign">1</property>
+                    <property name="invisible_char_set">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">4</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="warnTimeEntry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">•</property>
+                    <property name="activates_default">True</property>
+                    <property name="width_chars">8</property>
+                    <property name="xalign">1</property>
+                    <property name="invisible_char_set">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">4</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="endTimeEntry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">•</property>
+                    <property name="activates_default">True</property>
+                    <property name="width_chars">8</property>
+                    <property name="xalign">1</property>
+                    <property name="invisible_char_set">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">4</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="timerDialogSpacer">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">	</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkTable" id="timerDialogTable2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="column_spacing">5</property>
+                <child>
+                  <object class="GtkLabel" id="timerModeLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">1</property>
+                    <property name="label" translatable="yes">Timer Mode:</property>
+                    <property name="single_line_mode">True</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="displayModeLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">1</property>
+                    <property name="label" translatable="yes">Display Mode:</property>
+                    <property name="single_line_mode">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkRadioButton" id="remainingTimeRadio">
+                    <property name="label" translatable="yes">Re_maining Time (⌛)</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="related_action">dummyAction</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="xalign">0</property>
+                    <property name="active">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkRadioButton" id="12HourClockRadio">
+                    <property name="label" translatable="yes">_12 Hour Clock</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="related_action">dummyAction</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="xalign">0</property>
+                    <property name="active">True</property>
+                    <property name="draw_indicator">True</property>
+                    <property name="group">remainingTimeRadio</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <object class="GtkRadioButton" id="elapsedTimeRadio">
+                    <property name="label" translatable="yes">E_lapsed Time (⌚)</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="related_action">dummyAction</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="xalign">0</property>
+                    <property name="active">True</property>
+                    <property name="draw_indicator">True</property>
+                    <property name="group">remainingTimeRadio</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkRadioButton" id="24HourClockRadio">
+                    <property name="label" translatable="yes">_24 Hour Clock</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="related_action">dummyAction</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="xalign">0</property>
+                    <property name="active">True</property>
+                    <property name="draw_indicator">True</property>
+                    <property name="group">remainingTimeRadio</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="timerDialogTimerModeBox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="spacing">5</property>
+                    <child>
+                      <object class="GtkRadioButton" id="runningRadio">
+                        <property name="label" translatable="yes">R_unning (▶)</property>
+                        <property name="use_action_appearance">False</property>
+                        <property name="related_action">dummyAction</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="xalign">0</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="pausedRadio">
+                        <property name="label" translatable="yes">_Paused (▮▮)</property>
+                        <property name="use_action_appearance">False</property>
+                        <property name="related_action">dummyAction</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="xalign">0</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">runningRadio</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="stoppedRadio">
+                        <property name="label" translatable="yes">_Stopped (■)</property>
+                        <property name="use_action_appearance">False</property>
+                        <property name="related_action">dummyAction</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="xalign">0</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">runningRadio</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">3</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">timerDialogCancelButton</action-widget>
+      <action-widget response="-5">timerDialogOkButton</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkWindow" id="window1">
+    <property name="can_focus">False</property>
+    <child>
+      <placeholder/>
+    </child>
+  </object>
+</interface>
diff --git a/HaskellPdfPresenter.hs b/HaskellPdfPresenter.hs
new file mode 100644
--- /dev/null
+++ b/HaskellPdfPresenter.hs
@@ -0,0 +1,978 @@
+{-# LANGUAGE TemplateHaskell, PatternGuards, TupleSections #-}
+import Codec.Compression.Zlib.Raw
+import Control.Exception (catch)
+import Control.Monad
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BSL
+import qualified Data.ByteString.Unsafe as BSU
+import Data.Char (toLower, isSpace)
+import Data.IORef
+import Data.List (sort, sortBy, nub)
+import qualified Data.Map as Map
+import Data.Ord (comparing)
+import Data.Time.LocalTime (getCurrentTimeZone, utcToLocalTime)
+import Data.Time.Clock (getCurrentTime)
+import qualified Data.Time.Format (formatTime)
+import Foreign.Ptr (castPtr)
+import Graphics.Rendering.Cairo
+import Graphics.UI.Gtk
+import Graphics.UI.Gtk.Gdk.GC
+import Graphics.UI.Gtk.Poppler.Document
+import Graphics.UI.Gtk.Poppler.Page
+import Language.Haskell.TH (Exp(LitE), Lit(StringL), runIO)
+import Language.Haskell.TH.Syntax (addDependentFile)
+import System.Console.GetOpt
+import System.Directory (canonicalizePath)
+import System.Exit (exitSuccess, exitFailure)
+import System.FilePath (takeFileName)
+import System.Glib
+import System.Locale (defaultTimeLocale)
+import Text.Printf (printf)
+
+data TimerState =
+  Counting Integer{-starting microseconds-} Integer{-ending microseconds-} |
+  Stopped Bool{-soft pause-} Integer{-elapsed microseconds-} Integer{-remaining microseconds-}
+data VideoMuteState = MuteOff | MuteBlack | MuteWhite deriving (Eq)
+data ClockState = RemainingTime | ElapsedTime | WallTime12 | WallTime24
+
+data State = State
+-- Settings that are used only at program initialization
+ { initSlide :: IORef Int
+ , initPreviewPercentage :: IORef Int
+ , initPresenterMonitor :: IORef Int
+ , initAudienceMonitor :: IORef Int
+ , initTimerMode :: IORef (TimerState -> IO TimerState)
+
+-- Settable on command line
+ , startTime :: IORef Integer{-microseconds-}
+ , warningTime :: IORef Integer{-microseconds-}
+ , endTime :: IORef Integer{-microseconds-}
+ , documentURL :: IORef (Maybe String)
+ , compression :: IORef Int
+
+-- Dynamic State
+ , timer :: IORef TimerState -- 'Maybe' for "dont" display or haven't started counting yet? Or as a seperate flag?
+ , clock :: IORef ClockState
+ , videoMute :: IORef VideoMuteState
+ , fullscreen :: IORef Bool
+ , mouseTimeout :: IORef Integer
+ , history :: IORef ([Int]{-back-}, [Int]{-forward-})
+
+-- UI State
+ , builder :: Builder
+
+-- Render State
+ , views :: IORef (Map.Map Widget (Int{-width-}, Int{-height-}))
+ , document :: IORef (Maybe Document)
+ , pages :: IORef (Map.Map (Int{-page number-}, Int{-width-}, Int{-height-})
+                          (BSL.ByteString, Int{-width-}, Int{-height-}, Int{-stride-}))
+ , pageAdjustment :: Adjustment
+}
+
+header = "Haskell Pdf Presenter, version 0.2.2, <http://michaeldadams.org/projects/haskell-pdf-presenter>\nUsage: hpdfp [OPTION...] file"
+options = [
+   Option "h?" ["help"] (NoArg (const $ putStr (usageInfo header options) >> exitSuccess)) "Display usage message"
+ , Option "s" ["slide"] (argOption initSlide "slide" maybeRead "INT") "Initial slide number (default 1)"
+ , Option "t" ["start-time", "starting-time"] (argOption startTime "start-time" parseTime "TIME")
+              "Start time (default 10:00)"
+ , Option "w" ["warn-time", "warning-time"] (argOption warningTime "warning-time" parseTime "TIME")
+              "Warning time (default 5:00)"
+ , Option "e" ["end-time", "ending-time"] (argOption endTime "end-time" parseTime "TIME")
+              "End time (default 0:00)"
+ , Option "f" ["fullscreen"] (setOption fullscreen True) "Start in full screen mode"
+ , Option "p" ["preview-percentage"] (argOption initPreviewPercentage "preview-percentage" maybeRead "INT")
+              "Initial preview splitter percentage (default 50)"
+ , Option "" ["presenter-monitor"] (argOption initPresenterMonitor "presenter-monitor" maybeRead "INT")
+              "Initial monitor for presenter window (default 0)"
+ , Option "" ["audience-monitor"] (argOption initAudienceMonitor "audience-monitor" maybeRead "INT")
+              "Initial monitor for audience window (default 1)"
+ , Option "" ["video-mute"] (enumOption videoMute "mute" [("black", MuteBlack), ("white", MuteWhite), ("off", MuteOff)] "MODE")
+              "Initial video mute mode: \"black\", \"white\" or \"off\" (default)"
+ , Option "" ["clock-mode"] (enumOption clock "clock-mode" [("remaining", RemainingTime), ("elapsed", ElapsedTime), ("12hour", WallTime12), ("24hour", WallTime24)] "MODE")
+              "Initial clock mode: \"remaining\" (default), \"elapsed\", \"12hour\" or \"24hour\""
+ , Option "" ["timer-mode"] (enumOption initTimerMode "timer-mode" (
+                  map (,startTimer) ["play", "playing", "running", "start", "started"] ++
+                  map (,pauseTimer) ["pause", "paused"] ++ map (,stopTimer) ["stop", "stopped"]) "MODE")
+              "Initial timer mode: \"play\", \"pause\" (default) or \"stop\""
+ , Option "" ["compression"] (argOption compression "compression" (maybeRead >=> maybeRange 0 9) "[0-9]")
+              "Cache compression level: 0 is none, 1 is fastest (default), 9 is best"
+ ]
+
+----------------
+-- Utils and constants
+----------------
+
+-- TODO: cleanup
+heightFromWidth w = w * 3 `div` 4
+numColumns = 4
+textSize = 60
+mouseTimeoutDelay = 3 * 1000 * 1000 {-microseconds-}
+appName = "Haskell Pdf Presenter"
+
+-- Color constants
+red = Color 0xffff 0x0000 0x0000
+purple = Color 0x8888 0x3333 0xffff
+white = Color 0xffff 0xffff 0xffff
+black = Color 0x0000 0x0000 0x0000
+
+incMod x n = (x + 1) `mod` n
+decMod x n = (x - 1) `mod` n
+
+-- Like "read" but returns "Nothing" instead of throwing an exception
+maybeRead :: (Read a) => String -> Maybe a
+maybeRead str | [(n, "")] <- reads str = Just n
+              | otherwise = Nothing
+
+-- Like "Just" but returns "Nothing" if a value is outside a range
+maybeRange :: Int -> Int -> Int -> Maybe Int
+maybeRange lo hi x | lo <= x && x <= hi = Just x
+                   | otherwise = Nothing
+
+-- A list of the top level windows
+-- NOTE: Order of windows effects which window is on top in fullscreen
+mainWindows state = mapM ($ state) [presenterWindow, audienceWindow]
+--  mapM (builderGetObject (builder state) castToWindow) [presenterWindow, audienceWindow]
+presenterWindow state = builderGetObject (builder state) castToWindow "presenterWindow"
+audienceWindow state = builderGetObject (builder state) castToWindow "audienceWindow"
+
+-- Set an option to a given value when a command line flag is set
+setOption opt val = NoArg $ \state -> writeIORef (opt state) val
+
+-- Set an option based on the argument given to a command line
+argOption :: (State -> IORef a) -> String -> (String -> Maybe a) -> String -> ArgDescr (State -> IO ())
+argOption opt name f = ReqArg (\val state -> case f val of
+    Nothing -> putStrLn ("ERROR: Invalid argument to option \""++name++"\": "++show val) >>
+               putStr ("\n" ++ usageInfo header options) >> exitFailure
+    Just val' -> writeIORef (opt state) val')
+
+-- Set an option from a list of valid option values
+enumOption :: (State -> IORef a) -> String -> [(String, a)] -> String -> ArgDescr (State -> IO ())
+enumOption opt name optList = argOption opt name (`lookup` optList)
+
+----------------
+-- Main Program
+----------------
+
+main :: IO ()
+main = do
+  -- Set initialize application and get program arguments
+  setApplicationName appName
+  args <- initGUI
+
+  -- Initialize shared state
+  state <- return State
+    `ap` newIORef 1 {-init slide-}
+    `ap` newIORef 50 {-init preview percentage-}
+    `ap` newIORef 0 {-init presenter monitor-}
+    `ap` newIORef 1 {-init audience monitor-}
+    `ap` newIORef pauseTimer
+
+    `ap` newIORef (10 * 60 * 1000 * 1000) {-startTime-}
+    `ap` newIORef (5 * 60 * 1000 * 1000) {-warningTime-}
+    `ap` newIORef (0 * 60 * 1000 * 1000) {-20 * 60 * 1000 * 1000-} {-endTime-}
+    `ap` newIORef Nothing {-file uri-}
+    `ap` newIORef 1 {-compression-}
+
+    `ap` newIORef (error "internal error: undefined timer state")
+    `ap` newIORef RemainingTime
+    `ap` newIORef MuteOff {-videoMute-}
+    `ap` newIORef False {-fullscreen-}
+    `ap` newIORef 0 {-mouseTimeout-}
+    `ap` newIORef ([], []) {-history-}
+
+    `ap` builderNew
+
+    `ap` newIORef Map.empty {-views-}
+    `ap` newIORef Nothing {-document-}
+    `ap` newIORef Map.empty {-pages-}
+    `ap` adjustmentNew 0 0 0 1 10 1 {-pageAdjustment-}
+
+  -- Parse command line options and call guiMain
+  case getOpt Permute options args of
+    (opts, [], []) -> mapM_ ($ state) opts >> guiMain state
+    (opts, [file], []) -> do
+      mapM_ ($ state) opts
+      file' <- canonicalizePath file
+      postGUIAsync (void $ openDoc state ("file://"++file'))
+      guiMain state
+    (_, [_,_], []) -> putStrLn "Error: Multiple files on command line" >> putStr (usageInfo header options)
+    (_, _, errors) -> putStrLn (concat errors) >> putStr (usageInfo header options)
+
+guiMain :: State -> IO ()
+guiMain state = do
+  -- Load and setup the GUI
+  builderAddFromString (builder state)
+    $(let f = "HaskellPdfPresenter.glade" in addDependentFile f >> liftM (LitE . StringL) (runIO $ readFile f))
+
+  -- Finish setting up the state and configure gui to match state
+  pageAdjustment state `set` [adjustmentUpper :=> liftM fromIntegral (readIORef (initSlide state)),
+                              adjustmentValue :=> liftM fromIntegral (readIORef (initSlide state))]
+  modifyTimerState state (const $ liftM (Stopped True 0) (readIORef (startTime state)))
+  modifyTimerState state =<< readIORef (initTimerMode state)
+  modifyVideoMute state return
+
+  -- Connect the widgets to their events
+  -- * Audience Window
+  do window <- audienceWindow state
+     view <- makeView state id (postDrawVideoMute state)
+     window `containerAdd` view
+
+  -- * Presenter window
+  -- ** Views
+  -- *** Previews
+  do paned <- builderGetObject (builder state) castToPaned "preview.paned"
+     view1 <- makeView state id postDrawNone
+     view2 <- makeView state (+1) postDrawNone
+     panedPack1 paned view1 True True
+     panedPack2 paned view2 True True
+
+  -- *** Thumbnails
+  do oldWidth <- newIORef 0
+     layout <- builderGetObject (builder state) castToLayout "thumbnails.layout"
+     layout `onSizeAllocate` (\(Graphics.UI.Gtk.Rectangle _ _ newWidth _) -> do
+       oldWidth' <- readIORef oldWidth
+       writeIORef oldWidth newWidth
+       when (oldWidth' /= newWidth) $ recomputeThumbnails state newWidth)
+     pageAdjustment state `onAdjChanged` (readIORef oldWidth >>= recomputeThumbnails state)
+
+     -- Set a handler that scrolls to keep the current slide visible
+     adjustment <- scrolledWindowGetVAdjustment =<<
+                   builderGetObject (builder state) castToScrolledWindow "thumbnails.scrolled"
+     let update = do p <- liftM round $ pageAdjustment state `get` adjustmentValue
+                     let row = (p-1) `div` numColumns
+                     height <- liftM (heightFromWidth . (`div` numColumns)) $ readIORef oldWidth
+                     adjustmentClampPage adjustment (fromIntegral $ row*height) (fromIntegral $ row*height+height)
+     pageAdjustment state `onValueChanged` update
+     pageAdjustment state `onAdjChanged` update
+
+  -- ** Meta-data
+  -- *** Current time
+  do eventBox <- builderGetObject (builder state) castToEventBox "timeEventBox"
+     eventBox `on` buttonPressEvent $ tryEvent $
+       do DoubleClick <- eventClick; liftIO $ timerDialog state
+     timeoutAdd (displayTime state >> return True) 100{-milliseconds-}
+       -- ^ Really this should be run on redraw, but because the
+       -- computation triggers more redraws, we have it here.  This
+       -- may result in a 1/10 second lag before the label resizes.
+
+  -- *** Current slide number
+  do eventBox <- builderGetObject (builder state) castToEventBox "slideEventBox"
+     eventBox `on` buttonPressEvent $ tryEvent $
+       do DoubleClick <- eventClick; liftIO $ gotoSlideDialog state
+     slideNum <- builderGetObject (builder state) castToLabel "slideLabel"
+     let update = do p <- liftM round $ pageAdjustment state `get` adjustmentValue
+                     n <- liftM round $ pageAdjustment state `get` adjustmentUpper
+                     l <- liftM round $ pageAdjustment state `get` adjustmentLower
+                     slideNum `set` [
+                       labelText := if (l :: Integer) == 0 then "" else show (p :: Integer) ++ "/" ++ show (n :: Integer),
+                       labelAttributes := [AttrSize 0 (negate 1) textSize, -- TODO: we have to do this to get the right font???
+                                           AttrForeground 0 (negate 1) white]]
+     pageAdjustment state `onValueChanged` update
+     pageAdjustment state `onAdjChanged` update
+
+  -- Setup the top-level windows
+  do let setupWindow window = do
+           -- Make the window fullscreen if needed
+           f <- readIORef (fullscreen state)
+           when f $ windowFullscreen window
+           -- Make background black
+           widgetModifyBg window StateNormal (Color 0 0 0)
+           -- Add event handlers
+           window `onDestroy` mainQuit
+           window `on` keyPressEvent $ do
+             mods <- eventModifier
+             name <- eventKeyName
+             b <- liftIO $ handleKey state mods (map toLower name)
+             liftIO $ mapM_ widgetQueueDraw =<< mainWindows state
+             return b
+           -- Redraw the window when we switch slides.  Most (though not all)
+           -- of it needs to be redrawn so we redraw the entire window.
+           pageAdjustment state `onValueChanged` widgetQueueDraw window
+           pageAdjustment state `onAdjChanged` widgetQueueDraw window
+           -- Show the window
+           widgetShowAll window
+     mapM_ setupWindow =<< mainWindows state
+
+  -- * Put windows on correct monitors
+  join $ return moveWindow `ap` presenterWindow state `ap` readIORef (initPresenterMonitor state)
+  join $ return moveWindow `ap` audienceWindow state `ap` readIORef (initAudienceMonitor state)
+
+  -- * Adjust the preview splitter (must be done after windows are shown)
+  do paned <- builderGetObject (builder state) castToPaned "preview.paned"
+     maxPos <- paned `get` panedMaxPosition
+     percentage <- readIORef (initPreviewPercentage state)
+     paned `set` [panedPosition := maxPos * percentage `div` 100]
+
+  -- * Setup about dialog
+  do dialog <- builderGetObject (builder state) castToAboutDialog "aboutDialog"
+     dialog `on` response $ const $ widgetHideAll dialog
+     dialog `on` deleteEvent $ liftIO (widgetHideAll dialog) >> return True
+
+  -- * Make sure the screen saver doesn't start by moving the cursor.
+  --   Since we move the mouse by zero pixels, this shouldn't effect the user.
+  --   Also set up the handler for hiding the mouse after a delay.
+  do w <- audienceWindow state
+     dw <- widgetGetDrawWindow w
+     blankCursor <- cursorNew BlankCursor
+     timeoutAdd (do -- Move the cursor by zero pixels
+                    display <- screenGetDisplay =<< windowGetScreen w
+                    (screen, _, x, y) <- displayGetPointer display
+                    displayWarpPointer display screen x y
+                    -- Hide the mouse if needed.
+                    mouseTimeout' <- readIORef (mouseTimeout state)
+                    currTime <- getMicroseconds
+                    fullscreen' <- readIORef (fullscreen state)
+                    when (fullscreen' && currTime > mouseTimeout') $
+                      dw `drawWindowSetCursor` Just blankCursor
+                    return True) (5000{-milliseconds-})
+     w `widgetAddEvents` [PointerMotionMask]
+     oldCoordRef <- newIORef (0, 0)
+     let update = do dw `drawWindowSetCursor` Nothing
+                     writeIORef (mouseTimeout state) =<< liftM (+ mouseTimeoutDelay) getMicroseconds
+     w `on` enterNotifyEvent $ liftIO update >> return False
+     -- Reset the timeout if the user moves the mouse
+     w `on` motionNotifyEvent $ do
+       coord <- eventRootCoordinates
+       oldCoord <- liftIO $ readIORef oldCoordRef
+       liftIO $ writeIORef oldCoordRef coord
+       when (oldCoord /= coord) $ liftIO update
+       return False
+
+  -- Schedule rendering, and start main loop
+  startRenderThread state =<< builderGetObject (builder state) castToProgressBar "renderingProgressbar"
+  mainGUI
+
+-- Recompute thumbnails when sizes need to change
+recomputeThumbnails state newWidth = do
+  layout <- builderGetObject (builder state) castToLayout "thumbnails.layout"
+  -- Remove the old thumbnails
+  containerForeach layout (containerRemove layout)
+  -- Figure out how big the thumbnails and thumbnail panel should be
+  numPages <- pageAdjustment state `get` adjustmentUpper
+  let width = newWidth `div` numColumns
+      height = heightFromWidth width
+      numRows = round numPages `div` numColumns + 1
+  layoutSetSize layout newWidth (height*numRows)
+  -- Add thumbnails for the new sizes
+  sequence_ [ do
+    let page = numColumns*row+col+1
+    view <- makeView state (const page) (postDrawBorder page state)
+    widgetSetSizeRequest view width height
+    layoutPut layout view (col*width) (row*height)
+    view `widgetAddEvents` [ButtonPressMask, ButtonReleaseMask]
+    view `on` buttonReleaseEvent $ tryEvent $ liftIO $ gotoPage state (const page)
+    --view `set` [widgetTooltipText := Just ("Slide " ++ show page)] -- TODO: this triggers a bug where window drawing does not update
+    | row <- [0..numRows-1], col <- [0..numColumns-1]]
+  widgetShowAll layout
+
+-- Apply "f" to the current page number
+gotoPage state f = do
+  oldPage <- liftM round $ pageAdjustment state `get` adjustmentValue
+  pageAdjustment state `set` [adjustmentValue := fromIntegral ((f :: Int -> Int) oldPage)]
+  newPage <- liftM round $ pageAdjustment state `get` adjustmentValue
+  -- Update the history if needed
+  when (oldPage /= newPage) $ do
+    (back, _) <- readIORef (history state)
+    writeIORef (history state) (oldPage : back, [])
+
+------------------------
+-- User Event handling
+------------------------
+
+-- Handler for key presses from the user
+handleKey :: State -> [Modifier] -> String -> IO Bool
+handleKey state [Control] "q" = mainQuit >> return True
+handleKey state _ "h" = builderGetObject (builder state) castToAboutDialog "aboutDialog" >>= widgetShowAll >> return True
+handleKey state _ "question" = builderGetObject (builder state) castToAboutDialog "aboutDialog" >>= widgetShowAll >> return True
+
+-- Switching slides
+handleKey state [] key | key `elem` ["left", "up", "page_up", "backspace"] =
+  gotoPage state (+(-1)) >> modifyTimerState state unpauseTimer >> return True
+handleKey state [] key | key `elem` ["right", "down", "page_down", "right", "space", "return"] =
+  gotoPage state (+1) >> modifyTimerState state unpauseTimer >> return True
+handleKey state [Shift] key | key `elem` ["left", "up", "page_up", "backspace"] =
+  gotoPage state (+(-10)) >> modifyTimerState state unpauseTimer >> return True
+handleKey state [Shift] key | key `elem` ["right", "down", "page_down", "right", "space", "return"] =
+  gotoPage state (+10) >> modifyTimerState state unpauseTimer >> return True
+handleKey state [] "home" = gotoPage state (const 0) >> return True
+handleKey state [] "end" =
+  do p <- pageAdjustment state `get` adjustmentUpper; gotoPage state (const (round p)); return True
+handleKey state [Control] "g" = gotoSlideDialog state >> return True
+handleKey state [Alt] "left" = historyBack state >> return True
+handleKey state [] "xf86back" = historyBack state >> return True
+handleKey state [Alt] "right" = historyForward state >> return True
+handleKey state [] "xf86forward" = historyForward state >> return True
+
+-- Clock/timer control
+handleKey state [] "p" = modifyTimerState state togglePauseTimer >> return True
+handleKey state [Shift] "p" = modifyTimerState state toggleStopTimer >> return True
+handleKey state [Control] "p" = modifyTimerState state toggleStopTimer >> return True
+handleKey state [] "pause" = modifyTimerState state togglePauseTimer >> return True
+handleKey state [] "c" = modifyClockState state (\c -> case c of
+                              RemainingTime -> ElapsedTime
+                              ElapsedTime -> WallTime12
+                              WallTime12 -> WallTime24
+                              WallTime24 -> RemainingTime) >> return True
+handleKey state [Shift] "c" = modifyTimerState state (const $ liftM (Stopped True 0) (readIORef (startTime state))) >> return True
+handleKey state [Control] "c" = timerDialog state >> return True
+
+-- Video mute
+handleKey state [] "b" = modifyVideoMute state (\b -> return $ if b == MuteBlack then MuteOff else MuteBlack) >> return True
+handleKey state [] "w" = modifyVideoMute state (\b -> return $ if b == MuteWhite then MuteOff else MuteWhite) >> return True
+
+-- Presenter screen
+handleKey state [] "tab" =
+  builderGetObject (builder state) castToNotebook "presenter.notebook" >>=
+    (`set` [notebookCurrentPage :~ (`mod` 2) . (+ 1)]) >> return True
+handleKey state [] "bracketleft" =
+  builderGetObject (builder state) castToPaned "preview.paned" >>= (`set` [panedPosition :~ max 0 . (+(-20))]) >> return True
+handleKey state [] "bracketright" =
+  builderGetObject (builder state) castToPaned "preview.paned" >>= (`set` [panedPosition :~ (+20)]) >> return True
+handleKey state [Shift] "braceleft" =
+  builderGetObject (builder state) castToPaned "preview.paned" >>= (`set` [panedPosition :~ max 0 . (+(-1))]) >> return True
+handleKey state [Shift] "braceright" =
+  builderGetObject (builder state) castToPaned "preview.paned" >>= (`set` [panedPosition :~ (+1)]) >> return True
+handleKey state [] "equal" = panedStops state >> return True
+
+-- Files
+handleKey state [Shift] "q" = setDocument state makeTitle 0 0 Nothing Nothing where
+  makeTitle t = t ++ " Window - " ++ appName
+handleKey state [Control] "r" = readIORef (documentURL state) >>=
+  maybe (errorDialog "Cannot open file: No file selected" >> return False) (openDoc state) >> return True
+handleKey state [Control] "o" = openFileDialog state >> return True
+
+-- Window control
+handleKey state [] "escape" = do f <- readIORef (fullscreen state); when f (toggleFullScreen state); return True
+handleKey state [] "f" = toggleFullScreen state >> return True
+handleKey state [] "f11" = toggleFullScreen state >> return True
+handleKey state [Alt] "return" = toggleFullScreen state >> return True
+handleKey state [Control] "l" = toggleFullScreen state >> return True
+handleKey state [] "s" = changeMonitors state f where
+  f mP mA n = if mA `incMod` n == mP then (mP `incMod` n, mP `incMod` n) else (mP, mA `incMod` n)
+handleKey state [Shift] "s" = changeMonitors state f where
+  f mP mA n = if mA == mP then (mP `decMod` n, mP `decMod` n `decMod` n) else (mP, mA `decMod` n)
+handleKey state [Control] "s" = changeMonitors state f where f mP mA n = (mP `incMod` n, mA)
+handleKey state [Shift,Control] "s" = changeMonitors state f where f mP mA n = (mP `decMod` n, mA)
+handleKey state [Alt] "s" = changeMonitors state f where f mP mA n = (mP, mA `incMod` n)
+handleKey state [Shift,Alt] "s" = changeMonitors state f where f mP mA n = (mP, mA `decMod` n)
+
+handleKey _ _ name | name `elem` [ -- Ignore modifier keys
+  "shift_l", "shift_r", "control_l", "control_r", "alt_l", "alt_r",
+  "super_l", "super_r", "caps_lock", "menu", "xf86wakeup"]
+  = return True
+handleKey state mods name = putStrLn ("Unknown key \""++name++"\" with mods "++show mods) >> return False
+
+-- Move back one slide in the history
+historyBack state = do
+  (back, forward) <- readIORef (history state)
+  currPage <- liftM round $ pageAdjustment state `get` adjustmentValue
+  case back of
+    [] -> return ()
+    prevPage : back -> do pageAdjustment state `set` [adjustmentValue := fromIntegral prevPage]
+                          writeIORef (history state) (back, currPage : forward)
+
+-- Move forward one slide in the history
+historyForward state = do
+  (back, forward) <- readIORef (history state)
+  currPage <- liftM round $ pageAdjustment state `get` adjustmentValue
+  case forward of
+    [] -> return ()
+    nextPage : forward -> do pageAdjustment state `set` [adjustmentValue := fromIntegral nextPage]
+                             writeIORef (history state) (currPage : back, forward)
+
+-- Toggle whether we are in fullscreen mode
+toggleFullScreen state = do
+  f <- readIORef (fullscreen state)
+  mapM_ (if f then windowUnfullscreen else windowFullscreen) =<< mainWindows state
+  writeIORef (fullscreen state) $ not f
+
+-- Move the divider on the preview window to the next logical division point.
+-- These points are:
+--   - All the way to the left
+--   - Perfectly fit the right preview
+--   - 1/3 of the way from the left
+--   - Centered
+--   - 1/3 of the way from the right
+--   - Perfectly fit the left preview
+--   - All the way to the right
+panedStops state = do
+  -- Get the current position and size of the divider
+  paned <- builderGetObject (builder state) castToPaned "preview.paned"
+  maxPos <- liftM fromIntegral $ paned `get` panedMaxPosition
+  oldPos <- liftM fromIntegral $ paned `get` panedPosition
+  (_, panedHeight) <- widgetGetSize paned
+  -- Get the page number of the current and last pages
+  p <- liftM round $ pageAdjustment state `get` adjustmentValue
+  pLast <- liftM round $ pageAdjustment state `get` adjustmentUpper
+  -- Compute where do divide to get perfect fits and store them in "stops"
+  document <- readIORef (document state)
+  stops <- case document of
+    Nothing -> return [] -- If there is no document, then there are no stops
+    Just document -> do
+      (pageWidth1, pageHeight1) <- pageGetSize =<< documentGetPage document (p - 1)
+      (pageWidth2, pageHeight2) <- pageGetSize =<< documentGetPage document (p `min` pLast - 1)
+      return [round (fromIntegral panedHeight * pageWidth1 / pageHeight1),
+              maxPos - round (fromIntegral panedHeight * pageWidth2 / pageHeight2)]
+  -- Store the other divisions in stops'
+  let stops' = [maxPos `div` 3, maxPos `div` 2, maxPos * 2 `div` 3, maxPos]
+  -- Move to the next stop greater than the current stop.
+  paned `set` [panedPosition := case sort $ filter (> oldPos) $ stops ++ stops' of
+                                  [] -> 0 -- If no more stops, go fully left
+                                  (x:_) -> x]
+
+-- Move the windows between the available monitors according to the
+-- provided f function.  For forwards movement, the order is that we
+-- always move the audience view first, but if the audience view would
+-- move to the same monitor as the presenter view then we advance the
+-- presenter view and also move the audience view to that monitor.
+changeMonitors state f = do
+  wP <- presenterWindow state
+  wA <- audienceWindow state
+  screenP <- wP `get` windowScreen
+  screenA <- wA `get` windowScreen
+  if screenP /= screenA
+    then do errorDialog ("Cannot move windows as the presenter and audience windows are on different screens.\n\n"++
+                         "Contact the developer if you want support for this.")
+            return True
+    else do
+      dwP <- widgetGetDrawWindow wP
+      dwA <- widgetGetDrawWindow wA
+      mP <- screenGetMonitorAtWindow screenP dwP
+      mA <- screenGetMonitorAtWindow screenA dwA
+      -- There are render glitches if we move the windows while in full screen
+      -- so we have to move out of full screen before moving.
+      -- It would be nice if we could move back into full screen after moving
+      -- but that also triggers render errors.  I don't know why.
+      readIORef (fullscreen state) >>= flip when (toggleFullScreen state)
+      n <- screenGetNMonitors screenP
+      let (mP', mA') = f mP mA n
+      moveWindow wP mP'
+      moveWindow wA mA'
+      return True
+
+-- Move a window to a given monitor (mod the number of monitors) if it is not already there
+moveWindow w m = do
+  screen <- w `get` windowScreen
+  dw <- widgetGetDrawWindow w
+  m' <- screenGetMonitorAtWindow screen dw
+  n <- screenGetNMonitors screen
+  when (m' /= m `mod` n) $ do
+    Rectangle x y _ _ <- screenGetMonitorGeometry screen (m `mod` n)
+    windowMove w x y
+
+----------------
+-- Dialogs
+----------------
+
+timerDialog state = do
+  -- Get dialog and text fields from GUI builder
+  dialog <- builderGetObject (builder state) castToDialog "timerDialog"
+  [remainingTimeEntry, elapsedTimeEntry, startTimeEntry, warningTimeEntry, endTimeEntry] <-
+    mapM (builderGetObject (builder state) castToEntry)
+    ["remainingTimeEntry", "elapsedTimeEntry", "startTimeEntry", "warnTimeEntry", "endTimeEntry"]
+  [running, paused, stopped] <-
+    mapM (builderGetObject (builder state) castToRadioButton) ["runningRadio", "pausedRadio", "stoppedRadio"]
+  [remainingTimeRadio, elapsedTimeRadio, h12ClockRadio, h24ClockRadio] <-
+    mapM (builderGetObject (builder state) castToRadioButton)
+    ["remainingTimeRadio", "elapsedTimeRadio", "12HourClockRadio", "24HourClockRadio"]
+
+  -- Populate the GUI elements based on the state
+  clock' <- readIORef (clock state)
+  (case clock' of RemainingTime -> remainingTimeRadio; ElapsedTime -> elapsedTimeRadio;
+                  WallTime12 -> h12ClockRadio; WallTime24 -> h24ClockRadio) `set` [toggleButtonActive := True]
+
+  timer' <- readIORef (timer state)
+  (case timer' of Stopped False _ _ -> stopped; Stopped True _ _ -> paused;
+                  Counting {} -> running) `set` [toggleButtonActive := True]
+
+  Stopped True timeElapsed timeRemaining <- pauseTimer =<< readIORef (timer state)
+    -- ^ Get the amount of time remaining, don't actually pause anything
+  remainingTimeEntry `set` [entryText := formatTime timeRemaining]
+  elapsedTimeEntry `set` [entryText := formatTime (timeElapsed+1000*1000-1{-microseconds-})]
+    -- ^ Compensate for rounding to we keep elapsed + remaining = total
+
+  startTimeEntry `set` [entryText :=> liftM formatTime (readIORef (startTime state))]
+  warningTimeEntry `set` [entryText :=> liftM formatTime (readIORef (warningTime state))]
+  endTimeEntry `set` [entryText :=> liftM formatTime (readIORef (endTime state))]
+
+  -- Set the default keyboard focus and display the dialog
+  widgetGrabFocus remainingTimeEntry
+  loopDialog dialog $ do -- NOTE: The "do" block runs when the user clicks "Okay"
+    -- Get the values of the test fields
+    [remainingTime'', elapsedTime'', startTime'', warningTime'', endTime''] <-
+        mapM (`get` entryText) [remainingTimeEntry, elapsedTimeEntry, startTimeEntry, warningTimeEntry, endTimeEntry]
+    -- Parse the time fields and report errors if needed
+    case (parseTime remainingTime'', parseTime elapsedTime'', parseTime startTime'',
+          parseTime warningTime'', parseTime endTime'') of
+      (Nothing, _, _, _, _) -> errorDialog ("Error parsing remaining time: "++remainingTime'') >> return False
+      (_, Nothing, _, _, _) -> errorDialog ("Error parsing elapsed time: "++elapsedTime'') >> return False
+      (_, _, Nothing, _, _) -> errorDialog ("Error parsing start time: "++startTime'') >> return False
+      (_, _, _, Nothing, _) -> errorDialog ("Error parsing warning time: "++warningTime'') >> return False
+      (_, _, _, _, Nothing) -> errorDialog ("Error parsing end time: "++warningTime'') >> return False
+      (Just remain, Just elapsed, Just start, Just warn, Just end) -> do
+        -- Save the parsed time value to the state
+        writeIORef (startTime state) start
+        writeIORef (warningTime state) warn
+        writeIORef (endTime state) end
+        -- Set the timer state based the selected radio button
+        do [r,p,s] <- mapM (`get` toggleButtonActive) [running, paused, stopped]
+           when r $ modifyTimerState state (const $ return $ Stopped True elapsed remain) >>
+                    modifyTimerState state startTimer
+           when p $ modifyTimerState state (const $ return $ Stopped True elapsed remain)
+           when s $ modifyTimerState state (const $ return $ Stopped False elapsed remain)
+        -- Set the clock mode based on the selected radio button
+        do [r,e,h12,h24] <- mapM (`get` toggleButtonActive) [
+                             remainingTimeRadio, elapsedTimeRadio, h12ClockRadio, h24ClockRadio]
+           modifyClockState state (const $ if r then RemainingTime 
+                                      else if e then ElapsedTime
+                                      else if h12 then WallTime12
+                                      else if h24 then WallTime24
+                                      else error "INTERNAL ERROR: No clock radio active")
+        return True
+  -- Close the dialog
+  widgetHide dialog
+
+gotoSlideDialog state = do
+  -- Get dialog and text fields from GUI builder
+  dialog <- builderGetObject (builder state) castToDialog "pageDialog"
+  entry <- builderGetObject (builder state) castToSpinButton "pageDialogSpinButton"
+  adjustment <- builderGetObject (builder state) castToAdjustment "pageDialogAdjustment"
+  label <- builderGetObject (builder state) castToLabel "pageDialogLabel"
+
+  -- Set the dialog spinner and label to correspond to the current and max page number of the document
+  pageNum <- pageAdjustment state `get` adjustmentValue
+  pageMax <- pageAdjustment state `get` adjustmentUpper
+  adjustment `set` [adjustmentValue := pageNum, adjustmentUpper := pageMax]
+  label `set` [labelText := "Go to slide (1-" ++ show (round pageMax :: Integer) ++ "):"]
+
+  -- Show the dialog
+  widgetShowAll dialog
+  widgetGrabFocus entry
+  r <- dialogRun dialog
+
+  -- When the dialog closes, get the spinner value and set the current page
+  value <- entry `get` spinButtonValue
+  when (r == ResponseOk) $ gotoPage state (const (round value))
+  widgetHide dialog
+
+-- Open and run a modal file selection dialog and set load the PDF document that is selected.
+openFileDialog state = do
+  dialog <- fileChooserDialogNew (Just $ "Open - " ++ appName) Nothing FileChooserActionOpen
+            [(stockOpen, ResponseOk), (stockCancel, ResponseCancel)]
+  maybe (return ()) (void . fileChooserSetURI dialog) =<< readIORef (documentURL state)
+  loopDialog dialog (openDoc state . head =<< fileChooserGetURIs dialog)
+  widgetDestroy dialog
+  return True
+
+-- Set the video mute state and the text of the video mute status label
+modifyVideoMute state f = do
+  mute <- f =<< readIORef (videoMute state)
+  builderGetObject (builder state) castToLabel "videoMuteStatusLabel" >>= (`set` case mute of
+    MuteOff -> [labelText := "\x2600", -- U+2600 = Black sun with rays (solid center)
+                labelAttributes := [AttrSize 0 (negate 1) 30, AttrForeground 0 (negate 1) black]]
+    MuteWhite -> [labelText := "\x2600", -- U+2600 = Black sun with rays (solid center)
+                  labelAttributes := [AttrSize 0 (negate 1) 30, AttrForeground 0 (negate 1) white]]
+    MuteBlack -> [labelText := "\x263C", -- U+263C = White sun with rays (hollow center)
+                  labelAttributes := [AttrSize 0 (negate 1) 30, AttrForeground 0 (negate 1) white]])
+  writeIORef (videoMute state) mute
+
+-- Open and run a modal error dialog with a textual message.
+errorDialog msg = do
+  dialog <- messageDialogNew Nothing [DialogModal, DialogDestroyWithParent] MessageError ButtonsClose msg
+  dialogRun dialog
+  widgetDestroy dialog
+
+-- Keep running a dialog until the user clicks "Okay" and "m" returns "True"
+loopDialog d m = do
+  r <- dialogRun d
+  when (r == ResponseOk) $ do
+    m' <- m
+    unless m' $ loopDialog d m
+
+------------------------
+-- Views
+------------------------
+
+-- Make a widget that displays a slide.
+--   - offset: computes the slide to display from the current slide number
+--   - postDraw: extra drawing that should be done at the end
+makeView :: State -> (Int -> Int) -> (DrawWindow -> GC -> DrawingArea -> EventM EExpose ()) -> IO DrawingArea
+makeView state offset postDraw = do
+  area <- drawingAreaNew
+  widgetModifyBg area StateNormal black
+  area `on` exposeEvent $ tryEvent $ drawView state offset postDraw area
+  area `on` sizeAllocate $ \(Graphics.UI.Gtk.Rectangle _ _ width height) ->
+    liftIO $ modifyIORef (views state) (Map.insert (castToWidget area) (width, height))
+  area `on` unrealize $
+    liftIO $ modifyIORef (views state) (Map.delete (castToWidget area))
+  return area
+
+-- Event handler for drawing a view
+--   - offset: computes the slide to display from the current slide number
+--   - postDraw: extra drawing that should be done at the end
+--   - area: the widget that needs to be redrawn
+drawView state offset postDraw area = do
+  -- Get the maximum and current slide number
+  n <- liftIO $ liftM round $ pageAdjustment state `get` adjustmentUpper
+  p <- liftIO $ liftM (offset . round) $ pageAdjustment state `get` adjustmentValue
+  -- Don't render if not in range.  Needed b/c the preview panel may
+  -- go out of range when on the last slide.
+  when (p <= n && p >= 1) $ do
+    drawWindow <- eventWindow
+    (w, h) <- liftIO $ widgetGetSize area
+    cache' <- liftIO $ readIORef (pages state)
+    gc <- liftIO $ gcNew drawWindow
+    case Map.lookup (p, w, h) cache' of
+      -- If we haven't rendered the slide yet, then use placeholder text
+      Nothing -> do
+        pc <- liftIO $ widgetGetPangoContext area
+        doc <- liftIO $ readIORef (document state)
+        layout <- liftIO $ layoutText pc $ case doc of
+          Nothing -> "" -- No document so no text needed
+          Just _  -> "Rendering slide "++show p++" of "++show n++"..."
+        liftIO $ layoutSetAttributes layout [AttrForeground 0 (negate 1) white,
+                                             AttrWeight 0 (negate 1) WeightBold,
+                                             AttrSize 0 (negate 1) 24]
+        liftIO $ drawLayout drawWindow gc 0 0 layout
+        return ()
+      -- If we have already rendered the slide, then uncompress and draw the data
+      Just (pixels, width', height', stride) -> do
+        (width, height) <- liftIO $ drawableGetSize drawWindow
+        liftIO $ BSU.unsafeUseAsCString
+          (BS.concat (BSL.toChunks (decompressWith (defaultDecompressParams { decompressBufferSize = height' * stride }) pixels)))
+          (\pixelPtr ->
+            withImageSurfaceForData (castPtr pixelPtr) FormatRGB24 width' height' stride (\surface ->
+              renderWithDrawable drawWindow $ do
+                translate (fromIntegral $ (width - width') `div` 2) (fromIntegral $ (height - height') `div` 2)
+                setSourceSurface surface 0 0
+                paint))
+        return ()
+    postDraw drawWindow gc area
+
+-- The following functions are postDraw functions to be passed to drawView
+
+-- No extra drawing to be done (used by preview views)
+postDrawNone _ _ _ = return ()
+
+-- Draw a border if the current slide is the shown slide (used by thumbnails)
+postDrawBorder p state drawWindow gc area = do
+  p' <- liftIO $ liftM round $ pageAdjustment state `get` adjustmentValue
+  when (p == p') $ liftIO $ do
+    color <- widgetGetStyle area >>= flip styleGetBackground StateSelected
+    gcGetValues gc >>= \v -> gcSetValues gc (v { foreground = color, lineWidth = 6 })
+    (width, height) <- drawableGetSize drawWindow
+    drawRectangle drawWindow gc False 0 0 width height
+
+-- Draw a blank screen if video mute is enabled (used by audience view)
+postDrawVideoMute state drawWindow _gc _area = do
+  videoMute' <- liftIO $ readIORef (videoMute state)
+  case videoMute' of
+    MuteOff -> return ()
+    MuteBlack -> liftIO $ renderWithDrawable drawWindow (setSourceRGB 0.0 0.0 0.0 >> paint)
+    MuteWhite -> liftIO $ renderWithDrawable drawWindow (setSourceRGB 1.0 1.0 1.0 >> paint)
+    --MuteFreeze n -> drawView state (const n) postDrawNone area
+
+------------------------
+-- Clock/timer rendering and control
+------------------------
+
+-- Set the clock mode (e.g., 12 vs 24 hour)
+modifyClockState state f = modifyIORef (clock state) f >> displayTime state
+
+-- Functions that are used as parameter to modifyTimerState
+startTimer (Counting start end) = return (Counting start end)
+startTimer (Stopped _ elapsed remaining) = getMicroseconds >>= \t -> return (Counting (t-elapsed) (t+remaining))
+
+pauseTimer (Counting start end) = getMicroseconds >>= \t -> return (Stopped True (t-start) (end-t))
+pauseTimer (Stopped _ elapsed remaining) = return (Stopped True elapsed remaining)
+
+stopTimer (Counting start end) = getMicroseconds >>= \t -> return (Stopped False (t-start) (end-t))
+stopTimer (Stopped _ elapsed remaining) = return (Stopped False elapsed remaining)
+
+unpauseTimer (Stopped False elapsed remaining) = return (Stopped False elapsed remaining)
+unpauseTimer t = startTimer t
+
+togglePauseTimer t@(Stopped True _ _) = startTimer t
+togglePauseTimer t = pauseTimer t
+
+toggleStopTimer t@(Stopped False _ _) = startTimer t
+toggleStopTimer t = stopTimer t
+
+-- Modifies the current timer state. "f" should be one of 'startTimer', etc.
+modifyTimerState state f = do
+  timerState <- f =<< readIORef (timer state)
+  builderGetObject (builder state) castToLabel "timerStatusLabel" >>= (`set` case timerState of
+    Stopped True _ _ -> [labelText := "\x25AE\x25AE", -- U+25AE = One bar of pause icon
+                 labelAttributes := [AttrSize 0 (negate 1) 30, AttrForeground 0 (negate 1) white]]
+    Stopped False _ _ -> [labelText := "\x25A0", -- U+25A0 = Stop
+                 labelAttributes := [AttrSize 0 (negate 1) 30, AttrForeground 0 (negate 1) white]]
+    Counting _ _ -> [labelText := "\x25B6", -- U+25B6 = Play
+                   labelAttributes := [AttrSize 0 (negate 1) 30, AttrForeground 0 (negate 1) black]])
+  writeIORef (timer state) timerState
+
+-- Render the clock/timer
+displayTime state = do
+  -- Compute foreground and background colors based on timer status and times
+  timer' <- readIORef (timer state)
+  (elapsed, remaining) <- case timer' of
+    Stopped _ elapsed remaining -> return (elapsed, remaining)
+    Counting start end -> getMicroseconds >>= \t -> return (t - start, end - t)
+  warningTime' <- readIORef (warningTime state)
+  endTime' <- readIORef (endTime state)
+  let (fg_color, bg_color) | remaining < endTime' = (white, red)
+                           | remaining < warningTime' = (white, purple)
+                           | otherwise = (white, black)
+
+  -- Get time string based on clock mode
+  clockMode <- readIORef (clock state)
+  let clockStr = case clockMode of 
+        RemainingTime -> " \x231B" -- U+231B = Hour glass
+        ElapsedTime -> " \x231A" -- U+231A = Wall clock
+        WallTime12 -> ""
+        WallTime24 -> " (24H)"
+  time <- case clockMode of
+            RemainingTime -> return (formatTime remaining)
+            ElapsedTime -> return (formatTime elapsed)
+            WallTime12 -> getTime "%I:%M %p"
+            WallTime24 -> getTime "%H:%M"
+
+  -- Function for setting the text of a label at a given size
+  let setLabel attrSize size label = label `set` [
+        labelText := time++clockStr,
+        labelAttributes := [attrSize 0 (length time) size, -- The first text is at full size
+                            attrSize (length time) (negate 1) (size / 2), -- The second test is at half size
+                            AttrForeground 0 (negate 1) fg_color,
+                            AttrBackground 0 (negate 1) bg_color]]
+
+  -- Set the hidden label so we maintain a minimum size
+  setLabel AttrSize textSize =<< builderGetObject (builder state) castToLabel "hiddenTimeLabel"
+  -- TODO: Why is AttrSize a different font than what the GUI builder makes?
+
+  -- Initially set the size based on height
+  label <- builderGetObject (builder state) castToLabel "timeLabel"
+  (w, h) <- widgetGetSize label
+  setLabel AttrAbsSize (fromIntegral h) label
+
+  -- Then rescale the text to ensure it is not too wide
+  (_, PangoRectangle _ _ iw _) <- layoutGetExtents =<< labelGetLayout label
+  setLabel AttrAbsSize (fromIntegral (h `min` (h * w `div` round iw))) label
+
+------------------------
+-- Time formatting
+------------------------
+
+-- Parse time entered by the user.
+-- The accepted format is "-* ((float :)? float :)? float" but whitespace is allowed anywhere.
+-- I'd really prefer to use standard code for this, but I couldn't find any
+parseTime :: String -> Maybe Integer{-microseconds-}
+parseTime str = go (filter (not . isSpace) str) where
+  go ('-' : str) = liftM negate (go str)
+  go str = do
+    let (sc, sc') = break (':'==) (reverse str)
+        (mn, mn') = break (':'==) (if null sc' then "0" else drop 1 sc')
+        hr = if null mn' then "0" else drop 1 mn'
+    [Just h, Just m, Just s] <- return $ map (maybeRead . reverse) [hr, mn, sc]
+    return $ round (1000 * 1000 * (s + 60 * (m + 60 * h)) :: Double)
+
+-- Format time for display to user.  Format: H:MM:SS or -H:MM:SS
+formatTime :: Integer -> String
+formatTime microseconds = printf "%d:%02d:%02d" hours (abs minutes) (abs seconds) where
+  ((((hours, minutes), seconds), _tenths), _) = id // 60 // 60 // 10 // (100*1000) $ microseconds
+  (//) f base val = (f q, r) where (q, r) = val `quotRem` base
+
+-- Return current time in microseconds
+getMicroseconds :: IO Integer
+getMicroseconds = do
+  GTimeVal { gTimeValSec = sec, gTimeValUSec = usec } <- gGetCurrentTime
+  return (fromIntegral sec * 1000 * 1000 + fromIntegral usec)
+
+-- Return the time of day according to "format"
+getTime format = do
+  tz <- getCurrentTimeZone
+  utc <- getCurrentTime
+  return $ Data.Time.Format.formatTime defaultTimeLocale format (utcToLocalTime tz utc)
+
+------------------------
+-- Loading and Rendering the PDF document
+------------------------
+
+-- Load a new PDF document
+openDoc state uri = do
+  doc <- catch (documentNewFromFile uri Nothing)
+           (\x -> errorDialog ("Error opening \"" ++ uri ++ "\": " ++ show (x :: GError)) >> return Nothing)
+  case doc of
+    Nothing -> errorDialog ("Unknown error opening \"" ++ uri ++ "\"") >> return True
+    Just doc -> do
+      -- Use the document title or filename as the window title
+      title <- doc `get` documentTitle
+      let makeTitle t = (if null title then takeFileName uri else title) ++ " (" ++ t ++ " Window) - " ++ appName
+      upper <- liftM fromIntegral (documentGetNPages doc)
+      setDocument state makeTitle 1 upper (Just uri) (Just doc)
+
+-- Set state variables for a new PDF document
+setDocument state makeTitle lower upper uri doc = do
+  audienceWindow state >>= (`windowSetTitle` makeTitle "Audience")
+  presenterWindow state >>= (`windowSetTitle` makeTitle "Presenter")
+  -- Ensure that the current page number is in bounds for the new document
+  -- NOTE: we increment the current page number by 0 to trigger the range clamping on pageAdjustment
+  pageAdjustment state `set` [adjustmentLower := lower, adjustmentUpper := upper, adjustmentValue :~ (+0)]
+  -- Save the new state and return
+  writeIORef (pages state) Map.empty
+  writeIORef (documentURL state) uri
+  writeIORef (document state) doc
+  return True
+
+-- We render in a timeout to keep the GUI responsive.  We move to a
+-- slower timeout when everything is already rendered to avoid hogging
+-- the CPU.  We avoid using a separate thread because that would
+-- require using postGUISync which causes delays that slow down the
+-- rendering.
+startRenderThread state progress = renderThreadSoon where
+  renderThreadSoon = widgetShow progress >> void (timeoutAdd (renderThread >> return False) 1{-milliseconds-})
+  renderThreadDelayed = widgetHide progress >> void (timeoutAdd (renderThread >> return False) 100{-milliseconds-})
+  renderThread = do
+    doc <- readIORef (document state)
+    case doc of
+      Nothing -> renderThreadDelayed -- We have no document to render
+      Just doc -> do
+        numPages <- liftM round $ pageAdjustment state `get` adjustmentUpper
+        currPage <- liftM round $ pageAdjustment state `get` adjustmentValue
+        -- Get the sizes of views and clean the cache of sizes with no views
+        views <- liftM (nub . Map.elems) $ readIORef (views state)
+        cache <- liftM (Map.filterWithKey (\(_, w, h) _ -> (w, h) `elem` views)) $ readIORef (pages state)
+        -- Select a page to be rendered for a view that is not in the cache.
+        -- Choose the view close to the current page if possible.
+        case [(page,w,h) |
+              page <- sortBy (comparing (\i -> max (i - currPage) (2 * (currPage - i)))) [1..numPages],
+              (w,h) <- sortBy (flip compare) views,
+              (page,w,h) `Map.notMember` cache] of
+          -- If nothing to be rendered, then move to the slower timeout to avoid hogging the CPU.
+          [] -> renderThreadDelayed
+          -- Otherwise, render that page and store a compressed copy in the cache.
+          work@((pageNum, width, height) : _) -> do
+            progressBarSetFraction progress (1 - fromIntegral (length work) / fromIntegral (numPages * length views))
+            page <- documentGetPage doc (pageNum-1)
+            (docWidth, docHeight) <- pageGetSize page
+            -- Find a scaling factor that fits in the view while preserving aspect ratio
+            let scaleFactor = min (fromIntegral width  / docWidth)
+                                  (fromIntegral height / docHeight)
+            -- Render the page to an image surface
+            -- NOTE: we use 24 bits instead of 32 to cut down on size and time
+            (pixels, w, h, stride) <- withImageSurface FormatRGB24
+              (round $ scaleFactor * docWidth) (round $ scaleFactor * docHeight) (\surface -> do
+                renderWith surface $ do scale scaleFactor scaleFactor
+                                        setSourceRGB 1.0 1.0 1.0 >> paint -- draw a white page background
+                                        pageRender page -- draw page
+                return (,,,) `ap` imageSurfaceGetData surface
+                             `ap` imageSurfaceGetWidth surface
+                             `ap` imageSurfaceGetHeight surface
+                             `ap` imageSurfaceGetStride surface)
+            -- Compress the data stored in the image surface
+            compression' <- readIORef (compression state)
+            let pixels' = compressWith (defaultCompressParams { compressLevel = compressionLevel compression' })
+                                       (BSL.fromChunks [pixels])
+            BSL.length pixels' `seq` return () -- avoid memory leak due to not being strict enough
+            writeIORef (pages state) (Map.insert (pageNum,width,height) (pixels', w, h, stride) cache)
+            -- Redraw the windows and re-enter the render thread quickly in case their is more to render
+            mainWindows state >>= mapM_ widgetQueueDraw
+            renderThreadSoon
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,675 @@
+              GNU GENERAL PUBLIC LICENSE
+                Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                     Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+ 
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+  
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+              END OF TERMS AND CONDITIONS
+
+     How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program 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.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/Makefile b/Makefile
new file mode 100644
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,23 @@
+all:
+	@echo !!! This Makefile is for development purposes only. !!!
+	@echo !!! Users should instead use Setup.hs or cabal. !!!
+	@echo
+	@echo Development targets: std prof lint clean
+	@exit 1
+
+WARNING_FLAGS=-W -Wall -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-unused-do-bind -fno-warn-missing-signatures
+GHC=ghc $(WARNING_FLAGS) -rtsopts 
+
+std:
+	$(GHC) --make HaskellPdfPresenter.hs
+
+# Two stage profile build, due to Template Haskell not playing nice with profiling.
+# See: http://www.haskell.org/ghc/docs/6.12.1/html/users_guide/template-haskell.html#id3029367
+prof: std
+	$(GHC) --make HaskellPdfPresenter.hs -prof -osuf p_o
+
+lint:
+	hlint HaskellPdfPresenter.hs
+
+clean:
+	rm -f HaskellPdfPresenter HaskellPdfPresenter.hi HaskellPdfPresenter.hp HaskellPdfPresenter.o HaskellPdfPresenter.p_o
diff --git a/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,190 @@
+*************************************
+***                               ***
+*** Haskell Pdf Presenter (hpdfp) ***
+***                               ***
+*************************************
+
+This application is a viewer for PDF-based presentations such as those
+generated by Beamer.  Unlike standard PDF viewers it has features
+specifically for presentations such as a visual clock/timer and a
+preview showing the next slide to the person delivering the talk.
+
+For basic use of the program, simply pass the path to the PDF as an
+argument as in the following:
+
+    $ hpdfp [filename]
+
+For help with the command line use the "--help" flag.
+
+For help with the GUI press the "?" key when the program is running.
+
+There are a lot of features that may not be immediately obvious so
+please be sure to look at those bits of documentation.
+
+You can also visit the project home page for example use cases:
+    http://michaeldadams.org/projects/haskell-pdf-presenter/
+
+***********
+WARNING: This application intentionally disables both the screen saver
+and automatic screen dimming.  This is to avoid having the screen saver
+start in the middle of your presentation.
+***********
+
+================
+Features
+================
+- Rendering
+  - Slides are pre-rendered in the background
+  - Pre-rendered slides are compressed to memory usage is low
+      (e.g., my dissertation defense with 95 slides takes only 60MB at max resolution)
+  - slide number or file name until rendered
+  - Renders slide close to current slide first (at a rate of two forward per one backward).
+  - Cache is filtered and wiped when the screen resizes
+  - Since poppler is not thread safe (see https://bugs.freedesktop.org/show_bug.cgi?id=50992),
+    we avoid using a separate thread for rendering, but it is still done incrementally
+    so the GUI is still responsive.
+
+- Compatible with most remote clickers
+
+- Disables screen saver when running in fullscreen mode
+  - WARNING: This is done by programatically giggling the mouse by zero pixels (even when running in the background).
+    This may lead to other programs thinking the mouse is constantly moving
+    (e.g. a video player may not dim the background or hide the mouse).
+    I haven't found any programs where this is an actual problem, but it is a possibility.
+
+- Audience view
+  - Mouse hides after ~3 seconds
+  - Shows on top if there is only one monitor
+  - Automatically placed on auxiliary monitor/projector if present
+  - Both black and white video mute
+
+- Presenter view
+  - Meta-data:
+    - A small progress bar is shown at the bottom while pre-rendering,
+      but since pre-rendering is done in the background, the GUI is
+      still be responsive and you do not have to wait for
+      pre-rendering to complete.
+    - Fonts are big (60pt = 5 * 12pt)
+    - Resizeable
+    - Timer:
+      - Both stopped and paused mode:
+        - Paused automatically starts on slide forward or back
+        - Stopped says stopped until user explicitly changes it
+      - Four different modes: count down, count up, 12 hour and 24 hour
+      - Grows to fit size (useful for session chairs)
+      - Colors: white, red (warning), purple (overtime)
+          Yeah, these colors are a bit strange, but I've chosen them to
+          be easily distinguishable even in peripheral vision or at a
+          glance.  I don't know how well they work for color-blind
+          people.  Feedback is welcome.
+      - Double click to configure timer
+        - Current remaining time
+        - Initial time
+        - Warning time
+    - Slide counter
+      - Double click to go to slide
+    - Mute status
+    - Timer status
+      - Multiple modes: Count down, count up, wall clock (12 or 24 hr)
+      - When paused (but not stopped) automatically goes to running if user
+        goes forward or backward one slide.  Changing slide in other ways does not unpause.
+      - NOTE: to start timer but stay on first slide, just hit the back key
+    - Progress bar when doing rendering in the background
+  - Preview
+  - Thumbnails
+    - Current slide highlighted
+    - Tooltips show slide number (Currently disabled due to a bug in GTK)
+    - Click to go to slide
+    - Mouse wheel scrolls instead of changes slide
+
+- Times specified in dialogues or command line are parsed according to
+  the format "-* ((float:)? float:)? float" with whitespace
+  allowed anywhere. The following are some examples.
+    1:30:05     (1 hour, 30 min and 5 seconds)
+      30:00     (30 min)
+         90     (90 seconds)
+          0     (0 seconds)
+      -1:30     (negative 1 minute and 30 seconds = negative 90 seconds)
+      1:0:0     (1 hour)
+     1:-5:0     (1 hour minus 5 minutes = 55 minutes)
+    1.5:0:0     (1.5 hours)
+
+- Key bindings:
+  (Note that the keys are far apart on a Qwerty keyboard and irreversible
+  actions (e.g., resetting the timer) all use a modifier key.)
+
+  - Help/About dialog: H, ?
+  - Quit: Alt-F4, Ctrl-Q
+  - Switching slides:
+    - Forward slide and unpause (shift moves by 10): space, page down, enter, right arrow, down arrow
+    - Backward slide and unpause (shift moves by 10): back space, page up, left arrow, up arrow
+    - First slide: home
+    - Last slide: end
+    - Goto slide number: Ctrl-G, double-click slide number
+    - History back: Alt-L, XF86Back
+    - History forward: Alt-R, XF86Forward
+  - Clock/timer control:
+    - Toggle pause: P, pause
+    - Toggle stop: Shift-P
+    - Reset timer: C
+    - Open timer dialog: Ctrl-C
+  - Mute:
+    - Toggle white: W
+    - Toggle black: B
+  - Presenter screen:
+    - Switch views: Tab
+    - Change preview percentage: [, ], Shift-[ (minor), Shift-] (minor), = (standard stops)
+      - Standard stops: 0, best-fit on left, 1/3, 1/2, 2/3, best-fit on right, 1
+  - Files:
+    - Reload file: Ctrl-R
+    - Open file: Ctrl-O
+    - Close file: Shift-Q
+  - Window control:
+    - Enter fullscreen: F, F11, Alt-Enter, Control-L
+    - Exit fullscreen: Escape, F, F11, Alt-Enter, Control-L
+    - Move between monitors: S, Shift-S (reverse)
+    - Move presenter window between monitors: Ctrl-S, Shift-Ctrl-S (reverse)
+    - Move audience window between monitors: Alt-S, Shift-Alt-S (reverse)
+
+- Command line:
+  - Zero or one files as argument
+      (NOTE: Zero files is useful for creating a simple timer with no slides)
+  - Options:
+    -h, -?   --help                                   Display usage message
+    -s INT   --slide=INT                              Initial slide number (default 1)
+    -t TIME  --start-time=TIME, --starting-time=TIME  Start time (default 10:00)
+    -w TIME  --warn-time=TIME, --warning-time=TIME    Warning time (default 5:00)
+    -e TIME  --end-time=TIME, --ending-time=TIME      End time (default 0:00)
+    -f       --fullscreen                             Start in full screen mode
+    -p INT   --preview-percentage=INT                 Initial preview percentage (default 50)
+             --presenter-monitor=INT                  Initial monitor for presenter window (default 0)
+             --audience-monitor=INT                   Initial monitor for audience window (default 1)
+             --video-mute=MODE                        Initial video mute mode: "black", "white" or "off" (default)
+             --clock-mode=MODE                        Initial clock mode: "remaining" (default), "elapsed", "12hour" or "24hour"
+             --timer-mode=MODE                        Initial timer mode: "play", "pause" (default) or "stop"
+             --compression=[0-9]                      Compression level (default 1). 0 is none. 1 is fastest. 9 is best.
+
+================
+Inspired by
+================
+
+* pdfpc: http://davvil.github.com/pdfpc/
+
+* pdf-presenter-console: http://westhoffswelt.de/projects/pdf_presenter_console.html
+
+* pympress: http://pympress.org/
+
+================
+Known Bugs
+================
+
+If you
+  (1) are in full screen mode,
+  (2) are displaying to two monitors,
+  (3) have the presenter toggled to show thumbnails, and
+  (4) hover the mouse over the vertical scroll bar,
+then the rendering will glitch and show older versions of the screen.
+I don't know why this happens, but I'm guessing it is a bug in GTK.
+A similar bug happens if you put tooltips on the thumbnails (which
+are currently disabled to avoid that bug).  Any help in solving
+this bug would be appreciated.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/haskell-pdf-presenter.cabal b/haskell-pdf-presenter.cabal
new file mode 100644
--- /dev/null
+++ b/haskell-pdf-presenter.cabal
@@ -0,0 +1,79 @@
+-- The name of the package.
+Name:                haskell-pdf-presenter
+
+-- The package version. See the Haskell package versioning policy
+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for
+-- standards guiding when and how versions should be incremented.
+Version:             0.2.2
+
+-- A short (one-line) description of the package.
+Synopsis:            Tool for presenting PDF-based presentations.
+
+-- A longer description of the package.
+Description:         The Haskell Pdf Presenter ("hpdfp") is a tool for presenting PDF-based
+                     slide shows.  It has many features and niceties that make delivering
+                     a presentation easier.  For example, as a
+                     presenter, it is useful to see the next slide before it is shown to
+                     the audience as well as how much allotted time remains.
+                     .
+                     There are a lot of features that may not be immediately obvious so
+                     please be sure to look at the documentation.
+                     For help with the command line, use the "--help" flag.
+                     For help with the GUI, press the "?" key when the program is running.
+                     The "README" contains a terse summary of all the features.
+                     .
+                     For more information and example uses, please visit the project home page.
+
+-- URL for the project homepage.
+Homepage:            http://michaeldadams.org/projects/haskell-pdf-presenter/
+
+-- The license under which the package is released.
+License:             GPL-3
+
+-- The file containing the license text.
+License-file:        LICENSE
+
+-- The package author(s).
+Author:              Michael D. Adams
+
+-- An email address to which users can send suggestions, bug reports, and patches.
+Maintainer:          Michael D. Adams <http://michaeldadams.org/>
+
+-- A copyright notice.
+Copyright:           (c) 2012-2013, Michael D. Adams
+
+Category:            Application, Graphics, Image Viewer, PDF
+
+Build-type:          Simple
+
+-- Extra files to be distributed with the package, such as examples or a README.
+Extra-source-files:  README, CHANGELOG, HaskellPdfPresenter.glade, Makefile
+
+-- Constraint on the version of Cabal needed to build this package.
+Cabal-version:       >=1.6
+
+Executable hpdfp
+  -- .hs or .lhs file containing the Main module.
+  Main-is: HaskellPdfPresenter.hs
+  
+  -- Packages needed in order to build this package.
+  Build-depends: base == 4.*, bytestring >= 0.9, containers >= 0.1, directory >= 1.0,
+                 filepath >= 1.0, template-haskell >= 2.2, zlib == 0.5.*,
+                 old-locale, time >= 1.1.2.0,
+                 glib, poppler == 0.12.2.*, gtk >= 0.12.2, cairo
+  
+  -- Modules not exported by this package.
+  -- Other-modules:       
+  
+  -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.
+  -- Build-tools:         
+
+-- URL for the project repository.
+Source-repository head
+  Type: mercurial
+  Location: http://michaeldadams.org/repos-pub/hg/haskell-pdf-presenter/
+
+Source-repository this
+  Type: mercurial
+  Location: http://michaeldadams.org/repos-pub/hg/haskell-pdf-presenter/
+  Tag: v0.2.2
