#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
# ---------------------------------------------------------------------------#
#
# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
# Copyright (C) 2003 Mt. Hood Playing Card Co.
# Copyright (C) 2005-2009 Skomoroh
#
# 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 3 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, see .
#
# ---------------------------------------------------------------------------#
# imports
import os
import sys
# import htmllib
import pysollib.htmllib2 as htmllib
import formatter
# PySol imports
from pysollib.mygettext import _
from pysollib.mfxutil import Struct, openURL
from pysollib.settings import TITLE
from pysollib.kivy.LApp import LTopLevel
from pysollib.kivy.LApp import LScrollView
from pysollib.kivy.LApp import LPopCommander
from pysollib.kivy.LApp import get_platform
from pysollib.pysoltk import MfxMessageDialog
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
REMOTE_PROTOCOLS = ("ftp:", "gopher:", "http:", "mailto:", "news:", "telnet:")
# ************************************************************************
# *
# ************************************************************************
def cmp2(a, b):
"""python 3 replacement for python 2 cmp function"""
return (a > b) - (a < b)
class tkHTMLWriter(formatter.NullWriter):
def __init__(self, text, viewer, app):
formatter.NullWriter.__init__(self)
self.text = text
self.viewer = viewer
#
if app:
font = app.getFont("sans")
fixed = app.getFont("fixed")
else:
font = ('helvetica', 12)
fixed = ('courier', 12)
size = font[1]
sign = 1
if size < 0:
sign = -1
self.fontmap = {
"h1": (font[0], size + 12 * sign, "bold"),
"h2": (font[0], size + 8 * sign, "bold"),
"h3": (font[0], size + 6 * sign, "bold"),
"h4": (font[0], size + 4 * sign, "bold"),
"h5": (font[0], size + 2 * sign, "bold"),
"h6": (font[0], size + 1 * sign, "bold"),
"bold": (font[0], size, "bold"),
"italic": (font[0], size, "italic"),
"pre": fixed,
}
self.text.config(cursor=self.viewer.defcursor, font=font)
for f in self.fontmap.keys():
self.text.tag_config(f, font=self.fontmap[f])
self.anchor = None
self.anchor_mark = None
self.font = None
self.font_mark = None
self.indent = ""
self.text.label.bind(on_ref_press=self.refpress)
'''
def createCallback(self, href):
class Functor:
def __init__(self, viewer, arg):
self.viewer = viewer
self.arg = arg
def __call__(self, *args):
self.viewer.updateHistoryXYView()
return self.viewer.display(self.arg)
return Functor(self.viewer, href)
'''
def write(self, data):
# print('writer: write %s' % data)
self.text.insert("insert", data)
def anchor_bgn(self, href, name, type):
# print('writer: anchor_bgn %s - %s' % (href, name))
if href:
# self.text.update_idletasks() # update display during parsing
self.anchor = (href, name, type)
self.anchor_mark = self.text.index("insert")
self.write('[ref=' + href + ']')
url = self.anchor[0]
fg = '0000cc'
u = self.viewer.normurl(url, with_protocol=False)
if u in self.viewer.visited_urls:
fg = '660099'
self.write('[color=' + fg + '][i]')
# self.text.tag_config(tag, foreground=fg, underline=1)
def refpress(self, instance, value):
# print('writer: refpress %s, %s' % (instance, value))
pass
def anchor_end(self):
# print('writer: anchor_end')
if self.anchor:
self.anchor = None
self.write('[/i][/color]')
self.write('[/ref]')
def anchor_enter(self, url):
url = self.viewer.normurl(url)
self.viewer.statusbar.updateText(url=url)
self.text.config(cursor=self.viewer.handcursor)
def anchor_leave(self, *args):
self.viewer.statusbar.updateText(url='')
self.text.config(cursor=self.viewer.defcursor)
def new_font(self, font):
# print('writer: new_font %s' % str(font))
# end the current font
if self.font:
# print "end_font(%s)" % `self.font`
self.text.tag_add(self.font, self.font_mark, "insert")
self.font = None
# start the new font
if font:
# print "start_font(%s)" % `font`
self.font_mark = self.text.index("insert")
if font[0] in self.fontmap:
self.font = font[0]
elif font[3]:
self.font = "pre"
elif font[2]:
self.font = "bold"
elif font[1]:
self.font = "italic"
else:
self.font = None
def new_margin(self, margin, level):
# print('writer: new_margin %s, %s' % (margin, level))
self.indent = " " * level
def send_label_data(self, data):
# print('writer: send_label_data %s' % (data))
# self.write(self.indent + data + " ")
self.write(self.indent)
if data == '*': #
img = self.viewer.symbols_img.get('disk')
if img:
self.text.image_create(index='insert', image=img,
padx=0, pady=0)
else:
self.write('*')
else:
self.write(data)
self.write(' ')
def send_paragraph(self, blankline):
# print('writer: send_paragraph %s' % (blankline))
self.write('\n' * blankline)
def send_line_break(self):
# print('writer: send_break')
self.write('\n')
def send_hor_rule(self, *args):
if (args):
pass
# print('writer: send_hor_rule %s' % (args))
# width = int(int(self.text["width"]) * 0.9)
width = 20
self.write("_" * width)
self.write("\n")
def send_literal_data(self, data):
# print('writer: send_literal_data %s' % (data))
self.write(data)
def send_flowing_data(self, data):
# print('writer: send_flowing_data %s' % (data))
self.write(data)
# ************************************************************************
# *
# ************************************************************************
class tkHTMLParser(htmllib.HTMLParser):
def anchor_bgn(self, href, name, type):
self.formatter.flush_softspace()
htmllib.HTMLParser.anchor_bgn(self, href, name, type)
self.formatter.writer.anchor_bgn(href, name, type)
def close(self):
# print('tkHTMLParser1: close()')
self.formatter.writer.text.applyBuffer()
# label = self.formatter.writer.text.label
# print('tkHTMLParser: label.refs %s' % str(label.refs))
# print ('tkHTMLParser: label.refs %s' % str(Label.refs))
# print('tkHTMLParser2: close()')
htmllib.HTMLParser.close(self)
def anchor_end(self):
if self.anchor:
self.anchor = None
self.formatter.writer.anchor_end()
def do_dt(self, attrs):
self.formatter.end_paragraph(1)
self.ddpop()
def handle_image(self, src, alt, ismap, align, width, height):
self.formatter.writer.viewer.showImage(
src, alt, ismap, align, width, height)
# ************************************************************************
# *
# ************************************************************************
class HTMLButton(Button):
def __init__(self, **kw):
super(HTMLButton, self).__init__(**kw)
def config(self, **kw):
pass
class HTMLLabel(Label):
def __init__(self, **kw):
super(HTMLLabel, self).__init__(**kw)
self.bind(size=self.onUpdate)
self.bind(pos=self.onUpdate)
self.bind(text=self.onUpdate)
def onUpdate(self, instance, size):
self.size_hint_y = None
self.text_size = self.width, None
self.texture_update()
self.height = self.texture_size[1]
class HTMLText(LScrollView, LPopCommander):
def __init__(self, **kw):
super(HTMLText, self).__init__(**kw)
self.label = HTMLLabel(text='', markup=True)
self.tags = {}
self.textbuffer = ''
self.add_widget(self.label)
def applyBuffer(self):
# print('applybuffer:')
self.label.text = self.textbuffer
def config(self, **kw):
# print('config: %s' % kw)
pass
def update_idletasks(self):
pass
def delete(self, val, val1):
pass
def insert(self, cmd, data):
# print('insert text: %s' % data)
self.textbuffer = self.textbuffer + data
# self.label.text = self.textbuffer
pass
def index(self, cmd):
# print('index: %s' % cmd)
# was sollen wir hier zuruckgeben ?
return 0
def tag_add(self, font, fontmark, cmd):
# print('tag_add: %s, %s, %s' % (font, fontmark, cmd))
pass
def tag_config(self, tag, **kw):
# print('tag_config: %s, %s' % (tag, kw))
# self.tags[tag] = kw
# for t, k in self.tags:
# print('tagslist: %s, %s' % (t, k))
pass
def xview_moveto(self, xview):
# print('xview_moveto: %s' % xview)
pass
def yview_moveto(self, yview):
# print('yview_moveto: %s' % yview)
pass
class HTMLViewer:
symbols_fn = {} # filenames, loaded in Application.loadImages3
symbols_img = {}
def make_pop_command(self, parent, title):
def pop_command(event):
if self.history.index > 1:
self.goBack(event)
return None
del self.history.list
self.history.index = 0
parent.popWork(title)
return pop_command
def make_close_command(self, parent, title):
def close_command(event):
del self.history.list
self.history.index = 0
parent.popWork(title)
return close_command
def refpress(self, instance, value):
# print('writer: refpress %s, %s' % (instance, value))
self.updateHistoryXYView()
return self.display(value)
def __init__(self, parent, app=None, home=None):
self.parent = parent
self.app = app
self.home = home
self.url = None
self.history = Struct(
list=[],
index=0,
)
self.visited_urls = []
self.images = {}
# need to keep a reference because of garbage collection
self.defcursor = "default"
# self.defcursor = parent["cursor"]
# self.defcursor = 'xterm'
self.handcursor = "hand2"
self.title = "Browser"
self.window = None
self.running = False
# prüfen ob noch aktiv.
if parent.workStack.peek(self.title) is not None:
parent.popWork(self.title)
pc = self.make_pop_command(parent, self.title)
cc = self.make_close_command(parent, self.title)
# neuen Dialog aufbauen.
window = LTopLevel(app.top, self.title, size_hint=(1.8, 1.0))
window.titleline.bind(on_press=cc)
self.parent.pushWork(self.title, window)
self.window = window
self.running = True
content = BoxLayout(orientation='vertical')
# buttonline =
# BoxLayout(orientation='horizontal', size_hint=(1.0, 0.1))
# create buttons
self.homeButton = HTMLButton(text="Index", on_release=self.goHome)
self.backButton = HTMLButton(text="Back", on_release=self.goBack)
self.forwardButton = HTMLButton(
text="Forward", on_release=self.goForward)
self.closeButton = HTMLButton(text="Close", on_release=self.goHome)
'''
buttonline.add_widget(self.homeButton)
buttonline.add_widget(self.backButton)
buttonline.add_widget(self.forwardButton)
buttonline.add_widget(self.closeButton)
content.add_widget(buttonline)
'''
'''
self.homeButton = Tkinter.Button(parent, text=_("Index"),
width=button_width,
command=self.goHome)
self.homeButton.grid(row=0, column=0, sticky='w')
self.backButton = Tkinter.Button(parent, text=_("Back"),
width=button_width,
command=self.goBack)
self.backButton.grid(row=0, column=1, sticky='w')
self.forwardButton = Tkinter.Button(parent, text=_("Forward"),
width=button_width,
command=self.goForward)
self.forwardButton.grid(row=0, column=2, sticky='w')
self.closeButton = Tkinter.Button(parent, text=_("Close"),
width=button_width,
command=self.destroy)
self.closeButton.grid(row=0, column=3, sticky='e')
'''
# create text widget
self.text = HTMLText(
pop_command=pc, text="hallo", size_hint=(1.0, 1.0))
self.text.label.bind(on_ref_press=self.refpress)
content.add_widget(self.text)
'''
text_frame = Tkinter.Frame(parent)
text_frame.grid(row=1, column=0, columnspan=4, sticky='nsew')
text_frame.grid_propagate(False)
vbar = Tkinter.Scrollbar(text_frame)
vbar.pack(side='right', fill='y')
self.text = Tkinter.Text(text_frame,
fg='black', bg='white',
bd=1, relief='sunken',
cursor=self.defcursor,
wrap='word', padx=10)
self.text.pack(side='left', fill='both', expand=True)
self.text["yscrollcommand"] = vbar.set
vbar["command"] = self.text.yview
'''
self.window.content.add_widget(content)
# statusbar
# self.statusbar = HtmlStatusbar(parent, row=2, column=0, columnspan=4)
# parent.columnconfigure(2, weight=1)
# parent.rowconfigure(1, weight=1)
# load images
for name, fn in self.symbols_fn.items():
self.symbols_img[name] = self.getImage(fn)
def _yview(self, *args):
self.text.yview(*args)
return 'break'
def page_up(self, *event):
return self._yview('scroll', -1, 'page')
def page_down(self, *event):
return self._yview('scroll', 1, 'page')
def unit_up(self, *event):
return self._yview('scroll', -1, 'unit')
def unit_down(self, *event):
return self._yview('scroll', 1, 'unit')
def scroll_top(self, *event):
return self._yview('moveto', 0)
def scroll_bottom(self, *event):
return self._yview('moveto', 1)
# locate a file relative to the current self.url
def basejoin(self, url, baseurl=None, relpath=1):
if baseurl is None:
baseurl = self.url
if 0:
import urllib
url = urllib.pathname2url(url)
if relpath and self.url:
url = urllib.basejoin(baseurl, url)
else:
url = os.path.normpath(url)
if relpath and baseurl and not os.path.isabs(url):
h1, t1 = os.path.split(url)
h2, t2 = os.path.split(baseurl)
if cmp2(h1, h2) != 0:
url = os.path.join(h2, h1, t1)
url = os.path.normpath(url)
return url
def normurl(self, url, with_protocol=True):
for p in REMOTE_PROTOCOLS:
if url.startswith(p):
break
else:
url = self.basejoin(url)
if with_protocol:
if os.name == 'nt':
url = url.replace('\\', '/')
url = 'file://' + url
return url
def openfile(self, url):
if url[-1:] == "/" or os.path.isdir(url):
url = os.path.join(url, "index.html")
url = os.path.normpath(url)
if sys.version_info > (3,):
import codecs
return codecs.open(url, encoding='utf-8'), url
return open(url, "rb"), url
def display(self, url, add=1, relpath=1, xview=0, yview=0):
# for some reason we have to stop the PySol demo
# (is this a multithread problem with Tkinter ?)
if self.app and self.app.game:
self.app.game.stopDemo()
# self.app.game._cancelDrag()
# pass
# ftp: and http: would work if we use urllib, but this widget is
# far too limited to display anything but our documentation...
for p in REMOTE_PROTOCOLS:
if url.startswith(p):
plat = get_platform()
if plat == 'android':
pass
print("Open url: %s (TBD)" % url)
# import android
# tbd.
# start android webbrowser. with url
# ???
elif not openURL(url):
return
# locate the file relative to the current url
url = self.basejoin(url, relpath=relpath)
# read the file
try:
file = None
if 0:
import urllib
file = urllib.urlopen(url)
else:
file, url = self.openfile(url)
data = file.read()
file.close()
file = None
except Exception:
print("Open url(1) - Exception: %s" % url)
if file:
file.close()
'''
self.errorDialog(_("Unable to service request:\n") + url)
'''
return
self.url = url
if self.home is None:
self.home = self.url
if add:
self.addHistory(self.url, xview=xview, yview=yview)
# print self.history.index, self.history.list
if self.history.index > 1:
self.backButton.config(state="normal")
else:
self.backButton.config(state="disabled")
if self.history.index < len(self.history.list):
self.forwardButton.config(state="normal")
else:
self.forwardButton.config(state="disabled")
old_c1, old_c2 = self.defcursor, self.handcursor
self.defcursor = self.handcursor = "watch"
self.text.config(cursor=self.defcursor)
self.text.update_idletasks()
# self.frame.config(cursor=self.defcursor)
# self.frame.update_idletasks()
self.text.config(state="normal")
self.text.delete("1.0", "end")
# self.images = {}
self.text.textbuffer = ''
writer = tkHTMLWriter(self.text, self, self.app)
fmt = formatter.AbstractFormatter(writer)
parser = tkHTMLParser(fmt)
parser.feed(data)
parser.close()
self.text.config(state="disabled")
if 0.0 <= xview <= 1.0:
self.text.xview_moveto(xview)
if 0.0 <= yview <= 1.0:
self.text.yview_moveto(yview)
# self.parent.wm_title(parser.title)
self.window.titleline.text = parser.title
self.parent.wm_iconname(parser.title)
self.defcursor, self.handcursor = old_c1, old_c2
self.text.config(cursor=self.defcursor)
# self.frame.config(cursor=self.defcursor)
def addHistory(self, url, xview=0, yview=0):
if url not in self.visited_urls:
self.visited_urls.append(url)
if self.history.index > 0:
u, xv, yv = self.history.list[self.history.index - 1]
if cmp2(u, url) == 0:
self.updateHistoryXYView()
return
del self.history.list[self.history.index:]
self.history.list.append((url, xview, yview))
self.history.index = self.history.index + 1
def updateHistoryXYView(self):
if self.history.index > 0:
url, xview, yview = self.history.list[self.history.index - 1]
self.history.list[self.history.index - 1] = (url, xview, yview)
def goBack(self, *event):
if self.history.index > 1:
self.updateHistoryXYView()
self.history.index = self.history.index - 1
url, xview, yview = self.history.list[self.history.index - 1]
self.display(url, add=0, relpath=0, xview=xview, yview=yview)
def goForward(self, *event):
if self.history.index < len(self.history.list):
self.updateHistoryXYView()
url, xview, yview = self.history.list[self.history.index]
self.history.index = self.history.index + 1
self.display(url, add=0, relpath=0, xview=xview, yview=yview)
def goHome(self, *event):
if self.home and cmp2(self.home, self.url) != 0:
self.updateHistoryXYView()
self.display(self.home, relpath=0)
def errorDialog(self, msg):
MfxMessageDialog(self.parent, title=TITLE + " HTML Problem",
text=msg,
# bitmap="warning"
# FIXME: this interp don't have images
strings=(_("&OK"), ), default=0)
def getImage(self, fn):
if fn in self.images:
return self.images[fn]
else:
return None
def showImage(self, src, alt, ismap, align, width, height):
url = self.basejoin(src)
img = self.getImage(url)
if img:
self.text.image_create(index="insert", image=img, padx=0, pady=0)
# ************************************************************************
# *
# ************************************************************************
''