scrolling groups, take 2

Bert Wesarg wesarg at informatik.uni-halle.de
Thu Jul 5 17:16:42 CEST 2007


Hello,

here is a small update patch.

Changes:

 * fix none working menu entries
 * dim the "Detach" menu entry, if the current pane is not in a group

Regards
Bert
-------------- next part --------------
diff -ur nedit.orig/doc/help.etx nedit.syncscroll/doc/help.etx
--- nedit.orig/doc/help.etx	2007-07-02 18:26:15.663198719 +0200
+++ nedit.syncscroll/doc/help.etx	2007-07-03 22:25:01.514574712 +0200
@@ -2464,6 +2464,9 @@
 **$show_line_numbers**
   Whether line numbers are shown next to the text.
 
+**$scroll_group**
+  True if this text pane is in an syncronized scroll group;
+
 **$show_matching**
   Contains the current preference for showing matching pairs,
   such as "[]" and "{}" pairs. Can be "off", "delimiter", or "range".
@@ -3254,6 +3257,13 @@
   A value of 0 turns it off and a value of 1 turns it on.
   If no parameters are supplied the option is toggled.
 
+**set_scroll_group( [window_name] [, paneIndex] )**
+  Include this window to the same sync scroll group as window_name and
+  possibly remove this window from the previously group.
+  If no window_name is given or window_name is the empty string remove
+  this window from any group. If paneIndex is not given or paneIndex == -1
+  use the last focused pane from the window, else a valid pane index.
+
 **set_show_line_numbers( [0 | 1] )**
   Show or hide line numbers for the current window.
   A value of 0 turns it off and a value of 1 turns it on.
diff -ur nedit.orig/source/highlightData.c nedit.syncscroll/source/highlightData.c
--- nedit.orig/source/highlightData.c	2007-07-02 18:26:16.497269298 +0200
+++ nedit.syncscroll/source/highlightData.c	2007-07-03 22:26:03.701987020 +0200
@@ -550,10 +550,10 @@
         README:\"NEdit Macro syntax highlighting patterns, version 2.6, maintainer Thorsten Haude, nedit at thorstenhau.de\":::Flag::D\n\
         Comment:\"#\":\"$\"::Comment::\n\
         Built-in Misc Vars:\"(?<!\\Y)\\$(?:active_pane|args|calltip_ID|column|cursor|display_width|empty_array|file_name|file_path|language_mode|line|locked|max_font_width|min_font_width|modified|n_display_lines|n_panes|rangeset_list|read_only|selection_(?:start|end|left|right)|server_name|text_length|top_line)>\":::Identifier::\n\
-        Built-in Pref Vars:\"(?<!\\Y)\\$(?:auto_indent|em_tab_dist|file_format|font_name|font_name_bold|font_name_bold_italic|font_name_italic|highlight_syntax|incremental_backup|incremental_search_line|make_backup_copy|match_syntax_based|overtype_mode|show_line_numbers|show_matching|statistics_line|tab_dist|use_tabs|wrap_margin|wrap_text)>\":::Identifier2::\n\
+        Built-in Pref Vars:\"(?<!\\Y)\\$(?:auto_indent|em_tab_dist|file_format|font_name|font_name_bold|font_name_bold_italic|font_name_italic|highlight_syntax|incremental_backup|incremental_search_line|make_backup_copy|match_syntax_based|overtype_mode|show_line_numbers|show_matching|statistics_line|scroll_group|tab_dist|use_tabs|wrap_margin|wrap_text)>\":::Identifier2::\n\
         Built-in Special Vars:\"(?<!\\Y)\\$(?:[1-9]|list_dialog_button|n_args|read_status|search_end|shell_cmd_status|string_dialog_button|sub_sep)>\":::String1::\n\
         Built-in Subrs:\"<(?:append_file|beep|calltip|clipboard_to_string|dialog|focus_window|get_character|get_pattern_(by_name|at_pos)|get_range|get_selection|get_style_(by_name|at_pos)|getenv|kill_calltip|length|list_dialog|max|min|rangeset_(?:add|create|destroy|get_by_name|includes|info|invert|range|set_color|set_mode|set_name|subtract)|read_file|replace_in_string|replace_range|replace_selection|replace_substring|search|search_string|select|select_rectangle|set_cursor_pos|set_language_mode|set_locked|shell_command|split|string_compare|string_dialog|string_to_clipboard|substring|t_print|tolower|toupper|valid_number|write_file)>\":::Subroutine::\n\
-        Menu Actions:\"<(?:new|open|open-dialog|open_dialog|open-selected|open_selected|close|save|save-as|save_as|save-as-dialog|save_as_dialog|revert-to-saved|revert_to_saved|revert_to_saved_dialog|include-file|include_file|include-file-dialog|include_file_dialog|load-macro-file|load_macro_file|load-macro-file-dialog|load_macro_file_dialog|load-tags-file|load_tags_file|load-tags-file-dialog|load_tags_file_dialog|unload_tags_file|load_tips_file|load_tips_file_dialog|unload_tips_file|print|print-selection|print_selection|exit|undo|redo|delete|select-all|select_all|shift-left|shift_left|shift-left-by-tab|shift_left_by_tab|shift-right|shift_right|shift-right-by-tab|shift_right_by_tab|find|find-dialog|find_dialog|find-again|find_again|find-selection|find_selection|find_incremental|start_incremental_find|replace|replace-dialog|replace_dialog|replace-all|replace_all|replace-in-selection|replace_in_selection|replace-again|replace_again|replace_find|replace_find_same|replace_find_again|goto-line-number|goto_line_number|goto-line-number-dialog|goto_line_number_dialog|goto-selected|goto_selected|mark|mark-dialog|mark_dialog|goto-mark|goto_mark|goto-mark-dialog|goto_mark_dialog|match|select_to_matching|goto_matching|find-definition|find_definition|show_tip|split-window|split_window|close-pane|close_pane|uppercase|lowercase|fill-paragraph|fill_paragraph|control-code-dialog|control_code_dialog|filter-selection-dialog|filter_selection_dialog|filter-selection|filter_selection|execute-command|execute_command|execute-command-dialog|execute_command_dialog|execute-command-line|execute_command_line|shell-menu-command|shell_menu_command|macro-menu-command|macro_menu_command|bg_menu_command|post_window_bg_menu|beginning-of-selection|beginning_of_selection|end-of-selection|end_of_selection|repeat_macro|repeat_dialog|raise_window|focus_pane|set_statistics_line|set_incremental_search_line|set_show_line_numbers|set_auto_indent|set_wrap_text|set_wrap_margin|set_highlight_syntax|set_make_backup_copy|set_incremental_backup|set_show_matching|set_match_syntax_based|set_overtype_mode|set_locked|set_tab_dist|set_em_tab_dist|set_use_tabs|set_fonts|set_language_mode)(?=\\s*\\()\":::Subroutine::\n\
+        Menu Actions:\"<(?:new|open|open-dialog|open_dialog|open-selected|open_selected|close|save|save-as|save_as|save-as-dialog|save_as_dialog|revert-to-saved|revert_to_saved|revert_to_saved_dialog|include-file|include_file|include-file-dialog|include_file_dialog|load-macro-file|load_macro_file|load-macro-file-dialog|load_macro_file_dialog|load-tags-file|load_tags_file|load-tags-file-dialog|load_tags_file_dialog|unload_tags_file|load_tips_file|load_tips_file_dialog|unload_tips_file|print|print-selection|print_selection|exit|undo|redo|delete|select-all|select_all|shift-left|shift_left|shift-left-by-tab|shift_left_by_tab|shift-right|shift_right|shift-right-by-tab|shift_right_by_tab|find|find-dialog|find_dialog|find-again|find_again|find-selection|find_selection|find_incremental|start_incremental_find|replace|replace-dialog|replace_dialog|replace-all|replace_all|replace-in-selection|replace_in_selection|replace-again|replace_again|replace_find|replace_find_same|replace_find_again|goto-line-number|goto_line_number|goto-line-number-dialog|goto_line_number_dialog|goto-selected|goto_selected|mark|mark-dialog|mark_dialog|goto-mark|goto_mark|goto-mark-dialog|goto_mark_dialog|match|select_to_matching|goto_matching|find-definition|find_definition|show_tip|split-window|split_window|close-pane|close_pane|uppercase|lowercase|fill-paragraph|fill_paragraph|control-code-dialog|control_code_dialog|filter-selection-dialog|filter_selection_dialog|filter-selection|filter_selection|execute-command|execute_command|execute-command-dialog|execute_command_dialog|execute-command-line|execute_command_line|shell-menu-command|shell_menu_command|macro-menu-command|macro_menu_command|bg_menu_command|post_window_bg_menu|beginning-of-selection|beginning_of_selection|end-of-selection|end_of_selection|repeat_macro|repeat_dialog|raise_window|focus_pane|set_statistics_line|set_incremental_search_line|set_show_line_numbers|set_auto_indent|set_wrap_text|set_wrap_margin|set_highlight_syntax|set_make_backup_copy|set_incremental_backup|set_show_matching|set_match_syntax_based|set_overtype_mode|set_scroll_group|set_locked|set_tab_dist|set_em_tab_dist|set_use_tabs|set_fonts|set_language_mode)(?=\\s*\\()\":::Subroutine::\n\
         Text Actions:\"<(?:self-insert|self_insert|grab-focus|grab_focus|extend-adjust|extend_adjust|extend-start|extend_start|extend-end|extend_end|secondary-adjust|secondary_adjust|secondary-or-drag-adjust|secondary_or_drag_adjust|secondary-start|secondary_start|secondary-or-drag-start|secondary_or_drag_start|process-bdrag|process_bdrag|move-destination|move_destination|move-to|move_to|move-to-or-end-drag|move_to_or_end_drag|end_drag|copy-to|copy_to|copy-to-or-end-drag|copy_to_or_end_drag|exchange|process-cancel|process_cancel|paste-clipboard|paste_clipboard|copy-clipboard|copy_clipboard|cut-clipboard|cut_clipboard|copy-primary|copy_primary|cut-primary|cut_primary|newline|newline-and-indent|newline_and_indent|newline-no-indent|newline_no_indent|delete-selection|delete_selection|delete-previous-character|delete_previous_character|delete-next-character|delete_next_character|delete-previous-word|delete_previous_word|delete-next-word|delete_next_word|delete-to-start-of-line|delete_to_start_of_line|delete-to-end-of-line|delete_to_end_of_line|forward-character|forward_character|backward-character|backward_character|key-select|key_select|process-up|process_up|process-down|process_down|process-shift-up|process_shift_up|process-shift-down|process_shift_down|process-home|process_home|forward-word|forward_word|backward-word|backward_word|forward-paragraph|forward_paragraph|backward-paragraph|backward_paragraph|beginning-of-line|beginning_of_line|end-of-line|end_of_line|beginning-of-file|beginning_of_file|end-of-file|end_of_file|next-page|next_page|previous-page|previous_page|page-left|page_left|page-right|page_right|toggle-overstrike|toggle_overstrike|scroll-up|scroll_up|scroll-down|scroll_down|scroll_left|scroll_right|scroll-to-line|scroll_to_line|select-all|select_all|deselect-all|deselect_all|focusIn|focusOut|process-return|process_return|process-tab|process_tab|insert-string|insert_string|mouse_pan)>\":::Subroutine::\n\
         Keyword:\"<(?:break|continue|define|delete|else|for|if|in|return|while)>\":::Keyword::\n\
         Braces:\"[{}\\[\\]]\":::Keyword::\n\
diff -ur nedit.orig/source/macro.c nedit.syncscroll/source/macro.c
--- nedit.orig/source/macro.c	2007-07-02 18:26:16.529272006 +0200
+++ nedit.syncscroll/source/macro.c	2007-07-03 22:31:32.708538832 +0200
@@ -297,6 +297,8 @@
     DataValue *result, char **errMsg);
 static int overTypeModeMV(WindowInfo *window, DataValue *argList, int nArgs,
     DataValue *result, char **errMsg);
+static int scrollGroupMV(WindowInfo *window, DataValue *argList, int nArgs,
+    DataValue *result, char **errMsg);
 static int readOnlyMV(WindowInfo *window, DataValue *argList, int nArgs,
     DataValue *result, char **errMsg);
 static int lockedMV(WindowInfo *window, DataValue *argList, int nArgs,
@@ -449,7 +451,7 @@
         statisticsLineMV, incSearchLineMV, showLineNumbersMV,
         autoIndentMV, wrapTextMV, highlightSyntaxMV,
         makeBackupCopyMV, incBackupMV, showMatchingMV,
-        overTypeModeMV, readOnlyMV, lockedMV, fileFormatMV,
+        overTypeModeMV, scrollGroupMV, readOnlyMV, lockedMV, fileFormatMV,
         fontNameMV, fontNameItalicMV,
         fontNameBoldMV, fontNameBoldItalicMV, subscriptSepMV,
         minFontWidthMV, maxFontWidthMV, topLineMV, numDisplayLinesMV,
@@ -467,7 +469,7 @@
         "$statistics_line", "$incremental_search_line", "$show_line_numbers",
         "$auto_indent", "$wrap_text", "$highlight_syntax",
         "$make_backup_copy", "$incremental_backup", "$show_matching",
-        "$overtype_mode", "$read_only", "$locked", "$file_format",
+        "$overtype_mode", "$scroll_group", "$read_only", "$locked", "$file_format",
         "$font_name", "$font_name_italic",
         "$font_name_bold", "$font_name_bold_italic", "$sub_sep",
         "$min_font_width", "$max_font_width", "$top_line", "$n_display_lines",
@@ -4298,6 +4300,15 @@
     return True;
 }
 
+static int scrollGroupMV(WindowInfo *window, DataValue *argList, int nArgs,
+    DataValue *result, char **errMsg)
+{
+    result->tag = INT_TAG;
+    result->val.n = !TextScrollGroupIsSingle((TextWidget)window->lastFocus);
+
+    return True;
+}
+
 static int readOnlyMV(WindowInfo *window, DataValue *argList, int nArgs,
     DataValue *result, char **errMsg)
 {
diff -ur nedit.orig/source/menu.c nedit.syncscroll/source/menu.c
--- nedit.orig/source/menu.c	2007-07-02 18:26:16.812295955 +0200
+++ nedit.syncscroll/source/menu.c	2007-07-05 17:04:57.780806217 +0200
@@ -311,6 +311,12 @@
 	Cardinal *nArgs);
 static void moveDocumentDialogAP(Widget w, XEvent *event, String *args,
 	Cardinal *nArgs);
+static void scrollGroupDetachAP(Widget w, XEvent *event, String *args,
+	Cardinal *nArgs);
+static void scrollGroupDetachDialogAP(Widget w, XEvent *event, String *args,
+	Cardinal *nArgs);
+static void scrollGroupDialogAP(Widget w, XEvent *event, String *args,
+	Cardinal *nArgs);
 static void nextDocumentAP(Widget w, XEvent *event, String *args,
 	Cardinal *nArgs);
 static void prevDocumentAP(Widget w, XEvent *event, String *args,
@@ -407,6 +413,8 @@
     Cardinal *nArgs);
 static void setOvertypeModeAP(Widget w, XEvent *event, String *args,
     Cardinal *nArgs);
+static void setScrollGroupAP(Widget w, XEvent *event, String *args,
+    Cardinal *nArgs);
 static void setLockedAP(Widget w, XEvent *event, String *args,
     Cardinal *nArgs);
 static void setUseTabsAP(Widget w, XEvent *event, String *args,
@@ -527,6 +535,9 @@
     {"detach_document", detachDocumentAP},
     {"detach_document_dialog", detachDocumentDialogAP},
     {"move_document_dialog", moveDocumentDialogAP},
+    {"scroll_group_detach", scrollGroupDetachAP},
+    {"scroll_group_detach_dialog", scrollGroupDetachDialogAP},
+    {"scroll_group_dialog", scrollGroupDialogAP},
     {"next_document", nextDocumentAP},
     {"previous_document", prevDocumentAP},
     {"last_document", lastDocumentAP},
@@ -577,6 +588,7 @@
     {"set_show_matching", setShowMatchingAP},
     {"set_match_syntax_based", setMatchSyntaxBasedAP},
     {"set_overtype_mode", setOvertypeModeAP},
+    {"set_scroll_group", setScrollGroupAP},
     {"set_locked", setLockedAP},
     {"set_tab_dist", setTabDistAP},
     {"set_em_tab_dist", setEmTabDistAP},
@@ -1222,6 +1234,15 @@
     window->moveDocumentItem = createMenuItem(menuPane, "moveDocument",
     	    "Move Tab To...", 'M', doActionCB, "move_document_dialog", SHORT);
     XtSetSensitive(window->moveDocumentItem, False);
+
+    btn = createMenuSeparator(menuPane, "sep02", SHORT);
+    XtVaSetValues(btn, XmNuserData, PERMANENT_MENU_ITEM, NULL);
+    window->scrollGroupDetachItem = createMenuItem(menuPane, "scrollGroupDetach",
+            "Detach from Scroll Group", 'S', doActionCB, "scroll_group_detach", SHORT);
+    XtSetSensitive(window->scrollGroupDetachItem, False);
+    window->scrollGroupItem = createMenuItem(menuPane, "scrollGroupDialog",
+            "Join Document To Scroll Group...", 'G', doActionCB, "scroll_group_dialog", SHORT);
+
     btn = createMenuSeparator(menuPane, "sep1", SHORT);
     XtVaSetValues(btn, XmNuserData, PERMANENT_MENU_ITEM, NULL);
     
@@ -3531,6 +3552,41 @@
     MoveDocumentDialog(WidgetToWindow(w));
 }
 
+static void scrollGroupDetachDialogAP(Widget w, XEvent *event, String *args,
+                                      Cardinal *nArgs)
+{
+    WindowInfo *window = WidgetToWindow(w);
+    TextWidget text = (TextWidget)window->lastFocus;
+    int resp;
+
+    if (TextScrollGroupIsSingle(text))
+        return;
+
+    resp = DialogF(DF_QUES, window->shell, 2, "Detach %s from Scroll Group?",
+                   "Detach", "Cancel", window->filename);
+
+    if (resp == 1)
+        TextScrollGroupDetach(text);
+}
+
+static void scrollGroupDetachAP(Widget w, XEvent *event, String *args,
+                                Cardinal *nArgs)
+{
+    WindowInfo *window = WidgetToWindow(w);
+    TextWidget text = (TextWidget)window->lastFocus;
+
+    if (TextScrollGroupIsSingle(text))
+        return;
+
+    TextScrollGroupDetach(text);
+}
+
+static void scrollGroupDialogAP(Widget w, XEvent *event, String *args,
+                                Cardinal *nArgs)
+{
+    ScrollGroupDialog(WidgetToWindow(w), -1);
+}
+
 static void nextDocumentAP(Widget w, XEvent *event, String *args,
 	Cardinal *nArgs)
 {
@@ -4182,6 +4238,85 @@
     SetOverstrike(window, newState);
 }
 
+static void setScrollGroupAP(Widget w, XEvent *event, String *args,
+                             Cardinal *nArgs)
+{
+    WindowInfo *window = WidgetToWindow(w);
+    WindowInfo *win;
+    TextWidget text, targetText = NULL;
+
+    if (*nArgs > 2) {
+        fprintf(stderr, "NEdit: set_scroll_group too many arguments\n");
+        return;
+    }
+
+    text = (TextWidget)window->lastFocus;
+
+    TextScrollGroupDetach(text);
+
+    win = NULL;
+    if (*nArgs == 0 || !strcmp(args[0], "")) {
+        return;
+    }
+    else if (!strcmp(args[0], "last")) {
+        win = WindowList;
+    }
+    else if (!strcmp(args[0], "next")) {
+        win = window->next;
+    }
+    else if (strlen(args[0]) >= MAXPATHLEN) {
+        fprintf(stderr, "NEdit: Pathname too long in set_scroll_group()\n");
+        return;
+    }
+    else {
+        char fullname[MAXPATHLEN];
+        char normalizedString[MAXPATHLEN];
+        /* just use the plain name as supplied */
+        for (win = WindowList; win != NULL; win = win->next) {
+            sprintf(fullname, "%s%s", win->path, win->filename);
+            if (!strcmp(args[0], fullname)) {
+                break;
+            }
+        }
+        /* didn't work? try normalizing the string passed in */
+        if (win == NULL) {
+            strncpy(normalizedString, args[0], MAXPATHLEN);
+            normalizedString[MAXPATHLEN-1] = '\0';
+            if (1 == NormalizePathname(normalizedString)) {
+                /*  Something is broken with the input pathname. */
+                fprintf(stderr, "NEdit: Pathname broken in set_scroll_group()\n");
+                return;
+            }
+            for (win = WindowList; win != NULL; win = win->next) {
+                sprintf(fullname, "%s%s", win->path, win->filename);
+                if (!strcmp(normalizedString, fullname))
+                    break;
+            }
+        }
+    }
+
+    if (!win)
+        return;
+
+    targetText = NULL;
+    if (*nArgs > 1) {
+        int paneIndex = atoi(args[1]);
+        if (paneIndex >= 0) {
+            targetText = (TextWidget)GetPaneByIndex(win, paneIndex);
+            if (NULL == targetText) {
+                fprintf(stderr, "NEdit: set_scroll_group wrong argument for 2nd parameter\n");
+                return;
+            }
+        }
+    }
+    if (NULL == targetText) {
+        targetText = (TextWidget)win->lastFocus;
+    }
+
+    if (text != targetText)
+        TextScrollGroupJoinWith(text, targetText);
+}
+
 static void setLockedAP(Widget w, XEvent *event, String *args,
     Cardinal *nArgs)
 {
diff -ur nedit.orig/source/nedit.h nedit.syncscroll/source/nedit.h
--- nedit.orig/source/nedit.h	2007-07-02 18:26:16.618279538 +0200
+++ nedit.syncscroll/source/nedit.h	2007-07-03 22:09:34.528709431 +0200
@@ -443,6 +443,8 @@
     Widget	moveDocumentItem;
     Widget	contextMoveDocumentItem;
     Widget	contextDetachDocumentItem;
+    Widget	scrollGroupDetachItem;
+    Widget	scrollGroupItem;
     Widget  	bgMenuUndoItem;
     Widget  	bgMenuRedoItem;
 #ifdef SGI_CUSTOM
diff -ur nedit.orig/source/text.c nedit.syncscroll/source/text.c
--- nedit.orig/source/text.c	2007-07-02 18:26:16.302252796 +0200
+++ nedit.syncscroll/source/text.c	2007-07-03 22:33:54.153727988 +0200
@@ -80,6 +80,10 @@
 /* Length of delay in milliseconds for vertical autoscrolling */
 #define VERTICAL_SCROLL_DELAY 50
 
+typedef void (textActionProcCall)(Widget, XEvent*, String*, Cardinal*);
+
+static void syncScrollAP(textActionProcCall actionProc, Widget w,
+    	XEvent *event, String *args, Cardinal *nArgs);
 static void initialize(TextWidget request, TextWidget new);
 static void handleHidePointer(Widget w, XtPointer unused, 
         XEvent *event, Boolean *continue_to_dispatch);
@@ -877,6 +881,9 @@
         XtAddEventHandler((Widget)new, NEDIT_HIDE_CURSOR_MASK, False, 
                 handleHidePointer, (Opaque)NULL);
     }
+
+    new->nextScrollGroup = new;
+    new->prevScrollGroup = new;
 }
 
 /* Hide the pointer while the user is typing */
@@ -953,6 +960,8 @@
     XtRemoveAllCallbacks((Widget)w, textNdragStartCallback);
     XtRemoveAllCallbacks((Widget)w, textNdragEndCallback);
 
+    TextScrollGroupDetach(w);
+
 #ifndef NO_XMIM
     /* Unregister the widget from the input manager */
     XmImUnregister((Widget)w);
@@ -2910,7 +2919,7 @@
     }
 }
 
-static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
+static void _nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
 {
     textDisp *textD = ((TextWidget)w)->text.textD;
     textBuffer *buf = textD->buffer;
@@ -3007,7 +3016,12 @@
     }
 }
 
-static void previousPageAP(Widget w, XEvent *event, String *args,
+static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
+{
+    _nextPageAP(w, event, args, nArgs);
+}
+
+static void _previousPageAP(Widget w, XEvent *event, String *args,
 	Cardinal *nArgs)
 {
     textDisp *textD = ((TextWidget)w)->text.textD;
@@ -3092,6 +3106,12 @@
     }
 }
 
+static void previousPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
+{
+    _previousPageAP(w, event, args, nArgs);
+    
+}
+
 static void pageLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
 {
     textDisp *textD = ((TextWidget)w)->text.textD;
@@ -3183,7 +3203,25 @@
     }
 }
 
-static void scrollUpAP(Widget w, XEvent *event, String *args,
+/*
+** do synchronous scroll for this action.
+*/
+static void syncScrollAP(textActionProcCall actionProc, Widget w,
+    	XEvent *event, String *args, Cardinal *nArgs)
+{
+    TextWidget text = (TextWidget)w;
+    TextWidget t;
+
+    actionProc(w, event, args, nArgs);
+
+    if (text == text->nextScrollGroup)
+        return;
+
+    for (t = text->nextScrollGroup; t != text; t = t->nextScrollGroup)
+        actionProc((Widget)t, event, args, nArgs);
+}
+
+static void _scrollUpAP(Widget w, XEvent *event, String *args,
     	Cardinal *nArgs)
 {
     textDisp *textD = ((TextWidget)w)->text.textD;
@@ -3204,7 +3242,13 @@
     TextDSetScroll(textD, topLineNum-nLines, horizOffset);
 }
 
-static void scrollDownAP(Widget w, XEvent *event, String *args,
+static void scrollUpAP(Widget w, XEvent *event, String *args,
+    	Cardinal *nArgs)
+{
+    syncScrollAP(_scrollUpAP, w, event, args, nArgs);
+}
+
+static void _scrollDownAP(Widget w, XEvent *event, String *args,
     	Cardinal *nArgs)
 {
     textDisp *textD = ((TextWidget)w)->text.textD;
@@ -3225,6 +3269,12 @@
     TextDSetScroll(textD, topLineNum+nLines, horizOffset);
 }
 
+static void scrollDownAP(Widget w, XEvent *event, String *args,
+    	Cardinal *nArgs)
+{
+    syncScrollAP(_scrollDownAP, w, event, args, nArgs);
+}
+
 static void scrollLeftAP(Widget w, XEvent *event, String *args,
     	Cardinal *nArgs)
 {
@@ -4232,3 +4282,29 @@
     if (!silent)
         XBell(XtDisplay(w), 0);
 }
+
+void TextScrollGroupJoinWith(TextWidget new, TextWidget prev)
+{
+    TextWidget next = prev->nextScrollGroup;
+
+    next->prevScrollGroup = new;
+    prev->nextScrollGroup = new;
+    new->nextScrollGroup = next;
+    new->prevScrollGroup = prev;
+}
+
+void TextScrollGroupDetach(TextWidget old)
+{
+    TextWidget next = old->nextScrollGroup;
+    TextWidget prev = old->prevScrollGroup;
+
+    next->prevScrollGroup = prev;
+    prev->nextScrollGroup = next;
+    old->nextScrollGroup = old;
+    old->prevScrollGroup = old;
+}
+
+Boolean TextScrollGroupIsSingle(TextWidget text)
+{
+    return (text == text->nextScrollGroup);
+}
diff -ur nedit.orig/source/textDisp.c nedit.syncscroll/source/textDisp.c
--- nedit.orig/source/textDisp.c	2007-07-02 18:26:16.355257281 +0200
+++ nedit.syncscroll/source/textDisp.c	2007-07-03 22:35:09.685088728 +0200
@@ -2902,11 +2902,35 @@
     textDisp *textD = (textDisp *)clientData;
     int newValue = ((XmScrollBarCallbackStruct *)callData)->value;
     int lineDelta = newValue - textD->topLineNum;
-    
+    TextWidget text, t;
+
     if (lineDelta == 0)
         return;
     setScroll(textD, newValue, textD->horizOffset, False, True);
+
+    text = (TextWidget)textD->w;
+
+    if (text == text->nextScrollGroup)
+        return;
+
+    /* do synchronous scroll */
+    for (t = text->nextScrollGroup; t != text; t= t->nextScrollGroup) {
+        int newLine;
+        textDisp *tD = t->text.textD;
+        if (lineDelta > 0) {
+            newLine = tD->topLineNum +
+                      min(lineDelta, tD->nBufferLines -
+                                     tD->topLineNum -
+                                     tD->nVisibleLines + 2);
+        }
+        else {
+            newLine = tD->topLineNum + lineDelta;
+        }
+
+        setScroll(tD, newLine < 1 ? 1 : newLine, 0, True, True);
+    }
 }
+
 static void hScrollCB(Widget w, XtPointer clientData, XtPointer callData)
 {
     textDisp *textD = (textDisp *)clientData;
diff -ur nedit.orig/source/text.h nedit.syncscroll/source/text.h
--- nedit.orig/source/text.h	2007-07-02 18:26:16.199244079 +0200
+++ nedit.syncscroll/source/text.h	2007-07-03 21:52:41.955650466 +0200
@@ -169,6 +169,10 @@
 void ShowHidePointer(TextWidget w, Boolean hidePointer);
 void ResetCursorBlink(TextWidget textWidget, Boolean startsBlanked);
 
+void TextScrollGroupJoinWith(TextWidget new, TextWidget prev);
+void TextScrollGroupDetach(TextWidget text);
+Boolean TextScrollGroupIsSingle(TextWidget text);
+
 #ifdef VMS /* VMS linker doesn't like long names (>31 chars) */
 #define HandleAllPendingGraphicsExposeNoExposeEvents HandlePendingExpNoExpEvents
 #endif /* VMS */
diff -ur nedit.orig/source/textP.h nedit.syncscroll/source/textP.h
--- nedit.orig/source/textP.h	2007-07-02 18:26:16.247248141 +0200
+++ nedit.syncscroll/source/textP.h	2007-07-03 18:21:21.917827744 +0200
@@ -133,6 +133,8 @@
    CorePart        core;
    XmPrimitivePart primitive;
    TextPart        text;
+   struct _TextRec *nextScrollGroup;
+   struct _TextRec *prevScrollGroup;
 } TextRec;
 
 #endif /* NEDIT_TEXTP_H_INCLUDED */
diff -ur nedit.orig/source/window.c nedit.syncscroll/source/window.c
--- nedit.orig/source/window.c	2007-07-02 18:26:16.256248903 +0200
+++ nedit.syncscroll/source/window.c	2007-07-05 16:56:57.999893111 +0200
@@ -195,6 +195,7 @@
 static WindowInfo *inFocusDocument = NULL;  	/* where we are now */
 static WindowInfo *lastFocusDocument = NULL;	    	/* where we came from */
 static int DoneWithMoveDocumentDialog;
+static int DoneWithScrollGroupDialog;
 static void updateLineNumDisp(const WindowInfo* window);
 static int max(const int i1, const int i2);
 static void setGutterWidth(const WindowInfo* window, const unsigned reqCols);
@@ -1032,6 +1033,7 @@
         }
     }
     
+    TextScrollGroupDetach((TextWidget)window->textArea);
     /* remove the window from the global window list, update window menus */
     removeFromWindowList(window);
     InvalidateWindowMenus();
@@ -2407,6 +2409,8 @@
     
     /* Check for changes to read-only status and/or file modifications */
     CheckForChangesToFile(window);
+
+    XtSetSensitive(window->scrollGroupDetachItem, !TextScrollGroupIsSingle((TextWidget)w));
 }
 
 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData) 
@@ -4200,6 +4204,9 @@
                     textD->selectFGPixel, textD->selectBGPixel,
                     textD->highlightFGPixel,textD->highlightBGPixel,
                     textD->lineNumFGPixel, textD->cursorFGPixel);
+
+            TextScrollGroupJoinWith((TextWidget)window->textPanes[i],
+                                    (TextWidget)orgWin->textPanes[i]);
 	}
         
 	/* Set the minimum pane height in the new pane */
@@ -4351,10 +4358,13 @@
     window->inode = orgWin->inode;
     window->fileClosedAtom = orgWin->fileClosedAtom;
     orgWin->fileClosedAtom = None;
-    
+
     /* copy the text/split panes settings, cursor pos & selection */
     cloneTextPanes(window, orgWin);
-    
+
+    TextScrollGroupJoinWith((TextWidget)window->textArea,
+                            (TextWidget)orgWin->textArea);
+
     /* copy undo & redo list */
     window->undo = cloneUndoItems(orgWin->undo);
     window->redo = cloneUndoItems(orgWin->redo);
@@ -4639,6 +4649,133 @@
     XtDestroyWidget(dialog);	
 }
 
+static void scrollGroupDocumentCB(Widget dialog, WindowInfo *window,
+	XtPointer call_data)
+{
+    XmSelectionBoxCallbackStruct *cbs = (XmSelectionBoxCallbackStruct *) call_data;
+    DoneWithScrollGroupDialog = cbs->reason;
+}
+
+/*
+** present dialog for selecting a target window to sync this document
+** with. Do nothing if there is only one shell window opened.
+*/
+void ScrollGroupDialog(WindowInfo *window, int paneIndex)
+{
+    WindowInfo *win;
+    TextWidget text, targetText, *textList;
+    int i, nList = 0, nPanes = 0, ac;
+    char tmpStr[MAXPATHLEN + 64];
+    Widget parent, dialog, listBox;
+    XmString *list = NULL;
+    XmString popupTitle, s1;
+    Arg csdargs[20];
+    int *position_list, position_count;
+
+    if (paneIndex < 0)
+        text = (TextWidget)window->lastFocus;
+    else
+        text = (TextWidget)GetPaneByIndex(window, paneIndex);
+
+    nPanes = 0;
+    for (win = WindowList; win; win = win->next)
+        nPanes += win->nPanes + 1;
+    list = (XmStringTable)XtMalloc(nPanes * sizeof(XmString *));
+    textList = (TextWidget *)XtMalloc(nPanes * sizeof(TextWidget *));
+
+    for (win = WindowList; win; win = win->next) {
+        if (0 == win->nPanes) {
+            if (win == window)
+                continue;
+
+            sprintf(tmpStr, "%s%s",
+                    win->filenameSet ? win->path : "", win->filename);
+
+            list[nList] = XmStringCreateSimple(tmpStr);
+            textList[nList] = (TextWidget)win->lastFocus;
+            nList++;
+        }
+        else {
+            for (i = 0; i <= win->nPanes; i++) {
+                TextWidget t = i == 0 ? (TextWidget)win->textArea
+                                      : (TextWidget)win->textPanes[i - 1];
+                if (text == t)
+                    continue;
+
+                sprintf(tmpStr, "[%d] %s%s", i,
+                        win->filenameSet ? win->path : "", win->filename);
+
+                list[nList] = XmStringCreateSimple(tmpStr);
+                textList[nList] = t;
+                nList++;
+            }
+        }
+    }
+
+    /* stop here if there's no other window to move to */
+    if (!nList) {
+        XtFree((char *)list);
+        XtFree((char *)textList);
+        return;
+    }
+
+    /* create the dialog */
+    parent = window->shell;
+    popupTitle = XmStringCreateSimple("Scroll Group");
+    sprintf(tmpStr, "Sync scrolling %s with window/group", window->filename);
+    s1 = XmStringCreateSimple(tmpStr);
+    ac = 0;
+    XtSetArg(csdargs[ac], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); ac++;
+    XtSetArg(csdargs[ac], XmNdialogTitle, popupTitle); ac++;
+    XtSetArg(csdargs[ac], XmNlistLabelString, s1); ac++;
+    XtSetArg(csdargs[ac], XmNlistItems, list); ac++;
+    XtSetArg(csdargs[ac], XmNlistItemCount, nList); ac++;
+    XtSetArg(csdargs[ac], XmNvisibleItemCount, 12); ac++;
+    XtSetArg(csdargs[ac], XmNautoUnmanage, False); ac++;
+    dialog = CreateSelectionDialog(parent,"syncDocument",csdargs,ac);
+    XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
+    XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
+    XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_SELECTION_LABEL));
+    XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)scrollGroupDocumentCB, window);
+    XtAddCallback(dialog, XmNapplyCallback, (XtCallbackProc)scrollGroupDocumentCB, window);
+    XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)scrollGroupDocumentCB, window);
+    XmStringFree(s1);
+    XmStringFree(popupTitle);
+
+    /* free the window list */
+    for (i=0; i < nList; i++)
+        XmStringFree(list[i]);
+    XtFree((char *)list);
+
+    /* disable option if only one document in the window */
+    XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON));
+
+    s1 = MKSTRING("Join");
+    XtVaSetValues(dialog, XmNokLabelString, s1, NULL);
+    XmStringFree(s1);
+
+    /* default to the first window on the list */
+    listBox = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
+    XmListSelectPos(listBox, 1, True);
+
+    /* show the dialog */
+    DoneWithScrollGroupDialog = 0;
+    ManageDialogCenteredOnPointer(dialog);
+    while (!DoneWithScrollGroupDialog)
+        XtAppProcessEvent(XtWidgetToApplicationContext(parent), XtIMAll);
+
+    /* get the window to sync document into */
+    XmListGetSelectedPos(listBox, &position_list, &position_count);
+    targetText = textList[position_list[0] - 1];
+    XtFree((char *)position_list);
+    XtFree((char *)textList);
+    XtDestroyWidget(dialog);
+
+    /* now sync document */
+    if (DoneWithScrollGroupDialog == XmCR_OK)
+        TextScrollGroupJoinWith(text, targetText);
+}
+
 static void hideTooltip(Widget tab)
 {
     Widget tooltip = XtNameToWidget(tab, "*BubbleShell");
diff -ur nedit.orig/source/window.h nedit.syncscroll/source/window.h
--- nedit.orig/source/window.h	2007-07-02 18:26:16.199244079 +0200
+++ nedit.syncscroll/source/window.h	2007-07-03 22:37:31.260309676 +0200
@@ -92,6 +92,7 @@
 WindowInfo *MoveDocument(WindowInfo *toWindow, WindowInfo *window);
 WindowInfo *DetachDocument(WindowInfo *window);
 void MoveDocumentDialog(WindowInfo *window);
+void ScrollGroupDialog(WindowInfo *window, int paneIndex);
 WindowInfo* GetTopDocument(Widget w);
 Boolean IsTopDocument(const WindowInfo *window);
 int IsIconic(WindowInfo *window);


More information about the Develop mailing list