Menu

#183 Ctrl+Tab Switch - cycle through recently active tabs

development
open
nobody
None
5
2022-10-11
2020-01-06
No

I have created a patch for Nedit 5.6 that changes the Ctrl+Tab[/Ctrl+Shift+Tab] tab switch behaviour to cycle through all multiple recently active tabs (by most recently active to least recently active[/vice versa]). These keyboard shortcuts are commonly used in modern text editors (e.g. MS Visual Studio). See https://sourceforge.net/p/nedit/patches/107

Limitations:
- The only issue I have detected while testing the patch occurred when tabs are closed in a particular fashion (recency/switch order is not guaranteed).

Usage:
- To utilise the patch, apply it to the nedit-5.6 source (eg nedit_5.6~cvs20081118.orig.tar.gz), then modify a .Xresources file (and place in $HOME). The .Xresources file should include the following:

NEdit*text.Translations: #override \
~Shift Ctrl<KeyPress>Tab:switch_documents()\n\
~Shift Ctrl<KeyRelease>Tab:switch_documents_null()\n\
~Shift Ctrl<KeyRelease>:switch_documents_end()\n\
Shift Ctrl<KeyPress>Tab:switch_documents_reverse()\n\

Here is a summary of the changes;

menu.c;

~l105: 
#ifdef SWITCH_DOCUMENTS_PATCH
void initialiseSwitchDocumentsListWrapper();
#endif

~l323:
#ifdef SWITCH_DOCUMENTS_PATCH   
static void switchDocumentsAP(Widget w, XEvent *event, String *args,
    Cardinal *nArgs);
static void switchDocumentsReverseAP(Widget w, XEvent *event, String *args,
    Cardinal *nArgs);
static void switchDocumentsEndAP(Widget w, XEvent *event, String *args,
    Cardinal *nArgs);
static void switchDocumentsNullAP(Widget w, XEvent *event, String *args,
    Cardinal *nArgs);           
#endif  

~l545:
#ifdef SWITCH_DOCUMENTS_PATCH    
    {"switch_documents", switchDocumentsAP},
    {"switch_documents_reverse", switchDocumentsReverseAP},
    {"switch_documents_end", switchDocumentsEndAP},
    {"switch_documents_null", switchDocumentsNullAP},        
#endif 

~l615:
#ifdef SWITCH_DOCUMENTS_PATCH
void initialiseSwitchDocumentsListWrapper()
{
    initialiseSwitchDocumentsList();
}
#endif

~l3578:
#ifdef SWITCH_DOCUMENTS_PATCH
static void switchDocumentsAP(Widget w, XEvent *event, String *args,
    Cardinal *nArgs)
{    
    switchDocuments(WidgetToWindow(w), SWITCH_DOCUMENTS_PATCH_DIRECTION_NORMAL);    
}

static void switchDocumentsReverseAP(Widget w, XEvent *event, String *args,
    Cardinal *nArgs)
{    
    switchDocuments(WidgetToWindow(w), SWITCH_DOCUMENTS_PATCH_DIRECTION_REVERSE);    
}

static void switchDocumentsEndAP(Widget w, XEvent *event, String *args,
    Cardinal *nArgs)
{    
    switchDocumentsEnd(WidgetToWindow(w));    
}

static void switchDocumentsNullAP(Widget w, XEvent *event, String *args,
    Cardinal *nArgs)
{    

}
#endif


nedit.c;

~l414: 
    #ifdef SWITCH_DOCUMENTS_PATCH
    initialiseSwitchDocumentsListWrapper();
    #endif


nedit.h;

~l48: 
#define SWITCH_DOCUMENTS_PATCH


window.c;

~l197: 
#ifdef SWITCH_DOCUMENTS_PATCH

typedef struct SwitchDocumentsListNode
{
    int windowIDdebug;
    WindowInfo * focusDocument;
    struct SwitchDocumentsListNode* next;
    struct SwitchDocumentsListNode* previous;
} SwitchDocumentsList;

static SwitchDocumentsList* firstNodeInSwitchDocumentsList;
static SwitchDocumentsList* activeNodeInSwitchDocumentsList;
int switchDocumentsListTraverseDisableMarkActiveDocument;
struct timeval switchDocumentsClockLastSave;
int switchingDocumentsAtPresent;

struct timeval getTimeNow()
{
    struct timeval timenow;
    gettimeofday(&timenow, NULL);
    return timenow;
}

void printWindowList()
{
    printf("*****printWindowList:******\n");
    printf("firstNodeInSwitchDocumentsList = %d\n", firstNodeInSwitchDocumentsList->windowIDdebug);
    printf("activeNodeInSwitchDocumentsList = %d\n", activeNodeInSwitchDocumentsList->windowIDdebug);
    SwitchDocumentsList* currentNode = firstNodeInSwitchDocumentsList;
    WindowInfo *win;   

    while(currentNode->next != NULL)
    {
        printf("\tcurrentNode = %d, currentNode pointer = %d\n", currentNode->windowIDdebug, currentNode->focusDocument);
        currentNode = currentNode->next;
    }
}

void initialiseSwitchDocumentsList()
{
    switchDocumentsClockLastSave = getTimeNow();
    firstNodeInSwitchDocumentsList = malloc(sizeof(SwitchDocumentsList));
    firstNodeInSwitchDocumentsList->windowIDdebug = 0;
    firstNodeInSwitchDocumentsList->focusDocument = NULL;
    firstNodeInSwitchDocumentsList->next = NULL;
    firstNodeInSwitchDocumentsList->previous = NULL;
    activeNodeInSwitchDocumentsList = firstNodeInSwitchDocumentsList;
    switchDocumentsListTraverseDisableMarkActiveDocument = 0;
    switchingDocumentsAtPresent = 0;
}

SwitchDocumentsList* switchDocumentsGetLastNodeInList()
{
    SwitchDocumentsList* currentNode = firstNodeInSwitchDocumentsList;
    SwitchDocumentsList* lastNodeInList = currentNode;
    while(currentNode->next != NULL)
    {
        lastNodeInList = currentNode;
        currentNode = currentNode->next;
    }
    return lastNodeInList;
}

SwitchDocumentsList* switchDocumentsListRemoveNode(SwitchDocumentsList* nodeToRemove)
{   
    if(nodeToRemove->next != NULL)
    {
    if(nodeToRemove->previous == NULL)
    {/*nodeToRemove is first node in list*/

            nodeToRemove->next->previous = NULL;
        firstNodeInSwitchDocumentsList = nodeToRemove->next;

    }
    else
    {/*nodeToRemove is not first node in list*/
        nodeToRemove->previous->next = nodeToRemove->next;
        nodeToRemove->next->previous = nodeToRemove->previous;
    }
    free(nodeToRemove);
    }
    else
    {
    printf("switchDocumentsListRemoveNode() error: removing dummy node is impossible: (nodeToRemove->next == NULL)\n");
    exit(0);
    }   

    SwitchDocumentsList* lastNodeInList = switchDocumentsGetLastNodeInList();

    return lastNodeInList;     /*update activeNodeInSwitchDocumentsList (restore to last document in list)*/
}

SwitchDocumentsList* switchDocumentsListRemove(WindowInfo * focusDocument)
{
    SwitchDocumentsList* currentNode = firstNodeInSwitchDocumentsList;
    SwitchDocumentsList* nodeToRemove = NULL;
    int foundNodeToRemove = 0;
    while(currentNode->next != NULL)
    { 
        if(currentNode->focusDocument == focusDocument)
    {
        /*found node to remove*/
        nodeToRemove = currentNode;
        foundNodeToRemove = 1;
    }
        currentNode = currentNode->next;
    }
    SwitchDocumentsList* lastNodeInList = currentNode;
    if(foundNodeToRemove)
    {
         lastNodeInList = switchDocumentsListRemoveNode(nodeToRemove);
    }
    else
    {
    }
    return lastNodeInList;
}

SwitchDocumentsList* switchDocumentsListAdd(WindowInfo * focusDocument)
{
    switchDocumentsListRemove(focusDocument);   /*remove previous instance if existent*/

    SwitchDocumentsList* currentNode = firstNodeInSwitchDocumentsList;
    int lastNewWindowIDdebug = 0;
    while(currentNode->next != NULL)
    {
    lastNewWindowIDdebug = currentNode->windowIDdebug;
        currentNode = currentNode->next;
    }
    currentNode->windowIDdebug = lastNewWindowIDdebug+1;
    currentNode->focusDocument = focusDocument;
    SwitchDocumentsList* newNode = malloc(sizeof(SwitchDocumentsList));
    newNode->focusDocument = NULL;
    newNode->next = NULL;
    newNode->previous = currentNode;
    currentNode->next = newNode; 

    return currentNode;
}

#endif

~l946:
    #ifdef SWITCH_DOCUMENTS_PATCH
    activeNodeInSwitchDocumentsList = switchDocumentsListAdd(window);  
    #endif

~l3949:
    #ifdef SWITCH_DOCUMENTS_PATCH
    if(switchDocumentsListTraverseDisableMarkActiveDocument == 0)
    {
    if(switchingDocumentsAtPresent == 1)
    {/*in strange case user is switching documents while creating a new window for example?*/
            activeNodeInSwitchDocumentsList = switchDocumentsListAdd(activeNodeInSwitchDocumentsList->focusDocument);   
        switchingDocumentsAtPresent = 0; 
    } 
        activeNodeInSwitchDocumentsList = switchDocumentsListAdd(inFocusDocument);
    }   
    #endif

~l4025:
#ifdef SWITCH_DOCUMENTS_PATCH

void switchDocumentsEnd(WindowInfo *window)
{
    if(switchingDocumentsAtPresent == 1)
    {
            activeNodeInSwitchDocumentsList = switchDocumentsListAdd(activeNodeInSwitchDocumentsList->focusDocument);
        switchingDocumentsAtPresent = 0;
    }
}
/*
** Switch through last active windows
*/
void switchDocuments(WindowInfo *window, int direction)
{
    WindowInfo *win;

    if((activeNodeInSwitchDocumentsList->previous != NULL) || (activeNodeInSwitchDocumentsList->next->next != NULL))
    {
    if(direction == SWITCH_DOCUMENTS_PATCH_DIRECTION_NORMAL)
    {
        if(activeNodeInSwitchDocumentsList->previous != NULL)
        {
        activeNodeInSwitchDocumentsList = activeNodeInSwitchDocumentsList->previous;
        }
        else
        {/*go around in circles...*/
        activeNodeInSwitchDocumentsList = switchDocumentsGetLastNodeInList();
        }
    }
    else if(direction != SWITCH_DOCUMENTS_PATCH_DIRECTION_NORMAL)
    {
        if(activeNodeInSwitchDocumentsList->next->next != NULL)
        {
        activeNodeInSwitchDocumentsList = activeNodeInSwitchDocumentsList->next;
        }
        else
        {/*go around in circles...*/
        activeNodeInSwitchDocumentsList = firstNodeInSwitchDocumentsList;
        }   
    }
    }

    switchingDocumentsAtPresent = 1;

    for(win = WindowList; win; win=win->next)
        if (activeNodeInSwitchDocumentsList->focusDocument == win)
        break;

    if (!win)
        return;

    switchDocumentsListTraverseDisableMarkActiveDocument = 1;

    if (window->shell == win->shell)
    RaiseDocument(win);
    else
        RaiseDocumentWindow(win);

    switchDocumentsListTraverseDisableMarkActiveDocument = 0;   
}
#endif

~l4285:
    #ifdef SWITCH_DOCUMENTS_PATCH
    switchDocumentsListRemove(window);   
    #endif


window.h;

~l87: 
#ifdef SWITCH_DOCUMENTS_PATCH
#define SWITCH_DOCUMENTS_PATCH_DIRECTION_NORMAL (1)
#define SWITCH_DOCUMENTS_PATCH_DIRECTION_REVERSE (2)
void switchDocuments(WindowInfo *window, int direction);
void switchDocumentsEnd(WindowInfo *window);
#endif
1 Attachments

Discussion


Log in to post a comment.