Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #you could try gemini://namno.duckdns.org to see how much of a mess this is
- import tkinter as tk
- from tkinter import ttk
- from tkinter import font
- import socket
- import ssl
- from urllib import parse as url
- import functools
- default_scheme="gemini"
- print(tk.TkVersion)
- class Document():
- def Raw():
- return ""
- def Type():
- return ("about","blank",None)
- def Tree():
- return None
- class Gemtext(Document):
- def __init__(self, raw, options=None):
- self.raw=raw
- self.options=options
- self.textarray=[]
- def parsehelp(l):
- if l=="":
- return (("",None),"")
- if l[0:2]=="=>":
- link=l[2:].lstrip().split(None, 2)
- if link[1]=="":
- link[1]=link[0]
- return (("=>", link[0]), link[1])
- if l[0]==">":
- level=1
- for c in l[1:]:
- if c!=">":
- break
- level+=1
- level=max(level,3)
- return ((">", level),l[level:])
- if l[0]=="#":
- level=1
- for c in l[1:]:
- if c!="#":
- break
- level+=1
- level=max(level,3)
- return (("#", level),l[level:])
- if l[0]=="*":
- level=1
- for c in l[1:]:
- if c!="*":
- break
- level+=1
- level=max(level,3)
- return (("*", level),l[level:])
- return (("", None),l)
- self.tree=[]
- preformated=False
- for l in self.raw.split("\n"):
- if l[0:3]=="```":
- preformated= not preformated
- continue
- if preformated:
- self.tree.append((("```", None),l))
- else:
- self.tree.append(parsehelp(l))
- def Raw(self):
- return self.raw
- def Type(self):
- return("text","gemini", options)
- def Tree(self):
- return self.tree
- def view_document(self, canv):#refactor into document class, expose only canvas from tab
- print("FOO")
- for i in self.textarray:
- i[0].destroy()
- try:
- i[2].destroy()
- except IndexError:
- pass
- self.textarray.clear()
- for el in D.Tree():
- if len(self.textarray)>0 and self.textarray[-1][1]==el[0]:
- self.textarray[-1][0].insert("end", "\n"+el[1],"tag")
- else:
- i=tk.Text(self.txfr, borderwidth=0, height=1, relief=tk.FLAT)
- i.insert("end", el[1],"tag")
- print(el)
- i.tag_configure("tag", cnf=self.style_helper(el[0]))
- self.textarray.append((i,el[0]))
- for i in self.textarray:
- i.configure( height=i.count("0.1","end","displaylines", "update"), state="disable")
- print("LINECOUNT:")
- print(i.count("0.1","end","displaylines", "update"))
- #i[0]["state"]="disable"
- i[0].pack(expand=1, fill="both")
- try:
- i[2].pack(expand=1, fill="x")
- except IndexError:
- pass
- class Protocols:
- p={
- "gemini":{"port":1965,"TLS":True},
- "https":{"port":443,"TLS":True}
- }
- def Get(host: str, user=None, port=None, path=None, q=None, frag=None):
- pass
- def CreateTLS(host: str, port: int, callback):
- print(host)
- print(port)
- context=ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_CLIENT)
- context.check_hostname=False
- context.verify_mode=ssl.CERT_NONE
- context.minimum_version=ssl.TLSVersion.TLSv1_3
- context.maximum_version=ssl.TLSVersion.TLSv1_3
- with socket.create_connection((host,port)) as sock:
- with context.wrap_socket(sock, server_hostname=host) as ssock:
- #TODO watch out for socket.herror, socket.gaierror and socket.timeout
- return callback(ssock)
- class gemini(Protocols):
- scheme="gemini"
- defport=1965
- def Get(host: str, user=None, port=None, path=None, q=None, frag=None):#TODO reimplement in restartable manner
- header="gemini://"+host
- #TODO make sure using ipv6 with custom port works
- if port is None:
- port=1965
- else:
- header+=":"+port
- if path is None:
- path="/"
- header+="/"+path
- if not q is None:
- header+="?"+q
- header=header.encode("utf-8")+b"\r\n"
- def cb(s):
- mime=""
- body=""
- s.sendall(header)
- r= b""
- a=s.recv(1024)#TODO add exception handling
- while(a !=b""):
- r+=a
- a=s.recv(1024)
- #if r==b"":
- #TODO
- r=[x.removesuffix(b"\r") for x in r.split(b"\n",1)]
- statusdigit1=r[0][0]-b"0"[0]
- statusdigit2=r[0][1]-b"0"[0]
- if statusdigit1==1:
- #TODO input
- raise NotImplementedError
- elif statusdigit1==2:
- #success
- mimetemp=r[0][2:].decode()
- if mimetemp=="" :#TODO factor out
- mime=("text","gemtext",None)
- else:
- mimetemp=mimetemp.split("/",1)
- if len(mimetemp)<2:
- return Document()#TODO: raise propper error
- if mimetemp[1].find(";")==-1:
- mimetemp.append([x.strip() for x in mimetemp[1].split(";")[1:] ])
- mimetemp[1]=mimetemp[1].split(";")[0]
- else:
- mimetemp.append(None)
- if mimetemp[1].find("+")==-1:
- mimetemp[1]=[x.strip() for x in mimetemp[1].split("+") ]
- else:
- mimetemp[1]=mimetemp[1].strip()
- mime=(mimetemp[0],mimetemp[1],mimetemp[2])
- del mimetemp
- #TODO refactor decoding into it's own function
- if not mime[2] is None:
- for x in mime[2]:
- if x[:8].casefold()=="charset=":
- body=r[1].decode(x.split("=",1)[1])
- break
- if body=="":
- body=r[1].decode()#no charset parameter=>UTF-8
- else:
- print("CODE "+r[0].decode()+", body "+r[1].decode())
- raise NotImplementedError
- return Gemtext(body, options=mime[2])#TODO UGLYUGLYUGLY refactor this
- return Protocols.CreateTLS(host,port, cb)#TODO rethink this interface
- protocol_table={
- gemini
- }
- document_table={
- Gemtext
- }
- class about(Protocols):
- def Get(host: str, user=None, port=None, path=None, q=None, frag=None):
- return ""
- class T:
- #def __init__(w: W, url=None):
- def __init__(self, w, url=None):
- if url is None:
- url="about:blank"
- self.textarray=[]
- self.fr=tk.Frame(w.nb)
- self.adress_var=tk.StringVar()
- self.adress=tk.Entry(self.fr, justify="left",relief="sunken",textvariable=self.adress_var)
- self.adress.pack(fill="x")
- self.adress.bind('<Return>', functools.partial(self.adress_press_enter) )
- self.canv=tk.Canvas(self.fr)
- self.canv.pack(side="left", fill="both", expand=True)
- self.scrl=tk.Scrollbar(self.fr, orient="vertical", command=self.canv.yview)
- self.scrl.pack(side="right", fill="y")
- #self.scrl.congigure(command=self.fr.yview)
- self.canv.configure(yscrollcommand=self.scrl.set)
- self.txfr=tk.Frame(self.canv)
- self.canv.create_window((0, 0), window=self.txfr, anchor="nw", tags=("txfr"))
- def handler_fix(event):
- canv=event.widget
- canv.configure(scrollregion=canv.bbox("all"))
- self.canv.bind("<Configure>", handler_fix)
- self.fr.pack(fill="both", expand=True)
- w.nb.add(self.fr, text="TODO TITLE")
- def go_adress(self, adress_str):
- parsed=url.urlsplit(adress_str)
- host=parsed.netloc
- #TODO maybe protect against ValueError?
- if not parsed.port is None:
- host=host[0:host.rfind(":")]
- for p in protocol_table:
- if parsed.scheme==p.scheme:
- #TODO this will need to be reworked wthen decoding will be reworked
- self.view_document(p.Get(
- host,
- user=parsed.username,
- port=parsed.port,
- path=parsed.path,
- q=parsed.query,
- frag=parsed.fragment
- ))
- return
- print("PROTOCOL NOT FOUND")
- def adress_press_enter(self, event):
- adress_str=self.adress_var.get()
- if not "//" in adress_str:
- adress_str=default_scheme+"://"+adress_str
- self.adress_var.set(adress_str)
- self.go_adress(adress_str)
- def close_handler(self):
- for i in self.textarray:
- i[0].destroy()
- try:
- i[2].destroy()
- except IndexError:
- pass
- self.txfr.destroy()
- self.scrl.destroy()
- self.canv.destroy()
- self.fr.destroy()
- @staticmethod
- def style_helper(S):
- r={"wrap":"word"}
- basic={
- "": { },
- "=>": {"foreground": "blue" },
- ">": {"lmargincolor": "cyan" },
- "```": {"font": "TkFixedFont" ,
- "wrap": "none" },
- "#": {"justify": "center" },
- "*": { }
- }
- #print(type(basic))
- #print(type(S))
- #print(S[0])
- for k,v in basic[S[0]].items():
- r[k]=v
- r=basic[S[0]]
- if S[0]==">" or S[0]=="*":
- r["lmargin1"]=10*S[1]
- r["lmargin2"]=10*S[1]
- if S[0]=="#" and S[1]>1:
- f=font.nametofont("TkTextFont")
- f["weight"]="bold"
- if S[1]==3:
- f["size"]=2*f["size"]
- r["font"]=f
- return r
- def view_document(self, D: Document):#refactor into document class, expose only canvas from tab
- print("FOO")
- for i in self.textarray:
- i[0].destroy()
- try:
- i[2].destroy()
- except IndexError:
- pass
- self.textarray.clear()
- for el in D.Tree():
- if len(self.textarray)>0 and self.textarray[-1][1]==el[0]:
- self.textarray[-1][0].insert("end", "\n"+el[1],"tag")
- else:
- i=tk.Text(self.txfr, borderwidth=0, height=1, relief=tk.FLAT)
- i.insert("end", el[1],"tag")
- print(el)
- i.tag_configure("tag", cnf=self.style_helper(el[0]))
- self.textarray.append((i,el[0]))
- for i in self.textarray:
- logic_lines=i[0].count("1.1", "end", "lines", return_ints=True)
- print("LOGICAL LINES="+str(logic_lines))
- for j in range(1, logic_lines+1):
- print("LINE "+str(j)+": "+str(i[0].count(
- str(j)+".1",
- str(j)+".end",
- "update",
- "chars",
- "displaychars",
- "displayindices",
- "displaylines",
- "indices")))
- i[0]["state"]="disable"
- i[0].pack(expand=1, fill="both")
- try:
- i[2].pack(expand=1, fill="x")
- except IndexError:
- pass
- class W:
- def __init__(self):
- self.w=tk.Toplevel(Manager_Instance.root)
- self.w.protocol("WM_DELETE_WINDOW", functools.partial(self.close_handler))
- self.w.geometry("640x480")
- self.nb=ttk.Notebook(self.w)
- self.nb.pack(fill="both", expand=True)
- self.tabs=[T(self)]
- def close_handler(self):
- for i in self.tabs:
- i.close_handler()
- self.nb.destroy()
- self.w.destroy()
- Manager_Instance.windows.remove(self)
- if len(Manager_Instance.windows)==0:
- Manager_Instance.root.destroy()
- class Manager:
- def __init__(self):
- self.root=tk.Tk()
- self.root.withdraw()#so that user can close windows in any order
- global Manager_Instance
- Manager_Instance=self
- self.windows=[W()]
- self.root.mainloop()
- def main():
- _=Manager()
- #Manager_Instance.windows[0].tabs[0].go_adress("gemini://namno.duckdns.org")
- if __name__=="__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment