Page 1 of 2

MultiTrackBarGadget Version 0.7 (Windows+Linux)

Posted: Thu Feb 11, 2016 3:56 pm
by RichardL
Hi,
I created this track bar gadget sometime back and found it as I was starting a spring clean on my hard disk. Horizontal version only.

Version 0.7 ~ I took a laptop on holiday and to keep sane I made a number of improvements. I only need to add a few more items before it gets to Rev1.0 and incorporated into a speech processor I'm working on for an amateur radio transceiver. At present the speech processor includes an Automatic Level Control, which works well, you can whisper or shout, the output amplitude remains much the same. The processor also includes record and playback features. My next step is to add frequency domain shaping with the user able to adjust the profile with something similar to the '10-slider' demo code. This will use FFT / IFFT and the key elements are in place. Finally I'm going to add dynamic amplitude shaping to increase the average 'talk power'.

Changes / additions are documented at the top of the code.

Please try / play. Comments welcome.

RichardL

Code: Select all

; This is like a one or two axis TrackBarGadget() with multiple sliders so MTrackBarGadget()

; Part of concept study for TTx-1101. (c) Richard Leman (G8CDD). Use, plunder or ignore at your own risk.

;{ Admin and TO DO list 
; Some ideas for providing a multiple slider trackbar to allow bandwidth and notch width to be specified graphically.

; Clicking 'NEAR' a slider automatically jumps the mouse to the slider. What is NEAR? Within 5 pixels. 
; (This is a 'good style' thing... merely selecting an item should not change it.)
; Multiple MTrackBarGadgets use just one handler. (Raided my KnobGadget() for that.)
; Slider moves are POSTed to the event manager as #MT_EventSliderMove.

; Version 0.7 - A holiday update! 18th May 2016
; DONE   : Option to apply a smoothing operation to the lines between points. (Chickened out with Bezier!)
; DONE   : Option to have X and/or Y lines showing while dragging
; DONE:  : Option to join drag balls with lines. Table of Y values produced.**
; DONE   : Option to produce a smoothed line between drag balls, also makes table. **
; DONE   : Dispensed with MinVal and MaxVal (Sorry PB). I never used them and perform scaling based on pixel co-ords.
; DONE   : Made drag bar width configurable.
; DONE   : Simplification - Dispensed with #NarrowSlider option. Slider widths from 1 now valid.
; DONE   : Single slider uses an almost MS look alike button... added option flag. 
; DONE   : Switch to turn vertical lines on/off
; DONE   : Optional horizontal lines and on/off switch

; I sense a 1.0 is not far off!
; ** Tables can be scaled to control other processes. ie: Control FFT / IFFT frequency shaping / Machine tool.

; TO DO  : Add X lock and Y lock on a per slider basis?
;        : Think about a proper VERTICAL identity. VSCROLL style?
;        : Two speed?
;}
; ------------------ Clip starting here and save as MTrackBar.pbi -------------------------
; MTrack_0.7
;{ Procedure Declarations
Declare MTrack_Service()
Declare SetGadgetMouseXY(Win,Gadget,MX,MY)
Declare MTrack_Refresh(Dragging)
Declare MTrack_GetIndex(Gadnum)
Declare MakeMSButton(BM)
Declare MakeMSArrow(BM,LR_Switch)
;}
;{ Variables and structures
;{ Define colours for our Linux friends
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
 
  ImportC ""
    gdk_device_manager_get_client_pointer(*device_manager);                 Gtk3
    gdk_device_warp(*device.GdkDevice, *screen.GdkScreen, X, Y);            Gtk3
    gdk_display_get_default()
    gdk_display_get_device_manager(*display.GdkDisplay);                    Gtk3
    gdk_display_warp_pointer(*display.GdkDisplay, *screen.GdkScreen, X, Y); Gtk2
    gdk_screen_get_default()
  EndImport
  
  #Red   = $0000FF
  #Green = $008000
  #Cyan  = $FFFF00
  #Yellow= $00FFFF
  #White = $FFFFFF
  #Black = $0
  #Blue  = $FF0000
CompilerEndIf
;}
; ** [Maximum number of sliders] ... adjust to suit application. Must be literal value. 

; [.....................|.................|........|.....|........]
; ^                     ^                 ^        ^     ^        ^
; Min                   S0                S1       S2    S1       Max

; Variables specific to multi-trackbargadgets
Structure MULTITRACKBAR
  MT_Win.l            ; Window in which the gadget resides
  MT_GadNum.l         ; Gadget number
  MT_X.l              ; Gadget X,Y,W and H
  MT_Y.l
  MT_W.l
  MT_H.l
  ; _MinVal.l         ; Slider range - Min
  ; _MaxVal.l         ; Slider range - Max
  MT_BorderWidth.l
  MT_GratSpace.f      ; Flag / Number of divisions in graticule
  MT_NumSliders.l     ; Number of sliders
  MT_SlideWid.l
  MT_SlideMode.l      ; See 'Styles of pointer sliding'
  MT_SlideProx.l      ; Click proximity to grab a slider
  MT_BackImage.l      ; Bitmap# for backdrop. 
  MT_BackColour.l     ; Backdrop colour
  
  MT_ProgBarCol.l
  MT_ProgBarTextCol.l
  MT_ProgBarFontID.l
  
  ; Maximum number of sliders on an MTrack gadget = 10
  MT_SlideX.l[10]     ; ** Position of sliders in pixels from left
  MT_Colour.l[10]     ; ** Colours of sliders
  MT_X_SlideBMP.l[10] ; ** Bitmaps for individual VERTICAL sliders.
  MT_Y_SlideBMP.l[10] ; ** HORIZONTAL
  MT_SlideY.l[10]     ; ** Y position of capture ball
  
  ; Maximum width of an MTrack = 1000
  MT_LineBuf.w[1000]  ; Buffer: Y values for line plot
  MT_SmoothBuf.w[1000]; Buffer: Smoothed Y values
  
EndStructure

; Styles of pointer sliding
EnumerationBinary
  ; Slider collision type... set one bit in Least Significant four bits
  #SlideThro          ; All sliders full range   ** DEFAULT **
  #Caged              ; Slider constrained between neighbours
  #Push               ; Slider can shove neighbours along the road
  #NoSlide            ; Prevent user moving sliders, still allows programatic movements
  
  ; Misc flags
  #Graticule = %100000; 05
  #Balls              ; 06 Sliders have small ball to aid identification when overlapped.
  #UseMSStyle         ; 07 
  #Borderless         ; 08 Gadget does NOT have a border.
  #ProgressBar        ; 09 Fill to left of slider and show percentage display.
  
  #BallYDrag          ; 10 Drag balls may be slid vertically.
  #BallJoin           ; 11 Drag balls are joined with a line.
  #BallJoinSmooth     ; 12 Drag balls determine shape of a smoothed curve between them.
  #Vert_LineSwitch    ; 13 Turn vertical cursor lines on/off.
  #Horiz_LineSwitch   ; 14 Turn horizontal cursor lines on/off.
  #LinesWhenDragging  ; 15 Show vert and horiz lines ONLY when ball is being dragged.

EndEnumeration

; Define custom events
Enumeration #PB_Event_FirstCustomValue
  #MT_EventSliderMove
  #MT_EventRedrawSmooth
EndEnumeration

; Private
Global NumBarSlides = 1            ; Number of slider gadgets created
Global Dim MSlide.MULTITRACKBAR(1) ; Array of structures, one structure per gadget
Global     MSDefs.MULTITRACKBAR    ; Default values. Adjust these before creating a gadget.
Global BW.l = 2                    ; Border width. 0 or  2

With MSDefs
  \MT_BorderWidth = 2
  \MT_SlideProx   = 5 
  \MT_BackColour  = GetSysColor_(#COLOR_BTNHILIGHT)
  \MT_ProgBarCol      = #Blue
  \MT_ProgBarTextCol  = #White
  \MT_ProgBarFontID   = FontID(LoadFont(#PB_Any,"Calibri",10))
  \MT_NumSliders = 1
  \MT_SlideWid   = 3
  
  For n = 0 To 9
    \MT_SlideY[n]  = (n+1)*4
    \MT_Colour[n] = #White
  Next
  
  \MT_Colour[0]   = #Red
  \MT_Colour[1]   = #Green
  \MT_Colour[2]   = #Cyan
  \MT_Colour[3]   = #Yellow   
  
EndWith
;}

EnableExplicit

; To create a new MTrackBarGadget adjust the default values then call this procedure 
Procedure MTrackBarGadget(Gadnum,GadX,GadY,GadW,GadH,NumSliders,SlideMode = #SlideThro | #Vert_LineSwitch,WindowNum = -1)
  
  Protected F.f,Result.l,n.l,T.l,X
  
  ; Create storage for a new MTrackBarGadget
  If MSlide(NumBarSlides)\MT_W <> 0  
    NumBarSlides + 1
    Redim MSlide(NumBarSlides)
  EndIf

  T = #PB_Canvas_Border
  If SlideMode & #Borderless : T = 0 : EndIf
  Result = CanvasGadget(Gadnum,GadX,GadY,GadW,GadH,#PB_Canvas_ClipMouse | T )
  
  If Result 
    If Gadnum = #PB_Any : Gadnum = Result : EndIf
    
    CopyStructure(@MSDefs,@MSlide(NumBarSlides),MULTITRACKBAR)
    
    With MSlide(NumBarSlides)
      
      If WindowNum = -1
        \MT_Win = GetActiveWindow()
      Else
        \MT_Win = WindowNum
      EndIf
      \MT_GadNum = Gadnum
      \MT_X = GadX
      \MT_Y = GadY
      \MT_W = GadW
      \MT_H = GadH
      
      \MT_SlideMode   = SlideMode
      \MT_GratSpace   = \MT_W / 32 
      \MT_BackImage   = CreateImage(#PB_Any,GadW,GadH,32,\MT_BackColour)
      \MT_NumSliders  = NumSliders
      For n = 0 To NumSliders - 1
        \MT_SlideX.l[n] = (n+1) * GadW / (NumSliders +1)
      Next
     
      ; Create optional graticule.
      If \MT_SlideMode & #Graticule
        If StartDrawing(ImageOutput(\MT_BackImage))
            F = 0
            While F < \MT_W
              LineXY(F,\MT_H,F,\MT_H-\MT_H>>2,#White)
              F + \MT_GratSpace
            Wend
          StopDrawing()
        EndIf
        
      EndIf
      
      ; Set global border margin width
      If \MT_SlideMode & #Borderless
        \MT_BorderWidth = 0
      Else
        \MT_BorderWidth = 2
      EndIf      
      
      ; Images for each slider bar.
      ; Vertical...
      For n = 0 To \MT_NumSliders - 1
        \MT_X_SlideBMP[n] = CreateImage(#PB_Any,\MT_SlideWid,\MT_H-(2*\MT_BorderWidth ),32,\MT_Colour[n])
        If \MT_NumSliders = 1 And \MT_SlideMode & #UseMSStyle
          MakeMSArrow(\MT_X_SlideBMP[n],2)
        EndIf
        
      Next
      ; Horizontal...
      For n = 0 To \MT_NumSliders - 1
        \MT_Y_SlideBMP[n] = CreateImage(#PB_Any,\MT_W-(2*\MT_BorderWidth ),\MT_SlideWid,32,\MT_Colour[n])
      Next
      
    EndWith
    
    ; Each gadget calls the same service routine
    BindGadgetEvent(Gadnum,@MTrack_Service())
    
    MTrack_Refresh(NumBarSlides)
  EndIf
  
  ProcedureReturn Gadnum
  
EndProcedure

; Use these two calls to set or get the position of a gadget's 'X' cursor/s
Procedure MTrack_SetCursor(Gadnum,Cursor,XPosition,YPosition=-1) ; Set a cursor position
  Protected GadIndex.l
  
  ; Find the index to the data for the chosen gadget
  GadIndex = MTrack_GetIndex(Gadnum) 
  
  ; Quit if index not found
  If GadIndex = 0
    ProcedureReturn -1
  EndIf
  
  MSlide(GadIndex)\MT_SlideX[Cursor] = XPosition ; Set the cursor X position
  If YPosition > -1
    MSlide(GadIndex)\MT_SlideY[Cursor] = YPosition ; Set the cursor Y position
  EndIf
  
  MTrack_Refresh(GadIndex) ; Refresh the MTBG
  
EndProcedure
Procedure MTrack_GetCursor(Gadnum,Cursor)          ; Return cursor position
  Protected GadIndex.l
  
  ; Find the index to the data for the chosen gadget
  GadIndex = MTrack_GetIndex(Gadnum) 
  
  ; Quit if index not found
  If GadIndex = 0
    ProcedureReturn -1
  EndIf
  ProcedureReturn MSlide(GadIndex)\MT_SlideX[Cursor] - MSlide(GadIndex)\MT_BorderWidth
EndProcedure

; Keep out!
Procedure MTrack_Service()                         ; An Event has occurred on a MultiTrackBarGadget()...
  Global Dragging
  Static LastX
  Protected EGad.l,n.l,DragX.l,DragY.l,T.l,GadIndex.l,X.l
  
  ; Get the Gadget# that was tickled...
  EGad = EventGadget()
  
  ; Find the index to the data for the chosen gadget
  GadIndex = MTrack_GetIndex(EGad) 
  If GadIndex = 0
    ProcedureReturn - 1
  EndIf
  
  ; Event manager for left mouse button down/up and mouse slide
  With MSlide(GadIndex)
    Select EventType()
      Case #PB_EventType_LeftButtonDown ;{ Left mouse button went DOWN, start dragging a pointer...
        
        DragX = GetGadgetAttribute(\MT_GadNum, #PB_Canvas_MouseX) + \MT_BorderWidth
        DragY = GetGadgetAttribute(\MT_GadNum, #PB_Canvas_MouseY) + \MT_BorderWidth
       
        ; Find a slider position that is 'close' and jump to it.
        ; (This neat trick ensures that real time variables  
        ;  only change when the user moves the cursor.)
        
        Dragging = 0
        For n = 0 To \MT_NumSliders - 1  ; For each of the cursors
          
          T = DragX - \MT_SlideX[n]      ; Find how close the mouse is to the slider...
          T - \MT_BorderWidth
          If T < 0 : T = -T : EndIf      ; regardless of side.
          If T < \MT_SlideProx           ; If close enough in 'X'
            
            If \MT_SlideMode & #Balls    ; If drag balls enabled...
              T = DragY - \MT_SlideY[n]  ; Find how close mouse is to ball...
              If T<0 : T=-T : EndIf      ; regardless of height.
              If T >= \MT_SlideProx      ; If NOT close enough
                Continue                 ; Check the next slider...
              EndIf
              DragY = \MT_SlideY[n]+\MT_BorderWidth ; Mouse 'Y' position = vertical centre of ball
            EndIf
            
            SetGadgetMouseXY(\MT_Win,\MT_GadNum,\MT_SlideX[n] + \MT_BorderWidth,DragY); Jump mouse to cursor
            Dragging = n+1               ; and set flag for 'dragging' (=Cursor+1)
            
            ; Nice comforting sound! 
            CompilerIf #PB_Compiler_OS = #PB_OS_Windows
              beep_(2500,50)
              CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
              RunProgram("beep", "-f 2500 -l 50", ""); 'beep' must be installed on Linux
            CompilerEndIf
           
            Break
            
          EndIf
        Next
        ;}
      Case #PB_EventType_MouseMove      ;{ A pointer is being dragged, update position
        If Dragging
          
          X = GetGadgetAttribute(\MT_GadNum, #PB_Canvas_MouseX) 
          T = \MT_SlideMode & %11111
          Select (T) ; Five bits that define the slider type ...
              
            Case #SlideThro               ; Free to roam over whole gadget
              \MT_SlideX[Dragging-1] = X
              PostEvent(#MT_EventSliderMove,\MT_Win,\MT_GadNum,Dragging-1,X) ; x-2
              MTrack_Refresh(GadIndex)
              
            Case #Caged                 ; Restricted to space between neighbours / ends
              If  (Dragging = \MT_NumSliders) Or (X <= \MT_SlideX[Dragging]) 
                If (Dragging = 1) Or (X >= \MT_SlideX[Dragging-2])               
                  \MT_SlideX[Dragging-1] = X
                  PostEvent(#MT_EventSliderMove,\MT_Win,\MT_GadNum,Dragging-1,X)
                  MTrack_Refresh(GadIndex)
                EndIf
              EndIf
              
            Case #Push                  ; Able to push neighbours
              \MT_SlideX[Dragging-1] = X 
              
              For n = Dragging-2 To 0 Step -1
                If X <  \MT_SlideX[n]
                  \MT_SlideX[n] = X
                  PostEvent(#MT_EventSliderMove,\MT_Win,\MT_GadNum,n,X)
                EndIf
              Next
              
              For n = Dragging To \MT_NumSliders - 1
                If X > \MT_SlideX[n]
                  \MT_SlideX[n] = X
                  PostEvent(#MT_EventSliderMove,\MT_Win,\MT_GadNum,n,X-\MT_BorderWidth)
                EndIf
              Next
              
              PostEvent(#MT_EventSliderMove,\MT_Win,\MT_GadNum,Dragging-1,X-\MT_BorderWidth)
              MTrack_Refresh(GadIndex)
              
            Case #NoSlide        ; User cannot move sliders on this gadget
              If \MT_SlideMode & #ProgressBar
                beep_(2500,55)
              EndIf
              
              
            Default
              MessageRequester("MTrackBar","Flag error (%"+Bin(T)+")")
              
          EndSelect
          
          If  \MT_SlideMode & #BallYDrag
            \MT_SlideY[Dragging-1] = GetGadgetAttribute(\MT_GadNum, #PB_Canvas_MouseY)
          EndIf
          
          
        EndIf
        ;}
      Case #PB_EventType_LeftButtonUp   ;{ Button went UP, stop dragging pointer
        Dragging = #False
        ;}
    EndSelect
  EndWith
  
EndProcedure
Procedure MTrack_GetIndex(Gadnum)                  ; Find the index to the data for the chosen gadget
  Protected n.l, GadIndex.l
  
  GadIndex = 0
  For n = 1 To NumBarSlides
    If MSlide(n)\MT_GadNum = Gadnum
      GadIndex = n
      Break
    EndIf
  Next
  ProcedureReturn GadIndex
EndProcedure
Procedure MTrack_Refresh(Index)                    ; Redraw a MTBG referenced by table index (order of creation). Base 1
  Protected n.l, r.l,k$, DyDx.f, X.l,Y.f,T.l, FirstX.l, LastX.l, Sw.l, U1.f, Y1.f, F.f, Z.l
  With MSlide(Index)
    
    StartDrawing(CanvasOutput(\MT_GadNum))
      
      DrawImage(ImageID(\MT_BackImage),0,0)   ; Redraw backdop
      
      FirstX = \MT_SlideX[0]
      
      For n = 0 To \MT_NumSliders - 1  
        ; Redraw...
        If \MT_SlideMode & #ProgressBar ;{ Progress bar
          DrawingMode(#PB_2DDrawing_Transparent)
          Box(0,0,\MT_SlideX[n],\MT_H,\MT_ProgBarCol)         
          k$ = StrF((\MT_SlideX[n] / (\MT_W-2*\MT_BorderWidth)) *100,1) + "%"     ; Percent full
          DrawingFont(\MT_ProgBarFontID)                      ; Set font
          DrawText((\MT_W - TextWidth(k$))/2,(\MT_H-(\MT_BorderWidth*2)-TextHeight("0"))/2,k$,\MT_ProgBarTextCol)
          ;}
        Else ;{ Vertical sliders
          If (\MT_SlideMode & #Vert_LineSwitch) Or ((\MT_SlideMode & #LinesWhenDragging) And n=Dragging-1 And \MT_GadNum = EventGadget())
            DrawImage(ImageID(\MT_X_SlideBMP[n]),\MT_SlideX[n] - \MT_SlideWid>>1,0)         ; Image
          EndIf
          r = 3
          ;}
        EndIf
        ;{ Draw optional drag ball and horizontal line/slider
        If \MT_SlideMode & #Balls
          If \MT_SlideMode & #Horiz_LineSwitch Or ((\MT_SlideMode & #LinesWhenDragging) And n=Dragging-1 And \MT_GadNum = EventGadget())
            DrawImage(ImageID(\MT_Y_SlideBMP[n]),0,\MT_SlideY[n]-(\MT_SlideWid>>1))
          EndIf
          Circle(\MT_SlideX[n], \MT_SlideY[n],r+1,#Black) ; +++
          Circle(\MT_SlideX[n], \MT_SlideY[n],r,\MT_Colour[n]) ; +++
        EndIf
          ;}
        ;{ Draw optionalal lines joining drag balls together (N balls ... N-1 lines)
        If n And (\MT_SlideMode & #BallJoin Or \MT_SlideMode & #BallJoinSmooth)
          If \MT_SlideX[n]-\MT_SlideX[n-1] = 0  ; Special case if X1=X2. ie: vertical line
           
          Else
          ; Make table of points for drawing LINEs between points
            ; Calculate slope of LINE to next point
            DyDx = (\MT_SlideY[n]-\MT_SlideY[n-1]) / (\MT_SlideX[n]-\MT_SlideX[n-1])
            
            ; Initial values
            X = \MT_SlideX[n-1] 
            Y = \MT_SlideY[n-1]
            
            ; Fill the table...
            While X < \MT_SlideX[n]
              \MT_LineBuf[X] = Round(Y,#PB_Round_Nearest)
              Y + DyDx
              X + 1
            Wend
            
            LastX = X-1
            
          EndIf
          
        EndIf        
        ;}
      Next
      
      ;{ Optional create smoothed table.
      ; The straight lines joining drag points are 'smoothed' by averaging the 'LineBuf' points 
      ; in the range + / - 'Sw' either side of each point.
      ; The smoothed values are written to 'SmoothBuf' so Line and Smooth values are available
      ; for displaying and experiment control.
      
      If LastX
        Sw = 10   ; Semi-smoothing width
        
        ; Calculate sum of Y values for first group.
        T = 0
        For X = FirstX To FirstX + (2*Sw)
          T + \MT_LineBuf[X] 
        Next 
        
        ; Smoothing...
        U1 = 1 + (2*Sw)            ; Number of samples in smoothing group
        Y1 = T / U1
        For X = FirstX + Sw  To LastX - Sw  
          \MT_SmoothBuf[X] = Round(T / U1,#PB_Round_Nearest); New Y is average of group.
          T - \MT_LineBuf[X-Sw]    ; Remove left group member
          T + \MT_LineBuf[X+Sw+1]  ; Add new right member 
        Next
        
        ; First 'Sw' width
        F = \MT_SlideY[0]
        DyDx = (Y1 - F) / Sw
        For X = FirstX To FirstX + Sw
          \MT_SmoothBuf[X] = Round(F,#PB_Round_Nearest)
          F + DyDx
        Next
        
        ; Last 'Sw' width 
        F = \MT_SmoothBuf[LastX - Sw]
        DyDx = (\MT_LineBuf[LastX] - F) / Sw
        For X = LastX - Sw To LastX
          \MT_SmoothBuf[X] = Round(F,#PB_Round_Nearest)
          F + DyDx
        Next
      EndIf
      ;}
      ;{ Plot smoothed lines
      If \MT_SlideMode & #BallJoinSmooth
        Plot(FirstX,\MT_SmoothBuf[FirstX],#Cyan)
        For X = FirstX + 1 To LastX 
          LineXY(X-1,\MT_SmoothBuf[X-1],X,\MT_SmoothBuf[X],#Cyan)
        Next
      EndIf
      ;}
      ;{ Plot straight connecting lines
      If \MT_SlideMode & #BallJoin
        For X = FirstX + 1 To LastX 
          LineXY(X-1,\MT_LineBuf[X-1],X,\MT_LineBuf[X],#Red)
        Next
      EndIf
      ;}
      
    StopDrawing()
  EndWith
EndProcedure
Procedure MTrack_GetBackdropBitmap(Index)
  ProcedureReturn MSlide(Index)\MT_BackImage
EndProcedure
Procedure MakeMSButton(BM)
  Protected Result,IW.w,IH.w
  Result = #False
  IW = ImageWidth(BM)
  IH = ImageHeight(BM)
  If IW > 5
    If StartDrawing(ImageOutput(BM))
        Box(0,0,IW,IH,GetSysColor_(#COLOR_SCROLLBAR))
        
        LineXY(IW-1,0,IW-1,IH-1, GetSysColor_(#COLOR_3DDKSHADOW))
        LineXY(IW-2,1,IW-2,IH-2, GetSysColor_(#COLOR_3DSHADOW))
        LineXY(1,   1,IW-3,1,    GetSysColor_(#COLOR_3DHIGHLIGHT))
        
        LineXY(0,IH-1,IW-1,IH-1, GetSysColor_(#COLOR_3DDKSHADOW))
        LineXY(1,IH-2,IW-2,IH-2, GetSysColor_(#COLOR_3DSHADOW))
        LineXY(1,1,   1,   IH-3, GetSysColor_(#COLOR_3DHIGHLIGHT))
      StopDrawing()
      Result = #True
    EndIf
EndIf
ProcedureReturn Result
EndProcedure

Enumeration
  #Left
  #Right
  #LeftRight
  #Up
  #Down
  #UpDown
EndEnumeration

Procedure MakeMSArrow(BM,LR_Switch)
  Protected Result,T,IW.w,IH.w
  Protected Y.w,Y1.w,Y2.w,X.w,X1.w,X2.w
  Result = #False
  IW = ImageWidth(BM)
  IH = ImageHeight(BM)
  
  If (IH<6) Or (IW<6)
    ProcedureReturn #False
  Else
    If MakeMSButton(BM)
      If StartDrawing(ImageOutput(BM))
          Select LR_Switch 
            Case #Left      ;{
              Y1 = (IH/2)-1 : Y2 = Y1 : X = 4
              Repeat
                For Y = Y2 To Y1
                  Plot(X,Y,#Black)
                Next
                X+1 : Y1+1 : Y2-1
              Until Y2 < 4
              ;}
            Case #Right     ;{
              Y1 = (IH/2)-1 : Y2 = Y1 : X = IW - 4
              Repeat
                For Y = Y2 To Y1
                  Plot(X,Y,#Black)
                Next
                X-1 : Y1+1 : Y2-1
              Until Y2 < 4
              ;}
            Case #LeftRight ;{
              Y1 = (IH/2)-1 : Y2 = Y1 : X = 3
              Repeat
                For Y = Y2 To Y1
                  Plot(X,Y,#Black)
                Next
                X+1 : Y1+1 : Y2-1
              Until Y2 < 4
              
              Y1 = (IH/2)-1 : Y2 = Y1 : X = IW - 4
              Repeat
                For Y = Y2 To Y1
                  Plot(X,Y,#Black)
                Next
                X-1 : Y1+1 : Y2-1
              Until Y2 < 4
              ;}
            Case #Up        ;{
              X1 = (IW/2) : X2 = X1 : Y = 3
              Repeat
                For X = X1 To X2
                  Plot(X,Y,#Black)
                Next
                X1-1 : X2+1 : Y+1
              Until X1 < 4
              ;}
            Case #Down      ;{
              X1 = (IW/2) : X2 = X1 : Y = IH - 4
              Repeat
                For X = X1 To X2
                  Plot(X,Y,#Black)
                Next
                X1-1 : X2+1 : Y-1
              Until X1 < 4
              ;}
            Case #UpDown    ;{
              X1 = (IW/2) : X2 = X1 : Y = 3
              Repeat
                For X = X1 To X2
                  Plot(X,Y,#Black)
                Next
                X1-1 : X2+1 : Y+1
              Until X1 < 4
              
              X1 = (IW/2) : X2 = X1 : Y = IH - 4
              Repeat
                For X = X1 To X2
                  Plot(X,Y,#Black)
                Next
                X1-1 : X2+1 : Y-1
              Until X1 < 4
              ;}
          EndSelect
        StopDrawing()
      EndIf
    EndIf
  EndIf
  ProcedureReturn Result
EndProcedure

Procedure SetGadgetMouseXY(Win,Gadget,MX,MY)       ; Position mouse pointer at specified co-ordinated within a gadget
  MX + WindowX(Win,#PB_Window_InnerCoordinate) + GadgetX(Gadget)
  MY + WindowY(Win,#PB_Window_InnerCoordinate) + GadgetY(Gadget)
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    SetCursorPos_(MX,MY)
    CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
    gdk_display_warp_pointer(gdk_display_get_default (), gdk_screen_get_default(), MX, MY);  Gtk2, officialy deprecated
    ;-> replace the line above with the following line, if 'gdk_display_warp_pointer' is finally deprecated on newer gtk3-systems...
    ;  gdk_device_warp (gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default ())), gdk_screen_get_default(), MX, MY)
  CompilerEndIf
  
EndProcedure

DisableExplicit
; ------------------ End of Clip -----------------------------------------------------------

;{ ------------- TEST AND DEMO CODE -------------
CompilerIf #PB_Compiler_IsMainFile
  Dim XDat(10): Dim YDat(10)              ; Needed for demo only...
  
  OpenWindow(1,0,0,600,400,"MultiBar_Test Rev 0.7", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  SetActiveWindow(1)
  StickyWindow(1,#True)
  
  ; Create some MTrackBarGadgets. 
  ;(They all use BindGadget() to call MTrack_Service, so are not serviced by  EventGadget() in the Event Manager)
  
  ; 1 Slider
  MSDefs\MT_SlideWid   = 25
  MTrackBarGadget(5, 10,25,512+4,20,1, #SlideThro | #Graticule | #Vert_LineSwitch | #UseMSStyle)       ;+4? Two borders...     
  TextGadget(#PB_Any,10,10,512,15,"Code: MTrackBarGadget(5, 10,25,512+4,20,1, #SlideThro | #Graticule | #Vert_LineSwitch| #UseMSStyle)")
  TextGadget(35,532,25,50,15,"")
  
  ; 2 Sliders
  MSDefs\MT_SlideWid   = 3
  MTrackBarGadget(6, 10,65,512,30,2,#SlideThro | #Borderless | #Balls | #Vert_LineSwitch)    
  TextGadget(#PB_Any,10,50,560,15,"Code: MTrackBarGadget(6, 10,65,512,30,2,#SlideThro | #Borderless | #Balls  | #Vert_LineSwitch)")
  TextGadget(36,532,65,50,15,"")
  TextGadget(37,532,80,50,15,"")
  
  ; 3 Sliders
  MSDefs\MT_BackColour = $B0B080
  MTrackBarGadget(7, 10,115,512+4,50,3,#Caged | #Graticule | #Balls | #Vert_LineSwitch)        
  TextGadget(#PB_Any,10,100,540,15,"Code: MTrackBarGadget(7, 10,135,512+4,50,3,#Caged | #Graticule | #Balls | #Vert_LineSwitch) ")
  TextGadget(38,532,115,50,15,"")
  TextGadget(39,532,130,50,15,"")
  TextGadget(40,532,145,50,15,"")
  
  ; 10 Sliders
  MSDefs\MT_SlideWid   = 1
  MTrackBarGadget(8, 10,185,512,150,10,#Push | #Borderless | #Balls | #BallYDrag | #BallJoinSmooth | #LinesWhenDragging); 10 Sliders
  TextGadget(#PB_Any,10,170,580,15,"Code: MTrackBarGadget(8, 10,185,512,150,9,#Push | #Borderless | #Balls | #BallYDrag | #BallJoin)")
  For n = 0 To 9
    TextGadget(41+n,532,185+(n*15),60,15,"")
  Next
  
  ; Buttons to turn options on/off
  CheckBoxGadget(20,10, 335,80,15,"Lines")
  CheckBoxGadget(21,110,335,80,15,"Smoothed")
  
  In = MTrack_GetIndex(8)
  SetGadgetState(20,MSlide(In)\MT_SlideMode & #BallJoin)
  SetGadgetState(21,MSlide(In)\MT_SlideMode & #BallJoinSmooth)
  
  ; Example:  Set some initial positions
  For n = 0 To 9
    MTrack_SetCursor(8,n,25+(n*50),50+((n&1)*50))
  Next
  
  ; Example: Progress bar
  TextGadget(#PB_Any,10,355,512,15,"Code: MTrackBarGadget((9, 12,325,250,20,1,#NoSlide | #ProgressBar)")
  MTrackBarGadget(9, 12,370,260,20,1, #ProgressBar | #NoSlide )             
  
  ; Example: Put a gradient backdrop on a MTrack.
  In = MTrack_GetIndex(6)                                 ; Get index of MTrack gadget (Order of creation)
  With MSlide(In)                                         ; Use the structure for the MTrack gadget
    If StartDrawing(ImageOutput(\MT_BackImage))           ; Draw on its backdrop bitmap
        Box(0,0,\MT_W,\MT_H,GetSysColor_(#COLOR_BTNFACE)) ; Rectangle, same colour as Window
        DrawingMode(#PB_2DDrawing_Gradient)               ; Draw the backdrop image...
        BackColor($B0B080)
        FrontColor($FF8080)
        LinearGradient(0,0,\MT_W,\MT_H)
        RoundBox(0,0,\MT_W,\MT_H,8,8)                    
        DrawingMode(#PB_2DDrawing_Outlined)               ; Detail line... looks good!
        RoundBox(0,0,\MT_W,\MT_H,8,8,#Black)              
      StopDrawing()
      MTrack_Refresh(In)                                  ; Update the gadget
    EndIf
  EndWith
  
  ; Skeleton Event Manager... all the usual stuff.
  Repeat
    
    Select WaitWindowEvent(15) ; Timeout purely for convenience in this demo
      Case #PB_Event_CloseWindow
        Break
        
      Case  #MT_EventSliderMove ; Arrive here when an MTrack is accessed.
        Debug "Window  " + Str(EventWindow())
        Debug "Gadget  " + Str(EventGadget())
        Debug "Slider  " + Str(EventType())
        Debug "X Value " + Str(EventData())
        Debug ""
        
        Select EventGadget()
          Case 5
            SetGadgetText(35,Str(EventData()))
            
          Case 6
            If EventType() = 0 : SetGadgetText(36,Str(EventData())) : EndIf
            If EventType() = 1 : SetGadgetText(37,Str(EventData())) : EndIf 
            
          Case 7
            In = MTrack_GetIndex(7)
            X = EventData()
            Y = MSlide(In)\MT_SlideY[EventType()]
            SetGadgetText(38+EventType(),RSet(Str(X),3,"0") )
            
          Case 8
            In = MTrack_GetIndex(8)
            X = MSlide(In)\MT_SlideX[EventType()] ; or: use EventData()
            Y = MSlide(In)\MT_SlideY[EventType()]
            SetGadgetText(41+EventType(),"X"+RSet(Str(X),3,"0")+", Y"+RSet(Str(Y),3,"0") ) 
            
        EndSelect
       
      Case #PB_Event_Gadget
        EGad = EventGadget()
        Select EGad
          Case 20 : In = MTrack_GetIndex(8) : MSlide(In)\MT_SlideMode ! #BallJoin       : SetGadgetState(20,MSlide(In)\MT_SlideMode & #BallJoin)
          Case 21 : In = MTrack_GetIndex(8) : MSlide(In)\MT_SlideMode ! #BallJoinSmooth : SetGadgetState(21,MSlide(In)\MT_SlideMode & #BallJoinSmooth)
         ; All the usual stuff... 
         
        EndSelect
        
    EndSelect
    
    ; Simulate sending data to the progress bar. 
    MTrack_SetCursor(9,0,(ElapsedMilliseconds()/32)& $FF)
    
    ; == ACTIVE BACKDROP DEMO ==============================================
    ; One way to update the backdrop to a MTrackBarGadget() periodically... not the best,
    ; WaitWindowEvent(n) MUST include the optional TimeOut.
    
    ; Make a dynamic data display... this would be the output from your 'machine'.
    ; I just made a random noise base line, simulating a spectrum display.
    
    In = MTrack_GetIndex(8)               ; Get the index of the gadget to be refreshed
    Bd = MTrack_GetBackdropBitmap(In)     ; Get the BitMap number for the gadget
    
    If StartDrawing(ImageOutput(Bd))      ; Draw the new backdrop on the gadget's Bitmap
        DrawingFont(#PB_Default )
        Box(0,0,512,150, RGB($08,$21,$3)) ; Clear the display
        
        ; Make some spoof noise data.
        For X = 0 To 511 Step 2
          Y = 149-Random(10)
          If X
            LineXY(LastX,LastY,X,Y,#White)
          EndIf
          LastY = Y : LastX = X
        Next
      StopDrawing()
      
      MTrack_Refresh(In) ; Redraw the 'spectrum display baseline noise' onto the Canvas()
    EndIf
    
  ForEver
  ; ----------- END OF TEST AND DEMO --------------------------------
CompilerEndIf
;}

Re: MultiTrackBarGadget (Windows)

Posted: Thu Feb 11, 2016 5:38 pm
by davido
@RichardL,
Impressive. It also runs on PureBasic 5.41LTS, as expected.

I'm not requesting you do this, but do you think it could be made cross-platform?

Re: MultiTrackBarGadget (Windows)

Posted: Thu Feb 11, 2016 6:49 pm
by RichardL
@Davido
If you search for '_(' you will find two calls into the Windoze API.

One is to Beep() which I'm sure can be reproduced on any OS... or taken out, its just that I like a confirmatory noise when the mouse locks on to a cursor.

The second call is to SetCursorPos() which is used to position the mouse pointer exactly on to a cursor. Again, this should be a feature that can be realise in other OS' without too much difficulty.

Apart from these two items the rest is all PB, so potentially portable.

Regards
RichardL

Re: MultiTrackBarGadget (Windows)

Posted: Thu Feb 11, 2016 7:27 pm
by Oma
Thanks, Richard.
Since i had just the right mood ...
An addition for Linux (only tested on Kubuntu 64/5.10)
I hope it works ;-)

Code: Select all

; This is like a TrackBarGadget() with multiple sliders so MTrackBarGadget()
; Part of concept study for TTx-1101. (c) Richard Leman (G8CDD). Use, plunder or ignore at your own risk.
;{ Admin
; Some ideas for providing a multiple slider trackbar to allow bandwidths and notchs to be specified graphically.

; Clicking 'NEAR' a slider automatically jumps the mouse to the slider. What is NEAR? Within 5 pixels.
; Multiple MTrackBarGadgets use just one handler. (Raided my KnobGadget() for that.)
; Slider moves are POSTed to the event manager.

; TO DO  : Add Set and Get slider positions... original code read the structures, OK but needs more work!
; TO DO  : Add structure with full set of default values for all items not in MTrackBarGadget() call.
;          User can then modify the 'Defaults' structure before creating gadget... Good for thematic consistency
; TO DO  : Check variables are declared properly... EnableExplicit()
; TO DO  : Add a NoMove flag so a gadget can be locked or just used for a display.
; TO DO  : Add a graticule option switch.
; Maybe  : Click near 'ball' to untangle stacked sliders.

; Status : Works OK for demo... needs items on TO DO list. 12 August 2015

; BUGS
; If all sliders are PUSH'ed to the left edge they are all jammed! Need a method of separating them!
; (NB: Having difficulty making this happen again... maybe something else was wrong)
;}
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
	ImportC ""
		gdk_device_manager_get_client_pointer(*device_manager);                 Gtk3
		gdk_device_warp(*device.GdkDevice, *screen.GdkScreen, x, y);            Gtk3
		gdk_display_get_default()
		gdk_display_get_device_manager(*display.GdkDisplay);                    Gtk3
		gdk_display_warp_pointer(*display.GdkDisplay, *screen.GdkScreen, x, y); Gtk2
		gdk_screen_get_default()
	EndImport
	
	#Red   = $0000FF
	#Green = $008000
	#Cyan  = $FFFF00
	#Yellow= $00FFFF
	#White = $FFFFFF
	#Black = $0
CompilerEndIf


;{ Procedure Declarations
Declare MultiBarService()
Declare SetGadgetMouseXY(Win,Gadget,MX,MY)
Declare MB_Refresh(Dragging)
;}
;{ Variables and structures
; [Maximum number of sliders] ... adjust to suit application. Must be literal value.

; [.....................|.................|........|.....|........]
; ^                     ^                 ^        ^     ^        ^
; Min                   S0                S1       S2    S1       Max

; Variables specific to multi-trackbargadgets
Structure MULTITRACKBAR
  MT_Win.l            ; Window in which the gadget resides
  MT_GadNum.l         ; Gadget number
  MT_X.l              ; Gadget X,Y,W and H
  MT_Y.l
  MT_W.l
  MT_H.l
  MT_MinVal.l         ; Slider range - Min
  MT_MaxVal.l         ; Slider range - Max
  MT_GratSpace.f        ; Flag / Number of divisions in graticule
  MT_NumSlides.l      ; Number of sliders
  MT_SlideMode.l      ; See 'Style of sliding'
  MT_SlideProx.l      ; Click proximity to grab a slider
  MT_BackImage.l      ; Bitmap# for backdrop.
  MT_BackColour.l     ; Backdrop colour
 
  MT_SlideX.l[4]      ; Position of sliders in pixels from left
  MT_Colour.l[4]      ; Colours of sliders
  MT_SlideBMP.l[4]    ; Bitmaps for individual sliders.
 
  MT_SlideScale.f[4]  ; Not used at present
  MT_SlideVal.f[4]    ; Not used at present
EndStructure

; Styles of sliding
Enumeration 100
  #SlideThro ; All sliders full range   ** DEFAULT **
  #Caged     ; Slider constrained between neighbours
  #Push      ; Slider can shove neighbours
  #NoSlide   ; Locks all sliders, still allows programatic movements
EndEnumeration

; Define custom events
Enumeration #PB_Event_FirstCustomValue
  #EventSliderMove
 
EndEnumeration

; Private
Global NumBarSlides = 1            ; Number of slider gadgets created
Global Dragging.l = 0              ; Flag... a pointer is being dragged
Global Dim MSlide.MULTITRACKBAR(1) ; Array of structures, one structure per gadget
Dim SDat(4)                        ; For demo only...
;}

Procedure MTrackBarGadget(Gadnum,GadX,GadY,GadW,GadH,MinVal,MaxVal,NumSlides,SlideMode=#SlideThro)
 
  ; Create storage for a new MTrackBarGadget
  If MSlide(NumBarSlides)\MT_W <> 0 
    NumBarSlides + 1
    ReDim MSlide(NumBarSlides)
  EndIf

  Result = CanvasGadget(Gadnum,GadX,GadY,GadW,GadH,#PB_Canvas_ClipMouse| #PB_Canvas_Border)
 
  If Result
    If Gadnum = #PB_Any : Gadnum = Result : EndIf
   
    With MSlide(NumBarSlides)
      \MT_Win    = GetActiveWindow()
      \MT_GadNum = Gadnum
      \MT_X = GadX
      \MT_Y = GadY
      \MT_W = GadW
      \MT_H = GadH
      \MT_MinVal      = MinVal
      \MT_MaxVal      = MaxVal
      \MT_GratSpace   = \MT_W / 32
      \MT_NumSlides   = NumSlides
      \MT_SlideMode   = SlideMode
      \MT_SlideProx   = 5 ; Pixels
      \MT_BackColour  = RGB(100,100,100)
      \MT_BackImage   = CreateImage(#PB_Any,GadW,GadH,32,\MT_BackColour)
      \MT_SlideX.l[0] = 1*GadW/5
      \MT_SlideX.l[1] = 2*GadW/5
      \MT_SlideX.l[2] = 3*GadW/5
      \MT_SlideX.l[3] = 4*GadW/5
      \MT_Colour[0]   = #Red
      \MT_Colour[1]   = #Green
      \MT_Colour[2]   = #Cyan
      \MT_Colour[3]   = #Yellow 
     
      ; Create image for backdrop, with graticule.
      If StartDrawing(ImageOutput(\MT_BackImage))
          F.f = 0
          While F < \MT_W
            LineXY(F,\MT_H,F,\MT_H-\MT_H>>2,#White)
            F + \MT_GratSpace
          Wend
        StopDrawing()
      EndIf
     
      ; Images for each slider
      For n = 0 To \MT_NumSlides-1
        \MT_SlideBMP[n] = CreateImage(#PB_Any,3,\MT_H,32,\MT_Colour[n])
      Next

    EndWith
   
    ; Each gadget is to call the same service routine
    BindGadgetEvent(Gadnum,@MultiBarService())
   
    MB_Refresh(NumBarSlides)
  EndIf
 
  ProcedureReturn Gadnum
 
EndProcedure
Procedure MultiBarService()  ; An Event has occurred on a MultiTrackBarGadget()...
  Static LastX
 
  ; Get the Gadget# that was tickled...
  EGad = EventGadget()
 
  ; Find the index to the data for the chosen gadget
  GadIndex = 0
  For n = 1 To NumBarSlides
    If EGad = MSlide(n)\MT_GadNum
      GadIndex = n
      Break
    EndIf
  Next
 
  ; Quit if index not found
  If GadIndex = 0
    ProcedureReturn #False
  EndIf
 
  ; Event manager for left mouse button down/up and mouse slide
  With MSlide(GadIndex)
    Select EventType()
      Case #PB_EventType_LeftButtonDown ;{ Button went DOWN, start dragging a pointer...
       
        DragX = GetGadgetAttribute(\MT_GadNum, #PB_Canvas_MouseX) + 2
        DragY = GetGadgetAttribute(\MT_GadNum, #PB_Canvas_MouseY) + 2
       
        ; Find a cursor position that is 'close' and jump to it.
        Dragging = 0
        For n = 0 To \MT_NumSlides-1     ; For each of the cursors
          t = DragX - \MT_SlideX[n]      ; Find how close the mouse is...
          If t<0 : t=-t : EndIf          ; regardless of side.
          If t < \MT_SlideProx           ; If close enough
            SetGadgetMouseXY(\MT_Win,\MT_GadNum,\MT_SlideX[n],DragY); Jump mouse to cursor
            Dragging = n+1; and set flag for 'dragging' (=Cursor+1)
            CompilerIf #PB_Compiler_OS = #PB_OS_Windows
            	beep_(2500,50)
            CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
            	RunProgram("beep"); 'beep' must be installed on Linux
            CompilerEndIf
            Break
          EndIf
        Next
        ;}
      Case #PB_EventType_MouseMove      ;{ A pointer is being dragged, update position
        If Dragging
         
          X = GetGadgetAttribute(\MT_GadNum, #PB_Canvas_MouseX)+2
         
          Select \MT_SlideMode
             
            Case #SlideThro  ; Sliders are free to roam over whole gadget
              \MT_SlideX[Dragging-1] = X
              PostEvent(#EventSliderMove,\MT_Win,\MT_GadNum,Dragging-1,X-2)
              MB_Refresh(GadIndex)
             
            Case #Caged      ; Sliders are restricted to space between neighbours / ends
              If  (Dragging = \MT_NumSlides) Or (X <= \MT_SlideX[Dragging])
                If (Dragging = 1) Or (X >= \MT_SlideX[Dragging-2])               
                  \MT_SlideX[Dragging-1] = X
                  Debug \MT_GadNum
                  PostEvent(#EventSliderMove,\MT_Win,\MT_GadNum,Dragging-1,X-2)
                  MB_Refresh(GadIndex)
                EndIf
              EndIf
             
            Case #Push       ; Sliders can push neighbours
              \MT_SlideX[Dragging-1] = X
               
              For n = Dragging-2 To 0 Step -1
                If X <  \MT_SlideX[n]
                  \MT_SlideX[n] = X
                  PostEvent(#EventSliderMove,\MT_Win,\MT_GadNum,n,X-2)
                EndIf
              Next
             
              For n = Dragging To \MT_NumSlides - 1
                If X > \MT_SlideX[n]
                  \MT_SlideX[n] = X
                  PostEvent(#EventSliderMove,\MT_Win,\MT_GadNum,n,X-2)
                EndIf
              Next
             
              PostEvent(#EventSliderMove,\MT_Win,\MT_GadNum,Dragging-1,X-2)
              MB_Refresh(GadIndex)
             
            Case #NoSlide
              ; Nothing to do!
             
          EndSelect
         
        EndIf
        ;}
      Case #PB_EventType_LeftButtonUp   ;{ Button went UP, stop dragging pointer
        Dragging = #False
        ;}
    EndSelect
  EndWith
 
EndProcedure
Procedure MB_Refresh(Index)  ; Redraw a MTBG referenced by table index (order of creation) Base 1
  With MSlide(Index)
    StartDrawing(CanvasOutput(\MT_GadNum))
      DrawImage(ImageID(\MT_BackImage),0,0)   ; Redraw backdop
      For n = 0 To \MT_NumSlides-1            ; Redraw pointers
        LineXY(\MT_SlideX[n]-2,0,\MT_SlideX[n]-2,\MT_H,\MT_Colour[n]) ; Single line
        ; DrawImage(ImageID(\MT_SlideBMP[n]),\MT_SlideX[n]-3,0)         ; Image
       
        ; If more than one slider put coloured balls on them
        If \MT_NumSlides > 1
          Circle(\MT_SlideX[n]-2,(n+1)*4,2,\MT_Colour[n])
        EndIf
       
      Next
    StopDrawing()
  EndWith
EndProcedure

Procedure SetGadgetMouseXY(Win,Gadget,MX,MY) ;- Position mouse pointer at specified co-ordinated within a gadget
  MX + WindowX(Win,#PB_Window_InnerCoordinate) + GadgetX(Gadget)
  MY + WindowY(Win,#PB_Window_InnerCoordinate) + GadgetY(Gadget)
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    SetCursorPos_(MX,MY)
  CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
   	gdk_display_warp_pointer(gdk_display_get_default (), gdk_screen_get_default(), MX, MY);  Gtk2, officialy deprecated
   ;-> replace the line above with the following line, if 'gdk_display_warp_pointer' is finally deprecated on newer gtk3-systems...
;  	gdk_device_warp (gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default ())), gdk_screen_get_default(), MX, MY)
  CompilerEndIf
EndProcedure

; ------------- Test/demo code -------------
CompilerIf #PB_Compiler_IsMainFile
  OpenWindow(1,0,0,600,320,"MultiBar_Test", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  SetWindowColor(1,#Cyan)
 
  ; Create some test gadgets.
  ;(They use BindGadget() so do Not appear under event gadgets in the Event Manager)
  TextGadget(#PB_Any,10,10,580,20,"Single slider - Full range.")
  MTrackBarGadget(5, 10,25,580,20,0,1000,1)           ; 1 Slider
 
  TextGadget(#PB_Any,10,50,560,20,"Two sliders - Full range.")
  MTrackBarGadget(6, 10,65,560,50,0,1000,2)                ; 2
 
  TextGadget(#PB_Any,10,120,540,20,"Three sliders - 'Caged', no cross overs.")
  MTrackBarGadget(7, 10,135,540,50,0,100, 3,#Caged)   ; 3
 
  TextGadget(#PB_Any,10,190,512+4,20,"Four sliders - Neighbours pushed. Active backdrop.")
  MTrackBarGadget(8, 10,205,512+4,100+4,0,100,4,#Push)  ; 4 +4? Left+right borders!
 
  TestPic = CreateImage(#PB_Any,512,100,32) ; For active backdrop
 
  ; Skeleton Event Manager... all the usual stuff.
  Repeat
   
    Select WaitWindowEvent(15)
      Case #PB_Event_CloseWindow
        Break
       
      Case  #EventSliderMove
        Debug "Window  " + Str(EventWindow())
        Debug "Gadget  " + Str(EventGadget())
        Debug "Slider  " + Str(EventType())
        Debug "X Value " + Str(EventData())
        Debug ""
        SDat(EventType())=EventData()
       
      Case #PB_Event_Gadget
        EGad = EventGadget()
        Select EGad
           
           
           
        EndSelect
       
    EndSelect
   
    ; == ACTIVE BACKDROP DEMO ==============================================
    ; How to update the backdrop to a MTrackBarGadget() periodically
    ; WaitWindowEvent(n) MUST include the optional TimeOut.
   
    ; Make a dynamic data display... this would be the output from your 'machine'.
    ; I just made a random noise base line, simulating a spectrum display.
    StartDrawing(ImageOutput(TestPic))
      Box(0,0,512,100,#Black)  ; Clear the display
      LastY = 0 : LastX = 0
      For X = 0 To 511 Step 2
        Y = 99-Random(10)
        LineXY(LastX,LastY,X,Y,#White)
        LastY = Y : LastX = X
      Next
     
      For n = 0 To 3
        DrawText(5,n*15,Str(n)+":  "+Str(SDat(n)))
      Next
    StopDrawing()
   
    ; Refresh the MTBG #4
    If StartDrawing(ImageOutput(MSlide(4)\MT_BackImage))
      DrawImage(ImageID(TestPic),0,0) 
      StopDrawing()
      MB_Refresh(4)
    EndIf
    ; == END OF DEMO ========================================================
   
  ForEver
 
CompilerEndIf
Please check the windows version and colors again.

Best Regards, Charly

Re: MultiTrackBarGadget (Windows)

Posted: Sat Feb 13, 2016 10:56 am
by RichardL
Good morning Oma,

Thanks for your Linux conversion. I am unable to test it, but can confirm the modified version
still compiles in Windoze OK.

Anyone for Mac?

RichardL

Re: MultiTrackBarGadget (Windows)

Posted: Sat Feb 13, 2016 12:56 pm
by davido
@RichardL,
I have tried the code on my MacBook Pro. I cannot get it to run.

First I checked with Windows what would happen if I removed the two API calls.
At first I could find no difference (except 'no beep'). Then by closer examination I discovered that the cursor works up to 5 pixels from each side but only locks when the mouse is moved, in either direction.
So is this SetCursor call really needed? I suspect I may be missing something here. :)

Anyway it crashes on the Mac at the creation of the image. Can't see why at the moment but will take another look later on.

On Windows 10, PureBasic 5.42LTS
Mac OSX, PureBasic 5.42LTS

Re: MultiTrackBarGadget (Windows)

Posted: Sat Feb 13, 2016 2:32 pm
by Oma
Hello Richard,
thank you for the feedback.
So is this SetCursor call really needed?
I also asked myself. :?
And yes, it sometimes crashes on the first mouse action/click after the start with an 'uninitialized window' within the Procedure SetGadgetMouseXY(Win,Gadget,MX,MY) but at the moment i can't reproduce it.

Regards, Charly

Re: MultiTrackBarGadget (Windows)

Posted: Sat Feb 13, 2016 3:12 pm
by davido
@Oma,
If all the API calls are removed, why should a crash occur unless some of the code remaining is relying upon such a call?
As far as I can see the rest of the code is normal PureBasic code.

It would be interesting to find out!

Re: MultiTrackBarGadget (Windows)

Posted: Sun Feb 14, 2016 8:57 am
by Oma
Good morning.
You can start it 50 times and nothing happens, but if you don't wait for it, it crashes.
The crash happens in: MX + WindowX(Win,#PB_Window_InnerCoordinate) + GadgetX(Gadget) so it has nothing to do with the API call.
It seems that it is caused in \MT_Win = GetActiveWindow()?. Time will tell :wink:

But first of all. To be really usable in Apps, it should be made fit for EnableExplicit because there are a lot of undefined Globals in the demo that travel through all parts of the program.

Best Regards, Charly

Re: MultiTrackBarGadget (Windows)

Posted: Sun Feb 14, 2016 10:13 pm
by davido
@RichardL,
I've got it working on the Mac as follows:
1. Remove the Window API calls.
2. Change the longs in the structure to integers so that it will work on 64 bit systems.

@Oma,
Thank you for your comments. I have no doubt that your observations are correct. However, I have not been able to get the program to crash and for the moment I find it quite acceptable.

Re: MultiTrackBarGadget (Windows)

Posted: Wed Mar 09, 2016 11:31 am
by RichardL
Hi Folks,

I have updated the code in the first posting, added a few useful flags and tidied up a bit.
Please let me know of any significant bugs... it is mainly a 'fun thing' so has not been given
very much testing.

Regards,
RichardL

Re: MultiTrackBarGadget (Windows)

Posted: Wed Mar 09, 2016 7:39 pm
by VB6_to_PBx

Code: Select all

; Styles of pointer sliding
EnumerationBinary
  ; Slider collision style
  #SlideThro          ; All sliders full range   ** DEFAULT **
  #Caged              ; Slider constrained between neighbours
  #Push               ; Slider can shove neighbours
  #NoSlide            ; Prevent user moving sliders, still allows programatic movements
 
  ; Misc flags
  #Graticule = %1000
  #Balls
  #NarrowSlider
EndEnumeration
EnumerationBinary <= ERROR

should be :
Enumeration Binary

Re: MultiTrackBarGadget (Windows)

Posted: Wed Mar 09, 2016 9:15 pm
by RichardL
@VB6_to_PBx
Hi, That is strange.
(1) From the PB5.41LTS Docs...

Code: Select all

  EnumerationBinary
    #Flags1 ; will be 1
    #Flags2 ; will be 2
    #Flags3 ; will be 4
    #Flags4 ; will be 8
    #Flags5 ; will be 16
  EndEnumeration
(2) EnumerationBinary works as I expect it to.

(3) Enumeration Binary does not!

RichardL

Re: MultiTrackBarGadget (Windows)

Posted: Thu Mar 10, 2016 12:11 am
by VB6_to_PBx
@VB6_to_PBx
Hi, That is strange.
(1) From the PB5.41LTS Docs...
yes, sorry i tested your Code in PB5.31
and PB5.31 wanted
Enumeration Binary
instead of :
EnumerationBinary
:oops:

Re: MultiTrackBarGadget 0.3 (Windows)

Posted: Thu Mar 10, 2016 10:44 am
by RichardL
Hi Folks,

I have updated the code in the first posting, adding a new function to return the backdrop bitmap to a MTrackBarGadget()
and tidied up a bit. The dynamic refresh example shows how to use the new function.

Must get on with the main job now, so unless I hear of a non-trivial bug... that's it folks!

Regards,
RichardL