~ubuntu-branches/ubuntu/lucid/pida/lucid

« back to all changes in this revision

Viewing changes to pida-plugins/library/library.py

  • Committer: Bazaar Package Importer
  • Author(s): Jan Luebbe
  • Date: 2007-09-05 17:54:09 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20070905175409-ty9f6qpuctyjv1sd
Tags: 0.5.1-2
* Depend on librsvg2-common, which is not pulled in by the other depends
  (closes: #394860)
* gvim is no alternative for python-gnome2 and python-gnome2-extras
  (closes: #436431)
* Pida now uses ~/.pida2, so it can no longer be confused by old
  configurations (closes: #421378)
* Culebra is no longer supported by upstream (closes: #349009)
* Update manpage (closes: #440375)
* Update watchfile (pida is now called PIDA)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*- 
 
2
 
 
3
# Copyright (c) 2007 The PIDA Project
 
4
 
 
5
#Permission is hereby granted, free of charge, to any person obtaining a copy
 
6
#of this software and associated documentation files (the "Software"), to deal
 
7
#in the Software without restriction, including without limitation the rights
 
8
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
9
#copies of the Software, and to permit persons to whom the Software is
 
10
#furnished to do so, subject to the following conditions:
 
11
 
 
12
#The above copyright notice and this permission notice shall be included in
 
13
#all copies or substantial portions of the Software.
 
14
 
 
15
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
16
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
17
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
18
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
19
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
20
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
21
#SOFTWARE.
 
22
 
 
23
import os
 
24
import gzip
 
25
import tempfile
 
26
import threading
 
27
import xml.sax
 
28
import xml.dom.minidom as minidom
 
29
 
 
30
xml.sax.handler.feature_external_pes = False
 
31
 
 
32
import gtk
 
33
import gobject
 
34
from kiwi.ui.objectlist import ObjectTree, Column
 
35
 
 
36
# PIDA Imports
 
37
from pida.core.service import Service
 
38
from pida.core.features import FeaturesConfig
 
39
from pida.core.commands import CommandsConfig
 
40
from pida.core.events import EventsConfig
 
41
from pida.core.actions import ActionsConfig
 
42
from pida.core.actions import TYPE_NORMAL, TYPE_MENUTOOL, TYPE_RADIO, TYPE_TOGGLE
 
43
 
 
44
from pida.ui.views import PidaGladeView
 
45
 
 
46
from pida.utils.gthreads import GeneratorTask, AsyncTask
 
47
 
 
48
# locale
 
49
from pida.core.locale import Locale
 
50
locale = Locale('library')
 
51
_ = locale.gettext
 
52
 
 
53
class LibraryView(PidaGladeView):
 
54
    
 
55
    _columns = [
 
56
        Column('title', expand=True, sorted=True)
 
57
    ]
 
58
 
 
59
    gladefile = 'library-viewer'
 
60
    locale = locale
 
61
    icon_name = 'gtk-library'
 
62
    label_text = _('Documentation')
 
63
 
 
64
    def create_ui(self):
 
65
        self.books_list.set_columns(self._columns)
 
66
        self.contents_tree.set_columns(self._columns)
 
67
        self.books_list.set_headers_visible(False)
 
68
        self.contents_tree.set_headers_visible(False)
 
69
 
 
70
    def fetch_books(self):
 
71
        self.books_list.clear()
 
72
        self.contents_tree.clear()
 
73
        task = GeneratorTask(fetch_books, self.add_book)
 
74
        task.start()
 
75
 
 
76
    def add_book(self, item):
 
77
        self.books_list.append(item)
 
78
 
 
79
    def on_books_list__double_click(self, ol, item):
 
80
        self.contents_tree.clear()
 
81
        if item is not None:
 
82
            task = AsyncTask(self.load_book, self.book_loaded)
 
83
            task.start()
 
84
 
 
85
    def on_contents_tree__double_click(self, ot, item):
 
86
        #self.svc.boss.cmd('webbrowser', 'browse', url=item.path)
 
87
        self.svc.browse_file(item.path)
 
88
 
 
89
    def load_book(self):
 
90
        item = self.books_list.get_selected()
 
91
        return item.load()
 
92
        
 
93
    def book_loaded(self, bookmarks):
 
94
        self.contents_tree.clear()
 
95
        task = GeneratorTask(bookmarks.get_subs, self.add_bookmark)
 
96
        task.start()
 
97
        self.view_book.set_current_page(1)
 
98
 
 
99
    def add_bookmark(self, bookmark, parent):
 
100
        self.contents_tree.append(parent, bookmark)
 
101
 
 
102
    def on_refresh_button__clicked(self, button):
 
103
        self.fetch_books()
 
104
 
 
105
    def can_be_closed(self):
 
106
        self.svc.get_action('show_library').set_active(False)
 
107
 
 
108
 
 
109
class LibraryActions(ActionsConfig):
 
110
 
 
111
    def create_actions(self):
 
112
        self.create_action(
 
113
            'show_library',
 
114
            TYPE_TOGGLE,
 
115
            _('Documentation Library'),
 
116
            _('Show the documentation library'),
 
117
            '',
 
118
            self.on_show_library,
 
119
            '<Shift><Control>r',
 
120
        )
 
121
 
 
122
        self.create_action(
 
123
            'show_browser',
 
124
            TYPE_TOGGLE,
 
125
            _('Documentation Browser'),
 
126
            _('Documentation Browser'),
 
127
            '',
 
128
            self.on_show_browser,
 
129
            'NOACCEL',
 
130
        )
 
131
 
 
132
    def on_show_library(self, action):
 
133
        if action.get_active():
 
134
            self.svc.show_library()
 
135
        else:
 
136
            self.svc.hide_library()
 
137
 
 
138
    def on_show_browser(self, action):
 
139
        if action.get_active():
 
140
            self.svc.show_browser()
 
141
        else:
 
142
            self.svc.hide_browser()
 
143
 
 
144
def fetch_books():
 
145
    dirs = ['/usr/share/gtk-doc/html',
 
146
            '/usr/share/devhelp/books',
 
147
            os.path.expanduser('~/.devhelp/books')]
 
148
    
 
149
    use_gzip = True#self.opts.book_locations__use_gzipped_book_files
 
150
    for dir in dirs:
 
151
        for book in fetch_directory(dir):
 
152
            yield book
 
153
 
 
154
def fetch_directory(directory):
 
155
    if os.path.exists(directory):
 
156
        for name in os.listdir(directory):
 
157
            path = os.path.join(directory, name)
 
158
            if os.path.exists(path):
 
159
                load_book = Book(path, True)
 
160
                yield load_book
 
161
 
 
162
 
 
163
 
 
164
 
 
165
 
 
166
class TitleHandler(xml.sax.handler.ContentHandler):
 
167
 
 
168
    def __init__(self):
 
169
        self.title = _('untitled')
 
170
        self.is_finished = False
 
171
 
 
172
    def startElement(self, name, attributes):
 
173
        self.title = attributes['title']
 
174
        self.is_finished = True
 
175
 
 
176
 
 
177
class Book(object):
 
178
 
 
179
    def __init__(self, path, include_gz=True):
 
180
        self.directory = path
 
181
        self.key = path
 
182
        self.name = os.path.basename(path)
 
183
        self.bookmarks = None
 
184
        try:
 
185
            self.short_load()
 
186
        except (OSError, IOError):
 
187
            pass
 
188
 
 
189
    def has_load(self):
 
190
        return self.bookmarks is not None
 
191
 
 
192
    def short_load(self):
 
193
        config_path = None
 
194
        path = self.directory
 
195
        if not os.path.isdir(path):
 
196
            return
 
197
        for name in os.listdir(path):
 
198
            if name.endswith('.devhelp'):
 
199
                config_path = os.path.join(path, name)
 
200
                break
 
201
            elif name.endswith('.devhelp.gz'):
 
202
                gz_path = os.path.join(path, name)
 
203
                f = gzip.open(gz_path, 'rb', 1)
 
204
                gz_data = f.read()
 
205
                f.close()
 
206
                fd, config_path = tempfile.mkstemp()
 
207
                os.write(fd, gz_data)
 
208
                os.close(fd)
 
209
                break
 
210
        self.title = None
 
211
        if config_path:
 
212
            parser = xml.sax.make_parser()
 
213
            parser.setFeature(xml.sax.handler.feature_external_ges, 0)
 
214
            handler = TitleHandler()
 
215
            parser.setContentHandler(handler)
 
216
            f = open(config_path)
 
217
            for line in f:
 
218
                try:
 
219
                    parser.feed(line)
 
220
                except:
 
221
                    raise
 
222
                if handler.is_finished:
 
223
                    break
 
224
            f.close()
 
225
            self.title = handler.title
 
226
        if not self.title:
 
227
            self.title = os.path.basename(path)
 
228
 
 
229
    def load(self):
 
230
        config_path = None
 
231
        path = self.directory
 
232
        for name in os.listdir(path):
 
233
            if name.endswith('.devhelp'):
 
234
                config_path = os.path.join(path, name)
 
235
                break
 
236
            elif name.endswith('.devhelp.gz'):
 
237
                gz_path = os.path.join(path, name)
 
238
                f = gzip.open(gz_path, 'rb', 1)
 
239
                gz_data = f.read()
 
240
                f.close()
 
241
                fd, config_path = tempfile.mkstemp()
 
242
                os.write(fd, gz_data)
 
243
                os.close(fd)
 
244
                break
 
245
        if config_path and os.path.exists(config_path):
 
246
            dom = minidom.parse(config_path)
 
247
            main = dom.documentElement
 
248
            book_attrs = dict(main.attributes)
 
249
            for attr in book_attrs:
 
250
                setattr(self, attr, book_attrs[attr].value)
 
251
            self.chapters = dom.getElementsByTagName('chapters')[0]
 
252
            self.root = os.path.join(self.directory, self.link)
 
253
            self.bookmarks = self.get_bookmarks()
 
254
        else:
 
255
            for index in ['index.html']:
 
256
                indexpath = os.path.join(path, index)
 
257
                if os.path.exists(indexpath):
 
258
                    self.root = indexpath
 
259
                    break
 
260
                self.root = indexpath
 
261
 
 
262
        self.key = path
 
263
        return self.get_bookmarks()
 
264
 
 
265
    def get_bookmarks(self):
 
266
        root = BookMark(self.chapters, self.directory)
 
267
        root.name = self.title
 
268
        root.path = self.root
 
269
        return root
 
270
 
 
271
 
 
272
class BookMark(object):
 
273
 
 
274
    def __init__(self, node, root_path):
 
275
        try:
 
276
            self.name = node.attributes['name'].value
 
277
        except:
 
278
            self.name = None
 
279
        self.title = self.name
 
280
        try:
 
281
            self.path = os.path.join(root_path, node.attributes['link'].value)
 
282
        except:
 
283
            self.path = None
 
284
        self.key = self.path
 
285
        self.subs = []
 
286
        for child in self._get_child_subs(node):
 
287
            bm = BookMark(child, root_path)
 
288
            self.subs.append(bm)
 
289
 
 
290
    def _get_child_subs(self, node):
 
291
        return [n for n in node.childNodes if n.nodeType == 1]
 
292
 
 
293
    def get_subs(self, parent=None):
 
294
        if parent is None:
 
295
            aparent = self
 
296
        else:
 
297
            aparent = parent
 
298
        for sub in aparent.subs:
 
299
            yield sub, parent
 
300
            for csub in self.get_subs(sub):
 
301
                yield csub
 
302
                
 
303
    def __iter__(self):
 
304
            yield None, sub
 
305
        
 
306
 
 
307
 
 
308
# Service class
 
309
class Library(Service):
 
310
    """Describe your Service Here""" 
 
311
 
 
312
    actions_config = LibraryActions
 
313
 
 
314
    def start(self):
 
315
        self._view = LibraryView(self)
 
316
        bclass = self.boss.cmd('webbrowser', 'get_web_browser')
 
317
        self._browser = bclass(self)
 
318
        self._browser.label_text = _('Documentation')
 
319
        self._browser.connect_closed(self._on_close_clicked)
 
320
        self._has_loaded = False
 
321
 
 
322
    def show_library(self):
 
323
        self.boss.cmd('window', 'add_view', paned='Plugin', view=self._view)
 
324
        if not self._has_loaded:
 
325
            self._has_loaded = True
 
326
            self._view.fetch_books()
 
327
 
 
328
    def hide_library(self):
 
329
        self.boss.cmd('window', 'remove_view', view=self._view)
 
330
 
 
331
    def _on_close_clicked(self, button):
 
332
        self.get_action('show_browser').set_active(False)
 
333
 
 
334
    def show_browser(self):
 
335
        self.boss.cmd('window', 'add_view', paned='Terminal',
 
336
                      view=self._browser)
 
337
 
 
338
    def hide_browser(self):
 
339
        self.boss.cmd('window', 'remove_view', view=self._browser)
 
340
 
 
341
    def ensure_browser_visible(self):
 
342
        if not self.get_action('show_browser').get_active():
 
343
            self.get_action('show_browser').set_active(True)
 
344
        self.boss.cmd('window', 'present_view', view=self._browser)
 
345
 
 
346
    def browse_file(self, url):
 
347
        self.ensure_browser_visible()
 
348
        self._browser.fetch(url)
 
349
 
 
350
    def stop(self):
 
351
        if self.get_action('show_library').get_active():
 
352
            self.hide_library()
 
353
        if self.get_action('show_browser').get_active():
 
354
            self.hide_browser()
 
355
 
 
356
 
 
357
# Required Service attribute for service loading
 
358
Service = Library
 
359
 
 
360
 
 
361
 
 
362
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: