1 # Copyright 2011 Robert Lopez Toscano
3 # This file is part of Giterdone.
5 # Giterdone 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 3 of the License, or
10 # Giterdone 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 Giterdone. If not, see <http://www.gnu.org/licenses/>.
25 from giterdone
.common
import get_icon
26 from giterdone
.repository
import Repository
27 import giterdone
.vcs
as vcs
29 class RepositoryPane(gtk
.TreeView
):
30 """A gtk.TreeView that displays a list of the repositories being managed by
35 <popup name="RepositoryPanePopup">
36 <menuitem action="VcsCommit"/>
37 <menuitem action="VcsFetch"/>
40 _ICON_NAME
= 'giterdone-repo-manager'
41 _TITLE
= 'Repository Manager'
46 _FB_BUS_ID
= '/plugins/filebrowser'
47 _FB_ROOT_CHANGED_EVT
= 'root_changed'
48 _logger
= logging
.getLogger('giterdone.RepositoryPane')
50 def __init__(self
, window
, repo_monitor
, repo_manager
, window_controller
):
51 super(RepositoryPane
, self
).__init
__(gtk
.ListStore(object, # repo
54 gtk
.gdk
.Pixbuf
)) # emblem
55 self
._curr
_root
= None
56 self
._statusbars
= {} # statusbar context id -> statusbar
57 self
._bus
_connection
_ids
= []
59 self
._repo
_monitor
= repo_monitor
60 self
._repo
_monitor
.subscribe_to_repo_branch_changes(
61 self
._on
_repo
_branch
_changed
)
62 self
._repo
_monitor
.subscribe_to_repo_discoveries(self
._add
_repo
)
63 self
._repo
_monitor
.subscribe_to_repo_state_changes(
64 self
._on
_repo
_state
_changed
)
65 self
._repo
_manager
= repo_manager
66 self
._window
_controller
= window_controller
69 self
.set_grid_lines(False)
70 self
.set_headers_clickable(True)
71 self
.append_column(gtk
.TreeViewColumn(None, gtk
.CellRendererPixbuf(),
72 pixbuf
=RepositoryPane
._COL
_EMBLEM
))
73 self
.append_column(gtk
.TreeViewColumn('Name', gtk
.CellRendererText(),
74 text
=RepositoryPane
._COL
_NAME
))
75 self
.append_column(gtk
.TreeViewColumn('Branch', gtk
.CellRendererText(),
76 text
=RepositoryPane
._COL
_BRANCH
))
77 self
.connect('button-release-event', self
._on
_button
_released
)
79 # Create the repository pane popup
80 self
.uimanager
= gtk
.UIManager()
81 self
.uimanager
.add_ui_from_string(RepositoryPane
._POPUP
_XML
)
82 self
.actiongroup
= gtk
.ActionGroup('VcsBase')
83 # TODO(robert) BEFORE ACTIVATING THESE, USE REPO_MANAGER WHEN DOING VCS
85 self
.actiongroup
.add_actions([
86 ('VcsCommit', 'giterdone-commit', 'Commit', None,
87 'Commit all staged files', self
._on
_vcs
_commit
_all
)])
88 # ('VcsFetch', gtk.STOCK_GO_DOWN, 'Fetch', None,
89 # 'Fetch from remote repositories', self._on_vcs_fetch)])
90 # ('VcsPush', gtk.STOCK_GOTO_TOP, 'Push', None,
91 # 'Push to origin repository', self._on_vcs_push),
92 # ('VcsPull', gtk.STOCK_GOTO_BOTTOM, 'Pull', None,
93 # 'Pull from remote reposities', self._on_vcs_pull)])
94 self
.uimanager
.insert_action_group(self
.actiongroup
, 0)
96 self
._window
.get_side_panel().add_item(
97 self
, RepositoryPane
._TITLE
,
98 gtk
.image_new_from_icon_name(
99 RepositoryPane
._ICON
_NAME
, gtk
.ICON_SIZE_MENU
))
100 self
._bus
_connection
_ids
.append(window
.get_message_bus().connect(
101 RepositoryPane
._FB
_BUS
_ID
,
102 RepositoryPane
._FB
_ROOT
_CHANGED
_EVT
,
103 self
._on
_fb
_root
_changed
))
105 def deactivate(self
):
106 self
._window
.get_side_panel().remove_item(self
)
107 map(self
._window
.get_message_bus().disconnect
, self
._bus
_connection
_ids
)
108 self
._repo
_monitor
.unsubscribe_to_repo_branch_changes(
109 self
._on
_repo
_branch
_changed
)
110 self
._repo
_monitor
.unsubscribe_to_repo_discoveries(self
._add
_repo
)
111 self
._repo
_monitor
.unsubscribe_to_repo_state_changes(
112 self
._on
_repo
_state
_changed
)
114 def _on_fb_root_changed(self
, bus
, message
):
115 """Adds/removes repositories to/from the repository pane based on the new
116 root and each repository's path."""
117 self
._curr
_root
= message
.get_value('uri')[len('file://'):]
119 # remove repos from the model
121 for row
in self
.get_model(): toremove
.append(row
)
122 for row
in toremove
: self
.get_model().remove(row
.iter)
124 # Add current set of repos
125 curr_repos
= self
._repo
_monitor
.get_repos_under_path(self
._curr
_root
)
126 for repo
in curr_repos
:
129 def _add_repo(self
, repo
):
130 self
.get_model().append((
132 self
._generate
_repo
_name
(repo
),
133 repo
.get_branch_name(),
134 self
._get
_emblem
(repo
)))
135 RepositoryPane
._logger
.info('Added repository %s' % (repo
.path
,))
137 def _on_repo_state_changed(self
, repo
):
138 for row
in self
.get_model():
139 if row
[RepositoryPane
._COL
_REPO
] == repo
:
140 self
.get_model().set_value(
141 row
.iter, RepositoryPane
._COL
_EMBLEM
, self
._get
_emblem
(repo
))
144 def _on_repo_branch_changed(self
, repo
):
145 # TODO(robert) hook this up to an event
146 for row
in self
.get_model():
147 if row
[RepositoryPane
._COL
_REPO
] == repo
:
148 self
.get_model().set_value(
149 row
.iter, RepositoryPane
._COL
_BRANCH
, repo
.get_branch_name())
152 def _set_action_sensitivities(self
):
153 model
, rows
= self
.get_selection().get_selected_rows()
156 RepositoryPane
._logger
.info('User selected multiple repositories')
157 for action
in self
.actiongroup
.list_actions():
158 action
.set_sensitive(False)
161 repo
= model
[rows
[0]][RepositoryPane
._COL
_REPO
]
163 RepositoryPane
._logger
.info('User selected repository %s' % (repo
.path
,))
165 # activate all commands
166 for action
in self
.actiongroup
.list_actions():
167 action
.set_sensitive(True)
169 # don't activate commit if there is nothing staged
170 if not repo
.has_staged_files():
171 RepositoryPane
._logger
.debug(
172 'Deactivating commit action since repository has no staged files')
173 self
.actiongroup
.get_action('VcsCommit').set_sensitive(False)
175 def _on_button_released(self
, widget
, event
):
176 if event
.button
== 3: # right-click
177 self
._set
_action
_sensitivities
()
180 self
.uimanager
.get_widget('/RepositoryPanePopup').popup(
181 None, None, None, event
.button
, 0)
183 def _on_vcs_commit_all(self
, action
):
184 model
, rows
= self
.get_selection().get_selected_rows()
186 repo
= model
[rows
[0]][RepositoryPane
._COL
_REPO
]
187 self
._window
_controller
.start_commit_edit([repo
.path
])
189 def _on_vcs_pull(self
, action
):
190 model
, rows
= self
.get_selection().get_selected_rows()
192 for repo
in [row
[RepositoryPane
._COL
_REPO
] for row
in rows
]:
193 # TODO(robert) implement
196 def _on_vcs_push(self
, action
):
197 model
, rows
= self
.get_selection().get_selected_rows()
199 for repo
in [row
[RepositoryPane
._COL
_REPO
] for row
in rows
]:
200 # TODO(robert) implement
203 def _on_vcs_fetch(self
, action
):
204 model
, rows
= self
.get_selection().get_selected_rows()
206 for repo
in [model
[row
][RepositoryPane
._COL
_REPO
] for row
in rows
]:
207 for context_id
, statusbar
in self
._statusbars
.iteritems():
208 self
._window
.get_statusbar().flash_message(
209 self
._sb
_context
_id
, 'Fetching repository %s' % (repo
.path
,))
210 vcs
.interfaces
[repo
.type].fetch(repo
.path
)
212 def _get_emblem(self
, repo
):
213 return get_icon(repo
.get_file_state(repo
.path
))
215 def _generate_repo_name(self
, repo
):
216 if not self
._curr
_root
:
217 msg
= self
._window
.get_message_bus().send_sync(RepositoryPane
._FB
_BUS
_ID
,
219 self
._curr
_root
= msg
.get_value('uri')[len('file://'):]
220 if repo
.path
== self
._curr
_root
: # curr_root is repo
221 return os
.path
.basename(repo
.path
)
222 elif repo
.path
.startswith(self
._curr
_root
+ '/'): # curr_root contains repo
223 return '.' + repo
.path
[len(self
._curr
_root
):]
224 else: # curr_root is within repo
225 return os
.path
.basename(repo
.path
)