@@ -11,6 +11,8 @@ import sys
1111from os .path import abspath , join , expanduser
1212#! from os import getenv # Not useful right now, but could be useful if translations are available
1313import json
14+ from random import choice , randint
15+ from string import ascii_letters , digits
1416import tkinter as tk
1517from tkinter import font
1618from tkinter import ttk
@@ -53,13 +55,27 @@ if len(arguments) == 2 and arguments[1] == hash_str:
5355 print (updateMode ())
5456 sys .exit (0 )
5557elif len (arguments ) == 2 :
56- print ((Fernet .generate_key ().decode (), Fernet .generate_key ().decode (), Fernet .generate_key ().decode (), Fernet .generate_key ().decode ()))
58+ possible_characters = ascii_letters + digits
59+ print (("" .join ([choice (possible_characters ) for i in range (randint (15 , 45 ))]), "" .join ([choice (possible_characters ) for i in range (randint (15 , 45 ))]), "" .join ([choice (possible_characters ) for i in range (randint (15 , 45 ))]), Fernet .generate_key ().decode ()))
5760 sys .exit (0 )
5861
5962try :
6063 settings_path = join (expanduser ("~" ), ".encryptext" , "settings.json" )
6164 with open (settings_path , "r" , encoding = "utf-8" ) as file :
6265 settings = json .load (file )
66+ # Replace the "true" and "false" strings with the boolean version
67+ for key , value in settings .items ():
68+ if isinstance (value , dict ):
69+ for sub_key , sub_value in value .items ():
70+ if sub_value == "false" :
71+ settings [key ][sub_key ] = False
72+ elif sub_value == "true" :
73+ settings [key ][sub_key ] = True
74+ else :
75+ if value == "false" :
76+ settings [key ] = False
77+ elif value == "true" :
78+ settings [key ] = True
6379except FileNotFoundError :
6480 settings = {
6581 "version" : "'Encryptext Offline Mode'" ,
@@ -85,7 +101,61 @@ font_scale_factor = settings["otherSettings"]["fontScaleFactor"]
85101"""
86102Custom Classes
87103"""
88- # From: https://www.reddit.com/r/learnpython/comments/6dndqz/comment/di42keo/
104+ # https://stackoverflow.com/a/16375233
105+ class TextLineNumbers (tk .Canvas ):
106+ def __init__ (self , * args , ** kwargs ):
107+ tk .Canvas .__init__ (self , * args , ** kwargs )
108+ self .textwidget = None
109+
110+ def attach (self , text_widget ):
111+ self .textwidget = text_widget
112+
113+ def redraw (self , * args ):
114+ '''redraw line numbers'''
115+ self .delete ("all" )
116+
117+ i = self .textwidget .index ("@0,0" )
118+ while True :
119+ dline = self .textwidget .dlineinfo (i )
120+ if dline is None : break
121+ y = dline [1 ]
122+ linenum = str (i ).split ("." )[0 ]
123+ self .create_text (2 ,y ,anchor = "nw" , text = linenum )
124+ i = self .textwidget .index ("%s+1line" % i )
125+
126+ # https://stackoverflow.com/a/16375233
127+ class CustomText (tk .Text ):
128+ def __init__ (self , * args , ** kwargs ):
129+ tk .Text .__init__ (self , * args , ** kwargs )
130+
131+ # create a proxy for the underlying widget
132+ self ._orig = self ._w + "_orig"
133+ self .tk .call ("rename" , self ._w , self ._orig )
134+ self .tk .createcommand (self ._w , self ._proxy )
135+
136+ def _proxy (self , * args ):
137+ # let the actual widget perform the requested action
138+ try :
139+ cmd = (self ._orig ,) + args
140+ result = self .tk .call (cmd )
141+ except tk ._tkinter .TclError :
142+ result = ""
143+
144+ # generate an event if something was added or deleted,
145+ # or the cursor position changed
146+ if (args [0 ] in ("insert" , "replace" , "delete" ) or
147+ args [0 :3 ] == ("mark" , "set" , "insert" ) or
148+ args [0 :2 ] == ("xview" , "moveto" ) or
149+ args [0 :2 ] == ("xview" , "scroll" ) or
150+ args [0 :2 ] == ("yview" , "moveto" ) or
151+ args [0 :2 ] == ("yview" , "scroll" )
152+ ):
153+ self .event_generate ("<<Change>>" , when = "tail" )
154+
155+ # return what the actual widget returned
156+ return result
157+
158+ # https://www.reddit.com/r/learnpython/comments/6dndqz/comment/di42keo/
89159class WrappedLabel (ttk .Label ):
90160 """a type of Label that automatically adjusts the wrap to the size"""
91161 def __init__ (self , master = None , ** kwargs ):
@@ -343,7 +413,9 @@ max_history = 50
343413file_format_tags = []
344414file_format_tag_nums = []
345415
416+ frames = []
346417textboxes = []
418+ line_number_areas = []
347419
348420saved = []
349421prev_key = ""
@@ -1106,12 +1178,15 @@ def showQuickMenu(Event=None):
11061178 rightclickmenu .grab_release ()
11071179
11081180def addNewTab (Event = None ):
1181+ # Create a frame to add all the stuff to
1182+ frames .append (tk .Frame (tab_panes , cursor = "xterm" ))
1183+
11091184 # Create new textbox
11101185 if settings ["otherSettings" ]["wrapLines" ] == True :
11111186 wrap_mode = "word"
11121187 else :
11131188 wrap_mode = "none"
1114- textboxes .append (tk . Text ( tab_panes , state = tk .NORMAL , font = (default_font_type , default_font_size , "normal" ), cursor = "xterm" , wrap = wrap_mode ))
1189+ textboxes .append (CustomText ( frames [ - 1 ] , state = tk .NORMAL , font = (default_font_type , default_font_size , "normal" ), cursor = "xterm" , wrap = wrap_mode ))
11151190
11161191 # Create new tab info slot in arrays
11171192 file_save_locations .append ("" )
@@ -1124,10 +1199,18 @@ def addNewTab(Event=None):
11241199 font_type .append (default_font_type )
11251200 saved .append (True )
11261201
1202+ if settings ["otherSettings" ]["showLineNumbers" ] == True :
1203+ line_number_areas .append (TextLineNumbers (frames [- 1 ], width = 30 ))
1204+ line_number_areas [- 1 ].attach (textboxes [- 1 ])
1205+
1206+ if settings ["otherSettings" ]["highlightActiveLine" ] == True :
1207+ # Adapted from https://stackoverflow.com/a/9720858
1208+ textboxes [- 1 ].tag_configure ("current_line" , background = "#e9e9e9" )
1209+ textboxes [- 1 ].tag_raise ("sel" , "current_line" )
1210+
11271211 # Create scroll bar and link it
11281212 scroll_bars = []
1129- scroll_bars .append ([tk .Scrollbar (textboxes [- 1 ], orient = tk .VERTICAL , cursor = "arrow" )])
1130- scroll_bars [- 1 ][0 ].config (command = textboxes [- 1 ].yview )
1213+ scroll_bars .append ([tk .Scrollbar (frames [- 1 ], orient = tk .VERTICAL , cursor = "arrow" , command = textboxes [- 1 ].yview )])
11311214 textboxes [- 1 ].config (yscrollcommand = scroll_bars [- 1 ][0 ].set )
11321215
11331216 if settings ["otherSettings" ]["wrapLines" ] == False :
@@ -1136,24 +1219,36 @@ def addNewTab(Event=None):
11361219 textboxes [- 1 ].config (xscrollcommand = scroll_bars [- 1 ][1 ].set )
11371220
11381221 # Add to display
1139- textboxes [- 1 ].pack (side = tk .TOP , fill = tk .BOTH )
1222+ frames [- 1 ].pack (side = tk .TOP , fill = tk .BOTH )
1223+ if settings ["otherSettings" ]["showLineNumbers" ] == True :
1224+ line_number_areas [- 1 ].pack (side = tk .LEFT , fill = tk .Y )
1225+ textboxes [- 1 ].pack (side = tk .LEFT , fill = tk .BOTH , expand = True )
11401226 scroll_bars [- 1 ][0 ].pack (side = tk .RIGHT , fill = tk .Y )
11411227
11421228 if settings ["otherSettings" ]["wrapLines" ] == False :
11431229 scroll_bars [- 1 ][1 ].pack (side = tk .BOTTOM , fill = tk .X )
11441230
1145- tab_panes .add (textboxes [- 1 ], text = " Untitled " )
1231+ tab_panes .add (frames [- 1 ], text = " Untitled " )
11461232
11471233 # Allow right-click menu to show up
11481234 textboxes [- 1 ].bind ("<Button-3>" , showQuickMenu )
11491235
1150- # Fix Ctrl+T switching last char in textbox
1236+ # Fix shortcut doing built-in process instead of custom process in textbox
11511237 # https://stackoverflow.com/a/54185644
11521238 bindtags = textboxes [- 1 ].bindtags ()
11531239 textboxes [- 1 ].bindtags ((bindtags [2 ], bindtags [0 ], bindtags [1 ], bindtags [3 ]))
11541240
11551241 # Track document changes and update markdown preview
11561242 textboxes [- 1 ].bind ('<Key>' , trackChanges )
1243+ if settings ["otherSettings" ]["showLineNumbers" ] == True and settings ["otherSettings" ]["highlightActiveLine" ] == True :
1244+ textboxes [- 1 ].bind ("<<Change>>" , updateHighlightAndNumbers )
1245+ textboxes [- 1 ].bind ("<Configure>" , updateHighlightAndNumbers )
1246+ elif settings ["otherSettings" ]["showLineNumbers" ] == True :
1247+ textboxes [- 1 ].bind ("<<Change>>" , line_number_areas [- 1 ].redraw )
1248+ textboxes [- 1 ].bind ("<Configure>" , line_number_areas [- 1 ].redraw )
1249+ elif settings ["otherSettings" ]["highlightActiveLine" ] == True :
1250+ textboxes [- 1 ].bind ("<<Change>>" , updateActiveLine )
1251+ textboxes [- 1 ].bind ("<Configure>" , updateActiveLine )
11571252
11581253 # Sets the tab focus to the newly created tab
11591254 tab_panes .select (tab_panes .tabs ()[- 1 ])
@@ -1200,6 +1295,21 @@ def getCurrentTab() -> int:
12001295 except : # Returns -1 if there are no tabs
12011296 return - 1
12021297
1298+ # Update both the highlight and line numbers
1299+ def updateHighlightAndNumbers (Event = None ):
1300+ current_tab = getCurrentTab ()
1301+ if current_tab != - 1 :
1302+ line_number_areas [current_tab ].redraw ()
1303+ updateActiveLine ()
1304+
1305+ # Update the textbox's current line highlight
1306+ # Adapted from https://stackoverflow.com/a/9720858
1307+ def updateActiveLine (Event = None ):
1308+ current_tab = getCurrentTab ()
1309+ if current_tab != - 1 :
1310+ textboxes [current_tab ].tag_remove ("current_line" , "1.0" , "end" )
1311+ textboxes [current_tab ].tag_add ("current_line" , "insert linestart" , "insert lineend+1c" )
1312+
12031313def setSaveStatus (save : bool , current_tab : int ) -> None :
12041314 saved [current_tab ] = save
12051315 cur_tab_id = tab_panes .tabs ()[current_tab ]
@@ -1242,16 +1352,22 @@ def captureSpecialKeys(Event=None):
12421352 openPreview ()
12431353 elif cur_key == "P" :
12441354 preview_window .closeWindow ()
1245- elif cur_key == "+" :
1355+ elif cur_key == "plus" and mod_key == 5 :
12461356 increaseFont ()
1247- elif cur_key == "_" :
1357+ elif cur_key == "underscore" and mod_key == 5 :
12481358 decreaseFont ()
12491359 elif cur_key == "i" :
12501360 changeToItalic ()
12511361 elif cur_key == "b" :
12521362 changeToBold ()
12531363 elif cur_key == "n" :
12541364 changeToNormal ()
1365+ elif cur_key == "c" :
1366+ copy ()
1367+ elif cur_key == "v" :
1368+ paste ()
1369+ elif cur_key == "a" :
1370+ selectAll ()
12551371
12561372 return "break"
12571373
0 commit comments