[meld] Port to GTK 3 and introspection



commit 0e80454e781681ed93f775b32ca37a237a1071e1
Author: Kai Willadsen <kai willadsen gmail com>
Date:   Sun Sep 29 06:09:37 2013 +1000

    Port to GTK 3 and introspection

 data/ui/filediff.ui           |  351 ++++++++++++++++++++++++-----------------
 data/ui/preferences.ui        |   53 +++----
 meld/diffmap.py               |   84 +++++------
 meld/diffutil.py              |    8 +-
 meld/dirdiff.py               |  118 ++++++++-------
 meld/filediff.py              |  300 ++++++++++++++++++-----------------
 meld/linkmap.py               |   65 ++++----
 meld/meldapp.py               |   24 ++--
 meld/meldbuffer.py            |   14 +-
 meld/melddoc.py               |   55 ++++---
 meld/meldwindow.py            |  167 ++++++++++----------
 meld/misc.py                  |   68 ++++----
 meld/newdifftab.py            |   12 +-
 meld/patchdialog.py           |   10 +-
 meld/preferences.py           |   26 ++--
 meld/recent.py                |   55 ++++---
 meld/tree.py                  |   32 ++--
 meld/ui/emblemcellrenderer.py |   55 ++++---
 meld/ui/findbar.py            |   21 ++-
 meld/ui/gnomeglade.py         |   10 +-
 meld/ui/historyentry.py       |   18 +-
 meld/ui/msgarea.py            |   25 ++--
 meld/ui/notebooklabel.py      |   46 +++---
 meld/ui/statusbar.py          |   44 +++---
 meld/ui/vcdialogs.py          |   12 +-
 meld/ui/wraplabel.py          |   18 +-
 meld/undo.py                  |   12 +-
 meld/util/prefs.py            |   14 +-
 meld/util/sourceviewer.py     |   74 +++++-----
 meld/vcview.py                |   87 ++++++-----
 30 files changed, 991 insertions(+), 887 deletions(-)
---
diff --git a/data/ui/filediff.ui b/data/ui/filediff.ui
index 553fb08..e6affe7 100644
--- a/data/ui/filediff.ui
+++ b/data/ui/filediff.ui
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="2.16"/>
-  <!-- interface-naming-policy toplevel-contextual -->
+  <!-- interface-requires gtk+ 3.0 -->
+  <!-- interface-requires meld.ui.gladesupport 0.0 -->
   <object class="GtkMessageDialog" id="check_save_dialog">
     <property name="can_focus">False</property>
     <property name="border_width">5</property>
@@ -11,12 +11,13 @@
     <property name="text" translatable="yes">Save changes to documents before closing?</property>
     <property name="secondary_text" translatable="yes">If you don't save, changes will be permanently 
lost.</property>
     <child internal-child="vbox">
-      <object class="GtkVBox" id="dialog-vbox1">
+      <object class="GtkBox" id="dialog-vbox1">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
         <property name="spacing">6</property>
         <child internal-child="action_area">
-          <object class="GtkHButtonBox" id="dialog-action_area1">
+          <object class="GtkButtonBox" id="dialog-action_area1">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="layout_style">end</property>
@@ -26,7 +27,6 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
-                <property name="use_action_appearance">False</property>
                 <property name="use_underline">True</property>
               </object>
               <packing>
@@ -41,7 +41,6 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
-                <property name="use_action_appearance">False</property>
                 <property name="use_underline">True</property>
               </object>
               <packing>
@@ -56,7 +55,6 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
-                <property name="use_action_appearance">False</property>
                 <property name="use_underline">True</property>
               </object>
               <packing>
@@ -101,99 +99,40 @@
       <action-widget response="-5">save_button</action-widget>
     </action-widgets>
   </object>
-  <object class="GtkMessageDialog" id="revert_dialog">
+  <object class="GtkWindow" id="container">
+    <property name="visible">True</property>
     <property name="can_focus">False</property>
-    <property name="border_width">5</property>
-    <property name="type_hint">dialog</property>
-    <property name="skip_taskbar_hint">True</property>
-    <property name="message_type">question</property>
-    <property name="text" translatable="yes">Revert unsaved changes to documents?</property>
-    <property name="secondary_text" translatable="yes">Changes made to the following documents will be 
permanently lost:
-</property>
-    <child internal-child="vbox">
-      <object class="GtkVBox" id="dialog-vbox1">
+    <property name="title" translatable="yes">window1</property>
+    <signal name="destroy" handler="on_delete_event" swapped="no"/>
+    <child>
+      <object class="GtkVBox" id="filediff">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
-        <property name="spacing">6</property>
-        <child internal-child="action_area">
-          <object class="GtkHButtonBox" id="dialog-action_area1">
+        <signal name="key-press-event" handler="on_key_press_event" swapped="no"/>
+        <child>
+          <object class="GtkTable" id="table">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="layout_style">end</property>
+            <property name="n_rows">3</property>
+            <property name="n_columns">7</property>
             <child>
-              <object class="GtkButton" id="cancel_button">
-                <property name="label">gtk-cancel</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="use_action_appearance">False</property>
-                <property name="use_underline">True</property>
-                <property name="use_stock">True</property>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">0</property>
-              </packing>
+              <placeholder/>
             </child>
             <child>
-              <object class="GtkButton" id="revert_button">
-                <property name="label">gtk-revert-to-saved</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="use_action_appearance">False</property>
-                <property name="use_underline">True</property>
-                <property name="use_stock">True</property>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
             </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="extra_vbox">
-            <property name="visible">False</property>
-            <property name="can_focus">False</property>
             <child>
               <placeholder/>
             </child>
-          </object>
-          <packing>
-            <property name="expand">True</property>
-            <property name="fill">True</property>
-            <property name="position">3</property>
-          </packing>
-        </child>
-      </object>
-    </child>
-    <action-widgets>
-      <action-widget response="-6">cancel_button</action-widget>
-      <action-widget response="-5">revert_button</action-widget>
-    </action-widgets>
-  </object>
-  <object class="GtkWindow" id="container">
-    <property name="visible">True</property>
-    <property name="title" translatable="yes">window1</property>
-    <signal handler="on_delete_event" name="destroy"/>
-    <child>
-      <object class="GtkVBox" id="filediff">
-        <property name="visible">True</property>
-        <signal handler="on_key_press_event" name="key_press_event"/>
-        <child>
-          <object class="GtkTable" id="table">
-            <property name="visible">True</property>
-            <property name="n_rows">3</property>
-            <property name="n_columns">7</property>
             <child>
               <placeholder/>
             </child>
@@ -203,31 +142,35 @@
                 <property name="can_focus">False</property>
                 <child>
                   <object class="GtkToggleButton" id="readonlytoggle2">
-                    <property name="visible">False</property>
                     <property name="can_focus">False</property>
+                    <property name="receives_default">False</property>
+                    <property name="tooltip_text" translatable="yes">This file can not be written to. You 
may click here to unlock this file and make changes anyway, but these changes must be saved to a new 
file.</property>
                     <property name="relief">none</property>
-                    <property name="tooltip-text" translatable="yes">This file can not be written to. You 
may click here to unlock this file and make changes anyway, but these changes must be saved to a new 
file.</property>
-                    <signal handler="on_readonly_button_toggled" name="toggled"/>
+                    <signal name="toggled" handler="on_readonly_button_toggled" swapped="no"/>
                     <child>
                       <object class="GtkImage" id="readonly_image2">
                         <property name="visible">True</property>
-                        <property name="icon-name">emblem-readonly</property>
+                        <property name="can_focus">False</property>
+                        <property name="icon_name">emblem-readonly</property>
                       </object>
                     </child>
                   </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
+                    <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkFileChooserButton" id="fileentry2">
                     <property name="visible">True</property>
-                    <signal handler="on_fileentry_file_set" name="file-set"/>
+                    <property name="can_focus">False</property>
+                    <signal name="file-set" handler="on_fileentry_file_set" swapped="no"/>
                   </object>
                   <packing>
                     <property name="expand">True</property>
                     <property name="fill">True</property>
+                    <property name="position">1</property>
                   </packing>
                 </child>
               </object>
@@ -244,31 +187,35 @@
                 <property name="can_focus">False</property>
                 <child>
                   <object class="GtkToggleButton" id="readonlytoggle1">
-                    <property name="visible">False</property>
                     <property name="can_focus">False</property>
+                    <property name="receives_default">False</property>
+                    <property name="tooltip_text" translatable="yes">This file can not be written to. You 
may click here to unlock this file and make changes anyway, but these changes must be saved to a new 
file.</property>
                     <property name="relief">none</property>
-                    <property name="tooltip-text" translatable="yes">This file can not be written to. You 
may click here to unlock this file and make changes anyway, but these changes must be saved to a new 
file.</property>
-                    <signal handler="on_readonly_button_toggled" name="toggled"/>
+                    <signal name="toggled" handler="on_readonly_button_toggled" swapped="no"/>
                     <child>
                       <object class="GtkImage" id="readonly_image1">
                         <property name="visible">True</property>
-                        <property name="icon-name">emblem-readonly</property>
+                        <property name="can_focus">False</property>
+                        <property name="icon_name">emblem-readonly</property>
                       </object>
                     </child>
                   </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
+                    <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkFileChooserButton" id="fileentry1">
                     <property name="visible">True</property>
-                    <signal handler="on_fileentry_file_set" name="file-set"/>
+                    <property name="can_focus">False</property>
+                    <signal name="file-set" handler="on_fileentry_file_set" swapped="no"/>
                   </object>
                   <packing>
                     <property name="expand">True</property>
                     <property name="fill">True</property>
+                    <property name="position">1</property>
                   </packing>
                 </child>
               </object>
@@ -285,31 +232,35 @@
                 <property name="can_focus">False</property>
                 <child>
                   <object class="GtkToggleButton" id="readonlytoggle0">
-                    <property name="visible">False</property>
                     <property name="can_focus">False</property>
+                    <property name="receives_default">False</property>
+                    <property name="tooltip_text" translatable="yes">This file can not be written to. You 
may click here to unlock this file and make changes anyway, but these changes must be saved to a new 
file.</property>
                     <property name="relief">none</property>
-                    <property name="tooltip-text" translatable="yes">This file can not be written to. You 
may click here to unlock this file and make changes anyway, but these changes must be saved to a new 
file.</property>
-                    <signal handler="on_readonly_button_toggled" name="toggled"/>
+                    <signal name="toggled" handler="on_readonly_button_toggled" swapped="no"/>
                     <child>
                       <object class="GtkImage" id="readonly_image0">
                         <property name="visible">True</property>
-                        <property name="icon-name">emblem-readonly</property>
+                        <property name="can_focus">False</property>
+                        <property name="icon_name">emblem-readonly</property>
                       </object>
                     </child>
                   </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
+                    <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkFileChooserButton" id="fileentry0">
                     <property name="visible">True</property>
-                    <signal handler="on_fileentry_file_set" name="file-set"/>
+                    <property name="can_focus">False</property>
+                    <signal name="file-set" handler="on_fileentry_file_set" swapped="no"/>
                   </object>
                   <packing>
                     <property name="expand">True</property>
                     <property name="fill">True</property>
+                    <property name="position">1</property>
                   </packing>
                 </child>
               </object>
@@ -323,35 +274,45 @@
             <child>
               <object class="GtkVBox" id="vbox0">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <child>
                   <object class="MsgAreaController" id="msgarea_mgr0">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkScrolledWindow" id="scrolledwindow0">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="window_placement">GTK_CORNER_TOP_RIGHT</property>
+                    <property name="vscrollbar_policy">always</property>
+                    <property name="window_placement">top-right</property>
                     <property name="window_placement_set">True</property>
-                    <signal handler="on_scrolledwindow_size_allocate" name="size-allocate"/>
+                    <signal name="size-allocate" handler="on_scrolledwindow_size_allocate" swapped="no"/>
                     <child>
                       <object class="MeldSourceView" id="textview0">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
-                        <signal handler="on_textview_button_press_event" name="button_press_event"/>
-                        <signal handler="on_textview_expose_event" name="expose_event"/>
-                        <signal handler="on_textview_focus_in_event" name="focus_in_event"/>
-                        <signal handler="on_textview_focus_out_event" name="focus_out_event"/>
-                        <signal handler="on_key_press_event" name="key_press_event"/>
-                        <signal handler="on_key_release_event" name="key_release_event"/>
-                        <signal handler="on_textview_popup_menu" name="popup-menu"/>
+                        <signal name="button-press-event" handler="on_textview_button_press_event" 
swapped="no"/>
+                        <signal name="draw" handler="on_textview_draw" swapped="no"/>
+                        <signal name="key-release-event" handler="on_key_release_event" swapped="no"/>
+                        <signal name="key-press-event" handler="on_key_press_event" swapped="no"/>
+                        <signal name="focus-in-event" handler="on_textview_focus_in_event" swapped="no"/>
+                        <signal name="focus-out-event" handler="on_textview_focus_out_event" swapped="no"/>
+                        <signal name="popup-menu" handler="on_textview_popup_menu" swapped="no"/>
                       </object>
                     </child>
                   </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
                 </child>
               </object>
               <packing>
@@ -365,6 +326,7 @@
               <object class="GtkImage" id="statusimage2">
                 <property name="width_request">20</property>
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="xalign">1</property>
                 <property name="stock">gtk-missing-image</property>
               </object>
@@ -378,6 +340,7 @@
             <child>
               <object class="GtkImage" id="statusimage1">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="xalign">1</property>
                 <property name="stock">gtk-missing-image</property>
               </object>
@@ -391,6 +354,7 @@
             <child>
               <object class="GtkImage" id="statusimage0">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="xalign">1</property>
                 <property name="stock">gtk-missing-image</property>
               </object>
@@ -402,33 +366,43 @@
             <child>
               <object class="GtkVBox" id="vbox2">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <child>
                   <object class="MsgAreaController" id="msgarea_mgr2">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkScrolledWindow" id="scrolledwindow2">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <signal handler="on_scrolledwindow_size_allocate" name="size-allocate"/>
+                    <property name="vscrollbar_policy">always</property>
+                    <signal name="size-allocate" handler="on_scrolledwindow_size_allocate" swapped="no"/>
                     <child>
                       <object class="MeldSourceView" id="textview2">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
-                        <signal handler="on_textview_button_press_event" name="button_press_event"/>
-                        <signal handler="on_textview_expose_event" name="expose_event"/>
-                        <signal handler="on_textview_focus_in_event" name="focus_in_event"/>
-                        <signal handler="on_textview_focus_out_event" name="focus_out_event"/>
-                        <signal handler="on_key_press_event" name="key_press_event"/>
-                        <signal handler="on_key_release_event" name="key_release_event"/>
-                        <signal handler="on_textview_popup_menu" name="popup-menu"/>
+                        <signal name="button-press-event" handler="on_textview_button_press_event" 
swapped="no"/>
+                        <signal name="draw" handler="on_textview_draw" swapped="no"/>
+                        <signal name="key-release-event" handler="on_key_release_event" swapped="no"/>
+                        <signal name="key-press-event" handler="on_key_press_event" swapped="no"/>
+                        <signal name="focus-in-event" handler="on_textview_focus_in_event" swapped="no"/>
+                        <signal name="focus-out-event" handler="on_textview_focus_out_event" swapped="no"/>
+                        <signal name="popup-menu" handler="on_textview_popup_menu" swapped="no"/>
                       </object>
                     </child>
                   </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
                 </child>
               </object>
               <packing>
@@ -441,33 +415,43 @@
             <child>
               <object class="GtkVBox" id="vbox1">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <child>
                   <object class="MsgAreaController" id="msgarea_mgr1">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkScrolledWindow" id="scrolledwindow1">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <signal handler="on_scrolledwindow_size_allocate" name="size-allocate"/>
+                    <property name="vscrollbar_policy">always</property>
+                    <signal name="size-allocate" handler="on_scrolledwindow_size_allocate" swapped="no"/>
                     <child>
                       <object class="MeldSourceView" id="textview1">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
-                        <signal handler="on_textview_button_press_event" name="button_press_event"/>
-                        <signal handler="on_textview_expose_event" name="expose_event"/>
-                        <signal handler="on_textview_focus_in_event" name="focus_in_event"/>
-                        <signal handler="on_textview_focus_out_event" name="focus_out_event"/>
-                        <signal handler="on_key_press_event" name="key_press_event"/>
-                        <signal handler="on_key_release_event" name="key_release_event"/>
-                        <signal handler="on_textview_popup_menu" name="popup-menu"/>
+                        <signal name="button-press-event" handler="on_textview_button_press_event" 
swapped="no"/>
+                        <signal name="draw" handler="on_textview_draw" swapped="no"/>
+                        <signal name="key-release-event" handler="on_key_release_event" swapped="no"/>
+                        <signal name="key-press-event" handler="on_key_press_event" swapped="no"/>
+                        <signal name="focus-in-event" handler="on_textview_focus_in_event" swapped="no"/>
+                        <signal name="focus-out-event" handler="on_textview_focus_out_event" swapped="no"/>
+                        <signal name="popup-menu" handler="on_textview_popup_menu" swapped="no"/>
                       </object>
                     </child>
                   </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
                 </child>
               </object>
               <packing>
@@ -480,34 +464,35 @@
             <child>
               <object class="DiffMap" id="diffmap1">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
               </object>
               <packing>
                 <property name="left_attach">6</property>
                 <property name="right_attach">7</property>
                 <property name="top_attach">1</property>
                 <property name="bottom_attach">2</property>
-                <property name="x_options">0</property>
+                <property name="x_options"/>
               </packing>
             </child>
             <child>
               <object class="GtkToggleButton" id="lock_button">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
+                <property name="receives_default">False</property>
+                <property name="tooltip_text" translatable="yes">Lock scrolling of all panes</property>
                 <property name="relief">none</property>
-                <signal handler="on_lock_button_toggled" name="toggled"/>
-                <property name="tooltip-text" translatable="yes">Lock scrolling of all panes</property>
+                <signal name="toggled" handler="on_lock_button_toggled" swapped="no"/>
                 <child>
-                      <object class="GtkImage" id="lock_button_image">
-                        <property name="visible">True</property>
-                        <property name="icon-name">meld-locked</property>
-                      </object>
+                  <object class="GtkImage" id="lock_button_image">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">meld-locked</property>
+                  </object>
                 </child>
               </object>
               <packing>
                 <property name="left_attach">6</property>
                 <property name="right_attach">7</property>
-                <property name="top_attach">0</property>
-                <property name="bottom_attach">1</property>
                 <property name="x_options">GTK_FILL</property>
                 <property name="y_options">GTK_FILL</property>
               </packing>
@@ -517,8 +502,7 @@
                 <property name="width_request">50</property>
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="has_focus">False</property>
-                <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK 
| GDK_KEY_RELEASE_MASK</property>
+                <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK 
| GDK_KEY_RELEASE_MASK | GDK_SCROLL_MASK</property>
               </object>
               <packing>
                 <property name="left_attach">2</property>
@@ -532,11 +516,12 @@
             <child>
               <object class="DiffMap" id="diffmap0">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
               </object>
               <packing>
                 <property name="top_attach">1</property>
                 <property name="bottom_attach">2</property>
-                <property name="x_options">0</property>
+                <property name="x_options"/>
               </packing>
             </child>
             <child>
@@ -544,8 +529,7 @@
                 <property name="width_request">50</property>
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="has_focus">False</property>
-                <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK 
| GDK_KEY_RELEASE_MASK</property>
+                <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK 
| GDK_KEY_RELEASE_MASK | GDK_SCROLL_MASK</property>
               </object>
               <packing>
                 <property name="left_attach">4</property>
@@ -557,8 +541,91 @@
               </packing>
             </child>
           </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkMessageDialog" id="revert_dialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="type_hint">dialog</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="message_type">question</property>
+    <property name="text" translatable="yes">Revert unsaved changes to documents?</property>
+    <property name="secondary_text" translatable="yes">Changes made to the following documents will be 
permanently lost:
+</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox2">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel_button1">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="revert_button">
+                <property name="label">gtk-revert-to-saved</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</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="extra_vbox1">
+            <property name="can_focus">False</property>
+            <child>
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">3</property>
+          </packing>
         </child>
       </object>
     </child>
+    <action-widgets>
+      <action-widget response="-6">cancel_button1</action-widget>
+      <action-widget response="-5">revert_button</action-widget>
+    </action-widgets>
   </object>
 </interface>
diff --git a/data/ui/preferences.ui b/data/ui/preferences.ui
index 6501f79..83f8131 100644
--- a/data/ui/preferences.ui
+++ b/data/ui/preferences.ui
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="2.18"/>
-  <!-- interface-naming-policy toplevel-contextual -->
+  <!-- interface-requires gtk+ 3.0 -->
   <object class="GtkAdjustment" id="adjustment1">
     <property name="lower">1</property>
     <property name="upper">8</property>
@@ -39,14 +38,14 @@
     <property name="border_width">5</property>
     <property name="title" translatable="yes">Meld Preferences</property>
     <property name="type_hint">dialog</property>
-    <property name="has_separator">False</property>
     <signal name="response" handler="on_response" swapped="no"/>
     <child internal-child="vbox">
-      <object class="GtkVBox" id="dialog-vbox4">
+      <object class="GtkBox" id="dialog-vbox4">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
         <child internal-child="action_area">
-          <object class="GtkHButtonBox" id="dialog-action_area4">
+          <object class="GtkButtonBox" id="dialog-action_area4">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="layout_style">end</property>
@@ -56,7 +55,6 @@
                 <property name="can_focus">True</property>
                 <property name="can_default">True</property>
                 <property name="receives_default">False</property>
-                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
               </object>
               <packing>
@@ -72,7 +70,6 @@
                 <property name="can_focus">True</property>
                 <property name="can_default">True</property>
                 <property name="receives_default">False</property>
-                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
                 <accelerator key="Escape" signal="clicked"/>
               </object>
@@ -151,8 +148,8 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
                                 <property name="active">True</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="on_checkbutton_default_font_toggled" 
swapped="no"/>
@@ -188,7 +185,6 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
                                     <property name="receives_default">False</property>
-                                    <property name="use_action_appearance">False</property>
                                     <property name="font_name">Monospace 12</property>
                                     <property name="use_font">True</property>
                                     <signal name="font-set" handler="on_fontpicker_font_set" swapped="no"/>
@@ -317,8 +313,8 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" 
handler="on_checkbutton_spaces_instead_of_tabs_toggled" swapped="no"/>
                               </object>
@@ -334,8 +330,8 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="on_checkbutton_wrap_text_toggled" 
swapped="no"/>
                               </object>
@@ -357,8 +353,8 @@
                                     <property name="sensitive">False</property>
                                     <property name="can_focus">True</property>
                                     <property name="receives_default">False</property>
-                                    <property name="use_action_appearance">False</property>
                                     <property name="use_underline">True</property>
+                                    <property name="xalign">0</property>
                                     <property name="active">True</property>
                                     <property name="draw_indicator">True</property>
                                     <signal name="toggled" handler="on_checkbutton_wrap_text_toggled" 
swapped="no"/>
@@ -377,8 +373,8 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" 
handler="on_checkbutton_highlight_current_line_toggled" swapped="no"/>
                               </object>
@@ -394,8 +390,8 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="on_checkbutton_show_line_numbers_toggled" 
swapped="no"/>
                               </object>
@@ -411,8 +407,8 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="on_checkbutton_show_whitespace_toggled" 
swapped="no"/>
                               </object>
@@ -428,8 +424,8 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" 
handler="on_checkbutton_use_syntax_highlighting_toggled" swapped="no"/>
                               </object>
@@ -509,8 +505,8 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="on_system_editor_checkbutton_toggled" 
swapped="no"/>
                               </object>
@@ -544,8 +540,8 @@
                                   <object class="GtkEntry" id="custom_edit_command_entry">
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
-                                    <signal name="focus-out-event" 
handler="on_custom_edit_command_entry_activate" swapped="no"/>
                                     <signal name="activate" handler="on_custom_edit_command_entry_activate" 
swapped="no"/>
+                                    <signal name="focus-out-event" 
handler="on_custom_edit_command_entry_activate" swapped="no"/>
                                   </object>
                                   <packing>
                                     <property name="expand">True</property>
@@ -648,9 +644,8 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
-                                <property name="xalign">0.51999998092651367</property>
+                                <property name="xalign">0</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="on_checkbutton_shallow_compare_toggled" 
swapped="no"/>
                               </object>
@@ -687,7 +682,7 @@
                                     <signal name="changed" handler="on_combo_timestamp_changed" 
swapped="no"/>
                                   </object>
                                   <packing>
-                                    <property name="expand">True</property>
+                                    <property name="expand">False</property>
                                     <property name="fill">True</property>
                                     <property name="position">1</property>
                                   </packing>
@@ -764,8 +759,8 @@
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="receives_default">False</property>
-                            <property name="use_action_appearance">False</property>
                             <property name="use_underline">True</property>
+                            <property name="xalign">0</property>
                             <property name="draw_indicator">True</property>
                             <signal name="toggled" handler="on_checkbutton_ignore_symlinks_toggled" 
swapped="no"/>
                           </object>
@@ -956,7 +951,7 @@
                                     </child>
                                   </object>
                                   <packing>
-                                    <property name="expand">True</property>
+                                    <property name="expand">False</property>
                                     <property name="fill">True</property>
                                     <property name="position">1</property>
                                   </packing>
@@ -1043,9 +1038,8 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
                                     <property name="receives_default">False</property>
-                                    <property name="use_action_appearance">False</property>
                                     <property name="use_underline">True</property>
-                                    <property name="xalign">0.51999998092651367</property>
+                                    <property name="xalign">0</property>
                                     <property name="draw_indicator">True</property>
                                     <signal name="toggled" 
handler="on_checkbutton_show_commit_margin_toggled" swapped="no"/>
                                   </object>
@@ -1087,16 +1081,15 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
                                     <property name="receives_default">False</property>
-                                    <property name="use_action_appearance">False</property>
                                     <property name="use_underline">True</property>
-                                    <property name="xalign">0.51999998092651367</property>
+                                    <property name="xalign">0</property>
                                     <property name="draw_indicator">True</property>
                                     <signal name="toggled" 
handler="on_checkbutton_break_commit_lines_toggled" swapped="no"/>
                                   </object>
                                 </child>
                               </object>
                               <packing>
-                                <property name="expand">True</property>
+                                <property name="expand">False</property>
                                 <property name="fill">True</property>
                                 <property name="position">1</property>
                               </packing>
@@ -1213,8 +1206,8 @@
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
-                        <property name="use_action_appearance">False</property>
                         <property name="use_underline">True</property>
+                        <property name="xalign">0.5</property>
                         <property name="draw_indicator">True</property>
                         <signal name="toggled" handler="on_checkbutton_ignore_blank_lines_toggled" 
swapped="no"/>
                       </object>
@@ -1322,8 +1315,8 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
                                     <property name="text">utf8 iso8859</property>
-                                    <signal name="focus-out-event" handler="on_entry_text_codecs_activate" 
swapped="no"/>
                                     <signal name="activate" handler="on_entry_text_codecs_activate" 
swapped="no"/>
+                                    <signal name="focus-out-event" handler="on_entry_text_codecs_activate" 
swapped="no"/>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
diff --git a/meld/diffmap.py b/meld/diffmap.py
index d6027a4..9cbb35b 100644
--- a/meld/diffmap.py
+++ b/meld/diffmap.py
@@ -18,23 +18,18 @@
 
 import collections
 
-import gobject
-import gtk
+from gi.repository import GObject
+from gi.repository import Gdk
+from gi.repository import Gtk
 
 
-class DiffMap(gtk.DrawingArea):
+class DiffMap(Gtk.DrawingArea):
 
     __gtype_name__ = "DiffMap"
 
-    __gsignals__ = {
-        'expose-event': 'override',
-        'button-press-event': 'override',
-        'size-request': 'override',
-    }
-
     def __init__(self):
-        gtk.DrawingArea.__init__(self)
-        self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
+        GObject.GObject.__init__(self)
+        self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
         self._scrolladj = None
         self._difffunc = lambda: None
         self._handlers = []
@@ -50,7 +45,7 @@ class DiffMap(gtk.DrawingArea):
 
         self._scrolladj = scrollbar.get_adjustment()
         self.on_scrollbar_style_set(scrollbar, None)
-        self.on_scrollbar_size_allocate(scrollbar, scrollbar.allocation)
+        self.on_scrollbar_size_allocate(scrollbar, scrollbar.get_allocation())
         scrollbar.ensure_style()
         scroll_style_hid = scrollbar.connect("style-set",
                                              self.on_scrollbar_style_set)
@@ -74,11 +69,22 @@ class DiffMap(gtk.DrawingArea):
         self.queue_draw()
 
     def on_scrollbar_style_set(self, scrollbar, previous_style):
-        stepper_size = scrollbar.style_get_property("stepper-size")
-        steppers = [scrollbar.style_get_property(x) for x in
-                    ("has-backward-stepper", "has-secondary-forward-stepper",
-                     "has-secondary-backward-stepper", "has-forward-stepper")]
-        stepper_spacing = scrollbar.style_get_property("stepper-spacing")
+        value = GObject.Value(int)
+        scrollbar.style_get_property("stepper-size", value)
+        stepper_size = value.get_int()
+        scrollbar.style_get_property("stepper-spacing", value)
+        stepper_spacing = value.get_int()
+
+        bool_value = GObject.Value(bool)
+        scrollbar.style_get_property("has-backward-stepper", bool_value)
+        has_backward = bool_value.get_boolean()
+        scrollbar.style_get_property("has-secondary-forward-stepper", bool_value)
+        has_secondary_forward = bool_value.get_boolean()
+        scrollbar.style_get_property("has-secondary-backward-stepper", bool_value)
+        has_secondary_backward = bool_value.get_boolean()
+        scrollbar.style_get_property("has-forward-stepper", bool_value)
+        has_foreward = bool_value.get_boolean()
+        steppers = [has_backward, has_secondary_forward, has_secondary_backward, has_foreward]
 
         offset = stepper_size * steppers[0:2].count(True)
         shorter = stepper_size * steppers.count(True)
@@ -96,16 +102,16 @@ class DiffMap(gtk.DrawingArea):
         self._scroll_height = allocation.height
         self.queue_draw()
 
-    def do_expose_event(self, event):
+    def do_draw(self, context):
         if not self._setup:
             return
         height = self._scroll_height - self._h_offset - 1
-        y_start = self._scroll_y - self.allocation.y + self._y_offset + 1
-        xpad = self.style_get_property('x-padding')
+        y_start = self._scroll_y - self.get_allocation().y - self._y_offset + 1
+        width = self.get_allocated_width()
+        xpad = 2.5
         x0 = xpad
-        x1 = self.allocation.width - 2 * xpad
+        x1 = width - 2 * xpad
 
-        context = self.window.cairo_create()
         context.translate(0, y_start)
         context.set_line_width(1)
         context.rectangle(x0 - 3, -1, x1 + 6, height + 1)
@@ -116,19 +122,19 @@ class DiffMap(gtk.DrawingArea):
             tagged_diffs[c].append((y0, y1))
 
         for tag, diffs in tagged_diffs.items():
-            context.set_source_color(self.fill_colors[tag])
+            context.set_source_rgba(*self.fill_colors[tag])
             for y0, y1 in diffs:
                 y0, y1 = round(y0 * height) - 0.5, round(y1 * height) - 0.5
                 context.rectangle(x0, y0, x1, y1 - y0)
             context.fill_preserve()
-            context.set_source_color(self.line_colors[tag])
+            context.set_source_rgba(*self.line_colors[tag])
             context.stroke()
 
         page_color = (0., 0., 0., 0.1)
         page_outline_color = (0.0, 0.0, 0.0, 0.3)
         adj = self._scrolladj
-        s = round(height * (adj.value / adj.upper)) - 0.5
-        e = round(height * (adj.page_size / adj.upper))
+        s = round(height * (adj.get_value() / adj.get_upper())) - 0.5
+        e = round(height * (adj.get_page_size() / adj.get_upper()))
         context.set_source_rgba(*page_color)
         context.rectangle(x0 - 2, s, x1 + 4, e)
         context.fill_preserve()
@@ -137,30 +143,16 @@ class DiffMap(gtk.DrawingArea):
 
     def do_button_press_event(self, event):
         if event.button == 1:
-            y_start = self.allocation.y - self._scroll_y - self._y_offset
+            y_start = self.get_allocation().y - self._scroll_y - self._y_offset
             total_height = self._scroll_height - self._h_offset
             fraction = (event.y + y_start) / total_height
 
             adj = self._scrolladj
-            val = fraction * adj.upper - adj.page_size / 2
-            upper = adj.upper - adj.page_size
-            adj.set_value(max(min(upper, val), adj.lower))
+            val = fraction * adj.get_upper() - adj.get_page_size() / 2
+            upper = adj.get_upper() - adj.get_page_size()
+            adj.set_value(max(min(upper, val), adj.get_lower()))
             return True
         return False
 
-    def do_size_request(self, request):
-        request.width = self.style_get_property('width')
-
-gtk.widget_class_install_style_property(DiffMap,
-                                        ('width', float,
-                                         'Width',
-                                         'Width of the bar',
-                                         0.0, gobject.G_MAXFLOAT, 20,
-                                         gobject.PARAM_READABLE))
-gtk.widget_class_install_style_property(DiffMap,
-                                        ('x-padding', float,
-                                         'Width-wise padding',
-                                         'Padding to be left between left and '
-                                         'right edges and change blocks',
-                                         0.0, gobject.G_MAXFLOAT, 3.5,
-                                         gobject.PARAM_READABLE))
+    def do_get_preferred_width(self):
+        return 20, 20
diff --git a/meld/diffutil.py b/meld/diffutil.py
index b393a57..446b416 100644
--- a/meld/diffutil.py
+++ b/meld/diffutil.py
@@ -16,7 +16,7 @@
 ### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 ### USA.
 
-import gobject
+from gi.repository import GObject
 
 from .matchers import DiffChunk, MyersSequenceMatcher, \
     SyncPointMyersSequenceMatcher
@@ -60,11 +60,11 @@ def consume_blank_lines(chunk, texts, pane1, pane2):
     return DiffChunk._make((tag, c1, c2, c3, c4))
 
 
-class Differ(gobject.GObject):
+class Differ(GObject.GObject):
     """Utility class to hold diff2 or diff3 chunks"""
 
     __gsignals__ = {
-        'diffs-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+        'diffs-changed': (GObject.SignalFlags.RUN_FIRST, None,
                                                     (object,)),
     }
 
@@ -73,7 +73,7 @@ class Differ(gobject.GObject):
 
     def __init__(self):
         # Internally, diffs are stored from text1 -> text0 and text1 -> text2.
-        gobject.GObject.__init__(self)
+        GObject.GObject.__init__(self)
         self.num_sequences = 0
         self.seqlength = [0, 0, 0]
         self.diffs = [[], []]
diff --git a/meld/dirdiff.py b/meld/dirdiff.py
index 7eb68ac..4941dfc 100644
--- a/meld/dirdiff.py
+++ b/meld/dirdiff.py
@@ -27,10 +27,11 @@ import stat
 import sys
 import time
 
-import gio
-import gobject
-import gtk
-import gtk.keysyms
+from gi.repository import GLib
+from gi.repository import Gio
+from gi.repository import GObject
+from gi.repository import Gdk
+from gi.repository import Gtk
 
 from . import melddoc
 from . import tree
@@ -45,7 +46,6 @@ from gettext import gettext as _
 from gettext import ngettext
 from .meldapp import app
 
-gdk = gtk.gdk
 
 ################################################################################
 #
@@ -302,15 +302,15 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
             self.focus_in_events.append(handler_id)
             handler_id = treeview.connect("focus-out-event", self.on_treeview_focus_out_event)
             self.focus_out_events.append(handler_id)
-            treeview.set_search_equal_func(self.model.treeview_search_cb)
+            treeview.set_search_equal_func(self.model.treeview_search_cb, None)
         self.current_path, self.prev_path, self.next_path = None, None, None
         self.on_treeview_focus_out_event(None, None)
         self.focus_pane = None
 
-        lastchanged_label = gtk.Label()
+        lastchanged_label = Gtk.Label()
         lastchanged_label.set_size_request(100, -1)
         lastchanged_label.show()
-        permissions_label = gtk.Label()
+        permissions_label = Gtk.Label()
         permissions_label.set_size_request(100, -1)
         permissions_label.show()
         self.status_info_labels = [lastchanged_label, permissions_label]
@@ -320,12 +320,12 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
         for i in range(3):
             col_index = self.model.column_index
             # Create icon and filename CellRenderer
-            column = gtk.TreeViewColumn(_("Name"))
+            column = Gtk.TreeViewColumn(_("Name"))
             column.set_resizable(True)
-            rentext = gtk.CellRendererText()
+            rentext = Gtk.CellRendererText()
             renicon = emblemcellrenderer.EmblemCellRenderer()
-            column.pack_start(renicon, expand=0)
-            column.pack_start(rentext, expand=1)
+            column.pack_start(renicon, False)
+            column.pack_start(rentext, True)
             column.set_attributes(rentext, markup=col_index(tree.COL_TEXT, i),
                                   foreground_gdk=col_index(tree.COL_FG, i),
                                   style=col_index(tree.COL_STYLE, i),
@@ -338,26 +338,26 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
             self.treeview[i].append_column(column)
             self.columns_dict[i]["name"] = column
             # Create file size CellRenderer
-            column = gtk.TreeViewColumn(_("Size"))
+            column = Gtk.TreeViewColumn(_("Size"))
             column.set_resizable(True)
-            rentext = gtk.CellRendererText()
-            column.pack_start(rentext, expand=1)
+            rentext = Gtk.CellRendererText()
+            column.pack_start(rentext, True)
             column.set_attributes(rentext, markup=col_index(COL_SIZE, i))
             self.treeview[i].append_column(column)
             self.columns_dict[i]["size"] = column
             # Create date-time CellRenderer
-            column = gtk.TreeViewColumn(_("Modification time"))
+            column = Gtk.TreeViewColumn(_("Modification time"))
             column.set_resizable(True)
-            rentext = gtk.CellRendererText()
-            column.pack_start(rentext, expand=1)
+            rentext = Gtk.CellRendererText()
+            column.pack_start(rentext, True)
             column.set_attributes(rentext, markup=col_index(COL_TIME, i))
             self.treeview[i].append_column(column)
             self.columns_dict[i]["modification time"] = column
             # Create permissions CellRenderer
-            column = gtk.TreeViewColumn(_("Permissions"))
+            column = Gtk.TreeViewColumn(_("Permissions"))
             column.set_resizable(True)
-            rentext = gtk.CellRendererText()
-            column.pack_start(rentext, expand=0)
+            rentext = Gtk.CellRendererText()
+            column.pack_start(rentext, False)
             column.set_attributes(rentext, markup=col_index(COL_PERMS, i))
             self.treeview[i].append_column(column)
             self.columns_dict[i]["permissions"] = column
@@ -365,7 +365,7 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
 
         for i in range(3):
             selection = self.treeview[i].get_selection()
-            selection.set_mode(gtk.SELECTION_MULTIPLE)
+            selection.set_mode(Gtk.SelectionMode.MULTIPLE)
             selection.connect('changed', self.on_treeview_selection_changed, i)
             self.scrolledwindow[i].get_vadjustment().connect(
                 "value-changed", self._sync_vscroll)
@@ -381,10 +381,14 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
                 self.actiongroup.get_action(action_name).set_active(True)
 
     def on_style_set(self, widget, prev_style):
-        style = widget.get_style()
+        style = widget.get_style_context()
 
-        lookup = lambda color_id, default: style.lookup_color(color_id) or \
-                                           gtk.gdk.color_parse(default)
+        def lookup(name, default):
+            found, colour = style.lookup_color(name)
+            if not found:
+                colour = Gdk.RGBA()
+                colour.parse(default)
+            return colour
 
         self.fill_colors = {"insert"  : lookup("insert-bg", "DarkSeaGreen1"),
                             "delete"  : lookup("delete-bg", "White"),
@@ -434,9 +438,10 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
         if item.get_active():
             self.custom_popup.connect("deactivate",
                                       lambda popup: item.set_active(False))
-            self.custom_popup.popup(None, None, misc.position_menu_under_widget,
-                                    1, gtk.get_current_event_time(),
-                                    self.filter_menu_button)
+            self.custom_popup.popup(None, None,
+                                    misc.position_menu_under_widget,
+                                    self.filter_menu_button, 1,
+                                    Gtk.get_current_event_time())
 
     def _cleanup_filter_menu_button(self, ui):
         if self.popup_deactivate_id:
@@ -489,12 +494,12 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
             name = "Hide%d" % i
             callback = lambda b, i=i: self._update_name_filter(b, i)
             actions.append((name, None, f.label, None, _("Hide %s") % f.label, callback, f.active))
-            self.filter_ui.append(["/CustomPopup" , name, name, gtk.UI_MANAGER_MENUITEM, False])
-            self.filter_ui.append(["/Menubar/ViewMenu/FileFilters" , name, name, gtk.UI_MANAGER_MENUITEM, 
False])
+            self.filter_ui.append(["/CustomPopup" , name, name, Gtk.UIManagerItemType.MENUITEM, False])
+            self.filter_ui.append(["/Menubar/ViewMenu/FileFilters" , name, name, 
Gtk.UIManagerItemType.MENUITEM, False])
             if f.filter is None:
                 disabled_actions.append(name)
 
-        self.filter_actiongroup = gtk.ActionGroup("DirdiffFilterActions")
+        self.filter_actiongroup = Gtk.ActionGroup("DirdiffFilterActions")
         self.filter_actiongroup.add_toggle_actions(actions)
         for name in disabled_actions:
             self.filter_actiongroup.get_action(name).set_sensitive(False)
@@ -531,11 +536,13 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
 
     def _sync_vscroll(self, adjustment):
         adjs = [sw.get_vadjustment() for sw in self.scrolledwindow]
-        self._do_to_others(adjustment, adjs, "set_value", (adjustment.value, ))
+        self._do_to_others(adjustment, adjs, "set_value",
+                           (adjustment.get_value(), ))
 
     def _sync_hscroll(self, adjustment):
         adjs = [sw.get_hadjustment() for sw in self.scrolledwindow]
-        self._do_to_others(adjustment, adjs, "set_value", (adjustment.value, ))
+        self._do_to_others(adjustment, adjs, "set_value",
+                           (adjustment.get_value(), ))
 
     def _get_focused_pane(self):
         for i, treeview in enumerate(self.treeview):
@@ -590,7 +597,7 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
         self._update_diffmaps()
 
     def get_comparison(self):
-        root = self.model.get_iter_root()
+        root = self.model.get_iter_first()
         if root:
             folders = self.model.value_paths(root)
         else:
@@ -616,7 +623,10 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
         yield _("[%s] Scanning %s") % (self.label_text, "")
         prefixlen = 1 + len( self.model.value_path( self.model.get_iter(rootpath), 0 ) )
         symlinks_followed = set()
-        todo = [ rootpath ]
+        # TODO: This is horrible.
+        if isinstance(rootpath, tuple):
+            rootpath = Gtk.TreePath(rootpath)
+        todo = [rootpath]
         expanded = set()
 
         shadowed_entries = []
@@ -775,13 +785,13 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
                 else:
                     continue
                 secondary = "\n".join(messages)
-                self.add_dismissable_msg(pane, gtk.STOCK_DIALOG_ERROR, header,
+                self.add_dismissable_msg(pane, Gtk.STOCK_DIALOG_ERROR, header,
                                          secondary)
 
     def add_dismissable_msg(self, pane, icon, primary, secondary):
         msgarea = self.msgarea_mgr[pane].new_from_text_and_icon(
                         icon, primary, secondary)
-        msgarea.add_button(_("Hi_de"), gtk.RESPONSE_CLOSE)
+        msgarea.add_button(_("Hi_de"), Gtk.ResponseType.CLOSE)
         msgarea.connect("response",
                         lambda *args: self.msgarea_mgr[pane].clear())
         msgarea.show_all()
@@ -814,7 +824,7 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
                         if os.path.exists(dst):
                             if misc.run_dialog( _("'%s' exists.\nOverwrite?") % os.path.basename(dst),
                                     parent = self,
-                                    buttonstype = gtk.BUTTONS_OK_CANCEL) != gtk.RESPONSE_OK:
+                                    buttonstype = Gtk.ButtonsType.OK_CANCEL) != Gtk.ResponseType.OK:
                                 continue
                         misc.copytree(src, dst)
                         self.recursively_update( path )
@@ -822,9 +832,9 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
                     misc.error_dialog(
                         _("Error copying file"),
                         _("Couldn't copy %s\nto %s.\n\n%s") % (
-                            gobject.markup_escape_text(src),
-                            gobject.markup_escape_text(dst),
-                            gobject.markup_escape_text(str(err)),
+                            GObject.markup_escape_text(src),
+                            GObject.markup_escape_text(dst),
+                            GObject.markup_escape_text(str(err)),
                         )
                     )
 
@@ -840,10 +850,10 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
                 it = self.model.get_iter(path)
                 name = self.model.value_path(it, pane)
                 try:
-                    gfile = gio.File(name)
-                    gfile.trash()
+                    gfile = Gio.File.new_for_path(name)
+                    gfile.trash(None)
                     self.file_deleted(path, pane)
-                except gio.Error as e:
+                except GLib.GError as e:
                     misc.error_dialog(_("Error deleting %s") % name, str(e))
 
     def on_treemodel_row_deleted(self, model, path):
@@ -1002,10 +1012,10 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
     def on_treeview_key_press_event(self, view, event):
         pane = self.treeview.index(view)
         tree = None
-        if gtk.keysyms.Right == event.keyval:
+        if Gdk.KEY_Right == event.keyval:
             if pane+1 < self.num_panes:
                 tree = self.treeview[pane+1]
-        elif gtk.keysyms.Left == event.keyval:
+        elif Gdk.KEY_Left == event.keyval:
             if pane-1 >= 0:
                 tree = self.treeview[pane-1]
         if tree is not None:
@@ -1018,7 +1028,7 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
                 for p in paths:
                     tree.get_selection().select_path(p)
             tree.emit("cursor-changed")
-        return event.keyval in (gtk.keysyms.Left, gtk.keysyms.Right) #handled
+        return event.keyval in (Gdk.KEY_Left, Gdk.KEY_Right) #handled
 
     def on_treeview_row_activated(self, view, path, column):
         pane = self.treeview.index(view)
@@ -1270,8 +1280,8 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
             time = event.time
         else:
             button = 0
-            time = gtk.get_current_event_time()
-        self.popup_menu.popup(None, None, None, button, time)
+            time = Gtk.get_current_event_time()
+        self.popup_menu.popup(None, None, None, None, button, time)
 
     def on_treeview_popup_menu(self, treeview):
         self.popup_in_pane(self.treeview.index(treeview), None)
@@ -1351,13 +1361,13 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
                 self.num_panes = n
 
     def refresh(self):
-        root = self.model.get_iter_root()
+        root = self.model.get_iter_first()
         if root:
             roots = self.model.value_paths(root)
             self.set_locations( roots )
 
     def recompute_label(self):
-        root = self.model.get_iter_root()
+        root = self.model.get_iter_first()
         filenames = self.model.value_paths(root)
         if self.custom_labels:
             label_options = zip(self.custom_labels, filenames)
@@ -1388,7 +1398,7 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
         changed_paths = []
         # search each panes tree for changed_filename
         for pane in range(self.num_panes):
-            it = model.get_iter_root()
+            it = model.get_iter_first()
             current = model.value_path(it, pane).split(os.sep)
             changed = changed_filename.split(os.sep)
             # early exit. does filename begin with root?
@@ -1424,7 +1434,7 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
             pane = self.treeview.index(self.focus_pane)
         else:
             pane = 0
-        if direction == gtk.gdk.SCROLL_UP:
+        if direction == Gdk.ScrollDirection.UP:
             path = self.prev_path
         else:
             path = self.next_path
@@ -1439,7 +1449,7 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
         for h in self.app_handlers:
             app.disconnect(h)
 
-        return gtk.RESPONSE_OK
+        return Gtk.ResponseType.OK
 
     def on_find_activate(self, *extra):
         self.focus_pane.emit("start-interactive-search")
diff --git a/meld/filediff.py b/meld/filediff.py
index f2951a9..fc4b4ea 100644
--- a/meld/filediff.py
+++ b/meld/filediff.py
@@ -27,11 +27,11 @@ from multiprocessing import Pool
 from multiprocessing.pool import ThreadPool
 
 
-import pango
-import glib
-import gobject
-import gtk
-import gtk.keysyms
+from gi.repository import GLib
+from gi.repository import Pango
+from gi.repository import GObject
+from gi.repository import Gdk
+from gi.repository import Gtk
 
 from . import diffutil
 from . import matchers
@@ -81,7 +81,7 @@ class CachedSequenceMatcher(object):
         except KeyError:
             def inline_cb(opcodes):
                 self.cache[(text1, textn)] = [opcodes, time.time()]
-                gobject.idle_add(lambda: cb(opcodes))
+                GObject.idle_add(lambda: cb(opcodes))
             self.process_pool.apply_async(matchers.matcher_worker,
                                           (text1, textn),
                                           callback=inline_cb)
@@ -129,7 +129,7 @@ class TextviewLineAnimation(object):
         self.end_mark = mark1
         self.start_rgba = rgba0
         self.end_rgba = rgba1
-        self.start_time = glib.get_current_time()
+        self.start_time = GLib.get_current_time()
         self.duration = duration
 
 
@@ -139,17 +139,19 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
 
     differ = diffutil.Differ
 
-    keylookup = {gtk.keysyms.Shift_L : MASK_SHIFT,
-                 gtk.keysyms.Control_L : MASK_CTRL,
-                 gtk.keysyms.Shift_R : MASK_SHIFT,
-                 gtk.keysyms.Control_R : MASK_CTRL}
+    keylookup = {
+        Gdk.KEY_Shift_L: MASK_SHIFT,
+        Gdk.KEY_Shift_R: MASK_SHIFT,
+        Gdk.KEY_Control_L: MASK_CTRL,
+        Gdk.KEY_Control_R: MASK_CTRL,
+    }
 
     # Identifiers for MsgArea messages
     (MSG_SAME, MSG_SLOW_HIGHLIGHT, MSG_SYNCPOINTS) = list(range(3))
 
     __gsignals__ = {
-        'next-conflict-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (bool, bool)),
-        'action-mode-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (int,)),
+        'next-conflict-changed': (GObject.SignalFlags.RUN_FIRST, None, (bool, bool)),
+        'action-mode-changed': (GObject.SignalFlags.RUN_FIRST, None, (int,)),
     }
 
     def __init__(self, prefs, num_panes):
@@ -164,17 +166,17 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
 
         # This SizeGroup isn't actually necessary for FileDiff; it's for
         # handling non-homogenous selectors in FileComp. It's also fragile.
-        column_sizes = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+        column_sizes = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
         column_sizes.set_ignore_hidden(True)
         for widget in self.selector_hbox:
             column_sizes.add_widget(widget)
 
         self.warned_bad_comparison = False
         # Some sourceviews bind their own undo mechanism, which we replace
-        gtk.binding_entry_remove(srcviewer.GtkTextView, gtk.keysyms.z,
-                                 gtk.gdk.CONTROL_MASK)
-        gtk.binding_entry_remove(srcviewer.GtkTextView, gtk.keysyms.z,
-                                 gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK)
+        # Gtk.binding_entry_remove(srcviewer.GtkTextView, Gdk.KEY_z,
+        #                          Gdk.ModifierType.CONTROL_MASK)
+        # Gtk.binding_entry_remove(srcviewer.GtkTextView, Gdk.KEY_z,
+        #                          Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK)
         for v in self.textview:
             buf = meldbuffer.MeldBuffer()
             buf.connect('begin_user_action',
@@ -229,7 +231,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             ("SaveAll", None, _("Save A_ll"), "<Ctrl><Shift>L",
                 _("Save all files in the current comparison"),
                 self.on_save_all_activate),
-            ("Revert", gtk.STOCK_REVERT_TO_SAVED, None, None,
+            ("Revert", Gtk.STOCK_REVERT_TO_SAVED, None, None,
                 _("Revert files to their saved versions"),
                 self.on_revert_activate),
             ("SplitAdd", None, _("Add Synchronization Point"), None,
@@ -241,23 +243,23 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                 self.clear_sync_points),
             ("PrevConflict", None, _("Previous Conflict"), "<Ctrl>I",
                 _("Go to the previous conflict"),
-                lambda x: self.on_next_conflict(gtk.gdk.SCROLL_UP)),
+                lambda x: self.on_next_conflict(Gdk.ScrollDirection.UP)),
             ("NextConflict", None, _("Next Conflict"), "<Ctrl>K",
                 _("Go to the next conflict"),
-                lambda x: self.on_next_conflict(gtk.gdk.SCROLL_DOWN)),
-            ("PushLeft", gtk.STOCK_GO_BACK, _("Push to Left"), "<Alt>Left",
+                lambda x: self.on_next_conflict(Gdk.ScrollDirection.DOWN)),
+            ("PushLeft", Gtk.STOCK_GO_BACK, _("Push to Left"), "<Alt>Left",
                 _("Push current change to the left"),
                 lambda x: self.push_change(-1)),
-            ("PushRight", gtk.STOCK_GO_FORWARD,
+            ("PushRight", Gtk.STOCK_GO_FORWARD,
                 _("Push to Right"), "<Alt>Right",
                 _("Push current change to the right"),
                 lambda x: self.push_change(1)),
             # FIXME: using LAST and FIRST is terrible and unreliable icon abuse
-            ("PullLeft", gtk.STOCK_GOTO_LAST,
+            ("PullLeft", Gtk.STOCK_GOTO_LAST,
                 _("Pull from Left"), "<Alt><Shift>Right",
                 _("Pull change from the left"),
                 lambda x: self.pull_change(-1)),
-            ("PullRight", gtk.STOCK_GOTO_FIRST,
+            ("PullRight", Gtk.STOCK_GOTO_FIRST,
                 _("Pull from Right"), "<Alt><Shift>Left",
                 _("Pull change from the right"),
                 lambda x: self.pull_change(1)),
@@ -273,7 +275,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             ("CopyRightDown", None, _("Copy Below Right"), "<Alt>quoteright",
                 _("Copy change below the right chunk"),
                 lambda x: self.copy_change(1, 1)),
-            ("Delete", gtk.STOCK_DELETE, _("Delete"), "<Alt>Delete",
+            ("Delete", Gtk.STOCK_DELETE, _("Delete"), "<Alt>Delete",
                 _("Delete change"),
                 self.delete_change),
             ("MergeFromLeft", None, _("Merge All from Left"), None,
@@ -300,7 +302,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         )
 
         self.ui_file = gnomeglade.ui_file("filediff-ui.xml")
-        self.actiongroup = gtk.ActionGroup('FilediffPopupActions')
+        self.actiongroup = Gtk.ActionGroup('FilediffPopupActions')
         self.actiongroup.set_translation_domain("meld")
         self.actiongroup.add_actions(actions)
         self.actiongroup.add_toggle_actions(toggle_actions)
@@ -313,7 +315,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         self.widget.connect("style-set", self.on_style_set)
 
         self.set_num_panes(num_panes)
-        gobject.idle_add( lambda *args: self.load_font()) # hack around Bug 316730
+        GObject.idle_add( lambda *args: self.load_font()) # hack around Bug 316730
         self.cursor = CursorDetails()
         self.connect("current-diff-changed", self.on_current_diff_changed)
         for t in self.textview:
@@ -323,9 +325,9 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         self.undosequence.connect("checkpointed", self.on_undo_checkpointed)
         self.connect("next-conflict-changed", self.on_next_conflict_changed)
 
-        overwrite_label = gtk.Label()
+        overwrite_label = Gtk.Label()
         overwrite_label.show()
-        cursor_label = gtk.Label()
+        cursor_label = Gtk.Label()
         cursor_label.show()
         self.status_info_labels = [overwrite_label, cursor_label]
 
@@ -345,13 +347,17 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
     def on_style_set(self, widget, prev_style):
         style = widget.get_style()
 
-        lookup = lambda color_id, default: style.lookup_color(color_id) or \
-                                           gtk.gdk.color_parse(default)
+        def lookup(name, default):
+            found, colour = style.lookup_color(name)
+            if not found:
+                colour = Gdk.RGBA()
+                colour.parse(default)
+            return colour
 
         for buf in self.textbuffer:
             tag = buf.get_tag_table().lookup("inline")
-            tag.props.background = lookup("inline-bg", "LightSteelBlue2")
-            tag.props.foreground = lookup("inline-fg", "Red")
+            tag.props.background_rgba = lookup("inline-bg", "LightSteelBlue2")
+            tag.props.foreground_rgba = lookup("inline-fg", "Red")
 
         self.fill_colors = {"insert"  : lookup("insert-bg", "DarkSeaGreen1"),
                             "delete"  : lookup("insert-bg", "DarkSeaGreen1"),
@@ -531,9 +537,9 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         self.actiongroup.get_action("NextConflict").set_sensitive(have_next)
 
     def on_next_conflict(self, direction):
-        if direction == gtk.gdk.SCROLL_DOWN:
+        if direction == Gdk.ScrollDirection.DOWN:
             target = self.cursor.next_conflict
-        else: # direction == gtk.gdk.SCROLL_UP
+        else: # direction == Gdk.ScrollDirection.UP
             target = self.cursor.prev_conflict
 
         if target is None:
@@ -542,7 +548,8 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         buf = self.textbuffer[self.cursor.pane]
         chunk = self.linediffer.get_chunk(target, self.cursor.pane)
         buf.place_cursor(buf.get_iter_at_line(chunk[1]))
-        self.textview[self.cursor.pane].scroll_to_mark(buf.get_insert(), 0.1)
+        self.textview[self.cursor.pane].scroll_to_mark(
+            buf.get_insert(), 0.1, True, 0.5, 0.5)
 
     def push_change(self, direction):
         src = self._get_focused_pane()
@@ -710,7 +717,8 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         new_buf = self.textbuffer[new_pane]
         self.textview[new_pane].grab_focus()
         new_buf.place_cursor(new_buf.get_iter_at_line(new_line))
-        self.textview[new_pane].scroll_to_mark(new_buf.get_insert(), 0.1)
+        self.textview[new_pane].scroll_to_mark(
+            new_buf.get_insert(), 0.1, True, 0.5, 0.5)
 
     def on_textview_focus_in_event(self, view, event):
         self.focus_pane = view
@@ -772,16 +780,16 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         self.deleted_lines_pending = -1
 
     def load_font(self):
-        fontdesc = pango.FontDescription(self.prefs.get_current_font())
+        fontdesc = Pango.FontDescription(self.prefs.get_current_font())
         context = self.textview0.get_pango_context()
         metrics = context.get_metrics( fontdesc, context.get_language() )
         line_height_points = metrics.get_ascent() + metrics.get_descent()
         self.pixels_per_line = line_height_points // 1024
         self.pango_char_width = metrics.get_approximate_char_width()
-        tabs = pango.TabArray(10, 0)
+        tabs = Pango.TabArray.new(10, 0)
         tab_size = self.prefs.tab_size
         for i in range(10):
-            tabs.set_tab(i, pango.TAB_LEFT, i*tab_size*self.pango_char_width)
+            tabs.set_tab(i, Pango.TabAlign.LEFT, i*tab_size*self.pango_char_width)
         for i in range(3):
             self.textview[i].modify_font(fontdesc)
             self.textview[i].set_tabs(tabs)
@@ -790,9 +798,9 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
 
     def on_preference_changed(self, key, value):
         if key == "tab_size":
-            tabs = pango.TabArray(10, 0)
+            tabs = Pango.TabArray.new(10, 0)
             for i in range(10):
-                tabs.set_tab(i, pango.TAB_LEFT, i*value*self.pango_char_width)
+                tabs.set_tab(i, Pango.TabAlign.LEFT, i*value*self.pango_char_width)
             for i in range(3):
                 self.textview[i].set_tabs(tabs)
             for t in self.textview:
@@ -825,25 +833,25 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             self.refresh_comparison()
 
     def on_key_press_event(self, object, event):
-        # The correct way to handle these modifiers would be to use
-        # gdk_keymap_get_modifier_state method, available from GDK 3.4.
-        keymap = gtk.gdk.keymap_get_default()
-        x = self.keylookup.get(keymap.translate_keyboard_state(
-                               event.hardware_keycode, 0, event.group)[0], 0)
-        if self.keymask | x != self.keymask:
-            self.keymask |= x
-        elif event.keyval == gtk.keysyms.Escape:
+        keymap = Gdk.Keymap.get_default()
+        ok, keyval, group, lvl, consumed = keymap.translate_keyboard_state(
+            event.hardware_keycode, 0, event.group)
+        mod_key = self.keylookup.get(keyval, 0)
+        if self.keymask | mod_key != self.keymask:
+            self.keymask |= mod_key
+        elif event.keyval == Gdk.KEY_Escape:
             self.findbar.hide()
 
     def on_key_release_event(self, object, event):
-        keymap = gtk.gdk.keymap_get_default()
-        x = self.keylookup.get(keymap.translate_keyboard_state(
-                               event.hardware_keycode, 0, event.group)[0], 0)
-        if self.keymask & ~x != self.keymask:
-            self.keymask &= ~x
+        keymap = Gdk.Keymap.get_default()
+        ok, keyval, group, level, consumed = keymap.translate_keyboard_state(
+            event.hardware_keycode, 0, event.group)
+        mod_key = self.keylookup.get(keyval, 0)
+        if self.keymask & ~mod_key != self.keymask:
+            self.keymask &= ~mod_key
 
     def check_save_modified(self, label=None):
-        response = gtk.RESPONSE_OK
+        response = Gtk.ResponseType.OK
         modified = [b.data.modified for b in self.textbuffer]
         if True in modified:
             dialog = gnomeglade.Component("filediff.ui", "check_save_dialog")
@@ -854,28 +862,29 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             # but this is unbound on currently required PyGTK.
             buttons = []
             for i in range(self.num_panes):
-                button = gtk.CheckButton(self.textbuffer[i].data.label)
+                button = Gtk.CheckButton(self.textbuffer[i].data.label)
                 button.set_use_underline(False)
                 button.set_sensitive(modified[i])
                 button.set_active(modified[i])
-                dialog.extra_vbox.pack_start(button, expand=True, fill=True)
+                dialog.extra_vbox.pack_start(button, expand=True, fill=True,
+                                             padding=0)
                 buttons.append(button)
             dialog.extra_vbox.show_all()
             response = dialog.widget.run()
             try_save = [b.get_active() for b in buttons]
             dialog.widget.destroy()
-            if response == gtk.RESPONSE_OK:
+            if response == Gtk.ResponseType.OK:
                 for i in range(self.num_panes):
                     if try_save[i]:
                         if not self.save_file(i):
-                            return gtk.RESPONSE_CANCEL
-            elif response == gtk.RESPONSE_DELETE_EVENT:
-                response = gtk.RESPONSE_CANCEL
+                            return Gtk.ResponseType.CANCEL
+            elif response == Gtk.ResponseType.DELETE_EVENT:
+                response = Gtk.ResponseType.CANCEL
         return response
 
     def on_delete_event(self, appquit=0):
         response = self.check_save_modified()
-        if response == gtk.RESPONSE_OK:
+        if response == Gtk.ResponseType.OK:
             for h in self.app_handlers:
                 app.disconnect(h)
         return response
@@ -936,7 +945,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             # Ideally, this would check whether the clipboard included
             # something pasteable. However, there is no changed signal.
             # widget.get_clipboard(
-            #    gtk.gdk.SELECTION_CLIPBOARD).wait_is_text_available()
+            #    Gdk.SELECTION_CLIPBOARD).wait_is_text_available()
             paste = widget.get_editable()
         if self.main_actiongroup:
             for action, sens in zip(
@@ -977,14 +986,14 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             self.linkmap[1].queue_draw()
 
     def on_textview_popup_menu(self, textview):
-        self.popup_menu.popup(None, None, None, 0,
-                              gtk.get_current_event_time())
+        self.popup_menu.popup(None, None, None, None, 0,
+                              Gtk.get_current_event_time())
         return True
 
     def on_textview_button_press_event(self, textview, event):
         if event.button == 3:
             textview.grab_focus()
-            self.popup_menu.popup(None, None, None, event.button, event.time)
+            self.popup_menu.popup(None, None, None, None, event.button, event.time)
             return True
         return False
 
@@ -1037,13 +1046,14 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             if self.textbuffer[i].data.modified:
                 shortnames[i] += "*"
                 if self.textbuffer[i].data.writable:
-                    stock = gtk.STOCK_SAVE
+                    stock = Gtk.STOCK_SAVE
                 else:
-                    stock = gtk.STOCK_SAVE_AS
+                    stock = Gtk.STOCK_SAVE_AS
             if stock:
                 self.statusimage[i].show()
-                self.statusimage[i].set_from_stock(stock, gtk.ICON_SIZE_MENU)
-                self.statusimage[i].set_size_request(self.diffmap[0].size_request()[0],-1)
+                self.statusimage[i].set_from_stock(stock, Gtk.IconSize.MENU)
+                width = self.diffmap[0].size_request().width
+                self.statusimage[i].set_size_request(width, -1)
             else:
                 self.statusimage[i].hide()
         self.label_text = " : ".join(shortnames)
@@ -1089,7 +1099,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         def add_dismissable_msg(pane, icon, primary, secondary):
             msgarea = self.msgarea_mgr[pane].new_from_text_and_icon(
                             icon, primary, secondary)
-            msgarea.add_button(_("Hi_de"), gtk.RESPONSE_CLOSE)
+            msgarea.add_button(_("Hi_de"), Gtk.ResponseType.CLOSE)
             msgarea.connect("response",
                             lambda *args: self.msgarea_mgr[pane].clear())
             msgarea.show_all()
@@ -1105,7 +1115,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                     tasks.append(task)
                 except (IOError, LookupError) as e:
                     buf.delete(*buf.get_bounds())
-                    add_dismissable_msg(pane, gtk.STOCK_DIALOG_ERROR,
+                    add_dismissable_msg(pane, Gtk.STOCK_DIALOG_ERROR,
                                         _("Could not read file"), str(e))
         yield _("[%s] Reading files") % self.label_text
         while len(tasks):
@@ -1114,8 +1124,8 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                     nextbit = t.file.read(4096)
                     if nextbit.find("\x00") != -1:
                         t.buf.delete(*t.buf.get_bounds())
-                        filename = gobject.markup_escape_text(t.filename)
-                        add_dismissable_msg(t.pane, gtk.STOCK_DIALOG_ERROR,
+                        filename = GObject.markup_escape_text(t.filename)
+                        add_dismissable_msg(t.pane, Gtk.STOCK_DIALOG_ERROR,
                             _("Could not read file"),
                             _("%s appears to be a binary file.") % filename)
                         tasks.remove(t)
@@ -1127,14 +1137,14 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                         t.file = io.open(t.filename, "r", encoding=t.codec[0])
                     else:
                         t.buf.delete(*t.buf.get_bounds())
-                        filename = gobject.markup_escape_text(t.filename)
-                        add_dismissable_msg(t.pane, gtk.STOCK_DIALOG_ERROR,
+                        filename = GObject.markup_escape_text(t.filename)
+                        add_dismissable_msg(t.pane, Gtk.STOCK_DIALOG_ERROR,
                                         _("Could not read file"),
                                         _("%s is not in encodings: %s") %
                                             (filename, try_codecs))
                         tasks.remove(t)
                 except IOError as ioerr:
-                    add_dismissable_msg(t.pane, gtk.STOCK_DIALOG_ERROR,
+                    add_dismissable_msg(t.pane, Gtk.STOCK_DIALOG_ERROR,
                                     _("Could not read file"), str(ioerr))
                     tasks.remove(t)
                 else:
@@ -1180,7 +1190,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
 
             if self.cursor.next is not None:
                 self.scheduler.add_task(
-                    lambda: self.next_diff(gtk.gdk.SCROLL_DOWN, True), True)
+                    lambda: self.next_diff(Gdk.ScrollDirection.DOWN, True), True)
             else:
                 buf = self.textbuffer[1 if self.num_panes > 1 else 0]
                 self.on_cursor_position_changed(buf, None, True)
@@ -1369,17 +1379,17 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                                        "Would you like to compare the "
                                        "unfiltered files?")
 
-                msgarea = mgr.new_from_text_and_icon(gtk.STOCK_INFO,
+                msgarea = mgr.new_from_text_and_icon(Gtk.STOCK_INFO,
                                                      _("Files are identical"),
                                                      secondary_text)
                 mgr.set_msg_id(FileDiff.MSG_SAME)
-                button = msgarea.add_button(_("Hide"), gtk.RESPONSE_CLOSE)
+                button = msgarea.add_button(_("Hide"), Gtk.ResponseType.CLOSE)
                 if index == 0:
                     button.props.label = _("Hi_de")
 
                 if active_filters:
                     msgarea.add_button(_("Show without filters"),
-                                       gtk.RESPONSE_OK)
+                                       Gtk.ResponseType.OK)
 
                 msgarea.connect("response", self.on_msgarea_identical_response)
                 msgarea.show_all()
@@ -1393,23 +1403,23 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         def on_msgarea_highlighting_response(msgarea, respid):
             for mgr in self.msgarea_mgr:
                 mgr.clear()
-            if respid == gtk.RESPONSE_OK:
+            if respid == Gtk.ResponseType.OK:
                 self.force_highlight = True
                 self.refresh_comparison()
 
         for index, mgr in enumerate(self.msgarea_mgr):
             msgarea = mgr.new_from_text_and_icon(
-                gtk.STOCK_INFO,
+                Gtk.STOCK_INFO,
                 _("Change highlighting incomplete"),
                 _("Some changes were not highlighted because they were too "
                   "large. You can force Meld to take longer to highlight "
                   "larger changes, though this may be slow."))
             mgr.set_msg_id(FileDiff.MSG_SLOW_HIGHLIGHT)
-            button = msgarea.add_button(_("Hi_de"), gtk.RESPONSE_CLOSE)
+            button = msgarea.add_button(_("Hi_de"), Gtk.ResponseType.CLOSE)
             if index == 0:
                 button.props.label = _("Hi_de")
             button = msgarea.add_button(
-                _("Keep highlighting"), gtk.RESPONSE_OK)
+                _("Keep highlighting"), Gtk.ResponseType.OK)
             if index == 0:
                 button.props.label = _("_Keep highlighting")
             msgarea.connect("response",
@@ -1419,36 +1429,36 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
     def on_msgarea_identical_response(self, msgarea, respid):
         for mgr in self.msgarea_mgr:
             mgr.clear()
-        if respid == gtk.RESPONSE_OK:
+        if respid == Gtk.ResponseType.OK:
             self.text_filters = []
             self.refresh_comparison()
 
-    def on_textview_expose_event(self, textview, event):
+    def on_textview_draw(self, textview, context):
         if self.num_panes == 1:
             return
-        if event.window != textview.get_window(gtk.TEXT_WINDOW_TEXT) \
-            and event.window != textview.get_window(gtk.TEXT_WINDOW_LEFT):
-            return
 
-        # Hack to redraw the line number gutter used by post-2.10 GtkSourceView
-        if event.window == textview.get_window(gtk.TEXT_WINDOW_LEFT) and \
-           self.in_nested_textview_gutter_expose:
-            self.in_nested_textview_gutter_expose = False
-            return
+        # FIXME: Update to use gtk_cairo_should_draw_window()
+
+        # if event.window != textview.get_window(Gtk.TextWindowType.TEXT) \
+        #     and event.window != textview.get_window(Gtk.TextWindowType.LEFT):
+        #     return
+
+        # # Hack to redraw the line number gutter used by post-2.10 GtkSourceView
+        # if event.window == textview.get_window(Gtk.TextWindowType.LEFT) and \
+        #    self.in_nested_textview_gutter_expose:
+        #     self.in_nested_textview_gutter_expose = False
+        #     return
 
         visible = textview.get_visible_rect()
         pane = self.textview.index(textview)
         textbuffer = textview.get_buffer()
-        area = event.area
-        x, y = textview.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET,
-                                                area.x, area.y)
+        x, y = textview.window_to_buffer_coords(Gtk.TextWindowType.WIDGET,
+                                                0, 0)
+        view_allocation = textview.get_allocation()
         bounds = (textview.get_line_num_for_y(y),
-                  textview.get_line_num_for_y(y + area.height + 1))
+                  textview.get_line_num_for_y(y + view_allocation.height + 1))
 
-        width, height = textview.allocation.width, textview.allocation.height
-        context = event.window.cairo_create()
-        context.rectangle(area.x, area.y, area.width, area.height)
-        context.clip()
+        width, height = view_allocation.width, view_allocation.height
         context.set_line_width(1.0)
 
         for change in self.linediffer.single_changes(pane, bounds):
@@ -1457,15 +1467,14 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
 
             context.rectangle(-0.5, ypos0 - 0.5, width + 1, ypos1 - ypos0)
             if change[1] != change[2]:
-                context.set_source_color(self.fill_colors[change[0]])
+                context.set_source_rgba(*self.fill_colors[change[0]])
                 context.fill_preserve()
                 if self.linediffer.locate_chunk(pane, change[1])[0] == self.cursor.chunk:
                     h = self.fill_colors['current-chunk-highlight']
-                    context.set_source_rgba(
-                        h.red_float, h.green_float, h.blue_float, 0.5)
+                    context.set_source_rgba(h.red, h.green, h.blue, 0.5)
                     context.fill_preserve()
 
-            context.set_source_color(self.line_colors[change[0]])
+            context.set_source_rgba(*self.line_colors[change[0]])
             context.stroke()
 
         if (self.prefs.highlight_current_line and textview.is_focus() and
@@ -1475,7 +1484,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             context.save()
             context.rectangle(0, ypos - visible.y, width, line_height)
             context.clip()
-            context.set_source_color(self.highlight_color)
+            context.set_source_rgba(*self.highlight_color)
             context.paint_with_alpha(0.25)
             context.restore()
 
@@ -1486,10 +1495,10 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             if bounds[0] <= syncline <= bounds[1]:
                 ypos = textview.get_y_for_line_num(syncline) - visible.y
                 context.rectangle(-0.5, ypos - 0.5, width + 1, 1)
-                context.set_source_color(self.syncpoint_color)
+                context.set_source_rgba(*self.syncpoint_color)
                 context.stroke()
 
-        current_time = glib.get_current_time()
+        current_time = GLib.get_current_time()
         new_anim_chunks = []
         for c in self.animating_chunks[pane]:
             percent = min(1.0, (current_time - c.start_time) / c.duration)
@@ -1520,24 +1529,24 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                 return True
             # Using timeout_add interferes with recalculation of inline
             # highlighting; this mechanism could be improved.
-            self.anim_source_id[pane] = gobject.idle_add(anim_cb)
+            self.anim_source_id[pane] = GObject.idle_add(anim_cb)
         elif not self.animating_chunks[pane] and self.anim_source_id[pane]:
-            gobject.source_remove(self.anim_source_id[pane])
+            GObject.source_remove(self.anim_source_id[pane])
             self.anim_source_id[pane] = None
 
-        if event.window == textview.get_window(gtk.TEXT_WINDOW_LEFT):
-            self.in_nested_textview_gutter_expose = True
-            textview.emit("expose-event", event)
+        # if event.window == textview.get_window(Gtk.TextWindowType.LEFT):
+        #     self.in_nested_textview_gutter_expose = True
+        #     textview.emit("expose-event", event)
 
     def _get_filename_for_saving(self, title ):
-        dialog = gtk.FileChooserDialog(title,
+        dialog = Gtk.FileChooserDialog(title,
             parent=self.widget.get_toplevel(),
-            action=gtk.FILE_CHOOSER_ACTION_SAVE,
-            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK) )
-        dialog.set_default_response(gtk.RESPONSE_OK)
+            action=Gtk.FileChooserAction.SAVE,
+            buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK) )
+        dialog.set_default_response(Gtk.ResponseType.OK)
         response = dialog.run()
         filename = None
-        if response == gtk.RESPONSE_OK:
+        if response == Gtk.ResponseType.OK:
             filename = dialog.get_filename()
         dialog.destroy()
         if filename:
@@ -1545,8 +1554,8 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                 response = misc.run_dialog(
                     _('"%s" exists!\nOverwrite?') % os.path.basename(filename),
                     parent = self,
-                    buttonstype = gtk.BUTTONS_YES_NO)
-                if response == gtk.RESPONSE_NO:
+                    buttonstype = Gtk.ButtonsType.YES_NO)
+                if response == Gtk.ResponseType.NO:
                     return None
             return filename
         return None
@@ -1557,7 +1566,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         except IOError as e:
             misc.run_dialog(
                 _("Error writing to %s\n\n%s.") % (filename, e),
-                self, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK)
+                self, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK)
             return False
         return True
 
@@ -1592,7 +1601,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                     '\r': ("Mac OS (CR)", 2),
                 }
                 newline = misc.run_dialog( _("This file '%s' contains a mixture of line endings.\n\nWhich 
format would you like to use?") % bufdata.label,
-                    self, gtk.MESSAGE_WARNING, buttonstype=gtk.BUTTONS_CANCEL,
+                    self, Gtk.MessageType.WARNING, buttonstype=Gtk.ButtonsType.CANCEL,
                     extrabuttons=[ buttons[b] for b in bufdata.newlines ] )
                 if newline < 0:
                     return
@@ -1608,7 +1617,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             except UnicodeEncodeError:
                 if misc.run_dialog(
                     _("'%s' contains characters not encodable with '%s'\nWould you like to save as UTF-8?") 
% (bufdata.label, bufdata.encoding),
-                    self, gtk.MESSAGE_ERROR, gtk.BUTTONS_YES_NO) != gtk.RESPONSE_YES:
+                    self, Gtk.MessageType.ERROR, Gtk.ButtonsType.YES_NO) != Gtk.ResponseType.YES:
                     return False
 
         save_to = bufdata.savefile or bufdata.filename
@@ -1659,7 +1668,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                 self.save_file(i)
 
     def on_fileentry_file_set(self, entry):
-        if self.check_save_modified() != gtk.RESPONSE_CANCEL:
+        if self.check_save_modified() != Gtk.ResponseType.CANCEL:
             entries = self.fileentry[:self.num_panes]
             files = [e.get_file() for e in entries]
             paths = [f.get_path() for f in files]
@@ -1673,7 +1682,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         return -1
 
     def on_revert_activate(self, *extra):
-        response = gtk.RESPONSE_OK
+        response = Gtk.ResponseType.OK
         unsaved = [b.data.label for b in self.textbuffer if b.data.modified]
         if unsaved:
             dialog = gnomeglade.Component("filediff.ui", "revert_dialog")
@@ -1685,7 +1694,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             response = dialog.widget.run()
             dialog.widget.destroy()
 
-        if response == gtk.RESPONSE_OK:
+        if response == Gtk.ResponseType.OK:
             files = [b.data.filename for b in self.textbuffer[:self.num_panes]]
             self.set_files(files)
 
@@ -1743,7 +1752,8 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             syncpoint = 0.5
 
             # the line to search for in the 'master' text
-            master_y = adjustment.value + adjustment.page_size * syncpoint
+            master_y = (adjustment.get_value() + adjustment.get_page_size() *
+                        syncpoint)
             it = self.textview[master].get_line_at_y(int(master_y))[0]
             line_y, height = self.textview[master].get_line_yrange(it)
             line = it.get_line() + ((master_y-line_y)/height)
@@ -1772,10 +1782,11 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                 other_line = (obegin + fraction * (oend - obegin))
                 it = self.textbuffer[i].get_iter_at_line(int(other_line))
                 val, height = self.textview[i].get_line_yrange(it)
-                val -= (adj.page_size) * syncpoint
+                val -= (adj.get_page_size()) * syncpoint
                 val += (other_line-int(other_line)) * height
-                val = min(max(val, adj.lower), adj.upper - adj.page_size)
-                adj.set_value( val )
+                val = min(max(val, adj.get_lower()),
+                          adj.get_upper() - adj.get_page_size())
+                adj.set_value(val)
 
                 # If we just changed the central bar, make it the master
                 if i == 1:
@@ -1783,9 +1794,9 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             self._sync_vscroll_lock = False
 
         for lm in self.linkmap:
-            if lm.window:
-                lm.window.invalidate_rect(None, True)
-                lm.window.process_updates(True)
+            if lm.get_window():
+                lm.get_window().invalidate_rect(None, True)
+                lm.get_window().process_updates(True)
 
     def set_num_panes(self, n):
         if n != self.num_panes and n in (1,2,3):
@@ -1808,7 +1819,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             if self.findbar.widget in self.table:
                 self.table.remove(self.findbar.widget)
             self.table.attach(self.findbar.widget, 1, right_attach, 2, 3,
-                              gtk.FILL, gtk.FILL)
+                              Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL)
 
             self.actiongroup.get_action("MakePatch").set_sensitive(n > 1)
             self.actiongroup.get_action("CycleDocuments").set_sensitive(n > 1)
@@ -1853,9 +1864,9 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                 pane = 0
         buf = self.textbuffer[pane]
 
-        if direction == gtk.gdk.SCROLL_DOWN:
+        if direction == Gdk.ScrollDirection.DOWN:
             target = self.cursor.next
-        else: # direction == gtk.gdk.SCROLL_UP
+        else: # direction == Gdk.ScrollDirection.UP
             target = self.cursor.prev
 
         if target is None:
@@ -1867,10 +1878,11 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             if self.cursor.line != c[1]:
                 buf.place_cursor(buf.get_iter_at_line(c[1]))
             if centered:
-                self.textview[pane].scroll_to_mark(buf.get_insert(), 0.0,
-                                                   True)
+                self.textview[pane].scroll_to_mark(
+                    buf.get_insert(), 0.0, True, 0.5, 0.5)
             else:
-                self.textview[pane].scroll_to_mark(buf.get_insert(), 0.2)
+                self.textview[pane].scroll_to_mark(
+                    buf.get_insert(), 0.2, True, 0.5, 0.5)
 
     def copy_chunk(self, src, dst, chunk, copy_up):
         b0, b1 = self.textbuffer[src], self.textbuffer[dst]
@@ -1973,7 +1985,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         if valid_points:
             for mgr in self.msgarea_mgr:
                 msgarea = mgr.new_from_text_and_icon(
-                    gtk.STOCK_DIALOG_INFO,
+                    Gtk.STOCK_DIALOG_INFO,
                     _("Live comparison updating disabled"),
                     _("Live updating of comparisons is disabled when "
                       "synchronization points are active. You can still "
diff --git a/meld/linkmap.py b/meld/linkmap.py
index 5148829..6368a57 100644
--- a/meld/linkmap.py
+++ b/meld/linkmap.py
@@ -19,7 +19,8 @@
 
 import math
 
-import gtk
+from gi.repository import Gdk
+from gi.repository import Gtk
 
 from . import diffutil
 
@@ -28,17 +29,10 @@ from . import diffutil
 MODE_REPLACE, MODE_DELETE, MODE_INSERT = 0, 1, 2
 
 
-class LinkMap(gtk.DrawingArea):
+class LinkMap(Gtk.DrawingArea):
 
     __gtype_name__ = "LinkMap"
 
-    __gsignals__ = {
-        'expose-event': 'override',
-        'scroll-event': 'override',
-        'button-press-event': 'override',
-        'button-release-event': 'override',
-    }
-
     def __init__(self):
         self.mode = MODE_REPLACE
         self._setup = False
@@ -46,14 +40,14 @@ class LinkMap(gtk.DrawingArea):
     def associate(self, filediff, left_view, right_view):
         self.filediff = filediff
         self.views = [left_view, right_view]
-        if self.get_direction() == gtk.TEXT_DIR_RTL:
+        if self.get_direction() == Gtk.TextDirection.RTL:
             self.views.reverse()
         self.view_indices = [filediff.textview.index(t) for t in self.views]
 
         self.set_color_scheme((filediff.fill_colors, filediff.line_colors))
 
         self.line_height = filediff.pixels_per_line
-        icon_theme = gtk.icon_theme_get_default()
+        icon_theme = Gtk.IconTheme.get_default()
         load = lambda x: icon_theme.load_icon(x, self.line_height, 0)
         pixbuf_apply0 = load("button_apply0")
         pixbuf_apply1 = load("button_apply1")
@@ -92,14 +86,15 @@ class LinkMap(gtk.DrawingArea):
         # Shift, then releases the button... what do we do?
         self.mode = mode
         self.mouse_chunk = None
-        x, y, width, height = self.allocation
+        allocation = self.get_allocation()
         pixbuf_width = self.button_width
-        self.queue_draw_area(0, 0, pixbuf_width, height)
-        self.queue_draw_area(width - pixbuf_width, 0, pixbuf_width, height)
+        self.queue_draw_area(0, 0, pixbuf_width, allocation.height)
+        self.queue_draw_area(allocation.width - pixbuf_width, 0,
+                             pixbuf_width, allocation.height)
 
     def paint_pixbuf_at(self, context, pixbuf, x, y):
         context.translate(x, y)
-        context.set_source_pixbuf(pixbuf, 0, 0)
+        Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0)
         context.paint()
         context.identity_matrix()
 
@@ -155,26 +150,24 @@ class LinkMap(gtk.DrawingArea):
 
         return left_act, right_act
 
-    def do_expose_event(self, event):
+    def do_draw(self, context):
         if not self._setup:
             return
 
-        context = self.window.cairo_create()
-        context.rectangle(event.area.x, event.area.y, event.area.width,
-                          event.area.height)
-        context.clip()
         context.set_line_width(1.0)
+        allocation = self.get_allocation()
 
         pix_start = [t.get_visible_rect().y for t in self.views]
-        rel_offset = [t.allocation.y - self.allocation.y for t in self.views]
+        # FIXME: If the linkmap has a different vertical offset to its
+        # associated textviews, things will now be misaligned.
 
-        height = self.allocation.height
+        height = allocation.height
         visible = [self.views[0].get_line_num_for_y(pix_start[0]),
                    self.views[0].get_line_num_for_y(pix_start[0] + height),
                    self.views[1].get_line_num_for_y(pix_start[1]),
                    self.views[1].get_line_num_for_y(pix_start[1] + height)]
 
-        wtotal = self.allocation.width
+        wtotal = allocation.width
         # For bezier control points
         x_steps = [-0.5, (1. / 3) * wtotal, (2. / 3) * wtotal, wtotal + 0.5]
         # Rounded rectangle corner radius for culled changes display
@@ -183,7 +176,7 @@ class LinkMap(gtk.DrawingArea):
 
         left, right = self.view_indices
         view_offset_line = lambda v, l: (self.views[v].get_y_for_line_num(l) -
-                                         pix_start[v] + rel_offset[v])
+                                         pix_start[v])
         for c in self.filediff.linediffer.pair_changes(left, right, visible):
             # f and t are short for "from" and "to"
             f0, f1 = [view_offset_line(0, l) for l in c[1:3]]
@@ -222,17 +215,17 @@ class LinkMap(gtk.DrawingArea):
                                  x_steps[0], f1 - 0.5)
                 context.close_path()
 
-            context.set_source_color(self.fill_colors[c[0]])
+            context.set_source_rgba(*self.fill_colors[c[0]])
             context.fill_preserve()
 
             chunk_idx = self.filediff.linediffer.locate_chunk(left, c[1])[0]
             if chunk_idx == self.filediff.cursor.chunk:
                 h = self.fill_colors['current-chunk-highlight']
                 context.set_source_rgba(
-                    h.red_float, h.green_float, h.blue_float, 0.5)
+                    h.red, h.green, h.blue, 0.5)
                 context.fill_preserve()
 
-            context.set_source_color(self.line_colors[c[0]])
+            context.set_source_rgba(*self.line_colors[c[0]])
             context.stroke()
 
             if culled:
@@ -248,7 +241,7 @@ class LinkMap(gtk.DrawingArea):
                 self.paint_pixbuf_at(context, pix1, x, t0)
 
         # allow for scrollbar at end of textview
-        mid = int(0.5 * self.views[0].allocation.height) + 0.5
+        mid = int(0.5 * self.views[0].get_allocation().height) + 0.5
         context.set_source_rgba(0., 0., 0., 0.5)
         context.move_to(.35 * wtotal, mid)
         context.line_to(.65 * wtotal, mid)
@@ -261,9 +254,9 @@ class LinkMap(gtk.DrawingArea):
         src_idx, dst_idx = side, 1 if side == 0 else 0
         src, dst = self.view_indices[src_idx], self.view_indices[dst_idx]
 
+        allocation = self.get_allocation()
         vis_offset = [t.get_visible_rect().y for t in self.views]
-        rel_offset = [t.allocation.y - self.allocation.y for t in self.views]
-        height = self.allocation.height
+        height = allocation.height
 
         bounds = []
         for v in (self.views[src_idx], self.views[dst_idx]):
@@ -271,8 +264,10 @@ class LinkMap(gtk.DrawingArea):
             bounds.append(v.get_line_num_for_y(visible.y))
             bounds.append(v.get_line_num_for_y(visible.y + visible.height))
 
+        # FIXME: As above, with relative offset gone we'd better be at the same
+        # y position as our textview.
         view_offset_line = lambda v, l: (self.views[v].get_y_for_line_num(l) -
-                                         vis_offset[v] + rel_offset[v])
+                                         vis_offset[v])
         for c in self.filediff.linediffer.pair_changes(src, dst, bounds):
             f0, f1 = [view_offset_line(src_idx, l) for l in c[1:3]]
             t0, t1 = [view_offset_line(dst_idx, l) for l in c[3:5]]
@@ -288,7 +283,11 @@ class LinkMap(gtk.DrawingArea):
                 action_change = diffutil.reverse_chunk(c) if dst < src else c
                 actions = self._classify_change_actions(action_change)
                 if actions[side] is not None:
-                    rect = gtk.gdk.Rectangle(x, f0, pix_width, pix_height)
+                    rect = Gdk.Rectangle()
+                    rect.x = x
+                    rect.y = f0
+                    rect.width = pix_width
+                    rect.height = pix_height
                     self.mouse_chunk = ((src, dst), rect, c, actions[side])
                 break
 
@@ -302,7 +301,7 @@ class LinkMap(gtk.DrawingArea):
                 pix_height *= 2
 
             # Quick reject if not in the area used to draw our buttons
-            right_gutter_x = self.allocation.width - pix_width
+            right_gutter_x = self.get_allocation().width - pix_width
             if event.x >= pix_width and event.x <= right_gutter_x:
                 return True
 
diff --git a/meld/meldapp.py b/meld/meldapp.py
index 00c39c9..e6d1d55 100644
--- a/meld/meldapp.py
+++ b/meld/meldapp.py
@@ -24,9 +24,9 @@ import os
 import sys
 from gettext import gettext as _
 
-import gio
-import gobject
-import gtk
+from gi.repository import Gio
+from gi.repository import GObject
+from gi.repository import Gtk
 
 from . import conf
 from . import filters
@@ -34,19 +34,19 @@ from . import preferences
 from . import recent
 
 
-class MeldApp(gobject.GObject):
+class MeldApp(GObject.GObject):
 
     __gsignals__ = {
-        'file-filters-changed': (gobject.SIGNAL_RUN_FIRST,
-                                 gobject.TYPE_NONE, ()),
-        'text-filters-changed': (gobject.SIGNAL_RUN_FIRST,
-                                 gobject.TYPE_NONE, ()),
+        'file-filters-changed': (GObject.SignalFlags.RUN_FIRST,
+                                 None, ()),
+        'text-filters-changed': (GObject.SignalFlags.RUN_FIRST,
+                                 None, ()),
     }
 
     def __init__(self):
-        gobject.GObject.__init__(self)
-        gobject.set_application_name("Meld")
-        gtk.window_set_default_icon_name("meld")
+        GObject.GObject.__init__(self)
+        GObject.set_application_name("Meld")
+        Gtk.Window.set_default_icon_name("meld")
         self.version = conf.__version__
         self.prefs = preferences.MeldPreferences()
         self.prefs.notify_add(self.on_preference_changed)
@@ -159,7 +159,7 @@ class MeldApp(gobject.GObject):
                                        args[0].endswith(".meldcmp")):
             path = options.comparison_file or args[0]
             comparison_file_path = os.path.expanduser(path)
-            gio_file = gio.File(path=comparison_file_path)
+            gio_file = Gio.File.new_for_path(comparison_file_path)
             try:
                 tab = self.window.append_recent(gio_file.get_uri())
             except (IOError, ValueError):
diff --git a/meld/meldbuffer.py b/meld/meldbuffer.py
index 5049eb3..9658a97 100644
--- a/meld/meldbuffer.py
+++ b/meld/meldbuffer.py
@@ -44,7 +44,7 @@ class MeldBuffer(sourceviewer.srcviewer.GtkTextBuffer):
         self.data = new_data
 
     def get_iter_at_line_or_eof(self, line):
-        """Return a gtk.TextIter at the given line, or the end of the buffer.
+        """Return a Gtk.TextIter at the given line, or the end of the buffer.
 
         This method is like get_iter_at_line, but if asked for a position past
         the end of the buffer, this returns the end of the buffer; the
@@ -60,7 +60,7 @@ class MeldBuffer(sourceviewer.srcviewer.GtkTextBuffer):
 
         This method is like insert, but if asked to insert something past the
         last line in the buffer, this will insert at the end, and will add a
-        linebreak before the inserted text. The last line in a gtk.TextBuffer
+        linebreak before the inserted text. The last line in a Gtk.TextBuffer
         is guaranteed never to have a newline, so we need to handle this.
         """
         if line >= self.get_line_count():
@@ -95,14 +95,14 @@ class MeldBufferData(object):
 
 
 class BufferLines(object):
-    """gtk.TextBuffer shim with line-based access and optional filtering
+    """Gtk.TextBuffer shim with line-based access and optional filtering
 
-    This class allows a gtk.TextBuffer to be treated as a list of lines of
+    This class allows a Gtk.TextBuffer to be treated as a list of lines of
     possibly-filtered text. If no filter is given, the raw output from the
-    gtk.TextBuffer is used.
+    Gtk.TextBuffer is used.
 
     The logic here (and in places in FileDiff) requires that Python's
-    unicode splitlines() implementation and gtk.TextBuffer agree on where
+    unicode splitlines() implementation and Gtk.TextBuffer agree on where
     linebreaks occur. Happily, this is usually the case.
     """
 
@@ -127,7 +127,7 @@ class BufferLines(object):
             lines = filter_txt.splitlines()
             ends = filter_txt.splitlines(True)
 
-            # The last line in a gtk.TextBuffer is guaranteed never to end in a
+            # The last line in a Gtk.TextBuffer is guaranteed never to end in a
             # newline. As splitlines() discards an empty line at the end, we
             # need to artificially add a line if the requested slice is past
             # the end of the buffer, and the last line in the slice ended in a
diff --git a/meld/melddoc.py b/meld/melddoc.py
index aa74a44..2d1e581 100644
--- a/meld/melddoc.py
+++ b/meld/melddoc.py
@@ -20,37 +20,38 @@
 import subprocess
 import sys
 
-import gobject
-import gio
-import gtk
+from gi.repository import GLib
+from gi.repository import GObject
+from gi.repository import Gio
+from gi.repository import Gtk
 
 from . import task
 
 from gettext import gettext as _
 
 
-class MeldDoc(gobject.GObject):
+class MeldDoc(GObject.GObject):
     """Base class for documents in the meld application.
     """
 
     __gsignals__ = {
-        'label-changed':        (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
-                                 (gobject.TYPE_STRING, gobject.TYPE_STRING)),
-        'file-changed':         (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
-                                 (gobject.TYPE_STRING,)),
-        'create-diff':          (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
-                                 (gobject.TYPE_PYOBJECT,
-                                  gobject.TYPE_PYOBJECT)),
-        'status-changed':       (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
-                                 (gobject.TYPE_PYOBJECT,)),
-        'current-diff-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+        'label-changed':        (GObject.SignalFlags.RUN_FIRST, None,
+                                 (GObject.TYPE_STRING, GObject.TYPE_STRING)),
+        'file-changed':         (GObject.SignalFlags.RUN_FIRST, None,
+                                 (GObject.TYPE_STRING,)),
+        'create-diff':          (GObject.SignalFlags.RUN_FIRST, None,
+                                 (GObject.TYPE_PYOBJECT,
+                                  GObject.TYPE_PYOBJECT)),
+        'status-changed':       (GObject.SignalFlags.RUN_FIRST, None,
+                                 (GObject.TYPE_PYOBJECT,)),
+        'current-diff-changed': (GObject.SignalFlags.RUN_FIRST, None,
                                  ()),
-        'next-diff-changed':    (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+        'next-diff-changed':    (GObject.SignalFlags.RUN_FIRST, None,
                                  (bool, bool)),
     }
 
     def __init__(self, prefs):
-        gobject.GObject.__init__(self)
+        GObject.GObject.__init__(self)
         self.scheduler = task.FifoScheduler()
         self.prefs = prefs
         self.prefs.notify_add(self.on_preference_changed)
@@ -77,8 +78,8 @@ class MeldDoc(gobject.GObject):
             self.scheduler.remove_task(self.scheduler.get_current_task())
 
     def _open_files(self, selected, line=0):
-        query_attrs = ",".join((gio.FILE_ATTRIBUTE_STANDARD_TYPE,
-                                gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE))
+        query_attrs = ",".join((Gio.FILE_ATTRIBUTE_STANDARD_TYPE,
+                                Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE))
 
         def os_open(path):
             if not path:
@@ -93,15 +94,16 @@ class MeldDoc(gobject.GObject):
         def open_cb(source, result, *data):
             info = source.query_info_finish(result)
             file_type = info.get_file_type()
-            if file_type == gio.FILE_TYPE_DIRECTORY:
+            if file_type == Gio.FileType.DIRECTORY:
                 os_open(source.get_path())
-            elif file_type == gio.FILE_TYPE_REGULAR:
+            elif file_type == Gio.FileType.REGULAR:
                 content_type = info.get_content_type()
                 path = source.get_path()
                 # FIXME: Content types are broken on Windows with current gio
-                if gio.content_type_is_a(content_type, "text/plain") or \
+                if Gio.content_type_is_a(content_type, "text/plain") or \
                         sys.platform == "win32":
                     editor = self.prefs.get_editor_command(path, line)
+                    # TODO: If the editor is badly set up, this fails silently
                     if editor:
                         subprocess.Popen(editor)
                     else:
@@ -112,8 +114,9 @@ class MeldDoc(gobject.GObject):
                 # TODO: Add some kind of 'failed to open' notification
                 pass
 
-        for f in [gio.File(s) for s in selected]:
-            f.query_info_async(query_attrs, open_cb)
+        for f in [Gio.File.new_for_path(s) for s in selected]:
+            f.query_info_async(query_attrs, 0, GLib.PRIORITY_LOW, None,
+                               open_cb, None)
 
     def open_external(self):
         pass
@@ -164,7 +167,7 @@ class MeldDoc(gobject.GObject):
     def on_delete_event(self, appquit=0):
         """Called when the docs container is about to close.
 
-        A doc normally returns gtk.RESPONSE_OK, but may instead return
-        gtk.RESPONSE_CANCEL to request that the container not delete it.
+        A doc normally returns Gtk.ResponseType.OK, but may instead return
+        Gtk.ResponseType.CANCEL to request that the container not delete it.
         """
-        return gtk.RESPONSE_OK
+        return Gtk.ResponseType.OK
diff --git a/meld/meldwindow.py b/meld/meldwindow.py
index 5005a84..698c95e 100644
--- a/meld/meldwindow.py
+++ b/meld/meldwindow.py
@@ -19,9 +19,10 @@
 import os
 from gettext import gettext as _
 
-import gio
-import gtk
-import gobject
+from gi.repository import Gio
+from gi.repository import Gdk
+from gi.repository import Gtk
+from gi.repository import GObject
 
 from . import conf
 from . import dirdiff
@@ -58,36 +59,36 @@ class MeldWindow(gnomeglade.Component):
 
         actions = (
             ("FileMenu", None, _("_File")),
-            ("New", gtk.STOCK_NEW, _("_New Comparison..."), "<control>N",
+            ("New", Gtk.STOCK_NEW, _("_New Comparison..."), "<control>N",
                 _("Start a new comparison"),
                 self.on_menu_file_new_activate),
-            ("Save", gtk.STOCK_SAVE, None, None,
+            ("Save", Gtk.STOCK_SAVE, None, None,
                 _("Save the current file"),
                 self.on_menu_save_activate),
-            ("SaveAs", gtk.STOCK_SAVE_AS, _("Save As..."), "<control><shift>S",
+            ("SaveAs", Gtk.STOCK_SAVE_AS, _("Save As..."), "<control><shift>S",
                 _("Save the current file with a different name"),
                 self.on_menu_save_as_activate),
-            ("Close", gtk.STOCK_CLOSE, None, None,
+            ("Close", Gtk.STOCK_CLOSE, None, None,
                 _("Close the current file"),
                 self.on_menu_close_activate),
-            ("Quit", gtk.STOCK_QUIT, None, None,
+            ("Quit", Gtk.STOCK_QUIT, None, None,
                 _("Quit the program"),
                 self.on_menu_quit_activate),
 
             ("EditMenu", None, _("_Edit")),
-            ("Undo", gtk.STOCK_UNDO, None, "<control>Z",
+            ("Undo", Gtk.STOCK_UNDO, None, "<control>Z",
                 _("Undo the last action"),
                 self.on_menu_undo_activate),
-            ("Redo", gtk.STOCK_REDO, None, "<control><shift>Z",
+            ("Redo", Gtk.STOCK_REDO, None, "<control><shift>Z",
                 _("Redo the last undone action"),
                 self.on_menu_redo_activate),
-            ("Cut", gtk.STOCK_CUT, None, None, _("Cut the selection"),
+            ("Cut", Gtk.STOCK_CUT, None, None, _("Cut the selection"),
                 self.on_menu_cut_activate),
-            ("Copy", gtk.STOCK_COPY, None, None, _("Copy the selection"),
+            ("Copy", Gtk.STOCK_COPY, None, None, _("Copy the selection"),
                 self.on_menu_copy_activate),
-            ("Paste", gtk.STOCK_PASTE, None, None, _("Paste the clipboard"),
+            ("Paste", Gtk.STOCK_PASTE, None, None, _("Paste the clipboard"),
                 self.on_menu_paste_activate),
-            ("Find", gtk.STOCK_FIND, _("Find..."), None, _("Search for text"),
+            ("Find", Gtk.STOCK_FIND, _("Find..."), None, _("Search for text"),
                 self.on_menu_find_activate),
             ("FindNext", None, _("Find Ne_xt"), "<control>G",
                 _("Search forwards for the same text"),
@@ -95,19 +96,19 @@ class MeldWindow(gnomeglade.Component):
             ("FindPrevious", None, _("Find _Previous"), "<control><shift>G",
                 _("Search backwards for the same text"),
                 self.on_menu_find_previous_activate),
-            ("Replace", gtk.STOCK_FIND_AND_REPLACE,
+            ("Replace", Gtk.STOCK_FIND_AND_REPLACE,
                 _("_Replace..."), "<control>H",
                 _("Find and replace text"),
                 self.on_menu_replace_activate),
-            ("Preferences", gtk.STOCK_PREFERENCES, _("Prefere_nces"), None,
+            ("Preferences", Gtk.STOCK_PREFERENCES, _("Prefere_nces"), None,
                 _("Configure the application"),
                 self.on_menu_preferences_activate),
 
             ("ChangesMenu", None, _("_Changes")),
-            ("NextChange", gtk.STOCK_GO_DOWN, _("Next Change"), "<Alt>Down",
+            ("NextChange", Gtk.STOCK_GO_DOWN, _("Next Change"), "<Alt>Down",
                 _("Go to the next change"),
                 self.on_menu_edit_down_activate),
-            ("PrevChange", gtk.STOCK_GO_UP, _("Previous Change"), "<Alt>Up",
+            ("PrevChange", Gtk.STOCK_GO_UP, _("Previous Change"), "<Alt>Up",
                 _("Go to the previous change"),
                 self.on_menu_edit_up_activate),
             ("OpenExternal", None, _("Open Externally"), None,
@@ -119,10 +120,10 @@ class MeldWindow(gnomeglade.Component):
             ("FileStatus", None, _("File Status")),
             ("VcStatus", None, _("Version Status")),
             ("FileFilters", None, _("File Filters")),
-            ("Stop", gtk.STOCK_STOP, None, "Escape",
+            ("Stop", Gtk.STOCK_STOP, None, "Escape",
                 _("Stop the current action"),
                 self.on_toolbar_stop_clicked),
-            ("Refresh", gtk.STOCK_REFRESH, None, "<control>R",
+            ("Refresh", Gtk.STOCK_REFRESH, None, "<control>R",
                 _("Refresh the view"),
                 self.on_menu_refresh_activate),
 
@@ -143,12 +144,12 @@ class MeldWindow(gnomeglade.Component):
                 self.on_move_tab_next),
 
             ("HelpMenu", None, _("_Help")),
-            ("Help", gtk.STOCK_HELP, _("_Contents"), "F1",
+            ("Help", Gtk.STOCK_HELP, _("_Contents"), "F1",
                 _("Open the Meld manual"), self.on_menu_help_activate),
-            ("BugReport", gtk.STOCK_DIALOG_WARNING, _("Report _Bug"), None,
+            ("BugReport", Gtk.STOCK_DIALOG_WARNING, _("Report _Bug"), None,
                 _("Report a bug in Meld"),
                 self.on_menu_help_bug_activate),
-            ("About", gtk.STOCK_ABOUT, None, None,
+            ("About", Gtk.STOCK_ABOUT, None, None,
                 _("About this program"),
                 self.on_menu_about_activate),
         )
@@ -164,20 +165,20 @@ class MeldWindow(gnomeglade.Component):
                 self.on_menu_statusbar_toggled, app.prefs.statusbar_visible)
         )
         ui_file = gnomeglade.ui_file("meldapp-ui.xml")
-        self.actiongroup = gtk.ActionGroup('MainActions')
+        self.actiongroup = Gtk.ActionGroup('MainActions')
         self.actiongroup.set_translation_domain("meld")
         self.actiongroup.add_actions(actions)
         self.actiongroup.add_toggle_actions(toggleactions)
 
-        recent_action = gtk.RecentAction("Recent",  _("Open Recent"),
+        recent_action = Gtk.RecentAction("Recent",  _("Open Recent"),
                                          _("Open recent files"), None)
         recent_action.set_show_private(True)
         recent_action.set_filter(app.recent_comparisons.recent_filter)
-        recent_action.set_sort_type(gtk.RECENT_SORT_MRU)
+        recent_action.set_sort_type(Gtk.RecentSortType.MRU)
         recent_action.connect("item-activated", self.on_action_recent)
         self.actiongroup.add_action(recent_action)
 
-        self.ui = gtk.UIManager()
+        self.ui = Gtk.UIManager()
         self.ui.insert_action_group(self.actiongroup, 0)
         self.ui.add_ui_from_file(ui_file)
         self.ui.connect("connect-proxy", self._on_uimanager_connect_proxy)
@@ -193,24 +194,24 @@ class MeldWindow(gnomeglade.Component):
 
         # Add alternate keybindings for Prev/Next Change
         accels = self.ui.get_accel_group()
-        (keyval, mask) = gtk.accelerator_parse("<Ctrl>D")
-        accels.connect_group(keyval, mask, 0, self.on_menu_edit_down_activate)
-        (keyval, mask) = gtk.accelerator_parse("<Ctrl>E")
-        accels.connect_group(keyval, mask, 0, self.on_menu_edit_up_activate)
-        (keyval, mask) = gtk.accelerator_parse("F5")
-        accels.connect_group(keyval, mask, 0, self.on_menu_refresh_activate)
+        (keyval, mask) = Gtk.accelerator_parse("<Ctrl>D")
+        accels.connect(keyval, mask, 0, self.on_menu_edit_down_activate)
+        (keyval, mask) = Gtk.accelerator_parse("<Ctrl>E")
+        accels.connect(keyval, mask, 0, self.on_menu_edit_up_activate)
+        (keyval, mask) = Gtk.accelerator_parse("F5")
+        accels.connect(keyval, mask, 0, self.on_menu_refresh_activate)
 
         # Initialise sensitivity for important actions
         self.actiongroup.get_action("Stop").set_sensitive(False)
         self._update_page_action_sensitivity()
 
-        self.appvbox.pack_start(self.menubar, expand=False)
-        self.appvbox.pack_start(self.toolbar, expand=False)
+        self.appvbox.pack_start(self.menubar, False, True, 0)
+        self.appvbox.pack_start(self.toolbar, False, True, 0)
         self._menu_context = self.statusbar.get_context_id("Tooltips")
         self.widget.drag_dest_set(
-            gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP,
-            [('text/uri-list', 0, 0)],
-            gtk.gdk.ACTION_COPY)
+            Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP,
+            None, Gdk.DragAction.COPY)
+        self.widget.drag_dest_add_uri_targets()
         self.widget.connect("drag_data_received",
                             self.on_widget_drag_data_received)
         self.toolbar.set_style(app.prefs.get_toolbar_style())
@@ -231,8 +232,8 @@ class MeldWindow(gnomeglade.Component):
     def on_focus_change(self, widget, event, callback_data=None):
         for idx in range(self.notebook.get_n_pages()):
             w = self.notebook.get_nth_page(idx)
-            if hasattr(w.get_data("pyobject"), 'on_focus_change'):
-                w.get_data("pyobject").on_focus_change()
+            if hasattr(w.pyobject, 'on_focus_change'):
+                w.pyobject.on_focus_change()
         # Let the rest of the stack know about this event
         return False
 
@@ -240,7 +241,7 @@ class MeldWindow(gnomeglade.Component):
         if len(selection_data.get_uris()) != 0:
             paths = []
             for uri in selection_data.get_uris():
-                paths.append(gio.File(uri=uri).get_path())
+                paths.append(Gio.File.new_for_uri(uri).get_path())
             self.open_paths(paths)
             return True
 
@@ -248,21 +249,22 @@ class MeldWindow(gnomeglade.Component):
         tooltip = action.props.tooltip
         if not tooltip:
             return
-        if isinstance(widget, gtk.MenuItem):
+        if isinstance(widget, Gtk.MenuItem):
             cid = widget.connect("select", self._on_action_item_select_enter, tooltip)
             cid2 = widget.connect("deselect", self._on_action_item_deselect_leave)
-            widget.set_data("meldapp::proxy-signal-ids", (cid, cid2))
-        elif isinstance(widget, gtk.ToolButton):
-            cid = widget.child.connect("enter", self._on_action_item_select_enter, tooltip)
-            cid2 = widget.child.connect("leave", self._on_action_item_deselect_leave)
-            widget.set_data("meldapp::proxy-signal-ids", (cid, cid2))
+            widget.proxy_signal_ids = (cid, cid2)
+        elif isinstance(widget, Gtk.ToolButton):
+            cid = widget.get_child().connect("enter", self._on_action_item_select_enter, tooltip)
+            cid2 = widget.get_child().connect("leave", self._on_action_item_deselect_leave)
+            widget.proxy_signal_ids = (cid, cid2)
 
     def _on_uimanager_disconnect_proxy(self, ui, action, widget):
-        cids = widget.get_data("meldapp::proxy-signal-ids")
-        if not cids:
+        try:
+            cids = widget.proxy_signal_ids
+        except AttributeError:
             return
-        if isinstance(widget, gtk.ToolButton):
-            widget = widget.child
+        if isinstance(widget, Gtk.ToolButton):
+            widget = widget.get_child()
         for cid in cids:
             widget.disconnect(cid)
 
@@ -289,7 +291,7 @@ class MeldWindow(gnomeglade.Component):
         if not self.idle_hooked:
             self.statusbar.start_pulse()
             self.actiongroup.get_action("Stop").set_sensitive(True)
-            self.idle_hooked = gobject.idle_add(self.on_idle)
+            self.idle_hooked = GObject.idle_add(self.on_idle)
 
     def on_preference_changed(self, key, value):
         if key == "toolbar_style":
@@ -318,8 +320,7 @@ class MeldWindow(gnomeglade.Component):
         self.actiongroup.get_action("MoveTabNext").set_sensitive(have_next_tab)
 
         if current_page != -1:
-            page = self.notebook.get_nth_page(
-                current_page).get_data("pyobject")
+            page = self.notebook.get_nth_page(current_page).pyobject
         else:
             page = None
 
@@ -340,7 +341,7 @@ class MeldWindow(gnomeglade.Component):
     def on_switch_page(self, notebook, page, which):
         oldidx = notebook.get_current_page()
         if oldidx >= 0:
-            olddoc = notebook.get_nth_page(oldidx).get_data("pyobject")
+            olddoc = notebook.get_nth_page(oldidx).pyobject
             if self.diff_handler is not None:
                 olddoc.disconnect(self.diff_handler)
             olddoc.on_container_switch_out_event(self.ui)
@@ -350,7 +351,7 @@ class MeldWindow(gnomeglade.Component):
                     undoseq.disconnect(handler)
                 self.undo_handlers = tuple()
 
-        newdoc = notebook.get_nth_page(which).get_data("pyobject")
+        newdoc = notebook.get_nth_page(which).pyobject
         try:
             undoseq = newdoc.undosequence
             can_undo = undoseq.can_undo()
@@ -408,7 +409,7 @@ class MeldWindow(gnomeglade.Component):
 
         actiongroup = self.tab_switch_actiongroup
         if actiongroup:
-            idx = self.notebook.child_get_property(page, "position")
+            idx = self.notebook.page_num(page)
             action_name = "SwitchTab%d" % idx
             actiongroup.get_action(action_name).set_label(text)
 
@@ -451,20 +452,20 @@ class MeldWindow(gnomeglade.Component):
     def on_menu_close_activate(self, *extra):
         i = self.notebook.get_current_page()
         if i >= 0:
-            page = self.notebook.get_nth_page(i).get_data("pyobject")
+            page = self.notebook.get_nth_page(i).pyobject
             self.try_remove_page(page)
 
     def on_menu_quit_activate(self, *extra):
         # Delete pages from right-to-left.  This ensures that if a version
         # control page is open in the far left page, it will be closed last.
         for c in reversed(self.notebook.get_children()):
-            page = c.get_data("pyobject")
+            page = c.pyobject
             self.notebook.set_current_page(self.notebook.page_num(page.widget))
             response = self.try_remove_page(page, appquit=1)
-            if response == gtk.RESPONSE_CANCEL:
-                return gtk.RESPONSE_CANCEL
-        gtk.main_quit()
-        return gtk.RESPONSE_CLOSE
+            if response == Gtk.ResponseType.CANCEL:
+                return Gtk.ResponseType.CANCEL
+        Gtk.main_quit()
+        return Gtk.ResponseType.CLOSE
 
     #
     # Toolbar and menu items (edit)
@@ -492,23 +493,23 @@ class MeldWindow(gnomeglade.Component):
 
     def on_menu_copy_activate(self, *extra):
         widget = self.widget.get_focus()
-        if isinstance(widget, gtk.Editable):
+        if isinstance(widget, Gtk.Editable):
             widget.copy_clipboard()
-        elif isinstance(widget, gtk.TextView):
+        elif isinstance(widget, Gtk.TextView):
             widget.emit("copy-clipboard")
 
     def on_menu_cut_activate(self, *extra):
         widget = self.widget.get_focus()
-        if isinstance(widget, gtk.Editable):
+        if isinstance(widget, Gtk.Editable):
             widget.cut_clipboard()
-        elif isinstance(widget, gtk.TextView):
+        elif isinstance(widget, Gtk.TextView):
             widget.emit("cut-clipboard")
 
     def on_menu_paste_activate(self, *extra):
         widget = self.widget.get_focus()
-        if isinstance(widget, gtk.Editable):
+        if isinstance(widget, Gtk.Editable):
             widget.paste_clipboard()
-        elif isinstance(widget, gtk.TextView):
+        elif isinstance(widget, Gtk.TextView):
             widget.emit("paste-clipboard")
 
     #
@@ -518,7 +519,7 @@ class MeldWindow(gnomeglade.Component):
         preferences.PreferencesDialog(self.widget, app.prefs)
 
     def on_action_fullscreen_toggled(self, widget):
-        is_full = self.widget.window.get_state() & gtk.gdk.WINDOW_STATE_FULLSCREEN
+        is_full = self.widget.get_window().get_state() & Gdk.WindowState.FULLSCREEN
         if widget.get_active() and not is_full:
             self.widget.fullscreen()
         elif is_full:
@@ -537,8 +538,7 @@ class MeldWindow(gnomeglade.Component):
         misc.open_uri("http://bugzilla.gnome.org/buglist.cgi?query=product%3Ameld";)
 
     def on_menu_about_activate(self, *extra):
-        gtk.about_dialog_set_url_hook(lambda dialog, uri: misc.open_uri(uri))
-        builder = gtk.Builder()
+        builder = Gtk.Builder()
         builder.set_translation_domain(conf.__package__)
         ui_file = gnomeglade.ui_file("meldapp.ui")
         builder.add_objects_from_file(ui_file, ["about"])
@@ -552,10 +552,10 @@ class MeldWindow(gnomeglade.Component):
     # Toolbar and menu items (misc)
     #
     def on_menu_edit_down_activate(self, *args):
-        self.current_doc().next_diff(gtk.gdk.SCROLL_DOWN)
+        self.current_doc().next_diff(Gdk.ScrollDirection.DOWN)
 
     def on_menu_edit_up_activate(self, *args):
-        self.current_doc().next_diff(gtk.gdk.SCROLL_UP)
+        self.current_doc().next_diff(Gdk.ScrollDirection.UP)
 
     def on_open_external(self, *args):
         self.current_doc().open_external()
@@ -586,7 +586,7 @@ class MeldWindow(gnomeglade.Component):
             self.ui.remove_action_group(self.tab_switch_actiongroup)
 
         self.tab_switch_merge_id = self.ui.new_merge_id()
-        self.tab_switch_actiongroup = gtk.ActionGroup("TabSwitchActions")
+        self.tab_switch_actiongroup = Gtk.ActionGroup("TabSwitchActions")
         self.ui.insert_action_group(self.tab_switch_actiongroup)
         group = None
         current_page = self.notebook.get_current_page()
@@ -595,10 +595,9 @@ class MeldWindow(gnomeglade.Component):
             label = self.notebook.get_menu_label_text(page) or ""
             name = "SwitchTab%d" % i
             tooltip = _("Switch to this tab")
-            action = gtk.RadioAction(name, label, tooltip, None, i)
-            action.set_group(group)
-            if group is None:
-                group = action
+            action = Gtk.RadioAction(name, label, tooltip, None, i)
+            action.join_group(group)
+            group = action
             action.set_active(current_page == i)
 
             def current_tab_changed_cb(action, current):
@@ -612,12 +611,12 @@ class MeldWindow(gnomeglade.Component):
             self.tab_switch_actiongroup.add_action_with_accel(action, accel)
             self.ui.add_ui(self.tab_switch_merge_id,
                            "/Menubar/TabMenu/TabPlaceholder",
-                           name, name, gtk.UI_MANAGER_MENUITEM, False)
+                           name, name, Gtk.UIManagerItemType.MENUITEM, False)
 
     def try_remove_page(self, page, appquit=0):
         "See if a page will allow itself to be removed"
         response = page.on_delete_event(appquit)
-        if response != gtk.RESPONSE_CANCEL:
+        if response != Gtk.ResponseType.CANCEL:
             if hasattr(page, 'scheduler'):
                 self.scheduler.remove_scheduler(page.scheduler)
             page_num = self.notebook.page_num(page.widget)
@@ -642,7 +641,7 @@ class MeldWindow(gnomeglade.Component):
 
     def on_file_changed(self, srcpage, filename):
         for c in self.notebook.get_children():
-            page = c.get_data("pyobject")
+            page = c.pyobject
             if page != srcpage:
                 page.on_file_changed(filename)
 
@@ -728,7 +727,7 @@ class MeldWindow(gnomeglade.Component):
                     else:
                         # exit at first non found directory + file
                         misc.run_dialog(_("Cannot compare a mixture of files and directories.\n"),
-                                        parent=self, buttonstype=gtk.BUTTONS_OK)
+                                        parent=self, buttonstype=Gtk.ButtonsType.OK)
                         return
                 else:
                     lastfilename = os.path.basename(elem)
@@ -798,7 +797,7 @@ class MeldWindow(gnomeglade.Component):
         "Get the current doc or a dummy object if there is no current"
         index = self.notebook.get_current_page()
         if index >= 0:
-            page = self.notebook.get_nth_page(index).get_data("pyobject")
+            page = self.notebook.get_nth_page(index).pyobject
             if isinstance(page, melddoc.MeldDoc):
                 return page
 
diff --git a/meld/misc.py b/meld/misc.py
index be2fc30..ae572d9 100644
--- a/meld/misc.py
+++ b/meld/misc.py
@@ -27,9 +27,9 @@ import shutil
 import re
 import subprocess
 
-import gio
-import gobject
-import gtk
+from gi.repository import Gio
+from gi.repository import GObject
+from gi.repository import Gtk
 
 
 if os.name != "nt":
@@ -42,7 +42,7 @@ else:
         return rlist, wlist, xlist
 
 
-def error_dialog(primary, secondary, parent=None, messagetype=gtk.MESSAGE_ERROR):
+def error_dialog(primary, secondary, parent=None, messagetype=Gtk.MessageType.ERROR):
     """A common error dialog handler for Meld
 
     This should only ever be used as a last resort, and for errors that
@@ -55,28 +55,28 @@ def error_dialog(primary, secondary, parent=None, messagetype=gtk.MESSAGE_ERROR)
         from meld.meldapp import app
         parent = app.window.widget
 
-    dialog = gtk.MessageDialog(
+    dialog = Gtk.MessageDialog(
         parent=parent,
-        flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+        flags=Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
         type=messagetype,
-        buttons=gtk.BUTTONS_CLOSE,
+        buttons=Gtk.ButtonsType.CLOSE,
         message_format=primary)
     dialog.format_secondary_markup(secondary)
     dialog.run()
     dialog.destroy()
 
 
-def run_dialog( text, parent=None, messagetype=gtk.MESSAGE_WARNING, buttonstype=gtk.BUTTONS_OK, 
extrabuttons=()):
+def run_dialog( text, parent=None, messagetype=Gtk.MessageType.WARNING, buttonstype=Gtk.ButtonsType.OK, 
extrabuttons=()):
     """Run a dialog with text 'text'.
        Extra buttons are passed as tuples of (button label, response id).
     """
-    escaped = gobject.markup_escape_text(text)
-    d = gtk.MessageDialog(None,
-        gtk.DIALOG_DESTROY_WITH_PARENT,
+    escaped = GObject.markup_escape_text(text)
+    d = Gtk.MessageDialog(None,
+        Gtk.DialogFlags.DESTROY_WITH_PARENT,
         messagetype,
         buttonstype,
         '<span weight="bold" size="larger">%s</span>' % escaped)
-    if parent and isinstance(parent, gtk.Window):
+    if parent and isinstance(parent, Gtk.Window):
         d.set_transient_for(parent.widget.get_toplevel())
     for b,rid in extrabuttons:
         d.add_button(b, rid)
@@ -93,8 +93,8 @@ def run_dialog( text, parent=None, messagetype=gtk.MESSAGE_WARNING, buttonstype=
 
 def open_uri(uri, timestamp=0):
     try:
-        gtk.show_uri(gtk.gdk.screen_get_default(), uri, timestamp)
-    except gio.Error:
+        Gtk.show_uri(Gdk.Screen.get_default(), uri, timestamp)
+    except Gio.Error:
         if uri.startswith("http://";):
             import webbrowser
             webbrowser.open_new_tab(uri)
@@ -104,33 +104,35 @@ def open_uri(uri, timestamp=0):
 
 # Taken from epiphany
 def position_menu_under_widget(menu, widget):
-    container = widget.get_ancestor(gtk.Container)
+    container = widget.get_ancestor(Gtk.Container)
 
-    widget_width, widget_height = widget.size_request()
-    menu_width, menu_height = menu.size_request()
+    widget_width = widget.get_allocation().width
+    menu_width = menu.get_allocation().width
+    menu_height = menu.get_allocation().height
 
     screen = menu.get_screen()
-    monitor_num = screen.get_monitor_at_window(widget.window)
+    monitor_num = screen.get_monitor_at_window(widget.get_window())
     if monitor_num < 0:
         monitor_num = 0
     monitor = screen.get_monitor_geometry(monitor_num)
 
-    x, y = widget.window.get_origin()
-    if widget.flags() & gtk.NO_WINDOW:
-        x += widget.allocation.x
-        y += widget.allocation.y
+    unused, x, y = widget.get_window().get_origin()
+    allocation = widget.get_allocation()
+    if not widget.get_has_window():
+        x += allocation.x
+        y += allocation.y
 
-    if container.get_direction() == gtk.TEXT_DIR_LTR:
-        x += widget.allocation.width - widget_width
+    if container.get_direction() == Gtk.TextDirection.LTR:
+        x += allocation.width - widget_width
     else:
         x += widget_width - menu_width
 
-    if (y + widget.allocation.height + menu_height) <= monitor.y + monitor.height:
-        y += widget.allocation.height
+    if (y + allocation.height + menu_height) <= monitor.y + monitor.height:
+        y += allocation.height
     elif (y - menu_height) >= monitor.y:
         y -= menu_height
-    elif monitor.y + monitor.height - (y + widget.allocation.height) > y:
-        y += widget.allocation.height
+    elif monitor.y + monitor.height - (y + allocation.height) > y:
+        y += allocation.height
     else:
         y -= menu_height
 
@@ -138,11 +140,11 @@ def position_menu_under_widget(menu, widget):
 
 def make_tool_button_widget(label):
     """Make a GtkToolButton label-widget suggestive of a menu dropdown"""
-    arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE)
-    label = gtk.Label(label)
-    hbox = gtk.HBox(spacing=3)
-    hbox.pack_end(arrow)
-    hbox.pack_end(label)
+    arrow = Gtk.Arrow(Gtk.ArrowType.DOWN, Gtk.ShadowType.NONE)
+    label = Gtk.Label(label=label)
+    hbox = Gtk.HBox(spacing=3)
+    hbox.pack_end(arrow, True, True, 0)
+    hbox.pack_end(label, True, True, 0)
     hbox.show_all()
     return hbox
 
diff --git a/meld/newdifftab.py b/meld/newdifftab.py
index e03c7db..1969feb 100644
--- a/meld/newdifftab.py
+++ b/meld/newdifftab.py
@@ -17,25 +17,25 @@
 
 import os
 
-import gobject
-import gtk
+from gi.repository import GObject
+from gi.repository import Gtk
 
 from .ui import gnomeglade
 
 from .meldapp import app
 
 
-class NewDiffTab(gobject.GObject, gnomeglade.Component):
+class NewDiffTab(GObject.GObject, gnomeglade.Component):
 
     __gtype_name__ = "NewDiffTab"
 
     __gsignals__ = {
-        'diff-created': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+        'diff-created': (GObject.SignalFlags.RUN_FIRST, None,
                          (object,)),
     }
 
     def __init__(self, parentapp):
-        gobject.GObject.__init__(self)
+        GObject.GObject.__init__(self)
         gnomeglade.Component.__init__(self, "tab-placeholder.ui",
                                       "new_comparison_tab")
         self.map_widgets_into_lists(["file_chooser", "dir_chooser",
@@ -130,4 +130,4 @@ class NewDiffTab(gobject.GObject, gnomeglade.Component):
         pass
 
     def on_delete_event(self, *args):
-        return gtk.RESPONSE_OK
+        return Gtk.ResponseType.OK
diff --git a/meld/patchdialog.py b/meld/patchdialog.py
index e45ef94..6075fe4 100644
--- a/meld/patchdialog.py
+++ b/meld/patchdialog.py
@@ -20,8 +20,8 @@ import difflib
 from gettext import gettext as _
 import os
 
-import gtk
-import pango
+from gi.repository import Gtk
+from gi.repository import Pango
 
 from .ui import gnomeglade
 
@@ -45,7 +45,7 @@ class PatchDialog(gnomeglade.Component):
         srcviewer.set_language(buf, lang)
         srcviewer.set_highlight_syntax(buf, True)
 
-        fontdesc = pango.FontDescription(self.prefs.get_current_font())
+        fontdesc = Pango.FontDescription(self.prefs.get_current_font())
         self.textview.modify_font(fontdesc)
         self.textview.set_editable(False)
 
@@ -60,7 +60,7 @@ class PatchDialog(gnomeglade.Component):
 
     def on_preference_changed(self, key, value):
         if key == "use_custom_font" or key == "custom_font":
-            fontdesc = pango.FontDescription(self.prefs.get_current_font())
+            fontdesc = Pango.FontDescription(self.prefs.get_current_font())
             self.textview.modify_font(fontdesc)
 
     def on_buffer_selection_changed(self, radiobutton):
@@ -111,7 +111,7 @@ class PatchDialog(gnomeglade.Component):
 
             # Copy patch to clipboard
             if result == 1:
-                clip = gtk.clipboard_get()
+                clip = Gtk.clipboard_get()
                 clip.set_text(txt)
                 clip.store()
                 break
diff --git a/meld/preferences.py b/meld/preferences.py
index 1342110..12e5ee9 100644
--- a/meld/preferences.py
+++ b/meld/preferences.py
@@ -22,7 +22,7 @@ import string
 
 from gettext import gettext as _
 
-import gtk
+from gi.repository import Gtk
 
 from . import filters
 from . import misc
@@ -173,12 +173,12 @@ class PreferencesDialog(gnomeglade.Component):
                 w.set_tooltip_text(no_sourceview_text)
         # TODO: This doesn't restore the state of character wrapping when word
         # wrapping is disabled, but this is hard with our existing gconf keys
-        if self.prefs.edit_wrap_lines != gtk.WRAP_NONE:
-            if self.prefs.edit_wrap_lines == gtk.WRAP_CHAR:
+        if self.prefs.edit_wrap_lines != Gtk.WrapMode.NONE:
+            if self.prefs.edit_wrap_lines == Gtk.WrapMode.CHAR:
                 self.checkbutton_split_words.set_active(False)
             self.checkbutton_wrap_text.set_active(True)
 
-        size_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+        size_group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
         size_group.add_widget(self.label1)
         size_group.add_widget(self.label2)
         size_group.add_widget(self.label16)
@@ -191,32 +191,32 @@ class PreferencesDialog(gnomeglade.Component):
         # file filters
         self.filefilter = FilterList(self.prefs, "filters",
                                      filters.FilterEntry.SHELL)
-        self.file_filters_tab.pack_start(self.filefilter.widget)
+        self.file_filters_tab.pack_start(self.filefilter.widget, True, True, 0)
         self.checkbutton_ignore_symlinks.set_active( self.prefs.ignore_symlinks)
 
         # text filters
         self.textfilter = FilterList(self.prefs, "regexes",
                                      filters.FilterEntry.REGEX)
-        self.text_filters_tab.pack_start(self.textfilter.widget)
+        self.text_filters_tab.pack_start(self.textfilter.widget, True, True, 0)
         self.checkbutton_ignore_blank_lines.set_active( self.prefs.ignore_blank_lines )
         # encoding
         self.entry_text_codecs.set_text( self.prefs.text_codecs )
 
         columnlist = ColumnList(self.prefs, "dirdiff_columns")
-        self.column_list_vbox.pack_start(columnlist.widget)
+        self.column_list_vbox.pack_start(columnlist.widget, True, True, 0)
 
         self.checkbutton_shallow_compare.set_active(
                 self.prefs.dirdiff_shallow_comparison)
 
         self.combo_timestamp.lock = True
-        model = gtk.ListStore(str, int)
+        model = Gtk.ListStore(str, int)
         active_idx = 0
         for i, entry in enumerate(TIMESTAMP_RESOLUTION_PRESETS):
             model.append(entry)
             if entry[1] == self.prefs.dirdiff_time_resolution_ns:
                 active_idx = i
         self.combo_timestamp.set_model(model)
-        cell = gtk.CellRendererText()
+        cell = Gtk.CellRendererText()
         self.combo_timestamp.pack_start(cell, False)
         self.combo_timestamp.add_attribute(cell, 'text', 0)
         self.combo_timestamp.set_active(active_idx)
@@ -414,10 +414,10 @@ class MeldPreferences(prefs.Preferences):
             style = self._gconf.get_string(
                       '/desktop/gnome/interface/toolbar_style') or "both-horiz"
         toolbar_styles = {
-            "both": gtk.TOOLBAR_BOTH, "text": gtk.TOOLBAR_TEXT,
-            "icon": gtk.TOOLBAR_ICONS, "icons": gtk.TOOLBAR_ICONS,
-            "both_horiz": gtk.TOOLBAR_BOTH_HORIZ,
-            "both-horiz": gtk.TOOLBAR_BOTH_HORIZ
+            "both": Gtk.ToolbarStyle.BOTH, "text": Gtk.ToolbarStyle.TEXT,
+            "icon": Gtk.ToolbarStyle.ICONS, "icons": Gtk.ToolbarStyle.ICONS,
+            "both_horiz": Gtk.ToolbarStyle.BOTH_HORIZ,
+            "both-horiz": Gtk.ToolbarStyle.BOTH_HORIZ
         }
         return toolbar_styles[style]
 
diff --git a/meld/recent.py b/meld/recent.py
index 54284b3..d1365e0 100644
--- a/meld/recent.py
+++ b/meld/recent.py
@@ -32,9 +32,9 @@ import tempfile
 
 from gettext import gettext as _
 
-import gio
+from gi.repository import Gio
 import glib
-import gtk
+from gi.repository import Gtk
 
 from . import misc
 
@@ -56,8 +56,8 @@ class RecentFiles(object):
     app_exec = "meld"
 
     def __init__(self, exec_path=None):
-        self.recent_manager = gtk.recent_manager_get_default()
-        self.recent_filter = gtk.RecentFilter()
+        self.recent_manager = Gtk.RecentManager.get_default()
+        self.recent_filter = Gtk.RecentFilter()
         self.recent_filter.add_application(self.app_name)
         self._stored_comparisons = []
         # Should be argv[0] to support roundtripping in uninstalled use
@@ -88,10 +88,10 @@ class RecentFiles(object):
         # the corresponding comparison file
         comparison_key = (comp_type, tuple(paths))
         if comparison_key in self._stored_comparisons:
-            gio_file = gio.File(uri=self._stored_comparisons[comparison_key])
+            gio_file = Gio.File.new_for_uri(self._stored_comparisons[comparison_key])
         else:
             recent_path = self._write_recent_file(comp_type, paths)
-            gio_file = gio.File(path=recent_path)
+            gio_file = Gio.File.new_for_path(recent_path)
 
         if len(paths) > 1:
             display_name = " : ".join(misc.shorten_names(*paths))
@@ -105,14 +105,13 @@ class RecentFiles(object):
         # FIXME: Should this be translatable? It's not actually used anywhere.
         description = "%s comparison\n%s" % (comp_type, ", ".join(paths))
 
-        recent_metadata = {
-            "mime_type": "application/x-meld-comparison",
-            "app_name": self.app_name,
-            "app_exec": "%s --comparison-file %%u" % self.app_exec,
-            "display_name": display_name.encode('utf8'),
-            "description": description.encode('utf8'),
-            "is_private": True,
-        }
+        recent_metadata = Gtk.RecentData()
+        recent_metadata.mime_type = "application/x-meld-comparison"
+        recent_metadata.app_name = self.app_name
+        recent_metadata.app_exec = "%s --comparison-file %%u" % self.app_exec
+        recent_metadata.display_name = display_name.encode('utf8')
+        recent_metadata.description = description.encode('utf8')
+        recent_metadata.is_private = True
         self.recent_manager.add_full(gio_file.get_uri(), recent_metadata)
 
     def read(self, uri):
@@ -121,7 +120,7 @@ class RecentFiles(object):
         Returns the comparison type, the paths involved and the comparison
         flags.
         """
-        gio_file = gio.File(uri=uri)
+        gio_file = Gio.File.new_for_uri(uri)
         path = gio_file.get_path()
         if not gio_file.query_exists() or not path:
             raise IOError("File does not exist")
@@ -145,7 +144,7 @@ class RecentFiles(object):
         return comp_type, paths, flags
 
     def _write_recent_file(self, comp_type, paths):
-        # TODO: Use GKeyFile instead, and return a gio.File. This is why we're
+        # TODO: Use GKeyFile instead, and return a Gio.File. This is why we're
         # using ';' to join comparison paths.
         with tempfile.NamedTemporaryFile(prefix='recent-',
                                          suffix=self.recent_suffix,
@@ -171,7 +170,7 @@ class RecentFiles(object):
 
         # Remove any comparison files that are not listed by RecentManager
         item_uris = [item.get_uri() for item in meld_items]
-        item_paths = [gio.File(uri=uri).get_path() for uri in item_uris]
+        item_paths = [Gio.File.new_for_uri(uri).get_path() for uri in item_uris]
         stored = [p for p in os.listdir(self.recent_path)
                   if p.endswith(self.recent_suffix)]
         for path in stored:
@@ -193,20 +192,26 @@ class RecentFiles(object):
             self._stored_comparisons[comp[:2]] = uri
 
     def _filter_items(self, recent_filter, items):
-        getters = {gtk.RECENT_FILTER_URI: "uri",
-                   gtk.RECENT_FILTER_DISPLAY_NAME: "display_name",
-                   gtk.RECENT_FILTER_MIME_TYPE: "mime_type",
-                   gtk.RECENT_FILTER_APPLICATION: "applications",
-                   gtk.RECENT_FILTER_GROUP: "groups",
-                   gtk.RECENT_FILTER_AGE: "age"}
+        getters = {Gtk.RecentFilterFlags.URI: "uri",
+                   Gtk.RecentFilterFlags.DISPLAY_NAME: "display_name",
+                   Gtk.RecentFilterFlags.MIME_TYPE: "mime_type",
+                   Gtk.RecentFilterFlags.APPLICATION: "applications",
+                   Gtk.RecentFilterFlags.GROUP: "groups",
+                   Gtk.RecentFilterFlags.AGE: "age"}
         needed = recent_filter.get_needed()
         attrs = [v for k, v in getters.iteritems() if needed & k]
 
         filtered_items = []
         for i in items:
-            filter_info = {}
+            filter_data = {}
             for attr in attrs:
-                filter_info[attr] = getattr(i, "get_" + attr)()
+                filter_data[attr] = getattr(i, "get_" + attr)()
+            filter_info = Gtk.RecentFilterInfo()
+            for f, v in filter_data.iteritems():
+                # https://bugzilla.gnome.org/show_bug.cgi?id=695970
+                if isinstance(v, list):
+                    continue
+                setattr(filter_info, f, v)
             if recent_filter.filter(filter_info):
                 filtered_items.append(i)
         return filtered_items
diff --git a/meld/tree.py b/meld/tree.py
index ad17508..aa57c9d 100644
--- a/meld/tree.py
+++ b/meld/tree.py
@@ -16,15 +16,16 @@
 ### USA.
 
 import os
-import gobject
-import gtk
-import pango
+from gi.repository import GObject
+from gi.repository import Gdk
+from gi.repository import Gtk
+from gi.repository import Pango
 
 COL_PATH, COL_STATE, COL_TEXT, COL_ICON, COL_TINT, COL_FG, COL_STYLE, \
     COL_WEIGHT, COL_STRIKE, COL_END = list(range(10))
 
-COL_TYPES = (str, str, str, str, str, gtk.gdk.Color, pango.Style,
-             pango.Weight, bool)
+COL_TYPES = (str, str, str, str, str, Gdk.Color, Pango.Style,
+             Pango.Weight, bool)
 
 
 from meld.vc._vc import \
@@ -36,13 +37,13 @@ from meld.vc._vc import \
     CONFLICT_MERGED, CONFLICT_OTHER, CONFLICT_THIS
 
 
-class DiffTreeStore(gtk.TreeStore):
+class DiffTreeStore(Gtk.TreeStore):
 
     def __init__(self, ntree, types):
         full_types = []
         for col_type in (COL_TYPES + tuple(types)):
             full_types.extend([col_type] * ntree)
-        gtk.TreeStore.__init__(self, *full_types)
+        Gtk.TreeStore.__init__(self, *full_types)
         self.ntree = ntree
         self._setup_default_styles()
 
@@ -51,13 +52,13 @@ class DiffTreeStore(gtk.TreeStore):
         self._setup_default_styles(style)
 
     def _setup_default_styles(self, style=None):
-        roman, italic = pango.STYLE_NORMAL, pango.STYLE_ITALIC
-        normal, bold = pango.WEIGHT_NORMAL, pango.WEIGHT_BOLD
+        roman, italic = Pango.Style.NORMAL, Pango.Style.ITALIC
+        normal, bold = Pango.Weight.NORMAL, Pango.Weight.BOLD
 
         if style:
-            lookup = lambda color_id, default: style.lookup_color(color_id)
+            lookup = lambda color_id, default: style.lookup_color(color_id)[1]
         else:
-            lookup = lambda color_id, default: gtk.gdk.color_parse(default)
+            lookup = lambda color_id, default: Gdk.color_parse(default)
 
         unk_fg = lookup("unknown-text", "#888888")
         new_fg = lookup("insert-text", "#008800")
@@ -133,7 +134,7 @@ class DiffTreeStore(gtk.TreeStore):
 
     def set_path_state(self, it, pane, state, isdir=0):
         fullname = self.get_value(it, self.column_index(COL_PATH,pane))
-        name = gobject.markup_escape_text(os.path.basename(fullname))
+        name = GObject.markup_escape_text(os.path.basename(fullname))
         self.set_state(it, pane, state, name, isdir)
 
     def set_state(self, it, pane, state, label, isdir=0):
@@ -143,7 +144,10 @@ class DiffTreeStore(gtk.TreeStore):
         self.set_value(it, col_idx(COL_STATE, pane), str(state))
         self.set_value(it, col_idx(COL_TEXT,  pane), label)
         self.set_value(it, col_idx(COL_ICON,  pane), icon)
-        self.set_value(it, col_idx(COL_TINT,  pane), tint)
+        # FIXME: This is horrible, but EmblemCellRenderer crashes
+        # if you try to give it a Gdk.Color property
+        tint_str = tint.to_string() if tint else "#fff"
+        self.set_value(it, col_idx(COL_TINT,  pane), tint_str)
 
         fg, style, weight, strike = self.text_attributes[state]
         self.set_value(it, col_idx(COL_FG, pane), fg)
@@ -183,7 +187,7 @@ class DiffTreeStore(gtk.TreeStore):
         while it:
             path = self.get_path(it)
             if path[-1]:
-                path = path[:-1] + (path[-1]-1,)
+                path = path[:-1] + [path[-1] - 1]
                 it = self.get_iter(path)
                 while 1:
                     nc = self.iter_n_children(it)
diff --git a/meld/ui/emblemcellrenderer.py b/meld/ui/emblemcellrenderer.py
index 6a7e526..3de2c21 100644
--- a/meld/ui/emblemcellrenderer.py
+++ b/meld/ui/emblemcellrenderer.py
@@ -17,26 +17,31 @@
 ### USA.
 
 import cairo
-import gobject
-import gtk
+from gi.repository import GObject
+from gi.repository import Gdk
+from gi.repository import Gtk
 
 
-class EmblemCellRenderer(gtk.GenericCellRenderer):
+class EmblemCellRenderer(Gtk.CellRenderer):
+
+    __gtype_name__ = "EmblemCellRenderer"
 
     __gproperties__ = {
         "icon-name":   (str, "Named icon",
                         "Name for base icon",
-                        "text-x-generic", gobject.PARAM_READWRITE),
+                        "text-x-generic", GObject.PARAM_READWRITE),
         "emblem-name": (str, "Named emblem icon",
                         "Name for emblem icon to overlay",
-                        None, gobject.PARAM_READWRITE),
+                        None, GObject.PARAM_READWRITE),
         "icon-tint":   (str, "Icon tint",
                         "GDK-parseable color to be used to tint icon",
-                        None, gobject.PARAM_READWRITE),
+                        None, GObject.PARAM_READWRITE),
     }
 
+    icon_cache = {}
+
     def __init__(self):
-        self.__gobject_init__()
+        super(EmblemCellRenderer, self).__init__()
         self._icon_name = "text-x-generic"
         self._emblem_name = None
         self._icon_tint = None
@@ -54,7 +59,7 @@ class EmblemCellRenderer(gtk.GenericCellRenderer):
         elif pspec.name == "icon-tint":
             self._icon_tint = value
             if self._icon_tint:
-                self._tint_color = gtk.gdk.color_parse(value)
+                self._tint_color = Gdk.color_parse(value)
             else:
                 self._tint_color = None
         else:
@@ -70,9 +75,14 @@ class EmblemCellRenderer(gtk.GenericCellRenderer):
         else:
             raise AttributeError("unknown property %s" % pspec.name)
 
-    def on_render(self, window, widget, background_area, cell_area,
-                  expose_area, flags):
-        context = window.cairo_create()
+    def _get_pixbuf(self, name, size):
+        if (name, size) not in self.icon_cache:
+            icon_theme = Gtk.IconTheme.get_default()
+            pixbuf = icon_theme.load_icon(name, size, 0).copy()
+            self.icon_cache[(name, size)] = pixbuf
+        return self.icon_cache[(name, size)]
+
+    def do_render(self, context, widget, background_area, cell_area, flags):
         context.translate(cell_area.x, cell_area.y)
         context.rectangle(0, 0, cell_area.width, cell_area.height)
         context.clip()
@@ -80,14 +90,13 @@ class EmblemCellRenderer(gtk.GenericCellRenderer):
         # TODO: Incorporate padding
         context.push_group()
         if self._icon_name:
-            icon_theme = gtk.icon_theme_get_default()
-            # Assumes square icons; may break if we don't get the requested size
-            pixbuf = icon_theme.load_icon(self._icon_name,
-                                          self._icon_size, 0).copy() # FIXME: copy?
-
+            pixbuf = self._get_pixbuf(self._icon_name, self._icon_size)
             context.set_operator(cairo.OPERATOR_SOURCE)
-            context.set_source_pixbuf(pixbuf, 0, 0)
-            context.rectangle(0, 0, cell_area.width, cell_area.height)
+            # Assumes square icons; may break if we don't get the requested size
+            height_offset = int((cell_area.height - pixbuf.get_height())/2)
+            Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, height_offset)
+            context.rectangle(0, height_offset,
+                              pixbuf.get_width(), pixbuf.get_height())
             context.fill()
 
             if self._tint_color:
@@ -104,21 +113,19 @@ class EmblemCellRenderer(gtk.GenericCellRenderer):
                 context.paint()
 
             if self._emblem_name:
-                emblem_pixbuf = icon_theme.load_icon(self._emblem_name,
-                                                     self._emblem_size, 0)
-
+                pixbuf = self._get_pixbuf(self._emblem_name, self._emblem_size)
                 x_offset = self._icon_size - self._emblem_size
                 context.set_operator(cairo.OPERATOR_OVER)
-                context.set_source_pixbuf(emblem_pixbuf, x_offset, 0)
+                Gdk.cairo_set_source_pixbuf(context, pixbuf, x_offset, 0)
                 context.rectangle(x_offset, 0,
                                   cell_area.width, self._emblem_size)
                 context.fill()
 
-        context.set_source(context.pop_group())
+        context.pop_group_to_source()
         context.set_operator(cairo.OPERATOR_OVER)
         context.paint()
 
-    def on_get_size(self, widget, cell_area):
+    def do_get_size(self, widget, cell_area):
         # TODO: Account for cell_area if we have alignment set
         x_offset, y_offset = 0, 0
         width, height = self._icon_size, self._icon_size
diff --git a/meld/ui/findbar.py b/meld/ui/findbar.py
index 861a08f..92a7485 100644
--- a/meld/ui/findbar.py
+++ b/meld/ui/findbar.py
@@ -16,7 +16,8 @@
 ### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 ### USA.
 
-import gtk
+from gi.repository import Gdk
+from gi.repository import Gtk
 import re
 
 from meld import misc
@@ -30,7 +31,8 @@ class FindBar(gnomeglade.Component):
         gnomeglade.Component.__init__(self, "findbar.ui", "findbar",
                                       ["arrow_left", "arrow_right"])
         self.textview = None
-        self.orig_base_color = self.find_entry.get_style().base[0]
+        context = self.find_entry.get_style_context()
+        self.orig_base_color = context.get_background_color(Gtk.StateFlags.NORMAL)
         self.arrow_left.show()
         self.arrow_right.show()
         parent.connect('set-focus-child', self.on_focus_child)
@@ -115,11 +117,9 @@ class FindBar(gnomeglade.Component):
             self.textview.scroll_to_mark(buf.get_insert(), 0.25)
 
     def on_find_entry_changed(self, entry):
-        entry.modify_base(gtk.STATE_NORMAL, self.orig_base_color)
+        entry.override_background_color(Gtk.StateType.NORMAL,
+                                        self.orig_base_color)
 
-        #
-        # find/replace buffer
-        #
     def _find_text(self, start_offset=1, backwards=False, wrap=True):
         match_case = self.match_case.get_active()
         whole_word = self.whole_word.get_active()
@@ -165,6 +165,11 @@ class FindBar(gnomeglade.Component):
                 return True
             else:
                 buf.place_cursor(buf.get_iter_at_mark(buf.get_insert()))
-                self.find_entry.modify_base(gtk.STATE_NORMAL,
-                                            gtk.gdk.color_parse("#ffdddd"))
+                # FIXME: Even though docs suggest this should work, it does
+                # not. It just sets the selection colour on the text, without
+                # affecting the entry colour at all.
+                color = Gdk.RGBA()
+                color.parse("#ffdddd")
+                self.find_entry.override_background_color(
+                    Gtk.StateType.NORMAL, color)
                 self.wrap_box.set_visible(False)
diff --git a/meld/ui/gnomeglade.py b/meld/ui/gnomeglade.py
index 7e82ddd..db58ac3 100644
--- a/meld/ui/gnomeglade.py
+++ b/meld/ui/gnomeglade.py
@@ -18,7 +18,7 @@
 
 import os
 
-import gtk
+from gi.repository import Gtk
 
 import meld.conf
 
@@ -31,26 +31,26 @@ def ui_file(filename):
 
 
 class Component(object):
-    """Base class for all gtk.Builder created objects
+    """Base class for all Gtk.Builder created objects
 
     This class loads the UI file, autoconnects signals, and makes
     widgets available as attributes. The toplevel widget is stored as
     'self.widget'.
 
     The python object can be accessed from the widget itself via
-    widget.get_data("pygobject"), which is sadly sometimes necessary.
+    widget.pygobject, which is sadly sometimes necessary.
     """
 
     def __init__(self, filename, root, extra=None):
         """Load the widgets from the node 'root' in file 'filename'"""
-        self.builder = gtk.Builder()
+        self.builder = Gtk.Builder()
         self.builder.set_translation_domain(meld.conf.__package__)
         objects = [root] + extra if extra else [root]
         filename = ui_file(filename)
         self.builder.add_objects_from_file(filename, objects)
         self.builder.connect_signals(self)
         self.widget = getattr(self, root)
-        self.widget.set_data("pyobject", self)
+        self.widget.pyobject = self
 
     def __getattr__(self, key):
         """Allow UI builder widgets to be accessed as self.widgetname"""
diff --git a/meld/ui/historyentry.py b/meld/ui/historyentry.py
index e96e873..adb467c 100644
--- a/meld/ui/historyentry.py
+++ b/meld/ui/historyentry.py
@@ -20,9 +20,9 @@ import os
 import sys
 
 import glib
-import gobject
-import gtk
-import pango
+from gi.repository import GObject
+from gi.repository import Gtk
+from gi.repository import Pango
 
 # This file started off as a Python translation of:
 #  * gedit/gedit/gedit-history-entry.c
@@ -55,18 +55,18 @@ def _clamp_list_store(liststore, max_items):
         valid = liststore.remove(it)
 
 
-class HistoryCombo(gtk.ComboBox):
+class HistoryCombo(Gtk.ComboBox):
     __gtype_name__ = "HistoryCombo"
 
     __gproperties__ = {
         "history-id": (str, "History ID",
                        "Identifier associated with entry's history store",
                        None,
-                       gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_READWRITE),
+                       GObject.PARAM_CONSTRUCT_ONLY | GObject.PARAM_READWRITE),
         "history-length": (int, "History length",
                            "Number of history items to display in the combo",
                            1, 20, HISTORY_ENTRY_HISTORY_LENGTH_DEFAULT,
-                           gobject.PARAM_READWRITE),
+                           GObject.PARAM_READWRITE),
     }
 
     def __init__(self, **kwargs):
@@ -88,10 +88,10 @@ class HistoryCombo(gtk.ComboBox):
         self._history_id = None
         self._history_length = HISTORY_ENTRY_HISTORY_LENGTH_DEFAULT
 
-        self.set_model(gtk.ListStore(str, str))
-        rentext = gtk.CellRendererText()
+        self.set_model(Gtk.ListStore(str, str))
+        rentext = Gtk.CellRendererText()
         rentext.props.width_chars = 60
-        rentext.props.ellipsize = pango.ELLIPSIZE_END
+        rentext.props.ellipsize = Pango.EllipsizeMode.END
         self.pack_start(rentext, True)
         self.set_attributes(rentext, text=0)
 
diff --git a/meld/ui/msgarea.py b/meld/ui/msgarea.py
index 3091654..c42fb35 100644
--- a/meld/ui/msgarea.py
+++ b/meld/ui/msgarea.py
@@ -21,22 +21,22 @@
 # GTK+.
 # Copyright (C) 2013 Kai Willadsen <kai willadsen gmail com>
 
-import gtk
+from gi.repository import Gtk
 
 from meld.ui.wraplabel import WrapLabel
 
 
 def layout_text_and_icon(stockid, primary_text, secondary_text=None):
-    hbox_content = gtk.HBox(False, 8)
+    hbox_content = Gtk.HBox(False, 8)
     hbox_content.show()
 
-    image = gtk.Image()
-    image.set_from_stock(stockid, gtk.ICON_SIZE_DIALOG)
+    image = Gtk.Image()
+    image.set_from_stock(stockid, Gtk.IconSize.DIALOG)
     image.show()
     hbox_content.pack_start(image, False, False, 0)
     image.set_alignment(0.5, 0.5)
 
-    vbox = gtk.VBox(False, 6)
+    vbox = Gtk.VBox(False, 6)
     vbox.show()
     hbox_content.pack_start(vbox, True, True, 0)
 
@@ -47,7 +47,7 @@ def layout_text_and_icon(stockid, primary_text, secondary_text=None):
     primary_label.set_use_markup(True)
     primary_label.set_line_wrap(True)
     primary_label.set_alignment(0, 0.5)
-    primary_label.set_flags(gtk.CAN_FOCUS)
+    primary_label.set_can_focus(True)
     primary_label.set_selectable(True)
 
     if secondary_text:
@@ -55,7 +55,7 @@ def layout_text_and_icon(stockid, primary_text, secondary_text=None):
         secondary_label = WrapLabel(secondary_markup)
         secondary_label.show()
         vbox.pack_start(secondary_label, True, True, 0)
-        secondary_label.set_flags(gtk.CAN_FOCUS)
+        secondary_label.set_can_focus(True)
         secondary_label.set_use_markup(True)
         secondary_label.set_line_wrap(True)
         secondary_label.set_selectable(True)
@@ -64,7 +64,7 @@ def layout_text_and_icon(stockid, primary_text, secondary_text=None):
     return hbox_content
 
 
-class MsgAreaController(gtk.HBox):
+class MsgAreaController(Gtk.HBox):
     __gtype_name__ = "MsgAreaController"
 
     def __init__(self):
@@ -89,9 +89,10 @@ class MsgAreaController(gtk.HBox):
             self.__msgarea = None
         self.__msgid = None
 
-    def new_from_text_and_icon(self, stockid, primary, secondary=None, buttons=[]):
+    def new_from_text_and_icon(self, stockid, primary, secondary=None,
+                               buttons=[]):
         self.clear()
-        msgarea = self.__msgarea = gtk.InfoBar()
+        msgarea = self.__msgarea = Gtk.InfoBar()
 
         for (text, respid) in buttons:
             self.add_button(text, respid)
@@ -99,8 +100,8 @@ class MsgAreaController(gtk.HBox):
         content = layout_text_and_icon(stockid, primary, secondary)
 
         content_area = msgarea.get_content_area()
-        content_area.foreach(content_area.remove)
+        content_area.foreach(content_area.remove, None)
         content_area.add(content)
 
-        self.pack_start(msgarea, expand=True)
+        self.pack_start(msgarea, True, True, 0)
         return msgarea
diff --git a/meld/ui/notebooklabel.py b/meld/ui/notebooklabel.py
index e0eeb2d..54ccaba 100644
--- a/meld/ui/notebooklabel.py
+++ b/meld/ui/notebooklabel.py
@@ -19,10 +19,11 @@
 
 from gettext import gettext as _
 
-import gtk
-import pango
+from gi.repository import Gdk
+from gi.repository import Gtk
+from gi.repository import Pango
 
-gtk.rc_parse_string(
+Gtk.rc_parse_string(
     """
     style "meld-tab-close-button-style" {
         GtkWidget::focus-padding = 0
@@ -33,50 +34,51 @@ gtk.rc_parse_string(
     widget "*.meld-tab-close-button" style "meld-tab-close-button-style"
     """)
 
-class NotebookLabel(gtk.HBox):
+class NotebookLabel(Gtk.HBox):
     __gtype_name__ = "NotebookLabel"
 
     tab_width_in_chars = 30
 
     def __init__(self, iconname, text, onclose):
-        gtk.HBox.__init__(self, False, 4)
+        Gtk.HBox.__init__(self, False, 4)
 
-        label = gtk.Label(text)
+        label = Gtk.Label(label=text)
         # FIXME: ideally, we would use custom ellipsization that ellipsized the
         # two paths separately, but that requires significant changes to label
         # generation in many different parts of the code
-        label.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
+        label.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
         label.set_single_line_mode(True)
         label.set_alignment(0.0, 0.5)
         label.set_padding(0, 0)
 
         context = self.get_pango_context()
-        metrics = context.get_metrics(self.style.font_desc, context.get_language())
-        char_width = metrics.get_approximate_digit_width()
-        (w, h) = gtk.icon_size_lookup_for_settings (self.get_settings(), gtk.ICON_SIZE_MENU)
-        self.set_size_request(self.tab_width_in_chars * pango.PIXELS(char_width) + 2 * w, -1)
-
-        button = gtk.Button()
-        button.set_relief(gtk.RELIEF_NONE)
+        metrics = context.get_metrics(self.get_style().font_desc, context.get_language())
+        char_width = metrics.get_approximate_char_width() / Pango.SCALE
+        valid, w, h = Gtk.icon_size_lookup_for_settings(self.get_settings(), Gtk.IconSize.MENU)
+        # FIXME: PIXELS replacement
+        self.set_size_request(self.tab_width_in_chars * char_width + 2 * w, -1)
+
+        button = Gtk.Button()
+        button.set_relief(Gtk.ReliefStyle.NONE)
         button.set_focus_on_click(False)
-        image = gtk.image_new_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
+        image = Gtk.Image.new_from_stock(Gtk.STOCK_CLOSE, Gtk.IconSize.MENU)
         image.set_tooltip_text(_("Close tab"))
         button.add(image)
         button.set_name("meld-tab-close-button")
         button.set_size_request(w + 2, h + 2)
         button.connect("clicked", onclose)
 
-        icon = gtk.image_new_from_icon_name(iconname, gtk.ICON_SIZE_MENU)
+        icon = Gtk.Image.new_from_icon_name(iconname, Gtk.IconSize.MENU)
 
-        label_box = gtk.EventBox()
-        label_box.add_events(gtk.gdk.BUTTON_PRESS_MASK)
+        label_box = Gtk.EventBox()
+        label_box.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
         label_box.props.visible_window = False
         label_box.connect("button-press-event", self.on_label_clicked)
         label_box.add(label)
 
-        self.pack_start(icon, expand=False)
-        self.pack_start(label_box)
-        self.pack_start(button, expand=False)
+        self.pack_start(icon, False, True, 0)
+        self.pack_start(label_box, True, True, 0)
+        self.pack_start(button, False, True, 0)
         self.set_tooltip_text(text)
         self.show_all()
 
@@ -84,7 +86,7 @@ class NotebookLabel(gtk.HBox):
         self.__onclose = onclose
 
     def on_label_clicked(self, box, event):
-        if event.type == gtk.gdk.BUTTON_PRESS and event.button == 2:
+        if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 2:
             self.__onclose(None)
 
     def get_label_text(self):
diff --git a/meld/ui/statusbar.py b/meld/ui/statusbar.py
index 9e98bca..157d5ef 100644
--- a/meld/ui/statusbar.py
+++ b/meld/ui/statusbar.py
@@ -15,12 +15,12 @@
 ### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 ### USA.
 
-import gobject
-import gtk
-import pango
+from gi.repository import GObject
+from gi.repository import Gtk
+from gi.repository import Pango
 
 
-gtk.rc_parse_string(
+Gtk.rc_parse_string(
     """
     style "meld-statusbar-style" {
         GtkStatusbar::shadow-type = GTK_SHADOW_NONE
@@ -35,24 +35,24 @@ gtk.rc_parse_string(
     """)
 
 
-class MeldStatusBar(gtk.Statusbar):
+class MeldStatusBar(Gtk.Statusbar):
     __gtype_name__ = "MeldStatusBar"
 
     def __init__(self):
-        gtk.Statusbar.__init__(self)
+        GObject.GObject.__init__(self)
         self.props.spacing = 6
 
         if hasattr(self, "get_message_area"):
-            # FIXME: added in 2.20, but not in the corresponding pygtk. Use this if available
+            # FIXME: added in 2.20, but not in the corresponding pyGtk. Use this if available
             hbox = self.get_message_area()
             label = hbox.get_children()[0]
         else:
             frame = self.get_children()[0]
-            self.set_child_packing(frame, False, False, 0, gtk.PACK_START)
+            self.set_child_packing(frame, False, False, 0, Gtk.PACK_START)
             child = frame.get_child()
             # Internal GTK widgetry changed when get_message_area was added.
-            if not isinstance(child, gtk.HBox):
-                hbox = gtk.HBox(False, 4)
+            if not isinstance(child, Gtk.HBox):
+                hbox = Gtk.HBox(False, 4)
                 child.reparent(hbox)
                 frame.add(hbox)
                 hbox.show()
@@ -61,26 +61,26 @@ class MeldStatusBar(gtk.Statusbar):
                 hbox = child
                 label = hbox.get_children()[0]
         hbox.props.spacing = 6
-        label.props.ellipsize = pango.ELLIPSIZE_NONE
+        label.props.ellipsize = Pango.EllipsizeMode.NONE
 
-        self.progress = gtk.ProgressBar()
+        self.progress = Gtk.ProgressBar()
         self.progress.props.pulse_step = 0.02
-        self.progress.props.ellipsize = pango.ELLIPSIZE_END
+        self.progress.props.ellipsize = Pango.EllipsizeMode.END
         self.progress.set_size_request(200, -1)
         progress_font = self.progress.get_style().font_desc
-        progress_font.set_size(progress_font.get_size() - 2 * pango.SCALE)
+        progress_font.set_size(progress_font.get_size() - 2 * Pango.SCALE)
         self.progress.modify_font(progress_font)
-        hbox.pack_start(self.progress, expand=False)
+        hbox.pack_start(self.progress, False, True, 0)
         self.progress.show()
 
         hbox.remove(label)
-        hbox.pack_start(label)
+        hbox.pack_start(label, True, True, 0)
 
-        alignment = gtk.Alignment(xalign=1.0)
-        self.info_box = gtk.HBox(False, 6)
+        alignment = Gtk.Alignment.new(xalign=1.0, yalign=0.5, xscale=1.0, yscale=1.0)
+        self.info_box = Gtk.HBox(False, 6)
         self.info_box.show()
         alignment.add(self.info_box)
-        self.pack_start(alignment, expand=True)
+        self.pack_start(alignment, True, True, 0)
         alignment.show()
 
         self.timeout_source = None
@@ -91,11 +91,11 @@ class MeldStatusBar(gtk.Statusbar):
             def pulse():
                 self.progress.pulse()
                 return True
-            self.timeout_source = gobject.timeout_add(50, pulse)
+            self.timeout_source = GObject.timeout_add(50, pulse)
 
     def stop_pulse(self):
         if self.timeout_source is not None:
-            gobject.source_remove(self.timeout_source)
+            GObject.source_remove(self.timeout_source)
             self.timeout_source = None
         self.progress.set_fraction(0)
         self.progress.hide()
@@ -107,4 +107,4 @@ class MeldStatusBar(gtk.Statusbar):
         for child in self.info_box.get_children():
             self.info_box.remove(child)
         for widget in widgets:
-            self.info_box.pack_end(widget)
+            self.info_box.pack_end(widget, True, True, 0)
diff --git a/meld/ui/vcdialogs.py b/meld/ui/vcdialogs.py
index 567c3b9..f747f8b 100644
--- a/meld/ui/vcdialogs.py
+++ b/meld/ui/vcdialogs.py
@@ -22,8 +22,8 @@ import os
 import textwrap
 from gettext import gettext as _
 
-import gtk
-import pango
+from gi.repository import Gtk
+from gi.repository import Pango
 
 from meld import misc
 from . import gnomeglade
@@ -59,7 +59,7 @@ class CommitDialog(gnomeglade.Component):
         self.changedfiles.set_text("(in %s)\n%s" %
                                    (topdir, "\n".join(to_commit)))
 
-        fontdesc = pango.FontDescription(self.parent.prefs.get_current_font())
+        fontdesc = Pango.FontDescription(self.parent.prefs.get_current_font())
         self.textview.modify_font(fontdesc)
         commit_prefill = self.parent.vc.get_commit_message_prefill()
         if commit_prefill:
@@ -72,7 +72,7 @@ class CommitDialog(gnomeglade.Component):
         context = self.textview.get_pango_context()
         metrics = context.get_metrics(fontdesc, context.get_language())
         char_width = metrics.get_approximate_char_width()
-        self.textview.set_size_request(80 * pango.PIXELS(char_width), -1)
+        self.textview.set_size_request(80 * Pango.PIXELS(char_width), -1)
 
         self.widget.show_all()
 
@@ -85,7 +85,7 @@ class CommitDialog(gnomeglade.Component):
         self.previousentry.set_active(-1)
         self.textview.grab_focus()
         response = self.widget.run()
-        if response == gtk.RESPONSE_OK:
+        if response == Gtk.ResponseType.OK:
             buf = self.textview.get_buffer()
             msg = buf.get_text(*buf.get_bounds(), include_hidden_chars=False)
             # This is a dependent option because of the margin column
@@ -119,6 +119,6 @@ class PushDialog(gnomeglade.Component):
         # In git, this is probably the parsed output of push --dry-run.
 
         response = self.widget.run()
-        if response == gtk.RESPONSE_OK:
+        if response == Gtk.ResponseType.OK:
             self.parent.vc.push(self.parent._command)
         self.widget.destroy()
diff --git a/meld/ui/wraplabel.py b/meld/ui/wraplabel.py
index 52b850e..29dbdd0 100644
--- a/meld/ui/wraplabel.py
+++ b/meld/ui/wraplabel.py
@@ -20,19 +20,19 @@
 
 # Python translation from wrapLabel.{cc|h} by Gian Mario Tagliaretti
 
-import gtk
-import pango
+from gi.repository import Gtk
+from gi.repository import Pango
 
 
-class WrapLabel(gtk.Label):
+class WrapLabel(Gtk.Label):
     __gtype_name__ = 'WrapLabel'
 
     def __init__(self, text=None):
-        gtk.Label.__init__(self)
+        Gtk.Label.__init__(self)
 
         self.__wrap_width = 0
         self.layout = self.get_layout()
-        self.layout.set_wrap(pango.WRAP_WORD_CHAR)
+        self.layout.set_wrap(Pango.WrapMode.WORD_CHAR)
 
         if text is not None:
             self.set_text(text)
@@ -46,22 +46,22 @@ class WrapLabel(gtk.Label):
         requisition.height = height
 
     def do_size_allocate(self, allocation):
-        gtk.Label.do_size_allocate(self, allocation)
+        Gtk.Label.do_size_allocate(self, allocation)
         self.__set_wrap_width(allocation.width)
 
     def set_text(self, text):
-        gtk.Label.set_text(self, text)
+        Gtk.Label.set_text(self, text)
         self.__set_wrap_width(self.__wrap_width)
 
     def set_markup(self, text):
-        gtk.Label.set_markup(self, text)
+        Gtk.Label.set_markup(self, text)
         self.__set_wrap_width(self.__wrap_width)
 
     def __set_wrap_width(self, width):
         if width == 0:
             return
         layout = self.get_layout()
-        layout.set_width(width * pango.SCALE)
+        layout.set_width(width * Pango.SCALE)
         if self.__wrap_width != width:
             self.__wrap_width = width
             self.queue_resize()
diff --git a/meld/undo.py b/meld/undo.py
index fcc2a7f..11cb119 100644
--- a/meld/undo.py
+++ b/meld/undo.py
@@ -33,7 +33,7 @@ def on_undo_button_pressed():
     s.undo()
 """
 
-import gobject
+from gi.repository import GObject
 
 class GroupAction(object):
     """A group action combines several actions into one logical action.
@@ -50,20 +50,20 @@ class GroupAction(object):
         while self.seq.can_redo():
             self.seq.redo()
 
-class UndoSequence(gobject.GObject):
+class UndoSequence(GObject.GObject):
     """A manager class for operations which can be undone/redone.
     """
 
     __gsignals__ = {
-        'can-undo': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
-        'can-redo': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
-        'checkpointed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_OBJECT, 
gobject.TYPE_BOOLEAN,)),
+        'can-undo': (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_BOOLEAN,)),
+        'can-redo': (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_BOOLEAN,)),
+        'checkpointed': (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_OBJECT, GObject.TYPE_BOOLEAN,)),
     }
 
     def __init__(self):
         """Create an empty UndoSequence.
         """
-        gobject.GObject.__init__(self)
+        GObject.GObject.__init__(self)
         self.actions = []
         self.next_redo = 0
         self.checkpoints = {}
diff --git a/meld/util/prefs.py b/meld/util/prefs.py
index 8cd616e..75aadd7 100644
--- a/meld/util/prefs.py
+++ b/meld/util/prefs.py
@@ -65,7 +65,7 @@ LIST = "list"
 
 
 class GConfPreferences(object):
-    """Persistent preferences object that handles preferences via gconf.
+    """Persistent preferences object that handles preferences via GConf.
 
     Example:
     import prefs
@@ -87,12 +87,12 @@ class GConfPreferences(object):
         rootkey : the root gconf key where the values will be stored
         initial : a dictionary of string to Value objects.
         """
-        self.__dict__["_gconf"] = gconf.client_get_default()
+        self.__dict__["_gconf"] = GConf.Client.get_default()
         self.__dict__["_listeners"] = []
         self.__dict__["_rootkey"] = rootkey
         self.__dict__["_prefs"] = initial
-        self._gconf.add_dir(rootkey, gconf.CLIENT_PRELOAD_NONE)
-        self._gconf.notify_add(rootkey, self._on_preference_changed)
+        self._gconf.add_dir(rootkey, GConf.ClientPreloadType.PRELOAD_NONE)
+        self._gconf.notify_add(rootkey, self._on_preference_changed, None)
         for key, value in self._prefs.items():
             gval = self._gconf.get_without_default("%s/%s" % (rootkey, key))
             if gval is not None:
@@ -116,7 +116,7 @@ class GConfPreferences(object):
             setfunc = getattr(self._gconf, "set_%s" % value.type)
             if value.type == LIST:
                 # We only use/support str lists at the moment
-                setfunc("%s/%s" % (self._rootkey, attr), gconf.VALUE_STRING,
+                setfunc("%s/%s" % (self._rootkey, attr), GConf.ValueType.STRING,
                         val)
             else:
                 setfunc("%s/%s" % (self._rootkey, attr), val)
@@ -276,9 +276,9 @@ skip_gconf = sys.platform == 'win32' or force_ini
 try:
     if skip_gconf:
         raise ImportError
-    import gconf
+    from gi.repository import GConf
     # Verify that gconf is actually working (bgo#666136)
-    client = gconf.client_get_default()
+    client = GConf.Client.get_default()
     key = '/apps/meld/gconf-test'
     client.set_int(key, os.getpid())
     client.unset(key)
diff --git a/meld/util/sourceviewer.py b/meld/util/sourceviewer.py
index 6f30a90..60b9861 100644
--- a/meld/util/sourceviewer.py
+++ b/meld/util/sourceviewer.py
@@ -19,8 +19,8 @@
 '''Abstraction from sourceview version API incompatibilities
 '''
 
-import gio
-import gtk
+from gi.repository import Gio
+from gi.repository import Gtk
 
 
 class _srcviewer(object):
@@ -77,12 +77,12 @@ class _gtksourceview2(_srcviewer):
         raise NotImplementedError
 
     def overrides(self):
-        self.GtkTextView = self.gsv.View
-        self.GtkTextBuffer = self.gsv.Buffer
-        self.spaces_flag = self.gsv.DRAW_SPACES_ALL
+        self.GtkTextView = self.GtkSource.View
+        self.GtkTextBuffer = self.GtkSource.Buffer
+        self.spaces_flag = self.GtkSource.DrawSpacesFlags.ALL
 
     def GtkLanguageManager(self):
-        return self.gsv.LanguageManager()
+        return self.GtkSource.LanguageManager()
 
     def set_tab_width(self, tab, tab_size):
         return tab.set_tab_width(tab_size)
@@ -91,17 +91,17 @@ class _gtksourceview2(_srcviewer):
         return buf.set_highlight_syntax(enabled)
 
     def get_language_from_file(self, filename):
-        f = gio.File(filename)
+        f = Gio.File(filename)
         try:
-            info = f.query_info(gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)
-        except gio.Error:
+            info = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)
+        except Gio.Error:
             return None
         content_type = info.get_content_type()
         return self.get_language_manager().guess_language(filename,
                                                           content_type)
 
     def get_language_from_mime_type(self, mime_type):
-        content_type = gio.content_type_from_mime_type(mime_type)
+        content_type = Gio.content_type_from_mime_type(mime_type)
         return self.get_language_manager().guess_language(None, content_type)
 
     def set_language(self, buf, lang):
@@ -111,12 +111,12 @@ class _gtksourceview2(_srcviewer):
 class gtksourceview24(_gtksourceview2):
 
     def version_check(self):
-        if self.gsv.pygtksourceview2_version[1] < 4:
+        if self.GtkSource.pygtksourceview2_version[1] < 4:
             raise ImportError
 
     def overrides(self):
-        _gtksourceview2.overrides(self)
-        viewClass = self.gsv.View
+        _GtkSource.overrides(self)
+        viewClass = self.GtkSource.View
 
         class SourceView(viewClass):
 
@@ -125,10 +125,10 @@ class gtksourceview24(_gtksourceview2):
             }
 
             def do_key_press_event(self, event):
-                if event.keyval in (gtk.keysyms.KP_Up, gtk.keysyms.KP_Down,
-                                    gtk.keysyms.Up, gtk.keysyms.Down) and \
-                   (event.state & gtk.gdk.MOD1_MASK) != 0 and \
-                   (event.state & gtk.gdk.SHIFT_MASK) == 0:
+                if event.keyval in (Gdk.KEY_KP_Up, Gdk.KEY_KP_Down,
+                                    Gdk.KEY_Up, Gdk.KEY_Down) and \
+                   (event.get_state() & Gdk.ModifierType.MOD1_MASK) != 0 and \
+                   (event.get_state() & Gdk.ModifierType.SHIFT_MASK) == 0:
                     return True
                 return viewClass.do_key_press_event(self, event)
 
@@ -138,19 +138,19 @@ class gtksourceview24(_gtksourceview2):
 class gtksourceview210(_gtksourceview2):
 
     def version_check(self):
-        if self.gsv.pygtksourceview2_version[1] < 10:
+        if self.GtkSource.pygtksourceview2_version[1] < 10:
             raise ImportError
 
     def overrides(self):
-        _gtksourceview2.overrides(self)
-        gtk.binding_entry_remove(self.GtkTextView, gtk.keysyms.Up,
-                                 gtk.gdk.MOD1_MASK)
-        gtk.binding_entry_remove(self.GtkTextView, gtk.keysyms.KP_Up,
-                                 gtk.gdk.MOD1_MASK)
-        gtk.binding_entry_remove(self.GtkTextView, gtk.keysyms.Down,
-                                 gtk.gdk.MOD1_MASK)
-        gtk.binding_entry_remove(self.GtkTextView, gtk.keysyms.KP_Down,
-                                 gtk.gdk.MOD1_MASK)
+        _GtkSource.overrides(self)
+        Gtk.binding_entry_remove(self.GtkTextView, Gdk.KEY_Up,
+                                 Gdk.ModifierType.MOD1_MASK)
+        Gtk.binding_entry_remove(self.GtkTextView, Gdk.KEY_KP_Up,
+                                 Gdk.ModifierType.MOD1_MASK)
+        Gtk.binding_entry_remove(self.GtkTextView, Gdk.KEY_Down,
+                                 Gdk.ModifierType.MOD1_MASK)
+        Gtk.binding_entry_remove(self.GtkTextView, Gdk.KEY_KP_Down,
+                                 Gdk.ModifierType.MOD1_MASK)
 
 
 class nullsourceview(_srcviewer):
@@ -164,31 +164,31 @@ class nullsourceview(_srcviewer):
     get_language_from_mime_type = lambda *args: None
 
     def overrides(self):
-        import gobject
-        import gtk
+        from gi.repository import GObject
+        from gi.repository import Gtk
 
-        class NullTextView(gtk.TextView):
+        class NullTextView(Gtk.TextView):
             set_tab_width = lambda *args: None
             set_show_line_numbers = lambda *args: None
             set_insert_spaces_instead_of_tabs = lambda *args: None
             set_draw_spaces = lambda *args: None
             set_right_margin_position = lambda *args: None
             set_show_right_margin = lambda *args: None
-        gobject.type_register(NullTextView)
+        GObject.type_register(NullTextView)
 
         self.GtkTextView = NullTextView
-        self.GtkTextBuffer = gtk.TextBuffer
+        self.GtkTextBuffer = Gtk.TextBuffer
 
     def version_check(self):
         pass
 
 
 def _get_srcviewer():
-    for srcv in (gtksourceview210, gtksourceview24):
-        try:
-            return srcv()
-        except ImportError:
-            pass
+    # for srcv in (gtksourceview210, gtksourceview24):
+    #     try:
+    #         return srcv()
+    #     except ImportError:
+    #         pass
     return nullsourceview()
 
 
diff --git a/meld/vcview.py b/meld/vcview.py
index db7a58d..83c9855 100644
--- a/meld/vcview.py
+++ b/meld/vcview.py
@@ -26,9 +26,10 @@ import stat
 import sys
 from gettext import gettext as _
 
-import gio
-import gtk
-import pango
+from gi.repository import GLib
+from gi.repository import Gio
+from gi.repository import Gtk
+from gi.repository import Pango
 
 from . import melddoc
 from . import misc
@@ -85,7 +86,7 @@ class ConsoleStream(object):
         self.textview = textview
         buf = textview.get_buffer()
         self.command_tag = buf.create_tag("command")
-        self.command_tag.props.weight = pango.WEIGHT_BOLD
+        self.command_tag.props.weight = Pango.Weight.BOLD
         self.output_tag = buf.create_tag("output")
         self.error_tag = buf.create_tag("error")
         # FIXME: Need to add this to the gtkrc?
@@ -163,37 +164,38 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
         self.widget.connect("style-set", self.model.on_style_set)
         self.treeview.set_model(self.model)
         selection = self.treeview.get_selection()
-        selection.set_mode(gtk.SELECTION_MULTIPLE)
+        selection.set_mode(Gtk.SelectionMode.MULTIPLE)
         selection.connect("changed", self.on_treeview_selection_changed)
         self.treeview.set_headers_visible(1)
-        self.treeview.set_search_equal_func(self.model.treeview_search_cb)
+        self.treeview.set_search_equal_func(
+            self.model.treeview_search_cb, None)
         self.current_path, self.prev_path, self.next_path = None, None, None
 
         self.column_name_map = {}
-        column = gtk.TreeViewColumn(_("Name"))
+        col_index = self.model.column_index
+        column = Gtk.TreeViewColumn(_("Name"))
         column.set_resizable(True)
         renicon = emblemcellrenderer.EmblemCellRenderer()
-        rentext = gtk.CellRendererText()
-        column.pack_start(renicon, expand=0)
-        column.pack_start(rentext, expand=1)
-        col_index = self.model.column_index
+        column.pack_start(renicon, False)
         column.set_attributes(renicon,
                               icon_name=col_index(tree.COL_ICON, 0),
                               icon_tint=col_index(tree.COL_TINT, 0))
+        rentext = Gtk.CellRendererText()
+        column.pack_start(rentext, True)
         column.set_attributes(rentext,
-                    text=col_index(tree.COL_TEXT, 0),
-                    foreground_gdk=col_index(tree.COL_FG, 0),
-                    style=col_index(tree.COL_STYLE, 0),
-                    weight=col_index(tree.COL_WEIGHT, 0),
-                    strikethrough=col_index(tree.COL_STRIKE, 0))
+                              text=col_index(tree.COL_TEXT, 0),
+                              foreground_gdk=col_index(tree.COL_FG, 0),
+                              style=col_index(tree.COL_STYLE, 0),
+                              weight=col_index(tree.COL_WEIGHT, 0),
+                              strikethrough=col_index(tree.COL_STRIKE, 0))
         column_index = self.treeview.append_column(column) - 1
         self.column_name_map[vc.DATA_NAME] = column_index
 
         def addCol(name, num, data_name=None):
-            column = gtk.TreeViewColumn(name)
+            column = Gtk.TreeViewColumn(name)
             column.set_resizable(True)
-            rentext = gtk.CellRendererText()
-            column.pack_start(rentext, expand=0)
+            rentext = Gtk.CellRendererText()
+            column.pack_start(rentext, True)
             column.set_attributes(rentext,
                                   markup=self.model.column_index(num, 0))
             column_index = self.treeview.append_column(column) - 1
@@ -221,7 +223,7 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
         self.vc = None
         self.valid_vc_actions = tuple()
 
-        cell = gtk.CellRendererText()
+        cell = Gtk.CellRendererText()
         self.combobox_vcs.pack_start(cell, False)
         self.combobox_vcs.add_attribute(cell, 'text', 0)
         self.combobox_vcs.add_attribute(cell, 'sensitive', 2)
@@ -339,7 +341,7 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
         # If the user is just diffing a file (ie not a directory), there's no
         # need to scan the rest of the repository
         if os.path.isdir(self.vc.location):
-            root = self.model.get_iter_root()
+            root = self.model.get_iter_first()
 
             try:
                 col = self.model.column_index(COL_OPTIONS, 0)
@@ -410,7 +412,7 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
                     todo.append((self.model.get_path(child), e.path))
 
             if flattened:
-                self.treeview.expand_row((0,), 0)
+                self.treeview.expand_row(Gtk.TreePath((0,)), False)
             else:
                 if not entries:
                     self.model.add_empty(it, _("(Empty)"))
@@ -425,7 +427,7 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
 
     def on_delete_event(self, appquit=0):
         self.scheduler.remove_all_tasks()
-        return gtk.RESPONSE_OK
+        return Gtk.ResponseType.OK
 
     def on_row_activated(self, treeview, path, tvc):
         it = self.model.get_iter(path)
@@ -433,7 +435,7 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
             if self.treeview.row_expanded(path):
                 self.treeview.collapse_row(path)
             else:
-                self.treeview.expand_row(path, 0)
+                self.treeview.expand_row(path, False)
         else:
             path = self.model.value_path(it, 0)
             self.run_diff(path)
@@ -476,8 +478,8 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
         self.emit("create-diff", diffs, kwargs)
 
     def on_treeview_popup_menu(self, treeview):
-        time = gtk.get_current_event_time()
-        self.popup_menu.popup(None, None, None, 0, time)
+        time = Gtk.get_current_event_time()
+        self.popup_menu.popup(None, None, None, None, 0, time)
         return True
 
     def on_button_press_event(self, treeview, event):
@@ -493,7 +495,8 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
                 selection.select_path(path[0])
                 treeview.set_cursor(path[0])
 
-            self.popup_menu.popup(None, None, None, event.button, event.time)
+            self.popup_menu.popup(None, None, None, None, event.button,
+                                  event.time)
             return True
         return False
 
@@ -631,20 +634,20 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
         selected = self._get_selected_files()
         if any(os.path.isdir(p) for p in selected):
             # TODO: Improve and reuse this dialog for the non-VC delete action
-            dialog = gtk.MessageDialog(
+            dialog = Gtk.MessageDialog(
                 parent=self.widget.get_toplevel(),
-                flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
-                type=gtk.MESSAGE_WARNING,
+                flags=Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                type=Gtk.MessageType.WARNING,
                 message_format=_("Remove folder and all its files?"))
             dialog.format_secondary_text(
                 _("This will remove all selected files and folders, and all "
                   "files within any selected folders, from version control."))
 
-            dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
-            dialog.add_button(_("_Remove"), gtk.RESPONSE_OK)
+            dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
+            dialog.add_button(_("_Remove"), Gtk.ResponseType.OK)
             response = dialog.run()
             dialog.destroy()
-            if response != gtk.RESPONSE_OK:
+            if response != Gtk.ResponseType.OK:
                 return
 
         try:
@@ -668,9 +671,9 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
         files = self._get_selected_files()
         for name in files:
             try:
-                gfile = gio.File(name)
-                gfile.trash()
-            except gio.Error as e:
+                gfile = Gio.File.new_for_path(name)
+                gfile.trash(None)
+            except GLib.GError as e:
                 misc.error_dialog(_("Error removing %s") % name, str(e))
         workdir = _commonprefix(files)
         self.refresh_partial(workdir)
@@ -684,7 +687,7 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
         self._open_files(self._get_selected_files())
 
     def refresh(self):
-        self.set_location(self.model.value_path(self.model.get_iter_root(), 0))
+        self.set_location(self.model.value_path(self.model.get_iter_first(), 0))
 
     def refresh_partial(self, where):
         if not self.actiongroup.get_action("VcFlatten").get_active():
@@ -723,12 +726,12 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
             files = self.vc.lookup_files([], [(os.path.basename(path), path)])[1]
             for e in files:
                 if e.path == path:
-                    prefixlen = 1 + len( self.model.value_path( self.model.get_iter_root(), 0 ) )
+                    prefixlen = 1 + len( self.model.value_path( self.model.get_iter_first(), 0 ) )
                     self._update_item_state( it, e, e.parent[prefixlen:])
                     return
 
     def find_iter_by_name(self, name):
-        it = self.model.get_iter_root()
+        it = self.model.get_iter_first()
         path = self.model.value_path(it, 0)
         while it:
             if name == path:
@@ -767,10 +770,10 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
     def on_consoleview_populate_popup(self, textview, menu):
         buf = textview.get_buffer()
         clear_cb = lambda *args: buf.delete(*buf.get_bounds())
-        clear_action = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
+        clear_action = Gtk.ImageMenuItem(Gtk.STOCK_CLEAR)
         clear_action.connect("activate", clear_cb)
         menu.insert(clear_action, 0)
-        menu.insert(gtk.SeparatorMenuItem(), 1)
+        menu.insert(Gtk.SeparatorMenuItem(), 1)
         menu.show_all()
 
     def on_treeview_cursor_changed(self, *args):
@@ -813,7 +816,7 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
         self.current_path = cursor_path
 
     def next_diff(self, direction):
-        if direction == gtk.gdk.SCROLL_UP:
+        if direction == Gdk.ScrollDirection.UP:
             path = self.prev_path
         else:
             path = self.next_path


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]