~bzr-gtk/bzr-gtk/0.95

0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
1
# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
import os
18
import sys
89 by Jelmer Vernooij
Rename OliveBranch -> BranchDialog.
19
20
# gettext support
21
import gettext
22
gettext.install('olive-gtk')
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
23
24
try:
25
    import pygtk
26
    pygtk.require("2.0")
27
except:
28
    pass
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
29
0.13.11 by Jelmer Vernooij
Bunch of small fixes, cleanups and simplifications.
30
import gtk
31
import gtk.gdk
32
import gtk.glade
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
33
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
34
from bzrlib.branch import Branch
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
35
import bzrlib.errors as errors
0.13.13 by Jelmer Vernooij
Update TODO
36
from bzrlib.workingtree import WorkingTree
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
37
38
# Olive GTK UI version
39
__version__ = '0.11.0'
40
0.13.10 by Jelmer Vernooij
Don't pass around gladefile all the time.
41
# Load the glade file
42
if sys.platform == 'win32':
0.13.13 by Jelmer Vernooij
Update TODO
43
    gladefile = os.path.dirname(sys.executable) + "/share/olive/olive.glade"
0.13.10 by Jelmer Vernooij
Don't pass around gladefile all the time.
44
else:
0.13.13 by Jelmer Vernooij
Update TODO
45
    gladefile = "/usr/share/olive/olive.glade"
0.13.10 by Jelmer Vernooij
Don't pass around gladefile all the time.
46
47
if not os.path.exists(gladefile):
0.14.9 by Alexander Belchenko
Try to find olive.glade in source directory (where is olive-gtk script)
48
    # Load from sources directory if not installed
49
    dir_ = os.path.split(os.path.dirname(__file__))[0]
50
    gladefile = os.path.join(dir_, "olive.glade")
0.13.13 by Jelmer Vernooij
Update TODO
51
    # Check again
52
    if not os.path.exists(gladefile):
53
        # Fail
54
        print _('Glade file cannot be found.')
55
        sys.exit(1)
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
56
57
from dialog import error_dialog, info_dialog
0.13.10 by Jelmer Vernooij
Don't pass around gladefile all the time.
58
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
59
class OliveGtk:
60
    """ The main Olive GTK frontend class. This is called when launching the
61
    program. """
62
    
63
    def __init__(self):
0.13.10 by Jelmer Vernooij
Don't pass around gladefile all the time.
64
        self.toplevel = gtk.glade.XML(gladefile, 'window_main', 'olive-gtk')
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
65
        
66
        self.window = self.toplevel.get_widget('window_main')
67
        
68
        self.pref = OlivePreferences()
69
70
        # Initialize the statusbar
71
        self.statusbar = self.toplevel.get_widget('statusbar')
72
        self.context_id = self.statusbar.get_context_id('olive')
73
        
74
        # Get the main window
75
        self.window_main = self.toplevel.get_widget('window_main')
76
        # Get the HPaned
77
        self.hpaned_main = self.toplevel.get_widget('hpaned_main')
78
        # Get the TreeViews
79
        self.treeview_left = self.toplevel.get_widget('treeview_left')
80
        self.treeview_right = self.toplevel.get_widget('treeview_right')
81
        # Get some important menu items
82
        self.menuitem_add_files = self.toplevel.get_widget('menuitem_add_files')
83
        self.menuitem_remove_files = self.toplevel.get_widget('menuitem_remove_file')
84
        self.menuitem_file_make_directory = self.toplevel.get_widget('menuitem_file_make_directory')
85
        self.menuitem_file_rename = self.toplevel.get_widget('menuitem_file_rename')
86
        self.menuitem_file_move = self.toplevel.get_widget('menuitem_file_move')
87
        self.menuitem_view_show_hidden_files = self.toplevel.get_widget('menuitem_view_show_hidden_files')
88
        self.menuitem_branch = self.toplevel.get_widget('menuitem_branch')
89
        self.menuitem_branch_init = self.toplevel.get_widget('menuitem_branch_initialize')
90
        self.menuitem_branch_get = self.toplevel.get_widget('menuitem_branch_get')
91
        self.menuitem_branch_checkout = self.toplevel.get_widget('menuitem_branch_checkout')
92
        self.menuitem_branch_pull = self.toplevel.get_widget('menuitem_branch_pull')
93
        self.menuitem_branch_push = self.toplevel.get_widget('menuitem_branch_push')
93 by Szilveszter Farkas (Phanatic)
Began to implement Merge dialog.
94
        self.menuitem_branch_merge = self.toplevel.get_widget('menuitem_branch_merge')
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
95
        self.menuitem_branch_commit = self.toplevel.get_widget('menuitem_branch_commit')
96
        self.menuitem_branch_status = self.toplevel.get_widget('menuitem_branch_status')
97
        self.menuitem_branch_missing = self.toplevel.get_widget('menuitem_branch_missing_revisions')
98
        self.menuitem_stats = self.toplevel.get_widget('menuitem_stats')
99
        self.menuitem_stats_diff = self.toplevel.get_widget('menuitem_stats_diff')
100
        self.menuitem_stats_log = self.toplevel.get_widget('menuitem_stats_log')
101
        # Get some toolbuttons
102
        #self.menutoolbutton_diff = self.toplevel.get_widget('menutoolbutton_diff')
103
        self.toolbutton_diff = self.toplevel.get_widget('toolbutton_diff')
104
        self.toolbutton_log = self.toplevel.get_widget('toolbutton_log')
105
        self.toolbutton_commit = self.toplevel.get_widget('toolbutton_commit')
106
        self.toolbutton_pull = self.toplevel.get_widget('toolbutton_pull')
107
        self.toolbutton_push = self.toplevel.get_widget('toolbutton_push')
108
        # Get the drive selector
109
        self.combobox_drive = gtk.combo_box_new_text()
110
        self.combobox_drive.connect("changed", self._refresh_drives)
111
        
112
        self.vbox_main_right = self.toplevel.get_widget('vbox_main_right')
0.13.13 by Jelmer Vernooij
Update TODO
113
 
114
        
115
        # Dictionary for signal_autoconnect
116
        dic = { "on_window_main_destroy": gtk.main_quit,
117
                "on_window_main_delete_event": self.on_window_main_delete_event,
118
                "on_quit_activate": self.on_window_main_delete_event,
119
                "on_about_activate": self.on_about_activate,
120
                "on_menuitem_add_files_activate": self.on_menuitem_add_files_activate,
121
                "on_menuitem_remove_file_activate": self.on_menuitem_remove_file_activate,
122
                "on_menuitem_file_make_directory_activate": self.on_menuitem_file_make_directory_activate,
123
                "on_menuitem_file_move_activate": self.on_menuitem_file_move_activate,
124
                "on_menuitem_file_rename_activate": self.on_menuitem_file_rename_activate,
125
                "on_menuitem_view_show_hidden_files_activate": self.on_menuitem_view_show_hidden_files_activate,
126
                "on_menuitem_view_refresh_activate": self.on_menuitem_view_refresh_activate,
127
                "on_menuitem_branch_initialize_activate": self.on_menuitem_branch_initialize_activate,
128
                "on_menuitem_branch_get_activate": self.on_menuitem_branch_get_activate,
129
                "on_menuitem_branch_checkout_activate": self.on_menuitem_branch_checkout_activate,
93 by Szilveszter Farkas (Phanatic)
Began to implement Merge dialog.
130
                "on_menuitem_branch_merge_activate": self.on_menuitem_branch_merge_activate,
0.13.13 by Jelmer Vernooij
Update TODO
131
                "on_menuitem_branch_commit_activate": self.on_menuitem_branch_commit_activate,
132
                "on_menuitem_branch_push_activate": self.on_menuitem_branch_push_activate,
133
                "on_menuitem_branch_pull_activate": self.on_menuitem_branch_pull_activate,
134
                "on_menuitem_branch_status_activate": self.on_menuitem_branch_status_activate,
135
                "on_menuitem_branch_missing_revisions_activate": self.on_menuitem_branch_missing_revisions_activate,
136
                "on_menuitem_stats_diff_activate": self.on_menuitem_stats_diff_activate,
137
                "on_menuitem_stats_log_activate": self.on_menuitem_stats_log_activate,
138
                "on_menuitem_stats_infos_activate": self.on_menuitem_stats_infos_activate,
139
                "on_toolbutton_refresh_clicked": self.on_menuitem_view_refresh_activate,
140
                "on_toolbutton_log_clicked": self.on_menuitem_stats_log_activate,
141
                #"on_menutoolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
142
                "on_toolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
143
                "on_toolbutton_commit_clicked": self.on_menuitem_branch_commit_activate,
144
                "on_toolbutton_pull_clicked": self.on_menuitem_branch_pull_activate,
145
                "on_toolbutton_push_clicked": self.on_menuitem_branch_push_activate,
146
                "on_treeview_right_button_press_event": self.on_treeview_right_button_press_event,
147
                "on_treeview_right_row_activated": self.on_treeview_right_row_activated,
148
                "on_treeview_left_button_press_event": self.on_treeview_left_button_press_event,
149
                "on_treeview_left_row_activated": self.on_treeview_left_row_activated }
150
        
151
        # Connect the signals to the handlers
152
        self.toplevel.signal_autoconnect(dic)
153
        
154
        # Apply window size and position
155
        width = self.pref.get_preference('window_width', 'int')
156
        height = self.pref.get_preference('window_height', 'int')
157
        self.window.resize(width, height)
158
        x = self.pref.get_preference('window_x', 'int')
159
        y = self.pref.get_preference('window_y', 'int')
160
        self.window.move(x, y)
161
        # Apply paned position
162
        pos = self.pref.get_preference('paned_position', 'int')
163
        self.hpaned_main.set_position(pos)
164
        
165
        # Apply menu to the toolbutton
166
        #menubutton = self.toplevel.get_widget('menutoolbutton_diff')
167
        #menubutton.set_menu(handler.menu.toolbar_diff)
168
        
169
        # Now we can show the window
170
        self.window.show()
171
        
172
        # Show drive selector if under Win32
173
        if sys.platform == 'win32':
174
            self.vbox_main_right.pack_start(self.combobox_drive, False, True, 0)
175
            self.vbox_main_right.reorder_child(self.combobox_drive, 0)
176
            self.combobox_drive.show()
177
            self.gen_hard_selector()
178
        
179
        self._load_left()
180
181
        # Apply menu state
182
        self.menuitem_view_show_hidden_files.set_active(self.pref.get_preference('dotted_files', 'bool'))
183
184
        self.set_path(os.getcwd())
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
185
        self._load_right()
0.13.13 by Jelmer Vernooij
Update TODO
186
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
187
    def set_path(self, path):
0.13.13 by Jelmer Vernooij
Update TODO
188
        self.path = path
189
        self.notbranch = False
190
        try:
191
            self.wt, self.wtpath = WorkingTree.open_containing(self.path)
0.14.10 by Alexander Belchenko
Fixed problem when user try to cd to shared repo root directory.
192
        except (errors.NotBranchError, errors.NoWorkingTree):
