~ubuntu-branches/ubuntu/trusty/gedit-plugins/trusty-proposed

« back to all changes in this revision

Viewing changes to plugins/git/git/windowactivatable.py

  • Committer: Package Import Robot
  • Author(s): Sebastien Bacher
  • Date: 2013-12-12 16:35:11 UTC
  • mto: (7.3.7 sid)
  • mto: This revision was merged to the branch mainline in revision 70.
  • Revision ID: package-import@ubuntu.com-20131212163511-d7275l3drwdrw1qq
Tags: upstream-3.10.1
ImportĀ upstreamĀ versionĀ 3.10.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
#  Copyright (C) 2013 - Garrett Regier
 
4
#
 
5
#  This program is free software; you can redistribute it and/or modify
 
6
#  it under the terms of the GNU General Public License as published by
 
7
#  the Free Software Foundation; either version 2 of the License, or
 
8
#  (at your option) any later version.
 
9
#
 
10
#  This program is distributed in the hope that it will be useful,
 
11
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
#  GNU General Public License for more details.
 
14
#
 
15
#  You should have received a copy of the GNU General Public License
 
16
#  along with this program; if not, write to the Free Software
 
17
#  Foundation, Inc., 59 Temple Place, Suite 330,
 
18
#  Boston, MA 02111-1307, USA.
 
19
 
 
20
from gi.repository import GLib, GObject, Gio, Gtk, Gedit, Ggit
 
21
 
 
22
import weakref
 
23
 
 
24
 
 
25
class GitWindowActivatable(GObject.Object, Gedit.WindowActivatable):
 
26
    window = GObject.property(type=Gedit.Window)
 
27
 
 
28
    windows = weakref.WeakValueDictionary()
 
29
 
 
30
    def __init__(self):
 
31
        super().__init__()
 
32
 
 
33
        Ggit.init()
 
34
 
 
35
        self.view_activatables = weakref.WeakSet()
 
36
 
 
37
        self.files = {}
 
38
        self.monitors = {}
 
39
 
 
40
        self.repo = None
 
41
        self.tree = None
 
42
 
 
43
    @classmethod
 
44
    def register_view_activatable(cls, view_activatable):
 
45
        window = view_activatable.view.get_toplevel()
 
46
 
 
47
        if window not in cls.windows:
 
48
            return None
 
49
 
 
50
        window_activatable = cls.windows[window]
 
51
 
 
52
        window_activatable.view_activatables.add(view_activatable)
 
53
        view_activatable.connect('notify::status',
 
54
                                 window_activatable.notify_status)
 
55
 
 
56
        return window_activatable
 
57
 
 
58
    def do_activate(self):
 
59
        # self.window is not set until now
 
60
        self.windows[self.window] = self
 
61
 
 
62
        self.bus = self.window.get_message_bus()
 
63
 
 
64
        self.files = {}
 
65
        self.file_names = {}
 
66
        self.monitors = {}
 
67
 
 
68
        self.window_signals = [
 
69
            self.window.connect('tab-removed', self.tab_removed),
 
70
            self.window.connect('focus-in-event', self.focus_in_event)
 
71
        ]
 
72
 
 
73
        self.bus_signals = [
 
74
            self.bus.connect('/plugins/filebrowser', 'root_changed',
 
75
                             self.root_changed, None),
 
76
            self.bus.connect('/plugins/filebrowser', 'inserted',
 
77
                             self.inserted, None),
 
78
            self.bus.connect('/plugins/filebrowser', 'deleted',
 
79
                             self.deleted, None)
 
80
        ]
 
81
 
 
82
        self.refresh();
 
83
 
 
84
    def do_deactivate(self):
 
85
        self.clear_monitors()
 
86
 
 
87
        for sid in self.window_signals:
 
88
            self.window.disconnect(sid)
 
89
 
 
90
        for sid in self.bus_signals:
 
91
            self.bus.disconnect(sid)
 
92
 
 
93
        self.files = {}
 
94
        self.file_names = {}
 
95
        self.repo = None
 
96
        self.tree = None
 
97
        self.window_signals = []
 
98
        self.bus_signals = []
 
99
 
 
100
        self.refresh();
 
101
 
 
102
    def refresh(self):
 
103
        if self.bus.is_registered('/plugins/filebrowser', 'refresh'):
 
104
            self.bus.send('/plugins/filebrowser', 'refresh')
 
105
 
 
106
    def notify_status(self, view_activatable, psepc):
 
107
        location = view_activatable.view.get_buffer().get_location()
 
108
 
 
109
        if location is not None:
 
110
            self.update_location(location)
 
111
 
 
112
    def tab_removed(self, window, tab):
 
113
        view = tab.get_view()
 
114
        location = view.get_buffer().get_location()
 
115
 
 
116
        if location is None:
 
117
            return
 
118
 
 
119
        # Need to remove the view activatable otherwise update_location()
 
120
        # will might use the view's status and not the file's actual status
 
121
        for view_activatable in self.view_activatables:
 
122
            if view_activatable.view == view:
 
123
                self.view_activatables.remove(view_activatable)
 
124
                break
 
125
 
 
126
        self.update_location(location)
 
127
 
 
128
    def focus_in_event(self, window, event):
 
129
        for view_activatable in self.view_activatables:
 
130
            view_activatable.update()
 
131
 
 
132
        for uri in self.files:
 
133
            self.update_location(Gio.File.new_for_uri(uri))
 
134
 
 
135
    def root_changed(self, bus, msg, data=None):
 
136
        self.clear_monitors()
 
137
 
 
138
        try:
 
139
            repo_file = Ggit.Repository.discover(msg.location)
 
140
            self.repo = Ggit.Repository.open(repo_file)
 
141
            head = self.repo.get_head()
 
142
            commit = self.repo.lookup(head.get_target(), Ggit.Commit.__gtype__)
 
143
            self.tree = commit.get_tree()
 
144
 
 
145
        except Exception as e:
 
146
            self.repo = None
 
147
            self.tree = None
 
148
 
 
149
        else:
 
150
            self.monitor_directory(msg.location)
 
151
 
 
152
    def inserted(self, bus, msg, data=None):
 
153
        self.files[msg.location.get_uri()] = msg.id
 
154
        self.file_names[msg.location.get_uri()] = msg.name
 
155
 
 
156
        self.update_location(msg.location)
 
157
 
 
158
        if msg.is_directory:
 
159
            self.monitor_directory(msg.location)
 
160
 
 
161
    def deleted(self, bus, msg, data=None):
 
162
        # File browser's deleted signal is broken
 
163
        return
 
164
 
 
165
        uri = msg.location.get_uri()
 
166
 
 
167
        del self.files[uri]
 
168
        del self.file_names[uri]
 
169
 
 
170
        if uri in self.monitors:
 
171
            self.monitors[uri].cancel()
 
172
            del self.monitors[uri]
 
173
 
 
174
    def update_location(self, location):
 
175
        if self.repo is None:
 
176
            return
 
177
 
 
178
        if location.get_uri() not in self.files:
 
179
            return
 
180
 
 
181
        status = None
 
182
 
 
183
        # First get the status from the open documents
 
184
        for view_activatable in self.view_activatables:
 
185
            doc_location = view_activatable.view.get_buffer().get_location()
 
186
 
 
187
            if doc_location is not None and doc_location.equal(location):
 
188
                status = view_activatable.status
 
189
                break
 
190
 
 
191
        try:
 
192
            git_status = self.repo.file_status(location)
 
193
 
 
194
        except Exception:
 
195
            pass
 
196
 
 
197
        else:
 
198
            # Don't use the view activatable's
 
199
            # status if the file is ignored
 
200
            if status is None or git_status & Ggit.StatusFlags.IGNORED:
 
201
                status = git_status
 
202
 
 
203
        markup = GLib.markup_escape_text(self.file_names[location.get_uri()]) 
 
204
 
 
205
        if status is not None:
 
206
            if status & Ggit.StatusFlags.INDEX_NEW or \
 
207
                    status & Ggit.StatusFlags.WORKING_TREE_NEW or \
 
208
                    status & Ggit.StatusFlags.INDEX_MODIFIED or \
 
209
                    status & Ggit.StatusFlags.WORKING_TREE_MODIFIED:
 
210
                markup = '<span weight="bold">%s</span>' % (markup)
 
211
 
 
212
            elif status & Ggit.StatusFlags.INDEX_DELETED or \
 
213
                    status & Ggit.StatusFlags.WORKING_TREE_DELETED:
 
214
                markup = '<span strikethrough="true">%s</span>' % (markup)
 
215
 
 
216
        self.bus.send('/plugins/filebrowser', 'set_markup',
 
217
                      id=self.files[location.get_uri()], markup=markup)
 
218
 
 
219
    def clear_monitors(self):
 
220
        for uri in self.monitors:
 
221
            self.monitors[uri].cancel()
 
222
 
 
223
        self.monitors = {}
 
224
 
 
225
    def monitor_directory(self, location):
 
226
        monitor = location.monitor(Gio.FileMonitorFlags.NONE, None)
 
227
        self.monitors[location.get_uri()] = monitor
 
228
 
 
229
        monitor.connect('changed', self.monitor_changed)
 
230
 
 
231
    def monitor_changed(self, monitor, file, other_file, event_type):
 
232
        # Only monitor for changes as the file browser will monitor
 
233
        # file created and deleted files and emit inserted and deleted
 
234
        if event_type == Gio.FileMonitorEvent.CHANGED:
 
235
            for f in (file, other_file):
 
236
                if f in self.files:
 
237
                    self.update_location(f)
 
238
 
 
239
# ex:ts=4:et: