Insert dragable and resizeable subgraphs into a graph window

This code provides a hook function which makes it possible to drag and resize subwindows in a graph with the mouse. To see how this works create a graph and execute the following:

TestAppendSubGraph(mainGraphName, plotThisWave)

This will insert a subpanel with a subgraph into the designated graph window, which can then be interacted with via the mouse. You can add as many subgraphs as you want. The subwindows are also prevented from disappearing outside the main graph area upon resizing the main graph. A minimum subwindow size is set by the subMinSize constant. The detection range for subwindow edge (i.e., where the dragging mode switches to resizing close to window edges) is controlled via the edgeDetect constant.

If you want to remove the hook and associated user data again, use:

TestRemoveHook(GraphName)

Here is the full code:

#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3
#pragma DefaultTab={3,20,4}

function TestAppendSubGraph(string mainGName, wave plotThis)
    GetWindow $mainGName, expand
    variable gExpand = V_value; gExpand = gExpand == 0 ? 1 : gExpand    // pixel conversion and expansion factor
    variable pixConv = ScreenResolution/72*gExpand
   
    GetWindow $mainGName psizeDC        // define an initial size
    variable size = max(min((V_bottom-V_top)/4,(V_right-V_left)/5),subMinSize)/pixConv
    variable wL = (V_left+20)/pixConv, wT = (V_top+20)/pixConv, wR = wL+size*1.2, wB = wT+size
   
    NewPanel/HOST=$mainGName/N=floatPanel/W=(wL,wT,wR,wB)
    Display/HOST=#/FG=(FL,FT,FR,FB) plotThis
    ModifyGraph frameStyle=1        // edges are easier to see with a frame
    SetActiveSubwindow $mainGName
    SetWindow $mainGName hook(DragUpdate)=DragHookFunction
end

function TestRemoveHook(string gName)
    string subWinList = ChildWindowList(gName)
    int i
    for (i=0; i<ItemsInList(subWinList); i++)
        SetWindow $gName userdata($StringFromList(i,subWinList))=""     // remove user data from graph
    endfor
    SetWindow $gName userdata(dragInfo)=""
    SetWindow $gName hook(DragUpdate)=$""
end

// ##################

static Constant subMinSize = 100
static Constant edgeDetect = 15

function DragHookFunction(STRUCT WMWinHookStruct &s)
    string gName = StringFromList(0,s.winName,"#")
    string sName = StringFromList(1,s.winName,"#")
    string dragInfo = "", floatStruct = ""
    int i, hookTakeOver = 0
   
    GetWindow $gName, expand
    variable gExpand = V_value; gExpand = gExpand == 0 ? 1 : gExpand    // pixel conversion and expansion factor
    variable pixConv = ScreenResolution/72*gExpand
    variable winEdge = 0, dragEdge = 0
    STRUCT DragStruct d
    STRUCT rect tempRect
   
    if (WhichListItem(s.eventName, "mousedown;mousemoved;mouseup;")>-1 && CmpStr(s.winName, gName))     // mouse events on the sub-graph
        GetWindow $s.winName psizeDC
        tempRect.top    = v_top
        tempRect.left   = v_left
        tempRect.right  = v_right
        tempRect.bottom = v_bottom
        if (!CheckPointInRect(s.mouseLoc,tempRect))     // mouse is not in plot area
            GetWindow $s.winName gsizeDC;    winEdge = 1            // encode sub-window mouse edge detection into winEdge
            winEdge = abs(s.mouseloc.h-v_left) <edgeDetect && abs(s.mouseloc.v-v_top)   <edgeDetect ? 2 : winEdge   // LT
            winEdge = abs(s.mouseloc.h-v_right)<edgeDetect && abs(s.mouseloc.v-v_top)   <edgeDetect ? 3 : winEdge   // RT
            winEdge = abs(s.mouseloc.h-v_left) <edgeDetect && abs(s.mouseloc.v-v_bottom)<edgeDetect ? 4 : winEdge   // LB
            winEdge = abs(s.mouseloc.h-v_right)<edgeDetect && abs(s.mouseloc.v-v_bottom)<edgeDetect ? 5 : winEdge   // RB
        endif
    endif
   
    StrSwitch(s.eventName)
        case "mousedown":
            if (CmpStr(s.winName, gName) && s.eventMod==1)
                if (CheckPointInRect(s.mouseLoc,tempRect))
                    break
                endif
                GetWindow $gName gsizeDC
                d.graphRect.top    = v_top
                d.graphRect.left   = v_left
                d.graphRect.bottom = v_bottom
                d.graphRect.right  = v_right
                d.mouse_init = s.mouseloc
                d.subwinRect = s.winrect
                StructPut/S d, floatStruct
                SetWindow $gName userdata($sName)=floatStruct           // save info for the current subwindow
                SetWindow $gName userdata(dragInfo)=num2str(winEdge)+";"+sName+";"      // a list of the edge to drag and the dragged subwindow name
                hookTakeOver = 1
            endif
        break
        case "mouseup":
            if (str2num(GetUserData(gName, "", "dragInfo")))
                SetActiveSubwindow $gName
                SetWindow $gName userdata(dragInfo)=""
            endif
        break
        case "mousemoved":
            if (winEdge)
                s.cursorCode = str2num(StringFromList(winEdge,"0;8;4;11;10;7;"))
                s.doSetCursor = 1   // change mouse cursor
            endif
            dragInfo = GetUserData(gName, "", "dragInfo")
            dragEdge = str2num(StringFromList(0,dragInfo))
            dragEdge = numtype(dragEdge) != 0 ? 0 : dragEdge
            if (dragEdge)
                GetMouse/W=$gName
                if (!(V_flag & 1))  // mouse button released? => maybe we missed a mouse-up event?
                    SetWindow $gName userdata(dragInfo)=""
                    dragEdge = 0
                endif
            else
                break
            endif
           
            sName = StringFromList(1,dragInfo)         
            StructGet/S d, GetUserData(gName, "", sName)
            variable deltah = s.mouseloc.h - d.mouse_init.h
            variable deltav = s.mouseloc.v - d.mouse_init.v
            tempRect = d.subwinRect
            tempRect.top    += deltav * (dragEdge == 1 || dragEdge == 2 || dragEdge == 3)
            tempRect.left   += deltah * (dragEdge == 1 || dragEdge == 2 || dragEdge == 4)
            tempRect.right  += deltah * (dragEdge == 1 || dragEdge == 5 || dragEdge == 3)
            tempRect.bottom += deltav * (dragEdge == 1 || dragEdge == 5 || dragEdge == 4)
           
            string subWin = gName + "#" + sName
            if (CheckRectPosAndSize(tempRect, d.graphRect) && WinType(subWin))
                MoveSubwindow/W=$subWin fnum=(tempRect.left/pixConv, tempRect.top/pixConv, tempRect.right/pixConv, tempRect.bottom/pixConv)
                d.subwinRect = tempRect
            endif
            d.mouse_init = s.mouseloc
            StructPut/S d, floatStruct
            SetWindow $gName userdata($sName)=floatStruct
        break
        case "resize":
            string subWinList = ChildWindowList(gName)
            if (!ItemsInList(subWinList))
                break
            endif
           
            GetWindow $gName wsizeDC
            int isHostMinW = abs(v_right-v_left) <= subMinSize
            int isHostMinH = abs(v_bottom-v_top) <= subMinSize
            for (i=0; i<ItemsInList(subWinList); i++)
                sName = StringFromList(i, subWinList)
                GetWindow $(gName+"#"+sName) wsizeDC
                variable subW = abs(v_right-v_left)
                variable subH = abs(v_bottom-v_top)
               
                variable dW = isHostMinW ? 0 : subMinSize - subW    // check if the panel width is smaller than the minimum
                variable dH = isHostMinH ? 0 : subMinSize - subH
                if (dW <= 0 && dH <= 0)
                    continue
                endif
               
                dragInfo = GetUserData(gName, "", sName)
                if (strlen(dragInfo))
                    StructGet/S d, dragInfo
                    subW = abs(d.subwinRect.right -d.subwinRect.left)
                    subH = abs(d.subwinRect.bottom-d.subwinRect.top)
                endif
               
                tempRect.top    = v_top  - dH*(dH>0)
                tempRect.bottom = v_top  - dH*(dH>0) + subH
                tempRect.left   = v_left - dW*(dW>0)
                tempRect.right  = v_left - dW*(dW>0) + subW
                MoveSubwindow/W=$(gName+"#"+sName) fnum=(tempRect.left/pixConv, tempRect.top/pixConv, tempRect.right/pixConv, tempRect.bottom/pixConv)
            endfor
        break
    endswitch
    return hookTakeOver
end

// ++++++++++++

static structure DragStruct
    STRUCT point mouse_init
    STRUCT Rect subwinRect
    STRUCT Rect graphRect
endstructure

static function CheckRectPosAndSize(STRUCT rect &in, STRUCT rect &ref)
    int isInside  = (in.left>ref.left && in.top>ref.top && in.right<ref.right && in.bottom<ref.bottom)
    int isMinSize = abs(in.right-in.left) >= subMinSize && abs(in.bottom-in.top) >= subMinSize
    return isInside && isMinSize
end

static function CheckPointInRect(STRUCT point &pnt, STRUCT rect &ref)
    return ( pnt.h>ref.left && pnt.h<ref.right && pnt.v>ref.top && pnt.v<ref.bottom )
end

 

An example graph Dragable Subgraphs.ipf

Forum

Support

Gallery

Igor Pro 9

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More