Source

pida-main / pida-plugins / library / library.py

Full commit
# -*- coding: utf-8 -*- 

# Copyright (c) 2007 The PIDA Project

import os
import gzip
import tempfile
import threading
import xml.sax
import xml.dom.minidom as minidom

xml.sax.handler.feature_external_pes = False

import gtk
import gobject
from pygtkhelpers.ui.objectlist import ObjectTree, Column

# PIDA Imports
from pida.core.service import Service
from pida.core.features import FeaturesConfig
from pida.core.commands import CommandsConfig
from pida.core.events import EventsConfig
from pida.core.actions import ActionsConfig

from pida.ui.views import PidaView

from pygtkhelpers.gthreads import GeneratorTask, AsyncTask
from pida.ui.actions import PidaRememberToggle

# locale
from pida.core.locale import Locale
locale = Locale('library')
_ = locale.gettext

class LibraryView(PidaView):

    key = 'library.list'
    
    _columns = [
        Column('title', expand=True, sorted=True)
    ]

    gladefile = 'library_viewer'
    locale = locale
    icon_name = 'gtk-library'
    label_text = _('Documentation')

    def create_ui(self):
        self.books_list.set_columns(self._columns)
        self.contents_tree.set_columns(self._columns)
        self.books_list.set_headers_visible(False)
        self.contents_tree.set_headers_visible(False)

    def fetch_books(self):
        self.books_list.clear()
        self.contents_tree.clear()
        task = GeneratorTask(fetch_books, self.add_book)
        task.start()

    def add_book(self, item):
        self.books_list.append(item)

    def on_books_list__item_activated(self, ol, item):
        self.contents_tree.clear()
        if item is not None:
            task = AsyncTask(self.load_book, self.book_loaded)
            task.start()

    def on_contents_tree__item_activated(self, ot, item):
        self.svc.browse_file('file://' + item.path)

    def load_book(self):
        item = self.books_list.selected_item
        return item.load()
 
    def book_loaded(self, bookmarks):
        self.contents_tree.clear()
        task = GeneratorTask(bookmarks.get_subs, self.add_bookmark)
        task.start()
        self.view_book.set_current_page(1)

    def add_bookmark(self, bookmark, parent):
        self.contents_tree.append(bookmark, parent=parent)

    def on_refresh_button__clicked(self, button):
        self.fetch_books()

    def can_be_closed(self):
        self.svc.get_action('show_library').set_active(False)


class LibraryActions(ActionsConfig):


    actions = [
        PidaRememberToggle('show_library',
            _('Documentation Library'),
            _('Show the documentation library'),
            ''),
        PidaRememberToggle('show_browser',
            _('Documentation Browser'),
            _('Documentation Browser'),
            ''),
    ]

    accels = {
        'show_library': '<Shift><Control>r',
    }

    def on_show_library(self, action):
        if action.get_active():
            self.svc.show_library()
        else:
            self.svc.hide_library()

    def on_show_browser(self, action):
        if action.get_active():
            self.svc.show_browser()
        else:
            self.svc.hide_browser()

def fetch_books():
    dirs = ['/usr/share/gtk-doc/html',
            '/usr/share/devhelp/books',
            os.path.expanduser('~/.devhelp/books')]
    
    use_gzip = True#self.opts.book_locations__use_gzipped_book_files
    for dir in dirs:
        for book in fetch_directory(dir):
            yield book

def fetch_directory(directory):
    if os.path.exists(directory):
        for name in os.listdir(directory):
            path = os.path.join(directory, name)
            if os.path.exists(path):
                load_book = Book(path, True)
                yield load_book





class TitleHandler(xml.sax.handler.ContentHandler):

    def __init__(self):
        self.title = _('untitled')
        self.is_finished = False

    def startElement(self, name, attributes):
        self.title = attributes['title']
        self.is_finished = True


class Book(object):

    def __init__(self, path, include_gz=True):
        self.directory = path
        self.key = path
        self.name = os.path.basename(path)
        self.bookmarks = None
        try:
            self.short_load()
        except (OSError, IOError):
            pass

    def __repr__(self):
        return '<Book {name}>'.format(name=self.name)

    def has_load(self):
        return self.bookmarks is not None

    def short_load(self):
        config_path = None
        path = self.directory
        if not os.path.isdir(path):
            return
        for name in os.listdir(path):
            if name.endswith('.devhelp'):
                config_path = os.path.join(path, name)
                break
            elif name.endswith('.devhelp.gz'):
                gz_path = os.path.join(path, name)
                f = gzip.open(gz_path, 'rb', 1)
                gz_data = f.read()
                f.close()
                fd, config_path = tempfile.mkstemp()
                os.write(fd, gz_data)
                os.close(fd)
                break
        self.title = None
        if config_path:
            parser = xml.sax.make_parser()
            parser.setFeature(xml.sax.handler.feature_external_ges, 0)
            handler = TitleHandler()
            parser.setContentHandler(handler)
            f = open(config_path)
            for line in f:
                try:
                    parser.feed(line)
                except:
                    raise
                if handler.is_finished:
                    break
            f.close()
            self.title = handler.title
        if not self.title:
            self.title = os.path.basename(path)

    def load(self):
        config_path = None
        path = self.directory
        for name in os.listdir(path):
            if name.endswith('.devhelp'):
                config_path = os.path.join(path, name)
                break
            elif name.endswith('.devhelp.gz'):
                gz_path = os.path.join(path, name)
                f = gzip.open(gz_path, 'rb', 1)
                gz_data = f.read()
                f.close()
                fd, config_path = tempfile.mkstemp()
                os.write(fd, gz_data)
                os.close(fd)
                break
        if config_path and os.path.exists(config_path):
            dom = minidom.parse(config_path)
            main = dom.documentElement
            book_attrs = dict(main.attributes)
            for attr in book_attrs:
                setattr(self, attr, book_attrs[attr].value)
            self.chapters = dom.getElementsByTagName('chapters')[0]
            self.root = os.path.join(self.directory, self.link)
            self.bookmarks = self.get_bookmarks()
        else:
            for index in ['index.html']:
                indexpath = os.path.join(path, index)
                if os.path.exists(indexpath):
                    self.root = indexpath
                    break
                self.root = indexpath

        self.key = path
        return self.get_bookmarks()

    def get_bookmarks(self):
        root = BookMark(self.chapters, self.directory)
        root.name = self.title
        root.path = self.root
        return root


class BookMark(object):

    def __init__(self, node, root_path):
        try:
            self.name = node.attributes['name'].value
        except:
            self.name = None
        self.title = self.name
        try:
            self.path = os.path.join(root_path, node.attributes['link'].value)
        except:
            self.path = None
        self.key = self.path
        self.subs = []
        for child in self._get_child_subs(node):
            bm = BookMark(child, root_path)
            self.subs.append(bm)

    def _get_child_subs(self, node):
        return [n for n in node.childNodes if n.nodeType == 1]

    def get_subs(self, parent=None):
        if parent is None:
            aparent = self
        else:
            aparent = parent
        for sub in aparent.subs:
            yield sub, parent
            for csub in self.get_subs(sub):
                yield csub
                
    def __iter__(self):
            yield None, sub


# Service class
class Library(Service):
    """Describe your Service Here""" 

    actions_config = LibraryActions

    def start(self):
        self._view = LibraryView(self)
        bclass = self.boss.cmd('browseweb', 'get_web_browser')
        self._browser = bclass(self)
        self._browser.label_text = _('Documentation')
        self._browser.connect_closed(self._on_close_clicked)
        self._has_loaded = False

    def show_library(self):
        self.boss.cmd('window', 'add_view', paned='Plugin', view=self._view)
        if not self._has_loaded:
            self._has_loaded = True
            self._view.fetch_books()

    def hide_library(self):
        self.boss.cmd('window', 'remove_view', view=self._view)

    def _on_close_clicked(self, button):
        self.get_action('show_browser').set_active(False)

    def show_browser(self):
        self.boss.cmd('window', 'add_view', paned='Terminal',
                      view=self._browser)

    def hide_browser(self):
        self.boss.cmd('window', 'remove_view', view=self._browser)

    def ensure_browser_visible(self):
        if not self.get_action('show_browser').get_active():
            self.get_action('show_browser').set_active(True)
        self.boss.cmd('window', 'present_view', view=self._browser)

    def browse_file(self, url):
        self.ensure_browser_visible()
        self._browser.fetch(url)

    def stop(self):
        if self.get_action('show_library').get_active():
            self.hide_library()
        if self.get_action('show_browser').get_active():
            self.hide_browser()

# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: