oobrowser.py
author Nicolas Chauvat <nicolas.chauvat@logilab.fr>
Mon, 23 Jun 2008 13:42:22 +0200
changeset 6 6aad8b499e86
parent 0 7710b138d4eb
permissions -rw-r--r--
fix reST markup

#!/usr/bin/env python
#
# Copyright (c) 2004 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# 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.
"""A file system objects browser. This module contains the main application
window.

USAGE: oobrother [root directory]
"""

__revision__ = '$Id: oobrowser.py,v 1.34 2006-03-26 20:20:54 nico Exp $'

import sys
import os
from os.path import abspath, basename, isdir, isfile, join

import gtk, gobject, gtk.glade

from pigg.utils import confirm

from oobrother import get_path, localize_app
from oobrother.uiutils.windows import LogServiceWindow, SystemWindow, \
     ConfigurationWindow
from oobrother.uiutils.basemixins import PluggableFrameMixIn, TreeViewMixIn
from oobrother.uiutils.trees import init_treeview_columns
from oobrother.uiutils import init_treeview_columns, create_popup_menu, \
     merge_actions, save_session, restore_session, change_cursor_for_operation
from oobrother.registry import PlugInsRegistry
from oobrother.sysutils import OOBROTHER_HOME
from oobrother.config_tools import PluggableConfig
from oobrother.editors import EmacsClient
from oobrother.filelib import get_fs_treeview

__metaclass__ = type

class FSBrowser(PluggableFrameMixIn, TreeViewMixIn):
    """The FileSystem Browser"""
    options = (
        ('plugins', {'type': 'csv',
                     'default': (#'oobrother.plugins.base',
                                 'oobrother.plugins.vcs',
                                 'oobrother.plugins.devtools',
                                 'oobrother.plugins.python',
                                 'oobrother.plugins.pychecker',
                                 'oobrother.plugins.debian')
                     }),
        ('ignored-extensions', {'type': 'csv',
                                'default': ('CVS', 'BAK', 'bak', '~',
                                            'swp', 'pyc', 'pyo', ',cover')
                                }),
        ('show-hidden-files', {'type': 'yn',
                               'default': False}),
        )

    name = 'main'
    def __init__(self, root_dir):
        # We need to have self.treeview in TreeviewMixIn's init()
        self.widgets = gtk.glade.XML(get_path('glade'), 'main_window', 'oobrother')
        super(FSBrowser, self).__init__()
        self.root_directory = root_dir = abspath(root_dir)
        self.main_window = self.widgets.get_widget('main_window')
        # first init the log window to not loose anything
        self.log_window = LogServiceWindow()
        # reference main windows / widgets
        self.console = SystemWindow()
        self.editor = EmacsClient()
        self.plugin_frame = self.widgets.get_widget('plugin_frame')
        self.filename_filter_entry = self.widgets.get_widget('filename_filter_entry')
        self.content_filter_entry = self.widgets.get_widget('content_filter_entry')
        self.filename_filter_entry.connect('activate', self.cb_update_fname_filter)
        self.content_filter_entry.connect('activate', self.cb_update_content_filter)
        # self.content_filter_entry.connect('activate', self.cb_filter_filetree)
        # build configuration stuff
        self.cfg = PluggableConfig(self.name, 'fsbrowser.ini', self.options)
        self.registry = PlugInsRegistry(self)
        self.registry.register_all(self.cfg['plugins'])
        self.cfg_window = ConfigurationWindow( (self, self.registry) )
        # set tree model
        scr = self.widgets.get_widget('scr_window')
        self.treeview = get_fs_treeview(root_dir, self.cfg['ignored-extensions'],
                                        self.cfg['show-hidden-files'])
        self.treeview.show()
        self.activate_default_callbacks()
        scr.add(self.treeview)
        # Quit program when main_window gets closed
        self.main_window.connect('delete-event', self.cb_quit)
        # Main callbacks
        handlers = {
                    'on_reload_activate' : self.cb_reload_filetree,
                    'on_filter_button_clicked' : self.cb_filter_filetree,
                    # 'on_refilter_button_clicked' : self.cb_refilter_filetree,
                    'on_configuration1_activate': self.cfg_window.cb_toggle_visibility,
                    'on_log1_activate': self.log_window.cb_toggle_visibility,
                    'on_console1_activate': self.console.cb_toggle_visibility,
                    'on_quit_activate': self.cb_quit,
                    }
        self.widgets.signal_autoconnect(handlers)
        self._wdefs = {'main': self.main_window,
                       'log': self.log_window.win,
                       'console': self.console.win,
                       'config': self.cfg_window.win,
                       }
        self.log_window.set_menu_item(self.widgets.get_widget('log1'))
        self.console.set_menu_item(self.widgets.get_widget('console1'))
        self.cfg_window.set_menu_item(self.widgets.get_widget('configuration1'))

        restore_session(self._wdefs, join(OOBROTHER_HOME, 'session.pickle'))
        
    def subconfiguration(self):
        """return a list of configuration nodes for subconfiguration
        (i.e. only the log window at this day...)
        """
        return [self.log_window]

    def cb_reload_filetree(self, menuitem):
        """force file browser to reload"""
        # FIXME : find a way to restore the original unfolded aspect
        model = self.treeview.get_model() 
        self.treeview.collapse_all()
        model.root_node.force_update()

    def cb_filter_filetree(self, source):
        """set a filter on the file browser"""
        filter_model = self.treeview.get_model()
        basemodel = filter_model.get_model()
        pattern = self.filename_filter_entry.get_text().strip()
        basemodel.filter_def.remove_all_filename_restrictions()
        if pattern:
            basemodel.filter_def.add_filename_restriction(pattern)
        pattern = self.content_filter_entry.get_text().strip()
        basemodel.filter_def.remove_all_content_restrictions()
        if pattern:
            basemodel.filter_def.add_content_restriction(pattern)
        change_cursor_for_operation(basemodel.update, self.treeview)
            
    def cb_update_fname_filter(self, fname_entry):
        pattern = fname_entry.get_text().strip()
        basemodel = self.treeview.get_model().get_model()
        basemodel.filter_def.remove_all_filename_restrictions()
        basemodel.filter_def.add_filename_restriction(pattern)
        change_cursor_for_operation(basemodel.update, self.treeview)

    def cb_update_content_filter(self, content_entry):
        pattern = content_entry.get_text().strip()
        basemodel = self.treeview.get_model().get_model()
        basemodel.filter_def.remove_all_content_restrictions()
        basemodel.filter_def.add_content_restriction(pattern)
        change_cursor_for_operation(basemodel.update, self.treeview)
    
    def cb_quit(self, *args):
        """exit the application after having saved the current session"""
        save_session(self._wdefs, join(OOBROTHER_HOME, 'session.pickle'))
        gtk.main_quit()
        
    def on_double_click(self, row_model, event):
        """called on double-click in the tree view:
        
        open the selected file in the editor
        """
        thefile = row_model[0]
        self.editor.open(thefile.abspath)
        return True

    def on_right_click(self, row_model, event):
        """called on right button click in the tree view:
        
        display available plugin actions for the selected file in a popup menu
        """
        thefile = row_model[0]
        if thefile is not None:
            plugins = self.registry.get_plugins(thefile)
            self._popup_action_menu(event, plugins, thefile)
        else:
            return True

    def on_delete_pressed(self, row_model, event):
        """called when the Delete key is pressed in the tree view:
        
        remove the selected file or directory
        """
        thefile = row_model[0]
        # FIXME: ask confirmation ?
        if thefile is not None and confirm(_('are you sure you want to delete this item ?')):
            thefile.get_parent().remove(thefile.get_name())
        else:
            return True
        
    def _popup_action_menu(self, event, plugins, thefile):
        """Popups the actions contextual menu
        """
        actions = []
        for plugin in plugins:
            actions += plugin.get_actions(thefile)
        actions = merge_actions(actions)
        popup_menu = create_popup_menu(actions, thefile)
        popup_menu.show_all()
        popup_menu.popup(None, None, None, event.button, event.time)
        return True

    def show(self):
        """Shows the main window"""
        self.main_window.show()

import hotshot
import atexit
def run_profiled(args=None):
    import hotshot as profile
    prof = profile.Profile("oobrother.prof")
    atexit.register(prof.close)
    try:
        prof.runctx('run(args)', globals(), locals())
    except SystemExit:
        pass
    
    
def run(args=None):
    """main()"""

    if args is None:
        args = sys.argv[1:]
    if len(args) > 1:
        print "Bad usage"
        print __doc__
        sys.exit(1)
    elif args:
        root_dir = args[0]
    else:
        root_dir = os.getcwd()
    localize_app()
    assert isdir(root_dir)
    spb = FSBrowser(root_dir)
    spb.show()
    gtk.main()

    
if __name__ == '__main__':
    run(sys.argv[1:])