[meld] Notify the user when compared files are identical



commit f57babe725f92a05b47ce7ceaa512e9f7b8ba43d
Author: Kai Willadsen <kai willadsen gmail com>
Date:   Mon Jun 29 11:26:07 2009 +1000

    Notify the user when compared files are identical
    
    Using the MsgArea python port taken from hotssh, we display a message when
    there are no differences found between the compared files.
    
    Fixes part of bgo#458372.

 diffutil.py           |    3 +
 filediff.py           |   22 +++++-
 glade2/filediff.glade |   84 ++++++++++++++---
 msgarea.py            |  237 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 330 insertions(+), 16 deletions(-)
---
diff --git a/diffutil.py b/diffutil.py
index 99077a6..8f9c63b 100644
--- a/diffutil.py
+++ b/diffutil.py
@@ -158,6 +158,9 @@ class Differ(object):
                 elif cs[1]:
                     yield cs[1] + (2,)
 
+    def sequences_identical(self):
+        return self.diffs == [[], []]
+
     def _merge_blocks(self, using):
         LO, HI = 1,2
         lowc  =  min(using[0][ 0][LO], using[1][ 0][LO])
diff --git a/filediff.py b/filediff.py
index f5617bb..1e62eea 100644
--- a/filediff.py
+++ b/filediff.py
@@ -30,6 +30,7 @@ import diffutil
 import gnomeglade
 import misc
 import melddoc
+import msgarea
 import paths
 import cairo
 
@@ -64,7 +65,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         """
         melddoc.MeldDoc.__init__(self, prefs)
         gnomeglade.Component.__init__(self, paths.share_dir("glade2/filediff.glade"), "filediff", srcviewer.override)
-        self.map_widgets_into_lists( ["textview", "fileentry", "diffmap", "scrolledwindow", "linkmap", "statusimage"] )
+        self.map_widgets_into_lists( ["textview", "fileentry", "diffmap", "scrolledwindow", "linkmap", "statusimage", "msgarea_mgr"] )
         self._update_regexes()
         self.warned_bad_comparison = False
         if srcviewer:
@@ -593,6 +594,19 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         step = self.linediffer.set_sequences_iter(*lines)
         while step.next() == None:
             yield 1
+
+        if self.num_panes > 1 and self.linediffer.sequences_identical():
+            for index, mgr in enumerate(self.msgarea_mgr):
+                msgarea = mgr.new_from_text_and_icon(gtk.STOCK_INFO,
+                                                     _("Files are identical"))
+                button = msgarea.add_stock_button_with_text(_("Hide"),
+                                                            gtk.STOCK_CLOSE,
+                                                            gtk.RESPONSE_CLOSE)
+                if index == 0:
+                    button.props.label = _("Hi_de")
+                msgarea.connect("response", self.on_msgarea_identical_response)
+                msgarea.show_all()
+
         self.scheduler.add_task( lambda: self.next_diff(gdk.SCROLL_DOWN, jump_to_first=True), True )
         self.queue_draw()
         self.scheduler.add_task(self._update_highlighting().next)
@@ -603,6 +617,10 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                     srcviewer.set_highlighting_enabled_from_file(self.textbuffer[i], files[i], self.prefs.use_syntax_highlighting)
         yield 0
 
+    def on_msgarea_identical_response(self, msgarea, respid):
+        for mgr in self.msgarea_mgr:
+            mgr.clear()
+
     def _update_highlighting(self):
         for b in self.textbuffer:
             taglist = ["delete line", "conflict line", "replace line", "inline line"]
@@ -1019,10 +1037,12 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         if n != self.num_panes and n in (1,2,3):
             self.num_panes = n
             toshow =  self.scrolledwindow[:n] + self.fileentry[:n]
+            toshow += self.msgarea_mgr[:n]
             toshow += self.linkmap[:n-1] + self.diffmap[:n]
             map( lambda x: x.show(), toshow )
 
             tohide =  self.statusimage + self.scrolledwindow[n:] + self.fileentry[n:]
+            tohide += self.msgarea_mgr[n:]
             tohide += self.linkmap[n-1:] + self.diffmap[n:]
             map( lambda x: x.hide(), tohide )
 
diff --git a/glade2/filediff.glade b/glade2/filediff.glade
index 05b8f3d..0dbffc5 100644
--- a/glade2/filediff.glade
+++ b/glade2/filediff.glade
@@ -12,7 +12,7 @@
         <child>
           <widget class="GtkTable" id="table">
             <property name="visible">True</property>
-            <property name="n_rows">2</property>
+            <property name="n_rows">3</property>
             <property name="n_columns">7</property>
             <child>
               <placeholder/>
@@ -83,8 +83,8 @@
               <packing>
                 <property name="left_attach">1</property>
                 <property name="right_attach">2</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
               </packing>
             </child>
             <child>
@@ -145,8 +145,8 @@
               <packing>
                 <property name="left_attach">5</property>
                 <property name="right_attach">6</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
               </packing>
             </child>
             <child>
@@ -169,8 +169,8 @@
               <packing>
                 <property name="left_attach">3</property>
                 <property name="right_attach">4</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
               </packing>
             </child>
             <child>
@@ -184,8 +184,8 @@
               <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="top_attach">2</property>
+                <property name="bottom_attach">3</property>
                 <property name="x_options">GTK_FILL</property>
                 <property name="y_options">GTK_FILL</property>
               </packing>
@@ -207,8 +207,8 @@
               <packing>
                 <property name="left_attach">2</property>
                 <property name="right_attach">3</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
                 <property name="x_options">GTK_FILL</property>
                 <property name="y_options">GTK_FILL</property>
               </packing>
@@ -222,8 +222,8 @@
                 <signal name="button_press_event" handler="on_diffmap_button_press_event"/>
               </widget>
               <packing>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
                 <property name="x_options">GTK_FILL</property>
               </packing>
             </child>
@@ -244,12 +244,66 @@
               <packing>
                 <property name="left_attach">4</property>
                 <property name="right_attach">5</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
                 <property name="x_options">GTK_FILL</property>
                 <property name="y_options">GTK_FILL</property>
               </packing>
             </child>
+            <child>
+              <widget class="Custom" id="msgarea_mgr0">
+                <property name="visible">True</property>
+                <property name="creation_function">msgarea.msgarea_mgr_create</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="y_options">GTK_SHRINK | GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="Custom" id="msgarea_mgr1">
+                <property name="visible">True</property>
+                <property name="creation_function">msgarea.msgarea_mgr_create</property>
+              </widget>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="y_options">GTK_SHRINK | GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="Custom" id="msgarea_mgr2">
+                <property name="visible">True</property>
+                <property name="creation_function">msgarea.msgarea_mgr_create</property>
+              </widget>
+              <packing>
+                <property name="left_attach">5</property>
+                <property name="right_attach">6</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="y_options">GTK_SHRINK | GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
           </widget>
         </child>
         <child>
diff --git a/msgarea.py b/msgarea.py
new file mode 100644
index 0000000..14dcd36
--- /dev/null
+++ b/msgarea.py
@@ -0,0 +1,237 @@
+# This file is part of the Hotwire Shell user interface.
+#
+# Copyright (C) 2007,2008 Colin Walters <walters verbum org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import os, sys, re, logging, string
+
+import gtk, gobject, pango
+
+_logger = logging.getLogger("hotwire.ui.MsgArea")
+
+# This file is a Python translation of gedit/gedit/gedit-message-area.c
+
+class MsgArea(gtk.HBox):
+    __gsignals__ = {
+        "response" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_INT,)),
+        "close" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [])
+    }
+
+    def __init__(self, buttons, **kwargs):
+        super(MsgArea, self).__init__(**kwargs)
+
+        self.__contents = None
+        self.__changing_style = False
+
+        self.__main_hbox = gtk.HBox(False, 16) # FIXME: use style properties
+        self.__main_hbox.show()
+        self.__main_hbox.set_border_width(8) # FIXME: use style properties
+
+        self.__action_area = gtk.HBox(True, 4); # FIXME: use style properties
+        self.__action_area.show()
+        self.__main_hbox.pack_end (self.__action_area, False, True, 0)
+
+        self.pack_start(self.__main_hbox, True, True, 0)
+
+        self.set_app_paintable(True)
+
+        self.connect("expose-event", self.__paint)
+
+        # Note that we connect to style-set on one of the internal
+        # widgets, not on the message area itself, since gtk does
+        # not deliver any further style-set signals for a widget on
+        # which the style has been forced with gtk_widget_set_style()
+        self.__main_hbox.connect("style-set", self.__on_style_set)
+
+        self.add_buttons(buttons)
+
+    def __get_response_data(self, w, create):
+        d = w.get_data('hotwire-msg-area-data')
+        if (d is None) and create:
+            d = {'respid': None}
+            w.set_data('hotwire-msg-area-data', d)
+        return d
+
+    def __find_button(self, respid):
+        children = self.__actionarea.get_children()
+        for child in children:
+            rd = self.__get_response_data(child, False)
+            if rd is not None and rd['respid'] == respid:
+                return child
+
+    def __close(self):
+        cancel = self.__find_button(gtk.RESPONSE_CANCEL)
+        if cancel is None:
+            return
+        self.response(gtk.RESPONSE_CANCEL)
+
+    def __paint(self, w, event):
+        gtk.Style.paint_flat_box(w.style,
+                                 w.window,
+                                 gtk.STATE_NORMAL,
+                                 gtk.SHADOW_OUT,
+                                 None,
+                                 w,
+                                 "tooltip",
+                                 w.allocation.x + 1,
+                                 w.allocation.y + 1,
+                                 w.allocation.width - 2,
+                                 w.allocation.height - 2)
+
+        return False
+
+    def __on_style_set(self, w, style):
+        if self.__changing_style:
+            return
+        # This is a hack needed to use the tooltip background color
+        window = gtk.Window(gtk.WINDOW_POPUP);
+        window.set_name("gtk-tooltip")
+        window.ensure_style()
+        style = window.get_style()
+
+        self.__changing_style = True
+        self.set_style(style)
+        self.__changing_style = False
+
+        window.destroy()
+
+        self.queue_draw()
+
+    def __get_response_for_widget(self, w):
+        rd = self.__get_response_data(w, False)
+        if rd is None:
+            return gtk.RESPONSE_NONE
+        return rd['respid']
+
+    def __on_action_widget_activated(self, w):
+        response_id = self.__get_response_for_widget(w)
+        self.response(response_id)
+
+    def add_action_widget(self, child, respid):
+        rd = self.__get_response_data(child, True)
+        rd['respid'] = respid
+        if not isinstance(child, gtk.Button):
+            raise ValueError("Can only pack buttons as action widgets")
+        child.connect('clicked', self.__on_action_widget_activated)
+        if respid != gtk.RESPONSE_HELP:
+            self.__action_area.pack_start(child, False, False, 0)
+        else:
+            self.__action_area.pack_end(child, False, False, 0)
+
+    def set_contents(self, contents):
+        self.__contents = contents
+        self.__main_hbox.pack_start(contents, True, True, 0)
+
+
+    def add_button(self, btext, respid):
+        button = gtk.Button(stock=btext)
+        button.set_focus_on_click(False)
+        button.set_flags(gtk.CAN_DEFAULT)
+        button.show()
+        self.add_action_widget(button, respid)
+        return button
+
+    def add_buttons(self, args):
+        _logger.debug("init buttons: %r", args)
+        for (btext, respid) in args:
+            self.add_button(btext, respid)
+
+    def set_response_sensitive(self, respid, setting):
+        for child in self.__action_area.get_children():
+            rd = self.__get_response_data(child, False)
+            if rd is not None and rd['respid'] == respid:
+                child.set_sensitive(setting)
+                break
+
+    def set_default_response(self, respid):
+        for child in self.__action_area.get_children():
+            rd = self.__get_response_data(child, False)
+            if rd is not None and rd['respid'] == respid:
+                child.grab_default()
+                break
+
+    def response(self, respid):
+        self.emit('response', respid)
+
+    def add_stock_button_with_text(self, text, stockid, respid):
+        b = gtk.Button(label=text)
+        b.set_focus_on_click(False)
+        img = gtk.Image()
+        img.set_from_stock(stockid, gtk.ICON_SIZE_BUTTON)
+        b.set_image(img)
+        b.show_all()
+        self.add_action_widget(b, respid)
+        return b
+
+    def set_text_and_icon(self, stockid, primary_text, secondary_text=None):
+        hbox_content = gtk.HBox(False, 8)
+        hbox_content.show()
+
+        image = gtk.Image()
+        image.set_from_stock(stockid, gtk.ICON_SIZE_BUTTON)
+        image.show()
+        hbox_content.pack_start(image, False, False, 0)
+        image.set_alignment(0.5, 0.5)
+
+        vbox = gtk.VBox(False, 6)
+        vbox.show()
+        hbox_content.pack_start (vbox, True, True, 0)
+
+        primary_markup = "<b>%s</b>" % (primary_text,)
+        primary_label = gtk.Label(primary_markup)
+        primary_label.show()
+        vbox.pack_start(primary_label, True, True, 0)
+        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_selectable(True)
+
+        if secondary_text:
+            secondary_markup = "<small>%s</small>" % (secondary_text,)
+            secondary_label = gtk.Label(secondary_markup)
+            secondary_label.show()
+            vbox.pack_start(secondary_label, True, True, 0)
+            secondary_label.set_flags(gtk.CAN_FOCUS)
+            secondary_label.set_use_markup(True)
+            secondary_label.set_line_wrap(True)
+            secondary_label.set_selectable(True)
+            secondary_label.set_alignment(0, 0.5)
+
+        self.set_contents(hbox_content)
+
+class MsgAreaController(gtk.HBox):
+    def __init__(self):
+        super(MsgAreaController, self).__init__()
+
+        self.__msgarea = None
+
+    def clear(self):
+        if self.__msgarea is not None:
+            self.remove(self.__msgarea)
+            self.__msgarea.destroy()
+            self.__msgarea = None
+
+    def new_from_text_and_icon(self, stockid, primary, secondary=None, buttons=[]):
+        self.clear()
+        msgarea = self.__msgarea = MsgArea(buttons)
+        msgarea.set_text_and_icon(stockid, primary, secondary)
+        self.pack_start(msgarea, expand=True)
+        return msgarea
+
+def msgarea_mgr_create(str1, str2, int1, int2):
+    return MsgAreaController()
+



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