Viewer.py :  » Network » Grail-Internet-Browser » grail-0.6 » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Network » Grail Internet Browser 
Grail Internet Browser » grail 0.6 » Viewer.py
"""Viewer class."""

import sys
from Tkinter import *
import tktools
import formatter
import string
from string import strip
from Context import Context,SimpleContext
from Cursors import *
from types import StringType
from urlparse import urljoin,urlparse
from Stylesheet import UndefinedStyle


MIN_IMAGE_LEADER = "\240"               # Non-spacing space
INDENTATION_WIDTH = 30                  # Pixels / indent level
TARGET_SEPARATOR = '\1'                 # url TARGET_SEPARATOR target


font_dingbats = {
    'disc': ('\x6c', '_ding'),
    'circle': ('\x6d', '_ding'),
    'square': ('\x6f', '_ding'),
    }


class WidthMagic:
    def __init__(self, viewer, abswidth, percentwidth):
        self.__abswidth = abswidth
        self.__percentwidth = percentwidth
        self.__text = viewer.text
        dents = viewer.marginlevel + viewer.rightmarginlevel
        self.__removable = (dents * INDENTATION_WIDTH) \
                           + viewer.RULE_WIDTH_MAGIC

    def close(self):
        self.__text = None              # free reference

    def get_available_width(self):
        #  Getting the 'padx' option of the text widget needs to be done
        #  here for an as-yet undetermined reason.
        return int(self.__text.winfo_width() - self.__removable
                   - (2 * string.atoi(self.__text["padx"])))

    def get_requested_widths(self):
        return self.__abswidth, self.__percentwidth


class HRule(Canvas):
    def __init__(self, viewer, abswidth, percentwidth, height=2, **kw):
        self.__magic = viewer.width_magic(abswidth, percentwidth)
        kw["borderwidth"] = 1
        kw["relief"] = SUNKEN
        kw["height"] = max(0, height - 2)
        bgcolor = viewer.text["background"]
        kw["background"] = bgcolor
        kw["highlightbackground"] = bgcolor
        kw["highlightthickness"] = 0
        kw["width"] = self.get_width()
        apply(Canvas.__init__, (self, viewer.text), kw)

    def get_width(self):
        maxwid = self.__magic.get_available_width()
        abswidth, percentwidth = self.__magic.get_requested_widths()
        if abswidth:
            return min(abswidth, maxwid)
        else:
            return maxwid * percentwidth

    def destroy(self):
        self.__magic.close()
        Canvas.destroy(self)


class Viewer(formatter.AbstractWriter):

    """A viewer is mostly a fancy text widget with scroll bars.

    It also doubles as the 'writer' for a Formatter.

    """

    def __init__(self, master, browser=None, context=None, stylesheet=None,
                 width=80, height=40, name="", scrolling="auto",
                 parent=None):
        formatter.AbstractWriter.__init__(self)
        self.master = master
        if not browser:
            if parent:
                browser = parent.context.browser
        self.context = context or Context(self, browser)
        self.prefs = self.context.app.prefs
        self.stylesheet = stylesheet or self.context.app.stylesheet
        self.name = name
        self.scrolling = scrolling
        self.parent = parent
        self.subwindows = []
        self.rules = []
        self.subviewers = []
        self.resize_interests = [self.__class__.resize_rules]
        self.reset_interests = [self.__class__.clear_targets]
        self.current_cursor = CURSOR_NORMAL
        if not self.parent:
            # Avoid showing the widget until it's fully constructed:
            self.master.withdraw()
        self.__fonttags_built = {}
        self.init_presentation()
        self.create_widgets(width=width, height=height)
        self.reset_state()
        self.freeze(1)
        self.text.bind('<Configure>', self.resize_event)
        self._atemp = []
        self.current_index = None
        self.popup_menu = None
        self.status = StringVar(self.master)
        self.linkinfo = ""
        if self.context.viewer is self:
            self.frame.bind('<Enter>', self.enter_frame)
        if self.parent:
            self.parent.add_subviewer(self)
        self.message("")
        self.add_styles_callbacks()
        if not self.parent:
            # Ok, now show the fully constructed widget:
            self.master.deiconify()
            self.master.tkraise()

    def add_styles_callbacks(self):
        """Add prefs callbacks so text widget's reconfigured on major changes.
        """
        self.prefs.AddGroupCallback('styles-common', self.init_styles)
        self.prefs.AddGroupCallback('styles-fonts', self.init_styles)
        self.prefs.AddGroupCallback('styles', self.init_styles)
        self.prefs.AddGroupCallback('presentation',
                                    self.configure_presentation)

    def remove_styles_callbacks(self):
        self.prefs.RemoveGroupCallback('styles-common', self.init_styles)
        self.prefs.RemoveGroupCallback('styles-fonts', self.init_styles)
        self.prefs.RemoveGroupCallback('styles', self.init_styles)
        self.prefs.RemoveGroupCallback('presentation',
                                       self.configure_presentation)

    def message(self, message):
        if not self.context or self.linkinfo:
            return
        if self.name:
            message = "%s: %s" % (self.name, message)
        self.status.set(message)
        if not self.parent:
            self.context.browser.messagevariable(self.status)
        if self.context.busy() and self.context.viewer is self:
            cursor = CURSOR_WAIT
        else:
            cursor = CURSOR_NORMAL
        self.set_cursor(cursor)

    def enter_frame(self, event):
        self.context.browser.messagevariable(self.status)

    def reset_state(self):
        self.fonttag = None             # Tag specifying font
        self.margintag = None           # Tag specifying margin (left)
        self.marginlevel = 0            # Numeric margin level (left)
        self.rightmargintag = None      # Tag specifying margin (right)
        self.rightmarginlevel = 0       # Numeric margin level (right)
        self.spacingtag = None          # Tag specifying spacing
        self.addtags = ()               # Additional tags (e.g. anchors)
        self.align = None               # Alignment setting
        self.pendingdata = ''           # Data 'on hold'
        self.targets = {}               # Mark names for anchors/footnotes
        self.new_tags()

    def __del__(self):
        self.close()

    def close(self):
        context = self.context
        self.remove_styles_callbacks()
        if context and context.viewer is self:
            context.stop()
        if context:
            self.clear_reset()
            self.context = None
        frame = self.frame
        if frame:
            self.text = None
            frame.destroy()
            self.frame = None
        parent = self.parent
        if parent:
            parent.remove_subviewer(self)
            self.parent = None

    def create_widgets(self, width, height):
        bars = self.scrolling == "auto" or self.scrolling
        self.smoothscroll = bars and self.context.app.prefs.GetBoolean(
            "browser", "smooth-scroll-hack")
        if self.smoothscroll:
            from supertextbox import make_super_text_box
            self.text, self.frame = make_super_text_box(self.master,
                                                      width=width,
                                                      height=height,
                                                      hbar=bars, vbar=bars)
        else:
            self.text, self.frame = tktools.make_text_box(self.master,
                                                      width=width,
                                                      height=height,
                                                      hbar=bars, vbar=bars,
                                                      class_="Viewer")
        if self.parent:
            self.text.config(background=self.parent.text['background'],
                             foreground=self.parent.text['foreground'])
        self.text.config(padx=10, cursor=self.current_cursor,
                         selectbackground='yellow', insertwidth=0)
        self.default_bg = self.text['background']
        self.default_fg = self.text['foreground']
        self.configure_styles()
        if self.parent:
            link = self.parent.text.tag_configure('a', 'foreground')[-1]
            vlink = self.parent.text.tag_configure('ahist', 'foreground')[-1]
            alink = self.parent.text.tag_configure('atemp', 'foreground')[-1]
            hover_fg = self.parent.text.tag_configure(
                'hover', 'foreground')[-1]
            hover_un = self.parent.text.tag_configure(
                'hover', 'underline')[-1]
            self.text.tag_configure('a', foreground=link)
            self.text.tag_configure('ahist', foreground=vlink)
            self.text.tag_configure('atemp', foreground=alink)
            self.text.tag_configure(
                'hover', foreground=hover_fg, underline=hover_un)
        self.configure_tags_fixed()
        if self.context.viewer is self:
            self.text.config(takefocus=1)
        self.text.bind("<Tab>", self.tab_event)
        self.text.bind("<Shift-Tab>", self.shift_tab_event)
        self.text.bind("<Button-1>", self.button_1_event)
        self.text.bind("<Button-2>", self.button_2_event)
        self.text.bind("<Button-3>", self.button_3_event)
        self.frame.bind("<Button-3>", self.button_3_event)

    def init_presentation(self):
        self.SHOW_TITLES = self.prefs.GetBoolean(
            'presentation',  'show-link-titles')
        self.hovering_enabled = self.prefs.GetBoolean(
            'presentation', 'hover-on-links')

    def configure_presentation(self):
        self.init_presentation()
        if self.hovering_enabled:
            foreground = self.prefs.Get(
                'presentation', 'hover-foreground')
            underline = self.prefs.Get(
                'presentation', 'hover-underline')
        else:
            foreground = underline = None
        self.text.tag_configure(
            'hover', foreground=foreground, underline=underline)

    def init_styles(self):
        self.configure_styles(new_styles=1)

    def configure_styles(self, new_styles=0):
        """Used on widget creation, clear, and as a callback when style
        preferences change."""
        try:
            self.configure_tags(self.stylesheet)
        except UndefinedStyle:
            pass
        self.configure_presentation()

    def configure_tags(self, stylesheet):
        if self.text:
            if not self.parent:
                # Top level viewer.
                current_cursor = self.current_cursor
                self.set_cursor(CURSOR_WAIT)

            self.text.config(stylesheet.default)
            use_font_dingbats = 1
            # Build tags that we might be using or have special semantics;
            # other font tags will be configured dynamically.
            for tag in ['_ding'] + self.__fonttags_built.keys():
                try:
                    self.configure_fonttag(tag)
                except TclError, err:
                    # This extra logic is needed to switch to gif-based
                    # dingbats if the font is not available in the current
                    # size.
                    if tag == '_ding':
                        use_font_dingbats = 0
                    else:
                        raise TclError, err, sys.exc_traceback
                else:
                    if tag == '_ding':
                        try:
                            fontname = self.stylesheet.styles[tag]['font']
                            fontname = self.text.tk.call('font', 'create',
                                                         '-family', fontname)
                            if string.find(fontname, 'dingbat') == -1:
                                use_font_dingbats = 0
                        except TclError:
                            pass            # pre-8.0 Tk
            #
            # Set dingbat approach appropriately:
            #
            if use_font_dingbats:
                for name, value in font_dingbats.items():
                    self.context.app.set_dingbat(name, value)
            else:
                map(self.context.app.clear_dingbat, font_dingbats.keys())
            #
            for tag, cnf in stylesheet.history.items():
                self.text.tag_configure(tag, cnf)
            self.text.tag_add('hover', '0.1', '0.1')
            self.text.tag_raise('ahist', 'a')
            self.text.tag_raise('hover', 'ahist')
            self.text.tag_raise('atemp', 'hover')

            if not self.parent:
                self.resize_event()
                self.set_cursor(current_cursor)
            self.init_presentation()

    def configure_tags_fixed(self):
        # These are used in aligning block-level elements:
        self.text.tag_configure('right', justify='right')
        self.text.tag_configure('center', justify='center')
        #  Typographic controls:
        self.text.tag_configure('pre', wrap='none')
        self.text.tag_configure('underline', underline=1)
        self.text.tag_configure('overstrike', overstrike=1)
        self.text.tag_configure('red', foreground='red')
        self.text.tag_configure('ins', foreground='darkgreen')
        # Configure margin tags
        for level in range(1, 20):
            pix = level * INDENTATION_WIDTH
            self.text.tag_configure('margin_%d' % level,
                                    lmargin1=pix, lmargin2=pix)
            self.text.tag_configure('rightmargin_%d' % level, rmargin=pix)
            tabs = "%d right %d left" % (pix-5, pix)
            self.text.tag_configure('label_%d' % level,
                                    lmargin1=pix-INDENTATION_WIDTH, tabs=tabs)
        # Configure anchor tags
        for tag in 'a', 'ahist':
            self.text.tag_bind(tag, '<ButtonPress-1>', self.anchor_press)
            self.text.tag_bind(tag, '<Shift-ButtonPress-1>',
                               self.shift_anchor_press)
            self.text.tag_bind(tag, '<ButtonPress-2>', self.anchor_press)
            self.text.tag_bind(tag, '<ButtonRelease-1>', self.anchor_click)
            self.text.tag_bind(tag, '<ButtonRelease-2>', self.anchor_click_new)
            self.text.tag_bind(tag, '<Leave>', self.anchor_leave)

    def configure_fonttag(self, tag):
        # configure a single font
        if self.__fonttags_built is None:
            self.__fonttags_built = {}
        self.__fonttags_built[tag] = tag
        apply(self.text.tag_configure, (tag,), self.stylesheet.styles[tag])

    def bind_anchors(self, tag):
        # Each URL must have a separate binding so moving between
        # adjacent anchors updates the URL shown in the feedback area
        self.text.tag_bind(tag, '<Enter>', self.anchor_enter)
        # XXX Don't tag bindings need to be garbage-collected?

    def register_interest(self, interests, func):
        interests.append(func)

    def unregister_interest(self, interests, func):
        found = -1
        for i in range(len(interests)):
            if interests[i] == func:
                found = i
        if found < 0:
            print "resize interest", func, "not registered"
            return
        del interests[found]

    def register_reset_interest(self, func):
        self.register_interest(self.reset_interests, func)

    def unregister_reset_interest(self, func):
        self.unregister_interest(self.reset_interests, func)

    def register_resize_interest(self, func):
        self.register_interest(self.resize_interests, func)

    def unregister_resize_interest(self, func):
        self.unregister_interest(self.resize_interests, func)

    def clear_reset(self):
        self._atemp = []
        for func in self.reset_interests[:]:
            func(self)
        # XXX Eventually the following code should be done using interests too
        subwindows = self.subwindows + self.rules
        subviewers = self.subviewers
        self.subwindows = []
        self.rules = []
        self.subviewers = []
        for viewer in subviewers:
            viewer.close()
        for w in subwindows:
            w.destroy()
        if self.text:
            self.pendingdata = ''
            self.unfreeze()
            self.text.delete('1.0', END)
            self.freeze()
            self.text.config(background=self.default_bg,
                             foreground=self.default_fg)
            self.configure_styles()
            self.reset_state()

    def tab_event(self, event):
        w = self.text.tk_focusNext()
        if w:
            w.focus_set()
        return 'break'

    def shift_tab_event(self, event):
        w = self.text.tk_focusPrev()
        if w:
            w.focus_set()
        return 'break'

    def button_1_event(self, event):
        self.context.viewer.text.focus_set()
        self.current_index = self.text.index(CURRENT) # For anchor_click

    def button_2_event(self, event):
        self.current_index = self.text.index(CURRENT) # For anchor_click_new

    def button_3_event(self, event):
        url = self.find_tag_url()
        if url:
            url, target = self.split_target(self.context.get_baseurl(url))
            self.add_temp_tag()
        self.open_popup_menu(event, link_url=url)

    def open_popup_menu(self, event, link_url=None, image_url=None):
        if not self.popup_menu:
            self.popup_menu = ViewerMenu(self.text, self)
        self.popup_menu.set_link_url(link_url)
        self.popup_menu.set_image_url(image_url)
        self.popup_menu.tk_popup(event.x_root, event.y_root)

    def resize_event(self, event=None):
        for func in self.resize_interests:
            func(self)

    def resize_rules(self):
        for rule in self.rules:
            rule["width"] = rule.get_width()

    def unfreeze(self):
        self.text['state'] = NORMAL

    def freeze(self, update=0):
        if self.pendingdata and strip(self.pendingdata):
            self.text.insert(END, self.pendingdata, self.flowingtags)
            self.pendingdata = ''
        if self.smoothscroll:
            from supertextbox import resize_super_text_box
            resize_super_text_box(frame=self.frame)
        self.text['state'] = DISABLED
        if update:
            self.text.update_idletasks()

    def flush(self):
        if self.pendingdata:
            self.text.insert(END, self.pendingdata, self.flowingtags)
            self.pendingdata = ''

    def scroll_page_down(self, event=None):
        self.text.tk.call('tkScrollByPages', self.text.vbar, 'v', 1)

    def scroll_page_up(self, event=None):
        self.text.tk.call('tkScrollByPages', self.text.vbar, 'v', -1)

    def scroll_line_down(self, event=None):
        self.text.tk.call('tkScrollByUnits', self.text.vbar, 'v', 1)

    def scroll_line_up(self, event=None):
        self.text.tk.call('tkScrollByUnits', self.text.vbar, 'v', -1)

    def new_tags(self, doit_now = 0):
        if self.pendingdata and strip(self.pendingdata):
            self.text.insert(END, self.pendingdata, self.flowingtags)
            self.pendingdata = ''
        self.flowingtags = filter(
            None,
            (self.align, self.fonttag, self.margintag, self.rightmargintag,
             self.spacingtag) + self.addtags)

    # AbstractWriter methods

    def new_alignment(self, align):
##      print "New alignment:", align
        if align == 'left': align = None
        self.align = align
        self.new_tags()

    def new_font(self, font):
##      print "New font:", font
        if font:
            tag = self.make_fonttag(font)
        else:
            tag = None
        if tag != self.fonttag:
            self.flush()
            self.fonttag = tag
        self.new_tags()

    def make_fonttag(self, (tag, i, b, tt)):
        tag = tag or ''
        if tt: tag = tag + '_tt'
        if b: tag = tag + '_b'
        if i: tag = tag + '_i'
        if tag:
            if self.__fonttags_built and self.__fonttags_built.has_key(tag):
                return tag
            self.configure_fonttag(tag)
        return tag or None

    def get_font(self, spec=None):
        tag = self.fonttag
        if spec:
            tag = self.make_fonttag(spec)
        if tag:
            font = self.text.tag_cget(tag, '-font')
        else:
            font = self.text["font"]
        return font

    def new_margin(self, margin, level):
##      print "New margin:", margin, level
        self.marginlevel = level
        self.margintag = level and ('margin_%d' % level)
        self.new_tags()

    def new_spacing(self, spacing):
        self.spacingtag = spacingtag
        self.new_tags()

    def new_styles(self, styles):
##      print 'New styles:', styles
        self.addtags = styles
        if self.pendingdata:
            self.text.insert(END, self.pendingdata, self.flowingtags)
            self.pendingdata = ''
        self.rightmarginlevel = rl = map(None, styles).count('blockquote')
        self.rightmargintag = rl and ('rightmargin_%d' % rl) or None
        self.flowingtags = filter(
            None,
            (self.align, self.fonttag, self.margintag, self.rightmargintag,
             self.spacingtag) + styles)

    def send_paragraph(self, blankline):
        self.pendingdata = self.pendingdata + ('\n' * blankline)
##      self.text.update_idletasks()

    def send_line_break(self):
        self.pendingdata = self.pendingdata + '\n'
##      self.text.update_idletasks()

    def width_magic(self, abswidth, percentwidth):
        return WidthMagic(self, abswidth, percentwidth)

    def send_hor_rule(self, abswidth=None, percentwidth=1.0,
                      height=None, align=None):
        window = HRule(self, abswidth, percentwidth, height=(height or 0))
        self.rules.append(window)
        self.prepare_for_insertion(align)
        self.add_subwindow(window)
        del self.subwindows[-1]
        self.send_line_break()
##      self.text.update_idletasks()

    RULE_WIDTH_MAGIC = 10

    def send_label_data(self, data):
##      print "Label data:", `data`
        tags = self.flowingtags + ('label_%d' % self.marginlevel,)
        data_type = type(data)
        if data_type is StringType:
            self.text.insert(END, self.pendingdata, self.flowingtags,
                             '\t%s\t' % data, tags)
            self.pendingdata = ''
        elif data_type is TupleType:
            #  (string, fonttag) pair
            data, fonttag = data
            if fonttag:
                self.text.insert(END, self.pendingdata, self.flowingtags,
                                 '\t', tags,
                                 data, tags + (fonttag,))
                self.text.tag_raise(fonttag)
                self.pendingdata = '\t'
            else:
                self.text.insert(END, self.pendingdata, self.flowingtags,
                                 '\t%s\t' % data, tags)
                self.pendingdata = ''
        elif data_type is InstanceType:
            #  Some sort of image specified by DINGBAT or SRC
            self.text.insert(END, self.pendingdata, self.flowingtags,
                             '\t', tags)
            self.pendingdata = ''
            window = Label(self.text, image = data,
                           background = self.text['background'],
                           borderwidth = 0)
            self.add_subwindow(window, align=BASELINE)
            self.pendingdata = '\t'

    def send_flowing_data(self, data):
##      print "Flowing data:", `data`, self.flowingtags
        self.pendingdata = self.pendingdata + data

    def send_literal_data(self, data):
##      print "Literal data:", `data`, self.flowingtags + ('pre',)
        self.text.insert(END, self.pendingdata, self.flowingtags,
                         data, self.flowingtags + ('pre',))
        self.pendingdata = ''

    # Viewer's own methods

    SHOW_TITLES = 0
    def anchor_enter(self, event):
        tagurl = self.find_tag_url()
        url, target = self.split_target(tagurl)
        message = ''
        if url:
            if self.SHOW_TITLES:
                absurl = self.context.get_baseurl(url)
                ghist = self.context.app.global_history
                title, when = ghist.lookup_url(absurl)
                if title:
                    message = string.join(string.split(title))
            self.text.tag_remove('hover', '0.1', END)
            ranges = self.find_tag_ranges()
            split = string.split
            point = map(int, split(self.text.index(CURRENT), '.'))
            for start, end in ranges:
                startv = map(int, split(start, '.'))
                endv = map(int, split(end, '.'))
                if startv <= point < endv:
                    self.text.tag_add('hover', start, end)
                    break
        if not message:
            url = url or "???"
            if not target:
                target = self.context.get_target()
            if target:
                message = "%s in %s" % (url, target)
            else:
                message = url
        self.enter_message(message)

    def enter_message(self, message):
        self.linkinfo = message
        self.status.set(message)
        self.context.browser.messagevariable(self.status)
        self.set_cursor(CURSOR_LINK)

    def anchor_leave(self, event):
        self.text.tag_remove('hover', '1.0', END)
        self.leave_message()

    def leave_message(self):
        self.linkinfo = ""
        self.context.message_clear()

    def anchor_press(self, event):
        self._shifted = 0
        self.context.viewer.text.focus_set()
        self.current_index = self.text.index(CURRENT) # For anchor_click
        url = self.find_tag_url()
        if url:
            self.add_temp_tag()

    def shift_anchor_press(self, event):
        self.anchor_press(event)
        self._shifted = 1

    def anchor_click(self, event):
        here = self.text.index("@%d,%d" % (event.x, event.y))
        if self.current_index != here:
            self.remove_temp_tag()
            return
        url = self.find_tag_url()
        if url:
            self.linkinfo = ""
            url, target = self.split_target(self.context.get_baseurl(url))
            if self._shifted:
                self.remove_temp_tag(
                    histify=self.context.save_document(url))
            else:
                self.context.follow(url, target)

    def anchor_click_new(self, event):
        here = self.text.index("@%d,%d" % (event.x, event.y))
        if self.current_index != here:
            return
        url = self.find_tag_url()
        if url:
            url, target = self.split_target(url)
            self.master.update_idletasks()
            from Browser import Browser
            app = self.context.app
            b = Browser(app.root, app)
            b.context.load(self.context.get_baseurl(url))
            self.remove_temp_tag(histify=1)

    def split_target(self, url):
        i = string.find(url, TARGET_SEPARATOR)
        if i < 0: return url, ""
        return url[:i], url[i+1:]

    def add_temp_tag(self):
        list = self.find_tag_ranges()
        if list:
            self._atemp = list
            for (start, end) in list:
                self.text.tag_add('atemp', start, end)

    def remove_temp_tag(self, histify=0):
        for (start, end) in self._atemp:
            self.text.tag_remove('atemp', start, end)
        if histify:
            for (start, end) in self._atemp:
                self.text.tag_add('ahist', start, end)
        self._atemp = []

    def find_tag_ranges(self):
        for tag in self.text.tag_names(CURRENT):
            if tag[0] == '>':
                raw = self.text.tag_ranges(tag)
                list = []
                for i in range(0, len(raw), 2):
                    list.append((raw[i], raw[i+1]))
                return list
        return ()

    def find_tag_url(self):
        for tag in self.text.tag_names(CURRENT):
            if tag[0] == '>':
                return tag[1:]

    def find_tag_label(self):
        for tag in self.text.tag_names(CURRENT):
            if tag[0] == '#':
                return tag[1:]

    def get_cursor(self):
        return self.current_cursor

    def set_cursor(self, cursor):
        if cursor != self.current_cursor:
            self.text['cursor'] = self.current_cursor = cursor
            if cursor == CURSOR_WAIT:
                self.text.update_idletasks()

    def scrollpos(self): return self.text.index('@0,0')
    def scroll_to_position(self, pos): self.text.yview(pos)

    def clear_targets(self):
        targs = self.targets.keys()
        if targs:
            apply(self.text.mark_unset, tuple(targs))

    def add_target(self, fragment):
        if self.pendingdata:
            self.text.insert(END, self.pendingdata, self.flowingtags)
            self.pendingdata = ''
        self.text.mark_set(fragment, END + ' - 1 char')
        self.text.mark_gravity(fragment, 'left')
        self.targets[fragment] = 1

    def scroll_to(self, fragment):
        fragment = '#' + fragment
        if self.targets.has_key(fragment):
            r = self.text.tag_nextrange(fragment, '1.0')
            if not r:
                #  Maybe an empty target; try the mark database:
                try:
                    first = self.text.index(fragment)
                except TclError:
                    return              # unknown mark
                #  Highlight the entire line:
                r = (first,
                     `1 + string.atoi(string.splitfields(first,'.')[0])` \
                     + '.0')
        else:
            r = self.parse_range(fragment)
            if not r:
                return
        first, last = r
        self.text.yview(first)
        self.text.tag_remove(SEL, '1.0', END)
        self.text.tag_add(SEL, first, last)

    def clear_selection(self):
        self.text.tag_remove(SEL, '1.0', END)

    def parse_range(self, fragment):
        try:
            p = self.range_pattern
        except AttributeError:
            import regex
            p = regex.compile('#\([0-9]+\.[0-9]+\)-\([0-9]+\.[0-9]+\)')
            self.range_pattern = p
        if p.match(fragment) == len(fragment):
            return p.group(1, 2)
        else:
            return None

    def prepare_for_insertion(self, align=None):
        if align:
            if align == 'left':
                align = None
        else:
            align = self.align
        prev_align, self.align = self.align, align
        self.new_tags()
        self.pendingdata = self.pendingdata + MIN_IMAGE_LEADER
        self.align = prev_align
        self.new_tags()

    def add_subwindow(self, window, align=CENTER, index=END):
        if self.pendingdata:
            self.text.insert(END, self.pendingdata, self.flowingtags)
            self.pendingdata = ''
        window.bind("<Button-3>", self.button_3_event)
        self.subwindows.append(window)
        self.text.window_create(index, window=window, align=align)

    def add_subviewer(self, subviewer):
        self.flush()
        self.subviewers.append(subviewer)

    def remove_subviewer(self, subviewer):
        if subviewer in self.subviewers:
            self.subviewers.remove(subviewer)

    def make_subviewer(self, master, name="", scrolling="auto"):
        depth = 0
        v = self
        while v:
            depth = depth + 1
            v = v.parent
        if depth > 5:
            return None                 # Ridiculous nesting
        viewer = Viewer(master=master,
                        browser=self.context.browser,
                        stylesheet=self.stylesheet,
                        name=name,
                        scrolling=scrolling,
                        parent=self)
        viewer.context.set_baseurl(self.context.get_baseurl())
        return viewer

    def find_subviewer(self, name):
        if self.name == name:
            return self
        for v in self.subviewers:
            v = v.find_subviewer(name)
            if v:
                return v

    def find_parentviewer(self):
        return self.parent


class ViewerMenu:
    __have_link = 0
    __have_image = 0
    __link_url = None
    __image_url = None
    __image_file = ""
    __image_prev = None

    def __init__(self, master, viewer):
        self.__menu = menu = Menu(master, tearoff=0)
        self.__context = context = viewer.context
        self.__viewer = viewer
        menu.add_command(label="Back in Frame", command=context.go_back)
        menu.add_command(label="Forward in Frame",
                         command=context.go_forward)
        menu.add_command(label="Reload Frame",
                         command=context.reload_page)
        menu.add_command(label="Open Frame in New Window",
                         command=self.__open_in_new)
        menu.add_separator()
        menu.add_command(label="Frame History...",
                         command=context.show_history_dialog)
        menu.add_separator()
        menu.add_command(label="View Frame Source",
                         command=context.view_source)
        import DocumentInfo
        menu.add_command(label="Document Info...",
                         command=DocumentInfo.DocumentInfoCommand(viewer))
        self.__source_item = menu.index(END)
        menu.add_command(label="Print Frame...",
                         command=context.print_document)
        menu.add_command(label="Save Frame As...",
                         command=context.save_document)
        self.__last_standard_index = menu.index(END)
        menu.bind("<Unmap>", self.__unmap)

    def tk_popup(self, x, y):
        # update the "Forward in Frame" item
        context = self.__context
        future, page = context.history.peek(+1)
        self.__menu.entryconfig(1, state=(page and NORMAL or DISABLED))
        #
        # update the "Back in Frame" item
        viewer = self.__viewer
        future, page = context.history.peek(-1)
        while viewer.parent and not page:
            viewer = viewer.parent
            context = viewer.context
            future, page = context.history.peek(-1)
        self.__menu.entryconfig(0, state=(page and NORMAL or DISABLED))
        #
        # update the "Open Frame in New Window" item
        # (disable if there is no parent frame)
        parent = self.__context.viewer.parent
        self.__menu.entryconfig(3, state=(parent and NORMAL or DISABLED))
        #
        need_link = self.__link_url and 1 or 0
        need_image = self.__image_url and 1 or 0
        if (need_link != self.__have_link
            or need_image != self.__have_image
            or self.__image_prev != self.__image_file):
            if self.__have_link or self.__have_image:
                self.__menu.delete(self.__last_standard_index + 1, END)
                self.__have_link = self.__have_image = 0
            if need_link:
                self.__add_link_items()
            if need_image:
                self.__add_image_items()
            self.__menu.update_idletasks()
        self.__menu.tk_popup(x, y)

    def set_link_url(self, url):
        self.__link_url = url or None

    def set_image_url(self, url):
        self.__image_url = url or ""
        url = self.__context.get_baseurl(url)
        if len(url) < 5 or string.lower(url[:5]) != "data:":
            from posixpath import basename
            self.__image_file = basename(urlparse(url)[2])
        else:
            self.__image_file = ""

    def __add_image_items(self):
        self.__have_image = 1
        self.__menu.add_separator()
        self.__image_prev = self.__image_file
        self.__menu.add_command(label="Open Image " + self.__image_file,
                                command=self.__open_image)
        self.__menu.add_command(label="Save Image %s..." % self.__image_file,
                                command=self.__save_image)
        self.__menu.add_command(label="Copy Image Location",
                                command=self.__select_image_url)

    def __add_link_items(self):
        self.__have_link = 1
        self.__menu.add_separator()
        self.__menu.add_command(label="Bookmark Link",
                                command=self.__bkmark_link)
        self.__menu.add_command(label="Print Link...",
                                command=self.__print_link)
        self.__menu.add_command(label="Save Link As...",
                                command=self.__save_link)
        self.__menu.add_command(label="Copy Link Location",
                                command=self.__select_link_url)

    __selection = ''
    def __select_image_url(self, event=None):
        self.__select(self.__context.get_baseurl(self.__image_url))

    def __select_link_url(self, event=None):
        self.__select(self.__context.get_baseurl(self.__link_url))

    def __select(self, selection):
        self.__selection = selection
        self.__viewer.text.selection_handle(self.__selection_handler)
        self.__viewer.text.selection_own()

    def __selection_handler(self, offset, maxbytes):
        offset = string.atoi(offset)
        maxbytes = string.atoi(maxbytes)
        endpos = min(maxbytes + offset, len(self.__selection))
        return self.__selection[offset:endpos]

    def __bkmark_link(self, event=None):
        try:
            bmarks = self.__context.browser.app.bookmarks_controller
        except AttributeError:
            import BookmarksGUI
            bmarks = BookmarksGUI.BookmarksController(
                self.__context.browser.app)
            self.__context.browser.app.bookmarks_controller = bmarks
        bmarks.add_link(self.__context.get_baseurl(self.__link_url))

    def __print_link(self, event=None):
        context = self.__copy_context()
        context.print_document()
        context.browser.remove()

    def __save_link(self, event=None):
        self.__viewer.remove_temp_tag(
            histify=self.__context.save_document(self.__link_url))

    def __open_image(self, event=None):
        self.__context.follow(self.__image_url)

    def __save_image(self, event=None):
        self.__context.save_document(self.__image_url)

    class DummyBrowser:
        context = None

        def __init__(self, browser):
            self.app = browser.context.app
            self.root = browser.root
            self.master = browser.master
            # this is the really evil part:
            #app.browsers.append(self)

        def remove(self):
            # remove faked out connections to other objects
            #self.app.browsers.remove(self)
            self.context = self.app = self.root = None

        def message(self): pass

    def __copy_context(self, url=None):
        # copy context and set to link target
        br = self.DummyBrowser(self.__context.browser)
        context = SimpleContext(self.__context.viewer, br)
        context._url = context._baseurl = url or self.__link_url
        br.context = context
        return context

    def __unmap(self, event=None):
        if self.__link_url:
            self.__viewer.remove_temp_tag()

    def __open_in_new(self, event=None):
        from Browser import Browser
        url = self.__context.get_url()
        b = Browser(self.__context.browser.master, self.__context.app)
        b.context.load(url)


def test():
    """Test the Viewer class."""
    import sys
    file = "Viewer.py"
    if sys.argv[1:]: file = sys.argv[1]
    f = open(file)
    data = f.read()
    f.close()
    root = Tk()
    v = Viewer(root, None)
    v.handle_data(data)
    root.mainloop()


if __name__ == '__main__':
    test()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.