0.13.13 by Jelmer Vernooij
Update TODO
193
            self.notbranch = True
194
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
195
    def get_path(self):
0.13.13 by Jelmer Vernooij
Update TODO
196
        return self.path
197
   
198
    def on_about_activate(self, widget):
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
199
        from dialog import about
0.13.13 by Jelmer Vernooij
Update TODO
200
        about()
201
        
202
    def on_menuitem_add_files_activate(self, widget):
203
        """ Add file(s)... menu handler. """
204
        from add import OliveAdd
205
        add = OliveAdd(self.wt, self.wtpath, self.get_selected_right())
206
        add.display()
207
    
208
    def on_menuitem_branch_get_activate(self, widget):
209
        """ Branch/Get... menu handler. """
89 by Jelmer Vernooij
Rename OliveBranch -> BranchDialog.
210
        from branch import BranchDialog
211
        branch = BranchDialog(self.get_path())
0.13.13 by Jelmer Vernooij
Update TODO
212
        branch.display()
213
    
214
    def on_menuitem_branch_checkout_activate(self, widget):
215
        """ Branch/Checkout... menu handler. """
216
        from checkout import OliveCheckout
0.8.96 by Szilveszter Farkas (Phanatic)
Checkout cleaned up.
217
        checkout = OliveCheckout(self.get_path())
0.13.13 by Jelmer Vernooij
Update TODO
218
        checkout.display()
219
    
220
    def on_menuitem_branch_commit_activate(self, widget):
221
        """ Branch/Commit... menu handler. """
89 by Jelmer Vernooij
Rename OliveBranch -> BranchDialog.
222
        from commit import CommitDialog
223
        commit = CommitDialog(self.wt, self.wtpath)
0.13.13 by Jelmer Vernooij
Update TODO
224
        commit.display()
225
    
93 by Szilveszter Farkas (Phanatic)
Began to implement Merge dialog.
226
    def on_menuitem_branch_merge_activate(self, widget):
227
        """ Branch/Merge... menu handler. """
228
        from merge import MergeDialog
229
        merge = MergeDialog(self.wt, self.wtpath)
230
        merge.display()
231
0.13.13 by Jelmer Vernooij
Update TODO
232
    def on_menuitem_branch_missing_revisions_activate(self, widget):
233
        """ Branch/Missing revisions menu handler. """
234
        local_branch = self.wt.branch
235
        
236
        other_branch = local_branch.get_parent()
237
        if other_branch is None:
238
            error_dialog(_('Parent location is unknown'),
0.8.94 by Szilveszter Farkas (Phanatic)
Cleanups: bookmarks and sensitivity.
239
                         _('Cannot determine missing revisions if no parent location is known.'))
0.13.13 by Jelmer Vernooij
Update TODO
240
            return
241
        
242
        remote_branch = Branch.open(other_branch)
243
        
244
        if remote_branch.base == local_branch.base:
245
            remote_branch = local_branch
246
247
        ret = len(local_branch.missing_revisions(remote_branch))
248
249
        if ret > 0:
250
            info_dialog(_('There are missing revisions'),
0.8.94 by Szilveszter Farkas (Phanatic)
Cleanups: bookmarks and sensitivity.
251
                        _('%d revision(s) missing.') % ret)
0.13.13 by Jelmer Vernooij
Update TODO
252
        else:
253
            info_dialog(_('Local branch up to date'),
0.8.94 by Szilveszter Farkas (Phanatic)
Cleanups: bookmarks and sensitivity.
254
                        _('There are no missing revisions.'))
0.13.13 by Jelmer Vernooij
Update TODO
255
256
    def on_menuitem_branch_pull_activate(self, widget):
257
        """ Branch/Pull menu handler. """
258
        branch_to = self.wt.branch
259
260
        location = branch_to.get_parent()
261
        if location is None:
262
            error_dialog(_('Parent location is unknown'),
263
                                     _('Pulling is not possible until there is a parent location.'))
264
            return
265
266
        try:
267
            branch_from = Branch.open(location)
268
        except errors.NotBranchError:
269
            error_dialog(_('Directory is not a branch'),
270
                                     _('You can perform this action only in a branch.'))
271
272
        if branch_to.get_parent() is None:
273
            branch_to.set_parent(branch_from.base)
274
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
275
        #old_rh = branch_to.revision_history()
276
        #if tree_to is not None:
277
        #    tree_to.pull(branch_from)
278
        #else:
279
        #    branch_to.pull(branch_from)
0.14.14 by Alexander Belchenko
get number of pulled revision: it's return value of branch.pull() method
280
        ret = branch_to.pull(branch_from)
0.13.13 by Jelmer Vernooij
Update TODO
281
        
282
        info_dialog(_('Pull successful'), _('%d revision(s) pulled.') % ret)
283
    
284
    def on_menuitem_branch_push_activate(self, widget):
285
        """ Branch/Push... menu handler. """
286
        from push import OlivePush
287
        push = OlivePush(self.wt.branch)
288
        push.display()
289
    
290
    def on_menuitem_branch_status_activate(self, widget):
291
        """ Branch/Status... menu handler. """
292
        from status import OliveStatus
293
        status = OliveStatus(self.wt, self.wtpath)
294
        status.display()
295
    
296
    def on_menuitem_branch_initialize_activate(self, widget):
297
        """ Initialize current directory. """
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
298
        import bzrlib.bzrdir as bzrdir
299
        
0.13.13 by Jelmer Vernooij
Update TODO
300
        try:
301
            if not os.path.exists(self.path):
302
                os.mkdir(self.path)
303
     
304
            try:
305
                existing_bzrdir = bzrdir.BzrDir.open(self.path)
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
306
            except errors.NotBranchError:
0.13.13 by Jelmer Vernooij
Update TODO
307
                bzrdir.BzrDir.create_branch_convenience(self.path)
308
            else:
309
                if existing_bzrdir.has_branch():
310
                    if existing_bzrdir.has_workingtree():
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
311
                        raise errors.AlreadyBranchError(self.path)
0.13.13 by Jelmer Vernooij
Update TODO
312
                    else:
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
313
                        raise errors.BranchExistsWithoutWorkingTree(self.path)
0.13.13 by Jelmer Vernooij
Update TODO
314
                else:
315
                    existing_bzrdir.create_branch()
316
                    existing_bzrdir.create_workingtree()
317
        except errors.AlreadyBranchError, errmsg:
318
            error_dialog(_('Directory is already a branch'),
319
                                     _('The current directory (%s) is already a branch.\nYou can start using it, or initialize another directory.') % errmsg)
320
        except errors.BranchExistsWithoutWorkingTree, errmsg:
321
            error_dialog(_('Branch without a working tree'),
322
                                     _('The current directory (%s)\nis a branch without a working tree.') % errmsg)
323
        else:
324
            info_dialog(_('Initialize successful'),
325
                                    _('Directory successfully initialized.'))
326
            self.refresh_right()
327
        
328
    def on_menuitem_file_make_directory_activate(self, widget):
329
        """ File/Make directory... menu handler. """
330
        from mkdir import OliveMkdir
331
        mkdir = OliveMkdir(self.wt, self.wtpath)
332
        mkdir.display()
333
    
334
    def on_menuitem_file_move_activate(self, widget):
335
        """ File/Move... menu handler. """
336
        from move import OliveMove
337
        move = OliveMove(self.wt, self.wtpath, self.get_selected_right())
338
        move.display()
339
    
340
    def on_menuitem_file_rename_activate(self, widget):
341
        """ File/Rename... menu handler. """
342
        from rename import OliveRename
343
        rename = OliveRename(self.wt, self.wtpath, self.get_selected_right())
344
        rename.display()
345
346
    def on_menuitem_remove_file_activate(self, widget):
347
        """ Remove (unversion) selected file. """
348
        from remove import OliveRemove
349
        remove = OliveRemove(self.wt, self.wtpath, self.get_selected_right())
350
        remove.display()
351
    
352
    def on_menuitem_stats_diff_activate(self, widget):
353
        """ Statistics/Differences... menu handler. """
354
        from bzrlib.plugins.gtk.viz.diffwin import DiffWindow
355
        window = DiffWindow()
356
        parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
357
        window.set_diff(self.wt.branch.nick, self.wt, parent_tree)
358
        window.show()
359
    
360
    def on_menuitem_stats_infos_activate(self, widget):
361
        """ Statistics/Informations... menu handler. """
362
        from info import OliveInfo
363
        info = OliveInfo(self.wt)
364
        info.display()
365
    
366
    def on_menuitem_stats_log_activate(self, widget):
367
        """ Statistics/Log... menu handler. """
368
        from bzrlib.plugins.gtk.viz.branchwin import BranchWindow
369
        window = BranchWindow()
370
        window.set_branch(self.wt.branch, self.wt.branch.last_revision(), None)
371
        window.show()
372
    
373
    def on_menuitem_view_refresh_activate(self, widget):
374
        """ View/Refresh menu handler. """
375
        # Refresh the left pane
376
        self.refresh_left()
377
        # Refresh the right pane
378
        self.refresh_right()
379
   
380
    def on_menuitem_view_show_hidden_files_activate(self, widget):
381
        """ View/Show hidden files menu handler. """
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
382
        self.pref.set_preference('dotted_files', widget.get_active())
0.13.13 by Jelmer Vernooij
Update TODO
383
384
    def on_treeview_left_button_press_event(self, widget, event):
385
        """ Occurs when somebody right-clicks in the bookmark list. """
386
        if event.button == 3:
387
            # Don't show context with nothing selected
388
            if self.get_selected_left() == None:
389
                return
390
0.8.93 by Szilveszter Farkas (Phanatic)
Some further cleanups. More to come.
391
            # Create a menu
392
            from menu import OliveMenu
393
            menu = OliveMenu(self.get_path(), self.get_selected_left())
394
            
395
            menu.left_context_menu().popup(None, None, None, 0,
396
                                           event.time)
0.8.94 by Szilveszter Farkas (Phanatic)
Cleanups: bookmarks and sensitivity.
397
0.13.13 by Jelmer Vernooij
Update TODO
398
    def on_treeview_left_row_activated(self, treeview, path, view_column):
399
        """ Occurs when somebody double-clicks or enters an item in the
400
        bookmark list. """
401
402
        newdir = self.get_selected_left()
403
        if newdir == None:
404
            return
405
406
        self.set_path(newdir)
407
        self.refresh_right()
0.8.94 by Szilveszter Farkas (Phanatic)
Cleanups: bookmarks and sensitivity.
408
0.13.13 by Jelmer Vernooij
Update TODO
409
    def on_treeview_right_button_press_event(self, widget, event):
410
        """ Occurs when somebody right-clicks in the file list. """
411
        if event.button == 3:
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
412
            # Create a menu
413
            from menu import OliveMenu
414
            menu = OliveMenu(self.get_path(), self.get_selected_right())
0.13.13 by Jelmer Vernooij
Update TODO
415
            # get the menu items
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
416
            m_add = menu.ui.get_widget('/context_right/add')
417
            m_remove = menu.ui.get_widget('/context_right/remove')
418
            m_commit = menu.ui.get_widget('/context_right/commit')
419
            m_diff = menu.ui.get_widget('/context_right/diff')
0.13.13 by Jelmer Vernooij
Update TODO
420
            # check if we're in a branch
421
            try:
422
                from bzrlib.branch import Branch
423
                Branch.open_containing(self.get_path())
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
424
                m_add.set_sensitive(True)
425
                m_remove.set_sensitive(True)
426
                m_commit.set_sensitive(True)
427
                m_diff.set_sensitive(True)
428
            except errors.NotBranchError:
0.13.13 by Jelmer Vernooij
Update TODO
429
                m_add.set_sensitive(False)
430
                m_remove.set_sensitive(False)
431
                m_commit.set_sensitive(False)
432
                m_diff.set_sensitive(False)
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
433
            menu.right_context_menu().popup(None, None, None, 0,
434
                                            event.time)
0.13.13 by Jelmer Vernooij
Update TODO
435
        
436
    def on_treeview_right_row_activated(self, treeview, path, view_column):
437
        """ Occurs when somebody double-clicks or enters an item in the
438
        file list. """
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
439
        from launch import launch
0.13.13 by Jelmer Vernooij
Update TODO
440
        
441
        newdir = self.get_selected_right()
442
        
443
        if newdir == '..':
444
            self.set_path(os.path.split(self.get_path())[0])
445
        else:
0.14.11 by Alexander Belchenko
Make change of drive letter on win32 is actually workable
446
            fullpath = os.path.join(self.get_path(), newdir)
0.13.13 by Jelmer Vernooij
Update TODO
447
            if os.path.isdir(fullpath):
448
                # selected item is an existant directory
449
                self.set_path(fullpath)
450
            else:
451
                launch(fullpath) 
452
        
453
        self.refresh_right()
454
    
455
    def on_window_main_delete_event(self, widget, event=None):
456
        """ Do some stuff before exiting. """
457
        width, height = self.window_main.get_size()
458
        self.pref.set_preference('window_width', width)
459
        self.pref.set_preference('window_height', height)
460
        x, y = self.window_main.get_position()
461
        self.pref.set_preference('window_x', x)
462
        self.pref.set_preference('window_y', y)
463
        self.pref.set_preference('paned_position',
464
                                      self.hpaned_main.get_position())
465
        
466
        self.pref.write()
467
        self.window_main.destroy()
468
        
469
    def _load_left(self):
470
        """ Load data into the left panel. (Bookmarks) """
471
        # Create TreeStore
472
        treestore = gtk.TreeStore(str, str)
473
        
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
474
        # Get bookmarks
475
        bookmarks = self.pref.get_bookmarks()
0.13.13 by Jelmer Vernooij
Update TODO
476
        
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
477
        # Add them to the TreeStore
478
        titer = treestore.append(None, [_('Bookmarks'), None])
479
        for item in bookmarks:
480
            title = self.pref.get_bookmark_title(item)
481
            treestore.append(titer, [title, item])
0.13.13 by Jelmer Vernooij
Update TODO
482
        
483
        # Create the column and add it to the TreeView
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
484
        self.treeview_left.set_model(treestore)
0.13.13 by Jelmer Vernooij
Update TODO
485
        tvcolumn_bookmark = gtk.TreeViewColumn(_('Bookmark'))
486
        self.treeview_left.append_column(tvcolumn_bookmark)
487
        
488
        # Set up the cells
489
        cell = gtk.CellRendererText()
490
        tvcolumn_bookmark.pack_start(cell, True)
491
        tvcolumn_bookmark.add_attribute(cell, 'text', 0)
492
        
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
493
        # Expand the tree
494
        self.treeview_left.expand_all()
495
0.14.12 by Alexander Belchenko
updir link (..) added always to the top of directories list
496
    def _add_updir_to_dirlist(self, dirlist, curdir):
497
        """Add .. to the top of directories list if we not in root directory
498
499
        @param  dirlist:    list of directories (modified in place)
500
        @param  curdir:     current directory
501
        @return:            nothing
502
        """
503
        if curdir is None:
504
            curdir = self.path
505
506
        if sys.platform == 'win32':
507
            drive, tail = os.path.splitdrive(curdir)
508
            if tail in ('', '/', '\\'):
509
                return
510
        else:
511
            if curdir == '/':
512
                return
513
514
        # insert always as first element
515
        dirlist.insert(0, '..')
516
0.13.13 by Jelmer Vernooij
Update TODO
517
    def _load_right(self):
518
        """ Load data into the right panel. (Filelist) """
519
        # Create ListStore
520
        liststore = gtk.ListStore(str, str, str)
521
        
0.14.12 by Alexander Belchenko
updir link (..) added always to the top of directories list
522
        dirs = []
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
523
        files = []
0.13.13 by Jelmer Vernooij
Update TODO
524
        
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
525
        # Fill the appropriate lists
526
        dotted_files = self.pref.get_preference('dotted_files', 'bool')
0.13.13 by Jelmer Vernooij
Update TODO
527
        for item in os.listdir(self.path):
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
528
            if not dotted_files and item[0] == '.':
529
                continue
0.13.13 by Jelmer Vernooij
Update TODO
530
            if os.path.isdir(self.path + os.sep + item):
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
531
                dirs.append(item)
532
            else:
533
                files.append(item)
0.13.13 by Jelmer Vernooij
Update TODO
534
            
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
535
        # Sort'em
536
        dirs.sort()
537
        files.sort()
0.14.12 by Alexander Belchenko
updir link (..) added always to the top of directories list
538
539
        # add updir link to dirs
540
        self._add_updir_to_dirlist(dirs, self.path)
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
541
        
0.13.13 by Jelmer Vernooij
Update TODO
542
        if not self.notbranch:
543
            branch = self.wt.branch
544
            tree2 = self.wt.branch.repository.revision_tree(branch.last_revision())
545
        
546
            delta = self.wt.changes_from(tree2, want_unchanged=True)
547
        
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
548
        # Add'em to the ListStore
0.13.13 by Jelmer Vernooij
Update TODO
549
        for item in dirs:    
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
550
            liststore.append([gtk.STOCK_DIRECTORY, item, ''])
551
        for item in files:
552
            status = 'unknown'
0.13.13 by Jelmer Vernooij
Update TODO
553
            if not self.notbranch:
554
                filename = self.wt.relpath(self.path + os.sep + item)
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
555
                
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
556
                for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
557
                    if rpathnew == filename:
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
558
                        status = 'renamed'
559
                for rpath, id, kind in delta.added:
560
                    if rpath == filename:
561
                        status = 'added'
0.13.13 by Jelmer Vernooij
Update TODO
562
                for rpath, id, kind in delta.removed:
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
563
                    if rpath == filename:
564
                        status = 'removed'
565
                for rpath, id, kind, text_modified, meta_modified in delta.modified:
566
                    if rpath == filename:
567
                        status = 'modified'
568
                for rpath, id, kind in delta.unchanged:
569
                    if rpath == filename:
570
                        status = 'unchanged'
571
            
572
            #try:
573
            #    status = fileops.status(path + os.sep + item)
574
            #except errors.PermissionDenied:
575
            #    continue
0.13.13 by Jelmer Vernooij
Update TODO
576
            
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
577
            if status == 'renamed':
578
                st = _('renamed')
579
            elif status == 'removed':
580
                st = _('removed')
581
            elif status == 'added':
582
                st = _('added')
583
            elif status == 'modified':
584
                st = _('modified')
585
            elif status == 'unchanged':
586
                st = _('unchanged')
587
            else:
588
                st = _('unknown')
589
            liststore.append([gtk.STOCK_FILE, item, st])
0.13.13 by Jelmer Vernooij
Update TODO
590
        
591
        # Create the columns and add them to the TreeView
592
        self.treeview_right.set_model(liststore)
593
        tvcolumn_filename = gtk.TreeViewColumn(_('Filename'))
594
        tvcolumn_status = gtk.TreeViewColumn(_('Status'))
595
        self.treeview_right.append_column(tvcolumn_filename)
596
        self.treeview_right.append_column(tvcolumn_status)
597
        
598
        # Set up the cells
599
        cellpb = gtk.CellRendererPixbuf()
600
        cell = gtk.CellRendererText()
601
        tvcolumn_filename.pack_start(cellpb, False)
602
        tvcolumn_filename.pack_start(cell, True)
603
        tvcolumn_filename.set_attributes(cellpb, stock_id=0)
604
        tvcolumn_filename.add_attribute(cell, 'text', 1)
605
        tvcolumn_status.pack_start(cell, True)
606
        tvcolumn_status.add_attribute(cell, 'text', 2)
0.8.95 by Szilveszter Farkas (Phanatic)
Some sensitivity changes; branching (get) fixed.
607
        
608
        # Set sensitivity
609
        self.set_sensitivity()
610
        
611
    def get_selected_right(self):
612
        """ Get the selected filename. """
613
        treeselection = self.treeview_right.get_selection()
614
        (model, iter) = treeselection.get_selected()
615
        
616
        if iter is None:
617
            return None
618
        else:
619
            return model.get_value(iter, 1)
620
    
621
    def get_selected_left(self):
622
        """ Get the selected bookmark. """
623
        treeselection = self.treeview_left.get_selection()
624
        (model, iter) = treeselection.get_selected()
625
        
626
        if iter is None:
627
            return None
628
        else:
629
            return model.get_value(iter, 1)
630
631
    def set_statusbar(self, message):
632
        """ Set the statusbar message. """
633
        self.statusbar.push(self.context_id, message)
634
    
635
    def clear_statusbar(self):
636
        """ Clean the last message from the statusbar. """
637
        self.statusbar.pop(self.context_id)
638
    
639
    def set_sensitivity(self):
640
        """ Set menu and toolbar sensitivity. """
0.13.13 by Jelmer Vernooij
Update TODO
641
        self.menuitem_branch_init.set_sensitive(self.notbranch)
0.8.95 by Szilveszter Farkas (Phanatic)
Some sensitivity changes; branching (get) fixed.
642
        self.menuitem_branch_checkout.set_sensitive(self.notbranch)
0.13.13 by Jelmer Vernooij
Update TODO
643
        self.menuitem_branch_pull.set_sensitive(not self.notbranch)
644
        self.menuitem_branch_push.set_sensitive(not self.notbranch)
93 by Szilveszter Farkas (Phanatic)
Began to implement Merge dialog.
645
        self.menuitem_branch_merge.set_sensitive(not self.notbranch)
0.13.13 by Jelmer Vernooij
Update TODO
646
        self.menuitem_branch_commit.set_sensitive(not self.notbranch)
647
        self.menuitem_branch_status.set_sensitive(not self.notbranch)
648
        self.menuitem_branch_missing.set_sensitive(not self.notbranch)
649
        self.menuitem_stats.set_sensitive(not self.notbranch)
650
        self.menuitem_add_files.set_sensitive(not self.notbranch)
651
        self.menuitem_remove_files.set_sensitive(not self.notbranch)
652
        self.menuitem_file_make_directory.set_sensitive(not self.notbranch)
653
        self.menuitem_file_rename.set_sensitive(not self.notbranch)
654
        self.menuitem_file_move.set_sensitive(not self.notbranch)
655
        #self.menutoolbutton_diff.set_sensitive(True)
656
        self.toolbutton_diff.set_sensitive(not self.notbranch)
657
        self.toolbutton_log.set_sensitive(not self.notbranch)
658
        self.toolbutton_commit.set_sensitive(not self.notbranch)
659
        self.toolbutton_pull.set_sensitive(not self.notbranch)
660
        self.toolbutton_push.set_sensitive(not self.notbranch)
661
    
662
    def refresh_left(self):
663
        """ Refresh the bookmark list. """
664
        
665
        # Get TreeStore and clear it
666
        treestore = self.treeview_left.get_model()
667
        treestore.clear()
0.8.94 by Szilveszter Farkas (Phanatic)
Cleanups: bookmarks and sensitivity.
668
669
        # Re-read preferences
670
        self.pref.read()
0.13.13 by Jelmer Vernooij
Update TODO
671
672
        # Get bookmarks
673
        bookmarks = self.pref.get_bookmarks()
674
675
        # Add them to the TreeStore
676
        titer = treestore.append(None, [_('Bookmarks'), None])
677
        for item in bookmarks:
678
            title = self.pref.get_bookmark_title(item)
679
            treestore.append(titer, [title, item])
680
681
        # Add the TreeStore to the TreeView
682
        self.treeview_left.set_model(treestore)
683
684
        # Expand the tree
685
        self.treeview_left.expand_all()
686
687
    def refresh_right(self, path=None):
688
        """ Refresh the file list. """
689
        from bzrlib.workingtree import WorkingTree
690
691
        if path is None:
692
            path = self.get_path()
693
694
        # A workaround for double-clicking Bookmarks
695
        if not os.path.exists(path):
696
            return
697
698
        # Get ListStore and clear it
699
        liststore = self.treeview_right.get_model()
700
        liststore.clear()
701
0.14.12 by Alexander Belchenko
updir link (..) added always to the top of directories list
702
        dirs = []
0.13.13 by Jelmer Vernooij
Update TODO
703
        files = []
704
705
        # Fill the appropriate lists
706
        dotted_files = self.pref.get_preference('dotted_files', 'bool')
707
        for item in os.listdir(path):
708
            if not dotted_files and item[0] == '.':
709
                continue
710
            if os.path.isdir(path + os.sep + item):
711
                dirs.append(item)
712
            else:
713
                files.append(item)
714
715
        # Sort'em
716
        dirs.sort()
717
        files.sort()
0.14.12 by Alexander Belchenko
updir link (..) added always to the top of directories list
718
719
        # add updir link to dirs
720
        self._add_updir_to_dirlist(dirs, path)
721
0.13.13 by Jelmer Vernooij
Update TODO
722
        # Try to open the working tree
723
        notbranch = False
724
        try:
725
            tree1 = WorkingTree.open_containing(path)[0]
0.14.10 by Alexander Belchenko
Fixed problem when user try to cd to shared repo root directory.
726
        except (errors.NotBranchError, errors.NoWorkingTree):
0.13.13 by Jelmer Vernooij
Update TODO
727
            notbranch = True
728
        except errors.PermissionDenied:
729
            print "DEBUG: permission denied."
730
        
731
        if not notbranch:
732
            branch = tree1.branch
733
            tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
734
        
735
            delta = tree1.changes_from(tree2, want_unchanged=True)
736
737
        # Add'em to the ListStore
738
        for item in dirs:
739
            liststore.append([gtk.STOCK_DIRECTORY, item, ''])
740
        for item in files:
741
            status = 'unknown'
742
            if not notbranch:
743
                filename = tree1.relpath(path + os.sep + item)
744
                
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
745
                for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
746
                    if rpathnew == filename:
0.13.13 by Jelmer Vernooij
Update TODO
747
                        status = 'renamed'
748
                for rpath, id, kind in delta.added:
749
                    if rpath == filename:
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
750
                        status = 'added'                
89 by Jelmer Vernooij
Rename OliveBranch -> BranchDialog.
751
                for rpath, id, kind in delta.removed:
0.13.13 by Jelmer Vernooij
Update TODO
752
                    if rpath == filename:
753
                        status = 'removed'
754
                for rpath, id, kind, text_modified, meta_modified in delta.modified:
755
                    if rpath == filename:
756
                        status = 'modified'
757
                for rpath, id, kind in delta.unchanged:
758
                    if rpath == filename:
759
                        status = 'unchanged'
760
            
761
            #try:
762
            #    status = fileops.status(path + os.sep + item)
763
            #except errors.PermissionDenied:
764
            #    continue
765
766
            if status == 'renamed':
767
                st = _('renamed')
768
            elif status == 'removed':
769
                st = _('removed')
770
            elif status == 'added':
771
                st = _('added')
772
            elif status == 'modified':
773
                st = _('modified')
774
            elif status == 'unchanged':
775
                st = _('unchanged')
776
            else:
777
                st = _('unknown')
778
            liststore.append([gtk.STOCK_FILE, item, st])
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
779
780
        # Add the ListStore to the TreeView
781
        self.treeview_right.set_model(liststore)
0.8.94 by Szilveszter Farkas (Phanatic)
Cleanups: bookmarks and sensitivity.
782
        
0.8.95 by Szilveszter Farkas (Phanatic)
Some sensitivity changes; branching (get) fixed.
783
        # Set sensitivity
784
        self.set_sensitivity()
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
785
786
    def _harddisks(self):
787
        """ Returns hard drive letters under Win32. """
788
        try:
789
            import win32file
790
            import string
791
        except ImportError:
792
            if sys.platform == 'win32':
793
                print "pyWin32 modules needed to run Olive on Win32."
794
                sys.exit(1)
795
            else:
796
                pass
797
        
798
        driveletters = []
799
        for drive in string.ascii_uppercase:
800
            if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED:
801
                driveletters.append(drive+':')
802
        return driveletters
803
    
804
    def gen_hard_selector(self):
805
        """ Generate the hard drive selector under Win32. """
806
        drives = self._harddisks()
807
        for drive in drives:
808
            self.combobox_drive.append_text(drive)
809
    
810
    def _refresh_drives(self, combobox):
811
        model = combobox.get_model()
812
        active = combobox.get_active()
813
        if active >= 0:
814
            drive = model[active][0]
0.14.11 by Alexander Belchenko
Make change of drive letter on win32 is actually workable
815
            self.set_path(drive + '\\')
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
816
            self.refresh_right(drive + '\\')
0.13.13 by Jelmer Vernooij
Update TODO
817
818
import ConfigParser
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
819
820
class OlivePreferences:
821
    """ A class which handles Olive's preferences. """
822
    def __init__(self):
823
        """ Initialize the Preferences class. """
824
        # Some default options
825
        self.defaults = { 'strict_commit' : False,
826
                          'dotted_files'  : False,
827
                          'window_width'  : 700,
828
                          'window_height' : 400,
829
                          'window_x'      : 40,
830
                          'window_y'      : 40,
831
                          'paned_position': 200 }
832
833
        # Create a config parser object
834
        self.config = ConfigParser.RawConfigParser()
835
        
836
        # Load the configuration
0.8.93 by Szilveszter Farkas (Phanatic)
Some further cleanups. More to come.
837
        self.read()
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
838
        
839
    def _get_default(self, option):
840
        """ Get the default option for a preference. """
841
        try:
842
            ret = self.defaults[option]
843
        except KeyError:
844
            return None
845
        else:
846
            return ret
847
848
    def refresh(self):
849
        """ Refresh the configuration. """
850
        # First write out the changes
851
        self.write()
852
        # Then load the configuration again
0.8.93 by Szilveszter Farkas (Phanatic)
Some further cleanups. More to come.
853
        self.read()
854
855
    def read(self):
856
        """ Just read the configuration. """
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
857
        if sys.platform == 'win32':
858
            # Windows - no dotted files
859
            self.config.read([os.path.expanduser('~/olive.conf')])
860
        else:
861
            self.config.read([os.path.expanduser('~/.olive.conf')])
0.8.93 by Szilveszter Farkas (Phanatic)
Some further cleanups. More to come.
862
    
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
863
    def write(self):
864
        """ Write the configuration to the appropriate files. """
865
        if sys.platform == 'win32':
866
            # Windows - no dotted files
867
            fp = open(os.path.expanduser('~/olive.conf'), 'w')
868
            self.config.write(fp)
869
            fp.close()
870
        else:
871
            fp = open(os.path.expanduser('~/.olive.conf'), 'w')
872
            self.config.write(fp)
873
            fp.close()
0.13.13 by Jelmer Vernooij
Update TODO
874
875
    def get_bookmarks(self):
876
        """ Return the list of bookmarks. """
877
        bookmarks = self.config.sections()
878
        if self.config.has_section('preferences'):
879
            bookmarks.remove('preferences')
880
        return bookmarks
881
882
    def add_bookmark(self, path):
883
        """ Add bookmark. """
884
        try:
885
            self.config.add_section(path)
886
        except ConfigParser.DuplicateSectionError:
887
            return False
888
        else:
889
            return True
890
891
    def get_bookmark_title(self, path):
892
        """ Get bookmark title. """
893
        try:
894
            ret = self.config.get(path, 'title')
895
        except ConfigParser.NoOptionError:
896
            ret = path
897
        
898
        return ret
899
    
900
    def set_bookmark_title(self, path, title):
901
        """ Set bookmark title. """
902
        self.config.set(path, 'title', title)
903
    
904
    def remove_bookmark(self, path):
905
        """ Remove bookmark. """
906
        return self.config.remove_section(path)
907
908
    def set_preference(self, option, value):
909
        """ Set the value of the given option. """
910
        if self.config.has_section('preferences'):
911
            self.config.set('preferences', option, value)
912
        else:
913
            self.config.add_section('preferences')
914
            self.config.set('preferences', option, value)
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
915
916
    def get_preference(self, option, kind='str'):
917
        """ Get the value of the given option.
918
        
919
        :param kind: str/bool/int/float. default: str
920
        """
921
        if self.config.has_option('preferences', option):
922
            if kind == 'bool':
0.8.92 by Szilveszter Farkas (Phanatic)
Cleanup Jelmer's changes.
923
                #return self.config.getboolean('preferences', option)
924
                return True
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
925
            elif kind == 'int':
926
                return self.config.getint('preferences', option)
927
            elif kind == 'float':
928
                return self.config.getfloat('preferences', option)
929
            else:
930
                return self.config.get('preferences', option)
931
        else:
932
            try:
933
                return self._get_default(option)
934
            except KeyError:
935
                return None
0.13.13 by Jelmer Vernooij
Update TODO
936