From 2382209594ee51996fbe9caf409b39ff4a5b62f4 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sun, 4 Nov 2018 17:20:51 -0800 Subject: [PATCH 1/7] Added loading bar. It is finicky and not animating properly --- gui.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/gui.py b/gui.py index e05367d..fbd3a1e 100644 --- a/gui.py +++ b/gui.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import tkinter as tk +from tkinter import ttk import tkinter.simpledialog as dialog from connect import connect as conn from parser import parser @@ -72,6 +73,7 @@ class GUI: #status bar objects self.status_info = tk.Label(self.status_bar, textvariable=self.message_bar_content, bg=self.STATUS_BG, takefocus=0, fg=self.ACTIVELINK) + self.pack_geometry() self.add_status_titles() self.add_event_listeners() @@ -143,6 +145,11 @@ class GUI: # ------------Start navigation methods---------------------------- def handle_request(self,event=False, url=False, history=True): + self.progress_bar = ttk.Progressbar(self.entry_url, orient="horizontal", length=130, mode='indeterminate') + self.progress_bar.pack(side=tk.RIGHT, padx=(0,10)) + self.progress_bar.start(10) + self.progress_bar.update_idletasks() + url = url if url else self.entry_url.get() parsed_url = self.parse_url(url) @@ -268,7 +275,7 @@ class GUI: entry = '{}{}\t{}\t{}\t{}\n'.format(url['type'], x['name'], url['resource'], url['host'], url['port']) header += entry return header - # self.send_to_screen(data=header, clear=False) + def show_search(self): text1 = ' __ ___ __ __\n/__` |__ /\ |__) / ` |__|\n.__/ |___ /~~\ | \ \__, | |\n\n\nPlease enter your search terms and press the enter key:\n\n' @@ -279,6 +286,7 @@ class GUI: self.site_display.insert(tk.END,text1) self.site_display.window_create(tk.END,window=self.search) self.site_display.config(state=tk.DISABLED) + self.search.focus_set() def query_search_engine(self, event): @@ -291,7 +299,6 @@ class GUI: self.search = None - def show_menu(self, data, clear=True): if not data: #error handling will go here @@ -349,7 +356,6 @@ class GUI: link_count += 1 self.site_display.config(state=tk.DISABLED) - return True @@ -379,6 +385,10 @@ class GUI: elif itemtype in ['p','I','g']: self.show_image(data) + try: + self.progress_bar.destroy() + except: + pass def update_status(self, event, href=False): if href: From 74c1ad4ca8205c6757500e79de96706f2c5f76d4 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sun, 4 Nov 2018 21:30:33 -0800 Subject: [PATCH 2/7] Removed progress bar in favor of loading widget in the address bar --- gui.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gui.py b/gui.py index fbd3a1e..d01f98a 100644 --- a/gui.py +++ b/gui.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import tkinter as tk -from tkinter import ttk import tkinter.simpledialog as dialog from connect import connect as conn from parser import parser @@ -145,9 +144,8 @@ class GUI: # ------------Start navigation methods---------------------------- def handle_request(self,event=False, url=False, history=True): - self.progress_bar = ttk.Progressbar(self.entry_url, orient="horizontal", length=130, mode='indeterminate') + self.progress_bar = tk.Label(self.entry_url, text=' Loading... ', width=12, relief=tk.FLAT, height=1, fg='#FFFFFF', bg=self.TYPES) self.progress_bar.pack(side=tk.RIGHT, padx=(0,10)) - self.progress_bar.start(10) self.progress_bar.update_idletasks() url = url if url else self.entry_url.get() @@ -175,6 +173,7 @@ class GUI: self.send_to_screen(self.conn.raw_response,self.conn.filetype) + def parse_url(self, url=False): parsed_url = self.parser.parse_url(url) From ccb66be447257b2f4b25488a4c1fe5bd258903c5 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sun, 4 Nov 2018 22:04:15 -0800 Subject: [PATCH 3/7] Fixed removal of terminal period in text files --- connect.py | 4 ++-- gui.py | 23 ++++++++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/connect.py b/connect.py index bfa108a..b07df7f 100644 --- a/connect.py +++ b/connect.py @@ -21,11 +21,11 @@ class connect: except socket.timeout: print('Socket timeout') socket_conn.close() - return {'type': '3', 'body': '3ERROR: Server request timed out\tfalse\tnull.host\t1'} + return {'type': '1', 'body': '3ERROR: Server request timed out\tfalse\tnull.host\t1'} except Exception as e: print('Misc socket error: ', e) socket_conn.close() - return {'type': '3', 'body': '3ERROR: Unable to communicate with remote server\tfalse\tnull.host\t1'} + return {'type': '1', 'body': '3ERROR: Unable to communicate with remote server\tfalse\tnull.host\t1'} try: self.raw_response = response.read() diff --git a/gui.py b/gui.py index d01f98a..6d1cc82 100644 --- a/gui.py +++ b/gui.py @@ -144,9 +144,9 @@ class GUI: # ------------Start navigation methods---------------------------- def handle_request(self,event=False, url=False, history=True): - self.progress_bar = tk.Label(self.entry_url, text=' Loading... ', width=12, relief=tk.FLAT, height=1, fg='#FFFFFF', bg=self.TYPES) - self.progress_bar.pack(side=tk.RIGHT, padx=(0,10)) - self.progress_bar.update_idletasks() + self.loading_bar = tk.Label(self.entry_url, text=' Loading... ', width=12, relief=tk.FLAT, height=1, fg='#FFFFFF', bg=self.TYPES) + self.loading_bar.pack(side=tk.RIGHT, padx=(0,10)) + self.loading_bar.update_idletasks() url = url if url else self.entry_url.get() parsed_url = self.parse_url(url) @@ -170,7 +170,7 @@ class GUI: if not data: return False #error handling goes here - self.send_to_screen(self.conn.raw_response,self.conn.filetype) + self.send_to_screen(data['body'],data['type']) @@ -197,8 +197,7 @@ class GUI: self.site_display.focus_set() self.config["last_viewed"] = url - self.send_to_screen(self.conn.raw_response, self.conn.filetype) - return True + return response def add_to_history(self, url): @@ -287,6 +286,12 @@ class GUI: self.site_display.config(state=tk.DISABLED) self.search.focus_set() + try: + self.loading_bar.destroy() + except: + pass + + def query_search_engine(self, event): base_url = self.entry_url.get() @@ -324,7 +329,7 @@ class GUI: link_count = 0 - for x in data[1:]: + for x in data: if x['type'] == 'i': self.site_display.insert(tk.END,' \t\t{}\n'.format(x['description'])) elif x['type'] == '3': @@ -359,7 +364,7 @@ class GUI: def show_text(self, data): - if data[-2:] == '\n.': + if data[-2:] == '.\n': data = data[:-2] self.site_display.config(state=tk.NORMAL) self.site_display.delete(1.0, tk.END) @@ -385,7 +390,7 @@ class GUI: self.show_image(data) try: - self.progress_bar.destroy() + self.loading_bar.destroy() except: pass From 0d69c834eeb48097e2477f2d4156e9704b727576 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Wed, 7 Nov 2018 23:04:34 -0800 Subject: [PATCH 4/7] Added ability to delete favorite via r click --- gui.py | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/gui.py b/gui.py index 6d1cc82..ecbfc42 100644 --- a/gui.py +++ b/gui.py @@ -45,7 +45,11 @@ class GUI: #create and configure root window self.root = tk.Tk(className='Burrow') self.root.title('Burrow') - self.root.geometry("1200x800") + sh = self.root.winfo_screenheight() + sw = self.root.winfo_screenwidth() + w = int(sw * 0.7) + h = sh - 200 + self.root.geometry("{}x{}+{}+{}".format(w, h, sw//2-w//2, 50)) self.add_assets() #main frame objects @@ -227,8 +231,9 @@ class GUI: self.entry_url.insert(tk.END, 'home') if event: self.add_to_history('home') - data += self.load_favorites() - self.send_to_screen(data, '1') + data2 = self.load_favorites() + link_count = self.send_to_screen(data, '1', True) + self.send_to_screen(data2, '1', False, link_count) def go_back(self, event): @@ -303,7 +308,7 @@ class GUI: self.search = None - def show_menu(self, data, clear=True): + def show_menu(self, data, clear=True, links=0): if not data: #error handling will go here return False @@ -327,7 +332,7 @@ class GUI: if clear: self.site_display.delete(1.0, tk.END) - link_count = 0 + link_count = links for x in data: if x['type'] == 'i': @@ -349,18 +354,25 @@ class GUI: tag_name = 'link{}'.format(link_count) callback = (lambda event, href=link, tag_name=tag_name: self.gotolink(event, href, tag_name)) hover = (lambda event, href=link, tag_name=tag_name: self.hoverlink(event, href, tag_name)) + favorite = [x for x in self.config['favorites'] if x['url'] == link] clear = (lambda event, tag_name=tag_name: self.clear_status(event, tag_name)) self.site_display.tag_bind(tag_name, "", callback) self.site_display.tag_bind(tag_name, "", hover) self.site_display.tag_bind(tag_name, '', clear) self.site_display.insert(tk.END, types[x['type']], ('type_tag',)) self.site_display.insert(tk.END,'\t\t') - self.site_display.insert(tk.END, x['description'], (tag_name,'linkcolor')) + if favorite: + tag_name_f = 'favorite{}'.format(link_count) + callback_f = (lambda event, href=link: self.remove_favorite(event, href)) + self.site_display.tag_bind(tag_name_f, '', callback_f) + self.site_display.insert(tk.END, x['description'], (tag_name, tag_name_f, 'linkcolor')) + else: + self.site_display.insert(tk.END, x['description'], (tag_name,'linkcolor')) self.site_display.insert(tk.END, '\n') link_count += 1 self.site_display.config(state=tk.DISABLED) - return True + return link_count def show_text(self, data): @@ -380,12 +392,13 @@ class GUI: self.site_display.config(state=tk.DISABLED) - def send_to_screen(self, data, itemtype='1', clear=True): + def send_to_screen(self, data, itemtype='1', clear=True, link_count=0): if itemtype == '0': self.show_text(data) elif itemtype in ['1','3','7']: data = self.parser.parse_menu(data) - self.show_menu(data, clear) + links = self.show_menu(data, clear, link_count) + return links elif itemtype in ['p','I','g']: self.show_image(data) @@ -458,6 +471,17 @@ class GUI: self.root.destroy() + def remove_favorite(self, event, href): + print('remove!') + index = None + for ind, val in enumerate(self.config['favorites']): + if val['url'] == href: + index = ind + break + if index is not None: + self.config['favorites'].pop(index) + self.load_home_screen() + if __name__ == '__main__': app = GUI() From daa33307602c404dc48c855214902906ebe86b65 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Thu, 8 Nov 2018 20:25:27 -0800 Subject: [PATCH 5/7] Added context menu to delete favorites --- gui.py | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/gui.py b/gui.py index ecbfc42..ec0d4a7 100644 --- a/gui.py +++ b/gui.py @@ -17,7 +17,7 @@ class GUI: self.history = [] self.history_location = -1 self.message_bar_content = '' - self.config = None + # self.config = None self.read_config() self.conn = conn() self.parser = parser() @@ -76,6 +76,9 @@ class GUI: #status bar objects self.status_info = tk.Label(self.status_bar, textvariable=self.message_bar_content, bg=self.STATUS_BG, takefocus=0, fg=self.ACTIVELINK) + #menu objects + self.fave_context = tk.Menu(self.body, tearoff=0) + self.pack_geometry() self.add_status_titles() @@ -145,6 +148,14 @@ class GUI: self.message_bar_content.set('Ready.') + def show_fave_context(self, e, href): + if self.fave_context.type(0): + self.fave_context.delete(0) + delete_favorite = (lambda event=e, link=href: self.remove_favorite(event, link)) + self.fave_context.add_command(label="Remove favorite", command=delete_favorite) + self.fave_context.tk_popup(e.x_root, e.y_root) + + # ------------Start navigation methods---------------------------- def handle_request(self,event=False, url=False, history=True): @@ -233,7 +244,7 @@ class GUI: self.add_to_history('home') data2 = self.load_favorites() link_count = self.send_to_screen(data, '1', True) - self.send_to_screen(data2, '1', False, link_count) + self.send_to_screen(data2, '1', False) def go_back(self, event): @@ -308,7 +319,7 @@ class GUI: self.search = None - def show_menu(self, data, clear=True, links=0): + def show_menu(self, data, clear=True): if not data: #error handling will go here return False @@ -332,7 +343,8 @@ class GUI: if clear: self.site_display.delete(1.0, tk.END) - link_count = links + if clear: + self.link_count = 0 for x in data: if x['type'] == 'i': @@ -351,7 +363,7 @@ class GUI: else: link = 'gopher://{}{}/{}{}'.format(x['host'], x['port'], x['type'], x['resource']) - tag_name = 'link{}'.format(link_count) + tag_name = 'link{}'.format(self.link_count) callback = (lambda event, href=link, tag_name=tag_name: self.gotolink(event, href, tag_name)) hover = (lambda event, href=link, tag_name=tag_name: self.hoverlink(event, href, tag_name)) favorite = [x for x in self.config['favorites'] if x['url'] == link] @@ -362,17 +374,17 @@ class GUI: self.site_display.insert(tk.END, types[x['type']], ('type_tag',)) self.site_display.insert(tk.END,'\t\t') if favorite: - tag_name_f = 'favorite{}'.format(link_count) - callback_f = (lambda event, href=link: self.remove_favorite(event, href)) + tag_name_f = 'favorite{}'.format(self.link_count) + callback_f = (lambda event, href=link: self.show_fave_context(event, href)) self.site_display.tag_bind(tag_name_f, '', callback_f) self.site_display.insert(tk.END, x['description'], (tag_name, tag_name_f, 'linkcolor')) else: self.site_display.insert(tk.END, x['description'], (tag_name,'linkcolor')) self.site_display.insert(tk.END, '\n') - link_count += 1 + self.link_count += 1 self.site_display.config(state=tk.DISABLED) - return link_count + return self.link_count def show_text(self, data): @@ -392,13 +404,12 @@ class GUI: self.site_display.config(state=tk.DISABLED) - def send_to_screen(self, data, itemtype='1', clear=True, link_count=0): + def send_to_screen(self, data, itemtype='1', clear=True): if itemtype == '0': self.show_text(data) elif itemtype in ['1','3','7']: data = self.parser.parse_menu(data) - links = self.show_menu(data, clear, link_count) - return links + self.show_menu(data, clear) elif itemtype in ['p','I','g']: self.show_image(data) @@ -472,7 +483,6 @@ class GUI: def remove_favorite(self, event, href): - print('remove!') index = None for ind, val in enumerate(self.config['favorites']): if val['url'] == href: From 450599428c91e4ee068813d3a2dd4a742310b038 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Fri, 9 Nov 2018 20:03:43 -0800 Subject: [PATCH 6/7] Added more versatile right click menu --- gui.py | 72 +++++++++++++++++++++++++++++++++++++++---------------- parser.py | 2 +- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/gui.py b/gui.py index ec0d4a7..80569f3 100644 --- a/gui.py +++ b/gui.py @@ -27,12 +27,13 @@ class GUI: self.FG = '#E0E2E4' self.BG = '#2F393C' self.LINK = '#E8E2B7' + self.FLINK = '#93C763' self.ACTIVELINK = '#678CB1' self.HLB = '#804000' self.HLF = '#E0E2E4' self.STATUS_BG = '#293134' self.STATUS_FG = '#FFCD22' - self.ERROR = '#E8E2B7' + self.ERROR = '#EC7600' self.BAR_BG = '#293134' self.BAR_FG = '#2F393C' self.BAR_HLB = '#804000' @@ -70,6 +71,8 @@ class GUI: self.site_display = tk.Text(self.body, bg=self.BG, foreground=self.FG, padx=20, pady=20, wrap=tk.WORD, state=tk.DISABLED, spacing2=2, spacing1=2, spacing3=2, yscrollcommand=self.scroll_bar.set, highlightcolor=self.BG, highlightbackground=self.BAR_BG, relief=tk.FLAT) self.scroll_bar.config(command=self.site_display.yview, width=20, relief=tk.RIDGE) self.site_display.tag_configure('linkcolor', foreground=self.LINK, spacing1=5, spacing2=5, spacing3=5) + self.site_display.tag_configure('favoritecolor', foreground=self.FLINK, spacing1=5, spacing2=5, spacing3=5) + self.site_display.tag_configure('type_tag', background=self.BG, foreground=self.TYPES, spacing2=1, spacing1=1, spacing3=1) self.site_display.tag_configure('error_text', foreground=self.ERROR, spacing1=5, spacing2=5, spacing3=5) @@ -77,7 +80,7 @@ class GUI: self.status_info = tk.Label(self.status_bar, textvariable=self.message_bar_content, bg=self.STATUS_BG, takefocus=0, fg=self.ACTIVELINK) #menu objects - self.fave_context = tk.Menu(self.body, tearoff=0) + self.context_menu = tk.Menu(self.body, tearoff=0) self.pack_geometry() @@ -148,12 +151,22 @@ class GUI: self.message_bar_content.set('Ready.') - def show_fave_context(self, e, href): - if self.fave_context.type(0): - self.fave_context.delete(0) - delete_favorite = (lambda event=e, link=href: self.remove_favorite(event, link)) - self.fave_context.add_command(label="Remove favorite", command=delete_favorite) - self.fave_context.tk_popup(e.x_root, e.y_root) + def show_context_menu(self, e, href): + self.context_menu.delete(0,20) + copy_link = (lambda link=href: self.copy_to_clipboard(link)) + self.context_menu.add_command(label="Copy link to clipboard", command=copy_link) + if self.is_favorite(href): + delete_favorite = (lambda event=e, link=href: self.remove_favorite(event, link)) + self.context_menu.add_command(label="Remove favorite", command=delete_favorite) + else: + add_favorite = (lambda event=e, link=href: self.add_to_favorites(event, link)) + self.context_menu.add_command(label="Add to favorites", command=add_favorite) + self.context_menu.tk_popup(e.x_root, e.y_root) + + + def copy_to_clipboard(self, test): + self.root.clipboard_clear() + self.root.clipboard_append(text) # ------------Start navigation methods---------------------------- @@ -170,6 +183,8 @@ class GUI: if url == 'home': return self.load_home_screen(history) else: + data = {'type': '3', 'body': '3ERROR: Improperly formatted URL\tfalse\tnull.host\t1\n'} + print('Error parsing url') return False #error handling goes here self.populate_url_bar(url) @@ -180,6 +195,8 @@ class GUI: if parsed_url['type'] == '7': self.show_search() return False # display search + elif not parsed_url: + pass else: data = self.execute_address(parsed_url) if not data: @@ -265,14 +282,19 @@ class GUI: self.handle_request(False, href, False) - def add_to_favorites(self, event): + def add_to_favorites(self, event, url=None): favorite_name = dialog.askstring("Add to favorites", "What would you like to title this favorite?") - url = self.entry_url.get() + reload_home = True + if url is None: + url = self.entry_url.get() + reload_home = False if not favorite_name or not url: return False favorite = {"url": url, "name": favorite_name} self.config["favorites"].append(favorite) self.write_config(self.config) + if reload_home: + self.load_home_screen() @@ -365,21 +387,23 @@ class GUI: tag_name = 'link{}'.format(self.link_count) callback = (lambda event, href=link, tag_name=tag_name: self.gotolink(event, href, tag_name)) - hover = (lambda event, href=link, tag_name=tag_name: self.hoverlink(event, href, tag_name)) favorite = [x for x in self.config['favorites'] if x['url'] == link] - clear = (lambda event, tag_name=tag_name: self.clear_status(event, tag_name)) self.site_display.tag_bind(tag_name, "", callback) - self.site_display.tag_bind(tag_name, "", hover) - self.site_display.tag_bind(tag_name, '', clear) self.site_display.insert(tk.END, types[x['type']], ('type_tag',)) self.site_display.insert(tk.END,'\t\t') + callback_menu = (lambda event, href=link: self.show_context_menu(event, href)) + self.site_display.tag_bind(tag_name, '', callback_menu) + if favorite: - tag_name_f = 'favorite{}'.format(self.link_count) - callback_f = (lambda event, href=link: self.show_fave_context(event, href)) - self.site_display.tag_bind(tag_name_f, '', callback_f) - self.site_display.insert(tk.END, x['description'], (tag_name, tag_name_f, 'linkcolor')) + styletag = 'favoritecolor' else: - self.site_display.insert(tk.END, x['description'], (tag_name,'linkcolor')) + styletag = 'linkcolor' + hover = (lambda event, href=link, tag_name=tag_name: self.hoverlink(event, href, tag_name)) + clear = (lambda event, tag_name=tag_name: self.clear_status(event, tag_name)) + self.site_display.tag_bind(tag_name, "", hover) + self.site_display.tag_bind(tag_name, '', clear) + + self.site_display.insert(tk.END, x['description'], (tag_name,styletag)) self.site_display.insert(tk.END, '\n') self.link_count += 1 @@ -418,6 +442,7 @@ class GUI: except: pass + def update_status(self, event, href=False): if href: self.message_bar_content.set(href) @@ -442,10 +467,11 @@ class GUI: def hoverlink(self, event, href, tag_name): self.update_status(event, href) e = event.widget - e.tag_config(tag_name, underline=1, foreground=self.LINK) + e.tag_config(tag_name, underline=1) self.site_display.config(cursor="arrow") e.update_idletasks() + def build_image(self, bytes_str): stream = BytesIO(bytes_str) pilimage = Image.open(stream) @@ -481,6 +507,12 @@ class GUI: self.write_config(self.config) self.root.destroy() + def is_favorite(self, href): + for val in self.config['favorites']: + if val['url'] == href: + return True + return False + def remove_favorite(self, event, href): index = None diff --git a/parser.py b/parser.py index 5fb6210..f1bf56e 100644 --- a/parser.py +++ b/parser.py @@ -18,7 +18,7 @@ class parser: if url == 'home': return False - regex = r'^(?P(?:(gopher|http):\/\/)?)?(?P[\w\.\d]+)(?P(?::\d+)?)?(?P(?:\/[\dgIp])?)?(?P(?:\/.*)?)?$' + regex = r'^(?P(?:(gopher|http):\/\/)?)?(?P[\w\-\.\d]+)(?P(?::\d+)?)?(?P(?:\/[\dgIp])?)?(?P(?:\/.*)?)?$' match = re.match(regex, url) From 49571178db16f658ae3b69cef29df034cd5dd1c0 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sat, 10 Nov 2018 15:48:08 -0800 Subject: [PATCH 7/7] More fully fleshed out right click menu --- gui.py | 119 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 41 deletions(-) diff --git a/gui.py b/gui.py index 80569f3..9b8a6df 100644 --- a/gui.py +++ b/gui.py @@ -30,18 +30,21 @@ class GUI: self.FLINK = '#93C763' self.ACTIVELINK = '#678CB1' self.HLB = '#804000' - self.HLF = '#E0E2E4' + self.HLF = self.FG self.STATUS_BG = '#293134' self.STATUS_FG = '#FFCD22' self.ERROR = '#EC7600' - self.BAR_BG = '#293134' - self.BAR_FG = '#2F393C' - self.BAR_HLB = '#804000' - self.BAR_HLF = '#E0E2E4' - self.BAR_SLOT = '#E0E2E4' + self.BAR_BG = self.STATUS_BG + self.BAR_FG = self.BG + self.BAR_HLB = self.HLB + self.BAR_HLF = self.FG + self.BAR_SLOT = self.FG self.SCROLL = '#434A57' self.TYPES = '#A082BD' - + self.MENU_BG = self.BAR_BG + self.MENU_FG = self.FG + self.MENU_HLB = self.LINK + self.MENU_HLF = self.BAR_BG #create and configure root window self.root = tk.Tk(className='Burrow') @@ -80,7 +83,7 @@ class GUI: self.status_info = tk.Label(self.status_bar, textvariable=self.message_bar_content, bg=self.STATUS_BG, takefocus=0, fg=self.ACTIVELINK) #menu objects - self.context_menu = tk.Menu(self.body, tearoff=0) + self.context_menu = tk.Menu(self.body, tearoff=0, bg=self.MENU_BG, fg=self.MENU_FG, activebackground=self.MENU_HLB, activeforeground=self.MENU_HLF, activeborderwidth=0) self.pack_geometry() @@ -106,15 +109,17 @@ class GUI: x.bind('', self.clear_status) x.config(activebackground=self.BG) self.entry_url.bind('', self.handle_request) - self.btn_back.bind('', self.go_back) - self.btn_forward.bind('', self.go_forward) - self.btn_home.bind('', self.load_home_screen) + self.btn_back.bind('', self.go_back) + self.btn_forward.bind('', self.go_forward) + self.btn_home.bind('', self.load_home_screen) self.site_display.bind("", lambda event: self.site_display.yview_scroll(-1, 'units')) self.site_display.bind("", lambda event: self.site_display.yview_scroll(1, 'units')) self.site_display.bind("", lambda event: self.site_display.focus_set()) self.entry_url.bind("", lambda event: self.entry_url.focus_set()) self.root.protocol('WM_DELETE_WINDOW', self.close_window) self.btn_favorite.bind("", self.add_to_favorites) + self.site_display.tag_bind('generic_r_click', "", (lambda event, href=None: self.show_context_menu(event, href))) + def pack_geometry(self): @@ -151,20 +156,37 @@ class GUI: self.message_bar_content.set('Ready.') - def show_context_menu(self, e, href): + def show_context_menu(self, e, href=None): self.context_menu.delete(0,20) - copy_link = (lambda link=href: self.copy_to_clipboard(link)) - self.context_menu.add_command(label="Copy link to clipboard", command=copy_link) - if self.is_favorite(href): - delete_favorite = (lambda event=e, link=href: self.remove_favorite(event, link)) - self.context_menu.add_command(label="Remove favorite", command=delete_favorite) - else: - add_favorite = (lambda event=e, link=href: self.add_to_favorites(event, link)) - self.context_menu.add_command(label="Add to favorites", command=add_favorite) + #add navigation options + if len(self.history) > 1 or self.history_location > 0: + back = (lambda event=e: self.go_back(event)) + self.context_menu.add_command(label="Back", command=back) + if len(self.history) > 1 or self.history_location < len(self.history) - 2: + forward = (lambda event=e: self.go_forward(event)) + self.context_menu.add_command(label="Forward", command=forward) + if self.entry_url.get() != 'home': + home = (lambda event=e: self.load_home_screen(event)) + self.context_menu.add_command(label="Home", command=home) + + + if href: + copy_link = (lambda link=href: self.copy_to_clipboard(link)) + self.context_menu.add_command(label="Copy URL to clipboard", command=copy_link) + + self.context_menu.add_separator() + if self.is_favorite(href): + delete_favorite = (lambda event=e, link=href: self.remove_favorite(event, link)) + self.context_menu.add_command(label="Delete from favorites", command=delete_favorite) + rename_favorite = (lambda event=e, link=href: self.rename_favorite(event, link)) + self.context_menu.add_command(label="Rename this favorite", command=rename_favorite) + elif href: + add_favorite = (lambda event=e, link=href: self.add_to_favorites(event, link)) + self.context_menu.add_command(label="Add to favorites", command=add_favorite) self.context_menu.tk_popup(e.x_root, e.y_root) - def copy_to_clipboard(self, test): + def copy_to_clipboard(self, text): self.root.clipboard_clear() self.root.clipboard_append(text) @@ -184,7 +206,6 @@ class GUI: return self.load_home_screen(history) else: data = {'type': '3', 'body': '3ERROR: Improperly formatted URL\tfalse\tnull.host\t1\n'} - print('Error parsing url') return False #error handling goes here self.populate_url_bar(url) @@ -210,8 +231,6 @@ class GUI: parsed_url = self.parser.parse_url(url) if not parsed_url: - # send error to screen - print('Error parsing URL') return False return parsed_url @@ -282,20 +301,6 @@ class GUI: self.handle_request(False, href, False) - def add_to_favorites(self, event, url=None): - favorite_name = dialog.askstring("Add to favorites", "What would you like to title this favorite?") - reload_home = True - if url is None: - url = self.entry_url.get() - reload_home = False - if not favorite_name or not url: - return False - favorite = {"url": url, "name": favorite_name} - self.config["favorites"].append(favorite) - self.write_config(self.config) - if reload_home: - self.load_home_screen() - #-------------Start view methods---------------- @@ -370,9 +375,9 @@ class GUI: for x in data: if x['type'] == 'i': - self.site_display.insert(tk.END,' \t\t{}\n'.format(x['description'])) + self.site_display.insert(tk.END,' \t\t{}\n'.format(x['description']), ('generic_r_click')) elif x['type'] == '3': - self.site_display.insert(tk.END,' \t\t{}\n'.format(x['description'])) + self.site_display.insert(tk.END,' \t\t{}\n'.format(x['description']), ('generic_r_click')) elif x['type'] in types: # adapted from: # https://stackoverflow.com/questions/27760561/tkinter-and-hyperlinks @@ -406,7 +411,6 @@ class GUI: self.site_display.insert(tk.END, x['description'], (tag_name,styletag)) self.site_display.insert(tk.END, '\n') self.link_count += 1 - self.site_display.config(state=tk.DISABLED) return self.link_count @@ -514,6 +518,24 @@ class GUI: return False + def rename_favorite(self, event, link): + index = None + for ind, val in enumerate(self.config['favorites']): + if val['url'] == link: + index = ind + break + if index is not None: + title = self.config['favorites'][index]['name'] + new_name = dialog.askstring("Rename favorite","\n Change favorite name to: \n".format(title), initialvalue=title) + if new_name: + self.config['favorites'][index]['name'] = new_name + self.write_config(self.config) + if self.entry_url.get() == 'home': + self.load_home_screen() + return True + return False + + def remove_favorite(self, event, href): index = None for ind, val in enumerate(self.config['favorites']): @@ -522,9 +544,24 @@ class GUI: break if index is not None: self.config['favorites'].pop(index) + self.write_config(self.config) self.load_home_screen() + def add_to_favorites(self, event, url=None): + favorite_name = dialog.askstring("Add to favorites", "What would you like to title this favorite?") + if url is None: + url = self.entry_url.get() + if not favorite_name or not url: + return False + favorite = {"url": url, "name": favorite_name} + self.config["favorites"].append(favorite) + self.write_config(self.config) + if self.entry_url.get() == 'home': + self.load_home_screen() + return True + + if __name__ == '__main__': app = GUI() app.root.mainloop()