@@ -378,8 +378,8 @@ def helper_case_quotes(words_data1, words_data2, case_insensitive, ignore_quotes
378378 a_compare = [word .lower () for word in a_compare ]
379379 b_compare = [word .lower () for word in b_compare ]
380380 if ignore_quotes :
381- a_compare = [word .replace ("‘ " , "'" ).replace ("’ " , "'" ).replace ("ʼ " , "'" ).replace ('“ ' , '"' ).replace ('” ' , '"' ) for word in a_compare ]
382- b_compare = [word .replace ("‘ " , "'" ).replace ("’ " , "'" ).replace ("ʼ " , "'" ).replace ('“ ' , '"' ).replace ('” ' , '"' ) for word in b_compare ]
381+ a_compare = [word .replace ("‘ " , "'" ).replace ("’ " , "'" ).replace ("ʼ " , "'" ).replace ('“ ' , '"' ).replace ('†' , '"' ) for word in a_compare ]
382+ b_compare = [word .replace ("‘ " , "'" ).replace ("’ " , "'" ).replace ("ʼ " , "'" ).replace ('“ ' , '"' ).replace ('†' , '"' ) for word in b_compare ]
383383 return a_compare ,b_compare
384384
385385def align_words_with_difflib (words_data1 , words_data2 , case_insensitive , ignore_quotes ):#difflib (standard, uses Ratcliff-Obershelp algorithm)
@@ -589,6 +589,13 @@ def get_opcodes(self):
589589
590590 diff_output = process .stdout
591591
592+
593+ if process .returncode == 0 and not diff_output .strip ():# in case the two files are equal and therefore git diff returns 0 and empty stdout
594+ with open (self .temp_file_a , 'r' , encoding = 'utf-8' , errors = 'replace' ) as f :
595+ num_lines = sum (1 for _ in f )
596+ return [('equal' , 0 , num_lines , 0 , num_lines , False )]
597+
598+
592599 COLOR_RED_FG = r'\x1b\[31m' # deletions
593600 COLOR_GREEN_FG = r'\x1b\[32m' # insertions
594601 COLOR_BOLD_MAGENTA_FG = r'\x1b\[1;35m' # deletions (move)
@@ -882,7 +889,7 @@ def is_git_diff_available():
882889
883890class PDFViewerPane :
884891 PAGE_PADDING = 10
885- BUFFER_PAGES = 1
892+ BUFFER_PAGES = 3
886893 def __init__ (self , master , parent_app , pane_id ):
887894 self .sorted = None
888895 self .master = master
@@ -982,8 +989,9 @@ def _deactivate_pan_mode(self):
982989 def _on_pan_move (self , event ):#with timer continuosly postponed
983990 """Drags the canvas view, as the mouse moves and without click, if pan mode is active."""
984991 if self ._pan_mode_active :
992+ #print("event: ",event.x, event.y, "self._cursor_start_pos.x: ",self._cursor_start_pos.x,self.canvas.winfo_rootx())
985993 #self.canvas.scan_dragto(event.x, event.y, gain=1)
986- self .canvas .scan_dragto (0 , event .y , gain = 3 )
994+ self .canvas .scan_dragto (self . _cursor_start_pos . x - self . canvas . winfo_rootx () , event .y , gain = 3 ) #instead ov event.x we stick to original x (where the user double clicked )
987995 self .schedule_render_visible_pages ()
988996 if self .ignore_scroll_events_counter == 0 :
989997 self .canvas .event_generate ("<<UserCanvasScrolled>>" )
@@ -1005,23 +1013,6 @@ def _snap_back_timer(self):
10051013
10061014 # Schedule the next snap-back
10071015 self ._after_id = self .master .after (400 , self ._snap_back_timer )
1008- def on_right_clickOLD (self , event ):
1009- """Displays a context menu on right-click."""
1010- self .context_menu .delete (0 , tk .END )
1011- if self .pdf_document and not self .pdf_document .is_closed :
1012- self .context_menu .add_command (
1013- label = "Save PDF with Annotations..." ,
1014- command = self .save_pdf_with_annotations
1015- )
1016- self .context_menu .add_separator ()
1017- self .context_menu .add_command (
1018- label = "Paste from Clipboard" ,
1019- command = self .paste_from_clipboard_action
1020- )
1021- try :
1022- self .context_menu .tk_popup (event .x_root , event .y_root )
1023- finally :
1024- self .context_menu .grab_release ()
10251016 def on_right_click (self , event ):
10261017 """Displays a context menu on right-click."""
10271018 self .context_menu .delete (0 , tk .END )
@@ -1558,6 +1549,14 @@ def __init__(self, master):
15581549 self .master .after_idle (self .update_ui_state )
15591550 self .master .after_idle (lambda : self .pane1 .canvas .focus_set ())
15601551 self ._process_command_line_args ()
1552+ #variable used in the sync_scroll()
1553+ self .scroll_time = 0
1554+ self .scroll_y = 0
1555+ self .scroll_pane = None
1556+ self .scroll_height = 0
1557+ self .scroll_target_y = 0
1558+ self .scroll_distance = 0
1559+
15611560 def setup_ui (self ):
15621561 """Sets up the main application UI, including control frame and viewer panes."""
15631562 control_frame = ttk .Frame (self .master , padding = "10" )
@@ -1780,6 +1779,19 @@ def sync_scroll(self, source_pane):
17801779 return
17811780 source_x , source_y = source_pane .get_current_view_coords ()
17821781 source_canvas_height = source_pane .canvas .winfo_height ()
1782+
1783+ # read previous scroll
1784+ prev_scroll_time = self .scroll_time
1785+ prev_scroll_y = self .scroll_y
1786+ prev_scroll_pane = self .scroll_pane
1787+ prev_scroll_height = self .scroll_height
1788+ #set for next one
1789+ time_scroll = time .time ()
1790+ self .scroll_time = time_scroll
1791+ self .scroll_y = source_y
1792+ self .scroll_pane = source_pane
1793+ self .scroll_height = source_canvas_height
1794+
17831795 if source_canvas_height == 0 :
17841796 return
17851797 first_common_word_in_view = None
@@ -1803,6 +1815,8 @@ def sync_scroll(self, source_pane):
18031815 if word_info_target ["unique_id" ] == common_word_id :
18041816 target_word_info = word_info_target
18051817 break
1818+ #print(f"\nscroll direction: {source_y-prev_scroll_y}")# positive=we are scrolling down
1819+ #print("source y: ",source_y)
18061820 #print(f"source: {first_common_word_in_view["text"]}, {first_common_word_in_view["page_num"]}, {first_common_word_in_view["x0"]}, {first_common_word_in_view["y0"]},\ntarget: {target_word_info["text"]}, {target_word_info["page_num"]}, {target_word_info["x0"]}, {target_word_info["y0"]}")
18071821 if target_word_info :
18081822 target_page_num = target_word_info ["page_num" ]
@@ -1813,9 +1827,21 @@ def sync_scroll(self, source_pane):
18131827 y_offset_in_source_view = source_word_y_content_coord_exact - source_y
18141828 target_word_y_content_coord = (target_page_info ["y_start_offset" ] + target_word_y0_doc ) * target_pane .zoom_level
18151829 target_y_scroll_pixels = target_word_y_content_coord - y_offset_in_source_view
1816- source_x_prop = source_pane .canvas .xview ()[0 ]
1817- target_x_scroll_pixels = source_x_prop * target_pane .max_document_width * target_pane .zoom_level
1818- target_pane ._apply_scroll (target_x_scroll_pixels , target_y_scroll_pixels )
1830+ prev_distance = self .scroll_distance
1831+ distance = target_word_y_content_coord - source_y
1832+ prev_target_y = self .scroll_target_y
1833+ is_target_word_visible = (target_word_y_content_coord > target_pane .get_current_view_coords ()[1 ] and target_word_y_content_coord < target_pane .get_current_view_coords ()[1 ]+ target_pane .canvas .winfo_height ())
1834+ #print("target_y_scroll_delta: ",target_y_scroll_pixels-prev_target_y)
1835+ #print("target y", target_y_scroll_pixels)
1836+ #print("same direction? ", (source_y-prev_scroll_y)*(target_y_scroll_pixels-prev_target_y)>0)
1837+ #print("is_target_word_visible?",is_target_word_visible)
1838+ if (source_y - prev_scroll_y )* (target_y_scroll_pixels - prev_target_y )> 0 or not is_target_word_visible :
1839+ #print("scrolled!")
1840+ self .scroll_distance = distance
1841+ self .scroll_target_y = target_y_scroll_pixels
1842+ source_x_prop = source_pane .canvas .xview ()[0 ]
1843+ target_x_scroll_pixels = source_x_prop * target_pane .max_document_width * target_pane .zoom_level
1844+ target_pane ._apply_scroll (target_x_scroll_pixels , target_y_scroll_pixels )
18191845 #else:
18201846 elif 0 :#don't scroll target pane if no common words are found in the source pane
18211847 source_x_prop , source_y_prop = source_pane .canvas .xview ()[0 ], source_pane .canvas .yview ()[0 ]
0 commit comments