1
# -*- coding: utf-8 -*-
3
# Copyright (C) 2013 - Garrett Regier
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.
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.
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.
20
from gi.repository import GLib, GObject, Gio, Gtk, Gedit, Ggit
25
class GitWindowActivatable(GObject.Object, Gedit.WindowActivatable):
26
window = GObject.property(type=Gedit.Window)
28
windows = weakref.WeakValueDictionary()
35
self.view_activatables = weakref.WeakSet()
44
def register_view_activatable(cls, view_activatable):
45
window = view_activatable.view.get_toplevel()
47
if window not in cls.windows:
50
window_activatable = cls.windows[window]
52
window_activatable.view_activatables.add(view_activatable)
53
view_activatable.connect('notify::status',
54
window_activatable.notify_status)
56
return window_activatable
58
def do_activate(self):
59
# self.window is not set until now
60
self.windows[self.window] = self
62
self.bus = self.window.get_message_bus()
68
self.window_signals = [
69
self.window.connect('tab-removed', self.tab_removed),
70
self.window.connect('focus-in-event', self.focus_in_event)
74
self.bus.connect('/plugins/filebrowser', 'root_changed',
75
self.root_changed, None),
76
self.bus.connect('/plugins/filebrowser', 'inserted',
78
self.bus.connect('/plugins/filebrowser', 'deleted',
84
def do_deactivate(self):
87
for sid in self.window_signals:
88
self.window.disconnect(sid)
90
for sid in self.bus_signals:
91
self.bus.disconnect(sid)
97
self.window_signals = []
103
if self.bus.is_registered('/plugins/filebrowser', 'refresh'):
104
self.bus.send('/plugins/filebrowser', 'refresh')
106
def notify_status(self, view_activatable, psepc):
107
location = view_activatable.view.get_buffer().get_location()
109
if location is not None:
110
self.update_location(location)
112
def tab_removed(self, window, tab):
113
view = tab.get_view()
114
location = view.get_buffer().get_location()
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)
126
self.update_location(location)
128
def focus_in_event(self, window, event):
129
for view_activatable in self.view_activatables:
130
view_activatable.update()
132
for uri in self.files:
133
self.update_location(Gio.File.new_for_uri(uri))
135
def root_changed(self, bus, msg, data=None):
136
self.clear_monitors()
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()
145
except Exception as e:
150
self.monitor_directory(msg.location)
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
156
self.update_location(msg.location)
159
self.monitor_directory(msg.location)
161
def deleted(self, bus, msg, data=None):
162
# File browser's deleted signal is broken
165
uri = msg.location.get_uri()
168
del self.file_names[uri]
170
if uri in self.monitors:
171
self.monitors[uri].cancel()
172
del self.monitors[uri]
174
def update_location(self, location):
175
if self.repo is None:
178
if location.get_uri() not in self.files:
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()
187
if doc_location is not None and doc_location.equal(location):
188
status = view_activatable.status
192
git_status = self.repo.file_status(location)
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:
203
markup = GLib.markup_escape_text(self.file_names[location.get_uri()])
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)
212
elif status & Ggit.StatusFlags.INDEX_DELETED or \
213
status & Ggit.StatusFlags.WORKING_TREE_DELETED:
214
markup = '<span strikethrough="true">%s</span>' % (markup)
216
self.bus.send('/plugins/filebrowser', 'set_markup',
217
id=self.files[location.get_uri()], markup=markup)
219
def clear_monitors(self):
220
for uri in self.monitors:
221
self.monitors[uri].cancel()
225
def monitor_directory(self, location):
226
monitor = location.monitor(Gio.FileMonitorFlags.NONE, None)
227
self.monitors[location.get_uri()] = monitor
229
monitor.connect('changed', self.monitor_changed)
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):
237
self.update_location(f)